made with requirebin
Last active
August 3, 2016 23:56
-
-
Save realazthat/2f9560e3fa0add69f95a81844569275e to your computer and use it in GitHub Desktop.
requirebin sketch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const resl = require('resl'); | |
const regl = require('regl')(); | |
const quad = require('glsl-quad'); | |
resl({ | |
manifest: { | |
texture: { | |
type: 'image', | |
// quad provides a bitmap as a uri to display; the "directions" bitmap shows two | |
// axis with up/down/right/left lines/arrows. | |
src: quad.bitmaps.directions.uri, | |
parser: (data) => regl.texture({ | |
data: data, | |
mag: 'nearest', | |
min: 'nearest', | |
flipY: true | |
}) | |
} | |
}, | |
onDone: ({texture}) => { | |
const drawTexture = regl({ | |
vert: quad.show.uvs.vert, | |
frag: quad.show.uvs.frag, | |
attributes: { | |
a_position: quad.verts, | |
a_uv: quad.uvs | |
}, | |
elements: quad.indices, | |
frontFace: 'ccw', | |
cull: { | |
enable: false, | |
face: 'back' | |
}, | |
uniforms: { | |
u_tex: regl.prop('texture'), | |
// Use a negative y, webgl display works upside down. I think. | |
u_clip_y: -1 | |
}, | |
viewport: { | |
x: 0, | |
y: 0, | |
width: texture.width, | |
height: texture.height | |
} | |
}); | |
drawTexture({texture}); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
setTimeout(function(){ | |
;require=(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})({"resl":[function(require,module,exports){ | |
/* global XMLHttpRequest */ | |
var configParameters = [ | |
'manifest', | |
'onDone', | |
'onProgress', | |
'onError' | |
] | |
var manifestParameters = [ | |
'type', | |
'src', | |
'stream', | |
'credentials', | |
'parser' | |
] | |
var parserParameters = [ | |
'onData', | |
'onDone' | |
] | |
var STATE_ERROR = -1 | |
var STATE_DATA = 0 | |
var STATE_COMPLETE = 1 | |
function raise (message) { | |
throw new Error('resl: ' + message) | |
} | |
function checkType (object, parameters, name) { | |
Object.keys(object).forEach(function (param) { | |
if (parameters.indexOf(param) < 0) { | |
raise('invalid parameter "' + param + '" in ' + name) | |
} | |
}) | |
} | |
function Loader (name, cancel) { | |
this.state = STATE_DATA | |
this.ready = false | |
this.progress = 0 | |
this.name = name | |
this.cancel = cancel | |
} | |
module.exports = function resl (config) { | |
if (typeof config !== 'object' || !config) { | |
raise('invalid or missing configuration') | |
} | |
checkType(config, configParameters, 'config') | |
var manifest = config.manifest | |
if (typeof manifest !== 'object' || !manifest) { | |
raise('missing manifest') | |
} | |
function getFunction (name, dflt) { | |
if (name in config) { | |
var func = config[name] | |
if (typeof func !== 'function') { | |
raise('invalid callback "' + name + '"') | |
} | |
return func | |
} | |
return null | |
} | |
var onDone = getFunction('onDone') | |
if (!onDone) { | |
raise('missing onDone() callback') | |
} | |
var onProgress = getFunction('onProgress') | |
var onError = getFunction('onError') | |
var assets = {} | |
var state = STATE_DATA | |
function loadXHR (request) { | |
var name = request.name | |
var stream = request.stream | |
var binary = request.type === 'binary' | |
var parser = request.parser | |
var xhr = new XMLHttpRequest() | |
var asset = null | |
var loader = new Loader(name, cancel) | |
if (stream) { | |
xhr.onreadystatechange = onReadyStateChange | |
} else { | |
xhr.onreadystatechange = function () { | |
if (xhr.readyState === 4) { | |
onReadyStateChange() | |
} | |
} | |
} | |
if (binary) { | |
xhr.responseType = 'arraybuffer' | |
} | |
function onReadyStateChange () { | |
if (xhr.readyState < 2 || | |
loader.state === STATE_COMPLETE || | |
loader.state === STATE_ERROR) { | |
return | |
} | |
if (xhr.status !== 200) { | |
return abort('error loading resource "' + request.name + '"') | |
} | |
if (xhr.readyState > 2 && loader.state === STATE_DATA) { | |
var response | |
if (request.type === 'binary') { | |
response = xhr.response | |
} else { | |
response = xhr.responseText | |
} | |
if (parser.data) { | |
try { | |
asset = parser.data(response) | |
} catch (e) { | |
return abort(e) | |
} | |
} else { | |
asset = response | |
} | |
} | |
if (xhr.readyState > 3 && loader.state === STATE_DATA) { | |
if (parser.done) { | |
try { | |
asset = parser.done() | |
} catch (e) { | |
return abort(e) | |
} | |
} | |
loader.state = STATE_COMPLETE | |
} | |
assets[name] = asset | |
loader.progress = 0.75 * loader.progress + 0.25 | |
loader.ready = | |
(request.stream && !!asset) || | |
loader.state === STATE_COMPLETE | |
notifyProgress() | |
} | |
function cancel () { | |
if (loader.state === STATE_COMPLETE || loader.state === STATE_ERROR) { | |
return | |
} | |
xhr.onreadystatechange = null | |
xhr.abort() | |
loader.state = STATE_ERROR | |
} | |
// set up request | |
if (request.credentials) { | |
xhr.withCredentials = true | |
} | |
xhr.open('GET', request.src, true) | |
xhr.send() | |
return loader | |
} | |
function loadElement (request, element) { | |
var name = request.name | |
var parser = request.parser | |
var loader = new Loader(name, cancel) | |
var asset = element | |
function handleProgress () { | |
if (loader.state === STATE_DATA) { | |
if (parser.data) { | |
try { | |
asset = parser.data(element) | |
} catch (e) { | |
return abort(e) | |
} | |
} else { | |
asset = element | |
} | |
} | |
} | |
function onProgress (e) { | |
handleProgress() | |
assets[name] = asset | |
if (e.lengthComputable) { | |
loader.progress = Math.max(loader.progress, e.loaded / e.total) | |
} else { | |
loader.progress = 0.75 * loader.progress + 0.25 | |
} | |
loader.ready = request.stream | |
notifyProgress(name) | |
} | |
function onComplete () { | |
handleProgress() | |
if (loader.state === STATE_DATA) { | |
if (parser.done) { | |
try { | |
asset = parser.done() | |
} catch (e) { | |
return abort(e) | |
} | |
} | |
loader.state = STATE_COMPLETE | |
} | |
loader.progress = 1 | |
loader.ready = true | |
assets[name] = asset | |
removeListeners() | |
notifyProgress('finish ' + name) | |
} | |
function onError () { | |
abort('error loading asset "' + name + '"') | |
} | |
if (request.stream) { | |
element.addEventListener('progress', onProgress) | |
} | |
if (request.type === 'image') { | |
element.addEventListener('load', onComplete) | |
} else { | |
element.addEventListener('canplay', onComplete) | |
} | |
element.addEventListener('error', onError) | |
function removeListeners () { | |
if (request.stream) { | |
element.removeEventListener('progress', onProgress) | |
} | |
if (request.type === 'image') { | |
element.addEventListener('load', onComplete) | |
} else { | |
element.addEventListener('canplay', onComplete) | |
} | |
element.removeEventListener('error', onError) | |
} | |
function cancel () { | |
if (loader.state === STATE_COMPLETE || loader.state === STATE_ERROR) { | |
return | |
} | |
loader.state = STATE_ERROR | |
removeListeners() | |
element.src = '' | |
} | |
// set up request | |
if (request.credentials) { | |
element.crossOrigin = 'use-credentials' | |
} else { | |
element.crossOrigin = 'anonymous' | |
} | |
element.src = request.src | |
return loader | |
} | |
var loaders = { | |
text: loadXHR, | |
binary: function (request) { | |
// TODO use fetch API for streaming if supported | |
return loadXHR(request) | |
}, | |
image: function (request) { | |
return loadElement(request, document.createElement('img')) | |
}, | |
video: function (request) { | |
return loadElement(request, document.createElement('video')) | |
}, | |
audio: function (request) { | |
return loadElement(request, document.createElement('audio')) | |
} | |
} | |
// First we parse all objects in order to verify that all type information | |
// is correct | |
var pending = Object.keys(manifest).map(function (name) { | |
var request = manifest[name] | |
if (typeof request === 'string') { | |
request = { | |
src: request | |
} | |
} else if (typeof request !== 'object' || !request) { | |
raise('invalid asset definition "' + name + '"') | |
} | |
checkType(request, manifestParameters, 'asset "' + name + '"') | |
function getParameter (prop, accepted, init) { | |
var value = init | |
if (prop in request) { | |
value = request[prop] | |
} | |
if (accepted.indexOf(value) < 0) { | |
raise('invalid ' + prop + ' "' + value + '" for asset "' + name + '", possible values: ' + accepted) | |
} | |
return value | |
} | |
function getString (prop, required, init) { | |
var value = init | |
if (prop in request) { | |
value = request[prop] | |
} else if (required) { | |
raise('missing ' + prop + ' for asset "' + name + '"') | |
} | |
if (typeof value !== 'string') { | |
raise('invalid ' + prop + ' for asset "' + name + '", must be a string') | |
} | |
return value | |
} | |
function getParseFunc (name, dflt) { | |
if (name in request.parser) { | |
var result = request.parser[name] | |
if (typeof result !== 'function') { | |
raise('invalid parser callback ' + name + ' for asset "' + name + '"') | |
} | |
return result | |
} else { | |
return dflt | |
} | |
} | |
var parser = {} | |
if ('parser' in request) { | |
if (typeof request.parser === 'function') { | |
parser = { | |
data: request.parser | |
} | |
} else if (typeof request.parser === 'object' && request.parser) { | |
checkType(parser, parserParameters, 'parser for asset "' + name + '"') | |
if (!('onData' in parser)) { | |
raise('missing onData callback for parser in asset "' + name + '"') | |
} | |
parser = { | |
data: getParseFunc('onData'), | |
done: getParseFunc('onDone') | |
} | |
} else { | |
raise('invalid parser for asset "' + name + '"') | |
} | |
} | |
return { | |
name: name, | |
type: getParameter('type', Object.keys(loaders), 'text'), | |
stream: !!request.stream, | |
credentials: !!request.credentials, | |
src: getString('src', true, ''), | |
parser: parser | |
} | |
}).map(function (request) { | |
return (loaders[request.type])(request) | |
}) | |
function abort (message) { | |
if (state === STATE_ERROR || state === STATE_COMPLETE) { | |
return | |
} | |
state = STATE_ERROR | |
pending.forEach(function (loader) { | |
loader.cancel() | |
}) | |
if (onError) { | |
if (typeof message === 'string') { | |
onError(new Error('resl: ' + message)) | |
} else { | |
onError(message) | |
} | |
} else { | |
console.error('resl error:', message) | |
} | |
} | |
function notifyProgress (message) { | |
if (state === STATE_ERROR || state === STATE_COMPLETE) { | |
return | |
} | |
var progress = 0 | |
var numReady = 0 | |
pending.forEach(function (loader) { | |
if (loader.ready) { | |
numReady += 1 | |
} | |
progress += loader.progress | |
}) | |
if (numReady === pending.length) { | |
state = STATE_COMPLETE | |
onDone(assets) | |
} else { | |
if (onProgress) { | |
onProgress(progress / pending.length, message) | |
} | |
} | |
} | |
} | |
},{}]},{},[]) | |
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2hvbWUvYWRtaW4vYnJvd3NlcmlmeS1jZG4vbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsImluZGV4LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiLyogZ2xvYmFsIFhNTEh0dHBSZXF1ZXN0ICovXG52YXIgY29uZmlnUGFyYW1ldGVycyA9IFtcbiAgJ21hbmlmZXN0JyxcbiAgJ29uRG9uZScsXG4gICdvblByb2dyZXNzJyxcbiAgJ29uRXJyb3InXG5dXG5cbnZhciBtYW5pZmVzdFBhcmFtZXRlcnMgPSBbXG4gICd0eXBlJyxcbiAgJ3NyYycsXG4gICdzdHJlYW0nLFxuICAnY3JlZGVudGlhbHMnLFxuICAncGFyc2VyJ1xuXVxuXG52YXIgcGFyc2VyUGFyYW1ldGVycyA9IFtcbiAgJ29uRGF0YScsXG4gICdvbkRvbmUnXG5dXG5cbnZhciBTVEFURV9FUlJPUiA9IC0xXG52YXIgU1RBVEVfREFUQSA9IDBcbnZhciBTVEFURV9DT01QTEVURSA9IDFcblxuZnVuY3Rpb24gcmFpc2UgKG1lc3NhZ2UpIHtcbiAgdGhyb3cgbmV3IEVycm9yKCdyZXNsOiAnICsgbWVzc2FnZSlcbn1cblxuZnVuY3Rpb24gY2hlY2tUeXBlIChvYmplY3QsIHBhcmFtZXRlcnMsIG5hbWUpIHtcbiAgT2JqZWN0LmtleXMob2JqZWN0KS5mb3JFYWNoKGZ1bmN0aW9uIChwYXJhbSkge1xuICAgIGlmIChwYXJhbWV0ZXJzLmluZGV4T2YocGFyYW0pIDwgMCkge1xuICAgICAgcmFpc2UoJ2ludmFsaWQgcGFyYW1ldGVyIFwiJyArIHBhcmFtICsgJ1wiIGluICcgKyBuYW1lKVxuICAgIH1cbiAgfSlcbn1cblxuZnVuY3Rpb24gTG9hZGVyIChuYW1lLCBjYW5jZWwpIHtcbiAgdGhpcy5zdGF0ZSA9IFNUQVRFX0RBVEFcbiAgdGhpcy5yZWFkeSA9IGZhbHNlXG4gIHRoaXMucHJvZ3Jlc3MgPSAwXG4gIHRoaXMubmFtZSA9IG5hbWVcbiAgdGhpcy5jYW5jZWwgPSBjYW5jZWxcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiByZXNsIChjb25maWcpIHtcbiAgaWYgKHR5cGVvZiBjb25maWcgIT09ICdvYmplY3QnIHx8ICFjb25maWcpIHtcbiAgICByYWlzZSgnaW52YWxpZCBvciBtaXNzaW5nIGNvbmZpZ3VyYXRpb24nKVxuICB9XG5cbiAgY2hlY2tUeXBlKGNvbmZpZywgY29uZmlnUGFyYW1ldGVycywgJ2NvbmZpZycpXG5cbiAgdmFyIG1hbmlmZXN0ID0gY29uZmlnLm1hbmlmZXN0XG4gIGlmICh0eXBlb2YgbWFuaWZlc3QgIT09ICdvYmplY3QnIHx8ICFtYW5pZmVzdCkge1xuICAgIHJhaXNlKCdtaXNzaW5nIG1hbmlmZXN0JylcbiAgfVxuXG4gIGZ1bmN0aW9uIGdldEZ1bmN0aW9uIChuYW1lLCBkZmx0KSB7XG4gICAgaWYgKG5hbWUgaW4gY29uZmlnKSB7XG4gICAgICB2YXIgZnVuYyA9IGNvbmZpZ1tuYW1lXVxuICAgICAgaWYgKHR5cGVvZiBmdW5jICE9PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHJhaXNlKCdpbnZhbGlkIGNhbGxiYWNrIFwiJyArIG5hbWUgKyAnXCInKVxuICAgICAgfVxuICAgICAgcmV0dXJuIGZ1bmNcbiAgICB9XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIHZhciBvbkRvbmUgPSBnZXRGdW5jdGlvbignb25Eb25lJylcbiAgaWYgKCFvbkRvbmUpIHtcbiAgICByYWlzZSgnbWlzc2luZyBvbkRvbmUoKSBjYWxsYmFjaycpXG4gIH1cblxuICB2YXIgb25Qcm9ncmVzcyA9IGdldEZ1bmN0aW9uKCdvblByb2dyZXNzJylcbiAgdmFyIG9uRXJyb3IgPSBnZXRGdW5jdGlvbignb25FcnJvcicpXG5cbiAgdmFyIGFzc2V0cyA9IHt9XG5cbiAgdmFyIHN0YXRlID0gU1RBVEVfREFUQVxuXG4gIGZ1bmN0aW9uIGxvYWRYSFIgKHJlcXVlc3QpIHtcbiAgICB2YXIgbmFtZSA9IHJlcXVlc3QubmFtZVxuICAgIHZhciBzdHJlYW0gPSByZXF1ZXN0LnN0cmVhbVxuICAgIHZhciBiaW5hcnkgPSByZXF1ZXN0LnR5cGUgPT09ICdiaW5hcnknXG4gICAgdmFyIHBhcnNlciA9IHJlcXVlc3QucGFyc2VyXG5cbiAgICB2YXIgeGhyID0gbmV3IFhNTEh0dHBSZXF1ZXN0KClcbiAgICB2YXIgYXNzZXQgPSBudWxsXG5cbiAgICB2YXIgbG9hZGVyID0gbmV3IExvYWRlcihuYW1lLCBjYW5jZWwpXG5cbiAgICBpZiAoc3RyZWFtKSB7XG4gICAgICB4aHIub25yZWFkeXN0YXRlY2hhbmdlID0gb25SZWFkeVN0YXRlQ2hhbmdlXG4gICAgfSBlbHNlIHtcbiAgICAgIHhoci5vbnJlYWR5c3RhdGVjaGFuZ2UgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmICh4aHIucmVhZHlTdGF0ZSA9PT0gNCkge1xuICAgICAgICAgIG9uUmVhZHlTdGF0ZUNoYW5nZSgpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoYmluYXJ5KSB7XG4gICAgICB4aHIucmVzcG9uc2VUeXBlID0gJ2FycmF5YnVmZmVyJ1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIG9uUmVhZHlTdGF0ZUNoYW5nZSAoKSB7XG4gICAgICBpZiAoeGhyLnJlYWR5U3RhdGUgPCAyIHx8XG4gICAgICAgICAgbG9hZGVyLnN0YXRlID09PSBTVEFURV9DT01QTEVURSB8fFxuICAgICAgICAgIGxvYWRlci5zdGF0ZSA9PT0gU1RBVEVfRVJST1IpIHtcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgICBpZiAoeGhyLnN0YXR1cyAhPT0gMjAwKSB7XG4gICAgICAgIHJldHVybiBhYm9ydCgnZXJyb3IgbG9hZGluZyByZXNvdXJjZSBcIicgKyByZXF1ZXN0Lm5hbWUgKyAnXCInKVxuICAgICAgfVxuICAgICAgaWYgKHhoci5yZWFkeVN0YXRlID4gMiAmJiBsb2FkZXIuc3RhdGUgPT09IFNUQVRFX0RBVEEpIHtcbiAgICAgICAgdmFyIHJlc3BvbnNlXG4gICAgICAgIGlmIChyZXF1ZXN0LnR5cGUgPT09ICdiaW5hcnknKSB7XG4gICAgICAgICAgcmVzcG9uc2UgPSB4aHIucmVzcG9uc2VcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXNwb25zZSA9IHhoci5yZXNwb25zZVRleHRcbiAgICAgICAgfVxuICAgICAgICBpZiAocGFyc2VyLmRhdGEpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXNzZXQgPSBwYXJzZXIuZGF0YShyZXNwb25zZSlcbiAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICByZXR1cm4gYWJvcnQoZSlcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgYXNzZXQgPSByZXNwb25zZVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoeGhyLnJlYWR5U3RhdGUgPiAzICYmIGxvYWRlci5zdGF0ZSA9PT0gU1RBVEVfREFUQSkge1xuICAgICAgICBpZiAocGFyc2VyLmRvbmUpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXNzZXQgPSBwYXJzZXIuZG9uZSgpXG4gICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgcmV0dXJuIGFib3J0KGUpXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGxvYWRlci5zdGF0ZSA9IFNUQVRFX0NPTVBMRVRFXG4gICAgICB9XG4gICAgICBhc3NldHNbbmFtZV0gPSBhc3NldFxuICAgICAgbG9hZGVyLnByb2dyZXNzID0gMC43NSAqIGxvYWRlci5wcm9ncmVzcyArIDAuMjVcbiAgICAgIGxvYWRlci5yZWFkeSA9XG4gICAgICAgIChyZXF1ZXN0LnN0cmVhbSAmJiAhIWFzc2V0KSB8fFxuICAgICAgICBsb2FkZXIuc3RhdGUgPT09IFNUQVRFX0NPTVBMRVRFXG4gICAgICBub3RpZnlQcm9ncmVzcygpXG4gICAgfVxuXG4gICAgZnVuY3Rpb24gY2FuY2VsICgpIHtcbiAgICAgIGlmIChsb2FkZXIuc3RhdGUgPT09IFNUQVRFX0NPTVBMRVRFIHx8IGxvYWRlci5zdGF0ZSA9PT0gU1RBVEVfRVJST1IpIHtcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgICB4aHIub25yZWFkeXN0YXRlY2hhbmdlID0gbnVsbFxuICAgICAgeGhyLmFib3J0KClcbiAgICAgIGxvYWRlci5zdGF0ZSA9IFNUQVRFX0VSUk9SXG4gICAgfVxuXG4gICAgLy8gc2V0IHVwIHJlcXVlc3RcbiAgICBpZiAocmVxdWVzdC5jcmVkZW50aWFscykge1xuICAgICAgeGhyLndpdGhDcmVkZW50aWFscyA9IHRydWVcbiAgICB9XG4gICAgeGhyLm9wZW4oJ0dFVCcsIHJlcXVlc3Quc3JjLCB0cnVlKVxuICAgIHhoci5zZW5kKClcblxuICAgIHJldHVybiBsb2FkZXJcbiAgfVxuXG4gIGZ1bmN0aW9uIGxvYWRFbGVtZW50IChyZXF1ZXN0LCBlbGVtZW50KSB7XG4gICAgdmFyIG5hbWUgPSByZXF1ZXN0Lm5hbWVcbiAgICB2YXIgcGFyc2VyID0gcmVxdWVzdC5wYXJzZXJcblxuICAgIHZhciBsb2FkZXIgPSBuZXcgTG9hZGVyKG5hbWUsIGNhbmNlbClcbiAgICB2YXIgYXNzZXQgPSBlbGVtZW50XG5cbiAgICBmdW5jdGlvbiBoYW5kbGVQcm9ncmVzcyAoKSB7XG4gICAgICBpZiAobG9hZGVyLnN0YXRlID09PSBTVEFURV9EQVRBKSB7XG4gICAgICAgIGlmIChwYXJzZXIuZGF0YSkge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhc3NldCA9IHBhcnNlci5kYXRhKGVsZW1lbnQpXG4gICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgcmV0dXJuIGFib3J0KGUpXG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGFzc2V0ID0gZWxlbWVudFxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gb25Qcm9ncmVzcyAoZSkge1xuICAgICAgaGFuZGxlUHJvZ3Jlc3MoKVxuICAgICAgYXNzZXRzW25hbWVdID0gYXNzZXRcbiAgICAgIGlmIChlLmxlbmd0aENvbXB1dGFibGUpIHtcbiAgICAgICAgbG9hZGVyLnByb2dyZXNzID0gTWF0aC5tYXgobG9hZGVyLnByb2dyZXNzLCBlLmxvYWRlZCAvIGUudG90YWwpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBsb2FkZXIucHJvZ3Jlc3MgPSAwLjc1ICogbG9hZGVyLnByb2dyZXNzICsgMC4yNVxuICAgICAgfVxuICAgICAgbG9hZGVyLnJlYWR5ID0gcmVxdWVzdC5zdHJlYW1cbiAgICAgIG5vdGlmeVByb2dyZXNzKG5hbWUpXG4gICAgfVxuXG4gICAgZnVuY3Rpb24gb25Db21wbGV0ZSAoKSB7XG4gICAgICBoYW5kbGVQcm9ncmVzcygpXG4gICAgICBpZiAobG9hZGVyLnN0YXRlID09PSBTVEFURV9EQVRBKSB7XG4gICAgICAgIGlmIChwYXJzZXIuZG9uZSkge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhc3NldCA9IHBhcnNlci5kb25lKClcbiAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICByZXR1cm4gYWJvcnQoZSlcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbG9hZGVyLnN0YXRlID0gU1RBVEVfQ09NUExFVEVcbiAgICAgIH1cbiAgICAgIGxvYWRlci5wcm9ncmVzcyA9IDFcbiAgICAgIGxvYWRlci5yZWFkeSA9IHRydWVcbiAgICAgIGFzc2V0c1tuYW1lXSA9IGFzc2V0XG4gICAgICByZW1vdmVMaXN0ZW5lcnMoKVxuICAgICAgbm90aWZ5UHJvZ3Jlc3MoJ2ZpbmlzaCAnICsgbmFtZSlcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBvbkVycm9yICgpIHtcbiAgICAgIGFib3J0KCdlcnJvciBsb2FkaW5nIGFzc2V0IFwiJyArIG5hbWUgKyAnXCInKVxuICAgIH1cblxuICAgIGlmIChyZXF1ZXN0LnN0cmVhbSkge1xuICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCdwcm9ncmVzcycsIG9uUHJvZ3Jlc3MpXG4gICAgfVxuICAgIGlmIChyZXF1ZXN0LnR5cGUgPT09ICdpbWFnZScpIHtcbiAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignbG9hZCcsIG9uQ29tcGxldGUpXG4gICAgfSBlbHNlIHtcbiAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignY2FucGxheScsIG9uQ29tcGxldGUpXG4gICAgfVxuICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignZXJyb3InLCBvbkVycm9yKVxuXG4gICAgZnVuY3Rpb24gcmVtb3ZlTGlzdGVuZXJzICgpIHtcbiAgICAgIGlmIChyZXF1ZXN0LnN0cmVhbSkge1xuICAgICAgICBlbGVtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3Byb2dyZXNzJywgb25Qcm9ncmVzcylcbiAgICAgIH1cbiAgICAgIGlmIChyZXF1ZXN0LnR5cGUgPT09ICdpbWFnZScpIHtcbiAgICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCdsb2FkJywgb25Db21wbGV0ZSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignY2FucGxheScsIG9uQ29tcGxldGUpXG4gICAgICB9XG4gICAgICBlbGVtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2Vycm9yJywgb25FcnJvcilcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBjYW5jZWwgKCkge1xuICAgICAgaWYgKGxvYWRlci5zdGF0ZSA9PT0gU1RBVEVfQ09NUExFVEUgfHwgbG9hZGVyLnN0YXRlID09PSBTVEFURV9FUlJPUikge1xuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICAgIGxvYWRlci5zdGF0ZSA9IFNUQVRFX0VSUk9SXG4gICAgICByZW1vdmVMaXN0ZW5lcnMoKVxuICAgICAgZWxlbWVudC5zcmMgPSAnJ1xuICAgIH1cblxuICAgIC8vIHNldCB1cCByZXF1ZXN0XG4gICAgaWYgKHJlcXVlc3QuY3JlZGVudGlhbHMpIHtcbiAgICAgIGVsZW1lbnQuY3Jvc3NPcmlnaW4gPSAndXNlLWNyZWRlbnRpYWxzJ1xuICAgIH0gZWxzZSB7XG4gICAgICBlbGVtZW50LmNyb3NzT3JpZ2luID0gJ2Fub255bW91cydcbiAgICB9XG4gICAgZWxlbWVudC5zcmMgPSByZXF1ZXN0LnNyY1xuXG4gICAgcmV0dXJuIGxvYWRlclxuICB9XG5cbiAgdmFyIGxvYWRlcnMgPSB7XG4gICAgdGV4dDogbG9hZFhIUixcbiAgICBiaW5hcnk6IGZ1bmN0aW9uIChyZXF1ZXN0KSB7XG4gICAgICAvLyBUT0RPIHVzZSBmZXRjaCBBUEkgZm9yIHN0cmVhbWluZyBpZiBzdXBwb3J0ZWRcbiAgICAgIHJldHVybiBsb2FkWEhSKHJlcXVlc3QpXG4gICAgfSxcbiAgICBpbWFnZTogZnVuY3Rpb24gKHJlcXVlc3QpIHtcbiAgICAgIHJldHVybiBsb2FkRWxlbWVudChyZXF1ZXN0LCBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpbWcnKSlcbiAgICB9LFxuICAgIHZpZGVvOiBmdW5jdGlvbiAocmVxdWVzdCkge1xuICAgICAgcmV0dXJuIGxvYWRFbGVtZW50KHJlcXVlc3QsIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3ZpZGVvJykpXG4gICAgfSxcbiAgICBhdWRpbzogZnVuY3Rpb24gKHJlcXVlc3QpIHtcbiAgICAgIHJldHVybiBsb2FkRWxlbWVudChyZXF1ZXN0LCBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhdWRpbycpKVxuICAgIH1cbiAgfVxuXG4gIC8vIEZpcnN0IHdlIHBhcnNlIGFsbCBvYmplY3RzIGluIG9yZGVyIHRvIHZlcmlmeSB0aGF0IGFsbCB0eXBlIGluZm9ybWF0aW9uXG4gIC8vIGlzIGNvcnJlY3RcbiAgdmFyIHBlbmRpbmcgPSBPYmplY3Qua2V5cyhtYW5pZmVzdCkubWFwKGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgdmFyIHJlcXVlc3QgPSBtYW5pZmVzdFtuYW1lXVxuICAgIGlmICh0eXBlb2YgcmVxdWVzdCA9PT0gJ3N0cmluZycpIHtcbiAgICAgIHJlcXVlc3QgPSB7XG4gICAgICAgIHNyYzogcmVxdWVzdFxuICAgICAgfVxuICAgIH0gZWxzZSBpZiAodHlwZW9mIHJlcXVlc3QgIT09ICdvYmplY3QnIHx8ICFyZXF1ZXN0KSB7XG4gICAgICByYWlzZSgnaW52YWxpZCBhc3NldCBkZWZpbml0aW9uIFwiJyArIG5hbWUgKyAnXCInKVxuICAgIH1cblxuICAgIGNoZWNrVHlwZShyZXF1ZXN0LCBtYW5pZmVzdFBhcmFtZXRlcnMsICdhc3NldCBcIicgKyBuYW1lICsgJ1wiJylcblxuICAgIGZ1bmN0aW9uIGdldFBhcmFtZXRlciAocHJvcCwgYWNjZXB0ZWQsIGluaXQpIHtcbiAgICAgIHZhciB2YWx1ZSA9IGluaXRcbiAgICAgIGlmIChwcm9wIGluIHJlcXVlc3QpIHtcbiAgICAgICAgdmFsdWUgPSByZXF1ZXN0W3Byb3BdXG4gICAgICB9XG4gICAgICBpZiAoYWNjZXB0ZWQuaW5kZXhPZih2YWx1ZSkgPCAwKSB7XG4gICAgICAgIHJhaXNlKCdpbnZhbGlkICcgKyBwcm9wICsgJyBcIicgKyB2YWx1ZSArICdcIiBmb3IgYXNzZXQgXCInICsgbmFtZSArICdcIiwgcG9zc2libGUgdmFsdWVzOiAnICsgYWNjZXB0ZWQpXG4gICAgICB9XG4gICAgICByZXR1cm4gdmFsdWVcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBnZXRTdHJpbmcgKHByb3AsIHJlcXVpcmVkLCBpbml0KSB7XG4gICAgICB2YXIgdmFsdWUgPSBpbml0XG4gICAgICBpZiAocHJvcCBpbiByZXF1ZXN0KSB7XG4gICAgICAgIHZhbHVlID0gcmVxdWVzdFtwcm9wXVxuICAgICAgfSBlbHNlIGlmIChyZXF1aXJlZCkge1xuICAgICAgICByYWlzZSgnbWlzc2luZyAnICsgcHJvcCArICcgZm9yIGFzc2V0IFwiJyArIG5hbWUgKyAnXCInKVxuICAgICAgfVxuICAgICAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gJ3N0cmluZycpIHtcbiAgICAgICAgcmFpc2UoJ2ludmFsaWQgJyArIHByb3AgKyAnIGZvciBhc3NldCBcIicgKyBuYW1lICsgJ1wiLCBtdXN0IGJlIGEgc3RyaW5nJylcbiAgICAgIH1cbiAgICAgIHJldHVybiB2YWx1ZVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIGdldFBhcnNlRnVuYyAobmFtZSwgZGZsdCkge1xuICAgICAgaWYgKG5hbWUgaW4gcmVxdWVzdC5wYXJzZXIpIHtcbiAgICAgICAgdmFyIHJlc3VsdCA9IHJlcXVlc3QucGFyc2VyW25hbWVdXG4gICAgICAgIGlmICh0eXBlb2YgcmVzdWx0ICE9PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgcmFpc2UoJ2ludmFsaWQgcGFyc2VyIGNhbGxiYWNrICcgKyBuYW1lICsgJyBmb3IgYXNzZXQgXCInICsgbmFtZSArICdcIicpXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlc3VsdFxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIGRmbHRcbiAgICAgIH1cbiAgICB9XG5cbiAgICB2YXIgcGFyc2VyID0ge31cbiAgICBpZiAoJ3BhcnNlcicgaW4gcmVxdWVzdCkge1xuICAgICAgaWYgKHR5cGVvZiByZXF1ZXN0LnBhcnNlciA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICBwYXJzZXIgPSB7XG4gICAgICAgICAgZGF0YTogcmVxdWVzdC5wYXJzZXJcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmICh0eXBlb2YgcmVxdWVzdC5wYXJzZXIgPT09ICdvYmplY3QnICYmIHJlcXVlc3QucGFyc2VyKSB7XG4gICAgICAgIGNoZWNrVHlwZShwYXJzZXIsIHBhcnNlclBhcmFtZXRlcnMsICdwYXJzZXIgZm9yIGFzc2V0IFwiJyArIG5hbWUgKyAnXCInKVxuICAgICAgICBpZiAoISgnb25EYXRhJyBpbiBwYXJzZXIpKSB7XG4gICAgICAgICAgcmFpc2UoJ21pc3Npbmcgb25EYXRhIGNhbGxiYWNrIGZvciBwYXJzZXIgaW4gYXNzZXQgXCInICsgbmFtZSArICdcIicpXG4gICAgICAgIH1cbiAgICAgICAgcGFyc2VyID0ge1xuICAgICAgICAgIGRhdGE6IGdldFBhcnNlRnVuYygnb25EYXRhJyksXG4gICAgICAgICAgZG9uZTogZ2V0UGFyc2VGdW5jKCdvbkRvbmUnKVxuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByYWlzZSgnaW52YWxpZCBwYXJzZXIgZm9yIGFzc2V0IFwiJyArIG5hbWUgKyAnXCInKVxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBuYW1lOiBuYW1lLFxuICAgICAgdHlwZTogZ2V0UGFyYW1ldGVyKCd0eXBlJywgT2JqZWN0LmtleXMobG9hZGVycyksICd0ZXh0JyksXG4gICAgICBzdHJlYW06ICEhcmVxdWVzdC5zdHJlYW0sXG4gICAgICBjcmVkZW50aWFsczogISFyZXF1ZXN0LmNyZWRlbnRpYWxzLFxuICAgICAgc3JjOiBnZXRTdHJpbmcoJ3NyYycsIHRydWUsICcnKSxcbiAgICAgIHBhcnNlcjogcGFyc2VyXG4gICAgfVxuICB9KS5tYXAoZnVuY3Rpb24gKHJlcXVlc3QpIHtcbiAgICByZXR1cm4gKGxvYWRlcnNbcmVxdWVzdC50eXBlXSkocmVxdWVzdClcbiAgfSlcblxuICBmdW5jdGlvbiBhYm9ydCAobWVzc2FnZSkge1xuICAgIGlmIChzdGF0ZSA9PT0gU1RBVEVfRVJST1IgfHwgc3RhdGUgPT09IFNUQVRFX0NPTVBMRVRFKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgc3RhdGUgPSBTVEFURV9FUlJPUlxuICAgIHBlbmRpbmcuZm9yRWFjaChmdW5jdGlvbiAobG9hZGVyKSB7XG4gICAgICBsb2FkZXIuY2FuY2VsKClcbiAgICB9KVxuICAgIGlmIChvbkVycm9yKSB7XG4gICAgICBpZiAodHlwZW9mIG1lc3NhZ2UgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgIG9uRXJyb3IobmV3IEVycm9yKCdyZXNsOiAnICsgbWVzc2FnZSkpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBvbkVycm9yKG1lc3NhZ2UpXG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ3Jlc2wgZXJyb3I6JywgbWVzc2FnZSlcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBub3RpZnlQcm9ncmVzcyAobWVzc2FnZSkge1xuICAgIGlmIChzdGF0ZSA9PT0gU1RBVEVfRVJST1IgfHwgc3RhdGUgPT09IFNUQVRFX0NPTVBMRVRFKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICB2YXIgcHJvZ3Jlc3MgPSAwXG4gICAgdmFyIG51bVJlYWR5ID0gMFxuICAgIHBlbmRpbmcuZm9yRWFjaChmdW5jdGlvbiAobG9hZGVyKSB7XG4gICAgICBpZiAobG9hZGVyLnJlYWR5KSB7XG4gICAgICAgIG51bVJlYWR5ICs9IDFcbiAgICAgIH1cbiAgICAgIHByb2dyZXNzICs9IGxvYWRlci5wcm9ncmVzc1xuICAgIH0pXG5cbiAgICBpZiAobnVtUmVhZHkgPT09IHBlbmRpbmcubGVuZ3RoKSB7XG4gICAgICBzdGF0ZSA9IFNUQVRFX0NPTVBMRVRFXG4gICAgICBvbkRvbmUoYXNzZXRzKVxuICAgIH0gZWxzZSB7XG4gICAgICBpZiAob25Qcm9ncmVzcykge1xuICAgICAgICBvblByb2dyZXNzKHByb2dyZXNzIC8gcGVuZGluZy5sZW5ndGgsIG1lc3NhZ2UpXG4gICAgICB9XG4gICAgfVxuICB9XG59XG4iXX0= | |
require=(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){ | |
var GL_FLOAT = 5126 | |
function AttributeRecord () { | |
this.state = 0 | |
this.x = 0.0 | |
this.y = 0.0 | |
this.z = 0.0 | |
this.w = 0.0 | |
this.buffer = null | |
this.size = 0 | |
this.normalized = false | |
this.type = GL_FLOAT | |
this.offset = 0 | |
this.stride = 0 | |
this.divisor = 0 | |
} | |
module.exports = function wrapAttributeState ( | |
gl, | |
extensions, | |
limits, | |
bufferState, | |
stringStore) { | |
var NUM_ATTRIBUTES = limits.maxAttributes | |
var attributeBindings = new Array(NUM_ATTRIBUTES) | |
for (var i = 0; i < NUM_ATTRIBUTES; ++i) { | |
attributeBindings[i] = new AttributeRecord() | |
} | |
return { | |
Record: AttributeRecord, | |
scope: {}, | |
state: attributeBindings | |
} | |
} | |
},{}],2:[function(require,module,exports){ | |
var check = require('./util/check') | |
var isTypedArray = require('./util/is-typed-array') | |
var isNDArrayLike = require('./util/is-ndarray') | |
var values = require('./util/values') | |
var pool = require('./util/pool') | |
var arrayTypes = require('./constants/arraytypes.json') | |
var bufferTypes = require('./constants/dtypes.json') | |
var usageTypes = require('./constants/usage.json') | |
var GL_STATIC_DRAW = 0x88E4 | |
var GL_STREAM_DRAW = 0x88E0 | |
var GL_UNSIGNED_BYTE = 5121 | |
var GL_FLOAT = 5126 | |
function typedArrayCode (data) { | |
return arrayTypes[Object.prototype.toString.call(data)] | 0 | |
} | |
function copyArray (out, inp) { | |
for (var i = 0; i < inp.length; ++i) { | |
out[i] = inp[i] | |
} | |
} | |
function transpose ( | |
result, data, shapeX, shapeY, strideX, strideY, offset) { | |
var ptr = 0 | |
for (var i = 0; i < shapeX; ++i) { | |
for (var j = 0; j < shapeY; ++j) { | |
result[ptr++] = data[strideX * i + strideY * j + offset] | |
} | |
} | |
} | |
function flatten (result, data, dimension) { | |
var ptr = 0 | |
for (var i = 0; i < data.length; ++i) { | |
var v = data[i] | |
for (var j = 0; j < dimension; ++j) { | |
result[ptr++] = v[j] | |
} | |
} | |
} | |
module.exports = function wrapBufferState (gl, stats) { | |
var bufferCount = 0 | |
var bufferSet = {} | |
function REGLBuffer (type) { | |
this.id = bufferCount++ | |
this.buffer = gl.createBuffer() | |
this.type = type | |
this.usage = GL_STATIC_DRAW | |
this.byteLength = 0 | |
this.dimension = 1 | |
this.dtype = GL_UNSIGNED_BYTE | |
} | |
REGLBuffer.prototype.bind = function () { | |
gl.bindBuffer(this.type, this.buffer) | |
} | |
REGLBuffer.prototype.destroy = function () { | |
destroy(this) | |
} | |
var streamPool = [] | |
function createStream (type, data) { | |
var buffer = streamPool.pop() | |
if (!buffer) { | |
buffer = new REGLBuffer(type) | |
buffer.buffer = gl.createBuffer() | |
} | |
buffer.bind() | |
initBufferFromData(buffer, data, GL_STREAM_DRAW, 0, 1) | |
return buffer | |
} | |
function destroyStream (stream) { | |
streamPool.push(stream) | |
} | |
function initBufferFromTypedArray (buffer, data, usage) { | |
buffer.byteLength = data.byteLength | |
gl.bufferData(buffer.type, data, usage) | |
} | |
function initBufferFromData (buffer, data, usage, dtype, dimension) { | |
buffer.usage = usage | |
if (Array.isArray(data)) { | |
buffer.dtype = dtype || GL_FLOAT | |
if (data.length > 0) { | |
var flatData | |
if (Array.isArray(data[0])) { | |
buffer.dimension = data[0].length | |
flatData = pool.allocType( | |
buffer.dtype, | |
data.length * buffer.dimension) | |
flatten(flatData, data, buffer.dimension) | |
initBufferFromTypedArray(buffer, flatData, usage) | |
pool.freeType(flatData) | |
} else if (typeof data[0] === 'number') { | |
buffer.dimension = dimension | |
var typedData = pool.allocType(buffer.dtype, data.length) | |
copyArray(typedData, data) | |
initBufferFromTypedArray(buffer, typedData, usage) | |
pool.freeType(typedData) | |
} else if (isTypedArray(data[0])) { | |
buffer.dimension = data[0].length | |
buffer.dtype = dtype || typedArrayCode(data[0]) || GL_FLOAT | |
flatData = pool.allocType( | |
buffer.dtype, | |
data.length * buffer.dimension) | |
flatten(flatData, data, buffer.dimension) | |
initBufferFromTypedArray(buffer, flatData, usage) | |
pool.freeType(flatData) | |
} else { | |
check.raise('invalid buffer data') | |
} | |
} | |
} else if (isTypedArray(data)) { | |
buffer.dtype = dtype || typedArrayCode(data) | |
buffer.dimension = dimension | |
initBufferFromTypedArray(buffer, data, usage) | |
} else if (isNDArrayLike(data)) { | |
var shape = data.shape | |
var stride = data.stride | |
var offset = data.offset | |
var shapeX = 0 | |
var shapeY = 0 | |
var strideX = 0 | |
var strideY = 0 | |
if (shape.length === 1) { | |
shapeX = shape[0] | |
shapeY = 1 | |
strideX = stride[0] | |
strideY = 0 | |
} else if (shape.length === 2) { | |
shapeX = shape[0] | |
shapeY = shape[1] | |
strideX = stride[0] | |
strideY = stride[1] | |
} else { | |
check.raise('invalid shape') | |
} | |
buffer.dtype = dtype || typedArrayCode(data.data) || GL_FLOAT | |
buffer.dimension = shapeY | |
var transposeData = pool.allocType(buffer.dtype, shapeX * shapeY) | |
transpose(transposeData, | |
data.data, | |
shapeX, shapeY, | |
strideX, strideY, | |
offset) | |
initBufferFromTypedArray(buffer, transposeData, usage) | |
pool.freeType(transposeData) | |
} else { | |
check.raise('invalid buffer data') | |
} | |
} | |
function destroy (buffer) { | |
stats.bufferCount-- | |
var handle = buffer.buffer | |
check(handle, 'buffer must not be deleted already') | |
gl.deleteBuffer(handle) | |
buffer.buffer = null | |
delete bufferSet[buffer.id] | |
} | |
function createBuffer (options, type, deferInit) { | |
stats.bufferCount++ | |
var buffer = new REGLBuffer(type) | |
bufferSet[buffer.id] = buffer | |
function reglBuffer (options) { | |
var usage = GL_STATIC_DRAW | |
var data = null | |
var byteLength = 0 | |
var dtype = 0 | |
var dimension = 1 | |
if (Array.isArray(options) || | |
isTypedArray(options) || | |
isNDArrayLike(options)) { | |
data = options | |
} else if (typeof options === 'number') { | |
byteLength = options | 0 | |
} else if (options) { | |
check.type( | |
options, 'object', | |
'buffer arguments must be an object, a number or an array') | |
if ('data' in options) { | |
check( | |
data === null || | |
Array.isArray(data) || | |
isTypedArray(data) || | |
isNDArrayLike(data), | |
'invalid data for buffer') | |
data = options.data | |
} | |
if ('usage' in options) { | |
check.parameter(options.usage, usageTypes, 'invalid buffer usage') | |
usage = usageTypes[options.usage] | |
} | |
if ('type' in options) { | |
check.parameter(options.type, bufferTypes, 'invalid buffer type') | |
dtype = bufferTypes[options.type] | |
} | |
if ('dimension' in options) { | |
check.type(options.dimension, 'number', 'invalid dimension') | |
dimension = options.dimension | 0 | |
} | |
if ('length' in options) { | |
check.nni(byteLength, 'buffer length must be a nonnegative integer') | |
byteLength = options.length | 0 | |
} | |
} | |
buffer.bind() | |
if (!data) { | |
gl.bufferData(buffer.type, byteLength, usage) | |
buffer.dtype = dtype || GL_UNSIGNED_BYTE | |
buffer.usage = usage | |
buffer.dimension = dimension | |
buffer.byteLength = byteLength | |
} else { | |
initBufferFromData(buffer, data, usage, dtype, dimension) | |
} | |
return reglBuffer | |
} | |
function setSubData (data, offset) { | |
check(offset + data.byteLength <= buffer.byteLength, | |
'invalid buffer subdata call, buffer is too small. ' + ' Can\'t write data of size ' + data.byteLength + ' starting from offset ' + offset + ' to a buffer of size ' + buffer.byteLength) | |
gl.bufferSubData(buffer.type, offset, data) | |
} | |
function subdata (data, offset_) { | |
var offset = (offset_ || 0) | 0 | |
buffer.bind() | |
if (Array.isArray(data)) { | |
if (data.length > 0) { | |
if (typeof data[0] === 'number') { | |
var converted = pool.allocType(buffer.dtype, data.length) | |
copyArray(converted, data) | |
setSubData(converted, offset) | |
pool.freeType(converted) | |
} else if (Array.isArray(data[0]) || isTypedArray(data[0])) { | |
var dimension = data[0].length | |
var flatData = pool.allocType(buffer.dtype, data.length * dimension) | |
flatten(flatData, data, dimension) | |
setSubData(flatData, offset) | |
pool.freeType(flatData) | |
} else { | |
check.raise('invalid buffer data') | |
} | |
} | |
} else if (isTypedArray(data)) { | |
setSubData(data, offset) | |
} else if (isNDArrayLike(data)) { | |
var shape = data.shape | |
var stride = data.stride | |
var shapeX = 0 | |
var shapeY = 0 | |
var strideX = 0 | |
var strideY = 0 | |
if (shape.length === 1) { | |
shapeX = shape[0] | |
shapeY = 1 | |
strideX = stride[0] | |
strideY = 0 | |
} else if (shape.length === 2) { | |
shapeX = shape[0] | |
shapeY = shape[1] | |
strideX = stride[0] | |
strideY = stride[1] | |
} else { | |
check.raise('invalid shape') | |
} | |
var dtype = Array.isArray(data.data) | |
? buffer.dtype | |
: typedArrayCode(data.data) | |
var transposeData = pool.allocType(dtype, shapeX * shapeY) | |
transpose(transposeData, | |
data.data, | |
shapeX, shapeY, | |
strideX, strideY, | |
data.offset) | |
setSubData(transposeData, offset) | |
pool.freeType(transposeData) | |
} else { | |
check.raise('invalid data for buffer subdata') | |
} | |
return reglBuffer | |
} | |
if (!deferInit) { | |
reglBuffer(options) | |
} | |
reglBuffer._reglType = 'buffer' | |
reglBuffer._buffer = buffer | |
reglBuffer.subdata = subdata | |
reglBuffer.destroy = function () { destroy(buffer) } | |
return reglBuffer | |
} | |
return { | |
create: createBuffer, | |
createStream: createStream, | |
destroyStream: destroyStream, | |
clear: function () { | |
values(bufferSet).forEach(destroy) | |
}, | |
getBuffer: function (wrapper) { | |
if (wrapper && wrapper._buffer instanceof REGLBuffer) { | |
return wrapper._buffer | |
} | |
return null | |
}, | |
_initBuffer: initBufferFromData | |
} | |
} | |
},{"./constants/arraytypes.json":3,"./constants/dtypes.json":4,"./constants/usage.json":6,"./util/check":20,"./util/is-ndarray":25,"./util/is-typed-array":26,"./util/pool":28,"./util/values":31}],3:[function(require,module,exports){ | |
module.exports={ | |
"[object Int8Array]": 5120 | |
, "[object Int16Array]": 5122 | |
, "[object Int32Array]": 5124 | |
, "[object Uint8Array]": 5121 | |
, "[object Uint8ClampedArray]": 5121 | |
, "[object Uint16Array]": 5123 | |
, "[object Uint32Array]": 5125 | |
, "[object Float32Array]": 5126 | |
, "[object Float64Array]": 5121 | |
, "[object ArrayBuffer]": 5121 | |
} | |
},{}],4:[function(require,module,exports){ | |
module.exports={ | |
"int8": 5120 | |
, "int16": 5122 | |
, "int32": 5124 | |
, "uint8": 5121 | |
, "uint16": 5123 | |
, "uint32": 5125 | |
, "float": 5126 | |
, "float32": 5126 | |
} | |
},{}],5:[function(require,module,exports){ | |
module.exports={ | |
"points": 0, | |
"point": 0, | |
"lines": 1, | |
"line": 1, | |
"line loop": 2, | |
"line strip": 3, | |
"triangles": 4, | |
"triangle": 4, | |
"triangle strip": 5, | |
"triangle fan": 6 | |
} | |
},{}],6:[function(require,module,exports){ | |
module.exports={ | |
"static": 35044, | |
"dynamic": 35048, | |
"stream": 35040 | |
} | |
},{}],7:[function(require,module,exports){ | |
var check = require('./util/check') | |
var createEnvironment = require('./util/codegen') | |
var loop = require('./util/loop') | |
var isTypedArray = require('./util/is-typed-array') | |
var isNDArray = require('./util/is-ndarray') | |
var isArrayLike = require('./util/is-array-like') | |
var primTypes = require('./constants/primitives.json') | |
var glTypes = require('./constants/dtypes.json') | |
// "cute" names for vector components | |
var CUTE_COMPONENTS = 'xyzw'.split('') | |
var GL_UNSIGNED_BYTE = 5121 | |
var ATTRIB_STATE_POINTER = 1 | |
var ATTRIB_STATE_CONSTANT = 2 | |
var DYN_FUNC = 0 | |
var DYN_PROP = 1 | |
var DYN_CONTEXT = 2 | |
var DYN_STATE = 3 | |
var S_DITHER = 'dither' | |
var S_BLEND_ENABLE = 'blend.enable' | |
var S_BLEND_COLOR = 'blend.color' | |
var S_BLEND_EQUATION = 'blend.equation' | |
var S_BLEND_FUNC = 'blend.func' | |
var S_DEPTH_ENABLE = 'depth.enable' | |
var S_DEPTH_FUNC = 'depth.func' | |
var S_DEPTH_RANGE = 'depth.range' | |
var S_DEPTH_MASK = 'depth.mask' | |
var S_COLOR_MASK = 'colorMask' | |
var S_CULL_ENABLE = 'cull.enable' | |
var S_CULL_FACE = 'cull.face' | |
var S_FRONT_FACE = 'frontFace' | |
var S_LINE_WIDTH = 'lineWidth' | |
var S_POLYGON_OFFSET_ENABLE = 'polygonOffset.enable' | |
var S_POLYGON_OFFSET_OFFSET = 'polygonOffset.offset' | |
var S_SAMPLE_ALPHA = 'sample.alpha' | |
var S_SAMPLE_ENABLE = 'sample.enable' | |
var S_SAMPLE_COVERAGE = 'sample.coverage' | |
var S_STENCIL_ENABLE = 'stencil.enable' | |
var S_STENCIL_MASK = 'stencil.mask' | |
var S_STENCIL_FUNC = 'stencil.func' | |
var S_STENCIL_OPFRONT = 'stencil.opFront' | |
var S_STENCIL_OPBACK = 'stencil.opBack' | |
var S_SCISSOR_ENABLE = 'scissor.enable' | |
var S_SCISSOR_BOX = 'scissor.box' | |
var S_VIEWPORT = 'viewport' | |
var S_PROFILE = 'profile' | |
var S_FRAMEBUFFER = 'framebuffer' | |
var S_VERT = 'vert' | |
var S_FRAG = 'frag' | |
var S_ELEMENTS = 'elements' | |
var S_PRIMITIVE = 'primitive' | |
var S_COUNT = 'count' | |
var S_OFFSET = 'offset' | |
var S_INSTANCES = 'instances' | |
var SUFFIX_WIDTH = 'Width' | |
var SUFFIX_HEIGHT = 'Height' | |
var S_FRAMEBUFFER_WIDTH = S_FRAMEBUFFER + SUFFIX_WIDTH | |
var S_FRAMEBUFFER_HEIGHT = S_FRAMEBUFFER + SUFFIX_HEIGHT | |
var S_VIEWPORT_WIDTH = S_VIEWPORT + SUFFIX_WIDTH | |
var S_VIEWPORT_HEIGHT = S_VIEWPORT + SUFFIX_HEIGHT | |
var S_DRAWINGBUFFER = 'drawingBuffer' | |
var S_DRAWINGBUFFER_WIDTH = S_DRAWINGBUFFER + SUFFIX_WIDTH | |
var S_DRAWINGBUFFER_HEIGHT = S_DRAWINGBUFFER + SUFFIX_HEIGHT | |
var GL_ARRAY_BUFFER = 34962 | |
var GL_ELEMENT_ARRAY_BUFFER = 34963 | |
var GL_FRAGMENT_SHADER = 35632 | |
var GL_VERTEX_SHADER = 35633 | |
var GL_TEXTURE_2D = 0x0DE1 | |
var GL_TEXTURE_CUBE_MAP = 0x8513 | |
var GL_CULL_FACE = 0x0B44 | |
var GL_BLEND = 0x0BE2 | |
var GL_DITHER = 0x0BD0 | |
var GL_STENCIL_TEST = 0x0B90 | |
var GL_DEPTH_TEST = 0x0B71 | |
var GL_SCISSOR_TEST = 0x0C11 | |
var GL_POLYGON_OFFSET_FILL = 0x8037 | |
var GL_SAMPLE_ALPHA_TO_COVERAGE = 0x809E | |
var GL_SAMPLE_COVERAGE = 0x80A0 | |
var GL_FLOAT = 5126 | |
var GL_FLOAT_VEC2 = 35664 | |
var GL_FLOAT_VEC3 = 35665 | |
var GL_FLOAT_VEC4 = 35666 | |
var GL_INT = 5124 | |
var GL_INT_VEC2 = 35667 | |
var GL_INT_VEC3 = 35668 | |
var GL_INT_VEC4 = 35669 | |
var GL_BOOL = 35670 | |
var GL_BOOL_VEC2 = 35671 | |
var GL_BOOL_VEC3 = 35672 | |
var GL_BOOL_VEC4 = 35673 | |
var GL_FLOAT_MAT2 = 35674 | |
var GL_FLOAT_MAT3 = 35675 | |
var GL_FLOAT_MAT4 = 35676 | |
var GL_SAMPLER_2D = 35678 | |
var GL_SAMPLER_CUBE = 35680 | |
var GL_TRIANGLES = 4 | |
var GL_FRONT = 1028 | |
var GL_BACK = 1029 | |
var GL_CW = 0x0900 | |
var GL_CCW = 0x0901 | |
var GL_MIN_EXT = 0x8007 | |
var GL_MAX_EXT = 0x8008 | |
var GL_ALWAYS = 519 | |
var GL_KEEP = 7680 | |
var GL_ZERO = 0 | |
var GL_ONE = 1 | |
var GL_FUNC_ADD = 0x8006 | |
var GL_LESS = 513 | |
var GL_FRAMEBUFFER = 0x8D40 | |
var GL_COLOR_ATTACHMENT0 = 0x8CE0 | |
var blendFuncs = { | |
'0': 0, | |
'1': 1, | |
'zero': 0, | |
'one': 1, | |
'src color': 768, | |
'one minus src color': 769, | |
'src alpha': 770, | |
'one minus src alpha': 771, | |
'dst color': 774, | |
'one minus dst color': 775, | |
'dst alpha': 772, | |
'one minus dst alpha': 773, | |
'constant color': 32769, | |
'one minus constant color': 32770, | |
'constant alpha': 32771, | |
'one minus constant alpha': 32772, | |
'src alpha saturate': 776 | |
} | |
var compareFuncs = { | |
'never': 512, | |
'less': 513, | |
'<': 513, | |
'equal': 514, | |
'=': 514, | |
'==': 514, | |
'===': 514, | |
'lequal': 515, | |
'<=': 515, | |
'greater': 516, | |
'>': 516, | |
'notequal': 517, | |
'!=': 517, | |
'!==': 517, | |
'gequal': 518, | |
'>=': 518, | |
'always': 519 | |
} | |
var stencilOps = { | |
'0': 0, | |
'zero': 0, | |
'keep': 7680, | |
'replace': 7681, | |
'increment': 7682, | |
'decrement': 7683, | |
'increment wrap': 34055, | |
'decrement wrap': 34056, | |
'invert': 5386 | |
} | |
var shaderType = { | |
'frag': GL_FRAGMENT_SHADER, | |
'vert': GL_VERTEX_SHADER | |
} | |
var orientationType = { | |
'cw': GL_CW, | |
'ccw': GL_CCW | |
} | |
function isBufferArgs (x) { | |
return Array.isArray(x) || | |
isTypedArray(x) || | |
isNDArray(x) | |
} | |
// Make sure viewport is processed first | |
function sortState (state) { | |
return state.sort(function (a, b) { | |
if (a === S_VIEWPORT) { | |
return -1 | |
} else if (b === S_VIEWPORT) { | |
return 1 | |
} | |
return (a < b) ? -1 : 1 | |
}) | |
} | |
function Declaration (thisDep, contextDep, propDep, append) { | |
this.thisDep = thisDep | |
this.contextDep = contextDep | |
this.propDep = propDep | |
this.append = append | |
} | |
function isStatic (decl) { | |
return decl && !(decl.thisDep || decl.contextDep || decl.propDep) | |
} | |
function createStaticDecl (append) { | |
return new Declaration(false, false, false, append) | |
} | |
function createDynamicDecl (dyn, append) { | |
var type = dyn.type | |
if (type === DYN_FUNC) { | |
var numArgs = dyn.data.length | |
return new Declaration( | |
true, | |
numArgs >= 1, | |
numArgs >= 2, | |
append) | |
} else { | |
return new Declaration( | |
type === DYN_STATE, | |
type === DYN_CONTEXT, | |
type === DYN_PROP, | |
append) | |
} | |
} | |
var SCOPE_DECL = new Declaration(false, false, false, function () {}) | |
module.exports = function reglCore ( | |
gl, | |
stringStore, | |
extensions, | |
limits, | |
bufferState, | |
elementState, | |
textureState, | |
framebufferState, | |
uniformState, | |
attributeState, | |
shaderState, | |
drawState, | |
contextState, | |
timer, | |
config) { | |
var AttributeRecord = attributeState.Record | |
var blendEquations = { | |
'add': 32774, | |
'subtract': 32778, | |
'reverse subtract': 32779 | |
} | |
if (extensions.ext_blend_minmax) { | |
blendEquations.min = GL_MIN_EXT | |
blendEquations.max = GL_MAX_EXT | |
} | |
var extInstancing = extensions.angle_instanced_arrays | |
var extDrawBuffers = extensions.webgl_draw_buffers | |
// =================================================== | |
// =================================================== | |
// WEBGL STATE | |
// =================================================== | |
// =================================================== | |
var currentState = { | |
dirty: true, | |
profile: config.profile | |
} | |
var nextState = {} | |
var GL_STATE_NAMES = [] | |
var GL_FLAGS = {} | |
var GL_VARIABLES = {} | |
function propName (name) { | |
return name.replace('.', '_') | |
} | |
function stateFlag (sname, cap, init) { | |
var name = propName(sname) | |
GL_STATE_NAMES.push(sname) | |
nextState[name] = currentState[name] = !!init | |
GL_FLAGS[name] = cap | |
} | |
function stateVariable (sname, func, init) { | |
var name = propName(sname) | |
GL_STATE_NAMES.push(sname) | |
if (Array.isArray(init)) { | |
currentState[name] = init.slice() | |
nextState[name] = init.slice() | |
} else { | |
currentState[name] = nextState[name] = init | |
} | |
GL_VARIABLES[name] = func | |
} | |
// Dithering | |
stateFlag(S_DITHER, GL_DITHER) | |
// Blending | |
stateFlag(S_BLEND_ENABLE, GL_BLEND) | |
stateVariable(S_BLEND_COLOR, 'blendColor', [0, 0, 0, 0]) | |
stateVariable(S_BLEND_EQUATION, 'blendEquationSeparate', | |
[GL_FUNC_ADD, GL_FUNC_ADD]) | |
stateVariable(S_BLEND_FUNC, 'blendFuncSeparate', | |
[GL_ONE, GL_ZERO, GL_ONE, GL_ZERO]) | |
// Depth | |
stateFlag(S_DEPTH_ENABLE, GL_DEPTH_TEST, true) | |
stateVariable(S_DEPTH_FUNC, 'depthFunc', GL_LESS) | |
stateVariable(S_DEPTH_RANGE, 'depthRange', [0, 1]) | |
stateVariable(S_DEPTH_MASK, 'depthMask', true) | |
// Color mask | |
stateVariable(S_COLOR_MASK, S_COLOR_MASK, [true, true, true, true]) | |
// Face culling | |
stateFlag(S_CULL_ENABLE, GL_CULL_FACE) | |
stateVariable(S_CULL_FACE, 'cullFace', GL_BACK) | |
// Front face orientation | |
stateVariable(S_FRONT_FACE, S_FRONT_FACE, GL_CCW) | |
// Line width | |
stateVariable(S_LINE_WIDTH, S_LINE_WIDTH, 1) | |
// Polygon offset | |
stateFlag(S_POLYGON_OFFSET_ENABLE, GL_POLYGON_OFFSET_FILL) | |
stateVariable(S_POLYGON_OFFSET_OFFSET, 'polygonOffset', [0, 0]) | |
// Sample coverage | |
stateFlag(S_SAMPLE_ALPHA, GL_SAMPLE_ALPHA_TO_COVERAGE) | |
stateFlag(S_SAMPLE_ENABLE, GL_SAMPLE_COVERAGE) | |
stateVariable(S_SAMPLE_COVERAGE, 'sampleCoverage', [1, false]) | |
// Stencil | |
stateFlag(S_STENCIL_ENABLE, GL_STENCIL_TEST) | |
stateVariable(S_STENCIL_MASK, 'stencilMask', -1) | |
stateVariable(S_STENCIL_FUNC, 'stencilFunc', [GL_ALWAYS, 0, -1]) | |
stateVariable(S_STENCIL_OPFRONT, 'stencilOpSeparate', | |
[GL_FRONT, GL_KEEP, GL_KEEP, GL_KEEP]) | |
stateVariable(S_STENCIL_OPBACK, 'stencilOpSeparate', | |
[GL_BACK, GL_KEEP, GL_KEEP, GL_KEEP]) | |
// Scissor | |
stateFlag(S_SCISSOR_ENABLE, GL_SCISSOR_TEST) | |
stateVariable(S_SCISSOR_BOX, 'scissor', | |
[0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight]) | |
// Viewport | |
stateVariable(S_VIEWPORT, S_VIEWPORT, | |
[0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight]) | |
// =================================================== | |
// =================================================== | |
// ENVIRONMENT | |
// =================================================== | |
// =================================================== | |
var sharedState = { | |
gl: gl, | |
context: contextState, | |
strings: stringStore, | |
next: nextState, | |
current: currentState, | |
draw: drawState, | |
elements: elementState, | |
buffer: bufferState, | |
shader: shaderState, | |
attributes: attributeState.state, | |
uniforms: uniformState, | |
framebuffer: framebufferState, | |
extensions: extensions, | |
timer: timer, | |
isBufferArgs: isBufferArgs | |
} | |
var sharedConstants = { | |
primTypes: primTypes, | |
compareFuncs: compareFuncs, | |
blendFuncs: blendFuncs, | |
blendEquations: blendEquations, | |
stencilOps: stencilOps, | |
glTypes: glTypes, | |
orientationType: orientationType | |
} | |
check.optional(function () { | |
sharedState.isArrayLike = isArrayLike | |
}) | |
if (extDrawBuffers) { | |
sharedConstants.backBuffer = [GL_BACK] | |
sharedConstants.drawBuffer = loop(limits.maxDrawbuffers, function (i) { | |
if (i === 0) { | |
return [0] | |
} | |
return loop(i, function (j) { | |
return GL_COLOR_ATTACHMENT0 + j | |
}) | |
}) | |
} | |
var drawCallCounter = 0 | |
function createREGLEnvironment () { | |
var env = createEnvironment() | |
var link = env.link | |
var global = env.global | |
env.id = drawCallCounter++ | |
env.batchId = '0' | |
// link shared state | |
var SHARED = link(sharedState) | |
var shared = env.shared = { | |
props: 'a0' | |
} | |
Object.keys(sharedState).forEach(function (prop) { | |
shared[prop] = global.def(SHARED, '.', prop) | |
}) | |
// Inject runtime assertion stuff for debug builds | |
check.optional(function () { | |
env.CHECK = link(check) | |
env.commandStr = check.guessCommand() | |
env.command = link(env.commandStr) | |
env.assert = function (block, pred, message) { | |
block( | |
'if(!(', pred, '))', | |
this.CHECK, '.commandRaise(', link(message), ',', this.command, ');') | |
} | |
}) | |
// Copy GL state variables over | |
var nextVars = env.next = {} | |
var currentVars = env.current = {} | |
Object.keys(GL_VARIABLES).forEach(function (variable) { | |
if (Array.isArray(currentState[variable])) { | |
nextVars[variable] = global.def(shared.next, '.', variable) | |
currentVars[variable] = global.def(shared.current, '.', variable) | |
} | |
}) | |
// Initialize shared constants | |
var constants = env.constants = {} | |
Object.keys(sharedConstants).forEach(function (name) { | |
constants[name] = global.def(JSON.stringify(sharedConstants[name])) | |
}) | |
// Helper function for calling a block | |
env.invoke = function (block, x) { | |
switch (x.type) { | |
case DYN_FUNC: | |
var argList = [ | |
'this', | |
shared.context, | |
shared.props, | |
env.batchId | |
] | |
return block.def( | |
link(x.data), '.call(', | |
argList.slice(0, Math.max(x.data.length + 1, 4)), | |
')') | |
case DYN_PROP: | |
return block.def(shared.props, x.data) | |
case DYN_CONTEXT: | |
return block.def(shared.context, x.data) | |
case DYN_STATE: | |
return block.def('this', x.data) | |
} | |
} | |
env.attribCache = {} | |
var scopeAttribs = {} | |
env.scopeAttrib = function (name) { | |
var id = stringStore.id(name) | |
if (id in scopeAttribs) { | |
return scopeAttribs[id] | |
} | |
var binding = attributeState.scope[id] | |
if (!binding) { | |
binding = attributeState.scope[id] = new AttributeRecord() | |
} | |
var result = scopeAttribs[id] = link(binding) | |
return result | |
} | |
return env | |
} | |
// =================================================== | |
// =================================================== | |
// PARSING | |
// =================================================== | |
// =================================================== | |
function parseProfile (options) { | |
var staticOptions = options.static | |
var dynamicOptions = options.dynamic | |
var profileEnable | |
if (S_PROFILE in staticOptions) { | |
var value = !!staticOptions[S_PROFILE] | |
profileEnable = createStaticDecl(function (env, scope) { | |
return value | |
}) | |
profileEnable.enable = value | |
} else if (S_PROFILE in dynamicOptions) { | |
var dyn = dynamicOptions[S_PROFILE] | |
profileEnable = createDynamicDecl(dyn, function (env, scope) { | |
return env.invoke(scope, dyn) | |
}) | |
} | |
return profileEnable | |
} | |
function parseFramebuffer (options) { | |
var staticOptions = options.static | |
var dynamicOptions = options.dynamic | |
if (S_FRAMEBUFFER in staticOptions) { | |
var framebuffer = staticOptions[S_FRAMEBUFFER] | |
if (framebuffer) { | |
framebuffer = framebufferState.getFramebuffer(framebuffer) | |
check.command(framebuffer, 'invalid framebuffer object') | |
return createStaticDecl(function (env, block) { | |
var FRAMEBUFFER = env.link(framebuffer) | |
var shared = env.shared | |
block.set( | |
shared.framebuffer, | |
'.next', | |
FRAMEBUFFER) | |
var CONTEXT = shared.context | |
block.set( | |
CONTEXT, | |
'.' + S_FRAMEBUFFER_WIDTH, | |
FRAMEBUFFER + '.width') | |
block.set( | |
CONTEXT, | |
'.' + S_FRAMEBUFFER_HEIGHT, | |
FRAMEBUFFER + '.height') | |
return FRAMEBUFFER | |
}) | |
} else { | |
return createStaticDecl(function (env, scope) { | |
var shared = env.shared | |
scope.set( | |
shared.framebuffer, | |
'.next', | |
'null') | |
var CONTEXT = shared.context | |
scope.set( | |
CONTEXT, | |
'.' + S_FRAMEBUFFER_WIDTH, | |
CONTEXT + '.' + S_DRAWINGBUFFER_WIDTH) | |
scope.set( | |
CONTEXT, | |
'.' + S_FRAMEBUFFER_HEIGHT, | |
CONTEXT + '.' + S_DRAWINGBUFFER_HEIGHT) | |
return 'null' | |
}) | |
} | |
} else if (S_FRAMEBUFFER in dynamicOptions) { | |
var dyn = dynamicOptions[S_FRAMEBUFFER] | |
return createDynamicDecl(dyn, function (env, scope) { | |
var FRAMEBUFFER_FUNC = env.invoke(scope, dyn) | |
var shared = env.shared | |
var FRAMEBUFFER_STATE = shared.framebuffer | |
var FRAMEBUFFER = scope.def( | |
FRAMEBUFFER_STATE, '.getFramebuffer(', FRAMEBUFFER_FUNC, ')') | |
check.optional(function () { | |
env.assert(scope, | |
'!' + FRAMEBUFFER_FUNC + '||' + FRAMEBUFFER, | |
'invalid framebuffer object') | |
}) | |
scope.set( | |
FRAMEBUFFER_STATE, | |
'.next', | |
FRAMEBUFFER) | |
var CONTEXT = shared.context | |
scope.set( | |
CONTEXT, | |
'.' + S_FRAMEBUFFER_WIDTH, | |
FRAMEBUFFER + '?' + FRAMEBUFFER + '.width:' + | |
CONTEXT + '.' + S_DRAWINGBUFFER_WIDTH) | |
scope.set( | |
CONTEXT, | |
'.' + S_FRAMEBUFFER_HEIGHT, | |
FRAMEBUFFER + | |
'?' + FRAMEBUFFER + '.height:' + | |
CONTEXT + '.' + S_DRAWINGBUFFER_HEIGHT) | |
return FRAMEBUFFER | |
}) | |
} else { | |
return null | |
} | |
} | |
function parseViewportScissor (options, framebuffer) { | |
var staticOptions = options.static | |
var dynamicOptions = options.dynamic | |
function parseBox (param) { | |
if (param in staticOptions) { | |
var box = staticOptions[param] | |
check.commandType(box, 'object', 'invalid ' + param) | |
var isStatic = true | |
var x = box.x | 0 | |
var y = box.y | 0 | |
check.command(x >= 0 && y >= 0, 'invalid ' + param) | |
var w, h | |
if ('width' in box) { | |
w = box.width | 0 | |
check.command(w > 0, 'invalid ' + param) | |
} else { | |
isStatic = false | |
} | |
if ('height' in box) { | |
h = box.height | 0 | |
check.command(h > 0, 'invalid ' + param) | |
} else { | |
isStatic = false | |
} | |
return new Declaration( | |
!isStatic && framebuffer && framebuffer.thisDep, | |
!isStatic && framebuffer && framebuffer.contextDep, | |
!isStatic && framebuffer && framebuffer.propDep, | |
function (env, scope) { | |
var CONTEXT = env.shared.context | |
var BOX_W = w | |
if (!('width' in box)) { | |
BOX_W = scope.def(CONTEXT, '.', S_FRAMEBUFFER_WIDTH, '-', x) | |
} else { | |
check.command(w > 0, 'invalid ' + param) | |
} | |
var BOX_H = h | |
if (!('height' in box)) { | |
BOX_H = scope.def(CONTEXT, '.', S_FRAMEBUFFER_HEIGHT, '-', y) | |
} else { | |
check.command(h > 0, 'invalid ' + param) | |
} | |
return [x, y, BOX_W, BOX_H] | |
}) | |
} else if (param in dynamicOptions) { | |
var dynBox = dynamicOptions[param] | |
var result = createDynamicDecl(dynBox, function (env, scope) { | |
var BOX = env.invoke(scope, dynBox) | |
check.optional(function () { | |
env.assert(scope, | |
BOX + '&&typeof ' + BOX + '==="object"', | |
'invalid ' + param) | |
}) | |
var CONTEXT = env.shared.context | |
var BOX_X = scope.def(BOX, '.x|0') | |
var BOX_Y = scope.def(BOX, '.y|0') | |
var BOX_W = scope.def( | |
'"width" in ', BOX, '?', BOX, '.width|0:', | |
'(', CONTEXT, '.', S_FRAMEBUFFER_WIDTH, '-', BOX_X, ')') | |
var BOX_H = scope.def( | |
'"height" in ', BOX, '?', BOX, '.height|0:', | |
'(', CONTEXT, '.', S_FRAMEBUFFER_HEIGHT, '-', BOX_Y, ')') | |
check.optional(function () { | |
env.assert(scope, | |
BOX_X + '>=0&&' + | |
BOX_Y + '>=0&&' + | |
BOX_W + '>0&&' + | |
BOX_H + '>0', | |
'invalid ' + param) | |
}) | |
return [BOX_X, BOX_Y, BOX_W, BOX_H] | |
}) | |
if (framebuffer) { | |
result.thisDep = result.thisDep || framebuffer.thisDep | |
result.contextDep = result.contextDep || framebuffer.contextDep | |
result.propDep = result.propDep || framebuffer.propDep | |
} | |
return result | |
} else if (framebuffer) { | |
return new Declaration( | |
framebuffer.thisDep, | |
framebuffer.contextDep, | |
framebuffer.propDep, | |
function (env, scope) { | |
var CONTEXT = env.shared.context | |
return [ | |
0, 0, | |
scope.def(CONTEXT, '.', S_FRAMEBUFFER_WIDTH), | |
scope.def(CONTEXT, '.', S_FRAMEBUFFER_HEIGHT)] | |
}) | |
} else { | |
return null | |
} | |
} | |
var viewport = parseBox(S_VIEWPORT) | |
if (viewport) { | |
var prevViewport = viewport | |
viewport = new Declaration( | |
viewport.thisDep, | |
viewport.contextDep, | |
viewport.propDep, | |
function (env, scope) { | |
var VIEWPORT = prevViewport.append(env, scope) | |
var CONTEXT = env.shared.context | |
scope.set( | |
CONTEXT, | |
'.' + S_VIEWPORT_WIDTH, | |
VIEWPORT[2]) | |
scope.set( | |
CONTEXT, | |
'.' + S_VIEWPORT_HEIGHT, | |
VIEWPORT[3]) | |
return VIEWPORT | |
}) | |
} | |
return { | |
viewport: viewport, | |
scissor_box: parseBox(S_SCISSOR_BOX) | |
} | |
} | |
function parseProgram (options) { | |
var staticOptions = options.static | |
var dynamicOptions = options.dynamic | |
function parseShader (name) { | |
if (name in staticOptions) { | |
var id = stringStore.id(staticOptions[name]) | |
check.optional(function () { | |
shaderState.shader(shaderType[name], id) | |
}) | |
var result = createStaticDecl(function () { | |
return id | |
}) | |
result.id = id | |
return result | |
} else if (name in dynamicOptions) { | |
var dyn = dynamicOptions[name] | |
return createDynamicDecl(dyn, function (env, scope) { | |
var str = env.invoke(scope, dyn) | |
var id = scope.def(env.shared.strings, '.id(', str, ')') | |
check.optional(function () { | |
scope( | |
env.shared.shader, '.shader(', | |
shaderType[name], ',', | |
id, ',', | |
env.command, ');') | |
}) | |
return id | |
}) | |
} | |
return null | |
} | |
var frag = parseShader(S_FRAG) | |
var vert = parseShader(S_VERT) | |
var program = null | |
var progVar | |
if (isStatic(frag) && isStatic(vert)) { | |
program = shaderState.program(vert.id, frag.id) | |
progVar = createStaticDecl(function (env, scope) { | |
return env.link(program) | |
}) | |
} else { | |
progVar = new Declaration( | |
(frag && frag.thisDep) || (vert && vert.thisDep), | |
(frag && frag.contextDep) || (vert && vert.contextDep), | |
(frag && frag.propDep) || (vert && vert.propDep), | |
function (env, scope) { | |
var SHADER_STATE = env.shared.shader | |
var fragId | |
if (frag) { | |
fragId = frag.append(env, scope) | |
} else { | |
fragId = scope.def(SHADER_STATE, '.', S_FRAG) | |
} | |
var vertId | |
if (vert) { | |
vertId = vert.append(env, scope) | |
} else { | |
vertId = scope.def(SHADER_STATE, '.', S_VERT) | |
} | |
var progDef = SHADER_STATE + '.program(' + vertId + ',' + fragId | |
check.optional(function () { | |
progDef += ',' + env.command | |
}) | |
return scope.def(progDef + ')') | |
}) | |
} | |
return { | |
frag: frag, | |
vert: vert, | |
progVar: progVar, | |
program: program | |
} | |
} | |
function parseDraw (options) { | |
var staticOptions = options.static | |
var dynamicOptions = options.dynamic | |
function parseElements () { | |
if (S_ELEMENTS in staticOptions) { | |
var elements = staticOptions[S_ELEMENTS] | |
if (isBufferArgs(elements)) { | |
elements = elementState.getElements(elementState.create(elements)) | |
} else if (elements) { | |
elements = elementState.getElements(elements) | |
check.command(elements, 'invalid elements') | |
} | |
var result = createStaticDecl(function (env, scope) { | |
if (elements) { | |
var result = env.link(elements) | |
env.ELEMENTS = result | |
return result | |
} | |
env.ELEMENTS = null | |
return null | |
}) | |
result.value = elements | |
return result | |
} else if (S_ELEMENTS in dynamicOptions) { | |
var dyn = dynamicOptions[S_ELEMENTS] | |
return createDynamicDecl(dyn, function (env, scope) { | |
var shared = env.shared | |
var IS_BUFFER_ARGS = shared.isBufferArgs | |
var ELEMENT_STATE = shared.elements | |
var elementDefn = env.invoke(scope, dyn) | |
var elements = scope.def('null') | |
var elementStream = scope.def(IS_BUFFER_ARGS, '(', elementDefn, ')') | |
var ifte = env.cond(elementStream) | |
.then(elements, '=', ELEMENT_STATE, '.createStream(', elementDefn, ');') | |
.else(elements, '=', ELEMENT_STATE, '.getElements(', elementDefn, ');') | |
check.optional(function () { | |
env.assert(ifte.else, | |
'!' + elementDefn + '||' + elements, | |
'invalid elements') | |
}) | |
scope.entry(ifte) | |
scope.exit( | |
env.cond(elementStream) | |
.then(ELEMENT_STATE, '.destroyStream(', elements, ');')) | |
env.ELEMENTS = elements | |
return elements | |
}) | |
} | |
return null | |
} | |
var elements = parseElements() | |
function parsePrimitive () { | |
if (S_PRIMITIVE in staticOptions) { | |
var primitive = staticOptions[S_PRIMITIVE] | |
check.commandParameter(primitive, primTypes, 'invalid primitve') | |
return createStaticDecl(function (env, scope) { | |
return primTypes[primitive] | |
}) | |
} else if (S_PRIMITIVE in dynamicOptions) { | |
var dynPrimitive = dynamicOptions[S_PRIMITIVE] | |
return createDynamicDecl(dynPrimitive, function (env, scope) { | |
var PRIM_TYPES = env.constants.primTypes | |
var prim = env.invoke(scope, dynPrimitive) | |
check.optional(function () { | |
env.assert(scope, | |
prim + ' in ' + PRIM_TYPES, | |
'invalid primitive, must be one of ' + Object.keys(primTypes)) | |
}) | |
return scope.def(PRIM_TYPES, '[', prim, ']') | |
}) | |
} else if (elements) { | |
if (isStatic(elements)) { | |
if (elements.value) { | |
return createStaticDecl(function (env, scope) { | |
return scope.def(env.ELEMENTS, '.primType') | |
}) | |
} else { | |
return createStaticDecl(function () { | |
return GL_TRIANGLES | |
}) | |
} | |
} else { | |
return new Declaration( | |
elements.thisDep, | |
elements.contextDep, | |
elements.propDep, | |
function (env, scope) { | |
var elements = env.ELEMENTS | |
return scope.def(elements, '?', elements, '.primType:', GL_TRIANGLES) | |
}) | |
} | |
} | |
return null | |
} | |
function parseParam (param, isOffset) { | |
if (param in staticOptions) { | |
var value = staticOptions[param] | 0 | |
check.command(!isOffset || value >= 0, 'invalid ' + param) | |
return createStaticDecl(function (env, scope) { | |
if (isOffset) { | |
env.OFFSET = value | |
} | |
return value | |
}) | |
} else if (param in dynamicOptions) { | |
var dynValue = dynamicOptions[param] | |
return createDynamicDecl(dynValue, function (env, scope) { | |
var result = env.invoke(scope, dynValue) | |
if (isOffset) { | |
env.OFFSET = result | |
check.optional(function () { | |
env.assert(scope, | |
result + '>=0', | |
'invalid ' + param) | |
}) | |
} | |
return result | |
}) | |
} else if (isOffset && elements) { | |
return createStaticDecl(function (env, scope) { | |
env.OFFSET = '0' | |
return 0 | |
}) | |
} | |
return null | |
} | |
var OFFSET = parseParam(S_OFFSET, true) | |
function parseVertCount () { | |
if (S_COUNT in staticOptions) { | |
var count = staticOptions[S_COUNT] | 0 | |
check.command( | |
typeof count === 'number' && count >= 0, 'invalid vertex count') | |
return createStaticDecl(function () { | |
return count | |
}) | |
} else if (S_COUNT in dynamicOptions) { | |
var dynCount = dynamicOptions[S_COUNT] | |
return createDynamicDecl(dynCount, function (env, scope) { | |
var result = env.invoke(scope, dynCount) | |
check.optional(function () { | |
env.assert(scope, | |
'typeof ' + result + '==="number"&&' + | |
result + '>=0&&' + | |
result + '===(' + result + '|0)', | |
'invalid vertex count') | |
}) | |
return result | |
}) | |
} else if (elements) { | |
if (isStatic(elements)) { | |
if (elements) { | |
if (OFFSET) { | |
return new Declaration( | |
OFFSET.thisDep, | |
OFFSET.contextDep, | |
OFFSET.propDep, | |
function (env, scope) { | |
var result = scope.def( | |
env.ELEMENTS, '.vertCount-', env.OFFSET) | |
check.optional(function () { | |
env.assert(scope, | |
result + '>=0', | |
'invalid vertex offset/element buffer too small') | |
}) | |
return result | |
}) | |
} else { | |
return createStaticDecl(function (env, scope) { | |
return scope.def(env.ELEMENTS, '.vertCount') | |
}) | |
} | |
} else { | |
var result = createStaticDecl(function () { | |
return -1 | |
}) | |
check.optional(function () { | |
result.MISSING = true | |
}) | |
return result | |
} | |
} else { | |
var variable = new Declaration( | |
elements.thisDep || OFFSET.thisDep, | |
elements.contextDep || OFFSET.contextDep, | |
elements.propDep || OFFSET.propDep, | |
function (env, scope) { | |
var elements = env.ELEMENTS | |
if (env.OFFSET) { | |
return scope.def(elements, '?', elements, '.vertCount-', | |
env.OFFSET, ':-1') | |
} | |
return scope.def(elements, '?', elements, '.vertCount:-1') | |
}) | |
check.optional(function () { | |
variable.DYNAMIC = true | |
}) | |
return variable | |
} | |
} | |
return null | |
} | |
return { | |
elements: elements, | |
primitive: parsePrimitive(), | |
count: parseVertCount(), | |
instances: parseParam(S_INSTANCES, false), | |
offset: OFFSET | |
} | |
} | |
function parseGLState (options) { | |
var staticOptions = options.static | |
var dynamicOptions = options.dynamic | |
var STATE = {} | |
GL_STATE_NAMES.forEach(function (prop) { | |
var param = propName(prop) | |
function parseParam (parseStatic, parseDynamic) { | |
if (prop in staticOptions) { | |
var value = parseStatic(staticOptions[prop]) | |
STATE[param] = createStaticDecl(function () { | |
return value | |
}) | |
} else if (prop in dynamicOptions) { | |
var dyn = dynamicOptions[prop] | |
STATE[param] = createDynamicDecl(dyn, function (env, scope) { | |
return parseDynamic(env, scope, env.invoke(scope, dyn)) | |
}) | |
} | |
} | |
switch (prop) { | |
case S_CULL_ENABLE: | |
case S_BLEND_ENABLE: | |
case S_DITHER: | |
case S_STENCIL_ENABLE: | |
case S_DEPTH_ENABLE: | |
case S_SCISSOR_ENABLE: | |
case S_POLYGON_OFFSET_ENABLE: | |
case S_SAMPLE_ALPHA: | |
case S_SAMPLE_ENABLE: | |
case S_DEPTH_MASK: | |
return parseParam( | |
function (value) { | |
check.commandType(value, 'boolean', prop) | |
return value | |
}, | |
function (env, scope, value) { | |
check.optional(function () { | |
env.assert(scope, | |
'typeof ' + value + '==="boolean"', | |
'invalid flag ' + prop) | |
}) | |
return value | |
}) | |
case S_DEPTH_FUNC: | |
return parseParam( | |
function (value) { | |
check.commandParameter(value, compareFuncs, 'invalid ' + prop) | |
return compareFuncs[value] | |
}, | |
function (env, scope, value) { | |
var COMPARE_FUNCS = env.constants.compareFuncs | |
check.optional(function () { | |
env.assert(scope, | |
value + ' in ' + COMPARE_FUNCS, | |
'invalid ' + prop + ', must be one of ' + Object.keys(compareFuncs)) | |
}) | |
return scope.def(COMPARE_FUNCS, '[', value, ']') | |
}) | |
case S_DEPTH_RANGE: | |
return parseParam( | |
function (value) { | |
check.command( | |
isArrayLike(value) && | |
value.length === 2 && | |
typeof value[0] === 'number' && | |
typeof value[1] === 'number' && | |
value[0] <= value[1], | |
'depth range is 2d array') | |
return value | |
}, | |
function (env, scope, value) { | |
check.optional(function () { | |
env.assert(scope, | |
env.shared.isArrayLike + '(' + value + ')&&' + | |
value + '.length===2&&' + | |
'typeof ' + value + '[0]==="number"&&' + | |
'typeof ' + value + '[1]==="number"&&' + | |
value + '[0]<=' + value + '[1]', | |
'depth range must be a 2d array') | |
}) | |
var Z_NEAR = scope.def('+', value, '[0]') | |
var Z_FAR = scope.def('+', value, '[1]') | |
return [Z_NEAR, Z_FAR] | |
}) | |
case S_BLEND_FUNC: | |
return parseParam( | |
function (value) { | |
check.commandType(value, 'object', 'blend.func') | |
var srcRGB = ('srcRGB' in value ? value.srcRGB : value.src) | |
var srcAlpha = ('srcAlpha' in value ? value.srcAlpha : value.src) | |
var dstRGB = ('dstRGB' in value ? value.dstRGB : value.dst) | |
var dstAlpha = ('dstAlpha' in value ? value.dstAlpha : value.dst) | |
check.commandParameter(srcRGB, blendFuncs, param + '.srcRGB') | |
check.commandParameter(srcAlpha, blendFuncs, param + '.srcAlpha') | |
check.commandParameter(dstRGB, blendFuncs, param + '.dstRGB') | |
check.commandParameter(dstAlpha, blendFuncs, param + '.dstAlpha') | |
return [ | |
blendFuncs[srcRGB], | |
blendFuncs[dstRGB], | |
blendFuncs[srcAlpha], | |
blendFuncs[dstAlpha] | |
] | |
}, | |
function (env, scope, value) { | |
var BLEND_FUNCS = env.constants.blendFuncs | |
check.optional(function () { | |
env.assert(scope, | |
value + '&&typeof ' + value + '==="object"', | |
'invalid blend func, must be an object') | |
}) | |
function read (prefix, suffix) { | |
var func = scope.def( | |
'"', prefix, suffix, '" in ', value, | |
'?', value, '.', prefix, suffix, | |
':', value, '.', prefix) | |
check.optional(function () { | |
env.assert(scope, | |
func + ' in ' + BLEND_FUNCS, | |
'invalid ' + prop + '.' + prefix + suffix + ', must be one of ' + Object.keys(blendFuncs)) | |
}) | |
return scope.def(BLEND_FUNCS, '[', func, ']') | |
} | |
var SRC_RGB = read('src', 'RGB') | |
var SRC_ALPHA = read('src', 'Alpha') | |
var DST_RGB = read('dst', 'RGB') | |
var DST_ALPHA = read('dst', 'Alpha') | |
return [SRC_RGB, DST_RGB, SRC_ALPHA, DST_ALPHA] | |
}) | |
case S_BLEND_EQUATION: | |
return parseParam( | |
function (value) { | |
if (typeof value === 'string') { | |
check.commandParameter(value, blendEquations, 'invalid ' + prop) | |
return [ | |
blendEquations[value], | |
blendEquations[value] | |
] | |
} else if (typeof value === 'object') { | |
check.commandParameter( | |
value.rgb, blendEquations, prop + '.rgb') | |
check.commandParameter( | |
value.alpha, blendEquations, prop + '.alpha') | |
return [ | |
blendEquations[value.rgb], | |
blendEquations[value.alpha] | |
] | |
} else { | |
check.commandRaise('invalid blend.equation') | |
} | |
}, | |
function (env, scope, value) { | |
var BLEND_EQUATIONS = env.constants.blendEquations | |
var RGB = scope.def() | |
var ALPHA = scope.def() | |
var ifte = env.cond('typeof ', value, '==="string"') | |
check.optional(function () { | |
function checkProp (block, name, value) { | |
env.assert(block, | |
value + ' in ' + BLEND_EQUATIONS, | |
'invalid ' + name + ', must be one of ' + Object.keys(blendEquations)) | |
} | |
checkProp(ifte.then, prop, value) | |
env.assert(ifte.else, | |
value + '&&typeof ' + value + '==="object"', | |
'invalid ' + prop) | |
checkProp(ifte.else, prop + '.rgb', value + '.rgb') | |
checkProp(ifte.else, prop + '.alpha', value + '.alpha') | |
}) | |
ifte.then( | |
RGB, '=', ALPHA, '=', BLEND_EQUATIONS, '[', value, '];') | |
ifte.else( | |
RGB, '=', BLEND_EQUATIONS, '[', value, '.rgb];', | |
ALPHA, '=', BLEND_EQUATIONS, '[', value, '.alpha];') | |
scope(ifte) | |
return [RGB, ALPHA] | |
}) | |
case S_BLEND_COLOR: | |
return parseParam( | |
function (value) { | |
check.command( | |
isArrayLike(value) && | |
value.length === 4, | |
'blend.color must be a 4d array') | |
return loop(4, function (i) { | |
return +value[i] | |
}) | |
}, | |
function (env, scope, value) { | |
check.optional(function () { | |
env.assert(scope, | |
env.shared.isArrayLike + '(' + value + ')&&' + | |
value + '.length===4', | |
'blend.color must be a 4d array') | |
}) | |
return loop(4, function (i) { | |
return scope.def('+', value, '[', i, ']') | |
}) | |
}) | |
case S_STENCIL_MASK: | |
return parseParam( | |
function (value) { | |
check.commandType(value, 'number', param) | |
return value | 0 | |
}, | |
function (env, scope, value) { | |
check.optional(function () { | |
env.assert(scope, | |
'typeof ' + value + '==="number"', | |
'invalid stencil.mask') | |
}) | |
return scope.def(value, '|0') | |
}) | |
case S_STENCIL_FUNC: | |
return parseParam( | |
function (value) { | |
check.commandType(value, 'object', param) | |
var cmp = value.cmp || 'keep' | |
var ref = value.ref || 0 | |
var mask = 'mask' in value ? value.mask : -1 | |
check.commandParameter(cmp, compareFuncs, prop + '.cmp') | |
check.commandType(ref, 'number', prop + '.ref') | |
check.commandType(mask, 'number', prop + '.mask') | |
return [ | |
compareFuncs[cmp], | |
ref, | |
mask | |
] | |
}, | |
function (env, scope, value) { | |
var COMPARE_FUNCS = env.constants.compareFuncs | |
check.optional(function () { | |
function assert () { | |
env.assert(scope, | |
Array.prototype.join.call(arguments, ''), | |
'invalid stencil.func') | |
} | |
assert(value + '&&typeof ', value, '==="object"') | |
assert('!("cmp" in ', value, ')||(', | |
value, '.cmp in ', COMPARE_FUNCS, ')') | |
}) | |
var cmp = scope.def( | |
'"cmp" in ', value, | |
'?', COMPARE_FUNCS, '[', value, '.cmp]', | |
':', GL_KEEP) | |
var ref = scope.def(value, '.ref|0') | |
var mask = scope.def( | |
'"mask" in ', value, | |
'?', value, '.mask|0:-1') | |
return [cmp, ref, mask] | |
}) | |
case S_STENCIL_OPFRONT: | |
case S_STENCIL_OPBACK: | |
return parseParam( | |
function (value) { | |
check.commandType(value, 'object', param) | |
var fail = value.fail || 'keep' | |
var zfail = value.zfail || 'keep' | |
var pass = value.pass || 'keep' | |
check.commandParameter(fail, stencilOps, prop + '.fail') | |
check.commandParameter(zfail, stencilOps, prop + '.zfail') | |
check.commandParameter(pass, stencilOps, prop + '.pass') | |
return [ | |
prop === S_STENCIL_OPBACK ? GL_BACK : GL_FRONT, | |
stencilOps[fail], | |
stencilOps[zfail], | |
stencilOps[pass] | |
] | |
}, | |
function (env, scope, value) { | |
var STENCIL_OPS = env.constants.stencilOps | |
check.optional(function () { | |
env.assert(scope, | |
value + '&&typeof ' + value + '==="object"', | |
'invalid ' + prop) | |
}) | |
function read (name) { | |
check.optional(function () { | |
env.assert(scope, | |
'!("' + name + '" in ' + value + ')||' + | |
'(' + value + '.' + name + ' in ' + STENCIL_OPS + ')', | |
'invalid ' + prop + '.' + name + ', must be one of ' + Object.keys(stencilOps)) | |
}) | |
return scope.def( | |
'"', name, '" in ', value, | |
'?', STENCIL_OPS, '[', value, '.', name, ']:', | |
GL_KEEP) | |
} | |
return [ | |
prop === S_STENCIL_OPBACK ? GL_BACK : GL_FRONT, | |
read('fail'), | |
read('zfail'), | |
read('pass') | |
] | |
}) | |
case S_POLYGON_OFFSET_OFFSET: | |
return parseParam( | |
function (value) { | |
check.commandType(value, 'object', param) | |
var factor = value.factor | 0 | |
var units = value.units | 0 | |
check.commandType(factor, 'number', param + '.factor') | |
check.commandType(units, 'number', param + '.units') | |
return [factor, units] | |
}, | |
function (env, scope, value) { | |
check.optional(function () { | |
env.assert(scope, | |
value + '&&typeof ' + value + '==="object"', | |
'invalid ' + prop) | |
}) | |
var FACTOR = scope.def(value, '.factor|0') | |
var UNITS = scope.def(value, '.units|0') | |
return [FACTOR, UNITS] | |
}) | |
case S_CULL_FACE: | |
return parseParam( | |
function (value) { | |
var face = 0 | |
if (value === 'front') { | |
face = GL_FRONT | |
} else if (value === 'back') { | |
face = GL_BACK | |
} | |
check.command(!!face, param) | |
return face | |
}, | |
function (env, scope, value) { | |
check.optional(function () { | |
env.assert(scope, | |
value + '==="front"||' + | |
value + '==="back"', | |
'invalid cull.face') | |
}) | |
return scope.def(value, '==="front"?', GL_FRONT, ':', GL_BACK) | |
}) | |
case S_LINE_WIDTH: | |
return parseParam( | |
function (value) { | |
check.command( | |
typeof value === 'number' && | |
value >= limits.lineWidthDims[0] && | |
value <= limits.lineWidthDims[1], | |
'invalid line width, must positive number between ' + | |
limits.lineWidthDims[0] + ' and ' + limits.lineWidthDims[1]) | |
return value | |
}, | |
function (env, scope, value) { | |
check.optional(function () { | |
env.assert(scope, | |
'typeof ' + value + '==="number"&&' + | |
value + '>=' + limits.lineWidthDims[0] + '&&' + | |
value + '<=' + limits.lineWidthDims[1], | |
'invalid line width') | |
}) | |
return value | |
}) | |
case S_FRONT_FACE: | |
return parseParam( | |
function (value) { | |
check.commandParameter(value, orientationType, param) | |
return orientationType[value] | |
}, | |
function (env, scope, value) { | |
check.optional(function () { | |
env.assert(scope, | |
value + '==="cw"||' + | |
value + '==="ccw"', | |
'invalid frontFace, must be one of cw,ccw') | |
}) | |
return scope.def(value + '==="cw"?' + GL_CW + ':' + GL_CCW) | |
}) | |
case S_COLOR_MASK: | |
return parseParam( | |
function (value) { | |
check.command( | |
isArrayLike(value) && value.length === 4, | |
'color.mask must be length 4 array') | |
return value.map(function (v) { return !!v }) | |
}, | |
function (env, scope, value) { | |
check.optional(function () { | |
env.assert(scope, | |
env.shared.isArrayLike + '(' + value + ')&&' + | |
value + '.length===4', | |
'invalid color.mask') | |
}) | |
return loop(4, function (i) { | |
return '!!' + value + '[' + i + ']' | |
}) | |
}) | |
case S_SAMPLE_COVERAGE: | |
return parseParam( | |
function (value) { | |
check.command(typeof value === 'object' && value, param) | |
var sampleValue = 'value' in value ? value.value : 1 | |
var sampleInvert = !!value.invert | |
check.command( | |
typeof sampleValue === 'number' && | |
sampleValue >= 0 && sampleValue <= 1, | |
'sample.coverage.value must be a number between 0 and 1') | |
return [sampleValue, sampleInvert] | |
}, | |
function (env, scope, value) { | |
check.optional(function () { | |
env.assert(scope, | |
value + '&&typeof ' + value + '==="object"', | |
'invalid sample.coverage') | |
}) | |
var VALUE = scope.def( | |
'"value" in ', value, '?+', value, '.value:1') | |
var INVERT = scope.def('!!', value, '.invert') | |
return [VALUE, INVERT] | |
}) | |
} | |
}) | |
return STATE | |
} | |
function parseOptions (options) { | |
var staticOptions = options.static | |
var dynamicOptions = options.dynamic | |
check.optional(function () { | |
var command = check.guessCommand() | |
var KEY_NAMES = [ | |
S_FRAMEBUFFER, | |
S_VERT, | |
S_FRAG, | |
S_ELEMENTS, | |
S_PRIMITIVE, | |
S_OFFSET, | |
S_COUNT, | |
S_INSTANCES, | |
S_PROFILE | |
].concat(GL_STATE_NAMES) | |
function checkKeys (dict) { | |
Object.keys(dict).forEach(function (key) { | |
check.command( | |
KEY_NAMES.indexOf(key) >= 0, | |
'unknown parameter "' + key + '"', | |
command) | |
}) | |
} | |
checkKeys(staticOptions) | |
checkKeys(dynamicOptions) | |
}) | |
var framebuffer = parseFramebuffer(options) | |
var viewportAndScissor = parseViewportScissor(options, framebuffer) | |
var draw = parseDraw(options) | |
var state = parseGLState(options) | |
var shader = parseProgram(options) | |
function copyBox (name) { | |
var defn = viewportAndScissor[name] | |
if (defn) { | |
state[name] = defn | |
} | |
} | |
copyBox(S_VIEWPORT) | |
copyBox(propName(S_SCISSOR_BOX)) | |
var dirty = Object.keys(state).length > 0 | |
return { | |
framebuffer: framebuffer, | |
draw: draw, | |
shader: shader, | |
state: state, | |
dirty: dirty | |
} | |
} | |
function parseUniforms (uniforms) { | |
var staticUniforms = uniforms.static | |
var dynamicUniforms = uniforms.dynamic | |
var UNIFORMS = {} | |
Object.keys(staticUniforms).forEach(function (name) { | |
var value = staticUniforms[name] | |
var result | |
if (typeof value === 'number' || | |
typeof value === 'boolean') { | |
result = createStaticDecl(function () { | |
return value | |
}) | |
} else if ( | |
typeof value === 'function' && | |
(value._reglType === 'texture2d' || | |
value._reglType === 'textureCube')) { | |
result = createStaticDecl(function (env) { | |
return env.link(value) | |
}) | |
} else if (isArrayLike(value)) { | |
result = createStaticDecl(function (env) { | |
var ITEM = env.global.def('[', | |
loop(value.length, function (i) { | |
check.command( | |
typeof value[i] === 'number' || | |
typeof value[i] === 'boolean', | |
'invalid uniform ' + name) | |
return value[i] | |
}), ']') | |
return ITEM | |
}) | |
} else { | |
check.commandRaise('invalid uniform ' + name) | |
} | |
result.value = value | |
UNIFORMS[name] = result | |
}) | |
Object.keys(dynamicUniforms).forEach(function (key) { | |
var dyn = dynamicUniforms[key] | |
UNIFORMS[key] = createDynamicDecl(dyn, function (env, scope) { | |
return env.invoke(scope, dyn) | |
}) | |
}) | |
return UNIFORMS | |
} | |
function parseAttributes (attributes) { | |
var staticAttributes = attributes.static | |
var dynamicAttributes = attributes.dynamic | |
var attributeDefs = {} | |
Object.keys(staticAttributes).forEach(function (attribute) { | |
var value = staticAttributes[attribute] | |
var id = stringStore.id(attribute) | |
var record = new AttributeRecord() | |
if (isBufferArgs(value)) { | |
record.state = ATTRIB_STATE_POINTER | |
record.buffer = bufferState.getBuffer( | |
bufferState.create(value, GL_ARRAY_BUFFER, false)) | |
record.type = record.buffer.dtype | |
} else { | |
var buffer = bufferState.getBuffer(value) | |
if (buffer) { | |
record.state = ATTRIB_STATE_POINTER | |
record.buffer = buffer | |
record.type = buffer.dtype | |
} else { | |
check.command(typeof value === 'object' && value, | |
'invalid data for attribute ' + attribute) | |
if (value.constant) { | |
var constant = value.constant | |
record.state = ATTRIB_STATE_CONSTANT | |
if (typeof constant === 'number') { | |
record.x = constant | |
} else { | |
check.command( | |
isArrayLike(constant) && | |
constant.length > 0 && | |
constant.length <= 4, | |
'invalid constant for attribute ' + attribute) | |
CUTE_COMPONENTS.forEach(function (c, i) { | |
if (i < constant.length) { | |
record[c] = constant[i] | |
} | |
}) | |
} | |
} else { | |
buffer = bufferState.getBuffer(value.buffer) | |
check.command(!!buffer, 'missing buffer for attribute "' + attribute + '"') | |
var offset = value.offset | 0 | |
check.command(offset >= 0, | |
'invalid offset for attribute "' + attribute + '"') | |
var stride = value.stride | 0 | |
check.command(stride >= 0 && stride < 256, | |
'invalid stride for attribute "' + attribute + '", must be integer betweeen [0, 255]') | |
var size = value.size | 0 | |
check.command(!('size' in value) || (size > 0 && size <= 4), | |
'invalid size for attribute "' + attribute + '", must be 1,2,3,4') | |
var normalized = !!value.normalized | |
var type = 0 | |
if ('type' in value) { | |
check.commandParameter( | |
value.type, glTypes, | |
'invalid type for attribute ' + attribute) | |
type = glTypes[value.type] | |
} | |
var divisor = value.divisor | 0 | |
if ('divisor' in value) { | |
check.command(divisor === 0 || extInstancing, | |
'cannot specify divisor for attribute "' + attribute + '", instancing not supported') | |
check.command(divisor >= 0, | |
'invalid divisor for attribute "' + attribute + '"') | |
} | |
check.optional(function () { | |
var command = check.guessCommand() | |
var VALID_KEYS = [ | |
'buffer', | |
'offset', | |
'divisor', | |
'normalized', | |
'type', | |
'size', | |
'stride' | |
] | |
Object.keys(value).forEach(function (prop) { | |
check.command( | |
VALID_KEYS.indexOf(prop) >= 0, | |
'unknown parameter "' + prop + '" for attribute pointer "' + attribute + '" (valid parameters are ' + VALID_KEYS + ')', | |
command) | |
}) | |
}) | |
record.buffer = buffer | |
record.state = ATTRIB_STATE_POINTER | |
record.size = size | |
record.normalized = normalized | |
record.type = type || buffer.dtype | |
record.offset = offset | |
record.stride = stride | |
record.divisor = divisor | |
} | |
} | |
} | |
attributeDefs[attribute] = createStaticDecl(function (env, scope) { | |
var cache = env.attribCache | |
if (id in cache) { | |
return cache[id] | |
} | |
var result = { | |
isStream: false | |
} | |
Object.keys(record).forEach(function (key) { | |
result[key] = record[key] | |
}) | |
if (record.buffer) { | |
result.buffer = env.link(record.buffer) | |
} | |
cache[id] = result | |
return result | |
}) | |
}) | |
Object.keys(dynamicAttributes).forEach(function (attribute) { | |
var dyn = dynamicAttributes[attribute] | |
function appendAttributeCode (env, block) { | |
var VALUE = env.invoke(block, dyn) | |
var shared = env.shared | |
var IS_BUFFER_ARGS = shared.isBufferArgs | |
var BUFFER_STATE = shared.buffer | |
// Perform validation on attribute | |
check.optional(function () { | |
env.assert(block, | |
VALUE + '&&(typeof ' + VALUE + '==="object"||typeof ' + | |
VALUE + '==="function")&&(' + | |
IS_BUFFER_ARGS + '(' + VALUE + ')||' + | |
BUFFER_STATE + '.getBuffer(' + VALUE + ')||' + | |
BUFFER_STATE + '.getBuffer(' + VALUE + '.buffer)||' + | |
'("constant" in ' + VALUE + | |
'&&(typeof ' + VALUE + '.constant==="number"||' + | |
shared.isArrayLike + '(' + VALUE + '))))', | |
'invalid dynamic attribute "' + attribute + '"') | |
}) | |
// allocate names for result | |
var result = { | |
isStream: block.def(false) | |
} | |
var defaultRecord = new AttributeRecord() | |
defaultRecord.state = ATTRIB_STATE_POINTER | |
Object.keys(defaultRecord).forEach(function (key) { | |
result[key] = block.def('' + defaultRecord[key]) | |
}) | |
var BUFFER = result.buffer | |
var TYPE = result.type | |
block( | |
'if(', IS_BUFFER_ARGS, '(', VALUE, ')){', | |
result.isStream, '=true;', | |
BUFFER, '=', BUFFER_STATE, '.createStream(', GL_ARRAY_BUFFER, ',', VALUE, ');', | |
TYPE, '=', BUFFER, '.dtype;', | |
'}else{', | |
BUFFER, '=', BUFFER_STATE, '.getBuffer(', VALUE, ');', | |
'if(', BUFFER, '){', | |
TYPE, '=', BUFFER, '.dtype;', | |
'}else if(', VALUE, '.constant){', | |
result.state, '=', ATTRIB_STATE_CONSTANT, ';', | |
CUTE_COMPONENTS.map(function (name, i) { | |
return ( | |
result[name] + '=' + VALUE + '.length>=' + i + | |
'?' + VALUE + '[' + i + ']:0;' | |
) | |
}).join(''), | |
'}else{', | |
BUFFER, '=', BUFFER_STATE, '.getBuffer(', VALUE, '.buffer);', | |
TYPE, '="type" in ', VALUE, '?', | |
shared.glTypes, '[', VALUE, '.type]:', BUFFER, '.dtype;', | |
result.normalized, '=!!', VALUE, '.normalized;') | |
function emitReadRecord (name) { | |
block(result[name], '=', VALUE, '.', name, '|0;') | |
} | |
emitReadRecord('size') | |
emitReadRecord('offset') | |
emitReadRecord('stride') | |
emitReadRecord('divisor') | |
block('}}') | |
return result | |
} | |
attributeDefs[attribute] = createDynamicDecl(dyn, appendAttributeCode) | |
}) | |
return attributeDefs | |
} | |
function parseContext (context) { | |
var staticContext = context.static | |
var dynamicContext = context.dynamic | |
var result = {} | |
Object.keys(staticContext).forEach(function (name) { | |
var value = staticContext[name] | |
result[name] = createStaticDecl(function (env, scope) { | |
if (typeof value === 'number' || typeof value === 'boolean') { | |
return '' + value | |
} else { | |
return env.link(value) | |
} | |
}) | |
}) | |
Object.keys(dynamicContext).forEach(function (name) { | |
var dyn = dynamicContext[name] | |
result[name] = createDynamicDecl(dyn, function (env, scope) { | |
return env.invoke(scope, dyn) | |
}) | |
}) | |
return result | |
} | |
function parseArguments (options, attributes, uniforms, context) { | |
var result = parseOptions(options) | |
result.profile = parseProfile(options) | |
result.uniforms = parseUniforms(uniforms) | |
result.attributes = parseAttributes(attributes) | |
result.context = parseContext(context) | |
return result | |
} | |
// =================================================== | |
// =================================================== | |
// COMMON UPDATE FUNCTIONS | |
// =================================================== | |
// =================================================== | |
function emitContext (env, scope, context) { | |
var shared = env.shared | |
var CONTEXT = shared.context | |
var contextEnter = env.scope() | |
Object.keys(context).forEach(function (name) { | |
scope.save(CONTEXT, '.' + name) | |
var defn = context[name] | |
contextEnter(CONTEXT, '.', name, '=', defn.append(env, scope), ';') | |
}) | |
scope(contextEnter) | |
} | |
// =================================================== | |
// =================================================== | |
// COMMON DRAWING FUNCTIONS | |
// =================================================== | |
// =================================================== | |
function emitPollFramebuffer (env, scope, framebuffer) { | |
var shared = env.shared | |
var GL = shared.gl | |
var FRAMEBUFFER_STATE = shared.framebuffer | |
var EXT_DRAW_BUFFERS | |
if (extDrawBuffers) { | |
EXT_DRAW_BUFFERS = scope.def(shared.extensions, '.webgl_draw_buffers') | |
} | |
var constants = env.constants | |
var DRAW_BUFFERS = constants.drawBuffer | |
var BACK_BUFFER = constants.backBuffer | |
var NEXT | |
if (framebuffer) { | |
NEXT = framebuffer.append(env, scope) | |
} else { | |
NEXT = scope.def(FRAMEBUFFER_STATE, '.next') | |
} | |
scope( | |
'if(', FRAMEBUFFER_STATE, '.dirty||', NEXT, '!==', FRAMEBUFFER_STATE, '.cur){', | |
'if(', NEXT, '){', | |
GL, '.bindFramebuffer(', GL_FRAMEBUFFER, ',', NEXT, '.framebuffer);') | |
if (extDrawBuffers) { | |
scope(EXT_DRAW_BUFFERS, '.drawBuffersWEBGL(', | |
DRAW_BUFFERS, '[', NEXT, '.colorAttachments.length]);') | |
} | |
scope('}else{', | |
GL, '.bindFramebuffer(', GL_FRAMEBUFFER, ',null);') | |
if (extDrawBuffers) { | |
scope(EXT_DRAW_BUFFERS, '.drawBuffersWEBGL(', BACK_BUFFER, ');') | |
} | |
scope( | |
'}', | |
FRAMEBUFFER_STATE, '.cur=', NEXT, ';', | |
FRAMEBUFFER_STATE, '.dirty=false;', | |
'}') | |
} | |
function emitPollState (env, scope, args) { | |
var shared = env.shared | |
var GL = shared.gl | |
var CURRENT_VARS = env.current | |
var NEXT_VARS = env.next | |
var CURRENT_STATE = shared.current | |
var NEXT_STATE = shared.next | |
var block = env.cond(CURRENT_STATE, '.dirty') | |
GL_STATE_NAMES.forEach(function (prop) { | |
var param = propName(prop) | |
if (param in args.state) { | |
return | |
} | |
var NEXT, CURRENT | |
if (param in NEXT_VARS) { | |
NEXT = NEXT_VARS[param] | |
CURRENT = CURRENT_VARS[param] | |
var parts = loop(currentState[param].length, function (i) { | |
return block.def(NEXT, '[', i, ']') | |
}) | |
block(env.cond(parts.map(function (p, i) { | |
return p + '!==' + CURRENT + '[' + i + ']' | |
}).join('||')) | |
.then( | |
GL, '.', GL_VARIABLES[param], '(', parts, ');', | |
parts.map(function (p, i) { | |
return CURRENT + '[' + i + ']=' + p | |
}).join(';'), ';')) | |
} else { | |
NEXT = block.def(NEXT_STATE, '.', param) | |
var ifte = env.cond(NEXT, '!==', CURRENT_STATE, '.', param) | |
block(ifte) | |
if (param in GL_FLAGS) { | |
ifte( | |
env.cond(NEXT) | |
.then(GL, '.enable(', GL_FLAGS[param], ');') | |
.else(GL, '.disable(', GL_FLAGS[param], ');'), | |
CURRENT_STATE, '.', param, '=', NEXT, ';') | |
} else { | |
ifte( | |
GL, '.', GL_VARIABLES[param], '(', NEXT, ');', | |
CURRENT_STATE, '.', param, '=', NEXT, ';') | |
} | |
} | |
}) | |
if (Object.keys(args.state).length === 0) { | |
block(CURRENT_STATE, '.dirty=false;') | |
} | |
scope(block) | |
} | |
function emitSetOptions (env, scope, options, filter) { | |
var shared = env.shared | |
var CURRENT_VARS = env.current | |
var CURRENT_STATE = shared.current | |
var GL = shared.gl | |
sortState(Object.keys(options)).forEach(function (param) { | |
var defn = options[param] | |
if (filter && !filter(defn)) { | |
return | |
} | |
var variable = defn.append(env, scope) | |
if (GL_FLAGS[param]) { | |
var flag = GL_FLAGS[param] | |
if (isStatic(defn)) { | |
if (variable) { | |
scope(GL, '.enable(', flag, ');') | |
} else { | |
scope(GL, '.disable(', flag, ');') | |
} | |
} else { | |
scope(env.cond(variable) | |
.then(GL, '.enable(', flag, ');') | |
.else(GL, '.disable(', flag, ');')) | |
} | |
scope(CURRENT_STATE, '.', param, '=', variable, ';') | |
} else if (isArrayLike(variable)) { | |
var CURRENT = CURRENT_VARS[param] | |
scope( | |
GL, '.', GL_VARIABLES[param], '(', variable, ');', | |
variable.map(function (v, i) { | |
return CURRENT + '[' + i + ']=' + v | |
}).join(';'), ';') | |
} else { | |
scope( | |
GL, '.', GL_VARIABLES[param], '(', variable, ');', | |
CURRENT_STATE, '.', param, '=', variable, ';') | |
} | |
}) | |
} | |
function injectExtensions (env, scope) { | |
if (extInstancing && !env.instancing) { | |
env.instancing = scope.def( | |
env.shared.extensions, '.angle_instanced_arrays') | |
} | |
} | |
function emitProfile (env, scope, args, useScope, incrementCounter) { | |
var shared = env.shared | |
var STATS = env.stats | |
var CURRENT_STATE = shared.current | |
var TIMER = shared.timer | |
var profileArg = args.profile | |
function perfCounter () { | |
if (typeof performance === 'undefined') { | |
return 'Date.now()' | |
} else { | |
return 'performance.now()' | |
} | |
} | |
var CPU_START, QUERY_COUNTER | |
function emitProfileStart (block) { | |
CPU_START = scope.def() | |
block(CPU_START, '=', perfCounter(), ';') | |
if (typeof incrementCounter === 'string') { | |
block(STATS, '.count+=', incrementCounter, ';') | |
} else { | |
block(STATS, '.count++;') | |
} | |
if (timer) { | |
if (useScope) { | |
QUERY_COUNTER = scope.def() | |
block(QUERY_COUNTER, '=', TIMER, '.getNumPendingQueries();') | |
} else { | |
block(TIMER, '.beginQuery(', STATS, ');') | |
} | |
} | |
} | |
function emitProfileEnd (block) { | |
block(STATS, '.cpuTime+=', perfCounter(), '-', CPU_START, ';') | |
if (timer) { | |
if (useScope) { | |
block(TIMER, '.pushScopeStats(', | |
QUERY_COUNTER, ',', | |
TIMER, '.getNumPendingQueries(),', | |
STATS, ');') | |
} else { | |
block(TIMER, '.endQuery();') | |
} | |
} | |
} | |
function scopeProfile (value) { | |
var prev = scope.def(CURRENT_STATE, '.profile') | |
scope(CURRENT_STATE, '.profile=', value, ';') | |
scope.exit(CURRENT_STATE, '.profile=', prev, ';') | |
} | |
var USE_PROFILE | |
if (profileArg) { | |
if (isStatic(profileArg)) { | |
if (profileArg.enable) { | |
emitProfileStart(scope) | |
emitProfileEnd(scope.exit) | |
scopeProfile('true') | |
} else { | |
scopeProfile('false') | |
} | |
return | |
} | |
USE_PROFILE = profileArg.append(env, scope) | |
scopeProfile(USE_PROFILE) | |
} else { | |
USE_PROFILE = scope.def(CURRENT_STATE, '.profile') | |
} | |
var start = env.block() | |
emitProfileStart(start) | |
scope('if(', USE_PROFILE, '){', start, '}') | |
var end = env.block() | |
emitProfileEnd(end) | |
scope.exit('if(', USE_PROFILE, '){', end, '}') | |
} | |
function emitAttributes (env, scope, args, attributes, filter) { | |
var shared = env.shared | |
function typeLength (x) { | |
switch (x) { | |
case GL_FLOAT_VEC2: | |
case GL_INT_VEC2: | |
case GL_BOOL_VEC2: | |
return 2 | |
case GL_FLOAT_VEC3: | |
case GL_INT_VEC3: | |
case GL_BOOL_VEC3: | |
return 3 | |
case GL_FLOAT_VEC4: | |
case GL_INT_VEC4: | |
case GL_BOOL_VEC4: | |
return 4 | |
default: | |
return 1 | |
} | |
} | |
function emitBindAttribute (ATTRIBUTE, size, record) { | |
var GL = shared.gl | |
var LOCATION = scope.def(ATTRIBUTE, '.location') | |
var BINDING = scope.def(shared.attributes, '[', LOCATION, ']') | |
var STATE = record.state | |
var BUFFER = record.buffer | |
var CONST_COMPONENTS = [ | |
record.x, | |
record.y, | |
record.z, | |
record.w | |
] | |
var COMMON_KEYS = [ | |
'buffer', | |
'normalized', | |
'offset', | |
'stride' | |
] | |
function emitBuffer () { | |
scope( | |
'if(!', BINDING, '.pointer){', | |
GL, '.enableVertexAttribArray(', LOCATION, ');', | |
BINDING, '.pointer=true;}') | |
var TYPE = record.type | |
var SIZE | |
if (!record.size) { | |
SIZE = size | |
} else { | |
SIZE = scope.def(record.size, '||', size) | |
} | |
scope('if(', | |
BINDING, '.type!==', TYPE, '||', | |
BINDING, '.size!==', SIZE, '||', | |
COMMON_KEYS.map(function (key) { | |
return BINDING + '.' + key + '!==' + record[key] | |
}).join('||'), | |
'){', | |
GL, '.bindBuffer(', GL_ARRAY_BUFFER, ',', BUFFER, '.buffer);', | |
GL, '.vertexAttribPointer(', [ | |
LOCATION, | |
SIZE, | |
TYPE, | |
record.normalized, | |
record.stride, | |
record.offset | |
], ');', | |
BINDING, '.type=', TYPE, ';', | |
BINDING, '.size=', SIZE, ';', | |
COMMON_KEYS.map(function (key) { | |
return BINDING + '.' + key + '=' + record[key] + ';' | |
}).join(''), | |
'}') | |
if (extInstancing) { | |
var DIVISOR = record.divisor | |
scope( | |
'if(', BINDING, '.divisor!==', DIVISOR, '){', | |
env.instancing, '.vertexAttribDivisorANGLE(', [LOCATION, DIVISOR], ');', | |
BINDING, '.divisor=', DIVISOR, ';}') | |
} | |
} | |
function emitConstant () { | |
scope( | |
'if(', BINDING, '.pointer){', | |
GL, '.disableVertexAttribArray(', LOCATION, ');', | |
BINDING, '.pointer=false;', | |
'}if(', CUTE_COMPONENTS.map(function (c, i) { | |
return BINDING + '.' + c + '!==' + CONST_COMPONENTS[i] | |
}).join('||'), '){', | |
GL, '.vertexAttrib4f(', LOCATION, ',', CONST_COMPONENTS, ');', | |
CUTE_COMPONENTS.map(function (c, i) { | |
return BINDING + '.' + c + '=' + CONST_COMPONENTS[i] + ';' | |
}).join(''), | |
'}') | |
} | |
if (STATE === ATTRIB_STATE_POINTER) { | |
emitBuffer() | |
} else if (STATE === ATTRIB_STATE_CONSTANT) { | |
emitConstant() | |
} else { | |
scope('if(', STATE, '===', ATTRIB_STATE_POINTER, '){') | |
emitBuffer() | |
scope('}else{') | |
emitConstant() | |
scope('}') | |
} | |
} | |
attributes.forEach(function (attribute) { | |
var name = attribute.name | |
var arg = args.attributes[name] | |
var record | |
if (arg) { | |
if (!filter(arg)) { | |
return | |
} | |
record = arg.append(env, scope) | |
} else { | |
if (!filter(SCOPE_DECL)) { | |
return | |
} | |
var scopeAttrib = env.scopeAttrib(name) | |
check.optional(function () { | |
env.assert(scope, | |
scopeAttrib + '.state', | |
'missing attribute ' + name) | |
}) | |
record = {} | |
Object.keys(new AttributeRecord()).forEach(function (key) { | |
record[key] = scope.def(scopeAttrib, '.', key) | |
}) | |
} | |
emitBindAttribute( | |
env.link(attribute), typeLength(attribute.info.type), record) | |
}) | |
} | |
function emitUniforms (env, scope, args, uniforms, filter) { | |
var shared = env.shared | |
var GL = shared.gl | |
var infix | |
for (var i = 0; i < uniforms.length; ++i) { | |
var uniform = uniforms[i] | |
var name = uniform.name | |
var type = uniform.info.type | |
var arg = args.uniforms[name] | |
var UNIFORM = env.link(uniform) | |
var LOCATION = UNIFORM + '.location' | |
var VALUE | |
if (arg) { | |
if (!filter(arg)) { | |
continue | |
} | |
if (isStatic(arg)) { | |
var value = arg.value | |
if (type === GL_SAMPLER_2D || type === GL_SAMPLER_CUBE) { | |
check.command( | |
typeof value === 'function' && | |
((value._reglType === 'texture2d' && type === GL_SAMPLER_2D) || | |
(value._reglType === 'textureCube' && type === GL_SAMPLER_CUBE)), | |
'invalid texture for uniform ' + name) | |
var TEX_VALUE = env.link(value._texture) | |
scope(GL, '.uniform1i(', LOCATION, ',', TEX_VALUE + '.bind());') | |
scope.exit(TEX_VALUE, '.unbind();') | |
} else if ( | |
type === GL_FLOAT_MAT2 || | |
type === GL_FLOAT_MAT3 || | |
type === GL_FLOAT_MAT4) { | |
check.optional(function () { | |
check.command(isArrayLike(value), | |
'invalid matrix for uniform ' + name) | |
check.command( | |
(type === GL_FLOAT_MAT2 && value.length === 4) || | |
(type === GL_FLOAT_MAT3 && value.length === 9) || | |
(type === GL_FLOAT_MAT4 && value.length === 16), | |
'invalid length for matrix uniform ' + name) | |
}) | |
var MAT_VALUE = env.global.def('new Float32Array([' + | |
Array.prototype.slice.call(value) + '])') | |
var dim = 2 | |
if (type === GL_FLOAT_MAT3) { | |
dim = 3 | |
} else if (type === GL_FLOAT_MAT4) { | |
dim = 4 | |
} | |
scope( | |
GL, '.uniformMatrix', dim, 'fv(', | |
LOCATION, ',false,', MAT_VALUE, ');') | |
} else { | |
switch (type) { | |
case GL_FLOAT: | |
check.commandType(value, 'number', 'uniform ' + name) | |
infix = '1f' | |
break | |
case GL_FLOAT_VEC2: | |
check.command( | |
isArrayLike(value) && value.length === 2, | |
'uniform ' + name) | |
infix = '2f' | |
break | |
case GL_FLOAT_VEC3: | |
check.command( | |
isArrayLike(value) && value.length === 3, | |
'uniform ' + name) | |
infix = '3f' | |
break | |
case GL_FLOAT_VEC4: | |
check.command( | |
isArrayLike(value) && value.length === 4, | |
'uniform ' + name) | |
infix = '4f' | |
break | |
case GL_BOOL: | |
check.commandType(value, 'boolean', 'uniform ' + name) | |
infix = '1i' | |
break | |
case GL_INT: | |
check.commandType(value, 'number', 'uniform ' + name) | |
infix = '1i' | |
break | |
case GL_BOOL_VEC2: | |
check.command( | |
isArrayLike(value) && value.length === 2, | |
'uniform ' + name) | |
infix = '2i' | |
break | |
case GL_INT_VEC2: | |
check.command( | |
isArrayLike(value) && value.length === 2, | |
'uniform ' + name) | |
infix = '2i' | |
break | |
case GL_BOOL_VEC3: | |
check.command( | |
isArrayLike(value) && value.length === 3, | |
'uniform ' + name) | |
infix = '3i' | |
break | |
case GL_INT_VEC3: | |
check.command( | |
isArrayLike(value) && value.length === 3, | |
'uniform ' + name) | |
infix = '3i' | |
break | |
case GL_BOOL_VEC4: | |
check.command( | |
isArrayLike(value) && value.length === 4, | |
'uniform ' + name) | |
infix = '4i' | |
break | |
case GL_INT_VEC4: | |
check.command( | |
isArrayLike(value) && value.length === 4, | |
'uniform ' + name) | |
infix = '4i' | |
break | |
} | |
scope(GL, '.uniform', infix, '(', LOCATION, ',', | |
isArrayLike(value) ? Array.prototype.slice.call(value) : value, | |
');') | |
} | |
continue | |
} else { | |
VALUE = arg.append(env, scope) | |
} | |
} else { | |
if (!filter(SCOPE_DECL)) { | |
continue | |
} | |
VALUE = scope.def(shared.uniforms, '[', stringStore.id(name), ']') | |
} | |
// perform type validation | |
check.optional(function () { | |
function check (pred) { | |
env.assert(scope, pred, 'invalid or missing uniform "' + name + '"') | |
} | |
function checkType (type) { | |
check('typeof ' + VALUE + '==="' + type + '"') | |
} | |
function checkVector (n, type) { | |
check(shared.isArrayLike + '(' + VALUE + ')&&' + VALUE + '.length===' + n) | |
} | |
function checkTexture (target) { | |
check( | |
'typeof ' + VALUE + '==="function"&&' + | |
VALUE + '._reglType==="texture' + | |
(target === GL_TEXTURE_2D ? '2d' : 'Cube') + '"') | |
} | |
switch (type) { | |
case GL_INT: | |
checkType('number') | |
break | |
case GL_INT_VEC2: | |
checkVector(2, 'number') | |
break | |
case GL_INT_VEC3: | |
checkVector(3, 'number') | |
break | |
case GL_INT_VEC4: | |
checkVector(4, 'number') | |
break | |
case GL_FLOAT: | |
checkType('number') | |
break | |
case GL_FLOAT_VEC2: | |
checkVector(2, 'number') | |
break | |
case GL_FLOAT_VEC3: | |
checkVector(3, 'number') | |
break | |
case GL_FLOAT_VEC4: | |
checkVector(4, 'number') | |
break | |
case GL_BOOL: | |
checkType('boolean') | |
break | |
case GL_BOOL_VEC2: | |
checkVector(2, 'boolean') | |
break | |
case GL_BOOL_VEC3: | |
checkVector(3, 'boolean') | |
break | |
case GL_BOOL_VEC4: | |
checkVector(4, 'boolean') | |
break | |
case GL_FLOAT_MAT2: | |
checkVector(4, 'number') | |
break | |
case GL_FLOAT_MAT3: | |
checkVector(9, 'number') | |
break | |
case GL_FLOAT_MAT4: | |
checkVector(16, 'number') | |
break | |
case GL_SAMPLER_2D: | |
checkTexture(GL_TEXTURE_2D) | |
break | |
case GL_SAMPLER_CUBE: | |
checkTexture(GL_TEXTURE_CUBE_MAP) | |
break | |
} | |
}) | |
var unroll = 1 | |
switch (type) { | |
case GL_SAMPLER_2D: | |
case GL_SAMPLER_CUBE: | |
var TEX = scope.def(VALUE, '._texture') | |
scope(GL, '.uniform1i(', LOCATION, ',', TEX, '.bind());') | |
scope.exit(TEX, '.unbind();') | |
continue | |
case GL_INT: | |
case GL_BOOL: | |
infix = '1i' | |
break | |
case GL_INT_VEC2: | |
case GL_BOOL_VEC2: | |
infix = '2i' | |
unroll = 2 | |
break | |
case GL_INT_VEC3: | |
case GL_BOOL_VEC3: | |
infix = '3i' | |
unroll = 3 | |
break | |
case GL_INT_VEC4: | |
case GL_BOOL_VEC4: | |
infix = '4i' | |
unroll = 4 | |
break | |
case GL_FLOAT: | |
infix = '1f' | |
break | |
case GL_FLOAT_VEC2: | |
infix = '2f' | |
unroll = 2 | |
break | |
case GL_FLOAT_VEC3: | |
infix = '3f' | |
unroll = 3 | |
break | |
case GL_FLOAT_VEC4: | |
infix = '4f' | |
unroll = 4 | |
break | |
case GL_FLOAT_MAT2: | |
infix = 'Matrix2fv' | |
break | |
case GL_FLOAT_MAT3: | |
infix = 'Matrix3fv' | |
break | |
case GL_FLOAT_MAT4: | |
infix = 'Matrix4fv' | |
break | |
} | |
scope(GL, '.uniform', infix, '(', LOCATION, ',') | |
if (infix.charAt(0) === 'M') { | |
var matSize = Math.pow(type - GL_FLOAT_MAT2 + 2, 2) | |
var STORAGE = env.global.def('new Float32Array(', matSize, ')') | |
scope( | |
'false,(Array.isArray(', VALUE, ')||', VALUE, ' instanceof Float32Array)?', VALUE, ':(', | |
loop(matSize, function (i) { | |
return STORAGE + '[' + i + ']=' + VALUE + '[' + i + ']' | |
}), ',', STORAGE, ')') | |
} else if (unroll > 1) { | |
scope(loop(unroll, function (i) { | |
return VALUE + '[' + i + ']' | |
})) | |
} else { | |
scope(VALUE) | |
} | |
scope(');') | |
} | |
} | |
function emitDraw (env, outer, inner, args) { | |
var shared = env.shared | |
var GL = shared.gl | |
var DRAW_STATE = shared.draw | |
var drawOptions = args.draw | |
function emitElements () { | |
var defn = drawOptions.elements | |
var ELEMENTS | |
var scope = outer | |
if (defn) { | |
if ((defn.contextDep && args.contextDynamic) || defn.propDep) { | |
scope = inner | |
} | |
ELEMENTS = defn.append(env, scope) | |
} else { | |
ELEMENTS = scope.def(DRAW_STATE, '.', S_ELEMENTS) | |
} | |
if (ELEMENTS) { | |
scope( | |
'if(' + ELEMENTS + ')' + | |
GL + '.bindBuffer(' + GL_ELEMENT_ARRAY_BUFFER + ',' + ELEMENTS + '.buffer.buffer);') | |
} | |
return ELEMENTS | |
} | |
function emitCount () { | |
var defn = drawOptions.count | |
var COUNT | |
var scope = outer | |
if (defn) { | |
if ((defn.contextDep && args.contextDynamic) || defn.propDep) { | |
scope = inner | |
} | |
COUNT = defn.append(env, scope) | |
check.optional(function () { | |
if (defn.MISSING) { | |
env.assert(outer, 'false', 'missing vertex count') | |
} | |
if (defn.DYNAMIC) { | |
env.assert(scope, COUNT + '>=0', 'missing vertex count') | |
} | |
}) | |
} else { | |
COUNT = scope.def(DRAW_STATE, '.', S_COUNT) | |
check.optional(function () { | |
env.assert(scope, COUNT + '>=0', 'missing vertex count') | |
}) | |
} | |
return COUNT | |
} | |
var ELEMENTS = emitElements() | |
function emitValue (name) { | |
var defn = drawOptions[name] | |
if (defn) { | |
if ((defn.contextDep && args.contextDynamic) || defn.propDep) { | |
return defn.append(env, inner) | |
} else { | |
return defn.append(env, outer) | |
} | |
} else { | |
return outer.def(DRAW_STATE, '.', name) | |
} | |
} | |
var PRIMITIVE = emitValue(S_PRIMITIVE) | |
var OFFSET = emitValue(S_OFFSET) | |
var COUNT = emitCount() | |
if (typeof COUNT === 'number') { | |
if (COUNT === 0) { | |
return | |
} | |
} else { | |
inner('if(', COUNT, '){') | |
inner.exit('}') | |
} | |
var INSTANCES, EXT_INSTANCING | |
if (extInstancing) { | |
INSTANCES = emitValue(S_INSTANCES) | |
EXT_INSTANCING = env.instancing | |
} | |
var ELEMENT_TYPE = ELEMENTS + '.type' | |
var elementsStatic = drawOptions.elements && isStatic(drawOptions.elements) | |
function emitInstancing () { | |
function drawElements () { | |
inner(EXT_INSTANCING, '.drawElementsInstancedANGLE(', [ | |
PRIMITIVE, | |
COUNT, | |
ELEMENT_TYPE, | |
OFFSET + '<<((' + ELEMENT_TYPE + '-' + GL_UNSIGNED_BYTE + ')>>1)', | |
INSTANCES | |
], ');') | |
} | |
function drawArrays () { | |
inner(EXT_INSTANCING, '.drawArraysInstancedANGLE(', | |
[PRIMITIVE, OFFSET, COUNT, INSTANCES], ');') | |
} | |
if (ELEMENTS) { | |
if (!elementsStatic) { | |
inner('if(', ELEMENTS, '){') | |
drawElements() | |
inner('}else{') | |
drawArrays() | |
inner('}') | |
} else { | |
drawElements() | |
} | |
} else { | |
drawArrays() | |
} | |
} | |
function emitRegular () { | |
function drawElements () { | |
inner(GL + '.drawElements(' + [ | |
PRIMITIVE, | |
COUNT, | |
ELEMENT_TYPE, | |
OFFSET + '<<((' + ELEMENT_TYPE + '-' + GL_UNSIGNED_BYTE + ')>>1)' | |
] + ');') | |
} | |
function drawArrays () { | |
inner(GL + '.drawArrays(' + [PRIMITIVE, OFFSET, COUNT] + ');') | |
} | |
if (ELEMENTS) { | |
if (!elementsStatic) { | |
inner('if(', ELEMENTS, '){') | |
drawElements() | |
inner('}else{') | |
drawArrays() | |
inner('}') | |
} else { | |
drawElements() | |
} | |
} else { | |
drawArrays() | |
} | |
} | |
if (extInstancing && (typeof INSTANCES !== 'number' || INSTANCES >= 0)) { | |
if (typeof INSTANCES === 'string') { | |
inner('if(', INSTANCES, '>0){') | |
emitInstancing() | |
inner('}else if(', INSTANCES, '<0){') | |
emitRegular() | |
inner('}') | |
} else { | |
emitInstancing() | |
} | |
} else { | |
emitRegular() | |
} | |
} | |
function createBody (emitBody, parentEnv, args, program, count) { | |
var env = createREGLEnvironment() | |
var scope = env.proc('body', count) | |
check.optional(function () { | |
env.commandStr = parentEnv.commandStr | |
env.command = env.link(parentEnv.commandStr) | |
}) | |
if (extInstancing) { | |
env.instancing = scope.def( | |
env.shared.extensions, '.angle_instanced_arrays') | |
} | |
emitBody(env, scope, args, program) | |
return env.compile().body | |
} | |
// =================================================== | |
// =================================================== | |
// DRAW PROC | |
// =================================================== | |
// =================================================== | |
function emitDrawBody (env, draw, args, program) { | |
injectExtensions(env, draw) | |
emitAttributes(env, draw, args, program.attributes, function () { | |
return true | |
}) | |
emitUniforms(env, draw, args, program.uniforms, function () { | |
return true | |
}) | |
emitDraw(env, draw, draw, args) | |
} | |
function emitDrawProc (env, args) { | |
var draw = env.proc('draw', 1) | |
injectExtensions(env, draw) | |
emitContext(env, draw, args.context) | |
emitPollFramebuffer(env, draw, args.framebuffer) | |
emitPollState(env, draw, args) | |
emitSetOptions(env, draw, args.state) | |
emitProfile(env, draw, args, false, true) | |
var program = args.shader.progVar.append(env, draw) | |
draw(env.shared.gl, '.useProgram(', program, '.program);') | |
if (args.shader.program) { | |
emitDrawBody(env, draw, args, args.shader.program) | |
} else { | |
var drawCache = env.global.def('{}') | |
var PROG_ID = draw.def(program, '.id') | |
var CACHED_PROC = draw.def(drawCache, '[', PROG_ID, ']') | |
draw( | |
env.cond(CACHED_PROC) | |
.then(CACHED_PROC, '.call(this,a0);') | |
.else( | |
CACHED_PROC, '=', drawCache, '[', PROG_ID, ']=', | |
env.link(function (program) { | |
return createBody(emitDrawBody, env, args, program, 1) | |
}), '(', program, ');', | |
CACHED_PROC, '.call(this,a0);')) | |
} | |
if (Object.keys(args.state).length > 0) { | |
draw(env.shared.current, '.dirty=true;') | |
} | |
} | |
// =================================================== | |
// =================================================== | |
// BATCH PROC | |
// =================================================== | |
// =================================================== | |
function emitBatchDynamicShaderBody (env, scope, args, program) { | |
env.batchId = 'a1' | |
injectExtensions(env, scope) | |
function all () { | |
return true | |
} | |
emitAttributes(env, scope, args, program.attributes, all) | |
emitUniforms(env, scope, args, program.uniforms, all) | |
emitDraw(env, scope, scope, args) | |
} | |
function emitBatchBody (env, scope, args, program) { | |
injectExtensions(env, scope) | |
var contextDynamic = args.contextDep | |
var BATCH_ID = scope.def() | |
var PROP_LIST = 'a0' | |
var NUM_PROPS = 'a1' | |
var PROPS = scope.def() | |
env.shared.props = PROPS | |
env.batchId = BATCH_ID | |
var outer = env.scope() | |
var inner = env.scope() | |
scope( | |
outer.entry, | |
'for(', BATCH_ID, '=0;', BATCH_ID, '<', NUM_PROPS, ';++', BATCH_ID, '){', | |
PROPS, '=', PROP_LIST, '[', BATCH_ID, '];', | |
inner, | |
'}', | |
outer.exit) | |
function isInnerDefn (defn) { | |
return ((defn.contextDep && contextDynamic) || defn.propDep) | |
} | |
function isOuterDefn (defn) { | |
return !isInnerDefn(defn) | |
} | |
if (args.needsContext) { | |
emitContext(env, inner, args.context) | |
} | |
if (args.needsFramebuffer) { | |
emitPollFramebuffer(env, inner, args.framebuffer) | |
} | |
emitSetOptions(env, inner, args.state, isInnerDefn) | |
if (args.profile && isInnerDefn(args.profile)) { | |
emitProfile(env, inner, args, false, true) | |
} | |
if (!program) { | |
var progCache = env.global.def('{}') | |
var PROGRAM = args.shader.progVar.append(env, inner) | |
var PROG_ID = inner.def(PROGRAM, '.id') | |
var CACHED_PROC = inner.def(progCache, '[', PROG_ID, ']') | |
inner( | |
env.shared.gl, '.useProgram(', PROGRAM, '.program);', | |
'if(!', CACHED_PROC, '){', | |
CACHED_PROC, '=', progCache, '[', PROG_ID, ']=', | |
env.link(function (program) { | |
return createBody( | |
emitBatchDynamicShaderBody, env, args, program, 2) | |
}), '(', PROGRAM, ');}', | |
CACHED_PROC, '.call(this,a0[', BATCH_ID, '],', BATCH_ID, ');') | |
} else { | |
emitAttributes(env, outer, args, program.attributes, isOuterDefn) | |
emitAttributes(env, inner, args, program.attributes, isInnerDefn) | |
emitUniforms(env, outer, args, program.uniforms, isOuterDefn) | |
emitUniforms(env, inner, args, program.uniforms, isInnerDefn) | |
emitDraw(env, outer, inner, args) | |
} | |
} | |
function emitBatchProc (env, args) { | |
var batch = env.proc('batch', 2) | |
env.batchId = '0' | |
injectExtensions(env, batch) | |
// Check if any context variables depend on props | |
var contextDynamic = false | |
var needsContext = true | |
Object.keys(args.context).forEach(function (name) { | |
contextDynamic = contextDynamic || args.context[name].propDep | |
}) | |
if (!contextDynamic) { | |
emitContext(env, batch, args.context) | |
needsContext = false | |
} | |
// framebuffer state affects framebufferWidth/height context vars | |
var framebuffer = args.framebuffer | |
var needsFramebuffer = false | |
if (framebuffer) { | |
if (framebuffer.propDep) { | |
contextDynamic = needsFramebuffer = true | |
} else if (framebuffer.contextDep && contextDynamic) { | |
needsFramebuffer = true | |
} | |
if (!needsFramebuffer) { | |
emitPollFramebuffer(env, batch, framebuffer) | |
} | |
} else { | |
emitPollFramebuffer(env, batch, null) | |
} | |
// viewport is weird because it can affect context vars | |
if (args.state.viewport && args.state.viewport.propDep) { | |
contextDynamic = true | |
} | |
function isInnerDefn (defn) { | |
return (defn.contextDep && contextDynamic) || defn.propDep | |
} | |
// set webgl options | |
emitPollState(env, batch, args) | |
emitSetOptions(env, batch, args.state, function (defn) { | |
return !isInnerDefn(defn) | |
}) | |
if (!args.profile || !isInnerDefn(args.profile)) { | |
emitProfile(env, batch, args, false, 'a1') | |
} | |
// Save these values to args so that the batch body routine can use them | |
args.contextDep = contextDynamic | |
args.needsContext = needsContext | |
args.needsFramebuffer = needsFramebuffer | |
// determine if shader is dynamic | |
var progDefn = args.shader.progVar | |
if ((progDefn.contextDep && contextDynamic) || progDefn.propDep) { | |
emitBatchBody( | |
env, | |
batch, | |
args, | |
null) | |
} else { | |
var PROGRAM = progDefn.append(env, batch) | |
batch(env.shared.gl, '.useProgram(', PROGRAM, '.program);') | |
if (args.shader.program) { | |
emitBatchBody( | |
env, | |
batch, | |
args, | |
args.shader.program) | |
} else { | |
var batchCache = env.global.def('{}') | |
var PROG_ID = batch.def(PROGRAM, '.id') | |
var CACHED_PROC = batch.def(batchCache, '[', PROG_ID, ']') | |
batch( | |
env.cond(CACHED_PROC) | |
.then(CACHED_PROC, '.call(this,a0,a1);') | |
.else( | |
CACHED_PROC, '=', batchCache, '[', PROG_ID, ']=', | |
env.link(function (program) { | |
return createBody(emitBatchBody, env, args, program, 2) | |
}), '(', PROGRAM, ');', | |
CACHED_PROC, '.call(this,a0,a1);')) | |
} | |
} | |
if (Object.keys(args.state).length > 0) { | |
batch(env.shared.current, '.dirty=true;') | |
} | |
} | |
// =================================================== | |
// =================================================== | |
// SCOPE COMMAND | |
// =================================================== | |
// =================================================== | |
function emitScopeProc (env, args) { | |
var scope = env.proc('scope', 3) | |
env.batchId = 'a2' | |
var shared = env.shared | |
var CURRENT_STATE = shared.current | |
emitContext(env, scope, args.context) | |
if (args.framebuffer) { | |
args.framebuffer.append(env, scope) | |
} | |
sortState(Object.keys(args.state)).forEach(function (name) { | |
var defn = args.state[name] | |
var value = defn.append(env, scope) | |
if (isArrayLike(value)) { | |
value.forEach(function (v, i) { | |
scope.set(env.next[name], '[' + i + ']', v) | |
}) | |
} else { | |
scope.set(shared.next, '.' + name, value) | |
} | |
}) | |
emitProfile(env, scope, args, true, true) | |
;[S_ELEMENTS, S_OFFSET, S_COUNT, S_INSTANCES, S_PRIMITIVE].forEach( | |
function (opt) { | |
var variable = args.draw[opt] | |
if (!variable) { | |
return | |
} | |
scope.set(shared.draw, '.' + opt, '' + variable.append(env, scope)) | |
}) | |
Object.keys(args.uniforms).forEach(function (opt) { | |
scope.set( | |
shared.uniforms, | |
'[' + stringStore.id(opt) + ']', | |
args.uniforms[opt].append(env, scope)) | |
}) | |
Object.keys(args.attributes).forEach(function (name) { | |
var record = args.attributes[name].append(env, scope) | |
var scopeAttrib = env.scopeAttrib(name) | |
Object.keys(new AttributeRecord()).forEach(function (prop) { | |
scope.set(scopeAttrib, '.' + prop, record[prop]) | |
}) | |
}) | |
function saveShader (name) { | |
var shader = args.shader[name] | |
if (shader) { | |
scope.set(shared.shader, '.' + name, shader.append(env, scope)) | |
} | |
} | |
saveShader(S_VERT) | |
saveShader(S_FRAG) | |
if (Object.keys(args.state).length > 0) { | |
scope(CURRENT_STATE, '.dirty=true;') | |
scope.exit(CURRENT_STATE, '.dirty=true;') | |
} | |
scope('a1(', env.shared.context, ',a0,0);') | |
} | |
// =========================================================================== | |
// =========================================================================== | |
// MAIN DRAW COMMAND | |
// =========================================================================== | |
// =========================================================================== | |
function compileCommand (options, attributes, uniforms, context, stats) { | |
var env = createREGLEnvironment() | |
// link stats, so that we can easily access it in the program. | |
env.stats = env.link(stats) | |
var args = parseArguments(options, attributes, uniforms, context) | |
emitDrawProc(env, args) | |
emitScopeProc(env, args) | |
emitBatchProc(env, args) | |
return env.compile() | |
} | |
// =========================================================================== | |
// =========================================================================== | |
// POLL / REFRESH | |
// =========================================================================== | |
// =========================================================================== | |
return { | |
next: nextState, | |
current: currentState, | |
procs: (function () { | |
var env = createREGLEnvironment() | |
var poll = env.proc('poll') | |
var refresh = env.proc('refresh') | |
var common = env.block() | |
poll(common) | |
refresh(common) | |
var shared = env.shared | |
var GL = shared.gl | |
var NEXT_STATE = shared.next | |
var CURRENT_STATE = shared.current | |
common(CURRENT_STATE, '.dirty=false;') | |
emitPollFramebuffer(env, poll) | |
refresh(shared.framebuffer, '.dirty=true;') | |
emitPollFramebuffer(env, refresh) | |
// FIXME: refresh should update vertex attribute pointers | |
Object.keys(GL_FLAGS).forEach(function (flag) { | |
var cap = GL_FLAGS[flag] | |
var NEXT = common.def(NEXT_STATE, '.', flag) | |
var block = env.block() | |
block('if(', NEXT, '){', | |
GL, '.enable(', cap, ')}else{', | |
GL, '.disable(', cap, ')}', | |
CURRENT_STATE, '.', flag, '=', NEXT, ';') | |
refresh(block) | |
poll( | |
'if(', NEXT, '!==', CURRENT_STATE, '.', flag, '){', | |
block, | |
'}') | |
}) | |
Object.keys(GL_VARIABLES).forEach(function (name) { | |
var func = GL_VARIABLES[name] | |
var init = currentState[name] | |
var NEXT, CURRENT | |
var block = env.block() | |
block(GL, '.', func, '(') | |
if (isArrayLike(init)) { | |
var n = init.length | |
NEXT = env.global.def(NEXT_STATE, '.', name) | |
CURRENT = env.global.def(CURRENT_STATE, '.', name) | |
block( | |
loop(n, function (i) { | |
return NEXT + '[' + i + ']' | |
}), ');', | |
loop(n, function (i) { | |
return CURRENT + '[' + i + ']=' + NEXT + '[' + i + '];' | |
}).join('')) | |
poll( | |
'if(', loop(n, function (i) { | |
return NEXT + '[' + i + ']!==' + CURRENT + '[' + i + ']' | |
}).join('||'), '){', | |
block, | |
'}') | |
} else { | |
NEXT = common.def(NEXT_STATE, '.', name) | |
CURRENT = common.def(CURRENT_STATE, '.', name) | |
block( | |
NEXT, ');', | |
CURRENT_STATE, '.', name, '=', NEXT, ';') | |
poll( | |
'if(', NEXT, '!==', CURRENT, '){', | |
block, | |
'}') | |
} | |
refresh(block) | |
}) | |
return env.compile() | |
})(), | |
compile: compileCommand | |
} | |
} | |
},{"./constants/dtypes.json":4,"./constants/primitives.json":5,"./util/check":20,"./util/codegen":22,"./util/is-array-like":24,"./util/is-ndarray":25,"./util/is-typed-array":26,"./util/loop":27}],8:[function(require,module,exports){ | |
var check = require('./util/check') | |
var VARIABLE_COUNTER = 0 | |
var DYN_FUNC = 0 | |
var DYN_PENDING_FLAG = 128 | |
function DynamicVariable (type, data) { | |
this.id = (VARIABLE_COUNTER++) | |
this.type = type | |
this.data = data | |
} | |
function escapeStr (str) { | |
return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"') | |
} | |
function splitParts (str) { | |
if (str.length === 0) { | |
return [] | |
} | |
var firstChar = str.charAt(0) | |
var lastChar = str.charAt(str.length - 1) | |
if (str.length > 1 && | |
firstChar === lastChar && | |
(firstChar === '"' || firstChar === "'")) { | |
return ['"' + escapeStr(str.substr(1, str.length - 2)) + '"'] | |
} | |
var parts = /\[(false|true|null|\d+|'[^']*'|"[^"]*")\]/.exec(str) | |
if (parts) { | |
return ( | |
splitParts(str.substr(0, parts.index)) | |
.concat(splitParts(parts[1])) | |
.concat(splitParts(str.substr(parts.index + parts[0].length))) | |
) | |
} | |
var subparts = str.split('.') | |
if (subparts.length === 1) { | |
return ['"' + escapeStr(str) + '"'] | |
} | |
var result = [] | |
for (var i = 0; i < subparts.length; ++i) { | |
result = result.concat(splitParts(subparts[i])) | |
} | |
return result | |
} | |
function toAccessorString (str) { | |
return '[' + splitParts(str).join('][') + ']' | |
} | |
function defineDynamic (type, data) { | |
switch (typeof data) { | |
case 'boolean': | |
case 'number': | |
case 'string': | |
return new DynamicVariable(type, toAccessorString(data + '')) | |
case 'undefined': | |
return new DynamicVariable(type | DYN_PENDING_FLAG, null) | |
default: | |
check.raise('invalid property type') | |
} | |
} | |
function isDynamic (x) { | |
return (typeof x === 'function' && !x._reglType) || | |
x instanceof DynamicVariable | |
} | |
function unbox (x, path) { | |
if (x instanceof DynamicVariable) { | |
if (x.type & DYN_PENDING_FLAG) { | |
return new DynamicVariable( | |
x.type & ~DYN_PENDING_FLAG, | |
toAccessorString(path)) | |
} | |
} else if (typeof x === 'function') { | |
return new DynamicVariable(DYN_FUNC, x) | |
} | |
return x | |
} | |
module.exports = { | |
define: defineDynamic, | |
isDynamic: isDynamic, | |
unbox: unbox, | |
accessor: toAccessorString | |
} | |
},{"./util/check":20}],9:[function(require,module,exports){ | |
var check = require('./util/check') | |
var isTypedArray = require('./util/is-typed-array') | |
var isNDArrayLike = require('./util/is-ndarray') | |
var values = require('./util/values') | |
var primTypes = require('./constants/primitives.json') | |
var usageTypes = require('./constants/usage.json') | |
var GL_POINTS = 0 | |
var GL_LINES = 1 | |
var GL_TRIANGLES = 4 | |
var GL_BYTE = 5120 | |
var GL_UNSIGNED_BYTE = 5121 | |
var GL_SHORT = 5122 | |
var GL_UNSIGNED_SHORT = 5123 | |
var GL_INT = 5124 | |
var GL_UNSIGNED_INT = 5125 | |
var GL_ELEMENT_ARRAY_BUFFER = 34963 | |
var GL_STREAM_DRAW = 0x88E0 | |
var GL_STATIC_DRAW = 0x88E4 | |
module.exports = function wrapElementsState (gl, extensions, bufferState, stats) { | |
var elementSet = {} | |
var elementCount = 0 | |
var elementTypes = { | |
'uint8': GL_UNSIGNED_BYTE, | |
'uint16': GL_UNSIGNED_SHORT | |
} | |
if (extensions.oes_element_index_uint) { | |
elementTypes.uint32 = GL_UNSIGNED_INT | |
} | |
function REGLElementBuffer (buffer) { | |
this.id = elementCount++ | |
elementSet[this.id] = this | |
this.buffer = buffer | |
this.primType = GL_TRIANGLES | |
this.vertCount = 0 | |
this.type = 0 | |
} | |
REGLElementBuffer.prototype.bind = function () { | |
this.buffer.bind() | |
} | |
var bufferPool = [] | |
function createElementStream (data) { | |
var result = bufferPool.pop() | |
if (!result) { | |
result = new REGLElementBuffer(bufferState.create( | |
null, | |
GL_ELEMENT_ARRAY_BUFFER, | |
true)._buffer) | |
} | |
initElements(result, data, GL_STREAM_DRAW, -1, -1, 0, 0) | |
return result | |
} | |
function destroyElementStream (elements) { | |
bufferPool.push(elements) | |
} | |
function initElements ( | |
elements, | |
data, | |
usage, | |
prim, | |
count, | |
byteLength, | |
type) { | |
var predictedType = type | |
if (!type && ( | |
!isTypedArray(data) || | |
(isNDArrayLike(data) && !isTypedArray(data.data)))) { | |
predictedType = extensions.oes_element_index_uint | |
? GL_UNSIGNED_INT | |
: GL_UNSIGNED_SHORT | |
} | |
elements.buffer.bind() | |
bufferState._initBuffer( | |
elements.buffer, | |
data, | |
usage, | |
predictedType, | |
3) | |
var dtype = type | |
if (!type) { | |
switch (elements.buffer.dtype) { | |
case GL_UNSIGNED_BYTE: | |
case GL_BYTE: | |
dtype = GL_UNSIGNED_BYTE | |
break | |
case GL_UNSIGNED_SHORT: | |
case GL_SHORT: | |
dtype = GL_UNSIGNED_SHORT | |
break | |
case GL_UNSIGNED_INT: | |
case GL_INT: | |
dtype = GL_UNSIGNED_INT | |
break | |
default: | |
check.raise('unsupported type for element array') | |
} | |
elements.buffer.dtype = dtype | |
} | |
elements.type = dtype | |
// Check oes_element_index_uint extension | |
check( | |
dtype !== GL_UNSIGNED_INT || | |
!!extensions.oes_element_index_uint, | |
'32 bit element buffers not supported, enable oes_element_index_uint first') | |
// try to guess default primitive type and arguments | |
var vertCount = count | |
if (vertCount < 0) { | |
vertCount = elements.buffer.byteLength | |
if (dtype === GL_UNSIGNED_SHORT) { | |
vertCount >>= 1 | |
} else if (dtype === GL_UNSIGNED_INT) { | |
vertCount >>= 2 | |
} | |
} | |
elements.vertCount = vertCount | |
// try to guess primitive type from cell dimension | |
var primType = prim | |
if (prim < 0) { | |
primType = GL_TRIANGLES | |
var dimension = elements.buffer.dimension | |
if (dimension === 1) primType = GL_POINTS | |
if (dimension === 2) primType = GL_LINES | |
if (dimension === 3) primType = GL_TRIANGLES | |
} | |
elements.primType = primType | |
} | |
function destroyElements (elements) { | |
stats.elementsCount-- | |
check(elements.buffer !== null, 'must not double destroy elements') | |
delete elementSet[elements.id] | |
elements.buffer.destroy() | |
elements.buffer = null | |
} | |
function createElements (options) { | |
var buffer = bufferState.create(null, GL_ELEMENT_ARRAY_BUFFER, true) | |
var elements = new REGLElementBuffer(buffer._buffer) | |
stats.elementsCount++ | |
function reglElements (options) { | |
if (!options) { | |
buffer() | |
elements.primType = GL_TRIANGLES | |
elements.vertCount = 0 | |
elements.type = GL_UNSIGNED_BYTE | |
} else if (typeof options === 'number') { | |
buffer(options) | |
elements.primType = GL_TRIANGLES | |
elements.vertCount = options | 0 | |
elements.type = GL_UNSIGNED_BYTE | |
} else { | |
var data = null | |
var usage = GL_STATIC_DRAW | |
var primType = -1 | |
var vertCount = -1 | |
var byteLength = 0 | |
var dtype = 0 | |
if (Array.isArray(options) || | |
isTypedArray(options) || | |
isNDArrayLike(options)) { | |
data = options | |
} else { | |
check.type(options, 'object', 'invalid arguments for elements') | |
if ('data' in options) { | |
data = options.data | |
check( | |
Array.isArray(data) || | |
isTypedArray(data) || | |
isNDArrayLike(data), | |
'invalid data for element buffer') | |
} | |
if ('usage' in options) { | |
check.parameter( | |
options.usage, | |
usageTypes, | |
'invalid element buffer usage') | |
usage = usageTypes[options.usage] | |
} | |
if ('primitive' in options) { | |
check.parameter( | |
options.primitive, | |
primTypes, | |
'invalid element buffer primitive') | |
primType = primTypes[options.primitive] | |
} | |
if ('count' in options) { | |
check( | |
typeof options.count === 'number' && options.count >= 0, | |
'invalid vertex count for elements') | |
vertCount = options.count | 0 | |
} | |
if ('length' in options) { | |
byteLength = options.length | 0 | |
} | |
if ('type' in options) { | |
check.parameter( | |
options.type, | |
elementTypes, | |
'invalid buffer type') | |
dtype = elementTypes[options.type] | |
} | |
} | |
if (data) { | |
initElements( | |
elements, | |
data, | |
usage, | |
primType, | |
vertCount, | |
byteLength, | |
dtype) | |
} else { | |
var _buffer = elements.buffer | |
_buffer.bind() | |
gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, byteLength, usage) | |
_buffer.dtype = dtype || GL_UNSIGNED_BYTE | |
_buffer.usage = usage | |
_buffer.dimension = 3 | |
_buffer.byteLength = byteLength | |
elements.primType = primType < 0 ? GL_TRIANGLES : primType | |
elements.vertCount = vertCount < 0 ? 0 : vertCount | |
elements.type = _buffer.dtype | |
} | |
} | |
return reglElements | |
} | |
reglElements(options) | |
reglElements._reglType = 'elements' | |
reglElements._elements = elements | |
reglElements.subdata = function (data, offset) { | |
buffer.subdata(data, offset) | |
return reglElements | |
} | |
reglElements.destroy = function () { | |
destroyElements(elements) | |
} | |
return reglElements | |
} | |
return { | |
create: createElements, | |
createStream: createElementStream, | |
destroyStream: destroyElementStream, | |
getElements: function (elements) { | |
if (typeof elements === 'function' && | |
elements._elements instanceof REGLElementBuffer) { | |
return elements._elements | |
} | |
return null | |
}, | |
clear: function () { | |
values(elementSet).forEach(destroyElements) | |
} | |
} | |
} | |
},{"./constants/primitives.json":5,"./constants/usage.json":6,"./util/check":20,"./util/is-ndarray":25,"./util/is-typed-array":26,"./util/values":31}],10:[function(require,module,exports){ | |
var check = require('./util/check') | |
module.exports = function createExtensionCache (gl, config) { | |
var extensions = {} | |
function tryLoadExtension (name_) { | |
check.type(name_, 'string', 'extension name must be string') | |
var name = name_.toLowerCase() | |
if (name in extensions) { | |
return true | |
} | |
var ext | |
try { | |
ext = extensions[name] = gl.getExtension(name) | |
} catch (e) {} | |
return !!ext | |
} | |
for (var i = 0; i < config.extensions.length; ++i) { | |
var name = config.extensions[i] | |
if (!tryLoadExtension(name)) { | |
config.onDestroy() | |
config.onDone('"' + name + '" extension is not supported by the current WebGL context, try upgrading your system or a different browser') | |
return null | |
} | |
} | |
config.optionalExtensions.forEach(tryLoadExtension) | |
return { | |
extensions: extensions, | |
refresh: function () { | |
config.extensions.forEach(tryLoadExtension) | |
config.optionalExtensions.forEach(tryLoadExtension) | |
} | |
} | |
} | |
},{"./util/check":20}],11:[function(require,module,exports){ | |
var check = require('./util/check') | |
var values = require('./util/values') | |
var extend = require('./util/extend') | |
// We store these constants so that the minifier can inline them | |
var GL_FRAMEBUFFER = 0x8D40 | |
var GL_RENDERBUFFER = 0x8D41 | |
var GL_TEXTURE_2D = 0x0DE1 | |
var GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515 | |
var GL_COLOR_ATTACHMENT0 = 0x8CE0 | |
var GL_DEPTH_ATTACHMENT = 0x8D00 | |
var GL_STENCIL_ATTACHMENT = 0x8D20 | |
var GL_DEPTH_STENCIL_ATTACHMENT = 0x821A | |
var GL_FRAMEBUFFER_COMPLETE = 0x8CD5 | |
var GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6 | |
var GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7 | |
var GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS = 0x8CD9 | |
var GL_FRAMEBUFFER_UNSUPPORTED = 0x8CDD | |
var GL_HALF_FLOAT_OES = 0x8D61 | |
var GL_UNSIGNED_BYTE = 0x1401 | |
var GL_FLOAT = 0x1406 | |
var GL_RGBA = 0x1908 | |
var GL_ALPHA = 0x1906 | |
var GL_RGB = 0x1907 | |
var GL_LUMINANCE = 0x1909 | |
var GL_LUMINANCE_ALPHA = 0x190A | |
var GL_DEPTH_COMPONENT = 0x1902 | |
var colorTextureFormatEnums = [ | |
GL_ALPHA, | |
GL_LUMINANCE, | |
GL_LUMINANCE_ALPHA, | |
GL_RGB, | |
GL_RGBA | |
] | |
// for every texture format, store | |
// the number of channels | |
var textureFormatChannels = [] | |
textureFormatChannels[GL_ALPHA] = 1 | |
textureFormatChannels[GL_LUMINANCE] = 1 | |
textureFormatChannels[GL_LUMINANCE_ALPHA] = 2 | |
textureFormatChannels[GL_RGB] = 3 | |
textureFormatChannels[GL_RGBA] = 4 | |
// for every texture type, store | |
// the size in bytes. | |
var textureTypeSizes = [] | |
textureTypeSizes[GL_UNSIGNED_BYTE] = 1 | |
textureTypeSizes[GL_FLOAT] = 4 | |
textureTypeSizes[GL_HALF_FLOAT_OES] = 2 | |
var GL_RGBA4 = 0x8056 | |
var GL_RGB5_A1 = 0x8057 | |
var GL_RGB565 = 0x8D62 | |
var GL_DEPTH_COMPONENT16 = 0x81A5 | |
var GL_STENCIL_INDEX8 = 0x8D48 | |
var GL_DEPTH_STENCIL = 0x84F9 | |
var GL_SRGB8_ALPHA8_EXT = 0x8C43 | |
var GL_RGBA32F_EXT = 0x8814 | |
var GL_RGBA16F_EXT = 0x881A | |
var GL_RGB16F_EXT = 0x881B | |
var colorRenderbufferFormatEnums = [ | |
GL_RGBA4, | |
GL_RGB5_A1, | |
GL_RGB565, | |
GL_SRGB8_ALPHA8_EXT, | |
GL_RGBA16F_EXT, | |
GL_RGB16F_EXT, | |
GL_RGBA32F_EXT | |
] | |
var statusCode = {} | |
statusCode[GL_FRAMEBUFFER_COMPLETE] = 'complete' | |
statusCode[GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT] = 'incomplete attachment' | |
statusCode[GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS] = 'incomplete dimensions' | |
statusCode[GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT] = 'incomplete, missing attachment' | |
statusCode[GL_FRAMEBUFFER_UNSUPPORTED] = 'unsupported' | |
module.exports = function wrapFBOState ( | |
gl, | |
extensions, | |
limits, | |
textureState, | |
renderbufferState, | |
stats) { | |
var framebufferState = { | |
current: null, | |
next: null, | |
dirty: false | |
} | |
var colorTextureFormats = ['rgba'] | |
var colorRenderbufferFormats = ['rgba4', 'rgb565', 'rgb5 a1'] | |
if (extensions.ext_srgb) { | |
colorRenderbufferFormats.push('srgba') | |
} | |
if (extensions.ext_color_buffer_half_float) { | |
colorRenderbufferFormats.push('rgba16f', 'rgb16f') | |
} | |
if (extensions.webgl_color_buffer_float) { | |
colorRenderbufferFormats.push('rgba32f') | |
} | |
var colorTypes = ['uint8'] | |
if (extensions.oes_texture_half_float) { | |
colorTypes.push('half float') | |
} | |
if (extensions.oes_texture_float) { | |
colorTypes.push('float') | |
} | |
function FramebufferAttachment (target, texture, renderbuffer) { | |
this.target = target | |
this.texture = texture | |
this.renderbuffer = renderbuffer | |
var w = 0 | |
var h = 0 | |
if (texture) { | |
w = texture.width | |
h = texture.height | |
} else if (renderbuffer) { | |
w = renderbuffer.width | |
h = renderbuffer.height | |
} | |
this.width = w | |
this.height = h | |
} | |
function decRef (attachment) { | |
if (attachment) { | |
if (attachment.texture) { | |
attachment.texture._texture.decRef() | |
} | |
if (attachment.renderbuffer) { | |
attachment.renderbuffer._renderbuffer.decRef() | |
} | |
} | |
} | |
function incRefAndCheckShape (attachment, width, height) { | |
if (!attachment) { | |
return | |
} | |
if (attachment.texture) { | |
var texture = attachment.texture._texture | |
var tw = Math.max(1, texture.width) | |
var th = Math.max(1, texture.height) | |
check(tw === width && th === height, | |
'inconsistent width/height for supplied texture') | |
texture.refCount += 1 | |
} else { | |
var renderbuffer = attachment.renderbuffer._renderbuffer | |
check( | |
renderbuffer.width === width && renderbuffer.height === height, | |
'inconsistent width/height for renderbuffer') | |
renderbuffer.refCount += 1 | |
} | |
} | |
function attach (location, attachment) { | |
if (attachment) { | |
if (attachment.texture) { | |
gl.framebufferTexture2D( | |
GL_FRAMEBUFFER, | |
location, | |
attachment.target, | |
attachment.texture._texture.texture, | |
0) | |
} else { | |
gl.framebufferRenderbuffer( | |
GL_FRAMEBUFFER, | |
location, | |
GL_RENDERBUFFER, | |
attachment.renderbuffer._renderbuffer.renderbuffer) | |
} | |
} else { | |
gl.framebufferTexture2D( | |
GL_FRAMEBUFFER, | |
location, | |
GL_TEXTURE_2D, | |
null, | |
0) | |
} | |
} | |
function parseAttachment (attachment) { | |
var target = GL_TEXTURE_2D | |
var texture = null | |
var renderbuffer = null | |
var data = attachment | |
if (typeof attachment === 'object') { | |
data = attachment.data | |
if ('target' in attachment) { | |
// target = attachment.target | 0 | |
} | |
} | |
check.type(data, 'function', 'invalid attachment data') | |
var type = data._reglType | |
if (type === 'texture2d') { | |
texture = data | |
check(target === GL_TEXTURE_2D) | |
} else if (type === 'textureCube') { | |
texture = data | |
check( | |
target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && | |
target < GL_TEXTURE_CUBE_MAP_POSITIVE_X + 6, | |
'invalid cube map target') | |
} else if (type === 'renderbuffer') { | |
renderbuffer = data | |
target = GL_RENDERBUFFER | |
} else { | |
check.raise('invalid regl object for attachment') | |
} | |
return new FramebufferAttachment(target, texture, renderbuffer) | |
} | |
function allocAttachment ( | |
width, | |
height, | |
isTexture, | |
format, | |
type) { | |
if (isTexture) { | |
var texture = textureState.create2D({ | |
width: width, | |
height: height, | |
format: format, | |
type: type | |
}) | |
texture._texture.refCount = 0 | |
return new FramebufferAttachment(GL_TEXTURE_2D, texture, null) | |
} else { | |
var rb = renderbufferState.create({ | |
width: width, | |
height: height, | |
format: format | |
}) | |
rb._renderbuffer.refCount = 0 | |
return new FramebufferAttachment(GL_RENDERBUFFER, null, rb) | |
} | |
} | |
function unwrapAttachment (attachment) { | |
return attachment && (attachment.texture || attachment.renderbuffer) | |
} | |
function resizeAttachment (attachment, w, h) { | |
if (attachment) { | |
if (attachment.texture) { | |
attachment.texture.resize(w, h) | |
} else if (attachment.renderbuffer) { | |
attachment.renderbuffer.resize(w, h) | |
} | |
} | |
} | |
var framebufferCount = 0 | |
var framebufferSet = {} | |
function REGLFramebuffer () { | |
this.id = framebufferCount++ | |
framebufferSet[this.id] = this | |
this.framebuffer = gl.createFramebuffer() | |
this.width = 0 | |
this.height = 0 | |
this.colorAttachments = [] | |
this.depthAttachment = null | |
this.stencilAttachment = null | |
this.depthStencilAttachment = null | |
} | |
function decFBORefs (framebuffer) { | |
framebuffer.colorAttachments.forEach(decRef) | |
decRef(framebuffer.depthAttachment) | |
decRef(framebuffer.stencilAttachment) | |
decRef(framebuffer.depthStencilAttachment) | |
} | |
function destroy (framebuffer) { | |
var handle = framebuffer.framebuffer | |
check(handle, 'must not double destroy framebuffer') | |
gl.deleteFramebuffer(handle) | |
framebuffer.framebuffer = null | |
stats.framebufferCount-- | |
delete framebufferSet[framebuffer.id] | |
} | |
function updateFramebuffer (framebuffer) { | |
var i | |
gl.bindFramebuffer(GL_FRAMEBUFFER, framebuffer.framebuffer) | |
var colorAttachments = framebuffer.colorAttachments | |
for (i = 0; i < colorAttachments.length; ++i) { | |
attach(GL_COLOR_ATTACHMENT0 + i, colorAttachments[i]) | |
} | |
for (i = colorAttachments.length; i < limits.maxColorAttachments; ++i) { | |
attach(GL_COLOR_ATTACHMENT0 + i, null) | |
} | |
attach(GL_DEPTH_ATTACHMENT, framebuffer.depthAttachment) | |
attach(GL_STENCIL_ATTACHMENT, framebuffer.stencilAttachment) | |
attach(GL_DEPTH_STENCIL_ATTACHMENT, framebuffer.depthStencilAttachment) | |
// Check status code | |
var status = gl.checkFramebufferStatus(GL_FRAMEBUFFER) | |
if (status !== GL_FRAMEBUFFER_COMPLETE) { | |
check.raise('framebuffer configuration not supported, status = ' + | |
statusCode[status]) | |
} | |
gl.bindFramebuffer(GL_FRAMEBUFFER, framebufferState.next) | |
framebufferState.current = framebufferState.next | |
} | |
function createFBO (a0, a1) { | |
var framebuffer = new REGLFramebuffer() | |
stats.framebufferCount++ | |
function reglFramebuffer (a, b) { | |
var i | |
check(framebufferState.next !== framebuffer, | |
'can not update framebuffer which is currently in use') | |
var extDrawBuffers = extensions.webgl_draw_buffers | |
var width = 0 | |
var height = 0 | |
var needsDepth = true | |
var needsStencil = true | |
var colorBuffer = null | |
var colorTexture = true | |
var colorFormat = 'rgba' | |
var colorType = 'uint8' | |
var colorCount = 1 | |
var depthBuffer = null | |
var stencilBuffer = null | |
var depthStencilBuffer = null | |
var depthStencilTexture = false | |
if (typeof a === 'number') { | |
width = a | 0 | |
height = (b | 0) || width | |
} else if (!a) { | |
width = height = 1 | |
} else { | |
check.type(a, 'object', 'invalid arguments for framebuffer') | |
var options = a | |
if ('shape' in options) { | |
var shape = options.shape | |
check(Array.isArray(shape) && shape.length >= 2, | |
'invalid shape for framebuffer') | |
width = shape[0] | |
height = shape[1] | |
} else { | |
if ('radius' in options) { | |
width = height = options.radius | |
} | |
if ('width' in options) { | |
width = options.width | |
} | |
if ('height' in options) { | |
height = options.height | |
} | |
} | |
if ('color' in options || | |
'colors' in options) { | |
colorBuffer = | |
options.color || | |
options.colors | |
if (Array.isArray(colorBuffer)) { | |
check( | |
colorBuffer.length === 1 || extDrawBuffers, | |
'multiple render targets not supported') | |
} | |
} | |
if (!colorBuffer) { | |
if ('colorCount' in options) { | |
colorCount = options.colorCount | 0 | |
check(colorCount > 0, 'invalid color buffer count') | |
} | |
if ('colorTexture' in options) { | |
colorTexture = !!options.colorTexture | |
colorFormat = 'rgba4' | |
} | |
if ('colorType' in options) { | |
check.oneOf( | |
options.colorType, colorTypes, | |
'invalid color type') | |
colorType = options.colorType | |
if (!colorTexture) { | |
if (colorType === 'half float') { | |
if (extensions.ext_color_buffer_half_float) { | |
colorFormat = 'rgba16f' | |
} | |
} else if (colorType === 'float') { | |
if (extensions.webgl_color_buffer_float) { | |
colorFormat = 'rgba32f' | |
} | |
} | |
} | |
} | |
if ('colorFormat' in options) { | |
colorFormat = options.colorFormat | |
if (colorTextureFormats.indexOf(colorFormat) >= 0) { | |
colorTexture = true | |
} else if (colorRenderbufferFormats.indexOf(colorFormat) >= 0) { | |
colorTexture = false | |
} else { | |
if (colorTexture) { | |
check.oneOf( | |
options.colorFormat, colorTextureFormats, | |
'invalid color format for texture') | |
} else { | |
check.oneOf( | |
options.colorFormat, colorRenderbufferFormats, | |
'invalid color format for renderbuffer') | |
} | |
} | |
} | |
} | |
if ('depthTexture' in options || 'depthStencilTexture' in options) { | |
depthStencilTexture = !!(options.depthTexture || | |
options.depthStencilTexture) | |
check(!depthStencilTexture || extensions.webgl_depth_texture, | |
'webgl_depth_texture extension not supported') | |
} | |
if ('depth' in options) { | |
if (typeof options.depth === 'boolean') { | |
needsDepth = options.depth | |
} else { | |
depthBuffer = options.depth | |
needsStencil = false | |
} | |
} | |
if ('stencil' in options) { | |
if (typeof options.stencil === 'boolean') { | |
needsStencil = options.stencil | |
} else { | |
stencilBuffer = options.stencil | |
needsDepth = false | |
} | |
} | |
if ('depthStencil' in options) { | |
if (typeof options.depthStencil === 'boolean') { | |
needsDepth = needsStencil = options.depthStencil | |
} else { | |
depthStencilBuffer = options.depthStencil | |
needsDepth = false | |
needsStencil = false | |
} | |
} | |
} | |
// parse attachments | |
var colorAttachments = null | |
var depthAttachment = null | |
var stencilAttachment = null | |
var depthStencilAttachment = null | |
// Set up color attachments | |
if (Array.isArray(colorBuffer)) { | |
colorAttachments = colorBuffer.map(parseAttachment) | |
} else if (colorBuffer) { | |
colorAttachments = [parseAttachment(colorBuffer)] | |
} else { | |
colorAttachments = new Array(colorCount) | |
for (i = 0; i < colorCount; ++i) { | |
colorAttachments[i] = allocAttachment( | |
width, | |
height, | |
colorTexture, | |
colorFormat, | |
colorType) | |
} | |
} | |
check(colorAttachments.length <= limits.maxColorAttachments, | |
'too many color attachments, not supported') | |
width = width || colorAttachments[0].width | |
height = height || colorAttachments[0].height | |
if (depthBuffer) { | |
depthAttachment = parseAttachment(depthBuffer) | |
} else if (needsDepth && !needsStencil) { | |
depthAttachment = allocAttachment( | |
width, | |
height, | |
depthStencilTexture, | |
'depth', | |
'uint32') | |
} | |
if (stencilBuffer) { | |
stencilAttachment = parseAttachment(stencilBuffer) | |
} else if (needsStencil && !needsDepth) { | |
stencilAttachment = allocAttachment( | |
width, | |
height, | |
false, | |
'stencil', | |
'uint8') | |
} | |
if (depthStencilBuffer) { | |
depthStencilAttachment = parseAttachment(depthStencilBuffer) | |
} else if (!depthBuffer && !stencilBuffer && needsStencil && needsDepth) { | |
depthStencilAttachment = allocAttachment( | |
width, | |
height, | |
depthStencilTexture, | |
'depth stencil', | |
'depth stencil') | |
} | |
check( | |
(!!depthBuffer) + (!!stencilBuffer) + (!!depthStencilBuffer) <= 1, | |
'invalid framebuffer configuration, can specify exactly one depth/stencil attachment') | |
var commonColorAttachmentSize = null | |
for (i = 0; i < colorAttachments.length; ++i) { | |
incRefAndCheckShape(colorAttachments[i], width, height) | |
check(!colorAttachments[i] || | |
(colorAttachments[i].texture && | |
colorTextureFormatEnums.indexOf(colorAttachments[i].texture._texture.format) >= 0) || | |
(colorAttachments[i].renderbuffer && | |
colorRenderbufferFormatEnums.indexOf(colorAttachments[i].renderbuffer._renderbuffer.format) >= 0), | |
'framebuffer color attachment ' + i + ' is invalid') | |
if (colorAttachments[i] && colorAttachments[i].texture) { | |
var colorAttachmentSize = | |
textureFormatChannels[colorAttachments[i].texture._texture.format] * | |
textureTypeSizes[colorAttachments[i].texture._texture.type] | |
if (commonColorAttachmentSize === null) { | |
commonColorAttachmentSize = colorAttachmentSize | |
} else { | |
// We need to make sure that all color attachments have the same number of bitplanes | |
// (that is, the same numer of bits per pixel) | |
// This is required by the GLES2.0 standard. See the beginning of Chapter 4 in that document. | |
check(commonColorAttachmentSize === colorAttachmentSize, | |
'all color attachments much have the same number of bits per pixel.') | |
} | |
} | |
} | |
incRefAndCheckShape(depthAttachment, width, height) | |
check(!depthAttachment || | |
(depthAttachment.texture && | |
depthAttachment.texture._texture.format === GL_DEPTH_COMPONENT) || | |
(depthAttachment.renderbuffer && | |
depthAttachment.renderbuffer._renderbuffer.format === GL_DEPTH_COMPONENT16), | |
'invalid depth attachment for framebuffer object') | |
incRefAndCheckShape(stencilAttachment, width, height) | |
check(!stencilAttachment || | |
(stencilAttachment.renderbuffer && | |
stencilAttachment.renderbuffer._renderbuffer.format === GL_STENCIL_INDEX8), | |
'invalid stencil attachment for framebuffer object') | |
incRefAndCheckShape(depthStencilAttachment, width, height) | |
check(!depthStencilAttachment || | |
(depthStencilAttachment.texture && | |
depthStencilAttachment.texture._texture.format === GL_DEPTH_STENCIL) || | |
(depthStencilAttachment.renderbuffer && | |
depthStencilAttachment.renderbuffer._renderbuffer.format === GL_DEPTH_STENCIL), | |
'invalid depth-stencil attachment for framebuffer object') | |
// decrement references | |
decFBORefs(framebuffer) | |
framebuffer.width = width | |
framebuffer.height = height | |
framebuffer.colorAttachments = colorAttachments | |
framebuffer.depthAttachment = depthAttachment | |
framebuffer.stencilAttachment = stencilAttachment | |
framebuffer.depthStencilAttachment = depthStencilAttachment | |
reglFramebuffer.color = colorAttachments.map(unwrapAttachment) | |
reglFramebuffer.depth = unwrapAttachment(depthAttachment) | |
reglFramebuffer.stencil = unwrapAttachment(stencilAttachment) | |
reglFramebuffer.depthStencil = unwrapAttachment(depthStencilAttachment) | |
reglFramebuffer.width = framebuffer.width | |
reglFramebuffer.height = framebuffer.height | |
updateFramebuffer(framebuffer) | |
return reglFramebuffer | |
} | |
function resize (w_, h_) { | |
check(framebufferState.next !== framebuffer, | |
'can not resize a framebuffer which is currently in use') | |
var w = w_ | 0 | |
var h = (h_ | 0) || w | |
if (w === framebuffer.width && h === framebuffer.height) { | |
return reglFramebuffer | |
} | |
// resize all buffers | |
var colorAttachments = framebuffer.colorAttachments | |
for (var i = 0; i < colorAttachments.length; ++i) { | |
resizeAttachment(colorAttachments[i], w, h) | |
} | |
resizeAttachment(framebuffer.depthAttachment, w, h) | |
resizeAttachment(framebuffer.stencilAttachment, w, h) | |
resizeAttachment(framebuffer.depthStencilAttachment, w, h) | |
framebuffer.width = reglFramebuffer.width = w | |
framebuffer.height = reglFramebuffer.height = h | |
updateFramebuffer(framebuffer) | |
return reglFramebuffer | |
} | |
reglFramebuffer(a0, a1) | |
reglFramebuffer.resize = resize | |
reglFramebuffer._reglType = 'framebuffer' | |
reglFramebuffer._framebuffer = framebuffer | |
reglFramebuffer.destroy = function () { | |
destroy(framebuffer) | |
decFBORefs(framebuffer) | |
} | |
return reglFramebuffer | |
} | |
return extend(framebufferState, { | |
getFramebuffer: function (object) { | |
if (typeof object === 'function' && object._reglType === 'framebuffer') { | |
var fbo = object._framebuffer | |
if (fbo instanceof REGLFramebuffer) { | |
return fbo | |
} | |
} | |
return null | |
}, | |
create: createFBO, | |
clear: function () { | |
values(framebufferSet).forEach(destroy) | |
} | |
}) | |
} | |
},{"./util/check":20,"./util/extend":23,"./util/values":31}],12:[function(require,module,exports){ | |
var GL_SUBPIXEL_BITS = 0x0D50 | |
var GL_RED_BITS = 0x0D52 | |
var GL_GREEN_BITS = 0x0D53 | |
var GL_BLUE_BITS = 0x0D54 | |
var GL_ALPHA_BITS = 0x0D55 | |
var GL_DEPTH_BITS = 0x0D56 | |
var GL_STENCIL_BITS = 0x0D57 | |
var GL_ALIASED_POINT_SIZE_RANGE = 0x846D | |
var GL_ALIASED_LINE_WIDTH_RANGE = 0x846E | |
var GL_MAX_TEXTURE_SIZE = 0x0D33 | |
var GL_MAX_VIEWPORT_DIMS = 0x0D3A | |
var GL_MAX_VERTEX_ATTRIBS = 0x8869 | |
var GL_MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB | |
var GL_MAX_VARYING_VECTORS = 0x8DFC | |
var GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = 0x8B4D | |
var GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x8B4C | |
var GL_MAX_TEXTURE_IMAGE_UNITS = 0x8872 | |
var GL_MAX_FRAGMENT_UNIFORM_VECTORS = 0x8DFD | |
var GL_MAX_CUBE_MAP_TEXTURE_SIZE = 0x851C | |
var GL_MAX_RENDERBUFFER_SIZE = 0x84E8 | |
var GL_VENDOR = 0x1F00 | |
var GL_RENDERER = 0x1F01 | |
var GL_VERSION = 0x1F02 | |
var GL_SHADING_LANGUAGE_VERSION = 0x8B8C | |
var GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF | |
var GL_MAX_COLOR_ATTACHMENTS_WEBGL = 0x8CDF | |
var GL_MAX_DRAW_BUFFERS_WEBGL = 0x8824 | |
module.exports = function (gl, extensions) { | |
var maxAnisotropic = 1 | |
if (extensions.ext_texture_filter_anisotropic) { | |
maxAnisotropic = gl.getParameter(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT) | |
} | |
var maxDrawbuffers = 1 | |
var maxColorAttachments = 1 | |
if (extensions.webgl_draw_buffers) { | |
maxDrawbuffers = gl.getParameter(GL_MAX_DRAW_BUFFERS_WEBGL) | |
maxColorAttachments = gl.getParameter(GL_MAX_COLOR_ATTACHMENTS_WEBGL) | |
} | |
return { | |
// drawing buffer bit depth | |
colorBits: [ | |
gl.getParameter(GL_RED_BITS), | |
gl.getParameter(GL_GREEN_BITS), | |
gl.getParameter(GL_BLUE_BITS), | |
gl.getParameter(GL_ALPHA_BITS) | |
], | |
depthBits: gl.getParameter(GL_DEPTH_BITS), | |
stencilBits: gl.getParameter(GL_STENCIL_BITS), | |
subpixelBits: gl.getParameter(GL_SUBPIXEL_BITS), | |
// supported extensions | |
extensions: Object.keys(extensions).filter(function (ext) { | |
return !!extensions[ext] | |
}), | |
// max aniso samples | |
maxAnisotropic: maxAnisotropic, | |
// max draw buffers | |
maxDrawbuffers: maxDrawbuffers, | |
maxColorAttachments: maxColorAttachments, | |
// point and line size ranges | |
pointSizeDims: gl.getParameter(GL_ALIASED_POINT_SIZE_RANGE), | |
lineWidthDims: gl.getParameter(GL_ALIASED_LINE_WIDTH_RANGE), | |
maxViewportDims: gl.getParameter(GL_MAX_VIEWPORT_DIMS), | |
maxCombinedTextureUnits: gl.getParameter(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS), | |
maxCubeMapSize: gl.getParameter(GL_MAX_CUBE_MAP_TEXTURE_SIZE), | |
maxRenderbufferSize: gl.getParameter(GL_MAX_RENDERBUFFER_SIZE), | |
maxTextureUnits: gl.getParameter(GL_MAX_TEXTURE_IMAGE_UNITS), | |
maxTextureSize: gl.getParameter(GL_MAX_TEXTURE_SIZE), | |
maxAttributes: gl.getParameter(GL_MAX_VERTEX_ATTRIBS), | |
maxVertexUniforms: gl.getParameter(GL_MAX_VERTEX_UNIFORM_VECTORS), | |
maxVertexTextureUnits: gl.getParameter(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS), | |
maxVaryingVectors: gl.getParameter(GL_MAX_VARYING_VECTORS), | |
maxFragmentUniforms: gl.getParameter(GL_MAX_FRAGMENT_UNIFORM_VECTORS), | |
// vendor info | |
glsl: gl.getParameter(GL_SHADING_LANGUAGE_VERSION), | |
renderer: gl.getParameter(GL_RENDERER), | |
vendor: gl.getParameter(GL_VENDOR), | |
version: gl.getParameter(GL_VERSION) | |
} | |
} | |
},{}],13:[function(require,module,exports){ | |
var check = require('./util/check') | |
var isTypedArray = require('./util/is-typed-array') | |
var GL_RGBA = 6408 | |
var GL_UNSIGNED_BYTE = 5121 | |
var GL_PACK_ALIGNMENT = 0x0D05 | |
module.exports = function wrapReadPixels ( | |
gl, | |
framebufferState, | |
reglPoll, | |
context, | |
glAttributes) { | |
function readPixels (input) { | |
if (framebufferState.current === null) { | |
check( | |
glAttributes.preserveDrawingBuffer, | |
'you must create a webgl context with "preserveDrawingBuffer":true in order to read pixels from the drawing buffer') | |
} else { | |
// TODO check framebuffer supports read pixels | |
} | |
var x = 0 | |
var y = 0 | |
var width = context.framebufferWidth | |
var height = context.framebufferHeight | |
var data = null | |
if (isTypedArray(input)) { | |
data = input | |
} else if (input) { | |
check.type(input, 'object', 'invalid arguments to regl.read()') | |
x = input.x | 0 | |
y = input.y | 0 | |
check( | |
x >= 0 && x < context.framebufferWidth, | |
'invalid x offset for regl.read') | |
check( | |
y >= 0 && y < context.framebufferHeight, | |
'invalid y offset for regl.read') | |
width = (input.width || (context.framebufferWidth - x)) | 0 | |
height = (input.height || (context.framebufferHeight - y)) | 0 | |
data = input.data || null | |
} | |
check( | |
width > 0 && width + x <= context.framebufferWidth, | |
'invalid width for read pixels') | |
check( | |
height > 0 && height + y <= context.framebufferHeight, | |
'invalid height for read pixels') | |
// Update WebGL state | |
reglPoll() | |
// TODO: | |
// float color buffers | |
// implementation specific formats | |
// Compute size | |
var size = width * height * 4 | |
// Allocate data | |
data = data || new Uint8Array(size) | |
// Type check | |
check.isTypedArray(data, 'data buffer for regl.read() must be a typedarray') | |
check(data.byteLength >= size, 'data buffer for regl.read() too small') | |
// Run read pixels | |
gl.pixelStorei(GL_PACK_ALIGNMENT, 4) | |
gl.readPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data) | |
return data | |
} | |
return readPixels | |
} | |
},{"./util/check":20,"./util/is-typed-array":26}],14:[function(require,module,exports){ | |
var check = require('./util/check') | |
var values = require('./util/values') | |
var GL_RENDERBUFFER = 0x8D41 | |
var GL_RGBA4 = 0x8056 | |
var GL_RGB5_A1 = 0x8057 | |
var GL_RGB565 = 0x8D62 | |
var GL_DEPTH_COMPONENT16 = 0x81A5 | |
var GL_STENCIL_INDEX8 = 0x8D48 | |
var GL_DEPTH_STENCIL = 0x84F9 | |
var GL_SRGB8_ALPHA8_EXT = 0x8C43 | |
var GL_RGBA32F_EXT = 0x8814 | |
var GL_RGBA16F_EXT = 0x881A | |
var GL_RGB16F_EXT = 0x881B | |
module.exports = function (gl, extensions, limits, stats) { | |
var formatTypes = { | |
'rgba4': GL_RGBA4, | |
'rgb565': GL_RGB565, | |
'rgb5 a1': GL_RGB5_A1, | |
'depth': GL_DEPTH_COMPONENT16, | |
'stencil': GL_STENCIL_INDEX8, | |
'depth stencil': GL_DEPTH_STENCIL | |
} | |
if (extensions.ext_srgb) { | |
formatTypes['srgba'] = GL_SRGB8_ALPHA8_EXT | |
} | |
if (extensions.ext_color_buffer_half_float) { | |
formatTypes['rgba16f'] = GL_RGBA16F_EXT | |
formatTypes['rgb16f'] = GL_RGB16F_EXT | |
} | |
if (extensions.webgl_color_buffer_float) { | |
formatTypes['rgba32f'] = GL_RGBA32F_EXT | |
} | |
var renderbufferCount = 0 | |
var renderbufferSet = {} | |
function REGLRenderbuffer (renderbuffer) { | |
this.id = renderbufferCount++ | |
this.refCount = 1 | |
this.renderbuffer = renderbuffer | |
this.format = GL_RGBA4 | |
this.width = 0 | |
this.height = 0 | |
} | |
REGLRenderbuffer.prototype.decRef = function () { | |
if (--this.refCount <= 0) { | |
destroy(this) | |
} | |
} | |
function destroy (rb) { | |
var handle = rb.renderbuffer | |
check(handle, 'must not double destroy renderbuffer') | |
gl.bindRenderbuffer(GL_RENDERBUFFER, null) | |
gl.deleteRenderbuffer(handle) | |
rb.renderbuffer = null | |
rb.refCount = 0 | |
delete renderbufferSet[rb.id] | |
stats.renderbufferCount-- | |
} | |
function createRenderbuffer (a, b) { | |
var renderbuffer = new REGLRenderbuffer(gl.createRenderbuffer()) | |
renderbufferSet[renderbuffer.id] = renderbuffer | |
stats.renderbufferCount++ | |
function reglRenderbuffer (a, b) { | |
var w = 0 | |
var h = 0 | |
var format = GL_RGBA4 | |
if (typeof a === 'object' && a) { | |
var options = a | |
if ('shape' in options) { | |
var shape = options.shape | |
check(Array.isArray(shape) && shape.length >= 2, | |
'invalid renderbuffer shape') | |
w = shape[0] | 0 | |
h = shape[1] | 0 | |
} else { | |
if ('radius' in options) { | |
w = h = options.radius | 0 | |
} | |
if ('width' in options) { | |
w = options.width | 0 | |
} | |
if ('height' in options) { | |
h = options.height | 0 | |
} | |
} | |
if ('format' in options) { | |
check.parameter(options.format, formatTypes, | |
'invalid renderbuffer format') | |
format = formatTypes[options.format] | |
} | |
} else if (typeof a === 'number') { | |
w = a | 0 | |
if (typeof b === 'number') { | |
h = b | 0 | |
} else { | |
h = w | |
} | |
} else if (!a) { | |
w = h = 1 | |
} else { | |
check.raise('invalid arguments to renderbuffer constructor') | |
} | |
// check shape | |
check( | |
w > 0 && h > 0 && | |
w <= limits.maxRenderbufferSize && h <= limits.maxRenderbufferSize, | |
'invalid renderbuffer size') | |
if (w === renderbuffer.width && | |
h === renderbuffer.height && | |
format === renderbuffer.format) { | |
return | |
} | |
reglRenderbuffer.width = renderbuffer.width = w | |
reglRenderbuffer.height = renderbuffer.height = h | |
renderbuffer.format = format | |
gl.bindRenderbuffer(GL_RENDERBUFFER, renderbuffer.renderbuffer) | |
gl.renderbufferStorage(GL_RENDERBUFFER, format, w, h) | |
return reglRenderbuffer | |
} | |
function resize (w_, h_) { | |
var w = w_ | 0 | |
var h = (h_ | 0) || w | |
if (w === renderbuffer.width && h === renderbuffer.height) { | |
return reglRenderbuffer | |
} | |
// check shape | |
check( | |
w > 0 && h > 0 && | |
w <= limits.maxRenderbufferSize && h <= limits.maxRenderbufferSize, | |
'invalid renderbuffer size') | |
reglRenderbuffer.width = renderbuffer.width = w | |
reglRenderbuffer.height = renderbuffer.height = h | |
gl.bindRenderbuffer(GL_RENDERBUFFER, renderbuffer.renderbuffer) | |
gl.renderbufferStorage(GL_RENDERBUFFER, renderbuffer.format, w, h) | |
return reglRenderbuffer | |
} | |
reglRenderbuffer(a, b) | |
reglRenderbuffer.resize = resize | |
reglRenderbuffer._reglType = 'renderbuffer' | |
reglRenderbuffer._renderbuffer = renderbuffer | |
reglRenderbuffer.destroy = function () { | |
renderbuffer.decRef() | |
} | |
return reglRenderbuffer | |
} | |
return { | |
create: createRenderbuffer, | |
clear: function () { | |
values(renderbufferSet).forEach(destroy) | |
} | |
} | |
} | |
},{"./util/check":20,"./util/values":31}],15:[function(require,module,exports){ | |
var check = require('./util/check') | |
var values = require('./util/values') | |
var GL_FRAGMENT_SHADER = 35632 | |
var GL_VERTEX_SHADER = 35633 | |
var GL_ACTIVE_UNIFORMS = 0x8B86 | |
var GL_ACTIVE_ATTRIBUTES = 0x8B89 | |
module.exports = function wrapShaderState (gl, stringStore, stats) { | |
// =================================================== | |
// glsl compilation and linking | |
// =================================================== | |
var fragShaders = {} | |
var vertShaders = {} | |
function ActiveInfo (name, id, location, info) { | |
this.name = name | |
this.id = id | |
this.location = location | |
this.info = info | |
} | |
function insertActiveInfo (list, info) { | |
for (var i = 0; i < list.length; ++i) { | |
if (list[i].id === info.id) { | |
list[i].location = info.location | |
return | |
} | |
} | |
list.push(info) | |
} | |
function getShader (type, id, command) { | |
var cache = type === GL_FRAGMENT_SHADER ? fragShaders : vertShaders | |
var shader = cache[id] | |
if (!shader) { | |
var source = stringStore.str(id) | |
shader = gl.createShader(type) | |
gl.shaderSource(shader, source) | |
gl.compileShader(shader) | |
check.shaderError(gl, shader, source, type, command) | |
cache[id] = shader | |
} | |
return shader | |
} | |
// =================================================== | |
// program linking | |
// =================================================== | |
var programCache = {} | |
var programList = [] | |
var PROGRAM_COUNTER = 0 | |
function REGLProgram (fragId, vertId) { | |
this.id = PROGRAM_COUNTER++ | |
this.fragId = fragId | |
this.vertId = vertId | |
this.program = null | |
this.uniforms = [] | |
this.attributes = [] | |
} | |
function linkProgram (desc, command) { | |
var i, info | |
// ------------------------------- | |
// compile & link | |
// ------------------------------- | |
var fragShader = getShader(GL_FRAGMENT_SHADER, desc.fragId) | |
var vertShader = getShader(GL_VERTEX_SHADER, desc.vertId) | |
var program = desc.program = gl.createProgram() | |
gl.attachShader(program, fragShader) | |
gl.attachShader(program, vertShader) | |
gl.linkProgram(program) | |
check.linkError( | |
gl, | |
program, | |
stringStore.str(desc.fragId), | |
stringStore.str(desc.vertId), | |
command) | |
// ------------------------------- | |
// grab uniforms | |
// ------------------------------- | |
var numUniforms = gl.getProgramParameter(program, GL_ACTIVE_UNIFORMS) | |
var uniforms = desc.uniforms | |
for (i = 0; i < numUniforms; ++i) { | |
info = gl.getActiveUniform(program, i) | |
if (info) { | |
if (info.size > 1) { | |
for (var j = 0; j < info.size; ++j) { | |
var name = info.name.replace('[0]', '[' + j + ']') | |
insertActiveInfo(uniforms, new ActiveInfo( | |
name, | |
stringStore.id(name), | |
gl.getUniformLocation(program, name), | |
info)) | |
} | |
} else { | |
insertActiveInfo(uniforms, new ActiveInfo( | |
info.name, | |
stringStore.id(info.name), | |
gl.getUniformLocation(program, info.name), | |
info)) | |
} | |
} | |
} | |
// ------------------------------- | |
// grab attributes | |
// ------------------------------- | |
var numAttributes = gl.getProgramParameter(program, GL_ACTIVE_ATTRIBUTES) | |
var attributes = desc.attributes | |
for (i = 0; i < numAttributes; ++i) { | |
info = gl.getActiveAttrib(program, i) | |
if (info) { | |
insertActiveInfo(attributes, new ActiveInfo( | |
info.name, | |
stringStore.id(info.name), | |
gl.getAttribLocation(program, info.name), | |
info)) | |
} | |
} | |
} | |
return { | |
clear: function () { | |
var deleteShader = gl.deleteShader.bind(gl) | |
values(fragShaders).forEach(deleteShader) | |
fragShaders = {} | |
values(vertShaders).forEach(deleteShader) | |
vertShaders = {} | |
programList.forEach(function (desc) { | |
gl.deleteProgram(desc.program) | |
}) | |
programList.length = 0 | |
programCache = {} | |
stats.shaderCount = 0 | |
}, | |
program: function (vertId, fragId, command) { | |
check(vertId >= 0, 'missing vertex shader') | |
check(fragId >= 0, 'missing fragment shader') | |
stats.shaderCount++ | |
var cache = programCache[fragId] | |
if (!cache) { | |
cache = programCache[fragId] = {} | |
} | |
var program = cache[vertId] | |
if (!program) { | |
program = new REGLProgram(fragId, vertId) | |
linkProgram(program, command) | |
cache[vertId] = program | |
programList.push(program) | |
} | |
return program | |
}, | |
shader: getShader, | |
frag: null, | |
vert: null | |
} | |
} | |
},{"./util/check":20,"./util/values":31}],16:[function(require,module,exports){ | |
module.exports = function stats () { | |
return { | |
bufferCount: 0, | |
elementsCount: 0, | |
framebufferCount: 0, | |
shaderCount: 0, | |
textureCount: 0, | |
cubeCount: 0, | |
renderbufferCount: 0 | |
} | |
} | |
},{}],17:[function(require,module,exports){ | |
module.exports = function createStringStore () { | |
var stringIds = {'': 0} | |
var stringValues = [''] | |
return { | |
id: function (str) { | |
var result = stringIds[str] | |
if (result) { | |
return result | |
} | |
result = stringIds[str] = stringValues.length | |
stringValues.push(str) | |
return result | |
}, | |
str: function (id) { | |
return stringValues[id] | |
} | |
} | |
} | |
},{}],18:[function(require,module,exports){ | |
var check = require('./util/check') | |
var extend = require('./util/extend') | |
var values = require('./util/values') | |
var isTypedArray = require('./util/is-typed-array') | |
var isNDArrayLike = require('./util/is-ndarray') | |
var pool = require('./util/pool') | |
var convertToHalfFloat = require('./util/to-half-float') | |
var isArrayLike = require('./util/is-array-like') | |
var dtypes = require('./constants/arraytypes.json') | |
var arrayTypes = require('./constants/arraytypes.json') | |
var GL_COMPRESSED_TEXTURE_FORMATS = 0x86A3 | |
var GL_TEXTURE_2D = 0x0DE1 | |
var GL_TEXTURE_CUBE_MAP = 0x8513 | |
var GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515 | |
var GL_RGBA = 0x1908 | |
var GL_ALPHA = 0x1906 | |
var GL_RGB = 0x1907 | |
var GL_LUMINANCE = 0x1909 | |
var GL_LUMINANCE_ALPHA = 0x190A | |
var GL_RGBA4 = 0x8056 | |
var GL_RGB5_A1 = 0x8057 | |
var GL_RGB565 = 0x8D62 | |
var GL_UNSIGNED_SHORT_4_4_4_4 = 0x8033 | |
var GL_UNSIGNED_SHORT_5_5_5_1 = 0x8034 | |
var GL_UNSIGNED_SHORT_5_6_5 = 0x8363 | |
var GL_UNSIGNED_INT_24_8_WEBGL = 0x84FA | |
var GL_DEPTH_COMPONENT = 0x1902 | |
var GL_DEPTH_STENCIL = 0x84F9 | |
var GL_SRGB_EXT = 0x8C40 | |
var GL_SRGB_ALPHA_EXT = 0x8C42 | |
var GL_HALF_FLOAT_OES = 0x8D61 | |
var GL_COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0 | |
var GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1 | |
var GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2 | |
var GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3 | |
var GL_COMPRESSED_RGB_ATC_WEBGL = 0x8C92 | |
var GL_COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL = 0x8C93 | |
var GL_COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL = 0x87EE | |
var GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00 | |
var GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01 | |
var GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02 | |
var GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03 | |
var GL_COMPRESSED_RGB_ETC1_WEBGL = 0x8D64 | |
var GL_UNSIGNED_BYTE = 0x1401 | |
var GL_UNSIGNED_SHORT = 0x1403 | |
var GL_UNSIGNED_INT = 0x1405 | |
var GL_FLOAT = 0x1406 | |
var GL_TEXTURE_WRAP_S = 0x2802 | |
var GL_TEXTURE_WRAP_T = 0x2803 | |
var GL_REPEAT = 0x2901 | |
var GL_CLAMP_TO_EDGE = 0x812F | |
var GL_MIRRORED_REPEAT = 0x8370 | |
var GL_TEXTURE_MAG_FILTER = 0x2800 | |
var GL_TEXTURE_MIN_FILTER = 0x2801 | |
var GL_NEAREST = 0x2600 | |
var GL_LINEAR = 0x2601 | |
var GL_NEAREST_MIPMAP_NEAREST = 0x2700 | |
var GL_LINEAR_MIPMAP_NEAREST = 0x2701 | |
var GL_NEAREST_MIPMAP_LINEAR = 0x2702 | |
var GL_LINEAR_MIPMAP_LINEAR = 0x2703 | |
var GL_GENERATE_MIPMAP_HINT = 0x8192 | |
var GL_DONT_CARE = 0x1100 | |
var GL_FASTEST = 0x1101 | |
var GL_NICEST = 0x1102 | |
var GL_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE | |
var GL_UNPACK_ALIGNMENT = 0x0CF5 | |
var GL_UNPACK_FLIP_Y_WEBGL = 0x9240 | |
var GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241 | |
var GL_UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243 | |
var GL_BROWSER_DEFAULT_WEBGL = 0x9244 | |
var GL_TEXTURE0 = 0x84C0 | |
var MIPMAP_FILTERS = [ | |
GL_NEAREST_MIPMAP_NEAREST, | |
GL_NEAREST_MIPMAP_LINEAR, | |
GL_LINEAR_MIPMAP_NEAREST, | |
GL_LINEAR_MIPMAP_LINEAR | |
] | |
var CHANNELS_FORMAT = [ | |
0, | |
GL_LUMINANCE, | |
GL_LUMINANCE_ALPHA, | |
GL_RGB, | |
GL_RGBA | |
] | |
var FORMAT_CHANNELS = {} | |
FORMAT_CHANNELS[GL_LUMINANCE] = | |
FORMAT_CHANNELS[GL_ALPHA] = | |
FORMAT_CHANNELS[GL_DEPTH_COMPONENT] = 1 | |
FORMAT_CHANNELS[GL_DEPTH_STENCIL] = | |
FORMAT_CHANNELS[GL_LUMINANCE_ALPHA] = 2 | |
FORMAT_CHANNELS[GL_RGB] = 3 | |
FORMAT_CHANNELS[GL_RGBA] = 4 | |
var formatTypes = {} | |
formatTypes[GL_RGBA4] = GL_UNSIGNED_SHORT_4_4_4_4 | |
formatTypes[GL_RGB565] = GL_UNSIGNED_SHORT_5_6_5 | |
formatTypes[GL_RGB5_A1] = GL_UNSIGNED_SHORT_5_5_5_1 | |
formatTypes[GL_DEPTH_COMPONENT] = GL_UNSIGNED_INT | |
formatTypes[GL_DEPTH_STENCIL] = GL_UNSIGNED_INT_24_8_WEBGL | |
function objectName (str) { | |
return '[object ' + str + ']' | |
} | |
var CANVAS_CLASS = objectName('HTMLCanvasElement') | |
var CONTEXT2D_CLASS = objectName('CanvasRenderingContext2D') | |
var IMAGE_CLASS = objectName('HTMLImageElement') | |
var VIDEO_CLASS = objectName('HTMLVideoElement') | |
var PIXEL_CLASSES = Object.keys(dtypes).concat([ | |
CANVAS_CLASS, | |
CONTEXT2D_CLASS, | |
IMAGE_CLASS, | |
VIDEO_CLASS | |
]) | |
function isNumericArray (arr) { | |
return ( | |
Array.isArray(arr) && | |
(arr.length === 0 || | |
typeof arr[0] === 'number')) | |
} | |
function isRectArray (arr) { | |
if (!Array.isArray(arr)) { | |
return false | |
} | |
var width = arr.length | |
if (width === 0 || !isArrayLike(arr[0])) { | |
return false | |
} | |
return true | |
} | |
function classString (x) { | |
return Object.prototype.toString.call(x) | |
} | |
function isCanvasElement (object) { | |
return classString(object) === CANVAS_CLASS | |
} | |
function isContext2D (object) { | |
return classString(object) === CONTEXT2D_CLASS | |
} | |
function isImageElement (object) { | |
return classString(object) === IMAGE_CLASS | |
} | |
function isVideoElement (object) { | |
return classString(object) === VIDEO_CLASS | |
} | |
function isPixelData (object) { | |
if (!object) { | |
return false | |
} | |
var className = classString(object) | |
if (PIXEL_CLASSES.indexOf(className) >= 0) { | |
return true | |
} | |
return ( | |
isNumericArray(object) || | |
isRectArray(object) || | |
isNDArrayLike(object)) | |
} | |
function typedArrayCode (data) { | |
return arrayTypes[Object.prototype.toString.call(data)] | 0 | |
} | |
function convertData (result, data) { | |
var n = result.width * result.height * result.channels | |
check(data.length === n, 'inconsistent array length for texture') | |
switch (result.type) { | |
case GL_UNSIGNED_BYTE: | |
case GL_UNSIGNED_SHORT: | |
case GL_UNSIGNED_INT: | |
case GL_FLOAT: | |
var converted = pool.allocType(result.type, n) | |
converted.set(data) | |
result.data = converted | |
break | |
case GL_HALF_FLOAT_OES: | |
result.data = convertToHalfFloat(data) | |
break | |
default: | |
check.raise('unsupported texture type, must specify a typed array') | |
} | |
} | |
function preConvert (image, n) { | |
return pool.allocType( | |
image.type === GL_HALF_FLOAT_OES | |
? GL_FLOAT | |
: image.type, n) | |
} | |
function postConvert (image, data) { | |
if (image.type === GL_HALF_FLOAT_OES) { | |
image.data = convertToHalfFloat(data) | |
pool.freeType(data) | |
} else { | |
image.data = data | |
} | |
} | |
function transposeData (image, array, strideX, strideY, strideC, offset) { | |
var w = image.width | |
var h = image.height | |
var c = image.channels | |
var n = w * h * c | |
var data = preConvert(image, n) | |
var p = 0 | |
for (var i = 0; i < h; ++i) { | |
for (var j = 0; j < w; ++j) { | |
for (var k = 0; k < c; ++k) { | |
data[p++] = array[strideX * j + strideY * i + strideC * k + offset] | |
} | |
} | |
} | |
postConvert(image, data) | |
} | |
function flatten2DData (image, array, w, h) { | |
var n = w * h | |
var data = preConvert(image, n) | |
var p = 0 | |
for (var i = 0; i < h; ++i) { | |
var row = array[i] | |
for (var j = 0; j < w; ++j) { | |
data[p++] = row[j] | |
} | |
} | |
postConvert(image, data) | |
} | |
function flatten3DData (image, array, w, h, c) { | |
var n = w * h * c | |
var data = preConvert(image, n) | |
var p = 0 | |
for (var i = 0; i < h; ++i) { | |
var row = array[i] | |
for (var j = 0; j < w; ++j) { | |
var pixel = row[j] | |
for (var k = 0; k < c; ++k) { | |
data[p++] = pixel[k] | |
} | |
} | |
} | |
postConvert(image, data) | |
} | |
module.exports = function createTextureSet ( | |
gl, extensions, limits, reglPoll, contextState, stats) { | |
// ------------------------------------------------------- | |
// Initialize constants and parameter tables here | |
// ------------------------------------------------------- | |
var mipmapHint = { | |
"don't care": GL_DONT_CARE, | |
'dont care': GL_DONT_CARE, | |
'nice': GL_NICEST, | |
'fast': GL_FASTEST | |
} | |
var wrapModes = { | |
'repeat': GL_REPEAT, | |
'clamp': GL_CLAMP_TO_EDGE, | |
'mirror': GL_MIRRORED_REPEAT | |
} | |
var magFilters = { | |
'nearest': GL_NEAREST, | |
'linear': GL_LINEAR | |
} | |
var minFilters = extend({ | |
'nearest mipmap nearest': GL_NEAREST_MIPMAP_NEAREST, | |
'linear mipmap nearest': GL_LINEAR_MIPMAP_NEAREST, | |
'nearest mipmap linear': GL_NEAREST_MIPMAP_LINEAR, | |
'linear mipmap linear': GL_LINEAR_MIPMAP_LINEAR, | |
'mipmap': GL_LINEAR_MIPMAP_LINEAR | |
}, magFilters) | |
var colorSpace = { | |
'none': 0, | |
'browser': GL_BROWSER_DEFAULT_WEBGL | |
} | |
var textureTypes = { | |
'uint8': GL_UNSIGNED_BYTE, | |
'rgba4': GL_UNSIGNED_SHORT_4_4_4_4, | |
'rgb565': GL_UNSIGNED_SHORT_5_6_5, | |
'rgb5 a1': GL_UNSIGNED_SHORT_5_5_5_1 | |
} | |
var textureFormats = { | |
'alpha': GL_ALPHA, | |
'luminance': GL_LUMINANCE, | |
'luminance alpha': GL_LUMINANCE_ALPHA, | |
'rgb': GL_RGB, | |
'rgba': GL_RGBA, | |
'rgba4': GL_RGBA4, | |
'rgb5 a1': GL_RGB5_A1, | |
'rgb565': GL_RGB565 | |
} | |
var compressedTextureFormats = {} | |
if (extensions.ext_srgb) { | |
textureFormats.srgb = GL_SRGB_EXT | |
textureFormats.srgba = GL_SRGB_ALPHA_EXT | |
} | |
if (extensions.oes_texture_float) { | |
textureTypes.float = GL_FLOAT | |
} | |
if (extensions.oes_texture_half_float) { | |
textureTypes['float16'] = textureTypes['half float'] = GL_HALF_FLOAT_OES | |
} | |
if (extensions.webgl_depth_texture) { | |
extend(textureFormats, { | |
'depth': GL_DEPTH_COMPONENT, | |
'depth stencil': GL_DEPTH_STENCIL | |
}) | |
extend(textureTypes, { | |
'uint16': GL_UNSIGNED_SHORT, | |
'uint32': GL_UNSIGNED_INT, | |
'depth stencil': GL_UNSIGNED_INT_24_8_WEBGL | |
}) | |
} | |
if (extensions.webgl_compressed_texture_s3tc) { | |
extend(compressedTextureFormats, { | |
'rgb s3tc dxt1': GL_COMPRESSED_RGB_S3TC_DXT1_EXT, | |
'rgba s3tc dxt1': GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, | |
'rgba s3tc dxt3': GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, | |
'rgba s3tc dxt5': GL_COMPRESSED_RGBA_S3TC_DXT5_EXT | |
}) | |
} | |
if (extensions.webgl_compressed_texture_atc) { | |
extend(compressedTextureFormats, { | |
'rgb atc': GL_COMPRESSED_RGB_ATC_WEBGL, | |
'rgba atc explicit alpha': GL_COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL, | |
'rgba atc interpolated alpha': GL_COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL | |
}) | |
} | |
if (extensions.webgl_compressed_texture_pvrtc) { | |
extend(compressedTextureFormats, { | |
'rgb pvrtc 4bppv1': GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, | |
'rgb pvrtc 2bppv1': GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG, | |
'rgba pvrtc 4bppv1': GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, | |
'rgba pvrtc 2bppv1': GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG | |
}) | |
} | |
if (extensions.webgl_compressed_texture_etc1) { | |
compressedTextureFormats['rgb etc1'] = GL_COMPRESSED_RGB_ETC1_WEBGL | |
} | |
// Copy over all texture formats | |
var supportedCompressedFormats = Array.prototype.slice.call( | |
gl.getParameter(GL_COMPRESSED_TEXTURE_FORMATS)) | |
Object.keys(compressedTextureFormats).forEach(function (name) { | |
var format = compressedTextureFormats[name] | |
if (supportedCompressedFormats.indexOf(format) >= 0) { | |
textureFormats[name] = format | |
} | |
}) | |
var supportedFormats = Object.keys(textureFormats) | |
limits.textureFormats = supportedFormats | |
// colorFormats[] gives the format (channels) associated to an | |
// internalformat | |
var colorFormats = supportedFormats.reduce(function (color, key) { | |
var glenum = textureFormats[key] | |
if (glenum === GL_LUMINANCE || | |
glenum === GL_ALPHA || | |
glenum === GL_LUMINANCE || | |
glenum === GL_LUMINANCE_ALPHA || | |
glenum === GL_DEPTH_COMPONENT || | |
glenum === GL_DEPTH_STENCIL) { | |
color[glenum] = glenum | |
} else if (glenum === GL_RGB5_A1 || key.indexOf('rgba') >= 0) { | |
color[glenum] = GL_RGBA | |
} else { | |
color[glenum] = GL_RGB | |
} | |
return color | |
}, {}) | |
function TexFlags () { | |
// format info | |
this.internalformat = GL_RGBA | |
this.format = GL_RGBA | |
this.type = GL_UNSIGNED_BYTE | |
this.compressed = false | |
// pixel storage | |
this.premultiplyAlpha = false | |
this.flipY = false | |
this.unpackAlignment = 1 | |
this.colorSpace = 0 | |
// shape info | |
this.width = 0 | |
this.height = 0 | |
this.channels = 4 | |
} | |
function copyFlags (result, other) { | |
result.internalformat = other.internalformat | |
result.format = other.format | |
result.type = other.type | |
result.compressed = other.compressed | |
result.premultiplyAlpha = other.premultiplyAlpha | |
result.flipY = other.flipY | |
result.unpackAlignment = other.unpackAlignment | |
result.colorSpace = other.colorSpace | |
result.width = other.width | |
result.height = other.height | |
result.channels = other.channels | |
} | |
function parseFlags (flags, options) { | |
if (typeof options !== 'object' || !options) { | |
return | |
} | |
if ('premultiplyAlpha' in options) { | |
check.type(options.premultiplyAlpha, 'boolean', | |
'invalid premultiplyAlpha') | |
flags.premultiplyAlpha = options.premultiplyAlpha | |
} | |
if ('flipY' in options) { | |
check.type(options.flipY, 'boolean', | |
'invalid texture flip') | |
flags.flipY = options.flipY | |
} | |
if ('alignment' in options) { | |
check.oneOf(options.alignment, [1, 2, 4, 8], | |
'invalid texture unpack alignment') | |
flags.unpackAlignment = options.alignment | |
} | |
if ('colorSpace' in options) { | |
check.parameter(options.colorSpace, colorSpace, | |
'invalid colorSpace') | |
flags.colorSpace = colorSpace[options.colorSpace] | |
} | |
if ('type' in options) { | |
var type = options.type | |
check.parameter(type, textureTypes, | |
'invalid texture type') | |
flags.type = textureTypes[type] | |
} | |
var w = flags.width | |
var h = flags.height | |
var c = flags.channels | |
var hasChannels = false | |
if ('shape' in options) { | |
check(Array.isArray(options.shape) && options.shape.length >= 2, | |
'shape must be an array') | |
w = options.shape[0] | |
h = options.shape[1] | |
if (options.shape.length === 3) { | |
c = options.shape[2] | |
check(c > 0 && c <= 4, 'invalid number of channels') | |
hasChannels = true | |
} | |
check(w > 0 && w <= limits.maxTextureSize, 'invalid width') | |
check(h > 0 && h <= limits.maxTextureSize, 'invalid height') | |
} else { | |
if ('radius' in options) { | |
w = h = options.radius | |
check(w > 0 && w <= limits.maxTextureSize, 'invalid radius') | |
} | |
if ('width' in options) { | |
w = options.width | |
check(w > 0 && w <= limits.maxTextureSize, 'invalid width') | |
} | |
if ('height' in options) { | |
h = options.height | |
check(h > 0 && h <= limits.maxTextureSize, 'invalid height') | |
} | |
if ('channels' in options) { | |
c = options.channels | |
check(c > 0 && c <= 4, 'invalid number of channels') | |
hasChannels = true | |
} | |
} | |
flags.width = w | 0 | |
flags.height = h | 0 | |
flags.channels = c | 0 | |
var hasFormat = false | |
if ('format' in options) { | |
var formatStr = options.format | |
check.parameter(formatStr, textureFormats, | |
'invalid texture format') | |
var internalformat = flags.internalformat = textureFormats[formatStr] | |
flags.format = colorFormats[internalformat] | |
if (formatStr in textureTypes) { | |
if (!('type' in options)) { | |
flags.type = textureTypes[formatStr] | |
} | |
} | |
if (formatStr in compressedTextureFormats) { | |
flags.compressed = true | |
} | |
hasFormat = true | |
} | |
// Reconcile channels and format | |
if (!hasChannels && hasFormat) { | |
flags.channels = FORMAT_CHANNELS[flags.format] | |
} else if (hasChannels && !hasFormat) { | |
if (flags.channels !== CHANNELS_FORMAT[flags.format]) { | |
flags.format = flags.internalformat = CHANNELS_FORMAT[flags.channels] | |
} | |
} else if (hasFormat && hasChannels) { | |
check( | |
flags.channels === FORMAT_CHANNELS[flags.format], | |
'number of channels inconsistent with specified format') | |
} | |
} | |
function setFlags (flags) { | |
gl.pixelStorei(GL_UNPACK_FLIP_Y_WEBGL, flags.flipY) | |
gl.pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL, flags.premultiplyAlpha) | |
gl.pixelStorei(GL_UNPACK_COLORSPACE_CONVERSION_WEBGL, flags.colorSpace) | |
gl.pixelStorei(GL_UNPACK_ALIGNMENT, flags.unpackAlignment) | |
} | |
// ------------------------------------------------------- | |
// Tex image data | |
// ------------------------------------------------------- | |
function TexImage () { | |
TexFlags.call(this) | |
this.xOffset = 0 | |
this.yOffset = 0 | |
// data | |
this.data = null | |
this.needsFree = false | |
// html element | |
this.element = null | |
// copyTexImage info | |
this.needsCopy = false | |
} | |
function parseImage (image, options) { | |
var data = null | |
if (isPixelData(options)) { | |
data = options | |
} else if (options) { | |
check.type(options, 'object', 'invalid pixel data type') | |
parseFlags(image, options) | |
if ('x' in options) { | |
image.xOffset = options.x | 0 | |
} | |
if ('y' in options) { | |
image.yOffset = options.y | 0 | |
} | |
if (isPixelData(options.data)) { | |
data = options.data | |
} | |
} | |
check( | |
!image.compressed || | |
data instanceof Uint8Array, | |
'compressed texture data must be stored in a uint8array') | |
if (options.copy) { | |
check(!data, 'can not specify copy and data field for the same texture') | |
var viewW = contextState.viewportWidth | |
var viewH = contextState.viewportHeight | |
image.width = image.width || (viewW - image.xOffset) | |
image.height = image.height || (viewH - image.yOffset) | |
image.needsCopy = true | |
check(image.xOffset >= 0 && image.xOffset < viewW && | |
image.yOffset >= 0 && image.yOffset < viewH && | |
image.width > 0 && image.width <= viewW && | |
image.height > 0 && image.height <= viewH, | |
'copy texture read out of bounds') | |
} else if (!data) { | |
image.width = image.width || 1 | |
image.height = image.height || 1 | |
} else if (isTypedArray(data)) { | |
image.data = data | |
if (!('type' in options) && image.type === GL_UNSIGNED_BYTE) { | |
image.type = typedArrayCode(data) | |
} | |
} else if (isNumericArray(data)) { | |
convertData(image, data) | |
image.alignment = 1 | |
image.needsFree = true | |
} else if (isNDArrayLike(data)) { | |
var array = data.data | |
if (!Array.isArray(array) && image.type === GL_UNSIGNED_BYTE) { | |
image.type = typedArrayCode(array) | |
} | |
var shape = data.shape | |
var stride = data.stride | |
var shapeX, shapeY, shapeC, strideX, strideY, strideC | |
if (shape.length === 3) { | |
shapeC = shape[2] | |
strideC = stride[2] | |
} else { | |
check(shape.length === 2, 'invalid ndarray pixel data, must be 2 or 3D') | |
shapeC = 1 | |
strideC = 1 | |
} | |
shapeX = shape[0] | |
shapeY = shape[1] | |
strideX = stride[0] | |
strideY = stride[1] | |
image.alignment = 1 | |
image.width = shapeX | |
image.height = shapeY | |
image.channels = shapeC | |
image.format = image.internalformat = CHANNELS_FORMAT[shapeC] | |
image.needsFree = true | |
transposeData(image, array, strideX, strideY, strideC, data.offset) | |
} else if (isCanvasElement(data) || isContext2D(data)) { | |
if (isCanvasElement(data)) { | |
image.element = data | |
} else { | |
image.element = data.canvas | |
} | |
image.width = image.element.width | |
image.height = image.element.height | |
} else if (isImageElement(data)) { | |
image.element = data | |
image.width = data.naturalWidth | |
image.height = data.naturalHeight | |
} else if (isVideoElement(data)) { | |
image.element = data | |
image.width = data.videoWidth | |
image.height = data.videoHeight | |
image.needsPoll = true | |
} else if (isRectArray(data)) { | |
var w = data[0].length | |
var h = data.length | |
var c = 1 | |
if (isArrayLike(data[0][0])) { | |
c = data[0][0].length | |
flatten3DData(image, data, w, h, c) | |
} else { | |
flatten2DData(image, data, w, h) | |
} | |
image.alignment = 1 | |
image.width = w | |
image.height = h | |
image.channels = c | |
image.format = image.internalformat = CHANNELS_FORMAT[c] | |
image.needsFree = true | |
} | |
if (image.type === GL_FLOAT) { | |
check(limits.extensions.indexOf('oes_texture_float') >= 0, | |
'oes_texture_float extension not enabled') | |
} else if (image.type === GL_HALF_FLOAT_OES) { | |
check(limits.extensions.indexOf('oes_texture_half_float') >= 0, | |
'oes_texture_half_float extension not enabled') | |
} | |
} | |
function setImage (info, target, miplevel) { | |
var element = info.element | |
var data = info.data | |
var internalformat = info.internalformat | |
var format = info.format | |
var type = info.type | |
var width = info.width | |
var height = info.height | |
setFlags(info) | |
if (element) { | |
gl.texImage2D(target, miplevel, format, format, type, element) | |
} else if (info.compressed) { | |
gl.compressedTexImage2D(target, miplevel, internalformat, width, height, 0, data) | |
} else if (info.needsCopy) { | |
reglPoll() | |
gl.copyTexImage2D( | |
target, miplevel, format, info.xOffset, info.yOffset, width, height, 0) | |
} else { | |
gl.texImage2D( | |
target, miplevel, format, width, height, 0, format, type, data) | |
} | |
} | |
function setSubImage (info, target, x, y, miplevel) { | |
var element = info.element | |
var data = info.data | |
var internalformat = info.internalformat | |
var format = info.format | |
var type = info.type | |
var width = info.width | |
var height = info.height | |
setFlags(info) | |
if (element) { | |
gl.texSubImage2D( | |
target, miplevel, x, y, format, type, element) | |
} else if (info.compressed) { | |
gl.compressedTexSubImage2D( | |
target, miplevel, x, y, internalformat, width, height, data) | |
} else if (info.needsCopy) { | |
reglPoll() | |
gl.copyTexSubImage2D( | |
target, miplevel, x, y, info.xOffset, info.yOffset, width, height) | |
} else { | |
gl.texSubImage2D( | |
target, miplevel, x, y, width, height, format, type, data) | |
} | |
} | |
// texImage pool | |
var imagePool = [] | |
function allocImage () { | |
return imagePool.pop() || new TexImage() | |
} | |
function freeImage (image) { | |
if (image.needsFree) { | |
pool.freeType(image.data) | |
} | |
TexImage.call(image) | |
imagePool.push(image) | |
} | |
// ------------------------------------------------------- | |
// Mip map | |
// ------------------------------------------------------- | |
function MipMap () { | |
TexFlags.call(this) | |
this.genMipmaps = false | |
this.mipmapHint = GL_DONT_CARE | |
this.mipmask = 0 | |
this.images = Array(16) | |
} | |
function parseMipMapFromShape (mipmap, width, height) { | |
var img = mipmap.images[0] = allocImage() | |
mipmap.mipmask = 1 | |
img.width = mipmap.width = width | |
img.height = mipmap.height = height | |
} | |
function parseMipMapFromObject (mipmap, options) { | |
var imgData = null | |
if (isPixelData(options)) { | |
imgData = mipmap.images[0] = allocImage() | |
copyFlags(imgData, mipmap) | |
parseImage(imgData, options) | |
mipmap.mipmask = 1 | |
} else { | |
parseFlags(mipmap, options) | |
if (Array.isArray(options.mipmap)) { | |
var mipData = options.mipmap | |
for (var i = 0; i < mipData.length; ++i) { | |
imgData = mipmap.images[i] = allocImage() | |
copyFlags(imgData, mipmap) | |
imgData.width >>= i | |
imgData.height >>= i | |
parseImage(imgData, mipData[i]) | |
mipmap.mipmask |= (1 << i) | |
} | |
} else { | |
imgData = mipmap.images[0] = allocImage() | |
copyFlags(imgData, mipmap) | |
parseImage(imgData, options) | |
mipmap.mipmask = 1 | |
} | |
} | |
copyFlags(mipmap, mipmap.images[0]) | |
} | |
function setMipMap (mipmap, target) { | |
var images = mipmap.images | |
for (var i = 0; i < images.length; ++i) { | |
if (!images[i]) { | |
return | |
} | |
setImage(images[i], target, i) | |
} | |
} | |
var mipPool = [] | |
function allocMipMap () { | |
var result = mipPool.pop() || new MipMap() | |
TexFlags.call(result) | |
result.mipmask = 0 | |
for (var i = 0; i < 16; ++i) { | |
result.images[i] = null | |
} | |
return result | |
} | |
function freeMipMap (mipmap) { | |
var images = mipmap.images | |
for (var i = 0; i < images.length; ++i) { | |
if (images[i]) { | |
freeImage(images[i]) | |
} | |
images[i] = null | |
} | |
mipPool.push(mipmap) | |
} | |
// ------------------------------------------------------- | |
// Tex info | |
// ------------------------------------------------------- | |
function TexInfo () { | |
this.minFilter = GL_NEAREST | |
this.magFilter = GL_NEAREST | |
this.wrapS = GL_CLAMP_TO_EDGE | |
this.wrapT = GL_CLAMP_TO_EDGE | |
this.anisotropic = 1 | |
this.genMipmaps = false | |
this.mipmapHint = GL_DONT_CARE | |
} | |
function parseTexInfo (info, options) { | |
if ('min' in options) { | |
var minFilter = options.min | |
check.parameter(minFilter, minFilters) | |
info.minFilter = minFilters[minFilter] | |
if (MIPMAP_FILTERS.indexOf(info.minFilter) >= 0) { | |
info.genMipmaps = true | |
} | |
} | |
if ('mag' in options) { | |
var magFilter = options.mag | |
check.parameter(magFilter, magFilters) | |
info.magFilter = magFilters[magFilter] | |
} | |
var wrapS = info.wrapS | |
var wrapT = info.wrapT | |
if ('wrap' in options) { | |
var wrap = options.wrap | |
if (typeof wrap === 'string') { | |
check.parameter(wrap, wrapModes) | |
wrapS = wrapT = wrapModes[wrap] | |
} else if (Array.isArray(wrap)) { | |
check.parameter(wrap[0], wrapModes) | |
check.parameter(wrap[1], wrapModes) | |
wrapS = wrapModes[wrap[0]] | |
wrapT = wrapModes[wrap[1]] | |
} | |
} else { | |
if ('wrapS' in options) { | |
var optWrapS = options.wrapS | |
check.parameter(optWrapS, wrapModes) | |
wrapS = wrapModes[optWrapS] | |
} | |
if ('wrapT' in options) { | |
var optWrapT = options.wrapT | |
check.parameter(optWrapT, wrapModes) | |
wrapT = wrapModes[optWrapT] | |
} | |
} | |
info.wrapS = wrapS | |
info.wrapT = wrapT | |
if ('anisotropic' in options) { | |
var anisotropic = options.anisotropic | |
check(typeof anisotropic === 'number' && | |
anisotropic >= 1 && anisotropic <= limits.maxAnisotropic, | |
'aniso samples must be between 1 and ') | |
info.anisotropic = options.anisotropic | |
} | |
if ('mipmap' in options) { | |
var hasMipMap = false | |
switch (typeof options.mipmap) { | |
case 'string': | |
check.parameter(options.mipmap, mipmapHint, | |
'invalid mipmap hint') | |
info.mipmapHint = mipmapHint[options.mipmap] | |
info.genMipmaps = true | |
hasMipMap = true | |
break | |
case 'boolean': | |
hasMipMap = info.genMipmaps = options.mipmap | |
break | |
case 'object': | |
check(Array.isArray(options.mipmap), 'invalid mipmap type') | |
info.genMipmaps = false | |
hasMipMap = true | |
break | |
default: | |
check.raise('invalid mipmap type') | |
} | |
if (hasMipMap && !('min' in options)) { | |
info.minFilter = GL_NEAREST_MIPMAP_NEAREST | |
} | |
} | |
} | |
function setTexInfo (info, target) { | |
gl.texParameteri(target, GL_TEXTURE_MIN_FILTER, info.minFilter) | |
gl.texParameteri(target, GL_TEXTURE_MAG_FILTER, info.magFilter) | |
gl.texParameteri(target, GL_TEXTURE_WRAP_S, info.wrapS) | |
gl.texParameteri(target, GL_TEXTURE_WRAP_T, info.wrapT) | |
if (extensions.ext_texture_filter_anisotropic) { | |
gl.texParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, info.anisotropic) | |
} | |
if (info.genMipmaps) { | |
gl.hint(GL_GENERATE_MIPMAP_HINT, info.mipmapHint) | |
gl.generateMipmap(target) | |
} | |
} | |
var infoPool = [] | |
function allocInfo () { | |
var result = infoPool.pop() || new TexInfo() | |
TexInfo.call(result) | |
return result | |
} | |
function freeInfo (info) { | |
infoPool.push(info) | |
} | |
// ------------------------------------------------------- | |
// Full texture object | |
// ------------------------------------------------------- | |
var textureCount = 0 | |
var textureSet = {} | |
var numTexUnits = limits.maxTextureUnits | |
var textureUnits = Array(numTexUnits).map(function () { | |
return null | |
}) | |
function REGLTexture (target) { | |
TexFlags.call(this) | |
this.mipmask = 0 | |
this.internalformat = GL_RGBA | |
this.id = textureCount++ | |
this.refCount = 1 | |
this.target = target | |
this.texture = gl.createTexture() | |
this.unit = -1 | |
this.bindCount = 0 | |
} | |
function tempBind (texture) { | |
gl.activeTexture(GL_TEXTURE0) | |
gl.bindTexture(texture.target, texture.texture) | |
} | |
function tempRestore () { | |
var prev = textureUnits[0] | |
if (prev) { | |
gl.bindTexture(prev.target, prev.texture) | |
} else { | |
gl.bindTexture(GL_TEXTURE_2D, null) | |
} | |
} | |
function destroy (texture) { | |
var handle = texture.texture | |
check(handle, 'must not double destroy texture') | |
var unit = texture.unit | |
var target = texture.target | |
if (unit >= 0) { | |
gl.activeTexture(GL_TEXTURE0 + unit) | |
gl.bindTexture(target, null) | |
textureUnits[unit] = null | |
} | |
gl.deleteTexture(handle) | |
texture.texture = null | |
texture.params = null | |
texture.pixels = null | |
texture.refCount = 0 | |
delete textureSet[texture.id] | |
stats.textureCount-- | |
} | |
extend(REGLTexture.prototype, { | |
bind: function () { | |
var texture = this | |
texture.bindCount += 1 | |
var unit = texture.unit | |
if (unit < 0) { | |
for (var i = 0; i < numTexUnits; ++i) { | |
var other = textureUnits[i] | |
if (other) { | |
if (other.bindCount > 0) { | |
continue | |
} | |
other.unit = -1 | |
} | |
textureUnits[i] = texture | |
unit = i | |
break | |
} | |
if (unit >= numTexUnits) { | |
check.raise('insufficient number of texture units') | |
} | |
texture.unit = unit | |
gl.activeTexture(GL_TEXTURE0 + unit) | |
gl.bindTexture(texture.target, texture.texture) | |
} | |
return unit | |
}, | |
unbind: function () { | |
this.bindCount -= 1 | |
}, | |
decRef: function () { | |
if (--this.refCount <= 0) { | |
destroy(this) | |
} | |
} | |
}) | |
function createTexture2D (a, b) { | |
var texture = new REGLTexture(GL_TEXTURE_2D) | |
textureSet[texture.id] = texture | |
stats.textureCount++ | |
function reglTexture2D (a, b) { | |
var texInfo = allocInfo() | |
var mipData = allocMipMap() | |
if (typeof a === 'number') { | |
if (typeof b === 'number') { | |
parseMipMapFromShape(mipData, a | 0, b | 0) | |
} else { | |
parseMipMapFromShape(mipData, a | 0, a | 0) | |
} | |
} else if (a) { | |
check.type(a, 'object', 'invalid arguments to regl.texture') | |
parseTexInfo(texInfo, a) | |
parseMipMapFromObject(mipData, a) | |
} else { | |
// empty textures get assigned a default shape of 1x1 | |
parseMipMapFromShape(mipData, 1, 1) | |
} | |
if (texInfo.genMipmaps) { | |
mipData.mipmask = (mipData.width << 1) - 1 | |
} | |
texture.mipmask = mipData.mipmask | |
copyFlags(texture, mipData) | |
check.texture2D(texInfo, mipData, limits) | |
texture.internalformat = mipData.internalformat | |
reglTexture2D.width = mipData.width | |
reglTexture2D.height = mipData.height | |
tempBind(texture) | |
setMipMap(mipData, GL_TEXTURE_2D) | |
setTexInfo(texInfo, GL_TEXTURE_2D) | |
tempRestore() | |
freeInfo(texInfo) | |
freeMipMap(mipData) | |
return reglTexture2D | |
} | |
function subimage (image, x_, y_, level_) { | |
check(!!image, 'must specify image data') | |
var x = x_ | 0 | |
var y = y_ | 0 | |
var level = level_ | 0 | |
var imageData = allocImage() | |
copyFlags(imageData, texture) | |
imageData.width >>= level | |
imageData.height >>= level | |
imageData.width -= x | |
imageData.height -= y | |
parseImage(imageData, image) | |
check( | |
texture.type === imageData.type && | |
texture.format === imageData.format && | |
texture.internalformat === imageData.internalformat, | |
'incompatible format for texture.subimage') | |
check( | |
x >= 0 && y >= 0 && | |
x + imageData.width <= texture.width && | |
y + imageData.height <= texture.height, | |
'texture.subimage write out of bounds') | |
check( | |
texture.mipmask & (1 << level), | |
'missing mipmap data') | |
check( | |
imageData.data || imageData.element || imageData.needsCopy, | |
'missing image data') | |
tempBind(texture) | |
setSubImage(imageData, GL_TEXTURE_2D, x, y, level) | |
tempRestore() | |
freeImage(imageData) | |
return reglTexture2D | |
} | |
function resize (w_, h_) { | |
var w = w_ | 0 | |
var h = (h_ | 0) || w | |
if (w === texture.width && h === texture.height) { | |
return reglTexture2D | |
} | |
reglTexture2D.width = texture.width = w | |
reglTexture2D.height = texture.height = h | |
tempBind(texture) | |
gl.texImage2D( | |
GL_TEXTURE_2D, | |
0, | |
texture.format, | |
w, | |
h, | |
0, | |
texture.format, | |
texture.type, | |
null) | |
tempRestore() | |
return reglTexture2D | |
} | |
reglTexture2D(a, b) | |
reglTexture2D.subimage = subimage | |
reglTexture2D.resize = resize | |
reglTexture2D._reglType = 'texture2d' | |
reglTexture2D._texture = texture | |
reglTexture2D.destroy = function () { | |
texture.decRef() | |
} | |
return reglTexture2D | |
} | |
function createTextureCube (a0, a1, a2, a3, a4, a5) { | |
var texture = new REGLTexture(GL_TEXTURE_CUBE_MAP) | |
textureSet[texture.id] = texture | |
stats.cubeCount++ | |
var faces = new Array(6) | |
function reglTextureCube (a0, a1, a2, a3, a4, a5) { | |
var i | |
var texInfo = allocInfo() | |
for (i = 0; i < 6; ++i) { | |
faces[i] = allocMipMap() | |
} | |
if (typeof a0 === 'number' || !a0) { | |
var s = (a0 | 0) || 1 | |
for (i = 0; i < 6; ++i) { | |
parseMipMapFromShape(faces[i], s, s) | |
} | |
} else if (typeof a0 === 'object') { | |
if (a1) { | |
parseMipMapFromObject(faces[0], a0) | |
parseMipMapFromObject(faces[1], a1) | |
parseMipMapFromObject(faces[2], a2) | |
parseMipMapFromObject(faces[3], a3) | |
parseMipMapFromObject(faces[4], a4) | |
parseMipMapFromObject(faces[5], a5) | |
} else { | |
parseTexInfo(texInfo, a0) | |
parseFlags(texture, a0) | |
if ('faces' in a0) { | |
var face_input = a0.faces | |
check(Array.isArray(face_input) && face_input.length === 6, | |
'cube faces must be a length 6 array') | |
for (i = 0; i < 6; ++i) { | |
check(typeof face_input[i] === 'object' && !!face_input[i], | |
'invalid input for cube map face') | |
copyFlags(faces[i], texture) | |
parseMipMapFromObject(faces[i], face_input[i]) | |
} | |
} else { | |
for (i = 0; i < 6; ++i) { | |
parseMipMapFromObject(faces[i], a0) | |
} | |
} | |
} | |
} else { | |
check.raise('invalid arguments to cube map') | |
} | |
copyFlags(texture, faces[0]) | |
if (texInfo.genMipmaps) { | |
texture.mipmask = (faces[0].width << 1) - 1 | |
} else { | |
texture.mipmask = faces[0].mipmask | |
} | |
check.textureCube(texture, texInfo, faces, limits) | |
texture.internalformat = faces[0].internalformat | |
reglTextureCube.width = faces[0].width | |
reglTextureCube.height = faces[0].height | |
tempBind(texture) | |
for (i = 0; i < 6; ++i) { | |
setMipMap(faces[i], GL_TEXTURE_CUBE_MAP_POSITIVE_X + i) | |
} | |
setTexInfo(texInfo, GL_TEXTURE_CUBE_MAP) | |
tempRestore() | |
freeInfo(texInfo) | |
for (i = 0; i < 6; ++i) { | |
freeMipMap(faces[i]) | |
} | |
return reglTextureCube | |
} | |
function subimage (face, image, x_, y_, level_) { | |
check(!!image, 'must specify image data') | |
check(typeof face === 'number' && face === (face | 0) && | |
face >= 0 && face < 6, 'invalid face') | |
var x = x_ | 0 | |
var y = y_ | 0 | |
var level = level_ | 0 | |
var imageData = allocImage() | |
copyFlags(imageData, texture) | |
imageData.width >>= level | |
imageData.height >>= level | |
imageData.width -= x | |
imageData.height -= y | |
parseImage(imageData, image) | |
check( | |
texture.type === imageData.type && | |
texture.format === imageData.format && | |
texture.internalformat === imageData.internalformat, | |
'incompatible format for texture.subimage') | |
check( | |
x >= 0 && y >= 0 && | |
x + imageData.width <= texture.width && | |
y + imageData.height <= texture.height, | |
'texture.subimage write out of bounds') | |
check( | |
texture.mipmask & (1 << level), | |
'missing mipmap data') | |
check( | |
imageData.data || imageData.element || imageData.needsCopy, | |
'missing image data') | |
tempBind(texture) | |
setSubImage(imageData, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, x, y, level) | |
tempRestore() | |
freeImage(imageData) | |
return reglTextureCube | |
} | |
function resize (w_, h_) { | |
var w = w_ | 0 | |
var h = (h_ | 0) || w | |
if (w === texture.width && h === texture.height) { | |
return reglTextureCube | |
} | |
reglTextureCube.width = texture.width = w | |
reglTextureCube.height = texture.height = h | |
tempBind(texture) | |
for (var i = 0; i < 6; ++i) { | |
gl.texImage2D( | |
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, | |
0, | |
texture.format, | |
w, | |
h, | |
0, | |
texture.format, | |
texture.type, | |
null) | |
} | |
tempRestore() | |
return reglTextureCube | |
} | |
reglTextureCube(a0, a1, a2, a3, a4, a5) | |
reglTextureCube.subimage = subimage | |
reglTextureCube.resize = resize | |
reglTextureCube._reglType = 'textureCube' | |
reglTextureCube._texture = texture | |
reglTextureCube.destroy = function () { | |
texture.decRef() | |
} | |
return reglTextureCube | |
} | |
// Called when regl is destroyed | |
function destroyTextures () { | |
for (var i = 0; i < numTexUnits; ++i) { | |
gl.activeTexture(GL_TEXTURE0 + i) | |
gl.bindTexture(GL_TEXTURE_2D, null) | |
textureUnits[i] = null | |
} | |
values(textureSet).forEach(destroy) | |
stats.cubeCount = 0 | |
stats.textureCount = 0 | |
} | |
return { | |
create2D: createTexture2D, | |
createCube: createTextureCube, | |
clear: destroyTextures, | |
getTexture: function (wrapper) { | |
return null | |
} | |
} | |
} | |
},{"./constants/arraytypes.json":3,"./util/check":20,"./util/extend":23,"./util/is-array-like":24,"./util/is-ndarray":25,"./util/is-typed-array":26,"./util/pool":28,"./util/to-half-float":30,"./util/values":31}],19:[function(require,module,exports){ | |
var GL_QUERY_RESULT_EXT = 0x8866 | |
var GL_QUERY_RESULT_AVAILABLE_EXT = 0x8867 | |
var GL_TIME_ELAPSED_EXT = 0x88BF | |
module.exports = function (gl, extensions) { | |
var extTimer = extensions.ext_disjoint_timer_query | |
if (!extTimer) { | |
return null | |
} | |
// QUERY POOL BEGIN | |
var queryPool = [] | |
function allocQuery () { | |
return queryPool.pop() || extTimer.createQueryEXT() | |
} | |
function freeQuery (query) { | |
queryPool.push(query) | |
} | |
// QUERY POOL END | |
var pendingQueries = [] | |
function beginQuery (stats) { | |
var query = allocQuery() | |
extTimer.beginQueryEXT(GL_TIME_ELAPSED_EXT, query) | |
pendingQueries.push(query) | |
pushScopeStats(pendingQueries.length - 1, pendingQueries.length, stats) | |
} | |
function endQuery () { | |
extTimer.endQueryEXT(GL_TIME_ELAPSED_EXT) | |
} | |
// | |
// Pending stats pool. | |
// | |
function PendingStats () { | |
this.startQueryIndex = -1 | |
this.endQueryIndex = -1 | |
this.sum = 0 | |
this.stats = null | |
} | |
var pendingStatsPool = [] | |
function allocPendingStats () { | |
return pendingStatsPool.pop() || new PendingStats() | |
} | |
function freePendingStats (pendingStats) { | |
pendingStatsPool.push(pendingStats) | |
} | |
// Pending stats pool end | |
var pendingStats = [] | |
function pushScopeStats (start, end, stats) { | |
var ps = allocPendingStats() | |
ps.startQueryIndex = start | |
ps.endQueryIndex = end | |
ps.sum = 0 | |
ps.stats = stats | |
pendingStats.push(ps) | |
} | |
// we should call this at the beginning of the frame, | |
// in order to update gpuTime | |
var timeSum = [] | |
var queryPtr = [] | |
function update () { | |
var ptr, i | |
var n = pendingQueries.length | |
if (n === 0) { | |
return | |
} | |
// Reserve space | |
queryPtr.length = Math.max(queryPtr.length, n + 1) | |
timeSum.length = Math.max(timeSum.length, n + 1) | |
timeSum[0] = 0 | |
queryPtr[0] = 0 | |
// Update all pending timer queries | |
var queryTime = 0 | |
ptr = 0 | |
for (i = 0; i < pendingQueries.length; ++i) { | |
var query = pendingQueries[i] | |
if (extTimer.getQueryObjectEXT(query, GL_QUERY_RESULT_AVAILABLE_EXT)) { | |
queryTime += extTimer.getQueryObjectEXT(query, GL_QUERY_RESULT_EXT) | |
freeQuery(query) | |
} else { | |
pendingQueries[ptr++] = query | |
} | |
timeSum[i + 1] = queryTime | |
queryPtr[i + 1] = ptr | |
} | |
pendingQueries.length = ptr | |
// Update all pending stat queries | |
ptr = 0 | |
for (i = 0; i < pendingStats.length; ++i) { | |
var stats = pendingStats[i] | |
var start = stats.startQueryIndex | |
var end = stats.endQueryIndex | |
stats.sum += timeSum[end] - timeSum[start] | |
var startPtr = queryPtr[start] | |
var endPtr = queryPtr[end] | |
if (endPtr === startPtr) { | |
stats.stats.gpuTime += stats.sum / 1e6 | |
freePendingStats(stats) | |
} else { | |
stats.startQueryIndex = startPtr | |
stats.endQueryIndex = endPtr | |
pendingStats[ptr++] = stats | |
} | |
} | |
pendingStats.length = ptr | |
} | |
return { | |
beginQuery: beginQuery, | |
endQuery: endQuery, | |
pushScopeStats: pushScopeStats, | |
update: update, | |
getNumPendingQueries: function () { | |
return pendingQueries.length | |
}, | |
clear: function () { | |
queryPool.push.apply(queryPool, pendingQueries) | |
for (var i = 0; i < queryPool.length; i++) { | |
extTimer.deleteQueryEXT(queryPool[i]) | |
} | |
pendingQueries.length = 0 | |
queryPool.length = 0 | |
} | |
} | |
} | |
},{}],20:[function(require,module,exports){ | |
// Error checking and parameter validation. | |
// | |
// Statements for the form `check.someProcedure(...)` get removed by | |
// a browserify transform for optimized/minified bundles. | |
// | |
/* globals btoa */ | |
var isTypedArray = require('./is-typed-array') | |
var extend = require('./extend') | |
// only used for extracting shader names. if btoa not present, then errors | |
// will be slightly crappier | |
function decodeB64 (str) { | |
if (typeof btoa !== 'undefined') { | |
return btoa(str) | |
} | |
return 'base64:' + str | |
} | |
function raise (message) { | |
var error = new Error('(regl) ' + message) | |
console.error(error) | |
throw error | |
} | |
function check (pred, message) { | |
if (!pred) { | |
raise(message) | |
} | |
} | |
function encolon (message) { | |
if (message) { | |
return ': ' + message | |
} | |
return '' | |
} | |
function checkParameter (param, possibilities, message) { | |
if (!(param in possibilities)) { | |
raise('unknown parameter (' + param + ')' + encolon(message) + | |
'. possible values: ' + Object.keys(possibilities).join()) | |
} | |
} | |
function checkIsTypedArray (data, message) { | |
if (!isTypedArray(data)) { | |
raise( | |
'invalid parameter type' + encolon(message) + | |
'. must be a typed array') | |
} | |
} | |
function checkTypeOf (value, type, message) { | |
if (typeof value !== type) { | |
raise( | |
'invalid parameter type' + encolon(message) + | |
'. expected ' + type + ', got ' + (typeof value)) | |
} | |
} | |
function checkNonNegativeInt (value, message) { | |
if (!((value >= 0) && | |
((value | 0) === value))) { | |
raise('invalid parameter type, (' + value + ')' + encolon(message) + | |
'. must be a nonnegative integer') | |
} | |
} | |
function checkOneOf (value, list, message) { | |
if (list.indexOf(value) < 0) { | |
raise('invalid value' + encolon(message) + '. must be one of: ' + list) | |
} | |
} | |
var constructorKeys = [ | |
'gl', | |
'canvas', | |
'container', | |
'attributes', | |
'pixelRatio', | |
'extensions', | |
'optionalExtensions', | |
'profile', | |
'onDone' | |
] | |
function checkConstructor (obj) { | |
Object.keys(obj).forEach(function (key) { | |
if (constructorKeys.indexOf(key) < 0) { | |
raise('invalid regl constructor argument "' + key + '". must be one of ' + constructorKeys) | |
} | |
}) | |
} | |
function leftPad (str, n) { | |
str = str + '' | |
while (str.length < n) { | |
str = ' ' + str | |
} | |
return str | |
} | |
function ShaderFile () { | |
this.name = 'unknown' | |
this.lines = [] | |
this.index = {} | |
this.hasErrors = false | |
} | |
function ShaderLine (number, line) { | |
this.number = number | |
this.line = line | |
this.errors = [] | |
} | |
function ShaderError (fileNumber, lineNumber, message) { | |
this.file = fileNumber | |
this.line = lineNumber | |
this.message = message | |
} | |
function guessCommand () { | |
var error = new Error() | |
var stack = (error.stack || error).toString() | |
var pat = /compileProcedure.*\n\s*at.*\((.*)\)/.exec(stack) | |
if (pat) { | |
return pat[1] | |
} | |
var pat2 = /compileProcedure.*\n\s*at\s+(.*)(\n|$)/.exec(stack) | |
if (pat2) { | |
return pat2[1] | |
} | |
return 'unknown' | |
} | |
function guessCallSite () { | |
var error = new Error() | |
var stack = (error.stack || error).toString() | |
var pat = /at REGLCommand.*\n\s+at.*\((.*)\)/.exec(stack) | |
if (pat) { | |
return pat[1] | |
} | |
var pat2 = /at REGLCommand.*\n\s+at\s+(.*)\n/.exec(stack) | |
if (pat2) { | |
return pat2[1] | |
} | |
return 'unknown' | |
} | |
function parseSource (source, command) { | |
var lines = source.split('\n') | |
var lineNumber = 1 | |
var fileNumber = 0 | |
var files = { | |
unknown: new ShaderFile(), | |
0: new ShaderFile() | |
} | |
files.unknown.name = files[0].name = command || guessCommand() | |
files.unknown.lines.push(new ShaderLine(0, '')) | |
for (var i = 0; i < lines.length; ++i) { | |
var line = lines[i] | |
var parts = /^\s*\#\s*(\w+)\s+(.+)\s*$/.exec(line) | |
if (parts) { | |
switch (parts[1]) { | |
case 'line': | |
var lineNumberInfo = /(\d+)(\s+\d+)?/.exec(parts[2]) | |
if (lineNumberInfo) { | |
lineNumber = lineNumberInfo[1] | 0 | |
if (lineNumberInfo[2]) { | |
fileNumber = lineNumberInfo[2] | 0 | |
if (!(fileNumber in files)) { | |
files[fileNumber] = new ShaderFile() | |
} | |
} | |
} | |
break | |
case 'define': | |
var nameInfo = /SHADER_NAME(_B64)?\s+(.*)$/.exec(parts[2]) | |
if (nameInfo) { | |
files[fileNumber].name = (nameInfo[1] | |
? decodeB64(nameInfo[2]) | |
: nameInfo[2]) | |
} | |
break | |
} | |
} | |
files[fileNumber].lines.push(new ShaderLine(lineNumber++, line)) | |
} | |
Object.keys(files).forEach(function (fileNumber) { | |
var file = files[fileNumber] | |
file.lines.forEach(function (line) { | |
file.index[line.number] = line | |
}) | |
}) | |
return files | |
} | |
function parseErrorLog (errLog) { | |
var result = [] | |
errLog.split('\n').forEach(function (errMsg) { | |
var parts = /^ERROR\:\s+(\d+)\:(\d+)\:\s*(.*)$/.exec(errMsg) | |
if (parts) { | |
result.push(new ShaderError( | |
parts[1] | 0, | |
parts[2] | 0, | |
parts[3].trim())) | |
} else if (errMsg.length > 0) { | |
result.push(new ShaderError('unknown', 0, errMsg)) | |
} | |
}) | |
return result | |
} | |
function annotateFiles (files, errors) { | |
errors.forEach(function (error) { | |
var file = files[error.file] | |
if (file) { | |
var line = file.index[error.line] | |
if (line) { | |
line.errors.push(error) | |
file.hasErrors = true | |
return | |
} | |
} | |
files.unknown.hasErrors = true | |
files.unknown.lines[0].errors.push(error) | |
}) | |
} | |
function checkShaderError (gl, shader, source, type, command) { | |
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { | |
var errLog = gl.getShaderInfoLog(shader) | |
var typeName = type === gl.FRAGMENT_SHADER ? 'fragment' : 'vertex' | |
var files = parseSource(source, command) | |
var errors = parseErrorLog(errLog) | |
annotateFiles(files, errors) | |
Object.keys(files).forEach(function (fileNumber) { | |
var file = files[fileNumber] | |
if (!file.hasErrors) { | |
return | |
} | |
var strings = [''] | |
var styles = [''] | |
function push (str, style) { | |
strings.push(str) | |
styles.push(style || '') | |
} | |
push('file number ' + fileNumber + ': ' + file.name + '\n', 'color:red;text-decoration:underline;font-weight:bold') | |
file.lines.forEach(function (line) { | |
if (line.errors.length > 0) { | |
push(leftPad(line.number, 4) + '| ', 'background-color:yellow; font-weight:bold') | |
push(line.line + '\n', 'color:red; background-color:yellow; font-weight:bold') | |
// try to guess token | |
var offset = 0 | |
line.errors.forEach(function (error) { | |
var message = error.message | |
var token = /^\s*\'(.*)\'\s*\:\s*(.*)$/.exec(message) | |
if (token) { | |
var tokenPat = token[1] | |
message = token[2] | |
switch (tokenPat) { | |
case 'assign': | |
tokenPat = '=' | |
break | |
} | |
offset = Math.max(line.line.indexOf(tokenPat, offset), 0) | |
} else { | |
offset = 0 | |
} | |
push(leftPad('| ', 6)) | |
push(leftPad('^^^', offset + 3) + '\n', 'font-weight:bold') | |
push(leftPad('| ', 6)) | |
push(message + '\n', 'font-weight:bold') | |
}) | |
push(leftPad('| ', 6) + '\n') | |
} else { | |
push(leftPad(line.number, 4) + '| ') | |
push(line.line + '\n', 'color:red') | |
} | |
}) | |
if (typeof document !== 'undefined') { | |
styles[0] = strings.join('%c') | |
console.log.apply(console, styles) | |
} else { | |
console.log(strings.join('')) | |
} | |
}) | |
check.raise('Error compiling ' + typeName + ' shader, ' + files[0].name) | |
} | |
} | |
function checkLinkError (gl, program, fragShader, vertShader, command) { | |
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { | |
var errLog = gl.getProgramInfoLog(program) | |
var fragParse = parseSource(fragShader, command) | |
var vertParse = parseSource(vertShader, command) | |
var header = 'Error linking program with vertex shader, "' + | |
vertParse[0].name + '", and fragment shader "' + fragParse[0].name + '"' | |
if (typeof document !== 'undefined') { | |
console.log('%c' + header + '\n%c' + errLog, | |
'color:red;text-decoration:underline;font-weight:bold', | |
'color:red') | |
} else { | |
console.log(header + '\n' + errLog) | |
} | |
check.raise(header) | |
} | |
} | |
function saveCommandRef (object) { | |
object._commandRef = guessCommand() | |
} | |
function saveDrawCommandInfo (opts, uniforms, attributes, stringStore) { | |
saveCommandRef(opts) | |
function id (str) { | |
if (str) { | |
return stringStore.id(str) | |
} | |
return 0 | |
} | |
opts._fragId = id(opts.static.frag) | |
opts._vertId = id(opts.static.vert) | |
function addProps (dict, set) { | |
Object.keys(set).forEach(function (u) { | |
dict[stringStore.id(u)] = true | |
}) | |
} | |
var uniformSet = opts._uniformSet = {} | |
addProps(uniformSet, uniforms.static) | |
addProps(uniformSet, uniforms.dynamic) | |
var attributeSet = opts._attributeSet = {} | |
addProps(attributeSet, attributes.static) | |
addProps(attributeSet, attributes.dynamic) | |
opts._hasCount = ( | |
'count' in opts.static || | |
'count' in opts.dynamic || | |
'elements' in opts.static || | |
'elements' in opts.dynamic) | |
} | |
function commandRaise (message, command) { | |
var callSite = guessCallSite() | |
raise(message + | |
' in command ' + (command || guessCommand()) + | |
(callSite === 'unknown' ? '' : ' called from ' + callSite)) | |
} | |
function checkCommand (pred, message, command) { | |
if (!pred) { | |
commandRaise(message, command || guessCommand()) | |
} | |
} | |
function checkParameterCommand (param, possibilities, message, command) { | |
if (!(param in possibilities)) { | |
commandRaise( | |
'unknown parameter (' + param + ')' + encolon(message) + | |
'. possible values: ' + Object.keys(possibilities).join(), | |
command || guessCommand()) | |
} | |
} | |
function checkCommandType (value, type, message, command) { | |
if (typeof value !== type) { | |
commandRaise( | |
'invalid parameter type' + encolon(message) + | |
'. expected ' + type + ', got ' + (typeof value), | |
command || guessCommand()) | |
} | |
} | |
function checkOptional (block) { | |
block() | |
} | |
function checkFramebufferFormat (attachment, texFormats, rbFormats) { | |
if (attachment.texture) { | |
checkOneOf( | |
attachment.texture._texture.internalformat, | |
texFormats, | |
'unsupported texture format for attachment') | |
} else { | |
checkOneOf( | |
attachment.renderbuffer._renderbuffer.format, | |
rbFormats, | |
'unsupported renderbuffer format for attachment') | |
} | |
} | |
var GL_CLAMP_TO_EDGE = 0x812F | |
var GL_NEAREST = 0x2600 | |
var GL_NEAREST_MIPMAP_NEAREST = 0x2700 | |
var GL_LINEAR_MIPMAP_NEAREST = 0x2701 | |
var GL_NEAREST_MIPMAP_LINEAR = 0x2702 | |
var GL_LINEAR_MIPMAP_LINEAR = 0x2703 | |
var GL_BYTE = 5120 | |
var GL_UNSIGNED_BYTE = 5121 | |
var GL_SHORT = 5122 | |
var GL_UNSIGNED_SHORT = 5123 | |
var GL_INT = 5124 | |
var GL_UNSIGNED_INT = 5125 | |
var GL_FLOAT = 5126 | |
var GL_UNSIGNED_SHORT_4_4_4_4 = 0x8033 | |
var GL_UNSIGNED_SHORT_5_5_5_1 = 0x8034 | |
var GL_UNSIGNED_SHORT_5_6_5 = 0x8363 | |
var GL_UNSIGNED_INT_24_8_WEBGL = 0x84FA | |
var GL_HALF_FLOAT_OES = 0x8D61 | |
var TYPE_SIZE = {} | |
TYPE_SIZE[GL_BYTE] = | |
TYPE_SIZE[GL_UNSIGNED_BYTE] = 1 | |
TYPE_SIZE[GL_SHORT] = | |
TYPE_SIZE[GL_UNSIGNED_SHORT] = | |
TYPE_SIZE[GL_HALF_FLOAT_OES] = | |
TYPE_SIZE[GL_UNSIGNED_SHORT_5_6_5] = | |
TYPE_SIZE[GL_UNSIGNED_SHORT_4_4_4_4] = | |
TYPE_SIZE[GL_UNSIGNED_SHORT_5_5_5_1] = 2 | |
TYPE_SIZE[GL_INT] = | |
TYPE_SIZE[GL_UNSIGNED_INT] = | |
TYPE_SIZE[GL_FLOAT] = | |
TYPE_SIZE[GL_UNSIGNED_INT_24_8_WEBGL] = 4 | |
function pixelSize (type, channels) { | |
if (type === GL_UNSIGNED_SHORT_5_5_5_1 || | |
type === GL_UNSIGNED_SHORT_4_4_4_4 || | |
type === GL_UNSIGNED_SHORT_5_6_5) { | |
return 2 | |
} else if (type === GL_UNSIGNED_INT_24_8_WEBGL) { | |
return 4 | |
} else { | |
return TYPE_SIZE[type] * channels | |
} | |
} | |
function isPow2 (v) { | |
return !(v & (v - 1)) && (!!v) | |
} | |
function checkTexture2D (info, mipData, limits) { | |
var i | |
var w = mipData.width | |
var h = mipData.height | |
var c = mipData.channels | |
// Check texture shape | |
check(w > 0 && w <= limits.maxTextureSize && | |
h > 0 && h <= limits.maxTextureSize, | |
'invalid texture shape') | |
// check wrap mode | |
if (info.wrapS !== GL_CLAMP_TO_EDGE || info.wrapT !== GL_CLAMP_TO_EDGE) { | |
check(isPow2(w) && isPow2(h), | |
'incompatible wrap mode for texture, both width and height must be power of 2') | |
} | |
if (mipData.mipmask === 1) { | |
if (w !== 1 && h !== 1) { | |
check( | |
info.minFilter !== GL_NEAREST_MIPMAP_NEAREST && | |
info.minFilter !== GL_NEAREST_MIPMAP_LINEAR && | |
info.minFilter !== GL_LINEAR_MIPMAP_NEAREST && | |
info.minFilter !== GL_LINEAR_MIPMAP_LINEAR, | |
'min filter requires mipmap') | |
} | |
} else { | |
// texture must be power of 2 | |
check(isPow2(w) && isPow2(h), | |
'texture must be a square power of 2 to support mipmapping') | |
check(mipData.mipmask === (w << 1) - 1, | |
'missing or incomplete mipmap data') | |
} | |
if (mipData.type === GL_FLOAT) { | |
if (limits.extensions.indexOf('oes_texture_float_linear') < 0) { | |
check(info.minFilter === GL_NEAREST && info.magFilter === GL_NEAREST, | |
'filter not supported, must enable oes_texture_float_linear') | |
} | |
check(!info.genMipmaps, | |
'mipmap generation not supported with float textures') | |
} | |
// check image complete | |
var mipimages = mipData.images | |
for (i = 0; i < 16; ++i) { | |
if (mipimages[i]) { | |
var mw = w >> i | |
var mh = h >> i | |
check(mipData.mipmask & (1 << i), 'missing mipmap data') | |
var img = mipimages[i] | |
check( | |
img.width === mw && | |
img.height === mh, | |
'invalid shape for mip images') | |
check( | |
img.format === mipData.format && | |
img.internalformat === mipData.internalformat && | |
img.type === mipData.type, | |
'incompatible type for mip image') | |
if (img.compressed) { | |
// TODO: check size for compressed images | |
} else if (img.data) { | |
check(img.data.byteLength === mw * mh * | |
Math.max(pixelSize(img.type, c), img.unpackAlignment), | |
'invalid data for image, buffer size is inconsistent with image format') | |
} else if (img.element) { | |
// TODO: check element can be loaded | |
} else if (img.copy) { | |
// TODO: check compatible format and type | |
} | |
} else if (!info.genMipmaps) { | |
check((mipData.mipmask & (1 << i)) === 0, 'extra mipmap data') | |
} | |
} | |
if (mipData.compressed) { | |
check(!info.genMipmaps, | |
'mipmap generation for compressed images not supported') | |
} | |
} | |
function checkTextureCube (texture, info, faces, limits) { | |
var w = texture.width | |
var h = texture.height | |
var c = texture.channels | |
// Check texture shape | |
check( | |
w > 0 && w <= limits.maxTextureSize && h > 0 && h <= limits.maxTextureSize, | |
'invalid texture shape') | |
check( | |
w === h, | |
'cube map must be square') | |
check( | |
info.wrapS === GL_CLAMP_TO_EDGE && info.wrapT === GL_CLAMP_TO_EDGE, | |
'wrap mode not supported by cube map') | |
for (var i = 0; i < faces.length; ++i) { | |
var face = faces[i] | |
check( | |
face.width === w && face.height === h, | |
'inconsistent cube map face shape') | |
if (info.genMipmaps) { | |
check(!face.compressed, | |
'can not generate mipmap for compressed textures') | |
check(face.mipmask === 1, | |
'can not specify mipmaps and generate mipmaps') | |
} else { | |
// TODO: check mip and filter mode | |
} | |
var mipmaps = face.images | |
for (var j = 0; j < 16; ++j) { | |
var img = mipmaps[j] | |
if (img) { | |
var mw = w >> j | |
var mh = h >> j | |
check(face.mipmask & (1 << j), 'missing mipmap data') | |
check( | |
img.width === mw && | |
img.height === mh, | |
'invalid shape for mip images') | |
check( | |
img.format === texture.format && | |
img.internalformat === texture.internalformat && | |
img.type === texture.type, | |
'incompatible type for mip image') | |
if (img.compressed) { | |
// TODO: check size for compressed images | |
} else if (img.data) { | |
check(img.data.byteLength === mw * mh * | |
Math.max(pixelSize(img.type, c), img.unpackAlignment), | |
'invalid data for image, buffer size is inconsistent with image format') | |
} else if (img.element) { | |
// TODO: check element can be loaded | |
} else if (img.copy) { | |
// TODO: check compatible format and type | |
} | |
} | |
} | |
} | |
} | |
module.exports = extend(check, { | |
optional: checkOptional, | |
raise: raise, | |
commandRaise: commandRaise, | |
command: checkCommand, | |
parameter: checkParameter, | |
commandParameter: checkParameterCommand, | |
constructor: checkConstructor, | |
type: checkTypeOf, | |
commandType: checkCommandType, | |
isTypedArray: checkIsTypedArray, | |
nni: checkNonNegativeInt, | |
oneOf: checkOneOf, | |
shaderError: checkShaderError, | |
linkError: checkLinkError, | |
callSite: guessCallSite, | |
saveCommandRef: saveCommandRef, | |
saveDrawInfo: saveDrawCommandInfo, | |
framebufferFormat: checkFramebufferFormat, | |
guessCommand: guessCommand, | |
texture2D: checkTexture2D, | |
textureCube: checkTextureCube | |
}) | |
},{"./extend":23,"./is-typed-array":26}],21:[function(require,module,exports){ | |
/* globals performance */ | |
module.exports = | |
(typeof performance !== 'undefined' && performance.now) | |
? function () { return performance.now() } | |
: function () { return +(new Date()) } | |
},{}],22:[function(require,module,exports){ | |
var extend = require('./extend') | |
function slice (x) { | |
return Array.prototype.slice.call(x) | |
} | |
function join (x) { | |
return slice(x).join('') | |
} | |
module.exports = function createEnvironment () { | |
// Unique variable id counter | |
var varCounter = 0 | |
// Linked values are passed from this scope into the generated code block | |
// Calling link() passes a value into the generated scope and returns | |
// the variable name which it is bound to | |
var linkedNames = [] | |
var linkedValues = [] | |
function link (value) { | |
for (var i = 0; i < linkedValues.length; ++i) { | |
if (linkedValues[i] === value) { | |
return linkedNames[i] | |
} | |
} | |
var name = 'g' + (varCounter++) | |
linkedNames.push(name) | |
linkedValues.push(value) | |
return name | |
} | |
// create a code block | |
function block () { | |
var code = [] | |
function push () { | |
code.push.apply(code, slice(arguments)) | |
} | |
var vars = [] | |
function def () { | |
var name = 'v' + (varCounter++) | |
vars.push(name) | |
if (arguments.length > 0) { | |
code.push(name, '=') | |
code.push.apply(code, slice(arguments)) | |
code.push(';') | |
} | |
return name | |
} | |
return extend(push, { | |
def: def, | |
toString: function () { | |
return join([ | |
(vars.length > 0 ? 'var ' + vars + ';' : ''), | |
join(code) | |
]) | |
} | |
}) | |
} | |
function scope () { | |
var entry = block() | |
var exit = block() | |
var entryToString = entry.toString | |
var exitToString = exit.toString | |
function save (object, prop) { | |
exit(object, prop, '=', entry.def(object, prop), ';') | |
} | |
return extend(entry, { | |
entry: entry, | |
exit: exit, | |
save: save, | |
set: function (object, prop, value) { | |
save(object, prop) | |
entry(object, prop, '=', value, ';') | |
}, | |
toString: function () { | |
return entryToString() + exitToString() | |
} | |
}) | |
} | |
function conditional () { | |
var pred = join(arguments) | |
var thenBlock = scope() | |
var elseBlock = scope() | |
var thenToString = thenBlock.toString | |
var elseToString = elseBlock.toString | |
return extend(thenBlock, { | |
then: function () { | |
thenBlock.apply(thenBlock, slice(arguments)) | |
return this | |
}, | |
else: function () { | |
elseBlock.apply(elseBlock, slice(arguments)) | |
return this | |
}, | |
toString: function () { | |
var elseClause = elseToString() | |
if (elseClause) { | |
elseClause = 'else{' + elseClause + '}' | |
} | |
return join([ | |
'if(', pred, '){', | |
thenToString(), | |
'}', elseClause | |
]) | |
} | |
}) | |
} | |
// procedure list | |
var globalBlock = block() | |
var procedures = {} | |
function proc (name, count) { | |
var args = [] | |
function arg () { | |
var name = 'a' + args.length | |
args.push(name) | |
return name | |
} | |
count = count || 0 | |
for (var i = 0; i < count; ++i) { | |
arg() | |
} | |
var body = scope() | |
var bodyToString = body.toString | |
var result = procedures[name] = extend(body, { | |
arg: arg, | |
toString: function () { | |
return join([ | |
'function(', args.join(), '){', | |
bodyToString(), | |
'}' | |
]) | |
} | |
}) | |
return result | |
} | |
function compile () { | |
var code = ['"use strict";', | |
globalBlock, | |
'return {'] | |
Object.keys(procedures).forEach(function (name) { | |
code.push('"', name, '":', procedures[name].toString(), ',') | |
}) | |
code.push('}') | |
var src = join(code) | |
.replace(/;/g, ';\n') | |
.replace(/}/g, '}\n') | |
.replace(/{/g, '{\n') | |
var proc = Function.apply(null, linkedNames.concat(src)) | |
// console.log('src: ', src) | |
return proc.apply(null, linkedValues) | |
} | |
return { | |
global: globalBlock, | |
link: link, | |
block: block, | |
proc: proc, | |
scope: scope, | |
cond: conditional, | |
compile: compile | |
} | |
} | |
},{"./extend":23}],23:[function(require,module,exports){ | |
module.exports = function (base, opts) { | |
var keys = Object.keys(opts) | |
for (var i = 0; i < keys.length; ++i) { | |
base[keys[i]] = opts[keys[i]] | |
} | |
return base | |
} | |
},{}],24:[function(require,module,exports){ | |
var isTypedArray = require('./is-typed-array') | |
module.exports = function isArrayLike (s) { | |
return Array.isArray(s) || isTypedArray(s) | |
} | |
},{"./is-typed-array":26}],25:[function(require,module,exports){ | |
var isTypedArray = require('./is-typed-array') | |
module.exports = function isNDArrayLike (obj) { | |
return ( | |
!!obj && | |
typeof obj === 'object' && | |
Array.isArray(obj.shape) && | |
Array.isArray(obj.stride) && | |
typeof obj.offset === 'number' && | |
obj.shape.length === obj.stride.length && | |
(Array.isArray(obj.data) || | |
isTypedArray(obj.data))) | |
} | |
},{"./is-typed-array":26}],26:[function(require,module,exports){ | |
var dtypes = require('../constants/arraytypes.json') | |
module.exports = function (x) { | |
return Object.prototype.toString.call(x) in dtypes | |
} | |
},{"../constants/arraytypes.json":3}],27:[function(require,module,exports){ | |
module.exports = function loop (n, f) { | |
var result = Array(n) | |
for (var i = 0; i < n; ++i) { | |
result[i] = f(i) | |
} | |
return result | |
} | |
},{}],28:[function(require,module,exports){ | |
var loop = require('./loop') | |
var GL_BYTE = 5120 | |
var GL_UNSIGNED_BYTE = 5121 | |
var GL_SHORT = 5122 | |
var GL_UNSIGNED_SHORT = 5123 | |
var GL_INT = 5124 | |
var GL_UNSIGNED_INT = 5125 | |
var GL_FLOAT = 5126 | |
var bufferPool = loop(8, function () { | |
return [] | |
}) | |
function nextPow16 (v) { | |
for (var i = 16; i <= (1 << 28); i *= 16) { | |
if (v <= i) { | |
return i | |
} | |
} | |
return 0 | |
} | |
function log2 (v) { | |
var r, shift | |
r = (v > 0xFFFF) << 4 | |
v >>>= r | |
shift = (v > 0xFF) << 3 | |
v >>>= shift; r |= shift | |
shift = (v > 0xF) << 2 | |
v >>>= shift; r |= shift | |
shift = (v > 0x3) << 1 | |
v >>>= shift; r |= shift | |
return r | (v >> 1) | |
} | |
function alloc (n) { | |
var sz = nextPow16(n) | |
var bin = bufferPool[log2(sz) >> 2] | |
if (bin.length > 0) { | |
return bin.pop() | |
} | |
return new ArrayBuffer(sz) | |
} | |
function free (buf) { | |
bufferPool[log2(buf.byteLength) >> 2].push(buf) | |
} | |
function allocType (type, n) { | |
var result = null | |
switch (type) { | |
case GL_BYTE: | |
result = new Int8Array(alloc(n), 0, n) | |
break | |
case GL_UNSIGNED_BYTE: | |
result = new Uint8Array(alloc(n), 0, n) | |
break | |
case GL_SHORT: | |
result = new Int16Array(alloc(2 * n), 0, n) | |
break | |
case GL_UNSIGNED_SHORT: | |
result = new Uint16Array(alloc(2 * n), 0, n) | |
break | |
case GL_INT: | |
result = new Int32Array(alloc(4 * n), 0, n) | |
break | |
case GL_UNSIGNED_INT: | |
result = new Uint32Array(alloc(4 * n), 0, n) | |
break | |
case GL_FLOAT: | |
result = new Float32Array(alloc(4 * n), 0, n) | |
break | |
default: | |
return null | |
} | |
if (result.length !== n) { | |
return result.subarray(0, n) | |
} | |
return result | |
} | |
function freeType (array) { | |
free(array.buffer) | |
} | |
module.exports = { | |
alloc: alloc, | |
free: free, | |
allocType: allocType, | |
freeType: freeType | |
} | |
},{"./loop":27}],29:[function(require,module,exports){ | |
/* globals requestAnimationFrame, cancelAnimationFrame */ | |
if (typeof requestAnimationFrame === 'function' && | |
typeof cancelAnimationFrame === 'function') { | |
module.exports = { | |
next: function (x) { return requestAnimationFrame(x) }, | |
cancel: function (x) { return cancelAnimationFrame(x) } | |
} | |
} else { | |
module.exports = { | |
next: function (cb) { | |
return setTimeout(cb, 16) | |
}, | |
cancel: clearTimeout | |
} | |
} | |
},{}],30:[function(require,module,exports){ | |
var pool = require('./pool') | |
var FLOAT = new Float32Array(1) | |
var INT = new Uint32Array(FLOAT.buffer) | |
var GL_UNSIGNED_SHORT = 5123 | |
module.exports = function convertToHalfFloat (array) { | |
var ushorts = pool.allocType(GL_UNSIGNED_SHORT, array.length) | |
for (var i = 0; i < array.length; ++i) { | |
if (isNaN(array[i])) { | |
ushorts[i] = 0xffff | |
} else if (array[i] === Infinity) { | |
ushorts[i] = 0x7c00 | |
} else if (array[i] === -Infinity) { | |
ushorts[i] = 0xfc00 | |
} else { | |
FLOAT[0] = array[i] | |
var x = INT[0] | |
var sgn = (x >>> 31) << 15 | |
var exp = ((x << 1) >>> 24) - 127 | |
var frac = (x >> 13) & ((1 << 10) - 1) | |
if (exp < -24) { | |
// round non-representable denormals to 0 | |
ushorts[i] = sgn | |
} else if (exp < -14) { | |
// handle denormals | |
var s = -14 - exp | |
ushorts[i] = sgn + ((frac + (1 << 10)) >> s) | |
} else if (exp > 15) { | |
// round overflow to +/- Infinity | |
ushorts[i] = sgn + 0x7c00 | |
} else { | |
// otherwise convert directly | |
ushorts[i] = sgn + ((exp + 15) << 10) + frac | |
} | |
} | |
} | |
return ushorts | |
} | |
},{"./pool":28}],31:[function(require,module,exports){ | |
module.exports = function (obj) { | |
return Object.keys(obj).map(function (key) { return obj[key] }) | |
} | |
},{}],32:[function(require,module,exports){ | |
// Context and canvas creation helper functions | |
var check = require('./util/check') | |
var extend = require('./util/extend') | |
function createCanvas (element, onDone, pixelRatio) { | |
var canvas = document.createElement('canvas') | |
extend(canvas.style, { | |
border: 0, | |
margin: 0, | |
padding: 0, | |
top: 0, | |
left: 0 | |
}) | |
element.appendChild(canvas) | |
if (element === document.body) { | |
canvas.style.position = 'absolute' | |
extend(element.style, { | |
margin: 0, | |
padding: 0 | |
}) | |
} | |
function resize () { | |
var w = window.innerWidth | |
var h = window.innerHeight | |
if (element !== document.body) { | |
var bounds = element.getBoundingClientRect() | |
w = bounds.right - bounds.left | |
h = bounds.top - bounds.bottom | |
} | |
canvas.width = pixelRatio * w | |
canvas.height = pixelRatio * h | |
extend(canvas.style, { | |
width: w + 'px', | |
height: h + 'px' | |
}) | |
} | |
window.addEventListener('resize', resize, false) | |
function onDestroy () { | |
window.removeEventListener('resize', resize) | |
element.removeChild(canvas) | |
} | |
resize() | |
return { | |
canvas: canvas, | |
onDestroy: onDestroy | |
} | |
} | |
function createContext (canvas, contexAttributes) { | |
function get (name) { | |
try { | |
return canvas.getContext(name, contexAttributes) | |
} catch (e) { | |
return null | |
} | |
} | |
return ( | |
get('webgl') || | |
get('experimental-webgl') || | |
get('webgl-experimental') | |
) | |
} | |
function isHTMLElement (obj) { | |
return ( | |
typeof obj.nodeName === 'string' && | |
typeof obj.appendChild === 'function' && | |
typeof obj.getBoundingClientRect === 'function' | |
) | |
} | |
function isWebGLContext (obj) { | |
return ( | |
typeof obj.drawArrays === 'function' || | |
typeof obj.drawElements === 'function' | |
) | |
} | |
function parseExtensions (input) { | |
if (typeof input === 'string') { | |
return input.split() | |
} | |
check(Array.isArray(input), 'invalid extension array') | |
return input | |
} | |
function getElement (desc) { | |
if (typeof desc === 'string') { | |
check(typeof document !== 'undefined', 'not supported outside of DOM') | |
return document.querySelector(desc) | |
} | |
return desc | |
} | |
module.exports = function parseArgs (args_) { | |
var args = args_ || {} | |
var element, container, canvas, gl | |
var contextAttributes = {} | |
var extensions = [] | |
var optionalExtensions = [] | |
var pixelRatio = (typeof window === 'undefined' ? 1 : window.devicePixelRatio) | |
var profile = false | |
var onDone = function (err) { | |
if (err) { | |
check.raise(err) | |
} | |
} | |
var onDestroy = function () {} | |
if (typeof args === 'string') { | |
check( | |
typeof document !== 'undefined', | |
'selector queries only supported in DOM enviroments') | |
element = document.querySelector(args) | |
check(element, 'invalid query string for element') | |
} else if (typeof args === 'object') { | |
if (isHTMLElement(args)) { | |
element = args | |
} else if (isWebGLContext(args)) { | |
gl = args | |
canvas = gl.canvas | |
} else { | |
check.constructor(args) | |
if ('gl' in args) { | |
gl = args.gl | |
} else if ('canvas' in args) { | |
canvas = getElement(args.canvas) | |
} else if ('container' in args) { | |
container = getElement(args.container) | |
} | |
if ('attributes' in args) { | |
contextAttributes = args.attributes | |
check.type(contextAttributes, 'object', 'invalid context attributes') | |
} | |
if ('extensions' in args) { | |
extensions = parseExtensions(args.extensions) | |
} | |
if ('optionalExtensions' in args) { | |
optionalExtensions = parseExtensions(args.optionalExtensions) | |
} | |
if ('onDone' in args) { | |
check.type( | |
args.onDone, 'function', | |
'invalid or missing onDone callback') | |
onDone = args.onDone | |
} | |
if ('profile' in args) { | |
profile = !!args.profile | |
} | |
if ('pixelRatio' in args) { | |
pixelRatio = +args.pixelRatio | |
check(pixelRatio > 0, 'invalid pixel ratio') | |
} | |
} | |
} else { | |
check.raise('invalid arguments to regl') | |
} | |
if (element) { | |
if (element.nodeName.toLowerCase() === 'canvas') { | |
canvas = element | |
} else { | |
container = element | |
} | |
} | |
if (!gl) { | |
if (!canvas) { | |
check( | |
typeof document !== 'undefined', | |
'must manually specify webgl context outside of DOM environments') | |
var result = createCanvas(container || document.body, onDone, pixelRatio) | |
if (!result) { | |
return null | |
} | |
canvas = result.canvas | |
onDestroy = result.onDestroy | |
} | |
gl = createContext(canvas, contextAttributes) | |
} | |
if (!gl) { | |
onDestroy() | |
onDone('webgl not supported, try upgrading your browser or graphics drivers http://get.webgl.org') | |
return null | |
} | |
return { | |
gl: gl, | |
canvas: canvas, | |
container: container, | |
extensions: extensions, | |
optionalExtensions: optionalExtensions, | |
pixelRatio: pixelRatio, | |
profile: profile, | |
onDone: onDone, | |
onDestroy: onDestroy | |
} | |
} | |
},{"./util/check":20,"./util/extend":23}],"regl":[function(require,module,exports){ | |
var check = require('./lib/util/check') | |
var extend = require('./lib/util/extend') | |
var dynamic = require('./lib/dynamic') | |
var raf = require('./lib/util/raf') | |
var clock = require('./lib/util/clock') | |
var createStringStore = require('./lib/strings') | |
var initWebGL = require('./lib/webgl') | |
var wrapExtensions = require('./lib/extension') | |
var wrapLimits = require('./lib/limits') | |
var wrapBuffers = require('./lib/buffer') | |
var wrapElements = require('./lib/elements') | |
var wrapTextures = require('./lib/texture') | |
var wrapRenderbuffers = require('./lib/renderbuffer') | |
var wrapFramebuffers = require('./lib/framebuffer') | |
var wrapAttributes = require('./lib/attribute') | |
var wrapShaders = require('./lib/shader') | |
var wrapRead = require('./lib/read') | |
var createCore = require('./lib/core') | |
var createStats = require('./lib/stats') | |
var createTimer = require('./lib/timer') | |
var GL_COLOR_BUFFER_BIT = 16384 | |
var GL_DEPTH_BUFFER_BIT = 256 | |
var GL_STENCIL_BUFFER_BIT = 1024 | |
var GL_ARRAY_BUFFER = 34962 | |
var CONTEXT_LOST_EVENT = 'webglcontextlost' | |
var CONTEXT_RESTORED_EVENT = 'webglcontextrestored' | |
var DYN_PROP = 1 | |
var DYN_CONTEXT = 2 | |
var DYN_STATE = 3 | |
function find (haystack, needle) { | |
for (var i = 0; i < haystack.length; ++i) { | |
if (haystack[i] === needle) { | |
return i | |
} | |
} | |
return -1 | |
} | |
module.exports = function wrapREGL (args) { | |
var config = initWebGL(args) | |
if (!config) { | |
return null | |
} | |
var gl = config.gl | |
var glAttributes = gl.getContextAttributes() | |
var extensionState = wrapExtensions(gl, config) | |
if (!extensionState) { | |
return null | |
} | |
var stringStore = createStringStore() | |
var stats = createStats() | |
var extensions = extensionState.extensions | |
var timer = createTimer(gl, extensions) | |
var START_TIME = clock() | |
var WIDTH = gl.drawingBufferWidth | |
var HEIGHT = gl.drawingBufferHeight | |
var contextState = { | |
tick: 0, | |
time: 0, | |
viewportWidth: WIDTH, | |
viewportHeight: HEIGHT, | |
framebufferWidth: WIDTH, | |
framebufferHeight: HEIGHT, | |
drawingBufferWidth: WIDTH, | |
drawingBufferHeight: HEIGHT, | |
pixelRatio: config.pixelRatio | |
} | |
var uniformState = {} | |
var drawState = { | |
elements: null, | |
primitive: 4, // GL_TRIANGLES | |
count: -1, | |
offset: 0, | |
instances: -1 | |
} | |
var limits = wrapLimits(gl, extensions) | |
var bufferState = wrapBuffers(gl, stats) | |
var elementState = wrapElements(gl, extensions, bufferState, stats) | |
var attributeState = wrapAttributes( | |
gl, | |
extensions, | |
limits, | |
bufferState, | |
stringStore) | |
var shaderState = wrapShaders(gl, stringStore, stats) | |
var textureState = wrapTextures( | |
gl, | |
extensions, | |
limits, | |
function () { core.procs.poll() }, | |
contextState, | |
stats) | |
var renderbufferState = wrapRenderbuffers(gl, extensions, limits, stats) | |
var framebufferState = wrapFramebuffers( | |
gl, | |
extensions, | |
limits, | |
textureState, | |
renderbufferState, | |
stats) | |
var core = createCore( | |
gl, | |
stringStore, | |
extensions, | |
limits, | |
bufferState, | |
elementState, | |
textureState, | |
framebufferState, | |
uniformState, | |
attributeState, | |
shaderState, | |
drawState, | |
contextState, | |
timer, | |
config) | |
var readPixels = wrapRead( | |
gl, | |
framebufferState, | |
core.procs.poll, | |
contextState, | |
glAttributes) | |
var nextState = core.next | |
var canvas = gl.canvas | |
var rafCallbacks = [] | |
var activeRAF = null | |
function handleRAF () { | |
if (rafCallbacks.length === 0) { | |
if (timer) { | |
timer.update() | |
} | |
activeRAF = null | |
return | |
} | |
// schedule next animation frame | |
activeRAF = raf.next(handleRAF) | |
// poll for changes | |
poll() | |
// fire a callback for all pending rafs | |
for (var i = rafCallbacks.length - 1; i >= 0; --i) { | |
var cb = rafCallbacks[i] | |
if (cb) { | |
cb(contextState, null, 0) | |
} | |
} | |
// flush all pending webgl calls | |
gl.flush() | |
// poll GPU timers *after* gl.flush so we don't delay command dispatch | |
if (timer) { | |
timer.update() | |
} | |
} | |
function startRAF () { | |
if (!activeRAF && rafCallbacks.length > 0) { | |
activeRAF = raf.next(handleRAF) | |
} | |
} | |
function stopRAF () { | |
if (activeRAF) { | |
raf.cancel(handleRAF) | |
activeRAF = null | |
} | |
} | |
function handleContextLoss (event) { | |
// TODO | |
} | |
function handleContextRestored (event) { | |
// TODO | |
} | |
if (canvas) { | |
canvas.addEventListener(CONTEXT_LOST_EVENT, handleContextLoss, false) | |
canvas.addEventListener(CONTEXT_RESTORED_EVENT, handleContextRestored, false) | |
} | |
function destroy () { | |
rafCallbacks.length = 0 | |
stopRAF() | |
if (canvas) { | |
canvas.removeEventListener(CONTEXT_LOST_EVENT, handleContextLoss) | |
canvas.removeEventListener(CONTEXT_RESTORED_EVENT, handleContextRestored) | |
} | |
shaderState.clear() | |
framebufferState.clear() | |
renderbufferState.clear() | |
textureState.clear() | |
elementState.clear() | |
bufferState.clear() | |
if (timer) { | |
timer.clear() | |
} | |
config.onDestroy() | |
} | |
function compileProcedure (options) { | |
check(!!options, 'invalid args to regl({...})') | |
check.type(options, 'object', 'invalid args to regl({...})') | |
function flattenNestedOptions (options) { | |
var result = extend({}, options) | |
delete result.uniforms | |
delete result.attributes | |
delete result.context | |
function merge (name) { | |
if (name in result) { | |
var child = result[name] | |
delete result[name] | |
Object.keys(child).forEach(function (prop) { | |
result[name + '.' + prop] = child[prop] | |
}) | |
} | |
} | |
merge('blend') | |
merge('depth') | |
merge('cull') | |
merge('stencil') | |
merge('polygonOffset') | |
merge('scissor') | |
merge('sample') | |
return result | |
} | |
function separateDynamic (object) { | |
var staticItems = {} | |
var dynamicItems = {} | |
Object.keys(object).forEach(function (option) { | |
var value = object[option] | |
if (dynamic.isDynamic(value)) { | |
dynamicItems[option] = dynamic.unbox(value, option) | |
} else { | |
staticItems[option] = value | |
} | |
}) | |
return { | |
dynamic: dynamicItems, | |
static: staticItems | |
} | |
} | |
// Treat context variables separate from other dynamic variables | |
var context = separateDynamic(options.context || {}) | |
var uniforms = separateDynamic(options.uniforms || {}) | |
var attributes = separateDynamic(options.attributes || {}) | |
var opts = separateDynamic(flattenNestedOptions(options)) | |
var stats = { | |
gpuTime: 0.0, | |
cpuTime: 0.0, | |
count: 0 | |
} | |
var compiled = core.compile(opts, attributes, uniforms, context, stats) | |
var draw = compiled.draw | |
var batch = compiled.batch | |
var scope = compiled.scope | |
var EMPTY_ARRAY = [] | |
function reserve (count) { | |
while (EMPTY_ARRAY.length < count) { | |
EMPTY_ARRAY.push(null) | |
} | |
return EMPTY_ARRAY | |
} | |
function REGLCommand (args, body) { | |
var i | |
if (typeof args === 'function') { | |
return scope.call(this, null, args, 0) | |
} else if (typeof body === 'function') { | |
if (typeof args === 'number') { | |
for (i = 0; i < args; ++i) { | |
scope.call(this, null, body, i) | |
} | |
return | |
} else if (Array.isArray(args)) { | |
for (i = 0; i < args.length; ++i) { | |
scope.call(this, args[i], body, i) | |
} | |
return | |
} else { | |
return scope.call(this, args, body, 0) | |
} | |
} else if (typeof args === 'number') { | |
if (args > 0) { | |
return batch.call(this, reserve(args | 0), args | 0) | |
} | |
} else if (Array.isArray(args)) { | |
if (args.length) { | |
return batch.call(this, args, args.length) | |
} | |
} else { | |
return draw.call(this, args) | |
} | |
} | |
return extend(REGLCommand, { | |
stats: stats | |
}) | |
} | |
function clear (options) { | |
check( | |
typeof options === 'object' && options, | |
'regl.clear() takes an object as input') | |
var clearFlags = 0 | |
core.procs.poll() | |
var c = options.color | |
if (c) { | |
gl.clearColor(+c[0] || 0, +c[1] || 0, +c[2] || 0, +c[3] || 0) | |
clearFlags |= GL_COLOR_BUFFER_BIT | |
} | |
if ('depth' in options) { | |
gl.clearDepth(+options.depth) | |
clearFlags |= GL_DEPTH_BUFFER_BIT | |
} | |
if ('stencil' in options) { | |
gl.clearStencil(options.stencil | 0) | |
clearFlags |= GL_STENCIL_BUFFER_BIT | |
} | |
check(!!clearFlags, 'called regl.clear with no buffer specified') | |
gl.clear(clearFlags) | |
} | |
function frame (cb) { | |
check.type(cb, 'function', 'regl.frame() callback must be a function') | |
rafCallbacks.push(cb) | |
function cancel () { | |
// FIXME: should we check something other than equals cb here? | |
// what if a user calls frame twice with the same callback... | |
// | |
var i = find(rafCallbacks, cb) | |
check(i >= 0, 'cannot cancel a frame twice') | |
function pendingCancel () { | |
var index = find(rafCallbacks, pendingCancel) | |
rafCallbacks[index] = rafCallbacks[rafCallbacks.length - 1] | |
rafCallbacks.length -= 1 | |
if (rafCallbacks.length <= 0) { | |
stopRAF() | |
} | |
} | |
rafCallbacks[i] = pendingCancel | |
} | |
startRAF() | |
return { | |
cancel: cancel | |
} | |
} | |
// poll viewport | |
function pollViewport () { | |
var viewport = nextState.viewport | |
var scissorBox = nextState.scissor_box | |
viewport[0] = viewport[1] = scissorBox[0] = scissorBox[1] = 0 | |
contextState.viewportWidth = | |
contextState.framebufferWidth = | |
contextState.drawingBufferWidth = | |
viewport[2] = | |
scissorBox[2] = gl.drawingBufferWidth | |
contextState.viewportHeight = | |
contextState.framebufferHeight = | |
contextState.drawingBufferHeight = | |
viewport[3] = | |
scissorBox[3] = gl.drawingBufferHeight | |
} | |
function poll () { | |
contextState.tick += 1 | |
contextState.time = (clock() - START_TIME) / 1000.0 | |
pollViewport() | |
core.procs.poll() | |
} | |
function refresh () { | |
pollViewport() | |
core.procs.refresh() | |
if (timer) { | |
timer.update() | |
} | |
} | |
refresh() | |
var regl = extend(compileProcedure, { | |
// Clear current FBO | |
clear: clear, | |
// Short cuts for dynamic variables | |
prop: dynamic.define.bind(null, DYN_PROP), | |
context: dynamic.define.bind(null, DYN_CONTEXT), | |
this: dynamic.define.bind(null, DYN_STATE), | |
// executes an empty draw command | |
draw: compileProcedure({}), | |
// Resources | |
buffer: function (options) { | |
return bufferState.create(options, GL_ARRAY_BUFFER) | |
}, | |
elements: elementState.create, | |
texture: textureState.create2D, | |
cube: textureState.createCube, | |
renderbuffer: renderbufferState.create, | |
framebuffer: framebufferState.create, | |
framebufferCube: function (options) { | |
check.raise('framebuffer cube not yet implemented') | |
}, | |
// Expose context attributes | |
attributes: glAttributes, | |
// Frame rendering | |
frame: frame, | |
// System limits | |
limits: limits, | |
hasExtension: function (name) { | |
return limits.extensions.indexOf(name.toLowerCase()) >= 0 | |
}, | |
// Read pixels | |
read: readPixels, | |
// Destroy regl and all associated resources | |
destroy: destroy, | |
// Direct GL state manipulation | |
_gl: gl, | |
_refresh: refresh, | |
poll: function () { | |
poll() | |
if (timer) { | |
timer.update() | |
} | |
}, | |
// regl Statistics Information | |
stats: stats | |
}) | |
config.onDone(null, regl) | |
return regl | |
} | |
},{"./lib/attribute":1,"./lib/buffer":2,"./lib/core":7,"./lib/dynamic":8,"./lib/elements":9,"./lib/extension":10,"./lib/framebuffer":11,"./lib/limits":12,"./lib/read":13,"./lib/renderbuffer":14,"./lib/shader":15,"./lib/stats":16,"./lib/strings":17,"./lib/texture":18,"./lib/timer":19,"./lib/util/check":20,"./lib/util/clock":21,"./lib/util/extend":23,"./lib/util/raf":29,"./lib/webgl":32}]},{},[]) | |
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2hvbWUvYWRtaW4vYnJvd3NlcmlmeS1jZG4vbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsImxpYi9hdHRyaWJ1dGUuanMiLCJsaWIvYnVmZmVyLmpzIiwibGliL2NvbnN0YW50cy9hcnJheXR5cGVzLmpzb24iLCJsaWIvY29uc3RhbnRzL2R0eXBlcy5qc29uIiwibGliL2NvbnN0YW50cy9wcmltaXRpdmVzLmpzb24iLCJsaWIvY29uc3RhbnRzL3VzYWdlLmpzb24iLCJsaWIvY29yZS5qcyIsImxpYi9keW5hbWljLmpzIiwibGliL2VsZW1lbnRzLmpzIiwibGliL2V4dGVuc2lvbi5qcyIsImxpYi9mcmFtZWJ1ZmZlci5qcyIsImxpYi9saW1pdHMuanMiLCJsaWIvcmVhZC5qcyIsImxpYi9yZW5kZXJidWZmZXIuanMiLCJsaWIvc2hhZGVyLmpzIiwibGliL3N0YXRzLmpzIiwibGliL3N0cmluZ3MuanMiLCJsaWIvdGV4dHVyZS5qcyIsImxpYi90aW1lci5qcyIsImxpYi91dGlsL2NoZWNrLmpzIiwibGliL3V0aWwvY2xvY2suanMiLCJsaWIvdXRpbC9jb2RlZ2VuLmpzIiwibGliL3V0aWwvZXh0ZW5kLmpzIiwibGliL3V0aWwvaXMtYXJyYXktbGlrZS5qcyIsImxpYi91dGlsL2lzLW5kYXJyYXkuanMiLCJsaWIvdXRpbC9pcy10eXBlZC1hcnJheS5qcyIsImxpYi91dGlsL2xvb3AuanMiLCJsaWIvdXRpbC9wb29sLmpzIiwibGliL3V0aWwvcmFmLmpzIiwibGliL3V0aWwvdG8taGFsZi1mbG9hdC5qcyIsImxpYi91dGlsL3ZhbHVlcy5qcyIsImxpYi93ZWJnbC5qcyIsInJlZ2wuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeFZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ1pBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDVkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDWkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ0xBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3a0dBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pSQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3ZxQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzVGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM5RUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4TEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzdLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNaQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25CQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMzNDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdElBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMW5CQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDTEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcExBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDUEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNKQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDSkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNQQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1Q0E7QUFDQTtBQUNBO0FBQ0E7O0FDSEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNU1BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCJ2YXIgR0xfRkxPQVQgPSA1MTI2XG5cbmZ1bmN0aW9uIEF0dHJpYnV0ZVJlY29yZCAoKSB7XG4gIHRoaXMuc3RhdGUgPSAwXG5cbiAgdGhpcy54ID0gMC4wXG4gIHRoaXMueSA9IDAuMFxuICB0aGlzLnogPSAwLjBcbiAgdGhpcy53ID0gMC4wXG5cbiAgdGhpcy5idWZmZXIgPSBudWxsXG4gIHRoaXMuc2l6ZSA9IDBcbiAgdGhpcy5ub3JtYWxpemVkID0gZmFsc2VcbiAgdGhpcy50eXBlID0gR0xfRkxPQVRcbiAgdGhpcy5vZmZzZXQgPSAwXG4gIHRoaXMuc3RyaWRlID0gMFxuICB0aGlzLmRpdmlzb3IgPSAwXG59XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gd3JhcEF0dHJpYnV0ZVN0YXRlIChcbiAgZ2wsXG4gIGV4dGVuc2lvbnMsXG4gIGxpbWl0cyxcbiAgYnVmZmVyU3RhdGUsXG4gIHN0cmluZ1N0b3JlKSB7XG4gIHZhciBOVU1fQVRUUklCVVRFUyA9IGxpbWl0cy5tYXhBdHRyaWJ1dGVzXG4gIHZhciBhdHRyaWJ1dGVCaW5kaW5ncyA9IG5ldyBBcnJheShOVU1fQVRUUklCVVRFUylcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBOVU1fQVRUUklCVVRFUzsgKytpKSB7XG4gICAgYXR0cmlidXRlQmluZGluZ3NbaV0gPSBuZXcgQXR0cmlidXRlUmVjb3JkKClcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgUmVjb3JkOiBBdHRyaWJ1dGVSZWNvcmQsXG4gICAgc2NvcGU6IHt9LFxuICAgIHN0YXRlOiBhdHRyaWJ1dGVCaW5kaW5nc1xuICB9XG59XG4iLCJ2YXIgY2hlY2sgPSByZXF1aXJlKCcuL3V0aWwvY2hlY2snKVxudmFyIGlzVHlwZWRBcnJheSA9IHJlcXVpcmUoJy4vdXRpbC9pcy10eXBlZC1hcnJheScpXG52YXIgaXNOREFycmF5TGlrZSA9IHJlcXVpcmUoJy4vdXRpbC9pcy1uZGFycmF5JylcbnZhciB2YWx1ZXMgPSByZXF1aXJlKCcuL3V0aWwvdmFsdWVzJylcbnZhciBwb29sID0gcmVxdWlyZSgnLi91dGlsL3Bvb2wnKVxuXG52YXIgYXJyYXlUeXBlcyA9IHJlcXVpcmUoJy4vY29uc3RhbnRzL2FycmF5dHlwZXMuanNvbicpXG52YXIgYnVmZmVyVHlwZXMgPSByZXF1aXJlKCcuL2NvbnN0YW50cy9kdHlwZXMuanNvbicpXG52YXIgdXNhZ2VUeXBlcyA9IHJlcXVpcmUoJy4vY29uc3RhbnRzL3VzYWdlLmpzb24nKVxuXG52YXIgR0xfU1RBVElDX0RSQVcgPSAweDg4RTRcbnZhciBHTF9TVFJFQU1fRFJBVyA9IDB4ODhFMFxuXG52YXIgR0xfVU5TSUdORURfQllURSA9IDUxMjFcbnZhciBHTF9GTE9BVCA9IDUxMjZcblxuZnVuY3Rpb24gdHlwZWRBcnJheUNvZGUgKGRhdGEpIHtcbiAgcmV0dXJuIGFycmF5VHlwZXNbT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKGRhdGEpXSB8IDBcbn1cblxuZnVuY3Rpb24gY29weUFycmF5IChvdXQsIGlucCkge1xuICBmb3IgKHZhciBpID0gMDsgaSA8IGlucC5sZW5ndGg7ICsraSkge1xuICAgIG91dFtpXSA9IGlucFtpXVxuICB9XG59XG5cbmZ1bmN0aW9uIHRyYW5zcG9zZSAoXG4gIHJlc3VsdCwgZGF0YSwgc2hhcGVYLCBzaGFwZVksIHN0cmlkZVgsIHN0cmlkZVksIG9mZnNldCkge1xuICB2YXIgcHRyID0gMFxuICBmb3IgKHZhciBpID0gMDsgaSA8IHNoYXBlWDsgKytpKSB7XG4gICAgZm9yICh2YXIgaiA9IDA7IGogPCBzaGFwZVk7ICsraikge1xuICAgICAgcmVzdWx0W3B0cisrXSA9IGRhdGFbc3RyaWRlWCAqIGkgKyBzdHJpZGVZICogaiArIG9mZnNldF1cbiAgICB9XG4gIH1cbn1cblxuZnVuY3Rpb24gZmxhdHRlbiAocmVzdWx0LCBkYXRhLCBkaW1lbnNpb24pIHtcbiAgdmFyIHB0ciA9IDBcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBkYXRhLmxlbmd0aDsgKytpKSB7XG4gICAgdmFyIHYgPSBkYXRhW2ldXG4gICAgZm9yICh2YXIgaiA9IDA7IGogPCBkaW1lbnNpb247ICsraikge1xuICAgICAgcmVzdWx0W3B0cisrXSA9IHZbal1cbiAgICB9XG4gIH1cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiB3cmFwQnVmZmVyU3RhdGUgKGdsLCBzdGF0cykge1xuICB2YXIgYnVmZmVyQ291bnQgPSAwXG4gIHZhciBidWZmZXJTZXQgPSB7fVxuXG4gIGZ1bmN0aW9uIFJFR0xCdWZmZXIgKHR5cGUpIHtcbiAgICB0aGlzLmlkID0gYnVmZmVyQ291bnQrK1xuICAgIHRoaXMuYnVmZmVyID0gZ2wuY3JlYXRlQnVmZmVyKClcbiAgICB0aGlzLnR5cGUgPSB0eXBlXG4gICAgdGhpcy51c2FnZSA9IEdMX1NUQVRJQ19EUkFXXG4gICAgdGhpcy5ieXRlTGVuZ3RoID0gMFxuICAgIHRoaXMuZGltZW5zaW9uID0gMVxuICAgIHRoaXMuZHR5cGUgPSBHTF9VTlNJR05FRF9CWVRFXG4gIH1cblxuICBSRUdMQnVmZmVyLnByb3RvdHlwZS5iaW5kID0gZnVuY3Rpb24gKCkge1xuICAgIGdsLmJpbmRCdWZmZXIodGhpcy50eXBlLCB0aGlzLmJ1ZmZlcilcbiAgfVxuXG4gIFJFR0xCdWZmZXIucHJvdG90eXBlLmRlc3Ryb3kgPSBmdW5jdGlvbiAoKSB7XG4gICAgZGVzdHJveSh0aGlzKVxuICB9XG5cbiAgdmFyIHN0cmVhbVBvb2wgPSBbXVxuXG4gIGZ1bmN0aW9uIGNyZWF0ZVN0cmVhbSAodHlwZSwgZGF0YSkge1xuICAgIHZhciBidWZmZXIgPSBzdHJlYW1Qb29sLnBvcCgpXG4gICAgaWYgKCFidWZmZXIpIHtcbiAgICAgIGJ1ZmZlciA9IG5ldyBSRUdMQnVmZmVyKHR5cGUpXG4gICAgICBidWZmZXIuYnVmZmVyID0gZ2wuY3JlYXRlQnVmZmVyKClcbiAgICB9XG4gICAgYnVmZmVyLmJpbmQoKVxuICAgIGluaXRCdWZmZXJGcm9tRGF0YShidWZmZXIsIGRhdGEsIEdMX1NUUkVBTV9EUkFXLCAwLCAxKVxuICAgIHJldHVybiBidWZmZXJcbiAgfVxuXG4gIGZ1bmN0aW9uIGRlc3Ryb3lTdHJlYW0gKHN0cmVhbSkge1xuICAgIHN0cmVhbVBvb2wucHVzaChzdHJlYW0pXG4gIH1cblxuICBmdW5jdGlvbiBpbml0QnVmZmVyRnJvbVR5cGVkQXJyYXkgKGJ1ZmZlciwgZGF0YSwgdXNhZ2UpIHtcbiAgICBidWZmZXIuYnl0ZUxlbmd0aCA9IGRhdGEuYnl0ZUxlbmd0aFxuICAgIGdsLmJ1ZmZlckRhdGEoYnVmZmVyLnR5cGUsIGRhdGEsIHVzYWdlKVxuICB9XG5cbiAgZnVuY3Rpb24gaW5pdEJ1ZmZlckZyb21EYXRhIChidWZmZXIsIGRhdGEsIHVzYWdlLCBkdHlwZSwgZGltZW5zaW9uKSB7XG4gICAgYnVmZmVyLnVzYWdlID0gdXNhZ2VcbiAgICBpZiAoQXJyYXkuaXNBcnJheShkYXRhKSkge1xuICAgICAgYnVmZmVyLmR0eXBlID0gZHR5cGUgfHwgR0xfRkxPQVRcbiAgICAgIGlmIChkYXRhLmxlbmd0aCA+IDApIHtcbiAgICAgICAgdmFyIGZsYXREYXRhXG4gICAgICAgIGlmIChBcnJheS5pc0FycmF5KGRhdGFbMF0pKSB7XG4gICAgICAgICAgYnVmZmVyLmRpbWVuc2lvbiA9IGRhdGFbMF0ubGVuZ3RoXG4gICAgICAgICAgZmxhdERhdGEgPSBwb29sLmFsbG9jVHlwZShcbiAgICAgICAgICAgIGJ1ZmZlci5kdHlwZSxcbiAgICAgICAgICAgIGRhdGEubGVuZ3RoICogYnVmZmVyLmRpbWVuc2lvbilcbiAgICAgICAgICBmbGF0dGVuKGZsYXREYXRhLCBkYXRhLCBidWZmZXIuZGltZW5zaW9uKVxuICAgICAgICAgIGluaXRCdWZmZXJGcm9tVHlwZWRBcnJheShidWZmZXIsIGZsYXREYXRhLCB1c2FnZSlcbiAgICAgICAgICBwb29sLmZyZWVUeXBlKGZsYXREYXRhKVxuICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiBkYXRhWzBdID09PSAnbnVtYmVyJykge1xuICAgICAgICAgIGJ1ZmZlci5kaW1lbnNpb24gPSBkaW1lbnNpb25cbiAgICAgICAgICB2YXIgdHlwZWREYXRhID0gcG9vbC5hbGxvY1R5cGUoYnVmZmVyLmR0eXBlLCBkYXRhLmxlbmd0aClcbiAgICAgICAgICBjb3B5QXJyYXkodHlwZWREYXRhLCBkYXRhKVxuICAgICAgICAgIGluaXRCdWZmZXJGcm9tVHlwZWRBcnJheShidWZmZXIsIHR5cGVkRGF0YSwgdXNhZ2UpXG4gICAgICAgICAgcG9vbC5mcmVlVHlwZSh0eXBlZERhdGEpXG4gICAgICAgIH0gZWxzZSBpZiAoaXNUeXBlZEFycmF5KGRhdGFbMF0pKSB7XG4gICAgICAgICAgYnVmZmVyLmRpbWVuc2lvbiA9IGRhdGFbMF0ubGVuZ3RoXG4gICAgICAgICAgYnVmZmVyLmR0eXBlID0gZHR5cGUgfHwgdHlwZWRBcnJheUNvZGUoZGF0YVswXSkgfHwgR0xfRkxPQVRcbiAgICAgICAgICBmbGF0RGF0YSA9IHBvb2wuYWxsb2NUeXBlKFxuICAgICAgICAgICAgYnVmZmVyLmR0eXBlLFxuICAgICAgICAgICAgZGF0YS5sZW5ndGggKiBidWZmZXIuZGltZW5zaW9uKVxuICAgICAgICAgIGZsYXR0ZW4oZmxhdERhdGEsIGRhdGEsIGJ1ZmZlci5kaW1lbnNpb24pXG4gICAgICAgICAgaW5pdEJ1ZmZlckZyb21UeXBlZEFycmF5KGJ1ZmZlciwgZmxhdERhdGEsIHVzYWdlKVxuICAgICAgICAgIHBvb2wuZnJlZVR5cGUoZmxhdERhdGEpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY2hlY2sucmFpc2UoJ2ludmFsaWQgYnVmZmVyIGRhdGEnKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChpc1R5cGVkQXJyYXkoZGF0YSkpIHtcbiAgICAgIGJ1ZmZlci5kdHlwZSA9IGR0eXBlIHx8IHR5cGVkQXJyYXlDb2RlKGRhdGEpXG4gICAgICBidWZmZXIuZGltZW5zaW9uID0gZGltZW5zaW9uXG4gICAgICBpbml0QnVmZmVyRnJvbVR5cGVkQXJyYXkoYnVmZmVyLCBkYXRhLCB1c2FnZSlcbiAgICB9IGVsc2UgaWYgKGlzTkRBcnJheUxpa2UoZGF0YSkpIHtcbiAgICAgIHZhciBzaGFwZSA9IGRhdGEuc2hhcGVcbiAgICAgIHZhciBzdHJpZGUgPSBkYXRhLnN0cmlkZVxuICAgICAgdmFyIG9mZnNldCA9IGRhdGEub2Zmc2V0XG5cbiAgICAgIHZhciBzaGFwZVggPSAwXG4gICAgICB2YXIgc2hhcGVZID0gMFxuICAgICAgdmFyIHN0cmlkZVggPSAwXG4gICAgICB2YXIgc3RyaWRlWSA9IDBcbiAgICAgIGlmIChzaGFwZS5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgc2hhcGVYID0gc2hhcGVbMF1cbiAgICAgICAgc2hhcGVZID0gMVxuICAgICAgICBzdHJpZGVYID0gc3RyaWRlWzBdXG4gICAgICAgIHN0cmlkZVkgPSAwXG4gICAgICB9IGVsc2UgaWYgKHNoYXBlLmxlbmd0aCA9PT0gMikge1xuICAgICAgICBzaGFwZVggPSBzaGFwZVswXVxuICAgICAgICBzaGFwZVkgPSBzaGFwZVsxXVxuICAgICAgICBzdHJpZGVYID0gc3RyaWRlWzBdXG4gICAgICAgIHN0cmlkZVkgPSBzdHJpZGVbMV1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNoZWNrLnJhaXNlKCdpbnZhbGlkIHNoYXBlJylcbiAgICAgIH1cblxuICAgICAgYnVmZmVyLmR0eXBlID0gZHR5cGUgfHwgdHlwZWRBcnJheUNvZGUoZGF0YS5kYXRhKSB8fCBHTF9GTE9BVFxuICAgICAgYnVmZmVyLmRpbWVuc2lvbiA9IHNoYXBlWVxuXG4gICAgICB2YXIgdHJhbnNwb3NlRGF0YSA9IHBvb2wuYWxsb2NUeXBlKGJ1ZmZlci5kdHlwZSwgc2hhcGVYICogc2hhcGVZKVxuICAgICAgdHJhbnNwb3NlKHRyYW5zcG9zZURhdGEsXG4gICAgICAgIGRhdGEuZGF0YSxcbiAgICAgICAgc2hhcGVYLCBzaGFwZVksXG4gICAgICAgIHN0cmlkZVgsIHN0cmlkZVksXG4gICAgICAgIG9mZnNldClcbiAgICAgIGluaXRCdWZmZXJGcm9tVHlwZWRBcnJheShidWZmZXIsIHRyYW5zcG9zZURhdGEsIHVzYWdlKVxuICAgICAgcG9vbC5mcmVlVHlwZSh0cmFuc3Bvc2VEYXRhKVxuICAgIH0gZWxzZSB7XG4gICAgICBjaGVjay5yYWlzZSgnaW52YWxpZCBidWZmZXIgZGF0YScpXG4gICAgfVxuICB9XG5cbiAgZnVuY3Rpb24gZGVzdHJveSAoYnVmZmVyKSB7XG4gICAgc3RhdHMuYnVmZmVyQ291bnQtLVxuXG4gICAgdmFyIGhhbmRsZSA9IGJ1ZmZlci5idWZmZXJcbiAgICBjaGVjayhoYW5kbGUsICdidWZmZXIgbXVzdCBub3QgYmUgZGVsZXRlZCBhbHJlYWR5JylcbiAgICBnbC5kZWxldGVCdWZmZXIoaGFuZGxlKVxuICAgIGJ1ZmZlci5idWZmZXIgPSBudWxsXG4gICAgZGVsZXRlIGJ1ZmZlclNldFtidWZmZXIuaWRdXG4gIH1cblxuICBmdW5jdGlvbiBjcmVhdGVCdWZmZXIgKG9wdGlvbnMsIHR5cGUsIGRlZmVySW5pdCkge1xuICAgIHN0YXRzLmJ1ZmZlckNvdW50KytcblxuICAgIHZhciBidWZmZXIgPSBuZXcgUkVHTEJ1ZmZlcih0eXBlKVxuICAgIGJ1ZmZlclNldFtidWZmZXIuaWRdID0gYnVmZmVyXG5cbiAgICBmdW5jdGlvbiByZWdsQnVmZmVyIChvcHRpb25zKSB7XG4gICAgICB2YXIgdXNhZ2UgPSBHTF9TVEFUSUNfRFJBV1xuICAgICAgdmFyIGRhdGEgPSBudWxsXG4gICAgICB2YXIgYnl0ZUxlbmd0aCA9IDBcbiAgICAgIHZhciBkdHlwZSA9IDBcbiAgICAgIHZhciBkaW1lbnNpb24gPSAxXG4gICAgICBpZiAoQXJyYXkuaXNBcnJheShvcHRpb25zKSB8fFxuICAgICAgICAgIGlzVHlwZWRBcnJheShvcHRpb25zKSB8fFxuICAgICAgICAgIGlzTkRBcnJheUxpa2Uob3B0aW9ucykpIHtcbiAgICAgICAgZGF0YSA9IG9wdGlvbnNcbiAgICAgIH0gZWxzZSBpZiAodHlwZW9mIG9wdGlvbnMgPT09ICdudW1iZXInKSB7XG4gICAgICAgIGJ5dGVMZW5ndGggPSBvcHRpb25zIHwgMFxuICAgICAgfSBlbHNlIGlmIChvcHRpb25zKSB7XG4gICAgICAgIGNoZWNrLnR5cGUoXG4gICAgICAgICAgb3B0aW9ucywgJ29iamVjdCcsXG4gICAgICAgICAgJ2J1ZmZlciBhcmd1bWVudHMgbXVzdCBiZSBhbiBvYmplY3QsIGEgbnVtYmVyIG9yIGFuIGFycmF5JylcblxuICAgICAgICBpZiAoJ2RhdGEnIGluIG9wdGlvbnMpIHtcbiAgICAgICAgICBjaGVjayhcbiAgICAgICAgICAgIGRhdGEgPT09IG51bGwgfHxcbiAgICAgICAgICAgIEFycmF5LmlzQXJyYXkoZGF0YSkgfHxcbiAgICAgICAgICAgIGlzVHlwZWRBcnJheShkYXRhKSB8fFxuICAgICAgICAgICAgaXNOREFycmF5TGlrZShkYXRhKSxcbiAgICAgICAgICAgICdpbnZhbGlkIGRhdGEgZm9yIGJ1ZmZlcicpXG4gICAgICAgICAgZGF0YSA9IG9wdGlvbnMuZGF0YVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCd1c2FnZScgaW4gb3B0aW9ucykge1xuICAgICAgICAgIGNoZWNrLnBhcmFtZXRlcihvcHRpb25zLnVzYWdlLCB1c2FnZVR5cGVzLCAnaW52YWxpZCBidWZmZXIgdXNhZ2UnKVxuICAgICAgICAgIHVzYWdlID0gdXNhZ2VUeXBlc1tvcHRpb25zLnVzYWdlXVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCd0eXBlJyBpbiBvcHRpb25zKSB7XG4gICAgICAgICAgY2hlY2sucGFyYW1ldGVyKG9wdGlvbnMudHlwZSwgYnVmZmVyVHlwZXMsICdpbnZhbGlkIGJ1ZmZlciB0eXBlJylcbiAgICAgICAgICBkdHlwZSA9IGJ1ZmZlclR5cGVzW29wdGlvbnMudHlwZV1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmICgnZGltZW5zaW9uJyBpbiBvcHRpb25zKSB7XG4gICAgICAgICAgY2hlY2sudHlwZShvcHRpb25zLmRpbWVuc2lvbiwgJ251bWJlcicsICdpbnZhbGlkIGRpbWVuc2lvbicpXG4gICAgICAgICAgZGltZW5zaW9uID0gb3B0aW9ucy5kaW1lbnNpb24gfCAwXG4gICAgICAgIH1cblxuICAgICAgICBpZiAoJ2xlbmd0aCcgaW4gb3B0aW9ucykge1xuICAgICAgICAgIGNoZWNrLm5uaShieXRlTGVuZ3RoLCAnYnVmZmVyIGxlbmd0aCBtdXN0IGJlIGEgbm9ubmVnYXRpdmUgaW50ZWdlcicpXG4gICAgICAgICAgYnl0ZUxlbmd0aCA9IG9wdGlvbnMubGVuZ3RoIHwgMFxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGJ1ZmZlci5iaW5kKClcbiAgICAgIGlmICghZGF0YSkge1xuICAgICAgICBnbC5idWZmZXJEYXRhKGJ1ZmZlci50eXBlLCBieXRlTGVuZ3RoLCB1c2FnZSlcbiAgICAgICAgYnVmZmVyLmR0eXBlID0gZHR5cGUgfHwgR0xfVU5TSUdORURfQllURVxuICAgICAgICBidWZmZXIudXNhZ2UgPSB1c2FnZVxuICAgICAgICBidWZmZXIuZGltZW5zaW9uID0gZGltZW5zaW9uXG4gICAgICAgIGJ1ZmZlci5ieXRlTGVuZ3RoID0gYnl0ZUxlbmd0aFxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaW5pdEJ1ZmZlckZyb21EYXRhKGJ1ZmZlciwgZGF0YSwgdXNhZ2UsIGR0eXBlLCBkaW1lbnNpb24pXG4gICAgICB9XG5cbiAgICAgIHJldHVybiByZWdsQnVmZmVyXG4gICAgfVxuXG4gICAgZnVuY3Rpb24gc2V0U3ViRGF0YSAoZGF0YSwgb2Zmc2V0KSB7XG4gICAgICBjaGVjayhvZmZzZXQgKyBkYXRhLmJ5dGVMZW5ndGggPD0gYnVmZmVyLmJ5dGVMZW5ndGgsXG4gICAgICAgICdpbnZhbGlkIGJ1ZmZlciBzdWJkYXRhIGNhbGwsIGJ1ZmZlciBpcyB0b28gc21hbGwuICcgKyAnIENhblxcJ3Qgd3JpdGUgZGF0YSBvZiBzaXplICcgKyBkYXRhLmJ5dGVMZW5ndGggKyAnIHN0YXJ0aW5nIGZyb20gb2Zmc2V0ICcgKyBvZmZzZXQgKyAnIHRvIGEgYnVmZmVyIG9mIHNpemUgJyArIGJ1ZmZlci5ieXRlTGVuZ3RoKVxuXG4gICAgICBnbC5idWZmZXJTdWJEYXRhKGJ1ZmZlci50eXBlLCBvZmZzZXQsIGRhdGEpXG4gICAgfVxuXG4gICAgZnVuY3Rpb24gc3ViZGF0YSAoZGF0YSwgb2Zmc2V0Xykge1xuICAgICAgdmFyIG9mZnNldCA9IChvZmZzZXRfIHx8IDApIHwgMFxuICAgICAgYnVmZmVyLmJpbmQoKVxuICAgICAgaWYgKEFycmF5LmlzQXJyYXkoZGF0YSkpIHtcbiAgICAgICAgaWYgKGRhdGEubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGlmICh0eXBlb2YgZGF0YVswXSA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgICAgIHZhciBjb252ZXJ0ZWQgPSBwb29sLmFsbG9jVHlwZShidWZmZXIuZHR5cGUsIGRhdGEubGVuZ3RoKVxuICAgICAgICAgICAgY29weUFycmF5KGNvbnZlcnRlZCwgZGF0YSlcbiAgICAgICAgICAgIHNldFN1YkRhdGEoY29udmVydGVkLCBvZmZzZXQpXG4gICAgICAgICAgICBwb29sLmZyZWVUeXBlKGNvbnZlcnRlZClcbiAgICAgICAgICB9IGVsc2UgaWYgKEFycmF5LmlzQXJyYXkoZGF0YVswXSkgfHwgaXNUeXBlZEFycmF5KGRhdGFbMF0pKSB7XG4gICAgICAgICAgICB2YXIgZGltZW5zaW9uID0gZGF0YVswXS5sZW5ndGhcbiAgICAgICAgICAgIHZhciBmbGF0RGF0YSA9IHBvb2wuYWxsb2NUeXBlKGJ1ZmZlci5kdHlwZSwgZGF0YS5sZW5ndGggKiBkaW1lbnNpb24pXG4gICAgICAgICAgICBmbGF0dGVuKGZsYXREYXRhLCBkYXRhLCBkaW1lbnNpb24pXG4gICAgICAgICAgICBzZXRTdWJEYXRhKGZsYXREYXRhLCBvZmZzZXQpXG4gICAgICAgICAgICBwb29sLmZyZWVUeXBlKGZsYXREYXRhKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjaGVjay5yYWlzZSgnaW52YWxpZCBidWZmZXIgZGF0YScpXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGlzVHlwZWRBcnJheShkYXRhKSkge1xuICAgICAgICBzZXRTdWJEYXRhKGRhdGEsIG9mZnNldClcbiAgICAgIH0gZWxzZSBpZiAoaXNOREFycmF5TGlrZShkYXRhKSkge1xuICAgICAgICB2YXIgc2hhcGUgPSBkYXRhLnNoYXBlXG4gICAgICAgIHZhciBzdHJpZGUgPSBkYXRhLnN0cmlkZVxuXG4gICAgICAgIHZhciBzaGFwZVggPSAwXG4gICAgICAgIHZhciBzaGFwZVkgPSAwXG4gICAgICAgIHZhciBzdHJpZGVYID0gMFxuICAgICAgICB2YXIgc3RyaWRlWSA9IDBcbiAgICAgICAgaWYgKHNoYXBlLmxlbmd0aCA9PT0gMSkge1xuICAgICAgICAgIHNoYXBlWCA9IHNoYXBlWzBdXG4gICAgICAgICAgc2hhcGVZID0gMVxuICAgICAgICAgIHN0cmlkZVggPSBzdHJpZGVbMF1cbiAgICAgICAgICBzdHJpZGVZID0gMFxuICAgICAgICB9IGVsc2UgaWYgKHNoYXBlLmxlbmd0aCA9PT0gMikge1xuICAgICAgICAgIHNoYXBlWCA9IHNoYXBlWzBdXG4gICAgICAgICAgc2hhcGVZID0gc2hhcGVbMV1cbiAgICAgICAgICBzdHJpZGVYID0gc3RyaWRlWzBdXG4gICAgICAgICAgc3RyaWRlWSA9IHN0cmlkZVsxXVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGNoZWNrLnJhaXNlKCdpbnZhbGlkIHNoYXBlJylcbiAgICAgICAgfVxuICAgICAgICB2YXIgZHR5cGUgPSBBcnJheS5pc0FycmF5KGRhdGEuZGF0YSlcbiAgICAgICAgICA/IGJ1ZmZlci5kdHlwZVxuICAgICAgICAgIDogdHlwZWRBcnJheUNvZGUoZGF0YS5kYXRhKVxuXG4gICAgICAgIHZhciB0cmFuc3Bvc2VEYXRhID0gcG9vbC5hbGxvY1R5cGUoZHR5cGUsIHNoYXBlWCAqIHNoYXBlWSlcbiAgICAgICAgdHJhbnNwb3NlKHRyYW5zcG9zZURhdGEsXG4gICAgICAgICAgZGF0YS5kYXRhLFxuICAgICAgICAgIHNoYXBlWCwgc2hhcGVZLFxuICAgICAgICAgIHN0cmlkZVgsIHN0cmlkZVksXG4gICAgICAgICAgZGF0YS5vZmZzZXQpXG4gICAgICAgIHNldFN1YkRhdGEodHJhbnNwb3NlRGF0YSwgb2Zmc2V0KVxuICAgICAgICBwb29sLmZyZWVUeXBlKHRyYW5zcG9zZURhdGEpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjaGVjay5yYWlzZSgnaW52YWxpZCBkYXRhIGZvciBidWZmZXIgc3ViZGF0YScpXG4gICAgICB9XG4gICAgICByZXR1cm4gcmVnbEJ1ZmZlclxuICAgIH1cblxuICAgIGlmICghZGVmZXJJbml0KSB7XG4gICAgICByZWdsQnVmZmVyKG9wdGlvbnMpXG4gICAgfVxuXG4gICAgcmVnbEJ1ZmZlci5fcmVnbFR5cGUgPSAnYnVmZmVyJ1xuICAgIHJlZ2xCdWZmZXIuX2J1ZmZlciA9IGJ1ZmZlclxuICAgIHJlZ2xCdWZmZXIuc3ViZGF0YSA9IHN1YmRhdGFcbiAgICByZWdsQnVmZmVyLmRlc3Ryb3kgPSBmdW5jdGlvbiAoKSB7IGRlc3Ryb3koYnVmZmVyKSB9XG5cbiAgICByZXR1cm4gcmVnbEJ1ZmZlclxuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBjcmVhdGU6IGNyZWF0ZUJ1ZmZlcixcblxuICAgIGNyZWF0ZVN0cmVhbTogY3JlYXRlU3RyZWFtLFxuICAgIGRlc3Ryb3lTdHJlYW06IGRlc3Ryb3lTdHJlYW0sXG5cbiAgICBjbGVhcjogZnVuY3Rpb24gKCkge1xuICAgICAgdmFsdWVzKGJ1ZmZlclNldCkuZm9yRWFjaChkZXN0cm95KVxuICAgIH0sXG5cbiAgICBnZXRCdWZmZXI6IGZ1bmN0aW9uICh3cmFwcGVyKSB7XG4gICAgICBpZiAod3JhcHBlciAmJiB3cmFwcGVyLl9idWZmZXIgaW5zdGFuY2VvZiBSRUdMQnVmZmVyKSB7XG4gICAgICAgIHJldHVybiB3cmFwcGVyLl9idWZmZXJcbiAgICAgIH1cbiAgICAgIHJldHVybiBudWxsXG4gICAgfSxcblxuICAgIF9pbml0QnVmZmVyOiBpbml0QnVmZmVyRnJvbURhdGFcbiAgfVxufVxuIiwibW9kdWxlLmV4cG9ydHM9e1xuICBcIltvYmplY3QgSW50OEFycmF5XVwiOiA1MTIwXG4sIFwiW29iamVjdCBJbnQxNkFycmF5XVwiOiA1MTIyXG4sIFwiW29iamVjdCBJbnQzMkFycmF5XVwiOiA1MTI0XG4sIFwiW29iamVjdCBVaW50OEFycmF5XVwiOiA1MTIxXG4sIFwiW29iamVjdCBVaW50OENsYW1wZWRBcnJheV1cIjogNTEyMVxuLCBcIltvYmplY3QgVWludDE2QXJyYXldXCI6IDUxMjNcbiwgXCJbb2JqZWN0IFVpbnQzMkFycmF5XVwiOiA1MTI1XG4sIFwiW29iamVjdCBGbG9hdDMyQXJyYXldXCI6IDUxMjZcbiwgXCJbb2JqZWN0IEZsb2F0NjRBcnJheV1cIjogNTEyMVxuLCBcIltvYmplY3QgQXJyYXlCdWZmZXJdXCI6IDUxMjFcbn1cbiIsIm1vZHVsZS5leHBvcnRzPXtcbiAgXCJpbnQ4XCI6IDUxMjBcbiwgXCJpbnQxNlwiOiA1MTIyXG4sIFwiaW50MzJcIjogNTEyNFxuLCBcInVpbnQ4XCI6IDUxMjFcbiwgXCJ1aW50MTZcIjogNTEyM1xuLCBcInVpbnQzMlwiOiA1MTI1XG4sIFwiZmxvYXRcIjogNTEyNlxuLCBcImZsb2F0MzJcIjogNTEyNlxufVxuIiwibW9kdWxlLmV4cG9ydHM9e1xuICBcInBvaW50c1wiOiAwLFxuICBcInBvaW50XCI6IDAsXG4gIFwibGluZXNcIjogMSxcbiAgXCJsaW5lXCI6IDEsXG4gIFwibGluZSBsb29wXCI6IDIsXG4gIFwibGluZSBzdHJpcFwiOiAzLFxuICBcInRyaWFuZ2xlc1wiOiA0LFxuICBcInRyaWFuZ2xlXCI6IDQsXG4gIFwidHJpYW5nbGUgc3RyaXBcIjogNSxcbiAgXCJ0cmlhbmdsZSBmYW5cIjogNlxufVxuIiwibW9kdWxlLmV4cG9ydHM9e1xuICBcInN0YXRpY1wiOiAzNTA0NCxcbiAgXCJkeW5hbWljXCI6IDM1MDQ4LFxuICBcInN0cmVhbVwiOiAzNTA0MFxufVxuIiwidmFyIGNoZWNrID0gcmVxdWlyZSgnLi91dGlsL2NoZWNrJylcbnZhciBjcmVhdGVFbnZpcm9ubWVudCA9IHJlcXVpcmUoJy4vdXRpbC9jb2RlZ2VuJylcbnZhciBsb29wID0gcmVxdWlyZSgnLi91dGlsL2xvb3AnKVxudmFyIGlzVHlwZWRBcnJheSA9IHJlcXVpcmUoJy4vdXRpbC9pcy10eXBlZC1hcnJheScpXG52YXIgaXNOREFycmF5ID0gcmVxdWlyZSgnLi91dGlsL2lzLW5kYXJyYXknKVxudmFyIGlzQXJyYXlMaWtlID0gcmVxdWlyZSgnLi91dGlsL2lzLWFycmF5LWxpa2UnKVxuXG52YXIgcHJpbVR5cGVzID0gcmVxdWlyZSgnLi9jb25zdGFudHMvcHJpbWl0aXZlcy5qc29uJylcbnZhciBnbFR5cGVzID0gcmVxdWlyZSgnLi9jb25zdGFudHMvZHR5cGVzLmpzb24nKVxuXG4vLyBcImN1dGVcIiBuYW1lcyBmb3IgdmVjdG9yIGNvbXBvbmVudHNcbnZhciBDVVRFX0NPTVBPTkVOVFMgPSAneHl6dycuc3BsaXQoJycpXG5cbnZhciBHTF9VTlNJR05FRF9CWVRFID0gNTEyMVxuXG52YXIgQVRUUklCX1NUQVRFX1BPSU5URVIgPSAxXG52YXIgQVRUUklCX1NUQVRFX0NPTlNUQU5UID0gMlxuXG52YXIgRFlOX0ZVTkMgPSAwXG52YXIgRFlOX1BST1AgPSAxXG52YXIgRFlOX0NPTlRFWFQgPSAyXG52YXIgRFlOX1NUQVRFID0gM1xuXG52YXIgU19ESVRIRVIgPSAnZGl0aGVyJ1xudmFyIFNfQkxFTkRfRU5BQkxFID0gJ2JsZW5kLmVuYWJsZSdcbnZhciBTX0JMRU5EX0NPTE9SID0gJ2JsZW5kLmNvbG9yJ1xudmFyIFNfQkxFTkRfRVFVQVRJT04gPSAnYmxlbmQuZXF1YXRpb24nXG52YXIgU19CTEVORF9GVU5DID0gJ2JsZW5kLmZ1bmMnXG52YXIgU19ERVBUSF9FTkFCTEUgPSAnZGVwdGguZW5hYmxlJ1xudmFyIFNfREVQVEhfRlVOQyA9ICdkZXB0aC5mdW5jJ1xudmFyIFNfREVQVEhfUkFOR0UgPSAnZGVwdGgucmFuZ2UnXG52YXIgU19ERVBUSF9NQVNLID0gJ2RlcHRoLm1hc2snXG52YXIgU19DT0xPUl9NQVNLID0gJ2NvbG9yTWFzaydcbnZhciBTX0NVTExfRU5BQkxFID0gJ2N1bGwuZW5hYmxlJ1xudmFyIFNfQ1VMTF9GQUNFID0gJ2N1bGwuZmFjZSdcbnZhciBTX0ZST05UX0ZBQ0UgPSAnZnJvbnRGYWNlJ1xudmFyIFNfTElORV9XSURUSCA9ICdsaW5lV2lkdGgnXG52YXIgU19QT0xZR09OX09GRlNFVF9FTkFCTEUgPSAncG9seWdvbk9mZnNldC5lbmFibGUnXG52YXIgU19QT0xZR09OX09GRlNFVF9PRkZTRVQgPSAncG9seWdvbk9mZnNldC5vZmZzZXQnXG52YXIgU19TQU1QTEVfQUxQSEEgPSAnc2FtcGxlLmFscGhhJ1xudmFyIFNfU0FNUExFX0VOQUJMRSA9ICdzYW1wbGUuZW5hYmxlJ1xudmFyIFNfU0FNUExFX0NPVkVSQUdFID0gJ3NhbXBsZS5jb3ZlcmFnZSdcbnZhciBTX1NURU5DSUxfRU5BQkxFID0gJ3N0ZW5jaWwuZW5hYmxlJ1xudmFyIFNfU1RFTkNJTF9NQVNLID0gJ3N0ZW5jaWwubWFzaydcbnZhciBTX1NURU5DSUxfRlVOQyA9ICdzdGVuY2lsLmZ1bmMnXG52YXIgU19TVEVOQ0lMX09QRlJPTlQgPSAnc3RlbmNpbC5vcEZyb250J1xudmFyIFNfU1RFTkNJTF9PUEJBQ0sgPSAnc3RlbmNpbC5vcEJhY2snXG52YXIgU19TQ0lTU09SX0VOQUJMRSA9ICdzY2lzc29yLmVuYWJsZSdcbnZhciBTX1NDSVNTT1JfQk9YID0gJ3NjaXNzb3IuYm94J1xudmFyIFNfVklFV1BPUlQgPSAndmlld3BvcnQnXG5cbnZhciBTX1BST0ZJTEUgPSAncHJvZmlsZSdcblxudmFyIFNfRlJBTUVCVUZGRVIgPSAnZnJhbWVidWZmZXInXG52YXIgU19WRVJUID0gJ3ZlcnQnXG52YXIgU19GUkFHID0gJ2ZyYWcnXG52YXIgU19FTEVNRU5UUyA9ICdlbGVtZW50cydcbnZhciBTX1BSSU1JVElWRSA9ICdwcmltaXRpdmUnXG52YXIgU19DT1VOVCA9ICdjb3VudCdcbnZhciBTX09GRlNFVCA9ICdvZmZzZXQnXG52YXIgU19JTlNUQU5DRVMgPSAnaW5zdGFuY2VzJ1xuXG52YXIgU1VGRklYX1dJRFRIID0gJ1dpZHRoJ1xudmFyIFNVRkZJWF9IRUlHSFQgPSAnSGVpZ2h0J1xuXG52YXIgU19GUkFNRUJVRkZFUl9XSURUSCA9IFNfRlJBTUVCVUZGRVIgKyBTVUZGSVhfV0lEVEhcbnZhciBTX0ZSQU1FQlVGRkVSX0hFSUdIVCA9IFNfRlJBTUVCVUZGRVIgKyBTVUZGSVhfSEVJR0hUXG52YXIgU19WSUVXUE9SVF9XSURUSCA9IFNfVklFV1BPUlQgKyBTVUZGSVhfV0lEVEhcbnZhciBTX1ZJRVdQT1JUX0hFSUdIVCA9IFNfVklFV1BPUlQgKyBTVUZGSVhfSEVJR0hUXG52YXIgU19EUkFXSU5HQlVGRkVSID0gJ2RyYXdpbmdCdWZmZXInXG52YXIgU19EUkFXSU5HQlVGRkVSX1dJRFRIID0gU19EUkFXSU5HQlVGRkVSICsgU1VGRklYX1dJRFRIXG52YXIgU19EUkFXSU5HQlVGRkVSX0hFSUdIVCA9IFNfRFJBV0lOR0JVRkZFUiArIFNVRkZJWF9IRUlHSFRcblxudmFyIEdMX0FSUkFZX0JVRkZFUiA9IDM0OTYyXG52YXIgR0xfRUxFTUVOVF9BUlJBWV9CVUZGRVIgPSAzNDk2M1xuXG52YXIgR0xfRlJBR01FTlRfU0hBREVSID0gMzU2MzJcbnZhciBHTF9WRVJURVhfU0hBREVSID0gMzU2MzNcblxudmFyIEdMX1RFWFRVUkVfMkQgPSAweDBERTFcbnZhciBHTF9URVhUVVJFX0NVQkVfTUFQID0gMHg4NTEzXG5cbnZhciBHTF9DVUxMX0ZBQ0UgPSAweDBCNDRcbnZhciBHTF9CTEVORCA9IDB4MEJFMlxudmFyIEdMX0RJVEhFUiA9IDB4MEJEMFxudmFyIEdMX1NURU5DSUxfVEVTVCA9IDB4MEI5MFxudmFyIEdMX0RFUFRIX1RFU1QgPSAweDBCNzFcbnZhciBHTF9TQ0lTU09SX1RFU1QgPSAweDBDMTFcbnZhciBHTF9QT0xZR09OX09GRlNFVF9GSUxMID0gMHg4MDM3XG52YXIgR0xfU0FNUExFX0FMUEhBX1RPX0NPVkVSQUdFID0gMHg4MDlFXG52YXIgR0xfU0FNUExFX0NPVkVSQUdFID0gMHg4MEEwXG5cbnZhciBHTF9GTE9BVCA9IDUxMjZcbnZhciBHTF9GTE9BVF9WRUMyID0gMzU2NjRcbnZhciBHTF9GTE9BVF9WRUMzID0gMzU2NjVcbnZhciBHTF9GTE9BVF9WRUM0ID0gMzU2NjZcbnZhciBHTF9JTlQgPSA1MTI0XG52YXIgR0xfSU5UX1ZFQzIgPSAzNTY2N1xudmFyIEdMX0lOVF9WRUMzID0gMzU2NjhcbnZhciBHTF9JTlRfVkVDNCA9IDM1NjY5XG52YXIgR0xfQk9PTCA9IDM1NjcwXG52YXIgR0xfQk9PTF9WRUMyID0gMzU2NzFcbnZhciBHTF9CT09MX1ZFQzMgPSAzNTY3MlxudmFyIEdMX0JPT0xfVkVDNCA9IDM1NjczXG52YXIgR0xfRkxPQVRfTUFUMiA9IDM1Njc0XG52YXIgR0xfRkxPQVRfTUFUMyA9IDM1Njc1XG52YXIgR0xfRkxPQVRfTUFUNCA9IDM1Njc2XG52YXIgR0xfU0FNUExFUl8yRCA9IDM1Njc4XG52YXIgR0xfU0FNUExFUl9DVUJFID0gMzU2ODBcblxudmFyIEdMX1RSSUFOR0xFUyA9IDRcblxudmFyIEdMX0ZST05UID0gMTAyOFxudmFyIEdMX0JBQ0sgPSAxMDI5XG52YXIgR0xfQ1cgPSAweDA5MDBcbnZhciBHTF9DQ1cgPSAweDA5MDFcbnZhciBHTF9NSU5fRVhUID0gMHg4MDA3XG52YXIgR0xfTUFYX0VYVCA9IDB4ODAwOFxudmFyIEdMX0FMV0FZUyA9IDUxOVxudmFyIEdMX0tFRVAgPSA3NjgwXG52YXIgR0xfWkVSTyA9IDBcbnZhciBHTF9PTkUgPSAxXG52YXIgR0xfRlVOQ19BREQgPSAweDgwMDZcbnZhciBHTF9MRVNTID0gNTEzXG5cbnZhciBHTF9GUkFNRUJVRkZFUiA9IDB4OEQ0MFxudmFyIEdMX0NPTE9SX0FUVEFDSE1FTlQwID0gMHg4Q0UwXG5cbnZhciBibGVuZEZ1bmNzID0ge1xuICAnMCc6IDAsXG4gICcxJzogMSxcbiAgJ3plcm8nOiAwLFxuICAnb25lJzogMSxcbiAgJ3NyYyBjb2xvcic6IDc2OCxcbiAgJ29uZSBtaW51cyBzcmMgY29sb3InOiA3NjksXG4gICdzcmMgYWxwaGEnOiA3NzAsXG4gICdvbmUgbWludXMgc3JjIGFscGhhJzogNzcxLFxuICAnZHN0IGNvbG9yJzogNzc0LFxuICAnb25lIG1pbnVzIGRzdCBjb2xvcic6IDc3NSxcbiAgJ2RzdCBhbHBoYSc6IDc3MixcbiAgJ29uZSBtaW51cyBkc3QgYWxwaGEnOiA3NzMsXG4gICdjb25zdGFudCBjb2xvcic6IDMyNzY5LFxuICAnb25lIG1pbnVzIGNvbnN0YW50IGNvbG9yJzogMzI3NzAsXG4gICdjb25zdGFudCBhbHBoYSc6IDMyNzcxLFxuICAnb25lIG1pbnVzIGNvbnN0YW50IGFscGhhJzogMzI3NzIsXG4gICdzcmMgYWxwaGEgc2F0dXJhdGUnOiA3NzZcbn1cblxudmFyIGNvbXBhcmVGdW5jcyA9IHtcbiAgJ25ldmVyJzogNTEyLFxuICAnbGVzcyc6IDUxMyxcbiAgJzwnOiA1MTMsXG4gICdlcXVhbCc6IDUxNCxcbiAgJz0nOiA1MTQsXG4gICc9PSc6IDUxNCxcbiAgJz09PSc6IDUxNCxcbiAgJ2xlcXVhbCc6IDUxNSxcbiAgJzw9JzogNTE1LFxuICAnZ3JlYXRlcic6IDUxNixcbiAgJz4nOiA1MTYsXG4gICdub3RlcXVhbCc6IDUxNyxcbiAgJyE9JzogNTE3LFxuICAnIT09JzogNTE3LFxuICAnZ2VxdWFsJzogNTE4LFxuICAnPj0nOiA1MTgsXG4gICdhbHdheXMnOiA1MTlcbn1cblxudmFyIHN0ZW5jaWxPcHMgPSB7XG4gICcwJzogMCxcbiAgJ3plcm8nOiAwLFxuICAna2VlcCc6IDc2ODAsXG4gICdyZXBsYWNlJzogNzY4MSxcbiAgJ2luY3JlbWVudCc6IDc2ODIsXG4gICdkZWNyZW1lbnQnOiA3NjgzLFxuICAnaW5jcmVtZW50IHdyYXAnOiAzNDA1NSxcbiAgJ2RlY3JlbWVudCB3cmFwJzogMzQwNTYsXG4gICdpbnZlcnQnOiA1Mzg2XG59XG5cbnZhciBzaGFkZXJUeXBlID0ge1xuICAnZnJhZyc6IEdMX0ZSQUdNRU5UX1NIQURFUixcbiAgJ3ZlcnQnOiBHTF9WRVJURVhfU0hBREVSXG59XG5cbnZhciBvcmllbnRhdGlvblR5cGUgPSB7XG4gICdjdyc6IEdMX0NXLFxuICAnY2N3JzogR0xfQ0NXXG59XG5cbmZ1bmN0aW9uIGlzQnVmZmVyQXJncyAoeCkge1xuICByZXR1cm4gQXJyYXkuaXNBcnJheSh4KSB8fFxuICAgIGlzVHlwZWRBcnJheSh4KSB8fFxuICAgIGlzTkRBcnJheSh4KVxufVxuXG4vLyBNYWtlIHN1cmUgdmlld3BvcnQgaXMgcHJvY2Vzc2VkIGZpcnN0XG5mdW5jdGlvbiBzb3J0U3RhdGUgKHN0YXRlKSB7XG4gIHJldHVybiBzdGF0ZS5zb3J0KGZ1bmN0aW9uIChhLCBiKSB7XG4gICAgaWYgKGEgPT09IFNfVklFV1BPUlQpIHtcbiAgICAgIHJldHVybiAtMVxuICAgIH0gZWxzZSBpZiAoYiA9PT0gU19WSUVXUE9SVCkge1xuICAgICAgcmV0dXJuIDFcbiAgICB9XG4gICAgcmV0dXJuIChhIDwgYikgPyAtMSA6IDFcbiAgfSlcbn1cblxuZnVuY3Rpb24gRGVjbGFyYXRpb24gKHRoaXNEZXAsIGNvbnRleHREZXAsIHByb3BEZXAsIGFwcGVuZCkge1xuICB0aGlzLnRoaXNEZXAgPSB0aGlzRGVwXG4gIHRoaXMuY29udGV4dERlcCA9IGNvbnRleHREZXBcbiAgdGhpcy5wcm9wRGVwID0gcHJvcERlcFxuICB0aGlzLmFwcGVuZCA9IGFwcGVuZFxufVxuXG5mdW5jdGlvbiBpc1N0YXRpYyAoZGVjbCkge1xuICByZXR1cm4gZGVjbCAmJiAhKGRlY2wudGhpc0RlcCB8fCBkZWNsLmNvbnRleHREZXAgfHwgZGVjbC5wcm9wRGVwKVxufVxuXG5mdW5jdGlvbiBjcmVhdGVTdGF0aWNEZWNsIChhcHBlbmQpIHtcbiAgcmV0dXJuIG5ldyBEZWNsYXJhdGlvbihmYWxzZSwgZmFsc2UsIGZhbHNlLCBhcHBlbmQpXG59XG5cbmZ1bmN0aW9uIGNyZWF0ZUR5bmFtaWNEZWNsIChkeW4sIGFwcGVuZCkge1xuICB2YXIgdHlwZSA9IGR5bi50eXBlXG4gIGlmICh0eXBlID09PSBEWU5fRlVOQykge1xuICAgIHZhciBudW1BcmdzID0gZHluLmRhdGEubGVuZ3RoXG4gICAgcmV0dXJuIG5ldyBEZWNsYXJhdGlvbihcbiAgICAgIHRydWUsXG4gICAgICBudW1BcmdzID49IDEsXG4gICAgICBudW1BcmdzID49IDIsXG4gICAgICBhcHBlbmQpXG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIG5ldyBEZWNsYXJhdGlvbihcbiAgICAgIHR5cGUgPT09IERZTl9TVEFURSxcbiAgICAgIHR5cGUgPT09IERZTl9DT05URVhULFxuICAgICAgdHlwZSA9PT0gRFlOX1BST1AsXG4gICAgICBhcHBlbmQpXG4gIH1cbn1cblxudmFyIFNDT1BFX0RFQ0wgPSBuZXcgRGVjbGFyYXRpb24oZmFsc2UsIGZhbHNlLCBmYWxzZSwgZnVuY3Rpb24gKCkge30pXG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gcmVnbENvcmUgKFxuICBnbCxcbiAgc3RyaW5nU3RvcmUsXG4gIGV4dGVuc2lvbnMsXG4gIGxpbWl0cyxcbiAgYnVmZmVyU3RhdGUsXG4gIGVsZW1lbnRTdGF0ZSxcbiAgdGV4dHVyZVN0YXRlLFxuICBmcmFtZWJ1ZmZlclN0YXRlLFxuICB1bmlmb3JtU3RhdGUsXG4gIGF0dHJpYnV0ZVN0YXRlLFxuICBzaGFkZXJTdGF0ZSxcbiAgZHJhd1N0YXRlLFxuICBjb250ZXh0U3RhdGUsXG4gIHRpbWVyLFxuICBjb25maWcpIHtcbiAgdmFyIEF0dHJpYnV0ZVJlY29yZCA9IGF0dHJpYnV0ZVN0YXRlLlJlY29yZFxuXG4gIHZhciBibGVuZEVxdWF0aW9ucyA9IHtcbiAgICAnYWRkJzogMzI3NzQsXG4gICAgJ3N1YnRyYWN0JzogMzI3NzgsXG4gICAgJ3JldmVyc2Ugc3VidHJhY3QnOiAzMjc3OVxuICB9XG4gIGlmIChleHRlbnNpb25zLmV4dF9ibGVuZF9taW5tYXgpIHtcbiAgICBibGVuZEVxdWF0aW9ucy5taW4gPSBHTF9NSU5fRVhUXG4gICAgYmxlbmRFcXVhdGlvbnMubWF4ID0gR0xfTUFYX0VYVFxuICB9XG5cbiAgdmFyIGV4dEluc3RhbmNpbmcgPSBleHRlbnNpb25zLmFuZ2xlX2luc3RhbmNlZF9hcnJheXNcbiAgdmFyIGV4dERyYXdCdWZmZXJzID0gZXh0ZW5zaW9ucy53ZWJnbF9kcmF3X2J1ZmZlcnNcblxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vIFdFQkdMIFNUQVRFXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgdmFyIGN1cnJlbnRTdGF0ZSA9IHtcbiAgICBkaXJ0eTogdHJ1ZSxcbiAgICBwcm9maWxlOiBjb25maWcucHJvZmlsZVxuICB9XG4gIHZhciBuZXh0U3RhdGUgPSB7fVxuICB2YXIgR0xfU1RBVEVfTkFNRVMgPSBbXVxuICB2YXIgR0xfRkxBR1MgPSB7fVxuICB2YXIgR0xfVkFSSUFCTEVTID0ge31cblxuICBmdW5jdGlvbiBwcm9wTmFtZSAobmFtZSkge1xuICAgIHJldHVybiBuYW1lLnJlcGxhY2UoJy4nLCAnXycpXG4gIH1cblxuICBmdW5jdGlvbiBzdGF0ZUZsYWcgKHNuYW1lLCBjYXAsIGluaXQpIHtcbiAgICB2YXIgbmFtZSA9IHByb3BOYW1lKHNuYW1lKVxuICAgIEdMX1NUQVRFX05BTUVTLnB1c2goc25hbWUpXG4gICAgbmV4dFN0YXRlW25hbWVdID0gY3VycmVudFN0YXRlW25hbWVdID0gISFpbml0XG4gICAgR0xfRkxBR1NbbmFtZV0gPSBjYXBcbiAgfVxuXG4gIGZ1bmN0aW9uIHN0YXRlVmFyaWFibGUgKHNuYW1lLCBmdW5jLCBpbml0KSB7XG4gICAgdmFyIG5hbWUgPSBwcm9wTmFtZShzbmFtZSlcbiAgICBHTF9TVEFURV9OQU1FUy5wdXNoKHNuYW1lKVxuICAgIGlmIChBcnJheS5pc0FycmF5KGluaXQpKSB7XG4gICAgICBjdXJyZW50U3RhdGVbbmFtZV0gPSBpbml0LnNsaWNlKClcbiAgICAgIG5leHRTdGF0ZVtuYW1lXSA9IGluaXQuc2xpY2UoKVxuICAgIH0gZWxzZSB7XG4gICAgICBjdXJyZW50U3RhdGVbbmFtZV0gPSBuZXh0U3RhdGVbbmFtZV0gPSBpbml0XG4gICAgfVxuICAgIEdMX1ZBUklBQkxFU1tuYW1lXSA9IGZ1bmNcbiAgfVxuXG4gIC8vIERpdGhlcmluZ1xuICBzdGF0ZUZsYWcoU19ESVRIRVIsIEdMX0RJVEhFUilcblxuICAvLyBCbGVuZGluZ1xuICBzdGF0ZUZsYWcoU19CTEVORF9FTkFCTEUsIEdMX0JMRU5EKVxuICBzdGF0ZVZhcmlhYmxlKFNfQkxFTkRfQ09MT1IsICdibGVuZENvbG9yJywgWzAsIDAsIDAsIDBdKVxuICBzdGF0ZVZhcmlhYmxlKFNfQkxFTkRfRVFVQVRJT04sICdibGVuZEVxdWF0aW9uU2VwYXJhdGUnLFxuICAgIFtHTF9GVU5DX0FERCwgR0xfRlVOQ19BRERdKVxuICBzdGF0ZVZhcmlhYmxlKFNfQkxFTkRfRlVOQywgJ2JsZW5kRnVuY1NlcGFyYXRlJyxcbiAgICBbR0xfT05FLCBHTF9aRVJPLCBHTF9PTkUsIEdMX1pFUk9dKVxuXG4gIC8vIERlcHRoXG4gIHN0YXRlRmxhZyhTX0RFUFRIX0VOQUJMRSwgR0xfREVQVEhfVEVTVCwgdHJ1ZSlcbiAgc3RhdGVWYXJpYWJsZShTX0RFUFRIX0ZVTkMsICdkZXB0aEZ1bmMnLCBHTF9MRVNTKVxuICBzdGF0ZVZhcmlhYmxlKFNfREVQVEhfUkFOR0UsICdkZXB0aFJhbmdlJywgWzAsIDFdKVxuICBzdGF0ZVZhcmlhYmxlKFNfREVQVEhfTUFTSywgJ2RlcHRoTWFzaycsIHRydWUpXG5cbiAgLy8gQ29sb3IgbWFza1xuICBzdGF0ZVZhcmlhYmxlKFNfQ09MT1JfTUFTSywgU19DT0xPUl9NQVNLLCBbdHJ1ZSwgdHJ1ZSwgdHJ1ZSwgdHJ1ZV0pXG5cbiAgLy8gRmFjZSBjdWxsaW5nXG4gIHN0YXRlRmxhZyhTX0NVTExfRU5BQkxFLCBHTF9DVUxMX0ZBQ0UpXG4gIHN0YXRlVmFyaWFibGUoU19DVUxMX0ZBQ0UsICdjdWxsRmFjZScsIEdMX0JBQ0spXG5cbiAgLy8gRnJvbnQgZmFjZSBvcmllbnRhdGlvblxuICBzdGF0ZVZhcmlhYmxlKFNfRlJPTlRfRkFDRSwgU19GUk9OVF9GQUNFLCBHTF9DQ1cpXG5cbiAgLy8gTGluZSB3aWR0aFxuICBzdGF0ZVZhcmlhYmxlKFNfTElORV9XSURUSCwgU19MSU5FX1dJRFRILCAxKVxuXG4gIC8vIFBvbHlnb24gb2Zmc2V0XG4gIHN0YXRlRmxhZyhTX1BPTFlHT05fT0ZGU0VUX0VOQUJMRSwgR0xfUE9MWUdPTl9PRkZTRVRfRklMTClcbiAgc3RhdGVWYXJpYWJsZShTX1BPTFlHT05fT0ZGU0VUX09GRlNFVCwgJ3BvbHlnb25PZmZzZXQnLCBbMCwgMF0pXG5cbiAgLy8gU2FtcGxlIGNvdmVyYWdlXG4gIHN0YXRlRmxhZyhTX1NBTVBMRV9BTFBIQSwgR0xfU0FNUExFX0FMUEhBX1RPX0NPVkVSQUdFKVxuICBzdGF0ZUZsYWcoU19TQU1QTEVfRU5BQkxFLCBHTF9TQU1QTEVfQ09WRVJBR0UpXG4gIHN0YXRlVmFyaWFibGUoU19TQU1QTEVfQ09WRVJBR0UsICdzYW1wbGVDb3ZlcmFnZScsIFsxLCBmYWxzZV0pXG5cbiAgLy8gU3RlbmNpbFxuICBzdGF0ZUZsYWcoU19TVEVOQ0lMX0VOQUJMRSwgR0xfU1RFTkNJTF9URVNUKVxuICBzdGF0ZVZhcmlhYmxlKFNfU1RFTkNJTF9NQVNLLCAnc3RlbmNpbE1hc2snLCAtMSlcbiAgc3RhdGVWYXJpYWJsZShTX1NURU5DSUxfRlVOQywgJ3N0ZW5jaWxGdW5jJywgW0dMX0FMV0FZUywgMCwgLTFdKVxuICBzdGF0ZVZhcmlhYmxlKFNfU1RFTkNJTF9PUEZST05ULCAnc3RlbmNpbE9wU2VwYXJhdGUnLFxuICAgIFtHTF9GUk9OVCwgR0xfS0VFUCwgR0xfS0VFUCwgR0xfS0VFUF0pXG4gIHN0YXRlVmFyaWFibGUoU19TVEVOQ0lMX09QQkFDSywgJ3N0ZW5jaWxPcFNlcGFyYXRlJyxcbiAgICBbR0xfQkFDSywgR0xfS0VFUCwgR0xfS0VFUCwgR0xfS0VFUF0pXG5cbiAgLy8gU2Npc3NvclxuICBzdGF0ZUZsYWcoU19TQ0lTU09SX0VOQUJMRSwgR0xfU0NJU1NPUl9URVNUKVxuICBzdGF0ZVZhcmlhYmxlKFNfU0NJU1NPUl9CT1gsICdzY2lzc29yJyxcbiAgICBbMCwgMCwgZ2wuZHJhd2luZ0J1ZmZlcldpZHRoLCBnbC5kcmF3aW5nQnVmZmVySGVpZ2h0XSlcblxuICAvLyBWaWV3cG9ydFxuICBzdGF0ZVZhcmlhYmxlKFNfVklFV1BPUlQsIFNfVklFV1BPUlQsXG4gICAgWzAsIDAsIGdsLmRyYXdpbmdCdWZmZXJXaWR0aCwgZ2wuZHJhd2luZ0J1ZmZlckhlaWdodF0pXG5cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBFTlZJUk9OTUVOVFxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIHZhciBzaGFyZWRTdGF0ZSA9IHtcbiAgICBnbDogZ2wsXG4gICAgY29udGV4dDogY29udGV4dFN0YXRlLFxuICAgIHN0cmluZ3M6IHN0cmluZ1N0b3JlLFxuICAgIG5leHQ6IG5leHRTdGF0ZSxcbiAgICBjdXJyZW50OiBjdXJyZW50U3RhdGUsXG4gICAgZHJhdzogZHJhd1N0YXRlLFxuICAgIGVsZW1lbnRzOiBlbGVtZW50U3RhdGUsXG4gICAgYnVmZmVyOiBidWZmZXJTdGF0ZSxcbiAgICBzaGFkZXI6IHNoYWRlclN0YXRlLFxuICAgIGF0dHJpYnV0ZXM6IGF0dHJpYnV0ZVN0YXRlLnN0YXRlLFxuICAgIHVuaWZvcm1zOiB1bmlmb3JtU3RhdGUsXG4gICAgZnJhbWVidWZmZXI6IGZyYW1lYnVmZmVyU3RhdGUsXG4gICAgZXh0ZW5zaW9uczogZXh0ZW5zaW9ucyxcblxuICAgIHRpbWVyOiB0aW1lcixcbiAgICBpc0J1ZmZlckFyZ3M6IGlzQnVmZmVyQXJnc1xuICB9XG5cbiAgdmFyIHNoYXJlZENvbnN0YW50cyA9IHtcbiAgICBwcmltVHlwZXM6IHByaW1UeXBlcyxcbiAgICBjb21wYXJlRnVuY3M6IGNvbXBhcmVGdW5jcyxcbiAgICBibGVuZEZ1bmNzOiBibGVuZEZ1bmNzLFxuICAgIGJsZW5kRXF1YXRpb25zOiBibGVuZEVxdWF0aW9ucyxcbiAgICBzdGVuY2lsT3BzOiBzdGVuY2lsT3BzLFxuICAgIGdsVHlwZXM6IGdsVHlwZXMsXG4gICAgb3JpZW50YXRpb25UeXBlOiBvcmllbnRhdGlvblR5cGVcbiAgfVxuXG4gIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICBzaGFyZWRTdGF0ZS5pc0FycmF5TGlrZSA9IGlzQXJyYXlMaWtlXG4gIH0pXG5cbiAgaWYgKGV4dERyYXdCdWZmZXJzKSB7XG4gICAgc2hhcmVkQ29uc3RhbnRzLmJhY2tCdWZmZXIgPSBbR0xfQkFDS11cbiAgICBzaGFyZWRDb25zdGFudHMuZHJhd0J1ZmZlciA9IGxvb3AobGltaXRzLm1heERyYXdidWZmZXJzLCBmdW5jdGlvbiAoaSkge1xuICAgICAgaWYgKGkgPT09IDApIHtcbiAgICAgICAgcmV0dXJuIFswXVxuICAgICAgfVxuICAgICAgcmV0dXJuIGxvb3AoaSwgZnVuY3Rpb24gKGopIHtcbiAgICAgICAgcmV0dXJuIEdMX0NPTE9SX0FUVEFDSE1FTlQwICsgalxuICAgICAgfSlcbiAgICB9KVxuICB9XG5cbiAgdmFyIGRyYXdDYWxsQ291bnRlciA9IDBcbiAgZnVuY3Rpb24gY3JlYXRlUkVHTEVudmlyb25tZW50ICgpIHtcbiAgICB2YXIgZW52ID0gY3JlYXRlRW52aXJvbm1lbnQoKVxuICAgIHZhciBsaW5rID0gZW52LmxpbmtcbiAgICB2YXIgZ2xvYmFsID0gZW52Lmdsb2JhbFxuICAgIGVudi5pZCA9IGRyYXdDYWxsQ291bnRlcisrXG5cbiAgICBlbnYuYmF0Y2hJZCA9ICcwJ1xuXG4gICAgLy8gbGluayBzaGFyZWQgc3RhdGVcbiAgICB2YXIgU0hBUkVEID0gbGluayhzaGFyZWRTdGF0ZSlcbiAgICB2YXIgc2hhcmVkID0gZW52LnNoYXJlZCA9IHtcbiAgICAgIHByb3BzOiAnYTAnXG4gICAgfVxuICAgIE9iamVjdC5rZXlzKHNoYXJlZFN0YXRlKS5mb3JFYWNoKGZ1bmN0aW9uIChwcm9wKSB7XG4gICAgICBzaGFyZWRbcHJvcF0gPSBnbG9iYWwuZGVmKFNIQVJFRCwgJy4nLCBwcm9wKVxuICAgIH0pXG5cbiAgICAvLyBJbmplY3QgcnVudGltZSBhc3NlcnRpb24gc3R1ZmYgZm9yIGRlYnVnIGJ1aWxkc1xuICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgIGVudi5DSEVDSyA9IGxpbmsoY2hlY2spXG4gICAgICBlbnYuY29tbWFuZFN0ciA9IGNoZWNrLmd1ZXNzQ29tbWFuZCgpXG4gICAgICBlbnYuY29tbWFuZCA9IGxpbmsoZW52LmNvbW1hbmRTdHIpXG4gICAgICBlbnYuYXNzZXJ0ID0gZnVuY3Rpb24gKGJsb2NrLCBwcmVkLCBtZXNzYWdlKSB7XG4gICAgICAgIGJsb2NrKFxuICAgICAgICAgICdpZighKCcsIHByZWQsICcpKScsXG4gICAgICAgICAgdGhpcy5DSEVDSywgJy5jb21tYW5kUmFpc2UoJywgbGluayhtZXNzYWdlKSwgJywnLCB0aGlzLmNvbW1hbmQsICcpOycpXG4gICAgICB9XG4gICAgfSlcblxuICAgIC8vIENvcHkgR0wgc3RhdGUgdmFyaWFibGVzIG92ZXJcbiAgICB2YXIgbmV4dFZhcnMgPSBlbnYubmV4dCA9IHt9XG4gICAgdmFyIGN1cnJlbnRWYXJzID0gZW52LmN1cnJlbnQgPSB7fVxuICAgIE9iamVjdC5rZXlzKEdMX1ZBUklBQkxFUykuZm9yRWFjaChmdW5jdGlvbiAodmFyaWFibGUpIHtcbiAgICAgIGlmIChBcnJheS5pc0FycmF5KGN1cnJlbnRTdGF0ZVt2YXJpYWJsZV0pKSB7XG4gICAgICAgIG5leHRWYXJzW3ZhcmlhYmxlXSA9IGdsb2JhbC5kZWYoc2hhcmVkLm5leHQsICcuJywgdmFyaWFibGUpXG4gICAgICAgIGN1cnJlbnRWYXJzW3ZhcmlhYmxlXSA9IGdsb2JhbC5kZWYoc2hhcmVkLmN1cnJlbnQsICcuJywgdmFyaWFibGUpXG4gICAgICB9XG4gICAgfSlcblxuICAgIC8vIEluaXRpYWxpemUgc2hhcmVkIGNvbnN0YW50c1xuICAgIHZhciBjb25zdGFudHMgPSBlbnYuY29uc3RhbnRzID0ge31cbiAgICBPYmplY3Qua2V5cyhzaGFyZWRDb25zdGFudHMpLmZvckVhY2goZnVuY3Rpb24gKG5hbWUpIHtcbiAgICAgIGNvbnN0YW50c1tuYW1lXSA9IGdsb2JhbC5kZWYoSlNPTi5zdHJpbmdpZnkoc2hhcmVkQ29uc3RhbnRzW25hbWVdKSlcbiAgICB9KVxuXG4gICAgLy8gSGVscGVyIGZ1bmN0aW9uIGZvciBjYWxsaW5nIGEgYmxvY2tcbiAgICBlbnYuaW52b2tlID0gZnVuY3Rpb24gKGJsb2NrLCB4KSB7XG4gICAgICBzd2l0Y2ggKHgudHlwZSkge1xuICAgICAgICBjYXNlIERZTl9GVU5DOlxuICAgICAgICAgIHZhciBhcmdMaXN0ID0gW1xuICAgICAgICAgICAgJ3RoaXMnLFxuICAgICAgICAgICAgc2hhcmVkLmNvbnRleHQsXG4gICAgICAgICAgICBzaGFyZWQucHJvcHMsXG4gICAgICAgICAgICBlbnYuYmF0Y2hJZFxuICAgICAgICAgIF1cbiAgICAgICAgICByZXR1cm4gYmxvY2suZGVmKFxuICAgICAgICAgICAgbGluayh4LmRhdGEpLCAnLmNhbGwoJyxcbiAgICAgICAgICAgICAgYXJnTGlzdC5zbGljZSgwLCBNYXRoLm1heCh4LmRhdGEubGVuZ3RoICsgMSwgNCkpLFxuICAgICAgICAgICAgICcpJylcbiAgICAgICAgY2FzZSBEWU5fUFJPUDpcbiAgICAgICAgICByZXR1cm4gYmxvY2suZGVmKHNoYXJlZC5wcm9wcywgeC5kYXRhKVxuICAgICAgICBjYXNlIERZTl9DT05URVhUOlxuICAgICAgICAgIHJldHVybiBibG9jay5kZWYoc2hhcmVkLmNvbnRleHQsIHguZGF0YSlcbiAgICAgICAgY2FzZSBEWU5fU1RBVEU6XG4gICAgICAgICAgcmV0dXJuIGJsb2NrLmRlZigndGhpcycsIHguZGF0YSlcbiAgICAgIH1cbiAgICB9XG5cbiAgICBlbnYuYXR0cmliQ2FjaGUgPSB7fVxuXG4gICAgdmFyIHNjb3BlQXR0cmlicyA9IHt9XG4gICAgZW52LnNjb3BlQXR0cmliID0gZnVuY3Rpb24gKG5hbWUpIHtcbiAgICAgIHZhciBpZCA9IHN0cmluZ1N0b3JlLmlkKG5hbWUpXG4gICAgICBpZiAoaWQgaW4gc2NvcGVBdHRyaWJzKSB7XG4gICAgICAgIHJldHVybiBzY29wZUF0dHJpYnNbaWRdXG4gICAgICB9XG4gICAgICB2YXIgYmluZGluZyA9IGF0dHJpYnV0ZVN0YXRlLnNjb3BlW2lkXVxuICAgICAgaWYgKCFiaW5kaW5nKSB7XG4gICAgICAgIGJpbmRpbmcgPSBhdHRyaWJ1dGVTdGF0ZS5zY29wZVtpZF0gPSBuZXcgQXR0cmlidXRlUmVjb3JkKClcbiAgICAgIH1cbiAgICAgIHZhciByZXN1bHQgPSBzY29wZUF0dHJpYnNbaWRdID0gbGluayhiaW5kaW5nKVxuICAgICAgcmV0dXJuIHJlc3VsdFxuICAgIH1cblxuICAgIHJldHVybiBlbnZcbiAgfVxuXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gUEFSU0lOR1xuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIGZ1bmN0aW9uIHBhcnNlUHJvZmlsZSAob3B0aW9ucykge1xuICAgIHZhciBzdGF0aWNPcHRpb25zID0gb3B0aW9ucy5zdGF0aWNcbiAgICB2YXIgZHluYW1pY09wdGlvbnMgPSBvcHRpb25zLmR5bmFtaWNcblxuICAgIHZhciBwcm9maWxlRW5hYmxlXG4gICAgaWYgKFNfUFJPRklMRSBpbiBzdGF0aWNPcHRpb25zKSB7XG4gICAgICB2YXIgdmFsdWUgPSAhIXN0YXRpY09wdGlvbnNbU19QUk9GSUxFXVxuICAgICAgcHJvZmlsZUVuYWJsZSA9IGNyZWF0ZVN0YXRpY0RlY2woZnVuY3Rpb24gKGVudiwgc2NvcGUpIHtcbiAgICAgICAgcmV0dXJuIHZhbHVlXG4gICAgICB9KVxuICAgICAgcHJvZmlsZUVuYWJsZS5lbmFibGUgPSB2YWx1ZVxuICAgIH0gZWxzZSBpZiAoU19QUk9GSUxFIGluIGR5bmFtaWNPcHRpb25zKSB7XG4gICAgICB2YXIgZHluID0gZHluYW1pY09wdGlvbnNbU19QUk9GSUxFXVxuICAgICAgcHJvZmlsZUVuYWJsZSA9IGNyZWF0ZUR5bmFtaWNEZWNsKGR5biwgZnVuY3Rpb24gKGVudiwgc2NvcGUpIHtcbiAgICAgICAgcmV0dXJuIGVudi5pbnZva2Uoc2NvcGUsIGR5bilcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgcmV0dXJuIHByb2ZpbGVFbmFibGVcbiAgfVxuXG4gIGZ1bmN0aW9uIHBhcnNlRnJhbWVidWZmZXIgKG9wdGlvbnMpIHtcbiAgICB2YXIgc3RhdGljT3B0aW9ucyA9IG9wdGlvbnMuc3RhdGljXG4gICAgdmFyIGR5bmFtaWNPcHRpb25zID0gb3B0aW9ucy5keW5hbWljXG5cbiAgICBpZiAoU19GUkFNRUJVRkZFUiBpbiBzdGF0aWNPcHRpb25zKSB7XG4gICAgICB2YXIgZnJhbWVidWZmZXIgPSBzdGF0aWNPcHRpb25zW1NfRlJBTUVCVUZGRVJdXG4gICAgICBpZiAoZnJhbWVidWZmZXIpIHtcbiAgICAgICAgZnJhbWVidWZmZXIgPSBmcmFtZWJ1ZmZlclN0YXRlLmdldEZyYW1lYnVmZmVyKGZyYW1lYnVmZmVyKVxuICAgICAgICBjaGVjay5jb21tYW5kKGZyYW1lYnVmZmVyLCAnaW52YWxpZCBmcmFtZWJ1ZmZlciBvYmplY3QnKVxuICAgICAgICByZXR1cm4gY3JlYXRlU3RhdGljRGVjbChmdW5jdGlvbiAoZW52LCBibG9jaykge1xuICAgICAgICAgIHZhciBGUkFNRUJVRkZFUiA9IGVudi5saW5rKGZyYW1lYnVmZmVyKVxuICAgICAgICAgIHZhciBzaGFyZWQgPSBlbnYuc2hhcmVkXG4gICAgICAgICAgYmxvY2suc2V0KFxuICAgICAgICAgICAgc2hhcmVkLmZyYW1lYnVmZmVyLFxuICAgICAgICAgICAgJy5uZXh0JyxcbiAgICAgICAgICAgIEZSQU1FQlVGRkVSKVxuICAgICAgICAgIHZhciBDT05URVhUID0gc2hhcmVkLmNvbnRleHRcbiAgICAgICAgICBibG9jay5zZXQoXG4gICAgICAgICAgICBDT05URVhULFxuICAgICAgICAgICAgJy4nICsgU19GUkFNRUJVRkZFUl9XSURUSCxcbiAgICAgICAgICAgIEZSQU1FQlVGRkVSICsgJy53aWR0aCcpXG4gICAgICAgICAgYmxvY2suc2V0KFxuICAgICAgICAgICAgQ09OVEVYVCxcbiAgICAgICAgICAgICcuJyArIFNfRlJBTUVCVUZGRVJfSEVJR0hULFxuICAgICAgICAgICAgRlJBTUVCVUZGRVIgKyAnLmhlaWdodCcpXG4gICAgICAgICAgcmV0dXJuIEZSQU1FQlVGRkVSXG4gICAgICAgIH0pXG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gY3JlYXRlU3RhdGljRGVjbChmdW5jdGlvbiAoZW52LCBzY29wZSkge1xuICAgICAgICAgIHZhciBzaGFyZWQgPSBlbnYuc2hhcmVkXG4gICAgICAgICAgc2NvcGUuc2V0KFxuICAgICAgICAgICAgc2hhcmVkLmZyYW1lYnVmZmVyLFxuICAgICAgICAgICAgJy5uZXh0JyxcbiAgICAgICAgICAgICdudWxsJylcbiAgICAgICAgICB2YXIgQ09OVEVYVCA9IHNoYXJlZC5jb250ZXh0XG4gICAgICAgICAgc2NvcGUuc2V0KFxuICAgICAgICAgICAgQ09OVEVYVCxcbiAgICAgICAgICAgICcuJyArIFNfRlJBTUVCVUZGRVJfV0lEVEgsXG4gICAgICAgICAgICBDT05URVhUICsgJy4nICsgU19EUkFXSU5HQlVGRkVSX1dJRFRIKVxuICAgICAgICAgIHNjb3BlLnNldChcbiAgICAgICAgICAgIENPTlRFWFQsXG4gICAgICAgICAgICAnLicgKyBTX0ZSQU1FQlVGRkVSX0hFSUdIVCxcbiAgICAgICAgICAgIENPTlRFWFQgKyAnLicgKyBTX0RSQVdJTkdCVUZGRVJfSEVJR0hUKVxuICAgICAgICAgIHJldHVybiAnbnVsbCdcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKFNfRlJBTUVCVUZGRVIgaW4gZHluYW1pY09wdGlvbnMpIHtcbiAgICAgIHZhciBkeW4gPSBkeW5hbWljT3B0aW9uc1tTX0ZSQU1FQlVGRkVSXVxuICAgICAgcmV0dXJuIGNyZWF0ZUR5bmFtaWNEZWNsKGR5biwgZnVuY3Rpb24gKGVudiwgc2NvcGUpIHtcbiAgICAgICAgdmFyIEZSQU1FQlVGRkVSX0ZVTkMgPSBlbnYuaW52b2tlKHNjb3BlLCBkeW4pXG4gICAgICAgIHZhciBzaGFyZWQgPSBlbnYuc2hhcmVkXG4gICAgICAgIHZhciBGUkFNRUJVRkZFUl9TVEFURSA9IHNoYXJlZC5mcmFtZWJ1ZmZlclxuICAgICAgICB2YXIgRlJBTUVCVUZGRVIgPSBzY29wZS5kZWYoXG4gICAgICAgICAgRlJBTUVCVUZGRVJfU1RBVEUsICcuZ2V0RnJhbWVidWZmZXIoJywgRlJBTUVCVUZGRVJfRlVOQywgJyknKVxuXG4gICAgICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICBlbnYuYXNzZXJ0KHNjb3BlLFxuICAgICAgICAgICAgJyEnICsgRlJBTUVCVUZGRVJfRlVOQyArICd8fCcgKyBGUkFNRUJVRkZFUixcbiAgICAgICAgICAgICdpbnZhbGlkIGZyYW1lYnVmZmVyIG9iamVjdCcpXG4gICAgICAgIH0pXG5cbiAgICAgICAgc2NvcGUuc2V0KFxuICAgICAgICAgIEZSQU1FQlVGRkVSX1NUQVRFLFxuICAgICAgICAgICcubmV4dCcsXG4gICAgICAgICAgRlJBTUVCVUZGRVIpXG4gICAgICAgIHZhciBDT05URVhUID0gc2hhcmVkLmNvbnRleHRcbiAgICAgICAgc2NvcGUuc2V0KFxuICAgICAgICAgIENPTlRFWFQsXG4gICAgICAgICAgJy4nICsgU19GUkFNRUJVRkZFUl9XSURUSCxcbiAgICAgICAgICBGUkFNRUJVRkZFUiArICc/JyArIEZSQU1FQlVGRkVSICsgJy53aWR0aDonICtcbiAgICAgICAgICBDT05URVhUICsgJy4nICsgU19EUkFXSU5HQlVGRkVSX1dJRFRIKVxuICAgICAgICBzY29wZS5zZXQoXG4gICAgICAgICAgQ09OVEVYVCxcbiAgICAgICAgICAnLicgKyBTX0ZSQU1FQlVGRkVSX0hFSUdIVCxcbiAgICAgICAgICBGUkFNRUJVRkZFUiArXG4gICAgICAgICAgJz8nICsgRlJBTUVCVUZGRVIgKyAnLmhlaWdodDonICtcbiAgICAgICAgICBDT05URVhUICsgJy4nICsgU19EUkFXSU5HQlVGRkVSX0hFSUdIVClcbiAgICAgICAgcmV0dXJuIEZSQU1FQlVGRkVSXG4gICAgICB9KVxuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gbnVsbFxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHBhcnNlVmlld3BvcnRTY2lzc29yIChvcHRpb25zLCBmcmFtZWJ1ZmZlcikge1xuICAgIHZhciBzdGF0aWNPcHRpb25zID0gb3B0aW9ucy5zdGF0aWNcbiAgICB2YXIgZHluYW1pY09wdGlvbnMgPSBvcHRpb25zLmR5bmFtaWNcblxuICAgIGZ1bmN0aW9uIHBhcnNlQm94IChwYXJhbSkge1xuICAgICAgaWYgKHBhcmFtIGluIHN0YXRpY09wdGlvbnMpIHtcbiAgICAgICAgdmFyIGJveCA9IHN0YXRpY09wdGlvbnNbcGFyYW1dXG4gICAgICAgIGNoZWNrLmNvbW1hbmRUeXBlKGJveCwgJ29iamVjdCcsICdpbnZhbGlkICcgKyBwYXJhbSlcblxuICAgICAgICB2YXIgaXNTdGF0aWMgPSB0cnVlXG4gICAgICAgIHZhciB4ID0gYm94LnggfCAwXG4gICAgICAgIHZhciB5ID0gYm94LnkgfCAwXG4gICAgICAgIGNoZWNrLmNvbW1hbmQoeCA+PSAwICYmIHkgPj0gMCwgJ2ludmFsaWQgJyArIHBhcmFtKVxuICAgICAgICB2YXIgdywgaFxuICAgICAgICBpZiAoJ3dpZHRoJyBpbiBib3gpIHtcbiAgICAgICAgICB3ID0gYm94LndpZHRoIHwgMFxuICAgICAgICAgIGNoZWNrLmNvbW1hbmQodyA+IDAsICdpbnZhbGlkICcgKyBwYXJhbSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpc1N0YXRpYyA9IGZhbHNlXG4gICAgICAgIH1cbiAgICAgICAgaWYgKCdoZWlnaHQnIGluIGJveCkge1xuICAgICAgICAgIGggPSBib3guaGVpZ2h0IHwgMFxuICAgICAgICAgIGNoZWNrLmNvbW1hbmQoaCA+IDAsICdpbnZhbGlkICcgKyBwYXJhbSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpc1N0YXRpYyA9IGZhbHNlXG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gbmV3IERlY2xhcmF0aW9uKFxuICAgICAgICAgICFpc1N0YXRpYyAmJiBmcmFtZWJ1ZmZlciAmJiBmcmFtZWJ1ZmZlci50aGlzRGVwLFxuICAgICAgICAgICFpc1N0YXRpYyAmJiBmcmFtZWJ1ZmZlciAmJiBmcmFtZWJ1ZmZlci5jb250ZXh0RGVwLFxuICAgICAgICAgICFpc1N0YXRpYyAmJiBmcmFtZWJ1ZmZlciAmJiBmcmFtZWJ1ZmZlci5wcm9wRGVwLFxuICAgICAgICAgIGZ1bmN0aW9uIChlbnYsIHNjb3BlKSB7XG4gICAgICAgICAgICB2YXIgQ09OVEVYVCA9IGVudi5zaGFyZWQuY29udGV4dFxuICAgICAgICAgICAgdmFyIEJPWF9XID0gd1xuICAgICAgICAgICAgaWYgKCEoJ3dpZHRoJyBpbiBib3gpKSB7XG4gICAgICAgICAgICAgIEJPWF9XID0gc2NvcGUuZGVmKENPTlRFWFQsICcuJywgU19GUkFNRUJVRkZFUl9XSURUSCwgJy0nLCB4KVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgY2hlY2suY29tbWFuZCh3ID4gMCwgJ2ludmFsaWQgJyArIHBhcmFtKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFyIEJPWF9IID0gaFxuICAgICAgICAgICAgaWYgKCEoJ2hlaWdodCcgaW4gYm94KSkge1xuICAgICAgICAgICAgICBCT1hfSCA9IHNjb3BlLmRlZihDT05URVhULCAnLicsIFNfRlJBTUVCVUZGRVJfSEVJR0hULCAnLScsIHkpXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBjaGVjay5jb21tYW5kKGggPiAwLCAnaW52YWxpZCAnICsgcGFyYW0pXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gW3gsIHksIEJPWF9XLCBCT1hfSF1cbiAgICAgICAgICB9KVxuICAgICAgfSBlbHNlIGlmIChwYXJhbSBpbiBkeW5hbWljT3B0aW9ucykge1xuICAgICAgICB2YXIgZHluQm94ID0gZHluYW1pY09wdGlvbnNbcGFyYW1dXG4gICAgICAgIHZhciByZXN1bHQgPSBjcmVhdGVEeW5hbWljRGVjbChkeW5Cb3gsIGZ1bmN0aW9uIChlbnYsIHNjb3BlKSB7XG4gICAgICAgICAgdmFyIEJPWCA9IGVudi5pbnZva2Uoc2NvcGUsIGR5bkJveClcblxuICAgICAgICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGVudi5hc3NlcnQoc2NvcGUsXG4gICAgICAgICAgICAgIEJPWCArICcmJnR5cGVvZiAnICsgQk9YICsgJz09PVwib2JqZWN0XCInLFxuICAgICAgICAgICAgICAnaW52YWxpZCAnICsgcGFyYW0pXG4gICAgICAgICAgfSlcblxuICAgICAgICAgIHZhciBDT05URVhUID0gZW52LnNoYXJlZC5jb250ZXh0XG4gICAgICAgICAgdmFyIEJPWF9YID0gc2NvcGUuZGVmKEJPWCwgJy54fDAnKVxuICAgICAgICAgIHZhciBCT1hfWSA9IHNjb3BlLmRlZihCT1gsICcueXwwJylcbiAgICAgICAgICB2YXIgQk9YX1cgPSBzY29wZS5kZWYoXG4gICAgICAgICAgICAnXCJ3aWR0aFwiIGluICcsIEJPWCwgJz8nLCBCT1gsICcud2lkdGh8MDonLFxuICAgICAgICAgICAgJygnLCBDT05URVhULCAnLicsIFNfRlJBTUVCVUZGRVJfV0lEVEgsICctJywgQk9YX1gsICcpJylcbiAgICAgICAgICB2YXIgQk9YX0ggPSBzY29wZS5kZWYoXG4gICAgICAgICAgICAnXCJoZWlnaHRcIiBpbiAnLCBCT1gsICc/JywgQk9YLCAnLmhlaWdodHwwOicsXG4gICAgICAgICAgICAnKCcsIENPTlRFWFQsICcuJywgU19GUkFNRUJVRkZFUl9IRUlHSFQsICctJywgQk9YX1ksICcpJylcblxuICAgICAgICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGVudi5hc3NlcnQoc2NvcGUsXG4gICAgICAgICAgICAgIEJPWF9YICsgJz49MCYmJyArXG4gICAgICAgICAgICAgIEJPWF9ZICsgJz49MCYmJyArXG4gICAgICAgICAgICAgIEJPWF9XICsgJz4wJiYnICtcbiAgICAgICAgICAgICAgQk9YX0ggKyAnPjAnLFxuICAgICAgICAgICAgICAnaW52YWxpZCAnICsgcGFyYW0pXG4gICAgICAgICAgfSlcblxuICAgICAgICAgIHJldHVybiBbQk9YX1gsIEJPWF9ZLCBCT1hfVywgQk9YX0hdXG4gICAgICAgIH0pXG4gICAgICAgIGlmIChmcmFtZWJ1ZmZlcikge1xuICAgICAgICAgIHJlc3VsdC50aGlzRGVwID0gcmVzdWx0LnRoaXNEZXAgfHwgZnJhbWVidWZmZXIudGhpc0RlcFxuICAgICAgICAgIHJlc3VsdC5jb250ZXh0RGVwID0gcmVzdWx0LmNvbnRleHREZXAgfHwgZnJhbWVidWZmZXIuY29udGV4dERlcFxuICAgICAgICAgIHJlc3VsdC5wcm9wRGVwID0gcmVzdWx0LnByb3BEZXAgfHwgZnJhbWVidWZmZXIucHJvcERlcFxuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXN1bHRcbiAgICAgIH0gZWxzZSBpZiAoZnJhbWVidWZmZXIpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBEZWNsYXJhdGlvbihcbiAgICAgICAgICBmcmFtZWJ1ZmZlci50aGlzRGVwLFxuICAgICAgICAgIGZyYW1lYnVmZmVyLmNvbnRleHREZXAsXG4gICAgICAgICAgZnJhbWVidWZmZXIucHJvcERlcCxcbiAgICAgICAgICBmdW5jdGlvbiAoZW52LCBzY29wZSkge1xuICAgICAgICAgICAgdmFyIENPTlRFWFQgPSBlbnYuc2hhcmVkLmNvbnRleHRcbiAgICAgICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICAgIDAsIDAsXG4gICAgICAgICAgICAgIHNjb3BlLmRlZihDT05URVhULCAnLicsIFNfRlJBTUVCVUZGRVJfV0lEVEgpLFxuICAgICAgICAgICAgICBzY29wZS5kZWYoQ09OVEVYVCwgJy4nLCBTX0ZSQU1FQlVGRkVSX0hFSUdIVCldXG4gICAgICAgICAgfSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiBudWxsXG4gICAgICB9XG4gICAgfVxuXG4gICAgdmFyIHZpZXdwb3J0ID0gcGFyc2VCb3goU19WSUVXUE9SVClcblxuICAgIGlmICh2aWV3cG9ydCkge1xuICAgICAgdmFyIHByZXZWaWV3cG9ydCA9IHZpZXdwb3J0XG4gICAgICB2aWV3cG9ydCA9IG5ldyBEZWNsYXJhdGlvbihcbiAgICAgICAgdmlld3BvcnQudGhpc0RlcCxcbiAgICAgICAgdmlld3BvcnQuY29udGV4dERlcCxcbiAgICAgICAgdmlld3BvcnQucHJvcERlcCxcbiAgICAgICAgZnVuY3Rpb24gKGVudiwgc2NvcGUpIHtcbiAgICAgICAgICB2YXIgVklFV1BPUlQgPSBwcmV2Vmlld3BvcnQuYXBwZW5kKGVudiwgc2NvcGUpXG4gICAgICAgICAgdmFyIENPTlRFWFQgPSBlbnYuc2hhcmVkLmNvbnRleHRcbiAgICAgICAgICBzY29wZS5zZXQoXG4gICAgICAgICAgICBDT05URVhULFxuICAgICAgICAgICAgJy4nICsgU19WSUVXUE9SVF9XSURUSCxcbiAgICAgICAgICAgIFZJRVdQT1JUWzJdKVxuICAgICAgICAgIHNjb3BlLnNldChcbiAgICAgICAgICAgIENPTlRFWFQsXG4gICAgICAgICAgICAnLicgKyBTX1ZJRVdQT1JUX0hFSUdIVCxcbiAgICAgICAgICAgIFZJRVdQT1JUWzNdKVxuICAgICAgICAgIHJldHVybiBWSUVXUE9SVFxuICAgICAgICB9KVxuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICB2aWV3cG9ydDogdmlld3BvcnQsXG4gICAgICBzY2lzc29yX2JveDogcGFyc2VCb3goU19TQ0lTU09SX0JPWClcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBwYXJzZVByb2dyYW0gKG9wdGlvbnMpIHtcbiAgICB2YXIgc3RhdGljT3B0aW9ucyA9IG9wdGlvbnMuc3RhdGljXG4gICAgdmFyIGR5bmFtaWNPcHRpb25zID0gb3B0aW9ucy5keW5hbWljXG5cbiAgICBmdW5jdGlvbiBwYXJzZVNoYWRlciAobmFtZSkge1xuICAgICAgaWYgKG5hbWUgaW4gc3RhdGljT3B0aW9ucykge1xuICAgICAgICB2YXIgaWQgPSBzdHJpbmdTdG9yZS5pZChzdGF0aWNPcHRpb25zW25hbWVdKVxuICAgICAgICBjaGVjay5vcHRpb25hbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgc2hhZGVyU3RhdGUuc2hhZGVyKHNoYWRlclR5cGVbbmFtZV0sIGlkKVxuICAgICAgICB9KVxuICAgICAgICB2YXIgcmVzdWx0ID0gY3JlYXRlU3RhdGljRGVjbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgcmV0dXJuIGlkXG4gICAgICAgIH0pXG4gICAgICAgIHJlc3VsdC5pZCA9IGlkXG4gICAgICAgIHJldHVybiByZXN1bHRcbiAgICAgIH0gZWxzZSBpZiAobmFtZSBpbiBkeW5hbWljT3B0aW9ucykge1xuICAgICAgICB2YXIgZHluID0gZHluYW1pY09wdGlvbnNbbmFtZV1cbiAgICAgICAgcmV0dXJuIGNyZWF0ZUR5bmFtaWNEZWNsKGR5biwgZnVuY3Rpb24gKGVudiwgc2NvcGUpIHtcbiAgICAgICAgICB2YXIgc3RyID0gZW52Lmludm9rZShzY29wZSwgZHluKVxuICAgICAgICAgIHZhciBpZCA9IHNjb3BlLmRlZihlbnYuc2hhcmVkLnN0cmluZ3MsICcuaWQoJywgc3RyLCAnKScpXG4gICAgICAgICAgY2hlY2sub3B0aW9uYWwoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgc2NvcGUoXG4gICAgICAgICAgICAgIGVudi5zaGFyZWQuc2hhZGVyLCAnLnNoYWRlcignLFxuICAgICAgICAgICAgICBzaGFkZXJUeXBlW25hbWVdLCAnLCcsXG4gICAgICAgICAgICAgIGlkLCAnLCcsXG4gICAgICAgICAgICAgIGVudi5jb21tYW5kLCAnKTsnKVxuICAgICAgICAgIH0pXG4gICAgICAgICAgcmV0dXJuIGlkXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgICByZXR1cm4gbnVsbFxuICAgIH1cblxuICAgIHZhciBmcmFnID0gcGFyc2VTaGFkZXIoU19GUkFHKVxuICAgIHZhciB2ZXJ0ID0gcGFyc2VTaGFkZXIoU19WRVJUKVxuXG4gICAgdmFyIHByb2dyYW0gPSBudWxsXG4gICAgdmFyIHByb2dWYXJcbiAgICBpZiAoaXNTdGF0aWMoZnJhZykgJiYgaXNTdGF0aWModmVydCkpIHtcbiAgICAgIHByb2dyYW0gPSBzaGFkZXJTdGF0ZS5wcm9ncmFtKHZlcnQuaWQsIGZyYWcuaWQpXG4gICAgICBwcm9nVmFyID0gY3JlYXRlU3RhdGljRGVjbChmdW5jdGlvbiAoZW52LCBzY29wZSkge1xuICAgICAgICByZXR1cm4gZW52LmxpbmsocHJvZ3JhbSlcbiAgICAgIH0pXG4gICAgfSBlbHNlIHtcbiAgICAgIHByb2dWYXIgPSBuZXcgRGVjbGFyYXRpb24oXG4gICAgICAgIChmcmFnICYmIGZyYWcudGhpc0RlcCkgfHwgKHZlcnQgJiYgdmVydC50aGlzRGVwKSxcbiAgICAgICAgKGZyYWcgJiYgZnJhZy5jb250ZXh0RGVwKSB8fCAodmVydCAmJiB2ZXJ0LmNvbnRleHREZXApLFxuICAgICAgICAoZnJhZyAmJiBmcmFnLnByb3BEZXApIHx8ICh2ZXJ0ICYmIHZlcnQucHJvcERlcCksXG4gICAgICAgIGZ1bmN0aW9uIChlbnYsIHNjb3BlKSB7XG4gICAgICAgICAgdmFyIFNIQURFUl9TVEFURSA9IGVudi5zaGFyZWQuc2hhZGVyXG4gICAgICAgICAgdmFyIGZyYWdJZFxuICAgICAgICAgIGlmIChmcmFnKSB7XG4gICAgICAgICAgICBmcmFnSWQgPSBmcmFnLmFwcGVuZChlbnYsIHNjb3BlKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBmcmFnSWQgPSBzY29wZS5kZWYoU0hBREVSX1NUQVRFLCAnLicsIFNfRlJBRylcbiAgICAgICAgICB9XG4gICAgICAgICAgdmFyIHZlcnRJZFxuICAgICAgICAgIGlmICh2ZXJ0KSB7XG4gICAgICAgICAgICB2ZXJ0SWQgPSB2ZXJ0LmFwcGVuZChlbnYsIHNjb3BlKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB2ZXJ0SWQgPSBzY29wZS5kZWYoU0hBREVSX1NUQVRFLCAnLicsIFNfVkVSVClcbiAgICAgICAgICB9XG4gICAgICAgICAgdmFyIHByb2dEZWYgPSBTSEFERVJfU1RBVEUgKyAnLnByb2dyYW0oJyArIHZlcnRJZCArICcsJyArIGZyYWdJZFxuICAgICAgICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHByb2dEZWYgKz0gJywnICsgZW52LmNvbW1hbmRcbiAgICAgICAgICB9KVxuICAgICAgICAgIHJldHVybiBzY29wZS5kZWYocHJvZ0RlZiArICcpJylcbiAgICAgICAgfSlcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgZnJhZzogZnJhZyxcbiAgICAgIHZlcnQ6IHZlcnQsXG4gICAgICBwcm9nVmFyOiBwcm9nVmFyLFxuICAgICAgcHJvZ3JhbTogcHJvZ3JhbVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHBhcnNlRHJhdyAob3B0aW9ucykge1xuICAgIHZhciBzdGF0aWNPcHRpb25zID0gb3B0aW9ucy5zdGF0aWNcbiAgICB2YXIgZHluYW1pY09wdGlvbnMgPSBvcHRpb25zLmR5bmFtaWNcblxuICAgIGZ1bmN0aW9uIHBhcnNlRWxlbWVudHMgKCkge1xuICAgICAgaWYgKFNfRUxFTUVOVFMgaW4gc3RhdGljT3B0aW9ucykge1xuICAgICAgICB2YXIgZWxlbWVudHMgPSBzdGF0aWNPcHRpb25zW1NfRUxFTUVOVFNdXG4gICAgICAgIGlmIChpc0J1ZmZlckFyZ3MoZWxlbWVudHMpKSB7XG4gICAgICAgICAgZWxlbWVudHMgPSBlbGVtZW50U3RhdGUuZ2V0RWxlbWVudHMoZWxlbWVudFN0YXRlLmNyZWF0ZShlbGVtZW50cykpXG4gICAgICAgIH0gZWxzZSBpZiAoZWxlbWVudHMpIHtcbiAgICAgICAgICBlbGVtZW50cyA9IGVsZW1lbnRTdGF0ZS5nZXRFbGVtZW50cyhlbGVtZW50cylcbiAgICAgICAgICBjaGVjay5jb21tYW5kKGVsZW1lbnRzLCAnaW52YWxpZCBlbGVtZW50cycpXG4gICAgICAgIH1cbiAgICAgICAgdmFyIHJlc3VsdCA9IGNyZWF0ZVN0YXRpY0RlY2woZnVuY3Rpb24gKGVudiwgc2NvcGUpIHtcbiAgICAgICAgICBpZiAoZWxlbWVudHMpIHtcbiAgICAgICAgICAgIHZhciByZXN1bHQgPSBlbnYubGluayhlbGVtZW50cylcbiAgICAgICAgICAgIGVudi5FTEVNRU5UUyA9IHJlc3VsdFxuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdFxuICAgICAgICAgIH1cbiAgICAgICAgICBlbnYuRUxFTUVOVFMgPSBudWxsXG4gICAgICAgICAgcmV0dXJuIG51bGxcbiAgICAgICAgfSlcbiAgICAgICAgcmVzdWx0LnZhbHVlID0gZWxlbWVudHNcbiAgICAgICAgcmV0dXJuIHJlc3VsdFxuICAgICAgfSBlbHNlIGlmIChTX0VMRU1FTlRTIGluIGR5bmFtaWNPcHRpb25zKSB7XG4gICAgICAgIHZhciBkeW4gPSBkeW5hbWljT3B0aW9uc1tTX0VMRU1FTlRTXVxuICAgICAgICByZXR1cm4gY3JlYXRlRHluYW1pY0RlY2woZHluLCBmdW5jdGlvbiAoZW52LCBzY29wZSkge1xuICAgICAgICAgIHZhciBzaGFyZWQgPSBlbnYuc2hhcmVkXG5cbiAgICAgICAgICB2YXIgSVNfQlVGRkVSX0FSR1MgPSBzaGFyZWQuaXNCdWZmZXJBcmdzXG4gICAgICAgICAgdmFyIEVMRU1FTlRfU1RBVEUgPSBzaGFyZWQuZWxlbWVudHNcblxuICAgICAgICAgIHZhciBlbGVtZW50RGVmbiA9IGVudi5pbnZva2Uoc2NvcGUsIGR5bilcbiAgICAgICAgICB2YXIgZWxlbWVudHMgPSBzY29wZS5kZWYoJ251bGwnKVxuICAgICAgICAgIHZhciBlbGVtZW50U3RyZWFtID0gc2NvcGUuZGVmKElTX0JVRkZFUl9BUkdTLCAnKCcsIGVsZW1lbnREZWZuLCAnKScpXG5cbiAgICAgICAgICB2YXIgaWZ0ZSA9IGVudi5jb25kKGVsZW1lbnRTdHJlYW0pXG4gICAgICAgICAgICAudGhlbihlbGVtZW50cywgJz0nLCBFTEVNRU5UX1NUQVRFLCAnLmNyZWF0ZVN0cmVhbSgnLCBlbGVtZW50RGVmbiwgJyk7JylcbiAgICAgICAgICAgIC5lbHNlKGVsZW1lbnRzLCAnPScsIEVMRU1FTlRfU1RBVEUsICcuZ2V0RWxlbWVudHMoJywgZWxlbWVudERlZm4sICcpOycpXG5cbiAgICAgICAgICBjaGVjay5vcHRpb25hbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICBlbnYuYXNzZXJ0KGlmdGUuZWxzZSxcbiAgICAgICAgICAgICAgJyEnICsgZWxlbWVudERlZm4gKyAnfHwnICsgZWxlbWVudHMsXG4gICAgICAgICAgICAgICdpbnZhbGlkIGVsZW1lbnRzJylcbiAgICAgICAgICB9KVxuXG4gICAgICAgICAgc2NvcGUuZW50cnkoaWZ0ZSlcbiAgICAgICAgICBzY29wZS5leGl0KFxuICAgICAgICAgICAgZW52LmNvbmQoZWxlbWVudFN0cmVhbSlcbiAgICAgICAgICAgICAgLnRoZW4oRUxFTUVOVF9TVEFURSwgJy5kZXN0cm95U3RyZWFtKCcsIGVsZW1lbnRzLCAnKTsnKSlcblxuICAgICAgICAgIGVudi5FTEVNRU5UUyA9IGVsZW1lbnRzXG5cbiAgICAgICAgICByZXR1cm4gZWxlbWVudHNcbiAgICAgICAgfSlcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIG51bGxcbiAgICB9XG5cbiAgICB2YXIgZWxlbWVudHMgPSBwYXJzZUVsZW1lbnRzKClcblxuICAgIGZ1bmN0aW9uIHBhcnNlUHJpbWl0aXZlICgpIHtcbiAgICAgIGlmIChTX1BSSU1JVElWRSBpbiBzdGF0aWNPcHRpb25zKSB7XG4gICAgICAgIHZhciBwcmltaXRpdmUgPSBzdGF0aWNPcHRpb25zW1NfUFJJTUlUSVZFXVxuICAgICAgICBjaGVjay5jb21tYW5kUGFyYW1ldGVyKHByaW1pdGl2ZSwgcHJpbVR5cGVzLCAnaW52YWxpZCBwcmltaXR2ZScpXG4gICAgICAgIHJldHVybiBjcmVhdGVTdGF0aWNEZWNsKGZ1bmN0aW9uIChlbnYsIHNjb3BlKSB7XG4gICAgICAgICAgcmV0dXJuIHByaW1UeXBlc1twcmltaXRpdmVdXG4gICAgICAgIH0pXG4gICAgICB9IGVsc2UgaWYgKFNfUFJJTUlUSVZFIGluIGR5bmFtaWNPcHRpb25zKSB7XG4gICAgICAgIHZhciBkeW5QcmltaXRpdmUgPSBkeW5hbWljT3B0aW9uc1tTX1BSSU1JVElWRV1cbiAgICAgICAgcmV0dXJuIGNyZWF0ZUR5bmFtaWNEZWNsKGR5blByaW1pdGl2ZSwgZnVuY3Rpb24gKGVudiwgc2NvcGUpIHtcbiAgICAgICAgICB2YXIgUFJJTV9UWVBFUyA9IGVudi5jb25zdGFudHMucHJpbVR5cGVzXG4gICAgICAgICAgdmFyIHByaW0gPSBlbnYuaW52b2tlKHNjb3BlLCBkeW5QcmltaXRpdmUpXG4gICAgICAgICAgY2hlY2sub3B0aW9uYWwoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgZW52LmFzc2VydChzY29wZSxcbiAgICAgICAgICAgICAgcHJpbSArICcgaW4gJyArIFBSSU1fVFlQRVMsXG4gICAgICAgICAgICAgICdpbnZhbGlkIHByaW1pdGl2ZSwgbXVzdCBiZSBvbmUgb2YgJyArIE9iamVjdC5rZXlzKHByaW1UeXBlcykpXG4gICAgICAgICAgfSlcbiAgICAgICAgICByZXR1cm4gc2NvcGUuZGVmKFBSSU1fVFlQRVMsICdbJywgcHJpbSwgJ10nKVxuICAgICAgICB9KVxuICAgICAgfSBlbHNlIGlmIChlbGVtZW50cykge1xuICAgICAgICBpZiAoaXNTdGF0aWMoZWxlbWVudHMpKSB7XG4gICAgICAgICAgaWYgKGVsZW1lbnRzLnZhbHVlKSB7XG4gICAgICAgICAgICByZXR1cm4gY3JlYXRlU3RhdGljRGVjbChmdW5jdGlvbiAoZW52LCBzY29wZSkge1xuICAgICAgICAgICAgICByZXR1cm4gc2NvcGUuZGVmKGVudi5FTEVNRU5UUywgJy5wcmltVHlwZScpXG4gICAgICAgICAgICB9KVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gY3JlYXRlU3RhdGljRGVjbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgIHJldHVybiBHTF9UUklBTkdMRVNcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiBuZXcgRGVjbGFyYXRpb24oXG4gICAgICAgICAgICBlbGVtZW50cy50aGlzRGVwLFxuICAgICAgICAgICAgZWxlbWVudHMuY29udGV4dERlcCxcbiAgICAgICAgICAgIGVsZW1lbnRzLnByb3BEZXAsXG4gICAgICAgICAgICBmdW5jdGlvbiAoZW52LCBzY29wZSkge1xuICAgICAgICAgICAgICB2YXIgZWxlbWVudHMgPSBlbnYuRUxFTUVOVFNcbiAgICAgICAgICAgICAgcmV0dXJuIHNjb3BlLmRlZihlbGVtZW50cywgJz8nLCBlbGVtZW50cywgJy5wcmltVHlwZTonLCBHTF9UUklBTkdMRVMpXG4gICAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gbnVsbFxuICAgIH1cblxuICAgIGZ1bmN0aW9uIHBhcnNlUGFyYW0gKHBhcmFtLCBpc09mZnNldCkge1xuICAgICAgaWYgKHBhcmFtIGluIHN0YXRpY09wdGlvbnMpIHtcbiAgICAgICAgdmFyIHZhbHVlID0gc3RhdGljT3B0aW9uc1twYXJhbV0gfCAwXG4gICAgICAgIGNoZWNrLmNvbW1hbmQoIWlzT2Zmc2V0IHx8IHZhbHVlID49IDAsICdpbnZhbGlkICcgKyBwYXJhbSlcbiAgICAgICAgcmV0dXJuIGNyZWF0ZVN0YXRpY0RlY2woZnVuY3Rpb24gKGVudiwgc2NvcGUpIHtcbiAgICAgICAgICBpZiAoaXNPZmZzZXQpIHtcbiAgICAgICAgICAgIGVudi5PRkZTRVQgPSB2YWx1ZVxuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gdmFsdWVcbiAgICAgICAgfSlcbiAgICAgIH0gZWxzZSBpZiAocGFyYW0gaW4gZHluYW1pY09wdGlvbnMpIHtcbiAgICAgICAgdmFyIGR5blZhbHVlID0gZHluYW1pY09wdGlvbnNbcGFyYW1dXG4gICAgICAgIHJldHVybiBjcmVhdGVEeW5hbWljRGVjbChkeW5WYWx1ZSwgZnVuY3Rpb24gKGVudiwgc2NvcGUpIHtcbiAgICAgICAgICB2YXIgcmVzdWx0ID0gZW52Lmludm9rZShzY29wZSwgZHluVmFsdWUpXG4gICAgICAgICAgaWYgKGlzT2Zmc2V0KSB7XG4gICAgICAgICAgICBlbnYuT0ZGU0VUID0gcmVzdWx0XG4gICAgICAgICAgICBjaGVjay5vcHRpb25hbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgIGVudi5hc3NlcnQoc2NvcGUsXG4gICAgICAgICAgICAgICAgcmVzdWx0ICsgJz49MCcsXG4gICAgICAgICAgICAgICAgJ2ludmFsaWQgJyArIHBhcmFtKVxuICAgICAgICAgICAgfSlcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIHJlc3VsdFxuICAgICAgICB9KVxuICAgICAgfSBlbHNlIGlmIChpc09mZnNldCAmJiBlbGVtZW50cykge1xuICAgICAgICByZXR1cm4gY3JlYXRlU3RhdGljRGVjbChmdW5jdGlvbiAoZW52LCBzY29wZSkge1xuICAgICAgICAgIGVudi5PRkZTRVQgPSAnMCdcbiAgICAgICAgICByZXR1cm4gMFxuICAgICAgICB9KVxuICAgICAgfVxuICAgICAgcmV0dXJuIG51bGxcbiAgICB9XG5cbiAgICB2YXIgT0ZGU0VUID0gcGFyc2VQYXJhbShTX09GRlNFVCwgdHJ1ZSlcblxuICAgIGZ1bmN0aW9uIHBhcnNlVmVydENvdW50ICgpIHtcbiAgICAgIGlmIChTX0NPVU5UIGluIHN0YXRpY09wdGlvbnMpIHtcbiAgICAgICAgdmFyIGNvdW50ID0gc3RhdGljT3B0aW9uc1tTX0NPVU5UXSB8IDBcbiAgICAgICAgY2hlY2suY29tbWFuZChcbiAgICAgICAgICB0eXBlb2YgY291bnQgPT09ICdudW1iZXInICYmIGNvdW50ID49IDAsICdpbnZhbGlkIHZlcnRleCBjb3VudCcpXG4gICAgICAgIHJldHVybiBjcmVhdGVTdGF0aWNEZWNsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICByZXR1cm4gY291bnRcbiAgICAgICAgfSlcbiAgICAgIH0gZWxzZSBpZiAoU19DT1VOVCBpbiBkeW5hbWljT3B0aW9ucykge1xuICAgICAgICB2YXIgZHluQ291bnQgPSBkeW5hbWljT3B0aW9uc1tTX0NPVU5UXVxuICAgICAgICByZXR1cm4gY3JlYXRlRHluYW1pY0RlY2woZHluQ291bnQsIGZ1bmN0aW9uIChlbnYsIHNjb3BlKSB7XG4gICAgICAgICAgdmFyIHJlc3VsdCA9IGVudi5pbnZva2Uoc2NvcGUsIGR5bkNvdW50KVxuICAgICAgICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIGVudi5hc3NlcnQoc2NvcGUsXG4gICAgICAgICAgICAgICd0eXBlb2YgJyArIHJlc3VsdCArICc9PT1cIm51bWJlclwiJiYnICtcbiAgICAgICAgICAgICAgcmVzdWx0ICsgJz49MCYmJyArXG4gICAgICAgICAgICAgIHJlc3VsdCArICc9PT0oJyArIHJlc3VsdCArICd8MCknLFxuICAgICAgICAgICAgICAnaW52YWxpZCB2ZXJ0ZXggY291bnQnKVxuICAgICAgICAgIH0pXG4gICAgICAgICAgcmV0dXJuIHJlc3VsdFxuICAgICAgICB9KVxuICAgICAgfSBlbHNlIGlmIChlbGVtZW50cykge1xuICAgICAgICBpZiAoaXNTdGF0aWMoZWxlbWVudHMpKSB7XG4gICAgICAgICAgaWYgKGVsZW1lbnRzKSB7XG4gICAgICAgICAgICBpZiAoT0ZGU0VUKSB7XG4gICAgICAgICAgICAgIHJldHVybiBuZXcgRGVjbGFyYXRpb24oXG4gICAgICAgICAgICAgICAgT0ZGU0VULnRoaXNEZXAsXG4gICAgICAgICAgICAgICAgT0ZGU0VULmNvbnRleHREZXAsXG4gICAgICAgICAgICAgICAgT0ZGU0VULnByb3BEZXAsXG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gKGVudiwgc2NvcGUpIHtcbiAgICAgICAgICAgICAgICAgIHZhciByZXN1bHQgPSBzY29wZS5kZWYoXG4gICAgICAgICAgICAgICAgICAgIGVudi5FTEVNRU5UUywgJy52ZXJ0Q291bnQtJywgZW52Lk9GRlNFVClcblxuICAgICAgICAgICAgICAgICAgY2hlY2sub3B0aW9uYWwoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgICAgICBlbnYuYXNzZXJ0KHNjb3BlLFxuICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCArICc+PTAnLFxuICAgICAgICAgICAgICAgICAgICAgICdpbnZhbGlkIHZlcnRleCBvZmZzZXQvZWxlbWVudCBidWZmZXIgdG9vIHNtYWxsJylcbiAgICAgICAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgICAgICAgIHJldHVybiByZXN1bHRcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgcmV0dXJuIGNyZWF0ZVN0YXRpY0RlY2woZnVuY3Rpb24gKGVudiwgc2NvcGUpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gc2NvcGUuZGVmKGVudi5FTEVNRU5UUywgJy52ZXJ0Q291bnQnKVxuICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB2YXIgcmVzdWx0ID0gY3JlYXRlU3RhdGljRGVjbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgIHJldHVybiAtMVxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgcmVzdWx0Lk1JU1NJTkcgPSB0cnVlXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdFxuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB2YXIgdmFyaWFibGUgPSBuZXcgRGVjbGFyYXRpb24oXG4gICAgICAgICAgICBlbGVtZW50cy50aGlzRGVwIHx8IE9GRlNFVC50aGlzRGVwLFxuICAgICAgICAgICAgZWxlbWVudHMuY29udGV4dERlcCB8fCBPRkZTRVQuY29udGV4dERlcCxcbiAgICAgICAgICAgIGVsZW1lbnRzLnByb3BEZXAgfHwgT0ZGU0VULnByb3BEZXAsXG4gICAgICAgICAgICBmdW5jdGlvbiAoZW52LCBzY29wZSkge1xuICAgICAgICAgICAgICB2YXIgZWxlbWVudHMgPSBlbnYuRUxFTUVOVFNcbiAgICAgICAgICAgICAgaWYgKGVudi5PRkZTRVQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gc2NvcGUuZGVmKGVsZW1lbnRzLCAnPycsIGVsZW1lbnRzLCAnLnZlcnRDb3VudC0nLFxuICAgICAgICAgICAgICAgICAgZW52Lk9GRlNFVCwgJzotMScpXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgcmV0dXJuIHNjb3BlLmRlZihlbGVtZW50cywgJz8nLCBlbGVtZW50cywgJy52ZXJ0Q291bnQ6LTEnKVxuICAgICAgICAgICAgfSlcbiAgICAgICAgICBjaGVjay5vcHRpb25hbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXJpYWJsZS5EWU5BTUlDID0gdHJ1ZVxuICAgICAgICAgIH0pXG4gICAgICAgICAgcmV0dXJuIHZhcmlhYmxlXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBudWxsXG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGVsZW1lbnRzOiBlbGVtZW50cyxcbiAgICAgIHByaW1pdGl2ZTogcGFyc2VQcmltaXRpdmUoKSxcbiAgICAgIGNvdW50OiBwYXJzZVZlcnRDb3VudCgpLFxuICAgICAgaW5zdGFuY2VzOiBwYXJzZVBhcmFtKFNfSU5TVEFOQ0VTLCBmYWxzZSksXG4gICAgICBvZmZzZXQ6IE9GRlNFVFxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHBhcnNlR0xTdGF0ZSAob3B0aW9ucykge1xuICAgIHZhciBzdGF0aWNPcHRpb25zID0gb3B0aW9ucy5zdGF0aWNcbiAgICB2YXIgZHluYW1pY09wdGlvbnMgPSBvcHRpb25zLmR5bmFtaWNcblxuICAgIHZhciBTVEFURSA9IHt9XG5cbiAgICBHTF9TVEFURV9OQU1FUy5mb3JFYWNoKGZ1bmN0aW9uIChwcm9wKSB7XG4gICAgICB2YXIgcGFyYW0gPSBwcm9wTmFtZShwcm9wKVxuXG4gICAgICBmdW5jdGlvbiBwYXJzZVBhcmFtIChwYXJzZVN0YXRpYywgcGFyc2VEeW5hbWljKSB7XG4gICAgICAgIGlmIChwcm9wIGluIHN0YXRpY09wdGlvbnMpIHtcbiAgICAgICAgICB2YXIgdmFsdWUgPSBwYXJzZVN0YXRpYyhzdGF0aWNPcHRpb25zW3Byb3BdKVxuICAgICAgICAgIFNUQVRFW3BhcmFtXSA9IGNyZWF0ZVN0YXRpY0RlY2woZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgcmV0dXJuIHZhbHVlXG4gICAgICAgICAgfSlcbiAgICAgICAgfSBlbHNlIGlmIChwcm9wIGluIGR5bmFtaWNPcHRpb25zKSB7XG4gICAgICAgICAgdmFyIGR5biA9IGR5bmFtaWNPcHRpb25zW3Byb3BdXG4gICAgICAgICAgU1RBVEVbcGFyYW1dID0gY3JlYXRlRHluYW1pY0RlY2woZHluLCBmdW5jdGlvbiAoZW52LCBzY29wZSkge1xuICAgICAgICAgICAgcmV0dXJuIHBhcnNlRHluYW1pYyhlbnYsIHNjb3BlLCBlbnYuaW52b2tlKHNjb3BlLCBkeW4pKVxuICAgICAgICAgIH0pXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgc3dpdGNoIChwcm9wKSB7XG4gICAgICAgIGNhc2UgU19DVUxMX0VOQUJMRTpcbiAgICAgICAgY2FzZSBTX0JMRU5EX0VOQUJMRTpcbiAgICAgICAgY2FzZSBTX0RJVEhFUjpcbiAgICAgICAgY2FzZSBTX1NURU5DSUxfRU5BQkxFOlxuICAgICAgICBjYXNlIFNfREVQVEhfRU5BQkxFOlxuICAgICAgICBjYXNlIFNfU0NJU1NPUl9FTkFCTEU6XG4gICAgICAgIGNhc2UgU19QT0xZR09OX09GRlNFVF9FTkFCTEU6XG4gICAgICAgIGNhc2UgU19TQU1QTEVfQUxQSEE6XG4gICAgICAgIGNhc2UgU19TQU1QTEVfRU5BQkxFOlxuICAgICAgICBjYXNlIFNfREVQVEhfTUFTSzpcbiAgICAgICAgICByZXR1cm4gcGFyc2VQYXJhbShcbiAgICAgICAgICAgIGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgICAgICAgICAgICBjaGVjay5jb21tYW5kVHlwZSh2YWx1ZSwgJ2Jvb2xlYW4nLCBwcm9wKVxuICAgICAgICAgICAgICByZXR1cm4gdmFsdWVcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoZW52LCBzY29wZSwgdmFsdWUpIHtcbiAgICAgICAgICAgICAgY2hlY2sub3B0aW9uYWwoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIGVudi5hc3NlcnQoc2NvcGUsXG4gICAgICAgICAgICAgICAgICAndHlwZW9mICcgKyB2YWx1ZSArICc9PT1cImJvb2xlYW5cIicsXG4gICAgICAgICAgICAgICAgICAnaW52YWxpZCBmbGFnICcgKyBwcm9wKVxuICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICByZXR1cm4gdmFsdWVcbiAgICAgICAgICAgIH0pXG5cbiAgICAgICAgY2FzZSBTX0RFUFRIX0ZVTkM6XG4gICAgICAgICAgcmV0dXJuIHBhcnNlUGFyYW0oXG4gICAgICAgICAgICBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICAgICAgICAgICAgY2hlY2suY29tbWFuZFBhcmFtZXRlcih2YWx1ZSwgY29tcGFyZUZ1bmNzLCAnaW52YWxpZCAnICsgcHJvcClcbiAgICAgICAgICAgICAgcmV0dXJuIGNvbXBhcmVGdW5jc1t2YWx1ZV1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoZW52LCBzY29wZSwgdmFsdWUpIHtcbiAgICAgICAgICAgICAgdmFyIENPTVBBUkVfRlVOQ1MgPSBlbnYuY29uc3RhbnRzLmNvbXBhcmVGdW5jc1xuICAgICAgICAgICAgICBjaGVjay5vcHRpb25hbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgZW52LmFzc2VydChzY29wZSxcbiAgICAgICAgICAgICAgICAgIHZhbHVlICsgJyBpbiAnICsgQ09NUEFSRV9GVU5DUyxcbiAgICAgICAgICAgICAgICAgICdpbnZhbGlkICcgKyBwcm9wICsgJywgbXVzdCBiZSBvbmUgb2YgJyArIE9iamVjdC5rZXlzKGNvbXBhcmVGdW5jcykpXG4gICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgIHJldHVybiBzY29wZS5kZWYoQ09NUEFSRV9GVU5DUywgJ1snLCB2YWx1ZSwgJ10nKVxuICAgICAgICAgICAgfSlcblxuICAgICAgICBjYXNlIFNfREVQVEhfUkFOR0U6XG4gICAgICAgICAgcmV0dXJuIHBhcnNlUGFyYW0oXG4gICAgICAgICAgICBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICAgICAgICAgICAgY2hlY2suY29tbWFuZChcbiAgICAgICAgICAgICAgICBpc0FycmF5TGlrZSh2YWx1ZSkgJiZcbiAgICAgICAgICAgICAgICB2YWx1ZS5sZW5ndGggPT09IDIgJiZcbiAgICAgICAgICAgICAgICB0eXBlb2YgdmFsdWVbMF0gPT09ICdudW1iZXInICYmXG4gICAgICAgICAgICAgICAgdHlwZW9mIHZhbHVlWzFdID09PSAnbnVtYmVyJyAmJlxuICAgICAgICAgICAgICAgIHZhbHVlWzBdIDw9IHZhbHVlWzFdLFxuICAgICAgICAgICAgICAgICdkZXB0aCByYW5nZSBpcyAyZCBhcnJheScpXG4gICAgICAgICAgICAgIHJldHVybiB2YWx1ZVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChlbnYsIHNjb3BlLCB2YWx1ZSkge1xuICAgICAgICAgICAgICBjaGVjay5vcHRpb25hbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgZW52LmFzc2VydChzY29wZSxcbiAgICAgICAgICAgICAgICAgIGVudi5zaGFyZWQuaXNBcnJheUxpa2UgKyAnKCcgKyB2YWx1ZSArICcpJiYnICtcbiAgICAgICAgICAgICAgICAgIHZhbHVlICsgJy5sZW5ndGg9PT0yJiYnICtcbiAgICAgICAgICAgICAgICAgICd0eXBlb2YgJyArIHZhbHVlICsgJ1swXT09PVwibnVtYmVyXCImJicgK1xuICAgICAgICAgICAgICAgICAgJ3R5cGVvZiAnICsgdmFsdWUgKyAnWzFdPT09XCJudW1iZXJcIiYmJyArXG4gICAgICAgICAgICAgICAgICB2YWx1ZSArICdbMF08PScgKyB2YWx1ZSArICdbMV0nLFxuICAgICAgICAgICAgICAgICAgJ2RlcHRoIHJhbmdlIG11c3QgYmUgYSAyZCBhcnJheScpXG4gICAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgICAgdmFyIFpfTkVBUiA9IHNjb3BlLmRlZignKycsIHZhbHVlLCAnWzBdJylcbiAgICAgICAgICAgICAgdmFyIFpfRkFSID0gc2NvcGUuZGVmKCcrJywgdmFsdWUsICdbMV0nKVxuICAgICAgICAgICAgICByZXR1cm4gW1pfTkVBUiwgWl9GQVJdXG4gICAgICAgICAgICB9KVxuXG4gICAgICAgIGNhc2UgU19CTEVORF9GVU5DOlxuICAgICAgICAgIHJldHVybiBwYXJzZVBhcmFtKFxuICAgICAgICAgICAgZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgICAgICAgIGNoZWNrLmNvbW1hbmRUeXBlKHZhbHVlLCAnb2JqZWN0JywgJ2JsZW5kLmZ1bmMnKVxuICAgICAgICAgICAgICB2YXIgc3JjUkdCID0gKCdzcmNSR0InIGluIHZhbHVlID8gdmFsdWUuc3JjUkdCIDogdmFsdWUuc3JjKVxuICAgICAgICAgICAgICB2YXIgc3JjQWxwaGEgPSAoJ3NyY0FscGhhJyBpbiB2YWx1ZSA/IHZhbHVlLnNyY0FscGhhIDogdmFsdWUuc3JjKVxuICAgICAgICAgICAgICB2YXIgZHN0UkdCID0gKCdkc3RSR0InIGluIHZhbHVlID8gdmFsdWUuZHN0UkdCIDogdmFsdWUuZHN0KVxuICAgICAgICAgICAgICB2YXIgZHN0QWxwaGEgPSAoJ2RzdEFscGhhJyBpbiB2YWx1ZSA/IHZhbHVlLmRzdEFscGhhIDogdmFsdWUuZHN0KVxuICAgICAgICAgICAgICBjaGVjay5jb21tYW5kUGFyYW1ldGVyKHNyY1JHQiwgYmxlbmRGdW5jcywgcGFyYW0gKyAnLnNyY1JHQicpXG4gICAgICAgICAgICAgIGNoZWNrLmNvbW1hbmRQYXJhbWV0ZXIoc3JjQWxwaGEsIGJsZW5kRnVuY3MsIHBhcmFtICsgJy5zcmNBbHBoYScpXG4gICAgICAgICAgICAgIGNoZWNrLmNvbW1hbmRQYXJhbWV0ZXIoZHN0UkdCLCBibGVuZEZ1bmNzLCBwYXJhbSArICcuZHN0UkdCJylcbiAgICAgICAgICAgICAgY2hlY2suY29tbWFuZFBhcmFtZXRlcihkc3RBbHBoYSwgYmxlbmRGdW5jcywgcGFyYW0gKyAnLmRzdEFscGhhJylcbiAgICAgICAgICAgICAgcmV0dXJuIFtcbiAgICAgICAgICAgICAgICBibGVuZEZ1bmNzW3NyY1JHQl0sXG4gICAgICAgICAgICAgICAgYmxlbmRGdW5jc1tkc3RSR0JdLFxuICAgICAgICAgICAgICAgIGJsZW5kRnVuY3Nbc3JjQWxwaGFdLFxuICAgICAgICAgICAgICAgIGJsZW5kRnVuY3NbZHN0QWxwaGFdXG4gICAgICAgICAgICAgIF1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoZW52LCBzY29wZSwgdmFsdWUpIHtcbiAgICAgICAgICAgICAgdmFyIEJMRU5EX0ZVTkNTID0gZW52LmNvbnN0YW50cy5ibGVuZEZ1bmNzXG5cbiAgICAgICAgICAgICAgY2hlY2sub3B0aW9uYWwoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIGVudi5hc3NlcnQoc2NvcGUsXG4gICAgICAgICAgICAgICAgICB2YWx1ZSArICcmJnR5cGVvZiAnICsgdmFsdWUgKyAnPT09XCJvYmplY3RcIicsXG4gICAgICAgICAgICAgICAgICAnaW52YWxpZCBibGVuZCBmdW5jLCBtdXN0IGJlIGFuIG9iamVjdCcpXG4gICAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgICAgZnVuY3Rpb24gcmVhZCAocHJlZml4LCBzdWZmaXgpIHtcbiAgICAgICAgICAgICAgICB2YXIgZnVuYyA9IHNjb3BlLmRlZihcbiAgICAgICAgICAgICAgICAgICdcIicsIHByZWZpeCwgc3VmZml4LCAnXCIgaW4gJywgdmFsdWUsXG4gICAgICAgICAgICAgICAgICAnPycsIHZhbHVlLCAnLicsIHByZWZpeCwgc3VmZml4LFxuICAgICAgICAgICAgICAgICAgJzonLCB2YWx1ZSwgJy4nLCBwcmVmaXgpXG5cbiAgICAgICAgICAgICAgICBjaGVjay5vcHRpb25hbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgICBlbnYuYXNzZXJ0KHNjb3BlLFxuICAgICAgICAgICAgICAgICAgICBmdW5jICsgJyBpbiAnICsgQkxFTkRfRlVOQ1MsXG4gICAgICAgICAgICAgICAgICAgICdpbnZhbGlkICcgKyBwcm9wICsgJy4nICsgcHJlZml4ICsgc3VmZml4ICsgJywgbXVzdCBiZSBvbmUgb2YgJyArIE9iamVjdC5rZXlzKGJsZW5kRnVuY3MpKVxuICAgICAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgICAgICByZXR1cm4gc2NvcGUuZGVmKEJMRU5EX0ZVTkNTLCAnWycsIGZ1bmMsICddJylcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIHZhciBTUkNfUkdCID0gcmVhZCgnc3JjJywgJ1JHQicpXG4gICAgICAgICAgICAgIHZhciBTUkNfQUxQSEEgPSByZWFkKCdzcmMnLCAnQWxwaGEnKVxuICAgICAgICAgICAgICB2YXIgRFNUX1JHQiA9IHJlYWQoJ2RzdCcsICdSR0InKVxuICAgICAgICAgICAgICB2YXIgRFNUX0FMUEhBID0gcmVhZCgnZHN0JywgJ0FscGhhJylcblxuICAgICAgICAgICAgICByZXR1cm4gW1NSQ19SR0IsIERTVF9SR0IsIFNSQ19BTFBIQSwgRFNUX0FMUEhBXVxuICAgICAgICAgICAgfSlcblxuICAgICAgICBjYXNlIFNfQkxFTkRfRVFVQVRJT046XG4gICAgICAgICAgcmV0dXJuIHBhcnNlUGFyYW0oXG4gICAgICAgICAgICBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICAgICAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgICBjaGVjay5jb21tYW5kUGFyYW1ldGVyKHZhbHVlLCBibGVuZEVxdWF0aW9ucywgJ2ludmFsaWQgJyArIHByb3ApXG4gICAgICAgICAgICAgICAgcmV0dXJuIFtcbiAgICAgICAgICAgICAgICAgIGJsZW5kRXF1YXRpb25zW3ZhbHVlXSxcbiAgICAgICAgICAgICAgICAgIGJsZW5kRXF1YXRpb25zW3ZhbHVlXVxuICAgICAgICAgICAgICAgIF1cbiAgICAgICAgICAgICAgfSBlbHNlIGlmICh0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnKSB7XG4gICAgICAgICAgICAgICAgY2hlY2suY29tbWFuZFBhcmFtZXRlcihcbiAgICAgICAgICAgICAgICAgIHZhbHVlLnJnYiwgYmxlbmRFcXVhdGlvbnMsIHByb3AgKyAnLnJnYicpXG4gICAgICAgICAgICAgICAgY2hlY2suY29tbWFuZFBhcmFtZXRlcihcbiAgICAgICAgICAgICAgICAgIHZhbHVlLmFscGhhLCBibGVuZEVxdWF0aW9ucywgcHJvcCArICcuYWxwaGEnKVxuICAgICAgICAgICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICAgICAgICBibGVuZEVxdWF0aW9uc1t2YWx1ZS5yZ2JdLFxuICAgICAgICAgICAgICAgICAgYmxlbmRFcXVhdGlvbnNbdmFsdWUuYWxwaGFdXG4gICAgICAgICAgICAgICAgXVxuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGNoZWNrLmNvbW1hbmRSYWlzZSgnaW52YWxpZCBibGVuZC5lcXVhdGlvbicpXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoZW52LCBzY29wZSwgdmFsdWUpIHtcbiAgICAgICAgICAgICAgdmFyIEJMRU5EX0VRVUFUSU9OUyA9IGVudi5jb25zdGFudHMuYmxlbmRFcXVhdGlvbnNcblxuICAgICAgICAgICAgICB2YXIgUkdCID0gc2NvcGUuZGVmKClcbiAgICAgICAgICAgICAgdmFyIEFMUEhBID0gc2NvcGUuZGVmKClcblxuICAgICAgICAgICAgICB2YXIgaWZ0ZSA9IGVudi5jb25kKCd0eXBlb2YgJywgdmFsdWUsICc9PT1cInN0cmluZ1wiJylcblxuICAgICAgICAgICAgICBjaGVjay5vcHRpb25hbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gY2hlY2tQcm9wIChibG9jaywgbmFtZSwgdmFsdWUpIHtcbiAgICAgICAgICAgICAgICAgIGVudi5hc3NlcnQoYmxvY2ssXG4gICAgICAgICAgICAgICAgICAgIHZhbHVlICsgJyBpbiAnICsgQkxFTkRfRVFVQVRJT05TLFxuICAgICAgICAgICAgICAgICAgICAnaW52YWxpZCAnICsgbmFtZSArICcsIG11c3QgYmUgb25lIG9mICcgKyBPYmplY3Qua2V5cyhibGVuZEVxdWF0aW9ucykpXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGNoZWNrUHJvcChpZnRlLnRoZW4sIHByb3AsIHZhbHVlKVxuXG4gICAgICAgICAgICAgICAgZW52LmFzc2VydChpZnRlLmVsc2UsXG4gICAgICAgICAgICAgICAgICB2YWx1ZSArICcmJnR5cGVvZiAnICsgdmFsdWUgKyAnPT09XCJvYmplY3RcIicsXG4gICAgICAgICAgICAgICAgICAnaW52YWxpZCAnICsgcHJvcClcbiAgICAgICAgICAgICAgICBjaGVja1Byb3AoaWZ0ZS5lbHNlLCBwcm9wICsgJy5yZ2InLCB2YWx1ZSArICcucmdiJylcbiAgICAgICAgICAgICAgICBjaGVja1Byb3AoaWZ0ZS5lbHNlLCBwcm9wICsgJy5hbHBoYScsIHZhbHVlICsgJy5hbHBoYScpXG4gICAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgICAgaWZ0ZS50aGVuKFxuICAgICAgICAgICAgICAgIFJHQiwgJz0nLCBBTFBIQSwgJz0nLCBCTEVORF9FUVVBVElPTlMsICdbJywgdmFsdWUsICddOycpXG4gICAgICAgICAgICAgIGlmdGUuZWxzZShcbiAgICAgICAgICAgICAgICBSR0IsICc9JywgQkxFTkRfRVFVQVRJT05TLCAnWycsIHZhbHVlLCAnLnJnYl07JyxcbiAgICAgICAgICAgICAgICBBTFBIQSwgJz0nLCBCTEVORF9FUVVBVElPTlMsICdbJywgdmFsdWUsICcuYWxwaGFdOycpXG5cbiAgICAgICAgICAgICAgc2NvcGUoaWZ0ZSlcblxuICAgICAgICAgICAgICByZXR1cm4gW1JHQiwgQUxQSEFdXG4gICAgICAgICAgICB9KVxuXG4gICAgICAgIGNhc2UgU19CTEVORF9DT0xPUjpcbiAgICAgICAgICByZXR1cm4gcGFyc2VQYXJhbShcbiAgICAgICAgICAgIGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgICAgICAgICAgICBjaGVjay5jb21tYW5kKFxuICAgICAgICAgICAgICAgIGlzQXJyYXlMaWtlKHZhbHVlKSAmJlxuICAgICAgICAgICAgICAgIHZhbHVlLmxlbmd0aCA9PT0gNCxcbiAgICAgICAgICAgICAgICAnYmxlbmQuY29sb3IgbXVzdCBiZSBhIDRkIGFycmF5JylcbiAgICAgICAgICAgICAgcmV0dXJuIGxvb3AoNCwgZnVuY3Rpb24gKGkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gK3ZhbHVlW2ldXG4gICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKGVudiwgc2NvcGUsIHZhbHVlKSB7XG4gICAgICAgICAgICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBlbnYuYXNzZXJ0KHNjb3BlLFxuICAgICAgICAgICAgICAgICAgZW52LnNoYXJlZC5pc0FycmF5TGlrZSArICcoJyArIHZhbHVlICsgJykmJicgK1xuICAgICAgICAgICAgICAgICAgdmFsdWUgKyAnLmxlbmd0aD09PTQnLFxuICAgICAgICAgICAgICAgICAgJ2JsZW5kLmNvbG9yIG11c3QgYmUgYSA0ZCBhcnJheScpXG4gICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgIHJldHVybiBsb29wKDQsIGZ1bmN0aW9uIChpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHNjb3BlLmRlZignKycsIHZhbHVlLCAnWycsIGksICddJylcbiAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH0pXG5cbiAgICAgICAgY2FzZSBTX1NURU5DSUxfTUFTSzpcbiAgICAgICAgICByZXR1cm4gcGFyc2VQYXJhbShcbiAgICAgICAgICAgIGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgICAgICAgICAgICBjaGVjay5jb21tYW5kVHlwZSh2YWx1ZSwgJ251bWJlcicsIHBhcmFtKVxuICAgICAgICAgICAgICByZXR1cm4gdmFsdWUgfCAwXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKGVudiwgc2NvcGUsIHZhbHVlKSB7XG4gICAgICAgICAgICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBlbnYuYXNzZXJ0KHNjb3BlLFxuICAgICAgICAgICAgICAgICAgJ3R5cGVvZiAnICsgdmFsdWUgKyAnPT09XCJudW1iZXJcIicsXG4gICAgICAgICAgICAgICAgICAnaW52YWxpZCBzdGVuY2lsLm1hc2snKVxuICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICByZXR1cm4gc2NvcGUuZGVmKHZhbHVlLCAnfDAnKVxuICAgICAgICAgICAgfSlcblxuICAgICAgICBjYXNlIFNfU1RFTkNJTF9GVU5DOlxuICAgICAgICAgIHJldHVybiBwYXJzZVBhcmFtKFxuICAgICAgICAgICAgZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgICAgICAgIGNoZWNrLmNvbW1hbmRUeXBlKHZhbHVlLCAnb2JqZWN0JywgcGFyYW0pXG4gICAgICAgICAgICAgIHZhciBjbXAgPSB2YWx1ZS5jbXAgfHwgJ2tlZXAnXG4gICAgICAgICAgICAgIHZhciByZWYgPSB2YWx1ZS5yZWYgfHwgMFxuICAgICAgICAgICAgICB2YXIgbWFzayA9ICdtYXNrJyBpbiB2YWx1ZSA/IHZhbHVlLm1hc2sgOiAtMVxuICAgICAgICAgICAgICBjaGVjay5jb21tYW5kUGFyYW1ldGVyKGNtcCwgY29tcGFyZUZ1bmNzLCBwcm9wICsgJy5jbXAnKVxuICAgICAgICAgICAgICBjaGVjay5jb21tYW5kVHlwZShyZWYsICdudW1iZXInLCBwcm9wICsgJy5yZWYnKVxuICAgICAgICAgICAgICBjaGVjay5jb21tYW5kVHlwZShtYXNrLCAnbnVtYmVyJywgcHJvcCArICcubWFzaycpXG4gICAgICAgICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICAgICAgY29tcGFyZUZ1bmNzW2NtcF0sXG4gICAgICAgICAgICAgICAgcmVmLFxuICAgICAgICAgICAgICAgIG1hc2tcbiAgICAgICAgICAgICAgXVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChlbnYsIHNjb3BlLCB2YWx1ZSkge1xuICAgICAgICAgICAgICB2YXIgQ09NUEFSRV9GVU5DUyA9IGVudi5jb25zdGFudHMuY29tcGFyZUZ1bmNzXG4gICAgICAgICAgICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBmdW5jdGlvbiBhc3NlcnQgKCkge1xuICAgICAgICAgICAgICAgICAgZW52LmFzc2VydChzY29wZSxcbiAgICAgICAgICAgICAgICAgICAgQXJyYXkucHJvdG90eXBlLmpvaW4uY2FsbChhcmd1bWVudHMsICcnKSxcbiAgICAgICAgICAgICAgICAgICAgJ2ludmFsaWQgc3RlbmNpbC5mdW5jJylcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYXNzZXJ0KHZhbHVlICsgJyYmdHlwZW9mICcsIHZhbHVlLCAnPT09XCJvYmplY3RcIicpXG4gICAgICAgICAgICAgICAgYXNzZXJ0KCchKFwiY21wXCIgaW4gJywgdmFsdWUsICcpfHwoJyxcbiAgICAgICAgICAgICAgICAgIHZhbHVlLCAnLmNtcCBpbiAnLCBDT01QQVJFX0ZVTkNTLCAnKScpXG4gICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgIHZhciBjbXAgPSBzY29wZS5kZWYoXG4gICAgICAgICAgICAgICAgJ1wiY21wXCIgaW4gJywgdmFsdWUsXG4gICAgICAgICAgICAgICAgJz8nLCBDT01QQVJFX0ZVTkNTLCAnWycsIHZhbHVlLCAnLmNtcF0nLFxuICAgICAgICAgICAgICAgICc6JywgR0xfS0VFUClcbiAgICAgICAgICAgICAgdmFyIHJlZiA9IHNjb3BlLmRlZih2YWx1ZSwgJy5yZWZ8MCcpXG4gICAgICAgICAgICAgIHZhciBtYXNrID0gc2NvcGUuZGVmKFxuICAgICAgICAgICAgICAgICdcIm1hc2tcIiBpbiAnLCB2YWx1ZSxcbiAgICAgICAgICAgICAgICAnPycsIHZhbHVlLCAnLm1hc2t8MDotMScpXG4gICAgICAgICAgICAgIHJldHVybiBbY21wLCByZWYsIG1hc2tdXG4gICAgICAgICAgICB9KVxuXG4gICAgICAgIGNhc2UgU19TVEVOQ0lMX09QRlJPTlQ6XG4gICAgICAgIGNhc2UgU19TVEVOQ0lMX09QQkFDSzpcbiAgICAgICAgICByZXR1cm4gcGFyc2VQYXJhbShcbiAgICAgICAgICAgIGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgICAgICAgICAgICBjaGVjay5jb21tYW5kVHlwZSh2YWx1ZSwgJ29iamVjdCcsIHBhcmFtKVxuICAgICAgICAgICAgICB2YXIgZmFpbCA9IHZhbHVlLmZhaWwgfHwgJ2tlZXAnXG4gICAgICAgICAgICAgIHZhciB6ZmFpbCA9IHZhbHVlLnpmYWlsIHx8ICdrZWVwJ1xuICAgICAgICAgICAgICB2YXIgcGFzcyA9IHZhbHVlLnBhc3MgfHwgJ2tlZXAnXG4gICAgICAgICAgICAgIGNoZWNrLmNvbW1hbmRQYXJhbWV0ZXIoZmFpbCwgc3RlbmNpbE9wcywgcHJvcCArICcuZmFpbCcpXG4gICAgICAgICAgICAgIGNoZWNrLmNvbW1hbmRQYXJhbWV0ZXIoemZhaWwsIHN0ZW5jaWxPcHMsIHByb3AgKyAnLnpmYWlsJylcbiAgICAgICAgICAgICAgY2hlY2suY29tbWFuZFBhcmFtZXRlcihwYXNzLCBzdGVuY2lsT3BzLCBwcm9wICsgJy5wYXNzJylcbiAgICAgICAgICAgICAgcmV0dXJuIFtcbiAgICAgICAgICAgICAgICBwcm9wID09PSBTX1NURU5DSUxfT1BCQUNLID8gR0xfQkFDSyA6IEdMX0ZST05ULFxuICAgICAgICAgICAgICAgIHN0ZW5jaWxPcHNbZmFpbF0sXG4gICAgICAgICAgICAgICAgc3RlbmNpbE9wc1t6ZmFpbF0sXG4gICAgICAgICAgICAgICAgc3RlbmNpbE9wc1twYXNzXVxuICAgICAgICAgICAgICBdXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKGVudiwgc2NvcGUsIHZhbHVlKSB7XG4gICAgICAgICAgICAgIHZhciBTVEVOQ0lMX09QUyA9IGVudi5jb25zdGFudHMuc3RlbmNpbE9wc1xuXG4gICAgICAgICAgICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBlbnYuYXNzZXJ0KHNjb3BlLFxuICAgICAgICAgICAgICAgICAgdmFsdWUgKyAnJiZ0eXBlb2YgJyArIHZhbHVlICsgJz09PVwib2JqZWN0XCInLFxuICAgICAgICAgICAgICAgICAgJ2ludmFsaWQgJyArIHByb3ApXG4gICAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgICAgZnVuY3Rpb24gcmVhZCAobmFtZSkge1xuICAgICAgICAgICAgICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICAgIGVudi5hc3NlcnQoc2NvcGUsXG4gICAgICAgICAgICAgICAgICAgICchKFwiJyArIG5hbWUgKyAnXCIgaW4gJyArIHZhbHVlICsgJyl8fCcgK1xuICAgICAgICAgICAgICAgICAgICAnKCcgKyB2YWx1ZSArICcuJyArIG5hbWUgKyAnIGluICcgKyBTVEVOQ0lMX09QUyArICcpJyxcbiAgICAgICAgICAgICAgICAgICAgJ2ludmFsaWQgJyArIHByb3AgKyAnLicgKyBuYW1lICsgJywgbXVzdCBiZSBvbmUgb2YgJyArIE9iamVjdC5rZXlzKHN0ZW5jaWxPcHMpKVxuICAgICAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgICAgICByZXR1cm4gc2NvcGUuZGVmKFxuICAgICAgICAgICAgICAgICAgJ1wiJywgbmFtZSwgJ1wiIGluICcsIHZhbHVlLFxuICAgICAgICAgICAgICAgICAgJz8nLCBTVEVOQ0lMX09QUywgJ1snLCB2YWx1ZSwgJy4nLCBuYW1lLCAnXTonLFxuICAgICAgICAgICAgICAgICAgR0xfS0VFUClcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICAgICAgcHJvcCA9PT0gU19TVEVOQ0lMX09QQkFDSyA/IEdMX0JBQ0sgOiBHTF9GUk9OVCxcbiAgICAgICAgICAgICAgICByZWFkKCdmYWlsJyksXG4gICAgICAgICAgICAgICAgcmVhZCgnemZhaWwnKSxcbiAgICAgICAgICAgICAgICByZWFkKCdwYXNzJylcbiAgICAgICAgICAgICAgXVxuICAgICAgICAgICAgfSlcblxuICAgICAgICBjYXNlIFNfUE9MWUdPTl9PRkZTRVRfT0ZGU0VUOlxuICAgICAgICAgIHJldHVybiBwYXJzZVBhcmFtKFxuICAgICAgICAgICAgZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgICAgICAgIGNoZWNrLmNvbW1hbmRUeXBlKHZhbHVlLCAnb2JqZWN0JywgcGFyYW0pXG4gICAgICAgICAgICAgIHZhciBmYWN0b3IgPSB2YWx1ZS5mYWN0b3IgfCAwXG4gICAgICAgICAgICAgIHZhciB1bml0cyA9IHZhbHVlLnVuaXRzIHwgMFxuICAgICAgICAgICAgICBjaGVjay5jb21tYW5kVHlwZShmYWN0b3IsICdudW1iZXInLCBwYXJhbSArICcuZmFjdG9yJylcbiAgICAgICAgICAgICAgY2hlY2suY29tbWFuZFR5cGUodW5pdHMsICdudW1iZXInLCBwYXJhbSArICcudW5pdHMnKVxuICAgICAgICAgICAgICByZXR1cm4gW2ZhY3RvciwgdW5pdHNdXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKGVudiwgc2NvcGUsIHZhbHVlKSB7XG4gICAgICAgICAgICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBlbnYuYXNzZXJ0KHNjb3BlLFxuICAgICAgICAgICAgICAgICAgdmFsdWUgKyAnJiZ0eXBlb2YgJyArIHZhbHVlICsgJz09PVwib2JqZWN0XCInLFxuICAgICAgICAgICAgICAgICAgJ2ludmFsaWQgJyArIHByb3ApXG4gICAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgICAgdmFyIEZBQ1RPUiA9IHNjb3BlLmRlZih2YWx1ZSwgJy5mYWN0b3J8MCcpXG4gICAgICAgICAgICAgIHZhciBVTklUUyA9IHNjb3BlLmRlZih2YWx1ZSwgJy51bml0c3wwJylcblxuICAgICAgICAgICAgICByZXR1cm4gW0ZBQ1RPUiwgVU5JVFNdXG4gICAgICAgICAgICB9KVxuXG4gICAgICAgIGNhc2UgU19DVUxMX0ZBQ0U6XG4gICAgICAgICAgcmV0dXJuIHBhcnNlUGFyYW0oXG4gICAgICAgICAgICBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICAgICAgICAgICAgdmFyIGZhY2UgPSAwXG4gICAgICAgICAgICAgIGlmICh2YWx1ZSA9PT0gJ2Zyb250Jykge1xuICAgICAgICAgICAgICAgIGZhY2UgPSBHTF9GUk9OVFxuICAgICAgICAgICAgICB9IGVsc2UgaWYgKHZhbHVlID09PSAnYmFjaycpIHtcbiAgICAgICAgICAgICAgICBmYWNlID0gR0xfQkFDS1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGNoZWNrLmNvbW1hbmQoISFmYWNlLCBwYXJhbSlcbiAgICAgICAgICAgICAgcmV0dXJuIGZhY2VcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoZW52LCBzY29wZSwgdmFsdWUpIHtcbiAgICAgICAgICAgICAgY2hlY2sub3B0aW9uYWwoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIGVudi5hc3NlcnQoc2NvcGUsXG4gICAgICAgICAgICAgICAgICB2YWx1ZSArICc9PT1cImZyb250XCJ8fCcgK1xuICAgICAgICAgICAgICAgICAgdmFsdWUgKyAnPT09XCJiYWNrXCInLFxuICAgICAgICAgICAgICAgICAgJ2ludmFsaWQgY3VsbC5mYWNlJylcbiAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgcmV0dXJuIHNjb3BlLmRlZih2YWx1ZSwgJz09PVwiZnJvbnRcIj8nLCBHTF9GUk9OVCwgJzonLCBHTF9CQUNLKVxuICAgICAgICAgICAgfSlcblxuICAgICAgICBjYXNlIFNfTElORV9XSURUSDpcbiAgICAgICAgICByZXR1cm4gcGFyc2VQYXJhbShcbiAgICAgICAgICAgIGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgICAgICAgICAgICBjaGVjay5jb21tYW5kKFxuICAgICAgICAgICAgICAgIHR5cGVvZiB2YWx1ZSA9PT0gJ251bWJlcicgJiZcbiAgICAgICAgICAgICAgICB2YWx1ZSA+PSBsaW1pdHMubGluZVdpZHRoRGltc1swXSAmJlxuICAgICAgICAgICAgICAgIHZhbHVlIDw9IGxpbWl0cy5saW5lV2lkdGhEaW1zWzFdLFxuICAgICAgICAgICAgICAgICdpbnZhbGlkIGxpbmUgd2lkdGgsIG11c3QgcG9zaXRpdmUgbnVtYmVyIGJldHdlZW4gJyArXG4gICAgICAgICAgICAgICAgbGltaXRzLmxpbmVXaWR0aERpbXNbMF0gKyAnIGFuZCAnICsgbGltaXRzLmxpbmVXaWR0aERpbXNbMV0pXG4gICAgICAgICAgICAgIHJldHVybiB2YWx1ZVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGZ1bmN0aW9uIChlbnYsIHNjb3BlLCB2YWx1ZSkge1xuICAgICAgICAgICAgICBjaGVjay5vcHRpb25hbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgZW52LmFzc2VydChzY29wZSxcbiAgICAgICAgICAgICAgICAgICd0eXBlb2YgJyArIHZhbHVlICsgJz09PVwibnVtYmVyXCImJicgK1xuICAgICAgICAgICAgICAgICAgdmFsdWUgKyAnPj0nICsgbGltaXRzLmxpbmVXaWR0aERpbXNbMF0gKyAnJiYnICtcbiAgICAgICAgICAgICAgICAgIHZhbHVlICsgJzw9JyArIGxpbWl0cy5saW5lV2lkdGhEaW1zWzFdLFxuICAgICAgICAgICAgICAgICAgJ2ludmFsaWQgbGluZSB3aWR0aCcpXG4gICAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgICAgcmV0dXJuIHZhbHVlXG4gICAgICAgICAgICB9KVxuXG4gICAgICAgIGNhc2UgU19GUk9OVF9GQUNFOlxuICAgICAgICAgIHJldHVybiBwYXJzZVBhcmFtKFxuICAgICAgICAgICAgZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgICAgICAgIGNoZWNrLmNvbW1hbmRQYXJhbWV0ZXIodmFsdWUsIG9yaWVudGF0aW9uVHlwZSwgcGFyYW0pXG4gICAgICAgICAgICAgIHJldHVybiBvcmllbnRhdGlvblR5cGVbdmFsdWVdXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKGVudiwgc2NvcGUsIHZhbHVlKSB7XG4gICAgICAgICAgICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBlbnYuYXNzZXJ0KHNjb3BlLFxuICAgICAgICAgICAgICAgICAgdmFsdWUgKyAnPT09XCJjd1wifHwnICtcbiAgICAgICAgICAgICAgICAgIHZhbHVlICsgJz09PVwiY2N3XCInLFxuICAgICAgICAgICAgICAgICAgJ2ludmFsaWQgZnJvbnRGYWNlLCBtdXN0IGJlIG9uZSBvZiBjdyxjY3cnKVxuICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICByZXR1cm4gc2NvcGUuZGVmKHZhbHVlICsgJz09PVwiY3dcIj8nICsgR0xfQ1cgKyAnOicgKyBHTF9DQ1cpXG4gICAgICAgICAgICB9KVxuXG4gICAgICAgIGNhc2UgU19DT0xPUl9NQVNLOlxuICAgICAgICAgIHJldHVybiBwYXJzZVBhcmFtKFxuICAgICAgICAgICAgZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgICAgICAgIGNoZWNrLmNvbW1hbmQoXG4gICAgICAgICAgICAgICAgaXNBcnJheUxpa2UodmFsdWUpICYmIHZhbHVlLmxlbmd0aCA9PT0gNCxcbiAgICAgICAgICAgICAgICAnY29sb3IubWFzayBtdXN0IGJlIGxlbmd0aCA0IGFycmF5JylcbiAgICAgICAgICAgICAgcmV0dXJuIHZhbHVlLm1hcChmdW5jdGlvbiAodikgeyByZXR1cm4gISF2IH0pXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZnVuY3Rpb24gKGVudiwgc2NvcGUsIHZhbHVlKSB7XG4gICAgICAgICAgICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgICBlbnYuYXNzZXJ0KHNjb3BlLFxuICAgICAgICAgICAgICAgICAgZW52LnNoYXJlZC5pc0FycmF5TGlrZSArICcoJyArIHZhbHVlICsgJykmJicgK1xuICAgICAgICAgICAgICAgICAgdmFsdWUgKyAnLmxlbmd0aD09PTQnLFxuICAgICAgICAgICAgICAgICAgJ2ludmFsaWQgY29sb3IubWFzaycpXG4gICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgIHJldHVybiBsb29wKDQsIGZ1bmN0aW9uIChpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuICchIScgKyB2YWx1ZSArICdbJyArIGkgKyAnXSdcbiAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH0pXG5cbiAgICAgICAgY2FzZSBTX1NBTVBMRV9DT1ZFUkFHRTpcbiAgICAgICAgICByZXR1cm4gcGFyc2VQYXJhbShcbiAgICAgICAgICAgIGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgICAgICAgICAgICBjaGVjay5jb21tYW5kKHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcgJiYgdmFsdWUsIHBhcmFtKVxuICAgICAgICAgICAgICB2YXIgc2FtcGxlVmFsdWUgPSAndmFsdWUnIGluIHZhbHVlID8gdmFsdWUudmFsdWUgOiAxXG4gICAgICAgICAgICAgIHZhciBzYW1wbGVJbnZlcnQgPSAhIXZhbHVlLmludmVydFxuICAgICAgICAgICAgICBjaGVjay5jb21tYW5kKFxuICAgICAgICAgICAgICAgIHR5cGVvZiBzYW1wbGVWYWx1ZSA9PT0gJ251bWJlcicgJiZcbiAgICAgICAgICAgICAgICBzYW1wbGVWYWx1ZSA+PSAwICYmIHNhbXBsZVZhbHVlIDw9IDEsXG4gICAgICAgICAgICAgICAgJ3NhbXBsZS5jb3ZlcmFnZS52YWx1ZSBtdXN0IGJlIGEgbnVtYmVyIGJldHdlZW4gMCBhbmQgMScpXG4gICAgICAgICAgICAgIHJldHVybiBbc2FtcGxlVmFsdWUsIHNhbXBsZUludmVydF1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBmdW5jdGlvbiAoZW52LCBzY29wZSwgdmFsdWUpIHtcbiAgICAgICAgICAgICAgY2hlY2sub3B0aW9uYWwoZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgICAgIGVudi5hc3NlcnQoc2NvcGUsXG4gICAgICAgICAgICAgICAgICB2YWx1ZSArICcmJnR5cGVvZiAnICsgdmFsdWUgKyAnPT09XCJvYmplY3RcIicsXG4gICAgICAgICAgICAgICAgICAnaW52YWxpZCBzYW1wbGUuY292ZXJhZ2UnKVxuICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICB2YXIgVkFMVUUgPSBzY29wZS5kZWYoXG4gICAgICAgICAgICAgICAgJ1widmFsdWVcIiBpbiAnLCB2YWx1ZSwgJz8rJywgdmFsdWUsICcudmFsdWU6MScpXG4gICAgICAgICAgICAgIHZhciBJTlZFUlQgPSBzY29wZS5kZWYoJyEhJywgdmFsdWUsICcuaW52ZXJ0JylcbiAgICAgICAgICAgICAgcmV0dXJuIFtWQUxVRSwgSU5WRVJUXVxuICAgICAgICAgICAgfSlcbiAgICAgIH1cbiAgICB9KVxuXG4gICAgcmV0dXJuIFNUQVRFXG4gIH1cblxuICBmdW5jdGlvbiBwYXJzZU9wdGlvbnMgKG9wdGlvbnMpIHtcbiAgICB2YXIgc3RhdGljT3B0aW9ucyA9IG9wdGlvbnMuc3RhdGljXG4gICAgdmFyIGR5bmFtaWNPcHRpb25zID0gb3B0aW9ucy5keW5hbWljXG5cbiAgICBjaGVjay5vcHRpb25hbChmdW5jdGlvbiAoKSB7XG4gICAgICB2YXIgY29tbWFuZCA9IGNoZWNrLmd1ZXNzQ29tbWFuZCgpXG5cbiAgICAgIHZhciBLRVlfTkFNRVMgPSBbXG4gICAgICAgIFNfRlJBTUVCVUZGRVIsXG4gICAgICAgIFNfVkVSVCxcbiAgICAgICAgU19GUkFHLFxuICAgICAgICBTX0VMRU1FTlRTLFxuICAgICAgICBTX1BSSU1JVElWRSxcbiAgICAgICAgU19PRkZTRVQsXG4gICAgICAgIFNfQ09VTlQsXG4gICAgICAgIFNfSU5TVEFOQ0VTLFxuICAgICAgICBTX1BST0ZJTEVcbiAgICAgIF0uY29uY2F0KEdMX1NUQVRFX05BTUVTKVxuXG4gICAgICBmdW5jdGlvbiBjaGVja0tleXMgKGRpY3QpIHtcbiAgICAgICAgT2JqZWN0LmtleXMoZGljdCkuZm9yRWFjaChmdW5jdGlvbiAoa2V5KSB7XG4gICAgICAgICAgY2hlY2suY29tbWFuZChcbiAgICAgICAgICAgIEtFWV9OQU1FUy5pbmRleE9mKGtleSkgPj0gMCxcbiAgICAgICAgICAgICd1bmtub3duIHBhcmFtZXRlciBcIicgKyBrZXkgKyAnXCInLFxuICAgICAgICAgICAgY29tbWFuZClcbiAgICAgICAgfSlcbiAgICAgIH1cblxuICAgICAgY2hlY2tLZXlzKHN0YXRpY09wdGlvbnMpXG4gICAgICBjaGVja0tleXMoZHluYW1pY09wdGlvbnMpXG4gICAgfSlcblxuICAgIHZhciBmcmFtZWJ1ZmZlciA9IHBhcnNlRnJhbWVidWZmZXIob3B0aW9ucylcbiAgICB2YXIgdmlld3BvcnRBbmRTY2lzc29yID0gcGFyc2VWaWV3cG9ydFNjaXNzb3Iob3B0aW9ucywgZnJhbWVidWZmZXIpXG4gICAgdmFyIGRyYXcgPSBwYXJzZURyYXcob3B0aW9ucylcbiAgICB2YXIgc3RhdGUgPSBwYXJzZUdMU3RhdGUob3B0aW9ucylcbiAgICB2YXIgc2hhZGVyID0gcGFyc2VQcm9ncmFtKG9wdGlvbnMpXG5cbiAgICBmdW5jdGlvbiBjb3B5Qm94IChuYW1lKSB7XG4gICAgICB2YXIgZGVmbiA9IHZpZXdwb3J0QW5kU2Npc3NvcltuYW1lXVxuICAgICAgaWYgKGRlZm4pIHtcbiAgICAgICAgc3RhdGVbbmFtZV0gPSBkZWZuXG4gICAgICB9XG4gICAgfVxuICAgIGNvcHlCb3goU19WSUVXUE9SVClcbiAgICBjb3B5Qm94KHByb3BOYW1lKFNfU0NJU1NPUl9CT1gpKVxuXG4gICAgdmFyIGRpcnR5ID0gT2JqZWN0LmtleXMoc3RhdGUpLmxlbmd0aCA+IDBcblxuICAgIHJldHVybiB7XG4gICAgICBmcmFtZWJ1ZmZlcjogZnJhbWVidWZmZXIsXG4gICAgICBkcmF3OiBkcmF3LFxuICAgICAgc2hhZGVyOiBzaGFkZXIsXG4gICAgICBzdGF0ZTogc3RhdGUsXG4gICAgICBkaXJ0eTogZGlydHlcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBwYXJzZVVuaWZvcm1zICh1bmlmb3Jtcykge1xuICAgIHZhciBzdGF0aWNVbmlmb3JtcyA9IHVuaWZvcm1zLnN0YXRpY1xuICAgIHZhciBkeW5hbWljVW5pZm9ybXMgPSB1bmlmb3Jtcy5keW5hbWljXG5cbiAgICB2YXIgVU5JRk9STVMgPSB7fVxuXG4gICAgT2JqZWN0LmtleXMoc3RhdGljVW5pZm9ybXMpLmZvckVhY2goZnVuY3Rpb24gKG5hbWUpIHtcbiAgICAgIHZhciB2YWx1ZSA9IHN0YXRpY1VuaWZvcm1zW25hbWVdXG4gICAgICB2YXIgcmVzdWx0XG4gICAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnbnVtYmVyJyB8fFxuICAgICAgICAgIHR5cGVvZiB2YWx1ZSA9PT0gJ2Jvb2xlYW4nKSB7XG4gICAgICAgIHJlc3VsdCA9IGNyZWF0ZVN0YXRpY0RlY2woZnVuY3Rpb24gKCkge1xuICAgICAgICAgIHJldHVybiB2YWx1ZVxuICAgICAgICB9KVxuICAgICAgfSBlbHNlIGlmIChcbiAgICAgICAgdHlwZW9mIHZhbHVlID09PSAnZnVuY3Rpb24nICYmXG4gICAgICAgICh2YWx1ZS5fcmVnbFR5cGUgPT09ICd0ZXh0dXJlMmQnIHx8XG4gICAgICAgICB2YWx1ZS5fcmVnbFR5cGUgPT09ICd0ZXh0dXJlQ3ViZScpKSB7XG4gICAgICAgIHJlc3VsdCA9IGNyZWF0ZVN0YXRpY0RlY2woZnVuY3Rpb24gKGVudikge1xuICAgICAgICAgIHJldHVybiBlbnYubGluayh2YWx1ZSlcbiAgICAgICAgfSlcbiAgICAgIH0gZWxzZSBpZiAoaXNBcnJheUxpa2UodmFsdWUpKSB7XG4gICAgICAgIHJlc3VsdCA9IGNyZWF0ZVN0YXRpY0RlY2woZnVuY3Rpb24gKGVudikge1xuICAgICAgICAgIHZhciBJVEVNID0gZW52Lmdsb2JhbC5kZWYoJ1snLFxuICAgICAgICAgICAgbG9vcCh2YWx1ZS5sZW5ndGgsIGZ1bmN0aW9uIChpKSB7XG4gICAgICAgICAgICAgIGNoZWNrLmNvbW1hbmQoXG4gICAgICAgICAgICAgICAgdHlwZW9mIHZhbHVlW2ldID09PSAnbnVtYmVyJyB8fFxuICAgICAgICAgICAgICAgIHR5cGVvZiB2YWx1ZVtpXSA9PT0gJ2Jvb2xlYW4nLFxuICAgICAgICAgICAgICAgICdpbnZhbGlkIHVuaWZvcm0gJyArIG5hbWUpXG4gICAgICAgICAgICAgIHJldHVybiB2YWx1ZVtpXVxuICAgICAgICAgICAgfSksICddJylcbiAgICAgICAgICByZXR1cm4gSVRFTVxuICAgICAgICB9KVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY2hlY2suY29tbWFuZFJhaXNlKCdpbnZhbGlkIHVuaWZvcm0gJyArIG5hbWUpXG4gICAgICB9XG4gICAgICByZXN1bHQudmFsdWUgPSB2YWx1ZVxuICAgICAgVU5JRk9STVNbbmFtZV0gPSByZXN1bHRcbiAgICB9KVxuXG4gICAgT2JqZWN0LmtleXMoZHluYW1pY1VuaWZvcm1zKS5mb3JFYWNoKGZ1bmN0aW9uIChrZXkpIHtcbiAgICAgIHZhciBkeW4gPSBkeW5hbWljVW5pZm9ybXNba2V5XVxuICAgICAgVU5JRk9STVNba2V5XSA9IGNyZWF0ZUR5bmFtaWNEZWNsKGR5biwgZnVuY3Rpb24gKGVudiwgc2NvcGUpIHtcbiAgICAgICAgcmV0dXJuIGVudi5pbnZva2Uoc2NvcGUsIGR5bilcbiAgICAgIH0pXG4gICAgfSlcblxuICAgIHJldHVybiBVTklGT1JNU1xuICB9XG5cbiAgZnVuY3Rpb24gcGFyc2VBdHRyaWJ1dGVzIChhdHRyaWJ1dGVzKSB7XG4gICAgdmFyIHN0YXRpY0F0dHJpYnV0ZXMgPSBhdHRyaWJ1dGVzLnN0YXRpY1xuICAgIHZhciBkeW5hbWljQXR0cmlidXRlcyA9IGF0dHJpYnV0ZXMuZHluYW1pY1xuXG4gICAgdmFyIGF0dHJpYnV0ZURlZnMgPSB7fVxuXG4gICAgT2JqZWN0LmtleXMoc3RhdGljQXR0cmlidXRlcykuZm9yRWFjaChmdW5jdGlvbiAoYXR0cmlidXRlKSB7XG4gICAgICB2YXIgdmFsdWUgPSBzdGF0aWNBdHRyaWJ1dGVzW2F0dHJpYnV0ZV1cbiAgICAgIHZhciBpZCA9IHN0cmluZ1N0b3JlLmlkKGF0dHJpYnV0ZSlcblxuICAgICAgdmFyIHJlY29yZCA9IG5ldyBBdHRyaWJ1dGVSZWNvcmQoKVxuICAgICAgaWYgKGlzQnVmZmVyQXJncyh2YWx1ZSkpIHtcbiAgICAgICAgcmVjb3JkLnN0YXRlID0gQVRUUklCX1NUQVRFX1BPSU5URVJcbiAgICAgICAgcmVjb3JkLmJ1ZmZlciA9IGJ1ZmZlclN0YXRlLmdldEJ1ZmZlcihcbiAgICAgICAgICBidWZmZXJTdGF0ZS5jcmVhdGUodmFsdWUsIEdMX0FSUkFZX0JVRkZFUiwgZmFsc2UpKVxuICAgICAgICByZWNvcmQudHlwZSA9IHJlY29yZC5idWZmZXIuZHR5cGVcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHZhciBidWZmZXIgPSBidWZmZXJTdGF0ZS5nZXRCdWZmZXIodmFsdWUpXG4gICAgICAgIGlmIChidWZmZXIpIHtcbiAgICAgICAgICByZWNvcmQuc3RhdGUgPSBBVFRSSUJfU1RBVEVfUE9JTlRFUlxuICAgICAgICAgIHJlY29yZC5idWZmZXIgPSBidWZmZXJcbiAgICAgICAgICByZWNvcmQudHlwZSA9IGJ1ZmZlci5kdHlwZVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGNoZWNrLmNvbW1hbmQodHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSxcbiAgICAgICAgICAgICdpbnZhbGlkIGRhdGEgZm9yIGF0dHJpYnV0ZSAnICsgYXR0cmlidXRlKVxuICAgICAgICAgIGlmICh2YWx1ZS5jb25zdGFudCkge1xuICAgICAgICAgICAgdmFyIGNvbnN0YW50ID0gdmFsdWUuY29uc3RhbnRcbiAgICAgICAgICAgIHJlY29yZC5zdGF0ZSA9IEFUVFJJQl9TVEFURV9DT05TVEFOVFxuICAgICAgICAgICAgaWYgKHR5cGVvZiBjb25zdGFudCA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgICAgICAgcmVjb3JkLnggPSBjb25zdGFudFxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgY2hlY2suY29tbWFuZChcbiAgICAgICAgICAgICAgICBpc0FycmF5TGlrZShjb25zdGFudCkgJiZcbiAgICAgICAgICAgICAgICBjb25zdGFudC5sZW5ndGggPiAwICYmXG4gICAgICAgICAgICAgICAgY29uc3RhbnQubGVuZ3RoIDw9IDQsXG4gICAgICAgICAgICAgICAgJ2ludmFsaWQgY29uc3RhbnQgZm9yIGF0dHJpYnV0ZSAnICsgYXR0cmlidXRlKVxuICAgICAgICAgICAgICBDVVRFX0NPTVBPTkVOVFMuZm9yRWFjaChmdW5jdGlvbiAoYywgaSkge1xuICAgICAgICAgICAgICAgIGlmIChpIDwgY29uc3RhbnQubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICByZWNvcmRbY10gPSBjb25zdGFudFtpXVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgYnVmZmVyID0gYnVmZmVyU3RhdGUuZ2V0QnVmZmVyKHZhbHVlLmJ1ZmZlcilcbiAgICAgICAgICAgIGNoZWNrLmNvbW1hbmQoISFidWZmZXIsICdtaXNzaW5nIGJ1ZmZlciBmb3IgYXR0cmlidXRlIFwiJyArIGF0dHJpYnV0ZSArICdcIicpXG5cbiAgICAgICAgICAgIHZhciBvZmZzZXQgPSB2YWx1ZS5vZmZzZXQgfCAwXG4gICAgICAgICAgICBjaGVjay5jb21tYW5kKG9mZnNldCA+PSAwLFxuICAgICAgICAgICAgICAnaW52YWxpZCBvZmZzZXQgZm9yIGF0dHJpYnV0ZSBcIicgKyBhdHRyaWJ1dGUgKyAnXCInKVxuXG4gICAgICAgICAgICB2YXIgc3RyaWRlID0gdmFsdWUuc3RyaWRlIHwgMFxuICAgICAgICAgICAgY2hlY2suY29tbWFuZChzdHJpZGUgPj0gMCAmJiBzdHJpZGUgPCAyNTYsXG4gICAgICAgICAgICAgICdpbnZhbGlkIHN0cmlkZSBmb3IgYXR0cmlidXRlIFwiJyArIGF0dHJpYnV0ZSArICdcIiwgbXVzdCBiZSBpbnRlZ2VyIGJldHdlZWVuIFswLCAyNTVdJylcblxuICAgICAgICAgICAgdmFyIHNpemUgPSB2YWx1ZS5zaXplIHwgMFxuICAgICAgICAgICAgY2hlY2suY29tbWFuZCghKCdzaXplJyBpbiB2YWx1ZSkgfHwgKHNpemUgPiAwICYmIHNpemUgPD0gNCksXG4gICAgICAgICAgICAgICdpbnZhbGlkIHNpemUgZm9yIGF0dHJpYnV0ZSBcIicgKyBhdHRyaWJ1dGUgKyAnXCIsIG11c3QgYmUgMSwyLDMsNCcpXG5cbiAgICAgICAgICAgIHZhciBub3JtYWxpemVkID0gISF2YWx1ZS5ub3JtYWxpemVkXG5cbiAgICAgICAgICAgIHZhciB0eXBlID0gMFxuICAgICAgICAgICAgaWYgKCd0eXBlJyBpbiB2YWx1ZSkge1xuICAgICAgICAgICAgICBjaGVjay5jb21tYW5kUGFyYW1ldGVyKFxuICAgICAgICAgICAgICAgIHZhbHVlLnR5cGUsIGdsVHlwZXMsXG4gICAgICAgICAgICAgICAgJ2ludmFsaWQgdHlwZSBmb3IgYXR0cmlidXRlICcgKyBhdHRyaWJ1dGUpXG4gICAgICAgICAgICAgIHR5cGUgPSBnbFR5cGVzW3ZhbHVlLnR5cGVdXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBkaXZpc29yID0gdmFsdWUuZGl2aXNvciB8IDBcbiAgICAgICAgICAgIGlmICgnZGl2aXNvcicgaW4gdmFsdWUpIHtcbiAgICAgICAgICAgICAgY2hlY2suY29tbWFuZChkaXZpc29yID09PSAwIHx8IGV4dEluc3RhbmNpbmcsXG4gICAgICAgICAgICAgICAgJ2Nhbm5vdCBzcGVjaWZ5IGRpdmlzb3IgZm9yIGF0dHJpYnV0ZSBcIicgKyBhdHRyaWJ1dGUgKyAnXCIsIGluc3RhbmNpbmcgbm90IHN1cHBvcnRlZCcpXG4gICAgICAgICAgICAgIGNoZWNrLmNvbW1hbmQoZGl2aXNvciA+PSAwLFxuICAgICAgICAgICAgICAgICdpbnZhbGlkIGRpdmlzb3IgZm9yIGF0dHJpYnV0ZSBcIicgKyBhdHRyaWJ1dGUgKyAnXCInKVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjaGVjay5vcHRpb25hbChmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgIHZhciBjb21tYW5kID0gY2hlY2suZ3Vlc3NDb21tYW5kKClcblxuICAgICAgICAgICAgICB2YXIgVkFMSURfS0VZUyA9IFtcbiAgICAgICAgICAgICAgICAnYnVmZmVyJyxcbiAgICAgICAgICAgICAgICAnb2Zmc2V0JyxcbiAgICAgICAgICAgICAgICAnZGl2aXNvcicsXG4gICAgICAgICAgICAgICAgJ25vcm1hbGl6ZWQnLFxuICAgICAgICAgICAgICAgICd0eXBlJyxcbiAgICAgICAgICAgICAgICAnc2l6ZScsXG4gICAgICAgICAgICAgICAgJ3N0cmlkZSdcbiAgICAgICAgICAgICAgXVxuXG4gICAgICAgICAgICAgIE9iamVjdC5rZXlzKHZhbHVlKS5mb3JFYWNoKGZ1bmN0aW9uIChwcm9wKSB7XG4gICAgICAgICAgICAgICAgY2hlY2suY29tbWFuZChcbiAgICAgICAgICAgICAgICAgIFZBTElEX0tFWVMuaW5kZXhPZihwcm9wKSA+PSAwLFxuICAgICAgICAgICAgICAgICAgJ3Vua25vd24gcGFyYW1ldGVyIFwiJyArIHByb3AgKyAnXCIgZm9yIGF0dHJpYnV0ZSBwb2ludGVyIFwiJyArIGF0dHJpYnV0ZSArICdcIiAodmFsaWQgcGFyYW1ldGVycyBhcmUgJyArIFZBTElEX0tFWVMgKyAnKScsXG4gICAgICAgICAgICAgICAgICBjb21tYW5kKVxuICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfSlcblxuICAgICAgICAgICAgcmVjb3JkLmJ1ZmZlciA9IGJ1ZmZlclxuICAgICAgICAgICAgcmVjb3JkLnN0YXRlID0gQVRUUklCX1NUQVRFX1BPSU5URVJcbiAgICAgICAgICAgIHJlY29yZC5zaXplID0gc2l6ZVxuICAgICAgICAgICAgcmVjb3JkLm5vcm1hbGl6ZWQgPSBub3JtYWxpemVkXG4gICAgICAgICAgICByZWNvcmQudHlwZSA9IHR5cGUgfHwgYnVmZmVyLmR0eXBlXG4gICAgICAgICAgICByZWNvcmQub2Zmc2V0ID0gb2Zmc2V0XG4gICAgICAgICAgICByZWNvcmQuc3RyaWRlID0gc3RyaWRlXG4gICAgICAgICAgICByZWNvcmQuZGl2aXNvciA9IGRpdmlzb3JcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgYXR0cmlidXRlRGVmc1thdHRyaWJ1dGVdID0gY3JlYXRlU3RhdGljRGVjbChmdW5jdGlvbiAoZW52LCBzY29wZSkge1xuICAgICAgICB2YXIgY2FjaGUgPSBlbnYuYXR0cmliQ2FjaGVcbiAgICAgICAgaWYgKGlkIGluIGNhY2hlKSB7XG4gICAgICAgICAgcmV0dXJuIGNhY2hlW2lkXVxuICAgICAgICB9XG4gICAgICAgIHZhciByZXN1bHQgPSB7XG4gICAgICAgICAgaXNTdHJlYW06IGZhbHNlXG4gICAgICAgIH1cbiAgICAgICAgT2JqZWN0LmtleXMocmVjb3JkKS5mb3JFYWNoKGZ1bmN0aW9uIChrZXkpIHtcbiAgICAgICAgICByZXN1bHRba2V5XSA9IHJlY29yZFtrZXldXG4gICAgICAgIH0pXG4gICAgICAgIGlmIChyZWNvcmQuYnVmZmVyKSB7XG4gICAgICAgICAgcmVzdWx0LmJ1ZmZlciA9IGVudi5saW5rKHJlY29yZC5idWZmZXIpXG4gICAgICAgIH1cbiAgICAgICAgY2FjaGVbaWRdID0gcmVzdWx0XG4gICAgICAgIHJldHVybiByZXN1bHRcbiAgICAgIH0pXG4gICAgfSlcblxuICAgIE9iamVjdC5rZXlzKGR5bmFtaWNBdHRyaWJ1dGVzKS5mb3JFYWNoKGZ1bmN0aW9uIChhdHRyaWJ1dGUpIHtcbiAgICAgIHZhciBkeW4gPSBkeW5hbWljQXR0cmlidXRlc1thdHRyaWJ1dGVdXG5cbiAgICAgIGZ1bmN0aW9uIGFwcGVuZEF0dHJpYnV0ZUNvZGUgKGVudiwgYmxvY2spIHtcbiAgICAgICAgdmFyIFZBTFVFID0gZW52Lmludm9rZShibG9jaywgZHluKVxuXG4gICAgICAgIHZhciBzaGFyZWQgPSBlbnYuc2hhcmVkXG5cbiAgICAgICAgdmFyIElTX0JVRkZFUl9BUkdTID0gc2hhcmVkLmlzQnVmZmVyQXJnc1xuICAgICAgICB2YXIgQlVGRkVSX1NUQVRFID0gc2hhcmVkLmJ1ZmZlclxuXG4gICAgICAgIC8vIFBlcmZvcm0gdmFsaWRhdGlvbiBvbiBhdHRyaWJ1dGVcbiAgICAgICAgY2hlY2sub3B0aW9uYWwoZnVuY3Rpb24gKCkge1xuICAgICAgICAgIGVudi5hc3NlcnQoYmxvY2ssXG4gICAgICAgICAgICBWQUxVRSArICcmJih0eXBlb2YgJyArIFZBTFVFICsgJz09PVwib2JqZWN0XCJ8fHR5cGVvZiAnICtcbiAgICAgICAgICAgIFZBTFVFICsgJz09PVwiZnVuY3Rpb25cIikmJignICtcbiAgICAgICAgICAgIElTX0JVRkZFUl9BUkdTICsgJygnICsgVkFMVUUgKyAnKXx8JyArXG4gICAgICAgICAgICBCVUZGRVJfU1RBVEUgKyAnLmdldEJ1ZmZlcignICsgVkFMVUUgKyAnKXx8JyArXG4gICAgICAgICAgICBCVUZGRVJfU1RBVEUgKyAnLmdldEJ1ZmZlcignICsgVkFMVUUgKyAnLmJ1ZmZlcil8fCcgK1xuICAgICAgICAgICAgJyhcImNvbnN0YW50XCIgaW4gJyArIFZBTFVFICtcbiAgICAgICAgICAgICcmJih0eXBlb2YgJyArIFZBTFVFICsgJy5jb25zdGFudD09PVwibnVtYmVyXCJ8fCcgK1xuICAgICAgICAgICAgc2hhcmVkLmlzQXJyYXlMaWtlICsgJygnICsgVkFMVUUgKyAnKSkpKScsXG4gICAgICAgICAgICAnaW52YWxpZCBkeW5hbWljIGF0dHJpYnV0ZSBcIicgKyBhdHRyaWJ1dGUgKyAnXCInKVxuICAgICAgICB9KVxuXG4gICAgICAgIC8vIGFsbG9jYXRlIG5hbWVzIGZvciByZXN1bHRcbiAgICAgICAgdmFyIHJlc3VsdCA9IHtcbiAgICAgICAgICBpc1N0cmVhbTogYmxvY2suZGVmKGZhbHNlKVxuICAgICAgICB9XG4gICAgICAgIHZhciBkZWZhdWx0UmVjb3JkID0gbmV3IEF0dHJpYnV0ZVJlY29yZCgpXG4gICAgICAgIGRlZmF1bHRSZWNvcmQuc3RhdGUgPSBBVFRSSUJfU1RBVEVfUE9JTlRFUlxuICAgICAgICBPYmplY3Qua2V5cyhkZWZhdWx0UmVjb3JkKS5mb3JFYWNoKGZ1bmN0aW9uIChrZXkpIHtcbiAgICAgICAgICByZXN1bHRba2V5XSA9IGJsb2NrLmRlZignJyArIGRlZmF1bHRSZWNvcmRba2V5XSlcbiAgICAgICAgfSlcblxuICAgICAgICB2YXIgQlVGRkVSID0gcmVzdWx0LmJ1ZmZlclxuICAgICAgICB2YXIgVFlQRSA9IHJlc3VsdC50eXBlXG4gICAgICAgIGJsb2NrKFxuICAgICAgICAgICdpZignLCBJU19CVUZGRVJfQVJHUywgJygnLCBWQUxVRSwgJykpeycsXG4gICAgICAgICAgcmVzdWx0LmlzU3RyZWFtLCAnPXRydWU7JyxcbiAgICAgICAgICBCVUZGRVIsICc9JywgQlVGRkVSX1NUQVRFLCAnLmNyZWF0ZVN0cmVhbSgnLCBHTF9BUlJBWV9CVUZGRVIsICcsJywgVkFMVUUsICcpOycsXG4gICAgICAgICAgVFlQRSwgJz0nLCBCVUZGRVIsICcuZHR5cGU7JyxcbiAgICAgICAgICAnfWVsc2V7JyxcbiAgICAgICAgICBCVUZGRVIsICc9JywgQlVGRkVSX1NUQVRFLCAnLmdldEJ1ZmZlcignLCBWQUxVRSwgJyk7JyxcbiAgICAgICAgICAnaWYoJywgQlVGRkVSLCAnKXsnLFxuICAgICAgICAgIFRZUEUsICc9JywgQlVGRkVSLCAnLmR0eXBlOycsXG4gICAgICAgICAgJ31lbHNlIGlmKCcsIFZBTFVFLCAnLmNvbnN0YW50KXsnLFxuICAgICAgICAgIHJlc3VsdC5zdGF0ZSwgJz0nLCBBVFRSSUJfU1RBVEVfQ09OU1RBTlQsICc7JyxcbiAgICAgICAgICBDVVRFX0NPTVBPTkVOVFMubWFwKGZ1bmN0aW9uIChuYW1lLCBpKSB7XG4gICAgICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgICByZXN1bHRbbmFtZV0gKyAnPScgKyBWQUxVRSArICcubGVuZ3RoPj0nICsgaSArXG4gICAgICAgICAgICAgICc/JyArIFZBTFVFICsgJ1snICsgaSArICddOjA7J1xuICAgICAgICAgICAgKVxuICAgICAgICAgIH0pLmpvaW4oJycpLFxuICAgICAgICAgICd9ZWxzZXsnLFxuICAgICAgICAgIEJVRkZFUiwgJz0nLCBCVUZGRVJfU1RBVEUsICcuZ2V0QnVmZmVyKCcsIFZBTFVFLCAnLmJ1ZmZlcik7JyxcbiAgICAgICAgICBUWVBFLCAnPVwidHlwZVwiIGluICcsIFZBTFVFLCAnPycsXG4gICAgICAgICAgc2hhcmVkLmdsVHlwZXMsICdbJywgVkFMVUUsICcudHlwZV06JywgQlVGRkVSLCAnLmR0eXBlOycsXG4gICAgICAgICAgcmVzdWx0Lm5vcm1hbGl6ZWQsICc9ISEnLCBWQUxVRSwgJy5ub3JtYWxpemVkOycpXG4gICAgICAgIGZ1bmN0aW9uIGVtaXRSZWFkUmVjb3JkIChuYW1lKSB7XG4gICAgICAgICAgYmxvY2socmVzdWx0W25hbWVdLCAnPScsIFZBTFVFLCAnLicsIG5hbWUsICd8MDsnKVxuICAgICAgICB9XG4gICAgICAgIGVtaXRSZWFkUmVjb3JkKCdzaXplJylcbiAgICAgICAgZW1pdFJlYWRSZWNvcmQoJ29mZnNldCcpXG4gICAgICAgIGVtaXRSZWFkUmVjb3JkKCdzdHJpZGUnKVxuICAgICAgICBlbWl0UmVhZFJlY29yZCgnZGl2aXNvcicpXG5cbiAgICAgICAgYmxvY2soJ319JylcblxuICAgICAgICByZXR1cm4gcmVzdWx0XG4gICAgICB9XG5cbiAgICAgIGF0dHJpYnV0ZURlZnNbYXR0cmlidXRlXSA9IGNyZWF0ZUR5bmFtaWNEZWNsKGR5biwgYXBwZW5kQXR0cmlidXRlQ29kZSlcbiAgICB9KVxuXG4gICAgcmV0dXJuIGF0dHJpYnV0ZURlZnNcbiAgfVxuXG4gIGZ1bmN0aW9uIHBhcnNlQ29udGV4dCAoY29udGV4dCkge1xuICAgIHZhciBzdGF0aWNDb250ZXh0ID0gY29udGV4dC5zdGF0aWNcbiAgICB2YXIgZHluYW1pY0NvbnRleHQgPSBjb250ZXh0LmR5bmFtaWNcbiAgICB2YXIgcmVzdWx0ID0ge31cblxuICAgIE9iamVjdC5rZXlzKHN0YXRpY0NvbnRleHQpLmZvckVhY2goZnVuY3Rpb24gKG5hbWUpIHtcbiAgICAgIHZhciB2YWx1ZSA9IHN0YXRpY0NvbnRleHRbbmFtZV1cbiAgICAgIHJlc3VsdFtuYW1lXSA9IGNyZWF0ZVN0YXRpY0RlY2woZnVuY3Rpb24gKGVudiwgc2NvcGUpIHtcbiAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ251bWJlcicgfHwgdHlwZW9mIHZhbHVlID09PSAnYm9vbGVhbicpIHtcbiAgICAgICAgICByZXR1cm4gJycgKyB2YWx1ZVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiBlbnYubGluayh2YWx1ZSlcbiAgICAgICAgfVxuICAgICAgfSlcbiAgICB9KVxuXG4gICAgT2JqZWN0LmtleXMoZHluYW1pY0NvbnRleHQpLmZvckVhY2goZnVuY3Rpb24gKG5hbWUpIHtcbiAgICAgIHZhciBkeW4gPSBkeW5hbWljQ29udGV4dFtuYW1lXVxuICAgICAgcmVzdWx0W25hbWVdID0gY3JlYXRlRHluYW1pY0RlY2woZHluLCBmdW5jdGlvbiAoZW52LCBzY29wZSkge1xuICAgICAgICByZXR1cm4gZW52Lmludm9rZShzY29wZSwgZHluKVxuICAgICAgfSlcbiAgICB9KVxuXG4gICAgcmV0dXJuIHJlc3VsdFxuICB9XG5cbiAgZnVuY3Rpb24gcGFyc2VBcmd1bWVudHMgKG9wdGlvbnMsIGF0dHJpYnV0ZXMsIHVuaWZvcm1zLCBjb250ZXh0KSB7XG4gICAgdmFyIHJlc3VsdCA9IHBhcnNlT3B0aW9ucyhvcHRpb25zKVxuXG4gICAgcmVzdWx0LnByb2ZpbGUgPSBwYXJzZVByb2ZpbGUob3B0aW9ucylcbiAgICByZXN1bHQudW5pZm9ybXMgPSBwYXJzZVVuaWZvcm1zKHVuaWZvcm1zKVxuICAgIHJlc3VsdC5hdHRyaWJ1dGVzID0gcGFyc2VBdHRyaWJ1dGVzKGF0dHJpYnV0ZXMpXG4gICAgcmVzdWx0LmNvbnRleHQgPSBwYXJzZUNvbnRleHQoY29udGV4dClcbiAgICByZXR1cm4gcmVzdWx0XG4gIH1cblxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vIENPTU1PTiBVUERBVEUgRlVOQ1RJT05TXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgZnVuY3Rpb24gZW1pdENvbnRleHQgKGVudiwgc2NvcGUsIGNvbnRleHQpIHtcbiAgICB2YXIgc2hhcmVkID0gZW52LnNoYXJlZFxuICAgIHZhciBDT05URVhUID0gc2hhcmVkLmNvbnRleHRcblxuICAgIHZhciBjb250ZXh0RW50ZXIgPSBlbnYuc2NvcGUoKVxuXG4gICAgT2JqZWN0LmtleXMoY29udGV4dCkuZm9yRWFjaChmdW5jdGlvbiAobmFtZSkge1xuICAgICAgc2NvcGUuc2F2ZShDT05URVhULCAnLicgKyBuYW1lKVxuICAgICAgdmFyIGRlZm4gPSBjb250ZXh0W25hbWVdXG4gICAgICBjb250ZXh0RW50ZXIoQ09OVEVYVCwgJy4nLCBuYW1lLCAnPScsIGRlZm4uYXBwZW5kKGVudiwgc2NvcGUpLCAnOycpXG4gICAgfSlcblxuICAgIHNjb3BlKGNvbnRleHRFbnRlcilcbiAgfVxuXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gQ09NTU9OIERSQVdJTkcgRlVOQ1RJT05TXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgZnVuY3Rpb24gZW1pdFBvbGxGcmFtZWJ1ZmZlciAoZW52LCBzY29wZSwgZnJhbWVidWZmZXIpIHtcbiAgICB2YXIgc2hhcmVkID0gZW52LnNoYXJlZFxuXG4gICAgdmFyIEdMID0gc2hhcmVkLmdsXG4gICAgdmFyIEZSQU1FQlVGRkVSX1NUQVRFID0gc2hhcmVkLmZyYW1lYnVmZmVyXG4gICAgdmFyIEVYVF9EUkFXX0JVRkZFUlNcbiAgICBpZiAoZXh0RHJhd0J1ZmZlcnMpIHtcbiAgICAgIEVYVF9EUkFXX0JVRkZFUlMgPSBzY29wZS5kZWYoc2hhcmVkLmV4dGVuc2lvbnMsICcud2ViZ2xfZHJhd19idWZmZXJzJylcbiAgICB9XG5cbiAgICB2YXIgY29uc3RhbnRzID0gZW52LmNvbnN0YW50c1xuXG4gICAgdmFyIERSQVdfQlVGRkVSUyA9IGNvbnN0YW50cy5kcmF3QnVmZmVyXG4gICAgdmFyIEJBQ0tfQlVGRkVSID0gY29uc3RhbnRzLmJhY2tCdWZmZXJcblxuICAgIHZhciBORVhUXG4gICAgaWYgKGZyYW1lYnVmZmVyKSB7XG4gICAgICBORVhUID0gZnJhbWVidWZmZXIuYXBwZW5kKGVudiwgc2NvcGUpXG4gICAgfSBlbHNlIHtcbiAgICAgIE5FWFQgPSBzY29wZS5kZWYoRlJBTUVCVUZGRVJfU1RBVEUsICcubmV4dCcpXG4gICAgfVxuXG4gICAgc2NvcGUoXG4gICAgICAnaWYoJywgRlJBTUVCVUZGRVJfU1RBVEUsICcuZGlydHl8fCcsIE5FWFQsICchPT0nLCBGUkFNRUJVRkZFUl9TVEFURSwgJy5jdXIpeycsXG4gICAgICAnaWYoJywgTkVYVCwgJyl7JyxcbiAgICAgIEdMLCAnLmJpbmRGcmFtZWJ1ZmZlcignLCBHTF9GUkFNRUJVRkZFUiwgJywnLCBORVhULCAnLmZyYW1lYnVmZmVyKTsnKVxuICAgIGlmIChleHREcmF3QnVmZmVycykge1xuICAgICAgc2NvcGUoRVhUX0RSQVdfQlVGRkVSUywgJy5kcmF3QnVmZmVyc1dFQkdMKCcsXG4gICAgICAgIERSQVdfQlVGRkVSUywgJ1snLCBORVhULCAnLmNvbG9yQXR0YWNobWVudHMubGVuZ3RoXSk7JylcbiAgICB9XG4gICAgc2NvcGUoJ31lbHNleycsXG4gICAgICBHTCwgJy5iaW5kRnJhbWVidWZmZXIoJywgR0xfRlJBTUVCVUZGRVIsICcsbnVsbCk7JylcbiAgICBpZiAoZXh0RHJhd0J1ZmZlcnMpIHtcbiAgICAgIHNjb3BlKEVYVF9EUkFXX0JVRkZFUlMsICcuZHJhd0J1ZmZlcnNXRUJHTCgnLCBCQUNLX0JVRkZFUiwgJyk7JylcbiAgICB9XG4gICAgc2NvcGUoXG4gICAgICAnfScsXG4gICAgICBGUkFNRUJVRkZFUl9TVEFURSwgJy5jdXI9JywgTkVYVCwgJzsnLFxuICAgICAgRlJBTUVCVUZGRVJfU1RBVEUsICcuZGlydHk9ZmFsc2U7JyxcbiAgICAgICd9JylcbiAgfVxuXG4gIGZ1bmN0aW9uIGVtaXRQb2xsU3RhdGUgKGVudiwgc2NvcGUsIGFyZ3MpIHtcbiAgICB2YXIgc2hhcmVkID0gZW52LnNoYXJlZFxuXG4gICAgdmFyIEdMID0gc2hhcmVkLmdsXG5cbiAgICB2YXIgQ1VSUkVOVF9WQVJTID0gZW52LmN1cnJlbnRcbiAgICB2YXIgTkVYVF9WQVJTID0gZW52Lm5leHRcbiAgICB2YXIgQ1VSUkVOVF9TVEFURSA9IHNoYXJlZC5jdXJyZW50XG4gICAgdmFyIE5FWFRfU1RBVEUgPSBzaGFyZWQubmV4dFxuXG4gICAgdmFyIGJsb2NrID0gZW52LmNvbmQoQ1VSUkVOVF9TVEFURSwgJy5kaXJ0eScpXG5cbiAgICBHTF9TVEFURV9OQU1FUy5mb3JFYWNoKGZ1bmN0aW9uIChwcm9wKSB7XG4gICAgICB2YXIgcGFyYW0gPSBwcm9wTmFtZShwcm9wKVxuICAgICAgaWYgKHBhcmFtIGluIGFyZ3Muc3RhdGUpIHtcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIHZhciBORVhULCBDVVJSRU5UXG4gICAgICBpZiAocGFyYW0gaW4gTkVYVF9WQVJTKSB7XG4gICAgICAgIE5FWFQgPSBORVhUX1ZBUlNbcGFyYW1dXG4gICAgICAgIENVUlJFTlQgPSBDVVJSRU5UX1ZBUlNbcGFyYW1dXG4gICAgICAgIHZhciBwYXJ0cyA9IGxvb3AoY3VycmVudFN0YXRlW3BhcmFtXS5sZW5ndGgsIGZ1bmN0aW9uIChpKSB7XG4gICAgICAgICAgcmV0dXJuIGJsb2NrLmRlZihORVhULCAnWycsIGksICddJylcbiAgICAgICAgfSlcbiAgICAgICAgYmxvY2soZW52LmNvbmQocGFydHMubWFwKGZ1bmN0aW9uIChwLCBpKSB7XG4gICAgICAgICAgcmV0dXJuIHAgKyAnIT09JyArIENVUlJFTlQgKyAnWycgKyBpICsgJ10nXG4gICAgICAgIH0pLmpvaW4oJ3x8JykpXG4gICAgICAgICAgLnRoZW4oXG4gICAgICAgICAgICBHTCwgJy4nLCBHTF9WQVJJQUJMRVNbcGFyYW1dLCAnKCcsIHBhcnRzLCAnKTsnLFxuICAgICAgICAgICAgcGFydHMubWFwKGZ1bmN0aW9uIChwLCBpKSB7XG4gICAgICAgICAgICAgIHJldHVybiBDVVJSRU5UICsgJ1snICsgaSArICddPScgKyBwXG4gICAgICAgICAgICB9KS5qb2luKCc7JyksICc7JykpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBORVhUID0gYmxvY2suZGVmKE5FWFRfU1RBVEUsICcuJywgcGFyYW0pXG4gICAgICAgIHZhciBpZnRlID0gZW52LmNvbmQoTkVYVCwgJyE9PScsIENVUlJFTlRfU1RBVEUsICcuJywgcGFyYW0pXG4gICAgICAgIGJsb2NrKGlmdGUpXG4gICAgICAgIGlmIChwYXJhbSBpbiBHTF9GTEFHUykge1xuICAgICAgICAgIGlmdGUoXG4gICAgICAgICAgICBlbnYuY29uZChORVhUKVxuICAgICAgICAgICAgICAgIC50aGVuKEdMLCAnLmVuYWJsZSgnLCBHTF9GTEFHU1twYXJhbV0sICcpOycpXG4gICAgICAgICAgICAgICAgLmVsc2UoR0wsICcuZGlzYWJsZSgnLCBHTF9GTEFHU1twYXJhbV0sICcpOycpLFxuICAgICAgICAgICAgQ1VSUkVOVF9TVEFURSwgJy4nLCBwYXJhbSwgJz0nLCBORVhULCAnOycpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaWZ0ZShcbiAgICAgICAgICAgIEdMLCAnLicsIEdMX1ZBUklBQkxFU1twYXJhbV0sICcoJywgTkVYVCwgJyk7JyxcbiAgICAgICAgICAgIENVUlJFTlRfU1RBVEUsICcuJywgcGFyYW0sICc9JywgTkVYVCwgJzsnKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSlcbiAgICBpZiAoT2JqZWN0LmtleXMoYXJncy5zdGF0ZSkubGVuZ3RoID09PSAwKSB7XG4gICAgICBibG9jayhDVVJSRU5UX1NUQVRFLCAnLmRpcnR5PWZhbHNlOycpXG4gICAgfVxuICAgIHNjb3BlKGJsb2NrKVxuICB9XG5cbiAgZnVuY3Rpb24gZW1pdFNldE9wdGlvbnMgKGVudiwgc2NvcGUsIG9wdGlvbnMsIGZpbHRlcikge1xuICAgIHZhciBzaGFyZWQgPSBlbnYuc2hhcmVkXG4gICAgdmFyIENVUlJFTlRfVkFSUyA9IGVudi5jdXJyZW50XG4gICAgdmFyIENVUlJFTlRfU1RBVEUgPSBzaGFyZWQuY3VycmVudFxuICAgIHZhciBHTCA9IHNoYXJlZC5nbFxuICAgIHNvcnRTdGF0ZShPYmplY3Qua2V5cyhvcHRpb25zKSkuZm9yRWFjaChmdW5jdGlvbiAocGFyYW0pIHtcbiAgICAgIHZhciBkZWZuID0gb3B0aW9uc1twYXJhbV1cbiAgICAgIGlmIChmaWx0ZXIgJiYgIWZpbHRlcihkZWZuKSkge1xuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICAgIHZhciB2YXJpYWJsZSA9IGRlZm4uYXBwZW5kKGVudiwgc2NvcGUpXG4gICAgICBpZiAoR0xfRkxBR1NbcGFyYW1dKSB7XG4gICAgICAgIHZhciBmbGFnID0gR0xfRkxBR1NbcGFyYW1dXG4gICAgICAgIGlmIChpc1N0YXRpYyhkZWZuKSkge1xuICAgICAgICAgIGlmICh2YXJpYWJsZSkge1xuICAgICAgICAgICAgc2NvcGUoR0wsICcuZW5hYmxlKCcsIGZsYWcsICcpOycpXG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHNjb3BlKEdMLCAnLmRpc2FibGUoJywgZmxhZywgJyk7JylcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgc2NvcGUoZW52LmNvbmQodmFyaWFibGUpXG4gICAgICAgICAgICAudGhlbihHTCwgJy5lbmFibGUoJywgZmxhZywgJyk7JylcbiAgICAgICAgICAgIC5lbHNlKEdMLCAnLmRpc2FibGUoJywgZmxhZywgJyk7JykpXG4gICAgICAgIH1cbiAgICAgICAgc2NvcGUoQ1VSUkVOVF9TVEFURSwgJy4nLCBwYXJhbSwgJz0nLCB2YXJpYWJsZSwgJzsnKVxuICAgICAgfSBlbHNlIGlmIChpc0FycmF5TGlrZSh2YXJpYWJsZSkpIHtcbiAgICAgICAgdmFyIENVUlJFTlQgPSBDVVJSRU5UX1ZBUlNbcGFyYW1dXG4gICAgICAgIHNjb3BlKFxuICAgICAgICAgIEdMLCAnLicsIEdMX1ZBUklBQkxFU1twYXJhbV0sICcoJywgdmFyaWFibGUsICcpOycsXG4gICAgICAgICAgdmFyaWFibGUubWFwKGZ1bmN0aW9uICh2LCBpKSB7XG4gICAgICAgICAgICByZXR1cm4gQ1VSUkVOVCArICdbJyArIGkgKyAnXT0nICsgdlxuICAgICAgICAgIH0pLmpvaW4oJzsnKSwgJzsnKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgc2NvcGUoXG4gICAgICAgICAgR0wsICcuJywgR0xfVkFSSUFCTEVTW3BhcmFtXSwgJygnLCB2YXJpYWJsZSwgJyk7JyxcbiAgICAgICAgICBDVVJSRU5UX1NUQVRFLCAnLicsIHBhcmFtLCAnPScsIHZhcmlhYmxlLCAnOycpXG4gICAgICB9XG4gICAgfSlcbiAgfVxuXG4gIGZ1bmN0aW9uIGluamVjdEV4dGVuc2lvbnMgKGVudiwgc2NvcGUpIHtcbiAgICBpZiAoZXh0SW5zdGFuY2luZyAmJiAhZW52Lmluc3RhbmNpbmcpIHtcbiAgICAgIGVudi5pbnN0YW5jaW5nID0gc2NvcGUuZGVmKFxuICAgICAgICBlbnYuc2hhcmVkLmV4dGVuc2lvbnMsICcuYW5nbGVfaW5zdGFuY2VkX2FycmF5cycpXG4gICAgfVxuICB9XG5cbiAgZnVuY3Rpb24gZW1pdFByb2ZpbGUgKGVudiwgc2NvcGUsIGFyZ3MsIHVzZVNjb3BlLCBpbmNyZW1lbnRDb3VudGVyKSB7XG4gICAgdmFyIHNoYXJlZCA9IGVudi5zaGFyZWRcbiAgICB2YXIgU1RBVFMgPSBlbnYuc3RhdHNcbiAgICB2YXIgQ1VSUkVOVF9TVEFURSA9IHNoYXJlZC5jdXJyZW50XG4gICAgdmFyIFRJTUVSID0gc2hhcmVkLnRpbWVyXG4gICAgdmFyIHByb2ZpbGVBcmcgPSBhcmdzLnByb2ZpbGVcblxuICAgIGZ1bmN0aW9uIHBlcmZDb3VudGVyICgpIHtcbiAgICAgIGlmICh0eXBlb2YgcGVyZm9ybWFuY2UgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgIHJldHVybiAnRGF0ZS5ub3coKSdcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiAncGVyZm9ybWFuY2Uubm93KCknXG4gICAgICB9XG4gICAgfVxuXG4gICAgdmFyIENQVV9TVEFSVCwgUVVFUllfQ09VTlRFUlxuICAgIGZ1bmN0aW9uIGVtaXRQcm9maWxlU3RhcnQgKGJsb2NrKSB7XG4gICAgICBDUFVfU1RBUlQgPSBzY29wZS5kZWYoKVxuICAgICAgYmxvY2soQ1BVX1NUQVJULCAnPScsIHBlcmZDb3VudGVyKCksICc7JylcbiAgICAgIGlmICh0eXBlb2YgaW5jcmVtZW50Q291bnRlciA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgYmxvY2soU1RBVFMsICcuY291bnQrPScsIGluY3JlbWVudENvdW50ZXIsICc7JylcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGJsb2NrKFNUQVRTLCAnLmNvdW50Kys7JylcbiAgICAgIH1cbiAgICAgIGlmICh0aW1lcikge1xuICAgICAgICBpZiAodXNlU2NvcGUpIHtcbiAgICAgICAgICBRVUVSWV9DT1VOVEVSID0gc2NvcGUuZGVmKClcbiAgICAgICAgICBibG9jayhRVUVSWV9DT1VOVEVSLCAnPScsIFRJTUVSLCAnLmdldE51bVBlbmRpbmdRdWVyaWVzKCk7JylcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBibG9jayhUSU1FUiwgJy5iZWdpblF1ZXJ5KCcsIFNUQVRTLCAnKTsnKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gZW1pdFByb2ZpbGVFbmQgKGJsb2NrKSB7XG4gICAgICBibG9jayhTVEFUUywgJy5jcHVUaW1lKz0nLCBwZXJmQ291bnRlcigpLCAnLScsIENQVV9TVEFSVCwgJzsnKVxuICAgICAgaWYgKHRpbWVyKSB7XG4gICAgICAgIGlmICh1c2VTY29wZSkge1xuICAgICAgICAgIGJsb2NrKFRJTUVSLCAnLnB1c2hTY29wZVN0YXRzKCcsXG4gICAgICAgICAgICBRVUVSWV9DT1VOVEVSLCAnLCcsXG4gICAgICAgICAgICBUSU1FUiwgJy5nZXROdW1QZW5kaW5nUXVlcmllcygpLCcsXG4gICAgICAgICAgICBTVEFUUywgJyk7JylcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBibG9jayhUSU1FUiwgJy5lbmRRdWVyeSgpOycpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiBzY29wZVByb2ZpbGUgKHZhbHVlKSB7XG4gICAgICB2YXIgcHJldiA9IHNjb3BlLmRlZihDVVJSRU5UX1NUQVRFLCAnLnByb2ZpbGUnKVxuICAgICAgc2NvcGUoQ1VSUkVOVF9TVEFURSwgJy5wcm9maWxlPScsIHZhbHVlLCAnOycpXG4gICAgICBzY29wZS5leGl0KENVUlJFTlRfU1RBVEUsICcucHJvZmlsZT0nLCBwcmV2LCAnOycpXG4gICAgfVxuXG4gICAgdmFyIFVTRV9QUk9GSUxFXG4gICAgaWYgKHByb2ZpbGVBcmcpIHtcbiAgICAgIGlmIChpc1N0YXRpYyhwcm9maWxlQXJnKSkge1xuICAgICAgICBpZiAocHJvZmlsZUFyZy5lbmFibGUpIHtcbiAgICAgICAgICBlbWl0UHJvZmlsZVN0YXJ0KHNjb3BlKVxuICAgICAgICAgIGVtaXRQcm9maWxlRW5kKHNjb3BlLmV4aXQpXG4gICAgICAgICAgc2NvcGVQcm9maWxlKCd0cnVlJylcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBzY29wZVByb2ZpbGUoJ2ZhbHNlJylcbiAgICAgICAgfVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICAgIFVTRV9QUk9GSUxFID0gcHJvZmlsZUFyZy5hcHBlbmQoZW52LCBzY29wZSlcbiAgICAgIHNjb3BlUHJvZmlsZShVU0VfUFJPRklMRSlcbiAgICB9IGVsc2Uge1xuICAgICAgVVNFX1BST0ZJTEUgPSBzY29wZS5kZWYoQ1VSUkVOVF9TVEFURSwgJy5wcm9maWxlJylcbiAgICB9XG5cbiAgICB2YXIgc3RhcnQgPSBlbnYuYmxvY2soKVxuICAgIGVtaXRQcm9maWxlU3RhcnQoc3RhcnQpXG4gICAgc2NvcGUoJ2lmKCcsIFVTRV9QUk9GSUxFLCAnKXsnLCBzdGFydCwgJ30nKVxuICAgIHZhciBlbmQgPSBlbnYuYmxvY2soKVxuICAgIGVtaXRQcm9maWxlRW5kKGVuZClcbiAgICBzY29wZS5leGl0KCdpZignLCBVU0VfUFJPRklMRSwgJyl7JywgZW5kLCAnfScpXG4gIH1cblxuICBmdW5jdGlvbiBlbWl0QXR0cmlidXRlcyAoZW52LCBzY29wZSwgYXJncywgYXR0cmlidXRlcywgZmlsdGVyKSB7XG4gICAgdmFyIHNoYXJlZCA9IGVudi5zaGFyZWRcblxuICAgIGZ1bmN0aW9uIHR5cGVMZW5ndGggKHgpIHtcbiAgICAgIHN3aXRjaCAoeCkge1xuICAgICAgICBjYXNlIEdMX0ZMT0FUX1ZFQzI6XG4gICAgICAgIGNhc2UgR0xfSU5UX1ZFQzI6XG4gICAgICAgIGNhc2UgR0xfQk9PTF9WRUMyOlxuICAgICAgICAgIHJldHVybiAyXG4gICAgICAgIGNhc2UgR0xfRkxPQVRfVkVDMzpcbiAgICAgICAgY2FzZSBHTF9JTlRfVkVDMzpcbiAgICAgICAgY2FzZSBHTF9CT09MX1ZFQzM6XG4gICAgICAgICAgcmV0dXJuIDNcbiAgICAgICAgY2FzZSBHTF9GTE9BVF9WRUM0OlxuICAgICAgICBjYXNlIEdMX0lOVF9WRUM0OlxuICAgICAgICBjYXNlIEdMX0JPT0xfVkVDNDpcbiAgICAgICAgICByZXR1cm4gNFxuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHJldHVybiAxXG4gICAgICB9XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gZW1pdEJpbmRBdHRyaWJ1dGUgKEFUVFJJQlVURSwgc2l6ZSwgcmVjb3JkKSB7XG4gICAgICB2YXIgR0wgPSBzaGFyZWQuZ2xcblxuICAgICAgdmFyIExPQ0FUSU9OID0gc2NvcGUuZGVmKEFUVFJJQlVURSwgJy5sb2NhdGlvbicpXG4gICAgICB2YXIgQklORElORyA9IHNjb3BlLmRlZihzaGFyZWQuYXR0cmlidXRlcywgJ1snLCBMT0NBVElPTiwgJ10nKVxuXG4gICAgICB2YXIgU1RBVEUgPSByZWNvcmQuc3RhdGVcbiAgICAgIHZhciBCVUZGRVIgPSByZWNvcmQuYnVmZmVyXG4gICAgICB2YXIgQ09OU1RfQ09NUE9ORU5UUyA9IFtcbiAgICAgICAgcmVjb3JkLngsXG4gICAgICAgIHJlY29yZC55LFxuICAgICAgICByZWNvcmQueixcbiAgICAgICAgcmVjb3JkLndcbiAgICAgIF1cblxuICAgICAgdmFyIENPTU1PTl9LRVlTID0gW1xuICAgICAgICAnYnVmZmVyJyxcbiAgICAgICAgJ25vcm1hbGl6ZWQnLFxuICAgICAgICAnb2Zmc2V0JyxcbiAgICAgICAgJ3N0cmlkZSdcbiAgICAgIF1cblxuICAgICAgZnVuY3Rpb24gZW1pdEJ1ZmZlciAoKSB7XG4gICAgICAgIHNjb3BlKFxuICAgICAgICAgICdpZighJywgQklORElORywgJy5wb2ludGVyKXsnLFxuICAgICAgICAgIEdMLCAnLmVuYWJsZVZlcnRleEF0dHJpYkFycmF5KCcsIExPQ0FUSU9OLCAnKTsnLFxuICAgICAgICAgIEJJTkRJTkcsICcucG9pbnRlcj10cnVlO30nKVxuXG4gICAgICAgIHZhciBUWVBFID0gcmVjb3JkLnR5cGVcbiAgICAgICAgdmFyIFNJWkVcbiAgICAgICAgaWYgKCFyZWNvcmQuc2l6ZSkge1xuICAgICAgICAgIFNJWkUgPSBzaXplXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgU0laRSA9IHNjb3BlLmRlZihyZWNvcmQuc2l6ZSwgJ3x8Jywgc2l6ZSlcbiAgICAgICAgfVxuXG4gICAgICAgIHNjb3BlKCdpZignLFxuICAgICAgICAgIEJJTkRJTkcsICcudHlwZSE9PScsIFRZUEUsICd8fCcsXG4gICAgICAgICAgQklORElORywgJy5zaXplIT09JywgU0laRSwgJ3x8JyxcbiAgICAgICAgICBDT01NT05fS0VZUy5tYXAoZnVuY3Rpb24gKGtleSkge1xuICAgICAgICAgICAgcmV0dXJuIEJJTkRJTkcgKyAnLicgKyBrZXkgKyAnIT09JyArIHJlY29yZFtrZXldXG4gICAgICAgICAgfSkuam9pbignfHwnKSxcbiAgICAgICAgICAnKXsnLFxuICAgICAgICAgIEdMLCAnLmJpbmRCdWZmZXIoJywgR0xfQVJSQVlfQlVGRkVSLCAnLCcsIEJVRkZFUiwgJy5idWZmZXIpOycsXG4gICAgICAgICAgR0wsICcudmVydGV4QXR0cmliUG9pbnRlcignLCBbXG4gICAgICAgICAgICBMT0NBVElPTixcbiAgICAgICAgICAgIFNJWkUsXG4gICAgICAgICAgICBUWVBFLFxuICAgICAgICAgICAgcmVjb3JkLm5vcm1hbGl6ZWQsXG4gICAgICAgICAgICByZWNvcmQuc3RyaWRlLFxuICAgICAgICAgICAgcmVjb3JkLm9mZnNldFxuICAgICAgICAgIF0sICcpOycsXG4gICAgICAgICAgQklORElORywgJy50eXBlPScsIFRZUEUsICc7JyxcbiAgICAgICAgICBCSU5ESU5HLCAnLnNpemU9JywgU0laRSwgJzsnLFxuICAgICAgICAgIENPTU1PTl9LRVlTLm1hcChmdW5jdGlvbiAoa2V5KSB7XG4gICAgICAgICAgICByZXR1cm4gQklORElORyArICcuJyArIGtleSArICc9JyArIHJlY29yZFtrZXldICsgJzsnXG4gICAgICAgICAgfSkuam9pbignJyksXG4gICAgICAgICAgJ30nKVxuXG4gICAgICAgIGlmIChleHRJbnN0YW5jaW5nKSB7XG4gICAgICAgICAgdmFyIERJVklTT1IgPSByZWNvcmQuZGl2aXNvclxuICAgICAgICAgIHNjb3BlKFxuICAgICAgICAgICAgJ2lmKCcsIEJJTkRJTkcsICcuZGl2aXNvciE9PScsIERJVklTT1IsICcpeycsXG4gICAgICAgICAgICBlbnYuaW5zdGFuY2luZywgJy52ZXJ0ZXhBdHRyaWJEaXZpc29yQU5HTEUoJywgW0xPQ0FUSU9OLCBESVZJU09SXSwgJyk7JyxcbiAgICAgICAgICAgIEJJTkRJTkcsICcuZGl2aXNvcj0nLCBESVZJU09SLCAnO30nKVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGZ1bmN0aW9uIGVtaXRDb25zdGFudCAoKSB7XG4gICAgICAgIHNjb3BlKFxuICAgICAgICAgICdpZignLCBCSU5ESU5HLCAnLnBvaW50ZXIpeycsXG4gICAgICAgICAgR0wsICcuZGlzYWJsZVZlcnRleEF0dHJpYkFycmF5KCcsIExPQ0FUSU9OLCAnKTsnLFxuICAgICAgICAgIEJJTkRJTkcsICcucG9pbnRlcj1mYWxzZTsnLFxuICAgICAgICAgICd9aWYoJywgQ1VURV9DT01QT05FTlRTLm1hcChmdW5jdGlvbiAoYywgaSkge1xuICAgICAgICAgICAgcmV0dXJuIEJJTkRJTkcgKyAnLicgKyBjICsgJyE9PScgKyBDT05TVF9DT01QT05FTlRTW2ldXG4gICAgICAgICAgfSkuam9pbignfHwnKSwgJyl7JyxcbiAgICAgICAgICBHTCwgJy52ZXJ0ZXhBdHRyaWI0ZignLCBMT0NBVElPTiwgJywnLCBDT05TVF9DT01QT05FTlRTLCAnKTsnLFxuICAgICAgICAgIENVVEVfQ09NUE9ORU5UUy5tYXAoZnVuY3Rpb24gKGMsIGkpIHtcbiAgICAgICAgICAgIHJldHVybiBCSU5ESU5HICsgJy4nICsgYyArICc9JyArIENPTlNUX0NPTVBPTkVOVFNbaV0gKyAnOydcbiAgICAgICAgICB9KS5qb2luKCcnKSxcbiAgICAgICAgICAnfScpXG4gICAgICB9XG5cbiAgICAgIGlmIChTVEFURSA9PT0gQVRUUklCX1NUQVRFX1BPSU5URVIpIHtcbiAgICAgICAgZW1pdEJ1ZmZlcigpXG4gICAgICB9IGVsc2UgaWYgKFNUQVRFID09PSBBVFRSSUJfU1RBVEVfQ09OU1RBTlQpIHtcbiAgICAgICAgZW1pdENvbnN0YW50KClcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHNjb3BlKCdpZignLCBTVEFURSwgJz09PScsIEFUVFJJQl9TVEFURV9QT0lOVEVSLCAnKXsnKVxuICAgICAgICBlbWl0QnVmZmVyKClcbiAgICAgICAgc2NvcGUoJ31lbHNleycpXG4gICAgICAgIGVtaXRDb25zdGFudCgpXG4gICAgICAgIHNjb3BlKCd9JylcbiAgICAgIH1cbiAgICB9XG5cbiAgICBhdHRyaWJ1dGVzLmZvckVhY2goZnVuY3Rpb24gKGF0dHJpYnV0ZSkge1xuICAgICAgdmFyIG5hbWUgPSBhdHRyaWJ1dGUubmFtZVxuICAgICAgdmFyIGFyZyA9IGFyZ3MuYXR0cmlidXRlc1tuYW1lXVxuICAgICAgdmFyIHJlY29yZFxuICAgICAgaWYgKGFyZykge1xuICAgICAgICBpZiAoIWZpbHRlcihhcmcpKSB7XG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgcmVjb3JkID0gYXJnLmFwcGVuZChlbnYsIHNjb3BlKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaWYgKCFmaWx0ZXIoU0NPUEVfREVDTCkpIHtcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICB2YXIgc2NvcGVBdHRyaWIgPSBlbnYuc2NvcGVBdHRyaWIobmFtZSlcbiAgICAgICAgY2hlY2sub3B0aW9uYWwoZnVuY3Rpb24gKCkge1xuICAgICAgICAgIGVudi5hc3NlcnQoc2NvcGUsXG4gICAgICAgICAgICBzY29wZUF0dHJpYiArICcuc3RhdGUnLFxuICAgICAgICAgICAgJ21pc3NpbmcgYXR0cmlidXRlICcgKyBuYW1lKVxuICAgICAgICB9KVxuICAgICAgICByZWNvcmQgPSB7fVxuICAgICAgICBPYmplY3Qua2V5cyhuZXcgQXR0cmlidXRlUmVjb3JkKCkpLmZvckVhY2goZnVuY3Rpb24gKGtleSkge1xuICAgICAgICAgIHJlY29yZFtrZXldID0gc2NvcGUuZGVmKHNjb3BlQXR0cmliLCAnLicsIGtleSlcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICAgIGVtaXRCaW5kQXR0cmlidXRlKFxuICAgICAgICBlbnYubGluayhhdHRyaWJ1dGUpLCB0eXBlTGVuZ3RoKGF0dHJpYnV0ZS5pbmZvLnR5cGUpLCByZWNvcmQpXG4gICAgfSlcbiAgfVxuXG4gIGZ1bmN0aW9uIGVtaXRVbmlmb3JtcyAoZW52LCBzY29wZSwgYXJncywgdW5pZm9ybXMsIGZpbHRlcikge1xuICAgIHZhciBzaGFyZWQgPSBlbnYuc2hhcmVkXG4gICAgdmFyIEdMID0gc2hhcmVkLmdsXG5cbiAgICB2YXIgaW5maXhcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IHVuaWZvcm1zLmxlbmd0aDsgKytpKSB7XG4gICAgICB2YXIgdW5pZm9ybSA9IHVuaWZvcm1zW2ldXG4gICAgICB2YXIgbmFtZSA9IHVuaWZvcm0ubmFtZVxuICAgICAgdmFyIHR5cGUgPSB1bmlmb3JtLmluZm8udHlwZVxuICAgICAgdmFyIGFyZyA9IGFyZ3MudW5pZm9ybXNbbmFtZV1cbiAgICAgIHZhciBVTklGT1JNID0gZW52LmxpbmsodW5pZm9ybSlcbiAgICAgIHZhciBMT0NBVElPTiA9IFVOSUZPUk0gKyAnLmxvY2F0aW9uJ1xuXG4gICAgICB2YXIgVkFMVUVcbiAgICAgIGlmIChhcmcpIHtcbiAgICAgICAgaWYgKCFmaWx0ZXIoYXJnKSkge1xuICAgICAgICAgIGNvbnRpbnVlXG4gICAgICAgIH1cbiAgICAgICAgaWYgKGlzU3RhdGljKGFyZykpIHtcbiAgICAgICAgICB2YXIgdmFsdWUgPSBhcmcudmFsdWVcbiAgICAgICAgICBpZiAodHlwZSA9PT0gR0xfU0FNUExFUl8yRCB8fCB0eXBlID09PSBHTF9TQU1QTEVSX0NVQkUpIHtcbiAgICAgICAgICAgIGNoZWNrLmNvbW1hbmQoXG4gICAgICAgICAgICAgIHR5cGVvZiB2YWx1ZSA9PT0gJ2Z1bmN0aW9uJyAmJlxuICAgICAgICAgICAgICAoKHZhbHVlLl9yZWdsVHlwZSA9PT0gJ3RleHR1cmUyZCcgJiYgdHlwZSA9PT0gR0xfU0FNUExFUl8yRCkgfHxcbiAgICAgICAgICAgICAgKHZhbHVlLl9yZWdsVHlwZSA9PT0gJ3RleHR1cmVDdWJlJyAmJiB0eXBlID09PSBHTF9TQU1QTEVSX0NVQkUpKSxcbiAgICAgICAgICAgICAgJ2ludmFsaWQgdGV4dHVyZSBmb3IgdW5pZm9ybSAnICsgbmFtZSlcbiAgICAgICAgICAgIHZhciBURVhfVkFMVUUgPSBlbnYubGluayh2YWx1ZS5fdGV4dHVyZSlcbiAgICAgICAgICAgIHNjb3BlKEdMLCAnLnVuaWZvcm0xaSgnLCBMT0NBVElPTiwgJywnLCBURVhfVkFMVUUgKyAnLmJpbmQoKSk7JylcbiAgICAgICAgICAgIHNjb3BlLmV4aXQoVEVYX1ZBTFVFLCAnLnVuYmluZCgpOycpXG4gICAgICAgICAgfSBlbHNlIGlmIChcbiAgICAgICAgICAgIHR5cGUgPT09IEdMX0ZMT0FUX01BVDIgfHxcbiAgICAgICAgICAgIHR5cGUgPT09IEdMX0ZMT0FUX01BVDMgfHxcbiAgICAgICAgICAgIHR5cGUgPT09IEdMX0ZMT0FUX01BVDQpIHtcbiAgICAgICAgICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgICAgY2hlY2suY29tbWFuZChpc0FycmF5TGlrZSh2YWx1ZSksXG4gICAgICAgICAgICAgICAgJ2ludmFsaWQgbWF0cml4IGZvciB1bmlmb3JtICcgKyBuYW1lKVxuICAgICAgICAgICAgICBjaGVjay5jb21tYW5kKFxuICAgICAgICAgICAgICAgICh0eXBlID09PSBHTF9GTE9BVF9NQVQyICYmIHZhbHVlLmxlbmd0aCA9PT0gNCkgfHxcbiAgICAgICAgICAgICAgICAodHlwZSA9PT0gR0xfRkxPQVRfTUFUMyAmJiB2YWx1ZS5sZW5ndGggPT09IDkpIHx8XG4gICAgICAgICAgICAgICAgKHR5cGUgPT09IEdMX0ZMT0FUX01BVDQgJiYgdmFsdWUubGVuZ3RoID09PSAxNiksXG4gICAgICAgICAgICAgICAgJ2ludmFsaWQgbGVuZ3RoIGZvciBtYXRyaXggdW5pZm9ybSAnICsgbmFtZSlcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB2YXIgTUFUX1ZBTFVFID0gZW52Lmdsb2JhbC5kZWYoJ25ldyBGbG9hdDMyQXJyYXkoWycgK1xuICAgICAgICAgICAgICBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbCh2YWx1ZSkgKyAnXSknKVxuICAgICAgICAgICAgdmFyIGRpbSA9IDJcbiAgICAgICAgICAgIGlmICh0eXBlID09PSBHTF9GTE9BVF9NQVQzKSB7XG4gICAgICAgICAgICAgIGRpbSA9IDNcbiAgICAgICAgICAgIH0gZWxzZSBpZiAodHlwZSA9PT0gR0xfRkxPQVRfTUFUNCkge1xuICAgICAgICAgICAgICBkaW0gPSA0XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBzY29wZShcbiAgICAgICAgICAgICAgR0wsICcudW5pZm9ybU1hdHJpeCcsIGRpbSwgJ2Z2KCcsXG4gICAgICAgICAgICAgIExPQ0FUSU9OLCAnLGZhbHNlLCcsIE1BVF9WQUxVRSwgJyk7JylcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgc3dpdGNoICh0eXBlKSB7XG4gICAgICAgICAgICAgIGNhc2UgR0xfRkxPQVQ6XG4gICAgICAgICAgICAgICAgY2hlY2suY29tbWFuZFR5cGUodmFsdWUsICdudW1iZXInLCAndW5pZm9ybSAnICsgbmFtZSlcbiAgICAgICAgICAgICAgICBpbmZpeCA9ICcxZidcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICBjYXNlIEdMX0ZMT0FUX1ZFQzI6XG4gICAgICAgICAgICAgICAgY2hlY2suY29tbWFuZChcbiAgICAgICAgICAgICAgICAgIGlzQXJyYXlMaWtlKHZhbHVlKSAmJiB2YWx1ZS5sZW5ndGggPT09IDIsXG4gICAgICAgICAgICAgICAgICAndW5pZm9ybSAnICsgbmFtZSlcbiAgICAgICAgICAgICAgICBpbmZpeCA9ICcyZidcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICBjYXNlIEdMX0ZMT0FUX1ZFQzM6XG4gICAgICAgICAgICAgICAgY2hlY2suY29tbWFuZChcbiAgICAgICAgICAgICAgICAgIGlzQXJyYXlMaWtlKHZhbHVlKSAmJiB2YWx1ZS5sZW5ndGggPT09IDMsXG4gICAgICAgICAgICAgICAgICAndW5pZm9ybSAnICsgbmFtZSlcbiAgICAgICAgICAgICAgICBpbmZpeCA9ICczZidcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICBjYXNlIEdMX0ZMT0FUX1ZFQzQ6XG4gICAgICAgICAgICAgICAgY2hlY2suY29tbWFuZChcbiAgICAgICAgICAgICAgICAgIGlzQXJyYXlMaWtlKHZhbHVlKSAmJiB2YWx1ZS5sZW5ndGggPT09IDQsXG4gICAgICAgICAgICAgICAgICAndW5pZm9ybSAnICsgbmFtZSlcbiAgICAgICAgICAgICAgICBpbmZpeCA9ICc0ZidcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICBjYXNlIEdMX0JPT0w6XG4gICAgICAgICAgICAgICAgY2hlY2suY29tbWFuZFR5cGUodmFsdWUsICdib29sZWFuJywgJ3VuaWZvcm0gJyArIG5hbWUpXG4gICAgICAgICAgICAgICAgaW5maXggPSAnMWknXG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgY2FzZSBHTF9JTlQ6XG4gICAgICAgICAgICAgICAgY2hlY2suY29tbWFuZFR5cGUodmFsdWUsICdudW1iZXInLCAndW5pZm9ybSAnICsgbmFtZSlcbiAgICAgICAgICAgICAgICBpbmZpeCA9ICcxaSdcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICBjYXNlIEdMX0JPT0xfVkVDMjpcbiAgICAgICAgICAgICAgICBjaGVjay5jb21tYW5kKFxuICAgICAgICAgICAgICAgICAgaXNBcnJheUxpa2UodmFsdWUpICYmIHZhbHVlLmxlbmd0aCA9PT0gMixcbiAgICAgICAgICAgICAgICAgICd1bmlmb3JtICcgKyBuYW1lKVxuICAgICAgICAgICAgICAgIGluZml4ID0gJzJpJ1xuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgIGNhc2UgR0xfSU5UX1ZFQzI6XG4gICAgICAgICAgICAgICAgY2hlY2suY29tbWFuZChcbiAgICAgICAgICAgICAgICAgIGlzQXJyYXlMaWtlKHZhbHVlKSAmJiB2YWx1ZS5sZW5ndGggPT09IDIsXG4gICAgICAgICAgICAgICAgICAndW5pZm9ybSAnICsgbmFtZSlcbiAgICAgICAgICAgICAgICBpbmZpeCA9ICcyaSdcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICBjYXNlIEdMX0JPT0xfVkVDMzpcbiAgICAgICAgICAgICAgICBjaGVjay5jb21tYW5kKFxuICAgICAgICAgICAgICAgICAgaXNBcnJheUxpa2UodmFsdWUpICYmIHZhbHVlLmxlbmd0aCA9PT0gMyxcbiAgICAgICAgICAgICAgICAgICd1bmlmb3JtICcgKyBuYW1lKVxuICAgICAgICAgICAgICAgIGluZml4ID0gJzNpJ1xuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgIGNhc2UgR0xfSU5UX1ZFQzM6XG4gICAgICAgICAgICAgICAgY2hlY2suY29tbWFuZChcbiAgICAgICAgICAgICAgICAgIGlzQXJyYXlMaWtlKHZhbHVlKSAmJiB2YWx1ZS5sZW5ndGggPT09IDMsXG4gICAgICAgICAgICAgICAgICAndW5pZm9ybSAnICsgbmFtZSlcbiAgICAgICAgICAgICAgICBpbmZpeCA9ICczaSdcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICBjYXNlIEdMX0JPT0xfVkVDNDpcbiAgICAgICAgICAgICAgICBjaGVjay5jb21tYW5kKFxuICAgICAgICAgICAgICAgICAgaXNBcnJheUxpa2UodmFsdWUpICYmIHZhbHVlLmxlbmd0aCA9PT0gNCxcbiAgICAgICAgICAgICAgICAgICd1bmlmb3JtICcgKyBuYW1lKVxuICAgICAgICAgICAgICAgIGluZml4ID0gJzRpJ1xuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgIGNhc2UgR0xfSU5UX1ZFQzQ6XG4gICAgICAgICAgICAgICAgY2hlY2suY29tbWFuZChcbiAgICAgICAgICAgICAgICAgIGlzQXJyYXlMaWtlKHZhbHVlKSAmJiB2YWx1ZS5sZW5ndGggPT09IDQsXG4gICAgICAgICAgICAgICAgICAndW5pZm9ybSAnICsgbmFtZSlcbiAgICAgICAgICAgICAgICBpbmZpeCA9ICc0aSdcbiAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc2NvcGUoR0wsICcudW5pZm9ybScsIGluZml4LCAnKCcsIExPQ0FUSU9OLCAnLCcsXG4gICAgICAgICAgICAgIGlzQXJyYXlMaWtlKHZhbHVlKSA/IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKHZhbHVlKSA6IHZhbHVlLFxuICAgICAgICAgICAgICAnKTsnKVxuICAgICAgICAgIH1cbiAgICAgICAgICBjb250aW51ZVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIFZBTFVFID0gYXJnLmFwcGVuZChlbnYsIHNjb3BlKVxuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpZiAoIWZpbHRlcihTQ09QRV9ERUNMKSkge1xuICAgICAgICAgIGNvbnRpbnVlXG4gICAgICAgIH1cbiAgICAgICAgVkFMVUUgPSBzY29wZS5kZWYoc2hhcmVkLnVuaWZvcm1zLCAnWycsIHN0cmluZ1N0b3JlLmlkKG5hbWUpLCAnXScpXG4gICAgICB9XG5cbiAgICAgIC8vIHBlcmZvcm0gdHlwZSB2YWxpZGF0aW9uXG4gICAgICBjaGVjay5vcHRpb25hbChmdW5jdGlvbiAoKSB7XG4gICAgICAgIGZ1bmN0aW9uIGNoZWNrIChwcmVkKSB7XG4gICAgICAgICAgZW52LmFzc2VydChzY29wZSwgcHJlZCwgJ2ludmFsaWQgb3IgbWlzc2luZyB1bmlmb3JtIFwiJyArIG5hbWUgKyAnXCInKVxuICAgICAgICB9XG5cbiAgICAgICAgZnVuY3Rpb24gY2hlY2tUeXBlICh0eXBlKSB7XG4gICAgICAgICAgY2hlY2soJ3R5cGVvZiAnICsgVkFMVUUgKyAnPT09XCInICsgdHlwZSArICdcIicpXG4gICAgICAgIH1cblxuICAgICAgICBmdW5jdGlvbiBjaGVja1ZlY3RvciAobiwgdHlwZSkge1xuICAgICAgICAgIGNoZWNrKHNoYXJlZC5pc0FycmF5TGlrZSArICcoJyArIFZBTFVFICsgJykmJicgKyBWQUxVRSArICcubGVuZ3RoPT09JyArIG4pXG4gICAgICAgIH1cblxuICAgICAgICBmdW5jdGlvbiBjaGVja1RleHR1cmUgKHRhcmdldCkge1xuICAgICAgICAgIGNoZWNrKFxuICAgICAgICAgICAgJ3R5cGVvZiAnICsgVkFMVUUgKyAnPT09XCJmdW5jdGlvblwiJiYnICtcbiAgICAgICAgICAgIFZBTFVFICsgJy5fcmVnbFR5cGU9PT1cInRleHR1cmUnICtcbiAgICAgICAgICAgICh0YXJnZXQgPT09IEdMX1RFWFRVUkVfMkQgPyAnMmQnIDogJ0N1YmUnKSArICdcIicpXG4gICAgICAgIH1cblxuICAgICAgICBzd2l0Y2ggKHR5cGUpIHtcbiAgICAgICAgICBjYXNlIEdMX0lOVDpcbiAgICAgICAgICAgIGNoZWNrVHlwZSgnbnVtYmVyJylcbiAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgY2FzZSBHTF9JTlRfVkVDMjpcbiAgICAgICAgICAgIGNoZWNrVmVjdG9yKDIsICdudW1iZXInKVxuICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICBjYXNlIEdMX0lOVF9WRUMzOlxuICAgICAgICAgICAgY2hlY2tWZWN0b3IoMywgJ251bWJlcicpXG4gICAgICAgICAgICBicmVha1xuICAgICAgICAgIGNhc2UgR0xfSU5UX1ZFQzQ6XG4gICAgICAgICAgICBjaGVja1ZlY3Rvcig0LCAnbnVtYmVyJylcbiAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgY2FzZSBHTF9GTE9BVDpcbiAgICAgICAgICAgIGNoZWNrVHlwZSgnbnVtYmVyJylcbiAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgY2FzZSBHTF9GTE9BVF9WRUMyOlxuICAgICAgICAgICAgY2hlY2tWZWN0b3IoMiwgJ251bWJlcicpXG4gICAgICAgICAgICBicmVha1xuICAgICAgICAgIGNhc2UgR0xfRkxPQVRfVkVDMzpcbiAgICAgICAgICAgIGNoZWNrVmVjdG9yKDMsICdudW1iZXInKVxuICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICBjYXNlIEdMX0ZMT0FUX1ZFQzQ6XG4gICAgICAgICAgICBjaGVja1ZlY3Rvcig0LCAnbnVtYmVyJylcbiAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgY2FzZSBHTF9CT09MOlxuICAgICAgICAgICAgY2hlY2tUeXBlKCdib29sZWFuJylcbiAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgY2FzZSBHTF9CT09MX1ZFQzI6XG4gICAgICAgICAgICBjaGVja1ZlY3RvcigyLCAnYm9vbGVhbicpXG4gICAgICAgICAgICBicmVha1xuICAgICAgICAgIGNhc2UgR0xfQk9PTF9WRUMzOlxuICAgICAgICAgICAgY2hlY2tWZWN0b3IoMywgJ2Jvb2xlYW4nKVxuICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICBjYXNlIEdMX0JPT0xfVkVDNDpcbiAgICAgICAgICAgIGNoZWNrVmVjdG9yKDQsICdib29sZWFuJylcbiAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgY2FzZSBHTF9GTE9BVF9NQVQyOlxuICAgICAgICAgICAgY2hlY2tWZWN0b3IoNCwgJ251bWJlcicpXG4gICAgICAgICAgICBicmVha1xuICAgICAgICAgIGNhc2UgR0xfRkxPQVRfTUFUMzpcbiAgICAgICAgICAgIGNoZWNrVmVjdG9yKDksICdudW1iZXInKVxuICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICBjYXNlIEdMX0ZMT0FUX01BVDQ6XG4gICAgICAgICAgICBjaGVja1ZlY3RvcigxNiwgJ251bWJlcicpXG4gICAgICAgICAgICBicmVha1xuICAgICAgICAgIGNhc2UgR0xfU0FNUExFUl8yRDpcbiAgICAgICAgICAgIGNoZWNrVGV4dHVyZShHTF9URVhUVVJFXzJEKVxuICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICBjYXNlIEdMX1NBTVBMRVJfQ1VCRTpcbiAgICAgICAgICAgIGNoZWNrVGV4dHVyZShHTF9URVhUVVJFX0NVQkVfTUFQKVxuICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgfVxuICAgICAgfSlcblxuICAgICAgdmFyIHVucm9sbCA9IDFcbiAgICAgIHN3aXRjaCAodHlwZSkge1xuICAgICAgICBjYXNlIEdMX1NBTVBMRVJfMkQ6XG4gICAgICAgIGNhc2UgR0xfU0FNUExFUl9DVUJFOlxuICAgICAgICAgIHZhciBURVggPSBzY29wZS5kZWYoVkFMVUUsICcuX3RleHR1cmUnKVxuICAgICAgICAgIHNjb3BlKEdMLCAnLnVuaWZvcm0xaSgnLCBMT0NBVElPTiwgJywnLCBURVgsICcuYmluZCgpKTsnKVxuICAgICAgICAgIHNjb3BlLmV4aXQoVEVYLCAnLnVuYmluZCgpOycpXG4gICAgICAgICAgY29udGludWVcblxuICAgICAgICBjYXNlIEdMX0lOVDpcbiAgICAgICAgY2FzZSBHTF9CT09MOlxuICAgICAgICAgIGluZml4ID0gJzFpJ1xuICAgICAgICAgIGJyZWFrXG5cbiAgICAgICAgY2FzZSBHTF9JTlRfVkVDMjpcbiAgICAgICAgY2FzZSBHTF9CT09MX1ZFQzI6XG4gICAgICAgICAgaW5maXggPSAnMmknXG4gICAgICAgICAgdW5yb2xsID0gMlxuICAgICAgICAgIGJyZWFrXG5cbiAgICAgICAgY2FzZSBHTF9JTlRfVkVDMzpcbiAgICAgICAgY2FzZSBHTF9CT09MX1ZFQzM6XG4gICAgICAgICAgaW5maXggPSAnM2knXG4gICAgICAgICAgdW5yb2xsID0gM1xuICAgICAgICAgIGJyZWFrXG5cbiAgICAgICAgY2FzZSBHTF9JTlRfVkVDNDpcbiAgICAgICAgY2FzZSBHTF9CT09MX1ZFQzQ6XG4gICAgICAgICAgaW5maXggPSAnNGknXG4gICAgICAgICAgdW5yb2xsID0gNFxuICAgICAgICAgIGJyZWFrXG5cbiAgICAgICAgY2FzZSBHTF9GTE9BVDpcbiAgICAgICAgICBpbmZpeCA9ICcxZidcbiAgICAgICAgICBicmVha1xuXG4gICAgICAgIGNhc2UgR0xfRkxPQVRfVkVDMjpcbiAgICAgICAgICBpbmZpeCA9ICcyZidcbiAgICAgICAgICB1bnJvbGwgPSAyXG4gICAgICAgICAgYnJlYWtcblxuICAgICAgICBjYXNlIEdMX0ZMT0FUX1ZFQzM6XG4gICAgICAgICAgaW5maXggPSAnM2YnXG4gICAgICAgICAgdW5yb2xsID0gM1xuICAgICAgICAgIGJyZWFrXG5cbiAgICAgICAgY2FzZSBHTF9GTE9BVF9WRUM0OlxuICAgICAgICAgIGluZml4ID0gJzRmJ1xuICAgICAgICAgIHVucm9sbCA9IDRcbiAgICAgICAgICBicmVha1xuXG4gICAgICAgIGNhc2UgR0xfRkxPQVRfTUFUMjpcbiAgICAgICAgICBpbmZpeCA9ICdNYXRyaXgyZnYnXG4gICAgICAgICAgYnJlYWtcblxuICAgICAgICBjYXNlIEdMX0ZMT0FUX01BVDM6XG4gICAgICAgICAgaW5maXggPSAnTWF0cml4M2Z2J1xuICAgICAgICAgIGJyZWFrXG5cbiAgICAgICAgY2FzZSBHTF9GTE9BVF9NQVQ0OlxuICAgICAgICAgIGluZml4ID0gJ01hdHJpeDRmdidcbiAgICAgICAgICBicmVha1xuICAgICAgfVxuXG4gICAgICBzY29wZShHTCwgJy51bmlmb3JtJywgaW5maXgsICcoJywgTE9DQVRJT04sICcsJylcbiAgICAgIGlmIChpbmZpeC5jaGFyQXQoMCkgPT09ICdNJykge1xuICAgICAgICB2YXIgbWF0U2l6ZSA9IE1hdGgucG93KHR5cGUgLSBHTF9GTE9BVF9NQVQyICsgMiwgMilcbiAgICAgICAgdmFyIFNUT1JBR0UgPSBlbnYuZ2xvYmFsLmRlZignbmV3IEZsb2F0MzJBcnJheSgnLCBtYXRTaXplLCAnKScpXG4gICAgICAgIHNjb3BlKFxuICAgICAgICAgICdmYWxzZSwoQXJyYXkuaXNBcnJheSgnLCBWQUxVRSwgJyl8fCcsIFZBTFVFLCAnIGluc3RhbmNlb2YgRmxvYXQzMkFycmF5KT8nLCBWQUxVRSwgJzooJyxcbiAgICAgICAgICBsb29wKG1hdFNpemUsIGZ1bmN0aW9uIChpKSB7XG4gICAgICAgICAgICByZXR1cm4gU1RPUkFHRSArICdbJyArIGkgKyAnXT0nICsgVkFMVUUgKyAnWycgKyBpICsgJ10nXG4gICAgICAgICAgfSksICcsJywgU1RPUkFHRSwgJyknKVxuICAgICAgfSBlbHNlIGlmICh1bnJvbGwgPiAxKSB7XG4gICAgICAgIHNjb3BlKGxvb3AodW5yb2xsLCBmdW5jdGlvbiAoaSkge1xuICAgICAgICAgIHJldHVybiBWQUxVRSArICdbJyArIGkgKyAnXSdcbiAgICAgICAgfSkpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzY29wZShWQUxVRSlcbiAgICAgIH1cbiAgICAgIHNjb3BlKCcpOycpXG4gICAgfVxuICB9XG5cbiAgZnVuY3Rpb24gZW1pdERyYXcgKGVudiwgb3V0ZXIsIGlubmVyLCBhcmdzKSB7XG4gICAgdmFyIHNoYXJlZCA9IGVudi5zaGFyZWRcbiAgICB2YXIgR0wgPSBzaGFyZWQuZ2xcbiAgICB2YXIgRFJBV19TVEFURSA9IHNoYXJlZC5kcmF3XG5cbiAgICB2YXIgZHJhd09wdGlvbnMgPSBhcmdzLmRyYXdcblxuICAgIGZ1bmN0aW9uIGVtaXRFbGVtZW50cyAoKSB7XG4gICAgICB2YXIgZGVmbiA9IGRyYXdPcHRpb25zLmVsZW1lbnRzXG4gICAgICB2YXIgRUxFTUVOVFNcbiAgICAgIHZhciBzY29wZSA9IG91dGVyXG4gICAgICBpZiAoZGVmbikge1xuICAgICAgICBpZiAoKGRlZm4uY29udGV4dERlcCAmJiBhcmdzLmNvbnRleHREeW5hbWljKSB8fCBkZWZuLnByb3BEZXApIHtcbiAgICAgICAgICBzY29wZSA9IGlubmVyXG4gICAgICAgIH1cbiAgICAgICAgRUxFTUVOVFMgPSBkZWZuLmFwcGVuZChlbnYsIHNjb3BlKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgRUxFTUVOVFMgPSBzY29wZS5kZWYoRFJBV19TVEFURSwgJy4nLCBTX0VMRU1FTlRTKVxuICAgICAgfVxuICAgICAgaWYgKEVMRU1FTlRTKSB7XG4gICAgICAgIHNjb3BlKFxuICAgICAgICAgICdpZignICsgRUxFTUVOVFMgKyAnKScgK1xuICAgICAgICAgIEdMICsgJy5iaW5kQnVmZmVyKCcgKyBHTF9FTEVNRU5UX0FSUkFZX0JVRkZFUiArICcsJyArIEVMRU1FTlRTICsgJy5idWZmZXIuYnVmZmVyKTsnKVxuICAgICAgfVxuICAgICAgcmV0dXJuIEVMRU1FTlRTXG4gICAgfVxuXG4gICAgZnVuY3Rpb24gZW1pdENvdW50ICgpIHtcbiAgICAgIHZhciBkZWZuID0gZHJhd09wdGlvbnMuY291bnRcbiAgICAgIHZhciBDT1VOVFxuICAgICAgdmFyIHNjb3BlID0gb3V0ZXJcbiAgICAgIGlmIChkZWZuKSB7XG4gICAgICAgIGlmICgoZGVmbi5jb250ZXh0RGVwICYmIGFyZ3MuY29udGV4dER5bmFtaWMpIHx8IGRlZm4ucHJvcERlcCkge1xuICAgICAgICAgIHNjb3BlID0gaW5uZXJcbiAgICAgICAgfVxuICAgICAgICBDT1VOVCA9IGRlZm4uYXBwZW5kKGVudiwgc2NvcGUpXG4gICAgICAgIGNoZWNrLm9wdGlvbmFsKGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICBpZiAoZGVmbi5NSVNTSU5HKSB7XG4gICAgICAgICAgICBlbnYuYXNzZXJ0KG91dGVyLCAnZmFsc2UnLCAnbWlzc2luZyB2ZXJ0ZXggY291bnQnKVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoZGVmbi5EWU5BTUlDKSB7XG4gICAgICAgICAgICBlbnYuYXNzZXJ0KHNjb3BlLCBDT1VOVCArICc+PTAnLCAnbWlzc2luZyB2ZXJ0ZXggY291bnQnKVxuICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIENPVU5UID0gc2NvcGUuZGVmKERSQVdfU1RBVEUsICcuJywgU19DT1VOVClcbiAgICAgICAgY2hlY2sub3B0aW9uYWwoZnVuY3Rpb24gKCkge1xuICAgICAgICAgIGVudi5hc3NlcnQoc2NvcGUsIENPVU5UICsgJz49MCcsICdtaXNzaW5nIHZlcnRleCBjb3VudCcpXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgICByZXR1cm4gQ09VTlRcbiAgICB9XG5cbiAgICB2YXIgRUxFTUVOVFMgPSBlbWl0RWxlbWVudHMoKVxuICAgIGZ1bmN0aW9uIGVtaXRWYWx1ZSAobmFtZSkge1xuICAgICAgdmFyIGRlZm4gPSBkcmF3T3B0aW9uc1tuYW1lXVxuICAgICAgaWYgKGRlZm4pIHtcbiAgICAgICAgaWYgKChkZWZuLmNvbnRleHREZXAgJiYgYXJncy5jb250ZXh0RHluYW1pYykgfHwgZGVmbi5wcm9wRGVwKSB7XG4gICAgICAgICAgcmV0dXJuIGRlZm4uYXBwZW5kKGVudiwgaW5uZXIpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIGRlZm4uYXBwZW5kKGVudiwgb3V0ZXIpXG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiBvdXRlci5kZWYoRFJBV19TVEFURSwgJy4nLCBuYW1lKVxuICAgICAgfVxuICAgIH1cblxuICAgIHZhciBQUklNSVRJVkUgPSBlbWl0VmFsdWUoU19QUklNSVRJVkUpXG4gICAgdmFyIE9GRlNFVCA9IGVtaXRWYWx1ZShTX09GRlNFVClcblxuICAgIHZhciBDT1VOVCA9IGVtaXRDb3VudCgpXG4gICAgaWYgKHR5cGVvZiBDT1VOVCA9PT0gJ251bWJlcicpIHtcbiAgICAgIGlmIChDT1VOVCA9PT0gMCkge1xuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgaW5uZXIoJ2lmKCcsIENPVU5ULCAnKXsnKVxuICAgICAgaW5uZXIuZXhpdCgnfScpXG4gICAgfVxuXG4gICAgdmFyIElOU1RBTkNFUywgRVhUX0lOU1RBTkNJTkdcbiAgICBpZiAoZXh0SW5zdGFuY2luZykge1xuICAgICAgSU5TVEFOQ0VTID0gZW1pdFZhbHVlKFNfSU5TVEFOQ0VTKVxuICAgICAgRVhUX0lOU1RBTkNJTkcgPSBlbnYuaW5zdGFuY2luZ1xuICAgIH1cblxuICAgIHZhciBFTEVNRU5UX1RZUEUgPSBFTEVNRU5UUyArICcudHlwZSdcblxuICAgIHZhciBlbGVtZW50c1N0YXRpYyA9IGRyYXdPcHRpb25zLmVsZW1lbnRzICYmIGlzU3RhdGljKGRyYXdPcHRpb25zLmVsZW1lbnRzKVxuXG4gICAgZnVuY3Rpb24gZW1pdEluc3RhbmNpbmcgKCkge1xuICAgICAgZnVuY3Rpb24gZHJhd0VsZW1lbnRzICgpIHtcbiAgICAgICAgaW5uZXIoRVhUX0lOU1RBTkNJTkcsICcuZHJhd0VsZW1lbnRzSW5zdGFuY2VkQU5HTEUoJywgW1xuICAgICAgICAgIFBSSU1JVElWRSxcbiAgICAgICAgICBDT1VOVCxcbiAgICAgICAgICBFTEVNRU5UX1RZUEUsXG4gICAgICAgICAgT0ZGU0VUICsgJzw8KCgnICsgRUxFTUVOVF9UWVBFICsgJy0nICsgR0xfVU5TSUdORURfQllURSArICcpPj4xKScsXG4gICAgICAgICAgSU5TVEFOQ0VTXG4gICAgICAgIF0sICcpOycpXG4gICAgICB9XG5cbiAgICAgIGZ1bmN0aW9uIGRyYXdBcnJheXMgKCkge1xuICAgICAgICBpbm5lcihFWFRfSU5TVEFOQ0lORywgJy5kcmF3QXJyYXlzSW5zdGFuY2VkQU5HTEUoJyxcbiAgICAgICAgICBbUFJJTUlUSVZFLCBPRkZTRVQsIENPVU5ULCBJTlNUQU5DRVNdLCAnKTsnKVxuICAgICAgfVxuXG4gICAgICBpZiAoRUxFTUVOVFMpIHtcbiAgICAgICAgaWYgKCFlbGVtZW50c1N0YXRpYykge1xuICAgICAgICAgIGlubmVyKCdpZignLCBFTEVNRU5UUywgJyl7JylcbiAgICAgICAgICBkcmF3RWxlbWVudHMoKVxuICAgICAgICAgIGlubmVyKCd9ZWxzZXsnKVxuICAgICAgICAgIGRyYXdBcnJheXMoKVxuICAgICAgICAgIGlubmVyKCd9JylcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBkcmF3RWxlbWVudHMoKVxuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBkcmF3QXJyYXlzKClcbiAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiBlbWl0UmVndWxhciAoKSB7XG4gICAgICBmdW5jdGlvbiBkcmF3RWxlbWVudHMgKCkge1xuICAgICAgICBpbm5lcihHTCArICcuZHJhd0VsZW1lbnRzKCcgKyBbXG4gICAgICAgICAgUFJJTUlUSVZFLFxuICAgICAgICAgIENPVU5ULFxuICAgICAgICAgIEVMRU1FTlRfVFlQRSxcbiAgICAgICAgICBPRkZTRVQgKyAnPDwoKCcgKyBFTEVNRU5UX1RZUEUgKyAnLScgKyBHTF9VTlNJR05FRF9CWVRFICsgJyk+PjEpJ1xuICAgICAgICBdICsgJyk7JylcbiAgICAgIH1cblxuICAgICAgZnVuY3Rpb24gZHJhd0FycmF5cyAoKSB7XG4gICAgICAgIGlubmVyKEdMICsgJy5kcmF3QXJyYXlzKCcgKyBbUFJJTUlUSVZFLCBPRkZTRVQsIENPVU5UXSArICcpOycpXG4gICAgICB9XG5cbiAgICAgIGlmIChFTEVNRU5UUykge1xuICAgICAgICBpZiAoIWVsZW1lbnRzU3RhdGljKSB7XG4gICAgICAgICAgaW5uZXIoJ2lmKCcsIEVMRU1FTlRTLCAnKXsnKVxuICAgICAgICAgIGRyYXdFbGVtZW50cygpXG4gICAgICAgICAgaW5uZXIoJ31lbHNleycpXG4gICAgICAgICAgZHJhd0FycmF5cygpXG4gICAgICAgICAgaW5uZXIoJ30nKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGRyYXdFbGVtZW50cygpXG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGRyYXdBcnJheXMoKVxuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChleHRJbnN0YW5jaW5nICYmICh0eXBlb2YgSU5TVEFOQ0VTICE9PSAnbnVtYmVyJyB8fCBJTlNUQU5DRVMgPj0gMCkpIHtcbiAgICAgIGlmICh0eXBlb2YgSU5TVEFOQ0VTID09PSAnc3RyaW5nJykge1xuICAgICAgICBpbm5lcignaWYoJywgSU5TVEFOQ0VTLCAnPjApeycpXG4gICAgICAgIGVtaXRJbnN0YW5jaW5nKClcbiAgICAgICAgaW5uZXIoJ31lbHNlIGlmKCcsIElOU1RBTkNFUywgJzwwKXsnKVxuICAgICAgICBlbWl0UmVndWxhcigpXG4gICAgICAgIGlubmVyKCd9JylcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGVtaXRJbnN0YW5jaW5nKClcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgZW1pdFJlZ3VsYXIoKVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGNyZWF0ZUJvZHkgKGVtaXRCb2R5LCBwYXJlbnRFbnYsIGFyZ3MsIHByb2dyYW0sIGNvdW50KSB7XG4gICAgdmFyIGVudiA9IGNyZWF0ZVJFR0xFbnZpcm9ubWVudCgpXG4gICAgdmFyIHNjb3BlID0gZW52LnByb2MoJ2JvZHknLCBjb3VudClcbiAgICBjaGVjay5vcHRpb25hbChmdW5jdGlvbiAoKSB7XG4gICAgICBlbnYuY29tbWFuZFN0ciA9IHBhcmVudEVudi5jb21tYW5kU3RyXG4gICAgICBlbnYuY29tbWFuZCA9IGVudi5saW5rKHBhcmVudEVudi5jb21tYW5kU3RyKVxuICAgIH0pXG4gICAgaWYgKGV4dEluc3RhbmNpbmcpIHtcbiAgICAgIGVudi5pbnN0YW5jaW5nID0gc2NvcGUuZGVmKFxuICAgICAgICBlbnYuc2hhcmVkLmV4dGVuc2lvbnMsICcuYW5nbGVfaW5zdGFuY2VkX2FycmF5cycpXG4gICAgfVxuICAgIGVtaXRCb2R5KGVudiwgc2NvcGUsIGFyZ3MsIHByb2dyYW0pXG4gICAgcmV0dXJuIGVudi5jb21waWxlKCkuYm9keVxuICB9XG5cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBEUkFXIFBST0NcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICBmdW5jdGlvbiBlbWl0RHJhd0JvZHkgKGVudiwgZHJhdywgYXJncywgcHJvZ3JhbSkge1xuICAgIGluamVjdEV4dGVuc2lvbnMoZW52LCBkcmF3KVxuICAgIGVtaXRBdHRyaWJ1dGVzKGVudiwgZHJhdywgYXJncywgcHJvZ3JhbS5hdHRyaWJ1dGVzLCBmdW5jdGlvbiAoKSB7XG4gICAgICByZXR1cm4gdHJ1ZVxuICAgIH0pXG4gICAgZW1pdFVuaWZvcm1zKGVudiwgZHJhdywgYXJncywgcHJvZ3JhbS51bmlmb3JtcywgZnVuY3Rpb24gKCkge1xuICAgICAgcmV0dXJuIHRydWVcbiAgICB9KVxuICAgIGVtaXREcmF3KGVudiwgZHJhdywgZHJhdywgYXJncylcbiAgfVxuXG4gIGZ1bmN0aW9uIGVtaXREcmF3UHJvYyAoZW52LCBhcmdzKSB7XG4gICAgdmFyIGRyYXcgPSBlbnYucHJvYygnZHJhdycsIDEpXG5cbiAgICBpbmplY3RFeHRlbnNpb25zKGVudiwgZHJhdylcblxuICAgIGVtaXRDb250ZXh0KGVudiwgZHJhdywgYXJncy5jb250ZXh0KVxuICAgIGVtaXRQb2xsRnJhbWVidWZmZXIoZW52LCBkcmF3LCBhcmdzLmZyYW1lYnVmZmVyKVxuXG4gICAgZW1pdFBvbGxTdGF0ZShlbnYsIGRyYXcsIGFyZ3MpXG4gICAgZW1pdFNldE9wdGlvbnMoZW52LCBkcmF3LCBhcmdzLnN0YXRlKVxuXG4gICAgZW1pdFByb2ZpbGUoZW52LCBkcmF3LCBhcmdzLCBmYWxzZSwgdHJ1ZSlcblxuICAgIHZhciBwcm9ncmFtID0gYXJncy5zaGFkZXIucHJvZ1Zhci5hcHBlbmQoZW52LCBkcmF3KVxuICAgIGRyYXcoZW52LnNoYXJlZC5nbCwgJy51c2VQcm9ncmFtKCcsIHByb2dyYW0sICcucHJvZ3JhbSk7JylcblxuICAgIGlmIChhcmdzLnNoYWRlci5wcm9ncmFtKSB7XG4gICAgICBlbWl0RHJhd0JvZHkoZW52LCBkcmF3LCBhcmdzLCBhcmdzLnNoYWRlci5wcm9ncmFtKVxuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgZHJhd0NhY2hlID0gZW52Lmdsb2JhbC5kZWYoJ3t9JylcbiAgICAgIHZhciBQUk9HX0lEID0gZHJhdy5kZWYocHJvZ3JhbSwgJy5pZCcpXG4gICAgICB2YXIgQ0FDSEVEX1BST0MgPSBkcmF3LmRlZihkcmF3Q2FjaGUsICdbJywgUFJPR19JRCwgJ10nKVxuICAgICAgZHJhdyhcbiAgICAgICAgZW52LmNvbmQoQ0FDSEVEX1BST0MpXG4gICAgICAgICAgLnRoZW4oQ0FDSEVEX1BST0MsICcuY2FsbCh0aGlzLGEwKTsnKVxuICAgICAgICAgIC5lbHNlKFxuICAgICAgICAgICAgQ0FDSEVEX1BST0MsICc9JywgZHJhd0NhY2hlLCAnWycsIFBST0dfSUQsICddPScsXG4gICAgICAgICAgICBlbnYubGluayhmdW5jdGlvbiAocHJvZ3JhbSkge1xuICAgICAgICAgICAgICByZXR1cm4gY3JlYXRlQm9keShlbWl0RHJhd0JvZHksIGVudiwgYXJncywgcHJvZ3JhbSwgMSlcbiAgICAgICAgICAgIH0pLCAnKCcsIHByb2dyYW0sICcpOycsXG4gICAgICAgICAgICBDQUNIRURfUFJPQywgJy5jYWxsKHRoaXMsYTApOycpKVxuICAgIH1cblxuICAgIGlmIChPYmplY3Qua2V5cyhhcmdzLnN0YXRlKS5sZW5ndGggPiAwKSB7XG4gICAgICBkcmF3KGVudi5zaGFyZWQuY3VycmVudCwgJy5kaXJ0eT10cnVlOycpXG4gICAgfVxuICB9XG5cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBCQVRDSCBQUk9DXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICBmdW5jdGlvbiBlbWl0QmF0Y2hEeW5hbWljU2hhZGVyQm9keSAoZW52LCBzY29wZSwgYXJncywgcHJvZ3JhbSkge1xuICAgIGVudi5iYXRjaElkID0gJ2ExJ1xuXG4gICAgaW5qZWN0RXh0ZW5zaW9ucyhlbnYsIHNjb3BlKVxuXG4gICAgZnVuY3Rpb24gYWxsICgpIHtcbiAgICAgIHJldHVybiB0cnVlXG4gICAgfVxuXG4gICAgZW1pdEF0dHJpYnV0ZXMoZW52LCBzY29wZSwgYXJncywgcHJvZ3JhbS5hdHRyaWJ1dGVzLCBhbGwpXG4gICAgZW1pdFVuaWZvcm1zKGVudiwgc2NvcGUsIGFyZ3MsIHByb2dyYW0udW5pZm9ybXMsIGFsbClcbiAgICBlbWl0RHJhdyhlbnYsIHNjb3BlLCBzY29wZSwgYXJncylcbiAgfVxuXG4gIGZ1bmN0aW9uIGVtaXRCYXRjaEJvZHkgKGVudiwgc2NvcGUsIGFyZ3MsIHByb2dyYW0pIHtcbiAgICBpbmplY3RFeHRlbnNpb25zKGVudiwgc2NvcGUpXG5cbiAgICB2YXIgY29udGV4dER5bmFtaWMgPSBhcmdzLmNvbnRleHREZXBcblxuICAgIHZhciBCQVRDSF9JRCA9IHNjb3BlLmRlZigpXG4gICAgdmFyIFBST1BfTElTVCA9ICdhMCdcbiAgICB2YXIgTlVNX1BST1BTID0gJ2ExJ1xuICAgIHZhciBQUk9QUyA9IHNjb3BlLmRlZigpXG4gICAgZW52LnNoYXJlZC5wcm9wcyA9IFBST1BTXG4gICAgZW52LmJhdGNoSWQgPSBCQVRDSF9JRFxuXG4gICAgdmFyIG91dGVyID0gZW52LnNjb3BlKClcbiAgICB2YXIgaW5uZXIgPSBlbnYuc2NvcGUoKVxuXG4gICAgc2NvcGUoXG4gICAgICBvdXRlci5lbnRyeSxcbiAgICAgICdmb3IoJywgQkFUQ0hfSUQsICc9MDsnLCBCQVRDSF9JRCwgJzwnLCBOVU1fUFJPUFMsICc7KysnLCBCQVRDSF9JRCwgJyl7JyxcbiAgICAgIFBST1BTLCAnPScsIFBST1BfTElTVCwgJ1snLCBCQVRDSF9JRCwgJ107JyxcbiAgICAgIGlubmVyLFxuICAgICAgJ30nLFxuICAgICAgb3V0ZXIuZXhpdClcblxuICAgIGZ1bmN0aW9uIGlzSW5uZXJEZWZuIChkZWZuKSB7XG4gICAgICByZXR1cm4gKChkZWZuLmNvbnRleHREZXAgJiYgY29udGV4dER5bmFtaWMpIHx8IGRlZm4ucHJvcERlcClcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBpc091dGVyRGVmbiAoZGVmbikge1xuICAgICAgcmV0dXJuICFpc0lubmVyRGVmbihkZWZuKVxuICAgIH1cblxuICAgIGlmIChhcmdzLm5lZWRzQ29udGV4dCkge1xuICAgICAgZW1pdENvbnRleHQoZW52LCBpbm5lciwgYXJncy5jb250ZXh0KVxuICAgIH1cbiAgICBpZiAoYXJncy5uZWVkc0ZyYW1lYnVmZmVyKSB7XG4gICAgICBlbWl0UG9sbEZyYW1lYnVmZmVyKGVudiwgaW5uZXIsIGFyZ3MuZnJhbWVidWZmZXIpXG4gICAgfVxuICAgIGVtaXRTZXRPcHRpb25zKGVudiwgaW5uZXIsIGFyZ3Muc3RhdGUsIGlzSW5uZXJEZWZuKVxuXG4gICAgaWYgKGFyZ3MucHJvZmlsZSAmJiBpc0lubmVyRGVmbihhcmdzLnByb2ZpbGUpKSB7XG4gICAgICBlbWl0UHJvZmlsZShlbnYsIGlubmVyLCBhcmdzLCBmYWxzZSwgdHJ1ZSlcbiAgICB9XG5cbiAgICBpZiAoIXByb2dyYW0pIHtcbiAgICAgIHZhciBwcm9nQ2FjaGUgPSBlbnYuZ2xvYmFsLmRlZigne30nKVxuICAgICAgdmFyIFBST0dSQU0gPSBhcmdzLnNoYWRlci5wcm9nVmFyLmFwcGVuZChlbnYsIGlubmVyKVxuICAgICAgdmFyIFBST0dfSUQgPSBpbm5lci5kZWYoUFJPR1JBTSwgJy5pZCcpXG4gICAgICB2YXIgQ0FDSEVEX1BST0MgPSBpbm5lci5kZWYocHJvZ0NhY2hlLCAnWycsIFBST0dfSUQsICddJylcbiAgICAgIGlubmVyKFxuICAgICAgICBlbnYuc2hhcmVkLmdsLCAnLnVzZVByb2dyYW0oJywgUFJPR1JBTSwgJy5wcm9ncmFtKTsnLFxuICAgICAgICAnaWYoIScsIENBQ0hFRF9QUk9DLCAnKXsnLFxuICAgICAgICBDQUNIRURfUFJPQywgJz0nLCBwcm9nQ2FjaGUsICdbJywgUFJPR19JRCwgJ109JyxcbiAgICAgICAgZW52LmxpbmsoZnVuY3Rpb24gKHByb2dyYW0pIHtcbiAgICAgICAgICByZXR1cm4gY3JlYXRlQm9keShcbiAgICAgICAgICAgIGVtaXRCYXRjaER5bmFtaWNTaGFkZXJCb2R5LCBlbnYsIGFyZ3MsIHByb2dyYW0sIDIpXG4gICAgICAgIH0pLCAnKCcsIFBST0dSQU0sICcpO30nLFxuICAgICAgICBDQUNIRURfUFJPQywgJy5jYWxsKHRoaXMsYTBbJywgQkFUQ0hfSUQsICddLCcsIEJBVENIX0lELCAnKTsnKVxuICAgIH0gZWxzZSB7XG4gICAgICBlbWl0QXR0cmlidXRlcyhlbnYsIG91dGVyLCBhcmdzLCBwcm9ncmFtLmF0dHJpYnV0ZXMsIGlzT3V0ZXJEZWZuKVxuICAgICAgZW1pdEF0dHJpYnV0ZXMoZW52LCBpbm5lciwgYXJncywgcHJvZ3JhbS5hdHRyaWJ1dGVzLCBpc0lubmVyRGVmbilcbiAgICAgIGVtaXRVbmlmb3JtcyhlbnYsIG91dGVyLCBhcmdzLCBwcm9ncmFtLnVuaWZvcm1zLCBpc091dGVyRGVmbilcbiAgICAgIGVtaXRVbmlmb3JtcyhlbnYsIGlubmVyLCBhcmdzLCBwcm9ncmFtLnVuaWZvcm1zLCBpc0lubmVyRGVmbilcbiAgICAgIGVtaXREcmF3KGVudiwgb3V0ZXIsIGlubmVyLCBhcmdzKVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGVtaXRCYXRjaFByb2MgKGVudiwgYXJncykge1xuICAgIHZhciBiYXRjaCA9IGVudi5wcm9jKCdiYXRjaCcsIDIpXG4gICAgZW52LmJhdGNoSWQgPSAnMCdcblxuICAgIGluamVjdEV4dGVuc2lvbnMoZW52LCBiYXRjaClcblxuICAgIC8vIENoZWNrIGlmIGFueSBjb250ZXh0IHZhcmlhYmxlcyBkZXBlbmQgb24gcHJvcHNcbiAgICB2YXIgY29udGV4dER5bmFtaWMgPSBmYWxzZVxuICAgIHZhciBuZWVkc0NvbnRleHQgPSB0cnVlXG4gICAgT2JqZWN0LmtleXMoYXJncy5jb250ZXh0KS5mb3JFYWNoKGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgICBjb250ZXh0RHluYW1pYyA9IGNvbnRleHREeW5hbWljIHx8IGFyZ3MuY29udGV4dFtuYW1lXS5wcm9wRGVwXG4gICAgfSlcbiAgICBpZiAoIWNvbnRleHREeW5hbWljKSB7XG4gICAgICBlbWl0Q29udGV4dChlbnYsIGJhdGNoLCBhcmdzLmNvbnRleHQpXG4gICAgICBuZWVkc0NvbnRleHQgPSBmYWxzZVxuICAgIH1cblxuICAgIC8vIGZyYW1lYnVmZmVyIHN0YXRlIGFmZmVjdHMgZnJhbWVidWZmZXJXaWR0aC9oZWlnaHQgY29udGV4dCB2YXJzXG4gICAgdmFyIGZyYW1lYnVmZmVyID0gYXJncy5mcmFtZWJ1ZmZlclxuICAgIHZhciBuZWVkc0ZyYW1lYnVmZmVyID0gZmFsc2VcbiAgICBpZiAoZnJhbWVidWZmZXIpIHtcbiAgICAgIGlmIChmcmFtZWJ1ZmZlci5wcm9wRGVwKSB7XG4gICAgICAgIGNvbnRleHREeW5hbWljID0gbmVlZHNGcmFtZWJ1ZmZlciA9IHRydWVcbiAgICAgIH0gZWxzZSBpZiAoZnJhbWVidWZmZXIuY29udGV4dERlcCAmJiBjb250ZXh0RHluYW1pYykge1xuICAgICAgICBuZWVkc0ZyYW1lYnVmZmVyID0gdHJ1ZVxuICAgICAgfVxuICAgICAgaWYgKCFuZWVkc0ZyYW1lYnVmZmVyKSB7XG4gICAgICAgIGVtaXRQb2xsRnJhbWVidWZmZXIoZW52LCBiYXRjaCwgZnJhbWVidWZmZXIpXG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGVtaXRQb2xsRnJhbWVidWZmZXIoZW52LCBiYXRjaCwgbnVsbClcbiAgICB9XG5cbiAgICAvLyB2aWV3cG9ydCBpcyB3ZWlyZCBiZWNhdXNlIGl0IGNhbiBhZmZlY3QgY29udGV4dCB2YXJzXG4gICAgaWYgKGFyZ3Muc3RhdGUudmlld3BvcnQgJiYgYXJncy5zdGF0ZS52aWV3cG9ydC5wcm9wRGVwKSB7XG4gICAgICBjb250ZXh0RHluYW1pYyA9IHRydWVcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBpc0lubmVyRGVmbiAoZGVmbikge1xuICAgICAgcmV0dXJuIChkZWZuLmNvbnRleHREZXAgJiYgY29udGV4dER5bmFtaWMpIHx8IGRlZm4ucHJvcERlcFxuICAgIH1cblxuICAgIC8vIHNldCB3ZWJnbCBvcHRpb25zXG4gICAgZW1pdFBvbGxTdGF0ZShlbnYsIGJhdGNoLCBhcmdzKVxuICAgIGVtaXRTZXRPcHRpb25zKGVudiwgYmF0Y2gsIGFyZ3Muc3RhdGUsIGZ1bmN0aW9uIChkZWZuKSB7XG4gICAgICByZXR1cm4gIWlzSW5uZXJEZWZuKGRlZm4pXG4gICAgfSlcblxuICAgIGlmICghYXJncy5wcm9maWxlIHx8ICFpc0lubmVyRGVmbihhcmdzLnByb2ZpbGUpKSB7XG4gICAgICBlbWl0UHJvZmlsZShlbnYsIGJhdGNoLCBhcmdzLCBmYWxzZSwgJ2ExJylcbiAgICB9XG5cbiAgICAvLyBTYXZlIHRoZXNlIHZhbHVlcyB0byBhcmdzIHNvIHRoYXQgdGhlIGJhdGNoIGJvZHkgcm91dGluZSBjYW4gdXNlIHRoZW1cbiAgICBhcmdzLmNvbnRleHREZXAgPSBjb250ZXh0RHluYW1pY1xuICAgIGFyZ3MubmVlZHNDb250ZXh0ID0gbmVlZHNDb250ZXh0XG4gICAgYXJncy5uZWVkc0ZyYW1lYnVmZmVyID0gbmVlZHNGcmFtZWJ1ZmZlclxuXG4gICAgLy8gZGV0ZXJtaW5lIGlmIHNoYWRlciBpcyBkeW5hbWljXG4gICAgdmFyIHByb2dEZWZuID0gYXJncy5zaGFkZXIucHJvZ1ZhclxuICAgIGlmICgocHJvZ0RlZm4uY29udGV4dERlcCAmJiBjb250ZXh0RHluYW1pYykgfHwgcHJvZ0RlZm4ucHJvcERlcCkge1xuICAgICAgZW1pdEJhdGNoQm9keShcbiAgICAgICAgZW52LFxuICAgICAgICBiYXRjaCxcbiAgICAgICAgYXJncyxcbiAgICAgICAgbnVsbClcbiAgICB9IGVsc2Uge1xuICAgICAgdmFyIFBST0dSQU0gPSBwcm9nRGVmbi5hcHBlbmQoZW52LCBiYXRjaClcbiAgICAgIGJhdGNoKGVudi5zaGFyZWQuZ2wsICcudXNlUHJvZ3JhbSgnLCBQUk9HUkFNLCAnLnByb2dyYW0pOycpXG4gICAgICBpZiAoYXJncy5zaGFkZXIucHJvZ3JhbSkge1xuICAgICAgICBlbWl0QmF0Y2hCb2R5KFxuICAgICAgICAgIGVudixcbiAgICAgICAgICBiYXRjaCxcbiAgICAgICAgICBhcmdzLFxuICAgICAgICAgIGFyZ3Muc2hhZGVyLnByb2dyYW0pXG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YXIgYmF0Y2hDYWNoZSA9IGVudi5nbG9iYWwuZGVmKCd7fScpXG4gICAgICAgIHZhciBQUk9HX0lEID0gYmF0Y2guZGVmKFBST0dSQU0sICcuaWQnKVxuICAgICAgICB2YXIgQ0FDSEVEX1BST0MgPSBiYXRjaC5kZWYoYmF0Y2hDYWNoZSwgJ1snLCBQUk9HX0lELCAnXScpXG4gICAgICAgIGJhdGNoKFxuICAgICAgICAgIGVudi5jb25kKENBQ0hFRF9QUk9DKVxuICAgICAgICAgICAgLnRoZW4oQ0FDSEVEX1BST0MsICcuY2FsbCh0aGlzLGEwLGExKTsnKVxuICAgICAgICAgICAgLmVsc2UoXG4gICAgICAgICAgICAgIENBQ0hFRF9QUk9DLCAnPScsIGJhdGNoQ2FjaGUsICdbJywgUFJPR19JRCwgJ109JyxcbiAgICAgICAgICAgICAgZW52LmxpbmsoZnVuY3Rpb24gKHByb2dyYW0pIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gY3JlYXRlQm9keShlbWl0QmF0Y2hCb2R5LCBlbnYsIGFyZ3MsIHByb2dyYW0sIDIpXG4gICAgICAgICAgICAgIH0pLCAnKCcsIFBST0dSQU0sICcpOycsXG4gICAgICAgICAgICAgIENBQ0hFRF9QUk9DLCAnLmNhbGwodGhpcyxhMCxhMSk7JykpXG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKE9iamVjdC5rZXlzKGFyZ3Muc3RhdGUpLmxlbmd0aCA+IDApIHtcbiAgICAgIGJhdGNoKGVudi5zaGFyZWQuY3VycmVudCwgJy5kaXJ0eT10cnVlOycpXG4gICAgfVxuICB9XG5cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBTQ09QRSBDT01NQU5EXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgZnVuY3Rpb24gZW1pdFNjb3BlUHJvYyAoZW52LCBhcmdzKSB7XG4gICAgdmFyIHNjb3BlID0gZW52LnByb2MoJ3Njb3BlJywgMylcbiAgICBlbnYuYmF0Y2hJZCA9ICdhMidcblxuICAgIHZhciBzaGFyZWQgPSBlbnYuc2hhcmVkXG4gICAgdmFyIENVUlJFTlRfU1RBVEUgPSBzaGFyZWQuY3VycmVudFxuXG4gICAgZW1pdENvbnRleHQoZW52LCBzY29wZSwgYXJncy5jb250ZXh0KVxuXG4gICAgaWYgKGFyZ3MuZnJhbWVidWZmZXIpIHtcbiAgICAgIGFyZ3MuZnJhbWVidWZmZXIuYXBwZW5kKGVudiwgc2NvcGUpXG4gICAgfVxuXG4gICAgc29ydFN0YXRlKE9iamVjdC5rZXlzKGFyZ3Muc3RhdGUpKS5mb3JFYWNoKGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgICB2YXIgZGVmbiA9IGFyZ3Muc3RhdGVbbmFtZV1cbiAgICAgIHZhciB2YWx1ZSA9IGRlZm4uYXBwZW5kKGVudiwgc2NvcGUpXG4gICAgICBpZiAoaXNBcnJheUxpa2UodmFsdWUpKSB7XG4gICAgICAgIHZhbHVlLmZvckVhY2goZnVuY3Rpb24gKHYsIGkpIHtcbiAgICAgICAgICBzY29wZS5zZXQoZW52Lm5leHRbbmFtZV0sICdbJyArIGkgKyAnXScsIHYpXG4gICAgICAgIH0pXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzY29wZS5zZXQoc2hhcmVkLm5leHQsICcuJyArIG5hbWUsIHZhbHVlKVxuICAgICAgfVxuICAgIH0pXG5cbiAgICBlbWl0UHJvZmlsZShlbnYsIHNjb3BlLCBhcmdzLCB0cnVlLCB0cnVlKVxuXG4gICAgO1tTX0VMRU1FTlRTLCBTX09GRlNFVCwgU19DT1VOVCwgU19JTlNUQU5DRVMsIFNfUFJJTUlUSVZFXS5mb3JFYWNoKFxuICAgICAgZnVuY3Rpb24gKG9wdCkge1xuICAgICAgICB2YXIgdmFyaWFibGUgPSBhcmdzLmRyYXdbb3B0XVxuICAgICAgICBpZiAoIXZhcmlhYmxlKSB7XG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgc2NvcGUuc2V0KHNoYXJlZC5kcmF3LCAnLicgKyBvcHQsICcnICsgdmFyaWFibGUuYXBwZW5kKGVudiwgc2NvcGUpKVxuICAgICAgfSlcblxuICAgIE9iamVjdC5rZXlzKGFyZ3MudW5pZm9ybXMpLmZvckVhY2goZnVuY3Rpb24gKG9wdCkge1xuICAgICAgc2NvcGUuc2V0KFxuICAgICAgICBzaGFyZWQudW5pZm9ybXMsXG4gICAgICAgICdbJyArIHN0cmluZ1N0b3JlLmlkKG9wdCkgKyAnXScsXG4gICAgICAgIGFyZ3MudW5pZm9ybXNbb3B0XS5hcHBlbmQoZW52LCBzY29wZSkpXG4gICAgfSlcblxuICAgIE9iamVjdC5rZXlzKGFyZ3MuYXR0cmlidXRlcykuZm9yRWFjaChmdW5jdGlvbiAobmFtZSkge1xuICAgICAgdmFyIHJlY29yZCA9IGFyZ3MuYXR0cmlidXRlc1tuYW1lXS5hcHBlbmQoZW52LCBzY29wZSlcbiAgICAgIHZhciBzY29wZUF0dHJpYiA9IGVudi5zY29wZUF0dHJpYihuYW1lKVxuICAgICAgT2JqZWN0LmtleXMobmV3IEF0dHJpYnV0ZVJlY29yZCgpKS5mb3JFYWNoKGZ1bmN0aW9uIChwcm9wKSB7XG4gICAgICAgIHNjb3BlLnNldChzY29wZUF0dHJpYiwgJy4nICsgcHJvcCwgcmVjb3JkW3Byb3BdKVxuICAgICAgfSlcbiAgICB9KVxuXG4gICAgZnVuY3Rpb24gc2F2ZVNoYWRlciAobmFtZSkge1xuICAgICAgdmFyIHNoYWRlciA9IGFyZ3Muc2hhZGVyW25hbWVdXG4gICAgICBpZiAoc2hhZGVyKSB7XG4gICAgICAgIHNjb3BlLnNldChzaGFyZWQuc2hhZGVyLCAnLicgKyBuYW1lLCBzaGFkZXIuYXBwZW5kKGVudiwgc2NvcGUpKVxuICAgICAgfVxuICAgIH1cbiAgICBzYXZlU2hhZGVyKFNfVkVSVClcbiAgICBzYXZlU2hhZGVyKFNfRlJBRylcblxuICAgIGlmIChPYmplY3Qua2V5cyhhcmdzLnN0YXRlKS5sZW5ndGggPiAwKSB7XG4gICAgICBzY29wZShDVVJSRU5UX1NUQVRFLCAnLmRpcnR5PXRydWU7JylcbiAgICAgIHNjb3BlLmV4aXQoQ1VSUkVOVF9TVEFURSwgJy5kaXJ0eT10cnVlOycpXG4gICAgfVxuXG4gICAgc2NvcGUoJ2ExKCcsIGVudi5zaGFyZWQuY29udGV4dCwgJyxhMCwwKTsnKVxuICB9XG5cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBNQUlOIERSQVcgQ09NTUFORFxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIGZ1bmN0aW9uIGNvbXBpbGVDb21tYW5kIChvcHRpb25zLCBhdHRyaWJ1dGVzLCB1bmlmb3JtcywgY29udGV4dCwgc3RhdHMpIHtcbiAgICB2YXIgZW52ID0gY3JlYXRlUkVHTEVudmlyb25tZW50KClcblxuICAgIC8vIGxpbmsgc3RhdHMsIHNvIHRoYXQgd2UgY2FuIGVhc2lseSBhY2Nlc3MgaXQgaW4gdGhlIHByb2dyYW0uXG4gICAgZW52LnN0YXRzID0gZW52Lmxpbmsoc3RhdHMpXG5cbiAgICB2YXIgYXJncyA9IHBhcnNlQXJndW1lbnRzKG9wdGlvbnMsIGF0dHJpYnV0ZXMsIHVuaWZvcm1zLCBjb250ZXh0KVxuXG4gICAgZW1pdERyYXdQcm9jKGVudiwgYXJncylcbiAgICBlbWl0U2NvcGVQcm9jKGVudiwgYXJncylcbiAgICBlbWl0QmF0Y2hQcm9jKGVudiwgYXJncylcblxuICAgIHJldHVybiBlbnYuY29tcGlsZSgpXG4gIH1cblxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vIFBPTEwgLyBSRUZSRVNIXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgcmV0dXJuIHtcbiAgICBuZXh0OiBuZXh0U3RhdGUsXG4gICAgY3VycmVudDogY3VycmVudFN0YXRlLFxuICAgIHByb2NzOiAoZnVuY3Rpb24gKCkge1xuICAgICAgdmFyIGVudiA9IGNyZWF0ZVJFR0xFbnZpcm9ubWVudCgpXG4gICAgICB2YXIgcG9sbCA9IGVudi5wcm9jKCdwb2xsJylcbiAgICAgIHZhciByZWZyZXNoID0gZW52LnByb2MoJ3JlZnJlc2gnKVxuICAgICAgdmFyIGNvbW1vbiA9IGVudi5ibG9jaygpXG4gICAgICBwb2xsKGNvbW1vbilcbiAgICAgIHJlZnJlc2goY29tbW9uKVxuXG4gICAgICB2YXIgc2hhcmVkID0gZW52LnNoYXJlZFxuICAgICAgdmFyIEdMID0gc2hhcmVkLmdsXG4gICAgICB2YXIgTkVYVF9TVEFURSA9IHNoYXJlZC5uZXh0XG4gICAgICB2YXIgQ1VSUkVOVF9TVEFURSA9IHNoYXJlZC5jdXJyZW50XG5cbiAgICAgIGNvbW1vbihDVVJSRU5UX1NUQVRFLCAnLmRpcnR5PWZhbHNlOycpXG5cbiAgICAgIGVtaXRQb2xsRnJhbWVidWZmZXIoZW52LCBwb2xsKVxuXG4gICAgICByZWZyZXNoKHNoYXJlZC5mcmFtZWJ1ZmZlciwgJy5kaXJ0eT10cnVlOycpXG4gICAgICBlbWl0UG9sbEZyYW1lYnVmZmVyKGVudiwgcmVmcmVzaClcblxuICAgICAgLy8gRklYTUU6IHJlZnJlc2ggc2hvdWxkIHVwZGF0ZSB2ZXJ0ZXggYXR0cmlidXRlIHBvaW50ZXJzXG5cbiAgICAgIE9iamVjdC5rZXlzKEdMX0ZMQUdTKS5mb3JFYWNoKGZ1bmN0aW9uIChmbGFnKSB7XG4gICAgICAgIHZhciBjYXAgPSBHTF9GTEFHU1tmbGFnXVxuICAgICAgICB2YXIgTkVYVCA9IGNvbW1vbi5kZWYoTkVYVF9TVEFURSwgJy4nLCBmbGFnKVxuICAgICAgICB2YXIgYmxvY2sgPSBlbnYuYmxvY2soKVxuICAgICAgICBibG9jaygnaWYoJywgTkVYVCwgJyl7JyxcbiAgICAgICAgICBHTCwgJy5lbmFibGUoJywgY2FwLCAnKX1lbHNleycsXG4gICAgICAgICAgR0wsICcuZGlzYWJsZSgnLCBjYXAsICcpfScsXG4gICAgICAgICAgQ1VSUkVOVF9TVEFURSwgJy4nLCBmbGFnLCAnPScsIE5FWFQsICc7JylcbiAgICAgICAgcmVmcmVzaChibG9jaylcbiAgICAgICAgcG9sbChcbiAgICAgICAgICAnaWYoJywgTkVYVCwgJyE9PScsIENVUlJFTlRfU1RBVEUsICcuJywgZmxhZywgJyl7JyxcbiAgICAgICAgICBibG9jayxcbiAgICAgICAgICAnfScpXG4gICAgICB9KVxuXG4gICAgICBPYmplY3Qua2V5cyhHTF9WQVJJQUJMRVMpLmZvckVhY2goZnVuY3Rpb24gKG5hbWUpIHtcbiAgICAgICAgdmFyIGZ1bmMgPSBHTF9WQVJJQUJMRVNbbmFtZV1cbiAgICAgICAgdmFyIGluaXQgPSBjdXJyZW50U3RhdGVbbmFtZV1cbiAgICAgICAgdmFyIE5FWFQsIENVUlJFTlRcbiAgICAgICAgdmFyIGJsb2NrID0gZW52LmJsb2NrKClcbiAgICAgICAgYmxvY2soR0wsICcuJywgZnVuYywgJygnKVxuICAgICAgICBpZiAoaXNBcnJheUxpa2UoaW5pdCkpIHtcbiAgICAgICAgICB2YXIgbiA9IGluaXQubGVuZ3RoXG4gICAgICAgICAgTkVYVCA9IGVudi5nbG9iYWwuZGVmKE5FWFRfU1RBVEUsICcuJywgbmFtZSlcbiAgICAgICAgICBDVVJSRU5UID0gZW52Lmdsb2JhbC5kZWYoQ1VSUkVOVF9TVEFURSwgJy4nLCBuYW1lKVxuICAgICAgICAgIGJsb2NrKFxuICAgICAgICAgICAgbG9vcChuLCBmdW5jdGlvbiAoaSkge1xuICAgICAgICAgICAgICByZXR1cm4gTkVYVCArICdbJyArIGkgKyAnXSdcbiAgICAgICAgICAgIH0pLCAnKTsnLFxuICAgICAgICAgICAgbG9vcChuLCBmdW5jdGlvbiAoaSkge1xuICAgICAgICAgICAgICByZXR1cm4gQ1VSUkVOVCArICdbJyArIGkgKyAnXT0nICsgTkVYVCArICdbJyArIGkgKyAnXTsnXG4gICAgICAgICAgICB9KS5qb2luKCcnKSlcbiAgICAgICAgICBwb2xsKFxuICAgICAgICAgICAgJ2lmKCcsIGxvb3AobiwgZnVuY3Rpb24gKGkpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIE5FWFQgKyAnWycgKyBpICsgJ10hPT0nICsgQ1VSUkVOVCArICdbJyArIGkgKyAnXSdcbiAgICAgICAgICAgIH0pLmpvaW4oJ3x8JyksICcpeycsXG4gICAgICAgICAgICBibG9jayxcbiAgICAgICAgICAgICd9JylcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBORVhUID0gY29tbW9uLmRlZihORVhUX1NUQVRFLCAnLicsIG5hbWUpXG4gICAgICAgICAgQ1VSUkVOVCA9IGNvbW1vbi5kZWYoQ1VSUkVOVF9TVEFURSwgJy4nLCBuYW1lKVxuICAgICAgICAgIGJsb2NrKFxuICAgICAgICAgICAgTkVYVCwgJyk7JyxcbiAgICAgICAgICAgIENVUlJFTlRfU1RBVEUsICcuJywgbmFtZSwgJz0nLCBORVhULCAnOycpXG4gICAgICAgICAgcG9sbChcbiAgICAgICAgICAgICdpZignLCBORVhULCAnIT09JywgQ1VSUkVOVCwgJyl7JyxcbiAgICAgICAgICAgIGJsb2NrLFxuICAgICAgICAgICAgJ30nKVxuICAgICAgICB9XG4gICAgICAgIHJlZnJlc2goYmxvY2spXG4gICAgICB9KVxuXG4gICAgICByZXR1cm4gZW52LmNvbXBpbGUoKVxuICAgIH0pKCksXG4gICAgY29tcGlsZTogY29tcGlsZUNvbW1hbmRcbiAgfVxufVxuIiwidmFyIGNoZWNrID0gcmVxdWlyZSgnLi91dGlsL2NoZWNrJylcblxudmFyIFZBUklBQkxFX0NPVU5URVIgPSAwXG5cbnZhciBEWU5fRlVOQyA9IDBcbnZhciBEWU5fUEVORElOR19GTEFHID0gMTI4XG5cbmZ1bmN0aW9uIER5bmFtaWNWYXJpYWJsZSAodHlwZSwgZGF0YSkge1xuICB0aGlzLmlkID0gKFZBUklBQkxFX0NPVU5URVIrKylcbiAgdGhpcy50eXBlID0gdHlwZVxuICB0aGlzLmRhdGEgPSBkYXRhXG59XG5cbmZ1bmN0aW9uIGVzY2FwZVN0ciAoc3RyKSB7XG4gIHJldHVybiBzdHIucmVwbGFjZSgvXFxcXC9nLCAnXFxcXFxcXFwnKS5yZXBsYWNlKC9cIi9nLCAnXFxcXFwiJylcbn1cblxuZnVuY3Rpb24gc3BsaXRQYXJ0cyAoc3RyKSB7XG4gIGlmIChzdHIubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIFtdXG4gIH1cblxuICB2YXIgZmlyc3RDaGFyID0gc3RyLmNoYXJBdCgwKVxuICB2YXIgbGFzdENoYXIgPSBzdHIuY2hhckF0KHN0ci5sZW5ndGggLSAxKVxuXG4gIGlmIChzdHIubGVuZ3RoID4gMSAmJlxuICAgICAgZmlyc3RDaGFyID09PSBsYXN0Q2hhciAmJlxuICAgICAgKGZpcnN0Q2hhciA9PT0gJ1wiJyB8fCBmaXJzdENoYXIgPT09IFwiJ1wiKSkge1xuICAgIHJldHVybiBbJ1wiJyArIGVzY2FwZVN0cihzdHIuc3Vic3RyKDEsIHN0ci5sZW5ndGggLSAyKSkgKyAnXCInXVxuICB9XG5cbiAgdmFyIHBhcnRzID0gL1xcWyhmYWxzZXx0cnVlfG51bGx8XFxkK3wnW14nXSonfFwiW15cIl0qXCIpXFxdLy5leGVjKHN0cilcbiAgaWYgKHBhcnRzKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIHNwbGl0UGFydHMoc3RyLnN1YnN0cigwLCBwYXJ0cy5pbmRleCkpXG4gICAgICAuY29uY2F0KHNwbGl0UGFydHMocGFydHNbMV0pKVxuICAgICAgLmNvbmNhdChzcGxpdFBhcnRzKHN0ci5zdWJzdHIocGFydHMuaW5kZXggKyBwYXJ0c1swXS5sZW5ndGgpKSlcbiAgICApXG4gIH1cblxuICB2YXIgc3VicGFydHMgPSBzdHIuc3BsaXQoJy4nKVxuICBpZiAoc3VicGFydHMubGVuZ3RoID09PSAxKSB7XG4gICAgcmV0dXJuIFsnXCInICsgZXNjYXBlU3RyKHN0cikgKyAnXCInXVxuICB9XG5cbiAgdmFyIHJlc3VsdCA9IFtdXG4gIGZvciAodmFyIGkgPSAwOyBpIDwgc3VicGFydHMubGVuZ3RoOyArK2kpIHtcbiAgICByZXN1bHQgPSByZXN1bHQuY29uY2F0KHNwbGl0UGFydHMoc3VicGFydHNbaV0pKVxuICB9XG4gIHJldHVybiByZXN1bHRcbn1cblxuZnVuY3Rpb24gdG9BY2Nlc3NvclN0cmluZyAoc3RyKSB7XG4gIHJldHVybiAnWycgKyBzcGxpdFBhcnRzKHN0cikuam9pbignXVsnKSArICddJ1xufVxuXG5mdW5jdGlvbiBkZWZpbmVEeW5hbWljICh0eXBlLCBkYXRhKSB7XG4gIHN3aXRjaCAodHlwZW9mIGRhdGEpIHtcbiAgICBjYXNlICdib29sZWFuJzpcbiAgICBjYXNlICdudW1iZXInOlxuICAgIGNhc2UgJ3N0cmluZyc6XG4gICAgICByZXR1cm4gbmV3IER5bmFtaWNWYXJpYWJsZSh0eXBlLCB0b0FjY2Vzc29yU3RyaW5nKGRhdGEgKyAnJykpXG5cbiAgICBjYXNlICd1bmRlZmluZWQnOlxuICAgICAgcmV0dXJuIG5ldyBEeW5hbWljVmFyaWFibGUodHlwZSB8IERZTl9QRU5ESU5HX0ZMQUcsIG51bGwpXG5cbiAgICBkZWZhdWx0OlxuICAgICAgY2hlY2sucmFpc2UoJ2ludmFsaWQgcHJvcGVydHkgdHlwZScpXG4gIH1cbn1cblxuZnVuY3Rpb24gaXNEeW5hbWljICh4KSB7XG4gIHJldHVybiAodHlwZW9mIHggPT09ICdmdW5jdGlvbicgJiYgIXguX3JlZ2xUeXBlKSB8fFxuICAgICAgICAgeCBpbnN0YW5jZW9mIER5bmFtaWNWYXJpYWJsZVxufVxuXG5mdW5jdGlvbiB1bmJveCAoeCwgcGF0aCkge1xuICBpZiAoeCBpbnN0YW5jZW9mIER5bmFtaWNWYXJpYWJsZSkge1xuICAgIGlmICh4LnR5cGUgJiBEWU5fUEVORElOR19GTEFHKSB7XG4gICAgICByZXR1cm4gbmV3IER5bmFtaWNWYXJpYWJsZShcbiAgICAgICAgeC50eXBlICYgfkRZTl9QRU5ESU5HX0ZMQUcsXG4gICAgICAgIHRvQWNjZXNzb3JTdHJpbmcocGF0aCkpXG4gICAgfVxuICB9IGVsc2UgaWYgKHR5cGVvZiB4ID09PSAnZnVuY3Rpb24nKSB7XG4gICAgcmV0dXJuIG5ldyBEeW5hbWljVmFyaWFibGUoRFlOX0ZVTkMsIHgpXG4gIH1cbiAgcmV0dXJuIHhcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIGRlZmluZTogZGVmaW5lRHluYW1pYyxcbiAgaXNEeW5hbWljOiBpc0R5bmFtaWMsXG4gIHVuYm94OiB1bmJveCxcbiAgYWNjZXNzb3I6IHRvQWNjZXNzb3JTdHJpbmdcbn1cbiIsInZhciBjaGVjayA9IHJlcXVpcmUoJy4vdXRpbC9jaGVjaycpXG52YXIgaXNUeXBlZEFycmF5ID0gcmVxdWlyZSgnLi91dGlsL2lzLXR5cGVkLWFycmF5JylcbnZhciBpc05EQXJyYXlMaWtlID0gcmVxdWlyZSgnLi91dGlsL2lzLW5kYXJyYXknKVxudmFyIHZhbHVlcyA9IHJlcXVpcmUoJy4vdXRpbC92YWx1ZXMnKVxuXG52YXIgcHJpbVR5cGVzID0gcmVxdWlyZSgnLi9jb25zdGFudHMvcHJpbWl0aXZlcy5qc29uJylcbnZhciB1c2FnZVR5cGVzID0gcmVxdWlyZSgnLi9jb25zdGFudHMvdXNhZ2UuanNvbicpXG5cbnZhciBHTF9QT0lOVFMgPSAwXG52YXIgR0xfTElORVMgPSAxXG52YXIgR0xfVFJJQU5HTEVTID0gNFxuXG52YXIgR0xfQllURSA9IDUxMjBcbnZhciBHTF9VTlNJR05FRF9CWVRFID0gNTEyMVxudmFyIEdMX1NIT1JUID0gNTEyMlxudmFyIEdMX1VOU0lHTkVEX1NIT1JUID0gNTEyM1xudmFyIEdMX0lOVCA9IDUxMjRcbnZhciBHTF9VTlNJR05FRF9JTlQgPSA1MTI1XG5cbnZhciBHTF9FTEVNRU5UX0FSUkFZX0JVRkZFUiA9IDM0OTYzXG5cbnZhciBHTF9TVFJFQU1fRFJBVyA9IDB4ODhFMFxudmFyIEdMX1NUQVRJQ19EUkFXID0gMHg4OEU0XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gd3JhcEVsZW1lbnRzU3RhdGUgKGdsLCBleHRlbnNpb25zLCBidWZmZXJTdGF0ZSwgc3RhdHMpIHtcbiAgdmFyIGVsZW1lbnRTZXQgPSB7fVxuICB2YXIgZWxlbWVudENvdW50ID0gMFxuXG4gIHZhciBlbGVtZW50VHlwZXMgPSB7XG4gICAgJ3VpbnQ4JzogR0xfVU5TSUdORURfQllURSxcbiAgICAndWludDE2JzogR0xfVU5TSUdORURfU0hPUlRcbiAgfVxuXG4gIGlmIChleHRlbnNpb25zLm9lc19lbGVtZW50X2luZGV4X3VpbnQpIHtcbiAgICBlbGVtZW50VHlwZXMudWludDMyID0gR0xfVU5TSUdORURfSU5UXG4gIH1cblxuICBmdW5jdGlvbiBSRUdMRWxlbWVudEJ1ZmZlciAoYnVmZmVyKSB7XG4gICAgdGhpcy5pZCA9IGVsZW1lbnRDb3VudCsrXG4gICAgZWxlbWVudFNldFt0aGlzLmlkXSA9IHRoaXNcbiAgICB0aGlzLmJ1ZmZlciA9IGJ1ZmZlclxuICAgIHRoaXMucHJpbVR5cGUgPSBHTF9UUklBTkdMRVNcbiAgICB0aGlzLnZlcnRDb3VudCA9IDBcbiAgICB0aGlzLnR5cGUgPSAwXG4gIH1cblxuICBSRUdMRWxlbWVudEJ1ZmZlci5wcm90b3R5cGUuYmluZCA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLmJ1ZmZlci5iaW5kKClcbiAgfVxuXG4gIHZhciBidWZmZXJQb29sID0gW11cblxuICBmdW5jdGlvbiBjcmVhdGVFbGVtZW50U3RyZWFtIChkYXRhKSB7XG4gICAgdmFyIHJlc3VsdCA9IGJ1ZmZlclBvb2wucG9wKClcbiAgICBpZiAoIXJlc3VsdCkge1xuICAgICAgcmVzdWx0ID0gbmV3IFJFR0xFbGVtZW50QnVmZmVyKGJ1ZmZlclN0YXRlLmNyZWF0ZShcbiAgICAgICAgbnVsbCxcbiAgICAgICAgR0xfRUxFTUVOVF9BUlJBWV9CVUZGRVIsXG4gICAgICAgIHRydWUpLl9idWZmZXIpXG4gICAgfVxuICAgIGluaXRFbGVtZW50cyhyZXN1bHQsIGRhdGEsIEdMX1NUUkVBTV9EUkFXLCAtMSwgLTEsIDAsIDApXG4gICAgcmV0dXJuIHJlc3VsdFxuICB9XG5cbiAgZnVuY3Rpb24gZGVzdHJveUVsZW1lbnRTdHJlYW0gKGVsZW1lbnRzKSB7XG4gICAgYnVmZmVyUG9vbC5wdXNoKGVsZW1lbnRzKVxuICB9XG5cbiAgZnVuY3Rpb24gaW5pdEVsZW1lbnRzIChcbiAgICBlbGVtZW50cyxcbiAgICBkYXRhLFxuICAgIHVzYWdlLFxuICAgIHByaW0sXG4gICAgY291bnQsXG4gICAgYnl0ZUxlbmd0aCxcbiAgICB0eXBlKSB7XG4gICAgdmFyIHByZWRpY3RlZFR5cGUgPSB0eXBlXG4gICAgaWYgKCF0eXBlICYmIChcbiAgICAgICAgIWlzVHlwZWRBcnJheShkYXRhKSB8fFxuICAgICAgIChpc05EQXJyYXlMaWtlKGRhdGEpICYmICFpc1R5cGVkQXJyYXkoZGF0YS5kYXRhKSkpKSB7XG4gICAgICBwcmVkaWN0ZWRUeXBlID0gZXh0ZW5zaW9ucy5vZXNfZWxlbWVudF9pbmRleF91aW50XG4gICAgICAgID8gR0xfVU5TSUdORURfSU5UXG4gICAgICAgIDogR0xfVU5TSUdORURfU0hPUlRcbiAgICB9XG4gICAgZWxlbWVudHMuYnVmZmVyLmJpbmQoKVxuICAgIGJ1ZmZlclN0YXRlLl9pbml0QnVmZmVyKFxuICAgICAgZWxlbWVudHMuYnVmZmVyLFxuICAgICAgZGF0YSxcbiAgICAgIHVzYWdlLFxuICAgICAgcHJlZGljdGVkVHlwZSxcbiAgICAgIDMpXG5cbiAgICB2YXIgZHR5cGUgPSB0eXBlXG4gICAgaWYgKCF0eXBlKSB7XG4gICAgICBzd2l0Y2ggKGVsZW1lbnRzLmJ1ZmZlci5kdHlwZSkge1xuICAgICAgICBjYXNlIEdMX1VOU0lHTkVEX0JZVEU6XG4gICAgICAgIGNhc2UgR0xfQllURTpcbiAgICAgICAgICBkdHlwZSA9IEdMX1VOU0lHTkVEX0JZVEVcbiAgICAgICAgICBicmVha1xuXG4gICAgICAgIGNhc2UgR0xfVU5TSUdORURfU0hPUlQ6XG4gICAgICAgIGNhc2UgR0xfU0hPUlQ6XG4gICAgICAgICAgZHR5cGUgPSBHTF9VTlNJR05FRF9TSE9SVFxuICAgICAgICAgIGJyZWFrXG5cbiAgICAgICAgY2FzZSBHTF9VTlNJR05FRF9JTlQ6XG4gICAgICAgIGNhc2UgR0xfSU5UOlxuICAgICAgICAgIGR0eXBlID0gR0xfVU5TSUdORURfSU5UXG4gICAgICAgICAgYnJlYWtcblxuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIGNoZWNrLnJhaXNlKCd1bnN1cHBvcnRlZCB0eXBlIGZvciBlbGVtZW50IGFycmF5JylcbiAgICAgIH1cbiAgICAgIGVsZW1lbnRzLmJ1ZmZlci5kdHlwZSA9IGR0eXBlXG4gICAgfVxuICAgIGVsZW1lbnRzLnR5cGUgPSBkdHlwZVxuXG4gICAgLy8gQ2hlY2sgb2VzX2VsZW1lbnRfaW5kZXhfdWludCBleHRlbnNpb25cbiAgICBjaGVjayhcbiAgICAgIGR0eXBlICE9PSBHTF9VTlNJR05FRF9JTlQgfHxcbiAgICAgICEhZXh0ZW5zaW9ucy5vZXNfZWxlbWVudF9pbmRleF91aW50LFxuICAgICAgJzMyIGJpdCBlbGVtZW50IGJ1ZmZlcnMgbm90IHN1cHBvcnRlZCwgZW5hYmxlIG9lc19lbGVtZW50X2luZGV4X3VpbnQgZmlyc3QnKVxuXG4gICAgLy8gdHJ5IHRvIGd1ZXNzIGRlZmF1bHQgcHJpbWl0aXZlIHR5cGUgYW5kIGFyZ3VtZW50c1xuICAgIHZhciB2ZXJ0Q291bnQgPSBjb3VudFxuICAgIGlmICh2ZXJ0Q291bnQgPCAwKSB7XG4gICAgICB2ZXJ0Q291bnQgPSBlbGVtZW50cy5idWZmZXIuYnl0ZUxlbmd0aFxuICAgICAgaWYgKGR0eXBlID09PSBHTF9VTlNJR05FRF9TSE9SVCkge1xuICAgICAgICB2ZXJ0Q291bnQgPj49IDFcbiAgICAgIH0gZWxzZSBpZiAoZHR5cGUgPT09IEdMX1VOU0lHTkVEX0lOVCkge1xuICAgICAgICB2ZXJ0Q291bnQgPj49IDJcbiAgICAgIH1cbiAgICB9XG4gICAgZWxlbWVudHMudmVydENvdW50ID0gdmVydENvdW50XG5cbiAgICAvLyB0cnkgdG8gZ3Vlc3MgcHJpbWl0aXZlIHR5cGUgZnJvbSBjZWxsIGRpbWVuc2lvblxuICAgIHZhciBwcmltVHlwZSA9IHByaW1cbiAgICBpZiAocHJpbSA8IDApIHtcbiAgICAgIHByaW1UeXBlID0gR0xfVFJJQU5HTEVTXG4gICAgICB2YXIgZGltZW5zaW9uID0gZWxlbWVudHMuYnVmZmVyLmRpbWVuc2lvblxuICAgICAgaWYgKGRpbWVuc2lvbiA9PT0gMSkgcHJpbVR5cGUgPSBHTF9QT0lOVFNcbiAgICAgIGlmIChkaW1lbnNpb24gPT09IDIpIHByaW1UeXBlID0gR0xfTElORVNcbiAgICAgIGlmIChkaW1lbnNpb24gPT09IDMpIHByaW1UeXBlID0gR0xfVFJJQU5HTEVTXG4gICAgfVxuICAgIGVsZW1lbnRzLnByaW1UeXBlID0gcHJpbVR5cGVcbiAgfVxuXG4gIGZ1bmN0aW9uIGRlc3Ryb3lFbGVtZW50cyAoZWxlbWVudHMpIHtcbiAgICBzdGF0cy5lbGVtZW50c0NvdW50LS1cblxuICAgIGNoZWNrKGVsZW1lbnRzLmJ1ZmZlciAhPT0gbnVsbCwgJ211c3Qgbm90IGRvdWJsZSBkZXN0cm95IGVsZW1lbnRzJylcbiAgICBkZWxldGUgZWxlbWVudFNldFtlbGVtZW50cy5pZF1cbiAgICBlbGVtZW50cy5idWZmZXIuZGVzdHJveSgpXG4gICAgZWxlbWVudHMuYnVmZmVyID0gbnVsbFxuICB9XG5cbiAgZnVuY3Rpb24gY3JlYXRlRWxlbWVudHMgKG9wdGlvbnMpIHtcbiAgICB2YXIgYnVmZmVyID0gYnVmZmVyU3RhdGUuY3JlYXRlKG51bGwsIEdMX0VMRU1FTlRfQVJSQVlfQlVGRkVSLCB0cnVlKVxuICAgIHZhciBlbGVtZW50cyA9IG5ldyBSRUdMRWxlbWVudEJ1ZmZlcihidWZmZXIuX2J1ZmZlcilcbiAgICBzdGF0cy5lbGVtZW50c0NvdW50KytcblxuICAgIGZ1bmN0aW9uIHJlZ2xFbGVtZW50cyAob3B0aW9ucykge1xuICAgICAgaWYgKCFvcHRpb25zKSB7XG4gICAgICAgIGJ1ZmZlcigpXG4gICAgICAgIGVsZW1lbnRzLnByaW1UeXBlID0gR0xfVFJJQU5HTEVTXG4gICAgICAgIGVsZW1lbnRzLnZlcnRDb3VudCA9IDBcbiAgICAgICAgZWxlbWVudHMudHlwZSA9IEdMX1VOU0lHTkVEX0JZVEVcbiAgICAgIH0gZWxzZSBpZiAodHlwZW9mIG9wdGlvbnMgPT09ICdudW1iZXInKSB7XG4gICAgICAgIGJ1ZmZlcihvcHRpb25zKVxuICAgICAgICBlbGVtZW50cy5wcmltVHlwZSA9IEdMX1RSSUFOR0xFU1xuICAgICAgICBlbGVtZW50cy52ZXJ0Q291bnQgPSBvcHRpb25zIHwgMFxuICAgICAgICBlbGVtZW50cy50eXBlID0gR0xfVU5TSUdORURfQllURVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdmFyIGRhdGEgPSBudWxsXG4gICAgICAgIHZhciB1c2FnZSA9IEdMX1NUQVRJQ19EUkFXXG4gICAgICAgIHZhciBwcmltVHlwZSA9IC0xXG4gICAgICAgIHZhciB2ZXJ0Q291bnQgPSAtMVxuICAgICAgICB2YXIgYnl0ZUxlbmd0aCA9IDBcbiAgICAgICAgdmFyIGR0eXBlID0gMFxuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShvcHRpb25zKSB8fFxuICAgICAgICAgICAgaXNUeXBlZEFycmF5KG9wdGlvbnMpIHx8XG4gICAgICAgICAgICBpc05EQXJyYXlMaWtlKG9wdGlvbnMpKSB7XG4gICAgICAgICAgZGF0YSA9IG9wdGlvbnNcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjaGVjay50eXBlKG9wdGlvbnMsICdvYmplY3QnLCAnaW52YWxpZCBhcmd1bWVudHMgZm9yIGVsZW1lbnRzJylcbiAgICAgICAgICBpZiAoJ2RhdGEnIGluIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIGRhdGEgPSBvcHRpb25zLmRhdGFcbiAgICAgICAgICAgIGNoZWNrKFxuICAgICAgICAgICAgICAgIEFycmF5LmlzQXJyYXkoZGF0YSkgfHxcbiAgICAgICAgICAgICAgICBpc1R5cGVkQXJyYXkoZGF0YSkgfHxcbiAgICAgICAgICAgICAgICBpc05EQXJyYXlMaWtlKGRhdGEpLFxuICAgICAgICAgICAgICAgICdpbnZhbGlkIGRhdGEgZm9yIGVsZW1lbnQgYnVmZmVyJylcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKCd1c2FnZScgaW4gb3B0aW9ucykge1xuICAgICAgICAgICAgY2hlY2sucGFyYW1ldGVyKFxuICAgICAgICAgICAgICBvcHRpb25zLnVzYWdlLFxuICAgICAgICAgICAgICB1c2FnZVR5cGVzLFxuICAgICAgICAgICAgICAnaW52YWxpZCBlbGVtZW50IGJ1ZmZlciB1c2FnZScpXG4gICAgICAgICAgICB1c2FnZSA9IHVzYWdlVHlwZXNbb3B0aW9ucy51c2FnZV1cbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKCdwcmltaXRpdmUnIGluIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIGNoZWNrLnBhcmFtZXRlcihcbiAgICAgICAgICAgICAgb3B0aW9ucy5wcmltaXRpdmUsXG4gICAgICAgICAgICAgIHByaW1UeXBlcyxcbiAgICAgICAgICAgICAgJ2ludmFsaWQgZWxlbWVudCBidWZmZXIgcHJpbWl0aXZlJylcbiAgICAgICAgICAgIHByaW1UeXBlID0gcHJpbVR5cGVzW29wdGlvbnMucHJpbWl0aXZlXVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoJ2NvdW50JyBpbiBvcHRpb25zKSB7XG4gICAgICAgICAgICBjaGVjayhcbiAgICAgICAgICAgICAgdHlwZW9mIG9wdGlvbnMuY291bnQgPT09ICdudW1iZXInICYmIG9wdGlvbnMuY291bnQgPj0gMCxcbiAgICAgICAgICAgICAgJ2ludmFsaWQgdmVydGV4IGNvdW50IGZvciBlbGVtZW50cycpXG4gICAgICAgICAgICB2ZXJ0Q291bnQgPSBvcHRpb25zLmNvdW50IHwgMFxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoJ2xlbmd0aCcgaW4gb3B0aW9ucykge1xuICAgICAgICAgICAgYnl0ZUxlbmd0aCA9IG9wdGlvbnMubGVuZ3RoIHwgMFxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoJ3R5cGUnIGluIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIGNoZWNrLnBhcmFtZXRlcihcbiAgICAgICAgICAgICAgb3B0aW9ucy50eXBlLFxuICAgICAgICAgICAgICBlbGVtZW50VHlwZXMsXG4gICAgICAgICAgICAgICdpbnZhbGlkIGJ1ZmZlciB0eXBlJylcbiAgICAgICAgICAgIGR0eXBlID0gZWxlbWVudFR5cGVzW29wdGlvbnMudHlwZV1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGRhdGEpIHtcbiAgICAgICAgICBpbml0RWxlbWVudHMoXG4gICAgICAgICAgICBlbGVtZW50cyxcbiAgICAgICAgICAgIGRhdGEsXG4gICAgICAgICAgICB1c2FnZSxcbiAgICAgICAgICAgIHByaW1UeXBlLFxuICAgICAgICAgICAgdmVydENvdW50LFxuICAgICAgICAgICAgYnl0ZUxlbmd0aCxcbiAgICAgICAgICAgIGR0eXBlKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHZhciBfYnVmZmVyID0gZWxlbWVudHMuYnVmZmVyXG4gICAgICAgICAgX2J1ZmZlci5iaW5kKClcbiAgICAgICAgICBnbC5idWZmZXJEYXRhKEdMX0VMRU1FTlRfQVJSQVlfQlVGRkVSLCBieXRlTGVuZ3RoLCB1c2FnZSlcbiAgICAgICAgICBfYnVmZmVyLmR0eXBlID0gZHR5cGUgfHwgR0xfVU5TSUdORURfQllURVxuICAgICAgICAgIF9idWZmZXIudXNhZ2UgPSB1c2FnZVxuICAgICAgICAgIF9idWZmZXIuZGltZW5zaW9uID0gM1xuICAgICAgICAgIF9idWZmZXIuYnl0ZUxlbmd0aCA9IGJ5dGVMZW5ndGhcbiAgICAgICAgICBlbGVtZW50cy5wcmltVHlwZSA9IHByaW1UeXBlIDwgMCA/IEdMX1RSSUFOR0xFUyA6IHByaW1UeXBlXG4gICAgICAgICAgZWxlbWVudHMudmVydENvdW50ID0gdmVydENvdW50IDwgMCA/IDAgOiB2ZXJ0Q291bnRcbiAgICAgICAgICBlbGVtZW50cy50eXBlID0gX2J1ZmZlci5kdHlwZVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHJldHVybiByZWdsRWxlbWVudHNcbiAgICB9XG5cbiAgICByZWdsRWxlbWVudHMob3B0aW9ucylcblxuICAgIHJlZ2xFbGVtZW50cy5fcmVnbFR5cGUgPSAnZWxlbWVudHMnXG4gICAgcmVnbEVsZW1lbnRzLl9lbGVtZW50cyA9IGVsZW1lbnRzXG4gICAgcmVnbEVsZW1lbnRzLnN1YmRhdGEgPSBmdW5jdGlvbiAoZGF0YSwgb2Zmc2V0KSB7XG4gICAgICBidWZmZXIuc3ViZGF0YShkYXRhLCBvZmZzZXQpXG4gICAgICByZXR1cm4gcmVnbEVsZW1lbnRzXG4gICAgfVxuICAgIHJlZ2xFbGVtZW50cy5kZXN0cm95ID0gZnVuY3Rpb24gKCkge1xuICAgICAgZGVzdHJveUVsZW1lbnRzKGVsZW1lbnRzKVxuICAgIH1cblxuICAgIHJldHVybiByZWdsRWxlbWVudHNcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgY3JlYXRlOiBjcmVhdGVFbGVtZW50cyxcbiAgICBjcmVhdGVTdHJlYW06IGNyZWF0ZUVsZW1lbnRTdHJlYW0sXG4gICAgZGVzdHJveVN0cmVhbTogZGVzdHJveUVsZW1lbnRTdHJlYW0sXG4gICAgZ2V0RWxlbWVudHM6IGZ1bmN0aW9uIChlbGVtZW50cykge1xuICAgICAgaWYgKHR5cGVvZiBlbGVtZW50cyA9PT0gJ2Z1bmN0aW9uJyAmJlxuICAgICAgICAgIGVsZW1lbnRzLl9lbGVtZW50cyBpbnN0YW5jZW9mIFJFR0xFbGVtZW50QnVmZmVyKSB7XG4gICAgICAgIHJldHVybiBlbGVtZW50cy5fZWxlbWVudHNcbiAgICAgIH1cbiAgICAgIHJldHVybiBudWxsXG4gICAgfSxcbiAgICBjbGVhcjogZnVuY3Rpb24gKCkge1xuICAgICAgdmFsdWVzKGVsZW1lbnRTZXQpLmZvckVhY2goZGVzdHJveUVsZW1lbnRzKVxuICAgIH1cbiAgfVxufVxuIiwidmFyIGNoZWNrID0gcmVxdWlyZSgnLi91dGlsL2NoZWNrJylcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBjcmVhdGVFeHRlbnNpb25DYWNoZSAoZ2wsIGNvbmZpZykge1xuICB2YXIgZXh0ZW5zaW9ucyA9IHt9XG5cbiAgZnVuY3Rpb24gdHJ5TG9hZEV4dGVuc2lvbiAobmFtZV8pIHtcbiAgICBjaGVjay50eXBlKG5hbWVfLCAnc3RyaW5nJywgJ2V4dGVuc2lvbiBuYW1lIG11c3QgYmUgc3RyaW5nJylcbiAgICB2YXIgbmFtZSA9IG5hbWVfLnRvTG93ZXJDYXNlKClcbiAgICBpZiAobmFtZSBpbiBleHRlbnNpb25zKSB7XG4gICAgICByZXR1cm4gdHJ1ZVxuICAgIH1cbiAgICB2YXIgZXh0XG4gICAgdHJ5IHtcbiAgICAgIGV4dCA9IGV4dGVuc2lvbnNbbmFtZV0gPSBnbC5nZXRFeHRlbnNpb24obmFtZSlcbiAgICB9IGNhdGNoIChlKSB7fVxuICAgIHJldHVybiAhIWV4dFxuICB9XG5cbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBjb25maWcuZXh0ZW5zaW9ucy5sZW5ndGg7ICsraSkge1xuICAgIHZhciBuYW1lID0gY29uZmlnLmV4dGVuc2lvbnNbaV1cbiAgICBpZiAoIXRyeUxvYWRFeHRlbnNpb24obmFtZSkpIHtcbiAgICAgIGNvbmZpZy5vbkRlc3Ryb3koKVxuICAgICAgY29uZmlnLm9uRG9uZSgnXCInICsgbmFtZSArICdcIiBleHRlbnNpb24gaXMgbm90IHN1cHBvcnRlZCBieSB0aGUgY3VycmVudCBXZWJHTCBjb250ZXh0LCB0cnkgdXBncmFkaW5nIHlvdXIgc3lzdGVtIG9yIGEgZGlmZmVyZW50IGJyb3dzZXInKVxuICAgICAgcmV0dXJuIG51bGxcbiAgICB9XG4gIH1cblxuICBjb25maWcub3B0aW9uYWxFeHRlbnNpb25zLmZvckVhY2godHJ5TG9hZEV4dGVuc2lvbilcblxuICByZXR1cm4ge1xuICAgIGV4dGVuc2lvbnM6IGV4dGVuc2lvbnMsXG4gICAgcmVmcmVzaDogZnVuY3Rpb24gKCkge1xuICAgICAgY29uZmlnLmV4dGVuc2lvbnMuZm9yRWFjaCh0cnlMb2FkRXh0ZW5zaW9uKVxuICAgICAgY29uZmlnLm9wdGlvbmFsRXh0ZW5zaW9ucy5mb3JFYWNoKHRyeUxvYWRFeHRlbnNpb24pXG4gICAgfVxuICB9XG59XG4iLCJ2YXIgY2hlY2sgPSByZXF1aXJlKCcuL3V0aWwvY2hlY2snKVxudmFyIHZhbHVlcyA9IHJlcXVpcmUoJy4vdXRpbC92YWx1ZXMnKVxudmFyIGV4dGVuZCA9IHJlcXVpcmUoJy4vdXRpbC9leHRlbmQnKVxuXG4vLyBXZSBzdG9yZSB0aGVzZSBjb25zdGFudHMgc28gdGhhdCB0aGUgbWluaWZpZXIgY2FuIGlubGluZSB0aGVtXG52YXIgR0xfRlJBTUVCVUZGRVIgPSAweDhENDBcbnZhciBHTF9SRU5ERVJCVUZGRVIgPSAweDhENDFcblxudmFyIEdMX1RFWFRVUkVfMkQgPSAweDBERTFcbnZhciBHTF9URVhUVVJFX0NVQkVfTUFQX1BPU0lUSVZFX1ggPSAweDg1MTVcblxudmFyIEdMX0NPTE9SX0FUVEFDSE1FTlQwID0gMHg4Q0UwXG52YXIgR0xfREVQVEhfQVRUQUNITUVOVCA9IDB4OEQwMFxudmFyIEdMX1NURU5DSUxfQVRUQUNITUVOVCA9IDB4OEQyMFxudmFyIEdMX0RFUFRIX1NURU5DSUxfQVRUQUNITUVOVCA9IDB4ODIxQVxuXG52YXIgR0xfRlJBTUVCVUZGRVJfQ09NUExFVEUgPSAweDhDRDVcbnZhciBHTF9GUkFNRUJVRkZFUl9JTkNPTVBMRVRFX0FUVEFDSE1FTlQgPSAweDhDRDZcbnZhciBHTF9GUkFNRUJVRkZFUl9JTkNPTVBMRVRFX01JU1NJTkdfQVRUQUNITUVOVCA9IDB4OENEN1xudmFyIEdMX0ZSQU1FQlVGRkVSX0lOQ09NUExFVEVfRElNRU5TSU9OUyA9IDB4OENEOVxudmFyIEdMX0ZSQU1FQlVGRkVSX1VOU1VQUE9SVEVEID0gMHg4Q0REXG5cbnZhciBHTF9IQUxGX0ZMT0FUX09FUyA9IDB4OEQ2MVxudmFyIEdMX1VOU0lHTkVEX0JZVEUgPSAweDE0MDFcbnZhciBHTF9GTE9BVCA9IDB4MTQwNlxuXG52YXIgR0xfUkdCQSA9IDB4MTkwOFxudmFyIEdMX0FMUEhBID0gMHgxOTA2XG52YXIgR0xfUkdCID0gMHgxOTA3XG52YXIgR0xfTFVNSU5BTkNFID0gMHgxOTA5XG52YXIgR0xfTFVNSU5BTkNFX0FMUEhBID0gMHgxOTBBXG5cbnZhciBHTF9ERVBUSF9DT01QT05FTlQgPSAweDE5MDJcblxudmFyIGNvbG9yVGV4dHVyZUZvcm1hdEVudW1zID0gW1xuICBHTF9BTFBIQSxcbiAgR0xfTFVNSU5BTkNFLFxuICBHTF9MVU1JTkFOQ0VfQUxQSEEsXG4gIEdMX1JHQixcbiAgR0xfUkdCQVxuXVxuXG4vLyBmb3IgZXZlcnkgdGV4dHVyZSBmb3JtYXQsIHN0b3JlXG4vLyB0aGUgbnVtYmVyIG9mIGNoYW5uZWxzXG52YXIgdGV4dHVyZUZvcm1hdENoYW5uZWxzID0gW11cbnRleHR1cmVGb3JtYXRDaGFubmVsc1tHTF9BTFBIQV0gPSAxXG50ZXh0dXJlRm9ybWF0Q2hhbm5lbHNbR0xfTFVNSU5BTkNFXSA9IDFcbnRleHR1cmVGb3JtYXRDaGFubmVsc1tHTF9MVU1JTkFOQ0VfQUxQSEFdID0gMlxudGV4dHVyZUZvcm1hdENoYW5uZWxzW0dMX1JHQl0gPSAzXG50ZXh0dXJlRm9ybWF0Q2hhbm5lbHNbR0xfUkdCQV0gPSA0XG5cbi8vIGZvciBldmVyeSB0ZXh0dXJlIHR5cGUsIHN0b3JlXG4vLyB0aGUgc2l6ZSBpbiBieXRlcy5cbnZhciB0ZXh0dXJlVHlwZVNpemVzID0gW11cbnRleHR1cmVUeXBlU2l6ZXNbR0xfVU5TSUdORURfQllURV0gPSAxXG50ZXh0dXJlVHlwZVNpemVzW0dMX0ZMT0FUXSA9IDRcbnRleHR1cmVUeXBlU2l6ZXNbR0xfSEFMRl9GTE9BVF9PRVNdID0gMlxuXG52YXIgR0xfUkdCQTQgPSAweDgwNTZcbnZhciBHTF9SR0I1X0ExID0gMHg4MDU3XG52YXIgR0xfUkdCNTY1ID0gMHg4RDYyXG52YXIgR0xfREVQVEhfQ09NUE9ORU5UMTYgPSAweDgxQTVcbnZhciBHTF9TVEVOQ0lMX0lOREVYOCA9IDB4OEQ0OFxudmFyIEdMX0RFUFRIX1NURU5DSUwgPSAweDg0RjlcblxudmFyIEdMX1NSR0I4X0FMUEhBOF9FWFQgPSAweDhDNDNcblxudmFyIEdMX1JHQkEzMkZfRVhUID0gMHg4ODE0XG5cbnZhciBHTF9SR0JBMTZGX0VYVCA9IDB4ODgxQVxudmFyIEdMX1JHQjE2Rl9FWFQgPSAweDg4MUJcblxudmFyIGNvbG9yUmVuZGVyYnVmZmVyRm9ybWF0RW51bXMgPSBbXG4gIEdMX1JHQkE0LFxuICBHTF9SR0I1X0ExLFxuICBHTF9SR0I1NjUsXG4gIEdMX1NSR0I4X0FMUEhBOF9FWFQsXG4gIEdMX1JHQkExNkZfRVhULFxuICBHTF9SR0IxNkZfRVhULFxuICBHTF9SR0JBMzJGX0VYVFxuXVxuXG52YXIgc3RhdHVzQ29kZSA9IHt9XG5zdGF0dXNDb2RlW0dMX0ZSQU1FQlVGRkVSX0NPTVBMRVRFXSA9ICdjb21wbGV0ZSdcbnN0YXR1c0NvZGVbR0xfRlJBTUVCVUZGRVJfSU5DT01QTEVURV9BVFRBQ0hNRU5UXSA9ICdpbmNvbXBsZXRlIGF0dGFjaG1lbnQnXG5zdGF0dXNDb2RlW0dMX0ZSQU1FQlVGRkVSX0lOQ09NUExFVEVfRElNRU5TSU9OU10gPSAnaW5jb21wbGV0ZSBkaW1lbnNpb25zJ1xuc3RhdHVzQ29kZVtHTF9GUkFNRUJVRkZFUl9JTkNPTVBMRVRFX01JU1NJTkdfQVRUQUNITUVOVF0gPSAnaW5jb21wbGV0ZSwgbWlzc2luZyBhdHRhY2htZW50J1xuc3RhdHVzQ29kZVtHTF9GUkFNRUJVRkZFUl9VTlNVUFBPUlRFRF0gPSAndW5zdXBwb3J0ZWQnXG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gd3JhcEZCT1N0YXRlIChcbiAgZ2wsXG4gIGV4dGVuc2lvbnMsXG4gIGxpbWl0cyxcbiAgdGV4dHVyZVN0YXRlLFxuICByZW5kZXJidWZmZXJTdGF0ZSxcbiAgc3RhdHMpIHtcbiAgdmFyIGZyYW1lYnVmZmVyU3RhdGUgPSB7XG4gICAgY3VycmVudDogbnVsbCxcbiAgICBuZXh0OiBudWxsLFxuICAgIGRpcnR5OiBmYWxzZVxuICB9XG5cbiAgdmFyIGNvbG9yVGV4dHVyZUZvcm1hdHMgPSBbJ3JnYmEnXVxuICB2YXIgY29sb3JSZW5kZXJidWZmZXJGb3JtYXRzID0gWydyZ2JhNCcsICdyZ2I1NjUnLCAncmdiNSBhMSddXG5cbiAgaWYgKGV4dGVuc2lvbnMuZXh0X3NyZ2IpIHtcbiAgICBjb2xvclJlbmRlcmJ1ZmZlckZvcm1hdHMucHVzaCgnc3JnYmEnKVxuICB9XG5cbiAgaWYgKGV4dGVuc2lvbnMuZXh0X2NvbG9yX2J1ZmZlcl9oYWxmX2Zsb2F0KSB7XG4gICAgY29sb3JSZW5kZXJidWZmZXJGb3JtYXRzLnB1c2goJ3JnYmExNmYnLCAncmdiMTZmJylcbiAgfVxuXG4gIGlmIChleHRlbnNpb25zLndlYmdsX2NvbG9yX2J1ZmZlcl9mbG9hdCkge1xuICAgIGNvbG9yUmVuZGVyYnVmZmVyRm9ybWF0cy5wdXNoKCdyZ2JhMzJmJylcbiAgfVxuXG4gIHZhciBjb2xvclR5cGVzID0gWyd1aW50OCddXG4gIGlmIChleHRlbnNpb25zLm9lc190ZXh0dXJlX2hhbGZfZmxvYXQpIHtcbiAgICBjb2xvclR5cGVzLnB1c2goJ2hhbGYgZmxvYXQnKVxuICB9XG4gIGlmIChleHRlbnNpb25zLm9lc190ZXh0dXJlX2Zsb2F0KSB7XG4gICAgY29sb3JUeXBlcy5wdXNoKCdmbG9hdCcpXG4gIH1cblxuICBmdW5jdGlvbiBGcmFtZWJ1ZmZlckF0dGFjaG1lbnQgKHRhcmdldCwgdGV4dHVyZSwgcmVuZGVyYnVmZmVyKSB7XG4gICAgdGhpcy50YXJnZXQgPSB0YXJnZXRcbiAgICB0aGlzLnRleHR1cmUgPSB0ZXh0dXJlXG4gICAgdGhpcy5yZW5kZXJidWZmZXIgPSByZW5kZXJidWZmZXJcblxuICAgIHZhciB3ID0gMFxuICAgIHZhciBoID0gMFxuICAgIGlmICh0ZXh0dXJlKSB7XG4gICAgICB3ID0gdGV4dHVyZS53aWR0aFxuICAgICAgaCA9IHRleHR1cmUuaGVpZ2h0XG4gICAgfSBlbHNlIGlmIChyZW5kZXJidWZmZXIpIHtcbiAgICAgIHcgPSByZW5kZXJidWZmZXIud2lkdGhcbiAgICAgIGggPSByZW5kZXJidWZmZXIuaGVpZ2h0XG4gICAgfVxuICAgIHRoaXMud2lkdGggPSB3XG4gICAgdGhpcy5oZWlnaHQgPSBoXG4gIH1cblxuICBmdW5jdGlvbiBkZWNSZWYgKGF0dGFjaG1lbnQpIHtcbiAgICBpZiAoYXR0YWNobWVudCkge1xuICAgICAgaWYgKGF0dGFjaG1lbnQudGV4dHVyZSkge1xuICAgICAgICBhdHRhY2htZW50LnRleHR1cmUuX3RleHR1cmUuZGVjUmVmKClcbiAgICAgIH1cbiAgICAgIGlmIChhdHRhY2htZW50LnJlbmRlcmJ1ZmZlcikge1xuICAgICAgICBhdHRhY2htZW50LnJlbmRlcmJ1ZmZlci5fcmVuZGVyYnVmZmVyLmRlY1JlZigpXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgZnVuY3Rpb24gaW5jUmVmQW5kQ2hlY2tTaGFwZSAoYXR0YWNobWVudCwgd2lkdGgsIGhlaWdodCkge1xuICAgIGlmICghYXR0YWNobWVudCkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGlmIChhdHRhY2htZW50LnRleHR1cmUpIHtcbiAgICAgIHZhciB0ZXh0dXJlID0gYXR0YWNobWVudC50ZXh0dXJlLl90ZXh0dXJlXG4gICAgICB2YXIgdHcgPSBNYXRoLm1heCgxLCB0ZXh0dXJlLndpZHRoKVxuICAgICAgdmFyIHRoID0gTWF0aC5tYXgoMSwgdGV4dHVyZS5oZWlnaHQpXG4gICAgICBjaGVjayh0dyA9PT0gd2lkdGggJiYgdGggPT09IGhlaWdodCxcbiAgICAgICAgJ2luY29uc2lzdGVudCB3aWR0aC9oZWlnaHQgZm9yIHN1cHBsaWVkIHRleHR1cmUnKVxuICAgICAgdGV4dHVyZS5yZWZDb3VudCArPSAxXG4gICAgfSBlbHNlIHtcbiAgICAgIHZhciByZW5kZXJidWZmZXIgPSBhdHRhY2htZW50LnJlbmRlcmJ1ZmZlci5fcmVuZGVyYnVmZmVyXG4gICAgICBjaGVjayhcbiAgICAgICAgcmVuZGVyYnVmZmVyLndpZHRoID09PSB3aWR0aCAmJiByZW5kZXJidWZmZXIuaGVpZ2h0ID09PSBoZWlnaHQsXG4gICAgICAgICdpbmNvbnNpc3RlbnQgd2lkdGgvaGVpZ2h0IGZvciByZW5kZXJidWZmZXInKVxuICAgICAgcmVuZGVyYnVmZmVyLnJlZkNvdW50ICs9IDFcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBhdHRhY2ggKGxvY2F0aW9uLCBhdHRhY2htZW50KSB7XG4gICAgaWYgKGF0dGFjaG1lbnQpIHtcbiAgICAgIGlmIChhdHRhY2htZW50LnRleHR1cmUpIHtcbiAgICAgICAgZ2wuZnJhbWVidWZmZXJUZXh0dXJlMkQoXG4gICAgICAgICAgR0xfRlJBTUVCVUZGRVIsXG4gICAgICAgICAgbG9jYXRpb24sXG4gICAgICAgICAgYXR0YWNobWVudC50YXJnZXQsXG4gICAgICAgICAgYXR0YWNobWVudC50ZXh0dXJlLl90ZXh0dXJlLnRleHR1cmUsXG4gICAgICAgICAgMClcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGdsLmZyYW1lYnVmZmVyUmVuZGVyYnVmZmVyKFxuICAgICAgICAgIEdMX0ZSQU1FQlVGRkVSLFxuICAgICAgICAgIGxvY2F0aW9uLFxuICAgICAgICAgIEdMX1JFTkRFUkJVRkZFUixcbiAgICAgICAgICBhdHRhY2htZW50LnJlbmRlcmJ1ZmZlci5fcmVuZGVyYnVmZmVyLnJlbmRlcmJ1ZmZlcilcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgZ2wuZnJhbWVidWZmZXJUZXh0dXJlMkQoXG4gICAgICAgIEdMX0ZSQU1FQlVGRkVSLFxuICAgICAgICBsb2NhdGlvbixcbiAgICAgICAgR0xfVEVYVFVSRV8yRCxcbiAgICAgICAgbnVsbCxcbiAgICAgICAgMClcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBwYXJzZUF0dGFjaG1lbnQgKGF0dGFjaG1lbnQpIHtcbiAgICB2YXIgdGFyZ2V0ID0gR0xfVEVYVFVSRV8yRFxuICAgIHZhciB0ZXh0dXJlID0gbnVsbFxuICAgIHZhciByZW5kZXJidWZmZXIgPSBudWxsXG5cbiAgICB2YXIgZGF0YSA9IGF0dGFjaG1lbnRcbiAgICBpZiAodHlwZW9mIGF0dGFjaG1lbnQgPT09ICdvYmplY3QnKSB7XG4gICAgICBkYXRhID0gYXR0YWNobWVudC5kYXRhXG4gICAgICBpZiAoJ3RhcmdldCcgaW4gYXR0YWNobWVudCkge1xuLy8gICAgICAgIHRhcmdldCA9IGF0dGFjaG1lbnQudGFyZ2V0IHwgMFxuICAgICAgfVxuICAgIH1cblxuICAgIGNoZWNrLnR5cGUoZGF0YSwgJ2Z1bmN0aW9uJywgJ2ludmFsaWQgYXR0YWNobWVudCBkYXRhJylcblxuICAgIHZhciB0eXBlID0gZGF0YS5fcmVnbFR5cGVcbiAgICBpZiAodHlwZSA9PT0gJ3RleHR1cmUyZCcpIHtcbiAgICAgIHRleHR1cmUgPSBkYXRhXG4gICAgICBjaGVjayh0YXJnZXQgPT09IEdMX1RFWFRVUkVfMkQpXG4gICAgfSBlbHNlIGlmICh0eXBlID09PSAndGV4dHVyZUN1YmUnKSB7XG4gICAgICB0ZXh0dXJlID0gZGF0YVxuICAgICAgY2hlY2soXG4gICAgICAgIHRhcmdldCA+PSBHTF9URVhUVVJFX0NVQkVfTUFQX1BPU0lUSVZFX1ggJiZcbiAgICAgICAgdGFyZ2V0IDwgR0xfVEVYVFVSRV9DVUJFX01BUF9QT1NJVElWRV9YICsgNixcbiAgICAgICAgJ2ludmFsaWQgY3ViZSBtYXAgdGFyZ2V0JylcbiAgICB9IGVsc2UgaWYgKHR5cGUgPT09ICdyZW5kZXJidWZmZXInKSB7XG4gICAgICByZW5kZXJidWZmZXIgPSBkYXRhXG4gICAgICB0YXJnZXQgPSBHTF9SRU5ERVJCVUZGRVJcbiAgICB9IGVsc2Uge1xuICAgICAgY2hlY2sucmFpc2UoJ2ludmFsaWQgcmVnbCBvYmplY3QgZm9yIGF0dGFjaG1lbnQnKVxuICAgIH1cblxuICAgIHJldHVybiBuZXcgRnJhbWVidWZmZXJBdHRhY2htZW50KHRhcmdldCwgdGV4dHVyZSwgcmVuZGVyYnVmZmVyKVxuICB9XG5cbiAgZnVuY3Rpb24gYWxsb2NBdHRhY2htZW50IChcbiAgICB3aWR0aCxcbiAgICBoZWlnaHQsXG4gICAgaXNUZXh0dXJlLFxuICAgIGZvcm1hdCxcbiAgICB0eXBlKSB7XG4gICAgaWYgKGlzVGV4dHVyZSkge1xuICAgICAgdmFyIHRleHR1cmUgPSB0ZXh0dXJlU3RhdGUuY3JlYXRlMkQoe1xuICAgICAgICB3aWR0aDogd2lkdGgsXG4gICAgICAgIGhlaWdodDogaGVpZ2h0LFxuICAgICAgICBmb3JtYXQ6IGZvcm1hdCxcbiAgICAgICAgdHlwZTogdHlwZVxuICAgICAgfSlcbiAgICAgIHRleHR1cmUuX3RleHR1cmUucmVmQ291bnQgPSAwXG4gICAgICByZXR1cm4gbmV3IEZyYW1lYnVmZmVyQXR0YWNobWVudChHTF9URVhUVVJFXzJELCB0ZXh0dXJlLCBudWxsKVxuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgcmIgPSByZW5kZXJidWZmZXJTdGF0ZS5jcmVhdGUoe1xuICAgICAgICB3aWR0aDogd2lkdGgsXG4gICAgICAgIGhlaWdodDogaGVpZ2h0LFxuICAgICAgICBmb3JtYXQ6IGZvcm1hdFxuICAgICAgfSlcbiAgICAgIHJiLl9yZW5kZXJidWZmZXIucmVmQ291bnQgPSAwXG4gICAgICByZXR1cm4gbmV3IEZyYW1lYnVmZmVyQXR0YWNobWVudChHTF9SRU5ERVJCVUZGRVIsIG51bGwsIHJiKVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHVud3JhcEF0dGFjaG1lbnQgKGF0dGFjaG1lbnQpIHtcbiAgICByZXR1cm4gYXR0YWNobWVudCAmJiAoYXR0YWNobWVudC50ZXh0dXJlIHx8IGF0dGFjaG1lbnQucmVuZGVyYnVmZmVyKVxuICB9XG5cbiAgZnVuY3Rpb24gcmVzaXplQXR0YWNobWVudCAoYXR0YWNobWVudCwgdywgaCkge1xuICAgIGlmIChhdHRhY2htZW50KSB7XG4gICAgICBpZiAoYXR0YWNobWVudC50ZXh0dXJlKSB7XG4gICAgICAgIGF0dGFjaG1lbnQudGV4dHVyZS5yZXNpemUodywgaClcbiAgICAgIH0gZWxzZSBpZiAoYXR0YWNobWVudC5yZW5kZXJidWZmZXIpIHtcbiAgICAgICAgYXR0YWNobWVudC5yZW5kZXJidWZmZXIucmVzaXplKHcsIGgpXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgdmFyIGZyYW1lYnVmZmVyQ291bnQgPSAwXG4gIHZhciBmcmFtZWJ1ZmZlclNldCA9IHt9XG5cbiAgZnVuY3Rpb24gUkVHTEZyYW1lYnVmZmVyICgpIHtcbiAgICB0aGlzLmlkID0gZnJhbWVidWZmZXJDb3VudCsrXG4gICAgZnJhbWVidWZmZXJTZXRbdGhpcy5pZF0gPSB0aGlzXG5cbiAgICB0aGlzLmZyYW1lYnVmZmVyID0gZ2wuY3JlYXRlRnJhbWVidWZmZXIoKVxuICAgIHRoaXMud2lkdGggPSAwXG4gICAgdGhpcy5oZWlnaHQgPSAwXG5cbiAgICB0aGlzLmNvbG9yQXR0YWNobWVudHMgPSBbXVxuICAgIHRoaXMuZGVwdGhBdHRhY2htZW50ID0gbnVsbFxuICAgIHRoaXMuc3RlbmNpbEF0dGFjaG1lbnQgPSBudWxsXG4gICAgdGhpcy5kZXB0aFN0ZW5jaWxBdHRhY2htZW50ID0gbnVsbFxuICB9XG5cbiAgZnVuY3Rpb24gZGVjRkJPUmVmcyAoZnJhbWVidWZmZXIpIHtcbiAgICBmcmFtZWJ1ZmZlci5jb2xvckF0dGFjaG1lbnRzLmZvckVhY2goZGVjUmVmKVxuICAgIGRlY1JlZihmcmFtZWJ1ZmZlci5kZXB0aEF0dGFjaG1lbnQpXG4gICAgZGVjUmVmKGZyYW1lYnVmZmVyLnN0ZW5jaWxBdHRhY2htZW50KVxuICAgIGRlY1JlZihmcmFtZWJ1ZmZlci5kZXB0aFN0ZW5jaWxBdHRhY2htZW50KVxuICB9XG5cbiAgZnVuY3Rpb24gZGVzdHJveSAoZnJhbWVidWZmZXIpIHtcbiAgICB2YXIgaGFuZGxlID0gZnJhbWVidWZmZXIuZnJhbWVidWZmZXJcbiAgICBjaGVjayhoYW5kbGUsICdtdXN0IG5vdCBkb3VibGUgZGVzdHJveSBmcmFtZWJ1ZmZlcicpXG4gICAgZ2wuZGVsZXRlRnJhbWVidWZmZXIoaGFuZGxlKVxuICAgIGZyYW1lYnVmZmVyLmZyYW1lYnVmZmVyID0gbnVsbFxuICAgIHN0YXRzLmZyYW1lYnVmZmVyQ291bnQtLVxuICAgIGRlbGV0ZSBmcmFtZWJ1ZmZlclNldFtmcmFtZWJ1ZmZlci5pZF1cbiAgfVxuXG4gIGZ1bmN0aW9uIHVwZGF0ZUZyYW1lYnVmZmVyIChmcmFtZWJ1ZmZlcikge1xuICAgIHZhciBpXG4gICAgZ2wuYmluZEZyYW1lYnVmZmVyKEdMX0ZSQU1FQlVGRkVSLCBmcmFtZWJ1ZmZlci5mcmFtZWJ1ZmZlcilcbiAgICB2YXIgY29sb3JBdHRhY2htZW50cyA9IGZyYW1lYnVmZmVyLmNvbG9yQXR0YWNobWVudHNcbiAgICBmb3IgKGkgPSAwOyBpIDwgY29sb3JBdHRhY2htZW50cy5sZW5ndGg7ICsraSkge1xuICAgICAgYXR0YWNoKEdMX0NPTE9SX0FUVEFDSE1FTlQwICsgaSwgY29sb3JBdHRhY2htZW50c1tpXSlcbiAgICB9XG4gICAgZm9yIChpID0gY29sb3JBdHRhY2htZW50cy5sZW5ndGg7IGkgPCBsaW1pdHMubWF4Q29sb3JBdHRhY2htZW50czsgKytpKSB7XG4gICAgICBhdHRhY2goR0xfQ09MT1JfQVRUQUNITUVOVDAgKyBpLCBudWxsKVxuICAgIH1cbiAgICBhdHRhY2goR0xfREVQVEhfQVRUQUNITUVOVCwgZnJhbWVidWZmZXIuZGVwdGhBdHRhY2htZW50KVxuICAgIGF0dGFjaChHTF9TVEVOQ0lMX0FUVEFDSE1FTlQsIGZyYW1lYnVmZmVyLnN0ZW5jaWxBdHRhY2htZW50KVxuICAgIGF0dGFjaChHTF9ERVBUSF9TVEVOQ0lMX0FUVEFDSE1FTlQsIGZyYW1lYnVmZmVyLmRlcHRoU3RlbmNpbEF0dGFjaG1lbnQpXG5cbiAgICAvLyBDaGVjayBzdGF0dXMgY29kZVxuICAgIHZhciBzdGF0dXMgPSBnbC5jaGVja0ZyYW1lYnVmZmVyU3RhdHVzKEdMX0ZSQU1FQlVGRkVSKVxuICAgIGlmIChzdGF0dXMgIT09IEdMX0ZSQU1FQlVGRkVSX0NPTVBMRVRFKSB7XG4gICAgICBjaGVjay5yYWlzZSgnZnJhbWVidWZmZXIgY29uZmlndXJhdGlvbiBub3Qgc3VwcG9ydGVkLCBzdGF0dXMgPSAnICtcbiAgICAgICAgc3RhdHVzQ29kZVtzdGF0dXNdKVxuICAgIH1cblxuICAgIGdsLmJpbmRGcmFtZWJ1ZmZlcihHTF9GUkFNRUJVRkZFUiwgZnJhbWVidWZmZXJTdGF0ZS5uZXh0KVxuICAgIGZyYW1lYnVmZmVyU3RhdGUuY3VycmVudCA9IGZyYW1lYnVmZmVyU3RhdGUubmV4dFxuICB9XG5cbiAgZnVuY3Rpb24gY3JlYXRlRkJPIChhMCwgYTEpIHtcbiAgICB2YXIgZnJhbWVidWZmZXIgPSBuZXcgUkVHTEZyYW1lYnVmZmVyKClcbiAgICBzdGF0cy5mcmFtZWJ1ZmZlckNvdW50KytcblxuICAgIGZ1bmN0aW9uIHJlZ2xGcmFtZWJ1ZmZlciAoYSwgYikge1xuICAgICAgdmFyIGlcblxuICAgICAgY2hlY2soZnJhbWVidWZmZXJTdGF0ZS5uZXh0ICE9PSBmcmFtZWJ1ZmZlcixcbiAgICAgICAgJ2NhbiBub3QgdXBkYXRlIGZyYW1lYnVmZmVyIHdoaWNoIGlzIGN1cnJlbnRseSBpbiB1c2UnKVxuXG4gICAgICB2YXIgZXh0RHJhd0J1ZmZlcnMgPSBleHRlbnNpb25zLndlYmdsX2RyYXdfYnVmZmVyc1xuXG4gICAgICB2YXIgd2lkdGggPSAwXG4gICAgICB2YXIgaGVpZ2h0ID0gMFxuXG4gICAgICB2YXIgbmVlZHNEZXB0aCA9IHRydWVcbiAgICAgIHZhciBuZWVkc1N0ZW5jaWwgPSB0cnVlXG5cbiAgICAgIHZhciBjb2xvckJ1ZmZlciA9IG51bGxcbiAgICAgIHZhciBjb2xvclRleHR1cmUgPSB0cnVlXG4gICAgICB2YXIgY29sb3JGb3JtYXQgPSAncmdiYSdcbiAgICAgIHZhciBjb2xvclR5cGUgPSAndWludDgnXG4gICAgICB2YXIgY29sb3JDb3VudCA9IDFcblxuICAgICAgdmFyIGRlcHRoQnVmZmVyID0gbnVsbFxuICAgICAgdmFyIHN0ZW5jaWxCdWZmZXIgPSBudWxsXG4gICAgICB2YXIgZGVwdGhTdGVuY2lsQnVmZmVyID0gbnVsbFxuICAgICAgdmFyIGRlcHRoU3RlbmNpbFRleHR1cmUgPSBmYWxzZVxuXG4gICAgICBpZiAodHlwZW9mIGEgPT09ICdudW1iZXInKSB7XG4gICAgICAgIHdpZHRoID0gYSB8IDBcbiAgICAgICAgaGVpZ2h0ID0gKGIgfCAwKSB8fCB3aWR0aFxuICAgICAgfSBlbHNlIGlmICghYSkge1xuICAgICAgICB3aWR0aCA9IGhlaWdodCA9IDFcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNoZWNrLnR5cGUoYSwgJ29iamVjdCcsICdpbnZhbGlkIGFyZ3VtZW50cyBmb3IgZnJhbWVidWZmZXInKVxuICAgICAgICB2YXIgb3B0aW9ucyA9IGFcblxuICAgICAgICBpZiAoJ3NoYXBlJyBpbiBvcHRpb25zKSB7XG4gICAgICAgICAgdmFyIHNoYXBlID0gb3B0aW9ucy5zaGFwZVxuICAgICAgICAgIGNoZWNrKEFycmF5LmlzQXJyYXkoc2hhcGUpICYmIHNoYXBlLmxlbmd0aCA+PSAyLFxuICAgICAgICAgICAgJ2ludmFsaWQgc2hhcGUgZm9yIGZyYW1lYnVmZmVyJylcbiAgICAgICAgICB3aWR0aCA9IHNoYXBlWzBdXG4gICAgICAgICAgaGVpZ2h0ID0gc2hhcGVbMV1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpZiAoJ3JhZGl1cycgaW4gb3B0aW9ucykge1xuICAgICAgICAgICAgd2lkdGggPSBoZWlnaHQgPSBvcHRpb25zLnJhZGl1c1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoJ3dpZHRoJyBpbiBvcHRpb25zKSB7XG4gICAgICAgICAgICB3aWR0aCA9IG9wdGlvbnMud2lkdGhcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKCdoZWlnaHQnIGluIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIGhlaWdodCA9IG9wdGlvbnMuaGVpZ2h0XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCdjb2xvcicgaW4gb3B0aW9ucyB8fFxuICAgICAgICAgICAgJ2NvbG9ycycgaW4gb3B0aW9ucykge1xuICAgICAgICAgIGNvbG9yQnVmZmVyID1cbiAgICAgICAgICAgIG9wdGlvbnMuY29sb3IgfHxcbiAgICAgICAgICAgIG9wdGlvbnMuY29sb3JzXG4gICAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkoY29sb3JCdWZmZXIpKSB7XG4gICAgICAgICAgICBjaGVjayhcbiAgICAgICAgICAgICAgY29sb3JCdWZmZXIubGVuZ3RoID09PSAxIHx8IGV4dERyYXdCdWZmZXJzLFxuICAgICAgICAgICAgICAnbXVsdGlwbGUgcmVuZGVyIHRhcmdldHMgbm90IHN1cHBvcnRlZCcpXG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFjb2xvckJ1ZmZlcikge1xuICAgICAgICAgIGlmICgnY29sb3JDb3VudCcgaW4gb3B0aW9ucykge1xuICAgICAgICAgICAgY29sb3JDb3VudCA9IG9wdGlvbnMuY29sb3JDb3VudCB8IDBcbiAgICAgICAgICAgIGNoZWNrKGNvbG9yQ291bnQgPiAwLCAnaW52YWxpZCBjb2xvciBidWZmZXIgY291bnQnKVxuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmICgnY29sb3JUZXh0dXJlJyBpbiBvcHRpb25zKSB7XG4gICAgICAgICAgICBjb2xvclRleHR1cmUgPSAhIW9wdGlvbnMuY29sb3JUZXh0dXJlXG4gICAgICAgICAgICBjb2xvckZvcm1hdCA9ICdyZ2JhNCdcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBpZiAoJ2NvbG9yVHlwZScgaW4gb3B0aW9ucykge1xuICAgICAgICAgICAgY2hlY2sub25lT2YoXG4gICAgICAgICAgICAgIG9wdGlvbnMuY29sb3JUeXBlLCBjb2xvclR5cGVzLFxuICAgICAgICAgICAgICAnaW52YWxpZCBjb2xvciB0eXBlJylcbiAgICAgICAgICAgIGNvbG9yVHlwZSA9IG9wdGlvbnMuY29sb3JUeXBlXG4gICAgICAgICAgICBpZiAoIWNvbG9yVGV4dHVyZSkge1xuICAgICAgICAgICAgICBpZiAoY29sb3JUeXBlID09PSAnaGFsZiBmbG9hdCcpIHtcbiAgICAgICAgICAgICAgICBpZiAoZXh0ZW5zaW9ucy5leHRfY29sb3JfYnVmZmVyX2hhbGZfZmxvYXQpIHtcbiAgICAgICAgICAgICAgICAgIGNvbG9yRm9ybWF0ID0gJ3JnYmExNmYnXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9IGVsc2UgaWYgKGNvbG9yVHlwZSA9PT0gJ2Zsb2F0Jykge1xuICAgICAgICAgICAgICAgIGlmIChleHRlbnNpb25zLndlYmdsX2NvbG9yX2J1ZmZlcl9mbG9hdCkge1xuICAgICAgICAgICAgICAgICAgY29sb3JGb3JtYXQgPSAncmdiYTMyZidcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG5cbiAgICAgICAgICBpZiAoJ2NvbG9yRm9ybWF0JyBpbiBvcHRpb25zKSB7XG4gICAgICAgICAgICBjb2xvckZvcm1hdCA9IG9wdGlvbnMuY29sb3JGb3JtYXRcbiAgICAgICAgICAgIGlmIChjb2xvclRleHR1cmVGb3JtYXRzLmluZGV4T2YoY29sb3JGb3JtYXQpID49IDApIHtcbiAgICAgICAgICAgICAgY29sb3JUZXh0dXJlID0gdHJ1ZVxuICAgICAgICAgICAgfSBlbHNlIGlmIChjb2xvclJlbmRlcmJ1ZmZlckZvcm1hdHMuaW5kZXhPZihjb2xvckZvcm1hdCkgPj0gMCkge1xuICAgICAgICAgICAgICBjb2xvclRleHR1cmUgPSBmYWxzZVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgaWYgKGNvbG9yVGV4dHVyZSkge1xuICAgICAgICAgICAgICAgIGNoZWNrLm9uZU9mKFxuICAgICAgICAgICAgICAgICAgb3B0aW9ucy5jb2xvckZvcm1hdCwgY29sb3JUZXh0dXJlRm9ybWF0cyxcbiAgICAgICAgICAgICAgICAgICdpbnZhbGlkIGNvbG9yIGZvcm1hdCBmb3IgdGV4dHVyZScpXG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgY2hlY2sub25lT2YoXG4gICAgICAgICAgICAgICAgICBvcHRpb25zLmNvbG9yRm9ybWF0LCBjb2xvclJlbmRlcmJ1ZmZlckZvcm1hdHMsXG4gICAgICAgICAgICAgICAgICAnaW52YWxpZCBjb2xvciBmb3JtYXQgZm9yIHJlbmRlcmJ1ZmZlcicpXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoJ2RlcHRoVGV4dHVyZScgaW4gb3B0aW9ucyB8fCAnZGVwdGhTdGVuY2lsVGV4dHVyZScgaW4gb3B0aW9ucykge1xuICAgICAgICAgIGRlcHRoU3RlbmNpbFRleHR1cmUgPSAhIShvcHRpb25zLmRlcHRoVGV4dHVyZSB8fFxuICAgICAgICAgICAgb3B0aW9ucy5kZXB0aFN0ZW5jaWxUZXh0dXJlKVxuICAgICAgICAgIGNoZWNrKCFkZXB0aFN0ZW5jaWxUZXh0dXJlIHx8IGV4dGVuc2lvbnMud2ViZ2xfZGVwdGhfdGV4dHVyZSxcbiAgICAgICAgICAgICd3ZWJnbF9kZXB0aF90ZXh0dXJlIGV4dGVuc2lvbiBub3Qgc3VwcG9ydGVkJylcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICgnZGVwdGgnIGluIG9wdGlvbnMpIHtcbiAgICAgICAgICBpZiAodHlwZW9mIG9wdGlvbnMuZGVwdGggPT09ICdib29sZWFuJykge1xuICAgICAgICAgICAgbmVlZHNEZXB0aCA9IG9wdGlvbnMuZGVwdGhcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZGVwdGhCdWZmZXIgPSBvcHRpb25zLmRlcHRoXG4gICAgICAgICAgICBuZWVkc1N0ZW5jaWwgPSBmYWxzZVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmICgnc3RlbmNpbCcgaW4gb3B0aW9ucykge1xuICAgICAgICAgIGlmICh0eXBlb2Ygb3B0aW9ucy5zdGVuY2lsID09PSAnYm9vbGVhbicpIHtcbiAgICAgICAgICAgIG5lZWRzU3RlbmNpbCA9IG9wdGlvbnMuc3RlbmNpbFxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBzdGVuY2lsQnVmZmVyID0gb3B0aW9ucy5zdGVuY2lsXG4gICAgICAgICAgICBuZWVkc0RlcHRoID0gZmFsc2VcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoJ2RlcHRoU3RlbmNpbCcgaW4gb3B0aW9ucykge1xuICAgICAgICAgIGlmICh0eXBlb2Ygb3B0aW9ucy5kZXB0aFN0ZW5jaWwgPT09ICdib29sZWFuJykge1xuICAgICAgICAgICAgbmVlZHNEZXB0aCA9IG5lZWRzU3RlbmNpbCA9IG9wdGlvbnMuZGVwdGhTdGVuY2lsXG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGRlcHRoU3RlbmNpbEJ1ZmZlciA9IG9wdGlvbnMuZGVwdGhTdGVuY2lsXG4gICAgICAgICAgICBuZWVkc0RlcHRoID0gZmFsc2VcbiAgICAgICAgICAgIG5lZWRzU3RlbmNpbCA9IGZhbHNlXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIHBhcnNlIGF0dGFjaG1lbnRzXG4gICAgICB2YXIgY29sb3JBdHRhY2htZW50cyA9IG51bGxcbiAgICAgIHZhciBkZXB0aEF0dGFjaG1lbnQgPSBudWxsXG4gICAgICB2YXIgc3RlbmNpbEF0dGFjaG1lbnQgPSBudWxsXG4gICAgICB2YXIgZGVwdGhTdGVuY2lsQXR0YWNobWVudCA9IG51bGxcblxuICAgICAgLy8gU2V0IHVwIGNvbG9yIGF0dGFjaG1lbnRzXG4gICAgICBpZiAoQXJyYXkuaXNBcnJheShjb2xvckJ1ZmZlcikpIHtcbiAgICAgICAgY29sb3JBdHRhY2htZW50cyA9IGNvbG9yQnVmZmVyLm1hcChwYXJzZUF0dGFjaG1lbnQpXG4gICAgICB9IGVsc2UgaWYgKGNvbG9yQnVmZmVyKSB7XG4gICAgICAgIGNvbG9yQXR0YWNobWVudHMgPSBbcGFyc2VBdHRhY2htZW50KGNvbG9yQnVmZmVyKV1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbG9yQXR0YWNobWVudHMgPSBuZXcgQXJyYXkoY29sb3JDb3VudClcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IGNvbG9yQ291bnQ7ICsraSkge1xuICAgICAgICAgIGNvbG9yQXR0YWNobWVudHNbaV0gPSBhbGxvY0F0dGFjaG1lbnQoXG4gICAgICAgICAgICB3aWR0aCxcbiAgICAgICAgICAgIGhlaWdodCxcbiAgICAgICAgICAgIGNvbG9yVGV4dHVyZSxcbiAgICAgICAgICAgIGNvbG9yRm9ybWF0LFxuICAgICAgICAgICAgY29sb3JUeXBlKVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGNoZWNrKGNvbG9yQXR0YWNobWVudHMubGVuZ3RoIDw9IGxpbWl0cy5tYXhDb2xvckF0dGFjaG1lbnRzLFxuICAgICAgICAndG9vIG1hbnkgY29sb3IgYXR0YWNobWVudHMsIG5vdCBzdXBwb3J0ZWQnKVxuXG4gICAgICB3aWR0aCA9IHdpZHRoIHx8IGNvbG9yQXR0YWNobWVudHNbMF0ud2lkdGhcbiAgICAgIGhlaWdodCA9IGhlaWdodCB8fCBjb2xvckF0dGFjaG1lbnRzWzBdLmhlaWdodFxuXG4gICAgICBpZiAoZGVwdGhCdWZmZXIpIHtcbiAgICAgICAgZGVwdGhBdHRhY2htZW50ID0gcGFyc2VBdHRhY2htZW50KGRlcHRoQnVmZmVyKVxuICAgICAgfSBlbHNlIGlmIChuZWVkc0RlcHRoICYmICFuZWVkc1N0ZW5jaWwpIHtcbiAgICAgICAgZGVwdGhBdHRhY2htZW50ID0gYWxsb2NBdHRhY2htZW50KFxuICAgICAgICAgIHdpZHRoLFxuICAgICAgICAgIGhlaWdodCxcbiAgICAgICAgICBkZXB0aFN0ZW5jaWxUZXh0dXJlLFxuICAgICAgICAgICdkZXB0aCcsXG4gICAgICAgICAgJ3VpbnQzMicpXG4gICAgICB9XG5cbiAgICAgIGlmIChzdGVuY2lsQnVmZmVyKSB7XG4gICAgICAgIHN0ZW5jaWxBdHRhY2htZW50ID0gcGFyc2VBdHRhY2htZW50KHN0ZW5jaWxCdWZmZXIpXG4gICAgICB9IGVsc2UgaWYgKG5lZWRzU3RlbmNpbCAmJiAhbmVlZHNEZXB0aCkge1xuICAgICAgICBzdGVuY2lsQXR0YWNobWVudCA9IGFsbG9jQXR0YWNobWVudChcbiAgICAgICAgICB3aWR0aCxcbiAgICAgICAgICBoZWlnaHQsXG4gICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgJ3N0ZW5jaWwnLFxuICAgICAgICAgICd1aW50OCcpXG4gICAgICB9XG5cbiAgICAgIGlmIChkZXB0aFN0ZW5jaWxCdWZmZXIpIHtcbiAgICAgICAgZGVwdGhTdGVuY2lsQXR0YWNobWVudCA9IHBhcnNlQXR0YWNobWVudChkZXB0aFN0ZW5jaWxCdWZmZXIpXG4gICAgICB9IGVsc2UgaWYgKCFkZXB0aEJ1ZmZlciAmJiAhc3RlbmNpbEJ1ZmZlciAmJiBuZWVkc1N0ZW5jaWwgJiYgbmVlZHNEZXB0aCkge1xuICAgICAgICBkZXB0aFN0ZW5jaWxBdHRhY2htZW50ID0gYWxsb2NBdHRhY2htZW50KFxuICAgICAgICAgIHdpZHRoLFxuICAgICAgICAgIGhlaWdodCxcbiAgICAgICAgICBkZXB0aFN0ZW5jaWxUZXh0dXJlLFxuICAgICAgICAgICdkZXB0aCBzdGVuY2lsJyxcbiAgICAgICAgICAnZGVwdGggc3RlbmNpbCcpXG4gICAgICB9XG5cbiAgICAgIGNoZWNrKFxuICAgICAgICAoISFkZXB0aEJ1ZmZlcikgKyAoISFzdGVuY2lsQnVmZmVyKSArICghIWRlcHRoU3RlbmNpbEJ1ZmZlcikgPD0gMSxcbiAgICAgICAgJ2ludmFsaWQgZnJhbWVidWZmZXIgY29uZmlndXJhdGlvbiwgY2FuIHNwZWNpZnkgZXhhY3RseSBvbmUgZGVwdGgvc3RlbmNpbCBhdHRhY2htZW50JylcblxuICAgICAgdmFyIGNvbW1vbkNvbG9yQXR0YWNobWVudFNpemUgPSBudWxsXG5cbiAgICAgIGZvciAoaSA9IDA7IGkgPCBjb2xvckF0dGFjaG1lbnRzLmxlbmd0aDsgKytpKSB7XG4gICAgICAgIGluY1JlZkFuZENoZWNrU2hhcGUoY29sb3JBdHRhY2htZW50c1tpXSwgd2lkdGgsIGhlaWdodClcbiAgICAgICAgY2hlY2soIWNvbG9yQXR0YWNobWVudHNbaV0gfHxcbiAgICAgICAgICAoY29sb3JBdHRhY2htZW50c1tpXS50ZXh0dXJlICYmXG4gICAgICAgICAgICBjb2xvclRleHR1cmVGb3JtYXRFbnVtcy5pbmRleE9mKGNvbG9yQXR0YWNobWVudHNbaV0udGV4dHVyZS5fdGV4dHVyZS5mb3JtYXQpID49IDApIHx8XG4gICAgICAgICAgKGNvbG9yQXR0YWNobWVudHNbaV0ucmVuZGVyYnVmZmVyICYmXG4gICAgICAgICAgICBjb2xvclJlbmRlcmJ1ZmZlckZvcm1hdEVudW1zLmluZGV4T2YoY29sb3JBdHRhY2htZW50c1tpXS5yZW5kZXJidWZmZXIuX3JlbmRlcmJ1ZmZlci5mb3JtYXQpID49IDApLFxuICAgICAgICAgICdmcmFtZWJ1ZmZlciBjb2xvciBhdHRhY2htZW50ICcgKyBpICsgJyBpcyBpbnZhbGlkJylcblxuICAgICAgICBpZiAoY29sb3JBdHRhY2htZW50c1tpXSAmJiBjb2xvckF0dGFjaG1lbnRzW2ldLnRleHR1cmUpIHtcbiAgICAgICAgICB2YXIgY29sb3JBdHRhY2htZW50U2l6ZSA9XG4gICAgICAgICAgICAgIHRleHR1cmVGb3JtYXRDaGFubmVsc1tjb2xvckF0dGFjaG1lbnRzW2ldLnRleHR1cmUuX3RleHR1cmUuZm9ybWF0XSAqXG4gICAgICAgICAgICAgIHRleHR1cmVUeXBlU2l6ZXNbY29sb3JBdHRhY2htZW50c1tpXS50ZXh0dXJlLl90ZXh0dXJlLnR5cGVdXG5cbiAgICAgICAgICBpZiAoY29tbW9uQ29sb3JBdHRhY2htZW50U2l6ZSA9PT0gbnVsbCkge1xuICAgICAgICAgICAgY29tbW9uQ29sb3JBdHRhY2htZW50U2l6ZSA9IGNvbG9yQXR0YWNobWVudFNpemVcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gV2UgbmVlZCB0byBtYWtlIHN1cmUgdGhhdCBhbGwgY29sb3IgYXR0YWNobWVudHMgaGF2ZSB0aGUgc2FtZSBudW1iZXIgb2YgYml0cGxhbmVzXG4gICAgICAgICAgICAvLyAodGhhdCBpcywgdGhlIHNhbWUgbnVtZXIgb2YgYml0cyBwZXIgcGl4ZWwpXG4gICAgICAgICAgICAvLyBUaGlzIGlzIHJlcXVpcmVkIGJ5IHRoZSBHTEVTMi4wIHN0YW5kYXJkLiBTZWUgdGhlIGJlZ2lubmluZyBvZiBDaGFwdGVyIDQgaW4gdGhhdCBkb2N1bWVudC5cbiAgICAgICAgICAgIGNoZWNrKGNvbW1vbkNvbG9yQXR0YWNobWVudFNpemUgPT09IGNvbG9yQXR0YWNobWVudFNpemUsXG4gICAgICAgICAgICAgICAgICAnYWxsIGNvbG9yIGF0dGFjaG1lbnRzIG11Y2ggaGF2ZSB0aGUgc2FtZSBudW1iZXIgb2YgYml0cyBwZXIgcGl4ZWwuJylcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGluY1JlZkFuZENoZWNrU2hhcGUoZGVwdGhBdHRhY2htZW50LCB3aWR0aCwgaGVpZ2h0KVxuICAgICAgY2hlY2soIWRlcHRoQXR0YWNobWVudCB8fFxuICAgICAgICAoZGVwdGhBdHRhY2htZW50LnRleHR1cmUgJiZcbiAgICAgICAgICBkZXB0aEF0dGFjaG1lbnQudGV4dHVyZS5fdGV4dHVyZS5mb3JtYXQgPT09IEdMX0RFUFRIX0NPTVBPTkVOVCkgfHxcbiAgICAgICAgKGRlcHRoQXR0YWNobWVudC5yZW5kZXJidWZmZXIgJiZcbiAgICAgICAgICBkZXB0aEF0dGFjaG1lbnQucmVuZGVyYnVmZmVyLl9yZW5kZXJidWZmZXIuZm9ybWF0ID09PSBHTF9ERVBUSF9DT01QT05FTlQxNiksXG4gICAgICAgICdpbnZhbGlkIGRlcHRoIGF0dGFjaG1lbnQgZm9yIGZyYW1lYnVmZmVyIG9iamVjdCcpXG4gICAgICBpbmNSZWZBbmRDaGVja1NoYXBlKHN0ZW5jaWxBdHRhY2htZW50LCB3aWR0aCwgaGVpZ2h0KVxuICAgICAgY2hlY2soIXN0ZW5jaWxBdHRhY2htZW50IHx8XG4gICAgICAgIChzdGVuY2lsQXR0YWNobWVudC5yZW5kZXJidWZmZXIgJiZcbiAgICAgICAgICBzdGVuY2lsQXR0YWNobWVudC5yZW5kZXJidWZmZXIuX3JlbmRlcmJ1ZmZlci5mb3JtYXQgPT09IEdMX1NURU5DSUxfSU5ERVg4KSxcbiAgICAgICAgJ2ludmFsaWQgc3RlbmNpbCBhdHRhY2htZW50IGZvciBmcmFtZWJ1ZmZlciBvYmplY3QnKVxuICAgICAgaW5jUmVmQW5kQ2hlY2tTaGFwZShkZXB0aFN0ZW5jaWxBdHRhY2htZW50LCB3aWR0aCwgaGVpZ2h0KVxuICAgICAgY2hlY2soIWRlcHRoU3RlbmNpbEF0dGFjaG1lbnQgfHxcbiAgICAgICAgKGRlcHRoU3RlbmNpbEF0dGFjaG1lbnQudGV4dHVyZSAmJlxuICAgICAgICAgIGRlcHRoU3RlbmNpbEF0dGFjaG1lbnQudGV4dHVyZS5fdGV4dHVyZS5mb3JtYXQgPT09IEdMX0RFUFRIX1NURU5DSUwpIHx8XG4gICAgICAgIChkZXB0aFN0ZW5jaWxBdHRhY2htZW50LnJlbmRlcmJ1ZmZlciAmJlxuICAgICAgICAgIGRlcHRoU3RlbmNpbEF0dGFjaG1lbnQucmVuZGVyYnVmZmVyLl9yZW5kZXJidWZmZXIuZm9ybWF0ID09PSBHTF9ERVBUSF9TVEVOQ0lMKSxcbiAgICAgICAgJ2ludmFsaWQgZGVwdGgtc3RlbmNpbCBhdHRhY2htZW50IGZvciBmcmFtZWJ1ZmZlciBvYmplY3QnKVxuXG4gICAgICAvLyBkZWNyZW1lbnQgcmVmZXJlbmNlc1xuICAgICAgZGVjRkJPUmVmcyhmcmFtZWJ1ZmZlcilcblxuICAgICAgZnJhbWVidWZmZXIud2lkdGggPSB3aWR0aFxuICAgICAgZnJhbWVidWZmZXIuaGVpZ2h0ID0gaGVpZ2h0XG5cbiAgICAgIGZyYW1lYnVmZmVyLmNvbG9yQXR0YWNobWVudHMgPSBjb2xvckF0dGFjaG1lbnRzXG4gICAgICBmcmFtZWJ1ZmZlci5kZXB0aEF0dGFjaG1lbnQgPSBkZXB0aEF0dGFjaG1lbnRcbiAgICAgIGZyYW1lYnVmZmVyLnN0ZW5jaWxBdHRhY2htZW50ID0gc3RlbmNpbEF0dGFjaG1lbnRcbiAgICAgIGZyYW1lYnVmZmVyLmRlcHRoU3RlbmNpbEF0dGFjaG1lbnQgPSBkZXB0aFN0ZW5jaWxBdHRhY2htZW50XG5cbiAgICAgIHJlZ2xGcmFtZWJ1ZmZlci5jb2xvciA9IGNvbG9yQXR0YWNobWVudHMubWFwKHVud3JhcEF0dGFjaG1lbnQpXG4gICAgICByZWdsRnJhbWVidWZmZXIuZGVwdGggPSB1bndyYXBBdHRhY2htZW50KGRlcHRoQXR0YWNobWVudClcbiAgICAgIHJlZ2xGcmFtZWJ1ZmZlci5zdGVuY2lsID0gdW53cmFwQXR0YWNobWVudChzdGVuY2lsQXR0YWNobWVudClcbiAgICAgIHJlZ2xGcmFtZWJ1ZmZlci5kZXB0aFN0ZW5jaWwgPSB1bndyYXBBdHRhY2htZW50KGRlcHRoU3RlbmNpbEF0dGFjaG1lbnQpXG5cbiAgICAgIHJlZ2xGcmFtZWJ1ZmZlci53aWR0aCA9IGZyYW1lYnVmZmVyLndpZHRoXG4gICAgICByZWdsRnJhbWVidWZmZXIuaGVpZ2h0ID0gZnJhbWVidWZmZXIuaGVpZ2h0XG5cbiAgICAgIHVwZGF0ZUZyYW1lYnVmZmVyKGZyYW1lYnVmZmVyKVxuXG4gICAgICByZXR1cm4gcmVnbEZyYW1lYnVmZmVyXG4gICAgfVxuXG4gICAgZnVuY3Rpb24gcmVzaXplICh3XywgaF8pIHtcbiAgICAgIGNoZWNrKGZyYW1lYnVmZmVyU3RhdGUubmV4dCAhPT0gZnJhbWVidWZmZXIsXG4gICAgICAgICdjYW4gbm90IHJlc2l6ZSBhIGZyYW1lYnVmZmVyIHdoaWNoIGlzIGN1cnJlbnRseSBpbiB1c2UnKVxuXG4gICAgICB2YXIgdyA9IHdfIHwgMFxuICAgICAgdmFyIGggPSAoaF8gfCAwKSB8fCB3XG4gICAgICBpZiAodyA9PT0gZnJhbWVidWZmZXIud2lkdGggJiYgaCA9PT0gZnJhbWVidWZmZXIuaGVpZ2h0KSB7XG4gICAgICAgIHJldHVybiByZWdsRnJhbWVidWZmZXJcbiAgICAgIH1cblxuICAgICAgLy8gcmVzaXplIGFsbCBidWZmZXJzXG4gICAgICB2YXIgY29sb3JBdHRhY2htZW50cyA9IGZyYW1lYnVmZmVyLmNvbG9yQXR0YWNobWVudHNcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY29sb3JBdHRhY2htZW50cy5sZW5ndGg7ICsraSkge1xuICAgICAgICByZXNpemVBdHRhY2htZW50KGNvbG9yQXR0YWNobWVudHNbaV0sIHcsIGgpXG4gICAgICB9XG4gICAgICByZXNpemVBdHRhY2htZW50KGZyYW1lYnVmZmVyLmRlcHRoQXR0YWNobWVudCwgdywgaClcbiAgICAgIHJlc2l6ZUF0dGFjaG1lbnQoZnJhbWVidWZmZXIuc3RlbmNpbEF0dGFjaG1lbnQsIHcsIGgpXG4gICAgICByZXNpemVBdHRhY2htZW50KGZyYW1lYnVmZmVyLmRlcHRoU3RlbmNpbEF0dGFjaG1lbnQsIHcsIGgpXG5cbiAgICAgIGZyYW1lYnVmZmVyLndpZHRoID0gcmVnbEZyYW1lYnVmZmVyLndpZHRoID0gd1xuICAgICAgZnJhbWVidWZmZXIuaGVpZ2h0ID0gcmVnbEZyYW1lYnVmZmVyLmhlaWdodCA9IGhcblxuICAgICAgdXBkYXRlRnJhbWVidWZmZXIoZnJhbWVidWZmZXIpXG5cbiAgICAgIHJldHVybiByZWdsRnJhbWVidWZmZXJcbiAgICB9XG5cbiAgICByZWdsRnJhbWVidWZmZXIoYTAsIGExKVxuXG4gICAgcmVnbEZyYW1lYnVmZmVyLnJlc2l6ZSA9IHJlc2l6ZVxuICAgIHJlZ2xGcmFtZWJ1ZmZlci5fcmVnbFR5cGUgPSAnZnJhbWVidWZmZXInXG4gICAgcmVnbEZyYW1lYnVmZmVyLl9mcmFtZWJ1ZmZlciA9IGZyYW1lYnVmZmVyXG4gICAgcmVnbEZyYW1lYnVmZmVyLmRlc3Ryb3kgPSBmdW5jdGlvbiAoKSB7XG4gICAgICBkZXN0cm95KGZyYW1lYnVmZmVyKVxuICAgICAgZGVjRkJPUmVmcyhmcmFtZWJ1ZmZlcilcbiAgICB9XG5cbiAgICByZXR1cm4gcmVnbEZyYW1lYnVmZmVyXG4gIH1cblxuICByZXR1cm4gZXh0ZW5kKGZyYW1lYnVmZmVyU3RhdGUsIHtcbiAgICBnZXRGcmFtZWJ1ZmZlcjogZnVuY3Rpb24gKG9iamVjdCkge1xuICAgICAgaWYgKHR5cGVvZiBvYmplY3QgPT09ICdmdW5jdGlvbicgJiYgb2JqZWN0Ll9yZWdsVHlwZSA9PT0gJ2ZyYW1lYnVmZmVyJykge1xuICAgICAgICB2YXIgZmJvID0gb2JqZWN0Ll9mcmFtZWJ1ZmZlclxuICAgICAgICBpZiAoZmJvIGluc3RhbmNlb2YgUkVHTEZyYW1lYnVmZmVyKSB7XG4gICAgICAgICAgcmV0dXJuIGZib1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gbnVsbFxuICAgIH0sXG4gICAgY3JlYXRlOiBjcmVhdGVGQk8sXG4gICAgY2xlYXI6IGZ1bmN0aW9uICgpIHtcbiAgICAgIHZhbHVlcyhmcmFtZWJ1ZmZlclNldCkuZm9yRWFjaChkZXN0cm95KVxuICAgIH1cbiAgfSlcbn1cbiIsInZhciBHTF9TVUJQSVhFTF9CSVRTID0gMHgwRDUwXG52YXIgR0xfUkVEX0JJVFMgPSAweDBENTJcbnZhciBHTF9HUkVFTl9CSVRTID0gMHgwRDUzXG52YXIgR0xfQkxVRV9CSVRTID0gMHgwRDU0XG52YXIgR0xfQUxQSEFfQklUUyA9IDB4MEQ1NVxudmFyIEdMX0RFUFRIX0JJVFMgPSAweDBENTZcbnZhciBHTF9TVEVOQ0lMX0JJVFMgPSAweDBENTdcblxudmFyIEdMX0FMSUFTRURfUE9JTlRfU0laRV9SQU5HRSA9IDB4ODQ2RFxudmFyIEdMX0FMSUFTRURfTElORV9XSURUSF9SQU5HRSA9IDB4ODQ2RVxuXG52YXIgR0xfTUFYX1RFWFRVUkVfU0laRSA9IDB4MEQzM1xudmFyIEdMX01BWF9WSUVXUE9SVF9ESU1TID0gMHgwRDNBXG52YXIgR0xfTUFYX1ZFUlRFWF9BVFRSSUJTID0gMHg4ODY5XG52YXIgR0xfTUFYX1ZFUlRFWF9VTklGT1JNX1ZFQ1RPUlMgPSAweDhERkJcbnZhciBHTF9NQVhfVkFSWUlOR19WRUNUT1JTID0gMHg4REZDXG52YXIgR0xfTUFYX0NPTUJJTkVEX1RFWFRVUkVfSU1BR0VfVU5JVFMgPSAweDhCNERcbnZhciBHTF9NQVhfVkVSVEVYX1RFWFRVUkVfSU1BR0VfVU5JVFMgPSAweDhCNENcbnZhciBHTF9NQVhfVEVYVFVSRV9JTUFHRV9VTklUUyA9IDB4ODg3MlxudmFyIEdMX01BWF9GUkFHTUVOVF9VTklGT1JNX1ZFQ1RPUlMgPSAweDhERkRcbnZhciBHTF9NQVhfQ1VCRV9NQVBfVEVYVFVSRV9TSVpFID0gMHg4NTFDXG52YXIgR0xfTUFYX1JFTkRFUkJVRkZFUl9TSVpFID0gMHg4NEU4XG5cbnZhciBHTF9WRU5ET1IgPSAweDFGMDBcbnZhciBHTF9SRU5ERVJFUiA9IDB4MUYwMVxudmFyIEdMX1ZFUlNJT04gPSAweDFGMDJcbnZhciBHTF9TSEFESU5HX0xBTkdVQUdFX1ZFUlNJT04gPSAweDhCOENcblxudmFyIEdMX01BWF9URVhUVVJFX01BWF9BTklTT1RST1BZX0VYVCA9IDB4ODRGRlxuXG52YXIgR0xfTUFYX0NPTE9SX0FUVEFDSE1FTlRTX1dFQkdMID0gMHg4Q0RGXG52YXIgR0xfTUFYX0RSQVdfQlVGRkVSU19XRUJHTCA9IDB4ODgyNFxuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChnbCwgZXh0ZW5zaW9ucykge1xuICB2YXIgbWF4QW5pc290cm9waWMgPSAxXG4gIGlmIChleHRlbnNpb25zLmV4dF90ZXh0dXJlX2ZpbHRlcl9hbmlzb3Ryb3BpYykge1xuICAgIG1heEFuaXNvdHJvcGljID0gZ2wuZ2V0UGFyYW1ldGVyKEdMX01BWF9URVhUVVJFX01BWF9BTklTT1RST1BZX0VYVClcbiAgfVxuXG4gIHZhciBtYXhEcmF3YnVmZmVycyA9IDFcbiAgdmFyIG1heENvbG9yQXR0YWNobWVudHMgPSAxXG4gIGlmIChleHRlbnNpb25zLndlYmdsX2RyYXdfYnVmZmVycykge1xuICAgIG1heERyYXdidWZmZXJzID0gZ2wuZ2V0UGFyYW1ldGVyKEdMX01BWF9EUkFXX0JVRkZFUlNfV0VCR0wpXG4gICAgbWF4Q29sb3JBdHRhY2htZW50cyA9IGdsLmdldFBhcmFtZXRlcihHTF9NQVhfQ09MT1JfQVRUQUNITUVOVFNfV0VCR0wpXG4gIH1cblxuICByZXR1cm4ge1xuICAgIC8vIGRyYXdpbmcgYnVmZmVyIGJpdCBkZXB0aFxuICAgIGNvbG9yQml0czogW1xuICAgICAgZ2wuZ2V0UGFyYW1ldGVyKEdMX1JFRF9CSVRTKSxcbiAgICAgIGdsLmdldFBhcmFtZXRlcihHTF9HUkVFTl9CSVRTKSxcbiAgICAgIGdsLmdldFBhcmFtZXRlcihHTF9CTFVFX0JJVFMpLFxuICAgICAgZ2wuZ2V0UGFyYW1ldGVyKEdMX0FMUEhBX0JJVFMpXG4gICAgXSxcbiAgICBkZXB0aEJpdHM6IGdsLmdldFBhcmFtZXRlcihHTF9ERVBUSF9CSVRTKSxcbiAgICBzdGVuY2lsQml0czogZ2wuZ2V0UGFyYW1ldGVyKEdMX1NURU5DSUxfQklUUyksXG4gICAgc3VicGl4ZWxCaXRzOiBnbC5nZXRQYXJhbWV0ZXIoR0xfU1VCUElYRUxfQklUUyksXG5cbiAgICAvLyBzdXBwb3J0ZWQgZXh0ZW5zaW9uc1xuICAgIGV4dGVuc2lvbnM6IE9iamVjdC5rZXlzKGV4dGVuc2lvbnMpLmZpbHRlcihmdW5jdGlvbiAoZXh0KSB7XG4gICAgICByZXR1cm4gISFleHRlbnNpb25zW2V4dF1cbiAgICB9KSxcblxuICAgIC8vIG1heCBhbmlzbyBzYW1wbGVzXG4gICAgbWF4QW5pc290cm9waWM6IG1heEFuaXNvdHJvcGljLFxuXG4gICAgLy8gbWF4IGRyYXcgYnVmZmVyc1xuICAgIG1heERyYXdidWZmZXJzOiBtYXhEcmF3YnVmZmVycyxcbiAgICBtYXhDb2xvckF0dGFjaG1lbnRzOiBtYXhDb2xvckF0dGFjaG1lbnRzLFxuXG4gICAgLy8gcG9pbnQgYW5kIGxpbmUgc2l6ZSByYW5nZXNcbiAgICBwb2ludFNpemVEaW1zOiBnbC5nZXRQYXJhbWV0ZXIoR0xfQUxJQVNFRF9QT0lOVF9TSVpFX1JBTkdFKSxcbiAgICBsaW5lV2lkdGhEaW1zOiBnbC5nZXRQYXJhbWV0ZXIoR0xfQUxJQVNFRF9MSU5FX1dJRFRIX1JBTkdFKSxcbiAgICBtYXhWaWV3cG9ydERpbXM6IGdsLmdldFBhcmFtZXRlcihHTF9NQVhfVklFV1BPUlRfRElNUyksXG4gICAgbWF4Q29tYmluZWRUZXh0dXJlVW5pdHM6IGdsLmdldFBhcmFtZXRlcihHTF9NQVhfQ09NQklORURfVEVYVFVSRV9JTUFHRV9VTklUUyksXG4gICAgbWF4Q3ViZU1hcFNpemU6IGdsLmdldFBhcmFtZXRlcihHTF9NQVhfQ1VCRV9NQVBfVEVYVFVSRV9TSVpFKSxcbiAgICBtYXhSZW5kZXJidWZmZXJTaXplOiBnbC5nZXRQYXJhbWV0ZXIoR0xfTUFYX1JFTkRFUkJVRkZFUl9TSVpFKSxcbiAgICBtYXhUZXh0dXJlVW5pdHM6IGdsLmdldFBhcmFtZXRlcihHTF9NQVhfVEVYVFVSRV9JTUFHRV9VTklUUyksXG4gICAgbWF4VGV4dHVyZVNpemU6IGdsLmdldFBhcmFtZXRlcihHTF9NQVhfVEVYVFVSRV9TSVpFKSxcbiAgICBtYXhBdHRyaWJ1dGVzOiBnbC5nZXRQYXJhbWV0ZXIoR0xfTUFYX1ZFUlRFWF9BVFRSSUJTKSxcbiAgICBtYXhWZXJ0ZXhVbmlmb3JtczogZ2wuZ2V0UGFyYW1ldGVyKEdMX01BWF9WRVJURVhfVU5JRk9STV9WRUNUT1JTKSxcbiAgICBtYXhWZXJ0ZXhUZXh0dXJlVW5pdHM6IGdsLmdldFBhcmFtZXRlcihHTF9NQVhfVkVSVEVYX1RFWFRVUkVfSU1BR0VfVU5JVFMpLFxuICAgIG1heFZhcnlpbmdWZWN0b3JzOiBnbC5nZXRQYXJhbWV0ZXIoR0xfTUFYX1ZBUllJTkdfVkVDVE9SUyksXG4gICAgbWF4RnJhZ21lbnRVbmlmb3JtczogZ2wuZ2V0UGFyYW1ldGVyKEdMX01BWF9GUkFHTUVOVF9VTklGT1JNX1ZFQ1RPUlMpLFxuXG4gICAgLy8gdmVuZG9yIGluZm9cbiAgICBnbHNsOiBnbC5nZXRQYXJhbWV0ZXIoR0xfU0hBRElOR19MQU5HVUFHRV9WRVJTSU9OKSxcbiAgICByZW5kZXJlcjogZ2wuZ2V0UGFyYW1ldGVyKEdMX1JFTkRFUkVSKSxcbiAgICB2ZW5kb3I6IGdsLmdldFBhcmFtZXRlcihHTF9WRU5ET1IpLFxuICAgIHZlcnNpb246IGdsLmdldFBhcmFtZXRlcihHTF9WRVJTSU9OKVxuICB9XG59XG4iLCJ2YXIgY2hlY2sgPSByZXF1aXJlKCcuL3V0aWwvY2hlY2snKVxudmFyIGlzVHlwZWRBcnJheSA9IHJlcXVpcmUoJy4vdXRpbC9pcy10eXBlZC1hcnJheScpXG5cbnZhciBHTF9SR0JBID0gNjQwOFxudmFyIEdMX1VOU0lHTkVEX0JZVEUgPSA1MTIxXG52YXIgR0xfUEFDS19BTElHTk1FTlQgPSAweDBEMDVcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiB3cmFwUmVhZFBpeGVscyAoXG4gIGdsLFxuICBmcmFtZWJ1ZmZlclN0YXRlLFxuICByZWdsUG9sbCxcbiAgY29udGV4dCxcbiAgZ2xBdHRyaWJ1dGVzKSB7XG4gIGZ1bmN0aW9uIHJlYWRQaXhlbHMgKGlucHV0KSB7XG4gICAgaWYgKGZyYW1lYnVmZmVyU3RhdGUuY3VycmVudCA9PT0gbnVsbCkge1xuICAgICAgY2hlY2soXG4gICAgICAgIGdsQXR0cmlidXRlcy5wcmVzZXJ2ZURyYXdpbmdCdWZmZXIsXG4gICAgICAgICd5b3UgbXVzdCBjcmVhdGUgYSB3ZWJnbCBjb250ZXh0IHdpdGggXCJwcmVzZXJ2ZURyYXdpbmdCdWZmZXJcIjp0cnVlIGluIG9yZGVyIHRvIHJlYWQgcGl4ZWxzIGZyb20gdGhlIGRyYXdpbmcgYnVmZmVyJylcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gVE9ETyBjaGVjayBmcmFtZWJ1ZmZlciBzdXBwb3J0cyByZWFkIHBpeGVsc1xuICAgIH1cblxuICAgIHZhciB4ID0gMFxuICAgIHZhciB5ID0gMFxuICAgIHZhciB3aWR0aCA9IGNvbnRleHQuZnJhbWVidWZmZXJXaWR0aFxuICAgIHZhciBoZWlnaHQgPSBjb250ZXh0LmZyYW1lYnVmZmVySGVpZ2h0XG4gICAgdmFyIGRhdGEgPSBudWxsXG5cbiAgICBpZiAoaXNUeXBlZEFycmF5KGlucHV0KSkge1xuICAgICAgZGF0YSA9IGlucHV0XG4gICAgfSBlbHNlIGlmIChpbnB1dCkge1xuICAgICAgY2hlY2sudHlwZShpbnB1dCwgJ29iamVjdCcsICdpbnZhbGlkIGFyZ3VtZW50cyB0byByZWdsLnJlYWQoKScpXG4gICAgICB4ID0gaW5wdXQueCB8IDBcbiAgICAgIHkgPSBpbnB1dC55IHwgMFxuICAgICAgY2hlY2soXG4gICAgICAgIHggPj0gMCAmJiB4IDwgY29udGV4dC5mcmFtZWJ1ZmZlcldpZHRoLFxuICAgICAgICAnaW52YWxpZCB4IG9mZnNldCBmb3IgcmVnbC5yZWFkJylcbiAgICAgIGNoZWNrKFxuICAgICAgICB5ID49IDAgJiYgeSA8IGNvbnRleHQuZnJhbWVidWZmZXJIZWlnaHQsXG4gICAgICAgICdpbnZhbGlkIHkgb2Zmc2V0IGZvciByZWdsLnJlYWQnKVxuICAgICAgd2lkdGggPSAoaW5wdXQud2lkdGggfHwgKGNvbnRleHQuZnJhbWVidWZmZXJXaWR0aCAtIHgpKSB8IDBcbiAgICAgIGhlaWdodCA9IChpbnB1dC5oZWlnaHQgfHwgKGNvbnRleHQuZnJhbWVidWZmZXJIZWlnaHQgLSB5KSkgfCAwXG4gICAgICBkYXRhID0gaW5wdXQuZGF0YSB8fCBudWxsXG4gICAgfVxuXG4gICAgY2hlY2soXG4gICAgICB3aWR0aCA+IDAgJiYgd2lkdGggKyB4IDw9IGNvbnRleHQuZnJhbWVidWZmZXJXaWR0aCxcbiAgICAgICdpbnZhbGlkIHdpZHRoIGZvciByZWFkIHBpeGVscycpXG4gICAgY2hlY2soXG4gICAgICBoZWlnaHQgPiAwICYmIGhlaWdodCArIHkgPD0gY29udGV4dC5mcmFtZWJ1ZmZlckhlaWdodCxcbiAgICAgICdpbnZhbGlkIGhlaWdodCBmb3IgcmVhZCBwaXhlbHMnKVxuXG4gICAgLy8gVXBkYXRlIFdlYkdMIHN0YXRlXG4gICAgcmVnbFBvbGwoKVxuXG4gICAgLy8gVE9ETzpcbiAgICAvLyAgZmxvYXQgY29sb3IgYnVmZmVyc1xuICAgIC8vICBpbXBsZW1lbnRhdGlvbiBzcGVjaWZpYyBmb3JtYXRzXG5cbiAgICAvLyBDb21wdXRlIHNpemVcbiAgICB2YXIgc2l6ZSA9IHdpZHRoICogaGVpZ2h0ICogNFxuXG4gICAgLy8gQWxsb2NhdGUgZGF0YVxuICAgIGRhdGEgPSBkYXRhIHx8IG5ldyBVaW50OEFycmF5KHNpemUpXG5cbiAgICAvLyBUeXBlIGNoZWNrXG4gICAgY2hlY2suaXNUeXBlZEFycmF5KGRhdGEsICdkYXRhIGJ1ZmZlciBmb3IgcmVnbC5yZWFkKCkgbXVzdCBiZSBhIHR5cGVkYXJyYXknKVxuICAgIGNoZWNrKGRhdGEuYnl0ZUxlbmd0aCA+PSBzaXplLCAnZGF0YSBidWZmZXIgZm9yIHJlZ2wucmVhZCgpIHRvbyBzbWFsbCcpXG5cbiAgICAvLyBSdW4gcmVhZCBwaXhlbHNcbiAgICBnbC5waXhlbFN0b3JlaShHTF9QQUNLX0FMSUdOTUVOVCwgNClcbiAgICBnbC5yZWFkUGl4ZWxzKHgsIHksIHdpZHRoLCBoZWlnaHQsIEdMX1JHQkEsIEdMX1VOU0lHTkVEX0JZVEUsIGRhdGEpXG5cbiAgICByZXR1cm4gZGF0YVxuICB9XG5cbiAgcmV0dXJuIHJlYWRQaXhlbHNcbn1cbiIsInZhciBjaGVjayA9IHJlcXVpcmUoJy4vdXRpbC9jaGVjaycpXG52YXIgdmFsdWVzID0gcmVxdWlyZSgnLi91dGlsL3ZhbHVlcycpXG5cbnZhciBHTF9SRU5ERVJCVUZGRVIgPSAweDhENDFcblxudmFyIEdMX1JHQkE0ID0gMHg4MDU2XG52YXIgR0xfUkdCNV9BMSA9IDB4ODA1N1xudmFyIEdMX1JHQjU2NSA9IDB4OEQ2MlxudmFyIEdMX0RFUFRIX0NPTVBPTkVOVDE2ID0gMHg4MUE1XG52YXIgR0xfU1RFTkNJTF9JTkRFWDggPSAweDhENDhcbnZhciBHTF9ERVBUSF9TVEVOQ0lMID0gMHg4NEY5XG5cbnZhciBHTF9TUkdCOF9BTFBIQThfRVhUID0gMHg4QzQzXG5cbnZhciBHTF9SR0JBMzJGX0VYVCA9IDB4ODgxNFxuXG52YXIgR0xfUkdCQTE2Rl9FWFQgPSAweDg4MUFcbnZhciBHTF9SR0IxNkZfRVhUID0gMHg4ODFCXG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGdsLCBleHRlbnNpb25zLCBsaW1pdHMsIHN0YXRzKSB7XG4gIHZhciBmb3JtYXRUeXBlcyA9IHtcbiAgICAncmdiYTQnOiBHTF9SR0JBNCxcbiAgICAncmdiNTY1JzogR0xfUkdCNTY1LFxuICAgICdyZ2I1IGExJzogR0xfUkdCNV9BMSxcbiAgICAnZGVwdGgnOiBHTF9ERVBUSF9DT01QT05FTlQxNixcbiAgICAnc3RlbmNpbCc6IEdMX1NURU5DSUxfSU5ERVg4LFxuICAgICdkZXB0aCBzdGVuY2lsJzogR0xfREVQVEhfU1RFTkNJTFxuICB9XG5cbiAgaWYgKGV4dGVuc2lvbnMuZXh0X3NyZ2IpIHtcbiAgICBmb3JtYXRUeXBlc1snc3JnYmEnXSA9IEdMX1NSR0I4X0FMUEhBOF9FWFRcbiAgfVxuXG4gIGlmIChleHRlbnNpb25zLmV4dF9jb2xvcl9idWZmZXJfaGFsZl9mbG9hdCkge1xuICAgIGZvcm1hdFR5cGVzWydyZ2JhMTZmJ10gPSBHTF9SR0JBMTZGX0VYVFxuICAgIGZvcm1hdFR5cGVzWydyZ2IxNmYnXSA9IEdMX1JHQjE2Rl9FWFRcbiAgfVxuXG4gIGlmIChleHRlbnNpb25zLndlYmdsX2NvbG9yX2J1ZmZlcl9mbG9hdCkge1xuICAgIGZvcm1hdFR5cGVzWydyZ2JhMzJmJ10gPSBHTF9SR0JBMzJGX0VYVFxuICB9XG5cbiAgdmFyIHJlbmRlcmJ1ZmZlckNvdW50ID0gMFxuICB2YXIgcmVuZGVyYnVmZmVyU2V0ID0ge31cblxuICBmdW5jdGlvbiBSRUdMUmVuZGVyYnVmZmVyIChyZW5kZXJidWZmZXIpIHtcbiAgICB0aGlzLmlkID0gcmVuZGVyYnVmZmVyQ291bnQrK1xuICAgIHRoaXMucmVmQ291bnQgPSAxXG5cbiAgICB0aGlzLnJlbmRlcmJ1ZmZlciA9IHJlbmRlcmJ1ZmZlclxuXG4gICAgdGhpcy5mb3JtYXQgPSBHTF9SR0JBNFxuICAgIHRoaXMud2lkdGggPSAwXG4gICAgdGhpcy5oZWlnaHQgPSAwXG4gIH1cblxuICBSRUdMUmVuZGVyYnVmZmVyLnByb3RvdHlwZS5kZWNSZWYgPSBmdW5jdGlvbiAoKSB7XG4gICAgaWYgKC0tdGhpcy5yZWZDb3VudCA8PSAwKSB7XG4gICAgICBkZXN0cm95KHRoaXMpXG4gICAgfVxuICB9XG5cbiAgZnVuY3Rpb24gZGVzdHJveSAocmIpIHtcbiAgICB2YXIgaGFuZGxlID0gcmIucmVuZGVyYnVmZmVyXG4gICAgY2hlY2soaGFuZGxlLCAnbXVzdCBub3QgZG91YmxlIGRlc3Ryb3kgcmVuZGVyYnVmZmVyJylcbiAgICBnbC5iaW5kUmVuZGVyYnVmZmVyKEdMX1JFTkRFUkJVRkZFUiwgbnVsbClcbiAgICBnbC5kZWxldGVSZW5kZXJidWZmZXIoaGFuZGxlKVxuICAgIHJiLnJlbmRlcmJ1ZmZlciA9IG51bGxcbiAgICByYi5yZWZDb3VudCA9IDBcbiAgICBkZWxldGUgcmVuZGVyYnVmZmVyU2V0W3JiLmlkXVxuICAgIHN0YXRzLnJlbmRlcmJ1ZmZlckNvdW50LS1cbiAgfVxuXG4gIGZ1bmN0aW9uIGNyZWF0ZVJlbmRlcmJ1ZmZlciAoYSwgYikge1xuICAgIHZhciByZW5kZXJidWZmZXIgPSBuZXcgUkVHTFJlbmRlcmJ1ZmZlcihnbC5jcmVhdGVSZW5kZXJidWZmZXIoKSlcbiAgICByZW5kZXJidWZmZXJTZXRbcmVuZGVyYnVmZmVyLmlkXSA9IHJlbmRlcmJ1ZmZlclxuICAgIHN0YXRzLnJlbmRlcmJ1ZmZlckNvdW50KytcblxuICAgIGZ1bmN0aW9uIHJlZ2xSZW5kZXJidWZmZXIgKGEsIGIpIHtcbiAgICAgIHZhciB3ID0gMFxuICAgICAgdmFyIGggPSAwXG4gICAgICB2YXIgZm9ybWF0ID0gR0xfUkdCQTRcblxuICAgICAgaWYgKHR5cGVvZiBhID09PSAnb2JqZWN0JyAmJiBhKSB7XG4gICAgICAgIHZhciBvcHRpb25zID0gYVxuICAgICAgICBpZiAoJ3NoYXBlJyBpbiBvcHRpb25zKSB7XG4gICAgICAgICAgdmFyIHNoYXBlID0gb3B0aW9ucy5zaGFwZVxuICAgICAgICAgIGNoZWNrKEFycmF5LmlzQXJyYXkoc2hhcGUpICYmIHNoYXBlLmxlbmd0aCA+PSAyLFxuICAgICAgICAgICAgJ2ludmFsaWQgcmVuZGVyYnVmZmVyIHNoYXBlJylcbiAgICAgICAgICB3ID0gc2hhcGVbMF0gfCAwXG4gICAgICAgICAgaCA9IHNoYXBlWzFdIHwgMFxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGlmICgncmFkaXVzJyBpbiBvcHRpb25zKSB7XG4gICAgICAgICAgICB3ID0gaCA9IG9wdGlvbnMucmFkaXVzIHwgMFxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoJ3dpZHRoJyBpbiBvcHRpb25zKSB7XG4gICAgICAgICAgICB3ID0gb3B0aW9ucy53aWR0aCB8IDBcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKCdoZWlnaHQnIGluIG9wdGlvbnMpIHtcbiAgICAgICAgICAgIGggPSBvcHRpb25zLmhlaWdodCB8IDBcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCdmb3JtYXQnIGluIG9wdGlvbnMpIHtcbiAgICAgICAgICBjaGVjay5wYXJhbWV0ZXIob3B0aW9ucy5mb3JtYXQsIGZvcm1hdFR5cGVzLFxuICAgICAgICAgICAgJ2ludmFsaWQgcmVuZGVyYnVmZmVyIGZvcm1hdCcpXG4gICAgICAgICAgZm9ybWF0ID0gZm9ybWF0VHlwZXNbb3B0aW9ucy5mb3JtYXRdXG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAodHlwZW9mIGEgPT09ICdudW1iZXInKSB7XG4gICAgICAgIHcgPSBhIHwgMFxuICAgICAgICBpZiAodHlwZW9mIGIgPT09ICdudW1iZXInKSB7XG4gICAgICAgICAgaCA9IGIgfCAwXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaCA9IHdcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmICghYSkge1xuICAgICAgICB3ID0gaCA9IDFcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNoZWNrLnJhaXNlKCdpbnZhbGlkIGFyZ3VtZW50cyB0byByZW5kZXJidWZmZXIgY29uc3RydWN0b3InKVxuICAgICAgfVxuXG4gICAgICAvLyBjaGVjayBzaGFwZVxuICAgICAgY2hlY2soXG4gICAgICAgIHcgPiAwICYmIGggPiAwICYmXG4gICAgICAgIHcgPD0gbGltaXRzLm1heFJlbmRlcmJ1ZmZlclNpemUgJiYgaCA8PSBsaW1pdHMubWF4UmVuZGVyYnVmZmVyU2l6ZSxcbiAgICAgICAgJ2ludmFsaWQgcmVuZGVyYnVmZmVyIHNpemUnKVxuXG4gICAgICBpZiAodyA9PT0gcmVuZGVyYnVmZmVyLndpZHRoICYmXG4gICAgICAgICAgaCA9PT0gcmVuZGVyYnVmZmVyLmhlaWdodCAmJlxuICAgICAgICAgIGZvcm1hdCA9PT0gcmVuZGVyYnVmZmVyLmZvcm1hdCkge1xuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgcmVnbFJlbmRlcmJ1ZmZlci53aWR0aCA9IHJlbmRlcmJ1ZmZlci53aWR0aCA9IHdcbiAgICAgIHJlZ2xSZW5kZXJidWZmZXIuaGVpZ2h0ID0gcmVuZGVyYnVmZmVyLmhlaWdodCA9IGhcbiAgICAgIHJlbmRlcmJ1ZmZlci5mb3JtYXQgPSBmb3JtYXRcblxuICAgICAgZ2wuYmluZFJlbmRlcmJ1ZmZlcihHTF9SRU5ERVJCVUZGRVIsIHJlbmRlcmJ1ZmZlci5yZW5kZXJidWZmZXIpXG4gICAgICBnbC5yZW5kZXJidWZmZXJTdG9yYWdlKEdMX1JFTkRFUkJVRkZFUiwgZm9ybWF0LCB3LCBoKVxuXG4gICAgICByZXR1cm4gcmVnbFJlbmRlcmJ1ZmZlclxuICAgIH1cblxuICAgIGZ1bmN0aW9uIHJlc2l6ZSAod18sIGhfKSB7XG4gICAgICB2YXIgdyA9IHdfIHwgMFxuICAgICAgdmFyIGggPSAoaF8gfCAwKSB8fCB3XG5cbiAgICAgIGlmICh3ID09PSByZW5kZXJidWZmZXIud2lkdGggJiYgaCA9PT0gcmVuZGVyYnVmZmVyLmhlaWdodCkge1xuICAgICAgICByZXR1cm4gcmVnbFJlbmRlcmJ1ZmZlclxuICAgICAgfVxuXG4gICAgICAvLyBjaGVjayBzaGFwZVxuICAgICAgY2hlY2soXG4gICAgICAgIHcgPiAwICYmIGggPiAwICYmXG4gICAgICAgIHcgPD0gbGltaXRzLm1heFJlbmRlcmJ1ZmZlclNpemUgJiYgaCA8PSBsaW1pdHMubWF4UmVuZGVyYnVmZmVyU2l6ZSxcbiAgICAgICAgJ2ludmFsaWQgcmVuZGVyYnVmZmVyIHNpemUnKVxuXG4gICAgICByZWdsUmVuZGVyYnVmZmVyLndpZHRoID0gcmVuZGVyYnVmZmVyLndpZHRoID0gd1xuICAgICAgcmVnbFJlbmRlcmJ1ZmZlci5oZWlnaHQgPSByZW5kZXJidWZmZXIuaGVpZ2h0ID0gaFxuXG4gICAgICBnbC5iaW5kUmVuZGVyYnVmZmVyKEdMX1JFTkRFUkJVRkZFUiwgcmVuZGVyYnVmZmVyLnJlbmRlcmJ1ZmZlcilcbiAgICAgIGdsLnJlbmRlcmJ1ZmZlclN0b3JhZ2UoR0xfUkVOREVSQlVGRkVSLCByZW5kZXJidWZmZXIuZm9ybWF0LCB3LCBoKVxuXG4gICAgICByZXR1cm4gcmVnbFJlbmRlcmJ1ZmZlclxuICAgIH1cblxuICAgIHJlZ2xSZW5kZXJidWZmZXIoYSwgYilcblxuICAgIHJlZ2xSZW5kZXJidWZmZXIucmVzaXplID0gcmVzaXplXG4gICAgcmVnbFJlbmRlcmJ1ZmZlci5fcmVnbFR5cGUgPSAncmVuZGVyYnVmZmVyJ1xuICAgIHJlZ2xSZW5kZXJidWZmZXIuX3JlbmRlcmJ1ZmZlciA9IHJlbmRlcmJ1ZmZlclxuICAgIHJlZ2xSZW5kZXJidWZmZXIuZGVzdHJveSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgIHJlbmRlcmJ1ZmZlci5kZWNSZWYoKVxuICAgIH1cblxuICAgIHJldHVybiByZWdsUmVuZGVyYnVmZmVyXG4gIH1cblxuICByZXR1cm4ge1xuICAgIGNyZWF0ZTogY3JlYXRlUmVuZGVyYnVmZmVyLFxuICAgIGNsZWFyOiBmdW5jdGlvbiAoKSB7XG4gICAgICB2YWx1ZXMocmVuZGVyYnVmZmVyU2V0KS5mb3JFYWNoKGRlc3Ryb3kpXG4gICAgfVxuICB9XG59XG4iLCJ2YXIgY2hlY2sgPSByZXF1aXJlKCcuL3V0aWwvY2hlY2snKVxudmFyIHZhbHVlcyA9IHJlcXVpcmUoJy4vdXRpbC92YWx1ZXMnKVxuXG52YXIgR0xfRlJBR01FTlRfU0hBREVSID0gMzU2MzJcbnZhciBHTF9WRVJURVhfU0hBREVSID0gMzU2MzNcblxudmFyIEdMX0FDVElWRV9VTklGT1JNUyA9IDB4OEI4NlxudmFyIEdMX0FDVElWRV9BVFRSSUJVVEVTID0gMHg4Qjg5XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gd3JhcFNoYWRlclN0YXRlIChnbCwgc3RyaW5nU3RvcmUsIHN0YXRzKSB7XG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBnbHNsIGNvbXBpbGF0aW9uIGFuZCBsaW5raW5nXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICB2YXIgZnJhZ1NoYWRlcnMgPSB7fVxuICB2YXIgdmVydFNoYWRlcnMgPSB7fVxuXG4gIGZ1bmN0aW9uIEFjdGl2ZUluZm8gKG5hbWUsIGlkLCBsb2NhdGlvbiwgaW5mbykge1xuICAgIHRoaXMubmFtZSA9IG5hbWVcbiAgICB0aGlzLmlkID0gaWRcbiAgICB0aGlzLmxvY2F0aW9uID0gbG9jYXRpb25cbiAgICB0aGlzLmluZm8gPSBpbmZvXG4gIH1cblxuICBmdW5jdGlvbiBpbnNlcnRBY3RpdmVJbmZvIChsaXN0LCBpbmZvKSB7XG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsaXN0Lmxlbmd0aDsgKytpKSB7XG4gICAgICBpZiAobGlzdFtpXS5pZCA9PT0gaW5mby5pZCkge1xuICAgICAgICBsaXN0W2ldLmxvY2F0aW9uID0gaW5mby5sb2NhdGlvblxuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICB9XG4gICAgbGlzdC5wdXNoKGluZm8pXG4gIH1cblxuICBmdW5jdGlvbiBnZXRTaGFkZXIgKHR5cGUsIGlkLCBjb21tYW5kKSB7XG4gICAgdmFyIGNhY2hlID0gdHlwZSA9PT0gR0xfRlJBR01FTlRfU0hBREVSID8gZnJhZ1NoYWRlcnMgOiB2ZXJ0U2hhZGVyc1xuICAgIHZhciBzaGFkZXIgPSBjYWNoZVtpZF1cblxuICAgIGlmICghc2hhZGVyKSB7XG4gICAgICB2YXIgc291cmNlID0gc3RyaW5nU3RvcmUuc3RyKGlkKVxuICAgICAgc2hhZGVyID0gZ2wuY3JlYXRlU2hhZGVyKHR5cGUpXG4gICAgICBnbC5zaGFkZXJTb3VyY2Uoc2hhZGVyLCBzb3VyY2UpXG4gICAgICBnbC5jb21waWxlU2hhZGVyKHNoYWRlcilcbiAgICAgIGNoZWNrLnNoYWRlckVycm9yKGdsLCBzaGFkZXIsIHNvdXJjZSwgdHlwZSwgY29tbWFuZClcbiAgICAgIGNhY2hlW2lkXSA9IHNoYWRlclxuICAgIH1cblxuICAgIHJldHVybiBzaGFkZXJcbiAgfVxuXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBwcm9ncmFtIGxpbmtpbmdcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIHZhciBwcm9ncmFtQ2FjaGUgPSB7fVxuICB2YXIgcHJvZ3JhbUxpc3QgPSBbXVxuXG4gIHZhciBQUk9HUkFNX0NPVU5URVIgPSAwXG5cbiAgZnVuY3Rpb24gUkVHTFByb2dyYW0gKGZyYWdJZCwgdmVydElkKSB7XG4gICAgdGhpcy5pZCA9IFBST0dSQU1fQ09VTlRFUisrXG4gICAgdGhpcy5mcmFnSWQgPSBmcmFnSWRcbiAgICB0aGlzLnZlcnRJZCA9IHZlcnRJZFxuICAgIHRoaXMucHJvZ3JhbSA9IG51bGxcbiAgICB0aGlzLnVuaWZvcm1zID0gW11cbiAgICB0aGlzLmF0dHJpYnV0ZXMgPSBbXVxuICB9XG5cbiAgZnVuY3Rpb24gbGlua1Byb2dyYW0gKGRlc2MsIGNvbW1hbmQpIHtcbiAgICB2YXIgaSwgaW5mb1xuXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIGNvbXBpbGUgJiBsaW5rXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIHZhciBmcmFnU2hhZGVyID0gZ2V0U2hhZGVyKEdMX0ZSQUdNRU5UX1NIQURFUiwgZGVzYy5mcmFnSWQpXG4gICAgdmFyIHZlcnRTaGFkZXIgPSBnZXRTaGFkZXIoR0xfVkVSVEVYX1NIQURFUiwgZGVzYy52ZXJ0SWQpXG5cbiAgICB2YXIgcHJvZ3JhbSA9IGRlc2MucHJvZ3JhbSA9IGdsLmNyZWF0ZVByb2dyYW0oKVxuICAgIGdsLmF0dGFjaFNoYWRlcihwcm9ncmFtLCBmcmFnU2hhZGVyKVxuICAgIGdsLmF0dGFjaFNoYWRlcihwcm9ncmFtLCB2ZXJ0U2hhZGVyKVxuICAgIGdsLmxpbmtQcm9ncmFtKHByb2dyYW0pXG4gICAgY2hlY2subGlua0Vycm9yKFxuICAgICAgZ2wsXG4gICAgICBwcm9ncmFtLFxuICAgICAgc3RyaW5nU3RvcmUuc3RyKGRlc2MuZnJhZ0lkKSxcbiAgICAgIHN0cmluZ1N0b3JlLnN0cihkZXNjLnZlcnRJZCksXG4gICAgICBjb21tYW5kKVxuXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIGdyYWIgdW5pZm9ybXNcbiAgICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgdmFyIG51bVVuaWZvcm1zID0gZ2wuZ2V0UHJvZ3JhbVBhcmFtZXRlcihwcm9ncmFtLCBHTF9BQ1RJVkVfVU5JRk9STVMpXG4gICAgdmFyIHVuaWZvcm1zID0gZGVzYy51bmlmb3Jtc1xuICAgIGZvciAoaSA9IDA7IGkgPCBudW1Vbmlmb3JtczsgKytpKSB7XG4gICAgICBpbmZvID0gZ2wuZ2V0QWN0aXZlVW5pZm9ybShwcm9ncmFtLCBpKVxuICAgICAgaWYgKGluZm8pIHtcbiAgICAgICAgaWYgKGluZm8uc2l6ZSA+IDEpIHtcbiAgICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGluZm8uc2l6ZTsgKytqKSB7XG4gICAgICAgICAgICB2YXIgbmFtZSA9IGluZm8ubmFtZS5yZXBsYWNlKCdbMF0nLCAnWycgKyBqICsgJ10nKVxuICAgICAgICAgICAgaW5zZXJ0QWN0aXZlSW5mbyh1bmlmb3JtcywgbmV3IEFjdGl2ZUluZm8oXG4gICAgICAgICAgICAgIG5hbWUsXG4gICAgICAgICAgICAgIHN0cmluZ1N0b3JlLmlkKG5hbWUpLFxuICAgICAgICAgICAgICBnbC5nZXRVbmlmb3JtTG9jYXRpb24ocHJvZ3JhbSwgbmFtZSksXG4gICAgICAgICAgICAgIGluZm8pKVxuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpbnNlcnRBY3RpdmVJbmZvKHVuaWZvcm1zLCBuZXcgQWN0aXZlSW5mbyhcbiAgICAgICAgICAgIGluZm8ubmFtZSxcbiAgICAgICAgICAgIHN0cmluZ1N0b3JlLmlkKGluZm8ubmFtZSksXG4gICAgICAgICAgICBnbC5nZXRVbmlmb3JtTG9jYXRpb24ocHJvZ3JhbSwgaW5mby5uYW1lKSxcbiAgICAgICAgICAgIGluZm8pKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIGdyYWIgYXR0cmlidXRlc1xuICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICB2YXIgbnVtQXR0cmlidXRlcyA9IGdsLmdldFByb2dyYW1QYXJhbWV0ZXIocHJvZ3JhbSwgR0xfQUNUSVZFX0FUVFJJQlVURVMpXG4gICAgdmFyIGF0dHJpYnV0ZXMgPSBkZXNjLmF0dHJpYnV0ZXNcbiAgICBmb3IgKGkgPSAwOyBpIDwgbnVtQXR0cmlidXRlczsgKytpKSB7XG4gICAgICBpbmZvID0gZ2wuZ2V0QWN0aXZlQXR0cmliKHByb2dyYW0sIGkpXG4gICAgICBpZiAoaW5mbykge1xuICAgICAgICBpbnNlcnRBY3RpdmVJbmZvKGF0dHJpYnV0ZXMsIG5ldyBBY3RpdmVJbmZvKFxuICAgICAgICAgIGluZm8ubmFtZSxcbiAgICAgICAgICBzdHJpbmdTdG9yZS5pZChpbmZvLm5hbWUpLFxuICAgICAgICAgIGdsLmdldEF0dHJpYkxvY2F0aW9uKHByb2dyYW0sIGluZm8ubmFtZSksXG4gICAgICAgICAgaW5mbykpXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBjbGVhcjogZnVuY3Rpb24gKCkge1xuICAgICAgdmFyIGRlbGV0ZVNoYWRlciA9IGdsLmRlbGV0ZVNoYWRlci5iaW5kKGdsKVxuICAgICAgdmFsdWVzKGZyYWdTaGFkZXJzKS5mb3JFYWNoKGRlbGV0ZVNoYWRlcilcbiAgICAgIGZyYWdTaGFkZXJzID0ge31cbiAgICAgIHZhbHVlcyh2ZXJ0U2hhZGVycykuZm9yRWFjaChkZWxldGVTaGFkZXIpXG4gICAgICB2ZXJ0U2hhZGVycyA9IHt9XG5cbiAgICAgIHByb2dyYW1MaXN0LmZvckVhY2goZnVuY3Rpb24gKGRlc2MpIHtcbiAgICAgICAgZ2wuZGVsZXRlUHJvZ3JhbShkZXNjLnByb2dyYW0pXG4gICAgICB9KVxuICAgICAgcHJvZ3JhbUxpc3QubGVuZ3RoID0gMFxuICAgICAgcHJvZ3JhbUNhY2hlID0ge31cblxuICAgICAgc3RhdHMuc2hhZGVyQ291bnQgPSAwXG4gICAgfSxcblxuICAgIHByb2dyYW06IGZ1bmN0aW9uICh2ZXJ0SWQsIGZyYWdJZCwgY29tbWFuZCkge1xuICAgICAgY2hlY2sodmVydElkID49IDAsICdtaXNzaW5nIHZlcnRleCBzaGFkZXInKVxuICAgICAgY2hlY2soZnJhZ0lkID49IDAsICdtaXNzaW5nIGZyYWdtZW50IHNoYWRlcicpXG5cbiAgICAgIHN0YXRzLnNoYWRlckNvdW50KytcblxuICAgICAgdmFyIGNhY2hlID0gcHJvZ3JhbUNhY2hlW2ZyYWdJZF1cbiAgICAgIGlmICghY2FjaGUpIHtcbiAgICAgICAgY2FjaGUgPSBwcm9ncmFtQ2FjaGVbZnJhZ0lkXSA9IHt9XG4gICAgICB9XG4gICAgICB2YXIgcHJvZ3JhbSA9IGNhY2hlW3ZlcnRJZF1cbiAgICAgIGlmICghcHJvZ3JhbSkge1xuICAgICAgICBwcm9ncmFtID0gbmV3IFJFR0xQcm9ncmFtKGZyYWdJZCwgdmVydElkKVxuICAgICAgICBsaW5rUHJvZ3JhbShwcm9ncmFtLCBjb21tYW5kKVxuICAgICAgICBjYWNoZVt2ZXJ0SWRdID0gcHJvZ3JhbVxuICAgICAgICBwcm9ncmFtTGlzdC5wdXNoKHByb2dyYW0pXG4gICAgICB9XG4gICAgICByZXR1cm4gcHJvZ3JhbVxuICAgIH0sXG5cbiAgICBzaGFkZXI6IGdldFNoYWRlcixcblxuICAgIGZyYWc6IG51bGwsXG4gICAgdmVydDogbnVsbFxuICB9XG59XG4iLCJcbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gc3RhdHMgKCkge1xuICByZXR1cm4ge1xuICAgIGJ1ZmZlckNvdW50OiAwLFxuICAgIGVsZW1lbnRzQ291bnQ6IDAsXG4gICAgZnJhbWVidWZmZXJDb3VudDogMCxcbiAgICBzaGFkZXJDb3VudDogMCxcbiAgICB0ZXh0dXJlQ291bnQ6IDAsXG4gICAgY3ViZUNvdW50OiAwLFxuICAgIHJlbmRlcmJ1ZmZlckNvdW50OiAwXG4gIH1cbn1cbiIsIm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gY3JlYXRlU3RyaW5nU3RvcmUgKCkge1xuICB2YXIgc3RyaW5nSWRzID0geycnOiAwfVxuICB2YXIgc3RyaW5nVmFsdWVzID0gWycnXVxuICByZXR1cm4ge1xuICAgIGlkOiBmdW5jdGlvbiAoc3RyKSB7XG4gICAgICB2YXIgcmVzdWx0ID0gc3RyaW5nSWRzW3N0cl1cbiAgICAgIGlmIChyZXN1bHQpIHtcbiAgICAgICAgcmV0dXJuIHJlc3VsdFxuICAgICAgfVxuICAgICAgcmVzdWx0ID0gc3RyaW5nSWRzW3N0cl0gPSBzdHJpbmdWYWx1ZXMubGVuZ3RoXG4gICAgICBzdHJpbmdWYWx1ZXMucHVzaChzdHIpXG4gICAgICByZXR1cm4gcmVzdWx0XG4gICAgfSxcblxuICAgIHN0cjogZnVuY3Rpb24gKGlkKSB7XG4gICAgICByZXR1cm4gc3RyaW5nVmFsdWVzW2lkXVxuICAgIH1cbiAgfVxufVxuIiwidmFyIGNoZWNrID0gcmVxdWlyZSgnLi91dGlsL2NoZWNrJylcbnZhciBleHRlbmQgPSByZXF1aXJlKCcuL3V0aWwvZXh0ZW5kJylcbnZhciB2YWx1ZXMgPSByZXF1aXJlKCcuL3V0aWwvdmFsdWVzJylcbnZhciBpc1R5cGVkQXJyYXkgPSByZXF1aXJlKCcuL3V0aWwvaXMtdHlwZWQtYXJyYXknKVxudmFyIGlzTkRBcnJheUxpa2UgPSByZXF1aXJlKCcuL3V0aWwvaXMtbmRhcnJheScpXG52YXIgcG9vbCA9IHJlcXVpcmUoJy4vdXRpbC9wb29sJylcbnZhciBjb252ZXJ0VG9IYWxmRmxvYXQgPSByZXF1aXJlKCcuL3V0aWwvdG8taGFsZi1mbG9hdCcpXG52YXIgaXNBcnJheUxpa2UgPSByZXF1aXJlKCcuL3V0aWwvaXMtYXJyYXktbGlrZScpXG5cbnZhciBkdHlwZXMgPSByZXF1aXJlKCcuL2NvbnN0YW50cy9hcnJheXR5cGVzLmpzb24nKVxudmFyIGFycmF5VHlwZXMgPSByZXF1aXJlKCcuL2NvbnN0YW50cy9hcnJheXR5cGVzLmpzb24nKVxuXG52YXIgR0xfQ09NUFJFU1NFRF9URVhUVVJFX0ZPUk1BVFMgPSAweDg2QTNcblxudmFyIEdMX1RFWFRVUkVfMkQgPSAweDBERTFcbnZhciBHTF9URVhUVVJFX0NVQkVfTUFQID0gMHg4NTEzXG52YXIgR0xfVEVYVFVSRV9DVUJFX01BUF9QT1NJVElWRV9YID0gMHg4NTE1XG5cbnZhciBHTF9SR0JBID0gMHgxOTA4XG52YXIgR0xfQUxQSEEgPSAweDE5MDZcbnZhciBHTF9SR0IgPSAweDE5MDdcbnZhciBHTF9MVU1JTkFOQ0UgPSAweDE5MDlcbnZhciBHTF9MVU1JTkFOQ0VfQUxQSEEgPSAweDE5MEFcblxudmFyIEdMX1JHQkE0ID0gMHg4MDU2XG52YXIgR0xfUkdCNV9BMSA9IDB4ODA1N1xudmFyIEdMX1JHQjU2NSA9IDB4OEQ2MlxuXG52YXIgR0xfVU5TSUdORURfU0hPUlRfNF80XzRfNCA9IDB4ODAzM1xudmFyIEdMX1VOU0lHTkVEX1NIT1JUXzVfNV81XzEgPSAweDgwMzRcbnZhciBHTF9VTlNJR05FRF9TSE9SVF81XzZfNSA9IDB4ODM2M1xudmFyIEdMX1VOU0lHTkVEX0lOVF8yNF84X1dFQkdMID0gMHg4NEZBXG5cbnZhciBHTF9ERVBUSF9DT01QT05FTlQgPSAweDE5MDJcbnZhciBHTF9ERVBUSF9TVEVOQ0lMID0gMHg4NEY5XG5cbnZhciBHTF9TUkdCX0VYVCA9IDB4OEM0MFxudmFyIEdMX1NSR0JfQUxQSEFfRVhUID0gMHg4QzQyXG5cbnZhciBHTF9IQUxGX0ZMT0FUX09FUyA9IDB4OEQ2MVxuXG52YXIgR0xfQ09NUFJFU1NFRF9SR0JfUzNUQ19EWFQxX0VYVCA9IDB4ODNGMFxudmFyIEdMX0NPTVBSRVNTRURfUkdCQV9TM1RDX0RYVDFfRVhUID0gMHg4M0YxXG52YXIgR0xfQ09NUFJFU1NFRF9SR0JBX1MzVENfRFhUM19FWFQgPSAweDgzRjJcbnZhciBHTF9DT01QUkVTU0VEX1JHQkFfUzNUQ19EWFQ1X0VYVCA9IDB4ODNGM1xuXG52YXIgR0xfQ09NUFJFU1NFRF9SR0JfQVRDX1dFQkdMID0gMHg4QzkyXG52YXIgR0xfQ09NUFJFU1NFRF9SR0JBX0FUQ19FWFBMSUNJVF9BTFBIQV9XRUJHTCA9IDB4OEM5M1xudmFyIEdMX0NPTVBSRVNTRURfUkdCQV9BVENfSU5URVJQT0xBVEVEX0FMUEhBX1dFQkdMID0gMHg4N0VFXG5cbnZhciBHTF9DT01QUkVTU0VEX1JHQl9QVlJUQ180QlBQVjFfSU1HID0gMHg4QzAwXG52YXIgR0xfQ09NUFJFU1NFRF9SR0JfUFZSVENfMkJQUFYxX0lNRyA9IDB4OEMwMVxudmFyIEdMX0NPTVBSRVNTRURfUkdCQV9QVlJUQ180QlBQVjFfSU1HID0gMHg4QzAyXG52YXIgR0xfQ09NUFJFU1NFRF9SR0JBX1BWUlRDXzJCUFBWMV9JTUcgPSAweDhDMDNcblxudmFyIEdMX0NPTVBSRVNTRURfUkdCX0VUQzFfV0VCR0wgPSAweDhENjRcblxudmFyIEdMX1VOU0lHTkVEX0JZVEUgPSAweDE0MDFcbnZhciBHTF9VTlNJR05FRF9TSE9SVCA9IDB4MTQwM1xudmFyIEdMX1VOU0lHTkVEX0lOVCA9IDB4MTQwNVxudmFyIEdMX0ZMT0FUID0gMHgxNDA2XG5cbnZhciBHTF9URVhUVVJFX1dSQVBfUyA9IDB4MjgwMlxudmFyIEdMX1RFWFRVUkVfV1JBUF9UID0gMHgyODAzXG5cbnZhciBHTF9SRVBFQVQgPSAweDI5MDFcbnZhciBHTF9DTEFNUF9UT19FREdFID0gMHg4MTJGXG52YXIgR0xfTUlSUk9SRURfUkVQRUFUID0gMHg4MzcwXG5cbnZhciBHTF9URVhUVVJFX01BR19GSUxURVIgPSAweDI4MDBcbnZhciBHTF9URVhUVVJFX01JTl9GSUxURVIgPSAweDI4MDFcblxudmFyIEdMX05FQVJFU1QgPSAweDI2MDBcbnZhciBHTF9MSU5FQVIgPSAweDI2MDFcbnZhciBHTF9ORUFSRVNUX01JUE1BUF9ORUFSRVNUID0gMHgyNzAwXG52YXIgR0xfTElORUFSX01JUE1BUF9ORUFSRVNUID0gMHgyNzAxXG52YXIgR0xfTkVBUkVTVF9NSVBNQVBfTElORUFSID0gMHgyNzAyXG52YXIgR0xfTElORUFSX01JUE1BUF9MSU5FQVIgPSAweDI3MDNcblxudmFyIEdMX0dFTkVSQVRFX01JUE1BUF9ISU5UID0gMHg4MTkyXG52YXIgR0xfRE9OVF9DQVJFID0gMHgxMTAwXG52YXIgR0xfRkFTVEVTVCA9IDB4MTEwMVxudmFyIEdMX05JQ0VTVCA9IDB4MTEwMlxuXG52YXIgR0xfVEVYVFVSRV9NQVhfQU5JU09UUk9QWV9FWFQgPSAweDg0RkVcblxudmFyIEdMX1VOUEFDS19BTElHTk1FTlQgPSAweDBDRjVcbnZhciBHTF9VTlBBQ0tfRkxJUF9ZX1dFQkdMID0gMHg5MjQwXG52YXIgR0xfVU5QQUNLX1BSRU1VTFRJUExZX0FMUEhBX1dFQkdMID0gMHg5MjQxXG52YXIgR0xfVU5QQUNLX0NPTE9SU1BBQ0VfQ09OVkVSU0lPTl9XRUJHTCA9IDB4OTI0M1xuXG52YXIgR0xfQlJPV1NFUl9ERUZBVUxUX1dFQkdMID0gMHg5MjQ0XG5cbnZhciBHTF9URVhUVVJFMCA9IDB4ODRDMFxuXG52YXIgTUlQTUFQX0ZJTFRFUlMgPSBbXG4gIEdMX05FQVJFU1RfTUlQTUFQX05FQVJFU1QsXG4gIEdMX05FQVJFU1RfTUlQTUFQX0xJTkVBUixcbiAgR0xfTElORUFSX01JUE1BUF9ORUFSRVNULFxuICBHTF9MSU5FQVJfTUlQTUFQX0xJTkVBUlxuXVxuXG52YXIgQ0hBTk5FTFNfRk9STUFUID0gW1xuICAwLFxuICBHTF9MVU1JTkFOQ0UsXG4gIEdMX0xVTUlOQU5DRV9BTFBIQSxcbiAgR0xfUkdCLFxuICBHTF9SR0JBXG5dXG5cbnZhciBGT1JNQVRfQ0hBTk5FTFMgPSB7fVxuRk9STUFUX0NIQU5ORUxTW0dMX0xVTUlOQU5DRV0gPVxuRk9STUFUX0NIQU5ORUxTW0dMX0FMUEhBXSA9XG5GT1JNQVRfQ0hBTk5FTFNbR0xfREVQVEhfQ09NUE9ORU5UXSA9IDFcbkZPUk1BVF9DSEFOTkVMU1tHTF9ERVBUSF9TVEVOQ0lMXSA9XG5GT1JNQVRfQ0hBTk5FTFNbR0xfTFVNSU5BTkNFX0FMUEhBXSA9IDJcbkZPUk1BVF9DSEFOTkVMU1tHTF9SR0JdID0gM1xuRk9STUFUX0NIQU5ORUxTW0dMX1JHQkFdID0gNFxuXG52YXIgZm9ybWF0VHlwZXMgPSB7fVxuZm9ybWF0VHlwZXNbR0xfUkdCQTRdID0gR0xfVU5TSUdORURfU0hPUlRfNF80XzRfNFxuZm9ybWF0VHlwZXNbR0xfUkdCNTY1XSA9IEdMX1VOU0lHTkVEX1NIT1JUXzVfNl81XG5mb3JtYXRUeXBlc1tHTF9SR0I1X0ExXSA9IEdMX1VOU0lHTkVEX1NIT1JUXzVfNV81XzFcbmZvcm1hdFR5cGVzW0dMX0RFUFRIX0NPTVBPTkVOVF0gPSBHTF9VTlNJR05FRF9JTlRcbmZvcm1hdFR5cGVzW0dMX0RFUFRIX1NURU5DSUxdID0gR0xfVU5TSUdORURfSU5UXzI0XzhfV0VCR0xcblxuZnVuY3Rpb24gb2JqZWN0TmFtZSAoc3RyKSB7XG4gIHJldHVybiAnW29iamVjdCAnICsgc3RyICsgJ10nXG59XG5cbnZhciBDQU5WQVNfQ0xBU1MgPSBvYmplY3ROYW1lKCdIVE1MQ2FudmFzRWxlbWVudCcpXG52YXIgQ09OVEVYVDJEX0NMQVNTID0gb2JqZWN0TmFtZSgnQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJEJylcbnZhciBJTUFHRV9DTEFTUyA9IG9iamVjdE5hbWUoJ0hUTUxJbWFnZUVsZW1lbnQnKVxudmFyIFZJREVPX0NMQVNTID0gb2JqZWN0TmFtZSgnSFRNTFZpZGVvRWxlbWVudCcpXG5cbnZhciBQSVhFTF9DTEFTU0VTID0gT2JqZWN0LmtleXMoZHR5cGVzKS5jb25jYXQoW1xuICBDQU5WQVNfQ0xBU1MsXG4gIENPTlRFWFQyRF9DTEFTUyxcbiAgSU1BR0VfQ0xBU1MsXG4gIFZJREVPX0NMQVNTXG5dKVxuXG5mdW5jdGlvbiBpc051bWVyaWNBcnJheSAoYXJyKSB7XG4gIHJldHVybiAoXG4gICAgQXJyYXkuaXNBcnJheShhcnIpICYmXG4gICAgKGFyci5sZW5ndGggPT09IDAgfHxcbiAgICB0eXBlb2YgYXJyWzBdID09PSAnbnVtYmVyJykpXG59XG5cbmZ1bmN0aW9uIGlzUmVjdEFycmF5IChhcnIpIHtcbiAgaWYgKCFBcnJheS5pc0FycmF5KGFycikpIHtcbiAgICByZXR1cm4gZmFsc2VcbiAgfVxuICB2YXIgd2lkdGggPSBhcnIubGVuZ3RoXG4gIGlmICh3aWR0aCA9PT0gMCB8fCAhaXNBcnJheUxpa2UoYXJyWzBdKSkge1xuICAgIHJldHVybiBmYWxzZVxuICB9XG4gIHJldHVybiB0cnVlXG59XG5cbmZ1bmN0aW9uIGNsYXNzU3RyaW5nICh4KSB7XG4gIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoeClcbn1cblxuZnVuY3Rpb24gaXNDYW52YXNFbGVtZW50IChvYmplY3QpIHtcbiAgcmV0dXJuIGNsYXNzU3RyaW5nKG9iamVjdCkgPT09IENBTlZBU19DTEFTU1xufVxuXG5mdW5jdGlvbiBpc0NvbnRleHQyRCAob2JqZWN0KSB7XG4gIHJldHVybiBjbGFzc1N0cmluZyhvYmplY3QpID09PSBDT05URVhUMkRfQ0xBU1Ncbn1cblxuZnVuY3Rpb24gaXNJbWFnZUVsZW1lbnQgKG9iamVjdCkge1xuICByZXR1cm4gY2xhc3NTdHJpbmcob2JqZWN0KSA9PT0gSU1BR0VfQ0xBU1Ncbn1cblxuZnVuY3Rpb24gaXNWaWRlb0VsZW1lbnQgKG9iamVjdCkge1xuICByZXR1cm4gY2xhc3NTdHJpbmcob2JqZWN0KSA9PT0gVklERU9fQ0xBU1Ncbn1cblxuZnVuY3Rpb24gaXNQaXhlbERhdGEgKG9iamVjdCkge1xuICBpZiAoIW9iamVjdCkge1xuICAgIHJldHVybiBmYWxzZVxuICB9XG4gIHZhciBjbGFzc05hbWUgPSBjbGFzc1N0cmluZyhvYmplY3QpXG4gIGlmIChQSVhFTF9DTEFTU0VTLmluZGV4T2YoY2xhc3NOYW1lKSA+PSAwKSB7XG4gICAgcmV0dXJuIHRydWVcbiAgfVxuICByZXR1cm4gKFxuICAgIGlzTnVtZXJpY0FycmF5KG9iamVjdCkgfHxcbiAgICBpc1JlY3RBcnJheShvYmplY3QpIHx8XG4gICAgaXNOREFycmF5TGlrZShvYmplY3QpKVxufVxuXG5mdW5jdGlvbiB0eXBlZEFycmF5Q29kZSAoZGF0YSkge1xuICByZXR1cm4gYXJyYXlUeXBlc1tPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoZGF0YSldIHwgMFxufVxuXG5mdW5jdGlvbiBjb252ZXJ0RGF0YSAocmVzdWx0LCBkYXRhKSB7XG4gIHZhciBuID0gcmVzdWx0LndpZHRoICogcmVzdWx0LmhlaWdodCAqIHJlc3VsdC5jaGFubmVsc1xuICBjaGVjayhkYXRhLmxlbmd0aCA9PT0gbiwgJ2luY29uc2lzdGVudCBhcnJheSBsZW5ndGggZm9yIHRleHR1cmUnKVxuXG4gIHN3aXRjaCAocmVzdWx0LnR5cGUpIHtcbiAgICBjYXNlIEdMX1VOU0lHTkVEX0JZVEU6XG4gICAgY2FzZSBHTF9VTlNJR05FRF9TSE9SVDpcbiAgICBjYXNlIEdMX1VOU0lHTkVEX0lOVDpcbiAgICBjYXNlIEdMX0ZMT0FUOlxuICAgICAgdmFyIGNvbnZlcnRlZCA9IHBvb2wuYWxsb2NUeXBlKHJlc3VsdC50eXBlLCBuKVxuICAgICAgY29udmVydGVkLnNldChkYXRhKVxuICAgICAgcmVzdWx0LmRhdGEgPSBjb252ZXJ0ZWRcbiAgICAgIGJyZWFrXG5cbiAgICBjYXNlIEdMX0hBTEZfRkxPQVRfT0VTOlxuICAgICAgcmVzdWx0LmRhdGEgPSBjb252ZXJ0VG9IYWxmRmxvYXQoZGF0YSlcbiAgICAgIGJyZWFrXG5cbiAgICBkZWZhdWx0OlxuICAgICAgY2hlY2sucmFpc2UoJ3Vuc3VwcG9ydGVkIHRleHR1cmUgdHlwZSwgbXVzdCBzcGVjaWZ5IGEgdHlwZWQgYXJyYXknKVxuICB9XG59XG5cbmZ1bmN0aW9uIHByZUNvbnZlcnQgKGltYWdlLCBuKSB7XG4gIHJldHVybiBwb29sLmFsbG9jVHlwZShcbiAgICBpbWFnZS50eXBlID09PSBHTF9IQUxGX0ZMT0FUX09FU1xuICAgICAgPyBHTF9GTE9BVFxuICAgICAgOiBpbWFnZS50eXBlLCBuKVxufVxuXG5mdW5jdGlvbiBwb3N0Q29udmVydCAoaW1hZ2UsIGRhdGEpIHtcbiAgaWYgKGltYWdlLnR5cGUgPT09IEdMX0hBTEZfRkxPQVRfT0VTKSB7XG4gICAgaW1hZ2UuZGF0YSA9IGNvbnZlcnRUb0hhbGZGbG9hdChkYXRhKVxuICAgIHBvb2wuZnJlZVR5cGUoZGF0YSlcbiAgfSBlbHNlIHtcbiAgICBpbWFnZS5kYXRhID0gZGF0YVxuICB9XG59XG5cbmZ1bmN0aW9uIHRyYW5zcG9zZURhdGEgKGltYWdlLCBhcnJheSwgc3RyaWRlWCwgc3RyaWRlWSwgc3RyaWRlQywgb2Zmc2V0KSB7XG4gIHZhciB3ID0gaW1hZ2Uud2lkdGhcbiAgdmFyIGggPSBpbWFnZS5oZWlnaHRcbiAgdmFyIGMgPSBpbWFnZS5jaGFubmVsc1xuICB2YXIgbiA9IHcgKiBoICogY1xuICB2YXIgZGF0YSA9IHByZUNvbnZlcnQoaW1hZ2UsIG4pXG5cbiAgdmFyIHAgPSAwXG4gIGZvciAodmFyIGkgPSAwOyBpIDwgaDsgKytpKSB7XG4gICAgZm9yICh2YXIgaiA9IDA7IGogPCB3OyArK2opIHtcbiAgICAgIGZvciAodmFyIGsgPSAwOyBrIDwgYzsgKytrKSB7XG4gICAgICAgIGRhdGFbcCsrXSA9IGFycmF5W3N0cmlkZVggKiBqICsgc3RyaWRlWSAqIGkgKyBzdHJpZGVDICogayArIG9mZnNldF1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBwb3N0Q29udmVydChpbWFnZSwgZGF0YSlcbn1cblxuZnVuY3Rpb24gZmxhdHRlbjJERGF0YSAoaW1hZ2UsIGFycmF5LCB3LCBoKSB7XG4gIHZhciBuID0gdyAqIGhcbiAgdmFyIGRhdGEgPSBwcmVDb252ZXJ0KGltYWdlLCBuKVxuXG4gIHZhciBwID0gMFxuICBmb3IgKHZhciBpID0gMDsgaSA8IGg7ICsraSkge1xuICAgIHZhciByb3cgPSBhcnJheVtpXVxuICAgIGZvciAodmFyIGogPSAwOyBqIDwgdzsgKytqKSB7XG4gICAgICBkYXRhW3ArK10gPSByb3dbal1cbiAgICB9XG4gIH1cblxuICBwb3N0Q29udmVydChpbWFnZSwgZGF0YSlcbn1cblxuZnVuY3Rpb24gZmxhdHRlbjNERGF0YSAoaW1hZ2UsIGFycmF5LCB3LCBoLCBjKSB7XG4gIHZhciBuID0gdyAqIGggKiBjXG4gIHZhciBkYXRhID0gcHJlQ29udmVydChpbWFnZSwgbilcblxuICB2YXIgcCA9IDBcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBoOyArK2kpIHtcbiAgICB2YXIgcm93ID0gYXJyYXlbaV1cbiAgICBmb3IgKHZhciBqID0gMDsgaiA8IHc7ICsraikge1xuICAgICAgdmFyIHBpeGVsID0gcm93W2pdXG4gICAgICBmb3IgKHZhciBrID0gMDsgayA8IGM7ICsraykge1xuICAgICAgICBkYXRhW3ArK10gPSBwaXhlbFtrXVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHBvc3RDb252ZXJ0KGltYWdlLCBkYXRhKVxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIGNyZWF0ZVRleHR1cmVTZXQgKFxuICBnbCwgZXh0ZW5zaW9ucywgbGltaXRzLCByZWdsUG9sbCwgY29udGV4dFN0YXRlLCBzdGF0cykge1xuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gIC8vIEluaXRpYWxpemUgY29uc3RhbnRzIGFuZCBwYXJhbWV0ZXIgdGFibGVzIGhlcmVcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICB2YXIgbWlwbWFwSGludCA9IHtcbiAgICBcImRvbid0IGNhcmVcIjogR0xfRE9OVF9DQVJFLFxuICAgICdkb250IGNhcmUnOiBHTF9ET05UX0NBUkUsXG4gICAgJ25pY2UnOiBHTF9OSUNFU1QsXG4gICAgJ2Zhc3QnOiBHTF9GQVNURVNUXG4gIH1cblxuICB2YXIgd3JhcE1vZGVzID0ge1xuICAgICdyZXBlYXQnOiBHTF9SRVBFQVQsXG4gICAgJ2NsYW1wJzogR0xfQ0xBTVBfVE9fRURHRSxcbiAgICAnbWlycm9yJzogR0xfTUlSUk9SRURfUkVQRUFUXG4gIH1cblxuICB2YXIgbWFnRmlsdGVycyA9IHtcbiAgICAnbmVhcmVzdCc6IEdMX05FQVJFU1QsXG4gICAgJ2xpbmVhcic6IEdMX0xJTkVBUlxuICB9XG5cbiAgdmFyIG1pbkZpbHRlcnMgPSBleHRlbmQoe1xuICAgICduZWFyZXN0IG1pcG1hcCBuZWFyZXN0JzogR0xfTkVBUkVTVF9NSVBNQVBfTkVBUkVTVCxcbiAgICAnbGluZWFyIG1pcG1hcCBuZWFyZXN0JzogR0xfTElORUFSX01JUE1BUF9ORUFSRVNULFxuICAgICduZWFyZXN0IG1pcG1hcCBsaW5lYXInOiBHTF9ORUFSRVNUX01JUE1BUF9MSU5FQVIsXG4gICAgJ2xpbmVhciBtaXBtYXAgbGluZWFyJzogR0xfTElORUFSX01JUE1BUF9MSU5FQVIsXG4gICAgJ21pcG1hcCc6IEdMX0xJTkVBUl9NSVBNQVBfTElORUFSXG4gIH0sIG1hZ0ZpbHRlcnMpXG5cbiAgdmFyIGNvbG9yU3BhY2UgPSB7XG4gICAgJ25vbmUnOiAwLFxuICAgICdicm93c2VyJzogR0xfQlJPV1NFUl9ERUZBVUxUX1dFQkdMXG4gIH1cblxuICB2YXIgdGV4dHVyZVR5cGVzID0ge1xuICAgICd1aW50OCc6IEdMX1VOU0lHTkVEX0JZVEUsXG4gICAgJ3JnYmE0JzogR0xfVU5TSUdORURfU0hPUlRfNF80XzRfNCxcbiAgICAncmdiNTY1JzogR0xfVU5TSUdORURfU0hPUlRfNV82XzUsXG4gICAgJ3JnYjUgYTEnOiBHTF9VTlNJR05FRF9TSE9SVF81XzVfNV8xXG4gIH1cblxuICB2YXIgdGV4dHVyZUZvcm1hdHMgPSB7XG4gICAgJ2FscGhhJzogR0xfQUxQSEEsXG4gICAgJ2x1bWluYW5jZSc6IEdMX0xVTUlOQU5DRSxcbiAgICAnbHVtaW5hbmNlIGFscGhhJzogR0xfTFVNSU5BTkNFX0FMUEhBLFxuICAgICdyZ2InOiBHTF9SR0IsXG4gICAgJ3JnYmEnOiBHTF9SR0JBLFxuICAgICdyZ2JhNCc6IEdMX1JHQkE0LFxuICAgICdyZ2I1IGExJzogR0xfUkdCNV9BMSxcbiAgICAncmdiNTY1JzogR0xfUkdCNTY1XG4gIH1cblxuICB2YXIgY29tcHJlc3NlZFRleHR1cmVGb3JtYXRzID0ge31cblxuICBpZiAoZXh0ZW5zaW9ucy5leHRfc3JnYikge1xuICAgIHRleHR1cmVGb3JtYXRzLnNyZ2IgPSBHTF9TUkdCX0VYVFxuICAgIHRleHR1cmVGb3JtYXRzLnNyZ2JhID0gR0xfU1JHQl9BTFBIQV9FWFRcbiAgfVxuXG4gIGlmIChleHRlbnNpb25zLm9lc190ZXh0dXJlX2Zsb2F0KSB7XG4gICAgdGV4dHVyZVR5cGVzLmZsb2F0ID0gR0xfRkxPQVRcbiAgfVxuXG4gIGlmIChleHRlbnNpb25zLm9lc190ZXh0dXJlX2hhbGZfZmxvYXQpIHtcbiAgICB0ZXh0dXJlVHlwZXNbJ2Zsb2F0MTYnXSA9IHRleHR1cmVUeXBlc1snaGFsZiBmbG9hdCddID0gR0xfSEFMRl9GTE9BVF9PRVNcbiAgfVxuXG4gIGlmIChleHRlbnNpb25zLndlYmdsX2RlcHRoX3RleHR1cmUpIHtcbiAgICBleHRlbmQodGV4dHVyZUZvcm1hdHMsIHtcbiAgICAgICdkZXB0aCc6IEdMX0RFUFRIX0NPTVBPTkVOVCxcbiAgICAgICdkZXB0aCBzdGVuY2lsJzogR0xfREVQVEhfU1RFTkNJTFxuICAgIH0pXG5cbiAgICBleHRlbmQodGV4dHVyZVR5cGVzLCB7XG4gICAgICAndWludDE2JzogR0xfVU5TSUdORURfU0hPUlQsXG4gICAgICAndWludDMyJzogR0xfVU5TSUdORURfSU5ULFxuICAgICAgJ2RlcHRoIHN0ZW5jaWwnOiBHTF9VTlNJR05FRF9JTlRfMjRfOF9XRUJHTFxuICAgIH0pXG4gIH1cblxuICBpZiAoZXh0ZW5zaW9ucy53ZWJnbF9jb21wcmVzc2VkX3RleHR1cmVfczN0Yykge1xuICAgIGV4dGVuZChjb21wcmVzc2VkVGV4dHVyZUZvcm1hdHMsIHtcbiAgICAgICdyZ2IgczN0YyBkeHQxJzogR0xfQ09NUFJFU1NFRF9SR0JfUzNUQ19EWFQxX0VYVCxcbiAgICAgICdyZ2JhIHMzdGMgZHh0MSc6IEdMX0NPTVBSRVNTRURfUkdCQV9TM1RDX0RYVDFfRVhULFxuICAgICAgJ3JnYmEgczN0YyBkeHQzJzogR0xfQ09NUFJFU1NFRF9SR0JBX1MzVENfRFhUM19FWFQsXG4gICAgICAncmdiYSBzM3RjIGR4dDUnOiBHTF9DT01QUkVTU0VEX1JHQkFfUzNUQ19EWFQ1X0VYVFxuICAgIH0pXG4gIH1cblxuICBpZiAoZXh0ZW5zaW9ucy53ZWJnbF9jb21wcmVzc2VkX3RleHR1cmVfYXRjKSB7XG4gICAgZXh0ZW5kKGNvbXByZXNzZWRUZXh0dXJlRm9ybWF0cywge1xuICAgICAgJ3JnYiBhdGMnOiBHTF9DT01QUkVTU0VEX1JHQl9BVENfV0VCR0wsXG4gICAgICAncmdiYSBhdGMgZXhwbGljaXQgYWxwaGEnOiBHTF9DT01QUkVTU0VEX1JHQkFfQVRDX0VYUExJQ0lUX0FMUEhBX1dFQkdMLFxuICAgICAgJ3JnYmEgYXRjIGludGVycG9sYXRlZCBhbHBoYSc6IEdMX0NPTVBSRVNTRURfUkdCQV9BVENfSU5URVJQT0xBVEVEX0FMUEhBX1dFQkdMXG4gICAgfSlcbiAgfVxuXG4gIGlmIChleHRlbnNpb25zLndlYmdsX2NvbXByZXNzZWRfdGV4dHVyZV9wdnJ0Yykge1xuICAgIGV4dGVuZChjb21wcmVzc2VkVGV4dHVyZUZvcm1hdHMsIHtcbiAgICAgICdyZ2IgcHZydGMgNGJwcHYxJzogR0xfQ09NUFJFU1NFRF9SR0JfUFZSVENfNEJQUFYxX0lNRyxcbiAgICAgICdyZ2IgcHZydGMgMmJwcHYxJzogR0xfQ09NUFJFU1NFRF9SR0JfUFZSVENfMkJQUFYxX0lNRyxcbiAgICAgICdyZ2JhIHB2cnRjIDRicHB2MSc6IEdMX0NPTVBSRVNTRURfUkdCQV9QVlJUQ180QlBQVjFfSU1HLFxuICAgICAgJ3JnYmEgcHZydGMgMmJwcHYxJzogR0xfQ09NUFJFU1NFRF9SR0JBX1BWUlRDXzJCUFBWMV9JTUdcbiAgICB9KVxuICB9XG5cbiAgaWYgKGV4dGVuc2lvbnMud2ViZ2xfY29tcHJlc3NlZF90ZXh0dXJlX2V0YzEpIHtcbiAgICBjb21wcmVzc2VkVGV4dHVyZUZvcm1hdHNbJ3JnYiBldGMxJ10gPSBHTF9DT01QUkVTU0VEX1JHQl9FVEMxX1dFQkdMXG4gIH1cblxuICAvLyBDb3B5IG92ZXIgYWxsIHRleHR1cmUgZm9ybWF0c1xuICB2YXIgc3VwcG9ydGVkQ29tcHJlc3NlZEZvcm1hdHMgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChcbiAgICBnbC5nZXRQYXJhbWV0ZXIoR0xfQ09NUFJFU1NFRF9URVhUVVJFX0ZPUk1BVFMpKVxuICBPYmplY3Qua2V5cyhjb21wcmVzc2VkVGV4dHVyZUZvcm1hdHMpLmZvckVhY2goZnVuY3Rpb24gKG5hbWUpIHtcbiAgICB2YXIgZm9ybWF0ID0gY29tcHJlc3NlZFRleHR1cmVGb3JtYXRzW25hbWVdXG4gICAgaWYgKHN1cHBvcnRlZENvbXByZXNzZWRGb3JtYXRzLmluZGV4T2YoZm9ybWF0KSA+PSAwKSB7XG4gICAgICB0ZXh0dXJlRm9ybWF0c1tuYW1lXSA9IGZvcm1hdFxuICAgIH1cbiAgfSlcblxuICB2YXIgc3VwcG9ydGVkRm9ybWF0cyA9IE9iamVjdC5rZXlzKHRleHR1cmVGb3JtYXRzKVxuICBsaW1pdHMudGV4dHVyZUZvcm1hdHMgPSBzdXBwb3J0ZWRGb3JtYXRzXG5cbiAgLy8gY29sb3JGb3JtYXRzW10gZ2l2ZXMgdGhlIGZvcm1hdCAoY2hhbm5lbHMpIGFzc29jaWF0ZWQgdG8gYW5cbiAgLy8gaW50ZXJuYWxmb3JtYXRcbiAgdmFyIGNvbG9yRm9ybWF0cyA9IHN1cHBvcnRlZEZvcm1hdHMucmVkdWNlKGZ1bmN0aW9uIChjb2xvciwga2V5KSB7XG4gICAgdmFyIGdsZW51bSA9IHRleHR1cmVGb3JtYXRzW2tleV1cbiAgICBpZiAoZ2xlbnVtID09PSBHTF9MVU1JTkFOQ0UgfHxcbiAgICAgICAgZ2xlbnVtID09PSBHTF9BTFBIQSB8fFxuICAgICAgICBnbGVudW0gPT09IEdMX0xVTUlOQU5DRSB8fFxuICAgICAgICBnbGVudW0gPT09IEdMX0xVTUlOQU5DRV9BTFBIQSB8fFxuICAgICAgICBnbGVudW0gPT09IEdMX0RFUFRIX0NPTVBPTkVOVCB8fFxuICAgICAgICBnbGVudW0gPT09IEdMX0RFUFRIX1NURU5DSUwpIHtcbiAgICAgIGNvbG9yW2dsZW51bV0gPSBnbGVudW1cbiAgICB9IGVsc2UgaWYgKGdsZW51bSA9PT0gR0xfUkdCNV9BMSB8fCBrZXkuaW5kZXhPZigncmdiYScpID49IDApIHtcbiAgICAgIGNvbG9yW2dsZW51bV0gPSBHTF9SR0JBXG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbG9yW2dsZW51bV0gPSBHTF9SR0JcbiAgICB9XG4gICAgcmV0dXJuIGNvbG9yXG4gIH0sIHt9KVxuXG4gIGZ1bmN0aW9uIFRleEZsYWdzICgpIHtcbiAgICAvLyBmb3JtYXQgaW5mb1xuICAgIHRoaXMuaW50ZXJuYWxmb3JtYXQgPSBHTF9SR0JBXG4gICAgdGhpcy5mb3JtYXQgPSBHTF9SR0JBXG4gICAgdGhpcy50eXBlID0gR0xfVU5TSUdORURfQllURVxuICAgIHRoaXMuY29tcHJlc3NlZCA9IGZhbHNlXG5cbiAgICAvLyBwaXhlbCBzdG9yYWdlXG4gICAgdGhpcy5wcmVtdWx0aXBseUFscGhhID0gZmFsc2VcbiAgICB0aGlzLmZsaXBZID0gZmFsc2VcbiAgICB0aGlzLnVucGFja0FsaWdubWVudCA9IDFcbiAgICB0aGlzLmNvbG9yU3BhY2UgPSAwXG5cbiAgICAvLyBzaGFwZSBpbmZvXG4gICAgdGhpcy53aWR0aCA9IDBcbiAgICB0aGlzLmhlaWdodCA9IDBcbiAgICB0aGlzLmNoYW5uZWxzID0gNFxuICB9XG5cbiAgZnVuY3Rpb24gY29weUZsYWdzIChyZXN1bHQsIG90aGVyKSB7XG4gICAgcmVzdWx0LmludGVybmFsZm9ybWF0ID0gb3RoZXIuaW50ZXJuYWxmb3JtYXRcbiAgICByZXN1bHQuZm9ybWF0ID0gb3RoZXIuZm9ybWF0XG4gICAgcmVzdWx0LnR5cGUgPSBvdGhlci50eXBlXG4gICAgcmVzdWx0LmNvbXByZXNzZWQgPSBvdGhlci5jb21wcmVzc2VkXG5cbiAgICByZXN1bHQucHJlbXVsdGlwbHlBbHBoYSA9IG90aGVyLnByZW11bHRpcGx5QWxwaGFcbiAgICByZXN1bHQuZmxpcFkgPSBvdGhlci5mbGlwWVxuICAgIHJlc3VsdC51bnBhY2tBbGlnbm1lbnQgPSBvdGhlci51bnBhY2tBbGlnbm1lbnRcbiAgICByZXN1bHQuY29sb3JTcGFjZSA9IG90aGVyLmNvbG9yU3BhY2VcblxuICAgIHJlc3VsdC53aWR0aCA9IG90aGVyLndpZHRoXG4gICAgcmVzdWx0LmhlaWdodCA9IG90aGVyLmhlaWdodFxuICAgIHJlc3VsdC5jaGFubmVscyA9IG90aGVyLmNoYW5uZWxzXG4gIH1cblxuICBmdW5jdGlvbiBwYXJzZUZsYWdzIChmbGFncywgb3B0aW9ucykge1xuICAgIGlmICh0eXBlb2Ygb3B0aW9ucyAhPT0gJ29iamVjdCcgfHwgIW9wdGlvbnMpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmICgncHJlbXVsdGlwbHlBbHBoYScgaW4gb3B0aW9ucykge1xuICAgICAgY2hlY2sudHlwZShvcHRpb25zLnByZW11bHRpcGx5QWxwaGEsICdib29sZWFuJyxcbiAgICAgICAgJ2ludmFsaWQgcHJlbXVsdGlwbHlBbHBoYScpXG4gICAgICBmbGFncy5wcmVtdWx0aXBseUFscGhhID0gb3B0aW9ucy5wcmVtdWx0aXBseUFscGhhXG4gICAgfVxuXG4gICAgaWYgKCdmbGlwWScgaW4gb3B0aW9ucykge1xuICAgICAgY2hlY2sudHlwZShvcHRpb25zLmZsaXBZLCAnYm9vbGVhbicsXG4gICAgICAgICdpbnZhbGlkIHRleHR1cmUgZmxpcCcpXG4gICAgICBmbGFncy5mbGlwWSA9IG9wdGlvbnMuZmxpcFlcbiAgICB9XG5cbiAgICBpZiAoJ2FsaWdubWVudCcgaW4gb3B0aW9ucykge1xuICAgICAgY2hlY2sub25lT2Yob3B0aW9ucy5hbGlnbm1lbnQsIFsxLCAyLCA0LCA4XSxcbiAgICAgICAgJ2ludmFsaWQgdGV4dHVyZSB1bnBhY2sgYWxpZ25tZW50JylcbiAgICAgIGZsYWdzLnVucGFja0FsaWdubWVudCA9IG9wdGlvbnMuYWxpZ25tZW50XG4gICAgfVxuXG4gICAgaWYgKCdjb2xvclNwYWNlJyBpbiBvcHRpb25zKSB7XG4gICAgICBjaGVjay5wYXJhbWV0ZXIob3B0aW9ucy5jb2xvclNwYWNlLCBjb2xvclNwYWNlLFxuICAgICAgICAnaW52YWxpZCBjb2xvclNwYWNlJylcbiAgICAgIGZsYWdzLmNvbG9yU3BhY2UgPSBjb2xvclNwYWNlW29wdGlvbnMuY29sb3JTcGFjZV1cbiAgICB9XG5cbiAgICBpZiAoJ3R5cGUnIGluIG9wdGlvbnMpIHtcbiAgICAgIHZhciB0eXBlID0gb3B0aW9ucy50eXBlXG4gICAgICBjaGVjay5wYXJhbWV0ZXIodHlwZSwgdGV4dHVyZVR5cGVzLFxuICAgICAgICAnaW52YWxpZCB0ZXh0dXJlIHR5cGUnKVxuICAgICAgZmxhZ3MudHlwZSA9IHRleHR1cmVUeXBlc1t0eXBlXVxuICAgIH1cblxuICAgIHZhciB3ID0gZmxhZ3Mud2lkdGhcbiAgICB2YXIgaCA9IGZsYWdzLmhlaWdodFxuICAgIHZhciBjID0gZmxhZ3MuY2hhbm5lbHNcbiAgICB2YXIgaGFzQ2hhbm5lbHMgPSBmYWxzZVxuICAgIGlmICgnc2hhcGUnIGluIG9wdGlvbnMpIHtcbiAgICAgIGNoZWNrKEFycmF5LmlzQXJyYXkob3B0aW9ucy5zaGFwZSkgJiYgb3B0aW9ucy5zaGFwZS5sZW5ndGggPj0gMixcbiAgICAgICAgJ3NoYXBlIG11c3QgYmUgYW4gYXJyYXknKVxuICAgICAgdyA9IG9wdGlvbnMuc2hhcGVbMF1cbiAgICAgIGggPSBvcHRpb25zLnNoYXBlWzFdXG4gICAgICBpZiAob3B0aW9ucy5zaGFwZS5sZW5ndGggPT09IDMpIHtcbiAgICAgICAgYyA9IG9wdGlvbnMuc2hhcGVbMl1cbiAgICAgICAgY2hlY2soYyA+IDAgJiYgYyA8PSA0LCAnaW52YWxpZCBudW1iZXIgb2YgY2hhbm5lbHMnKVxuICAgICAgICBoYXNDaGFubmVscyA9IHRydWVcbiAgICAgIH1cbiAgICAgIGNoZWNrKHcgPiAwICYmIHcgPD0gbGltaXRzLm1heFRleHR1cmVTaXplLCAnaW52YWxpZCB3aWR0aCcpXG4gICAgICBjaGVjayhoID4gMCAmJiBoIDw9IGxpbWl0cy5tYXhUZXh0dXJlU2l6ZSwgJ2ludmFsaWQgaGVpZ2h0JylcbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKCdyYWRpdXMnIGluIG9wdGlvbnMpIHtcbiAgICAgICAgdyA9IGggPSBvcHRpb25zLnJhZGl1c1xuICAgICAgICBjaGVjayh3ID4gMCAmJiB3IDw9IGxpbWl0cy5tYXhUZXh0dXJlU2l6ZSwgJ2ludmFsaWQgcmFkaXVzJylcbiAgICAgIH1cbiAgICAgIGlmICgnd2lkdGgnIGluIG9wdGlvbnMpIHtcbiAgICAgICAgdyA9IG9wdGlvbnMud2lkdGhcbiAgICAgICAgY2hlY2sodyA+IDAgJiYgdyA8PSBsaW1pdHMubWF4VGV4dHVyZVNpemUsICdpbnZhbGlkIHdpZHRoJylcbiAgICAgIH1cbiAgICAgIGlmICgnaGVpZ2h0JyBpbiBvcHRpb25zKSB7XG4gICAgICAgIGggPSBvcHRpb25zLmhlaWdodFxuICAgICAgICBjaGVjayhoID4gMCAmJiBoIDw9IGxpbWl0cy5tYXhUZXh0dXJlU2l6ZSwgJ2ludmFsaWQgaGVpZ2h0JylcbiAgICAgIH1cbiAgICAgIGlmICgnY2hhbm5lbHMnIGluIG9wdGlvbnMpIHtcbiAgICAgICAgYyA9IG9wdGlvbnMuY2hhbm5lbHNcbiAgICAgICAgY2hlY2soYyA+IDAgJiYgYyA8PSA0LCAnaW52YWxpZCBudW1iZXIgb2YgY2hhbm5lbHMnKVxuICAgICAgICBoYXNDaGFubmVscyA9IHRydWVcbiAgICAgIH1cbiAgICB9XG4gICAgZmxhZ3Mud2lkdGggPSB3IHwgMFxuICAgIGZsYWdzLmhlaWdodCA9IGggfCAwXG4gICAgZmxhZ3MuY2hhbm5lbHMgPSBjIHwgMFxuXG4gICAgdmFyIGhhc0Zvcm1hdCA9IGZhbHNlXG4gICAgaWYgKCdmb3JtYXQnIGluIG9wdGlvbnMpIHtcbiAgICAgIHZhciBmb3JtYXRTdHIgPSBvcHRpb25zLmZvcm1hdFxuICAgICAgY2hlY2sucGFyYW1ldGVyKGZvcm1hdFN0ciwgdGV4dHVyZUZvcm1hdHMsXG4gICAgICAgICdpbnZhbGlkIHRleHR1cmUgZm9ybWF0JylcbiAgICAgIHZhciBpbnRlcm5hbGZvcm1hdCA9IGZsYWdzLmludGVybmFsZm9ybWF0ID0gdGV4dHVyZUZvcm1hdHNbZm9ybWF0U3RyXVxuICAgICAgZmxhZ3MuZm9ybWF0ID0gY29sb3JGb3JtYXRzW2ludGVybmFsZm9ybWF0XVxuICAgICAgaWYgKGZvcm1hdFN0ciBpbiB0ZXh0dXJlVHlwZXMpIHtcbiAgICAgICAgaWYgKCEoJ3R5cGUnIGluIG9wdGlvbnMpKSB7XG4gICAgICAgICAgZmxhZ3MudHlwZSA9IHRleHR1cmVUeXBlc1tmb3JtYXRTdHJdXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChmb3JtYXRTdHIgaW4gY29tcHJlc3NlZFRleHR1cmVGb3JtYXRzKSB7XG4gICAgICAgIGZsYWdzLmNvbXByZXNzZWQgPSB0cnVlXG4gICAgICB9XG4gICAgICBoYXNGb3JtYXQgPSB0cnVlXG4gICAgfVxuXG4gICAgLy8gUmVjb25jaWxlIGNoYW5uZWxzIGFuZCBmb3JtYXRcbiAgICBpZiAoIWhhc0NoYW5uZWxzICYmIGhhc0Zvcm1hdCkge1xuICAgICAgZmxhZ3MuY2hhbm5lbHMgPSBGT1JNQVRfQ0hBTk5FTFNbZmxhZ3MuZm9ybWF0XVxuICAgIH0gZWxzZSBpZiAoaGFzQ2hhbm5lbHMgJiYgIWhhc0Zvcm1hdCkge1xuICAgICAgaWYgKGZsYWdzLmNoYW5uZWxzICE9PSBDSEFOTkVMU19GT1JNQVRbZmxhZ3MuZm9ybWF0XSkge1xuICAgICAgICBmbGFncy5mb3JtYXQgPSBmbGFncy5pbnRlcm5hbGZvcm1hdCA9IENIQU5ORUxTX0ZPUk1BVFtmbGFncy5jaGFubmVsc11cbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKGhhc0Zvcm1hdCAmJiBoYXNDaGFubmVscykge1xuICAgICAgY2hlY2soXG4gICAgICAgIGZsYWdzLmNoYW5uZWxzID09PSBGT1JNQVRfQ0hBTk5FTFNbZmxhZ3MuZm9ybWF0XSxcbiAgICAgICAgJ251bWJlciBvZiBjaGFubmVscyBpbmNvbnNpc3RlbnQgd2l0aCBzcGVjaWZpZWQgZm9ybWF0JylcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBzZXRGbGFncyAoZmxhZ3MpIHtcbiAgICBnbC5waXhlbFN0b3JlaShHTF9VTlBBQ0tfRkxJUF9ZX1dFQkdMLCBmbGFncy5mbGlwWSlcbiAgICBnbC5waXhlbFN0b3JlaShHTF9VTlBBQ0tfUFJFTVVMVElQTFlfQUxQSEFfV0VCR0wsIGZsYWdzLnByZW11bHRpcGx5QWxwaGEpXG4gICAgZ2wucGl4ZWxTdG9yZWkoR0xfVU5QQUNLX0NPTE9SU1BBQ0VfQ09OVkVSU0lPTl9XRUJHTCwgZmxhZ3MuY29sb3JTcGFjZSlcbiAgICBnbC5waXhlbFN0b3JlaShHTF9VTlBBQ0tfQUxJR05NRU5ULCBmbGFncy51bnBhY2tBbGlnbm1lbnQpXG4gIH1cblxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gIC8vIFRleCBpbWFnZSBkYXRhXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgZnVuY3Rpb24gVGV4SW1hZ2UgKCkge1xuICAgIFRleEZsYWdzLmNhbGwodGhpcylcblxuICAgIHRoaXMueE9mZnNldCA9IDBcbiAgICB0aGlzLnlPZmZzZXQgPSAwXG5cbiAgICAvLyBkYXRhXG4gICAgdGhpcy5kYXRhID0gbnVsbFxuICAgIHRoaXMubmVlZHNGcmVlID0gZmFsc2VcblxuICAgIC8vIGh0bWwgZWxlbWVudFxuICAgIHRoaXMuZWxlbWVudCA9IG51bGxcblxuICAgIC8vIGNvcHlUZXhJbWFnZSBpbmZvXG4gICAgdGhpcy5uZWVkc0NvcHkgPSBmYWxzZVxuICB9XG5cbiAgZnVuY3Rpb24gcGFyc2VJbWFnZSAoaW1hZ2UsIG9wdGlvbnMpIHtcbiAgICB2YXIgZGF0YSA9IG51bGxcbiAgICBpZiAoaXNQaXhlbERhdGEob3B0aW9ucykpIHtcbiAgICAgIGRhdGEgPSBvcHRpb25zXG4gICAgfSBlbHNlIGlmIChvcHRpb25zKSB7XG4gICAgICBjaGVjay50eXBlKG9wdGlvbnMsICdvYmplY3QnLCAnaW52YWxpZCBwaXhlbCBkYXRhIHR5cGUnKVxuICAgICAgcGFyc2VGbGFncyhpbWFnZSwgb3B0aW9ucylcbiAgICAgIGlmICgneCcgaW4gb3B0aW9ucykge1xuICAgICAgICBpbWFnZS54T2Zmc2V0ID0gb3B0aW9ucy54IHwgMFxuICAgICAgfVxuICAgICAgaWYgKCd5JyBpbiBvcHRpb25zKSB7XG4gICAgICAgIGltYWdlLnlPZmZzZXQgPSBvcHRpb25zLnkgfCAwXG4gICAgICB9XG4gICAgICBpZiAoaXNQaXhlbERhdGEob3B0aW9ucy5kYXRhKSkge1xuICAgICAgICBkYXRhID0gb3B0aW9ucy5kYXRhXG4gICAgICB9XG4gICAgfVxuXG4gICAgY2hlY2soXG4gICAgICAhaW1hZ2UuY29tcHJlc3NlZCB8fFxuICAgICAgZGF0YSBpbnN0YW5jZW9mIFVpbnQ4QXJyYXksXG4gICAgICAnY29tcHJlc3NlZCB0ZXh0dXJlIGRhdGEgbXVzdCBiZSBzdG9yZWQgaW4gYSB1aW50OGFycmF5JylcblxuICAgIGlmIChvcHRpb25zLmNvcHkpIHtcbiAgICAgIGNoZWNrKCFkYXRhLCAnY2FuIG5vdCBzcGVjaWZ5IGNvcHkgYW5kIGRhdGEgZmllbGQgZm9yIHRoZSBzYW1lIHRleHR1cmUnKVxuICAgICAgdmFyIHZpZXdXID0gY29udGV4dFN0YXRlLnZpZXdwb3J0V2lkdGhcbiAgICAgIHZhciB2aWV3SCA9IGNvbnRleHRTdGF0ZS52aWV3cG9ydEhlaWdodFxuICAgICAgaW1hZ2Uud2lkdGggPSBpbWFnZS53aWR0aCB8fCAodmlld1cgLSBpbWFnZS54T2Zmc2V0KVxuICAgICAgaW1hZ2UuaGVpZ2h0ID0gaW1hZ2UuaGVpZ2h0IHx8ICh2aWV3SCAtIGltYWdlLnlPZmZzZXQpXG4gICAgICBpbWFnZS5uZWVkc0NvcHkgPSB0cnVlXG4gICAgICBjaGVjayhpbWFnZS54T2Zmc2V0ID49IDAgJiYgaW1hZ2UueE9mZnNldCA8IHZpZXdXICYmXG4gICAgICAgICAgICBpbWFnZS55T2Zmc2V0ID49IDAgJiYgaW1hZ2UueU9mZnNldCA8IHZpZXdIICYmXG4gICAgICAgICAgICBpbWFnZS53aWR0aCA+IDAgJiYgaW1hZ2Uud2lkdGggPD0gdmlld1cgJiZcbiAgICAgICAgICAgIGltYWdlLmhlaWdodCA+IDAgJiYgaW1hZ2UuaGVpZ2h0IDw9IHZpZXdILFxuICAgICAgICAgICAgJ2NvcHkgdGV4dHVyZSByZWFkIG91dCBvZiBib3VuZHMnKVxuICAgIH0gZWxzZSBpZiAoIWRhdGEpIHtcbiAgICAgIGltYWdlLndpZHRoID0gaW1hZ2Uud2lkdGggfHwgMVxuICAgICAgaW1hZ2UuaGVpZ2h0ID0gaW1hZ2UuaGVpZ2h0IHx8IDFcbiAgICB9IGVsc2UgaWYgKGlzVHlwZWRBcnJheShkYXRhKSkge1xuICAgICAgaW1hZ2UuZGF0YSA9IGRhdGFcbiAgICAgIGlmICghKCd0eXBlJyBpbiBvcHRpb25zKSAmJiBpbWFnZS50eXBlID09PSBHTF9VTlNJR05FRF9CWVRFKSB7XG4gICAgICAgIGltYWdlLnR5cGUgPSB0eXBlZEFycmF5Q29kZShkYXRhKVxuICAgICAgfVxuICAgIH0gZWxzZSBpZiAoaXNOdW1lcmljQXJyYXkoZGF0YSkpIHtcbiAgICAgIGNvbnZlcnREYXRhKGltYWdlLCBkYXRhKVxuICAgICAgaW1hZ2UuYWxpZ25tZW50ID0gMVxuICAgICAgaW1hZ2UubmVlZHNGcmVlID0gdHJ1ZVxuICAgIH0gZWxzZSBpZiAoaXNOREFycmF5TGlrZShkYXRhKSkge1xuICAgICAgdmFyIGFycmF5ID0gZGF0YS5kYXRhXG4gICAgICBpZiAoIUFycmF5LmlzQXJyYXkoYXJyYXkpICYmIGltYWdlLnR5cGUgPT09IEdMX1VOU0lHTkVEX0JZVEUpIHtcbiAgICAgICAgaW1hZ2UudHlwZSA9IHR5cGVkQXJyYXlDb2RlKGFycmF5KVxuICAgICAgfVxuICAgICAgdmFyIHNoYXBlID0gZGF0YS5zaGFwZVxuICAgICAgdmFyIHN0cmlkZSA9IGRhdGEuc3RyaWRlXG4gICAgICB2YXIgc2hhcGVYLCBzaGFwZVksIHNoYXBlQywgc3RyaWRlWCwgc3RyaWRlWSwgc3RyaWRlQ1xuICAgICAgaWYgKHNoYXBlLmxlbmd0aCA9PT0gMykge1xuICAgICAgICBzaGFwZUMgPSBzaGFwZVsyXVxuICAgICAgICBzdHJpZGVDID0gc3RyaWRlWzJdXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjaGVjayhzaGFwZS5sZW5ndGggPT09IDIsICdpbnZhbGlkIG5kYXJyYXkgcGl4ZWwgZGF0YSwgbXVzdCBiZSAyIG9yIDNEJylcbiAgICAgICAgc2hhcGVDID0gMVxuICAgICAgICBzdHJpZGVDID0gMVxuICAgICAgfVxuICAgICAgc2hhcGVYID0gc2hhcGVbMF1cbiAgICAgIHNoYXBlWSA9IHNoYXBlWzFdXG4gICAgICBzdHJpZGVYID0gc3RyaWRlWzBdXG4gICAgICBzdHJpZGVZID0gc3RyaWRlWzFdXG4gICAgICBpbWFnZS5hbGlnbm1lbnQgPSAxXG4gICAgICBpbWFnZS53aWR0aCA9IHNoYXBlWFxuICAgICAgaW1hZ2UuaGVpZ2h0ID0gc2hhcGVZXG4gICAgICBpbWFnZS5jaGFubmVscyA9IHNoYXBlQ1xuICAgICAgaW1hZ2UuZm9ybWF0ID0gaW1hZ2UuaW50ZXJuYWxmb3JtYXQgPSBDSEFOTkVMU19GT1JNQVRbc2hhcGVDXVxuICAgICAgaW1hZ2UubmVlZHNGcmVlID0gdHJ1ZVxuICAgICAgdHJhbnNwb3NlRGF0YShpbWFnZSwgYXJyYXksIHN0cmlkZVgsIHN0cmlkZVksIHN0cmlkZUMsIGRhdGEub2Zmc2V0KVxuICAgIH0gZWxzZSBpZiAoaXNDYW52YXNFbGVtZW50KGRhdGEpIHx8IGlzQ29udGV4dDJEKGRhdGEpKSB7XG4gICAgICBpZiAoaXNDYW52YXNFbGVtZW50KGRhdGEpKSB7XG4gICAgICAgIGltYWdlLmVsZW1lbnQgPSBkYXRhXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpbWFnZS5lbGVtZW50ID0gZGF0YS5jYW52YXNcbiAgICAgIH1cbiAgICAgIGltYWdlLndpZHRoID0gaW1hZ2UuZWxlbWVudC53aWR0aFxuICAgICAgaW1hZ2UuaGVpZ2h0ID0gaW1hZ2UuZWxlbWVudC5oZWlnaHRcbiAgICB9IGVsc2UgaWYgKGlzSW1hZ2VFbGVtZW50KGRhdGEpKSB7XG4gICAgICBpbWFnZS5lbGVtZW50ID0gZGF0YVxuICAgICAgaW1hZ2Uud2lkdGggPSBkYXRhLm5hdHVyYWxXaWR0aFxuICAgICAgaW1hZ2UuaGVpZ2h0ID0gZGF0YS5uYXR1cmFsSGVpZ2h0XG4gICAgfSBlbHNlIGlmIChpc1ZpZGVvRWxlbWVudChkYXRhKSkge1xuICAgICAgaW1hZ2UuZWxlbWVudCA9IGRhdGFcbiAgICAgIGltYWdlLndpZHRoID0gZGF0YS52aWRlb1dpZHRoXG4gICAgICBpbWFnZS5oZWlnaHQgPSBkYXRhLnZpZGVvSGVpZ2h0XG4gICAgICBpbWFnZS5uZWVkc1BvbGwgPSB0cnVlXG4gICAgfSBlbHNlIGlmIChpc1JlY3RBcnJheShkYXRhKSkge1xuICAgICAgdmFyIHcgPSBkYXRhWzBdLmxlbmd0aFxuICAgICAgdmFyIGggPSBkYXRhLmxlbmd0aFxuICAgICAgdmFyIGMgPSAxXG4gICAgICBpZiAoaXNBcnJheUxpa2UoZGF0YVswXVswXSkpIHtcbiAgICAgICAgYyA9IGRhdGFbMF1bMF0ubGVuZ3RoXG4gICAgICAgIGZsYXR0ZW4zRERhdGEoaW1hZ2UsIGRhdGEsIHcsIGgsIGMpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBmbGF0dGVuMkREYXRhKGltYWdlLCBkYXRhLCB3LCBoKVxuICAgICAgfVxuICAgICAgaW1hZ2UuYWxpZ25tZW50ID0gMVxuICAgICAgaW1hZ2Uud2lkdGggPSB3XG4gICAgICBpbWFnZS5oZWlnaHQgPSBoXG4gICAgICBpbWFnZS5jaGFubmVscyA9IGNcbiAgICAgIGltYWdlLmZvcm1hdCA9IGltYWdlLmludGVybmFsZm9ybWF0ID0gQ0hBTk5FTFNfRk9STUFUW2NdXG4gICAgICBpbWFnZS5uZWVkc0ZyZWUgPSB0cnVlXG4gICAgfVxuXG4gICAgaWYgKGltYWdlLnR5cGUgPT09IEdMX0ZMT0FUKSB7XG4gICAgICBjaGVjayhsaW1pdHMuZXh0ZW5zaW9ucy5pbmRleE9mKCdvZXNfdGV4dHVyZV9mbG9hdCcpID49IDAsXG4gICAgICAgICdvZXNfdGV4dHVyZV9mbG9hdCBleHRlbnNpb24gbm90IGVuYWJsZWQnKVxuICAgIH0gZWxzZSBpZiAoaW1hZ2UudHlwZSA9PT0gR0xfSEFMRl9GTE9BVF9PRVMpIHtcbiAgICAgIGNoZWNrKGxpbWl0cy5leHRlbnNpb25zLmluZGV4T2YoJ29lc190ZXh0dXJlX2hhbGZfZmxvYXQnKSA+PSAwLFxuICAgICAgICAnb2VzX3RleHR1cmVfaGFsZl9mbG9hdCBleHRlbnNpb24gbm90IGVuYWJsZWQnKVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHNldEltYWdlIChpbmZvLCB0YXJnZXQsIG1pcGxldmVsKSB7XG4gICAgdmFyIGVsZW1lbnQgPSBpbmZvLmVsZW1lbnRcbiAgICB2YXIgZGF0YSA9IGluZm8uZGF0YVxuICAgIHZhciBpbnRlcm5hbGZvcm1hdCA9IGluZm8uaW50ZXJuYWxmb3JtYXRcbiAgICB2YXIgZm9ybWF0ID0gaW5mby5mb3JtYXRcbiAgICB2YXIgdHlwZSA9IGluZm8udHlwZVxuICAgIHZhciB3aWR0aCA9IGluZm8ud2lkdGhcbiAgICB2YXIgaGVpZ2h0ID0gaW5mby5oZWlnaHRcblxuICAgIHNldEZsYWdzKGluZm8pXG5cbiAgICBpZiAoZWxlbWVudCkge1xuICAgICAgZ2wudGV4SW1hZ2UyRCh0YXJnZXQsIG1pcGxldmVsLCBmb3JtYXQsIGZvcm1hdCwgdHlwZSwgZWxlbWVudClcbiAgICB9IGVsc2UgaWYgKGluZm8uY29tcHJlc3NlZCkge1xuICAgICAgZ2wuY29tcHJlc3NlZFRleEltYWdlMkQodGFyZ2V0LCBtaXBsZXZlbCwgaW50ZXJuYWxmb3JtYXQsIHdpZHRoLCBoZWlnaHQsIDAsIGRhdGEpXG4gICAgfSBlbHNlIGlmIChpbmZvLm5lZWRzQ29weSkge1xuICAgICAgcmVnbFBvbGwoKVxuICAgICAgZ2wuY29weVRleEltYWdlMkQoXG4gICAgICAgIHRhcmdldCwgbWlwbGV2ZWwsIGZvcm1hdCwgaW5mby54T2Zmc2V0LCBpbmZvLnlPZmZzZXQsIHdpZHRoLCBoZWlnaHQsIDApXG4gICAgfSBlbHNlIHtcbiAgICAgIGdsLnRleEltYWdlMkQoXG4gICAgICAgIHRhcmdldCwgbWlwbGV2ZWwsIGZvcm1hdCwgd2lkdGgsIGhlaWdodCwgMCwgZm9ybWF0LCB0eXBlLCBkYXRhKVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHNldFN1YkltYWdlIChpbmZvLCB0YXJnZXQsIHgsIHksIG1pcGxldmVsKSB7XG4gICAgdmFyIGVsZW1lbnQgPSBpbmZvLmVsZW1lbnRcbiAgICB2YXIgZGF0YSA9IGluZm8uZGF0YVxuICAgIHZhciBpbnRlcm5hbGZvcm1hdCA9IGluZm8uaW50ZXJuYWxmb3JtYXRcbiAgICB2YXIgZm9ybWF0ID0gaW5mby5mb3JtYXRcbiAgICB2YXIgdHlwZSA9IGluZm8udHlwZVxuICAgIHZhciB3aWR0aCA9IGluZm8ud2lkdGhcbiAgICB2YXIgaGVpZ2h0ID0gaW5mby5oZWlnaHRcblxuICAgIHNldEZsYWdzKGluZm8pXG5cbiAgICBpZiAoZWxlbWVudCkge1xuICAgICAgZ2wudGV4U3ViSW1hZ2UyRChcbiAgICAgICAgdGFyZ2V0LCBtaXBsZXZlbCwgeCwgeSwgZm9ybWF0LCB0eXBlLCBlbGVtZW50KVxuICAgIH0gZWxzZSBpZiAoaW5mby5jb21wcmVzc2VkKSB7XG4gICAgICBnbC5jb21wcmVzc2VkVGV4U3ViSW1hZ2UyRChcbiAgICAgICAgdGFyZ2V0LCBtaXBsZXZlbCwgeCwgeSwgaW50ZXJuYWxmb3JtYXQsIHdpZHRoLCBoZWlnaHQsIGRhdGEpXG4gICAgfSBlbHNlIGlmIChpbmZvLm5lZWRzQ29weSkge1xuICAgICAgcmVnbFBvbGwoKVxuICAgICAgZ2wuY29weVRleFN1YkltYWdlMkQoXG4gICAgICAgIHRhcmdldCwgbWlwbGV2ZWwsIHgsIHksIGluZm8ueE9mZnNldCwgaW5mby55T2Zmc2V0LCB3aWR0aCwgaGVpZ2h0KVxuICAgIH0gZWxzZSB7XG4gICAgICBnbC50ZXhTdWJJbWFnZTJEKFxuICAgICAgICB0YXJnZXQsIG1pcGxldmVsLCB4LCB5LCB3aWR0aCwgaGVpZ2h0LCBmb3JtYXQsIHR5cGUsIGRhdGEpXG4gICAgfVxuICB9XG5cbiAgLy8gdGV4SW1hZ2UgcG9vbFxuICB2YXIgaW1hZ2VQb29sID0gW11cblxuICBmdW5jdGlvbiBhbGxvY0ltYWdlICgpIHtcbiAgICByZXR1cm4gaW1hZ2VQb29sLnBvcCgpIHx8IG5ldyBUZXhJbWFnZSgpXG4gIH1cblxuICBmdW5jdGlvbiBmcmVlSW1hZ2UgKGltYWdlKSB7XG4gICAgaWYgKGltYWdlLm5lZWRzRnJlZSkge1xuICAgICAgcG9vbC5mcmVlVHlwZShpbWFnZS5kYXRhKVxuICAgIH1cbiAgICBUZXhJbWFnZS5jYWxsKGltYWdlKVxuICAgIGltYWdlUG9vbC5wdXNoKGltYWdlKVxuICB9XG5cbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAvLyBNaXAgbWFwXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgZnVuY3Rpb24gTWlwTWFwICgpIHtcbiAgICBUZXhGbGFncy5jYWxsKHRoaXMpXG5cbiAgICB0aGlzLmdlbk1pcG1hcHMgPSBmYWxzZVxuICAgIHRoaXMubWlwbWFwSGludCA9IEdMX0RPTlRfQ0FSRVxuICAgIHRoaXMubWlwbWFzayA9IDBcbiAgICB0aGlzLmltYWdlcyA9IEFycmF5KDE2KVxuICB9XG5cbiAgZnVuY3Rpb24gcGFyc2VNaXBNYXBGcm9tU2hhcGUgKG1pcG1hcCwgd2lkdGgsIGhlaWdodCkge1xuICAgIHZhciBpbWcgPSBtaXBtYXAuaW1hZ2VzWzBdID0gYWxsb2NJbWFnZSgpXG4gICAgbWlwbWFwLm1pcG1hc2sgPSAxXG4gICAgaW1nLndpZHRoID0gbWlwbWFwLndpZHRoID0gd2lkdGhcbiAgICBpbWcuaGVpZ2h0ID0gbWlwbWFwLmhlaWdodCA9IGhlaWdodFxuICB9XG5cbiAgZnVuY3Rpb24gcGFyc2VNaXBNYXBGcm9tT2JqZWN0IChtaXBtYXAsIG9wdGlvbnMpIHtcbiAgICB2YXIgaW1nRGF0YSA9IG51bGxcbiAgICBpZiAoaXNQaXhlbERhdGEob3B0aW9ucykpIHtcbiAgICAgIGltZ0RhdGEgPSBtaXBtYXAuaW1hZ2VzWzBdID0gYWxsb2NJbWFnZSgpXG4gICAgICBjb3B5RmxhZ3MoaW1nRGF0YSwgbWlwbWFwKVxuICAgICAgcGFyc2VJbWFnZShpbWdEYXRhLCBvcHRpb25zKVxuICAgICAgbWlwbWFwLm1pcG1hc2sgPSAxXG4gICAgfSBlbHNlIHtcbiAgICAgIHBhcnNlRmxhZ3MobWlwbWFwLCBvcHRpb25zKVxuICAgICAgaWYgKEFycmF5LmlzQXJyYXkob3B0aW9ucy5taXBtYXApKSB7XG4gICAgICAgIHZhciBtaXBEYXRhID0gb3B0aW9ucy5taXBtYXBcbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBtaXBEYXRhLmxlbmd0aDsgKytpKSB7XG4gICAgICAgICAgaW1nRGF0YSA9IG1pcG1hcC5pbWFnZXNbaV0gPSBhbGxvY0ltYWdlKClcbiAgICAgICAgICBjb3B5RmxhZ3MoaW1nRGF0YSwgbWlwbWFwKVxuICAgICAgICAgIGltZ0RhdGEud2lkdGggPj49IGlcbiAgICAgICAgICBpbWdEYXRhLmhlaWdodCA+Pj0gaVxuICAgICAgICAgIHBhcnNlSW1hZ2UoaW1nRGF0YSwgbWlwRGF0YVtpXSlcbiAgICAgICAgICBtaXBtYXAubWlwbWFzayB8PSAoMSA8PCBpKVxuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpbWdEYXRhID0gbWlwbWFwLmltYWdlc1swXSA9IGFsbG9jSW1hZ2UoKVxuICAgICAgICBjb3B5RmxhZ3MoaW1nRGF0YSwgbWlwbWFwKVxuICAgICAgICBwYXJzZUltYWdlKGltZ0RhdGEsIG9wdGlvbnMpXG4gICAgICAgIG1pcG1hcC5taXBtYXNrID0gMVxuICAgICAgfVxuICAgIH1cbiAgICBjb3B5RmxhZ3MobWlwbWFwLCBtaXBtYXAuaW1hZ2VzWzBdKVxuICB9XG5cbiAgZnVuY3Rpb24gc2V0TWlwTWFwIChtaXBtYXAsIHRhcmdldCkge1xuICAgIHZhciBpbWFnZXMgPSBtaXBtYXAuaW1hZ2VzXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBpbWFnZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGlmICghaW1hZ2VzW2ldKSB7XG4gICAgICAgIHJldHVyblxuICAgICAgfVxuICAgICAgc2V0SW1hZ2UoaW1hZ2VzW2ldLCB0YXJnZXQsIGkpXG4gICAgfVxuICB9XG5cbiAgdmFyIG1pcFBvb2wgPSBbXVxuXG4gIGZ1bmN0aW9uIGFsbG9jTWlwTWFwICgpIHtcbiAgICB2YXIgcmVzdWx0ID0gbWlwUG9vbC5wb3AoKSB8fCBuZXcgTWlwTWFwKClcbiAgICBUZXhGbGFncy5jYWxsKHJlc3VsdClcbiAgICByZXN1bHQubWlwbWFzayA9IDBcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IDE2OyArK2kpIHtcbiAgICAgIHJlc3VsdC5pbWFnZXNbaV0gPSBudWxsXG4gICAgfVxuICAgIHJldHVybiByZXN1bHRcbiAgfVxuXG4gIGZ1bmN0aW9uIGZyZWVNaXBNYXAgKG1pcG1hcCkge1xuICAgIHZhciBpbWFnZXMgPSBtaXBtYXAuaW1hZ2VzXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBpbWFnZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGlmIChpbWFnZXNbaV0pIHtcbiAgICAgICAgZnJlZUltYWdlKGltYWdlc1tpXSlcbiAgICAgIH1cbiAgICAgIGltYWdlc1tpXSA9IG51bGxcbiAgICB9XG4gICAgbWlwUG9vbC5wdXNoKG1pcG1hcClcbiAgfVxuXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgLy8gVGV4IGluZm9cbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICBmdW5jdGlvbiBUZXhJbmZvICgpIHtcbiAgICB0aGlzLm1pbkZpbHRlciA9IEdMX05FQVJFU1RcbiAgICB0aGlzLm1hZ0ZpbHRlciA9IEdMX05FQVJFU1RcblxuICAgIHRoaXMud3JhcFMgPSBHTF9DTEFNUF9UT19FREdFXG4gICAgdGhpcy53cmFwVCA9IEdMX0NMQU1QX1RPX0VER0VcblxuICAgIHRoaXMuYW5pc290cm9waWMgPSAxXG5cbiAgICB0aGlzLmdlbk1pcG1hcHMgPSBmYWxzZVxuICAgIHRoaXMubWlwbWFwSGludCA9IEdMX0RPTlRfQ0FSRVxuICB9XG5cbiAgZnVuY3Rpb24gcGFyc2VUZXhJbmZvIChpbmZvLCBvcHRpb25zKSB7XG4gICAgaWYgKCdtaW4nIGluIG9wdGlvbnMpIHtcbiAgICAgIHZhciBtaW5GaWx0ZXIgPSBvcHRpb25zLm1pblxuICAgICAgY2hlY2sucGFyYW1ldGVyKG1pbkZpbHRlciwgbWluRmlsdGVycylcbiAgICAgIGluZm8ubWluRmlsdGVyID0gbWluRmlsdGVyc1ttaW5GaWx0ZXJdXG4gICAgICBpZiAoTUlQTUFQX0ZJTFRFUlMuaW5kZXhPZihpbmZvLm1pbkZpbHRlcikgPj0gMCkge1xuICAgICAgICBpbmZvLmdlbk1pcG1hcHMgPSB0cnVlXG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKCdtYWcnIGluIG9wdGlvbnMpIHtcbiAgICAgIHZhciBtYWdGaWx0ZXIgPSBvcHRpb25zLm1hZ1xuICAgICAgY2hlY2sucGFyYW1ldGVyKG1hZ0ZpbHRlciwgbWFnRmlsdGVycylcbiAgICAgIGluZm8ubWFnRmlsdGVyID0gbWFnRmlsdGVyc1ttYWdGaWx0ZXJdXG4gICAgfVxuXG4gICAgdmFyIHdyYXBTID0gaW5mby53cmFwU1xuICAgIHZhciB3cmFwVCA9IGluZm8ud3JhcFRcbiAgICBpZiAoJ3dyYXAnIGluIG9wdGlvbnMpIHtcbiAgICAgIHZhciB3cmFwID0gb3B0aW9ucy53cmFwXG4gICAgICBpZiAodHlwZW9mIHdyYXAgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgIGNoZWNrLnBhcmFtZXRlcih3cmFwLCB3cmFwTW9kZXMpXG4gICAgICAgIHdyYXBTID0gd3JhcFQgPSB3cmFwTW9kZXNbd3JhcF1cbiAgICAgIH0gZWxzZSBpZiAoQXJyYXkuaXNBcnJheSh3cmFwKSkge1xuICAgICAgICBjaGVjay5wYXJhbWV0ZXIod3JhcFswXSwgd3JhcE1vZGVzKVxuICAgICAgICBjaGVjay5wYXJhbWV0ZXIod3JhcFsxXSwgd3JhcE1vZGVzKVxuICAgICAgICB3cmFwUyA9IHdyYXBNb2Rlc1t3cmFwWzBdXVxuICAgICAgICB3cmFwVCA9IHdyYXBNb2Rlc1t3cmFwWzFdXVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBpZiAoJ3dyYXBTJyBpbiBvcHRpb25zKSB7XG4gICAgICAgIHZhciBvcHRXcmFwUyA9IG9wdGlvbnMud3JhcFNcbiAgICAgICAgY2hlY2sucGFyYW1ldGVyKG9wdFdyYXBTLCB3cmFwTW9kZXMpXG4gICAgICAgIHdyYXBTID0gd3JhcE1vZGVzW29wdFdyYXBTXVxuICAgICAgfVxuICAgICAgaWYgKCd3cmFwVCcgaW4gb3B0aW9ucykge1xuICAgICAgICB2YXIgb3B0V3JhcFQgPSBvcHRpb25zLndyYXBUXG4gICAgICAgIGNoZWNrLnBhcmFtZXRlcihvcHRXcmFwVCwgd3JhcE1vZGVzKVxuICAgICAgICB3cmFwVCA9IHdyYXBNb2Rlc1tvcHRXcmFwVF1cbiAgICAgIH1cbiAgICB9XG4gICAgaW5mby53cmFwUyA9IHdyYXBTXG4gICAgaW5mby53cmFwVCA9IHdyYXBUXG5cbiAgICBpZiAoJ2FuaXNvdHJvcGljJyBpbiBvcHRpb25zKSB7XG4gICAgICB2YXIgYW5pc290cm9waWMgPSBvcHRpb25zLmFuaXNvdHJvcGljXG4gICAgICBjaGVjayh0eXBlb2YgYW5pc290cm9waWMgPT09ICdudW1iZXInICYmXG4gICAgICAgICBhbmlzb3Ryb3BpYyA+PSAxICYmIGFuaXNvdHJvcGljIDw9IGxpbWl0cy5tYXhBbmlzb3Ryb3BpYyxcbiAgICAgICAgJ2FuaXNvIHNhbXBsZXMgbXVzdCBiZSBiZXR3ZWVuIDEgYW5kICcpXG4gICAgICBpbmZvLmFuaXNvdHJvcGljID0gb3B0aW9ucy5hbmlzb3Ryb3BpY1xuICAgIH1cblxuICAgIGlmICgnbWlwbWFwJyBpbiBvcHRpb25zKSB7XG4gICAgICB2YXIgaGFzTWlwTWFwID0gZmFsc2VcbiAgICAgIHN3aXRjaCAodHlwZW9mIG9wdGlvbnMubWlwbWFwKSB7XG4gICAgICAgIGNhc2UgJ3N0cmluZyc6XG4gICAgICAgICAgY2hlY2sucGFyYW1ldGVyKG9wdGlvbnMubWlwbWFwLCBtaXBtYXBIaW50LFxuICAgICAgICAgICAgJ2ludmFsaWQgbWlwbWFwIGhpbnQnKVxuICAgICAgICAgIGluZm8ubWlwbWFwSGludCA9IG1pcG1hcEhpbnRbb3B0aW9ucy5taXBtYXBdXG4gICAgICAgICAgaW5mby5nZW5NaXBtYXBzID0gdHJ1ZVxuICAgICAgICAgIGhhc01pcE1hcCA9IHRydWVcbiAgICAgICAgICBicmVha1xuXG4gICAgICAgIGNhc2UgJ2Jvb2xlYW4nOlxuICAgICAgICAgIGhhc01pcE1hcCA9IGluZm8uZ2VuTWlwbWFwcyA9IG9wdGlvbnMubWlwbWFwXG4gICAgICAgICAgYnJlYWtcblxuICAgICAgICBjYXNlICdvYmplY3QnOlxuICAgICAgICAgIGNoZWNrKEFycmF5LmlzQXJyYXkob3B0aW9ucy5taXBtYXApLCAnaW52YWxpZCBtaXBtYXAgdHlwZScpXG4gICAgICAgICAgaW5mby5nZW5NaXBtYXBzID0gZmFsc2VcbiAgICAgICAgICBoYXNNaXBNYXAgPSB0cnVlXG4gICAgICAgICAgYnJlYWtcblxuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIGNoZWNrLnJhaXNlKCdpbnZhbGlkIG1pcG1hcCB0eXBlJylcbiAgICAgIH1cbiAgICAgIGlmIChoYXNNaXBNYXAgJiYgISgnbWluJyBpbiBvcHRpb25zKSkge1xuICAgICAgICBpbmZvLm1pbkZpbHRlciA9IEdMX05FQVJFU1RfTUlQTUFQX05FQVJFU1RcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBzZXRUZXhJbmZvIChpbmZvLCB0YXJnZXQpIHtcbiAgICBnbC50ZXhQYXJhbWV0ZXJpKHRhcmdldCwgR0xfVEVYVFVSRV9NSU5fRklMVEVSLCBpbmZvLm1pbkZpbHRlcilcbiAgICBnbC50ZXhQYXJhbWV0ZXJpKHRhcmdldCwgR0xfVEVYVFVSRV9NQUdfRklMVEVSLCBpbmZvLm1hZ0ZpbHRlcilcbiAgICBnbC50ZXhQYXJhbWV0ZXJpKHRhcmdldCwgR0xfVEVYVFVSRV9XUkFQX1MsIGluZm8ud3JhcFMpXG4gICAgZ2wudGV4UGFyYW1ldGVyaSh0YXJnZXQsIEdMX1RFWFRVUkVfV1JBUF9ULCBpbmZvLndyYXBUKVxuICAgIGlmIChleHRlbnNpb25zLmV4dF90ZXh0dXJlX2ZpbHRlcl9hbmlzb3Ryb3BpYykge1xuICAgICAgZ2wudGV4UGFyYW1ldGVyaSh0YXJnZXQsIEdMX1RFWFRVUkVfTUFYX0FOSVNPVFJPUFlfRVhULCBpbmZvLmFuaXNvdHJvcGljKVxuICAgIH1cbiAgICBpZiAoaW5mby5nZW5NaXBtYXBzKSB7XG4gICAgICBnbC5oaW50KEdMX0dFTkVSQVRFX01JUE1BUF9ISU5ULCBpbmZvLm1pcG1hcEhpbnQpXG4gICAgICBnbC5nZW5lcmF0ZU1pcG1hcCh0YXJnZXQpXG4gICAgfVxuICB9XG5cbiAgdmFyIGluZm9Qb29sID0gW11cblxuICBmdW5jdGlvbiBhbGxvY0luZm8gKCkge1xuICAgIHZhciByZXN1bHQgPSBpbmZvUG9vbC5wb3AoKSB8fCBuZXcgVGV4SW5mbygpXG4gICAgVGV4SW5mby5jYWxsKHJlc3VsdClcbiAgICByZXR1cm4gcmVzdWx0XG4gIH1cblxuICBmdW5jdGlvbiBmcmVlSW5mbyAoaW5mbykge1xuICAgIGluZm9Qb29sLnB1c2goaW5mbylcbiAgfVxuXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgLy8gRnVsbCB0ZXh0dXJlIG9iamVjdFxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gIHZhciB0ZXh0dXJlQ291bnQgPSAwXG4gIHZhciB0ZXh0dXJlU2V0ID0ge31cbiAgdmFyIG51bVRleFVuaXRzID0gbGltaXRzLm1heFRleHR1cmVVbml0c1xuICB2YXIgdGV4dHVyZVVuaXRzID0gQXJyYXkobnVtVGV4VW5pdHMpLm1hcChmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfSlcblxuICBmdW5jdGlvbiBSRUdMVGV4dHVyZSAodGFyZ2V0KSB7XG4gICAgVGV4RmxhZ3MuY2FsbCh0aGlzKVxuICAgIHRoaXMubWlwbWFzayA9IDBcbiAgICB0aGlzLmludGVybmFsZm9ybWF0ID0gR0xfUkdCQVxuXG4gICAgdGhpcy5pZCA9IHRleHR1cmVDb3VudCsrXG5cbiAgICB0aGlzLnJlZkNvdW50ID0gMVxuXG4gICAgdGhpcy50YXJnZXQgPSB0YXJnZXRcbiAgICB0aGlzLnRleHR1cmUgPSBnbC5jcmVhdGVUZXh0dXJlKClcblxuICAgIHRoaXMudW5pdCA9IC0xXG4gICAgdGhpcy5iaW5kQ291bnQgPSAwXG4gIH1cblxuICBmdW5jdGlvbiB0ZW1wQmluZCAodGV4dHVyZSkge1xuICAgIGdsLmFjdGl2ZVRleHR1cmUoR0xfVEVYVFVSRTApXG4gICAgZ2wuYmluZFRleHR1cmUodGV4dHVyZS50YXJnZXQsIHRleHR1cmUudGV4dHVyZSlcbiAgfVxuXG4gIGZ1bmN0aW9uIHRlbXBSZXN0b3JlICgpIHtcbiAgICB2YXIgcHJldiA9IHRleHR1cmVVbml0c1swXVxuICAgIGlmIChwcmV2KSB7XG4gICAgICBnbC5iaW5kVGV4dHVyZShwcmV2LnRhcmdldCwgcHJldi50ZXh0dXJlKVxuICAgIH0gZWxzZSB7XG4gICAgICBnbC5iaW5kVGV4dHVyZShHTF9URVhUVVJFXzJELCBudWxsKVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGRlc3Ryb3kgKHRleHR1cmUpIHtcbiAgICB2YXIgaGFuZGxlID0gdGV4dHVyZS50ZXh0dXJlXG4gICAgY2hlY2soaGFuZGxlLCAnbXVzdCBub3QgZG91YmxlIGRlc3Ryb3kgdGV4dHVyZScpXG4gICAgdmFyIHVuaXQgPSB0ZXh0dXJlLnVuaXRcbiAgICB2YXIgdGFyZ2V0ID0gdGV4dHVyZS50YXJnZXRcbiAgICBpZiAodW5pdCA+PSAwKSB7XG4gICAgICBnbC5hY3RpdmVUZXh0dXJlKEdMX1RFWFRVUkUwICsgdW5pdClcbiAgICAgIGdsLmJpbmRUZXh0dXJlKHRhcmdldCwgbnVsbClcbiAgICAgIHRleHR1cmVVbml0c1t1bml0XSA9IG51bGxcbiAgICB9XG4gICAgZ2wuZGVsZXRlVGV4dHVyZShoYW5kbGUpXG4gICAgdGV4dHVyZS50ZXh0dXJlID0gbnVsbFxuICAgIHRleHR1cmUucGFyYW1zID0gbnVsbFxuICAgIHRleHR1cmUucGl4ZWxzID0gbnVsbFxuICAgIHRleHR1cmUucmVmQ291bnQgPSAwXG4gICAgZGVsZXRlIHRleHR1cmVTZXRbdGV4dHVyZS5pZF1cbiAgICBzdGF0cy50ZXh0dXJlQ291bnQtLVxuICB9XG5cbiAgZXh0ZW5kKFJFR0xUZXh0dXJlLnByb3RvdHlwZSwge1xuICAgIGJpbmQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgIHZhciB0ZXh0dXJlID0gdGhpc1xuICAgICAgdGV4dHVyZS5iaW5kQ291bnQgKz0gMVxuICAgICAgdmFyIHVuaXQgPSB0ZXh0dXJlLnVuaXRcbiAgICAgIGlmICh1bml0IDwgMCkge1xuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IG51bVRleFVuaXRzOyArK2kpIHtcbiAgICAgICAgICB2YXIgb3RoZXIgPSB0ZXh0dXJlVW5pdHNbaV1cbiAgICAgICAgICBpZiAob3RoZXIpIHtcbiAgICAgICAgICAgIGlmIChvdGhlci5iaW5kQ291bnQgPiAwKSB7XG4gICAgICAgICAgICAgIGNvbnRpbnVlXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBvdGhlci51bml0ID0gLTFcbiAgICAgICAgICB9XG4gICAgICAgICAgdGV4dHVyZVVuaXRzW2ldID0gdGV4dHVyZVxuICAgICAgICAgIHVuaXQgPSBpXG4gICAgICAgICAgYnJlYWtcbiAgICAgICAgfVxuICAgICAgICBpZiAodW5pdCA+PSBudW1UZXhVbml0cykge1xuICAgICAgICAgIGNoZWNrLnJhaXNlKCdpbnN1ZmZpY2llbnQgbnVtYmVyIG9mIHRleHR1cmUgdW5pdHMnKVxuICAgICAgICB9XG4gICAgICAgIHRleHR1cmUudW5pdCA9IHVuaXRcbiAgICAgICAgZ2wuYWN0aXZlVGV4dHVyZShHTF9URVhUVVJFMCArIHVuaXQpXG4gICAgICAgIGdsLmJpbmRUZXh0dXJlKHRleHR1cmUudGFyZ2V0LCB0ZXh0dXJlLnRleHR1cmUpXG4gICAgICB9XG4gICAgICByZXR1cm4gdW5pdFxuICAgIH0sXG5cbiAgICB1bmJpbmQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgIHRoaXMuYmluZENvdW50IC09IDFcbiAgICB9LFxuXG4gICAgZGVjUmVmOiBmdW5jdGlvbiAoKSB7XG4gICAgICBpZiAoLS10aGlzLnJlZkNvdW50IDw9IDApIHtcbiAgICAgICAgZGVzdHJveSh0aGlzKVxuICAgICAgfVxuICAgIH1cbiAgfSlcblxuICBmdW5jdGlvbiBjcmVhdGVUZXh0dXJlMkQgKGEsIGIpIHtcbiAgICB2YXIgdGV4dHVyZSA9IG5ldyBSRUdMVGV4dHVyZShHTF9URVhUVVJFXzJEKVxuICAgIHRleHR1cmVTZXRbdGV4dHVyZS5pZF0gPSB0ZXh0dXJlXG4gICAgc3RhdHMudGV4dHVyZUNvdW50KytcblxuICAgIGZ1bmN0aW9uIHJlZ2xUZXh0dXJlMkQgKGEsIGIpIHtcbiAgICAgIHZhciB0ZXhJbmZvID0gYWxsb2NJbmZvKClcbiAgICAgIHZhciBtaXBEYXRhID0gYWxsb2NNaXBNYXAoKVxuXG4gICAgICBpZiAodHlwZW9mIGEgPT09ICdudW1iZXInKSB7XG4gICAgICAgIGlmICh0eXBlb2YgYiA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgICBwYXJzZU1pcE1hcEZyb21TaGFwZShtaXBEYXRhLCBhIHwgMCwgYiB8IDApXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcGFyc2VNaXBNYXBGcm9tU2hhcGUobWlwRGF0YSwgYSB8IDAsIGEgfCAwKVxuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGEpIHtcbiAgICAgICAgY2hlY2sudHlwZShhLCAnb2JqZWN0JywgJ2ludmFsaWQgYXJndW1lbnRzIHRvIHJlZ2wudGV4dHVyZScpXG4gICAgICAgIHBhcnNlVGV4SW5mbyh0ZXhJbmZvLCBhKVxuICAgICAgICBwYXJzZU1pcE1hcEZyb21PYmplY3QobWlwRGF0YSwgYSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIGVtcHR5IHRleHR1cmVzIGdldCBhc3NpZ25lZCBhIGRlZmF1bHQgc2hhcGUgb2YgMXgxXG4gICAgICAgIHBhcnNlTWlwTWFwRnJvbVNoYXBlKG1pcERhdGEsIDEsIDEpXG4gICAgICB9XG5cbiAgICAgIGlmICh0ZXhJbmZvLmdlbk1pcG1hcHMpIHtcbiAgICAgICAgbWlwRGF0YS5taXBtYXNrID0gKG1pcERhdGEud2lkdGggPDwgMSkgLSAxXG4gICAgICB9XG4gICAgICB0ZXh0dXJlLm1pcG1hc2sgPSBtaXBEYXRhLm1pcG1hc2tcblxuICAgICAgY29weUZsYWdzKHRleHR1cmUsIG1pcERhdGEpXG5cbiAgICAgIGNoZWNrLnRleHR1cmUyRCh0ZXhJbmZvLCBtaXBEYXRhLCBsaW1pdHMpXG4gICAgICB0ZXh0dXJlLmludGVybmFsZm9ybWF0ID0gbWlwRGF0YS5pbnRlcm5hbGZvcm1hdFxuXG4gICAgICByZWdsVGV4dHVyZTJELndpZHRoID0gbWlwRGF0YS53aWR0aFxuICAgICAgcmVnbFRleHR1cmUyRC5oZWlnaHQgPSBtaXBEYXRhLmhlaWdodFxuXG4gICAgICB0ZW1wQmluZCh0ZXh0dXJlKVxuICAgICAgc2V0TWlwTWFwKG1pcERhdGEsIEdMX1RFWFRVUkVfMkQpXG4gICAgICBzZXRUZXhJbmZvKHRleEluZm8sIEdMX1RFWFRVUkVfMkQpXG4gICAgICB0ZW1wUmVzdG9yZSgpXG5cbiAgICAgIGZyZWVJbmZvKHRleEluZm8pXG4gICAgICBmcmVlTWlwTWFwKG1pcERhdGEpXG5cbiAgICAgIHJldHVybiByZWdsVGV4dHVyZTJEXG4gICAgfVxuXG4gICAgZnVuY3Rpb24gc3ViaW1hZ2UgKGltYWdlLCB4XywgeV8sIGxldmVsXykge1xuICAgICAgY2hlY2soISFpbWFnZSwgJ211c3Qgc3BlY2lmeSBpbWFnZSBkYXRhJylcblxuICAgICAgdmFyIHggPSB4XyB8IDBcbiAgICAgIHZhciB5ID0geV8gfCAwXG4gICAgICB2YXIgbGV2ZWwgPSBsZXZlbF8gfCAwXG5cbiAgICAgIHZhciBpbWFnZURhdGEgPSBhbGxvY0ltYWdlKClcbiAgICAgIGNvcHlGbGFncyhpbWFnZURhdGEsIHRleHR1cmUpXG4gICAgICBpbWFnZURhdGEud2lkdGggPj49IGxldmVsXG4gICAgICBpbWFnZURhdGEuaGVpZ2h0ID4+PSBsZXZlbFxuICAgICAgaW1hZ2VEYXRhLndpZHRoIC09IHhcbiAgICAgIGltYWdlRGF0YS5oZWlnaHQgLT0geVxuICAgICAgcGFyc2VJbWFnZShpbWFnZURhdGEsIGltYWdlKVxuXG4gICAgICBjaGVjayhcbiAgICAgICAgdGV4dHVyZS50eXBlID09PSBpbWFnZURhdGEudHlwZSAmJlxuICAgICAgICB0ZXh0dXJlLmZvcm1hdCA9PT0gaW1hZ2VEYXRhLmZvcm1hdCAmJlxuICAgICAgICB0ZXh0dXJlLmludGVybmFsZm9ybWF0ID09PSBpbWFnZURhdGEuaW50ZXJuYWxmb3JtYXQsXG4gICAgICAgICdpbmNvbXBhdGlibGUgZm9ybWF0IGZvciB0ZXh0dXJlLnN1YmltYWdlJylcbiAgICAgIGNoZWNrKFxuICAgICAgICB4ID49IDAgJiYgeSA+PSAwICYmXG4gICAgICAgIHggKyBpbWFnZURhdGEud2lkdGggPD0gdGV4dHVyZS53aWR0aCAmJlxuICAgICAgICB5ICsgaW1hZ2VEYXRhLmhlaWdodCA8PSB0ZXh0dXJlLmhlaWdodCxcbiAgICAgICAgJ3RleHR1cmUuc3ViaW1hZ2Ugd3JpdGUgb3V0IG9mIGJvdW5kcycpXG4gICAgICBjaGVjayhcbiAgICAgICAgdGV4dHVyZS5taXBtYXNrICYgKDEgPDwgbGV2ZWwpLFxuICAgICAgICAnbWlzc2luZyBtaXBtYXAgZGF0YScpXG4gICAgICBjaGVjayhcbiAgICAgICAgaW1hZ2VEYXRhLmRhdGEgfHwgaW1hZ2VEYXRhLmVsZW1lbnQgfHwgaW1hZ2VEYXRhLm5lZWRzQ29weSxcbiAgICAgICAgJ21pc3NpbmcgaW1hZ2UgZGF0YScpXG5cbiAgICAgIHRlbXBCaW5kKHRleHR1cmUpXG4gICAgICBzZXRTdWJJbWFnZShpbWFnZURhdGEsIEdMX1RFWFRVUkVfMkQsIHgsIHksIGxldmVsKVxuICAgICAgdGVtcFJlc3RvcmUoKVxuXG4gICAgICBmcmVlSW1hZ2UoaW1hZ2VEYXRhKVxuXG4gICAgICByZXR1cm4gcmVnbFRleHR1cmUyRFxuICAgIH1cblxuICAgIGZ1bmN0aW9uIHJlc2l6ZSAod18sIGhfKSB7XG4gICAgICB2YXIgdyA9IHdfIHwgMFxuICAgICAgdmFyIGggPSAoaF8gfCAwKSB8fCB3XG4gICAgICBpZiAodyA9PT0gdGV4dHVyZS53aWR0aCAmJiBoID09PSB0ZXh0dXJlLmhlaWdodCkge1xuICAgICAgICByZXR1cm4gcmVnbFRleHR1cmUyRFxuICAgICAgfVxuXG4gICAgICByZWdsVGV4dHVyZTJELndpZHRoID0gdGV4dHVyZS53aWR0aCA9IHdcbiAgICAgIHJlZ2xUZXh0dXJlMkQuaGVpZ2h0ID0gdGV4dHVyZS5oZWlnaHQgPSBoXG5cbiAgICAgIHRlbXBCaW5kKHRleHR1cmUpXG4gICAgICBnbC50ZXhJbWFnZTJEKFxuICAgICAgICBHTF9URVhUVVJFXzJELFxuICAgICAgICAwLFxuICAgICAgICB0ZXh0dXJlLmZvcm1hdCxcbiAgICAgICAgdyxcbiAgICAgICAgaCxcbiAgICAgICAgMCxcbiAgICAgICAgdGV4dHVyZS5mb3JtYXQsXG4gICAgICAgIHRleHR1cmUudHlwZSxcbiAgICAgICAgbnVsbClcbiAgICAgIHRlbXBSZXN0b3JlKClcblxuICAgICAgcmV0dXJuIHJlZ2xUZXh0dXJlMkRcbiAgICB9XG5cbiAgICByZWdsVGV4dHVyZTJEKGEsIGIpXG5cbiAgICByZWdsVGV4dHVyZTJELnN1YmltYWdlID0gc3ViaW1hZ2VcbiAgICByZWdsVGV4dHVyZTJELnJlc2l6ZSA9IHJlc2l6ZVxuICAgIHJlZ2xUZXh0dXJlMkQuX3JlZ2xUeXBlID0gJ3RleHR1cmUyZCdcbiAgICByZWdsVGV4dHVyZTJELl90ZXh0dXJlID0gdGV4dHVyZVxuICAgIHJlZ2xUZXh0dXJlMkQuZGVzdHJveSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgIHRleHR1cmUuZGVjUmVmKClcbiAgICB9XG5cbiAgICByZXR1cm4gcmVnbFRleHR1cmUyRFxuICB9XG5cbiAgZnVuY3Rpb24gY3JlYXRlVGV4dHVyZUN1YmUgKGEwLCBhMSwgYTIsIGEzLCBhNCwgYTUpIHtcbiAgICB2YXIgdGV4dHVyZSA9IG5ldyBSRUdMVGV4dHVyZShHTF9URVhUVVJFX0NVQkVfTUFQKVxuICAgIHRleHR1cmVTZXRbdGV4dHVyZS5pZF0gPSB0ZXh0dXJlXG4gICAgc3RhdHMuY3ViZUNvdW50KytcblxuICAgIHZhciBmYWNlcyA9IG5ldyBBcnJheSg2KVxuXG4gICAgZnVuY3Rpb24gcmVnbFRleHR1cmVDdWJlIChhMCwgYTEsIGEyLCBhMywgYTQsIGE1KSB7XG4gICAgICB2YXIgaVxuICAgICAgdmFyIHRleEluZm8gPSBhbGxvY0luZm8oKVxuICAgICAgZm9yIChpID0gMDsgaSA8IDY7ICsraSkge1xuICAgICAgICBmYWNlc1tpXSA9IGFsbG9jTWlwTWFwKClcbiAgICAgIH1cblxuICAgICAgaWYgKHR5cGVvZiBhMCA9PT0gJ251bWJlcicgfHwgIWEwKSB7XG4gICAgICAgIHZhciBzID0gKGEwIHwgMCkgfHwgMVxuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgNjsgKytpKSB7XG4gICAgICAgICAgcGFyc2VNaXBNYXBGcm9tU2hhcGUoZmFjZXNbaV0sIHMsIHMpXG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAodHlwZW9mIGEwID09PSAnb2JqZWN0Jykge1xuICAgICAgICBpZiAoYTEpIHtcbiAgICAgICAgICBwYXJzZU1pcE1hcEZyb21PYmplY3QoZmFjZXNbMF0sIGEwKVxuICAgICAgICAgIHBhcnNlTWlwTWFwRnJvbU9iamVjdChmYWNlc1sxXSwgYTEpXG4gICAgICAgICAgcGFyc2VNaXBNYXBGcm9tT2JqZWN0KGZhY2VzWzJdLCBhMilcbiAgICAgICAgICBwYXJzZU1pcE1hcEZyb21PYmplY3QoZmFjZXNbM10sIGEzKVxuICAgICAgICAgIHBhcnNlTWlwTWFwRnJvbU9iamVjdChmYWNlc1s0XSwgYTQpXG4gICAgICAgICAgcGFyc2VNaXBNYXBGcm9tT2JqZWN0KGZhY2VzWzVdLCBhNSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBwYXJzZVRleEluZm8odGV4SW5mbywgYTApXG4gICAgICAgICAgcGFyc2VGbGFncyh0ZXh0dXJlLCBhMClcbiAgICAgICAgICBpZiAoJ2ZhY2VzJyBpbiBhMCkge1xuICAgICAgICAgICAgdmFyIGZhY2VfaW5wdXQgPSBhMC5mYWNlc1xuICAgICAgICAgICAgY2hlY2soQXJyYXkuaXNBcnJheShmYWNlX2lucHV0KSAmJiBmYWNlX2lucHV0Lmxlbmd0aCA9PT0gNixcbiAgICAgICAgICAgICAgJ2N1YmUgZmFjZXMgbXVzdCBiZSBhIGxlbmd0aCA2IGFycmF5JylcbiAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCA2OyArK2kpIHtcbiAgICAgICAgICAgICAgY2hlY2sodHlwZW9mIGZhY2VfaW5wdXRbaV0gPT09ICdvYmplY3QnICYmICEhZmFjZV9pbnB1dFtpXSxcbiAgICAgICAgICAgICAgICAnaW52YWxpZCBpbnB1dCBmb3IgY3ViZSBtYXAgZmFjZScpXG4gICAgICAgICAgICAgIGNvcHlGbGFncyhmYWNlc1tpXSwgdGV4dHVyZSlcbiAgICAgICAgICAgICAgcGFyc2VNaXBNYXBGcm9tT2JqZWN0KGZhY2VzW2ldLCBmYWNlX2lucHV0W2ldKVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBmb3IgKGkgPSAwOyBpIDwgNjsgKytpKSB7XG4gICAgICAgICAgICAgIHBhcnNlTWlwTWFwRnJvbU9iamVjdChmYWNlc1tpXSwgYTApXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjaGVjay5yYWlzZSgnaW52YWxpZCBhcmd1bWVudHMgdG8gY3ViZSBtYXAnKVxuICAgICAgfVxuXG4gICAgICBjb3B5RmxhZ3ModGV4dHVyZSwgZmFjZXNbMF0pXG4gICAgICBpZiAodGV4SW5mby5nZW5NaXBtYXBzKSB7XG4gICAgICAgIHRleHR1cmUubWlwbWFzayA9IChmYWNlc1swXS53aWR0aCA8PCAxKSAtIDFcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRleHR1cmUubWlwbWFzayA9IGZhY2VzWzBdLm1pcG1hc2tcbiAgICAgIH1cblxuICAgICAgY2hlY2sudGV4dHVyZUN1YmUodGV4dHVyZSwgdGV4SW5mbywgZmFjZXMsIGxpbWl0cylcbiAgICAgIHRleHR1cmUuaW50ZXJuYWxmb3JtYXQgPSBmYWNlc1swXS5pbnRlcm5hbGZvcm1hdFxuXG4gICAgICByZWdsVGV4dHVyZUN1YmUud2lkdGggPSBmYWNlc1swXS53aWR0aFxuICAgICAgcmVnbFRleHR1cmVDdWJlLmhlaWdodCA9IGZhY2VzWzBdLmhlaWdodFxuXG4gICAgICB0ZW1wQmluZCh0ZXh0dXJlKVxuICAgICAgZm9yIChpID0gMDsgaSA8IDY7ICsraSkge1xuICAgICAgICBzZXRNaXBNYXAoZmFjZXNbaV0sIEdMX1RFWFRVUkVfQ1VCRV9NQVBfUE9TSVRJVkVfWCArIGkpXG4gICAgICB9XG4gICAgICBzZXRUZXhJbmZvKHRleEluZm8sIEdMX1RFWFRVUkVfQ1VCRV9NQVApXG4gICAgICB0ZW1wUmVzdG9yZSgpXG5cbiAgICAgIGZyZWVJbmZvKHRleEluZm8pXG5cbiAgICAgIGZvciAoaSA9IDA7IGkgPCA2OyArK2kpIHtcbiAgICAgICAgZnJlZU1pcE1hcChmYWNlc1tpXSlcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHJlZ2xUZXh0dXJlQ3ViZVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIHN1YmltYWdlIChmYWNlLCBpbWFnZSwgeF8sIHlfLCBsZXZlbF8pIHtcbiAgICAgIGNoZWNrKCEhaW1hZ2UsICdtdXN0IHNwZWNpZnkgaW1hZ2UgZGF0YScpXG4gICAgICBjaGVjayh0eXBlb2YgZmFjZSA9PT0gJ251bWJlcicgJiYgZmFjZSA9PT0gKGZhY2UgfCAwKSAmJlxuICAgICAgICBmYWNlID49IDAgJiYgZmFjZSA8IDYsICdpbnZhbGlkIGZhY2UnKVxuXG4gICAgICB2YXIgeCA9IHhfIHwgMFxuICAgICAgdmFyIHkgPSB5XyB8IDBcbiAgICAgIHZhciBsZXZlbCA9IGxldmVsXyB8IDBcblxuICAgICAgdmFyIGltYWdlRGF0YSA9IGFsbG9jSW1hZ2UoKVxuICAgICAgY29weUZsYWdzKGltYWdlRGF0YSwgdGV4dHVyZSlcbiAgICAgIGltYWdlRGF0YS53aWR0aCA+Pj0gbGV2ZWxcbiAgICAgIGltYWdlRGF0YS5oZWlnaHQgPj49IGxldmVsXG4gICAgICBpbWFnZURhdGEud2lkdGggLT0geFxuICAgICAgaW1hZ2VEYXRhLmhlaWdodCAtPSB5XG4gICAgICBwYXJzZUltYWdlKGltYWdlRGF0YSwgaW1hZ2UpXG5cbiAgICAgIGNoZWNrKFxuICAgICAgICB0ZXh0dXJlLnR5cGUgPT09IGltYWdlRGF0YS50eXBlICYmXG4gICAgICAgIHRleHR1cmUuZm9ybWF0ID09PSBpbWFnZURhdGEuZm9ybWF0ICYmXG4gICAgICAgIHRleHR1cmUuaW50ZXJuYWxmb3JtYXQgPT09IGltYWdlRGF0YS5pbnRlcm5hbGZvcm1hdCxcbiAgICAgICAgJ2luY29tcGF0aWJsZSBmb3JtYXQgZm9yIHRleHR1cmUuc3ViaW1hZ2UnKVxuICAgICAgY2hlY2soXG4gICAgICAgIHggPj0gMCAmJiB5ID49IDAgJiZcbiAgICAgICAgeCArIGltYWdlRGF0YS53aWR0aCA8PSB0ZXh0dXJlLndpZHRoICYmXG4gICAgICAgIHkgKyBpbWFnZURhdGEuaGVpZ2h0IDw9IHRleHR1cmUuaGVpZ2h0LFxuICAgICAgICAndGV4dHVyZS5zdWJpbWFnZSB3cml0ZSBvdXQgb2YgYm91bmRzJylcbiAgICAgIGNoZWNrKFxuICAgICAgICB0ZXh0dXJlLm1pcG1hc2sgJiAoMSA8PCBsZXZlbCksXG4gICAgICAgICdtaXNzaW5nIG1pcG1hcCBkYXRhJylcbiAgICAgIGNoZWNrKFxuICAgICAgICBpbWFnZURhdGEuZGF0YSB8fCBpbWFnZURhdGEuZWxlbWVudCB8fCBpbWFnZURhdGEubmVlZHNDb3B5LFxuICAgICAgICAnbWlzc2luZyBpbWFnZSBkYXRhJylcblxuICAgICAgdGVtcEJpbmQodGV4dHVyZSlcbiAgICAgIHNldFN1YkltYWdlKGltYWdlRGF0YSwgR0xfVEVYVFVSRV9DVUJFX01BUF9QT1NJVElWRV9YICsgZmFjZSwgeCwgeSwgbGV2ZWwpXG4gICAgICB0ZW1wUmVzdG9yZSgpXG5cbiAgICAgIGZyZWVJbWFnZShpbWFnZURhdGEpXG5cbiAgICAgIHJldHVybiByZWdsVGV4dHVyZUN1YmVcbiAgICB9XG5cbiAgICBmdW5jdGlvbiByZXNpemUgKHdfLCBoXykge1xuICAgICAgdmFyIHcgPSB3XyB8IDBcbiAgICAgIHZhciBoID0gKGhfIHwgMCkgfHwgd1xuICAgICAgaWYgKHcgPT09IHRleHR1cmUud2lkdGggJiYgaCA9PT0gdGV4dHVyZS5oZWlnaHQpIHtcbiAgICAgICAgcmV0dXJuIHJlZ2xUZXh0dXJlQ3ViZVxuICAgICAgfVxuXG4gICAgICByZWdsVGV4dHVyZUN1YmUud2lkdGggPSB0ZXh0dXJlLndpZHRoID0gd1xuICAgICAgcmVnbFRleHR1cmVDdWJlLmhlaWdodCA9IHRleHR1cmUuaGVpZ2h0ID0gaFxuXG4gICAgICB0ZW1wQmluZCh0ZXh0dXJlKVxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCA2OyArK2kpIHtcbiAgICAgICAgZ2wudGV4SW1hZ2UyRChcbiAgICAgICAgICBHTF9URVhUVVJFX0NVQkVfTUFQX1BPU0lUSVZFX1ggKyBpLFxuICAgICAgICAgIDAsXG4gICAgICAgICAgdGV4dHVyZS5mb3JtYXQsXG4gICAgICAgICAgdyxcbiAgICAgICAgICBoLFxuICAgICAgICAgIDAsXG4gICAgICAgICAgdGV4dHVyZS5mb3JtYXQsXG4gICAgICAgICAgdGV4dHVyZS50eXBlLFxuICAgICAgICAgIG51bGwpXG4gICAgICB9XG4gICAgICB0ZW1wUmVzdG9yZSgpXG5cbiAgICAgIHJldHVybiByZWdsVGV4dHVyZUN1YmVcbiAgICB9XG5cbiAgICByZWdsVGV4dHVyZUN1YmUoYTAsIGExLCBhMiwgYTMsIGE0LCBhNSlcblxuICAgIHJlZ2xUZXh0dXJlQ3ViZS5zdWJpbWFnZSA9IHN1YmltYWdlXG4gICAgcmVnbFRleHR1cmVDdWJlLnJlc2l6ZSA9IHJlc2l6ZVxuICAgIHJlZ2xUZXh0dXJlQ3ViZS5fcmVnbFR5cGUgPSAndGV4dHVyZUN1YmUnXG4gICAgcmVnbFRleHR1cmVDdWJlLl90ZXh0dXJlID0gdGV4dHVyZVxuICAgIHJlZ2xUZXh0dXJlQ3ViZS5kZXN0cm95ID0gZnVuY3Rpb24gKCkge1xuICAgICAgdGV4dHVyZS5kZWNSZWYoKVxuICAgIH1cblxuICAgIHJldHVybiByZWdsVGV4dHVyZUN1YmVcbiAgfVxuXG4gIC8vIENhbGxlZCB3aGVuIHJlZ2wgaXMgZGVzdHJveWVkXG4gIGZ1bmN0aW9uIGRlc3Ryb3lUZXh0dXJlcyAoKSB7XG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBudW1UZXhVbml0czsgKytpKSB7XG4gICAgICBnbC5hY3RpdmVUZXh0dXJlKEdMX1RFWFRVUkUwICsgaSlcbiAgICAgIGdsLmJpbmRUZXh0dXJlKEdMX1RFWFRVUkVfMkQsIG51bGwpXG4gICAgICB0ZXh0dXJlVW5pdHNbaV0gPSBudWxsXG4gICAgfVxuICAgIHZhbHVlcyh0ZXh0dXJlU2V0KS5mb3JFYWNoKGRlc3Ryb3kpXG5cbiAgICBzdGF0cy5jdWJlQ291bnQgPSAwXG4gICAgc3RhdHMudGV4dHVyZUNvdW50ID0gMFxuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBjcmVhdGUyRDogY3JlYXRlVGV4dHVyZTJELFxuICAgIGNyZWF0ZUN1YmU6IGNyZWF0ZVRleHR1cmVDdWJlLFxuICAgIGNsZWFyOiBkZXN0cm95VGV4dHVyZXMsXG4gICAgZ2V0VGV4dHVyZTogZnVuY3Rpb24gKHdyYXBwZXIpIHtcbiAgICAgIHJldHVybiBudWxsXG4gICAgfVxuICB9XG59XG4iLCJ2YXIgR0xfUVVFUllfUkVTVUxUX0VYVCA9IDB4ODg2NlxudmFyIEdMX1FVRVJZX1JFU1VMVF9BVkFJTEFCTEVfRVhUID0gMHg4ODY3XG52YXIgR0xfVElNRV9FTEFQU0VEX0VYVCA9IDB4ODhCRlxuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChnbCwgZXh0ZW5zaW9ucykge1xuICB2YXIgZXh0VGltZXIgPSBleHRlbnNpb25zLmV4dF9kaXNqb2ludF90aW1lcl9xdWVyeVxuXG4gIGlmICghZXh0VGltZXIpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgLy8gUVVFUlkgUE9PTCBCRUdJTlxuICB2YXIgcXVlcnlQb29sID0gW11cbiAgZnVuY3Rpb24gYWxsb2NRdWVyeSAoKSB7XG4gICAgcmV0dXJuIHF1ZXJ5UG9vbC5wb3AoKSB8fCBleHRUaW1lci5jcmVhdGVRdWVyeUVYVCgpXG4gIH1cbiAgZnVuY3Rpb24gZnJlZVF1ZXJ5IChxdWVyeSkge1xuICAgIHF1ZXJ5UG9vbC5wdXNoKHF1ZXJ5KVxuICB9XG4gIC8vIFFVRVJZIFBPT0wgRU5EXG5cbiAgdmFyIHBlbmRpbmdRdWVyaWVzID0gW11cbiAgZnVuY3Rpb24gYmVnaW5RdWVyeSAoc3RhdHMpIHtcbiAgICB2YXIgcXVlcnkgPSBhbGxvY1F1ZXJ5KClcbiAgICBleHRUaW1lci5iZWdpblF1ZXJ5RVhUKEdMX1RJTUVfRUxBUFNFRF9FWFQsIHF1ZXJ5KVxuICAgIHBlbmRpbmdRdWVyaWVzLnB1c2gocXVlcnkpXG4gICAgcHVzaFNjb3BlU3RhdHMocGVuZGluZ1F1ZXJpZXMubGVuZ3RoIC0gMSwgcGVuZGluZ1F1ZXJpZXMubGVuZ3RoLCBzdGF0cylcbiAgfVxuXG4gIGZ1bmN0aW9uIGVuZFF1ZXJ5ICgpIHtcbiAgICBleHRUaW1lci5lbmRRdWVyeUVYVChHTF9USU1FX0VMQVBTRURfRVhUKVxuICB9XG5cbiAgLy9cbiAgLy8gUGVuZGluZyBzdGF0cyBwb29sLlxuICAvL1xuICBmdW5jdGlvbiBQZW5kaW5nU3RhdHMgKCkge1xuICAgIHRoaXMuc3RhcnRRdWVyeUluZGV4ID0gLTFcbiAgICB0aGlzLmVuZFF1ZXJ5SW5kZXggPSAtMVxuICAgIHRoaXMuc3VtID0gMFxuICAgIHRoaXMuc3RhdHMgPSBudWxsXG4gIH1cbiAgdmFyIHBlbmRpbmdTdGF0c1Bvb2wgPSBbXVxuICBmdW5jdGlvbiBhbGxvY1BlbmRpbmdTdGF0cyAoKSB7XG4gICAgcmV0dXJuIHBlbmRpbmdTdGF0c1Bvb2wucG9wKCkgfHwgbmV3IFBlbmRpbmdTdGF0cygpXG4gIH1cbiAgZnVuY3Rpb24gZnJlZVBlbmRpbmdTdGF0cyAocGVuZGluZ1N0YXRzKSB7XG4gICAgcGVuZGluZ1N0YXRzUG9vbC5wdXNoKHBlbmRpbmdTdGF0cylcbiAgfVxuICAvLyBQZW5kaW5nIHN0YXRzIHBvb2wgZW5kXG5cbiAgdmFyIHBlbmRpbmdTdGF0cyA9IFtdXG4gIGZ1bmN0aW9uIHB1c2hTY29wZVN0YXRzIChzdGFydCwgZW5kLCBzdGF0cykge1xuICAgIHZhciBwcyA9IGFsbG9jUGVuZGluZ1N0YXRzKClcbiAgICBwcy5zdGFydFF1ZXJ5SW5kZXggPSBzdGFydFxuICAgIHBzLmVuZFF1ZXJ5SW5kZXggPSBlbmRcbiAgICBwcy5zdW0gPSAwXG4gICAgcHMuc3RhdHMgPSBzdGF0c1xuICAgIHBlbmRpbmdTdGF0cy5wdXNoKHBzKVxuICB9XG5cbiAgLy8gd2Ugc2hvdWxkIGNhbGwgdGhpcyBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBmcmFtZSxcbiAgLy8gaW4gb3JkZXIgdG8gdXBkYXRlIGdwdVRpbWVcbiAgdmFyIHRpbWVTdW0gPSBbXVxuICB2YXIgcXVlcnlQdHIgPSBbXVxuICBmdW5jdGlvbiB1cGRhdGUgKCkge1xuICAgIHZhciBwdHIsIGlcblxuICAgIHZhciBuID0gcGVuZGluZ1F1ZXJpZXMubGVuZ3RoXG4gICAgaWYgKG4gPT09IDApIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIFJlc2VydmUgc3BhY2VcbiAgICBxdWVyeVB0ci5sZW5ndGggPSBNYXRoLm1heChxdWVyeVB0ci5sZW5ndGgsIG4gKyAxKVxuICAgIHRpbWVTdW0ubGVuZ3RoID0gTWF0aC5tYXgodGltZVN1bS5sZW5ndGgsIG4gKyAxKVxuICAgIHRpbWVTdW1bMF0gPSAwXG4gICAgcXVlcnlQdHJbMF0gPSAwXG5cbiAgICAvLyBVcGRhdGUgYWxsIHBlbmRpbmcgdGltZXIgcXVlcmllc1xuICAgIHZhciBxdWVyeVRpbWUgPSAwXG4gICAgcHRyID0gMFxuICAgIGZvciAoaSA9IDA7IGkgPCBwZW5kaW5nUXVlcmllcy5sZW5ndGg7ICsraSkge1xuICAgICAgdmFyIHF1ZXJ5ID0gcGVuZGluZ1F1ZXJpZXNbaV1cbiAgICAgIGlmIChleHRUaW1lci5nZXRRdWVyeU9iamVjdEVYVChxdWVyeSwgR0xfUVVFUllfUkVTVUxUX0FWQUlMQUJMRV9FWFQpKSB7XG4gICAgICAgIHF1ZXJ5VGltZSArPSBleHRUaW1lci5nZXRRdWVyeU9iamVjdEVYVChxdWVyeSwgR0xfUVVFUllfUkVTVUxUX0VYVClcbiAgICAgICAgZnJlZVF1ZXJ5KHF1ZXJ5KVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcGVuZGluZ1F1ZXJpZXNbcHRyKytdID0gcXVlcnlcbiAgICAgIH1cbiAgICAgIHRpbWVTdW1baSArIDFdID0gcXVlcnlUaW1lXG4gICAgICBxdWVyeVB0cltpICsgMV0gPSBwdHJcbiAgICB9XG4gICAgcGVuZGluZ1F1ZXJpZXMubGVuZ3RoID0gcHRyXG5cbiAgICAvLyBVcGRhdGUgYWxsIHBlbmRpbmcgc3RhdCBxdWVyaWVzXG4gICAgcHRyID0gMFxuICAgIGZvciAoaSA9IDA7IGkgPCBwZW5kaW5nU3RhdHMubGVuZ3RoOyArK2kpIHtcbiAgICAgIHZhciBzdGF0cyA9IHBlbmRpbmdTdGF0c1tpXVxuICAgICAgdmFyIHN0YXJ0ID0gc3RhdHMuc3RhcnRRdWVyeUluZGV4XG4gICAgICB2YXIgZW5kID0gc3RhdHMuZW5kUXVlcnlJbmRleFxuICAgICAgc3RhdHMuc3VtICs9IHRpbWVTdW1bZW5kXSAtIHRpbWVTdW1bc3RhcnRdXG4gICAgICB2YXIgc3RhcnRQdHIgPSBxdWVyeVB0cltzdGFydF1cbiAgICAgIHZhciBlbmRQdHIgPSBxdWVyeVB0cltlbmRdXG4gICAgICBpZiAoZW5kUHRyID09PSBzdGFydFB0cikge1xuICAgICAgICBzdGF0cy5zdGF0cy5ncHVUaW1lICs9IHN0YXRzLnN1bSAvIDFlNlxuICAgICAgICBmcmVlUGVuZGluZ1N0YXRzKHN0YXRzKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgc3RhdHMuc3RhcnRRdWVyeUluZGV4ID0gc3RhcnRQdHJcbiAgICAgICAgc3RhdHMuZW5kUXVlcnlJbmRleCA9IGVuZFB0clxuICAgICAgICBwZW5kaW5nU3RhdHNbcHRyKytdID0gc3RhdHNcbiAgICAgIH1cbiAgICB9XG4gICAgcGVuZGluZ1N0YXRzLmxlbmd0aCA9IHB0clxuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBiZWdpblF1ZXJ5OiBiZWdpblF1ZXJ5LFxuICAgIGVuZFF1ZXJ5OiBlbmRRdWVyeSxcbiAgICBwdXNoU2NvcGVTdGF0czogcHVzaFNjb3BlU3RhdHMsXG4gICAgdXBkYXRlOiB1cGRhdGUsXG4gICAgZ2V0TnVtUGVuZGluZ1F1ZXJpZXM6IGZ1bmN0aW9uICgpIHtcbiAgICAgIHJldHVybiBwZW5kaW5nUXVlcmllcy5sZW5ndGhcbiAgICB9LFxuICAgIGNsZWFyOiBmdW5jdGlvbiAoKSB7XG4gICAgICBxdWVyeVBvb2wucHVzaC5hcHBseShxdWVyeVBvb2wsIHBlbmRpbmdRdWVyaWVzKVxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBxdWVyeVBvb2wubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgZXh0VGltZXIuZGVsZXRlUXVlcnlFWFQocXVlcnlQb29sW2ldKVxuICAgICAgfVxuICAgICAgcGVuZGluZ1F1ZXJpZXMubGVuZ3RoID0gMFxuICAgICAgcXVlcnlQb29sLmxlbmd0aCA9IDBcbiAgICB9XG4gIH1cbn1cbiIsIi8vIEVycm9yIGNoZWNraW5nIGFuZCBwYXJhbWV0ZXIgdmFsaWRhdGlvbi5cbi8vXG4vLyBTdGF0ZW1lbnRzIGZvciB0aGUgZm9ybSBgY2hlY2suc29tZVByb2NlZHVyZSguLi4pYCBnZXQgcmVtb3ZlZCBieVxuLy8gYSBicm93c2VyaWZ5IHRyYW5zZm9ybSBmb3Igb3B0aW1pemVkL21pbmlmaWVkIGJ1bmRsZXMuXG4vL1xuLyogZ2xvYmFscyBidG9hICovXG52YXIgaXNUeXBlZEFycmF5ID0gcmVxdWlyZSgnLi9pcy10eXBlZC1hcnJheScpXG52YXIgZXh0ZW5kID0gcmVxdWlyZSgnLi9leHRlbmQnKVxuXG4vLyBvbmx5IHVzZWQgZm9yIGV4dHJhY3Rpbmcgc2hhZGVyIG5hbWVzLiAgaWYgYnRvYSBub3QgcHJlc2VudCwgdGhlbiBlcnJvcnNcbi8vIHdpbGwgYmUgc2xpZ2h0bHkgY3JhcHBpZXJcbmZ1bmN0aW9uIGRlY29kZUI2NCAoc3RyKSB7XG4gIGlmICh0eXBlb2YgYnRvYSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICByZXR1cm4gYnRvYShzdHIpXG4gIH1cbiAgcmV0dXJuICdiYXNlNjQ6JyArIHN0clxufVxuXG5mdW5jdGlvbiByYWlzZSAobWVzc2FnZSkge1xuICB2YXIgZXJyb3IgPSBuZXcgRXJyb3IoJyhyZWdsKSAnICsgbWVzc2FnZSlcbiAgY29uc29sZS5lcnJvcihlcnJvcilcbiAgdGhyb3cgZXJyb3Jcbn1cblxuZnVuY3Rpb24gY2hlY2sgKHByZWQsIG1lc3NhZ2UpIHtcbiAgaWYgKCFwcmVkKSB7XG4gICAgcmFpc2UobWVzc2FnZSlcbiAgfVxufVxuXG5mdW5jdGlvbiBlbmNvbG9uIChtZXNzYWdlKSB7XG4gIGlmIChtZXNzYWdlKSB7XG4gICAgcmV0dXJuICc6ICcgKyBtZXNzYWdlXG4gIH1cbiAgcmV0dXJuICcnXG59XG5cbmZ1bmN0aW9uIGNoZWNrUGFyYW1ldGVyIChwYXJhbSwgcG9zc2liaWxpdGllcywgbWVzc2FnZSkge1xuICBpZiAoIShwYXJhbSBpbiBwb3NzaWJpbGl0aWVzKSkge1xuICAgIHJhaXNlKCd1bmtub3duIHBhcmFtZXRlciAoJyArIHBhcmFtICsgJyknICsgZW5jb2xvbihtZXNzYWdlKSArXG4gICAgICAgICAgJy4gcG9zc2libGUgdmFsdWVzOiAnICsgT2JqZWN0LmtleXMocG9zc2liaWxpdGllcykuam9pbigpKVxuICB9XG59XG5cbmZ1bmN0aW9uIGNoZWNrSXNUeXBlZEFycmF5IChkYXRhLCBtZXNzYWdlKSB7XG4gIGlmICghaXNUeXBlZEFycmF5KGRhdGEpKSB7XG4gICAgcmFpc2UoXG4gICAgICAnaW52YWxpZCBwYXJhbWV0ZXIgdHlwZScgKyBlbmNvbG9uKG1lc3NhZ2UpICtcbiAgICAgICcuIG11c3QgYmUgYSB0eXBlZCBhcnJheScpXG4gIH1cbn1cblxuZnVuY3Rpb24gY2hlY2tUeXBlT2YgKHZhbHVlLCB0eXBlLCBtZXNzYWdlKSB7XG4gIGlmICh0eXBlb2YgdmFsdWUgIT09IHR5cGUpIHtcbiAgICByYWlzZShcbiAgICAgICdpbnZhbGlkIHBhcmFtZXRlciB0eXBlJyArIGVuY29sb24obWVzc2FnZSkgK1xuICAgICAgJy4gZXhwZWN0ZWQgJyArIHR5cGUgKyAnLCBnb3QgJyArICh0eXBlb2YgdmFsdWUpKVxuICB9XG59XG5cbmZ1bmN0aW9uIGNoZWNrTm9uTmVnYXRpdmVJbnQgKHZhbHVlLCBtZXNzYWdlKSB7XG4gIGlmICghKCh2YWx1ZSA+PSAwKSAmJlxuICAgICAgICAoKHZhbHVlIHwgMCkgPT09IHZhbHVlKSkpIHtcbiAgICByYWlzZSgnaW52YWxpZCBwYXJhbWV0ZXIgdHlwZSwgKCcgKyB2YWx1ZSArICcpJyArIGVuY29sb24obWVzc2FnZSkgK1xuICAgICAgICAgICcuIG11c3QgYmUgYSBub25uZWdhdGl2ZSBpbnRlZ2VyJylcbiAgfVxufVxuXG5mdW5jdGlvbiBjaGVja09uZU9mICh2YWx1ZSwgbGlzdCwgbWVzc2FnZSkge1xuICBpZiAobGlzdC5pbmRleE9mKHZhbHVlKSA8IDApIHtcbiAgICByYWlzZSgnaW52YWxpZCB2YWx1ZScgKyBlbmNvbG9uKG1lc3NhZ2UpICsgJy4gbXVzdCBiZSBvbmUgb2Y6ICcgKyBsaXN0KVxuICB9XG59XG5cbnZhciBjb25zdHJ1Y3RvcktleXMgPSBbXG4gICdnbCcsXG4gICdjYW52YXMnLFxuICAnY29udGFpbmVyJyxcbiAgJ2F0dHJpYnV0ZXMnLFxuICAncGl4ZWxSYXRpbycsXG4gICdleHRlbnNpb25zJyxcbiAgJ29wdGlvbmFsRXh0ZW5zaW9ucycsXG4gICdwcm9maWxlJyxcbiAgJ29uRG9uZSdcbl1cblxuZnVuY3Rpb24gY2hlY2tDb25zdHJ1Y3RvciAob2JqKSB7XG4gIE9iamVjdC5rZXlzKG9iaikuZm9yRWFjaChmdW5jdGlvbiAoa2V5KSB7XG4gICAgaWYgKGNvbnN0cnVjdG9yS2V5cy5pbmRleE9mKGtleSkgPCAwKSB7XG4gICAgICByYWlzZSgnaW52YWxpZCByZWdsIGNvbnN0cnVjdG9yIGFyZ3VtZW50IFwiJyArIGtleSArICdcIi4gbXVzdCBiZSBvbmUgb2YgJyArIGNvbnN0cnVjdG9yS2V5cylcbiAgICB9XG4gIH0pXG59XG5cbmZ1bmN0aW9uIGxlZnRQYWQgKHN0ciwgbikge1xuICBzdHIgPSBzdHIgKyAnJ1xuICB3aGlsZSAoc3RyLmxlbmd0aCA8IG4pIHtcbiAgICBzdHIgPSAnICcgKyBzdHJcbiAgfVxuICByZXR1cm4gc3RyXG59XG5cbmZ1bmN0aW9uIFNoYWRlckZpbGUgKCkge1xuICB0aGlzLm5hbWUgPSAndW5rbm93bidcbiAgdGhpcy5saW5lcyA9IFtdXG4gIHRoaXMuaW5kZXggPSB7fVxuICB0aGlzLmhhc0Vycm9ycyA9IGZhbHNlXG59XG5cbmZ1bmN0aW9uIFNoYWRlckxpbmUgKG51bWJlciwgbGluZSkge1xuICB0aGlzLm51bWJlciA9IG51bWJlclxuICB0aGlzLmxpbmUgPSBsaW5lXG4gIHRoaXMuZXJyb3JzID0gW11cbn1cblxuZnVuY3Rpb24gU2hhZGVyRXJyb3IgKGZpbGVOdW1iZXIsIGxpbmVOdW1iZXIsIG1lc3NhZ2UpIHtcbiAgdGhpcy5maWxlID0gZmlsZU51bWJlclxuICB0aGlzLmxpbmUgPSBsaW5lTnVtYmVyXG4gIHRoaXMubWVzc2FnZSA9IG1lc3NhZ2Vcbn1cblxuZnVuY3Rpb24gZ3Vlc3NDb21tYW5kICgpIHtcbiAgdmFyIGVycm9yID0gbmV3IEVycm9yKClcbiAgdmFyIHN0YWNrID0gKGVycm9yLnN0YWNrIHx8IGVycm9yKS50b1N0cmluZygpXG4gIHZhciBwYXQgPSAvY29tcGlsZVByb2NlZHVyZS4qXFxuXFxzKmF0LipcXCgoLiopXFwpLy5leGVjKHN0YWNrKVxuICBpZiAocGF0KSB7XG4gICAgcmV0dXJuIHBhdFsxXVxuICB9XG4gIHZhciBwYXQyID0gL2NvbXBpbGVQcm9jZWR1cmUuKlxcblxccyphdFxccysoLiopKFxcbnwkKS8uZXhlYyhzdGFjaylcbiAgaWYgKHBhdDIpIHtcbiAgICByZXR1cm4gcGF0MlsxXVxuICB9XG4gIHJldHVybiAndW5rbm93bidcbn1cblxuZnVuY3Rpb24gZ3Vlc3NDYWxsU2l0ZSAoKSB7XG4gIHZhciBlcnJvciA9IG5ldyBFcnJvcigpXG4gIHZhciBzdGFjayA9IChlcnJvci5zdGFjayB8fCBlcnJvcikudG9TdHJpbmcoKVxuICB2YXIgcGF0ID0gL2F0IFJFR0xDb21tYW5kLipcXG5cXHMrYXQuKlxcKCguKilcXCkvLmV4ZWMoc3RhY2spXG4gIGlmIChwYXQpIHtcbiAgICByZXR1cm4gcGF0WzFdXG4gIH1cbiAgdmFyIHBhdDIgPSAvYXQgUkVHTENvbW1hbmQuKlxcblxccythdFxccysoLiopXFxuLy5leGVjKHN0YWNrKVxuICBpZiAocGF0Mikge1xuICAgIHJldHVybiBwYXQyWzFdXG4gIH1cbiAgcmV0dXJuICd1bmtub3duJ1xufVxuXG5mdW5jdGlvbiBwYXJzZVNvdXJjZSAoc291cmNlLCBjb21tYW5kKSB7XG4gIHZhciBsaW5lcyA9IHNvdXJjZS5zcGxpdCgnXFxuJylcbiAgdmFyIGxpbmVOdW1iZXIgPSAxXG4gIHZhciBmaWxlTnVtYmVyID0gMFxuICB2YXIgZmlsZXMgPSB7XG4gICAgdW5rbm93bjogbmV3IFNoYWRlckZpbGUoKSxcbiAgICAwOiBuZXcgU2hhZGVyRmlsZSgpXG4gIH1cbiAgZmlsZXMudW5rbm93bi5uYW1lID0gZmlsZXNbMF0ubmFtZSA9IGNvbW1hbmQgfHwgZ3Vlc3NDb21tYW5kKClcbiAgZmlsZXMudW5rbm93bi5saW5lcy5wdXNoKG5ldyBTaGFkZXJMaW5lKDAsICcnKSlcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBsaW5lcy5sZW5ndGg7ICsraSkge1xuICAgIHZhciBsaW5lID0gbGluZXNbaV1cbiAgICB2YXIgcGFydHMgPSAvXlxccypcXCNcXHMqKFxcdyspXFxzKyguKylcXHMqJC8uZXhlYyhsaW5lKVxuICAgIGlmIChwYXJ0cykge1xuICAgICAgc3dpdGNoIChwYXJ0c1sxXSkge1xuICAgICAgICBjYXNlICdsaW5lJzpcbiAgICAgICAgICB2YXIgbGluZU51bWJlckluZm8gPSAvKFxcZCspKFxccytcXGQrKT8vLmV4ZWMocGFydHNbMl0pXG4gICAgICAgICAgaWYgKGxpbmVOdW1iZXJJbmZvKSB7XG4gICAgICAgICAgICBsaW5lTnVtYmVyID0gbGluZU51bWJlckluZm9bMV0gfCAwXG4gICAgICAgICAgICBpZiAobGluZU51bWJlckluZm9bMl0pIHtcbiAgICAgICAgICAgICAgZmlsZU51bWJlciA9IGxpbmVOdW1iZXJJbmZvWzJdIHwgMFxuICAgICAgICAgICAgICBpZiAoIShmaWxlTnVtYmVyIGluIGZpbGVzKSkge1xuICAgICAgICAgICAgICAgIGZpbGVzW2ZpbGVOdW1iZXJdID0gbmV3IFNoYWRlckZpbGUoKVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrXG4gICAgICAgIGNhc2UgJ2RlZmluZSc6XG4gICAgICAgICAgdmFyIG5hbWVJbmZvID0gL1NIQURFUl9OQU1FKF9CNjQpP1xccysoLiopJC8uZXhlYyhwYXJ0c1syXSlcbiAgICAgICAgICBpZiAobmFtZUluZm8pIHtcbiAgICAgICAgICAgIGZpbGVzW2ZpbGVOdW1iZXJdLm5hbWUgPSAobmFtZUluZm9bMV1cbiAgICAgICAgICAgICAgICA/IGRlY29kZUI2NChuYW1lSW5mb1syXSlcbiAgICAgICAgICAgICAgICA6IG5hbWVJbmZvWzJdKVxuICAgICAgICAgIH1cbiAgICAgICAgICBicmVha1xuICAgICAgfVxuICAgIH1cbiAgICBmaWxlc1tmaWxlTnVtYmVyXS5saW5lcy5wdXNoKG5ldyBTaGFkZXJMaW5lKGxpbmVOdW1iZXIrKywgbGluZSkpXG4gIH1cbiAgT2JqZWN0LmtleXMoZmlsZXMpLmZvckVhY2goZnVuY3Rpb24gKGZpbGVOdW1iZXIpIHtcbiAgICB2YXIgZmlsZSA9IGZpbGVzW2ZpbGVOdW1iZXJdXG4gICAgZmlsZS5saW5lcy5mb3JFYWNoKGZ1bmN0aW9uIChsaW5lKSB7XG4gICAgICBmaWxlLmluZGV4W2xpbmUubnVtYmVyXSA9IGxpbmVcbiAgICB9KVxuICB9KVxuICByZXR1cm4gZmlsZXNcbn1cblxuZnVuY3Rpb24gcGFyc2VFcnJvckxvZyAoZXJyTG9nKSB7XG4gIHZhciByZXN1bHQgPSBbXVxuICBlcnJMb2cuc3BsaXQoJ1xcbicpLmZvckVhY2goZnVuY3Rpb24gKGVyck1zZykge1xuICAgIHZhciBwYXJ0cyA9IC9eRVJST1JcXDpcXHMrKFxcZCspXFw6KFxcZCspXFw6XFxzKiguKikkLy5leGVjKGVyck1zZylcbiAgICBpZiAocGFydHMpIHtcbiAgICAgIHJlc3VsdC5wdXNoKG5ldyBTaGFkZXJFcnJvcihcbiAgICAgICAgcGFydHNbMV0gfCAwLFxuICAgICAgICBwYXJ0c1syXSB8IDAsXG4gICAgICAgIHBhcnRzWzNdLnRyaW0oKSkpXG4gICAgfSBlbHNlIGlmIChlcnJNc2cubGVuZ3RoID4gMCkge1xuICAgICAgcmVzdWx0LnB1c2gobmV3IFNoYWRlckVycm9yKCd1bmtub3duJywgMCwgZXJyTXNnKSlcbiAgICB9XG4gIH0pXG4gIHJldHVybiByZXN1bHRcbn1cblxuZnVuY3Rpb24gYW5ub3RhdGVGaWxlcyAoZmlsZXMsIGVycm9ycykge1xuICBlcnJvcnMuZm9yRWFjaChmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICB2YXIgZmlsZSA9IGZpbGVzW2Vycm9yLmZpbGVdXG4gICAgaWYgKGZpbGUpIHtcbiAgICAgIHZhciBsaW5lID0gZmlsZS5pbmRleFtlcnJvci5saW5lXVxuICAgICAgaWYgKGxpbmUpIHtcbiAgICAgICAgbGluZS5lcnJvcnMucHVzaChlcnJvcilcbiAgICAgICAgZmlsZS5oYXNFcnJvcnMgPSB0cnVlXG4gICAgICAgIHJldHVyblxuICAgICAgfVxuICAgIH1cbiAgICBmaWxlcy51bmtub3duLmhhc0Vycm9ycyA9IHRydWVcbiAgICBmaWxlcy51bmtub3duLmxpbmVzWzBdLmVycm9ycy5wdXNoKGVycm9yKVxuICB9KVxufVxuXG5mdW5jdGlvbiBjaGVja1NoYWRlckVycm9yIChnbCwgc2hhZGVyLCBzb3VyY2UsIHR5cGUsIGNvbW1hbmQpIHtcbiAgaWYgKCFnbC5nZXRTaGFkZXJQYXJhbWV0ZXIoc2hhZGVyLCBnbC5DT01QSUxFX1NUQVRVUykpIHtcbiAgICB2YXIgZXJyTG9nID0gZ2wuZ2V0U2hhZGVySW5mb0xvZyhzaGFkZXIpXG4gICAgdmFyIHR5cGVOYW1lID0gdHlwZSA9PT0gZ2wuRlJBR01FTlRfU0hBREVSID8gJ2ZyYWdtZW50JyA6ICd2ZXJ0ZXgnXG4gICAgdmFyIGZpbGVzID0gcGFyc2VTb3VyY2Uoc291cmNlLCBjb21tYW5kKVxuICAgIHZhciBlcnJvcnMgPSBwYXJzZUVycm9yTG9nKGVyckxvZylcbiAgICBhbm5vdGF0ZUZpbGVzKGZpbGVzLCBlcnJvcnMpXG5cbiAgICBPYmplY3Qua2V5cyhmaWxlcykuZm9yRWFjaChmdW5jdGlvbiAoZmlsZU51bWJlcikge1xuICAgICAgdmFyIGZpbGUgPSBmaWxlc1tmaWxlTnVtYmVyXVxuICAgICAgaWYgKCFmaWxlLmhhc0Vycm9ycykge1xuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgdmFyIHN0cmluZ3MgPSBbJyddXG4gICAgICB2YXIgc3R5bGVzID0gWycnXVxuXG4gICAgICBmdW5jdGlvbiBwdXNoIChzdHIsIHN0eWxlKSB7XG4gICAgICAgIHN0cmluZ3MucHVzaChzdHIpXG4gICAgICAgIHN0eWxlcy5wdXNoKHN0eWxlIHx8ICcnKVxuICAgICAgfVxuXG4gICAgICBwdXNoKCdmaWxlIG51bWJlciAnICsgZmlsZU51bWJlciArICc6ICcgKyBmaWxlLm5hbWUgKyAnXFxuJywgJ2NvbG9yOnJlZDt0ZXh0LWRlY29yYXRpb246dW5kZXJsaW5lO2ZvbnQtd2VpZ2h0OmJvbGQnKVxuXG4gICAgICBmaWxlLmxpbmVzLmZvckVhY2goZnVuY3Rpb24gKGxpbmUpIHtcbiAgICAgICAgaWYgKGxpbmUuZXJyb3JzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBwdXNoKGxlZnRQYWQobGluZS5udW1iZXIsIDQpICsgJ3wgICcsICdiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgZm9udC13ZWlnaHQ6Ym9sZCcpXG4gICAgICAgICAgcHVzaChsaW5lLmxpbmUgKyAnXFxuJywgJ2NvbG9yOnJlZDsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IGZvbnQtd2VpZ2h0OmJvbGQnKVxuXG4gICAgICAgICAgLy8gdHJ5IHRvIGd1ZXNzIHRva2VuXG4gICAgICAgICAgdmFyIG9mZnNldCA9IDBcbiAgICAgICAgICBsaW5lLmVycm9ycy5mb3JFYWNoKGZ1bmN0aW9uIChlcnJvcikge1xuICAgICAgICAgICAgdmFyIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlXG4gICAgICAgICAgICB2YXIgdG9rZW4gPSAvXlxccypcXCcoLiopXFwnXFxzKlxcOlxccyooLiopJC8uZXhlYyhtZXNzYWdlKVxuICAgICAgICAgICAgaWYgKHRva2VuKSB7XG4gICAgICAgICAgICAgIHZhciB0b2tlblBhdCA9IHRva2VuWzFdXG4gICAgICAgICAgICAgIG1lc3NhZ2UgPSB0b2tlblsyXVxuICAgICAgICAgICAgICBzd2l0Y2ggKHRva2VuUGF0KSB7XG4gICAgICAgICAgICAgICAgY2FzZSAnYXNzaWduJzpcbiAgICAgICAgICAgICAgICAgIHRva2VuUGF0ID0gJz0nXG4gICAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIG9mZnNldCA9IE1hdGgubWF4KGxpbmUubGluZS5pbmRleE9mKHRva2VuUGF0LCBvZmZzZXQpLCAwKVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgb2Zmc2V0ID0gMFxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBwdXNoKGxlZnRQYWQoJ3wgJywgNikpXG4gICAgICAgICAgICBwdXNoKGxlZnRQYWQoJ15eXicsIG9mZnNldCArIDMpICsgJ1xcbicsICdmb250LXdlaWdodDpib2xkJylcbiAgICAgICAgICAgIHB1c2gobGVmdFBhZCgnfCAnLCA2KSlcbiAgICAgICAgICAgIHB1c2gobWVzc2FnZSArICdcXG4nLCAnZm9udC13ZWlnaHQ6Ym9sZCcpXG4gICAgICAgICAgfSlcbiAgICAgICAgICBwdXNoKGxlZnRQYWQoJ3wgJywgNikgKyAnXFxuJylcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBwdXNoKGxlZnRQYWQobGluZS5udW1iZXIsIDQpICsgJ3wgICcpXG4gICAgICAgICAgcHVzaChsaW5lLmxpbmUgKyAnXFxuJywgJ2NvbG9yOnJlZCcpXG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgICBpZiAodHlwZW9mIGRvY3VtZW50ICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgICBzdHlsZXNbMF0gPSBzdHJpbmdzLmpvaW4oJyVjJylcbiAgICAgICAgY29uc29sZS5sb2cuYXBwbHkoY29uc29sZSwgc3R5bGVzKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc29sZS5sb2coc3RyaW5ncy5qb2luKCcnKSlcbiAgICAgIH1cbiAgICB9KVxuXG4gICAgY2hlY2sucmFpc2UoJ0Vycm9yIGNvbXBpbGluZyAnICsgdHlwZU5hbWUgKyAnIHNoYWRlciwgJyArIGZpbGVzWzBdLm5hbWUpXG4gIH1cbn1cblxuZnVuY3Rpb24gY2hlY2tMaW5rRXJyb3IgKGdsLCBwcm9ncmFtLCBmcmFnU2hhZGVyLCB2ZXJ0U2hhZGVyLCBjb21tYW5kKSB7XG4gIGlmICghZ2wuZ2V0UHJvZ3JhbVBhcmFtZXRlcihwcm9ncmFtLCBnbC5MSU5LX1NUQVRVUykpIHtcbiAgICB2YXIgZXJyTG9nID0gZ2wuZ2V0UHJvZ3JhbUluZm9Mb2cocHJvZ3JhbSlcbiAgICB2YXIgZnJhZ1BhcnNlID0gcGFyc2VTb3VyY2UoZnJhZ1NoYWRlciwgY29tbWFuZClcbiAgICB2YXIgdmVydFBhcnNlID0gcGFyc2VTb3VyY2UodmVydFNoYWRlciwgY29tbWFuZClcblxuICAgIHZhciBoZWFkZXIgPSAnRXJyb3IgbGlua2luZyBwcm9ncmFtIHdpdGggdmVydGV4IHNoYWRlciwgXCInICtcbiAgICAgIHZlcnRQYXJzZVswXS5uYW1lICsgJ1wiLCBhbmQgZnJhZ21lbnQgc2hhZGVyIFwiJyArIGZyYWdQYXJzZVswXS5uYW1lICsgJ1wiJ1xuXG4gICAgaWYgKHR5cGVvZiBkb2N1bWVudCAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIGNvbnNvbGUubG9nKCclYycgKyBoZWFkZXIgKyAnXFxuJWMnICsgZXJyTG9nLFxuICAgICAgICAnY29sb3I6cmVkO3RleHQtZGVjb3JhdGlvbjp1bmRlcmxpbmU7Zm9udC13ZWlnaHQ6Ym9sZCcsXG4gICAgICAgICdjb2xvcjpyZWQnKVxuICAgIH0gZWxzZSB7XG4gICAgICBjb25zb2xlLmxvZyhoZWFkZXIgKyAnXFxuJyArIGVyckxvZylcbiAgICB9XG4gICAgY2hlY2sucmFpc2UoaGVhZGVyKVxuICB9XG59XG5cbmZ1bmN0aW9uIHNhdmVDb21tYW5kUmVmIChvYmplY3QpIHtcbiAgb2JqZWN0Ll9jb21tYW5kUmVmID0gZ3Vlc3NDb21tYW5kKClcbn1cblxuZnVuY3Rpb24gc2F2ZURyYXdDb21tYW5kSW5mbyAob3B0cywgdW5pZm9ybXMsIGF0dHJpYnV0ZXMsIHN0cmluZ1N0b3JlKSB7XG4gIHNhdmVDb21tYW5kUmVmKG9wdHMpXG5cbiAgZnVuY3Rpb24gaWQgKHN0cikge1xuICAgIGlmIChzdHIpIHtcbiAgICAgIHJldHVybiBzdHJpbmdTdG9yZS5pZChzdHIpXG4gICAgfVxuICAgIHJldHVybiAwXG4gIH1cbiAgb3B0cy5fZnJhZ0lkID0gaWQob3B0cy5zdGF0aWMuZnJhZylcbiAgb3B0cy5fdmVydElkID0gaWQob3B0cy5zdGF0aWMudmVydClcblxuICBmdW5jdGlvbiBhZGRQcm9wcyAoZGljdCwgc2V0KSB7XG4gICAgT2JqZWN0LmtleXMoc2V0KS5mb3JFYWNoKGZ1bmN0aW9uICh1KSB7XG4gICAgICBkaWN0W3N0cmluZ1N0b3JlLmlkKHUpXSA9IHRydWVcbiAgICB9KVxuICB9XG5cbiAgdmFyIHVuaWZvcm1TZXQgPSBvcHRzLl91bmlmb3JtU2V0ID0ge31cbiAgYWRkUHJvcHModW5pZm9ybVNldCwgdW5pZm9ybXMuc3RhdGljKVxuICBhZGRQcm9wcyh1bmlmb3JtU2V0LCB1bmlmb3Jtcy5keW5hbWljKVxuXG4gIHZhciBhdHRyaWJ1dGVTZXQgPSBvcHRzLl9hdHRyaWJ1dGVTZXQgPSB7fVxuICBhZGRQcm9wcyhhdHRyaWJ1dGVTZXQsIGF0dHJpYnV0ZXMuc3RhdGljKVxuICBhZGRQcm9wcyhhdHRyaWJ1dGVTZXQsIGF0dHJpYnV0ZXMuZHluYW1pYylcblxuICBvcHRzLl9oYXNDb3VudCA9IChcbiAgICAnY291bnQnIGluIG9wdHMuc3RhdGljIHx8XG4gICAgJ2NvdW50JyBpbiBvcHRzLmR5bmFtaWMgfHxcbiAgICAnZWxlbWVudHMnIGluIG9wdHMuc3RhdGljIHx8XG4gICAgJ2VsZW1lbnRzJyBpbiBvcHRzLmR5bmFtaWMpXG59XG5cbmZ1bmN0aW9uIGNvbW1hbmRSYWlzZSAobWVzc2FnZSwgY29tbWFuZCkge1xuICB2YXIgY2FsbFNpdGUgPSBndWVzc0NhbGxTaXRlKClcbiAgcmFpc2UobWVzc2FnZSArXG4gICAgJyBpbiBjb21tYW5kICcgKyAoY29tbWFuZCB8fCBndWVzc0NvbW1hbmQoKSkgK1xuICAgIChjYWxsU2l0ZSA9PT0gJ3Vua25vd24nID8gJycgOiAnIGNhbGxlZCBmcm9tICcgKyBjYWxsU2l0ZSkpXG59XG5cbmZ1bmN0aW9uIGNoZWNrQ29tbWFuZCAocHJlZCwgbWVzc2FnZSwgY29tbWFuZCkge1xuICBpZiAoIXByZWQpIHtcbiAgICBjb21tYW5kUmFpc2UobWVzc2FnZSwgY29tbWFuZCB8fCBndWVzc0NvbW1hbmQoKSlcbiAgfVxufVxuXG5mdW5jdGlvbiBjaGVja1BhcmFtZXRlckNvbW1hbmQgKHBhcmFtLCBwb3NzaWJpbGl0aWVzLCBtZXNzYWdlLCBjb21tYW5kKSB7XG4gIGlmICghKHBhcmFtIGluIHBvc3NpYmlsaXRpZXMpKSB7XG4gICAgY29tbWFuZFJhaXNlKFxuICAgICAgJ3Vua25vd24gcGFyYW1ldGVyICgnICsgcGFyYW0gKyAnKScgKyBlbmNvbG9uKG1lc3NhZ2UpICtcbiAgICAgICcuIHBvc3NpYmxlIHZhbHVlczogJyArIE9iamVjdC5rZXlzKHBvc3NpYmlsaXRpZXMpLmpvaW4oKSxcbiAgICAgIGNvbW1hbmQgfHwgZ3Vlc3NDb21tYW5kKCkpXG4gIH1cbn1cblxuZnVuY3Rpb24gY2hlY2tDb21tYW5kVHlwZSAodmFsdWUsIHR5cGUsIG1lc3NhZ2UsIGNvbW1hbmQpIHtcbiAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gdHlwZSkge1xuICAgIGNvbW1hbmRSYWlzZShcbiAgICAgICdpbnZhbGlkIHBhcmFtZXRlciB0eXBlJyArIGVuY29sb24obWVzc2FnZSkgK1xuICAgICAgJy4gZXhwZWN0ZWQgJyArIHR5cGUgKyAnLCBnb3QgJyArICh0eXBlb2YgdmFsdWUpLFxuICAgICAgY29tbWFuZCB8fCBndWVzc0NvbW1hbmQoKSlcbiAgfVxufVxuXG5mdW5jdGlvbiBjaGVja09wdGlvbmFsIChibG9jaykge1xuICBibG9jaygpXG59XG5cbmZ1bmN0aW9uIGNoZWNrRnJhbWVidWZmZXJGb3JtYXQgKGF0dGFjaG1lbnQsIHRleEZvcm1hdHMsIHJiRm9ybWF0cykge1xuICBpZiAoYXR0YWNobWVudC50ZXh0dXJlKSB7XG4gICAgY2hlY2tPbmVPZihcbiAgICAgIGF0dGFjaG1lbnQudGV4dHVyZS5fdGV4dHVyZS5pbnRlcm5hbGZvcm1hdCxcbiAgICAgIHRleEZvcm1hdHMsXG4gICAgICAndW5zdXBwb3J0ZWQgdGV4dHVyZSBmb3JtYXQgZm9yIGF0dGFjaG1lbnQnKVxuICB9IGVsc2Uge1xuICAgIGNoZWNrT25lT2YoXG4gICAgICBhdHRhY2htZW50LnJlbmRlcmJ1ZmZlci5fcmVuZGVyYnVmZmVyLmZvcm1hdCxcbiAgICAgIHJiRm9ybWF0cyxcbiAgICAgICd1bnN1cHBvcnRlZCByZW5kZXJidWZmZXIgZm9ybWF0IGZvciBhdHRhY2htZW50JylcbiAgfVxufVxuXG52YXIgR0xfQ0xBTVBfVE9fRURHRSA9IDB4ODEyRlxuXG52YXIgR0xfTkVBUkVTVCA9IDB4MjYwMFxudmFyIEdMX05FQVJFU1RfTUlQTUFQX05FQVJFU1QgPSAweDI3MDBcbnZhciBHTF9MSU5FQVJfTUlQTUFQX05FQVJFU1QgPSAweDI3MDFcbnZhciBHTF9ORUFSRVNUX01JUE1BUF9MSU5FQVIgPSAweDI3MDJcbnZhciBHTF9MSU5FQVJfTUlQTUFQX0xJTkVBUiA9IDB4MjcwM1xuXG52YXIgR0xfQllURSA9IDUxMjBcbnZhciBHTF9VTlNJR05FRF9CWVRFID0gNTEyMVxudmFyIEdMX1NIT1JUID0gNTEyMlxudmFyIEdMX1VOU0lHTkVEX1NIT1JUID0gNTEyM1xudmFyIEdMX0lOVCA9IDUxMjRcbnZhciBHTF9VTlNJR05FRF9JTlQgPSA1MTI1XG52YXIgR0xfRkxPQVQgPSA1MTI2XG5cbnZhciBHTF9VTlNJR05FRF9TSE9SVF80XzRfNF80ID0gMHg4MDMzXG52YXIgR0xfVU5TSUdORURfU0hPUlRfNV81XzVfMSA9IDB4ODAzNFxudmFyIEdMX1VOU0lHTkVEX1NIT1JUXzVfNl81ID0gMHg4MzYzXG52YXIgR0xfVU5TSUdORURfSU5UXzI0XzhfV0VCR0wgPSAweDg0RkFcblxudmFyIEdMX0hBTEZfRkxPQVRfT0VTID0gMHg4RDYxXG5cbnZhciBUWVBFX1NJWkUgPSB7fVxuXG5UWVBFX1NJWkVbR0xfQllURV0gPVxuVFlQRV9TSVpFW0dMX1VOU0lHTkVEX0JZVEVdID0gMVxuXG5UWVBFX1NJWkVbR0xfU0hPUlRdID1cblRZUEVfU0laRVtHTF9VTlNJR05FRF9TSE9SVF0gPVxuVFlQRV9TSVpFW0dMX0hBTEZfRkxPQVRfT0VTXSA9XG5UWVBFX1NJWkVbR0xfVU5TSUdORURfU0hPUlRfNV82XzVdID1cblRZUEVfU0laRVtHTF9VTlNJR05FRF9TSE9SVF80XzRfNF80XSA9XG5UWVBFX1NJWkVbR0xfVU5TSUdORURfU0hPUlRfNV81XzVfMV0gPSAyXG5cblRZUEVfU0laRVtHTF9JTlRdID1cblRZUEVfU0laRVtHTF9VTlNJR05FRF9JTlRdID1cblRZUEVfU0laRVtHTF9GTE9BVF0gPVxuVFlQRV9TSVpFW0dMX1VOU0lHTkVEX0lOVF8yNF84X1dFQkdMXSA9IDRcblxuZnVuY3Rpb24gcGl4ZWxTaXplICh0eXBlLCBjaGFubmVscykge1xuICBpZiAodHlwZSA9PT0gR0xfVU5TSUdORURfU0hPUlRfNV81XzVfMSB8fFxuICAgICAgdHlwZSA9PT0gR0xfVU5TSUdORURfU0hPUlRfNF80XzRfNCB8fFxuICAgICAgdHlwZSA9PT0gR0xfVU5TSUdORURfU0hPUlRfNV82XzUpIHtcbiAgICByZXR1cm4gMlxuICB9IGVsc2UgaWYgKHR5cGUgPT09IEdMX1VOU0lHTkVEX0lOVF8yNF84X1dFQkdMKSB7XG4gICAgcmV0dXJuIDRcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gVFlQRV9TSVpFW3R5cGVdICogY2hhbm5lbHNcbiAgfVxufVxuXG5mdW5jdGlvbiBpc1BvdzIgKHYpIHtcbiAgcmV0dXJuICEodiAmICh2IC0gMSkpICYmICghIXYpXG59XG5cbmZ1bmN0aW9uIGNoZWNrVGV4dHVyZTJEIChpbmZvLCBtaXBEYXRhLCBsaW1pdHMpIHtcbiAgdmFyIGlcbiAgdmFyIHcgPSBtaXBEYXRhLndpZHRoXG4gIHZhciBoID0gbWlwRGF0YS5oZWlnaHRcbiAgdmFyIGMgPSBtaXBEYXRhLmNoYW5uZWxzXG5cbiAgLy8gQ2hlY2sgdGV4dHVyZSBzaGFwZVxuICBjaGVjayh3ID4gMCAmJiB3IDw9IGxpbWl0cy5tYXhUZXh0dXJlU2l6ZSAmJlxuICAgICAgICBoID4gMCAmJiBoIDw9IGxpbWl0cy5tYXhUZXh0dXJlU2l6ZSxcbiAgICAgICAgJ2ludmFsaWQgdGV4dHVyZSBzaGFwZScpXG5cbiAgLy8gY2hlY2sgd3JhcCBtb2RlXG4gIGlmIChpbmZvLndyYXBTICE9PSBHTF9DTEFNUF9UT19FREdFIHx8IGluZm8ud3JhcFQgIT09IEdMX0NMQU1QX1RPX0VER0UpIHtcbiAgICBjaGVjayhpc1BvdzIodykgJiYgaXNQb3cyKGgpLFxuICAgICAgJ2luY29tcGF0aWJsZSB3cmFwIG1vZGUgZm9yIHRleHR1cmUsIGJvdGggd2lkdGggYW5kIGhlaWdodCBtdXN0IGJlIHBvd2VyIG9mIDInKVxuICB9XG5cbiAgaWYgKG1pcERhdGEubWlwbWFzayA9PT0gMSkge1xuICAgIGlmICh3ICE9PSAxICYmIGggIT09IDEpIHtcbiAgICAgIGNoZWNrKFxuICAgICAgICBpbmZvLm1pbkZpbHRlciAhPT0gR0xfTkVBUkVTVF9NSVBNQVBfTkVBUkVTVCAmJlxuICAgICAgICBpbmZvLm1pbkZpbHRlciAhPT0gR0xfTkVBUkVTVF9NSVBNQVBfTElORUFSICYmXG4gICAgICAgIGluZm8ubWluRmlsdGVyICE9PSBHTF9MSU5FQVJfTUlQTUFQX05FQVJFU1QgJiZcbiAgICAgICAgaW5mby5taW5GaWx0ZXIgIT09IEdMX0xJTkVBUl9NSVBNQVBfTElORUFSLFxuICAgICAgICAnbWluIGZpbHRlciByZXF1aXJlcyBtaXBtYXAnKVxuICAgIH1cbiAgfSBlbHNlIHtcbiAgICAvLyB0ZXh0dXJlIG11c3QgYmUgcG93ZXIgb2YgMlxuICAgIGNoZWNrKGlzUG93Mih3KSAmJiBpc1BvdzIoaCksXG4gICAgICAndGV4dHVyZSBtdXN0IGJlIGEgc3F1YXJlIHBvd2VyIG9mIDIgdG8gc3VwcG9ydCBtaXBtYXBwaW5nJylcbiAgICBjaGVjayhtaXBEYXRhLm1pcG1hc2sgPT09ICh3IDw8IDEpIC0gMSxcbiAgICAgICdtaXNzaW5nIG9yIGluY29tcGxldGUgbWlwbWFwIGRhdGEnKVxuICB9XG5cbiAgaWYgKG1pcERhdGEudHlwZSA9PT0gR0xfRkxPQVQpIHtcbiAgICBpZiAobGltaXRzLmV4dGVuc2lvbnMuaW5kZXhPZignb2VzX3RleHR1cmVfZmxvYXRfbGluZWFyJykgPCAwKSB7XG4gICAgICBjaGVjayhpbmZvLm1pbkZpbHRlciA9PT0gR0xfTkVBUkVTVCAmJiBpbmZvLm1hZ0ZpbHRlciA9PT0gR0xfTkVBUkVTVCxcbiAgICAgICAgJ2ZpbHRlciBub3Qgc3VwcG9ydGVkLCBtdXN0IGVuYWJsZSBvZXNfdGV4dHVyZV9mbG9hdF9saW5lYXInKVxuICAgIH1cbiAgICBjaGVjayghaW5mby5nZW5NaXBtYXBzLFxuICAgICAgJ21pcG1hcCBnZW5lcmF0aW9uIG5vdCBzdXBwb3J0ZWQgd2l0aCBmbG9hdCB0ZXh0dXJlcycpXG4gIH1cblxuICAvLyBjaGVjayBpbWFnZSBjb21wbGV0ZVxuICB2YXIgbWlwaW1hZ2VzID0gbWlwRGF0YS5pbWFnZXNcbiAgZm9yIChpID0gMDsgaSA8IDE2OyArK2kpIHtcbiAgICBpZiAobWlwaW1hZ2VzW2ldKSB7XG4gICAgICB2YXIgbXcgPSB3ID4+IGlcbiAgICAgIHZhciBtaCA9IGggPj4gaVxuICAgICAgY2hlY2sobWlwRGF0YS5taXBtYXNrICYgKDEgPDwgaSksICdtaXNzaW5nIG1pcG1hcCBkYXRhJylcblxuICAgICAgdmFyIGltZyA9IG1pcGltYWdlc1tpXVxuXG4gICAgICBjaGVjayhcbiAgICAgICAgaW1nLndpZHRoID09PSBtdyAmJlxuICAgICAgICBpbWcuaGVpZ2h0ID09PSBtaCxcbiAgICAgICAgJ2ludmFsaWQgc2hhcGUgZm9yIG1pcCBpbWFnZXMnKVxuXG4gICAgICBjaGVjayhcbiAgICAgICAgaW1nLmZvcm1hdCA9PT0gbWlwRGF0YS5mb3JtYXQgJiZcbiAgICAgICAgaW1nLmludGVybmFsZm9ybWF0ID09PSBtaXBEYXRhLmludGVybmFsZm9ybWF0ICYmXG4gICAgICAgIGltZy50eXBlID09PSBtaXBEYXRhLnR5cGUsXG4gICAgICAgICdpbmNvbXBhdGlibGUgdHlwZSBmb3IgbWlwIGltYWdlJylcblxuICAgICAgaWYgKGltZy5jb21wcmVzc2VkKSB7XG4gICAgICAgIC8vIFRPRE86IGNoZWNrIHNpemUgZm9yIGNvbXByZXNzZWQgaW1hZ2VzXG4gICAgICB9IGVsc2UgaWYgKGltZy5kYXRhKSB7XG4gICAgICAgIGNoZWNrKGltZy5kYXRhLmJ5dGVMZW5ndGggPT09IG13ICogbWggKlxuICAgICAgICAgIE1hdGgubWF4KHBpeGVsU2l6ZShpbWcudHlwZSwgYyksIGltZy51bnBhY2tBbGlnbm1lbnQpLFxuICAgICAgICAgICdpbnZhbGlkIGRhdGEgZm9yIGltYWdlLCBidWZmZXIgc2l6ZSBpcyBpbmNvbnNpc3RlbnQgd2l0aCBpbWFnZSBmb3JtYXQnKVxuICAgICAgfSBlbHNlIGlmIChpbWcuZWxlbWVudCkge1xuICAgICAgICAvLyBUT0RPOiBjaGVjayBlbGVtZW50IGNhbiBiZSBsb2FkZWRcbiAgICAgIH0gZWxzZSBpZiAoaW1nLmNvcHkpIHtcbiAgICAgICAgLy8gVE9ETzogY2hlY2sgY29tcGF0aWJsZSBmb3JtYXQgYW5kIHR5cGVcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKCFpbmZvLmdlbk1pcG1hcHMpIHtcbiAgICAgIGNoZWNrKChtaXBEYXRhLm1pcG1hc2sgJiAoMSA8PCBpKSkgPT09IDAsICdleHRyYSBtaXBtYXAgZGF0YScpXG4gICAgfVxuICB9XG5cbiAgaWYgKG1pcERhdGEuY29tcHJlc3NlZCkge1xuICAgIGNoZWNrKCFpbmZvLmdlbk1pcG1hcHMsXG4gICAgICAnbWlwbWFwIGdlbmVyYXRpb24gZm9yIGNvbXByZXNzZWQgaW1hZ2VzIG5vdCBzdXBwb3J0ZWQnKVxuICB9XG59XG5cbmZ1bmN0aW9uIGNoZWNrVGV4dHVyZUN1YmUgKHRleHR1cmUsIGluZm8sIGZhY2VzLCBsaW1pdHMpIHtcbiAgdmFyIHcgPSB0ZXh0dXJlLndpZHRoXG4gIHZhciBoID0gdGV4dHVyZS5oZWlnaHRcbiAgdmFyIGMgPSB0ZXh0dXJlLmNoYW5uZWxzXG5cbiAgLy8gQ2hlY2sgdGV4dHVyZSBzaGFwZVxuICBjaGVjayhcbiAgICB3ID4gMCAmJiB3IDw9IGxpbWl0cy5tYXhUZXh0dXJlU2l6ZSAmJiBoID4gMCAmJiBoIDw9IGxpbWl0cy5tYXhUZXh0dXJlU2l6ZSxcbiAgICAnaW52YWxpZCB0ZXh0dXJlIHNoYXBlJylcbiAgY2hlY2soXG4gICAgdyA9PT0gaCxcbiAgICAnY3ViZSBtYXAgbXVzdCBiZSBzcXVhcmUnKVxuICBjaGVjayhcbiAgICBpbmZvLndyYXBTID09PSBHTF9DTEFNUF9UT19FREdFICYmIGluZm8ud3JhcFQgPT09IEdMX0NMQU1QX1RPX0VER0UsXG4gICAgJ3dyYXAgbW9kZSBub3Qgc3VwcG9ydGVkIGJ5IGN1YmUgbWFwJylcblxuICBmb3IgKHZhciBpID0gMDsgaSA8IGZhY2VzLmxlbmd0aDsgKytpKSB7XG4gICAgdmFyIGZhY2UgPSBmYWNlc1tpXVxuICAgIGNoZWNrKFxuICAgICAgZmFjZS53aWR0aCA9PT0gdyAmJiBmYWNlLmhlaWdodCA9PT0gaCxcbiAgICAgICdpbmNvbnNpc3RlbnQgY3ViZSBtYXAgZmFjZSBzaGFwZScpXG5cbiAgICBpZiAoaW5mby5nZW5NaXBtYXBzKSB7XG4gICAgICBjaGVjayghZmFjZS5jb21wcmVzc2VkLFxuICAgICAgICAnY2FuIG5vdCBnZW5lcmF0ZSBtaXBtYXAgZm9yIGNvbXByZXNzZWQgdGV4dHVyZXMnKVxuICAgICAgY2hlY2soZmFjZS5taXBtYXNrID09PSAxLFxuICAgICAgICAnY2FuIG5vdCBzcGVjaWZ5IG1pcG1hcHMgYW5kIGdlbmVyYXRlIG1pcG1hcHMnKVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBUT0RPOiBjaGVjayBtaXAgYW5kIGZpbHRlciBtb2RlXG4gICAgfVxuXG4gICAgdmFyIG1pcG1hcHMgPSBmYWNlLmltYWdlc1xuICAgIGZvciAodmFyIGogPSAwOyBqIDwgMTY7ICsraikge1xuICAgICAgdmFyIGltZyA9IG1pcG1hcHNbal1cbiAgICAgIGlmIChpbWcpIHtcbiAgICAgICAgdmFyIG13ID0gdyA+PiBqXG4gICAgICAgIHZhciBtaCA9IGggPj4galxuICAgICAgICBjaGVjayhmYWNlLm1pcG1hc2sgJiAoMSA8PCBqKSwgJ21pc3NpbmcgbWlwbWFwIGRhdGEnKVxuICAgICAgICBjaGVjayhcbiAgICAgICAgICBpbWcud2lkdGggPT09IG13ICYmXG4gICAgICAgICAgaW1nLmhlaWdodCA9PT0gbWgsXG4gICAgICAgICAgJ2ludmFsaWQgc2hhcGUgZm9yIG1pcCBpbWFnZXMnKVxuICAgICAgICBjaGVjayhcbiAgICAgICAgICBpbWcuZm9ybWF0ID09PSB0ZXh0dXJlLmZvcm1hdCAmJlxuICAgICAgICAgIGltZy5pbnRlcm5hbGZvcm1hdCA9PT0gdGV4dHVyZS5pbnRlcm5hbGZvcm1hdCAmJlxuICAgICAgICAgIGltZy50eXBlID09PSB0ZXh0dXJlLnR5cGUsXG4gICAgICAgICAgJ2luY29tcGF0aWJsZSB0eXBlIGZvciBtaXAgaW1hZ2UnKVxuXG4gICAgICAgIGlmIChpbWcuY29tcHJlc3NlZCkge1xuICAgICAgICAgIC8vIFRPRE86IGNoZWNrIHNpemUgZm9yIGNvbXByZXNzZWQgaW1hZ2VzXG4gICAgICAgIH0gZWxzZSBpZiAoaW1nLmRhdGEpIHtcbiAgICAgICAgICBjaGVjayhpbWcuZGF0YS5ieXRlTGVuZ3RoID09PSBtdyAqIG1oICpcbiAgICAgICAgICAgIE1hdGgubWF4KHBpeGVsU2l6ZShpbWcudHlwZSwgYyksIGltZy51bnBhY2tBbGlnbm1lbnQpLFxuICAgICAgICAgICAgJ2ludmFsaWQgZGF0YSBmb3IgaW1hZ2UsIGJ1ZmZlciBzaXplIGlzIGluY29uc2lzdGVudCB3aXRoIGltYWdlIGZvcm1hdCcpXG4gICAgICAgIH0gZWxzZSBpZiAoaW1nLmVsZW1lbnQpIHtcbiAgICAgICAgICAvLyBUT0RPOiBjaGVjayBlbGVtZW50IGNhbiBiZSBsb2FkZWRcbiAgICAgICAgfSBlbHNlIGlmIChpbWcuY29weSkge1xuICAgICAgICAgIC8vIFRPRE86IGNoZWNrIGNvbXBhdGlibGUgZm9ybWF0IGFuZCB0eXBlXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBleHRlbmQoY2hlY2ssIHtcbiAgb3B0aW9uYWw6IGNoZWNrT3B0aW9uYWwsXG4gIHJhaXNlOiByYWlzZSxcbiAgY29tbWFuZFJhaXNlOiBjb21tYW5kUmFpc2UsXG4gIGNvbW1hbmQ6IGNoZWNrQ29tbWFuZCxcbiAgcGFyYW1ldGVyOiBjaGVja1BhcmFtZXRlcixcbiAgY29tbWFuZFBhcmFtZXRlcjogY2hlY2tQYXJhbWV0ZXJDb21tYW5kLFxuICBjb25zdHJ1Y3RvcjogY2hlY2tDb25zdHJ1Y3RvcixcbiAgdHlwZTogY2hlY2tUeXBlT2YsXG4gIGNvbW1hbmRUeXBlOiBjaGVja0NvbW1hbmRUeXBlLFxuICBpc1R5cGVkQXJyYXk6IGNoZWNrSXNUeXBlZEFycmF5LFxuICBubmk6IGNoZWNrTm9uTmVnYXRpdmVJbnQsXG4gIG9uZU9mOiBjaGVja09uZU9mLFxuICBzaGFkZXJFcnJvcjogY2hlY2tTaGFkZXJFcnJvcixcbiAgbGlua0Vycm9yOiBjaGVja0xpbmtFcnJvcixcbiAgY2FsbFNpdGU6IGd1ZXNzQ2FsbFNpdGUsXG4gIHNhdmVDb21tYW5kUmVmOiBzYXZlQ29tbWFuZFJlZixcbiAgc2F2ZURyYXdJbmZvOiBzYXZlRHJhd0NvbW1hbmRJbmZvLFxuICBmcmFtZWJ1ZmZlckZvcm1hdDogY2hlY2tGcmFtZWJ1ZmZlckZvcm1hdCxcbiAgZ3Vlc3NDb21tYW5kOiBndWVzc0NvbW1hbmQsXG4gIHRleHR1cmUyRDogY2hlY2tUZXh0dXJlMkQsXG4gIHRleHR1cmVDdWJlOiBjaGVja1RleHR1cmVDdWJlXG59KVxuIiwiLyogZ2xvYmFscyBwZXJmb3JtYW5jZSAqL1xubW9kdWxlLmV4cG9ydHMgPVxuICAodHlwZW9mIHBlcmZvcm1hbmNlICE9PSAndW5kZWZpbmVkJyAmJiBwZXJmb3JtYW5jZS5ub3cpXG4gID8gZnVuY3Rpb24gKCkgeyByZXR1cm4gcGVyZm9ybWFuY2Uubm93KCkgfVxuICA6IGZ1bmN0aW9uICgpIHsgcmV0dXJuICsobmV3IERhdGUoKSkgfVxuIiwidmFyIGV4dGVuZCA9IHJlcXVpcmUoJy4vZXh0ZW5kJylcblxuZnVuY3Rpb24gc2xpY2UgKHgpIHtcbiAgcmV0dXJuIEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKHgpXG59XG5cbmZ1bmN0aW9uIGpvaW4gKHgpIHtcbiAgcmV0dXJuIHNsaWNlKHgpLmpvaW4oJycpXG59XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gY3JlYXRlRW52aXJvbm1lbnQgKCkge1xuICAvLyBVbmlxdWUgdmFyaWFibGUgaWQgY291bnRlclxuICB2YXIgdmFyQ291bnRlciA9IDBcblxuICAvLyBMaW5rZWQgdmFsdWVzIGFyZSBwYXNzZWQgZnJvbSB0aGlzIHNjb3BlIGludG8gdGhlIGdlbmVyYXRlZCBjb2RlIGJsb2NrXG4gIC8vIENhbGxpbmcgbGluaygpIHBhc3NlcyBhIHZhbHVlIGludG8gdGhlIGdlbmVyYXRlZCBzY29wZSBhbmQgcmV0dXJuc1xuICAvLyB0aGUgdmFyaWFibGUgbmFtZSB3aGljaCBpdCBpcyBib3VuZCB0b1xuICB2YXIgbGlua2VkTmFtZXMgPSBbXVxuICB2YXIgbGlua2VkVmFsdWVzID0gW11cbiAgZnVuY3Rpb24gbGluayAodmFsdWUpIHtcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxpbmtlZFZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgaWYgKGxpbmtlZFZhbHVlc1tpXSA9PT0gdmFsdWUpIHtcbiAgICAgICAgcmV0dXJuIGxpbmtlZE5hbWVzW2ldXG4gICAgICB9XG4gICAgfVxuXG4gICAgdmFyIG5hbWUgPSAnZycgKyAodmFyQ291bnRlcisrKVxuICAgIGxpbmtlZE5hbWVzLnB1c2gobmFtZSlcbiAgICBsaW5rZWRWYWx1ZXMucHVzaCh2YWx1ZSlcbiAgICByZXR1cm4gbmFtZVxuICB9XG5cbiAgLy8gY3JlYXRlIGEgY29kZSBibG9ja1xuICBmdW5jdGlvbiBibG9jayAoKSB7XG4gICAgdmFyIGNvZGUgPSBbXVxuICAgIGZ1bmN0aW9uIHB1c2ggKCkge1xuICAgICAgY29kZS5wdXNoLmFwcGx5KGNvZGUsIHNsaWNlKGFyZ3VtZW50cykpXG4gICAgfVxuXG4gICAgdmFyIHZhcnMgPSBbXVxuICAgIGZ1bmN0aW9uIGRlZiAoKSB7XG4gICAgICB2YXIgbmFtZSA9ICd2JyArICh2YXJDb3VudGVyKyspXG4gICAgICB2YXJzLnB1c2gobmFtZSlcblxuICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNvZGUucHVzaChuYW1lLCAnPScpXG4gICAgICAgIGNvZGUucHVzaC5hcHBseShjb2RlLCBzbGljZShhcmd1bWVudHMpKVxuICAgICAgICBjb2RlLnB1c2goJzsnKVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gbmFtZVxuICAgIH1cblxuICAgIHJldHVybiBleHRlbmQocHVzaCwge1xuICAgICAgZGVmOiBkZWYsXG4gICAgICB0b1N0cmluZzogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gam9pbihbXG4gICAgICAgICAgKHZhcnMubGVuZ3RoID4gMCA/ICd2YXIgJyArIHZhcnMgKyAnOycgOiAnJyksXG4gICAgICAgICAgam9pbihjb2RlKVxuICAgICAgICBdKVxuICAgICAgfVxuICAgIH0pXG4gIH1cblxuICBmdW5jdGlvbiBzY29wZSAoKSB7XG4gICAgdmFyIGVudHJ5ID0gYmxvY2soKVxuICAgIHZhciBleGl0ID0gYmxvY2soKVxuXG4gICAgdmFyIGVudHJ5VG9TdHJpbmcgPSBlbnRyeS50b1N0cmluZ1xuICAgIHZhciBleGl0VG9TdHJpbmcgPSBleGl0LnRvU3RyaW5nXG5cbiAgICBmdW5jdGlvbiBzYXZlIChvYmplY3QsIHByb3ApIHtcbiAgICAgIGV4aXQob2JqZWN0LCBwcm9wLCAnPScsIGVudHJ5LmRlZihvYmplY3QsIHByb3ApLCAnOycpXG4gICAgfVxuXG4gICAgcmV0dXJuIGV4dGVuZChlbnRyeSwge1xuICAgICAgZW50cnk6IGVudHJ5LFxuICAgICAgZXhpdDogZXhpdCxcbiAgICAgIHNhdmU6IHNhdmUsXG4gICAgICBzZXQ6IGZ1bmN0aW9uIChvYmplY3QsIHByb3AsIHZhbHVlKSB7XG4gICAgICAgIHNhdmUob2JqZWN0LCBwcm9wKVxuICAgICAgICBlbnRyeShvYmplY3QsIHByb3AsICc9JywgdmFsdWUsICc7JylcbiAgICAgIH0sXG4gICAgICB0b1N0cmluZzogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gZW50cnlUb1N0cmluZygpICsgZXhpdFRvU3RyaW5nKClcbiAgICAgIH1cbiAgICB9KVxuICB9XG5cbiAgZnVuY3Rpb24gY29uZGl0aW9uYWwgKCkge1xuICAgIHZhciBwcmVkID0gam9pbihhcmd1bWVudHMpXG4gICAgdmFyIHRoZW5CbG9jayA9IHNjb3BlKClcbiAgICB2YXIgZWxzZUJsb2NrID0gc2NvcGUoKVxuXG4gICAgdmFyIHRoZW5Ub1N0cmluZyA9IHRoZW5CbG9jay50b1N0cmluZ1xuICAgIHZhciBlbHNlVG9TdHJpbmcgPSBlbHNlQmxvY2sudG9TdHJpbmdcblxuICAgIHJldHVybiBleHRlbmQodGhlbkJsb2NrLCB7XG4gICAgICB0aGVuOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHRoZW5CbG9jay5hcHBseSh0aGVuQmxvY2ssIHNsaWNlKGFyZ3VtZW50cykpXG4gICAgICAgIHJldHVybiB0aGlzXG4gICAgICB9LFxuICAgICAgZWxzZTogZnVuY3Rpb24gKCkge1xuICAgICAgICBlbHNlQmxvY2suYXBwbHkoZWxzZUJsb2NrLCBzbGljZShhcmd1bWVudHMpKVxuICAgICAgICByZXR1cm4gdGhpc1xuICAgICAgfSxcbiAgICAgIHRvU3RyaW5nOiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHZhciBlbHNlQ2xhdXNlID0gZWxzZVRvU3RyaW5nKClcbiAgICAgICAgaWYgKGVsc2VDbGF1c2UpIHtcbiAgICAgICAgICBlbHNlQ2xhdXNlID0gJ2Vsc2V7JyArIGVsc2VDbGF1c2UgKyAnfSdcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gam9pbihbXG4gICAgICAgICAgJ2lmKCcsIHByZWQsICcpeycsXG4gICAgICAgICAgdGhlblRvU3RyaW5nKCksXG4gICAgICAgICAgJ30nLCBlbHNlQ2xhdXNlXG4gICAgICAgIF0pXG4gICAgICB9XG4gICAgfSlcbiAgfVxuXG4gIC8vIHByb2NlZHVyZSBsaXN0XG4gIHZhciBnbG9iYWxCbG9jayA9IGJsb2NrKClcbiAgdmFyIHByb2NlZHVyZXMgPSB7fVxuICBmdW5jdGlvbiBwcm9jIChuYW1lLCBjb3VudCkge1xuICAgIHZhciBhcmdzID0gW11cbiAgICBmdW5jdGlvbiBhcmcgKCkge1xuICAgICAgdmFyIG5hbWUgPSAnYScgKyBhcmdzLmxlbmd0aFxuICAgICAgYXJncy5wdXNoKG5hbWUpXG4gICAgICByZXR1cm4gbmFtZVxuICAgIH1cblxuICAgIGNvdW50ID0gY291bnQgfHwgMFxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY291bnQ7ICsraSkge1xuICAgICAgYXJnKClcbiAgICB9XG5cbiAgICB2YXIgYm9keSA9IHNjb3BlKClcbiAgICB2YXIgYm9keVRvU3RyaW5nID0gYm9keS50b1N0cmluZ1xuXG4gICAgdmFyIHJlc3VsdCA9IHByb2NlZHVyZXNbbmFtZV0gPSBleHRlbmQoYm9keSwge1xuICAgICAgYXJnOiBhcmcsXG4gICAgICB0b1N0cmluZzogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gam9pbihbXG4gICAgICAgICAgJ2Z1bmN0aW9uKCcsIGFyZ3Muam9pbigpLCAnKXsnLFxuICAgICAgICAgIGJvZHlUb1N0cmluZygpLFxuICAgICAgICAgICd9J1xuICAgICAgICBdKVxuICAgICAgfVxuICAgIH0pXG5cbiAgICByZXR1cm4gcmVzdWx0XG4gIH1cblxuICBmdW5jdGlvbiBjb21waWxlICgpIHtcbiAgICB2YXIgY29kZSA9IFsnXCJ1c2Ugc3RyaWN0XCI7JyxcbiAgICAgIGdsb2JhbEJsb2NrLFxuICAgICAgJ3JldHVybiB7J11cbiAgICBPYmplY3Qua2V5cyhwcm9jZWR1cmVzKS5mb3JFYWNoKGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgICBjb2RlLnB1c2goJ1wiJywgbmFtZSwgJ1wiOicsIHByb2NlZHVyZXNbbmFtZV0udG9TdHJpbmcoKSwgJywnKVxuICAgIH0pXG4gICAgY29kZS5wdXNoKCd9JylcbiAgICB2YXIgc3JjID0gam9pbihjb2RlKVxuICAgICAgLnJlcGxhY2UoLzsvZywgJztcXG4nKVxuICAgICAgLnJlcGxhY2UoL30vZywgJ31cXG4nKVxuICAgICAgLnJlcGxhY2UoL3svZywgJ3tcXG4nKVxuICAgIHZhciBwcm9jID0gRnVuY3Rpb24uYXBwbHkobnVsbCwgbGlua2VkTmFtZXMuY29uY2F0KHNyYykpXG4vLyAgICBjb25zb2xlLmxvZygnc3JjOiAnLCBzcmMpXG4gICAgcmV0dXJuIHByb2MuYXBwbHkobnVsbCwgbGlua2VkVmFsdWVzKVxuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBnbG9iYWw6IGdsb2JhbEJsb2NrLFxuICAgIGxpbms6IGxpbmssXG4gICAgYmxvY2s6IGJsb2NrLFxuICAgIHByb2M6IHByb2MsXG4gICAgc2NvcGU6IHNjb3BlLFxuICAgIGNvbmQ6IGNvbmRpdGlvbmFsLFxuICAgIGNvbXBpbGU6IGNvbXBpbGVcbiAgfVxufVxuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoYmFzZSwgb3B0cykge1xuICB2YXIga2V5cyA9IE9iamVjdC5rZXlzKG9wdHMpXG4gIGZvciAodmFyIGkgPSAwOyBpIDwga2V5cy5sZW5ndGg7ICsraSkge1xuICAgIGJhc2Vba2V5c1tpXV0gPSBvcHRzW2tleXNbaV1dXG4gIH1cbiAgcmV0dXJuIGJhc2Vcbn1cbiIsInZhciBpc1R5cGVkQXJyYXkgPSByZXF1aXJlKCcuL2lzLXR5cGVkLWFycmF5Jylcbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gaXNBcnJheUxpa2UgKHMpIHtcbiAgcmV0dXJuIEFycmF5LmlzQXJyYXkocykgfHwgaXNUeXBlZEFycmF5KHMpXG59XG4iLCJ2YXIgaXNUeXBlZEFycmF5ID0gcmVxdWlyZSgnLi9pcy10eXBlZC1hcnJheScpXG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gaXNOREFycmF5TGlrZSAob2JqKSB7XG4gIHJldHVybiAoXG4gICAgISFvYmogJiZcbiAgICB0eXBlb2Ygb2JqID09PSAnb2JqZWN0JyAmJlxuICAgIEFycmF5LmlzQXJyYXkob2JqLnNoYXBlKSAmJlxuICAgIEFycmF5LmlzQXJyYXkob2JqLnN0cmlkZSkgJiZcbiAgICB0eXBlb2Ygb2JqLm9mZnNldCA9PT0gJ251bWJlcicgJiZcbiAgICBvYmouc2hhcGUubGVuZ3RoID09PSBvYmouc3RyaWRlLmxlbmd0aCAmJlxuICAgIChBcnJheS5pc0FycmF5KG9iai5kYXRhKSB8fFxuICAgICAgaXNUeXBlZEFycmF5KG9iai5kYXRhKSkpXG59XG4iLCJ2YXIgZHR5cGVzID0gcmVxdWlyZSgnLi4vY29uc3RhbnRzL2FycmF5dHlwZXMuanNvbicpXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uICh4KSB7XG4gIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoeCkgaW4gZHR5cGVzXG59XG4iLCJtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIGxvb3AgKG4sIGYpIHtcbiAgdmFyIHJlc3VsdCA9IEFycmF5KG4pXG4gIGZvciAodmFyIGkgPSAwOyBpIDwgbjsgKytpKSB7XG4gICAgcmVzdWx0W2ldID0gZihpKVxuICB9XG4gIHJldHVybiByZXN1bHRcbn1cbiIsInZhciBsb29wID0gcmVxdWlyZSgnLi9sb29wJylcblxudmFyIEdMX0JZVEUgPSA1MTIwXG52YXIgR0xfVU5TSUdORURfQllURSA9IDUxMjFcbnZhciBHTF9TSE9SVCA9IDUxMjJcbnZhciBHTF9VTlNJR05FRF9TSE9SVCA9IDUxMjNcbnZhciBHTF9JTlQgPSA1MTI0XG52YXIgR0xfVU5TSUdORURfSU5UID0gNTEyNVxudmFyIEdMX0ZMT0FUID0gNTEyNlxuXG52YXIgYnVmZmVyUG9vbCA9IGxvb3AoOCwgZnVuY3Rpb24gKCkge1xuICByZXR1cm4gW11cbn0pXG5cbmZ1bmN0aW9uIG5leHRQb3cxNiAodikge1xuICBmb3IgKHZhciBpID0gMTY7IGkgPD0gKDEgPDwgMjgpOyBpICo9IDE2KSB7XG4gICAgaWYgKHYgPD0gaSkge1xuICAgICAgcmV0dXJuIGlcbiAgICB9XG4gIH1cbiAgcmV0dXJuIDBcbn1cblxuZnVuY3Rpb24gbG9nMiAodikge1xuICB2YXIgciwgc2hpZnRcbiAgciA9ICh2ID4gMHhGRkZGKSA8PCA0XG4gIHYgPj4+PSByXG4gIHNoaWZ0ID0gKHYgPiAweEZGKSA8PCAzXG4gIHYgPj4+PSBzaGlmdDsgciB8PSBzaGlmdFxuICBzaGlmdCA9ICh2ID4gMHhGKSA8PCAyXG4gIHYgPj4+PSBzaGlmdDsgciB8PSBzaGlmdFxuICBzaGlmdCA9ICh2ID4gMHgzKSA8PCAxXG4gIHYgPj4+PSBzaGlmdDsgciB8PSBzaGlmdFxuICByZXR1cm4gciB8ICh2ID4+IDEpXG59XG5cbmZ1bmN0aW9uIGFsbG9jIChuKSB7XG4gIHZhciBzeiA9IG5leHRQb3cxNihuKVxuICB2YXIgYmluID0gYnVmZmVyUG9vbFtsb2cyKHN6KSA+PiAyXVxuICBpZiAoYmluLmxlbmd0aCA+IDApIHtcbiAgICByZXR1cm4gYmluLnBvcCgpXG4gIH1cbiAgcmV0dXJuIG5ldyBBcnJheUJ1ZmZlcihzeilcbn1cblxuZnVuY3Rpb24gZnJlZSAoYnVmKSB7XG4gIGJ1ZmZlclBvb2xbbG9nMihidWYuYnl0ZUxlbmd0aCkgPj4gMl0ucHVzaChidWYpXG59XG5cbmZ1bmN0aW9uIGFsbG9jVHlwZSAodHlwZSwgbikge1xuICB2YXIgcmVzdWx0ID0gbnVsbFxuICBzd2l0Y2ggKHR5cGUpIHtcbiAgICBjYXNlIEdMX0JZVEU6XG4gICAgICByZXN1bHQgPSBuZXcgSW50OEFycmF5KGFsbG9jKG4pLCAwLCBuKVxuICAgICAgYnJlYWtcbiAgICBjYXNlIEdMX1VOU0lHTkVEX0JZVEU6XG4gICAgICByZXN1bHQgPSBuZXcgVWludDhBcnJheShhbGxvYyhuKSwgMCwgbilcbiAgICAgIGJyZWFrXG4gICAgY2FzZSBHTF9TSE9SVDpcbiAgICAgIHJlc3VsdCA9IG5ldyBJbnQxNkFycmF5KGFsbG9jKDIgKiBuKSwgMCwgbilcbiAgICAgIGJyZWFrXG4gICAgY2FzZSBHTF9VTlNJR05FRF9TSE9SVDpcbiAgICAgIHJlc3VsdCA9IG5ldyBVaW50MTZBcnJheShhbGxvYygyICogbiksIDAsIG4pXG4gICAgICBicmVha1xuICAgIGNhc2UgR0xfSU5UOlxuICAgICAgcmVzdWx0ID0gbmV3IEludDMyQXJyYXkoYWxsb2MoNCAqIG4pLCAwLCBuKVxuICAgICAgYnJlYWtcbiAgICBjYXNlIEdMX1VOU0lHTkVEX0lOVDpcbiAgICAgIHJlc3VsdCA9IG5ldyBVaW50MzJBcnJheShhbGxvYyg0ICogbiksIDAsIG4pXG4gICAgICBicmVha1xuICAgIGNhc2UgR0xfRkxPQVQ6XG4gICAgICByZXN1bHQgPSBuZXcgRmxvYXQzMkFycmF5KGFsbG9jKDQgKiBuKSwgMCwgbilcbiAgICAgIGJyZWFrXG4gICAgZGVmYXVsdDpcbiAgICAgIHJldHVybiBudWxsXG4gIH1cbiAgaWYgKHJlc3VsdC5sZW5ndGggIT09IG4pIHtcbiAgICByZXR1cm4gcmVzdWx0LnN1YmFycmF5KDAsIG4pXG4gIH1cbiAgcmV0dXJuIHJlc3VsdFxufVxuXG5mdW5jdGlvbiBmcmVlVHlwZSAoYXJyYXkpIHtcbiAgZnJlZShhcnJheS5idWZmZXIpXG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBhbGxvYzogYWxsb2MsXG4gIGZyZWU6IGZyZWUsXG4gIGFsbG9jVHlwZTogYWxsb2NUeXBlLFxuICBmcmVlVHlwZTogZnJlZVR5cGVcbn1cbiIsIi8qIGdsb2JhbHMgcmVxdWVzdEFuaW1hdGlvbkZyYW1lLCBjYW5jZWxBbmltYXRpb25GcmFtZSAqL1xuaWYgKHR5cGVvZiByZXF1ZXN0QW5pbWF0aW9uRnJhbWUgPT09ICdmdW5jdGlvbicgJiZcbiAgICB0eXBlb2YgY2FuY2VsQW5pbWF0aW9uRnJhbWUgPT09ICdmdW5jdGlvbicpIHtcbiAgbW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgbmV4dDogZnVuY3Rpb24gKHgpIHsgcmV0dXJuIHJlcXVlc3RBbmltYXRpb25GcmFtZSh4KSB9LFxuICAgIGNhbmNlbDogZnVuY3Rpb24gKHgpIHsgcmV0dXJuIGNhbmNlbEFuaW1hdGlvbkZyYW1lKHgpIH1cbiAgfVxufSBlbHNlIHtcbiAgbW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgbmV4dDogZnVuY3Rpb24gKGNiKSB7XG4gICAgICByZXR1cm4gc2V0VGltZW91dChjYiwgMTYpXG4gICAgfSxcbiAgICBjYW5jZWw6IGNsZWFyVGltZW91dFxuICB9XG59XG4iLCJ2YXIgcG9vbCA9IHJlcXVpcmUoJy4vcG9vbCcpXG5cbnZhciBGTE9BVCA9IG5ldyBGbG9hdDMyQXJyYXkoMSlcbnZhciBJTlQgPSBuZXcgVWludDMyQXJyYXkoRkxPQVQuYnVmZmVyKVxuXG52YXIgR0xfVU5TSUdORURfU0hPUlQgPSA1MTIzXG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gY29udmVydFRvSGFsZkZsb2F0IChhcnJheSkge1xuICB2YXIgdXNob3J0cyA9IHBvb2wuYWxsb2NUeXBlKEdMX1VOU0lHTkVEX1NIT1JULCBhcnJheS5sZW5ndGgpXG5cbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBhcnJheS5sZW5ndGg7ICsraSkge1xuICAgIGlmIChpc05hTihhcnJheVtpXSkpIHtcbiAgICAgIHVzaG9ydHNbaV0gPSAweGZmZmZcbiAgICB9IGVsc2UgaWYgKGFycmF5W2ldID09PSBJbmZpbml0eSkge1xuICAgICAgdXNob3J0c1tpXSA9IDB4N2MwMFxuICAgIH0gZWxzZSBpZiAoYXJyYXlbaV0gPT09IC1JbmZpbml0eSkge1xuICAgICAgdXNob3J0c1tpXSA9IDB4ZmMwMFxuICAgIH0gZWxzZSB7XG4gICAgICBGTE9BVFswXSA9IGFycmF5W2ldXG4gICAgICB2YXIgeCA9IElOVFswXVxuXG4gICAgICB2YXIgc2duID0gKHggPj4+IDMxKSA8PCAxNVxuICAgICAgdmFyIGV4cCA9ICgoeCA8PCAxKSA+Pj4gMjQpIC0gMTI3XG4gICAgICB2YXIgZnJhYyA9ICh4ID4+IDEzKSAmICgoMSA8PCAxMCkgLSAxKVxuXG4gICAgICBpZiAoZXhwIDwgLTI0KSB7XG4gICAgICAgIC8vIHJvdW5kIG5vbi1yZXByZXNlbnRhYmxlIGRlbm9ybWFscyB0byAwXG4gICAgICAgIHVzaG9ydHNbaV0gPSBzZ25cbiAgICAgIH0gZWxzZSBpZiAoZXhwIDwgLTE0KSB7XG4gICAgICAgIC8vIGhhbmRsZSBkZW5vcm1hbHNcbiAgICAgICAgdmFyIHMgPSAtMTQgLSBleHBcbiAgICAgICAgdXNob3J0c1tpXSA9IHNnbiArICgoZnJhYyArICgxIDw8IDEwKSkgPj4gcylcbiAgICAgIH0gZWxzZSBpZiAoZXhwID4gMTUpIHtcbiAgICAgICAgLy8gcm91bmQgb3ZlcmZsb3cgdG8gKy8tIEluZmluaXR5XG4gICAgICAgIHVzaG9ydHNbaV0gPSBzZ24gKyAweDdjMDBcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIG90aGVyd2lzZSBjb252ZXJ0IGRpcmVjdGx5XG4gICAgICAgIHVzaG9ydHNbaV0gPSBzZ24gKyAoKGV4cCArIDE1KSA8PCAxMCkgKyBmcmFjXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHVzaG9ydHNcbn1cbiIsIm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKG9iaikge1xuICByZXR1cm4gT2JqZWN0LmtleXMob2JqKS5tYXAoZnVuY3Rpb24gKGtleSkgeyByZXR1cm4gb2JqW2tleV0gfSlcbn1cbiIsIi8vIENvbnRleHQgYW5kIGNhbnZhcyBjcmVhdGlvbiBoZWxwZXIgZnVuY3Rpb25zXG52YXIgY2hlY2sgPSByZXF1aXJlKCcuL3V0aWwvY2hlY2snKVxudmFyIGV4dGVuZCA9IHJlcXVpcmUoJy4vdXRpbC9leHRlbmQnKVxuXG5mdW5jdGlvbiBjcmVhdGVDYW52YXMgKGVsZW1lbnQsIG9uRG9uZSwgcGl4ZWxSYXRpbykge1xuICB2YXIgY2FudmFzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnY2FudmFzJylcbiAgZXh0ZW5kKGNhbnZhcy5zdHlsZSwge1xuICAgIGJvcmRlcjogMCxcbiAgICBtYXJnaW46IDAsXG4gICAgcGFkZGluZzogMCxcbiAgICB0b3A6IDAsXG4gICAgbGVmdDogMFxuICB9KVxuICBlbGVtZW50LmFwcGVuZENoaWxkKGNhbnZhcylcblxuICBpZiAoZWxlbWVudCA9PT0gZG9jdW1lbnQuYm9keSkge1xuICAgIGNhbnZhcy5zdHlsZS5wb3NpdGlvbiA9ICdhYnNvbHV0ZSdcbiAgICBleHRlbmQoZWxlbWVudC5zdHlsZSwge1xuICAgICAgbWFyZ2luOiAwLFxuICAgICAgcGFkZGluZzogMFxuICAgIH0pXG4gIH1cblxuICBmdW5jdGlvbiByZXNpemUgKCkge1xuICAgIHZhciB3ID0gd2luZG93LmlubmVyV2lkdGhcbiAgICB2YXIgaCA9IHdpbmRvdy5pbm5lckhlaWdodFxuICAgIGlmIChlbGVtZW50ICE9PSBkb2N1bWVudC5ib2R5KSB7XG4gICAgICB2YXIgYm91bmRzID0gZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKVxuICAgICAgdyA9IGJvdW5kcy5yaWdodCAtIGJvdW5kcy5sZWZ0XG4gICAgICBoID0gYm91bmRzLnRvcCAtIGJvdW5kcy5ib3R0b21cbiAgICB9XG4gICAgY2FudmFzLndpZHRoID0gcGl4ZWxSYXRpbyAqIHdcbiAgICBjYW52YXMuaGVpZ2h0ID0gcGl4ZWxSYXRpbyAqIGhcbiAgICBleHRlbmQoY2FudmFzLnN0eWxlLCB7XG4gICAgICB3aWR0aDogdyArICdweCcsXG4gICAgICBoZWlnaHQ6IGggKyAncHgnXG4gICAgfSlcbiAgfVxuXG4gIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCByZXNpemUsIGZhbHNlKVxuXG4gIGZ1bmN0aW9uIG9uRGVzdHJveSAoKSB7XG4gICAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsIHJlc2l6ZSlcbiAgICBlbGVtZW50LnJlbW92ZUNoaWxkKGNhbnZhcylcbiAgfVxuXG4gIHJlc2l6ZSgpXG5cbiAgcmV0dXJuIHtcbiAgICBjYW52YXM6IGNhbnZhcyxcbiAgICBvbkRlc3Ryb3k6IG9uRGVzdHJveVxuICB9XG59XG5cbmZ1bmN0aW9uIGNyZWF0ZUNvbnRleHQgKGNhbnZhcywgY29udGV4QXR0cmlidXRlcykge1xuICBmdW5jdGlvbiBnZXQgKG5hbWUpIHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGNhbnZhcy5nZXRDb250ZXh0KG5hbWUsIGNvbnRleEF0dHJpYnV0ZXMpXG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgcmV0dXJuIG51bGxcbiAgICB9XG4gIH1cbiAgcmV0dXJuIChcbiAgICBnZXQoJ3dlYmdsJykgfHxcbiAgICBnZXQoJ2V4cGVyaW1lbnRhbC13ZWJnbCcpIHx8XG4gICAgZ2V0KCd3ZWJnbC1leHBlcmltZW50YWwnKVxuICApXG59XG5cbmZ1bmN0aW9uIGlzSFRNTEVsZW1lbnQgKG9iaikge1xuICByZXR1cm4gKFxuICAgIHR5cGVvZiBvYmoubm9kZU5hbWUgPT09ICdzdHJpbmcnICYmXG4gICAgdHlwZW9mIG9iai5hcHBlbmRDaGlsZCA9PT0gJ2Z1bmN0aW9uJyAmJlxuICAgIHR5cGVvZiBvYmouZ2V0Qm91bmRpbmdDbGllbnRSZWN0ID09PSAnZnVuY3Rpb24nXG4gIClcbn1cblxuZnVuY3Rpb24gaXNXZWJHTENvbnRleHQgKG9iaikge1xuICByZXR1cm4gKFxuICAgIHR5cGVvZiBvYmouZHJhd0FycmF5cyA9PT0gJ2Z1bmN0aW9uJyB8fFxuICAgIHR5cGVvZiBvYmouZHJhd0VsZW1lbnRzID09PSAnZnVuY3Rpb24nXG4gIClcbn1cblxuZnVuY3Rpb24gcGFyc2VFeHRlbnNpb25zIChpbnB1dCkge1xuICBpZiAodHlwZW9mIGlucHV0ID09PSAnc3RyaW5nJykge1xuICAgIHJldHVybiBpbnB1dC5zcGxpdCgpXG4gIH1cbiAgY2hlY2soQXJyYXkuaXNBcnJheShpbnB1dCksICdpbnZhbGlkIGV4dGVuc2lvbiBhcnJheScpXG4gIHJldHVybiBpbnB1dFxufVxuXG5mdW5jdGlvbiBnZXRFbGVtZW50IChkZXNjKSB7XG4gIGlmICh0eXBlb2YgZGVzYyA9PT0gJ3N0cmluZycpIHtcbiAgICBjaGVjayh0eXBlb2YgZG9jdW1lbnQgIT09ICd1bmRlZmluZWQnLCAnbm90IHN1cHBvcnRlZCBvdXRzaWRlIG9mIERPTScpXG4gICAgcmV0dXJuIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoZGVzYylcbiAgfVxuICByZXR1cm4gZGVzY1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIHBhcnNlQXJncyAoYXJnc18pIHtcbiAgdmFyIGFyZ3MgPSBhcmdzXyB8fCB7fVxuICB2YXIgZWxlbWVudCwgY29udGFpbmVyLCBjYW52YXMsIGdsXG4gIHZhciBjb250ZXh0QXR0cmlidXRlcyA9IHt9XG4gIHZhciBleHRlbnNpb25zID0gW11cbiAgdmFyIG9wdGlvbmFsRXh0ZW5zaW9ucyA9IFtdXG4gIHZhciBwaXhlbFJhdGlvID0gKHR5cGVvZiB3aW5kb3cgPT09ICd1bmRlZmluZWQnID8gMSA6IHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvKVxuICB2YXIgcHJvZmlsZSA9IGZhbHNlXG4gIHZhciBvbkRvbmUgPSBmdW5jdGlvbiAoZXJyKSB7XG4gICAgaWYgKGVycikge1xuICAgICAgY2hlY2sucmFpc2UoZXJyKVxuICAgIH1cbiAgfVxuICB2YXIgb25EZXN0cm95ID0gZnVuY3Rpb24gKCkge31cbiAgaWYgKHR5cGVvZiBhcmdzID09PSAnc3RyaW5nJykge1xuICAgIGNoZWNrKFxuICAgICAgdHlwZW9mIGRvY3VtZW50ICE9PSAndW5kZWZpbmVkJyxcbiAgICAgICdzZWxlY3RvciBxdWVyaWVzIG9ubHkgc3VwcG9ydGVkIGluIERPTSBlbnZpcm9tZW50cycpXG4gICAgZWxlbWVudCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoYXJncylcbiAgICBjaGVjayhlbGVtZW50LCAnaW52YWxpZCBxdWVyeSBzdHJpbmcgZm9yIGVsZW1lbnQnKVxuICB9IGVsc2UgaWYgKHR5cGVvZiBhcmdzID09PSAnb2JqZWN0Jykge1xuICAgIGlmIChpc0hUTUxFbGVtZW50KGFyZ3MpKSB7XG4gICAgICBlbGVtZW50ID0gYXJnc1xuICAgIH0gZWxzZSBpZiAoaXNXZWJHTENvbnRleHQoYXJncykpIHtcbiAgICAgIGdsID0gYXJnc1xuICAgICAgY2FudmFzID0gZ2wuY2FudmFzXG4gICAgfSBlbHNlIHtcbiAgICAgIGNoZWNrLmNvbnN0cnVjdG9yKGFyZ3MpXG4gICAgICBpZiAoJ2dsJyBpbiBhcmdzKSB7XG4gICAgICAgIGdsID0gYXJncy5nbFxuICAgICAgfSBlbHNlIGlmICgnY2FudmFzJyBpbiBhcmdzKSB7XG4gICAgICAgIGNhbnZhcyA9IGdldEVsZW1lbnQoYXJncy5jYW52YXMpXG4gICAgICB9IGVsc2UgaWYgKCdjb250YWluZXInIGluIGFyZ3MpIHtcbiAgICAgICAgY29udGFpbmVyID0gZ2V0RWxlbWVudChhcmdzLmNvbnRhaW5lcilcbiAgICAgIH1cbiAgICAgIGlmICgnYXR0cmlidXRlcycgaW4gYXJncykge1xuICAgICAgICBjb250ZXh0QXR0cmlidXRlcyA9IGFyZ3MuYXR0cmlidXRlc1xuICAgICAgICBjaGVjay50eXBlKGNvbnRleHRBdHRyaWJ1dGVzLCAnb2JqZWN0JywgJ2ludmFsaWQgY29udGV4dCBhdHRyaWJ1dGVzJylcbiAgICAgIH1cbiAgICAgIGlmICgnZXh0ZW5zaW9ucycgaW4gYXJncykge1xuICAgICAgICBleHRlbnNpb25zID0gcGFyc2VFeHRlbnNpb25zKGFyZ3MuZXh0ZW5zaW9ucylcbiAgICAgIH1cbiAgICAgIGlmICgnb3B0aW9uYWxFeHRlbnNpb25zJyBpbiBhcmdzKSB7XG4gICAgICAgIG9wdGlvbmFsRXh0ZW5zaW9ucyA9IHBhcnNlRXh0ZW5zaW9ucyhhcmdzLm9wdGlvbmFsRXh0ZW5zaW9ucylcbiAgICAgIH1cbiAgICAgIGlmICgnb25Eb25lJyBpbiBhcmdzKSB7XG4gICAgICAgIGNoZWNrLnR5cGUoXG4gICAgICAgICAgYXJncy5vbkRvbmUsICdmdW5jdGlvbicsXG4gICAgICAgICAgJ2ludmFsaWQgb3IgbWlzc2luZyBvbkRvbmUgY2FsbGJhY2snKVxuICAgICAgICBvbkRvbmUgPSBhcmdzLm9uRG9uZVxuICAgICAgfVxuICAgICAgaWYgKCdwcm9maWxlJyBpbiBhcmdzKSB7XG4gICAgICAgIHByb2ZpbGUgPSAhIWFyZ3MucHJvZmlsZVxuICAgICAgfVxuICAgICAgaWYgKCdwaXhlbFJhdGlvJyBpbiBhcmdzKSB7XG4gICAgICAgIHBpeGVsUmF0aW8gPSArYXJncy5waXhlbFJhdGlvXG4gICAgICAgIGNoZWNrKHBpeGVsUmF0aW8gPiAwLCAnaW52YWxpZCBwaXhlbCByYXRpbycpXG4gICAgICB9XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIGNoZWNrLnJhaXNlKCdpbnZhbGlkIGFyZ3VtZW50cyB0byByZWdsJylcbiAgfVxuXG4gIGlmIChlbGVtZW50KSB7XG4gICAgaWYgKGVsZW1lbnQubm9kZU5hbWUudG9Mb3dlckNhc2UoKSA9PT0gJ2NhbnZhcycpIHtcbiAgICAgIGNhbnZhcyA9IGVsZW1lbnRcbiAgICB9IGVsc2Uge1xuICAgICAgY29udGFpbmVyID0gZWxlbWVudFxuICAgIH1cbiAgfVxuXG4gIGlmICghZ2wpIHtcbiAgICBpZiAoIWNhbnZhcykge1xuICAgICAgY2hlY2soXG4gICAgICAgIHR5cGVvZiBkb2N1bWVudCAhPT0gJ3VuZGVmaW5lZCcsXG4gICAgICAgICdtdXN0IG1hbnVhbGx5IHNwZWNpZnkgd2ViZ2wgY29udGV4dCBvdXRzaWRlIG9mIERPTSBlbnZpcm9ubWVudHMnKVxuICAgICAgdmFyIHJlc3VsdCA9IGNyZWF0ZUNhbnZhcyhjb250YWluZXIgfHwgZG9jdW1lbnQuYm9keSwgb25Eb25lLCBwaXhlbFJhdGlvKVxuICAgICAgaWYgKCFyZXN1bHQpIHtcbiAgICAgICAgcmV0dXJuIG51bGxcbiAgICAgIH1cbiAgICAgIGNhbnZhcyA9IHJlc3VsdC5jYW52YXNcbiAgICAgIG9uRGVzdHJveSA9IHJlc3VsdC5vbkRlc3Ryb3lcbiAgICB9XG4gICAgZ2wgPSBjcmVhdGVDb250ZXh0KGNhbnZhcywgY29udGV4dEF0dHJpYnV0ZXMpXG4gIH1cblxuICBpZiAoIWdsKSB7XG4gICAgb25EZXN0cm95KClcbiAgICBvbkRvbmUoJ3dlYmdsIG5vdCBzdXBwb3J0ZWQsIHRyeSB1cGdyYWRpbmcgeW91ciBicm93c2VyIG9yIGdyYXBoaWNzIGRyaXZlcnMgaHR0cDovL2dldC53ZWJnbC5vcmcnKVxuICAgIHJldHVybiBudWxsXG4gIH1cblxuICByZXR1cm4ge1xuICAgIGdsOiBnbCxcbiAgICBjYW52YXM6IGNhbnZhcyxcbiAgICBjb250YWluZXI6IGNvbnRhaW5lcixcbiAgICBleHRlbnNpb25zOiBleHRlbnNpb25zLFxuICAgIG9wdGlvbmFsRXh0ZW5zaW9uczogb3B0aW9uYWxFeHRlbnNpb25zLFxuICAgIHBpeGVsUmF0aW86IHBpeGVsUmF0aW8sXG4gICAgcHJvZmlsZTogcHJvZmlsZSxcbiAgICBvbkRvbmU6IG9uRG9uZSxcbiAgICBvbkRlc3Ryb3k6IG9uRGVzdHJveVxuICB9XG59XG4iLCJ2YXIgY2hlY2sgPSByZXF1aXJlKCcuL2xpYi91dGlsL2NoZWNrJylcbnZhciBleHRlbmQgPSByZXF1aXJlKCcuL2xpYi91dGlsL2V4dGVuZCcpXG52YXIgZHluYW1pYyA9IHJlcXVpcmUoJy4vbGliL2R5bmFtaWMnKVxudmFyIHJhZiA9IHJlcXVpcmUoJy4vbGliL3V0aWwvcmFmJylcbnZhciBjbG9jayA9IHJlcXVpcmUoJy4vbGliL3V0aWwvY2xvY2snKVxudmFyIGNyZWF0ZVN0cmluZ1N0b3JlID0gcmVxdWlyZSgnLi9saWIvc3RyaW5ncycpXG52YXIgaW5pdFdlYkdMID0gcmVxdWlyZSgnLi9saWIvd2ViZ2wnKVxudmFyIHdyYXBFeHRlbnNpb25zID0gcmVxdWlyZSgnLi9saWIvZXh0ZW5zaW9uJylcbnZhciB3cmFwTGltaXRzID0gcmVxdWlyZSgnLi9saWIvbGltaXRzJylcbnZhciB3cmFwQnVmZmVycyA9IHJlcXVpcmUoJy4vbGliL2J1ZmZlcicpXG52YXIgd3JhcEVsZW1lbnRzID0gcmVxdWlyZSgnLi9saWIvZWxlbWVudHMnKVxudmFyIHdyYXBUZXh0dXJlcyA9IHJlcXVpcmUoJy4vbGliL3RleHR1cmUnKVxudmFyIHdyYXBSZW5kZXJidWZmZXJzID0gcmVxdWlyZSgnLi9saWIvcmVuZGVyYnVmZmVyJylcbnZhciB3cmFwRnJhbWVidWZmZXJzID0gcmVxdWlyZSgnLi9saWIvZnJhbWVidWZmZXInKVxudmFyIHdyYXBBdHRyaWJ1dGVzID0gcmVxdWlyZSgnLi9saWIvYXR0cmlidXRlJylcbnZhciB3cmFwU2hhZGVycyA9IHJlcXVpcmUoJy4vbGliL3NoYWRlcicpXG52YXIgd3JhcFJlYWQgPSByZXF1aXJlKCcuL2xpYi9yZWFkJylcbnZhciBjcmVhdGVDb3JlID0gcmVxdWlyZSgnLi9saWIvY29yZScpXG52YXIgY3JlYXRlU3RhdHMgPSByZXF1aXJlKCcuL2xpYi9zdGF0cycpXG52YXIgY3JlYXRlVGltZXIgPSByZXF1aXJlKCcuL2xpYi90aW1lcicpXG5cbnZhciBHTF9DT0xPUl9CVUZGRVJfQklUID0gMTYzODRcbnZhciBHTF9ERVBUSF9CVUZGRVJfQklUID0gMjU2XG52YXIgR0xfU1RFTkNJTF9CVUZGRVJfQklUID0gMTAyNFxuXG52YXIgR0xfQVJSQVlfQlVGRkVSID0gMzQ5NjJcblxudmFyIENPTlRFWFRfTE9TVF9FVkVOVCA9ICd3ZWJnbGNvbnRleHRsb3N0J1xudmFyIENPTlRFWFRfUkVTVE9SRURfRVZFTlQgPSAnd2ViZ2xjb250ZXh0cmVzdG9yZWQnXG5cbnZhciBEWU5fUFJPUCA9IDFcbnZhciBEWU5fQ09OVEVYVCA9IDJcbnZhciBEWU5fU1RBVEUgPSAzXG5cbmZ1bmN0aW9uIGZpbmQgKGhheXN0YWNrLCBuZWVkbGUpIHtcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBoYXlzdGFjay5sZW5ndGg7ICsraSkge1xuICAgIGlmIChoYXlzdGFja1tpXSA9PT0gbmVlZGxlKSB7XG4gICAgICByZXR1cm4gaVxuICAgIH1cbiAgfVxuICByZXR1cm4gLTFcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiB3cmFwUkVHTCAoYXJncykge1xuICB2YXIgY29uZmlnID0gaW5pdFdlYkdMKGFyZ3MpXG4gIGlmICghY29uZmlnKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIHZhciBnbCA9IGNvbmZpZy5nbFxuICB2YXIgZ2xBdHRyaWJ1dGVzID0gZ2wuZ2V0Q29udGV4dEF0dHJpYnV0ZXMoKVxuXG4gIHZhciBleHRlbnNpb25TdGF0ZSA9IHdyYXBFeHRlbnNpb25zKGdsLCBjb25maWcpXG4gIGlmICghZXh0ZW5zaW9uU3RhdGUpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgdmFyIHN0cmluZ1N0b3JlID0gY3JlYXRlU3RyaW5nU3RvcmUoKVxuICB2YXIgc3RhdHMgPSBjcmVhdGVTdGF0cygpXG4gIHZhciBleHRlbnNpb25zID0gZXh0ZW5zaW9uU3RhdGUuZXh0ZW5zaW9uc1xuICB2YXIgdGltZXIgPSBjcmVhdGVUaW1lcihnbCwgZXh0ZW5zaW9ucylcblxuICB2YXIgU1RBUlRfVElNRSA9IGNsb2NrKClcbiAgdmFyIFdJRFRIID0gZ2wuZHJhd2luZ0J1ZmZlcldpZHRoXG4gIHZhciBIRUlHSFQgPSBnbC5kcmF3aW5nQnVmZmVySGVpZ2h0XG5cbiAgdmFyIGNvbnRleHRTdGF0ZSA9IHtcbiAgICB0aWNrOiAwLFxuICAgIHRpbWU6IDAsXG4gICAgdmlld3BvcnRXaWR0aDogV0lEVEgsXG4gICAgdmlld3BvcnRIZWlnaHQ6IEhFSUdIVCxcbiAgICBmcmFtZWJ1ZmZlcldpZHRoOiBXSURUSCxcbiAgICBmcmFtZWJ1ZmZlckhlaWdodDogSEVJR0hULFxuICAgIGRyYXdpbmdCdWZmZXJXaWR0aDogV0lEVEgsXG4gICAgZHJhd2luZ0J1ZmZlckhlaWdodDogSEVJR0hULFxuICAgIHBpeGVsUmF0aW86IGNvbmZpZy5waXhlbFJhdGlvXG4gIH1cbiAgdmFyIHVuaWZvcm1TdGF0ZSA9IHt9XG4gIHZhciBkcmF3U3RhdGUgPSB7XG4gICAgZWxlbWVudHM6IG51bGwsXG4gICAgcHJpbWl0aXZlOiA0LCAvLyBHTF9UUklBTkdMRVNcbiAgICBjb3VudDogLTEsXG4gICAgb2Zmc2V0OiAwLFxuICAgIGluc3RhbmNlczogLTFcbiAgfVxuXG4gIHZhciBsaW1pdHMgPSB3cmFwTGltaXRzKGdsLCBleHRlbnNpb25zKVxuICB2YXIgYnVmZmVyU3RhdGUgPSB3cmFwQnVmZmVycyhnbCwgc3RhdHMpXG4gIHZhciBlbGVtZW50U3RhdGUgPSB3cmFwRWxlbWVudHMoZ2wsIGV4dGVuc2lvbnMsIGJ1ZmZlclN0YXRlLCBzdGF0cylcbiAgdmFyIGF0dHJpYnV0ZVN0YXRlID0gd3JhcEF0dHJpYnV0ZXMoXG4gICAgZ2wsXG4gICAgZXh0ZW5zaW9ucyxcbiAgICBsaW1pdHMsXG4gICAgYnVmZmVyU3RhdGUsXG4gICAgc3RyaW5nU3RvcmUpXG4gIHZhciBzaGFkZXJTdGF0ZSA9IHdyYXBTaGFkZXJzKGdsLCBzdHJpbmdTdG9yZSwgc3RhdHMpXG4gIHZhciB0ZXh0dXJlU3RhdGUgPSB3cmFwVGV4dHVyZXMoXG4gICAgZ2wsXG4gICAgZXh0ZW5zaW9ucyxcbiAgICBsaW1pdHMsXG4gICAgZnVuY3Rpb24gKCkgeyBjb3JlLnByb2NzLnBvbGwoKSB9LFxuICAgIGNvbnRleHRTdGF0ZSxcbiAgICBzdGF0cylcbiAgdmFyIHJlbmRlcmJ1ZmZlclN0YXRlID0gd3JhcFJlbmRlcmJ1ZmZlcnMoZ2wsIGV4dGVuc2lvbnMsIGxpbWl0cywgc3RhdHMpXG4gIHZhciBmcmFtZWJ1ZmZlclN0YXRlID0gd3JhcEZyYW1lYnVmZmVycyhcbiAgICBnbCxcbiAgICBleHRlbnNpb25zLFxuICAgIGxpbWl0cyxcbiAgICB0ZXh0dXJlU3RhdGUsXG4gICAgcmVuZGVyYnVmZmVyU3RhdGUsXG4gICAgc3RhdHMpXG4gIHZhciBjb3JlID0gY3JlYXRlQ29yZShcbiAgICBnbCxcbiAgICBzdHJpbmdTdG9yZSxcbiAgICBleHRlbnNpb25zLFxuICAgIGxpbWl0cyxcbiAgICBidWZmZXJTdGF0ZSxcbiAgICBlbGVtZW50U3RhdGUsXG4gICAgdGV4dHVyZVN0YXRlLFxuICAgIGZyYW1lYnVmZmVyU3RhdGUsXG4gICAgdW5pZm9ybVN0YXRlLFxuICAgIGF0dHJpYnV0ZVN0YXRlLFxuICAgIHNoYWRlclN0YXRlLFxuICAgIGRyYXdTdGF0ZSxcbiAgICBjb250ZXh0U3RhdGUsXG4gICAgdGltZXIsXG4gICAgY29uZmlnKVxuICB2YXIgcmVhZFBpeGVscyA9IHdyYXBSZWFkKFxuICAgIGdsLFxuICAgIGZyYW1lYnVmZmVyU3RhdGUsXG4gICAgY29yZS5wcm9jcy5wb2xsLFxuICAgIGNvbnRleHRTdGF0ZSxcbiAgICBnbEF0dHJpYnV0ZXMpXG5cbiAgdmFyIG5leHRTdGF0ZSA9IGNvcmUubmV4dFxuICB2YXIgY2FudmFzID0gZ2wuY2FudmFzXG5cbiAgdmFyIHJhZkNhbGxiYWNrcyA9IFtdXG4gIHZhciBhY3RpdmVSQUYgPSBudWxsXG4gIGZ1bmN0aW9uIGhhbmRsZVJBRiAoKSB7XG4gICAgaWYgKHJhZkNhbGxiYWNrcy5sZW5ndGggPT09IDApIHtcbiAgICAgIGlmICh0aW1lcikge1xuICAgICAgICB0aW1lci51cGRhdGUoKVxuICAgICAgfVxuICAgICAgYWN0aXZlUkFGID0gbnVsbFxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gc2NoZWR1bGUgbmV4dCBhbmltYXRpb24gZnJhbWVcbiAgICBhY3RpdmVSQUYgPSByYWYubmV4dChoYW5kbGVSQUYpXG5cbiAgICAvLyBwb2xsIGZvciBjaGFuZ2VzXG4gICAgcG9sbCgpXG5cbiAgICAvLyBmaXJlIGEgY2FsbGJhY2sgZm9yIGFsbCBwZW5kaW5nIHJhZnNcbiAgICBmb3IgKHZhciBpID0gcmFmQ2FsbGJhY2tzLmxlbmd0aCAtIDE7IGkgPj0gMDsgLS1pKSB7XG4gICAgICB2YXIgY2IgPSByYWZDYWxsYmFja3NbaV1cbiAgICAgIGlmIChjYikge1xuICAgICAgICBjYihjb250ZXh0U3RhdGUsIG51bGwsIDApXG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gZmx1c2ggYWxsIHBlbmRpbmcgd2ViZ2wgY2FsbHNcbiAgICBnbC5mbHVzaCgpXG5cbiAgICAvLyBwb2xsIEdQVSB0aW1lcnMgKmFmdGVyKiBnbC5mbHVzaCBzbyB3ZSBkb24ndCBkZWxheSBjb21tYW5kIGRpc3BhdGNoXG4gICAgaWYgKHRpbWVyKSB7XG4gICAgICB0aW1lci51cGRhdGUoKVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHN0YXJ0UkFGICgpIHtcbiAgICBpZiAoIWFjdGl2ZVJBRiAmJiByYWZDYWxsYmFja3MubGVuZ3RoID4gMCkge1xuICAgICAgYWN0aXZlUkFGID0gcmFmLm5leHQoaGFuZGxlUkFGKVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHN0b3BSQUYgKCkge1xuICAgIGlmIChhY3RpdmVSQUYpIHtcbiAgICAgIHJhZi5jYW5jZWwoaGFuZGxlUkFGKVxuICAgICAgYWN0aXZlUkFGID0gbnVsbFxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGhhbmRsZUNvbnRleHRMb3NzIChldmVudCkge1xuICAgIC8vIFRPRE9cbiAgfVxuXG4gIGZ1bmN0aW9uIGhhbmRsZUNvbnRleHRSZXN0b3JlZCAoZXZlbnQpIHtcbiAgICAvLyBUT0RPXG4gIH1cblxuICBpZiAoY2FudmFzKSB7XG4gICAgY2FudmFzLmFkZEV2ZW50TGlzdGVuZXIoQ09OVEVYVF9MT1NUX0VWRU5ULCBoYW5kbGVDb250ZXh0TG9zcywgZmFsc2UpXG4gICAgY2FudmFzLmFkZEV2ZW50TGlzdGVuZXIoQ09OVEVYVF9SRVNUT1JFRF9FVkVOVCwgaGFuZGxlQ29udGV4dFJlc3RvcmVkLCBmYWxzZSlcbiAgfVxuXG4gIGZ1bmN0aW9uIGRlc3Ryb3kgKCkge1xuICAgIHJhZkNhbGxiYWNrcy5sZW5ndGggPSAwXG4gICAgc3RvcFJBRigpXG5cbiAgICBpZiAoY2FudmFzKSB7XG4gICAgICBjYW52YXMucmVtb3ZlRXZlbnRMaXN0ZW5lcihDT05URVhUX0xPU1RfRVZFTlQsIGhhbmRsZUNvbnRleHRMb3NzKVxuICAgICAgY2FudmFzLnJlbW92ZUV2ZW50TGlzdGVuZXIoQ09OVEVYVF9SRVNUT1JFRF9FVkVOVCwgaGFuZGxlQ29udGV4dFJlc3RvcmVkKVxuICAgIH1cblxuICAgIHNoYWRlclN0YXRlLmNsZWFyKClcbiAgICBmcmFtZWJ1ZmZlclN0YXRlLmNsZWFyKClcbiAgICByZW5kZXJidWZmZXJTdGF0ZS5jbGVhcigpXG4gICAgdGV4dHVyZVN0YXRlLmNsZWFyKClcbiAgICBlbGVtZW50U3RhdGUuY2xlYXIoKVxuICAgIGJ1ZmZlclN0YXRlLmNsZWFyKClcblxuICAgIGlmICh0aW1lcikge1xuICAgICAgdGltZXIuY2xlYXIoKVxuICAgIH1cblxuICAgIGNvbmZpZy5vbkRlc3Ryb3koKVxuICB9XG5cbiAgZnVuY3Rpb24gY29tcGlsZVByb2NlZHVyZSAob3B0aW9ucykge1xuICAgIGNoZWNrKCEhb3B0aW9ucywgJ2ludmFsaWQgYXJncyB0byByZWdsKHsuLi59KScpXG4gICAgY2hlY2sudHlwZShvcHRpb25zLCAnb2JqZWN0JywgJ2ludmFsaWQgYXJncyB0byByZWdsKHsuLi59KScpXG5cbiAgICBmdW5jdGlvbiBmbGF0dGVuTmVzdGVkT3B0aW9ucyAob3B0aW9ucykge1xuICAgICAgdmFyIHJlc3VsdCA9IGV4dGVuZCh7fSwgb3B0aW9ucylcbiAgICAgIGRlbGV0ZSByZXN1bHQudW5pZm9ybXNcbiAgICAgIGRlbGV0ZSByZXN1bHQuYXR0cmlidXRlc1xuICAgICAgZGVsZXRlIHJlc3VsdC5jb250ZXh0XG5cbiAgICAgIGZ1bmN0aW9uIG1lcmdlIChuYW1lKSB7XG4gICAgICAgIGlmIChuYW1lIGluIHJlc3VsdCkge1xuICAgICAgICAgIHZhciBjaGlsZCA9IHJlc3VsdFtuYW1lXVxuICAgICAgICAgIGRlbGV0ZSByZXN1bHRbbmFtZV1cbiAgICAgICAgICBPYmplY3Qua2V5cyhjaGlsZCkuZm9yRWFjaChmdW5jdGlvbiAocHJvcCkge1xuICAgICAgICAgICAgcmVzdWx0W25hbWUgKyAnLicgKyBwcm9wXSA9IGNoaWxkW3Byb3BdXG4gICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgbWVyZ2UoJ2JsZW5kJylcbiAgICAgIG1lcmdlKCdkZXB0aCcpXG4gICAgICBtZXJnZSgnY3VsbCcpXG4gICAgICBtZXJnZSgnc3RlbmNpbCcpXG4gICAgICBtZXJnZSgncG9seWdvbk9mZnNldCcpXG4gICAgICBtZXJnZSgnc2Npc3NvcicpXG4gICAgICBtZXJnZSgnc2FtcGxlJylcblxuICAgICAgcmV0dXJuIHJlc3VsdFxuICAgIH1cblxuICAgIGZ1bmN0aW9uIHNlcGFyYXRlRHluYW1pYyAob2JqZWN0KSB7XG4gICAgICB2YXIgc3RhdGljSXRlbXMgPSB7fVxuICAgICAgdmFyIGR5bmFtaWNJdGVtcyA9IHt9XG4gICAgICBPYmplY3Qua2V5cyhvYmplY3QpLmZvckVhY2goZnVuY3Rpb24gKG9wdGlvbikge1xuICAgICAgICB2YXIgdmFsdWUgPSBvYmplY3Rbb3B0aW9uXVxuICAgICAgICBpZiAoZHluYW1pYy5pc0R5bmFtaWModmFsdWUpKSB7XG4gICAgICAgICAgZHluYW1pY0l0ZW1zW29wdGlvbl0gPSBkeW5hbWljLnVuYm94KHZhbHVlLCBvcHRpb24pXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgc3RhdGljSXRlbXNbb3B0aW9uXSA9IHZhbHVlXG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgICByZXR1cm4ge1xuICAgICAgICBkeW5hbWljOiBkeW5hbWljSXRlbXMsXG4gICAgICAgIHN0YXRpYzogc3RhdGljSXRlbXNcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBUcmVhdCBjb250ZXh0IHZhcmlhYmxlcyBzZXBhcmF0ZSBmcm9tIG90aGVyIGR5bmFtaWMgdmFyaWFibGVzXG4gICAgdmFyIGNvbnRleHQgPSBzZXBhcmF0ZUR5bmFtaWMob3B0aW9ucy5jb250ZXh0IHx8IHt9KVxuICAgIHZhciB1bmlmb3JtcyA9IHNlcGFyYXRlRHluYW1pYyhvcHRpb25zLnVuaWZvcm1zIHx8IHt9KVxuICAgIHZhciBhdHRyaWJ1dGVzID0gc2VwYXJhdGVEeW5hbWljKG9wdGlvbnMuYXR0cmlidXRlcyB8fCB7fSlcbiAgICB2YXIgb3B0cyA9IHNlcGFyYXRlRHluYW1pYyhmbGF0dGVuTmVzdGVkT3B0aW9ucyhvcHRpb25zKSlcblxuICAgIHZhciBzdGF0cyA9IHtcbiAgICAgIGdwdVRpbWU6IDAuMCxcbiAgICAgIGNwdVRpbWU6IDAuMCxcbiAgICAgIGNvdW50OiAwXG4gICAgfVxuXG4gICAgdmFyIGNvbXBpbGVkID0gY29yZS5jb21waWxlKG9wdHMsIGF0dHJpYnV0ZXMsIHVuaWZvcm1zLCBjb250ZXh0LCBzdGF0cylcblxuICAgIHZhciBkcmF3ID0gY29tcGlsZWQuZHJhd1xuICAgIHZhciBiYXRjaCA9IGNvbXBpbGVkLmJhdGNoXG4gICAgdmFyIHNjb3BlID0gY29tcGlsZWQuc2NvcGVcblxuICAgIHZhciBFTVBUWV9BUlJBWSA9IFtdXG4gICAgZnVuY3Rpb24gcmVzZXJ2ZSAoY291bnQpIHtcbiAgICAgIHdoaWxlIChFTVBUWV9BUlJBWS5sZW5ndGggPCBjb3VudCkge1xuICAgICAgICBFTVBUWV9BUlJBWS5wdXNoKG51bGwpXG4gICAgICB9XG4gICAgICByZXR1cm4gRU1QVFlfQVJSQVlcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBSRUdMQ29tbWFuZCAoYXJncywgYm9keSkge1xuICAgICAgdmFyIGlcbiAgICAgIGlmICh0eXBlb2YgYXJncyA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICByZXR1cm4gc2NvcGUuY2FsbCh0aGlzLCBudWxsLCBhcmdzLCAwKVxuICAgICAgfSBlbHNlIGlmICh0eXBlb2YgYm9keSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICBpZiAodHlwZW9mIGFyZ3MgPT09ICdudW1iZXInKSB7XG4gICAgICAgICAgZm9yIChpID0gMDsgaSA8IGFyZ3M7ICsraSkge1xuICAgICAgICAgICAgc2NvcGUuY2FsbCh0aGlzLCBudWxsLCBib2R5LCBpKVxuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfSBlbHNlIGlmIChBcnJheS5pc0FycmF5KGFyZ3MpKSB7XG4gICAgICAgICAgZm9yIChpID0gMDsgaSA8IGFyZ3MubGVuZ3RoOyArK2kpIHtcbiAgICAgICAgICAgIHNjb3BlLmNhbGwodGhpcywgYXJnc1tpXSwgYm9keSwgaSlcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIHNjb3BlLmNhbGwodGhpcywgYXJncywgYm9keSwgMClcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmICh0eXBlb2YgYXJncyA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgaWYgKGFyZ3MgPiAwKSB7XG4gICAgICAgICAgcmV0dXJuIGJhdGNoLmNhbGwodGhpcywgcmVzZXJ2ZShhcmdzIHwgMCksIGFyZ3MgfCAwKVxuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKEFycmF5LmlzQXJyYXkoYXJncykpIHtcbiAgICAgICAgaWYgKGFyZ3MubGVuZ3RoKSB7XG4gICAgICAgICAgcmV0dXJuIGJhdGNoLmNhbGwodGhpcywgYXJncywgYXJncy5sZW5ndGgpXG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiBkcmF3LmNhbGwodGhpcywgYXJncylcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gZXh0ZW5kKFJFR0xDb21tYW5kLCB7XG4gICAgICBzdGF0czogc3RhdHNcbiAgICB9KVxuICB9XG5cbiAgZnVuY3Rpb24gY2xlYXIgKG9wdGlvbnMpIHtcbiAgICBjaGVjayhcbiAgICAgIHR5cGVvZiBvcHRpb25zID09PSAnb2JqZWN0JyAmJiBvcHRpb25zLFxuICAgICAgJ3JlZ2wuY2xlYXIoKSB0YWtlcyBhbiBvYmplY3QgYXMgaW5wdXQnKVxuXG4gICAgdmFyIGNsZWFyRmxhZ3MgPSAwXG4gICAgY29yZS5wcm9jcy5wb2xsKClcblxuICAgIHZhciBjID0gb3B0aW9ucy5jb2xvclxuICAgIGlmIChjKSB7XG4gICAgICBnbC5jbGVhckNvbG9yKCtjWzBdIHx8IDAsICtjWzFdIHx8IDAsICtjWzJdIHx8IDAsICtjWzNdIHx8IDApXG4gICAgICBjbGVhckZsYWdzIHw9IEdMX0NPTE9SX0JVRkZFUl9CSVRcbiAgICB9XG4gICAgaWYgKCdkZXB0aCcgaW4gb3B0aW9ucykge1xuICAgICAgZ2wuY2xlYXJEZXB0aCgrb3B0aW9ucy5kZXB0aClcbiAgICAgIGNsZWFyRmxhZ3MgfD0gR0xfREVQVEhfQlVGRkVSX0JJVFxuICAgIH1cbiAgICBpZiAoJ3N0ZW5jaWwnIGluIG9wdGlvbnMpIHtcbiAgICAgIGdsLmNsZWFyU3RlbmNpbChvcHRpb25zLnN0ZW5jaWwgfCAwKVxuICAgICAgY2xlYXJGbGFncyB8PSBHTF9TVEVOQ0lMX0JVRkZFUl9CSVRcbiAgICB9XG5cbiAgICBjaGVjayghIWNsZWFyRmxhZ3MsICdjYWxsZWQgcmVnbC5jbGVhciB3aXRoIG5vIGJ1ZmZlciBzcGVjaWZpZWQnKVxuICAgIGdsLmNsZWFyKGNsZWFyRmxhZ3MpXG4gIH1cblxuICBmdW5jdGlvbiBmcmFtZSAoY2IpIHtcbiAgICBjaGVjay50eXBlKGNiLCAnZnVuY3Rpb24nLCAncmVnbC5mcmFtZSgpIGNhbGxiYWNrIG11c3QgYmUgYSBmdW5jdGlvbicpXG4gICAgcmFmQ2FsbGJhY2tzLnB1c2goY2IpXG5cbiAgICBmdW5jdGlvbiBjYW5jZWwgKCkge1xuICAgICAgLy8gRklYTUU6ICBzaG91bGQgd2UgY2hlY2sgc29tZXRoaW5nIG90aGVyIHRoYW4gZXF1YWxzIGNiIGhlcmU/XG4gICAgICAvLyB3aGF0IGlmIGEgdXNlciBjYWxscyBmcmFtZSB0d2ljZSB3aXRoIHRoZSBzYW1lIGNhbGxiYWNrLi4uXG4gICAgICAvL1xuICAgICAgdmFyIGkgPSBmaW5kKHJhZkNhbGxiYWNrcywgY2IpXG4gICAgICBjaGVjayhpID49IDAsICdjYW5ub3QgY2FuY2VsIGEgZnJhbWUgdHdpY2UnKVxuICAgICAgZnVuY3Rpb24gcGVuZGluZ0NhbmNlbCAoKSB7XG4gICAgICAgIHZhciBpbmRleCA9IGZpbmQocmFmQ2FsbGJhY2tzLCBwZW5kaW5nQ2FuY2VsKVxuICAgICAgICByYWZDYWxsYmFja3NbaW5kZXhdID0gcmFmQ2FsbGJhY2tzW3JhZkNhbGxiYWNrcy5sZW5ndGggLSAxXVxuICAgICAgICByYWZDYWxsYmFja3MubGVuZ3RoIC09IDFcbiAgICAgICAgaWYgKHJhZkNhbGxiYWNrcy5sZW5ndGggPD0gMCkge1xuICAgICAgICAgIHN0b3BSQUYoKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICByYWZDYWxsYmFja3NbaV0gPSBwZW5kaW5nQ2FuY2VsXG4gICAgfVxuXG4gICAgc3RhcnRSQUYoKVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGNhbmNlbDogY2FuY2VsXG4gICAgfVxuICB9XG5cbiAgLy8gcG9sbCB2aWV3cG9ydFxuICBmdW5jdGlvbiBwb2xsVmlld3BvcnQgKCkge1xuICAgIHZhciB2aWV3cG9ydCA9IG5leHRTdGF0ZS52aWV3cG9ydFxuICAgIHZhciBzY2lzc29yQm94ID0gbmV4dFN0YXRlLnNjaXNzb3JfYm94XG4gICAgdmlld3BvcnRbMF0gPSB2aWV3cG9ydFsxXSA9IHNjaXNzb3JCb3hbMF0gPSBzY2lzc29yQm94WzFdID0gMFxuICAgIGNvbnRleHRTdGF0ZS52aWV3cG9ydFdpZHRoID1cbiAgICAgIGNvbnRleHRTdGF0ZS5mcmFtZWJ1ZmZlcldpZHRoID1cbiAgICAgIGNvbnRleHRTdGF0ZS5kcmF3aW5nQnVmZmVyV2lkdGggPVxuICAgICAgdmlld3BvcnRbMl0gPVxuICAgICAgc2Npc3NvckJveFsyXSA9IGdsLmRyYXdpbmdCdWZmZXJXaWR0aFxuICAgIGNvbnRleHRTdGF0ZS52aWV3cG9ydEhlaWdodCA9XG4gICAgICBjb250ZXh0U3RhdGUuZnJhbWVidWZmZXJIZWlnaHQgPVxuICAgICAgY29udGV4dFN0YXRlLmRyYXdpbmdCdWZmZXJIZWlnaHQgPVxuICAgICAgdmlld3BvcnRbM10gPVxuICAgICAgc2Npc3NvckJveFszXSA9IGdsLmRyYXdpbmdCdWZmZXJIZWlnaHRcbiAgfVxuXG4gIGZ1bmN0aW9uIHBvbGwgKCkge1xuICAgIGNvbnRleHRTdGF0ZS50aWNrICs9IDFcbiAgICBjb250ZXh0U3RhdGUudGltZSA9IChjbG9jaygpIC0gU1RBUlRfVElNRSkgLyAxMDAwLjBcbiAgICBwb2xsVmlld3BvcnQoKVxuICAgIGNvcmUucHJvY3MucG9sbCgpXG4gIH1cblxuICBmdW5jdGlvbiByZWZyZXNoICgpIHtcbiAgICBwb2xsVmlld3BvcnQoKVxuICAgIGNvcmUucHJvY3MucmVmcmVzaCgpXG4gICAgaWYgKHRpbWVyKSB7XG4gICAgICB0aW1lci51cGRhdGUoKVxuICAgIH1cbiAgfVxuXG4gIHJlZnJlc2goKVxuXG4gIHZhciByZWdsID0gZXh0ZW5kKGNvbXBpbGVQcm9jZWR1cmUsIHtcbiAgICAvLyBDbGVhciBjdXJyZW50IEZCT1xuICAgIGNsZWFyOiBjbGVhcixcblxuICAgIC8vIFNob3J0IGN1dHMgZm9yIGR5bmFtaWMgdmFyaWFibGVzXG4gICAgcHJvcDogZHluYW1pYy5kZWZpbmUuYmluZChudWxsLCBEWU5fUFJPUCksXG4gICAgY29udGV4dDogZHluYW1pYy5kZWZpbmUuYmluZChudWxsLCBEWU5fQ09OVEVYVCksXG4gICAgdGhpczogZHluYW1pYy5kZWZpbmUuYmluZChudWxsLCBEWU5fU1RBVEUpLFxuXG4gICAgLy8gZXhlY3V0ZXMgYW4gZW1wdHkgZHJhdyBjb21tYW5kXG4gICAgZHJhdzogY29tcGlsZVByb2NlZHVyZSh7fSksXG5cbiAgICAvLyBSZXNvdXJjZXNcbiAgICBidWZmZXI6IGZ1bmN0aW9uIChvcHRpb25zKSB7XG4gICAgICByZXR1cm4gYnVmZmVyU3RhdGUuY3JlYXRlKG9wdGlvbnMsIEdMX0FSUkFZX0JVRkZFUilcbiAgICB9LFxuICAgIGVsZW1lbnRzOiBlbGVtZW50U3RhdGUuY3JlYXRlLFxuICAgIHRleHR1cmU6IHRleHR1cmVTdGF0ZS5jcmVhdGUyRCxcbiAgICBjdWJlOiB0ZXh0dXJlU3RhdGUuY3JlYXRlQ3ViZSxcbiAgICByZW5kZXJidWZmZXI6IHJlbmRlcmJ1ZmZlclN0YXRlLmNyZWF0ZSxcbiAgICBmcmFtZWJ1ZmZlcjogZnJhbWVidWZmZXJTdGF0ZS5jcmVhdGUsXG4gICAgZnJhbWVidWZmZXJDdWJlOiBmdW5jdGlvbiAob3B0aW9ucykge1xuICAgICAgY2hlY2sucmFpc2UoJ2ZyYW1lYnVmZmVyIGN1YmUgbm90IHlldCBpbXBsZW1lbnRlZCcpXG4gICAgfSxcblxuICAgIC8vIEV4cG9zZSBjb250ZXh0IGF0dHJpYnV0ZXNcbiAgICBhdHRyaWJ1dGVzOiBnbEF0dHJpYnV0ZXMsXG5cbiAgICAvLyBGcmFtZSByZW5kZXJpbmdcbiAgICBmcmFtZTogZnJhbWUsXG5cbiAgICAvLyBTeXN0ZW0gbGltaXRzXG4gICAgbGltaXRzOiBsaW1pdHMsXG4gICAgaGFzRXh0ZW5zaW9uOiBmdW5jdGlvbiAobmFtZSkge1xuICAgICAgcmV0dXJuIGxpbWl0cy5leHRlbnNpb25zLmluZGV4T2YobmFtZS50b0xvd2VyQ2FzZSgpKSA+PSAwXG4gICAgfSxcblxuICAgIC8vIFJlYWQgcGl4ZWxzXG4gICAgcmVhZDogcmVhZFBpeGVscyxcblxuICAgIC8vIERlc3Ryb3kgcmVnbCBhbmQgYWxsIGFzc29jaWF0ZWQgcmVzb3VyY2VzXG4gICAgZGVzdHJveTogZGVzdHJveSxcblxuICAgIC8vIERpcmVjdCBHTCBzdGF0ZSBtYW5pcHVsYXRpb25cbiAgICBfZ2w6IGdsLFxuICAgIF9yZWZyZXNoOiByZWZyZXNoLFxuXG4gICAgcG9sbDogZnVuY3Rpb24gKCkge1xuICAgICAgcG9sbCgpXG4gICAgICBpZiAodGltZXIpIHtcbiAgICAgICAgdGltZXIudXBkYXRlKClcbiAgICAgIH1cbiAgICB9LFxuXG4gICAgLy8gcmVnbCBTdGF0aXN0aWNzIEluZm9ybWF0aW9uXG4gICAgc3RhdHM6IHN0YXRzXG4gIH0pXG5cbiAgY29uZmlnLm9uRG9uZShudWxsLCByZWdsKVxuXG4gIHJldHVybiByZWdsXG59XG4iXX0= | |
require=(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})({"glsl-quad":[function(require,module,exports){ | |
const verts = [ | |
[-1.0, -1.0], | |
[+1.0, -1.0], | |
[-1.0, +1.0], | |
[-1.0, +1.0], | |
[+1.0, -1.0], | |
[+1.0, +1.0] | |
]; | |
const uvs = [ | |
[0.0, 0.0], | |
[1.0, 0.0], | |
[0.0, 1.0], | |
[0.0, 1.0], | |
[1.0, 0.0], | |
[1.0, 1.0] | |
]; | |
const indices = [ | |
[0, 1, 2], | |
[3, 4, 5] | |
]; | |
const vshader = ` | |
precision mediump float; | |
attribute vec2 a_position; | |
attribute vec2 a_uv; | |
uniform float u_clip_y; | |
varying vec2 v_uv; | |
void main() { | |
v_uv = a_uv; | |
gl_Position = vec4(a_position * vec2(1,u_clip_y), 0, 1); | |
} | |
`; | |
const fshader = ` | |
precision mediump float; | |
varying vec2 v_uv; | |
uniform sampler2D u_tex; | |
void main () { | |
gl_FragColor = texture2D(u_tex,v_uv); | |
} | |
`; | |
const showUVsFshader = ` | |
precision mediump float; | |
varying vec2 v_uv; | |
void main () { | |
gl_FragColor = vec4(v_uv,0,1); | |
} | |
`; | |
const showPositionsVshader = ` | |
precision mediump float; | |
attribute vec2 a_position; | |
uniform float u_clip_y; | |
varying vec2 v_uv; | |
void main() { | |
gl_Position = vec4(a_position * vec2(1,u_clip_y), 0, 1); | |
v_uv = gl_Position.xy; | |
} | |
`; | |
const showPositionsFshader = ` | |
precision mediump float; | |
varying vec2 v_uv; | |
void main () { | |
gl_FragColor = vec4(v_uv,0,1); | |
} | |
`; | |
const directionsDataUri = ` | |
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAA | |
BACAIAAAAlC+aJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQ | |
UAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAEbSURBVGhD7dhRDsIgEI | |
RhjubNPHqlHUTAdjfRWRKa+UIirQnd376Z0vZZG1vQsfvB76WAa3 | |
En3yug3GHD0HX6gIZCAaYaEGdSQM2g9yjApADfpIBhTzQvIIgCTA | |
rwKcCkAJ8CTArwKcCkAN/56Y/8XAZCwH7AsS6sEDBseisEYF1YIW | |
DY9Lq7eW6Mjk29/Bk/YD+vO7Bc/D/rKULAqSbj80tHrOehPC9mjY | |
/krhkBeBF4HvZE6CgXRJgeW3wAPYMf0IwO1NO/RL2BhgJMCvApwK | |
QAnwJMCvApwKQAnwJMCvApwNQGYE/vmRowbCgUYLpbQHvJMi8gSN | |
TpmLsGxGWsH9Aq90gwfW1gwv9zx+qUr0mWD8hCps/uE5DSC/pgVD | |
kvIARVAAAAAElFTkSuQmCC`.replace(/\s*/g, ''); | |
const bitmaps = { | |
directions: {uri: directionsDataUri} | |
}; | |
module.exports = {verts, indices, uvs, shader: {vert: vshader, frag: fshader}, | |
show: { | |
uvs: {frag: showUVsFshader, vert: vshader}, | |
positions: {frag: showPositionsFshader, vert: showPositionsVshader} | |
}, | |
bitmaps}; | |
},{}]},{},[]) | |
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2hvbWUvYWRtaW4vYnJvd3NlcmlmeS1jZG4vbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsImdsc2wtcXVhZC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIlxuY29uc3QgdmVydHMgPSBbXG4gIFstMS4wLCAtMS4wXSxcbiAgWysxLjAsIC0xLjBdLFxuICBbLTEuMCwgKzEuMF0sXG4gIFstMS4wLCArMS4wXSxcbiAgWysxLjAsIC0xLjBdLFxuICBbKzEuMCwgKzEuMF1cbl07XG5cbmNvbnN0IHV2cyA9IFtcbiAgWzAuMCwgMC4wXSxcbiAgWzEuMCwgMC4wXSxcbiAgWzAuMCwgMS4wXSxcbiAgWzAuMCwgMS4wXSxcbiAgWzEuMCwgMC4wXSxcbiAgWzEuMCwgMS4wXVxuXTtcblxuY29uc3QgaW5kaWNlcyA9IFtcbiAgWzAsIDEsIDJdLFxuICBbMywgNCwgNV1cbl07XG5cbmNvbnN0IHZzaGFkZXIgPSBgXG4gIHByZWNpc2lvbiBtZWRpdW1wIGZsb2F0O1xuICBhdHRyaWJ1dGUgdmVjMiBhX3Bvc2l0aW9uO1xuICBhdHRyaWJ1dGUgdmVjMiBhX3V2O1xuXG4gIHVuaWZvcm0gZmxvYXQgdV9jbGlwX3k7XG5cbiAgdmFyeWluZyB2ZWMyIHZfdXY7XG4gIFxuICB2b2lkIG1haW4oKSB7XG4gICAgdl91diA9IGFfdXY7XG4gICAgZ2xfUG9zaXRpb24gPSB2ZWM0KGFfcG9zaXRpb24gKiB2ZWMyKDEsdV9jbGlwX3kpLCAwLCAxKTtcbiAgfVxuYDtcblxuY29uc3QgZnNoYWRlciA9IGBcbiAgcHJlY2lzaW9uIG1lZGl1bXAgZmxvYXQ7XG4gIHZhcnlpbmcgdmVjMiB2X3V2O1xuICB1bmlmb3JtIHNhbXBsZXIyRCB1X3RleDtcbiAgdm9pZCBtYWluICgpIHtcbiAgICBnbF9GcmFnQ29sb3IgPSB0ZXh0dXJlMkQodV90ZXgsdl91dik7XG4gIH1cbmA7XG5cbmNvbnN0IHNob3dVVnNGc2hhZGVyID0gYFxuICBwcmVjaXNpb24gbWVkaXVtcCBmbG9hdDtcbiAgdmFyeWluZyB2ZWMyIHZfdXY7XG4gIHZvaWQgbWFpbiAoKSB7XG4gICAgZ2xfRnJhZ0NvbG9yID0gdmVjNCh2X3V2LDAsMSk7XG4gIH1cbmA7XG5cblxuY29uc3Qgc2hvd1Bvc2l0aW9uc1ZzaGFkZXIgPSBgXG4gIHByZWNpc2lvbiBtZWRpdW1wIGZsb2F0O1xuICBhdHRyaWJ1dGUgdmVjMiBhX3Bvc2l0aW9uO1xuXG4gIHVuaWZvcm0gZmxvYXQgdV9jbGlwX3k7XG5cbiAgdmFyeWluZyB2ZWMyIHZfdXY7XG4gIFxuICB2b2lkIG1haW4oKSB7XG4gICAgZ2xfUG9zaXRpb24gPSB2ZWM0KGFfcG9zaXRpb24gKiB2ZWMyKDEsdV9jbGlwX3kpLCAwLCAxKTtcbiAgICB2X3V2ID0gZ2xfUG9zaXRpb24ueHk7XG4gIH1cbmA7XG5cbmNvbnN0IHNob3dQb3NpdGlvbnNGc2hhZGVyID0gYFxuICBwcmVjaXNpb24gbWVkaXVtcCBmbG9hdDtcbiAgdmFyeWluZyB2ZWMyIHZfdXY7XG4gIHZvaWQgbWFpbiAoKSB7XG4gICAgZ2xfRnJhZ0NvbG9yID0gdmVjNCh2X3V2LDAsMSk7XG4gIH1cbmA7XG5cbmNvbnN0IGRpcmVjdGlvbnNEYXRhVXJpID0gYFxuZGF0YTppbWFnZS9wbmc7YmFzZTY0LGlWQk9SdzBLR2dvQUFBQU5TVWhFVWdBQUFFQUFBQVxuQkFDQUlBQUFBbEMrYUpBQUFBQVhOU1IwSUFyczRjNlFBQUFBUm5RVTFCQUFDeGp3djhZUVxuVUFBQUFKY0VoWmN3QUFFblFBQUJKMEFkNW1IM2dBQUFFYlNVUkJWR2hEN2RoUkRzSWdFSVxuUmhqdWJOUEhxbEhVVEFkamZSV1JLYStVSWlyUW5kMzc2WjB2WlpHMXZRc2Z2Qjc2V0FhM1xuRW4zeXVnM0dIRDBIWDZnSVpDQWFZYUVHZFNRTTJnOXlqQXBBRGZwSUJoVHpRdklJZ0NUQVxucndLY0NrQUo4Q1RBcndLY0NrQU4vNTZZLzhYQVpDd0g3QXNTNnNFREJzZWlzRVlGMVlJV1xuRFk5THE3ZVc2TWprMjkvQmsvWUQrdk83QmMvRC9yS1VMQXFTYmo4MHRIck9laFBDOW1qWVxuL2tyaGtCZUJGNEh2WkU2Q2dYUkpnZVczd0FQWU1mMEl3TzFOTy9STDJCaGdKTUN2QXB3S1xuUUFud0pNQ3ZBcHdLUUFud0pNQ3ZBcHdOUUdZRS92bVJvd2JDZ1VZTHBiUUh2Sk1pOGdTTlxuVHBtTHNHeEdXc0g5QXE5MGd3ZlcxZ3d2OXp4K3FVcjBtV0Q4aENwcy91RTVEU0MvcGdWRFxua3ZJQVJWQUFBQUFFbEZUa1N1UW1DQ2AucmVwbGFjZSgvXFxzKi9nLCAnJyk7XG5cbmNvbnN0IGJpdG1hcHMgPSB7XG4gIGRpcmVjdGlvbnM6IHt1cmk6IGRpcmVjdGlvbnNEYXRhVXJpfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSB7dmVydHMsIGluZGljZXMsIHV2cywgc2hhZGVyOiB7dmVydDogdnNoYWRlciwgZnJhZzogZnNoYWRlcn0sXG4gICAgICAgICAgICAgICAgICBzaG93OiB7XG4gICAgICAgICAgICAgICAgICAgIHV2czoge2ZyYWc6IHNob3dVVnNGc2hhZGVyLCB2ZXJ0OiB2c2hhZGVyfSxcbiAgICAgICAgICAgICAgICAgICAgcG9zaXRpb25zOiB7ZnJhZzogc2hvd1Bvc2l0aW9uc0ZzaGFkZXIsIHZlcnQ6IHNob3dQb3NpdGlvbnNWc2hhZGVyfVxuICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgIGJpdG1hcHN9O1xuIl19 | |
const resl = require('resl'); | |
const regl = require('regl')(); | |
const quad = require('glsl-quad'); | |
resl({ | |
manifest: { | |
texture: { | |
type: 'image', | |
// quad provides a bitmap as a uri to display; the "directions" bitmap shows two | |
// axis with up/down/right/left lines/arrows. | |
src: quad.bitmaps.directions.uri, | |
parser: (data) => regl.texture({ | |
data: data, | |
mag: 'nearest', | |
min: 'nearest', | |
flipY: true | |
}) | |
} | |
}, | |
onDone: ({texture}) => { | |
const drawTexture = regl({ | |
vert: quad.show.uvs.vert, | |
frag: quad.show.uvs.frag, | |
attributes: { | |
a_position: quad.verts, | |
a_uv: quad.uvs | |
}, | |
elements: quad.indices, | |
frontFace: 'ccw', | |
cull: { | |
enable: false, | |
face: 'back' | |
}, | |
uniforms: { | |
u_tex: regl.prop('texture'), | |
// Use a negative y, webgl display works upside down. I think. | |
u_clip_y: -1 | |
}, | |
viewport: { | |
x: 0, | |
y: 0, | |
width: texture.width, | |
height: texture.height | |
} | |
}); | |
drawTexture({texture}); | |
} | |
}); | |
;}, 0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "requirebin-sketch", | |
"version": "1.0.0", | |
"depdencies": { | |
"regl": "^0.10.0" | |
}, | |
"dependencies": { | |
"resl": "1.0.1", | |
"regl": "0.10.0", | |
"glsl-quad": "1.0.0" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- contents of this file will be placed inside the <body> --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- contents of this file will be placed inside the <head> --> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment