Skip to content

Instantly share code, notes, and snippets.

@o0101
Last active March 6, 2018 04:39
Show Gist options
  • Save o0101/0651c4d15c202b3e986e764294d3b028 to your computer and use it in GitHub Desktop.
Save o0101/0651c4d15c202b3e986e764294d3b028 to your computer and use it in GitHub Desktop.
requirebin sketch
const dc = require("dosycrypt");
self.dc = dc;
const page = ({message:message = ''}={}) => `
<title>DOSYCRYPT</title>
<meta name=viewport content="width=device-width, initial-scale=1">
<style>
:root, body, input, textarea, button {
background: orange; color: white;
}
form:invalid button.validonly {
display: none;
}
form {
display: table;
margin: 2rem auto;
}
</style>
<form>
<fieldset><legend>DOSYCRYPT</legend>
<p>
<input id=key type=password required
name=key placeholder="key (spaces okay)" minlength=4>
<p>
<textarea required id=message rows=8 cols=28
placeholder=message name=message>${safe(message)}</textarea>
<p>
<button class=validonly id=enc name=mode value=encrypt>Encrypt</button>
<button class=validonly id=dec name=mode value=decrypt>Decrypt</button>
<button id=res type=button>Destroy</button>
</fieldset>
</form>
<script>
enc.onclick = e => {
e.preventDefault();
const c = dc.full_encrypt( message.value, key.value );
document.body.innerHTML = '';
document.write(page({message:btoa(c)}));
};
dec.onclick = e => {
e.preventDefault();
const p = dc.full_decrypt( atob(message.value), key.value );
document.body.innerHTML = '';
document.write(page({message:p}));
};
res.onclick = e => {
key.value = '';
message.value = '';
};
</script>`;
self.page = page;
document.write(page());
function safe(s) {
return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').
replace(/>/g,'&gt;').replace(/"/g, '&quot;');
}
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})({1:[function(require,module,exports){
// shim for using process in browser
var process = module.exports = {};
// cached from whatever global is present so that test runners that stub it
// don't break things. But we need to wrap it in a try catch in case it is
// wrapped in strict mode code which doesn't define any globals. It's inside a
// function because try/catches deoptimize in certain engines.
var cachedSetTimeout;
var cachedClearTimeout;
function defaultSetTimout() {
throw new Error('setTimeout has not been defined');
}
function defaultClearTimeout () {
throw new Error('clearTimeout has not been defined');
}
(function () {
try {
if (typeof setTimeout === 'function') {
cachedSetTimeout = setTimeout;
} else {
cachedSetTimeout = defaultSetTimout;
}
} catch (e) {
cachedSetTimeout = defaultSetTimout;
}
try {
if (typeof clearTimeout === 'function') {
cachedClearTimeout = clearTimeout;
} else {
cachedClearTimeout = defaultClearTimeout;
}
} catch (e) {
cachedClearTimeout = defaultClearTimeout;
}
} ())
function runTimeout(fun) {
if (cachedSetTimeout === setTimeout) {
//normal enviroments in sane situations
return setTimeout(fun, 0);
}
// if setTimeout wasn't available but was latter defined
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
cachedSetTimeout = setTimeout;
return setTimeout(fun, 0);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedSetTimeout(fun, 0);
} catch(e){
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedSetTimeout.call(null, fun, 0);
} catch(e){
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
return cachedSetTimeout.call(this, fun, 0);
}
}
}
function runClearTimeout(marker) {
if (cachedClearTimeout === clearTimeout) {
//normal enviroments in sane situations
return clearTimeout(marker);
}
// if clearTimeout wasn't available but was latter defined
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
cachedClearTimeout = clearTimeout;
return clearTimeout(marker);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedClearTimeout(marker);
} catch (e){
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedClearTimeout.call(null, marker);
} catch (e){
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
return cachedClearTimeout.call(this, marker);
}
}
}
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;
function cleanUpNextTick() {
if (!draining || !currentQueue) {
return;
}
draining = false;
if (currentQueue.length) {
queue = currentQueue.concat(queue);
} else {
queueIndex = -1;
}
if (queue.length) {
drainQueue();
}
}
function drainQueue() {
if (draining) {
return;
}
var timeout = runTimeout(cleanUpNextTick);
draining = true;
var len = queue.length;
while(len) {
currentQueue = queue;
queue = [];
while (++queueIndex < len) {
if (currentQueue) {
currentQueue[queueIndex].run();
}
}
queueIndex = -1;
len = queue.length;
}
currentQueue = null;
draining = false;
runClearTimeout(timeout);
}
process.nextTick = function (fun) {
var args = new Array(arguments.length - 1);
if (arguments.length > 1) {
for (var i = 1; i < arguments.length; i++) {
args[i - 1] = arguments[i];
}
}
queue.push(new Item(fun, args));
if (queue.length === 1 && !draining) {
runTimeout(drainQueue);
}
};
// v8 likes predictible objects
function Item(fun, array) {
this.fun = fun;
this.array = array;
}
Item.prototype.run = function () {
this.fun.apply(null, this.array);
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};
function noop() {}
process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.binding = function (name) {
throw new Error('process.binding is not supported');
};
process.cwd = function () { return '/' };
process.chdir = function (dir) {
throw new Error('process.chdir is not supported');
};
process.umask = function() { return 0; };
},{}],2:[function(require,module,exports){
"use strict";
{
const dosybytes = {
bin2hex, hex2bin,
toBinary, toHex,
fromBinary, fromHex,
pad
};
try { module.exports = dosybytes; } catch(e) { Object.assign( self, { dosybytes } ); }
function toBinary( bytes ) {
return Array.from( bytes ).reduce( (bs,bv) => bs + String.fromCharCode(bv), "" );
}
function bin2hex( binstr ) {
return toHex( fromBinary( binstr ) );
}
function hex2bin( hexstr ) {
return toBinary( fromHex( hexstr ) );
}
function toHex( bytes ) {
return Array.from( bytes ).reduce( (hs,bv) => hs + pad( bv.toString(16), 2, '0', true ), "" );
}
function fromBinary( binstr ) {
return new Uint8Array( Array.from( binstr ).reduce( (ba,c) => (ba.push(c.charCodeAt(0)), ba), [] ) );
}
function fromHex( hexstr ) {
return new Uint8Array( Array.from( hexstr ).reduce(
(pa,c,i) => i % 2 ? (pa[pa.length-1]+=c, pa) : (pa.push(c), pa) ,
[]
).reduce(
(ba,hn) => (ba.push( parseInt(hn, 16)), ba),
[]
) );
}
function pad( str, width, char, left, right ) {
const padding_length = Math.max( 0, width - str.length );
const padding = new Array( padding_length + 1 ).join( char );
if( left ) {
str = padding + str
}
if ( right ) {
str = str + padding;
}
return str;
}
}
},{}],3:[function(require,module,exports){
"use strict";
{
// DOSYRNG - A family of 8-bit PRNGs ( CSPRNGs ) that are extraordinarily simple & pass PracRand
// For ref of PracRand - http://pracrand.sourceforge.net/
// To access the state ( to say, 'key' the generator ), pass in a 'surface' object
const dosyrng = {
d451: surface => iterator( 45, 1, surface ), // passes PracRand
d453: surface => iterator( 45, 3, surface ), // passes PracRand
custom: iterator // other values are untested. Set your own!
};
// Node.js or Browser, either is fine
try { module.exports = dosyrng; } catch( e ) { Object.assign( self, { dosyrng } ); }
// The main DOSY round function very simple and easy to memorize
function update( s, SZ, shift ) {
let j = SZ-1;
let sum = 1;
for( let i = 0; i < SZ; i++ ) {
s[j] ^= (s[i] >> shift) ^ (sum << shift);
s[i] += s[j] + 1;
j = ( j + 1 ) % SZ;
sum += s[i];
}
return sum & 255;
}
// An iterator wrapper to create the state and turn the round function
function iterator( state_sz = 45 /* bytes */, shift = 1 /* bits */, surface = {} /* .s is the state */) {
const s = new Uint8Array(state_sz);
const update_state = update.bind( null, s, state_sz, shift );
expose( surface, 'state', s );
return {
round() {
return update_state();
},
[Symbol.iterator]() {
return make_iterable( {
func_source: update_state,
max_iterations: this.length
} );
}
};
}
function make_iterable( {
func_source : func_source = () => 0,
max_iterations : max_iterations = null } = {}
) {
if ( Number.isInteger(max_iterations) ) {
const length = max_iterations;
let i = 0;
return {
next() {
if ( i < length ) {
i++;
return { value : func_source(), done : false };
} else {
return { done: true };
}
}
};
} else {
return {
next() {
return { value : func_source(), done : false };
}
}
}
}
function expose( obj, key, val ) {
Object.defineProperty( obj, key, { enumerable: true, get : () => val } );
}
}
},{}],4:[function(require,module,exports){
"use strict";
{
class UTF8Str extends String {
constructor( s = '' ) {
super(s);
const b = bytes(s);
const blen = b.length;
const syms = symbols(s);
const slen = syms.length;
Object.defineProperty( this, 'bytes', { value: Uint8Array.from(b), enumerable: true});
Object.defineProperty( this, 'byteLength', { value: blen, enumerable:true });
Object.defineProperty( this, 'chars', { value: Array.from(syms), enumerable: true});
Object.defineProperty( this, 'charLength', { value: slen, enumerable: true});
}
static fromBytes(b) {
return frombytes(b);
}
static fromUTF8Binary(bs) {
return frombinary(bs);
}
}
try{ module.exports = UTF8Str; } catch(e) { Object.assign( self, { UTF8Str } ); }
//test_basics();
function symbols(str) {
return Array.from(str);
}
function frombinary(bs) {
return new UTF8Str( decodeURIComponent(escape(bs) ) );
}
function frombytes(b) {
return frombinary( Array.from(b).map( v => String.fromCharCode(v) ).join(''));
}
function symbytes( sym ) {
const utf8 = unescape(encodeURIComponent(sym)).split('');
return Uint8Array.from( utf8.map( c => c.codePointAt(0) ) );
}
function bytes(str) {
const bs = symbols(str).reduce(
(b,s) => (b.push(...symbytes(s)), b),
[]
);
return Uint8Array.from(bs);
}
function test(t) {
console.log(t);
console.log( new UTF8Str(t));
console.log(Array.from(bytes(t)).map( b => b.toString(16) ));
const x = new UTF8Str(t);
console.log("fbytes", UTF8Str.fromBytes(x.bytes));
}
function test_basics() {
test("😂😌");
test("Foo © bar 𝌆 baz ☃ qux");;
}
}
},{}],"dosycrypt":[function(require,module,exports){
(function (process){
"use strict";
{
const UTF8Str = require('utf8str');
const dosyrng = require('dosyrng');
const bytes = require('dosybytes');
const dosycrypt = {
rng1: surface => dosyrng.d451( surface ),
rng2: surface => dosyrng.d453( surface )
};
// cross cutting concerns
Object.assign( dosycrypt, {
instance, include
});
function instance( algo ) {
const inst = {
round: () => source.round()
};
const source = algo( inst );
source.round();
return inst;
}
function include( vals, instance ) {
vals.forEach( (val, index) => {
instance.state[index] ^= val;
});
}
function stringify( vals ) {
return UTF8Str.fromBytes( vals );
}
// hash construction algorithms
{
const TURNS = 16;
const BLOCK_SZ = 5;
Object.assign( dosycrypt, {
hash, test_hash
});
function hash( message, digest_sz, algo = dosycrypt.rng2 ) {
const hasher = instance( algo );
absorb( message, hasher );
const digest = squeeze( message, digest_sz, hasher );
return bytes.toHex( digest );
}
function absorb( message, hasher ) {
const m = new UTF8Str( message ).bytes;
let i = 0;
while( i < m.length ) {
const chunk = m.subarray(i, BLOCK_SZ);
include( chunk, hasher );
let turns = TURNS;
while( turns-- ) {
hasher.round();
}
i += BLOCK_SZ;
}
}
function squeeze( message, digest_sz, hasher ) {
const digest = [];
while( digest_sz ) {
const next_slice = Math.min( BLOCK_SZ, digest_sz );
digest.push( ...hasher.state.slice(0, next_slice) );
digest_sz -= next_slice;
let turns = TURNS;
while( turns-- ) {
hasher.round();
}
}
return digest;
}
function test_hash() {
const message = "THIS IS A TEST!"
const digest = hash( message, 32 );
//console.log( "Message", message, "hash", digest );
}
}
// cipher construction algorithms
{
const KEY_SCHEDULE_ROUNDS = 32;
Object.assign( dosycrypt, {
encrypt, decrypt, schedule, test_cipher
});
function schedule( key, inst ) {
const key_vals = new UTF8Str( key ).bytes;
include( key_vals, inst );
let turns = KEY_SCHEDULE_ROUNDS;
while( turns-- ) {
inst.round();
}
}
function encrypt( key, plain, algo = dosycrypt.rng1, existing_instance ) {
plain = new UTF8Str( plain ).bytes;
const cipher = [];
const inst = existing_instance || instance( algo );
if ( !! key ) {
schedule( key, inst );
}
plain.forEach( val => {
cipher.push( val ^ inst.round() );
});
return cipher;
}
function decrypt( key, cipher, algo = dosycrypt.rng1, existing_instance ) {
cipher = bytes.fromBinary( cipher );
const plain = [];
const inst = existing_instance || instance( algo );
if ( !! key ) {
schedule( key, inst );
}
cipher.forEach( val => {
plain.push( val ^ inst.round() );
});
return plain;
}
function test_cipher() {
const message = "THIS IS A SECRET!"
const key = "thisisakey";
const cipher = encrypt( key, message );
const cipher_string = bytes.toBinary( cipher );
const plain = decrypt( key, cipher_string );
//console.log( "Message", message, "key", key );
//console.log( "cipher", bytes.toHex( cipher ) );
//console.log( "plain", stringify( plain ) );
}
}
// entropy collection algorithms
{
const RUN = 31;
let count = 0, result = 0;
let browser = false;
Object.assign( dosycrypt, {
collect_entropy_bytes, test_entropy
});
try {
if ( self ) {
browser = true;
}
} catch( e ) {
browser = false;
}
// timestamp with high res
function ts() {
if ( ! browser ) {
return process.hrtime()[1];
} else if ( !! self.performance ) {
return Math.round( self.performance.now() * 10 );
} else {
console.warn( "Date only - low res entropy warning!" );
return Date.now();
}
}
function float() {
count += Math.PI/180;
result += Math.sin( count ) * Math.cos( count );
}
function time_float_run() {
let run = RUN;
const start = ts();
while( run-- ) {
float();
}
const end = ts();
return end - start;
}
function collect_bits( n ) {
const bits = [];
while( n-- ) {
bits.push( time_float_run() % 2 );
}
return bits;
}
function bits_to_bytes( bits ) {
const bytes = [];
while( bits.length ) {
const val = parseInt( bits.splice(0,8).join(''), 2 );
bytes.push( String.fromCharCode( val ) );
}
return bytes;
}
function collect_entropy_bytes( n = 32 ) {
const bits = collect_bits( n * 8 );
const binary = bits_to_bytes( bits );
return binary.join('');
}
function test_entropy() {
let run = 100;
while( run--) {
//console.log( "Float run time", time_float_run() );
}
//console.log( "32 bytes of entropy", bytes.bin2hex( collect_entropy_bytes() ));
//console.log( "32 bytes of entropy", bytes.bin2hex( collect_entropy_bytes() ));
//console.log( "32 bytes of entropy", bytes.bin2hex( collect_entropy_bytes() ));
//console.log( "32 bytes of entropy", bytes.bin2hex( collect_entropy_bytes() ));
//console.log( "32 bytes of entropy", bytes.bin2hex( collect_entropy_bytes() ));
}
}
// initialization vector algorithms
{
const IV_ENTROPY_BYTES = 8;
const IV_BYTES = 16;
Object.assign( dosycrypt, {
generate_iv, test_iv
} );
function generate_iv( entropy_sz = IV_ENTROPY_BYTES, iv_sz = IV_BYTES ) {
const bytes = dosycrypt.collect_entropy_bytes( entropy_sz );
const digest = dosycrypt.hash( bytes, iv_sz );
return digest;
}
function test_iv() {
//console.log( "IV", generate_iv() );
}
}
// full encryption and integrity algorithm
/**
Format
Encryption:
E(K,IV):E(K+IV,data:H(IV:data))
Decryption:
Schedule K.
Decrypt DOSY
Decrypt IV up until ":" character.
Schedule IV.
Decrypt until end.
Split data from hash.
Append data to IV with ":" character.
Compute hash and check if matches.
If matches return data. If does not match
return integity error, ( key incorrect or data is corrupted ).
**/
{
const IV_ENTROPY = 15;
const IV_SZ = 15;
const HASH_SZ = 32;
Object.assign( dosycrypt, {
full_encrypt, full_decrypt, test_full_cipher, test_full_cipher2
});
function full_encrypt( data, key ) {
const inst = instance( dosycrypt.rng1 );
const iv = dosycrypt.generate_iv( IV_ENTROPY, IV_SZ );
//console.log("IV", iv);
// schedule key and encrypt iv
const e_iv = bytes.toBinary( dosycrypt.encrypt( key, iv + ":", null, inst ) );
// schedule iv
dosycrypt.schedule( iv, inst );
// form iv:data to hash it
const hashable = iv + ":" + data;
//console.log( "Hashable", hashable );
// compute its hash
const hash = dosycrypt.hash( hashable, HASH_SZ );
// form data:hash
const plain = data + ":" + hash;
// encrypt it
const e_plain = bytes.toBinary( dosycrypt.encrypt( null, plain, null, inst ) );
// combine
const cipher = e_iv + e_plain;
return cipher;
}
function full_decrypt( cipher, key ) {
const inst = instance( dosycrypt.rng1 );
dosycrypt.schedule( key, inst );
const iv = [];
const plain = [];
let iv_str;
let iv_mode = true;
bytes.fromBinary( cipher ).forEach( val => {
const p = val ^ inst.round();
if ( iv_mode ) {
if ( p == ":".charCodeAt(0) ) {
iv_str = bytes.toBinary( iv );
dosycrypt.schedule( iv_str, inst );
iv_mode = false;
return; // discard ":"
}
iv.push( p );
} else {
plain.push( p );
}
});
let plain_str;
//console.log( plain );
try {
plain_str = stringify( plain );
} catch(e) {
throw new TypeError( "Cannot decrypt." );
}
const hash_sep = plain_str.lastIndexOf( ":" );
if ( hash_sep == -1 ) {
throw new TypeError( "Cannot decrypt." );
}
const hash = plain_str.slice( hash_sep + 1 );
const data = plain_str.slice( 0, hash_sep );
const hashable = iv_str + ":" + data;
const computed_hash = dosycrypt.hash( hashable, HASH_SZ );
if ( hash == computed_hash ) {
//console.log( "IV", iv_str, "plain", plain_str );
//console.log( "Hashable", hashable );
//console.log( "computed_hash", computed_hash );
//console.log( "Computed hash equals. Data is valid." );
const data = plain_str.slice(0, hash_sep );
return data;
} else {
throw new TypeError( "Cannot decrypt." );
}
}
function test_full_cipher() {
const plain = "THIS IS SOME REAL DATA WOO";
const key = "thisisasecretkey";
//console.log( "Plain", plain, "key", key );
const cipher = full_encrypt( plain, key );
//console.log( "Cipher", bytes.bin2hex( cipher ) );
const decrypted = full_decrypt( cipher, key );
//console.log( "Decrypted", decrypted );
}
function test_full_cipher2() {
const plain = "Foo © bar 𝌆 baz ☃ qux";
const key = "thisisasecretkey";
//console.log( "Plain", plain, "key", key );
const cipher = full_encrypt( plain, key );
//console.log( "Cipher", bytes.bin2hex( cipher ) );
const decrypted = full_decrypt( cipher, key );
//console.log( "Decrypted", decrypted );
}
}
// tests
{
Object.assign( dosycrypt, {
test_all
});
function test_all() {
dosycrypt.test_hash();
dosycrypt.test_cipher();
dosycrypt.test_iv();
dosycrypt.test_entropy();
dosycrypt.test_full_cipher();
dosycrypt.test_full_cipher2();
}
}
//dosycrypt.test_all();
try {
module.exports = dosycrypt;
} catch(e) {
Object.assign( self, { dosycrypt } );
}
}
}).call(this,require('_process'))
},{"_process":1,"dosybytes":2,"dosyrng":3,"utf8str":4}]},{},[])
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2hvbWUvYWRtaW4vYnJvd3NlcmlmeS1jZG4vbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsIi4uLy4uLy4uLy4uL2hvbWUvYWRtaW4vYnJvd3NlcmlmeS1jZG4vbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL3Byb2Nlc3MvYnJvd3Nlci5qcyIsIm5vZGVfbW9kdWxlcy9kb3N5Ynl0ZXMvaW5kZXguanMiLCJub2RlX21vZHVsZXMvZG9zeXJuZy9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy91dGY4c3RyL2luZGV4LmpzIiwiaW5kZXguanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwTEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FDaEVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCIvLyBzaGltIGZvciB1c2luZyBwcm9jZXNzIGluIGJyb3dzZXJcbnZhciBwcm9jZXNzID0gbW9kdWxlLmV4cG9ydHMgPSB7fTtcblxuLy8gY2FjaGVkIGZyb20gd2hhdGV2ZXIgZ2xvYmFsIGlzIHByZXNlbnQgc28gdGhhdCB0ZXN0IHJ1bm5lcnMgdGhhdCBzdHViIGl0XG4vLyBkb24ndCBicmVhayB0aGluZ3MuICBCdXQgd2UgbmVlZCB0byB3cmFwIGl0IGluIGEgdHJ5IGNhdGNoIGluIGNhc2UgaXQgaXNcbi8vIHdyYXBwZWQgaW4gc3RyaWN0IG1vZGUgY29kZSB3aGljaCBkb2Vzbid0IGRlZmluZSBhbnkgZ2xvYmFscy4gIEl0J3MgaW5zaWRlIGFcbi8vIGZ1bmN0aW9uIGJlY2F1c2UgdHJ5L2NhdGNoZXMgZGVvcHRpbWl6ZSBpbiBjZXJ0YWluIGVuZ2luZXMuXG5cbnZhciBjYWNoZWRTZXRUaW1lb3V0O1xudmFyIGNhY2hlZENsZWFyVGltZW91dDtcblxuZnVuY3Rpb24gZGVmYXVsdFNldFRpbW91dCgpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ3NldFRpbWVvdXQgaGFzIG5vdCBiZWVuIGRlZmluZWQnKTtcbn1cbmZ1bmN0aW9uIGRlZmF1bHRDbGVhclRpbWVvdXQgKCkge1xuICAgIHRocm93IG5ldyBFcnJvcignY2xlYXJUaW1lb3V0IGhhcyBub3QgYmVlbiBkZWZpbmVkJyk7XG59XG4oZnVuY3Rpb24gKCkge1xuICAgIHRyeSB7XG4gICAgICAgIGlmICh0eXBlb2Ygc2V0VGltZW91dCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgY2FjaGVkU2V0VGltZW91dCA9IHNldFRpbWVvdXQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjYWNoZWRTZXRUaW1lb3V0ID0gZGVmYXVsdFNldFRpbW91dDtcbiAgICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY2FjaGVkU2V0VGltZW91dCA9IGRlZmF1bHRTZXRUaW1vdXQ7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICAgIGlmICh0eXBlb2YgY2xlYXJUaW1lb3V0ID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICBjYWNoZWRDbGVhclRpbWVvdXQgPSBjbGVhclRpbWVvdXQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjYWNoZWRDbGVhclRpbWVvdXQgPSBkZWZhdWx0Q2xlYXJUaW1lb3V0O1xuICAgICAgICB9XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBjYWNoZWRDbGVhclRpbWVvdXQgPSBkZWZhdWx0Q2xlYXJUaW1lb3V0O1xuICAgIH1cbn0gKCkpXG5mdW5jdGlvbiBydW5UaW1lb3V0KGZ1bikge1xuICAgIGlmIChjYWNoZWRTZXRUaW1lb3V0ID09PSBzZXRUaW1lb3V0KSB7XG4gICAgICAgIC8vbm9ybWFsIGVudmlyb21lbnRzIGluIHNhbmUgc2l0dWF0aW9uc1xuICAgICAgICByZXR1cm4gc2V0VGltZW91dChmdW4sIDApO1xuICAgIH1cbiAgICAvLyBpZiBzZXRUaW1lb3V0IHdhc24ndCBhdmFpbGFibGUgYnV0IHdhcyBsYXR0ZXIgZGVmaW5lZFxuICAgIGlmICgoY2FjaGVkU2V0VGltZW91dCA9PT0gZGVmYXVsdFNldFRpbW91dCB8fCAhY2FjaGVkU2V0VGltZW91dCkgJiYgc2V0VGltZW91dCkge1xuICAgICAgICBjYWNoZWRTZXRUaW1lb3V0ID0gc2V0VGltZW91dDtcbiAgICAgICAgcmV0dXJuIHNldFRpbWVvdXQoZnVuLCAwKTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgICAgLy8gd2hlbiB3aGVuIHNvbWVib2R5IGhhcyBzY3Jld2VkIHdpdGggc2V0VGltZW91dCBidXQgbm8gSS5FLiBtYWRkbmVzc1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0VGltZW91dChmdW4sIDApO1xuICAgIH0gY2F0Y2goZSl7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICAvLyBXaGVuIHdlIGFyZSBpbiBJLkUuIGJ1dCB0aGUgc2NyaXB0IGhhcyBiZWVuIGV2YWxlZCBzbyBJLkUuIGRvZXNuJ3QgdHJ1c3QgdGhlIGdsb2JhbCBvYmplY3Qgd2hlbiBjYWxsZWQgbm9ybWFsbHlcbiAgICAgICAgICAgIHJldHVybiBjYWNoZWRTZXRUaW1lb3V0LmNhbGwobnVsbCwgZnVuLCAwKTtcbiAgICAgICAgfSBjYXRjaChlKXtcbiAgICAgICAgICAgIC8vIHNhbWUgYXMgYWJvdmUgYnV0IHdoZW4gaXQncyBhIHZlcnNpb24gb2YgSS5FLiB0aGF0IG11c3QgaGF2ZSB0aGUgZ2xvYmFsIG9iamVjdCBmb3IgJ3RoaXMnLCBob3BmdWxseSBvdXIgY29udGV4dCBjb3JyZWN0IG90aGVyd2lzZSBpdCB3aWxsIHRocm93IGEgZ2xvYmFsIGVycm9yXG4gICAgICAgICAgICByZXR1cm4gY2FjaGVkU2V0VGltZW91dC5jYWxsKHRoaXMsIGZ1biwgMCk7XG4gICAgICAgIH1cbiAgICB9XG5cblxufVxuZnVuY3Rpb24gcnVuQ2xlYXJUaW1lb3V0KG1hcmtlcikge1xuICAgIGlmIChjYWNoZWRDbGVhclRpbWVvdXQgPT09IGNsZWFyVGltZW91dCkge1xuICAgICAgICAvL25vcm1hbCBlbnZpcm9tZW50cyBpbiBzYW5lIHNpdHVhdGlvbnNcbiAgICAgICAgcmV0dXJuIGNsZWFyVGltZW91dChtYXJrZXIpO1xuICAgIH1cbiAgICAvLyBpZiBjbGVhclRpbWVvdXQgd2Fzbid0IGF2YWlsYWJsZSBidXQgd2FzIGxhdHRlciBkZWZpbmVkXG4gICAgaWYgKChjYWNoZWRDbGVhclRpbWVvdXQgPT09IGRlZmF1bHRDbGVhclRpbWVvdXQgfHwgIWNhY2hlZENsZWFyVGltZW91dCkgJiYgY2xlYXJUaW1lb3V0KSB7XG4gICAgICAgIGNhY2hlZENsZWFyVGltZW91dCA9IGNsZWFyVGltZW91dDtcbiAgICAgICAgcmV0dXJuIGNsZWFyVGltZW91dChtYXJrZXIpO1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgICAvLyB3aGVuIHdoZW4gc29tZWJvZHkgaGFzIHNjcmV3ZWQgd2l0aCBzZXRUaW1lb3V0IGJ1dCBubyBJLkUuIG1hZGRuZXNzXG4gICAgICAgIHJldHVybiBjYWNoZWRDbGVhclRpbWVvdXQobWFya2VyKTtcbiAgICB9IGNhdGNoIChlKXtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIFdoZW4gd2UgYXJlIGluIEkuRS4gYnV0IHRoZSBzY3JpcHQgaGFzIGJlZW4gZXZhbGVkIHNvIEkuRS4gZG9lc24ndCAgdHJ1c3QgdGhlIGdsb2JhbCBvYmplY3Qgd2hlbiBjYWxsZWQgbm9ybWFsbHlcbiAgICAgICAgICAgIHJldHVybiBjYWNoZWRDbGVhclRpbWVvdXQuY2FsbChudWxsLCBtYXJrZXIpO1xuICAgICAgICB9IGNhdGNoIChlKXtcbiAgICAgICAgICAgIC8vIHNhbWUgYXMgYWJvdmUgYnV0IHdoZW4gaXQncyBhIHZlcnNpb24gb2YgSS5FLiB0aGF0IG11c3QgaGF2ZSB0aGUgZ2xvYmFsIG9iamVjdCBmb3IgJ3RoaXMnLCBob3BmdWxseSBvdXIgY29udGV4dCBjb3JyZWN0IG90aGVyd2lzZSBpdCB3aWxsIHRocm93IGEgZ2xvYmFsIGVycm9yLlxuICAgICAgICAgICAgLy8gU29tZSB2ZXJzaW9ucyBvZiBJLkUuIGhhdmUgZGlmZmVyZW50IHJ1bGVzIGZvciBjbGVhclRpbWVvdXQgdnMgc2V0VGltZW91dFxuICAgICAgICAgICAgcmV0dXJuIGNhY2hlZENsZWFyVGltZW91dC5jYWxsKHRoaXMsIG1hcmtlcik7XG4gICAgICAgIH1cbiAgICB9XG5cblxuXG59XG52YXIgcXVldWUgPSBbXTtcbnZhciBkcmFpbmluZyA9IGZhbHNlO1xudmFyIGN1cnJlbnRRdWV1ZTtcbnZhciBxdWV1ZUluZGV4ID0gLTE7XG5cbmZ1bmN0aW9uIGNsZWFuVXBOZXh0VGljaygpIHtcbiAgICBpZiAoIWRyYWluaW5nIHx8ICFjdXJyZW50UXVldWUpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBkcmFpbmluZyA9IGZhbHNlO1xuICAgIGlmIChjdXJyZW50UXVldWUubGVuZ3RoKSB7XG4gICAgICAgIHF1ZXVlID0gY3VycmVudFF1ZXVlLmNvbmNhdChxdWV1ZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgcXVldWVJbmRleCA9IC0xO1xuICAgIH1cbiAgICBpZiAocXVldWUubGVuZ3RoKSB7XG4gICAgICAgIGRyYWluUXVldWUoKTtcbiAgICB9XG59XG5cbmZ1bmN0aW9uIGRyYWluUXVldWUoKSB7XG4gICAgaWYgKGRyYWluaW5nKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdmFyIHRpbWVvdXQgPSBydW5UaW1lb3V0KGNsZWFuVXBOZXh0VGljayk7XG4gICAgZHJhaW5pbmcgPSB0cnVlO1xuXG4gICAgdmFyIGxlbiA9IHF1ZXVlLmxlbmd0aDtcbiAgICB3aGlsZShsZW4pIHtcbiAgICAgICAgY3VycmVudFF1ZXVlID0gcXVldWU7XG4gICAgICAgIHF1ZXVlID0gW107XG4gICAgICAgIHdoaWxlICgrK3F1ZXVlSW5kZXggPCBsZW4pIHtcbiAgICAgICAgICAgIGlmIChjdXJyZW50UXVldWUpIHtcbiAgICAgICAgICAgICAgICBjdXJyZW50UXVldWVbcXVldWVJbmRleF0ucnVuKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcXVldWVJbmRleCA9IC0xO1xuICAgICAgICBsZW4gPSBxdWV1ZS5sZW5ndGg7XG4gICAgfVxuICAgIGN1cnJlbnRRdWV1ZSA9IG51bGw7XG4gICAgZHJhaW5pbmcgPSBmYWxzZTtcbiAgICBydW5DbGVhclRpbWVvdXQodGltZW91dCk7XG59XG5cbnByb2Nlc3MubmV4dFRpY2sgPSBmdW5jdGlvbiAoZnVuKSB7XG4gICAgdmFyIGFyZ3MgPSBuZXcgQXJyYXkoYXJndW1lbnRzLmxlbmd0aCAtIDEpO1xuICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID4gMSkge1xuICAgICAgICBmb3IgKHZhciBpID0gMTsgaSA8IGFyZ3VtZW50cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgYXJnc1tpIC0gMV0gPSBhcmd1bWVudHNbaV07XG4gICAgICAgIH1cbiAgICB9XG4gICAgcXVldWUucHVzaChuZXcgSXRlbShmdW4sIGFyZ3MpKTtcbiAgICBpZiAocXVldWUubGVuZ3RoID09PSAxICYmICFkcmFpbmluZykge1xuICAgICAgICBydW5UaW1lb3V0KGRyYWluUXVldWUpO1xuICAgIH1cbn07XG5cbi8vIHY4IGxpa2VzIHByZWRpY3RpYmxlIG9iamVjdHNcbmZ1bmN0aW9uIEl0ZW0oZnVuLCBhcnJheSkge1xuICAgIHRoaXMuZnVuID0gZnVuO1xuICAgIHRoaXMuYXJyYXkgPSBhcnJheTtcbn1cbkl0ZW0ucHJvdG90eXBlLnJ1biA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLmZ1bi5hcHBseShudWxsLCB0aGlzLmFycmF5KTtcbn07XG5wcm9jZXNzLnRpdGxlID0gJ2Jyb3dzZXInO1xucHJvY2Vzcy5icm93c2VyID0gdHJ1ZTtcbnByb2Nlc3MuZW52ID0ge307XG5wcm9jZXNzLmFyZ3YgPSBbXTtcbnByb2Nlc3MudmVyc2lvbiA9ICcnOyAvLyBlbXB0eSBzdHJpbmcgdG8gYXZvaWQgcmVnZXhwIGlzc3Vlc1xucHJvY2Vzcy52ZXJzaW9ucyA9IHt9O1xuXG5mdW5jdGlvbiBub29wKCkge31cblxucHJvY2Vzcy5vbiA9IG5vb3A7XG5wcm9jZXNzLmFkZExpc3RlbmVyID0gbm9vcDtcbnByb2Nlc3Mub25jZSA9IG5vb3A7XG5wcm9jZXNzLm9mZiA9IG5vb3A7XG5wcm9jZXNzLnJlbW92ZUxpc3RlbmVyID0gbm9vcDtcbnByb2Nlc3MucmVtb3ZlQWxsTGlzdGVuZXJzID0gbm9vcDtcbnByb2Nlc3MuZW1pdCA9IG5vb3A7XG5cbnByb2Nlc3MuYmluZGluZyA9IGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdwcm9jZXNzLmJpbmRpbmcgaXMgbm90IHN1cHBvcnRlZCcpO1xufTtcblxucHJvY2Vzcy5jd2QgPSBmdW5jdGlvbiAoKSB7IHJldHVybiAnLycgfTtcbnByb2Nlc3MuY2hkaXIgPSBmdW5jdGlvbiAoZGlyKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdwcm9jZXNzLmNoZGlyIGlzIG5vdCBzdXBwb3J0ZWQnKTtcbn07XG5wcm9jZXNzLnVtYXNrID0gZnVuY3Rpb24oKSB7IHJldHVybiAwOyB9O1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG57XG4gIGNvbnN0IGRvc3lieXRlcyA9IHtcbiAgICBiaW4yaGV4LCBoZXgyYmluLFxuICAgIHRvQmluYXJ5LCB0b0hleCwgXG4gICAgZnJvbUJpbmFyeSwgZnJvbUhleCxcbiAgICBwYWRcbiAgfTtcblxuICB0cnkgeyBtb2R1bGUuZXhwb3J0cyA9IGRvc3lieXRlczsgfSBjYXRjaChlKSB7IE9iamVjdC5hc3NpZ24oIHNlbGYsIHsgZG9zeWJ5dGVzIH0gKTsgfVxuXG4gIGZ1bmN0aW9uIHRvQmluYXJ5KCBieXRlcyApIHtcbiAgICByZXR1cm4gQXJyYXkuZnJvbSggYnl0ZXMgKS5yZWR1Y2UoIChicyxidikgPT4gYnMgKyBTdHJpbmcuZnJvbUNoYXJDb2RlKGJ2KSwgXCJcIiApO1xuICB9XG5cbiAgZnVuY3Rpb24gYmluMmhleCggYmluc3RyICkge1xuICAgIHJldHVybiB0b0hleCggZnJvbUJpbmFyeSggYmluc3RyICkgKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGhleDJiaW4oIGhleHN0ciApIHtcbiAgICByZXR1cm4gdG9CaW5hcnkoIGZyb21IZXgoIGhleHN0ciApICk7XG4gIH1cblxuICBmdW5jdGlvbiB0b0hleCggYnl0ZXMgKSB7XG4gICAgcmV0dXJuIEFycmF5LmZyb20oIGJ5dGVzICkucmVkdWNlKCAoaHMsYnYpID0+IGhzICsgcGFkKCBidi50b1N0cmluZygxNiksIDIsICcwJywgdHJ1ZSApLCBcIlwiICk7XG4gIH1cblxuICBmdW5jdGlvbiBmcm9tQmluYXJ5KCBiaW5zdHIgKSB7XG4gICAgcmV0dXJuIG5ldyBVaW50OEFycmF5KCBBcnJheS5mcm9tKCBiaW5zdHIgKS5yZWR1Y2UoIChiYSxjKSA9PiAoYmEucHVzaChjLmNoYXJDb2RlQXQoMCkpLCBiYSksIFtdICkgKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGZyb21IZXgoIGhleHN0ciApIHtcbiAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoIEFycmF5LmZyb20oIGhleHN0ciApLnJlZHVjZSggXG4gICAgICAocGEsYyxpKSA9PiBpICUgMiA/IChwYVtwYS5sZW5ndGgtMV0rPWMsIHBhKSA6IChwYS5wdXNoKGMpLCBwYSkgLFxuICAgICAgW11cbiAgICApLnJlZHVjZSggXG4gICAgICAoYmEsaG4pID0+IChiYS5wdXNoKCBwYXJzZUludChobiwgMTYpKSwgYmEpLFxuICAgICAgW11cbiAgICApICk7XG4gIH1cblxuICBmdW5jdGlvbiBwYWQoIHN0ciwgd2lkdGgsIGNoYXIsIGxlZnQsIHJpZ2h0ICkge1xuICAgIGNvbnN0IHBhZGRpbmdfbGVuZ3RoID0gTWF0aC5tYXgoIDAsIHdpZHRoIC0gc3RyLmxlbmd0aCApO1xuICAgIGNvbnN0IHBhZGRpbmcgPSBuZXcgQXJyYXkoIHBhZGRpbmdfbGVuZ3RoICsgMSApLmpvaW4oIGNoYXIgKTtcbiAgICBpZiggbGVmdCApIHtcbiAgICAgIHN0ciA9IHBhZGRpbmcgKyBzdHJcbiAgICB9XG4gICAgaWYgKCByaWdodCApIHtcbiAgICAgIHN0ciA9IHN0ciArIHBhZGRpbmc7XG4gICAgfVxuICAgIHJldHVybiBzdHI7XG4gIH1cbn1cbiIsIlwidXNlIHN0cmljdFwiO1xue1xuICAvLyBET1NZUk5HIC0gQSBmYW1pbHkgb2YgOC1iaXQgUFJOR3MgKCBDU1BSTkdzICkgdGhhdCBhcmUgZXh0cmFvcmRpbmFyaWx5IHNpbXBsZSAmIHBhc3MgUHJhY1JhbmRcbiAgLy8gRm9yIHJlZiBvZiBQcmFjUmFuZCAtIGh0dHA6Ly9wcmFjcmFuZC5zb3VyY2Vmb3JnZS5uZXQvXG4gIC8vIFRvIGFjY2VzcyB0aGUgc3RhdGUgKCB0byBzYXksICdrZXknIHRoZSBnZW5lcmF0b3IgKSwgcGFzcyBpbiBhICdzdXJmYWNlJyBvYmplY3RcbiAgY29uc3QgZG9zeXJuZyA9IHtcbiAgICBkNDUxOiBzdXJmYWNlID0+IGl0ZXJhdG9yKCA0NSwgMSwgc3VyZmFjZSApLCAvLyBwYXNzZXMgUHJhY1JhbmRcbiAgICBkNDUzOiBzdXJmYWNlID0+IGl0ZXJhdG9yKCA0NSwgMywgc3VyZmFjZSApLCAvLyBwYXNzZXMgUHJhY1JhbmRcbiAgICBjdXN0b206IGl0ZXJhdG9yIC8vIG90aGVyIHZhbHVlcyBhcmUgdW50ZXN0ZWQuIFNldCB5b3VyIG93biFcbiAgfTtcblxuICAvLyBOb2RlLmpzIG9yIEJyb3dzZXIsIGVpdGhlciBpcyBmaW5lXG4gIHRyeSB7IG1vZHVsZS5leHBvcnRzID0gZG9zeXJuZzsgfSBjYXRjaCggZSApIHsgT2JqZWN0LmFzc2lnbiggc2VsZiwgeyBkb3N5cm5nIH0gKTsgfVxuXG4gIC8vIFRoZSBtYWluIERPU1kgcm91bmQgZnVuY3Rpb24gdmVyeSBzaW1wbGUgYW5kIGVhc3kgdG8gbWVtb3JpemVcbiAgZnVuY3Rpb24gdXBkYXRlKCBzLCBTWiwgc2hpZnQgKSB7XG4gICAgbGV0IGogPSBTWi0xO1xuICAgIGxldCBzdW0gPSAxO1xuICAgIGZvciggbGV0IGkgPSAwOyBpIDwgU1o7IGkrKyApIHtcbiAgICAgIHNbal0gXj0gKHNbaV0gPj4gc2hpZnQpIF4gKHN1bSA8PCBzaGlmdCk7XG4gICAgICBzW2ldICs9IHNbal0gKyAxO1xuICAgICAgaiA9ICggaiArIDEgKSAlIFNaO1xuICAgICAgc3VtICs9IHNbaV07XG4gICAgfVxuICAgIHJldHVybiBzdW0gJiAyNTU7XG4gIH1cblxuICAvLyBBbiBpdGVyYXRvciB3cmFwcGVyIHRvIGNyZWF0ZSB0aGUgc3RhdGUgYW5kIHR1cm4gdGhlIHJvdW5kIGZ1bmN0aW9uXG4gIGZ1bmN0aW9uIGl0ZXJhdG9yKCBzdGF0ZV9zeiA9IDQ1IC8qIGJ5dGVzICovLCBzaGlmdCA9IDEgLyogYml0cyAqLywgc3VyZmFjZSA9IHt9IC8qIC5zIGlzIHRoZSBzdGF0ZSAqLykge1xuICAgIGNvbnN0IHMgPSBuZXcgVWludDhBcnJheShzdGF0ZV9zeik7XG4gICAgY29uc3QgdXBkYXRlX3N0YXRlID0gdXBkYXRlLmJpbmQoIG51bGwsIHMsIHN0YXRlX3N6LCBzaGlmdCApO1xuICAgIGV4cG9zZSggc3VyZmFjZSwgJ3N0YXRlJywgcyApO1xuICAgIHJldHVybiB7XG4gICAgICByb3VuZCgpIHtcbiAgICAgICAgcmV0dXJuIHVwZGF0ZV9zdGF0ZSgpO1xuICAgICAgfSxcbiAgICAgIFtTeW1ib2wuaXRlcmF0b3JdKCkge1xuICAgICAgICByZXR1cm4gbWFrZV9pdGVyYWJsZSggeyBcbiAgICAgICAgICBmdW5jX3NvdXJjZTogdXBkYXRlX3N0YXRlLCBcbiAgICAgICAgICBtYXhfaXRlcmF0aW9uczogdGhpcy5sZW5ndGggXG4gICAgICAgIH0gKTtcbiAgICAgIH1cbiAgICB9O1xuICB9XG5cbiAgZnVuY3Rpb24gbWFrZV9pdGVyYWJsZSggeyBcbiAgICAgIGZ1bmNfc291cmNlIDogZnVuY19zb3VyY2UgPSAoKSA9PiAwLCBcbiAgICAgIG1heF9pdGVyYXRpb25zIDogbWF4X2l0ZXJhdGlvbnMgPSBudWxsIH0gPSB7fSBcbiAgICApIHtcbiAgICAgIGlmICggTnVtYmVyLmlzSW50ZWdlcihtYXhfaXRlcmF0aW9ucykgKSB7XG4gICAgICAgIGNvbnN0IGxlbmd0aCA9IG1heF9pdGVyYXRpb25zO1xuICAgICAgICBsZXQgaSA9IDA7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgbmV4dCgpIHtcbiAgICAgICAgICAgIGlmICggaSA8IGxlbmd0aCApIHtcbiAgICAgICAgICAgICAgaSsrO1xuICAgICAgICAgICAgICByZXR1cm4geyB2YWx1ZSA6IGZ1bmNfc291cmNlKCksIGRvbmUgOiBmYWxzZSB9O1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHsgZG9uZTogdHJ1ZSB9OyBcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIG5leHQoKSB7XG4gICAgICAgICAgICByZXR1cm4geyB2YWx1ZSA6IGZ1bmNfc291cmNlKCksIGRvbmUgOiBmYWxzZSB9O1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICB9XG5cbiAgZnVuY3Rpb24gZXhwb3NlKCBvYmosIGtleSwgdmFsICkge1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eSggb2JqLCBrZXksIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0IDogKCkgPT4gdmFsIH0gKTtcbiAgfVxufVxuIiwiXCJ1c2Ugc3RyaWN0XCI7XG57XG4gIGNsYXNzIFVURjhTdHIgZXh0ZW5kcyBTdHJpbmcge1xuICAgIGNvbnN0cnVjdG9yKCBzID0gJycgKSB7XG4gICAgICBzdXBlcihzKTtcbiAgICAgIGNvbnN0IGIgPSBieXRlcyhzKTsgXG4gICAgICBjb25zdCBibGVuID0gYi5sZW5ndGg7XG4gICAgICBjb25zdCBzeW1zID0gc3ltYm9scyhzKTtcbiAgICAgIGNvbnN0IHNsZW4gPSBzeW1zLmxlbmd0aDtcblxuICAgICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KCB0aGlzLCAnYnl0ZXMnLCB7IHZhbHVlOiBVaW50OEFycmF5LmZyb20oYiksIGVudW1lcmFibGU6IHRydWV9KTtcbiAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eSggdGhpcywgJ2J5dGVMZW5ndGgnLCB7IHZhbHVlOiBibGVuLCBlbnVtZXJhYmxlOnRydWUgfSk7XG4gICAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkoIHRoaXMsICdjaGFycycsIHsgdmFsdWU6IEFycmF5LmZyb20oc3ltcyksIGVudW1lcmFibGU6IHRydWV9KTtcbiAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eSggdGhpcywgJ2NoYXJMZW5ndGgnLCB7IHZhbHVlOiBzbGVuLCBlbnVtZXJhYmxlOiB0cnVlfSk7XG4gICAgfVxuICAgIHN0YXRpYyBmcm9tQnl0ZXMoYikge1xuICAgICAgcmV0dXJuIGZyb21ieXRlcyhiKTtcbiAgICB9XG4gICAgc3RhdGljIGZyb21VVEY4QmluYXJ5KGJzKSB7XG4gICAgICByZXR1cm4gZnJvbWJpbmFyeShicyk7XG4gICAgfVxuICB9XG5cbiAgdHJ5eyBtb2R1bGUuZXhwb3J0cyA9IFVURjhTdHI7IH0gY2F0Y2goZSkgeyBPYmplY3QuYXNzaWduKCBzZWxmLCB7IFVURjhTdHIgfSApOyB9XG5cbiAgLy90ZXN0X2Jhc2ljcygpO1xuXG4gIGZ1bmN0aW9uIHN5bWJvbHMoc3RyKSB7XG4gICAgcmV0dXJuIEFycmF5LmZyb20oc3RyKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGZyb21iaW5hcnkoYnMpIHtcbiAgICByZXR1cm4gbmV3IFVURjhTdHIoIGRlY29kZVVSSUNvbXBvbmVudChlc2NhcGUoYnMpICkgKTtcbiAgfVxuICBmdW5jdGlvbiBmcm9tYnl0ZXMoYikge1xuICAgIHJldHVybiBmcm9tYmluYXJ5KCBBcnJheS5mcm9tKGIpLm1hcCggdiA9PiBTdHJpbmcuZnJvbUNoYXJDb2RlKHYpICkuam9pbignJykpO1xuICB9XG5cbiAgZnVuY3Rpb24gc3ltYnl0ZXMoIHN5bSApIHtcbiAgICBjb25zdCB1dGY4ID0gdW5lc2NhcGUoZW5jb2RlVVJJQ29tcG9uZW50KHN5bSkpLnNwbGl0KCcnKTtcbiAgICByZXR1cm4gVWludDhBcnJheS5mcm9tKCB1dGY4Lm1hcCggYyA9PiBjLmNvZGVQb2ludEF0KDApICkgKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGJ5dGVzKHN0cikge1xuICAgIGNvbnN0IGJzID0gc3ltYm9scyhzdHIpLnJlZHVjZShcbiAgICAgIChiLHMpID0+IChiLnB1c2goLi4uc3ltYnl0ZXMocykpLCBiKSxcbiAgICAgIFtdXG4gICAgKTtcbiAgICByZXR1cm4gVWludDhBcnJheS5mcm9tKGJzKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHRlc3QodCkge1xuICAgIGNvbnNvbGUubG9nKHQpO1xuICAgIGNvbnNvbGUubG9nKCBuZXcgVVRGOFN0cih0KSk7XG4gICAgY29uc29sZS5sb2coQXJyYXkuZnJvbShieXRlcyh0KSkubWFwKCBiID0+IGIudG9TdHJpbmcoMTYpICkpO1xuICAgIGNvbnN0IHggPSBuZXcgVVRGOFN0cih0KTtcbiAgICBjb25zb2xlLmxvZyhcImZieXRlc1wiLCBVVEY4U3RyLmZyb21CeXRlcyh4LmJ5dGVzKSk7XG4gIH1cblxuICBmdW5jdGlvbiB0ZXN0X2Jhc2ljcygpIHtcbiAgICB0ZXN0KFwi8J+YgvCfmIxcIik7XG4gICAgdGVzdChcIkZvbyDCqSBiYXIg8J2MhiBiYXog4piDIHF1eFwiKTs7XG4gIH1cbn1cbiIsIlwidXNlIHN0cmljdFwiO1xue1xuICBjb25zdCBVVEY4U3RyID0gcmVxdWlyZSgndXRmOHN0cicpO1xuICBjb25zdCBkb3N5cm5nID0gcmVxdWlyZSgnZG9zeXJuZycpO1xuICBjb25zdCBieXRlcyA9IHJlcXVpcmUoJ2Rvc3lieXRlcycpO1xuICBjb25zdCBkb3N5Y3J5cHQgPSB7XG4gICAgcm5nMTogc3VyZmFjZSA9PiBkb3N5cm5nLmQ0NTEoIHN1cmZhY2UgKSxcbiAgICBybmcyOiBzdXJmYWNlID0+IGRvc3lybmcuZDQ1Myggc3VyZmFjZSApXG4gIH07XG5cbiAgLy8gY3Jvc3MgY3V0dGluZyBjb25jZXJuc1xuXG4gICAgT2JqZWN0LmFzc2lnbiggZG9zeWNyeXB0LCB7XG4gICAgICBpbnN0YW5jZSwgaW5jbHVkZVxuICAgIH0pO1xuXG5cbiAgICBmdW5jdGlvbiBpbnN0YW5jZSggYWxnbyApIHtcbiAgICAgIGNvbnN0IGluc3QgPSB7XG4gICAgICAgIHJvdW5kOiAoKSA9PiBzb3VyY2Uucm91bmQoKVxuICAgICAgfTtcbiAgICAgIGNvbnN0IHNvdXJjZSA9IGFsZ28oIGluc3QgKTtcbiAgICAgIHNvdXJjZS5yb3VuZCgpO1xuICAgICAgcmV0dXJuIGluc3Q7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gaW5jbHVkZSggdmFscywgaW5zdGFuY2UgKSB7XG4gICAgICB2YWxzLmZvckVhY2goICh2YWwsIGluZGV4KSA9PiB7XG4gICAgICAgIGluc3RhbmNlLnN0YXRlW2luZGV4XSBePSB2YWw7XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBzdHJpbmdpZnkoIHZhbHMgKSB7XG4gICAgICByZXR1cm4gVVRGOFN0ci5mcm9tQnl0ZXMoIHZhbHMgKTtcbiAgICB9XG5cbiAgLy8gaGFzaCBjb25zdHJ1Y3Rpb24gYWxnb3JpdGhtc1xuXG4gIHtcbiAgICBjb25zdCBUVVJOUyA9IDE2O1xuICAgIGNvbnN0IEJMT0NLX1NaID0gNTtcblxuICAgIE9iamVjdC5hc3NpZ24oIGRvc3ljcnlwdCwge1xuICAgICAgaGFzaCwgdGVzdF9oYXNoXG4gICAgfSk7XG5cbiAgICBmdW5jdGlvbiBoYXNoKCBtZXNzYWdlLCBkaWdlc3Rfc3osIGFsZ28gPSBkb3N5Y3J5cHQucm5nMiApIHtcbiAgICAgIGNvbnN0IGhhc2hlciA9IGluc3RhbmNlKCBhbGdvICk7XG4gICAgICBhYnNvcmIoIG1lc3NhZ2UsIGhhc2hlciApOyBcbiAgICAgIGNvbnN0IGRpZ2VzdCA9IHNxdWVlemUoIG1lc3NhZ2UsIGRpZ2VzdF9zeiwgaGFzaGVyICk7XG4gICAgICByZXR1cm4gYnl0ZXMudG9IZXgoIGRpZ2VzdCApO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGFic29yYiggbWVzc2FnZSwgaGFzaGVyICkge1xuICAgICAgY29uc3QgbSA9IG5ldyBVVEY4U3RyKCBtZXNzYWdlICkuYnl0ZXM7XG4gICAgICBsZXQgaSA9IDA7XG4gICAgICB3aGlsZSggaSA8IG0ubGVuZ3RoICkge1xuICAgICAgICBjb25zdCBjaHVuayA9IG0uc3ViYXJyYXkoaSwgQkxPQ0tfU1opOyBcbiAgICAgICAgaW5jbHVkZSggY2h1bmssIGhhc2hlciApO1xuICAgICAgICBsZXQgdHVybnMgPSBUVVJOUztcbiAgICAgICAgd2hpbGUoIHR1cm5zLS0gKSB7XG4gICAgICAgICAgaGFzaGVyLnJvdW5kKCk7XG4gICAgICAgIH1cbiAgICAgICAgaSArPSBCTE9DS19TWjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiBzcXVlZXplKCBtZXNzYWdlLCBkaWdlc3Rfc3osIGhhc2hlciApIHtcbiAgICAgIGNvbnN0IGRpZ2VzdCA9IFtdO1xuICAgICAgd2hpbGUoIGRpZ2VzdF9zeiApIHtcbiAgICAgICAgY29uc3QgbmV4dF9zbGljZSA9IE1hdGgubWluKCBCTE9DS19TWiwgZGlnZXN0X3N6ICk7XG4gICAgICAgIGRpZ2VzdC5wdXNoKCAuLi5oYXNoZXIuc3RhdGUuc2xpY2UoMCwgbmV4dF9zbGljZSkgKTtcbiAgICAgICAgZGlnZXN0X3N6IC09IG5leHRfc2xpY2U7XG4gICAgICAgIGxldCB0dXJucyA9IFRVUk5TO1xuICAgICAgICB3aGlsZSggdHVybnMtLSApIHtcbiAgICAgICAgICBoYXNoZXIucm91bmQoKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIGRpZ2VzdDtcbiAgICB9XG5cblxuICAgIGZ1bmN0aW9uIHRlc3RfaGFzaCgpIHtcbiAgICAgIGNvbnN0IG1lc3NhZ2UgPSBcIlRISVMgSVMgQSBURVNUIVwiXG4gICAgICBjb25zdCBkaWdlc3QgPSBoYXNoKCBtZXNzYWdlLCAzMiApO1xuICAgICAgLy9jb25zb2xlLmxvZyggXCJNZXNzYWdlXCIsIG1lc3NhZ2UsIFwiaGFzaFwiLCBkaWdlc3QgKTtcbiAgICB9XG4gIH1cblxuICAvLyBjaXBoZXIgY29uc3RydWN0aW9uIGFsZ29yaXRobXNcblxuICB7XG4gICAgY29uc3QgS0VZX1NDSEVEVUxFX1JPVU5EUyA9IDMyO1xuXG4gICAgT2JqZWN0LmFzc2lnbiggZG9zeWNyeXB0LCB7XG4gICAgICBlbmNyeXB0LCBkZWNyeXB0LCBzY2hlZHVsZSwgdGVzdF9jaXBoZXJcbiAgICB9KTtcblxuICAgIGZ1bmN0aW9uIHNjaGVkdWxlKCBrZXksIGluc3QgKSB7XG4gICAgICBjb25zdCBrZXlfdmFscyA9IG5ldyBVVEY4U3RyKCBrZXkgKS5ieXRlcztcbiAgICAgIGluY2x1ZGUoIGtleV92YWxzLCBpbnN0ICk7XG4gICAgICBsZXQgdHVybnMgPSBLRVlfU0NIRURVTEVfUk9VTkRTO1xuICAgICAgd2hpbGUoIHR1cm5zLS0gKSB7XG4gICAgICAgIGluc3Qucm91bmQoKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiBlbmNyeXB0KCBrZXksIHBsYWluLCBhbGdvID0gZG9zeWNyeXB0LnJuZzEsIGV4aXN0aW5nX2luc3RhbmNlICkge1xuICAgICAgcGxhaW4gPSBuZXcgVVRGOFN0ciggcGxhaW4gKS5ieXRlcztcbiAgICAgIGNvbnN0IGNpcGhlciA9IFtdO1xuXG4gICAgICBjb25zdCBpbnN0ID0gZXhpc3RpbmdfaW5zdGFuY2UgfHwgaW5zdGFuY2UoIGFsZ28gKTtcblxuICAgICAgaWYgKCAhISBrZXkgKSB7XG4gICAgICAgIHNjaGVkdWxlKCBrZXksIGluc3QgKTtcbiAgICAgIH0gXG5cbiAgICAgIHBsYWluLmZvckVhY2goIHZhbCA9PiB7XG4gICAgICAgIGNpcGhlci5wdXNoKCB2YWwgXiBpbnN0LnJvdW5kKCkgKTtcbiAgICAgIH0pO1xuXG4gICAgICByZXR1cm4gY2lwaGVyO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGRlY3J5cHQoIGtleSwgY2lwaGVyLCBhbGdvID0gZG9zeWNyeXB0LnJuZzEsIGV4aXN0aW5nX2luc3RhbmNlICkge1xuICAgICAgY2lwaGVyID0gYnl0ZXMuZnJvbUJpbmFyeSggY2lwaGVyICk7XG4gICAgICBjb25zdCBwbGFpbiA9IFtdO1xuXG4gICAgICBjb25zdCBpbnN0ID0gZXhpc3RpbmdfaW5zdGFuY2UgfHwgaW5zdGFuY2UoIGFsZ28gKTtcblxuICAgICAgaWYgKCAhISBrZXkgKSB7XG4gICAgICAgIHNjaGVkdWxlKCBrZXksIGluc3QgKTtcbiAgICAgIH1cblxuICAgICAgY2lwaGVyLmZvckVhY2goIHZhbCA9PiB7XG4gICAgICAgIHBsYWluLnB1c2goIHZhbCBeIGluc3Qucm91bmQoKSApO1xuICAgICAgfSk7XG4gICAgICBcbiAgICAgIHJldHVybiBwbGFpbjtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiB0ZXN0X2NpcGhlcigpIHtcbiAgICAgIGNvbnN0IG1lc3NhZ2UgPSBcIlRISVMgSVMgQSBTRUNSRVQhXCJcbiAgICAgIGNvbnN0IGtleSA9IFwidGhpc2lzYWtleVwiO1xuICAgICAgY29uc3QgY2lwaGVyID0gZW5jcnlwdCgga2V5LCBtZXNzYWdlICk7XG4gICAgICBjb25zdCBjaXBoZXJfc3RyaW5nID0gYnl0ZXMudG9CaW5hcnkoIGNpcGhlciApO1xuICAgICAgY29uc3QgcGxhaW4gPSBkZWNyeXB0KCBrZXksIGNpcGhlcl9zdHJpbmcgKTtcbiAgICAgIC8vY29uc29sZS5sb2coIFwiTWVzc2FnZVwiLCBtZXNzYWdlLCBcImtleVwiLCBrZXkgKTtcbiAgICAgIC8vY29uc29sZS5sb2coIFwiY2lwaGVyXCIsIGJ5dGVzLnRvSGV4KCBjaXBoZXIgKSApO1xuICAgICAgLy9jb25zb2xlLmxvZyggXCJwbGFpblwiLCBzdHJpbmdpZnkoIHBsYWluICkgKTtcbiAgICB9XG4gIH1cblxuICAvLyBlbnRyb3B5IGNvbGxlY3Rpb24gYWxnb3JpdGhtc1xuXG4gIHtcbiAgICBjb25zdCBSVU4gPSAzMTtcbiAgICBsZXQgY291bnQgPSAwLCByZXN1bHQgPSAwO1xuICAgIGxldCBicm93c2VyID0gZmFsc2U7XG5cbiAgICBPYmplY3QuYXNzaWduKCBkb3N5Y3J5cHQsIHtcbiAgICAgIGNvbGxlY3RfZW50cm9weV9ieXRlcywgdGVzdF9lbnRyb3B5XG4gICAgfSk7XG5cblxuICAgIHRyeSB7XG4gICAgICBpZiAoIHNlbGYgKSB7XG4gICAgICAgIGJyb3dzZXIgPSB0cnVlOyBcbiAgICAgIH1cbiAgICB9IGNhdGNoKCBlICkge1xuICAgICAgYnJvd3NlciA9IGZhbHNlOyBcbiAgICB9XG5cbiAgICAvLyB0aW1lc3RhbXAgd2l0aCBoaWdoIHJlc1xuICAgIGZ1bmN0aW9uIHRzKCkge1xuICAgICAgaWYgKCAhIGJyb3dzZXIgKSB7XG4gICAgICAgIHJldHVybiBwcm9jZXNzLmhydGltZSgpWzFdO1xuICAgICAgfSBlbHNlIGlmICggISEgc2VsZi5wZXJmb3JtYW5jZSApIHtcbiAgICAgICAgcmV0dXJuIE1hdGgucm91bmQoIHNlbGYucGVyZm9ybWFuY2Uubm93KCkgKiAxMCApOyBcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnNvbGUud2FybiggXCJEYXRlIG9ubHkgLSBsb3cgcmVzIGVudHJvcHkgd2FybmluZyFcIiApO1xuICAgICAgICByZXR1cm4gRGF0ZS5ub3coKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiBmbG9hdCgpIHtcbiAgICAgIGNvdW50ICs9IE1hdGguUEkvMTgwOyBcbiAgICAgIHJlc3VsdCArPSBNYXRoLnNpbiggY291bnQgKSAqIE1hdGguY29zKCBjb3VudCApO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIHRpbWVfZmxvYXRfcnVuKCkge1xuICAgICAgbGV0IHJ1biA9IFJVTjtcbiAgICAgIGNvbnN0IHN0YXJ0ID0gdHMoKTtcbiAgICAgIHdoaWxlKCBydW4tLSApIHtcbiAgICAgICAgZmxvYXQoKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGVuZCA9IHRzKCk7XG4gICAgICByZXR1cm4gZW5kIC0gc3RhcnQ7XG4gICAgfVxuICAgIFxuICAgIGZ1bmN0aW9uIGNvbGxlY3RfYml0cyggbiApIHtcbiAgICAgIGNvbnN0IGJpdHMgPSBbXTtcbiAgICAgIHdoaWxlKCBuLS0gKSB7XG4gICAgICAgIGJpdHMucHVzaCggdGltZV9mbG9hdF9ydW4oKSAlIDIgKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBiaXRzO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGJpdHNfdG9fYnl0ZXMoIGJpdHMgKSB7XG4gICAgICBjb25zdCBieXRlcyA9IFtdO1xuICAgICAgd2hpbGUoIGJpdHMubGVuZ3RoICkge1xuICAgICAgICBjb25zdCB2YWwgPSBwYXJzZUludCggYml0cy5zcGxpY2UoMCw4KS5qb2luKCcnKSwgMiApO1xuICAgICAgICBieXRlcy5wdXNoKCBTdHJpbmcuZnJvbUNoYXJDb2RlKCB2YWwgKSApO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGJ5dGVzO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGNvbGxlY3RfZW50cm9weV9ieXRlcyggbiA9IDMyICkge1xuICAgICAgY29uc3QgYml0cyA9IGNvbGxlY3RfYml0cyggbiAqIDggKTtcbiAgICAgIGNvbnN0IGJpbmFyeSA9IGJpdHNfdG9fYnl0ZXMoIGJpdHMgKTtcbiAgICAgIHJldHVybiBiaW5hcnkuam9pbignJyk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gdGVzdF9lbnRyb3B5KCkge1xuICAgICAgbGV0IHJ1biA9IDEwMDtcbiAgICAgIHdoaWxlKCBydW4tLSkge1xuICAgICAgICAvL2NvbnNvbGUubG9nKCBcIkZsb2F0IHJ1biB0aW1lXCIsIHRpbWVfZmxvYXRfcnVuKCkgKTtcbiAgICAgIH1cbiAgICAgIC8vY29uc29sZS5sb2coIFwiMzIgYnl0ZXMgb2YgZW50cm9weVwiLCBieXRlcy5iaW4yaGV4KCBjb2xsZWN0X2VudHJvcHlfYnl0ZXMoKSApKTtcbiAgICAgIC8vY29uc29sZS5sb2coIFwiMzIgYnl0ZXMgb2YgZW50cm9weVwiLCBieXRlcy5iaW4yaGV4KCBjb2xsZWN0X2VudHJvcHlfYnl0ZXMoKSApKTtcbiAgICAgIC8vY29uc29sZS5sb2coIFwiMzIgYnl0ZXMgb2YgZW50cm9weVwiLCBieXRlcy5iaW4yaGV4KCBjb2xsZWN0X2VudHJvcHlfYnl0ZXMoKSApKTtcbiAgICAgIC8vY29uc29sZS5sb2coIFwiMzIgYnl0ZXMgb2YgZW50cm9weVwiLCBieXRlcy5iaW4yaGV4KCBjb2xsZWN0X2VudHJvcHlfYnl0ZXMoKSApKTtcbiAgICAgIC8vY29uc29sZS5sb2coIFwiMzIgYnl0ZXMgb2YgZW50cm9weVwiLCBieXRlcy5iaW4yaGV4KCBjb2xsZWN0X2VudHJvcHlfYnl0ZXMoKSApKTtcbiAgICB9XG4gIH1cblxuICAvLyBpbml0aWFsaXphdGlvbiB2ZWN0b3IgYWxnb3JpdGhtc1xuXG4gIHtcbiAgICBjb25zdCBJVl9FTlRST1BZX0JZVEVTID0gODtcbiAgICBjb25zdCBJVl9CWVRFUyA9IDE2O1xuXG4gICAgT2JqZWN0LmFzc2lnbiggZG9zeWNyeXB0LCB7IFxuICAgICAgZ2VuZXJhdGVfaXYsIHRlc3RfaXZcbiAgICB9ICk7XG5cbiAgICBmdW5jdGlvbiBnZW5lcmF0ZV9pdiggZW50cm9weV9zeiA9IElWX0VOVFJPUFlfQllURVMsIGl2X3N6ID0gSVZfQllURVMgKSB7XG4gICAgICBjb25zdCBieXRlcyA9IGRvc3ljcnlwdC5jb2xsZWN0X2VudHJvcHlfYnl0ZXMoIGVudHJvcHlfc3ogKTtcbiAgICAgIGNvbnN0IGRpZ2VzdCA9IGRvc3ljcnlwdC5oYXNoKCBieXRlcywgaXZfc3ogKTtcbiAgICAgIHJldHVybiBkaWdlc3Q7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gdGVzdF9pdigpIHtcbiAgICAgIC8vY29uc29sZS5sb2coIFwiSVZcIiwgZ2VuZXJhdGVfaXYoKSApO1xuICAgIH1cbiAgfVxuXG4gIC8vIGZ1bGwgZW5jcnlwdGlvbiBhbmQgaW50ZWdyaXR5IGFsZ29yaXRobVxuXG4gICAgLyoqXG4gICAgICBGb3JtYXRcblxuICAgICAgRW5jcnlwdGlvbjogXG5cbiAgICAgICAgRShLLElWKTpFKEsrSVYsZGF0YTpIKElWOmRhdGEpKVxuXG4gICAgICAgRGVjcnlwdGlvbjpcbiAgICAgICAgIFNjaGVkdWxlIEsuXG4gICAgICAgICBEZWNyeXB0IERPU1lcbiAgICAgICAgIERlY3J5cHQgSVYgdXAgdW50aWwgXCI6XCIgY2hhcmFjdGVyLlxuICAgICAgICAgU2NoZWR1bGUgSVYuXG4gICAgICAgICBEZWNyeXB0IHVudGlsIGVuZC5cbiAgICAgICAgIFNwbGl0IGRhdGEgZnJvbSBoYXNoLlxuICAgICAgICAgQXBwZW5kIGRhdGEgdG8gSVYgd2l0aCBcIjpcIiBjaGFyYWN0ZXIuXG4gICAgICAgICBDb21wdXRlIGhhc2ggYW5kIGNoZWNrIGlmIG1hdGNoZXMuIFxuICAgICAgICAgSWYgbWF0Y2hlcyByZXR1cm4gZGF0YS4gSWYgZG9lcyBub3QgbWF0Y2hcbiAgICAgICAgIHJldHVybiBpbnRlZ2l0eSBlcnJvciwgKCBrZXkgaW5jb3JyZWN0IG9yIGRhdGEgaXMgY29ycnVwdGVkICkuXG4gICAgKiovXG5cbiAge1xuICAgIGNvbnN0IElWX0VOVFJPUFkgPSAxNTtcbiAgICBjb25zdCBJVl9TWiA9IDE1O1xuICAgIGNvbnN0IEhBU0hfU1ogPSAzMjtcblxuICAgIE9iamVjdC5hc3NpZ24oIGRvc3ljcnlwdCwge1xuICAgICAgZnVsbF9lbmNyeXB0LCBmdWxsX2RlY3J5cHQsIHRlc3RfZnVsbF9jaXBoZXIsIHRlc3RfZnVsbF9jaXBoZXIyXG4gICAgfSk7XG5cbiAgICBmdW5jdGlvbiBmdWxsX2VuY3J5cHQoIGRhdGEsIGtleSApIHtcbiAgICAgIGNvbnN0IGluc3QgPSBpbnN0YW5jZSggZG9zeWNyeXB0LnJuZzEgKTtcbiAgICAgIGNvbnN0IGl2ID0gZG9zeWNyeXB0LmdlbmVyYXRlX2l2KCBJVl9FTlRST1BZLCBJVl9TWiApO1xuICAgICAgLy9jb25zb2xlLmxvZyhcIklWXCIsIGl2KTtcbiAgICAgIC8vIHNjaGVkdWxlIGtleSBhbmQgZW5jcnlwdCBpdlxuICAgICAgY29uc3QgZV9pdiA9IGJ5dGVzLnRvQmluYXJ5KCBkb3N5Y3J5cHQuZW5jcnlwdCgga2V5LCBpdiArIFwiOlwiLCBudWxsLCBpbnN0ICkgKTtcbiAgICAgIC8vIHNjaGVkdWxlIGl2XG4gICAgICBkb3N5Y3J5cHQuc2NoZWR1bGUoIGl2LCBpbnN0ICk7XG4gICAgICAvLyBmb3JtIGl2OmRhdGEgdG8gaGFzaCBpdFxuICAgICAgY29uc3QgaGFzaGFibGUgPSBpdiArIFwiOlwiICsgZGF0YTtcbiAgICAgIC8vY29uc29sZS5sb2coIFwiSGFzaGFibGVcIiwgaGFzaGFibGUgKTtcbiAgICAgIC8vIGNvbXB1dGUgaXRzIGhhc2ggXG4gICAgICBjb25zdCBoYXNoID0gZG9zeWNyeXB0Lmhhc2goIGhhc2hhYmxlLCBIQVNIX1NaICk7XG4gICAgICAvLyBmb3JtIGRhdGE6aGFzaFxuICAgICAgY29uc3QgcGxhaW4gPSBkYXRhICsgXCI6XCIgKyBoYXNoO1xuICAgICAgLy8gZW5jcnlwdCBpdFxuICAgICAgY29uc3QgZV9wbGFpbiA9IGJ5dGVzLnRvQmluYXJ5KCBkb3N5Y3J5cHQuZW5jcnlwdCggbnVsbCwgcGxhaW4sIG51bGwsIGluc3QgKSApO1xuICAgICAgLy8gY29tYmluZVxuICAgICAgY29uc3QgY2lwaGVyID0gZV9pdiArIGVfcGxhaW47XG4gICAgICByZXR1cm4gY2lwaGVyO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGZ1bGxfZGVjcnlwdCggY2lwaGVyLCBrZXkgKSB7XG4gICAgICBjb25zdCBpbnN0ID0gaW5zdGFuY2UoIGRvc3ljcnlwdC5ybmcxICk7XG4gICAgICBkb3N5Y3J5cHQuc2NoZWR1bGUoIGtleSwgaW5zdCApO1xuICAgICAgY29uc3QgaXYgPSBbXTtcbiAgICAgIGNvbnN0IHBsYWluID0gW107XG4gICAgICBsZXQgaXZfc3RyO1xuICAgICAgbGV0IGl2X21vZGUgPSB0cnVlO1xuICAgICAgYnl0ZXMuZnJvbUJpbmFyeSggY2lwaGVyICkuZm9yRWFjaCggdmFsID0+IHtcbiAgICAgICAgY29uc3QgcCA9IHZhbCBeIGluc3Qucm91bmQoKTtcbiAgICAgICAgaWYgKCBpdl9tb2RlICkge1xuICAgICAgICAgIGlmICggcCA9PSBcIjpcIi5jaGFyQ29kZUF0KDApICkge1xuICAgICAgICAgICAgaXZfc3RyID0gYnl0ZXMudG9CaW5hcnkoIGl2ICk7XG4gICAgICAgICAgICBkb3N5Y3J5cHQuc2NoZWR1bGUoIGl2X3N0ciwgaW5zdCApO1xuICAgICAgICAgICAgaXZfbW9kZSA9IGZhbHNlO1xuICAgICAgICAgICAgcmV0dXJuOyAvLyBkaXNjYXJkIFwiOlwiXG4gICAgICAgICAgfVxuICAgICAgICAgIGl2LnB1c2goIHAgKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBwbGFpbi5wdXNoKCBwICk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgbGV0IHBsYWluX3N0cjtcbiAgICAgIC8vY29uc29sZS5sb2coIHBsYWluICk7XG4gICAgICB0cnkge1xuICAgICAgICBwbGFpbl9zdHIgPSBzdHJpbmdpZnkoIHBsYWluICk7XG4gICAgICB9IGNhdGNoKGUpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvciggXCJDYW5ub3QgZGVjcnlwdC5cIiApO1xuICAgICAgfVxuICAgICAgY29uc3QgaGFzaF9zZXAgPSBwbGFpbl9zdHIubGFzdEluZGV4T2YoIFwiOlwiICk7XG4gICAgICBpZiAoIGhhc2hfc2VwID09IC0xICkge1xuICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCBcIkNhbm5vdCBkZWNyeXB0LlwiICk7XG4gICAgICB9XG4gICAgICBjb25zdCBoYXNoID0gcGxhaW5fc3RyLnNsaWNlKCBoYXNoX3NlcCArIDEgKTtcbiAgICAgIGNvbnN0IGRhdGEgPSBwbGFpbl9zdHIuc2xpY2UoIDAsIGhhc2hfc2VwICk7XG4gICAgICBjb25zdCBoYXNoYWJsZSA9IGl2X3N0ciArIFwiOlwiICsgZGF0YTtcbiAgICAgIGNvbnN0IGNvbXB1dGVkX2hhc2ggPSBkb3N5Y3J5cHQuaGFzaCggaGFzaGFibGUsIEhBU0hfU1ogKTtcbiAgICAgIGlmICggaGFzaCA9PSBjb21wdXRlZF9oYXNoICkge1xuICAgICAgICAvL2NvbnNvbGUubG9nKCBcIklWXCIsIGl2X3N0ciwgXCJwbGFpblwiLCBwbGFpbl9zdHIgKTtcbiAgICAgICAgLy9jb25zb2xlLmxvZyggXCJIYXNoYWJsZVwiLCBoYXNoYWJsZSApO1xuICAgICAgICAvL2NvbnNvbGUubG9nKCBcImNvbXB1dGVkX2hhc2hcIiwgY29tcHV0ZWRfaGFzaCApO1xuICAgICAgICAvL2NvbnNvbGUubG9nKCBcIkNvbXB1dGVkIGhhc2ggZXF1YWxzLiBEYXRhIGlzIHZhbGlkLlwiICk7XG4gICAgICAgIGNvbnN0IGRhdGEgPSBwbGFpbl9zdHIuc2xpY2UoMCwgaGFzaF9zZXAgKTtcbiAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCBcIkNhbm5vdCBkZWNyeXB0LlwiICk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gdGVzdF9mdWxsX2NpcGhlcigpIHtcbiAgICAgIGNvbnN0IHBsYWluID0gXCJUSElTIElTIFNPTUUgUkVBTCBEQVRBIFdPT1wiO1xuICAgICAgY29uc3Qga2V5ID0gXCJ0aGlzaXNhc2VjcmV0a2V5XCI7XG4gICAgICAvL2NvbnNvbGUubG9nKCBcIlBsYWluXCIsIHBsYWluLCBcImtleVwiLCBrZXkgKTtcbiAgICAgIGNvbnN0IGNpcGhlciA9IGZ1bGxfZW5jcnlwdCggcGxhaW4sIGtleSApO1xuICAgICAgLy9jb25zb2xlLmxvZyggXCJDaXBoZXJcIiwgYnl0ZXMuYmluMmhleCggY2lwaGVyICkgKTtcbiAgICAgIGNvbnN0IGRlY3J5cHRlZCA9IGZ1bGxfZGVjcnlwdCggY2lwaGVyLCBrZXkgKTtcbiAgICAgIC8vY29uc29sZS5sb2coIFwiRGVjcnlwdGVkXCIsIGRlY3J5cHRlZCApO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIHRlc3RfZnVsbF9jaXBoZXIyKCkge1xuICAgICAgY29uc3QgcGxhaW4gPSBcIkZvbyDCqSBiYXIg8J2MhiBiYXog4piDIHF1eFwiO1xuICAgICAgY29uc3Qga2V5ID0gXCJ0aGlzaXNhc2VjcmV0a2V5XCI7XG4gICAgICAvL2NvbnNvbGUubG9nKCBcIlBsYWluXCIsIHBsYWluLCBcImtleVwiLCBrZXkgKTtcbiAgICAgIGNvbnN0IGNpcGhlciA9IGZ1bGxfZW5jcnlwdCggcGxhaW4sIGtleSApO1xuICAgICAgLy9jb25zb2xlLmxvZyggXCJDaXBoZXJcIiwgYnl0ZXMuYmluMmhleCggY2lwaGVyICkgKTtcbiAgICAgIGNvbnN0IGRlY3J5cHRlZCA9IGZ1bGxfZGVjcnlwdCggY2lwaGVyLCBrZXkgKTtcbiAgICAgIC8vY29uc29sZS5sb2coIFwiRGVjcnlwdGVkXCIsIGRlY3J5cHRlZCApO1xuICAgIH1cbiAgfVxuXG4gIC8vIHRlc3RzXG5cbiAge1xuICAgIE9iamVjdC5hc3NpZ24oIGRvc3ljcnlwdCwge1xuICAgICAgdGVzdF9hbGwgXG4gICAgfSk7XG5cbiAgICBmdW5jdGlvbiB0ZXN0X2FsbCgpIHtcbiAgICAgIGRvc3ljcnlwdC50ZXN0X2hhc2goKTtcbiAgICAgIGRvc3ljcnlwdC50ZXN0X2NpcGhlcigpO1xuICAgICAgZG9zeWNyeXB0LnRlc3RfaXYoKTtcbiAgICAgIGRvc3ljcnlwdC50ZXN0X2VudHJvcHkoKTtcbiAgICAgIGRvc3ljcnlwdC50ZXN0X2Z1bGxfY2lwaGVyKCk7XG4gICAgICBkb3N5Y3J5cHQudGVzdF9mdWxsX2NpcGhlcjIoKTtcbiAgICB9XG4gIH1cblxuICAvL2Rvc3ljcnlwdC50ZXN0X2FsbCgpO1xuXG4gIHRyeSB7IFxuICAgIG1vZHVsZS5leHBvcnRzID0gZG9zeWNyeXB0O1xuICB9IGNhdGNoKGUpIHtcbiAgICBPYmplY3QuYXNzaWduKCBzZWxmLCB7IGRvc3ljcnlwdCB9ICk7XG4gIH1cbn1cbiJdfQ==
const dc = require("dosycrypt");
self.dc = dc;
const page = ({message:message = ''}={}) => `
<title>DOSYCRYPT</title>
<meta name=viewport content="width=device-width, initial-scale=1">
<style>
:root, body, input, textarea, button {
background: orange; color: white;
}
form:invalid button.validonly {
display: none;
}
form {
display: table;
margin: 2rem auto;
}
</style>
<form>
<fieldset><legend>DOSYCRYPT</legend>
<p>
<input id=key type=password required
name=key placeholder="key (spaces okay)" minlength=4>
<p>
<textarea required id=message rows=8 cols=28
placeholder=message name=message>${safe(message)}</textarea>
<p>
<button class=validonly id=enc name=mode value=encrypt>Encrypt</button>
<button class=validonly id=dec name=mode value=decrypt>Decrypt</button>
<button id=res type=button>Destroy</button>
</fieldset>
</form>
<script>
enc.onclick = e => {
e.preventDefault();
const c = dc.full_encrypt( message.value, key.value );
document.body.innerHTML = '';
document.write(page({message:btoa(c)}));
};
dec.onclick = e => {
e.preventDefault();
const p = dc.full_decrypt( atob(message.value), key.value );
document.body.innerHTML = '';
document.write(page({message:p}));
};
res.onclick = e => {
key.value = '';
message.value = '';
};
</script>`;
self.page = page;
document.write(page());
function safe(s) {
return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').
replace(/>/g,'&gt;').replace(/"/g, '&quot;');
}
;}, 0)
{
"name": "dosycrypt-requirebin-sketch",
"version": "1.0.0",
"dependencies": {
"dosycrypt": "1.0.7"
}
}
<!-- contents of this file will be placed inside the <body> -->
<!-- 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