diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..fc6f5deb --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# http://editorconfig.org +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore index e2481d6d..efc5fcfd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,34 @@ -/node_modules/ -/bower_components/ +# OS +Thumbs.db +ehthumbs.db +Desktop.ini .DS_Store -/samples/ -yarn.lock +._* + +# Editors +*~ +*.swp +*.tmproj +*.tmproject +*.sublime-* +.idea/ +.project/ +.settings/ +.vscode/ + +# Logs +logs +*.log +npm-debug.log* + +# Dependency directories +bower_components/ +node_modules/ + +# Yeoman meta-data +.yo-rc.json + +# Build-related directories +dist/ +docs/api/ +test/dist/ diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..ea8c75d0 --- /dev/null +++ b/.npmignore @@ -0,0 +1,3 @@ +# Intentionally left blank, so that npm does not ignore anything by default, +# but relies on the package.json "files" array to explicitly define what ends +# up in the package. diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..cb8bf6fa --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +sudo: false +dist: trusty +language: node_js +node_js: + - 'node' + - 'lts/argon' +before_script: + - export CHROME_BIN=/usr/bin/google-chrome + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start +addons: + firefox: latest + apt: + sources: + - google-chrome + packages: + - google-chrome-stable diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..e69de29b diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..a184176c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,30 @@ +# CONTRIBUTING + +We welcome contributions from everyone! + +## Getting Started + +Make sure you have NodeJS 0.10 or higher and npm installed. + +1. Fork this repository and clone your fork +1. Install dependencies: `npm install` +1. Run a development server: `npm start` + +### Making Changes + +Refer to the [video.js plugin conventions][conventions] for more detail on best practices and tooling for video.js plugin authorship. + +When you've made your changes, push your commit(s) to your fork and issue a pull request against the original repository. + +### Running Tests + +Testing is a crucial part of any software project. For all but the most trivial changes (typos, etc) test cases are expected. Tests are run in actual browsers using [Karma][karma]. + +- In all available and supported browsers: `npm test` +- In a specific browser: `npm run test:chrome`, `npm run test:firefox`, etc. +- While development server is running (`npm start`), navigate to [`http://localhost:9999/test/`][local] + + +[karma]: http://karma-runner.github.io/ +[local]: http://localhost:9999/test/ +[conventions]: https://github.com/videojs/generator-videojs-plugin/blob/master/docs/conventions.md diff --git a/LICENSE-MIT b/LICENSE similarity index 100% rename from LICENSE-MIT rename to LICENSE diff --git a/README.md b/README.md index 4d650eee..c40e0a66 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,102 @@ + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [VR](#vr) + - [Installation](#installation) + - [Usage](#usage) + - [` + + ``` -npm run serve + +### Browserify/CommonJS + +When using with Browserify, install videojs-vr via npm and `require` the plugin as you would any other module. + +```js +var videojs = require('video.js'); + +// The actual plugin function is exported by this module, but it is also +// attached to the `Player.prototype`; so, there is no need to assign it +// to a variable. +require('videojs-vr'); + +var player = videojs('my-video'); + +player.vr({projection: '360'}); ``` -## Examples -To test locally, visit localhost:3000/example-external.html or localhost:3000/example-videocloud.html +### RequireJS/AMD + +When using with RequireJS (or another AMD library), get the script in whatever way you prefer and `require` the plugin as you normally would: +```js +require(['video.js', 'videojs-vr'], function(videojs) { + var player = videojs('my-video'); + + player.vr({projection: '360'}); +}); +``` -## Setting up your own player +### Optional integration with videojs-errors +If the [videojs-errors](https://github.com/brightcove/videojs-errors) plugin is intialized before `videojs-vr`, then it will be used to display errors to users. -Include the following script imports: +### Passing a projection on a source by source basis +Set `player.mediainfo` and `player.mediainfo.projection` to a valid projection value and pass in 'AUTO' or nothing for the `projection` key when initializing this plugin. +EX: +```js +var player = videojs('my-video'); - +if (!player.mediainfo) { + player.mediainfo = {}; +} -Host all video content on a HTTP Server that supports byte range requests if you want the seek bar to work (e.g. Apache). +if (!player.mediainfo.projection) { + player.mediainfo.projection = '360'; +} -### Projections -- 360 -- 360_LR -- 360_TB -- CUBE -- AUTO - will attempt to retrieve metadata from VideoCloud Video, and if present will start plugin otherwise defaults to NONE -- NONE - plugin doesnt run and renders the video flat +player.vr({projection: 'AUTO'}); + +// or player.vr(); since 'AUTO' is the default +``` ## Oculus Rift and HTC Vive Support This plugin leverages the [webvr-boilerplate](https://github.com/borismus/webvr-boilerplate) project (which in turn uses [webvr-polyfill](https://github.com/borismus/webvr-polyfill) and [three.js](https://github.com/mrdoob/three.js)) to create a 'responsive VR' experience across multiple devices. @@ -50,7 +111,7 @@ The Three.js rotation values are exposed under the property `cameraVector` on th For example, assuming the parent element for Video.js is `#video-container` the following code would return the current `cameraVector` values: document.getElementById('video-container').player.vr.cameraVector - + See `example-camera.html` for a working demo that logs camera object and rotation to the console every second. ## Accessing THREE.js objects @@ -63,13 +124,13 @@ For example, assuming the parent element for Video.js is `#video-container` the while: document.getElementById('video-container').player.vr.threeJs.scene - + would return the THREE.js Scene, and: document.getElementById('video-container').player.vr.threeJs.renderer - + would return the THREE.js renderer. - + ## Credits ## @@ -82,14 +143,3 @@ This project is a conglomeration of a few amazing open source libraries. ## Support ## This work is sponsored by [Brightcove](https://www.brightcove.com), [HapYak](http://corp.hapyak.com/) and [StreamShark](https://streamshark.io) - - -## Release History - -* 0.3.4 -* 0.3.3 -* 0.3.2 -* 0.3.1 -* 0.3.0 -* 0.2.2 -* 0.2.0 diff --git a/dist/videocloud.vr.css b/dist/videocloud.vr.css deleted file mode 100644 index 35925e26..00000000 --- a/dist/videocloud.vr.css +++ /dev/null @@ -1,23 +0,0 @@ -.video-js .vjs-big-play-button { - background: url(http://brightcove360.s3-website-us-east-1.amazonaws.com/assets/360-video.svg); - background-size: contain; - background-color: rgba(0, 0, 0, 0.5); } - .video-js .vjs-big-play-button .vjs-icon-placeholder { - display: none; } - :hover.video-js .vjs-big-play-button { - -webkit-transition: border-color 0.4s, outline 0.4s, background-color 0.4s; - -moz-transition: border-color 0.4s, outline 0.4s, background-color 0.4s; - -ms-transition: border-color 0.4s, outline 0.4s, background-color 0.4s; - -o-transition: border-color 0.4s, outline 0.4s, background-color 0.4s; - transition: border-color 0.4s, outline 0.4s, background-color 0.4s; } - .video-js .vjs-big-play-button::before { - content: ''; } - -.video-js canvas { - cursor: move; } - -.video-js .vjs-button-vr .vjs-icon-placeholder { - height: 30px; - width: 30px; - display: inline-block; - background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI0ZGRkZGRiI+CiAgICA8cGF0aCBkPSJNMjAuNzQgNkgzLjIxQzIuNTUgNiAyIDYuNTcgMiA3LjI4djEwLjQ0YzAgLjcuNTUgMS4yOCAxLjIzIDEuMjhoNC43OWMuNTIgMCAuOTYtLjMzIDEuMTQtLjc5bDEuNC0zLjQ4Yy4yMy0uNTkuNzktMS4wMSAxLjQ0LTEuMDFzMS4yMS40MiAxLjQ1IDEuMDFsMS4zOSAzLjQ4Yy4xOS40Ni42My43OSAxLjExLjc5aDQuNzljLjcxIDAgMS4yNi0uNTcgMS4yNi0xLjI4VjcuMjhjMC0uNy0uNTUtMS4yOC0xLjI2LTEuMjh6TTcuNSAxNC42MmMtMS4xNyAwLTIuMTMtLjk1LTIuMTMtMi4xMiAwLTEuMTcuOTYtMi4xMyAyLjEzLTIuMTMgMS4xOCAwIDIuMTIuOTYgMi4xMiAyLjEzcy0uOTUgMi4xMi0yLjEyIDIuMTJ6bTkgMGMtMS4xNyAwLTIuMTMtLjk1LTIuMTMtMi4xMiAwLTEuMTcuOTYtMi4xMyAyLjEzLTIuMTNzMi4xMi45NiAyLjEyIDIuMTMtLjk1IDIuMTItMi4xMiAyLjEyeiIvPgogICAgPHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgyNHYyNEgwVjB6Ii8+Cjwvc3ZnPgo=) no-repeat left center; } diff --git a/dist/videocloud.vr.js b/dist/videocloud.vr.js deleted file mode 100644 index 4608a26e..00000000 --- a/dist/videocloud.vr.js +++ /dev/null @@ -1,51686 +0,0 @@ -(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 0 ) ? 1 : + x; - - }; - - } - - if ( Function.prototype.name === undefined ) { - - // Missing in IE9-11. - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name - - Object.defineProperty( Function.prototype, 'name', { - - get: function () { - - return this.toString().match( /^\s*function\s*([^\(\s]*)/ )[ 1 ]; - - } - - } ); - - } - - if ( Object.assign === undefined ) { - - // Missing in IE. - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign - - ( function () { - - Object.assign = function ( target ) { - - 'use strict'; - - if ( target === undefined || target === null ) { - - throw new TypeError( 'Cannot convert undefined or null to object' ); - - } - - var output = Object( target ); - - for ( var index = 1; index < arguments.length; index ++ ) { - - var source = arguments[ index ]; - - if ( source !== undefined && source !== null ) { - - for ( var nextKey in source ) { - - if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) { - - output[ nextKey ] = source[ nextKey ]; - - } - - } - - } - - } - - return output; - - }; - - } )(); - - } - - /** - * https://github.com/mrdoob/eventdispatcher.js/ - */ - - function EventDispatcher() {} - - Object.assign( EventDispatcher.prototype, { - - addEventListener: function ( type, listener ) { - - if ( this._listeners === undefined ) this._listeners = {}; - - var listeners = this._listeners; - - if ( listeners[ type ] === undefined ) { - - listeners[ type ] = []; - - } - - if ( listeners[ type ].indexOf( listener ) === - 1 ) { - - listeners[ type ].push( listener ); - - } - - }, - - hasEventListener: function ( type, listener ) { - - if ( this._listeners === undefined ) return false; - - var listeners = this._listeners; - - return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; - - }, - - removeEventListener: function ( type, listener ) { - - if ( this._listeners === undefined ) return; - - var listeners = this._listeners; - var listenerArray = listeners[ type ]; - - if ( listenerArray !== undefined ) { - - var index = listenerArray.indexOf( listener ); - - if ( index !== - 1 ) { - - listenerArray.splice( index, 1 ); - - } - - } - - }, - - dispatchEvent: function ( event ) { - - if ( this._listeners === undefined ) return; - - var listeners = this._listeners; - var listenerArray = listeners[ event.type ]; - - if ( listenerArray !== undefined ) { - - event.target = this; - - var array = [], i = 0; - var length = listenerArray.length; - - for ( i = 0; i < length; i ++ ) { - - array[ i ] = listenerArray[ i ]; - - } - - for ( i = 0; i < length; i ++ ) { - - array[ i ].call( this, event ); - - } - - } - - } - - } ); - - var REVISION = '83'; - var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 }; - var CullFaceNone = 0; - var CullFaceBack = 1; - var CullFaceFront = 2; - var CullFaceFrontBack = 3; - var FrontFaceDirectionCW = 0; - var FrontFaceDirectionCCW = 1; - var BasicShadowMap = 0; - var PCFShadowMap = 1; - var PCFSoftShadowMap = 2; - var FrontSide = 0; - var BackSide = 1; - var DoubleSide = 2; - var FlatShading = 1; - var SmoothShading = 2; - var NoColors = 0; - var FaceColors = 1; - var VertexColors = 2; - var NoBlending = 0; - var NormalBlending = 1; - var AdditiveBlending = 2; - var SubtractiveBlending = 3; - var MultiplyBlending = 4; - var CustomBlending = 5; - var BlendingMode = { - NoBlending: NoBlending, - NormalBlending: NormalBlending, - AdditiveBlending: AdditiveBlending, - SubtractiveBlending: SubtractiveBlending, - MultiplyBlending: MultiplyBlending, - CustomBlending: CustomBlending - }; - var AddEquation = 100; - var SubtractEquation = 101; - var ReverseSubtractEquation = 102; - var MinEquation = 103; - var MaxEquation = 104; - var ZeroFactor = 200; - var OneFactor = 201; - var SrcColorFactor = 202; - var OneMinusSrcColorFactor = 203; - var SrcAlphaFactor = 204; - var OneMinusSrcAlphaFactor = 205; - var DstAlphaFactor = 206; - var OneMinusDstAlphaFactor = 207; - var DstColorFactor = 208; - var OneMinusDstColorFactor = 209; - var SrcAlphaSaturateFactor = 210; - var NeverDepth = 0; - var AlwaysDepth = 1; - var LessDepth = 2; - var LessEqualDepth = 3; - var EqualDepth = 4; - var GreaterEqualDepth = 5; - var GreaterDepth = 6; - var NotEqualDepth = 7; - var MultiplyOperation = 0; - var MixOperation = 1; - var AddOperation = 2; - var NoToneMapping = 0; - var LinearToneMapping = 1; - var ReinhardToneMapping = 2; - var Uncharted2ToneMapping = 3; - var CineonToneMapping = 4; - var UVMapping = 300; - var CubeReflectionMapping = 301; - var CubeRefractionMapping = 302; - var EquirectangularReflectionMapping = 303; - var EquirectangularRefractionMapping = 304; - var SphericalReflectionMapping = 305; - var CubeUVReflectionMapping = 306; - var CubeUVRefractionMapping = 307; - var TextureMapping = { - UVMapping: UVMapping, - CubeReflectionMapping: CubeReflectionMapping, - CubeRefractionMapping: CubeRefractionMapping, - EquirectangularReflectionMapping: EquirectangularReflectionMapping, - EquirectangularRefractionMapping: EquirectangularRefractionMapping, - SphericalReflectionMapping: SphericalReflectionMapping, - CubeUVReflectionMapping: CubeUVReflectionMapping, - CubeUVRefractionMapping: CubeUVRefractionMapping - }; - var RepeatWrapping = 1000; - var ClampToEdgeWrapping = 1001; - var MirroredRepeatWrapping = 1002; - var TextureWrapping = { - RepeatWrapping: RepeatWrapping, - ClampToEdgeWrapping: ClampToEdgeWrapping, - MirroredRepeatWrapping: MirroredRepeatWrapping - }; - var NearestFilter = 1003; - var NearestMipMapNearestFilter = 1004; - var NearestMipMapLinearFilter = 1005; - var LinearFilter = 1006; - var LinearMipMapNearestFilter = 1007; - var LinearMipMapLinearFilter = 1008; - var TextureFilter = { - NearestFilter: NearestFilter, - NearestMipMapNearestFilter: NearestMipMapNearestFilter, - NearestMipMapLinearFilter: NearestMipMapLinearFilter, - LinearFilter: LinearFilter, - LinearMipMapNearestFilter: LinearMipMapNearestFilter, - LinearMipMapLinearFilter: LinearMipMapLinearFilter - }; - var UnsignedByteType = 1009; - var ByteType = 1010; - var ShortType = 1011; - var UnsignedShortType = 1012; - var IntType = 1013; - var UnsignedIntType = 1014; - var FloatType = 1015; - var HalfFloatType = 1016; - var UnsignedShort4444Type = 1017; - var UnsignedShort5551Type = 1018; - var UnsignedShort565Type = 1019; - var UnsignedInt248Type = 1020; - var AlphaFormat = 1021; - var RGBFormat = 1022; - var RGBAFormat = 1023; - var LuminanceFormat = 1024; - var LuminanceAlphaFormat = 1025; - var RGBEFormat = RGBAFormat; - var DepthFormat = 1026; - var DepthStencilFormat = 1027; - var RGB_S3TC_DXT1_Format = 2001; - var RGBA_S3TC_DXT1_Format = 2002; - var RGBA_S3TC_DXT3_Format = 2003; - var RGBA_S3TC_DXT5_Format = 2004; - var RGB_PVRTC_4BPPV1_Format = 2100; - var RGB_PVRTC_2BPPV1_Format = 2101; - var RGBA_PVRTC_4BPPV1_Format = 2102; - var RGBA_PVRTC_2BPPV1_Format = 2103; - var RGB_ETC1_Format = 2151; - var LoopOnce = 2200; - var LoopRepeat = 2201; - var LoopPingPong = 2202; - var InterpolateDiscrete = 2300; - var InterpolateLinear = 2301; - var InterpolateSmooth = 2302; - var ZeroCurvatureEnding = 2400; - var ZeroSlopeEnding = 2401; - var WrapAroundEnding = 2402; - var TrianglesDrawMode = 0; - var TriangleStripDrawMode = 1; - var TriangleFanDrawMode = 2; - var LinearEncoding = 3000; - var sRGBEncoding = 3001; - var GammaEncoding = 3007; - var RGBEEncoding = 3002; - var LogLuvEncoding = 3003; - var RGBM7Encoding = 3004; - var RGBM16Encoding = 3005; - var RGBDEncoding = 3006; - var BasicDepthPacking = 3200; - var RGBADepthPacking = 3201; - - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ - - var _Math = { - - DEG2RAD: Math.PI / 180, - RAD2DEG: 180 / Math.PI, - - generateUUID: function () { - - // http://www.broofa.com/Tools/Math.uuid.htm - - var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' ); - var uuid = new Array( 36 ); - var rnd = 0, r; - - return function generateUUID() { - - for ( var i = 0; i < 36; i ++ ) { - - if ( i === 8 || i === 13 || i === 18 || i === 23 ) { - - uuid[ i ] = '-'; - - } else if ( i === 14 ) { - - uuid[ i ] = '4'; - - } else { - - if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0; - r = rnd & 0xf; - rnd = rnd >> 4; - uuid[ i ] = chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ]; - - } - - } - - return uuid.join( '' ); - - }; - - }(), - - clamp: function ( value, min, max ) { - - return Math.max( min, Math.min( max, value ) ); - - }, - - // compute euclidian modulo of m % n - // https://en.wikipedia.org/wiki/Modulo_operation - - euclideanModulo: function ( n, m ) { - - return ( ( n % m ) + m ) % m; - - }, - - // Linear mapping from range to range - - mapLinear: function ( x, a1, a2, b1, b2 ) { - - return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); - - }, - - // https://en.wikipedia.org/wiki/Linear_interpolation - - lerp: function ( x, y, t ) { - - return ( 1 - t ) * x + t * y; - - }, - - // http://en.wikipedia.org/wiki/Smoothstep - - smoothstep: function ( x, min, max ) { - - if ( x <= min ) return 0; - if ( x >= max ) return 1; - - x = ( x - min ) / ( max - min ); - - return x * x * ( 3 - 2 * x ); - - }, - - smootherstep: function ( x, min, max ) { - - if ( x <= min ) return 0; - if ( x >= max ) return 1; - - x = ( x - min ) / ( max - min ); - - return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); - - }, - - // Random integer from interval - - randInt: function ( low, high ) { - - return low + Math.floor( Math.random() * ( high - low + 1 ) ); - - }, - - // Random float from interval - - randFloat: function ( low, high ) { - - return low + Math.random() * ( high - low ); - - }, - - // Random float from <-range/2, range/2> interval - - randFloatSpread: function ( range ) { - - return range * ( 0.5 - Math.random() ); - - }, - - degToRad: function ( degrees ) { - - return degrees * _Math.DEG2RAD; - - }, - - radToDeg: function ( radians ) { - - return radians * _Math.RAD2DEG; - - }, - - isPowerOfTwo: function ( value ) { - - return ( value & ( value - 1 ) ) === 0 && value !== 0; - - }, - - nearestPowerOfTwo: function ( value ) { - - return Math.pow( 2, Math.round( Math.log( value ) / Math.LN2 ) ); - - }, - - nextPowerOfTwo: function ( value ) { - - value --; - value |= value >> 1; - value |= value >> 2; - value |= value >> 4; - value |= value >> 8; - value |= value >> 16; - value ++; - - return value; - - } - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author philogb / http://blog.thejit.org/ - * @author egraether / http://egraether.com/ - * @author zz85 / http://www.lab4games.net/zz85/blog - */ - - function Vector2( x, y ) { - - this.x = x || 0; - this.y = y || 0; - - } - - Vector2.prototype = { - - constructor: Vector2, - - isVector2: true, - - get width() { - - return this.x; - - }, - - set width( value ) { - - this.x = value; - - }, - - get height() { - - return this.y; - - }, - - set height( value ) { - - this.y = value; - - }, - - // - - set: function ( x, y ) { - - this.x = x; - this.y = y; - - return this; - - }, - - setScalar: function ( scalar ) { - - this.x = scalar; - this.y = scalar; - - return this; - - }, - - setX: function ( x ) { - - this.x = x; - - return this; - - }, - - setY: function ( y ) { - - this.y = y; - - return this; - - }, - - setComponent: function ( index, value ) { - - switch ( index ) { - - case 0: this.x = value; break; - case 1: this.y = value; break; - default: throw new Error( 'index is out of range: ' + index ); - - } - - return this; - - }, - - getComponent: function ( index ) { - - switch ( index ) { - - case 0: return this.x; - case 1: return this.y; - default: throw new Error( 'index is out of range: ' + index ); - - } - - }, - - clone: function () { - - return new this.constructor( this.x, this.y ); - - }, - - copy: function ( v ) { - - this.x = v.x; - this.y = v.y; - - return this; - - }, - - add: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); - - } - - this.x += v.x; - this.y += v.y; - - return this; - - }, - - addScalar: function ( s ) { - - this.x += s; - this.y += s; - - return this; - - }, - - addVectors: function ( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - - return this; - - }, - - addScaledVector: function ( v, s ) { - - this.x += v.x * s; - this.y += v.y * s; - - return this; - - }, - - sub: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } - - this.x -= v.x; - this.y -= v.y; - - return this; - - }, - - subScalar: function ( s ) { - - this.x -= s; - this.y -= s; - - return this; - - }, - - subVectors: function ( a, b ) { - - this.x = a.x - b.x; - this.y = a.y - b.y; - - return this; - - }, - - multiply: function ( v ) { - - this.x *= v.x; - this.y *= v.y; - - return this; - - }, - - multiplyScalar: function ( scalar ) { - - if ( isFinite( scalar ) ) { - - this.x *= scalar; - this.y *= scalar; - - } else { - - this.x = 0; - this.y = 0; - - } - - return this; - - }, - - divide: function ( v ) { - - this.x /= v.x; - this.y /= v.y; - - return this; - - }, - - divideScalar: function ( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - }, - - min: function ( v ) { - - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - - return this; - - }, - - max: function ( v ) { - - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - - return this; - - }, - - clamp: function ( min, max ) { - - // This function assumes min < max, if this assumption isn't true it will not operate correctly - - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - - return this; - - }, - - clampScalar: function () { - - var min, max; - - return function clampScalar( minVal, maxVal ) { - - if ( min === undefined ) { - - min = new Vector2(); - max = new Vector2(); - - } - - min.set( minVal, minVal ); - max.set( maxVal, maxVal ); - - return this.clamp( min, max ); - - }; - - }(), - - clampLength: function ( min, max ) { - - var length = this.length(); - - return this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length ); - - }, - - floor: function () { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - - return this; - - }, - - ceil: function () { - - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - - return this; - - }, - - round: function () { - - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - - return this; - - }, - - roundToZero: function () { - - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - - return this; - - }, - - negate: function () { - - this.x = - this.x; - this.y = - this.y; - - return this; - - }, - - dot: function ( v ) { - - return this.x * v.x + this.y * v.y; - - }, - - lengthSq: function () { - - return this.x * this.x + this.y * this.y; - - }, - - length: function () { - - return Math.sqrt( this.x * this.x + this.y * this.y ); - - }, - - lengthManhattan: function() { - - return Math.abs( this.x ) + Math.abs( this.y ); - - }, - - normalize: function () { - - return this.divideScalar( this.length() ); - - }, - - angle: function () { - - // computes the angle in radians with respect to the positive x-axis - - var angle = Math.atan2( this.y, this.x ); - - if ( angle < 0 ) angle += 2 * Math.PI; - - return angle; - - }, - - distanceTo: function ( v ) { - - return Math.sqrt( this.distanceToSquared( v ) ); - - }, - - distanceToSquared: function ( v ) { - - var dx = this.x - v.x, dy = this.y - v.y; - return dx * dx + dy * dy; - - }, - - distanceToManhattan: function ( v ) { - - return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); - - }, - - setLength: function ( length ) { - - return this.multiplyScalar( length / this.length() ); - - }, - - lerp: function ( v, alpha ) { - - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - - return this; - - }, - - lerpVectors: function ( v1, v2, alpha ) { - - return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); - - }, - - equals: function ( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) ); - - }, - - fromArray: function ( array, offset ) { - - if ( offset === undefined ) offset = 0; - - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - - return array; - - }, - - fromAttribute: function ( attribute, index, offset ) { - - if ( offset !== undefined ) { - - console.warn( 'THREE.Vector2: offset has been removed from .fromAttribute().' ); - - } - - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - - return this; - - }, - - rotateAround: function ( center, angle ) { - - var c = Math.cos( angle ), s = Math.sin( angle ); - - var x = this.x - center.x; - var y = this.y - center.y; - - this.x = x * c - y * s + center.x; - this.y = x * s + y * c + center.y; - - return this; - - } - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author szimek / https://github.com/szimek/ - */ - - var textureId = 0; - - function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { - - Object.defineProperty( this, 'id', { value: textureId ++ } ); - - this.uuid = _Math.generateUUID(); - - this.name = ''; - - this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE; - this.mipmaps = []; - - this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING; - - this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping; - this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping; - - this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; - this.minFilter = minFilter !== undefined ? minFilter : LinearMipMapLinearFilter; - - this.anisotropy = anisotropy !== undefined ? anisotropy : 1; - - this.format = format !== undefined ? format : RGBAFormat; - this.type = type !== undefined ? type : UnsignedByteType; - - this.offset = new Vector2( 0, 0 ); - this.repeat = new Vector2( 1, 1 ); - - this.generateMipmaps = true; - this.premultiplyAlpha = false; - this.flipY = true; - this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) - - - // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. - // - // Also changing the encoding after already used by a Material will not automatically make the Material - // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. - this.encoding = encoding !== undefined ? encoding : LinearEncoding; - - this.version = 0; - this.onUpdate = null; - - } - - Texture.DEFAULT_IMAGE = undefined; - Texture.DEFAULT_MAPPING = UVMapping; - - Texture.prototype = { - - constructor: Texture, - - isTexture: true, - - set needsUpdate( value ) { - - if ( value === true ) this.version ++; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.image = source.image; - this.mipmaps = source.mipmaps.slice( 0 ); - - this.mapping = source.mapping; - - this.wrapS = source.wrapS; - this.wrapT = source.wrapT; - - this.magFilter = source.magFilter; - this.minFilter = source.minFilter; - - this.anisotropy = source.anisotropy; - - this.format = source.format; - this.type = source.type; - - this.offset.copy( source.offset ); - this.repeat.copy( source.repeat ); - - this.generateMipmaps = source.generateMipmaps; - this.premultiplyAlpha = source.premultiplyAlpha; - this.flipY = source.flipY; - this.unpackAlignment = source.unpackAlignment; - this.encoding = source.encoding; - - return this; - - }, - - toJSON: function ( meta ) { - - if ( meta.textures[ this.uuid ] !== undefined ) { - - return meta.textures[ this.uuid ]; - - } - - function getDataURL( image ) { - - var canvas; - - if ( image.toDataURL !== undefined ) { - - canvas = image; - - } else { - - canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - canvas.width = image.width; - canvas.height = image.height; - - canvas.getContext( '2d' ).drawImage( image, 0, 0, image.width, image.height ); - - } - - if ( canvas.width > 2048 || canvas.height > 2048 ) { - - return canvas.toDataURL( 'image/jpeg', 0.6 ); - - } else { - - return canvas.toDataURL( 'image/png' ); - - } - - } - - var output = { - metadata: { - version: 4.4, - type: 'Texture', - generator: 'Texture.toJSON' - }, - - uuid: this.uuid, - name: this.name, - - mapping: this.mapping, - - repeat: [ this.repeat.x, this.repeat.y ], - offset: [ this.offset.x, this.offset.y ], - wrap: [ this.wrapS, this.wrapT ], - - minFilter: this.minFilter, - magFilter: this.magFilter, - anisotropy: this.anisotropy, - - flipY: this.flipY - }; - - if ( this.image !== undefined ) { - - // TODO: Move to THREE.Image - - var image = this.image; - - if ( image.uuid === undefined ) { - - image.uuid = _Math.generateUUID(); // UGH - - } - - if ( meta.images[ image.uuid ] === undefined ) { - - meta.images[ image.uuid ] = { - uuid: image.uuid, - url: getDataURL( image ) - }; - - } - - output.image = image.uuid; - - } - - meta.textures[ this.uuid ] = output; - - return output; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - }, - - transformUv: function ( uv ) { - - if ( this.mapping !== UVMapping ) return; - - uv.multiply( this.repeat ); - uv.add( this.offset ); - - if ( uv.x < 0 || uv.x > 1 ) { - - switch ( this.wrapS ) { - - case RepeatWrapping: - - uv.x = uv.x - Math.floor( uv.x ); - break; - - case ClampToEdgeWrapping: - - uv.x = uv.x < 0 ? 0 : 1; - break; - - case MirroredRepeatWrapping: - - if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { - - uv.x = Math.ceil( uv.x ) - uv.x; - - } else { - - uv.x = uv.x - Math.floor( uv.x ); - - } - break; - - } - - } - - if ( uv.y < 0 || uv.y > 1 ) { - - switch ( this.wrapT ) { - - case RepeatWrapping: - - uv.y = uv.y - Math.floor( uv.y ); - break; - - case ClampToEdgeWrapping: - - uv.y = uv.y < 0 ? 0 : 1; - break; - - case MirroredRepeatWrapping: - - if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { - - uv.y = Math.ceil( uv.y ) - uv.y; - - } else { - - uv.y = uv.y - Math.floor( uv.y ); - - } - break; - - } - - } - - if ( this.flipY ) { - - uv.y = 1 - uv.y; - - } - - } - - }; - - Object.assign( Texture.prototype, EventDispatcher.prototype ); - - /** - * @author supereggbert / http://www.paulbrunt.co.uk/ - * @author philogb / http://blog.thejit.org/ - * @author mikael emtinger / http://gomo.se/ - * @author egraether / http://egraether.com/ - * @author WestLangley / http://github.com/WestLangley - */ - - function Vector4( x, y, z, w ) { - - this.x = x || 0; - this.y = y || 0; - this.z = z || 0; - this.w = ( w !== undefined ) ? w : 1; - - } - - Vector4.prototype = { - - constructor: Vector4, - - isVector4: true, - - set: function ( x, y, z, w ) { - - this.x = x; - this.y = y; - this.z = z; - this.w = w; - - return this; - - }, - - setScalar: function ( scalar ) { - - this.x = scalar; - this.y = scalar; - this.z = scalar; - this.w = scalar; - - return this; - - }, - - setX: function ( x ) { - - this.x = x; - - return this; - - }, - - setY: function ( y ) { - - this.y = y; - - return this; - - }, - - setZ: function ( z ) { - - this.z = z; - - return this; - - }, - - setW: function ( w ) { - - this.w = w; - - return this; - - }, - - setComponent: function ( index, value ) { - - switch ( index ) { - - case 0: this.x = value; break; - case 1: this.y = value; break; - case 2: this.z = value; break; - case 3: this.w = value; break; - default: throw new Error( 'index is out of range: ' + index ); - - } - - return this; - - }, - - getComponent: function ( index ) { - - switch ( index ) { - - case 0: return this.x; - case 1: return this.y; - case 2: return this.z; - case 3: return this.w; - default: throw new Error( 'index is out of range: ' + index ); - - } - - }, - - clone: function () { - - return new this.constructor( this.x, this.y, this.z, this.w ); - - }, - - copy: function ( v ) { - - this.x = v.x; - this.y = v.y; - this.z = v.z; - this.w = ( v.w !== undefined ) ? v.w : 1; - - return this; - - }, - - add: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); - - } - - this.x += v.x; - this.y += v.y; - this.z += v.z; - this.w += v.w; - - return this; - - }, - - addScalar: function ( s ) { - - this.x += s; - this.y += s; - this.z += s; - this.w += s; - - return this; - - }, - - addVectors: function ( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; - this.w = a.w + b.w; - - return this; - - }, - - addScaledVector: function ( v, s ) { - - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; - this.w += v.w * s; - - return this; - - }, - - sub: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } - - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; - this.w -= v.w; - - return this; - - }, - - subScalar: function ( s ) { - - this.x -= s; - this.y -= s; - this.z -= s; - this.w -= s; - - return this; - - }, - - subVectors: function ( a, b ) { - - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; - this.w = a.w - b.w; - - return this; - - }, - - multiplyScalar: function ( scalar ) { - - if ( isFinite( scalar ) ) { - - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - this.w *= scalar; - - } else { - - this.x = 0; - this.y = 0; - this.z = 0; - this.w = 0; - - } - - return this; - - }, - - applyMatrix4: function ( m ) { - - var x = this.x, y = this.y, z = this.z, w = this.w; - var e = m.elements; - - this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; - this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; - this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; - this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; - - return this; - - }, - - divideScalar: function ( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - }, - - setAxisAngleFromQuaternion: function ( q ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm - - // q is assumed to be normalized - - this.w = 2 * Math.acos( q.w ); - - var s = Math.sqrt( 1 - q.w * q.w ); - - if ( s < 0.0001 ) { - - this.x = 1; - this.y = 0; - this.z = 0; - - } else { - - this.x = q.x / s; - this.y = q.y / s; - this.z = q.z / s; - - } - - return this; - - }, - - setAxisAngleFromRotationMatrix: function ( m ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - var angle, x, y, z, // variables for result - epsilon = 0.01, // margin to allow for rounding errors - epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees - - te = m.elements, - - m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], - m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], - m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; - - if ( ( Math.abs( m12 - m21 ) < epsilon ) && - ( Math.abs( m13 - m31 ) < epsilon ) && - ( Math.abs( m23 - m32 ) < epsilon ) ) { - - // singularity found - // first check for identity matrix which must have +1 for all terms - // in leading diagonal and zero in other terms - - if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && - ( Math.abs( m13 + m31 ) < epsilon2 ) && - ( Math.abs( m23 + m32 ) < epsilon2 ) && - ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { - - // this singularity is identity matrix so angle = 0 - - this.set( 1, 0, 0, 0 ); - - return this; // zero angle, arbitrary axis - - } - - // otherwise this singularity is angle = 180 - - angle = Math.PI; - - var xx = ( m11 + 1 ) / 2; - var yy = ( m22 + 1 ) / 2; - var zz = ( m33 + 1 ) / 2; - var xy = ( m12 + m21 ) / 4; - var xz = ( m13 + m31 ) / 4; - var yz = ( m23 + m32 ) / 4; - - if ( ( xx > yy ) && ( xx > zz ) ) { - - // m11 is the largest diagonal term - - if ( xx < epsilon ) { - - x = 0; - y = 0.707106781; - z = 0.707106781; - - } else { - - x = Math.sqrt( xx ); - y = xy / x; - z = xz / x; - - } - - } else if ( yy > zz ) { - - // m22 is the largest diagonal term - - if ( yy < epsilon ) { - - x = 0.707106781; - y = 0; - z = 0.707106781; - - } else { - - y = Math.sqrt( yy ); - x = xy / y; - z = yz / y; - - } - - } else { - - // m33 is the largest diagonal term so base result on this - - if ( zz < epsilon ) { - - x = 0.707106781; - y = 0.707106781; - z = 0; - - } else { - - z = Math.sqrt( zz ); - x = xz / z; - y = yz / z; - - } - - } - - this.set( x, y, z, angle ); - - return this; // return 180 deg rotation - - } - - // as we have reached here there are no singularities so we can handle normally - - var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + - ( m13 - m31 ) * ( m13 - m31 ) + - ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize - - if ( Math.abs( s ) < 0.001 ) s = 1; - - // prevent divide by zero, should not happen if matrix is orthogonal and should be - // caught by singularity test above, but I've left it in just in case - - this.x = ( m32 - m23 ) / s; - this.y = ( m13 - m31 ) / s; - this.z = ( m21 - m12 ) / s; - this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); - - return this; - - }, - - min: function ( v ) { - - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - this.z = Math.min( this.z, v.z ); - this.w = Math.min( this.w, v.w ); - - return this; - - }, - - max: function ( v ) { - - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); - this.w = Math.max( this.w, v.w ); - - return this; - - }, - - clamp: function ( min, max ) { - - // This function assumes min < max, if this assumption isn't true it will not operate correctly - - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - this.z = Math.max( min.z, Math.min( max.z, this.z ) ); - this.w = Math.max( min.w, Math.min( max.w, this.w ) ); - - return this; - - }, - - clampScalar: function () { - - var min, max; - - return function clampScalar( minVal, maxVal ) { - - if ( min === undefined ) { - - min = new Vector4(); - max = new Vector4(); - - } - - min.set( minVal, minVal, minVal, minVal ); - max.set( maxVal, maxVal, maxVal, maxVal ); - - return this.clamp( min, max ); - - }; - - }(), - - floor: function () { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); - this.w = Math.floor( this.w ); - - return this; - - }, - - ceil: function () { - - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); - this.w = Math.ceil( this.w ); - - return this; - - }, - - round: function () { - - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); - this.w = Math.round( this.w ); - - return this; - - }, - - roundToZero: function () { - - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); - this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); - - return this; - - }, - - negate: function () { - - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; - this.w = - this.w; - - return this; - - }, - - dot: function ( v ) { - - return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; - - }, - - lengthSq: function () { - - return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; - - }, - - length: function () { - - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); - - }, - - lengthManhattan: function () { - - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); - - }, - - normalize: function () { - - return this.divideScalar( this.length() ); - - }, - - setLength: function ( length ) { - - return this.multiplyScalar( length / this.length() ); - - }, - - lerp: function ( v, alpha ) { - - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; - this.w += ( v.w - this.w ) * alpha; - - return this; - - }, - - lerpVectors: function ( v1, v2, alpha ) { - - return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); - - }, - - equals: function ( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); - - }, - - fromArray: function ( array, offset ) { - - if ( offset === undefined ) offset = 0; - - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; - this.w = array[ offset + 3 ]; - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; - array[ offset + 3 ] = this.w; - - return array; - - }, - - fromAttribute: function ( attribute, index, offset ) { - - if ( offset !== undefined ) { - - console.warn( 'THREE.Vector4: offset has been removed from .fromAttribute().' ); - - } - - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); - this.w = attribute.getW( index ); - - return this; - - } - - }; - - /** - * @author szimek / https://github.com/szimek/ - * @author alteredq / http://alteredqualia.com/ - * @author Marius Kintel / https://github.com/kintel - */ - - /* - In options, we can specify: - * Texture parameters for an auto-generated target texture - * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers - */ - function WebGLRenderTarget( width, height, options ) { - - this.uuid = _Math.generateUUID(); - - this.width = width; - this.height = height; - - this.scissor = new Vector4( 0, 0, width, height ); - this.scissorTest = false; - - this.viewport = new Vector4( 0, 0, width, height ); - - options = options || {}; - - if ( options.minFilter === undefined ) options.minFilter = LinearFilter; - - this.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); - - this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; - this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; - this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; - - } - - Object.assign( WebGLRenderTarget.prototype, EventDispatcher.prototype, { - - isWebGLRenderTarget: true, - - setSize: function ( width, height ) { - - if ( this.width !== width || this.height !== height ) { - - this.width = width; - this.height = height; - - this.dispose(); - - } - - this.viewport.set( 0, 0, width, height ); - this.scissor.set( 0, 0, width, height ); - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.width = source.width; - this.height = source.height; - - this.viewport.copy( source.viewport ); - - this.texture = source.texture.clone(); - - this.depthBuffer = source.depthBuffer; - this.stencilBuffer = source.stencilBuffer; - this.depthTexture = source.depthTexture; - - return this; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - /** - * @author alteredq / http://alteredqualia.com - */ - - function WebGLRenderTargetCube( width, height, options ) { - - WebGLRenderTarget.call( this, width, height, options ); - - this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 - this.activeMipMapLevel = 0; - - } - - WebGLRenderTargetCube.prototype = Object.create( WebGLRenderTarget.prototype ); - WebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube; - - WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true; - - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://clara.io - */ - - function Quaternion( x, y, z, w ) { - - this._x = x || 0; - this._y = y || 0; - this._z = z || 0; - this._w = ( w !== undefined ) ? w : 1; - - } - - Quaternion.prototype = { - - constructor: Quaternion, - - get x () { - - return this._x; - - }, - - set x ( value ) { - - this._x = value; - this.onChangeCallback(); - - }, - - get y () { - - return this._y; - - }, - - set y ( value ) { - - this._y = value; - this.onChangeCallback(); - - }, - - get z () { - - return this._z; - - }, - - set z ( value ) { - - this._z = value; - this.onChangeCallback(); - - }, - - get w () { - - return this._w; - - }, - - set w ( value ) { - - this._w = value; - this.onChangeCallback(); - - }, - - set: function ( x, y, z, w ) { - - this._x = x; - this._y = y; - this._z = z; - this._w = w; - - this.onChangeCallback(); - - return this; - - }, - - clone: function () { - - return new this.constructor( this._x, this._y, this._z, this._w ); - - }, - - copy: function ( quaternion ) { - - this._x = quaternion.x; - this._y = quaternion.y; - this._z = quaternion.z; - this._w = quaternion.w; - - this.onChangeCallback(); - - return this; - - }, - - setFromEuler: function ( euler, update ) { - - if ( (euler && euler.isEuler) === false ) { - - throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' ); - - } - - // http://www.mathworks.com/matlabcentral/fileexchange/ - // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ - // content/SpinCalc.m - - var c1 = Math.cos( euler._x / 2 ); - var c2 = Math.cos( euler._y / 2 ); - var c3 = Math.cos( euler._z / 2 ); - var s1 = Math.sin( euler._x / 2 ); - var s2 = Math.sin( euler._y / 2 ); - var s3 = Math.sin( euler._z / 2 ); - - var order = euler.order; - - if ( order === 'XYZ' ) { - - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; - - } else if ( order === 'YXZ' ) { - - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; - - } else if ( order === 'ZXY' ) { - - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; - - } else if ( order === 'ZYX' ) { - - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; - - } else if ( order === 'YZX' ) { - - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; - - } else if ( order === 'XZY' ) { - - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; - - } - - if ( update !== false ) this.onChangeCallback(); - - return this; - - }, - - setFromAxisAngle: function ( axis, angle ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm - - // assumes axis is normalized - - var halfAngle = angle / 2, s = Math.sin( halfAngle ); - - this._x = axis.x * s; - this._y = axis.y * s; - this._z = axis.z * s; - this._w = Math.cos( halfAngle ); - - this.onChangeCallback(); - - return this; - - }, - - setFromRotationMatrix: function ( m ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - var te = m.elements, - - m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], - m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], - m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], - - trace = m11 + m22 + m33, - s; - - if ( trace > 0 ) { - - s = 0.5 / Math.sqrt( trace + 1.0 ); - - this._w = 0.25 / s; - this._x = ( m32 - m23 ) * s; - this._y = ( m13 - m31 ) * s; - this._z = ( m21 - m12 ) * s; - - } else if ( m11 > m22 && m11 > m33 ) { - - s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); - - this._w = ( m32 - m23 ) / s; - this._x = 0.25 * s; - this._y = ( m12 + m21 ) / s; - this._z = ( m13 + m31 ) / s; - - } else if ( m22 > m33 ) { - - s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); - - this._w = ( m13 - m31 ) / s; - this._x = ( m12 + m21 ) / s; - this._y = 0.25 * s; - this._z = ( m23 + m32 ) / s; - - } else { - - s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); - - this._w = ( m21 - m12 ) / s; - this._x = ( m13 + m31 ) / s; - this._y = ( m23 + m32 ) / s; - this._z = 0.25 * s; - - } - - this.onChangeCallback(); - - return this; - - }, - - setFromUnitVectors: function () { - - // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final - - // assumes direction vectors vFrom and vTo are normalized - - var v1, r; - - var EPS = 0.000001; - - return function setFromUnitVectors( vFrom, vTo ) { - - if ( v1 === undefined ) v1 = new Vector3(); - - r = vFrom.dot( vTo ) + 1; - - if ( r < EPS ) { - - r = 0; - - if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { - - v1.set( - vFrom.y, vFrom.x, 0 ); - - } else { - - v1.set( 0, - vFrom.z, vFrom.y ); - - } - - } else { - - v1.crossVectors( vFrom, vTo ); - - } - - this._x = v1.x; - this._y = v1.y; - this._z = v1.z; - this._w = r; - - return this.normalize(); - - }; - - }(), - - inverse: function () { - - return this.conjugate().normalize(); - - }, - - conjugate: function () { - - this._x *= - 1; - this._y *= - 1; - this._z *= - 1; - - this.onChangeCallback(); - - return this; - - }, - - dot: function ( v ) { - - return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; - - }, - - lengthSq: function () { - - return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; - - }, - - length: function () { - - return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); - - }, - - normalize: function () { - - var l = this.length(); - - if ( l === 0 ) { - - this._x = 0; - this._y = 0; - this._z = 0; - this._w = 1; - - } else { - - l = 1 / l; - - this._x = this._x * l; - this._y = this._y * l; - this._z = this._z * l; - this._w = this._w * l; - - } - - this.onChangeCallback(); - - return this; - - }, - - multiply: function ( q, p ) { - - if ( p !== undefined ) { - - console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); - return this.multiplyQuaternions( q, p ); - - } - - return this.multiplyQuaternions( this, q ); - - }, - - premultiply: function ( q ) { - - return this.multiplyQuaternions( q, this ); - - }, - - multiplyQuaternions: function ( a, b ) { - - // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm - - var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; - var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; - - this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; - this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; - this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; - this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; - - this.onChangeCallback(); - - return this; - - }, - - slerp: function ( qb, t ) { - - if ( t === 0 ) return this; - if ( t === 1 ) return this.copy( qb ); - - var x = this._x, y = this._y, z = this._z, w = this._w; - - // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ - - var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; - - if ( cosHalfTheta < 0 ) { - - this._w = - qb._w; - this._x = - qb._x; - this._y = - qb._y; - this._z = - qb._z; - - cosHalfTheta = - cosHalfTheta; - - } else { - - this.copy( qb ); - - } - - if ( cosHalfTheta >= 1.0 ) { - - this._w = w; - this._x = x; - this._y = y; - this._z = z; - - return this; - - } - - var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); - - if ( Math.abs( sinHalfTheta ) < 0.001 ) { - - this._w = 0.5 * ( w + this._w ); - this._x = 0.5 * ( x + this._x ); - this._y = 0.5 * ( y + this._y ); - this._z = 0.5 * ( z + this._z ); - - return this; - - } - - var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); - var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, - ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; - - this._w = ( w * ratioA + this._w * ratioB ); - this._x = ( x * ratioA + this._x * ratioB ); - this._y = ( y * ratioA + this._y * ratioB ); - this._z = ( z * ratioA + this._z * ratioB ); - - this.onChangeCallback(); - - return this; - - }, - - equals: function ( quaternion ) { - - return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); - - }, - - fromArray: function ( array, offset ) { - - if ( offset === undefined ) offset = 0; - - this._x = array[ offset ]; - this._y = array[ offset + 1 ]; - this._z = array[ offset + 2 ]; - this._w = array[ offset + 3 ]; - - this.onChangeCallback(); - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._w; - - return array; - - }, - - onChange: function ( callback ) { - - this.onChangeCallback = callback; - - return this; - - }, - - onChangeCallback: function () {} - - }; - - Object.assign( Quaternion, { - - slerp: function( qa, qb, qm, t ) { - - return qm.copy( qa ).slerp( qb, t ); - - }, - - slerpFlat: function( - dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { - - // fuzz-free, array-based Quaternion SLERP operation - - var x0 = src0[ srcOffset0 + 0 ], - y0 = src0[ srcOffset0 + 1 ], - z0 = src0[ srcOffset0 + 2 ], - w0 = src0[ srcOffset0 + 3 ], - - x1 = src1[ srcOffset1 + 0 ], - y1 = src1[ srcOffset1 + 1 ], - z1 = src1[ srcOffset1 + 2 ], - w1 = src1[ srcOffset1 + 3 ]; - - if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { - - var s = 1 - t, - - cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, - - dir = ( cos >= 0 ? 1 : - 1 ), - sqrSin = 1 - cos * cos; - - // Skip the Slerp for tiny steps to avoid numeric problems: - if ( sqrSin > Number.EPSILON ) { - - var sin = Math.sqrt( sqrSin ), - len = Math.atan2( sin, cos * dir ); - - s = Math.sin( s * len ) / sin; - t = Math.sin( t * len ) / sin; - - } - - var tDir = t * dir; - - x0 = x0 * s + x1 * tDir; - y0 = y0 * s + y1 * tDir; - z0 = z0 * s + z1 * tDir; - w0 = w0 * s + w1 * tDir; - - // Normalize in case we just did a lerp: - if ( s === 1 - t ) { - - var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); - - x0 *= f; - y0 *= f; - z0 *= f; - w0 *= f; - - } - - } - - dst[ dstOffset ] = x0; - dst[ dstOffset + 1 ] = y0; - dst[ dstOffset + 2 ] = z0; - dst[ dstOffset + 3 ] = w0; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author *kile / http://kile.stravaganza.org/ - * @author philogb / http://blog.thejit.org/ - * @author mikael emtinger / http://gomo.se/ - * @author egraether / http://egraether.com/ - * @author WestLangley / http://github.com/WestLangley - */ - - function Vector3( x, y, z ) { - - this.x = x || 0; - this.y = y || 0; - this.z = z || 0; - - } - - Vector3.prototype = { - - constructor: Vector3, - - isVector3: true, - - set: function ( x, y, z ) { - - this.x = x; - this.y = y; - this.z = z; - - return this; - - }, - - setScalar: function ( scalar ) { - - this.x = scalar; - this.y = scalar; - this.z = scalar; - - return this; - - }, - - setX: function ( x ) { - - this.x = x; - - return this; - - }, - - setY: function ( y ) { - - this.y = y; - - return this; - - }, - - setZ: function ( z ) { - - this.z = z; - - return this; - - }, - - setComponent: function ( index, value ) { - - switch ( index ) { - - case 0: this.x = value; break; - case 1: this.y = value; break; - case 2: this.z = value; break; - default: throw new Error( 'index is out of range: ' + index ); - - } - - return this; - - }, - - getComponent: function ( index ) { - - switch ( index ) { - - case 0: return this.x; - case 1: return this.y; - case 2: return this.z; - default: throw new Error( 'index is out of range: ' + index ); - - } - - }, - - clone: function () { - - return new this.constructor( this.x, this.y, this.z ); - - }, - - copy: function ( v ) { - - this.x = v.x; - this.y = v.y; - this.z = v.z; - - return this; - - }, - - add: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); - - } - - this.x += v.x; - this.y += v.y; - this.z += v.z; - - return this; - - }, - - addScalar: function ( s ) { - - this.x += s; - this.y += s; - this.z += s; - - return this; - - }, - - addVectors: function ( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; - - return this; - - }, - - addScaledVector: function ( v, s ) { - - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; - - return this; - - }, - - sub: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } - - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; - - return this; - - }, - - subScalar: function ( s ) { - - this.x -= s; - this.y -= s; - this.z -= s; - - return this; - - }, - - subVectors: function ( a, b ) { - - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; - - return this; - - }, - - multiply: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); - return this.multiplyVectors( v, w ); - - } - - this.x *= v.x; - this.y *= v.y; - this.z *= v.z; - - return this; - - }, - - multiplyScalar: function ( scalar ) { - - if ( isFinite( scalar ) ) { - - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - - } else { - - this.x = 0; - this.y = 0; - this.z = 0; - - } - - return this; - - }, - - multiplyVectors: function ( a, b ) { - - this.x = a.x * b.x; - this.y = a.y * b.y; - this.z = a.z * b.z; - - return this; - - }, - - applyEuler: function () { - - var quaternion; - - return function applyEuler( euler ) { - - if ( (euler && euler.isEuler) === false ) { - - console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); - - } - - if ( quaternion === undefined ) quaternion = new Quaternion(); - - return this.applyQuaternion( quaternion.setFromEuler( euler ) ); - - }; - - }(), - - applyAxisAngle: function () { - - var quaternion; - - return function applyAxisAngle( axis, angle ) { - - if ( quaternion === undefined ) quaternion = new Quaternion(); - - return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); - - }; - - }(), - - applyMatrix3: function ( m ) { - - var x = this.x, y = this.y, z = this.z; - var e = m.elements; - - this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; - this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; - this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; - - return this; - - }, - - applyMatrix4: function ( m ) { - - // input: THREE.Matrix4 affine matrix - - var x = this.x, y = this.y, z = this.z; - var e = m.elements; - - this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ]; - this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ]; - this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ]; - - return this; - - }, - - applyProjection: function ( m ) { - - // input: THREE.Matrix4 projection matrix - - var x = this.x, y = this.y, z = this.z; - var e = m.elements; - var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide - - this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d; - this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d; - this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d; - - return this; - - }, - - applyQuaternion: function ( q ) { - - var x = this.x, y = this.y, z = this.z; - var qx = q.x, qy = q.y, qz = q.z, qw = q.w; - - // calculate quat * vector - - var ix = qw * x + qy * z - qz * y; - var iy = qw * y + qz * x - qx * z; - var iz = qw * z + qx * y - qy * x; - var iw = - qx * x - qy * y - qz * z; - - // calculate result * inverse quat - - this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; - this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; - this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; - - return this; - - }, - - project: function () { - - var matrix; - - return function project( camera ) { - - if ( matrix === undefined ) matrix = new Matrix4(); - - matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) ); - return this.applyProjection( matrix ); - - }; - - }(), - - unproject: function () { - - var matrix; - - return function unproject( camera ) { - - if ( matrix === undefined ) matrix = new Matrix4(); - - matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) ); - return this.applyProjection( matrix ); - - }; - - }(), - - transformDirection: function ( m ) { - - // input: THREE.Matrix4 affine matrix - // vector interpreted as a direction - - var x = this.x, y = this.y, z = this.z; - var e = m.elements; - - this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; - this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; - this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; - - return this.normalize(); - - }, - - divide: function ( v ) { - - this.x /= v.x; - this.y /= v.y; - this.z /= v.z; - - return this; - - }, - - divideScalar: function ( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - }, - - min: function ( v ) { - - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - this.z = Math.min( this.z, v.z ); - - return this; - - }, - - max: function ( v ) { - - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); - - return this; - - }, - - clamp: function ( min, max ) { - - // This function assumes min < max, if this assumption isn't true it will not operate correctly - - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - this.z = Math.max( min.z, Math.min( max.z, this.z ) ); - - return this; - - }, - - clampScalar: function () { - - var min, max; - - return function clampScalar( minVal, maxVal ) { - - if ( min === undefined ) { - - min = new Vector3(); - max = new Vector3(); - - } - - min.set( minVal, minVal, minVal ); - max.set( maxVal, maxVal, maxVal ); - - return this.clamp( min, max ); - - }; - - }(), - - clampLength: function ( min, max ) { - - var length = this.length(); - - return this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length ); - - }, - - floor: function () { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); - - return this; - - }, - - ceil: function () { - - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); - - return this; - - }, - - round: function () { - - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); - - return this; - - }, - - roundToZero: function () { - - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); - - return this; - - }, - - negate: function () { - - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; - - return this; - - }, - - dot: function ( v ) { - - return this.x * v.x + this.y * v.y + this.z * v.z; - - }, - - lengthSq: function () { - - return this.x * this.x + this.y * this.y + this.z * this.z; - - }, - - length: function () { - - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); - - }, - - lengthManhattan: function () { - - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); - - }, - - normalize: function () { - - return this.divideScalar( this.length() ); - - }, - - setLength: function ( length ) { - - return this.multiplyScalar( length / this.length() ); - - }, - - lerp: function ( v, alpha ) { - - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; - - return this; - - }, - - lerpVectors: function ( v1, v2, alpha ) { - - return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); - - }, - - cross: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); - return this.crossVectors( v, w ); - - } - - var x = this.x, y = this.y, z = this.z; - - this.x = y * v.z - z * v.y; - this.y = z * v.x - x * v.z; - this.z = x * v.y - y * v.x; - - return this; - - }, - - crossVectors: function ( a, b ) { - - var ax = a.x, ay = a.y, az = a.z; - var bx = b.x, by = b.y, bz = b.z; - - this.x = ay * bz - az * by; - this.y = az * bx - ax * bz; - this.z = ax * by - ay * bx; - - return this; - - }, - - projectOnVector: function ( vector ) { - - var scalar = vector.dot( this ) / vector.lengthSq(); - - return this.copy( vector ).multiplyScalar( scalar ); - - }, - - projectOnPlane: function () { - - var v1; - - return function projectOnPlane( planeNormal ) { - - if ( v1 === undefined ) v1 = new Vector3(); - - v1.copy( this ).projectOnVector( planeNormal ); - - return this.sub( v1 ); - - }; - - }(), - - reflect: function () { - - // reflect incident vector off plane orthogonal to normal - // normal is assumed to have unit length - - var v1; - - return function reflect( normal ) { - - if ( v1 === undefined ) v1 = new Vector3(); - - return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); - - }; - - }(), - - angleTo: function ( v ) { - - var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) ); - - // clamp, to handle numerical problems - - return Math.acos( _Math.clamp( theta, - 1, 1 ) ); - - }, - - distanceTo: function ( v ) { - - return Math.sqrt( this.distanceToSquared( v ) ); - - }, - - distanceToSquared: function ( v ) { - - var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; - - return dx * dx + dy * dy + dz * dz; - - }, - - distanceToManhattan: function ( v ) { - - return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); - - }, - - setFromSpherical: function( s ) { - - var sinPhiRadius = Math.sin( s.phi ) * s.radius; - - this.x = sinPhiRadius * Math.sin( s.theta ); - this.y = Math.cos( s.phi ) * s.radius; - this.z = sinPhiRadius * Math.cos( s.theta ); - - return this; - - }, - - setFromCylindrical: function( c ) { - - this.x = c.radius * Math.sin( c.theta ); - this.y = c.y; - this.z = c.radius * Math.cos( c.theta ); - - return this; - - }, - - setFromMatrixPosition: function ( m ) { - - return this.setFromMatrixColumn( m, 3 ); - - }, - - setFromMatrixScale: function ( m ) { - - var sx = this.setFromMatrixColumn( m, 0 ).length(); - var sy = this.setFromMatrixColumn( m, 1 ).length(); - var sz = this.setFromMatrixColumn( m, 2 ).length(); - - this.x = sx; - this.y = sy; - this.z = sz; - - return this; - - }, - - setFromMatrixColumn: function ( m, index ) { - - if ( typeof m === 'number' ) { - - console.warn( 'THREE.Vector3: setFromMatrixColumn now expects ( matrix, index ).' ); - var temp = m; - m = index; - index = temp; - - } - - return this.fromArray( m.elements, index * 4 ); - - }, - - equals: function ( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); - - }, - - fromArray: function ( array, offset ) { - - if ( offset === undefined ) offset = 0; - - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; - - return array; - - }, - - fromAttribute: function ( attribute, index, offset ) { - - if ( offset !== undefined ) { - - console.warn( 'THREE.Vector3: offset has been removed from .fromAttribute().' ); - - } - - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); - - return this; - - } - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author supereggbert / http://www.paulbrunt.co.uk/ - * @author philogb / http://blog.thejit.org/ - * @author jordi_ros / http://plattsoft.com - * @author D1plo1d / http://github.com/D1plo1d - * @author alteredq / http://alteredqualia.com/ - * @author mikael emtinger / http://gomo.se/ - * @author timknip / http://www.floorplanner.com/ - * @author bhouston / http://clara.io - * @author WestLangley / http://github.com/WestLangley - */ - - function Matrix4() { - - this.elements = new Float32Array( [ - - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - - ] ); - - if ( arguments.length > 0 ) { - - console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); - - } - - } - - Matrix4.prototype = { - - constructor: Matrix4, - - isMatrix4: true, - - set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { - - var te = this.elements; - - te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; - te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; - te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; - te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; - - return this; - - }, - - identity: function () { - - this.set( - - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - - ); - - return this; - - }, - - clone: function () { - - return new Matrix4().fromArray( this.elements ); - - }, - - copy: function ( m ) { - - this.elements.set( m.elements ); - - return this; - - }, - - copyPosition: function ( m ) { - - var te = this.elements; - var me = m.elements; - - te[ 12 ] = me[ 12 ]; - te[ 13 ] = me[ 13 ]; - te[ 14 ] = me[ 14 ]; - - return this; - - }, - - extractBasis: function ( xAxis, yAxis, zAxis ) { - - xAxis.setFromMatrixColumn( this, 0 ); - yAxis.setFromMatrixColumn( this, 1 ); - zAxis.setFromMatrixColumn( this, 2 ); - - return this; - - }, - - makeBasis: function ( xAxis, yAxis, zAxis ) { - - this.set( - xAxis.x, yAxis.x, zAxis.x, 0, - xAxis.y, yAxis.y, zAxis.y, 0, - xAxis.z, yAxis.z, zAxis.z, 0, - 0, 0, 0, 1 - ); - - return this; - - }, - - extractRotation: function () { - - var v1; - - return function extractRotation( m ) { - - if ( v1 === undefined ) v1 = new Vector3(); - - var te = this.elements; - var me = m.elements; - - var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length(); - var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length(); - var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length(); - - te[ 0 ] = me[ 0 ] * scaleX; - te[ 1 ] = me[ 1 ] * scaleX; - te[ 2 ] = me[ 2 ] * scaleX; - - te[ 4 ] = me[ 4 ] * scaleY; - te[ 5 ] = me[ 5 ] * scaleY; - te[ 6 ] = me[ 6 ] * scaleY; - - te[ 8 ] = me[ 8 ] * scaleZ; - te[ 9 ] = me[ 9 ] * scaleZ; - te[ 10 ] = me[ 10 ] * scaleZ; - - return this; - - }; - - }(), - - makeRotationFromEuler: function ( euler ) { - - if ( (euler && euler.isEuler) === false ) { - - console.error( 'THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); - - } - - var te = this.elements; - - var x = euler.x, y = euler.y, z = euler.z; - var a = Math.cos( x ), b = Math.sin( x ); - var c = Math.cos( y ), d = Math.sin( y ); - var e = Math.cos( z ), f = Math.sin( z ); - - if ( euler.order === 'XYZ' ) { - - var ae = a * e, af = a * f, be = b * e, bf = b * f; - - te[ 0 ] = c * e; - te[ 4 ] = - c * f; - te[ 8 ] = d; - - te[ 1 ] = af + be * d; - te[ 5 ] = ae - bf * d; - te[ 9 ] = - b * c; - - te[ 2 ] = bf - ae * d; - te[ 6 ] = be + af * d; - te[ 10 ] = a * c; - - } else if ( euler.order === 'YXZ' ) { - - var ce = c * e, cf = c * f, de = d * e, df = d * f; - - te[ 0 ] = ce + df * b; - te[ 4 ] = de * b - cf; - te[ 8 ] = a * d; - - te[ 1 ] = a * f; - te[ 5 ] = a * e; - te[ 9 ] = - b; - - te[ 2 ] = cf * b - de; - te[ 6 ] = df + ce * b; - te[ 10 ] = a * c; - - } else if ( euler.order === 'ZXY' ) { - - var ce = c * e, cf = c * f, de = d * e, df = d * f; - - te[ 0 ] = ce - df * b; - te[ 4 ] = - a * f; - te[ 8 ] = de + cf * b; - - te[ 1 ] = cf + de * b; - te[ 5 ] = a * e; - te[ 9 ] = df - ce * b; - - te[ 2 ] = - a * d; - te[ 6 ] = b; - te[ 10 ] = a * c; - - } else if ( euler.order === 'ZYX' ) { - - var ae = a * e, af = a * f, be = b * e, bf = b * f; - - te[ 0 ] = c * e; - te[ 4 ] = be * d - af; - te[ 8 ] = ae * d + bf; - - te[ 1 ] = c * f; - te[ 5 ] = bf * d + ae; - te[ 9 ] = af * d - be; - - te[ 2 ] = - d; - te[ 6 ] = b * c; - te[ 10 ] = a * c; - - } else if ( euler.order === 'YZX' ) { - - var ac = a * c, ad = a * d, bc = b * c, bd = b * d; - - te[ 0 ] = c * e; - te[ 4 ] = bd - ac * f; - te[ 8 ] = bc * f + ad; - - te[ 1 ] = f; - te[ 5 ] = a * e; - te[ 9 ] = - b * e; - - te[ 2 ] = - d * e; - te[ 6 ] = ad * f + bc; - te[ 10 ] = ac - bd * f; - - } else if ( euler.order === 'XZY' ) { - - var ac = a * c, ad = a * d, bc = b * c, bd = b * d; - - te[ 0 ] = c * e; - te[ 4 ] = - f; - te[ 8 ] = d * e; - - te[ 1 ] = ac * f + bd; - te[ 5 ] = a * e; - te[ 9 ] = ad * f - bc; - - te[ 2 ] = bc * f - ad; - te[ 6 ] = b * e; - te[ 10 ] = bd * f + ac; - - } - - // last column - te[ 3 ] = 0; - te[ 7 ] = 0; - te[ 11 ] = 0; - - // bottom row - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; - - return this; - - }, - - makeRotationFromQuaternion: function ( q ) { - - var te = this.elements; - - var x = q.x, y = q.y, z = q.z, w = q.w; - var x2 = x + x, y2 = y + y, z2 = z + z; - var xx = x * x2, xy = x * y2, xz = x * z2; - var yy = y * y2, yz = y * z2, zz = z * z2; - var wx = w * x2, wy = w * y2, wz = w * z2; - - te[ 0 ] = 1 - ( yy + zz ); - te[ 4 ] = xy - wz; - te[ 8 ] = xz + wy; - - te[ 1 ] = xy + wz; - te[ 5 ] = 1 - ( xx + zz ); - te[ 9 ] = yz - wx; - - te[ 2 ] = xz - wy; - te[ 6 ] = yz + wx; - te[ 10 ] = 1 - ( xx + yy ); - - // last column - te[ 3 ] = 0; - te[ 7 ] = 0; - te[ 11 ] = 0; - - // bottom row - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; - - return this; - - }, - - lookAt: function () { - - var x, y, z; - - return function lookAt( eye, target, up ) { - - if ( x === undefined ) { - - x = new Vector3(); - y = new Vector3(); - z = new Vector3(); - - } - - var te = this.elements; - - z.subVectors( eye, target ).normalize(); - - if ( z.lengthSq() === 0 ) { - - z.z = 1; - - } - - x.crossVectors( up, z ).normalize(); - - if ( x.lengthSq() === 0 ) { - - z.z += 0.0001; - x.crossVectors( up, z ).normalize(); - - } - - y.crossVectors( z, x ); - - - te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x; - te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y; - te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z; - - return this; - - }; - - }(), - - multiply: function ( m, n ) { - - if ( n !== undefined ) { - - console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); - return this.multiplyMatrices( m, n ); - - } - - return this.multiplyMatrices( this, m ); - - }, - - premultiply: function ( m ) { - - return this.multiplyMatrices( m, this ); - - }, - - multiplyMatrices: function ( a, b ) { - - var ae = a.elements; - var be = b.elements; - var te = this.elements; - - var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; - var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; - var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; - var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; - - var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; - var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; - var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; - var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; - - te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; - te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; - te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; - te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; - - te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; - te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; - te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; - te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; - - te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; - te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; - te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; - te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; - - te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; - te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; - te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; - te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; - - return this; - - }, - - multiplyToArray: function ( a, b, r ) { - - var te = this.elements; - - this.multiplyMatrices( a, b ); - - r[ 0 ] = te[ 0 ]; r[ 1 ] = te[ 1 ]; r[ 2 ] = te[ 2 ]; r[ 3 ] = te[ 3 ]; - r[ 4 ] = te[ 4 ]; r[ 5 ] = te[ 5 ]; r[ 6 ] = te[ 6 ]; r[ 7 ] = te[ 7 ]; - r[ 8 ] = te[ 8 ]; r[ 9 ] = te[ 9 ]; r[ 10 ] = te[ 10 ]; r[ 11 ] = te[ 11 ]; - r[ 12 ] = te[ 12 ]; r[ 13 ] = te[ 13 ]; r[ 14 ] = te[ 14 ]; r[ 15 ] = te[ 15 ]; - - return this; - - }, - - multiplyScalar: function ( s ) { - - var te = this.elements; - - te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; - te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; - te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; - te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; - - return this; - - }, - - applyToVector3Array: function () { - - var v1; - - return function applyToVector3Array( array, offset, length ) { - - if ( v1 === undefined ) v1 = new Vector3(); - if ( offset === undefined ) offset = 0; - if ( length === undefined ) length = array.length; - - for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) { - - v1.fromArray( array, j ); - v1.applyMatrix4( this ); - v1.toArray( array, j ); - - } - - return array; - - }; - - }(), - - applyToBufferAttribute: function () { - - var v1; - - return function applyToBufferAttribute( attribute ) { - - if ( v1 === undefined ) v1 = new Vector3(); - - for ( var i = 0, l = attribute.count; i < l; i ++ ) { - - v1.x = attribute.getX( i ); - v1.y = attribute.getY( i ); - v1.z = attribute.getZ( i ); - - v1.applyMatrix4( this ); - - attribute.setXYZ( i, v1.x, v1.y, v1.z ); - - } - - return attribute; - - }; - - }(), - - determinant: function () { - - var te = this.elements; - - var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; - var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; - var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; - var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; - - //TODO: make this more efficient - //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) - - return ( - n41 * ( - + n14 * n23 * n32 - - n13 * n24 * n32 - - n14 * n22 * n33 - + n12 * n24 * n33 - + n13 * n22 * n34 - - n12 * n23 * n34 - ) + - n42 * ( - + n11 * n23 * n34 - - n11 * n24 * n33 - + n14 * n21 * n33 - - n13 * n21 * n34 - + n13 * n24 * n31 - - n14 * n23 * n31 - ) + - n43 * ( - + n11 * n24 * n32 - - n11 * n22 * n34 - - n14 * n21 * n32 - + n12 * n21 * n34 - + n14 * n22 * n31 - - n12 * n24 * n31 - ) + - n44 * ( - - n13 * n22 * n31 - - n11 * n23 * n32 - + n11 * n22 * n33 - + n13 * n21 * n32 - - n12 * n21 * n33 - + n12 * n23 * n31 - ) - - ); - - }, - - transpose: function () { - - var te = this.elements; - var tmp; - - tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; - tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; - tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; - - tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; - tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; - tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; - - return this; - - }, - - setPosition: function ( v ) { - - var te = this.elements; - - te[ 12 ] = v.x; - te[ 13 ] = v.y; - te[ 14 ] = v.z; - - return this; - - }, - - getInverse: function ( m, throwOnDegenerate ) { - - // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm - var te = this.elements, - me = m.elements, - - n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ], - n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ], - n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ], - n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ], - - t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, - t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, - t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, - t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; - - var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; - - if ( det === 0 ) { - - var msg = "THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0"; - - if ( throwOnDegenerate === true ) { - - throw new Error( msg ); - - } else { - - console.warn( msg ); - - } - - return this.identity(); - - } - - var detInv = 1 / det; - - te[ 0 ] = t11 * detInv; - te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; - te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; - te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; - - te[ 4 ] = t12 * detInv; - te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; - te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; - te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; - - te[ 8 ] = t13 * detInv; - te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; - te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; - te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; - - te[ 12 ] = t14 * detInv; - te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; - te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; - te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; - - return this; - - }, - - scale: function ( v ) { - - var te = this.elements; - var x = v.x, y = v.y, z = v.z; - - te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; - te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; - te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; - te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; - - return this; - - }, - - getMaxScaleOnAxis: function () { - - var te = this.elements; - - var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; - var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; - var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; - - return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); - - }, - - makeTranslation: function ( x, y, z ) { - - this.set( - - 1, 0, 0, x, - 0, 1, 0, y, - 0, 0, 1, z, - 0, 0, 0, 1 - - ); - - return this; - - }, - - makeRotationX: function ( theta ) { - - var c = Math.cos( theta ), s = Math.sin( theta ); - - this.set( - - 1, 0, 0, 0, - 0, c, - s, 0, - 0, s, c, 0, - 0, 0, 0, 1 - - ); - - return this; - - }, - - makeRotationY: function ( theta ) { - - var c = Math.cos( theta ), s = Math.sin( theta ); - - this.set( - - c, 0, s, 0, - 0, 1, 0, 0, - - s, 0, c, 0, - 0, 0, 0, 1 - - ); - - return this; - - }, - - makeRotationZ: function ( theta ) { - - var c = Math.cos( theta ), s = Math.sin( theta ); - - this.set( - - c, - s, 0, 0, - s, c, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - - ); - - return this; - - }, - - makeRotationAxis: function ( axis, angle ) { - - // Based on http://www.gamedev.net/reference/articles/article1199.asp - - var c = Math.cos( angle ); - var s = Math.sin( angle ); - var t = 1 - c; - var x = axis.x, y = axis.y, z = axis.z; - var tx = t * x, ty = t * y; - - this.set( - - tx * x + c, tx * y - s * z, tx * z + s * y, 0, - tx * y + s * z, ty * y + c, ty * z - s * x, 0, - tx * z - s * y, ty * z + s * x, t * z * z + c, 0, - 0, 0, 0, 1 - - ); - - return this; - - }, - - makeScale: function ( x, y, z ) { - - this.set( - - x, 0, 0, 0, - 0, y, 0, 0, - 0, 0, z, 0, - 0, 0, 0, 1 - - ); - - return this; - - }, - - makeShear: function ( x, y, z ) { - - this.set( - - 1, y, z, 0, - x, 1, z, 0, - x, y, 1, 0, - 0, 0, 0, 1 - - ); - - return this; - - }, - - compose: function ( position, quaternion, scale ) { - - this.makeRotationFromQuaternion( quaternion ); - this.scale( scale ); - this.setPosition( position ); - - return this; - - }, - - decompose: function () { - - var vector, matrix; - - return function decompose( position, quaternion, scale ) { - - if ( vector === undefined ) { - - vector = new Vector3(); - matrix = new Matrix4(); - - } - - var te = this.elements; - - var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); - var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); - var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); - - // if determine is negative, we need to invert one scale - var det = this.determinant(); - if ( det < 0 ) { - - sx = - sx; - - } - - position.x = te[ 12 ]; - position.y = te[ 13 ]; - position.z = te[ 14 ]; - - // scale the rotation part - - matrix.elements.set( this.elements ); // at this point matrix is incomplete so we can't use .copy() - - var invSX = 1 / sx; - var invSY = 1 / sy; - var invSZ = 1 / sz; - - matrix.elements[ 0 ] *= invSX; - matrix.elements[ 1 ] *= invSX; - matrix.elements[ 2 ] *= invSX; - - matrix.elements[ 4 ] *= invSY; - matrix.elements[ 5 ] *= invSY; - matrix.elements[ 6 ] *= invSY; - - matrix.elements[ 8 ] *= invSZ; - matrix.elements[ 9 ] *= invSZ; - matrix.elements[ 10 ] *= invSZ; - - quaternion.setFromRotationMatrix( matrix ); - - scale.x = sx; - scale.y = sy; - scale.z = sz; - - return this; - - }; - - }(), - - makeFrustum: function ( left, right, bottom, top, near, far ) { - - var te = this.elements; - var x = 2 * near / ( right - left ); - var y = 2 * near / ( top - bottom ); - - var a = ( right + left ) / ( right - left ); - var b = ( top + bottom ) / ( top - bottom ); - var c = - ( far + near ) / ( far - near ); - var d = - 2 * far * near / ( far - near ); - - te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; - te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; - te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; - te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; - - return this; - - }, - - makePerspective: function ( fov, aspect, near, far ) { - - var ymax = near * Math.tan( _Math.DEG2RAD * fov * 0.5 ); - var ymin = - ymax; - var xmin = ymin * aspect; - var xmax = ymax * aspect; - - return this.makeFrustum( xmin, xmax, ymin, ymax, near, far ); - - }, - - makeOrthographic: function ( left, right, top, bottom, near, far ) { - - var te = this.elements; - var w = 1.0 / ( right - left ); - var h = 1.0 / ( top - bottom ); - var p = 1.0 / ( far - near ); - - var x = ( right + left ) * w; - var y = ( top + bottom ) * h; - var z = ( far + near ) * p; - - te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; - te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; - te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z; - te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; - - return this; - - }, - - equals: function ( matrix ) { - - var te = this.elements; - var me = matrix.elements; - - for ( var i = 0; i < 16; i ++ ) { - - if ( te[ i ] !== me[ i ] ) return false; - - } - - return true; - - }, - - fromArray: function ( array, offset ) { - - if ( offset === undefined ) offset = 0; - - for( var i = 0; i < 16; i ++ ) { - - this.elements[ i ] = array[ i + offset ]; - - } - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - var te = this.elements; - - array[ offset ] = te[ 0 ]; - array[ offset + 1 ] = te[ 1 ]; - array[ offset + 2 ] = te[ 2 ]; - array[ offset + 3 ] = te[ 3 ]; - - array[ offset + 4 ] = te[ 4 ]; - array[ offset + 5 ] = te[ 5 ]; - array[ offset + 6 ] = te[ 6 ]; - array[ offset + 7 ] = te[ 7 ]; - - array[ offset + 8 ] = te[ 8 ]; - array[ offset + 9 ] = te[ 9 ]; - array[ offset + 10 ] = te[ 10 ]; - array[ offset + 11 ] = te[ 11 ]; - - array[ offset + 12 ] = te[ 12 ]; - array[ offset + 13 ] = te[ 13 ]; - array[ offset + 14 ] = te[ 14 ]; - array[ offset + 15 ] = te[ 15 ]; - - return array; - - } - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { - - images = images !== undefined ? images : []; - mapping = mapping !== undefined ? mapping : CubeReflectionMapping; - - Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - - this.flipY = false; - - } - - CubeTexture.prototype = Object.create( Texture.prototype ); - CubeTexture.prototype.constructor = CubeTexture; - - CubeTexture.prototype.isCubeTexture = true; - - Object.defineProperty( CubeTexture.prototype, 'images', { - - get: function () { - - return this.image; - - }, - - set: function ( value ) { - - this.image = value; - - } - - } ); - - /** - * @author tschw - * - * Uniforms of a program. - * Those form a tree structure with a special top-level container for the root, - * which you get by calling 'new WebGLUniforms( gl, program, renderer )'. - * - * - * Properties of inner nodes including the top-level container: - * - * .seq - array of nested uniforms - * .map - nested uniforms by name - * - * - * Methods of all nodes except the top-level container: - * - * .setValue( gl, value, [renderer] ) - * - * uploads a uniform value(s) - * the 'renderer' parameter is needed for sampler uniforms - * - * - * Static methods of the top-level container (renderer factorizations): - * - * .upload( gl, seq, values, renderer ) - * - * sets uniforms in 'seq' to 'values[id].value' - * - * .seqWithValue( seq, values ) : filteredSeq - * - * filters 'seq' entries with corresponding entry in values - * - * - * Methods of the top-level container (renderer factorizations): - * - * .setValue( gl, name, value ) - * - * sets uniform with name 'name' to 'value' - * - * .set( gl, obj, prop ) - * - * sets uniform from object and property with same name than uniform - * - * .setOptional( gl, obj, prop ) - * - * like .set for an optional property of the object - * - */ - - var emptyTexture = new Texture(); - var emptyCubeTexture = new CubeTexture(); - - // --- Base for inner nodes (including the root) --- - - function UniformContainer() { - - this.seq = []; - this.map = {}; - - } - - // --- Utilities --- - - // Array Caches (provide typed arrays for temporary by size) - - var arrayCacheF32 = []; - var arrayCacheI32 = []; - - // Flattening for arrays of vectors and matrices - - function flatten( array, nBlocks, blockSize ) { - - var firstElem = array[ 0 ]; - - if ( firstElem <= 0 || firstElem > 0 ) return array; - // unoptimized: ! isNaN( firstElem ) - // see http://jacksondunstan.com/articles/983 - - var n = nBlocks * blockSize, - r = arrayCacheF32[ n ]; - - if ( r === undefined ) { - - r = new Float32Array( n ); - arrayCacheF32[ n ] = r; - - } - - if ( nBlocks !== 0 ) { - - firstElem.toArray( r, 0 ); - - for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) { - - offset += blockSize; - array[ i ].toArray( r, offset ); - - } - - } - - return r; - - } - - // Texture unit allocation - - function allocTexUnits( renderer, n ) { - - var r = arrayCacheI32[ n ]; - - if ( r === undefined ) { - - r = new Int32Array( n ); - arrayCacheI32[ n ] = r; - - } - - for ( var i = 0; i !== n; ++ i ) - r[ i ] = renderer.allocTextureUnit(); - - return r; - - } - - // --- Setters --- - - // Note: Defining these methods externally, because they come in a bunch - // and this way their names minify. - - // Single scalar - - function setValue1f( gl, v ) { gl.uniform1f( this.addr, v ); } - function setValue1i( gl, v ) { gl.uniform1i( this.addr, v ); } - - // Single float vector (from flat array or THREE.VectorN) - - function setValue2fv( gl, v ) { - - if ( v.x === undefined ) gl.uniform2fv( this.addr, v ); - else gl.uniform2f( this.addr, v.x, v.y ); - - } - - function setValue3fv( gl, v ) { - - if ( v.x !== undefined ) - gl.uniform3f( this.addr, v.x, v.y, v.z ); - else if ( v.r !== undefined ) - gl.uniform3f( this.addr, v.r, v.g, v.b ); - else - gl.uniform3fv( this.addr, v ); - - } - - function setValue4fv( gl, v ) { - - if ( v.x === undefined ) gl.uniform4fv( this.addr, v ); - else gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); - - } - - // Single matrix (from flat array or MatrixN) - - function setValue2fm( gl, v ) { - - gl.uniformMatrix2fv( this.addr, false, v.elements || v ); - - } - - function setValue3fm( gl, v ) { - - gl.uniformMatrix3fv( this.addr, false, v.elements || v ); - - } - - function setValue4fm( gl, v ) { - - gl.uniformMatrix4fv( this.addr, false, v.elements || v ); - - } - - // Single texture (2D / Cube) - - function setValueT1( gl, v, renderer ) { - - var unit = renderer.allocTextureUnit(); - gl.uniform1i( this.addr, unit ); - renderer.setTexture2D( v || emptyTexture, unit ); - - } - - function setValueT6( gl, v, renderer ) { - - var unit = renderer.allocTextureUnit(); - gl.uniform1i( this.addr, unit ); - renderer.setTextureCube( v || emptyCubeTexture, unit ); - - } - - // Integer / Boolean vectors or arrays thereof (always flat arrays) - - function setValue2iv( gl, v ) { gl.uniform2iv( this.addr, v ); } - function setValue3iv( gl, v ) { gl.uniform3iv( this.addr, v ); } - function setValue4iv( gl, v ) { gl.uniform4iv( this.addr, v ); } - - // Helper to pick the right setter for the singular case - - function getSingularSetter( type ) { - - switch ( type ) { - - case 0x1406: return setValue1f; // FLOAT - case 0x8b50: return setValue2fv; // _VEC2 - case 0x8b51: return setValue3fv; // _VEC3 - case 0x8b52: return setValue4fv; // _VEC4 - - case 0x8b5a: return setValue2fm; // _MAT2 - case 0x8b5b: return setValue3fm; // _MAT3 - case 0x8b5c: return setValue4fm; // _MAT4 - - case 0x8b5e: return setValueT1; // SAMPLER_2D - case 0x8b60: return setValueT6; // SAMPLER_CUBE - - case 0x1404: case 0x8b56: return setValue1i; // INT, BOOL - case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2 - case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3 - case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4 - - } - - } - - // Array of scalars - - function setValue1fv( gl, v ) { gl.uniform1fv( this.addr, v ); } - function setValue1iv( gl, v ) { gl.uniform1iv( this.addr, v ); } - - // Array of vectors (flat or from THREE classes) - - function setValueV2a( gl, v ) { - - gl.uniform2fv( this.addr, flatten( v, this.size, 2 ) ); - - } - - function setValueV3a( gl, v ) { - - gl.uniform3fv( this.addr, flatten( v, this.size, 3 ) ); - - } - - function setValueV4a( gl, v ) { - - gl.uniform4fv( this.addr, flatten( v, this.size, 4 ) ); - - } - - // Array of matrices (flat or from THREE clases) - - function setValueM2a( gl, v ) { - - gl.uniformMatrix2fv( this.addr, false, flatten( v, this.size, 4 ) ); - - } - - function setValueM3a( gl, v ) { - - gl.uniformMatrix3fv( this.addr, false, flatten( v, this.size, 9 ) ); - - } - - function setValueM4a( gl, v ) { - - gl.uniformMatrix4fv( this.addr, false, flatten( v, this.size, 16 ) ); - - } - - // Array of textures (2D / Cube) - - function setValueT1a( gl, v, renderer ) { - - var n = v.length, - units = allocTexUnits( renderer, n ); - - gl.uniform1iv( this.addr, units ); - - for ( var i = 0; i !== n; ++ i ) { - - renderer.setTexture2D( v[ i ] || emptyTexture, units[ i ] ); - - } - - } - - function setValueT6a( gl, v, renderer ) { - - var n = v.length, - units = allocTexUnits( renderer, n ); - - gl.uniform1iv( this.addr, units ); - - for ( var i = 0; i !== n; ++ i ) { - - renderer.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); - - } - - } - - // Helper to pick the right setter for a pure (bottom-level) array - - function getPureArraySetter( type ) { - - switch ( type ) { - - case 0x1406: return setValue1fv; // FLOAT - case 0x8b50: return setValueV2a; // _VEC2 - case 0x8b51: return setValueV3a; // _VEC3 - case 0x8b52: return setValueV4a; // _VEC4 - - case 0x8b5a: return setValueM2a; // _MAT2 - case 0x8b5b: return setValueM3a; // _MAT3 - case 0x8b5c: return setValueM4a; // _MAT4 - - case 0x8b5e: return setValueT1a; // SAMPLER_2D - case 0x8b60: return setValueT6a; // SAMPLER_CUBE - - case 0x1404: case 0x8b56: return setValue1iv; // INT, BOOL - case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2 - case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3 - case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4 - - } - - } - - // --- Uniform Classes --- - - function SingleUniform( id, activeInfo, addr ) { - - this.id = id; - this.addr = addr; - this.setValue = getSingularSetter( activeInfo.type ); - - // this.path = activeInfo.name; // DEBUG - - } - - function PureArrayUniform( id, activeInfo, addr ) { - - this.id = id; - this.addr = addr; - this.size = activeInfo.size; - this.setValue = getPureArraySetter( activeInfo.type ); - - // this.path = activeInfo.name; // DEBUG - - } - - function StructuredUniform( id ) { - - this.id = id; - - UniformContainer.call( this ); // mix-in - - } - - StructuredUniform.prototype.setValue = function( gl, value ) { - - // Note: Don't need an extra 'renderer' parameter, since samplers - // are not allowed in structured uniforms. - - var seq = this.seq; - - for ( var i = 0, n = seq.length; i !== n; ++ i ) { - - var u = seq[ i ]; - u.setValue( gl, value[ u.id ] ); - - } - - }; - - // --- Top-level --- - - // Parser - builds up the property tree from the path strings - - var RePathPart = /([\w\d_]+)(\])?(\[|\.)?/g; - - // extracts - // - the identifier (member name or array index) - // - followed by an optional right bracket (found when array index) - // - followed by an optional left bracket or dot (type of subscript) - // - // Note: These portions can be read in a non-overlapping fashion and - // allow straightforward parsing of the hierarchy that WebGL encodes - // in the uniform names. - - function addUniform( container, uniformObject ) { - - container.seq.push( uniformObject ); - container.map[ uniformObject.id ] = uniformObject; - - } - - function parseUniform( activeInfo, addr, container ) { - - var path = activeInfo.name, - pathLength = path.length; - - // reset RegExp object, because of the early exit of a previous run - RePathPart.lastIndex = 0; - - for (; ;) { - - var match = RePathPart.exec( path ), - matchEnd = RePathPart.lastIndex, - - id = match[ 1 ], - idIsIndex = match[ 2 ] === ']', - subscript = match[ 3 ]; - - if ( idIsIndex ) id = id | 0; // convert to integer - - if ( subscript === undefined || - subscript === '[' && matchEnd + 2 === pathLength ) { - // bare name or "pure" bottom-level array "[0]" suffix - - addUniform( container, subscript === undefined ? - new SingleUniform( id, activeInfo, addr ) : - new PureArrayUniform( id, activeInfo, addr ) ); - - break; - - } else { - // step into inner node / create it in case it doesn't exist - - var map = container.map, - next = map[ id ]; - - if ( next === undefined ) { - - next = new StructuredUniform( id ); - addUniform( container, next ); - - } - - container = next; - - } - - } - - } - - // Root Container - - function WebGLUniforms( gl, program, renderer ) { - - UniformContainer.call( this ); - - this.renderer = renderer; - - var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); - - for ( var i = 0; i !== n; ++ i ) { - - var info = gl.getActiveUniform( program, i ), - path = info.name, - addr = gl.getUniformLocation( program, path ); - - parseUniform( info, addr, this ); - - } - - } - - WebGLUniforms.prototype.setValue = function( gl, name, value ) { - - var u = this.map[ name ]; - - if ( u !== undefined ) u.setValue( gl, value, this.renderer ); - - }; - - WebGLUniforms.prototype.set = function( gl, object, name ) { - - var u = this.map[ name ]; - - if ( u !== undefined ) u.setValue( gl, object[ name ], this.renderer ); - - }; - - WebGLUniforms.prototype.setOptional = function( gl, object, name ) { - - var v = object[ name ]; - - if ( v !== undefined ) this.setValue( gl, name, v ); - - }; - - - // Static interface - - WebGLUniforms.upload = function( gl, seq, values, renderer ) { - - for ( var i = 0, n = seq.length; i !== n; ++ i ) { - - var u = seq[ i ], - v = values[ u.id ]; - - if ( v.needsUpdate !== false ) { - // note: always updating when .needsUpdate is undefined - - u.setValue( gl, v.value, renderer ); - - } - - } - - }; - - WebGLUniforms.seqWithValue = function( seq, values ) { - - var r = []; - - for ( var i = 0, n = seq.length; i !== n; ++ i ) { - - var u = seq[ i ]; - if ( u.id in values ) r.push( u ); - - } - - return r; - - }; - - /** - * Uniform Utilities - */ - - var UniformsUtils = { - - merge: function ( uniforms ) { - - var merged = {}; - - for ( var u = 0; u < uniforms.length; u ++ ) { - - var tmp = this.clone( uniforms[ u ] ); - - for ( var p in tmp ) { - - merged[ p ] = tmp[ p ]; - - } - - } - - return merged; - - }, - - clone: function ( uniforms_src ) { - - var uniforms_dst = {}; - - for ( var u in uniforms_src ) { - - uniforms_dst[ u ] = {}; - - for ( var p in uniforms_src[ u ] ) { - - var parameter_src = uniforms_src[ u ][ p ]; - - if ( parameter_src && ( parameter_src.isColor || - parameter_src.isMatrix3 || parameter_src.isMatrix4 || - parameter_src.isVector2 || parameter_src.isVector3 || parameter_src.isVector4 || - parameter_src.isTexture ) ) { - - uniforms_dst[ u ][ p ] = parameter_src.clone(); - - } else if ( Array.isArray( parameter_src ) ) { - - uniforms_dst[ u ][ p ] = parameter_src.slice(); - - } else { - - uniforms_dst[ u ][ p ] = parameter_src; - - } - - } - - } - - return uniforms_dst; - - } - - }; - - var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif\n"; - - var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif\n"; - - var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif\n"; - - var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif\n"; - - var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif"; - - var begin_vertex = "\nvec3 transformed = vec3( position );\n"; - - var beginnormal_vertex = "\nvec3 objectNormal = vec3( normal );\n"; - - var bsdfs = "float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n\t\tif( decayExponent > 0.0 ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\t\t\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\t\t\tfloat maxDistanceCutoffFactor = pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t\t\treturn distanceFalloff * maxDistanceCutoffFactor;\n#else\n\t\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n#endif\n\t\t}\n\t\treturn 1.0;\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 ltcTextureCoords( const in GeometricContext geometry, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = (LUT_SIZE - 1.0)/LUT_SIZE;\n\tconst float LUT_BIAS = 0.5/LUT_SIZE;\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 P = geometry.position;\n\tfloat theta = acos( dot( N, V ) );\n\tvec2 uv = vec2(\n\t\tsqrt( saturate( roughness ) ),\n\t\tsaturate( theta / ( 0.5 * PI ) ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nvoid clipQuadToHorizon( inout vec3 L[5], out int n ) {\n\tint config = 0;\n\tif ( L[0].z > 0.0 ) config += 1;\n\tif ( L[1].z > 0.0 ) config += 2;\n\tif ( L[2].z > 0.0 ) config += 4;\n\tif ( L[3].z > 0.0 ) config += 8;\n\tn = 0;\n\tif ( config == 0 ) {\n\t} else if ( config == 1 ) {\n\t\tn = 3;\n\t\tL[1] = -L[1].z * L[0] + L[0].z * L[1];\n\t\tL[2] = -L[3].z * L[0] + L[0].z * L[3];\n\t} else if ( config == 2 ) {\n\t\tn = 3;\n\t\tL[0] = -L[0].z * L[1] + L[1].z * L[0];\n\t\tL[2] = -L[2].z * L[1] + L[1].z * L[2];\n\t} else if ( config == 3 ) {\n\t\tn = 4;\n\t\tL[2] = -L[2].z * L[1] + L[1].z * L[2];\n\t\tL[3] = -L[3].z * L[0] + L[0].z * L[3];\n\t} else if ( config == 4 ) {\n\t\tn = 3;\n\t\tL[0] = -L[3].z * L[2] + L[2].z * L[3];\n\t\tL[1] = -L[1].z * L[2] + L[2].z * L[1];\n\t} else if ( config == 5 ) {\n\t\tn = 0;\n\t} else if ( config == 6 ) {\n\t\tn = 4;\n\t\tL[0] = -L[0].z * L[1] + L[1].z * L[0];\n\t\tL[3] = -L[3].z * L[2] + L[2].z * L[3];\n\t} else if ( config == 7 ) {\n\t\tn = 5;\n\t\tL[4] = -L[3].z * L[0] + L[0].z * L[3];\n\t\tL[3] = -L[3].z * L[2] + L[2].z * L[3];\n\t} else if ( config == 8 ) {\n\t\tn = 3;\n\t\tL[0] = -L[0].z * L[3] + L[3].z * L[0];\n\t\tL[1] = -L[2].z * L[3] + L[3].z * L[2];\n\t\tL[2] = L[3];\n\t} else if ( config == 9 ) {\n\t\tn = 4;\n\t\tL[1] = -L[1].z * L[0] + L[0].z * L[1];\n\t\tL[2] = -L[2].z * L[3] + L[3].z * L[2];\n\t} else if ( config == 10 ) {\n\t\tn = 0;\n\t} else if ( config == 11 ) {\n\t\tn = 5;\n\t\tL[4] = L[3];\n\t\tL[3] = -L[2].z * L[3] + L[3].z * L[2];\n\t\tL[2] = -L[2].z * L[1] + L[1].z * L[2];\n\t} else if ( config == 12 ) {\n\t\tn = 4;\n\t\tL[1] = -L[1].z * L[2] + L[2].z * L[1];\n\t\tL[0] = -L[0].z * L[3] + L[3].z * L[0];\n\t} else if ( config == 13 ) {\n\t\tn = 5;\n\t\tL[4] = L[3];\n\t\tL[3] = L[2];\n\t\tL[2] = -L[1].z * L[2] + L[2].z * L[1];\n\t\tL[1] = -L[1].z * L[0] + L[0].z * L[1];\n\t} else if ( config == 14 ) {\n\t\tn = 5;\n\t\tL[4] = -L[0].z * L[3] + L[3].z * L[0];\n\t\tL[0] = -L[0].z * L[1] + L[1].z * L[0];\n\t} else if ( config == 15 ) {\n\t\tn = 4;\n\t}\n\tif ( n == 3 )\n\t\tL[3] = L[0];\n\tif ( n == 4 )\n\t\tL[4] = L[0];\n}\nfloat integrateLtcBrdfOverRectEdge( vec3 v1, vec3 v2 ) {\n\tfloat cosTheta = dot( v1, v2 );\n\tfloat theta = acos( cosTheta );\n\tfloat res = cross( v1, v2 ).z * ( ( theta > 0.001 ) ? theta / sin( theta ) : 1.0 );\n\treturn res;\n}\nvoid initRectPoints( const in vec3 pos, const in vec3 halfWidth, const in vec3 halfHeight, out vec3 rectPoints[4] ) {\n\trectPoints[0] = pos - halfWidth - halfHeight;\n\trectPoints[1] = pos + halfWidth - halfHeight;\n\trectPoints[2] = pos + halfWidth + halfHeight;\n\trectPoints[3] = pos - halfWidth + halfHeight;\n}\nvec3 integrateLtcBrdfOverRect( const in GeometricContext geometry, const in mat3 brdfMat, const in vec3 rectPoints[4] ) {\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 P = geometry.position;\n\tvec3 T1, T2;\n\tT1 = normalize(V - N * dot( V, N ));\n\tT2 = - cross( N, T1 );\n\tmat3 brdfWrtSurface = brdfMat * transpose( mat3( T1, T2, N ) );\n\tvec3 clippedRect[5];\n\tclippedRect[0] = brdfWrtSurface * ( rectPoints[0] - P );\n\tclippedRect[1] = brdfWrtSurface * ( rectPoints[1] - P );\n\tclippedRect[2] = brdfWrtSurface * ( rectPoints[2] - P );\n\tclippedRect[3] = brdfWrtSurface * ( rectPoints[3] - P );\n\tint n;\n\tclipQuadToHorizon(clippedRect, n);\n\tif ( n == 0 )\n\t\treturn vec3( 0, 0, 0 );\n\tclippedRect[0] = normalize( clippedRect[0] );\n\tclippedRect[1] = normalize( clippedRect[1] );\n\tclippedRect[2] = normalize( clippedRect[2] );\n\tclippedRect[3] = normalize( clippedRect[3] );\n\tclippedRect[4] = normalize( clippedRect[4] );\n\tfloat sum = 0.0;\n\tsum += integrateLtcBrdfOverRectEdge( clippedRect[0], clippedRect[1] );\n\tsum += integrateLtcBrdfOverRectEdge( clippedRect[1], clippedRect[2] );\n\tsum += integrateLtcBrdfOverRectEdge( clippedRect[2], clippedRect[3] );\n\tif (n >= 4)\n\t\tsum += integrateLtcBrdfOverRectEdge( clippedRect[3], clippedRect[4] );\n\tif (n == 5)\n\t\tsum += integrateLtcBrdfOverRectEdge( clippedRect[4], clippedRect[0] );\n\tsum = max( 0.0, sum );\n\tvec3 Lo_i = vec3( sum, sum, sum );\n\treturn Lo_i;\n}\nvec3 Rect_Area_Light_Specular_Reflectance(\n\t\tconst in GeometricContext geometry,\n\t\tconst in vec3 lightPos, const in vec3 lightHalfWidth, const in vec3 lightHalfHeight,\n\t\tconst in float roughness,\n\t\tconst in sampler2D ltcMat, const in sampler2D ltcMag ) {\n\tvec3 rectPoints[4];\n\tinitRectPoints( lightPos, lightHalfWidth, lightHalfHeight, rectPoints );\n\tvec2 uv = ltcTextureCoords( geometry, roughness );\n\tvec4 brdfLtcApproxParams, t;\n\tbrdfLtcApproxParams = texture2D( ltcMat, uv );\n\tt = texture2D( ltcMat, uv );\n\tfloat brdfLtcScalar = texture2D( ltcMag, uv ).a;\n\tmat3 brdfLtcApproxMat = mat3(\n\t\tvec3( 1, 0, t.y ),\n\t\tvec3( 0, t.z, 0 ),\n\t\tvec3( t.w, 0, t.x )\n\t);\n\tvec3 specularReflectance = integrateLtcBrdfOverRect( geometry, brdfLtcApproxMat, rectPoints );\n\tspecularReflectance *= brdfLtcScalar;\n\treturn specularReflectance;\n}\nvec3 Rect_Area_Light_Diffuse_Reflectance(\n\t\tconst in GeometricContext geometry,\n\t\tconst in vec3 lightPos, const in vec3 lightHalfWidth, const in vec3 lightHalfHeight ) {\n\tvec3 rectPoints[4];\n\tinitRectPoints( lightPos, lightHalfWidth, lightHalfHeight, rectPoints );\n\tmat3 diffuseBrdfMat = mat3(1);\n\tvec3 diffuseReflectance = integrateLtcBrdfOverRect( geometry, diffuseBrdfMat, rectPoints );\n\treturn diffuseReflectance;\n}\nvec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\tvec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;\n\treturn specularColor * AB.x + AB.y;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n"; - - var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = dFdx( surf_pos );\n\t\tvec3 vSigmaY = dFdy( surf_pos );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif\n"; - - var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; ++ i ) {\n\t\tvec4 plane = clippingPlanes[ i ];\n\t\tif ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t\t\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; ++ i ) {\n\t\t\tvec4 plane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\tif ( clipped ) discard;\n\t\n\t#endif\n#endif\n"; - - var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\t#if ! defined( PHYSICAL ) && ! defined( PHONG )\n\t\tvarying vec3 vViewPosition;\n\t#endif\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif\n"; - - var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvarying vec3 vViewPosition;\n#endif\n"; - - var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n"; - - var color_fragment = "#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif"; - - var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif\n"; - - var color_pars_vertex = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif"; - - var color_vertex = "#ifdef USE_COLOR\n\tvColor.xyz = color.xyz;\n#endif"; - - var common = "#define PI 3.14159265359\n#define PI2 6.28318530718\n#define PI_HALF 1.5707963267949\n#define RECIPROCAL_PI 0.31830988618\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transpose( const in mat3 v ) {\n\tmat3 tmp;\n\ttmp[0] = vec3(v[0].x, v[1].x, v[2].x);\n\ttmp[1] = vec3(v[0].y, v[1].y, v[2].y);\n\ttmp[2] = vec3(v[0].z, v[1].z, v[2].z);\n\treturn tmp;\n}\n"; - - var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n#define cubeUV_textureSize (1024.0)\nint getFaceFromDirection(vec3 direction) {\n\tvec3 absDirection = abs(direction);\n\tint face = -1;\n\tif( absDirection.x > absDirection.z ) {\n\t\tif(absDirection.x > absDirection.y )\n\t\t\tface = direction.x > 0.0 ? 0 : 3;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\telse {\n\t\tif(absDirection.z > absDirection.y )\n\t\t\tface = direction.z > 0.0 ? 2 : 5;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\treturn face;\n}\n#define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0)\n#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))\nvec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {\n\tfloat scale = exp2(cubeUV_maxLods1 - roughnessLevel);\n\tfloat dxRoughness = dFdx(roughness);\n\tfloat dyRoughness = dFdy(roughness);\n\tvec3 dx = dFdx( vec * scale * dxRoughness );\n\tvec3 dy = dFdy( vec * scale * dyRoughness );\n\tfloat d = max( dot( dx, dx ), dot( dy, dy ) );\n\td = clamp(d, 1.0, cubeUV_rangeClamp);\n\tfloat mipLevel = 0.5 * log2(d);\n\treturn vec2(floor(mipLevel), fract(mipLevel));\n}\n#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)\n#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)\nvec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {\n\tmipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;\n\tfloat a = 16.0 * cubeUV_rcpTextureSize;\n\tvec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );\n\tvec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;\n\tfloat powScale = exp2_packed.x * exp2_packed.y;\n\tfloat scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;\n\tfloat mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;\n\tbool bRes = mipLevel == 0.0;\n\tscale = bRes && (scale < a) ? a : scale;\n\tvec3 r;\n\tvec2 offset;\n\tint face = getFaceFromDirection(direction);\n\tfloat rcpPowScale = 1.0 / powScale;\n\tif( face == 0) {\n\t\tr = vec3(direction.x, -direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 1) {\n\t\tr = vec3(direction.y, direction.x, direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 2) {\n\t\tr = vec3(direction.z, direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 3) {\n\t\tr = vec3(direction.x, direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse if( face == 4) {\n\t\tr = vec3(direction.y, direction.x, -direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse {\n\t\tr = vec3(direction.z, -direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\tr = normalize(r);\n\tfloat texelOffset = 0.5 * cubeUV_rcpTextureSize;\n\tvec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;\n\tvec2 base = offset + vec2( texelOffset );\n\treturn base + s * ( scale - 2.0 * texelOffset );\n}\n#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)\nvec4 textureCubeUV(vec3 reflectedDirection, float roughness ) {\n\tfloat roughnessVal = roughness* cubeUV_maxLods3;\n\tfloat r1 = floor(roughnessVal);\n\tfloat r2 = r1 + 1.0;\n\tfloat t = fract(roughnessVal);\n\tvec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);\n\tfloat s = mipInfo.y;\n\tfloat level0 = mipInfo.x;\n\tfloat level1 = level0 + 1.0;\n\tlevel1 = level1 > 5.0 ? 5.0 : level1;\n\tlevel0 += min( floor( s + 0.5 ), 5.0 );\n\tvec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);\n\tvec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));\n\tvec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);\n\tvec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));\n\tvec4 result = mix(color10, color20, t);\n\treturn vec4(result.rgb, 1.0);\n}\n#endif\n"; - - var defaultnormal_vertex = "#ifdef FLIP_SIDED\n\tobjectNormal = -objectNormal;\n#endif\nvec3 transformedNormal = normalMatrix * objectNormal;\n"; - - var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif\n"; - - var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n#endif\n"; - - var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif\n"; - - var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif\n"; - - var encodings_fragment = " gl_FragColor = linearToOutputTexel( gl_FragColor );\n"; - - var encodings_pars_fragment = "\nvec4 LinearToLinear( in vec4 value ) {\n return value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n return vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.w );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.w );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n return vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n float maxComponent = max( max( value.r, value.g ), value.b );\n float fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n return vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n return vec4( value.xyz * value.w * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n float maxRGB = max( value.x, max( value.g, value.b ) );\n float M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n M = ceil( M * 255.0 ) / 255.0;\n return vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n return vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n float maxRGB = max( value.x, max( value.g, value.b ) );\n float D = max( maxRange / maxRGB, 1.0 );\n D = min( floor( D ) / 255.0, 1.0 );\n return vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n vec3 Xp_Y_XYZp = value.rgb * cLogLuvM;\n Xp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));\n vec4 vResult;\n vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n float Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n vResult.w = fract(Le);\n vResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0;\n return vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n float Le = value.z * 255.0 + value.w;\n vec3 Xp_Y_XYZp;\n Xp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);\n Xp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n Xp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n vec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM;\n return vec4( max(vRGB, 0.0), 1.0 );\n}\n"; - - var envmap_fragment = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\tvec2 sampleUV;\n\t\tsampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n\t\tsampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\tvec4 envColor = texture2D( envMap, sampleUV );\n\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\tvec3 reflectView = flipNormal * normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) );\n\t\tvec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\tenvColor = envMapTexelToLinear( envColor );\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif\n"; - - var envmap_pars_fragment = "#if defined( USE_ENVMAP ) || defined( PHYSICAL )\n\tuniform float reflectivity;\n\tuniform float envMapIntensity;\n#endif\n#ifdef USE_ENVMAP\n\t#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) )\n\t\tvarying vec3 vWorldPosition;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\tuniform float flipEnvMap;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif\n"; - - var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif\n"; - - var envmap_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif\n"; - - var fog_fragment = "#ifdef USE_FOG\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tfloat depth = gl_FragDepthEXT / gl_FragCoord.w;\n\t#else\n\t\tfloat depth = gl_FragCoord.z / gl_FragCoord.w;\n\t#endif\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, depth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif\n"; - - var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif"; - - var gradientmap_pars_fragment = "#ifdef TOON\n\tuniform sampler2D gradientMap;\n\tvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\t\tfloat dotNL = dot( normal, lightDirection );\n\t\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t\t#ifdef USE_GRADIENTMAP\n\t\t\treturn texture2D( gradientMap, coord ).rgb;\n\t\t#else\n\t\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t\t#endif\n\t}\n#endif\n"; - - var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\treflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n#endif\n"; - - var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif"; - - var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\n#if NUM_POINT_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t}\n#endif\n#if NUM_DIR_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n#endif\n"; - - var lights_pars = "uniform vec3 ambientLightColor;\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltcMat;\tuniform sampler2D ltcMag;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif\n#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\tvec4 envMapColor = textureCubeUV( queryVec, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar - 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\tvec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent));\n\t\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\t\tvec2 sampleUV;\n\t\t\tsampleUV.y = saturate( reflectVec.y * 0.5 + 0.5 );\n\t\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif\n"; - - var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;\n"; - - var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3\tdiffuseColor;\n\tvec3\tspecularColor;\n\tfloat\tspecularShininess;\n\tfloat\tspecularStrength;\n};\n#if NUM_RECT_AREA_LIGHTS > 0\n void RE_Direct_RectArea_BlinnPhong( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n vec3 matDiffColor = material.diffuseColor;\n vec3 matSpecColor = material.specularColor;\n vec3 lightColor = rectAreaLight.color;\n float roughness = BlinnExponentToGGXRoughness( material.specularShininess );\n vec3 spec = Rect_Area_Light_Specular_Reflectance(\n geometry,\n rectAreaLight.position, rectAreaLight.halfWidth, rectAreaLight.halfHeight,\n roughness,\n ltcMat, ltcMag );\n vec3 diff = Rect_Area_Light_Diffuse_Reflectance(\n geometry,\n rectAreaLight.position, rectAreaLight.halfWidth, rectAreaLight.halfHeight );\n reflectedLight.directSpecular += lightColor * matSpecColor * spec / PI2;\n reflectedLight.directDiffuse += lightColor * matDiffColor * diff / PI2;\n }\n#endif\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifdef TOON\n\t\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#else\n\t\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\t\tvec3 irradiance = dotNL * directLight.color;\n\t#endif\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)\n"; - - var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\n#ifdef STANDARD\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.clearCoat = saturate( clearCoat );\tmaterial.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 );\n#endif\n"; - - var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3\tdiffuseColor;\n\tfloat\tspecularRoughness;\n\tvec3\tspecularColor;\n\t#ifndef STANDARD\n\t\tfloat clearCoat;\n\t\tfloat clearCoatRoughness;\n\t#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearCoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n vec3 matDiffColor = material.diffuseColor;\n vec3 matSpecColor = material.specularColor;\n vec3 lightColor = rectAreaLight.color;\n float roughness = material.specularRoughness;\n vec3 spec = Rect_Area_Light_Specular_Reflectance(\n geometry,\n rectAreaLight.position, rectAreaLight.halfWidth, rectAreaLight.halfHeight,\n roughness,\n ltcMat, ltcMag );\n vec3 diff = Rect_Area_Light_Diffuse_Reflectance(\n geometry,\n rectAreaLight.position, rectAreaLight.halfWidth, rectAreaLight.halfHeight );\n reflectedLight.directSpecular += lightColor * matSpecColor * spec;\n reflectedLight.directDiffuse += lightColor * matDiffColor * diff;\n }\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifndef STANDARD\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );\n\treflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\t#ifndef STANDARD\n\t\treflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifndef STANDARD\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\tfloat dotNL = dotNV;\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.indirectSpecular += ( 1.0 - clearCoatDHR ) * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );\n\t#ifndef STANDARD\n\t\treflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\n#define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness )\n#define Material_ClearCoat_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.clearCoatRoughness )\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}\n"; - - var lights_template = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = normalize( vViewPosition );\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\t#ifdef USE_LIGHTMAP\n\t\tvec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t \tirradiance += getLightProbeIndirectIrradiance( geometry, 8 );\n\t#endif\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tvec3 radiance = getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), 8 );\n\t#ifndef STANDARD\n\t\tvec3 clearCoatRadiance = getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), 8 );\n\t#else\n\t\tvec3 clearCoatRadiance = vec3( 0.0 );\n\t#endif\n\t\t\n\tRE_IndirectSpecular( radiance, clearCoatRadiance, geometry, material, reflectedLight );\n#endif\n"; - - var logdepthbuf_fragment = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\tgl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n#endif"; - - var logdepthbuf_pars_fragment = "#ifdef USE_LOGDEPTHBUF\n\tuniform float logDepthBufFC;\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#endif\n#endif\n"; - - var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#endif\n\tuniform float logDepthBufFC;\n#endif"; - - var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\tgl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t#else\n\t\tgl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\t#endif\n#endif\n"; - - var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif\n"; - - var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n"; - - var map_particle_fragment = "#ifdef USE_MAP\n\tvec4 mapTexel = texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n"; - - var map_particle_pars_fragment = "#ifdef USE_MAP\n\tuniform vec4 offsetRepeat;\n\tuniform sampler2D map;\n#endif\n"; - - var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.r;\n#endif\n"; - - var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif"; - - var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n\tobjectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n\tobjectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n\tobjectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n#endif\n"; - - var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\t#ifndef USE_MORPHNORMALS\n\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif"; - - var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n\ttransformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n\ttransformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n\ttransformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\ttransformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n\ttransformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n\ttransformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n\ttransformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\t#endif\n#endif\n"; - - var normal_flip = "#ifdef DOUBLE_SIDED\n\tfloat flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n#else\n\tfloat flipNormal = 1.0;\n#endif\n"; - - var normal_fragment = "#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal ) * flipNormal;\n#endif\n#ifdef USE_NORMALMAP\n\tnormal = perturbNormal2Arb( -vViewPosition, normal );\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif\n"; - - var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\t\tvec3 q0 = dFdx( eye_pos.xyz );\n\t\tvec3 q1 = dFdy( eye_pos.xyz );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tvec3 S = normalize( q0 * st1.t - q1 * st0.t );\n\t\tvec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n\t\tvec3 N = normalize( surf_norm );\n\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\tmapN.xy = normalScale * mapN.xy;\n\t\tmat3 tsn = mat3( S, T, N );\n\t\treturn normalize( tsn * mapN );\n\t}\n#endif\n"; - - var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n return normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n return 1.0 - 2.0 * rgb.xyz;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n return ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n return linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n return (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n return ( near * far ) / ( ( far - near ) * invClipZ - far );\n}\n"; - - var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif\n"; - - var project_vertex = "#ifdef USE_SKINNING\n\tvec4 mvPosition = modelViewMatrix * skinned;\n#else\n\tvec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n#endif\ngl_Position = projectionMatrix * mvPosition;\n"; - - var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.r;\n#endif\n"; - - var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif"; - - var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n #if NUM_RECT_AREA_LIGHTS > 0\n #endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tfloat texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\n\t\tconst vec2 offset = vec2( 0.0, 1.0 );\n\t\tvec2 texelSize = vec2( 1.0 ) / size;\n\t\tvec2 centroidUV = floor( uv * size + 0.5 ) / size;\n\t\tfloat lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\n\t\tfloat lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\n\t\tfloat rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\n\t\tfloat rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\n\t\tvec2 f = fract( uv * size + 0.5 );\n\t\tfloat a = mix( lb, lt, f.y );\n\t\tfloat b = mix( rb, rt, f.y );\n\t\tfloat c = mix( a, b, f.x );\n\t\treturn c;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\treturn (\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn 1.0;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\tfloat dp = ( length( lightToPosition ) - shadowBias ) / 1000.0;\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif\n"; - - var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n #if NUM_RECT_AREA_LIGHTS > 0\n #endif\n#endif\n"; - - var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n #if NUM_RECT_AREA_LIGHTS > 0\n #endif\n#endif\n"; - - var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\tDirectionalLight directionalLight;\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tshadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\tSpotLight spotLight;\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tshadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\tPointLight pointLight;\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tshadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_RECT_AREA_LIGHTS > 0\n\t#endif\n\t#endif\n\treturn shadow;\n}\n"; - - var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif"; - - var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform sampler2D boneTexture;\n\t\tuniform int boneTextureWidth;\n\t\tuniform int boneTextureHeight;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureWidth ) );\n\t\t\tfloat y = floor( j / float( boneTextureWidth ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureWidth );\n\t\t\tfloat dy = 1.0 / float( boneTextureHeight );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif\n"; - - var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\tskinned = bindMatrixInverse * skinned;\n#endif\n"; - - var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n#endif\n"; - - var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif"; - - var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif"; - - var tonemapping_fragment = "#if defined( TONE_MAPPING )\n gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif\n"; - - var tonemapping_pars_fragment = "#define saturate(a) clamp( a, 0.0, 1.0 )\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n return toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n color *= toneMappingExposure;\n return saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n color *= toneMappingExposure;\n return saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n color *= toneMappingExposure;\n color = max( vec3( 0.0 ), color - 0.004 );\n return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\n"; - - var uv_pars_fragment = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n#endif"; - - var uv_pars_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n\tuniform vec4 offsetRepeat;\n#endif\n"; - - var uv_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n#endif"; - - var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif"; - - var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n#endif"; - - var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = uv2;\n#endif"; - - var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( PHYSICAL ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\t#ifdef USE_SKINNING\n\t\tvec4 worldPosition = modelMatrix * skinned;\n\t#else\n\t\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n\t#endif\n#endif\n"; - - var cube_frag = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldPosition;\n#include \nvoid main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n\tgl_FragColor.a *= opacity;\n}\n"; - - var cube_vert = "varying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}\n"; - - var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( gl_FragCoord.z ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( gl_FragCoord.z );\n\t#endif\n}\n"; - - var depth_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; - - var distanceRGBA_frag = "uniform vec3 lightPos;\nvarying vec4 vWorldPosition;\n#include \n#include \n#include \nvoid main () {\n\t#include \n\tgl_FragColor = packDepthToRGBA( length( vWorldPosition.xyz - lightPos.xyz ) / 1000.0 );\n}\n"; - - var distanceRGBA_vert = "varying vec4 vWorldPosition;\n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition;\n}\n"; - - var equirect_frag = "uniform sampler2D tEquirect;\nuniform float tFlip;\nvarying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldPosition );\n\tvec2 sampleUV;\n\tsampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );\n\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n}\n"; - - var equirect_vert = "varying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}\n"; - - var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; - - var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvLineDistance = scale * lineDistance;\n\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n}\n"; - - var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\treflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; - - var meshbasic_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include \n\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; - - var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\treflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; - - var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; - - var meshphong_frag = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; - - var meshphong_vert = "#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}\n"; - - var meshphysical_frag = "#define PHYSICAL\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifndef STANDARD\n\tuniform float clearCoat;\n\tuniform float clearCoatRoughness;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; - - var meshphysical_vert = "#define PHYSICAL\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n}\n"; - - var normal_frag = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n\t#include \n\t#include \n}\n"; - - var normal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}\n"; - - var points_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; - - var points_vert = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#ifdef USE_SIZEATTENUATION\n\t\tgl_PointSize = size * ( scale / - mvPosition.z );\n\t#else\n\t\tgl_PointSize = size;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; - - var shadow_frag = "uniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( 0.0, 0.0, 0.0, opacity * ( 1.0 - getShadowMask() ) );\n}\n"; - - var shadow_vert = "#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; - - var ShaderChunk = { - alphamap_fragment: alphamap_fragment, - alphamap_pars_fragment: alphamap_pars_fragment, - alphatest_fragment: alphatest_fragment, - aomap_fragment: aomap_fragment, - aomap_pars_fragment: aomap_pars_fragment, - begin_vertex: begin_vertex, - beginnormal_vertex: beginnormal_vertex, - bsdfs: bsdfs, - bumpmap_pars_fragment: bumpmap_pars_fragment, - clipping_planes_fragment: clipping_planes_fragment, - clipping_planes_pars_fragment: clipping_planes_pars_fragment, - clipping_planes_pars_vertex: clipping_planes_pars_vertex, - clipping_planes_vertex: clipping_planes_vertex, - color_fragment: color_fragment, - color_pars_fragment: color_pars_fragment, - color_pars_vertex: color_pars_vertex, - color_vertex: color_vertex, - common: common, - cube_uv_reflection_fragment: cube_uv_reflection_fragment, - defaultnormal_vertex: defaultnormal_vertex, - displacementmap_pars_vertex: displacementmap_pars_vertex, - displacementmap_vertex: displacementmap_vertex, - emissivemap_fragment: emissivemap_fragment, - emissivemap_pars_fragment: emissivemap_pars_fragment, - encodings_fragment: encodings_fragment, - encodings_pars_fragment: encodings_pars_fragment, - envmap_fragment: envmap_fragment, - envmap_pars_fragment: envmap_pars_fragment, - envmap_pars_vertex: envmap_pars_vertex, - envmap_vertex: envmap_vertex, - fog_fragment: fog_fragment, - fog_pars_fragment: fog_pars_fragment, - gradientmap_pars_fragment: gradientmap_pars_fragment, - lightmap_fragment: lightmap_fragment, - lightmap_pars_fragment: lightmap_pars_fragment, - lights_lambert_vertex: lights_lambert_vertex, - lights_pars: lights_pars, - lights_phong_fragment: lights_phong_fragment, - lights_phong_pars_fragment: lights_phong_pars_fragment, - lights_physical_fragment: lights_physical_fragment, - lights_physical_pars_fragment: lights_physical_pars_fragment, - lights_template: lights_template, - logdepthbuf_fragment: logdepthbuf_fragment, - logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, - logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, - logdepthbuf_vertex: logdepthbuf_vertex, - map_fragment: map_fragment, - map_pars_fragment: map_pars_fragment, - map_particle_fragment: map_particle_fragment, - map_particle_pars_fragment: map_particle_pars_fragment, - metalnessmap_fragment: metalnessmap_fragment, - metalnessmap_pars_fragment: metalnessmap_pars_fragment, - morphnormal_vertex: morphnormal_vertex, - morphtarget_pars_vertex: morphtarget_pars_vertex, - morphtarget_vertex: morphtarget_vertex, - normal_flip: normal_flip, - normal_fragment: normal_fragment, - normalmap_pars_fragment: normalmap_pars_fragment, - packing: packing, - premultiplied_alpha_fragment: premultiplied_alpha_fragment, - project_vertex: project_vertex, - roughnessmap_fragment: roughnessmap_fragment, - roughnessmap_pars_fragment: roughnessmap_pars_fragment, - shadowmap_pars_fragment: shadowmap_pars_fragment, - shadowmap_pars_vertex: shadowmap_pars_vertex, - shadowmap_vertex: shadowmap_vertex, - shadowmask_pars_fragment: shadowmask_pars_fragment, - skinbase_vertex: skinbase_vertex, - skinning_pars_vertex: skinning_pars_vertex, - skinning_vertex: skinning_vertex, - skinnormal_vertex: skinnormal_vertex, - specularmap_fragment: specularmap_fragment, - specularmap_pars_fragment: specularmap_pars_fragment, - tonemapping_fragment: tonemapping_fragment, - tonemapping_pars_fragment: tonemapping_pars_fragment, - uv_pars_fragment: uv_pars_fragment, - uv_pars_vertex: uv_pars_vertex, - uv_vertex: uv_vertex, - uv2_pars_fragment: uv2_pars_fragment, - uv2_pars_vertex: uv2_pars_vertex, - uv2_vertex: uv2_vertex, - worldpos_vertex: worldpos_vertex, - - cube_frag: cube_frag, - cube_vert: cube_vert, - depth_frag: depth_frag, - depth_vert: depth_vert, - distanceRGBA_frag: distanceRGBA_frag, - distanceRGBA_vert: distanceRGBA_vert, - equirect_frag: equirect_frag, - equirect_vert: equirect_vert, - linedashed_frag: linedashed_frag, - linedashed_vert: linedashed_vert, - meshbasic_frag: meshbasic_frag, - meshbasic_vert: meshbasic_vert, - meshlambert_frag: meshlambert_frag, - meshlambert_vert: meshlambert_vert, - meshphong_frag: meshphong_frag, - meshphong_vert: meshphong_vert, - meshphysical_frag: meshphysical_frag, - meshphysical_vert: meshphysical_vert, - normal_frag: normal_frag, - normal_vert: normal_vert, - points_frag: points_frag, - points_vert: points_vert, - shadow_frag: shadow_frag, - shadow_vert: shadow_vert - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function Color( r, g, b ) { - - if ( g === undefined && b === undefined ) { - - // r is THREE.Color, hex or string - return this.set( r ); - - } - - return this.setRGB( r, g, b ); - - } - - Color.prototype = { - - constructor: Color, - - isColor: true, - - r: 1, g: 1, b: 1, - - set: function ( value ) { - - if ( value && value.isColor ) { - - this.copy( value ); - - } else if ( typeof value === 'number' ) { - - this.setHex( value ); - - } else if ( typeof value === 'string' ) { - - this.setStyle( value ); - - } - - return this; - - }, - - setScalar: function ( scalar ) { - - this.r = scalar; - this.g = scalar; - this.b = scalar; - - return this; - - }, - - setHex: function ( hex ) { - - hex = Math.floor( hex ); - - this.r = ( hex >> 16 & 255 ) / 255; - this.g = ( hex >> 8 & 255 ) / 255; - this.b = ( hex & 255 ) / 255; - - return this; - - }, - - setRGB: function ( r, g, b ) { - - this.r = r; - this.g = g; - this.b = b; - - return this; - - }, - - setHSL: function () { - - function hue2rgb( p, q, t ) { - - if ( t < 0 ) t += 1; - if ( t > 1 ) t -= 1; - if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; - if ( t < 1 / 2 ) return q; - if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); - return p; - - } - - return function setHSL( h, s, l ) { - - // h,s,l ranges are in 0.0 - 1.0 - h = _Math.euclideanModulo( h, 1 ); - s = _Math.clamp( s, 0, 1 ); - l = _Math.clamp( l, 0, 1 ); - - if ( s === 0 ) { - - this.r = this.g = this.b = l; - - } else { - - var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); - var q = ( 2 * l ) - p; - - this.r = hue2rgb( q, p, h + 1 / 3 ); - this.g = hue2rgb( q, p, h ); - this.b = hue2rgb( q, p, h - 1 / 3 ); - - } - - return this; - - }; - - }(), - - setStyle: function ( style ) { - - function handleAlpha( string ) { - - if ( string === undefined ) return; - - if ( parseFloat( string ) < 1 ) { - - console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); - - } - - } - - - var m; - - if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) { - - // rgb / hsl - - var color; - var name = m[ 1 ]; - var components = m[ 2 ]; - - switch ( name ) { - - case 'rgb': - case 'rgba': - - if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { - - // rgb(255,0,0) rgba(255,0,0,0.5) - this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; - this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; - this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; - - handleAlpha( color[ 5 ] ); - - return this; - - } - - if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { - - // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) - this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; - this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; - this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; - - handleAlpha( color[ 5 ] ); - - return this; - - } - - break; - - case 'hsl': - case 'hsla': - - if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { - - // hsl(120,50%,50%) hsla(120,50%,50%,0.5) - var h = parseFloat( color[ 1 ] ) / 360; - var s = parseInt( color[ 2 ], 10 ) / 100; - var l = parseInt( color[ 3 ], 10 ) / 100; - - handleAlpha( color[ 5 ] ); - - return this.setHSL( h, s, l ); - - } - - break; - - } - - } else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) { - - // hex color - - var hex = m[ 1 ]; - var size = hex.length; - - if ( size === 3 ) { - - // #ff0 - this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255; - this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255; - this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255; - - return this; - - } else if ( size === 6 ) { - - // #ff0000 - this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255; - this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255; - this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255; - - return this; - - } - - } - - if ( style && style.length > 0 ) { - - // color keywords - var hex = ColorKeywords[ style ]; - - if ( hex !== undefined ) { - - // red - this.setHex( hex ); - - } else { - - // unknown color - console.warn( 'THREE.Color: Unknown color ' + style ); - - } - - } - - return this; - - }, - - clone: function () { - - return new this.constructor( this.r, this.g, this.b ); - - }, - - copy: function ( color ) { - - this.r = color.r; - this.g = color.g; - this.b = color.b; - - return this; - - }, - - copyGammaToLinear: function ( color, gammaFactor ) { - - if ( gammaFactor === undefined ) gammaFactor = 2.0; - - this.r = Math.pow( color.r, gammaFactor ); - this.g = Math.pow( color.g, gammaFactor ); - this.b = Math.pow( color.b, gammaFactor ); - - return this; - - }, - - copyLinearToGamma: function ( color, gammaFactor ) { - - if ( gammaFactor === undefined ) gammaFactor = 2.0; - - var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0; - - this.r = Math.pow( color.r, safeInverse ); - this.g = Math.pow( color.g, safeInverse ); - this.b = Math.pow( color.b, safeInverse ); - - return this; - - }, - - convertGammaToLinear: function () { - - var r = this.r, g = this.g, b = this.b; - - this.r = r * r; - this.g = g * g; - this.b = b * b; - - return this; - - }, - - convertLinearToGamma: function () { - - this.r = Math.sqrt( this.r ); - this.g = Math.sqrt( this.g ); - this.b = Math.sqrt( this.b ); - - return this; - - }, - - getHex: function () { - - return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; - - }, - - getHexString: function () { - - return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 ); - - }, - - getHSL: function ( optionalTarget ) { - - // h,s,l ranges are in 0.0 - 1.0 - - var hsl = optionalTarget || { h: 0, s: 0, l: 0 }; - - var r = this.r, g = this.g, b = this.b; - - var max = Math.max( r, g, b ); - var min = Math.min( r, g, b ); - - var hue, saturation; - var lightness = ( min + max ) / 2.0; - - if ( min === max ) { - - hue = 0; - saturation = 0; - - } else { - - var delta = max - min; - - saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); - - switch ( max ) { - - case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; - case g: hue = ( b - r ) / delta + 2; break; - case b: hue = ( r - g ) / delta + 4; break; - - } - - hue /= 6; - - } - - hsl.h = hue; - hsl.s = saturation; - hsl.l = lightness; - - return hsl; - - }, - - getStyle: function () { - - return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')'; - - }, - - offsetHSL: function ( h, s, l ) { - - var hsl = this.getHSL(); - - hsl.h += h; hsl.s += s; hsl.l += l; - - this.setHSL( hsl.h, hsl.s, hsl.l ); - - return this; - - }, - - add: function ( color ) { - - this.r += color.r; - this.g += color.g; - this.b += color.b; - - return this; - - }, - - addColors: function ( color1, color2 ) { - - this.r = color1.r + color2.r; - this.g = color1.g + color2.g; - this.b = color1.b + color2.b; - - return this; - - }, - - addScalar: function ( s ) { - - this.r += s; - this.g += s; - this.b += s; - - return this; - - }, - - sub: function( color ) { - - this.r = Math.max( 0, this.r - color.r ); - this.g = Math.max( 0, this.g - color.g ); - this.b = Math.max( 0, this.b - color.b ); - - return this; - - }, - - multiply: function ( color ) { - - this.r *= color.r; - this.g *= color.g; - this.b *= color.b; - - return this; - - }, - - multiplyScalar: function ( s ) { - - this.r *= s; - this.g *= s; - this.b *= s; - - return this; - - }, - - lerp: function ( color, alpha ) { - - this.r += ( color.r - this.r ) * alpha; - this.g += ( color.g - this.g ) * alpha; - this.b += ( color.b - this.b ) * alpha; - - return this; - - }, - - equals: function ( c ) { - - return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); - - }, - - fromArray: function ( array, offset ) { - - if ( offset === undefined ) offset = 0; - - this.r = array[ offset ]; - this.g = array[ offset + 1 ]; - this.b = array[ offset + 2 ]; - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this.r; - array[ offset + 1 ] = this.g; - array[ offset + 2 ] = this.b; - - return array; - - }, - - toJSON: function () { - - return this.getHex(); - - } - - }; - - var ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, - 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, - 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, - 'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B, - 'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B, - 'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F, - 'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3, - 'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222, - 'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700, - 'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4, - 'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00, - 'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3, - 'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA, - 'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32, - 'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3, - 'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC, - 'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD, - 'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6, - 'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9, - 'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F, - 'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE, - 'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA, - 'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0, - 'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 }; - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { - - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - - this.image = { data: data, width: width, height: height }; - - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; - - this.generateMipmaps = false; - this.flipY = false; - this.unpackAlignment = 1; - - } - - DataTexture.prototype = Object.create( Texture.prototype ); - DataTexture.prototype.constructor = DataTexture; - - DataTexture.prototype.isDataTexture = true; - - /** - * Uniforms library for shared webgl shaders - */ - - var UniformsLib = { - - common: { - - diffuse: { value: new Color( 0xeeeeee ) }, - opacity: { value: 1.0 }, - - map: { value: null }, - offsetRepeat: { value: new Vector4( 0, 0, 1, 1 ) }, - - specularMap: { value: null }, - alphaMap: { value: null }, - - envMap: { value: null }, - flipEnvMap: { value: - 1 }, - reflectivity: { value: 1.0 }, - refractionRatio: { value: 0.98 } - - }, - - aomap: { - - aoMap: { value: null }, - aoMapIntensity: { value: 1 } - - }, - - lightmap: { - - lightMap: { value: null }, - lightMapIntensity: { value: 1 } - - }, - - emissivemap: { - - emissiveMap: { value: null } - - }, - - bumpmap: { - - bumpMap: { value: null }, - bumpScale: { value: 1 } - - }, - - normalmap: { - - normalMap: { value: null }, - normalScale: { value: new Vector2( 1, 1 ) } - - }, - - displacementmap: { - - displacementMap: { value: null }, - displacementScale: { value: 1 }, - displacementBias: { value: 0 } - - }, - - roughnessmap: { - - roughnessMap: { value: null } - - }, - - metalnessmap: { - - metalnessMap: { value: null } - - }, - - gradientmap: { - - gradientMap: { value: null } - - }, - - fog: { - - fogDensity: { value: 0.00025 }, - fogNear: { value: 1 }, - fogFar: { value: 2000 }, - fogColor: { value: new Color( 0xffffff ) } - - }, - - lights: { - - ambientLightColor: { value: [] }, - - directionalLights: { value: [], properties: { - direction: {}, - color: {}, - - shadow: {}, - shadowBias: {}, - shadowRadius: {}, - shadowMapSize: {} - } }, - - directionalShadowMap: { value: [] }, - directionalShadowMatrix: { value: [] }, - - spotLights: { value: [], properties: { - color: {}, - position: {}, - direction: {}, - distance: {}, - coneCos: {}, - penumbraCos: {}, - decay: {}, - - shadow: {}, - shadowBias: {}, - shadowRadius: {}, - shadowMapSize: {} - } }, - - spotShadowMap: { value: [] }, - spotShadowMatrix: { value: [] }, - - pointLights: { value: [], properties: { - color: {}, - position: {}, - decay: {}, - distance: {}, - - shadow: {}, - shadowBias: {}, - shadowRadius: {}, - shadowMapSize: {} - } }, - - pointShadowMap: { value: [] }, - pointShadowMatrix: { value: [] }, - - hemisphereLights: { value: [], properties: { - direction: {}, - skyColor: {}, - groundColor: {} - } }, - - // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src - rectAreaLights: { value: [], properties: { - color: {}, - position: {}, - width: {}, - height: {}, - } } - - }, - - points: { - - diffuse: { value: new Color( 0xeeeeee ) }, - opacity: { value: 1.0 }, - size: { value: 1.0 }, - scale: { value: 1.0 }, - map: { value: null }, - offsetRepeat: { value: new Vector4( 0, 0, 1, 1 ) } - - } - - }; - - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author mikael emtinger / http://gomo.se/ - */ - - var ShaderLib = { - - basic: { - - uniforms: UniformsUtils.merge( [ - UniformsLib.common, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.fog - ] ), - - vertexShader: ShaderChunk.meshbasic_vert, - fragmentShader: ShaderChunk.meshbasic_frag - - }, - - lambert: { - - uniforms: UniformsUtils.merge( [ - UniformsLib.common, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) } - } - ] ), - - vertexShader: ShaderChunk.meshlambert_vert, - fragmentShader: ShaderChunk.meshlambert_frag - - }, - - phong: { - - uniforms: UniformsUtils.merge( [ - UniformsLib.common, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.gradientmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) }, - specular: { value: new Color( 0x111111 ) }, - shininess: { value: 30 } - } - ] ), - - vertexShader: ShaderChunk.meshphong_vert, - fragmentShader: ShaderChunk.meshphong_frag - - }, - - standard: { - - uniforms: UniformsUtils.merge( [ - UniformsLib.common, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.roughnessmap, - UniformsLib.metalnessmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) }, - roughness: { value: 0.5 }, - metalness: { value: 0 }, - envMapIntensity: { value: 1 } // temporary - } - ] ), - - vertexShader: ShaderChunk.meshphysical_vert, - fragmentShader: ShaderChunk.meshphysical_frag - - }, - - points: { - - uniforms: UniformsUtils.merge( [ - UniformsLib.points, - UniformsLib.fog - ] ), - - vertexShader: ShaderChunk.points_vert, - fragmentShader: ShaderChunk.points_frag - - }, - - dashed: { - - uniforms: UniformsUtils.merge( [ - UniformsLib.common, - UniformsLib.fog, - { - scale: { value: 1 }, - dashSize: { value: 1 }, - totalSize: { value: 2 } - } - ] ), - - vertexShader: ShaderChunk.linedashed_vert, - fragmentShader: ShaderChunk.linedashed_frag - - }, - - depth: { - - uniforms: UniformsUtils.merge( [ - UniformsLib.common, - UniformsLib.displacementmap - ] ), - - vertexShader: ShaderChunk.depth_vert, - fragmentShader: ShaderChunk.depth_frag - - }, - - normal: { - - uniforms: UniformsUtils.merge( [ - UniformsLib.common, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - { - opacity: { value: 1.0 } - } - ] ), - - vertexShader: ShaderChunk.normal_vert, - fragmentShader: ShaderChunk.normal_frag - - }, - - /* ------------------------------------------------------------------------- - // Cube map shader - ------------------------------------------------------------------------- */ - - cube: { - - uniforms: { - tCube: { value: null }, - tFlip: { value: - 1 }, - opacity: { value: 1.0 } - }, - - vertexShader: ShaderChunk.cube_vert, - fragmentShader: ShaderChunk.cube_frag - - }, - - /* ------------------------------------------------------------------------- - // Cube map shader - ------------------------------------------------------------------------- */ - - equirect: { - - uniforms: { - tEquirect: { value: null }, - tFlip: { value: - 1 } - }, - - vertexShader: ShaderChunk.equirect_vert, - fragmentShader: ShaderChunk.equirect_frag - - }, - - distanceRGBA: { - - uniforms: { - lightPos: { value: new Vector3() } - }, - - vertexShader: ShaderChunk.distanceRGBA_vert, - fragmentShader: ShaderChunk.distanceRGBA_frag - - } - - }; - - ShaderLib.physical = { - - uniforms: UniformsUtils.merge( [ - ShaderLib.standard.uniforms, - { - clearCoat: { value: 0 }, - clearCoatRoughness: { value: 0 } - } - ] ), - - vertexShader: ShaderChunk.meshphysical_vert, - fragmentShader: ShaderChunk.meshphysical_frag - - }; - - /** - * @author bhouston / http://clara.io - */ - - function Box2( min, max ) { - - this.min = ( min !== undefined ) ? min : new Vector2( + Infinity, + Infinity ); - this.max = ( max !== undefined ) ? max : new Vector2( - Infinity, - Infinity ); - - } - - Box2.prototype = { - - constructor: Box2, - - set: function ( min, max ) { - - this.min.copy( min ); - this.max.copy( max ); - - return this; - - }, - - setFromPoints: function ( points ) { - - this.makeEmpty(); - - for ( var i = 0, il = points.length; i < il; i ++ ) { - - this.expandByPoint( points[ i ] ); - - } - - return this; - - }, - - setFromCenterAndSize: function () { - - var v1 = new Vector2(); - - return function setFromCenterAndSize( center, size ) { - - var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); - this.min.copy( center ).sub( halfSize ); - this.max.copy( center ).add( halfSize ); - - return this; - - }; - - }(), - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( box ) { - - this.min.copy( box.min ); - this.max.copy( box.max ); - - return this; - - }, - - makeEmpty: function () { - - this.min.x = this.min.y = + Infinity; - this.max.x = this.max.y = - Infinity; - - return this; - - }, - - isEmpty: function () { - - // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes - - return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); - - }, - - getCenter: function ( optionalTarget ) { - - var result = optionalTarget || new Vector2(); - return this.isEmpty() ? result.set( 0, 0 ) : result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); - - }, - - getSize: function ( optionalTarget ) { - - var result = optionalTarget || new Vector2(); - return this.isEmpty() ? result.set( 0, 0 ) : result.subVectors( this.max, this.min ); - - }, - - expandByPoint: function ( point ) { - - this.min.min( point ); - this.max.max( point ); - - return this; - - }, - - expandByVector: function ( vector ) { - - this.min.sub( vector ); - this.max.add( vector ); - - return this; - - }, - - expandByScalar: function ( scalar ) { - - this.min.addScalar( - scalar ); - this.max.addScalar( scalar ); - - return this; - - }, - - containsPoint: function ( point ) { - - return point.x < this.min.x || point.x > this.max.x || - point.y < this.min.y || point.y > this.max.y ? false : true; - - }, - - containsBox: function ( box ) { - - return this.min.x <= box.min.x && box.max.x <= this.max.x && - this.min.y <= box.min.y && box.max.y <= this.max.y; - - }, - - getParameter: function ( point, optionalTarget ) { - - // This can potentially have a divide by zero if the box - // has a size dimension of 0. - - var result = optionalTarget || new Vector2(); - - return result.set( - ( point.x - this.min.x ) / ( this.max.x - this.min.x ), - ( point.y - this.min.y ) / ( this.max.y - this.min.y ) - ); - - }, - - intersectsBox: function ( box ) { - - // using 6 splitting planes to rule out intersections. - return box.max.x < this.min.x || box.min.x > this.max.x || - box.max.y < this.min.y || box.min.y > this.max.y ? false : true; - - }, - - clampPoint: function ( point, optionalTarget ) { - - var result = optionalTarget || new Vector2(); - return result.copy( point ).clamp( this.min, this.max ); - - }, - - distanceToPoint: function () { - - var v1 = new Vector2(); - - return function distanceToPoint( point ) { - - var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); - return clampedPoint.sub( point ).length(); - - }; - - }(), - - intersect: function ( box ) { - - this.min.max( box.min ); - this.max.min( box.max ); - - return this; - - }, - - union: function ( box ) { - - this.min.min( box.min ); - this.max.max( box.max ); - - return this; - - }, - - translate: function ( offset ) { - - this.min.add( offset ); - this.max.add( offset ); - - return this; - - }, - - equals: function ( box ) { - - return box.min.equals( this.min ) && box.max.equals( this.max ); - - } - - }; - - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ - - function LensFlarePlugin( renderer, flares ) { - - var gl = renderer.context; - var state = renderer.state; - - var vertexBuffer, elementBuffer; - var shader, program, attributes, uniforms; - - var tempTexture, occlusionTexture; - - function init() { - - var vertices = new Float32Array( [ - - 1, - 1, 0, 0, - 1, - 1, 1, 0, - 1, 1, 1, 1, - - 1, 1, 0, 1 - ] ); - - var faces = new Uint16Array( [ - 0, 1, 2, - 0, 2, 3 - ] ); - - // buffers - - vertexBuffer = gl.createBuffer(); - elementBuffer = gl.createBuffer(); - - gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); - gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); - - gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); - gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); - - // textures - - tempTexture = gl.createTexture(); - occlusionTexture = gl.createTexture(); - - state.bindTexture( gl.TEXTURE_2D, tempTexture ); - gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null ); - gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); - gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); - gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); - gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); - - state.bindTexture( gl.TEXTURE_2D, occlusionTexture ); - gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null ); - gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); - gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); - gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); - gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); - - shader = { - - vertexShader: [ - - "uniform lowp int renderType;", - - "uniform vec3 screenPosition;", - "uniform vec2 scale;", - "uniform float rotation;", - - "uniform sampler2D occlusionMap;", - - "attribute vec2 position;", - "attribute vec2 uv;", - - "varying vec2 vUV;", - "varying float vVisibility;", - - "void main() {", - - "vUV = uv;", - - "vec2 pos = position;", - - "if ( renderType == 2 ) {", - - "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );", - - "vVisibility = visibility.r / 9.0;", - "vVisibility *= 1.0 - visibility.g / 9.0;", - "vVisibility *= visibility.b / 9.0;", - "vVisibility *= 1.0 - visibility.a / 9.0;", - - "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", - "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", - - "}", - - "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", - - "}" - - ].join( "\n" ), - - fragmentShader: [ - - "uniform lowp int renderType;", - - "uniform sampler2D map;", - "uniform float opacity;", - "uniform vec3 color;", - - "varying vec2 vUV;", - "varying float vVisibility;", - - "void main() {", - - // pink square - - "if ( renderType == 0 ) {", - - "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", - - // restore - - "} else if ( renderType == 1 ) {", - - "gl_FragColor = texture2D( map, vUV );", - - // flare - - "} else {", - - "vec4 texture = texture2D( map, vUV );", - "texture.a *= opacity * vVisibility;", - "gl_FragColor = texture;", - "gl_FragColor.rgb *= color;", - - "}", - - "}" - - ].join( "\n" ) - - }; - - program = createProgram( shader ); - - attributes = { - vertex: gl.getAttribLocation ( program, "position" ), - uv: gl.getAttribLocation ( program, "uv" ) - }; - - uniforms = { - renderType: gl.getUniformLocation( program, "renderType" ), - map: gl.getUniformLocation( program, "map" ), - occlusionMap: gl.getUniformLocation( program, "occlusionMap" ), - opacity: gl.getUniformLocation( program, "opacity" ), - color: gl.getUniformLocation( program, "color" ), - scale: gl.getUniformLocation( program, "scale" ), - rotation: gl.getUniformLocation( program, "rotation" ), - screenPosition: gl.getUniformLocation( program, "screenPosition" ) - }; - - } - - /* - * Render lens flares - * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, - * reads these back and calculates occlusion. - */ - - this.render = function ( scene, camera, viewport ) { - - if ( flares.length === 0 ) return; - - var tempPosition = new Vector3(); - - var invAspect = viewport.w / viewport.z, - halfViewportWidth = viewport.z * 0.5, - halfViewportHeight = viewport.w * 0.5; - - var size = 16 / viewport.w, - scale = new Vector2( size * invAspect, size ); - - var screenPosition = new Vector3( 1, 1, 0 ), - screenPositionPixels = new Vector2( 1, 1 ); - - var validArea = new Box2(); - - validArea.min.set( viewport.x, viewport.y ); - validArea.max.set( viewport.x + ( viewport.z - 16 ), viewport.y + ( viewport.w - 16 ) ); - - if ( program === undefined ) { - - init(); - - } - - gl.useProgram( program ); - - state.initAttributes(); - state.enableAttribute( attributes.vertex ); - state.enableAttribute( attributes.uv ); - state.disableUnusedAttributes(); - - // loop through all lens flares to update their occlusion and positions - // setup gl and common used attribs/uniforms - - gl.uniform1i( uniforms.occlusionMap, 0 ); - gl.uniform1i( uniforms.map, 1 ); - - gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); - gl.vertexAttribPointer( attributes.vertex, 2, gl.FLOAT, false, 2 * 8, 0 ); - gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); - - gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); - - state.disable( gl.CULL_FACE ); - state.setDepthWrite( false ); - - for ( var i = 0, l = flares.length; i < l; i ++ ) { - - size = 16 / viewport.w; - scale.set( size * invAspect, size ); - - // calc object screen position - - var flare = flares[ i ]; - - tempPosition.set( flare.matrixWorld.elements[ 12 ], flare.matrixWorld.elements[ 13 ], flare.matrixWorld.elements[ 14 ] ); - - tempPosition.applyMatrix4( camera.matrixWorldInverse ); - tempPosition.applyProjection( camera.projectionMatrix ); - - // setup arrays for gl programs - - screenPosition.copy( tempPosition ); - - // horizontal and vertical coordinate of the lower left corner of the pixels to copy - - screenPositionPixels.x = viewport.x + ( screenPosition.x * halfViewportWidth ) + halfViewportWidth - 8; - screenPositionPixels.y = viewport.y + ( screenPosition.y * halfViewportHeight ) + halfViewportHeight - 8; - - // screen cull - - if ( validArea.containsPoint( screenPositionPixels ) === true ) { - - // save current RGB to temp texture - - state.activeTexture( gl.TEXTURE0 ); - state.bindTexture( gl.TEXTURE_2D, null ); - state.activeTexture( gl.TEXTURE1 ); - state.bindTexture( gl.TEXTURE_2D, tempTexture ); - gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x, screenPositionPixels.y, 16, 16, 0 ); - - - // render pink quad - - gl.uniform1i( uniforms.renderType, 0 ); - gl.uniform2f( uniforms.scale, scale.x, scale.y ); - gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); - - state.disable( gl.BLEND ); - state.enable( gl.DEPTH_TEST ); - - gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - - - // copy result to occlusionMap - - state.activeTexture( gl.TEXTURE0 ); - state.bindTexture( gl.TEXTURE_2D, occlusionTexture ); - gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x, screenPositionPixels.y, 16, 16, 0 ); - - - // restore graphics - - gl.uniform1i( uniforms.renderType, 1 ); - state.disable( gl.DEPTH_TEST ); - - state.activeTexture( gl.TEXTURE1 ); - state.bindTexture( gl.TEXTURE_2D, tempTexture ); - gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - - - // update object positions - - flare.positionScreen.copy( screenPosition ); - - if ( flare.customUpdateCallback ) { - - flare.customUpdateCallback( flare ); - - } else { - - flare.updateLensFlares(); - - } - - // render flares - - gl.uniform1i( uniforms.renderType, 2 ); - state.enable( gl.BLEND ); - - for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { - - var sprite = flare.lensFlares[ j ]; - - if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { - - screenPosition.x = sprite.x; - screenPosition.y = sprite.y; - screenPosition.z = sprite.z; - - size = sprite.size * sprite.scale / viewport.w; - - scale.x = size * invAspect; - scale.y = size; - - gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); - gl.uniform2f( uniforms.scale, scale.x, scale.y ); - gl.uniform1f( uniforms.rotation, sprite.rotation ); - - gl.uniform1f( uniforms.opacity, sprite.opacity ); - gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); - - state.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); - renderer.setTexture2D( sprite.texture, 1 ); - - gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - - } - - } - - } - - } - - // restore gl - - state.enable( gl.CULL_FACE ); - state.enable( gl.DEPTH_TEST ); - state.setDepthWrite( true ); - - renderer.resetGLState(); - - }; - - function createProgram( shader ) { - - var program = gl.createProgram(); - - var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); - var vertexShader = gl.createShader( gl.VERTEX_SHADER ); - - var prefix = "precision " + renderer.getPrecision() + " float;\n"; - - gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); - gl.shaderSource( vertexShader, prefix + shader.vertexShader ); - - gl.compileShader( fragmentShader ); - gl.compileShader( vertexShader ); - - gl.attachShader( program, fragmentShader ); - gl.attachShader( program, vertexShader ); - - gl.linkProgram( program ); - - return program; - - } - - } - - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ - - function SpritePlugin( renderer, sprites ) { - - var gl = renderer.context; - var state = renderer.state; - - var vertexBuffer, elementBuffer; - var program, attributes, uniforms; - - var texture; - - // decompose matrixWorld - - var spritePosition = new Vector3(); - var spriteRotation = new Quaternion(); - var spriteScale = new Vector3(); - - function init() { - - var vertices = new Float32Array( [ - - 0.5, - 0.5, 0, 0, - 0.5, - 0.5, 1, 0, - 0.5, 0.5, 1, 1, - - 0.5, 0.5, 0, 1 - ] ); - - var faces = new Uint16Array( [ - 0, 1, 2, - 0, 2, 3 - ] ); - - vertexBuffer = gl.createBuffer(); - elementBuffer = gl.createBuffer(); - - gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); - gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); - - gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); - gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); - - program = createProgram(); - - attributes = { - position: gl.getAttribLocation ( program, 'position' ), - uv: gl.getAttribLocation ( program, 'uv' ) - }; - - uniforms = { - uvOffset: gl.getUniformLocation( program, 'uvOffset' ), - uvScale: gl.getUniformLocation( program, 'uvScale' ), - - rotation: gl.getUniformLocation( program, 'rotation' ), - scale: gl.getUniformLocation( program, 'scale' ), - - color: gl.getUniformLocation( program, 'color' ), - map: gl.getUniformLocation( program, 'map' ), - opacity: gl.getUniformLocation( program, 'opacity' ), - - modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ), - projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ), - - fogType: gl.getUniformLocation( program, 'fogType' ), - fogDensity: gl.getUniformLocation( program, 'fogDensity' ), - fogNear: gl.getUniformLocation( program, 'fogNear' ), - fogFar: gl.getUniformLocation( program, 'fogFar' ), - fogColor: gl.getUniformLocation( program, 'fogColor' ), - - alphaTest: gl.getUniformLocation( program, 'alphaTest' ) - }; - - var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - canvas.width = 8; - canvas.height = 8; - - var context = canvas.getContext( '2d' ); - context.fillStyle = 'white'; - context.fillRect( 0, 0, 8, 8 ); - - texture = new Texture( canvas ); - texture.needsUpdate = true; - - } - - this.render = function ( scene, camera ) { - - if ( sprites.length === 0 ) return; - - // setup gl - - if ( program === undefined ) { - - init(); - - } - - gl.useProgram( program ); - - state.initAttributes(); - state.enableAttribute( attributes.position ); - state.enableAttribute( attributes.uv ); - state.disableUnusedAttributes(); - - state.disable( gl.CULL_FACE ); - state.enable( gl.BLEND ); - - gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); - gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 ); - gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); - - gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); - - gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); - - state.activeTexture( gl.TEXTURE0 ); - gl.uniform1i( uniforms.map, 0 ); - - var oldFogType = 0; - var sceneFogType = 0; - var fog = scene.fog; - - if ( fog ) { - - gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); - - if ( fog.isFog ) { - - gl.uniform1f( uniforms.fogNear, fog.near ); - gl.uniform1f( uniforms.fogFar, fog.far ); - - gl.uniform1i( uniforms.fogType, 1 ); - oldFogType = 1; - sceneFogType = 1; - - } else if ( fog.isFogExp2 ) { - - gl.uniform1f( uniforms.fogDensity, fog.density ); - - gl.uniform1i( uniforms.fogType, 2 ); - oldFogType = 2; - sceneFogType = 2; - - } - - } else { - - gl.uniform1i( uniforms.fogType, 0 ); - oldFogType = 0; - sceneFogType = 0; - - } - - - // update positions and sort - - for ( var i = 0, l = sprites.length; i < l; i ++ ) { - - var sprite = sprites[ i ]; - - sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); - sprite.z = - sprite.modelViewMatrix.elements[ 14 ]; - - } - - sprites.sort( painterSortStable ); - - // render all sprites - - var scale = []; - - for ( var i = 0, l = sprites.length; i < l; i ++ ) { - - var sprite = sprites[ i ]; - var material = sprite.material; - - if ( material.visible === false ) continue; - - gl.uniform1f( uniforms.alphaTest, material.alphaTest ); - gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements ); - - sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale ); - - scale[ 0 ] = spriteScale.x; - scale[ 1 ] = spriteScale.y; - - var fogType = 0; - - if ( scene.fog && material.fog ) { - - fogType = sceneFogType; - - } - - if ( oldFogType !== fogType ) { - - gl.uniform1i( uniforms.fogType, fogType ); - oldFogType = fogType; - - } - - if ( material.map !== null ) { - - gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); - gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); - - } else { - - gl.uniform2f( uniforms.uvOffset, 0, 0 ); - gl.uniform2f( uniforms.uvScale, 1, 1 ); - - } - - gl.uniform1f( uniforms.opacity, material.opacity ); - gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); - - gl.uniform1f( uniforms.rotation, material.rotation ); - gl.uniform2fv( uniforms.scale, scale ); - - state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); - state.setDepthTest( material.depthTest ); - state.setDepthWrite( material.depthWrite ); - - if ( material.map ) { - - renderer.setTexture2D( material.map, 0 ); - - } else { - - renderer.setTexture2D( texture, 0 ); - - } - - gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - - } - - // restore gl - - state.enable( gl.CULL_FACE ); - - renderer.resetGLState(); - - }; - - function createProgram() { - - var program = gl.createProgram(); - - var vertexShader = gl.createShader( gl.VERTEX_SHADER ); - var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); - - gl.shaderSource( vertexShader, [ - - 'precision ' + renderer.getPrecision() + ' float;', - - 'uniform mat4 modelViewMatrix;', - 'uniform mat4 projectionMatrix;', - 'uniform float rotation;', - 'uniform vec2 scale;', - 'uniform vec2 uvOffset;', - 'uniform vec2 uvScale;', - - 'attribute vec2 position;', - 'attribute vec2 uv;', - - 'varying vec2 vUV;', - - 'void main() {', - - 'vUV = uvOffset + uv * uvScale;', - - 'vec2 alignedPosition = position * scale;', - - 'vec2 rotatedPosition;', - 'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;', - 'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;', - - 'vec4 finalPosition;', - - 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );', - 'finalPosition.xy += rotatedPosition;', - 'finalPosition = projectionMatrix * finalPosition;', - - 'gl_Position = finalPosition;', - - '}' - - ].join( '\n' ) ); - - gl.shaderSource( fragmentShader, [ - - 'precision ' + renderer.getPrecision() + ' float;', - - 'uniform vec3 color;', - 'uniform sampler2D map;', - 'uniform float opacity;', - - 'uniform int fogType;', - 'uniform vec3 fogColor;', - 'uniform float fogDensity;', - 'uniform float fogNear;', - 'uniform float fogFar;', - 'uniform float alphaTest;', - - 'varying vec2 vUV;', - - 'void main() {', - - 'vec4 texture = texture2D( map, vUV );', - - 'if ( texture.a < alphaTest ) discard;', - - 'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );', - - 'if ( fogType > 0 ) {', - - 'float depth = gl_FragCoord.z / gl_FragCoord.w;', - 'float fogFactor = 0.0;', - - 'if ( fogType == 1 ) {', - - 'fogFactor = smoothstep( fogNear, fogFar, depth );', - - '} else {', - - 'const float LOG2 = 1.442695;', - 'fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );', - 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );', - - '}', - - 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );', - - '}', - - '}' - - ].join( '\n' ) ); - - gl.compileShader( vertexShader ); - gl.compileShader( fragmentShader ); - - gl.attachShader( program, vertexShader ); - gl.attachShader( program, fragmentShader ); - - gl.linkProgram( program ); - - return program; - - } - - function painterSortStable( a, b ) { - - if ( a.renderOrder !== b.renderOrder ) { - - return a.renderOrder - b.renderOrder; - - } else if ( a.z !== b.z ) { - - return b.z - a.z; - - } else { - - return b.id - a.id; - - } - - } - - } - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - - var materialId = 0; - - function Material() { - - Object.defineProperty( this, 'id', { value: materialId ++ } ); - - this.uuid = _Math.generateUUID(); - - this.name = ''; - this.type = 'Material'; - - this.fog = true; - this.lights = true; - - this.blending = NormalBlending; - this.side = FrontSide; - this.shading = SmoothShading; // THREE.FlatShading, THREE.SmoothShading - this.vertexColors = NoColors; // THREE.NoColors, THREE.VertexColors, THREE.FaceColors - - this.opacity = 1; - this.transparent = false; - - this.blendSrc = SrcAlphaFactor; - this.blendDst = OneMinusSrcAlphaFactor; - this.blendEquation = AddEquation; - this.blendSrcAlpha = null; - this.blendDstAlpha = null; - this.blendEquationAlpha = null; - - this.depthFunc = LessEqualDepth; - this.depthTest = true; - this.depthWrite = true; - - this.clippingPlanes = null; - this.clipIntersection = false; - this.clipShadows = false; - - this.colorWrite = true; - - this.precision = null; // override the renderer's default precision for this material - - this.polygonOffset = false; - this.polygonOffsetFactor = 0; - this.polygonOffsetUnits = 0; - - this.alphaTest = 0; - this.premultipliedAlpha = false; - - this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer - - this.visible = true; - - this._needsUpdate = true; - - } - - Material.prototype = { - - constructor: Material, - - isMaterial: true, - - get needsUpdate() { - - return this._needsUpdate; - - }, - - set needsUpdate( value ) { - - if ( value === true ) this.update(); - this._needsUpdate = value; - - }, - - setValues: function ( values ) { - - if ( values === undefined ) return; - - for ( var key in values ) { - - var newValue = values[ key ]; - - if ( newValue === undefined ) { - - console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); - continue; - - } - - var currentValue = this[ key ]; - - if ( currentValue === undefined ) { - - console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." ); - continue; - - } - - if ( currentValue && currentValue.isColor ) { - - currentValue.set( newValue ); - - } else if ( (currentValue && currentValue.isVector3) && (newValue && newValue.isVector3) ) { - - currentValue.copy( newValue ); - - } else if ( key === 'overdraw' ) { - - // ensure overdraw is backwards-compatible with legacy boolean type - this[ key ] = Number( newValue ); - - } else { - - this[ key ] = newValue; - - } - - } - - }, - - toJSON: function ( meta ) { - - var isRoot = meta === undefined; - - if ( isRoot ) { - - meta = { - textures: {}, - images: {} - }; - - } - - var data = { - metadata: { - version: 4.4, - type: 'Material', - generator: 'Material.toJSON' - } - }; - - // standard Material serialization - data.uuid = this.uuid; - data.type = this.type; - - if ( this.name !== '' ) data.name = this.name; - - if ( this.color && this.color.isColor ) data.color = this.color.getHex(); - - if ( this.roughness !== undefined ) data.roughness = this.roughness; - if ( this.metalness !== undefined ) data.metalness = this.metalness; - - if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); - if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); - if ( this.shininess !== undefined ) data.shininess = this.shininess; - if ( this.clearCoat !== undefined ) data.clearCoat = this.clearCoat; - if ( this.clearCoatRoughness !== undefined ) data.clearCoatRoughness = this.clearCoatRoughness; - - if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; - if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; - if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid; - if ( this.bumpMap && this.bumpMap.isTexture ) { - - data.bumpMap = this.bumpMap.toJSON( meta ).uuid; - data.bumpScale = this.bumpScale; - - } - if ( this.normalMap && this.normalMap.isTexture ) { - - data.normalMap = this.normalMap.toJSON( meta ).uuid; - data.normalScale = this.normalScale.toArray(); - - } - if ( this.displacementMap && this.displacementMap.isTexture ) { - - data.displacementMap = this.displacementMap.toJSON( meta ).uuid; - data.displacementScale = this.displacementScale; - data.displacementBias = this.displacementBias; - - } - if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; - if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; - - if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; - if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; - - if ( this.envMap && this.envMap.isTexture ) { - - data.envMap = this.envMap.toJSON( meta ).uuid; - data.reflectivity = this.reflectivity; // Scale behind envMap - - } - - if ( this.gradientMap && this.gradientMap.isTexture ) { - - data.gradientMap = this.gradientMap.toJSON( meta ).uuid; - - } - - if ( this.size !== undefined ) data.size = this.size; - if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; - - if ( this.blending !== NormalBlending ) data.blending = this.blending; - if ( this.shading !== SmoothShading ) data.shading = this.shading; - if ( this.side !== FrontSide ) data.side = this.side; - if ( this.vertexColors !== NoColors ) data.vertexColors = this.vertexColors; - - if ( this.opacity < 1 ) data.opacity = this.opacity; - if ( this.transparent === true ) data.transparent = this.transparent; - - data.depthFunc = this.depthFunc; - data.depthTest = this.depthTest; - data.depthWrite = this.depthWrite; - - if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; - if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; - if ( this.wireframe === true ) data.wireframe = this.wireframe; - if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; - if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap; - if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin; - - data.skinning = this.skinning; - data.morphTargets = this.morphTargets; - - // TODO: Copied from Object3D.toJSON - - function extractFromCache( cache ) { - - var values = []; - - for ( var key in cache ) { - - var data = cache[ key ]; - delete data.metadata; - values.push( data ); - - } - - return values; - - } - - if ( isRoot ) { - - var textures = extractFromCache( meta.textures ); - var images = extractFromCache( meta.images ); - - if ( textures.length > 0 ) data.textures = textures; - if ( images.length > 0 ) data.images = images; - - } - - return data; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.name = source.name; - - this.fog = source.fog; - this.lights = source.lights; - - this.blending = source.blending; - this.side = source.side; - this.shading = source.shading; - this.vertexColors = source.vertexColors; - - this.opacity = source.opacity; - this.transparent = source.transparent; - - this.blendSrc = source.blendSrc; - this.blendDst = source.blendDst; - this.blendEquation = source.blendEquation; - this.blendSrcAlpha = source.blendSrcAlpha; - this.blendDstAlpha = source.blendDstAlpha; - this.blendEquationAlpha = source.blendEquationAlpha; - - this.depthFunc = source.depthFunc; - this.depthTest = source.depthTest; - this.depthWrite = source.depthWrite; - - this.colorWrite = source.colorWrite; - - this.precision = source.precision; - - this.polygonOffset = source.polygonOffset; - this.polygonOffsetFactor = source.polygonOffsetFactor; - this.polygonOffsetUnits = source.polygonOffsetUnits; - - this.alphaTest = source.alphaTest; - - this.premultipliedAlpha = source.premultipliedAlpha; - - this.overdraw = source.overdraw; - - this.visible = source.visible; - this.clipShadows = source.clipShadows; - this.clipIntersection = source.clipIntersection; - - var srcPlanes = source.clippingPlanes, - dstPlanes = null; - - if ( srcPlanes !== null ) { - - var n = srcPlanes.length; - dstPlanes = new Array( n ); - - for ( var i = 0; i !== n; ++ i ) - dstPlanes[ i ] = srcPlanes[ i ].clone(); - - } - - this.clippingPlanes = dstPlanes; - - return this; - - }, - - update: function () { - - this.dispatchEvent( { type: 'update' } ); - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - }; - - Object.assign( Material.prototype, EventDispatcher.prototype ); - - /** - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * defines: { "label" : "value" }, - * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, - * - * fragmentShader: , - * vertexShader: , - * - * wireframe: , - * wireframeLinewidth: , - * - * lights: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function ShaderMaterial( parameters ) { - - Material.call( this ); - - this.type = 'ShaderMaterial'; - - this.defines = {}; - this.uniforms = {}; - - this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}'; - this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}'; - - this.linewidth = 1; - - this.wireframe = false; - this.wireframeLinewidth = 1; - - this.fog = false; // set to use scene fog - this.lights = false; // set to use scene lights - this.clipping = false; // set to use user-defined clipping planes - - this.skinning = false; // set to use skinning attribute streams - this.morphTargets = false; // set to use morph targets - this.morphNormals = false; // set to use morph normals - - this.extensions = { - derivatives: false, // set to use derivatives - fragDepth: false, // set to use fragment depth values - drawBuffers: false, // set to use draw buffers - shaderTextureLOD: false // set to use shader texture LOD - }; - - // When rendered geometry doesn't include these attributes but the material does, - // use these default values in WebGL. This avoids errors when buffer data is missing. - this.defaultAttributeValues = { - 'color': [ 1, 1, 1 ], - 'uv': [ 0, 0 ], - 'uv2': [ 0, 0 ] - }; - - this.index0AttributeName = undefined; - - if ( parameters !== undefined ) { - - if ( parameters.attributes !== undefined ) { - - console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); - - } - - this.setValues( parameters ); - - } - - } - - ShaderMaterial.prototype = Object.create( Material.prototype ); - ShaderMaterial.prototype.constructor = ShaderMaterial; - - ShaderMaterial.prototype.isShaderMaterial = true; - - ShaderMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.fragmentShader = source.fragmentShader; - this.vertexShader = source.vertexShader; - - this.uniforms = UniformsUtils.clone( source.uniforms ); - - this.defines = source.defines; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - - this.lights = source.lights; - this.clipping = source.clipping; - - this.skinning = source.skinning; - - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - this.extensions = source.extensions; - - return this; - - }; - - ShaderMaterial.prototype.toJSON = function ( meta ) { - - var data = Material.prototype.toJSON.call( this, meta ); - - data.uniforms = this.uniforms; - data.vertexShader = this.vertexShader; - data.fragmentShader = this.fragmentShader; - - return data; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author bhouston / https://clara.io - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * - * opacity: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * } - */ - - function MeshDepthMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshDepthMaterial'; - - this.depthPacking = BasicDepthPacking; - - this.skinning = false; - this.morphTargets = false; - - this.map = null; - - this.alphaMap = null; - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.wireframe = false; - this.wireframeLinewidth = 1; - - this.fog = false; - this.lights = false; - - this.setValues( parameters ); - - } - - MeshDepthMaterial.prototype = Object.create( Material.prototype ); - MeshDepthMaterial.prototype.constructor = MeshDepthMaterial; - - MeshDepthMaterial.prototype.isMeshDepthMaterial = true; - - MeshDepthMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.depthPacking = source.depthPacking; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - - return this; - - }; - - /** - * @author bhouston / http://clara.io - * @author WestLangley / http://github.com/WestLangley - */ - - function Box3( min, max ) { - - this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity ); - this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity ); - - } - - Box3.prototype = { - - constructor: Box3, - - isBox3: true, - - set: function ( min, max ) { - - this.min.copy( min ); - this.max.copy( max ); - - return this; - - }, - - setFromArray: function ( array ) { - - var minX = + Infinity; - var minY = + Infinity; - var minZ = + Infinity; - - var maxX = - Infinity; - var maxY = - Infinity; - var maxZ = - Infinity; - - for ( var i = 0, l = array.length; i < l; i += 3 ) { - - var x = array[ i ]; - var y = array[ i + 1 ]; - var z = array[ i + 2 ]; - - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( z < minZ ) minZ = z; - - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - if ( z > maxZ ) maxZ = z; - - } - - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); - - }, - - setFromBufferAttribute: function ( attribute ) { - - var minX = + Infinity; - var minY = + Infinity; - var minZ = + Infinity; - - var maxX = - Infinity; - var maxY = - Infinity; - var maxZ = - Infinity; - - for ( var i = 0, l = attribute.count; i < l; i ++ ) { - - var x = attribute.getX( i ); - var y = attribute.getY( i ); - var z = attribute.getZ( i ); - - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( z < minZ ) minZ = z; - - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - if ( z > maxZ ) maxZ = z; - - } - - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); - - }, - - setFromPoints: function ( points ) { - - this.makeEmpty(); - - for ( var i = 0, il = points.length; i < il; i ++ ) { - - this.expandByPoint( points[ i ] ); - - } - - return this; - - }, - - setFromCenterAndSize: function () { - - var v1 = new Vector3(); - - return function setFromCenterAndSize( center, size ) { - - var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); - - this.min.copy( center ).sub( halfSize ); - this.max.copy( center ).add( halfSize ); - - return this; - - }; - - }(), - - setFromObject: function () { - - // Computes the world-axis-aligned bounding box of an object (including its children), - // accounting for both the object's, and children's, world transforms - - var v1 = new Vector3(); - - return function setFromObject( object ) { - - var scope = this; - - object.updateMatrixWorld( true ); - - this.makeEmpty(); - - object.traverse( function ( node ) { - - var i, l; - - var geometry = node.geometry; - - if ( geometry !== undefined ) { - - if ( geometry.isGeometry ) { - - var vertices = geometry.vertices; - - for ( i = 0, l = vertices.length; i < l; i ++ ) { - - v1.copy( vertices[ i ] ); - v1.applyMatrix4( node.matrixWorld ); - - scope.expandByPoint( v1 ); - - } - - } else if ( geometry.isBufferGeometry ) { - - var attribute = geometry.attributes.position; - - if ( attribute !== undefined ) { - - for ( i = 0, l = attribute.count; i < l; i ++ ) { - - v1.fromAttribute( attribute, i ).applyMatrix4( node.matrixWorld ); - - scope.expandByPoint( v1 ); - - } - - } - - } - - } - - } ); - - return this; - - }; - - }(), - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( box ) { - - this.min.copy( box.min ); - this.max.copy( box.max ); - - return this; - - }, - - makeEmpty: function () { - - this.min.x = this.min.y = this.min.z = + Infinity; - this.max.x = this.max.y = this.max.z = - Infinity; - - return this; - - }, - - isEmpty: function () { - - // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes - - return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); - - }, - - getCenter: function ( optionalTarget ) { - - var result = optionalTarget || new Vector3(); - return this.isEmpty() ? result.set( 0, 0, 0 ) : result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); - - }, - - getSize: function ( optionalTarget ) { - - var result = optionalTarget || new Vector3(); - return this.isEmpty() ? result.set( 0, 0, 0 ) : result.subVectors( this.max, this.min ); - - }, - - expandByPoint: function ( point ) { - - this.min.min( point ); - this.max.max( point ); - - return this; - - }, - - expandByVector: function ( vector ) { - - this.min.sub( vector ); - this.max.add( vector ); - - return this; - - }, - - expandByScalar: function ( scalar ) { - - this.min.addScalar( - scalar ); - this.max.addScalar( scalar ); - - return this; - - }, - - containsPoint: function ( point ) { - - return point.x < this.min.x || point.x > this.max.x || - point.y < this.min.y || point.y > this.max.y || - point.z < this.min.z || point.z > this.max.z ? false : true; - - }, - - containsBox: function ( box ) { - - return this.min.x <= box.min.x && box.max.x <= this.max.x && - this.min.y <= box.min.y && box.max.y <= this.max.y && - this.min.z <= box.min.z && box.max.z <= this.max.z; - - }, - - getParameter: function ( point, optionalTarget ) { - - // This can potentially have a divide by zero if the box - // has a size dimension of 0. - - var result = optionalTarget || new Vector3(); - - return result.set( - ( point.x - this.min.x ) / ( this.max.x - this.min.x ), - ( point.y - this.min.y ) / ( this.max.y - this.min.y ), - ( point.z - this.min.z ) / ( this.max.z - this.min.z ) - ); - - }, - - intersectsBox: function ( box ) { - - // using 6 splitting planes to rule out intersections. - return box.max.x < this.min.x || box.min.x > this.max.x || - box.max.y < this.min.y || box.min.y > this.max.y || - box.max.z < this.min.z || box.min.z > this.max.z ? false : true; - - }, - - intersectsSphere: ( function () { - - var closestPoint; - - return function intersectsSphere( sphere ) { - - if ( closestPoint === undefined ) closestPoint = new Vector3(); - - // Find the point on the AABB closest to the sphere center. - this.clampPoint( sphere.center, closestPoint ); - - // If that point is inside the sphere, the AABB and sphere intersect. - return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); - - }; - - } )(), - - intersectsPlane: function ( plane ) { - - // We compute the minimum and maximum dot product values. If those values - // are on the same side (back or front) of the plane, then there is no intersection. - - var min, max; - - if ( plane.normal.x > 0 ) { - - min = plane.normal.x * this.min.x; - max = plane.normal.x * this.max.x; - - } else { - - min = plane.normal.x * this.max.x; - max = plane.normal.x * this.min.x; - - } - - if ( plane.normal.y > 0 ) { - - min += plane.normal.y * this.min.y; - max += plane.normal.y * this.max.y; - - } else { - - min += plane.normal.y * this.max.y; - max += plane.normal.y * this.min.y; - - } - - if ( plane.normal.z > 0 ) { - - min += plane.normal.z * this.min.z; - max += plane.normal.z * this.max.z; - - } else { - - min += plane.normal.z * this.max.z; - max += plane.normal.z * this.min.z; - - } - - return ( min <= plane.constant && max >= plane.constant ); - - }, - - clampPoint: function ( point, optionalTarget ) { - - var result = optionalTarget || new Vector3(); - return result.copy( point ).clamp( this.min, this.max ); - - }, - - distanceToPoint: function () { - - var v1 = new Vector3(); - - return function distanceToPoint( point ) { - - var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); - return clampedPoint.sub( point ).length(); - - }; - - }(), - - getBoundingSphere: function () { - - var v1 = new Vector3(); - - return function getBoundingSphere( optionalTarget ) { - - var result = optionalTarget || new Sphere(); - - this.getCenter( result.center ); - - result.radius = this.getSize( v1 ).length() * 0.5; - - return result; - - }; - - }(), - - intersect: function ( box ) { - - this.min.max( box.min ); - this.max.min( box.max ); - - // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. - if( this.isEmpty() ) this.makeEmpty(); - - return this; - - }, - - union: function ( box ) { - - this.min.min( box.min ); - this.max.max( box.max ); - - return this; - - }, - - applyMatrix4: function () { - - var points = [ - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3() - ]; - - return function applyMatrix4( matrix ) { - - // transform of empty box is an empty box. - if( this.isEmpty() ) return this; - - // NOTE: I am using a binary pattern to specify all 2^3 combinations below - points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 - points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 - points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 - points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 - points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 - points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 - points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 - points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 - - this.setFromPoints( points ); - - return this; - - }; - - }(), - - translate: function ( offset ) { - - this.min.add( offset ); - this.max.add( offset ); - - return this; - - }, - - equals: function ( box ) { - - return box.min.equals( this.min ) && box.max.equals( this.max ); - - } - - }; - - /** - * @author bhouston / http://clara.io - * @author mrdoob / http://mrdoob.com/ - */ - - function Sphere( center, radius ) { - - this.center = ( center !== undefined ) ? center : new Vector3(); - this.radius = ( radius !== undefined ) ? radius : 0; - - } - - Sphere.prototype = { - - constructor: Sphere, - - set: function ( center, radius ) { - - this.center.copy( center ); - this.radius = radius; - - return this; - - }, - - setFromPoints: function () { - - var box = new Box3(); - - return function setFromPoints( points, optionalCenter ) { - - var center = this.center; - - if ( optionalCenter !== undefined ) { - - center.copy( optionalCenter ); - - } else { - - box.setFromPoints( points ).getCenter( center ); - - } - - var maxRadiusSq = 0; - - for ( var i = 0, il = points.length; i < il; i ++ ) { - - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); - - } - - this.radius = Math.sqrt( maxRadiusSq ); - - return this; - - }; - - }(), - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( sphere ) { - - this.center.copy( sphere.center ); - this.radius = sphere.radius; - - return this; - - }, - - empty: function () { - - return ( this.radius <= 0 ); - - }, - - containsPoint: function ( point ) { - - return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); - - }, - - distanceToPoint: function ( point ) { - - return ( point.distanceTo( this.center ) - this.radius ); - - }, - - intersectsSphere: function ( sphere ) { - - var radiusSum = this.radius + sphere.radius; - - return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); - - }, - - intersectsBox: function ( box ) { - - return box.intersectsSphere( this ); - - }, - - intersectsPlane: function ( plane ) { - - // We use the following equation to compute the signed distance from - // the center of the sphere to the plane. - // - // distance = q * n - d - // - // If this distance is greater than the radius of the sphere, - // then there is no intersection. - - return Math.abs( this.center.dot( plane.normal ) - plane.constant ) <= this.radius; - - }, - - clampPoint: function ( point, optionalTarget ) { - - var deltaLengthSq = this.center.distanceToSquared( point ); - - var result = optionalTarget || new Vector3(); - - result.copy( point ); - - if ( deltaLengthSq > ( this.radius * this.radius ) ) { - - result.sub( this.center ).normalize(); - result.multiplyScalar( this.radius ).add( this.center ); - - } - - return result; - - }, - - getBoundingBox: function ( optionalTarget ) { - - var box = optionalTarget || new Box3(); - - box.set( this.center, this.center ); - box.expandByScalar( this.radius ); - - return box; - - }, - - applyMatrix4: function ( matrix ) { - - this.center.applyMatrix4( matrix ); - this.radius = this.radius * matrix.getMaxScaleOnAxis(); - - return this; - - }, - - translate: function ( offset ) { - - this.center.add( offset ); - - return this; - - }, - - equals: function ( sphere ) { - - return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); - - } - - }; - - /** - * @author alteredq / http://alteredqualia.com/ - * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://clara.io - * @author tschw - */ - - function Matrix3() { - - this.elements = new Float32Array( [ - - 1, 0, 0, - 0, 1, 0, - 0, 0, 1 - - ] ); - - if ( arguments.length > 0 ) { - - console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); - - } - - } - - Matrix3.prototype = { - - constructor: Matrix3, - - isMatrix3: true, - - set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { - - var te = this.elements; - - te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; - te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; - te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; - - return this; - - }, - - identity: function () { - - this.set( - - 1, 0, 0, - 0, 1, 0, - 0, 0, 1 - - ); - - return this; - - }, - - clone: function () { - - return new this.constructor().fromArray( this.elements ); - - }, - - copy: function ( m ) { - - var me = m.elements; - - this.set( - - me[ 0 ], me[ 3 ], me[ 6 ], - me[ 1 ], me[ 4 ], me[ 7 ], - me[ 2 ], me[ 5 ], me[ 8 ] - - ); - - return this; - - }, - - setFromMatrix4: function( m ) { - - var me = m.elements; - - this.set( - - me[ 0 ], me[ 4 ], me[ 8 ], - me[ 1 ], me[ 5 ], me[ 9 ], - me[ 2 ], me[ 6 ], me[ 10 ] - - ); - - return this; - - }, - - applyToVector3Array: function () { - - var v1; - - return function applyToVector3Array( array, offset, length ) { - - if ( v1 === undefined ) v1 = new Vector3(); - if ( offset === undefined ) offset = 0; - if ( length === undefined ) length = array.length; - - for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) { - - v1.fromArray( array, j ); - v1.applyMatrix3( this ); - v1.toArray( array, j ); - - } - - return array; - - }; - - }(), - - applyToBufferAttribute: function () { - - var v1; - - return function applyToBufferAttribute( attribute ) { - - if ( v1 === undefined ) v1 = new Vector3(); - - for ( var i = 0, l = attribute.count; i < l; i ++ ) { - - v1.x = attribute.getX( i ); - v1.y = attribute.getY( i ); - v1.z = attribute.getZ( i ); - - v1.applyMatrix3( this ); - - attribute.setXYZ( i, v1.x, v1.y, v1.z ); - - } - - return attribute; - - }; - - }(), - - multiplyScalar: function ( s ) { - - var te = this.elements; - - te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; - te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; - te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; - - return this; - - }, - - determinant: function () { - - var te = this.elements; - - var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], - d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], - g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; - - return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; - - }, - - getInverse: function ( matrix, throwOnDegenerate ) { - - if ( matrix && matrix.isMatrix4 ) { - - console.error( "THREE.Matrix3.getInverse no longer takes a Matrix4 argument." ); - - } - - var me = matrix.elements, - te = this.elements, - - n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], - n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ], - n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ], - - t11 = n33 * n22 - n32 * n23, - t12 = n32 * n13 - n33 * n12, - t13 = n23 * n12 - n22 * n13, - - det = n11 * t11 + n21 * t12 + n31 * t13; - - if ( det === 0 ) { - - var msg = "THREE.Matrix3.getInverse(): can't invert matrix, determinant is 0"; - - if ( throwOnDegenerate === true ) { - - throw new Error( msg ); - - } else { - - console.warn( msg ); - - } - - return this.identity(); - } - - var detInv = 1 / det; - - te[ 0 ] = t11 * detInv; - te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; - te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; - - te[ 3 ] = t12 * detInv; - te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; - te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; - - te[ 6 ] = t13 * detInv; - te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; - te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; - - return this; - - }, - - transpose: function () { - - var tmp, m = this.elements; - - tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; - tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; - tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; - - return this; - - }, - - getNormalMatrix: function ( matrix4 ) { - - return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose(); - - }, - - transposeIntoArray: function ( r ) { - - var m = this.elements; - - r[ 0 ] = m[ 0 ]; - r[ 1 ] = m[ 3 ]; - r[ 2 ] = m[ 6 ]; - r[ 3 ] = m[ 1 ]; - r[ 4 ] = m[ 4 ]; - r[ 5 ] = m[ 7 ]; - r[ 6 ] = m[ 2 ]; - r[ 7 ] = m[ 5 ]; - r[ 8 ] = m[ 8 ]; - - return this; - - }, - - fromArray: function ( array, offset ) { - - if ( offset === undefined ) offset = 0; - - for( var i = 0; i < 9; i ++ ) { - - this.elements[ i ] = array[ i + offset ]; - - } - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - var te = this.elements; - - array[ offset ] = te[ 0 ]; - array[ offset + 1 ] = te[ 1 ]; - array[ offset + 2 ] = te[ 2 ]; - - array[ offset + 3 ] = te[ 3 ]; - array[ offset + 4 ] = te[ 4 ]; - array[ offset + 5 ] = te[ 5 ]; - - array[ offset + 6 ] = te[ 6 ]; - array[ offset + 7 ] = te[ 7 ]; - array[ offset + 8 ] = te[ 8 ]; - - return array; - - } - - }; - - /** - * @author bhouston / http://clara.io - */ - - function Plane( normal, constant ) { - - this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 ); - this.constant = ( constant !== undefined ) ? constant : 0; - - } - - Plane.prototype = { - - constructor: Plane, - - set: function ( normal, constant ) { - - this.normal.copy( normal ); - this.constant = constant; - - return this; - - }, - - setComponents: function ( x, y, z, w ) { - - this.normal.set( x, y, z ); - this.constant = w; - - return this; - - }, - - setFromNormalAndCoplanarPoint: function ( normal, point ) { - - this.normal.copy( normal ); - this.constant = - point.dot( this.normal ); // must be this.normal, not normal, as this.normal is normalized - - return this; - - }, - - setFromCoplanarPoints: function () { - - var v1 = new Vector3(); - var v2 = new Vector3(); - - return function setFromCoplanarPoints( a, b, c ) { - - var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize(); - - // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? - - this.setFromNormalAndCoplanarPoint( normal, a ); - - return this; - - }; - - }(), - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( plane ) { - - this.normal.copy( plane.normal ); - this.constant = plane.constant; - - return this; - - }, - - normalize: function () { - - // Note: will lead to a divide by zero if the plane is invalid. - - var inverseNormalLength = 1.0 / this.normal.length(); - this.normal.multiplyScalar( inverseNormalLength ); - this.constant *= inverseNormalLength; - - return this; - - }, - - negate: function () { - - this.constant *= - 1; - this.normal.negate(); - - return this; - - }, - - distanceToPoint: function ( point ) { - - return this.normal.dot( point ) + this.constant; - - }, - - distanceToSphere: function ( sphere ) { - - return this.distanceToPoint( sphere.center ) - sphere.radius; - - }, - - projectPoint: function ( point, optionalTarget ) { - - return this.orthoPoint( point, optionalTarget ).sub( point ).negate(); - - }, - - orthoPoint: function ( point, optionalTarget ) { - - var perpendicularMagnitude = this.distanceToPoint( point ); - - var result = optionalTarget || new Vector3(); - return result.copy( this.normal ).multiplyScalar( perpendicularMagnitude ); - - }, - - intersectLine: function () { - - var v1 = new Vector3(); - - return function intersectLine( line, optionalTarget ) { - - var result = optionalTarget || new Vector3(); - - var direction = line.delta( v1 ); - - var denominator = this.normal.dot( direction ); - - if ( denominator === 0 ) { - - // line is coplanar, return origin - if ( this.distanceToPoint( line.start ) === 0 ) { - - return result.copy( line.start ); - - } - - // Unsure if this is the correct method to handle this case. - return undefined; - - } - - var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; - - if ( t < 0 || t > 1 ) { - - return undefined; - - } - - return result.copy( direction ).multiplyScalar( t ).add( line.start ); - - }; - - }(), - - intersectsLine: function ( line ) { - - // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. - - var startSign = this.distanceToPoint( line.start ); - var endSign = this.distanceToPoint( line.end ); - - return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); - - }, - - intersectsBox: function ( box ) { - - return box.intersectsPlane( this ); - - }, - - intersectsSphere: function ( sphere ) { - - return sphere.intersectsPlane( this ); - - }, - - coplanarPoint: function ( optionalTarget ) { - - var result = optionalTarget || new Vector3(); - return result.copy( this.normal ).multiplyScalar( - this.constant ); - - }, - - applyMatrix4: function () { - - var v1 = new Vector3(); - var m1 = new Matrix3(); - - return function applyMatrix4( matrix, optionalNormalMatrix ) { - - var referencePoint = this.coplanarPoint( v1 ).applyMatrix4( matrix ); - - // transform normal based on theory here: - // http://www.songho.ca/opengl/gl_normaltransform.html - var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix ); - var normal = this.normal.applyMatrix3( normalMatrix ).normalize(); - - // recalculate constant (like in setFromNormalAndCoplanarPoint) - this.constant = - referencePoint.dot( normal ); - - return this; - - }; - - }(), - - translate: function ( offset ) { - - this.constant = this.constant - offset.dot( this.normal ); - - return this; - - }, - - equals: function ( plane ) { - - return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); - - } - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author bhouston / http://clara.io - */ - - function Frustum( p0, p1, p2, p3, p4, p5 ) { - - this.planes = [ - - ( p0 !== undefined ) ? p0 : new Plane(), - ( p1 !== undefined ) ? p1 : new Plane(), - ( p2 !== undefined ) ? p2 : new Plane(), - ( p3 !== undefined ) ? p3 : new Plane(), - ( p4 !== undefined ) ? p4 : new Plane(), - ( p5 !== undefined ) ? p5 : new Plane() - - ]; - - } - - Frustum.prototype = { - - constructor: Frustum, - - set: function ( p0, p1, p2, p3, p4, p5 ) { - - var planes = this.planes; - - planes[ 0 ].copy( p0 ); - planes[ 1 ].copy( p1 ); - planes[ 2 ].copy( p2 ); - planes[ 3 ].copy( p3 ); - planes[ 4 ].copy( p4 ); - planes[ 5 ].copy( p5 ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( frustum ) { - - var planes = this.planes; - - for ( var i = 0; i < 6; i ++ ) { - - planes[ i ].copy( frustum.planes[ i ] ); - - } - - return this; - - }, - - setFromMatrix: function ( m ) { - - var planes = this.planes; - var me = m.elements; - var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; - var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; - var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; - var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; - - planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); - planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); - planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); - planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); - planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); - planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); - - return this; - - }, - - intersectsObject: function () { - - var sphere = new Sphere(); - - return function intersectsObject( object ) { - - var geometry = object.geometry; - - if ( geometry.boundingSphere === null ) - geometry.computeBoundingSphere(); - - sphere.copy( geometry.boundingSphere ) - .applyMatrix4( object.matrixWorld ); - - return this.intersectsSphere( sphere ); - - }; - - }(), - - intersectsSprite: function () { - - var sphere = new Sphere(); - - return function intersectsSprite( sprite ) { - - sphere.center.set( 0, 0, 0 ); - sphere.radius = 0.7071067811865476; - sphere.applyMatrix4( sprite.matrixWorld ); - - return this.intersectsSphere( sphere ); - - }; - - }(), - - intersectsSphere: function ( sphere ) { - - var planes = this.planes; - var center = sphere.center; - var negRadius = - sphere.radius; - - for ( var i = 0; i < 6; i ++ ) { - - var distance = planes[ i ].distanceToPoint( center ); - - if ( distance < negRadius ) { - - return false; - - } - - } - - return true; - - }, - - intersectsBox: function () { - - var p1 = new Vector3(), - p2 = new Vector3(); - - return function intersectsBox( box ) { - - var planes = this.planes; - - for ( var i = 0; i < 6 ; i ++ ) { - - var plane = planes[ i ]; - - p1.x = plane.normal.x > 0 ? box.min.x : box.max.x; - p2.x = plane.normal.x > 0 ? box.max.x : box.min.x; - p1.y = plane.normal.y > 0 ? box.min.y : box.max.y; - p2.y = plane.normal.y > 0 ? box.max.y : box.min.y; - p1.z = plane.normal.z > 0 ? box.min.z : box.max.z; - p2.z = plane.normal.z > 0 ? box.max.z : box.min.z; - - var d1 = plane.distanceToPoint( p1 ); - var d2 = plane.distanceToPoint( p2 ); - - // if both outside plane, no intersection - - if ( d1 < 0 && d2 < 0 ) { - - return false; - - } - - } - - return true; - - }; - - }(), - - - containsPoint: function ( point ) { - - var planes = this.planes; - - for ( var i = 0; i < 6; i ++ ) { - - if ( planes[ i ].distanceToPoint( point ) < 0 ) { - - return false; - - } - - } - - return true; - - } - - }; - - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLShadowMap( _renderer, _lights, _objects, capabilities ) { - - var _gl = _renderer.context, - _state = _renderer.state, - _frustum = new Frustum(), - _projScreenMatrix = new Matrix4(), - - _lightShadows = _lights.shadows, - - _shadowMapSize = new Vector2(), - _maxShadowMapSize = new Vector2( capabilities.maxTextureSize, capabilities.maxTextureSize ), - - _lookTarget = new Vector3(), - _lightPositionWorld = new Vector3(), - - _renderList = [], - - _MorphingFlag = 1, - _SkinningFlag = 2, - - _NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1, - - _depthMaterials = new Array( _NumberOfMaterialVariants ), - _distanceMaterials = new Array( _NumberOfMaterialVariants ), - - _materialCache = {}; - - var cubeDirections = [ - new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), - new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) - ]; - - var cubeUps = [ - new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), - new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) - ]; - - var cube2DViewPorts = [ - new Vector4(), new Vector4(), new Vector4(), - new Vector4(), new Vector4(), new Vector4() - ]; - - // init - - var depthMaterialTemplate = new MeshDepthMaterial(); - depthMaterialTemplate.depthPacking = RGBADepthPacking; - depthMaterialTemplate.clipping = true; - - var distanceShader = ShaderLib[ "distanceRGBA" ]; - var distanceUniforms = UniformsUtils.clone( distanceShader.uniforms ); - - for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) { - - var useMorphing = ( i & _MorphingFlag ) !== 0; - var useSkinning = ( i & _SkinningFlag ) !== 0; - - var depthMaterial = depthMaterialTemplate.clone(); - depthMaterial.morphTargets = useMorphing; - depthMaterial.skinning = useSkinning; - - _depthMaterials[ i ] = depthMaterial; - - var distanceMaterial = new ShaderMaterial( { - defines: { - 'USE_SHADOWMAP': '' - }, - uniforms: distanceUniforms, - vertexShader: distanceShader.vertexShader, - fragmentShader: distanceShader.fragmentShader, - morphTargets: useMorphing, - skinning: useSkinning, - clipping: true - } ); - - _distanceMaterials[ i ] = distanceMaterial; - - } - - // - - var scope = this; - - this.enabled = false; - - this.autoUpdate = true; - this.needsUpdate = false; - - this.type = PCFShadowMap; - - this.renderReverseSided = true; - this.renderSingleSided = true; - - this.render = function ( scene, camera ) { - - if ( scope.enabled === false ) return; - if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; - - if ( _lightShadows.length === 0 ) return; - - // Set GL state for depth map. - _state.buffers.color.setClear( 1, 1, 1, 1 ); - _state.disable( _gl.BLEND ); - _state.setDepthTest( true ); - _state.setScissorTest( false ); - - // render depth map - - var faceCount, isPointLight; - - for ( var i = 0, il = _lightShadows.length; i < il; i ++ ) { - - var light = _lightShadows[ i ]; - var shadow = light.shadow; - - if ( shadow === undefined ) { - - console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); - continue; - - } - - var shadowCamera = shadow.camera; - - _shadowMapSize.copy( shadow.mapSize ); - _shadowMapSize.min( _maxShadowMapSize ); - - if ( light && light.isPointLight ) { - - faceCount = 6; - isPointLight = true; - - var vpWidth = _shadowMapSize.x; - var vpHeight = _shadowMapSize.y; - - // These viewports map a cube-map onto a 2D texture with the - // following orientation: - // - // xzXZ - // y Y - // - // X - Positive x direction - // x - Negative x direction - // Y - Positive y direction - // y - Negative y direction - // Z - Positive z direction - // z - Negative z direction - - // positive X - cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight ); - // negative X - cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight ); - // positive Z - cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight ); - // negative Z - cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight ); - // positive Y - cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight ); - // negative Y - cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight ); - - _shadowMapSize.x *= 4.0; - _shadowMapSize.y *= 2.0; - - } else { - - faceCount = 1; - isPointLight = false; - - } - - if ( shadow.map === null ) { - - var pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; - - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - - shadowCamera.updateProjectionMatrix(); - - } - - if ( shadow.isSpotLightShadow ) { - - shadow.update( light ); - - } - - // TODO (abelnation / sam-g-steel): is this needed? - if (shadow && shadow.isRectAreaLightShadow ) { - - shadow.update( light ); - - } - - var shadowMap = shadow.map; - var shadowMatrix = shadow.matrix; - - _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); - shadowCamera.position.copy( _lightPositionWorld ); - - _renderer.setRenderTarget( shadowMap ); - _renderer.clear(); - - // render shadow map for each cube face (if omni-directional) or - // run a single pass if not - - for ( var face = 0; face < faceCount; face ++ ) { - - if ( isPointLight ) { - - _lookTarget.copy( shadowCamera.position ); - _lookTarget.add( cubeDirections[ face ] ); - shadowCamera.up.copy( cubeUps[ face ] ); - shadowCamera.lookAt( _lookTarget ); - - var vpDimensions = cube2DViewPorts[ face ]; - _state.viewport( vpDimensions ); - - } else { - - _lookTarget.setFromMatrixPosition( light.target.matrixWorld ); - shadowCamera.lookAt( _lookTarget ); - - } - - shadowCamera.updateMatrixWorld(); - shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); - - // compute shadow matrix - - shadowMatrix.set( - 0.5, 0.0, 0.0, 0.5, - 0.0, 0.5, 0.0, 0.5, - 0.0, 0.0, 0.5, 0.5, - 0.0, 0.0, 0.0, 1.0 - ); - - shadowMatrix.multiply( shadowCamera.projectionMatrix ); - shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); - - // update camera matrices and frustum - - _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); - _frustum.setFromMatrix( _projScreenMatrix ); - - // set object matrices & frustum culling - - _renderList.length = 0; - - projectObject( scene, camera, shadowCamera ); - - // render shadow map - // render regular objects - - for ( var j = 0, jl = _renderList.length; j < jl; j ++ ) { - - var object = _renderList[ j ]; - var geometry = _objects.update( object ); - var material = object.material; - - if ( material && material.isMultiMaterial ) { - - var groups = geometry.groups; - var materials = material.materials; - - for ( var k = 0, kl = groups.length; k < kl; k ++ ) { - - var group = groups[ k ]; - var groupMaterial = materials[ group.materialIndex ]; - - if ( groupMaterial.visible === true ) { - - var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld ); - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); - - } - - } - - } else { - - var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld ); - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); - - } - - } - - } - - } - - // Restore GL state. - var clearColor = _renderer.getClearColor(), - clearAlpha = _renderer.getClearAlpha(); - _renderer.setClearColor( clearColor, clearAlpha ); - - scope.needsUpdate = false; - - }; - - function getDepthMaterial( object, material, isPointLight, lightPositionWorld ) { - - var geometry = object.geometry; - - var result = null; - - var materialVariants = _depthMaterials; - var customMaterial = object.customDepthMaterial; - - if ( isPointLight ) { - - materialVariants = _distanceMaterials; - customMaterial = object.customDistanceMaterial; - - } - - if ( ! customMaterial ) { - - var useMorphing = false; - - if ( material.morphTargets ) { - - if ( geometry && geometry.isBufferGeometry ) { - - useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0; - - } else if ( geometry && geometry.isGeometry ) { - - useMorphing = geometry.morphTargets && geometry.morphTargets.length > 0; - - } - - } - - var useSkinning = object.isSkinnedMesh && material.skinning; - - var variantIndex = 0; - - if ( useMorphing ) variantIndex |= _MorphingFlag; - if ( useSkinning ) variantIndex |= _SkinningFlag; - - result = materialVariants[ variantIndex ]; - - } else { - - result = customMaterial; - - } - - if ( _renderer.localClippingEnabled && - material.clipShadows === true && - material.clippingPlanes.length !== 0 ) { - - // in this case we need a unique material instance reflecting the - // appropriate state - - var keyA = result.uuid, keyB = material.uuid; - - var materialsForVariant = _materialCache[ keyA ]; - - if ( materialsForVariant === undefined ) { - - materialsForVariant = {}; - _materialCache[ keyA ] = materialsForVariant; - - } - - var cachedMaterial = materialsForVariant[ keyB ]; - - if ( cachedMaterial === undefined ) { - - cachedMaterial = result.clone(); - materialsForVariant[ keyB ] = cachedMaterial; - - } - - result = cachedMaterial; - - } - - result.visible = material.visible; - result.wireframe = material.wireframe; - - var side = material.side; - - if ( scope.renderSingleSided && side == DoubleSide ) { - - side = FrontSide; - - } - - if ( scope.renderReverseSided ) { - - if ( side === FrontSide ) side = BackSide; - else if ( side === BackSide ) side = FrontSide; - - } - - result.side = side; - - result.clipShadows = material.clipShadows; - result.clippingPlanes = material.clippingPlanes; - - result.wireframeLinewidth = material.wireframeLinewidth; - result.linewidth = material.linewidth; - - if ( isPointLight && result.uniforms.lightPos !== undefined ) { - - result.uniforms.lightPos.value.copy( lightPositionWorld ); - - } - - return result; - - } - - function projectObject( object, camera, shadowCamera ) { - - if ( object.visible === false ) return; - - var visible = ( object.layers.mask & camera.layers.mask ) !== 0; - - if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { - - if ( object.castShadow && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) { - - var material = object.material; - - if ( material.visible === true ) { - - object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); - _renderList.push( object ); - - } - - } - - } - - var children = object.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - projectObject( children[ i ], camera, shadowCamera ); - - } - - } - - } - - /** - * @author bhouston / http://clara.io - */ - - function Ray( origin, direction ) { - - this.origin = ( origin !== undefined ) ? origin : new Vector3(); - this.direction = ( direction !== undefined ) ? direction : new Vector3(); - - } - - Ray.prototype = { - - constructor: Ray, - - set: function ( origin, direction ) { - - this.origin.copy( origin ); - this.direction.copy( direction ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( ray ) { - - this.origin.copy( ray.origin ); - this.direction.copy( ray.direction ); - - return this; - - }, - - at: function ( t, optionalTarget ) { - - var result = optionalTarget || new Vector3(); - - return result.copy( this.direction ).multiplyScalar( t ).add( this.origin ); - - }, - - lookAt: function ( v ) { - - this.direction.copy( v ).sub( this.origin ).normalize(); - - return this; - - }, - - recast: function () { - - var v1 = new Vector3(); - - return function recast( t ) { - - this.origin.copy( this.at( t, v1 ) ); - - return this; - - }; - - }(), - - closestPointToPoint: function ( point, optionalTarget ) { - - var result = optionalTarget || new Vector3(); - result.subVectors( point, this.origin ); - var directionDistance = result.dot( this.direction ); - - if ( directionDistance < 0 ) { - - return result.copy( this.origin ); - - } - - return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); - - }, - - distanceToPoint: function ( point ) { - - return Math.sqrt( this.distanceSqToPoint( point ) ); - - }, - - distanceSqToPoint: function () { - - var v1 = new Vector3(); - - return function distanceSqToPoint( point ) { - - var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction ); - - // point behind the ray - - if ( directionDistance < 0 ) { - - return this.origin.distanceToSquared( point ); - - } - - v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); - - return v1.distanceToSquared( point ); - - }; - - }(), - - distanceSqToSegment: function () { - - var segCenter = new Vector3(); - var segDir = new Vector3(); - var diff = new Vector3(); - - return function distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { - - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h - // It returns the min distance between the ray and the segment - // defined by v0 and v1 - // It can also set two optional targets : - // - The closest point on the ray - // - The closest point on the segment - - segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); - segDir.copy( v1 ).sub( v0 ).normalize(); - diff.copy( this.origin ).sub( segCenter ); - - var segExtent = v0.distanceTo( v1 ) * 0.5; - var a01 = - this.direction.dot( segDir ); - var b0 = diff.dot( this.direction ); - var b1 = - diff.dot( segDir ); - var c = diff.lengthSq(); - var det = Math.abs( 1 - a01 * a01 ); - var s0, s1, sqrDist, extDet; - - if ( det > 0 ) { - - // The ray and segment are not parallel. - - s0 = a01 * b1 - b0; - s1 = a01 * b0 - b1; - extDet = segExtent * det; - - if ( s0 >= 0 ) { - - if ( s1 >= - extDet ) { - - if ( s1 <= extDet ) { - - // region 0 - // Minimum at interior points of ray and segment. - - var invDet = 1 / det; - s0 *= invDet; - s1 *= invDet; - sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; - - } else { - - // region 1 - - s1 = segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - } else { - - // region 5 - - s1 = - segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - } else { - - if ( s1 <= - extDet ) { - - // region 4 - - s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } else if ( s1 <= extDet ) { - - // region 3 - - s0 = 0; - s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = s1 * ( s1 + 2 * b1 ) + c; - - } else { - - // region 2 - - s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - } - - } else { - - // Ray and segment are parallel. - - s1 = ( a01 > 0 ) ? - segExtent : segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - if ( optionalPointOnRay ) { - - optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); - - } - - if ( optionalPointOnSegment ) { - - optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter ); - - } - - return sqrDist; - - }; - - }(), - - intersectSphere: function () { - - var v1 = new Vector3(); - - return function intersectSphere( sphere, optionalTarget ) { - - v1.subVectors( sphere.center, this.origin ); - var tca = v1.dot( this.direction ); - var d2 = v1.dot( v1 ) - tca * tca; - var radius2 = sphere.radius * sphere.radius; - - if ( d2 > radius2 ) return null; - - var thc = Math.sqrt( radius2 - d2 ); - - // t0 = first intersect point - entrance on front of sphere - var t0 = tca - thc; - - // t1 = second intersect point - exit point on back of sphere - var t1 = tca + thc; - - // test to see if both t0 and t1 are behind the ray - if so, return null - if ( t0 < 0 && t1 < 0 ) return null; - - // test to see if t0 is behind the ray: - // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, - // in order to always return an intersect point that is in front of the ray. - if ( t0 < 0 ) return this.at( t1, optionalTarget ); - - // else t0 is in front of the ray, so return the first collision point scaled by t0 - return this.at( t0, optionalTarget ); - - }; - - }(), - - intersectsSphere: function ( sphere ) { - - return this.distanceToPoint( sphere.center ) <= sphere.radius; - - }, - - distanceToPlane: function ( plane ) { - - var denominator = plane.normal.dot( this.direction ); - - if ( denominator === 0 ) { - - // line is coplanar, return origin - if ( plane.distanceToPoint( this.origin ) === 0 ) { - - return 0; - - } - - // Null is preferable to undefined since undefined means.... it is undefined - - return null; - - } - - var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; - - // Return if the ray never intersects the plane - - return t >= 0 ? t : null; - - }, - - intersectPlane: function ( plane, optionalTarget ) { - - var t = this.distanceToPlane( plane ); - - if ( t === null ) { - - return null; - - } - - return this.at( t, optionalTarget ); - - }, - - - - intersectsPlane: function ( plane ) { - - // check if the ray lies on the plane first - - var distToPoint = plane.distanceToPoint( this.origin ); - - if ( distToPoint === 0 ) { - - return true; - - } - - var denominator = plane.normal.dot( this.direction ); - - if ( denominator * distToPoint < 0 ) { - - return true; - - } - - // ray origin is behind the plane (and is pointing behind it) - - return false; - - }, - - intersectBox: function ( box, optionalTarget ) { - - var tmin, tmax, tymin, tymax, tzmin, tzmax; - - var invdirx = 1 / this.direction.x, - invdiry = 1 / this.direction.y, - invdirz = 1 / this.direction.z; - - var origin = this.origin; - - if ( invdirx >= 0 ) { - - tmin = ( box.min.x - origin.x ) * invdirx; - tmax = ( box.max.x - origin.x ) * invdirx; - - } else { - - tmin = ( box.max.x - origin.x ) * invdirx; - tmax = ( box.min.x - origin.x ) * invdirx; - - } - - if ( invdiry >= 0 ) { - - tymin = ( box.min.y - origin.y ) * invdiry; - tymax = ( box.max.y - origin.y ) * invdiry; - - } else { - - tymin = ( box.max.y - origin.y ) * invdiry; - tymax = ( box.min.y - origin.y ) * invdiry; - - } - - if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; - - // These lines also handle the case where tmin or tmax is NaN - // (result of 0 * Infinity). x !== x returns true if x is NaN - - if ( tymin > tmin || tmin !== tmin ) tmin = tymin; - - if ( tymax < tmax || tmax !== tmax ) tmax = tymax; - - if ( invdirz >= 0 ) { - - tzmin = ( box.min.z - origin.z ) * invdirz; - tzmax = ( box.max.z - origin.z ) * invdirz; - - } else { - - tzmin = ( box.max.z - origin.z ) * invdirz; - tzmax = ( box.min.z - origin.z ) * invdirz; - - } - - if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; - - if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; - - if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; - - //return point closest to the ray (positive side) - - if ( tmax < 0 ) return null; - - return this.at( tmin >= 0 ? tmin : tmax, optionalTarget ); - - }, - - intersectsBox: ( function () { - - var v = new Vector3(); - - return function intersectsBox( box ) { - - return this.intersectBox( box, v ) !== null; - - }; - - } )(), - - intersectTriangle: function () { - - // Compute the offset origin, edges, and normal. - var diff = new Vector3(); - var edge1 = new Vector3(); - var edge2 = new Vector3(); - var normal = new Vector3(); - - return function intersectTriangle( a, b, c, backfaceCulling, optionalTarget ) { - - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h - - edge1.subVectors( b, a ); - edge2.subVectors( c, a ); - normal.crossVectors( edge1, edge2 ); - - // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, - // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by - // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) - // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) - // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) - var DdN = this.direction.dot( normal ); - var sign; - - if ( DdN > 0 ) { - - if ( backfaceCulling ) return null; - sign = 1; - - } else if ( DdN < 0 ) { - - sign = - 1; - DdN = - DdN; - - } else { - - return null; - - } - - diff.subVectors( this.origin, a ); - var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) ); - - // b1 < 0, no intersection - if ( DdQxE2 < 0 ) { - - return null; - - } - - var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) ); - - // b2 < 0, no intersection - if ( DdE1xQ < 0 ) { - - return null; - - } - - // b1+b2 > 1, no intersection - if ( DdQxE2 + DdE1xQ > DdN ) { - - return null; - - } - - // Line intersects triangle, check if ray does. - var QdN = - sign * diff.dot( normal ); - - // t < 0, no intersection - if ( QdN < 0 ) { - - return null; - - } - - // Ray intersects triangle. - return this.at( QdN / DdN, optionalTarget ); - - }; - - }(), - - applyMatrix4: function ( matrix4 ) { - - this.direction.add( this.origin ).applyMatrix4( matrix4 ); - this.origin.applyMatrix4( matrix4 ); - this.direction.sub( this.origin ); - this.direction.normalize(); - - return this; - - }, - - equals: function ( ray ) { - - return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); - - } - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://clara.io - */ - - function Euler( x, y, z, order ) { - - this._x = x || 0; - this._y = y || 0; - this._z = z || 0; - this._order = order || Euler.DefaultOrder; - - } - - Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; - - Euler.DefaultOrder = 'XYZ'; - - Euler.prototype = { - - constructor: Euler, - - isEuler: true, - - get x () { - - return this._x; - - }, - - set x ( value ) { - - this._x = value; - this.onChangeCallback(); - - }, - - get y () { - - return this._y; - - }, - - set y ( value ) { - - this._y = value; - this.onChangeCallback(); - - }, - - get z () { - - return this._z; - - }, - - set z ( value ) { - - this._z = value; - this.onChangeCallback(); - - }, - - get order () { - - return this._order; - - }, - - set order ( value ) { - - this._order = value; - this.onChangeCallback(); - - }, - - set: function ( x, y, z, order ) { - - this._x = x; - this._y = y; - this._z = z; - this._order = order || this._order; - - this.onChangeCallback(); - - return this; - - }, - - clone: function () { - - return new this.constructor( this._x, this._y, this._z, this._order ); - - }, - - copy: function ( euler ) { - - this._x = euler._x; - this._y = euler._y; - this._z = euler._z; - this._order = euler._order; - - this.onChangeCallback(); - - return this; - - }, - - setFromRotationMatrix: function ( m, order, update ) { - - var clamp = _Math.clamp; - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - var te = m.elements; - var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; - var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; - var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; - - order = order || this._order; - - if ( order === 'XYZ' ) { - - this._y = Math.asin( clamp( m13, - 1, 1 ) ); - - if ( Math.abs( m13 ) < 0.99999 ) { - - this._x = Math.atan2( - m23, m33 ); - this._z = Math.atan2( - m12, m11 ); - - } else { - - this._x = Math.atan2( m32, m22 ); - this._z = 0; - - } - - } else if ( order === 'YXZ' ) { - - this._x = Math.asin( - clamp( m23, - 1, 1 ) ); - - if ( Math.abs( m23 ) < 0.99999 ) { - - this._y = Math.atan2( m13, m33 ); - this._z = Math.atan2( m21, m22 ); - - } else { - - this._y = Math.atan2( - m31, m11 ); - this._z = 0; - - } - - } else if ( order === 'ZXY' ) { - - this._x = Math.asin( clamp( m32, - 1, 1 ) ); - - if ( Math.abs( m32 ) < 0.99999 ) { - - this._y = Math.atan2( - m31, m33 ); - this._z = Math.atan2( - m12, m22 ); - - } else { - - this._y = 0; - this._z = Math.atan2( m21, m11 ); - - } - - } else if ( order === 'ZYX' ) { - - this._y = Math.asin( - clamp( m31, - 1, 1 ) ); - - if ( Math.abs( m31 ) < 0.99999 ) { - - this._x = Math.atan2( m32, m33 ); - this._z = Math.atan2( m21, m11 ); - - } else { - - this._x = 0; - this._z = Math.atan2( - m12, m22 ); - - } - - } else if ( order === 'YZX' ) { - - this._z = Math.asin( clamp( m21, - 1, 1 ) ); - - if ( Math.abs( m21 ) < 0.99999 ) { - - this._x = Math.atan2( - m23, m22 ); - this._y = Math.atan2( - m31, m11 ); - - } else { - - this._x = 0; - this._y = Math.atan2( m13, m33 ); - - } - - } else if ( order === 'XZY' ) { - - this._z = Math.asin( - clamp( m12, - 1, 1 ) ); - - if ( Math.abs( m12 ) < 0.99999 ) { - - this._x = Math.atan2( m32, m22 ); - this._y = Math.atan2( m13, m11 ); - - } else { - - this._x = Math.atan2( - m23, m33 ); - this._y = 0; - - } - - } else { - - console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order ); - - } - - this._order = order; - - if ( update !== false ) this.onChangeCallback(); - - return this; - - }, - - setFromQuaternion: function () { - - var matrix; - - return function setFromQuaternion( q, order, update ) { - - if ( matrix === undefined ) matrix = new Matrix4(); - - matrix.makeRotationFromQuaternion( q ); - - return this.setFromRotationMatrix( matrix, order, update ); - - }; - - }(), - - setFromVector3: function ( v, order ) { - - return this.set( v.x, v.y, v.z, order || this._order ); - - }, - - reorder: function () { - - // WARNING: this discards revolution information -bhouston - - var q = new Quaternion(); - - return function reorder( newOrder ) { - - q.setFromEuler( this ); - - return this.setFromQuaternion( q, newOrder ); - - }; - - }(), - - equals: function ( euler ) { - - return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); - - }, - - fromArray: function ( array ) { - - this._x = array[ 0 ]; - this._y = array[ 1 ]; - this._z = array[ 2 ]; - if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; - - this.onChangeCallback(); - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._order; - - return array; - - }, - - toVector3: function ( optionalResult ) { - - if ( optionalResult ) { - - return optionalResult.set( this._x, this._y, this._z ); - - } else { - - return new Vector3( this._x, this._y, this._z ); - - } - - }, - - onChange: function ( callback ) { - - this.onChangeCallback = callback; - - return this; - - }, - - onChangeCallback: function () {} - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function Layers() { - - this.mask = 1; - - } - - Layers.prototype = { - - constructor: Layers, - - set: function ( channel ) { - - this.mask = 1 << channel; - - }, - - enable: function ( channel ) { - - this.mask |= 1 << channel; - - }, - - toggle: function ( channel ) { - - this.mask ^= 1 << channel; - - }, - - disable: function ( channel ) { - - this.mask &= ~ ( 1 << channel ); - - }, - - test: function ( layers ) { - - return ( this.mask & layers.mask ) !== 0; - - } - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author WestLangley / http://github.com/WestLangley - * @author elephantatwork / www.elephantatwork.ch - */ - - var object3DId = 0; - - function Object3D() { - - Object.defineProperty( this, 'id', { value: object3DId ++ } ); - - this.uuid = _Math.generateUUID(); - - this.name = ''; - this.type = 'Object3D'; - - this.parent = null; - this.children = []; - - this.up = Object3D.DefaultUp.clone(); - - var position = new Vector3(); - var rotation = new Euler(); - var quaternion = new Quaternion(); - var scale = new Vector3( 1, 1, 1 ); - - function onRotationChange() { - - quaternion.setFromEuler( rotation, false ); - - } - - function onQuaternionChange() { - - rotation.setFromQuaternion( quaternion, undefined, false ); - - } - - rotation.onChange( onRotationChange ); - quaternion.onChange( onQuaternionChange ); - - Object.defineProperties( this, { - position: { - enumerable: true, - value: position - }, - rotation: { - enumerable: true, - value: rotation - }, - quaternion: { - enumerable: true, - value: quaternion - }, - scale: { - enumerable: true, - value: scale - }, - modelViewMatrix: { - value: new Matrix4() - }, - normalMatrix: { - value: new Matrix3() - } - } ); - - this.matrix = new Matrix4(); - this.matrixWorld = new Matrix4(); - - this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; - this.matrixWorldNeedsUpdate = false; - - this.layers = new Layers(); - this.visible = true; - - this.castShadow = false; - this.receiveShadow = false; - - this.frustumCulled = true; - this.renderOrder = 0; - - this.userData = {}; - - this.onBeforeRender = function () {}; - this.onAfterRender = function () {}; - - } - - Object3D.DefaultUp = new Vector3( 0, 1, 0 ); - Object3D.DefaultMatrixAutoUpdate = true; - - Object.assign( Object3D.prototype, EventDispatcher.prototype, { - - isObject3D: true, - - applyMatrix: function ( matrix ) { - - this.matrix.multiplyMatrices( matrix, this.matrix ); - - this.matrix.decompose( this.position, this.quaternion, this.scale ); - - }, - - setRotationFromAxisAngle: function ( axis, angle ) { - - // assumes axis is normalized - - this.quaternion.setFromAxisAngle( axis, angle ); - - }, - - setRotationFromEuler: function ( euler ) { - - this.quaternion.setFromEuler( euler, true ); - - }, - - setRotationFromMatrix: function ( m ) { - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - this.quaternion.setFromRotationMatrix( m ); - - }, - - setRotationFromQuaternion: function ( q ) { - - // assumes q is normalized - - this.quaternion.copy( q ); - - }, - - rotateOnAxis: function () { - - // rotate object on axis in object space - // axis is assumed to be normalized - - var q1 = new Quaternion(); - - return function rotateOnAxis( axis, angle ) { - - q1.setFromAxisAngle( axis, angle ); - - this.quaternion.multiply( q1 ); - - return this; - - }; - - }(), - - rotateX: function () { - - var v1 = new Vector3( 1, 0, 0 ); - - return function rotateX( angle ) { - - return this.rotateOnAxis( v1, angle ); - - }; - - }(), - - rotateY: function () { - - var v1 = new Vector3( 0, 1, 0 ); - - return function rotateY( angle ) { - - return this.rotateOnAxis( v1, angle ); - - }; - - }(), - - rotateZ: function () { - - var v1 = new Vector3( 0, 0, 1 ); - - return function rotateZ( angle ) { - - return this.rotateOnAxis( v1, angle ); - - }; - - }(), - - translateOnAxis: function () { - - // translate object by distance along axis in object space - // axis is assumed to be normalized - - var v1 = new Vector3(); - - return function translateOnAxis( axis, distance ) { - - v1.copy( axis ).applyQuaternion( this.quaternion ); - - this.position.add( v1.multiplyScalar( distance ) ); - - return this; - - }; - - }(), - - translateX: function () { - - var v1 = new Vector3( 1, 0, 0 ); - - return function translateX( distance ) { - - return this.translateOnAxis( v1, distance ); - - }; - - }(), - - translateY: function () { - - var v1 = new Vector3( 0, 1, 0 ); - - return function translateY( distance ) { - - return this.translateOnAxis( v1, distance ); - - }; - - }(), - - translateZ: function () { - - var v1 = new Vector3( 0, 0, 1 ); - - return function translateZ( distance ) { - - return this.translateOnAxis( v1, distance ); - - }; - - }(), - - localToWorld: function ( vector ) { - - return vector.applyMatrix4( this.matrixWorld ); - - }, - - worldToLocal: function () { - - var m1 = new Matrix4(); - - return function worldToLocal( vector ) { - - return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) ); - - }; - - }(), - - lookAt: function () { - - // This routine does not support objects with rotated and/or translated parent(s) - - var m1 = new Matrix4(); - - return function lookAt( vector ) { - - m1.lookAt( vector, this.position, this.up ); - - this.quaternion.setFromRotationMatrix( m1 ); - - }; - - }(), - - add: function ( object ) { - - if ( arguments.length > 1 ) { - - for ( var i = 0; i < arguments.length; i ++ ) { - - this.add( arguments[ i ] ); - - } - - return this; - - } - - if ( object === this ) { - - console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object ); - return this; - - } - - if ( ( object && object.isObject3D ) ) { - - if ( object.parent !== null ) { - - object.parent.remove( object ); - - } - - object.parent = this; - object.dispatchEvent( { type: 'added' } ); - - this.children.push( object ); - - } else { - - console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); - - } - - return this; - - }, - - remove: function ( object ) { - - if ( arguments.length > 1 ) { - - for ( var i = 0; i < arguments.length; i ++ ) { - - this.remove( arguments[ i ] ); - - } - - } - - var index = this.children.indexOf( object ); - - if ( index !== - 1 ) { - - object.parent = null; - - object.dispatchEvent( { type: 'removed' } ); - - this.children.splice( index, 1 ); - - } - - }, - - getObjectById: function ( id ) { - - return this.getObjectByProperty( 'id', id ); - - }, - - getObjectByName: function ( name ) { - - return this.getObjectByProperty( 'name', name ); - - }, - - getObjectByProperty: function ( name, value ) { - - if ( this[ name ] === value ) return this; - - for ( var i = 0, l = this.children.length; i < l; i ++ ) { - - var child = this.children[ i ]; - var object = child.getObjectByProperty( name, value ); - - if ( object !== undefined ) { - - return object; - - } - - } - - return undefined; - - }, - - getWorldPosition: function ( optionalTarget ) { - - var result = optionalTarget || new Vector3(); - - this.updateMatrixWorld( true ); - - return result.setFromMatrixPosition( this.matrixWorld ); - - }, - - getWorldQuaternion: function () { - - var position = new Vector3(); - var scale = new Vector3(); - - return function getWorldQuaternion( optionalTarget ) { - - var result = optionalTarget || new Quaternion(); - - this.updateMatrixWorld( true ); - - this.matrixWorld.decompose( position, result, scale ); - - return result; - - }; - - }(), - - getWorldRotation: function () { - - var quaternion = new Quaternion(); - - return function getWorldRotation( optionalTarget ) { - - var result = optionalTarget || new Euler(); - - this.getWorldQuaternion( quaternion ); - - return result.setFromQuaternion( quaternion, this.rotation.order, false ); - - }; - - }(), - - getWorldScale: function () { - - var position = new Vector3(); - var quaternion = new Quaternion(); - - return function getWorldScale( optionalTarget ) { - - var result = optionalTarget || new Vector3(); - - this.updateMatrixWorld( true ); - - this.matrixWorld.decompose( position, quaternion, result ); - - return result; - - }; - - }(), - - getWorldDirection: function () { - - var quaternion = new Quaternion(); - - return function getWorldDirection( optionalTarget ) { - - var result = optionalTarget || new Vector3(); - - this.getWorldQuaternion( quaternion ); - - return result.set( 0, 0, 1 ).applyQuaternion( quaternion ); - - }; - - }(), - - raycast: function () {}, - - traverse: function ( callback ) { - - callback( this ); - - var children = this.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].traverse( callback ); - - } - - }, - - traverseVisible: function ( callback ) { - - if ( this.visible === false ) return; - - callback( this ); - - var children = this.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].traverseVisible( callback ); - - } - - }, - - traverseAncestors: function ( callback ) { - - var parent = this.parent; - - if ( parent !== null ) { - - callback( parent ); - - parent.traverseAncestors( callback ); - - } - - }, - - updateMatrix: function () { - - this.matrix.compose( this.position, this.quaternion, this.scale ); - - this.matrixWorldNeedsUpdate = true; - - }, - - updateMatrixWorld: function ( force ) { - - if ( this.matrixAutoUpdate === true ) this.updateMatrix(); - - if ( this.matrixWorldNeedsUpdate === true || force === true ) { - - if ( this.parent === null ) { - - this.matrixWorld.copy( this.matrix ); - - } else { - - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - - } - - this.matrixWorldNeedsUpdate = false; - - force = true; - - } - - // update children - - var children = this.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].updateMatrixWorld( force ); - - } - - }, - - toJSON: function ( meta ) { - - // meta is '' when called from JSON.stringify - var isRootObject = ( meta === undefined || meta === '' ); - - var output = {}; - - // meta is a hash used to collect geometries, materials. - // not providing it implies that this is the root object - // being serialized. - if ( isRootObject ) { - - // initialize meta obj - meta = { - geometries: {}, - materials: {}, - textures: {}, - images: {} - }; - - output.metadata = { - version: 4.4, - type: 'Object', - generator: 'Object3D.toJSON' - }; - - } - - // standard Object3D serialization - - var object = {}; - - object.uuid = this.uuid; - object.type = this.type; - - if ( this.name !== '' ) object.name = this.name; - if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData; - if ( this.castShadow === true ) object.castShadow = true; - if ( this.receiveShadow === true ) object.receiveShadow = true; - if ( this.visible === false ) object.visible = false; - - object.matrix = this.matrix.toArray(); - - // - - if ( this.geometry !== undefined ) { - - if ( meta.geometries[ this.geometry.uuid ] === undefined ) { - - meta.geometries[ this.geometry.uuid ] = this.geometry.toJSON( meta ); - - } - - object.geometry = this.geometry.uuid; - - } - - if ( this.material !== undefined ) { - - if ( meta.materials[ this.material.uuid ] === undefined ) { - - meta.materials[ this.material.uuid ] = this.material.toJSON( meta ); - - } - - object.material = this.material.uuid; - - } - - // - - if ( this.children.length > 0 ) { - - object.children = []; - - for ( var i = 0; i < this.children.length; i ++ ) { - - object.children.push( this.children[ i ].toJSON( meta ).object ); - - } - - } - - if ( isRootObject ) { - - var geometries = extractFromCache( meta.geometries ); - var materials = extractFromCache( meta.materials ); - var textures = extractFromCache( meta.textures ); - var images = extractFromCache( meta.images ); - - if ( geometries.length > 0 ) output.geometries = geometries; - if ( materials.length > 0 ) output.materials = materials; - if ( textures.length > 0 ) output.textures = textures; - if ( images.length > 0 ) output.images = images; - - } - - output.object = object; - - return output; - - // extract data from the cache hash - // remove metadata on each item - // and return as array - function extractFromCache( cache ) { - - var values = []; - for ( var key in cache ) { - - var data = cache[ key ]; - delete data.metadata; - values.push( data ); - - } - return values; - - } - - }, - - clone: function ( recursive ) { - - return new this.constructor().copy( this, recursive ); - - }, - - copy: function ( source, recursive ) { - - if ( recursive === undefined ) recursive = true; - - this.name = source.name; - - this.up.copy( source.up ); - - this.position.copy( source.position ); - this.quaternion.copy( source.quaternion ); - this.scale.copy( source.scale ); - - this.matrix.copy( source.matrix ); - this.matrixWorld.copy( source.matrixWorld ); - - this.matrixAutoUpdate = source.matrixAutoUpdate; - this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; - - this.layers.mask = source.layers.mask; - this.visible = source.visible; - - this.castShadow = source.castShadow; - this.receiveShadow = source.receiveShadow; - - this.frustumCulled = source.frustumCulled; - this.renderOrder = source.renderOrder; - - this.userData = JSON.parse( JSON.stringify( source.userData ) ); - - if ( recursive === true ) { - - for ( var i = 0; i < source.children.length; i ++ ) { - - var child = source.children[ i ]; - this.add( child.clone() ); - - } - - } - - return this; - - } - - } ); - - /** - * @author bhouston / http://clara.io - */ - - function Line3( start, end ) { - - this.start = ( start !== undefined ) ? start : new Vector3(); - this.end = ( end !== undefined ) ? end : new Vector3(); - - } - - Line3.prototype = { - - constructor: Line3, - - set: function ( start, end ) { - - this.start.copy( start ); - this.end.copy( end ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( line ) { - - this.start.copy( line.start ); - this.end.copy( line.end ); - - return this; - - }, - - getCenter: function ( optionalTarget ) { - - var result = optionalTarget || new Vector3(); - return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); - - }, - - delta: function ( optionalTarget ) { - - var result = optionalTarget || new Vector3(); - return result.subVectors( this.end, this.start ); - - }, - - distanceSq: function () { - - return this.start.distanceToSquared( this.end ); - - }, - - distance: function () { - - return this.start.distanceTo( this.end ); - - }, - - at: function ( t, optionalTarget ) { - - var result = optionalTarget || new Vector3(); - - return this.delta( result ).multiplyScalar( t ).add( this.start ); - - }, - - closestPointToPointParameter: function () { - - var startP = new Vector3(); - var startEnd = new Vector3(); - - return function closestPointToPointParameter( point, clampToLine ) { - - startP.subVectors( point, this.start ); - startEnd.subVectors( this.end, this.start ); - - var startEnd2 = startEnd.dot( startEnd ); - var startEnd_startP = startEnd.dot( startP ); - - var t = startEnd_startP / startEnd2; - - if ( clampToLine ) { - - t = _Math.clamp( t, 0, 1 ); - - } - - return t; - - }; - - }(), - - closestPointToPoint: function ( point, clampToLine, optionalTarget ) { - - var t = this.closestPointToPointParameter( point, clampToLine ); - - var result = optionalTarget || new Vector3(); - - return this.delta( result ).multiplyScalar( t ).add( this.start ); - - }, - - applyMatrix4: function ( matrix ) { - - this.start.applyMatrix4( matrix ); - this.end.applyMatrix4( matrix ); - - return this; - - }, - - equals: function ( line ) { - - return line.start.equals( this.start ) && line.end.equals( this.end ); - - } - - }; - - /** - * @author bhouston / http://clara.io - * @author mrdoob / http://mrdoob.com/ - */ - - function Triangle( a, b, c ) { - - this.a = ( a !== undefined ) ? a : new Vector3(); - this.b = ( b !== undefined ) ? b : new Vector3(); - this.c = ( c !== undefined ) ? c : new Vector3(); - - } - - Triangle.normal = function () { - - var v0 = new Vector3(); - - return function normal( a, b, c, optionalTarget ) { - - var result = optionalTarget || new Vector3(); - - result.subVectors( c, b ); - v0.subVectors( a, b ); - result.cross( v0 ); - - var resultLengthSq = result.lengthSq(); - if ( resultLengthSq > 0 ) { - - return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) ); - - } - - return result.set( 0, 0, 0 ); - - }; - - }(); - - // static/instance method to calculate barycentric coordinates - // based on: http://www.blackpawn.com/texts/pointinpoly/default.html - Triangle.barycoordFromPoint = function () { - - var v0 = new Vector3(); - var v1 = new Vector3(); - var v2 = new Vector3(); - - return function barycoordFromPoint( point, a, b, c, optionalTarget ) { - - v0.subVectors( c, a ); - v1.subVectors( b, a ); - v2.subVectors( point, a ); - - var dot00 = v0.dot( v0 ); - var dot01 = v0.dot( v1 ); - var dot02 = v0.dot( v2 ); - var dot11 = v1.dot( v1 ); - var dot12 = v1.dot( v2 ); - - var denom = ( dot00 * dot11 - dot01 * dot01 ); - - var result = optionalTarget || new Vector3(); - - // collinear or singular triangle - if ( denom === 0 ) { - - // arbitrary location outside of triangle? - // not sure if this is the best idea, maybe should be returning undefined - return result.set( - 2, - 1, - 1 ); - - } - - var invDenom = 1 / denom; - var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; - var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; - - // barycentric coordinates must always sum to 1 - return result.set( 1 - u - v, v, u ); - - }; - - }(); - - Triangle.containsPoint = function () { - - var v1 = new Vector3(); - - return function containsPoint( point, a, b, c ) { - - var result = Triangle.barycoordFromPoint( point, a, b, c, v1 ); - - return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 ); - - }; - - }(); - - Triangle.prototype = { - - constructor: Triangle, - - set: function ( a, b, c ) { - - this.a.copy( a ); - this.b.copy( b ); - this.c.copy( c ); - - return this; - - }, - - setFromPointsAndIndices: function ( points, i0, i1, i2 ) { - - this.a.copy( points[ i0 ] ); - this.b.copy( points[ i1 ] ); - this.c.copy( points[ i2 ] ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( triangle ) { - - this.a.copy( triangle.a ); - this.b.copy( triangle.b ); - this.c.copy( triangle.c ); - - return this; - - }, - - area: function () { - - var v0 = new Vector3(); - var v1 = new Vector3(); - - return function area() { - - v0.subVectors( this.c, this.b ); - v1.subVectors( this.a, this.b ); - - return v0.cross( v1 ).length() * 0.5; - - }; - - }(), - - midpoint: function ( optionalTarget ) { - - var result = optionalTarget || new Vector3(); - return result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); - - }, - - normal: function ( optionalTarget ) { - - return Triangle.normal( this.a, this.b, this.c, optionalTarget ); - - }, - - plane: function ( optionalTarget ) { - - var result = optionalTarget || new Plane(); - - return result.setFromCoplanarPoints( this.a, this.b, this.c ); - - }, - - barycoordFromPoint: function ( point, optionalTarget ) { - - return Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget ); - - }, - - containsPoint: function ( point ) { - - return Triangle.containsPoint( point, this.a, this.b, this.c ); - - }, - - closestPointToPoint: function () { - - var plane, edgeList, projectedPoint, closestPoint; - - return function closestPointToPoint( point, optionalTarget ) { - - if ( plane === undefined ) { - - plane = new Plane(); - edgeList = [ new Line3(), new Line3(), new Line3() ]; - projectedPoint = new Vector3(); - closestPoint = new Vector3(); - - } - - var result = optionalTarget || new Vector3(); - var minDistance = Infinity; - - // project the point onto the plane of the triangle - - plane.setFromCoplanarPoints( this.a, this.b, this.c ); - plane.projectPoint( point, projectedPoint ); - - // check if the projection lies within the triangle - - if( this.containsPoint( projectedPoint ) === true ) { - - // if so, this is the closest point - - result.copy( projectedPoint ); - - } else { - - // if not, the point falls outside the triangle. the result is the closest point to the triangle's edges or vertices - - edgeList[ 0 ].set( this.a, this.b ); - edgeList[ 1 ].set( this.b, this.c ); - edgeList[ 2 ].set( this.c, this.a ); - - for( var i = 0; i < edgeList.length; i ++ ) { - - edgeList[ i ].closestPointToPoint( projectedPoint, true, closestPoint ); - - var distance = projectedPoint.distanceToSquared( closestPoint ); - - if( distance < minDistance ) { - - minDistance = distance; - - result.copy( closestPoint ); - - } - - } - - } - - return result; - - }; - - }(), - - equals: function ( triangle ) { - - return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); - - } - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - - function Face3( a, b, c, normal, color, materialIndex ) { - - this.a = a; - this.b = b; - this.c = c; - - this.normal = (normal && normal.isVector3) ? normal : new Vector3(); - this.vertexNormals = Array.isArray( normal ) ? normal : []; - - this.color = (color && color.isColor) ? color : new Color(); - this.vertexColors = Array.isArray( color ) ? color : []; - - this.materialIndex = materialIndex !== undefined ? materialIndex : 0; - - } - - Face3.prototype = { - - constructor: Face3, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.a = source.a; - this.b = source.b; - this.c = source.c; - - this.normal.copy( source.normal ); - this.color.copy( source.color ); - - this.materialIndex = source.materialIndex; - - for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) { - - this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); - - } - - for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) { - - this.vertexColors[ i ] = source.vertexColors[ i ].clone(); - - } - - return this; - - } - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * shading: THREE.SmoothShading, - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: - * } - */ - - function MeshBasicMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshBasicMaterial'; - - this.color = new Color( 0xffffff ); // emissive - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - - this.lights = false; - - this.setValues( parameters ); - - } - - MeshBasicMaterial.prototype = Object.create( Material.prototype ); - MeshBasicMaterial.prototype.constructor = MeshBasicMaterial; - - MeshBasicMaterial.prototype.isMeshBasicMaterial = true; - - MeshBasicMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - - return this; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function BufferAttribute( array, itemSize, normalized ) { - - if ( Array.isArray( array ) ) { - - throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); - - } - - this.uuid = _Math.generateUUID(); - - this.array = array; - this.itemSize = itemSize; - this.count = array !== undefined ? array.length / itemSize : 0; - this.normalized = normalized === true; - - this.dynamic = false; - this.updateRange = { offset: 0, count: - 1 }; - - this.onUploadCallback = function () {}; - - this.version = 0; - - } - - BufferAttribute.prototype = { - - constructor: BufferAttribute, - - isBufferAttribute: true, - - set needsUpdate( value ) { - - if ( value === true ) this.version ++; - - }, - - setArray: function ( array ) { - - if ( Array.isArray( array ) ) { - - throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); - - } - - this.count = array !== undefined ? array.length / this.itemSize : 0; - this.array = array; - - }, - - setDynamic: function ( value ) { - - this.dynamic = value; - - return this; - - }, - - copy: function ( source ) { - - this.array = new source.array.constructor( source.array ); - this.itemSize = source.itemSize; - this.count = source.count; - this.normalized = source.normalized; - - this.dynamic = source.dynamic; - - return this; - - }, - - copyAt: function ( index1, attribute, index2 ) { - - index1 *= this.itemSize; - index2 *= attribute.itemSize; - - for ( var i = 0, l = this.itemSize; i < l; i ++ ) { - - this.array[ index1 + i ] = attribute.array[ index2 + i ]; - - } - - return this; - - }, - - copyArray: function ( array ) { - - this.array.set( array ); - - return this; - - }, - - copyColorsArray: function ( colors ) { - - var array = this.array, offset = 0; - - for ( var i = 0, l = colors.length; i < l; i ++ ) { - - var color = colors[ i ]; - - if ( color === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); - color = new Color(); - - } - - array[ offset ++ ] = color.r; - array[ offset ++ ] = color.g; - array[ offset ++ ] = color.b; - - } - - return this; - - }, - - copyIndicesArray: function ( indices ) { - - var array = this.array, offset = 0; - - for ( var i = 0, l = indices.length; i < l; i ++ ) { - - var index = indices[ i ]; - - array[ offset ++ ] = index.a; - array[ offset ++ ] = index.b; - array[ offset ++ ] = index.c; - - } - - return this; - - }, - - copyVector2sArray: function ( vectors ) { - - var array = this.array, offset = 0; - - for ( var i = 0, l = vectors.length; i < l; i ++ ) { - - var vector = vectors[ i ]; - - if ( vector === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); - vector = new Vector2(); - - } - - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - - } - - return this; - - }, - - copyVector3sArray: function ( vectors ) { - - var array = this.array, offset = 0; - - for ( var i = 0, l = vectors.length; i < l; i ++ ) { - - var vector = vectors[ i ]; - - if ( vector === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); - vector = new Vector3(); - - } - - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; - - } - - return this; - - }, - - copyVector4sArray: function ( vectors ) { - - var array = this.array, offset = 0; - - for ( var i = 0, l = vectors.length; i < l; i ++ ) { - - var vector = vectors[ i ]; - - if ( vector === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); - vector = new Vector4(); - - } - - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; - array[ offset ++ ] = vector.w; - - } - - return this; - - }, - - set: function ( value, offset ) { - - if ( offset === undefined ) offset = 0; - - this.array.set( value, offset ); - - return this; - - }, - - getX: function ( index ) { - - return this.array[ index * this.itemSize ]; - - }, - - setX: function ( index, x ) { - - this.array[ index * this.itemSize ] = x; - - return this; - - }, - - getY: function ( index ) { - - return this.array[ index * this.itemSize + 1 ]; - - }, - - setY: function ( index, y ) { - - this.array[ index * this.itemSize + 1 ] = y; - - return this; - - }, - - getZ: function ( index ) { - - return this.array[ index * this.itemSize + 2 ]; - - }, - - setZ: function ( index, z ) { - - this.array[ index * this.itemSize + 2 ] = z; - - return this; - - }, - - getW: function ( index ) { - - return this.array[ index * this.itemSize + 3 ]; - - }, - - setW: function ( index, w ) { - - this.array[ index * this.itemSize + 3 ] = w; - - return this; - - }, - - setXY: function ( index, x, y ) { - - index *= this.itemSize; - - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - - return this; - - }, - - setXYZ: function ( index, x, y, z ) { - - index *= this.itemSize; - - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; - - return this; - - }, - - setXYZW: function ( index, x, y, z, w ) { - - index *= this.itemSize; - - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; - this.array[ index + 3 ] = w; - - return this; - - }, - - onUpload: function ( callback ) { - - this.onUploadCallback = callback; - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - } - - }; - - // - - function Int8BufferAttribute( array, itemSize ) { - - BufferAttribute.call( this, new Int8Array( array ), itemSize ); - - } - - Int8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int8BufferAttribute.prototype.constructor = Int8BufferAttribute; - - - function Uint8BufferAttribute( array, itemSize ) { - - BufferAttribute.call( this, new Uint8Array( array ), itemSize ); - - } - - Uint8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute; - - - function Uint8ClampedBufferAttribute( array, itemSize ) { - - BufferAttribute.call( this, new Uint8ClampedArray( array ), itemSize ); - - } - - Uint8ClampedBufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute; - - - function Int16BufferAttribute( array, itemSize ) { - - BufferAttribute.call( this, new Int16Array( array ), itemSize ); - - } - - Int16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int16BufferAttribute.prototype.constructor = Int16BufferAttribute; - - - function Uint16BufferAttribute( array, itemSize ) { - - BufferAttribute.call( this, new Uint16Array( array ), itemSize ); - - } - - Uint16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute; - - - function Int32BufferAttribute( array, itemSize ) { - - BufferAttribute.call( this, new Int32Array( array ), itemSize ); - - } - - Int32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int32BufferAttribute.prototype.constructor = Int32BufferAttribute; - - - function Uint32BufferAttribute( array, itemSize ) { - - BufferAttribute.call( this, new Uint32Array( array ), itemSize ); - - } - - Uint32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute; - - - function Float32BufferAttribute( array, itemSize ) { - - BufferAttribute.call( this, new Float32Array( array ), itemSize ); - - } - - Float32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Float32BufferAttribute.prototype.constructor = Float32BufferAttribute; - - - function Float64BufferAttribute( array, itemSize ) { - - BufferAttribute.call( this, new Float64Array( array ), itemSize ); - - } - - Float64BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Float64BufferAttribute.prototype.constructor = Float64BufferAttribute; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function DirectGeometry() { - - this.indices = []; - this.vertices = []; - this.normals = []; - this.colors = []; - this.uvs = []; - this.uvs2 = []; - - this.groups = []; - - this.morphTargets = {}; - - this.skinWeights = []; - this.skinIndices = []; - - // this.lineDistances = []; - - this.boundingBox = null; - this.boundingSphere = null; - - // update flags - - this.verticesNeedUpdate = false; - this.normalsNeedUpdate = false; - this.colorsNeedUpdate = false; - this.uvsNeedUpdate = false; - this.groupsNeedUpdate = false; - - } - - Object.assign( DirectGeometry.prototype, { - - computeGroups: function ( geometry ) { - - var group; - var groups = []; - var materialIndex = undefined; - - var faces = geometry.faces; - - for ( var i = 0; i < faces.length; i ++ ) { - - var face = faces[ i ]; - - // materials - - if ( face.materialIndex !== materialIndex ) { - - materialIndex = face.materialIndex; - - if ( group !== undefined ) { - - group.count = ( i * 3 ) - group.start; - groups.push( group ); - - } - - group = { - start: i * 3, - materialIndex: materialIndex - }; - - } - - } - - if ( group !== undefined ) { - - group.count = ( i * 3 ) - group.start; - groups.push( group ); - - } - - this.groups = groups; - - }, - - fromGeometry: function ( geometry ) { - - var faces = geometry.faces; - var vertices = geometry.vertices; - var faceVertexUvs = geometry.faceVertexUvs; - - var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; - var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; - - // morphs - - var morphTargets = geometry.morphTargets; - var morphTargetsLength = morphTargets.length; - - var morphTargetsPosition; - - if ( morphTargetsLength > 0 ) { - - morphTargetsPosition = []; - - for ( var i = 0; i < morphTargetsLength; i ++ ) { - - morphTargetsPosition[ i ] = []; - - } - - this.morphTargets.position = morphTargetsPosition; - - } - - var morphNormals = geometry.morphNormals; - var morphNormalsLength = morphNormals.length; - - var morphTargetsNormal; - - if ( morphNormalsLength > 0 ) { - - morphTargetsNormal = []; - - for ( var i = 0; i < morphNormalsLength; i ++ ) { - - morphTargetsNormal[ i ] = []; - - } - - this.morphTargets.normal = morphTargetsNormal; - - } - - // skins - - var skinIndices = geometry.skinIndices; - var skinWeights = geometry.skinWeights; - - var hasSkinIndices = skinIndices.length === vertices.length; - var hasSkinWeights = skinWeights.length === vertices.length; - - // - - for ( var i = 0; i < faces.length; i ++ ) { - - var face = faces[ i ]; - - this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); - - var vertexNormals = face.vertexNormals; - - if ( vertexNormals.length === 3 ) { - - this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); - - } else { - - var normal = face.normal; - - this.normals.push( normal, normal, normal ); - - } - - var vertexColors = face.vertexColors; - - if ( vertexColors.length === 3 ) { - - this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); - - } else { - - var color = face.color; - - this.colors.push( color, color, color ); - - } - - if ( hasFaceVertexUv === true ) { - - var vertexUvs = faceVertexUvs[ 0 ][ i ]; - - if ( vertexUvs !== undefined ) { - - this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); - - } else { - - console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i ); - - this.uvs.push( new Vector2(), new Vector2(), new Vector2() ); - - } - - } - - if ( hasFaceVertexUv2 === true ) { - - var vertexUvs = faceVertexUvs[ 1 ][ i ]; - - if ( vertexUvs !== undefined ) { - - this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); - - } else { - - console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i ); - - this.uvs2.push( new Vector2(), new Vector2(), new Vector2() ); - - } - - } - - // morphs - - for ( var j = 0; j < morphTargetsLength; j ++ ) { - - var morphTarget = morphTargets[ j ].vertices; - - morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); - - } - - for ( var j = 0; j < morphNormalsLength; j ++ ) { - - var morphNormal = morphNormals[ j ].vertexNormals[ i ]; - - morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c ); - - } - - // skins - - if ( hasSkinIndices ) { - - this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); - - } - - if ( hasSkinWeights ) { - - this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); - - } - - } - - this.computeGroups( geometry ); - - this.verticesNeedUpdate = geometry.verticesNeedUpdate; - this.normalsNeedUpdate = geometry.normalsNeedUpdate; - this.colorsNeedUpdate = geometry.colorsNeedUpdate; - this.uvsNeedUpdate = geometry.uvsNeedUpdate; - this.groupsNeedUpdate = geometry.groupsNeedUpdate; - - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author kile / http://kile.stravaganza.org/ - * @author alteredq / http://alteredqualia.com/ - * @author mikael emtinger / http://gomo.se/ - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author bhouston / http://clara.io - */ - - function Geometry() { - - Object.defineProperty( this, 'id', { value: GeometryIdCount() } ); - - this.uuid = _Math.generateUUID(); - - this.name = ''; - this.type = 'Geometry'; - - this.vertices = []; - this.colors = []; - this.faces = []; - this.faceVertexUvs = [ [] ]; - - this.morphTargets = []; - this.morphNormals = []; - - this.skinWeights = []; - this.skinIndices = []; - - this.lineDistances = []; - - this.boundingBox = null; - this.boundingSphere = null; - - // update flags - - this.elementsNeedUpdate = false; - this.verticesNeedUpdate = false; - this.uvsNeedUpdate = false; - this.normalsNeedUpdate = false; - this.colorsNeedUpdate = false; - this.lineDistancesNeedUpdate = false; - this.groupsNeedUpdate = false; - - } - - Object.assign( Geometry.prototype, EventDispatcher.prototype, { - - isGeometry: true, - - applyMatrix: function ( matrix ) { - - var normalMatrix = new Matrix3().getNormalMatrix( matrix ); - - for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { - - var vertex = this.vertices[ i ]; - vertex.applyMatrix4( matrix ); - - } - - for ( var i = 0, il = this.faces.length; i < il; i ++ ) { - - var face = this.faces[ i ]; - face.normal.applyMatrix3( normalMatrix ).normalize(); - - for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { - - face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); - - } - - } - - if ( this.boundingBox !== null ) { - - this.computeBoundingBox(); - - } - - if ( this.boundingSphere !== null ) { - - this.computeBoundingSphere(); - - } - - this.verticesNeedUpdate = true; - this.normalsNeedUpdate = true; - - return this; - - }, - - rotateX: function () { - - // rotate geometry around world x-axis - - var m1; - - return function rotateX( angle ) { - - if ( m1 === undefined ) m1 = new Matrix4(); - - m1.makeRotationX( angle ); - - this.applyMatrix( m1 ); - - return this; - - }; - - }(), - - rotateY: function () { - - // rotate geometry around world y-axis - - var m1; - - return function rotateY( angle ) { - - if ( m1 === undefined ) m1 = new Matrix4(); - - m1.makeRotationY( angle ); - - this.applyMatrix( m1 ); - - return this; - - }; - - }(), - - rotateZ: function () { - - // rotate geometry around world z-axis - - var m1; - - return function rotateZ( angle ) { - - if ( m1 === undefined ) m1 = new Matrix4(); - - m1.makeRotationZ( angle ); - - this.applyMatrix( m1 ); - - return this; - - }; - - }(), - - translate: function () { - - // translate geometry - - var m1; - - return function translate( x, y, z ) { - - if ( m1 === undefined ) m1 = new Matrix4(); - - m1.makeTranslation( x, y, z ); - - this.applyMatrix( m1 ); - - return this; - - }; - - }(), - - scale: function () { - - // scale geometry - - var m1; - - return function scale( x, y, z ) { - - if ( m1 === undefined ) m1 = new Matrix4(); - - m1.makeScale( x, y, z ); - - this.applyMatrix( m1 ); - - return this; - - }; - - }(), - - lookAt: function () { - - var obj; - - return function lookAt( vector ) { - - if ( obj === undefined ) obj = new Object3D(); - - obj.lookAt( vector ); - - obj.updateMatrix(); - - this.applyMatrix( obj.matrix ); - - }; - - }(), - - fromBufferGeometry: function ( geometry ) { - - var scope = this; - - var indices = geometry.index !== null ? geometry.index.array : undefined; - var attributes = geometry.attributes; - - var positions = attributes.position.array; - var normals = attributes.normal !== undefined ? attributes.normal.array : undefined; - var colors = attributes.color !== undefined ? attributes.color.array : undefined; - var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined; - var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined; - - if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = []; - - var tempNormals = []; - var tempUVs = []; - var tempUVs2 = []; - - for ( var i = 0, j = 0; i < positions.length; i += 3, j += 2 ) { - - scope.vertices.push( new Vector3( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ) ); - - if ( normals !== undefined ) { - - tempNormals.push( new Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) ); - - } - - if ( colors !== undefined ) { - - scope.colors.push( new Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) ); - - } - - if ( uvs !== undefined ) { - - tempUVs.push( new Vector2( uvs[ j ], uvs[ j + 1 ] ) ); - - } - - if ( uvs2 !== undefined ) { - - tempUVs2.push( new Vector2( uvs2[ j ], uvs2[ j + 1 ] ) ); - - } - - } - - function addFace( a, b, c, materialIndex ) { - - var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : []; - var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : []; - - var face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex ); - - scope.faces.push( face ); - - if ( uvs !== undefined ) { - - scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] ); - - } - - if ( uvs2 !== undefined ) { - - scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] ); - - } - - } - - if ( indices !== undefined ) { - - var groups = geometry.groups; - - if ( groups.length > 0 ) { - - for ( var i = 0; i < groups.length; i ++ ) { - - var group = groups[ i ]; - - var start = group.start; - var count = group.count; - - for ( var j = start, jl = start + count; j < jl; j += 3 ) { - - addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex ); - - } - - } - - } else { - - for ( var i = 0; i < indices.length; i += 3 ) { - - addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); - - } - - } - - } else { - - for ( var i = 0; i < positions.length / 3; i += 3 ) { - - addFace( i, i + 1, i + 2 ); - - } - - } - - this.computeFaceNormals(); - - if ( geometry.boundingBox !== null ) { - - this.boundingBox = geometry.boundingBox.clone(); - - } - - if ( geometry.boundingSphere !== null ) { - - this.boundingSphere = geometry.boundingSphere.clone(); - - } - - return this; - - }, - - center: function () { - - this.computeBoundingBox(); - - var offset = this.boundingBox.getCenter().negate(); - - this.translate( offset.x, offset.y, offset.z ); - - return offset; - - }, - - normalize: function () { - - this.computeBoundingSphere(); - - var center = this.boundingSphere.center; - var radius = this.boundingSphere.radius; - - var s = radius === 0 ? 1 : 1.0 / radius; - - var matrix = new Matrix4(); - matrix.set( - s, 0, 0, - s * center.x, - 0, s, 0, - s * center.y, - 0, 0, s, - s * center.z, - 0, 0, 0, 1 - ); - - this.applyMatrix( matrix ); - - return this; - - }, - - computeFaceNormals: function () { - - var cb = new Vector3(), ab = new Vector3(); - - for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { - - var face = this.faces[ f ]; - - var vA = this.vertices[ face.a ]; - var vB = this.vertices[ face.b ]; - var vC = this.vertices[ face.c ]; - - cb.subVectors( vC, vB ); - ab.subVectors( vA, vB ); - cb.cross( ab ); - - cb.normalize(); - - face.normal.copy( cb ); - - } - - }, - - computeVertexNormals: function ( areaWeighted ) { - - if ( areaWeighted === undefined ) areaWeighted = true; - - var v, vl, f, fl, face, vertices; - - vertices = new Array( this.vertices.length ); - - for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { - - vertices[ v ] = new Vector3(); - - } - - if ( areaWeighted ) { - - // vertex normals weighted by triangle areas - // http://www.iquilezles.org/www/articles/normals/normals.htm - - var vA, vB, vC; - var cb = new Vector3(), ab = new Vector3(); - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - - vA = this.vertices[ face.a ]; - vB = this.vertices[ face.b ]; - vC = this.vertices[ face.c ]; - - cb.subVectors( vC, vB ); - ab.subVectors( vA, vB ); - cb.cross( ab ); - - vertices[ face.a ].add( cb ); - vertices[ face.b ].add( cb ); - vertices[ face.c ].add( cb ); - - } - - } else { - - this.computeFaceNormals(); - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - - vertices[ face.a ].add( face.normal ); - vertices[ face.b ].add( face.normal ); - vertices[ face.c ].add( face.normal ); - - } - - } - - for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { - - vertices[ v ].normalize(); - - } - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - - var vertexNormals = face.vertexNormals; - - if ( vertexNormals.length === 3 ) { - - vertexNormals[ 0 ].copy( vertices[ face.a ] ); - vertexNormals[ 1 ].copy( vertices[ face.b ] ); - vertexNormals[ 2 ].copy( vertices[ face.c ] ); - - } else { - - vertexNormals[ 0 ] = vertices[ face.a ].clone(); - vertexNormals[ 1 ] = vertices[ face.b ].clone(); - vertexNormals[ 2 ] = vertices[ face.c ].clone(); - - } - - } - - if ( this.faces.length > 0 ) { - - this.normalsNeedUpdate = true; - - } - - }, - - computeFlatVertexNormals: function () { - - var f, fl, face; - - this.computeFaceNormals(); - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - - var vertexNormals = face.vertexNormals; - - if ( vertexNormals.length === 3 ) { - - vertexNormals[ 0 ].copy( face.normal ); - vertexNormals[ 1 ].copy( face.normal ); - vertexNormals[ 2 ].copy( face.normal ); - - } else { - - vertexNormals[ 0 ] = face.normal.clone(); - vertexNormals[ 1 ] = face.normal.clone(); - vertexNormals[ 2 ] = face.normal.clone(); - - } - - } - - if ( this.faces.length > 0 ) { - - this.normalsNeedUpdate = true; - - } - - }, - - computeMorphNormals: function () { - - var i, il, f, fl, face; - - // save original normals - // - create temp variables on first access - // otherwise just copy (for faster repeated calls) - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - - if ( ! face.__originalFaceNormal ) { - - face.__originalFaceNormal = face.normal.clone(); - - } else { - - face.__originalFaceNormal.copy( face.normal ); - - } - - if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; - - for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { - - if ( ! face.__originalVertexNormals[ i ] ) { - - face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); - - } else { - - face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); - - } - - } - - } - - // use temp geometry to compute face and vertex normals for each morph - - var tmpGeo = new Geometry(); - tmpGeo.faces = this.faces; - - for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { - - // create on first access - - if ( ! this.morphNormals[ i ] ) { - - this.morphNormals[ i ] = {}; - this.morphNormals[ i ].faceNormals = []; - this.morphNormals[ i ].vertexNormals = []; - - var dstNormalsFace = this.morphNormals[ i ].faceNormals; - var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; - - var faceNormal, vertexNormals; - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - faceNormal = new Vector3(); - vertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() }; - - dstNormalsFace.push( faceNormal ); - dstNormalsVertex.push( vertexNormals ); - - } - - } - - var morphNormals = this.morphNormals[ i ]; - - // set vertices to morph target - - tmpGeo.vertices = this.morphTargets[ i ].vertices; - - // compute morph normals - - tmpGeo.computeFaceNormals(); - tmpGeo.computeVertexNormals(); - - // store morph normals - - var faceNormal, vertexNormals; - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - - faceNormal = morphNormals.faceNormals[ f ]; - vertexNormals = morphNormals.vertexNormals[ f ]; - - faceNormal.copy( face.normal ); - - vertexNormals.a.copy( face.vertexNormals[ 0 ] ); - vertexNormals.b.copy( face.vertexNormals[ 1 ] ); - vertexNormals.c.copy( face.vertexNormals[ 2 ] ); - - } - - } - - // restore original normals - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - - face.normal = face.__originalFaceNormal; - face.vertexNormals = face.__originalVertexNormals; - - } - - }, - - computeLineDistances: function () { - - var d = 0; - var vertices = this.vertices; - - for ( var i = 0, il = vertices.length; i < il; i ++ ) { - - if ( i > 0 ) { - - d += vertices[ i ].distanceTo( vertices[ i - 1 ] ); - - } - - this.lineDistances[ i ] = d; - - } - - }, - - computeBoundingBox: function () { - - if ( this.boundingBox === null ) { - - this.boundingBox = new Box3(); - - } - - this.boundingBox.setFromPoints( this.vertices ); - - }, - - computeBoundingSphere: function () { - - if ( this.boundingSphere === null ) { - - this.boundingSphere = new Sphere(); - - } - - this.boundingSphere.setFromPoints( this.vertices ); - - }, - - merge: function ( geometry, matrix, materialIndexOffset ) { - - if ( (geometry && geometry.isGeometry) === false ) { - - console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); - return; - - } - - var normalMatrix, - vertexOffset = this.vertices.length, - vertices1 = this.vertices, - vertices2 = geometry.vertices, - faces1 = this.faces, - faces2 = geometry.faces, - uvs1 = this.faceVertexUvs[ 0 ], - uvs2 = geometry.faceVertexUvs[ 0 ], - colors1 = this.colors, - colors2 = geometry.colors; - - if ( materialIndexOffset === undefined ) materialIndexOffset = 0; - - if ( matrix !== undefined ) { - - normalMatrix = new Matrix3().getNormalMatrix( matrix ); - - } - - // vertices - - for ( var i = 0, il = vertices2.length; i < il; i ++ ) { - - var vertex = vertices2[ i ]; - - var vertexCopy = vertex.clone(); - - if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); - - vertices1.push( vertexCopy ); - - } - - // colors - - for ( var i = 0, il = colors2.length; i < il; i ++ ) { - - colors1.push( colors2[ i ].clone() ); - - } - - // faces - - for ( i = 0, il = faces2.length; i < il; i ++ ) { - - var face = faces2[ i ], faceCopy, normal, color, - faceVertexNormals = face.vertexNormals, - faceVertexColors = face.vertexColors; - - faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); - faceCopy.normal.copy( face.normal ); - - if ( normalMatrix !== undefined ) { - - faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); - - } - - for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { - - normal = faceVertexNormals[ j ].clone(); - - if ( normalMatrix !== undefined ) { - - normal.applyMatrix3( normalMatrix ).normalize(); - - } - - faceCopy.vertexNormals.push( normal ); - - } - - faceCopy.color.copy( face.color ); - - for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { - - color = faceVertexColors[ j ]; - faceCopy.vertexColors.push( color.clone() ); - - } - - faceCopy.materialIndex = face.materialIndex + materialIndexOffset; - - faces1.push( faceCopy ); - - } - - // uvs - - for ( i = 0, il = uvs2.length; i < il; i ++ ) { - - var uv = uvs2[ i ], uvCopy = []; - - if ( uv === undefined ) { - - continue; - - } - - for ( var j = 0, jl = uv.length; j < jl; j ++ ) { - - uvCopy.push( uv[ j ].clone() ); - - } - - uvs1.push( uvCopy ); - - } - - }, - - mergeMesh: function ( mesh ) { - - if ( (mesh && mesh.isMesh) === false ) { - - console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh ); - return; - - } - - mesh.matrixAutoUpdate && mesh.updateMatrix(); - - this.merge( mesh.geometry, mesh.matrix ); - - }, - - /* - * Checks for duplicate vertices with hashmap. - * Duplicated vertices are removed - * and faces' vertices are updated. - */ - - mergeVertices: function () { - - var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) - var unique = [], changes = []; - - var v, key; - var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 - var precision = Math.pow( 10, precisionPoints ); - var i, il, face; - var indices, j, jl; - - for ( i = 0, il = this.vertices.length; i < il; i ++ ) { - - v = this.vertices[ i ]; - key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); - - if ( verticesMap[ key ] === undefined ) { - - verticesMap[ key ] = i; - unique.push( this.vertices[ i ] ); - changes[ i ] = unique.length - 1; - - } else { - - //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); - changes[ i ] = changes[ verticesMap[ key ] ]; - - } - - } - - - // if faces are completely degenerate after merging vertices, we - // have to remove them from the geometry. - var faceIndicesToRemove = []; - - for ( i = 0, il = this.faces.length; i < il; i ++ ) { - - face = this.faces[ i ]; - - face.a = changes[ face.a ]; - face.b = changes[ face.b ]; - face.c = changes[ face.c ]; - - indices = [ face.a, face.b, face.c ]; - - var dupIndex = - 1; - - // if any duplicate vertices are found in a Face3 - // we have to remove the face as nothing can be saved - for ( var n = 0; n < 3; n ++ ) { - - if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { - - dupIndex = n; - faceIndicesToRemove.push( i ); - break; - - } - - } - - } - - for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { - - var idx = faceIndicesToRemove[ i ]; - - this.faces.splice( idx, 1 ); - - for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { - - this.faceVertexUvs[ j ].splice( idx, 1 ); - - } - - } - - // Use unique set of vertices - - var diff = this.vertices.length - unique.length; - this.vertices = unique; - return diff; - - }, - - sortFacesByMaterialIndex: function () { - - var faces = this.faces; - var length = faces.length; - - // tag faces - - for ( var i = 0; i < length; i ++ ) { - - faces[ i ]._id = i; - - } - - // sort faces - - function materialIndexSort( a, b ) { - - return a.materialIndex - b.materialIndex; - - } - - faces.sort( materialIndexSort ); - - // sort uvs - - var uvs1 = this.faceVertexUvs[ 0 ]; - var uvs2 = this.faceVertexUvs[ 1 ]; - - var newUvs1, newUvs2; - - if ( uvs1 && uvs1.length === length ) newUvs1 = []; - if ( uvs2 && uvs2.length === length ) newUvs2 = []; - - for ( var i = 0; i < length; i ++ ) { - - var id = faces[ i ]._id; - - if ( newUvs1 ) newUvs1.push( uvs1[ id ] ); - if ( newUvs2 ) newUvs2.push( uvs2[ id ] ); - - } - - if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1; - if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2; - - }, - - toJSON: function () { - - var data = { - metadata: { - version: 4.4, - type: 'Geometry', - generator: 'Geometry.toJSON' - } - }; - - // standard Geometry serialization - - data.uuid = this.uuid; - data.type = this.type; - if ( this.name !== '' ) data.name = this.name; - - if ( this.parameters !== undefined ) { - - var parameters = this.parameters; - - for ( var key in parameters ) { - - if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; - - } - - return data; - - } - - var vertices = []; - - for ( var i = 0; i < this.vertices.length; i ++ ) { - - var vertex = this.vertices[ i ]; - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - var faces = []; - var normals = []; - var normalsHash = {}; - var colors = []; - var colorsHash = {}; - var uvs = []; - var uvsHash = {}; - - for ( var i = 0; i < this.faces.length; i ++ ) { - - var face = this.faces[ i ]; - - var hasMaterial = true; - var hasFaceUv = false; // deprecated - var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; - var hasFaceNormal = face.normal.length() > 0; - var hasFaceVertexNormal = face.vertexNormals.length > 0; - var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; - var hasFaceVertexColor = face.vertexColors.length > 0; - - var faceType = 0; - - faceType = setBit( faceType, 0, 0 ); // isQuad - faceType = setBit( faceType, 1, hasMaterial ); - faceType = setBit( faceType, 2, hasFaceUv ); - faceType = setBit( faceType, 3, hasFaceVertexUv ); - faceType = setBit( faceType, 4, hasFaceNormal ); - faceType = setBit( faceType, 5, hasFaceVertexNormal ); - faceType = setBit( faceType, 6, hasFaceColor ); - faceType = setBit( faceType, 7, hasFaceVertexColor ); - - faces.push( faceType ); - faces.push( face.a, face.b, face.c ); - faces.push( face.materialIndex ); - - if ( hasFaceVertexUv ) { - - var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; - - faces.push( - getUvIndex( faceVertexUvs[ 0 ] ), - getUvIndex( faceVertexUvs[ 1 ] ), - getUvIndex( faceVertexUvs[ 2 ] ) - ); - - } - - if ( hasFaceNormal ) { - - faces.push( getNormalIndex( face.normal ) ); - - } - - if ( hasFaceVertexNormal ) { - - var vertexNormals = face.vertexNormals; - - faces.push( - getNormalIndex( vertexNormals[ 0 ] ), - getNormalIndex( vertexNormals[ 1 ] ), - getNormalIndex( vertexNormals[ 2 ] ) - ); - - } - - if ( hasFaceColor ) { - - faces.push( getColorIndex( face.color ) ); - - } - - if ( hasFaceVertexColor ) { - - var vertexColors = face.vertexColors; - - faces.push( - getColorIndex( vertexColors[ 0 ] ), - getColorIndex( vertexColors[ 1 ] ), - getColorIndex( vertexColors[ 2 ] ) - ); - - } - - } - - function setBit( value, position, enabled ) { - - return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); - - } - - function getNormalIndex( normal ) { - - var hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); - - if ( normalsHash[ hash ] !== undefined ) { - - return normalsHash[ hash ]; - - } - - normalsHash[ hash ] = normals.length / 3; - normals.push( normal.x, normal.y, normal.z ); - - return normalsHash[ hash ]; - - } - - function getColorIndex( color ) { - - var hash = color.r.toString() + color.g.toString() + color.b.toString(); - - if ( colorsHash[ hash ] !== undefined ) { - - return colorsHash[ hash ]; - - } - - colorsHash[ hash ] = colors.length; - colors.push( color.getHex() ); - - return colorsHash[ hash ]; - - } - - function getUvIndex( uv ) { - - var hash = uv.x.toString() + uv.y.toString(); - - if ( uvsHash[ hash ] !== undefined ) { - - return uvsHash[ hash ]; - - } - - uvsHash[ hash ] = uvs.length / 2; - uvs.push( uv.x, uv.y ); - - return uvsHash[ hash ]; - - } - - data.data = {}; - - data.data.vertices = vertices; - data.data.normals = normals; - if ( colors.length > 0 ) data.data.colors = colors; - if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility - data.data.faces = faces; - - return data; - - }, - - clone: function () { - - /* - // Handle primitives - - var parameters = this.parameters; - - if ( parameters !== undefined ) { - - var values = []; - - for ( var key in parameters ) { - - values.push( parameters[ key ] ); - - } - - var geometry = Object.create( this.constructor.prototype ); - this.constructor.apply( geometry, values ); - return geometry; - - } - - return new this.constructor().copy( this ); - */ - - return new Geometry().copy( this ); - - }, - - copy: function ( source ) { - - this.vertices = []; - this.faces = []; - this.faceVertexUvs = [ [] ]; - this.colors = []; - - var vertices = source.vertices; - - for ( var i = 0, il = vertices.length; i < il; i ++ ) { - - this.vertices.push( vertices[ i ].clone() ); - - } - - var colors = source.colors; - - for ( var i = 0, il = colors.length; i < il; i ++ ) { - - this.colors.push( colors[ i ].clone() ); - - } - - var faces = source.faces; - - for ( var i = 0, il = faces.length; i < il; i ++ ) { - - this.faces.push( faces[ i ].clone() ); - - } - - for ( var i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { - - var faceVertexUvs = source.faceVertexUvs[ i ]; - - if ( this.faceVertexUvs[ i ] === undefined ) { - - this.faceVertexUvs[ i ] = []; - - } - - for ( var j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { - - var uvs = faceVertexUvs[ j ], uvsCopy = []; - - for ( var k = 0, kl = uvs.length; k < kl; k ++ ) { - - var uv = uvs[ k ]; - - uvsCopy.push( uv.clone() ); - - } - - this.faceVertexUvs[ i ].push( uvsCopy ); - - } - - } - - return this; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - var count = 0; - function GeometryIdCount() { return count++; } - - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ - - function BufferGeometry() { - - Object.defineProperty( this, 'id', { value: GeometryIdCount() } ); - - this.uuid = _Math.generateUUID(); - - this.name = ''; - this.type = 'BufferGeometry'; - - this.index = null; - this.attributes = {}; - - this.morphAttributes = {}; - - this.groups = []; - - this.boundingBox = null; - this.boundingSphere = null; - - this.drawRange = { start: 0, count: Infinity }; - - } - - Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, { - - isBufferGeometry: true, - - getIndex: function () { - - return this.index; - - }, - - setIndex: function ( index ) { - - this.index = index; - - }, - - addAttribute: function ( name, attribute ) { - - if ( ( attribute && attribute.isBufferAttribute ) === false && ( attribute && attribute.isInterleavedBufferAttribute ) === false ) { - - console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); - - this.addAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); - - return; - - } - - if ( name === 'index' ) { - - console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); - this.setIndex( attribute ); - - return; - - } - - this.attributes[ name ] = attribute; - - return this; - - }, - - getAttribute: function ( name ) { - - return this.attributes[ name ]; - - }, - - removeAttribute: function ( name ) { - - delete this.attributes[ name ]; - - return this; - - }, - - addGroup: function ( start, count, materialIndex ) { - - this.groups.push( { - - start: start, - count: count, - materialIndex: materialIndex !== undefined ? materialIndex : 0 - - } ); - - }, - - clearGroups: function () { - - this.groups = []; - - }, - - setDrawRange: function ( start, count ) { - - this.drawRange.start = start; - this.drawRange.count = count; - - }, - - applyMatrix: function ( matrix ) { - - var position = this.attributes.position; - - if ( position !== undefined ) { - - matrix.applyToVector3Array( position.array ); - position.needsUpdate = true; - - } - - var normal = this.attributes.normal; - - if ( normal !== undefined ) { - - var normalMatrix = new Matrix3().getNormalMatrix( matrix ); - - normalMatrix.applyToVector3Array( normal.array ); - normal.needsUpdate = true; - - } - - if ( this.boundingBox !== null ) { - - this.computeBoundingBox(); - - } - - if ( this.boundingSphere !== null ) { - - this.computeBoundingSphere(); - - } - - return this; - - }, - - rotateX: function () { - - // rotate geometry around world x-axis - - var m1; - - return function rotateX( angle ) { - - if ( m1 === undefined ) m1 = new Matrix4(); - - m1.makeRotationX( angle ); - - this.applyMatrix( m1 ); - - return this; - - }; - - }(), - - rotateY: function () { - - // rotate geometry around world y-axis - - var m1; - - return function rotateY( angle ) { - - if ( m1 === undefined ) m1 = new Matrix4(); - - m1.makeRotationY( angle ); - - this.applyMatrix( m1 ); - - return this; - - }; - - }(), - - rotateZ: function () { - - // rotate geometry around world z-axis - - var m1; - - return function rotateZ( angle ) { - - if ( m1 === undefined ) m1 = new Matrix4(); - - m1.makeRotationZ( angle ); - - this.applyMatrix( m1 ); - - return this; - - }; - - }(), - - translate: function () { - - // translate geometry - - var m1; - - return function translate( x, y, z ) { - - if ( m1 === undefined ) m1 = new Matrix4(); - - m1.makeTranslation( x, y, z ); - - this.applyMatrix( m1 ); - - return this; - - }; - - }(), - - scale: function () { - - // scale geometry - - var m1; - - return function scale( x, y, z ) { - - if ( m1 === undefined ) m1 = new Matrix4(); - - m1.makeScale( x, y, z ); - - this.applyMatrix( m1 ); - - return this; - - }; - - }(), - - lookAt: function () { - - var obj; - - return function lookAt( vector ) { - - if ( obj === undefined ) obj = new Object3D(); - - obj.lookAt( vector ); - - obj.updateMatrix(); - - this.applyMatrix( obj.matrix ); - - }; - - }(), - - center: function () { - - this.computeBoundingBox(); - - var offset = this.boundingBox.getCenter().negate(); - - this.translate( offset.x, offset.y, offset.z ); - - return offset; - - }, - - setFromObject: function ( object ) { - - // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this ); - - var geometry = object.geometry; - - if ( object.isPoints || object.isLine ) { - - var positions = new Float32BufferAttribute( geometry.vertices.length * 3, 3 ); - var colors = new Float32BufferAttribute( geometry.colors.length * 3, 3 ); - - this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) ); - this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) ); - - if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { - - var lineDistances = new Float32BufferAttribute( geometry.lineDistances.length, 1 ); - - this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) ); - - } - - if ( geometry.boundingSphere !== null ) { - - this.boundingSphere = geometry.boundingSphere.clone(); - - } - - if ( geometry.boundingBox !== null ) { - - this.boundingBox = geometry.boundingBox.clone(); - - } - - } else if ( object.isMesh ) { - - if ( geometry && geometry.isGeometry ) { - - this.fromGeometry( geometry ); - - } - - } - - return this; - - }, - - updateFromObject: function ( object ) { - - var geometry = object.geometry; - - if ( object.isMesh ) { - - var direct = geometry.__directGeometry; - - if ( geometry.elementsNeedUpdate === true ) { - - direct = undefined; - geometry.elementsNeedUpdate = false; - - } - - if ( direct === undefined ) { - - return this.fromGeometry( geometry ); - - } - - direct.verticesNeedUpdate = geometry.verticesNeedUpdate; - direct.normalsNeedUpdate = geometry.normalsNeedUpdate; - direct.colorsNeedUpdate = geometry.colorsNeedUpdate; - direct.uvsNeedUpdate = geometry.uvsNeedUpdate; - direct.groupsNeedUpdate = geometry.groupsNeedUpdate; - - geometry.verticesNeedUpdate = false; - geometry.normalsNeedUpdate = false; - geometry.colorsNeedUpdate = false; - geometry.uvsNeedUpdate = false; - geometry.groupsNeedUpdate = false; - - geometry = direct; - - } - - var attribute; - - if ( geometry.verticesNeedUpdate === true ) { - - attribute = this.attributes.position; - - if ( attribute !== undefined ) { - - attribute.copyVector3sArray( geometry.vertices ); - attribute.needsUpdate = true; - - } - - geometry.verticesNeedUpdate = false; - - } - - if ( geometry.normalsNeedUpdate === true ) { - - attribute = this.attributes.normal; - - if ( attribute !== undefined ) { - - attribute.copyVector3sArray( geometry.normals ); - attribute.needsUpdate = true; - - } - - geometry.normalsNeedUpdate = false; - - } - - if ( geometry.colorsNeedUpdate === true ) { - - attribute = this.attributes.color; - - if ( attribute !== undefined ) { - - attribute.copyColorsArray( geometry.colors ); - attribute.needsUpdate = true; - - } - - geometry.colorsNeedUpdate = false; - - } - - if ( geometry.uvsNeedUpdate ) { - - attribute = this.attributes.uv; - - if ( attribute !== undefined ) { - - attribute.copyVector2sArray( geometry.uvs ); - attribute.needsUpdate = true; - - } - - geometry.uvsNeedUpdate = false; - - } - - if ( geometry.lineDistancesNeedUpdate ) { - - attribute = this.attributes.lineDistance; - - if ( attribute !== undefined ) { - - attribute.copyArray( geometry.lineDistances ); - attribute.needsUpdate = true; - - } - - geometry.lineDistancesNeedUpdate = false; - - } - - if ( geometry.groupsNeedUpdate ) { - - geometry.computeGroups( object.geometry ); - this.groups = geometry.groups; - - geometry.groupsNeedUpdate = false; - - } - - return this; - - }, - - fromGeometry: function ( geometry ) { - - geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry ); - - return this.fromDirectGeometry( geometry.__directGeometry ); - - }, - - fromDirectGeometry: function ( geometry ) { - - var positions = new Float32Array( geometry.vertices.length * 3 ); - this.addAttribute( 'position', new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); - - if ( geometry.normals.length > 0 ) { - - var normals = new Float32Array( geometry.normals.length * 3 ); - this.addAttribute( 'normal', new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); - - } - - if ( geometry.colors.length > 0 ) { - - var colors = new Float32Array( geometry.colors.length * 3 ); - this.addAttribute( 'color', new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); - - } - - if ( geometry.uvs.length > 0 ) { - - var uvs = new Float32Array( geometry.uvs.length * 2 ); - this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); - - } - - if ( geometry.uvs2.length > 0 ) { - - var uvs2 = new Float32Array( geometry.uvs2.length * 2 ); - this.addAttribute( 'uv2', new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); - - } - - if ( geometry.indices.length > 0 ) { - - var TypeArray = geometry.vertices.length > 65535 ? Uint32Array : Uint16Array; - var indices = new TypeArray( geometry.indices.length * 3 ); - this.setIndex( new BufferAttribute( indices, 1 ).copyIndicesArray( geometry.indices ) ); - - } - - // groups - - this.groups = geometry.groups; - - // morphs - - for ( var name in geometry.morphTargets ) { - - var array = []; - var morphTargets = geometry.morphTargets[ name ]; - - for ( var i = 0, l = morphTargets.length; i < l; i ++ ) { - - var morphTarget = morphTargets[ i ]; - - var attribute = new Float32BufferAttribute( morphTarget.length * 3, 3 ); - - array.push( attribute.copyVector3sArray( morphTarget ) ); - - } - - this.morphAttributes[ name ] = array; - - } - - // skinning - - if ( geometry.skinIndices.length > 0 ) { - - var skinIndices = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 ); - this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) ); - - } - - if ( geometry.skinWeights.length > 0 ) { - - var skinWeights = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 ); - this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) ); - - } - - // - - if ( geometry.boundingSphere !== null ) { - - this.boundingSphere = geometry.boundingSphere.clone(); - - } - - if ( geometry.boundingBox !== null ) { - - this.boundingBox = geometry.boundingBox.clone(); - - } - - return this; - - }, - - computeBoundingBox: function () { - - if ( this.boundingBox === null ) { - - this.boundingBox = new Box3(); - - } - - var position = this.attributes.position; - - if ( position !== undefined ) { - - this.boundingBox.setFromBufferAttribute( position ); - - } else { - - this.boundingBox.makeEmpty(); - - } - - if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { - - console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); - - } - - }, - - computeBoundingSphere: function () { - - var box = new Box3(); - var vector = new Vector3(); - - return function computeBoundingSphere() { - - if ( this.boundingSphere === null ) { - - this.boundingSphere = new Sphere(); - - } - - var position = this.attributes.position; - - if ( position ) { - - var center = this.boundingSphere.center; - - box.setFromBufferAttribute( position ); - box.getCenter( center ); - - // hoping to find a boundingSphere with a radius smaller than the - // boundingSphere of the boundingBox: sqrt(3) smaller in the best case - - var maxRadiusSq = 0; - - for ( var i = 0, il = position.count; i < il; i ++ ) { - - vector.x = position.getX( i ); - vector.y = position.getY( i ); - vector.z = position.getZ( i ); - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); - - } - - this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); - - if ( isNaN( this.boundingSphere.radius ) ) { - - console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); - - } - - } - - }; - - }(), - - computeFaceNormals: function () { - - // backwards compatibility - - }, - - computeVertexNormals: function () { - - var index = this.index; - var attributes = this.attributes; - var groups = this.groups; - - if ( attributes.position ) { - - var positions = attributes.position.array; - - if ( attributes.normal === undefined ) { - - this.addAttribute( 'normal', new BufferAttribute( new Float32Array( positions.length ), 3 ) ); - - } else { - - // reset existing normals to zero - - var array = attributes.normal.array; - - for ( var i = 0, il = array.length; i < il; i ++ ) { - - array[ i ] = 0; - - } - - } - - var normals = attributes.normal.array; - - var vA, vB, vC; - var pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); - var cb = new Vector3(), ab = new Vector3(); - - // indexed elements - - if ( index ) { - - var indices = index.array; - - if ( groups.length === 0 ) { - - this.addGroup( 0, indices.length ); - - } - - for ( var j = 0, jl = groups.length; j < jl; ++ j ) { - - var group = groups[ j ]; - - var start = group.start; - var count = group.count; - - for ( var i = start, il = start + count; i < il; i += 3 ) { - - vA = indices[ i + 0 ] * 3; - vB = indices[ i + 1 ] * 3; - vC = indices[ i + 2 ] * 3; - - pA.fromArray( positions, vA ); - pB.fromArray( positions, vB ); - pC.fromArray( positions, vC ); - - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); - - normals[ vA ] += cb.x; - normals[ vA + 1 ] += cb.y; - normals[ vA + 2 ] += cb.z; - - normals[ vB ] += cb.x; - normals[ vB + 1 ] += cb.y; - normals[ vB + 2 ] += cb.z; - - normals[ vC ] += cb.x; - normals[ vC + 1 ] += cb.y; - normals[ vC + 2 ] += cb.z; - - } - - } - - } else { - - // non-indexed elements (unconnected triangle soup) - - for ( var i = 0, il = positions.length; i < il; i += 9 ) { - - pA.fromArray( positions, i ); - pB.fromArray( positions, i + 3 ); - pC.fromArray( positions, i + 6 ); - - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); - - normals[ i ] = cb.x; - normals[ i + 1 ] = cb.y; - normals[ i + 2 ] = cb.z; - - normals[ i + 3 ] = cb.x; - normals[ i + 4 ] = cb.y; - normals[ i + 5 ] = cb.z; - - normals[ i + 6 ] = cb.x; - normals[ i + 7 ] = cb.y; - normals[ i + 8 ] = cb.z; - - } - - } - - this.normalizeNormals(); - - attributes.normal.needsUpdate = true; - - } - - }, - - merge: function ( geometry, offset ) { - - if ( ( geometry && geometry.isBufferGeometry ) === false ) { - - console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry ); - return; - - } - - if ( offset === undefined ) offset = 0; - - var attributes = this.attributes; - - for ( var key in attributes ) { - - if ( geometry.attributes[ key ] === undefined ) continue; - - var attribute1 = attributes[ key ]; - var attributeArray1 = attribute1.array; - - var attribute2 = geometry.attributes[ key ]; - var attributeArray2 = attribute2.array; - - var attributeSize = attribute2.itemSize; - - for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) { - - attributeArray1[ j ] = attributeArray2[ i ]; - - } - - } - - return this; - - }, - - normalizeNormals: function () { - - var normals = this.attributes.normal.array; - - var x, y, z, n; - - for ( var i = 0, il = normals.length; i < il; i += 3 ) { - - x = normals[ i ]; - y = normals[ i + 1 ]; - z = normals[ i + 2 ]; - - n = 1.0 / Math.sqrt( x * x + y * y + z * z ); - - normals[ i ] *= n; - normals[ i + 1 ] *= n; - normals[ i + 2 ] *= n; - - } - - }, - - toNonIndexed: function () { - - if ( this.index === null ) { - - console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' ); - return this; - - } - - var geometry2 = new BufferGeometry(); - - var indices = this.index.array; - var attributes = this.attributes; - - for ( var name in attributes ) { - - var attribute = attributes[ name ]; - - var array = attribute.array; - var itemSize = attribute.itemSize; - - var array2 = new array.constructor( indices.length * itemSize ); - - var index = 0, index2 = 0; - - for ( var i = 0, l = indices.length; i < l; i ++ ) { - - index = indices[ i ] * itemSize; - - for ( var j = 0; j < itemSize; j ++ ) { - - array2[ index2 ++ ] = array[ index ++ ]; - - } - - } - - geometry2.addAttribute( name, new BufferAttribute( array2, itemSize ) ); - - } - - return geometry2; - - }, - - toJSON: function () { - - var data = { - metadata: { - version: 4.4, - type: 'BufferGeometry', - generator: 'BufferGeometry.toJSON' - } - }; - - // standard BufferGeometry serialization - - data.uuid = this.uuid; - data.type = this.type; - if ( this.name !== '' ) data.name = this.name; - - if ( this.parameters !== undefined ) { - - var parameters = this.parameters; - - for ( var key in parameters ) { - - if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; - - } - - return data; - - } - - data.data = { attributes: {} }; - - var index = this.index; - - if ( index !== null ) { - - var array = Array.prototype.slice.call( index.array ); - - data.data.index = { - type: index.array.constructor.name, - array: array - }; - - } - - var attributes = this.attributes; - - for ( var key in attributes ) { - - var attribute = attributes[ key ]; - - var array = Array.prototype.slice.call( attribute.array ); - - data.data.attributes[ key ] = { - itemSize: attribute.itemSize, - type: attribute.array.constructor.name, - array: array, - normalized: attribute.normalized - }; - - } - - var groups = this.groups; - - if ( groups.length > 0 ) { - - data.data.groups = JSON.parse( JSON.stringify( groups ) ); - - } - - var boundingSphere = this.boundingSphere; - - if ( boundingSphere !== null ) { - - data.data.boundingSphere = { - center: boundingSphere.center.toArray(), - radius: boundingSphere.radius - }; - - } - - return data; - - }, - - clone: function () { - - /* - // Handle primitives - - var parameters = this.parameters; - - if ( parameters !== undefined ) { - - var values = []; - - for ( var key in parameters ) { - - values.push( parameters[ key ] ); - - } - - var geometry = Object.create( this.constructor.prototype ); - this.constructor.apply( geometry, values ); - return geometry; - - } - - return new this.constructor().copy( this ); - */ - - return new BufferGeometry().copy( this ); - - }, - - copy: function ( source ) { - - var index = source.index; - - if ( index !== null ) { - - this.setIndex( index.clone() ); - - } - - var attributes = source.attributes; - - for ( var name in attributes ) { - - var attribute = attributes[ name ]; - this.addAttribute( name, attribute.clone() ); - - } - - var groups = source.groups; - - for ( var i = 0, l = groups.length; i < l; i ++ ) { - - var group = groups[ i ]; - this.addGroup( group.start, group.count, group.materialIndex ); - - } - - return this; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - BufferGeometry.MaxIndex = 65535; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author mikael emtinger / http://gomo.se/ - * @author jonobr1 / http://jonobr1.com/ - */ - - function Mesh( geometry, material ) { - - Object3D.call( this ); - - this.type = 'Mesh'; - - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new MeshBasicMaterial( { color: Math.random() * 0xffffff } ); - - this.drawMode = TrianglesDrawMode; - - this.updateMorphTargets(); - - } - - Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Mesh, - - isMesh: true, - - setDrawMode: function ( value ) { - - this.drawMode = value; - - }, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - this.drawMode = source.drawMode; - - return this; - - }, - - updateMorphTargets: function () { - - var morphTargets = this.geometry.morphTargets; - - if ( morphTargets !== undefined && morphTargets.length > 0 ) { - - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; - - for ( var m = 0, ml = morphTargets.length; m < ml; m ++ ) { - - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ morphTargets[ m ].name ] = m; - - } - - } - - }, - - raycast: ( function () { - - var inverseMatrix = new Matrix4(); - var ray = new Ray(); - var sphere = new Sphere(); - - var vA = new Vector3(); - var vB = new Vector3(); - var vC = new Vector3(); - - var tempA = new Vector3(); - var tempB = new Vector3(); - var tempC = new Vector3(); - - var uvA = new Vector2(); - var uvB = new Vector2(); - var uvC = new Vector2(); - - var barycoord = new Vector3(); - - var intersectionPoint = new Vector3(); - var intersectionPointWorld = new Vector3(); - - function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) { - - Triangle.barycoordFromPoint( point, p1, p2, p3, barycoord ); - - uv1.multiplyScalar( barycoord.x ); - uv2.multiplyScalar( barycoord.y ); - uv3.multiplyScalar( barycoord.z ); - - uv1.add( uv2 ).add( uv3 ); - - return uv1.clone(); - - } - - function checkIntersection( object, raycaster, ray, pA, pB, pC, point ) { - - var intersect; - var material = object.material; - - if ( material.side === BackSide ) { - - intersect = ray.intersectTriangle( pC, pB, pA, true, point ); - - } else { - - intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point ); - - } - - if ( intersect === null ) return null; - - intersectionPointWorld.copy( point ); - intersectionPointWorld.applyMatrix4( object.matrixWorld ); - - var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); - - if ( distance < raycaster.near || distance > raycaster.far ) return null; - - return { - distance: distance, - point: intersectionPointWorld.clone(), - object: object - }; - - } - - function checkBufferGeometryIntersection( object, raycaster, ray, positions, uvs, a, b, c ) { - - vA.fromArray( positions, a * 3 ); - vB.fromArray( positions, b * 3 ); - vC.fromArray( positions, c * 3 ); - - var intersection = checkIntersection( object, raycaster, ray, vA, vB, vC, intersectionPoint ); - - if ( intersection ) { - - if ( uvs ) { - - uvA.fromArray( uvs, a * 2 ); - uvB.fromArray( uvs, b * 2 ); - uvC.fromArray( uvs, c * 2 ); - - intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC ); - - } - - intersection.face = new Face3( a, b, c, Triangle.normal( vA, vB, vC ) ); - intersection.faceIndex = a; - - } - - return intersection; - - } - - return function raycast( raycaster, intersects ) { - - var geometry = this.geometry; - var material = this.material; - var matrixWorld = this.matrixWorld; - - if ( material === undefined ) return; - - // Checking boundingSphere distance to ray - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - - sphere.copy( geometry.boundingSphere ); - sphere.applyMatrix4( matrixWorld ); - - if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; - - // - - inverseMatrix.getInverse( matrixWorld ); - ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); - - // Check boundingBox before continuing - - if ( geometry.boundingBox !== null ) { - - if ( ray.intersectsBox( geometry.boundingBox ) === false ) return; - - } - - var uvs, intersection; - - if ( geometry.isBufferGeometry ) { - - var a, b, c; - var index = geometry.index; - var attributes = geometry.attributes; - var positions = attributes.position.array; - - if ( attributes.uv !== undefined ) { - - uvs = attributes.uv.array; - - } - - if ( index !== null ) { - - var indices = index.array; - - for ( var i = 0, l = indices.length; i < l; i += 3 ) { - - a = indices[ i ]; - b = indices[ i + 1 ]; - c = indices[ i + 2 ]; - - intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indices buffer semantics - intersects.push( intersection ); - - } - - } - - } else { - - - for ( var i = 0, l = positions.length; i < l; i += 9 ) { - - a = i / 3; - b = a + 1; - c = a + 2; - - intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c ); - - if ( intersection ) { - - intersection.index = a; // triangle number in positions buffer semantics - intersects.push( intersection ); - - } - - } - - } - - } else if ( geometry.isGeometry ) { - - var fvA, fvB, fvC; - var isFaceMaterial = (material && material.isMultiMaterial); - var materials = isFaceMaterial === true ? material.materials : null; - - var vertices = geometry.vertices; - var faces = geometry.faces; - var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; - if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs; - - for ( var f = 0, fl = faces.length; f < fl; f ++ ) { - - var face = faces[ f ]; - var faceMaterial = isFaceMaterial === true ? materials[ face.materialIndex ] : material; - - if ( faceMaterial === undefined ) continue; - - fvA = vertices[ face.a ]; - fvB = vertices[ face.b ]; - fvC = vertices[ face.c ]; - - if ( faceMaterial.morphTargets === true ) { - - var morphTargets = geometry.morphTargets; - var morphInfluences = this.morphTargetInfluences; - - vA.set( 0, 0, 0 ); - vB.set( 0, 0, 0 ); - vC.set( 0, 0, 0 ); - - for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { - - var influence = morphInfluences[ t ]; - - if ( influence === 0 ) continue; - - var targets = morphTargets[ t ].vertices; - - vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence ); - vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence ); - vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence ); - - } - - vA.add( fvA ); - vB.add( fvB ); - vC.add( fvC ); - - fvA = vA; - fvB = vB; - fvC = vC; - - } - - intersection = checkIntersection( this, raycaster, ray, fvA, fvB, fvC, intersectionPoint ); - - if ( intersection ) { - - if ( uvs ) { - - var uvs_f = uvs[ f ]; - uvA.copy( uvs_f[ 0 ] ); - uvB.copy( uvs_f[ 1 ] ); - uvC.copy( uvs_f[ 2 ] ); - - intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC ); - - } - - intersection.face = face; - intersection.faceIndex = f; - intersects.push( intersection ); - - } - - } - - } - - }; - - }() ), - - clone: function () { - - return new this.constructor( this.geometry, this.material ).copy( this ); - - } - - } ); - - /** - * @author Mugen87 / https://github.com/Mugen87 - */ - - function BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { - - BufferGeometry.call( this ); - - this.type = 'BoxBufferGeometry'; - - this.parameters = { - width: width, - height: height, - depth: depth, - widthSegments: widthSegments, - heightSegments: heightSegments, - depthSegments: depthSegments - }; - - var scope = this; - - // segments - widthSegments = Math.floor( widthSegments ) || 1; - heightSegments = Math.floor( heightSegments ) || 1; - depthSegments = Math.floor( depthSegments ) || 1; - - // these are used to calculate buffer length - var vertexCount = calculateVertexCount( widthSegments, heightSegments, depthSegments ); - var indexCount = calculateIndexCount( widthSegments, heightSegments, depthSegments ); - - // buffers - var indices = new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ); - var vertices = new Float32Array( vertexCount * 3 ); - var normals = new Float32Array( vertexCount * 3 ); - var uvs = new Float32Array( vertexCount * 2 ); - - // offset variables - var vertexBufferOffset = 0; - var uvBufferOffset = 0; - var indexBufferOffset = 0; - var numberOfVertices = 0; - - // group variables - var groupStart = 0; - - // build each side of the box geometry - buildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px - buildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx - buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py - buildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny - buildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz - buildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz - - // build geometry - this.setIndex( new BufferAttribute( indices, 1 ) ); - this.addAttribute( 'position', new BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) ); - - // helper functions - - function calculateVertexCount( w, h, d ) { - - var vertices = 0; - - // calculate the amount of vertices for each side (plane) - vertices += (w + 1) * (h + 1) * 2; // xy - vertices += (w + 1) * (d + 1) * 2; // xz - vertices += (d + 1) * (h + 1) * 2; // zy - - return vertices; - - } - - function calculateIndexCount( w, h, d ) { - - var index = 0; - - // calculate the amount of squares for each side - index += w * h * 2; // xy - index += w * d * 2; // xz - index += d * h * 2; // zy - - return index * 6; // two triangles per square => six vertices per square - - } - - function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { - - var segmentWidth = width / gridX; - var segmentHeight = height / gridY; - - var widthHalf = width / 2; - var heightHalf = height / 2; - var depthHalf = depth / 2; - - var gridX1 = gridX + 1; - var gridY1 = gridY + 1; - - var vertexCounter = 0; - var groupCount = 0; - - var vector = new Vector3(); - - // generate vertices, normals and uvs - - for ( var iy = 0; iy < gridY1; iy ++ ) { - - var y = iy * segmentHeight - heightHalf; - - for ( var ix = 0; ix < gridX1; ix ++ ) { - - var x = ix * segmentWidth - widthHalf; - - // set values to correct vector component - vector[ u ] = x * udir; - vector[ v ] = y * vdir; - vector[ w ] = depthHalf; - - // now apply vector to vertex buffer - vertices[ vertexBufferOffset ] = vector.x; - vertices[ vertexBufferOffset + 1 ] = vector.y; - vertices[ vertexBufferOffset + 2 ] = vector.z; - - // set values to correct vector component - vector[ u ] = 0; - vector[ v ] = 0; - vector[ w ] = depth > 0 ? 1 : - 1; - - // now apply vector to normal buffer - normals[ vertexBufferOffset ] = vector.x; - normals[ vertexBufferOffset + 1 ] = vector.y; - normals[ vertexBufferOffset + 2 ] = vector.z; - - // uvs - uvs[ uvBufferOffset ] = ix / gridX; - uvs[ uvBufferOffset + 1 ] = 1 - ( iy / gridY ); - - // update offsets and counters - vertexBufferOffset += 3; - uvBufferOffset += 2; - vertexCounter += 1; - - } - - } - - // 1. you need three indices to draw a single face - // 2. a single segment consists of two faces - // 3. so we need to generate six (2*3) indices per segment - - for ( iy = 0; iy < gridY; iy ++ ) { - - for ( ix = 0; ix < gridX; ix ++ ) { - - // indices - var a = numberOfVertices + ix + gridX1 * iy; - var b = numberOfVertices + ix + gridX1 * ( iy + 1 ); - var c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); - var d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; - - // face one - indices[ indexBufferOffset ] = a; - indices[ indexBufferOffset + 1 ] = b; - indices[ indexBufferOffset + 2 ] = d; - - // face two - indices[ indexBufferOffset + 3 ] = b; - indices[ indexBufferOffset + 4 ] = c; - indices[ indexBufferOffset + 5 ] = d; - - // update offsets and counters - indexBufferOffset += 6; - groupCount += 6; - - } - - } - - // add a group to the geometry. this will ensure multi material support - scope.addGroup( groupStart, groupCount, materialIndex ); - - // calculate new start value for groups - groupStart += groupCount; - - // update total number of vertices - numberOfVertices += vertexCounter; - - } - - } - - BoxBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - BoxBufferGeometry.prototype.constructor = BoxBufferGeometry; - - /** - * @author mrdoob / http://mrdoob.com/ - * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as - */ - - function PlaneBufferGeometry( width, height, widthSegments, heightSegments ) { - - BufferGeometry.call( this ); - - this.type = 'PlaneBufferGeometry'; - - this.parameters = { - width: width, - height: height, - widthSegments: widthSegments, - heightSegments: heightSegments - }; - - var width_half = width / 2; - var height_half = height / 2; - - var gridX = Math.floor( widthSegments ) || 1; - var gridY = Math.floor( heightSegments ) || 1; - - var gridX1 = gridX + 1; - var gridY1 = gridY + 1; - - var segment_width = width / gridX; - var segment_height = height / gridY; - - var vertices = new Float32Array( gridX1 * gridY1 * 3 ); - var normals = new Float32Array( gridX1 * gridY1 * 3 ); - var uvs = new Float32Array( gridX1 * gridY1 * 2 ); - - var offset = 0; - var offset2 = 0; - - for ( var iy = 0; iy < gridY1; iy ++ ) { - - var y = iy * segment_height - height_half; - - for ( var ix = 0; ix < gridX1; ix ++ ) { - - var x = ix * segment_width - width_half; - - vertices[ offset ] = x; - vertices[ offset + 1 ] = - y; - - normals[ offset + 2 ] = 1; - - uvs[ offset2 ] = ix / gridX; - uvs[ offset2 + 1 ] = 1 - ( iy / gridY ); - - offset += 3; - offset2 += 2; - - } - - } - - offset = 0; - - var indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 ); - - for ( var iy = 0; iy < gridY; iy ++ ) { - - for ( var ix = 0; ix < gridX; ix ++ ) { - - var a = ix + gridX1 * iy; - var b = ix + gridX1 * ( iy + 1 ); - var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); - var d = ( ix + 1 ) + gridX1 * iy; - - indices[ offset ] = a; - indices[ offset + 1 ] = b; - indices[ offset + 2 ] = d; - - indices[ offset + 3 ] = b; - indices[ offset + 4 ] = c; - indices[ offset + 5 ] = d; - - offset += 6; - - } - - } - - this.setIndex( new BufferAttribute( indices, 1 ) ); - this.addAttribute( 'position', new BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) ); - - } - - PlaneBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - PlaneBufferGeometry.prototype.constructor = PlaneBufferGeometry; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author mikael emtinger / http://gomo.se/ - * @author WestLangley / http://github.com/WestLangley - */ - - function Camera() { - - Object3D.call( this ); - - this.type = 'Camera'; - - this.matrixWorldInverse = new Matrix4(); - this.projectionMatrix = new Matrix4(); - - } - - Camera.prototype = Object.create( Object3D.prototype ); - Camera.prototype.constructor = Camera; - - Camera.prototype.isCamera = true; - - Camera.prototype.getWorldDirection = function () { - - var quaternion = new Quaternion(); - - return function getWorldDirection( optionalTarget ) { - - var result = optionalTarget || new Vector3(); - - this.getWorldQuaternion( quaternion ); - - return result.set( 0, 0, - 1 ).applyQuaternion( quaternion ); - - }; - - }(); - - Camera.prototype.lookAt = function () { - - // This routine does not support cameras with rotated and/or translated parent(s) - - var m1 = new Matrix4(); - - return function lookAt( vector ) { - - m1.lookAt( this.position, vector, this.up ); - - this.quaternion.setFromRotationMatrix( m1 ); - - }; - - }(); - - Camera.prototype.clone = function () { - - return new this.constructor().copy( this ); - - }; - - Camera.prototype.copy = function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - this.matrixWorldInverse.copy( source.matrixWorldInverse ); - this.projectionMatrix.copy( source.projectionMatrix ); - - return this; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author greggman / http://games.greggman.com/ - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author tschw - */ - - function PerspectiveCamera( fov, aspect, near, far ) { - - Camera.call( this ); - - this.type = 'PerspectiveCamera'; - - this.fov = fov !== undefined ? fov : 50; - this.zoom = 1; - - this.near = near !== undefined ? near : 0.1; - this.far = far !== undefined ? far : 2000; - this.focus = 10; - - this.aspect = aspect !== undefined ? aspect : 1; - this.view = null; - - this.filmGauge = 35; // width of the film (default in millimeters) - this.filmOffset = 0; // horizontal film offset (same unit as gauge) - - this.updateProjectionMatrix(); - - } - - PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), { - - constructor: PerspectiveCamera, - - isPerspectiveCamera: true, - - copy: function ( source ) { - - Camera.prototype.copy.call( this, source ); - - this.fov = source.fov; - this.zoom = source.zoom; - - this.near = source.near; - this.far = source.far; - this.focus = source.focus; - - this.aspect = source.aspect; - this.view = source.view === null ? null : Object.assign( {}, source.view ); - - this.filmGauge = source.filmGauge; - this.filmOffset = source.filmOffset; - - return this; - - }, - - /** - * Sets the FOV by focal length in respect to the current .filmGauge. - * - * The default film gauge is 35, so that the focal length can be specified for - * a 35mm (full frame) camera. - * - * Values for focal length and film gauge must have the same unit. - */ - setFocalLength: function ( focalLength ) { - - // see http://www.bobatkins.com/photography/technical/field_of_view.html - var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; - - this.fov = _Math.RAD2DEG * 2 * Math.atan( vExtentSlope ); - this.updateProjectionMatrix(); - - }, - - /** - * Calculates the focal length from the current .fov and .filmGauge. - */ - getFocalLength: function () { - - var vExtentSlope = Math.tan( _Math.DEG2RAD * 0.5 * this.fov ); - - return 0.5 * this.getFilmHeight() / vExtentSlope; - - }, - - getEffectiveFOV: function () { - - return _Math.RAD2DEG * 2 * Math.atan( - Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom ); - - }, - - getFilmWidth: function () { - - // film not completely covered in portrait format (aspect < 1) - return this.filmGauge * Math.min( this.aspect, 1 ); - - }, - - getFilmHeight: function () { - - // film not completely covered in landscape format (aspect > 1) - return this.filmGauge / Math.max( this.aspect, 1 ); - - }, - - /** - * Sets an offset in a larger frustum. This is useful for multi-window or - * multi-monitor/multi-machine setups. - * - * For example, if you have 3x2 monitors and each monitor is 1920x1080 and - * the monitors are in grid like this - * - * +---+---+---+ - * | A | B | C | - * +---+---+---+ - * | D | E | F | - * +---+---+---+ - * - * then for each monitor you would call it like this - * - * var w = 1920; - * var h = 1080; - * var fullWidth = w * 3; - * var fullHeight = h * 2; - * - * --A-- - * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); - * --B-- - * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); - * --C-- - * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); - * --D-- - * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); - * --E-- - * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); - * --F-- - * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); - * - * Note there is no reason monitors have to be the same size or in a grid. - */ - setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { - - this.aspect = fullWidth / fullHeight; - - this.view = { - fullWidth: fullWidth, - fullHeight: fullHeight, - offsetX: x, - offsetY: y, - width: width, - height: height - }; - - this.updateProjectionMatrix(); - - }, - - clearViewOffset: function() { - - this.view = null; - this.updateProjectionMatrix(); - - }, - - updateProjectionMatrix: function () { - - var near = this.near, - top = near * Math.tan( - _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom, - height = 2 * top, - width = this.aspect * height, - left = - 0.5 * width, - view = this.view; - - if ( view !== null ) { - - var fullWidth = view.fullWidth, - fullHeight = view.fullHeight; - - left += view.offsetX * width / fullWidth; - top -= view.offsetY * height / fullHeight; - width *= view.width / fullWidth; - height *= view.height / fullHeight; - - } - - var skew = this.filmOffset; - if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); - - this.projectionMatrix.makeFrustum( - left, left + width, top - height, top, near, this.far ); - - }, - - toJSON: function ( meta ) { - - var data = Object3D.prototype.toJSON.call( this, meta ); - - data.object.fov = this.fov; - data.object.zoom = this.zoom; - - data.object.near = this.near; - data.object.far = this.far; - data.object.focus = this.focus; - - data.object.aspect = this.aspect; - - if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); - - data.object.filmGauge = this.filmGauge; - data.object.filmOffset = this.filmOffset; - - return data; - - } - - } ); - - /** - * @author alteredq / http://alteredqualia.com/ - * @author arose / http://github.com/arose - */ - - function OrthographicCamera( left, right, top, bottom, near, far ) { - - Camera.call( this ); - - this.type = 'OrthographicCamera'; - - this.zoom = 1; - this.view = null; - - this.left = left; - this.right = right; - this.top = top; - this.bottom = bottom; - - this.near = ( near !== undefined ) ? near : 0.1; - this.far = ( far !== undefined ) ? far : 2000; - - this.updateProjectionMatrix(); - - } - - OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), { - - constructor: OrthographicCamera, - - isOrthographicCamera: true, - - copy: function ( source ) { - - Camera.prototype.copy.call( this, source ); - - this.left = source.left; - this.right = source.right; - this.top = source.top; - this.bottom = source.bottom; - this.near = source.near; - this.far = source.far; - - this.zoom = source.zoom; - this.view = source.view === null ? null : Object.assign( {}, source.view ); - - return this; - - }, - - setViewOffset: function( fullWidth, fullHeight, x, y, width, height ) { - - this.view = { - fullWidth: fullWidth, - fullHeight: fullHeight, - offsetX: x, - offsetY: y, - width: width, - height: height - }; - - this.updateProjectionMatrix(); - - }, - - clearViewOffset: function() { - - this.view = null; - this.updateProjectionMatrix(); - - }, - - updateProjectionMatrix: function () { - - var dx = ( this.right - this.left ) / ( 2 * this.zoom ); - var dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); - var cx = ( this.right + this.left ) / 2; - var cy = ( this.top + this.bottom ) / 2; - - var left = cx - dx; - var right = cx + dx; - var top = cy + dy; - var bottom = cy - dy; - - if ( this.view !== null ) { - - var zoomW = this.zoom / ( this.view.width / this.view.fullWidth ); - var zoomH = this.zoom / ( this.view.height / this.view.fullHeight ); - var scaleW = ( this.right - this.left ) / this.view.width; - var scaleH = ( this.top - this.bottom ) / this.view.height; - - left += scaleW * ( this.view.offsetX / zoomW ); - right = left + scaleW * ( this.view.width / zoomW ); - top -= scaleH * ( this.view.offsetY / zoomH ); - bottom = top - scaleH * ( this.view.height / zoomH ); - - } - - this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); - - }, - - toJSON: function ( meta ) { - - var data = Object3D.prototype.toJSON.call( this, meta ); - - data.object.zoom = this.zoom; - data.object.left = this.left; - data.object.right = this.right; - data.object.top = this.top; - data.object.bottom = this.bottom; - data.object.near = this.near; - data.object.far = this.far; - - if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); - - return data; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLIndexedBufferRenderer( gl, extensions, infoRender ) { - - var mode; - - function setMode( value ) { - - mode = value; - - } - - var type, size; - - function setIndex( index ) { - - if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { - - type = gl.UNSIGNED_INT; - size = 4; - - } else if ( index.array instanceof Uint16Array ) { - - type = gl.UNSIGNED_SHORT; - size = 2; - - } else { - - type = gl.UNSIGNED_BYTE; - size = 1; - } - - } - - function render( start, count ) { - - gl.drawElements( mode, count, type, start * size ); - - infoRender.calls ++; - infoRender.vertices += count; - - if ( mode === gl.TRIANGLES ) infoRender.faces += count / 3; - - } - - function renderInstances( geometry, start, count ) { - - var extension = extensions.get( 'ANGLE_instanced_arrays' ); - - if ( extension === null ) { - - console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; - - } - - extension.drawElementsInstancedANGLE( mode, count, type, start * size, geometry.maxInstancedCount ); - - infoRender.calls ++; - infoRender.vertices += count * geometry.maxInstancedCount; - - if ( mode === gl.TRIANGLES ) infoRender.faces += geometry.maxInstancedCount * count / 3; - - } - - return { - - setMode: setMode, - setIndex: setIndex, - render: render, - renderInstances: renderInstances - - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLBufferRenderer( gl, extensions, infoRender ) { - - var mode; - - function setMode( value ) { - - mode = value; - - } - - function render( start, count ) { - - gl.drawArrays( mode, start, count ); - - infoRender.calls ++; - infoRender.vertices += count; - - if ( mode === gl.TRIANGLES ) infoRender.faces += count / 3; - - } - - function renderInstances( geometry ) { - - var extension = extensions.get( 'ANGLE_instanced_arrays' ); - - if ( extension === null ) { - - console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; - - } - - var position = geometry.attributes.position; - - var count = 0; - - if ( position.isInterleavedBufferAttribute ) { - - count = position.data.count; - - extension.drawArraysInstancedANGLE( mode, 0, count, geometry.maxInstancedCount ); - - } else { - - count = position.count; - - extension.drawArraysInstancedANGLE( mode, 0, count, geometry.maxInstancedCount ); - - } - - infoRender.calls ++; - infoRender.vertices += count * geometry.maxInstancedCount; - - if ( mode === gl.TRIANGLES ) infoRender.faces += geometry.maxInstancedCount * count / 3; - - } - - return { - setMode: setMode, - render: render, - renderInstances: renderInstances - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLLights() { - - var lights = {}; - - return { - - get: function ( light ) { - - if ( lights[ light.id ] !== undefined ) { - - return lights[ light.id ]; - - } - - var uniforms; - - switch ( light.type ) { - - case 'DirectionalLight': - uniforms = { - direction: new Vector3(), - color: new Color(), - - shadow: false, - shadowBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; - - case 'SpotLight': - uniforms = { - position: new Vector3(), - direction: new Vector3(), - color: new Color(), - distance: 0, - coneCos: 0, - penumbraCos: 0, - decay: 0, - - shadow: false, - shadowBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; - - case 'PointLight': - uniforms = { - position: new Vector3(), - color: new Color(), - distance: 0, - decay: 0, - - shadow: false, - shadowBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; - - case 'HemisphereLight': - uniforms = { - direction: new Vector3(), - skyColor: new Color(), - groundColor: new Color() - }; - break; - - case 'RectAreaLight': - uniforms = { - color: new Color(), - position: new Vector3(), - halfWidth: new Vector3(), - halfHeight: new Vector3() - // TODO (abelnation): set RectAreaLight shadow uniforms - }; - break; - - } - - lights[ light.id ] = uniforms; - - return uniforms; - - } - - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function addLineNumbers( string ) { - - var lines = string.split( '\n' ); - - for ( var i = 0; i < lines.length; i ++ ) { - - lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; - - } - - return lines.join( '\n' ); - - } - - function WebGLShader( gl, type, string ) { - - var shader = gl.createShader( type ); - - gl.shaderSource( shader, string ); - gl.compileShader( shader ); - - if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { - - console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); - - } - - if ( gl.getShaderInfoLog( shader ) !== '' ) { - - console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) ); - - } - - // --enable-privileged-webgl-extension - // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); - - return shader; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - var programIdCount = 0; - - function getEncodingComponents( encoding ) { - - switch ( encoding ) { - - case LinearEncoding: - return [ 'Linear','( value )' ]; - case sRGBEncoding: - return [ 'sRGB','( value )' ]; - case RGBEEncoding: - return [ 'RGBE','( value )' ]; - case RGBM7Encoding: - return [ 'RGBM','( value, 7.0 )' ]; - case RGBM16Encoding: - return [ 'RGBM','( value, 16.0 )' ]; - case RGBDEncoding: - return [ 'RGBD','( value, 256.0 )' ]; - case GammaEncoding: - return [ 'Gamma','( value, float( GAMMA_FACTOR ) )' ]; - default: - throw new Error( 'unsupported encoding: ' + encoding ); - - } - - } - - function getTexelDecodingFunction( functionName, encoding ) { - - var components = getEncodingComponents( encoding ); - return "vec4 " + functionName + "( vec4 value ) { return " + components[ 0 ] + "ToLinear" + components[ 1 ] + "; }"; - - } - - function getTexelEncodingFunction( functionName, encoding ) { - - var components = getEncodingComponents( encoding ); - return "vec4 " + functionName + "( vec4 value ) { return LinearTo" + components[ 0 ] + components[ 1 ] + "; }"; - - } - - function getToneMappingFunction( functionName, toneMapping ) { - - var toneMappingName; - - switch ( toneMapping ) { - - case LinearToneMapping: - toneMappingName = "Linear"; - break; - - case ReinhardToneMapping: - toneMappingName = "Reinhard"; - break; - - case Uncharted2ToneMapping: - toneMappingName = "Uncharted2"; - break; - - case CineonToneMapping: - toneMappingName = "OptimizedCineon"; - break; - - default: - throw new Error( 'unsupported toneMapping: ' + toneMapping ); - - } - - return "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }"; - - } - - function generateExtensions( extensions, parameters, rendererExtensions ) { - - extensions = extensions || {}; - - var chunks = [ - ( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '', - ( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '', - ( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '', - ( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : '' - ]; - - return chunks.filter( filterEmptyLine ).join( '\n' ); - - } - - function generateDefines( defines ) { - - var chunks = []; - - for ( var name in defines ) { - - var value = defines[ name ]; - - if ( value === false ) continue; - - chunks.push( '#define ' + name + ' ' + value ); - - } - - return chunks.join( '\n' ); - - } - - function fetchAttributeLocations( gl, program, identifiers ) { - - var attributes = {}; - - var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); - - for ( var i = 0; i < n; i ++ ) { - - var info = gl.getActiveAttrib( program, i ); - var name = info.name; - - // console.log("THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); - - attributes[ name ] = gl.getAttribLocation( program, name ); - - } - - return attributes; - - } - - function filterEmptyLine( string ) { - - return string !== ''; - - } - - function replaceLightNums( string, parameters ) { - - return string - .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) - .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) - .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) - .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) - .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ); - - } - - function parseIncludes( string ) { - - var pattern = /#include +<([\w\d.]+)>/g; - - function replace( match, include ) { - - var replace = ShaderChunk[ include ]; - - if ( replace === undefined ) { - - throw new Error( 'Can not resolve #include <' + include + '>' ); - - } - - return parseIncludes( replace ); - - } - - return string.replace( pattern, replace ); - - } - - function unrollLoops( string ) { - - var pattern = /for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; - - function replace( match, start, end, snippet ) { - - var unroll = ''; - - for ( var i = parseInt( start ); i < parseInt( end ); i ++ ) { - - unroll += snippet.replace( /\[ i \]/g, '[ ' + i + ' ]' ); - - } - - return unroll; - - } - - return string.replace( pattern, replace ); - - } - - function WebGLProgram( renderer, code, material, parameters ) { - - var gl = renderer.context; - - var extensions = material.extensions; - var defines = material.defines; - - var vertexShader = material.__webglShader.vertexShader; - var fragmentShader = material.__webglShader.fragmentShader; - - var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; - - if ( parameters.shadowMapType === PCFShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; - - } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; - - } - - var envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - var envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; - var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; - - if ( parameters.envMap ) { - - switch ( material.envMap.mapping ) { - - case CubeReflectionMapping: - case CubeRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - break; - - case CubeUVReflectionMapping: - case CubeUVRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; - break; - - case EquirectangularReflectionMapping: - case EquirectangularRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC'; - break; - - case SphericalReflectionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_SPHERE'; - break; - - } - - switch ( material.envMap.mapping ) { - - case CubeRefractionMapping: - case EquirectangularRefractionMapping: - envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; - break; - - } - - switch ( material.combine ) { - - case MultiplyOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; - break; - - case MixOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; - break; - - case AddOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; - break; - - } - - } - - var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; - - // console.log( 'building new program ' ); - - // - - var customExtensions = generateExtensions( extensions, parameters, renderer.extensions ); - - var customDefines = generateDefines( defines ); - - // - - var program = gl.createProgram(); - - var prefixVertex, prefixFragment; - - if ( material.isRawShaderMaterial ) { - - prefixVertex = [ - - customDefines, - - '\n' - - ].filter( filterEmptyLine ).join( '\n' ); - - prefixFragment = [ - - customExtensions, - customDefines, - - '\n' - - ].filter( filterEmptyLine ).join( '\n' ); - - } else { - - prefixVertex = [ - - 'precision ' + parameters.precision + ' float;', - 'precision ' + parameters.precision + ' int;', - - '#define SHADER_NAME ' + material.__webglShader.name, - - customDefines, - - parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', - - '#define GAMMA_FACTOR ' + gammaFactorDefine, - - '#define MAX_BONES ' + parameters.maxBones, - - parameters.map ? '#define USE_MAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - parameters.vertexColors ? '#define USE_COLOR' : '', - - parameters.flatShading ? '#define FLAT_SHADED' : '', - - parameters.skinning ? '#define USE_SKINNING' : '', - parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', - - parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', - parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', - - '#define NUM_CLIPPING_PLANES ' + parameters.numClippingPlanes, - - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - - parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', - - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - - 'uniform mat4 modelMatrix;', - 'uniform mat4 modelViewMatrix;', - 'uniform mat4 projectionMatrix;', - 'uniform mat4 viewMatrix;', - 'uniform mat3 normalMatrix;', - 'uniform vec3 cameraPosition;', - - 'attribute vec3 position;', - 'attribute vec3 normal;', - 'attribute vec2 uv;', - - '#ifdef USE_COLOR', - - ' attribute vec3 color;', - - '#endif', - - '#ifdef USE_MORPHTARGETS', - - ' attribute vec3 morphTarget0;', - ' attribute vec3 morphTarget1;', - ' attribute vec3 morphTarget2;', - ' attribute vec3 morphTarget3;', - - ' #ifdef USE_MORPHNORMALS', - - ' attribute vec3 morphNormal0;', - ' attribute vec3 morphNormal1;', - ' attribute vec3 morphNormal2;', - ' attribute vec3 morphNormal3;', - - ' #else', - - ' attribute vec3 morphTarget4;', - ' attribute vec3 morphTarget5;', - ' attribute vec3 morphTarget6;', - ' attribute vec3 morphTarget7;', - - ' #endif', - - '#endif', - - '#ifdef USE_SKINNING', - - ' attribute vec4 skinIndex;', - ' attribute vec4 skinWeight;', - - '#endif', - - '\n' - - ].filter( filterEmptyLine ).join( '\n' ); - - prefixFragment = [ - - customExtensions, - - 'precision ' + parameters.precision + ' float;', - 'precision ' + parameters.precision + ' int;', - - '#define SHADER_NAME ' + material.__webglShader.name, - - customDefines, - - parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '', - - '#define GAMMA_FACTOR ' + gammaFactorDefine, - - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '', - - parameters.map ? '#define USE_MAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapTypeDefine : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.envMap ? '#define ' + envMapBlendingDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - parameters.vertexColors ? '#define USE_COLOR' : '', - - parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', - - parameters.flatShading ? '#define FLAT_SHADED' : '', - - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', - - '#define NUM_CLIPPING_PLANES ' + parameters.numClippingPlanes, - '#define UNION_CLIPPING_PLANES ' + (parameters.numClippingPlanes - parameters.numClipIntersection), - - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - - parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : '', - - parameters.physicallyCorrectLights ? "#define PHYSICALLY_CORRECT_LIGHTS" : '', - - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - - parameters.envMap && renderer.extensions.get( 'EXT_shader_texture_lod' ) ? '#define TEXTURE_LOD_EXT' : '', - - 'uniform mat4 viewMatrix;', - 'uniform vec3 cameraPosition;', - - ( parameters.toneMapping !== NoToneMapping ) ? "#define TONE_MAPPING" : '', - ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below - ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( "toneMapping", parameters.toneMapping ) : '', - - ( parameters.outputEncoding || parameters.mapEncoding || parameters.envMapEncoding || parameters.emissiveMapEncoding ) ? ShaderChunk[ 'encodings_pars_fragment' ] : '', // this code is required here because it is used by the various encoding/decoding function defined below - parameters.mapEncoding ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '', - parameters.envMapEncoding ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '', - parameters.emissiveMapEncoding ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '', - parameters.outputEncoding ? getTexelEncodingFunction( "linearToOutputTexel", parameters.outputEncoding ) : '', - - parameters.depthPacking ? "#define DEPTH_PACKING " + material.depthPacking : '', - - '\n' - - ].filter( filterEmptyLine ).join( '\n' ); - - } - - vertexShader = parseIncludes( vertexShader, parameters ); - vertexShader = replaceLightNums( vertexShader, parameters ); - - fragmentShader = parseIncludes( fragmentShader, parameters ); - fragmentShader = replaceLightNums( fragmentShader, parameters ); - - if ( ! material.isShaderMaterial ) { - - vertexShader = unrollLoops( vertexShader ); - fragmentShader = unrollLoops( fragmentShader ); - - } - - var vertexGlsl = prefixVertex + vertexShader; - var fragmentGlsl = prefixFragment + fragmentShader; - - // console.log( '*VERTEX*', vertexGlsl ); - // console.log( '*FRAGMENT*', fragmentGlsl ); - - var glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); - var glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); - - gl.attachShader( program, glVertexShader ); - gl.attachShader( program, glFragmentShader ); - - // Force a particular attribute to index 0. - - if ( material.index0AttributeName !== undefined ) { - - gl.bindAttribLocation( program, 0, material.index0AttributeName ); - - } else if ( parameters.morphTargets === true ) { - - // programs with morphTargets displace position out of attribute 0 - gl.bindAttribLocation( program, 0, 'position' ); - - } - - gl.linkProgram( program ); - - var programLog = gl.getProgramInfoLog( program ); - var vertexLog = gl.getShaderInfoLog( glVertexShader ); - var fragmentLog = gl.getShaderInfoLog( glFragmentShader ); - - var runnable = true; - var haveDiagnostics = true; - - // console.log( '**VERTEX**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader ) ); - // console.log( '**FRAGMENT**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader ) ); - - if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { - - runnable = false; - - console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog ); - - } else if ( programLog !== '' ) { - - console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); - - } else if ( vertexLog === '' || fragmentLog === '' ) { - - haveDiagnostics = false; - - } - - if ( haveDiagnostics ) { - - this.diagnostics = { - - runnable: runnable, - material: material, - - programLog: programLog, - - vertexShader: { - - log: vertexLog, - prefix: prefixVertex - - }, - - fragmentShader: { - - log: fragmentLog, - prefix: prefixFragment - - } - - }; - - } - - // clean up - - gl.deleteShader( glVertexShader ); - gl.deleteShader( glFragmentShader ); - - // set up caching for uniform locations - - var cachedUniforms; - - this.getUniforms = function() { - - if ( cachedUniforms === undefined ) { - - cachedUniforms = - new WebGLUniforms( gl, program, renderer ); - - } - - return cachedUniforms; - - }; - - // set up caching for attribute locations - - var cachedAttributes; - - this.getAttributes = function() { - - if ( cachedAttributes === undefined ) { - - cachedAttributes = fetchAttributeLocations( gl, program ); - - } - - return cachedAttributes; - - }; - - // free resource - - this.destroy = function() { - - gl.deleteProgram( program ); - this.program = undefined; - - }; - - // DEPRECATED - - Object.defineProperties( this, { - - uniforms: { - get: function() { - - console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' ); - return this.getUniforms(); - - } - }, - - attributes: { - get: function() { - - console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' ); - return this.getAttributes(); - - } - } - - } ); - - - // - - this.id = programIdCount ++; - this.code = code; - this.usedTimes = 1; - this.program = program; - this.vertexShader = glVertexShader; - this.fragmentShader = glFragmentShader; - - return this; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLPrograms( renderer, capabilities ) { - - var programs = []; - - var shaderIDs = { - MeshDepthMaterial: 'depth', - MeshNormalMaterial: 'normal', - MeshBasicMaterial: 'basic', - MeshLambertMaterial: 'lambert', - MeshPhongMaterial: 'phong', - MeshToonMaterial: 'phong', - MeshStandardMaterial: 'physical', - MeshPhysicalMaterial: 'physical', - LineBasicMaterial: 'basic', - LineDashedMaterial: 'dashed', - PointsMaterial: 'points' - }; - - var parameterNames = [ - "precision", "supportsVertexTextures", "map", "mapEncoding", "envMap", "envMapMode", "envMapEncoding", - "lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "displacementMap", "specularMap", - "roughnessMap", "metalnessMap", "gradientMap", - "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp", - "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", - "maxBones", "useVertexTexture", "morphTargets", "morphNormals", - "maxMorphTargets", "maxMorphNormals", "premultipliedAlpha", - "numDirLights", "numPointLights", "numSpotLights", "numHemiLights", "numRectAreaLights", - "shadowMapEnabled", "shadowMapType", "toneMapping", 'physicallyCorrectLights', - "alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking" - ]; - - - function allocateBones( object ) { - - if ( capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture ) { - - return 1024; - - } else { - - // default for when object is not specified - // ( for example when prebuilding shader to be used with multiple objects ) - // - // - leave some extra space for other uniforms - // - limit here is ANGLE's 254 max uniform vectors - // (up to 54 should be safe) - - var nVertexUniforms = capabilities.maxVertexUniforms; - var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); - - var maxBones = nVertexMatrices; - - if ( object !== undefined && (object && object.isSkinnedMesh) ) { - - maxBones = Math.min( object.skeleton.bones.length, maxBones ); - - if ( maxBones < object.skeleton.bones.length ) { - - console.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' ); - - } - - } - - return maxBones; - - } - - } - - function getTextureEncodingFromMap( map, gammaOverrideLinear ) { - - var encoding; - - if ( ! map ) { - - encoding = LinearEncoding; - - } else if ( map.isTexture ) { - - encoding = map.encoding; - - } else if ( map.isWebGLRenderTarget ) { - - console.warn( "THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead." ); - encoding = map.texture.encoding; - - } - - // add backwards compatibility for WebGLRenderer.gammaInput/gammaOutput parameter, should probably be removed at some point. - if ( encoding === LinearEncoding && gammaOverrideLinear ) { - - encoding = GammaEncoding; - - } - - return encoding; - - } - - this.getParameters = function ( material, lights, fog, nClipPlanes, nClipIntersection, object ) { - - var shaderID = shaderIDs[ material.type ]; - - // heuristics to create shader parameters according to lights in the scene - // (not to blow over maxLights budget) - - var maxBones = allocateBones( object ); - var precision = renderer.getPrecision(); - - if ( material.precision !== null ) { - - precision = capabilities.getMaxPrecision( material.precision ); - - if ( precision !== material.precision ) { - - console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); - - } - - } - - var currentRenderTarget = renderer.getCurrentRenderTarget(); - - var parameters = { - - shaderID: shaderID, - - precision: precision, - supportsVertexTextures: capabilities.vertexTextures, - outputEncoding: getTextureEncodingFromMap( ( ! currentRenderTarget ) ? null : currentRenderTarget.texture, renderer.gammaOutput ), - map: !! material.map, - mapEncoding: getTextureEncodingFromMap( material.map, renderer.gammaInput ), - envMap: !! material.envMap, - envMapMode: material.envMap && material.envMap.mapping, - envMapEncoding: getTextureEncodingFromMap( material.envMap, renderer.gammaInput ), - envMapCubeUV: ( !! material.envMap ) && ( ( material.envMap.mapping === CubeUVReflectionMapping ) || ( material.envMap.mapping === CubeUVRefractionMapping ) ), - lightMap: !! material.lightMap, - aoMap: !! material.aoMap, - emissiveMap: !! material.emissiveMap, - emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap, renderer.gammaInput ), - bumpMap: !! material.bumpMap, - normalMap: !! material.normalMap, - displacementMap: !! material.displacementMap, - roughnessMap: !! material.roughnessMap, - metalnessMap: !! material.metalnessMap, - specularMap: !! material.specularMap, - alphaMap: !! material.alphaMap, - - gradientMap: !! material.gradientMap, - - combine: material.combine, - - vertexColors: material.vertexColors, - - fog: !! fog, - useFog: material.fog, - fogExp: (fog && fog.isFogExp2), - - flatShading: material.shading === FlatShading, - - sizeAttenuation: material.sizeAttenuation, - logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer, - - skinning: material.skinning, - maxBones: maxBones, - useVertexTexture: capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture, - - morphTargets: material.morphTargets, - morphNormals: material.morphNormals, - maxMorphTargets: renderer.maxMorphTargets, - maxMorphNormals: renderer.maxMorphNormals, - - numDirLights: lights.directional.length, - numPointLights: lights.point.length, - numSpotLights: lights.spot.length, - numRectAreaLights: lights.rectArea.length, - numHemiLights: lights.hemi.length, - - numClippingPlanes: nClipPlanes, - numClipIntersection: nClipIntersection, - - shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && lights.shadows.length > 0, - shadowMapType: renderer.shadowMap.type, - - toneMapping: renderer.toneMapping, - physicallyCorrectLights: renderer.physicallyCorrectLights, - - premultipliedAlpha: material.premultipliedAlpha, - - alphaTest: material.alphaTest, - doubleSided: material.side === DoubleSide, - flipSided: material.side === BackSide, - - depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false - - }; - - return parameters; - - }; - - this.getProgramCode = function ( material, parameters ) { - - var array = []; - - if ( parameters.shaderID ) { - - array.push( parameters.shaderID ); - - } else { - - array.push( material.fragmentShader ); - array.push( material.vertexShader ); - - } - - if ( material.defines !== undefined ) { - - for ( var name in material.defines ) { - - array.push( name ); - array.push( material.defines[ name ] ); - - } - - } - - for ( var i = 0; i < parameterNames.length; i ++ ) { - - array.push( parameters[ parameterNames[ i ] ] ); - - } - - return array.join(); - - }; - - this.acquireProgram = function ( material, parameters, code ) { - - var program; - - // Check if code has been already compiled - for ( var p = 0, pl = programs.length; p < pl; p ++ ) { - - var programInfo = programs[ p ]; - - if ( programInfo.code === code ) { - - program = programInfo; - ++ program.usedTimes; - - break; - - } - - } - - if ( program === undefined ) { - - program = new WebGLProgram( renderer, code, material, parameters ); - programs.push( program ); - - } - - return program; - - }; - - this.releaseProgram = function( program ) { - - if ( -- program.usedTimes === 0 ) { - - // Remove from unordered set - var i = programs.indexOf( program ); - programs[ i ] = programs[ programs.length - 1 ]; - programs.pop(); - - // Free WebGL resources - program.destroy(); - - } - - }; - - // Exposed for resource monitoring & error feedback via renderer.info: - this.programs = programs; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLGeometries( gl, properties, info ) { - - var geometries = {}; - - function onGeometryDispose( event ) { - - var geometry = event.target; - var buffergeometry = geometries[ geometry.id ]; - - if ( buffergeometry.index !== null ) { - - deleteAttribute( buffergeometry.index ); - - } - - deleteAttributes( buffergeometry.attributes ); - - geometry.removeEventListener( 'dispose', onGeometryDispose ); - - delete geometries[ geometry.id ]; - - // TODO - - var property = properties.get( geometry ); - - if ( property.wireframe ) { - - deleteAttribute( property.wireframe ); - - } - - properties.delete( geometry ); - - var bufferproperty = properties.get( buffergeometry ); - - if ( bufferproperty.wireframe ) { - - deleteAttribute( bufferproperty.wireframe ); - - } - - properties.delete( buffergeometry ); - - // - - info.memory.geometries --; - - } - - function getAttributeBuffer( attribute ) { - - if ( attribute.isInterleavedBufferAttribute ) { - - return properties.get( attribute.data ).__webglBuffer; - - } - - return properties.get( attribute ).__webglBuffer; - - } - - function deleteAttribute( attribute ) { - - var buffer = getAttributeBuffer( attribute ); - - if ( buffer !== undefined ) { - - gl.deleteBuffer( buffer ); - removeAttributeBuffer( attribute ); - - } - - } - - function deleteAttributes( attributes ) { - - for ( var name in attributes ) { - - deleteAttribute( attributes[ name ] ); - - } - - } - - function removeAttributeBuffer( attribute ) { - - if ( attribute.isInterleavedBufferAttribute ) { - - properties.delete( attribute.data ); - - } else { - - properties.delete( attribute ); - - } - - } - - return { - - get: function ( object ) { - - var geometry = object.geometry; - - if ( geometries[ geometry.id ] !== undefined ) { - - return geometries[ geometry.id ]; - - } - - geometry.addEventListener( 'dispose', onGeometryDispose ); - - var buffergeometry; - - if ( geometry.isBufferGeometry ) { - - buffergeometry = geometry; - - } else if ( geometry.isGeometry ) { - - if ( geometry._bufferGeometry === undefined ) { - - geometry._bufferGeometry = new BufferGeometry().setFromObject( object ); - - } - - buffergeometry = geometry._bufferGeometry; - - } - - geometries[ geometry.id ] = buffergeometry; - - info.memory.geometries ++; - - return buffergeometry; - - } - - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLObjects( gl, properties, info ) { - - var geometries = new WebGLGeometries( gl, properties, info ); - - // - - function update( object ) { - - // TODO: Avoid updating twice (when using shadowMap). Maybe add frame counter. - - var geometry = geometries.get( object ); - - if ( object.geometry.isGeometry ) { - - geometry.updateFromObject( object ); - - } - - var index = geometry.index; - var attributes = geometry.attributes; - - if ( index !== null ) { - - updateAttribute( index, gl.ELEMENT_ARRAY_BUFFER ); - - } - - for ( var name in attributes ) { - - updateAttribute( attributes[ name ], gl.ARRAY_BUFFER ); - - } - - // morph targets - - var morphAttributes = geometry.morphAttributes; - - for ( var name in morphAttributes ) { - - var array = morphAttributes[ name ]; - - for ( var i = 0, l = array.length; i < l; i ++ ) { - - updateAttribute( array[ i ], gl.ARRAY_BUFFER ); - - } - - } - - return geometry; - - } - - function updateAttribute( attribute, bufferType ) { - - var data = ( attribute.isInterleavedBufferAttribute ) ? attribute.data : attribute; - - var attributeProperties = properties.get( data ); - - if ( attributeProperties.__webglBuffer === undefined ) { - - createBuffer( attributeProperties, data, bufferType ); - - } else if ( attributeProperties.version !== data.version ) { - - updateBuffer( attributeProperties, data, bufferType ); - - } - - } - - function createBuffer( attributeProperties, data, bufferType ) { - - attributeProperties.__webglBuffer = gl.createBuffer(); - gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); - - var usage = data.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW; - - gl.bufferData( bufferType, data.array, usage ); - - var type = gl.FLOAT; - var array = data.array; - - if ( array instanceof Float32Array ) { - - type = gl.FLOAT; - - } else if ( array instanceof Float64Array ) { - - console.warn( "Unsupported data buffer format: Float64Array" ); - - } else if ( array instanceof Uint16Array ) { - - type = gl.UNSIGNED_SHORT; - - } else if ( array instanceof Int16Array ) { - - type = gl.SHORT; - - } else if ( array instanceof Uint32Array ) { - - type = gl.UNSIGNED_INT; - - } else if ( array instanceof Int32Array ) { - - type = gl.INT; - - } else if ( array instanceof Int8Array ) { - - type = gl.BYTE; - - } else if ( array instanceof Uint8Array ) { - - type = gl.UNSIGNED_BYTE; - - } - - attributeProperties.bytesPerElement = array.BYTES_PER_ELEMENT; - attributeProperties.type = type; - attributeProperties.version = data.version; - - data.onUploadCallback(); - - } - - function updateBuffer( attributeProperties, data, bufferType ) { - - gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); - - if ( data.dynamic === false ) { - - gl.bufferData( bufferType, data.array, gl.STATIC_DRAW ); - - } else if ( data.updateRange.count === - 1 ) { - - // Not using update ranges - - gl.bufferSubData( bufferType, 0, data.array ); - - } else if ( data.updateRange.count === 0 ) { - - console.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' ); - - } else { - - gl.bufferSubData( bufferType, data.updateRange.offset * data.array.BYTES_PER_ELEMENT, - data.array.subarray( data.updateRange.offset, data.updateRange.offset + data.updateRange.count ) ); - - data.updateRange.count = 0; // reset range - - } - - attributeProperties.version = data.version; - - } - - function getAttributeBuffer( attribute ) { - - if ( attribute.isInterleavedBufferAttribute ) { - - return properties.get( attribute.data ).__webglBuffer; - - } - - return properties.get( attribute ).__webglBuffer; - - } - - function getAttributeProperties( attribute ) { - - if ( attribute.isInterleavedBufferAttribute ) { - - return properties.get( attribute.data ); - - } - - return properties.get( attribute ); - - } - - function getWireframeAttribute( geometry ) { - - var property = properties.get( geometry ); - - if ( property.wireframe !== undefined ) { - - return property.wireframe; - - } - - var indices = []; - - var index = geometry.index; - var attributes = geometry.attributes; - var position = attributes.position; - - // console.time( 'wireframe' ); - - if ( index !== null ) { - - var edges = {}; - var array = index.array; - - for ( var i = 0, l = array.length; i < l; i += 3 ) { - - var a = array[ i + 0 ]; - var b = array[ i + 1 ]; - var c = array[ i + 2 ]; - - indices.push( a, b, b, c, c, a ); - - } - - } else { - - var array = attributes.position.array; - - for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { - - var a = i + 0; - var b = i + 1; - var c = i + 2; - - indices.push( a, b, b, c, c, a ); - - } - - } - - // console.timeEnd( 'wireframe' ); - - var TypeArray = position.count > 65535 ? Uint32Array : Uint16Array; - var attribute = new BufferAttribute( new TypeArray( indices ), 1 ); - - updateAttribute( attribute, gl.ELEMENT_ARRAY_BUFFER ); - - property.wireframe = attribute; - - return attribute; - - } - - return { - - getAttributeBuffer: getAttributeBuffer, - getAttributeProperties: getAttributeProperties, - getWireframeAttribute: getWireframeAttribute, - - update: update - - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLTextures( _gl, extensions, state, properties, capabilities, paramThreeToGL, info ) { - - var _infoMemory = info.memory; - var _isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && _gl instanceof WebGL2RenderingContext ); - - // - - function clampToMaxSize( image, maxSize ) { - - if ( image.width > maxSize || image.height > maxSize ) { - - // Warning: Scaling through the canvas will only work with images that use - // premultiplied alpha. - - var scale = maxSize / Math.max( image.width, image.height ); - - var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - canvas.width = Math.floor( image.width * scale ); - canvas.height = Math.floor( image.height * scale ); - - var context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); - - console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image ); - - return canvas; - - } - - return image; - - } - - function isPowerOfTwo( image ) { - - return _Math.isPowerOfTwo( image.width ) && _Math.isPowerOfTwo( image.height ); - - } - - function makePowerOfTwo( image ) { - - if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement ) { - - var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - canvas.width = _Math.nearestPowerOfTwo( image.width ); - canvas.height = _Math.nearestPowerOfTwo( image.height ); - - var context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0, canvas.width, canvas.height ); - - console.warn( 'THREE.WebGLRenderer: image is not power of two (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image ); - - return canvas; - - } - - return image; - - } - - function textureNeedsPowerOfTwo( texture ) { - - return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || - ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); - - } - - // Fallback filters for non-power-of-2 textures - - function filterFallback( f ) { - - if ( f === NearestFilter || f === NearestMipMapNearestFilter || f === NearestMipMapLinearFilter ) { - - return _gl.NEAREST; - - } - - return _gl.LINEAR; - - } - - // - - function onTextureDispose( event ) { - - var texture = event.target; - - texture.removeEventListener( 'dispose', onTextureDispose ); - - deallocateTexture( texture ); - - _infoMemory.textures --; - - - } - - function onRenderTargetDispose( event ) { - - var renderTarget = event.target; - - renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); - - deallocateRenderTarget( renderTarget ); - - _infoMemory.textures --; - - } - - // - - function deallocateTexture( texture ) { - - var textureProperties = properties.get( texture ); - - if ( texture.image && textureProperties.__image__webglTextureCube ) { - - // cube texture - - _gl.deleteTexture( textureProperties.__image__webglTextureCube ); - - } else { - - // 2D texture - - if ( textureProperties.__webglInit === undefined ) return; - - _gl.deleteTexture( textureProperties.__webglTexture ); - - } - - // remove all webgl properties - properties.delete( texture ); - - } - - function deallocateRenderTarget( renderTarget ) { - - var renderTargetProperties = properties.get( renderTarget ); - var textureProperties = properties.get( renderTarget.texture ); - - if ( ! renderTarget ) return; - - if ( textureProperties.__webglTexture !== undefined ) { - - _gl.deleteTexture( textureProperties.__webglTexture ); - - } - - if ( renderTarget.depthTexture ) { - - renderTarget.depthTexture.dispose(); - - } - - if ( renderTarget.isWebGLRenderTargetCube ) { - - for ( var i = 0; i < 6; i ++ ) { - - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); - if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); - - } - - } else { - - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); - if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); - - } - - properties.delete( renderTarget.texture ); - properties.delete( renderTarget ); - - } - - // - - - - function setTexture2D( texture, slot ) { - - var textureProperties = properties.get( texture ); - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - var image = texture.image; - - if ( image === undefined ) { - - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined', texture ); - - } else if ( image.complete === false ) { - - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete', texture ); - - } else { - - uploadTexture( textureProperties, texture, slot ); - return; - - } - - } - - state.activeTexture( _gl.TEXTURE0 + slot ); - state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); - - } - - function setTextureCube( texture, slot ) { - - var textureProperties = properties.get( texture ); - - if ( texture.image.length === 6 ) { - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - if ( ! textureProperties.__image__webglTextureCube ) { - - texture.addEventListener( 'dispose', onTextureDispose ); - - textureProperties.__image__webglTextureCube = _gl.createTexture(); - - _infoMemory.textures ++; - - } - - state.activeTexture( _gl.TEXTURE0 + slot ); - state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); - - _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); - - var isCompressed = ( texture && texture.isCompressedTexture ); - var isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); - - var cubeImage = []; - - for ( var i = 0; i < 6; i ++ ) { - - if ( ! isCompressed && ! isDataTexture ) { - - cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize ); - - } else { - - cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; - - } - - } - - var image = cubeImage[ 0 ], - isPowerOfTwoImage = isPowerOfTwo( image ), - glFormat = paramThreeToGL( texture.format ), - glType = paramThreeToGL( texture.type ); - - setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isPowerOfTwoImage ); - - for ( var i = 0; i < 6; i ++ ) { - - if ( ! isCompressed ) { - - if ( isDataTexture ) { - - state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); - - } else { - - state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); - - } - - } else { - - var mipmap, mipmaps = cubeImage[ i ].mipmaps; - - for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { - - mipmap = mipmaps[ j ]; - - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { - - if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { - - state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - - } else { - - console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()" ); - - } - - } else { - - state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - - } - - } - - } - - } - - if ( texture.generateMipmaps && isPowerOfTwoImage ) { - - _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); - - } - - textureProperties.__version = texture.version; - - if ( texture.onUpdate ) texture.onUpdate( texture ); - - } else { - - state.activeTexture( _gl.TEXTURE0 + slot ); - state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); - - } - - } - - } - - function setTextureCubeDynamic( texture, slot ) { - - state.activeTexture( _gl.TEXTURE0 + slot ); - state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture ); - - } - - function setTextureParameters( textureType, texture, isPowerOfTwoImage ) { - - var extension; - - if ( isPowerOfTwoImage ) { - - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) ); - - _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) ); - _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) ); - - } else { - - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); - - if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { - - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.', texture ); - - } - - _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); - _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); - - if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { - - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.', texture ); - - } - - } - - extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - - if ( extension ) { - - if ( texture.type === FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return; - if ( texture.type === HalfFloatType && extensions.get( 'OES_texture_half_float_linear' ) === null ) return; - - if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { - - _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); - properties.get( texture ).__currentAnisotropy = texture.anisotropy; - - } - - } - - } - - function uploadTexture( textureProperties, texture, slot ) { - - if ( textureProperties.__webglInit === undefined ) { - - textureProperties.__webglInit = true; - - texture.addEventListener( 'dispose', onTextureDispose ); - - textureProperties.__webglTexture = _gl.createTexture(); - - _infoMemory.textures ++; - - } - - state.activeTexture( _gl.TEXTURE0 + slot ); - state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); - - _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); - _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); - _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); - - var image = clampToMaxSize( texture.image, capabilities.maxTextureSize ); - - if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( image ) === false ) { - - image = makePowerOfTwo( image ); - - } - - var isPowerOfTwoImage = isPowerOfTwo( image ), - glFormat = paramThreeToGL( texture.format ), - glType = paramThreeToGL( texture.type ); - - setTextureParameters( _gl.TEXTURE_2D, texture, isPowerOfTwoImage ); - - var mipmap, mipmaps = texture.mipmaps; - - if ( texture.isDepthTexture ) { - - // populate depth texture with dummy data - - var internalFormat = _gl.DEPTH_COMPONENT; - - if ( texture.type === FloatType ) { - - if ( !_isWebGL2 ) throw new Error('Float Depth Texture only supported in WebGL2.0'); - internalFormat = _gl.DEPTH_COMPONENT32F; - - } else if ( _isWebGL2 ) { - - // WebGL 2.0 requires signed internalformat for glTexImage2D - internalFormat = _gl.DEPTH_COMPONENT16; - - } - - if ( texture.format === DepthFormat && internalFormat === _gl.DEPTH_COMPONENT ) { - - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { - - console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' ); - - texture.type = UnsignedShortType; - glType = paramThreeToGL( texture.type ); - - } - - } - - // Depth stencil textures need the DEPTH_STENCIL internal format - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.format === DepthStencilFormat ) { - - internalFormat = _gl.DEPTH_STENCIL; - - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedInt248Type ) { - - console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' ); - - texture.type = UnsignedInt248Type; - glType = paramThreeToGL( texture.type ); - - } - - } - - state.texImage2D( _gl.TEXTURE_2D, 0, internalFormat, image.width, image.height, 0, glFormat, glType, null ); - - } else if ( texture.isDataTexture ) { - - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels - - if ( mipmaps.length > 0 && isPowerOfTwoImage ) { - - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { - - mipmap = mipmaps[ i ]; - state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - - } - - texture.generateMipmaps = false; - - } else { - - state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); - - } - - } else if ( texture.isCompressedTexture ) { - - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { - - mipmap = mipmaps[ i ]; - - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { - - if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { - - state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - - } else { - - console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); - - } - - } else { - - state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - - } - - } - - } else { - - // regular Texture (image, video, canvas) - - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels - - if ( mipmaps.length > 0 && isPowerOfTwoImage ) { - - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { - - mipmap = mipmaps[ i ]; - state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); - - } - - texture.generateMipmaps = false; - - } else { - - state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, image ); - - } - - } - - if ( texture.generateMipmaps && isPowerOfTwoImage ) _gl.generateMipmap( _gl.TEXTURE_2D ); - - textureProperties.__version = texture.version; - - if ( texture.onUpdate ) texture.onUpdate( texture ); - - } - - // Render targets - - // Setup storage for target texture and bind it to correct framebuffer - function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) { - - var glFormat = paramThreeToGL( renderTarget.texture.format ); - var glType = paramThreeToGL( renderTarget.texture.type ); - state.texImage2D( textureTarget, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); - _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); - _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); - - } - - // Setup storage for internal depth/stencil buffers and bind to correct framebuffer - function setupRenderBufferStorage( renderbuffer, renderTarget ) { - - _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); - - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { - - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); - - } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); - - } else { - - // FIXME: We don't support !depth !stencil - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); - - } - - _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); - - } - - // Setup resources for a Depth Texture for a FBO (needs an extension) - function setupDepthTexture( framebuffer, renderTarget ) { - - var isCube = ( renderTarget && renderTarget.isWebGLRenderTargetCube ); - if ( isCube ) throw new Error('Depth Texture with cube render targets is not supported!'); - - _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - - if ( !( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { - - throw new Error('renderTarget.depthTexture must be an instance of THREE.DepthTexture'); - - } - - // upload an empty depth texture with framebuffer size - if ( !properties.get( renderTarget.depthTexture ).__webglTexture || - renderTarget.depthTexture.image.width !== renderTarget.width || - renderTarget.depthTexture.image.height !== renderTarget.height ) { - renderTarget.depthTexture.image.width = renderTarget.width; - renderTarget.depthTexture.image.height = renderTarget.height; - renderTarget.depthTexture.needsUpdate = true; - } - - setTexture2D( renderTarget.depthTexture, 0 ); - - var webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; - - if ( renderTarget.depthTexture.format === DepthFormat ) { - - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); - - } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { - - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); - - } else { - - throw new Error('Unknown depthTexture format') - - } - - } - - // Setup GL resources for a non-texture depth buffer - function setupDepthRenderbuffer( renderTarget ) { - - var renderTargetProperties = properties.get( renderTarget ); - - var isCube = ( renderTarget.isWebGLRenderTargetCube === true ); - - if ( renderTarget.depthTexture ) { - - if ( isCube ) throw new Error('target.depthTexture not supported in Cube render targets'); - - setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); - - } else { - - if ( isCube ) { - - renderTargetProperties.__webglDepthbuffer = []; - - for ( var i = 0; i < 6; i ++ ) { - - _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] ); - renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget ); - - } - - } else { - - _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); - renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget ); - - } - - } - - _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); - - } - - // Set up GL resources for the render target - function setupRenderTarget( renderTarget ) { - - var renderTargetProperties = properties.get( renderTarget ); - var textureProperties = properties.get( renderTarget.texture ); - - renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); - - textureProperties.__webglTexture = _gl.createTexture(); - - _infoMemory.textures ++; - - var isCube = ( renderTarget.isWebGLRenderTargetCube === true ); - var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ); - - // Setup framebuffer - - if ( isCube ) { - - renderTargetProperties.__webglFramebuffer = []; - - for ( var i = 0; i < 6; i ++ ) { - - renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); - - } - - } else { - - renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); - - } - - // Setup color buffer - - if ( isCube ) { - - state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture ); - setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo ); - - for ( var i = 0; i < 6; i ++ ) { - - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); - - } - - if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); - state.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); - - } else { - - state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); - setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo ); - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D ); - - if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); - state.bindTexture( _gl.TEXTURE_2D, null ); - - } - - // Setup depth and stencil buffers - - if ( renderTarget.depthBuffer ) { - - setupDepthRenderbuffer( renderTarget ); - - } - - } - - function updateRenderTargetMipmap( renderTarget ) { - - var texture = renderTarget.texture; - - if ( texture.generateMipmaps && isPowerOfTwo( renderTarget ) && - texture.minFilter !== NearestFilter && - texture.minFilter !== LinearFilter ) { - - var target = (renderTarget && renderTarget.isWebGLRenderTargetCube) ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; - var webglTexture = properties.get( texture ).__webglTexture; - - state.bindTexture( target, webglTexture ); - _gl.generateMipmap( target ); - state.bindTexture( target, null ); - - } - - } - - this.setTexture2D = setTexture2D; - this.setTextureCube = setTextureCube; - this.setTextureCubeDynamic = setTextureCubeDynamic; - this.setupRenderTarget = setupRenderTarget; - this.updateRenderTargetMipmap = updateRenderTargetMipmap; - - } - - /** - * @author fordacious / fordacious.github.io - */ - - function WebGLProperties() { - - var properties = {}; - - return { - - get: function ( object ) { - - var uuid = object.uuid; - var map = properties[ uuid ]; - - if ( map === undefined ) { - - map = {}; - properties[ uuid ] = map; - - } - - return map; - - }, - - delete: function ( object ) { - - delete properties[ object.uuid ]; - - }, - - clear: function () { - - properties = {}; - - } - - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLState( gl, extensions, paramThreeToGL ) { - - function ColorBuffer() { - - var locked = false; - - var color = new Vector4(); - var currentColorMask = null; - var currentColorClear = new Vector4(); - - return { - - setMask: function ( colorMask ) { - - if ( currentColorMask !== colorMask && ! locked ) { - - gl.colorMask( colorMask, colorMask, colorMask, colorMask ); - currentColorMask = colorMask; - - } - - }, - - setLocked: function ( lock ) { - - locked = lock; - - }, - - setClear: function ( r, g, b, a, premultipliedAlpha ) { - - if ( premultipliedAlpha === true ) { - - r *= a; g *= a; b *= a; - - } - - color.set( r, g, b, a ); - - if ( currentColorClear.equals( color ) === false ) { - - gl.clearColor( r, g, b, a ); - currentColorClear.copy( color ); - - } - - }, - - reset: function () { - - locked = false; - - currentColorMask = null; - currentColorClear.set( 0, 0, 0, 1 ); - - } - - }; - - } - - function DepthBuffer() { - - var locked = false; - - var currentDepthMask = null; - var currentDepthFunc = null; - var currentDepthClear = null; - - return { - - setTest: function ( depthTest ) { - - if ( depthTest ) { - - enable( gl.DEPTH_TEST ); - - } else { - - disable( gl.DEPTH_TEST ); - - } - - }, - - setMask: function ( depthMask ) { - - if ( currentDepthMask !== depthMask && ! locked ) { - - gl.depthMask( depthMask ); - currentDepthMask = depthMask; - - } - - }, - - setFunc: function ( depthFunc ) { - - if ( currentDepthFunc !== depthFunc ) { - - if ( depthFunc ) { - - switch ( depthFunc ) { - - case NeverDepth: - - gl.depthFunc( gl.NEVER ); - break; - - case AlwaysDepth: - - gl.depthFunc( gl.ALWAYS ); - break; - - case LessDepth: - - gl.depthFunc( gl.LESS ); - break; - - case LessEqualDepth: - - gl.depthFunc( gl.LEQUAL ); - break; - - case EqualDepth: - - gl.depthFunc( gl.EQUAL ); - break; - - case GreaterEqualDepth: - - gl.depthFunc( gl.GEQUAL ); - break; - - case GreaterDepth: - - gl.depthFunc( gl.GREATER ); - break; - - case NotEqualDepth: - - gl.depthFunc( gl.NOTEQUAL ); - break; - - default: - - gl.depthFunc( gl.LEQUAL ); - - } - - } else { - - gl.depthFunc( gl.LEQUAL ); - - } - - currentDepthFunc = depthFunc; - - } - - }, - - setLocked: function ( lock ) { - - locked = lock; - - }, - - setClear: function ( depth ) { - - if ( currentDepthClear !== depth ) { - - gl.clearDepth( depth ); - currentDepthClear = depth; - - } - - }, - - reset: function () { - - locked = false; - - currentDepthMask = null; - currentDepthFunc = null; - currentDepthClear = null; - - } - - }; - - } - - function StencilBuffer() { - - var locked = false; - - var currentStencilMask = null; - var currentStencilFunc = null; - var currentStencilRef = null; - var currentStencilFuncMask = null; - var currentStencilFail = null; - var currentStencilZFail = null; - var currentStencilZPass = null; - var currentStencilClear = null; - - return { - - setTest: function ( stencilTest ) { - - if ( stencilTest ) { - - enable( gl.STENCIL_TEST ); - - } else { - - disable( gl.STENCIL_TEST ); - - } - - }, - - setMask: function ( stencilMask ) { - - if ( currentStencilMask !== stencilMask && ! locked ) { - - gl.stencilMask( stencilMask ); - currentStencilMask = stencilMask; - - } - - }, - - setFunc: function ( stencilFunc, stencilRef, stencilMask ) { - - if ( currentStencilFunc !== stencilFunc || - currentStencilRef !== stencilRef || - currentStencilFuncMask !== stencilMask ) { - - gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); - - currentStencilFunc = stencilFunc; - currentStencilRef = stencilRef; - currentStencilFuncMask = stencilMask; - - } - - }, - - setOp: function ( stencilFail, stencilZFail, stencilZPass ) { - - if ( currentStencilFail !== stencilFail || - currentStencilZFail !== stencilZFail || - currentStencilZPass !== stencilZPass ) { - - gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); - - currentStencilFail = stencilFail; - currentStencilZFail = stencilZFail; - currentStencilZPass = stencilZPass; - - } - - }, - - setLocked: function ( lock ) { - - locked = lock; - - }, - - setClear: function ( stencil ) { - - if ( currentStencilClear !== stencil ) { - - gl.clearStencil( stencil ); - currentStencilClear = stencil; - - } - - }, - - reset: function () { - - locked = false; - - currentStencilMask = null; - currentStencilFunc = null; - currentStencilRef = null; - currentStencilFuncMask = null; - currentStencilFail = null; - currentStencilZFail = null; - currentStencilZPass = null; - currentStencilClear = null; - - } - - }; - - } - - // - - var colorBuffer = new ColorBuffer(); - var depthBuffer = new DepthBuffer(); - var stencilBuffer = new StencilBuffer(); - - var maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); - var newAttributes = new Uint8Array( maxVertexAttributes ); - var enabledAttributes = new Uint8Array( maxVertexAttributes ); - var attributeDivisors = new Uint8Array( maxVertexAttributes ); - - var capabilities = {}; - - var compressedTextureFormats = null; - - var currentBlending = null; - var currentBlendEquation = null; - var currentBlendSrc = null; - var currentBlendDst = null; - var currentBlendEquationAlpha = null; - var currentBlendSrcAlpha = null; - var currentBlendDstAlpha = null; - var currentPremultipledAlpha = false; - - var currentFlipSided = null; - var currentCullFace = null; - - var currentLineWidth = null; - - var currentPolygonOffsetFactor = null; - var currentPolygonOffsetUnits = null; - - var currentScissorTest = null; - - var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); - - var version = parseFloat( /^WebGL\ ([0-9])/.exec( gl.getParameter( gl.VERSION ) )[ 1 ] ); - var lineWidthAvailable = parseFloat( version ) >= 1.0; - - var currentTextureSlot = null; - var currentBoundTextures = {}; - - var currentScissor = new Vector4(); - var currentViewport = new Vector4(); - - function createTexture( type, target, count ) { - - var data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. - var texture = gl.createTexture(); - - gl.bindTexture( type, texture ); - gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); - gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); - - for ( var i = 0; i < count; i ++ ) { - - gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); - - } - - return texture; - - } - - var emptyTextures = {}; - emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 ); - emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 ); - - // - - function init() { - - colorBuffer.setClear( 0, 0, 0, 1 ); - depthBuffer.setClear( 1 ); - stencilBuffer.setClear( 0 ); - - enable( gl.DEPTH_TEST ); - setDepthFunc( LessEqualDepth ); - - setFlipSided( false ); - setCullFace( CullFaceBack ); - enable( gl.CULL_FACE ); - - enable( gl.BLEND ); - setBlending( NormalBlending ); - - } - - function initAttributes() { - - for ( var i = 0, l = newAttributes.length; i < l; i ++ ) { - - newAttributes[ i ] = 0; - - } - - } - - function enableAttribute( attribute ) { - - newAttributes[ attribute ] = 1; - - if ( enabledAttributes[ attribute ] === 0 ) { - - gl.enableVertexAttribArray( attribute ); - enabledAttributes[ attribute ] = 1; - - } - - if ( attributeDivisors[ attribute ] !== 0 ) { - - var extension = extensions.get( 'ANGLE_instanced_arrays' ); - - extension.vertexAttribDivisorANGLE( attribute, 0 ); - attributeDivisors[ attribute ] = 0; - - } - - } - - function enableAttributeAndDivisor( attribute, meshPerAttribute, extension ) { - - newAttributes[ attribute ] = 1; - - if ( enabledAttributes[ attribute ] === 0 ) { - - gl.enableVertexAttribArray( attribute ); - enabledAttributes[ attribute ] = 1; - - } - - if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { - - extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute ); - attributeDivisors[ attribute ] = meshPerAttribute; - - } - - } - - function disableUnusedAttributes() { - - for ( var i = 0, l = enabledAttributes.length; i !== l; ++ i ) { - - if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { - - gl.disableVertexAttribArray( i ); - enabledAttributes[ i ] = 0; - - } - - } - - } - - function enable( id ) { - - if ( capabilities[ id ] !== true ) { - - gl.enable( id ); - capabilities[ id ] = true; - - } - - } - - function disable( id ) { - - if ( capabilities[ id ] !== false ) { - - gl.disable( id ); - capabilities[ id ] = false; - - } - - } - - function getCompressedTextureFormats() { - - if ( compressedTextureFormats === null ) { - - compressedTextureFormats = []; - - if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || - extensions.get( 'WEBGL_compressed_texture_s3tc' ) || - extensions.get( 'WEBGL_compressed_texture_etc1' ) ) { - - var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS ); - - for ( var i = 0; i < formats.length; i ++ ) { - - compressedTextureFormats.push( formats[ i ] ); - - } - - } - - } - - return compressedTextureFormats; - - } - - function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { - - if ( blending !== NoBlending ) { - - enable( gl.BLEND ); - - } else { - - disable( gl.BLEND ); - - } - - if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { - - if ( blending === AdditiveBlending ) { - - if ( premultipliedAlpha ) { - - gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); - gl.blendFuncSeparate( gl.ONE, gl.ONE, gl.ONE, gl.ONE ); - - } else { - - gl.blendEquation( gl.FUNC_ADD ); - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - - } - - } else if ( blending === SubtractiveBlending ) { - - if ( premultipliedAlpha ) { - - gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); - gl.blendFuncSeparate( gl.ZERO, gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA ); - - } else { - - gl.blendEquation( gl.FUNC_ADD ); - gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR ); - - } - - } else if ( blending === MultiplyBlending ) { - - if ( premultipliedAlpha ) { - - gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); - gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA ); - - } else { - - gl.blendEquation( gl.FUNC_ADD ); - gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); - - } - - } else { - - if ( premultipliedAlpha ) { - - gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); - gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); - - } else { - - gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); - gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); - - } - - } - - currentBlending = blending; - currentPremultipledAlpha = premultipliedAlpha; - - } - - if ( blending === CustomBlending ) { - - blendEquationAlpha = blendEquationAlpha || blendEquation; - blendSrcAlpha = blendSrcAlpha || blendSrc; - blendDstAlpha = blendDstAlpha || blendDst; - - if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { - - gl.blendEquationSeparate( paramThreeToGL( blendEquation ), paramThreeToGL( blendEquationAlpha ) ); - - currentBlendEquation = blendEquation; - currentBlendEquationAlpha = blendEquationAlpha; - - } - - if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { - - gl.blendFuncSeparate( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ), paramThreeToGL( blendSrcAlpha ), paramThreeToGL( blendDstAlpha ) ); - - currentBlendSrc = blendSrc; - currentBlendDst = blendDst; - currentBlendSrcAlpha = blendSrcAlpha; - currentBlendDstAlpha = blendDstAlpha; - - } - - } else { - - currentBlendEquation = null; - currentBlendSrc = null; - currentBlendDst = null; - currentBlendEquationAlpha = null; - currentBlendSrcAlpha = null; - currentBlendDstAlpha = null; - - } - - } - - // TODO Deprecate - - function setColorWrite( colorWrite ) { - - colorBuffer.setMask( colorWrite ); - - } - - function setDepthTest( depthTest ) { - - depthBuffer.setTest( depthTest ); - - } - - function setDepthWrite( depthWrite ) { - - depthBuffer.setMask( depthWrite ); - - } - - function setDepthFunc( depthFunc ) { - - depthBuffer.setFunc( depthFunc ); - - } - - function setStencilTest( stencilTest ) { - - stencilBuffer.setTest( stencilTest ); - - } - - function setStencilWrite( stencilWrite ) { - - stencilBuffer.setMask( stencilWrite ); - - } - - function setStencilFunc( stencilFunc, stencilRef, stencilMask ) { - - stencilBuffer.setFunc( stencilFunc, stencilRef, stencilMask ); - - } - - function setStencilOp( stencilFail, stencilZFail, stencilZPass ) { - - stencilBuffer.setOp( stencilFail, stencilZFail, stencilZPass ); - - } - - // - - function setFlipSided( flipSided ) { - - if ( currentFlipSided !== flipSided ) { - - if ( flipSided ) { - - gl.frontFace( gl.CW ); - - } else { - - gl.frontFace( gl.CCW ); - - } - - currentFlipSided = flipSided; - - } - - } - - function setCullFace( cullFace ) { - - if ( cullFace !== CullFaceNone ) { - - enable( gl.CULL_FACE ); - - if ( cullFace !== currentCullFace ) { - - if ( cullFace === CullFaceBack ) { - - gl.cullFace( gl.BACK ); - - } else if ( cullFace === CullFaceFront ) { - - gl.cullFace( gl.FRONT ); - - } else { - - gl.cullFace( gl.FRONT_AND_BACK ); - - } - - } - - } else { - - disable( gl.CULL_FACE ); - - } - - currentCullFace = cullFace; - - } - - function setLineWidth( width ) { - - if ( width !== currentLineWidth ) { - - if ( lineWidthAvailable ) gl.lineWidth( width ); - - currentLineWidth = width; - - } - - } - - function setPolygonOffset( polygonOffset, factor, units ) { - - if ( polygonOffset ) { - - enable( gl.POLYGON_OFFSET_FILL ); - - if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { - - gl.polygonOffset( factor, units ); - - currentPolygonOffsetFactor = factor; - currentPolygonOffsetUnits = units; - - } - - } else { - - disable( gl.POLYGON_OFFSET_FILL ); - - } - - } - - function getScissorTest() { - - return currentScissorTest; - - } - - function setScissorTest( scissorTest ) { - - currentScissorTest = scissorTest; - - if ( scissorTest ) { - - enable( gl.SCISSOR_TEST ); - - } else { - - disable( gl.SCISSOR_TEST ); - - } - - } - - // texture - - function activeTexture( webglSlot ) { - - if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; - - if ( currentTextureSlot !== webglSlot ) { - - gl.activeTexture( webglSlot ); - currentTextureSlot = webglSlot; - - } - - } - - function bindTexture( webglType, webglTexture ) { - - if ( currentTextureSlot === null ) { - - activeTexture(); - - } - - var boundTexture = currentBoundTextures[ currentTextureSlot ]; - - if ( boundTexture === undefined ) { - - boundTexture = { type: undefined, texture: undefined }; - currentBoundTextures[ currentTextureSlot ] = boundTexture; - - } - - if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { - - gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); - - boundTexture.type = webglType; - boundTexture.texture = webglTexture; - - } - - } - - function compressedTexImage2D() { - - try { - - gl.compressedTexImage2D.apply( gl, arguments ); - - } catch ( error ) { - - console.error( error ); - - } - - } - - function texImage2D() { - - try { - - gl.texImage2D.apply( gl, arguments ); - - } catch ( error ) { - - console.error( error ); - - } - - } - - // - - function scissor( scissor ) { - - if ( currentScissor.equals( scissor ) === false ) { - - gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); - currentScissor.copy( scissor ); - - } - - } - - function viewport( viewport ) { - - if ( currentViewport.equals( viewport ) === false ) { - - gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); - currentViewport.copy( viewport ); - - } - - } - - // - - function reset() { - - for ( var i = 0; i < enabledAttributes.length; i ++ ) { - - if ( enabledAttributes[ i ] === 1 ) { - - gl.disableVertexAttribArray( i ); - enabledAttributes[ i ] = 0; - - } - - } - - capabilities = {}; - - compressedTextureFormats = null; - - currentTextureSlot = null; - currentBoundTextures = {}; - - currentBlending = null; - - currentFlipSided = null; - currentCullFace = null; - - colorBuffer.reset(); - depthBuffer.reset(); - stencilBuffer.reset(); - - } - - return { - - buffers: { - color: colorBuffer, - depth: depthBuffer, - stencil: stencilBuffer - }, - - init: init, - initAttributes: initAttributes, - enableAttribute: enableAttribute, - enableAttributeAndDivisor: enableAttributeAndDivisor, - disableUnusedAttributes: disableUnusedAttributes, - enable: enable, - disable: disable, - getCompressedTextureFormats: getCompressedTextureFormats, - - setBlending: setBlending, - - setColorWrite: setColorWrite, - setDepthTest: setDepthTest, - setDepthWrite: setDepthWrite, - setDepthFunc: setDepthFunc, - setStencilTest: setStencilTest, - setStencilWrite: setStencilWrite, - setStencilFunc: setStencilFunc, - setStencilOp: setStencilOp, - - setFlipSided: setFlipSided, - setCullFace: setCullFace, - - setLineWidth: setLineWidth, - setPolygonOffset: setPolygonOffset, - - getScissorTest: getScissorTest, - setScissorTest: setScissorTest, - - activeTexture: activeTexture, - bindTexture: bindTexture, - compressedTexImage2D: compressedTexImage2D, - texImage2D: texImage2D, - - scissor: scissor, - viewport: viewport, - - reset: reset - - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLCapabilities( gl, extensions, parameters ) { - - var maxAnisotropy; - - function getMaxAnisotropy() { - - if ( maxAnisotropy !== undefined ) return maxAnisotropy; - - var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - - if ( extension !== null ) { - - maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); - - } else { - - maxAnisotropy = 0; - - } - - return maxAnisotropy; - - } - - function getMaxPrecision( precision ) { - - if ( precision === 'highp' ) { - - if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && - gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { - - return 'highp'; - - } - - precision = 'mediump'; - - } - - if ( precision === 'mediump' ) { - - if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && - gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { - - return 'mediump'; - - } - - } - - return 'lowp'; - - } - - var precision = parameters.precision !== undefined ? parameters.precision : 'highp'; - var maxPrecision = getMaxPrecision( precision ); - - if ( maxPrecision !== precision ) { - - console.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' ); - precision = maxPrecision; - - } - - var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true && !! extensions.get( 'EXT_frag_depth' ); - - var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); - var maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); - var maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE ); - var maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ); - - var maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); - var maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS ); - var maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); - var maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); - - var vertexTextures = maxVertexTextures > 0; - var floatFragmentTextures = !! extensions.get( 'OES_texture_float' ); - var floatVertexTextures = vertexTextures && floatFragmentTextures; - - return { - - getMaxAnisotropy: getMaxAnisotropy, - getMaxPrecision: getMaxPrecision, - - precision: precision, - logarithmicDepthBuffer: logarithmicDepthBuffer, - - maxTextures: maxTextures, - maxVertexTextures: maxVertexTextures, - maxTextureSize: maxTextureSize, - maxCubemapSize: maxCubemapSize, - - maxAttributes: maxAttributes, - maxVertexUniforms: maxVertexUniforms, - maxVaryings: maxVaryings, - maxFragmentUniforms: maxFragmentUniforms, - - vertexTextures: vertexTextures, - floatFragmentTextures: floatFragmentTextures, - floatVertexTextures: floatVertexTextures - - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLExtensions( gl ) { - - var extensions = {}; - - return { - - get: function ( name ) { - - if ( extensions[ name ] !== undefined ) { - - return extensions[ name ]; - - } - - var extension; - - switch ( name ) { - - case 'WEBGL_depth_texture': - extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); - break; - - case 'EXT_texture_filter_anisotropic': - extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); - break; - - case 'WEBGL_compressed_texture_s3tc': - extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); - break; - - case 'WEBGL_compressed_texture_pvrtc': - extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); - break; - - case 'WEBGL_compressed_texture_etc1': - extension = gl.getExtension( 'WEBGL_compressed_texture_etc1' ); - break; - - default: - extension = gl.getExtension( name ); - - } - - if ( extension === null ) { - - console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); - - } - - extensions[ name ] = extension; - - return extension; - - } - - }; - - } - - /** - * @author tschw - */ - - function WebGLClipping() { - - var scope = this, - - globalState = null, - numGlobalPlanes = 0, - localClippingEnabled = false, - renderingShadows = false, - - plane = new Plane(), - viewNormalMatrix = new Matrix3(), - - uniform = { value: null, needsUpdate: false }; - - this.uniform = uniform; - this.numPlanes = 0; - this.numIntersection = 0; - - this.init = function( planes, enableLocalClipping, camera ) { - - var enabled = - planes.length !== 0 || - enableLocalClipping || - // enable state of previous frame - the clipping code has to - // run another frame in order to reset the state: - numGlobalPlanes !== 0 || - localClippingEnabled; - - localClippingEnabled = enableLocalClipping; - - globalState = projectPlanes( planes, camera, 0 ); - numGlobalPlanes = planes.length; - - return enabled; - - }; - - this.beginShadows = function() { - - renderingShadows = true; - projectPlanes( null ); - - }; - - this.endShadows = function() { - - renderingShadows = false; - resetGlobalState(); - - }; - - this.setState = function( planes, clipIntersection, clipShadows, camera, cache, fromCache ) { - - if ( ! localClippingEnabled || - planes === null || planes.length === 0 || - renderingShadows && ! clipShadows ) { - // there's no local clipping - - if ( renderingShadows ) { - // there's no global clipping - - projectPlanes( null ); - - } else { - - resetGlobalState(); - } - - } else { - - var nGlobal = renderingShadows ? 0 : numGlobalPlanes, - lGlobal = nGlobal * 4, - - dstArray = cache.clippingState || null; - - uniform.value = dstArray; // ensure unique state - - dstArray = projectPlanes( planes, camera, lGlobal, fromCache ); - - for ( var i = 0; i !== lGlobal; ++ i ) { - - dstArray[ i ] = globalState[ i ]; - - } - - cache.clippingState = dstArray; - this.numIntersection = clipIntersection ? this.numPlanes : 0; - this.numPlanes += nGlobal; - - } - - - }; - - function resetGlobalState() { - - if ( uniform.value !== globalState ) { - - uniform.value = globalState; - uniform.needsUpdate = numGlobalPlanes > 0; - - } - - scope.numPlanes = numGlobalPlanes; - scope.numIntersection = 0; - - } - - function projectPlanes( planes, camera, dstOffset, skipTransform ) { - - var nPlanes = planes !== null ? planes.length : 0, - dstArray = null; - - if ( nPlanes !== 0 ) { - - dstArray = uniform.value; - - if ( skipTransform !== true || dstArray === null ) { - - var flatSize = dstOffset + nPlanes * 4, - viewMatrix = camera.matrixWorldInverse; - - viewNormalMatrix.getNormalMatrix( viewMatrix ); - - if ( dstArray === null || dstArray.length < flatSize ) { - - dstArray = new Float32Array( flatSize ); - - } - - for ( var i = 0, i4 = dstOffset; - i !== nPlanes; ++ i, i4 += 4 ) { - - plane.copy( planes[ i ] ). - applyMatrix4( viewMatrix, viewNormalMatrix ); - - plane.normal.toArray( dstArray, i4 ); - dstArray[ i4 + 3 ] = plane.constant; - - } - - } - - uniform.value = dstArray; - uniform.needsUpdate = true; - - } - - scope.numPlanes = nPlanes; - - return dstArray; - - } - - } - - /** - * @author supereggbert / http://www.paulbrunt.co.uk/ - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author szimek / https://github.com/szimek/ - * @author tschw - */ - - function WebGLRenderer( parameters ) { - - console.log( 'THREE.WebGLRenderer', REVISION ); - - parameters = parameters || {}; - - var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ), - _context = parameters.context !== undefined ? parameters.context : null, - - _alpha = parameters.alpha !== undefined ? parameters.alpha : false, - _depth = parameters.depth !== undefined ? parameters.depth : true, - _stencil = parameters.stencil !== undefined ? parameters.stencil : true, - _antialias = parameters.antialias !== undefined ? parameters.antialias : false, - _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, - _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false; - - var lights = []; - - var opaqueObjects = []; - var opaqueObjectsLastIndex = - 1; - var transparentObjects = []; - var transparentObjectsLastIndex = - 1; - - var morphInfluences = new Float32Array( 8 ); - - var sprites = []; - var lensFlares = []; - - // public properties - - this.domElement = _canvas; - this.context = null; - - // clearing - - this.autoClear = true; - this.autoClearColor = true; - this.autoClearDepth = true; - this.autoClearStencil = true; - - // scene graph - - this.sortObjects = true; - - // user-defined clipping - - this.clippingPlanes = []; - this.localClippingEnabled = false; - - // physically based shading - - this.gammaFactor = 2.0; // for backwards compatibility - this.gammaInput = false; - this.gammaOutput = false; - - // physical lights - - this.physicallyCorrectLights = false; - - // tone mapping - - this.toneMapping = LinearToneMapping; - this.toneMappingExposure = 1.0; - this.toneMappingWhitePoint = 1.0; - - // morphs - - this.maxMorphTargets = 8; - this.maxMorphNormals = 4; - - // internal properties - - var _this = this, - - // internal state cache - - _currentProgram = null, - _currentRenderTarget = null, - _currentFramebuffer = null, - _currentMaterialId = - 1, - _currentGeometryProgram = '', - _currentCamera = null, - - _currentScissor = new Vector4(), - _currentScissorTest = null, - - _currentViewport = new Vector4(), - - // - - _usedTextureUnits = 0, - - // - - _clearColor = new Color( 0x000000 ), - _clearAlpha = 0, - - _width = _canvas.width, - _height = _canvas.height, - - _pixelRatio = 1, - - _scissor = new Vector4( 0, 0, _width, _height ), - _scissorTest = false, - - _viewport = new Vector4( 0, 0, _width, _height ), - - // frustum - - _frustum = new Frustum(), - - // clipping - - _clipping = new WebGLClipping(), - _clippingEnabled = false, - _localClippingEnabled = false, - - _sphere = new Sphere(), - - // camera matrices cache - - _projScreenMatrix = new Matrix4(), - - _vector3 = new Vector3(), - _matrix4 = new Matrix4(), - _matrix42 = new Matrix4(), - - // light arrays cache - - _lights = { - - hash: '', - - ambient: [ 0, 0, 0 ], - directional: [], - directionalShadowMap: [], - directionalShadowMatrix: [], - spot: [], - spotShadowMap: [], - spotShadowMatrix: [], - rectArea: [], - point: [], - pointShadowMap: [], - pointShadowMatrix: [], - hemi: [], - - shadows: [] - - }, - - // info - - _infoRender = { - - calls: 0, - vertices: 0, - faces: 0, - points: 0 - - }; - - this.info = { - - render: _infoRender, - memory: { - - geometries: 0, - textures: 0 - - }, - programs: null - - }; - - - // initialize - - var _gl; - - try { - - var attributes = { - alpha: _alpha, - depth: _depth, - stencil: _stencil, - antialias: _antialias, - premultipliedAlpha: _premultipliedAlpha, - preserveDrawingBuffer: _preserveDrawingBuffer - }; - - _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); - - if ( _gl === null ) { - - if ( _canvas.getContext( 'webgl' ) !== null ) { - - throw 'Error creating WebGL context with your selected attributes.'; - - } else { - - throw 'Error creating WebGL context.'; - - } - - } - - // Some experimental-webgl implementations do not have getShaderPrecisionFormat - - if ( _gl.getShaderPrecisionFormat === undefined ) { - - _gl.getShaderPrecisionFormat = function () { - - return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; - - }; - - } - - _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); - - } catch ( error ) { - - console.error( 'THREE.WebGLRenderer: ' + error ); - - } - - var extensions = new WebGLExtensions( _gl ); - - extensions.get( 'WEBGL_depth_texture' ); - extensions.get( 'OES_texture_float' ); - extensions.get( 'OES_texture_float_linear' ); - extensions.get( 'OES_texture_half_float' ); - extensions.get( 'OES_texture_half_float_linear' ); - extensions.get( 'OES_standard_derivatives' ); - extensions.get( 'ANGLE_instanced_arrays' ); - - if ( extensions.get( 'OES_element_index_uint' ) ) { - - BufferGeometry.MaxIndex = 4294967296; - - } - - var capabilities = new WebGLCapabilities( _gl, extensions, parameters ); - - var state = new WebGLState( _gl, extensions, paramThreeToGL ); - var properties = new WebGLProperties(); - var textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, paramThreeToGL, this.info ); - var objects = new WebGLObjects( _gl, properties, this.info ); - var programCache = new WebGLPrograms( this, capabilities ); - var lightCache = new WebGLLights(); - - this.info.programs = programCache.programs; - - var bufferRenderer = new WebGLBufferRenderer( _gl, extensions, _infoRender ); - var indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, _infoRender ); - - // - - var backgroundCamera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); - var backgroundCamera2 = new PerspectiveCamera(); - var backgroundPlaneMesh = new Mesh( - new PlaneBufferGeometry( 2, 2 ), - new MeshBasicMaterial( { depthTest: false, depthWrite: false, fog: false } ) - ); - var backgroundBoxShader = ShaderLib[ 'cube' ]; - var backgroundBoxMesh = new Mesh( - new BoxBufferGeometry( 5, 5, 5 ), - new ShaderMaterial( { - uniforms: backgroundBoxShader.uniforms, - vertexShader: backgroundBoxShader.vertexShader, - fragmentShader: backgroundBoxShader.fragmentShader, - side: BackSide, - depthTest: false, - depthWrite: false, - fog: false - } ) - ); - - // - - function getTargetPixelRatio() { - - return _currentRenderTarget === null ? _pixelRatio : 1; - - } - - function setDefaultGLState() { - - state.init(); - - state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); - state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); - - state.buffers.color.setClear( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha, _premultipliedAlpha ); - - } - - function resetGLState() { - - _currentProgram = null; - _currentCamera = null; - - _currentGeometryProgram = ''; - _currentMaterialId = - 1; - - state.reset(); - - } - - setDefaultGLState(); - - this.context = _gl; - this.capabilities = capabilities; - this.extensions = extensions; - this.properties = properties; - this.state = state; - - // shadow map - - var shadowMap = new WebGLShadowMap( this, _lights, objects, capabilities ); - - this.shadowMap = shadowMap; - - - // Plugins - - var spritePlugin = new SpritePlugin( this, sprites ); - var lensFlarePlugin = new LensFlarePlugin( this, lensFlares ); - - // API - - this.getContext = function () { - - return _gl; - - }; - - this.getContextAttributes = function () { - - return _gl.getContextAttributes(); - - }; - - this.forceContextLoss = function () { - - extensions.get( 'WEBGL_lose_context' ).loseContext(); - - }; - - this.getMaxAnisotropy = function () { - - return capabilities.getMaxAnisotropy(); - - }; - - this.getPrecision = function () { - - return capabilities.precision; - - }; - - this.getPixelRatio = function () { - - return _pixelRatio; - - }; - - this.setPixelRatio = function ( value ) { - - if ( value === undefined ) return; - - _pixelRatio = value; - - this.setSize( _viewport.z, _viewport.w, false ); - - }; - - this.getSize = function () { - - return { - width: _width, - height: _height - }; - - }; - - this.setSize = function ( width, height, updateStyle ) { - - _width = width; - _height = height; - - _canvas.width = width * _pixelRatio; - _canvas.height = height * _pixelRatio; - - if ( updateStyle !== false ) { - - _canvas.style.width = width + 'px'; - _canvas.style.height = height + 'px'; - - } - - this.setViewport( 0, 0, width, height ); - - }; - - this.setViewport = function ( x, y, width, height ) { - - state.viewport( _viewport.set( x, y, width, height ) ); - - }; - - this.setScissor = function ( x, y, width, height ) { - - state.scissor( _scissor.set( x, y, width, height ) ); - - }; - - this.setScissorTest = function ( boolean ) { - - state.setScissorTest( _scissorTest = boolean ); - - }; - - // Clearing - - this.getClearColor = function () { - - return _clearColor; - - }; - - this.setClearColor = function ( color, alpha ) { - - _clearColor.set( color ); - - _clearAlpha = alpha !== undefined ? alpha : 1; - - state.buffers.color.setClear( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha, _premultipliedAlpha ); - - }; - - this.getClearAlpha = function () { - - return _clearAlpha; - - }; - - this.setClearAlpha = function ( alpha ) { - - _clearAlpha = alpha; - - state.buffers.color.setClear( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha, _premultipliedAlpha ); - - }; - - this.clear = function ( color, depth, stencil ) { - - var bits = 0; - - if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; - if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; - if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; - - _gl.clear( bits ); - - }; - - this.clearColor = function () { - - this.clear( true, false, false ); - - }; - - this.clearDepth = function () { - - this.clear( false, true, false ); - - }; - - this.clearStencil = function () { - - this.clear( false, false, true ); - - }; - - this.clearTarget = function ( renderTarget, color, depth, stencil ) { - - this.setRenderTarget( renderTarget ); - this.clear( color, depth, stencil ); - - }; - - // Reset - - this.resetGLState = resetGLState; - - this.dispose = function() { - - transparentObjects = []; - transparentObjectsLastIndex = -1; - opaqueObjects = []; - opaqueObjectsLastIndex = -1; - - _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); - - }; - - // Events - - function onContextLost( event ) { - - event.preventDefault(); - - resetGLState(); - setDefaultGLState(); - - properties.clear(); - - } - - function onMaterialDispose( event ) { - - var material = event.target; - - material.removeEventListener( 'dispose', onMaterialDispose ); - - deallocateMaterial( material ); - - } - - // Buffer deallocation - - function deallocateMaterial( material ) { - - releaseMaterialProgramReference( material ); - - properties.delete( material ); - - } - - - function releaseMaterialProgramReference( material ) { - - var programInfo = properties.get( material ).program; - - material.program = undefined; - - if ( programInfo !== undefined ) { - - programCache.releaseProgram( programInfo ); - - } - - } - - // Buffer rendering - - this.renderBufferImmediate = function ( object, program, material ) { - - state.initAttributes(); - - var buffers = properties.get( object ); - - if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); - if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); - if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); - if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); - - var attributes = program.getAttributes(); - - if ( object.hasPositions ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position ); - _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); - - state.enableAttribute( attributes.position ); - _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); - - } - - if ( object.hasNormals ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal ); - - if ( ! material.isMeshPhongMaterial && - ! material.isMeshStandardMaterial && - ! material.isMeshNormalMaterial && - material.shading === FlatShading ) { - - for ( var i = 0, l = object.count * 3; i < l; i += 9 ) { - - var array = object.normalArray; - - var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3; - var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3; - var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3; - - array[ i + 0 ] = nx; - array[ i + 1 ] = ny; - array[ i + 2 ] = nz; - - array[ i + 3 ] = nx; - array[ i + 4 ] = ny; - array[ i + 5 ] = nz; - - array[ i + 6 ] = nx; - array[ i + 7 ] = ny; - array[ i + 8 ] = nz; - - } - - } - - _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); - - state.enableAttribute( attributes.normal ); - - _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); - - } - - if ( object.hasUvs && material.map ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv ); - _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); - - state.enableAttribute( attributes.uv ); - - _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); - - } - - if ( object.hasColors && material.vertexColors !== NoColors ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color ); - _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); - - state.enableAttribute( attributes.color ); - - _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); - - } - - state.disableUnusedAttributes(); - - _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); - - object.count = 0; - - }; - - this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) { - - setMaterial( material ); - - var program = setProgram( camera, fog, material, object ); - - var updateBuffers = false; - var geometryProgram = geometry.id + '_' + program.id + '_' + material.wireframe; - - if ( geometryProgram !== _currentGeometryProgram ) { - - _currentGeometryProgram = geometryProgram; - updateBuffers = true; - - } - - // morph targets - - var morphTargetInfluences = object.morphTargetInfluences; - - if ( morphTargetInfluences !== undefined ) { - - var activeInfluences = []; - - for ( var i = 0, l = morphTargetInfluences.length; i < l; i ++ ) { - - var influence = morphTargetInfluences[ i ]; - activeInfluences.push( [ influence, i ] ); - - } - - activeInfluences.sort( absNumericalSort ); - - if ( activeInfluences.length > 8 ) { - - activeInfluences.length = 8; - - } - - var morphAttributes = geometry.morphAttributes; - - for ( var i = 0, l = activeInfluences.length; i < l; i ++ ) { - - var influence = activeInfluences[ i ]; - morphInfluences[ i ] = influence[ 0 ]; - - if ( influence[ 0 ] !== 0 ) { - - var index = influence[ 1 ]; - - if ( material.morphTargets === true && morphAttributes.position ) geometry.addAttribute( 'morphTarget' + i, morphAttributes.position[ index ] ); - if ( material.morphNormals === true && morphAttributes.normal ) geometry.addAttribute( 'morphNormal' + i, morphAttributes.normal[ index ] ); - - } else { - - if ( material.morphTargets === true ) geometry.removeAttribute( 'morphTarget' + i ); - if ( material.morphNormals === true ) geometry.removeAttribute( 'morphNormal' + i ); - - } - - } - - for ( var i = activeInfluences.length, il = morphInfluences.length; i < il; i ++ ) { - - morphInfluences[ i ] = 0.0; - - } - - program.getUniforms().setValue( - _gl, 'morphTargetInfluences', morphInfluences ); - - updateBuffers = true; - - } - - // - - var index = geometry.index; - var position = geometry.attributes.position; - var rangeFactor = 1; - - if ( material.wireframe === true ) { - - index = objects.getWireframeAttribute( geometry ); - rangeFactor = 2; - - } - - var renderer; - - if ( index !== null ) { - - renderer = indexedBufferRenderer; - renderer.setIndex( index ); - - } else { - - renderer = bufferRenderer; - - } - - if ( updateBuffers ) { - - setupVertexAttributes( material, program, geometry ); - - if ( index !== null ) { - - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, objects.getAttributeBuffer( index ) ); - - } - - } - - // - - var dataCount = 0; - - if ( index !== null ) { - - dataCount = index.count; - - } else if ( position !== undefined ) { - - dataCount = position.count; - - } - - var rangeStart = geometry.drawRange.start * rangeFactor; - var rangeCount = geometry.drawRange.count * rangeFactor; - - var groupStart = group !== null ? group.start * rangeFactor : 0; - var groupCount = group !== null ? group.count * rangeFactor : Infinity; - - var drawStart = Math.max( rangeStart, groupStart ); - var drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; - - var drawCount = Math.max( 0, drawEnd - drawStart + 1 ); - - if ( drawCount === 0 ) return; - - // - - if ( object.isMesh ) { - - if ( material.wireframe === true ) { - - state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); - renderer.setMode( _gl.LINES ); - - } else { - - switch ( object.drawMode ) { - - case TrianglesDrawMode: - renderer.setMode( _gl.TRIANGLES ); - break; - - case TriangleStripDrawMode: - renderer.setMode( _gl.TRIANGLE_STRIP ); - break; - - case TriangleFanDrawMode: - renderer.setMode( _gl.TRIANGLE_FAN ); - break; - - } - - } - - - } else if ( object.isLine ) { - - var lineWidth = material.linewidth; - - if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material - - state.setLineWidth( lineWidth * getTargetPixelRatio() ); - - if ( object.isLineSegments ) { - - renderer.setMode( _gl.LINES ); - - } else { - - renderer.setMode( _gl.LINE_STRIP ); - - } - - } else if ( object.isPoints ) { - - renderer.setMode( _gl.POINTS ); - - } - - if ( geometry && geometry.isInstancedBufferGeometry ) { - - if ( geometry.maxInstancedCount > 0 ) { - - renderer.renderInstances( geometry, drawStart, drawCount ); - - } - - } else { - - renderer.render( drawStart, drawCount ); - - } - - }; - - function setupVertexAttributes( material, program, geometry, startIndex ) { - - var extension; - - if ( geometry && geometry.isInstancedBufferGeometry ) { - - extension = extensions.get( 'ANGLE_instanced_arrays' ); - - if ( extension === null ) { - - console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; - - } - - } - - if ( startIndex === undefined ) startIndex = 0; - - state.initAttributes(); - - var geometryAttributes = geometry.attributes; - - var programAttributes = program.getAttributes(); - - var materialDefaultAttributeValues = material.defaultAttributeValues; - - for ( var name in programAttributes ) { - - var programAttribute = programAttributes[ name ]; - - if ( programAttribute >= 0 ) { - - var geometryAttribute = geometryAttributes[ name ]; - - if ( geometryAttribute !== undefined ) { - - var normalized = geometryAttribute.normalized; - var size = geometryAttribute.itemSize; - - var attributeProperties = objects.getAttributeProperties( geometryAttribute ); - - var buffer = attributeProperties.__webglBuffer; - var type = attributeProperties.type; - var bytesPerElement = attributeProperties.bytesPerElement; - - if ( geometryAttribute.isInterleavedBufferAttribute ) { - - var data = geometryAttribute.data; - var stride = data.stride; - var offset = geometryAttribute.offset; - - if ( data && data.isInstancedInterleavedBuffer ) { - - state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute, extension ); - - if ( geometry.maxInstancedCount === undefined ) { - - geometry.maxInstancedCount = data.meshPerAttribute * data.count; - - } - - } else { - - state.enableAttribute( programAttribute ); - - } - - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); - _gl.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, ( startIndex * stride + offset ) * bytesPerElement ); - - } else { - - if ( geometryAttribute.isInstancedBufferAttribute ) { - - state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute, extension ); - - if ( geometry.maxInstancedCount === undefined ) { - - geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; - - } - - } else { - - state.enableAttribute( programAttribute ); - - } - - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); - _gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, startIndex * size * bytesPerElement ); - - } - - } else if ( materialDefaultAttributeValues !== undefined ) { - - var value = materialDefaultAttributeValues[ name ]; - - if ( value !== undefined ) { - - switch ( value.length ) { - - case 2: - _gl.vertexAttrib2fv( programAttribute, value ); - break; - - case 3: - _gl.vertexAttrib3fv( programAttribute, value ); - break; - - case 4: - _gl.vertexAttrib4fv( programAttribute, value ); - break; - - default: - _gl.vertexAttrib1fv( programAttribute, value ); - - } - - } - - } - - } - - } - - state.disableUnusedAttributes(); - - } - - // Sorting - - function absNumericalSort( a, b ) { - - return Math.abs( b[ 0 ] ) - Math.abs( a[ 0 ] ); - - } - - function painterSortStable( a, b ) { - - if ( a.object.renderOrder !== b.object.renderOrder ) { - - return a.object.renderOrder - b.object.renderOrder; - - } else if ( a.material.program && b.material.program && a.material.program !== b.material.program ) { - - return a.material.program.id - b.material.program.id; - - } else if ( a.material.id !== b.material.id ) { - - return a.material.id - b.material.id; - - } else if ( a.z !== b.z ) { - - return a.z - b.z; - - } else { - - return a.id - b.id; - - } - - } - - function reversePainterSortStable( a, b ) { - - if ( a.object.renderOrder !== b.object.renderOrder ) { - - return a.object.renderOrder - b.object.renderOrder; - - } if ( a.z !== b.z ) { - - return b.z - a.z; - - } else { - - return a.id - b.id; - - } - - } - - // Rendering - - this.render = function ( scene, camera, renderTarget, forceClear ) { - - if ( camera !== undefined && camera.isCamera !== true ) { - - console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); - return; - - } - - // reset caching for this frame - - _currentGeometryProgram = ''; - _currentMaterialId = - 1; - _currentCamera = null; - - // update scene graph - - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - - // update camera matrices and frustum - - if ( camera.parent === null ) camera.updateMatrixWorld(); - - camera.matrixWorldInverse.getInverse( camera.matrixWorld ); - - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - _frustum.setFromMatrix( _projScreenMatrix ); - - lights.length = 0; - - opaqueObjectsLastIndex = - 1; - transparentObjectsLastIndex = - 1; - - sprites.length = 0; - lensFlares.length = 0; - - _localClippingEnabled = this.localClippingEnabled; - _clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); - - projectObject( scene, camera ); - - opaqueObjects.length = opaqueObjectsLastIndex + 1; - transparentObjects.length = transparentObjectsLastIndex + 1; - - if ( _this.sortObjects === true ) { - - opaqueObjects.sort( painterSortStable ); - transparentObjects.sort( reversePainterSortStable ); - - } - - // - - if ( _clippingEnabled ) _clipping.beginShadows(); - - setupShadows( lights ); - - shadowMap.render( scene, camera ); - - setupLights( lights, camera ); - - if ( _clippingEnabled ) _clipping.endShadows(); - - // - - _infoRender.calls = 0; - _infoRender.vertices = 0; - _infoRender.faces = 0; - _infoRender.points = 0; - - if ( renderTarget === undefined ) { - - renderTarget = null; - - } - - this.setRenderTarget( renderTarget ); - - // - - var background = scene.background; - - if ( background === null ) { - - state.buffers.color.setClear( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha, _premultipliedAlpha ); - - } else if ( background && background.isColor ) { - - state.buffers.color.setClear( background.r, background.g, background.b, 1, _premultipliedAlpha ); - forceClear = true; - - } - - if ( this.autoClear || forceClear ) { - - this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil ); - - } - - if ( background && background.isCubeTexture ) { - - backgroundCamera2.projectionMatrix.copy( camera.projectionMatrix ); - - backgroundCamera2.matrixWorld.extractRotation( camera.matrixWorld ); - backgroundCamera2.matrixWorldInverse.getInverse( backgroundCamera2.matrixWorld ); - - backgroundBoxMesh.material.uniforms[ "tCube" ].value = background; - backgroundBoxMesh.modelViewMatrix.multiplyMatrices( backgroundCamera2.matrixWorldInverse, backgroundBoxMesh.matrixWorld ); - - objects.update( backgroundBoxMesh ); - - _this.renderBufferDirect( backgroundCamera2, null, backgroundBoxMesh.geometry, backgroundBoxMesh.material, backgroundBoxMesh, null ); - - } else if ( background && background.isTexture ) { - - backgroundPlaneMesh.material.map = background; - - objects.update( backgroundPlaneMesh ); - - _this.renderBufferDirect( backgroundCamera, null, backgroundPlaneMesh.geometry, backgroundPlaneMesh.material, backgroundPlaneMesh, null ); - - } - - // - - if ( scene.overrideMaterial ) { - - var overrideMaterial = scene.overrideMaterial; - - renderObjects( opaqueObjects, scene, camera, overrideMaterial ); - renderObjects( transparentObjects, scene, camera, overrideMaterial ); - - } else { - - // opaque pass (front-to-back order) - - state.setBlending( NoBlending ); - renderObjects( opaqueObjects, scene, camera ); - - // transparent pass (back-to-front order) - - renderObjects( transparentObjects, scene, camera ); - - } - - // custom render plugins (post pass) - - spritePlugin.render( scene, camera ); - lensFlarePlugin.render( scene, camera, _currentViewport ); - - // Generate mipmap if we're using any kind of mipmap filtering - - if ( renderTarget ) { - - textures.updateRenderTargetMipmap( renderTarget ); - - } - - // Ensure depth buffer writing is enabled so it can be cleared on next render - - state.setDepthTest( true ); - state.setDepthWrite( true ); - state.setColorWrite( true ); - - // _gl.finish(); - - }; - - function pushRenderItem( object, geometry, material, z, group ) { - - var array, index; - - // allocate the next position in the appropriate array - - if ( material.transparent ) { - - array = transparentObjects; - index = ++ transparentObjectsLastIndex; - - } else { - - array = opaqueObjects; - index = ++ opaqueObjectsLastIndex; - - } - - // recycle existing render item or grow the array - - var renderItem = array[ index ]; - - if ( renderItem !== undefined ) { - - renderItem.id = object.id; - renderItem.object = object; - renderItem.geometry = geometry; - renderItem.material = material; - renderItem.z = _vector3.z; - renderItem.group = group; - - } else { - - renderItem = { - id: object.id, - object: object, - geometry: geometry, - material: material, - z: _vector3.z, - group: group - }; - - // assert( index === array.length ); - array.push( renderItem ); - - } - - } - - // TODO Duplicated code (Frustum) - - function isObjectViewable( object ) { - - var geometry = object.geometry; - - if ( geometry.boundingSphere === null ) - geometry.computeBoundingSphere(); - - _sphere.copy( geometry.boundingSphere ). - applyMatrix4( object.matrixWorld ); - - return isSphereViewable( _sphere ); - - } - - function isSpriteViewable( sprite ) { - - _sphere.center.set( 0, 0, 0 ); - _sphere.radius = 0.7071067811865476; - _sphere.applyMatrix4( sprite.matrixWorld ); - - return isSphereViewable( _sphere ); - - } - - function isSphereViewable( sphere ) { - - if ( ! _frustum.intersectsSphere( sphere ) ) return false; - - var numPlanes = _clipping.numPlanes; - - if ( numPlanes === 0 ) return true; - - var planes = _this.clippingPlanes, - - center = sphere.center, - negRad = - sphere.radius, - i = 0; - - do { - - // out when deeper than radius in the negative halfspace - if ( planes[ i ].distanceToPoint( center ) < negRad ) return false; - - } while ( ++ i !== numPlanes ); - - return true; - - } - - function projectObject( object, camera ) { - - if ( object.visible === false ) return; - - var visible = ( object.layers.mask & camera.layers.mask ) !== 0; - - if ( visible ) { - - if ( object.isLight ) { - - lights.push( object ); - - } else if ( object.isSprite ) { - - if ( object.frustumCulled === false || isSpriteViewable( object ) === true ) { - - sprites.push( object ); - - } - - } else if ( object.isLensFlare ) { - - lensFlares.push( object ); - - } else if ( object.isImmediateRenderObject ) { - - if ( _this.sortObjects === true ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ); - _vector3.applyProjection( _projScreenMatrix ); - - } - - pushRenderItem( object, null, object.material, _vector3.z, null ); - - } else if ( object.isMesh || object.isLine || object.isPoints ) { - - if ( object.isSkinnedMesh ) { - - object.skeleton.update(); - - } - - if ( object.frustumCulled === false || isObjectViewable( object ) === true ) { - - var material = object.material; - - if ( material.visible === true ) { - - if ( _this.sortObjects === true ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ); - _vector3.applyProjection( _projScreenMatrix ); - - } - - var geometry = objects.update( object ); - - if ( material.isMultiMaterial ) { - - var groups = geometry.groups; - var materials = material.materials; - - for ( var i = 0, l = groups.length; i < l; i ++ ) { - - var group = groups[ i ]; - var groupMaterial = materials[ group.materialIndex ]; - - if ( groupMaterial.visible === true ) { - - pushRenderItem( object, geometry, groupMaterial, _vector3.z, group ); - - } - - } - - } else { - - pushRenderItem( object, geometry, material, _vector3.z, null ); - - } - - } - - } - - } - - } - - var children = object.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - projectObject( children[ i ], camera ); - - } - - } - - function renderObjects( renderList, scene, camera, overrideMaterial ) { - - for ( var i = 0, l = renderList.length; i < l; i ++ ) { - - var renderItem = renderList[ i ]; - - var object = renderItem.object; - var geometry = renderItem.geometry; - var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial; - var group = renderItem.group; - - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); - - object.onBeforeRender( _this, scene, camera, geometry, material, group ); - - if ( object.isImmediateRenderObject ) { - - setMaterial( material ); - - var program = setProgram( camera, scene.fog, material, object ); - - _currentGeometryProgram = ''; - - object.render( function ( object ) { - - _this.renderBufferImmediate( object, program, material ); - - } ); - - } else { - - _this.renderBufferDirect( camera, scene.fog, geometry, material, object, group ); - - } - - object.onAfterRender( _this, scene, camera, geometry, material, group ); - - - } - - } - - function initMaterial( material, fog, object ) { - - var materialProperties = properties.get( material ); - - var parameters = programCache.getParameters( - material, _lights, fog, _clipping.numPlanes, _clipping.numIntersection, object ); - - var code = programCache.getProgramCode( material, parameters ); - - var program = materialProperties.program; - var programChange = true; - - if ( program === undefined ) { - - // new material - material.addEventListener( 'dispose', onMaterialDispose ); - - } else if ( program.code !== code ) { - - // changed glsl or parameters - releaseMaterialProgramReference( material ); - - } else if ( parameters.shaderID !== undefined ) { - - // same glsl and uniform list - return; - - } else { - - // only rebuild uniform list - programChange = false; - - } - - if ( programChange ) { - - if ( parameters.shaderID ) { - - var shader = ShaderLib[ parameters.shaderID ]; - - materialProperties.__webglShader = { - name: material.type, - uniforms: UniformsUtils.clone( shader.uniforms ), - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader - }; - - } else { - - materialProperties.__webglShader = { - name: material.type, - uniforms: material.uniforms, - vertexShader: material.vertexShader, - fragmentShader: material.fragmentShader - }; - - } - - material.__webglShader = materialProperties.__webglShader; - - program = programCache.acquireProgram( material, parameters, code ); - - materialProperties.program = program; - material.program = program; - - } - - var attributes = program.getAttributes(); - - if ( material.morphTargets ) { - - material.numSupportedMorphTargets = 0; - - for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { - - if ( attributes[ 'morphTarget' + i ] >= 0 ) { - - material.numSupportedMorphTargets ++; - - } - - } - - } - - if ( material.morphNormals ) { - - material.numSupportedMorphNormals = 0; - - for ( var i = 0; i < _this.maxMorphNormals; i ++ ) { - - if ( attributes[ 'morphNormal' + i ] >= 0 ) { - - material.numSupportedMorphNormals ++; - - } - - } - - } - - var uniforms = materialProperties.__webglShader.uniforms; - - if ( ! material.isShaderMaterial && - ! material.isRawShaderMaterial || - material.clipping === true ) { - - materialProperties.numClippingPlanes = _clipping.numPlanes; - materialProperties.numIntersection = _clipping.numIntersection; - uniforms.clippingPlanes = _clipping.uniform; - - } - - materialProperties.fog = fog; - - // store the light setup it was created for - - materialProperties.lightsHash = _lights.hash; - - if ( material.lights ) { - - // wire up the material to this renderer's lighting state - - uniforms.ambientLightColor.value = _lights.ambient; - uniforms.directionalLights.value = _lights.directional; - uniforms.spotLights.value = _lights.spot; - uniforms.rectAreaLights.value = _lights.rectArea; - uniforms.pointLights.value = _lights.point; - uniforms.hemisphereLights.value = _lights.hemi; - - uniforms.directionalShadowMap.value = _lights.directionalShadowMap; - uniforms.directionalShadowMatrix.value = _lights.directionalShadowMatrix; - uniforms.spotShadowMap.value = _lights.spotShadowMap; - uniforms.spotShadowMatrix.value = _lights.spotShadowMatrix; - uniforms.pointShadowMap.value = _lights.pointShadowMap; - uniforms.pointShadowMatrix.value = _lights.pointShadowMatrix; - // TODO (abelnation): add area lights shadow info to uniforms - - } - - var progUniforms = materialProperties.program.getUniforms(), - uniformsList = - WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); - - materialProperties.uniformsList = uniformsList; - - } - - function setMaterial( material ) { - - material.side === DoubleSide - ? state.disable( _gl.CULL_FACE ) - : state.enable( _gl.CULL_FACE ); - - state.setFlipSided( material.side === BackSide ); - - material.transparent === true - ? state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ) - : state.setBlending( NoBlending ); - - state.setDepthFunc( material.depthFunc ); - state.setDepthTest( material.depthTest ); - state.setDepthWrite( material.depthWrite ); - state.setColorWrite( material.colorWrite ); - state.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); - - } - - function setProgram( camera, fog, material, object ) { - - _usedTextureUnits = 0; - - var materialProperties = properties.get( material ); - - if ( _clippingEnabled ) { - - if ( _localClippingEnabled || camera !== _currentCamera ) { - - var useCache = - camera === _currentCamera && - material.id === _currentMaterialId; - - // we might want to call this function with some ClippingGroup - // object instead of the material, once it becomes feasible - // (#8465, #8379) - _clipping.setState( - material.clippingPlanes, material.clipIntersection, material.clipShadows, - camera, materialProperties, useCache ); - - } - - } - - if ( material.needsUpdate === false ) { - - if ( materialProperties.program === undefined ) { - - material.needsUpdate = true; - - } else if ( material.fog && materialProperties.fog !== fog ) { - - material.needsUpdate = true; - - } else if ( material.lights && materialProperties.lightsHash !== _lights.hash ) { - - material.needsUpdate = true; - - } else if ( materialProperties.numClippingPlanes !== undefined && - ( materialProperties.numClippingPlanes !== _clipping.numPlanes || - materialProperties.numIntersection !== _clipping.numIntersection ) ) { - - material.needsUpdate = true; - - } - - } - - if ( material.needsUpdate ) { - - initMaterial( material, fog, object ); - material.needsUpdate = false; - - } - - var refreshProgram = false; - var refreshMaterial = false; - var refreshLights = false; - - var program = materialProperties.program, - p_uniforms = program.getUniforms(), - m_uniforms = materialProperties.__webglShader.uniforms; - - if ( program.id !== _currentProgram ) { - - _gl.useProgram( program.program ); - _currentProgram = program.id; - - refreshProgram = true; - refreshMaterial = true; - refreshLights = true; - - } - - if ( material.id !== _currentMaterialId ) { - - _currentMaterialId = material.id; - - refreshMaterial = true; - - } - - if ( refreshProgram || camera !== _currentCamera ) { - - p_uniforms.set( _gl, camera, 'projectionMatrix' ); - - if ( capabilities.logarithmicDepthBuffer ) { - - p_uniforms.setValue( _gl, 'logDepthBufFC', - 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); - - } - - - if ( camera !== _currentCamera ) { - - _currentCamera = camera; - - // lighting uniforms depend on the camera so enforce an update - // now, in case this material supports lights - or later, when - // the next material that does gets activated: - - refreshMaterial = true; // set to true on material change - refreshLights = true; // remains set until update done - - } - - // load material specific uniforms - // (shader material also gets them for the sake of genericity) - - if ( material.isShaderMaterial || - material.isMeshPhongMaterial || - material.isMeshStandardMaterial || - material.envMap ) { - - var uCamPos = p_uniforms.map.cameraPosition; - - if ( uCamPos !== undefined ) { - - uCamPos.setValue( _gl, - _vector3.setFromMatrixPosition( camera.matrixWorld ) ); - - } - - } - - if ( material.isMeshPhongMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial || - material.skinning ) { - - p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); - - } - - p_uniforms.set( _gl, _this, 'toneMappingExposure' ); - p_uniforms.set( _gl, _this, 'toneMappingWhitePoint' ); - - } - - // skinning uniforms must be set even if material didn't change - // auto-setting of texture unit for bone texture must go before other textures - // not sure why, but otherwise weird things happen - - if ( material.skinning ) { - - p_uniforms.setOptional( _gl, object, 'bindMatrix' ); - p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); - - var skeleton = object.skeleton; - - if ( skeleton ) { - - if ( capabilities.floatVertexTextures && skeleton.useVertexTexture ) { - - p_uniforms.set( _gl, skeleton, 'boneTexture' ); - p_uniforms.set( _gl, skeleton, 'boneTextureWidth' ); - p_uniforms.set( _gl, skeleton, 'boneTextureHeight' ); - - } else { - - p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' ); - - } - - } - - } - - if ( refreshMaterial ) { - - if ( material.lights ) { - - // the current material requires lighting info - - // note: all lighting uniforms are always set correctly - // they simply reference the renderer's state for their - // values - // - // use the current material's .needsUpdate flags to set - // the GL state when required - - markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); - - } - - // refresh uniforms common to several materials - - if ( fog && material.fog ) { - - refreshUniformsFog( m_uniforms, fog ); - - } - - if ( material.isMeshBasicMaterial || - material.isMeshLambertMaterial || - material.isMeshPhongMaterial || - material.isMeshStandardMaterial || - material.isMeshNormalMaterial || - material.isMeshDepthMaterial ) { - - refreshUniformsCommon( m_uniforms, material ); - - } - - // refresh single material specific uniforms - - if ( material.isLineBasicMaterial ) { - - refreshUniformsLine( m_uniforms, material ); - - } else if ( material.isLineDashedMaterial ) { - - refreshUniformsLine( m_uniforms, material ); - refreshUniformsDash( m_uniforms, material ); - - } else if ( material.isPointsMaterial ) { - - refreshUniformsPoints( m_uniforms, material ); - - } else if ( material.isMeshLambertMaterial ) { - - refreshUniformsLambert( m_uniforms, material ); - - } else if ( material.isMeshToonMaterial ) { - - refreshUniformsToon( m_uniforms, material ); - - } else if ( material.isMeshPhongMaterial ) { - - refreshUniformsPhong( m_uniforms, material ); - - } else if ( material.isMeshPhysicalMaterial ) { - - refreshUniformsPhysical( m_uniforms, material ); - - } else if ( material.isMeshStandardMaterial ) { - - refreshUniformsStandard( m_uniforms, material ); - - } else if ( material.isMeshDepthMaterial ) { - - if ( material.displacementMap ) { - - m_uniforms.displacementMap.value = material.displacementMap; - m_uniforms.displacementScale.value = material.displacementScale; - m_uniforms.displacementBias.value = material.displacementBias; - - } - - } else if ( material.isMeshNormalMaterial ) { - - refreshUniformsNormal( m_uniforms, material ); - - } - - // RectAreaLight Texture - // TODO (mrdoob): Find a nicer implementation - - if ( m_uniforms.ltcMat !== undefined ) m_uniforms.ltcMat.value = THREE.UniformsLib.LTC_MAT_TEXTURE; - if ( m_uniforms.ltcMag !== undefined ) m_uniforms.ltcMag.value = THREE.UniformsLib.LTC_MAG_TEXTURE; - - WebGLUniforms.upload( - _gl, materialProperties.uniformsList, m_uniforms, _this ); - - } - - - // common matrices - - p_uniforms.set( _gl, object, 'modelViewMatrix' ); - p_uniforms.set( _gl, object, 'normalMatrix' ); - p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); - - return program; - - } - - // Uniforms (refresh uniforms objects) - - function refreshUniformsCommon( uniforms, material ) { - - uniforms.opacity.value = material.opacity; - - uniforms.diffuse.value = material.color; - - if ( material.emissive ) { - - uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); - - } - - uniforms.map.value = material.map; - uniforms.specularMap.value = material.specularMap; - uniforms.alphaMap.value = material.alphaMap; - - if ( material.lightMap ) { - - uniforms.lightMap.value = material.lightMap; - uniforms.lightMapIntensity.value = material.lightMapIntensity; - - } - - if ( material.aoMap ) { - - uniforms.aoMap.value = material.aoMap; - uniforms.aoMapIntensity.value = material.aoMapIntensity; - - } - - // uv repeat and offset setting priorities - // 1. color map - // 2. specular map - // 3. normal map - // 4. bump map - // 5. alpha map - // 6. emissive map - - var uvScaleMap; - - if ( material.map ) { - - uvScaleMap = material.map; - - } else if ( material.specularMap ) { - - uvScaleMap = material.specularMap; - - } else if ( material.displacementMap ) { - - uvScaleMap = material.displacementMap; - - } else if ( material.normalMap ) { - - uvScaleMap = material.normalMap; - - } else if ( material.bumpMap ) { - - uvScaleMap = material.bumpMap; - - } else if ( material.roughnessMap ) { - - uvScaleMap = material.roughnessMap; - - } else if ( material.metalnessMap ) { - - uvScaleMap = material.metalnessMap; - - } else if ( material.alphaMap ) { - - uvScaleMap = material.alphaMap; - - } else if ( material.emissiveMap ) { - - uvScaleMap = material.emissiveMap; - - } - - if ( uvScaleMap !== undefined ) { - - // backwards compatibility - if ( uvScaleMap.isWebGLRenderTarget ) { - - uvScaleMap = uvScaleMap.texture; - - } - - var offset = uvScaleMap.offset; - var repeat = uvScaleMap.repeat; - - uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); - - } - - uniforms.envMap.value = material.envMap; - - // don't flip CubeTexture envMaps, flip everything else: - // WebGLRenderTargetCube will be flipped for backwards compatibility - // WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture - // this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future - uniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1; - - uniforms.reflectivity.value = material.reflectivity; - uniforms.refractionRatio.value = material.refractionRatio; - - } - - function refreshUniformsLine( uniforms, material ) { - - uniforms.diffuse.value = material.color; - uniforms.opacity.value = material.opacity; - - } - - function refreshUniformsDash( uniforms, material ) { - - uniforms.dashSize.value = material.dashSize; - uniforms.totalSize.value = material.dashSize + material.gapSize; - uniforms.scale.value = material.scale; - - } - - function refreshUniformsPoints( uniforms, material ) { - - uniforms.diffuse.value = material.color; - uniforms.opacity.value = material.opacity; - uniforms.size.value = material.size * _pixelRatio; - uniforms.scale.value = _height * 0.5; - - uniforms.map.value = material.map; - - if ( material.map !== null ) { - - var offset = material.map.offset; - var repeat = material.map.repeat; - - uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); - - } - - } - - function refreshUniformsFog( uniforms, fog ) { - - uniforms.fogColor.value = fog.color; - - if ( fog.isFog ) { - - uniforms.fogNear.value = fog.near; - uniforms.fogFar.value = fog.far; - - } else if ( fog.isFogExp2 ) { - - uniforms.fogDensity.value = fog.density; - - } - - } - - function refreshUniformsLambert( uniforms, material ) { - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - } - - function refreshUniformsPhong( uniforms, material ) { - - uniforms.specular.value = material.specular; - uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsToon( uniforms, material ) { - - refreshUniformsPhong( uniforms, material ); - - if ( material.gradientMap ) { - - uniforms.gradientMap.value = material.gradientMap; - - } - - } - - function refreshUniformsStandard( uniforms, material ) { - - uniforms.roughness.value = material.roughness; - uniforms.metalness.value = material.metalness; - - if ( material.roughnessMap ) { - - uniforms.roughnessMap.value = material.roughnessMap; - - } - - if ( material.metalnessMap ) { - - uniforms.metalnessMap.value = material.metalnessMap; - - } - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - if ( material.envMap ) { - - //uniforms.envMap.value = material.envMap; // part of uniforms common - uniforms.envMapIntensity.value = material.envMapIntensity; - - } - - } - - function refreshUniformsPhysical( uniforms, material ) { - - uniforms.clearCoat.value = material.clearCoat; - uniforms.clearCoatRoughness.value = material.clearCoatRoughness; - - refreshUniformsStandard( uniforms, material ); - - } - - function refreshUniformsNormal( uniforms, material ) { - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - // If uniforms are marked as clean, they don't need to be loaded to the GPU. - - function markUniformsLightsNeedsUpdate( uniforms, value ) { - - uniforms.ambientLightColor.needsUpdate = value; - - uniforms.directionalLights.needsUpdate = value; - uniforms.pointLights.needsUpdate = value; - uniforms.spotLights.needsUpdate = value; - uniforms.rectAreaLights.needsUpdate = value; - uniforms.hemisphereLights.needsUpdate = value; - - } - - // Lighting - - function setupShadows( lights ) { - - var lightShadowsLength = 0; - - for ( var i = 0, l = lights.length; i < l; i ++ ) { - - var light = lights[ i ]; - - if ( light.castShadow ) { - - _lights.shadows[ lightShadowsLength ++ ] = light; - - } - - } - - _lights.shadows.length = lightShadowsLength; - - } - - function setupLights( lights, camera ) { - - var l, ll, light, - r = 0, g = 0, b = 0, - color, - intensity, - distance, - shadowMap, - - viewMatrix = camera.matrixWorldInverse, - - directionalLength = 0, - pointLength = 0, - spotLength = 0, - rectAreaLength = 0, - hemiLength = 0; - - for ( l = 0, ll = lights.length; l < ll; l ++ ) { - - light = lights[ l ]; - - color = light.color; - intensity = light.intensity; - distance = light.distance; - - shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; - - if ( light.isAmbientLight ) { - - r += color.r * intensity; - g += color.g * intensity; - b += color.b * intensity; - - } else if ( light.isDirectionalLight ) { - - var uniforms = lightCache.get( light ); - - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - _vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( _vector3 ); - uniforms.direction.transformDirection( viewMatrix ); - - uniforms.shadow = light.castShadow; - - if ( light.castShadow ) { - - uniforms.shadowBias = light.shadow.bias; - uniforms.shadowRadius = light.shadow.radius; - uniforms.shadowMapSize = light.shadow.mapSize; - - } - - _lights.directionalShadowMap[ directionalLength ] = shadowMap; - _lights.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; - _lights.directional[ directionalLength ++ ] = uniforms; - - } else if ( light.isSpotLight ) { - - var uniforms = lightCache.get( light ); - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - uniforms.color.copy( color ).multiplyScalar( intensity ); - uniforms.distance = distance; - - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - _vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( _vector3 ); - uniforms.direction.transformDirection( viewMatrix ); - - uniforms.coneCos = Math.cos( light.angle ); - uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); - uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; - - uniforms.shadow = light.castShadow; - - if ( light.castShadow ) { - - uniforms.shadowBias = light.shadow.bias; - uniforms.shadowRadius = light.shadow.radius; - uniforms.shadowMapSize = light.shadow.mapSize; - - } - - _lights.spotShadowMap[ spotLength ] = shadowMap; - _lights.spotShadowMatrix[ spotLength ] = light.shadow.matrix; - _lights.spot[ spotLength ++ ] = uniforms; - - } else if ( light.isRectAreaLight ) { - - var uniforms = lightCache.get( light ); - - // (a) intensity controls irradiance of entire light - uniforms.color - .copy( color ) - .multiplyScalar( intensity / ( light.width * light.height ) ); - - // (b) intensity controls the radiance per light area - // uniforms.color.copy( color ).multiplyScalar( intensity ); - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - // extract local rotation of light to derive width/height half vectors - _matrix42.identity(); - _matrix4.copy( light.matrixWorld ); - _matrix4.premultiply( viewMatrix ); - _matrix42.extractRotation( _matrix4 ); - - uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); - uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); - - uniforms.halfWidth.applyMatrix4( _matrix42 ); - uniforms.halfHeight.applyMatrix4( _matrix42 ); - - // TODO (abelnation): RectAreaLight distance? - // uniforms.distance = distance; - - _lights.rectArea[ rectAreaLength ++ ] = uniforms; - - } else if ( light.isPointLight ) { - - var uniforms = lightCache.get( light ); - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); - uniforms.distance = light.distance; - uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; - - uniforms.shadow = light.castShadow; - - if ( light.castShadow ) { - - uniforms.shadowBias = light.shadow.bias; - uniforms.shadowRadius = light.shadow.radius; - uniforms.shadowMapSize = light.shadow.mapSize; - - } - - _lights.pointShadowMap[ pointLength ] = shadowMap; - - if ( _lights.pointShadowMatrix[ pointLength ] === undefined ) { - - _lights.pointShadowMatrix[ pointLength ] = new Matrix4(); - - } - - // for point lights we set the shadow matrix to be a translation-only matrix - // equal to inverse of the light's position - _vector3.setFromMatrixPosition( light.matrixWorld ).negate(); - _lights.pointShadowMatrix[ pointLength ].identity().setPosition( _vector3 ); - - _lights.point[ pointLength ++ ] = uniforms; - - } else if ( light.isHemisphereLight ) { - - var uniforms = lightCache.get( light ); - - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - uniforms.direction.transformDirection( viewMatrix ); - uniforms.direction.normalize(); - - uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); - uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); - - _lights.hemi[ hemiLength ++ ] = uniforms; - - } - - } - - _lights.ambient[ 0 ] = r; - _lights.ambient[ 1 ] = g; - _lights.ambient[ 2 ] = b; - - _lights.directional.length = directionalLength; - _lights.spot.length = spotLength; - _lights.rectArea.length = rectAreaLength; - _lights.point.length = pointLength; - _lights.hemi.length = hemiLength; - - // TODO (sam-g-steel) why aren't we using join - _lights.hash = directionalLength + ',' + pointLength + ',' + spotLength + ',' + rectAreaLength + ',' + hemiLength + ',' + _lights.shadows.length; - - } - - // GL state setting - - this.setFaceCulling = function ( cullFace, frontFaceDirection ) { - - state.setCullFace( cullFace ); - state.setFlipSided( frontFaceDirection === FrontFaceDirectionCW ); - - }; - - // Textures - - function allocTextureUnit() { - - var textureUnit = _usedTextureUnits; - - if ( textureUnit >= capabilities.maxTextures ) { - - console.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures ); - - } - - _usedTextureUnits += 1; - - return textureUnit; - - } - - this.allocTextureUnit = allocTextureUnit; - - // this.setTexture2D = setTexture2D; - this.setTexture2D = ( function() { - - var warned = false; - - // backwards compatibility: peel texture.texture - return function setTexture2D( texture, slot ) { - - if ( texture && texture.isWebGLRenderTarget ) { - - if ( ! warned ) { - - console.warn( "THREE.WebGLRenderer.setTexture2D: don't use render targets as textures. Use their .texture property instead." ); - warned = true; - - } - - texture = texture.texture; - - } - - textures.setTexture2D( texture, slot ); - - }; - - }() ); - - this.setTexture = ( function() { - - var warned = false; - - return function setTexture( texture, slot ) { - - if ( ! warned ) { - - console.warn( "THREE.WebGLRenderer: .setTexture is deprecated, use setTexture2D instead." ); - warned = true; - - } - - textures.setTexture2D( texture, slot ); - - }; - - }() ); - - this.setTextureCube = ( function() { - - var warned = false; - - return function setTextureCube( texture, slot ) { - - // backwards compatibility: peel texture.texture - if ( texture && texture.isWebGLRenderTargetCube ) { - - if ( ! warned ) { - - console.warn( "THREE.WebGLRenderer.setTextureCube: don't use cube render targets as textures. Use their .texture property instead." ); - warned = true; - - } - - texture = texture.texture; - - } - - // currently relying on the fact that WebGLRenderTargetCube.texture is a Texture and NOT a CubeTexture - // TODO: unify these code paths - if ( ( texture && texture.isCubeTexture ) || - ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { - - // CompressedTexture can have Array in image :/ - - // this function alone should take care of cube textures - textures.setTextureCube( texture, slot ); - - } else { - - // assumed: texture property of THREE.WebGLRenderTargetCube - - textures.setTextureCubeDynamic( texture, slot ); - - } - - }; - - }() ); - - this.getCurrentRenderTarget = function() { - - return _currentRenderTarget; - - }; - - this.setRenderTarget = function ( renderTarget ) { - - _currentRenderTarget = renderTarget; - - if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { - - textures.setupRenderTarget( renderTarget ); - - } - - var isCube = ( renderTarget && renderTarget.isWebGLRenderTargetCube ); - var framebuffer; - - if ( renderTarget ) { - - var renderTargetProperties = properties.get( renderTarget ); - - if ( isCube ) { - - framebuffer = renderTargetProperties.__webglFramebuffer[ renderTarget.activeCubeFace ]; - - } else { - - framebuffer = renderTargetProperties.__webglFramebuffer; - - } - - _currentScissor.copy( renderTarget.scissor ); - _currentScissorTest = renderTarget.scissorTest; - - _currentViewport.copy( renderTarget.viewport ); - - } else { - - framebuffer = null; - - _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ); - _currentScissorTest = _scissorTest; - - _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ); - - } - - if ( _currentFramebuffer !== framebuffer ) { - - _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - _currentFramebuffer = framebuffer; - - } - - state.scissor( _currentScissor ); - state.setScissorTest( _currentScissorTest ); - - state.viewport( _currentViewport ); - - if ( isCube ) { - - var textureProperties = properties.get( renderTarget.texture ); - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, renderTarget.activeMipMapLevel ); - - } - - }; - - this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) { - - if ( ( renderTarget && renderTarget.isWebGLRenderTarget ) === false ) { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); - return; - - } - - var framebuffer = properties.get( renderTarget ).__webglFramebuffer; - - if ( framebuffer ) { - - var restore = false; - - if ( framebuffer !== _currentFramebuffer ) { - - _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - - restore = true; - - } - - try { - - var texture = renderTarget.texture; - var textureFormat = texture.format; - var textureType = texture.type; - - if ( textureFormat !== RGBAFormat && paramThreeToGL( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); - return; - - } - - if ( textureType !== UnsignedByteType && paramThreeToGL( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // IE11, Edge and Chrome Mac < 52 (#9513) - ! ( textureType === FloatType && ( extensions.get( 'OES_texture_float' ) || extensions.get( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox - ! ( textureType === HalfFloatType && extensions.get( 'EXT_color_buffer_half_float' ) ) ) { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); - return; - - } - - if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) { - - // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) - - if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { - - _gl.readPixels( x, y, width, height, paramThreeToGL( textureFormat ), paramThreeToGL( textureType ), buffer ); - - } - - } else { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); - - } - - } finally { - - if ( restore ) { - - _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer ); - - } - - } - - } - - }; - - // Map three.js constants to WebGL constants - - function paramThreeToGL( p ) { - - var extension; - - if ( p === RepeatWrapping ) return _gl.REPEAT; - if ( p === ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE; - if ( p === MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT; - - if ( p === NearestFilter ) return _gl.NEAREST; - if ( p === NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST; - if ( p === NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR; - - if ( p === LinearFilter ) return _gl.LINEAR; - if ( p === LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST; - if ( p === LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR; - - if ( p === UnsignedByteType ) return _gl.UNSIGNED_BYTE; - if ( p === UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4; - if ( p === UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1; - if ( p === UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5; - - if ( p === ByteType ) return _gl.BYTE; - if ( p === ShortType ) return _gl.SHORT; - if ( p === UnsignedShortType ) return _gl.UNSIGNED_SHORT; - if ( p === IntType ) return _gl.INT; - if ( p === UnsignedIntType ) return _gl.UNSIGNED_INT; - if ( p === FloatType ) return _gl.FLOAT; - - if ( p === HalfFloatType ) { - - extension = extensions.get( 'OES_texture_half_float' ); - - if ( extension !== null ) return extension.HALF_FLOAT_OES; - - } - - if ( p === AlphaFormat ) return _gl.ALPHA; - if ( p === RGBFormat ) return _gl.RGB; - if ( p === RGBAFormat ) return _gl.RGBA; - if ( p === LuminanceFormat ) return _gl.LUMINANCE; - if ( p === LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA; - if ( p === DepthFormat ) return _gl.DEPTH_COMPONENT; - if ( p === DepthStencilFormat ) return _gl.DEPTH_STENCIL; - - if ( p === AddEquation ) return _gl.FUNC_ADD; - if ( p === SubtractEquation ) return _gl.FUNC_SUBTRACT; - if ( p === ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT; - - if ( p === ZeroFactor ) return _gl.ZERO; - if ( p === OneFactor ) return _gl.ONE; - if ( p === SrcColorFactor ) return _gl.SRC_COLOR; - if ( p === OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR; - if ( p === SrcAlphaFactor ) return _gl.SRC_ALPHA; - if ( p === OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA; - if ( p === DstAlphaFactor ) return _gl.DST_ALPHA; - if ( p === OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA; - - if ( p === DstColorFactor ) return _gl.DST_COLOR; - if ( p === OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR; - if ( p === SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE; - - if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || - p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); - - if ( extension !== null ) { - - if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; - if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; - if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; - if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; - - } - - } - - if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || - p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - - if ( extension !== null ) { - - if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; - if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; - if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; - if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; - - } - - } - - if ( p === RGB_ETC1_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); - - if ( extension !== null ) return extension.COMPRESSED_RGB_ETC1_WEBGL; - - } - - if ( p === MinEquation || p === MaxEquation ) { - - extension = extensions.get( 'EXT_blend_minmax' ); - - if ( extension !== null ) { - - if ( p === MinEquation ) return extension.MIN_EXT; - if ( p === MaxEquation ) return extension.MAX_EXT; - - } - - } - - if ( p === UnsignedInt248Type ) { - - extension = extensions.get( 'WEBGL_depth_texture' ); - - if ( extension !== null ) return extension.UNSIGNED_INT_24_8_WEBGL; - - } - - return 0; - - } - - } - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - - function FogExp2 ( color, density ) { - - this.name = ''; - - this.color = new Color( color ); - this.density = ( density !== undefined ) ? density : 0.00025; - - } - - FogExp2.prototype.isFogExp2 = true; - - FogExp2.prototype.clone = function () { - - return new FogExp2( this.color.getHex(), this.density ); - - }; - - FogExp2.prototype.toJSON = function ( meta ) { - - return { - type: 'FogExp2', - color: this.color.getHex(), - density: this.density - }; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - - function Fog ( color, near, far ) { - - this.name = ''; - - this.color = new Color( color ); - - this.near = ( near !== undefined ) ? near : 1; - this.far = ( far !== undefined ) ? far : 1000; - - } - - Fog.prototype.isFog = true; - - Fog.prototype.clone = function () { - - return new Fog( this.color.getHex(), this.near, this.far ); - - }; - - Fog.prototype.toJSON = function ( meta ) { - - return { - type: 'Fog', - color: this.color.getHex(), - near: this.near, - far: this.far - }; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function Scene () { - - Object3D.call( this ); - - this.type = 'Scene'; - - this.background = null; - this.fog = null; - this.overrideMaterial = null; - - this.autoUpdate = true; // checked by the renderer - - } - - Scene.prototype = Object.create( Object3D.prototype ); - - Scene.prototype.constructor = Scene; - - Scene.prototype.copy = function ( source, recursive ) { - - Object3D.prototype.copy.call( this, source, recursive ); - - if ( source.background !== null ) this.background = source.background.clone(); - if ( source.fog !== null ) this.fog = source.fog.clone(); - if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); - - this.autoUpdate = source.autoUpdate; - this.matrixAutoUpdate = source.matrixAutoUpdate; - - return this; - - }; - - Scene.prototype.toJSON = function ( meta ) { - - var data = Object3D.prototype.toJSON.call( this, meta ); - - if ( this.background !== null ) data.object.background = this.background.toJSON( meta ); - if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); - - return data; - - }; - - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ - - function LensFlare( texture, size, distance, blending, color ) { - - Object3D.call( this ); - - this.lensFlares = []; - - this.positionScreen = new Vector3(); - this.customUpdateCallback = undefined; - - if ( texture !== undefined ) { - - this.add( texture, size, distance, blending, color ); - - } - - } - - LensFlare.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: LensFlare, - - isLensFlare: true, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - this.positionScreen.copy( source.positionScreen ); - this.customUpdateCallback = source.customUpdateCallback; - - for ( var i = 0, l = source.lensFlares.length; i < l; i ++ ) { - - this.lensFlares.push( source.lensFlares[ i ] ); - - } - - return this; - - }, - - add: function ( texture, size, distance, blending, color, opacity ) { - - if ( size === undefined ) size = - 1; - if ( distance === undefined ) distance = 0; - if ( opacity === undefined ) opacity = 1; - if ( color === undefined ) color = new Color( 0xffffff ); - if ( blending === undefined ) blending = NormalBlending; - - distance = Math.min( distance, Math.max( 0, distance ) ); - - this.lensFlares.push( { - texture: texture, // THREE.Texture - size: size, // size in pixels (-1 = use texture.width) - distance: distance, // distance (0-1) from light source (0=at light source) - x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is in front z = 1 is back - scale: 1, // scale - rotation: 0, // rotation - opacity: opacity, // opacity - color: color, // color - blending: blending // blending - } ); - - }, - - /* - * Update lens flares update positions on all flares based on the screen position - * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way. - */ - - updateLensFlares: function () { - - var f, fl = this.lensFlares.length; - var flare; - var vecX = - this.positionScreen.x * 2; - var vecY = - this.positionScreen.y * 2; - - for ( f = 0; f < fl; f ++ ) { - - flare = this.lensFlares[ f ]; - - flare.x = this.positionScreen.x + vecX * flare.distance; - flare.y = this.positionScreen.y + vecY * flare.distance; - - flare.wantedRotation = flare.x * Math.PI * 0.25; - flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; - - } - - } - - } ); - - /** - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * uvOffset: new THREE.Vector2(), - * uvScale: new THREE.Vector2() - * } - */ - - function SpriteMaterial( parameters ) { - - Material.call( this ); - - this.type = 'SpriteMaterial'; - - this.color = new Color( 0xffffff ); - this.map = null; - - this.rotation = 0; - - this.fog = false; - this.lights = false; - - this.setValues( parameters ); - - } - - SpriteMaterial.prototype = Object.create( Material.prototype ); - SpriteMaterial.prototype.constructor = SpriteMaterial; - - SpriteMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - this.map = source.map; - - this.rotation = source.rotation; - - return this; - - }; - - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ - - function Sprite( material ) { - - Object3D.call( this ); - - this.type = 'Sprite'; - - this.material = ( material !== undefined ) ? material : new SpriteMaterial(); - - } - - Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Sprite, - - isSprite: true, - - raycast: ( function () { - - var matrixPosition = new Vector3(); - - return function raycast( raycaster, intersects ) { - - matrixPosition.setFromMatrixPosition( this.matrixWorld ); - - var distanceSq = raycaster.ray.distanceSqToPoint( matrixPosition ); - var guessSizeSq = this.scale.x * this.scale.y / 4; - - if ( distanceSq > guessSizeSq ) { - - return; - - } - - intersects.push( { - - distance: Math.sqrt( distanceSq ), - point: this.position, - face: null, - object: this - - } ); - - }; - - }() ), - - clone: function () { - - return new this.constructor( this.material ).copy( this ); - - } - - } ); - - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ - - function LOD() { - - Object3D.call( this ); - - this.type = 'LOD'; - - Object.defineProperties( this, { - levels: { - enumerable: true, - value: [] - } - } ); - - } - - - LOD.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: LOD, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source, false ); - - var levels = source.levels; - - for ( var i = 0, l = levels.length; i < l; i ++ ) { - - var level = levels[ i ]; - - this.addLevel( level.object.clone(), level.distance ); - - } - - return this; - - }, - - addLevel: function ( object, distance ) { - - if ( distance === undefined ) distance = 0; - - distance = Math.abs( distance ); - - var levels = this.levels; - - for ( var l = 0; l < levels.length; l ++ ) { - - if ( distance < levels[ l ].distance ) { - - break; - - } - - } - - levels.splice( l, 0, { distance: distance, object: object } ); - - this.add( object ); - - }, - - getObjectForDistance: function ( distance ) { - - var levels = this.levels; - - for ( var i = 1, l = levels.length; i < l; i ++ ) { - - if ( distance < levels[ i ].distance ) { - - break; - - } - - } - - return levels[ i - 1 ].object; - - }, - - raycast: ( function () { - - var matrixPosition = new Vector3(); - - return function raycast( raycaster, intersects ) { - - matrixPosition.setFromMatrixPosition( this.matrixWorld ); - - var distance = raycaster.ray.origin.distanceTo( matrixPosition ); - - this.getObjectForDistance( distance ).raycast( raycaster, intersects ); - - }; - - }() ), - - update: function () { - - var v1 = new Vector3(); - var v2 = new Vector3(); - - return function update( camera ) { - - var levels = this.levels; - - if ( levels.length > 1 ) { - - v1.setFromMatrixPosition( camera.matrixWorld ); - v2.setFromMatrixPosition( this.matrixWorld ); - - var distance = v1.distanceTo( v2 ); - - levels[ 0 ].object.visible = true; - - for ( var i = 1, l = levels.length; i < l; i ++ ) { - - if ( distance >= levels[ i ].distance ) { - - levels[ i - 1 ].object.visible = false; - levels[ i ].object.visible = true; - - } else { - - break; - - } - - } - - for ( ; i < l; i ++ ) { - - levels[ i ].object.visible = false; - - } - - } - - }; - - }(), - - toJSON: function ( meta ) { - - var data = Object3D.prototype.toJSON.call( this, meta ); - - data.object.levels = []; - - var levels = this.levels; - - for ( var i = 0, l = levels.length; i < l; i ++ ) { - - var level = levels[ i ]; - - data.object.levels.push( { - object: level.object.uuid, - distance: level.distance - } ); - - } - - return data; - - } - - } ); - - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author michael guerrero / http://realitymeltdown.com - * @author ikerr / http://verold.com - */ - - function Skeleton( bones, boneInverses, useVertexTexture ) { - - this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true; - - this.identityMatrix = new Matrix4(); - - // copy the bone array - - bones = bones || []; - - this.bones = bones.slice( 0 ); - - // create a bone texture or an array of floats - - if ( this.useVertexTexture ) { - - // layout (1 matrix = 4 pixels) - // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) - // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) - // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) - // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) - // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) - - - var size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix - size = _Math.nextPowerOfTwo( Math.ceil( size ) ); - size = Math.max( size, 4 ); - - this.boneTextureWidth = size; - this.boneTextureHeight = size; - - this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel - this.boneTexture = new DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, RGBAFormat, FloatType ); - - } else { - - this.boneMatrices = new Float32Array( 16 * this.bones.length ); - - } - - // use the supplied bone inverses or calculate the inverses - - if ( boneInverses === undefined ) { - - this.calculateInverses(); - - } else { - - if ( this.bones.length === boneInverses.length ) { - - this.boneInverses = boneInverses.slice( 0 ); - - } else { - - console.warn( 'THREE.Skeleton bonInverses is the wrong length.' ); - - this.boneInverses = []; - - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - - this.boneInverses.push( new Matrix4() ); - - } - - } - - } - - } - - Object.assign( Skeleton.prototype, { - - calculateInverses: function () { - - this.boneInverses = []; - - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - - var inverse = new Matrix4(); - - if ( this.bones[ b ] ) { - - inverse.getInverse( this.bones[ b ].matrixWorld ); - - } - - this.boneInverses.push( inverse ); - - } - - }, - - pose: function () { - - var bone; - - // recover the bind-time world matrices - - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - - bone = this.bones[ b ]; - - if ( bone ) { - - bone.matrixWorld.getInverse( this.boneInverses[ b ] ); - - } - - } - - // compute the local matrices, positions, rotations and scales - - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - - bone = this.bones[ b ]; - - if ( bone ) { - - if ( bone.parent && bone.parent.isBone ) { - - bone.matrix.getInverse( bone.parent.matrixWorld ); - bone.matrix.multiply( bone.matrixWorld ); - - } else { - - bone.matrix.copy( bone.matrixWorld ); - - } - - bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); - - } - - } - - }, - - update: ( function () { - - var offsetMatrix = new Matrix4(); - - return function update() { - - // flatten bone matrices to array - - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - - // compute the offset between the current and the original transform - - var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix; - - offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] ); - offsetMatrix.toArray( this.boneMatrices, b * 16 ); - - } - - if ( this.useVertexTexture ) { - - this.boneTexture.needsUpdate = true; - - } - - }; - - } )(), - - clone: function () { - - return new Skeleton( this.bones, this.boneInverses, this.useVertexTexture ); - - } - - } ); - - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author ikerr / http://verold.com - */ - - function Bone() { - - Object3D.call( this ); - - this.type = 'Bone'; - - } - - Bone.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Bone, - - isBone: true - - } ); - - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author ikerr / http://verold.com - */ - - function SkinnedMesh( geometry, material, useVertexTexture ) { - - Mesh.call( this, geometry, material ); - - this.type = 'SkinnedMesh'; - - this.bindMode = "attached"; - this.bindMatrix = new Matrix4(); - this.bindMatrixInverse = new Matrix4(); - - // init bones - - // TODO: remove bone creation as there is no reason (other than - // convenience) for THREE.SkinnedMesh to do this. - - var bones = []; - - if ( this.geometry && this.geometry.bones !== undefined ) { - - var bone, gbone; - - for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { - - gbone = this.geometry.bones[ b ]; - - bone = new Bone(); - bones.push( bone ); - - bone.name = gbone.name; - bone.position.fromArray( gbone.pos ); - bone.quaternion.fromArray( gbone.rotq ); - if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); - - } - - for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { - - gbone = this.geometry.bones[ b ]; - - if ( gbone.parent !== - 1 && gbone.parent !== null && - bones[ gbone.parent ] !== undefined ) { - - bones[ gbone.parent ].add( bones[ b ] ); - - } else { - - this.add( bones[ b ] ); - - } - - } - - } - - this.normalizeSkinWeights(); - - this.updateMatrixWorld( true ); - this.bind( new Skeleton( bones, undefined, useVertexTexture ), this.matrixWorld ); - - } - - - SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { - - constructor: SkinnedMesh, - - isSkinnedMesh: true, - - bind: function( skeleton, bindMatrix ) { - - this.skeleton = skeleton; - - if ( bindMatrix === undefined ) { - - this.updateMatrixWorld( true ); - - this.skeleton.calculateInverses(); - - bindMatrix = this.matrixWorld; - - } - - this.bindMatrix.copy( bindMatrix ); - this.bindMatrixInverse.getInverse( bindMatrix ); - - }, - - pose: function () { - - this.skeleton.pose(); - - }, - - normalizeSkinWeights: function () { - - if ( this.geometry && this.geometry.isGeometry ) { - - for ( var i = 0; i < this.geometry.skinWeights.length; i ++ ) { - - var sw = this.geometry.skinWeights[ i ]; - - var scale = 1.0 / sw.lengthManhattan(); - - if ( scale !== Infinity ) { - - sw.multiplyScalar( scale ); - - } else { - - sw.set( 1, 0, 0, 0 ); // do something reasonable - - } - - } - - } else if ( this.geometry && this.geometry.isBufferGeometry ) { - - var vec = new Vector4(); - - var skinWeight = this.geometry.attributes.skinWeight; - - for ( var i = 0; i < skinWeight.count; i ++ ) { - - vec.x = skinWeight.getX( i ); - vec.y = skinWeight.getY( i ); - vec.z = skinWeight.getZ( i ); - vec.w = skinWeight.getW( i ); - - var scale = 1.0 / vec.lengthManhattan(); - - if ( scale !== Infinity ) { - - vec.multiplyScalar( scale ); - - } else { - - vec.set( 1, 0, 0, 0 ); // do something reasonable - - } - - skinWeight.setXYZW( i, vec.x, vec.y, vec.z, vec.w ); - - } - - } - - }, - - updateMatrixWorld: function( force ) { - - Mesh.prototype.updateMatrixWorld.call( this, true ); - - if ( this.bindMode === "attached" ) { - - this.bindMatrixInverse.getInverse( this.matrixWorld ); - - } else if ( this.bindMode === "detached" ) { - - this.bindMatrixInverse.getInverse( this.bindMatrix ); - - } else { - - console.warn( 'THREE.SkinnedMesh unrecognized bindMode: ' + this.bindMode ); - - } - - }, - - clone: function() { - - return new this.constructor( this.geometry, this.material, this.skeleton.useVertexTexture ).copy( this ); - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * linecap: "round", - * linejoin: "round" - * } - */ - - function LineBasicMaterial( parameters ) { - - Material.call( this ); - - this.type = 'LineBasicMaterial'; - - this.color = new Color( 0xffffff ); - - this.linewidth = 1; - this.linecap = 'round'; - this.linejoin = 'round'; - - this.lights = false; - - this.setValues( parameters ); - - } - - LineBasicMaterial.prototype = Object.create( Material.prototype ); - LineBasicMaterial.prototype.constructor = LineBasicMaterial; - - LineBasicMaterial.prototype.isLineBasicMaterial = true; - - LineBasicMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.linewidth = source.linewidth; - this.linecap = source.linecap; - this.linejoin = source.linejoin; - - return this; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function Line( geometry, material, mode ) { - - if ( mode === 1 ) { - - console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' ); - return new LineSegments( geometry, material ); - - } - - Object3D.call( this ); - - this.type = 'Line'; - - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new LineBasicMaterial( { color: Math.random() * 0xffffff } ); - - } - - Line.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Line, - - isLine: true, - - raycast: ( function () { - - var inverseMatrix = new Matrix4(); - var ray = new Ray(); - var sphere = new Sphere(); - - return function raycast( raycaster, intersects ) { - - var precision = raycaster.linePrecision; - var precisionSq = precision * precision; - - var geometry = this.geometry; - var matrixWorld = this.matrixWorld; - - // Checking boundingSphere distance to ray - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - - sphere.copy( geometry.boundingSphere ); - sphere.applyMatrix4( matrixWorld ); - - if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; - - // - - inverseMatrix.getInverse( matrixWorld ); - ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); - - var vStart = new Vector3(); - var vEnd = new Vector3(); - var interSegment = new Vector3(); - var interRay = new Vector3(); - var step = (this && this.isLineSegments) ? 2 : 1; - - if ( geometry.isBufferGeometry ) { - - var index = geometry.index; - var attributes = geometry.attributes; - var positions = attributes.position.array; - - if ( index !== null ) { - - var indices = index.array; - - for ( var i = 0, l = indices.length - 1; i < l; i += step ) { - - var a = indices[ i ]; - var b = indices[ i + 1 ]; - - vStart.fromArray( positions, a * 3 ); - vEnd.fromArray( positions, b * 3 ); - - var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - - if ( distSq > precisionSq ) continue; - - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - - var distance = raycaster.ray.origin.distanceTo( interRay ); - - if ( distance < raycaster.near || distance > raycaster.far ) continue; - - intersects.push( { - - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this - - } ); - - } - - } else { - - for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) { - - vStart.fromArray( positions, 3 * i ); - vEnd.fromArray( positions, 3 * i + 3 ); - - var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - - if ( distSq > precisionSq ) continue; - - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - - var distance = raycaster.ray.origin.distanceTo( interRay ); - - if ( distance < raycaster.near || distance > raycaster.far ) continue; - - intersects.push( { - - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this - - } ); - - } - - } - - } else if ( geometry.isGeometry ) { - - var vertices = geometry.vertices; - var nbVertices = vertices.length; - - for ( var i = 0; i < nbVertices - 1; i += step ) { - - var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); - - if ( distSq > precisionSq ) continue; - - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - - var distance = raycaster.ray.origin.distanceTo( interRay ); - - if ( distance < raycaster.near || distance > raycaster.far ) continue; - - intersects.push( { - - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this - - } ); - - } - - } - - }; - - }() ), - - clone: function () { - - return new this.constructor( this.geometry, this.material ).copy( this ); - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function LineSegments( geometry, material ) { - - Line.call( this, geometry, material ); - - this.type = 'LineSegments'; - - } - - LineSegments.prototype = Object.assign( Object.create( Line.prototype ), { - - constructor: LineSegments, - - isLineSegments: true - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * size: , - * sizeAttenuation: - * } - */ - - function PointsMaterial( parameters ) { - - Material.call( this ); - - this.type = 'PointsMaterial'; - - this.color = new Color( 0xffffff ); - - this.map = null; - - this.size = 1; - this.sizeAttenuation = true; - - this.lights = false; - - this.setValues( parameters ); - - } - - PointsMaterial.prototype = Object.create( Material.prototype ); - PointsMaterial.prototype.constructor = PointsMaterial; - - PointsMaterial.prototype.isPointsMaterial = true; - - PointsMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.size = source.size; - this.sizeAttenuation = source.sizeAttenuation; - - return this; - - }; - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function Points( geometry, material ) { - - Object3D.call( this ); - - this.type = 'Points'; - - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new PointsMaterial( { color: Math.random() * 0xffffff } ); - - } - - Points.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Points, - - isPoints: true, - - raycast: ( function () { - - var inverseMatrix = new Matrix4(); - var ray = new Ray(); - var sphere = new Sphere(); - - return function raycast( raycaster, intersects ) { - - var object = this; - var geometry = this.geometry; - var matrixWorld = this.matrixWorld; - var threshold = raycaster.params.Points.threshold; - - // Checking boundingSphere distance to ray - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - - sphere.copy( geometry.boundingSphere ); - sphere.applyMatrix4( matrixWorld ); - - if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; - - // - - inverseMatrix.getInverse( matrixWorld ); - ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); - - var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - var localThresholdSq = localThreshold * localThreshold; - var position = new Vector3(); - - function testPoint( point, index ) { - - var rayPointDistanceSq = ray.distanceSqToPoint( point ); - - if ( rayPointDistanceSq < localThresholdSq ) { - - var intersectPoint = ray.closestPointToPoint( point ); - intersectPoint.applyMatrix4( matrixWorld ); - - var distance = raycaster.ray.origin.distanceTo( intersectPoint ); - - if ( distance < raycaster.near || distance > raycaster.far ) return; - - intersects.push( { - - distance: distance, - distanceToRay: Math.sqrt( rayPointDistanceSq ), - point: intersectPoint.clone(), - index: index, - face: null, - object: object - - } ); - - } - - } - - if ( geometry.isBufferGeometry ) { - - var index = geometry.index; - var attributes = geometry.attributes; - var positions = attributes.position.array; - - if ( index !== null ) { - - var indices = index.array; - - for ( var i = 0, il = indices.length; i < il; i ++ ) { - - var a = indices[ i ]; - - position.fromArray( positions, a * 3 ); - - testPoint( position, a ); - - } - - } else { - - for ( var i = 0, l = positions.length / 3; i < l; i ++ ) { - - position.fromArray( positions, i * 3 ); - - testPoint( position, i ); - - } - - } - - } else { - - var vertices = geometry.vertices; - - for ( var i = 0, l = vertices.length; i < l; i ++ ) { - - testPoint( vertices[ i ], i ); - - } - - } - - }; - - }() ), - - clone: function () { - - return new this.constructor( this.geometry, this.material ).copy( this ); - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function Group() { - - Object3D.call( this ); - - this.type = 'Group'; - - } - - Group.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Group - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - - Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.generateMipmaps = false; - - var scope = this; - - function update() { - - requestAnimationFrame( update ); - - if ( video.readyState >= video.HAVE_CURRENT_DATA ) { - - scope.needsUpdate = true; - - } - - } - - update(); - - } - - VideoTexture.prototype = Object.create( Texture.prototype ); - VideoTexture.prototype.constructor = VideoTexture; - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function CompressedTexture( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { - - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - - this.image = { width: width, height: height }; - this.mipmaps = mipmaps; - - // no flipping for cube textures - // (also flipping doesn't work for compressed textures ) - - this.flipY = false; - - // can't generate mipmaps for compressed textures - // mips must be embedded in DDS files - - this.generateMipmaps = false; - - } - - CompressedTexture.prototype = Object.create( Texture.prototype ); - CompressedTexture.prototype.constructor = CompressedTexture; - - CompressedTexture.prototype.isCompressedTexture = true; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - - Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.needsUpdate = true; - - } - - CanvasTexture.prototype = Object.create( Texture.prototype ); - CanvasTexture.prototype.constructor = CanvasTexture; - - /** - * @author Matt DesLauriers / @mattdesl - * @author atix / arthursilber.de - */ - - function DepthTexture( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { - - format = format !== undefined ? format : DepthFormat; - - if ( format !== DepthFormat && format !== DepthStencilFormat ) { - - throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' ) - - } - - if ( type === undefined && format === DepthFormat ) type = UnsignedShortType; - if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; - - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.image = { width: width, height: height }; - - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; - - this.flipY = false; - this.generateMipmaps = false; - - } - - DepthTexture.prototype = Object.create( Texture.prototype ); - DepthTexture.prototype.constructor = DepthTexture; - DepthTexture.prototype.isDepthTexture = true; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WireframeGeometry( geometry ) { - - BufferGeometry.call( this ); - - var edge = [ 0, 0 ], hash = {}; - - function sortFunction( a, b ) { - - return a - b; - - } - - var keys = [ 'a', 'b', 'c' ]; - - if ( geometry && geometry.isGeometry ) { - - var vertices = geometry.vertices; - var faces = geometry.faces; - var numEdges = 0; - - // allocate maximal size - var edges = new Uint32Array( 6 * faces.length ); - - for ( var i = 0, l = faces.length; i < l; i ++ ) { - - var face = faces[ i ]; - - for ( var j = 0; j < 3; j ++ ) { - - edge[ 0 ] = face[ keys[ j ] ]; - edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; - edge.sort( sortFunction ); - - var key = edge.toString(); - - if ( hash[ key ] === undefined ) { - - edges[ 2 * numEdges ] = edge[ 0 ]; - edges[ 2 * numEdges + 1 ] = edge[ 1 ]; - hash[ key ] = true; - numEdges ++; - - } - - } - - } - - var coords = new Float32Array( numEdges * 2 * 3 ); - - for ( var i = 0, l = numEdges; i < l; i ++ ) { - - for ( var j = 0; j < 2; j ++ ) { - - var vertex = vertices[ edges [ 2 * i + j ] ]; - - var index = 6 * i + 3 * j; - coords[ index + 0 ] = vertex.x; - coords[ index + 1 ] = vertex.y; - coords[ index + 2 ] = vertex.z; - - } - - } - - this.addAttribute( 'position', new BufferAttribute( coords, 3 ) ); - - } else if ( geometry && geometry.isBufferGeometry ) { - - if ( geometry.index !== null ) { - - // Indexed BufferGeometry - - var indices = geometry.index.array; - var vertices = geometry.attributes.position; - var groups = geometry.groups; - var numEdges = 0; - - if ( groups.length === 0 ) { - - geometry.addGroup( 0, indices.length ); - - } - - // allocate maximal size - var edges = new Uint32Array( 2 * indices.length ); - - for ( var o = 0, ol = groups.length; o < ol; ++ o ) { - - var group = groups[ o ]; - - var start = group.start; - var count = group.count; - - for ( var i = start, il = start + count; i < il; i += 3 ) { - - for ( var j = 0; j < 3; j ++ ) { - - edge[ 0 ] = indices[ i + j ]; - edge[ 1 ] = indices[ i + ( j + 1 ) % 3 ]; - edge.sort( sortFunction ); - - var key = edge.toString(); - - if ( hash[ key ] === undefined ) { - - edges[ 2 * numEdges ] = edge[ 0 ]; - edges[ 2 * numEdges + 1 ] = edge[ 1 ]; - hash[ key ] = true; - numEdges ++; - - } - - } - - } - - } - - var coords = new Float32Array( numEdges * 2 * 3 ); - - for ( var i = 0, l = numEdges; i < l; i ++ ) { - - for ( var j = 0; j < 2; j ++ ) { - - var index = 6 * i + 3 * j; - var index2 = edges[ 2 * i + j ]; - - coords[ index + 0 ] = vertices.getX( index2 ); - coords[ index + 1 ] = vertices.getY( index2 ); - coords[ index + 2 ] = vertices.getZ( index2 ); - - } - - } - - this.addAttribute( 'position', new BufferAttribute( coords, 3 ) ); - - } else { - - // non-indexed BufferGeometry - - var vertices = geometry.attributes.position.array; - var numEdges = vertices.length / 3; - var numTris = numEdges / 3; - - var coords = new Float32Array( numEdges * 2 * 3 ); - - for ( var i = 0, l = numTris; i < l; i ++ ) { - - for ( var j = 0; j < 3; j ++ ) { - - var index = 18 * i + 6 * j; - - var index1 = 9 * i + 3 * j; - coords[ index + 0 ] = vertices[ index1 ]; - coords[ index + 1 ] = vertices[ index1 + 1 ]; - coords[ index + 2 ] = vertices[ index1 + 2 ]; - - var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 ); - coords[ index + 3 ] = vertices[ index2 ]; - coords[ index + 4 ] = vertices[ index2 + 1 ]; - coords[ index + 5 ] = vertices[ index2 + 2 ]; - - } - - } - - this.addAttribute( 'position', new BufferAttribute( coords, 3 ) ); - - } - - } - - } - - WireframeGeometry.prototype = Object.create( BufferGeometry.prototype ); - WireframeGeometry.prototype.constructor = WireframeGeometry; - - /** - * @author Mugen87 / https://github.com/Mugen87 - * - * Parametric Surfaces Geometry - * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 - */ - - function ParametricBufferGeometry( func, slices, stacks ) { - - BufferGeometry.call( this ); - - this.type = 'ParametricBufferGeometry'; - - this.parameters = { - func: func, - slices: slices, - stacks: stacks - }; - - // generate vertices and uvs - - var vertices = []; - var uvs = []; - - var i, j, p; - var u, v; - - var sliceCount = slices + 1; - - for ( i = 0; i <= stacks; i ++ ) { - - v = i / stacks; - - for ( j = 0; j <= slices; j ++ ) { - - u = j / slices; - - p = func( u, v ); - vertices.push( p.x, p.y, p.z ); - - uvs.push( u, v ); - - } - - } - - // generate indices - - var indices = []; - var a, b, c, d; - - for ( i = 0; i < stacks; i ++ ) { - - for ( j = 0; j < slices; j ++ ) { - - a = i * sliceCount + j; - b = i * sliceCount + j + 1; - c = ( i + 1 ) * sliceCount + j + 1; - d = ( i + 1 ) * sliceCount + j; - - // faces one and two - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( new ( indices.length > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ) ); - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - // generate normals - - this.computeVertexNormals(); - - } - - ParametricBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - ParametricBufferGeometry.prototype.constructor = ParametricBufferGeometry; - - /** - * @author zz85 / https://github.com/zz85 - * - * Parametric Surfaces Geometry - * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 - */ - - function ParametricGeometry( func, slices, stacks ) { - - Geometry.call( this ); - - this.type = 'ParametricGeometry'; - - this.parameters = { - func: func, - slices: slices, - stacks: stacks - }; - - this.fromBufferGeometry( new ParametricBufferGeometry( func, slices, stacks ) ); - this.mergeVertices(); - - } - - ParametricGeometry.prototype = Object.create( Geometry.prototype ); - ParametricGeometry.prototype.constructor = ParametricGeometry; - - /** - * @author Mugen87 / https://github.com/Mugen87 - */ - - function PolyhedronBufferGeometry( vertices, indices, radius, detail ) { - - BufferGeometry.call( this ); - - this.type = 'PolyhedronBufferGeometry'; - - this.parameters = { - vertices: vertices, - indices: indices, - radius: radius, - detail: detail - }; - - radius = radius || 1; - detail = detail || 0; - - // default buffer data - - var vertexBuffer = []; - var uvBuffer = []; - - // the subdivision creates the vertex buffer data - - subdivide( detail ); - - // all vertices should lie on a conceptual sphere with a given radius - - appplyRadius( radius ); - - // finally, create the uv data - - generateUVs(); - - // build non-indexed geometry - - this.addAttribute( 'position', new Float32BufferAttribute( vertexBuffer, 3 ) ); - this.addAttribute( 'normal', new Float32BufferAttribute( vertexBuffer.slice(), 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvBuffer, 2 ) ); - this.normalizeNormals(); - - this.boundingSphere = new Sphere( new Vector3(), radius ); - - // helper functions - - function subdivide( detail ) { - - var a = new Vector3(); - var b = new Vector3(); - var c = new Vector3(); - - // iterate over all faces and apply a subdivison with the given detail value - - for ( var i = 0; i < indices.length; i += 3 ) { - - // get the vertices of the face - - getVertexByIndex( indices[ i + 0 ], a ); - getVertexByIndex( indices[ i + 1 ], b ); - getVertexByIndex( indices[ i + 2 ], c ); - - // perform subdivision - - subdivideFace( a, b, c, detail ); - - } - - } - - function subdivideFace( a, b, c, detail ) { - - var cols = Math.pow( 2, detail ); - - // we use this multidimensional array as a data structure for creating the subdivision - - var v = []; - - var i, j; - - // construct all of the vertices for this subdivision - - for ( i = 0 ; i <= cols; i ++ ) { - - v[ i ] = []; - - var aj = a.clone().lerp( c, i / cols ); - var bj = b.clone().lerp( c, i / cols ); - - var rows = cols - i; - - for ( j = 0; j <= rows; j ++ ) { - - if ( j === 0 && i === cols ) { - - v[ i ][ j ] = aj; - - } else { - - v[ i ][ j ] = aj.clone().lerp( bj, j / rows ); - - } - - } - - } - - // construct all of the faces - - for ( i = 0; i < cols ; i ++ ) { - - for ( j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { - - var k = Math.floor( j / 2 ); - - if ( j % 2 === 0 ) { - - pushVertex( v[ i ][ k + 1 ] ); - pushVertex( v[ i + 1 ][ k ] ); - pushVertex( v[ i ][ k ] ); - - } else { - - pushVertex( v[ i ][ k + 1 ] ); - pushVertex( v[ i + 1 ][ k + 1 ] ); - pushVertex( v[ i + 1 ][ k ] ); - - } - - } - - } - - } - - function appplyRadius( radius ) { - - var vertex = new Vector3(); - - // iterate over the entire buffer and apply the radius to each vertex - - for ( var i = 0; i < vertexBuffer.length; i += 3 ) { - - vertex.x = vertexBuffer[ i + 0 ]; - vertex.y = vertexBuffer[ i + 1 ]; - vertex.z = vertexBuffer[ i + 2 ]; - - vertex.normalize().multiplyScalar( radius ); - - vertexBuffer[ i + 0 ] = vertex.x; - vertexBuffer[ i + 1 ] = vertex.y; - vertexBuffer[ i + 2 ] = vertex.z; - - } - - } - - function generateUVs() { - - var vertex = new Vector3(); - - for ( var i = 0; i < vertexBuffer.length; i += 3 ) { - - vertex.x = vertexBuffer[ i + 0 ]; - vertex.y = vertexBuffer[ i + 1 ]; - vertex.z = vertexBuffer[ i + 2 ]; - - var u = azimuth( vertex ) / 2 / Math.PI + 0.5; - var v = inclination( vertex ) / Math.PI + 0.5; - uvBuffer.push( u, 1 - v ); - - } - - correctUVs(); - - correctSeam(); - - } - - function correctSeam() { - - // handle case when face straddles the seam, see #3269 - - for ( var i = 0; i < uvBuffer.length; i += 6 ) { - - // uv data of a single face - - var x0 = uvBuffer[ i + 0 ]; - var x1 = uvBuffer[ i + 2 ]; - var x2 = uvBuffer[ i + 4 ]; - - var max = Math.max( x0, x1, x2 ); - var min = Math.min( x0, x1, x2 ); - - // 0.9 is somewhat arbitrary - - if ( max > 0.9 && min < 0.1 ) { - - if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1; - if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1; - if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1; - - } - - } - - } - - function pushVertex( vertex ) { - - vertexBuffer.push( vertex.x, vertex.y, vertex.z ); - - } - - function getVertexByIndex( index, vertex ) { - - var stride = index * 3; - - vertex.x = vertices[ stride + 0 ]; - vertex.y = vertices[ stride + 1 ]; - vertex.z = vertices[ stride + 2 ]; - - } - - function correctUVs() { - - var a = new Vector3(); - var b = new Vector3(); - var c = new Vector3(); - - var centroid = new Vector3(); - - var uvA = new Vector2(); - var uvB = new Vector2(); - var uvC = new Vector2(); - - for ( var i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) { - - a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] ); - b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] ); - c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] ); - - uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] ); - uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] ); - uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] ); - - centroid.copy( a ).add( b ).add( c ).divideScalar( 3 ); - - var azi = azimuth( centroid ); - - correctUV( uvA, j + 0, a, azi ); - correctUV( uvB, j + 2, b, azi ); - correctUV( uvC, j + 4, c, azi ); - - } - - } - - function correctUV( uv, stride, vector, azimuth ) { - - if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) { - - uvBuffer[ stride ] = uv.x - 1; - - } - - if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) { - - uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5; - - } - - } - - // Angle around the Y axis, counter-clockwise when looking from above. - - function azimuth( vector ) { - - return Math.atan2( vector.z, - vector.x ); - - } - - - // Angle above the XZ plane. - - function inclination( vector ) { - - return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); - - } - - } - - PolyhedronBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - PolyhedronBufferGeometry.prototype.constructor = PolyhedronBufferGeometry; - - /** - * @author Mugen87 / https://github.com/Mugen87 - */ - - function TetrahedronBufferGeometry( radius, detail ) { - - var vertices = [ - 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 - ]; - - var indices = [ - 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 - ]; - - PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); - - this.type = 'TetrahedronBufferGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - } - - TetrahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); - TetrahedronBufferGeometry.prototype.constructor = TetrahedronBufferGeometry; - - /** - * @author timothypratley / https://github.com/timothypratley - */ - - function TetrahedronGeometry( radius, detail ) { - - Geometry.call( this ); - - this.type = 'TetrahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new TetrahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } - - TetrahedronGeometry.prototype = Object.create( Geometry.prototype ); - TetrahedronGeometry.prototype.constructor = TetrahedronGeometry; - - /** - * @author Mugen87 / https://github.com/Mugen87 - */ - - function OctahedronBufferGeometry( radius,detail ) { - - var vertices = [ - 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1 - ]; - - var indices = [ - 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2 - ]; - - PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); - - this.type = 'OctahedronBufferGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - } - - OctahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); - OctahedronBufferGeometry.prototype.constructor = OctahedronBufferGeometry; - - /** - * @author timothypratley / https://github.com/timothypratley - */ - - function OctahedronGeometry( radius, detail ) { - - Geometry.call( this ); - - this.type = 'OctahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new OctahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } - - OctahedronGeometry.prototype = Object.create( Geometry.prototype ); - OctahedronGeometry.prototype.constructor = OctahedronGeometry; - - /** - * @author Mugen87 / https://github.com/Mugen87 - */ - - function IcosahedronBufferGeometry( radius, detail ) { - - var t = ( 1 + Math.sqrt( 5 ) ) / 2; - - var vertices = [ - - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, - 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, - t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 - ]; - - var indices = [ - 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, - 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, - 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, - 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 - ]; - - PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); - - this.type = 'IcosahedronBufferGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - } - - IcosahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); - IcosahedronBufferGeometry.prototype.constructor = IcosahedronBufferGeometry; - - /** - * @author timothypratley / https://github.com/timothypratley - */ - - function IcosahedronGeometry( radius, detail ) { - - Geometry.call( this ); - - this.type = 'IcosahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new IcosahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } - - IcosahedronGeometry.prototype = Object.create( Geometry.prototype ); - IcosahedronGeometry.prototype.constructor = IcosahedronGeometry; - - /** - * @author Mugen87 / https://github.com/Mugen87 - */ - - function DodecahedronBufferGeometry( radius, detail ) { - - var t = ( 1 + Math.sqrt( 5 ) ) / 2; - var r = 1 / t; - - var vertices = [ - - // (±1, ±1, ±1) - - 1, - 1, - 1, - 1, - 1, 1, - - 1, 1, - 1, - 1, 1, 1, - 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, 1, 1, 1, - - // (0, ±1/φ, ±φ) - 0, - r, - t, 0, - r, t, - 0, r, - t, 0, r, t, - - // (±1/φ, ±φ, 0) - - r, - t, 0, - r, t, 0, - r, - t, 0, r, t, 0, - - // (±φ, 0, ±1/φ) - - t, 0, - r, t, 0, - r, - - t, 0, r, t, 0, r - ]; - - var indices = [ - 3, 11, 7, 3, 7, 15, 3, 15, 13, - 7, 19, 17, 7, 17, 6, 7, 6, 15, - 17, 4, 8, 17, 8, 10, 17, 10, 6, - 8, 0, 16, 8, 16, 2, 8, 2, 10, - 0, 12, 1, 0, 1, 18, 0, 18, 16, - 6, 10, 2, 6, 2, 13, 6, 13, 15, - 2, 16, 18, 2, 18, 3, 2, 3, 13, - 18, 1, 9, 18, 9, 11, 18, 11, 3, - 4, 14, 12, 4, 12, 0, 4, 0, 8, - 11, 9, 5, 11, 5, 19, 11, 19, 7, - 19, 5, 14, 19, 14, 4, 19, 4, 17, - 1, 12, 14, 1, 14, 5, 1, 5, 9 - ]; - - PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); - - this.type = 'DodecahedronBufferGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - } - - DodecahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); - DodecahedronBufferGeometry.prototype.constructor = DodecahedronBufferGeometry; - - /** - * @author Abe Pazos / https://hamoid.com - */ - - function DodecahedronGeometry( radius, detail ) { - - Geometry.call( this ); - - this.type = 'DodecahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new DodecahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } - - DodecahedronGeometry.prototype = Object.create( Geometry.prototype ); - DodecahedronGeometry.prototype.constructor = DodecahedronGeometry; - - /** - * @author clockworkgeek / https://github.com/clockworkgeek - * @author timothypratley / https://github.com/timothypratley - * @author WestLangley / http://github.com/WestLangley - */ - - function PolyhedronGeometry( vertices, indices, radius, detail ) { - - Geometry.call( this ); - - this.type = 'PolyhedronGeometry'; - - this.parameters = { - vertices: vertices, - indices: indices, - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new PolyhedronBufferGeometry( vertices, indices, radius, detail ) ); - this.mergeVertices(); - - } - - PolyhedronGeometry.prototype = Object.create( Geometry.prototype ); - PolyhedronGeometry.prototype.constructor = PolyhedronGeometry; - - /** - * @author Mugen87 / https://github.com/Mugen87 - * - * Creates a tube which extrudes along a 3d spline. - * - */ - - function TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ) { - - BufferGeometry.call( this ); - - this.type = 'TubeBufferGeometry'; - - this.parameters = { - path: path, - tubularSegments: tubularSegments, - radius: radius, - radialSegments: radialSegments, - closed: closed - }; - - tubularSegments = tubularSegments || 64; - radius = radius || 1; - radialSegments = radialSegments || 8; - closed = closed || false; - - var frames = path.computeFrenetFrames( tubularSegments, closed ); - - // expose internals - - this.tangents = frames.tangents; - this.normals = frames.normals; - this.binormals = frames.binormals; - - // helper variables - - var vertex = new Vector3(); - var normal = new Vector3(); - var uv = new Vector2(); - - var i, j; - - // buffer - - var vertices = []; - var normals = []; - var uvs = []; - var indices = []; - - // create buffer data - - generateBufferData(); - - // build geometry - - this.setIndex( new ( indices.length > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ) ); - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - // functions - - function generateBufferData() { - - for ( i = 0; i < tubularSegments; i ++ ) { - - generateSegment( i ); - - } - - // if the geometry is not closed, generate the last row of vertices and normals - // at the regular position on the given path - // - // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) - - generateSegment( ( closed === false ) ? tubularSegments : 0 ); - - // uvs are generated in a separate function. - // this makes it easy compute correct values for closed geometries - - generateUVs(); - - // finally create faces - - generateIndices(); - - } - - function generateSegment( i ) { - - // we use getPointAt to sample evenly distributed points from the given path - - var P = path.getPointAt( i / tubularSegments ); - - // retrieve corresponding normal and binormal - - var N = frames.normals[ i ]; - var B = frames.binormals[ i ]; - - // generate normals and vertices for the current segment - - for ( j = 0; j <= radialSegments; j ++ ) { - - var v = j / radialSegments * Math.PI * 2; - - var sin = Math.sin( v ); - var cos = - Math.cos( v ); - - // normal - - normal.x = ( cos * N.x + sin * B.x ); - normal.y = ( cos * N.y + sin * B.y ); - normal.z = ( cos * N.z + sin * B.z ); - normal.normalize(); - - normals.push( normal.x, normal.y, normal.z ); - - // vertex - - vertex.x = P.x + radius * normal.x; - vertex.y = P.y + radius * normal.y; - vertex.z = P.z + radius * normal.z; - - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - } - - function generateIndices() { - - for ( j = 1; j <= tubularSegments; j ++ ) { - - for ( i = 1; i <= radialSegments; i ++ ) { - - var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); - var b = ( radialSegments + 1 ) * j + ( i - 1 ); - var c = ( radialSegments + 1 ) * j + i; - var d = ( radialSegments + 1 ) * ( j - 1 ) + i; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - } - - function generateUVs() { - - for ( i = 0; i <= tubularSegments; i ++ ) { - - for ( j = 0; j <= radialSegments; j ++ ) { - - uv.x = i / tubularSegments; - uv.y = j / radialSegments; - - uvs.push( uv.x, uv.y ); - - } - - } - - } - - } - - TubeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - TubeBufferGeometry.prototype.constructor = TubeBufferGeometry; - - /** - * @author oosmoxiecode / https://github.com/oosmoxiecode - * @author WestLangley / https://github.com/WestLangley - * @author zz85 / https://github.com/zz85 - * @author miningold / https://github.com/miningold - * @author jonobr1 / https://github.com/jonobr1 - * - * Creates a tube which extrudes along a 3d spline. - */ - - function TubeGeometry( path, tubularSegments, radius, radialSegments, closed, taper ) { - - Geometry.call( this ); - - this.type = 'TubeGeometry'; - - this.parameters = { - path: path, - tubularSegments: tubularSegments, - radius: radius, - radialSegments: radialSegments, - closed: closed - }; - - if ( taper !== undefined ) console.warn( 'THREE.TubeGeometry: taper has been removed.' ); - - var bufferGeometry = new TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ); - - // expose internals - - this.tangents = bufferGeometry.tangents; - this.normals = bufferGeometry.normals; - this.binormals = bufferGeometry.binormals; - - // create geometry - - this.fromBufferGeometry( bufferGeometry ); - this.mergeVertices(); - - } - - TubeGeometry.prototype = Object.create( Geometry.prototype ); - TubeGeometry.prototype.constructor = TubeGeometry; - - /** - * @author Mugen87 / https://github.com/Mugen87 - * - * see: http://www.blackpawn.com/texts/pqtorus/ - */ - function TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) { - - BufferGeometry.call( this ); - - this.type = 'TorusKnotBufferGeometry'; - - this.parameters = { - radius: radius, - tube: tube, - tubularSegments: tubularSegments, - radialSegments: radialSegments, - p: p, - q: q - }; - - radius = radius || 100; - tube = tube || 40; - tubularSegments = Math.floor( tubularSegments ) || 64; - radialSegments = Math.floor( radialSegments ) || 8; - p = p || 2; - q = q || 3; - - // used to calculate buffer length - var vertexCount = ( ( radialSegments + 1 ) * ( tubularSegments + 1 ) ); - var indexCount = radialSegments * tubularSegments * 2 * 3; - - // buffers - var indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 ); - var vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); - - // helper variables - var i, j, index = 0, indexOffset = 0; - - var vertex = new Vector3(); - var normal = new Vector3(); - var uv = new Vector2(); - - var P1 = new Vector3(); - var P2 = new Vector3(); - - var B = new Vector3(); - var T = new Vector3(); - var N = new Vector3(); - - // generate vertices, normals and uvs - - for ( i = 0; i <= tubularSegments; ++ i ) { - - // the radian "u" is used to calculate the position on the torus curve of the current tubular segement - - var u = i / tubularSegments * p * Math.PI * 2; - - // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. - // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions - - calculatePositionOnCurve( u, p, q, radius, P1 ); - calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); - - // calculate orthonormal basis - - T.subVectors( P2, P1 ); - N.addVectors( P2, P1 ); - B.crossVectors( T, N ); - N.crossVectors( B, T ); - - // normalize B, N. T can be ignored, we don't use it - - B.normalize(); - N.normalize(); - - for ( j = 0; j <= radialSegments; ++ j ) { - - // now calculate the vertices. they are nothing more than an extrusion of the torus curve. - // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. - - var v = j / radialSegments * Math.PI * 2; - var cx = - tube * Math.cos( v ); - var cy = tube * Math.sin( v ); - - // now calculate the final vertex position. - // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve - - vertex.x = P1.x + ( cx * N.x + cy * B.x ); - vertex.y = P1.y + ( cx * N.y + cy * B.y ); - vertex.z = P1.z + ( cx * N.z + cy * B.z ); - - // vertex - vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); - - // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) - normal.subVectors( vertex, P1 ).normalize(); - normals.setXYZ( index, normal.x, normal.y, normal.z ); - - // uv - uv.x = i / tubularSegments; - uv.y = j / radialSegments; - uvs.setXY( index, uv.x, uv.y ); - - // increase index - index ++; - - } - - } - - // generate indices - - for ( j = 1; j <= tubularSegments; j ++ ) { - - for ( i = 1; i <= radialSegments; i ++ ) { - - // indices - var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); - var b = ( radialSegments + 1 ) * j + ( i - 1 ); - var c = ( radialSegments + 1 ) * j + i; - var d = ( radialSegments + 1 ) * ( j - 1 ) + i; - - // face one - indices.setX( indexOffset, a ); indexOffset++; - indices.setX( indexOffset, b ); indexOffset++; - indices.setX( indexOffset, d ); indexOffset++; - - // face two - indices.setX( indexOffset, b ); indexOffset++; - indices.setX( indexOffset, c ); indexOffset++; - indices.setX( indexOffset, d ); indexOffset++; - - } - - } - - // build geometry - - this.setIndex( indices ); - this.addAttribute( 'position', vertices ); - this.addAttribute( 'normal', normals ); - this.addAttribute( 'uv', uvs ); - - // this function calculates the current position on the torus curve - - function calculatePositionOnCurve( u, p, q, radius, position ) { - - var cu = Math.cos( u ); - var su = Math.sin( u ); - var quOverP = q / p * u; - var cs = Math.cos( quOverP ); - - position.x = radius * ( 2 + cs ) * 0.5 * cu; - position.y = radius * ( 2 + cs ) * su * 0.5; - position.z = radius * Math.sin( quOverP ) * 0.5; - - } - - } - - TorusKnotBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - TorusKnotBufferGeometry.prototype.constructor = TorusKnotBufferGeometry; - - /** - * @author oosmoxiecode - */ - - function TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) { - - Geometry.call( this ); - - this.type = 'TorusKnotGeometry'; - - this.parameters = { - radius: radius, - tube: tube, - tubularSegments: tubularSegments, - radialSegments: radialSegments, - p: p, - q: q - }; - - if( heightScale !== undefined ) console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' ); - - this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) ); - this.mergeVertices(); - - } - - TorusKnotGeometry.prototype = Object.create( Geometry.prototype ); - TorusKnotGeometry.prototype.constructor = TorusKnotGeometry; - - /** - * @author Mugen87 / https://github.com/Mugen87 - */ - - function TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) { - - BufferGeometry.call( this ); - - this.type = 'TorusBufferGeometry'; - - this.parameters = { - radius: radius, - tube: tube, - radialSegments: radialSegments, - tubularSegments: tubularSegments, - arc: arc - }; - - radius = radius || 100; - tube = tube || 40; - radialSegments = Math.floor( radialSegments ) || 8; - tubularSegments = Math.floor( tubularSegments ) || 6; - arc = arc || Math.PI * 2; - - // used to calculate buffer length - var vertexCount = ( ( radialSegments + 1 ) * ( tubularSegments + 1 ) ); - var indexCount = radialSegments * tubularSegments * 2 * 3; - - // buffers - var indices = new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ); - var vertices = new Float32Array( vertexCount * 3 ); - var normals = new Float32Array( vertexCount * 3 ); - var uvs = new Float32Array( vertexCount * 2 ); - - // offset variables - var vertexBufferOffset = 0; - var uvBufferOffset = 0; - var indexBufferOffset = 0; - - // helper variables - var center = new Vector3(); - var vertex = new Vector3(); - var normal = new Vector3(); - - var j, i; - - // generate vertices, normals and uvs - - for ( j = 0; j <= radialSegments; j ++ ) { - - for ( i = 0; i <= tubularSegments; i ++ ) { - - var u = i / tubularSegments * arc; - var v = j / radialSegments * Math.PI * 2; - - // vertex - vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); - vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); - vertex.z = tube * Math.sin( v ); - - vertices[ vertexBufferOffset ] = vertex.x; - vertices[ vertexBufferOffset + 1 ] = vertex.y; - vertices[ vertexBufferOffset + 2 ] = vertex.z; - - // this vector is used to calculate the normal - center.x = radius * Math.cos( u ); - center.y = radius * Math.sin( u ); - - // normal - normal.subVectors( vertex, center ).normalize(); - - normals[ vertexBufferOffset ] = normal.x; - normals[ vertexBufferOffset + 1 ] = normal.y; - normals[ vertexBufferOffset + 2 ] = normal.z; - - // uv - uvs[ uvBufferOffset ] = i / tubularSegments; - uvs[ uvBufferOffset + 1 ] = j / radialSegments; - - // update offsets - vertexBufferOffset += 3; - uvBufferOffset += 2; - - } - - } - - // generate indices - - for ( j = 1; j <= radialSegments; j ++ ) { - - for ( i = 1; i <= tubularSegments; i ++ ) { - - // indices - var a = ( tubularSegments + 1 ) * j + i - 1; - var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; - var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; - var d = ( tubularSegments + 1 ) * j + i; - - // face one - indices[ indexBufferOffset ] = a; - indices[ indexBufferOffset + 1 ] = b; - indices[ indexBufferOffset + 2 ] = d; - - // face two - indices[ indexBufferOffset + 3 ] = b; - indices[ indexBufferOffset + 4 ] = c; - indices[ indexBufferOffset + 5 ] = d; - - // update offset - indexBufferOffset += 6; - - } - - } - - // build geometry - this.setIndex( new BufferAttribute( indices, 1 ) ); - this.addAttribute( 'position', new BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) ); - - } - - TorusBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - TorusBufferGeometry.prototype.constructor = TorusBufferGeometry; - - /** - * @author oosmoxiecode - * @author mrdoob / http://mrdoob.com/ - * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888 - */ - - function TorusGeometry( radius, tube, radialSegments, tubularSegments, arc ) { - - Geometry.call( this ); - - this.type = 'TorusGeometry'; - - this.parameters = { - radius: radius, - tube: tube, - radialSegments: radialSegments, - tubularSegments: tubularSegments, - arc: arc - }; - - this.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) ); - - } - - TorusGeometry.prototype = Object.create( Geometry.prototype ); - TorusGeometry.prototype.constructor = TorusGeometry; - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - */ - - var ShapeUtils = { - - // calculate area of the contour polygon - - area: function ( contour ) { - - var n = contour.length; - var a = 0.0; - - for ( var p = n - 1, q = 0; q < n; p = q ++ ) { - - a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; - - } - - return a * 0.5; - - }, - - triangulate: ( function () { - - /** - * This code is a quick port of code written in C++ which was submitted to - * flipcode.com by John W. Ratcliff // July 22, 2000 - * See original code and more information here: - * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml - * - * ported to actionscript by Zevan Rosser - * www.actionsnippet.com - * - * ported to javascript by Joshua Koo - * http://www.lab4games.net/zz85/blog - * - */ - - function snip( contour, u, v, w, n, verts ) { - - var p; - var ax, ay, bx, by; - var cx, cy, px, py; - - ax = contour[ verts[ u ] ].x; - ay = contour[ verts[ u ] ].y; - - bx = contour[ verts[ v ] ].x; - by = contour[ verts[ v ] ].y; - - cx = contour[ verts[ w ] ].x; - cy = contour[ verts[ w ] ].y; - - if ( ( bx - ax ) * ( cy - ay ) - ( by - ay ) * ( cx - ax ) <= 0 ) return false; - - var aX, aY, bX, bY, cX, cY; - var apx, apy, bpx, bpy, cpx, cpy; - var cCROSSap, bCROSScp, aCROSSbp; - - aX = cx - bx; aY = cy - by; - bX = ax - cx; bY = ay - cy; - cX = bx - ax; cY = by - ay; - - for ( p = 0; p < n; p ++ ) { - - px = contour[ verts[ p ] ].x; - py = contour[ verts[ p ] ].y; - - if ( ( ( px === ax ) && ( py === ay ) ) || - ( ( px === bx ) && ( py === by ) ) || - ( ( px === cx ) && ( py === cy ) ) ) continue; - - apx = px - ax; apy = py - ay; - bpx = px - bx; bpy = py - by; - cpx = px - cx; cpy = py - cy; - - // see if p is inside triangle abc - - aCROSSbp = aX * bpy - aY * bpx; - cCROSSap = cX * apy - cY * apx; - bCROSScp = bX * cpy - bY * cpx; - - if ( ( aCROSSbp >= - Number.EPSILON ) && ( bCROSScp >= - Number.EPSILON ) && ( cCROSSap >= - Number.EPSILON ) ) return false; - - } - - return true; - - } - - // takes in an contour array and returns - - return function triangulate( contour, indices ) { - - var n = contour.length; - - if ( n < 3 ) return null; - - var result = [], - verts = [], - vertIndices = []; - - /* we want a counter-clockwise polygon in verts */ - - var u, v, w; - - if ( ShapeUtils.area( contour ) > 0.0 ) { - - for ( v = 0; v < n; v ++ ) verts[ v ] = v; - - } else { - - for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v; - - } - - var nv = n; - - /* remove nv - 2 vertices, creating 1 triangle every time */ - - var count = 2 * nv; /* error detection */ - - for ( v = nv - 1; nv > 2; ) { - - /* if we loop, it is probably a non-simple polygon */ - - if ( ( count -- ) <= 0 ) { - - //** Triangulate: ERROR - probable bad polygon! - - //throw ( "Warning, unable to triangulate polygon!" ); - //return null; - // Sometimes warning is fine, especially polygons are triangulated in reverse. - console.warn( 'THREE.ShapeUtils: Unable to triangulate polygon! in triangulate()' ); - - if ( indices ) return vertIndices; - return result; - - } - - /* three consecutive vertices in current polygon, */ - - u = v; if ( nv <= u ) u = 0; /* previous */ - v = u + 1; if ( nv <= v ) v = 0; /* new v */ - w = v + 1; if ( nv <= w ) w = 0; /* next */ - - if ( snip( contour, u, v, w, nv, verts ) ) { - - var a, b, c, s, t; - - /* true names of the vertices */ - - a = verts[ u ]; - b = verts[ v ]; - c = verts[ w ]; - - /* output Triangle */ - - result.push( [ contour[ a ], - contour[ b ], - contour[ c ] ] ); - - - vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); - - /* remove v from the remaining polygon */ - - for ( s = v, t = v + 1; t < nv; s ++, t ++ ) { - - verts[ s ] = verts[ t ]; - - } - - nv --; - - /* reset error detection counter */ - - count = 2 * nv; - - } - - } - - if ( indices ) return vertIndices; - return result; - - } - - } )(), - - triangulateShape: function ( contour, holes ) { - - function removeDupEndPts(points) { - - var l = points.length; - - if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { - - points.pop(); - - } - - } - - removeDupEndPts( contour ); - holes.forEach( removeDupEndPts ); - - function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) { - - // inOtherPt needs to be collinear to the inSegment - if ( inSegPt1.x !== inSegPt2.x ) { - - if ( inSegPt1.x < inSegPt2.x ) { - - return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) ); - - } else { - - return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) ); - - } - - } else { - - if ( inSegPt1.y < inSegPt2.y ) { - - return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) ); - - } else { - - return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) ); - - } - - } - - } - - function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) { - - var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y; - var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y; - - var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x; - var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y; - - var limit = seg1dy * seg2dx - seg1dx * seg2dy; - var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy; - - if ( Math.abs( limit ) > Number.EPSILON ) { - - // not parallel - - var perpSeg2; - if ( limit > 0 ) { - - if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return []; - perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; - if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return []; - - } else { - - if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return []; - perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; - if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return []; - - } - - // i.e. to reduce rounding errors - // intersection at endpoint of segment#1? - if ( perpSeg2 === 0 ) { - - if ( ( inExcludeAdjacentSegs ) && - ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; - return [ inSeg1Pt1 ]; - - } - if ( perpSeg2 === limit ) { - - if ( ( inExcludeAdjacentSegs ) && - ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; - return [ inSeg1Pt2 ]; - - } - // intersection at endpoint of segment#2? - if ( perpSeg1 === 0 ) return [ inSeg2Pt1 ]; - if ( perpSeg1 === limit ) return [ inSeg2Pt2 ]; - - // return real intersection point - var factorSeg1 = perpSeg2 / limit; - return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx, - y: inSeg1Pt1.y + factorSeg1 * seg1dy } ]; - - } else { - - // parallel or collinear - if ( ( perpSeg1 !== 0 ) || - ( seg2dy * seg1seg2dx !== seg2dx * seg1seg2dy ) ) return []; - - // they are collinear or degenerate - var seg1Pt = ( ( seg1dx === 0 ) && ( seg1dy === 0 ) ); // segment1 is just a point? - var seg2Pt = ( ( seg2dx === 0 ) && ( seg2dy === 0 ) ); // segment2 is just a point? - // both segments are points - if ( seg1Pt && seg2Pt ) { - - if ( ( inSeg1Pt1.x !== inSeg2Pt1.x ) || - ( inSeg1Pt1.y !== inSeg2Pt1.y ) ) return []; // they are distinct points - return [ inSeg1Pt1 ]; // they are the same point - - } - // segment#1 is a single point - if ( seg1Pt ) { - - if ( ! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2 - return [ inSeg1Pt1 ]; - - } - // segment#2 is a single point - if ( seg2Pt ) { - - if ( ! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1 - return [ inSeg2Pt1 ]; - - } - - // they are collinear segments, which might overlap - var seg1min, seg1max, seg1minVal, seg1maxVal; - var seg2min, seg2max, seg2minVal, seg2maxVal; - if ( seg1dx !== 0 ) { - - // the segments are NOT on a vertical line - if ( inSeg1Pt1.x < inSeg1Pt2.x ) { - - seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x; - seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x; - - } else { - - seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x; - seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x; - - } - if ( inSeg2Pt1.x < inSeg2Pt2.x ) { - - seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x; - seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x; - - } else { - - seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x; - seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x; - - } - - } else { - - // the segments are on a vertical line - if ( inSeg1Pt1.y < inSeg1Pt2.y ) { - - seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y; - seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y; - - } else { - - seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y; - seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y; - - } - if ( inSeg2Pt1.y < inSeg2Pt2.y ) { - - seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y; - seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y; - - } else { - - seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y; - seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y; - - } - - } - if ( seg1minVal <= seg2minVal ) { - - if ( seg1maxVal < seg2minVal ) return []; - if ( seg1maxVal === seg2minVal ) { - - if ( inExcludeAdjacentSegs ) return []; - return [ seg2min ]; - - } - if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ]; - return [ seg2min, seg2max ]; - - } else { - - if ( seg1minVal > seg2maxVal ) return []; - if ( seg1minVal === seg2maxVal ) { - - if ( inExcludeAdjacentSegs ) return []; - return [ seg1min ]; - - } - if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ]; - return [ seg1min, seg2max ]; - - } - - } - - } - - function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) { - - // The order of legs is important - - // translation of all points, so that Vertex is at (0,0) - var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y; - var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y; - var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y; - - // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg. - var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX; - var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX; - - if ( Math.abs( from2toAngle ) > Number.EPSILON ) { - - // angle != 180 deg. - - var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX; - // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle ); - - if ( from2toAngle > 0 ) { - - // main angle < 180 deg. - return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) ); - - } else { - - // main angle > 180 deg. - return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) ); - - } - - } else { - - // angle == 180 deg. - // console.log( "from2to: 180 deg., from2other: " + from2otherAngle ); - return ( from2otherAngle > 0 ); - - } - - } - - - function removeHoles( contour, holes ) { - - var shape = contour.concat(); // work on this shape - var hole; - - function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) { - - // Check if hole point lies within angle around shape point - var lastShapeIdx = shape.length - 1; - - var prevShapeIdx = inShapeIdx - 1; - if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx; - - var nextShapeIdx = inShapeIdx + 1; - if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0; - - var insideAngle = isPointInsideAngle( shape[ inShapeIdx ], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[ inHoleIdx ] ); - if ( ! insideAngle ) { - - // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y ); - return false; - - } - - // Check if shape point lies within angle around hole point - var lastHoleIdx = hole.length - 1; - - var prevHoleIdx = inHoleIdx - 1; - if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx; - - var nextHoleIdx = inHoleIdx + 1; - if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0; - - insideAngle = isPointInsideAngle( hole[ inHoleIdx ], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[ inShapeIdx ] ); - if ( ! insideAngle ) { - - // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y ); - return false; - - } - - return true; - - } - - function intersectsShapeEdge( inShapePt, inHolePt ) { - - // checks for intersections with shape edges - var sIdx, nextIdx, intersection; - for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) { - - nextIdx = sIdx + 1; nextIdx %= shape.length; - intersection = intersect_segments_2D( inShapePt, inHolePt, shape[ sIdx ], shape[ nextIdx ], true ); - if ( intersection.length > 0 ) return true; - - } - - return false; - - } - - var indepHoles = []; - - function intersectsHoleEdge( inShapePt, inHolePt ) { - - // checks for intersections with hole edges - var ihIdx, chkHole, - hIdx, nextIdx, intersection; - for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) { - - chkHole = holes[ indepHoles[ ihIdx ]]; - for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) { - - nextIdx = hIdx + 1; nextIdx %= chkHole.length; - intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[ hIdx ], chkHole[ nextIdx ], true ); - if ( intersection.length > 0 ) return true; - - } - - } - return false; - - } - - var holeIndex, shapeIndex, - shapePt, holePt, - holeIdx, cutKey, failedCuts = [], - tmpShape1, tmpShape2, - tmpHole1, tmpHole2; - - for ( var h = 0, hl = holes.length; h < hl; h ++ ) { - - indepHoles.push( h ); - - } - - var minShapeIndex = 0; - var counter = indepHoles.length * 2; - while ( indepHoles.length > 0 ) { - - counter --; - if ( counter < 0 ) { - - console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" ); - break; - - } - - // search for shape-vertex and hole-vertex, - // which can be connected without intersections - for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) { - - shapePt = shape[ shapeIndex ]; - holeIndex = - 1; - - // search for hole which can be reached without intersections - for ( var h = 0; h < indepHoles.length; h ++ ) { - - holeIdx = indepHoles[ h ]; - - // prevent multiple checks - cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx; - if ( failedCuts[ cutKey ] !== undefined ) continue; - - hole = holes[ holeIdx ]; - for ( var h2 = 0; h2 < hole.length; h2 ++ ) { - - holePt = hole[ h2 ]; - if ( ! isCutLineInsideAngles( shapeIndex, h2 ) ) continue; - if ( intersectsShapeEdge( shapePt, holePt ) ) continue; - if ( intersectsHoleEdge( shapePt, holePt ) ) continue; - - holeIndex = h2; - indepHoles.splice( h, 1 ); - - tmpShape1 = shape.slice( 0, shapeIndex + 1 ); - tmpShape2 = shape.slice( shapeIndex ); - tmpHole1 = hole.slice( holeIndex ); - tmpHole2 = hole.slice( 0, holeIndex + 1 ); - - shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); - - minShapeIndex = shapeIndex; - - // Debug only, to show the selected cuts - // glob_CutLines.push( [ shapePt, holePt ] ); - - break; - - } - if ( holeIndex >= 0 ) break; // hole-vertex found - - failedCuts[ cutKey ] = true; // remember failure - - } - if ( holeIndex >= 0 ) break; // hole-vertex found - - } - - } - - return shape; /* shape with no holes */ - - } - - - var i, il, f, face, - key, index, - allPointsMap = {}; - - // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first. - - var allpoints = contour.concat(); - - for ( var h = 0, hl = holes.length; h < hl; h ++ ) { - - Array.prototype.push.apply( allpoints, holes[ h ] ); - - } - - //console.log( "allpoints",allpoints, allpoints.length ); - - // prepare all points map - - for ( i = 0, il = allpoints.length; i < il; i ++ ) { - - key = allpoints[ i ].x + ":" + allpoints[ i ].y; - - if ( allPointsMap[ key ] !== undefined ) { - - console.warn( "THREE.ShapeUtils: Duplicate point", key, i ); - - } - - allPointsMap[ key ] = i; - - } - - // remove holes by cutting paths to holes and adding them to the shape - var shapeWithoutHoles = removeHoles( contour, holes ); - - var triangles = ShapeUtils.triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape - //console.log( "triangles",triangles, triangles.length ); - - // check all face vertices against all points map - - for ( i = 0, il = triangles.length; i < il; i ++ ) { - - face = triangles[ i ]; - - for ( f = 0; f < 3; f ++ ) { - - key = face[ f ].x + ":" + face[ f ].y; - - index = allPointsMap[ key ]; - - if ( index !== undefined ) { - - face[ f ] = index; - - } - - } - - } - - return triangles.concat(); - - }, - - isClockWise: function ( pts ) { - - return ShapeUtils.area( pts ) < 0; - - }, - - // Bezier Curves formulas obtained from - // http://en.wikipedia.org/wiki/B%C3%A9zier_curve - - // Quad Bezier Functions - - b2: ( function () { - - function b2p0( t, p ) { - - var k = 1 - t; - return k * k * p; - - } - - function b2p1( t, p ) { - - return 2 * ( 1 - t ) * t * p; - - } - - function b2p2( t, p ) { - - return t * t * p; - - } - - return function b2( t, p0, p1, p2 ) { - - return b2p0( t, p0 ) + b2p1( t, p1 ) + b2p2( t, p2 ); - - }; - - } )(), - - // Cubic Bezier Functions - - b3: ( function () { - - function b3p0( t, p ) { - - var k = 1 - t; - return k * k * k * p; - - } - - function b3p1( t, p ) { - - var k = 1 - t; - return 3 * k * k * t * p; - - } - - function b3p2( t, p ) { - - var k = 1 - t; - return 3 * k * t * t * p; - - } - - function b3p3( t, p ) { - - return t * t * t * p; - - } - - return function b3( t, p0, p1, p2, p3 ) { - - return b3p0( t, p0 ) + b3p1( t, p1 ) + b3p2( t, p2 ) + b3p3( t, p3 ); - - }; - - } )() - - }; - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * - * Creates extruded geometry from a path shape. - * - * parameters = { - * - * curveSegments: , // number of points on the curves - * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too - * amount: , // Depth to extrude the shape - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into the original shape bevel goes - * bevelSize: , // how far from shape outline is bevel - * bevelSegments: , // number of bevel layers - * - * extrudePath: // curve to extrude shape along - * frames: // containing arrays of tangents, normals, binormals - * - * uvGenerator: // object that provides UV generator functions - * - * } - **/ - - function ExtrudeGeometry( shapes, options ) { - - if ( typeof( shapes ) === "undefined" ) { - - shapes = []; - return; - - } - - Geometry.call( this ); - - this.type = 'ExtrudeGeometry'; - - shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; - - this.addShapeList( shapes, options ); - - this.computeFaceNormals(); - - // can't really use automatic vertex normals - // as then front and back sides get smoothed too - // should do separate smoothing just for sides - - //this.computeVertexNormals(); - - //console.log( "took", ( Date.now() - startTime ) ); - - } - - ExtrudeGeometry.prototype = Object.create( Geometry.prototype ); - ExtrudeGeometry.prototype.constructor = ExtrudeGeometry; - - ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) { - - var sl = shapes.length; - - for ( var s = 0; s < sl; s ++ ) { - - var shape = shapes[ s ]; - this.addShape( shape, options ); - - } - - }; - - ExtrudeGeometry.prototype.addShape = function ( shape, options ) { - - var amount = options.amount !== undefined ? options.amount : 100; - - var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10 - var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8 - var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; - - var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false - - var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; - - var steps = options.steps !== undefined ? options.steps : 1; - - var extrudePath = options.extrudePath; - var extrudePts, extrudeByPath = false; - - // Use default WorldUVGenerator if no UV generators are specified. - var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : ExtrudeGeometry.WorldUVGenerator; - - var splineTube, binormal, normal, position2; - if ( extrudePath ) { - - extrudePts = extrudePath.getSpacedPoints( steps ); - - extrudeByPath = true; - bevelEnabled = false; // bevels not supported for path extrusion - - // SETUP TNB variables - - // TODO1 - have a .isClosed in spline? - - splineTube = options.frames !== undefined ? options.frames : extrudePath.computeFrenetFrames( steps, false ); - - // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); - - binormal = new Vector3(); - normal = new Vector3(); - position2 = new Vector3(); - - } - - // Safeguards if bevels are not enabled - - if ( ! bevelEnabled ) { - - bevelSegments = 0; - bevelThickness = 0; - bevelSize = 0; - - } - - // Variables initialization - - var ahole, h, hl; // looping of holes - var scope = this; - - var shapesOffset = this.vertices.length; - - var shapePoints = shape.extractPoints( curveSegments ); - - var vertices = shapePoints.shape; - var holes = shapePoints.holes; - - var reverse = ! ShapeUtils.isClockWise( vertices ); - - if ( reverse ) { - - vertices = vertices.reverse(); - - // Maybe we should also check if holes are in the opposite direction, just to be safe ... - - for ( h = 0, hl = holes.length; h < hl; h ++ ) { - - ahole = holes[ h ]; - - if ( ShapeUtils.isClockWise( ahole ) ) { - - holes[ h ] = ahole.reverse(); - - } - - } - - reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! - - } - - - var faces = ShapeUtils.triangulateShape( vertices, holes ); - - /* Vertices */ - - var contour = vertices; // vertices has all points but contour has only points of circumference - - for ( h = 0, hl = holes.length; h < hl; h ++ ) { - - ahole = holes[ h ]; - - vertices = vertices.concat( ahole ); - - } - - - function scalePt2( pt, vec, size ) { - - if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); - - return vec.clone().multiplyScalar( size ).add( pt ); - - } - - var b, bs, t, z, - vert, vlen = vertices.length, - face, flen = faces.length; - - - // Find directions for point movement - - - function getBevelVec( inPt, inPrev, inNext ) { - - // computes for inPt the corresponding point inPt' on a new contour - // shifted by 1 unit (length of normalized vector) to the left - // if we walk along contour clockwise, this new contour is outside the old one - // - // inPt' is the intersection of the two lines parallel to the two - // adjacent edges of inPt at a distance of 1 unit on the left side. - - var v_trans_x, v_trans_y, shrink_by = 1; // resulting translation vector for inPt - - // good reading for geometry algorithms (here: line-line intersection) - // http://geomalgorithms.com/a05-_intersect-1.html - - var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; - var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; - - var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); - - // check for collinear edges - var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - - if ( Math.abs( collinear0 ) > Number.EPSILON ) { - - // not collinear - - // length of vectors for normalizing - - var v_prev_len = Math.sqrt( v_prev_lensq ); - var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); - - // shift adjacent points by unit vectors to the left - - var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); - var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); - - var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); - var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); - - // scaling factor for v_prev to intersection point - - var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / - ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - - // vector from inPt to intersection point - - v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); - v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); - - // Don't normalize!, otherwise sharp corners become ugly - // but prevent crazy spikes - var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); - if ( v_trans_lensq <= 2 ) { - - return new Vector2( v_trans_x, v_trans_y ); - - } else { - - shrink_by = Math.sqrt( v_trans_lensq / 2 ); - - } - - } else { - - // handle special case of collinear edges - - var direction_eq = false; // assumes: opposite - if ( v_prev_x > Number.EPSILON ) { - - if ( v_next_x > Number.EPSILON ) { - - direction_eq = true; - - } - - } else { - - if ( v_prev_x < - Number.EPSILON ) { - - if ( v_next_x < - Number.EPSILON ) { - - direction_eq = true; - - } - - } else { - - if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { - - direction_eq = true; - - } - - } - - } - - if ( direction_eq ) { - - // console.log("Warning: lines are a straight sequence"); - v_trans_x = - v_prev_y; - v_trans_y = v_prev_x; - shrink_by = Math.sqrt( v_prev_lensq ); - - } else { - - // console.log("Warning: lines are a straight spike"); - v_trans_x = v_prev_x; - v_trans_y = v_prev_y; - shrink_by = Math.sqrt( v_prev_lensq / 2 ); - - } - - } - - return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); - - } - - - var contourMovements = []; - - for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - - if ( j === il ) j = 0; - if ( k === il ) k = 0; - - // (j)---(i)---(k) - // console.log('i,j,k', i, j , k) - - contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); - - } - - var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); - - for ( h = 0, hl = holes.length; h < hl; h ++ ) { - - ahole = holes[ h ]; - - oneHoleMovements = []; - - for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - - if ( j === il ) j = 0; - if ( k === il ) k = 0; - - // (j)---(i)---(k) - oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); - - } - - holesMovements.push( oneHoleMovements ); - verticesMovements = verticesMovements.concat( oneHoleMovements ); - - } - - - // Loop bevelSegments, 1 for the front, 1 for the back - - for ( b = 0; b < bevelSegments; b ++ ) { - - //for ( b = bevelSegments; b > 0; b -- ) { - - t = b / bevelSegments; - z = bevelThickness * Math.cos( t * Math.PI / 2 ); - bs = bevelSize * Math.sin( t * Math.PI / 2 ); - - // contract shape - - for ( i = 0, il = contour.length; i < il; i ++ ) { - - vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - - v( vert.x, vert.y, - z ); - - } - - // expand holes - - for ( h = 0, hl = holes.length; h < hl; h ++ ) { - - ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; - - for ( i = 0, il = ahole.length; i < il; i ++ ) { - - vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - - v( vert.x, vert.y, - z ); - - } - - } - - } - - bs = bevelSize; - - // Back facing vertices - - for ( i = 0; i < vlen; i ++ ) { - - vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - - if ( ! extrudeByPath ) { - - v( vert.x, vert.y, 0 ); - - } else { - - // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); - - normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); - - position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); - - v( position2.x, position2.y, position2.z ); - - } - - } - - // Add stepped vertices... - // Including front facing vertices - - var s; - - for ( s = 1; s <= steps; s ++ ) { - - for ( i = 0; i < vlen; i ++ ) { - - vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - - if ( ! extrudeByPath ) { - - v( vert.x, vert.y, amount / steps * s ); - - } else { - - // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); - - normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); - - position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); - - v( position2.x, position2.y, position2.z ); - - } - - } - - } - - - // Add bevel segments planes - - //for ( b = 1; b <= bevelSegments; b ++ ) { - for ( b = bevelSegments - 1; b >= 0; b -- ) { - - t = b / bevelSegments; - z = bevelThickness * Math.cos ( t * Math.PI / 2 ); - bs = bevelSize * Math.sin( t * Math.PI / 2 ); - - // contract shape - - for ( i = 0, il = contour.length; i < il; i ++ ) { - - vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - v( vert.x, vert.y, amount + z ); - - } - - // expand holes - - for ( h = 0, hl = holes.length; h < hl; h ++ ) { - - ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; - - for ( i = 0, il = ahole.length; i < il; i ++ ) { - - vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - - if ( ! extrudeByPath ) { - - v( vert.x, vert.y, amount + z ); - - } else { - - v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); - - } - - } - - } - - } - - /* Faces */ - - // Top and bottom faces - - buildLidFaces(); - - // Sides faces - - buildSideFaces(); - - - ///// Internal functions - - function buildLidFaces() { - - if ( bevelEnabled ) { - - var layer = 0; // steps + 1 - var offset = vlen * layer; - - // Bottom faces - - for ( i = 0; i < flen; i ++ ) { - - face = faces[ i ]; - f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); - - } - - layer = steps + bevelSegments * 2; - offset = vlen * layer; - - // Top faces - - for ( i = 0; i < flen; i ++ ) { - - face = faces[ i ]; - f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); - - } - - } else { - - // Bottom faces - - for ( i = 0; i < flen; i ++ ) { - - face = faces[ i ]; - f3( face[ 2 ], face[ 1 ], face[ 0 ] ); - - } - - // Top faces - - for ( i = 0; i < flen; i ++ ) { - - face = faces[ i ]; - f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); - - } - - } - - } - - // Create faces for the z-sides of the shape - - function buildSideFaces() { - - var layeroffset = 0; - sidewalls( contour, layeroffset ); - layeroffset += contour.length; - - for ( h = 0, hl = holes.length; h < hl; h ++ ) { - - ahole = holes[ h ]; - sidewalls( ahole, layeroffset ); - - //, true - layeroffset += ahole.length; - - } - - } - - function sidewalls( contour, layeroffset ) { - - var j, k; - i = contour.length; - - while ( -- i >= 0 ) { - - j = i; - k = i - 1; - if ( k < 0 ) k = contour.length - 1; - - //console.log('b', i,j, i-1, k,vertices.length); - - var s = 0, sl = steps + bevelSegments * 2; - - for ( s = 0; s < sl; s ++ ) { - - var slen1 = vlen * s; - var slen2 = vlen * ( s + 1 ); - - var a = layeroffset + j + slen1, - b = layeroffset + k + slen1, - c = layeroffset + k + slen2, - d = layeroffset + j + slen2; - - f4( a, b, c, d, contour, s, sl, j, k ); - - } - - } - - } - - - function v( x, y, z ) { - - scope.vertices.push( new Vector3( x, y, z ) ); - - } - - function f3( a, b, c ) { - - a += shapesOffset; - b += shapesOffset; - c += shapesOffset; - - scope.faces.push( new Face3( a, b, c, null, null, 0 ) ); - - var uvs = uvgen.generateTopUV( scope, a, b, c ); - - scope.faceVertexUvs[ 0 ].push( uvs ); - - } - - function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) { - - a += shapesOffset; - b += shapesOffset; - c += shapesOffset; - d += shapesOffset; - - scope.faces.push( new Face3( a, b, d, null, null, 1 ) ); - scope.faces.push( new Face3( b, c, d, null, null, 1 ) ); - - var uvs = uvgen.generateSideWallUV( scope, a, b, c, d ); - - scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] ); - scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] ); - - } - - }; - - ExtrudeGeometry.WorldUVGenerator = { - - generateTopUV: function ( geometry, indexA, indexB, indexC ) { - - var vertices = geometry.vertices; - - var a = vertices[ indexA ]; - var b = vertices[ indexB ]; - var c = vertices[ indexC ]; - - return [ - new Vector2( a.x, a.y ), - new Vector2( b.x, b.y ), - new Vector2( c.x, c.y ) - ]; - - }, - - generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) { - - var vertices = geometry.vertices; - - var a = vertices[ indexA ]; - var b = vertices[ indexB ]; - var c = vertices[ indexC ]; - var d = vertices[ indexD ]; - - if ( Math.abs( a.y - b.y ) < 0.01 ) { - - return [ - new Vector2( a.x, 1 - a.z ), - new Vector2( b.x, 1 - b.z ), - new Vector2( c.x, 1 - c.z ), - new Vector2( d.x, 1 - d.z ) - ]; - - } else { - - return [ - new Vector2( a.y, 1 - a.z ), - new Vector2( b.y, 1 - b.z ), - new Vector2( c.y, 1 - c.z ), - new Vector2( d.y, 1 - d.z ) - ]; - - } - - } - }; - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author alteredq / http://alteredqualia.com/ - * - * Text = 3D Text - * - * parameters = { - * font: , // font - * - * size: , // size of the text - * height: , // thickness to extrude text - * curveSegments: , // number of points on the curves - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into text bevel goes - * bevelSize: // how far from text outline is bevel - * } - */ - - function TextGeometry( text, parameters ) { - - parameters = parameters || {}; - - var font = parameters.font; - - if ( (font && font.isFont) === false ) { - - console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' ); - return new Geometry(); - - } - - var shapes = font.generateShapes( text, parameters.size, parameters.curveSegments ); - - // translate parameters to ExtrudeGeometry API - - parameters.amount = parameters.height !== undefined ? parameters.height : 50; - - // defaults - - if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; - if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; - if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; - - ExtrudeGeometry.call( this, shapes, parameters ); - - this.type = 'TextGeometry'; - - } - - TextGeometry.prototype = Object.create( ExtrudeGeometry.prototype ); - TextGeometry.prototype.constructor = TextGeometry; - - /** - * @author benaadams / https://twitter.com/ben_a_adams - * based on THREE.SphereGeometry - */ - - function SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { - - BufferGeometry.call( this ); - - this.type = 'SphereBufferGeometry'; - - this.parameters = { - radius: radius, - widthSegments: widthSegments, - heightSegments: heightSegments, - phiStart: phiStart, - phiLength: phiLength, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - radius = radius || 50; - - widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); - heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); - - phiStart = phiStart !== undefined ? phiStart : 0; - phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; - - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; - - var thetaEnd = thetaStart + thetaLength; - - var vertexCount = ( ( widthSegments + 1 ) * ( heightSegments + 1 ) ); - - var positions = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); - - var index = 0, vertices = [], normal = new Vector3(); - - for ( var y = 0; y <= heightSegments; y ++ ) { - - var verticesRow = []; - - var v = y / heightSegments; - - for ( var x = 0; x <= widthSegments; x ++ ) { - - var u = x / widthSegments; - - var px = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - var py = radius * Math.cos( thetaStart + v * thetaLength ); - var pz = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - - normal.set( px, py, pz ).normalize(); - - positions.setXYZ( index, px, py, pz ); - normals.setXYZ( index, normal.x, normal.y, normal.z ); - uvs.setXY( index, u, 1 - v ); - - verticesRow.push( index ); - - index ++; - - } - - vertices.push( verticesRow ); - - } - - var indices = []; - - for ( var y = 0; y < heightSegments; y ++ ) { - - for ( var x = 0; x < widthSegments; x ++ ) { - - var v1 = vertices[ y ][ x + 1 ]; - var v2 = vertices[ y ][ x ]; - var v3 = vertices[ y + 1 ][ x ]; - var v4 = vertices[ y + 1 ][ x + 1 ]; - - if ( y !== 0 || thetaStart > 0 ) indices.push( v1, v2, v4 ); - if ( y !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( v2, v3, v4 ); - - } - - } - - this.setIndex( new ( positions.count > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ) ); - this.addAttribute( 'position', positions ); - this.addAttribute( 'normal', normals ); - this.addAttribute( 'uv', uvs ); - - this.boundingSphere = new Sphere( new Vector3(), radius ); - - } - - SphereBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - SphereBufferGeometry.prototype.constructor = SphereBufferGeometry; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function SphereGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { - - Geometry.call( this ); - - this.type = 'SphereGeometry'; - - this.parameters = { - radius: radius, - widthSegments: widthSegments, - heightSegments: heightSegments, - phiStart: phiStart, - phiLength: phiLength, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - this.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) ); - - } - - SphereGeometry.prototype = Object.create( Geometry.prototype ); - SphereGeometry.prototype.constructor = SphereGeometry; - - /** - * @author Mugen87 / https://github.com/Mugen87 - */ - - function RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { - - BufferGeometry.call( this ); - - this.type = 'RingBufferGeometry'; - - this.parameters = { - innerRadius: innerRadius, - outerRadius: outerRadius, - thetaSegments: thetaSegments, - phiSegments: phiSegments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - innerRadius = innerRadius || 20; - outerRadius = outerRadius || 50; - - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - - thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; - phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1; - - // these are used to calculate buffer length - var vertexCount = ( thetaSegments + 1 ) * ( phiSegments + 1 ); - var indexCount = thetaSegments * phiSegments * 2 * 3; - - // buffers - var indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 ); - var vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); - - // some helper variables - var index = 0, indexOffset = 0, segment; - var radius = innerRadius; - var radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); - var vertex = new Vector3(); - var uv = new Vector2(); - var j, i; - - // generate vertices, normals and uvs - - // values are generate from the inside of the ring to the outside - - for ( j = 0; j <= phiSegments; j ++ ) { - - for ( i = 0; i <= thetaSegments; i ++ ) { - - segment = thetaStart + i / thetaSegments * thetaLength; - - // vertex - vertex.x = radius * Math.cos( segment ); - vertex.y = radius * Math.sin( segment ); - vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); - - // normal - normals.setXYZ( index, 0, 0, 1 ); - - // uv - uv.x = ( vertex.x / outerRadius + 1 ) / 2; - uv.y = ( vertex.y / outerRadius + 1 ) / 2; - uvs.setXY( index, uv.x, uv.y ); - - // increase index - index++; - - } - - // increase the radius for next row of vertices - radius += radiusStep; - - } - - // generate indices - - for ( j = 0; j < phiSegments; j ++ ) { - - var thetaSegmentLevel = j * ( thetaSegments + 1 ); - - for ( i = 0; i < thetaSegments; i ++ ) { - - segment = i + thetaSegmentLevel; - - // indices - var a = segment; - var b = segment + thetaSegments + 1; - var c = segment + thetaSegments + 2; - var d = segment + 1; - - // face one - indices.setX( indexOffset, a ); indexOffset++; - indices.setX( indexOffset, b ); indexOffset++; - indices.setX( indexOffset, c ); indexOffset++; - - // face two - indices.setX( indexOffset, a ); indexOffset++; - indices.setX( indexOffset, c ); indexOffset++; - indices.setX( indexOffset, d ); indexOffset++; - - } - - } - - // build geometry - - this.setIndex( indices ); - this.addAttribute( 'position', vertices ); - this.addAttribute( 'normal', normals ); - this.addAttribute( 'uv', uvs ); - - } - - RingBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - RingBufferGeometry.prototype.constructor = RingBufferGeometry; - - /** - * @author Kaleb Murphy - */ - - function RingGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { - - Geometry.call( this ); - - this.type = 'RingGeometry'; - - this.parameters = { - innerRadius: innerRadius, - outerRadius: outerRadius, - thetaSegments: thetaSegments, - phiSegments: phiSegments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - this.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) ); - - } - - RingGeometry.prototype = Object.create( Geometry.prototype ); - RingGeometry.prototype.constructor = RingGeometry; - - /** - * @author mrdoob / http://mrdoob.com/ - * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as - */ - - function PlaneGeometry( width, height, widthSegments, heightSegments ) { - - Geometry.call( this ); - - this.type = 'PlaneGeometry'; - - this.parameters = { - width: width, - height: height, - widthSegments: widthSegments, - heightSegments: heightSegments - }; - - this.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); - - } - - PlaneGeometry.prototype = Object.create( Geometry.prototype ); - PlaneGeometry.prototype.constructor = PlaneGeometry; - - /** - * @author Mugen87 / https://github.com/Mugen87 - */ - - // points - to create a closed torus, one must use a set of points - // like so: [ a, b, c, d, a ], see first is the same as last. - // segments - the number of circumference segments to create - // phiStart - the starting radian - // phiLength - the radian (0 to 2PI) range of the lathed section - // 2PI is a closed lathe, less than 2PI is a portion. - - function LatheBufferGeometry( points, segments, phiStart, phiLength ) { - - BufferGeometry.call( this ); - - this.type = 'LatheBufferGeometry'; - - this.parameters = { - points: points, - segments: segments, - phiStart: phiStart, - phiLength: phiLength - }; - - segments = Math.floor( segments ) || 12; - phiStart = phiStart || 0; - phiLength = phiLength || Math.PI * 2; - - // clamp phiLength so it's in range of [ 0, 2PI ] - phiLength = _Math.clamp( phiLength, 0, Math.PI * 2 ); - - // these are used to calculate buffer length - var vertexCount = ( segments + 1 ) * points.length; - var indexCount = segments * points.length * 2 * 3; - - // buffers - var indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 ); - var vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); - - // helper variables - var index = 0, indexOffset = 0, base; - var inverseSegments = 1.0 / segments; - var vertex = new Vector3(); - var uv = new Vector2(); - var i, j; - - // generate vertices and uvs - - for ( i = 0; i <= segments; i ++ ) { - - var phi = phiStart + i * inverseSegments * phiLength; - - var sin = Math.sin( phi ); - var cos = Math.cos( phi ); - - for ( j = 0; j <= ( points.length - 1 ); j ++ ) { - - // vertex - vertex.x = points[ j ].x * sin; - vertex.y = points[ j ].y; - vertex.z = points[ j ].x * cos; - vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); - - // uv - uv.x = i / segments; - uv.y = j / ( points.length - 1 ); - uvs.setXY( index, uv.x, uv.y ); - - // increase index - index ++; - - } - - } - - // generate indices - - for ( i = 0; i < segments; i ++ ) { - - for ( j = 0; j < ( points.length - 1 ); j ++ ) { - - base = j + i * points.length; - - // indices - var a = base; - var b = base + points.length; - var c = base + points.length + 1; - var d = base + 1; - - // face one - indices.setX( indexOffset, a ); indexOffset++; - indices.setX( indexOffset, b ); indexOffset++; - indices.setX( indexOffset, d ); indexOffset++; - - // face two - indices.setX( indexOffset, b ); indexOffset++; - indices.setX( indexOffset, c ); indexOffset++; - indices.setX( indexOffset, d ); indexOffset++; - - } - - } - - // build geometry - - this.setIndex( indices ); - this.addAttribute( 'position', vertices ); - this.addAttribute( 'uv', uvs ); - - // generate normals - - this.computeVertexNormals(); - - // if the geometry is closed, we need to average the normals along the seam. - // because the corresponding vertices are identical (but still have different UVs). - - if( phiLength === Math.PI * 2 ) { - - var normals = this.attributes.normal.array; - var n1 = new Vector3(); - var n2 = new Vector3(); - var n = new Vector3(); - - // this is the buffer offset for the last line of vertices - base = segments * points.length * 3; - - for( i = 0, j = 0; i < points.length; i ++, j += 3 ) { - - // select the normal of the vertex in the first line - n1.x = normals[ j + 0 ]; - n1.y = normals[ j + 1 ]; - n1.z = normals[ j + 2 ]; - - // select the normal of the vertex in the last line - n2.x = normals[ base + j + 0 ]; - n2.y = normals[ base + j + 1 ]; - n2.z = normals[ base + j + 2 ]; - - // average normals - n.addVectors( n1, n2 ).normalize(); - - // assign the new values to both normals - normals[ j + 0 ] = normals[ base + j + 0 ] = n.x; - normals[ j + 1 ] = normals[ base + j + 1 ] = n.y; - normals[ j + 2 ] = normals[ base + j + 2 ] = n.z; - - } // next row - - } - - } - - LatheBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - LatheBufferGeometry.prototype.constructor = LatheBufferGeometry; - - /** - * @author astrodud / http://astrodud.isgreat.org/ - * @author zz85 / https://github.com/zz85 - * @author bhouston / http://clara.io - */ - - // points - to create a closed torus, one must use a set of points - // like so: [ a, b, c, d, a ], see first is the same as last. - // segments - the number of circumference segments to create - // phiStart - the starting radian - // phiLength - the radian (0 to 2PI) range of the lathed section - // 2PI is a closed lathe, less than 2PI is a portion. - - function LatheGeometry( points, segments, phiStart, phiLength ) { - - Geometry.call( this ); - - this.type = 'LatheGeometry'; - - this.parameters = { - points: points, - segments: segments, - phiStart: phiStart, - phiLength: phiLength - }; - - this.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) ); - this.mergeVertices(); - - } - - LatheGeometry.prototype = Object.create( Geometry.prototype ); - LatheGeometry.prototype.constructor = LatheGeometry; - - /** - * @author Mugen87 / https://github.com/Mugen87 - * - * Creates a one-sided polygonal geometry from one or more shapes. - * - **/ - - function ShapeBufferGeometry( shapes, curveSegments ) { - - BufferGeometry.call( this ); - - this.type = 'ShapeBufferGeometry'; - - this.parameters = { - shapes: shapes, - curveSegments: curveSegments - }; - - curveSegments = curveSegments || 12; - - var vertices = []; - var normals = []; - var uvs = []; - var indices = []; - - var groupStart = 0; - var groupCount = 0; - - // allow single and array values for "shapes" parameter - - if ( Array.isArray( shapes ) === false ) { - - addShape( shapes ); - - } else { - - for ( var i = 0; i < shapes.length; i++ ) { - - addShape( shapes[ i ] ); - - this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support - - groupStart += groupCount; - groupCount = 0; - - } - - } - - // build geometry - - this.setIndex( new ( indices.length > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ) ); - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - - // helper functions - - function addShape( shape ) { - - var i, l, shapeHole; - - var indexOffset = vertices.length / 3; - var points = shape.extractPoints( curveSegments ); - - var shapeVertices = points.shape; - var shapeHoles = points.holes; - - // check direction of vertices - - if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { - - shapeVertices = shapeVertices.reverse(); - - // also check if holes are in the opposite direction - - for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { - - shapeHole = shapeHoles[ i ]; - - if ( ShapeUtils.isClockWise( shapeHole ) === true ) { - - shapeHoles[ i ] = shapeHole.reverse(); - - } - - } - - } - - var faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); - - // join vertices of inner and outer paths to a single array - - for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { - - shapeHole = shapeHoles[ i ]; - shapeVertices = shapeVertices.concat( shapeHole ); - - } - - // vertices, normals, uvs - - for ( i = 0, l = shapeVertices.length; i < l; i ++ ) { - - var vertex = shapeVertices[ i ]; - - vertices.push( vertex.x, vertex.y, 0 ); - normals.push( 0, 0, 1 ); - uvs.push( vertex.x, vertex.y ); // world uvs - - } - - // incides - - for ( i = 0, l = faces.length; i < l; i ++ ) { - - var face = faces[ i ]; - - var a = face[ 0 ] + indexOffset; - var b = face[ 1 ] + indexOffset; - var c = face[ 2 ] + indexOffset; - - indices.push( a, b, c ); - groupCount += 3; - - } - - } - - } - - ShapeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - ShapeBufferGeometry.prototype.constructor = ShapeBufferGeometry; - - /** - * @author jonobr1 / http://jonobr1.com - * - * Creates a one-sided polygonal geometry from a path shape. - * - **/ - - function ShapeGeometry( shapes, curveSegments ) { - - Geometry.call( this ); - - this.type = 'ShapeGeometry'; - - if ( typeof curveSegments === 'object' ) { - - console.warn( 'THREE.ShapeGeometry: Options parameter has been removed.' ); - - curveSegments = curveSegments.curveSegments; - - } - - this.parameters = { - shapes: shapes, - curveSegments: curveSegments - }; - - this.fromBufferGeometry( new ShapeBufferGeometry( shapes, curveSegments ) ); - this.mergeVertices(); - - } - - ShapeGeometry.prototype = Object.create( Geometry.prototype ); - ShapeGeometry.prototype.constructor = ShapeGeometry; - - /** - * @author WestLangley / http://github.com/WestLangley - */ - - function EdgesGeometry( geometry, thresholdAngle ) { - - BufferGeometry.call( this ); - - thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; - - var thresholdDot = Math.cos( _Math.DEG2RAD * thresholdAngle ); - - var edge = [ 0, 0 ], hash = {}; - - function sortFunction( a, b ) { - - return a - b; - - } - - var keys = [ 'a', 'b', 'c' ]; - - var geometry2; - - if ( geometry.isBufferGeometry ) { - - geometry2 = new Geometry(); - geometry2.fromBufferGeometry( geometry ); - - } else { - - geometry2 = geometry.clone(); - - } - - geometry2.mergeVertices(); - geometry2.computeFaceNormals(); - - var vertices = geometry2.vertices; - var faces = geometry2.faces; - - for ( var i = 0, l = faces.length; i < l; i ++ ) { - - var face = faces[ i ]; - - for ( var j = 0; j < 3; j ++ ) { - - edge[ 0 ] = face[ keys[ j ] ]; - edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; - edge.sort( sortFunction ); - - var key = edge.toString(); - - if ( hash[ key ] === undefined ) { - - hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined }; - - } else { - - hash[ key ].face2 = i; - - } - - } - - } - - var coords = []; - - for ( var key in hash ) { - - var h = hash[ key ]; - - // An edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree. - - if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) <= thresholdDot ) { - - var vertex = vertices[ h.vert1 ]; - coords.push( vertex.x ); - coords.push( vertex.y ); - coords.push( vertex.z ); - - vertex = vertices[ h.vert2 ]; - coords.push( vertex.x ); - coords.push( vertex.y ); - coords.push( vertex.z ); - - } - - } - - this.addAttribute( 'position', new Float32BufferAttribute( coords, 3 ) ); - - } - - EdgesGeometry.prototype = Object.create( BufferGeometry.prototype ); - EdgesGeometry.prototype.constructor = EdgesGeometry; - - /** - * @author Mugen87 / https://github.com/Mugen87 - */ - - function CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - - BufferGeometry.call( this ); - - this.type = 'CylinderBufferGeometry'; - - this.parameters = { - radiusTop: radiusTop, - radiusBottom: radiusBottom, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - var scope = this; - - radiusTop = radiusTop !== undefined ? radiusTop : 20; - radiusBottom = radiusBottom !== undefined ? radiusBottom : 20; - height = height !== undefined ? height : 100; - - radialSegments = Math.floor( radialSegments ) || 8; - heightSegments = Math.floor( heightSegments ) || 1; - - openEnded = openEnded !== undefined ? openEnded : false; - thetaStart = thetaStart !== undefined ? thetaStart : 0.0; - thetaLength = thetaLength !== undefined ? thetaLength : 2.0 * Math.PI; - - // used to calculate buffer length - - var nbCap = 0; - - if ( openEnded === false ) { - - if ( radiusTop > 0 ) nbCap ++; - if ( radiusBottom > 0 ) nbCap ++; - - } - - var vertexCount = calculateVertexCount(); - var indexCount = calculateIndexCount(); - - // buffers - - var indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ), 1 ); - var vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); - - // helper variables - - var index = 0, - indexOffset = 0, - indexArray = [], - halfHeight = height / 2; - - // group variables - var groupStart = 0; - - // generate geometry - - generateTorso(); - - if ( openEnded === false ) { - - if ( radiusTop > 0 ) generateCap( true ); - if ( radiusBottom > 0 ) generateCap( false ); - - } - - // build geometry - - this.setIndex( indices ); - this.addAttribute( 'position', vertices ); - this.addAttribute( 'normal', normals ); - this.addAttribute( 'uv', uvs ); - - // helper functions - - function calculateVertexCount() { - - var count = ( radialSegments + 1 ) * ( heightSegments + 1 ); - - if ( openEnded === false ) { - - count += ( ( radialSegments + 1 ) * nbCap ) + ( radialSegments * nbCap ); - - } - - return count; - - } - - function calculateIndexCount() { - - var count = radialSegments * heightSegments * 2 * 3; - - if ( openEnded === false ) { - - count += radialSegments * nbCap * 3; - - } - - return count; - - } - - function generateTorso() { - - var x, y; - var normal = new Vector3(); - var vertex = new Vector3(); - - var groupCount = 0; - - // this will be used to calculate the normal - var slope = ( radiusBottom - radiusTop ) / height; - - // generate vertices, normals and uvs - - for ( y = 0; y <= heightSegments; y ++ ) { - - var indexRow = []; - - var v = y / heightSegments; - - // calculate the radius of the current row - var radius = v * ( radiusBottom - radiusTop ) + radiusTop; - - for ( x = 0; x <= radialSegments; x ++ ) { - - var u = x / radialSegments; - - var theta = u * thetaLength + thetaStart; - - var sinTheta = Math.sin( theta ); - var cosTheta = Math.cos( theta ); - - // vertex - vertex.x = radius * sinTheta; - vertex.y = - v * height + halfHeight; - vertex.z = radius * cosTheta; - vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); - - // normal - normal.set( sinTheta, slope, cosTheta ).normalize(); - normals.setXYZ( index, normal.x, normal.y, normal.z ); - - // uv - uvs.setXY( index, u, 1 - v ); - - // save index of vertex in respective row - indexRow.push( index ); - - // increase index - index ++; - - } - - // now save vertices of the row in our index array - indexArray.push( indexRow ); - - } - - // generate indices - - for ( x = 0; x < radialSegments; x ++ ) { - - for ( y = 0; y < heightSegments; y ++ ) { - - // we use the index array to access the correct indices - var i1 = indexArray[ y ][ x ]; - var i2 = indexArray[ y + 1 ][ x ]; - var i3 = indexArray[ y + 1 ][ x + 1 ]; - var i4 = indexArray[ y ][ x + 1 ]; - - // face one - indices.setX( indexOffset, i1 ); indexOffset ++; - indices.setX( indexOffset, i2 ); indexOffset ++; - indices.setX( indexOffset, i4 ); indexOffset ++; - - // face two - indices.setX( indexOffset, i2 ); indexOffset ++; - indices.setX( indexOffset, i3 ); indexOffset ++; - indices.setX( indexOffset, i4 ); indexOffset ++; - - // update counters - groupCount += 6; - - } - - } - - // add a group to the geometry. this will ensure multi material support - scope.addGroup( groupStart, groupCount, 0 ); - - // calculate new start value for groups - groupStart += groupCount; - - } - - function generateCap( top ) { - - var x, centerIndexStart, centerIndexEnd; - - var uv = new Vector2(); - var vertex = new Vector3(); - - var groupCount = 0; - - var radius = ( top === true ) ? radiusTop : radiusBottom; - var sign = ( top === true ) ? 1 : - 1; - - // save the index of the first center vertex - centerIndexStart = index; - - // first we generate the center vertex data of the cap. - // because the geometry needs one set of uvs per face, - // we must generate a center vertex per face/segment - - for ( x = 1; x <= radialSegments; x ++ ) { - - // vertex - vertices.setXYZ( index, 0, halfHeight * sign, 0 ); - - // normal - normals.setXYZ( index, 0, sign, 0 ); - - // uv - uv.x = 0.5; - uv.y = 0.5; - - uvs.setXY( index, uv.x, uv.y ); - - // increase index - index ++; - - } - - // save the index of the last center vertex - centerIndexEnd = index; - - // now we generate the surrounding vertices, normals and uvs - - for ( x = 0; x <= radialSegments; x ++ ) { - - var u = x / radialSegments; - var theta = u * thetaLength + thetaStart; - - var cosTheta = Math.cos( theta ); - var sinTheta = Math.sin( theta ); - - // vertex - vertex.x = radius * sinTheta; - vertex.y = halfHeight * sign; - vertex.z = radius * cosTheta; - vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); - - // normal - normals.setXYZ( index, 0, sign, 0 ); - - // uv - uv.x = ( cosTheta * 0.5 ) + 0.5; - uv.y = ( sinTheta * 0.5 * sign ) + 0.5; - uvs.setXY( index, uv.x, uv.y ); - - // increase index - index ++; - - } - - // generate indices - - for ( x = 0; x < radialSegments; x ++ ) { - - var c = centerIndexStart + x; - var i = centerIndexEnd + x; - - if ( top === true ) { - - // face top - indices.setX( indexOffset, i ); indexOffset ++; - indices.setX( indexOffset, i + 1 ); indexOffset ++; - indices.setX( indexOffset, c ); indexOffset ++; - - } else { - - // face bottom - indices.setX( indexOffset, i + 1 ); indexOffset ++; - indices.setX( indexOffset, i ); indexOffset ++; - indices.setX( indexOffset, c ); indexOffset ++; - - } - - // update counters - groupCount += 3; - - } - - // add a group to the geometry. this will ensure multi material support - scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); - - // calculate new start value for groups - groupStart += groupCount; - - } - - } - - CylinderBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - CylinderBufferGeometry.prototype.constructor = CylinderBufferGeometry; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function CylinderGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - - Geometry.call( this ); - - this.type = 'CylinderGeometry'; - - this.parameters = { - radiusTop: radiusTop, - radiusBottom: radiusBottom, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - this.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) ); - this.mergeVertices(); - - } - - CylinderGeometry.prototype = Object.create( Geometry.prototype ); - CylinderGeometry.prototype.constructor = CylinderGeometry; - - /** - * @author abelnation / http://github.com/abelnation - */ - - function ConeGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - - CylinderGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); - - this.type = 'ConeGeometry'; - - this.parameters = { - radius: radius, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - } - - ConeGeometry.prototype = Object.create( CylinderGeometry.prototype ); - ConeGeometry.prototype.constructor = ConeGeometry; - - /** - * @author: abelnation / http://github.com/abelnation - */ - - function ConeBufferGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - - CylinderBufferGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); - - this.type = 'ConeBufferGeometry'; - - this.parameters = { - radius: radius, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - } - - ConeBufferGeometry.prototype = Object.create( CylinderBufferGeometry.prototype ); - ConeBufferGeometry.prototype.constructor = ConeBufferGeometry; - - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ - - function CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) { - - BufferGeometry.call( this ); - - this.type = 'CircleBufferGeometry'; - - this.parameters = { - radius: radius, - segments: segments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - radius = radius || 50; - segments = segments !== undefined ? Math.max( 3, segments ) : 8; - - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - - var vertices = segments + 2; - - var positions = new Float32Array( vertices * 3 ); - var normals = new Float32Array( vertices * 3 ); - var uvs = new Float32Array( vertices * 2 ); - - // center data is already zero, but need to set a few extras - normals[ 2 ] = 1.0; - uvs[ 0 ] = 0.5; - uvs[ 1 ] = 0.5; - - for ( var s = 0, i = 3, ii = 2 ; s <= segments; s ++, i += 3, ii += 2 ) { - - var segment = thetaStart + s / segments * thetaLength; - - positions[ i ] = radius * Math.cos( segment ); - positions[ i + 1 ] = radius * Math.sin( segment ); - - normals[ i + 2 ] = 1; // normal z - - uvs[ ii ] = ( positions[ i ] / radius + 1 ) / 2; - uvs[ ii + 1 ] = ( positions[ i + 1 ] / radius + 1 ) / 2; - - } - - var indices = []; - - for ( var i = 1; i <= segments; i ++ ) { - - indices.push( i, i + 1, 0 ); - - } - - this.setIndex( new BufferAttribute( new Uint16Array( indices ), 1 ) ); - this.addAttribute( 'position', new BufferAttribute( positions, 3 ) ); - this.addAttribute( 'normal', new BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) ); - - this.boundingSphere = new Sphere( new Vector3(), radius ); - - } - - CircleBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - CircleBufferGeometry.prototype.constructor = CircleBufferGeometry; - - /** - * @author hughes - */ - - function CircleGeometry( radius, segments, thetaStart, thetaLength ) { - - Geometry.call( this ); - - this.type = 'CircleGeometry'; - - this.parameters = { - radius: radius, - segments: segments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - this.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) ); - - } - - CircleGeometry.prototype = Object.create( Geometry.prototype ); - CircleGeometry.prototype.constructor = CircleGeometry; - - /** - * @author mrdoob / http://mrdoob.com/ - * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as - */ - - function BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { - - Geometry.call( this ); - - this.type = 'BoxGeometry'; - - this.parameters = { - width: width, - height: height, - depth: depth, - widthSegments: widthSegments, - heightSegments: heightSegments, - depthSegments: depthSegments - }; - - this.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) ); - this.mergeVertices(); - - } - - BoxGeometry.prototype = Object.create( Geometry.prototype ); - BoxGeometry.prototype.constructor = BoxGeometry; - - - - var Geometries = Object.freeze({ - WireframeGeometry: WireframeGeometry, - ParametricGeometry: ParametricGeometry, - ParametricBufferGeometry: ParametricBufferGeometry, - TetrahedronGeometry: TetrahedronGeometry, - TetrahedronBufferGeometry: TetrahedronBufferGeometry, - OctahedronGeometry: OctahedronGeometry, - OctahedronBufferGeometry: OctahedronBufferGeometry, - IcosahedronGeometry: IcosahedronGeometry, - IcosahedronBufferGeometry: IcosahedronBufferGeometry, - DodecahedronGeometry: DodecahedronGeometry, - DodecahedronBufferGeometry: DodecahedronBufferGeometry, - PolyhedronGeometry: PolyhedronGeometry, - PolyhedronBufferGeometry: PolyhedronBufferGeometry, - TubeGeometry: TubeGeometry, - TubeBufferGeometry: TubeBufferGeometry, - TorusKnotGeometry: TorusKnotGeometry, - TorusKnotBufferGeometry: TorusKnotBufferGeometry, - TorusGeometry: TorusGeometry, - TorusBufferGeometry: TorusBufferGeometry, - TextGeometry: TextGeometry, - SphereBufferGeometry: SphereBufferGeometry, - SphereGeometry: SphereGeometry, - RingGeometry: RingGeometry, - RingBufferGeometry: RingBufferGeometry, - PlaneBufferGeometry: PlaneBufferGeometry, - PlaneGeometry: PlaneGeometry, - LatheGeometry: LatheGeometry, - LatheBufferGeometry: LatheBufferGeometry, - ShapeGeometry: ShapeGeometry, - ShapeBufferGeometry: ShapeBufferGeometry, - ExtrudeGeometry: ExtrudeGeometry, - EdgesGeometry: EdgesGeometry, - ConeGeometry: ConeGeometry, - ConeBufferGeometry: ConeBufferGeometry, - CylinderGeometry: CylinderGeometry, - CylinderBufferGeometry: CylinderBufferGeometry, - CircleBufferGeometry: CircleBufferGeometry, - CircleGeometry: CircleGeometry, - BoxBufferGeometry: BoxBufferGeometry, - BoxGeometry: BoxGeometry - }); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function ShadowMaterial() { - - ShaderMaterial.call( this, { - uniforms: UniformsUtils.merge( [ - UniformsLib.lights, - { - opacity: { value: 1.0 } - } - ] ), - vertexShader: ShaderChunk[ 'shadow_vert' ], - fragmentShader: ShaderChunk[ 'shadow_frag' ] - } ); - - this.lights = true; - this.transparent = true; - - Object.defineProperties( this, { - opacity: { - enumerable: true, - get: function () { - return this.uniforms.opacity.value; - }, - set: function ( value ) { - this.uniforms.opacity.value = value; - } - } - } ); - - } - - ShadowMaterial.prototype = Object.create( ShaderMaterial.prototype ); - ShadowMaterial.prototype.constructor = ShadowMaterial; - - ShadowMaterial.prototype.isShadowMaterial = true; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function RawShaderMaterial( parameters ) { - - ShaderMaterial.call( this, parameters ); - - this.type = 'RawShaderMaterial'; - - } - - RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype ); - RawShaderMaterial.prototype.constructor = RawShaderMaterial; - - RawShaderMaterial.prototype.isRawShaderMaterial = true; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function MultiMaterial( materials ) { - - this.uuid = _Math.generateUUID(); - - this.type = 'MultiMaterial'; - - this.materials = Array.isArray( materials ) ? materials : []; - - this.visible = true; - - } - - MultiMaterial.prototype = { - - constructor: MultiMaterial, - - isMultiMaterial: true, - - toJSON: function ( meta ) { - - var output = { - metadata: { - version: 4.2, - type: 'material', - generator: 'MaterialExporter' - }, - uuid: this.uuid, - type: this.type, - materials: [] - }; - - var materials = this.materials; - - for ( var i = 0, l = materials.length; i < l; i ++ ) { - - var material = materials[ i ].toJSON( meta ); - delete material.metadata; - - output.materials.push( material ); - - } - - output.visible = this.visible; - - return output; - - }, - - clone: function () { - - var material = new this.constructor(); - - for ( var i = 0; i < this.materials.length; i ++ ) { - - material.materials.push( this.materials[ i ].clone() ); - - } - - material.visible = this.visible; - - return material; - - } - - }; - - /** - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * color: , - * roughness: , - * metalness: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * roughnessMap: new THREE.Texture( ), - * - * metalnessMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * envMapIntensity: - * - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshStandardMaterial( parameters ) { - - Material.call( this ); - - this.defines = { 'STANDARD': '' }; - - this.type = 'MeshStandardMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - this.roughness = 0.5; - this.metalness = 0.5; - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.roughnessMap = null; - - this.metalnessMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.envMapIntensity = 1.0; - - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshStandardMaterial.prototype = Object.create( Material.prototype ); - MeshStandardMaterial.prototype.constructor = MeshStandardMaterial; - - MeshStandardMaterial.prototype.isMeshStandardMaterial = true; - - MeshStandardMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.defines = { 'STANDARD': '' }; - - this.color.copy( source.color ); - this.roughness = source.roughness; - this.metalness = source.metalness; - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.roughnessMap = source.roughnessMap; - - this.metalnessMap = source.metalnessMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.envMapIntensity = source.envMapIntensity; - - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * reflectivity: - * } - */ - - function MeshPhysicalMaterial( parameters ) { - - MeshStandardMaterial.call( this ); - - this.defines = { 'PHYSICAL': '' }; - - this.type = 'MeshPhysicalMaterial'; - - this.reflectivity = 0.5; // maps to F0 = 0.04 - - this.clearCoat = 0.0; - this.clearCoatRoughness = 0.0; - - this.setValues( parameters ); - - } - - MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype ); - MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial; - - MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; - - MeshPhysicalMaterial.prototype.copy = function ( source ) { - - MeshStandardMaterial.prototype.copy.call( this, source ); - - this.defines = { 'PHYSICAL': '' }; - - this.reflectivity = source.reflectivity; - - this.clearCoat = source.clearCoat; - this.clearCoatRoughness = source.clearCoatRoughness; - - return this; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * specular: , - * shininess: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshPhongMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshPhongMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - this.specular = new Color( 0x111111 ); - this.shininess = 30; - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshPhongMaterial.prototype = Object.create( Material.prototype ); - MeshPhongMaterial.prototype.constructor = MeshPhongMaterial; - - MeshPhongMaterial.prototype.isMeshPhongMaterial = true; - - MeshPhongMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - this.specular.copy( source.specular ); - this.shininess = source.shininess; - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * @author takahirox / http://github.com/takahirox - * - * parameters = { - * gradientMap: new THREE.Texture( ) - * } - */ - - function MeshToonMaterial( parameters ) { - - MeshPhongMaterial.call( this ); - - this.defines = { 'TOON': '' }; - - this.type = 'MeshToonMaterial'; - - this.gradientMap = null; - - this.setValues( parameters ); - - } - - MeshToonMaterial.prototype = Object.create( MeshPhongMaterial.prototype ); - MeshToonMaterial.prototype.constructor = MeshToonMaterial; - - MeshToonMaterial.prototype.isMeshToonMaterial = true; - - MeshToonMaterial.prototype.copy = function ( source ) { - - MeshPhongMaterial.prototype.copy.call( this, source ); - - this.gradientMap = source.gradientMap; - - return this; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * opacity: , - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshNormalMaterial( parameters ) { - - Material.call( this, parameters ); - - this.type = 'MeshNormalMaterial'; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.wireframe = false; - this.wireframeLinewidth = 1; - - this.fog = false; - this.lights = false; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshNormalMaterial.prototype = Object.create( Material.prototype ); - MeshNormalMaterial.prototype.constructor = MeshNormalMaterial; - - MeshNormalMaterial.prototype.isMeshNormalMaterial = true; - - MeshNormalMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshLambertMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshLambertMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshLambertMaterial.prototype = Object.create( Material.prototype ); - MeshLambertMaterial.prototype.constructor = MeshLambertMaterial; - - MeshLambertMaterial.prototype.isMeshLambertMaterial = true; - - MeshLambertMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * - * scale: , - * dashSize: , - * gapSize: - * } - */ - - function LineDashedMaterial( parameters ) { - - Material.call( this ); - - this.type = 'LineDashedMaterial'; - - this.color = new Color( 0xffffff ); - - this.linewidth = 1; - - this.scale = 1; - this.dashSize = 3; - this.gapSize = 1; - - this.lights = false; - - this.setValues( parameters ); - - } - - LineDashedMaterial.prototype = Object.create( Material.prototype ); - LineDashedMaterial.prototype.constructor = LineDashedMaterial; - - LineDashedMaterial.prototype.isLineDashedMaterial = true; - - LineDashedMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.linewidth = source.linewidth; - - this.scale = source.scale; - this.dashSize = source.dashSize; - this.gapSize = source.gapSize; - - return this; - - }; - - - - var Materials = Object.freeze({ - ShadowMaterial: ShadowMaterial, - SpriteMaterial: SpriteMaterial, - RawShaderMaterial: RawShaderMaterial, - ShaderMaterial: ShaderMaterial, - PointsMaterial: PointsMaterial, - MultiMaterial: MultiMaterial, - MeshPhysicalMaterial: MeshPhysicalMaterial, - MeshStandardMaterial: MeshStandardMaterial, - MeshPhongMaterial: MeshPhongMaterial, - MeshToonMaterial: MeshToonMaterial, - MeshNormalMaterial: MeshNormalMaterial, - MeshLambertMaterial: MeshLambertMaterial, - MeshDepthMaterial: MeshDepthMaterial, - MeshBasicMaterial: MeshBasicMaterial, - LineDashedMaterial: LineDashedMaterial, - LineBasicMaterial: LineBasicMaterial, - Material: Material - }); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - var Cache = { - - enabled: false, - - files: {}, - - add: function ( key, file ) { - - if ( this.enabled === false ) return; - - // console.log( 'THREE.Cache', 'Adding key:', key ); - - this.files[ key ] = file; - - }, - - get: function ( key ) { - - if ( this.enabled === false ) return; - - // console.log( 'THREE.Cache', 'Checking key:', key ); - - return this.files[ key ]; - - }, - - remove: function ( key ) { - - delete this.files[ key ]; - - }, - - clear: function () { - - this.files = {}; - - } - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function LoadingManager( onLoad, onProgress, onError ) { - - var scope = this; - - var isLoading = false, itemsLoaded = 0, itemsTotal = 0; - - this.onStart = undefined; - this.onLoad = onLoad; - this.onProgress = onProgress; - this.onError = onError; - - this.itemStart = function ( url ) { - - itemsTotal ++; - - if ( isLoading === false ) { - - if ( scope.onStart !== undefined ) { - - scope.onStart( url, itemsLoaded, itemsTotal ); - - } - - } - - isLoading = true; - - }; - - this.itemEnd = function ( url ) { - - itemsLoaded ++; - - if ( scope.onProgress !== undefined ) { - - scope.onProgress( url, itemsLoaded, itemsTotal ); - - } - - if ( itemsLoaded === itemsTotal ) { - - isLoading = false; - - if ( scope.onLoad !== undefined ) { - - scope.onLoad(); - - } - - } - - }; - - this.itemError = function ( url ) { - - if ( scope.onError !== undefined ) { - - scope.onError( url ); - - } - - }; - - } - - var DefaultLoadingManager = new LoadingManager(); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function FileLoader( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - - } - - Object.assign( FileLoader.prototype, { - - load: function ( url, onLoad, onProgress, onError ) { - - if ( url === undefined ) url = ''; - - if ( this.path !== undefined ) url = this.path + url; - - var scope = this; - - var cached = Cache.get( url ); - - if ( cached !== undefined ) { - - scope.manager.itemStart( url ); - - setTimeout( function () { - - if ( onLoad ) onLoad( cached ); - - scope.manager.itemEnd( url ); - - }, 0 ); - - return cached; - - } - - // Check for data: URI - var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; - var dataUriRegexResult = url.match( dataUriRegex ); - - // Safari can not handle Data URIs through XMLHttpRequest so process manually - if ( dataUriRegexResult ) { - - var mimeType = dataUriRegexResult[ 1 ]; - var isBase64 = !! dataUriRegexResult[ 2 ]; - var data = dataUriRegexResult[ 3 ]; - - data = window.decodeURIComponent( data ); - - if ( isBase64 ) data = window.atob( data ); - - try { - - var response; - var responseType = ( this.responseType || '' ).toLowerCase(); - - switch ( responseType ) { - - case 'arraybuffer': - case 'blob': - - response = new ArrayBuffer( data.length ); - - var view = new Uint8Array( response ); - - for ( var i = 0; i < data.length; i ++ ) { - - view[ i ] = data.charCodeAt( i ); - - } - - if ( responseType === 'blob' ) { - - response = new Blob( [ response ], { type: mimeType } ); - - } - - break; - - case 'document': - - var parser = new DOMParser(); - response = parser.parseFromString( data, mimeType ); - - break; - - case 'json': - - response = JSON.parse( data ); - - break; - - default: // 'text' or other - - response = data; - - break; - - } - - // Wait for next browser tick - window.setTimeout( function () { - - if ( onLoad ) onLoad( response ); - - scope.manager.itemEnd( url ); - - }, 0 ); - - } catch ( error ) { - - // Wait for next browser tick - window.setTimeout( function () { - - if ( onError ) onError( error ); - - scope.manager.itemError( url ); - - }, 0 ); - - } - - } else { - - var request = new XMLHttpRequest(); - request.open( 'GET', url, true ); - - request.addEventListener( 'load', function ( event ) { - - var response = event.target.response; - - Cache.add( url, response ); - - if ( this.status === 200 ) { - - if ( onLoad ) onLoad( response ); - - scope.manager.itemEnd( url ); - - } else if ( this.status === 0 ) { - - // Some browsers return HTTP Status 0 when using non-http protocol - // e.g. 'file://' or 'data://'. Handle as success. - - console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); - - if ( onLoad ) onLoad( response ); - - scope.manager.itemEnd( url ); - - } else { - - if ( onError ) onError( event ); - - scope.manager.itemError( url ); - - } - - }, false ); - - if ( onProgress !== undefined ) { - - request.addEventListener( 'progress', function ( event ) { - - onProgress( event ); - - }, false ); - - } - - request.addEventListener( 'error', function ( event ) { - - if ( onError ) onError( event ); - - scope.manager.itemError( url ); - - }, false ); - - if ( this.responseType !== undefined ) request.responseType = this.responseType; - if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; - - if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' ); - - request.send( null ); - - } - - scope.manager.itemStart( url ); - - return request; - - }, - - setPath: function ( value ) { - - this.path = value; - return this; - - }, - - setResponseType: function ( value ) { - - this.responseType = value; - return this; - - }, - - setWithCredentials: function ( value ) { - - this.withCredentials = value; - return this; - - }, - - setMimeType: function ( value ) { - - this.mimeType = value; - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * - * Abstract Base class to block based textures loader (dds, pvr, ...) - */ - - function CompressedTextureLoader( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - - // override in sub classes - this._parser = null; - - } - - Object.assign( CompressedTextureLoader.prototype, { - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var images = []; - - var texture = new CompressedTexture(); - texture.image = images; - - var loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - - function loadTexture( i ) { - - loader.load( url[ i ], function ( buffer ) { - - var texDatas = scope._parser( buffer, true ); - - images[ i ] = { - width: texDatas.width, - height: texDatas.height, - format: texDatas.format, - mipmaps: texDatas.mipmaps - }; - - loaded += 1; - - if ( loaded === 6 ) { - - if ( texDatas.mipmapCount === 1 ) - texture.minFilter = LinearFilter; - - texture.format = texDatas.format; - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture ); - - } - - }, onProgress, onError ); - - } - - if ( Array.isArray( url ) ) { - - var loaded = 0; - - for ( var i = 0, il = url.length; i < il; ++ i ) { - - loadTexture( i ); - - } - - } else { - - // compressed cubemap texture stored in a single DDS file - - loader.load( url, function ( buffer ) { - - var texDatas = scope._parser( buffer, true ); - - if ( texDatas.isCubemap ) { - - var faces = texDatas.mipmaps.length / texDatas.mipmapCount; - - for ( var f = 0; f < faces; f ++ ) { - - images[ f ] = { mipmaps : [] }; - - for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { - - images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); - images[ f ].format = texDatas.format; - images[ f ].width = texDatas.width; - images[ f ].height = texDatas.height; - - } - - } - - } else { - - texture.image.width = texDatas.width; - texture.image.height = texDatas.height; - texture.mipmaps = texDatas.mipmaps; - - } - - if ( texDatas.mipmapCount === 1 ) { - - texture.minFilter = LinearFilter; - - } - - texture.format = texDatas.format; - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture ); - - }, onProgress, onError ); - - } - - return texture; - - }, - - setPath: function ( value ) { - - this.path = value; - return this; - - } - - } ); - - /** - * @author Nikos M. / https://github.com/foo123/ - * - * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) - */ - - var DataTextureLoader = BinaryTextureLoader; - function BinaryTextureLoader( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - - // override in sub classes - this._parser = null; - - } - - Object.assign( BinaryTextureLoader.prototype, { - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var texture = new DataTexture(); - - var loader = new FileLoader( this.manager ); - loader.setResponseType( 'arraybuffer' ); - - loader.load( url, function ( buffer ) { - - var texData = scope._parser( buffer ); - - if ( ! texData ) return; - - if ( undefined !== texData.image ) { - - texture.image = texData.image; - - } else if ( undefined !== texData.data ) { - - texture.image.width = texData.width; - texture.image.height = texData.height; - texture.image.data = texData.data; - - } - - texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : ClampToEdgeWrapping; - texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : ClampToEdgeWrapping; - - texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : LinearFilter; - texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : LinearMipMapLinearFilter; - - texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1; - - if ( undefined !== texData.format ) { - - texture.format = texData.format; - - } - if ( undefined !== texData.type ) { - - texture.type = texData.type; - - } - - if ( undefined !== texData.mipmaps ) { - - texture.mipmaps = texData.mipmaps; - - } - - if ( 1 === texData.mipmapCount ) { - - texture.minFilter = LinearFilter; - - } - - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture, texData ); - - }, onProgress, onError ); - - - return texture; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function ImageLoader( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - - } - - Object.assign( ImageLoader.prototype, { - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); - image.onload = function () { - - image.onload = null; - - URL.revokeObjectURL( image.src ); - - if ( onLoad ) onLoad( image ); - - scope.manager.itemEnd( url ); - - }; - image.onerror = onError; - - if ( url.indexOf( 'data:' ) === 0 ) { - - image.src = url; - - } else if ( this.crossOrigin !== undefined ) { - - // crossOrigin doesn't work with URL.createObjectURL()? - - image.crossOrigin = this.crossOrigin; - image.src = url; - - } else { - - var loader = new FileLoader(); - loader.setPath( this.path ); - loader.setResponseType( 'blob' ); - loader.setWithCredentials( this.withCredentials ); - loader.load( url, function ( blob ) { - - image.src = URL.createObjectURL( blob ); - - }, onProgress, onError ); - - } - - scope.manager.itemStart( url ); - - return image; - - }, - - setCrossOrigin: function ( value ) { - - this.crossOrigin = value; - return this; - - }, - - setWithCredentials: function ( value ) { - - this.withCredentials = value; - return this; - - }, - - setPath: function ( value ) { - - this.path = value; - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function CubeTextureLoader( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - - } - - Object.assign( CubeTextureLoader.prototype, { - - load: function ( urls, onLoad, onProgress, onError ) { - - var texture = new CubeTexture(); - - var loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); - - var loaded = 0; - - function loadTexture( i ) { - - loader.load( urls[ i ], function ( image ) { - - texture.images[ i ] = image; - - loaded ++; - - if ( loaded === 6 ) { - - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture ); - - } - - }, undefined, onError ); - - } - - for ( var i = 0; i < urls.length; ++ i ) { - - loadTexture( i ); - - } - - return texture; - - }, - - setCrossOrigin: function ( value ) { - - this.crossOrigin = value; - return this; - - }, - - setPath: function ( value ) { - - this.path = value; - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function TextureLoader( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - - } - - Object.assign( TextureLoader.prototype, { - - load: function ( url, onLoad, onProgress, onError ) { - - var texture = new Texture(); - - var loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setWithCredentials( this.withCredentials ); - loader.setPath( this.path ); - loader.load( url, function ( image ) { - - // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB. - var isJPEG = url.search( /\.(jpg|jpeg)$/ ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0; - - texture.format = isJPEG ? RGBFormat : RGBAFormat; - texture.image = image; - texture.needsUpdate = true; - - if ( onLoad !== undefined ) { - - onLoad( texture ); - - } - - }, onProgress, onError ); - - return texture; - - }, - - setCrossOrigin: function ( value ) { - - this.crossOrigin = value; - return this; - - }, - - setWithCredentials: function ( value ) { - - this.withCredentials = value; - return this; - - }, - - setPath: function ( value ) { - - this.path = value; - return this; - - } - - - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - - function Light( color, intensity ) { - - Object3D.call( this ); - - this.type = 'Light'; - - this.color = new Color( color ); - this.intensity = intensity !== undefined ? intensity : 1; - - this.receiveShadow = undefined; - - } - - Light.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Light, - - isLight: true, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - this.intensity = source.intensity; - - return this; - - }, - - toJSON: function ( meta ) { - - var data = Object3D.prototype.toJSON.call( this, meta ); - - data.object.color = this.color.getHex(); - data.object.intensity = this.intensity; - - if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); - - if ( this.distance !== undefined ) data.object.distance = this.distance; - if ( this.angle !== undefined ) data.object.angle = this.angle; - if ( this.decay !== undefined ) data.object.decay = this.decay; - if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; - - if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); - - return data; - - } - - } ); - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function HemisphereLight( skyColor, groundColor, intensity ) { - - Light.call( this, skyColor, intensity ); - - this.type = 'HemisphereLight'; - - this.castShadow = undefined; - - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); - - this.groundColor = new Color( groundColor ); - - } - - HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: HemisphereLight, - - isHemisphereLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.groundColor.copy( source.groundColor ); - - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function LightShadow( camera ) { - - this.camera = camera; - - this.bias = 0; - this.radius = 1; - - this.mapSize = new Vector2( 512, 512 ); - - this.map = null; - this.matrix = new Matrix4(); - - } - - Object.assign( LightShadow.prototype, { - - copy: function ( source ) { - - this.camera = source.camera.clone(); - - this.bias = source.bias; - this.radius = source.radius; - - this.mapSize.copy( source.mapSize ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - toJSON: function () { - - var object = {}; - - if ( this.bias !== 0 ) object.bias = this.bias; - if ( this.radius !== 1 ) object.radius = this.radius; - if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); - - object.camera = this.camera.toJSON( false ).object; - delete object.camera.matrix; - - return object; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function SpotLightShadow() { - - LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) ); - - } - - SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { - - constructor: SpotLightShadow, - - isSpotLightShadow: true, - - update: function ( light ) { - - var fov = _Math.RAD2DEG * 2 * light.angle; - var aspect = this.mapSize.width / this.mapSize.height; - var far = light.distance || 500; - - var camera = this.camera; - - if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { - - camera.fov = fov; - camera.aspect = aspect; - camera.far = far; - camera.updateProjectionMatrix(); - - } - - } - - } ); - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function SpotLight( color, intensity, distance, angle, penumbra, decay ) { - - Light.call( this, color, intensity ); - - this.type = 'SpotLight'; - - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); - - this.target = new Object3D(); - - Object.defineProperty( this, 'power', { - get: function () { - // intensity = power per solid angle. - // ref: equation (17) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf - return this.intensity * Math.PI; - }, - set: function ( power ) { - // intensity = power per solid angle. - // ref: equation (17) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf - this.intensity = power / Math.PI; - } - } ); - - this.distance = ( distance !== undefined ) ? distance : 0; - this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; - this.penumbra = ( penumbra !== undefined ) ? penumbra : 0; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. - - this.shadow = new SpotLightShadow(); - - } - - SpotLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: SpotLight, - - isSpotLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.distance = source.distance; - this.angle = source.angle; - this.penumbra = source.penumbra; - this.decay = source.decay; - - this.target = source.target.clone(); - - this.shadow = source.shadow.clone(); - - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - - function PointLight( color, intensity, distance, decay ) { - - Light.call( this, color, intensity ); - - this.type = 'PointLight'; - - Object.defineProperty( this, 'power', { - get: function () { - // intensity = power per solid angle. - // ref: equation (15) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf - return this.intensity * 4 * Math.PI; - - }, - set: function ( power ) { - // intensity = power per solid angle. - // ref: equation (15) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf - this.intensity = power / ( 4 * Math.PI ); - } - } ); - - this.distance = ( distance !== undefined ) ? distance : 0; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. - - this.shadow = new LightShadow( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); - - } - - PointLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: PointLight, - - isPointLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.distance = source.distance; - this.decay = source.decay; - - this.shadow = source.shadow.clone(); - - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function DirectionalLightShadow( light ) { - - LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); - - } - - DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { - - constructor: DirectionalLightShadow - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - - function DirectionalLight( color, intensity ) { - - Light.call( this, color, intensity ); - - this.type = 'DirectionalLight'; - - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); - - this.target = new Object3D(); - - this.shadow = new DirectionalLightShadow(); - - } - - DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: DirectionalLight, - - isDirectionalLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.target = source.target.clone(); - - this.shadow = source.shadow.clone(); - - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function AmbientLight( color, intensity ) { - - Light.call( this, color, intensity ); - - this.type = 'AmbientLight'; - - this.castShadow = undefined; - - } - - AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: AmbientLight, - - isAmbientLight: true - - } ); - - /** - * @author tschw - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - */ - - var AnimationUtils = { - - // same as Array.prototype.slice, but also works on typed arrays - arraySlice: function( array, from, to ) { - - if ( AnimationUtils.isTypedArray( array ) ) { - - return new array.constructor( array.subarray( from, to ) ); - - } - - return array.slice( from, to ); - - }, - - // converts an array to a specific type - convertArray: function( array, type, forceClone ) { - - if ( ! array || // let 'undefined' and 'null' pass - ! forceClone && array.constructor === type ) return array; - - if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { - - return new type( array ); // create typed array - - } - - return Array.prototype.slice.call( array ); // create Array - - }, - - isTypedArray: function( object ) { - - return ArrayBuffer.isView( object ) && - ! ( object instanceof DataView ); - - }, - - // returns an array by which times and values can be sorted - getKeyframeOrder: function( times ) { - - function compareTime( i, j ) { - - return times[ i ] - times[ j ]; - - } - - var n = times.length; - var result = new Array( n ); - for ( var i = 0; i !== n; ++ i ) result[ i ] = i; - - result.sort( compareTime ); - - return result; - - }, - - // uses the array previously returned by 'getKeyframeOrder' to sort data - sortedArray: function( values, stride, order ) { - - var nValues = values.length; - var result = new values.constructor( nValues ); - - for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { - - var srcOffset = order[ i ] * stride; - - for ( var j = 0; j !== stride; ++ j ) { - - result[ dstOffset ++ ] = values[ srcOffset + j ]; - - } - - } - - return result; - - }, - - // function for parsing AOS keyframe formats - flattenJSON: function( jsonKeys, times, values, valuePropertyName ) { - - var i = 1, key = jsonKeys[ 0 ]; - - while ( key !== undefined && key[ valuePropertyName ] === undefined ) { - - key = jsonKeys[ i ++ ]; - - } - - if ( key === undefined ) return; // no data - - var value = key[ valuePropertyName ]; - if ( value === undefined ) return; // no data - - if ( Array.isArray( value ) ) { - - do { - - value = key[ valuePropertyName ]; - - if ( value !== undefined ) { - - times.push( key.time ); - values.push.apply( values, value ); // push all elements - - } - - key = jsonKeys[ i ++ ]; - - } while ( key !== undefined ); - - } else if ( value.toArray !== undefined ) { - // ...assume THREE.Math-ish - - do { - - value = key[ valuePropertyName ]; - - if ( value !== undefined ) { - - times.push( key.time ); - value.toArray( values, values.length ); - - } - - key = jsonKeys[ i ++ ]; - - } while ( key !== undefined ); - - } else { - // otherwise push as-is - - do { - - value = key[ valuePropertyName ]; - - if ( value !== undefined ) { - - times.push( key.time ); - values.push( value ); - - } - - key = jsonKeys[ i ++ ]; - - } while ( key !== undefined ); - - } - - } - - }; - - /** - * Abstract base class of interpolants over parametric samples. - * - * The parameter domain is one dimensional, typically the time or a path - * along a curve defined by the data. - * - * The sample values can have any dimensionality and derived classes may - * apply special interpretations to the data. - * - * This class provides the interval seek in a Template Method, deferring - * the actual interpolation to derived classes. - * - * Time complexity is O(1) for linear access crossing at most two points - * and O(log N) for random access, where N is the number of positions. - * - * References: - * - * http://www.oodesign.com/template-method-pattern.html - * - * @author tschw - */ - - function Interpolant( - parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - this.parameterPositions = parameterPositions; - this._cachedIndex = 0; - - this.resultBuffer = resultBuffer !== undefined ? - resultBuffer : new sampleValues.constructor( sampleSize ); - this.sampleValues = sampleValues; - this.valueSize = sampleSize; - - } - - Interpolant.prototype = { - - constructor: Interpolant, - - evaluate: function( t ) { - - var pp = this.parameterPositions, - i1 = this._cachedIndex, - - t1 = pp[ i1 ], - t0 = pp[ i1 - 1 ]; - - validate_interval: { - - seek: { - - var right; - - linear_scan: { - //- See http://jsperf.com/comparison-to-undefined/3 - //- slower code: - //- - //- if ( t >= t1 || t1 === undefined ) { - forward_scan: if ( ! ( t < t1 ) ) { - - for ( var giveUpAt = i1 + 2; ;) { - - if ( t1 === undefined ) { - - if ( t < t0 ) break forward_scan; - - // after end - - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t, t0 ); - - } - - if ( i1 === giveUpAt ) break; // this loop - - t0 = t1; - t1 = pp[ ++ i1 ]; - - if ( t < t1 ) { - - // we have arrived at the sought interval - break seek; - - } - - } - - // prepare binary search on the right side of the index - right = pp.length; - break linear_scan; - - } - - //- slower code: - //- if ( t < t0 || t0 === undefined ) { - if ( ! ( t >= t0 ) ) { - - // looping? - - var t1global = pp[ 1 ]; - - if ( t < t1global ) { - - i1 = 2; // + 1, using the scan for the details - t0 = t1global; - - } - - // linear reverse scan - - for ( var giveUpAt = i1 - 2; ;) { - - if ( t0 === undefined ) { - - // before start - - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); - - } - - if ( i1 === giveUpAt ) break; // this loop - - t1 = t0; - t0 = pp[ -- i1 - 1 ]; - - if ( t >= t0 ) { - - // we have arrived at the sought interval - break seek; - - } - - } - - // prepare binary search on the left side of the index - right = i1; - i1 = 0; - break linear_scan; - - } - - // the interval is valid - - break validate_interval; - - } // linear scan - - // binary search - - while ( i1 < right ) { - - var mid = ( i1 + right ) >>> 1; - - if ( t < pp[ mid ] ) { - - right = mid; - - } else { - - i1 = mid + 1; - - } - - } - - t1 = pp[ i1 ]; - t0 = pp[ i1 - 1 ]; - - // check boundary cases, again - - if ( t0 === undefined ) { - - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); - - } - - if ( t1 === undefined ) { - - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t0, t ); - - } - - } // seek - - this._cachedIndex = i1; - - this.intervalChanged_( i1, t0, t1 ); - - } // validate_interval - - return this.interpolate_( i1, t0, t, t1 ); - - }, - - settings: null, // optional, subclass-specific settings structure - // Note: The indirection allows central control of many interpolants. - - // --- Protected interface - - DefaultSettings_: {}, - - getSettings_: function() { - - return this.settings || this.DefaultSettings_; - - }, - - copySampleValue_: function( index ) { - - // copies a sample value to the result buffer - - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - offset = index * stride; - - for ( var i = 0; i !== stride; ++ i ) { - - result[ i ] = values[ offset + i ]; - - } - - return result; - - }, - - // Template methods for derived classes: - - interpolate_: function( i1, t0, t, t1 ) { - - throw new Error( "call to abstract method" ); - // implementations shall return this.resultBuffer - - }, - - intervalChanged_: function( i1, t0, t1 ) { - - // empty - - } - - }; - - Object.assign( Interpolant.prototype, { - - beforeStart_: //( 0, t, t0 ), returns this.resultBuffer - Interpolant.prototype.copySampleValue_, - - afterEnd_: //( N-1, tN-1, t ), returns this.resultBuffer - Interpolant.prototype.copySampleValue_ - - } ); - - /** - * Fast and simple cubic spline interpolant. - * - * It was derived from a Hermitian construction setting the first derivative - * at each sample position to the linear slope between neighboring positions - * over their parameter interval. - * - * @author tschw - */ - - function CubicInterpolant( - parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( - this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - this._weightPrev = -0; - this._offsetPrev = -0; - this._weightNext = -0; - this._offsetNext = -0; - - } - - CubicInterpolant.prototype = - Object.assign( Object.create( Interpolant.prototype ), { - - constructor: CubicInterpolant, - - DefaultSettings_: { - - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding - - }, - - intervalChanged_: function( i1, t0, t1 ) { - - var pp = this.parameterPositions, - iPrev = i1 - 2, - iNext = i1 + 1, - - tPrev = pp[ iPrev ], - tNext = pp[ iNext ]; - - if ( tPrev === undefined ) { - - switch ( this.getSettings_().endingStart ) { - - case ZeroSlopeEnding: - - // f'(t0) = 0 - iPrev = i1; - tPrev = 2 * t0 - t1; - - break; - - case WrapAroundEnding: - - // use the other end of the curve - iPrev = pp.length - 2; - tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; - - break; - - default: // ZeroCurvatureEnding - - // f''(t0) = 0 a.k.a. Natural Spline - iPrev = i1; - tPrev = t1; - - } - - } - - if ( tNext === undefined ) { - - switch ( this.getSettings_().endingEnd ) { - - case ZeroSlopeEnding: - - // f'(tN) = 0 - iNext = i1; - tNext = 2 * t1 - t0; - - break; - - case WrapAroundEnding: - - // use the other end of the curve - iNext = 1; - tNext = t1 + pp[ 1 ] - pp[ 0 ]; - - break; - - default: // ZeroCurvatureEnding - - // f''(tN) = 0, a.k.a. Natural Spline - iNext = i1 - 1; - tNext = t0; - - } - - } - - var halfDt = ( t1 - t0 ) * 0.5, - stride = this.valueSize; - - this._weightPrev = halfDt / ( t0 - tPrev ); - this._weightNext = halfDt / ( tNext - t1 ); - this._offsetPrev = iPrev * stride; - this._offsetNext = iNext * stride; - - }, - - interpolate_: function( i1, t0, t, t1 ) { - - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - - o1 = i1 * stride, o0 = o1 - stride, - oP = this._offsetPrev, oN = this._offsetNext, - wP = this._weightPrev, wN = this._weightNext, - - p = ( t - t0 ) / ( t1 - t0 ), - pp = p * p, - ppp = pp * p; - - // evaluate polynomials - - var sP = - wP * ppp + 2 * wP * pp - wP * p; - var s0 = ( 1 + wP ) * ppp + (-1.5 - 2 * wP ) * pp + ( -0.5 + wP ) * p + 1; - var s1 = (-1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; - var sN = wN * ppp - wN * pp; - - // combine data linearly - - for ( var i = 0; i !== stride; ++ i ) { - - result[ i ] = - sP * values[ oP + i ] + - s0 * values[ o0 + i ] + - s1 * values[ o1 + i ] + - sN * values[ oN + i ]; - - } - - return result; - - } - - } ); - - /** - * @author tschw - */ - - function LinearInterpolant( - parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( - this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - LinearInterpolant.prototype = - Object.assign( Object.create( Interpolant.prototype ), { - - constructor: LinearInterpolant, - - interpolate_: function( i1, t0, t, t1 ) { - - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - - offset1 = i1 * stride, - offset0 = offset1 - stride, - - weight1 = ( t - t0 ) / ( t1 - t0 ), - weight0 = 1 - weight1; - - for ( var i = 0; i !== stride; ++ i ) { - - result[ i ] = - values[ offset0 + i ] * weight0 + - values[ offset1 + i ] * weight1; - - } - - return result; - - } - - } ); - - /** - * - * Interpolant that evaluates to the sample value at the position preceeding - * the parameter. - * - * @author tschw - */ - - function DiscreteInterpolant( - parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( - this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - DiscreteInterpolant.prototype = - Object.assign( Object.create( Interpolant.prototype ), { - - constructor: DiscreteInterpolant, - - interpolate_: function( i1, t0, t, t1 ) { - - return this.copySampleValue_( i1 - 1 ); - - } - - } ); - - var KeyframeTrackPrototype; - - KeyframeTrackPrototype = { - - TimeBufferType: Float32Array, - ValueBufferType: Float32Array, - - DefaultInterpolation: InterpolateLinear, - - InterpolantFactoryMethodDiscrete: function ( result ) { - - return new DiscreteInterpolant( - this.times, this.values, this.getValueSize(), result ); - - }, - - InterpolantFactoryMethodLinear: function ( result ) { - - return new LinearInterpolant( - this.times, this.values, this.getValueSize(), result ); - - }, - - InterpolantFactoryMethodSmooth: function ( result ) { - - return new CubicInterpolant( - this.times, this.values, this.getValueSize(), result ); - - }, - - setInterpolation: function ( interpolation ) { - - var factoryMethod; - - switch ( interpolation ) { - - case InterpolateDiscrete: - - factoryMethod = this.InterpolantFactoryMethodDiscrete; - - break; - - case InterpolateLinear: - - factoryMethod = this.InterpolantFactoryMethodLinear; - - break; - - case InterpolateSmooth: - - factoryMethod = this.InterpolantFactoryMethodSmooth; - - break; - - } - - if ( factoryMethod === undefined ) { - - var message = "unsupported interpolation for " + - this.ValueTypeName + " keyframe track named " + this.name; - - if ( this.createInterpolant === undefined ) { - - // fall back to default, unless the default itself is messed up - if ( interpolation !== this.DefaultInterpolation ) { - - this.setInterpolation( this.DefaultInterpolation ); - - } else { - - throw new Error( message ); // fatal, in this case - - } - - } - - console.warn( message ); - return; - - } - - this.createInterpolant = factoryMethod; - - }, - - getInterpolation: function () { - - switch ( this.createInterpolant ) { - - case this.InterpolantFactoryMethodDiscrete: - - return InterpolateDiscrete; - - case this.InterpolantFactoryMethodLinear: - - return InterpolateLinear; - - case this.InterpolantFactoryMethodSmooth: - - return InterpolateSmooth; - - } - - }, - - getValueSize: function () { - - return this.values.length / this.times.length; - - }, - - // move all keyframes either forwards or backwards in time - shift: function ( timeOffset ) { - - if ( timeOffset !== 0.0 ) { - - var times = this.times; - - for ( var i = 0, n = times.length; i !== n; ++ i ) { - - times[ i ] += timeOffset; - - } - - } - - return this; - - }, - - // scale all keyframe times by a factor (useful for frame <-> seconds conversions) - scale: function ( timeScale ) { - - if ( timeScale !== 1.0 ) { - - var times = this.times; - - for ( var i = 0, n = times.length; i !== n; ++ i ) { - - times[ i ] *= timeScale; - - } - - } - - return this; - - }, - - // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. - // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values - trim: function ( startTime, endTime ) { - - var times = this.times, - nKeys = times.length, - from = 0, - to = nKeys - 1; - - while ( from !== nKeys && times[ from ] < startTime ) ++ from; - while ( to !== - 1 && times[ to ] > endTime ) -- to; - - ++ to; // inclusive -> exclusive bound - - if ( from !== 0 || to !== nKeys ) { - - // empty tracks are forbidden, so keep at least one keyframe - if ( from >= to ) to = Math.max( to, 1 ), from = to - 1; - - var stride = this.getValueSize(); - this.times = AnimationUtils.arraySlice( times, from, to ); - this.values = AnimationUtils. - arraySlice( this.values, from * stride, to * stride ); - - } - - return this; - - }, - - // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable - validate: function () { - - var valid = true; - - var valueSize = this.getValueSize(); - if ( valueSize - Math.floor( valueSize ) !== 0 ) { - - console.error( "invalid value size in track", this ); - valid = false; - - } - - var times = this.times, - values = this.values, - - nKeys = times.length; - - if ( nKeys === 0 ) { - - console.error( "track is empty", this ); - valid = false; - - } - - var prevTime = null; - - for ( var i = 0; i !== nKeys; i ++ ) { - - var currTime = times[ i ]; - - if ( typeof currTime === 'number' && isNaN( currTime ) ) { - - console.error( "time is not a valid number", this, i, currTime ); - valid = false; - break; - - } - - if ( prevTime !== null && prevTime > currTime ) { - - console.error( "out of order keys", this, i, currTime, prevTime ); - valid = false; - break; - - } - - prevTime = currTime; - - } - - if ( values !== undefined ) { - - if ( AnimationUtils.isTypedArray( values ) ) { - - for ( var i = 0, n = values.length; i !== n; ++ i ) { - - var value = values[ i ]; - - if ( isNaN( value ) ) { - - console.error( "value is not a valid number", this, i, value ); - valid = false; - break; - - } - - } - - } - - } - - return valid; - - }, - - // removes equivalent sequential keys as common in morph target sequences - // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) - optimize: function () { - - var times = this.times, - values = this.values, - stride = this.getValueSize(), - - smoothInterpolation = this.getInterpolation() === InterpolateSmooth, - - writeIndex = 1, - lastIndex = times.length - 1; - - for ( var i = 1; i < lastIndex; ++ i ) { - - var keep = false; - - var time = times[ i ]; - var timeNext = times[ i + 1 ]; - - // remove adjacent keyframes scheduled at the same time - - if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { - - if ( ! smoothInterpolation ) { - - // remove unnecessary keyframes same as their neighbors - - var offset = i * stride, - offsetP = offset - stride, - offsetN = offset + stride; - - for ( var j = 0; j !== stride; ++ j ) { - - var value = values[ offset + j ]; - - if ( value !== values[ offsetP + j ] || - value !== values[ offsetN + j ] ) { - - keep = true; - break; - - } - - } - - } else keep = true; - - } - - // in-place compaction - - if ( keep ) { - - if ( i !== writeIndex ) { - - times[ writeIndex ] = times[ i ]; - - var readOffset = i * stride, - writeOffset = writeIndex * stride; - - for ( var j = 0; j !== stride; ++ j ) - - values[ writeOffset + j ] = values[ readOffset + j ]; - - } - - ++ writeIndex; - - } - - } - - // flush last keyframe (compaction looks ahead) - - if ( lastIndex > 0 ) { - - times[ writeIndex ] = times[ lastIndex ]; - - for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) - - values[ writeOffset + j ] = values[ readOffset + j ]; - - ++ writeIndex; - - } - - if ( writeIndex !== times.length ) { - - this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); - this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); - - } - - return this; - - } - - }; - - function KeyframeTrackConstructor( name, times, values, interpolation ) { - - if( name === undefined ) throw new Error( "track name is undefined" ); - - if( times === undefined || times.length === 0 ) { - - throw new Error( "no keyframes in track named " + name ); - - } - - this.name = name; - - this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); - this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); - - this.setInterpolation( interpolation || this.DefaultInterpolation ); - - this.validate(); - this.optimize(); - - } - - /** - * - * A Track of vectored keyframe values. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function VectorKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrackConstructor.call( this, name, times, values, interpolation ); - - } - - VectorKeyframeTrack.prototype = - Object.assign( Object.create( KeyframeTrackPrototype ), { - - constructor: VectorKeyframeTrack, - - ValueTypeName: 'vector' - - // ValueBufferType is inherited - - // DefaultInterpolation is inherited - - } ); - - /** - * Spherical linear unit quaternion interpolant. - * - * @author tschw - */ - - function QuaternionLinearInterpolant( - parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( - this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - QuaternionLinearInterpolant.prototype = - Object.assign( Object.create( Interpolant.prototype ), { - - constructor: QuaternionLinearInterpolant, - - interpolate_: function( i1, t0, t, t1 ) { - - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - - offset = i1 * stride, - - alpha = ( t - t0 ) / ( t1 - t0 ); - - for ( var end = offset + stride; offset !== end; offset += 4 ) { - - Quaternion.slerpFlat( result, 0, - values, offset - stride, values, offset, alpha ); - - } - - return result; - - } - - } ); - - /** - * - * A Track of quaternion keyframe values. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function QuaternionKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrackConstructor.call( this, name, times, values, interpolation ); - - } - - QuaternionKeyframeTrack.prototype = - Object.assign( Object.create( KeyframeTrackPrototype ), { - - constructor: QuaternionKeyframeTrack, - - ValueTypeName: 'quaternion', - - // ValueBufferType is inherited - - DefaultInterpolation: InterpolateLinear, - - InterpolantFactoryMethodLinear: function( result ) { - - return new QuaternionLinearInterpolant( - this.times, this.values, this.getValueSize(), result ); - - }, - - InterpolantFactoryMethodSmooth: undefined // not yet implemented - - } ); - - /** - * - * A Track of numeric keyframe values. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function NumberKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrackConstructor.call( this, name, times, values, interpolation ); - - } - - NumberKeyframeTrack.prototype = - Object.assign( Object.create( KeyframeTrackPrototype ), { - - constructor: NumberKeyframeTrack, - - ValueTypeName: 'number' - - // ValueBufferType is inherited - - // DefaultInterpolation is inherited - - } ); - - /** - * - * A Track that interpolates Strings - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function StringKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrackConstructor.call( this, name, times, values, interpolation ); - - } - - StringKeyframeTrack.prototype = - Object.assign( Object.create( KeyframeTrackPrototype ), { - - constructor: StringKeyframeTrack, - - ValueTypeName: 'string', - ValueBufferType: Array, - - DefaultInterpolation: InterpolateDiscrete, - - InterpolantFactoryMethodLinear: undefined, - - InterpolantFactoryMethodSmooth: undefined - - } ); - - /** - * - * A Track of Boolean keyframe values. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function BooleanKeyframeTrack( name, times, values ) { - - KeyframeTrackConstructor.call( this, name, times, values ); - - } - - BooleanKeyframeTrack.prototype = - Object.assign( Object.create( KeyframeTrackPrototype ), { - - constructor: BooleanKeyframeTrack, - - ValueTypeName: 'bool', - ValueBufferType: Array, - - DefaultInterpolation: InterpolateDiscrete, - - InterpolantFactoryMethodLinear: undefined, - InterpolantFactoryMethodSmooth: undefined - - // Note: Actually this track could have a optimized / compressed - // representation of a single value and a custom interpolant that - // computes "firstValue ^ isOdd( index )". - - } ); - - /** - * - * A Track of keyframe values that represent color. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function ColorKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrackConstructor.call( this, name, times, values, interpolation ); - - } - - ColorKeyframeTrack.prototype = - Object.assign( Object.create( KeyframeTrackPrototype ), { - - constructor: ColorKeyframeTrack, - - ValueTypeName: 'color' - - // ValueBufferType is inherited - - // DefaultInterpolation is inherited - - - // Note: Very basic implementation and nothing special yet. - // However, this is the place for color space parameterization. - - } ); - - /** - * - * A timed sequence of keyframes for a specific property. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function KeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrackConstructor.apply( this, arguments ); - - } - - KeyframeTrack.prototype = KeyframeTrackPrototype; - KeyframeTrackPrototype.constructor = KeyframeTrack; - - // Static methods: - - Object.assign( KeyframeTrack, { - - // Serialization (in static context, because of constructor invocation - // and automatic invocation of .toJSON): - - parse: function( json ) { - - if( json.type === undefined ) { - - throw new Error( "track type undefined, can not parse" ); - - } - - var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type ); - - if ( json.times === undefined ) { - - var times = [], values = []; - - AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); - - json.times = times; - json.values = values; - - } - - // derived classes can define a static parse method - if ( trackType.parse !== undefined ) { - - return trackType.parse( json ); - - } else { - - // by default, we asssume a constructor compatible with the base - return new trackType( - json.name, json.times, json.values, json.interpolation ); - - } - - }, - - toJSON: function( track ) { - - var trackType = track.constructor; - - var json; - - // derived classes can define a static toJSON method - if ( trackType.toJSON !== undefined ) { - - json = trackType.toJSON( track ); - - } else { - - // by default, we assume the data can be serialized as-is - json = { - - 'name': track.name, - 'times': AnimationUtils.convertArray( track.times, Array ), - 'values': AnimationUtils.convertArray( track.values, Array ) - - }; - - var interpolation = track.getInterpolation(); - - if ( interpolation !== track.DefaultInterpolation ) { - - json.interpolation = interpolation; - - } - - } - - json.type = track.ValueTypeName; // mandatory - - return json; - - }, - - _getTrackTypeForValueTypeName: function( typeName ) { - - switch( typeName.toLowerCase() ) { - - case "scalar": - case "double": - case "float": - case "number": - case "integer": - - return NumberKeyframeTrack; - - case "vector": - case "vector2": - case "vector3": - case "vector4": - - return VectorKeyframeTrack; - - case "color": - - return ColorKeyframeTrack; - - case "quaternion": - - return QuaternionKeyframeTrack; - - case "bool": - case "boolean": - - return BooleanKeyframeTrack; - - case "string": - - return StringKeyframeTrack; - - } - - throw new Error( "Unsupported typeName: " + typeName ); - - } - - } ); - - /** - * - * Reusable set of Tracks that represent an animation. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - */ - - function AnimationClip( name, duration, tracks ) { - - this.name = name; - this.tracks = tracks; - this.duration = ( duration !== undefined ) ? duration : -1; - - this.uuid = _Math.generateUUID(); - - // this means it should figure out its duration by scanning the tracks - if ( this.duration < 0 ) { - - this.resetDuration(); - - } - - this.optimize(); - - } - - AnimationClip.prototype = { - - constructor: AnimationClip, - - resetDuration: function() { - - var tracks = this.tracks, - duration = 0; - - for ( var i = 0, n = tracks.length; i !== n; ++ i ) { - - var track = this.tracks[ i ]; - - duration = Math.max( duration, track.times[ track.times.length - 1 ] ); - - } - - this.duration = duration; - - }, - - trim: function() { - - for ( var i = 0; i < this.tracks.length; i ++ ) { - - this.tracks[ i ].trim( 0, this.duration ); - - } - - return this; - - }, - - optimize: function() { - - for ( var i = 0; i < this.tracks.length; i ++ ) { - - this.tracks[ i ].optimize(); - - } - - return this; - - } - - }; - - // Static methods: - - Object.assign( AnimationClip, { - - parse: function( json ) { - - var tracks = [], - jsonTracks = json.tracks, - frameTime = 1.0 / ( json.fps || 1.0 ); - - for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) { - - tracks.push( KeyframeTrack.parse( jsonTracks[ i ] ).scale( frameTime ) ); - - } - - return new AnimationClip( json.name, json.duration, tracks ); - - }, - - - toJSON: function( clip ) { - - var tracks = [], - clipTracks = clip.tracks; - - var json = { - - 'name': clip.name, - 'duration': clip.duration, - 'tracks': tracks - - }; - - for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) { - - tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); - - } - - return json; - - }, - - - CreateFromMorphTargetSequence: function( name, morphTargetSequence, fps, noLoop ) { - - var numMorphTargets = morphTargetSequence.length; - var tracks = []; - - for ( var i = 0; i < numMorphTargets; i ++ ) { - - var times = []; - var values = []; - - times.push( - ( i + numMorphTargets - 1 ) % numMorphTargets, - i, - ( i + 1 ) % numMorphTargets ); - - values.push( 0, 1, 0 ); - - var order = AnimationUtils.getKeyframeOrder( times ); - times = AnimationUtils.sortedArray( times, 1, order ); - values = AnimationUtils.sortedArray( values, 1, order ); - - // if there is a key at the first frame, duplicate it as the - // last frame as well for perfect loop. - if ( ! noLoop && times[ 0 ] === 0 ) { - - times.push( numMorphTargets ); - values.push( values[ 0 ] ); - - } - - tracks.push( - new NumberKeyframeTrack( - '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', - times, values - ).scale( 1.0 / fps ) ); - } - - return new AnimationClip( name, -1, tracks ); - - }, - - findByName: function( objectOrClipArray, name ) { - - var clipArray = objectOrClipArray; - - if ( ! Array.isArray( objectOrClipArray ) ) { - - var o = objectOrClipArray; - clipArray = o.geometry && o.geometry.animations || o.animations; - - } - - for ( var i = 0; i < clipArray.length; i ++ ) { - - if ( clipArray[ i ].name === name ) { - - return clipArray[ i ]; - - } - } - - return null; - - }, - - CreateClipsFromMorphTargetSequences: function( morphTargets, fps, noLoop ) { - - var animationToMorphTargets = {}; - - // tested with https://regex101.com/ on trick sequences - // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 - var pattern = /^([\w-]*?)([\d]+)$/; - - // sort morph target names into animation groups based - // patterns like Walk_001, Walk_002, Run_001, Run_002 - for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { - - var morphTarget = morphTargets[ i ]; - var parts = morphTarget.name.match( pattern ); - - if ( parts && parts.length > 1 ) { - - var name = parts[ 1 ]; - - var animationMorphTargets = animationToMorphTargets[ name ]; - if ( ! animationMorphTargets ) { - - animationToMorphTargets[ name ] = animationMorphTargets = []; - - } - - animationMorphTargets.push( morphTarget ); - - } - - } - - var clips = []; - - for ( var name in animationToMorphTargets ) { - - clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); - - } - - return clips; - - }, - - // parse the animation.hierarchy format - parseAnimation: function( animation, bones ) { - - if ( ! animation ) { - - console.error( " no animation in JSONLoader data" ); - return null; - - } - - var addNonemptyTrack = function( - trackType, trackName, animationKeys, propertyName, destTracks ) { - - // only return track if there are actually keys. - if ( animationKeys.length !== 0 ) { - - var times = []; - var values = []; - - AnimationUtils.flattenJSON( - animationKeys, times, values, propertyName ); - - // empty keys are filtered out, so check again - if ( times.length !== 0 ) { - - destTracks.push( new trackType( trackName, times, values ) ); - - } - - } - - }; - - var tracks = []; - - var clipName = animation.name || 'default'; - // automatic length determination in AnimationClip. - var duration = animation.length || -1; - var fps = animation.fps || 30; - - var hierarchyTracks = animation.hierarchy || []; - - for ( var h = 0; h < hierarchyTracks.length; h ++ ) { - - var animationKeys = hierarchyTracks[ h ].keys; - - // skip empty tracks - if ( ! animationKeys || animationKeys.length === 0 ) continue; - - // process morph targets in a way exactly compatible - // with AnimationHandler.init( animation ) - if ( animationKeys[0].morphTargets ) { - - // figure out all morph targets used in this track - var morphTargetNames = {}; - for ( var k = 0; k < animationKeys.length; k ++ ) { - - if ( animationKeys[k].morphTargets ) { - - for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) { - - morphTargetNames[ animationKeys[k].morphTargets[m] ] = -1; - } - - } - - } - - // create a track for each morph target with all zero - // morphTargetInfluences except for the keys in which - // the morphTarget is named. - for ( var morphTargetName in morphTargetNames ) { - - var times = []; - var values = []; - - for ( var m = 0; m !== animationKeys[k].morphTargets.length; ++ m ) { - - var animationKey = animationKeys[k]; - - times.push( animationKey.time ); - values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); - - } - - tracks.push( new NumberKeyframeTrack('.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); - - } - - duration = morphTargetNames.length * ( fps || 1.0 ); - - } else { - // ...assume skeletal animation - - var boneName = '.bones[' + bones[ h ].name + ']'; - - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.position', - animationKeys, 'pos', tracks ); - - addNonemptyTrack( - QuaternionKeyframeTrack, boneName + '.quaternion', - animationKeys, 'rot', tracks ); - - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.scale', - animationKeys, 'scl', tracks ); - - } - - } - - if ( tracks.length === 0 ) { - - return null; - - } - - var clip = new AnimationClip( clipName, duration, tracks ); - - return clip; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function MaterialLoader( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - this.textures = {}; - - } - - Object.assign( MaterialLoader.prototype, { - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var loader = new FileLoader( scope.manager ); - loader.load( url, function ( text ) { - - onLoad( scope.parse( JSON.parse( text ) ) ); - - }, onProgress, onError ); - - }, - - setTextures: function ( value ) { - - this.textures = value; - - }, - - parse: function ( json ) { - - var textures = this.textures; - - function getTexture( name ) { - - if ( textures[ name ] === undefined ) { - - console.warn( 'THREE.MaterialLoader: Undefined texture', name ); - - } - - return textures[ name ]; - - } - - var material = new Materials[ json.type ](); - - if ( json.uuid !== undefined ) material.uuid = json.uuid; - if ( json.name !== undefined ) material.name = json.name; - if ( json.color !== undefined ) material.color.setHex( json.color ); - if ( json.roughness !== undefined ) material.roughness = json.roughness; - if ( json.metalness !== undefined ) material.metalness = json.metalness; - if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); - if ( json.specular !== undefined ) material.specular.setHex( json.specular ); - if ( json.shininess !== undefined ) material.shininess = json.shininess; - if ( json.clearCoat !== undefined ) material.clearCoat = json.clearCoat; - if ( json.clearCoatRoughness !== undefined ) material.clearCoatRoughness = json.clearCoatRoughness; - if ( json.uniforms !== undefined ) material.uniforms = json.uniforms; - if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; - if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; - if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; - if ( json.fog !== undefined ) material.fog = json.fog; - if ( json.shading !== undefined ) material.shading = json.shading; - if ( json.blending !== undefined ) material.blending = json.blending; - if ( json.side !== undefined ) material.side = json.side; - if ( json.opacity !== undefined ) material.opacity = json.opacity; - if ( json.transparent !== undefined ) material.transparent = json.transparent; - if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; - if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; - if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; - if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite; - if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; - if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; - if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap; - if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin; - if ( json.skinning !== undefined ) material.skinning = json.skinning; - if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets; - - // for PointsMaterial - - if ( json.size !== undefined ) material.size = json.size; - if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; - - // maps - - if ( json.map !== undefined ) material.map = getTexture( json.map ); - - if ( json.alphaMap !== undefined ) { - - material.alphaMap = getTexture( json.alphaMap ); - material.transparent = true; - - } - - if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap ); - if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; - - if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap ); - if ( json.normalScale !== undefined ) { - - var normalScale = json.normalScale; - - if ( Array.isArray( normalScale ) === false ) { - - // Blender exporter used to export a scalar. See #7459 - - normalScale = [ normalScale, normalScale ]; - - } - - material.normalScale = new Vector2().fromArray( normalScale ); - - } - - if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap ); - if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; - if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; - - if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap ); - if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap ); - - if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap ); - if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; - - if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap ); - - if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap ); - - if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; - - if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap ); - if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; - - if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap ); - if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; - - if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap ); - - // MultiMaterial - - if ( json.materials !== undefined ) { - - for ( var i = 0, l = json.materials.length; i < l; i ++ ) { - - material.materials.push( this.parse( json.materials[ i ] ) ); - - } - - } - - return material; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function BufferGeometryLoader( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - - } - - Object.assign( BufferGeometryLoader.prototype, { - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var loader = new FileLoader( scope.manager ); - loader.load( url, function ( text ) { - - onLoad( scope.parse( JSON.parse( text ) ) ); - - }, onProgress, onError ); - - }, - - parse: function ( json ) { - - var geometry = new BufferGeometry(); - - var index = json.data.index; - - var TYPED_ARRAYS = { - 'Int8Array': Int8Array, - 'Uint8Array': Uint8Array, - 'Uint8ClampedArray': Uint8ClampedArray, - 'Int16Array': Int16Array, - 'Uint16Array': Uint16Array, - 'Int32Array': Int32Array, - 'Uint32Array': Uint32Array, - 'Float32Array': Float32Array, - 'Float64Array': Float64Array - }; - - if ( index !== undefined ) { - - var typedArray = new TYPED_ARRAYS[ index.type ]( index.array ); - geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); - - } - - var attributes = json.data.attributes; - - for ( var key in attributes ) { - - var attribute = attributes[ key ]; - var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); - - geometry.addAttribute( key, new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ) ); - - } - - var groups = json.data.groups || json.data.drawcalls || json.data.offsets; - - if ( groups !== undefined ) { - - for ( var i = 0, n = groups.length; i !== n; ++ i ) { - - var group = groups[ i ]; - - geometry.addGroup( group.start, group.count, group.materialIndex ); - - } - - } - - var boundingSphere = json.data.boundingSphere; - - if ( boundingSphere !== undefined ) { - - var center = new Vector3(); - - if ( boundingSphere.center !== undefined ) { - - center.fromArray( boundingSphere.center ); - - } - - geometry.boundingSphere = new Sphere( center, boundingSphere.radius ); - - } - - return geometry; - - } - - } ); - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function Loader() { - - this.onLoadStart = function () {}; - this.onLoadProgress = function () {}; - this.onLoadComplete = function () {}; - - } - - Loader.prototype = { - - constructor: Loader, - - crossOrigin: undefined, - - extractUrlBase: function ( url ) { - - var parts = url.split( '/' ); - - if ( parts.length === 1 ) return './'; - - parts.pop(); - - return parts.join( '/' ) + '/'; - - }, - - initMaterials: function ( materials, texturePath, crossOrigin ) { - - var array = []; - - for ( var i = 0; i < materials.length; ++ i ) { - - array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); - - } - - return array; - - }, - - createMaterial: ( function () { - - var color, textureLoader, materialLoader; - - return function createMaterial( m, texturePath, crossOrigin ) { - - if ( color === undefined ) color = new Color(); - if ( textureLoader === undefined ) textureLoader = new TextureLoader(); - if ( materialLoader === undefined ) materialLoader = new MaterialLoader(); - - // convert from old material format - - var textures = {}; - - function loadTexture( path, repeat, offset, wrap, anisotropy ) { - - var fullPath = texturePath + path; - var loader = Loader.Handlers.get( fullPath ); - - var texture; - - if ( loader !== null ) { - - texture = loader.load( fullPath ); - - } else { - - textureLoader.setCrossOrigin( crossOrigin ); - texture = textureLoader.load( fullPath ); - - } - - if ( repeat !== undefined ) { - - texture.repeat.fromArray( repeat ); - - if ( repeat[ 0 ] !== 1 ) texture.wrapS = RepeatWrapping; - if ( repeat[ 1 ] !== 1 ) texture.wrapT = RepeatWrapping; - - } - - if ( offset !== undefined ) { - - texture.offset.fromArray( offset ); - - } - - if ( wrap !== undefined ) { - - if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = RepeatWrapping; - if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = MirroredRepeatWrapping; - - if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = RepeatWrapping; - if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = MirroredRepeatWrapping; - - } - - if ( anisotropy !== undefined ) { - - texture.anisotropy = anisotropy; - - } - - var uuid = _Math.generateUUID(); - - textures[ uuid ] = texture; - - return uuid; - - } - - // - - var json = { - uuid: _Math.generateUUID(), - type: 'MeshLambertMaterial' - }; - - for ( var name in m ) { - - var value = m[ name ]; - - switch ( name ) { - case 'DbgColor': - case 'DbgIndex': - case 'opticalDensity': - case 'illumination': - break; - case 'DbgName': - json.name = value; - break; - case 'blending': - json.blending = BlendingMode[ value ]; - break; - case 'colorAmbient': - case 'mapAmbient': - console.warn( 'THREE.Loader.createMaterial:', name, 'is no longer supported.' ); - break; - case 'colorDiffuse': - json.color = color.fromArray( value ).getHex(); - break; - case 'colorSpecular': - json.specular = color.fromArray( value ).getHex(); - break; - case 'colorEmissive': - json.emissive = color.fromArray( value ).getHex(); - break; - case 'specularCoef': - json.shininess = value; - break; - case 'shading': - if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial'; - if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial'; - if ( value.toLowerCase() === 'standard' ) json.type = 'MeshStandardMaterial'; - break; - case 'mapDiffuse': - json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); - break; - case 'mapDiffuseRepeat': - case 'mapDiffuseOffset': - case 'mapDiffuseWrap': - case 'mapDiffuseAnisotropy': - break; - case 'mapEmissive': - json.emissiveMap = loadTexture( value, m.mapEmissiveRepeat, m.mapEmissiveOffset, m.mapEmissiveWrap, m.mapEmissiveAnisotropy ); - break; - case 'mapEmissiveRepeat': - case 'mapEmissiveOffset': - case 'mapEmissiveWrap': - case 'mapEmissiveAnisotropy': - break; - case 'mapLight': - json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); - break; - case 'mapLightRepeat': - case 'mapLightOffset': - case 'mapLightWrap': - case 'mapLightAnisotropy': - break; - case 'mapAO': - json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy ); - break; - case 'mapAORepeat': - case 'mapAOOffset': - case 'mapAOWrap': - case 'mapAOAnisotropy': - break; - case 'mapBump': - json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); - break; - case 'mapBumpScale': - json.bumpScale = value; - break; - case 'mapBumpRepeat': - case 'mapBumpOffset': - case 'mapBumpWrap': - case 'mapBumpAnisotropy': - break; - case 'mapNormal': - json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); - break; - case 'mapNormalFactor': - json.normalScale = [ value, value ]; - break; - case 'mapNormalRepeat': - case 'mapNormalOffset': - case 'mapNormalWrap': - case 'mapNormalAnisotropy': - break; - case 'mapSpecular': - json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); - break; - case 'mapSpecularRepeat': - case 'mapSpecularOffset': - case 'mapSpecularWrap': - case 'mapSpecularAnisotropy': - break; - case 'mapMetalness': - json.metalnessMap = loadTexture( value, m.mapMetalnessRepeat, m.mapMetalnessOffset, m.mapMetalnessWrap, m.mapMetalnessAnisotropy ); - break; - case 'mapMetalnessRepeat': - case 'mapMetalnessOffset': - case 'mapMetalnessWrap': - case 'mapMetalnessAnisotropy': - break; - case 'mapRoughness': - json.roughnessMap = loadTexture( value, m.mapRoughnessRepeat, m.mapRoughnessOffset, m.mapRoughnessWrap, m.mapRoughnessAnisotropy ); - break; - case 'mapRoughnessRepeat': - case 'mapRoughnessOffset': - case 'mapRoughnessWrap': - case 'mapRoughnessAnisotropy': - break; - case 'mapAlpha': - json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); - break; - case 'mapAlphaRepeat': - case 'mapAlphaOffset': - case 'mapAlphaWrap': - case 'mapAlphaAnisotropy': - break; - case 'flipSided': - json.side = BackSide; - break; - case 'doubleSided': - json.side = DoubleSide; - break; - case 'transparency': - console.warn( 'THREE.Loader.createMaterial: transparency has been renamed to opacity' ); - json.opacity = value; - break; - case 'depthTest': - case 'depthWrite': - case 'colorWrite': - case 'opacity': - case 'reflectivity': - case 'transparent': - case 'visible': - case 'wireframe': - json[ name ] = value; - break; - case 'vertexColors': - if ( value === true ) json.vertexColors = VertexColors; - if ( value === 'face' ) json.vertexColors = FaceColors; - break; - default: - console.error( 'THREE.Loader.createMaterial: Unsupported', name, value ); - break; - } - - } - - if ( json.type === 'MeshBasicMaterial' ) delete json.emissive; - if ( json.type !== 'MeshPhongMaterial' ) delete json.specular; - - if ( json.opacity < 1 ) json.transparent = true; - - materialLoader.setTextures( textures ); - - return materialLoader.parse( json ); - - }; - - } )() - - }; - - Loader.Handlers = { - - handlers: [], - - add: function ( regex, loader ) { - - this.handlers.push( regex, loader ); - - }, - - get: function ( file ) { - - var handlers = this.handlers; - - for ( var i = 0, l = handlers.length; i < l; i += 2 ) { - - var regex = handlers[ i ]; - var loader = handlers[ i + 1 ]; - - if ( regex.test( file ) ) { - - return loader; - - } - - } - - return null; - - } - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - - function JSONLoader( manager ) { - - if ( typeof manager === 'boolean' ) { - - console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' ); - manager = undefined; - - } - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - - this.withCredentials = false; - - } - - Object.assign( JSONLoader.prototype, { - - load: function( url, onLoad, onProgress, onError ) { - - var scope = this; - - var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : Loader.prototype.extractUrlBase( url ); - - var loader = new FileLoader( this.manager ); - loader.setWithCredentials( this.withCredentials ); - loader.load( url, function ( text ) { - - var json = JSON.parse( text ); - var metadata = json.metadata; - - if ( metadata !== undefined ) { - - var type = metadata.type; - - if ( type !== undefined ) { - - if ( type.toLowerCase() === 'object' ) { - - console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' ); - return; - - } - - if ( type.toLowerCase() === 'scene' ) { - - console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' ); - return; - - } - - } - - } - - var object = scope.parse( json, texturePath ); - onLoad( object.geometry, object.materials ); - - }, onProgress, onError ); - - }, - - setTexturePath: function ( value ) { - - this.texturePath = value; - - }, - - parse: function ( json, texturePath ) { - - var geometry = new Geometry(), - scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; - - parseModel( scale ); - - parseSkin(); - parseMorphing( scale ); - parseAnimations(); - - geometry.computeFaceNormals(); - geometry.computeBoundingSphere(); - - function parseModel( scale ) { - - function isBitSet( value, position ) { - - return value & ( 1 << position ); - - } - - var i, j, fi, - - offset, zLength, - - colorIndex, normalIndex, uvIndex, materialIndex, - - type, - isQuad, - hasMaterial, - hasFaceVertexUv, - hasFaceNormal, hasFaceVertexNormal, - hasFaceColor, hasFaceVertexColor, - - vertex, face, faceA, faceB, hex, normal, - - uvLayer, uv, u, v, - - faces = json.faces, - vertices = json.vertices, - normals = json.normals, - colors = json.colors, - - nUvLayers = 0; - - if ( json.uvs !== undefined ) { - - // disregard empty arrays - - for ( i = 0; i < json.uvs.length; i ++ ) { - - if ( json.uvs[ i ].length ) nUvLayers ++; - - } - - for ( i = 0; i < nUvLayers; i ++ ) { - - geometry.faceVertexUvs[ i ] = []; - - } - - } - - offset = 0; - zLength = vertices.length; - - while ( offset < zLength ) { - - vertex = new Vector3(); - - vertex.x = vertices[ offset ++ ] * scale; - vertex.y = vertices[ offset ++ ] * scale; - vertex.z = vertices[ offset ++ ] * scale; - - geometry.vertices.push( vertex ); - - } - - offset = 0; - zLength = faces.length; - - while ( offset < zLength ) { - - type = faces[ offset ++ ]; - - - isQuad = isBitSet( type, 0 ); - hasMaterial = isBitSet( type, 1 ); - hasFaceVertexUv = isBitSet( type, 3 ); - hasFaceNormal = isBitSet( type, 4 ); - hasFaceVertexNormal = isBitSet( type, 5 ); - hasFaceColor = isBitSet( type, 6 ); - hasFaceVertexColor = isBitSet( type, 7 ); - - // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); - - if ( isQuad ) { - - faceA = new Face3(); - faceA.a = faces[ offset ]; - faceA.b = faces[ offset + 1 ]; - faceA.c = faces[ offset + 3 ]; - - faceB = new Face3(); - faceB.a = faces[ offset + 1 ]; - faceB.b = faces[ offset + 2 ]; - faceB.c = faces[ offset + 3 ]; - - offset += 4; - - if ( hasMaterial ) { - - materialIndex = faces[ offset ++ ]; - faceA.materialIndex = materialIndex; - faceB.materialIndex = materialIndex; - - } - - // to get face <=> uv index correspondence - - fi = geometry.faces.length; - - if ( hasFaceVertexUv ) { - - for ( i = 0; i < nUvLayers; i ++ ) { - - uvLayer = json.uvs[ i ]; - - geometry.faceVertexUvs[ i ][ fi ] = []; - geometry.faceVertexUvs[ i ][ fi + 1 ] = []; - - for ( j = 0; j < 4; j ++ ) { - - uvIndex = faces[ offset ++ ]; - - u = uvLayer[ uvIndex * 2 ]; - v = uvLayer[ uvIndex * 2 + 1 ]; - - uv = new Vector2( u, v ); - - if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); - if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); - - } - - } - - } - - if ( hasFaceNormal ) { - - normalIndex = faces[ offset ++ ] * 3; - - faceA.normal.set( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); - - faceB.normal.copy( faceA.normal ); - - } - - if ( hasFaceVertexNormal ) { - - for ( i = 0; i < 4; i ++ ) { - - normalIndex = faces[ offset ++ ] * 3; - - normal = new Vector3( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); - - - if ( i !== 2 ) faceA.vertexNormals.push( normal ); - if ( i !== 0 ) faceB.vertexNormals.push( normal ); - - } - - } - - - if ( hasFaceColor ) { - - colorIndex = faces[ offset ++ ]; - hex = colors[ colorIndex ]; - - faceA.color.setHex( hex ); - faceB.color.setHex( hex ); - - } - - - if ( hasFaceVertexColor ) { - - for ( i = 0; i < 4; i ++ ) { - - colorIndex = faces[ offset ++ ]; - hex = colors[ colorIndex ]; - - if ( i !== 2 ) faceA.vertexColors.push( new Color( hex ) ); - if ( i !== 0 ) faceB.vertexColors.push( new Color( hex ) ); - - } - - } - - geometry.faces.push( faceA ); - geometry.faces.push( faceB ); - - } else { - - face = new Face3(); - face.a = faces[ offset ++ ]; - face.b = faces[ offset ++ ]; - face.c = faces[ offset ++ ]; - - if ( hasMaterial ) { - - materialIndex = faces[ offset ++ ]; - face.materialIndex = materialIndex; - - } - - // to get face <=> uv index correspondence - - fi = geometry.faces.length; - - if ( hasFaceVertexUv ) { - - for ( i = 0; i < nUvLayers; i ++ ) { - - uvLayer = json.uvs[ i ]; - - geometry.faceVertexUvs[ i ][ fi ] = []; - - for ( j = 0; j < 3; j ++ ) { - - uvIndex = faces[ offset ++ ]; - - u = uvLayer[ uvIndex * 2 ]; - v = uvLayer[ uvIndex * 2 + 1 ]; - - uv = new Vector2( u, v ); - - geometry.faceVertexUvs[ i ][ fi ].push( uv ); - - } - - } - - } - - if ( hasFaceNormal ) { - - normalIndex = faces[ offset ++ ] * 3; - - face.normal.set( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); - - } - - if ( hasFaceVertexNormal ) { - - for ( i = 0; i < 3; i ++ ) { - - normalIndex = faces[ offset ++ ] * 3; - - normal = new Vector3( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); - - face.vertexNormals.push( normal ); - - } - - } - - - if ( hasFaceColor ) { - - colorIndex = faces[ offset ++ ]; - face.color.setHex( colors[ colorIndex ] ); - - } - - - if ( hasFaceVertexColor ) { - - for ( i = 0; i < 3; i ++ ) { - - colorIndex = faces[ offset ++ ]; - face.vertexColors.push( new Color( colors[ colorIndex ] ) ); - - } - - } - - geometry.faces.push( face ); - - } - - } - - } - - function parseSkin() { - - var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; - - if ( json.skinWeights ) { - - for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { - - var x = json.skinWeights[ i ]; - var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; - var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; - var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; - - geometry.skinWeights.push( new Vector4( x, y, z, w ) ); - - } - - } - - if ( json.skinIndices ) { - - for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { - - var a = json.skinIndices[ i ]; - var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; - var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; - var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; - - geometry.skinIndices.push( new Vector4( a, b, c, d ) ); - - } - - } - - geometry.bones = json.bones; - - if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { - - console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + - geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); - - } - - } - - function parseMorphing( scale ) { - - if ( json.morphTargets !== undefined ) { - - for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) { - - geometry.morphTargets[ i ] = {}; - geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; - geometry.morphTargets[ i ].vertices = []; - - var dstVertices = geometry.morphTargets[ i ].vertices; - var srcVertices = json.morphTargets[ i ].vertices; - - for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) { - - var vertex = new Vector3(); - vertex.x = srcVertices[ v ] * scale; - vertex.y = srcVertices[ v + 1 ] * scale; - vertex.z = srcVertices[ v + 2 ] * scale; - - dstVertices.push( vertex ); - - } - - } - - } - - if ( json.morphColors !== undefined && json.morphColors.length > 0 ) { - - console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' ); - - var faces = geometry.faces; - var morphColors = json.morphColors[ 0 ].colors; - - for ( var i = 0, l = faces.length; i < l; i ++ ) { - - faces[ i ].color.fromArray( morphColors, i * 3 ); - - } - - } - - } - - function parseAnimations() { - - var outputAnimations = []; - - // parse old style Bone/Hierarchy animations - var animations = []; - - if ( json.animation !== undefined ) { - - animations.push( json.animation ); - - } - - if ( json.animations !== undefined ) { - - if ( json.animations.length ) { - - animations = animations.concat( json.animations ); - - } else { - - animations.push( json.animations ); - - } - - } - - for ( var i = 0; i < animations.length; i ++ ) { - - var clip = AnimationClip.parseAnimation( animations[ i ], geometry.bones ); - if ( clip ) outputAnimations.push( clip ); - - } - - // parse implicit morph animations - if ( geometry.morphTargets ) { - - // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary. - var morphAnimationClips = AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ); - outputAnimations = outputAnimations.concat( morphAnimationClips ); - - } - - if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations; - - } - - if ( json.materials === undefined || json.materials.length === 0 ) { - - return { geometry: geometry }; - - } else { - - var materials = Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); - - return { geometry: geometry, materials: materials }; - - } - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function ObjectLoader( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - this.texturePath = ''; - - } - - Object.assign( ObjectLoader.prototype, { - - load: function ( url, onLoad, onProgress, onError ) { - - if ( this.texturePath === '' ) { - - this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 ); - - } - - var scope = this; - - var loader = new FileLoader( scope.manager ); - loader.load( url, function ( text ) { - - var json = null; - - try { - - json = JSON.parse( text ); - - } catch ( error ) { - - console.error( 'THREE:ObjectLoader: Can\'t parse ' + url + '.', error.message ); - return; - - } - - var metadata = json.metadata; - - if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) { - - console.error( 'THREE.ObjectLoader: Can\'t load ' + url + '. Use THREE.JSONLoader instead.' ); - return; - - } - - scope.parse( json, onLoad ); - - }, onProgress, onError ); - - }, - - setTexturePath: function ( value ) { - - this.texturePath = value; - - }, - - setCrossOrigin: function ( value ) { - - this.crossOrigin = value; - - }, - - parse: function ( json, onLoad ) { - - var geometries = this.parseGeometries( json.geometries ); - - var images = this.parseImages( json.images, function () { - - if ( onLoad !== undefined ) onLoad( object ); - - } ); - - var textures = this.parseTextures( json.textures, images ); - var materials = this.parseMaterials( json.materials, textures ); - - var object = this.parseObject( json.object, geometries, materials ); - - if ( json.animations ) { - - object.animations = this.parseAnimations( json.animations ); - - } - - if ( json.images === undefined || json.images.length === 0 ) { - - if ( onLoad !== undefined ) onLoad( object ); - - } - - return object; - - }, - - parseGeometries: function ( json ) { - - var geometries = {}; - - if ( json !== undefined ) { - - var geometryLoader = new JSONLoader(); - var bufferGeometryLoader = new BufferGeometryLoader(); - - for ( var i = 0, l = json.length; i < l; i ++ ) { - - var geometry; - var data = json[ i ]; - - switch ( data.type ) { - - case 'PlaneGeometry': - case 'PlaneBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.width, - data.height, - data.widthSegments, - data.heightSegments - ); - - break; - - case 'BoxGeometry': - case 'BoxBufferGeometry': - case 'CubeGeometry': // backwards compatible - - geometry = new Geometries[ data.type ]( - data.width, - data.height, - data.depth, - data.widthSegments, - data.heightSegments, - data.depthSegments - ); - - break; - - case 'CircleGeometry': - case 'CircleBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.segments, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'CylinderGeometry': - case 'CylinderBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radiusTop, - data.radiusBottom, - data.height, - data.radialSegments, - data.heightSegments, - data.openEnded, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'ConeGeometry': - case 'ConeBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.height, - data.radialSegments, - data.heightSegments, - data.openEnded, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'SphereGeometry': - case 'SphereBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.widthSegments, - data.heightSegments, - data.phiStart, - data.phiLength, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'DodecahedronGeometry': - case 'IcosahedronGeometry': - case 'OctahedronGeometry': - case 'TetrahedronGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.detail - ); - - break; - - case 'RingGeometry': - case 'RingBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.innerRadius, - data.outerRadius, - data.thetaSegments, - data.phiSegments, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'TorusGeometry': - case 'TorusBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.tube, - data.radialSegments, - data.tubularSegments, - data.arc - ); - - break; - - case 'TorusKnotGeometry': - case 'TorusKnotBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.tube, - data.tubularSegments, - data.radialSegments, - data.p, - data.q - ); - - break; - - case 'LatheGeometry': - case 'LatheBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.points, - data.segments, - data.phiStart, - data.phiLength - ); - - break; - - case 'BufferGeometry': - - geometry = bufferGeometryLoader.parse( data ); - - break; - - case 'Geometry': - - geometry = geometryLoader.parse( data.data, this.texturePath ).geometry; - - break; - - default: - - console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); - - continue; - - } - - geometry.uuid = data.uuid; - - if ( data.name !== undefined ) geometry.name = data.name; - - geometries[ data.uuid ] = geometry; - - } - - } - - return geometries; - - }, - - parseMaterials: function ( json, textures ) { - - var materials = {}; - - if ( json !== undefined ) { - - var loader = new MaterialLoader(); - loader.setTextures( textures ); - - for ( var i = 0, l = json.length; i < l; i ++ ) { - - var material = loader.parse( json[ i ] ); - materials[ material.uuid ] = material; - - } - - } - - return materials; - - }, - - parseAnimations: function ( json ) { - - var animations = []; - - for ( var i = 0; i < json.length; i ++ ) { - - var clip = AnimationClip.parse( json[ i ] ); - - animations.push( clip ); - - } - - return animations; - - }, - - parseImages: function ( json, onLoad ) { - - var scope = this; - var images = {}; - - function loadImage( url ) { - - scope.manager.itemStart( url ); - - return loader.load( url, function () { - - scope.manager.itemEnd( url ); - - }, undefined, function () { - - scope.manager.itemError( url ); - - } ); - - } - - if ( json !== undefined && json.length > 0 ) { - - var manager = new LoadingManager( onLoad ); - - var loader = new ImageLoader( manager ); - loader.setCrossOrigin( this.crossOrigin ); - - for ( var i = 0, l = json.length; i < l; i ++ ) { - - var image = json[ i ]; - var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url; - - images[ image.uuid ] = loadImage( path ); - - } - - } - - return images; - - }, - - parseTextures: function ( json, images ) { - - function parseConstant( value, type ) { - - if ( typeof( value ) === 'number' ) return value; - - console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); - - return type[ value ]; - - } - - var textures = {}; - - if ( json !== undefined ) { - - for ( var i = 0, l = json.length; i < l; i ++ ) { - - var data = json[ i ]; - - if ( data.image === undefined ) { - - console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); - - } - - if ( images[ data.image ] === undefined ) { - - console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); - - } - - var texture = new Texture( images[ data.image ] ); - texture.needsUpdate = true; - - texture.uuid = data.uuid; - - if ( data.name !== undefined ) texture.name = data.name; - - if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TextureMapping ); - - if ( data.offset !== undefined ) texture.offset.fromArray( data.offset ); - if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat ); - if ( data.wrap !== undefined ) { - - texture.wrapS = parseConstant( data.wrap[ 0 ], TextureWrapping ); - texture.wrapT = parseConstant( data.wrap[ 1 ], TextureWrapping ); - - } - - if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TextureFilter ); - if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TextureFilter ); - if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; - - if ( data.flipY !== undefined ) texture.flipY = data.flipY; - - textures[ data.uuid ] = texture; - - } - - } - - return textures; - - }, - - parseObject: function () { - - var matrix = new Matrix4(); - - return function parseObject( data, geometries, materials ) { - - var object; - - function getGeometry( name ) { - - if ( geometries[ name ] === undefined ) { - - console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); - - } - - return geometries[ name ]; - - } - - function getMaterial( name ) { - - if ( name === undefined ) return undefined; - - if ( materials[ name ] === undefined ) { - - console.warn( 'THREE.ObjectLoader: Undefined material', name ); - - } - - return materials[ name ]; - - } - - switch ( data.type ) { - - case 'Scene': - - object = new Scene(); - - if ( data.background !== undefined ) { - - if ( Number.isInteger( data.background ) ) { - - object.background = new Color( data.background ); - - } - - } - - if ( data.fog !== undefined ) { - - if ( data.fog.type === 'Fog' ) { - - object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far ); - - } else if ( data.fog.type === 'FogExp2' ) { - - object.fog = new FogExp2( data.fog.color, data.fog.density ); - - } - - } - - break; - - case 'PerspectiveCamera': - - object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); - - if ( data.focus !== undefined ) object.focus = data.focus; - if ( data.zoom !== undefined ) object.zoom = data.zoom; - if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge; - if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset; - if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); - - break; - - case 'OrthographicCamera': - - object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); - - break; - - case 'AmbientLight': - - object = new AmbientLight( data.color, data.intensity ); - - break; - - case 'DirectionalLight': - - object = new DirectionalLight( data.color, data.intensity ); - - break; - - case 'PointLight': - - object = new PointLight( data.color, data.intensity, data.distance, data.decay ); - - break; - - case 'SpotLight': - - object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); - - break; - - case 'HemisphereLight': - - object = new HemisphereLight( data.color, data.groundColor, data.intensity ); - - break; - - case 'Mesh': - - var geometry = getGeometry( data.geometry ); - var material = getMaterial( data.material ); - - if ( geometry.bones && geometry.bones.length > 0 ) { - - object = new SkinnedMesh( geometry, material ); - - } else { - - object = new Mesh( geometry, material ); - - } - - break; - - case 'LOD': - - object = new LOD(); - - break; - - case 'Line': - - object = new Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); - - break; - - case 'LineSegments': - - object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) ); - - break; - - case 'PointCloud': - case 'Points': - - object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) ); - - break; - - case 'Sprite': - - object = new Sprite( getMaterial( data.material ) ); - - break; - - case 'Group': - - object = new Group(); - - break; - - case 'SkinnedMesh': - - console.warn( 'THREE.ObjectLoader.parseObject() does not support SkinnedMesh type. Instantiates Object3D instead.' ); - - default: - - object = new Object3D(); - - } - - object.uuid = data.uuid; - - if ( data.name !== undefined ) object.name = data.name; - if ( data.matrix !== undefined ) { - - matrix.fromArray( data.matrix ); - matrix.decompose( object.position, object.quaternion, object.scale ); - - } else { - - if ( data.position !== undefined ) object.position.fromArray( data.position ); - if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); - if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion ); - if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); - - } - - if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; - if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; - - if ( data.shadow ) { - - if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias; - if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius; - if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize ); - if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera ); - - } - - if ( data.visible !== undefined ) object.visible = data.visible; - if ( data.userData !== undefined ) object.userData = data.userData; - - if ( data.children !== undefined ) { - - for ( var child in data.children ) { - - object.add( this.parseObject( data.children[ child ], geometries, materials ) ); - - } - - } - - if ( data.type === 'LOD' ) { - - var levels = data.levels; - - for ( var l = 0; l < levels.length; l ++ ) { - - var level = levels[ l ]; - var child = object.getObjectByProperty( 'uuid', level.object ); - - if ( child !== undefined ) { - - object.addLevel( child, level.distance ); - - } - - } - - } - - return object; - - }; - - }() - - } ); - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Extensible curve object - * - * Some common of Curve methods - * .getPoint(t), getTangent(t) - * .getPointAt(u), getTangentAt(u) - * .getPoints(), .getSpacedPoints() - * .getLength() - * .updateArcLengths() - * - * This following classes subclasses THREE.Curve: - * - * -- 2d classes -- - * THREE.LineCurve - * THREE.QuadraticBezierCurve - * THREE.CubicBezierCurve - * THREE.SplineCurve - * THREE.ArcCurve - * THREE.EllipseCurve - * - * -- 3d classes -- - * THREE.LineCurve3 - * THREE.QuadraticBezierCurve3 - * THREE.CubicBezierCurve3 - * THREE.SplineCurve3 - * - * A series of curves can be represented as a THREE.CurvePath - * - **/ - - /************************************************************** - * Abstract Curve base class - **************************************************************/ - - function Curve() {} - - Curve.prototype = { - - constructor: Curve, - - // Virtual base class method to overwrite and implement in subclasses - // - t [0 .. 1] - - getPoint: function ( t ) { - - console.warn( "THREE.Curve: Warning, getPoint() not implemented!" ); - return null; - - }, - - // Get point at relative position in curve according to arc length - // - u [0 .. 1] - - getPointAt: function ( u ) { - - var t = this.getUtoTmapping( u ); - return this.getPoint( t ); - - }, - - // Get sequence of points using getPoint( t ) - - getPoints: function ( divisions ) { - - if ( ! divisions ) divisions = 5; - - var points = []; - - for ( var d = 0; d <= divisions; d ++ ) { - - points.push( this.getPoint( d / divisions ) ); - - } - - return points; - - }, - - // Get sequence of points using getPointAt( u ) - - getSpacedPoints: function ( divisions ) { - - if ( ! divisions ) divisions = 5; - - var points = []; - - for ( var d = 0; d <= divisions; d ++ ) { - - points.push( this.getPointAt( d / divisions ) ); - - } - - return points; - - }, - - // Get total curve arc length - - getLength: function () { - - var lengths = this.getLengths(); - return lengths[ lengths.length - 1 ]; - - }, - - // Get list of cumulative segment lengths - - getLengths: function ( divisions ) { - - if ( ! divisions ) divisions = ( this.__arcLengthDivisions ) ? ( this.__arcLengthDivisions ) : 200; - - if ( this.cacheArcLengths - && ( this.cacheArcLengths.length === divisions + 1 ) - && ! this.needsUpdate ) { - - //console.log( "cached", this.cacheArcLengths ); - return this.cacheArcLengths; - - } - - this.needsUpdate = false; - - var cache = []; - var current, last = this.getPoint( 0 ); - var p, sum = 0; - - cache.push( 0 ); - - for ( p = 1; p <= divisions; p ++ ) { - - current = this.getPoint ( p / divisions ); - sum += current.distanceTo( last ); - cache.push( sum ); - last = current; - - } - - this.cacheArcLengths = cache; - - return cache; // { sums: cache, sum:sum }; Sum is in the last element. - - }, - - updateArcLengths: function() { - - this.needsUpdate = true; - this.getLengths(); - - }, - - // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant - - getUtoTmapping: function ( u, distance ) { - - var arcLengths = this.getLengths(); - - var i = 0, il = arcLengths.length; - - var targetArcLength; // The targeted u distance value to get - - if ( distance ) { - - targetArcLength = distance; - - } else { - - targetArcLength = u * arcLengths[ il - 1 ]; - - } - - //var time = Date.now(); - - // binary search for the index with largest value smaller than target u distance - - var low = 0, high = il - 1, comparison; - - while ( low <= high ) { - - i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats - - comparison = arcLengths[ i ] - targetArcLength; - - if ( comparison < 0 ) { - - low = i + 1; - - } else if ( comparison > 0 ) { - - high = i - 1; - - } else { - - high = i; - break; - - // DONE - - } - - } - - i = high; - - //console.log('b' , i, low, high, Date.now()- time); - - if ( arcLengths[ i ] === targetArcLength ) { - - var t = i / ( il - 1 ); - return t; - - } - - // we could get finer grain at lengths, or use simple interpolation between two points - - var lengthBefore = arcLengths[ i ]; - var lengthAfter = arcLengths[ i + 1 ]; - - var segmentLength = lengthAfter - lengthBefore; - - // determine where we are between the 'before' and 'after' points - - var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; - - // add that fractional amount to t - - var t = ( i + segmentFraction ) / ( il - 1 ); - - return t; - - }, - - // Returns a unit vector tangent at t - // In case any sub curve does not implement its tangent derivation, - // 2 points a small delta apart will be used to find its gradient - // which seems to give a reasonable approximation - - getTangent: function( t ) { - - var delta = 0.0001; - var t1 = t - delta; - var t2 = t + delta; - - // Capping in case of danger - - if ( t1 < 0 ) t1 = 0; - if ( t2 > 1 ) t2 = 1; - - var pt1 = this.getPoint( t1 ); - var pt2 = this.getPoint( t2 ); - - var vec = pt2.clone().sub( pt1 ); - return vec.normalize(); - - }, - - getTangentAt: function ( u ) { - - var t = this.getUtoTmapping( u ); - return this.getTangent( t ); - - }, - - computeFrenetFrames: function ( segments, closed ) { - - // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf - - var normal = new Vector3(); - - var tangents = []; - var normals = []; - var binormals = []; - - var vec = new Vector3(); - var mat = new Matrix4(); - - var i, u, theta; - - // compute the tangent vectors for each segment on the curve - - for ( i = 0; i <= segments; i ++ ) { - - u = i / segments; - - tangents[ i ] = this.getTangentAt( u ); - tangents[ i ].normalize(); - - } - - // select an initial normal vector perpendicular to the first tangent vector, - // and in the direction of the minimum tangent xyz component - - normals[ 0 ] = new Vector3(); - binormals[ 0 ] = new Vector3(); - var min = Number.MAX_VALUE; - var tx = Math.abs( tangents[ 0 ].x ); - var ty = Math.abs( tangents[ 0 ].y ); - var tz = Math.abs( tangents[ 0 ].z ); - - if ( tx <= min ) { - - min = tx; - normal.set( 1, 0, 0 ); - - } - - if ( ty <= min ) { - - min = ty; - normal.set( 0, 1, 0 ); - - } - - if ( tz <= min ) { - - normal.set( 0, 0, 1 ); - - } - - vec.crossVectors( tangents[ 0 ], normal ).normalize(); - - normals[ 0 ].crossVectors( tangents[ 0 ], vec ); - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); - - - // compute the slowly-varying normal and binormal vectors for each segment on the curve - - for ( i = 1; i <= segments; i ++ ) { - - normals[ i ] = normals[ i - 1 ].clone(); - - binormals[ i ] = binormals[ i - 1 ].clone(); - - vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); - - if ( vec.length() > Number.EPSILON ) { - - vec.normalize(); - - theta = Math.acos( _Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors - - normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); - - } - - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - - } - - // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same - - if ( closed === true ) { - - theta = Math.acos( _Math.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); - theta /= segments; - - if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { - - theta = - theta; - - } - - for ( i = 1; i <= segments; i ++ ) { - - // twist a little... - normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - - } - - } - - return { - tangents: tangents, - normals: normals, - binormals: binormals - }; - - } - - }; - - // TODO: Transformation for Curves? - - /************************************************************** - * 3D Curves - **************************************************************/ - - // A Factory method for creating new curve subclasses - - Curve.create = function ( constructor, getPointFunc ) { - - constructor.prototype = Object.create( Curve.prototype ); - constructor.prototype.constructor = constructor; - constructor.prototype.getPoint = getPointFunc; - - return constructor; - - }; - - /************************************************************** - * Line - **************************************************************/ - - function LineCurve( v1, v2 ) { - - this.v1 = v1; - this.v2 = v2; - - } - - LineCurve.prototype = Object.create( Curve.prototype ); - LineCurve.prototype.constructor = LineCurve; - - LineCurve.prototype.isLineCurve = true; - - LineCurve.prototype.getPoint = function ( t ) { - - if ( t === 1 ) { - - return this.v2.clone(); - - } - - var point = this.v2.clone().sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); - - return point; - - }; - - // Line curve is linear, so we can overwrite default getPointAt - - LineCurve.prototype.getPointAt = function ( u ) { - - return this.getPoint( u ); - - }; - - LineCurve.prototype.getTangent = function( t ) { - - var tangent = this.v2.clone().sub( this.v1 ); - - return tangent.normalize(); - - }; - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * - **/ - - /************************************************************** - * Curved Path - a curve path is simply a array of connected - * curves, but retains the api of a curve - **************************************************************/ - - function CurvePath() { - - this.curves = []; - - this.autoClose = false; // Automatically closes the path - - } - - CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), { - - constructor: CurvePath, - - add: function ( curve ) { - - this.curves.push( curve ); - - }, - - closePath: function () { - - // Add a line curve if start and end of lines are not connected - var startPoint = this.curves[ 0 ].getPoint( 0 ); - var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); - - if ( ! startPoint.equals( endPoint ) ) { - - this.curves.push( new LineCurve( endPoint, startPoint ) ); - - } - - }, - - // To get accurate point with reference to - // entire path distance at time t, - // following has to be done: - - // 1. Length of each sub path have to be known - // 2. Locate and identify type of curve - // 3. Get t for the curve - // 4. Return curve.getPointAt(t') - - getPoint: function ( t ) { - - var d = t * this.getLength(); - var curveLengths = this.getCurveLengths(); - var i = 0; - - // To think about boundaries points. - - while ( i < curveLengths.length ) { - - if ( curveLengths[ i ] >= d ) { - - var diff = curveLengths[ i ] - d; - var curve = this.curves[ i ]; - - var segmentLength = curve.getLength(); - var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; - - return curve.getPointAt( u ); - - } - - i ++; - - } - - return null; - - // loop where sum != 0, sum > d , sum+1 1 && !points[ points.length - 1 ].equals( points[ 0 ] ) ) { - - points.push( points[ 0 ] ); - - } - - return points; - - }, - - /************************************************************** - * Create Geometries Helpers - **************************************************************/ - - /// Generate geometry from path points (for Line or Points objects) - - createPointsGeometry: function ( divisions ) { - - var pts = this.getPoints( divisions ); - return this.createGeometry( pts ); - - }, - - // Generate geometry from equidistant sampling along the path - - createSpacedPointsGeometry: function ( divisions ) { - - var pts = this.getSpacedPoints( divisions ); - return this.createGeometry( pts ); - - }, - - createGeometry: function ( points ) { - - var geometry = new Geometry(); - - for ( var i = 0, l = points.length; i < l; i ++ ) { - - var point = points[ i ]; - geometry.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); - - } - - return geometry; - - } - - } ); - - /************************************************************** - * Ellipse curve - **************************************************************/ - - function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - - this.aX = aX; - this.aY = aY; - - this.xRadius = xRadius; - this.yRadius = yRadius; - - this.aStartAngle = aStartAngle; - this.aEndAngle = aEndAngle; - - this.aClockwise = aClockwise; - - this.aRotation = aRotation || 0; - - } - - EllipseCurve.prototype = Object.create( Curve.prototype ); - EllipseCurve.prototype.constructor = EllipseCurve; - - EllipseCurve.prototype.isEllipseCurve = true; - - EllipseCurve.prototype.getPoint = function( t ) { - - var twoPi = Math.PI * 2; - var deltaAngle = this.aEndAngle - this.aStartAngle; - var samePoints = Math.abs( deltaAngle ) < Number.EPSILON; - - // ensures that deltaAngle is 0 .. 2 PI - while ( deltaAngle < 0 ) deltaAngle += twoPi; - while ( deltaAngle > twoPi ) deltaAngle -= twoPi; - - if ( deltaAngle < Number.EPSILON ) { - - if ( samePoints ) { - - deltaAngle = 0; - - } else { - - deltaAngle = twoPi; - - } - - } - - if ( this.aClockwise === true && ! samePoints ) { - - if ( deltaAngle === twoPi ) { - - deltaAngle = - twoPi; - - } else { - - deltaAngle = deltaAngle - twoPi; - - } - - } - - var angle = this.aStartAngle + t * deltaAngle; - var x = this.aX + this.xRadius * Math.cos( angle ); - var y = this.aY + this.yRadius * Math.sin( angle ); - - if ( this.aRotation !== 0 ) { - - var cos = Math.cos( this.aRotation ); - var sin = Math.sin( this.aRotation ); - - var tx = x - this.aX; - var ty = y - this.aY; - - // Rotate the point about the center of the ellipse. - x = tx * cos - ty * sin + this.aX; - y = tx * sin + ty * cos + this.aY; - - } - - return new Vector2( x, y ); - - }; - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - */ - - var CurveUtils = { - - tangentQuadraticBezier: function ( t, p0, p1, p2 ) { - - return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 ); - - }, - - // Puay Bing, thanks for helping with this derivative! - - tangentCubicBezier: function ( t, p0, p1, p2, p3 ) { - - return - 3 * p0 * ( 1 - t ) * ( 1 - t ) + - 3 * p1 * ( 1 - t ) * ( 1 - t ) - 6 * t * p1 * ( 1 - t ) + - 6 * t * p2 * ( 1 - t ) - 3 * t * t * p2 + - 3 * t * t * p3; - - }, - - tangentSpline: function ( t, p0, p1, p2, p3 ) { - - // To check if my formulas are correct - - var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1 - var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t - var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2 - var h11 = 3 * t * t - 2 * t; // t3 − t2 - - return h00 + h10 + h01 + h11; - - }, - - // Catmull-Rom - - interpolate: function( p0, p1, p2, p3, t ) { - - var v0 = ( p2 - p0 ) * 0.5; - var v1 = ( p3 - p1 ) * 0.5; - var t2 = t * t; - var t3 = t * t2; - return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; - - } - - }; - - /************************************************************** - * Spline curve - **************************************************************/ - - function SplineCurve( points /* array of Vector2 */ ) { - - this.points = ( points === undefined ) ? [] : points; - - } - - SplineCurve.prototype = Object.create( Curve.prototype ); - SplineCurve.prototype.constructor = SplineCurve; - - SplineCurve.prototype.isSplineCurve = true; - - SplineCurve.prototype.getPoint = function ( t ) { - - var points = this.points; - var point = ( points.length - 1 ) * t; - - var intPoint = Math.floor( point ); - var weight = point - intPoint; - - var point0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; - var point1 = points[ intPoint ]; - var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; - var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; - - var interpolate = CurveUtils.interpolate; - - return new Vector2( - interpolate( point0.x, point1.x, point2.x, point3.x, weight ), - interpolate( point0.y, point1.y, point2.y, point3.y, weight ) - ); - - }; - - /************************************************************** - * Cubic Bezier curve - **************************************************************/ - - function CubicBezierCurve( v0, v1, v2, v3 ) { - - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; - - } - - CubicBezierCurve.prototype = Object.create( Curve.prototype ); - CubicBezierCurve.prototype.constructor = CubicBezierCurve; - - CubicBezierCurve.prototype.getPoint = function ( t ) { - - var b3 = ShapeUtils.b3; - - return new Vector2( - b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), - b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ) - ); - - }; - - CubicBezierCurve.prototype.getTangent = function( t ) { - - var tangentCubicBezier = CurveUtils.tangentCubicBezier; - - return new Vector2( - tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), - tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ) - ).normalize(); - - }; - - /************************************************************** - * Quadratic Bezier curve - **************************************************************/ - - - function QuadraticBezierCurve( v0, v1, v2 ) { - - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - - } - - QuadraticBezierCurve.prototype = Object.create( Curve.prototype ); - QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve; - - - QuadraticBezierCurve.prototype.getPoint = function ( t ) { - - var b2 = ShapeUtils.b2; - - return new Vector2( - b2( t, this.v0.x, this.v1.x, this.v2.x ), - b2( t, this.v0.y, this.v1.y, this.v2.y ) - ); - - }; - - - QuadraticBezierCurve.prototype.getTangent = function( t ) { - - var tangentQuadraticBezier = CurveUtils.tangentQuadraticBezier; - - return new Vector2( - tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x ), - tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y ) - ).normalize(); - - }; - - var PathPrototype = Object.assign( Object.create( CurvePath.prototype ), { - - fromPoints: function ( vectors ) { - - this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y ); - - for ( var i = 1, l = vectors.length; i < l; i ++ ) { - - this.lineTo( vectors[ i ].x, vectors[ i ].y ); - - } - - }, - - moveTo: function ( x, y ) { - - this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? - - }, - - lineTo: function ( x, y ) { - - var curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); - this.curves.push( curve ); - - this.currentPoint.set( x, y ); - - }, - - quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { - - var curve = new QuadraticBezierCurve( - this.currentPoint.clone(), - new Vector2( aCPx, aCPy ), - new Vector2( aX, aY ) - ); - - this.curves.push( curve ); - - this.currentPoint.set( aX, aY ); - - }, - - bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - - var curve = new CubicBezierCurve( - this.currentPoint.clone(), - new Vector2( aCP1x, aCP1y ), - new Vector2( aCP2x, aCP2y ), - new Vector2( aX, aY ) - ); - - this.curves.push( curve ); - - this.currentPoint.set( aX, aY ); - - }, - - splineThru: function ( pts /*Array of Vector*/ ) { - - var npts = [ this.currentPoint.clone() ].concat( pts ); - - var curve = new SplineCurve( npts ); - this.curves.push( curve ); - - this.currentPoint.copy( pts[ pts.length - 1 ] ); - - }, - - arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - - var x0 = this.currentPoint.x; - var y0 = this.currentPoint.y; - - this.absarc( aX + x0, aY + y0, aRadius, - aStartAngle, aEndAngle, aClockwise ); - - }, - - absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - - this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - - }, - - ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - - var x0 = this.currentPoint.x; - var y0 = this.currentPoint.y; - - this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - - }, - - absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - - var curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - - if ( this.curves.length > 0 ) { - - // if a previous curve is present, attempt to join - var firstPoint = curve.getPoint( 0 ); - - if ( ! firstPoint.equals( this.currentPoint ) ) { - - this.lineTo( firstPoint.x, firstPoint.y ); - - } - - } - - this.curves.push( curve ); - - var lastPoint = curve.getPoint( 1 ); - this.currentPoint.copy( lastPoint ); - - } - - } ); - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Defines a 2d shape plane using paths. - **/ - - // STEP 1 Create a path. - // STEP 2 Turn path into shape. - // STEP 3 ExtrudeGeometry takes in Shape/Shapes - // STEP 3a - Extract points from each shape, turn to vertices - // STEP 3b - Triangulate each shape, add faces. - - function Shape() { - - Path.apply( this, arguments ); - - this.holes = []; - - } - - Shape.prototype = Object.assign( Object.create( PathPrototype ), { - - constructor: Shape, - - getPointsHoles: function ( divisions ) { - - var holesPts = []; - - for ( var i = 0, l = this.holes.length; i < l; i ++ ) { - - holesPts[ i ] = this.holes[ i ].getPoints( divisions ); - - } - - return holesPts; - - }, - - // Get points of shape and holes (keypoints based on segments parameter) - - extractAllPoints: function ( divisions ) { - - return { - - shape: this.getPoints( divisions ), - holes: this.getPointsHoles( divisions ) - - }; - - }, - - extractPoints: function ( divisions ) { - - return this.extractAllPoints( divisions ); - - } - - } ); - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Creates free form 2d path using series of points, lines or curves. - * - **/ - - function Path( points ) { - - CurvePath.call( this ); - this.currentPoint = new Vector2(); - - if ( points ) { - - this.fromPoints( points ); - - } - - } - - Path.prototype = PathPrototype; - PathPrototype.constructor = Path; - - - // minimal class for proxing functions to Path. Replaces old "extractSubpaths()" - function ShapePath() { - this.subPaths = []; - this.currentPath = null; - } - - ShapePath.prototype = { - moveTo: function ( x, y ) { - this.currentPath = new Path(); - this.subPaths.push(this.currentPath); - this.currentPath.moveTo( x, y ); - }, - lineTo: function ( x, y ) { - this.currentPath.lineTo( x, y ); - }, - quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { - this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); - }, - bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); - }, - splineThru: function ( pts ) { - this.currentPath.splineThru( pts ); - }, - - toShapes: function ( isCCW, noHoles ) { - - function toShapesNoHoles( inSubpaths ) { - - var shapes = []; - - for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) { - - var tmpPath = inSubpaths[ i ]; - - var tmpShape = new Shape(); - tmpShape.curves = tmpPath.curves; - - shapes.push( tmpShape ); - - } - - return shapes; - - } - - function isPointInsidePolygon( inPt, inPolygon ) { - - var polyLen = inPolygon.length; - - // inPt on polygon contour => immediate success or - // toggling of inside/outside at every single! intersection point of an edge - // with the horizontal line through inPt, left of inPt - // not counting lowerY endpoints of edges and whole edges on that line - var inside = false; - for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { - - var edgeLowPt = inPolygon[ p ]; - var edgeHighPt = inPolygon[ q ]; - - var edgeDx = edgeHighPt.x - edgeLowPt.x; - var edgeDy = edgeHighPt.y - edgeLowPt.y; - - if ( Math.abs( edgeDy ) > Number.EPSILON ) { - - // not parallel - if ( edgeDy < 0 ) { - - edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; - edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; - - } - if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; - - if ( inPt.y === edgeLowPt.y ) { - - if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? - // continue; // no intersection or edgeLowPt => doesn't count !!! - - } else { - - var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); - if ( perpEdge === 0 ) return true; // inPt is on contour ? - if ( perpEdge < 0 ) continue; - inside = ! inside; // true intersection left of inPt - - } - - } else { - - // parallel or collinear - if ( inPt.y !== edgeLowPt.y ) continue; // parallel - // edge lies on the same horizontal line as inPt - if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || - ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! - // continue; - - } - - } - - return inside; - - } - - var isClockWise = ShapeUtils.isClockWise; - - var subPaths = this.subPaths; - if ( subPaths.length === 0 ) return []; - - if ( noHoles === true ) return toShapesNoHoles( subPaths ); - - - var solid, tmpPath, tmpShape, shapes = []; - - if ( subPaths.length === 1 ) { - - tmpPath = subPaths[ 0 ]; - tmpShape = new Shape(); - tmpShape.curves = tmpPath.curves; - shapes.push( tmpShape ); - return shapes; - - } - - var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); - holesFirst = isCCW ? ! holesFirst : holesFirst; - - // console.log("Holes first", holesFirst); - - var betterShapeHoles = []; - var newShapes = []; - var newShapeHoles = []; - var mainIdx = 0; - var tmpPoints; - - newShapes[ mainIdx ] = undefined; - newShapeHoles[ mainIdx ] = []; - - for ( var i = 0, l = subPaths.length; i < l; i ++ ) { - - tmpPath = subPaths[ i ]; - tmpPoints = tmpPath.getPoints(); - solid = isClockWise( tmpPoints ); - solid = isCCW ? ! solid : solid; - - if ( solid ) { - - if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; - - newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints }; - newShapes[ mainIdx ].s.curves = tmpPath.curves; - - if ( holesFirst ) mainIdx ++; - newShapeHoles[ mainIdx ] = []; - - //console.log('cw', i); - - } else { - - newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); - - //console.log('ccw', i); - - } - - } - - // only Holes? -> probably all Shapes with wrong orientation - if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); - - - if ( newShapes.length > 1 ) { - - var ambiguous = false; - var toChange = []; - - for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - - betterShapeHoles[ sIdx ] = []; - - } - - for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - - var sho = newShapeHoles[ sIdx ]; - - for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) { - - var ho = sho[ hIdx ]; - var hole_unassigned = true; - - for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { - - if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { - - if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); - if ( hole_unassigned ) { - - hole_unassigned = false; - betterShapeHoles[ s2Idx ].push( ho ); - - } else { - - ambiguous = true; - - } - - } - - } - if ( hole_unassigned ) { - - betterShapeHoles[ sIdx ].push( ho ); - - } - - } - - } - // console.log("ambiguous: ", ambiguous); - if ( toChange.length > 0 ) { - - // console.log("to change: ", toChange); - if ( ! ambiguous ) newShapeHoles = betterShapeHoles; - - } - - } - - var tmpHoles; - - for ( var i = 0, il = newShapes.length; i < il; i ++ ) { - - tmpShape = newShapes[ i ].s; - shapes.push( tmpShape ); - tmpHoles = newShapeHoles[ i ]; - - for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) { - - tmpShape.holes.push( tmpHoles[ j ].h ); - - } - - } - - //console.log("shape", shapes); - - return shapes; - - } - }; - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author mrdoob / http://mrdoob.com/ - */ - - function Font( data ) { - - this.data = data; - - } - - Object.assign( Font.prototype, { - - isFont: true, - - generateShapes: function ( text, size, divisions ) { - - function createPaths( text ) { - - var chars = String( text ).split( '' ); - var scale = size / data.resolution; - var offset = 0; - - var paths = []; - - for ( var i = 0; i < chars.length; i ++ ) { - - var ret = createPath( chars[ i ], scale, offset ); - offset += ret.offset; - - paths.push( ret.path ); - - } - - return paths; - - } - - function createPath( c, scale, offset ) { - - var glyph = data.glyphs[ c ] || data.glyphs[ '?' ]; - - if ( ! glyph ) return; - - var path = new ShapePath(); - - var pts = [], b2 = ShapeUtils.b2, b3 = ShapeUtils.b3; - var x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, laste; - - if ( glyph.o ) { - - var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); - - for ( var i = 0, l = outline.length; i < l; ) { - - var action = outline[ i ++ ]; - - switch ( action ) { - - case 'm': // moveTo - - x = outline[ i ++ ] * scale + offset; - y = outline[ i ++ ] * scale; - - path.moveTo( x, y ); - - break; - - case 'l': // lineTo - - x = outline[ i ++ ] * scale + offset; - y = outline[ i ++ ] * scale; - - path.lineTo( x, y ); - - break; - - case 'q': // quadraticCurveTo - - cpx = outline[ i ++ ] * scale + offset; - cpy = outline[ i ++ ] * scale; - cpx1 = outline[ i ++ ] * scale + offset; - cpy1 = outline[ i ++ ] * scale; - - path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); - - laste = pts[ pts.length - 1 ]; - - if ( laste ) { - - cpx0 = laste.x; - cpy0 = laste.y; - - for ( var i2 = 1; i2 <= divisions; i2 ++ ) { - - var t = i2 / divisions; - b2( t, cpx0, cpx1, cpx ); - b2( t, cpy0, cpy1, cpy ); - - } - - } - - break; - - case 'b': // bezierCurveTo - - cpx = outline[ i ++ ] * scale + offset; - cpy = outline[ i ++ ] * scale; - cpx1 = outline[ i ++ ] * scale + offset; - cpy1 = outline[ i ++ ] * scale; - cpx2 = outline[ i ++ ] * scale + offset; - cpy2 = outline[ i ++ ] * scale; - - path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); - - laste = pts[ pts.length - 1 ]; - - if ( laste ) { - - cpx0 = laste.x; - cpy0 = laste.y; - - for ( var i2 = 1; i2 <= divisions; i2 ++ ) { - - var t = i2 / divisions; - b3( t, cpx0, cpx1, cpx2, cpx ); - b3( t, cpy0, cpy1, cpy2, cpy ); - - } - - } - - break; - - } - - } - - } - - return { offset: glyph.ha * scale, path: path }; - - } - - // - - if ( size === undefined ) size = 100; - if ( divisions === undefined ) divisions = 4; - - var data = this.data; - - var paths = createPaths( text ); - var shapes = []; - - for ( var p = 0, pl = paths.length; p < pl; p ++ ) { - - Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); - - } - - return shapes; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function FontLoader( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - - } - - Object.assign( FontLoader.prototype, { - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var loader = new FileLoader( this.manager ); - loader.load( url, function ( text ) { - - var json; - - try { - - json = JSON.parse( text ); - - } catch ( e ) { - - console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' ); - json = JSON.parse( text.substring( 65, text.length - 2 ) ); - - } - - var font = scope.parse( json ); - - if ( onLoad ) onLoad( font ); - - }, onProgress, onError ); - - }, - - parse: function ( json ) { - - return new Font( json ); - - } - - } ); - - var context; - - var AudioContext = { - - getContext: function () { - - if ( context === undefined ) { - - context = new ( window.AudioContext || window.webkitAudioContext )(); - - } - - return context; - - }, - - setContext: function ( value ) { - - context = value; - - } - - }; - - /** - * @author Reece Aaron Lecrivain / http://reecenotes.com/ - */ - - function AudioLoader( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - - } - - Object.assign( AudioLoader.prototype, { - - load: function ( url, onLoad, onProgress, onError ) { - - var loader = new FileLoader( this.manager ); - loader.setResponseType( 'arraybuffer' ); - loader.load( url, function ( buffer ) { - - var context = AudioContext.getContext(); - - context.decodeAudioData( buffer, function ( audioBuffer ) { - - onLoad( audioBuffer ); - - } ); - - }, onProgress, onError ); - - } - - } ); - - /** - * @author abelnation / http://github.com/abelnation - */ - - function RectAreaLight ( color, intensity, width, height ) { - - Light.call( this, color, intensity ); - - this.type = 'RectAreaLight'; - - this.position.set( 0, 1, 0 ); - this.updateMatrix(); - - this.width = ( width !== undefined ) ? width : 10; - this.height = ( height !== undefined ) ? height : 10; - - // TODO (abelnation): distance/decay - - // TODO (abelnation): update method for RectAreaLight to update transform to lookat target - - // TODO (abelnation): shadows - // this.shadow = new THREE.RectAreaLightShadow( new THREE.PerspectiveCamera( 90, 1, 0.5, 500 ) ); - - } - - // TODO (abelnation): RectAreaLight update when light shape is changed - RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: RectAreaLight, - - isRectAreaLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.width = source.width; - this.height = source.height; - - // this.shadow = source.shadow.clone(); - - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function StereoCamera() { - - this.type = 'StereoCamera'; - - this.aspect = 1; - - this.eyeSep = 0.064; - - this.cameraL = new PerspectiveCamera(); - this.cameraL.layers.enable( 1 ); - this.cameraL.matrixAutoUpdate = false; - - this.cameraR = new PerspectiveCamera(); - this.cameraR.layers.enable( 2 ); - this.cameraR.matrixAutoUpdate = false; - - } - - Object.assign( StereoCamera.prototype, { - - update: ( function () { - - var instance, focus, fov, aspect, near, far, zoom; - - var eyeRight = new Matrix4(); - var eyeLeft = new Matrix4(); - - return function update( camera ) { - - var needsUpdate = instance !== this || focus !== camera.focus || fov !== camera.fov || - aspect !== camera.aspect * this.aspect || near !== camera.near || - far !== camera.far || zoom !== camera.zoom; - - if ( needsUpdate ) { - - instance = this; - focus = camera.focus; - fov = camera.fov; - aspect = camera.aspect * this.aspect; - near = camera.near; - far = camera.far; - zoom = camera.zoom; - - // Off-axis stereoscopic effect based on - // http://paulbourke.net/stereographics/stereorender/ - - var projectionMatrix = camera.projectionMatrix.clone(); - var eyeSep = this.eyeSep / 2; - var eyeSepOnProjection = eyeSep * near / focus; - var ymax = ( near * Math.tan( _Math.DEG2RAD * fov * 0.5 ) ) / zoom; - var xmin, xmax; - - // translate xOffset - - eyeLeft.elements[ 12 ] = - eyeSep; - eyeRight.elements[ 12 ] = eyeSep; - - // for left eye - - xmin = - ymax * aspect + eyeSepOnProjection; - xmax = ymax * aspect + eyeSepOnProjection; - - projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); - projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); - - this.cameraL.projectionMatrix.copy( projectionMatrix ); - - // for right eye - - xmin = - ymax * aspect - eyeSepOnProjection; - xmax = ymax * aspect - eyeSepOnProjection; - - projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); - projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); - - this.cameraR.projectionMatrix.copy( projectionMatrix ); - - } - - this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft ); - this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight ); - - }; - - } )() - - } ); - - /** - * Camera for rendering cube maps - * - renders scene into axis-aligned cube - * - * @author alteredq / http://alteredqualia.com/ - */ - - function CubeCamera( near, far, cubeResolution ) { - - Object3D.call( this ); - - this.type = 'CubeCamera'; - - var fov = 90, aspect = 1; - - var cameraPX = new PerspectiveCamera( fov, aspect, near, far ); - cameraPX.up.set( 0, - 1, 0 ); - cameraPX.lookAt( new Vector3( 1, 0, 0 ) ); - this.add( cameraPX ); - - var cameraNX = new PerspectiveCamera( fov, aspect, near, far ); - cameraNX.up.set( 0, - 1, 0 ); - cameraNX.lookAt( new Vector3( - 1, 0, 0 ) ); - this.add( cameraNX ); - - var cameraPY = new PerspectiveCamera( fov, aspect, near, far ); - cameraPY.up.set( 0, 0, 1 ); - cameraPY.lookAt( new Vector3( 0, 1, 0 ) ); - this.add( cameraPY ); - - var cameraNY = new PerspectiveCamera( fov, aspect, near, far ); - cameraNY.up.set( 0, 0, - 1 ); - cameraNY.lookAt( new Vector3( 0, - 1, 0 ) ); - this.add( cameraNY ); - - var cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); - cameraPZ.up.set( 0, - 1, 0 ); - cameraPZ.lookAt( new Vector3( 0, 0, 1 ) ); - this.add( cameraPZ ); - - var cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); - cameraNZ.up.set( 0, - 1, 0 ); - cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) ); - this.add( cameraNZ ); - - var options = { format: RGBFormat, magFilter: LinearFilter, minFilter: LinearFilter }; - - this.renderTarget = new WebGLRenderTargetCube( cubeResolution, cubeResolution, options ); - - this.updateCubeMap = function ( renderer, scene ) { - - if ( this.parent === null ) this.updateMatrixWorld(); - - var renderTarget = this.renderTarget; - var generateMipmaps = renderTarget.texture.generateMipmaps; - - renderTarget.texture.generateMipmaps = false; - - renderTarget.activeCubeFace = 0; - renderer.render( scene, cameraPX, renderTarget ); - - renderTarget.activeCubeFace = 1; - renderer.render( scene, cameraNX, renderTarget ); - - renderTarget.activeCubeFace = 2; - renderer.render( scene, cameraPY, renderTarget ); - - renderTarget.activeCubeFace = 3; - renderer.render( scene, cameraNY, renderTarget ); - - renderTarget.activeCubeFace = 4; - renderer.render( scene, cameraPZ, renderTarget ); - - renderTarget.texture.generateMipmaps = generateMipmaps; - - renderTarget.activeCubeFace = 5; - renderer.render( scene, cameraNZ, renderTarget ); - - renderer.setRenderTarget( null ); - - }; - - } - - CubeCamera.prototype = Object.create( Object3D.prototype ); - CubeCamera.prototype.constructor = CubeCamera; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function AudioListener() { - - Object3D.call( this ); - - this.type = 'AudioListener'; - - this.context = AudioContext.getContext(); - - this.gain = this.context.createGain(); - this.gain.connect( this.context.destination ); - - this.filter = null; - - } - - AudioListener.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: AudioListener, - - getInput: function () { - - return this.gain; - - }, - - removeFilter: function ( ) { - - if ( this.filter !== null ) { - - this.gain.disconnect( this.filter ); - this.filter.disconnect( this.context.destination ); - this.gain.connect( this.context.destination ); - this.filter = null; - - } - - }, - - getFilter: function () { - - return this.filter; - - }, - - setFilter: function ( value ) { - - if ( this.filter !== null ) { - - this.gain.disconnect( this.filter ); - this.filter.disconnect( this.context.destination ); - - } else { - - this.gain.disconnect( this.context.destination ); - - } - - this.filter = value; - this.gain.connect( this.filter ); - this.filter.connect( this.context.destination ); - - }, - - getMasterVolume: function () { - - return this.gain.gain.value; - - }, - - setMasterVolume: function ( value ) { - - this.gain.gain.value = value; - - }, - - updateMatrixWorld: ( function () { - - var position = new Vector3(); - var quaternion = new Quaternion(); - var scale = new Vector3(); - - var orientation = new Vector3(); - - return function updateMatrixWorld( force ) { - - Object3D.prototype.updateMatrixWorld.call( this, force ); - - var listener = this.context.listener; - var up = this.up; - - this.matrixWorld.decompose( position, quaternion, scale ); - - orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion ); - - if ( listener.positionX ) { - - listener.positionX.setValueAtTime( position.x, this.context.currentTime ); - listener.positionY.setValueAtTime( position.y, this.context.currentTime ); - listener.positionZ.setValueAtTime( position.z, this.context.currentTime ); - listener.forwardX.setValueAtTime( orientation.x, this.context.currentTime ); - listener.forwardY.setValueAtTime( orientation.y, this.context.currentTime ); - listener.forwardZ.setValueAtTime( orientation.z, this.context.currentTime ); - listener.upX.setValueAtTime( up.x, this.context.currentTime ); - listener.upY.setValueAtTime( up.y, this.context.currentTime ); - listener.upZ.setValueAtTime( up.z, this.context.currentTime ); - - } else { - - listener.setPosition( position.x, position.y, position.z ); - listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z ); - - } - - }; - - } )() - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author Reece Aaron Lecrivain / http://reecenotes.com/ - */ - - function Audio( listener ) { - - Object3D.call( this ); - - this.type = 'Audio'; - - this.context = listener.context; - - this.gain = this.context.createGain(); - this.gain.connect( listener.getInput() ); - - this.autoplay = false; - - this.buffer = null; - this.loop = false; - this.startTime = 0; - this.playbackRate = 1; - this.isPlaying = false; - this.hasPlaybackControl = true; - this.sourceType = 'empty'; - - this.filters = []; - - } - - Audio.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Audio, - - getOutput: function () { - - return this.gain; - - }, - - setNodeSource: function ( audioNode ) { - - this.hasPlaybackControl = false; - this.sourceType = 'audioNode'; - this.source = audioNode; - this.connect(); - - return this; - - }, - - setBuffer: function ( audioBuffer ) { - - this.buffer = audioBuffer; - this.sourceType = 'buffer'; - - if ( this.autoplay ) this.play(); - - return this; - - }, - - play: function () { - - if ( this.isPlaying === true ) { - - console.warn( 'THREE.Audio: Audio is already playing.' ); - return; - - } - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - var source = this.context.createBufferSource(); - - source.buffer = this.buffer; - source.loop = this.loop; - source.onended = this.onEnded.bind( this ); - source.playbackRate.setValueAtTime( this.playbackRate, this.startTime ); - source.start( 0, this.startTime ); - - this.isPlaying = true; - - this.source = source; - - return this.connect(); - - }, - - pause: function () { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this.source.stop(); - this.startTime = this.context.currentTime; - this.isPlaying = false; - - return this; - - }, - - stop: function () { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this.source.stop(); - this.startTime = 0; - this.isPlaying = false; - - return this; - - }, - - connect: function () { - - if ( this.filters.length > 0 ) { - - this.source.connect( this.filters[ 0 ] ); - - for ( var i = 1, l = this.filters.length; i < l; i ++ ) { - - this.filters[ i - 1 ].connect( this.filters[ i ] ); - - } - - this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); - - } else { - - this.source.connect( this.getOutput() ); - - } - - return this; - - }, - - disconnect: function () { - - if ( this.filters.length > 0 ) { - - this.source.disconnect( this.filters[ 0 ] ); - - for ( var i = 1, l = this.filters.length; i < l; i ++ ) { - - this.filters[ i - 1 ].disconnect( this.filters[ i ] ); - - } - - this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); - - } else { - - this.source.disconnect( this.getOutput() ); - - } - - return this; - - }, - - getFilters: function () { - - return this.filters; - - }, - - setFilters: function ( value ) { - - if ( ! value ) value = []; - - if ( this.isPlaying === true ) { - - this.disconnect(); - this.filters = value; - this.connect(); - - } else { - - this.filters = value; - - } - - return this; - - }, - - getFilter: function () { - - return this.getFilters()[ 0 ]; - - }, - - setFilter: function ( filter ) { - - return this.setFilters( filter ? [ filter ] : [] ); - - }, - - setPlaybackRate: function ( value ) { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this.playbackRate = value; - - if ( this.isPlaying === true ) { - - this.source.playbackRate.setValueAtTime( this.playbackRate, this.context.currentTime ); - - } - - return this; - - }, - - getPlaybackRate: function () { - - return this.playbackRate; - - }, - - onEnded: function () { - - this.isPlaying = false; - - }, - - getLoop: function () { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return false; - - } - - return this.loop; - - }, - - setLoop: function ( value ) { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this.loop = value; - - if ( this.isPlaying === true ) { - - this.source.loop = this.loop; - - } - - return this; - - }, - - getVolume: function () { - - return this.gain.gain.value; - - }, - - - setVolume: function ( value ) { - - this.gain.gain.value = value; - - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function PositionalAudio( listener ) { - - Audio.call( this, listener ); - - this.panner = this.context.createPanner(); - this.panner.connect( this.gain ); - - } - - PositionalAudio.prototype = Object.assign( Object.create( Audio.prototype ), { - - constructor: PositionalAudio, - - getOutput: function () { - - return this.panner; - - }, - - getRefDistance: function () { - - return this.panner.refDistance; - - }, - - setRefDistance: function ( value ) { - - this.panner.refDistance = value; - - }, - - getRolloffFactor: function () { - - return this.panner.rolloffFactor; - - }, - - setRolloffFactor: function ( value ) { - - this.panner.rolloffFactor = value; - - }, - - getDistanceModel: function () { - - return this.panner.distanceModel; - - }, - - setDistanceModel: function ( value ) { - - this.panner.distanceModel = value; - - }, - - getMaxDistance: function () { - - return this.panner.maxDistance; - - }, - - setMaxDistance: function ( value ) { - - this.panner.maxDistance = value; - - }, - - updateMatrixWorld: ( function () { - - var position = new Vector3(); - - return function updateMatrixWorld( force ) { - - Object3D.prototype.updateMatrixWorld.call( this, force ); - - position.setFromMatrixPosition( this.matrixWorld ); - - this.panner.setPosition( position.x, position.y, position.z ); - - }; - - } )() - - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function AudioAnalyser( audio, fftSize ) { - - this.analyser = audio.context.createAnalyser(); - this.analyser.fftSize = fftSize !== undefined ? fftSize : 2048; - - this.data = new Uint8Array( this.analyser.frequencyBinCount ); - - audio.getOutput().connect( this.analyser ); - - } - - Object.assign( AudioAnalyser.prototype, { - - getFrequencyData: function () { - - this.analyser.getByteFrequencyData( this.data ); - - return this.data; - - }, - - getAverageFrequency: function () { - - var value = 0, data = this.getFrequencyData(); - - for ( var i = 0; i < data.length; i ++ ) { - - value += data[ i ]; - - } - - return value / data.length; - - } - - } ); - - /** - * - * Buffered scene graph property that allows weighted accumulation. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function PropertyMixer( binding, typeName, valueSize ) { - - this.binding = binding; - this.valueSize = valueSize; - - var bufferType = Float64Array, - mixFunction; - - switch ( typeName ) { - - case 'quaternion': - mixFunction = this._slerp; - break; - - case 'string': - case 'bool': - bufferType = Array; - mixFunction = this._select; - break; - - default: - mixFunction = this._lerp; - - } - - this.buffer = new bufferType( valueSize * 4 ); - // layout: [ incoming | accu0 | accu1 | orig ] - // - // interpolators can use .buffer as their .result - // the data then goes to 'incoming' - // - // 'accu0' and 'accu1' are used frame-interleaved for - // the cumulative result and are compared to detect - // changes - // - // 'orig' stores the original state of the property - - this._mixBufferRegion = mixFunction; - - this.cumulativeWeight = 0; - - this.useCount = 0; - this.referenceCount = 0; - - } - - PropertyMixer.prototype = { - - constructor: PropertyMixer, - - // accumulate data in the 'incoming' region into 'accu' - accumulate: function( accuIndex, weight ) { - - // note: happily accumulating nothing when weight = 0, the caller knows - // the weight and shouldn't have made the call in the first place - - var buffer = this.buffer, - stride = this.valueSize, - offset = accuIndex * stride + stride, - - currentWeight = this.cumulativeWeight; - - if ( currentWeight === 0 ) { - - // accuN := incoming * weight - - for ( var i = 0; i !== stride; ++ i ) { - - buffer[ offset + i ] = buffer[ i ]; - - } - - currentWeight = weight; - - } else { - - // accuN := accuN + incoming * weight - - currentWeight += weight; - var mix = weight / currentWeight; - this._mixBufferRegion( buffer, offset, 0, mix, stride ); - - } - - this.cumulativeWeight = currentWeight; - - }, - - // apply the state of 'accu' to the binding when accus differ - apply: function( accuIndex ) { - - var stride = this.valueSize, - buffer = this.buffer, - offset = accuIndex * stride + stride, - - weight = this.cumulativeWeight, - - binding = this.binding; - - this.cumulativeWeight = 0; - - if ( weight < 1 ) { - - // accuN := accuN + original * ( 1 - cumulativeWeight ) - - var originalValueOffset = stride * 3; - - this._mixBufferRegion( - buffer, offset, originalValueOffset, 1 - weight, stride ); - - } - - for ( var i = stride, e = stride + stride; i !== e; ++ i ) { - - if ( buffer[ i ] !== buffer[ i + stride ] ) { - - // value has changed -> update scene graph - - binding.setValue( buffer, offset ); - break; - - } - - } - - }, - - // remember the state of the bound property and copy it to both accus - saveOriginalState: function() { - - var binding = this.binding; - - var buffer = this.buffer, - stride = this.valueSize, - - originalValueOffset = stride * 3; - - binding.getValue( buffer, originalValueOffset ); - - // accu[0..1] := orig -- initially detect changes against the original - for ( var i = stride, e = originalValueOffset; i !== e; ++ i ) { - - buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; - - } - - this.cumulativeWeight = 0; - - }, - - // apply the state previously taken via 'saveOriginalState' to the binding - restoreOriginalState: function() { - - var originalValueOffset = this.valueSize * 3; - this.binding.setValue( this.buffer, originalValueOffset ); - - }, - - - // mix functions - - _select: function( buffer, dstOffset, srcOffset, t, stride ) { - - if ( t >= 0.5 ) { - - for ( var i = 0; i !== stride; ++ i ) { - - buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; - - } - - } - - }, - - _slerp: function( buffer, dstOffset, srcOffset, t, stride ) { - - Quaternion.slerpFlat( buffer, dstOffset, - buffer, dstOffset, buffer, srcOffset, t ); - - }, - - _lerp: function( buffer, dstOffset, srcOffset, t, stride ) { - - var s = 1 - t; - - for ( var i = 0; i !== stride; ++ i ) { - - var j = dstOffset + i; - - buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; - - } - - } - - }; - - /** - * - * A reference to a real property in the scene graph. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function PropertyBinding( rootNode, path, parsedPath ) { - - this.path = path; - this.parsedPath = parsedPath || - PropertyBinding.parseTrackName( path ); - - this.node = PropertyBinding.findNode( - rootNode, this.parsedPath.nodeName ) || rootNode; - - this.rootNode = rootNode; - - } - - PropertyBinding.prototype = { - - constructor: PropertyBinding, - - getValue: function getValue_unbound( targetArray, offset ) { - - this.bind(); - this.getValue( targetArray, offset ); - - // Note: This class uses a State pattern on a per-method basis: - // 'bind' sets 'this.getValue' / 'setValue' and shadows the - // prototype version of these methods with one that represents - // the bound state. When the property is not found, the methods - // become no-ops. - - }, - - setValue: function getValue_unbound( sourceArray, offset ) { - - this.bind(); - this.setValue( sourceArray, offset ); - - }, - - // create getter / setter pair for a property in the scene graph - bind: function() { - - var targetObject = this.node, - parsedPath = this.parsedPath, - - objectName = parsedPath.objectName, - propertyName = parsedPath.propertyName, - propertyIndex = parsedPath.propertyIndex; - - if ( ! targetObject ) { - - targetObject = PropertyBinding.findNode( - this.rootNode, parsedPath.nodeName ) || this.rootNode; - - this.node = targetObject; - - } - - // set fail state so we can just 'return' on error - this.getValue = this._getValue_unavailable; - this.setValue = this._setValue_unavailable; - - // ensure there is a value node - if ( ! targetObject ) { - - console.error( " trying to update node for track: " + this.path + " but it wasn't found." ); - return; - - } - - if ( objectName ) { - - var objectIndex = parsedPath.objectIndex; - - // special cases were we need to reach deeper into the hierarchy to get the face materials.... - switch ( objectName ) { - - case 'materials': - - if ( ! targetObject.material ) { - - console.error( ' can not bind to material as node does not have a material', this ); - return; - - } - - if ( ! targetObject.material.materials ) { - - console.error( ' can not bind to material.materials as node.material does not have a materials array', this ); - return; - - } - - targetObject = targetObject.material.materials; - - break; - - case 'bones': - - if ( ! targetObject.skeleton ) { - - console.error( ' can not bind to bones as node does not have a skeleton', this ); - return; - - } - - // potential future optimization: skip this if propertyIndex is already an integer - // and convert the integer string to a true integer. - - targetObject = targetObject.skeleton.bones; - - // support resolving morphTarget names into indices. - for ( var i = 0; i < targetObject.length; i ++ ) { - - if ( targetObject[ i ].name === objectIndex ) { - - objectIndex = i; - break; - - } - - } - - break; - - default: - - if ( targetObject[ objectName ] === undefined ) { - - console.error( ' can not bind to objectName of node, undefined', this ); - return; - - } - - targetObject = targetObject[ objectName ]; - - } - - - if ( objectIndex !== undefined ) { - - if ( targetObject[ objectIndex ] === undefined ) { - - console.error( " trying to bind to objectIndex of objectName, but is undefined:", this, targetObject ); - return; - - } - - targetObject = targetObject[ objectIndex ]; - - } - - } - - // resolve property - var nodeProperty = targetObject[ propertyName ]; - - if ( nodeProperty === undefined ) { - - var nodeName = parsedPath.nodeName; - - console.error( " trying to update property for track: " + nodeName + - '.' + propertyName + " but it wasn't found.", targetObject ); - return; - - } - - // determine versioning scheme - var versioning = this.Versioning.None; - - if ( targetObject.needsUpdate !== undefined ) { // material - - versioning = this.Versioning.NeedsUpdate; - this.targetObject = targetObject; - - } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform - - versioning = this.Versioning.MatrixWorldNeedsUpdate; - this.targetObject = targetObject; - - } - - // determine how the property gets bound - var bindingType = this.BindingType.Direct; - - if ( propertyIndex !== undefined ) { - // access a sub element of the property array (only primitives are supported right now) - - if ( propertyName === "morphTargetInfluences" ) { - // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. - - // support resolving morphTarget names into indices. - if ( ! targetObject.geometry ) { - - console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry', this ); - return; - - } - - if ( ! targetObject.geometry.morphTargets ) { - - console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry.morphTargets', this ); - return; - - } - - for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) { - - if ( targetObject.geometry.morphTargets[ i ].name === propertyIndex ) { - - propertyIndex = i; - break; - - } - - } - - } - - bindingType = this.BindingType.ArrayElement; - - this.resolvedProperty = nodeProperty; - this.propertyIndex = propertyIndex; - - } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { - // must use copy for Object3D.Euler/Quaternion - - bindingType = this.BindingType.HasFromToArray; - - this.resolvedProperty = nodeProperty; - - } else if ( nodeProperty.length !== undefined ) { - - bindingType = this.BindingType.EntireArray; - - this.resolvedProperty = nodeProperty; - - } else { - - this.propertyName = propertyName; - - } - - // select getter / setter - this.getValue = this.GetterByBindingType[ bindingType ]; - this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; - - }, - - unbind: function() { - - this.node = null; - - // back to the prototype version of getValue / setValue - // note: avoiding to mutate the shape of 'this' via 'delete' - this.getValue = this._getValue_unbound; - this.setValue = this._setValue_unbound; - - } - - }; - - Object.assign( PropertyBinding.prototype, { // prototype, continued - - // these are used to "bind" a nonexistent property - _getValue_unavailable: function() {}, - _setValue_unavailable: function() {}, - - // initial state of these methods that calls 'bind' - _getValue_unbound: PropertyBinding.prototype.getValue, - _setValue_unbound: PropertyBinding.prototype.setValue, - - BindingType: { - Direct: 0, - EntireArray: 1, - ArrayElement: 2, - HasFromToArray: 3 - }, - - Versioning: { - None: 0, - NeedsUpdate: 1, - MatrixWorldNeedsUpdate: 2 - }, - - GetterByBindingType: [ - - function getValue_direct( buffer, offset ) { - - buffer[ offset ] = this.node[ this.propertyName ]; - - }, - - function getValue_array( buffer, offset ) { - - var source = this.resolvedProperty; - - for ( var i = 0, n = source.length; i !== n; ++ i ) { - - buffer[ offset ++ ] = source[ i ]; - - } - - }, - - function getValue_arrayElement( buffer, offset ) { - - buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; - - }, - - function getValue_toArray( buffer, offset ) { - - this.resolvedProperty.toArray( buffer, offset ); - - } - - ], - - SetterByBindingTypeAndVersioning: [ - - [ - // Direct - - function setValue_direct( buffer, offset ) { - - this.node[ this.propertyName ] = buffer[ offset ]; - - }, - - function setValue_direct_setNeedsUpdate( buffer, offset ) { - - this.node[ this.propertyName ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; - - }, - - function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { - - this.node[ this.propertyName ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - ], [ - - // EntireArray - - function setValue_array( buffer, offset ) { - - var dest = this.resolvedProperty; - - for ( var i = 0, n = dest.length; i !== n; ++ i ) { - - dest[ i ] = buffer[ offset ++ ]; - - } - - }, - - function setValue_array_setNeedsUpdate( buffer, offset ) { - - var dest = this.resolvedProperty; - - for ( var i = 0, n = dest.length; i !== n; ++ i ) { - - dest[ i ] = buffer[ offset ++ ]; - - } - - this.targetObject.needsUpdate = true; - - }, - - function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { - - var dest = this.resolvedProperty; - - for ( var i = 0, n = dest.length; i !== n; ++ i ) { - - dest[ i ] = buffer[ offset ++ ]; - - } - - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - ], [ - - // ArrayElement - - function setValue_arrayElement( buffer, offset ) { - - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - - }, - - function setValue_arrayElement_setNeedsUpdate( buffer, offset ) { - - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; - - }, - - function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { - - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - ], [ - - // HasToFromArray - - function setValue_fromArray( buffer, offset ) { - - this.resolvedProperty.fromArray( buffer, offset ); - - }, - - function setValue_fromArray_setNeedsUpdate( buffer, offset ) { - - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.needsUpdate = true; - - }, - - function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { - - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - ] - - ] - - } ); - - PropertyBinding.Composite = - function( targetGroup, path, optionalParsedPath ) { - - var parsedPath = optionalParsedPath || - PropertyBinding.parseTrackName( path ); - - this._targetGroup = targetGroup; - this._bindings = targetGroup.subscribe_( path, parsedPath ); - - }; - - PropertyBinding.Composite.prototype = { - - constructor: PropertyBinding.Composite, - - getValue: function( array, offset ) { - - this.bind(); // bind all binding - - var firstValidIndex = this._targetGroup.nCachedObjects_, - binding = this._bindings[ firstValidIndex ]; - - // and only call .getValue on the first - if ( binding !== undefined ) binding.getValue( array, offset ); - - }, - - setValue: function( array, offset ) { - - var bindings = this._bindings; - - for ( var i = this._targetGroup.nCachedObjects_, - n = bindings.length; i !== n; ++ i ) { - - bindings[ i ].setValue( array, offset ); - - } - - }, - - bind: function() { - - var bindings = this._bindings; - - for ( var i = this._targetGroup.nCachedObjects_, - n = bindings.length; i !== n; ++ i ) { - - bindings[ i ].bind(); - - } - - }, - - unbind: function() { - - var bindings = this._bindings; - - for ( var i = this._targetGroup.nCachedObjects_, - n = bindings.length; i !== n; ++ i ) { - - bindings[ i ].unbind(); - - } - - } - - }; - - PropertyBinding.create = function( root, path, parsedPath ) { - - if ( ! ( root && root.isAnimationObjectGroup ) ) { - - return new PropertyBinding( root, path, parsedPath ); - - } else { - - return new PropertyBinding.Composite( root, path, parsedPath ); - - } - - }; - - PropertyBinding.parseTrackName = function( trackName ) { - - // matches strings in the form of: - // nodeName.property - // nodeName.property[accessor] - // nodeName.material.property[accessor] - // uuid.property[accessor] - // uuid.objectName[objectIndex].propertyName[propertyIndex] - // parentName/nodeName.property - // parentName/parentName/nodeName.property[index] - // .bone[Armature.DEF_cog].position - // scene:helium_balloon_model:helium_balloon_model.position - // created and tested via https://regex101.com/#javascript - - var re = /^((?:[\w-]+[\/:])*)([\w-]+)?(?:\.([\w-]+)(?:\[(.+)\])?)?\.([\w-]+)(?:\[(.+)\])?$/; - var matches = re.exec( trackName ); - - if ( ! matches ) { - - throw new Error( "cannot parse trackName at all: " + trackName ); - - } - - var results = { - // directoryName: matches[ 1 ], // (tschw) currently unused - nodeName: matches[ 2 ], // allowed to be null, specified root node. - objectName: matches[ 3 ], - objectIndex: matches[ 4 ], - propertyName: matches[ 5 ], - propertyIndex: matches[ 6 ] // allowed to be null, specifies that the whole property is set. - }; - - if ( results.propertyName === null || results.propertyName.length === 0 ) { - - throw new Error( "can not parse propertyName from trackName: " + trackName ); - - } - - return results; - - }; - - PropertyBinding.findNode = function( root, nodeName ) { - - if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) { - - return root; - - } - - // search into skeleton bones. - if ( root.skeleton ) { - - var searchSkeleton = function( skeleton ) { - - for( var i = 0; i < skeleton.bones.length; i ++ ) { - - var bone = skeleton.bones[ i ]; - - if ( bone.name === nodeName ) { - - return bone; - - } - } - - return null; - - }; - - var bone = searchSkeleton( root.skeleton ); - - if ( bone ) { - - return bone; - - } - } - - // search into node subtree. - if ( root.children ) { - - var searchNodeSubtree = function( children ) { - - for( var i = 0; i < children.length; i ++ ) { - - var childNode = children[ i ]; - - if ( childNode.name === nodeName || childNode.uuid === nodeName ) { - - return childNode; - - } - - var result = searchNodeSubtree( childNode.children ); - - if ( result ) return result; - - } - - return null; - - }; - - var subTreeNode = searchNodeSubtree( root.children ); - - if ( subTreeNode ) { - - return subTreeNode; - - } - - } - - return null; - - }; - - /** - * - * A group of objects that receives a shared animation state. - * - * Usage: - * - * - Add objects you would otherwise pass as 'root' to the - * constructor or the .clipAction method of AnimationMixer. - * - * - Instead pass this object as 'root'. - * - * - You can also add and remove objects later when the mixer - * is running. - * - * Note: - * - * Objects of this class appear as one object to the mixer, - * so cache control of the individual objects must be done - * on the group. - * - * Limitation: - * - * - The animated properties must be compatible among the - * all objects in the group. - * - * - A single property can either be controlled through a - * target group or directly, but not both. - * - * @author tschw - */ - - function AnimationObjectGroup( var_args ) { - - this.uuid = _Math.generateUUID(); - - // cached objects followed by the active ones - this._objects = Array.prototype.slice.call( arguments ); - - this.nCachedObjects_ = 0; // threshold - // note: read by PropertyBinding.Composite - - var indices = {}; - this._indicesByUUID = indices; // for bookkeeping - - for ( var i = 0, n = arguments.length; i !== n; ++ i ) { - - indices[ arguments[ i ].uuid ] = i; - - } - - this._paths = []; // inside: string - this._parsedPaths = []; // inside: { we don't care, here } - this._bindings = []; // inside: Array< PropertyBinding > - this._bindingsIndicesByPath = {}; // inside: indices in these arrays - - var scope = this; - - this.stats = { - - objects: { - get total() { return scope._objects.length; }, - get inUse() { return this.total - scope.nCachedObjects_; } - }, - - get bindingsPerObject() { return scope._bindings.length; } - - }; - - } - - AnimationObjectGroup.prototype = { - - constructor: AnimationObjectGroup, - - isAnimationObjectGroup: true, - - add: function( var_args ) { - - var objects = this._objects, - nObjects = objects.length, - nCachedObjects = this.nCachedObjects_, - indicesByUUID = this._indicesByUUID, - paths = this._paths, - parsedPaths = this._parsedPaths, - bindings = this._bindings, - nBindings = bindings.length; - - for ( var i = 0, n = arguments.length; i !== n; ++ i ) { - - var object = arguments[ i ], - uuid = object.uuid, - index = indicesByUUID[ uuid ], - knownObject = undefined; - - if ( index === undefined ) { - - // unknown object -> add it to the ACTIVE region - - index = nObjects ++; - indicesByUUID[ uuid ] = index; - objects.push( object ); - - // accounting is done, now do the same for all bindings - - for ( var j = 0, m = nBindings; j !== m; ++ j ) { - - bindings[ j ].push( - new PropertyBinding( - object, paths[ j ], parsedPaths[ j ] ) ); - - } - - } else if ( index < nCachedObjects ) { - - knownObject = objects[ index ]; - - // move existing object to the ACTIVE region - - var firstActiveIndex = -- nCachedObjects, - lastCachedObject = objects[ firstActiveIndex ]; - - indicesByUUID[ lastCachedObject.uuid ] = index; - objects[ index ] = lastCachedObject; - - indicesByUUID[ uuid ] = firstActiveIndex; - objects[ firstActiveIndex ] = object; - - // accounting is done, now do the same for all bindings - - for ( var j = 0, m = nBindings; j !== m; ++ j ) { - - var bindingsForPath = bindings[ j ], - lastCached = bindingsForPath[ firstActiveIndex ], - binding = bindingsForPath[ index ]; - - bindingsForPath[ index ] = lastCached; - - if ( binding === undefined ) { - - // since we do not bother to create new bindings - // for objects that are cached, the binding may - // or may not exist - - binding = new PropertyBinding( - object, paths[ j ], parsedPaths[ j ] ); - - } - - bindingsForPath[ firstActiveIndex ] = binding; - - } - - } else if ( objects[ index ] !== knownObject) { - - console.error( "Different objects with the same UUID " + - "detected. Clean the caches or recreate your " + - "infrastructure when reloading scenes..." ); - - } // else the object is already where we want it to be - - } // for arguments - - this.nCachedObjects_ = nCachedObjects; - - }, - - remove: function( var_args ) { - - var objects = this._objects, - nCachedObjects = this.nCachedObjects_, - indicesByUUID = this._indicesByUUID, - bindings = this._bindings, - nBindings = bindings.length; - - for ( var i = 0, n = arguments.length; i !== n; ++ i ) { - - var object = arguments[ i ], - uuid = object.uuid, - index = indicesByUUID[ uuid ]; - - if ( index !== undefined && index >= nCachedObjects ) { - - // move existing object into the CACHED region - - var lastCachedIndex = nCachedObjects ++, - firstActiveObject = objects[ lastCachedIndex ]; - - indicesByUUID[ firstActiveObject.uuid ] = index; - objects[ index ] = firstActiveObject; - - indicesByUUID[ uuid ] = lastCachedIndex; - objects[ lastCachedIndex ] = object; - - // accounting is done, now do the same for all bindings - - for ( var j = 0, m = nBindings; j !== m; ++ j ) { - - var bindingsForPath = bindings[ j ], - firstActive = bindingsForPath[ lastCachedIndex ], - binding = bindingsForPath[ index ]; - - bindingsForPath[ index ] = firstActive; - bindingsForPath[ lastCachedIndex ] = binding; - - } - - } - - } // for arguments - - this.nCachedObjects_ = nCachedObjects; - - }, - - // remove & forget - uncache: function( var_args ) { - - var objects = this._objects, - nObjects = objects.length, - nCachedObjects = this.nCachedObjects_, - indicesByUUID = this._indicesByUUID, - bindings = this._bindings, - nBindings = bindings.length; - - for ( var i = 0, n = arguments.length; i !== n; ++ i ) { - - var object = arguments[ i ], - uuid = object.uuid, - index = indicesByUUID[ uuid ]; - - if ( index !== undefined ) { - - delete indicesByUUID[ uuid ]; - - if ( index < nCachedObjects ) { - - // object is cached, shrink the CACHED region - - var firstActiveIndex = -- nCachedObjects, - lastCachedObject = objects[ firstActiveIndex ], - lastIndex = -- nObjects, - lastObject = objects[ lastIndex ]; - - // last cached object takes this object's place - indicesByUUID[ lastCachedObject.uuid ] = index; - objects[ index ] = lastCachedObject; - - // last object goes to the activated slot and pop - indicesByUUID[ lastObject.uuid ] = firstActiveIndex; - objects[ firstActiveIndex ] = lastObject; - objects.pop(); - - // accounting is done, now do the same for all bindings - - for ( var j = 0, m = nBindings; j !== m; ++ j ) { - - var bindingsForPath = bindings[ j ], - lastCached = bindingsForPath[ firstActiveIndex ], - last = bindingsForPath[ lastIndex ]; - - bindingsForPath[ index ] = lastCached; - bindingsForPath[ firstActiveIndex ] = last; - bindingsForPath.pop(); - - } - - } else { - - // object is active, just swap with the last and pop - - var lastIndex = -- nObjects, - lastObject = objects[ lastIndex ]; - - indicesByUUID[ lastObject.uuid ] = index; - objects[ index ] = lastObject; - objects.pop(); - - // accounting is done, now do the same for all bindings - - for ( var j = 0, m = nBindings; j !== m; ++ j ) { - - var bindingsForPath = bindings[ j ]; - - bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; - bindingsForPath.pop(); - - } - - } // cached or active - - } // if object is known - - } // for arguments - - this.nCachedObjects_ = nCachedObjects; - - }, - - // Internal interface used by befriended PropertyBinding.Composite: - - subscribe_: function( path, parsedPath ) { - // returns an array of bindings for the given path that is changed - // according to the contained objects in the group - - var indicesByPath = this._bindingsIndicesByPath, - index = indicesByPath[ path ], - bindings = this._bindings; - - if ( index !== undefined ) return bindings[ index ]; - - var paths = this._paths, - parsedPaths = this._parsedPaths, - objects = this._objects, - nObjects = objects.length, - nCachedObjects = this.nCachedObjects_, - bindingsForPath = new Array( nObjects ); - - index = bindings.length; - - indicesByPath[ path ] = index; - - paths.push( path ); - parsedPaths.push( parsedPath ); - bindings.push( bindingsForPath ); - - for ( var i = nCachedObjects, - n = objects.length; i !== n; ++ i ) { - - var object = objects[ i ]; - - bindingsForPath[ i ] = - new PropertyBinding( object, path, parsedPath ); - - } - - return bindingsForPath; - - }, - - unsubscribe_: function( path ) { - // tells the group to forget about a property path and no longer - // update the array previously obtained with 'subscribe_' - - var indicesByPath = this._bindingsIndicesByPath, - index = indicesByPath[ path ]; - - if ( index !== undefined ) { - - var paths = this._paths, - parsedPaths = this._parsedPaths, - bindings = this._bindings, - lastBindingsIndex = bindings.length - 1, - lastBindings = bindings[ lastBindingsIndex ], - lastBindingsPath = path[ lastBindingsIndex ]; - - indicesByPath[ lastBindingsPath ] = index; - - bindings[ index ] = lastBindings; - bindings.pop(); - - parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; - parsedPaths.pop(); - - paths[ index ] = paths[ lastBindingsIndex ]; - paths.pop(); - - } - - } - - }; - - /** - * - * Action provided by AnimationMixer for scheduling clip playback on specific - * objects. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - * - */ - - function AnimationAction( mixer, clip, localRoot ) { - - this._mixer = mixer; - this._clip = clip; - this._localRoot = localRoot || null; - - var tracks = clip.tracks, - nTracks = tracks.length, - interpolants = new Array( nTracks ); - - var interpolantSettings = { - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding - }; - - for ( var i = 0; i !== nTracks; ++ i ) { - - var interpolant = tracks[ i ].createInterpolant( null ); - interpolants[ i ] = interpolant; - interpolant.settings = interpolantSettings; - - } - - this._interpolantSettings = interpolantSettings; - - this._interpolants = interpolants; // bound by the mixer - - // inside: PropertyMixer (managed by the mixer) - this._propertyBindings = new Array( nTracks ); - - this._cacheIndex = null; // for the memory manager - this._byClipCacheIndex = null; // for the memory manager - - this._timeScaleInterpolant = null; - this._weightInterpolant = null; - - this.loop = LoopRepeat; - this._loopCount = -1; - - // global mixer time when the action is to be started - // it's set back to 'null' upon start of the action - this._startTime = null; - - // scaled local time of the action - // gets clamped or wrapped to 0..clip.duration according to loop - this.time = 0; - - this.timeScale = 1; - this._effectiveTimeScale = 1; - - this.weight = 1; - this._effectiveWeight = 1; - - this.repetitions = Infinity; // no. of repetitions when looping - - this.paused = false; // false -> zero effective time scale - this.enabled = true; // true -> zero effective weight - - this.clampWhenFinished = false; // keep feeding the last frame? - - this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate - this.zeroSlopeAtEnd = true; // clips for start, loop and end - - } - - AnimationAction.prototype = { - - constructor: AnimationAction, - - // State & Scheduling - - play: function() { - - this._mixer._activateAction( this ); - - return this; - - }, - - stop: function() { - - this._mixer._deactivateAction( this ); - - return this.reset(); - - }, - - reset: function() { - - this.paused = false; - this.enabled = true; - - this.time = 0; // restart clip - this._loopCount = -1; // forget previous loops - this._startTime = null; // forget scheduling - - return this.stopFading().stopWarping(); - - }, - - isRunning: function() { - - return this.enabled && ! this.paused && this.timeScale !== 0 && - this._startTime === null && this._mixer._isActiveAction( this ); - - }, - - // return true when play has been called - isScheduled: function() { - - return this._mixer._isActiveAction( this ); - - }, - - startAt: function( time ) { - - this._startTime = time; - - return this; - - }, - - setLoop: function( mode, repetitions ) { - - this.loop = mode; - this.repetitions = repetitions; - - return this; - - }, - - // Weight - - // set the weight stopping any scheduled fading - // although .enabled = false yields an effective weight of zero, this - // method does *not* change .enabled, because it would be confusing - setEffectiveWeight: function( weight ) { - - this.weight = weight; - - // note: same logic as when updated at runtime - this._effectiveWeight = this.enabled ? weight : 0; - - return this.stopFading(); - - }, - - // return the weight considering fading and .enabled - getEffectiveWeight: function() { - - return this._effectiveWeight; - - }, - - fadeIn: function( duration ) { - - return this._scheduleFading( duration, 0, 1 ); - - }, - - fadeOut: function( duration ) { - - return this._scheduleFading( duration, 1, 0 ); - - }, - - crossFadeFrom: function( fadeOutAction, duration, warp ) { - - fadeOutAction.fadeOut( duration ); - this.fadeIn( duration ); - - if( warp ) { - - var fadeInDuration = this._clip.duration, - fadeOutDuration = fadeOutAction._clip.duration, - - startEndRatio = fadeOutDuration / fadeInDuration, - endStartRatio = fadeInDuration / fadeOutDuration; - - fadeOutAction.warp( 1.0, startEndRatio, duration ); - this.warp( endStartRatio, 1.0, duration ); - - } - - return this; - - }, - - crossFadeTo: function( fadeInAction, duration, warp ) { - - return fadeInAction.crossFadeFrom( this, duration, warp ); - - }, - - stopFading: function() { - - var weightInterpolant = this._weightInterpolant; - - if ( weightInterpolant !== null ) { - - this._weightInterpolant = null; - this._mixer._takeBackControlInterpolant( weightInterpolant ); - - } - - return this; - - }, - - // Time Scale Control - - // set the weight stopping any scheduled warping - // although .paused = true yields an effective time scale of zero, this - // method does *not* change .paused, because it would be confusing - setEffectiveTimeScale: function( timeScale ) { - - this.timeScale = timeScale; - this._effectiveTimeScale = this.paused ? 0 :timeScale; - - return this.stopWarping(); - - }, - - // return the time scale considering warping and .paused - getEffectiveTimeScale: function() { - - return this._effectiveTimeScale; - - }, - - setDuration: function( duration ) { - - this.timeScale = this._clip.duration / duration; - - return this.stopWarping(); - - }, - - syncWith: function( action ) { - - this.time = action.time; - this.timeScale = action.timeScale; - - return this.stopWarping(); - - }, - - halt: function( duration ) { - - return this.warp( this._effectiveTimeScale, 0, duration ); - - }, - - warp: function( startTimeScale, endTimeScale, duration ) { - - var mixer = this._mixer, now = mixer.time, - interpolant = this._timeScaleInterpolant, - - timeScale = this.timeScale; - - if ( interpolant === null ) { - - interpolant = mixer._lendControlInterpolant(); - this._timeScaleInterpolant = interpolant; - - } - - var times = interpolant.parameterPositions, - values = interpolant.sampleValues; - - times[ 0 ] = now; - times[ 1 ] = now + duration; - - values[ 0 ] = startTimeScale / timeScale; - values[ 1 ] = endTimeScale / timeScale; - - return this; - - }, - - stopWarping: function() { - - var timeScaleInterpolant = this._timeScaleInterpolant; - - if ( timeScaleInterpolant !== null ) { - - this._timeScaleInterpolant = null; - this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); - - } - - return this; - - }, - - // Object Accessors - - getMixer: function() { - - return this._mixer; - - }, - - getClip: function() { - - return this._clip; - - }, - - getRoot: function() { - - return this._localRoot || this._mixer._root; - - }, - - // Interna - - _update: function( time, deltaTime, timeDirection, accuIndex ) { - // called by the mixer - - var startTime = this._startTime; - - if ( startTime !== null ) { - - // check for scheduled start of action - - var timeRunning = ( time - startTime ) * timeDirection; - if ( timeRunning < 0 || timeDirection === 0 ) { - - return; // yet to come / don't decide when delta = 0 - - } - - // start - - this._startTime = null; // unschedule - deltaTime = timeDirection * timeRunning; - - } - - // apply time scale and advance time - - deltaTime *= this._updateTimeScale( time ); - var clipTime = this._updateTime( deltaTime ); - - // note: _updateTime may disable the action resulting in - // an effective weight of 0 - - var weight = this._updateWeight( time ); - - if ( weight > 0 ) { - - var interpolants = this._interpolants; - var propertyMixers = this._propertyBindings; - - for ( var j = 0, m = interpolants.length; j !== m; ++ j ) { - - interpolants[ j ].evaluate( clipTime ); - propertyMixers[ j ].accumulate( accuIndex, weight ); - - } - - } - - }, - - _updateWeight: function( time ) { - - var weight = 0; - - if ( this.enabled ) { - - weight = this.weight; - var interpolant = this._weightInterpolant; - - if ( interpolant !== null ) { - - var interpolantValue = interpolant.evaluate( time )[ 0 ]; - - weight *= interpolantValue; - - if ( time > interpolant.parameterPositions[ 1 ] ) { - - this.stopFading(); - - if ( interpolantValue === 0 ) { - - // faded out, disable - this.enabled = false; - - } - - } - - } - - } - - this._effectiveWeight = weight; - return weight; - - }, - - _updateTimeScale: function( time ) { - - var timeScale = 0; - - if ( ! this.paused ) { - - timeScale = this.timeScale; - - var interpolant = this._timeScaleInterpolant; - - if ( interpolant !== null ) { - - var interpolantValue = interpolant.evaluate( time )[ 0 ]; - - timeScale *= interpolantValue; - - if ( time > interpolant.parameterPositions[ 1 ] ) { - - this.stopWarping(); - - if ( timeScale === 0 ) { - - // motion has halted, pause - this.paused = true; - - } else { - - // warp done - apply final time scale - this.timeScale = timeScale; - - } - - } - - } - - } - - this._effectiveTimeScale = timeScale; - return timeScale; - - }, - - _updateTime: function( deltaTime ) { - - var time = this.time + deltaTime; - - if ( deltaTime === 0 ) return time; - - var duration = this._clip.duration, - - loop = this.loop, - loopCount = this._loopCount; - - if ( loop === LoopOnce ) { - - if ( loopCount === -1 ) { - // just started - - this._loopCount = 0; - this._setEndings( true, true, false ); - - } - - handle_stop: { - - if ( time >= duration ) { - - time = duration; - - } else if ( time < 0 ) { - - time = 0; - - } else break handle_stop; - - if ( this.clampWhenFinished ) this.paused = true; - else this.enabled = false; - - this._mixer.dispatchEvent( { - type: 'finished', action: this, - direction: deltaTime < 0 ? -1 : 1 - } ); - - } - - } else { // repetitive Repeat or PingPong - - var pingPong = ( loop === LoopPingPong ); - - if ( loopCount === -1 ) { - // just started - - if ( deltaTime >= 0 ) { - - loopCount = 0; - - this._setEndings( - true, this.repetitions === 0, pingPong ); - - } else { - - // when looping in reverse direction, the initial - // transition through zero counts as a repetition, - // so leave loopCount at -1 - - this._setEndings( - this.repetitions === 0, true, pingPong ); - - } - - } - - if ( time >= duration || time < 0 ) { - // wrap around - - var loopDelta = Math.floor( time / duration ); // signed - time -= duration * loopDelta; - - loopCount += Math.abs( loopDelta ); - - var pending = this.repetitions - loopCount; - - if ( pending < 0 ) { - // have to stop (switch state, clamp time, fire event) - - if ( this.clampWhenFinished ) this.paused = true; - else this.enabled = false; - - time = deltaTime > 0 ? duration : 0; - - this._mixer.dispatchEvent( { - type: 'finished', action: this, - direction: deltaTime > 0 ? 1 : -1 - } ); - - } else { - // keep running - - if ( pending === 0 ) { - // entering the last round - - var atStart = deltaTime < 0; - this._setEndings( atStart, ! atStart, pingPong ); - - } else { - - this._setEndings( false, false, pingPong ); - - } - - this._loopCount = loopCount; - - this._mixer.dispatchEvent( { - type: 'loop', action: this, loopDelta: loopDelta - } ); - - } - - } - - if ( pingPong && ( loopCount & 1 ) === 1 ) { - // invert time for the "pong round" - - this.time = time; - return duration - time; - - } - - } - - this.time = time; - return time; - - }, - - _setEndings: function( atStart, atEnd, pingPong ) { - - var settings = this._interpolantSettings; - - if ( pingPong ) { - - settings.endingStart = ZeroSlopeEnding; - settings.endingEnd = ZeroSlopeEnding; - - } else { - - // assuming for LoopOnce atStart == atEnd == true - - if ( atStart ) { - - settings.endingStart = this.zeroSlopeAtStart ? - ZeroSlopeEnding : ZeroCurvatureEnding; - - } else { - - settings.endingStart = WrapAroundEnding; - - } - - if ( atEnd ) { - - settings.endingEnd = this.zeroSlopeAtEnd ? - ZeroSlopeEnding : ZeroCurvatureEnding; - - } else { - - settings.endingEnd = WrapAroundEnding; - - } - - } - - }, - - _scheduleFading: function( duration, weightNow, weightThen ) { - - var mixer = this._mixer, now = mixer.time, - interpolant = this._weightInterpolant; - - if ( interpolant === null ) { - - interpolant = mixer._lendControlInterpolant(); - this._weightInterpolant = interpolant; - - } - - var times = interpolant.parameterPositions, - values = interpolant.sampleValues; - - times[ 0 ] = now; values[ 0 ] = weightNow; - times[ 1 ] = now + duration; values[ 1 ] = weightThen; - - return this; - - } - - }; - - /** - * - * Player for AnimationClips. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function AnimationMixer( root ) { - - this._root = root; - this._initMemoryManager(); - this._accuIndex = 0; - - this.time = 0; - - this.timeScale = 1.0; - - } - - Object.assign( AnimationMixer.prototype, EventDispatcher.prototype, { - - // return an action for a clip optionally using a custom root target - // object (this method allocates a lot of dynamic memory in case a - // previously unknown clip/root combination is specified) - clipAction: function( clip, optionalRoot ) { - - var root = optionalRoot || this._root, - rootUuid = root.uuid, - - clipObject = typeof clip === 'string' ? - AnimationClip.findByName( root, clip ) : clip, - - clipUuid = clipObject !== null ? clipObject.uuid : clip, - - actionsForClip = this._actionsByClip[ clipUuid ], - prototypeAction = null; - - if ( actionsForClip !== undefined ) { - - var existingAction = - actionsForClip.actionByRoot[ rootUuid ]; - - if ( existingAction !== undefined ) { - - return existingAction; - - } - - // we know the clip, so we don't have to parse all - // the bindings again but can just copy - prototypeAction = actionsForClip.knownActions[ 0 ]; - - // also, take the clip from the prototype action - if ( clipObject === null ) - clipObject = prototypeAction._clip; - - } - - // clip must be known when specified via string - if ( clipObject === null ) return null; - - // allocate all resources required to run it - var newAction = new AnimationAction( this, clipObject, optionalRoot ); - - this._bindAction( newAction, prototypeAction ); - - // and make the action known to the memory manager - this._addInactiveAction( newAction, clipUuid, rootUuid ); - - return newAction; - - }, - - // get an existing action - existingAction: function( clip, optionalRoot ) { - - var root = optionalRoot || this._root, - rootUuid = root.uuid, - - clipObject = typeof clip === 'string' ? - AnimationClip.findByName( root, clip ) : clip, - - clipUuid = clipObject ? clipObject.uuid : clip, - - actionsForClip = this._actionsByClip[ clipUuid ]; - - if ( actionsForClip !== undefined ) { - - return actionsForClip.actionByRoot[ rootUuid ] || null; - - } - - return null; - - }, - - // deactivates all previously scheduled actions - stopAllAction: function() { - - var actions = this._actions, - nActions = this._nActiveActions, - bindings = this._bindings, - nBindings = this._nActiveBindings; - - this._nActiveActions = 0; - this._nActiveBindings = 0; - - for ( var i = 0; i !== nActions; ++ i ) { - - actions[ i ].reset(); - - } - - for ( var i = 0; i !== nBindings; ++ i ) { - - bindings[ i ].useCount = 0; - - } - - return this; - - }, - - // advance the time and update apply the animation - update: function( deltaTime ) { - - deltaTime *= this.timeScale; - - var actions = this._actions, - nActions = this._nActiveActions, - - time = this.time += deltaTime, - timeDirection = Math.sign( deltaTime ), - - accuIndex = this._accuIndex ^= 1; - - // run active actions - - for ( var i = 0; i !== nActions; ++ i ) { - - var action = actions[ i ]; - - if ( action.enabled ) { - - action._update( time, deltaTime, timeDirection, accuIndex ); - - } - - } - - // update scene graph - - var bindings = this._bindings, - nBindings = this._nActiveBindings; - - for ( var i = 0; i !== nBindings; ++ i ) { - - bindings[ i ].apply( accuIndex ); - - } - - return this; - - }, - - // return this mixer's root target object - getRoot: function() { - - return this._root; - - }, - - // free all resources specific to a particular clip - uncacheClip: function( clip ) { - - var actions = this._actions, - clipUuid = clip.uuid, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ]; - - if ( actionsForClip !== undefined ) { - - // note: just calling _removeInactiveAction would mess up the - // iteration state and also require updating the state we can - // just throw away - - var actionsToRemove = actionsForClip.knownActions; - - for ( var i = 0, n = actionsToRemove.length; i !== n; ++ i ) { - - var action = actionsToRemove[ i ]; - - this._deactivateAction( action ); - - var cacheIndex = action._cacheIndex, - lastInactiveAction = actions[ actions.length - 1 ]; - - action._cacheIndex = null; - action._byClipCacheIndex = null; - - lastInactiveAction._cacheIndex = cacheIndex; - actions[ cacheIndex ] = lastInactiveAction; - actions.pop(); - - this._removeInactiveBindingsForAction( action ); - - } - - delete actionsByClip[ clipUuid ]; - - } - - }, - - // free all resources specific to a particular root target object - uncacheRoot: function( root ) { - - var rootUuid = root.uuid, - actionsByClip = this._actionsByClip; - - for ( var clipUuid in actionsByClip ) { - - var actionByRoot = actionsByClip[ clipUuid ].actionByRoot, - action = actionByRoot[ rootUuid ]; - - if ( action !== undefined ) { - - this._deactivateAction( action ); - this._removeInactiveAction( action ); - - } - - } - - var bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ]; - - if ( bindingByName !== undefined ) { - - for ( var trackName in bindingByName ) { - - var binding = bindingByName[ trackName ]; - binding.restoreOriginalState(); - this._removeInactiveBinding( binding ); - - } - - } - - }, - - // remove a targeted clip from the cache - uncacheAction: function( clip, optionalRoot ) { - - var action = this.existingAction( clip, optionalRoot ); - - if ( action !== null ) { - - this._deactivateAction( action ); - this._removeInactiveAction( action ); - - } - - } - - } ); - - // Implementation details: - - Object.assign( AnimationMixer.prototype, { - - _bindAction: function( action, prototypeAction ) { - - var root = action._localRoot || this._root, - tracks = action._clip.tracks, - nTracks = tracks.length, - bindings = action._propertyBindings, - interpolants = action._interpolants, - rootUuid = root.uuid, - bindingsByRoot = this._bindingsByRootAndName, - bindingsByName = bindingsByRoot[ rootUuid ]; - - if ( bindingsByName === undefined ) { - - bindingsByName = {}; - bindingsByRoot[ rootUuid ] = bindingsByName; - - } - - for ( var i = 0; i !== nTracks; ++ i ) { - - var track = tracks[ i ], - trackName = track.name, - binding = bindingsByName[ trackName ]; - - if ( binding !== undefined ) { - - bindings[ i ] = binding; - - } else { - - binding = bindings[ i ]; - - if ( binding !== undefined ) { - - // existing binding, make sure the cache knows - - if ( binding._cacheIndex === null ) { - - ++ binding.referenceCount; - this._addInactiveBinding( binding, rootUuid, trackName ); - - } - - continue; - - } - - var path = prototypeAction && prototypeAction. - _propertyBindings[ i ].binding.parsedPath; - - binding = new PropertyMixer( - PropertyBinding.create( root, trackName, path ), - track.ValueTypeName, track.getValueSize() ); - - ++ binding.referenceCount; - this._addInactiveBinding( binding, rootUuid, trackName ); - - bindings[ i ] = binding; - - } - - interpolants[ i ].resultBuffer = binding.buffer; - - } - - }, - - _activateAction: function( action ) { - - if ( ! this._isActiveAction( action ) ) { - - if ( action._cacheIndex === null ) { - - // this action has been forgotten by the cache, but the user - // appears to be still using it -> rebind - - var rootUuid = ( action._localRoot || this._root ).uuid, - clipUuid = action._clip.uuid, - actionsForClip = this._actionsByClip[ clipUuid ]; - - this._bindAction( action, - actionsForClip && actionsForClip.knownActions[ 0 ] ); - - this._addInactiveAction( action, clipUuid, rootUuid ); - - } - - var bindings = action._propertyBindings; - - // increment reference counts / sort out state - for ( var i = 0, n = bindings.length; i !== n; ++ i ) { - - var binding = bindings[ i ]; - - if ( binding.useCount ++ === 0 ) { - - this._lendBinding( binding ); - binding.saveOriginalState(); - - } - - } - - this._lendAction( action ); - - } - - }, - - _deactivateAction: function( action ) { - - if ( this._isActiveAction( action ) ) { - - var bindings = action._propertyBindings; - - // decrement reference counts / sort out state - for ( var i = 0, n = bindings.length; i !== n; ++ i ) { - - var binding = bindings[ i ]; - - if ( -- binding.useCount === 0 ) { - - binding.restoreOriginalState(); - this._takeBackBinding( binding ); - - } - - } - - this._takeBackAction( action ); - - } - - }, - - // Memory manager - - _initMemoryManager: function() { - - this._actions = []; // 'nActiveActions' followed by inactive ones - this._nActiveActions = 0; - - this._actionsByClip = {}; - // inside: - // { - // knownActions: Array< AnimationAction > - used as prototypes - // actionByRoot: AnimationAction - lookup - // } - - - this._bindings = []; // 'nActiveBindings' followed by inactive ones - this._nActiveBindings = 0; - - this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > - - - this._controlInterpolants = []; // same game as above - this._nActiveControlInterpolants = 0; - - var scope = this; - - this.stats = { - - actions: { - get total() { return scope._actions.length; }, - get inUse() { return scope._nActiveActions; } - }, - bindings: { - get total() { return scope._bindings.length; }, - get inUse() { return scope._nActiveBindings; } - }, - controlInterpolants: { - get total() { return scope._controlInterpolants.length; }, - get inUse() { return scope._nActiveControlInterpolants; } - } - - }; - - }, - - // Memory management for AnimationAction objects - - _isActiveAction: function( action ) { - - var index = action._cacheIndex; - return index !== null && index < this._nActiveActions; - - }, - - _addInactiveAction: function( action, clipUuid, rootUuid ) { - - var actions = this._actions, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ]; - - if ( actionsForClip === undefined ) { - - actionsForClip = { - - knownActions: [ action ], - actionByRoot: {} - - }; - - action._byClipCacheIndex = 0; - - actionsByClip[ clipUuid ] = actionsForClip; - - } else { - - var knownActions = actionsForClip.knownActions; - - action._byClipCacheIndex = knownActions.length; - knownActions.push( action ); - - } - - action._cacheIndex = actions.length; - actions.push( action ); - - actionsForClip.actionByRoot[ rootUuid ] = action; - - }, - - _removeInactiveAction: function( action ) { - - var actions = this._actions, - lastInactiveAction = actions[ actions.length - 1 ], - cacheIndex = action._cacheIndex; - - lastInactiveAction._cacheIndex = cacheIndex; - actions[ cacheIndex ] = lastInactiveAction; - actions.pop(); - - action._cacheIndex = null; - - - var clipUuid = action._clip.uuid, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ], - knownActionsForClip = actionsForClip.knownActions, - - lastKnownAction = - knownActionsForClip[ knownActionsForClip.length - 1 ], - - byClipCacheIndex = action._byClipCacheIndex; - - lastKnownAction._byClipCacheIndex = byClipCacheIndex; - knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; - knownActionsForClip.pop(); - - action._byClipCacheIndex = null; - - - var actionByRoot = actionsForClip.actionByRoot, - rootUuid = ( actions._localRoot || this._root ).uuid; - - delete actionByRoot[ rootUuid ]; - - if ( knownActionsForClip.length === 0 ) { - - delete actionsByClip[ clipUuid ]; - - } - - this._removeInactiveBindingsForAction( action ); - - }, - - _removeInactiveBindingsForAction: function( action ) { - - var bindings = action._propertyBindings; - for ( var i = 0, n = bindings.length; i !== n; ++ i ) { - - var binding = bindings[ i ]; - - if ( -- binding.referenceCount === 0 ) { - - this._removeInactiveBinding( binding ); - - } - - } - - }, - - _lendAction: function( action ) { - - // [ active actions | inactive actions ] - // [ active actions >| inactive actions ] - // s a - // <-swap-> - // a s - - var actions = this._actions, - prevIndex = action._cacheIndex, - - lastActiveIndex = this._nActiveActions ++, - - firstInactiveAction = actions[ lastActiveIndex ]; - - action._cacheIndex = lastActiveIndex; - actions[ lastActiveIndex ] = action; - - firstInactiveAction._cacheIndex = prevIndex; - actions[ prevIndex ] = firstInactiveAction; - - }, - - _takeBackAction: function( action ) { - - // [ active actions | inactive actions ] - // [ active actions |< inactive actions ] - // a s - // <-swap-> - // s a - - var actions = this._actions, - prevIndex = action._cacheIndex, - - firstInactiveIndex = -- this._nActiveActions, - - lastActiveAction = actions[ firstInactiveIndex ]; - - action._cacheIndex = firstInactiveIndex; - actions[ firstInactiveIndex ] = action; - - lastActiveAction._cacheIndex = prevIndex; - actions[ prevIndex ] = lastActiveAction; - - }, - - // Memory management for PropertyMixer objects - - _addInactiveBinding: function( binding, rootUuid, trackName ) { - - var bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ], - - bindings = this._bindings; - - if ( bindingByName === undefined ) { - - bindingByName = {}; - bindingsByRoot[ rootUuid ] = bindingByName; - - } - - bindingByName[ trackName ] = binding; - - binding._cacheIndex = bindings.length; - bindings.push( binding ); - - }, - - _removeInactiveBinding: function( binding ) { - - var bindings = this._bindings, - propBinding = binding.binding, - rootUuid = propBinding.rootNode.uuid, - trackName = propBinding.path, - bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ], - - lastInactiveBinding = bindings[ bindings.length - 1 ], - cacheIndex = binding._cacheIndex; - - lastInactiveBinding._cacheIndex = cacheIndex; - bindings[ cacheIndex ] = lastInactiveBinding; - bindings.pop(); - - delete bindingByName[ trackName ]; - - remove_empty_map: { - - for ( var _ in bindingByName ) break remove_empty_map; - - delete bindingsByRoot[ rootUuid ]; - - } - - }, - - _lendBinding: function( binding ) { - - var bindings = this._bindings, - prevIndex = binding._cacheIndex, - - lastActiveIndex = this._nActiveBindings ++, - - firstInactiveBinding = bindings[ lastActiveIndex ]; - - binding._cacheIndex = lastActiveIndex; - bindings[ lastActiveIndex ] = binding; - - firstInactiveBinding._cacheIndex = prevIndex; - bindings[ prevIndex ] = firstInactiveBinding; - - }, - - _takeBackBinding: function( binding ) { - - var bindings = this._bindings, - prevIndex = binding._cacheIndex, - - firstInactiveIndex = -- this._nActiveBindings, - - lastActiveBinding = bindings[ firstInactiveIndex ]; - - binding._cacheIndex = firstInactiveIndex; - bindings[ firstInactiveIndex ] = binding; - - lastActiveBinding._cacheIndex = prevIndex; - bindings[ prevIndex ] = lastActiveBinding; - - }, - - - // Memory management of Interpolants for weight and time scale - - _lendControlInterpolant: function() { - - var interpolants = this._controlInterpolants, - lastActiveIndex = this._nActiveControlInterpolants ++, - interpolant = interpolants[ lastActiveIndex ]; - - if ( interpolant === undefined ) { - - interpolant = new LinearInterpolant( - new Float32Array( 2 ), new Float32Array( 2 ), - 1, this._controlInterpolantsResultBuffer ); - - interpolant.__cacheIndex = lastActiveIndex; - interpolants[ lastActiveIndex ] = interpolant; - - } - - return interpolant; - - }, - - _takeBackControlInterpolant: function( interpolant ) { - - var interpolants = this._controlInterpolants, - prevIndex = interpolant.__cacheIndex, - - firstInactiveIndex = -- this._nActiveControlInterpolants, - - lastActiveInterpolant = interpolants[ firstInactiveIndex ]; - - interpolant.__cacheIndex = firstInactiveIndex; - interpolants[ firstInactiveIndex ] = interpolant; - - lastActiveInterpolant.__cacheIndex = prevIndex; - interpolants[ prevIndex ] = lastActiveInterpolant; - - }, - - _controlInterpolantsResultBuffer: new Float32Array( 1 ) - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function Uniform( value ) { - - if ( typeof value === 'string' ) { - - console.warn( 'THREE.Uniform: Type parameter is no longer needed.' ); - value = arguments[ 1 ]; - - } - - this.value = value; - - } - - Uniform.prototype.clone = function () { - - return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() ); - - }; - - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ - - function InstancedBufferGeometry() { - - BufferGeometry.call( this ); - - this.type = 'InstancedBufferGeometry'; - this.maxInstancedCount = undefined; - - } - - InstancedBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - InstancedBufferGeometry.prototype.constructor = InstancedBufferGeometry; - - InstancedBufferGeometry.prototype.isInstancedBufferGeometry = true; - - InstancedBufferGeometry.prototype.addGroup = function ( start, count, materialIndex ) { - - this.groups.push( { - - start: start, - count: count, - materialIndex: materialIndex - - } ); - - }; - - InstancedBufferGeometry.prototype.copy = function ( source ) { - - var index = source.index; - - if ( index !== null ) { - - this.setIndex( index.clone() ); - - } - - var attributes = source.attributes; - - for ( var name in attributes ) { - - var attribute = attributes[ name ]; - this.addAttribute( name, attribute.clone() ); - - } - - var groups = source.groups; - - for ( var i = 0, l = groups.length; i < l; i ++ ) { - - var group = groups[ i ]; - this.addGroup( group.start, group.count, group.materialIndex ); - - } - - return this; - - }; - - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ - - function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) { - - this.uuid = _Math.generateUUID(); - - this.data = interleavedBuffer; - this.itemSize = itemSize; - this.offset = offset; - - this.normalized = normalized === true; - - } - - - InterleavedBufferAttribute.prototype = { - - constructor: InterleavedBufferAttribute, - - isInterleavedBufferAttribute: true, - - get count() { - - return this.data.count; - - }, - - get array() { - - return this.data.array; - - }, - - setX: function ( index, x ) { - - this.data.array[ index * this.data.stride + this.offset ] = x; - - return this; - - }, - - setY: function ( index, y ) { - - this.data.array[ index * this.data.stride + this.offset + 1 ] = y; - - return this; - - }, - - setZ: function ( index, z ) { - - this.data.array[ index * this.data.stride + this.offset + 2 ] = z; - - return this; - - }, - - setW: function ( index, w ) { - - this.data.array[ index * this.data.stride + this.offset + 3 ] = w; - - return this; - - }, - - getX: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset ]; - - }, - - getY: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 1 ]; - - }, - - getZ: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 2 ]; - - }, - - getW: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 3 ]; - - }, - - setXY: function ( index, x, y ) { - - index = index * this.data.stride + this.offset; - - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - - return this; - - }, - - setXYZ: function ( index, x, y, z ) { - - index = index * this.data.stride + this.offset; - - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; - - return this; - - }, - - setXYZW: function ( index, x, y, z, w ) { - - index = index * this.data.stride + this.offset; - - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; - this.data.array[ index + 3 ] = w; - - return this; - - } - - }; - - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ - - function InterleavedBuffer( array, stride ) { - - this.uuid = _Math.generateUUID(); - - this.array = array; - this.stride = stride; - this.count = array !== undefined ? array.length / stride : 0; - - this.dynamic = false; - this.updateRange = { offset: 0, count: - 1 }; - - this.onUploadCallback = function () {}; - - this.version = 0; - - } - - InterleavedBuffer.prototype = { - - constructor: InterleavedBuffer, - - isInterleavedBuffer: true, - - set needsUpdate( value ) { - - if ( value === true ) this.version ++; - - }, - - setArray: function ( array ) { - - if ( Array.isArray( array ) ) { - - throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); - - } - - this.count = array !== undefined ? array.length / this.stride : 0; - this.array = array; - - }, - - setDynamic: function ( value ) { - - this.dynamic = value; - - return this; - - }, - - copy: function ( source ) { - - this.array = new source.array.constructor( source.array ); - this.count = source.count; - this.stride = source.stride; - this.dynamic = source.dynamic; - - return this; - - }, - - copyAt: function ( index1, attribute, index2 ) { - - index1 *= this.stride; - index2 *= attribute.stride; - - for ( var i = 0, l = this.stride; i < l; i ++ ) { - - this.array[ index1 + i ] = attribute.array[ index2 + i ]; - - } - - return this; - - }, - - set: function ( value, offset ) { - - if ( offset === undefined ) offset = 0; - - this.array.set( value, offset ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - onUpload: function ( callback ) { - - this.onUploadCallback = callback; - - return this; - - } - - }; - - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ - - function InstancedInterleavedBuffer( array, stride, meshPerAttribute ) { - - InterleavedBuffer.call( this, array, stride ); - - this.meshPerAttribute = meshPerAttribute || 1; - - } - - InstancedInterleavedBuffer.prototype = Object.create( InterleavedBuffer.prototype ); - InstancedInterleavedBuffer.prototype.constructor = InstancedInterleavedBuffer; - - InstancedInterleavedBuffer.prototype.isInstancedInterleavedBuffer = true; - - InstancedInterleavedBuffer.prototype.copy = function ( source ) { - - InterleavedBuffer.prototype.copy.call( this, source ); - - this.meshPerAttribute = source.meshPerAttribute; - - return this; - - }; - - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ - - function InstancedBufferAttribute( array, itemSize, meshPerAttribute ) { - - BufferAttribute.call( this, array, itemSize ); - - this.meshPerAttribute = meshPerAttribute || 1; - - } - - InstancedBufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - InstancedBufferAttribute.prototype.constructor = InstancedBufferAttribute; - - InstancedBufferAttribute.prototype.isInstancedBufferAttribute = true; - - InstancedBufferAttribute.prototype.copy = function ( source ) { - - BufferAttribute.prototype.copy.call( this, source ); - - this.meshPerAttribute = source.meshPerAttribute; - - return this; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author bhouston / http://clara.io/ - * @author stephomi / http://stephaneginier.com/ - */ - - function Raycaster( origin, direction, near, far ) { - - this.ray = new Ray( origin, direction ); - // direction is assumed to be normalized (for accurate distance calculations) - - this.near = near || 0; - this.far = far || Infinity; - - this.params = { - Mesh: {}, - Line: {}, - LOD: {}, - Points: { threshold: 1 }, - Sprite: {} - }; - - Object.defineProperties( this.params, { - PointCloud: { - get: function () { - console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' ); - return this.Points; - } - } - } ); - - } - - function ascSort( a, b ) { - - return a.distance - b.distance; - - } - - function intersectObject( object, raycaster, intersects, recursive ) { - - if ( object.visible === false ) return; - - object.raycast( raycaster, intersects ); - - if ( recursive === true ) { - - var children = object.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - intersectObject( children[ i ], raycaster, intersects, true ); - - } - - } - - } - - // - - Raycaster.prototype = { - - constructor: Raycaster, - - linePrecision: 1, - - set: function ( origin, direction ) { - - // direction is assumed to be normalized (for accurate distance calculations) - - this.ray.set( origin, direction ); - - }, - - setFromCamera: function ( coords, camera ) { - - if ( (camera && camera.isPerspectiveCamera) ) { - - this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); - this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); - - } else if ( (camera && camera.isOrthographicCamera) ) { - - this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera - this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); - - } else { - - console.error( 'THREE.Raycaster: Unsupported camera type.' ); - - } - - }, - - intersectObject: function ( object, recursive ) { - - var intersects = []; - - intersectObject( object, this, intersects, recursive ); - - intersects.sort( ascSort ); - - return intersects; - - }, - - intersectObjects: function ( objects, recursive ) { - - var intersects = []; - - if ( Array.isArray( objects ) === false ) { - - console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' ); - return intersects; - - } - - for ( var i = 0, l = objects.length; i < l; i ++ ) { - - intersectObject( objects[ i ], this, intersects, recursive ); - - } - - intersects.sort( ascSort ); - - return intersects; - - } - - }; - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function Clock( autoStart ) { - - this.autoStart = ( autoStart !== undefined ) ? autoStart : true; - - this.startTime = 0; - this.oldTime = 0; - this.elapsedTime = 0; - - this.running = false; - - } - - Clock.prototype = { - - constructor: Clock, - - start: function () { - - this.startTime = ( performance || Date ).now(); - - this.oldTime = this.startTime; - this.elapsedTime = 0; - this.running = true; - - }, - - stop: function () { - - this.getElapsedTime(); - this.running = false; - - }, - - getElapsedTime: function () { - - this.getDelta(); - return this.elapsedTime; - - }, - - getDelta: function () { - - var diff = 0; - - if ( this.autoStart && ! this.running ) { - - this.start(); - - } - - if ( this.running ) { - - var newTime = ( performance || Date ).now(); - - diff = ( newTime - this.oldTime ) / 1000; - this.oldTime = newTime; - - this.elapsedTime += diff; - - } - - return diff; - - } - - }; - - /** - * Spline from Tween.js, slightly optimized (and trashed) - * http://sole.github.com/tween.js/examples/05_spline.html - * - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - - function Spline( points ) { - - this.points = points; - - var c = [], v3 = { x: 0, y: 0, z: 0 }, - point, intPoint, weight, w2, w3, - pa, pb, pc, pd; - - this.initFromArray = function ( a ) { - - this.points = []; - - for ( var i = 0; i < a.length; i ++ ) { - - this.points[ i ] = { x: a[ i ][ 0 ], y: a[ i ][ 1 ], z: a[ i ][ 2 ] }; - - } - - }; - - this.getPoint = function ( k ) { - - point = ( this.points.length - 1 ) * k; - intPoint = Math.floor( point ); - weight = point - intPoint; - - c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; - c[ 1 ] = intPoint; - c[ 2 ] = intPoint > this.points.length - 2 ? this.points.length - 1 : intPoint + 1; - c[ 3 ] = intPoint > this.points.length - 3 ? this.points.length - 1 : intPoint + 2; - - pa = this.points[ c[ 0 ] ]; - pb = this.points[ c[ 1 ] ]; - pc = this.points[ c[ 2 ] ]; - pd = this.points[ c[ 3 ] ]; - - w2 = weight * weight; - w3 = weight * w2; - - v3.x = interpolate( pa.x, pb.x, pc.x, pd.x, weight, w2, w3 ); - v3.y = interpolate( pa.y, pb.y, pc.y, pd.y, weight, w2, w3 ); - v3.z = interpolate( pa.z, pb.z, pc.z, pd.z, weight, w2, w3 ); - - return v3; - - }; - - this.getControlPointsArray = function () { - - var i, p, l = this.points.length, - coords = []; - - for ( i = 0; i < l; i ++ ) { - - p = this.points[ i ]; - coords[ i ] = [ p.x, p.y, p.z ]; - - } - - return coords; - - }; - - // approximate length by summing linear segments - - this.getLength = function ( nSubDivisions ) { - - var i, index, nSamples, position, - point = 0, intPoint = 0, oldIntPoint = 0, - oldPosition = new Vector3(), - tmpVec = new Vector3(), - chunkLengths = [], - totalLength = 0; - - // first point has 0 length - - chunkLengths[ 0 ] = 0; - - if ( ! nSubDivisions ) nSubDivisions = 100; - - nSamples = this.points.length * nSubDivisions; - - oldPosition.copy( this.points[ 0 ] ); - - for ( i = 1; i < nSamples; i ++ ) { - - index = i / nSamples; - - position = this.getPoint( index ); - tmpVec.copy( position ); - - totalLength += tmpVec.distanceTo( oldPosition ); - - oldPosition.copy( position ); - - point = ( this.points.length - 1 ) * index; - intPoint = Math.floor( point ); - - if ( intPoint !== oldIntPoint ) { - - chunkLengths[ intPoint ] = totalLength; - oldIntPoint = intPoint; - - } - - } - - // last point ends with total length - - chunkLengths[ chunkLengths.length ] = totalLength; - - return { chunks: chunkLengths, total: totalLength }; - - }; - - this.reparametrizeByArcLength = function ( samplingCoef ) { - - var i, j, - index, indexCurrent, indexNext, - realDistance, - sampling, position, - newpoints = [], - tmpVec = new Vector3(), - sl = this.getLength(); - - newpoints.push( tmpVec.copy( this.points[ 0 ] ).clone() ); - - for ( i = 1; i < this.points.length; i ++ ) { - - //tmpVec.copy( this.points[ i - 1 ] ); - //linearDistance = tmpVec.distanceTo( this.points[ i ] ); - - realDistance = sl.chunks[ i ] - sl.chunks[ i - 1 ]; - - sampling = Math.ceil( samplingCoef * realDistance / sl.total ); - - indexCurrent = ( i - 1 ) / ( this.points.length - 1 ); - indexNext = i / ( this.points.length - 1 ); - - for ( j = 1; j < sampling - 1; j ++ ) { - - index = indexCurrent + j * ( 1 / sampling ) * ( indexNext - indexCurrent ); - - position = this.getPoint( index ); - newpoints.push( tmpVec.copy( position ).clone() ); - - } - - newpoints.push( tmpVec.copy( this.points[ i ] ).clone() ); - - } - - this.points = newpoints; - - }; - - // Catmull-Rom - - function interpolate( p0, p1, p2, p3, t, t2, t3 ) { - - var v0 = ( p2 - p0 ) * 0.5, - v1 = ( p3 - p1 ) * 0.5; - - return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; - - } - - } - - /** - * @author bhouston / http://clara.io - * @author WestLangley / http://github.com/WestLangley - * - * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system - * - * The poles (phi) are at the positive and negative y axis. - * The equator starts at positive z. - */ - - function Spherical( radius, phi, theta ) { - - this.radius = ( radius !== undefined ) ? radius : 1.0; - this.phi = ( phi !== undefined ) ? phi : 0; // up / down towards top and bottom pole - this.theta = ( theta !== undefined ) ? theta : 0; // around the equator of the sphere - - return this; - - } - - Spherical.prototype = { - - constructor: Spherical, - - set: function ( radius, phi, theta ) { - - this.radius = radius; - this.phi = phi; - this.theta = theta; - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( other ) { - - this.radius = other.radius; - this.phi = other.phi; - this.theta = other.theta; - - return this; - - }, - - // restrict phi to be betwee EPS and PI-EPS - makeSafe: function() { - - var EPS = 0.000001; - this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) ); - - return this; - - }, - - setFromVector3: function( vec3 ) { - - this.radius = vec3.length(); - - if ( this.radius === 0 ) { - - this.theta = 0; - this.phi = 0; - - } else { - - this.theta = Math.atan2( vec3.x, vec3.z ); // equator angle around y-up axis - this.phi = Math.acos( _Math.clamp( vec3.y / this.radius, - 1, 1 ) ); // polar angle - - } - - return this; - - } - - }; - - /** - * @author Mugen87 / https://github.com/Mugen87 - * - * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system - * - */ - - function Cylindrical( radius, theta, y ) { - - this.radius = ( radius !== undefined ) ? radius : 1.0; // distance from the origin to a point in the x-z plane - this.theta = ( theta !== undefined ) ? theta : 0; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis - this.y = ( y !== undefined ) ? y : 0; // height above the x-z plane - - return this; - - } - - Cylindrical.prototype = { - - constructor: Cylindrical, - - set: function ( radius, theta, y ) { - - this.radius = radius; - this.theta = theta; - this.y = y; - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( other ) { - - this.radius = other.radius; - this.theta = other.theta; - this.y = other.y; - - return this; - - }, - - setFromVector3: function( vec3 ) { - - this.radius = Math.sqrt( vec3.x * vec3.x + vec3.z * vec3.z ); - this.theta = Math.atan2( vec3.x, vec3.z ); - this.y = vec3.y; - - return this; - - } - - }; - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function MorphBlendMesh( geometry, material ) { - - Mesh.call( this, geometry, material ); - - this.animationsMap = {}; - this.animationsList = []; - - // prepare default animation - // (all frames played together in 1 second) - - var numFrames = this.geometry.morphTargets.length; - - var name = "__default"; - - var startFrame = 0; - var endFrame = numFrames - 1; - - var fps = numFrames / 1; - - this.createAnimation( name, startFrame, endFrame, fps ); - this.setAnimationWeight( name, 1 ); - - } - - MorphBlendMesh.prototype = Object.create( Mesh.prototype ); - MorphBlendMesh.prototype.constructor = MorphBlendMesh; - - MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) { - - var animation = { - - start: start, - end: end, - - length: end - start + 1, - - fps: fps, - duration: ( end - start ) / fps, - - lastFrame: 0, - currentFrame: 0, - - active: false, - - time: 0, - direction: 1, - weight: 1, - - directionBackwards: false, - mirroredLoop: false - - }; - - this.animationsMap[ name ] = animation; - this.animationsList.push( animation ); - - }; - - MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) { - - var pattern = /([a-z]+)_?(\d+)/i; - - var firstAnimation, frameRanges = {}; - - var geometry = this.geometry; - - for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { - - var morph = geometry.morphTargets[ i ]; - var chunks = morph.name.match( pattern ); - - if ( chunks && chunks.length > 1 ) { - - var name = chunks[ 1 ]; - - if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity }; - - var range = frameRanges[ name ]; - - if ( i < range.start ) range.start = i; - if ( i > range.end ) range.end = i; - - if ( ! firstAnimation ) firstAnimation = name; - - } - - } - - for ( var name in frameRanges ) { - - var range = frameRanges[ name ]; - this.createAnimation( name, range.start, range.end, fps ); - - } - - this.firstAnimation = firstAnimation; - - }; - - MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) { - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - animation.direction = 1; - animation.directionBackwards = false; - - } - - }; - - MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) { - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - animation.direction = - 1; - animation.directionBackwards = true; - - } - - }; - - MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) { - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - animation.fps = fps; - animation.duration = ( animation.end - animation.start ) / animation.fps; - - } - - }; - - MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) { - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - animation.duration = duration; - animation.fps = ( animation.end - animation.start ) / animation.duration; - - } - - }; - - MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) { - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - animation.weight = weight; - - } - - }; - - MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) { - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - animation.time = time; - - } - - }; - - MorphBlendMesh.prototype.getAnimationTime = function ( name ) { - - var time = 0; - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - time = animation.time; - - } - - return time; - - }; - - MorphBlendMesh.prototype.getAnimationDuration = function ( name ) { - - var duration = - 1; - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - duration = animation.duration; - - } - - return duration; - - }; - - MorphBlendMesh.prototype.playAnimation = function ( name ) { - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - animation.time = 0; - animation.active = true; - - } else { - - console.warn( "THREE.MorphBlendMesh: animation[" + name + "] undefined in .playAnimation()" ); - - } - - }; - - MorphBlendMesh.prototype.stopAnimation = function ( name ) { - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - animation.active = false; - - } - - }; - - MorphBlendMesh.prototype.update = function ( delta ) { - - for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) { - - var animation = this.animationsList[ i ]; - - if ( ! animation.active ) continue; - - var frameTime = animation.duration / animation.length; - - animation.time += animation.direction * delta; - - if ( animation.mirroredLoop ) { - - if ( animation.time > animation.duration || animation.time < 0 ) { - - animation.direction *= - 1; - - if ( animation.time > animation.duration ) { - - animation.time = animation.duration; - animation.directionBackwards = true; - - } - - if ( animation.time < 0 ) { - - animation.time = 0; - animation.directionBackwards = false; - - } - - } - - } else { - - animation.time = animation.time % animation.duration; - - if ( animation.time < 0 ) animation.time += animation.duration; - - } - - var keyframe = animation.start + _Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 ); - var weight = animation.weight; - - if ( keyframe !== animation.currentFrame ) { - - this.morphTargetInfluences[ animation.lastFrame ] = 0; - this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight; - - this.morphTargetInfluences[ keyframe ] = 0; - - animation.lastFrame = animation.currentFrame; - animation.currentFrame = keyframe; - - } - - var mix = ( animation.time % frameTime ) / frameTime; - - if ( animation.directionBackwards ) mix = 1 - mix; - - if ( animation.currentFrame !== animation.lastFrame ) { - - this.morphTargetInfluences[ animation.currentFrame ] = mix * weight; - this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight; - - } else { - - this.morphTargetInfluences[ animation.currentFrame ] = weight; - - } - - } - - }; - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function ImmediateRenderObject( material ) { - - Object3D.call( this ); - - this.material = material; - this.render = function ( renderCallback ) {}; - - } - - ImmediateRenderObject.prototype = Object.create( Object3D.prototype ); - ImmediateRenderObject.prototype.constructor = ImmediateRenderObject; - - ImmediateRenderObject.prototype.isImmediateRenderObject = true; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - */ - - function VertexNormalsHelper( object, size, hex, linewidth ) { - - this.object = object; - - this.size = ( size !== undefined ) ? size : 1; - - var color = ( hex !== undefined ) ? hex : 0xff0000; - - var width = ( linewidth !== undefined ) ? linewidth : 1; - - // - - var nNormals = 0; - - var objGeometry = this.object.geometry; - - if ( objGeometry && objGeometry.isGeometry ) { - - nNormals = objGeometry.faces.length * 3; - - } else if ( objGeometry && objGeometry.isBufferGeometry ) { - - nNormals = objGeometry.attributes.normal.count; - - } - - // - - var geometry = new BufferGeometry(); - - var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 ); - - geometry.addAttribute( 'position', positions ); - - LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) ); - - // - - this.matrixAutoUpdate = false; - - this.update(); - - } - - VertexNormalsHelper.prototype = Object.create( LineSegments.prototype ); - VertexNormalsHelper.prototype.constructor = VertexNormalsHelper; - - VertexNormalsHelper.prototype.update = ( function () { - - var v1 = new Vector3(); - var v2 = new Vector3(); - var normalMatrix = new Matrix3(); - - return function update() { - - var keys = [ 'a', 'b', 'c' ]; - - this.object.updateMatrixWorld( true ); - - normalMatrix.getNormalMatrix( this.object.matrixWorld ); - - var matrixWorld = this.object.matrixWorld; - - var position = this.geometry.attributes.position; - - // - - var objGeometry = this.object.geometry; - - if ( objGeometry && objGeometry.isGeometry ) { - - var vertices = objGeometry.vertices; - - var faces = objGeometry.faces; - - var idx = 0; - - for ( var i = 0, l = faces.length; i < l; i ++ ) { - - var face = faces[ i ]; - - for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { - - var vertex = vertices[ face[ keys[ j ] ] ]; - - var normal = face.vertexNormals[ j ]; - - v1.copy( vertex ).applyMatrix4( matrixWorld ); - - v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); - - position.setXYZ( idx, v1.x, v1.y, v1.z ); - - idx = idx + 1; - - position.setXYZ( idx, v2.x, v2.y, v2.z ); - - idx = idx + 1; - - } - - } - - } else if ( objGeometry && objGeometry.isBufferGeometry ) { - - var objPos = objGeometry.attributes.position; - - var objNorm = objGeometry.attributes.normal; - - var idx = 0; - - // for simplicity, ignore index and drawcalls, and render every normal - - for ( var j = 0, jl = objPos.count; j < jl; j ++ ) { - - v1.set( objPos.getX( j ), objPos.getY( j ), objPos.getZ( j ) ).applyMatrix4( matrixWorld ); - - v2.set( objNorm.getX( j ), objNorm.getY( j ), objNorm.getZ( j ) ); - - v2.applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); - - position.setXYZ( idx, v1.x, v1.y, v1.z ); - - idx = idx + 1; - - position.setXYZ( idx, v2.x, v2.y, v2.z ); - - idx = idx + 1; - - } - - } - - position.needsUpdate = true; - - return this; - - }; - - }() ); - - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - */ - - function SpotLightHelper( light ) { - - Object3D.call( this ); - - this.light = light; - this.light.updateMatrixWorld(); - - this.matrix = light.matrixWorld; - this.matrixAutoUpdate = false; - - var geometry = new BufferGeometry(); - - var positions = [ - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 1, 0, 1, - 0, 0, 0, - 1, 0, 1, - 0, 0, 0, 0, 1, 1, - 0, 0, 0, 0, - 1, 1 - ]; - - for ( var i = 0, j = 1, l = 32; i < l; i ++, j ++ ) { - - var p1 = ( i / l ) * Math.PI * 2; - var p2 = ( j / l ) * Math.PI * 2; - - positions.push( - Math.cos( p1 ), Math.sin( p1 ), 1, - Math.cos( p2 ), Math.sin( p2 ), 1 - ); - - } - - geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); - - var material = new LineBasicMaterial( { fog: false } ); - - this.cone = new LineSegments( geometry, material ); - this.add( this.cone ); - - this.update(); - - } - - SpotLightHelper.prototype = Object.create( Object3D.prototype ); - SpotLightHelper.prototype.constructor = SpotLightHelper; - - SpotLightHelper.prototype.dispose = function () { - - this.cone.geometry.dispose(); - this.cone.material.dispose(); - - }; - - SpotLightHelper.prototype.update = function () { - - var vector = new Vector3(); - var vector2 = new Vector3(); - - return function update() { - - var coneLength = this.light.distance ? this.light.distance : 1000; - var coneWidth = coneLength * Math.tan( this.light.angle ); - - this.cone.scale.set( coneWidth, coneWidth, coneLength ); - - vector.setFromMatrixPosition( this.light.matrixWorld ); - vector2.setFromMatrixPosition( this.light.target.matrixWorld ); - - this.cone.lookAt( vector2.sub( vector ) ); - - this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - - }; - - }(); - - /** - * @author Sean Griffin / http://twitter.com/sgrif - * @author Michael Guerrero / http://realitymeltdown.com - * @author mrdoob / http://mrdoob.com/ - * @author ikerr / http://verold.com - * @author Mugen87 / https://github.com/Mugen87 - */ - - function SkeletonHelper( object ) { - - this.bones = this.getBoneList( object ); - - var geometry = new BufferGeometry(); - - var vertices = []; - var colors = []; - - var color1 = new Color( 0, 0, 1 ); - var color2 = new Color( 0, 1, 0 ); - - for ( var i = 0; i < this.bones.length; i ++ ) { - - var bone = this.bones[ i ]; - - if ( bone.parent && bone.parent.isBone ) { - - vertices.push( 0, 0, 0 ); - vertices.push( 0, 0, 0 ); - colors.push( color1.r, color1.g, color1.b ); - colors.push( color2.r, color2.g, color2.b ); - - } - - } - - geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - - var material = new LineBasicMaterial( { vertexColors: VertexColors, depthTest: false, depthWrite: false, transparent: true } ); - - LineSegments.call( this, geometry, material ); - - this.root = object; - - this.matrix = object.matrixWorld; - this.matrixAutoUpdate = false; - - this.update(); - - } - - - SkeletonHelper.prototype = Object.create( LineSegments.prototype ); - SkeletonHelper.prototype.constructor = SkeletonHelper; - - SkeletonHelper.prototype.getBoneList = function( object ) { - - var boneList = []; - - if ( object && object.isBone ) { - - boneList.push( object ); - - } - - for ( var i = 0; i < object.children.length; i ++ ) { - - boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) ); - - } - - return boneList; - - }; - - SkeletonHelper.prototype.update = function () { - - var vector = new Vector3(); - - var boneMatrix = new Matrix4(); - var matrixWorldInv = new Matrix4(); - - return function update() { - - var geometry = this.geometry; - var position = geometry.getAttribute( 'position' ); - - matrixWorldInv.getInverse( this.root.matrixWorld ); - - for ( var i = 0, j = 0; i < this.bones.length; i ++ ) { - - var bone = this.bones[ i ]; - - if ( bone.parent && bone.parent.isBone ) { - - boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld ); - vector.setFromMatrixPosition( boneMatrix ); - position.setXYZ( j, vector.x, vector.y, vector.z ); - - boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld ); - vector.setFromMatrixPosition( boneMatrix ); - position.setXYZ( j + 1, vector.x, vector.y, vector.z ); - - j += 2; - - } - - } - - geometry.getAttribute( 'position' ).needsUpdate = true; - - }; - - }(); - - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ - - function PointLightHelper( light, sphereSize ) { - - this.light = light; - this.light.updateMatrixWorld(); - - var geometry = new SphereBufferGeometry( sphereSize, 4, 2 ); - var material = new MeshBasicMaterial( { wireframe: true, fog: false } ); - material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - - Mesh.call( this, geometry, material ); - - this.matrix = this.light.matrixWorld; - this.matrixAutoUpdate = false; - - /* - var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); - var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); - - this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); - this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); - - var d = light.distance; - - if ( d === 0.0 ) { - - this.lightDistance.visible = false; - - } else { - - this.lightDistance.scale.set( d, d, d ); - - } - - this.add( this.lightDistance ); - */ - - } - - PointLightHelper.prototype = Object.create( Mesh.prototype ); - PointLightHelper.prototype.constructor = PointLightHelper; - - PointLightHelper.prototype.dispose = function () { - - this.geometry.dispose(); - this.material.dispose(); - - }; - - PointLightHelper.prototype.update = function () { - - this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - - /* - var d = this.light.distance; - - if ( d === 0.0 ) { - - this.lightDistance.visible = false; - - } else { - - this.lightDistance.visible = true; - this.lightDistance.scale.set( d, d, d ); - - } - */ - - }; - - /** - * @author abelnation / http://github.com/abelnation - * @author Mugen87 / http://github.com/Mugen87 - */ - - function RectAreaLightHelper( light ) { - - Object3D.call( this ); - - this.light = light; - this.light.updateMatrixWorld(); - - var materialFront = new MeshBasicMaterial( { - color: light.color, - fog: false - } ); - - var materialBack = new MeshBasicMaterial( { - color: light.color, - fog: false, - wireframe: true - } ); - - var geometry = new BufferGeometry(); - - geometry.addAttribute( 'position', new BufferAttribute( new Float32Array( 6 * 3 ), 3 ) ); - - // shows the "front" of the light, e.g. where light comes from - - this.add( new Mesh( geometry, materialFront ) ); - - // shows the "back" of the light, which does not emit light - - this.add( new Mesh( geometry, materialBack ) ); - - this.update(); - - } - - RectAreaLightHelper.prototype = Object.create( Object3D.prototype ); - RectAreaLightHelper.prototype.constructor = RectAreaLightHelper; - - RectAreaLightHelper.prototype.dispose = function () { - - this.children[ 0 ].geometry.dispose(); - this.children[ 0 ].material.dispose(); - this.children[ 1 ].geometry.dispose(); - this.children[ 1 ].material.dispose(); - - }; - - RectAreaLightHelper.prototype.update = function () { - - var vector1 = new Vector3(); - var vector2 = new Vector3(); - - return function update() { - - var mesh1 = this.children[ 0 ]; - var mesh2 = this.children[ 1 ]; - - if ( this.light.target ) { - - vector1.setFromMatrixPosition( this.light.matrixWorld ); - vector2.setFromMatrixPosition( this.light.target.matrixWorld ); - - var lookVec = vector2.clone().sub( vector1 ); - mesh1.lookAt( lookVec ); - mesh2.lookAt( lookVec ); - - } - - // update materials - - mesh1.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - mesh2.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - - // calculate new dimensions of the helper - - var hx = this.light.width * 0.5; - var hy = this.light.height * 0.5; - - // because the buffer attribute is shared over both geometries, we only have to update once - - var position = mesh1.geometry.getAttribute( 'position' ); - var array = position.array; - - // first face - - array[ 0 ] = hx; array[ 1 ] = - hy; array[ 2 ] = 0; - array[ 3 ] = hx; array[ 4 ] = hy; array[ 5 ] = 0; - array[ 6 ] = - hx; array[ 7 ] = hy; array[ 8 ] = 0; - - // second face - - array[ 9 ] = - hx; array[ 10 ] = hy; array[ 11 ] = 0; - array[ 12 ] = - hx; array[ 13 ] = - hy; array[ 14 ] = 0; - array[ 15 ] = hx; array[ 16 ] = - hy; array[ 17 ] = 0; - - position.needsUpdate = true; - - }; - - }(); - - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / https://github.com/Mugen87 - */ - - function HemisphereLightHelper( light, size ) { - - Object3D.call( this ); - - this.light = light; - this.light.updateMatrixWorld(); - - this.matrix = light.matrixWorld; - this.matrixAutoUpdate = false; - - var geometry = new OctahedronBufferGeometry( size ); - geometry.rotateY( Math.PI * 0.5 ); - - var material = new MeshBasicMaterial( { vertexColors: VertexColors, wireframe: true } ); - - var position = geometry.getAttribute( 'position' ); - var colors = new Float32Array( position.count * 3 ); - - geometry.addAttribute( 'color', new BufferAttribute( colors, 3 ) ); - - this.add( new Mesh( geometry, material ) ); - - this.update(); - - } - - HemisphereLightHelper.prototype = Object.create( Object3D.prototype ); - HemisphereLightHelper.prototype.constructor = HemisphereLightHelper; - - HemisphereLightHelper.prototype.dispose = function () { - - this.children[ 0 ].geometry.dispose(); - this.children[ 0 ].material.dispose(); - - }; - - HemisphereLightHelper.prototype.update = function () { - - var vector = new Vector3(); - - var color1 = new Color(); - var color2 = new Color(); - - return function update() { - - var mesh = this.children[ 0 ]; - - var colors = mesh.geometry.getAttribute( 'color' ); - - color1.copy( this.light.color ).multiplyScalar( this.light.intensity ); - color2.copy( this.light.groundColor ).multiplyScalar( this.light.intensity ); - - for ( var i = 0, l = colors.count; i < l; i ++ ) { - - var color = ( i < ( l / 2 ) ) ? color1 : color2; - - colors.setXYZ( i, color.r, color.g, color.b ); - - } - - mesh.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() ); - - colors.needsUpdate = true; - - }; - - }(); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function GridHelper( size, divisions, color1, color2 ) { - - size = size || 10; - divisions = divisions || 10; - color1 = new Color( color1 !== undefined ? color1 : 0x444444 ); - color2 = new Color( color2 !== undefined ? color2 : 0x888888 ); - - var center = divisions / 2; - var step = ( size * 2 ) / divisions; - var vertices = [], colors = []; - - for ( var i = 0, j = 0, k = - size; i <= divisions; i ++, k += step ) { - - vertices.push( - size, 0, k, size, 0, k ); - vertices.push( k, 0, - size, k, 0, size ); - - var color = i === center ? color1 : color2; - - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; - - } - - var geometry = new BufferGeometry(); - geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - - var material = new LineBasicMaterial( { vertexColors: VertexColors } ); - - LineSegments.call( this, geometry, material ); - - } - - GridHelper.prototype = Object.create( LineSegments.prototype ); - GridHelper.prototype.constructor = GridHelper; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / http://github.com/Mugen87 - * @author Hectate / http://www.github.com/Hectate - */ - - function PolarGridHelper( radius, radials, circles, divisions, color1, color2 ) { - - radius = radius || 10; - radials = radials || 16; - circles = circles || 8; - divisions = divisions || 64; - color1 = new Color( color1 !== undefined ? color1 : 0x444444 ); - color2 = new Color( color2 !== undefined ? color2 : 0x888888 ); - - var vertices = []; - var colors = []; - - var x, z; - var v, i, j, r, color; - - // create the radials - - for ( i = 0; i <= radials; i ++ ) { - - v = ( i / radials ) * ( Math.PI * 2 ); - - x = Math.sin( v ) * radius; - z = Math.cos( v ) * radius; - - vertices.push( 0, 0, 0 ); - vertices.push( x, 0, z ); - - color = ( i & 1 ) ? color1 : color2; - - colors.push( color.r, color.g, color.b ); - colors.push( color.r, color.g, color.b ); - - } - - // create the circles - - for ( i = 0; i <= circles; i ++ ) { - - color = ( i & 1 ) ? color1 : color2; - - r = radius - ( radius / circles * i ); - - for ( j = 0; j < divisions; j ++ ) { - - // first vertex - - v = ( j / divisions ) * ( Math.PI * 2 ); - - x = Math.sin( v ) * r; - z = Math.cos( v ) * r; - - vertices.push( x, 0, z ); - colors.push( color.r, color.g, color.b ); - - // second vertex - - v = ( ( j + 1 ) / divisions ) * ( Math.PI * 2 ); - - x = Math.sin( v ) * r; - z = Math.cos( v ) * r; - - vertices.push( x, 0, z ); - colors.push( color.r, color.g, color.b ); - - } - - } - - var geometry = new BufferGeometry(); - geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - - var material = new LineBasicMaterial( { vertexColors: VertexColors } ); - - LineSegments.call( this, geometry, material ); - - } - - PolarGridHelper.prototype = Object.create( LineSegments.prototype ); - PolarGridHelper.prototype.constructor = PolarGridHelper; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - */ - - function FaceNormalsHelper( object, size, hex, linewidth ) { - - // FaceNormalsHelper only supports THREE.Geometry - - this.object = object; - - this.size = ( size !== undefined ) ? size : 1; - - var color = ( hex !== undefined ) ? hex : 0xffff00; - - var width = ( linewidth !== undefined ) ? linewidth : 1; - - // - - var nNormals = 0; - - var objGeometry = this.object.geometry; - - if ( objGeometry && objGeometry.isGeometry ) { - - nNormals = objGeometry.faces.length; - - } else { - - console.warn( 'THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead.' ); - - } - - // - - var geometry = new BufferGeometry(); - - var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 ); - - geometry.addAttribute( 'position', positions ); - - LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) ); - - // - - this.matrixAutoUpdate = false; - this.update(); - - } - - FaceNormalsHelper.prototype = Object.create( LineSegments.prototype ); - FaceNormalsHelper.prototype.constructor = FaceNormalsHelper; - - FaceNormalsHelper.prototype.update = ( function () { - - var v1 = new Vector3(); - var v2 = new Vector3(); - var normalMatrix = new Matrix3(); - - return function update() { - - this.object.updateMatrixWorld( true ); - - normalMatrix.getNormalMatrix( this.object.matrixWorld ); - - var matrixWorld = this.object.matrixWorld; - - var position = this.geometry.attributes.position; - - // - - var objGeometry = this.object.geometry; - - var vertices = objGeometry.vertices; - - var faces = objGeometry.faces; - - var idx = 0; - - for ( var i = 0, l = faces.length; i < l; i ++ ) { - - var face = faces[ i ]; - - var normal = face.normal; - - v1.copy( vertices[ face.a ] ) - .add( vertices[ face.b ] ) - .add( vertices[ face.c ] ) - .divideScalar( 3 ) - .applyMatrix4( matrixWorld ); - - v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); - - position.setXYZ( idx, v1.x, v1.y, v1.z ); - - idx = idx + 1; - - position.setXYZ( idx, v2.x, v2.y, v2.z ); - - idx = idx + 1; - - } - - position.needsUpdate = true; - - return this; - - }; - - }() ); - - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - */ - - function DirectionalLightHelper( light, size ) { - - Object3D.call( this ); - - this.light = light; - this.light.updateMatrixWorld(); - - this.matrix = light.matrixWorld; - this.matrixAutoUpdate = false; - - if ( size === undefined ) size = 1; - - var geometry = new BufferGeometry(); - geometry.addAttribute( 'position', new Float32BufferAttribute( [ - - size, size, 0, - size, size, 0, - size, - size, 0, - - size, - size, 0, - - size, size, 0 - ], 3 ) ); - - var material = new LineBasicMaterial( { fog: false } ); - - this.add( new Line( geometry, material ) ); - - geometry = new BufferGeometry(); - geometry.addAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) ); - - this.add( new Line( geometry, material )); - - this.update(); - - } - - DirectionalLightHelper.prototype = Object.create( Object3D.prototype ); - DirectionalLightHelper.prototype.constructor = DirectionalLightHelper; - - DirectionalLightHelper.prototype.dispose = function () { - - var lightPlane = this.children[ 0 ]; - var targetLine = this.children[ 1 ]; - - lightPlane.geometry.dispose(); - lightPlane.material.dispose(); - targetLine.geometry.dispose(); - targetLine.material.dispose(); - - }; - - DirectionalLightHelper.prototype.update = function () { - - var v1 = new Vector3(); - var v2 = new Vector3(); - var v3 = new Vector3(); - - return function update() { - - v1.setFromMatrixPosition( this.light.matrixWorld ); - v2.setFromMatrixPosition( this.light.target.matrixWorld ); - v3.subVectors( v2, v1 ); - - var lightPlane = this.children[ 0 ]; - var targetLine = this.children[ 1 ]; - - lightPlane.lookAt( v3 ); - lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - - targetLine.lookAt( v3 ); - targetLine.scale.z = v3.length(); - - }; - - }(); - - /** - * @author alteredq / http://alteredqualia.com/ - * @author Mugen87 / https://github.com/Mugen87 - * - * - shows frustum, line of sight and up of the camera - * - suitable for fast updates - * - based on frustum visualization in lightgl.js shadowmap example - * http://evanw.github.com/lightgl.js/tests/shadowmap.html - */ - - function CameraHelper( camera ) { - - var geometry = new BufferGeometry(); - var material = new LineBasicMaterial( { color: 0xffffff, vertexColors: FaceColors } ); - - var vertices = []; - var colors = []; - - var pointMap = {}; - - // colors - - var colorFrustum = new Color( 0xffaa00 ); - var colorCone = new Color( 0xff0000 ); - var colorUp = new Color( 0x00aaff ); - var colorTarget = new Color( 0xffffff ); - var colorCross = new Color( 0x333333 ); - - // near - - addLine( "n1", "n2", colorFrustum ); - addLine( "n2", "n4", colorFrustum ); - addLine( "n4", "n3", colorFrustum ); - addLine( "n3", "n1", colorFrustum ); - - // far - - addLine( "f1", "f2", colorFrustum ); - addLine( "f2", "f4", colorFrustum ); - addLine( "f4", "f3", colorFrustum ); - addLine( "f3", "f1", colorFrustum ); - - // sides - - addLine( "n1", "f1", colorFrustum ); - addLine( "n2", "f2", colorFrustum ); - addLine( "n3", "f3", colorFrustum ); - addLine( "n4", "f4", colorFrustum ); - - // cone - - addLine( "p", "n1", colorCone ); - addLine( "p", "n2", colorCone ); - addLine( "p", "n3", colorCone ); - addLine( "p", "n4", colorCone ); - - // up - - addLine( "u1", "u2", colorUp ); - addLine( "u2", "u3", colorUp ); - addLine( "u3", "u1", colorUp ); - - // target - - addLine( "c", "t", colorTarget ); - addLine( "p", "c", colorCross ); - - // cross - - addLine( "cn1", "cn2", colorCross ); - addLine( "cn3", "cn4", colorCross ); - - addLine( "cf1", "cf2", colorCross ); - addLine( "cf3", "cf4", colorCross ); - - function addLine( a, b, color ) { - - addPoint( a, color ); - addPoint( b, color ); - - } - - function addPoint( id, color ) { - - vertices.push( 0, 0, 0 ); - colors.push( color.r, color.g, color.b ); - - if ( pointMap[ id ] === undefined ) { - - pointMap[ id ] = []; - - } - - pointMap[ id ].push( ( vertices.length / 3 ) - 1 ); - - } - - geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - - LineSegments.call( this, geometry, material ); - - this.camera = camera; - if ( this.camera.updateProjectionMatrix ) this.camera.updateProjectionMatrix(); - - this.matrix = camera.matrixWorld; - this.matrixAutoUpdate = false; - - this.pointMap = pointMap; - - this.update(); - - } - - CameraHelper.prototype = Object.create( LineSegments.prototype ); - CameraHelper.prototype.constructor = CameraHelper; - - CameraHelper.prototype.update = function () { - - var geometry, pointMap; - - var vector = new Vector3(); - var camera = new Camera(); - - function setPoint( point, x, y, z ) { - - vector.set( x, y, z ).unproject( camera ); - - var points = pointMap[ point ]; - - if ( points !== undefined ) { - - var position = geometry.getAttribute( 'position' ); - - for ( var i = 0, l = points.length; i < l; i ++ ) { - - position.setXYZ( points[ i ], vector.x, vector.y, vector.z ); - - } - - } - - } - - return function update() { - - geometry = this.geometry; - pointMap = this.pointMap; - - var w = 1, h = 1; - - // we need just camera projection matrix - // world matrix must be identity - - camera.projectionMatrix.copy( this.camera.projectionMatrix ); - - // center / target - - setPoint( "c", 0, 0, - 1 ); - setPoint( "t", 0, 0, 1 ); - - // near - - setPoint( "n1", - w, - h, - 1 ); - setPoint( "n2", w, - h, - 1 ); - setPoint( "n3", - w, h, - 1 ); - setPoint( "n4", w, h, - 1 ); - - // far - - setPoint( "f1", - w, - h, 1 ); - setPoint( "f2", w, - h, 1 ); - setPoint( "f3", - w, h, 1 ); - setPoint( "f4", w, h, 1 ); - - // up - - setPoint( "u1", w * 0.7, h * 1.1, - 1 ); - setPoint( "u2", - w * 0.7, h * 1.1, - 1 ); - setPoint( "u3", 0, h * 2, - 1 ); - - // cross - - setPoint( "cf1", - w, 0, 1 ); - setPoint( "cf2", w, 0, 1 ); - setPoint( "cf3", 0, - h, 1 ); - setPoint( "cf4", 0, h, 1 ); - - setPoint( "cn1", - w, 0, - 1 ); - setPoint( "cn2", w, 0, - 1 ); - setPoint( "cn3", 0, - h, - 1 ); - setPoint( "cn4", 0, h, - 1 ); - - geometry.getAttribute( 'position' ).needsUpdate = true; - - }; - - }(); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function BoxHelper( object, color ) { - - if ( color === undefined ) color = 0xffff00; - - var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); - var positions = new Float32Array( 8 * 3 ); - - var geometry = new BufferGeometry(); - geometry.setIndex( new BufferAttribute( indices, 1 ) ); - geometry.addAttribute( 'position', new BufferAttribute( positions, 3 ) ); - - LineSegments.call( this, geometry, new LineBasicMaterial( { color: color } ) ); - - if ( object !== undefined ) { - - this.update( object ); - - } - - } - - BoxHelper.prototype = Object.create( LineSegments.prototype ); - BoxHelper.prototype.constructor = BoxHelper; - - BoxHelper.prototype.update = ( function () { - - var box = new Box3(); - - return function update( object ) { - - if ( object && object.isBox3 ) { - - box.copy( object ); - - } else { - - box.setFromObject( object ); - - } - - if ( box.isEmpty() ) return; - - var min = box.min; - var max = box.max; - - /* - 5____4 - 1/___0/| - | 6__|_7 - 2/___3/ - - 0: max.x, max.y, max.z - 1: min.x, max.y, max.z - 2: min.x, min.y, max.z - 3: max.x, min.y, max.z - 4: max.x, max.y, min.z - 5: min.x, max.y, min.z - 6: min.x, min.y, min.z - 7: max.x, min.y, min.z - */ - - var position = this.geometry.attributes.position; - var array = position.array; - - array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z; - array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z; - array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z; - array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z; - array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z; - array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z; - array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z; - array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z; - - position.needsUpdate = true; - - this.geometry.computeBoundingSphere(); - - }; - - } )(); - - /** - * @author WestLangley / http://github.com/WestLangley - * @author zz85 / http://github.com/zz85 - * @author bhouston / http://clara.io - * - * Creates an arrow for visualizing directions - * - * Parameters: - * dir - Vector3 - * origin - Vector3 - * length - Number - * color - color in hex value - * headLength - Number - * headWidth - Number - */ - - var lineGeometry = new BufferGeometry(); - lineGeometry.addAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) ); - - var coneGeometry = new CylinderBufferGeometry( 0, 0.5, 1, 5, 1 ); - coneGeometry.translate( 0, - 0.5, 0 ); - - function ArrowHelper( dir, origin, length, color, headLength, headWidth ) { - - // dir is assumed to be normalized - - Object3D.call( this ); - - if ( color === undefined ) color = 0xffff00; - if ( length === undefined ) length = 1; - if ( headLength === undefined ) headLength = 0.2 * length; - if ( headWidth === undefined ) headWidth = 0.2 * headLength; - - this.position.copy( origin ); - - this.line = new Line( lineGeometry, new LineBasicMaterial( { color: color } ) ); - this.line.matrixAutoUpdate = false; - this.add( this.line ); - - this.cone = new Mesh( coneGeometry, new MeshBasicMaterial( { color: color } ) ); - this.cone.matrixAutoUpdate = false; - this.add( this.cone ); - - this.setDirection( dir ); - this.setLength( length, headLength, headWidth ); - - } - - ArrowHelper.prototype = Object.create( Object3D.prototype ); - ArrowHelper.prototype.constructor = ArrowHelper; - - ArrowHelper.prototype.setDirection = ( function () { - - var axis = new Vector3(); - var radians; - - return function setDirection( dir ) { - - // dir is assumed to be normalized - - if ( dir.y > 0.99999 ) { - - this.quaternion.set( 0, 0, 0, 1 ); - - } else if ( dir.y < - 0.99999 ) { - - this.quaternion.set( 1, 0, 0, 0 ); - - } else { - - axis.set( dir.z, 0, - dir.x ).normalize(); - - radians = Math.acos( dir.y ); - - this.quaternion.setFromAxisAngle( axis, radians ); - - } - - }; - - }() ); - - ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) { - - if ( headLength === undefined ) headLength = 0.2 * length; - if ( headWidth === undefined ) headWidth = 0.2 * headLength; - - this.line.scale.set( 1, Math.max( 0, length - headLength ), 1 ); - this.line.updateMatrix(); - - this.cone.scale.set( headWidth, headLength, headWidth ); - this.cone.position.y = length; - this.cone.updateMatrix(); - - }; - - ArrowHelper.prototype.setColor = function ( color ) { - - this.line.material.color.copy( color ); - this.cone.material.color.copy( color ); - - }; - - /** - * @author sroucheray / http://sroucheray.org/ - * @author mrdoob / http://mrdoob.com/ - */ - - function AxisHelper( size ) { - - size = size || 1; - - var vertices = [ - 0, 0, 0, size, 0, 0, - 0, 0, 0, 0, size, 0, - 0, 0, 0, 0, 0, size - ]; - - var colors = [ - 1, 0, 0, 1, 0.6, 0, - 0, 1, 0, 0.6, 1, 0, - 0, 0, 1, 0, 0.6, 1 - ]; - - var geometry = new BufferGeometry(); - geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - - var material = new LineBasicMaterial( { vertexColors: VertexColors } ); - - LineSegments.call( this, geometry, material ); - - } - - AxisHelper.prototype = Object.create( LineSegments.prototype ); - AxisHelper.prototype.constructor = AxisHelper; - - /** - * @author zz85 https://github.com/zz85 - * - * Centripetal CatmullRom Curve - which is useful for avoiding - * cusps and self-intersections in non-uniform catmull rom curves. - * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf - * - * curve.type accepts centripetal(default), chordal and catmullrom - * curve.tension is used for catmullrom which defaults to 0.5 - */ - - var CatmullRomCurve3 = ( function() { - - var - tmp = new Vector3(), - px = new CubicPoly(), - py = new CubicPoly(), - pz = new CubicPoly(); - - /* - Based on an optimized c++ solution in - - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - - http://ideone.com/NoEbVM - - This CubicPoly class could be used for reusing some variables and calculations, - but for three.js curve use, it could be possible inlined and flatten into a single function call - which can be placed in CurveUtils. - */ - - function CubicPoly() {} - - /* - * Compute coefficients for a cubic polynomial - * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 - * such that - * p(0) = x0, p(1) = x1 - * and - * p'(0) = t0, p'(1) = t1. - */ - CubicPoly.prototype.init = function( x0, x1, t0, t1 ) { - - this.c0 = x0; - this.c1 = t0; - this.c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; - this.c3 = 2 * x0 - 2 * x1 + t0 + t1; - - }; - - CubicPoly.prototype.initNonuniformCatmullRom = function( x0, x1, x2, x3, dt0, dt1, dt2 ) { - - // compute tangents when parameterized in [t1,t2] - var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; - var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; - - // rescale tangents for parametrization in [0,1] - t1 *= dt1; - t2 *= dt1; - - // initCubicPoly - this.init( x1, x2, t1, t2 ); - - }; - - // standard Catmull-Rom spline: interpolate between x1 and x2 with previous/following points x1/x4 - CubicPoly.prototype.initCatmullRom = function( x0, x1, x2, x3, tension ) { - - this.init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); - - }; - - CubicPoly.prototype.calc = function( t ) { - - var t2 = t * t; - var t3 = t2 * t; - return this.c0 + this.c1 * t + this.c2 * t2 + this.c3 * t3; - - }; - - // Subclass Three.js curve - return Curve.create( - - function ( p /* array of Vector3 */ ) { - - this.points = p || []; - this.closed = false; - - }, - - function ( t ) { - - var points = this.points, - point, intPoint, weight, l; - - l = points.length; - - if ( l < 2 ) console.log( 'duh, you need at least 2 points' ); - - point = ( l - ( this.closed ? 0 : 1 ) ) * t; - intPoint = Math.floor( point ); - weight = point - intPoint; - - if ( this.closed ) { - - intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; - - } else if ( weight === 0 && intPoint === l - 1 ) { - - intPoint = l - 2; - weight = 1; - - } - - var p0, p1, p2, p3; // 4 points - - if ( this.closed || intPoint > 0 ) { - - p0 = points[ ( intPoint - 1 ) % l ]; - - } else { - - // extrapolate first point - tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); - p0 = tmp; - - } - - p1 = points[ intPoint % l ]; - p2 = points[ ( intPoint + 1 ) % l ]; - - if ( this.closed || intPoint + 2 < l ) { - - p3 = points[ ( intPoint + 2 ) % l ]; - - } else { - - // extrapolate last point - tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); - p3 = tmp; - - } - - if ( this.type === undefined || this.type === 'centripetal' || this.type === 'chordal' ) { - - // init Centripetal / Chordal Catmull-Rom - var pow = this.type === 'chordal' ? 0.5 : 0.25; - var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); - var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); - var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); - - // safety check for repeated points - if ( dt1 < 1e-4 ) dt1 = 1.0; - if ( dt0 < 1e-4 ) dt0 = dt1; - if ( dt2 < 1e-4 ) dt2 = dt1; - - px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); - py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); - pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); - - } else if ( this.type === 'catmullrom' ) { - - var tension = this.tension !== undefined ? this.tension : 0.5; - px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, tension ); - py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, tension ); - pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, tension ); - - } - - var v = new Vector3( - px.calc( weight ), - py.calc( weight ), - pz.calc( weight ) - ); - - return v; - - } - - ); - - } )(); - - /************************************************************** - * Spline 3D curve - **************************************************************/ - - - var SplineCurve3 = Curve.create( - - function ( points /* array of Vector3 */ ) { - - console.warn( 'THREE.SplineCurve3 will be deprecated. Please use THREE.CatmullRomCurve3' ); - this.points = ( points === undefined ) ? [] : points; - - }, - - function ( t ) { - - var points = this.points; - var point = ( points.length - 1 ) * t; - - var intPoint = Math.floor( point ); - var weight = point - intPoint; - - var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ]; - var point1 = points[ intPoint ]; - var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; - var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; - - var interpolate = CurveUtils.interpolate; - - return new Vector3( - interpolate( point0.x, point1.x, point2.x, point3.x, weight ), - interpolate( point0.y, point1.y, point2.y, point3.y, weight ), - interpolate( point0.z, point1.z, point2.z, point3.z, weight ) - ); - - } - - ); - - /************************************************************** - * Cubic Bezier 3D curve - **************************************************************/ - - var CubicBezierCurve3 = Curve.create( - - function ( v0, v1, v2, v3 ) { - - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; - - }, - - function ( t ) { - - var b3 = ShapeUtils.b3; - - return new Vector3( - b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), - b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ), - b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z ) - ); - - } - - ); - - /************************************************************** - * Quadratic Bezier 3D curve - **************************************************************/ - - var QuadraticBezierCurve3 = Curve.create( - - function ( v0, v1, v2 ) { - - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - - }, - - function ( t ) { - - var b2 = ShapeUtils.b2; - - return new Vector3( - b2( t, this.v0.x, this.v1.x, this.v2.x ), - b2( t, this.v0.y, this.v1.y, this.v2.y ), - b2( t, this.v0.z, this.v1.z, this.v2.z ) - ); - - } - - ); - - /************************************************************** - * Line3D - **************************************************************/ - - var LineCurve3 = Curve.create( - - function ( v1, v2 ) { - - this.v1 = v1; - this.v2 = v2; - - }, - - function ( t ) { - - if ( t === 1 ) { - - return this.v2.clone(); - - } - - var vector = new Vector3(); - - vector.subVectors( this.v2, this.v1 ); // diff - vector.multiplyScalar( t ); - vector.add( this.v1 ); - - return vector; - - } - - ); - - /************************************************************** - * Arc curve - **************************************************************/ - - function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - - EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - - } - - ArcCurve.prototype = Object.create( EllipseCurve.prototype ); - ArcCurve.prototype.constructor = ArcCurve; - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - var SceneUtils = { - - createMultiMaterialObject: function ( geometry, materials ) { - - var group = new Group(); - - for ( var i = 0, l = materials.length; i < l; i ++ ) { - - group.add( new Mesh( geometry, materials[ i ] ) ); - - } - - return group; - - }, - - detach: function ( child, parent, scene ) { - - child.applyMatrix( parent.matrixWorld ); - parent.remove( child ); - scene.add( child ); - - }, - - attach: function ( child, scene, parent ) { - - var matrixWorldInverse = new Matrix4(); - matrixWorldInverse.getInverse( parent.matrixWorld ); - child.applyMatrix( matrixWorldInverse ); - - scene.remove( child ); - parent.add( child ); - - } - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function Face4( a, b, c, d, normal, color, materialIndex ) { - - console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' ); - return new Face3( a, b, c, normal, color, materialIndex ); - - } - - var LineStrip = 0; - - var LinePieces = 1; - - function MeshFaceMaterial( materials ) { - - console.warn( 'THREE.MeshFaceMaterial has been renamed to THREE.MultiMaterial.' ); - return new MultiMaterial( materials ); - - } - - function PointCloud( geometry, material ) { - - console.warn( 'THREE.PointCloud has been renamed to THREE.Points.' ); - return new Points( geometry, material ); - - } - - function Particle( material ) { - - console.warn( 'THREE.Particle has been renamed to THREE.Sprite.' ); - return new Sprite( material ); - - } - - function ParticleSystem( geometry, material ) { - - console.warn( 'THREE.ParticleSystem has been renamed to THREE.Points.' ); - return new Points( geometry, material ); - - } - - function PointCloudMaterial( parameters ) { - - console.warn( 'THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.' ); - return new PointsMaterial( parameters ); - - } - - function ParticleBasicMaterial( parameters ) { - - console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.' ); - return new PointsMaterial( parameters ); - - } - - function ParticleSystemMaterial( parameters ) { - - console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.' ); - return new PointsMaterial( parameters ); - - } - - function Vertex( x, y, z ) { - - console.warn( 'THREE.Vertex has been removed. Use THREE.Vector3 instead.' ); - return new Vector3( x, y, z ); - - } - - // - - function DynamicBufferAttribute( array, itemSize ) { - - console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' ); - return new BufferAttribute( array, itemSize ).setDynamic( true ); - - } - - function Int8Attribute( array, itemSize ) { - - console.warn( 'THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead.' ); - return new Int8BufferAttribute( array, itemSize ); - - } - - function Uint8Attribute( array, itemSize ) { - - console.warn( 'THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead.' ); - return new Uint8BufferAttribute( array, itemSize ); - - } - - function Uint8ClampedAttribute( array, itemSize ) { - - console.warn( 'THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead.' ); - return new Uint8ClampedBufferAttribute( array, itemSize ); - - } - - function Int16Attribute( array, itemSize ) { - - console.warn( 'THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead.' ); - return new Int16BufferAttribute( array, itemSize ); - - } - - function Uint16Attribute( array, itemSize ) { - - console.warn( 'THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead.' ); - return new Uint16BufferAttribute( array, itemSize ); - - } - - function Int32Attribute( array, itemSize ) { - - console.warn( 'THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead.' ); - return new Int32BufferAttribute( array, itemSize ); - - } - - function Uint32Attribute( array, itemSize ) { - - console.warn( 'THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead.' ); - return new Uint32BufferAttribute( array, itemSize ); - - } - - function Float32Attribute( array, itemSize ) { - - console.warn( 'THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead.' ); - return new Float32BufferAttribute( array, itemSize ); - - } - - function Float64Attribute( array, itemSize ) { - - console.warn( 'THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead.' ); - return new Float64BufferAttribute( array, itemSize ); - - } - - // - - function ClosedSplineCurve3( points ) { - - console.warn( 'THREE.ClosedSplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead.' ); - - CatmullRomCurve3.call( this, points ); - this.type = 'catmullrom'; - this.closed = true; - - } - - ClosedSplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype ); - - - // - function BoundingBoxHelper( object, color ) { - - console.warn( 'THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead.' ); - return new BoxHelper( object, color ); - - } - - function EdgesHelper( object, hex ) { - - console.warn( 'THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead.' ); - return new LineSegments( new EdgesGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) ); - - } - - GridHelper.prototype.setColors = function () { - - console.error( 'THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.' ); - - }; - - function WireframeHelper( object, hex ) { - - console.warn( 'THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead.' ); - return new LineSegments( new WireframeGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) ); - - } - - // - - function XHRLoader( manager ) { - - console.warn( 'THREE.XHRLoader has been renamed to THREE.FileLoader.' ); - return new FileLoader( manager ); - - } - - // - - Object.assign( Box2.prototype, { - - center: function ( optionalTarget ) { - - console.warn( 'THREE.Box2: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); - - }, - empty: function () { - - console.warn( 'THREE.Box2: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); - - }, - isIntersectionBox: function ( box ) { - - console.warn( 'THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); - - }, - size: function ( optionalTarget ) { - - console.warn( 'THREE.Box2: .size() has been renamed to .getSize().' ); - return this.getSize( optionalTarget ); - - } - } ); - - Object.assign( Box3.prototype, { - - center: function ( optionalTarget ) { - - console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); - - }, - empty: function () { - - console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); - - }, - isIntersectionBox: function ( box ) { - - console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); - - }, - isIntersectionSphere: function ( sphere ) { - - console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); - - }, - size: function ( optionalTarget ) { - - console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' ); - return this.getSize( optionalTarget ); - - } - } ); - - Line3.prototype.center = function ( optionalTarget ) { - - console.warn( 'THREE.Line3: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); - - }; - - _Math.random16 = function () { - - console.warn( 'THREE.Math.random16() has been deprecated. Use Math.random() instead.' ); - return Math.random(); - - }; - - Object.assign( Matrix3.prototype, { - - flattenToArrayOffset: function ( array, offset ) { - - console.warn( "THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); - return this.toArray( array, offset ); - - }, - multiplyVector3: function ( vector ) { - - console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); - return vector.applyMatrix3( this ); - - }, - multiplyVector3Array: function ( a ) { - - console.warn( 'THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); - return this.applyToVector3Array( a ); - - }, - applyToBuffer: function( buffer, offset, length ) { - - console.warn( 'THREE.Matrix3: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead.' ); - return this.applyToBufferAttribute( buffer ); - - } - - } ); - - Object.assign( Matrix4.prototype, { - - extractPosition: function ( m ) { - - console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); - return this.copyPosition( m ); - - }, - flattenToArrayOffset: function ( array, offset ) { - - console.warn( "THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); - return this.toArray( array, offset ); - - }, - getPosition: function () { - - var v1; - - return function getPosition() { - - if ( v1 === undefined ) v1 = new Vector3(); - console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); - return v1.setFromMatrixColumn( this, 3 ); - - }; - - }(), - setRotationFromQuaternion: function ( q ) { - - console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); - return this.makeRotationFromQuaternion( q ); - - }, - multiplyVector3: function ( vector ) { - - console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.' ); - return vector.applyProjection( this ); - - }, - multiplyVector4: function ( vector ) { - - console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); - - }, - multiplyVector3Array: function ( a ) { - - console.warn( 'THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); - return this.applyToVector3Array( a ); - - }, - rotateAxis: function ( v ) { - - console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); - v.transformDirection( this ); - - }, - crossVector: function ( vector ) { - - console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); - - }, - translate: function () { - - console.error( 'THREE.Matrix4: .translate() has been removed.' ); - - }, - rotateX: function () { - - console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); - - }, - rotateY: function () { - - console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); - - }, - rotateZ: function () { - - console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); - - }, - rotateByAxis: function () { - - console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); - - }, - applyToBuffer: function( buffer, offset, length ) { - - console.warn( 'THREE.Matrix4: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead.' ); - return this.applyToBufferAttribute( buffer ); - - } - - } ); - - Plane.prototype.isIntersectionLine = function ( line ) { - - console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' ); - return this.intersectsLine( line ); - - }; - - Quaternion.prototype.multiplyVector3 = function ( vector ) { - - console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); - return vector.applyQuaternion( this ); - - }; - - Object.assign( Ray.prototype, { - - isIntersectionBox: function ( box ) { - - console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); - - }, - isIntersectionPlane: function ( plane ) { - - console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' ); - return this.intersectsPlane( plane ); - - }, - isIntersectionSphere: function ( sphere ) { - - console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); - - } - - } ); - - Object.assign( Shape.prototype, { - - extrude: function ( options ) { - - console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' ); - return new ExtrudeGeometry( this, options ); - - }, - makeGeometry: function ( options ) { - - console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' ); - return new ShapeGeometry( this, options ); - - } - - } ); - - Object.assign( Vector3.prototype, { - - setEulerFromRotationMatrix: function () { - - console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); - - }, - setEulerFromQuaternion: function () { - - console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); - - }, - getPositionFromMatrix: function ( m ) { - - console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); - return this.setFromMatrixPosition( m ); - - }, - getScaleFromMatrix: function ( m ) { - - console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); - return this.setFromMatrixScale( m ); - - }, - getColumnFromMatrix: function ( index, matrix ) { - - console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); - return this.setFromMatrixColumn( matrix, index ); - - } - - } ); - - // - - Geometry.prototype.computeTangents = function () { - - console.warn( 'THREE.Geometry: .computeTangents() has been removed.' ); - - }; - - Object.assign( Object3D.prototype, { - - getChildByName: function ( name ) { - - console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); - return this.getObjectByName( name ); - - }, - renderDepth: function () { - - console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); - - }, - translate: function ( distance, axis ) { - - console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); - return this.translateOnAxis( axis, distance ); - - } - - } ); - - Object.defineProperties( Object3D.prototype, { - - eulerOrder: { - get: function () { - - console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); - return this.rotation.order; - - }, - set: function ( value ) { - - console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); - this.rotation.order = value; - - } - }, - useQuaternion: { - get: function () { - - console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); - - }, - set: function () { - - console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); - - } - } - - } ); - - Object.defineProperties( LOD.prototype, { - - objects: { - get: function () { - - console.warn( 'THREE.LOD: .objects has been renamed to .levels.' ); - return this.levels; - - } - } - - } ); - - // - - PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) { - - console.warn( "THREE.PerspectiveCamera.setLens is deprecated. " + - "Use .setFocalLength and .filmGauge for a photographic setup." ); - - if ( filmGauge !== undefined ) this.filmGauge = filmGauge; - this.setFocalLength( focalLength ); - - }; - - // - - Object.defineProperties( Light.prototype, { - onlyShadow: { - set: function () { - - console.warn( 'THREE.Light: .onlyShadow has been removed.' ); - - } - }, - shadowCameraFov: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' ); - this.shadow.camera.fov = value; - - } - }, - shadowCameraLeft: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' ); - this.shadow.camera.left = value; - - } - }, - shadowCameraRight: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' ); - this.shadow.camera.right = value; - - } - }, - shadowCameraTop: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' ); - this.shadow.camera.top = value; - - } - }, - shadowCameraBottom: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' ); - this.shadow.camera.bottom = value; - - } - }, - shadowCameraNear: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' ); - this.shadow.camera.near = value; - - } - }, - shadowCameraFar: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' ); - this.shadow.camera.far = value; - - } - }, - shadowCameraVisible: { - set: function () { - - console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' ); - - } - }, - shadowBias: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' ); - this.shadow.bias = value; - - } - }, - shadowDarkness: { - set: function () { - - console.warn( 'THREE.Light: .shadowDarkness has been removed.' ); - - } - }, - shadowMapWidth: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' ); - this.shadow.mapSize.width = value; - - } - }, - shadowMapHeight: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' ); - this.shadow.mapSize.height = value; - - } - } - } ); - - // - - Object.defineProperties( BufferAttribute.prototype, { - - length: { - get: function () { - - console.warn( 'THREE.BufferAttribute: .length has been deprecated. Use .count instead.' ); - return this.array.length; - - } - } - - } ); - - Object.assign( BufferGeometry.prototype, { - - addIndex: function ( index ) { - - console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); - this.setIndex( index ); - - }, - addDrawCall: function ( start, count, indexOffset ) { - - if ( indexOffset !== undefined ) { - - console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); - - } - console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); - this.addGroup( start, count ); - - }, - clearDrawCalls: function () { - - console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); - this.clearGroups(); - - }, - computeTangents: function () { - - console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' ); - - }, - computeOffsets: function () { - - console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' ); - - } - - } ); - - Object.defineProperties( BufferGeometry.prototype, { - - drawcalls: { - get: function () { - - console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' ); - return this.groups; - - } - }, - offsets: { - get: function () { - - console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' ); - return this.groups; - - } - } - - } ); - - // - - Object.defineProperties( Uniform.prototype, { - - dynamic: { - set: function () { - - console.warn( 'THREE.Uniform: .dynamic has been removed. Use object.onBeforeRender() instead.' ); - - } - }, - onUpdate: { - value: function () { - - console.warn( 'THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead.' ); - return this; - - } - } - - } ); - - // - - Object.defineProperties( Material.prototype, { - - wrapAround: { - get: function () { - - console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' ); - - }, - set: function () { - - console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' ); - - } - }, - wrapRGB: { - get: function () { - - console.warn( 'THREE.' + this.type + ': .wrapRGB has been removed.' ); - return new Color(); - - } - } - - } ); - - Object.defineProperties( MeshPhongMaterial.prototype, { - - metal: { - get: function () { - - console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead.' ); - return false; - - }, - set: function () { - - console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead' ); - - } - } - - } ); - - Object.defineProperties( ShaderMaterial.prototype, { - - derivatives: { - get: function () { - - console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); - return this.extensions.derivatives; - - }, - set: function ( value ) { - - console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); - this.extensions.derivatives = value; - - } - } - - } ); - - // - - EventDispatcher.prototype = Object.assign( Object.create( { - - // Note: Extra base ensures these properties are not 'assign'ed. - - constructor: EventDispatcher, - - apply: function ( target ) { - - console.warn( "THREE.EventDispatcher: .apply is deprecated, " + - "just inherit or Object.assign the prototype to mix-in." ); - - Object.assign( target, this ); - - } - - } ), EventDispatcher.prototype ); - - // - - Object.assign( WebGLRenderer.prototype, { - - supportsFloatTextures: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); - return this.extensions.get( 'OES_texture_float' ); - - }, - supportsHalfFloatTextures: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); - return this.extensions.get( 'OES_texture_half_float' ); - - }, - supportsStandardDerivatives: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); - return this.extensions.get( 'OES_standard_derivatives' ); - - }, - supportsCompressedTextureS3TC: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_s3tc' ); - - }, - supportsCompressedTexturePVRTC: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - - }, - supportsBlendMinMax: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); - return this.extensions.get( 'EXT_blend_minmax' ); - - }, - supportsVertexTextures: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' ); - return this.capabilities.vertexTextures; - - }, - supportsInstancedArrays: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); - return this.extensions.get( 'ANGLE_instanced_arrays' ); - - }, - enableScissorTest: function ( boolean ) { - - console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' ); - this.setScissorTest( boolean ); - - }, - initMaterial: function () { - - console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); - - }, - addPrePlugin: function () { - - console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); - - }, - addPostPlugin: function () { - - console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); - - }, - updateShadowMap: function () { - - console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); - - } - - } ); - - Object.defineProperties( WebGLRenderer.prototype, { - - shadowMapEnabled: { - get: function () { - - return this.shadowMap.enabled; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); - this.shadowMap.enabled = value; - - } - }, - shadowMapType: { - get: function () { - - return this.shadowMap.type; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); - this.shadowMap.type = value; - - } - }, - shadowMapCullFace: { - get: function () { - - return this.shadowMap.cullFace; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace.' ); - this.shadowMap.cullFace = value; - - } - } - } ); - - Object.defineProperties( WebGLShadowMap.prototype, { - - cullFace: { - get: function () { - - return this.renderReverseSided ? CullFaceFront : CullFaceBack; - - }, - set: function ( cullFace ) { - - var value = ( cullFace !== CullFaceBack ); - console.warn( "WebGLRenderer: .shadowMap.cullFace is deprecated. Set .shadowMap.renderReverseSided to " + value + "." ); - this.renderReverseSided = value; - - } - } - - } ); - - // - - Object.defineProperties( WebGLRenderTarget.prototype, { - - wrapS: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); - return this.texture.wrapS; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); - this.texture.wrapS = value; - - } - }, - wrapT: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); - return this.texture.wrapT; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); - this.texture.wrapT = value; - - } - }, - magFilter: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); - return this.texture.magFilter; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); - this.texture.magFilter = value; - - } - }, - minFilter: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); - return this.texture.minFilter; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); - this.texture.minFilter = value; - - } - }, - anisotropy: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); - return this.texture.anisotropy; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); - this.texture.anisotropy = value; - - } - }, - offset: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); - return this.texture.offset; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); - this.texture.offset = value; - - } - }, - repeat: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); - return this.texture.repeat; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); - this.texture.repeat = value; - - } - }, - format: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); - return this.texture.format; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); - this.texture.format = value; - - } - }, - type: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); - return this.texture.type; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); - this.texture.type = value; - - } - }, - generateMipmaps: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); - return this.texture.generateMipmaps; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); - this.texture.generateMipmaps = value; - - } - } - - } ); - - // - - Audio.prototype.load = function ( file ) { - - console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' ); - var scope = this; - var audioLoader = new AudioLoader(); - audioLoader.load( file, function ( buffer ) { - - scope.setBuffer( buffer ); - - } ); - return this; - - }; - - AudioAnalyser.prototype.getData = function () { - - console.warn( 'THREE.AudioAnalyser: .getData() is now .getFrequencyData().' ); - return this.getFrequencyData(); - - }; - - // - - var GeometryUtils = { - - merge: function ( geometry1, geometry2, materialIndexOffset ) { - - console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' ); - var matrix; - - if ( geometry2.isMesh ) { - - geometry2.matrixAutoUpdate && geometry2.updateMatrix(); - - matrix = geometry2.matrix; - geometry2 = geometry2.geometry; - - } - - geometry1.merge( geometry2, matrix, materialIndexOffset ); - - }, - - center: function ( geometry ) { - - console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' ); - return geometry.center(); - - } - - }; - - var ImageUtils = { - - crossOrigin: undefined, - - loadTexture: function ( url, mapping, onLoad, onError ) { - - console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' ); - - var loader = new TextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); - - var texture = loader.load( url, onLoad, undefined, onError ); - - if ( mapping ) texture.mapping = mapping; - - return texture; - - }, - - loadTextureCube: function ( urls, mapping, onLoad, onError ) { - - console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' ); - - var loader = new CubeTextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); - - var texture = loader.load( urls, onLoad, undefined, onError ); - - if ( mapping ) texture.mapping = mapping; - - return texture; - - }, - - loadCompressedTexture: function () { - - console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ); - - }, - - loadCompressedTextureCube: function () { - - console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ); - - } - - }; - - // - - function Projector() { - - console.error( 'THREE.Projector has been moved to /examples/js/renderers/Projector.js.' ); - - this.projectVector = function ( vector, camera ) { - - console.warn( 'THREE.Projector: .projectVector() is now vector.project().' ); - vector.project( camera ); - - }; - - this.unprojectVector = function ( vector, camera ) { - - console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' ); - vector.unproject( camera ); - - }; - - this.pickingRay = function () { - - console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' ); - - }; - - } - - // - - function CanvasRenderer() { - - console.error( 'THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js' ); - - this.domElement = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - this.clear = function () {}; - this.render = function () {}; - this.setClearColor = function () {}; - this.setSize = function () {}; - - } - - exports.WebGLRenderTargetCube = WebGLRenderTargetCube; - exports.WebGLRenderTarget = WebGLRenderTarget; - exports.WebGLRenderer = WebGLRenderer; - exports.ShaderLib = ShaderLib; - exports.UniformsLib = UniformsLib; - exports.UniformsUtils = UniformsUtils; - exports.ShaderChunk = ShaderChunk; - exports.FogExp2 = FogExp2; - exports.Fog = Fog; - exports.Scene = Scene; - exports.LensFlare = LensFlare; - exports.Sprite = Sprite; - exports.LOD = LOD; - exports.SkinnedMesh = SkinnedMesh; - exports.Skeleton = Skeleton; - exports.Bone = Bone; - exports.Mesh = Mesh; - exports.LineSegments = LineSegments; - exports.Line = Line; - exports.Points = Points; - exports.Group = Group; - exports.VideoTexture = VideoTexture; - exports.DataTexture = DataTexture; - exports.CompressedTexture = CompressedTexture; - exports.CubeTexture = CubeTexture; - exports.CanvasTexture = CanvasTexture; - exports.DepthTexture = DepthTexture; - exports.Texture = Texture; - exports.CompressedTextureLoader = CompressedTextureLoader; - exports.BinaryTextureLoader = BinaryTextureLoader; - exports.DataTextureLoader = DataTextureLoader; - exports.CubeTextureLoader = CubeTextureLoader; - exports.TextureLoader = TextureLoader; - exports.ObjectLoader = ObjectLoader; - exports.MaterialLoader = MaterialLoader; - exports.BufferGeometryLoader = BufferGeometryLoader; - exports.DefaultLoadingManager = DefaultLoadingManager; - exports.LoadingManager = LoadingManager; - exports.JSONLoader = JSONLoader; - exports.ImageLoader = ImageLoader; - exports.FontLoader = FontLoader; - exports.FileLoader = FileLoader; - exports.Loader = Loader; - exports.Cache = Cache; - exports.AudioLoader = AudioLoader; - exports.SpotLightShadow = SpotLightShadow; - exports.SpotLight = SpotLight; - exports.PointLight = PointLight; - exports.RectAreaLight = RectAreaLight; - exports.HemisphereLight = HemisphereLight; - exports.DirectionalLightShadow = DirectionalLightShadow; - exports.DirectionalLight = DirectionalLight; - exports.AmbientLight = AmbientLight; - exports.LightShadow = LightShadow; - exports.Light = Light; - exports.StereoCamera = StereoCamera; - exports.PerspectiveCamera = PerspectiveCamera; - exports.OrthographicCamera = OrthographicCamera; - exports.CubeCamera = CubeCamera; - exports.Camera = Camera; - exports.AudioListener = AudioListener; - exports.PositionalAudio = PositionalAudio; - exports.AudioContext = AudioContext; - exports.AudioAnalyser = AudioAnalyser; - exports.Audio = Audio; - exports.VectorKeyframeTrack = VectorKeyframeTrack; - exports.StringKeyframeTrack = StringKeyframeTrack; - exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; - exports.NumberKeyframeTrack = NumberKeyframeTrack; - exports.ColorKeyframeTrack = ColorKeyframeTrack; - exports.BooleanKeyframeTrack = BooleanKeyframeTrack; - exports.PropertyMixer = PropertyMixer; - exports.PropertyBinding = PropertyBinding; - exports.KeyframeTrack = KeyframeTrack; - exports.AnimationUtils = AnimationUtils; - exports.AnimationObjectGroup = AnimationObjectGroup; - exports.AnimationMixer = AnimationMixer; - exports.AnimationClip = AnimationClip; - exports.Uniform = Uniform; - exports.InstancedBufferGeometry = InstancedBufferGeometry; - exports.BufferGeometry = BufferGeometry; - exports.GeometryIdCount = GeometryIdCount; - exports.Geometry = Geometry; - exports.InterleavedBufferAttribute = InterleavedBufferAttribute; - exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; - exports.InterleavedBuffer = InterleavedBuffer; - exports.InstancedBufferAttribute = InstancedBufferAttribute; - exports.Face3 = Face3; - exports.Object3D = Object3D; - exports.Raycaster = Raycaster; - exports.Layers = Layers; - exports.EventDispatcher = EventDispatcher; - exports.Clock = Clock; - exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; - exports.LinearInterpolant = LinearInterpolant; - exports.DiscreteInterpolant = DiscreteInterpolant; - exports.CubicInterpolant = CubicInterpolant; - exports.Interpolant = Interpolant; - exports.Triangle = Triangle; - exports.Spline = Spline; - exports.Math = _Math; - exports.Spherical = Spherical; - exports.Cylindrical = Cylindrical; - exports.Plane = Plane; - exports.Frustum = Frustum; - exports.Sphere = Sphere; - exports.Ray = Ray; - exports.Matrix4 = Matrix4; - exports.Matrix3 = Matrix3; - exports.Box3 = Box3; - exports.Box2 = Box2; - exports.Line3 = Line3; - exports.Euler = Euler; - exports.Vector4 = Vector4; - exports.Vector3 = Vector3; - exports.Vector2 = Vector2; - exports.Quaternion = Quaternion; - exports.Color = Color; - exports.MorphBlendMesh = MorphBlendMesh; - exports.ImmediateRenderObject = ImmediateRenderObject; - exports.VertexNormalsHelper = VertexNormalsHelper; - exports.SpotLightHelper = SpotLightHelper; - exports.SkeletonHelper = SkeletonHelper; - exports.PointLightHelper = PointLightHelper; - exports.RectAreaLightHelper = RectAreaLightHelper; - exports.HemisphereLightHelper = HemisphereLightHelper; - exports.GridHelper = GridHelper; - exports.PolarGridHelper = PolarGridHelper; - exports.FaceNormalsHelper = FaceNormalsHelper; - exports.DirectionalLightHelper = DirectionalLightHelper; - exports.CameraHelper = CameraHelper; - exports.BoxHelper = BoxHelper; - exports.ArrowHelper = ArrowHelper; - exports.AxisHelper = AxisHelper; - exports.CatmullRomCurve3 = CatmullRomCurve3; - exports.SplineCurve3 = SplineCurve3; - exports.CubicBezierCurve3 = CubicBezierCurve3; - exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; - exports.LineCurve3 = LineCurve3; - exports.ArcCurve = ArcCurve; - exports.EllipseCurve = EllipseCurve; - exports.SplineCurve = SplineCurve; - exports.CubicBezierCurve = CubicBezierCurve; - exports.QuadraticBezierCurve = QuadraticBezierCurve; - exports.LineCurve = LineCurve; - exports.Shape = Shape; - exports.ShapePath = ShapePath; - exports.Path = Path; - exports.Font = Font; - exports.CurvePath = CurvePath; - exports.Curve = Curve; - exports.ShapeUtils = ShapeUtils; - exports.SceneUtils = SceneUtils; - exports.CurveUtils = CurveUtils; - exports.WireframeGeometry = WireframeGeometry; - exports.ParametricGeometry = ParametricGeometry; - exports.ParametricBufferGeometry = ParametricBufferGeometry; - exports.TetrahedronGeometry = TetrahedronGeometry; - exports.TetrahedronBufferGeometry = TetrahedronBufferGeometry; - exports.OctahedronGeometry = OctahedronGeometry; - exports.OctahedronBufferGeometry = OctahedronBufferGeometry; - exports.IcosahedronGeometry = IcosahedronGeometry; - exports.IcosahedronBufferGeometry = IcosahedronBufferGeometry; - exports.DodecahedronGeometry = DodecahedronGeometry; - exports.DodecahedronBufferGeometry = DodecahedronBufferGeometry; - exports.PolyhedronGeometry = PolyhedronGeometry; - exports.PolyhedronBufferGeometry = PolyhedronBufferGeometry; - exports.TubeGeometry = TubeGeometry; - exports.TubeBufferGeometry = TubeBufferGeometry; - exports.TorusKnotGeometry = TorusKnotGeometry; - exports.TorusKnotBufferGeometry = TorusKnotBufferGeometry; - exports.TorusGeometry = TorusGeometry; - exports.TorusBufferGeometry = TorusBufferGeometry; - exports.TextGeometry = TextGeometry; - exports.SphereBufferGeometry = SphereBufferGeometry; - exports.SphereGeometry = SphereGeometry; - exports.RingGeometry = RingGeometry; - exports.RingBufferGeometry = RingBufferGeometry; - exports.PlaneBufferGeometry = PlaneBufferGeometry; - exports.PlaneGeometry = PlaneGeometry; - exports.LatheGeometry = LatheGeometry; - exports.LatheBufferGeometry = LatheBufferGeometry; - exports.ShapeGeometry = ShapeGeometry; - exports.ShapeBufferGeometry = ShapeBufferGeometry; - exports.ExtrudeGeometry = ExtrudeGeometry; - exports.EdgesGeometry = EdgesGeometry; - exports.ConeGeometry = ConeGeometry; - exports.ConeBufferGeometry = ConeBufferGeometry; - exports.CylinderGeometry = CylinderGeometry; - exports.CylinderBufferGeometry = CylinderBufferGeometry; - exports.CircleBufferGeometry = CircleBufferGeometry; - exports.CircleGeometry = CircleGeometry; - exports.BoxBufferGeometry = BoxBufferGeometry; - exports.BoxGeometry = BoxGeometry; - exports.ShadowMaterial = ShadowMaterial; - exports.SpriteMaterial = SpriteMaterial; - exports.RawShaderMaterial = RawShaderMaterial; - exports.ShaderMaterial = ShaderMaterial; - exports.PointsMaterial = PointsMaterial; - exports.MultiMaterial = MultiMaterial; - exports.MeshPhysicalMaterial = MeshPhysicalMaterial; - exports.MeshStandardMaterial = MeshStandardMaterial; - exports.MeshPhongMaterial = MeshPhongMaterial; - exports.MeshToonMaterial = MeshToonMaterial; - exports.MeshNormalMaterial = MeshNormalMaterial; - exports.MeshLambertMaterial = MeshLambertMaterial; - exports.MeshDepthMaterial = MeshDepthMaterial; - exports.MeshBasicMaterial = MeshBasicMaterial; - exports.LineDashedMaterial = LineDashedMaterial; - exports.LineBasicMaterial = LineBasicMaterial; - exports.Material = Material; - exports.Float64BufferAttribute = Float64BufferAttribute; - exports.Float32BufferAttribute = Float32BufferAttribute; - exports.Uint32BufferAttribute = Uint32BufferAttribute; - exports.Int32BufferAttribute = Int32BufferAttribute; - exports.Uint16BufferAttribute = Uint16BufferAttribute; - exports.Int16BufferAttribute = Int16BufferAttribute; - exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; - exports.Uint8BufferAttribute = Uint8BufferAttribute; - exports.Int8BufferAttribute = Int8BufferAttribute; - exports.BufferAttribute = BufferAttribute; - exports.REVISION = REVISION; - exports.MOUSE = MOUSE; - exports.CullFaceNone = CullFaceNone; - exports.CullFaceBack = CullFaceBack; - exports.CullFaceFront = CullFaceFront; - exports.CullFaceFrontBack = CullFaceFrontBack; - exports.FrontFaceDirectionCW = FrontFaceDirectionCW; - exports.FrontFaceDirectionCCW = FrontFaceDirectionCCW; - exports.BasicShadowMap = BasicShadowMap; - exports.PCFShadowMap = PCFShadowMap; - exports.PCFSoftShadowMap = PCFSoftShadowMap; - exports.FrontSide = FrontSide; - exports.BackSide = BackSide; - exports.DoubleSide = DoubleSide; - exports.FlatShading = FlatShading; - exports.SmoothShading = SmoothShading; - exports.NoColors = NoColors; - exports.FaceColors = FaceColors; - exports.VertexColors = VertexColors; - exports.NoBlending = NoBlending; - exports.NormalBlending = NormalBlending; - exports.AdditiveBlending = AdditiveBlending; - exports.SubtractiveBlending = SubtractiveBlending; - exports.MultiplyBlending = MultiplyBlending; - exports.CustomBlending = CustomBlending; - exports.BlendingMode = BlendingMode; - exports.AddEquation = AddEquation; - exports.SubtractEquation = SubtractEquation; - exports.ReverseSubtractEquation = ReverseSubtractEquation; - exports.MinEquation = MinEquation; - exports.MaxEquation = MaxEquation; - exports.ZeroFactor = ZeroFactor; - exports.OneFactor = OneFactor; - exports.SrcColorFactor = SrcColorFactor; - exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; - exports.SrcAlphaFactor = SrcAlphaFactor; - exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; - exports.DstAlphaFactor = DstAlphaFactor; - exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; - exports.DstColorFactor = DstColorFactor; - exports.OneMinusDstColorFactor = OneMinusDstColorFactor; - exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; - exports.NeverDepth = NeverDepth; - exports.AlwaysDepth = AlwaysDepth; - exports.LessDepth = LessDepth; - exports.LessEqualDepth = LessEqualDepth; - exports.EqualDepth = EqualDepth; - exports.GreaterEqualDepth = GreaterEqualDepth; - exports.GreaterDepth = GreaterDepth; - exports.NotEqualDepth = NotEqualDepth; - exports.MultiplyOperation = MultiplyOperation; - exports.MixOperation = MixOperation; - exports.AddOperation = AddOperation; - exports.NoToneMapping = NoToneMapping; - exports.LinearToneMapping = LinearToneMapping; - exports.ReinhardToneMapping = ReinhardToneMapping; - exports.Uncharted2ToneMapping = Uncharted2ToneMapping; - exports.CineonToneMapping = CineonToneMapping; - exports.UVMapping = UVMapping; - exports.CubeReflectionMapping = CubeReflectionMapping; - exports.CubeRefractionMapping = CubeRefractionMapping; - exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; - exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; - exports.SphericalReflectionMapping = SphericalReflectionMapping; - exports.CubeUVReflectionMapping = CubeUVReflectionMapping; - exports.CubeUVRefractionMapping = CubeUVRefractionMapping; - exports.TextureMapping = TextureMapping; - exports.RepeatWrapping = RepeatWrapping; - exports.ClampToEdgeWrapping = ClampToEdgeWrapping; - exports.MirroredRepeatWrapping = MirroredRepeatWrapping; - exports.TextureWrapping = TextureWrapping; - exports.NearestFilter = NearestFilter; - exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; - exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; - exports.LinearFilter = LinearFilter; - exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; - exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; - exports.TextureFilter = TextureFilter; - exports.UnsignedByteType = UnsignedByteType; - exports.ByteType = ByteType; - exports.ShortType = ShortType; - exports.UnsignedShortType = UnsignedShortType; - exports.IntType = IntType; - exports.UnsignedIntType = UnsignedIntType; - exports.FloatType = FloatType; - exports.HalfFloatType = HalfFloatType; - exports.UnsignedShort4444Type = UnsignedShort4444Type; - exports.UnsignedShort5551Type = UnsignedShort5551Type; - exports.UnsignedShort565Type = UnsignedShort565Type; - exports.UnsignedInt248Type = UnsignedInt248Type; - exports.AlphaFormat = AlphaFormat; - exports.RGBFormat = RGBFormat; - exports.RGBAFormat = RGBAFormat; - exports.LuminanceFormat = LuminanceFormat; - exports.LuminanceAlphaFormat = LuminanceAlphaFormat; - exports.RGBEFormat = RGBEFormat; - exports.DepthFormat = DepthFormat; - exports.DepthStencilFormat = DepthStencilFormat; - exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; - exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; - exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; - exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; - exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; - exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; - exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; - exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; - exports.RGB_ETC1_Format = RGB_ETC1_Format; - exports.LoopOnce = LoopOnce; - exports.LoopRepeat = LoopRepeat; - exports.LoopPingPong = LoopPingPong; - exports.InterpolateDiscrete = InterpolateDiscrete; - exports.InterpolateLinear = InterpolateLinear; - exports.InterpolateSmooth = InterpolateSmooth; - exports.ZeroCurvatureEnding = ZeroCurvatureEnding; - exports.ZeroSlopeEnding = ZeroSlopeEnding; - exports.WrapAroundEnding = WrapAroundEnding; - exports.TrianglesDrawMode = TrianglesDrawMode; - exports.TriangleStripDrawMode = TriangleStripDrawMode; - exports.TriangleFanDrawMode = TriangleFanDrawMode; - exports.LinearEncoding = LinearEncoding; - exports.sRGBEncoding = sRGBEncoding; - exports.GammaEncoding = GammaEncoding; - exports.RGBEEncoding = RGBEEncoding; - exports.LogLuvEncoding = LogLuvEncoding; - exports.RGBM7Encoding = RGBM7Encoding; - exports.RGBM16Encoding = RGBM16Encoding; - exports.RGBDEncoding = RGBDEncoding; - exports.BasicDepthPacking = BasicDepthPacking; - exports.RGBADepthPacking = RGBADepthPacking; - exports.CubeGeometry = BoxGeometry; - exports.Face4 = Face4; - exports.LineStrip = LineStrip; - exports.LinePieces = LinePieces; - exports.MeshFaceMaterial = MeshFaceMaterial; - exports.PointCloud = PointCloud; - exports.Particle = Particle; - exports.ParticleSystem = ParticleSystem; - exports.PointCloudMaterial = PointCloudMaterial; - exports.ParticleBasicMaterial = ParticleBasicMaterial; - exports.ParticleSystemMaterial = ParticleSystemMaterial; - exports.Vertex = Vertex; - exports.DynamicBufferAttribute = DynamicBufferAttribute; - exports.Int8Attribute = Int8Attribute; - exports.Uint8Attribute = Uint8Attribute; - exports.Uint8ClampedAttribute = Uint8ClampedAttribute; - exports.Int16Attribute = Int16Attribute; - exports.Uint16Attribute = Uint16Attribute; - exports.Int32Attribute = Int32Attribute; - exports.Uint32Attribute = Uint32Attribute; - exports.Float32Attribute = Float32Attribute; - exports.Float64Attribute = Float64Attribute; - exports.ClosedSplineCurve3 = ClosedSplineCurve3; - exports.BoundingBoxHelper = BoundingBoxHelper; - exports.EdgesHelper = EdgesHelper; - exports.WireframeHelper = WireframeHelper; - exports.XHRLoader = XHRLoader; - exports.GeometryUtils = GeometryUtils; - exports.ImageUtils = ImageUtils; - exports.Projector = Projector; - exports.CanvasRenderer = CanvasRenderer; - - Object.defineProperty(exports, '__esModule', { value: true }); - -}))); - -},{}],2:[function(require,module,exports){ -/** - * @author dmarcos / https://github.com/dmarcos - * @author mrdoob / http://mrdoob.com - */ - -THREE.VRControls = function ( object, onError ) { - - var scope = this; - - var vrDisplay, vrDisplays; - - var standingMatrix = new THREE.Matrix4(); - - var frameData = null; - - if ( 'VRFrameData' in window ) { - - frameData = new VRFrameData(); - - } - - function gotVRDisplays( displays ) { - - vrDisplays = displays; - - if ( displays.length > 0 ) { - - vrDisplay = displays[ 0 ]; - - } else { - - if ( onError ) onError( 'VR input not available.' ); - - } - - } - - if ( navigator.getVRDisplays ) { - - navigator.getVRDisplays().then( gotVRDisplays ).catch ( function () { - - console.warn( 'THREE.VRControls: Unable to get VR Displays' ); - - } ); - - } - - // the Rift SDK returns the position in meters - // this scale factor allows the user to define how meters - // are converted to scene units. - - this.scale = 1; - - // If true will use "standing space" coordinate system where y=0 is the - // floor and x=0, z=0 is the center of the room. - this.standing = false; - - // Distance from the users eyes to the floor in meters. Used when - // standing=true but the VRDisplay doesn't provide stageParameters. - this.userHeight = 1.6; - - this.getVRDisplay = function () { - - return vrDisplay; - - }; - - this.setVRDisplay = function ( value ) { - - vrDisplay = value; - - }; - - this.getVRDisplays = function () { - - console.warn( 'THREE.VRControls: getVRDisplays() is being deprecated.' ); - return vrDisplays; - - }; - - this.getStandingMatrix = function () { - - return standingMatrix; - - }; - - this.update = function () { - - if ( vrDisplay ) { - - var pose; - - if ( vrDisplay.getFrameData ) { - - vrDisplay.getFrameData( frameData ); - pose = frameData.pose; - - } else if ( vrDisplay.getPose ) { - - pose = vrDisplay.getPose(); - - } - - if ( pose.orientation !== null ) { - - object.quaternion.fromArray( pose.orientation ); - - } - - if ( pose.position !== null ) { - - object.position.fromArray( pose.position ); - - } else { - - object.position.set( 0, 0, 0 ); - - } - - if ( this.standing ) { - - if ( vrDisplay.stageParameters ) { - - object.updateMatrix(); - - standingMatrix.fromArray( vrDisplay.stageParameters.sittingToStandingTransform ); - object.applyMatrix( standingMatrix ); - - } else { - - object.position.setY( object.position.y + this.userHeight ); - - } - - } - - object.position.multiplyScalar( scope.scale ); - - } - - }; - - this.resetPose = function () { - - if ( vrDisplay ) { - - vrDisplay.resetPose(); - - } - - }; - - this.resetSensor = function () { - - console.warn( 'THREE.VRControls: .resetSensor() is now .resetPose().' ); - this.resetPose(); - - }; - - this.zeroSensor = function () { - - console.warn( 'THREE.VRControls: .zeroSensor() is now .resetPose().' ); - this.resetPose(); - - }; - - this.dispose = function () { - - vrDisplay = null; - - }; - -}; - -},{}],3:[function(require,module,exports){ -/** - * @author dmarcos / https://github.com/dmarcos - * @author mrdoob / http://mrdoob.com - * - * WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html - * - * Firefox: http://mozvr.com/downloads/ - * Chromium: https://webvr.info/get-chrome - * - */ - -THREE.VREffect = function( renderer, onError ) { - - var vrDisplay, vrDisplays; - var eyeTranslationL = new THREE.Vector3(); - var eyeTranslationR = new THREE.Vector3(); - var renderRectL, renderRectR; - - var frameData = null; - - if ( 'VRFrameData' in window ) { - - frameData = new window.VRFrameData(); - - } - - function gotVRDisplays( displays ) { - - vrDisplays = displays; - - if ( displays.length > 0 ) { - - vrDisplay = displays[ 0 ]; - - } else { - - if ( onError ) onError( 'HMD not available' ); - - } - - } - - if ( navigator.getVRDisplays ) { - - navigator.getVRDisplays().then( gotVRDisplays ).catch( function() { - - console.warn( 'THREE.VREffect: Unable to get VR Displays' ); - - } ); - - } - - // - - this.isPresenting = false; - this.scale = 1; - - var scope = this; - - var rendererSize = renderer.getSize(); - var rendererUpdateStyle = false; - var rendererPixelRatio = renderer.getPixelRatio(); - - this.getVRDisplay = function() { - - return vrDisplay; - - }; - - this.setVRDisplay = function( value ) { - - vrDisplay = value; - - }; - - this.getVRDisplays = function() { - - console.warn( 'THREE.VREffect: getVRDisplays() is being deprecated.' ); - return vrDisplays; - - }; - - this.setSize = function( width, height, updateStyle ) { - - rendererSize = { width: width, height: height }; - rendererUpdateStyle = updateStyle; - - if ( scope.isPresenting ) { - - var eyeParamsL = vrDisplay.getEyeParameters( 'left' ); - renderer.setPixelRatio( 1 ); - renderer.setSize( eyeParamsL.renderWidth * 2, eyeParamsL.renderHeight, false ); - - } else { - - renderer.setPixelRatio( rendererPixelRatio ); - renderer.setSize( width, height, updateStyle ); - - } - - }; - - // VR presentation - - var canvas = renderer.domElement; - var defaultLeftBounds = [ 0.0, 0.0, 0.5, 1.0 ]; - var defaultRightBounds = [ 0.5, 0.0, 0.5, 1.0 ]; - - function onVRDisplayPresentChange() { - - var wasPresenting = scope.isPresenting; - scope.isPresenting = vrDisplay !== undefined && vrDisplay.isPresenting; - - if ( scope.isPresenting ) { - - var eyeParamsL = vrDisplay.getEyeParameters( 'left' ); - var eyeWidth = eyeParamsL.renderWidth; - var eyeHeight = eyeParamsL.renderHeight; - - if ( ! wasPresenting ) { - - rendererPixelRatio = renderer.getPixelRatio(); - rendererSize = renderer.getSize(); - - renderer.setPixelRatio( 1 ); - renderer.setSize( eyeWidth * 2, eyeHeight, false ); - - } - - } else if ( wasPresenting ) { - - renderer.setPixelRatio( rendererPixelRatio ); - renderer.setSize( rendererSize.width, rendererSize.height, rendererUpdateStyle ); - - } - - } - - window.addEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange, false ); - - this.setFullScreen = function( boolean ) { - - return new Promise( function( resolve, reject ) { - - if ( vrDisplay === undefined ) { - - reject( new Error( 'No VR hardware found.' ) ); - return; - - } - - if ( scope.isPresenting === boolean ) { - - resolve(); - return; - - } - - if ( boolean ) { - - resolve( vrDisplay.requestPresent( [ { source: canvas } ] ) ); - - } else { - - resolve( vrDisplay.exitPresent() ); - - } - - } ); - - }; - - this.requestPresent = function() { - - return this.setFullScreen( true ); - - }; - - this.exitPresent = function() { - - return this.setFullScreen( false ); - - }; - - this.requestAnimationFrame = function( f ) { - - if ( vrDisplay !== undefined ) { - - return vrDisplay.requestAnimationFrame( f ); - - } else { - - return window.requestAnimationFrame( f ); - - } - - }; - - this.cancelAnimationFrame = function( h ) { - - if ( vrDisplay !== undefined ) { - - vrDisplay.cancelAnimationFrame( h ); - - } else { - - window.cancelAnimationFrame( h ); - - } - - }; - - this.submitFrame = function() { - - if ( vrDisplay !== undefined && scope.isPresenting ) { - - vrDisplay.submitFrame(); - - } - - }; - - this.autoSubmitFrame = true; - - // render - - var cameraL = new THREE.PerspectiveCamera(); - cameraL.layers.enable( 1 ); - - var cameraR = new THREE.PerspectiveCamera(); - cameraR.layers.enable( 2 ); - - this.render = function( scene, camera, renderTarget, forceClear ) { - - if ( vrDisplay && scope.isPresenting ) { - - var autoUpdate = scene.autoUpdate; - - if ( autoUpdate ) { - - scene.updateMatrixWorld(); - scene.autoUpdate = false; - - } - - var eyeParamsL = vrDisplay.getEyeParameters( 'left' ); - var eyeParamsR = vrDisplay.getEyeParameters( 'right' ); - - eyeTranslationL.fromArray( eyeParamsL.offset ); - eyeTranslationR.fromArray( eyeParamsR.offset ); - - if ( Array.isArray( scene ) ) { - - console.warn( 'THREE.VREffect.render() no longer supports arrays. Use object.layers instead.' ); - scene = scene[ 0 ]; - - } - - // When rendering we don't care what the recommended size is, only what the actual size - // of the backbuffer is. - var size = renderer.getSize(); - var layers = vrDisplay.getLayers(); - var leftBounds; - var rightBounds; - - if ( layers.length ) { - - var layer = layers[ 0 ]; - - leftBounds = layer.leftBounds !== null && layer.leftBounds.length === 4 ? layer.leftBounds : defaultLeftBounds; - rightBounds = layer.rightBounds !== null && layer.rightBounds.length === 4 ? layer.rightBounds : defaultRightBounds; - - } else { - - leftBounds = defaultLeftBounds; - rightBounds = defaultRightBounds; - - } - - renderRectL = { - x: Math.round( size.width * leftBounds[ 0 ] ), - y: Math.round( size.height * leftBounds[ 1 ] ), - width: Math.round( size.width * leftBounds[ 2 ] ), - height: Math.round( size.height * leftBounds[ 3 ] ) - }; - renderRectR = { - x: Math.round( size.width * rightBounds[ 0 ] ), - y: Math.round( size.height * rightBounds[ 1 ] ), - width: Math.round( size.width * rightBounds[ 2 ] ), - height: Math.round( size.height * rightBounds[ 3 ] ) - }; - - if ( renderTarget ) { - - renderer.setRenderTarget( renderTarget ); - renderTarget.scissorTest = true; - - } else { - - renderer.setRenderTarget( null ); - renderer.setScissorTest( true ); - - } - - if ( renderer.autoClear || forceClear ) renderer.clear(); - - if ( camera.parent === null ) camera.updateMatrixWorld(); - - camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale ); - camera.matrixWorld.decompose( cameraR.position, cameraR.quaternion, cameraR.scale ); - - var scale = this.scale; - cameraL.translateOnAxis( eyeTranslationL, scale ); - cameraR.translateOnAxis( eyeTranslationR, scale ); - - if ( vrDisplay.getFrameData ) { - - vrDisplay.depthNear = camera.near; - vrDisplay.depthFar = camera.far; - - vrDisplay.getFrameData( frameData ); - - cameraL.projectionMatrix.elements = frameData.leftProjectionMatrix; - cameraR.projectionMatrix.elements = frameData.rightProjectionMatrix; - - } else { - - cameraL.projectionMatrix = fovToProjection( eyeParamsL.fieldOfView, true, camera.near, camera.far ); - cameraR.projectionMatrix = fovToProjection( eyeParamsR.fieldOfView, true, camera.near, camera.far ); - - } - - // render left eye - if ( renderTarget ) { - - renderTarget.viewport.set( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height ); - renderTarget.scissor.set( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height ); - - } else { - - renderer.setViewport( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height ); - renderer.setScissor( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height ); - - } - renderer.render( scene, cameraL, renderTarget, forceClear ); - - // render right eye - if ( renderTarget ) { - - renderTarget.viewport.set( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height ); - renderTarget.scissor.set( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height ); - - } else { - - renderer.setViewport( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height ); - renderer.setScissor( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height ); - - } - renderer.render( scene, cameraR, renderTarget, forceClear ); - - if ( renderTarget ) { - - renderTarget.viewport.set( 0, 0, size.width, size.height ); - renderTarget.scissor.set( 0, 0, size.width, size.height ); - renderTarget.scissorTest = false; - renderer.setRenderTarget( null ); - - } else { - - renderer.setViewport( 0, 0, size.width, size.height ); - renderer.setScissorTest( false ); - - } - - if ( autoUpdate ) { - - scene.autoUpdate = true; - - } - - if ( scope.autoSubmitFrame ) { - - scope.submitFrame(); - - } - - return; - - } - - // Regular render mode if not HMD - - renderer.render( scene, camera, renderTarget, forceClear ); - - }; - - this.dispose = function() { - - window.removeEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange, false ); - - }; - - // - - function fovToNDCScaleOffset( fov ) { - - var pxscale = 2.0 / ( fov.leftTan + fov.rightTan ); - var pxoffset = ( fov.leftTan - fov.rightTan ) * pxscale * 0.5; - var pyscale = 2.0 / ( fov.upTan + fov.downTan ); - var pyoffset = ( fov.upTan - fov.downTan ) * pyscale * 0.5; - return { scale: [ pxscale, pyscale ], offset: [ pxoffset, pyoffset ] }; - - } - - function fovPortToProjection( fov, rightHanded, zNear, zFar ) { - - rightHanded = rightHanded === undefined ? true : rightHanded; - zNear = zNear === undefined ? 0.01 : zNear; - zFar = zFar === undefined ? 10000.0 : zFar; - - var handednessScale = rightHanded ? - 1.0 : 1.0; - - // start with an identity matrix - var mobj = new THREE.Matrix4(); - var m = mobj.elements; - - // and with scale/offset info for normalized device coords - var scaleAndOffset = fovToNDCScaleOffset( fov ); - - // X result, map clip edges to [-w,+w] - m[ 0 * 4 + 0 ] = scaleAndOffset.scale[ 0 ]; - m[ 0 * 4 + 1 ] = 0.0; - m[ 0 * 4 + 2 ] = scaleAndOffset.offset[ 0 ] * handednessScale; - m[ 0 * 4 + 3 ] = 0.0; - - // Y result, map clip edges to [-w,+w] - // Y offset is negated because this proj matrix transforms from world coords with Y=up, - // but the NDC scaling has Y=down (thanks D3D?) - m[ 1 * 4 + 0 ] = 0.0; - m[ 1 * 4 + 1 ] = scaleAndOffset.scale[ 1 ]; - m[ 1 * 4 + 2 ] = - scaleAndOffset.offset[ 1 ] * handednessScale; - m[ 1 * 4 + 3 ] = 0.0; - - // Z result (up to the app) - m[ 2 * 4 + 0 ] = 0.0; - m[ 2 * 4 + 1 ] = 0.0; - m[ 2 * 4 + 2 ] = zFar / ( zNear - zFar ) * - handednessScale; - m[ 2 * 4 + 3 ] = ( zFar * zNear ) / ( zNear - zFar ); - - // W result (= Z in) - m[ 3 * 4 + 0 ] = 0.0; - m[ 3 * 4 + 1 ] = 0.0; - m[ 3 * 4 + 2 ] = handednessScale; - m[ 3 * 4 + 3 ] = 0.0; - - mobj.transpose(); - - return mobj; - - } - - function fovToProjection( fov, rightHanded, zNear, zFar ) { - - var DEG2RAD = Math.PI / 180.0; - - var fovPort = { - upTan: Math.tan( fov.upDegrees * DEG2RAD ), - downTan: Math.tan( fov.downDegrees * DEG2RAD ), - leftTan: Math.tan( fov.leftDegrees * DEG2RAD ), - rightTan: Math.tan( fov.rightDegrees * DEG2RAD ) - }; - - return fovPortToProjection( fovPort, rightHanded, zNear, zFar ); - - } - -}; - -},{}],4:[function(require,module,exports){ -(function (global){ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.WebVRManager = f()}})(function(){var define,module,exports;return (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 %s', this.mode, mode); - this.mode = mode; - this.button.setMode(mode, this.isVRCompatible); - - // Emit an event indicating the mode changed. - this.emit('modechange', mode, oldMode); -}; - -/** - * Main button was clicked. - */ -WebVRManager.prototype.onFSClick_ = function() { - switch (this.mode) { - case Modes.NORMAL: - // TODO: Remove this hack if/when iOS gets real fullscreen mode. - // If this is an iframe on iOS, break out and open in no_fullscreen mode. - if (Util.isIOS() && Util.isIFrame()) { - if (this.fullscreenCallback) { - this.fullscreenCallback(); - } else { - var url = window.location.href; - url = Util.appendQueryParameter(url, 'no_fullscreen', 'true'); - url = Util.appendQueryParameter(url, 'start_mode', Modes.MAGIC_WINDOW); - top.location.href = url; - return; - } - } - this.setMode_(Modes.MAGIC_WINDOW); - this.requestFullscreen_(); - break; - case Modes.MAGIC_WINDOW: - if (this.isFullscreenDisabled) { - window.history.back(); - return; - } - if (this.exitFullscreenCallback) { - this.exitFullscreenCallback(); - } - this.setMode_(Modes.NORMAL); - this.exitFullscreen_(); - break; - } -}; - -/** - * The VR button was clicked. - */ -WebVRManager.prototype.onVRClick_ = function() { - // TODO: Remove this hack when iOS has fullscreen mode. - // If this is an iframe on iOS, break out and open in no_fullscreen mode. - if (this.mode == Modes.NORMAL && Util.isIOS() && Util.isIFrame()) { - if (this.vrCallback) { - this.vrCallback(); - } else { - var url = window.location.href; - url = Util.appendQueryParameter(url, 'no_fullscreen', 'true'); - url = Util.appendQueryParameter(url, 'start_mode', Modes.VR); - top.location.href = url; - return; - } - } - this.enterVRMode_(); -}; - -WebVRManager.prototype.requestFullscreen_ = function() { - var canvas = document.body; - //var canvas = this.renderer.domElement; - if (canvas.requestFullscreen) { - canvas.requestFullscreen(); - } else if (canvas.mozRequestFullScreen) { - canvas.mozRequestFullScreen(); - } else if (canvas.webkitRequestFullscreen) { - canvas.webkitRequestFullscreen(); - } else if (canvas.msRequestFullscreen) { - canvas.msRequestFullscreen(); - } -}; - -WebVRManager.prototype.exitFullscreen_ = function() { - if (document.exitFullscreen) { - document.exitFullscreen(); - } else if (document.mozCancelFullScreen) { - document.mozCancelFullScreen(); - } else if (document.webkitExitFullscreen) { - document.webkitExitFullscreen(); - } else if (document.msExitFullscreen) { - document.msExitFullscreen(); - } -}; - -WebVRManager.prototype.onVRDisplayPresentChange_ = function(e) { - console.log('onVRDisplayPresentChange_', e); - if (this.hmd.isPresenting) { - this.setMode_(Modes.VR); - } else { - this.setMode_(Modes.NORMAL); - } -}; - -WebVRManager.prototype.onVRDisplayDeviceParamsChange_ = function(e) { - console.log('onVRDisplayDeviceParamsChange_', e); -}; - -WebVRManager.prototype.onFullscreenChange_ = function(e) { - // If we leave full-screen, go back to normal mode. - if (document.webkitFullscreenElement === null || - document.mozFullScreenElement === null) { - this.setMode_(Modes.NORMAL); - } -}; - -module.exports = WebVRManager; - -},{"./button-manager.js":1,"./emitter.js":2,"./modes.js":3,"./util.js":4}]},{},[5])(5) -}); -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{}],5:[function(require,module,exports){ -module.exports={ - "name": "webvr-polyfill", - "version": "0.9.35", - "homepage": "https://github.com/googlevr/webvr-polyfill", - "authors": [ - "Boris Smus ", - "Brandon Jones ", - "Jordan Santell " - ], - "description": "Use WebVR today, on mobile or desktop, without requiring a special browser build.", - "devDependencies": { - "chai": "^3.5.0", - "jsdom": "^9.12.0", - "mocha": "^3.2.0", - "semver": "^5.3.0", - "webpack": "^2.6.1", - "webpack-dev-server": "^2.4.5" - }, - "main": "src/node-entry", - "keywords": [ - "vr", - "webvr" - ], - "license": "Apache-2.0", - "scripts": { - "start": "npm run watch", - "watch": "webpack-dev-server", - "build": "webpack", - "test": "mocha" - }, - "repository": "googlevr/webvr-polyfill", - "bugs": { - "url": "https://github.com/googlevr/webvr-polyfill/issues" - } -} -},{}],6:[function(require,module,exports){ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var Util = require('./util.js'); -var WakeLock = require('./wakelock.js'); - -// Start at a higher number to reduce chance of conflict. -var nextDisplayId = 1000; -var hasShowDeprecationWarning = false; - -var defaultLeftBounds = [0, 0, 0.5, 1]; -var defaultRightBounds = [0.5, 0, 0.5, 1]; - -/** - * The base class for all VR frame data. - */ - -function VRFrameData() { - this.leftProjectionMatrix = new Float32Array(16); - this.leftViewMatrix = new Float32Array(16); - this.rightProjectionMatrix = new Float32Array(16); - this.rightViewMatrix = new Float32Array(16); - this.pose = null; -}; - -/** - * The base class for all VR displays. - */ -function VRDisplay() { - this.isPolyfilled = true; - this.displayId = nextDisplayId++; - this.displayName = 'webvr-polyfill displayName'; - - this.depthNear = 0.01; - this.depthFar = 10000.0; - - this.isConnected = true; - this.isPresenting = false; - this.capabilities = { - hasPosition: false, - hasOrientation: false, - hasExternalDisplay: false, - canPresent: false, - maxLayers: 1 - }; - this.stageParameters = null; - - // "Private" members. - this.waitingForPresent_ = false; - this.layer_ = null; - - this.fullscreenElement_ = null; - this.fullscreenWrapper_ = null; - this.fullscreenElementCachedStyle_ = null; - - this.fullscreenEventTarget_ = null; - this.fullscreenChangeHandler_ = null; - this.fullscreenErrorHandler_ = null; - - this.wakelock_ = new WakeLock(); -} - -VRDisplay.prototype.getFrameData = function(frameData) { - // TODO: Technically this should retain it's value for the duration of a frame - // but I doubt that's practical to do in javascript. - return Util.frameDataFromPose(frameData, this.getPose(), this); -}; - -VRDisplay.prototype.getPose = function() { - // TODO: Technically this should retain it's value for the duration of a frame - // but I doubt that's practical to do in javascript. - return this.getImmediatePose(); -}; - -VRDisplay.prototype.requestAnimationFrame = function(callback) { - return window.requestAnimationFrame(callback); -}; - -VRDisplay.prototype.cancelAnimationFrame = function(id) { - return window.cancelAnimationFrame(id); -}; - -VRDisplay.prototype.wrapForFullscreen = function(element) { - // Don't wrap in iOS. - if (Util.isIOS()) { - return element; - } - if (!this.fullscreenWrapper_) { - this.fullscreenWrapper_ = document.createElement('div'); - var cssProperties = [ - 'height: ' + Math.min(screen.height, screen.width) + 'px !important', - 'top: 0 !important', - 'left: 0 !important', - 'right: 0 !important', - 'border: 0', - 'margin: 0', - 'padding: 0', - 'z-index: 999999 !important', - 'position: fixed', - ]; - this.fullscreenWrapper_.setAttribute('style', cssProperties.join('; ') + ';'); - this.fullscreenWrapper_.classList.add('webvr-polyfill-fullscreen-wrapper'); - } - - if (this.fullscreenElement_ == element) { - return this.fullscreenWrapper_; - } - - // Remove any previously applied wrappers - this.removeFullscreenWrapper(); - - this.fullscreenElement_ = element; - var parent = this.fullscreenElement_.parentElement; - parent.insertBefore(this.fullscreenWrapper_, this.fullscreenElement_); - parent.removeChild(this.fullscreenElement_); - this.fullscreenWrapper_.insertBefore(this.fullscreenElement_, this.fullscreenWrapper_.firstChild); - this.fullscreenElementCachedStyle_ = this.fullscreenElement_.getAttribute('style'); - - var self = this; - function applyFullscreenElementStyle() { - if (!self.fullscreenElement_) { - return; - } - - var cssProperties = [ - 'position: absolute', - 'top: 0', - 'left: 0', - 'width: ' + Math.max(screen.width, screen.height) + 'px', - 'height: ' + Math.min(screen.height, screen.width) + 'px', - 'border: 0', - 'margin: 0', - 'padding: 0', - ]; - self.fullscreenElement_.setAttribute('style', cssProperties.join('; ') + ';'); - } - - applyFullscreenElementStyle(); - - return this.fullscreenWrapper_; -}; - -VRDisplay.prototype.removeFullscreenWrapper = function() { - if (!this.fullscreenElement_) { - return; - } - - var element = this.fullscreenElement_; - if (this.fullscreenElementCachedStyle_) { - element.setAttribute('style', this.fullscreenElementCachedStyle_); - } else { - element.removeAttribute('style'); - } - this.fullscreenElement_ = null; - this.fullscreenElementCachedStyle_ = null; - - var parent = this.fullscreenWrapper_.parentElement; - this.fullscreenWrapper_.removeChild(element); - parent.insertBefore(element, this.fullscreenWrapper_); - parent.removeChild(this.fullscreenWrapper_); - - return element; -}; - -VRDisplay.prototype.requestPresent = function(layers) { - var wasPresenting = this.isPresenting; - var self = this; - - if (!(layers instanceof Array)) { - if (!hasShowDeprecationWarning) { - console.warn("Using a deprecated form of requestPresent. Should pass in an array of VRLayers."); - hasShowDeprecationWarning = true; - } - layers = [layers]; - } - - return new Promise(function(resolve, reject) { - if (!self.capabilities.canPresent) { - reject(new Error('VRDisplay is not capable of presenting.')); - return; - } - - if (layers.length == 0 || layers.length > self.capabilities.maxLayers) { - reject(new Error('Invalid number of layers.')); - return; - } - - var incomingLayer = layers[0]; - if (!incomingLayer.source) { - /* - todo: figure out the correct behavior if the source is not provided. - see https://github.com/w3c/webvr/issues/58 - */ - resolve(); - return; - } - - var leftBounds = incomingLayer.leftBounds || defaultLeftBounds; - var rightBounds = incomingLayer.rightBounds || defaultRightBounds; - if (wasPresenting) { - // Already presenting, just changing configuration - var layer = self.layer_; - if (layer.source !== incomingLayer.source) { - layer.source = incomingLayer.source; - } - - for (var i = 0; i < 4; i++) { - layer.leftBounds[i] = leftBounds[i]; - layer.rightBounds[i] = rightBounds[i]; - } - - resolve(); - return; - } - - // Was not already presenting. - self.layer_ = { - predistorted: incomingLayer.predistorted, - source: incomingLayer.source, - leftBounds: leftBounds.slice(0), - rightBounds: rightBounds.slice(0) - }; - - self.waitingForPresent_ = false; - if (self.layer_ && self.layer_.source) { - var fullscreenElement = self.wrapForFullscreen(self.layer_.source); - - var onFullscreenChange = function() { - var actualFullscreenElement = Util.getFullscreenElement(); - - self.isPresenting = (fullscreenElement === actualFullscreenElement); - if (self.isPresenting) { - if (screen.orientation && screen.orientation.lock) { - screen.orientation.lock('landscape-primary').catch(function(error){ - console.error('screen.orientation.lock() failed due to', error.message) - }); - } - self.waitingForPresent_ = false; - self.beginPresent_(); - resolve(); - } else { - if (screen.orientation && screen.orientation.unlock) { - screen.orientation.unlock(); - } - self.removeFullscreenWrapper(); - self.wakelock_.release(); - self.endPresent_(); - self.removeFullscreenListeners_(); - } - self.fireVRDisplayPresentChange_(); - } - var onFullscreenError = function() { - if (!self.waitingForPresent_) { - return; - } - - self.removeFullscreenWrapper(); - self.removeFullscreenListeners_(); - - self.wakelock_.release(); - self.waitingForPresent_ = false; - self.isPresenting = false; - - reject(new Error('Unable to present.')); - } - - self.addFullscreenListeners_(fullscreenElement, - onFullscreenChange, onFullscreenError); - - if (Util.requestFullscreen(fullscreenElement)) { - self.wakelock_.request(); - self.waitingForPresent_ = true; - } else if (Util.isIOS() || Util.isWebViewAndroid()) { - // *sigh* Just fake it. - self.wakelock_.request(); - self.isPresenting = true; - self.beginPresent_(); - self.fireVRDisplayPresentChange_(); - resolve(); - } - } - - if (!self.waitingForPresent_ && !Util.isIOS()) { - Util.exitFullscreen(); - reject(new Error('Unable to present.')); - } - }); -}; - -VRDisplay.prototype.exitPresent = function() { - var wasPresenting = this.isPresenting; - var self = this; - this.isPresenting = false; - this.layer_ = null; - this.wakelock_.release(); - - return new Promise(function(resolve, reject) { - if (wasPresenting) { - if (!Util.exitFullscreen() && Util.isIOS()) { - self.endPresent_(); - self.fireVRDisplayPresentChange_(); - } - - if (Util.isWebViewAndroid()) { - self.removeFullscreenWrapper(); - self.removeFullscreenListeners_(); - self.endPresent_(); - self.fireVRDisplayPresentChange_(); - } - - resolve(); - } else { - reject(new Error('Was not presenting to VRDisplay.')); - } - }); -}; - -VRDisplay.prototype.getLayers = function() { - if (this.layer_) { - return [this.layer_]; - } - return []; -}; - -VRDisplay.prototype.fireVRDisplayPresentChange_ = function() { - // Important: unfortunately we cannot have full spec compliance here. - // CustomEvent custom fields all go under e.detail (so the VRDisplay ends up - // being e.detail.display, instead of e.display as per WebVR spec). - var event = new CustomEvent('vrdisplaypresentchange', {detail: {display: this}}); - window.dispatchEvent(event); -}; - -VRDisplay.prototype.fireVRDisplayConnect_ = function() { - // Important: unfortunately we cannot have full spec compliance here. - // CustomEvent custom fields all go under e.detail (so the VRDisplay ends up - // being e.detail.display, instead of e.display as per WebVR spec). - var event = new CustomEvent('vrdisplayconnect', {detail: {display: this}}); - window.dispatchEvent(event); -}; - -VRDisplay.prototype.addFullscreenListeners_ = function(element, changeHandler, errorHandler) { - this.removeFullscreenListeners_(); - - this.fullscreenEventTarget_ = element; - this.fullscreenChangeHandler_ = changeHandler; - this.fullscreenErrorHandler_ = errorHandler; - - if (changeHandler) { - if (document.fullscreenEnabled) { - element.addEventListener('fullscreenchange', changeHandler, false); - } else if (document.webkitFullscreenEnabled) { - element.addEventListener('webkitfullscreenchange', changeHandler, false); - } else if (document.mozFullScreenEnabled) { - document.addEventListener('mozfullscreenchange', changeHandler, false); - } else if (document.msFullscreenEnabled) { - element.addEventListener('msfullscreenchange', changeHandler, false); - } - } - - if (errorHandler) { - if (document.fullscreenEnabled) { - element.addEventListener('fullscreenerror', errorHandler, false); - } else if (document.webkitFullscreenEnabled) { - element.addEventListener('webkitfullscreenerror', errorHandler, false); - } else if (document.mozFullScreenEnabled) { - document.addEventListener('mozfullscreenerror', errorHandler, false); - } else if (document.msFullscreenEnabled) { - element.addEventListener('msfullscreenerror', errorHandler, false); - } - } -}; - -VRDisplay.prototype.removeFullscreenListeners_ = function() { - if (!this.fullscreenEventTarget_) - return; - - var element = this.fullscreenEventTarget_; - - if (this.fullscreenChangeHandler_) { - var changeHandler = this.fullscreenChangeHandler_; - element.removeEventListener('fullscreenchange', changeHandler, false); - element.removeEventListener('webkitfullscreenchange', changeHandler, false); - document.removeEventListener('mozfullscreenchange', changeHandler, false); - element.removeEventListener('msfullscreenchange', changeHandler, false); - } - - if (this.fullscreenErrorHandler_) { - var errorHandler = this.fullscreenErrorHandler_; - element.removeEventListener('fullscreenerror', errorHandler, false); - element.removeEventListener('webkitfullscreenerror', errorHandler, false); - document.removeEventListener('mozfullscreenerror', errorHandler, false); - element.removeEventListener('msfullscreenerror', errorHandler, false); - } - - this.fullscreenEventTarget_ = null; - this.fullscreenChangeHandler_ = null; - this.fullscreenErrorHandler_ = null; -}; - -VRDisplay.prototype.beginPresent_ = function() { - // Override to add custom behavior when presentation begins. -}; - -VRDisplay.prototype.endPresent_ = function() { - // Override to add custom behavior when presentation ends. -}; - -VRDisplay.prototype.submitFrame = function(pose) { - // Override to add custom behavior for frame submission. -}; - -VRDisplay.prototype.getEyeParameters = function(whichEye) { - // Override to return accurate eye parameters if canPresent is true. - return null; -}; - -/* - * Deprecated classes - */ - -/** - * The base class for all VR devices. (Deprecated) - */ -function VRDevice() { - this.isPolyfilled = true; - this.hardwareUnitId = 'webvr-polyfill hardwareUnitId'; - this.deviceId = 'webvr-polyfill deviceId'; - this.deviceName = 'webvr-polyfill deviceName'; -} - -/** - * The base class for all VR HMD devices. (Deprecated) - */ -function HMDVRDevice() { -} -HMDVRDevice.prototype = new VRDevice(); - -/** - * The base class for all VR position sensor devices. (Deprecated) - */ -function PositionSensorVRDevice() { -} -PositionSensorVRDevice.prototype = new VRDevice(); - -module.exports.VRFrameData = VRFrameData; -module.exports.VRDisplay = VRDisplay; -module.exports.VRDevice = VRDevice; -module.exports.HMDVRDevice = HMDVRDevice; -module.exports.PositionSensorVRDevice = PositionSensorVRDevice; - -},{"./util.js":26,"./wakelock.js":28}],7:[function(require,module,exports){ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var CardboardUI = require('./cardboard-ui.js'); -var Util = require('./util.js'); -var WGLUPreserveGLState = require('./deps/wglu-preserve-state.js'); - -var distortionVS = [ - 'attribute vec2 position;', - 'attribute vec3 texCoord;', - - 'varying vec2 vTexCoord;', - - 'uniform vec4 viewportOffsetScale[2];', - - 'void main() {', - ' vec4 viewport = viewportOffsetScale[int(texCoord.z)];', - ' vTexCoord = (texCoord.xy * viewport.zw) + viewport.xy;', - ' gl_Position = vec4( position, 1.0, 1.0 );', - '}', -].join('\n'); - -var distortionFS = [ - 'precision mediump float;', - 'uniform sampler2D diffuse;', - - 'varying vec2 vTexCoord;', - - 'void main() {', - ' gl_FragColor = texture2D(diffuse, vTexCoord);', - '}', -].join('\n'); - -/** - * A mesh-based distorter. - */ -function CardboardDistorter(gl) { - this.gl = gl; - this.ctxAttribs = gl.getContextAttributes(); - - this.meshWidth = 20; - this.meshHeight = 20; - - this.bufferScale = window.WebVRConfig.BUFFER_SCALE; - - this.bufferWidth = gl.drawingBufferWidth; - this.bufferHeight = gl.drawingBufferHeight; - - // Patching support - this.realBindFramebuffer = gl.bindFramebuffer; - this.realEnable = gl.enable; - this.realDisable = gl.disable; - this.realColorMask = gl.colorMask; - this.realClearColor = gl.clearColor; - this.realViewport = gl.viewport; - - if (!Util.isIOS()) { - this.realCanvasWidth = Object.getOwnPropertyDescriptor(gl.canvas.__proto__, 'width'); - this.realCanvasHeight = Object.getOwnPropertyDescriptor(gl.canvas.__proto__, 'height'); - } - - this.isPatched = false; - - // State tracking - this.lastBoundFramebuffer = null; - this.cullFace = false; - this.depthTest = false; - this.blend = false; - this.scissorTest = false; - this.stencilTest = false; - this.viewport = [0, 0, 0, 0]; - this.colorMask = [true, true, true, true]; - this.clearColor = [0, 0, 0, 0]; - - this.attribs = { - position: 0, - texCoord: 1 - }; - this.program = Util.linkProgram(gl, distortionVS, distortionFS, this.attribs); - this.uniforms = Util.getProgramUniforms(gl, this.program); - - this.viewportOffsetScale = new Float32Array(8); - this.setTextureBounds(); - - this.vertexBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - this.indexCount = 0; - - this.renderTarget = gl.createTexture(); - this.framebuffer = gl.createFramebuffer(); - - this.depthStencilBuffer = null; - this.depthBuffer = null; - this.stencilBuffer = null; - - if (this.ctxAttribs.depth && this.ctxAttribs.stencil) { - this.depthStencilBuffer = gl.createRenderbuffer(); - } else if (this.ctxAttribs.depth) { - this.depthBuffer = gl.createRenderbuffer(); - } else if (this.ctxAttribs.stencil) { - this.stencilBuffer = gl.createRenderbuffer(); - } - - this.patch(); - - this.onResize(); - - if (!window.WebVRConfig.CARDBOARD_UI_DISABLED) { - this.cardboardUI = new CardboardUI(gl); - } -}; - -/** - * Tears down all the resources created by the distorter and removes any - * patches. - */ -CardboardDistorter.prototype.destroy = function() { - var gl = this.gl; - - this.unpatch(); - - gl.deleteProgram(this.program); - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.indexBuffer); - gl.deleteTexture(this.renderTarget); - gl.deleteFramebuffer(this.framebuffer); - if (this.depthStencilBuffer) { - gl.deleteRenderbuffer(this.depthStencilBuffer); - } - if (this.depthBuffer) { - gl.deleteRenderbuffer(this.depthBuffer); - } - if (this.stencilBuffer) { - gl.deleteRenderbuffer(this.stencilBuffer); - } - - if (this.cardboardUI) { - this.cardboardUI.destroy(); - } -}; - - -/** - * Resizes the backbuffer to match the canvas width and height. - */ -CardboardDistorter.prototype.onResize = function() { - var gl = this.gl; - var self = this; - - var glState = [ - gl.RENDERBUFFER_BINDING, - gl.TEXTURE_BINDING_2D, gl.TEXTURE0 - ]; - - WGLUPreserveGLState(gl, glState, function(gl) { - // Bind real backbuffer and clear it once. We don't need to clear it again - // after that because we're overwriting the same area every frame. - self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, null); - - // Put things in a good state - if (self.scissorTest) { self.realDisable.call(gl, gl.SCISSOR_TEST); } - self.realColorMask.call(gl, true, true, true, true); - self.realViewport.call(gl, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); - self.realClearColor.call(gl, 0, 0, 0, 1); - - gl.clear(gl.COLOR_BUFFER_BIT); - - // Now bind and resize the fake backbuffer - self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.framebuffer); - - gl.bindTexture(gl.TEXTURE_2D, self.renderTarget); - gl.texImage2D(gl.TEXTURE_2D, 0, self.ctxAttribs.alpha ? gl.RGBA : gl.RGB, - self.bufferWidth, self.bufferHeight, 0, - self.ctxAttribs.alpha ? gl.RGBA : gl.RGB, gl.UNSIGNED_BYTE, null); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, self.renderTarget, 0); - - if (self.ctxAttribs.depth && self.ctxAttribs.stencil) { - gl.bindRenderbuffer(gl.RENDERBUFFER, self.depthStencilBuffer); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, - self.bufferWidth, self.bufferHeight); - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, - gl.RENDERBUFFER, self.depthStencilBuffer); - } else if (self.ctxAttribs.depth) { - gl.bindRenderbuffer(gl.RENDERBUFFER, self.depthBuffer); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, - self.bufferWidth, self.bufferHeight); - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, - gl.RENDERBUFFER, self.depthBuffer); - } else if (self.ctxAttribs.stencil) { - gl.bindRenderbuffer(gl.RENDERBUFFER, self.stencilBuffer); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, - self.bufferWidth, self.bufferHeight); - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, - gl.RENDERBUFFER, self.stencilBuffer); - } - - if (!gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE) { - console.error('Framebuffer incomplete!'); - } - - self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.lastBoundFramebuffer); - - if (self.scissorTest) { self.realEnable.call(gl, gl.SCISSOR_TEST); } - - self.realColorMask.apply(gl, self.colorMask); - self.realViewport.apply(gl, self.viewport); - self.realClearColor.apply(gl, self.clearColor); - }); - - if (this.cardboardUI) { - this.cardboardUI.onResize(); - } -}; - -CardboardDistorter.prototype.patch = function() { - if (this.isPatched) { - return; - } - - var self = this; - var canvas = this.gl.canvas; - var gl = this.gl; - - if (!Util.isIOS()) { - canvas.width = Util.getScreenWidth() * this.bufferScale; - canvas.height = Util.getScreenHeight() * this.bufferScale; - - Object.defineProperty(canvas, 'width', { - configurable: true, - enumerable: true, - get: function() { - return self.bufferWidth; - }, - set: function(value) { - self.bufferWidth = value; - self.realCanvasWidth.set.call(canvas, value); - self.onResize(); - } - }); - - Object.defineProperty(canvas, 'height', { - configurable: true, - enumerable: true, - get: function() { - return self.bufferHeight; - }, - set: function(value) { - self.bufferHeight = value; - self.realCanvasHeight.set.call(canvas, value); - self.onResize(); - } - }); - } - - this.lastBoundFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING); - - if (this.lastBoundFramebuffer == null) { - this.lastBoundFramebuffer = this.framebuffer; - this.gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); - } - - this.gl.bindFramebuffer = function(target, framebuffer) { - self.lastBoundFramebuffer = framebuffer ? framebuffer : self.framebuffer; - // Silently make calls to bind the default framebuffer bind ours instead. - self.realBindFramebuffer.call(gl, target, self.lastBoundFramebuffer); - }; - - this.cullFace = gl.getParameter(gl.CULL_FACE); - this.depthTest = gl.getParameter(gl.DEPTH_TEST); - this.blend = gl.getParameter(gl.BLEND); - this.scissorTest = gl.getParameter(gl.SCISSOR_TEST); - this.stencilTest = gl.getParameter(gl.STENCIL_TEST); - - gl.enable = function(pname) { - switch (pname) { - case gl.CULL_FACE: self.cullFace = true; break; - case gl.DEPTH_TEST: self.depthTest = true; break; - case gl.BLEND: self.blend = true; break; - case gl.SCISSOR_TEST: self.scissorTest = true; break; - case gl.STENCIL_TEST: self.stencilTest = true; break; - } - self.realEnable.call(gl, pname); - }; - - gl.disable = function(pname) { - switch (pname) { - case gl.CULL_FACE: self.cullFace = false; break; - case gl.DEPTH_TEST: self.depthTest = false; break; - case gl.BLEND: self.blend = false; break; - case gl.SCISSOR_TEST: self.scissorTest = false; break; - case gl.STENCIL_TEST: self.stencilTest = false; break; - } - self.realDisable.call(gl, pname); - }; - - this.colorMask = gl.getParameter(gl.COLOR_WRITEMASK); - gl.colorMask = function(r, g, b, a) { - self.colorMask[0] = r; - self.colorMask[1] = g; - self.colorMask[2] = b; - self.colorMask[3] = a; - self.realColorMask.call(gl, r, g, b, a); - }; - - this.clearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE); - gl.clearColor = function(r, g, b, a) { - self.clearColor[0] = r; - self.clearColor[1] = g; - self.clearColor[2] = b; - self.clearColor[3] = a; - self.realClearColor.call(gl, r, g, b, a); - }; - - this.viewport = gl.getParameter(gl.VIEWPORT); - gl.viewport = function(x, y, w, h) { - self.viewport[0] = x; - self.viewport[1] = y; - self.viewport[2] = w; - self.viewport[3] = h; - self.realViewport.call(gl, x, y, w, h); - }; - - this.isPatched = true; - Util.safariCssSizeWorkaround(canvas); -}; - -CardboardDistorter.prototype.unpatch = function() { - if (!this.isPatched) { - return; - } - - var gl = this.gl; - var canvas = this.gl.canvas; - - if (!Util.isIOS()) { - Object.defineProperty(canvas, 'width', this.realCanvasWidth); - Object.defineProperty(canvas, 'height', this.realCanvasHeight); - } - canvas.width = this.bufferWidth; - canvas.height = this.bufferHeight; - - gl.bindFramebuffer = this.realBindFramebuffer; - gl.enable = this.realEnable; - gl.disable = this.realDisable; - gl.colorMask = this.realColorMask; - gl.clearColor = this.realClearColor; - gl.viewport = this.realViewport; - - // Check to see if our fake backbuffer is bound and bind the real backbuffer - // if that's the case. - if (this.lastBoundFramebuffer == this.framebuffer) { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - } - - this.isPatched = false; - - setTimeout(function() { - Util.safariCssSizeWorkaround(canvas); - }, 1); -}; - -CardboardDistorter.prototype.setTextureBounds = function(leftBounds, rightBounds) { - if (!leftBounds) { - leftBounds = [0, 0, 0.5, 1]; - } - - if (!rightBounds) { - rightBounds = [0.5, 0, 0.5, 1]; - } - - // Left eye - this.viewportOffsetScale[0] = leftBounds[0]; // X - this.viewportOffsetScale[1] = leftBounds[1]; // Y - this.viewportOffsetScale[2] = leftBounds[2]; // Width - this.viewportOffsetScale[3] = leftBounds[3]; // Height - - // Right eye - this.viewportOffsetScale[4] = rightBounds[0]; // X - this.viewportOffsetScale[5] = rightBounds[1]; // Y - this.viewportOffsetScale[6] = rightBounds[2]; // Width - this.viewportOffsetScale[7] = rightBounds[3]; // Height -}; - -/** - * Performs distortion pass on the injected backbuffer, rendering it to the real - * backbuffer. - */ -CardboardDistorter.prototype.submitFrame = function() { - var gl = this.gl; - var self = this; - - var glState = []; - - if (!window.WebVRConfig.DIRTY_SUBMIT_FRAME_BINDINGS) { - glState.push( - gl.CURRENT_PROGRAM, - gl.ARRAY_BUFFER_BINDING, - gl.ELEMENT_ARRAY_BUFFER_BINDING, - gl.TEXTURE_BINDING_2D, gl.TEXTURE0 - ); - } - - WGLUPreserveGLState(gl, glState, function(gl) { - // Bind the real default framebuffer - self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, null); - - // Make sure the GL state is in a good place - if (self.cullFace) { self.realDisable.call(gl, gl.CULL_FACE); } - if (self.depthTest) { self.realDisable.call(gl, gl.DEPTH_TEST); } - if (self.blend) { self.realDisable.call(gl, gl.BLEND); } - if (self.scissorTest) { self.realDisable.call(gl, gl.SCISSOR_TEST); } - if (self.stencilTest) { self.realDisable.call(gl, gl.STENCIL_TEST); } - self.realColorMask.call(gl, true, true, true, true); - self.realViewport.call(gl, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); - - // If the backbuffer has an alpha channel clear every frame so the page - // doesn't show through. - if (self.ctxAttribs.alpha || Util.isIOS()) { - self.realClearColor.call(gl, 0, 0, 0, 1); - gl.clear(gl.COLOR_BUFFER_BIT); - } - - // Bind distortion program and mesh - gl.useProgram(self.program); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.indexBuffer); - - gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer); - gl.enableVertexAttribArray(self.attribs.position); - gl.enableVertexAttribArray(self.attribs.texCoord); - gl.vertexAttribPointer(self.attribs.position, 2, gl.FLOAT, false, 20, 0); - gl.vertexAttribPointer(self.attribs.texCoord, 3, gl.FLOAT, false, 20, 8); - - gl.activeTexture(gl.TEXTURE0); - gl.uniform1i(self.uniforms.diffuse, 0); - gl.bindTexture(gl.TEXTURE_2D, self.renderTarget); - - gl.uniform4fv(self.uniforms.viewportOffsetScale, self.viewportOffsetScale); - - // Draws both eyes - gl.drawElements(gl.TRIANGLES, self.indexCount, gl.UNSIGNED_SHORT, 0); - - if (self.cardboardUI) { - self.cardboardUI.renderNoState(); - } - - // Bind the fake default framebuffer again - self.realBindFramebuffer.call(self.gl, gl.FRAMEBUFFER, self.framebuffer); - - // If preserveDrawingBuffer == false clear the framebuffer - if (!self.ctxAttribs.preserveDrawingBuffer) { - self.realClearColor.call(gl, 0, 0, 0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - } - - if (!window.WebVRConfig.DIRTY_SUBMIT_FRAME_BINDINGS) { - self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.lastBoundFramebuffer); - } - - // Restore state - if (self.cullFace) { self.realEnable.call(gl, gl.CULL_FACE); } - if (self.depthTest) { self.realEnable.call(gl, gl.DEPTH_TEST); } - if (self.blend) { self.realEnable.call(gl, gl.BLEND); } - if (self.scissorTest) { self.realEnable.call(gl, gl.SCISSOR_TEST); } - if (self.stencilTest) { self.realEnable.call(gl, gl.STENCIL_TEST); } - - self.realColorMask.apply(gl, self.colorMask); - self.realViewport.apply(gl, self.viewport); - if (self.ctxAttribs.alpha || !self.ctxAttribs.preserveDrawingBuffer) { - self.realClearColor.apply(gl, self.clearColor); - } - }); - - // Workaround for the fact that Safari doesn't allow us to patch the canvas - // width and height correctly. After each submit frame check to see what the - // real backbuffer size has been set to and resize the fake backbuffer size - // to match. - if (Util.isIOS()) { - var canvas = gl.canvas; - if (canvas.width != self.bufferWidth || canvas.height != self.bufferHeight) { - self.bufferWidth = canvas.width; - self.bufferHeight = canvas.height; - self.onResize(); - } - } -}; - -/** - * Call when the deviceInfo has changed. At this point we need - * to re-calculate the distortion mesh. - */ -CardboardDistorter.prototype.updateDeviceInfo = function(deviceInfo) { - var gl = this.gl; - var self = this; - - var glState = [gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING]; - WGLUPreserveGLState(gl, glState, function(gl) { - var vertices = self.computeMeshVertices_(self.meshWidth, self.meshHeight, deviceInfo); - gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); - - // Indices don't change based on device parameters, so only compute once. - if (!self.indexCount) { - var indices = self.computeMeshIndices_(self.meshWidth, self.meshHeight); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); - self.indexCount = indices.length; - } - }); -}; - -/** - * Build the distortion mesh vertices. - * Based on code from the Unity cardboard plugin. - */ -CardboardDistorter.prototype.computeMeshVertices_ = function(width, height, deviceInfo) { - var vertices = new Float32Array(2 * width * height * 5); - - var lensFrustum = deviceInfo.getLeftEyeVisibleTanAngles(); - var noLensFrustum = deviceInfo.getLeftEyeNoLensTanAngles(); - var viewport = deviceInfo.getLeftEyeVisibleScreenRect(noLensFrustum); - var vidx = 0; - var iidx = 0; - for (var e = 0; e < 2; e++) { - for (var j = 0; j < height; j++) { - for (var i = 0; i < width; i++, vidx++) { - var u = i / (width - 1); - var v = j / (height - 1); - - // Grid points regularly spaced in StreoScreen, and barrel distorted in - // the mesh. - var s = u; - var t = v; - var x = Util.lerp(lensFrustum[0], lensFrustum[2], u); - var y = Util.lerp(lensFrustum[3], lensFrustum[1], v); - var d = Math.sqrt(x * x + y * y); - var r = deviceInfo.distortion.distortInverse(d); - var p = x * r / d; - var q = y * r / d; - u = (p - noLensFrustum[0]) / (noLensFrustum[2] - noLensFrustum[0]); - v = (q - noLensFrustum[3]) / (noLensFrustum[1] - noLensFrustum[3]); - - // Convert u,v to mesh screen coordinates. - var aspect = deviceInfo.device.widthMeters / deviceInfo.device.heightMeters; - - // FIXME: The original Unity plugin multiplied U by the aspect ratio - // and didn't multiply either value by 2, but that seems to get it - // really close to correct looking for me. I hate this kind of "Don't - // know why it works" code though, and wold love a more logical - // explanation of what needs to happen here. - u = (viewport.x + u * viewport.width - 0.5) * 2.0; //* aspect; - v = (viewport.y + v * viewport.height - 0.5) * 2.0; - - vertices[(vidx * 5) + 0] = u; // position.x - vertices[(vidx * 5) + 1] = v; // position.y - vertices[(vidx * 5) + 2] = s; // texCoord.x - vertices[(vidx * 5) + 3] = t; // texCoord.y - vertices[(vidx * 5) + 4] = e; // texCoord.z (viewport index) - } - } - var w = lensFrustum[2] - lensFrustum[0]; - lensFrustum[0] = -(w + lensFrustum[0]); - lensFrustum[2] = w - lensFrustum[2]; - w = noLensFrustum[2] - noLensFrustum[0]; - noLensFrustum[0] = -(w + noLensFrustum[0]); - noLensFrustum[2] = w - noLensFrustum[2]; - viewport.x = 1 - (viewport.x + viewport.width); - } - return vertices; -} - -/** - * Build the distortion mesh indices. - * Based on code from the Unity cardboard plugin. - */ -CardboardDistorter.prototype.computeMeshIndices_ = function(width, height) { - var indices = new Uint16Array(2 * (width - 1) * (height - 1) * 6); - var halfwidth = width / 2; - var halfheight = height / 2; - var vidx = 0; - var iidx = 0; - for (var e = 0; e < 2; e++) { - for (var j = 0; j < height; j++) { - for (var i = 0; i < width; i++, vidx++) { - if (i == 0 || j == 0) - continue; - // Build a quad. Lower right and upper left quadrants have quads with - // the triangle diagonal flipped to get the vignette to interpolate - // correctly. - if ((i <= halfwidth) == (j <= halfheight)) { - // Quad diagonal lower left to upper right. - indices[iidx++] = vidx; - indices[iidx++] = vidx - width - 1; - indices[iidx++] = vidx - width; - indices[iidx++] = vidx - width - 1; - indices[iidx++] = vidx; - indices[iidx++] = vidx - 1; - } else { - // Quad diagonal upper left to lower right. - indices[iidx++] = vidx - 1; - indices[iidx++] = vidx - width; - indices[iidx++] = vidx; - indices[iidx++] = vidx - width; - indices[iidx++] = vidx - 1; - indices[iidx++] = vidx - width - 1; - } - } - } - } - return indices; -}; - -CardboardDistorter.prototype.getOwnPropertyDescriptor_ = function(proto, attrName) { - var descriptor = Object.getOwnPropertyDescriptor(proto, attrName); - // In some cases (ahem... Safari), the descriptor returns undefined get and - // set fields. In this case, we need to create a synthetic property - // descriptor. This works around some of the issues in - // https://github.com/borismus/webvr-polyfill/issues/46 - if (descriptor.get === undefined || descriptor.set === undefined) { - descriptor.configurable = true; - descriptor.enumerable = true; - descriptor.get = function() { - return this.getAttribute(attrName); - }; - descriptor.set = function(val) { - this.setAttribute(attrName, val); - }; - } - return descriptor; -}; - -module.exports = CardboardDistorter; - -},{"./cardboard-ui.js":8,"./deps/wglu-preserve-state.js":10,"./util.js":26}],8:[function(require,module,exports){ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var Util = require('./util.js'); -var WGLUPreserveGLState = require('./deps/wglu-preserve-state.js'); - -var uiVS = [ - 'attribute vec2 position;', - - 'uniform mat4 projectionMat;', - - 'void main() {', - ' gl_Position = projectionMat * vec4( position, -1.0, 1.0 );', - '}', -].join('\n'); - -var uiFS = [ - 'precision mediump float;', - - 'uniform vec4 color;', - - 'void main() {', - ' gl_FragColor = color;', - '}', -].join('\n'); - -var DEG2RAD = Math.PI/180.0; - -// The gear has 6 identical sections, each spanning 60 degrees. -var kAnglePerGearSection = 60; - -// Half-angle of the span of the outer rim. -var kOuterRimEndAngle = 12; - -// Angle between the middle of the outer rim and the start of the inner rim. -var kInnerRimBeginAngle = 20; - -// Distance from center to outer rim, normalized so that the entire model -// fits in a [-1, 1] x [-1, 1] square. -var kOuterRadius = 1; - -// Distance from center to depressed rim, in model units. -var kMiddleRadius = 0.75; - -// Radius of the inner hollow circle, in model units. -var kInnerRadius = 0.3125; - -// Center line thickness in DP. -var kCenterLineThicknessDp = 4; - -// Button width in DP. -var kButtonWidthDp = 28; - -// Factor to scale the touch area that responds to the touch. -var kTouchSlopFactor = 1.5; - -var Angles = [ - 0, kOuterRimEndAngle, kInnerRimBeginAngle, - kAnglePerGearSection - kInnerRimBeginAngle, - kAnglePerGearSection - kOuterRimEndAngle -]; - -/** - * Renders the alignment line and "options" gear. It is assumed that the canvas - * this is rendered into covers the entire screen (or close to it.) - */ -function CardboardUI(gl) { - this.gl = gl; - - this.attribs = { - position: 0 - }; - this.program = Util.linkProgram(gl, uiVS, uiFS, this.attribs); - this.uniforms = Util.getProgramUniforms(gl, this.program); - - this.vertexBuffer = gl.createBuffer(); - this.gearOffset = 0; - this.gearVertexCount = 0; - this.arrowOffset = 0; - this.arrowVertexCount = 0; - - this.projMat = new Float32Array(16); - - this.listener = null; - - this.onResize(); -}; - -/** - * Tears down all the resources created by the UI renderer. - */ -CardboardUI.prototype.destroy = function() { - var gl = this.gl; - - if (this.listener) { - gl.canvas.removeEventListener('click', this.listener, false); - } - - gl.deleteProgram(this.program); - gl.deleteBuffer(this.vertexBuffer); -}; - -/** - * Adds a listener to clicks on the gear and back icons - */ -CardboardUI.prototype.listen = function(optionsCallback, backCallback) { - var canvas = this.gl.canvas; - this.listener = function(event) { - var midline = canvas.clientWidth / 2; - var buttonSize = kButtonWidthDp * kTouchSlopFactor; - // Check to see if the user clicked on (or around) the gear icon - if (event.clientX > midline - buttonSize && - event.clientX < midline + buttonSize && - event.clientY > canvas.clientHeight - buttonSize) { - optionsCallback(event); - } - // Check to see if the user clicked on (or around) the back icon - else if (event.clientX < buttonSize && event.clientY < buttonSize) { - backCallback(event); - } - }; - canvas.addEventListener('click', this.listener, false); -}; - -/** - * Builds the UI mesh. - */ -CardboardUI.prototype.onResize = function() { - var gl = this.gl; - var self = this; - - var glState = [ - gl.ARRAY_BUFFER_BINDING - ]; - - WGLUPreserveGLState(gl, glState, function(gl) { - var vertices = []; - - var midline = gl.drawingBufferWidth / 2; - - // The gl buffer size will likely be smaller than the physical pixel count. - // So we need to scale the dps down based on the actual buffer size vs physical pixel count. - // This will properly size the ui elements no matter what the gl buffer resolution is - var physicalPixels = Math.max(screen.width, screen.height) * window.devicePixelRatio; - var scalingRatio = gl.drawingBufferWidth / physicalPixels; - var dps = scalingRatio * window.devicePixelRatio; - - var lineWidth = kCenterLineThicknessDp * dps / 2; - var buttonSize = kButtonWidthDp * kTouchSlopFactor * dps; - var buttonScale = kButtonWidthDp * dps / 2; - var buttonBorder = ((kButtonWidthDp * kTouchSlopFactor) - kButtonWidthDp) * dps; - - // Build centerline - vertices.push(midline - lineWidth, buttonSize); - vertices.push(midline - lineWidth, gl.drawingBufferHeight); - vertices.push(midline + lineWidth, buttonSize); - vertices.push(midline + lineWidth, gl.drawingBufferHeight); - - // Build gear - self.gearOffset = (vertices.length / 2); - - function addGearSegment(theta, r) { - var angle = (90 - theta) * DEG2RAD; - var x = Math.cos(angle); - var y = Math.sin(angle); - vertices.push(kInnerRadius * x * buttonScale + midline, kInnerRadius * y * buttonScale + buttonScale); - vertices.push(r * x * buttonScale + midline, r * y * buttonScale + buttonScale); - } - - for (var i = 0; i <= 6; i++) { - var segmentTheta = i * kAnglePerGearSection; - - addGearSegment(segmentTheta, kOuterRadius); - addGearSegment(segmentTheta + kOuterRimEndAngle, kOuterRadius); - addGearSegment(segmentTheta + kInnerRimBeginAngle, kMiddleRadius); - addGearSegment(segmentTheta + (kAnglePerGearSection - kInnerRimBeginAngle), kMiddleRadius); - addGearSegment(segmentTheta + (kAnglePerGearSection - kOuterRimEndAngle), kOuterRadius); - } - - self.gearVertexCount = (vertices.length / 2) - self.gearOffset; - - // Build back arrow - self.arrowOffset = (vertices.length / 2); - - function addArrowVertex(x, y) { - vertices.push(buttonBorder + x, gl.drawingBufferHeight - buttonBorder - y); - } - - var angledLineWidth = lineWidth / Math.sin(45 * DEG2RAD); - - addArrowVertex(0, buttonScale); - addArrowVertex(buttonScale, 0); - addArrowVertex(buttonScale + angledLineWidth, angledLineWidth); - addArrowVertex(angledLineWidth, buttonScale + angledLineWidth); - - addArrowVertex(angledLineWidth, buttonScale - angledLineWidth); - addArrowVertex(0, buttonScale); - addArrowVertex(buttonScale, buttonScale * 2); - addArrowVertex(buttonScale + angledLineWidth, (buttonScale * 2) - angledLineWidth); - - addArrowVertex(angledLineWidth, buttonScale - angledLineWidth); - addArrowVertex(0, buttonScale); - - addArrowVertex(angledLineWidth, buttonScale - lineWidth); - addArrowVertex(kButtonWidthDp * dps, buttonScale - lineWidth); - addArrowVertex(angledLineWidth, buttonScale + lineWidth); - addArrowVertex(kButtonWidthDp * dps, buttonScale + lineWidth); - - self.arrowVertexCount = (vertices.length / 2) - self.arrowOffset; - - // Buffer data - gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); - }); -}; - -/** - * Performs distortion pass on the injected backbuffer, rendering it to the real - * backbuffer. - */ -CardboardUI.prototype.render = function() { - var gl = this.gl; - var self = this; - - var glState = [ - gl.CULL_FACE, - gl.DEPTH_TEST, - gl.BLEND, - gl.SCISSOR_TEST, - gl.STENCIL_TEST, - gl.COLOR_WRITEMASK, - gl.VIEWPORT, - - gl.CURRENT_PROGRAM, - gl.ARRAY_BUFFER_BINDING - ]; - - WGLUPreserveGLState(gl, glState, function(gl) { - // Make sure the GL state is in a good place - gl.disable(gl.CULL_FACE); - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - gl.disable(gl.SCISSOR_TEST); - gl.disable(gl.STENCIL_TEST); - gl.colorMask(true, true, true, true); - gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); - - self.renderNoState(); - }); -}; - -CardboardUI.prototype.renderNoState = function() { - var gl = this.gl; - - // Bind distortion program and mesh - gl.useProgram(this.program); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.enableVertexAttribArray(this.attribs.position); - gl.vertexAttribPointer(this.attribs.position, 2, gl.FLOAT, false, 8, 0); - - gl.uniform4f(this.uniforms.color, 1.0, 1.0, 1.0, 1.0); - - Util.orthoMatrix(this.projMat, 0, gl.drawingBufferWidth, 0, gl.drawingBufferHeight, 0.1, 1024.0); - gl.uniformMatrix4fv(this.uniforms.projectionMat, false, this.projMat); - - // Draws UI element - gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); - gl.drawArrays(gl.TRIANGLE_STRIP, this.gearOffset, this.gearVertexCount); - gl.drawArrays(gl.TRIANGLE_STRIP, this.arrowOffset, this.arrowVertexCount); -}; - -module.exports = CardboardUI; - -},{"./deps/wglu-preserve-state.js":10,"./util.js":26}],9:[function(require,module,exports){ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var CardboardDistorter = require('./cardboard-distorter.js'); -var CardboardUI = require('./cardboard-ui.js'); -var DeviceInfo = require('./device-info.js'); -var Dpdb = require('./dpdb/dpdb.js'); -var FusionPoseSensor = require('./sensor-fusion/fusion-pose-sensor.js'); -var RotateInstructions = require('./rotate-instructions.js'); -var ViewerSelector = require('./viewer-selector.js'); -var VRDisplay = require('./base.js').VRDisplay; -var Util = require('./util.js'); - -var Eye = { - LEFT: 'left', - RIGHT: 'right' -}; - -/** - * VRDisplay based on mobile device parameters and DeviceMotion APIs. - */ -function CardboardVRDisplay() { - this.displayName = 'Cardboard VRDisplay (webvr-polyfill)'; - - this.capabilities.hasOrientation = true; - this.capabilities.canPresent = true; - - // "Private" members. - this.bufferScale_ = window.WebVRConfig.BUFFER_SCALE; - this.poseSensor_ = new FusionPoseSensor(); - this.distorter_ = null; - this.cardboardUI_ = null; - - this.dpdb_ = new Dpdb(true, this.onDeviceParamsUpdated_.bind(this)); - this.deviceInfo_ = new DeviceInfo(this.dpdb_.getDeviceParams()); - - this.viewerSelector_ = new ViewerSelector(); - this.viewerSelector_.onChange(this.onViewerChanged_.bind(this)); - - // Set the correct initial viewer. - this.deviceInfo_.setViewer(this.viewerSelector_.getCurrentViewer()); - - if (!window.WebVRConfig.ROTATE_INSTRUCTIONS_DISABLED) { - this.rotateInstructions_ = new RotateInstructions(); - } - - if (Util.isIOS()) { - // Listen for resize events to workaround this awful Safari bug. - window.addEventListener('resize', this.onResize_.bind(this)); - } -} -CardboardVRDisplay.prototype = new VRDisplay(); - -CardboardVRDisplay.prototype.getImmediatePose = function() { - return { - position: this.poseSensor_.getPosition(), - orientation: this.poseSensor_.getOrientation(), - linearVelocity: null, - linearAcceleration: null, - angularVelocity: null, - angularAcceleration: null - }; -}; - -CardboardVRDisplay.prototype.resetPose = function() { - this.poseSensor_.resetPose(); -}; - -CardboardVRDisplay.prototype.getEyeParameters = function(whichEye) { - var offset = [this.deviceInfo_.viewer.interLensDistance * 0.5, 0.0, 0.0]; - var fieldOfView; - - // TODO: FoV can be a little expensive to compute. Cache when device params change. - if (whichEye == Eye.LEFT) { - offset[0] *= -1.0; - fieldOfView = this.deviceInfo_.getFieldOfViewLeftEye(); - } else if (whichEye == Eye.RIGHT) { - fieldOfView = this.deviceInfo_.getFieldOfViewRightEye(); - } else { - console.error('Invalid eye provided: %s', whichEye); - return null; - } - - return { - fieldOfView: fieldOfView, - offset: offset, - // TODO: Should be able to provide better values than these. - renderWidth: this.deviceInfo_.device.width * 0.5 * this.bufferScale_, - renderHeight: this.deviceInfo_.device.height * this.bufferScale_, - }; -}; - -CardboardVRDisplay.prototype.onDeviceParamsUpdated_ = function(newParams) { - if (Util.isDebug()) { - console.log('DPDB reported that device params were updated.'); - } - this.deviceInfo_.updateDeviceParams(newParams); - - if (this.distorter_) { - this.distorter_.updateDeviceInfo(this.deviceInfo_); - } -}; - -CardboardVRDisplay.prototype.updateBounds_ = function () { - if (this.layer_ && this.distorter_ && (this.layer_.leftBounds || this.layer_.rightBounds)) { - this.distorter_.setTextureBounds(this.layer_.leftBounds, this.layer_.rightBounds); - } -}; - -CardboardVRDisplay.prototype.beginPresent_ = function() { - var gl = this.layer_.source.getContext('webgl'); - if (!gl) - gl = this.layer_.source.getContext('experimental-webgl'); - if (!gl) - gl = this.layer_.source.getContext('webgl2'); - - if (!gl) - return; // Can't do distortion without a WebGL context. - - // Provides a way to opt out of distortion - if (this.layer_.predistorted) { - if (!window.WebVRConfig.CARDBOARD_UI_DISABLED) { - gl.canvas.width = Util.getScreenWidth() * this.bufferScale_; - gl.canvas.height = Util.getScreenHeight() * this.bufferScale_; - this.cardboardUI_ = new CardboardUI(gl); - } - } else { - // Create a new distorter for the target context - this.distorter_ = new CardboardDistorter(gl); - this.distorter_.updateDeviceInfo(this.deviceInfo_); - this.cardboardUI_ = this.distorter_.cardboardUI; - } - - if (this.cardboardUI_) { - this.cardboardUI_.listen(function(e) { - // Options clicked. - this.viewerSelector_.show(this.layer_.source.parentElement); - e.stopPropagation(); - e.preventDefault(); - }.bind(this), function(e) { - // Back clicked. - this.exitPresent(); - e.stopPropagation(); - e.preventDefault(); - }.bind(this)); - } - - if (this.rotateInstructions_) { - if (Util.isLandscapeMode() && Util.isMobile()) { - // In landscape mode, temporarily show the "put into Cardboard" - // interstitial. Otherwise, do the default thing. - this.rotateInstructions_.showTemporarily(3000, this.layer_.source.parentElement); - } else { - this.rotateInstructions_.update(); - } - } - - // Listen for orientation change events in order to show interstitial. - this.orientationHandler = this.onOrientationChange_.bind(this); - window.addEventListener('orientationchange', this.orientationHandler); - - // Listen for present display change events in order to update distorter dimensions - this.vrdisplaypresentchangeHandler = this.updateBounds_.bind(this); - window.addEventListener('vrdisplaypresentchange', this.vrdisplaypresentchangeHandler); - - // Fire this event initially, to give geometry-distortion clients the chance - // to do something custom. - this.fireVRDisplayDeviceParamsChange_(); -}; - -CardboardVRDisplay.prototype.endPresent_ = function() { - if (this.distorter_) { - this.distorter_.destroy(); - this.distorter_ = null; - } - if (this.cardboardUI_) { - this.cardboardUI_.destroy(); - this.cardboardUI_ = null; - } - - if (this.rotateInstructions_) { - this.rotateInstructions_.hide(); - } - this.viewerSelector_.hide(); - - window.removeEventListener('orientationchange', this.orientationHandler); - window.removeEventListener('vrdisplaypresentchange', this.vrdisplaypresentchangeHandler); -}; - -CardboardVRDisplay.prototype.submitFrame = function(pose) { - if (this.distorter_) { - this.updateBounds_(); - this.distorter_.submitFrame(); - } else if (this.cardboardUI_ && this.layer_) { - // Hack for predistorted: true. - var canvas = this.layer_.source.getContext('webgl').canvas; - if (canvas.width != this.lastWidth || canvas.height != this.lastHeight) { - this.cardboardUI_.onResize(); - } - this.lastWidth = canvas.width; - this.lastHeight = canvas.height; - - // Render the Cardboard UI. - this.cardboardUI_.render(); - } -}; - -CardboardVRDisplay.prototype.onOrientationChange_ = function(e) { - // Hide the viewer selector. - this.viewerSelector_.hide(); - - // Update the rotate instructions. - if (this.rotateInstructions_) { - this.rotateInstructions_.update(); - } - - this.onResize_(); -}; - -CardboardVRDisplay.prototype.onResize_ = function(e) { - if (this.layer_) { - var gl = this.layer_.source.getContext('webgl'); - // Size the CSS canvas. - // Added padding on right and bottom because iPhone 5 will not - // hide the URL bar unless content is bigger than the screen. - // This will not be visible as long as the container element (e.g. body) - // is set to 'overflow: hidden'. - // Additionally, 'box-sizing: content-box' ensures renderWidth = width + padding. - // This is required when 'box-sizing: border-box' is used elsewhere in the page. - var cssProperties = [ - 'position: absolute', - 'top: 0', - 'left: 0', - 'width: ' + Math.max(screen.width, screen.height) + 'px', - 'height: ' + Math.min(screen.height, screen.width) + 'px', - 'border: 0', - 'margin: 0', - 'padding: 0 10px 10px 0', - 'box-sizing: content-box', - ]; - gl.canvas.setAttribute('style', cssProperties.join('; ') + ';'); - - Util.safariCssSizeWorkaround(gl.canvas); - } -}; - -CardboardVRDisplay.prototype.onViewerChanged_ = function(viewer) { - this.deviceInfo_.setViewer(viewer); - - if (this.distorter_) { - // Update the distortion appropriately. - this.distorter_.updateDeviceInfo(this.deviceInfo_); - } - - // Fire a new event containing viewer and device parameters for clients that - // want to implement their own geometry-based distortion. - this.fireVRDisplayDeviceParamsChange_(); -}; - -CardboardVRDisplay.prototype.fireVRDisplayDeviceParamsChange_ = function() { - var event = new CustomEvent('vrdisplaydeviceparamschange', { - detail: { - vrdisplay: this, - deviceInfo: this.deviceInfo_, - } - }); - window.dispatchEvent(event); -}; - -module.exports = CardboardVRDisplay; - -},{"./base.js":6,"./cardboard-distorter.js":7,"./cardboard-ui.js":8,"./device-info.js":11,"./dpdb/dpdb.js":15,"./rotate-instructions.js":20,"./sensor-fusion/fusion-pose-sensor.js":22,"./util.js":26,"./viewer-selector.js":27}],10:[function(require,module,exports){ -/** - * Copyright (c) 2016, Brandon Jones. - * https://github.com/toji/webgl-utils/blob/master/src/wglu-preserve-state.js - * LICENSE: https://github.com/toji/webgl-utils/blob/master/LICENSE.md - */ - -function WGLUPreserveGLState(gl, bindings, callback) { - if (!bindings) { - callback(gl); - return; - } - - var boundValues = []; - - var activeTexture = null; - for (var i = 0; i < bindings.length; ++i) { - var binding = bindings[i]; - switch (binding) { - case gl.TEXTURE_BINDING_2D: - case gl.TEXTURE_BINDING_CUBE_MAP: - var textureUnit = bindings[++i]; - if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) { - console.error("TEXTURE_BINDING_2D or TEXTURE_BINDING_CUBE_MAP must be followed by a valid texture unit"); - boundValues.push(null, null); - break; - } - if (!activeTexture) { - activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE); - } - gl.activeTexture(textureUnit); - boundValues.push(gl.getParameter(binding), null); - break; - case gl.ACTIVE_TEXTURE: - activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE); - boundValues.push(null); - break; - default: - boundValues.push(gl.getParameter(binding)); - break; - } - } - - callback(gl); - - for (var i = 0; i < bindings.length; ++i) { - var binding = bindings[i]; - var boundValue = boundValues[i]; - switch (binding) { - case gl.ACTIVE_TEXTURE: - break; // Ignore this binding, since we special-case it to happen last. - case gl.ARRAY_BUFFER_BINDING: - gl.bindBuffer(gl.ARRAY_BUFFER, boundValue); - break; - case gl.COLOR_CLEAR_VALUE: - gl.clearColor(boundValue[0], boundValue[1], boundValue[2], boundValue[3]); - break; - case gl.COLOR_WRITEMASK: - gl.colorMask(boundValue[0], boundValue[1], boundValue[2], boundValue[3]); - break; - case gl.CURRENT_PROGRAM: - gl.useProgram(boundValue); - break; - case gl.ELEMENT_ARRAY_BUFFER_BINDING: - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, boundValue); - break; - case gl.FRAMEBUFFER_BINDING: - gl.bindFramebuffer(gl.FRAMEBUFFER, boundValue); - break; - case gl.RENDERBUFFER_BINDING: - gl.bindRenderbuffer(gl.RENDERBUFFER, boundValue); - break; - case gl.TEXTURE_BINDING_2D: - var textureUnit = bindings[++i]; - if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) - break; - gl.activeTexture(textureUnit); - gl.bindTexture(gl.TEXTURE_2D, boundValue); - break; - case gl.TEXTURE_BINDING_CUBE_MAP: - var textureUnit = bindings[++i]; - if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) - break; - gl.activeTexture(textureUnit); - gl.bindTexture(gl.TEXTURE_CUBE_MAP, boundValue); - break; - case gl.VIEWPORT: - gl.viewport(boundValue[0], boundValue[1], boundValue[2], boundValue[3]); - break; - case gl.BLEND: - case gl.CULL_FACE: - case gl.DEPTH_TEST: - case gl.SCISSOR_TEST: - case gl.STENCIL_TEST: - if (boundValue) { - gl.enable(binding); - } else { - gl.disable(binding); - } - break; - default: - console.log("No GL restore behavior for 0x" + binding.toString(16)); - break; - } - - if (activeTexture) { - gl.activeTexture(activeTexture); - } - } -} - -module.exports = WGLUPreserveGLState; - -},{}],11:[function(require,module,exports){ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var Distortion = require('./distortion/distortion.js'); -var MathUtil = require('./math-util.js'); -var Util = require('./util.js'); - -function Device(params) { - this.width = params.width || Util.getScreenWidth(); - this.height = params.height || Util.getScreenHeight(); - this.widthMeters = params.widthMeters; - this.heightMeters = params.heightMeters; - this.bevelMeters = params.bevelMeters; -} - - -// Fallback Android device (based on Nexus 5 measurements) for use when -// we can't recognize an Android device. -var DEFAULT_ANDROID = new Device({ - widthMeters: 0.110, - heightMeters: 0.062, - bevelMeters: 0.004 -}); - -// Fallback iOS device (based on iPhone6) for use when -// we can't recognize an Android device. -var DEFAULT_IOS = new Device({ - widthMeters: 0.1038, - heightMeters: 0.0584, - bevelMeters: 0.004 -}); - - -var Viewers = { - CardboardV1: new CardboardViewer({ - id: 'CardboardV1', - label: 'Cardboard I/O 2014', - fov: 40, - interLensDistance: 0.060, - baselineLensDistance: 0.035, - screenLensDistance: 0.042, - distortionCoefficients: [0.441, 0.156], - inverseCoefficients: [-0.4410035, 0.42756155, -0.4804439, 0.5460139, - -0.58821183, 0.5733938, -0.48303202, 0.33299083, -0.17573841, - 0.0651772, -0.01488963, 0.001559834] - }), - CardboardV2: new CardboardViewer({ - id: 'CardboardV2', - label: 'Cardboard I/O 2015', - fov: 60, - interLensDistance: 0.064, - baselineLensDistance: 0.035, - screenLensDistance: 0.039, - distortionCoefficients: [0.34, 0.55], - inverseCoefficients: [-0.33836704, -0.18162185, 0.862655, -1.2462051, - 1.0560602, -0.58208317, 0.21609078, -0.05444823, 0.009177956, - -9.904169E-4, 6.183535E-5, -1.6981803E-6] - }) -}; - - -var DEFAULT_LEFT_CENTER = {x: 0.5, y: 0.5}; -var DEFAULT_RIGHT_CENTER = {x: 0.5, y: 0.5}; - -/** - * Manages information about the device and the viewer. - * - * deviceParams indicates the parameters of the device to use (generally - * obtained from dpdb.getDeviceParams()). Can be null to mean no device - * params were found. - */ -function DeviceInfo(deviceParams) { - this.viewer = Viewers.CardboardV2; - this.updateDeviceParams(deviceParams); - this.distortion = new Distortion(this.viewer.distortionCoefficients); -} - -DeviceInfo.prototype.updateDeviceParams = function(deviceParams) { - this.device = this.determineDevice_(deviceParams) || this.device; -}; - -DeviceInfo.prototype.getDevice = function() { - return this.device; -}; - -DeviceInfo.prototype.setViewer = function(viewer) { - this.viewer = viewer; - this.distortion = new Distortion(this.viewer.distortionCoefficients); -}; - -DeviceInfo.prototype.determineDevice_ = function(deviceParams) { - if (!deviceParams) { - // No parameters, so use a default. - if (Util.isIOS()) { - console.warn('Using fallback iOS device measurements.'); - return DEFAULT_IOS; - } else { - console.warn('Using fallback Android device measurements.'); - return DEFAULT_ANDROID; - } - } - - // Compute device screen dimensions based on deviceParams. - var METERS_PER_INCH = 0.0254; - var metersPerPixelX = METERS_PER_INCH / deviceParams.xdpi; - var metersPerPixelY = METERS_PER_INCH / deviceParams.ydpi; - var width = Util.getScreenWidth(); - var height = Util.getScreenHeight(); - return new Device({ - widthMeters: metersPerPixelX * width, - heightMeters: metersPerPixelY * height, - bevelMeters: deviceParams.bevelMm * 0.001, - }); -}; - -/** - * Calculates field of view for the left eye. - */ -DeviceInfo.prototype.getDistortedFieldOfViewLeftEye = function() { - var viewer = this.viewer; - var device = this.device; - var distortion = this.distortion; - - // Device.height and device.width for device in portrait mode, so transpose. - var eyeToScreenDistance = viewer.screenLensDistance; - - var outerDist = (device.widthMeters - viewer.interLensDistance) / 2; - var innerDist = viewer.interLensDistance / 2; - var bottomDist = viewer.baselineLensDistance - device.bevelMeters; - var topDist = device.heightMeters - bottomDist; - - var outerAngle = MathUtil.radToDeg * Math.atan( - distortion.distort(outerDist / eyeToScreenDistance)); - var innerAngle = MathUtil.radToDeg * Math.atan( - distortion.distort(innerDist / eyeToScreenDistance)); - var bottomAngle = MathUtil.radToDeg * Math.atan( - distortion.distort(bottomDist / eyeToScreenDistance)); - var topAngle = MathUtil.radToDeg * Math.atan( - distortion.distort(topDist / eyeToScreenDistance)); - - return { - leftDegrees: Math.min(outerAngle, viewer.fov), - rightDegrees: Math.min(innerAngle, viewer.fov), - downDegrees: Math.min(bottomAngle, viewer.fov), - upDegrees: Math.min(topAngle, viewer.fov) - }; -}; - -/** - * Calculates the tan-angles from the maximum FOV for the left eye for the - * current device and screen parameters. - */ -DeviceInfo.prototype.getLeftEyeVisibleTanAngles = function() { - var viewer = this.viewer; - var device = this.device; - var distortion = this.distortion; - - // Tan-angles from the max FOV. - var fovLeft = Math.tan(-MathUtil.degToRad * viewer.fov); - var fovTop = Math.tan(MathUtil.degToRad * viewer.fov); - var fovRight = Math.tan(MathUtil.degToRad * viewer.fov); - var fovBottom = Math.tan(-MathUtil.degToRad * viewer.fov); - // Viewport size. - var halfWidth = device.widthMeters / 4; - var halfHeight = device.heightMeters / 2; - // Viewport center, measured from left lens position. - var verticalLensOffset = (viewer.baselineLensDistance - device.bevelMeters - halfHeight); - var centerX = viewer.interLensDistance / 2 - halfWidth; - var centerY = -verticalLensOffset; - var centerZ = viewer.screenLensDistance; - // Tan-angles of the viewport edges, as seen through the lens. - var screenLeft = distortion.distort((centerX - halfWidth) / centerZ); - var screenTop = distortion.distort((centerY + halfHeight) / centerZ); - var screenRight = distortion.distort((centerX + halfWidth) / centerZ); - var screenBottom = distortion.distort((centerY - halfHeight) / centerZ); - // Compare the two sets of tan-angles and take the value closer to zero on each side. - var result = new Float32Array(4); - result[0] = Math.max(fovLeft, screenLeft); - result[1] = Math.min(fovTop, screenTop); - result[2] = Math.min(fovRight, screenRight); - result[3] = Math.max(fovBottom, screenBottom); - return result; -}; - -/** - * Calculates the tan-angles from the maximum FOV for the left eye for the - * current device and screen parameters, assuming no lenses. - */ -DeviceInfo.prototype.getLeftEyeNoLensTanAngles = function() { - var viewer = this.viewer; - var device = this.device; - var distortion = this.distortion; - - var result = new Float32Array(4); - // Tan-angles from the max FOV. - var fovLeft = distortion.distortInverse(Math.tan(-MathUtil.degToRad * viewer.fov)); - var fovTop = distortion.distortInverse(Math.tan(MathUtil.degToRad * viewer.fov)); - var fovRight = distortion.distortInverse(Math.tan(MathUtil.degToRad * viewer.fov)); - var fovBottom = distortion.distortInverse(Math.tan(-MathUtil.degToRad * viewer.fov)); - // Viewport size. - var halfWidth = device.widthMeters / 4; - var halfHeight = device.heightMeters / 2; - // Viewport center, measured from left lens position. - var verticalLensOffset = (viewer.baselineLensDistance - device.bevelMeters - halfHeight); - var centerX = viewer.interLensDistance / 2 - halfWidth; - var centerY = -verticalLensOffset; - var centerZ = viewer.screenLensDistance; - // Tan-angles of the viewport edges, as seen through the lens. - var screenLeft = (centerX - halfWidth) / centerZ; - var screenTop = (centerY + halfHeight) / centerZ; - var screenRight = (centerX + halfWidth) / centerZ; - var screenBottom = (centerY - halfHeight) / centerZ; - // Compare the two sets of tan-angles and take the value closer to zero on each side. - result[0] = Math.max(fovLeft, screenLeft); - result[1] = Math.min(fovTop, screenTop); - result[2] = Math.min(fovRight, screenRight); - result[3] = Math.max(fovBottom, screenBottom); - return result; -}; - -/** - * Calculates the screen rectangle visible from the left eye for the - * current device and screen parameters. - */ -DeviceInfo.prototype.getLeftEyeVisibleScreenRect = function(undistortedFrustum) { - var viewer = this.viewer; - var device = this.device; - - var dist = viewer.screenLensDistance; - var eyeX = (device.widthMeters - viewer.interLensDistance) / 2; - var eyeY = viewer.baselineLensDistance - device.bevelMeters; - var left = (undistortedFrustum[0] * dist + eyeX) / device.widthMeters; - var top = (undistortedFrustum[1] * dist + eyeY) / device.heightMeters; - var right = (undistortedFrustum[2] * dist + eyeX) / device.widthMeters; - var bottom = (undistortedFrustum[3] * dist + eyeY) / device.heightMeters; - return { - x: left, - y: bottom, - width: right - left, - height: top - bottom - }; -}; - -DeviceInfo.prototype.getFieldOfViewLeftEye = function(opt_isUndistorted) { - return opt_isUndistorted ? this.getUndistortedFieldOfViewLeftEye() : - this.getDistortedFieldOfViewLeftEye(); -}; - -DeviceInfo.prototype.getFieldOfViewRightEye = function(opt_isUndistorted) { - var fov = this.getFieldOfViewLeftEye(opt_isUndistorted); - return { - leftDegrees: fov.rightDegrees, - rightDegrees: fov.leftDegrees, - upDegrees: fov.upDegrees, - downDegrees: fov.downDegrees - }; -}; - -/** - * Calculates undistorted field of view for the left eye. - */ -DeviceInfo.prototype.getUndistortedFieldOfViewLeftEye = function() { - var p = this.getUndistortedParams_(); - - return { - leftDegrees: MathUtil.radToDeg * Math.atan(p.outerDist), - rightDegrees: MathUtil.radToDeg * Math.atan(p.innerDist), - downDegrees: MathUtil.radToDeg * Math.atan(p.bottomDist), - upDegrees: MathUtil.radToDeg * Math.atan(p.topDist) - }; -}; - -DeviceInfo.prototype.getUndistortedViewportLeftEye = function() { - var p = this.getUndistortedParams_(); - var viewer = this.viewer; - var device = this.device; - - // Distances stored in local variables are in tan-angle units unless otherwise - // noted. - var eyeToScreenDistance = viewer.screenLensDistance; - var screenWidth = device.widthMeters / eyeToScreenDistance; - var screenHeight = device.heightMeters / eyeToScreenDistance; - var xPxPerTanAngle = device.width / screenWidth; - var yPxPerTanAngle = device.height / screenHeight; - - var x = Math.round((p.eyePosX - p.outerDist) * xPxPerTanAngle); - var y = Math.round((p.eyePosY - p.bottomDist) * yPxPerTanAngle); - return { - x: x, - y: y, - width: Math.round((p.eyePosX + p.innerDist) * xPxPerTanAngle) - x, - height: Math.round((p.eyePosY + p.topDist) * yPxPerTanAngle) - y - }; -}; - -DeviceInfo.prototype.getUndistortedParams_ = function() { - var viewer = this.viewer; - var device = this.device; - var distortion = this.distortion; - - // Most of these variables in tan-angle units. - var eyeToScreenDistance = viewer.screenLensDistance; - var halfLensDistance = viewer.interLensDistance / 2 / eyeToScreenDistance; - var screenWidth = device.widthMeters / eyeToScreenDistance; - var screenHeight = device.heightMeters / eyeToScreenDistance; - - var eyePosX = screenWidth / 2 - halfLensDistance; - var eyePosY = (viewer.baselineLensDistance - device.bevelMeters) / eyeToScreenDistance; - - var maxFov = viewer.fov; - var viewerMax = distortion.distortInverse(Math.tan(MathUtil.degToRad * maxFov)); - var outerDist = Math.min(eyePosX, viewerMax); - var innerDist = Math.min(halfLensDistance, viewerMax); - var bottomDist = Math.min(eyePosY, viewerMax); - var topDist = Math.min(screenHeight - eyePosY, viewerMax); - - return { - outerDist: outerDist, - innerDist: innerDist, - topDist: topDist, - bottomDist: bottomDist, - eyePosX: eyePosX, - eyePosY: eyePosY - }; -}; - - -function CardboardViewer(params) { - // A machine readable ID. - this.id = params.id; - // A human readable label. - this.label = params.label; - - // Field of view in degrees (per side). - this.fov = params.fov; - - // Distance between lens centers in meters. - this.interLensDistance = params.interLensDistance; - // Distance between viewer baseline and lens center in meters. - this.baselineLensDistance = params.baselineLensDistance; - // Screen-to-lens distance in meters. - this.screenLensDistance = params.screenLensDistance; - - // Distortion coefficients. - this.distortionCoefficients = params.distortionCoefficients; - // Inverse distortion coefficients. - // TODO: Calculate these from distortionCoefficients in the future. - this.inverseCoefficients = params.inverseCoefficients; -} - -// Export viewer information. -DeviceInfo.Viewers = Viewers; -module.exports = DeviceInfo; - -},{"./distortion/distortion.js":13,"./math-util.js":17,"./util.js":26}],12:[function(require,module,exports){ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var VRDisplay = require('./base.js').VRDisplay; -var HMDVRDevice = require('./base.js').HMDVRDevice; -var PositionSensorVRDevice = require('./base.js').PositionSensorVRDevice; - -/** - * Wraps a VRDisplay and exposes it as a HMDVRDevice - */ -function VRDisplayHMDDevice(display) { - this.display = display; - - this.hardwareUnitId = display.displayId; - this.deviceId = 'webvr-polyfill:HMD:' + display.displayId; - this.deviceName = display.displayName + ' (HMD)'; -} -VRDisplayHMDDevice.prototype = new HMDVRDevice(); - -VRDisplayHMDDevice.prototype.getEyeParameters = function(whichEye) { - var eyeParameters = this.display.getEyeParameters(whichEye); - - return { - currentFieldOfView: eyeParameters.fieldOfView, - maximumFieldOfView: eyeParameters.fieldOfView, - minimumFieldOfView: eyeParameters.fieldOfView, - recommendedFieldOfView: eyeParameters.fieldOfView, - eyeTranslation: { x: eyeParameters.offset[0], y: eyeParameters.offset[1], z: eyeParameters.offset[2] }, - renderRect: { - x: (whichEye == 'right') ? eyeParameters.renderWidth : 0, - y: 0, - width: eyeParameters.renderWidth, - height: eyeParameters.renderHeight - } - }; -}; - -VRDisplayHMDDevice.prototype.setFieldOfView = - function(opt_fovLeft, opt_fovRight, opt_zNear, opt_zFar) { - // Not supported. getEyeParameters reports that the min, max, and recommended - // FoV is all the same, so no adjustment can be made. -}; - -// TODO: Need to hook requestFullscreen to see if a wrapped VRDisplay was passed -// in as an option. If so we should prevent the default fullscreen behavior and -// call VRDisplay.requestPresent instead. - -/** - * Wraps a VRDisplay and exposes it as a PositionSensorVRDevice - */ -function VRDisplayPositionSensorDevice(display) { - this.display = display; - - this.hardwareUnitId = display.displayId; - this.deviceId = 'webvr-polyfill:PositionSensor: ' + display.displayId; - this.deviceName = display.displayName + ' (PositionSensor)'; -} -VRDisplayPositionSensorDevice.prototype = new PositionSensorVRDevice(); - -VRDisplayPositionSensorDevice.prototype.getState = function() { - var pose = this.display.getPose(); - return { - position: pose.position ? { x: pose.position[0], y: pose.position[1], z: pose.position[2] } : null, - orientation: pose.orientation ? { x: pose.orientation[0], y: pose.orientation[1], z: pose.orientation[2], w: pose.orientation[3] } : null, - linearVelocity: null, - linearAcceleration: null, - angularVelocity: null, - angularAcceleration: null - }; -}; - -VRDisplayPositionSensorDevice.prototype.resetState = function() { - return this.positionDevice.resetPose(); -}; - - -module.exports.VRDisplayHMDDevice = VRDisplayHMDDevice; -module.exports.VRDisplayPositionSensorDevice = VRDisplayPositionSensorDevice; - - -},{"./base.js":6}],13:[function(require,module,exports){ -/** - * TODO(smus): Implement coefficient inversion. - */ -function Distortion(coefficients) { - this.coefficients = coefficients; -} - -/** - * Calculates the inverse distortion for a radius. - *

- * Allows to compute the original undistorted radius from a distorted one. - * See also getApproximateInverseDistortion() for a faster but potentially - * less accurate method. - * - * @param {Number} radius Distorted radius from the lens center in tan-angle units. - * @return {Number} The undistorted radius in tan-angle units. - */ -Distortion.prototype.distortInverse = function(radius) { - // Secant method. - var r0 = 0; - var r1 = 1; - var dr0 = radius - this.distort(r0); - while (Math.abs(r1 - r0) > 0.0001 /** 0.1mm */) { - var dr1 = radius - this.distort(r1); - var r2 = r1 - dr1 * ((r1 - r0) / (dr1 - dr0)); - r0 = r1; - r1 = r2; - dr0 = dr1; - } - return r1; -}; - -/** - * Distorts a radius by its distortion factor from the center of the lenses. - * - * @param {Number} radius Radius from the lens center in tan-angle units. - * @return {Number} The distorted radius in tan-angle units. - */ -Distortion.prototype.distort = function(radius) { - var r2 = radius * radius; - var ret = 0; - for (var i = 0; i < this.coefficients.length; i++) { - ret = r2 * (ret + this.coefficients[i]); - } - return (ret + 1) * radius; -}; - -module.exports = Distortion; - -},{}],14:[function(require,module,exports){ -module.exports={ - "format": 1, - "last_updated": "2017-06-01T22:33:42Z", - "devices": [ - { - "type": "android", - "rules": [ - { - "mdmh": "asus/*/Nexus 7/*" - }, - { - "ua": "Nexus 7" - } - ], - "dpi": [ - 320.8, - 323 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "asus/*/ASUS_Z00AD/*" - }, - { - "ua": "ASUS_Z00AD" - } - ], - "dpi": [ - 403, - 404.6 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "Google/*/Pixel XL/*" - }, - { - "ua": "Pixel XL" - } - ], - "dpi": [ - 537.9, - 533 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "Google/*/Pixel/*" - }, - { - "ua": "Pixel" - } - ], - "dpi": [ - 432.6, - 436.7 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "HTC/*/HTC6435LVW/*" - }, - { - "ua": "HTC6435LVW" - } - ], - "dpi": [ - 449.7, - 443.3 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "HTC/*/HTC One XL/*" - }, - { - "ua": "HTC One XL" - } - ], - "dpi": [ - 315.3, - 314.6 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "htc/*/Nexus 9/*" - }, - { - "ua": "Nexus 9" - } - ], - "dpi": 289, - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "HTC/*/HTC One M9/*" - }, - { - "ua": "HTC One M9" - } - ], - "dpi": [ - 442.5, - 443.3 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "HTC/*/HTC One_M8/*" - }, - { - "ua": "HTC One_M8" - } - ], - "dpi": [ - 449.7, - 447.4 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "HTC/*/HTC One/*" - }, - { - "ua": "HTC One" - } - ], - "dpi": 472.8, - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "Huawei/*/Nexus 6P/*" - }, - { - "ua": "Nexus 6P" - } - ], - "dpi": [ - 515.1, - 518 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "LGE/*/Nexus 5X/*" - }, - { - "ua": "Nexus 5X" - } - ], - "dpi": [ - 422, - 419.9 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "LGE/*/LGMS345/*" - }, - { - "ua": "LGMS345" - } - ], - "dpi": [ - 221.7, - 219.1 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "LGE/*/LG-D800/*" - }, - { - "ua": "LG-D800" - } - ], - "dpi": [ - 422, - 424.1 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "LGE/*/LG-D850/*" - }, - { - "ua": "LG-D850" - } - ], - "dpi": [ - 537.9, - 541.9 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "LGE/*/VS985 4G/*" - }, - { - "ua": "VS985 4G" - } - ], - "dpi": [ - 537.9, - 535.6 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "LGE/*/Nexus 5/*" - }, - { - "ua": "Nexus 5 B" - } - ], - "dpi": [ - 442.4, - 444.8 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "LGE/*/Nexus 4/*" - }, - { - "ua": "Nexus 4" - } - ], - "dpi": [ - 319.8, - 318.4 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "LGE/*/LG-P769/*" - }, - { - "ua": "LG-P769" - } - ], - "dpi": [ - 240.6, - 247.5 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "LGE/*/LGMS323/*" - }, - { - "ua": "LGMS323" - } - ], - "dpi": [ - 206.6, - 204.6 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "LGE/*/LGLS996/*" - }, - { - "ua": "LGLS996" - } - ], - "dpi": [ - 403.4, - 401.5 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "Micromax/*/4560MMX/*" - }, - { - "ua": "4560MMX" - } - ], - "dpi": [ - 240, - 219.4 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "Micromax/*/A250/*" - }, - { - "ua": "Micromax A250" - } - ], - "dpi": [ - 480, - 446.4 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "Micromax/*/Micromax AQ4501/*" - }, - { - "ua": "Micromax AQ4501" - } - ], - "dpi": 240, - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "motorola/*/DROID RAZR/*" - }, - { - "ua": "DROID RAZR" - } - ], - "dpi": [ - 368.1, - 256.7 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "motorola/*/XT830C/*" - }, - { - "ua": "XT830C" - } - ], - "dpi": [ - 254, - 255.9 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "motorola/*/XT1021/*" - }, - { - "ua": "XT1021" - } - ], - "dpi": [ - 254, - 256.7 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "motorola/*/XT1023/*" - }, - { - "ua": "XT1023" - } - ], - "dpi": [ - 254, - 256.7 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "motorola/*/XT1028/*" - }, - { - "ua": "XT1028" - } - ], - "dpi": [ - 326.6, - 327.6 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "motorola/*/XT1034/*" - }, - { - "ua": "XT1034" - } - ], - "dpi": [ - 326.6, - 328.4 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "motorola/*/XT1053/*" - }, - { - "ua": "XT1053" - } - ], - "dpi": [ - 315.3, - 316.1 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "motorola/*/XT1562/*" - }, - { - "ua": "XT1562" - } - ], - "dpi": [ - 403.4, - 402.7 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "motorola/*/Nexus 6/*" - }, - { - "ua": "Nexus 6 B" - } - ], - "dpi": [ - 494.3, - 489.7 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "motorola/*/XT1063/*" - }, - { - "ua": "XT1063" - } - ], - "dpi": [ - 295, - 296.6 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "motorola/*/XT1064/*" - }, - { - "ua": "XT1064" - } - ], - "dpi": [ - 295, - 295.6 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "motorola/*/XT1092/*" - }, - { - "ua": "XT1092" - } - ], - "dpi": [ - 422, - 424.1 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "motorola/*/XT1095/*" - }, - { - "ua": "XT1095" - } - ], - "dpi": [ - 422, - 423.4 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "motorola/*/G4/*" - }, - { - "ua": "Moto G (4)" - } - ], - "dpi": 401, - "bw": 4, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "OnePlus/*/A0001/*" - }, - { - "ua": "A0001" - } - ], - "dpi": [ - 403.4, - 401 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "OnePlus/*/ONE E1005/*" - }, - { - "ua": "ONE E1005" - } - ], - "dpi": [ - 442.4, - 441.4 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "OnePlus/*/ONE A2005/*" - }, - { - "ua": "ONE A2005" - } - ], - "dpi": [ - 391.9, - 405.4 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "OPPO/*/X909/*" - }, - { - "ua": "X909" - } - ], - "dpi": [ - 442.4, - 444.1 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/GT-I9082/*" - }, - { - "ua": "GT-I9082" - } - ], - "dpi": [ - 184.7, - 185.4 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-G360P/*" - }, - { - "ua": "SM-G360P" - } - ], - "dpi": [ - 196.7, - 205.4 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/Nexus S/*" - }, - { - "ua": "Nexus S" - } - ], - "dpi": [ - 234.5, - 229.8 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/GT-I9300/*" - }, - { - "ua": "GT-I9300" - } - ], - "dpi": [ - 304.8, - 303.9 - ], - "bw": 5, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-T230NU/*" - }, - { - "ua": "SM-T230NU" - } - ], - "dpi": 216, - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SGH-T399/*" - }, - { - "ua": "SGH-T399" - } - ], - "dpi": [ - 217.7, - 231.4 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SGH-M919/*" - }, - { - "ua": "SGH-M919" - } - ], - "dpi": [ - 440.8, - 437.7 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-N9005/*" - }, - { - "ua": "SM-N9005" - } - ], - "dpi": [ - 386.4, - 387 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SAMSUNG-SM-N900A/*" - }, - { - "ua": "SAMSUNG-SM-N900A" - } - ], - "dpi": [ - 386.4, - 387.7 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/GT-I9500/*" - }, - { - "ua": "GT-I9500" - } - ], - "dpi": [ - 442.5, - 443.3 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/GT-I9505/*" - }, - { - "ua": "GT-I9505" - } - ], - "dpi": 439.4, - "bw": 4, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-G900F/*" - }, - { - "ua": "SM-G900F" - } - ], - "dpi": [ - 415.6, - 431.6 - ], - "bw": 5, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-G900M/*" - }, - { - "ua": "SM-G900M" - } - ], - "dpi": [ - 415.6, - 431.6 - ], - "bw": 5, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-G800F/*" - }, - { - "ua": "SM-G800F" - } - ], - "dpi": 326.8, - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-G906S/*" - }, - { - "ua": "SM-G906S" - } - ], - "dpi": [ - 562.7, - 572.4 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/GT-I9300/*" - }, - { - "ua": "GT-I9300" - } - ], - "dpi": [ - 306.7, - 304.8 - ], - "bw": 5, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-T535/*" - }, - { - "ua": "SM-T535" - } - ], - "dpi": [ - 142.6, - 136.4 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-N920C/*" - }, - { - "ua": "SM-N920C" - } - ], - "dpi": [ - 515.1, - 518.4 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-N920W8/*" - }, - { - "ua": "SM-N920W8" - } - ], - "dpi": [ - 515.1, - 518.4 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/GT-I9300I/*" - }, - { - "ua": "GT-I9300I" - } - ], - "dpi": [ - 304.8, - 305.8 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/GT-I9195/*" - }, - { - "ua": "GT-I9195" - } - ], - "dpi": [ - 249.4, - 256.7 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SPH-L520/*" - }, - { - "ua": "SPH-L520" - } - ], - "dpi": [ - 249.4, - 255.9 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SAMSUNG-SGH-I717/*" - }, - { - "ua": "SAMSUNG-SGH-I717" - } - ], - "dpi": 285.8, - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SPH-D710/*" - }, - { - "ua": "SPH-D710" - } - ], - "dpi": [ - 217.7, - 204.2 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/GT-N7100/*" - }, - { - "ua": "GT-N7100" - } - ], - "dpi": 265.1, - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SCH-I605/*" - }, - { - "ua": "SCH-I605" - } - ], - "dpi": 265.1, - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/Galaxy Nexus/*" - }, - { - "ua": "Galaxy Nexus" - } - ], - "dpi": [ - 315.3, - 314.2 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-N910H/*" - }, - { - "ua": "SM-N910H" - } - ], - "dpi": [ - 515.1, - 518 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-N910C/*" - }, - { - "ua": "SM-N910C" - } - ], - "dpi": [ - 515.2, - 520.2 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-G130M/*" - }, - { - "ua": "SM-G130M" - } - ], - "dpi": [ - 165.9, - 164.8 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-G928I/*" - }, - { - "ua": "SM-G928I" - } - ], - "dpi": [ - 515.1, - 518.4 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-G920F/*" - }, - { - "ua": "SM-G920F" - } - ], - "dpi": 580.6, - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-G920P/*" - }, - { - "ua": "SM-G920P" - } - ], - "dpi": [ - 522.5, - 577 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-G925F/*" - }, - { - "ua": "SM-G925F" - } - ], - "dpi": 580.6, - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-G925V/*" - }, - { - "ua": "SM-G925V" - } - ], - "dpi": [ - 522.5, - 576.6 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-G930F/*" - }, - { - "ua": "SM-G930F" - } - ], - "dpi": 576.6, - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "samsung/*/SM-G935F/*" - }, - { - "ua": "SM-G935F" - } - ], - "dpi": 533, - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "Sony/*/C6903/*" - }, - { - "ua": "C6903" - } - ], - "dpi": [ - 442.5, - 443.3 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "Sony/*/D6653/*" - }, - { - "ua": "D6653" - } - ], - "dpi": [ - 428.6, - 427.6 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "Sony/*/E6653/*" - }, - { - "ua": "E6653" - } - ], - "dpi": [ - 428.6, - 425.7 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "Sony/*/E6853/*" - }, - { - "ua": "E6853" - } - ], - "dpi": [ - 403.4, - 401.9 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "Sony/*/SGP321/*" - }, - { - "ua": "SGP321" - } - ], - "dpi": [ - 224.7, - 224.1 - ], - "bw": 3, - "ac": 500 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "TCT/*/ALCATEL ONE TOUCH Fierce/*" - }, - { - "ua": "ALCATEL ONE TOUCH Fierce" - } - ], - "dpi": [ - 240, - 247.5 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "THL/*/thl 5000/*" - }, - { - "ua": "thl 5000" - } - ], - "dpi": [ - 480, - 443.3 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "android", - "rules": [ - { - "mdmh": "ZTE/*/ZTE Blade L2/*" - }, - { - "ua": "ZTE Blade L2" - } - ], - "dpi": 240, - "bw": 3, - "ac": 500 - }, - { - "type": "ios", - "rules": [ - { - "res": [ - 640, - 960 - ] - } - ], - "dpi": [ - 325.1, - 328.4 - ], - "bw": 4, - "ac": 1000 - }, - { - "type": "ios", - "rules": [ - { - "res": [ - 640, - 1136 - ] - } - ], - "dpi": [ - 317.1, - 320.2 - ], - "bw": 3, - "ac": 1000 - }, - { - "type": "ios", - "rules": [ - { - "res": [ - 750, - 1334 - ] - } - ], - "dpi": 326.4, - "bw": 4, - "ac": 1000 - }, - { - "type": "ios", - "rules": [ - { - "res": [ - 1242, - 2208 - ] - } - ], - "dpi": [ - 453.6, - 458.4 - ], - "bw": 4, - "ac": 1000 - }, - { - "type": "ios", - "rules": [ - { - "res": [ - 1125, - 2001 - ] - } - ], - "dpi": [ - 410.9, - 415.4 - ], - "bw": 4, - "ac": 1000 - } - ] -} -},{}],15:[function(require,module,exports){ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Offline cache of the DPDB, to be used until we load the online one (and -// as a fallback in case we can't load the online one). -var DPDB_CACHE = require('./dpdb.json'); -var Util = require('../util.js'); - -// Online DPDB URL. -var ONLINE_DPDB_URL = - 'https://dpdb.webvr.rocks/dpdb.json'; - -/** - * Calculates device parameters based on the DPDB (Device Parameter Database). - * Initially, uses the cached DPDB values. - * - * If fetchOnline == true, then this object tries to fetch the online version - * of the DPDB and updates the device info if a better match is found. - * Calls the onDeviceParamsUpdated callback when there is an update to the - * device information. - */ -function Dpdb(fetchOnline, onDeviceParamsUpdated) { - // Start with the offline DPDB cache while we are loading the real one. - this.dpdb = DPDB_CACHE; - - // Calculate device params based on the offline version of the DPDB. - this.recalculateDeviceParams_(); - - // XHR to fetch online DPDB file, if requested. - if (fetchOnline) { - // Set the callback. - this.onDeviceParamsUpdated = onDeviceParamsUpdated; - - var xhr = new XMLHttpRequest(); - var obj = this; - xhr.open('GET', ONLINE_DPDB_URL, true); - xhr.addEventListener('load', function() { - obj.loading = false; - if (xhr.status >= 200 && xhr.status <= 299) { - // Success. - obj.dpdb = JSON.parse(xhr.response); - obj.recalculateDeviceParams_(); - } else { - // Error loading the DPDB. - console.error('Error loading online DPDB!'); - } - }); - xhr.send(); - } -} - -// Returns the current device parameters. -Dpdb.prototype.getDeviceParams = function() { - return this.deviceParams; -}; - -// Recalculates this device's parameters based on the DPDB. -Dpdb.prototype.recalculateDeviceParams_ = function() { - var newDeviceParams = this.calcDeviceParams_(); - if (newDeviceParams) { - this.deviceParams = newDeviceParams; - // Invoke callback, if it is set. - if (this.onDeviceParamsUpdated) { - this.onDeviceParamsUpdated(this.deviceParams); - } - } else { - console.error('Failed to recalculate device parameters.'); - } -}; - -// Returns a DeviceParams object that represents the best guess as to this -// device's parameters. Can return null if the device does not match any -// known devices. -Dpdb.prototype.calcDeviceParams_ = function() { - var db = this.dpdb; // shorthand - if (!db) { - console.error('DPDB not available.'); - return null; - } - if (db.format != 1) { - console.error('DPDB has unexpected format version.'); - return null; - } - if (!db.devices || !db.devices.length) { - console.error('DPDB does not have a devices section.'); - return null; - } - - // Get the actual user agent and screen dimensions in pixels. - var userAgent = navigator.userAgent || navigator.vendor || window.opera; - var width = Util.getScreenWidth(); - var height = Util.getScreenHeight(); - - if (!db.devices) { - console.error('DPDB has no devices section.'); - return null; - } - - for (var i = 0; i < db.devices.length; i++) { - var device = db.devices[i]; - if (!device.rules) { - console.warn('Device[' + i + '] has no rules section.'); - continue; - } - - if (device.type != 'ios' && device.type != 'android') { - console.warn('Device[' + i + '] has invalid type.'); - continue; - } - - // See if this device is of the appropriate type. - if (Util.isIOS() != (device.type == 'ios')) continue; - - // See if this device matches any of the rules: - var matched = false; - for (var j = 0; j < device.rules.length; j++) { - var rule = device.rules[j]; - if (this.matchRule_(rule, userAgent, width, height)) { - matched = true; - break; - } - } - if (!matched) continue; - - // device.dpi might be an array of [ xdpi, ydpi] or just a scalar. - var xdpi = device.dpi[0] || device.dpi; - var ydpi = device.dpi[1] || device.dpi; - - return new DeviceParams({ xdpi: xdpi, ydpi: ydpi, bevelMm: device.bw }); - } - - console.warn('No DPDB device match.'); - return null; -}; - -Dpdb.prototype.matchRule_ = function(rule, ua, screenWidth, screenHeight) { - // We can only match 'ua' and 'res' rules, not other types like 'mdmh' - // (which are meant for native platforms). - if (!rule.ua && !rule.res) return false; - - // If our user agent string doesn't contain the indicated user agent string, - // the match fails. - if (rule.ua && ua.indexOf(rule.ua) < 0) return false; - - // If the rule specifies screen dimensions that don't correspond to ours, - // the match fails. - if (rule.res) { - if (!rule.res[0] || !rule.res[1]) return false; - var resX = rule.res[0]; - var resY = rule.res[1]; - // Compare min and max so as to make the order not matter, i.e., it should - // be true that 640x480 == 480x640. - if (Math.min(screenWidth, screenHeight) != Math.min(resX, resY) || - (Math.max(screenWidth, screenHeight) != Math.max(resX, resY))) { - return false; - } - } - - return true; -} - -function DeviceParams(params) { - this.xdpi = params.xdpi; - this.ydpi = params.ydpi; - this.bevelMm = params.bevelMm; -} - -module.exports = Dpdb; - -},{"../util.js":26,"./dpdb.json":14}],16:[function(require,module,exports){ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var Util = require('./util.js'); -var WebVRPolyfill = require('./webvr-polyfill.js').WebVRPolyfill; - -// Initialize a WebVRConfig just in case. -window.WebVRConfig = Util.extend({ - // Forces availability of VR mode, even for non-mobile devices. - FORCE_ENABLE_VR: false, - - // Complementary filter coefficient. 0 for accelerometer, 1 for gyro. - K_FILTER: 0.98, - - // How far into the future to predict during fast motion (in seconds). - PREDICTION_TIME_S: 0.040, - - // Flag to enable touch panner. In case you have your own touch controls. - TOUCH_PANNER_DISABLED: true, - - // Flag to disabled the UI in VR Mode. - CARDBOARD_UI_DISABLED: false, // Default: false - - // Flag to disable the instructions to rotate your device. - ROTATE_INSTRUCTIONS_DISABLED: false, // Default: false. - - // Enable yaw panning only, disabling roll and pitch. This can be useful - // for panoramas with nothing interesting above or below. - YAW_ONLY: false, - - // To disable keyboard and mouse controls, if you want to use your own - // implementation. - MOUSE_KEYBOARD_CONTROLS_DISABLED: false, - - // Prevent the polyfill from initializing immediately. Requires the app - // to call InitializeWebVRPolyfill() before it can be used. - DEFER_INITIALIZATION: false, - - // Enable the deprecated version of the API (navigator.getVRDevices). - ENABLE_DEPRECATED_API: false, - - // Scales the recommended buffer size reported by WebVR, which can improve - // performance. - // UPDATE(2016-05-03): Setting this to 0.5 by default since 1.0 does not - // perform well on many mobile devices. - BUFFER_SCALE: 0.5, - - // Allow VRDisplay.submitFrame to change gl bindings, which is more - // efficient if the application code will re-bind its resources on the - // next frame anyway. This has been seen to cause rendering glitches with - // THREE.js. - // Dirty bindings include: gl.FRAMEBUFFER_BINDING, gl.CURRENT_PROGRAM, - // gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING, - // and gl.TEXTURE_BINDING_2D for texture unit 0. - DIRTY_SUBMIT_FRAME_BINDINGS: false, - - // When set to true, this will cause a polyfilled VRDisplay to always be - // appended to the list returned by navigator.getVRDisplays(), even if that - // list includes a native VRDisplay. - ALWAYS_APPEND_POLYFILL_DISPLAY: false, - - // There are versions of Chrome (M58-M60?) where the native WebVR API exists, - // and instead of returning 0 VR displays when none are detected, - // `navigator.getVRDisplays()`'s promise never resolves. This results - // in the polyfill hanging and not being able to provide fallback - // displays, so set a timeout in milliseconds to stop waiting for a response - // and just use polyfilled displays. - // https://bugs.chromium.org/p/chromium/issues/detail?id=727969 - GET_VR_DISPLAYS_TIMEOUT: 1000, -}, window.WebVRConfig); - -if (!window.WebVRConfig.DEFER_INITIALIZATION) { - new WebVRPolyfill(); -} else { - window.InitializeWebVRPolyfill = function() { - new WebVRPolyfill(); - } -} - -window.WebVRPolyfill = WebVRPolyfill; - -},{"./util.js":26,"./webvr-polyfill.js":29}],17:[function(require,module,exports){ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var MathUtil = window.MathUtil || {}; - -MathUtil.degToRad = Math.PI / 180; -MathUtil.radToDeg = 180 / Math.PI; - -// Some minimal math functionality borrowed from THREE.Math and stripped down -// for the purposes of this library. - - -MathUtil.Vector2 = function ( x, y ) { - this.x = x || 0; - this.y = y || 0; -}; - -MathUtil.Vector2.prototype = { - constructor: MathUtil.Vector2, - - set: function ( x, y ) { - this.x = x; - this.y = y; - - return this; - }, - - copy: function ( v ) { - this.x = v.x; - this.y = v.y; - - return this; - }, - - subVectors: function ( a, b ) { - this.x = a.x - b.x; - this.y = a.y - b.y; - - return this; - }, -}; - -MathUtil.Vector3 = function ( x, y, z ) { - this.x = x || 0; - this.y = y || 0; - this.z = z || 0; -}; - -MathUtil.Vector3.prototype = { - constructor: MathUtil.Vector3, - - set: function ( x, y, z ) { - this.x = x; - this.y = y; - this.z = z; - - return this; - }, - - copy: function ( v ) { - this.x = v.x; - this.y = v.y; - this.z = v.z; - - return this; - }, - - length: function () { - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); - }, - - normalize: function () { - var scalar = this.length(); - - if ( scalar !== 0 ) { - var invScalar = 1 / scalar; - - this.multiplyScalar(invScalar); - } else { - this.x = 0; - this.y = 0; - this.z = 0; - } - - return this; - }, - - multiplyScalar: function ( scalar ) { - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - }, - - applyQuaternion: function ( q ) { - var x = this.x; - var y = this.y; - var z = this.z; - - var qx = q.x; - var qy = q.y; - var qz = q.z; - var qw = q.w; - - // calculate quat * vector - var ix = qw * x + qy * z - qz * y; - var iy = qw * y + qz * x - qx * z; - var iz = qw * z + qx * y - qy * x; - var iw = - qx * x - qy * y - qz * z; - - // calculate result * inverse quat - this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; - this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; - this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; - - return this; - }, - - dot: function ( v ) { - return this.x * v.x + this.y * v.y + this.z * v.z; - }, - - crossVectors: function ( a, b ) { - var ax = a.x, ay = a.y, az = a.z; - var bx = b.x, by = b.y, bz = b.z; - - this.x = ay * bz - az * by; - this.y = az * bx - ax * bz; - this.z = ax * by - ay * bx; - - return this; - }, -}; - -MathUtil.Quaternion = function ( x, y, z, w ) { - this.x = x || 0; - this.y = y || 0; - this.z = z || 0; - this.w = ( w !== undefined ) ? w : 1; -}; - -MathUtil.Quaternion.prototype = { - constructor: MathUtil.Quaternion, - - set: function ( x, y, z, w ) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; - - return this; - }, - - copy: function ( quaternion ) { - this.x = quaternion.x; - this.y = quaternion.y; - this.z = quaternion.z; - this.w = quaternion.w; - - return this; - }, - - setFromEulerXYZ: function( x, y, z ) { - var c1 = Math.cos( x / 2 ); - var c2 = Math.cos( y / 2 ); - var c3 = Math.cos( z / 2 ); - var s1 = Math.sin( x / 2 ); - var s2 = Math.sin( y / 2 ); - var s3 = Math.sin( z / 2 ); - - this.x = s1 * c2 * c3 + c1 * s2 * s3; - this.y = c1 * s2 * c3 - s1 * c2 * s3; - this.z = c1 * c2 * s3 + s1 * s2 * c3; - this.w = c1 * c2 * c3 - s1 * s2 * s3; - - return this; - }, - - setFromEulerYXZ: function( x, y, z ) { - var c1 = Math.cos( x / 2 ); - var c2 = Math.cos( y / 2 ); - var c3 = Math.cos( z / 2 ); - var s1 = Math.sin( x / 2 ); - var s2 = Math.sin( y / 2 ); - var s3 = Math.sin( z / 2 ); - - this.x = s1 * c2 * c3 + c1 * s2 * s3; - this.y = c1 * s2 * c3 - s1 * c2 * s3; - this.z = c1 * c2 * s3 - s1 * s2 * c3; - this.w = c1 * c2 * c3 + s1 * s2 * s3; - - return this; - }, - - setFromAxisAngle: function ( axis, angle ) { - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm - // assumes axis is normalized - - var halfAngle = angle / 2, s = Math.sin( halfAngle ); - - this.x = axis.x * s; - this.y = axis.y * s; - this.z = axis.z * s; - this.w = Math.cos( halfAngle ); - - return this; - }, - - multiply: function ( q ) { - return this.multiplyQuaternions( this, q ); - }, - - multiplyQuaternions: function ( a, b ) { - // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm - - var qax = a.x, qay = a.y, qaz = a.z, qaw = a.w; - var qbx = b.x, qby = b.y, qbz = b.z, qbw = b.w; - - this.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; - this.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; - this.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; - this.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; - - return this; - }, - - inverse: function () { - this.x *= -1; - this.y *= -1; - this.z *= -1; - - this.normalize(); - - return this; - }, - - normalize: function () { - var l = Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); - - if ( l === 0 ) { - this.x = 0; - this.y = 0; - this.z = 0; - this.w = 1; - } else { - l = 1 / l; - - this.x = this.x * l; - this.y = this.y * l; - this.z = this.z * l; - this.w = this.w * l; - } - - return this; - }, - - slerp: function ( qb, t ) { - if ( t === 0 ) return this; - if ( t === 1 ) return this.copy( qb ); - - var x = this.x, y = this.y, z = this.z, w = this.w; - - // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ - - var cosHalfTheta = w * qb.w + x * qb.x + y * qb.y + z * qb.z; - - if ( cosHalfTheta < 0 ) { - this.w = - qb.w; - this.x = - qb.x; - this.y = - qb.y; - this.z = - qb.z; - - cosHalfTheta = - cosHalfTheta; - } else { - this.copy( qb ); - } - - if ( cosHalfTheta >= 1.0 ) { - this.w = w; - this.x = x; - this.y = y; - this.z = z; - - return this; - } - - var halfTheta = Math.acos( cosHalfTheta ); - var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); - - if ( Math.abs( sinHalfTheta ) < 0.001 ) { - this.w = 0.5 * ( w + this.w ); - this.x = 0.5 * ( x + this.x ); - this.y = 0.5 * ( y + this.y ); - this.z = 0.5 * ( z + this.z ); - - return this; - } - - var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, - ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; - - this.w = ( w * ratioA + this.w * ratioB ); - this.x = ( x * ratioA + this.x * ratioB ); - this.y = ( y * ratioA + this.y * ratioB ); - this.z = ( z * ratioA + this.z * ratioB ); - - return this; - }, - - setFromUnitVectors: function () { - // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final - // assumes direction vectors vFrom and vTo are normalized - - var v1, r; - var EPS = 0.000001; - - return function ( vFrom, vTo ) { - if ( v1 === undefined ) v1 = new MathUtil.Vector3(); - - r = vFrom.dot( vTo ) + 1; - - if ( r < EPS ) { - r = 0; - - if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { - v1.set( - vFrom.y, vFrom.x, 0 ); - } else { - v1.set( 0, - vFrom.z, vFrom.y ); - } - } else { - v1.crossVectors( vFrom, vTo ); - } - - this.x = v1.x; - this.y = v1.y; - this.z = v1.z; - this.w = r; - - this.normalize(); - - return this; - } - }(), -}; - -module.exports = MathUtil; - -},{}],18:[function(require,module,exports){ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var VRDisplay = require('./base.js').VRDisplay; -var MathUtil = require('./math-util.js'); -var Util = require('./util.js'); - -// How much to rotate per key stroke. -var KEY_SPEED = 0.15; -var KEY_ANIMATION_DURATION = 80; - -// How much to rotate for mouse events. -var MOUSE_SPEED_X = 0.5; -var MOUSE_SPEED_Y = 0.3; - -/** - * VRDisplay based on mouse and keyboard input. Designed for desktops/laptops - * where orientation events aren't supported. Cannot present. - */ -function MouseKeyboardVRDisplay() { - this.displayName = 'Mouse and Keyboard VRDisplay (webvr-polyfill)'; - - this.capabilities.hasOrientation = true; - - // Attach to mouse and keyboard events. - window.addEventListener('keydown', this.onKeyDown_.bind(this)); - window.addEventListener('mousemove', this.onMouseMove_.bind(this)); - window.addEventListener('mousedown', this.onMouseDown_.bind(this)); - window.addEventListener('mouseup', this.onMouseUp_.bind(this)); - - // "Private" members. - this.phi_ = 0; - this.theta_ = 0; - - // Variables for keyboard-based rotation animation. - this.targetAngle_ = null; - this.angleAnimation_ = null; - - // State variables for calculations. - this.orientation_ = new MathUtil.Quaternion(); - - // Variables for mouse-based rotation. - this.rotateStart_ = new MathUtil.Vector2(); - this.rotateEnd_ = new MathUtil.Vector2(); - this.rotateDelta_ = new MathUtil.Vector2(); - this.isDragging_ = false; - - this.orientationOut_ = new Float32Array(4); -} -MouseKeyboardVRDisplay.prototype = new VRDisplay(); - -MouseKeyboardVRDisplay.prototype.getImmediatePose = function() { - this.orientation_.setFromEulerYXZ(this.phi_, this.theta_, 0); - - this.orientationOut_[0] = this.orientation_.x; - this.orientationOut_[1] = this.orientation_.y; - this.orientationOut_[2] = this.orientation_.z; - this.orientationOut_[3] = this.orientation_.w; - - return { - position: null, - orientation: this.orientationOut_, - linearVelocity: null, - linearAcceleration: null, - angularVelocity: null, - angularAcceleration: null - }; -}; - -MouseKeyboardVRDisplay.prototype.onKeyDown_ = function(e) { - // Track WASD and arrow keys. - if (e.keyCode == 38) { // Up key. - this.animatePhi_(this.phi_ + KEY_SPEED); - } else if (e.keyCode == 39) { // Right key. - this.animateTheta_(this.theta_ - KEY_SPEED); - } else if (e.keyCode == 40) { // Down key. - this.animatePhi_(this.phi_ - KEY_SPEED); - } else if (e.keyCode == 37) { // Left key. - this.animateTheta_(this.theta_ + KEY_SPEED); - } -}; - -MouseKeyboardVRDisplay.prototype.animateTheta_ = function(targetAngle) { - this.animateKeyTransitions_('theta_', targetAngle); -}; - -MouseKeyboardVRDisplay.prototype.animatePhi_ = function(targetAngle) { - // Prevent looking too far up or down. - targetAngle = Util.clamp(targetAngle, -Math.PI/2, Math.PI/2); - this.animateKeyTransitions_('phi_', targetAngle); -}; - -/** - * Start an animation to transition an angle from one value to another. - */ -MouseKeyboardVRDisplay.prototype.animateKeyTransitions_ = function(angleName, targetAngle) { - // If an animation is currently running, cancel it. - if (this.angleAnimation_) { - cancelAnimationFrame(this.angleAnimation_); - } - var startAngle = this[angleName]; - var startTime = new Date(); - // Set up an interval timer to perform the animation. - this.angleAnimation_ = requestAnimationFrame(function animate() { - // Once we're finished the animation, we're done. - var elapsed = new Date() - startTime; - if (elapsed >= KEY_ANIMATION_DURATION) { - this[angleName] = targetAngle; - cancelAnimationFrame(this.angleAnimation_); - return; - } - // loop with requestAnimationFrame - this.angleAnimation_ = requestAnimationFrame(animate.bind(this)) - // Linearly interpolate the angle some amount. - var percent = elapsed / KEY_ANIMATION_DURATION; - this[angleName] = startAngle + (targetAngle - startAngle) * percent; - }.bind(this)); -}; - -MouseKeyboardVRDisplay.prototype.onMouseDown_ = function(e) { - this.rotateStart_.set(e.clientX, e.clientY); - this.isDragging_ = true; -}; - -// Very similar to https://gist.github.com/mrflix/8351020 -MouseKeyboardVRDisplay.prototype.onMouseMove_ = function(e) { - if (!this.isDragging_ && !this.isPointerLocked_()) { - return; - } - // Support pointer lock API. - if (this.isPointerLocked_()) { - var movementX = e.movementX || e.mozMovementX || 0; - var movementY = e.movementY || e.mozMovementY || 0; - this.rotateEnd_.set(this.rotateStart_.x - movementX, this.rotateStart_.y - movementY); - } else { - this.rotateEnd_.set(e.clientX, e.clientY); - } - // Calculate how much we moved in mouse space. - this.rotateDelta_.subVectors(this.rotateEnd_, this.rotateStart_); - this.rotateStart_.copy(this.rotateEnd_); - - // Keep track of the cumulative euler angles. - this.phi_ += 2 * Math.PI * this.rotateDelta_.y / screen.height * MOUSE_SPEED_Y; - this.theta_ += 2 * Math.PI * this.rotateDelta_.x / screen.width * MOUSE_SPEED_X; - - // Prevent looking too far up or down. - this.phi_ = Util.clamp(this.phi_, -Math.PI/2, Math.PI/2); -}; - -MouseKeyboardVRDisplay.prototype.onMouseUp_ = function(e) { - this.isDragging_ = false; -}; - -MouseKeyboardVRDisplay.prototype.isPointerLocked_ = function() { - var el = document.pointerLockElement || document.mozPointerLockElement || - document.webkitPointerLockElement; - return el !== undefined; -}; - -MouseKeyboardVRDisplay.prototype.resetPose = function() { - this.phi_ = 0; - this.theta_ = 0; -}; - -module.exports = MouseKeyboardVRDisplay; - -},{"./base.js":6,"./math-util.js":17,"./util.js":26}],19:[function(require,module,exports){ -(function (global){ -// This is the entry point if requiring/importing via node, or -// a build tool that uses package.json entry (like browserify, webpack). -// If running in node with a window mock available, globalize its members -// if needed. Otherwise, just continue to `./main` -if (typeof global !== 'undefined' && global.window) { - global.document = global.window.document; - global.navigator = global.window.navigator; -} - -require('./main'); - -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"./main":16}],20:[function(require,module,exports){ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var Util = require('./util.js'); - -function RotateInstructions() { - this.loadIcon_(); - - var overlay = document.createElement('div'); - var s = overlay.style; - s.position = 'fixed'; - s.top = 0; - s.right = 0; - s.bottom = 0; - s.left = 0; - s.backgroundColor = 'gray'; - s.fontFamily = 'sans-serif'; - // Force this to be above the fullscreen canvas, which is at zIndex: 999999. - s.zIndex = 1000000; - - var img = document.createElement('img'); - img.src = this.icon; - var s = img.style; - s.marginLeft = '25%'; - s.marginTop = '25%'; - s.width = '50%'; - overlay.appendChild(img); - - var text = document.createElement('div'); - var s = text.style; - s.textAlign = 'center'; - s.fontSize = '16px'; - s.lineHeight = '24px'; - s.margin = '24px 25%'; - s.width = '50%'; - text.innerHTML = 'Place your phone into your Cardboard viewer.'; - overlay.appendChild(text); - - var snackbar = document.createElement('div'); - var s = snackbar.style; - s.backgroundColor = '#CFD8DC'; - s.position = 'fixed'; - s.bottom = 0; - s.width = '100%'; - s.height = '48px'; - s.padding = '14px 24px'; - s.boxSizing = 'border-box'; - s.color = '#656A6B'; - overlay.appendChild(snackbar); - - var snackbarText = document.createElement('div'); - snackbarText.style.float = 'left'; - snackbarText.innerHTML = 'No Cardboard viewer?'; - - var snackbarButton = document.createElement('a'); - snackbarButton.href = 'https://www.google.com/get/cardboard/get-cardboard/'; - snackbarButton.innerHTML = 'get one'; - snackbarButton.target = '_blank'; - var s = snackbarButton.style; - s.float = 'right'; - s.fontWeight = 600; - s.textTransform = 'uppercase'; - s.borderLeft = '1px solid gray'; - s.paddingLeft = '24px'; - s.textDecoration = 'none'; - s.color = '#656A6B'; - - snackbar.appendChild(snackbarText); - snackbar.appendChild(snackbarButton); - - this.overlay = overlay; - this.text = text; - - this.hide(); -} - -RotateInstructions.prototype.show = function(parent) { - if (!parent && !this.overlay.parentElement) { - document.body.appendChild(this.overlay); - } else if (parent) { - if (this.overlay.parentElement && this.overlay.parentElement != parent) - this.overlay.parentElement.removeChild(this.overlay); - - parent.appendChild(this.overlay); - } - - this.overlay.style.display = 'block'; - - var img = this.overlay.querySelector('img'); - var s = img.style; - - if (Util.isLandscapeMode()) { - s.width = '20%'; - s.marginLeft = '40%'; - s.marginTop = '3%'; - } else { - s.width = '50%'; - s.marginLeft = '25%'; - s.marginTop = '25%'; - } -}; - -RotateInstructions.prototype.hide = function() { - this.overlay.style.display = 'none'; -}; - -RotateInstructions.prototype.showTemporarily = function(ms, parent) { - this.show(parent); - this.timer = setTimeout(this.hide.bind(this), ms); -}; - -RotateInstructions.prototype.disableShowTemporarily = function() { - clearTimeout(this.timer); -}; - -RotateInstructions.prototype.update = function() { - this.disableShowTemporarily(); - // In portrait VR mode, tell the user to rotate to landscape. Otherwise, hide - // the instructions. - if (!Util.isLandscapeMode() && Util.isMobile()) { - this.show(); - } else { - this.hide(); - } -}; - -RotateInstructions.prototype.loadIcon_ = function() { - // Encoded asset_src/rotate-instructions.svg - this.icon = Util.base64('image/svg+xml', 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjE5OHB4IiBoZWlnaHQ9IjI0MHB4IiB2aWV3Qm94PSIwIDAgMTk4IDI0MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxuczpza2V0Y2g9Imh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaC9ucyI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDMuMy4zICgxMjA4MSkgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+dHJhbnNpdGlvbjwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPjwvZGVmcz4KICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHNrZXRjaDp0eXBlPSJNU1BhZ2UiPgogICAgICAgIDxnIGlkPSJ0cmFuc2l0aW9uIiBza2V0Y2g6dHlwZT0iTVNBcnRib2FyZEdyb3VwIj4KICAgICAgICAgICAgPGcgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTQtKy1JbXBvcnRlZC1MYXllcnMtQ29weS0rLUltcG9ydGVkLUxheWVycy1Db3B5LTItQ29weSIgc2tldGNoOnR5cGU9Ik1TTGF5ZXJHcm91cCI+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHktNCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDEwNy4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjYyNSwyLjUyNyBDMTQ5LjYyNSwyLjUyNyAxNTUuODA1LDYuMDk2IDE1Ni4zNjIsNi40MTggTDE1Ni4zNjIsNy4zMDQgQzE1Ni4zNjIsNy40ODEgMTU2LjM3NSw3LjY2NCAxNTYuNCw3Ljg1MyBDMTU2LjQxLDcuOTM0IDE1Ni40Miw4LjAxNSAxNTYuNDI3LDguMDk1IEMxNTYuNTY3LDkuNTEgMTU3LjQwMSwxMS4wOTMgMTU4LjUzMiwxMi4wOTQgTDE2NC4yNTIsMTcuMTU2IEwxNjQuMzMzLDE3LjA2NiBDMTY0LjMzMywxNy4wNjYgMTY4LjcxNSwxNC41MzYgMTY5LjU2OCwxNC4wNDIgQzE3MS4wMjUsMTQuODgzIDE5NS41MzgsMjkuMDM1IDE5NS41MzgsMjkuMDM1IEwxOTUuNTM4LDgzLjAzNiBDMTk1LjUzOCw4My44MDcgMTk1LjE1Miw4NC4yNTMgMTk0LjU5LDg0LjI1MyBDMTk0LjM1Nyw4NC4yNTMgMTk0LjA5NSw4NC4xNzcgMTkzLjgxOCw4NC4wMTcgTDE2OS44NTEsNzAuMTc5IEwxNjkuODM3LDcwLjIwMyBMMTQyLjUxNSw4NS45NzggTDE0MS42NjUsODQuNjU1IEMxMzYuOTM0LDgzLjEyNiAxMzEuOTE3LDgxLjkxNSAxMjYuNzE0LDgxLjA0NSBDMTI2LjcwOSw4MS4wNiAxMjYuNzA3LDgxLjA2OSAxMjYuNzA3LDgxLjA2OSBMMTIxLjY0LDk4LjAzIEwxMTMuNzQ5LDEwMi41ODYgTDExMy43MTIsMTAyLjUyMyBMMTEzLjcxMiwxMzAuMTEzIEMxMTMuNzEyLDEzMC44ODUgMTEzLjMyNiwxMzEuMzMgMTEyLjc2NCwxMzEuMzMgQzExMi41MzIsMTMxLjMzIDExMi4yNjksMTMxLjI1NCAxMTEuOTkyLDEzMS4wOTQgTDY5LjUxOSwxMDYuNTcyIEM2OC41NjksMTA2LjAyMyA2Ny43OTksMTA0LjY5NSA2Ny43OTksMTAzLjYwNSBMNjcuNzk5LDEwMi41NyBMNjcuNzc4LDEwMi42MTcgQzY3LjI3LDEwMi4zOTMgNjYuNjQ4LDEwMi4yNDkgNjUuOTYyLDEwMi4yMTggQzY1Ljg3NSwxMDIuMjE0IDY1Ljc4OCwxMDIuMjEyIDY1LjcwMSwxMDIuMjEyIEM2NS42MDYsMTAyLjIxMiA2NS41MTEsMTAyLjIxNSA2NS40MTYsMTAyLjIxOSBDNjUuMTk1LDEwMi4yMjkgNjQuOTc0LDEwMi4yMzUgNjQuNzU0LDEwMi4yMzUgQzY0LjMzMSwxMDIuMjM1IDYzLjkxMSwxMDIuMjE2IDYzLjQ5OCwxMDIuMTc4IEM2MS44NDMsMTAyLjAyNSA2MC4yOTgsMTAxLjU3OCA1OS4wOTQsMTAwLjg4MiBMMTIuNTE4LDczLjk5MiBMMTIuNTIzLDc0LjAwNCBMMi4yNDUsNTUuMjU0IEMxLjI0NCw1My40MjcgMi4wMDQsNTEuMDM4IDMuOTQzLDQ5LjkxOCBMNTkuOTU0LDE3LjU3MyBDNjAuNjI2LDE3LjE4NSA2MS4zNSwxNy4wMDEgNjIuMDUzLDE3LjAwMSBDNjMuMzc5LDE3LjAwMSA2NC42MjUsMTcuNjYgNjUuMjgsMTguODU0IEw2NS4yODUsMTguODUxIEw2NS41MTIsMTkuMjY0IEw2NS41MDYsMTkuMjY4IEM2NS45MDksMjAuMDAzIDY2LjQwNSwyMC42OCA2Ni45ODMsMjEuMjg2IEw2Ny4yNiwyMS41NTYgQzY5LjE3NCwyMy40MDYgNzEuNzI4LDI0LjM1NyA3NC4zNzMsMjQuMzU3IEM3Ni4zMjIsMjQuMzU3IDc4LjMyMSwyMy44NCA4MC4xNDgsMjIuNzg1IEM4MC4xNjEsMjIuNzg1IDg3LjQ2NywxOC41NjYgODcuNDY3LDE4LjU2NiBDODguMTM5LDE4LjE3OCA4OC44NjMsMTcuOTk0IDg5LjU2NiwxNy45OTQgQzkwLjg5MiwxNy45OTQgOTIuMTM4LDE4LjY1MiA5Mi43OTIsMTkuODQ3IEw5Ni4wNDIsMjUuNzc1IEw5Ni4wNjQsMjUuNzU3IEwxMDIuODQ5LDI5LjY3NCBMMTAyLjc0NCwyOS40OTIgTDE0OS42MjUsMi41MjcgTTE0OS42MjUsMC44OTIgQzE0OS4zNDMsMC44OTIgMTQ5LjA2MiwwLjk2NSAxNDguODEsMS4xMSBMMTAyLjY0MSwyNy42NjYgTDk3LjIzMSwyNC41NDIgTDk0LjIyNiwxOS4wNjEgQzkzLjMxMywxNy4zOTQgOTEuNTI3LDE2LjM1OSA4OS41NjYsMTYuMzU4IEM4OC41NTUsMTYuMzU4IDg3LjU0NiwxNi42MzIgODYuNjQ5LDE3LjE1IEM4My44NzgsMTguNzUgNzkuNjg3LDIxLjE2OSA3OS4zNzQsMjEuMzQ1IEM3OS4zNTksMjEuMzUzIDc5LjM0NSwyMS4zNjEgNzkuMzMsMjEuMzY5IEM3Ny43OTgsMjIuMjU0IDc2LjA4NCwyMi43MjIgNzQuMzczLDIyLjcyMiBDNzIuMDgxLDIyLjcyMiA2OS45NTksMjEuODkgNjguMzk3LDIwLjM4IEw2OC4xNDUsMjAuMTM1IEM2Ny43MDYsMTkuNjcyIDY3LjMyMywxOS4xNTYgNjcuMDA2LDE4LjYwMSBDNjYuOTg4LDE4LjU1OSA2Ni45NjgsMTguNTE5IDY2Ljk0NiwxOC40NzkgTDY2LjcxOSwxOC4wNjUgQzY2LjY5LDE4LjAxMiA2Ni42NTgsMTcuOTYgNjYuNjI0LDE3LjkxMSBDNjUuNjg2LDE2LjMzNyA2My45NTEsMTUuMzY2IDYyLjA1MywxNS4zNjYgQzYxLjA0MiwxNS4zNjYgNjAuMDMzLDE1LjY0IDU5LjEzNiwxNi4xNTggTDMuMTI1LDQ4LjUwMiBDMC40MjYsNTAuMDYxIC0wLjYxMyw1My40NDIgMC44MTEsNTYuMDQgTDExLjA4OSw3NC43OSBDMTEuMjY2LDc1LjExMyAxMS41MzcsNzUuMzUzIDExLjg1LDc1LjQ5NCBMNTguMjc2LDEwMi4yOTggQzU5LjY3OSwxMDMuMTA4IDYxLjQzMywxMDMuNjMgNjMuMzQ4LDEwMy44MDYgQzYzLjgxMiwxMDMuODQ4IDY0LjI4NSwxMDMuODcgNjQuNzU0LDEwMy44NyBDNjUsMTAzLjg3IDY1LjI0OSwxMDMuODY0IDY1LjQ5NCwxMDMuODUyIEM2NS41NjMsMTAzLjg0OSA2NS42MzIsMTAzLjg0NyA2NS43MDEsMTAzLjg0NyBDNjUuNzY0LDEwMy44NDcgNjUuODI4LDEwMy44NDkgNjUuODksMTAzLjg1MiBDNjUuOTg2LDEwMy44NTYgNjYuMDgsMTAzLjg2MyA2Ni4xNzMsMTAzLjg3NCBDNjYuMjgyLDEwNS40NjcgNjcuMzMyLDEwNy4xOTcgNjguNzAyLDEwNy45ODggTDExMS4xNzQsMTMyLjUxIEMxMTEuNjk4LDEzMi44MTIgMTEyLjIzMiwxMzIuOTY1IDExMi43NjQsMTMyLjk2NSBDMTE0LjI2MSwxMzIuOTY1IDExNS4zNDcsMTMxLjc2NSAxMTUuMzQ3LDEzMC4xMTMgTDExNS4zNDcsMTAzLjU1MSBMMTIyLjQ1OCw5OS40NDYgQzEyMi44MTksOTkuMjM3IDEyMy4wODcsOTguODk4IDEyMy4yMDcsOTguNDk4IEwxMjcuODY1LDgyLjkwNSBDMTMyLjI3OSw4My43MDIgMTM2LjU1Nyw4NC43NTMgMTQwLjYwNyw4Ni4wMzMgTDE0MS4xNCw4Ni44NjIgQzE0MS40NTEsODcuMzQ2IDE0MS45NzcsODcuNjEzIDE0Mi41MTYsODcuNjEzIEMxNDIuNzk0LDg3LjYxMyAxNDMuMDc2LDg3LjU0MiAxNDMuMzMzLDg3LjM5MyBMMTY5Ljg2NSw3Mi4wNzYgTDE5Myw4NS40MzMgQzE5My41MjMsODUuNzM1IDE5NC4wNTgsODUuODg4IDE5NC41OSw4NS44ODggQzE5Ni4wODcsODUuODg4IDE5Ny4xNzMsODQuNjg5IDE5Ny4xNzMsODMuMDM2IEwxOTcuMTczLDI5LjAzNSBDMTk3LjE3MywyOC40NTEgMTk2Ljg2MSwyNy45MTEgMTk2LjM1NSwyNy42MTkgQzE5Ni4zNTUsMjcuNjE5IDE3MS44NDMsMTMuNDY3IDE3MC4zODUsMTIuNjI2IEMxNzAuMTMyLDEyLjQ4IDE2OS44NSwxMi40MDcgMTY5LjU2OCwxMi40MDcgQzE2OS4yODUsMTIuNDA3IDE2OS4wMDIsMTIuNDgxIDE2OC43NDksMTIuNjI3IEMxNjguMTQzLDEyLjk3OCAxNjUuNzU2LDE0LjM1NyAxNjQuNDI0LDE1LjEyNSBMMTU5LjYxNSwxMC44NyBDMTU4Ljc5NiwxMC4xNDUgMTU4LjE1NCw4LjkzNyAxNTguMDU0LDcuOTM0IEMxNTguMDQ1LDcuODM3IDE1OC4wMzQsNy43MzkgMTU4LjAyMSw3LjY0IEMxNTguMDA1LDcuNTIzIDE1Ny45OTgsNy40MSAxNTcuOTk4LDcuMzA0IEwxNTcuOTk4LDYuNDE4IEMxNTcuOTk4LDUuODM0IDE1Ny42ODYsNS4yOTUgMTU3LjE4MSw1LjAwMiBDMTU2LjYyNCw0LjY4IDE1MC40NDIsMS4xMTEgMTUwLjQ0MiwxLjExMSBDMTUwLjE4OSwwLjk2NSAxNDkuOTA3LDAuODkyIDE0OS42MjUsMC44OTIiIGlkPSJGaWxsLTEiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTYuMDI3LDI1LjYzNiBMMTQyLjYwMyw1Mi41MjcgQzE0My44MDcsNTMuMjIyIDE0NC41ODIsNTQuMTE0IDE0NC44NDUsNTUuMDY4IEwxNDQuODM1LDU1LjA3NSBMNjMuNDYxLDEwMi4wNTcgTDYzLjQ2LDEwMi4wNTcgQzYxLjgwNiwxMDEuOTA1IDYwLjI2MSwxMDEuNDU3IDU5LjA1NywxMDAuNzYyIEwxMi40ODEsNzMuODcxIEw5Ni4wMjcsMjUuNjM2IiBpZD0iRmlsbC0yIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYzLjQ2MSwxMDIuMTc0IEM2My40NTMsMTAyLjE3NCA2My40NDYsMTAyLjE3NCA2My40MzksMTAyLjE3MiBDNjEuNzQ2LDEwMi4wMTYgNjAuMjExLDEwMS41NjMgNTguOTk4LDEwMC44NjMgTDEyLjQyMiw3My45NzMgQzEyLjM4Niw3My45NTIgMTIuMzY0LDczLjkxNCAxMi4zNjQsNzMuODcxIEMxMi4zNjQsNzMuODMgMTIuMzg2LDczLjc5MSAxMi40MjIsNzMuNzcgTDk1Ljk2OCwyNS41MzUgQzk2LjAwNCwyNS41MTQgOTYuMDQ5LDI1LjUxNCA5Ni4wODUsMjUuNTM1IEwxNDIuNjYxLDUyLjQyNiBDMTQzLjg4OCw1My4xMzQgMTQ0LjY4Miw1NC4wMzggMTQ0Ljk1Nyw1NS4wMzcgQzE0NC45Nyw1NS4wODMgMTQ0Ljk1Myw1NS4xMzMgMTQ0LjkxNSw1NS4xNjEgQzE0NC45MTEsNTUuMTY1IDE0NC44OTgsNTUuMTc0IDE0NC44OTQsNTUuMTc3IEw2My41MTksMTAyLjE1OCBDNjMuNTAxLDEwMi4xNjkgNjMuNDgxLDEwMi4xNzQgNjMuNDYxLDEwMi4xNzQgTDYzLjQ2MSwxMDIuMTc0IFogTTEyLjcxNCw3My44NzEgTDU5LjExNSwxMDAuNjYxIEM2MC4yOTMsMTAxLjM0MSA2MS43ODYsMTAxLjc4MiA2My40MzUsMTAxLjkzNyBMMTQ0LjcwNyw1NS4wMTUgQzE0NC40MjgsNTQuMTA4IDE0My42ODIsNTMuMjg1IDE0Mi41NDQsNTIuNjI4IEw5Ni4wMjcsMjUuNzcxIEwxMi43MTQsNzMuODcxIEwxMi43MTQsNzMuODcxIFoiIGlkPSJGaWxsLTMiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ4LjMyNyw1OC40NzEgQzE0OC4xNDUsNTguNDggMTQ3Ljk2Miw1OC40OCAxNDcuNzgxLDU4LjQ3MiBDMTQ1Ljg4Nyw1OC4zODkgMTQ0LjQ3OSw1Ny40MzQgMTQ0LjYzNiw1Ni4zNCBDMTQ0LjY4OSw1NS45NjcgMTQ0LjY2NCw1NS41OTcgMTQ0LjU2NCw1NS4yMzUgTDYzLjQ2MSwxMDIuMDU3IEM2NC4wODksMTAyLjExNSA2NC43MzMsMTAyLjEzIDY1LjM3OSwxMDIuMDk5IEM2NS41NjEsMTAyLjA5IDY1Ljc0MywxMDIuMDkgNjUuOTI1LDEwMi4wOTggQzY3LjgxOSwxMDIuMTgxIDY5LjIyNywxMDMuMTM2IDY5LjA3LDEwNC4yMyBMMTQ4LjMyNyw1OC40NzEiIGlkPSJGaWxsLTQiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNjkuMDcsMTA0LjM0NyBDNjkuMDQ4LDEwNC4zNDcgNjkuMDI1LDEwNC4zNCA2OS4wMDUsMTA0LjMyNyBDNjguOTY4LDEwNC4zMDEgNjguOTQ4LDEwNC4yNTcgNjguOTU1LDEwNC4yMTMgQzY5LDEwMy44OTYgNjguODk4LDEwMy41NzYgNjguNjU4LDEwMy4yODggQzY4LjE1MywxMDIuNjc4IDY3LjEwMywxMDIuMjY2IDY1LjkyLDEwMi4yMTQgQzY1Ljc0MiwxMDIuMjA2IDY1LjU2MywxMDIuMjA3IDY1LjM4NSwxMDIuMjE1IEM2NC43NDIsMTAyLjI0NiA2NC4wODcsMTAyLjIzMiA2My40NSwxMDIuMTc0IEM2My4zOTksMTAyLjE2OSA2My4zNTgsMTAyLjEzMiA2My4zNDcsMTAyLjA4MiBDNjMuMzM2LDEwMi4wMzMgNjMuMzU4LDEwMS45ODEgNjMuNDAyLDEwMS45NTYgTDE0NC41MDYsNTUuMTM0IEMxNDQuNTM3LDU1LjExNiAxNDQuNTc1LDU1LjExMyAxNDQuNjA5LDU1LjEyNyBDMTQ0LjY0Miw1NS4xNDEgMTQ0LjY2OCw1NS4xNyAxNDQuNjc3LDU1LjIwNCBDMTQ0Ljc4MSw1NS41ODUgMTQ0LjgwNiw1NS45NzIgMTQ0Ljc1MSw1Ni4zNTcgQzE0NC43MDYsNTYuNjczIDE0NC44MDgsNTYuOTk0IDE0NS4wNDcsNTcuMjgyIEMxNDUuNTUzLDU3Ljg5MiAxNDYuNjAyLDU4LjMwMyAxNDcuNzg2LDU4LjM1NSBDMTQ3Ljk2NCw1OC4zNjMgMTQ4LjE0Myw1OC4zNjMgMTQ4LjMyMSw1OC4zNTQgQzE0OC4zNzcsNTguMzUyIDE0OC40MjQsNTguMzg3IDE0OC40MzksNTguNDM4IEMxNDguNDU0LDU4LjQ5IDE0OC40MzIsNTguNTQ1IDE0OC4zODUsNTguNTcyIEw2OS4xMjksMTA0LjMzMSBDNjkuMTExLDEwNC4zNDIgNjkuMDksMTA0LjM0NyA2OS4wNywxMDQuMzQ3IEw2OS4wNywxMDQuMzQ3IFogTTY1LjY2NSwxMDEuOTc1IEM2NS43NTQsMTAxLjk3NSA2NS44NDIsMTAxLjk3NyA2NS45MywxMDEuOTgxIEM2Ny4xOTYsMTAyLjAzNyA2OC4yODMsMTAyLjQ2OSA2OC44MzgsMTAzLjEzOSBDNjkuMDY1LDEwMy40MTMgNjkuMTg4LDEwMy43MTQgNjkuMTk4LDEwNC4wMjEgTDE0Ny44ODMsNTguNTkyIEMxNDcuODQ3LDU4LjU5MiAxNDcuODExLDU4LjU5MSAxNDcuNzc2LDU4LjU4OSBDMTQ2LjUwOSw1OC41MzMgMTQ1LjQyMiw1OC4xIDE0NC44NjcsNTcuNDMxIEMxNDQuNTg1LDU3LjA5MSAxNDQuNDY1LDU2LjcwNyAxNDQuNTIsNTYuMzI0IEMxNDQuNTYzLDU2LjAyMSAxNDQuNTUyLDU1LjcxNiAxNDQuNDg4LDU1LjQxNCBMNjMuODQ2LDEwMS45NyBDNjQuMzUzLDEwMi4wMDIgNjQuODY3LDEwMi4wMDYgNjUuMzc0LDEwMS45ODIgQzY1LjQ3MSwxMDEuOTc3IDY1LjU2OCwxMDEuOTc1IDY1LjY2NSwxMDEuOTc1IEw2NS42NjUsMTAxLjk3NSBaIiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTIuMjA4LDU1LjEzNCBDMS4yMDcsNTMuMzA3IDEuOTY3LDUwLjkxNyAzLjkwNiw0OS43OTcgTDU5LjkxNywxNy40NTMgQzYxLjg1NiwxNi4zMzMgNjQuMjQxLDE2LjkwNyA2NS4yNDMsMTguNzM0IEw2NS40NzUsMTkuMTQ0IEM2NS44NzIsMTkuODgyIDY2LjM2OCwyMC41NiA2Ni45NDUsMjEuMTY1IEw2Ny4yMjMsMjEuNDM1IEM3MC41NDgsMjQuNjQ5IDc1LjgwNiwyNS4xNTEgODAuMTExLDIyLjY2NSBMODcuNDMsMTguNDQ1IEM4OS4zNywxNy4zMjYgOTEuNzU0LDE3Ljg5OSA5Mi43NTUsMTkuNzI3IEw5Ni4wMDUsMjUuNjU1IEwxMi40ODYsNzMuODg0IEwyLjIwOCw1NS4xMzQgWiIgaWQ9IkZpbGwtNiIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMi40ODYsNzQuMDAxIEMxMi40NzYsNzQuMDAxIDEyLjQ2NSw3My45OTkgMTIuNDU1LDczLjk5NiBDMTIuNDI0LDczLjk4OCAxMi4zOTksNzMuOTY3IDEyLjM4NCw3My45NCBMMi4xMDYsNTUuMTkgQzEuMDc1LDUzLjMxIDEuODU3LDUwLjg0NSAzLjg0OCw0OS42OTYgTDU5Ljg1OCwxNy4zNTIgQzYwLjUyNSwxNi45NjcgNjEuMjcxLDE2Ljc2NCA2Mi4wMTYsMTYuNzY0IEM2My40MzEsMTYuNzY0IDY0LjY2NiwxNy40NjYgNjUuMzI3LDE4LjY0NiBDNjUuMzM3LDE4LjY1NCA2NS4zNDUsMTguNjYzIDY1LjM1MSwxOC42NzQgTDY1LjU3OCwxOS4wODggQzY1LjU4NCwxOS4xIDY1LjU4OSwxOS4xMTIgNjUuNTkxLDE5LjEyNiBDNjUuOTg1LDE5LjgzOCA2Ni40NjksMjAuNDk3IDY3LjAzLDIxLjA4NSBMNjcuMzA1LDIxLjM1MSBDNjkuMTUxLDIzLjEzNyA3MS42NDksMjQuMTIgNzQuMzM2LDI0LjEyIEM3Ni4zMTMsMjQuMTIgNzguMjksMjMuNTgyIDgwLjA1MywyMi41NjMgQzgwLjA2NCwyMi41NTcgODAuMDc2LDIyLjU1MyA4MC4wODgsMjIuNTUgTDg3LjM3MiwxOC4zNDQgQzg4LjAzOCwxNy45NTkgODguNzg0LDE3Ljc1NiA4OS41MjksMTcuNzU2IEM5MC45NTYsMTcuNzU2IDkyLjIwMSwxOC40NzIgOTIuODU4LDE5LjY3IEw5Ni4xMDcsMjUuNTk5IEM5Ni4xMzgsMjUuNjU0IDk2LjExOCwyNS43MjQgOTYuMDYzLDI1Ljc1NiBMMTIuNTQ1LDczLjk4NSBDMTIuNTI2LDczLjk5NiAxMi41MDYsNzQuMDAxIDEyLjQ4Niw3NC4wMDEgTDEyLjQ4Niw3NC4wMDEgWiBNNjIuMDE2LDE2Ljk5NyBDNjEuMzEyLDE2Ljk5NyA2MC42MDYsMTcuMTkgNTkuOTc1LDE3LjU1NCBMMy45NjUsNDkuODk5IEMyLjA4Myw1MC45ODUgMS4zNDEsNTMuMzA4IDIuMzEsNTUuMDc4IEwxMi41MzEsNzMuNzIzIEw5NS44NDgsMjUuNjExIEw5Mi42NTMsMTkuNzgyIEM5Mi4wMzgsMTguNjYgOTAuODcsMTcuOTkgODkuNTI5LDE3Ljk5IEM4OC44MjUsMTcuOTkgODguMTE5LDE4LjE4MiA4Ny40ODksMTguNTQ3IEw4MC4xNzIsMjIuNzcyIEM4MC4xNjEsMjIuNzc4IDgwLjE0OSwyMi43ODIgODAuMTM3LDIyLjc4NSBDNzguMzQ2LDIzLjgxMSA3Ni4zNDEsMjQuMzU0IDc0LjMzNiwyNC4zNTQgQzcxLjU4OCwyNC4zNTQgNjkuMDMzLDIzLjM0NyA2Ny4xNDIsMjEuNTE5IEw2Ni44NjQsMjEuMjQ5IEM2Ni4yNzcsMjAuNjM0IDY1Ljc3NCwxOS45NDcgNjUuMzY3LDE5LjIwMyBDNjUuMzYsMTkuMTkyIDY1LjM1NiwxOS4xNzkgNjUuMzU0LDE5LjE2NiBMNjUuMTYzLDE4LjgxOSBDNjUuMTU0LDE4LjgxMSA2NS4xNDYsMTguODAxIDY1LjE0LDE4Ljc5IEM2NC41MjUsMTcuNjY3IDYzLjM1NywxNi45OTcgNjIuMDE2LDE2Ljk5NyBMNjIuMDE2LDE2Ljk5NyBaIiBpZD0iRmlsbC03IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTQyLjQzNCw0OC44MDggTDQyLjQzNCw0OC44MDggQzM5LjkyNCw0OC44MDcgMzcuNzM3LDQ3LjU1IDM2LjU4Miw0NS40NDMgQzM0Ljc3MSw0Mi4xMzkgMzYuMTQ0LDM3LjgwOSAzOS42NDEsMzUuNzg5IEw1MS45MzIsMjguNjkxIEM1My4xMDMsMjguMDE1IDU0LjQxMywyNy42NTggNTUuNzIxLDI3LjY1OCBDNTguMjMxLDI3LjY1OCA2MC40MTgsMjguOTE2IDYxLjU3MywzMS4wMjMgQzYzLjM4NCwzNC4zMjcgNjIuMDEyLDM4LjY1NyA1OC41MTQsNDAuNjc3IEw0Ni4yMjMsNDcuNzc1IEM0NS4wNTMsNDguNDUgNDMuNzQyLDQ4LjgwOCA0Mi40MzQsNDguODA4IEw0Mi40MzQsNDguODA4IFogTTU1LjcyMSwyOC4xMjUgQzU0LjQ5NSwyOC4xMjUgNTMuMjY1LDI4LjQ2MSA1Mi4xNjYsMjkuMDk2IEwzOS44NzUsMzYuMTk0IEMzNi41OTYsMzguMDg3IDM1LjMwMiw0Mi4xMzYgMzYuOTkyLDQ1LjIxOCBDMzguMDYzLDQ3LjE3MyA0MC4wOTgsNDguMzQgNDIuNDM0LDQ4LjM0IEM0My42NjEsNDguMzQgNDQuODksNDguMDA1IDQ1Ljk5LDQ3LjM3IEw1OC4yODEsNDAuMjcyIEM2MS41NiwzOC4zNzkgNjIuODUzLDM0LjMzIDYxLjE2NCwzMS4yNDggQzYwLjA5MiwyOS4yOTMgNTguMDU4LDI4LjEyNSA1NS43MjEsMjguMTI1IEw1NS43MjEsMjguMTI1IFoiIGlkPSJGaWxsLTgiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjU4OCwyLjQwNyBDMTQ5LjU4OCwyLjQwNyAxNTUuNzY4LDUuOTc1IDE1Ni4zMjUsNi4yOTcgTDE1Ni4zMjUsNy4xODQgQzE1Ni4zMjUsNy4zNiAxNTYuMzM4LDcuNTQ0IDE1Ni4zNjIsNy43MzMgQzE1Ni4zNzMsNy44MTQgMTU2LjM4Miw3Ljg5NCAxNTYuMzksNy45NzUgQzE1Ni41Myw5LjM5IDE1Ny4zNjMsMTAuOTczIDE1OC40OTUsMTEuOTc0IEwxNjUuODkxLDE4LjUxOSBDMTY2LjA2OCwxOC42NzUgMTY2LjI0OSwxOC44MTQgMTY2LjQzMiwxOC45MzQgQzE2OC4wMTEsMTkuOTc0IDE2OS4zODIsMTkuNCAxNjkuNDk0LDE3LjY1MiBDMTY5LjU0MywxNi44NjggMTY5LjU1MSwxNi4wNTcgMTY5LjUxNywxNS4yMjMgTDE2OS41MTQsMTUuMDYzIEwxNjkuNTE0LDEzLjkxMiBDMTcwLjc4LDE0LjY0MiAxOTUuNTAxLDI4LjkxNSAxOTUuNTAxLDI4LjkxNSBMMTk1LjUwMSw4Mi45MTUgQzE5NS41MDEsODQuMDA1IDE5NC43MzEsODQuNDQ1IDE5My43ODEsODMuODk3IEwxNTEuMzA4LDU5LjM3NCBDMTUwLjM1OCw1OC44MjYgMTQ5LjU4OCw1Ny40OTcgMTQ5LjU4OCw1Ni40MDggTDE0OS41ODgsMjIuMzc1IiBpZD0iRmlsbC05IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE5NC41NTMsODQuMjUgQzE5NC4yOTYsODQuMjUgMTk0LjAxMyw4NC4xNjUgMTkzLjcyMiw4My45OTcgTDE1MS4yNSw1OS40NzYgQzE1MC4yNjksNTguOTA5IDE0OS40NzEsNTcuNTMzIDE0OS40NzEsNTYuNDA4IEwxNDkuNDcxLDIyLjM3NSBMMTQ5LjcwNSwyMi4zNzUgTDE0OS43MDUsNTYuNDA4IEMxNDkuNzA1LDU3LjQ1OSAxNTAuNDUsNTguNzQ0IDE1MS4zNjYsNTkuMjc0IEwxOTMuODM5LDgzLjc5NSBDMTk0LjI2Myw4NC4wNCAxOTQuNjU1LDg0LjA4MyAxOTQuOTQyLDgzLjkxNyBDMTk1LjIyNyw4My43NTMgMTk1LjM4NCw4My4zOTcgMTk1LjM4NCw4Mi45MTUgTDE5NS4zODQsMjguOTgyIEMxOTQuMTAyLDI4LjI0MiAxNzIuMTA0LDE1LjU0MiAxNjkuNjMxLDE0LjExNCBMMTY5LjYzNCwxNS4yMiBDMTY5LjY2OCwxNi4wNTIgMTY5LjY2LDE2Ljg3NCAxNjkuNjEsMTcuNjU5IEMxNjkuNTU2LDE4LjUwMyAxNjkuMjE0LDE5LjEyMyAxNjguNjQ3LDE5LjQwNSBDMTY4LjAyOCwxOS43MTQgMTY3LjE5NywxOS41NzggMTY2LjM2NywxOS4wMzIgQzE2Ni4xODEsMTguOTA5IDE2NS45OTUsMTguNzY2IDE2NS44MTQsMTguNjA2IEwxNTguNDE3LDEyLjA2MiBDMTU3LjI1OSwxMS4wMzYgMTU2LjQxOCw5LjQzNyAxNTYuMjc0LDcuOTg2IEMxNTYuMjY2LDcuOTA3IDE1Ni4yNTcsNy44MjcgMTU2LjI0Nyw3Ljc0OCBDMTU2LjIyMSw3LjU1NSAxNTYuMjA5LDcuMzY1IDE1Ni4yMDksNy4xODQgTDE1Ni4yMDksNi4zNjQgQzE1NS4zNzUsNS44ODMgMTQ5LjUyOSwyLjUwOCAxNDkuNTI5LDIuNTA4IEwxNDkuNjQ2LDIuMzA2IEMxNDkuNjQ2LDIuMzA2IDE1NS44MjcsNS44NzQgMTU2LjM4NCw2LjE5NiBMMTU2LjQ0Miw2LjIzIEwxNTYuNDQyLDcuMTg0IEMxNTYuNDQyLDcuMzU1IDE1Ni40NTQsNy41MzUgMTU2LjQ3OCw3LjcxNyBDMTU2LjQ4OSw3LjggMTU2LjQ5OSw3Ljg4MiAxNTYuNTA3LDcuOTYzIEMxNTYuNjQ1LDkuMzU4IDE1Ny40NTUsMTAuODk4IDE1OC41NzIsMTEuODg2IEwxNjUuOTY5LDE4LjQzMSBDMTY2LjE0MiwxOC41ODQgMTY2LjMxOSwxOC43MiAxNjYuNDk2LDE4LjgzNyBDMTY3LjI1NCwxOS4zMzYgMTY4LDE5LjQ2NyAxNjguNTQzLDE5LjE5NiBDMTY5LjAzMywxOC45NTMgMTY5LjMyOSwxOC40MDEgMTY5LjM3NywxNy42NDUgQzE2OS40MjcsMTYuODY3IDE2OS40MzQsMTYuMDU0IDE2OS40MDEsMTUuMjI4IEwxNjkuMzk3LDE1LjA2NSBMMTY5LjM5NywxMy43MSBMMTY5LjU3MiwxMy44MSBDMTcwLjgzOSwxNC41NDEgMTk1LjU1OSwyOC44MTQgMTk1LjU1OSwyOC44MTQgTDE5NS42MTgsMjguODQ3IEwxOTUuNjE4LDgyLjkxNSBDMTk1LjYxOCw4My40ODQgMTk1LjQyLDgzLjkxMSAxOTUuMDU5LDg0LjExOSBDMTk0LjkwOCw4NC4yMDYgMTk0LjczNyw4NC4yNSAxOTQuNTUzLDg0LjI1IiBpZD0iRmlsbC0xMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDUuNjg1LDU2LjE2MSBMMTY5LjgsNzAuMDgzIEwxNDMuODIyLDg1LjA4MSBMMTQyLjM2LDg0Ljc3NCBDMTM1LjgyNiw4Mi42MDQgMTI4LjczMiw4MS4wNDYgMTIxLjM0MSw4MC4xNTggQzExNi45NzYsNzkuNjM0IDExMi42NzgsODEuMjU0IDExMS43NDMsODMuNzc4IEMxMTEuNTA2LDg0LjQxNCAxMTEuNTAzLDg1LjA3MSAxMTEuNzMyLDg1LjcwNiBDMTEzLjI3LDg5Ljk3MyAxMTUuOTY4LDk0LjA2OSAxMTkuNzI3LDk3Ljg0MSBMMTIwLjI1OSw5OC42ODYgQzEyMC4yNiw5OC42ODUgOTQuMjgyLDExMy42ODMgOTQuMjgyLDExMy42ODMgTDcwLjE2Nyw5OS43NjEgTDE0NS42ODUsNTYuMTYxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik05NC4yODIsMTEzLjgxOCBMOTQuMjIzLDExMy43ODUgTDY5LjkzMyw5OS43NjEgTDcwLjEwOCw5OS42NiBMMTQ1LjY4NSw1Ni4wMjYgTDE0NS43NDMsNTYuMDU5IEwxNzAuMDMzLDcwLjA4MyBMMTQzLjg0Miw4NS4yMDUgTDE0My43OTcsODUuMTk1IEMxNDMuNzcyLDg1LjE5IDE0Mi4zMzYsODQuODg4IDE0Mi4zMzYsODQuODg4IEMxMzUuNzg3LDgyLjcxNCAxMjguNzIzLDgxLjE2MyAxMjEuMzI3LDgwLjI3NCBDMTIwLjc4OCw4MC4yMDkgMTIwLjIzNiw4MC4xNzcgMTE5LjY4OSw4MC4xNzcgQzExNS45MzEsODAuMTc3IDExMi42MzUsODEuNzA4IDExMS44NTIsODMuODE5IEMxMTEuNjI0LDg0LjQzMiAxMTEuNjIxLDg1LjA1MyAxMTEuODQyLDg1LjY2NyBDMTEzLjM3Nyw4OS45MjUgMTE2LjA1OCw5My45OTMgMTE5LjgxLDk3Ljc1OCBMMTE5LjgyNiw5Ny43NzkgTDEyMC4zNTIsOTguNjE0IEMxMjAuMzU0LDk4LjYxNyAxMjAuMzU2LDk4LjYyIDEyMC4zNTgsOTguNjI0IEwxMjAuNDIyLDk4LjcyNiBMMTIwLjMxNyw5OC43ODcgQzEyMC4yNjQsOTguODE4IDk0LjU5OSwxMTMuNjM1IDk0LjM0LDExMy43ODUgTDk0LjI4MiwxMTMuODE4IEw5NC4yODIsMTEzLjgxOCBaIE03MC40MDEsOTkuNzYxIEw5NC4yODIsMTEzLjU0OSBMMTE5LjA4NCw5OS4yMjkgQzExOS42Myw5OC45MTQgMTE5LjkzLDk4Ljc0IDEyMC4xMDEsOTguNjU0IEwxMTkuNjM1LDk3LjkxNCBDMTE1Ljg2NCw5NC4xMjcgMTEzLjE2OCw5MC4wMzMgMTExLjYyMiw4NS43NDYgQzExMS4zODIsODUuMDc5IDExMS4zODYsODQuNDA0IDExMS42MzMsODMuNzM4IEMxMTIuNDQ4LDgxLjUzOSAxMTUuODM2LDc5Ljk0MyAxMTkuNjg5LDc5Ljk0MyBDMTIwLjI0Niw3OS45NDMgMTIwLjgwNiw3OS45NzYgMTIxLjM1NSw4MC4wNDIgQzEyOC43NjcsODAuOTMzIDEzNS44NDYsODIuNDg3IDE0Mi4zOTYsODQuNjYzIEMxNDMuMjMyLDg0LjgzOCAxNDMuNjExLDg0LjkxNyAxNDMuNzg2LDg0Ljk2NyBMMTY5LjU2Niw3MC4wODMgTDE0NS42ODUsNTYuMjk1IEw3MC40MDEsOTkuNzYxIEw3MC40MDEsOTkuNzYxIFoiIGlkPSJGaWxsLTEyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2Ny4yMywxOC45NzkgTDE2Ny4yMyw2OS44NSBMMTM5LjkwOSw4NS42MjMgTDEzMy40NDgsNzEuNDU2IEMxMzIuNTM4LDY5LjQ2IDEzMC4wMiw2OS43MTggMTI3LjgyNCw3Mi4wMyBDMTI2Ljc2OSw3My4xNCAxMjUuOTMxLDc0LjU4NSAxMjUuNDk0LDc2LjA0OCBMMTE5LjAzNCw5Ny42NzYgTDkxLjcxMiwxMTMuNDUgTDkxLjcxMiw2Mi41NzkgTDE2Ny4yMywxOC45NzkiIGlkPSJGaWxsLTEzIiBmaWxsPSIjRkZGRkZGIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTkxLjcxMiwxMTMuNTY3IEM5MS42OTIsMTEzLjU2NyA5MS42NzIsMTEzLjU2MSA5MS42NTMsMTEzLjU1MSBDOTEuNjE4LDExMy41MyA5MS41OTUsMTEzLjQ5MiA5MS41OTUsMTEzLjQ1IEw5MS41OTUsNjIuNTc5IEM5MS41OTUsNjIuNTM3IDkxLjYxOCw2Mi40OTkgOTEuNjUzLDYyLjQ3OCBMMTY3LjE3MiwxOC44NzggQzE2Ny4yMDgsMTguODU3IDE2Ny4yNTIsMTguODU3IDE2Ny4yODgsMTguODc4IEMxNjcuMzI0LDE4Ljg5OSAxNjcuMzQ3LDE4LjkzNyAxNjcuMzQ3LDE4Ljk3OSBMMTY3LjM0Nyw2OS44NSBDMTY3LjM0Nyw2OS44OTEgMTY3LjMyNCw2OS45MyAxNjcuMjg4LDY5Ljk1IEwxMzkuOTY3LDg1LjcyNSBDMTM5LjkzOSw4NS43NDEgMTM5LjkwNSw4NS43NDUgMTM5Ljg3Myw4NS43MzUgQzEzOS44NDIsODUuNzI1IDEzOS44MTYsODUuNzAyIDEzOS44MDIsODUuNjcyIEwxMzMuMzQyLDcxLjUwNCBDMTMyLjk2Nyw3MC42ODIgMTMyLjI4LDcwLjIyOSAxMzEuNDA4LDcwLjIyOSBDMTMwLjMxOSw3MC4yMjkgMTI5LjA0NCw3MC45MTUgMTI3LjkwOCw3Mi4xMSBDMTI2Ljg3NCw3My4yIDEyNi4wMzQsNzQuNjQ3IDEyNS42MDYsNzYuMDgyIEwxMTkuMTQ2LDk3LjcwOSBDMTE5LjEzNyw5Ny43MzggMTE5LjExOCw5Ny43NjIgMTE5LjA5Miw5Ny43NzcgTDkxLjc3LDExMy41NTEgQzkxLjc1MiwxMTMuNTYxIDkxLjczMiwxMTMuNTY3IDkxLjcxMiwxMTMuNTY3IEw5MS43MTIsMTEzLjU2NyBaIE05MS44MjksNjIuNjQ3IEw5MS44MjksMTEzLjI0OCBMMTE4LjkzNSw5Ny41OTggTDEyNS4zODIsNzYuMDE1IEMxMjUuODI3LDc0LjUyNSAxMjYuNjY0LDczLjA4MSAxMjcuNzM5LDcxLjk1IEMxMjguOTE5LDcwLjcwOCAxMzAuMjU2LDY5Ljk5NiAxMzEuNDA4LDY5Ljk5NiBDMTMyLjM3Nyw2OS45OTYgMTMzLjEzOSw3MC40OTcgMTMzLjU1NCw3MS40MDcgTDEzOS45NjEsODUuNDU4IEwxNjcuMTEzLDY5Ljc4MiBMMTY3LjExMywxOS4xODEgTDkxLjgyOSw2Mi42NDcgTDkxLjgyOSw2Mi42NDcgWiIgaWQ9IkZpbGwtMTQiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTY4LjU0MywxOS4yMTMgTDE2OC41NDMsNzAuMDgzIEwxNDEuMjIxLDg1Ljg1NyBMMTM0Ljc2MSw3MS42ODkgQzEzMy44NTEsNjkuNjk0IDEzMS4zMzMsNjkuOTUxIDEyOS4xMzcsNzIuMjYzIEMxMjguMDgyLDczLjM3NCAxMjcuMjQ0LDc0LjgxOSAxMjYuODA3LDc2LjI4MiBMMTIwLjM0Niw5Ny45MDkgTDkzLjAyNSwxMTMuNjgzIEw5My4wMjUsNjIuODEzIEwxNjguNTQzLDE5LjIxMyIgaWQ9IkZpbGwtMTUiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTMuMDI1LDExMy44IEM5My4wMDUsMTEzLjggOTIuOTg0LDExMy43OTUgOTIuOTY2LDExMy43ODUgQzkyLjkzMSwxMTMuNzY0IDkyLjkwOCwxMTMuNzI1IDkyLjkwOCwxMTMuNjg0IEw5Mi45MDgsNjIuODEzIEM5Mi45MDgsNjIuNzcxIDkyLjkzMSw2Mi43MzMgOTIuOTY2LDYyLjcxMiBMMTY4LjQ4NCwxOS4xMTIgQzE2OC41MiwxOS4wOSAxNjguNTY1LDE5LjA5IDE2OC42MDEsMTkuMTEyIEMxNjguNjM3LDE5LjEzMiAxNjguNjYsMTkuMTcxIDE2OC42NiwxOS4yMTIgTDE2OC42Niw3MC4wODMgQzE2OC42Niw3MC4xMjUgMTY4LjYzNyw3MC4xNjQgMTY4LjYwMSw3MC4xODQgTDE0MS4yOCw4NS45NTggQzE0MS4yNTEsODUuOTc1IDE0MS4yMTcsODUuOTc5IDE0MS4xODYsODUuOTY4IEMxNDEuMTU0LDg1Ljk1OCAxNDEuMTI5LDg1LjkzNiAxNDEuMTE1LDg1LjkwNiBMMTM0LjY1NSw3MS43MzggQzEzNC4yOCw3MC45MTUgMTMzLjU5Myw3MC40NjMgMTMyLjcyLDcwLjQ2MyBDMTMxLjYzMiw3MC40NjMgMTMwLjM1Nyw3MS4xNDggMTI5LjIyMSw3Mi4zNDQgQzEyOC4xODYsNzMuNDMzIDEyNy4zNDcsNzQuODgxIDEyNi45MTksNzYuMzE1IEwxMjAuNDU4LDk3Ljk0MyBDMTIwLjQ1LDk3Ljk3MiAxMjAuNDMxLDk3Ljk5NiAxMjAuNDA1LDk4LjAxIEw5My4wODMsMTEzLjc4NSBDOTMuMDY1LDExMy43OTUgOTMuMDQ1LDExMy44IDkzLjAyNSwxMTMuOCBMOTMuMDI1LDExMy44IFogTTkzLjE0Miw2Mi44ODEgTDkzLjE0MiwxMTMuNDgxIEwxMjAuMjQ4LDk3LjgzMiBMMTI2LjY5NSw3Ni4yNDggQzEyNy4xNCw3NC43NTggMTI3Ljk3Nyw3My4zMTUgMTI5LjA1Miw3Mi4xODMgQzEzMC4yMzEsNzAuOTQyIDEzMS41NjgsNzAuMjI5IDEzMi43Miw3MC4yMjkgQzEzMy42ODksNzAuMjI5IDEzNC40NTIsNzAuNzMxIDEzNC44NjcsNzEuNjQxIEwxNDEuMjc0LDg1LjY5MiBMMTY4LjQyNiw3MC4wMTYgTDE2OC40MjYsMTkuNDE1IEw5My4xNDIsNjIuODgxIEw5My4xNDIsNjIuODgxIFoiIGlkPSJGaWxsLTE2IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS44LDcwLjA4MyBMMTQyLjQ3OCw4NS44NTcgTDEzNi4wMTgsNzEuNjg5IEMxMzUuMTA4LDY5LjY5NCAxMzIuNTksNjkuOTUxIDEzMC4zOTMsNzIuMjYzIEMxMjkuMzM5LDczLjM3NCAxMjguNSw3NC44MTkgMTI4LjA2NCw3Ni4yODIgTDEyMS42MDMsOTcuOTA5IEw5NC4yODIsMTEzLjY4MyBMOTQuMjgyLDYyLjgxMyBMMTY5LjgsMTkuMjEzIEwxNjkuOCw3MC4wODMgWiIgaWQ9IkZpbGwtMTciIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTQuMjgyLDExMy45MTcgQzk0LjI0MSwxMTMuOTE3IDk0LjIwMSwxMTMuOTA3IDk0LjE2NSwxMTMuODg2IEM5NC4wOTMsMTEzLjg0NSA5NC4wNDgsMTEzLjc2NyA5NC4wNDgsMTEzLjY4NCBMOTQuMDQ4LDYyLjgxMyBDOTQuMDQ4LDYyLjczIDk0LjA5Myw2Mi42NTIgOTQuMTY1LDYyLjYxMSBMMTY5LjY4MywxOS4wMSBDMTY5Ljc1NSwxOC45NjkgMTY5Ljg0NCwxOC45NjkgMTY5LjkxNywxOS4wMSBDMTY5Ljk4OSwxOS4wNTIgMTcwLjAzMywxOS4xMjkgMTcwLjAzMywxOS4yMTIgTDE3MC4wMzMsNzAuMDgzIEMxNzAuMDMzLDcwLjE2NiAxNjkuOTg5LDcwLjI0NCAxNjkuOTE3LDcwLjI4NSBMMTQyLjU5NSw4Ni4wNiBDMTQyLjUzOCw4Ni4wOTIgMTQyLjQ2OSw4Ni4xIDE0Mi40MDcsODYuMDggQzE0Mi4zNDQsODYuMDYgMTQyLjI5Myw4Ni4wMTQgMTQyLjI2Niw4NS45NTQgTDEzNS44MDUsNzEuNzg2IEMxMzUuNDQ1LDcwLjk5NyAxMzQuODEzLDcwLjU4IDEzMy45NzcsNzAuNTggQzEzMi45MjEsNzAuNTggMTMxLjY3Niw3MS4yNTIgMTMwLjU2Miw3Mi40MjQgQzEyOS41NCw3My41MDEgMTI4LjcxMSw3NC45MzEgMTI4LjI4Nyw3Ni4zNDggTDEyMS44MjcsOTcuOTc2IEMxMjEuODEsOTguMDM0IDEyMS43NzEsOTguMDgyIDEyMS43Miw5OC4xMTIgTDk0LjM5OCwxMTMuODg2IEM5NC4zNjIsMTEzLjkwNyA5NC4zMjIsMTEzLjkxNyA5NC4yODIsMTEzLjkxNyBMOTQuMjgyLDExMy45MTcgWiBNOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDExMy4yNzkgTDEyMS40MDYsOTcuNzU0IEwxMjcuODQsNzYuMjE1IEMxMjguMjksNzQuNzA4IDEyOS4xMzcsNzMuMjQ3IDEzMC4yMjQsNzIuMTAzIEMxMzEuNDI1LDcwLjgzOCAxMzIuNzkzLDcwLjExMiAxMzMuOTc3LDcwLjExMiBDMTM0Ljk5NSw3MC4xMTIgMTM1Ljc5NSw3MC42MzggMTM2LjIzLDcxLjU5MiBMMTQyLjU4NCw4NS41MjYgTDE2OS41NjYsNjkuOTQ4IEwxNjkuNTY2LDE5LjYxNyBMOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDYyLjk0OCBaIiBpZD0iRmlsbC0xOCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMDkuODk0LDkyLjk0MyBMMTA5Ljg5NCw5Mi45NDMgQzEwOC4xMiw5Mi45NDMgMTA2LjY1Myw5Mi4yMTggMTA1LjY1LDkwLjgyMyBDMTA1LjU4Myw5MC43MzEgMTA1LjU5Myw5MC42MSAxMDUuNjczLDkwLjUyOSBDMTA1Ljc1Myw5MC40NDggMTA1Ljg4LDkwLjQ0IDEwNS45NzQsOTAuNTA2IEMxMDYuNzU0LDkxLjA1MyAxMDcuNjc5LDkxLjMzMyAxMDguNzI0LDkxLjMzMyBDMTEwLjA0Nyw5MS4zMzMgMTExLjQ3OCw5MC44OTQgMTEyLjk4LDkwLjAyNyBDMTE4LjI5MSw4Ni45NiAxMjIuNjExLDc5LjUwOSAxMjIuNjExLDczLjQxNiBDMTIyLjYxMSw3MS40ODkgMTIyLjE2OSw2OS44NTYgMTIxLjMzMyw2OC42OTIgQzEyMS4yNjYsNjguNiAxMjEuMjc2LDY4LjQ3MyAxMjEuMzU2LDY4LjM5MiBDMTIxLjQzNiw2OC4zMTEgMTIxLjU2Myw2OC4yOTkgMTIxLjY1Niw2OC4zNjUgQzEyMy4zMjcsNjkuNTM3IDEyNC4yNDcsNzEuNzQ2IDEyNC4yNDcsNzQuNTg0IEMxMjQuMjQ3LDgwLjgyNiAxMTkuODIxLDg4LjQ0NyAxMTQuMzgyLDkxLjU4NyBDMTEyLjgwOCw5Mi40OTUgMTExLjI5OCw5Mi45NDMgMTA5Ljg5NCw5Mi45NDMgTDEwOS44OTQsOTIuOTQzIFogTTEwNi45MjUsOTEuNDAxIEMxMDcuNzM4LDkyLjA1MiAxMDguNzQ1LDkyLjI3OCAxMDkuODkzLDkyLjI3OCBMMTA5Ljg5NCw5Mi4yNzggQzExMS4yMTUsOTIuMjc4IDExMi42NDcsOTEuOTUxIDExNC4xNDgsOTEuMDg0IEMxMTkuNDU5LDg4LjAxNyAxMjMuNzgsODAuNjIxIDEyMy43OCw3NC41MjggQzEyMy43OCw3Mi41NDkgMTIzLjMxNyw3MC45MjkgMTIyLjQ1NCw2OS43NjcgQzEyMi44NjUsNzAuODAyIDEyMy4wNzksNzIuMDQyIDEyMy4wNzksNzMuNDAyIEMxMjMuMDc5LDc5LjY0NSAxMTguNjUzLDg3LjI4NSAxMTMuMjE0LDkwLjQyNSBDMTExLjY0LDkxLjMzNCAxMTAuMTMsOTEuNzQyIDEwOC43MjQsOTEuNzQyIEMxMDguMDgzLDkxLjc0MiAxMDcuNDgxLDkxLjU5MyAxMDYuOTI1LDkxLjQwMSBMMTA2LjkyNSw5MS40MDEgWiIgaWQ9IkZpbGwtMTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjA5Nyw5MC4yMyBDMTE4LjQ4MSw4Ny4xMjIgMTIyLjg0NSw3OS41OTQgMTIyLjg0NSw3My40MTYgQzEyMi44NDUsNzEuMzY1IDEyMi4zNjIsNjkuNzI0IDEyMS41MjIsNjguNTU2IEMxMTkuNzM4LDY3LjMwNCAxMTcuMTQ4LDY3LjM2MiAxMTQuMjY1LDY5LjAyNiBDMTA4Ljg4MSw3Mi4xMzQgMTA0LjUxNyw3OS42NjIgMTA0LjUxNyw4NS44NCBDMTA0LjUxNyw4Ny44OTEgMTA1LDg5LjUzMiAxMDUuODQsOTAuNyBDMTA3LjYyNCw5MS45NTIgMTEwLjIxNCw5MS44OTQgMTEzLjA5Nyw5MC4yMyIgaWQ9IkZpbGwtMjAiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTA4LjcyNCw5MS42MTQgTDEwOC43MjQsOTEuNjE0IEMxMDcuNTgyLDkxLjYxNCAxMDYuNTY2LDkxLjQwMSAxMDUuNzA1LDkwLjc5NyBDMTA1LjY4NCw5MC43ODMgMTA1LjY2NSw5MC44MTEgMTA1LjY1LDkwLjc5IEMxMDQuNzU2LDg5LjU0NiAxMDQuMjgzLDg3Ljg0MiAxMDQuMjgzLDg1LjgxNyBDMTA0LjI4Myw3OS41NzUgMTA4LjcwOSw3MS45NTMgMTE0LjE0OCw2OC44MTIgQzExNS43MjIsNjcuOTA0IDExNy4yMzIsNjcuNDQ5IDExOC42MzgsNjcuNDQ5IEMxMTkuNzgsNjcuNDQ5IDEyMC43OTYsNjcuNzU4IDEyMS42NTYsNjguMzYyIEMxMjEuNjc4LDY4LjM3NyAxMjEuNjk3LDY4LjM5NyAxMjEuNzEyLDY4LjQxOCBDMTIyLjYwNiw2OS42NjIgMTIzLjA3OSw3MS4zOSAxMjMuMDc5LDczLjQxNSBDMTIzLjA3OSw3OS42NTggMTE4LjY1Myw4Ny4xOTggMTEzLjIxNCw5MC4zMzggQzExMS42NCw5MS4yNDcgMTEwLjEzLDkxLjYxNCAxMDguNzI0LDkxLjYxNCBMMTA4LjcyNCw5MS42MTQgWiBNMTA2LjAwNiw5MC41MDUgQzEwNi43OCw5MS4wMzcgMTA3LjY5NCw5MS4yODEgMTA4LjcyNCw5MS4yODEgQzExMC4wNDcsOTEuMjgxIDExMS40NzgsOTAuODY4IDExMi45OCw5MC4wMDEgQzExOC4yOTEsODYuOTM1IDEyMi42MTEsNzkuNDk2IDEyMi42MTEsNzMuNDAzIEMxMjIuNjExLDcxLjQ5NCAxMjIuMTc3LDY5Ljg4IDEyMS4zNTYsNjguNzE4IEMxMjAuNTgyLDY4LjE4NSAxMTkuNjY4LDY3LjkxOSAxMTguNjM4LDY3LjkxOSBDMTE3LjMxNSw2Ny45MTkgMTE1Ljg4Myw2OC4zNiAxMTQuMzgyLDY5LjIyNyBDMTA5LjA3MSw3Mi4yOTMgMTA0Ljc1MSw3OS43MzMgMTA0Ljc1MSw4NS44MjYgQzEwNC43NTEsODcuNzM1IDEwNS4xODUsODkuMzQzIDEwNi4wMDYsOTAuNTA1IEwxMDYuMDA2LDkwLjUwNSBaIiBpZD0iRmlsbC0yMSIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDkuMzE4LDcuMjYyIEwxMzkuMzM0LDE2LjE0IEwxNTUuMjI3LDI3LjE3MSBMMTYwLjgxNiwyMS4wNTkgTDE0OS4zMTgsNy4yNjIiIGlkPSJGaWxsLTIyIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS42NzYsMTMuODQgTDE1OS45MjgsMTkuNDY3IEMxNTYuMjg2LDIxLjU3IDE1MC40LDIxLjU4IDE0Ni43ODEsMTkuNDkxIEMxNDMuMTYxLDE3LjQwMiAxNDMuMTgsMTQuMDAzIDE0Ni44MjIsMTEuOSBMMTU2LjMxNyw2LjI5MiBMMTQ5LjU4OCwyLjQwNyBMNjcuNzUyLDQ5LjQ3OCBMMTEzLjY3NSw3NS45OTIgTDExNi43NTYsNzQuMjEzIEMxMTcuMzg3LDczLjg0OCAxMTcuNjI1LDczLjMxNSAxMTcuMzc0LDcyLjgyMyBDMTE1LjAxNyw2OC4xOTEgMTE0Ljc4MSw2My4yNzcgMTE2LjY5MSw1OC41NjEgQzEyMi4zMjksNDQuNjQxIDE0MS4yLDMzLjc0NiAxNjUuMzA5LDMwLjQ5MSBDMTczLjQ3OCwyOS4zODggMTgxLjk4OSwyOS41MjQgMTkwLjAxMywzMC44ODUgQzE5MC44NjUsMzEuMDMgMTkxLjc4OSwzMC44OTMgMTkyLjQyLDMwLjUyOCBMMTk1LjUwMSwyOC43NSBMMTY5LjY3NiwxMy44NCIgaWQ9IkZpbGwtMjMiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3Ni40NTkgQzExMy41OTQsNzYuNDU5IDExMy41MTQsNzYuNDM4IDExMy40NDIsNzYuMzk3IEw2Ny41MTgsNDkuODgyIEM2Ny4zNzQsNDkuNzk5IDY3LjI4NCw0OS42NDUgNjcuMjg1LDQ5LjQ3OCBDNjcuMjg1LDQ5LjMxMSA2Ny4zNzQsNDkuMTU3IDY3LjUxOSw0OS4wNzMgTDE0OS4zNTUsMi4wMDIgQzE0OS40OTksMS45MTkgMTQ5LjY3NywxLjkxOSAxNDkuODIxLDIuMDAyIEwxNTYuNTUsNS44ODcgQzE1Ni43NzQsNi4wMTcgMTU2Ljg1LDYuMzAyIDE1Ni43MjIsNi41MjYgQzE1Ni41OTIsNi43NDkgMTU2LjMwNyw2LjgyNiAxNTYuMDgzLDYuNjk2IEwxNDkuNTg3LDIuOTQ2IEw2OC42ODcsNDkuNDc5IEwxMTMuNjc1LDc1LjQ1MiBMMTE2LjUyMyw3My44MDggQzExNi43MTUsNzMuNjk3IDExNy4xNDMsNzMuMzk5IDExNi45NTgsNzMuMDM1IEMxMTQuNTQyLDY4LjI4NyAxMTQuMyw2My4yMjEgMTE2LjI1OCw1OC4zODUgQzExOS4wNjQsNTEuNDU4IDEyNS4xNDMsNDUuMTQzIDEzMy44NCw0MC4xMjIgQzE0Mi40OTcsMzUuMTI0IDE1My4zNTgsMzEuNjMzIDE2NS4yNDcsMzAuMDI4IEMxNzMuNDQ1LDI4LjkyMSAxODIuMDM3LDI5LjA1OCAxOTAuMDkxLDMwLjQyNSBDMTkwLjgzLDMwLjU1IDE5MS42NTIsMzAuNDMyIDE5Mi4xODYsMzAuMTI0IEwxOTQuNTY3LDI4Ljc1IEwxNjkuNDQyLDE0LjI0NCBDMTY5LjIxOSwxNC4xMTUgMTY5LjE0MiwxMy44MjkgMTY5LjI3MSwxMy42MDYgQzE2OS40LDEzLjM4MiAxNjkuNjg1LDEzLjMwNiAxNjkuOTA5LDEzLjQzNSBMMTk1LjczNCwyOC4zNDUgQzE5NS44NzksMjguNDI4IDE5NS45NjgsMjguNTgzIDE5NS45NjgsMjguNzUgQzE5NS45NjgsMjguOTE2IDE5NS44NzksMjkuMDcxIDE5NS43MzQsMjkuMTU0IEwxOTIuNjUzLDMwLjkzMyBDMTkxLjkzMiwzMS4zNSAxOTAuODksMzEuNTA4IDE4OS45MzUsMzEuMzQ2IEMxODEuOTcyLDI5Ljk5NSAxNzMuNDc4LDI5Ljg2IDE2NS4zNzIsMzAuOTU0IEMxNTMuNjAyLDMyLjU0MyAxNDIuODYsMzUuOTkzIDEzNC4zMDcsNDAuOTMxIEMxMjUuNzkzLDQ1Ljg0NyAxMTkuODUxLDUyLjAwNCAxMTcuMTI0LDU4LjczNiBDMTE1LjI3LDYzLjMxNCAxMTUuNTAxLDY4LjExMiAxMTcuNzksNzIuNjExIEMxMTguMTYsNzMuMzM2IDExNy44NDUsNzQuMTI0IDExNi45OSw3NC42MTcgTDExMy45MDksNzYuMzk3IEMxMTMuODM2LDc2LjQzOCAxMTMuNzU2LDc2LjQ1OSAxMTMuNjc1LDc2LjQ1OSIgaWQ9IkZpbGwtMjQiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUzLjMxNiwyMS4yNzkgQzE1MC45MDMsMjEuMjc5IDE0OC40OTUsMjAuNzUxIDE0Ni42NjQsMTkuNjkzIEMxNDQuODQ2LDE4LjY0NCAxNDMuODQ0LDE3LjIzMiAxNDMuODQ0LDE1LjcxOCBDMTQzLjg0NCwxNC4xOTEgMTQ0Ljg2LDEyLjc2MyAxNDYuNzA1LDExLjY5OCBMMTU2LjE5OCw2LjA5MSBDMTU2LjMwOSw2LjAyNSAxNTYuNDUyLDYuMDYyIDE1Ni41MTgsNi4xNzMgQzE1Ni41ODMsNi4yODQgMTU2LjU0Nyw2LjQyNyAxNTYuNDM2LDYuNDkzIEwxNDYuOTQsMTIuMTAyIEMxNDUuMjQ0LDEzLjA4MSAxNDQuMzEyLDE0LjM2NSAxNDQuMzEyLDE1LjcxOCBDMTQ0LjMxMiwxNy4wNTggMTQ1LjIzLDE4LjMyNiAxNDYuODk3LDE5LjI4OSBDMTUwLjQ0NiwyMS4zMzggMTU2LjI0LDIxLjMyNyAxNTkuODExLDE5LjI2NSBMMTY5LjU1OSwxMy42MzcgQzE2OS42NywxMy41NzMgMTY5LjgxMywxMy42MTEgMTY5Ljg3OCwxMy43MjMgQzE2OS45NDMsMTMuODM0IDE2OS45MDQsMTMuOTc3IDE2OS43OTMsMTQuMDQyIEwxNjAuMDQ1LDE5LjY3IEMxNTguMTg3LDIwLjc0MiAxNTUuNzQ5LDIxLjI3OSAxNTMuMzE2LDIxLjI3OSIgaWQ9IkZpbGwtMjUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3NS45OTIgTDY3Ljc2Miw0OS40ODQiIGlkPSJGaWxsLTI2IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMy42NzUsNzYuMzQyIEMxMTMuNjE1LDc2LjM0MiAxMTMuNTU1LDc2LjMyNyAxMTMuNSw3Ni4yOTUgTDY3LjU4Nyw0OS43ODcgQzY3LjQxOSw0OS42OSA2Ny4zNjIsNDkuNDc2IDY3LjQ1OSw0OS4zMDkgQzY3LjU1Niw0OS4xNDEgNjcuNzcsNDkuMDgzIDY3LjkzNyw0OS4xOCBMMTEzLjg1LDc1LjY4OCBDMTE0LjAxOCw3NS43ODUgMTE0LjA3NSw3NiAxMTMuOTc4LDc2LjE2NyBDMTEzLjkxNCw3Ni4yNzkgMTEzLjc5Niw3Ni4zNDIgMTEzLjY3NSw3Ni4zNDIiIGlkPSJGaWxsLTI3IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY3Ljc2Miw0OS40ODQgTDY3Ljc2MiwxMDMuNDg1IEM2Ny43NjIsMTA0LjU3NSA2OC41MzIsMTA1LjkwMyA2OS40ODIsMTA2LjQ1MiBMMTExLjk1NSwxMzAuOTczIEMxMTIuOTA1LDEzMS41MjIgMTEzLjY3NSwxMzEuMDgzIDExMy42NzUsMTI5Ljk5MyBMMTEzLjY3NSw3NS45OTIiIGlkPSJGaWxsLTI4IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMi43MjcsMTMxLjU2MSBDMTEyLjQzLDEzMS41NjEgMTEyLjEwNywxMzEuNDY2IDExMS43OCwxMzEuMjc2IEw2OS4zMDcsMTA2Ljc1NSBDNjguMjQ0LDEwNi4xNDIgNjcuNDEyLDEwNC43MDUgNjcuNDEyLDEwMy40ODUgTDY3LjQxMiw0OS40ODQgQzY3LjQxMiw0OS4yOSA2Ny41NjksNDkuMTM0IDY3Ljc2Miw0OS4xMzQgQzY3Ljk1Niw0OS4xMzQgNjguMTEzLDQ5LjI5IDY4LjExMyw0OS40ODQgTDY4LjExMywxMDMuNDg1IEM2OC4xMTMsMTA0LjQ0NSA2OC44MiwxMDUuNjY1IDY5LjY1NywxMDYuMTQ4IEwxMTIuMTMsMTMwLjY3IEMxMTIuNDc0LDEzMC44NjggMTEyLjc5MSwxMzAuOTEzIDExMywxMzAuNzkyIEMxMTMuMjA2LDEzMC42NzMgMTEzLjMyNSwxMzAuMzgxIDExMy4zMjUsMTI5Ljk5MyBMMTEzLjMyNSw3NS45OTIgQzExMy4zMjUsNzUuNzk4IDExMy40ODIsNzUuNjQxIDExMy42NzUsNzUuNjQxIEMxMTMuODY5LDc1LjY0MSAxMTQuMDI1LDc1Ljc5OCAxMTQuMDI1LDc1Ljk5MiBMMTE0LjAyNSwxMjkuOTkzIEMxMTQuMDI1LDEzMC42NDggMTEzLjc4NiwxMzEuMTQ3IDExMy4zNSwxMzEuMzk5IEMxMTMuMTYyLDEzMS41MDcgMTEyLjk1MiwxMzEuNTYxIDExMi43MjcsMTMxLjU2MSIgaWQ9IkZpbGwtMjkiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEyLjg2LDQwLjUxMiBDMTEyLjg2LDQwLjUxMiAxMTIuODYsNDAuNTEyIDExMi44NTksNDAuNTEyIEMxMTAuNTQxLDQwLjUxMiAxMDguMzYsMzkuOTkgMTA2LjcxNywzOS4wNDEgQzEwNS4wMTIsMzguMDU3IDEwNC4wNzQsMzYuNzI2IDEwNC4wNzQsMzUuMjkyIEMxMDQuMDc0LDMzLjg0NyAxMDUuMDI2LDMyLjUwMSAxMDYuNzU0LDMxLjUwNCBMMTE4Ljc5NSwyNC41NTEgQzEyMC40NjMsMjMuNTg5IDEyMi42NjksMjMuMDU4IDEyNS4wMDcsMjMuMDU4IEMxMjcuMzI1LDIzLjA1OCAxMjkuNTA2LDIzLjU4MSAxMzEuMTUsMjQuNTMgQzEzMi44NTQsMjUuNTE0IDEzMy43OTMsMjYuODQ1IDEzMy43OTMsMjguMjc4IEMxMzMuNzkzLDI5LjcyNCAxMzIuODQxLDMxLjA2OSAxMzEuMTEzLDMyLjA2NyBMMTE5LjA3MSwzOS4wMTkgQzExNy40MDMsMzkuOTgyIDExNS4xOTcsNDAuNTEyIDExMi44Niw0MC41MTIgTDExMi44Niw0MC41MTIgWiBNMTI1LjAwNywyMy43NTkgQzEyMi43OSwyMy43NTkgMTIwLjcwOSwyNC4yNTYgMTE5LjE0NiwyNS4xNTggTDEwNy4xMDQsMzIuMTEgQzEwNS42MDIsMzIuOTc4IDEwNC43NzQsMzQuMTA4IDEwNC43NzQsMzUuMjkyIEMxMDQuNzc0LDM2LjQ2NSAxMDUuNTg5LDM3LjU4MSAxMDcuMDY3LDM4LjQzNCBDMTA4LjYwNSwzOS4zMjMgMTEwLjY2MywzOS44MTIgMTEyLjg1OSwzOS44MTIgTDExMi44NiwzOS44MTIgQzExNS4wNzYsMzkuODEyIDExNy4xNTgsMzkuMzE1IDExOC43MjEsMzguNDEzIEwxMzAuNzYyLDMxLjQ2IEMxMzIuMjY0LDMwLjU5MyAxMzMuMDkyLDI5LjQ2MyAxMzMuMDkyLDI4LjI3OCBDMTMzLjA5MiwyNy4xMDYgMTMyLjI3OCwyNS45OSAxMzAuOCwyNS4xMzYgQzEyOS4yNjEsMjQuMjQ4IDEyNy4yMDQsMjMuNzU5IDEyNS4wMDcsMjMuNzU5IEwxMjUuMDA3LDIzLjc1OSBaIiBpZD0iRmlsbC0zMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNjUuNjMsMTYuMjE5IEwxNTkuODk2LDE5LjUzIEMxNTYuNzI5LDIxLjM1OCAxNTEuNjEsMjEuMzY3IDE0OC40NjMsMTkuNTUgQzE0NS4zMTYsMTcuNzMzIDE0NS4zMzIsMTQuNzc4IDE0OC40OTksMTIuOTQ5IEwxNTQuMjMzLDkuNjM5IEwxNjUuNjMsMTYuMjE5IiBpZD0iRmlsbC0zMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNTQuMjMzLDEwLjQ0OCBMMTY0LjIyOCwxNi4yMTkgTDE1OS41NDYsMTguOTIzIEMxNTguMTEyLDE5Ljc1IDE1Ni4xOTQsMjAuMjA2IDE1NC4xNDcsMjAuMjA2IEMxNTIuMTE4LDIwLjIwNiAxNTAuMjI0LDE5Ljc1NyAxNDguODE0LDE4Ljk0MyBDMTQ3LjUyNCwxOC4xOTkgMTQ2LjgxNCwxNy4yNDkgMTQ2LjgxNCwxNi4yNjkgQzE0Ni44MTQsMTUuMjc4IDE0Ny41MzcsMTQuMzE0IDE0OC44NSwxMy41NTYgTDE1NC4yMzMsMTAuNDQ4IE0xNTQuMjMzLDkuNjM5IEwxNDguNDk5LDEyLjk0OSBDMTQ1LjMzMiwxNC43NzggMTQ1LjMxNiwxNy43MzMgMTQ4LjQ2MywxOS41NSBDMTUwLjAzMSwyMC40NTUgMTUyLjA4NiwyMC45MDcgMTU0LjE0NywyMC45MDcgQzE1Ni4yMjQsMjAuOTA3IDE1OC4zMDYsMjAuNDQ3IDE1OS44OTYsMTkuNTMgTDE2NS42MywxNi4yMTkgTDE1NC4yMzMsOS42MzkiIGlkPSJGaWxsLTMyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NS40NDUsNzIuNjY3IEwxNDUuNDQ1LDcyLjY2NyBDMTQzLjY3Miw3Mi42NjcgMTQyLjIwNCw3MS44MTcgMTQxLjIwMiw3MC40MjIgQzE0MS4xMzUsNzAuMzMgMTQxLjE0NSw3MC4xNDcgMTQxLjIyNSw3MC4wNjYgQzE0MS4zMDUsNjkuOTg1IDE0MS40MzIsNjkuOTQ2IDE0MS41MjUsNzAuMDExIEMxNDIuMzA2LDcwLjU1OSAxNDMuMjMxLDcwLjgyMyAxNDQuMjc2LDcwLjgyMiBDMTQ1LjU5OCw3MC44MjIgMTQ3LjAzLDcwLjM3NiAxNDguNTMyLDY5LjUwOSBDMTUzLjg0Miw2Ni40NDMgMTU4LjE2Myw1OC45ODcgMTU4LjE2Myw1Mi44OTQgQzE1OC4xNjMsNTAuOTY3IDE1Ny43MjEsNDkuMzMyIDE1Ni44ODQsNDguMTY4IEMxNTYuODE4LDQ4LjA3NiAxNTYuODI4LDQ3Ljk0OCAxNTYuOTA4LDQ3Ljg2NyBDMTU2Ljk4OCw0Ny43ODYgMTU3LjExNCw0Ny43NzQgMTU3LjIwOCw0Ny44NCBDMTU4Ljg3OCw0OS4wMTIgMTU5Ljc5OCw1MS4yMiAxNTkuNzk4LDU0LjA1OSBDMTU5Ljc5OCw2MC4zMDEgMTU1LjM3Myw2OC4wNDYgMTQ5LjkzMyw3MS4xODYgQzE0OC4zNiw3Mi4wOTQgMTQ2Ljg1LDcyLjY2NyAxNDUuNDQ1LDcyLjY2NyBMMTQ1LjQ0NSw3Mi42NjcgWiBNMTQyLjQ3Niw3MSBDMTQzLjI5LDcxLjY1MSAxNDQuMjk2LDcyLjAwMiAxNDUuNDQ1LDcyLjAwMiBDMTQ2Ljc2Nyw3Mi4wMDIgMTQ4LjE5OCw3MS41NSAxNDkuNyw3MC42ODIgQzE1NS4wMSw2Ny42MTcgMTU5LjMzMSw2MC4xNTkgMTU5LjMzMSw1NC4wNjUgQzE1OS4zMzEsNTIuMDg1IDE1OC44NjgsNTAuNDM1IDE1OC4wMDYsNDkuMjcyIEMxNTguNDE3LDUwLjMwNyAxNTguNjMsNTEuNTMyIDE1OC42Myw1Mi44OTIgQzE1OC42Myw1OS4xMzQgMTU0LjIwNSw2Ni43NjcgMTQ4Ljc2NSw2OS45MDcgQzE0Ny4xOTIsNzAuODE2IDE0NS42ODEsNzEuMjgzIDE0NC4yNzYsNzEuMjgzIEMxNDMuNjM0LDcxLjI4MyAxNDMuMDMzLDcxLjE5MiAxNDIuNDc2LDcxIEwxNDIuNDc2LDcxIFoiIGlkPSJGaWxsLTMzIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0OC42NDgsNjkuNzA0IEMxNTQuMDMyLDY2LjU5NiAxNTguMzk2LDU5LjA2OCAxNTguMzk2LDUyLjg5MSBDMTU4LjM5Niw1MC44MzkgMTU3LjkxMyw0OS4xOTggMTU3LjA3NCw0OC4wMyBDMTU1LjI4OSw0Ni43NzggMTUyLjY5OSw0Ni44MzYgMTQ5LjgxNiw0OC41MDEgQzE0NC40MzMsNTEuNjA5IDE0MC4wNjgsNTkuMTM3IDE0MC4wNjgsNjUuMzE0IEMxNDAuMDY4LDY3LjM2NSAxNDAuNTUyLDY5LjAwNiAxNDEuMzkxLDcwLjE3NCBDMTQzLjE3Niw3MS40MjcgMTQ1Ljc2NSw3MS4zNjkgMTQ4LjY0OCw2OS43MDQiIGlkPSJGaWxsLTM0IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NC4yNzYsNzEuMjc2IEwxNDQuMjc2LDcxLjI3NiBDMTQzLjEzMyw3MS4yNzYgMTQyLjExOCw3MC45NjkgMTQxLjI1Nyw3MC4zNjUgQzE0MS4yMzYsNzAuMzUxIDE0MS4yMTcsNzAuMzMyIDE0MS4yMDIsNzAuMzExIEMxNDAuMzA3LDY5LjA2NyAxMzkuODM1LDY3LjMzOSAxMzkuODM1LDY1LjMxNCBDMTM5LjgzNSw1OS4wNzMgMTQ0LjI2LDUxLjQzOSAxNDkuNyw0OC4yOTggQzE1MS4yNzMsNDcuMzkgMTUyLjc4NCw0Ni45MjkgMTU0LjE4OSw0Ni45MjkgQzE1NS4zMzIsNDYuOTI5IDE1Ni4zNDcsNDcuMjM2IDE1Ny4yMDgsNDcuODM5IEMxNTcuMjI5LDQ3Ljg1NCAxNTcuMjQ4LDQ3Ljg3MyAxNTcuMjYzLDQ3Ljg5NCBDMTU4LjE1Nyw0OS4xMzggMTU4LjYzLDUwLjg2NSAxNTguNjMsNTIuODkxIEMxNTguNjMsNTkuMTMyIDE1NC4yMDUsNjYuNzY2IDE0OC43NjUsNjkuOTA3IEMxNDcuMTkyLDcwLjgxNSAxNDUuNjgxLDcxLjI3NiAxNDQuMjc2LDcxLjI3NiBMMTQ0LjI3Niw3MS4yNzYgWiBNMTQxLjU1OCw3MC4xMDQgQzE0Mi4zMzEsNzAuNjM3IDE0My4yNDUsNzEuMDA1IDE0NC4yNzYsNzEuMDA1IEMxNDUuNTk4LDcxLjAwNSAxNDcuMDMsNzAuNDY3IDE0OC41MzIsNjkuNiBDMTUzLjg0Miw2Ni41MzQgMTU4LjE2Myw1OS4wMzMgMTU4LjE2Myw1Mi45MzkgQzE1OC4xNjMsNTEuMDMxIDE1Ny43MjksNDkuMzg1IDE1Ni45MDcsNDguMjIzIEMxNTYuMTMzLDQ3LjY5MSAxNTUuMjE5LDQ3LjQwOSAxNTQuMTg5LDQ3LjQwOSBDMTUyLjg2Nyw0Ny40MDkgMTUxLjQzNSw0Ny44NDIgMTQ5LjkzMyw0OC43MDkgQzE0NC42MjMsNTEuNzc1IDE0MC4zMDIsNTkuMjczIDE0MC4zMDIsNjUuMzY2IEMxNDAuMzAyLDY3LjI3NiAxNDAuNzM2LDY4Ljk0MiAxNDEuNTU4LDcwLjEwNCBMMTQxLjU1OCw3MC4xMDQgWiIgaWQ9IkZpbGwtMzUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUwLjcyLDY1LjM2MSBMMTUwLjM1Nyw2NS4wNjYgQzE1MS4xNDcsNjQuMDkyIDE1MS44NjksNjMuMDQgMTUyLjUwNSw2MS45MzggQzE1My4zMTMsNjAuNTM5IDE1My45NzgsNTkuMDY3IDE1NC40ODIsNTcuNTYzIEwxNTQuOTI1LDU3LjcxMiBDMTU0LjQxMiw1OS4yNDUgMTUzLjczMyw2MC43NDUgMTUyLjkxLDYyLjE3MiBDMTUyLjI2Miw2My4yOTUgMTUxLjUyNSw2NC4zNjggMTUwLjcyLDY1LjM2MSIgaWQ9IkZpbGwtMzYiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE1LjkxNyw4NC41MTQgTDExNS41NTQsODQuMjIgQzExNi4zNDQsODMuMjQ1IDExNy4wNjYsODIuMTk0IDExNy43MDIsODEuMDkyIEMxMTguNTEsNzkuNjkyIDExOS4xNzUsNzguMjIgMTE5LjY3OCw3Ni43MTcgTDEyMC4xMjEsNzYuODY1IEMxMTkuNjA4LDc4LjM5OCAxMTguOTMsNzkuODk5IDExOC4xMDYsODEuMzI2IEMxMTcuNDU4LDgyLjQ0OCAxMTYuNzIyLDgzLjUyMSAxMTUuOTE3LDg0LjUxNCIgaWQ9IkZpbGwtMzciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE0LDEzMC40NzYgTDExNCwxMzAuMDA4IEwxMTQsNzYuMDUyIEwxMTQsNzUuNTg0IEwxMTQsNzYuMDUyIEwxMTQsMTMwLjAwOCBMMTE0LDEzMC40NzYiIGlkPSJGaWxsLTM4IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYyLjAwMDAwMCwgMC4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTkuODIyLDM3LjQ3NCBDMTkuODM5LDM3LjMzOSAxOS43NDcsMzcuMTk0IDE5LjU1NSwzNy4wODIgQzE5LjIyOCwzNi44OTQgMTguNzI5LDM2Ljg3MiAxOC40NDYsMzcuMDM3IEwxMi40MzQsNDAuNTA4IEMxMi4zMDMsNDAuNTg0IDEyLjI0LDQwLjY4NiAxMi4yNDMsNDAuNzkzIEMxMi4yNDUsNDAuOTI1IDEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQxLjM3MSBMMTIuMjQ1LDQxLjQxNCBMMTIuMjM4LDQxLjU0MiBDOC4xNDgsNDMuODg3IDUuNjQ3LDQ1LjMyMSA1LjY0Nyw0NS4zMjEgQzUuNjQ2LDQ1LjMyMSAzLjU3LDQ2LjM2NyAyLjg2LDUwLjUxMyBDMi44Niw1MC41MTMgMS45NDgsNTcuNDc0IDEuOTYyLDcwLjI1OCBDMS45NzcsODIuODI4IDIuNTY4LDg3LjMyOCAzLjEyOSw5MS42MDkgQzMuMzQ5LDkzLjI5MyA2LjEzLDkzLjczNCA2LjEzLDkzLjczNCBDNi40NjEsOTMuNzc0IDYuODI4LDkzLjcwNyA3LjIxLDkzLjQ4NiBMODIuNDgzLDQ5LjkzNSBDODQuMjkxLDQ4Ljg2NiA4NS4xNSw0Ni4yMTYgODUuNTM5LDQzLjY1MSBDODYuNzUyLDM1LjY2MSA4Ny4yMTQsMTAuNjczIDg1LjI2NCwzLjc3MyBDODUuMDY4LDMuMDggODQuNzU0LDIuNjkgODQuMzk2LDIuNDkxIEw4Mi4zMSwxLjcwMSBDODEuNTgzLDEuNzI5IDgwLjg5NCwyLjE2OCA4MC43NzYsMi4yMzYgQzgwLjYzNiwyLjMxNyA0MS44MDcsMjQuNTg1IDIwLjAzMiwzNy4wNzIgTDE5LjgyMiwzNy40NzQiIGlkPSJGaWxsLTEiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNODIuMzExLDEuNzAxIEw4NC4zOTYsMi40OTEgQzg0Ljc1NCwyLjY5IDg1LjA2OCwzLjA4IDg1LjI2NCwzLjc3MyBDODcuMjEzLDEwLjY3MyA4Ni43NTEsMzUuNjYgODUuNTM5LDQzLjY1MSBDODUuMTQ5LDQ2LjIxNiA4NC4yOSw0OC44NjYgODIuNDgzLDQ5LjkzNSBMNy4yMSw5My40ODYgQzYuODk3LDkzLjY2NyA2LjU5NSw5My43NDQgNi4zMTQsOTMuNzQ0IEw2LjEzMSw5My43MzMgQzYuMTMxLDkzLjczNCAzLjM0OSw5My4yOTMgMy4xMjgsOTEuNjA5IEMyLjU2OCw4Ny4zMjcgMS45NzcsODIuODI4IDEuOTYzLDcwLjI1OCBDMS45NDgsNTcuNDc0IDIuODYsNTAuNTEzIDIuODYsNTAuNTEzIEMzLjU3LDQ2LjM2NyA1LjY0Nyw0NS4zMjEgNS42NDcsNDUuMzIxIEM1LjY0Nyw0NS4zMjEgOC4xNDgsNDMuODg3IDEyLjIzOCw0MS41NDIgTDEyLjI0NSw0MS40MTQgTDEyLjI0NSw0MS4zNzEgQzEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQwLjkyNSAxMi4yNDMsNDAuNzkzIEMxMi4yNCw0MC42ODYgMTIuMzAyLDQwLjU4MyAxMi40MzQsNDAuNTA4IEwxOC40NDYsMzcuMDM2IEMxOC41NzQsMzYuOTYyIDE4Ljc0NiwzNi45MjYgMTguOTI3LDM2LjkyNiBDMTkuMTQ1LDM2LjkyNiAxOS4zNzYsMzYuOTc5IDE5LjU1NCwzNy4wODIgQzE5Ljc0NywzNy4xOTQgMTkuODM5LDM3LjM0IDE5LjgyMiwzNy40NzQgTDIwLjAzMywzNy4wNzIgQzQxLjgwNiwyNC41ODUgODAuNjM2LDIuMzE4IDgwLjc3NywyLjIzNiBDODAuODk0LDIuMTY4IDgxLjU4MywxLjcyOSA4Mi4zMTEsMS43MDEgTTgyLjMxMSwwLjcwNCBMODIuMjcyLDAuNzA1IEM4MS42NTQsMC43MjggODAuOTg5LDAuOTQ5IDgwLjI5OCwxLjM2MSBMODAuMjc3LDEuMzczIEM4MC4xMjksMS40NTggNTkuNzY4LDEzLjEzNSAxOS43NTgsMzYuMDc5IEMxOS41LDM1Ljk4MSAxOS4yMTQsMzUuOTI5IDE4LjkyNywzNS45MjkgQzE4LjU2MiwzNS45MjkgMTguMjIzLDM2LjAxMyAxNy45NDcsMzYuMTczIEwxMS45MzUsMzkuNjQ0IEMxMS40OTMsMzkuODk5IDExLjIzNiw0MC4zMzQgMTEuMjQ2LDQwLjgxIEwxMS4yNDcsNDAuOTYgTDUuMTY3LDQ0LjQ0NyBDNC43OTQsNDQuNjQ2IDIuNjI1LDQ1Ljk3OCAxLjg3Nyw1MC4zNDUgTDEuODcxLDUwLjM4NCBDMS44NjIsNTAuNDU0IDAuOTUxLDU3LjU1NyAwLjk2NSw3MC4yNTkgQzAuOTc5LDgyLjg3OSAxLjU2OCw4Ny4zNzUgMi4xMzcsOTEuNzI0IEwyLjEzOSw5MS43MzkgQzIuNDQ3LDk0LjA5NCA1LjYxNCw5NC42NjIgNS45NzUsOTQuNzE5IEw2LjAwOSw5NC43MjMgQzYuMTEsOTQuNzM2IDYuMjEzLDk0Ljc0MiA2LjMxNCw5NC43NDIgQzYuNzksOTQuNzQyIDcuMjYsOTQuNjEgNy43MSw5NC4zNSBMODIuOTgzLDUwLjc5OCBDODQuNzk0LDQ5LjcyNyA4NS45ODIsNDcuMzc1IDg2LjUyNSw0My44MDEgQzg3LjcxMSwzNS45ODcgODguMjU5LDEwLjcwNSA4Ni4yMjQsMy41MDIgQzg1Ljk3MSwyLjYwOSA4NS41MiwxLjk3NSA4NC44ODEsMS42MiBMODQuNzQ5LDEuNTU4IEw4Mi42NjQsMC43NjkgQzgyLjU1MSwwLjcyNSA4Mi40MzEsMC43MDQgODIuMzExLDAuNzA0IiBpZD0iRmlsbC0yIiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY2LjI2NywxMS41NjUgTDY3Ljc2MiwxMS45OTkgTDExLjQyMyw0NC4zMjUiIGlkPSJGaWxsLTMiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMjAyLDkwLjU0NSBDMTIuMDI5LDkwLjU0NSAxMS44NjIsOTAuNDU1IDExLjc2OSw5MC4yOTUgQzExLjYzMiw5MC4wNTcgMTEuNzEzLDg5Ljc1MiAxMS45NTIsODkuNjE0IEwzMC4zODksNzguOTY5IEMzMC42MjgsNzguODMxIDMwLjkzMyw3OC45MTMgMzEuMDcxLDc5LjE1MiBDMzEuMjA4LDc5LjM5IDMxLjEyNyw3OS42OTYgMzAuODg4LDc5LjgzMyBMMTIuNDUxLDkwLjQ3OCBMMTIuMjAyLDkwLjU0NSIgaWQ9IkZpbGwtNCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMy43NjQsNDIuNjU0IEwxMy42NTYsNDIuNTkyIEwxMy43MDIsNDIuNDIxIEwxOC44MzcsMzkuNDU3IEwxOS4wMDcsMzkuNTAyIEwxOC45NjIsMzkuNjczIEwxMy44MjcsNDIuNjM3IEwxMy43NjQsNDIuNjU0IiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTguNTIsOTAuMzc1IEw4LjUyLDQ2LjQyMSBMOC41ODMsNDYuMzg1IEw3NS44NCw3LjU1NCBMNzUuODQsNTEuNTA4IEw3NS43NzgsNTEuNTQ0IEw4LjUyLDkwLjM3NSBMOC41Miw5MC4zNzUgWiBNOC43Nyw0Ni41NjQgTDguNzcsODkuOTQ0IEw3NS41OTEsNTEuMzY1IEw3NS41OTEsNy45ODUgTDguNzcsNDYuNTY0IEw4Ljc3LDQ2LjU2NCBaIiBpZD0iRmlsbC02IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTI0Ljk4Niw4My4xODIgQzI0Ljc1Niw4My4zMzEgMjQuMzc0LDgzLjU2NiAyNC4xMzcsODMuNzA1IEwxMi42MzIsOTAuNDA2IEMxMi4zOTUsOTAuNTQ1IDEyLjQyNiw5MC42NTggMTIuNyw5MC42NTggTDEzLjI2NSw5MC42NTggQzEzLjU0LDkwLjY1OCAxMy45NTgsOTAuNTQ1IDE0LjE5NSw5MC40MDYgTDI1LjcsODMuNzA1IEMyNS45MzcsODMuNTY2IDI2LjEyOCw4My40NTIgMjYuMTI1LDgzLjQ0OSBDMjYuMTIyLDgzLjQ0NyAyNi4xMTksODMuMjIgMjYuMTE5LDgyLjk0NiBDMjYuMTE5LDgyLjY3MiAyNS45MzEsODIuNTY5IDI1LjcwMSw4Mi43MTkgTDI0Ljk4Niw4My4xODIiIGlkPSJGaWxsLTciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTMuMjY2LDkwLjc4MiBMMTIuNyw5MC43ODIgQzEyLjUsOTAuNzgyIDEyLjM4NCw5MC43MjYgMTIuMzU0LDkwLjYxNiBDMTIuMzI0LDkwLjUwNiAxMi4zOTcsOTAuMzk5IDEyLjU2OSw5MC4yOTkgTDI0LjA3NCw4My41OTcgQzI0LjMxLDgzLjQ1OSAyNC42ODksODMuMjI2IDI0LjkxOCw4My4wNzggTDI1LjYzMyw4Mi42MTQgQzI1LjcyMyw4Mi41NTUgMjUuODEzLDgyLjUyNSAyNS44OTksODIuNTI1IEMyNi4wNzEsODIuNTI1IDI2LjI0NCw4Mi42NTUgMjYuMjQ0LDgyLjk0NiBDMjYuMjQ0LDgzLjE2IDI2LjI0NSw4My4zMDkgMjYuMjQ3LDgzLjM4MyBMMjYuMjUzLDgzLjM4NyBMMjYuMjQ5LDgzLjQ1NiBDMjYuMjQ2LDgzLjUzMSAyNi4yNDYsODMuNTMxIDI1Ljc2Myw4My44MTIgTDE0LjI1OCw5MC41MTQgQzE0LDkwLjY2NSAxMy41NjQsOTAuNzgyIDEzLjI2Niw5MC43ODIgTDEzLjI2Niw5MC43ODIgWiBNMTIuNjY2LDkwLjUzMiBMMTIuNyw5MC41MzMgTDEzLjI2Niw5MC41MzMgQzEzLjUxOCw5MC41MzMgMTMuOTE1LDkwLjQyNSAxNC4xMzIsOTAuMjk5IEwyNS42MzcsODMuNTk3IEMyNS44MDUsODMuNDk5IDI1LjkzMSw4My40MjQgMjUuOTk4LDgzLjM4MyBDMjUuOTk0LDgzLjI5OSAyNS45OTQsODMuMTY1IDI1Ljk5NCw4Mi45NDYgTDI1Ljg5OSw4Mi43NzUgTDI1Ljc2OCw4Mi44MjQgTDI1LjA1NCw4My4yODcgQzI0LjgyMiw4My40MzcgMjQuNDM4LDgzLjY3MyAyNC4yLDgzLjgxMiBMMTIuNjk1LDkwLjUxNCBMMTIuNjY2LDkwLjUzMiBMMTIuNjY2LDkwLjUzMiBaIiBpZD0iRmlsbC04IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEzLjI2Niw4OS44NzEgTDEyLjcsODkuODcxIEMxMi41LDg5Ljg3MSAxMi4zODQsODkuODE1IDEyLjM1NCw4OS43MDUgQzEyLjMyNCw4OS41OTUgMTIuMzk3LDg5LjQ4OCAxMi41NjksODkuMzg4IEwyNC4wNzQsODIuNjg2IEMyNC4zMzIsODIuNTM1IDI0Ljc2OCw4Mi40MTggMjUuMDY3LDgyLjQxOCBMMjUuNjMyLDgyLjQxOCBDMjUuODMyLDgyLjQxOCAyNS45NDgsODIuNDc0IDI1Ljk3OCw4Mi41ODQgQzI2LjAwOCw4Mi42OTQgMjUuOTM1LDgyLjgwMSAyNS43NjMsODIuOTAxIEwxNC4yNTgsODkuNjAzIEMxNCw4OS43NTQgMTMuNTY0LDg5Ljg3MSAxMy4yNjYsODkuODcxIEwxMy4yNjYsODkuODcxIFogTTEyLjY2Niw4OS42MjEgTDEyLjcsODkuNjIyIEwxMy4yNjYsODkuNjIyIEMxMy41MTgsODkuNjIyIDEzLjkxNSw4OS41MTUgMTQuMTMyLDg5LjM4OCBMMjUuNjM3LDgyLjY4NiBMMjUuNjY3LDgyLjY2OCBMMjUuNjMyLDgyLjY2NyBMMjUuMDY3LDgyLjY2NyBDMjQuODE1LDgyLjY2NyAyNC40MTgsODIuNzc1IDI0LjIsODIuOTAxIEwxMi42OTUsODkuNjAzIEwxMi42NjYsODkuNjIxIEwxMi42NjYsODkuNjIxIFoiIGlkPSJGaWxsLTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMzcsOTAuODAxIEwxMi4zNyw4OS41NTQgTDEyLjM3LDkwLjgwMSIgaWQ9IkZpbGwtMTAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNi4xMyw5My45MDEgQzUuMzc5LDkzLjgwOCA0LjgxNiw5My4xNjQgNC42OTEsOTIuNTI1IEMzLjg2LDg4LjI4NyAzLjU0LDgzLjc0MyAzLjUyNiw3MS4xNzMgQzMuNTExLDU4LjM4OSA0LjQyMyw1MS40MjggNC40MjMsNTEuNDI4IEM1LjEzNCw0Ny4yODIgNy4yMSw0Ni4yMzYgNy4yMSw0Ni4yMzYgQzcuMjEsNDYuMjM2IDgxLjY2NywzLjI1IDgyLjA2OSwzLjAxNyBDODIuMjkyLDIuODg4IDg0LjU1NiwxLjQzMyA4NS4yNjQsMy45NCBDODcuMjE0LDEwLjg0IDg2Ljc1MiwzNS44MjcgODUuNTM5LDQzLjgxOCBDODUuMTUsNDYuMzgzIDg0LjI5MSw0OS4wMzMgODIuNDgzLDUwLjEwMSBMNy4yMSw5My42NTMgQzYuODI4LDkzLjg3NCA2LjQ2MSw5My45NDEgNi4xMyw5My45MDEgQzYuMTMsOTMuOTAxIDMuMzQ5LDkzLjQ2IDMuMTI5LDkxLjc3NiBDMi41NjgsODcuNDk1IDEuOTc3LDgyLjk5NSAxLjk2Miw3MC40MjUgQzEuOTQ4LDU3LjY0MSAyLjg2LDUwLjY4IDIuODYsNTAuNjggQzMuNTcsNDYuNTM0IDUuNjQ3LDQ1LjQ4OSA1LjY0Nyw0NS40ODkgQzUuNjQ2LDQ1LjQ4OSA4LjA2NSw0NC4wOTIgMTIuMjQ1LDQxLjY3OSBMMTMuMTE2LDQxLjU2IEwxOS43MTUsMzcuNzMgTDE5Ljc2MSwzNy4yNjkgTDYuMTMsOTMuOTAxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjMxNyw5NC4xNjEgTDYuMTAyLDk0LjE0OCBMNi4xMDEsOTQuMTQ4IEw1Ljg1Nyw5NC4xMDEgQzUuMTM4LDkzLjk0NSAzLjA4NSw5My4zNjUgMi44ODEsOTEuODA5IEMyLjMxMyw4Ny40NjkgMS43MjcsODIuOTk2IDEuNzEzLDcwLjQyNSBDMS42OTksNTcuNzcxIDIuNjA0LDUwLjcxOCAyLjYxMyw1MC42NDggQzMuMzM4LDQ2LjQxNyA1LjQ0NSw0NS4zMSA1LjUzNSw0NS4yNjYgTDEyLjE2Myw0MS40MzkgTDEzLjAzMyw0MS4zMiBMMTkuNDc5LDM3LjU3OCBMMTkuNTEzLDM3LjI0NCBDMTkuNTI2LDM3LjEwNyAxOS42NDcsMzcuMDA4IDE5Ljc4NiwzNy4wMjEgQzE5LjkyMiwzNy4wMzQgMjAuMDIzLDM3LjE1NiAyMC4wMDksMzcuMjkzIEwxOS45NSwzNy44ODIgTDEzLjE5OCw0MS44MDEgTDEyLjMyOCw0MS45MTkgTDUuNzcyLDQ1LjcwNCBDNS43NDEsNDUuNzIgMy43ODIsNDYuNzcyIDMuMTA2LDUwLjcyMiBDMy4wOTksNTAuNzgyIDIuMTk4LDU3LjgwOCAyLjIxMiw3MC40MjQgQzIuMjI2LDgyLjk2MyAyLjgwOSw4Ny40MiAzLjM3Myw5MS43MjkgQzMuNDY0LDkyLjQyIDQuMDYyLDkyLjg4MyA0LjY4Miw5My4xODEgQzQuNTY2LDkyLjk4NCA0LjQ4Niw5Mi43NzYgNC40NDYsOTIuNTcyIEMzLjY2NSw4OC41ODggMy4yOTEsODQuMzcgMy4yNzYsNzEuMTczIEMzLjI2Miw1OC41MiA0LjE2Nyw1MS40NjYgNC4xNzYsNTEuMzk2IEM0LjkwMSw0Ny4xNjUgNy4wMDgsNDYuMDU5IDcuMDk4LDQ2LjAxNCBDNy4wOTQsNDYuMDE1IDgxLjU0MiwzLjAzNCA4MS45NDQsMi44MDIgTDgxLjk3MiwyLjc4NSBDODIuODc2LDIuMjQ3IDgzLjY5MiwyLjA5NyA4NC4zMzIsMi4zNTIgQzg0Ljg4NywyLjU3MyA4NS4yODEsMy4wODUgODUuNTA0LDMuODcyIEM4Ny41MTgsMTEgODYuOTY0LDM2LjA5MSA4NS43ODUsNDMuODU1IEM4NS4yNzgsNDcuMTk2IDg0LjIxLDQ5LjM3IDgyLjYxLDUwLjMxNyBMNy4zMzUsOTMuODY5IEM2Ljk5OSw5NC4wNjMgNi42NTgsOTQuMTYxIDYuMzE3LDk0LjE2MSBMNi4zMTcsOTQuMTYxIFogTTYuMTcsOTMuNjU0IEM2LjQ2Myw5My42OSA2Ljc3NCw5My42MTcgNy4wODUsOTMuNDM3IEw4Mi4zNTgsNDkuODg2IEM4NC4xODEsNDguODA4IDg0Ljk2LDQ1Ljk3MSA4NS4yOTIsNDMuNzggQzg2LjQ2NiwzNi4wNDkgODcuMDIzLDExLjA4NSA4NS4wMjQsNC4wMDggQzg0Ljg0NiwzLjM3NyA4NC41NTEsMi45NzYgODQuMTQ4LDIuODE2IEM4My42NjQsMi42MjMgODIuOTgyLDIuNzY0IDgyLjIyNywzLjIxMyBMODIuMTkzLDMuMjM0IEM4MS43OTEsMy40NjYgNy4zMzUsNDYuNDUyIDcuMzM1LDQ2LjQ1MiBDNy4zMDQsNDYuNDY5IDUuMzQ2LDQ3LjUyMSA0LjY2OSw1MS40NzEgQzQuNjYyLDUxLjUzIDMuNzYxLDU4LjU1NiAzLjc3NSw3MS4xNzMgQzMuNzksODQuMzI4IDQuMTYxLDg4LjUyNCA0LjkzNiw5Mi40NzYgQzUuMDI2LDkyLjkzNyA1LjQxMiw5My40NTkgNS45NzMsOTMuNjE1IEM2LjA4Nyw5My42NCA2LjE1OCw5My42NTIgNi4xNjksOTMuNjU0IEw2LjE3LDkzLjY1NCBMNi4xNyw5My42NTQgWiIgaWQ9IkZpbGwtMTIiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4zMTcsNjguOTgyIEM3LjgwNiw2OC43MDEgOC4yMDIsNjguOTI2IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNi44MjksNzEuMjk0IDYuNDMzLDcxLjA2OSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIiBpZD0iRmlsbC0xMyIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjkyLDcxLjEzMyBDNi42MzEsNzEuMTMzIDYuNDMzLDcwLjkwNSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIEM3LjQ2LDY4LjkgNy41OTUsNjguODYxIDcuNzE0LDY4Ljg2MSBDOC4wMDMsNjguODYxIDguMjAyLDY5LjA5IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNy4xNzQsNzEuMDk0IDcuMDM5LDcxLjEzMyA2LjkyLDcxLjEzMyBNNy43MTQsNjguNjc0IEM3LjU1Nyw2OC42NzQgNy4zOTIsNjguNzIzIDcuMjI0LDY4LjgyMSBDNi42NzYsNjkuMTM4IDYuMjQ2LDY5Ljg3OSA2LjI0Niw3MC41MDggQzYuMjQ2LDcwLjk5NCA2LjUxNyw3MS4zMiA2LjkyLDcxLjMyIEM3LjA3OCw3MS4zMiA3LjI0Myw3MS4yNzEgNy40MTEsNzEuMTc0IEM3Ljk1OSw3MC44NTcgOC4zODksNzAuMTE3IDguMzg5LDY5LjQ4NyBDOC4zODksNjkuMDAxIDguMTE3LDY4LjY3NCA3LjcxNCw2OC42NzQiIGlkPSJGaWxsLTE0IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYuOTIsNzAuOTQ3IEM2LjY0OSw3MC45NDcgNi42MjEsNzAuNjQgNi42MjEsNzAuNTA4IEM2LjYyMSw3MC4wMTcgNi45ODIsNjkuMzkyIDcuNDExLDY5LjE0NSBDNy41MjEsNjkuMDgyIDcuNjI1LDY5LjA0OSA3LjcxNCw2OS4wNDkgQzcuOTg2LDY5LjA0OSA4LjAxNSw2OS4zNTUgOC4wMTUsNjkuNDg3IEM4LjAxNSw2OS45NzggNy42NTIsNzAuNjAzIDcuMjI0LDcwLjg1MSBDNy4xMTUsNzAuOTE0IDcuMDEsNzAuOTQ3IDYuOTIsNzAuOTQ3IE03LjcxNCw2OC44NjEgQzcuNTk1LDY4Ljg2MSA3LjQ2LDY4LjkgNy4zMTcsNjguOTgyIEM2LjgyOSw2OS4yNjUgNi40MzMsNjkuOTQ4IDYuNDMzLDcwLjUwOCBDNi40MzMsNzAuOTA1IDYuNjMxLDcxLjEzMyA2LjkyLDcxLjEzMyBDNy4wMzksNzEuMTMzIDcuMTc0LDcxLjA5NCA3LjMxNyw3MS4wMTIgQzcuODA2LDcwLjczIDguMjAyLDcwLjA0NyA4LjIwMiw2OS40ODcgQzguMjAyLDY5LjA5IDguMDAzLDY4Ljg2MSA3LjcxNCw2OC44NjEiIGlkPSJGaWxsLTE1IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTcuNDQ0LDg1LjM1IEM3LjcwOCw4NS4xOTggNy45MjEsODUuMzE5IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuOTI1IDcuNzA4LDg2LjI5MiA3LjQ0NCw4Ni40NDQgQzcuMTgxLDg2LjU5NyA2Ljk2Nyw4Ni40NzUgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IiBpZD0iRmlsbC0xNiIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik03LjIzLDg2LjUxIEM3LjA3NCw4Ni41MSA2Ljk2Nyw4Ni4zODcgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IEM3LjUyMSw4NS4zMDUgNy41OTQsODUuMjg0IDcuNjU4LDg1LjI4NCBDNy44MTQsODUuMjg0IDcuOTIxLDg1LjQwOCA3LjkyMSw4NS42MjIgQzcuOTIxLDg1LjkyNSA3LjcwOCw4Ni4yOTIgNy40NDQsODYuNDQ0IEM3LjM2Nyw4Ni40ODkgNy4yOTQsODYuNTEgNy4yMyw4Ni41MSBNNy42NTgsODUuMDk4IEM3LjU1OCw4NS4wOTggNy40NTUsODUuMTI3IDcuMzUxLDg1LjE4OCBDNy4wMzEsODUuMzczIDYuNzgxLDg1LjgwNiA2Ljc4MSw4Ni4xNzMgQzYuNzgxLDg2LjQ4MiA2Ljk2Niw4Ni42OTcgNy4yMyw4Ni42OTcgQzcuMzMsODYuNjk3IDcuNDMzLDg2LjY2NiA3LjUzOCw4Ni42MDcgQzcuODU4LDg2LjQyMiA4LjEwOCw4NS45ODkgOC4xMDgsODUuNjIyIEM4LjEwOCw4NS4zMTMgNy45MjMsODUuMDk4IDcuNjU4LDg1LjA5OCIgaWQ9IkZpbGwtMTciIGZpbGw9IiM4MDk3QTIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4yMyw4Ni4zMjIgTDcuMTU0LDg2LjE3MyBDNy4xNTQsODUuOTM4IDcuMzMzLDg1LjYyOSA3LjUzOCw4NS41MTIgTDcuNjU4LDg1LjQ3MSBMNy43MzQsODUuNjIyIEM3LjczNCw4NS44NTYgNy41NTUsODYuMTY0IDcuMzUxLDg2LjI4MiBMNy4yMyw4Ni4zMjIgTTcuNjU4LDg1LjI4NCBDNy41OTQsODUuMjg0IDcuNTIxLDg1LjMwNSA3LjQ0NCw4NS4zNSBDNy4xODEsODUuNTAyIDYuOTY3LDg1Ljg3MSA2Ljk2Nyw4Ni4xNzMgQzYuOTY3LDg2LjM4NyA3LjA3NCw4Ni41MSA3LjIzLDg2LjUxIEM3LjI5NCw4Ni41MSA3LjM2Nyw4Ni40ODkgNy40NDQsODYuNDQ0IEM3LjcwOCw4Ni4yOTIgNy45MjEsODUuOTI1IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuNDA4IDcuODE0LDg1LjI4NCA3LjY1OCw4NS4yODQiIGlkPSJGaWxsLTE4IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTc3LjI3OCw3Ljc2OSBMNzcuMjc4LDUxLjQzNiBMMTAuMjA4LDkwLjE2IEwxMC4yMDgsNDYuNDkzIEw3Ny4yNzgsNy43NjkiIGlkPSJGaWxsLTE5IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEwLjA4Myw5MC4zNzUgTDEwLjA4Myw0Ni40MjEgTDEwLjE0Niw0Ni4zODUgTDc3LjQwMyw3LjU1NCBMNzcuNDAzLDUxLjUwOCBMNzcuMzQxLDUxLjU0NCBMMTAuMDgzLDkwLjM3NSBMMTAuMDgzLDkwLjM3NSBaIE0xMC4zMzMsNDYuNTY0IEwxMC4zMzMsODkuOTQ0IEw3Ny4xNTQsNTEuMzY1IEw3Ny4xNTQsNy45ODUgTDEwLjMzMyw0Ni41NjQgTDEwLjMzMyw0Ni41NjQgWiIgaWQ9IkZpbGwtMjAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMjUuNzM3LDg4LjY0NyBMMTE4LjA5OCw5MS45ODEgTDExOC4wOTgsODQgTDEwNi42MzksODguNzEzIEwxMDYuNjM5LDk2Ljk4MiBMOTksMTAwLjMxNSBMMTEyLjM2OSwxMDMuOTYxIEwxMjUuNzM3LDg4LjY0NyIgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTIiIGZpbGw9IiM0NTVBNjQiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+'); -}; - -module.exports = RotateInstructions; - -},{"./util.js":26}],21:[function(require,module,exports){ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var SensorSample = require('./sensor-sample.js'); -var MathUtil = require('../math-util.js'); -var Util = require('../util.js'); - -/** - * An implementation of a simple complementary filter, which fuses gyroscope and - * accelerometer data from the 'devicemotion' event. - * - * Accelerometer data is very noisy, but stable over the long term. - * Gyroscope data is smooth, but tends to drift over the long term. - * - * This fusion is relatively simple: - * 1. Get orientation estimates from accelerometer by applying a low-pass filter - * on that data. - * 2. Get orientation estimates from gyroscope by integrating over time. - * 3. Combine the two estimates, weighing (1) in the long term, but (2) for the - * short term. - */ -function ComplementaryFilter(kFilter) { - this.kFilter = kFilter; - - // Raw sensor measurements. - this.currentAccelMeasurement = new SensorSample(); - this.currentGyroMeasurement = new SensorSample(); - this.previousGyroMeasurement = new SensorSample(); - - // Set default look direction to be in the correct direction. - if (Util.isIOS()) { - this.filterQ = new MathUtil.Quaternion(-1, 0, 0, 1); - } else { - this.filterQ = new MathUtil.Quaternion(1, 0, 0, 1); - } - this.previousFilterQ = new MathUtil.Quaternion(); - this.previousFilterQ.copy(this.filterQ); - - // Orientation based on the accelerometer. - this.accelQ = new MathUtil.Quaternion(); - // Whether or not the orientation has been initialized. - this.isOrientationInitialized = false; - // Running estimate of gravity based on the current orientation. - this.estimatedGravity = new MathUtil.Vector3(); - // Measured gravity based on accelerometer. - this.measuredGravity = new MathUtil.Vector3(); - - // Debug only quaternion of gyro-based orientation. - this.gyroIntegralQ = new MathUtil.Quaternion(); -} - -ComplementaryFilter.prototype.addAccelMeasurement = function(vector, timestampS) { - this.currentAccelMeasurement.set(vector, timestampS); -}; - -ComplementaryFilter.prototype.addGyroMeasurement = function(vector, timestampS) { - this.currentGyroMeasurement.set(vector, timestampS); - - var deltaT = timestampS - this.previousGyroMeasurement.timestampS; - if (Util.isTimestampDeltaValid(deltaT)) { - this.run_(); - } - - this.previousGyroMeasurement.copy(this.currentGyroMeasurement); -}; - -ComplementaryFilter.prototype.run_ = function() { - - if (!this.isOrientationInitialized) { - this.accelQ = this.accelToQuaternion_(this.currentAccelMeasurement.sample); - this.previousFilterQ.copy(this.accelQ); - this.isOrientationInitialized = true; - return; - } - - var deltaT = this.currentGyroMeasurement.timestampS - - this.previousGyroMeasurement.timestampS; - - // Convert gyro rotation vector to a quaternion delta. - var gyroDeltaQ = this.gyroToQuaternionDelta_(this.currentGyroMeasurement.sample, deltaT); - this.gyroIntegralQ.multiply(gyroDeltaQ); - - // filter_1 = K * (filter_0 + gyro * dT) + (1 - K) * accel. - this.filterQ.copy(this.previousFilterQ); - this.filterQ.multiply(gyroDeltaQ); - - // Calculate the delta between the current estimated gravity and the real - // gravity vector from accelerometer. - var invFilterQ = new MathUtil.Quaternion(); - invFilterQ.copy(this.filterQ); - invFilterQ.inverse(); - - this.estimatedGravity.set(0, 0, -1); - this.estimatedGravity.applyQuaternion(invFilterQ); - this.estimatedGravity.normalize(); - - this.measuredGravity.copy(this.currentAccelMeasurement.sample); - this.measuredGravity.normalize(); - - // Compare estimated gravity with measured gravity, get the delta quaternion - // between the two. - var deltaQ = new MathUtil.Quaternion(); - deltaQ.setFromUnitVectors(this.estimatedGravity, this.measuredGravity); - deltaQ.inverse(); - - if (Util.isDebug()) { - console.log('Delta: %d deg, G_est: (%s, %s, %s), G_meas: (%s, %s, %s)', - MathUtil.radToDeg * Util.getQuaternionAngle(deltaQ), - (this.estimatedGravity.x).toFixed(1), - (this.estimatedGravity.y).toFixed(1), - (this.estimatedGravity.z).toFixed(1), - (this.measuredGravity.x).toFixed(1), - (this.measuredGravity.y).toFixed(1), - (this.measuredGravity.z).toFixed(1)); - } - - // Calculate the SLERP target: current orientation plus the measured-estimated - // quaternion delta. - var targetQ = new MathUtil.Quaternion(); - targetQ.copy(this.filterQ); - targetQ.multiply(deltaQ); - - // SLERP factor: 0 is pure gyro, 1 is pure accel. - this.filterQ.slerp(targetQ, 1 - this.kFilter); - - this.previousFilterQ.copy(this.filterQ); -}; - -ComplementaryFilter.prototype.getOrientation = function() { - return this.filterQ; -}; - -ComplementaryFilter.prototype.accelToQuaternion_ = function(accel) { - var normAccel = new MathUtil.Vector3(); - normAccel.copy(accel); - normAccel.normalize(); - var quat = new MathUtil.Quaternion(); - quat.setFromUnitVectors(new MathUtil.Vector3(0, 0, -1), normAccel); - quat.inverse(); - return quat; -}; - -ComplementaryFilter.prototype.gyroToQuaternionDelta_ = function(gyro, dt) { - // Extract axis and angle from the gyroscope data. - var quat = new MathUtil.Quaternion(); - var axis = new MathUtil.Vector3(); - axis.copy(gyro); - axis.normalize(); - quat.setFromAxisAngle(axis, gyro.length() * dt); - return quat; -}; - - -module.exports = ComplementaryFilter; - -},{"../math-util.js":17,"../util.js":26,"./sensor-sample.js":24}],22:[function(require,module,exports){ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var ComplementaryFilter = require('./complementary-filter.js'); -var PosePredictor = require('./pose-predictor.js'); -var TouchPanner = require('../touch-panner.js'); -var MathUtil = require('../math-util.js'); -var Util = require('../util.js'); - -/** - * The pose sensor, implemented using DeviceMotion APIs. - */ -function FusionPoseSensor() { - this.deviceId = 'webvr-polyfill:fused'; - this.deviceName = 'VR Position Device (webvr-polyfill:fused)'; - - this.accelerometer = new MathUtil.Vector3(); - this.gyroscope = new MathUtil.Vector3(); - - this.start(); - - this.filter = new ComplementaryFilter(window.WebVRConfig.K_FILTER); - this.posePredictor = new PosePredictor(window.WebVRConfig.PREDICTION_TIME_S); - this.touchPanner = new TouchPanner(); - - this.filterToWorldQ = new MathUtil.Quaternion(); - - // Set the filter to world transform, depending on OS. - if (Util.isIOS()) { - this.filterToWorldQ.setFromAxisAngle(new MathUtil.Vector3(1, 0, 0), Math.PI / 2); - } else { - this.filterToWorldQ.setFromAxisAngle(new MathUtil.Vector3(1, 0, 0), -Math.PI / 2); - } - - this.inverseWorldToScreenQ = new MathUtil.Quaternion(); - this.worldToScreenQ = new MathUtil.Quaternion(); - this.originalPoseAdjustQ = new MathUtil.Quaternion(); - this.originalPoseAdjustQ.setFromAxisAngle(new MathUtil.Vector3(0, 0, 1), - -window.orientation * Math.PI / 180); - - this.setScreenTransform_(); - // Adjust this filter for being in landscape mode. - if (Util.isLandscapeMode()) { - this.filterToWorldQ.multiply(this.inverseWorldToScreenQ); - } - - // Keep track of a reset transform for resetSensor. - this.resetQ = new MathUtil.Quaternion(); - - this.isFirefoxAndroid = Util.isFirefoxAndroid(); - this.isIOS = Util.isIOS(); - - this.orientationOut_ = new Float32Array(4); -} - -FusionPoseSensor.prototype.getPosition = function() { - // This PoseSensor doesn't support position - return null; -}; - -FusionPoseSensor.prototype.getOrientation = function() { - // Convert from filter space to the the same system used by the - // deviceorientation event. - var orientation = this.filter.getOrientation(); - - // Predict orientation. - this.predictedQ = this.posePredictor.getPrediction(orientation, this.gyroscope, this.previousTimestampS); - - // Convert to THREE coordinate system: -Z forward, Y up, X right. - var out = new MathUtil.Quaternion(); - out.copy(this.filterToWorldQ); - out.multiply(this.resetQ); - if (!window.WebVRConfig.TOUCH_PANNER_DISABLED) { - out.multiply(this.touchPanner.getOrientation()); - } - out.multiply(this.predictedQ); - out.multiply(this.worldToScreenQ); - - // Handle the yaw-only case. - if (window.WebVRConfig.YAW_ONLY) { - // Make a quaternion that only turns around the Y-axis. - out.x = 0; - out.z = 0; - out.normalize(); - } - - this.orientationOut_[0] = out.x; - this.orientationOut_[1] = out.y; - this.orientationOut_[2] = out.z; - this.orientationOut_[3] = out.w; - return this.orientationOut_; -}; - -FusionPoseSensor.prototype.resetPose = function() { - // Reduce to inverted yaw-only. - this.resetQ.copy(this.filter.getOrientation()); - this.resetQ.x = 0; - this.resetQ.y = 0; - this.resetQ.z *= -1; - this.resetQ.normalize(); - - // Take into account extra transformations in landscape mode. - if (Util.isLandscapeMode()) { - this.resetQ.multiply(this.inverseWorldToScreenQ); - } - - // Take into account original pose. - this.resetQ.multiply(this.originalPoseAdjustQ); - - if (!window.WebVRConfig.TOUCH_PANNER_DISABLED) { - this.touchPanner.resetSensor(); - } -}; - -FusionPoseSensor.prototype.onDeviceMotion_ = function(deviceMotion) { - this.updateDeviceMotion_(deviceMotion); -}; - -FusionPoseSensor.prototype.updateDeviceMotion_ = function(deviceMotion) { - var accGravity = deviceMotion.accelerationIncludingGravity; - var rotRate = deviceMotion.rotationRate; - var timestampS = deviceMotion.timeStamp / 1000; - - // Firefox Android timeStamp returns one thousandth of a millisecond. - if (this.isFirefoxAndroid) { - timestampS /= 1000; - } - - var deltaS = timestampS - this.previousTimestampS; - if (deltaS <= Util.MIN_TIMESTEP || deltaS > Util.MAX_TIMESTEP) { - console.warn('Invalid timestamps detected. Time step between successive ' + - 'gyroscope sensor samples is very small or not monotonic'); - this.previousTimestampS = timestampS; - return; - } - this.accelerometer.set(-accGravity.x, -accGravity.y, -accGravity.z); - this.gyroscope.set(rotRate.alpha, rotRate.beta, rotRate.gamma); - - // With iOS and Firefox Android, rotationRate is reported in degrees, - // so we first convert to radians. - if (this.isIOS || this.isFirefoxAndroid) { - this.gyroscope.multiplyScalar(Math.PI / 180); - } - - this.filter.addAccelMeasurement(this.accelerometer, timestampS); - this.filter.addGyroMeasurement(this.gyroscope, timestampS); - - this.previousTimestampS = timestampS; -}; - -FusionPoseSensor.prototype.onOrientationChange_ = function(screenOrientation) { - this.setScreenTransform_(); -}; - -/** - * This is only needed if we are in an cross origin iframe on iOS to work around - * this issue: https://bugs.webkit.org/show_bug.cgi?id=152299. - */ -FusionPoseSensor.prototype.onMessage_ = function(event) { - var message = event.data; - - // If there's no message type, ignore it. - if (!message || !message.type) { - return; - } - - // Ignore all messages that aren't devicemotion. - var type = message.type.toLowerCase(); - if (type !== 'devicemotion') { - return; - } - - // Update device motion. - this.updateDeviceMotion_(message.deviceMotionEvent); -}; - -FusionPoseSensor.prototype.setScreenTransform_ = function() { - this.worldToScreenQ.set(0, 0, 0, 1); - switch (window.orientation) { - case 0: - break; - case 90: - this.worldToScreenQ.setFromAxisAngle(new MathUtil.Vector3(0, 0, 1), -Math.PI / 2); - break; - case -90: - this.worldToScreenQ.setFromAxisAngle(new MathUtil.Vector3(0, 0, 1), Math.PI / 2); - break; - case 180: - // TODO. - break; - } - this.inverseWorldToScreenQ.copy(this.worldToScreenQ); - this.inverseWorldToScreenQ.inverse(); -}; - -FusionPoseSensor.prototype.start = function() { - this.onDeviceMotionCallback_ = this.onDeviceMotion_.bind(this); - this.onOrientationChangeCallback_ = this.onOrientationChange_.bind(this); - this.onMessageCallback_ = this.onMessage_.bind(this); - - // Only listen for postMessages if we're in an iOS and embedded inside a cross - // domain IFrame. In this case, the polyfill can still work if the containing - // page sends synthetic devicemotion events. For an example of this, see - // iframe-message-sender.js in VR View: https://goo.gl/XDtvFZ - if (Util.isIOS() && Util.isInsideCrossDomainIFrame()) { - window.addEventListener('message', this.onMessageCallback_); - } - window.addEventListener('orientationchange', this.onOrientationChangeCallback_); - window.addEventListener('devicemotion', this.onDeviceMotionCallback_); -}; - -FusionPoseSensor.prototype.stop = function() { - window.removeEventListener('devicemotion', this.onDeviceMotionCallback_); - window.removeEventListener('orientationchange', this.onOrientationChangeCallback_); - window.removeEventListener('message', this.onMessageCallback_); -}; - -module.exports = FusionPoseSensor; - -},{"../math-util.js":17,"../touch-panner.js":25,"../util.js":26,"./complementary-filter.js":21,"./pose-predictor.js":23}],23:[function(require,module,exports){ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var MathUtil = require('../math-util'); -var Util = require('../util'); - -/** - * Given an orientation and the gyroscope data, predicts the future orientation - * of the head. This makes rendering appear faster. - * - * Also see: http://msl.cs.uiuc.edu/~lavalle/papers/LavYerKatAnt14.pdf - * - * @param {Number} predictionTimeS time from head movement to the appearance of - * the corresponding image. - */ -function PosePredictor(predictionTimeS) { - this.predictionTimeS = predictionTimeS; - - // The quaternion corresponding to the previous state. - this.previousQ = new MathUtil.Quaternion(); - // Previous time a prediction occurred. - this.previousTimestampS = null; - - // The delta quaternion that adjusts the current pose. - this.deltaQ = new MathUtil.Quaternion(); - // The output quaternion. - this.outQ = new MathUtil.Quaternion(); -} - -PosePredictor.prototype.getPrediction = function(currentQ, gyro, timestampS) { - if (!this.previousTimestampS) { - this.previousQ.copy(currentQ); - this.previousTimestampS = timestampS; - return currentQ; - } - - // Calculate axis and angle based on gyroscope rotation rate data. - var axis = new MathUtil.Vector3(); - axis.copy(gyro); - axis.normalize(); - - var angularSpeed = gyro.length(); - - // If we're rotating slowly, don't do prediction. - if (angularSpeed < MathUtil.degToRad * 20) { - if (Util.isDebug()) { - console.log('Moving slowly, at %s deg/s: no prediction', - (MathUtil.radToDeg * angularSpeed).toFixed(1)); - } - this.outQ.copy(currentQ); - this.previousQ.copy(currentQ); - return this.outQ; - } - - // Get the predicted angle based on the time delta and latency. - var deltaT = timestampS - this.previousTimestampS; - var predictAngle = angularSpeed * this.predictionTimeS; - - this.deltaQ.setFromAxisAngle(axis, predictAngle); - this.outQ.copy(this.previousQ); - this.outQ.multiply(this.deltaQ); - - this.previousQ.copy(currentQ); - this.previousTimestampS = timestampS; - - return this.outQ; -}; - - -module.exports = PosePredictor; - -},{"../math-util":17,"../util":26}],24:[function(require,module,exports){ -function SensorSample(sample, timestampS) { - this.set(sample, timestampS); -}; - -SensorSample.prototype.set = function(sample, timestampS) { - this.sample = sample; - this.timestampS = timestampS; -}; - -SensorSample.prototype.copy = function(sensorSample) { - this.set(sensorSample.sample, sensorSample.timestampS); -}; - -module.exports = SensorSample; - -},{}],25:[function(require,module,exports){ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -var MathUtil = require('./math-util.js'); -var Util = require('./util.js'); - -var ROTATE_SPEED = 0.5; -/** - * Provides a quaternion responsible for pre-panning the scene before further - * transformations due to device sensors. - */ -function TouchPanner() { - window.addEventListener('touchstart', this.onTouchStart_.bind(this)); - window.addEventListener('touchmove', this.onTouchMove_.bind(this)); - window.addEventListener('touchend', this.onTouchEnd_.bind(this)); - - this.isTouching = false; - this.rotateStart = new MathUtil.Vector2(); - this.rotateEnd = new MathUtil.Vector2(); - this.rotateDelta = new MathUtil.Vector2(); - - this.theta = 0; - this.orientation = new MathUtil.Quaternion(); -} - -TouchPanner.prototype.getOrientation = function() { - this.orientation.setFromEulerXYZ(0, 0, this.theta); - return this.orientation; -}; - -TouchPanner.prototype.resetSensor = function() { - this.theta = 0; -}; - -TouchPanner.prototype.onTouchStart_ = function(e) { - // Only respond if there is exactly one touch. - // Note that the Daydream controller passes in a `touchstart` event with - // no `touches` property, so we must check for that case too. - if (!e.touches || e.touches.length != 1) { - return; - } - this.rotateStart.set(e.touches[0].pageX, e.touches[0].pageY); - this.isTouching = true; -}; - -TouchPanner.prototype.onTouchMove_ = function(e) { - if (!this.isTouching) { - return; - } - this.rotateEnd.set(e.touches[0].pageX, e.touches[0].pageY); - this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart); - this.rotateStart.copy(this.rotateEnd); - - // On iOS, direction is inverted. - if (Util.isIOS()) { - this.rotateDelta.x *= -1; - } - - var element = document.body; - this.theta += 2 * Math.PI * this.rotateDelta.x / element.clientWidth * ROTATE_SPEED; -}; - -TouchPanner.prototype.onTouchEnd_ = function(e) { - this.isTouching = false; -}; - -module.exports = TouchPanner; - -},{"./math-util.js":17,"./util.js":26}],26:[function(require,module,exports){ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var Util = window.Util || {}; - -Util.MIN_TIMESTEP = 0.001; -Util.MAX_TIMESTEP = 1; - -Util.base64 = function(mimeType, base64) { - return 'data:' + mimeType + ';base64,' + base64; -}; - -Util.clamp = function(value, min, max) { - return Math.min(Math.max(min, value), max); -}; - -Util.lerp = function(a, b, t) { - return a + ((b - a) * t); -}; - -/** - * Light polyfill for `Promise.race`. Returns - * a promise that resolves when the first promise - * provided resolves. - * - * @param {Array} promises - */ -Util.race = function(promises) { - if (Promise.race) { - return Promise.race(promises); - } - - return new Promise(function (resolve, reject) { - for (var i = 0; i < promises.length; i++) { - promises[i].then(resolve, reject); - } - }); -}; - -Util.isIOS = (function() { - var isIOS = /iPad|iPhone|iPod/.test(navigator.platform); - return function() { - return isIOS; - }; -})(); - -Util.isWebViewAndroid = (function() { - var isWebViewAndroid = navigator.userAgent.indexOf('Version') !== -1 && - navigator.userAgent.indexOf('Android') !== -1 && - navigator.userAgent.indexOf('Chrome') !== -1; - return function() { - return isWebViewAndroid; - }; -})(); - -Util.isSafari = (function() { - var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); - return function() { - return isSafari; - }; -})(); - -Util.isFirefoxAndroid = (function() { - var isFirefoxAndroid = navigator.userAgent.indexOf('Firefox') !== -1 && - navigator.userAgent.indexOf('Android') !== -1; - return function() { - return isFirefoxAndroid; - }; -})(); - -Util.isLandscapeMode = function() { - return (window.orientation == 90 || window.orientation == -90); -}; - -// Helper method to validate the time steps of sensor timestamps. -Util.isTimestampDeltaValid = function(timestampDeltaS) { - if (isNaN(timestampDeltaS)) { - return false; - } - if (timestampDeltaS <= Util.MIN_TIMESTEP) { - return false; - } - if (timestampDeltaS > Util.MAX_TIMESTEP) { - return false; - } - return true; -}; - -Util.getScreenWidth = function() { - return Math.max(window.screen.width, window.screen.height) * - window.devicePixelRatio; -}; - -Util.getScreenHeight = function() { - return Math.min(window.screen.width, window.screen.height) * - window.devicePixelRatio; -}; - -Util.requestFullscreen = function(element) { - if (Util.isWebViewAndroid()) { - return false; - } - if (element.requestFullscreen) { - element.requestFullscreen(); - } else if (element.webkitRequestFullscreen) { - element.webkitRequestFullscreen(); - } else if (element.mozRequestFullScreen) { - element.mozRequestFullScreen(); - } else if (element.msRequestFullscreen) { - element.msRequestFullscreen(); - } else { - return false; - } - - return true; -}; - -Util.exitFullscreen = function() { - if (document.exitFullscreen) { - document.exitFullscreen(); - } else if (document.webkitExitFullscreen) { - document.webkitExitFullscreen(); - } else if (document.mozCancelFullScreen) { - document.mozCancelFullScreen(); - } else if (document.msExitFullscreen) { - document.msExitFullscreen(); - } else { - return false; - } - - return true; -}; - -Util.getFullscreenElement = function() { - return document.fullscreenElement || - document.webkitFullscreenElement || - document.mozFullScreenElement || - document.msFullscreenElement; -}; - -Util.linkProgram = function(gl, vertexSource, fragmentSource, attribLocationMap) { - // No error checking for brevity. - var vertexShader = gl.createShader(gl.VERTEX_SHADER); - gl.shaderSource(vertexShader, vertexSource); - gl.compileShader(vertexShader); - - var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); - gl.shaderSource(fragmentShader, fragmentSource); - gl.compileShader(fragmentShader); - - var program = gl.createProgram(); - gl.attachShader(program, vertexShader); - gl.attachShader(program, fragmentShader); - - for (var attribName in attribLocationMap) - gl.bindAttribLocation(program, attribLocationMap[attribName], attribName); - - gl.linkProgram(program); - - gl.deleteShader(vertexShader); - gl.deleteShader(fragmentShader); - - return program; -}; - -Util.getProgramUniforms = function(gl, program) { - var uniforms = {}; - var uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); - var uniformName = ''; - for (var i = 0; i < uniformCount; i++) { - var uniformInfo = gl.getActiveUniform(program, i); - uniformName = uniformInfo.name.replace('[0]', ''); - uniforms[uniformName] = gl.getUniformLocation(program, uniformName); - } - return uniforms; -}; - -Util.orthoMatrix = function (out, left, right, bottom, top, near, far) { - var lr = 1 / (left - right), - bt = 1 / (bottom - top), - nf = 1 / (near - far); - out[0] = -2 * lr; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = -2 * bt; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[10] = 2 * nf; - out[11] = 0; - out[12] = (left + right) * lr; - out[13] = (top + bottom) * bt; - out[14] = (far + near) * nf; - out[15] = 1; - return out; -}; - -Util.copyArray = function (source, dest) { - for (var i = 0, n = source.length; i < n; i++) { - dest[i] = source[i]; - } -}; - -Util.isMobile = function() { - var check = false; - (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); - return check; -}; - -Util.extend = function(dest, src) { - for (var key in src) { - if (src.hasOwnProperty(key)) { - dest[key] = src[key]; - } - } - - return dest; -} - -Util.safariCssSizeWorkaround = function(canvas) { - // TODO(smus): Remove this workaround when Safari for iOS is fixed. - // iOS only workaround (for https://bugs.webkit.org/show_bug.cgi?id=152556). - // - // "To the last I grapple with thee; - // from hell's heart I stab at thee; - // for hate's sake I spit my last breath at thee." - // -- Moby Dick, by Herman Melville - if (Util.isIOS()) { - var width = canvas.style.width; - var height = canvas.style.height; - canvas.style.width = (parseInt(width) + 1) + 'px'; - canvas.style.height = (parseInt(height)) + 'px'; - setTimeout(function() { - canvas.style.width = width; - canvas.style.height = height; - }, 100); - } - - // Debug only. - window.Util = Util; - window.canvas = canvas; -}; - -Util.isDebug = function() { - return Util.getQueryParameter('debug'); -}; - -Util.getQueryParameter = function(name) { - var name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), - results = regex.exec(location.search); - return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); -}; - -Util.frameDataFromPose = (function() { - var piOver180 = Math.PI / 180.0; - var rad45 = Math.PI * 0.25; - - // Borrowed from glMatrix. - function mat4_perspectiveFromFieldOfView(out, fov, near, far) { - var upTan = Math.tan(fov ? (fov.upDegrees * piOver180) : rad45), - downTan = Math.tan(fov ? (fov.downDegrees * piOver180) : rad45), - leftTan = Math.tan(fov ? (fov.leftDegrees * piOver180) : rad45), - rightTan = Math.tan(fov ? (fov.rightDegrees * piOver180) : rad45), - xScale = 2.0 / (leftTan + rightTan), - yScale = 2.0 / (upTan + downTan); - - out[0] = xScale; - out[1] = 0.0; - out[2] = 0.0; - out[3] = 0.0; - out[4] = 0.0; - out[5] = yScale; - out[6] = 0.0; - out[7] = 0.0; - out[8] = -((leftTan - rightTan) * xScale * 0.5); - out[9] = ((upTan - downTan) * yScale * 0.5); - out[10] = far / (near - far); - out[11] = -1.0; - out[12] = 0.0; - out[13] = 0.0; - out[14] = (far * near) / (near - far); - out[15] = 0.0; - return out; - } - - function mat4_fromRotationTranslation(out, q, v) { - // Quaternion math - var x = q[0], y = q[1], z = q[2], w = q[3], - x2 = x + x, - y2 = y + y, - z2 = z + z, - - xx = x * x2, - xy = x * y2, - xz = x * z2, - yy = y * y2, - yz = y * z2, - zz = z * z2, - wx = w * x2, - wy = w * y2, - wz = w * z2; - - out[0] = 1 - (yy + zz); - out[1] = xy + wz; - out[2] = xz - wy; - out[3] = 0; - out[4] = xy - wz; - out[5] = 1 - (xx + zz); - out[6] = yz + wx; - out[7] = 0; - out[8] = xz + wy; - out[9] = yz - wx; - out[10] = 1 - (xx + yy); - out[11] = 0; - out[12] = v[0]; - out[13] = v[1]; - out[14] = v[2]; - out[15] = 1; - - return out; - }; - - function mat4_translate(out, a, v) { - var x = v[0], y = v[1], z = v[2], - a00, a01, a02, a03, - a10, a11, a12, a13, - a20, a21, a22, a23; - - if (a === out) { - out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; - out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; - out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; - out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; - } else { - a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; - a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; - a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; - - out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03; - out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13; - out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23; - - out[12] = a00 * x + a10 * y + a20 * z + a[12]; - out[13] = a01 * x + a11 * y + a21 * z + a[13]; - out[14] = a02 * x + a12 * y + a22 * z + a[14]; - out[15] = a03 * x + a13 * y + a23 * z + a[15]; - } - - return out; - }; - - function mat4_invert(out, a) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], - - b00 = a00 * a11 - a01 * a10, - b01 = a00 * a12 - a02 * a10, - b02 = a00 * a13 - a03 * a10, - b03 = a01 * a12 - a02 * a11, - b04 = a01 * a13 - a03 * a11, - b05 = a02 * a13 - a03 * a12, - b06 = a20 * a31 - a21 * a30, - b07 = a20 * a32 - a22 * a30, - b08 = a20 * a33 - a23 * a30, - b09 = a21 * a32 - a22 * a31, - b10 = a21 * a33 - a23 * a31, - b11 = a22 * a33 - a23 * a32, - - // Calculate the determinant - det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; - - if (!det) { - return null; - } - det = 1.0 / det; - - out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; - out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; - out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; - out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; - out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; - out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; - out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; - out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; - out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; - out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; - out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; - out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; - out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; - out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; - out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; - out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; - - return out; - }; - - var defaultOrientation = new Float32Array([0, 0, 0, 1]); - var defaultPosition = new Float32Array([0, 0, 0]); - - function updateEyeMatrices(projection, view, pose, parameters, vrDisplay) { - mat4_perspectiveFromFieldOfView(projection, parameters ? parameters.fieldOfView : null, vrDisplay.depthNear, vrDisplay.depthFar); - - var orientation = pose.orientation || defaultOrientation; - var position = pose.position || defaultPosition; - - mat4_fromRotationTranslation(view, orientation, position); - if (parameters) - mat4_translate(view, view, parameters.offset); - mat4_invert(view, view); - } - - return function(frameData, pose, vrDisplay) { - if (!frameData || !pose) - return false; - - frameData.pose = pose; - frameData.timestamp = pose.timestamp; - - updateEyeMatrices( - frameData.leftProjectionMatrix, frameData.leftViewMatrix, - pose, vrDisplay.getEyeParameters("left"), vrDisplay); - updateEyeMatrices( - frameData.rightProjectionMatrix, frameData.rightViewMatrix, - pose, vrDisplay.getEyeParameters("right"), vrDisplay); - - return true; - }; -})(); - -Util.isInsideCrossDomainIFrame = function() { - var isFramed = (window.self !== window.top); - var refDomain = Util.getDomainFromUrl(document.referrer); - var thisDomain = Util.getDomainFromUrl(window.location.href); - - return isFramed && (refDomain !== thisDomain); -}; - -// From http://stackoverflow.com/a/23945027. -Util.getDomainFromUrl = function(url) { - var domain; - // Find & remove protocol (http, ftp, etc.) and get domain. - if (url.indexOf("://") > -1) { - domain = url.split('/')[2]; - } - else { - domain = url.split('/')[0]; - } - - //find & remove port number - domain = domain.split(':')[0]; - - return domain; -} - -module.exports = Util; - -},{}],27:[function(require,module,exports){ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var DeviceInfo = require('./device-info.js'); -var Util = require('./util.js'); - -var DEFAULT_VIEWER = 'CardboardV1'; -var VIEWER_KEY = 'WEBVR_CARDBOARD_VIEWER'; -var CLASS_NAME = 'webvr-polyfill-viewer-selector'; - -/** - * Creates a viewer selector with the options specified. Supports being shown - * and hidden. Generates events when viewer parameters change. Also supports - * saving the currently selected index in localStorage. - */ -function ViewerSelector() { - // Try to load the selected key from local storage. - try { - this.selectedKey = localStorage.getItem(VIEWER_KEY); - } catch (error) { - console.error('Failed to load viewer profile: %s', error); - } - - //If none exists, or if localstorage is unavailable, use the default key. - if (!this.selectedKey) { - this.selectedKey = DEFAULT_VIEWER; - } - - this.dialog = this.createDialog_(DeviceInfo.Viewers); - this.root = null; - this.onChangeCallbacks_ = []; -} - -ViewerSelector.prototype.show = function(root) { - this.root = root; - - root.appendChild(this.dialog); - - // Ensure the currently selected item is checked. - var selected = this.dialog.querySelector('#' + this.selectedKey); - selected.checked = true; - - // Show the UI. - this.dialog.style.display = 'block'; -}; - -ViewerSelector.prototype.hide = function() { - if (this.root && this.root.contains(this.dialog)) { - this.root.removeChild(this.dialog); - } - this.dialog.style.display = 'none'; -}; - -ViewerSelector.prototype.getCurrentViewer = function() { - return DeviceInfo.Viewers[this.selectedKey]; -}; - -ViewerSelector.prototype.getSelectedKey_ = function() { - var input = this.dialog.querySelector('input[name=field]:checked'); - if (input) { - return input.id; - } - return null; -}; - -ViewerSelector.prototype.onChange = function(cb) { - this.onChangeCallbacks_.push(cb); -}; - -ViewerSelector.prototype.fireOnChange_ = function(viewer) { - for (var i = 0; i < this.onChangeCallbacks_.length; i++) { - this.onChangeCallbacks_[i](viewer); - } -}; - -ViewerSelector.prototype.onSave_ = function() { - this.selectedKey = this.getSelectedKey_(); - if (!this.selectedKey || !DeviceInfo.Viewers[this.selectedKey]) { - console.error('ViewerSelector.onSave_: this should never happen!'); - return; - } - - this.fireOnChange_(DeviceInfo.Viewers[this.selectedKey]); - - // Attempt to save the viewer profile, but fails in private mode. - try { - localStorage.setItem(VIEWER_KEY, this.selectedKey); - } catch(error) { - console.error('Failed to save viewer profile: %s', error); - } - this.hide(); -}; - -/** - * Creates the dialog. - */ -ViewerSelector.prototype.createDialog_ = function(options) { - var container = document.createElement('div'); - container.classList.add(CLASS_NAME); - container.style.display = 'none'; - // Create an overlay that dims the background, and which goes away when you - // tap it. - var overlay = document.createElement('div'); - var s = overlay.style; - s.position = 'fixed'; - s.left = 0; - s.top = 0; - s.width = '100%'; - s.height = '100%'; - s.background = 'rgba(0, 0, 0, 0.3)'; - overlay.addEventListener('click', this.hide.bind(this)); - - var width = 280; - var dialog = document.createElement('div'); - var s = dialog.style; - s.boxSizing = 'border-box'; - s.position = 'fixed'; - s.top = '24px'; - s.left = '50%'; - s.marginLeft = (-width/2) + 'px'; - s.width = width + 'px'; - s.padding = '24px'; - s.overflow = 'hidden'; - s.background = '#fafafa'; - s.fontFamily = "'Roboto', sans-serif"; - s.boxShadow = '0px 5px 20px #666'; - - dialog.appendChild(this.createH1_('Select your viewer')); - for (var id in options) { - dialog.appendChild(this.createChoice_(id, options[id].label)); - } - dialog.appendChild(this.createButton_('Save', this.onSave_.bind(this))); - - container.appendChild(overlay); - container.appendChild(dialog); - - return container; -}; - -ViewerSelector.prototype.createH1_ = function(name) { - var h1 = document.createElement('h1'); - var s = h1.style; - s.color = 'black'; - s.fontSize = '20px'; - s.fontWeight = 'bold'; - s.marginTop = 0; - s.marginBottom = '24px'; - h1.innerHTML = name; - return h1; -}; - -ViewerSelector.prototype.createChoice_ = function(id, name) { - /* -

- - -
- */ - var div = document.createElement('div'); - div.style.marginTop = '8px'; - div.style.color = 'black'; - - var input = document.createElement('input'); - input.style.fontSize = '30px'; - input.setAttribute('id', id); - input.setAttribute('type', 'radio'); - input.setAttribute('value', id); - input.setAttribute('name', 'field'); - - var label = document.createElement('label'); - label.style.marginLeft = '4px'; - label.setAttribute('for', id); - label.innerHTML = name; - - div.appendChild(input); - div.appendChild(label); - - return div; -}; - -ViewerSelector.prototype.createButton_ = function(label, onclick) { - var button = document.createElement('button'); - button.innerHTML = label; - var s = button.style; - s.float = 'right'; - s.textTransform = 'uppercase'; - s.color = '#1094f7'; - s.fontSize = '14px'; - s.letterSpacing = 0; - s.border = 0; - s.background = 'none'; - s.marginTop = '16px'; - - button.addEventListener('click', onclick); - - return button; -}; - -module.exports = ViewerSelector; - -},{"./device-info.js":11,"./util.js":26}],28:[function(require,module,exports){ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var Util = require('./util.js'); - -/** - * Android and iOS compatible wakelock implementation. - * - * Refactored thanks to dkovalev@. - */ -function AndroidWakeLock() { - var video = document.createElement('video'); - video.setAttribute('loop', ''); - - function addSourceToVideo(element, type, dataURI) { - var source = document.createElement('source'); - source.src = dataURI; - source.type = 'video/' + type; - element.appendChild(source); - } - - addSourceToVideo(video,'webm', Util.base64('video/webm', 'GkXfo0AgQoaBAUL3gQFC8oEEQvOBCEKCQAR3ZWJtQoeBAkKFgQIYU4BnQI0VSalmQCgq17FAAw9CQE2AQAZ3aGFtbXlXQUAGd2hhbW15RIlACECPQAAAAAAAFlSua0AxrkAu14EBY8WBAZyBACK1nEADdW5khkAFVl9WUDglhohAA1ZQOIOBAeBABrCBCLqBCB9DtnVAIueBAKNAHIEAAIAwAQCdASoIAAgAAUAmJaQAA3AA/vz0AAA=')); - addSourceToVideo(video, 'mp4', Util.base64('video/mp4', 'AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAAG21kYXQAAAGzABAHAAABthADAowdbb9/AAAC6W1vb3YAAABsbXZoZAAAAAB8JbCAfCWwgAAAA+gAAAAAAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIVdHJhawAAAFx0a2hkAAAAD3wlsIB8JbCAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAIAAAACAAAAAABsW1kaWEAAAAgbWRoZAAAAAB8JbCAfCWwgAAAA+gAAAAAVcQAAAAAAC1oZGxyAAAAAAAAAAB2aWRlAAAAAAAAAAAAAAAAVmlkZW9IYW5kbGVyAAAAAVxtaW5mAAAAFHZtaGQAAAABAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAAEcc3RibAAAALhzdHNkAAAAAAAAAAEAAACobXA0dgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAIAAgASAAAAEgAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABj//wAAAFJlc2RzAAAAAANEAAEABDwgEQAAAAADDUAAAAAABS0AAAGwAQAAAbWJEwAAAQAAAAEgAMSNiB9FAEQBFGMAAAGyTGF2YzUyLjg3LjQGAQIAAAAYc3R0cwAAAAAAAAABAAAAAQAAAAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAEAAAABAAAAFHN0c3oAAAAAAAAAEwAAAAEAAAAUc3RjbwAAAAAAAAABAAAALAAAAGB1ZHRhAAAAWG1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAAK2lsc3QAAAAjqXRvbwAAABtkYXRhAAAAAQAAAABMYXZmNTIuNzguMw==')); - - this.request = function() { - if (video.paused) { - video.play(); - } - }; - - this.release = function() { - video.pause(); - }; -} - -function iOSWakeLock() { - var timer = null; - - this.request = function() { - if (!timer) { - timer = setInterval(function() { - window.location = window.location; - setTimeout(window.stop, 0); - }, 30000); - } - } - - this.release = function() { - if (timer) { - clearInterval(timer); - timer = null; - } - } -} - - -function getWakeLock() { - var userAgent = navigator.userAgent || navigator.vendor || window.opera; - if (userAgent.match(/iPhone/i) || userAgent.match(/iPod/i)) { - return iOSWakeLock; - } else { - return AndroidWakeLock; - } -} - -module.exports = getWakeLock(); -},{"./util.js":26}],29:[function(require,module,exports){ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var Util = require('./util.js'); -var CardboardVRDisplay = require('./cardboard-vr-display.js'); -var MouseKeyboardVRDisplay = require('./mouse-keyboard-vr-display.js'); -// Uncomment to add positional tracking via webcam. -//var WebcamPositionSensorVRDevice = require('./webcam-position-sensor-vr-device.js'); -var VRDisplay = require('./base.js').VRDisplay; -var VRFrameData = require('./base.js').VRFrameData; -var HMDVRDevice = require('./base.js').HMDVRDevice; -var PositionSensorVRDevice = require('./base.js').PositionSensorVRDevice; -var VRDisplayHMDDevice = require('./display-wrappers.js').VRDisplayHMDDevice; -var VRDisplayPositionSensorDevice = require('./display-wrappers.js').VRDisplayPositionSensorDevice; -var version = require('../package.json').version; - -function WebVRPolyfill() { - this.displays = []; - this.devices = []; // For deprecated objects - this.devicesPopulated = false; - this.nativeWebVRAvailable = this.isWebVRAvailable(); - this.nativeLegacyWebVRAvailable = this.isDeprecatedWebVRAvailable(); - this.nativeGetVRDisplaysFunc = this.nativeWebVRAvailable ? - navigator.getVRDisplays : - null; - - if (!this.nativeLegacyWebVRAvailable) { - this.enablePolyfill(); - if (window.WebVRConfig.ENABLE_DEPRECATED_API) { - this.enableDeprecatedPolyfill(); - } - } - - // Put a shim in place to update the API to 1.1 if needed. - InstallWebVRSpecShim(); -} - -WebVRPolyfill.prototype.isWebVRAvailable = function() { - return ('getVRDisplays' in navigator); -}; - -WebVRPolyfill.prototype.isDeprecatedWebVRAvailable = function() { - return ('getVRDevices' in navigator) || ('mozGetVRDevices' in navigator); -}; - -WebVRPolyfill.prototype.connectDisplay = function(vrDisplay) { - vrDisplay.fireVRDisplayConnect_(); - this.displays.push(vrDisplay); -}; - -WebVRPolyfill.prototype.populateDevices = function() { - if (this.devicesPopulated) { - return; - } - - // Initialize our virtual VR devices. - var vrDisplay = null; - - // Add a Cardboard VRDisplay on compatible mobile devices - if (this.isCardboardCompatible()) { - vrDisplay = new CardboardVRDisplay(); - - this.connectDisplay(vrDisplay); - - // For backwards compatibility - if (window.WebVRConfig.ENABLE_DEPRECATED_API) { - this.devices.push(new VRDisplayHMDDevice(vrDisplay)); - this.devices.push(new VRDisplayPositionSensorDevice(vrDisplay)); - } - } - - // Add a Mouse and Keyboard driven VRDisplay for desktops/laptops - if (!this.isMobile() && !window.WebVRConfig.MOUSE_KEYBOARD_CONTROLS_DISABLED) { - vrDisplay = new MouseKeyboardVRDisplay(); - this.connectDisplay(vrDisplay); - - // For backwards compatibility - if (window.WebVRConfig.ENABLE_DEPRECATED_API) { - this.devices.push(new VRDisplayHMDDevice(vrDisplay)); - this.devices.push(new VRDisplayPositionSensorDevice(vrDisplay)); - } - } - - // Uncomment to add positional tracking via webcam. - //if (!this.isMobile() && window.WebVRConfig.ENABLE_DEPRECATED_API) { - // positionDevice = new WebcamPositionSensorVRDevice(); - // this.devices.push(positionDevice); - //} - - this.devicesPopulated = true; -}; - -WebVRPolyfill.prototype.enablePolyfill = function() { - // Provide navigator.getVRDisplays. - navigator.getVRDisplays = this.getVRDisplays.bind(this); - - // Polyfill native VRDisplay.getFrameData - if (this.nativeWebVRAvailable && window.VRFrameData) { - var NativeVRFrameData = window.VRFrameData; - var nativeFrameData = new window.VRFrameData(); - var nativeGetFrameData = window.VRDisplay.prototype.getFrameData; - window.VRFrameData = VRFrameData; - - window.VRDisplay.prototype.getFrameData = function(frameData) { - if (frameData instanceof NativeVRFrameData) { - nativeGetFrameData.call(this, frameData); - return; - } - - /* - Copy frame data from the native object into the polyfilled object. - */ - - nativeGetFrameData.call(this, nativeFrameData); - frameData.pose = nativeFrameData.pose; - Util.copyArray(nativeFrameData.leftProjectionMatrix, frameData.leftProjectionMatrix); - Util.copyArray(nativeFrameData.rightProjectionMatrix, frameData.rightProjectionMatrix); - Util.copyArray(nativeFrameData.leftViewMatrix, frameData.leftViewMatrix); - Util.copyArray(nativeFrameData.rightViewMatrix, frameData.rightViewMatrix); - //todo: copy - }; - } - - // Provide the `VRDisplay` object. - window.VRDisplay = VRDisplay; - - // Provide the `navigator.vrEnabled` property. - if (navigator && !navigator.vrEnabled) { - var self = this; - Object.defineProperty(navigator, 'vrEnabled', { - get: function () { - return self.isCardboardCompatible() && - (self.isFullScreenAvailable() || Util.isIOS()); - } - }); - } - - if (!('VRFrameData' in window)) { - // Provide the VRFrameData object. - window.VRFrameData = VRFrameData; - } -}; - -WebVRPolyfill.prototype.enableDeprecatedPolyfill = function() { - // Provide navigator.getVRDevices. - navigator.getVRDevices = this.getVRDevices.bind(this); - - // Provide the CardboardHMDVRDevice and PositionSensorVRDevice objects. - window.HMDVRDevice = HMDVRDevice; - window.PositionSensorVRDevice = PositionSensorVRDevice; -}; - -WebVRPolyfill.prototype.getVRDisplays = function() { - this.populateDevices(); - var polyfillDisplays = this.displays; - - if (!this.nativeWebVRAvailable) { - return Promise.resolve(polyfillDisplays); - } - - // Set up a race condition if this browser has a bug where - // `navigator.getVRDisplays()` never resolves. - var timeoutId; - var vrDisplaysNative = this.nativeGetVRDisplaysFunc.call(navigator); - var timeoutPromise = new Promise(function(resolve) { - timeoutId = setTimeout(function() { - console.warn('Native WebVR implementation detected, but `getVRDisplays()` failed to resolve. Falling back to polyfill.'); - resolve([]); - }, window.WebVRConfig.GET_VR_DISPLAYS_TIMEOUT); - }); - - return Util.race([ - vrDisplaysNative, - timeoutPromise - ]).then(function(nativeDisplays) { - clearTimeout(timeoutId); - if (window.WebVRConfig.ALWAYS_APPEND_POLYFILL_DISPLAY) { - return nativeDisplays.concat(polyfillDisplays); - } else { - return nativeDisplays.length > 0 ? nativeDisplays : polyfillDisplays; - } - }); -}; - -WebVRPolyfill.prototype.getVRDevices = function() { - console.warn('getVRDevices is deprecated. Please update your code to use getVRDisplays instead.'); - var self = this; - return new Promise(function(resolve, reject) { - try { - if (!self.devicesPopulated) { - if (self.nativeWebVRAvailable) { - return navigator.getVRDisplays(function(displays) { - for (var i = 0; i < displays.length; ++i) { - self.devices.push(new VRDisplayHMDDevice(displays[i])); - self.devices.push(new VRDisplayPositionSensorDevice(displays[i])); - } - self.devicesPopulated = true; - resolve(self.devices); - }, reject); - } - - if (self.nativeLegacyWebVRAvailable) { - return (navigator.getVRDDevices || navigator.mozGetVRDevices)(function(devices) { - for (var i = 0; i < devices.length; ++i) { - if (devices[i] instanceof HMDVRDevice) { - self.devices.push(devices[i]); - } - if (devices[i] instanceof PositionSensorVRDevice) { - self.devices.push(devices[i]); - } - } - self.devicesPopulated = true; - resolve(self.devices); - }, reject); - } - } - - self.populateDevices(); - resolve(self.devices); - } catch (e) { - reject(e); - } - }); -}; - -WebVRPolyfill.prototype.NativeVRFrameData = window.VRFrameData; - -/** - * Determine if a device is mobile. - */ -WebVRPolyfill.prototype.isMobile = function() { - return /Android/i.test(navigator.userAgent) || - /iPhone|iPad|iPod/i.test(navigator.userAgent); -}; - -WebVRPolyfill.prototype.isCardboardCompatible = function() { - // For now, support all iOS and Android devices. - // Also enable the WebVRConfig.FORCE_VR flag for debugging. - return this.isMobile() || window.WebVRConfig.FORCE_ENABLE_VR; -}; - -WebVRPolyfill.prototype.isFullScreenAvailable = function() { - return (document.fullscreenEnabled || - document.mozFullScreenEnabled || - document.webkitFullscreenEnabled || - false); -}; - -// Installs a shim that updates a WebVR 1.0 spec implementation to WebVR 1.1 -function InstallWebVRSpecShim() { - if ('VRDisplay' in window && !('VRFrameData' in window)) { - // Provide the VRFrameData object. - window.VRFrameData = VRFrameData; - - // A lot of Chrome builds don't have depthNear and depthFar, even - // though they're in the WebVR 1.0 spec. Patch them in if they're not present. - if(!('depthNear' in window.VRDisplay.prototype)) { - window.VRDisplay.prototype.depthNear = 0.01; - } - - if(!('depthFar' in window.VRDisplay.prototype)) { - window.VRDisplay.prototype.depthFar = 10000.0; - } - - window.VRDisplay.prototype.getFrameData = function(frameData) { - return Util.frameDataFromPose(frameData, this.getPose(), this); - } - } -}; - -WebVRPolyfill.InstallWebVRSpecShim = InstallWebVRSpecShim; -WebVRPolyfill.version = version; - -module.exports.WebVRPolyfill = WebVRPolyfill; - -},{"../package.json":5,"./base.js":6,"./cardboard-vr-display.js":9,"./display-wrappers.js":12,"./mouse-keyboard-vr-display.js":18,"./util.js":26}],30:[function(require,module,exports){ -(function (global){ -global.THREE = require('three'); -require('three/examples/js/controls/VRControls.js'); -require('three/examples/js/effects/VREffect.js'); -global.WebVRConfig = require('./webvr.config.js'); -var WebVrPolyfill = require('webvr-polyfill'); -var WebVRManager = require('webvr-boilerplate'); -window.WebVRManager = WebVRManager; -require('./videojs.vr.js'); - -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - -},{"./videojs.vr.js":31,"./webvr.config.js":32,"three":1,"three/examples/js/controls/VRControls.js":2,"three/examples/js/effects/VREffect.js":3,"webvr-boilerplate":4,"webvr-polyfill":19}],31:[function(require,module,exports){ -/*! videojs-vr - v0.2.0 - 2016-09-10 */ -/* - * vr - * https://github.com/MetaCDN/videojs-vr - * - * Copyright (c) 2014 Sean Lawrence - * Copyright (c) 2015 James Broberg - * Copyright (c) 2016 Brightcove Inc., HapYak Inc., MetaCDN Pty. Ltd. - * Licensed under the MIT license. - */ -(function(vjs) { - var utils = { - browser: { - isMobile: function() { - var check = false; - (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); - return check; - }, - isIOS: function () { - return /(iPad|iPhone|iPod)/g.test(navigator.userAgent); - }, - isSafari: function(){ - return /^((?!chrome|android).)*safari/i.test(navigator.userAgent); - }, - //we need this as IE 11 reports that it has a VR display, but isnt compatible with Video as a Texture. for example - isBrowserCompatible: function () { - var ua = window.navigator.userAgent; - - var msie = ua.indexOf('MSIE '); - if (msie > 0) { - return false; - } - - var trident = ua.indexOf('Trident/'); - if (trident > 0) { - return false; - } - - // MS Edge and other browsers - return true; - } - } - }; - - /** - * Copies properties from one or more objects onto an original. - */ - var extend = function(obj /*, arg1, arg2, ... */) { - var arg, i, k; - for (i = 1; i < arguments.length; i++) { - arg = arguments[i]; - for (k in arg) { - if (arg.hasOwnProperty(k)) { - obj[k] = arg[k]; - } - } - } - return obj; - }, - - projections = ["360", "360_LR", "360_TB", "360_CUBE", "NONE", "AUTO"], - - defaults = { - projection: "AUTO", - debug: false - }, - - /** - * Initializes the plugin - */ - plugin = function(options) { - //vars global (via closure) to the plugin - var player = this, - settings = extend({}, defaults, options || {}), - videoEl = this.el().getElementsByTagName('video')[0], - container = this.el(), - current_proj = settings.projection, - movieMaterial, - movieGeometry, - movieScreen, - controls3d, - scene, - cameraVector, - x, - y, - z; - - var log = function(msg){ - if(settings.debug) { - console.log(msg); - } - }; - - function registerErrorMessages() { - player.errors({ - "errors": { - "web-vr-no-devices-found": { - "headline": "No 360 devices found", - "type": "360_NO_DEVICES_FOUND", - "message": "Your browser supports 360, but no 360 displays found." - }, - "web-vr-out-of-date": { - "headline": "360 is out of date", - "type": "360_OUT_OF_DATE", - "message": "Your browser supports 360 but not the latest version. See webvr.info for more info." - }, - "web-vr-not-supported": { - "headline": "360 not supported on this device", - "type": "360_NOT_SUPPORTED", - "message": "Your browser does not support 360. See webvr.info for assistance." - } - } - }); - } - - registerErrorMessages(); - - if (videoEl == undefined || videoEl == null) { - // Player is not using HTML5 tech, so don't init it. - return ; - } - - if (current_proj === "NONE") { - // Show raw 360 video. - return ; - } - - function changeProjection(projection) { - var position = {x:0, y:0, z:0 }; - if (scene) { - scene.remove(movieScreen); - } - if (projection === "AUTO"){ - if(player.mediainfo && player.mediainfo.projection && player.mediainfo.projection === "equirectangular"){ - current_proj = "360"; - changeProjection("360"); - } - else{ - current_proj = "NONE"; - } - } - else if (projection === "360") { - movieGeometry = new THREE.SphereBufferGeometry( 256, 32, 32 ); - - movieScreen = new THREE.Mesh( movieGeometry, movieMaterial ); - movieScreen.position.set(position.x, position.y, position.z); - - movieScreen.scale.x = -1; - movieScreen.quaternion.setFromAxisAngle({x: 0, y: 1, z: 0}, -Math.PI / 2); - scene.add(movieScreen); - - } else if ((projection === "360_LR") || (projection === "360_TB")) { - var geometry = new THREE.SphereGeometry( 256, 32, 32 ); - - // Left eye view - geometry.scale( - 1, 1, 1 ); - var uvs = geometry.faceVertexUvs[ 0 ]; - for ( var i = 0; i < uvs.length; i ++ ) { - for ( var j = 0; j < 3; j ++ ) { - if (projection === "360_LR") { - uvs[ i ][ j ].x *= 0.5; - } else { - uvs[ i ][ j ].y *= 0.5; - } - } - } - - movieGeometry = new THREE.BufferGeometry().fromGeometry( geometry ); - movieScreen = new THREE.Mesh( movieGeometry, movieMaterial ); - movieScreen.rotation.y = - Math.PI / 2; - movieScreen.layers.set( 1 ); // display in left eye only - scene.add( movieScreen ); - - // Right eye view - var geometry = new THREE.SphereGeometry( 256, 32, 32 ); - geometry.scale( - 1, 1, 1 ); - var uvs = geometry.faceVertexUvs[ 0 ]; - - for ( var i = 0; i < uvs.length; i ++ ) { - for ( var j = 0; j < 3; j ++ ) { - if (projection === "360_LR") { - uvs[ i ][ j ].x *= 0.5; - uvs[ i ][ j ].x += 0.5; - } else { - uvs[ i ][ j ].y *= 0.5; - uvs[ i ][ j ].y += 0.5; - } - } - } - - movieGeometry = new THREE.BufferGeometry().fromGeometry( geometry ); - movieScreen = new THREE.Mesh( movieGeometry, movieMaterial ); - movieScreen.rotation.y = - Math.PI / 2; - movieScreen.layers.set( 2 ); // display in right eye only - scene.add( movieScreen ); - - } else if (projection === "360_CUBE") { - // Currently doesn't work - need to figure out order of cube faces - movieGeometry = new THREE.CubeGeometry( 256, 256, 256 ); - var face1 = [new THREE.Vector2(0, .5), new THREE.Vector2(.333, .5), new THREE.Vector2(.333, 1), new THREE.Vector2(0, 1)]; - var face2 = [new THREE.Vector2(.333, .5), new THREE.Vector2(.666, .5), new THREE.Vector2(.666, 1), new THREE.Vector2(.333, 1)]; - var face3 = [new THREE.Vector2(.666, .5), new THREE.Vector2(1, .5), new THREE.Vector2(1, 1), new THREE.Vector2(0.666, 1)]; - var face4 = [new THREE.Vector2(0, 0), new THREE.Vector2(.333, 1), new THREE.Vector2(.333, .5), new THREE.Vector2(0, .5)]; - var face5 = [new THREE.Vector2(.333, 1), new THREE.Vector2(.666, 1), new THREE.Vector2(.666, .5), new THREE.Vector2(.333, .5)]; - var face6 = [new THREE.Vector2(.666, 1), new THREE.Vector2(1, 0), new THREE.Vector2(1, .5), new THREE.Vector2(.666, .5)]; - - movieGeometry.faceVertexUvs[0] = []; - - movieGeometry.faceVertexUvs[0][0] = [ face1[0], face1[1], face1[3] ]; - movieGeometry.faceVertexUvs[0][1] = [ face1[1], face1[2], face1[3] ]; - - movieGeometry.faceVertexUvs[0][2] = [ face2[0], face2[1], face2[3] ]; - movieGeometry.faceVertexUvs[0][3] = [ face2[1], face2[2], face2[3] ]; - - movieGeometry.faceVertexUvs[0][4] = [ face3[0], face3[1], face3[3] ]; - movieGeometry.faceVertexUvs[0][5] = [ face3[1], face3[2], face3[3] ]; - - movieGeometry.faceVertexUvs[0][6] = [ face4[0], face4[1], face4[3] ]; - movieGeometry.faceVertexUvs[0][7] = [ face4[1], face4[2], face4[3] ]; - - movieGeometry.faceVertexUvs[0][8] = [ face5[0], face5[1], face5[3] ]; - movieGeometry.faceVertexUvs[0][9] = [ face5[1], face5[2], face5[3] ]; - - movieGeometry.faceVertexUvs[0][10] = [ face6[0], face6[1], face6[3] ]; - movieGeometry.faceVertexUvs[0][11] = [ face6[1], face6[2], face6[3] ]; - - movieScreen = new THREE.Mesh( movieGeometry, movieMaterial ); - movieScreen.position.set(position.x, position.y, position.z); - - scene.add(movieScreen); - - } - } - - function initScene() { - - if(!utils.browser.isBrowserCompatible()) - { - player.error({code: 'web-vr-not-supported', dismiss: false}); - return; - } - - var time = Date.now(), - effect, - videoTexture, - requestId, - renderer, - camera, - renderedCanvas; - - camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1000 ); - - cameraVector = new THREE.Vector3(); // Store vector representing the direction in which the camera is looking, in world space. - - if ((current_proj === "360_LR") || (current_proj === "360_TB")) { - camera.layers.enable( 1 ); // Render left eye when not in VR mode - } - - scene = new THREE.Scene(); - - controls3d = new THREE.VRControls(camera); - - videoTexture = new THREE.VideoTexture( videoEl ); - videoTexture.generateMipmaps = false; - videoTexture.minFilter = THREE.LinearFilter; - videoTexture.magFilter = THREE.LinearFilter; - - // iOS and macOS HLS fix/hacks - // https://bugs.webkit.org/show_bug.cgi?id=163866#c3 - // https://github.com/mrdoob/three.js/issues/9754 - // On iOS with HLS, color space is wrong and texture is flipped on Y axis - // On macOS, just need to flip texture Y axis - - if (isHLS() && utils.browser.isSafari() && utils.browser.isIOS()) { - log("Safari + iOS + HLS = flipY and colorspace hack"); - videoTexture.format = THREE.RGBAFormat; - videoTexture.flipY = false; - } else if (isHLS() && utils.browser.isSafari()) { - log("Safari + HLS = flipY hack"); - videoTexture.format = THREE.RGBFormat; - videoTexture.flipY = false; - } else { - videoTexture.format = THREE.RGBFormat; - } - - if (videoTexture.format === THREE.RGBAFormat && videoTexture.flipY === false) { - movieMaterial = new THREE.ShaderMaterial({ - uniforms: { - texture: { value: videoTexture } - }, - vertexShader: [ - "varying vec2 vUV;", - "void main() {", - " vUV = vec2( uv.x, 1.0 - uv.y );", - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - "}" - ].join("\n"), - fragmentShader: [ - "uniform sampler2D texture;", - "varying vec2 vUV;", - "void main() {", - " gl_FragColor = texture2D( texture, vUV ).bgra;", - "}" - ].join("\n") - }); - } else if (videoTexture.format === THREE.RGBFormat && videoTexture.flipY === false) { - movieMaterial = new THREE.ShaderMaterial({ - uniforms: { - texture: { value: videoTexture } - }, - vertexShader: [ - "varying vec2 vUV;", - "void main() {", - " vUV = vec2( uv.x, 1.0 - uv.y );", - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - "}" - ].join("\n"), - fragmentShader: [ - "uniform sampler2D texture;", - "varying vec2 vUV;", - "void main() {", - " gl_FragColor = texture2D( texture, vUV );", - "}" - ].join("\n") - }); - } else { - movieMaterial = new THREE.MeshBasicMaterial( { map: videoTexture, overdraw: true, side:THREE.DoubleSide } ); - } - - changeProjection(current_proj); - - if(current_proj === "NONE") { - log("Projection is NONE, dont init"); - return; - } - - camera.position.set(0,0,0); - - renderer = new THREE.WebGLRenderer({ - devicePixelRatio: window.devicePixelRatio, - alpha: false, - clearColor: 0xffffff, - antialias: true - }); - - renderer.setSize(window.innerWidth, window.innerHeight); - effect = new THREE.VREffect(renderer); - effect.setSize(window.innerWidth, window.innerHeight); - - var vrDisplay = null; - var frameData = null; - - // Previous timestamps for gamepad updates - var prevTimestamps = []; - - var manager = new WebVRManager(renderer, effect, {hideButton: true}); - - renderedCanvas = renderer.domElement; - renderedCanvas.style.width = "inherit"; - renderedCanvas.style.height = "inherit"; - - container.insertBefore(renderedCanvas, container.firstChild); - videoEl.style.display = "none"; - - // Handle window resizes - function onWindowResize() { - //if (window.orientation == undefined) { - effect.setSize(window.innerWidth, window.innerHeight); - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - //} - } - - window.addEventListener('resize', onWindowResize, false); - window.addEventListener('vrdisplaypresentchange', onWindowResize, true); - - function onVRRequestPresent() { - manager.enterVRMode_(); - manager.setMode_(3); - } - - function onVRExitPresent() { - if (!vrDisplay.isPresenting) - return; - vrDisplay.exitPresent(); - } - - window.addEventListener('vrdisplayactivate', onVRRequestPresent, true); - window.addEventListener('vrdisplaydeactivate', onVRExitPresent, true); - - if (navigator.getVRDisplays) { - //frameData = new VRFrameData(); - navigator.getVRDisplays().then(function (displays) { - if (displays.length > 0) { - log("WebVR supported, VRDisplays found."); - vrDisplay = displays[0]; - log(vrDisplay); - } else { - player.error({code: 'web-vr-no-devices-found', dismiss: false}); - } - }); - } else if (navigator.getVRDevices) { - player.error({code: 'web-vr-out-of-date', dismiss: false}); - } else { - player.error({code: 'web-vr-not-supported', dismiss: false}); - } - - - function isHLS(videoElement) { - var result = false; - var currentType = player.currentType(); - - if (currentType == "application/x-mpegURL") { - result = true; - log("Detected HLS Stream"); - } - - return result; - } - - // Handle window rotate - function onWindowRotate() { - if (window.orientation == -90 || window.orientation == 90) { - // in iOS, width and height never changes regardless orientation - // so when in a horizontal mode, height still greater than width - if (screen.height > screen.width) { - camera.aspect = screen.height / screen.width; - } else { - // in Android, width and height will swap value depending on orientation - camera.aspect = screen.width / screen.height; - } - } else { - camera.aspect = screen.width / screen.height; - } - camera.updateProjectionMatrix(); - } - window.addEventListener('orientationchange', onWindowRotate, false); - - function togglePlay() { - // Doesn't currently cater for case where paused due to buffering - // and/or lack of data - if (player.paused()) { - player.play(); - } else { - player.pause(); - } - } - - (function animate() { - if ( videoEl.readyState === videoEl.HAVE_ENOUGH_DATA ) { - if (videoTexture) { - videoTexture.needsUpdate = true; - } - } - - controls3d.update(); - manager.render( scene, camera ); - - if (vrDisplay) { - vrDisplay.requestAnimationFrame(animate); - - // Grab all gamepads - if(navigator.getGamepads) { - var gamepads = navigator.getGamepads(); - for (var i = 0; i < gamepads.length; ++i) { - var gamepad = gamepads[i]; - // Make sure gamepad is defined - if (gamepad) { - // Only take input if state has changed since we checked last - if (gamepad.timestamp && !(gamepad.timestamp === prevTimestamps[i])) { - for (var j = 0; j < gamepad.buttons.length; ++j) { - if (gamepad.buttons[j].pressed) { - togglePlay(); - prevTimestamps[i] = gamepad.timestamp; - break; - } - } - } - } - } - } - //vrDisplay.getFrameData(frameData); - - //if (vrDisplay.isPresenting) { - // vrDisplay.submitFrame(); - //} - } else { - window.requestAnimationFrame(animate); - } - - camera.getWorldDirection( cameraVector ); - x = getCameraAngle( cameraVector, "x"); - y = getCameraAngle( cameraVector, "y"); - - // log('X camera angle:' + getCameraAngle( cameraVector, "x") ); - // log('Y camera angle:' + getCameraAngle( cameraVector, "y") ); - // log(cameraVector.x, cameraVector.y, cameraVector.z); - }()); - - function getCameraAngle(cameraVector, axis) { - if (axis == "x") { - return 180-radiansToDegrees(Math.atan2(cameraVector.x, cameraVector.z)); - //return cameraVector.x * 114.59155902616465; - } else if (axis == "y") { - return 180-radiansToDegrees(Math.atan2(cameraVector.y, cameraVector.z)); - //return cameraVector.y * 114.59155902616465; - } - } - - function radiansToDegrees(radians) { - return radians * 180 / Math.PI; - } - - // Expose the Three.js perspective camera and rotation values on the player under the 'vr' namespace: - player.vr = { - cameraVector: cameraVector, - threeJs: { - scene: scene, - camera: camera, - renderer: renderer - } - }; - - } - - player.on('loadstart', function () { - initScene(); - }); - - player.ready(function () { - - function updateVrControls(cb) { - - //Add Carboard button - var Button = videojs.getComponent('Button'); - var MyButton = videojs.extend(Button, { - constructor: function() { - Button.apply(this, arguments); - log(this); - /* initialize your button */ - }, - handleClick: function() { - var event = new Event('vrdisplayactivate'); - window.dispatchEvent(event); - /* do something on click */ - }, - buildCSSClass: function() { - return Button.prototype.buildCSSClass.call(this) + 'vjs-button-vr'; - } - }); - videojs.registerComponent('MyButton', MyButton); - cb.addChild('myButton', {}); - - //if ios remove full screen toggle - if(utils.browser.isIOS()) - { - player.controlBar.fullscreenToggle.hide(); - } - } - - if(utils.browser.isMobile()){ - updateVrControls(player.controlBar); - } - }); - - - return { - changeProjection: changeProjection - }; - }; - - // register the plugin with video.js - videojs.registerPlugin('vr', plugin); - -}(window.videojs)); - -},{}],32:[function(require,module,exports){ -module.exports = { - // Forces availability of VR mode, even for non-mobile devices. - FORCE_ENABLE_VR: false, // Default: false. - - // Complementary filter coefficient. 0 for accelerometer, 1 for gyro. - K_FILTER: 0.98, // Default: 0.98. - - // How far into the future to predict during fast motion (in seconds). - PREDICTION_TIME_S: 0.040, // Default: 0.040. - - // Flag to disable touch panner. In case you have your own touch controls. - TOUCH_PANNER_DISABLED: false, // Default: false. - - // Enable yaw panning only, disabling roll and pitch. This can be useful - // for panoramas with nothing interesting above or below. - YAW_ONLY: false, // Default: false. - - // To disable keyboard and mouse controls, if you want to use your own - // implementation. - MOUSE_KEYBOARD_CONTROLS_DISABLED: false, // Default: false. - - // Prevent the polyfill from initializing immediately. Requires the app - // to call InitializeWebVRPolyfill() before it can be used. - DEFER_INITIALIZATION: false, // Default: false. - - // Enable the deprecated version of the API (navigator.getVRDevices). - ENABLE_DEPRECATED_API: false, // Default: false. - - // Scales the recommended buffer size reported by WebVR, which can improve - // performance. - BUFFER_SCALE: 0.5, // Default: 1.0. - - // Allow VRDisplay.submitFrame to change gl bindings, which is more - // efficient if the application code will re-bind its resources on the - // next frame anyway. This has been seen to cause rendering glitches with - // THREE.js. - // Dirty bindings include: gl.FRAMEBUFFER_BINDING, gl.CURRENT_PROGRAM, - // gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING, - // and gl.TEXTURE_BINDING_2D for texture unit 0. - DIRTY_SUBMIT_FRAME_BINDINGS: false // Default: false. -} - -},{}]},{},[30]) -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJub2RlX21vZHVsZXMvdGhyZWUvYnVpbGQvdGhyZWUuanMiLCJub2RlX21vZHVsZXMvdGhyZWUvZXhhbXBsZXMvanMvY29udHJvbHMvVlJDb250cm9scy5qcyIsIm5vZGVfbW9kdWxlcy90aHJlZS9leGFtcGxlcy9qcy9lZmZlY3RzL1ZSRWZmZWN0LmpzIiwibm9kZV9tb2R1bGVzL3dlYnZyLWJvaWxlcnBsYXRlL2J1aWxkL3dlYnZyLW1hbmFnZXIuanMiLCJub2RlX21vZHVsZXMvd2VidnItcG9seWZpbGwvcGFja2FnZS5qc29uIiwibm9kZV9tb2R1bGVzL3dlYnZyLXBvbHlmaWxsL3NyYy9iYXNlLmpzIiwibm9kZV9tb2R1bGVzL3dlYnZyLXBvbHlmaWxsL3NyYy9jYXJkYm9hcmQtZGlzdG9ydGVyLmpzIiwibm9kZV9tb2R1bGVzL3dlYnZyLXBvbHlmaWxsL3NyYy9jYXJkYm9hcmQtdWkuanMiLCJub2RlX21vZHVsZXMvd2VidnItcG9seWZpbGwvc3JjL2NhcmRib2FyZC12ci1kaXNwbGF5LmpzIiwibm9kZV9tb2R1bGVzL3dlYnZyLXBvbHlmaWxsL3NyYy9kZXBzL3dnbHUtcHJlc2VydmUtc3RhdGUuanMiLCJub2RlX21vZHVsZXMvd2VidnItcG9seWZpbGwvc3JjL2RldmljZS1pbmZvLmpzIiwibm9kZV9tb2R1bGVzL3dlYnZyLXBvbHlmaWxsL3NyYy9kaXNwbGF5LXdyYXBwZXJzLmpzIiwibm9kZV9tb2R1bGVzL3dlYnZyLXBvbHlmaWxsL3NyYy9kaXN0b3J0aW9uL2Rpc3RvcnRpb24uanMiLCJub2RlX21vZHVsZXMvd2VidnItcG9seWZpbGwvc3JjL2RwZGIvZHBkYi5qc29uIiwibm9kZV9tb2R1bGVzL3dlYnZyLXBvbHlmaWxsL3NyYy9kcGRiL2RwZGIuanMiLCJub2RlX21vZHVsZXMvd2VidnItcG9seWZpbGwvc3JjL21haW4uanMiLCJub2RlX21vZHVsZXMvd2VidnItcG9seWZpbGwvc3JjL21hdGgtdXRpbC5qcyIsIm5vZGVfbW9kdWxlcy93ZWJ2ci1wb2x5ZmlsbC9zcmMvbW91c2Uta2V5Ym9hcmQtdnItZGlzcGxheS5qcyIsIm5vZGVfbW9kdWxlcy93ZWJ2ci1wb2x5ZmlsbC9zcmMvbm9kZS1lbnRyeS5qcyIsIm5vZGVfbW9kdWxlcy93ZWJ2ci1wb2x5ZmlsbC9zcmMvcm90YXRlLWluc3RydWN0aW9ucy5qcyIsIm5vZGVfbW9kdWxlcy93ZWJ2ci1wb2x5ZmlsbC9zcmMvc2Vuc29yLWZ1c2lvbi9jb21wbGVtZW50YXJ5LWZpbHRlci5qcyIsIm5vZGVfbW9kdWxlcy93ZWJ2ci1wb2x5ZmlsbC9zcmMvc2Vuc29yLWZ1c2lvbi9mdXNpb24tcG9zZS1zZW5zb3IuanMiLCJub2RlX21vZHVsZXMvd2VidnItcG9seWZpbGwvc3JjL3NlbnNvci1mdXNpb24vcG9zZS1wcmVkaWN0b3IuanMiLCJub2RlX21vZHVsZXMvd2VidnItcG9seWZpbGwvc3JjL3NlbnNvci1mdXNpb24vc2Vuc29yLXNhbXBsZS5qcyIsIm5vZGVfbW9kdWxlcy93ZWJ2ci1wb2x5ZmlsbC9zcmMvdG91Y2gtcGFubmVyLmpzIiwibm9kZV9tb2R1bGVzL3dlYnZyLXBvbHlmaWxsL3NyYy91dGlsLmpzIiwibm9kZV9tb2R1bGVzL3dlYnZyLXBvbHlmaWxsL3NyYy92aWV3ZXItc2VsZWN0b3IuanMiLCJub2RlX21vZHVsZXMvd2VidnItcG9seWZpbGwvc3JjL3dha2Vsb2NrLmpzIiwibm9kZV9tb2R1bGVzL3dlYnZyLXBvbHlmaWxsL3NyYy93ZWJ2ci1wb2x5ZmlsbC5qcyIsInNyYy9qcy92aWRlb2Nsb3VkLnZyLmpzIiwic3JjL2pzL3ZpZGVvanMudnIuanMiLCJzcmMvanMvd2VidnIuY29uZmlnLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDaDEwQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzdLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FDN2RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7QUM5akJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbENBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3Y0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeG9CQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3UkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMVJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQy9HQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDN1dBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNoREE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDai9DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwTEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcldBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7QUNqTEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7OztBQ1ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hKQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3RLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JPQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzlFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDemRBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbk5BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3RUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FDOVJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7OztBQ1JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvakJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCIoZnVuY3Rpb24gKGdsb2JhbCwgZmFjdG9yeSkge1xuXHR0eXBlb2YgZXhwb3J0cyA9PT0gJ29iamVjdCcgJiYgdHlwZW9mIG1vZHVsZSAhPT0gJ3VuZGVmaW5lZCcgPyBmYWN0b3J5KGV4cG9ydHMpIDpcblx0dHlwZW9mIGRlZmluZSA9PT0gJ2Z1bmN0aW9uJyAmJiBkZWZpbmUuYW1kID8gZGVmaW5lKFsnZXhwb3J0cyddLCBmYWN0b3J5KSA6XG5cdChmYWN0b3J5KChnbG9iYWwuVEhSRUUgPSBnbG9iYWwuVEhSRUUgfHwge30pKSk7XG59KHRoaXMsIChmdW5jdGlvbiAoZXhwb3J0cykgeyAndXNlIHN0cmljdCc7XG5cblx0Ly8gUG9seWZpbGxzXG5cblx0aWYgKCBOdW1iZXIuRVBTSUxPTiA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0TnVtYmVyLkVQU0lMT04gPSBNYXRoLnBvdyggMiwgLSA1MiApO1xuXG5cdH1cblxuXHQvL1xuXG5cdGlmICggTWF0aC5zaWduID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHQvLyBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9NYXRoL3NpZ25cblxuXHRcdE1hdGguc2lnbiA9IGZ1bmN0aW9uICggeCApIHtcblxuXHRcdFx0cmV0dXJuICggeCA8IDAgKSA/IC0gMSA6ICggeCA+IDAgKSA/IDEgOiArIHg7XG5cblx0XHR9O1xuXG5cdH1cblxuXHRpZiAoIEZ1bmN0aW9uLnByb3RvdHlwZS5uYW1lID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHQvLyBNaXNzaW5nIGluIElFOS0xMS5cblx0XHQvLyBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9GdW5jdGlvbi9uYW1lXG5cblx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoIEZ1bmN0aW9uLnByb3RvdHlwZSwgJ25hbWUnLCB7XG5cblx0XHRcdGdldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdHJldHVybiB0aGlzLnRvU3RyaW5nKCkubWF0Y2goIC9eXFxzKmZ1bmN0aW9uXFxzKihbXlxcKFxcc10qKS8gKVsgMSBdO1xuXG5cdFx0XHR9XG5cblx0XHR9ICk7XG5cblx0fVxuXG5cdGlmICggT2JqZWN0LmFzc2lnbiA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0Ly8gTWlzc2luZyBpbiBJRS5cblx0XHQvLyBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9PYmplY3QvYXNzaWduXG5cblx0XHQoIGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0T2JqZWN0LmFzc2lnbiA9IGZ1bmN0aW9uICggdGFyZ2V0ICkge1xuXG5cdFx0XHRcdCd1c2Ugc3RyaWN0JztcblxuXHRcdFx0XHRpZiAoIHRhcmdldCA9PT0gdW5kZWZpbmVkIHx8IHRhcmdldCA9PT0gbnVsbCApIHtcblxuXHRcdFx0XHRcdHRocm93IG5ldyBUeXBlRXJyb3IoICdDYW5ub3QgY29udmVydCB1bmRlZmluZWQgb3IgbnVsbCB0byBvYmplY3QnICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBvdXRwdXQgPSBPYmplY3QoIHRhcmdldCApO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpbmRleCA9IDE7IGluZGV4IDwgYXJndW1lbnRzLmxlbmd0aDsgaW5kZXggKysgKSB7XG5cblx0XHRcdFx0XHR2YXIgc291cmNlID0gYXJndW1lbnRzWyBpbmRleCBdO1xuXG5cdFx0XHRcdFx0aWYgKCBzb3VyY2UgIT09IHVuZGVmaW5lZCAmJiBzb3VyY2UgIT09IG51bGwgKSB7XG5cblx0XHRcdFx0XHRcdGZvciAoIHZhciBuZXh0S2V5IGluIHNvdXJjZSApIHtcblxuXHRcdFx0XHRcdFx0XHRpZiAoIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCggc291cmNlLCBuZXh0S2V5ICkgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHRvdXRwdXRbIG5leHRLZXkgXSA9IHNvdXJjZVsgbmV4dEtleSBdO1xuXG5cdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRyZXR1cm4gb3V0cHV0O1xuXG5cdFx0XHR9O1xuXG5cdFx0fSApKCk7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBodHRwczovL2dpdGh1Yi5jb20vbXJkb29iL2V2ZW50ZGlzcGF0Y2hlci5qcy9cblx0ICovXG5cblx0ZnVuY3Rpb24gRXZlbnREaXNwYXRjaGVyKCkge31cblxuXHRPYmplY3QuYXNzaWduKCBFdmVudERpc3BhdGNoZXIucHJvdG90eXBlLCB7XG5cblx0XHRhZGRFdmVudExpc3RlbmVyOiBmdW5jdGlvbiAoIHR5cGUsIGxpc3RlbmVyICkge1xuXG5cdFx0XHRpZiAoIHRoaXMuX2xpc3RlbmVycyA9PT0gdW5kZWZpbmVkICkgdGhpcy5fbGlzdGVuZXJzID0ge307XG5cblx0XHRcdHZhciBsaXN0ZW5lcnMgPSB0aGlzLl9saXN0ZW5lcnM7XG5cblx0XHRcdGlmICggbGlzdGVuZXJzWyB0eXBlIF0gPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRsaXN0ZW5lcnNbIHR5cGUgXSA9IFtdO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggbGlzdGVuZXJzWyB0eXBlIF0uaW5kZXhPZiggbGlzdGVuZXIgKSA9PT0gLSAxICkge1xuXG5cdFx0XHRcdGxpc3RlbmVyc1sgdHlwZSBdLnB1c2goIGxpc3RlbmVyICk7XG5cblx0XHRcdH1cblxuXHRcdH0sXG5cblx0XHRoYXNFdmVudExpc3RlbmVyOiBmdW5jdGlvbiAoIHR5cGUsIGxpc3RlbmVyICkge1xuXG5cdFx0XHRpZiAoIHRoaXMuX2xpc3RlbmVycyA9PT0gdW5kZWZpbmVkICkgcmV0dXJuIGZhbHNlO1xuXG5cdFx0XHR2YXIgbGlzdGVuZXJzID0gdGhpcy5fbGlzdGVuZXJzO1xuXG5cdFx0XHRyZXR1cm4gbGlzdGVuZXJzWyB0eXBlIF0gIT09IHVuZGVmaW5lZCAmJiBsaXN0ZW5lcnNbIHR5cGUgXS5pbmRleE9mKCBsaXN0ZW5lciApICE9PSAtIDE7XG5cblx0XHR9LFxuXG5cdFx0cmVtb3ZlRXZlbnRMaXN0ZW5lcjogZnVuY3Rpb24gKCB0eXBlLCBsaXN0ZW5lciApIHtcblxuXHRcdFx0aWYgKCB0aGlzLl9saXN0ZW5lcnMgPT09IHVuZGVmaW5lZCApIHJldHVybjtcblxuXHRcdFx0dmFyIGxpc3RlbmVycyA9IHRoaXMuX2xpc3RlbmVycztcblx0XHRcdHZhciBsaXN0ZW5lckFycmF5ID0gbGlzdGVuZXJzWyB0eXBlIF07XG5cblx0XHRcdGlmICggbGlzdGVuZXJBcnJheSAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdHZhciBpbmRleCA9IGxpc3RlbmVyQXJyYXkuaW5kZXhPZiggbGlzdGVuZXIgKTtcblxuXHRcdFx0XHRpZiAoIGluZGV4ICE9PSAtIDEgKSB7XG5cblx0XHRcdFx0XHRsaXN0ZW5lckFycmF5LnNwbGljZSggaW5kZXgsIDEgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdH0sXG5cblx0XHRkaXNwYXRjaEV2ZW50OiBmdW5jdGlvbiAoIGV2ZW50ICkge1xuXG5cdFx0XHRpZiAoIHRoaXMuX2xpc3RlbmVycyA9PT0gdW5kZWZpbmVkICkgcmV0dXJuO1xuXG5cdFx0XHR2YXIgbGlzdGVuZXJzID0gdGhpcy5fbGlzdGVuZXJzO1xuXHRcdFx0dmFyIGxpc3RlbmVyQXJyYXkgPSBsaXN0ZW5lcnNbIGV2ZW50LnR5cGUgXTtcblxuXHRcdFx0aWYgKCBsaXN0ZW5lckFycmF5ICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0ZXZlbnQudGFyZ2V0ID0gdGhpcztcblxuXHRcdFx0XHR2YXIgYXJyYXkgPSBbXSwgaSA9IDA7XG5cdFx0XHRcdHZhciBsZW5ndGggPSBsaXN0ZW5lckFycmF5Lmxlbmd0aDtcblxuXHRcdFx0XHRmb3IgKCBpID0gMDsgaSA8IGxlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHRcdGFycmF5WyBpIF0gPSBsaXN0ZW5lckFycmF5WyBpIF07XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGZvciAoIGkgPSAwOyBpIDwgbGVuZ3RoOyBpICsrICkge1xuXG5cdFx0XHRcdFx0YXJyYXlbIGkgXS5jYWxsKCB0aGlzLCBldmVudCApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdH0gKTtcblxuXHR2YXIgUkVWSVNJT04gPSAnODMnO1xuXHR2YXIgTU9VU0UgPSB7IExFRlQ6IDAsIE1JRERMRTogMSwgUklHSFQ6IDIgfTtcblx0dmFyIEN1bGxGYWNlTm9uZSA9IDA7XG5cdHZhciBDdWxsRmFjZUJhY2sgPSAxO1xuXHR2YXIgQ3VsbEZhY2VGcm9udCA9IDI7XG5cdHZhciBDdWxsRmFjZUZyb250QmFjayA9IDM7XG5cdHZhciBGcm9udEZhY2VEaXJlY3Rpb25DVyA9IDA7XG5cdHZhciBGcm9udEZhY2VEaXJlY3Rpb25DQ1cgPSAxO1xuXHR2YXIgQmFzaWNTaGFkb3dNYXAgPSAwO1xuXHR2YXIgUENGU2hhZG93TWFwID0gMTtcblx0dmFyIFBDRlNvZnRTaGFkb3dNYXAgPSAyO1xuXHR2YXIgRnJvbnRTaWRlID0gMDtcblx0dmFyIEJhY2tTaWRlID0gMTtcblx0dmFyIERvdWJsZVNpZGUgPSAyO1xuXHR2YXIgRmxhdFNoYWRpbmcgPSAxO1xuXHR2YXIgU21vb3RoU2hhZGluZyA9IDI7XG5cdHZhciBOb0NvbG9ycyA9IDA7XG5cdHZhciBGYWNlQ29sb3JzID0gMTtcblx0dmFyIFZlcnRleENvbG9ycyA9IDI7XG5cdHZhciBOb0JsZW5kaW5nID0gMDtcblx0dmFyIE5vcm1hbEJsZW5kaW5nID0gMTtcblx0dmFyIEFkZGl0aXZlQmxlbmRpbmcgPSAyO1xuXHR2YXIgU3VidHJhY3RpdmVCbGVuZGluZyA9IDM7XG5cdHZhciBNdWx0aXBseUJsZW5kaW5nID0gNDtcblx0dmFyIEN1c3RvbUJsZW5kaW5nID0gNTtcblx0dmFyIEJsZW5kaW5nTW9kZSA9IHtcblx0XHROb0JsZW5kaW5nOiBOb0JsZW5kaW5nLFxuXHRcdE5vcm1hbEJsZW5kaW5nOiBOb3JtYWxCbGVuZGluZyxcblx0XHRBZGRpdGl2ZUJsZW5kaW5nOiBBZGRpdGl2ZUJsZW5kaW5nLFxuXHRcdFN1YnRyYWN0aXZlQmxlbmRpbmc6IFN1YnRyYWN0aXZlQmxlbmRpbmcsXG5cdFx0TXVsdGlwbHlCbGVuZGluZzogTXVsdGlwbHlCbGVuZGluZyxcblx0XHRDdXN0b21CbGVuZGluZzogQ3VzdG9tQmxlbmRpbmdcblx0fTtcblx0dmFyIEFkZEVxdWF0aW9uID0gMTAwO1xuXHR2YXIgU3VidHJhY3RFcXVhdGlvbiA9IDEwMTtcblx0dmFyIFJldmVyc2VTdWJ0cmFjdEVxdWF0aW9uID0gMTAyO1xuXHR2YXIgTWluRXF1YXRpb24gPSAxMDM7XG5cdHZhciBNYXhFcXVhdGlvbiA9IDEwNDtcblx0dmFyIFplcm9GYWN0b3IgPSAyMDA7XG5cdHZhciBPbmVGYWN0b3IgPSAyMDE7XG5cdHZhciBTcmNDb2xvckZhY3RvciA9IDIwMjtcblx0dmFyIE9uZU1pbnVzU3JjQ29sb3JGYWN0b3IgPSAyMDM7XG5cdHZhciBTcmNBbHBoYUZhY3RvciA9IDIwNDtcblx0dmFyIE9uZU1pbnVzU3JjQWxwaGFGYWN0b3IgPSAyMDU7XG5cdHZhciBEc3RBbHBoYUZhY3RvciA9IDIwNjtcblx0dmFyIE9uZU1pbnVzRHN0QWxwaGFGYWN0b3IgPSAyMDc7XG5cdHZhciBEc3RDb2xvckZhY3RvciA9IDIwODtcblx0dmFyIE9uZU1pbnVzRHN0Q29sb3JGYWN0b3IgPSAyMDk7XG5cdHZhciBTcmNBbHBoYVNhdHVyYXRlRmFjdG9yID0gMjEwO1xuXHR2YXIgTmV2ZXJEZXB0aCA9IDA7XG5cdHZhciBBbHdheXNEZXB0aCA9IDE7XG5cdHZhciBMZXNzRGVwdGggPSAyO1xuXHR2YXIgTGVzc0VxdWFsRGVwdGggPSAzO1xuXHR2YXIgRXF1YWxEZXB0aCA9IDQ7XG5cdHZhciBHcmVhdGVyRXF1YWxEZXB0aCA9IDU7XG5cdHZhciBHcmVhdGVyRGVwdGggPSA2O1xuXHR2YXIgTm90RXF1YWxEZXB0aCA9IDc7XG5cdHZhciBNdWx0aXBseU9wZXJhdGlvbiA9IDA7XG5cdHZhciBNaXhPcGVyYXRpb24gPSAxO1xuXHR2YXIgQWRkT3BlcmF0aW9uID0gMjtcblx0dmFyIE5vVG9uZU1hcHBpbmcgPSAwO1xuXHR2YXIgTGluZWFyVG9uZU1hcHBpbmcgPSAxO1xuXHR2YXIgUmVpbmhhcmRUb25lTWFwcGluZyA9IDI7XG5cdHZhciBVbmNoYXJ0ZWQyVG9uZU1hcHBpbmcgPSAzO1xuXHR2YXIgQ2luZW9uVG9uZU1hcHBpbmcgPSA0O1xuXHR2YXIgVVZNYXBwaW5nID0gMzAwO1xuXHR2YXIgQ3ViZVJlZmxlY3Rpb25NYXBwaW5nID0gMzAxO1xuXHR2YXIgQ3ViZVJlZnJhY3Rpb25NYXBwaW5nID0gMzAyO1xuXHR2YXIgRXF1aXJlY3Rhbmd1bGFyUmVmbGVjdGlvbk1hcHBpbmcgPSAzMDM7XG5cdHZhciBFcXVpcmVjdGFuZ3VsYXJSZWZyYWN0aW9uTWFwcGluZyA9IDMwNDtcblx0dmFyIFNwaGVyaWNhbFJlZmxlY3Rpb25NYXBwaW5nID0gMzA1O1xuXHR2YXIgQ3ViZVVWUmVmbGVjdGlvbk1hcHBpbmcgPSAzMDY7XG5cdHZhciBDdWJlVVZSZWZyYWN0aW9uTWFwcGluZyA9IDMwNztcblx0dmFyIFRleHR1cmVNYXBwaW5nID0ge1xuXHRcdFVWTWFwcGluZzogVVZNYXBwaW5nLFxuXHRcdEN1YmVSZWZsZWN0aW9uTWFwcGluZzogQ3ViZVJlZmxlY3Rpb25NYXBwaW5nLFxuXHRcdEN1YmVSZWZyYWN0aW9uTWFwcGluZzogQ3ViZVJlZnJhY3Rpb25NYXBwaW5nLFxuXHRcdEVxdWlyZWN0YW5ndWxhclJlZmxlY3Rpb25NYXBwaW5nOiBFcXVpcmVjdGFuZ3VsYXJSZWZsZWN0aW9uTWFwcGluZyxcblx0XHRFcXVpcmVjdGFuZ3VsYXJSZWZyYWN0aW9uTWFwcGluZzogRXF1aXJlY3Rhbmd1bGFyUmVmcmFjdGlvbk1hcHBpbmcsXG5cdFx0U3BoZXJpY2FsUmVmbGVjdGlvbk1hcHBpbmc6IFNwaGVyaWNhbFJlZmxlY3Rpb25NYXBwaW5nLFxuXHRcdEN1YmVVVlJlZmxlY3Rpb25NYXBwaW5nOiBDdWJlVVZSZWZsZWN0aW9uTWFwcGluZyxcblx0XHRDdWJlVVZSZWZyYWN0aW9uTWFwcGluZzogQ3ViZVVWUmVmcmFjdGlvbk1hcHBpbmdcblx0fTtcblx0dmFyIFJlcGVhdFdyYXBwaW5nID0gMTAwMDtcblx0dmFyIENsYW1wVG9FZGdlV3JhcHBpbmcgPSAxMDAxO1xuXHR2YXIgTWlycm9yZWRSZXBlYXRXcmFwcGluZyA9IDEwMDI7XG5cdHZhciBUZXh0dXJlV3JhcHBpbmcgPSB7XG5cdFx0UmVwZWF0V3JhcHBpbmc6IFJlcGVhdFdyYXBwaW5nLFxuXHRcdENsYW1wVG9FZGdlV3JhcHBpbmc6IENsYW1wVG9FZGdlV3JhcHBpbmcsXG5cdFx0TWlycm9yZWRSZXBlYXRXcmFwcGluZzogTWlycm9yZWRSZXBlYXRXcmFwcGluZ1xuXHR9O1xuXHR2YXIgTmVhcmVzdEZpbHRlciA9IDEwMDM7XG5cdHZhciBOZWFyZXN0TWlwTWFwTmVhcmVzdEZpbHRlciA9IDEwMDQ7XG5cdHZhciBOZWFyZXN0TWlwTWFwTGluZWFyRmlsdGVyID0gMTAwNTtcblx0dmFyIExpbmVhckZpbHRlciA9IDEwMDY7XG5cdHZhciBMaW5lYXJNaXBNYXBOZWFyZXN0RmlsdGVyID0gMTAwNztcblx0dmFyIExpbmVhck1pcE1hcExpbmVhckZpbHRlciA9IDEwMDg7XG5cdHZhciBUZXh0dXJlRmlsdGVyID0ge1xuXHRcdE5lYXJlc3RGaWx0ZXI6IE5lYXJlc3RGaWx0ZXIsXG5cdFx0TmVhcmVzdE1pcE1hcE5lYXJlc3RGaWx0ZXI6IE5lYXJlc3RNaXBNYXBOZWFyZXN0RmlsdGVyLFxuXHRcdE5lYXJlc3RNaXBNYXBMaW5lYXJGaWx0ZXI6IE5lYXJlc3RNaXBNYXBMaW5lYXJGaWx0ZXIsXG5cdFx0TGluZWFyRmlsdGVyOiBMaW5lYXJGaWx0ZXIsXG5cdFx0TGluZWFyTWlwTWFwTmVhcmVzdEZpbHRlcjogTGluZWFyTWlwTWFwTmVhcmVzdEZpbHRlcixcblx0XHRMaW5lYXJNaXBNYXBMaW5lYXJGaWx0ZXI6IExpbmVhck1pcE1hcExpbmVhckZpbHRlclxuXHR9O1xuXHR2YXIgVW5zaWduZWRCeXRlVHlwZSA9IDEwMDk7XG5cdHZhciBCeXRlVHlwZSA9IDEwMTA7XG5cdHZhciBTaG9ydFR5cGUgPSAxMDExO1xuXHR2YXIgVW5zaWduZWRTaG9ydFR5cGUgPSAxMDEyO1xuXHR2YXIgSW50VHlwZSA9IDEwMTM7XG5cdHZhciBVbnNpZ25lZEludFR5cGUgPSAxMDE0O1xuXHR2YXIgRmxvYXRUeXBlID0gMTAxNTtcblx0dmFyIEhhbGZGbG9hdFR5cGUgPSAxMDE2O1xuXHR2YXIgVW5zaWduZWRTaG9ydDQ0NDRUeXBlID0gMTAxNztcblx0dmFyIFVuc2lnbmVkU2hvcnQ1NTUxVHlwZSA9IDEwMTg7XG5cdHZhciBVbnNpZ25lZFNob3J0NTY1VHlwZSA9IDEwMTk7XG5cdHZhciBVbnNpZ25lZEludDI0OFR5cGUgPSAxMDIwO1xuXHR2YXIgQWxwaGFGb3JtYXQgPSAxMDIxO1xuXHR2YXIgUkdCRm9ybWF0ID0gMTAyMjtcblx0dmFyIFJHQkFGb3JtYXQgPSAxMDIzO1xuXHR2YXIgTHVtaW5hbmNlRm9ybWF0ID0gMTAyNDtcblx0dmFyIEx1bWluYW5jZUFscGhhRm9ybWF0ID0gMTAyNTtcblx0dmFyIFJHQkVGb3JtYXQgPSBSR0JBRm9ybWF0O1xuXHR2YXIgRGVwdGhGb3JtYXQgPSAxMDI2O1xuXHR2YXIgRGVwdGhTdGVuY2lsRm9ybWF0ID0gMTAyNztcblx0dmFyIFJHQl9TM1RDX0RYVDFfRm9ybWF0ID0gMjAwMTtcblx0dmFyIFJHQkFfUzNUQ19EWFQxX0Zvcm1hdCA9IDIwMDI7XG5cdHZhciBSR0JBX1MzVENfRFhUM19Gb3JtYXQgPSAyMDAzO1xuXHR2YXIgUkdCQV9TM1RDX0RYVDVfRm9ybWF0ID0gMjAwNDtcblx0dmFyIFJHQl9QVlJUQ180QlBQVjFfRm9ybWF0ID0gMjEwMDtcblx0dmFyIFJHQl9QVlJUQ18yQlBQVjFfRm9ybWF0ID0gMjEwMTtcblx0dmFyIFJHQkFfUFZSVENfNEJQUFYxX0Zvcm1hdCA9IDIxMDI7XG5cdHZhciBSR0JBX1BWUlRDXzJCUFBWMV9Gb3JtYXQgPSAyMTAzO1xuXHR2YXIgUkdCX0VUQzFfRm9ybWF0ID0gMjE1MTtcblx0dmFyIExvb3BPbmNlID0gMjIwMDtcblx0dmFyIExvb3BSZXBlYXQgPSAyMjAxO1xuXHR2YXIgTG9vcFBpbmdQb25nID0gMjIwMjtcblx0dmFyIEludGVycG9sYXRlRGlzY3JldGUgPSAyMzAwO1xuXHR2YXIgSW50ZXJwb2xhdGVMaW5lYXIgPSAyMzAxO1xuXHR2YXIgSW50ZXJwb2xhdGVTbW9vdGggPSAyMzAyO1xuXHR2YXIgWmVyb0N1cnZhdHVyZUVuZGluZyA9IDI0MDA7XG5cdHZhciBaZXJvU2xvcGVFbmRpbmcgPSAyNDAxO1xuXHR2YXIgV3JhcEFyb3VuZEVuZGluZyA9IDI0MDI7XG5cdHZhciBUcmlhbmdsZXNEcmF3TW9kZSA9IDA7XG5cdHZhciBUcmlhbmdsZVN0cmlwRHJhd01vZGUgPSAxO1xuXHR2YXIgVHJpYW5nbGVGYW5EcmF3TW9kZSA9IDI7XG5cdHZhciBMaW5lYXJFbmNvZGluZyA9IDMwMDA7XG5cdHZhciBzUkdCRW5jb2RpbmcgPSAzMDAxO1xuXHR2YXIgR2FtbWFFbmNvZGluZyA9IDMwMDc7XG5cdHZhciBSR0JFRW5jb2RpbmcgPSAzMDAyO1xuXHR2YXIgTG9nTHV2RW5jb2RpbmcgPSAzMDAzO1xuXHR2YXIgUkdCTTdFbmNvZGluZyA9IDMwMDQ7XG5cdHZhciBSR0JNMTZFbmNvZGluZyA9IDMwMDU7XG5cdHZhciBSR0JERW5jb2RpbmcgPSAzMDA2O1xuXHR2YXIgQmFzaWNEZXB0aFBhY2tpbmcgPSAzMjAwO1xuXHR2YXIgUkdCQURlcHRoUGFja2luZyA9IDMyMDE7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdHZhciBfTWF0aCA9IHtcblxuXHRcdERFRzJSQUQ6IE1hdGguUEkgLyAxODAsXG5cdFx0UkFEMkRFRzogMTgwIC8gTWF0aC5QSSxcblxuXHRcdGdlbmVyYXRlVVVJRDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHQvLyBodHRwOi8vd3d3LmJyb29mYS5jb20vVG9vbHMvTWF0aC51dWlkLmh0bVxuXG5cdFx0XHR2YXIgY2hhcnMgPSAnMDEyMzQ1Njc4OUFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXonLnNwbGl0KCAnJyApO1xuXHRcdFx0dmFyIHV1aWQgPSBuZXcgQXJyYXkoIDM2ICk7XG5cdFx0XHR2YXIgcm5kID0gMCwgcjtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIGdlbmVyYXRlVVVJRCgpIHtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCAzNjsgaSArKyApIHtcblxuXHRcdFx0XHRcdGlmICggaSA9PT0gOCB8fCBpID09PSAxMyB8fCBpID09PSAxOCB8fCBpID09PSAyMyApIHtcblxuXHRcdFx0XHRcdFx0dXVpZFsgaSBdID0gJy0nO1xuXG5cdFx0XHRcdFx0fSBlbHNlIGlmICggaSA9PT0gMTQgKSB7XG5cblx0XHRcdFx0XHRcdHV1aWRbIGkgXSA9ICc0JztcblxuXHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdGlmICggcm5kIDw9IDB4MDIgKSBybmQgPSAweDIwMDAwMDAgKyAoIE1hdGgucmFuZG9tKCkgKiAweDEwMDAwMDAgKSB8IDA7XG5cdFx0XHRcdFx0XHRyID0gcm5kICYgMHhmO1xuXHRcdFx0XHRcdFx0cm5kID0gcm5kID4+IDQ7XG5cdFx0XHRcdFx0XHR1dWlkWyBpIF0gPSBjaGFyc1sgKCBpID09PSAxOSApID8gKCByICYgMHgzICkgfCAweDggOiByIF07XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJldHVybiB1dWlkLmpvaW4oICcnICk7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRjbGFtcDogZnVuY3Rpb24gKCB2YWx1ZSwgbWluLCBtYXggKSB7XG5cblx0XHRcdHJldHVybiBNYXRoLm1heCggbWluLCBNYXRoLm1pbiggbWF4LCB2YWx1ZSApICk7XG5cblx0XHR9LFxuXG5cdFx0Ly8gY29tcHV0ZSBldWNsaWRpYW4gbW9kdWxvIG9mIG0gJSBuXG5cdFx0Ly8gaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTW9kdWxvX29wZXJhdGlvblxuXG5cdFx0ZXVjbGlkZWFuTW9kdWxvOiBmdW5jdGlvbiAoIG4sIG0gKSB7XG5cblx0XHRcdHJldHVybiAoICggbiAlIG0gKSArIG0gKSAlIG07XG5cblx0XHR9LFxuXG5cdFx0Ly8gTGluZWFyIG1hcHBpbmcgZnJvbSByYW5nZSA8YTEsIGEyPiB0byByYW5nZSA8YjEsIGIyPlxuXG5cdFx0bWFwTGluZWFyOiBmdW5jdGlvbiAoIHgsIGExLCBhMiwgYjEsIGIyICkge1xuXG5cdFx0XHRyZXR1cm4gYjEgKyAoIHggLSBhMSApICogKCBiMiAtIGIxICkgLyAoIGEyIC0gYTEgKTtcblxuXHRcdH0sXG5cblx0XHQvLyBodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9MaW5lYXJfaW50ZXJwb2xhdGlvblxuXG5cdFx0bGVycDogZnVuY3Rpb24gKCB4LCB5LCB0ICkge1xuXG5cdFx0XHRyZXR1cm4gKCAxIC0gdCApICogeCArIHQgKiB5O1xuXG5cdFx0fSxcblxuXHRcdC8vIGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU21vb3Roc3RlcFxuXG5cdFx0c21vb3Roc3RlcDogZnVuY3Rpb24gKCB4LCBtaW4sIG1heCApIHtcblxuXHRcdFx0aWYgKCB4IDw9IG1pbiApIHJldHVybiAwO1xuXHRcdFx0aWYgKCB4ID49IG1heCApIHJldHVybiAxO1xuXG5cdFx0XHR4ID0gKCB4IC0gbWluICkgLyAoIG1heCAtIG1pbiApO1xuXG5cdFx0XHRyZXR1cm4geCAqIHggKiAoIDMgLSAyICogeCApO1xuXG5cdFx0fSxcblxuXHRcdHNtb290aGVyc3RlcDogZnVuY3Rpb24gKCB4LCBtaW4sIG1heCApIHtcblxuXHRcdFx0aWYgKCB4IDw9IG1pbiApIHJldHVybiAwO1xuXHRcdFx0aWYgKCB4ID49IG1heCApIHJldHVybiAxO1xuXG5cdFx0XHR4ID0gKCB4IC0gbWluICkgLyAoIG1heCAtIG1pbiApO1xuXG5cdFx0XHRyZXR1cm4geCAqIHggKiB4ICogKCB4ICogKCB4ICogNiAtIDE1ICkgKyAxMCApO1xuXG5cdFx0fSxcblxuXHRcdC8vIFJhbmRvbSBpbnRlZ2VyIGZyb20gPGxvdywgaGlnaD4gaW50ZXJ2YWxcblxuXHRcdHJhbmRJbnQ6IGZ1bmN0aW9uICggbG93LCBoaWdoICkge1xuXG5cdFx0XHRyZXR1cm4gbG93ICsgTWF0aC5mbG9vciggTWF0aC5yYW5kb20oKSAqICggaGlnaCAtIGxvdyArIDEgKSApO1xuXG5cdFx0fSxcblxuXHRcdC8vIFJhbmRvbSBmbG9hdCBmcm9tIDxsb3csIGhpZ2g+IGludGVydmFsXG5cblx0XHRyYW5kRmxvYXQ6IGZ1bmN0aW9uICggbG93LCBoaWdoICkge1xuXG5cdFx0XHRyZXR1cm4gbG93ICsgTWF0aC5yYW5kb20oKSAqICggaGlnaCAtIGxvdyApO1xuXG5cdFx0fSxcblxuXHRcdC8vIFJhbmRvbSBmbG9hdCBmcm9tIDwtcmFuZ2UvMiwgcmFuZ2UvMj4gaW50ZXJ2YWxcblxuXHRcdHJhbmRGbG9hdFNwcmVhZDogZnVuY3Rpb24gKCByYW5nZSApIHtcblxuXHRcdFx0cmV0dXJuIHJhbmdlICogKCAwLjUgLSBNYXRoLnJhbmRvbSgpICk7XG5cblx0XHR9LFxuXG5cdFx0ZGVnVG9SYWQ6IGZ1bmN0aW9uICggZGVncmVlcyApIHtcblxuXHRcdFx0cmV0dXJuIGRlZ3JlZXMgKiBfTWF0aC5ERUcyUkFEO1xuXG5cdFx0fSxcblxuXHRcdHJhZFRvRGVnOiBmdW5jdGlvbiAoIHJhZGlhbnMgKSB7XG5cblx0XHRcdHJldHVybiByYWRpYW5zICogX01hdGguUkFEMkRFRztcblxuXHRcdH0sXG5cblx0XHRpc1Bvd2VyT2ZUd286IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdHJldHVybiAoIHZhbHVlICYgKCB2YWx1ZSAtIDEgKSApID09PSAwICYmIHZhbHVlICE9PSAwO1xuXG5cdFx0fSxcblxuXHRcdG5lYXJlc3RQb3dlck9mVHdvOiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHRyZXR1cm4gTWF0aC5wb3coIDIsIE1hdGgucm91bmQoIE1hdGgubG9nKCB2YWx1ZSApIC8gTWF0aC5MTjIgKSApO1xuXG5cdFx0fSxcblxuXHRcdG5leHRQb3dlck9mVHdvOiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHR2YWx1ZSAtLTtcblx0XHRcdHZhbHVlIHw9IHZhbHVlID4+IDE7XG5cdFx0XHR2YWx1ZSB8PSB2YWx1ZSA+PiAyO1xuXHRcdFx0dmFsdWUgfD0gdmFsdWUgPj4gNDtcblx0XHRcdHZhbHVlIHw9IHZhbHVlID4+IDg7XG5cdFx0XHR2YWx1ZSB8PSB2YWx1ZSA+PiAxNjtcblx0XHRcdHZhbHVlICsrO1xuXG5cdFx0XHRyZXR1cm4gdmFsdWU7XG5cblx0XHR9XG5cblx0fTtcblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICogQGF1dGhvciBwaGlsb2diIC8gaHR0cDovL2Jsb2cudGhlaml0Lm9yZy9cblx0ICogQGF1dGhvciBlZ3JhZXRoZXIgLyBodHRwOi8vZWdyYWV0aGVyLmNvbS9cblx0ICogQGF1dGhvciB6ejg1IC8gaHR0cDovL3d3dy5sYWI0Z2FtZXMubmV0L3p6ODUvYmxvZ1xuXHQgKi9cblxuXHRmdW5jdGlvbiBWZWN0b3IyKCB4LCB5ICkge1xuXG5cdFx0dGhpcy54ID0geCB8fCAwO1xuXHRcdHRoaXMueSA9IHkgfHwgMDtcblxuXHR9XG5cblx0VmVjdG9yMi5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogVmVjdG9yMixcblxuXHRcdGlzVmVjdG9yMjogdHJ1ZSxcblxuXHRcdGdldCB3aWR0aCgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMueDtcblxuXHRcdH0sXG5cblx0XHRzZXQgd2lkdGgoIHZhbHVlICkge1xuXG5cdFx0XHR0aGlzLnggPSB2YWx1ZTtcblxuXHRcdH0sXG5cblx0XHRnZXQgaGVpZ2h0KCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy55O1xuXG5cdFx0fSxcblxuXHRcdHNldCBoZWlnaHQoIHZhbHVlICkge1xuXG5cdFx0XHR0aGlzLnkgPSB2YWx1ZTtcblxuXHRcdH0sXG5cblx0XHQvL1xuXG5cdFx0c2V0OiBmdW5jdGlvbiAoIHgsIHkgKSB7XG5cblx0XHRcdHRoaXMueCA9IHg7XG5cdFx0XHR0aGlzLnkgPSB5O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRTY2FsYXI6IGZ1bmN0aW9uICggc2NhbGFyICkge1xuXG5cdFx0XHR0aGlzLnggPSBzY2FsYXI7XG5cdFx0XHR0aGlzLnkgPSBzY2FsYXI7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldFg6IGZ1bmN0aW9uICggeCApIHtcblxuXHRcdFx0dGhpcy54ID0geDtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0WTogZnVuY3Rpb24gKCB5ICkge1xuXG5cdFx0XHR0aGlzLnkgPSB5O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRDb21wb25lbnQ6IGZ1bmN0aW9uICggaW5kZXgsIHZhbHVlICkge1xuXG5cdFx0XHRzd2l0Y2ggKCBpbmRleCApIHtcblxuXHRcdFx0XHRjYXNlIDA6IHRoaXMueCA9IHZhbHVlOyBicmVhaztcblx0XHRcdFx0Y2FzZSAxOiB0aGlzLnkgPSB2YWx1ZTsgYnJlYWs7XG5cdFx0XHRcdGRlZmF1bHQ6IHRocm93IG5ldyBFcnJvciggJ2luZGV4IGlzIG91dCBvZiByYW5nZTogJyArIGluZGV4ICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Z2V0Q29tcG9uZW50OiBmdW5jdGlvbiAoIGluZGV4ICkge1xuXG5cdFx0XHRzd2l0Y2ggKCBpbmRleCApIHtcblxuXHRcdFx0XHRjYXNlIDA6IHJldHVybiB0aGlzLng7XG5cdFx0XHRcdGNhc2UgMTogcmV0dXJuIHRoaXMueTtcblx0XHRcdFx0ZGVmYXVsdDogdGhyb3cgbmV3IEVycm9yKCAnaW5kZXggaXMgb3V0IG9mIHJhbmdlOiAnICsgaW5kZXggKTtcblxuXHRcdFx0fVxuXG5cdFx0fSxcblxuXHRcdGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiBuZXcgdGhpcy5jb25zdHJ1Y3RvciggdGhpcy54LCB0aGlzLnkgKTtcblxuXHRcdH0sXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIHYgKSB7XG5cblx0XHRcdHRoaXMueCA9IHYueDtcblx0XHRcdHRoaXMueSA9IHYueTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0YWRkOiBmdW5jdGlvbiAoIHYsIHcgKSB7XG5cblx0XHRcdGlmICggdyAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLlZlY3RvcjI6IC5hZGQoKSBub3cgb25seSBhY2NlcHRzIG9uZSBhcmd1bWVudC4gVXNlIC5hZGRWZWN0b3JzKCBhLCBiICkgaW5zdGVhZC4nICk7XG5cdFx0XHRcdHJldHVybiB0aGlzLmFkZFZlY3RvcnMoIHYsIHcgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLnggKz0gdi54O1xuXHRcdFx0dGhpcy55ICs9IHYueTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0YWRkU2NhbGFyOiBmdW5jdGlvbiAoIHMgKSB7XG5cblx0XHRcdHRoaXMueCArPSBzO1xuXHRcdFx0dGhpcy55ICs9IHM7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGFkZFZlY3RvcnM6IGZ1bmN0aW9uICggYSwgYiApIHtcblxuXHRcdFx0dGhpcy54ID0gYS54ICsgYi54O1xuXHRcdFx0dGhpcy55ID0gYS55ICsgYi55O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRhZGRTY2FsZWRWZWN0b3I6IGZ1bmN0aW9uICggdiwgcyApIHtcblxuXHRcdFx0dGhpcy54ICs9IHYueCAqIHM7XG5cdFx0XHR0aGlzLnkgKz0gdi55ICogcztcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c3ViOiBmdW5jdGlvbiAoIHYsIHcgKSB7XG5cblx0XHRcdGlmICggdyAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLlZlY3RvcjI6IC5zdWIoKSBub3cgb25seSBhY2NlcHRzIG9uZSBhcmd1bWVudC4gVXNlIC5zdWJWZWN0b3JzKCBhLCBiICkgaW5zdGVhZC4nICk7XG5cdFx0XHRcdHJldHVybiB0aGlzLnN1YlZlY3RvcnMoIHYsIHcgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLnggLT0gdi54O1xuXHRcdFx0dGhpcy55IC09IHYueTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c3ViU2NhbGFyOiBmdW5jdGlvbiAoIHMgKSB7XG5cblx0XHRcdHRoaXMueCAtPSBzO1xuXHRcdFx0dGhpcy55IC09IHM7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHN1YlZlY3RvcnM6IGZ1bmN0aW9uICggYSwgYiApIHtcblxuXHRcdFx0dGhpcy54ID0gYS54IC0gYi54O1xuXHRcdFx0dGhpcy55ID0gYS55IC0gYi55O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRtdWx0aXBseTogZnVuY3Rpb24gKCB2ICkge1xuXG5cdFx0XHR0aGlzLnggKj0gdi54O1xuXHRcdFx0dGhpcy55ICo9IHYueTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0bXVsdGlwbHlTY2FsYXI6IGZ1bmN0aW9uICggc2NhbGFyICkge1xuXG5cdFx0XHRpZiAoIGlzRmluaXRlKCBzY2FsYXIgKSApIHtcblxuXHRcdFx0XHR0aGlzLnggKj0gc2NhbGFyO1xuXHRcdFx0XHR0aGlzLnkgKj0gc2NhbGFyO1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdHRoaXMueCA9IDA7XG5cdFx0XHRcdHRoaXMueSA9IDA7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0ZGl2aWRlOiBmdW5jdGlvbiAoIHYgKSB7XG5cblx0XHRcdHRoaXMueCAvPSB2Lng7XG5cdFx0XHR0aGlzLnkgLz0gdi55O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRkaXZpZGVTY2FsYXI6IGZ1bmN0aW9uICggc2NhbGFyICkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5tdWx0aXBseVNjYWxhciggMSAvIHNjYWxhciApO1xuXG5cdFx0fSxcblxuXHRcdG1pbjogZnVuY3Rpb24gKCB2ICkge1xuXG5cdFx0XHR0aGlzLnggPSBNYXRoLm1pbiggdGhpcy54LCB2LnggKTtcblx0XHRcdHRoaXMueSA9IE1hdGgubWluKCB0aGlzLnksIHYueSApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRtYXg6IGZ1bmN0aW9uICggdiApIHtcblxuXHRcdFx0dGhpcy54ID0gTWF0aC5tYXgoIHRoaXMueCwgdi54ICk7XG5cdFx0XHR0aGlzLnkgPSBNYXRoLm1heCggdGhpcy55LCB2LnkgKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Y2xhbXA6IGZ1bmN0aW9uICggbWluLCBtYXggKSB7XG5cblx0XHRcdC8vIFRoaXMgZnVuY3Rpb24gYXNzdW1lcyBtaW4gPCBtYXgsIGlmIHRoaXMgYXNzdW1wdGlvbiBpc24ndCB0cnVlIGl0IHdpbGwgbm90IG9wZXJhdGUgY29ycmVjdGx5XG5cblx0XHRcdHRoaXMueCA9IE1hdGgubWF4KCBtaW4ueCwgTWF0aC5taW4oIG1heC54LCB0aGlzLnggKSApO1xuXHRcdFx0dGhpcy55ID0gTWF0aC5tYXgoIG1pbi55LCBNYXRoLm1pbiggbWF4LnksIHRoaXMueSApICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGNsYW1wU2NhbGFyOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBtaW4sIG1heDtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIGNsYW1wU2NhbGFyKCBtaW5WYWwsIG1heFZhbCApIHtcblxuXHRcdFx0XHRpZiAoIG1pbiA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0bWluID0gbmV3IFZlY3RvcjIoKTtcblx0XHRcdFx0XHRtYXggPSBuZXcgVmVjdG9yMigpO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRtaW4uc2V0KCBtaW5WYWwsIG1pblZhbCApO1xuXHRcdFx0XHRtYXguc2V0KCBtYXhWYWwsIG1heFZhbCApO1xuXG5cdFx0XHRcdHJldHVybiB0aGlzLmNsYW1wKCBtaW4sIG1heCApO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0Y2xhbXBMZW5ndGg6IGZ1bmN0aW9uICggbWluLCBtYXggKSB7XG5cblx0XHRcdHZhciBsZW5ndGggPSB0aGlzLmxlbmd0aCgpO1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5tdWx0aXBseVNjYWxhciggTWF0aC5tYXgoIG1pbiwgTWF0aC5taW4oIG1heCwgbGVuZ3RoICkgKSAvIGxlbmd0aCApO1xuXG5cdFx0fSxcblxuXHRcdGZsb29yOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMueCA9IE1hdGguZmxvb3IoIHRoaXMueCApO1xuXHRcdFx0dGhpcy55ID0gTWF0aC5mbG9vciggdGhpcy55ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGNlaWw6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dGhpcy54ID0gTWF0aC5jZWlsKCB0aGlzLnggKTtcblx0XHRcdHRoaXMueSA9IE1hdGguY2VpbCggdGhpcy55ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHJvdW5kOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMueCA9IE1hdGgucm91bmQoIHRoaXMueCApO1xuXHRcdFx0dGhpcy55ID0gTWF0aC5yb3VuZCggdGhpcy55ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHJvdW5kVG9aZXJvOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMueCA9ICggdGhpcy54IDwgMCApID8gTWF0aC5jZWlsKCB0aGlzLnggKSA6IE1hdGguZmxvb3IoIHRoaXMueCApO1xuXHRcdFx0dGhpcy55ID0gKCB0aGlzLnkgPCAwICkgPyBNYXRoLmNlaWwoIHRoaXMueSApIDogTWF0aC5mbG9vciggdGhpcy55ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdG5lZ2F0ZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR0aGlzLnggPSAtIHRoaXMueDtcblx0XHRcdHRoaXMueSA9IC0gdGhpcy55O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRkb3Q6IGZ1bmN0aW9uICggdiApIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMueCAqIHYueCArIHRoaXMueSAqIHYueTtcblxuXHRcdH0sXG5cblx0XHRsZW5ndGhTcTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy54ICogdGhpcy54ICsgdGhpcy55ICogdGhpcy55O1xuXG5cdFx0fSxcblxuXHRcdGxlbmd0aDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gTWF0aC5zcXJ0KCB0aGlzLnggKiB0aGlzLnggKyB0aGlzLnkgKiB0aGlzLnkgKTtcblxuXHRcdH0sXG5cblx0XHRsZW5ndGhNYW5oYXR0YW46IGZ1bmN0aW9uKCkge1xuXG5cdFx0XHRyZXR1cm4gTWF0aC5hYnMoIHRoaXMueCApICsgTWF0aC5hYnMoIHRoaXMueSApO1xuXG5cdFx0fSxcblxuXHRcdG5vcm1hbGl6ZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5kaXZpZGVTY2FsYXIoIHRoaXMubGVuZ3RoKCkgKTtcblxuXHRcdH0sXG5cblx0XHRhbmdsZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHQvLyBjb21wdXRlcyB0aGUgYW5nbGUgaW4gcmFkaWFucyB3aXRoIHJlc3BlY3QgdG8gdGhlIHBvc2l0aXZlIHgtYXhpc1xuXG5cdFx0XHR2YXIgYW5nbGUgPSBNYXRoLmF0YW4yKCB0aGlzLnksIHRoaXMueCApO1xuXG5cdFx0XHRpZiAoIGFuZ2xlIDwgMCApIGFuZ2xlICs9IDIgKiBNYXRoLlBJO1xuXG5cdFx0XHRyZXR1cm4gYW5nbGU7XG5cblx0XHR9LFxuXG5cdFx0ZGlzdGFuY2VUbzogZnVuY3Rpb24gKCB2ICkge1xuXG5cdFx0XHRyZXR1cm4gTWF0aC5zcXJ0KCB0aGlzLmRpc3RhbmNlVG9TcXVhcmVkKCB2ICkgKTtcblxuXHRcdH0sXG5cblx0XHRkaXN0YW5jZVRvU3F1YXJlZDogZnVuY3Rpb24gKCB2ICkge1xuXG5cdFx0XHR2YXIgZHggPSB0aGlzLnggLSB2LngsIGR5ID0gdGhpcy55IC0gdi55O1xuXHRcdFx0cmV0dXJuIGR4ICogZHggKyBkeSAqIGR5O1xuXG5cdFx0fSxcblxuXHRcdGRpc3RhbmNlVG9NYW5oYXR0YW46IGZ1bmN0aW9uICggdiApIHtcblxuXHRcdFx0cmV0dXJuIE1hdGguYWJzKCB0aGlzLnggLSB2LnggKSArIE1hdGguYWJzKCB0aGlzLnkgLSB2LnkgKTtcblxuXHRcdH0sXG5cblx0XHRzZXRMZW5ndGg6IGZ1bmN0aW9uICggbGVuZ3RoICkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5tdWx0aXBseVNjYWxhciggbGVuZ3RoIC8gdGhpcy5sZW5ndGgoKSApO1xuXG5cdFx0fSxcblxuXHRcdGxlcnA6IGZ1bmN0aW9uICggdiwgYWxwaGEgKSB7XG5cblx0XHRcdHRoaXMueCArPSAoIHYueCAtIHRoaXMueCApICogYWxwaGE7XG5cdFx0XHR0aGlzLnkgKz0gKCB2LnkgLSB0aGlzLnkgKSAqIGFscGhhO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRsZXJwVmVjdG9yczogZnVuY3Rpb24gKCB2MSwgdjIsIGFscGhhICkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5zdWJWZWN0b3JzKCB2MiwgdjEgKS5tdWx0aXBseVNjYWxhciggYWxwaGEgKS5hZGQoIHYxICk7XG5cblx0XHR9LFxuXG5cdFx0ZXF1YWxzOiBmdW5jdGlvbiAoIHYgKSB7XG5cblx0XHRcdHJldHVybiAoICggdi54ID09PSB0aGlzLnggKSAmJiAoIHYueSA9PT0gdGhpcy55ICkgKTtcblxuXHRcdH0sXG5cblx0XHRmcm9tQXJyYXk6IGZ1bmN0aW9uICggYXJyYXksIG9mZnNldCApIHtcblxuXHRcdFx0aWYgKCBvZmZzZXQgPT09IHVuZGVmaW5lZCApIG9mZnNldCA9IDA7XG5cblx0XHRcdHRoaXMueCA9IGFycmF5WyBvZmZzZXQgXTtcblx0XHRcdHRoaXMueSA9IGFycmF5WyBvZmZzZXQgKyAxIF07XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHRvQXJyYXk6IGZ1bmN0aW9uICggYXJyYXksIG9mZnNldCApIHtcblxuXHRcdFx0aWYgKCBhcnJheSA9PT0gdW5kZWZpbmVkICkgYXJyYXkgPSBbXTtcblx0XHRcdGlmICggb2Zmc2V0ID09PSB1bmRlZmluZWQgKSBvZmZzZXQgPSAwO1xuXG5cdFx0XHRhcnJheVsgb2Zmc2V0IF0gPSB0aGlzLng7XG5cdFx0XHRhcnJheVsgb2Zmc2V0ICsgMSBdID0gdGhpcy55O1xuXG5cdFx0XHRyZXR1cm4gYXJyYXk7XG5cblx0XHR9LFxuXG5cdFx0ZnJvbUF0dHJpYnV0ZTogZnVuY3Rpb24gKCBhdHRyaWJ1dGUsIGluZGV4LCBvZmZzZXQgKSB7XG5cblx0XHRcdGlmICggb2Zmc2V0ICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuVmVjdG9yMjogb2Zmc2V0IGhhcyBiZWVuIHJlbW92ZWQgZnJvbSAuZnJvbUF0dHJpYnV0ZSgpLicgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLnggPSBhdHRyaWJ1dGUuZ2V0WCggaW5kZXggKTtcblx0XHRcdHRoaXMueSA9IGF0dHJpYnV0ZS5nZXRZKCBpbmRleCApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRyb3RhdGVBcm91bmQ6IGZ1bmN0aW9uICggY2VudGVyLCBhbmdsZSApIHtcblxuXHRcdFx0dmFyIGMgPSBNYXRoLmNvcyggYW5nbGUgKSwgcyA9IE1hdGguc2luKCBhbmdsZSApO1xuXG5cdFx0XHR2YXIgeCA9IHRoaXMueCAtIGNlbnRlci54O1xuXHRcdFx0dmFyIHkgPSB0aGlzLnkgLSBjZW50ZXIueTtcblxuXHRcdFx0dGhpcy54ID0geCAqIGMgLSB5ICogcyArIGNlbnRlci54O1xuXHRcdFx0dGhpcy55ID0geCAqIHMgKyB5ICogYyArIGNlbnRlci55O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH1cblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKiBAYXV0aG9yIHN6aW1layAvIGh0dHBzOi8vZ2l0aHViLmNvbS9zemltZWsvXG5cdCAqL1xuXG5cdHZhciB0ZXh0dXJlSWQgPSAwO1xuXG5cdGZ1bmN0aW9uIFRleHR1cmUoIGltYWdlLCBtYXBwaW5nLCB3cmFwUywgd3JhcFQsIG1hZ0ZpbHRlciwgbWluRmlsdGVyLCBmb3JtYXQsIHR5cGUsIGFuaXNvdHJvcHksIGVuY29kaW5nICkge1xuXG5cdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KCB0aGlzLCAnaWQnLCB7IHZhbHVlOiB0ZXh0dXJlSWQgKysgfSApO1xuXG5cdFx0dGhpcy51dWlkID0gX01hdGguZ2VuZXJhdGVVVUlEKCk7XG5cblx0XHR0aGlzLm5hbWUgPSAnJztcblxuXHRcdHRoaXMuaW1hZ2UgPSBpbWFnZSAhPT0gdW5kZWZpbmVkID8gaW1hZ2UgOiBUZXh0dXJlLkRFRkFVTFRfSU1BR0U7XG5cdFx0dGhpcy5taXBtYXBzID0gW107XG5cblx0XHR0aGlzLm1hcHBpbmcgPSBtYXBwaW5nICE9PSB1bmRlZmluZWQgPyBtYXBwaW5nIDogVGV4dHVyZS5ERUZBVUxUX01BUFBJTkc7XG5cblx0XHR0aGlzLndyYXBTID0gd3JhcFMgIT09IHVuZGVmaW5lZCA/IHdyYXBTIDogQ2xhbXBUb0VkZ2VXcmFwcGluZztcblx0XHR0aGlzLndyYXBUID0gd3JhcFQgIT09IHVuZGVmaW5lZCA/IHdyYXBUIDogQ2xhbXBUb0VkZ2VXcmFwcGluZztcblxuXHRcdHRoaXMubWFnRmlsdGVyID0gbWFnRmlsdGVyICE9PSB1bmRlZmluZWQgPyBtYWdGaWx0ZXIgOiBMaW5lYXJGaWx0ZXI7XG5cdFx0dGhpcy5taW5GaWx0ZXIgPSBtaW5GaWx0ZXIgIT09IHVuZGVmaW5lZCA/IG1pbkZpbHRlciA6IExpbmVhck1pcE1hcExpbmVhckZpbHRlcjtcblxuXHRcdHRoaXMuYW5pc290cm9weSA9IGFuaXNvdHJvcHkgIT09IHVuZGVmaW5lZCA/IGFuaXNvdHJvcHkgOiAxO1xuXG5cdFx0dGhpcy5mb3JtYXQgPSBmb3JtYXQgIT09IHVuZGVmaW5lZCA/IGZvcm1hdCA6IFJHQkFGb3JtYXQ7XG5cdFx0dGhpcy50eXBlID0gdHlwZSAhPT0gdW5kZWZpbmVkID8gdHlwZSA6IFVuc2lnbmVkQnl0ZVR5cGU7XG5cblx0XHR0aGlzLm9mZnNldCA9IG5ldyBWZWN0b3IyKCAwLCAwICk7XG5cdFx0dGhpcy5yZXBlYXQgPSBuZXcgVmVjdG9yMiggMSwgMSApO1xuXG5cdFx0dGhpcy5nZW5lcmF0ZU1pcG1hcHMgPSB0cnVlO1xuXHRcdHRoaXMucHJlbXVsdGlwbHlBbHBoYSA9IGZhbHNlO1xuXHRcdHRoaXMuZmxpcFkgPSB0cnVlO1xuXHRcdHRoaXMudW5wYWNrQWxpZ25tZW50ID0gNDtcdC8vIHZhbGlkIHZhbHVlczogMSwgMiwgNCwgOCAoc2VlIGh0dHA6Ly93d3cua2hyb25vcy5vcmcvb3BlbmdsZXMvc2RrL2RvY3MvbWFuL3hodG1sL2dsUGl4ZWxTdG9yZWkueG1sKVxuXG5cblx0XHQvLyBWYWx1ZXMgb2YgZW5jb2RpbmcgIT09IFRIUkVFLkxpbmVhckVuY29kaW5nIG9ubHkgc3VwcG9ydGVkIG9uIG1hcCwgZW52TWFwIGFuZCBlbWlzc2l2ZU1hcC5cblx0XHQvL1xuXHRcdC8vIEFsc28gY2hhbmdpbmcgdGhlIGVuY29kaW5nIGFmdGVyIGFscmVhZHkgdXNlZCBieSBhIE1hdGVyaWFsIHdpbGwgbm90IGF1dG9tYXRpY2FsbHkgbWFrZSB0aGUgTWF0ZXJpYWxcblx0XHQvLyB1cGRhdGUuICBZb3UgbmVlZCB0byBleHBsaWNpdGx5IGNhbGwgTWF0ZXJpYWwubmVlZHNVcGRhdGUgdG8gdHJpZ2dlciBpdCB0byByZWNvbXBpbGUuXG5cdFx0dGhpcy5lbmNvZGluZyA9IGVuY29kaW5nICE9PSB1bmRlZmluZWQgPyBlbmNvZGluZyA6IExpbmVhckVuY29kaW5nO1xuXG5cdFx0dGhpcy52ZXJzaW9uID0gMDtcblx0XHR0aGlzLm9uVXBkYXRlID0gbnVsbDtcblxuXHR9XG5cblx0VGV4dHVyZS5ERUZBVUxUX0lNQUdFID0gdW5kZWZpbmVkO1xuXHRUZXh0dXJlLkRFRkFVTFRfTUFQUElORyA9IFVWTWFwcGluZztcblxuXHRUZXh0dXJlLnByb3RvdHlwZSA9IHtcblxuXHRcdGNvbnN0cnVjdG9yOiBUZXh0dXJlLFxuXG5cdFx0aXNUZXh0dXJlOiB0cnVlLFxuXG5cdFx0c2V0IG5lZWRzVXBkYXRlKCB2YWx1ZSApIHtcblxuXHRcdFx0aWYgKCB2YWx1ZSA9PT0gdHJ1ZSApIHRoaXMudmVyc2lvbiArKztcblxuXHRcdH0sXG5cblx0XHRjbG9uZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IHRoaXMuY29uc3RydWN0b3IoKS5jb3B5KCB0aGlzICk7XG5cblx0XHR9LFxuXG5cdFx0Y29weTogZnVuY3Rpb24gKCBzb3VyY2UgKSB7XG5cblx0XHRcdHRoaXMuaW1hZ2UgPSBzb3VyY2UuaW1hZ2U7XG5cdFx0XHR0aGlzLm1pcG1hcHMgPSBzb3VyY2UubWlwbWFwcy5zbGljZSggMCApO1xuXG5cdFx0XHR0aGlzLm1hcHBpbmcgPSBzb3VyY2UubWFwcGluZztcblxuXHRcdFx0dGhpcy53cmFwUyA9IHNvdXJjZS53cmFwUztcblx0XHRcdHRoaXMud3JhcFQgPSBzb3VyY2Uud3JhcFQ7XG5cblx0XHRcdHRoaXMubWFnRmlsdGVyID0gc291cmNlLm1hZ0ZpbHRlcjtcblx0XHRcdHRoaXMubWluRmlsdGVyID0gc291cmNlLm1pbkZpbHRlcjtcblxuXHRcdFx0dGhpcy5hbmlzb3Ryb3B5ID0gc291cmNlLmFuaXNvdHJvcHk7XG5cblx0XHRcdHRoaXMuZm9ybWF0ID0gc291cmNlLmZvcm1hdDtcblx0XHRcdHRoaXMudHlwZSA9IHNvdXJjZS50eXBlO1xuXG5cdFx0XHR0aGlzLm9mZnNldC5jb3B5KCBzb3VyY2Uub2Zmc2V0ICk7XG5cdFx0XHR0aGlzLnJlcGVhdC5jb3B5KCBzb3VyY2UucmVwZWF0ICk7XG5cblx0XHRcdHRoaXMuZ2VuZXJhdGVNaXBtYXBzID0gc291cmNlLmdlbmVyYXRlTWlwbWFwcztcblx0XHRcdHRoaXMucHJlbXVsdGlwbHlBbHBoYSA9IHNvdXJjZS5wcmVtdWx0aXBseUFscGhhO1xuXHRcdFx0dGhpcy5mbGlwWSA9IHNvdXJjZS5mbGlwWTtcblx0XHRcdHRoaXMudW5wYWNrQWxpZ25tZW50ID0gc291cmNlLnVucGFja0FsaWdubWVudDtcblx0XHRcdHRoaXMuZW5jb2RpbmcgPSBzb3VyY2UuZW5jb2Rpbmc7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHRvSlNPTjogZnVuY3Rpb24gKCBtZXRhICkge1xuXG5cdFx0XHRpZiAoIG1ldGEudGV4dHVyZXNbIHRoaXMudXVpZCBdICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0cmV0dXJuIG1ldGEudGV4dHVyZXNbIHRoaXMudXVpZCBdO1xuXG5cdFx0XHR9XG5cblx0XHRcdGZ1bmN0aW9uIGdldERhdGFVUkwoIGltYWdlICkge1xuXG5cdFx0XHRcdHZhciBjYW52YXM7XG5cblx0XHRcdFx0aWYgKCBpbWFnZS50b0RhdGFVUkwgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdGNhbnZhcyA9IGltYWdlO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRjYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMoICdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sJywgJ2NhbnZhcycgKTtcblx0XHRcdFx0XHRjYW52YXMud2lkdGggPSBpbWFnZS53aWR0aDtcblx0XHRcdFx0XHRjYW52YXMuaGVpZ2h0ID0gaW1hZ2UuaGVpZ2h0O1xuXG5cdFx0XHRcdFx0Y2FudmFzLmdldENvbnRleHQoICcyZCcgKS5kcmF3SW1hZ2UoIGltYWdlLCAwLCAwLCBpbWFnZS53aWR0aCwgaW1hZ2UuaGVpZ2h0ICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggY2FudmFzLndpZHRoID4gMjA0OCB8fCBjYW52YXMuaGVpZ2h0ID4gMjA0OCApIHtcblxuXHRcdFx0XHRcdHJldHVybiBjYW52YXMudG9EYXRhVVJMKCAnaW1hZ2UvanBlZycsIDAuNiApO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRyZXR1cm4gY2FudmFzLnRvRGF0YVVSTCggJ2ltYWdlL3BuZycgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIG91dHB1dCA9IHtcblx0XHRcdFx0bWV0YWRhdGE6IHtcblx0XHRcdFx0XHR2ZXJzaW9uOiA0LjQsXG5cdFx0XHRcdFx0dHlwZTogJ1RleHR1cmUnLFxuXHRcdFx0XHRcdGdlbmVyYXRvcjogJ1RleHR1cmUudG9KU09OJ1xuXHRcdFx0XHR9LFxuXG5cdFx0XHRcdHV1aWQ6IHRoaXMudXVpZCxcblx0XHRcdFx0bmFtZTogdGhpcy5uYW1lLFxuXG5cdFx0XHRcdG1hcHBpbmc6IHRoaXMubWFwcGluZyxcblxuXHRcdFx0XHRyZXBlYXQ6IFsgdGhpcy5yZXBlYXQueCwgdGhpcy5yZXBlYXQueSBdLFxuXHRcdFx0XHRvZmZzZXQ6IFsgdGhpcy5vZmZzZXQueCwgdGhpcy5vZmZzZXQueSBdLFxuXHRcdFx0XHR3cmFwOiBbIHRoaXMud3JhcFMsIHRoaXMud3JhcFQgXSxcblxuXHRcdFx0XHRtaW5GaWx0ZXI6IHRoaXMubWluRmlsdGVyLFxuXHRcdFx0XHRtYWdGaWx0ZXI6IHRoaXMubWFnRmlsdGVyLFxuXHRcdFx0XHRhbmlzb3Ryb3B5OiB0aGlzLmFuaXNvdHJvcHksXG5cblx0XHRcdFx0ZmxpcFk6IHRoaXMuZmxpcFlcblx0XHRcdH07XG5cblx0XHRcdGlmICggdGhpcy5pbWFnZSAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdC8vIFRPRE86IE1vdmUgdG8gVEhSRUUuSW1hZ2VcblxuXHRcdFx0XHR2YXIgaW1hZ2UgPSB0aGlzLmltYWdlO1xuXG5cdFx0XHRcdGlmICggaW1hZ2UudXVpZCA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0aW1hZ2UudXVpZCA9IF9NYXRoLmdlbmVyYXRlVVVJRCgpOyAvLyBVR0hcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCBtZXRhLmltYWdlc1sgaW1hZ2UudXVpZCBdID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRtZXRhLmltYWdlc1sgaW1hZ2UudXVpZCBdID0ge1xuXHRcdFx0XHRcdFx0dXVpZDogaW1hZ2UudXVpZCxcblx0XHRcdFx0XHRcdHVybDogZ2V0RGF0YVVSTCggaW1hZ2UgKVxuXHRcdFx0XHRcdH07XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdG91dHB1dC5pbWFnZSA9IGltYWdlLnV1aWQ7XG5cblx0XHRcdH1cblxuXHRcdFx0bWV0YS50ZXh0dXJlc1sgdGhpcy51dWlkIF0gPSBvdXRwdXQ7XG5cblx0XHRcdHJldHVybiBvdXRwdXQ7XG5cblx0XHR9LFxuXG5cdFx0ZGlzcG9zZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR0aGlzLmRpc3BhdGNoRXZlbnQoIHsgdHlwZTogJ2Rpc3Bvc2UnIH0gKTtcblxuXHRcdH0sXG5cblx0XHR0cmFuc2Zvcm1VdjogZnVuY3Rpb24gKCB1diApIHtcblxuXHRcdFx0aWYgKCB0aGlzLm1hcHBpbmcgIT09IFVWTWFwcGluZyApIHJldHVybjtcblxuXHRcdFx0dXYubXVsdGlwbHkoIHRoaXMucmVwZWF0ICk7XG5cdFx0XHR1di5hZGQoIHRoaXMub2Zmc2V0ICk7XG5cblx0XHRcdGlmICggdXYueCA8IDAgfHwgdXYueCA+IDEgKSB7XG5cblx0XHRcdFx0c3dpdGNoICggdGhpcy53cmFwUyApIHtcblxuXHRcdFx0XHRcdGNhc2UgUmVwZWF0V3JhcHBpbmc6XG5cblx0XHRcdFx0XHRcdHV2LnggPSB1di54IC0gTWF0aC5mbG9vciggdXYueCApO1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRjYXNlIENsYW1wVG9FZGdlV3JhcHBpbmc6XG5cblx0XHRcdFx0XHRcdHV2LnggPSB1di54IDwgMCA/IDAgOiAxO1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRjYXNlIE1pcnJvcmVkUmVwZWF0V3JhcHBpbmc6XG5cblx0XHRcdFx0XHRcdGlmICggTWF0aC5hYnMoIE1hdGguZmxvb3IoIHV2LnggKSAlIDIgKSA9PT0gMSApIHtcblxuXHRcdFx0XHRcdFx0XHR1di54ID0gTWF0aC5jZWlsKCB1di54ICkgLSB1di54O1xuXG5cdFx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRcdHV2LnggPSB1di54IC0gTWF0aC5mbG9vciggdXYueCApO1xuXG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCB1di55IDwgMCB8fCB1di55ID4gMSApIHtcblxuXHRcdFx0XHRzd2l0Y2ggKCB0aGlzLndyYXBUICkge1xuXG5cdFx0XHRcdFx0Y2FzZSBSZXBlYXRXcmFwcGluZzpcblxuXHRcdFx0XHRcdFx0dXYueSA9IHV2LnkgLSBNYXRoLmZsb29yKCB1di55ICk7XG5cdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdGNhc2UgQ2xhbXBUb0VkZ2VXcmFwcGluZzpcblxuXHRcdFx0XHRcdFx0dXYueSA9IHV2LnkgPCAwID8gMCA6IDE7XG5cdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdGNhc2UgTWlycm9yZWRSZXBlYXRXcmFwcGluZzpcblxuXHRcdFx0XHRcdFx0aWYgKCBNYXRoLmFicyggTWF0aC5mbG9vciggdXYueSApICUgMiApID09PSAxICkge1xuXG5cdFx0XHRcdFx0XHRcdHV2LnkgPSBNYXRoLmNlaWwoIHV2LnkgKSAtIHV2Lnk7XG5cblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdFx0dXYueSA9IHV2LnkgLSBNYXRoLmZsb29yKCB1di55ICk7XG5cblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHRoaXMuZmxpcFkgKSB7XG5cblx0XHRcdFx0dXYueSA9IDEgLSB1di55O1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0fTtcblxuXHRPYmplY3QuYXNzaWduKCBUZXh0dXJlLnByb3RvdHlwZSwgRXZlbnREaXNwYXRjaGVyLnByb3RvdHlwZSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIHN1cGVyZWdnYmVydCAvIGh0dHA6Ly93d3cucGF1bGJydW50LmNvLnVrL1xuXHQgKiBAYXV0aG9yIHBoaWxvZ2IgLyBodHRwOi8vYmxvZy50aGVqaXQub3JnL1xuXHQgKiBAYXV0aG9yIG1pa2FlbCBlbXRpbmdlciAvIGh0dHA6Ly9nb21vLnNlL1xuXHQgKiBAYXV0aG9yIGVncmFldGhlciAvIGh0dHA6Ly9lZ3JhZXRoZXIuY29tL1xuXHQgKiBAYXV0aG9yIFdlc3RMYW5nbGV5IC8gaHR0cDovL2dpdGh1Yi5jb20vV2VzdExhbmdsZXlcblx0ICovXG5cblx0ZnVuY3Rpb24gVmVjdG9yNCggeCwgeSwgeiwgdyApIHtcblxuXHRcdHRoaXMueCA9IHggfHwgMDtcblx0XHR0aGlzLnkgPSB5IHx8IDA7XG5cdFx0dGhpcy56ID0geiB8fCAwO1xuXHRcdHRoaXMudyA9ICggdyAhPT0gdW5kZWZpbmVkICkgPyB3IDogMTtcblxuXHR9XG5cblx0VmVjdG9yNC5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogVmVjdG9yNCxcblxuXHRcdGlzVmVjdG9yNDogdHJ1ZSxcblxuXHRcdHNldDogZnVuY3Rpb24gKCB4LCB5LCB6LCB3ICkge1xuXG5cdFx0XHR0aGlzLnggPSB4O1xuXHRcdFx0dGhpcy55ID0geTtcblx0XHRcdHRoaXMueiA9IHo7XG5cdFx0XHR0aGlzLncgPSB3O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRTY2FsYXI6IGZ1bmN0aW9uICggc2NhbGFyICkge1xuXG5cdFx0XHR0aGlzLnggPSBzY2FsYXI7XG5cdFx0XHR0aGlzLnkgPSBzY2FsYXI7XG5cdFx0XHR0aGlzLnogPSBzY2FsYXI7XG5cdFx0XHR0aGlzLncgPSBzY2FsYXI7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldFg6IGZ1bmN0aW9uICggeCApIHtcblxuXHRcdFx0dGhpcy54ID0geDtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0WTogZnVuY3Rpb24gKCB5ICkge1xuXG5cdFx0XHR0aGlzLnkgPSB5O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRaOiBmdW5jdGlvbiAoIHogKSB7XG5cblx0XHRcdHRoaXMueiA9IHo7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldFc6IGZ1bmN0aW9uICggdyApIHtcblxuXHRcdFx0dGhpcy53ID0gdztcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0Q29tcG9uZW50OiBmdW5jdGlvbiAoIGluZGV4LCB2YWx1ZSApIHtcblxuXHRcdFx0c3dpdGNoICggaW5kZXggKSB7XG5cblx0XHRcdFx0Y2FzZSAwOiB0aGlzLnggPSB2YWx1ZTsgYnJlYWs7XG5cdFx0XHRcdGNhc2UgMTogdGhpcy55ID0gdmFsdWU7IGJyZWFrO1xuXHRcdFx0XHRjYXNlIDI6IHRoaXMueiA9IHZhbHVlOyBicmVhaztcblx0XHRcdFx0Y2FzZSAzOiB0aGlzLncgPSB2YWx1ZTsgYnJlYWs7XG5cdFx0XHRcdGRlZmF1bHQ6IHRocm93IG5ldyBFcnJvciggJ2luZGV4IGlzIG91dCBvZiByYW5nZTogJyArIGluZGV4ICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Z2V0Q29tcG9uZW50OiBmdW5jdGlvbiAoIGluZGV4ICkge1xuXG5cdFx0XHRzd2l0Y2ggKCBpbmRleCApIHtcblxuXHRcdFx0XHRjYXNlIDA6IHJldHVybiB0aGlzLng7XG5cdFx0XHRcdGNhc2UgMTogcmV0dXJuIHRoaXMueTtcblx0XHRcdFx0Y2FzZSAyOiByZXR1cm4gdGhpcy56O1xuXHRcdFx0XHRjYXNlIDM6IHJldHVybiB0aGlzLnc7XG5cdFx0XHRcdGRlZmF1bHQ6IHRocm93IG5ldyBFcnJvciggJ2luZGV4IGlzIG91dCBvZiByYW5nZTogJyArIGluZGV4ICk7XG5cblx0XHRcdH1cblxuXHRcdH0sXG5cblx0XHRjbG9uZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IHRoaXMuY29uc3RydWN0b3IoIHRoaXMueCwgdGhpcy55LCB0aGlzLnosIHRoaXMudyApO1xuXG5cdFx0fSxcblxuXHRcdGNvcHk6IGZ1bmN0aW9uICggdiApIHtcblxuXHRcdFx0dGhpcy54ID0gdi54O1xuXHRcdFx0dGhpcy55ID0gdi55O1xuXHRcdFx0dGhpcy56ID0gdi56O1xuXHRcdFx0dGhpcy53ID0gKCB2LncgIT09IHVuZGVmaW5lZCApID8gdi53IDogMTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0YWRkOiBmdW5jdGlvbiAoIHYsIHcgKSB7XG5cblx0XHRcdGlmICggdyAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLlZlY3RvcjQ6IC5hZGQoKSBub3cgb25seSBhY2NlcHRzIG9uZSBhcmd1bWVudC4gVXNlIC5hZGRWZWN0b3JzKCBhLCBiICkgaW5zdGVhZC4nICk7XG5cdFx0XHRcdHJldHVybiB0aGlzLmFkZFZlY3RvcnMoIHYsIHcgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLnggKz0gdi54O1xuXHRcdFx0dGhpcy55ICs9IHYueTtcblx0XHRcdHRoaXMueiArPSB2Lno7XG5cdFx0XHR0aGlzLncgKz0gdi53O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRhZGRTY2FsYXI6IGZ1bmN0aW9uICggcyApIHtcblxuXHRcdFx0dGhpcy54ICs9IHM7XG5cdFx0XHR0aGlzLnkgKz0gcztcblx0XHRcdHRoaXMueiArPSBzO1xuXHRcdFx0dGhpcy53ICs9IHM7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGFkZFZlY3RvcnM6IGZ1bmN0aW9uICggYSwgYiApIHtcblxuXHRcdFx0dGhpcy54ID0gYS54ICsgYi54O1xuXHRcdFx0dGhpcy55ID0gYS55ICsgYi55O1xuXHRcdFx0dGhpcy56ID0gYS56ICsgYi56O1xuXHRcdFx0dGhpcy53ID0gYS53ICsgYi53O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRhZGRTY2FsZWRWZWN0b3I6IGZ1bmN0aW9uICggdiwgcyApIHtcblxuXHRcdFx0dGhpcy54ICs9IHYueCAqIHM7XG5cdFx0XHR0aGlzLnkgKz0gdi55ICogcztcblx0XHRcdHRoaXMueiArPSB2LnogKiBzO1xuXHRcdFx0dGhpcy53ICs9IHYudyAqIHM7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHN1YjogZnVuY3Rpb24gKCB2LCB3ICkge1xuXG5cdFx0XHRpZiAoIHcgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5WZWN0b3I0OiAuc3ViKCkgbm93IG9ubHkgYWNjZXB0cyBvbmUgYXJndW1lbnQuIFVzZSAuc3ViVmVjdG9ycyggYSwgYiApIGluc3RlYWQuJyApO1xuXHRcdFx0XHRyZXR1cm4gdGhpcy5zdWJWZWN0b3JzKCB2LCB3ICk7XG5cblx0XHRcdH1cblxuXHRcdFx0dGhpcy54IC09IHYueDtcblx0XHRcdHRoaXMueSAtPSB2Lnk7XG5cdFx0XHR0aGlzLnogLT0gdi56O1xuXHRcdFx0dGhpcy53IC09IHYudztcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c3ViU2NhbGFyOiBmdW5jdGlvbiAoIHMgKSB7XG5cblx0XHRcdHRoaXMueCAtPSBzO1xuXHRcdFx0dGhpcy55IC09IHM7XG5cdFx0XHR0aGlzLnogLT0gcztcblx0XHRcdHRoaXMudyAtPSBzO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzdWJWZWN0b3JzOiBmdW5jdGlvbiAoIGEsIGIgKSB7XG5cblx0XHRcdHRoaXMueCA9IGEueCAtIGIueDtcblx0XHRcdHRoaXMueSA9IGEueSAtIGIueTtcblx0XHRcdHRoaXMueiA9IGEueiAtIGIuejtcblx0XHRcdHRoaXMudyA9IGEudyAtIGIudztcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0bXVsdGlwbHlTY2FsYXI6IGZ1bmN0aW9uICggc2NhbGFyICkge1xuXG5cdFx0XHRpZiAoIGlzRmluaXRlKCBzY2FsYXIgKSApIHtcblxuXHRcdFx0XHR0aGlzLnggKj0gc2NhbGFyO1xuXHRcdFx0XHR0aGlzLnkgKj0gc2NhbGFyO1xuXHRcdFx0XHR0aGlzLnogKj0gc2NhbGFyO1xuXHRcdFx0XHR0aGlzLncgKj0gc2NhbGFyO1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdHRoaXMueCA9IDA7XG5cdFx0XHRcdHRoaXMueSA9IDA7XG5cdFx0XHRcdHRoaXMueiA9IDA7XG5cdFx0XHRcdHRoaXMudyA9IDA7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0YXBwbHlNYXRyaXg0OiBmdW5jdGlvbiAoIG0gKSB7XG5cblx0XHRcdHZhciB4ID0gdGhpcy54LCB5ID0gdGhpcy55LCB6ID0gdGhpcy56LCB3ID0gdGhpcy53O1xuXHRcdFx0dmFyIGUgPSBtLmVsZW1lbnRzO1xuXG5cdFx0XHR0aGlzLnggPSBlWyAwIF0gKiB4ICsgZVsgNCBdICogeSArIGVbIDggXSAqIHogKyBlWyAxMiBdICogdztcblx0XHRcdHRoaXMueSA9IGVbIDEgXSAqIHggKyBlWyA1IF0gKiB5ICsgZVsgOSBdICogeiArIGVbIDEzIF0gKiB3O1xuXHRcdFx0dGhpcy56ID0gZVsgMiBdICogeCArIGVbIDYgXSAqIHkgKyBlWyAxMCBdICogeiArIGVbIDE0IF0gKiB3O1xuXHRcdFx0dGhpcy53ID0gZVsgMyBdICogeCArIGVbIDcgXSAqIHkgKyBlWyAxMSBdICogeiArIGVbIDE1IF0gKiB3O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRkaXZpZGVTY2FsYXI6IGZ1bmN0aW9uICggc2NhbGFyICkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5tdWx0aXBseVNjYWxhciggMSAvIHNjYWxhciApO1xuXG5cdFx0fSxcblxuXHRcdHNldEF4aXNBbmdsZUZyb21RdWF0ZXJuaW9uOiBmdW5jdGlvbiAoIHEgKSB7XG5cblx0XHRcdC8vIGh0dHA6Ly93d3cuZXVjbGlkZWFuc3BhY2UuY29tL21hdGhzL2dlb21ldHJ5L3JvdGF0aW9ucy9jb252ZXJzaW9ucy9xdWF0ZXJuaW9uVG9BbmdsZS9pbmRleC5odG1cblxuXHRcdFx0Ly8gcSBpcyBhc3N1bWVkIHRvIGJlIG5vcm1hbGl6ZWRcblxuXHRcdFx0dGhpcy53ID0gMiAqIE1hdGguYWNvcyggcS53ICk7XG5cblx0XHRcdHZhciBzID0gTWF0aC5zcXJ0KCAxIC0gcS53ICogcS53ICk7XG5cblx0XHRcdGlmICggcyA8IDAuMDAwMSApIHtcblxuXHRcdFx0XHQgdGhpcy54ID0gMTtcblx0XHRcdFx0IHRoaXMueSA9IDA7XG5cdFx0XHRcdCB0aGlzLnogPSAwO1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdCB0aGlzLnggPSBxLnggLyBzO1xuXHRcdFx0XHQgdGhpcy55ID0gcS55IC8gcztcblx0XHRcdFx0IHRoaXMueiA9IHEueiAvIHM7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0QXhpc0FuZ2xlRnJvbVJvdGF0aW9uTWF0cml4OiBmdW5jdGlvbiAoIG0gKSB7XG5cblx0XHRcdC8vIGh0dHA6Ly93d3cuZXVjbGlkZWFuc3BhY2UuY29tL21hdGhzL2dlb21ldHJ5L3JvdGF0aW9ucy9jb252ZXJzaW9ucy9tYXRyaXhUb0FuZ2xlL2luZGV4Lmh0bVxuXG5cdFx0XHQvLyBhc3N1bWVzIHRoZSB1cHBlciAzeDMgb2YgbSBpcyBhIHB1cmUgcm90YXRpb24gbWF0cml4IChpLmUsIHVuc2NhbGVkKVxuXG5cdFx0XHR2YXIgYW5nbGUsIHgsIHksIHosXHRcdC8vIHZhcmlhYmxlcyBmb3IgcmVzdWx0XG5cdFx0XHRcdGVwc2lsb24gPSAwLjAxLFx0XHQvLyBtYXJnaW4gdG8gYWxsb3cgZm9yIHJvdW5kaW5nIGVycm9yc1xuXHRcdFx0XHRlcHNpbG9uMiA9IDAuMSxcdFx0Ly8gbWFyZ2luIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gMCBhbmQgMTgwIGRlZ3JlZXNcblxuXHRcdFx0XHR0ZSA9IG0uZWxlbWVudHMsXG5cblx0XHRcdFx0bTExID0gdGVbIDAgXSwgbTEyID0gdGVbIDQgXSwgbTEzID0gdGVbIDggXSxcblx0XHRcdFx0bTIxID0gdGVbIDEgXSwgbTIyID0gdGVbIDUgXSwgbTIzID0gdGVbIDkgXSxcblx0XHRcdFx0bTMxID0gdGVbIDIgXSwgbTMyID0gdGVbIDYgXSwgbTMzID0gdGVbIDEwIF07XG5cblx0XHRcdGlmICggKCBNYXRoLmFicyggbTEyIC0gbTIxICkgPCBlcHNpbG9uICkgJiZcblx0XHRcdCAgICAgKCBNYXRoLmFicyggbTEzIC0gbTMxICkgPCBlcHNpbG9uICkgJiZcblx0XHRcdCAgICAgKCBNYXRoLmFicyggbTIzIC0gbTMyICkgPCBlcHNpbG9uICkgKSB7XG5cblx0XHRcdFx0Ly8gc2luZ3VsYXJpdHkgZm91bmRcblx0XHRcdFx0Ly8gZmlyc3QgY2hlY2sgZm9yIGlkZW50aXR5IG1hdHJpeCB3aGljaCBtdXN0IGhhdmUgKzEgZm9yIGFsbCB0ZXJtc1xuXHRcdFx0XHQvLyBpbiBsZWFkaW5nIGRpYWdvbmFsIGFuZCB6ZXJvIGluIG90aGVyIHRlcm1zXG5cblx0XHRcdFx0aWYgKCAoIE1hdGguYWJzKCBtMTIgKyBtMjEgKSA8IGVwc2lsb24yICkgJiZcblx0XHRcdFx0ICAgICAoIE1hdGguYWJzKCBtMTMgKyBtMzEgKSA8IGVwc2lsb24yICkgJiZcblx0XHRcdFx0ICAgICAoIE1hdGguYWJzKCBtMjMgKyBtMzIgKSA8IGVwc2lsb24yICkgJiZcblx0XHRcdFx0ICAgICAoIE1hdGguYWJzKCBtMTEgKyBtMjIgKyBtMzMgLSAzICkgPCBlcHNpbG9uMiApICkge1xuXG5cdFx0XHRcdFx0Ly8gdGhpcyBzaW5ndWxhcml0eSBpcyBpZGVudGl0eSBtYXRyaXggc28gYW5nbGUgPSAwXG5cblx0XHRcdFx0XHR0aGlzLnNldCggMSwgMCwgMCwgMCApO1xuXG5cdFx0XHRcdFx0cmV0dXJuIHRoaXM7IC8vIHplcm8gYW5nbGUsIGFyYml0cmFyeSBheGlzXG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIG90aGVyd2lzZSB0aGlzIHNpbmd1bGFyaXR5IGlzIGFuZ2xlID0gMTgwXG5cblx0XHRcdFx0YW5nbGUgPSBNYXRoLlBJO1xuXG5cdFx0XHRcdHZhciB4eCA9ICggbTExICsgMSApIC8gMjtcblx0XHRcdFx0dmFyIHl5ID0gKCBtMjIgKyAxICkgLyAyO1xuXHRcdFx0XHR2YXIgenogPSAoIG0zMyArIDEgKSAvIDI7XG5cdFx0XHRcdHZhciB4eSA9ICggbTEyICsgbTIxICkgLyA0O1xuXHRcdFx0XHR2YXIgeHogPSAoIG0xMyArIG0zMSApIC8gNDtcblx0XHRcdFx0dmFyIHl6ID0gKCBtMjMgKyBtMzIgKSAvIDQ7XG5cblx0XHRcdFx0aWYgKCAoIHh4ID4geXkgKSAmJiAoIHh4ID4genogKSApIHtcblxuXHRcdFx0XHRcdC8vIG0xMSBpcyB0aGUgbGFyZ2VzdCBkaWFnb25hbCB0ZXJtXG5cblx0XHRcdFx0XHRpZiAoIHh4IDwgZXBzaWxvbiApIHtcblxuXHRcdFx0XHRcdFx0eCA9IDA7XG5cdFx0XHRcdFx0XHR5ID0gMC43MDcxMDY3ODE7XG5cdFx0XHRcdFx0XHR6ID0gMC43MDcxMDY3ODE7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHR4ID0gTWF0aC5zcXJ0KCB4eCApO1xuXHRcdFx0XHRcdFx0eSA9IHh5IC8geDtcblx0XHRcdFx0XHRcdHogPSB4eiAvIHg7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSBlbHNlIGlmICggeXkgPiB6eiApIHtcblxuXHRcdFx0XHRcdC8vIG0yMiBpcyB0aGUgbGFyZ2VzdCBkaWFnb25hbCB0ZXJtXG5cblx0XHRcdFx0XHRpZiAoIHl5IDwgZXBzaWxvbiApIHtcblxuXHRcdFx0XHRcdFx0eCA9IDAuNzA3MTA2NzgxO1xuXHRcdFx0XHRcdFx0eSA9IDA7XG5cdFx0XHRcdFx0XHR6ID0gMC43MDcxMDY3ODE7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHR5ID0gTWF0aC5zcXJ0KCB5eSApO1xuXHRcdFx0XHRcdFx0eCA9IHh5IC8geTtcblx0XHRcdFx0XHRcdHogPSB5eiAvIHk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdC8vIG0zMyBpcyB0aGUgbGFyZ2VzdCBkaWFnb25hbCB0ZXJtIHNvIGJhc2UgcmVzdWx0IG9uIHRoaXNcblxuXHRcdFx0XHRcdGlmICggenogPCBlcHNpbG9uICkge1xuXG5cdFx0XHRcdFx0XHR4ID0gMC43MDcxMDY3ODE7XG5cdFx0XHRcdFx0XHR5ID0gMC43MDcxMDY3ODE7XG5cdFx0XHRcdFx0XHR6ID0gMDtcblxuXHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdHogPSBNYXRoLnNxcnQoIHp6ICk7XG5cdFx0XHRcdFx0XHR4ID0geHogLyB6O1xuXHRcdFx0XHRcdFx0eSA9IHl6IC8gejtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0dGhpcy5zZXQoIHgsIHksIHosIGFuZ2xlICk7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXM7IC8vIHJldHVybiAxODAgZGVnIHJvdGF0aW9uXG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gYXMgd2UgaGF2ZSByZWFjaGVkIGhlcmUgdGhlcmUgYXJlIG5vIHNpbmd1bGFyaXRpZXMgc28gd2UgY2FuIGhhbmRsZSBub3JtYWxseVxuXG5cdFx0XHR2YXIgcyA9IE1hdGguc3FydCggKCBtMzIgLSBtMjMgKSAqICggbTMyIC0gbTIzICkgK1xuXHRcdFx0ICAgICAgICAgICAgICAgICAgICggbTEzIC0gbTMxICkgKiAoIG0xMyAtIG0zMSApICtcblx0XHRcdCAgICAgICAgICAgICAgICAgICAoIG0yMSAtIG0xMiApICogKCBtMjEgLSBtMTIgKSApOyAvLyB1c2VkIHRvIG5vcm1hbGl6ZVxuXG5cdFx0XHRpZiAoIE1hdGguYWJzKCBzICkgPCAwLjAwMSApIHMgPSAxO1xuXG5cdFx0XHQvLyBwcmV2ZW50IGRpdmlkZSBieSB6ZXJvLCBzaG91bGQgbm90IGhhcHBlbiBpZiBtYXRyaXggaXMgb3J0aG9nb25hbCBhbmQgc2hvdWxkIGJlXG5cdFx0XHQvLyBjYXVnaHQgYnkgc2luZ3VsYXJpdHkgdGVzdCBhYm92ZSwgYnV0IEkndmUgbGVmdCBpdCBpbiBqdXN0IGluIGNhc2VcblxuXHRcdFx0dGhpcy54ID0gKCBtMzIgLSBtMjMgKSAvIHM7XG5cdFx0XHR0aGlzLnkgPSAoIG0xMyAtIG0zMSApIC8gcztcblx0XHRcdHRoaXMueiA9ICggbTIxIC0gbTEyICkgLyBzO1xuXHRcdFx0dGhpcy53ID0gTWF0aC5hY29zKCAoIG0xMSArIG0yMiArIG0zMyAtIDEgKSAvIDIgKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0bWluOiBmdW5jdGlvbiAoIHYgKSB7XG5cblx0XHRcdHRoaXMueCA9IE1hdGgubWluKCB0aGlzLngsIHYueCApO1xuXHRcdFx0dGhpcy55ID0gTWF0aC5taW4oIHRoaXMueSwgdi55ICk7XG5cdFx0XHR0aGlzLnogPSBNYXRoLm1pbiggdGhpcy56LCB2LnogKTtcblx0XHRcdHRoaXMudyA9IE1hdGgubWluKCB0aGlzLncsIHYudyApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRtYXg6IGZ1bmN0aW9uICggdiApIHtcblxuXHRcdFx0dGhpcy54ID0gTWF0aC5tYXgoIHRoaXMueCwgdi54ICk7XG5cdFx0XHR0aGlzLnkgPSBNYXRoLm1heCggdGhpcy55LCB2LnkgKTtcblx0XHRcdHRoaXMueiA9IE1hdGgubWF4KCB0aGlzLnosIHYueiApO1xuXHRcdFx0dGhpcy53ID0gTWF0aC5tYXgoIHRoaXMudywgdi53ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGNsYW1wOiBmdW5jdGlvbiAoIG1pbiwgbWF4ICkge1xuXG5cdFx0XHQvLyBUaGlzIGZ1bmN0aW9uIGFzc3VtZXMgbWluIDwgbWF4LCBpZiB0aGlzIGFzc3VtcHRpb24gaXNuJ3QgdHJ1ZSBpdCB3aWxsIG5vdCBvcGVyYXRlIGNvcnJlY3RseVxuXG5cdFx0XHR0aGlzLnggPSBNYXRoLm1heCggbWluLngsIE1hdGgubWluKCBtYXgueCwgdGhpcy54ICkgKTtcblx0XHRcdHRoaXMueSA9IE1hdGgubWF4KCBtaW4ueSwgTWF0aC5taW4oIG1heC55LCB0aGlzLnkgKSApO1xuXHRcdFx0dGhpcy56ID0gTWF0aC5tYXgoIG1pbi56LCBNYXRoLm1pbiggbWF4LnosIHRoaXMueiApICk7XG5cdFx0XHR0aGlzLncgPSBNYXRoLm1heCggbWluLncsIE1hdGgubWluKCBtYXgudywgdGhpcy53ICkgKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Y2xhbXBTY2FsYXI6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIG1pbiwgbWF4O1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gY2xhbXBTY2FsYXIoIG1pblZhbCwgbWF4VmFsICkge1xuXG5cdFx0XHRcdGlmICggbWluID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRtaW4gPSBuZXcgVmVjdG9yNCgpO1xuXHRcdFx0XHRcdG1heCA9IG5ldyBWZWN0b3I0KCk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdG1pbi5zZXQoIG1pblZhbCwgbWluVmFsLCBtaW5WYWwsIG1pblZhbCApO1xuXHRcdFx0XHRtYXguc2V0KCBtYXhWYWwsIG1heFZhbCwgbWF4VmFsLCBtYXhWYWwgKTtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcy5jbGFtcCggbWluLCBtYXggKTtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdGZsb29yOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMueCA9IE1hdGguZmxvb3IoIHRoaXMueCApO1xuXHRcdFx0dGhpcy55ID0gTWF0aC5mbG9vciggdGhpcy55ICk7XG5cdFx0XHR0aGlzLnogPSBNYXRoLmZsb29yKCB0aGlzLnogKTtcblx0XHRcdHRoaXMudyA9IE1hdGguZmxvb3IoIHRoaXMudyApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjZWlsOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMueCA9IE1hdGguY2VpbCggdGhpcy54ICk7XG5cdFx0XHR0aGlzLnkgPSBNYXRoLmNlaWwoIHRoaXMueSApO1xuXHRcdFx0dGhpcy56ID0gTWF0aC5jZWlsKCB0aGlzLnogKTtcblx0XHRcdHRoaXMudyA9IE1hdGguY2VpbCggdGhpcy53ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHJvdW5kOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMueCA9IE1hdGgucm91bmQoIHRoaXMueCApO1xuXHRcdFx0dGhpcy55ID0gTWF0aC5yb3VuZCggdGhpcy55ICk7XG5cdFx0XHR0aGlzLnogPSBNYXRoLnJvdW5kKCB0aGlzLnogKTtcblx0XHRcdHRoaXMudyA9IE1hdGgucm91bmQoIHRoaXMudyApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRyb3VuZFRvWmVybzogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR0aGlzLnggPSAoIHRoaXMueCA8IDAgKSA/IE1hdGguY2VpbCggdGhpcy54ICkgOiBNYXRoLmZsb29yKCB0aGlzLnggKTtcblx0XHRcdHRoaXMueSA9ICggdGhpcy55IDwgMCApID8gTWF0aC5jZWlsKCB0aGlzLnkgKSA6IE1hdGguZmxvb3IoIHRoaXMueSApO1xuXHRcdFx0dGhpcy56ID0gKCB0aGlzLnogPCAwICkgPyBNYXRoLmNlaWwoIHRoaXMueiApIDogTWF0aC5mbG9vciggdGhpcy56ICk7XG5cdFx0XHR0aGlzLncgPSAoIHRoaXMudyA8IDAgKSA/IE1hdGguY2VpbCggdGhpcy53ICkgOiBNYXRoLmZsb29yKCB0aGlzLncgKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0bmVnYXRlOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMueCA9IC0gdGhpcy54O1xuXHRcdFx0dGhpcy55ID0gLSB0aGlzLnk7XG5cdFx0XHR0aGlzLnogPSAtIHRoaXMuejtcblx0XHRcdHRoaXMudyA9IC0gdGhpcy53O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRkb3Q6IGZ1bmN0aW9uICggdiApIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMueCAqIHYueCArIHRoaXMueSAqIHYueSArIHRoaXMueiAqIHYueiArIHRoaXMudyAqIHYudztcblxuXHRcdH0sXG5cblx0XHRsZW5ndGhTcTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy54ICogdGhpcy54ICsgdGhpcy55ICogdGhpcy55ICsgdGhpcy56ICogdGhpcy56ICsgdGhpcy53ICogdGhpcy53O1xuXG5cdFx0fSxcblxuXHRcdGxlbmd0aDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gTWF0aC5zcXJ0KCB0aGlzLnggKiB0aGlzLnggKyB0aGlzLnkgKiB0aGlzLnkgKyB0aGlzLnogKiB0aGlzLnogKyB0aGlzLncgKiB0aGlzLncgKTtcblxuXHRcdH0sXG5cblx0XHRsZW5ndGhNYW5oYXR0YW46IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIE1hdGguYWJzKCB0aGlzLnggKSArIE1hdGguYWJzKCB0aGlzLnkgKSArIE1hdGguYWJzKCB0aGlzLnogKSArIE1hdGguYWJzKCB0aGlzLncgKTtcblxuXHRcdH0sXG5cblx0XHRub3JtYWxpemU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuZGl2aWRlU2NhbGFyKCB0aGlzLmxlbmd0aCgpICk7XG5cblx0XHR9LFxuXG5cdFx0c2V0TGVuZ3RoOiBmdW5jdGlvbiAoIGxlbmd0aCApIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMubXVsdGlwbHlTY2FsYXIoIGxlbmd0aCAvIHRoaXMubGVuZ3RoKCkgKTtcblxuXHRcdH0sXG5cblx0XHRsZXJwOiBmdW5jdGlvbiAoIHYsIGFscGhhICkge1xuXG5cdFx0XHR0aGlzLnggKz0gKCB2LnggLSB0aGlzLnggKSAqIGFscGhhO1xuXHRcdFx0dGhpcy55ICs9ICggdi55IC0gdGhpcy55ICkgKiBhbHBoYTtcblx0XHRcdHRoaXMueiArPSAoIHYueiAtIHRoaXMueiApICogYWxwaGE7XG5cdFx0XHR0aGlzLncgKz0gKCB2LncgLSB0aGlzLncgKSAqIGFscGhhO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRsZXJwVmVjdG9yczogZnVuY3Rpb24gKCB2MSwgdjIsIGFscGhhICkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5zdWJWZWN0b3JzKCB2MiwgdjEgKS5tdWx0aXBseVNjYWxhciggYWxwaGEgKS5hZGQoIHYxICk7XG5cblx0XHR9LFxuXG5cdFx0ZXF1YWxzOiBmdW5jdGlvbiAoIHYgKSB7XG5cblx0XHRcdHJldHVybiAoICggdi54ID09PSB0aGlzLnggKSAmJiAoIHYueSA9PT0gdGhpcy55ICkgJiYgKCB2LnogPT09IHRoaXMueiApICYmICggdi53ID09PSB0aGlzLncgKSApO1xuXG5cdFx0fSxcblxuXHRcdGZyb21BcnJheTogZnVuY3Rpb24gKCBhcnJheSwgb2Zmc2V0ICkge1xuXG5cdFx0XHRpZiAoIG9mZnNldCA9PT0gdW5kZWZpbmVkICkgb2Zmc2V0ID0gMDtcblxuXHRcdFx0dGhpcy54ID0gYXJyYXlbIG9mZnNldCBdO1xuXHRcdFx0dGhpcy55ID0gYXJyYXlbIG9mZnNldCArIDEgXTtcblx0XHRcdHRoaXMueiA9IGFycmF5WyBvZmZzZXQgKyAyIF07XG5cdFx0XHR0aGlzLncgPSBhcnJheVsgb2Zmc2V0ICsgMyBdO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHR0b0FycmF5OiBmdW5jdGlvbiAoIGFycmF5LCBvZmZzZXQgKSB7XG5cblx0XHRcdGlmICggYXJyYXkgPT09IHVuZGVmaW5lZCApIGFycmF5ID0gW107XG5cdFx0XHRpZiAoIG9mZnNldCA9PT0gdW5kZWZpbmVkICkgb2Zmc2V0ID0gMDtcblxuXHRcdFx0YXJyYXlbIG9mZnNldCBdID0gdGhpcy54O1xuXHRcdFx0YXJyYXlbIG9mZnNldCArIDEgXSA9IHRoaXMueTtcblx0XHRcdGFycmF5WyBvZmZzZXQgKyAyIF0gPSB0aGlzLno7XG5cdFx0XHRhcnJheVsgb2Zmc2V0ICsgMyBdID0gdGhpcy53O1xuXG5cdFx0XHRyZXR1cm4gYXJyYXk7XG5cblx0XHR9LFxuXG5cdFx0ZnJvbUF0dHJpYnV0ZTogZnVuY3Rpb24gKCBhdHRyaWJ1dGUsIGluZGV4LCBvZmZzZXQgKSB7XG5cblx0XHRcdGlmICggb2Zmc2V0ICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuVmVjdG9yNDogb2Zmc2V0IGhhcyBiZWVuIHJlbW92ZWQgZnJvbSAuZnJvbUF0dHJpYnV0ZSgpLicgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLnggPSBhdHRyaWJ1dGUuZ2V0WCggaW5kZXggKTtcblx0XHRcdHRoaXMueSA9IGF0dHJpYnV0ZS5nZXRZKCBpbmRleCApO1xuXHRcdFx0dGhpcy56ID0gYXR0cmlidXRlLmdldFooIGluZGV4ICk7XG5cdFx0XHR0aGlzLncgPSBhdHRyaWJ1dGUuZ2V0VyggaW5kZXggKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9XG5cblx0fTtcblxuXHQvKipcblx0ICogQGF1dGhvciBzemltZWsgLyBodHRwczovL2dpdGh1Yi5jb20vc3ppbWVrL1xuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKiBAYXV0aG9yIE1hcml1cyBLaW50ZWwgLyBodHRwczovL2dpdGh1Yi5jb20va2ludGVsXG5cdCAqL1xuXG5cdC8qXG5cdCBJbiBvcHRpb25zLCB3ZSBjYW4gc3BlY2lmeTpcblx0ICogVGV4dHVyZSBwYXJhbWV0ZXJzIGZvciBhbiBhdXRvLWdlbmVyYXRlZCB0YXJnZXQgdGV4dHVyZVxuXHQgKiBkZXB0aEJ1ZmZlci9zdGVuY2lsQnVmZmVyOiBCb29sZWFucyB0byBpbmRpY2F0ZSBpZiB3ZSBzaG91bGQgZ2VuZXJhdGUgdGhlc2UgYnVmZmVyc1xuXHQqL1xuXHRmdW5jdGlvbiBXZWJHTFJlbmRlclRhcmdldCggd2lkdGgsIGhlaWdodCwgb3B0aW9ucyApIHtcblxuXHRcdHRoaXMudXVpZCA9IF9NYXRoLmdlbmVyYXRlVVVJRCgpO1xuXG5cdFx0dGhpcy53aWR0aCA9IHdpZHRoO1xuXHRcdHRoaXMuaGVpZ2h0ID0gaGVpZ2h0O1xuXG5cdFx0dGhpcy5zY2lzc29yID0gbmV3IFZlY3RvcjQoIDAsIDAsIHdpZHRoLCBoZWlnaHQgKTtcblx0XHR0aGlzLnNjaXNzb3JUZXN0ID0gZmFsc2U7XG5cblx0XHR0aGlzLnZpZXdwb3J0ID0gbmV3IFZlY3RvcjQoIDAsIDAsIHdpZHRoLCBoZWlnaHQgKTtcblxuXHRcdG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuXG5cdFx0aWYgKCBvcHRpb25zLm1pbkZpbHRlciA9PT0gdW5kZWZpbmVkICkgb3B0aW9ucy5taW5GaWx0ZXIgPSBMaW5lYXJGaWx0ZXI7XG5cblx0XHR0aGlzLnRleHR1cmUgPSBuZXcgVGV4dHVyZSggdW5kZWZpbmVkLCB1bmRlZmluZWQsIG9wdGlvbnMud3JhcFMsIG9wdGlvbnMud3JhcFQsIG9wdGlvbnMubWFnRmlsdGVyLCBvcHRpb25zLm1pbkZpbHRlciwgb3B0aW9ucy5mb3JtYXQsIG9wdGlvbnMudHlwZSwgb3B0aW9ucy5hbmlzb3Ryb3B5LCBvcHRpb25zLmVuY29kaW5nICk7XG5cblx0XHR0aGlzLmRlcHRoQnVmZmVyID0gb3B0aW9ucy5kZXB0aEJ1ZmZlciAhPT0gdW5kZWZpbmVkID8gb3B0aW9ucy5kZXB0aEJ1ZmZlciA6IHRydWU7XG5cdFx0dGhpcy5zdGVuY2lsQnVmZmVyID0gb3B0aW9ucy5zdGVuY2lsQnVmZmVyICE9PSB1bmRlZmluZWQgPyBvcHRpb25zLnN0ZW5jaWxCdWZmZXIgOiB0cnVlO1xuXHRcdHRoaXMuZGVwdGhUZXh0dXJlID0gb3B0aW9ucy5kZXB0aFRleHR1cmUgIT09IHVuZGVmaW5lZCA/IG9wdGlvbnMuZGVwdGhUZXh0dXJlIDogbnVsbDtcblxuXHR9XG5cblx0T2JqZWN0LmFzc2lnbiggV2ViR0xSZW5kZXJUYXJnZXQucHJvdG90eXBlLCBFdmVudERpc3BhdGNoZXIucHJvdG90eXBlLCB7XG5cblx0XHRpc1dlYkdMUmVuZGVyVGFyZ2V0OiB0cnVlLFxuXG5cdFx0c2V0U2l6ZTogZnVuY3Rpb24gKCB3aWR0aCwgaGVpZ2h0ICkge1xuXG5cdFx0XHRpZiAoIHRoaXMud2lkdGggIT09IHdpZHRoIHx8IHRoaXMuaGVpZ2h0ICE9PSBoZWlnaHQgKSB7XG5cblx0XHRcdFx0dGhpcy53aWR0aCA9IHdpZHRoO1xuXHRcdFx0XHR0aGlzLmhlaWdodCA9IGhlaWdodDtcblxuXHRcdFx0XHR0aGlzLmRpc3Bvc2UoKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLnZpZXdwb3J0LnNldCggMCwgMCwgd2lkdGgsIGhlaWdodCApO1xuXHRcdFx0dGhpcy5zY2lzc29yLnNldCggMCwgMCwgd2lkdGgsIGhlaWdodCApO1xuXG5cdFx0fSxcblxuXHRcdGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiBuZXcgdGhpcy5jb25zdHJ1Y3RvcigpLmNvcHkoIHRoaXMgKTtcblxuXHRcdH0sXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIHNvdXJjZSApIHtcblxuXHRcdFx0dGhpcy53aWR0aCA9IHNvdXJjZS53aWR0aDtcblx0XHRcdHRoaXMuaGVpZ2h0ID0gc291cmNlLmhlaWdodDtcblxuXHRcdFx0dGhpcy52aWV3cG9ydC5jb3B5KCBzb3VyY2Uudmlld3BvcnQgKTtcblxuXHRcdFx0dGhpcy50ZXh0dXJlID0gc291cmNlLnRleHR1cmUuY2xvbmUoKTtcblxuXHRcdFx0dGhpcy5kZXB0aEJ1ZmZlciA9IHNvdXJjZS5kZXB0aEJ1ZmZlcjtcblx0XHRcdHRoaXMuc3RlbmNpbEJ1ZmZlciA9IHNvdXJjZS5zdGVuY2lsQnVmZmVyO1xuXHRcdFx0dGhpcy5kZXB0aFRleHR1cmUgPSBzb3VyY2UuZGVwdGhUZXh0dXJlO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRkaXNwb3NlOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMuZGlzcGF0Y2hFdmVudCggeyB0eXBlOiAnZGlzcG9zZScgfSApO1xuXG5cdFx0fVxuXG5cdH0gKTtcblxuXHQvKipcblx0ICogQGF1dGhvciBhbHRlcmVkcSAvIGh0dHA6Ly9hbHRlcmVkcXVhbGlhLmNvbVxuXHQgKi9cblxuXHRmdW5jdGlvbiBXZWJHTFJlbmRlclRhcmdldEN1YmUoIHdpZHRoLCBoZWlnaHQsIG9wdGlvbnMgKSB7XG5cblx0XHRXZWJHTFJlbmRlclRhcmdldC5jYWxsKCB0aGlzLCB3aWR0aCwgaGVpZ2h0LCBvcHRpb25zICk7XG5cblx0XHR0aGlzLmFjdGl2ZUN1YmVGYWNlID0gMDsgLy8gUFggMCwgTlggMSwgUFkgMiwgTlkgMywgUFogNCwgTlogNVxuXHRcdHRoaXMuYWN0aXZlTWlwTWFwTGV2ZWwgPSAwO1xuXG5cdH1cblxuXHRXZWJHTFJlbmRlclRhcmdldEN1YmUucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggV2ViR0xSZW5kZXJUYXJnZXQucHJvdG90eXBlICk7XG5cdFdlYkdMUmVuZGVyVGFyZ2V0Q3ViZS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBXZWJHTFJlbmRlclRhcmdldEN1YmU7XG5cblx0V2ViR0xSZW5kZXJUYXJnZXRDdWJlLnByb3RvdHlwZS5pc1dlYkdMUmVuZGVyVGFyZ2V0Q3ViZSA9IHRydWU7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbWlrYWVsIGVtdGluZ2VyIC8gaHR0cDovL2dvbW8uc2UvXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqIEBhdXRob3IgV2VzdExhbmdsZXkgLyBodHRwOi8vZ2l0aHViLmNvbS9XZXN0TGFuZ2xleVxuXHQgKiBAYXV0aG9yIGJob3VzdG9uIC8gaHR0cDovL2NsYXJhLmlvXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFF1YXRlcm5pb24oIHgsIHksIHosIHcgKSB7XG5cblx0XHR0aGlzLl94ID0geCB8fCAwO1xuXHRcdHRoaXMuX3kgPSB5IHx8IDA7XG5cdFx0dGhpcy5feiA9IHogfHwgMDtcblx0XHR0aGlzLl93ID0gKCB3ICE9PSB1bmRlZmluZWQgKSA/IHcgOiAxO1xuXG5cdH1cblxuXHRRdWF0ZXJuaW9uLnByb3RvdHlwZSA9IHtcblxuXHRcdGNvbnN0cnVjdG9yOiBRdWF0ZXJuaW9uLFxuXG5cdFx0Z2V0IHggKCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5feDtcblxuXHRcdH0sXG5cblx0XHRzZXQgeCAoIHZhbHVlICkge1xuXG5cdFx0XHR0aGlzLl94ID0gdmFsdWU7XG5cdFx0XHR0aGlzLm9uQ2hhbmdlQ2FsbGJhY2soKTtcblxuXHRcdH0sXG5cblx0XHRnZXQgeSAoKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLl95O1xuXG5cdFx0fSxcblxuXHRcdHNldCB5ICggdmFsdWUgKSB7XG5cblx0XHRcdHRoaXMuX3kgPSB2YWx1ZTtcblx0XHRcdHRoaXMub25DaGFuZ2VDYWxsYmFjaygpO1xuXG5cdFx0fSxcblxuXHRcdGdldCB6ICgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuX3o7XG5cblx0XHR9LFxuXG5cdFx0c2V0IHogKCB2YWx1ZSApIHtcblxuXHRcdFx0dGhpcy5feiA9IHZhbHVlO1xuXHRcdFx0dGhpcy5vbkNoYW5nZUNhbGxiYWNrKCk7XG5cblx0XHR9LFxuXG5cdFx0Z2V0IHcgKCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5fdztcblxuXHRcdH0sXG5cblx0XHRzZXQgdyAoIHZhbHVlICkge1xuXG5cdFx0XHR0aGlzLl93ID0gdmFsdWU7XG5cdFx0XHR0aGlzLm9uQ2hhbmdlQ2FsbGJhY2soKTtcblxuXHRcdH0sXG5cblx0XHRzZXQ6IGZ1bmN0aW9uICggeCwgeSwgeiwgdyApIHtcblxuXHRcdFx0dGhpcy5feCA9IHg7XG5cdFx0XHR0aGlzLl95ID0geTtcblx0XHRcdHRoaXMuX3ogPSB6O1xuXHRcdFx0dGhpcy5fdyA9IHc7XG5cblx0XHRcdHRoaXMub25DaGFuZ2VDYWxsYmFjaygpO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjbG9uZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IHRoaXMuY29uc3RydWN0b3IoIHRoaXMuX3gsIHRoaXMuX3ksIHRoaXMuX3osIHRoaXMuX3cgKTtcblxuXHRcdH0sXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIHF1YXRlcm5pb24gKSB7XG5cblx0XHRcdHRoaXMuX3ggPSBxdWF0ZXJuaW9uLng7XG5cdFx0XHR0aGlzLl95ID0gcXVhdGVybmlvbi55O1xuXHRcdFx0dGhpcy5feiA9IHF1YXRlcm5pb24uejtcblx0XHRcdHRoaXMuX3cgPSBxdWF0ZXJuaW9uLnc7XG5cblx0XHRcdHRoaXMub25DaGFuZ2VDYWxsYmFjaygpO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRGcm9tRXVsZXI6IGZ1bmN0aW9uICggZXVsZXIsIHVwZGF0ZSApIHtcblxuXHRcdFx0aWYgKCAoZXVsZXIgJiYgZXVsZXIuaXNFdWxlcikgPT09IGZhbHNlICkge1xuXG5cdFx0XHRcdHRocm93IG5ldyBFcnJvciggJ1RIUkVFLlF1YXRlcm5pb246IC5zZXRGcm9tRXVsZXIoKSBub3cgZXhwZWN0cyBhbiBFdWxlciByb3RhdGlvbiByYXRoZXIgdGhhbiBhIFZlY3RvcjMgYW5kIG9yZGVyLicgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyBodHRwOi8vd3d3Lm1hdGh3b3Jrcy5jb20vbWF0bGFiY2VudHJhbC9maWxlZXhjaGFuZ2UvXG5cdFx0XHQvLyBcdDIwNjk2LWZ1bmN0aW9uLXRvLWNvbnZlcnQtYmV0d2Vlbi1kY20tZXVsZXItYW5nbGVzLXF1YXRlcm5pb25zLWFuZC1ldWxlci12ZWN0b3JzL1xuXHRcdFx0Ly9cdGNvbnRlbnQvU3BpbkNhbGMubVxuXG5cdFx0XHR2YXIgYzEgPSBNYXRoLmNvcyggZXVsZXIuX3ggLyAyICk7XG5cdFx0XHR2YXIgYzIgPSBNYXRoLmNvcyggZXVsZXIuX3kgLyAyICk7XG5cdFx0XHR2YXIgYzMgPSBNYXRoLmNvcyggZXVsZXIuX3ogLyAyICk7XG5cdFx0XHR2YXIgczEgPSBNYXRoLnNpbiggZXVsZXIuX3ggLyAyICk7XG5cdFx0XHR2YXIgczIgPSBNYXRoLnNpbiggZXVsZXIuX3kgLyAyICk7XG5cdFx0XHR2YXIgczMgPSBNYXRoLnNpbiggZXVsZXIuX3ogLyAyICk7XG5cblx0XHRcdHZhciBvcmRlciA9IGV1bGVyLm9yZGVyO1xuXG5cdFx0XHRpZiAoIG9yZGVyID09PSAnWFlaJyApIHtcblxuXHRcdFx0XHR0aGlzLl94ID0gczEgKiBjMiAqIGMzICsgYzEgKiBzMiAqIHMzO1xuXHRcdFx0XHR0aGlzLl95ID0gYzEgKiBzMiAqIGMzIC0gczEgKiBjMiAqIHMzO1xuXHRcdFx0XHR0aGlzLl96ID0gYzEgKiBjMiAqIHMzICsgczEgKiBzMiAqIGMzO1xuXHRcdFx0XHR0aGlzLl93ID0gYzEgKiBjMiAqIGMzIC0gczEgKiBzMiAqIHMzO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCBvcmRlciA9PT0gJ1lYWicgKSB7XG5cblx0XHRcdFx0dGhpcy5feCA9IHMxICogYzIgKiBjMyArIGMxICogczIgKiBzMztcblx0XHRcdFx0dGhpcy5feSA9IGMxICogczIgKiBjMyAtIHMxICogYzIgKiBzMztcblx0XHRcdFx0dGhpcy5feiA9IGMxICogYzIgKiBzMyAtIHMxICogczIgKiBjMztcblx0XHRcdFx0dGhpcy5fdyA9IGMxICogYzIgKiBjMyArIHMxICogczIgKiBzMztcblxuXHRcdFx0fSBlbHNlIGlmICggb3JkZXIgPT09ICdaWFknICkge1xuXG5cdFx0XHRcdHRoaXMuX3ggPSBzMSAqIGMyICogYzMgLSBjMSAqIHMyICogczM7XG5cdFx0XHRcdHRoaXMuX3kgPSBjMSAqIHMyICogYzMgKyBzMSAqIGMyICogczM7XG5cdFx0XHRcdHRoaXMuX3ogPSBjMSAqIGMyICogczMgKyBzMSAqIHMyICogYzM7XG5cdFx0XHRcdHRoaXMuX3cgPSBjMSAqIGMyICogYzMgLSBzMSAqIHMyICogczM7XG5cblx0XHRcdH0gZWxzZSBpZiAoIG9yZGVyID09PSAnWllYJyApIHtcblxuXHRcdFx0XHR0aGlzLl94ID0gczEgKiBjMiAqIGMzIC0gYzEgKiBzMiAqIHMzO1xuXHRcdFx0XHR0aGlzLl95ID0gYzEgKiBzMiAqIGMzICsgczEgKiBjMiAqIHMzO1xuXHRcdFx0XHR0aGlzLl96ID0gYzEgKiBjMiAqIHMzIC0gczEgKiBzMiAqIGMzO1xuXHRcdFx0XHR0aGlzLl93ID0gYzEgKiBjMiAqIGMzICsgczEgKiBzMiAqIHMzO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCBvcmRlciA9PT0gJ1laWCcgKSB7XG5cblx0XHRcdFx0dGhpcy5feCA9IHMxICogYzIgKiBjMyArIGMxICogczIgKiBzMztcblx0XHRcdFx0dGhpcy5feSA9IGMxICogczIgKiBjMyArIHMxICogYzIgKiBzMztcblx0XHRcdFx0dGhpcy5feiA9IGMxICogYzIgKiBzMyAtIHMxICogczIgKiBjMztcblx0XHRcdFx0dGhpcy5fdyA9IGMxICogYzIgKiBjMyAtIHMxICogczIgKiBzMztcblxuXHRcdFx0fSBlbHNlIGlmICggb3JkZXIgPT09ICdYWlknICkge1xuXG5cdFx0XHRcdHRoaXMuX3ggPSBzMSAqIGMyICogYzMgLSBjMSAqIHMyICogczM7XG5cdFx0XHRcdHRoaXMuX3kgPSBjMSAqIHMyICogYzMgLSBzMSAqIGMyICogczM7XG5cdFx0XHRcdHRoaXMuX3ogPSBjMSAqIGMyICogczMgKyBzMSAqIHMyICogYzM7XG5cdFx0XHRcdHRoaXMuX3cgPSBjMSAqIGMyICogYzMgKyBzMSAqIHMyICogczM7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCB1cGRhdGUgIT09IGZhbHNlICkgdGhpcy5vbkNoYW5nZUNhbGxiYWNrKCk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldEZyb21BeGlzQW5nbGU6IGZ1bmN0aW9uICggYXhpcywgYW5nbGUgKSB7XG5cblx0XHRcdC8vIGh0dHA6Ly93d3cuZXVjbGlkZWFuc3BhY2UuY29tL21hdGhzL2dlb21ldHJ5L3JvdGF0aW9ucy9jb252ZXJzaW9ucy9hbmdsZVRvUXVhdGVybmlvbi9pbmRleC5odG1cblxuXHRcdFx0Ly8gYXNzdW1lcyBheGlzIGlzIG5vcm1hbGl6ZWRcblxuXHRcdFx0dmFyIGhhbGZBbmdsZSA9IGFuZ2xlIC8gMiwgcyA9IE1hdGguc2luKCBoYWxmQW5nbGUgKTtcblxuXHRcdFx0dGhpcy5feCA9IGF4aXMueCAqIHM7XG5cdFx0XHR0aGlzLl95ID0gYXhpcy55ICogcztcblx0XHRcdHRoaXMuX3ogPSBheGlzLnogKiBzO1xuXHRcdFx0dGhpcy5fdyA9IE1hdGguY29zKCBoYWxmQW5nbGUgKTtcblxuXHRcdFx0dGhpcy5vbkNoYW5nZUNhbGxiYWNrKCk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldEZyb21Sb3RhdGlvbk1hdHJpeDogZnVuY3Rpb24gKCBtICkge1xuXG5cdFx0XHQvLyBodHRwOi8vd3d3LmV1Y2xpZGVhbnNwYWNlLmNvbS9tYXRocy9nZW9tZXRyeS9yb3RhdGlvbnMvY29udmVyc2lvbnMvbWF0cml4VG9RdWF0ZXJuaW9uL2luZGV4Lmh0bVxuXG5cdFx0XHQvLyBhc3N1bWVzIHRoZSB1cHBlciAzeDMgb2YgbSBpcyBhIHB1cmUgcm90YXRpb24gbWF0cml4IChpLmUsIHVuc2NhbGVkKVxuXG5cdFx0XHR2YXIgdGUgPSBtLmVsZW1lbnRzLFxuXG5cdFx0XHRcdG0xMSA9IHRlWyAwIF0sIG0xMiA9IHRlWyA0IF0sIG0xMyA9IHRlWyA4IF0sXG5cdFx0XHRcdG0yMSA9IHRlWyAxIF0sIG0yMiA9IHRlWyA1IF0sIG0yMyA9IHRlWyA5IF0sXG5cdFx0XHRcdG0zMSA9IHRlWyAyIF0sIG0zMiA9IHRlWyA2IF0sIG0zMyA9IHRlWyAxMCBdLFxuXG5cdFx0XHRcdHRyYWNlID0gbTExICsgbTIyICsgbTMzLFxuXHRcdFx0XHRzO1xuXG5cdFx0XHRpZiAoIHRyYWNlID4gMCApIHtcblxuXHRcdFx0XHRzID0gMC41IC8gTWF0aC5zcXJ0KCB0cmFjZSArIDEuMCApO1xuXG5cdFx0XHRcdHRoaXMuX3cgPSAwLjI1IC8gcztcblx0XHRcdFx0dGhpcy5feCA9ICggbTMyIC0gbTIzICkgKiBzO1xuXHRcdFx0XHR0aGlzLl95ID0gKCBtMTMgLSBtMzEgKSAqIHM7XG5cdFx0XHRcdHRoaXMuX3ogPSAoIG0yMSAtIG0xMiApICogcztcblxuXHRcdFx0fSBlbHNlIGlmICggbTExID4gbTIyICYmIG0xMSA+IG0zMyApIHtcblxuXHRcdFx0XHRzID0gMi4wICogTWF0aC5zcXJ0KCAxLjAgKyBtMTEgLSBtMjIgLSBtMzMgKTtcblxuXHRcdFx0XHR0aGlzLl93ID0gKCBtMzIgLSBtMjMgKSAvIHM7XG5cdFx0XHRcdHRoaXMuX3ggPSAwLjI1ICogcztcblx0XHRcdFx0dGhpcy5feSA9ICggbTEyICsgbTIxICkgLyBzO1xuXHRcdFx0XHR0aGlzLl96ID0gKCBtMTMgKyBtMzEgKSAvIHM7XG5cblx0XHRcdH0gZWxzZSBpZiAoIG0yMiA+IG0zMyApIHtcblxuXHRcdFx0XHRzID0gMi4wICogTWF0aC5zcXJ0KCAxLjAgKyBtMjIgLSBtMTEgLSBtMzMgKTtcblxuXHRcdFx0XHR0aGlzLl93ID0gKCBtMTMgLSBtMzEgKSAvIHM7XG5cdFx0XHRcdHRoaXMuX3ggPSAoIG0xMiArIG0yMSApIC8gcztcblx0XHRcdFx0dGhpcy5feSA9IDAuMjUgKiBzO1xuXHRcdFx0XHR0aGlzLl96ID0gKCBtMjMgKyBtMzIgKSAvIHM7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0cyA9IDIuMCAqIE1hdGguc3FydCggMS4wICsgbTMzIC0gbTExIC0gbTIyICk7XG5cblx0XHRcdFx0dGhpcy5fdyA9ICggbTIxIC0gbTEyICkgLyBzO1xuXHRcdFx0XHR0aGlzLl94ID0gKCBtMTMgKyBtMzEgKSAvIHM7XG5cdFx0XHRcdHRoaXMuX3kgPSAoIG0yMyArIG0zMiApIC8gcztcblx0XHRcdFx0dGhpcy5feiA9IDAuMjUgKiBzO1xuXG5cdFx0XHR9XG5cblx0XHRcdHRoaXMub25DaGFuZ2VDYWxsYmFjaygpO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRGcm9tVW5pdFZlY3RvcnM6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Ly8gaHR0cDovL2xvbGVuZ2luZS5uZXQvYmxvZy8yMDE0LzAyLzI0L3F1YXRlcm5pb24tZnJvbS10d28tdmVjdG9ycy1maW5hbFxuXG5cdFx0XHQvLyBhc3N1bWVzIGRpcmVjdGlvbiB2ZWN0b3JzIHZGcm9tIGFuZCB2VG8gYXJlIG5vcm1hbGl6ZWRcblxuXHRcdFx0dmFyIHYxLCByO1xuXG5cdFx0XHR2YXIgRVBTID0gMC4wMDAwMDE7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBzZXRGcm9tVW5pdFZlY3RvcnMoIHZGcm9tLCB2VG8gKSB7XG5cblx0XHRcdFx0aWYgKCB2MSA9PT0gdW5kZWZpbmVkICkgdjEgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRcdHIgPSB2RnJvbS5kb3QoIHZUbyApICsgMTtcblxuXHRcdFx0XHRpZiAoIHIgPCBFUFMgKSB7XG5cblx0XHRcdFx0XHRyID0gMDtcblxuXHRcdFx0XHRcdGlmICggTWF0aC5hYnMoIHZGcm9tLnggKSA+IE1hdGguYWJzKCB2RnJvbS56ICkgKSB7XG5cblx0XHRcdFx0XHRcdHYxLnNldCggLSB2RnJvbS55LCB2RnJvbS54LCAwICk7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHR2MS5zZXQoIDAsIC0gdkZyb20ueiwgdkZyb20ueSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHR2MS5jcm9zc1ZlY3RvcnMoIHZGcm9tLCB2VG8gKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0dGhpcy5feCA9IHYxLng7XG5cdFx0XHRcdHRoaXMuX3kgPSB2MS55O1xuXHRcdFx0XHR0aGlzLl96ID0gdjEuejtcblx0XHRcdFx0dGhpcy5fdyA9IHI7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXMubm9ybWFsaXplKCk7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRpbnZlcnNlOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLmNvbmp1Z2F0ZSgpLm5vcm1hbGl6ZSgpO1xuXG5cdFx0fSxcblxuXHRcdGNvbmp1Z2F0ZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR0aGlzLl94ICo9IC0gMTtcblx0XHRcdHRoaXMuX3kgKj0gLSAxO1xuXHRcdFx0dGhpcy5feiAqPSAtIDE7XG5cblx0XHRcdHRoaXMub25DaGFuZ2VDYWxsYmFjaygpO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRkb3Q6IGZ1bmN0aW9uICggdiApIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuX3ggKiB2Ll94ICsgdGhpcy5feSAqIHYuX3kgKyB0aGlzLl96ICogdi5feiArIHRoaXMuX3cgKiB2Ll93O1xuXG5cdFx0fSxcblxuXHRcdGxlbmd0aFNxOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLl94ICogdGhpcy5feCArIHRoaXMuX3kgKiB0aGlzLl95ICsgdGhpcy5feiAqIHRoaXMuX3ogKyB0aGlzLl93ICogdGhpcy5fdztcblxuXHRcdH0sXG5cblx0XHRsZW5ndGg6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIE1hdGguc3FydCggdGhpcy5feCAqIHRoaXMuX3ggKyB0aGlzLl95ICogdGhpcy5feSArIHRoaXMuX3ogKiB0aGlzLl96ICsgdGhpcy5fdyAqIHRoaXMuX3cgKTtcblxuXHRcdH0sXG5cblx0XHRub3JtYWxpemU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIGwgPSB0aGlzLmxlbmd0aCgpO1xuXG5cdFx0XHRpZiAoIGwgPT09IDAgKSB7XG5cblx0XHRcdFx0dGhpcy5feCA9IDA7XG5cdFx0XHRcdHRoaXMuX3kgPSAwO1xuXHRcdFx0XHR0aGlzLl96ID0gMDtcblx0XHRcdFx0dGhpcy5fdyA9IDE7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0bCA9IDEgLyBsO1xuXG5cdFx0XHRcdHRoaXMuX3ggPSB0aGlzLl94ICogbDtcblx0XHRcdFx0dGhpcy5feSA9IHRoaXMuX3kgKiBsO1xuXHRcdFx0XHR0aGlzLl96ID0gdGhpcy5feiAqIGw7XG5cdFx0XHRcdHRoaXMuX3cgPSB0aGlzLl93ICogbDtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLm9uQ2hhbmdlQ2FsbGJhY2soKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0bXVsdGlwbHk6IGZ1bmN0aW9uICggcSwgcCApIHtcblxuXHRcdFx0aWYgKCBwICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuUXVhdGVybmlvbjogLm11bHRpcGx5KCkgbm93IG9ubHkgYWNjZXB0cyBvbmUgYXJndW1lbnQuIFVzZSAubXVsdGlwbHlRdWF0ZXJuaW9ucyggYSwgYiApIGluc3RlYWQuJyApO1xuXHRcdFx0XHRyZXR1cm4gdGhpcy5tdWx0aXBseVF1YXRlcm5pb25zKCBxLCBwICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXMubXVsdGlwbHlRdWF0ZXJuaW9ucyggdGhpcywgcSApO1xuXG5cdFx0fSxcblxuXHRcdHByZW11bHRpcGx5OiBmdW5jdGlvbiAoIHEgKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLm11bHRpcGx5UXVhdGVybmlvbnMoIHEsIHRoaXMgKTtcblxuXHRcdH0sXG5cblx0XHRtdWx0aXBseVF1YXRlcm5pb25zOiBmdW5jdGlvbiAoIGEsIGIgKSB7XG5cblx0XHRcdC8vIGZyb20gaHR0cDovL3d3dy5ldWNsaWRlYW5zcGFjZS5jb20vbWF0aHMvYWxnZWJyYS9yZWFsTm9ybWVkQWxnZWJyYS9xdWF0ZXJuaW9ucy9jb2RlL2luZGV4Lmh0bVxuXG5cdFx0XHR2YXIgcWF4ID0gYS5feCwgcWF5ID0gYS5feSwgcWF6ID0gYS5feiwgcWF3ID0gYS5fdztcblx0XHRcdHZhciBxYnggPSBiLl94LCBxYnkgPSBiLl95LCBxYnogPSBiLl96LCBxYncgPSBiLl93O1xuXG5cdFx0XHR0aGlzLl94ID0gcWF4ICogcWJ3ICsgcWF3ICogcWJ4ICsgcWF5ICogcWJ6IC0gcWF6ICogcWJ5O1xuXHRcdFx0dGhpcy5feSA9IHFheSAqIHFidyArIHFhdyAqIHFieSArIHFheiAqIHFieCAtIHFheCAqIHFiejtcblx0XHRcdHRoaXMuX3ogPSBxYXogKiBxYncgKyBxYXcgKiBxYnogKyBxYXggKiBxYnkgLSBxYXkgKiBxYng7XG5cdFx0XHR0aGlzLl93ID0gcWF3ICogcWJ3IC0gcWF4ICogcWJ4IC0gcWF5ICogcWJ5IC0gcWF6ICogcWJ6O1xuXG5cdFx0XHR0aGlzLm9uQ2hhbmdlQ2FsbGJhY2soKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2xlcnA6IGZ1bmN0aW9uICggcWIsIHQgKSB7XG5cblx0XHRcdGlmICggdCA9PT0gMCApIHJldHVybiB0aGlzO1xuXHRcdFx0aWYgKCB0ID09PSAxICkgcmV0dXJuIHRoaXMuY29weSggcWIgKTtcblxuXHRcdFx0dmFyIHggPSB0aGlzLl94LCB5ID0gdGhpcy5feSwgeiA9IHRoaXMuX3osIHcgPSB0aGlzLl93O1xuXG5cdFx0XHQvLyBodHRwOi8vd3d3LmV1Y2xpZGVhbnNwYWNlLmNvbS9tYXRocy9hbGdlYnJhL3JlYWxOb3JtZWRBbGdlYnJhL3F1YXRlcm5pb25zL3NsZXJwL1xuXG5cdFx0XHR2YXIgY29zSGFsZlRoZXRhID0gdyAqIHFiLl93ICsgeCAqIHFiLl94ICsgeSAqIHFiLl95ICsgeiAqIHFiLl96O1xuXG5cdFx0XHRpZiAoIGNvc0hhbGZUaGV0YSA8IDAgKSB7XG5cblx0XHRcdFx0dGhpcy5fdyA9IC0gcWIuX3c7XG5cdFx0XHRcdHRoaXMuX3ggPSAtIHFiLl94O1xuXHRcdFx0XHR0aGlzLl95ID0gLSBxYi5feTtcblx0XHRcdFx0dGhpcy5feiA9IC0gcWIuX3o7XG5cblx0XHRcdFx0Y29zSGFsZlRoZXRhID0gLSBjb3NIYWxmVGhldGE7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0dGhpcy5jb3B5KCBxYiApO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggY29zSGFsZlRoZXRhID49IDEuMCApIHtcblxuXHRcdFx0XHR0aGlzLl93ID0gdztcblx0XHRcdFx0dGhpcy5feCA9IHg7XG5cdFx0XHRcdHRoaXMuX3kgPSB5O1xuXHRcdFx0XHR0aGlzLl96ID0gejtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgc2luSGFsZlRoZXRhID0gTWF0aC5zcXJ0KCAxLjAgLSBjb3NIYWxmVGhldGEgKiBjb3NIYWxmVGhldGEgKTtcblxuXHRcdFx0aWYgKCBNYXRoLmFicyggc2luSGFsZlRoZXRhICkgPCAwLjAwMSApIHtcblxuXHRcdFx0XHR0aGlzLl93ID0gMC41ICogKCB3ICsgdGhpcy5fdyApO1xuXHRcdFx0XHR0aGlzLl94ID0gMC41ICogKCB4ICsgdGhpcy5feCApO1xuXHRcdFx0XHR0aGlzLl95ID0gMC41ICogKCB5ICsgdGhpcy5feSApO1xuXHRcdFx0XHR0aGlzLl96ID0gMC41ICogKCB6ICsgdGhpcy5feiApO1xuXG5cdFx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBoYWxmVGhldGEgPSBNYXRoLmF0YW4yKCBzaW5IYWxmVGhldGEsIGNvc0hhbGZUaGV0YSApO1xuXHRcdFx0dmFyIHJhdGlvQSA9IE1hdGguc2luKCAoIDEgLSB0ICkgKiBoYWxmVGhldGEgKSAvIHNpbkhhbGZUaGV0YSxcblx0XHRcdHJhdGlvQiA9IE1hdGguc2luKCB0ICogaGFsZlRoZXRhICkgLyBzaW5IYWxmVGhldGE7XG5cblx0XHRcdHRoaXMuX3cgPSAoIHcgKiByYXRpb0EgKyB0aGlzLl93ICogcmF0aW9CICk7XG5cdFx0XHR0aGlzLl94ID0gKCB4ICogcmF0aW9BICsgdGhpcy5feCAqIHJhdGlvQiApO1xuXHRcdFx0dGhpcy5feSA9ICggeSAqIHJhdGlvQSArIHRoaXMuX3kgKiByYXRpb0IgKTtcblx0XHRcdHRoaXMuX3ogPSAoIHogKiByYXRpb0EgKyB0aGlzLl96ICogcmF0aW9CICk7XG5cblx0XHRcdHRoaXMub25DaGFuZ2VDYWxsYmFjaygpO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRlcXVhbHM6IGZ1bmN0aW9uICggcXVhdGVybmlvbiApIHtcblxuXHRcdFx0cmV0dXJuICggcXVhdGVybmlvbi5feCA9PT0gdGhpcy5feCApICYmICggcXVhdGVybmlvbi5feSA9PT0gdGhpcy5feSApICYmICggcXVhdGVybmlvbi5feiA9PT0gdGhpcy5feiApICYmICggcXVhdGVybmlvbi5fdyA9PT0gdGhpcy5fdyApO1xuXG5cdFx0fSxcblxuXHRcdGZyb21BcnJheTogZnVuY3Rpb24gKCBhcnJheSwgb2Zmc2V0ICkge1xuXG5cdFx0XHRpZiAoIG9mZnNldCA9PT0gdW5kZWZpbmVkICkgb2Zmc2V0ID0gMDtcblxuXHRcdFx0dGhpcy5feCA9IGFycmF5WyBvZmZzZXQgXTtcblx0XHRcdHRoaXMuX3kgPSBhcnJheVsgb2Zmc2V0ICsgMSBdO1xuXHRcdFx0dGhpcy5feiA9IGFycmF5WyBvZmZzZXQgKyAyIF07XG5cdFx0XHR0aGlzLl93ID0gYXJyYXlbIG9mZnNldCArIDMgXTtcblxuXHRcdFx0dGhpcy5vbkNoYW5nZUNhbGxiYWNrKCk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHRvQXJyYXk6IGZ1bmN0aW9uICggYXJyYXksIG9mZnNldCApIHtcblxuXHRcdFx0aWYgKCBhcnJheSA9PT0gdW5kZWZpbmVkICkgYXJyYXkgPSBbXTtcblx0XHRcdGlmICggb2Zmc2V0ID09PSB1bmRlZmluZWQgKSBvZmZzZXQgPSAwO1xuXG5cdFx0XHRhcnJheVsgb2Zmc2V0IF0gPSB0aGlzLl94O1xuXHRcdFx0YXJyYXlbIG9mZnNldCArIDEgXSA9IHRoaXMuX3k7XG5cdFx0XHRhcnJheVsgb2Zmc2V0ICsgMiBdID0gdGhpcy5fejtcblx0XHRcdGFycmF5WyBvZmZzZXQgKyAzIF0gPSB0aGlzLl93O1xuXG5cdFx0XHRyZXR1cm4gYXJyYXk7XG5cblx0XHR9LFxuXG5cdFx0b25DaGFuZ2U6IGZ1bmN0aW9uICggY2FsbGJhY2sgKSB7XG5cblx0XHRcdHRoaXMub25DaGFuZ2VDYWxsYmFjayA9IGNhbGxiYWNrO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRvbkNoYW5nZUNhbGxiYWNrOiBmdW5jdGlvbiAoKSB7fVxuXG5cdH07XG5cblx0T2JqZWN0LmFzc2lnbiggUXVhdGVybmlvbiwge1xuXG5cdFx0c2xlcnA6IGZ1bmN0aW9uKCBxYSwgcWIsIHFtLCB0ICkge1xuXG5cdFx0XHRyZXR1cm4gcW0uY29weSggcWEgKS5zbGVycCggcWIsIHQgKTtcblxuXHRcdH0sXG5cblx0XHRzbGVycEZsYXQ6IGZ1bmN0aW9uKFxuXHRcdFx0XHRkc3QsIGRzdE9mZnNldCwgc3JjMCwgc3JjT2Zmc2V0MCwgc3JjMSwgc3JjT2Zmc2V0MSwgdCApIHtcblxuXHRcdFx0Ly8gZnV6ei1mcmVlLCBhcnJheS1iYXNlZCBRdWF0ZXJuaW9uIFNMRVJQIG9wZXJhdGlvblxuXG5cdFx0XHR2YXIgeDAgPSBzcmMwWyBzcmNPZmZzZXQwICsgMCBdLFxuXHRcdFx0XHR5MCA9IHNyYzBbIHNyY09mZnNldDAgKyAxIF0sXG5cdFx0XHRcdHowID0gc3JjMFsgc3JjT2Zmc2V0MCArIDIgXSxcblx0XHRcdFx0dzAgPSBzcmMwWyBzcmNPZmZzZXQwICsgMyBdLFxuXG5cdFx0XHRcdHgxID0gc3JjMVsgc3JjT2Zmc2V0MSArIDAgXSxcblx0XHRcdFx0eTEgPSBzcmMxWyBzcmNPZmZzZXQxICsgMSBdLFxuXHRcdFx0XHR6MSA9IHNyYzFbIHNyY09mZnNldDEgKyAyIF0sXG5cdFx0XHRcdHcxID0gc3JjMVsgc3JjT2Zmc2V0MSArIDMgXTtcblxuXHRcdFx0aWYgKCB3MCAhPT0gdzEgfHwgeDAgIT09IHgxIHx8IHkwICE9PSB5MSB8fCB6MCAhPT0gejEgKSB7XG5cblx0XHRcdFx0dmFyIHMgPSAxIC0gdCxcblxuXHRcdFx0XHRcdGNvcyA9IHgwICogeDEgKyB5MCAqIHkxICsgejAgKiB6MSArIHcwICogdzEsXG5cblx0XHRcdFx0XHRkaXIgPSAoIGNvcyA+PSAwID8gMSA6IC0gMSApLFxuXHRcdFx0XHRcdHNxclNpbiA9IDEgLSBjb3MgKiBjb3M7XG5cblx0XHRcdFx0Ly8gU2tpcCB0aGUgU2xlcnAgZm9yIHRpbnkgc3RlcHMgdG8gYXZvaWQgbnVtZXJpYyBwcm9ibGVtczpcblx0XHRcdFx0aWYgKCBzcXJTaW4gPiBOdW1iZXIuRVBTSUxPTiApIHtcblxuXHRcdFx0XHRcdHZhciBzaW4gPSBNYXRoLnNxcnQoIHNxclNpbiApLFxuXHRcdFx0XHRcdFx0bGVuID0gTWF0aC5hdGFuMiggc2luLCBjb3MgKiBkaXIgKTtcblxuXHRcdFx0XHRcdHMgPSBNYXRoLnNpbiggcyAqIGxlbiApIC8gc2luO1xuXHRcdFx0XHRcdHQgPSBNYXRoLnNpbiggdCAqIGxlbiApIC8gc2luO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR2YXIgdERpciA9IHQgKiBkaXI7XG5cblx0XHRcdFx0eDAgPSB4MCAqIHMgKyB4MSAqIHREaXI7XG5cdFx0XHRcdHkwID0geTAgKiBzICsgeTEgKiB0RGlyO1xuXHRcdFx0XHR6MCA9IHowICogcyArIHoxICogdERpcjtcblx0XHRcdFx0dzAgPSB3MCAqIHMgKyB3MSAqIHREaXI7XG5cblx0XHRcdFx0Ly8gTm9ybWFsaXplIGluIGNhc2Ugd2UganVzdCBkaWQgYSBsZXJwOlxuXHRcdFx0XHRpZiAoIHMgPT09IDEgLSB0ICkge1xuXG5cdFx0XHRcdFx0dmFyIGYgPSAxIC8gTWF0aC5zcXJ0KCB4MCAqIHgwICsgeTAgKiB5MCArIHowICogejAgKyB3MCAqIHcwICk7XG5cblx0XHRcdFx0XHR4MCAqPSBmO1xuXHRcdFx0XHRcdHkwICo9IGY7XG5cdFx0XHRcdFx0ejAgKj0gZjtcblx0XHRcdFx0XHR3MCAqPSBmO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRkc3RbIGRzdE9mZnNldCBdID0geDA7XG5cdFx0XHRkc3RbIGRzdE9mZnNldCArIDEgXSA9IHkwO1xuXHRcdFx0ZHN0WyBkc3RPZmZzZXQgKyAyIF0gPSB6MDtcblx0XHRcdGRzdFsgZHN0T2Zmc2V0ICsgMyBdID0gdzA7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yICpraWxlIC8gaHR0cDovL2tpbGUuc3RyYXZhZ2FuemEub3JnL1xuXHQgKiBAYXV0aG9yIHBoaWxvZ2IgLyBodHRwOi8vYmxvZy50aGVqaXQub3JnL1xuXHQgKiBAYXV0aG9yIG1pa2FlbCBlbXRpbmdlciAvIGh0dHA6Ly9nb21vLnNlL1xuXHQgKiBAYXV0aG9yIGVncmFldGhlciAvIGh0dHA6Ly9lZ3JhZXRoZXIuY29tL1xuXHQgKiBAYXV0aG9yIFdlc3RMYW5nbGV5IC8gaHR0cDovL2dpdGh1Yi5jb20vV2VzdExhbmdsZXlcblx0ICovXG5cblx0ZnVuY3Rpb24gVmVjdG9yMyggeCwgeSwgeiApIHtcblxuXHRcdHRoaXMueCA9IHggfHwgMDtcblx0XHR0aGlzLnkgPSB5IHx8IDA7XG5cdFx0dGhpcy56ID0geiB8fCAwO1xuXG5cdH1cblxuXHRWZWN0b3IzLnByb3RvdHlwZSA9IHtcblxuXHRcdGNvbnN0cnVjdG9yOiBWZWN0b3IzLFxuXG5cdFx0aXNWZWN0b3IzOiB0cnVlLFxuXG5cdFx0c2V0OiBmdW5jdGlvbiAoIHgsIHksIHogKSB7XG5cblx0XHRcdHRoaXMueCA9IHg7XG5cdFx0XHR0aGlzLnkgPSB5O1xuXHRcdFx0dGhpcy56ID0gejtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0U2NhbGFyOiBmdW5jdGlvbiAoIHNjYWxhciApIHtcblxuXHRcdFx0dGhpcy54ID0gc2NhbGFyO1xuXHRcdFx0dGhpcy55ID0gc2NhbGFyO1xuXHRcdFx0dGhpcy56ID0gc2NhbGFyO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRYOiBmdW5jdGlvbiAoIHggKSB7XG5cblx0XHRcdHRoaXMueCA9IHg7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldFk6IGZ1bmN0aW9uICggeSApIHtcblxuXHRcdFx0dGhpcy55ID0geTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0WjogZnVuY3Rpb24gKCB6ICkge1xuXG5cdFx0XHR0aGlzLnogPSB6O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRDb21wb25lbnQ6IGZ1bmN0aW9uICggaW5kZXgsIHZhbHVlICkge1xuXG5cdFx0XHRzd2l0Y2ggKCBpbmRleCApIHtcblxuXHRcdFx0XHRjYXNlIDA6IHRoaXMueCA9IHZhbHVlOyBicmVhaztcblx0XHRcdFx0Y2FzZSAxOiB0aGlzLnkgPSB2YWx1ZTsgYnJlYWs7XG5cdFx0XHRcdGNhc2UgMjogdGhpcy56ID0gdmFsdWU7IGJyZWFrO1xuXHRcdFx0XHRkZWZhdWx0OiB0aHJvdyBuZXcgRXJyb3IoICdpbmRleCBpcyBvdXQgb2YgcmFuZ2U6ICcgKyBpbmRleCApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGdldENvbXBvbmVudDogZnVuY3Rpb24gKCBpbmRleCApIHtcblxuXHRcdFx0c3dpdGNoICggaW5kZXggKSB7XG5cblx0XHRcdFx0Y2FzZSAwOiByZXR1cm4gdGhpcy54O1xuXHRcdFx0XHRjYXNlIDE6IHJldHVybiB0aGlzLnk7XG5cdFx0XHRcdGNhc2UgMjogcmV0dXJuIHRoaXMuejtcblx0XHRcdFx0ZGVmYXVsdDogdGhyb3cgbmV3IEVycm9yKCAnaW5kZXggaXMgb3V0IG9mIHJhbmdlOiAnICsgaW5kZXggKTtcblxuXHRcdFx0fVxuXG5cdFx0fSxcblxuXHRcdGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiBuZXcgdGhpcy5jb25zdHJ1Y3RvciggdGhpcy54LCB0aGlzLnksIHRoaXMueiApO1xuXG5cdFx0fSxcblxuXHRcdGNvcHk6IGZ1bmN0aW9uICggdiApIHtcblxuXHRcdFx0dGhpcy54ID0gdi54O1xuXHRcdFx0dGhpcy55ID0gdi55O1xuXHRcdFx0dGhpcy56ID0gdi56O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRhZGQ6IGZ1bmN0aW9uICggdiwgdyApIHtcblxuXHRcdFx0aWYgKCB3ICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuVmVjdG9yMzogLmFkZCgpIG5vdyBvbmx5IGFjY2VwdHMgb25lIGFyZ3VtZW50LiBVc2UgLmFkZFZlY3RvcnMoIGEsIGIgKSBpbnN0ZWFkLicgKTtcblx0XHRcdFx0cmV0dXJuIHRoaXMuYWRkVmVjdG9ycyggdiwgdyApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHRoaXMueCArPSB2Lng7XG5cdFx0XHR0aGlzLnkgKz0gdi55O1xuXHRcdFx0dGhpcy56ICs9IHYuejtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0YWRkU2NhbGFyOiBmdW5jdGlvbiAoIHMgKSB7XG5cblx0XHRcdHRoaXMueCArPSBzO1xuXHRcdFx0dGhpcy55ICs9IHM7XG5cdFx0XHR0aGlzLnogKz0gcztcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0YWRkVmVjdG9yczogZnVuY3Rpb24gKCBhLCBiICkge1xuXG5cdFx0XHR0aGlzLnggPSBhLnggKyBiLng7XG5cdFx0XHR0aGlzLnkgPSBhLnkgKyBiLnk7XG5cdFx0XHR0aGlzLnogPSBhLnogKyBiLno7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGFkZFNjYWxlZFZlY3RvcjogZnVuY3Rpb24gKCB2LCBzICkge1xuXG5cdFx0XHR0aGlzLnggKz0gdi54ICogcztcblx0XHRcdHRoaXMueSArPSB2LnkgKiBzO1xuXHRcdFx0dGhpcy56ICs9IHYueiAqIHM7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHN1YjogZnVuY3Rpb24gKCB2LCB3ICkge1xuXG5cdFx0XHRpZiAoIHcgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5WZWN0b3IzOiAuc3ViKCkgbm93IG9ubHkgYWNjZXB0cyBvbmUgYXJndW1lbnQuIFVzZSAuc3ViVmVjdG9ycyggYSwgYiApIGluc3RlYWQuJyApO1xuXHRcdFx0XHRyZXR1cm4gdGhpcy5zdWJWZWN0b3JzKCB2LCB3ICk7XG5cblx0XHRcdH1cblxuXHRcdFx0dGhpcy54IC09IHYueDtcblx0XHRcdHRoaXMueSAtPSB2Lnk7XG5cdFx0XHR0aGlzLnogLT0gdi56O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzdWJTY2FsYXI6IGZ1bmN0aW9uICggcyApIHtcblxuXHRcdFx0dGhpcy54IC09IHM7XG5cdFx0XHR0aGlzLnkgLT0gcztcblx0XHRcdHRoaXMueiAtPSBzO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzdWJWZWN0b3JzOiBmdW5jdGlvbiAoIGEsIGIgKSB7XG5cblx0XHRcdHRoaXMueCA9IGEueCAtIGIueDtcblx0XHRcdHRoaXMueSA9IGEueSAtIGIueTtcblx0XHRcdHRoaXMueiA9IGEueiAtIGIuejtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0bXVsdGlwbHk6IGZ1bmN0aW9uICggdiwgdyApIHtcblxuXHRcdFx0aWYgKCB3ICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuVmVjdG9yMzogLm11bHRpcGx5KCkgbm93IG9ubHkgYWNjZXB0cyBvbmUgYXJndW1lbnQuIFVzZSAubXVsdGlwbHlWZWN0b3JzKCBhLCBiICkgaW5zdGVhZC4nICk7XG5cdFx0XHRcdHJldHVybiB0aGlzLm11bHRpcGx5VmVjdG9ycyggdiwgdyApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHRoaXMueCAqPSB2Lng7XG5cdFx0XHR0aGlzLnkgKj0gdi55O1xuXHRcdFx0dGhpcy56ICo9IHYuejtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0bXVsdGlwbHlTY2FsYXI6IGZ1bmN0aW9uICggc2NhbGFyICkge1xuXG5cdFx0XHRpZiAoIGlzRmluaXRlKCBzY2FsYXIgKSApIHtcblxuXHRcdFx0XHR0aGlzLnggKj0gc2NhbGFyO1xuXHRcdFx0XHR0aGlzLnkgKj0gc2NhbGFyO1xuXHRcdFx0XHR0aGlzLnogKj0gc2NhbGFyO1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdHRoaXMueCA9IDA7XG5cdFx0XHRcdHRoaXMueSA9IDA7XG5cdFx0XHRcdHRoaXMueiA9IDA7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0bXVsdGlwbHlWZWN0b3JzOiBmdW5jdGlvbiAoIGEsIGIgKSB7XG5cblx0XHRcdHRoaXMueCA9IGEueCAqIGIueDtcblx0XHRcdHRoaXMueSA9IGEueSAqIGIueTtcblx0XHRcdHRoaXMueiA9IGEueiAqIGIuejtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0YXBwbHlFdWxlcjogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgcXVhdGVybmlvbjtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIGFwcGx5RXVsZXIoIGV1bGVyICkge1xuXG5cdFx0XHRcdGlmICggKGV1bGVyICYmIGV1bGVyLmlzRXVsZXIpID09PSBmYWxzZSApIHtcblxuXHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5WZWN0b3IzOiAuYXBwbHlFdWxlcigpIG5vdyBleHBlY3RzIGFuIEV1bGVyIHJvdGF0aW9uIHJhdGhlciB0aGFuIGEgVmVjdG9yMyBhbmQgb3JkZXIuJyApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoIHF1YXRlcm5pb24gPT09IHVuZGVmaW5lZCApIHF1YXRlcm5pb24gPSBuZXcgUXVhdGVybmlvbigpO1xuXG5cdFx0XHRcdHJldHVybiB0aGlzLmFwcGx5UXVhdGVybmlvbiggcXVhdGVybmlvbi5zZXRGcm9tRXVsZXIoIGV1bGVyICkgKTtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdGFwcGx5QXhpc0FuZ2xlOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBxdWF0ZXJuaW9uO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gYXBwbHlBeGlzQW5nbGUoIGF4aXMsIGFuZ2xlICkge1xuXG5cdFx0XHRcdGlmICggcXVhdGVybmlvbiA9PT0gdW5kZWZpbmVkICkgcXVhdGVybmlvbiA9IG5ldyBRdWF0ZXJuaW9uKCk7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXMuYXBwbHlRdWF0ZXJuaW9uKCBxdWF0ZXJuaW9uLnNldEZyb21BeGlzQW5nbGUoIGF4aXMsIGFuZ2xlICkgKTtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdGFwcGx5TWF0cml4MzogZnVuY3Rpb24gKCBtICkge1xuXG5cdFx0XHR2YXIgeCA9IHRoaXMueCwgeSA9IHRoaXMueSwgeiA9IHRoaXMuejtcblx0XHRcdHZhciBlID0gbS5lbGVtZW50cztcblxuXHRcdFx0dGhpcy54ID0gZVsgMCBdICogeCArIGVbIDMgXSAqIHkgKyBlWyA2IF0gKiB6O1xuXHRcdFx0dGhpcy55ID0gZVsgMSBdICogeCArIGVbIDQgXSAqIHkgKyBlWyA3IF0gKiB6O1xuXHRcdFx0dGhpcy56ID0gZVsgMiBdICogeCArIGVbIDUgXSAqIHkgKyBlWyA4IF0gKiB6O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRhcHBseU1hdHJpeDQ6IGZ1bmN0aW9uICggbSApIHtcblxuXHRcdFx0Ly8gaW5wdXQ6IFRIUkVFLk1hdHJpeDQgYWZmaW5lIG1hdHJpeFxuXG5cdFx0XHR2YXIgeCA9IHRoaXMueCwgeSA9IHRoaXMueSwgeiA9IHRoaXMuejtcblx0XHRcdHZhciBlID0gbS5lbGVtZW50cztcblxuXHRcdFx0dGhpcy54ID0gZVsgMCBdICogeCArIGVbIDQgXSAqIHkgKyBlWyA4IF0gICogeiArIGVbIDEyIF07XG5cdFx0XHR0aGlzLnkgPSBlWyAxIF0gKiB4ICsgZVsgNSBdICogeSArIGVbIDkgXSAgKiB6ICsgZVsgMTMgXTtcblx0XHRcdHRoaXMueiA9IGVbIDIgXSAqIHggKyBlWyA2IF0gKiB5ICsgZVsgMTAgXSAqIHogKyBlWyAxNCBdO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRhcHBseVByb2plY3Rpb246IGZ1bmN0aW9uICggbSApIHtcblxuXHRcdFx0Ly8gaW5wdXQ6IFRIUkVFLk1hdHJpeDQgcHJvamVjdGlvbiBtYXRyaXhcblxuXHRcdFx0dmFyIHggPSB0aGlzLngsIHkgPSB0aGlzLnksIHogPSB0aGlzLno7XG5cdFx0XHR2YXIgZSA9IG0uZWxlbWVudHM7XG5cdFx0XHR2YXIgZCA9IDEgLyAoIGVbIDMgXSAqIHggKyBlWyA3IF0gKiB5ICsgZVsgMTEgXSAqIHogKyBlWyAxNSBdICk7IC8vIHBlcnNwZWN0aXZlIGRpdmlkZVxuXG5cdFx0XHR0aGlzLnggPSAoIGVbIDAgXSAqIHggKyBlWyA0IF0gKiB5ICsgZVsgOCBdICAqIHogKyBlWyAxMiBdICkgKiBkO1xuXHRcdFx0dGhpcy55ID0gKCBlWyAxIF0gKiB4ICsgZVsgNSBdICogeSArIGVbIDkgXSAgKiB6ICsgZVsgMTMgXSApICogZDtcblx0XHRcdHRoaXMueiA9ICggZVsgMiBdICogeCArIGVbIDYgXSAqIHkgKyBlWyAxMCBdICogeiArIGVbIDE0IF0gKSAqIGQ7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGFwcGx5UXVhdGVybmlvbjogZnVuY3Rpb24gKCBxICkge1xuXG5cdFx0XHR2YXIgeCA9IHRoaXMueCwgeSA9IHRoaXMueSwgeiA9IHRoaXMuejtcblx0XHRcdHZhciBxeCA9IHEueCwgcXkgPSBxLnksIHF6ID0gcS56LCBxdyA9IHEudztcblxuXHRcdFx0Ly8gY2FsY3VsYXRlIHF1YXQgKiB2ZWN0b3JcblxuXHRcdFx0dmFyIGl4ID0gIHF3ICogeCArIHF5ICogeiAtIHF6ICogeTtcblx0XHRcdHZhciBpeSA9ICBxdyAqIHkgKyBxeiAqIHggLSBxeCAqIHo7XG5cdFx0XHR2YXIgaXogPSAgcXcgKiB6ICsgcXggKiB5IC0gcXkgKiB4O1xuXHRcdFx0dmFyIGl3ID0gLSBxeCAqIHggLSBxeSAqIHkgLSBxeiAqIHo7XG5cblx0XHRcdC8vIGNhbGN1bGF0ZSByZXN1bHQgKiBpbnZlcnNlIHF1YXRcblxuXHRcdFx0dGhpcy54ID0gaXggKiBxdyArIGl3ICogLSBxeCArIGl5ICogLSBxeiAtIGl6ICogLSBxeTtcblx0XHRcdHRoaXMueSA9IGl5ICogcXcgKyBpdyAqIC0gcXkgKyBpeiAqIC0gcXggLSBpeCAqIC0gcXo7XG5cdFx0XHR0aGlzLnogPSBpeiAqIHF3ICsgaXcgKiAtIHF6ICsgaXggKiAtIHF5IC0gaXkgKiAtIHF4O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRwcm9qZWN0OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBtYXRyaXg7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBwcm9qZWN0KCBjYW1lcmEgKSB7XG5cblx0XHRcdFx0aWYgKCBtYXRyaXggPT09IHVuZGVmaW5lZCApIG1hdHJpeCA9IG5ldyBNYXRyaXg0KCk7XG5cblx0XHRcdFx0bWF0cml4Lm11bHRpcGx5TWF0cmljZXMoIGNhbWVyYS5wcm9qZWN0aW9uTWF0cml4LCBtYXRyaXguZ2V0SW52ZXJzZSggY2FtZXJhLm1hdHJpeFdvcmxkICkgKTtcblx0XHRcdFx0cmV0dXJuIHRoaXMuYXBwbHlQcm9qZWN0aW9uKCBtYXRyaXggKTtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdHVucHJvamVjdDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgbWF0cml4O1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gdW5wcm9qZWN0KCBjYW1lcmEgKSB7XG5cblx0XHRcdFx0aWYgKCBtYXRyaXggPT09IHVuZGVmaW5lZCApIG1hdHJpeCA9IG5ldyBNYXRyaXg0KCk7XG5cblx0XHRcdFx0bWF0cml4Lm11bHRpcGx5TWF0cmljZXMoIGNhbWVyYS5tYXRyaXhXb3JsZCwgbWF0cml4LmdldEludmVyc2UoIGNhbWVyYS5wcm9qZWN0aW9uTWF0cml4ICkgKTtcblx0XHRcdFx0cmV0dXJuIHRoaXMuYXBwbHlQcm9qZWN0aW9uKCBtYXRyaXggKTtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdHRyYW5zZm9ybURpcmVjdGlvbjogZnVuY3Rpb24gKCBtICkge1xuXG5cdFx0XHQvLyBpbnB1dDogVEhSRUUuTWF0cml4NCBhZmZpbmUgbWF0cml4XG5cdFx0XHQvLyB2ZWN0b3IgaW50ZXJwcmV0ZWQgYXMgYSBkaXJlY3Rpb25cblxuXHRcdFx0dmFyIHggPSB0aGlzLngsIHkgPSB0aGlzLnksIHogPSB0aGlzLno7XG5cdFx0XHR2YXIgZSA9IG0uZWxlbWVudHM7XG5cblx0XHRcdHRoaXMueCA9IGVbIDAgXSAqIHggKyBlWyA0IF0gKiB5ICsgZVsgOCBdICAqIHo7XG5cdFx0XHR0aGlzLnkgPSBlWyAxIF0gKiB4ICsgZVsgNSBdICogeSArIGVbIDkgXSAgKiB6O1xuXHRcdFx0dGhpcy56ID0gZVsgMiBdICogeCArIGVbIDYgXSAqIHkgKyBlWyAxMCBdICogejtcblxuXHRcdFx0cmV0dXJuIHRoaXMubm9ybWFsaXplKCk7XG5cblx0XHR9LFxuXG5cdFx0ZGl2aWRlOiBmdW5jdGlvbiAoIHYgKSB7XG5cblx0XHRcdHRoaXMueCAvPSB2Lng7XG5cdFx0XHR0aGlzLnkgLz0gdi55O1xuXHRcdFx0dGhpcy56IC89IHYuejtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0ZGl2aWRlU2NhbGFyOiBmdW5jdGlvbiAoIHNjYWxhciApIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMubXVsdGlwbHlTY2FsYXIoIDEgLyBzY2FsYXIgKTtcblxuXHRcdH0sXG5cblx0XHRtaW46IGZ1bmN0aW9uICggdiApIHtcblxuXHRcdFx0dGhpcy54ID0gTWF0aC5taW4oIHRoaXMueCwgdi54ICk7XG5cdFx0XHR0aGlzLnkgPSBNYXRoLm1pbiggdGhpcy55LCB2LnkgKTtcblx0XHRcdHRoaXMueiA9IE1hdGgubWluKCB0aGlzLnosIHYueiApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRtYXg6IGZ1bmN0aW9uICggdiApIHtcblxuXHRcdFx0dGhpcy54ID0gTWF0aC5tYXgoIHRoaXMueCwgdi54ICk7XG5cdFx0XHR0aGlzLnkgPSBNYXRoLm1heCggdGhpcy55LCB2LnkgKTtcblx0XHRcdHRoaXMueiA9IE1hdGgubWF4KCB0aGlzLnosIHYueiApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjbGFtcDogZnVuY3Rpb24gKCBtaW4sIG1heCApIHtcblxuXHRcdFx0Ly8gVGhpcyBmdW5jdGlvbiBhc3N1bWVzIG1pbiA8IG1heCwgaWYgdGhpcyBhc3N1bXB0aW9uIGlzbid0IHRydWUgaXQgd2lsbCBub3Qgb3BlcmF0ZSBjb3JyZWN0bHlcblxuXHRcdFx0dGhpcy54ID0gTWF0aC5tYXgoIG1pbi54LCBNYXRoLm1pbiggbWF4LngsIHRoaXMueCApICk7XG5cdFx0XHR0aGlzLnkgPSBNYXRoLm1heCggbWluLnksIE1hdGgubWluKCBtYXgueSwgdGhpcy55ICkgKTtcblx0XHRcdHRoaXMueiA9IE1hdGgubWF4KCBtaW4ueiwgTWF0aC5taW4oIG1heC56LCB0aGlzLnogKSApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjbGFtcFNjYWxhcjogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgbWluLCBtYXg7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBjbGFtcFNjYWxhciggbWluVmFsLCBtYXhWYWwgKSB7XG5cblx0XHRcdFx0aWYgKCBtaW4gPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdG1pbiA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHRcdFx0bWF4ID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0bWluLnNldCggbWluVmFsLCBtaW5WYWwsIG1pblZhbCApO1xuXHRcdFx0XHRtYXguc2V0KCBtYXhWYWwsIG1heFZhbCwgbWF4VmFsICk7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXMuY2xhbXAoIG1pbiwgbWF4ICk7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRjbGFtcExlbmd0aDogZnVuY3Rpb24gKCBtaW4sIG1heCApIHtcblxuXHRcdFx0dmFyIGxlbmd0aCA9IHRoaXMubGVuZ3RoKCk7XG5cblx0XHRcdHJldHVybiB0aGlzLm11bHRpcGx5U2NhbGFyKCBNYXRoLm1heCggbWluLCBNYXRoLm1pbiggbWF4LCBsZW5ndGggKSApIC8gbGVuZ3RoICk7XG5cblx0XHR9LFxuXG5cdFx0Zmxvb3I6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dGhpcy54ID0gTWF0aC5mbG9vciggdGhpcy54ICk7XG5cdFx0XHR0aGlzLnkgPSBNYXRoLmZsb29yKCB0aGlzLnkgKTtcblx0XHRcdHRoaXMueiA9IE1hdGguZmxvb3IoIHRoaXMueiApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjZWlsOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMueCA9IE1hdGguY2VpbCggdGhpcy54ICk7XG5cdFx0XHR0aGlzLnkgPSBNYXRoLmNlaWwoIHRoaXMueSApO1xuXHRcdFx0dGhpcy56ID0gTWF0aC5jZWlsKCB0aGlzLnogKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0cm91bmQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dGhpcy54ID0gTWF0aC5yb3VuZCggdGhpcy54ICk7XG5cdFx0XHR0aGlzLnkgPSBNYXRoLnJvdW5kKCB0aGlzLnkgKTtcblx0XHRcdHRoaXMueiA9IE1hdGgucm91bmQoIHRoaXMueiApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRyb3VuZFRvWmVybzogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR0aGlzLnggPSAoIHRoaXMueCA8IDAgKSA/IE1hdGguY2VpbCggdGhpcy54ICkgOiBNYXRoLmZsb29yKCB0aGlzLnggKTtcblx0XHRcdHRoaXMueSA9ICggdGhpcy55IDwgMCApID8gTWF0aC5jZWlsKCB0aGlzLnkgKSA6IE1hdGguZmxvb3IoIHRoaXMueSApO1xuXHRcdFx0dGhpcy56ID0gKCB0aGlzLnogPCAwICkgPyBNYXRoLmNlaWwoIHRoaXMueiApIDogTWF0aC5mbG9vciggdGhpcy56ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdG5lZ2F0ZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR0aGlzLnggPSAtIHRoaXMueDtcblx0XHRcdHRoaXMueSA9IC0gdGhpcy55O1xuXHRcdFx0dGhpcy56ID0gLSB0aGlzLno7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGRvdDogZnVuY3Rpb24gKCB2ICkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy54ICogdi54ICsgdGhpcy55ICogdi55ICsgdGhpcy56ICogdi56O1xuXG5cdFx0fSxcblxuXHRcdGxlbmd0aFNxOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLnggKiB0aGlzLnggKyB0aGlzLnkgKiB0aGlzLnkgKyB0aGlzLnogKiB0aGlzLno7XG5cblx0XHR9LFxuXG5cdFx0bGVuZ3RoOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiBNYXRoLnNxcnQoIHRoaXMueCAqIHRoaXMueCArIHRoaXMueSAqIHRoaXMueSArIHRoaXMueiAqIHRoaXMueiApO1xuXG5cdFx0fSxcblxuXHRcdGxlbmd0aE1hbmhhdHRhbjogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gTWF0aC5hYnMoIHRoaXMueCApICsgTWF0aC5hYnMoIHRoaXMueSApICsgTWF0aC5hYnMoIHRoaXMueiApO1xuXG5cdFx0fSxcblxuXHRcdG5vcm1hbGl6ZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5kaXZpZGVTY2FsYXIoIHRoaXMubGVuZ3RoKCkgKTtcblxuXHRcdH0sXG5cblx0XHRzZXRMZW5ndGg6IGZ1bmN0aW9uICggbGVuZ3RoICkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5tdWx0aXBseVNjYWxhciggbGVuZ3RoIC8gdGhpcy5sZW5ndGgoKSApO1xuXG5cdFx0fSxcblxuXHRcdGxlcnA6IGZ1bmN0aW9uICggdiwgYWxwaGEgKSB7XG5cblx0XHRcdHRoaXMueCArPSAoIHYueCAtIHRoaXMueCApICogYWxwaGE7XG5cdFx0XHR0aGlzLnkgKz0gKCB2LnkgLSB0aGlzLnkgKSAqIGFscGhhO1xuXHRcdFx0dGhpcy56ICs9ICggdi56IC0gdGhpcy56ICkgKiBhbHBoYTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0bGVycFZlY3RvcnM6IGZ1bmN0aW9uICggdjEsIHYyLCBhbHBoYSApIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuc3ViVmVjdG9ycyggdjIsIHYxICkubXVsdGlwbHlTY2FsYXIoIGFscGhhICkuYWRkKCB2MSApO1xuXG5cdFx0fSxcblxuXHRcdGNyb3NzOiBmdW5jdGlvbiAoIHYsIHcgKSB7XG5cblx0XHRcdGlmICggdyAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLlZlY3RvcjM6IC5jcm9zcygpIG5vdyBvbmx5IGFjY2VwdHMgb25lIGFyZ3VtZW50LiBVc2UgLmNyb3NzVmVjdG9ycyggYSwgYiApIGluc3RlYWQuJyApO1xuXHRcdFx0XHRyZXR1cm4gdGhpcy5jcm9zc1ZlY3RvcnMoIHYsIHcgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgeCA9IHRoaXMueCwgeSA9IHRoaXMueSwgeiA9IHRoaXMuejtcblxuXHRcdFx0dGhpcy54ID0geSAqIHYueiAtIHogKiB2Lnk7XG5cdFx0XHR0aGlzLnkgPSB6ICogdi54IC0geCAqIHYuejtcblx0XHRcdHRoaXMueiA9IHggKiB2LnkgLSB5ICogdi54O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjcm9zc1ZlY3RvcnM6IGZ1bmN0aW9uICggYSwgYiApIHtcblxuXHRcdFx0dmFyIGF4ID0gYS54LCBheSA9IGEueSwgYXogPSBhLno7XG5cdFx0XHR2YXIgYnggPSBiLngsIGJ5ID0gYi55LCBieiA9IGIuejtcblxuXHRcdFx0dGhpcy54ID0gYXkgKiBieiAtIGF6ICogYnk7XG5cdFx0XHR0aGlzLnkgPSBheiAqIGJ4IC0gYXggKiBiejtcblx0XHRcdHRoaXMueiA9IGF4ICogYnkgLSBheSAqIGJ4O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRwcm9qZWN0T25WZWN0b3I6IGZ1bmN0aW9uICggdmVjdG9yICkge1xuXG5cdFx0XHR2YXIgc2NhbGFyID0gdmVjdG9yLmRvdCggdGhpcyApIC8gdmVjdG9yLmxlbmd0aFNxKCk7XG5cblx0XHRcdHJldHVybiB0aGlzLmNvcHkoIHZlY3RvciApLm11bHRpcGx5U2NhbGFyKCBzY2FsYXIgKTtcblxuXHRcdH0sXG5cblx0XHRwcm9qZWN0T25QbGFuZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgdjE7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBwcm9qZWN0T25QbGFuZSggcGxhbmVOb3JtYWwgKSB7XG5cblx0XHRcdFx0aWYgKCB2MSA9PT0gdW5kZWZpbmVkICkgdjEgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRcdHYxLmNvcHkoIHRoaXMgKS5wcm9qZWN0T25WZWN0b3IoIHBsYW5lTm9ybWFsICk7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXMuc3ViKCB2MSApO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0cmVmbGVjdDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHQvLyByZWZsZWN0IGluY2lkZW50IHZlY3RvciBvZmYgcGxhbmUgb3J0aG9nb25hbCB0byBub3JtYWxcblx0XHRcdC8vIG5vcm1hbCBpcyBhc3N1bWVkIHRvIGhhdmUgdW5pdCBsZW5ndGhcblxuXHRcdFx0dmFyIHYxO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gcmVmbGVjdCggbm9ybWFsICkge1xuXG5cdFx0XHRcdGlmICggdjEgPT09IHVuZGVmaW5lZCApIHYxID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcy5zdWIoIHYxLmNvcHkoIG5vcm1hbCApLm11bHRpcGx5U2NhbGFyKCAyICogdGhpcy5kb3QoIG5vcm1hbCApICkgKTtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdGFuZ2xlVG86IGZ1bmN0aW9uICggdiApIHtcblxuXHRcdFx0dmFyIHRoZXRhID0gdGhpcy5kb3QoIHYgKSAvICggTWF0aC5zcXJ0KCB0aGlzLmxlbmd0aFNxKCkgKiB2Lmxlbmd0aFNxKCkgKSApO1xuXG5cdFx0XHQvLyBjbGFtcCwgdG8gaGFuZGxlIG51bWVyaWNhbCBwcm9ibGVtc1xuXG5cdFx0XHRyZXR1cm4gTWF0aC5hY29zKCBfTWF0aC5jbGFtcCggdGhldGEsIC0gMSwgMSApICk7XG5cblx0XHR9LFxuXG5cdFx0ZGlzdGFuY2VUbzogZnVuY3Rpb24gKCB2ICkge1xuXG5cdFx0XHRyZXR1cm4gTWF0aC5zcXJ0KCB0aGlzLmRpc3RhbmNlVG9TcXVhcmVkKCB2ICkgKTtcblxuXHRcdH0sXG5cblx0XHRkaXN0YW5jZVRvU3F1YXJlZDogZnVuY3Rpb24gKCB2ICkge1xuXG5cdFx0XHR2YXIgZHggPSB0aGlzLnggLSB2LngsIGR5ID0gdGhpcy55IC0gdi55LCBkeiA9IHRoaXMueiAtIHYuejtcblxuXHRcdFx0cmV0dXJuIGR4ICogZHggKyBkeSAqIGR5ICsgZHogKiBkejtcblxuXHRcdH0sXG5cblx0XHRkaXN0YW5jZVRvTWFuaGF0dGFuOiBmdW5jdGlvbiAoIHYgKSB7XG5cblx0XHRcdHJldHVybiBNYXRoLmFicyggdGhpcy54IC0gdi54ICkgKyBNYXRoLmFicyggdGhpcy55IC0gdi55ICkgKyBNYXRoLmFicyggdGhpcy56IC0gdi56ICk7XG5cblx0XHR9LFxuXG5cdFx0c2V0RnJvbVNwaGVyaWNhbDogZnVuY3Rpb24oIHMgKSB7XG5cblx0XHRcdHZhciBzaW5QaGlSYWRpdXMgPSBNYXRoLnNpbiggcy5waGkgKSAqIHMucmFkaXVzO1xuXG5cdFx0XHR0aGlzLnggPSBzaW5QaGlSYWRpdXMgKiBNYXRoLnNpbiggcy50aGV0YSApO1xuXHRcdFx0dGhpcy55ID0gTWF0aC5jb3MoIHMucGhpICkgKiBzLnJhZGl1cztcblx0XHRcdHRoaXMueiA9IHNpblBoaVJhZGl1cyAqIE1hdGguY29zKCBzLnRoZXRhICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldEZyb21DeWxpbmRyaWNhbDogZnVuY3Rpb24oIGMgKSB7XG5cblx0XHRcdHRoaXMueCA9IGMucmFkaXVzICogTWF0aC5zaW4oIGMudGhldGEgKTtcblx0XHRcdHRoaXMueSA9IGMueTtcblx0XHRcdHRoaXMueiA9IGMucmFkaXVzICogTWF0aC5jb3MoIGMudGhldGEgKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0RnJvbU1hdHJpeFBvc2l0aW9uOiBmdW5jdGlvbiAoIG0gKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLnNldEZyb21NYXRyaXhDb2x1bW4oIG0sIDMgKTtcblxuXHRcdH0sXG5cblx0XHRzZXRGcm9tTWF0cml4U2NhbGU6IGZ1bmN0aW9uICggbSApIHtcblxuXHRcdFx0dmFyIHN4ID0gdGhpcy5zZXRGcm9tTWF0cml4Q29sdW1uKCBtLCAwICkubGVuZ3RoKCk7XG5cdFx0XHR2YXIgc3kgPSB0aGlzLnNldEZyb21NYXRyaXhDb2x1bW4oIG0sIDEgKS5sZW5ndGgoKTtcblx0XHRcdHZhciBzeiA9IHRoaXMuc2V0RnJvbU1hdHJpeENvbHVtbiggbSwgMiApLmxlbmd0aCgpO1xuXG5cdFx0XHR0aGlzLnggPSBzeDtcblx0XHRcdHRoaXMueSA9IHN5O1xuXHRcdFx0dGhpcy56ID0gc3o7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldEZyb21NYXRyaXhDb2x1bW46IGZ1bmN0aW9uICggbSwgaW5kZXggKSB7XG5cblx0XHRcdGlmICggdHlwZW9mIG0gPT09ICdudW1iZXInICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLlZlY3RvcjM6IHNldEZyb21NYXRyaXhDb2x1bW4gbm93IGV4cGVjdHMgKCBtYXRyaXgsIGluZGV4ICkuJyApO1xuXHRcdFx0XHR2YXIgdGVtcCA9IG07XG5cdFx0XHRcdG0gPSBpbmRleDtcblx0XHRcdFx0aW5kZXggPSB0ZW1wO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzLmZyb21BcnJheSggbS5lbGVtZW50cywgaW5kZXggKiA0ICk7XG5cblx0XHR9LFxuXG5cdFx0ZXF1YWxzOiBmdW5jdGlvbiAoIHYgKSB7XG5cblx0XHRcdHJldHVybiAoICggdi54ID09PSB0aGlzLnggKSAmJiAoIHYueSA9PT0gdGhpcy55ICkgJiYgKCB2LnogPT09IHRoaXMueiApICk7XG5cblx0XHR9LFxuXG5cdFx0ZnJvbUFycmF5OiBmdW5jdGlvbiAoIGFycmF5LCBvZmZzZXQgKSB7XG5cblx0XHRcdGlmICggb2Zmc2V0ID09PSB1bmRlZmluZWQgKSBvZmZzZXQgPSAwO1xuXG5cdFx0XHR0aGlzLnggPSBhcnJheVsgb2Zmc2V0IF07XG5cdFx0XHR0aGlzLnkgPSBhcnJheVsgb2Zmc2V0ICsgMSBdO1xuXHRcdFx0dGhpcy56ID0gYXJyYXlbIG9mZnNldCArIDIgXTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0dG9BcnJheTogZnVuY3Rpb24gKCBhcnJheSwgb2Zmc2V0ICkge1xuXG5cdFx0XHRpZiAoIGFycmF5ID09PSB1bmRlZmluZWQgKSBhcnJheSA9IFtdO1xuXHRcdFx0aWYgKCBvZmZzZXQgPT09IHVuZGVmaW5lZCApIG9mZnNldCA9IDA7XG5cblx0XHRcdGFycmF5WyBvZmZzZXQgXSA9IHRoaXMueDtcblx0XHRcdGFycmF5WyBvZmZzZXQgKyAxIF0gPSB0aGlzLnk7XG5cdFx0XHRhcnJheVsgb2Zmc2V0ICsgMiBdID0gdGhpcy56O1xuXG5cdFx0XHRyZXR1cm4gYXJyYXk7XG5cblx0XHR9LFxuXG5cdFx0ZnJvbUF0dHJpYnV0ZTogZnVuY3Rpb24gKCBhdHRyaWJ1dGUsIGluZGV4LCBvZmZzZXQgKSB7XG5cblx0XHRcdGlmICggb2Zmc2V0ICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuVmVjdG9yMzogb2Zmc2V0IGhhcyBiZWVuIHJlbW92ZWQgZnJvbSAuZnJvbUF0dHJpYnV0ZSgpLicgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLnggPSBhdHRyaWJ1dGUuZ2V0WCggaW5kZXggKTtcblx0XHRcdHRoaXMueSA9IGF0dHJpYnV0ZS5nZXRZKCBpbmRleCApO1xuXHRcdFx0dGhpcy56ID0gYXR0cmlidXRlLmdldFooIGluZGV4ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqIEBhdXRob3Igc3VwZXJlZ2diZXJ0IC8gaHR0cDovL3d3dy5wYXVsYnJ1bnQuY28udWsvXG5cdCAqIEBhdXRob3IgcGhpbG9nYiAvIGh0dHA6Ly9ibG9nLnRoZWppdC5vcmcvXG5cdCAqIEBhdXRob3Igam9yZGlfcm9zIC8gaHR0cDovL3BsYXR0c29mdC5jb21cblx0ICogQGF1dGhvciBEMXBsbzFkIC8gaHR0cDovL2dpdGh1Yi5jb20vRDFwbG8xZFxuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKiBAYXV0aG9yIG1pa2FlbCBlbXRpbmdlciAvIGh0dHA6Ly9nb21vLnNlL1xuXHQgKiBAYXV0aG9yIHRpbWtuaXAgLyBodHRwOi8vd3d3LmZsb29ycGxhbm5lci5jb20vXG5cdCAqIEBhdXRob3IgYmhvdXN0b24gLyBodHRwOi8vY2xhcmEuaW9cblx0ICogQGF1dGhvciBXZXN0TGFuZ2xleSAvIGh0dHA6Ly9naXRodWIuY29tL1dlc3RMYW5nbGV5XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIE1hdHJpeDQoKSB7XG5cblx0XHR0aGlzLmVsZW1lbnRzID0gbmV3IEZsb2F0MzJBcnJheSggW1xuXG5cdFx0XHQxLCAwLCAwLCAwLFxuXHRcdFx0MCwgMSwgMCwgMCxcblx0XHRcdDAsIDAsIDEsIDAsXG5cdFx0XHQwLCAwLCAwLCAxXG5cblx0XHRdICk7XG5cblx0XHRpZiAoIGFyZ3VtZW50cy5sZW5ndGggPiAwICkge1xuXG5cdFx0XHRjb25zb2xlLmVycm9yKCAnVEhSRUUuTWF0cml4NDogdGhlIGNvbnN0cnVjdG9yIG5vIGxvbmdlciByZWFkcyBhcmd1bWVudHMuIHVzZSAuc2V0KCkgaW5zdGVhZC4nICk7XG5cblx0XHR9XG5cblx0fVxuXG5cdE1hdHJpeDQucHJvdG90eXBlID0ge1xuXG5cdFx0Y29uc3RydWN0b3I6IE1hdHJpeDQsXG5cblx0XHRpc01hdHJpeDQ6IHRydWUsXG5cblx0XHRzZXQ6IGZ1bmN0aW9uICggbjExLCBuMTIsIG4xMywgbjE0LCBuMjEsIG4yMiwgbjIzLCBuMjQsIG4zMSwgbjMyLCBuMzMsIG4zNCwgbjQxLCBuNDIsIG40MywgbjQ0ICkge1xuXG5cdFx0XHR2YXIgdGUgPSB0aGlzLmVsZW1lbnRzO1xuXG5cdFx0XHR0ZVsgMCBdID0gbjExOyB0ZVsgNCBdID0gbjEyOyB0ZVsgOCBdID0gbjEzOyB0ZVsgMTIgXSA9IG4xNDtcblx0XHRcdHRlWyAxIF0gPSBuMjE7IHRlWyA1IF0gPSBuMjI7IHRlWyA5IF0gPSBuMjM7IHRlWyAxMyBdID0gbjI0O1xuXHRcdFx0dGVbIDIgXSA9IG4zMTsgdGVbIDYgXSA9IG4zMjsgdGVbIDEwIF0gPSBuMzM7IHRlWyAxNCBdID0gbjM0O1xuXHRcdFx0dGVbIDMgXSA9IG40MTsgdGVbIDcgXSA9IG40MjsgdGVbIDExIF0gPSBuNDM7IHRlWyAxNSBdID0gbjQ0O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRpZGVudGl0eTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR0aGlzLnNldChcblxuXHRcdFx0XHQxLCAwLCAwLCAwLFxuXHRcdFx0XHQwLCAxLCAwLCAwLFxuXHRcdFx0XHQwLCAwLCAxLCAwLFxuXHRcdFx0XHQwLCAwLCAwLCAxXG5cblx0XHRcdCk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiBuZXcgTWF0cml4NCgpLmZyb21BcnJheSggdGhpcy5lbGVtZW50cyApO1xuXG5cdFx0fSxcblxuXHRcdGNvcHk6IGZ1bmN0aW9uICggbSApIHtcblxuXHRcdFx0dGhpcy5lbGVtZW50cy5zZXQoIG0uZWxlbWVudHMgKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Y29weVBvc2l0aW9uOiBmdW5jdGlvbiAoIG0gKSB7XG5cblx0XHRcdHZhciB0ZSA9IHRoaXMuZWxlbWVudHM7XG5cdFx0XHR2YXIgbWUgPSBtLmVsZW1lbnRzO1xuXG5cdFx0XHR0ZVsgMTIgXSA9IG1lWyAxMiBdO1xuXHRcdFx0dGVbIDEzIF0gPSBtZVsgMTMgXTtcblx0XHRcdHRlWyAxNCBdID0gbWVbIDE0IF07XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGV4dHJhY3RCYXNpczogZnVuY3Rpb24gKCB4QXhpcywgeUF4aXMsIHpBeGlzICkge1xuXG5cdFx0XHR4QXhpcy5zZXRGcm9tTWF0cml4Q29sdW1uKCB0aGlzLCAwICk7XG5cdFx0XHR5QXhpcy5zZXRGcm9tTWF0cml4Q29sdW1uKCB0aGlzLCAxICk7XG5cdFx0XHR6QXhpcy5zZXRGcm9tTWF0cml4Q29sdW1uKCB0aGlzLCAyICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdG1ha2VCYXNpczogZnVuY3Rpb24gKCB4QXhpcywgeUF4aXMsIHpBeGlzICkge1xuXG5cdFx0XHR0aGlzLnNldChcblx0XHRcdFx0eEF4aXMueCwgeUF4aXMueCwgekF4aXMueCwgMCxcblx0XHRcdFx0eEF4aXMueSwgeUF4aXMueSwgekF4aXMueSwgMCxcblx0XHRcdFx0eEF4aXMueiwgeUF4aXMueiwgekF4aXMueiwgMCxcblx0XHRcdFx0MCwgICAgICAgMCwgICAgICAgMCwgICAgICAgMVxuXHRcdFx0KTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0ZXh0cmFjdFJvdGF0aW9uOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB2MTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIGV4dHJhY3RSb3RhdGlvbiggbSApIHtcblxuXHRcdFx0XHRpZiAoIHYxID09PSB1bmRlZmluZWQgKSB2MSA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdFx0dmFyIHRlID0gdGhpcy5lbGVtZW50cztcblx0XHRcdFx0dmFyIG1lID0gbS5lbGVtZW50cztcblxuXHRcdFx0XHR2YXIgc2NhbGVYID0gMSAvIHYxLnNldEZyb21NYXRyaXhDb2x1bW4oIG0sIDAgKS5sZW5ndGgoKTtcblx0XHRcdFx0dmFyIHNjYWxlWSA9IDEgLyB2MS5zZXRGcm9tTWF0cml4Q29sdW1uKCBtLCAxICkubGVuZ3RoKCk7XG5cdFx0XHRcdHZhciBzY2FsZVogPSAxIC8gdjEuc2V0RnJvbU1hdHJpeENvbHVtbiggbSwgMiApLmxlbmd0aCgpO1xuXG5cdFx0XHRcdHRlWyAwIF0gPSBtZVsgMCBdICogc2NhbGVYO1xuXHRcdFx0XHR0ZVsgMSBdID0gbWVbIDEgXSAqIHNjYWxlWDtcblx0XHRcdFx0dGVbIDIgXSA9IG1lWyAyIF0gKiBzY2FsZVg7XG5cblx0XHRcdFx0dGVbIDQgXSA9IG1lWyA0IF0gKiBzY2FsZVk7XG5cdFx0XHRcdHRlWyA1IF0gPSBtZVsgNSBdICogc2NhbGVZO1xuXHRcdFx0XHR0ZVsgNiBdID0gbWVbIDYgXSAqIHNjYWxlWTtcblxuXHRcdFx0XHR0ZVsgOCBdID0gbWVbIDggXSAqIHNjYWxlWjtcblx0XHRcdFx0dGVbIDkgXSA9IG1lWyA5IF0gKiBzY2FsZVo7XG5cdFx0XHRcdHRlWyAxMCBdID0gbWVbIDEwIF0gKiBzY2FsZVo7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRtYWtlUm90YXRpb25Gcm9tRXVsZXI6IGZ1bmN0aW9uICggZXVsZXIgKSB7XG5cblx0XHRcdGlmICggKGV1bGVyICYmIGV1bGVyLmlzRXVsZXIpID09PSBmYWxzZSApIHtcblxuXHRcdFx0XHRjb25zb2xlLmVycm9yKCAnVEhSRUUuTWF0cml4OiAubWFrZVJvdGF0aW9uRnJvbUV1bGVyKCkgbm93IGV4cGVjdHMgYSBFdWxlciByb3RhdGlvbiByYXRoZXIgdGhhbiBhIFZlY3RvcjMgYW5kIG9yZGVyLicgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgdGUgPSB0aGlzLmVsZW1lbnRzO1xuXG5cdFx0XHR2YXIgeCA9IGV1bGVyLngsIHkgPSBldWxlci55LCB6ID0gZXVsZXIuejtcblx0XHRcdHZhciBhID0gTWF0aC5jb3MoIHggKSwgYiA9IE1hdGguc2luKCB4ICk7XG5cdFx0XHR2YXIgYyA9IE1hdGguY29zKCB5ICksIGQgPSBNYXRoLnNpbiggeSApO1xuXHRcdFx0dmFyIGUgPSBNYXRoLmNvcyggeiApLCBmID0gTWF0aC5zaW4oIHogKTtcblxuXHRcdFx0aWYgKCBldWxlci5vcmRlciA9PT0gJ1hZWicgKSB7XG5cblx0XHRcdFx0dmFyIGFlID0gYSAqIGUsIGFmID0gYSAqIGYsIGJlID0gYiAqIGUsIGJmID0gYiAqIGY7XG5cblx0XHRcdFx0dGVbIDAgXSA9IGMgKiBlO1xuXHRcdFx0XHR0ZVsgNCBdID0gLSBjICogZjtcblx0XHRcdFx0dGVbIDggXSA9IGQ7XG5cblx0XHRcdFx0dGVbIDEgXSA9IGFmICsgYmUgKiBkO1xuXHRcdFx0XHR0ZVsgNSBdID0gYWUgLSBiZiAqIGQ7XG5cdFx0XHRcdHRlWyA5IF0gPSAtIGIgKiBjO1xuXG5cdFx0XHRcdHRlWyAyIF0gPSBiZiAtIGFlICogZDtcblx0XHRcdFx0dGVbIDYgXSA9IGJlICsgYWYgKiBkO1xuXHRcdFx0XHR0ZVsgMTAgXSA9IGEgKiBjO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCBldWxlci5vcmRlciA9PT0gJ1lYWicgKSB7XG5cblx0XHRcdFx0dmFyIGNlID0gYyAqIGUsIGNmID0gYyAqIGYsIGRlID0gZCAqIGUsIGRmID0gZCAqIGY7XG5cblx0XHRcdFx0dGVbIDAgXSA9IGNlICsgZGYgKiBiO1xuXHRcdFx0XHR0ZVsgNCBdID0gZGUgKiBiIC0gY2Y7XG5cdFx0XHRcdHRlWyA4IF0gPSBhICogZDtcblxuXHRcdFx0XHR0ZVsgMSBdID0gYSAqIGY7XG5cdFx0XHRcdHRlWyA1IF0gPSBhICogZTtcblx0XHRcdFx0dGVbIDkgXSA9IC0gYjtcblxuXHRcdFx0XHR0ZVsgMiBdID0gY2YgKiBiIC0gZGU7XG5cdFx0XHRcdHRlWyA2IF0gPSBkZiArIGNlICogYjtcblx0XHRcdFx0dGVbIDEwIF0gPSBhICogYztcblxuXHRcdFx0fSBlbHNlIGlmICggZXVsZXIub3JkZXIgPT09ICdaWFknICkge1xuXG5cdFx0XHRcdHZhciBjZSA9IGMgKiBlLCBjZiA9IGMgKiBmLCBkZSA9IGQgKiBlLCBkZiA9IGQgKiBmO1xuXG5cdFx0XHRcdHRlWyAwIF0gPSBjZSAtIGRmICogYjtcblx0XHRcdFx0dGVbIDQgXSA9IC0gYSAqIGY7XG5cdFx0XHRcdHRlWyA4IF0gPSBkZSArIGNmICogYjtcblxuXHRcdFx0XHR0ZVsgMSBdID0gY2YgKyBkZSAqIGI7XG5cdFx0XHRcdHRlWyA1IF0gPSBhICogZTtcblx0XHRcdFx0dGVbIDkgXSA9IGRmIC0gY2UgKiBiO1xuXG5cdFx0XHRcdHRlWyAyIF0gPSAtIGEgKiBkO1xuXHRcdFx0XHR0ZVsgNiBdID0gYjtcblx0XHRcdFx0dGVbIDEwIF0gPSBhICogYztcblxuXHRcdFx0fSBlbHNlIGlmICggZXVsZXIub3JkZXIgPT09ICdaWVgnICkge1xuXG5cdFx0XHRcdHZhciBhZSA9IGEgKiBlLCBhZiA9IGEgKiBmLCBiZSA9IGIgKiBlLCBiZiA9IGIgKiBmO1xuXG5cdFx0XHRcdHRlWyAwIF0gPSBjICogZTtcblx0XHRcdFx0dGVbIDQgXSA9IGJlICogZCAtIGFmO1xuXHRcdFx0XHR0ZVsgOCBdID0gYWUgKiBkICsgYmY7XG5cblx0XHRcdFx0dGVbIDEgXSA9IGMgKiBmO1xuXHRcdFx0XHR0ZVsgNSBdID0gYmYgKiBkICsgYWU7XG5cdFx0XHRcdHRlWyA5IF0gPSBhZiAqIGQgLSBiZTtcblxuXHRcdFx0XHR0ZVsgMiBdID0gLSBkO1xuXHRcdFx0XHR0ZVsgNiBdID0gYiAqIGM7XG5cdFx0XHRcdHRlWyAxMCBdID0gYSAqIGM7XG5cblx0XHRcdH0gZWxzZSBpZiAoIGV1bGVyLm9yZGVyID09PSAnWVpYJyApIHtcblxuXHRcdFx0XHR2YXIgYWMgPSBhICogYywgYWQgPSBhICogZCwgYmMgPSBiICogYywgYmQgPSBiICogZDtcblxuXHRcdFx0XHR0ZVsgMCBdID0gYyAqIGU7XG5cdFx0XHRcdHRlWyA0IF0gPSBiZCAtIGFjICogZjtcblx0XHRcdFx0dGVbIDggXSA9IGJjICogZiArIGFkO1xuXG5cdFx0XHRcdHRlWyAxIF0gPSBmO1xuXHRcdFx0XHR0ZVsgNSBdID0gYSAqIGU7XG5cdFx0XHRcdHRlWyA5IF0gPSAtIGIgKiBlO1xuXG5cdFx0XHRcdHRlWyAyIF0gPSAtIGQgKiBlO1xuXHRcdFx0XHR0ZVsgNiBdID0gYWQgKiBmICsgYmM7XG5cdFx0XHRcdHRlWyAxMCBdID0gYWMgLSBiZCAqIGY7XG5cblx0XHRcdH0gZWxzZSBpZiAoIGV1bGVyLm9yZGVyID09PSAnWFpZJyApIHtcblxuXHRcdFx0XHR2YXIgYWMgPSBhICogYywgYWQgPSBhICogZCwgYmMgPSBiICogYywgYmQgPSBiICogZDtcblxuXHRcdFx0XHR0ZVsgMCBdID0gYyAqIGU7XG5cdFx0XHRcdHRlWyA0IF0gPSAtIGY7XG5cdFx0XHRcdHRlWyA4IF0gPSBkICogZTtcblxuXHRcdFx0XHR0ZVsgMSBdID0gYWMgKiBmICsgYmQ7XG5cdFx0XHRcdHRlWyA1IF0gPSBhICogZTtcblx0XHRcdFx0dGVbIDkgXSA9IGFkICogZiAtIGJjO1xuXG5cdFx0XHRcdHRlWyAyIF0gPSBiYyAqIGYgLSBhZDtcblx0XHRcdFx0dGVbIDYgXSA9IGIgKiBlO1xuXHRcdFx0XHR0ZVsgMTAgXSA9IGJkICogZiArIGFjO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIGxhc3QgY29sdW1uXG5cdFx0XHR0ZVsgMyBdID0gMDtcblx0XHRcdHRlWyA3IF0gPSAwO1xuXHRcdFx0dGVbIDExIF0gPSAwO1xuXG5cdFx0XHQvLyBib3R0b20gcm93XG5cdFx0XHR0ZVsgMTIgXSA9IDA7XG5cdFx0XHR0ZVsgMTMgXSA9IDA7XG5cdFx0XHR0ZVsgMTQgXSA9IDA7XG5cdFx0XHR0ZVsgMTUgXSA9IDE7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdG1ha2VSb3RhdGlvbkZyb21RdWF0ZXJuaW9uOiBmdW5jdGlvbiAoIHEgKSB7XG5cblx0XHRcdHZhciB0ZSA9IHRoaXMuZWxlbWVudHM7XG5cblx0XHRcdHZhciB4ID0gcS54LCB5ID0gcS55LCB6ID0gcS56LCB3ID0gcS53O1xuXHRcdFx0dmFyIHgyID0geCArIHgsIHkyID0geSArIHksIHoyID0geiArIHo7XG5cdFx0XHR2YXIgeHggPSB4ICogeDIsIHh5ID0geCAqIHkyLCB4eiA9IHggKiB6Mjtcblx0XHRcdHZhciB5eSA9IHkgKiB5MiwgeXogPSB5ICogejIsIHp6ID0geiAqIHoyO1xuXHRcdFx0dmFyIHd4ID0gdyAqIHgyLCB3eSA9IHcgKiB5Miwgd3ogPSB3ICogejI7XG5cblx0XHRcdHRlWyAwIF0gPSAxIC0gKCB5eSArIHp6ICk7XG5cdFx0XHR0ZVsgNCBdID0geHkgLSB3ejtcblx0XHRcdHRlWyA4IF0gPSB4eiArIHd5O1xuXG5cdFx0XHR0ZVsgMSBdID0geHkgKyB3ejtcblx0XHRcdHRlWyA1IF0gPSAxIC0gKCB4eCArIHp6ICk7XG5cdFx0XHR0ZVsgOSBdID0geXogLSB3eDtcblxuXHRcdFx0dGVbIDIgXSA9IHh6IC0gd3k7XG5cdFx0XHR0ZVsgNiBdID0geXogKyB3eDtcblx0XHRcdHRlWyAxMCBdID0gMSAtICggeHggKyB5eSApO1xuXG5cdFx0XHQvLyBsYXN0IGNvbHVtblxuXHRcdFx0dGVbIDMgXSA9IDA7XG5cdFx0XHR0ZVsgNyBdID0gMDtcblx0XHRcdHRlWyAxMSBdID0gMDtcblxuXHRcdFx0Ly8gYm90dG9tIHJvd1xuXHRcdFx0dGVbIDEyIF0gPSAwO1xuXHRcdFx0dGVbIDEzIF0gPSAwO1xuXHRcdFx0dGVbIDE0IF0gPSAwO1xuXHRcdFx0dGVbIDE1IF0gPSAxO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRsb29rQXQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIHgsIHksIHo7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBsb29rQXQoIGV5ZSwgdGFyZ2V0LCB1cCApIHtcblxuXHRcdFx0XHRpZiAoIHggPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdHggPSBuZXcgVmVjdG9yMygpO1xuXHRcdFx0XHRcdHkgPSBuZXcgVmVjdG9yMygpO1xuXHRcdFx0XHRcdHogPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR2YXIgdGUgPSB0aGlzLmVsZW1lbnRzO1xuXG5cdFx0XHRcdHouc3ViVmVjdG9ycyggZXllLCB0YXJnZXQgKS5ub3JtYWxpemUoKTtcblxuXHRcdFx0XHRpZiAoIHoubGVuZ3RoU3EoKSA9PT0gMCApIHtcblxuXHRcdFx0XHRcdHoueiA9IDE7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHguY3Jvc3NWZWN0b3JzKCB1cCwgeiApLm5vcm1hbGl6ZSgpO1xuXG5cdFx0XHRcdGlmICggeC5sZW5ndGhTcSgpID09PSAwICkge1xuXG5cdFx0XHRcdFx0ei56ICs9IDAuMDAwMTtcblx0XHRcdFx0XHR4LmNyb3NzVmVjdG9ycyggdXAsIHogKS5ub3JtYWxpemUoKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0eS5jcm9zc1ZlY3RvcnMoIHosIHggKTtcblxuXG5cdFx0XHRcdHRlWyAwIF0gPSB4Lng7IHRlWyA0IF0gPSB5Lng7IHRlWyA4IF0gPSB6Lng7XG5cdFx0XHRcdHRlWyAxIF0gPSB4Lnk7IHRlWyA1IF0gPSB5Lnk7IHRlWyA5IF0gPSB6Lnk7XG5cdFx0XHRcdHRlWyAyIF0gPSB4Lno7IHRlWyA2IF0gPSB5Lno7IHRlWyAxMCBdID0gei56O1xuXG5cdFx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0bXVsdGlwbHk6IGZ1bmN0aW9uICggbSwgbiApIHtcblxuXHRcdFx0aWYgKCBuICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuTWF0cml4NDogLm11bHRpcGx5KCkgbm93IG9ubHkgYWNjZXB0cyBvbmUgYXJndW1lbnQuIFVzZSAubXVsdGlwbHlNYXRyaWNlcyggYSwgYiApIGluc3RlYWQuJyApO1xuXHRcdFx0XHRyZXR1cm4gdGhpcy5tdWx0aXBseU1hdHJpY2VzKCBtLCBuICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXMubXVsdGlwbHlNYXRyaWNlcyggdGhpcywgbSApO1xuXG5cdFx0fSxcblxuXHRcdHByZW11bHRpcGx5OiBmdW5jdGlvbiAoIG0gKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLm11bHRpcGx5TWF0cmljZXMoIG0sIHRoaXMgKTtcblxuXHRcdH0sXG5cblx0XHRtdWx0aXBseU1hdHJpY2VzOiBmdW5jdGlvbiAoIGEsIGIgKSB7XG5cblx0XHRcdHZhciBhZSA9IGEuZWxlbWVudHM7XG5cdFx0XHR2YXIgYmUgPSBiLmVsZW1lbnRzO1xuXHRcdFx0dmFyIHRlID0gdGhpcy5lbGVtZW50cztcblxuXHRcdFx0dmFyIGExMSA9IGFlWyAwIF0sIGExMiA9IGFlWyA0IF0sIGExMyA9IGFlWyA4IF0sIGExNCA9IGFlWyAxMiBdO1xuXHRcdFx0dmFyIGEyMSA9IGFlWyAxIF0sIGEyMiA9IGFlWyA1IF0sIGEyMyA9IGFlWyA5IF0sIGEyNCA9IGFlWyAxMyBdO1xuXHRcdFx0dmFyIGEzMSA9IGFlWyAyIF0sIGEzMiA9IGFlWyA2IF0sIGEzMyA9IGFlWyAxMCBdLCBhMzQgPSBhZVsgMTQgXTtcblx0XHRcdHZhciBhNDEgPSBhZVsgMyBdLCBhNDIgPSBhZVsgNyBdLCBhNDMgPSBhZVsgMTEgXSwgYTQ0ID0gYWVbIDE1IF07XG5cblx0XHRcdHZhciBiMTEgPSBiZVsgMCBdLCBiMTIgPSBiZVsgNCBdLCBiMTMgPSBiZVsgOCBdLCBiMTQgPSBiZVsgMTIgXTtcblx0XHRcdHZhciBiMjEgPSBiZVsgMSBdLCBiMjIgPSBiZVsgNSBdLCBiMjMgPSBiZVsgOSBdLCBiMjQgPSBiZVsgMTMgXTtcblx0XHRcdHZhciBiMzEgPSBiZVsgMiBdLCBiMzIgPSBiZVsgNiBdLCBiMzMgPSBiZVsgMTAgXSwgYjM0ID0gYmVbIDE0IF07XG5cdFx0XHR2YXIgYjQxID0gYmVbIDMgXSwgYjQyID0gYmVbIDcgXSwgYjQzID0gYmVbIDExIF0sIGI0NCA9IGJlWyAxNSBdO1xuXG5cdFx0XHR0ZVsgMCBdID0gYTExICogYjExICsgYTEyICogYjIxICsgYTEzICogYjMxICsgYTE0ICogYjQxO1xuXHRcdFx0dGVbIDQgXSA9IGExMSAqIGIxMiArIGExMiAqIGIyMiArIGExMyAqIGIzMiArIGExNCAqIGI0Mjtcblx0XHRcdHRlWyA4IF0gPSBhMTEgKiBiMTMgKyBhMTIgKiBiMjMgKyBhMTMgKiBiMzMgKyBhMTQgKiBiNDM7XG5cdFx0XHR0ZVsgMTIgXSA9IGExMSAqIGIxNCArIGExMiAqIGIyNCArIGExMyAqIGIzNCArIGExNCAqIGI0NDtcblxuXHRcdFx0dGVbIDEgXSA9IGEyMSAqIGIxMSArIGEyMiAqIGIyMSArIGEyMyAqIGIzMSArIGEyNCAqIGI0MTtcblx0XHRcdHRlWyA1IF0gPSBhMjEgKiBiMTIgKyBhMjIgKiBiMjIgKyBhMjMgKiBiMzIgKyBhMjQgKiBiNDI7XG5cdFx0XHR0ZVsgOSBdID0gYTIxICogYjEzICsgYTIyICogYjIzICsgYTIzICogYjMzICsgYTI0ICogYjQzO1xuXHRcdFx0dGVbIDEzIF0gPSBhMjEgKiBiMTQgKyBhMjIgKiBiMjQgKyBhMjMgKiBiMzQgKyBhMjQgKiBiNDQ7XG5cblx0XHRcdHRlWyAyIF0gPSBhMzEgKiBiMTEgKyBhMzIgKiBiMjEgKyBhMzMgKiBiMzEgKyBhMzQgKiBiNDE7XG5cdFx0XHR0ZVsgNiBdID0gYTMxICogYjEyICsgYTMyICogYjIyICsgYTMzICogYjMyICsgYTM0ICogYjQyO1xuXHRcdFx0dGVbIDEwIF0gPSBhMzEgKiBiMTMgKyBhMzIgKiBiMjMgKyBhMzMgKiBiMzMgKyBhMzQgKiBiNDM7XG5cdFx0XHR0ZVsgMTQgXSA9IGEzMSAqIGIxNCArIGEzMiAqIGIyNCArIGEzMyAqIGIzNCArIGEzNCAqIGI0NDtcblxuXHRcdFx0dGVbIDMgXSA9IGE0MSAqIGIxMSArIGE0MiAqIGIyMSArIGE0MyAqIGIzMSArIGE0NCAqIGI0MTtcblx0XHRcdHRlWyA3IF0gPSBhNDEgKiBiMTIgKyBhNDIgKiBiMjIgKyBhNDMgKiBiMzIgKyBhNDQgKiBiNDI7XG5cdFx0XHR0ZVsgMTEgXSA9IGE0MSAqIGIxMyArIGE0MiAqIGIyMyArIGE0MyAqIGIzMyArIGE0NCAqIGI0Mztcblx0XHRcdHRlWyAxNSBdID0gYTQxICogYjE0ICsgYTQyICogYjI0ICsgYTQzICogYjM0ICsgYTQ0ICogYjQ0O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRtdWx0aXBseVRvQXJyYXk6IGZ1bmN0aW9uICggYSwgYiwgciApIHtcblxuXHRcdFx0dmFyIHRlID0gdGhpcy5lbGVtZW50cztcblxuXHRcdFx0dGhpcy5tdWx0aXBseU1hdHJpY2VzKCBhLCBiICk7XG5cblx0XHRcdHJbIDAgXSA9IHRlWyAwIF07IHJbIDEgXSA9IHRlWyAxIF07IHJbIDIgXSA9IHRlWyAyIF07IHJbIDMgXSA9IHRlWyAzIF07XG5cdFx0XHRyWyA0IF0gPSB0ZVsgNCBdOyByWyA1IF0gPSB0ZVsgNSBdOyByWyA2IF0gPSB0ZVsgNiBdOyByWyA3IF0gPSB0ZVsgNyBdO1xuXHRcdFx0clsgOCBdICA9IHRlWyA4IF07IHJbIDkgXSAgPSB0ZVsgOSBdOyByWyAxMCBdID0gdGVbIDEwIF07IHJbIDExIF0gPSB0ZVsgMTEgXTtcblx0XHRcdHJbIDEyIF0gPSB0ZVsgMTIgXTsgclsgMTMgXSA9IHRlWyAxMyBdOyByWyAxNCBdID0gdGVbIDE0IF07IHJbIDE1IF0gPSB0ZVsgMTUgXTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0bXVsdGlwbHlTY2FsYXI6IGZ1bmN0aW9uICggcyApIHtcblxuXHRcdFx0dmFyIHRlID0gdGhpcy5lbGVtZW50cztcblxuXHRcdFx0dGVbIDAgXSAqPSBzOyB0ZVsgNCBdICo9IHM7IHRlWyA4IF0gKj0gczsgdGVbIDEyIF0gKj0gcztcblx0XHRcdHRlWyAxIF0gKj0gczsgdGVbIDUgXSAqPSBzOyB0ZVsgOSBdICo9IHM7IHRlWyAxMyBdICo9IHM7XG5cdFx0XHR0ZVsgMiBdICo9IHM7IHRlWyA2IF0gKj0gczsgdGVbIDEwIF0gKj0gczsgdGVbIDE0IF0gKj0gcztcblx0XHRcdHRlWyAzIF0gKj0gczsgdGVbIDcgXSAqPSBzOyB0ZVsgMTEgXSAqPSBzOyB0ZVsgMTUgXSAqPSBzO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRhcHBseVRvVmVjdG9yM0FycmF5OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB2MTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIGFwcGx5VG9WZWN0b3IzQXJyYXkoIGFycmF5LCBvZmZzZXQsIGxlbmd0aCApIHtcblxuXHRcdFx0XHRpZiAoIHYxID09PSB1bmRlZmluZWQgKSB2MSA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHRcdGlmICggb2Zmc2V0ID09PSB1bmRlZmluZWQgKSBvZmZzZXQgPSAwO1xuXHRcdFx0XHRpZiAoIGxlbmd0aCA9PT0gdW5kZWZpbmVkICkgbGVuZ3RoID0gYXJyYXkubGVuZ3RoO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMCwgaiA9IG9mZnNldDsgaSA8IGxlbmd0aDsgaSArPSAzLCBqICs9IDMgKSB7XG5cblx0XHRcdFx0XHR2MS5mcm9tQXJyYXkoIGFycmF5LCBqICk7XG5cdFx0XHRcdFx0djEuYXBwbHlNYXRyaXg0KCB0aGlzICk7XG5cdFx0XHRcdFx0djEudG9BcnJheSggYXJyYXksIGogKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuIGFycmF5O1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0YXBwbHlUb0J1ZmZlckF0dHJpYnV0ZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgdjE7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBhcHBseVRvQnVmZmVyQXR0cmlidXRlKCBhdHRyaWJ1dGUgKSB7XG5cblx0XHRcdFx0aWYgKCB2MSA9PT0gdW5kZWZpbmVkICkgdjEgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGF0dHJpYnV0ZS5jb3VudDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0XHR2MS54ID0gYXR0cmlidXRlLmdldFgoIGkgKTtcblx0XHRcdFx0XHR2MS55ID0gYXR0cmlidXRlLmdldFkoIGkgKTtcblx0XHRcdFx0XHR2MS56ID0gYXR0cmlidXRlLmdldFooIGkgKTtcblxuXHRcdFx0XHRcdHYxLmFwcGx5TWF0cml4NCggdGhpcyApO1xuXG5cdFx0XHRcdFx0YXR0cmlidXRlLnNldFhZWiggaSwgdjEueCwgdjEueSwgdjEueiApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRyZXR1cm4gYXR0cmlidXRlO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0ZGV0ZXJtaW5hbnQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIHRlID0gdGhpcy5lbGVtZW50cztcblxuXHRcdFx0dmFyIG4xMSA9IHRlWyAwIF0sIG4xMiA9IHRlWyA0IF0sIG4xMyA9IHRlWyA4IF0sIG4xNCA9IHRlWyAxMiBdO1xuXHRcdFx0dmFyIG4yMSA9IHRlWyAxIF0sIG4yMiA9IHRlWyA1IF0sIG4yMyA9IHRlWyA5IF0sIG4yNCA9IHRlWyAxMyBdO1xuXHRcdFx0dmFyIG4zMSA9IHRlWyAyIF0sIG4zMiA9IHRlWyA2IF0sIG4zMyA9IHRlWyAxMCBdLCBuMzQgPSB0ZVsgMTQgXTtcblx0XHRcdHZhciBuNDEgPSB0ZVsgMyBdLCBuNDIgPSB0ZVsgNyBdLCBuNDMgPSB0ZVsgMTEgXSwgbjQ0ID0gdGVbIDE1IF07XG5cblx0XHRcdC8vVE9ETzogbWFrZSB0aGlzIG1vcmUgZWZmaWNpZW50XG5cdFx0XHQvLyggYmFzZWQgb24gaHR0cDovL3d3dy5ldWNsaWRlYW5zcGFjZS5jb20vbWF0aHMvYWxnZWJyYS9tYXRyaXgvZnVuY3Rpb25zL2ludmVyc2UvZm91ckQvaW5kZXguaHRtIClcblxuXHRcdFx0cmV0dXJuIChcblx0XHRcdFx0bjQxICogKFxuXHRcdFx0XHRcdCsgbjE0ICogbjIzICogbjMyXG5cdFx0XHRcdFx0IC0gbjEzICogbjI0ICogbjMyXG5cdFx0XHRcdFx0IC0gbjE0ICogbjIyICogbjMzXG5cdFx0XHRcdFx0ICsgbjEyICogbjI0ICogbjMzXG5cdFx0XHRcdFx0ICsgbjEzICogbjIyICogbjM0XG5cdFx0XHRcdFx0IC0gbjEyICogbjIzICogbjM0XG5cdFx0XHRcdCkgK1xuXHRcdFx0XHRuNDIgKiAoXG5cdFx0XHRcdFx0KyBuMTEgKiBuMjMgKiBuMzRcblx0XHRcdFx0XHQgLSBuMTEgKiBuMjQgKiBuMzNcblx0XHRcdFx0XHQgKyBuMTQgKiBuMjEgKiBuMzNcblx0XHRcdFx0XHQgLSBuMTMgKiBuMjEgKiBuMzRcblx0XHRcdFx0XHQgKyBuMTMgKiBuMjQgKiBuMzFcblx0XHRcdFx0XHQgLSBuMTQgKiBuMjMgKiBuMzFcblx0XHRcdFx0KSArXG5cdFx0XHRcdG40MyAqIChcblx0XHRcdFx0XHQrIG4xMSAqIG4yNCAqIG4zMlxuXHRcdFx0XHRcdCAtIG4xMSAqIG4yMiAqIG4zNFxuXHRcdFx0XHRcdCAtIG4xNCAqIG4yMSAqIG4zMlxuXHRcdFx0XHRcdCArIG4xMiAqIG4yMSAqIG4zNFxuXHRcdFx0XHRcdCArIG4xNCAqIG4yMiAqIG4zMVxuXHRcdFx0XHRcdCAtIG4xMiAqIG4yNCAqIG4zMVxuXHRcdFx0XHQpICtcblx0XHRcdFx0bjQ0ICogKFxuXHRcdFx0XHRcdC0gbjEzICogbjIyICogbjMxXG5cdFx0XHRcdFx0IC0gbjExICogbjIzICogbjMyXG5cdFx0XHRcdFx0ICsgbjExICogbjIyICogbjMzXG5cdFx0XHRcdFx0ICsgbjEzICogbjIxICogbjMyXG5cdFx0XHRcdFx0IC0gbjEyICogbjIxICogbjMzXG5cdFx0XHRcdFx0ICsgbjEyICogbjIzICogbjMxXG5cdFx0XHRcdClcblxuXHRcdFx0KTtcblxuXHRcdH0sXG5cblx0XHR0cmFuc3Bvc2U6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIHRlID0gdGhpcy5lbGVtZW50cztcblx0XHRcdHZhciB0bXA7XG5cblx0XHRcdHRtcCA9IHRlWyAxIF07IHRlWyAxIF0gPSB0ZVsgNCBdOyB0ZVsgNCBdID0gdG1wO1xuXHRcdFx0dG1wID0gdGVbIDIgXTsgdGVbIDIgXSA9IHRlWyA4IF07IHRlWyA4IF0gPSB0bXA7XG5cdFx0XHR0bXAgPSB0ZVsgNiBdOyB0ZVsgNiBdID0gdGVbIDkgXTsgdGVbIDkgXSA9IHRtcDtcblxuXHRcdFx0dG1wID0gdGVbIDMgXTsgdGVbIDMgXSA9IHRlWyAxMiBdOyB0ZVsgMTIgXSA9IHRtcDtcblx0XHRcdHRtcCA9IHRlWyA3IF07IHRlWyA3IF0gPSB0ZVsgMTMgXTsgdGVbIDEzIF0gPSB0bXA7XG5cdFx0XHR0bXAgPSB0ZVsgMTEgXTsgdGVbIDExIF0gPSB0ZVsgMTQgXTsgdGVbIDE0IF0gPSB0bXA7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldFBvc2l0aW9uOiBmdW5jdGlvbiAoIHYgKSB7XG5cblx0XHRcdHZhciB0ZSA9IHRoaXMuZWxlbWVudHM7XG5cblx0XHRcdHRlWyAxMiBdID0gdi54O1xuXHRcdFx0dGVbIDEzIF0gPSB2Lnk7XG5cdFx0XHR0ZVsgMTQgXSA9IHYuejtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Z2V0SW52ZXJzZTogZnVuY3Rpb24gKCBtLCB0aHJvd09uRGVnZW5lcmF0ZSApIHtcblxuXHRcdFx0Ly8gYmFzZWQgb24gaHR0cDovL3d3dy5ldWNsaWRlYW5zcGFjZS5jb20vbWF0aHMvYWxnZWJyYS9tYXRyaXgvZnVuY3Rpb25zL2ludmVyc2UvZm91ckQvaW5kZXguaHRtXG5cdFx0XHR2YXIgdGUgPSB0aGlzLmVsZW1lbnRzLFxuXHRcdFx0XHRtZSA9IG0uZWxlbWVudHMsXG5cblx0XHRcdFx0bjExID0gbWVbIDAgXSwgbjIxID0gbWVbIDEgXSwgbjMxID0gbWVbIDIgXSwgbjQxID0gbWVbIDMgXSxcblx0XHRcdFx0bjEyID0gbWVbIDQgXSwgbjIyID0gbWVbIDUgXSwgbjMyID0gbWVbIDYgXSwgbjQyID0gbWVbIDcgXSxcblx0XHRcdFx0bjEzID0gbWVbIDggXSwgbjIzID0gbWVbIDkgXSwgbjMzID0gbWVbIDEwIF0sIG40MyA9IG1lWyAxMSBdLFxuXHRcdFx0XHRuMTQgPSBtZVsgMTIgXSwgbjI0ID0gbWVbIDEzIF0sIG4zNCA9IG1lWyAxNCBdLCBuNDQgPSBtZVsgMTUgXSxcblxuXHRcdFx0XHR0MTEgPSBuMjMgKiBuMzQgKiBuNDIgLSBuMjQgKiBuMzMgKiBuNDIgKyBuMjQgKiBuMzIgKiBuNDMgLSBuMjIgKiBuMzQgKiBuNDMgLSBuMjMgKiBuMzIgKiBuNDQgKyBuMjIgKiBuMzMgKiBuNDQsXG5cdFx0XHRcdHQxMiA9IG4xNCAqIG4zMyAqIG40MiAtIG4xMyAqIG4zNCAqIG40MiAtIG4xNCAqIG4zMiAqIG40MyArIG4xMiAqIG4zNCAqIG40MyArIG4xMyAqIG4zMiAqIG40NCAtIG4xMiAqIG4zMyAqIG40NCxcblx0XHRcdFx0dDEzID0gbjEzICogbjI0ICogbjQyIC0gbjE0ICogbjIzICogbjQyICsgbjE0ICogbjIyICogbjQzIC0gbjEyICogbjI0ICogbjQzIC0gbjEzICogbjIyICogbjQ0ICsgbjEyICogbjIzICogbjQ0LFxuXHRcdFx0XHR0MTQgPSBuMTQgKiBuMjMgKiBuMzIgLSBuMTMgKiBuMjQgKiBuMzIgLSBuMTQgKiBuMjIgKiBuMzMgKyBuMTIgKiBuMjQgKiBuMzMgKyBuMTMgKiBuMjIgKiBuMzQgLSBuMTIgKiBuMjMgKiBuMzQ7XG5cblx0XHRcdHZhciBkZXQgPSBuMTEgKiB0MTEgKyBuMjEgKiB0MTIgKyBuMzEgKiB0MTMgKyBuNDEgKiB0MTQ7XG5cblx0XHRcdGlmICggZGV0ID09PSAwICkge1xuXG5cdFx0XHRcdHZhciBtc2cgPSBcIlRIUkVFLk1hdHJpeDQuZ2V0SW52ZXJzZSgpOiBjYW4ndCBpbnZlcnQgbWF0cml4LCBkZXRlcm1pbmFudCBpcyAwXCI7XG5cblx0XHRcdFx0aWYgKCB0aHJvd09uRGVnZW5lcmF0ZSA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRcdHRocm93IG5ldyBFcnJvciggbXNnICk7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdGNvbnNvbGUud2FybiggbXNnICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJldHVybiB0aGlzLmlkZW50aXR5KCk7XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIGRldEludiA9IDEgLyBkZXQ7XG5cblx0XHRcdHRlWyAwIF0gPSB0MTEgKiBkZXRJbnY7XG5cdFx0XHR0ZVsgMSBdID0gKCBuMjQgKiBuMzMgKiBuNDEgLSBuMjMgKiBuMzQgKiBuNDEgLSBuMjQgKiBuMzEgKiBuNDMgKyBuMjEgKiBuMzQgKiBuNDMgKyBuMjMgKiBuMzEgKiBuNDQgLSBuMjEgKiBuMzMgKiBuNDQgKSAqIGRldEludjtcblx0XHRcdHRlWyAyIF0gPSAoIG4yMiAqIG4zNCAqIG40MSAtIG4yNCAqIG4zMiAqIG40MSArIG4yNCAqIG4zMSAqIG40MiAtIG4yMSAqIG4zNCAqIG40MiAtIG4yMiAqIG4zMSAqIG40NCArIG4yMSAqIG4zMiAqIG40NCApICogZGV0SW52O1xuXHRcdFx0dGVbIDMgXSA9ICggbjIzICogbjMyICogbjQxIC0gbjIyICogbjMzICogbjQxIC0gbjIzICogbjMxICogbjQyICsgbjIxICogbjMzICogbjQyICsgbjIyICogbjMxICogbjQzIC0gbjIxICogbjMyICogbjQzICkgKiBkZXRJbnY7XG5cblx0XHRcdHRlWyA0IF0gPSB0MTIgKiBkZXRJbnY7XG5cdFx0XHR0ZVsgNSBdID0gKCBuMTMgKiBuMzQgKiBuNDEgLSBuMTQgKiBuMzMgKiBuNDEgKyBuMTQgKiBuMzEgKiBuNDMgLSBuMTEgKiBuMzQgKiBuNDMgLSBuMTMgKiBuMzEgKiBuNDQgKyBuMTEgKiBuMzMgKiBuNDQgKSAqIGRldEludjtcblx0XHRcdHRlWyA2IF0gPSAoIG4xNCAqIG4zMiAqIG40MSAtIG4xMiAqIG4zNCAqIG40MSAtIG4xNCAqIG4zMSAqIG40MiArIG4xMSAqIG4zNCAqIG40MiArIG4xMiAqIG4zMSAqIG40NCAtIG4xMSAqIG4zMiAqIG40NCApICogZGV0SW52O1xuXHRcdFx0dGVbIDcgXSA9ICggbjEyICogbjMzICogbjQxIC0gbjEzICogbjMyICogbjQxICsgbjEzICogbjMxICogbjQyIC0gbjExICogbjMzICogbjQyIC0gbjEyICogbjMxICogbjQzICsgbjExICogbjMyICogbjQzICkgKiBkZXRJbnY7XG5cblx0XHRcdHRlWyA4IF0gPSB0MTMgKiBkZXRJbnY7XG5cdFx0XHR0ZVsgOSBdID0gKCBuMTQgKiBuMjMgKiBuNDEgLSBuMTMgKiBuMjQgKiBuNDEgLSBuMTQgKiBuMjEgKiBuNDMgKyBuMTEgKiBuMjQgKiBuNDMgKyBuMTMgKiBuMjEgKiBuNDQgLSBuMTEgKiBuMjMgKiBuNDQgKSAqIGRldEludjtcblx0XHRcdHRlWyAxMCBdID0gKCBuMTIgKiBuMjQgKiBuNDEgLSBuMTQgKiBuMjIgKiBuNDEgKyBuMTQgKiBuMjEgKiBuNDIgLSBuMTEgKiBuMjQgKiBuNDIgLSBuMTIgKiBuMjEgKiBuNDQgKyBuMTEgKiBuMjIgKiBuNDQgKSAqIGRldEludjtcblx0XHRcdHRlWyAxMSBdID0gKCBuMTMgKiBuMjIgKiBuNDEgLSBuMTIgKiBuMjMgKiBuNDEgLSBuMTMgKiBuMjEgKiBuNDIgKyBuMTEgKiBuMjMgKiBuNDIgKyBuMTIgKiBuMjEgKiBuNDMgLSBuMTEgKiBuMjIgKiBuNDMgKSAqIGRldEludjtcblxuXHRcdFx0dGVbIDEyIF0gPSB0MTQgKiBkZXRJbnY7XG5cdFx0XHR0ZVsgMTMgXSA9ICggbjEzICogbjI0ICogbjMxIC0gbjE0ICogbjIzICogbjMxICsgbjE0ICogbjIxICogbjMzIC0gbjExICogbjI0ICogbjMzIC0gbjEzICogbjIxICogbjM0ICsgbjExICogbjIzICogbjM0ICkgKiBkZXRJbnY7XG5cdFx0XHR0ZVsgMTQgXSA9ICggbjE0ICogbjIyICogbjMxIC0gbjEyICogbjI0ICogbjMxIC0gbjE0ICogbjIxICogbjMyICsgbjExICogbjI0ICogbjMyICsgbjEyICogbjIxICogbjM0IC0gbjExICogbjIyICogbjM0ICkgKiBkZXRJbnY7XG5cdFx0XHR0ZVsgMTUgXSA9ICggbjEyICogbjIzICogbjMxIC0gbjEzICogbjIyICogbjMxICsgbjEzICogbjIxICogbjMyIC0gbjExICogbjIzICogbjMyIC0gbjEyICogbjIxICogbjMzICsgbjExICogbjIyICogbjMzICkgKiBkZXRJbnY7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNjYWxlOiBmdW5jdGlvbiAoIHYgKSB7XG5cblx0XHRcdHZhciB0ZSA9IHRoaXMuZWxlbWVudHM7XG5cdFx0XHR2YXIgeCA9IHYueCwgeSA9IHYueSwgeiA9IHYuejtcblxuXHRcdFx0dGVbIDAgXSAqPSB4OyB0ZVsgNCBdICo9IHk7IHRlWyA4IF0gKj0gejtcblx0XHRcdHRlWyAxIF0gKj0geDsgdGVbIDUgXSAqPSB5OyB0ZVsgOSBdICo9IHo7XG5cdFx0XHR0ZVsgMiBdICo9IHg7IHRlWyA2IF0gKj0geTsgdGVbIDEwIF0gKj0gejtcblx0XHRcdHRlWyAzIF0gKj0geDsgdGVbIDcgXSAqPSB5OyB0ZVsgMTEgXSAqPSB6O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRnZXRNYXhTY2FsZU9uQXhpczogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgdGUgPSB0aGlzLmVsZW1lbnRzO1xuXG5cdFx0XHR2YXIgc2NhbGVYU3EgPSB0ZVsgMCBdICogdGVbIDAgXSArIHRlWyAxIF0gKiB0ZVsgMSBdICsgdGVbIDIgXSAqIHRlWyAyIF07XG5cdFx0XHR2YXIgc2NhbGVZU3EgPSB0ZVsgNCBdICogdGVbIDQgXSArIHRlWyA1IF0gKiB0ZVsgNSBdICsgdGVbIDYgXSAqIHRlWyA2IF07XG5cdFx0XHR2YXIgc2NhbGVaU3EgPSB0ZVsgOCBdICogdGVbIDggXSArIHRlWyA5IF0gKiB0ZVsgOSBdICsgdGVbIDEwIF0gKiB0ZVsgMTAgXTtcblxuXHRcdFx0cmV0dXJuIE1hdGguc3FydCggTWF0aC5tYXgoIHNjYWxlWFNxLCBzY2FsZVlTcSwgc2NhbGVaU3EgKSApO1xuXG5cdFx0fSxcblxuXHRcdG1ha2VUcmFuc2xhdGlvbjogZnVuY3Rpb24gKCB4LCB5LCB6ICkge1xuXG5cdFx0XHR0aGlzLnNldChcblxuXHRcdFx0XHQxLCAwLCAwLCB4LFxuXHRcdFx0XHQwLCAxLCAwLCB5LFxuXHRcdFx0XHQwLCAwLCAxLCB6LFxuXHRcdFx0XHQwLCAwLCAwLCAxXG5cblx0XHRcdCk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdG1ha2VSb3RhdGlvblg6IGZ1bmN0aW9uICggdGhldGEgKSB7XG5cblx0XHRcdHZhciBjID0gTWF0aC5jb3MoIHRoZXRhICksIHMgPSBNYXRoLnNpbiggdGhldGEgKTtcblxuXHRcdFx0dGhpcy5zZXQoXG5cblx0XHRcdFx0MSwgMCwgIDAsIDAsXG5cdFx0XHRcdDAsIGMsIC0gcywgMCxcblx0XHRcdFx0MCwgcywgIGMsIDAsXG5cdFx0XHRcdDAsIDAsICAwLCAxXG5cblx0XHRcdCk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdG1ha2VSb3RhdGlvblk6IGZ1bmN0aW9uICggdGhldGEgKSB7XG5cblx0XHRcdHZhciBjID0gTWF0aC5jb3MoIHRoZXRhICksIHMgPSBNYXRoLnNpbiggdGhldGEgKTtcblxuXHRcdFx0dGhpcy5zZXQoXG5cblx0XHRcdFx0IGMsIDAsIHMsIDAsXG5cdFx0XHRcdCAwLCAxLCAwLCAwLFxuXHRcdFx0XHQtIHMsIDAsIGMsIDAsXG5cdFx0XHRcdCAwLCAwLCAwLCAxXG5cblx0XHRcdCk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdG1ha2VSb3RhdGlvblo6IGZ1bmN0aW9uICggdGhldGEgKSB7XG5cblx0XHRcdHZhciBjID0gTWF0aC5jb3MoIHRoZXRhICksIHMgPSBNYXRoLnNpbiggdGhldGEgKTtcblxuXHRcdFx0dGhpcy5zZXQoXG5cblx0XHRcdFx0YywgLSBzLCAwLCAwLFxuXHRcdFx0XHRzLCAgYywgMCwgMCxcblx0XHRcdFx0MCwgIDAsIDEsIDAsXG5cdFx0XHRcdDAsICAwLCAwLCAxXG5cblx0XHRcdCk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdG1ha2VSb3RhdGlvbkF4aXM6IGZ1bmN0aW9uICggYXhpcywgYW5nbGUgKSB7XG5cblx0XHRcdC8vIEJhc2VkIG9uIGh0dHA6Ly93d3cuZ2FtZWRldi5uZXQvcmVmZXJlbmNlL2FydGljbGVzL2FydGljbGUxMTk5LmFzcFxuXG5cdFx0XHR2YXIgYyA9IE1hdGguY29zKCBhbmdsZSApO1xuXHRcdFx0dmFyIHMgPSBNYXRoLnNpbiggYW5nbGUgKTtcblx0XHRcdHZhciB0ID0gMSAtIGM7XG5cdFx0XHR2YXIgeCA9IGF4aXMueCwgeSA9IGF4aXMueSwgeiA9IGF4aXMuejtcblx0XHRcdHZhciB0eCA9IHQgKiB4LCB0eSA9IHQgKiB5O1xuXG5cdFx0XHR0aGlzLnNldChcblxuXHRcdFx0XHR0eCAqIHggKyBjLCB0eCAqIHkgLSBzICogeiwgdHggKiB6ICsgcyAqIHksIDAsXG5cdFx0XHRcdHR4ICogeSArIHMgKiB6LCB0eSAqIHkgKyBjLCB0eSAqIHogLSBzICogeCwgMCxcblx0XHRcdFx0dHggKiB6IC0gcyAqIHksIHR5ICogeiArIHMgKiB4LCB0ICogeiAqIHogKyBjLCAwLFxuXHRcdFx0XHQwLCAwLCAwLCAxXG5cblx0XHRcdCk7XG5cblx0XHRcdCByZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRtYWtlU2NhbGU6IGZ1bmN0aW9uICggeCwgeSwgeiApIHtcblxuXHRcdFx0dGhpcy5zZXQoXG5cblx0XHRcdFx0eCwgMCwgMCwgMCxcblx0XHRcdFx0MCwgeSwgMCwgMCxcblx0XHRcdFx0MCwgMCwgeiwgMCxcblx0XHRcdFx0MCwgMCwgMCwgMVxuXG5cdFx0XHQpO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRtYWtlU2hlYXI6IGZ1bmN0aW9uICggeCwgeSwgeiApIHtcblxuXHRcdFx0dGhpcy5zZXQoXG5cblx0XHRcdFx0MSwgeSwgeiwgMCxcblx0XHRcdFx0eCwgMSwgeiwgMCxcblx0XHRcdFx0eCwgeSwgMSwgMCxcblx0XHRcdFx0MCwgMCwgMCwgMVxuXG5cdFx0XHQpO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjb21wb3NlOiBmdW5jdGlvbiAoIHBvc2l0aW9uLCBxdWF0ZXJuaW9uLCBzY2FsZSApIHtcblxuXHRcdFx0dGhpcy5tYWtlUm90YXRpb25Gcm9tUXVhdGVybmlvbiggcXVhdGVybmlvbiApO1xuXHRcdFx0dGhpcy5zY2FsZSggc2NhbGUgKTtcblx0XHRcdHRoaXMuc2V0UG9zaXRpb24oIHBvc2l0aW9uICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGRlY29tcG9zZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgdmVjdG9yLCBtYXRyaXg7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBkZWNvbXBvc2UoIHBvc2l0aW9uLCBxdWF0ZXJuaW9uLCBzY2FsZSApIHtcblxuXHRcdFx0XHRpZiAoIHZlY3RvciA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0dmVjdG9yID0gbmV3IFZlY3RvcjMoKTtcblx0XHRcdFx0XHRtYXRyaXggPSBuZXcgTWF0cml4NCgpO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR2YXIgdGUgPSB0aGlzLmVsZW1lbnRzO1xuXG5cdFx0XHRcdHZhciBzeCA9IHZlY3Rvci5zZXQoIHRlWyAwIF0sIHRlWyAxIF0sIHRlWyAyIF0gKS5sZW5ndGgoKTtcblx0XHRcdFx0dmFyIHN5ID0gdmVjdG9yLnNldCggdGVbIDQgXSwgdGVbIDUgXSwgdGVbIDYgXSApLmxlbmd0aCgpO1xuXHRcdFx0XHR2YXIgc3ogPSB2ZWN0b3Iuc2V0KCB0ZVsgOCBdLCB0ZVsgOSBdLCB0ZVsgMTAgXSApLmxlbmd0aCgpO1xuXG5cdFx0XHRcdC8vIGlmIGRldGVybWluZSBpcyBuZWdhdGl2ZSwgd2UgbmVlZCB0byBpbnZlcnQgb25lIHNjYWxlXG5cdFx0XHRcdHZhciBkZXQgPSB0aGlzLmRldGVybWluYW50KCk7XG5cdFx0XHRcdGlmICggZGV0IDwgMCApIHtcblxuXHRcdFx0XHRcdHN4ID0gLSBzeDtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cG9zaXRpb24ueCA9IHRlWyAxMiBdO1xuXHRcdFx0XHRwb3NpdGlvbi55ID0gdGVbIDEzIF07XG5cdFx0XHRcdHBvc2l0aW9uLnogPSB0ZVsgMTQgXTtcblxuXHRcdFx0XHQvLyBzY2FsZSB0aGUgcm90YXRpb24gcGFydFxuXG5cdFx0XHRcdG1hdHJpeC5lbGVtZW50cy5zZXQoIHRoaXMuZWxlbWVudHMgKTsgLy8gYXQgdGhpcyBwb2ludCBtYXRyaXggaXMgaW5jb21wbGV0ZSBzbyB3ZSBjYW4ndCB1c2UgLmNvcHkoKVxuXG5cdFx0XHRcdHZhciBpbnZTWCA9IDEgLyBzeDtcblx0XHRcdFx0dmFyIGludlNZID0gMSAvIHN5O1xuXHRcdFx0XHR2YXIgaW52U1ogPSAxIC8gc3o7XG5cblx0XHRcdFx0bWF0cml4LmVsZW1lbnRzWyAwIF0gKj0gaW52U1g7XG5cdFx0XHRcdG1hdHJpeC5lbGVtZW50c1sgMSBdICo9IGludlNYO1xuXHRcdFx0XHRtYXRyaXguZWxlbWVudHNbIDIgXSAqPSBpbnZTWDtcblxuXHRcdFx0XHRtYXRyaXguZWxlbWVudHNbIDQgXSAqPSBpbnZTWTtcblx0XHRcdFx0bWF0cml4LmVsZW1lbnRzWyA1IF0gKj0gaW52U1k7XG5cdFx0XHRcdG1hdHJpeC5lbGVtZW50c1sgNiBdICo9IGludlNZO1xuXG5cdFx0XHRcdG1hdHJpeC5lbGVtZW50c1sgOCBdICo9IGludlNaO1xuXHRcdFx0XHRtYXRyaXguZWxlbWVudHNbIDkgXSAqPSBpbnZTWjtcblx0XHRcdFx0bWF0cml4LmVsZW1lbnRzWyAxMCBdICo9IGludlNaO1xuXG5cdFx0XHRcdHF1YXRlcm5pb24uc2V0RnJvbVJvdGF0aW9uTWF0cml4KCBtYXRyaXggKTtcblxuXHRcdFx0XHRzY2FsZS54ID0gc3g7XG5cdFx0XHRcdHNjYWxlLnkgPSBzeTtcblx0XHRcdFx0c2NhbGUueiA9IHN6O1xuXG5cdFx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0bWFrZUZydXN0dW06IGZ1bmN0aW9uICggbGVmdCwgcmlnaHQsIGJvdHRvbSwgdG9wLCBuZWFyLCBmYXIgKSB7XG5cblx0XHRcdHZhciB0ZSA9IHRoaXMuZWxlbWVudHM7XG5cdFx0XHR2YXIgeCA9IDIgKiBuZWFyIC8gKCByaWdodCAtIGxlZnQgKTtcblx0XHRcdHZhciB5ID0gMiAqIG5lYXIgLyAoIHRvcCAtIGJvdHRvbSApO1xuXG5cdFx0XHR2YXIgYSA9ICggcmlnaHQgKyBsZWZ0ICkgLyAoIHJpZ2h0IC0gbGVmdCApO1xuXHRcdFx0dmFyIGIgPSAoIHRvcCArIGJvdHRvbSApIC8gKCB0b3AgLSBib3R0b20gKTtcblx0XHRcdHZhciBjID0gLSAoIGZhciArIG5lYXIgKSAvICggZmFyIC0gbmVhciApO1xuXHRcdFx0dmFyIGQgPSAtIDIgKiBmYXIgKiBuZWFyIC8gKCBmYXIgLSBuZWFyICk7XG5cblx0XHRcdHRlWyAwIF0gPSB4O1x0dGVbIDQgXSA9IDA7XHR0ZVsgOCBdID0gYTtcdHRlWyAxMiBdID0gMDtcblx0XHRcdHRlWyAxIF0gPSAwO1x0dGVbIDUgXSA9IHk7XHR0ZVsgOSBdID0gYjtcdHRlWyAxMyBdID0gMDtcblx0XHRcdHRlWyAyIF0gPSAwO1x0dGVbIDYgXSA9IDA7XHR0ZVsgMTAgXSA9IGM7XHR0ZVsgMTQgXSA9IGQ7XG5cdFx0XHR0ZVsgMyBdID0gMDtcdHRlWyA3IF0gPSAwO1x0dGVbIDExIF0gPSAtIDE7XHR0ZVsgMTUgXSA9IDA7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdG1ha2VQZXJzcGVjdGl2ZTogZnVuY3Rpb24gKCBmb3YsIGFzcGVjdCwgbmVhciwgZmFyICkge1xuXG5cdFx0XHR2YXIgeW1heCA9IG5lYXIgKiBNYXRoLnRhbiggX01hdGguREVHMlJBRCAqIGZvdiAqIDAuNSApO1xuXHRcdFx0dmFyIHltaW4gPSAtIHltYXg7XG5cdFx0XHR2YXIgeG1pbiA9IHltaW4gKiBhc3BlY3Q7XG5cdFx0XHR2YXIgeG1heCA9IHltYXggKiBhc3BlY3Q7XG5cblx0XHRcdHJldHVybiB0aGlzLm1ha2VGcnVzdHVtKCB4bWluLCB4bWF4LCB5bWluLCB5bWF4LCBuZWFyLCBmYXIgKTtcblxuXHRcdH0sXG5cblx0XHRtYWtlT3J0aG9ncmFwaGljOiBmdW5jdGlvbiAoIGxlZnQsIHJpZ2h0LCB0b3AsIGJvdHRvbSwgbmVhciwgZmFyICkge1xuXG5cdFx0XHR2YXIgdGUgPSB0aGlzLmVsZW1lbnRzO1xuXHRcdFx0dmFyIHcgPSAxLjAgLyAoIHJpZ2h0IC0gbGVmdCApO1xuXHRcdFx0dmFyIGggPSAxLjAgLyAoIHRvcCAtIGJvdHRvbSApO1xuXHRcdFx0dmFyIHAgPSAxLjAgLyAoIGZhciAtIG5lYXIgKTtcblxuXHRcdFx0dmFyIHggPSAoIHJpZ2h0ICsgbGVmdCApICogdztcblx0XHRcdHZhciB5ID0gKCB0b3AgKyBib3R0b20gKSAqIGg7XG5cdFx0XHR2YXIgeiA9ICggZmFyICsgbmVhciApICogcDtcblxuXHRcdFx0dGVbIDAgXSA9IDIgKiB3O1x0dGVbIDQgXSA9IDA7XHR0ZVsgOCBdID0gMDtcdHRlWyAxMiBdID0gLSB4O1xuXHRcdFx0dGVbIDEgXSA9IDA7XHR0ZVsgNSBdID0gMiAqIGg7XHR0ZVsgOSBdID0gMDtcdHRlWyAxMyBdID0gLSB5O1xuXHRcdFx0dGVbIDIgXSA9IDA7XHR0ZVsgNiBdID0gMDtcdHRlWyAxMCBdID0gLSAyICogcDtcdHRlWyAxNCBdID0gLSB6O1xuXHRcdFx0dGVbIDMgXSA9IDA7XHR0ZVsgNyBdID0gMDtcdHRlWyAxMSBdID0gMDtcdHRlWyAxNSBdID0gMTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0ZXF1YWxzOiBmdW5jdGlvbiAoIG1hdHJpeCApIHtcblxuXHRcdFx0dmFyIHRlID0gdGhpcy5lbGVtZW50cztcblx0XHRcdHZhciBtZSA9IG1hdHJpeC5lbGVtZW50cztcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgMTY7IGkgKysgKSB7XG5cblx0XHRcdFx0aWYgKCB0ZVsgaSBdICE9PSBtZVsgaSBdICkgcmV0dXJuIGZhbHNlO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0cnVlO1xuXG5cdFx0fSxcblxuXHRcdGZyb21BcnJheTogZnVuY3Rpb24gKCBhcnJheSwgb2Zmc2V0ICkge1xuXG5cdFx0XHRpZiAoIG9mZnNldCA9PT0gdW5kZWZpbmVkICkgb2Zmc2V0ID0gMDtcblxuXHRcdFx0Zm9yKCB2YXIgaSA9IDA7IGkgPCAxNjsgaSArKyApIHtcblxuXHRcdFx0XHR0aGlzLmVsZW1lbnRzWyBpIF0gPSBhcnJheVsgaSArIG9mZnNldCBdO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHRvQXJyYXk6IGZ1bmN0aW9uICggYXJyYXksIG9mZnNldCApIHtcblxuXHRcdFx0aWYgKCBhcnJheSA9PT0gdW5kZWZpbmVkICkgYXJyYXkgPSBbXTtcblx0XHRcdGlmICggb2Zmc2V0ID09PSB1bmRlZmluZWQgKSBvZmZzZXQgPSAwO1xuXG5cdFx0XHR2YXIgdGUgPSB0aGlzLmVsZW1lbnRzO1xuXG5cdFx0XHRhcnJheVsgb2Zmc2V0IF0gPSB0ZVsgMCBdO1xuXHRcdFx0YXJyYXlbIG9mZnNldCArIDEgXSA9IHRlWyAxIF07XG5cdFx0XHRhcnJheVsgb2Zmc2V0ICsgMiBdID0gdGVbIDIgXTtcblx0XHRcdGFycmF5WyBvZmZzZXQgKyAzIF0gPSB0ZVsgMyBdO1xuXG5cdFx0XHRhcnJheVsgb2Zmc2V0ICsgNCBdID0gdGVbIDQgXTtcblx0XHRcdGFycmF5WyBvZmZzZXQgKyA1IF0gPSB0ZVsgNSBdO1xuXHRcdFx0YXJyYXlbIG9mZnNldCArIDYgXSA9IHRlWyA2IF07XG5cdFx0XHRhcnJheVsgb2Zmc2V0ICsgNyBdID0gdGVbIDcgXTtcblxuXHRcdFx0YXJyYXlbIG9mZnNldCArIDggXSAgPSB0ZVsgOCBdO1xuXHRcdFx0YXJyYXlbIG9mZnNldCArIDkgXSAgPSB0ZVsgOSBdO1xuXHRcdFx0YXJyYXlbIG9mZnNldCArIDEwIF0gPSB0ZVsgMTAgXTtcblx0XHRcdGFycmF5WyBvZmZzZXQgKyAxMSBdID0gdGVbIDExIF07XG5cblx0XHRcdGFycmF5WyBvZmZzZXQgKyAxMiBdID0gdGVbIDEyIF07XG5cdFx0XHRhcnJheVsgb2Zmc2V0ICsgMTMgXSA9IHRlWyAxMyBdO1xuXHRcdFx0YXJyYXlbIG9mZnNldCArIDE0IF0gPSB0ZVsgMTQgXTtcblx0XHRcdGFycmF5WyBvZmZzZXQgKyAxNSBdID0gdGVbIDE1IF07XG5cblx0XHRcdHJldHVybiBhcnJheTtcblxuXHRcdH1cblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBDdWJlVGV4dHVyZSggaW1hZ2VzLCBtYXBwaW5nLCB3cmFwUywgd3JhcFQsIG1hZ0ZpbHRlciwgbWluRmlsdGVyLCBmb3JtYXQsIHR5cGUsIGFuaXNvdHJvcHksIGVuY29kaW5nICkge1xuXG5cdFx0aW1hZ2VzID0gaW1hZ2VzICE9PSB1bmRlZmluZWQgPyBpbWFnZXMgOiBbXTtcblx0XHRtYXBwaW5nID0gbWFwcGluZyAhPT0gdW5kZWZpbmVkID8gbWFwcGluZyA6IEN1YmVSZWZsZWN0aW9uTWFwcGluZztcblxuXHRcdFRleHR1cmUuY2FsbCggdGhpcywgaW1hZ2VzLCBtYXBwaW5nLCB3cmFwUywgd3JhcFQsIG1hZ0ZpbHRlciwgbWluRmlsdGVyLCBmb3JtYXQsIHR5cGUsIGFuaXNvdHJvcHksIGVuY29kaW5nICk7XG5cblx0XHR0aGlzLmZsaXBZID0gZmFsc2U7XG5cblx0fVxuXG5cdEN1YmVUZXh0dXJlLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIFRleHR1cmUucHJvdG90eXBlICk7XG5cdEN1YmVUZXh0dXJlLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IEN1YmVUZXh0dXJlO1xuXG5cdEN1YmVUZXh0dXJlLnByb3RvdHlwZS5pc0N1YmVUZXh0dXJlID0gdHJ1ZTtcblxuXHRPYmplY3QuZGVmaW5lUHJvcGVydHkoIEN1YmVUZXh0dXJlLnByb3RvdHlwZSwgJ2ltYWdlcycsIHtcblxuXHRcdGdldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5pbWFnZTtcblxuXHRcdH0sXG5cblx0XHRzZXQ6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdHRoaXMuaW1hZ2UgPSB2YWx1ZTtcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgdHNjaHdcblx0ICpcblx0ICogVW5pZm9ybXMgb2YgYSBwcm9ncmFtLlxuXHQgKiBUaG9zZSBmb3JtIGEgdHJlZSBzdHJ1Y3R1cmUgd2l0aCBhIHNwZWNpYWwgdG9wLWxldmVsIGNvbnRhaW5lciBmb3IgdGhlIHJvb3QsXG5cdCAqIHdoaWNoIHlvdSBnZXQgYnkgY2FsbGluZyAnbmV3IFdlYkdMVW5pZm9ybXMoIGdsLCBwcm9ncmFtLCByZW5kZXJlciApJy5cblx0ICpcblx0ICpcblx0ICogUHJvcGVydGllcyBvZiBpbm5lciBub2RlcyBpbmNsdWRpbmcgdGhlIHRvcC1sZXZlbCBjb250YWluZXI6XG5cdCAqXG5cdCAqIC5zZXEgLSBhcnJheSBvZiBuZXN0ZWQgdW5pZm9ybXNcblx0ICogLm1hcCAtIG5lc3RlZCB1bmlmb3JtcyBieSBuYW1lXG5cdCAqXG5cdCAqXG5cdCAqIE1ldGhvZHMgb2YgYWxsIG5vZGVzIGV4Y2VwdCB0aGUgdG9wLWxldmVsIGNvbnRhaW5lcjpcblx0ICpcblx0ICogLnNldFZhbHVlKCBnbCwgdmFsdWUsIFtyZW5kZXJlcl0gKVxuXHQgKlxuXHQgKiBcdFx0dXBsb2FkcyBhIHVuaWZvcm0gdmFsdWUocylcblx0ICogIFx0dGhlICdyZW5kZXJlcicgcGFyYW1ldGVyIGlzIG5lZWRlZCBmb3Igc2FtcGxlciB1bmlmb3Jtc1xuXHQgKlxuXHQgKlxuXHQgKiBTdGF0aWMgbWV0aG9kcyBvZiB0aGUgdG9wLWxldmVsIGNvbnRhaW5lciAocmVuZGVyZXIgZmFjdG9yaXphdGlvbnMpOlxuXHQgKlxuXHQgKiAudXBsb2FkKCBnbCwgc2VxLCB2YWx1ZXMsIHJlbmRlcmVyIClcblx0ICpcblx0ICogXHRcdHNldHMgdW5pZm9ybXMgaW4gJ3NlcScgdG8gJ3ZhbHVlc1tpZF0udmFsdWUnXG5cdCAqXG5cdCAqIC5zZXFXaXRoVmFsdWUoIHNlcSwgdmFsdWVzICkgOiBmaWx0ZXJlZFNlcVxuXHQgKlxuXHQgKiBcdFx0ZmlsdGVycyAnc2VxJyBlbnRyaWVzIHdpdGggY29ycmVzcG9uZGluZyBlbnRyeSBpbiB2YWx1ZXNcblx0ICpcblx0ICpcblx0ICogTWV0aG9kcyBvZiB0aGUgdG9wLWxldmVsIGNvbnRhaW5lciAocmVuZGVyZXIgZmFjdG9yaXphdGlvbnMpOlxuXHQgKlxuXHQgKiAuc2V0VmFsdWUoIGdsLCBuYW1lLCB2YWx1ZSApXG5cdCAqXG5cdCAqIFx0XHRzZXRzIHVuaWZvcm0gd2l0aCAgbmFtZSAnbmFtZScgdG8gJ3ZhbHVlJ1xuXHQgKlxuXHQgKiAuc2V0KCBnbCwgb2JqLCBwcm9wIClcblx0ICpcblx0ICogXHRcdHNldHMgdW5pZm9ybSBmcm9tIG9iamVjdCBhbmQgcHJvcGVydHkgd2l0aCBzYW1lIG5hbWUgdGhhbiB1bmlmb3JtXG5cdCAqXG5cdCAqIC5zZXRPcHRpb25hbCggZ2wsIG9iaiwgcHJvcCApXG5cdCAqXG5cdCAqIFx0XHRsaWtlIC5zZXQgZm9yIGFuIG9wdGlvbmFsIHByb3BlcnR5IG9mIHRoZSBvYmplY3Rcblx0ICpcblx0ICovXG5cblx0dmFyIGVtcHR5VGV4dHVyZSA9IG5ldyBUZXh0dXJlKCk7XG5cdHZhciBlbXB0eUN1YmVUZXh0dXJlID0gbmV3IEN1YmVUZXh0dXJlKCk7XG5cblx0Ly8gLS0tIEJhc2UgZm9yIGlubmVyIG5vZGVzIChpbmNsdWRpbmcgdGhlIHJvb3QpIC0tLVxuXG5cdGZ1bmN0aW9uIFVuaWZvcm1Db250YWluZXIoKSB7XG5cblx0XHR0aGlzLnNlcSA9IFtdO1xuXHRcdHRoaXMubWFwID0ge307XG5cblx0fVxuXG5cdC8vIC0tLSBVdGlsaXRpZXMgLS0tXG5cblx0Ly8gQXJyYXkgQ2FjaGVzIChwcm92aWRlIHR5cGVkIGFycmF5cyBmb3IgdGVtcG9yYXJ5IGJ5IHNpemUpXG5cblx0dmFyIGFycmF5Q2FjaGVGMzIgPSBbXTtcblx0dmFyIGFycmF5Q2FjaGVJMzIgPSBbXTtcblxuXHQvLyBGbGF0dGVuaW5nIGZvciBhcnJheXMgb2YgdmVjdG9ycyBhbmQgbWF0cmljZXNcblxuXHRmdW5jdGlvbiBmbGF0dGVuKCBhcnJheSwgbkJsb2NrcywgYmxvY2tTaXplICkge1xuXG5cdFx0dmFyIGZpcnN0RWxlbSA9IGFycmF5WyAwIF07XG5cblx0XHRpZiAoIGZpcnN0RWxlbSA8PSAwIHx8IGZpcnN0RWxlbSA+IDAgKSByZXR1cm4gYXJyYXk7XG5cdFx0Ly8gdW5vcHRpbWl6ZWQ6ICEgaXNOYU4oIGZpcnN0RWxlbSApXG5cdFx0Ly8gc2VlIGh0dHA6Ly9qYWNrc29uZHVuc3Rhbi5jb20vYXJ0aWNsZXMvOTgzXG5cblx0XHR2YXIgbiA9IG5CbG9ja3MgKiBibG9ja1NpemUsXG5cdFx0XHRyID0gYXJyYXlDYWNoZUYzMlsgbiBdO1xuXG5cdFx0aWYgKCByID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdHIgPSBuZXcgRmxvYXQzMkFycmF5KCBuICk7XG5cdFx0XHRhcnJheUNhY2hlRjMyWyBuIF0gPSByO1xuXG5cdFx0fVxuXG5cdFx0aWYgKCBuQmxvY2tzICE9PSAwICkge1xuXG5cdFx0XHRmaXJzdEVsZW0udG9BcnJheSggciwgMCApO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDEsIG9mZnNldCA9IDA7IGkgIT09IG5CbG9ja3M7ICsrIGkgKSB7XG5cblx0XHRcdFx0b2Zmc2V0ICs9IGJsb2NrU2l6ZTtcblx0XHRcdFx0YXJyYXlbIGkgXS50b0FycmF5KCByLCBvZmZzZXQgKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0cmV0dXJuIHI7XG5cblx0fVxuXG5cdC8vIFRleHR1cmUgdW5pdCBhbGxvY2F0aW9uXG5cblx0ZnVuY3Rpb24gYWxsb2NUZXhVbml0cyggcmVuZGVyZXIsIG4gKSB7XG5cblx0XHR2YXIgciA9IGFycmF5Q2FjaGVJMzJbIG4gXTtcblxuXHRcdGlmICggciA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRyID0gbmV3IEludDMyQXJyYXkoIG4gKTtcblx0XHRcdGFycmF5Q2FjaGVJMzJbIG4gXSA9IHI7XG5cblx0XHR9XG5cblx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgIT09IG47ICsrIGkgKVxuXHRcdFx0clsgaSBdID0gcmVuZGVyZXIuYWxsb2NUZXh0dXJlVW5pdCgpO1xuXG5cdFx0cmV0dXJuIHI7XG5cblx0fVxuXG5cdC8vIC0tLSBTZXR0ZXJzIC0tLVxuXG5cdC8vIE5vdGU6IERlZmluaW5nIHRoZXNlIG1ldGhvZHMgZXh0ZXJuYWxseSwgYmVjYXVzZSB0aGV5IGNvbWUgaW4gYSBidW5jaFxuXHQvLyBhbmQgdGhpcyB3YXkgdGhlaXIgbmFtZXMgbWluaWZ5LlxuXG5cdC8vIFNpbmdsZSBzY2FsYXJcblxuXHRmdW5jdGlvbiBzZXRWYWx1ZTFmKCBnbCwgdiApIHsgZ2wudW5pZm9ybTFmKCB0aGlzLmFkZHIsIHYgKTsgfVxuXHRmdW5jdGlvbiBzZXRWYWx1ZTFpKCBnbCwgdiApIHsgZ2wudW5pZm9ybTFpKCB0aGlzLmFkZHIsIHYgKTsgfVxuXG5cdC8vIFNpbmdsZSBmbG9hdCB2ZWN0b3IgKGZyb20gZmxhdCBhcnJheSBvciBUSFJFRS5WZWN0b3JOKVxuXG5cdGZ1bmN0aW9uIHNldFZhbHVlMmZ2KCBnbCwgdiApIHtcblxuXHRcdGlmICggdi54ID09PSB1bmRlZmluZWQgKSBnbC51bmlmb3JtMmZ2KCB0aGlzLmFkZHIsIHYgKTtcblx0XHRlbHNlIGdsLnVuaWZvcm0yZiggdGhpcy5hZGRyLCB2LngsIHYueSApO1xuXG5cdH1cblxuXHRmdW5jdGlvbiBzZXRWYWx1ZTNmdiggZ2wsIHYgKSB7XG5cblx0XHRpZiAoIHYueCAhPT0gdW5kZWZpbmVkIClcblx0XHRcdGdsLnVuaWZvcm0zZiggdGhpcy5hZGRyLCB2LngsIHYueSwgdi56ICk7XG5cdFx0ZWxzZSBpZiAoIHYuciAhPT0gdW5kZWZpbmVkIClcblx0XHRcdGdsLnVuaWZvcm0zZiggdGhpcy5hZGRyLCB2LnIsIHYuZywgdi5iICk7XG5cdFx0ZWxzZVxuXHRcdFx0Z2wudW5pZm9ybTNmdiggdGhpcy5hZGRyLCB2ICk7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIHNldFZhbHVlNGZ2KCBnbCwgdiApIHtcblxuXHRcdGlmICggdi54ID09PSB1bmRlZmluZWQgKSBnbC51bmlmb3JtNGZ2KCB0aGlzLmFkZHIsIHYgKTtcblx0XHRlbHNlIGdsLnVuaWZvcm00ZiggdGhpcy5hZGRyLCB2LngsIHYueSwgdi56LCB2LncgKTtcblxuXHR9XG5cblx0Ly8gU2luZ2xlIG1hdHJpeCAoZnJvbSBmbGF0IGFycmF5IG9yIE1hdHJpeE4pXG5cblx0ZnVuY3Rpb24gc2V0VmFsdWUyZm0oIGdsLCB2ICkge1xuXG5cdFx0Z2wudW5pZm9ybU1hdHJpeDJmdiggdGhpcy5hZGRyLCBmYWxzZSwgdi5lbGVtZW50cyB8fCB2ICk7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIHNldFZhbHVlM2ZtKCBnbCwgdiApIHtcblxuXHRcdGdsLnVuaWZvcm1NYXRyaXgzZnYoIHRoaXMuYWRkciwgZmFsc2UsIHYuZWxlbWVudHMgfHwgdiApO1xuXG5cdH1cblxuXHRmdW5jdGlvbiBzZXRWYWx1ZTRmbSggZ2wsIHYgKSB7XG5cblx0XHRnbC51bmlmb3JtTWF0cml4NGZ2KCB0aGlzLmFkZHIsIGZhbHNlLCB2LmVsZW1lbnRzIHx8IHYgKTtcblxuXHR9XG5cblx0Ly8gU2luZ2xlIHRleHR1cmUgKDJEIC8gQ3ViZSlcblxuXHRmdW5jdGlvbiBzZXRWYWx1ZVQxKCBnbCwgdiwgcmVuZGVyZXIgKSB7XG5cblx0XHR2YXIgdW5pdCA9IHJlbmRlcmVyLmFsbG9jVGV4dHVyZVVuaXQoKTtcblx0XHRnbC51bmlmb3JtMWkoIHRoaXMuYWRkciwgdW5pdCApO1xuXHRcdHJlbmRlcmVyLnNldFRleHR1cmUyRCggdiB8fCBlbXB0eVRleHR1cmUsIHVuaXQgKTtcblxuXHR9XG5cblx0ZnVuY3Rpb24gc2V0VmFsdWVUNiggZ2wsIHYsIHJlbmRlcmVyICkge1xuXG5cdFx0dmFyIHVuaXQgPSByZW5kZXJlci5hbGxvY1RleHR1cmVVbml0KCk7XG5cdFx0Z2wudW5pZm9ybTFpKCB0aGlzLmFkZHIsIHVuaXQgKTtcblx0XHRyZW5kZXJlci5zZXRUZXh0dXJlQ3ViZSggdiB8fCBlbXB0eUN1YmVUZXh0dXJlLCB1bml0ICk7XG5cblx0fVxuXG5cdC8vIEludGVnZXIgLyBCb29sZWFuIHZlY3RvcnMgb3IgYXJyYXlzIHRoZXJlb2YgKGFsd2F5cyBmbGF0IGFycmF5cylcblxuXHRmdW5jdGlvbiBzZXRWYWx1ZTJpdiggZ2wsIHYgKSB7IGdsLnVuaWZvcm0yaXYoIHRoaXMuYWRkciwgdiApOyB9XG5cdGZ1bmN0aW9uIHNldFZhbHVlM2l2KCBnbCwgdiApIHsgZ2wudW5pZm9ybTNpdiggdGhpcy5hZGRyLCB2ICk7IH1cblx0ZnVuY3Rpb24gc2V0VmFsdWU0aXYoIGdsLCB2ICkgeyBnbC51bmlmb3JtNGl2KCB0aGlzLmFkZHIsIHYgKTsgfVxuXG5cdC8vIEhlbHBlciB0byBwaWNrIHRoZSByaWdodCBzZXR0ZXIgZm9yIHRoZSBzaW5ndWxhciBjYXNlXG5cblx0ZnVuY3Rpb24gZ2V0U2luZ3VsYXJTZXR0ZXIoIHR5cGUgKSB7XG5cblx0XHRzd2l0Y2ggKCB0eXBlICkge1xuXG5cdFx0XHRjYXNlIDB4MTQwNjogcmV0dXJuIHNldFZhbHVlMWY7IC8vIEZMT0FUXG5cdFx0XHRjYXNlIDB4OGI1MDogcmV0dXJuIHNldFZhbHVlMmZ2OyAvLyBfVkVDMlxuXHRcdFx0Y2FzZSAweDhiNTE6IHJldHVybiBzZXRWYWx1ZTNmdjsgLy8gX1ZFQzNcblx0XHRcdGNhc2UgMHg4YjUyOiByZXR1cm4gc2V0VmFsdWU0ZnY7IC8vIF9WRUM0XG5cblx0XHRcdGNhc2UgMHg4YjVhOiByZXR1cm4gc2V0VmFsdWUyZm07IC8vIF9NQVQyXG5cdFx0XHRjYXNlIDB4OGI1YjogcmV0dXJuIHNldFZhbHVlM2ZtOyAvLyBfTUFUM1xuXHRcdFx0Y2FzZSAweDhiNWM6IHJldHVybiBzZXRWYWx1ZTRmbTsgLy8gX01BVDRcblxuXHRcdFx0Y2FzZSAweDhiNWU6IHJldHVybiBzZXRWYWx1ZVQxOyAvLyBTQU1QTEVSXzJEXG5cdFx0XHRjYXNlIDB4OGI2MDogcmV0dXJuIHNldFZhbHVlVDY7IC8vIFNBTVBMRVJfQ1VCRVxuXG5cdFx0XHRjYXNlIDB4MTQwNDogY2FzZSAweDhiNTY6IHJldHVybiBzZXRWYWx1ZTFpOyAvLyBJTlQsIEJPT0xcblx0XHRcdGNhc2UgMHg4YjUzOiBjYXNlIDB4OGI1NzogcmV0dXJuIHNldFZhbHVlMml2OyAvLyBfVkVDMlxuXHRcdFx0Y2FzZSAweDhiNTQ6IGNhc2UgMHg4YjU4OiByZXR1cm4gc2V0VmFsdWUzaXY7IC8vIF9WRUMzXG5cdFx0XHRjYXNlIDB4OGI1NTogY2FzZSAweDhiNTk6IHJldHVybiBzZXRWYWx1ZTRpdjsgLy8gX1ZFQzRcblxuXHRcdH1cblxuXHR9XG5cblx0Ly8gQXJyYXkgb2Ygc2NhbGFyc1xuXG5cdGZ1bmN0aW9uIHNldFZhbHVlMWZ2KCBnbCwgdiApIHsgZ2wudW5pZm9ybTFmdiggdGhpcy5hZGRyLCB2ICk7IH1cblx0ZnVuY3Rpb24gc2V0VmFsdWUxaXYoIGdsLCB2ICkgeyBnbC51bmlmb3JtMWl2KCB0aGlzLmFkZHIsIHYgKTsgfVxuXG5cdC8vIEFycmF5IG9mIHZlY3RvcnMgKGZsYXQgb3IgZnJvbSBUSFJFRSBjbGFzc2VzKVxuXG5cdGZ1bmN0aW9uIHNldFZhbHVlVjJhKCBnbCwgdiApIHtcblxuXHRcdGdsLnVuaWZvcm0yZnYoIHRoaXMuYWRkciwgZmxhdHRlbiggdiwgdGhpcy5zaXplLCAyICkgKTtcblxuXHR9XG5cblx0ZnVuY3Rpb24gc2V0VmFsdWVWM2EoIGdsLCB2ICkge1xuXG5cdFx0Z2wudW5pZm9ybTNmdiggdGhpcy5hZGRyLCBmbGF0dGVuKCB2LCB0aGlzLnNpemUsIDMgKSApO1xuXG5cdH1cblxuXHRmdW5jdGlvbiBzZXRWYWx1ZVY0YSggZ2wsIHYgKSB7XG5cblx0XHRnbC51bmlmb3JtNGZ2KCB0aGlzLmFkZHIsIGZsYXR0ZW4oIHYsIHRoaXMuc2l6ZSwgNCApICk7XG5cblx0fVxuXG5cdC8vIEFycmF5IG9mIG1hdHJpY2VzIChmbGF0IG9yIGZyb20gVEhSRUUgY2xhc2VzKVxuXG5cdGZ1bmN0aW9uIHNldFZhbHVlTTJhKCBnbCwgdiApIHtcblxuXHRcdGdsLnVuaWZvcm1NYXRyaXgyZnYoIHRoaXMuYWRkciwgZmFsc2UsIGZsYXR0ZW4oIHYsIHRoaXMuc2l6ZSwgNCApICk7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIHNldFZhbHVlTTNhKCBnbCwgdiApIHtcblxuXHRcdGdsLnVuaWZvcm1NYXRyaXgzZnYoIHRoaXMuYWRkciwgZmFsc2UsIGZsYXR0ZW4oIHYsIHRoaXMuc2l6ZSwgOSApICk7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIHNldFZhbHVlTTRhKCBnbCwgdiApIHtcblxuXHRcdGdsLnVuaWZvcm1NYXRyaXg0ZnYoIHRoaXMuYWRkciwgZmFsc2UsIGZsYXR0ZW4oIHYsIHRoaXMuc2l6ZSwgMTYgKSApO1xuXG5cdH1cblxuXHQvLyBBcnJheSBvZiB0ZXh0dXJlcyAoMkQgLyBDdWJlKVxuXG5cdGZ1bmN0aW9uIHNldFZhbHVlVDFhKCBnbCwgdiwgcmVuZGVyZXIgKSB7XG5cblx0XHR2YXIgbiA9IHYubGVuZ3RoLFxuXHRcdFx0dW5pdHMgPSBhbGxvY1RleFVuaXRzKCByZW5kZXJlciwgbiApO1xuXG5cdFx0Z2wudW5pZm9ybTFpdiggdGhpcy5hZGRyLCB1bml0cyApO1xuXG5cdFx0Zm9yICggdmFyIGkgPSAwOyBpICE9PSBuOyArKyBpICkge1xuXG5cdFx0XHRyZW5kZXJlci5zZXRUZXh0dXJlMkQoIHZbIGkgXSB8fCBlbXB0eVRleHR1cmUsIHVuaXRzWyBpIF0gKTtcblxuXHRcdH1cblxuXHR9XG5cblx0ZnVuY3Rpb24gc2V0VmFsdWVUNmEoIGdsLCB2LCByZW5kZXJlciApIHtcblxuXHRcdHZhciBuID0gdi5sZW5ndGgsXG5cdFx0XHR1bml0cyA9IGFsbG9jVGV4VW5pdHMoIHJlbmRlcmVyLCBuICk7XG5cblx0XHRnbC51bmlmb3JtMWl2KCB0aGlzLmFkZHIsIHVuaXRzICk7XG5cblx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgIT09IG47ICsrIGkgKSB7XG5cblx0XHRcdHJlbmRlcmVyLnNldFRleHR1cmVDdWJlKCB2WyBpIF0gfHwgZW1wdHlDdWJlVGV4dHVyZSwgdW5pdHNbIGkgXSApO1xuXG5cdFx0fVxuXG5cdH1cblxuXHQvLyBIZWxwZXIgdG8gcGljayB0aGUgcmlnaHQgc2V0dGVyIGZvciBhIHB1cmUgKGJvdHRvbS1sZXZlbCkgYXJyYXlcblxuXHRmdW5jdGlvbiBnZXRQdXJlQXJyYXlTZXR0ZXIoIHR5cGUgKSB7XG5cblx0XHRzd2l0Y2ggKCB0eXBlICkge1xuXG5cdFx0XHRjYXNlIDB4MTQwNjogcmV0dXJuIHNldFZhbHVlMWZ2OyAvLyBGTE9BVFxuXHRcdFx0Y2FzZSAweDhiNTA6IHJldHVybiBzZXRWYWx1ZVYyYTsgLy8gX1ZFQzJcblx0XHRcdGNhc2UgMHg4YjUxOiByZXR1cm4gc2V0VmFsdWVWM2E7IC8vIF9WRUMzXG5cdFx0XHRjYXNlIDB4OGI1MjogcmV0dXJuIHNldFZhbHVlVjRhOyAvLyBfVkVDNFxuXG5cdFx0XHRjYXNlIDB4OGI1YTogcmV0dXJuIHNldFZhbHVlTTJhOyAvLyBfTUFUMlxuXHRcdFx0Y2FzZSAweDhiNWI6IHJldHVybiBzZXRWYWx1ZU0zYTsgLy8gX01BVDNcblx0XHRcdGNhc2UgMHg4YjVjOiByZXR1cm4gc2V0VmFsdWVNNGE7IC8vIF9NQVQ0XG5cblx0XHRcdGNhc2UgMHg4YjVlOiByZXR1cm4gc2V0VmFsdWVUMWE7IC8vIFNBTVBMRVJfMkRcblx0XHRcdGNhc2UgMHg4YjYwOiByZXR1cm4gc2V0VmFsdWVUNmE7IC8vIFNBTVBMRVJfQ1VCRVxuXG5cdFx0XHRjYXNlIDB4MTQwNDogY2FzZSAweDhiNTY6IHJldHVybiBzZXRWYWx1ZTFpdjsgLy8gSU5ULCBCT09MXG5cdFx0XHRjYXNlIDB4OGI1MzogY2FzZSAweDhiNTc6IHJldHVybiBzZXRWYWx1ZTJpdjsgLy8gX1ZFQzJcblx0XHRcdGNhc2UgMHg4YjU0OiBjYXNlIDB4OGI1ODogcmV0dXJuIHNldFZhbHVlM2l2OyAvLyBfVkVDM1xuXHRcdFx0Y2FzZSAweDhiNTU6IGNhc2UgMHg4YjU5OiByZXR1cm4gc2V0VmFsdWU0aXY7IC8vIF9WRUM0XG5cblx0XHR9XG5cblx0fVxuXG5cdC8vIC0tLSBVbmlmb3JtIENsYXNzZXMgLS0tXG5cblx0ZnVuY3Rpb24gU2luZ2xlVW5pZm9ybSggaWQsIGFjdGl2ZUluZm8sIGFkZHIgKSB7XG5cblx0XHR0aGlzLmlkID0gaWQ7XG5cdFx0dGhpcy5hZGRyID0gYWRkcjtcblx0XHR0aGlzLnNldFZhbHVlID0gZ2V0U2luZ3VsYXJTZXR0ZXIoIGFjdGl2ZUluZm8udHlwZSApO1xuXG5cdFx0Ly8gdGhpcy5wYXRoID0gYWN0aXZlSW5mby5uYW1lOyAvLyBERUJVR1xuXG5cdH1cblxuXHRmdW5jdGlvbiBQdXJlQXJyYXlVbmlmb3JtKCBpZCwgYWN0aXZlSW5mbywgYWRkciApIHtcblxuXHRcdHRoaXMuaWQgPSBpZDtcblx0XHR0aGlzLmFkZHIgPSBhZGRyO1xuXHRcdHRoaXMuc2l6ZSA9IGFjdGl2ZUluZm8uc2l6ZTtcblx0XHR0aGlzLnNldFZhbHVlID0gZ2V0UHVyZUFycmF5U2V0dGVyKCBhY3RpdmVJbmZvLnR5cGUgKTtcblxuXHRcdC8vIHRoaXMucGF0aCA9IGFjdGl2ZUluZm8ubmFtZTsgLy8gREVCVUdcblxuXHR9XG5cblx0ZnVuY3Rpb24gU3RydWN0dXJlZFVuaWZvcm0oIGlkICkge1xuXG5cdFx0dGhpcy5pZCA9IGlkO1xuXG5cdFx0VW5pZm9ybUNvbnRhaW5lci5jYWxsKCB0aGlzICk7IC8vIG1peC1pblxuXG5cdH1cblxuXHRTdHJ1Y3R1cmVkVW5pZm9ybS5wcm90b3R5cGUuc2V0VmFsdWUgPSBmdW5jdGlvbiggZ2wsIHZhbHVlICkge1xuXG5cdFx0Ly8gTm90ZTogRG9uJ3QgbmVlZCBhbiBleHRyYSAncmVuZGVyZXInIHBhcmFtZXRlciwgc2luY2Ugc2FtcGxlcnNcblx0XHQvLyBhcmUgbm90IGFsbG93ZWQgaW4gc3RydWN0dXJlZCB1bmlmb3Jtcy5cblxuXHRcdHZhciBzZXEgPSB0aGlzLnNlcTtcblxuXHRcdGZvciAoIHZhciBpID0gMCwgbiA9IHNlcS5sZW5ndGg7IGkgIT09IG47ICsrIGkgKSB7XG5cblx0XHRcdHZhciB1ID0gc2VxWyBpIF07XG5cdFx0XHR1LnNldFZhbHVlKCBnbCwgdmFsdWVbIHUuaWQgXSApO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0Ly8gLS0tIFRvcC1sZXZlbCAtLS1cblxuXHQvLyBQYXJzZXIgLSBidWlsZHMgdXAgdGhlIHByb3BlcnR5IHRyZWUgZnJvbSB0aGUgcGF0aCBzdHJpbmdzXG5cblx0dmFyIFJlUGF0aFBhcnQgPSAvKFtcXHdcXGRfXSspKFxcXSk/KFxcW3xcXC4pPy9nO1xuXG5cdC8vIGV4dHJhY3RzXG5cdC8vIFx0LSB0aGUgaWRlbnRpZmllciAobWVtYmVyIG5hbWUgb3IgYXJyYXkgaW5kZXgpXG5cdC8vICAtIGZvbGxvd2VkIGJ5IGFuIG9wdGlvbmFsIHJpZ2h0IGJyYWNrZXQgKGZvdW5kIHdoZW4gYXJyYXkgaW5kZXgpXG5cdC8vICAtIGZvbGxvd2VkIGJ5IGFuIG9wdGlvbmFsIGxlZnQgYnJhY2tldCBvciBkb3QgKHR5cGUgb2Ygc3Vic2NyaXB0KVxuXHQvL1xuXHQvLyBOb3RlOiBUaGVzZSBwb3J0aW9ucyBjYW4gYmUgcmVhZCBpbiBhIG5vbi1vdmVybGFwcGluZyBmYXNoaW9uIGFuZFxuXHQvLyBhbGxvdyBzdHJhaWdodGZvcndhcmQgcGFyc2luZyBvZiB0aGUgaGllcmFyY2h5IHRoYXQgV2ViR0wgZW5jb2Rlc1xuXHQvLyBpbiB0aGUgdW5pZm9ybSBuYW1lcy5cblxuXHRmdW5jdGlvbiBhZGRVbmlmb3JtKCBjb250YWluZXIsIHVuaWZvcm1PYmplY3QgKSB7XG5cblx0XHRjb250YWluZXIuc2VxLnB1c2goIHVuaWZvcm1PYmplY3QgKTtcblx0XHRjb250YWluZXIubWFwWyB1bmlmb3JtT2JqZWN0LmlkIF0gPSB1bmlmb3JtT2JqZWN0O1xuXG5cdH1cblxuXHRmdW5jdGlvbiBwYXJzZVVuaWZvcm0oIGFjdGl2ZUluZm8sIGFkZHIsIGNvbnRhaW5lciApIHtcblxuXHRcdHZhciBwYXRoID0gYWN0aXZlSW5mby5uYW1lLFxuXHRcdFx0cGF0aExlbmd0aCA9IHBhdGgubGVuZ3RoO1xuXG5cdFx0Ly8gcmVzZXQgUmVnRXhwIG9iamVjdCwgYmVjYXVzZSBvZiB0aGUgZWFybHkgZXhpdCBvZiBhIHByZXZpb3VzIHJ1blxuXHRcdFJlUGF0aFBhcnQubGFzdEluZGV4ID0gMDtcblxuXHRcdGZvciAoOyA7KSB7XG5cblx0XHRcdHZhciBtYXRjaCA9IFJlUGF0aFBhcnQuZXhlYyggcGF0aCApLFxuXHRcdFx0XHRtYXRjaEVuZCA9IFJlUGF0aFBhcnQubGFzdEluZGV4LFxuXG5cdFx0XHRcdGlkID0gbWF0Y2hbIDEgXSxcblx0XHRcdFx0aWRJc0luZGV4ID0gbWF0Y2hbIDIgXSA9PT0gJ10nLFxuXHRcdFx0XHRzdWJzY3JpcHQgPSBtYXRjaFsgMyBdO1xuXG5cdFx0XHRpZiAoIGlkSXNJbmRleCApIGlkID0gaWQgfCAwOyAvLyBjb252ZXJ0IHRvIGludGVnZXJcblxuXHRcdFx0aWYgKCBzdWJzY3JpcHQgPT09IHVuZGVmaW5lZCB8fFxuXHRcdFx0XHRcdHN1YnNjcmlwdCA9PT0gJ1snICYmIG1hdGNoRW5kICsgMiA9PT0gcGF0aExlbmd0aCApIHtcblx0XHRcdFx0Ly8gYmFyZSBuYW1lIG9yIFwicHVyZVwiIGJvdHRvbS1sZXZlbCBhcnJheSBcIlswXVwiIHN1ZmZpeFxuXG5cdFx0XHRcdGFkZFVuaWZvcm0oIGNvbnRhaW5lciwgc3Vic2NyaXB0ID09PSB1bmRlZmluZWQgP1xuXHRcdFx0XHRcdFx0bmV3IFNpbmdsZVVuaWZvcm0oIGlkLCBhY3RpdmVJbmZvLCBhZGRyICkgOlxuXHRcdFx0XHRcdFx0bmV3IFB1cmVBcnJheVVuaWZvcm0oIGlkLCBhY3RpdmVJbmZvLCBhZGRyICkgKTtcblxuXHRcdFx0XHRicmVhaztcblxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Ly8gc3RlcCBpbnRvIGlubmVyIG5vZGUgLyBjcmVhdGUgaXQgaW4gY2FzZSBpdCBkb2Vzbid0IGV4aXN0XG5cblx0XHRcdFx0dmFyIG1hcCA9IGNvbnRhaW5lci5tYXAsXG5cdFx0XHRcdFx0bmV4dCA9IG1hcFsgaWQgXTtcblxuXHRcdFx0XHRpZiAoIG5leHQgPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdG5leHQgPSBuZXcgU3RydWN0dXJlZFVuaWZvcm0oIGlkICk7XG5cdFx0XHRcdFx0YWRkVW5pZm9ybSggY29udGFpbmVyLCBuZXh0ICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGNvbnRhaW5lciA9IG5leHQ7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHR9XG5cblx0Ly8gUm9vdCBDb250YWluZXJcblxuXHRmdW5jdGlvbiBXZWJHTFVuaWZvcm1zKCBnbCwgcHJvZ3JhbSwgcmVuZGVyZXIgKSB7XG5cblx0XHRVbmlmb3JtQ29udGFpbmVyLmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMucmVuZGVyZXIgPSByZW5kZXJlcjtcblxuXHRcdHZhciBuID0gZ2wuZ2V0UHJvZ3JhbVBhcmFtZXRlciggcHJvZ3JhbSwgZ2wuQUNUSVZFX1VOSUZPUk1TICk7XG5cblx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgIT09IG47ICsrIGkgKSB7XG5cblx0XHRcdHZhciBpbmZvID0gZ2wuZ2V0QWN0aXZlVW5pZm9ybSggcHJvZ3JhbSwgaSApLFxuXHRcdFx0XHRwYXRoID0gaW5mby5uYW1lLFxuXHRcdFx0XHRhZGRyID0gZ2wuZ2V0VW5pZm9ybUxvY2F0aW9uKCBwcm9ncmFtLCBwYXRoICk7XG5cblx0XHRcdHBhcnNlVW5pZm9ybSggaW5mbywgYWRkciwgdGhpcyApO1xuXG5cdFx0fVxuXG5cdH1cblxuXHRXZWJHTFVuaWZvcm1zLnByb3RvdHlwZS5zZXRWYWx1ZSA9IGZ1bmN0aW9uKCBnbCwgbmFtZSwgdmFsdWUgKSB7XG5cblx0XHR2YXIgdSA9IHRoaXMubWFwWyBuYW1lIF07XG5cblx0XHRpZiAoIHUgIT09IHVuZGVmaW5lZCApIHUuc2V0VmFsdWUoIGdsLCB2YWx1ZSwgdGhpcy5yZW5kZXJlciApO1xuXG5cdH07XG5cblx0V2ViR0xVbmlmb3Jtcy5wcm90b3R5cGUuc2V0ID0gZnVuY3Rpb24oIGdsLCBvYmplY3QsIG5hbWUgKSB7XG5cblx0XHR2YXIgdSA9IHRoaXMubWFwWyBuYW1lIF07XG5cblx0XHRpZiAoIHUgIT09IHVuZGVmaW5lZCApIHUuc2V0VmFsdWUoIGdsLCBvYmplY3RbIG5hbWUgXSwgdGhpcy5yZW5kZXJlciApO1xuXG5cdH07XG5cblx0V2ViR0xVbmlmb3Jtcy5wcm90b3R5cGUuc2V0T3B0aW9uYWwgPSBmdW5jdGlvbiggZ2wsIG9iamVjdCwgbmFtZSApIHtcblxuXHRcdHZhciB2ID0gb2JqZWN0WyBuYW1lIF07XG5cblx0XHRpZiAoIHYgIT09IHVuZGVmaW5lZCApIHRoaXMuc2V0VmFsdWUoIGdsLCBuYW1lLCB2ICk7XG5cblx0fTtcblxuXG5cdC8vIFN0YXRpYyBpbnRlcmZhY2VcblxuXHRXZWJHTFVuaWZvcm1zLnVwbG9hZCA9IGZ1bmN0aW9uKCBnbCwgc2VxLCB2YWx1ZXMsIHJlbmRlcmVyICkge1xuXG5cdFx0Zm9yICggdmFyIGkgPSAwLCBuID0gc2VxLmxlbmd0aDsgaSAhPT0gbjsgKysgaSApIHtcblxuXHRcdFx0dmFyIHUgPSBzZXFbIGkgXSxcblx0XHRcdFx0diA9IHZhbHVlc1sgdS5pZCBdO1xuXG5cdFx0XHRpZiAoIHYubmVlZHNVcGRhdGUgIT09IGZhbHNlICkge1xuXHRcdFx0XHQvLyBub3RlOiBhbHdheXMgdXBkYXRpbmcgd2hlbiAubmVlZHNVcGRhdGUgaXMgdW5kZWZpbmVkXG5cblx0XHRcdFx0dS5zZXRWYWx1ZSggZ2wsIHYudmFsdWUsIHJlbmRlcmVyICk7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHR9O1xuXG5cdFdlYkdMVW5pZm9ybXMuc2VxV2l0aFZhbHVlID0gZnVuY3Rpb24oIHNlcSwgdmFsdWVzICkge1xuXG5cdFx0dmFyIHIgPSBbXTtcblxuXHRcdGZvciAoIHZhciBpID0gMCwgbiA9IHNlcS5sZW5ndGg7IGkgIT09IG47ICsrIGkgKSB7XG5cblx0XHRcdHZhciB1ID0gc2VxWyBpIF07XG5cdFx0XHRpZiAoIHUuaWQgaW4gdmFsdWVzICkgci5wdXNoKCB1ICk7XG5cblx0XHR9XG5cblx0XHRyZXR1cm4gcjtcblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBVbmlmb3JtIFV0aWxpdGllc1xuXHQgKi9cblxuXHR2YXIgVW5pZm9ybXNVdGlscyA9IHtcblxuXHRcdG1lcmdlOiBmdW5jdGlvbiAoIHVuaWZvcm1zICkge1xuXG5cdFx0XHR2YXIgbWVyZ2VkID0ge307XG5cblx0XHRcdGZvciAoIHZhciB1ID0gMDsgdSA8IHVuaWZvcm1zLmxlbmd0aDsgdSArKyApIHtcblxuXHRcdFx0XHR2YXIgdG1wID0gdGhpcy5jbG9uZSggdW5pZm9ybXNbIHUgXSApO1xuXG5cdFx0XHRcdGZvciAoIHZhciBwIGluIHRtcCApIHtcblxuXHRcdFx0XHRcdG1lcmdlZFsgcCBdID0gdG1wWyBwIF07XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBtZXJnZWQ7XG5cblx0XHR9LFxuXG5cdFx0Y2xvbmU6IGZ1bmN0aW9uICggdW5pZm9ybXNfc3JjICkge1xuXG5cdFx0XHR2YXIgdW5pZm9ybXNfZHN0ID0ge307XG5cblx0XHRcdGZvciAoIHZhciB1IGluIHVuaWZvcm1zX3NyYyApIHtcblxuXHRcdFx0XHR1bmlmb3Jtc19kc3RbIHUgXSA9IHt9O1xuXG5cdFx0XHRcdGZvciAoIHZhciBwIGluIHVuaWZvcm1zX3NyY1sgdSBdICkge1xuXG5cdFx0XHRcdFx0dmFyIHBhcmFtZXRlcl9zcmMgPSB1bmlmb3Jtc19zcmNbIHUgXVsgcCBdO1xuXG5cdFx0XHRcdFx0aWYgKCBwYXJhbWV0ZXJfc3JjICYmICggcGFyYW1ldGVyX3NyYy5pc0NvbG9yIHx8XG5cdFx0XHRcdFx0XHRwYXJhbWV0ZXJfc3JjLmlzTWF0cml4MyB8fCBwYXJhbWV0ZXJfc3JjLmlzTWF0cml4NCB8fFxuXHRcdFx0XHRcdFx0cGFyYW1ldGVyX3NyYy5pc1ZlY3RvcjIgfHwgcGFyYW1ldGVyX3NyYy5pc1ZlY3RvcjMgfHwgcGFyYW1ldGVyX3NyYy5pc1ZlY3RvcjQgfHxcblx0XHRcdFx0XHRcdHBhcmFtZXRlcl9zcmMuaXNUZXh0dXJlICkgKSB7XG5cblx0XHRcdFx0XHRcdHVuaWZvcm1zX2RzdFsgdSBdWyBwIF0gPSBwYXJhbWV0ZXJfc3JjLmNsb25lKCk7XG5cblx0XHRcdFx0XHR9IGVsc2UgaWYgKCBBcnJheS5pc0FycmF5KCBwYXJhbWV0ZXJfc3JjICkgKSB7XG5cblx0XHRcdFx0XHRcdHVuaWZvcm1zX2RzdFsgdSBdWyBwIF0gPSBwYXJhbWV0ZXJfc3JjLnNsaWNlKCk7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHR1bmlmb3Jtc19kc3RbIHUgXVsgcCBdID0gcGFyYW1ldGVyX3NyYztcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHVuaWZvcm1zX2RzdDtcblxuXHRcdH1cblxuXHR9O1xuXG5cdHZhciBhbHBoYW1hcF9mcmFnbWVudCA9IFwiI2lmZGVmIFVTRV9BTFBIQU1BUFxcblxcdGRpZmZ1c2VDb2xvci5hICo9IHRleHR1cmUyRCggYWxwaGFNYXAsIHZVdiApLmc7XFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIGFscGhhbWFwX3BhcnNfZnJhZ21lbnQgPSBcIiNpZmRlZiBVU0VfQUxQSEFNQVBcXG5cXHR1bmlmb3JtIHNhbXBsZXIyRCBhbHBoYU1hcDtcXG4jZW5kaWZcXG5cIjtcblxuXHR2YXIgYWxwaGF0ZXN0X2ZyYWdtZW50ID0gXCIjaWZkZWYgQUxQSEFURVNUXFxuXFx0aWYgKCBkaWZmdXNlQ29sb3IuYSA8IEFMUEhBVEVTVCApIGRpc2NhcmQ7XFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIGFvbWFwX2ZyYWdtZW50ID0gXCIjaWZkZWYgVVNFX0FPTUFQXFxuXFx0ZmxvYXQgYW1iaWVudE9jY2x1c2lvbiA9ICggdGV4dHVyZTJEKCBhb01hcCwgdlV2MiApLnIgLSAxLjAgKSAqIGFvTWFwSW50ZW5zaXR5ICsgMS4wO1xcblxcdHJlZmxlY3RlZExpZ2h0LmluZGlyZWN0RGlmZnVzZSAqPSBhbWJpZW50T2NjbHVzaW9uO1xcblxcdCNpZiBkZWZpbmVkKCBVU0VfRU5WTUFQICkgJiYgZGVmaW5lZCggUEhZU0lDQUwgKVxcblxcdFxcdGZsb2F0IGRvdE5WID0gc2F0dXJhdGUoIGRvdCggZ2VvbWV0cnkubm9ybWFsLCBnZW9tZXRyeS52aWV3RGlyICkgKTtcXG5cXHRcXHRyZWZsZWN0ZWRMaWdodC5pbmRpcmVjdFNwZWN1bGFyICo9IGNvbXB1dGVTcGVjdWxhck9jY2x1c2lvbiggZG90TlYsIGFtYmllbnRPY2NsdXNpb24sIG1hdGVyaWFsLnNwZWN1bGFyUm91Z2huZXNzICk7XFxuXFx0I2VuZGlmXFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIGFvbWFwX3BhcnNfZnJhZ21lbnQgPSBcIiNpZmRlZiBVU0VfQU9NQVBcXG5cXHR1bmlmb3JtIHNhbXBsZXIyRCBhb01hcDtcXG5cXHR1bmlmb3JtIGZsb2F0IGFvTWFwSW50ZW5zaXR5O1xcbiNlbmRpZlwiO1xuXG5cdHZhciBiZWdpbl92ZXJ0ZXggPSBcIlxcbnZlYzMgdHJhbnNmb3JtZWQgPSB2ZWMzKCBwb3NpdGlvbiApO1xcblwiO1xuXG5cdHZhciBiZWdpbm5vcm1hbF92ZXJ0ZXggPSBcIlxcbnZlYzMgb2JqZWN0Tm9ybWFsID0gdmVjMyggbm9ybWFsICk7XFxuXCI7XG5cblx0dmFyIGJzZGZzID0gXCJmbG9hdCBwdW5jdHVhbExpZ2h0SW50ZW5zaXR5VG9JcnJhZGlhbmNlRmFjdG9yKCBjb25zdCBpbiBmbG9hdCBsaWdodERpc3RhbmNlLCBjb25zdCBpbiBmbG9hdCBjdXRvZmZEaXN0YW5jZSwgY29uc3QgaW4gZmxvYXQgZGVjYXlFeHBvbmVudCApIHtcXG5cXHRcXHRpZiggZGVjYXlFeHBvbmVudCA+IDAuMCApIHtcXG4jaWYgZGVmaW5lZCAoIFBIWVNJQ0FMTFlfQ09SUkVDVF9MSUdIVFMgKVxcblxcdFxcdFxcdGZsb2F0IGRpc3RhbmNlRmFsbG9mZiA9IDEuMCAvIG1heCggcG93KCBsaWdodERpc3RhbmNlLCBkZWNheUV4cG9uZW50ICksIDAuMDEgKTtcXG5cXHRcXHRcXHRmbG9hdCBtYXhEaXN0YW5jZUN1dG9mZkZhY3RvciA9IHBvdzIoIHNhdHVyYXRlKCAxLjAgLSBwb3c0KCBsaWdodERpc3RhbmNlIC8gY3V0b2ZmRGlzdGFuY2UgKSApICk7XFxuXFx0XFx0XFx0cmV0dXJuIGRpc3RhbmNlRmFsbG9mZiAqIG1heERpc3RhbmNlQ3V0b2ZmRmFjdG9yO1xcbiNlbHNlXFxuXFx0XFx0XFx0cmV0dXJuIHBvdyggc2F0dXJhdGUoIC1saWdodERpc3RhbmNlIC8gY3V0b2ZmRGlzdGFuY2UgKyAxLjAgKSwgZGVjYXlFeHBvbmVudCApO1xcbiNlbmRpZlxcblxcdFxcdH1cXG5cXHRcXHRyZXR1cm4gMS4wO1xcbn1cXG52ZWMzIEJSREZfRGlmZnVzZV9MYW1iZXJ0KCBjb25zdCBpbiB2ZWMzIGRpZmZ1c2VDb2xvciApIHtcXG5cXHRyZXR1cm4gUkVDSVBST0NBTF9QSSAqIGRpZmZ1c2VDb2xvcjtcXG59XFxudmVjMyBGX1NjaGxpY2soIGNvbnN0IGluIHZlYzMgc3BlY3VsYXJDb2xvciwgY29uc3QgaW4gZmxvYXQgZG90TEggKSB7XFxuXFx0ZmxvYXQgZnJlc25lbCA9IGV4cDIoICggLTUuNTU0NzMgKiBkb3RMSCAtIDYuOTgzMTYgKSAqIGRvdExIICk7XFxuXFx0cmV0dXJuICggMS4wIC0gc3BlY3VsYXJDb2xvciApICogZnJlc25lbCArIHNwZWN1bGFyQ29sb3I7XFxufVxcbmZsb2F0IEdfR0dYX1NtaXRoKCBjb25zdCBpbiBmbG9hdCBhbHBoYSwgY29uc3QgaW4gZmxvYXQgZG90TkwsIGNvbnN0IGluIGZsb2F0IGRvdE5WICkge1xcblxcdGZsb2F0IGEyID0gcG93MiggYWxwaGEgKTtcXG5cXHRmbG9hdCBnbCA9IGRvdE5MICsgc3FydCggYTIgKyAoIDEuMCAtIGEyICkgKiBwb3cyKCBkb3ROTCApICk7XFxuXFx0ZmxvYXQgZ3YgPSBkb3ROViArIHNxcnQoIGEyICsgKCAxLjAgLSBhMiApICogcG93MiggZG90TlYgKSApO1xcblxcdHJldHVybiAxLjAgLyAoIGdsICogZ3YgKTtcXG59XFxuZmxvYXQgR19HR1hfU21pdGhDb3JyZWxhdGVkKCBjb25zdCBpbiBmbG9hdCBhbHBoYSwgY29uc3QgaW4gZmxvYXQgZG90TkwsIGNvbnN0IGluIGZsb2F0IGRvdE5WICkge1xcblxcdGZsb2F0IGEyID0gcG93MiggYWxwaGEgKTtcXG5cXHRmbG9hdCBndiA9IGRvdE5MICogc3FydCggYTIgKyAoIDEuMCAtIGEyICkgKiBwb3cyKCBkb3ROViApICk7XFxuXFx0ZmxvYXQgZ2wgPSBkb3ROViAqIHNxcnQoIGEyICsgKCAxLjAgLSBhMiApICogcG93MiggZG90TkwgKSApO1xcblxcdHJldHVybiAwLjUgLyBtYXgoIGd2ICsgZ2wsIEVQU0lMT04gKTtcXG59XFxuZmxvYXQgRF9HR1goIGNvbnN0IGluIGZsb2F0IGFscGhhLCBjb25zdCBpbiBmbG9hdCBkb3ROSCApIHtcXG5cXHRmbG9hdCBhMiA9IHBvdzIoIGFscGhhICk7XFxuXFx0ZmxvYXQgZGVub20gPSBwb3cyKCBkb3ROSCApICogKCBhMiAtIDEuMCApICsgMS4wO1xcblxcdHJldHVybiBSRUNJUFJPQ0FMX1BJICogYTIgLyBwb3cyKCBkZW5vbSApO1xcbn1cXG52ZWMzIEJSREZfU3BlY3VsYXJfR0dYKCBjb25zdCBpbiBJbmNpZGVudExpZ2h0IGluY2lkZW50TGlnaHQsIGNvbnN0IGluIEdlb21ldHJpY0NvbnRleHQgZ2VvbWV0cnksIGNvbnN0IGluIHZlYzMgc3BlY3VsYXJDb2xvciwgY29uc3QgaW4gZmxvYXQgcm91Z2huZXNzICkge1xcblxcdGZsb2F0IGFscGhhID0gcG93Miggcm91Z2huZXNzICk7XFxuXFx0dmVjMyBoYWxmRGlyID0gbm9ybWFsaXplKCBpbmNpZGVudExpZ2h0LmRpcmVjdGlvbiArIGdlb21ldHJ5LnZpZXdEaXIgKTtcXG5cXHRmbG9hdCBkb3ROTCA9IHNhdHVyYXRlKCBkb3QoIGdlb21ldHJ5Lm5vcm1hbCwgaW5jaWRlbnRMaWdodC5kaXJlY3Rpb24gKSApO1xcblxcdGZsb2F0IGRvdE5WID0gc2F0dXJhdGUoIGRvdCggZ2VvbWV0cnkubm9ybWFsLCBnZW9tZXRyeS52aWV3RGlyICkgKTtcXG5cXHRmbG9hdCBkb3ROSCA9IHNhdHVyYXRlKCBkb3QoIGdlb21ldHJ5Lm5vcm1hbCwgaGFsZkRpciApICk7XFxuXFx0ZmxvYXQgZG90TEggPSBzYXR1cmF0ZSggZG90KCBpbmNpZGVudExpZ2h0LmRpcmVjdGlvbiwgaGFsZkRpciApICk7XFxuXFx0dmVjMyBGID0gRl9TY2hsaWNrKCBzcGVjdWxhckNvbG9yLCBkb3RMSCApO1xcblxcdGZsb2F0IEcgPSBHX0dHWF9TbWl0aENvcnJlbGF0ZWQoIGFscGhhLCBkb3ROTCwgZG90TlYgKTtcXG5cXHRmbG9hdCBEID0gRF9HR1goIGFscGhhLCBkb3ROSCApO1xcblxcdHJldHVybiBGICogKCBHICogRCApO1xcbn1cXG52ZWMyIGx0Y1RleHR1cmVDb29yZHMoIGNvbnN0IGluIEdlb21ldHJpY0NvbnRleHQgZ2VvbWV0cnksIGNvbnN0IGluIGZsb2F0IHJvdWdobmVzcyApIHtcXG5cXHRjb25zdCBmbG9hdCBMVVRfU0laRSAgPSA2NC4wO1xcblxcdGNvbnN0IGZsb2F0IExVVF9TQ0FMRSA9IChMVVRfU0laRSAtIDEuMCkvTFVUX1NJWkU7XFxuXFx0Y29uc3QgZmxvYXQgTFVUX0JJQVMgID0gMC41L0xVVF9TSVpFO1xcblxcdHZlYzMgTiA9IGdlb21ldHJ5Lm5vcm1hbDtcXG5cXHR2ZWMzIFYgPSBnZW9tZXRyeS52aWV3RGlyO1xcblxcdHZlYzMgUCA9IGdlb21ldHJ5LnBvc2l0aW9uO1xcblxcdGZsb2F0IHRoZXRhID0gYWNvcyggZG90KCBOLCBWICkgKTtcXG5cXHR2ZWMyIHV2ID0gdmVjMihcXG5cXHRcXHRzcXJ0KCBzYXR1cmF0ZSggcm91Z2huZXNzICkgKSxcXG5cXHRcXHRzYXR1cmF0ZSggdGhldGEgLyAoIDAuNSAqIFBJICkgKSApO1xcblxcdHV2ID0gdXYgKiBMVVRfU0NBTEUgKyBMVVRfQklBUztcXG5cXHRyZXR1cm4gdXY7XFxufVxcbnZvaWQgY2xpcFF1YWRUb0hvcml6b24oIGlub3V0IHZlYzMgTFs1XSwgb3V0IGludCBuICkge1xcblxcdGludCBjb25maWcgPSAwO1xcblxcdGlmICggTFswXS56ID4gMC4wICkgY29uZmlnICs9IDE7XFxuXFx0aWYgKCBMWzFdLnogPiAwLjAgKSBjb25maWcgKz0gMjtcXG5cXHRpZiAoIExbMl0ueiA+IDAuMCApIGNvbmZpZyArPSA0O1xcblxcdGlmICggTFszXS56ID4gMC4wICkgY29uZmlnICs9IDg7XFxuXFx0biA9IDA7XFxuXFx0aWYgKCBjb25maWcgPT0gMCApIHtcXG5cXHR9IGVsc2UgaWYgKCBjb25maWcgPT0gMSApIHtcXG5cXHRcXHRuID0gMztcXG5cXHRcXHRMWzFdID0gLUxbMV0ueiAqIExbMF0gKyBMWzBdLnogKiBMWzFdO1xcblxcdFxcdExbMl0gPSAtTFszXS56ICogTFswXSArIExbMF0ueiAqIExbM107XFxuXFx0fSBlbHNlIGlmICggY29uZmlnID09IDIgKSB7XFxuXFx0XFx0biA9IDM7XFxuXFx0XFx0TFswXSA9IC1MWzBdLnogKiBMWzFdICsgTFsxXS56ICogTFswXTtcXG5cXHRcXHRMWzJdID0gLUxbMl0ueiAqIExbMV0gKyBMWzFdLnogKiBMWzJdO1xcblxcdH0gZWxzZSBpZiAoIGNvbmZpZyA9PSAzICkge1xcblxcdFxcdG4gPSA0O1xcblxcdFxcdExbMl0gPSAtTFsyXS56ICogTFsxXSArIExbMV0ueiAqIExbMl07XFxuXFx0XFx0TFszXSA9IC1MWzNdLnogKiBMWzBdICsgTFswXS56ICogTFszXTtcXG5cXHR9IGVsc2UgaWYgKCBjb25maWcgPT0gNCApIHtcXG5cXHRcXHRuID0gMztcXG5cXHRcXHRMWzBdID0gLUxbM10ueiAqIExbMl0gKyBMWzJdLnogKiBMWzNdO1xcblxcdFxcdExbMV0gPSAtTFsxXS56ICogTFsyXSArIExbMl0ueiAqIExbMV07XFxuXFx0fSBlbHNlIGlmICggY29uZmlnID09IDUgKSB7XFxuXFx0XFx0biA9IDA7XFxuXFx0fSBlbHNlIGlmICggY29uZmlnID09IDYgKSB7XFxuXFx0XFx0biA9IDQ7XFxuXFx0XFx0TFswXSA9IC1MWzBdLnogKiBMWzFdICsgTFsxXS56ICogTFswXTtcXG5cXHRcXHRMWzNdID0gLUxbM10ueiAqIExbMl0gKyBMWzJdLnogKiBMWzNdO1xcblxcdH0gZWxzZSBpZiAoIGNvbmZpZyA9PSA3ICkge1xcblxcdFxcdG4gPSA1O1xcblxcdFxcdExbNF0gPSAtTFszXS56ICogTFswXSArIExbMF0ueiAqIExbM107XFxuXFx0XFx0TFszXSA9IC1MWzNdLnogKiBMWzJdICsgTFsyXS56ICogTFszXTtcXG5cXHR9IGVsc2UgaWYgKCBjb25maWcgPT0gOCApIHtcXG5cXHRcXHRuID0gMztcXG5cXHRcXHRMWzBdID0gLUxbMF0ueiAqIExbM10gKyBMWzNdLnogKiBMWzBdO1xcblxcdFxcdExbMV0gPSAtTFsyXS56ICogTFszXSArIExbM10ueiAqIExbMl07XFxuXFx0XFx0TFsyXSA9ICBMWzNdO1xcblxcdH0gZWxzZSBpZiAoIGNvbmZpZyA9PSA5ICkge1xcblxcdFxcdG4gPSA0O1xcblxcdFxcdExbMV0gPSAtTFsxXS56ICogTFswXSArIExbMF0ueiAqIExbMV07XFxuXFx0XFx0TFsyXSA9IC1MWzJdLnogKiBMWzNdICsgTFszXS56ICogTFsyXTtcXG5cXHR9IGVsc2UgaWYgKCBjb25maWcgPT0gMTAgKSB7XFxuXFx0XFx0biA9IDA7XFxuXFx0fSBlbHNlIGlmICggY29uZmlnID09IDExICkge1xcblxcdFxcdG4gPSA1O1xcblxcdFxcdExbNF0gPSBMWzNdO1xcblxcdFxcdExbM10gPSAtTFsyXS56ICogTFszXSArIExbM10ueiAqIExbMl07XFxuXFx0XFx0TFsyXSA9IC1MWzJdLnogKiBMWzFdICsgTFsxXS56ICogTFsyXTtcXG5cXHR9IGVsc2UgaWYgKCBjb25maWcgPT0gMTIgKSB7XFxuXFx0XFx0biA9IDQ7XFxuXFx0XFx0TFsxXSA9IC1MWzFdLnogKiBMWzJdICsgTFsyXS56ICogTFsxXTtcXG5cXHRcXHRMWzBdID0gLUxbMF0ueiAqIExbM10gKyBMWzNdLnogKiBMWzBdO1xcblxcdH0gZWxzZSBpZiAoIGNvbmZpZyA9PSAxMyApIHtcXG5cXHRcXHRuID0gNTtcXG5cXHRcXHRMWzRdID0gTFszXTtcXG5cXHRcXHRMWzNdID0gTFsyXTtcXG5cXHRcXHRMWzJdID0gLUxbMV0ueiAqIExbMl0gKyBMWzJdLnogKiBMWzFdO1xcblxcdFxcdExbMV0gPSAtTFsxXS56ICogTFswXSArIExbMF0ueiAqIExbMV07XFxuXFx0fSBlbHNlIGlmICggY29uZmlnID09IDE0ICkge1xcblxcdFxcdG4gPSA1O1xcblxcdFxcdExbNF0gPSAtTFswXS56ICogTFszXSArIExbM10ueiAqIExbMF07XFxuXFx0XFx0TFswXSA9IC1MWzBdLnogKiBMWzFdICsgTFsxXS56ICogTFswXTtcXG5cXHR9IGVsc2UgaWYgKCBjb25maWcgPT0gMTUgKSB7XFxuXFx0XFx0biA9IDQ7XFxuXFx0fVxcblxcdGlmICggbiA9PSAzIClcXG5cXHRcXHRMWzNdID0gTFswXTtcXG5cXHRpZiAoIG4gPT0gNCApXFxuXFx0XFx0TFs0XSA9IExbMF07XFxufVxcbmZsb2F0IGludGVncmF0ZUx0Y0JyZGZPdmVyUmVjdEVkZ2UoIHZlYzMgdjEsIHZlYzMgdjIgKSB7XFxuXFx0ZmxvYXQgY29zVGhldGEgPSBkb3QoIHYxLCB2MiApO1xcblxcdGZsb2F0IHRoZXRhID0gYWNvcyggY29zVGhldGEgKTtcXG5cXHRmbG9hdCByZXMgPSBjcm9zcyggdjEsIHYyICkueiAqICggKCB0aGV0YSA+IDAuMDAxICkgPyB0aGV0YSAvIHNpbiggdGhldGEgKSA6IDEuMCApO1xcblxcdHJldHVybiByZXM7XFxufVxcbnZvaWQgaW5pdFJlY3RQb2ludHMoIGNvbnN0IGluIHZlYzMgcG9zLCBjb25zdCBpbiB2ZWMzIGhhbGZXaWR0aCwgY29uc3QgaW4gdmVjMyBoYWxmSGVpZ2h0LCBvdXQgdmVjMyByZWN0UG9pbnRzWzRdICkge1xcblxcdHJlY3RQb2ludHNbMF0gPSBwb3MgLSBoYWxmV2lkdGggLSBoYWxmSGVpZ2h0O1xcblxcdHJlY3RQb2ludHNbMV0gPSBwb3MgKyBoYWxmV2lkdGggLSBoYWxmSGVpZ2h0O1xcblxcdHJlY3RQb2ludHNbMl0gPSBwb3MgKyBoYWxmV2lkdGggKyBoYWxmSGVpZ2h0O1xcblxcdHJlY3RQb2ludHNbM10gPSBwb3MgLSBoYWxmV2lkdGggKyBoYWxmSGVpZ2h0O1xcbn1cXG52ZWMzIGludGVncmF0ZUx0Y0JyZGZPdmVyUmVjdCggY29uc3QgaW4gR2VvbWV0cmljQ29udGV4dCBnZW9tZXRyeSwgY29uc3QgaW4gbWF0MyBicmRmTWF0LCBjb25zdCBpbiB2ZWMzIHJlY3RQb2ludHNbNF0gKSB7XFxuXFx0dmVjMyBOID0gZ2VvbWV0cnkubm9ybWFsO1xcblxcdHZlYzMgViA9IGdlb21ldHJ5LnZpZXdEaXI7XFxuXFx0dmVjMyBQID0gZ2VvbWV0cnkucG9zaXRpb247XFxuXFx0dmVjMyBUMSwgVDI7XFxuXFx0VDEgPSBub3JtYWxpemUoViAtIE4gKiBkb3QoIFYsIE4gKSk7XFxuXFx0VDIgPSAtIGNyb3NzKCBOLCBUMSApO1xcblxcdG1hdDMgYnJkZldydFN1cmZhY2UgPSBicmRmTWF0ICogdHJhbnNwb3NlKCBtYXQzKCBUMSwgVDIsIE4gKSApO1xcblxcdHZlYzMgY2xpcHBlZFJlY3RbNV07XFxuXFx0Y2xpcHBlZFJlY3RbMF0gPSBicmRmV3J0U3VyZmFjZSAqICggcmVjdFBvaW50c1swXSAtIFAgKTtcXG5cXHRjbGlwcGVkUmVjdFsxXSA9IGJyZGZXcnRTdXJmYWNlICogKCByZWN0UG9pbnRzWzFdIC0gUCApO1xcblxcdGNsaXBwZWRSZWN0WzJdID0gYnJkZldydFN1cmZhY2UgKiAoIHJlY3RQb2ludHNbMl0gLSBQICk7XFxuXFx0Y2xpcHBlZFJlY3RbM10gPSBicmRmV3J0U3VyZmFjZSAqICggcmVjdFBvaW50c1szXSAtIFAgKTtcXG5cXHRpbnQgbjtcXG5cXHRjbGlwUXVhZFRvSG9yaXpvbihjbGlwcGVkUmVjdCwgbik7XFxuXFx0aWYgKCBuID09IDAgKVxcblxcdFxcdHJldHVybiB2ZWMzKCAwLCAwLCAwICk7XFxuXFx0Y2xpcHBlZFJlY3RbMF0gPSBub3JtYWxpemUoIGNsaXBwZWRSZWN0WzBdICk7XFxuXFx0Y2xpcHBlZFJlY3RbMV0gPSBub3JtYWxpemUoIGNsaXBwZWRSZWN0WzFdICk7XFxuXFx0Y2xpcHBlZFJlY3RbMl0gPSBub3JtYWxpemUoIGNsaXBwZWRSZWN0WzJdICk7XFxuXFx0Y2xpcHBlZFJlY3RbM10gPSBub3JtYWxpemUoIGNsaXBwZWRSZWN0WzNdICk7XFxuXFx0Y2xpcHBlZFJlY3RbNF0gPSBub3JtYWxpemUoIGNsaXBwZWRSZWN0WzRdICk7XFxuXFx0ZmxvYXQgc3VtID0gMC4wO1xcblxcdHN1bSArPSBpbnRlZ3JhdGVMdGNCcmRmT3ZlclJlY3RFZGdlKCBjbGlwcGVkUmVjdFswXSwgY2xpcHBlZFJlY3RbMV0gKTtcXG5cXHRzdW0gKz0gaW50ZWdyYXRlTHRjQnJkZk92ZXJSZWN0RWRnZSggY2xpcHBlZFJlY3RbMV0sIGNsaXBwZWRSZWN0WzJdICk7XFxuXFx0c3VtICs9IGludGVncmF0ZUx0Y0JyZGZPdmVyUmVjdEVkZ2UoIGNsaXBwZWRSZWN0WzJdLCBjbGlwcGVkUmVjdFszXSApO1xcblxcdGlmIChuID49IDQpXFxuXFx0XFx0c3VtICs9IGludGVncmF0ZUx0Y0JyZGZPdmVyUmVjdEVkZ2UoIGNsaXBwZWRSZWN0WzNdLCBjbGlwcGVkUmVjdFs0XSApO1xcblxcdGlmIChuID09IDUpXFxuXFx0XFx0c3VtICs9IGludGVncmF0ZUx0Y0JyZGZPdmVyUmVjdEVkZ2UoIGNsaXBwZWRSZWN0WzRdLCBjbGlwcGVkUmVjdFswXSApO1xcblxcdHN1bSA9IG1heCggMC4wLCBzdW0gKTtcXG5cXHR2ZWMzIExvX2kgPSB2ZWMzKCBzdW0sIHN1bSwgc3VtICk7XFxuXFx0cmV0dXJuIExvX2k7XFxufVxcbnZlYzMgUmVjdF9BcmVhX0xpZ2h0X1NwZWN1bGFyX1JlZmxlY3RhbmNlKFxcblxcdFxcdGNvbnN0IGluIEdlb21ldHJpY0NvbnRleHQgZ2VvbWV0cnksXFxuXFx0XFx0Y29uc3QgaW4gdmVjMyBsaWdodFBvcywgY29uc3QgaW4gdmVjMyBsaWdodEhhbGZXaWR0aCwgY29uc3QgaW4gdmVjMyBsaWdodEhhbGZIZWlnaHQsXFxuXFx0XFx0Y29uc3QgaW4gZmxvYXQgcm91Z2huZXNzLFxcblxcdFxcdGNvbnN0IGluIHNhbXBsZXIyRCBsdGNNYXQsIGNvbnN0IGluIHNhbXBsZXIyRCBsdGNNYWcgKSB7XFxuXFx0dmVjMyByZWN0UG9pbnRzWzRdO1xcblxcdGluaXRSZWN0UG9pbnRzKCBsaWdodFBvcywgbGlnaHRIYWxmV2lkdGgsIGxpZ2h0SGFsZkhlaWdodCwgcmVjdFBvaW50cyApO1xcblxcdHZlYzIgdXYgPSBsdGNUZXh0dXJlQ29vcmRzKCBnZW9tZXRyeSwgcm91Z2huZXNzICk7XFxuXFx0dmVjNCBicmRmTHRjQXBwcm94UGFyYW1zLCB0O1xcblxcdGJyZGZMdGNBcHByb3hQYXJhbXMgPSB0ZXh0dXJlMkQoIGx0Y01hdCwgdXYgKTtcXG5cXHR0ID0gdGV4dHVyZTJEKCBsdGNNYXQsIHV2ICk7XFxuXFx0ZmxvYXQgYnJkZkx0Y1NjYWxhciA9IHRleHR1cmUyRCggbHRjTWFnLCB1diApLmE7XFxuXFx0bWF0MyBicmRmTHRjQXBwcm94TWF0ID0gbWF0MyhcXG5cXHRcXHR2ZWMzKCAgIDEsICAgMCwgdC55ICksXFxuXFx0XFx0dmVjMyggICAwLCB0LnosICAgMCApLFxcblxcdFxcdHZlYzMoIHQudywgICAwLCB0LnggKVxcblxcdCk7XFxuXFx0dmVjMyBzcGVjdWxhclJlZmxlY3RhbmNlID0gaW50ZWdyYXRlTHRjQnJkZk92ZXJSZWN0KCBnZW9tZXRyeSwgYnJkZkx0Y0FwcHJveE1hdCwgcmVjdFBvaW50cyApO1xcblxcdHNwZWN1bGFyUmVmbGVjdGFuY2UgKj0gYnJkZkx0Y1NjYWxhcjtcXG5cXHRyZXR1cm4gc3BlY3VsYXJSZWZsZWN0YW5jZTtcXG59XFxudmVjMyBSZWN0X0FyZWFfTGlnaHRfRGlmZnVzZV9SZWZsZWN0YW5jZShcXG5cXHRcXHRjb25zdCBpbiBHZW9tZXRyaWNDb250ZXh0IGdlb21ldHJ5LFxcblxcdFxcdGNvbnN0IGluIHZlYzMgbGlnaHRQb3MsIGNvbnN0IGluIHZlYzMgbGlnaHRIYWxmV2lkdGgsIGNvbnN0IGluIHZlYzMgbGlnaHRIYWxmSGVpZ2h0ICkge1xcblxcdHZlYzMgcmVjdFBvaW50c1s0XTtcXG5cXHRpbml0UmVjdFBvaW50cyggbGlnaHRQb3MsIGxpZ2h0SGFsZldpZHRoLCBsaWdodEhhbGZIZWlnaHQsIHJlY3RQb2ludHMgKTtcXG5cXHRtYXQzIGRpZmZ1c2VCcmRmTWF0ID0gbWF0MygxKTtcXG5cXHR2ZWMzIGRpZmZ1c2VSZWZsZWN0YW5jZSA9IGludGVncmF0ZUx0Y0JyZGZPdmVyUmVjdCggZ2VvbWV0cnksIGRpZmZ1c2VCcmRmTWF0LCByZWN0UG9pbnRzICk7XFxuXFx0cmV0dXJuIGRpZmZ1c2VSZWZsZWN0YW5jZTtcXG59XFxudmVjMyBCUkRGX1NwZWN1bGFyX0dHWF9FbnZpcm9ubWVudCggY29uc3QgaW4gR2VvbWV0cmljQ29udGV4dCBnZW9tZXRyeSwgY29uc3QgaW4gdmVjMyBzcGVjdWxhckNvbG9yLCBjb25zdCBpbiBmbG9hdCByb3VnaG5lc3MgKSB7XFxuXFx0ZmxvYXQgZG90TlYgPSBzYXR1cmF0ZSggZG90KCBnZW9tZXRyeS5ub3JtYWwsIGdlb21ldHJ5LnZpZXdEaXIgKSApO1xcblxcdGNvbnN0IHZlYzQgYzAgPSB2ZWM0KCAtIDEsIC0gMC4wMjc1LCAtIDAuNTcyLCAwLjAyMiApO1xcblxcdGNvbnN0IHZlYzQgYzEgPSB2ZWM0KCAxLCAwLjA0MjUsIDEuMDQsIC0gMC4wNCApO1xcblxcdHZlYzQgciA9IHJvdWdobmVzcyAqIGMwICsgYzE7XFxuXFx0ZmxvYXQgYTAwNCA9IG1pbiggci54ICogci54LCBleHAyKCAtIDkuMjggKiBkb3ROViApICkgKiByLnggKyByLnk7XFxuXFx0dmVjMiBBQiA9IHZlYzIoIC0xLjA0LCAxLjA0ICkgKiBhMDA0ICsgci56dztcXG5cXHRyZXR1cm4gc3BlY3VsYXJDb2xvciAqIEFCLnggKyBBQi55O1xcbn1cXG5mbG9hdCBHX0JsaW5uUGhvbmdfSW1wbGljaXQoICkge1xcblxcdHJldHVybiAwLjI1O1xcbn1cXG5mbG9hdCBEX0JsaW5uUGhvbmcoIGNvbnN0IGluIGZsb2F0IHNoaW5pbmVzcywgY29uc3QgaW4gZmxvYXQgZG90TkggKSB7XFxuXFx0cmV0dXJuIFJFQ0lQUk9DQUxfUEkgKiAoIHNoaW5pbmVzcyAqIDAuNSArIDEuMCApICogcG93KCBkb3ROSCwgc2hpbmluZXNzICk7XFxufVxcbnZlYzMgQlJERl9TcGVjdWxhcl9CbGlublBob25nKCBjb25zdCBpbiBJbmNpZGVudExpZ2h0IGluY2lkZW50TGlnaHQsIGNvbnN0IGluIEdlb21ldHJpY0NvbnRleHQgZ2VvbWV0cnksIGNvbnN0IGluIHZlYzMgc3BlY3VsYXJDb2xvciwgY29uc3QgaW4gZmxvYXQgc2hpbmluZXNzICkge1xcblxcdHZlYzMgaGFsZkRpciA9IG5vcm1hbGl6ZSggaW5jaWRlbnRMaWdodC5kaXJlY3Rpb24gKyBnZW9tZXRyeS52aWV3RGlyICk7XFxuXFx0ZmxvYXQgZG90TkggPSBzYXR1cmF0ZSggZG90KCBnZW9tZXRyeS5ub3JtYWwsIGhhbGZEaXIgKSApO1xcblxcdGZsb2F0IGRvdExIID0gc2F0dXJhdGUoIGRvdCggaW5jaWRlbnRMaWdodC5kaXJlY3Rpb24sIGhhbGZEaXIgKSApO1xcblxcdHZlYzMgRiA9IEZfU2NobGljayggc3BlY3VsYXJDb2xvciwgZG90TEggKTtcXG5cXHRmbG9hdCBHID0gR19CbGlublBob25nX0ltcGxpY2l0KCApO1xcblxcdGZsb2F0IEQgPSBEX0JsaW5uUGhvbmcoIHNoaW5pbmVzcywgZG90TkggKTtcXG5cXHRyZXR1cm4gRiAqICggRyAqIEQgKTtcXG59XFxuZmxvYXQgR0dYUm91Z2huZXNzVG9CbGlubkV4cG9uZW50KCBjb25zdCBpbiBmbG9hdCBnZ3hSb3VnaG5lc3MgKSB7XFxuXFx0cmV0dXJuICggMi4wIC8gcG93MiggZ2d4Um91Z2huZXNzICsgMC4wMDAxICkgLSAyLjAgKTtcXG59XFxuZmxvYXQgQmxpbm5FeHBvbmVudFRvR0dYUm91Z2huZXNzKCBjb25zdCBpbiBmbG9hdCBibGlubkV4cG9uZW50ICkge1xcblxcdHJldHVybiBzcXJ0KCAyLjAgLyAoIGJsaW5uRXhwb25lbnQgKyAyLjAgKSApO1xcbn1cXG5cIjtcblxuXHR2YXIgYnVtcG1hcF9wYXJzX2ZyYWdtZW50ID0gXCIjaWZkZWYgVVNFX0JVTVBNQVBcXG5cXHR1bmlmb3JtIHNhbXBsZXIyRCBidW1wTWFwO1xcblxcdHVuaWZvcm0gZmxvYXQgYnVtcFNjYWxlO1xcblxcdHZlYzIgZEhkeHlfZndkKCkge1xcblxcdFxcdHZlYzIgZFNUZHggPSBkRmR4KCB2VXYgKTtcXG5cXHRcXHR2ZWMyIGRTVGR5ID0gZEZkeSggdlV2ICk7XFxuXFx0XFx0ZmxvYXQgSGxsID0gYnVtcFNjYWxlICogdGV4dHVyZTJEKCBidW1wTWFwLCB2VXYgKS54O1xcblxcdFxcdGZsb2F0IGRCeCA9IGJ1bXBTY2FsZSAqIHRleHR1cmUyRCggYnVtcE1hcCwgdlV2ICsgZFNUZHggKS54IC0gSGxsO1xcblxcdFxcdGZsb2F0IGRCeSA9IGJ1bXBTY2FsZSAqIHRleHR1cmUyRCggYnVtcE1hcCwgdlV2ICsgZFNUZHkgKS54IC0gSGxsO1xcblxcdFxcdHJldHVybiB2ZWMyKCBkQngsIGRCeSApO1xcblxcdH1cXG5cXHR2ZWMzIHBlcnR1cmJOb3JtYWxBcmIoIHZlYzMgc3VyZl9wb3MsIHZlYzMgc3VyZl9ub3JtLCB2ZWMyIGRIZHh5ICkge1xcblxcdFxcdHZlYzMgdlNpZ21hWCA9IGRGZHgoIHN1cmZfcG9zICk7XFxuXFx0XFx0dmVjMyB2U2lnbWFZID0gZEZkeSggc3VyZl9wb3MgKTtcXG5cXHRcXHR2ZWMzIHZOID0gc3VyZl9ub3JtO1xcblxcdFxcdHZlYzMgUjEgPSBjcm9zcyggdlNpZ21hWSwgdk4gKTtcXG5cXHRcXHR2ZWMzIFIyID0gY3Jvc3MoIHZOLCB2U2lnbWFYICk7XFxuXFx0XFx0ZmxvYXQgZkRldCA9IGRvdCggdlNpZ21hWCwgUjEgKTtcXG5cXHRcXHR2ZWMzIHZHcmFkID0gc2lnbiggZkRldCApICogKCBkSGR4eS54ICogUjEgKyBkSGR4eS55ICogUjIgKTtcXG5cXHRcXHRyZXR1cm4gbm9ybWFsaXplKCBhYnMoIGZEZXQgKSAqIHN1cmZfbm9ybSAtIHZHcmFkICk7XFxuXFx0fVxcbiNlbmRpZlxcblwiO1xuXG5cdHZhciBjbGlwcGluZ19wbGFuZXNfZnJhZ21lbnQgPSBcIiNpZiBOVU1fQ0xJUFBJTkdfUExBTkVTID4gMFxcblxcdGZvciAoIGludCBpID0gMDsgaSA8IFVOSU9OX0NMSVBQSU5HX1BMQU5FUzsgKysgaSApIHtcXG5cXHRcXHR2ZWM0IHBsYW5lID0gY2xpcHBpbmdQbGFuZXNbIGkgXTtcXG5cXHRcXHRpZiAoIGRvdCggdlZpZXdQb3NpdGlvbiwgcGxhbmUueHl6ICkgPiBwbGFuZS53ICkgZGlzY2FyZDtcXG5cXHR9XFxuXFx0XFx0XFxuXFx0I2lmIFVOSU9OX0NMSVBQSU5HX1BMQU5FUyA8IE5VTV9DTElQUElOR19QTEFORVNcXG5cXHRcXHRib29sIGNsaXBwZWQgPSB0cnVlO1xcblxcdFxcdGZvciAoIGludCBpID0gVU5JT05fQ0xJUFBJTkdfUExBTkVTOyBpIDwgTlVNX0NMSVBQSU5HX1BMQU5FUzsgKysgaSApIHtcXG5cXHRcXHRcXHR2ZWM0IHBsYW5lID0gY2xpcHBpbmdQbGFuZXNbIGkgXTtcXG5cXHRcXHRcXHRjbGlwcGVkID0gKCBkb3QoIHZWaWV3UG9zaXRpb24sIHBsYW5lLnh5eiApID4gcGxhbmUudyApICYmIGNsaXBwZWQ7XFxuXFx0XFx0fVxcblxcdFxcdGlmICggY2xpcHBlZCApIGRpc2NhcmQ7XFxuXFx0XFxuXFx0I2VuZGlmXFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIGNsaXBwaW5nX3BsYW5lc19wYXJzX2ZyYWdtZW50ID0gXCIjaWYgTlVNX0NMSVBQSU5HX1BMQU5FUyA+IDBcXG5cXHQjaWYgISBkZWZpbmVkKCBQSFlTSUNBTCApICYmICEgZGVmaW5lZCggUEhPTkcgKVxcblxcdFxcdHZhcnlpbmcgdmVjMyB2Vmlld1Bvc2l0aW9uO1xcblxcdCNlbmRpZlxcblxcdHVuaWZvcm0gdmVjNCBjbGlwcGluZ1BsYW5lc1sgTlVNX0NMSVBQSU5HX1BMQU5FUyBdO1xcbiNlbmRpZlxcblwiO1xuXG5cdHZhciBjbGlwcGluZ19wbGFuZXNfcGFyc192ZXJ0ZXggPSBcIiNpZiBOVU1fQ0xJUFBJTkdfUExBTkVTID4gMCAmJiAhIGRlZmluZWQoIFBIWVNJQ0FMICkgJiYgISBkZWZpbmVkKCBQSE9ORyApXFxuXFx0dmFyeWluZyB2ZWMzIHZWaWV3UG9zaXRpb247XFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIGNsaXBwaW5nX3BsYW5lc192ZXJ0ZXggPSBcIiNpZiBOVU1fQ0xJUFBJTkdfUExBTkVTID4gMCAmJiAhIGRlZmluZWQoIFBIWVNJQ0FMICkgJiYgISBkZWZpbmVkKCBQSE9ORyApXFxuXFx0dlZpZXdQb3NpdGlvbiA9IC0gbXZQb3NpdGlvbi54eXo7XFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIGNvbG9yX2ZyYWdtZW50ID0gXCIjaWZkZWYgVVNFX0NPTE9SXFxuXFx0ZGlmZnVzZUNvbG9yLnJnYiAqPSB2Q29sb3I7XFxuI2VuZGlmXCI7XG5cblx0dmFyIGNvbG9yX3BhcnNfZnJhZ21lbnQgPSBcIiNpZmRlZiBVU0VfQ09MT1JcXG5cXHR2YXJ5aW5nIHZlYzMgdkNvbG9yO1xcbiNlbmRpZlxcblwiO1xuXG5cdHZhciBjb2xvcl9wYXJzX3ZlcnRleCA9IFwiI2lmZGVmIFVTRV9DT0xPUlxcblxcdHZhcnlpbmcgdmVjMyB2Q29sb3I7XFxuI2VuZGlmXCI7XG5cblx0dmFyIGNvbG9yX3ZlcnRleCA9IFwiI2lmZGVmIFVTRV9DT0xPUlxcblxcdHZDb2xvci54eXogPSBjb2xvci54eXo7XFxuI2VuZGlmXCI7XG5cblx0dmFyIGNvbW1vbiA9IFwiI2RlZmluZSBQSSAzLjE0MTU5MjY1MzU5XFxuI2RlZmluZSBQSTIgNi4yODMxODUzMDcxOFxcbiNkZWZpbmUgUElfSEFMRiAxLjU3MDc5NjMyNjc5NDlcXG4jZGVmaW5lIFJFQ0lQUk9DQUxfUEkgMC4zMTgzMDk4ODYxOFxcbiNkZWZpbmUgUkVDSVBST0NBTF9QSTIgMC4xNTkxNTQ5NFxcbiNkZWZpbmUgTE9HMiAxLjQ0MjY5NVxcbiNkZWZpbmUgRVBTSUxPTiAxZS02XFxuI2RlZmluZSBzYXR1cmF0ZShhKSBjbGFtcCggYSwgMC4wLCAxLjAgKVxcbiNkZWZpbmUgd2hpdGVDb21wbGltZW50KGEpICggMS4wIC0gc2F0dXJhdGUoIGEgKSApXFxuZmxvYXQgcG93MiggY29uc3QgaW4gZmxvYXQgeCApIHsgcmV0dXJuIHgqeDsgfVxcbmZsb2F0IHBvdzMoIGNvbnN0IGluIGZsb2F0IHggKSB7IHJldHVybiB4KngqeDsgfVxcbmZsb2F0IHBvdzQoIGNvbnN0IGluIGZsb2F0IHggKSB7IGZsb2F0IHgyID0geCp4OyByZXR1cm4geDIqeDI7IH1cXG5mbG9hdCBhdmVyYWdlKCBjb25zdCBpbiB2ZWMzIGNvbG9yICkgeyByZXR1cm4gZG90KCBjb2xvciwgdmVjMyggMC4zMzMzICkgKTsgfVxcbmhpZ2hwIGZsb2F0IHJhbmQoIGNvbnN0IGluIHZlYzIgdXYgKSB7XFxuXFx0Y29uc3QgaGlnaHAgZmxvYXQgYSA9IDEyLjk4OTgsIGIgPSA3OC4yMzMsIGMgPSA0Mzc1OC41NDUzO1xcblxcdGhpZ2hwIGZsb2F0IGR0ID0gZG90KCB1di54eSwgdmVjMiggYSxiICkgKSwgc24gPSBtb2QoIGR0LCBQSSApO1xcblxcdHJldHVybiBmcmFjdChzaW4oc24pICogYyk7XFxufVxcbnN0cnVjdCBJbmNpZGVudExpZ2h0IHtcXG5cXHR2ZWMzIGNvbG9yO1xcblxcdHZlYzMgZGlyZWN0aW9uO1xcblxcdGJvb2wgdmlzaWJsZTtcXG59O1xcbnN0cnVjdCBSZWZsZWN0ZWRMaWdodCB7XFxuXFx0dmVjMyBkaXJlY3REaWZmdXNlO1xcblxcdHZlYzMgZGlyZWN0U3BlY3VsYXI7XFxuXFx0dmVjMyBpbmRpcmVjdERpZmZ1c2U7XFxuXFx0dmVjMyBpbmRpcmVjdFNwZWN1bGFyO1xcbn07XFxuc3RydWN0IEdlb21ldHJpY0NvbnRleHQge1xcblxcdHZlYzMgcG9zaXRpb247XFxuXFx0dmVjMyBub3JtYWw7XFxuXFx0dmVjMyB2aWV3RGlyO1xcbn07XFxudmVjMyB0cmFuc2Zvcm1EaXJlY3Rpb24oIGluIHZlYzMgZGlyLCBpbiBtYXQ0IG1hdHJpeCApIHtcXG5cXHRyZXR1cm4gbm9ybWFsaXplKCAoIG1hdHJpeCAqIHZlYzQoIGRpciwgMC4wICkgKS54eXogKTtcXG59XFxudmVjMyBpbnZlcnNlVHJhbnNmb3JtRGlyZWN0aW9uKCBpbiB2ZWMzIGRpciwgaW4gbWF0NCBtYXRyaXggKSB7XFxuXFx0cmV0dXJuIG5vcm1hbGl6ZSggKCB2ZWM0KCBkaXIsIDAuMCApICogbWF0cml4ICkueHl6ICk7XFxufVxcbnZlYzMgcHJvamVjdE9uUGxhbmUoaW4gdmVjMyBwb2ludCwgaW4gdmVjMyBwb2ludE9uUGxhbmUsIGluIHZlYzMgcGxhbmVOb3JtYWwgKSB7XFxuXFx0ZmxvYXQgZGlzdGFuY2UgPSBkb3QoIHBsYW5lTm9ybWFsLCBwb2ludCAtIHBvaW50T25QbGFuZSApO1xcblxcdHJldHVybiAtIGRpc3RhbmNlICogcGxhbmVOb3JtYWwgKyBwb2ludDtcXG59XFxuZmxvYXQgc2lkZU9mUGxhbmUoIGluIHZlYzMgcG9pbnQsIGluIHZlYzMgcG9pbnRPblBsYW5lLCBpbiB2ZWMzIHBsYW5lTm9ybWFsICkge1xcblxcdHJldHVybiBzaWduKCBkb3QoIHBvaW50IC0gcG9pbnRPblBsYW5lLCBwbGFuZU5vcm1hbCApICk7XFxufVxcbnZlYzMgbGluZVBsYW5lSW50ZXJzZWN0KCBpbiB2ZWMzIHBvaW50T25MaW5lLCBpbiB2ZWMzIGxpbmVEaXJlY3Rpb24sIGluIHZlYzMgcG9pbnRPblBsYW5lLCBpbiB2ZWMzIHBsYW5lTm9ybWFsICkge1xcblxcdHJldHVybiBsaW5lRGlyZWN0aW9uICogKCBkb3QoIHBsYW5lTm9ybWFsLCBwb2ludE9uUGxhbmUgLSBwb2ludE9uTGluZSApIC8gZG90KCBwbGFuZU5vcm1hbCwgbGluZURpcmVjdGlvbiApICkgKyBwb2ludE9uTGluZTtcXG59XFxubWF0MyB0cmFuc3Bvc2UoIGNvbnN0IGluIG1hdDMgdiApIHtcXG5cXHRtYXQzIHRtcDtcXG5cXHR0bXBbMF0gPSB2ZWMzKHZbMF0ueCwgdlsxXS54LCB2WzJdLngpO1xcblxcdHRtcFsxXSA9IHZlYzModlswXS55LCB2WzFdLnksIHZbMl0ueSk7XFxuXFx0dG1wWzJdID0gdmVjMyh2WzBdLnosIHZbMV0ueiwgdlsyXS56KTtcXG5cXHRyZXR1cm4gdG1wO1xcbn1cXG5cIjtcblxuXHR2YXIgY3ViZV91dl9yZWZsZWN0aW9uX2ZyYWdtZW50ID0gXCIjaWZkZWYgRU5WTUFQX1RZUEVfQ1VCRV9VVlxcbiNkZWZpbmUgY3ViZVVWX3RleHR1cmVTaXplICgxMDI0LjApXFxuaW50IGdldEZhY2VGcm9tRGlyZWN0aW9uKHZlYzMgZGlyZWN0aW9uKSB7XFxuXFx0dmVjMyBhYnNEaXJlY3Rpb24gPSBhYnMoZGlyZWN0aW9uKTtcXG5cXHRpbnQgZmFjZSA9IC0xO1xcblxcdGlmKCBhYnNEaXJlY3Rpb24ueCA+IGFic0RpcmVjdGlvbi56ICkge1xcblxcdFxcdGlmKGFic0RpcmVjdGlvbi54ID4gYWJzRGlyZWN0aW9uLnkgKVxcblxcdFxcdFxcdGZhY2UgPSBkaXJlY3Rpb24ueCA+IDAuMCA/IDAgOiAzO1xcblxcdFxcdGVsc2VcXG5cXHRcXHRcXHRmYWNlID0gZGlyZWN0aW9uLnkgPiAwLjAgPyAxIDogNDtcXG5cXHR9XFxuXFx0ZWxzZSB7XFxuXFx0XFx0aWYoYWJzRGlyZWN0aW9uLnogPiBhYnNEaXJlY3Rpb24ueSApXFxuXFx0XFx0XFx0ZmFjZSA9IGRpcmVjdGlvbi56ID4gMC4wID8gMiA6IDU7XFxuXFx0XFx0ZWxzZVxcblxcdFxcdFxcdGZhY2UgPSBkaXJlY3Rpb24ueSA+IDAuMCA/IDEgOiA0O1xcblxcdH1cXG5cXHRyZXR1cm4gZmFjZTtcXG59XFxuI2RlZmluZSBjdWJlVVZfbWF4TG9kczEgIChsb2cyKGN1YmVVVl90ZXh0dXJlU2l6ZSowLjI1KSAtIDEuMClcXG4jZGVmaW5lIGN1YmVVVl9yYW5nZUNsYW1wIChleHAyKCg2LjAgLSAxLjApICogMi4wKSlcXG52ZWMyIE1pcExldmVsSW5mbyggdmVjMyB2ZWMsIGZsb2F0IHJvdWdobmVzc0xldmVsLCBmbG9hdCByb3VnaG5lc3MgKSB7XFxuXFx0ZmxvYXQgc2NhbGUgPSBleHAyKGN1YmVVVl9tYXhMb2RzMSAtIHJvdWdobmVzc0xldmVsKTtcXG5cXHRmbG9hdCBkeFJvdWdobmVzcyA9IGRGZHgocm91Z2huZXNzKTtcXG5cXHRmbG9hdCBkeVJvdWdobmVzcyA9IGRGZHkocm91Z2huZXNzKTtcXG5cXHR2ZWMzIGR4ID0gZEZkeCggdmVjICogc2NhbGUgKiBkeFJvdWdobmVzcyApO1xcblxcdHZlYzMgZHkgPSBkRmR5KCB2ZWMgKiBzY2FsZSAqIGR5Um91Z2huZXNzICk7XFxuXFx0ZmxvYXQgZCA9IG1heCggZG90KCBkeCwgZHggKSwgZG90KCBkeSwgZHkgKSApO1xcblxcdGQgPSBjbGFtcChkLCAxLjAsIGN1YmVVVl9yYW5nZUNsYW1wKTtcXG5cXHRmbG9hdCBtaXBMZXZlbCA9IDAuNSAqIGxvZzIoZCk7XFxuXFx0cmV0dXJuIHZlYzIoZmxvb3IobWlwTGV2ZWwpLCBmcmFjdChtaXBMZXZlbCkpO1xcbn1cXG4jZGVmaW5lIGN1YmVVVl9tYXhMb2RzMiAobG9nMihjdWJlVVZfdGV4dHVyZVNpemUqMC4yNSkgLSAyLjApXFxuI2RlZmluZSBjdWJlVVZfcmNwVGV4dHVyZVNpemUgKDEuMCAvIGN1YmVVVl90ZXh0dXJlU2l6ZSlcXG52ZWMyIGdldEN1YmVVVih2ZWMzIGRpcmVjdGlvbiwgZmxvYXQgcm91Z2huZXNzTGV2ZWwsIGZsb2F0IG1pcExldmVsKSB7XFxuXFx0bWlwTGV2ZWwgPSByb3VnaG5lc3NMZXZlbCA+IGN1YmVVVl9tYXhMb2RzMiAtIDMuMCA/IDAuMCA6IG1pcExldmVsO1xcblxcdGZsb2F0IGEgPSAxNi4wICogY3ViZVVWX3JjcFRleHR1cmVTaXplO1xcblxcdHZlYzIgZXhwMl9wYWNrZWQgPSBleHAyKCB2ZWMyKCByb3VnaG5lc3NMZXZlbCwgbWlwTGV2ZWwgKSApO1xcblxcdHZlYzIgcmNwX2V4cDJfcGFja2VkID0gdmVjMiggMS4wICkgLyBleHAyX3BhY2tlZDtcXG5cXHRmbG9hdCBwb3dTY2FsZSA9IGV4cDJfcGFja2VkLnggKiBleHAyX3BhY2tlZC55O1xcblxcdGZsb2F0IHNjYWxlID0gcmNwX2V4cDJfcGFja2VkLnggKiByY3BfZXhwMl9wYWNrZWQueSAqIDAuMjU7XFxuXFx0ZmxvYXQgbWlwT2Zmc2V0ID0gMC43NSooMS4wIC0gcmNwX2V4cDJfcGFja2VkLnkpICogcmNwX2V4cDJfcGFja2VkLng7XFxuXFx0Ym9vbCBiUmVzID0gbWlwTGV2ZWwgPT0gMC4wO1xcblxcdHNjYWxlID0gIGJSZXMgJiYgKHNjYWxlIDwgYSkgPyBhIDogc2NhbGU7XFxuXFx0dmVjMyByO1xcblxcdHZlYzIgb2Zmc2V0O1xcblxcdGludCBmYWNlID0gZ2V0RmFjZUZyb21EaXJlY3Rpb24oZGlyZWN0aW9uKTtcXG5cXHRmbG9hdCByY3BQb3dTY2FsZSA9IDEuMCAvIHBvd1NjYWxlO1xcblxcdGlmKCBmYWNlID09IDApIHtcXG5cXHRcXHRyID0gdmVjMyhkaXJlY3Rpb24ueCwgLWRpcmVjdGlvbi56LCBkaXJlY3Rpb24ueSk7XFxuXFx0XFx0b2Zmc2V0ID0gdmVjMigwLjArbWlwT2Zmc2V0LDAuNzUgKiByY3BQb3dTY2FsZSk7XFxuXFx0XFx0b2Zmc2V0LnkgPSBiUmVzICYmIChvZmZzZXQueSA8IDIuMCphKSA/ICBhIDogb2Zmc2V0Lnk7XFxuXFx0fVxcblxcdGVsc2UgaWYoIGZhY2UgPT0gMSkge1xcblxcdFxcdHIgPSB2ZWMzKGRpcmVjdGlvbi55LCBkaXJlY3Rpb24ueCwgZGlyZWN0aW9uLnopO1xcblxcdFxcdG9mZnNldCA9IHZlYzIoc2NhbGUrbWlwT2Zmc2V0LCAwLjc1ICogcmNwUG93U2NhbGUpO1xcblxcdFxcdG9mZnNldC55ID0gYlJlcyAmJiAob2Zmc2V0LnkgPCAyLjAqYSkgPyAgYSA6IG9mZnNldC55O1xcblxcdH1cXG5cXHRlbHNlIGlmKCBmYWNlID09IDIpIHtcXG5cXHRcXHRyID0gdmVjMyhkaXJlY3Rpb24ueiwgZGlyZWN0aW9uLngsIGRpcmVjdGlvbi55KTtcXG5cXHRcXHRvZmZzZXQgPSB2ZWMyKDIuMCpzY2FsZSttaXBPZmZzZXQsIDAuNzUgKiByY3BQb3dTY2FsZSk7XFxuXFx0XFx0b2Zmc2V0LnkgPSBiUmVzICYmIChvZmZzZXQueSA8IDIuMCphKSA/ICBhIDogb2Zmc2V0Lnk7XFxuXFx0fVxcblxcdGVsc2UgaWYoIGZhY2UgPT0gMykge1xcblxcdFxcdHIgPSB2ZWMzKGRpcmVjdGlvbi54LCBkaXJlY3Rpb24ueiwgZGlyZWN0aW9uLnkpO1xcblxcdFxcdG9mZnNldCA9IHZlYzIoMC4wK21pcE9mZnNldCwwLjUgKiByY3BQb3dTY2FsZSk7XFxuXFx0XFx0b2Zmc2V0LnkgPSBiUmVzICYmIChvZmZzZXQueSA8IDIuMCphKSA/ICAwLjAgOiBvZmZzZXQueTtcXG5cXHR9XFxuXFx0ZWxzZSBpZiggZmFjZSA9PSA0KSB7XFxuXFx0XFx0ciA9IHZlYzMoZGlyZWN0aW9uLnksIGRpcmVjdGlvbi54LCAtZGlyZWN0aW9uLnopO1xcblxcdFxcdG9mZnNldCA9IHZlYzIoc2NhbGUrbWlwT2Zmc2V0LCAwLjUgKiByY3BQb3dTY2FsZSk7XFxuXFx0XFx0b2Zmc2V0LnkgPSBiUmVzICYmIChvZmZzZXQueSA8IDIuMCphKSA/ICAwLjAgOiBvZmZzZXQueTtcXG5cXHR9XFxuXFx0ZWxzZSB7XFxuXFx0XFx0ciA9IHZlYzMoZGlyZWN0aW9uLnosIC1kaXJlY3Rpb24ueCwgZGlyZWN0aW9uLnkpO1xcblxcdFxcdG9mZnNldCA9IHZlYzIoMi4wKnNjYWxlK21pcE9mZnNldCwgMC41ICogcmNwUG93U2NhbGUpO1xcblxcdFxcdG9mZnNldC55ID0gYlJlcyAmJiAob2Zmc2V0LnkgPCAyLjAqYSkgPyAgMC4wIDogb2Zmc2V0Lnk7XFxuXFx0fVxcblxcdHIgPSBub3JtYWxpemUocik7XFxuXFx0ZmxvYXQgdGV4ZWxPZmZzZXQgPSAwLjUgKiBjdWJlVVZfcmNwVGV4dHVyZVNpemU7XFxuXFx0dmVjMiBzID0gKCByLnl6IC8gYWJzKCByLnggKSArIHZlYzIoIDEuMCApICkgKiAwLjU7XFxuXFx0dmVjMiBiYXNlID0gb2Zmc2V0ICsgdmVjMiggdGV4ZWxPZmZzZXQgKTtcXG5cXHRyZXR1cm4gYmFzZSArIHMgKiAoIHNjYWxlIC0gMi4wICogdGV4ZWxPZmZzZXQgKTtcXG59XFxuI2RlZmluZSBjdWJlVVZfbWF4TG9kczMgKGxvZzIoY3ViZVVWX3RleHR1cmVTaXplKjAuMjUpIC0gMy4wKVxcbnZlYzQgdGV4dHVyZUN1YmVVVih2ZWMzIHJlZmxlY3RlZERpcmVjdGlvbiwgZmxvYXQgcm91Z2huZXNzICkge1xcblxcdGZsb2F0IHJvdWdobmVzc1ZhbCA9IHJvdWdobmVzcyogY3ViZVVWX21heExvZHMzO1xcblxcdGZsb2F0IHIxID0gZmxvb3Iocm91Z2huZXNzVmFsKTtcXG5cXHRmbG9hdCByMiA9IHIxICsgMS4wO1xcblxcdGZsb2F0IHQgPSBmcmFjdChyb3VnaG5lc3NWYWwpO1xcblxcdHZlYzIgbWlwSW5mbyA9IE1pcExldmVsSW5mbyhyZWZsZWN0ZWREaXJlY3Rpb24sIHIxLCByb3VnaG5lc3MpO1xcblxcdGZsb2F0IHMgPSBtaXBJbmZvLnk7XFxuXFx0ZmxvYXQgbGV2ZWwwID0gbWlwSW5mby54O1xcblxcdGZsb2F0IGxldmVsMSA9IGxldmVsMCArIDEuMDtcXG5cXHRsZXZlbDEgPSBsZXZlbDEgPiA1LjAgPyA1LjAgOiBsZXZlbDE7XFxuXFx0bGV2ZWwwICs9IG1pbiggZmxvb3IoIHMgKyAwLjUgKSwgNS4wICk7XFxuXFx0dmVjMiB1dl8xMCA9IGdldEN1YmVVVihyZWZsZWN0ZWREaXJlY3Rpb24sIHIxLCBsZXZlbDApO1xcblxcdHZlYzQgY29sb3IxMCA9IGVudk1hcFRleGVsVG9MaW5lYXIodGV4dHVyZTJEKGVudk1hcCwgdXZfMTApKTtcXG5cXHR2ZWMyIHV2XzIwID0gZ2V0Q3ViZVVWKHJlZmxlY3RlZERpcmVjdGlvbiwgcjIsIGxldmVsMCk7XFxuXFx0dmVjNCBjb2xvcjIwID0gZW52TWFwVGV4ZWxUb0xpbmVhcih0ZXh0dXJlMkQoZW52TWFwLCB1dl8yMCkpO1xcblxcdHZlYzQgcmVzdWx0ID0gbWl4KGNvbG9yMTAsIGNvbG9yMjAsIHQpO1xcblxcdHJldHVybiB2ZWM0KHJlc3VsdC5yZ2IsIDEuMCk7XFxufVxcbiNlbmRpZlxcblwiO1xuXG5cdHZhciBkZWZhdWx0bm9ybWFsX3ZlcnRleCA9IFwiI2lmZGVmIEZMSVBfU0lERURcXG5cXHRvYmplY3ROb3JtYWwgPSAtb2JqZWN0Tm9ybWFsO1xcbiNlbmRpZlxcbnZlYzMgdHJhbnNmb3JtZWROb3JtYWwgPSBub3JtYWxNYXRyaXggKiBvYmplY3ROb3JtYWw7XFxuXCI7XG5cblx0dmFyIGRpc3BsYWNlbWVudG1hcF9wYXJzX3ZlcnRleCA9IFwiI2lmZGVmIFVTRV9ESVNQTEFDRU1FTlRNQVBcXG5cXHR1bmlmb3JtIHNhbXBsZXIyRCBkaXNwbGFjZW1lbnRNYXA7XFxuXFx0dW5pZm9ybSBmbG9hdCBkaXNwbGFjZW1lbnRTY2FsZTtcXG5cXHR1bmlmb3JtIGZsb2F0IGRpc3BsYWNlbWVudEJpYXM7XFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIGRpc3BsYWNlbWVudG1hcF92ZXJ0ZXggPSBcIiNpZmRlZiBVU0VfRElTUExBQ0VNRU5UTUFQXFxuXFx0dHJhbnNmb3JtZWQgKz0gbm9ybWFsICogKCB0ZXh0dXJlMkQoIGRpc3BsYWNlbWVudE1hcCwgdXYgKS54ICogZGlzcGxhY2VtZW50U2NhbGUgKyBkaXNwbGFjZW1lbnRCaWFzICk7XFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIGVtaXNzaXZlbWFwX2ZyYWdtZW50ID0gXCIjaWZkZWYgVVNFX0VNSVNTSVZFTUFQXFxuXFx0dmVjNCBlbWlzc2l2ZUNvbG9yID0gdGV4dHVyZTJEKCBlbWlzc2l2ZU1hcCwgdlV2ICk7XFxuXFx0ZW1pc3NpdmVDb2xvci5yZ2IgPSBlbWlzc2l2ZU1hcFRleGVsVG9MaW5lYXIoIGVtaXNzaXZlQ29sb3IgKS5yZ2I7XFxuXFx0dG90YWxFbWlzc2l2ZVJhZGlhbmNlICo9IGVtaXNzaXZlQ29sb3IucmdiO1xcbiNlbmRpZlxcblwiO1xuXG5cdHZhciBlbWlzc2l2ZW1hcF9wYXJzX2ZyYWdtZW50ID0gXCIjaWZkZWYgVVNFX0VNSVNTSVZFTUFQXFxuXFx0dW5pZm9ybSBzYW1wbGVyMkQgZW1pc3NpdmVNYXA7XFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIGVuY29kaW5nc19mcmFnbWVudCA9IFwiICBnbF9GcmFnQ29sb3IgPSBsaW5lYXJUb091dHB1dFRleGVsKCBnbF9GcmFnQ29sb3IgKTtcXG5cIjtcblxuXHR2YXIgZW5jb2RpbmdzX3BhcnNfZnJhZ21lbnQgPSBcIlxcbnZlYzQgTGluZWFyVG9MaW5lYXIoIGluIHZlYzQgdmFsdWUgKSB7XFxuICByZXR1cm4gdmFsdWU7XFxufVxcbnZlYzQgR2FtbWFUb0xpbmVhciggaW4gdmVjNCB2YWx1ZSwgaW4gZmxvYXQgZ2FtbWFGYWN0b3IgKSB7XFxuICByZXR1cm4gdmVjNCggcG93KCB2YWx1ZS54eXosIHZlYzMoIGdhbW1hRmFjdG9yICkgKSwgdmFsdWUudyApO1xcbn1cXG52ZWM0IExpbmVhclRvR2FtbWEoIGluIHZlYzQgdmFsdWUsIGluIGZsb2F0IGdhbW1hRmFjdG9yICkge1xcbiAgcmV0dXJuIHZlYzQoIHBvdyggdmFsdWUueHl6LCB2ZWMzKCAxLjAgLyBnYW1tYUZhY3RvciApICksIHZhbHVlLncgKTtcXG59XFxudmVjNCBzUkdCVG9MaW5lYXIoIGluIHZlYzQgdmFsdWUgKSB7XFxuICByZXR1cm4gdmVjNCggbWl4KCBwb3coIHZhbHVlLnJnYiAqIDAuOTQ3ODY3Mjk4NiArIHZlYzMoIDAuMDUyMTMyNzAxNCApLCB2ZWMzKCAyLjQgKSApLCB2YWx1ZS5yZ2IgKiAwLjA3NzM5OTM4MDgsIHZlYzMoIGxlc3NUaGFuRXF1YWwoIHZhbHVlLnJnYiwgdmVjMyggMC4wNDA0NSApICkgKSApLCB2YWx1ZS53ICk7XFxufVxcbnZlYzQgTGluZWFyVG9zUkdCKCBpbiB2ZWM0IHZhbHVlICkge1xcbiAgcmV0dXJuIHZlYzQoIG1peCggcG93KCB2YWx1ZS5yZ2IsIHZlYzMoIDAuNDE2NjYgKSApICogMS4wNTUgLSB2ZWMzKCAwLjA1NSApLCB2YWx1ZS5yZ2IgKiAxMi45MiwgdmVjMyggbGVzc1RoYW5FcXVhbCggdmFsdWUucmdiLCB2ZWMzKCAwLjAwMzEzMDggKSApICkgKSwgdmFsdWUudyApO1xcbn1cXG52ZWM0IFJHQkVUb0xpbmVhciggaW4gdmVjNCB2YWx1ZSApIHtcXG4gIHJldHVybiB2ZWM0KCB2YWx1ZS5yZ2IgKiBleHAyKCB2YWx1ZS5hICogMjU1LjAgLSAxMjguMCApLCAxLjAgKTtcXG59XFxudmVjNCBMaW5lYXJUb1JHQkUoIGluIHZlYzQgdmFsdWUgKSB7XFxuICBmbG9hdCBtYXhDb21wb25lbnQgPSBtYXgoIG1heCggdmFsdWUuciwgdmFsdWUuZyApLCB2YWx1ZS5iICk7XFxuICBmbG9hdCBmRXhwID0gY2xhbXAoIGNlaWwoIGxvZzIoIG1heENvbXBvbmVudCApICksIC0xMjguMCwgMTI3LjAgKTtcXG4gIHJldHVybiB2ZWM0KCB2YWx1ZS5yZ2IgLyBleHAyKCBmRXhwICksICggZkV4cCArIDEyOC4wICkgLyAyNTUuMCApO1xcbn1cXG52ZWM0IFJHQk1Ub0xpbmVhciggaW4gdmVjNCB2YWx1ZSwgaW4gZmxvYXQgbWF4UmFuZ2UgKSB7XFxuICByZXR1cm4gdmVjNCggdmFsdWUueHl6ICogdmFsdWUudyAqIG1heFJhbmdlLCAxLjAgKTtcXG59XFxudmVjNCBMaW5lYXJUb1JHQk0oIGluIHZlYzQgdmFsdWUsIGluIGZsb2F0IG1heFJhbmdlICkge1xcbiAgZmxvYXQgbWF4UkdCID0gbWF4KCB2YWx1ZS54LCBtYXgoIHZhbHVlLmcsIHZhbHVlLmIgKSApO1xcbiAgZmxvYXQgTSAgICAgID0gY2xhbXAoIG1heFJHQiAvIG1heFJhbmdlLCAwLjAsIDEuMCApO1xcbiAgTSAgICAgICAgICAgID0gY2VpbCggTSAqIDI1NS4wICkgLyAyNTUuMDtcXG4gIHJldHVybiB2ZWM0KCB2YWx1ZS5yZ2IgLyAoIE0gKiBtYXhSYW5nZSApLCBNICk7XFxufVxcbnZlYzQgUkdCRFRvTGluZWFyKCBpbiB2ZWM0IHZhbHVlLCBpbiBmbG9hdCBtYXhSYW5nZSApIHtcXG4gICAgcmV0dXJuIHZlYzQoIHZhbHVlLnJnYiAqICggKCBtYXhSYW5nZSAvIDI1NS4wICkgLyB2YWx1ZS5hICksIDEuMCApO1xcbn1cXG52ZWM0IExpbmVhclRvUkdCRCggaW4gdmVjNCB2YWx1ZSwgaW4gZmxvYXQgbWF4UmFuZ2UgKSB7XFxuICAgIGZsb2F0IG1heFJHQiA9IG1heCggdmFsdWUueCwgbWF4KCB2YWx1ZS5nLCB2YWx1ZS5iICkgKTtcXG4gICAgZmxvYXQgRCAgICAgID0gbWF4KCBtYXhSYW5nZSAvIG1heFJHQiwgMS4wICk7XFxuICAgIEQgICAgICAgICAgICA9IG1pbiggZmxvb3IoIEQgKSAvIDI1NS4wLCAxLjAgKTtcXG4gICAgcmV0dXJuIHZlYzQoIHZhbHVlLnJnYiAqICggRCAqICggMjU1LjAgLyBtYXhSYW5nZSApICksIEQgKTtcXG59XFxuY29uc3QgbWF0MyBjTG9nTHV2TSA9IG1hdDMoIDAuMjIwOSwgMC4zMzkwLCAwLjQxODQsIDAuMTEzOCwgMC42NzgwLCAwLjczMTksIDAuMDEwMiwgMC4xMTMwLCAwLjI5NjkgKTtcXG52ZWM0IExpbmVhclRvTG9nTHV2KCBpbiB2ZWM0IHZhbHVlICkgIHtcXG4gIHZlYzMgWHBfWV9YWVpwID0gdmFsdWUucmdiICogY0xvZ0x1dk07XFxuICBYcF9ZX1hZWnAgPSBtYXgoWHBfWV9YWVpwLCB2ZWMzKDFlLTYsIDFlLTYsIDFlLTYpKTtcXG4gIHZlYzQgdlJlc3VsdDtcXG4gIHZSZXN1bHQueHkgPSBYcF9ZX1hZWnAueHkgLyBYcF9ZX1hZWnAuejtcXG4gIGZsb2F0IExlID0gMi4wICogbG9nMihYcF9ZX1hZWnAueSkgKyAxMjcuMDtcXG4gIHZSZXN1bHQudyA9IGZyYWN0KExlKTtcXG4gIHZSZXN1bHQueiA9IChMZSAtIChmbG9vcih2UmVzdWx0LncqMjU1LjApKS8yNTUuMCkvMjU1LjA7XFxuICByZXR1cm4gdlJlc3VsdDtcXG59XFxuY29uc3QgbWF0MyBjTG9nTHV2SW52ZXJzZU0gPSBtYXQzKCA2LjAwMTQsIC0yLjcwMDgsIC0xLjc5OTYsIC0xLjMzMjAsIDMuMTAyOSwgLTUuNzcyMSwgMC4zMDA4LCAtMS4wODgyLCA1LjYyNjggKTtcXG52ZWM0IExvZ0x1dlRvTGluZWFyKCBpbiB2ZWM0IHZhbHVlICkge1xcbiAgZmxvYXQgTGUgPSB2YWx1ZS56ICogMjU1LjAgKyB2YWx1ZS53O1xcbiAgdmVjMyBYcF9ZX1hZWnA7XFxuICBYcF9ZX1hZWnAueSA9IGV4cDIoKExlIC0gMTI3LjApIC8gMi4wKTtcXG4gIFhwX1lfWFlacC56ID0gWHBfWV9YWVpwLnkgLyB2YWx1ZS55O1xcbiAgWHBfWV9YWVpwLnggPSB2YWx1ZS54ICogWHBfWV9YWVpwLno7XFxuICB2ZWMzIHZSR0IgPSBYcF9ZX1hZWnAucmdiICogY0xvZ0x1dkludmVyc2VNO1xcbiAgcmV0dXJuIHZlYzQoIG1heCh2UkdCLCAwLjApLCAxLjAgKTtcXG59XFxuXCI7XG5cblx0dmFyIGVudm1hcF9mcmFnbWVudCA9IFwiI2lmZGVmIFVTRV9FTlZNQVBcXG5cXHQjaWYgZGVmaW5lZCggVVNFX0JVTVBNQVAgKSB8fCBkZWZpbmVkKCBVU0VfTk9STUFMTUFQICkgfHwgZGVmaW5lZCggUEhPTkcgKVxcblxcdFxcdHZlYzMgY2FtZXJhVG9WZXJ0ZXggPSBub3JtYWxpemUoIHZXb3JsZFBvc2l0aW9uIC0gY2FtZXJhUG9zaXRpb24gKTtcXG5cXHRcXHR2ZWMzIHdvcmxkTm9ybWFsID0gaW52ZXJzZVRyYW5zZm9ybURpcmVjdGlvbiggbm9ybWFsLCB2aWV3TWF0cml4ICk7XFxuXFx0XFx0I2lmZGVmIEVOVk1BUF9NT0RFX1JFRkxFQ1RJT05cXG5cXHRcXHRcXHR2ZWMzIHJlZmxlY3RWZWMgPSByZWZsZWN0KCBjYW1lcmFUb1ZlcnRleCwgd29ybGROb3JtYWwgKTtcXG5cXHRcXHQjZWxzZVxcblxcdFxcdFxcdHZlYzMgcmVmbGVjdFZlYyA9IHJlZnJhY3QoIGNhbWVyYVRvVmVydGV4LCB3b3JsZE5vcm1hbCwgcmVmcmFjdGlvblJhdGlvICk7XFxuXFx0XFx0I2VuZGlmXFxuXFx0I2Vsc2VcXG5cXHRcXHR2ZWMzIHJlZmxlY3RWZWMgPSB2UmVmbGVjdDtcXG5cXHQjZW5kaWZcXG5cXHQjaWZkZWYgRU5WTUFQX1RZUEVfQ1VCRVxcblxcdFxcdHZlYzQgZW52Q29sb3IgPSB0ZXh0dXJlQ3ViZSggZW52TWFwLCBmbGlwTm9ybWFsICogdmVjMyggZmxpcEVudk1hcCAqIHJlZmxlY3RWZWMueCwgcmVmbGVjdFZlYy55eiApICk7XFxuXFx0I2VsaWYgZGVmaW5lZCggRU5WTUFQX1RZUEVfRVFVSVJFQyApXFxuXFx0XFx0dmVjMiBzYW1wbGVVVjtcXG5cXHRcXHRzYW1wbGVVVi55ID0gc2F0dXJhdGUoIGZsaXBOb3JtYWwgKiByZWZsZWN0VmVjLnkgKiAwLjUgKyAwLjUgKTtcXG5cXHRcXHRzYW1wbGVVVi54ID0gYXRhbiggZmxpcE5vcm1hbCAqIHJlZmxlY3RWZWMueiwgZmxpcE5vcm1hbCAqIHJlZmxlY3RWZWMueCApICogUkVDSVBST0NBTF9QSTIgKyAwLjU7XFxuXFx0XFx0dmVjNCBlbnZDb2xvciA9IHRleHR1cmUyRCggZW52TWFwLCBzYW1wbGVVViApO1xcblxcdCNlbGlmIGRlZmluZWQoIEVOVk1BUF9UWVBFX1NQSEVSRSApXFxuXFx0XFx0dmVjMyByZWZsZWN0VmlldyA9IGZsaXBOb3JtYWwgKiBub3JtYWxpemUoICggdmlld01hdHJpeCAqIHZlYzQoIHJlZmxlY3RWZWMsIDAuMCApICkueHl6ICsgdmVjMyggMC4wLCAwLjAsIDEuMCApICk7XFxuXFx0XFx0dmVjNCBlbnZDb2xvciA9IHRleHR1cmUyRCggZW52TWFwLCByZWZsZWN0Vmlldy54eSAqIDAuNSArIDAuNSApO1xcblxcdCNlbHNlXFxuXFx0XFx0dmVjNCBlbnZDb2xvciA9IHZlYzQoIDAuMCApO1xcblxcdCNlbmRpZlxcblxcdGVudkNvbG9yID0gZW52TWFwVGV4ZWxUb0xpbmVhciggZW52Q29sb3IgKTtcXG5cXHQjaWZkZWYgRU5WTUFQX0JMRU5ESU5HX01VTFRJUExZXFxuXFx0XFx0b3V0Z29pbmdMaWdodCA9IG1peCggb3V0Z29pbmdMaWdodCwgb3V0Z29pbmdMaWdodCAqIGVudkNvbG9yLnh5eiwgc3BlY3VsYXJTdHJlbmd0aCAqIHJlZmxlY3Rpdml0eSApO1xcblxcdCNlbGlmIGRlZmluZWQoIEVOVk1BUF9CTEVORElOR19NSVggKVxcblxcdFxcdG91dGdvaW5nTGlnaHQgPSBtaXgoIG91dGdvaW5nTGlnaHQsIGVudkNvbG9yLnh5eiwgc3BlY3VsYXJTdHJlbmd0aCAqIHJlZmxlY3Rpdml0eSApO1xcblxcdCNlbGlmIGRlZmluZWQoIEVOVk1BUF9CTEVORElOR19BREQgKVxcblxcdFxcdG91dGdvaW5nTGlnaHQgKz0gZW52Q29sb3IueHl6ICogc3BlY3VsYXJTdHJlbmd0aCAqIHJlZmxlY3Rpdml0eTtcXG5cXHQjZW5kaWZcXG4jZW5kaWZcXG5cIjtcblxuXHR2YXIgZW52bWFwX3BhcnNfZnJhZ21lbnQgPSBcIiNpZiBkZWZpbmVkKCBVU0VfRU5WTUFQICkgfHwgZGVmaW5lZCggUEhZU0lDQUwgKVxcblxcdHVuaWZvcm0gZmxvYXQgcmVmbGVjdGl2aXR5O1xcblxcdHVuaWZvcm0gZmxvYXQgZW52TWFwSW50ZW5zaXR5O1xcbiNlbmRpZlxcbiNpZmRlZiBVU0VfRU5WTUFQXFxuXFx0I2lmICEgZGVmaW5lZCggUEhZU0lDQUwgKSAmJiAoIGRlZmluZWQoIFVTRV9CVU1QTUFQICkgfHwgZGVmaW5lZCggVVNFX05PUk1BTE1BUCApIHx8IGRlZmluZWQoIFBIT05HICkgKVxcblxcdFxcdHZhcnlpbmcgdmVjMyB2V29ybGRQb3NpdGlvbjtcXG5cXHQjZW5kaWZcXG5cXHQjaWZkZWYgRU5WTUFQX1RZUEVfQ1VCRVxcblxcdFxcdHVuaWZvcm0gc2FtcGxlckN1YmUgZW52TWFwO1xcblxcdCNlbHNlXFxuXFx0XFx0dW5pZm9ybSBzYW1wbGVyMkQgZW52TWFwO1xcblxcdCNlbmRpZlxcblxcdHVuaWZvcm0gZmxvYXQgZmxpcEVudk1hcDtcXG5cXHQjaWYgZGVmaW5lZCggVVNFX0JVTVBNQVAgKSB8fCBkZWZpbmVkKCBVU0VfTk9STUFMTUFQICkgfHwgZGVmaW5lZCggUEhPTkcgKSB8fCBkZWZpbmVkKCBQSFlTSUNBTCApXFxuXFx0XFx0dW5pZm9ybSBmbG9hdCByZWZyYWN0aW9uUmF0aW87XFxuXFx0I2Vsc2VcXG5cXHRcXHR2YXJ5aW5nIHZlYzMgdlJlZmxlY3Q7XFxuXFx0I2VuZGlmXFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIGVudm1hcF9wYXJzX3ZlcnRleCA9IFwiI2lmZGVmIFVTRV9FTlZNQVBcXG5cXHQjaWYgZGVmaW5lZCggVVNFX0JVTVBNQVAgKSB8fCBkZWZpbmVkKCBVU0VfTk9STUFMTUFQICkgfHwgZGVmaW5lZCggUEhPTkcgKVxcblxcdFxcdHZhcnlpbmcgdmVjMyB2V29ybGRQb3NpdGlvbjtcXG5cXHQjZWxzZVxcblxcdFxcdHZhcnlpbmcgdmVjMyB2UmVmbGVjdDtcXG5cXHRcXHR1bmlmb3JtIGZsb2F0IHJlZnJhY3Rpb25SYXRpbztcXG5cXHQjZW5kaWZcXG4jZW5kaWZcXG5cIjtcblxuXHR2YXIgZW52bWFwX3ZlcnRleCA9IFwiI2lmZGVmIFVTRV9FTlZNQVBcXG5cXHQjaWYgZGVmaW5lZCggVVNFX0JVTVBNQVAgKSB8fCBkZWZpbmVkKCBVU0VfTk9STUFMTUFQICkgfHwgZGVmaW5lZCggUEhPTkcgKVxcblxcdFxcdHZXb3JsZFBvc2l0aW9uID0gd29ybGRQb3NpdGlvbi54eXo7XFxuXFx0I2Vsc2VcXG5cXHRcXHR2ZWMzIGNhbWVyYVRvVmVydGV4ID0gbm9ybWFsaXplKCB3b3JsZFBvc2l0aW9uLnh5eiAtIGNhbWVyYVBvc2l0aW9uICk7XFxuXFx0XFx0dmVjMyB3b3JsZE5vcm1hbCA9IGludmVyc2VUcmFuc2Zvcm1EaXJlY3Rpb24oIHRyYW5zZm9ybWVkTm9ybWFsLCB2aWV3TWF0cml4ICk7XFxuXFx0XFx0I2lmZGVmIEVOVk1BUF9NT0RFX1JFRkxFQ1RJT05cXG5cXHRcXHRcXHR2UmVmbGVjdCA9IHJlZmxlY3QoIGNhbWVyYVRvVmVydGV4LCB3b3JsZE5vcm1hbCApO1xcblxcdFxcdCNlbHNlXFxuXFx0XFx0XFx0dlJlZmxlY3QgPSByZWZyYWN0KCBjYW1lcmFUb1ZlcnRleCwgd29ybGROb3JtYWwsIHJlZnJhY3Rpb25SYXRpbyApO1xcblxcdFxcdCNlbmRpZlxcblxcdCNlbmRpZlxcbiNlbmRpZlxcblwiO1xuXG5cdHZhciBmb2dfZnJhZ21lbnQgPSBcIiNpZmRlZiBVU0VfRk9HXFxuXFx0I2lmZGVmIFVTRV9MT0dERVBUSEJVRl9FWFRcXG5cXHRcXHRmbG9hdCBkZXB0aCA9IGdsX0ZyYWdEZXB0aEVYVCAvIGdsX0ZyYWdDb29yZC53O1xcblxcdCNlbHNlXFxuXFx0XFx0ZmxvYXQgZGVwdGggPSBnbF9GcmFnQ29vcmQueiAvIGdsX0ZyYWdDb29yZC53O1xcblxcdCNlbmRpZlxcblxcdCNpZmRlZiBGT0dfRVhQMlxcblxcdFxcdGZsb2F0IGZvZ0ZhY3RvciA9IHdoaXRlQ29tcGxpbWVudCggZXhwMiggLSBmb2dEZW5zaXR5ICogZm9nRGVuc2l0eSAqIGRlcHRoICogZGVwdGggKiBMT0cyICkgKTtcXG5cXHQjZWxzZVxcblxcdFxcdGZsb2F0IGZvZ0ZhY3RvciA9IHNtb290aHN0ZXAoIGZvZ05lYXIsIGZvZ0ZhciwgZGVwdGggKTtcXG5cXHQjZW5kaWZcXG5cXHRnbF9GcmFnQ29sb3IucmdiID0gbWl4KCBnbF9GcmFnQ29sb3IucmdiLCBmb2dDb2xvciwgZm9nRmFjdG9yICk7XFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIGZvZ19wYXJzX2ZyYWdtZW50ID0gXCIjaWZkZWYgVVNFX0ZPR1xcblxcdHVuaWZvcm0gdmVjMyBmb2dDb2xvcjtcXG5cXHQjaWZkZWYgRk9HX0VYUDJcXG5cXHRcXHR1bmlmb3JtIGZsb2F0IGZvZ0RlbnNpdHk7XFxuXFx0I2Vsc2VcXG5cXHRcXHR1bmlmb3JtIGZsb2F0IGZvZ05lYXI7XFxuXFx0XFx0dW5pZm9ybSBmbG9hdCBmb2dGYXI7XFxuXFx0I2VuZGlmXFxuI2VuZGlmXCI7XG5cblx0dmFyIGdyYWRpZW50bWFwX3BhcnNfZnJhZ21lbnQgPSBcIiNpZmRlZiBUT09OXFxuXFx0dW5pZm9ybSBzYW1wbGVyMkQgZ3JhZGllbnRNYXA7XFxuXFx0dmVjMyBnZXRHcmFkaWVudElycmFkaWFuY2UoIHZlYzMgbm9ybWFsLCB2ZWMzIGxpZ2h0RGlyZWN0aW9uICkge1xcblxcdFxcdGZsb2F0IGRvdE5MID0gZG90KCBub3JtYWwsIGxpZ2h0RGlyZWN0aW9uICk7XFxuXFx0XFx0dmVjMiBjb29yZCA9IHZlYzIoIGRvdE5MICogMC41ICsgMC41LCAwLjAgKTtcXG5cXHRcXHQjaWZkZWYgVVNFX0dSQURJRU5UTUFQXFxuXFx0XFx0XFx0cmV0dXJuIHRleHR1cmUyRCggZ3JhZGllbnRNYXAsIGNvb3JkICkucmdiO1xcblxcdFxcdCNlbHNlXFxuXFx0XFx0XFx0cmV0dXJuICggY29vcmQueCA8IDAuNyApID8gdmVjMyggMC43ICkgOiB2ZWMzKCAxLjAgKTtcXG5cXHRcXHQjZW5kaWZcXG5cXHR9XFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIGxpZ2h0bWFwX2ZyYWdtZW50ID0gXCIjaWZkZWYgVVNFX0xJR0hUTUFQXFxuXFx0cmVmbGVjdGVkTGlnaHQuaW5kaXJlY3REaWZmdXNlICs9IFBJICogdGV4dHVyZTJEKCBsaWdodE1hcCwgdlV2MiApLnh5eiAqIGxpZ2h0TWFwSW50ZW5zaXR5O1xcbiNlbmRpZlxcblwiO1xuXG5cdHZhciBsaWdodG1hcF9wYXJzX2ZyYWdtZW50ID0gXCIjaWZkZWYgVVNFX0xJR0hUTUFQXFxuXFx0dW5pZm9ybSBzYW1wbGVyMkQgbGlnaHRNYXA7XFxuXFx0dW5pZm9ybSBmbG9hdCBsaWdodE1hcEludGVuc2l0eTtcXG4jZW5kaWZcIjtcblxuXHR2YXIgbGlnaHRzX2xhbWJlcnRfdmVydGV4ID0gXCJ2ZWMzIGRpZmZ1c2UgPSB2ZWMzKCAxLjAgKTtcXG5HZW9tZXRyaWNDb250ZXh0IGdlb21ldHJ5O1xcbmdlb21ldHJ5LnBvc2l0aW9uID0gbXZQb3NpdGlvbi54eXo7XFxuZ2VvbWV0cnkubm9ybWFsID0gbm9ybWFsaXplKCB0cmFuc2Zvcm1lZE5vcm1hbCApO1xcbmdlb21ldHJ5LnZpZXdEaXIgPSBub3JtYWxpemUoIC1tdlBvc2l0aW9uLnh5eiApO1xcbkdlb21ldHJpY0NvbnRleHQgYmFja0dlb21ldHJ5O1xcbmJhY2tHZW9tZXRyeS5wb3NpdGlvbiA9IGdlb21ldHJ5LnBvc2l0aW9uO1xcbmJhY2tHZW9tZXRyeS5ub3JtYWwgPSAtZ2VvbWV0cnkubm9ybWFsO1xcbmJhY2tHZW9tZXRyeS52aWV3RGlyID0gZ2VvbWV0cnkudmlld0RpcjtcXG52TGlnaHRGcm9udCA9IHZlYzMoIDAuMCApO1xcbiNpZmRlZiBET1VCTEVfU0lERURcXG5cXHR2TGlnaHRCYWNrID0gdmVjMyggMC4wICk7XFxuI2VuZGlmXFxuSW5jaWRlbnRMaWdodCBkaXJlY3RMaWdodDtcXG5mbG9hdCBkb3ROTDtcXG52ZWMzIGRpcmVjdExpZ2h0Q29sb3JfRGlmZnVzZTtcXG4jaWYgTlVNX1BPSU5UX0xJR0hUUyA+IDBcXG5cXHRmb3IgKCBpbnQgaSA9IDA7IGkgPCBOVU1fUE9JTlRfTElHSFRTOyBpICsrICkge1xcblxcdFxcdGdldFBvaW50RGlyZWN0TGlnaHRJcnJhZGlhbmNlKCBwb2ludExpZ2h0c1sgaSBdLCBnZW9tZXRyeSwgZGlyZWN0TGlnaHQgKTtcXG5cXHRcXHRkb3ROTCA9IGRvdCggZ2VvbWV0cnkubm9ybWFsLCBkaXJlY3RMaWdodC5kaXJlY3Rpb24gKTtcXG5cXHRcXHRkaXJlY3RMaWdodENvbG9yX0RpZmZ1c2UgPSBQSSAqIGRpcmVjdExpZ2h0LmNvbG9yO1xcblxcdFxcdHZMaWdodEZyb250ICs9IHNhdHVyYXRlKCBkb3ROTCApICogZGlyZWN0TGlnaHRDb2xvcl9EaWZmdXNlO1xcblxcdFxcdCNpZmRlZiBET1VCTEVfU0lERURcXG5cXHRcXHRcXHR2TGlnaHRCYWNrICs9IHNhdHVyYXRlKCAtZG90TkwgKSAqIGRpcmVjdExpZ2h0Q29sb3JfRGlmZnVzZTtcXG5cXHRcXHQjZW5kaWZcXG5cXHR9XFxuI2VuZGlmXFxuI2lmIE5VTV9TUE9UX0xJR0hUUyA+IDBcXG5cXHRmb3IgKCBpbnQgaSA9IDA7IGkgPCBOVU1fU1BPVF9MSUdIVFM7IGkgKysgKSB7XFxuXFx0XFx0Z2V0U3BvdERpcmVjdExpZ2h0SXJyYWRpYW5jZSggc3BvdExpZ2h0c1sgaSBdLCBnZW9tZXRyeSwgZGlyZWN0TGlnaHQgKTtcXG5cXHRcXHRkb3ROTCA9IGRvdCggZ2VvbWV0cnkubm9ybWFsLCBkaXJlY3RMaWdodC5kaXJlY3Rpb24gKTtcXG5cXHRcXHRkaXJlY3RMaWdodENvbG9yX0RpZmZ1c2UgPSBQSSAqIGRpcmVjdExpZ2h0LmNvbG9yO1xcblxcdFxcdHZMaWdodEZyb250ICs9IHNhdHVyYXRlKCBkb3ROTCApICogZGlyZWN0TGlnaHRDb2xvcl9EaWZmdXNlO1xcblxcdFxcdCNpZmRlZiBET1VCTEVfU0lERURcXG5cXHRcXHRcXHR2TGlnaHRCYWNrICs9IHNhdHVyYXRlKCAtZG90TkwgKSAqIGRpcmVjdExpZ2h0Q29sb3JfRGlmZnVzZTtcXG5cXHRcXHQjZW5kaWZcXG5cXHR9XFxuI2VuZGlmXFxuI2lmIE5VTV9SRUNUX0FSRUFfTElHSFRTID4gMFxcblxcdGZvciAoIGludCBpID0gMDsgaSA8IE5VTV9SRUNUX0FSRUFfTElHSFRTOyBpICsrICkge1xcblxcdH1cXG4jZW5kaWZcXG4jaWYgTlVNX0RJUl9MSUdIVFMgPiAwXFxuXFx0Zm9yICggaW50IGkgPSAwOyBpIDwgTlVNX0RJUl9MSUdIVFM7IGkgKysgKSB7XFxuXFx0XFx0Z2V0RGlyZWN0aW9uYWxEaXJlY3RMaWdodElycmFkaWFuY2UoIGRpcmVjdGlvbmFsTGlnaHRzWyBpIF0sIGdlb21ldHJ5LCBkaXJlY3RMaWdodCApO1xcblxcdFxcdGRvdE5MID0gZG90KCBnZW9tZXRyeS5ub3JtYWwsIGRpcmVjdExpZ2h0LmRpcmVjdGlvbiApO1xcblxcdFxcdGRpcmVjdExpZ2h0Q29sb3JfRGlmZnVzZSA9IFBJICogZGlyZWN0TGlnaHQuY29sb3I7XFxuXFx0XFx0dkxpZ2h0RnJvbnQgKz0gc2F0dXJhdGUoIGRvdE5MICkgKiBkaXJlY3RMaWdodENvbG9yX0RpZmZ1c2U7XFxuXFx0XFx0I2lmZGVmIERPVUJMRV9TSURFRFxcblxcdFxcdFxcdHZMaWdodEJhY2sgKz0gc2F0dXJhdGUoIC1kb3ROTCApICogZGlyZWN0TGlnaHRDb2xvcl9EaWZmdXNlO1xcblxcdFxcdCNlbmRpZlxcblxcdH1cXG4jZW5kaWZcXG4jaWYgTlVNX0hFTUlfTElHSFRTID4gMFxcblxcdGZvciAoIGludCBpID0gMDsgaSA8IE5VTV9IRU1JX0xJR0hUUzsgaSArKyApIHtcXG5cXHRcXHR2TGlnaHRGcm9udCArPSBnZXRIZW1pc3BoZXJlTGlnaHRJcnJhZGlhbmNlKCBoZW1pc3BoZXJlTGlnaHRzWyBpIF0sIGdlb21ldHJ5ICk7XFxuXFx0XFx0I2lmZGVmIERPVUJMRV9TSURFRFxcblxcdFxcdFxcdHZMaWdodEJhY2sgKz0gZ2V0SGVtaXNwaGVyZUxpZ2h0SXJyYWRpYW5jZSggaGVtaXNwaGVyZUxpZ2h0c1sgaSBdLCBiYWNrR2VvbWV0cnkgKTtcXG5cXHRcXHQjZW5kaWZcXG5cXHR9XFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIGxpZ2h0c19wYXJzID0gXCJ1bmlmb3JtIHZlYzMgYW1iaWVudExpZ2h0Q29sb3I7XFxudmVjMyBnZXRBbWJpZW50TGlnaHRJcnJhZGlhbmNlKCBjb25zdCBpbiB2ZWMzIGFtYmllbnRMaWdodENvbG9yICkge1xcblxcdHZlYzMgaXJyYWRpYW5jZSA9IGFtYmllbnRMaWdodENvbG9yO1xcblxcdCNpZm5kZWYgUEhZU0lDQUxMWV9DT1JSRUNUX0xJR0hUU1xcblxcdFxcdGlycmFkaWFuY2UgKj0gUEk7XFxuXFx0I2VuZGlmXFxuXFx0cmV0dXJuIGlycmFkaWFuY2U7XFxufVxcbiNpZiBOVU1fRElSX0xJR0hUUyA+IDBcXG5cXHRzdHJ1Y3QgRGlyZWN0aW9uYWxMaWdodCB7XFxuXFx0XFx0dmVjMyBkaXJlY3Rpb247XFxuXFx0XFx0dmVjMyBjb2xvcjtcXG5cXHRcXHRpbnQgc2hhZG93O1xcblxcdFxcdGZsb2F0IHNoYWRvd0JpYXM7XFxuXFx0XFx0ZmxvYXQgc2hhZG93UmFkaXVzO1xcblxcdFxcdHZlYzIgc2hhZG93TWFwU2l6ZTtcXG5cXHR9O1xcblxcdHVuaWZvcm0gRGlyZWN0aW9uYWxMaWdodCBkaXJlY3Rpb25hbExpZ2h0c1sgTlVNX0RJUl9MSUdIVFMgXTtcXG5cXHR2b2lkIGdldERpcmVjdGlvbmFsRGlyZWN0TGlnaHRJcnJhZGlhbmNlKCBjb25zdCBpbiBEaXJlY3Rpb25hbExpZ2h0IGRpcmVjdGlvbmFsTGlnaHQsIGNvbnN0IGluIEdlb21ldHJpY0NvbnRleHQgZ2VvbWV0cnksIG91dCBJbmNpZGVudExpZ2h0IGRpcmVjdExpZ2h0ICkge1xcblxcdFxcdGRpcmVjdExpZ2h0LmNvbG9yID0gZGlyZWN0aW9uYWxMaWdodC5jb2xvcjtcXG5cXHRcXHRkaXJlY3RMaWdodC5kaXJlY3Rpb24gPSBkaXJlY3Rpb25hbExpZ2h0LmRpcmVjdGlvbjtcXG5cXHRcXHRkaXJlY3RMaWdodC52aXNpYmxlID0gdHJ1ZTtcXG5cXHR9XFxuI2VuZGlmXFxuI2lmIE5VTV9QT0lOVF9MSUdIVFMgPiAwXFxuXFx0c3RydWN0IFBvaW50TGlnaHQge1xcblxcdFxcdHZlYzMgcG9zaXRpb247XFxuXFx0XFx0dmVjMyBjb2xvcjtcXG5cXHRcXHRmbG9hdCBkaXN0YW5jZTtcXG5cXHRcXHRmbG9hdCBkZWNheTtcXG5cXHRcXHRpbnQgc2hhZG93O1xcblxcdFxcdGZsb2F0IHNoYWRvd0JpYXM7XFxuXFx0XFx0ZmxvYXQgc2hhZG93UmFkaXVzO1xcblxcdFxcdHZlYzIgc2hhZG93TWFwU2l6ZTtcXG5cXHR9O1xcblxcdHVuaWZvcm0gUG9pbnRMaWdodCBwb2ludExpZ2h0c1sgTlVNX1BPSU5UX0xJR0hUUyBdO1xcblxcdHZvaWQgZ2V0UG9pbnREaXJlY3RMaWdodElycmFkaWFuY2UoIGNvbnN0IGluIFBvaW50TGlnaHQgcG9pbnRMaWdodCwgY29uc3QgaW4gR2VvbWV0cmljQ29udGV4dCBnZW9tZXRyeSwgb3V0IEluY2lkZW50TGlnaHQgZGlyZWN0TGlnaHQgKSB7XFxuXFx0XFx0dmVjMyBsVmVjdG9yID0gcG9pbnRMaWdodC5wb3NpdGlvbiAtIGdlb21ldHJ5LnBvc2l0aW9uO1xcblxcdFxcdGRpcmVjdExpZ2h0LmRpcmVjdGlvbiA9IG5vcm1hbGl6ZSggbFZlY3RvciApO1xcblxcdFxcdGZsb2F0IGxpZ2h0RGlzdGFuY2UgPSBsZW5ndGgoIGxWZWN0b3IgKTtcXG5cXHRcXHRkaXJlY3RMaWdodC5jb2xvciA9IHBvaW50TGlnaHQuY29sb3I7XFxuXFx0XFx0ZGlyZWN0TGlnaHQuY29sb3IgKj0gcHVuY3R1YWxMaWdodEludGVuc2l0eVRvSXJyYWRpYW5jZUZhY3RvciggbGlnaHREaXN0YW5jZSwgcG9pbnRMaWdodC5kaXN0YW5jZSwgcG9pbnRMaWdodC5kZWNheSApO1xcblxcdFxcdGRpcmVjdExpZ2h0LnZpc2libGUgPSAoIGRpcmVjdExpZ2h0LmNvbG9yICE9IHZlYzMoIDAuMCApICk7XFxuXFx0fVxcbiNlbmRpZlxcbiNpZiBOVU1fU1BPVF9MSUdIVFMgPiAwXFxuXFx0c3RydWN0IFNwb3RMaWdodCB7XFxuXFx0XFx0dmVjMyBwb3NpdGlvbjtcXG5cXHRcXHR2ZWMzIGRpcmVjdGlvbjtcXG5cXHRcXHR2ZWMzIGNvbG9yO1xcblxcdFxcdGZsb2F0IGRpc3RhbmNlO1xcblxcdFxcdGZsb2F0IGRlY2F5O1xcblxcdFxcdGZsb2F0IGNvbmVDb3M7XFxuXFx0XFx0ZmxvYXQgcGVudW1icmFDb3M7XFxuXFx0XFx0aW50IHNoYWRvdztcXG5cXHRcXHRmbG9hdCBzaGFkb3dCaWFzO1xcblxcdFxcdGZsb2F0IHNoYWRvd1JhZGl1cztcXG5cXHRcXHR2ZWMyIHNoYWRvd01hcFNpemU7XFxuXFx0fTtcXG5cXHR1bmlmb3JtIFNwb3RMaWdodCBzcG90TGlnaHRzWyBOVU1fU1BPVF9MSUdIVFMgXTtcXG5cXHR2b2lkIGdldFNwb3REaXJlY3RMaWdodElycmFkaWFuY2UoIGNvbnN0IGluIFNwb3RMaWdodCBzcG90TGlnaHQsIGNvbnN0IGluIEdlb21ldHJpY0NvbnRleHQgZ2VvbWV0cnksIG91dCBJbmNpZGVudExpZ2h0IGRpcmVjdExpZ2h0ICApIHtcXG5cXHRcXHR2ZWMzIGxWZWN0b3IgPSBzcG90TGlnaHQucG9zaXRpb24gLSBnZW9tZXRyeS5wb3NpdGlvbjtcXG5cXHRcXHRkaXJlY3RMaWdodC5kaXJlY3Rpb24gPSBub3JtYWxpemUoIGxWZWN0b3IgKTtcXG5cXHRcXHRmbG9hdCBsaWdodERpc3RhbmNlID0gbGVuZ3RoKCBsVmVjdG9yICk7XFxuXFx0XFx0ZmxvYXQgYW5nbGVDb3MgPSBkb3QoIGRpcmVjdExpZ2h0LmRpcmVjdGlvbiwgc3BvdExpZ2h0LmRpcmVjdGlvbiApO1xcblxcdFxcdGlmICggYW5nbGVDb3MgPiBzcG90TGlnaHQuY29uZUNvcyApIHtcXG5cXHRcXHRcXHRmbG9hdCBzcG90RWZmZWN0ID0gc21vb3Roc3RlcCggc3BvdExpZ2h0LmNvbmVDb3MsIHNwb3RMaWdodC5wZW51bWJyYUNvcywgYW5nbGVDb3MgKTtcXG5cXHRcXHRcXHRkaXJlY3RMaWdodC5jb2xvciA9IHNwb3RMaWdodC5jb2xvcjtcXG5cXHRcXHRcXHRkaXJlY3RMaWdodC5jb2xvciAqPSBzcG90RWZmZWN0ICogcHVuY3R1YWxMaWdodEludGVuc2l0eVRvSXJyYWRpYW5jZUZhY3RvciggbGlnaHREaXN0YW5jZSwgc3BvdExpZ2h0LmRpc3RhbmNlLCBzcG90TGlnaHQuZGVjYXkgKTtcXG5cXHRcXHRcXHRkaXJlY3RMaWdodC52aXNpYmxlID0gdHJ1ZTtcXG5cXHRcXHR9IGVsc2Uge1xcblxcdFxcdFxcdGRpcmVjdExpZ2h0LmNvbG9yID0gdmVjMyggMC4wICk7XFxuXFx0XFx0XFx0ZGlyZWN0TGlnaHQudmlzaWJsZSA9IGZhbHNlO1xcblxcdFxcdH1cXG5cXHR9XFxuI2VuZGlmXFxuI2lmIE5VTV9SRUNUX0FSRUFfTElHSFRTID4gMFxcblxcdHN0cnVjdCBSZWN0QXJlYUxpZ2h0IHtcXG5cXHRcXHR2ZWMzIGNvbG9yO1xcblxcdFxcdHZlYzMgcG9zaXRpb247XFxuXFx0XFx0dmVjMyBoYWxmV2lkdGg7XFxuXFx0XFx0dmVjMyBoYWxmSGVpZ2h0O1xcblxcdH07XFxuXFx0dW5pZm9ybSBzYW1wbGVyMkQgbHRjTWF0O1xcdHVuaWZvcm0gc2FtcGxlcjJEIGx0Y01hZztcXG5cXHR1bmlmb3JtIFJlY3RBcmVhTGlnaHQgcmVjdEFyZWFMaWdodHNbIE5VTV9SRUNUX0FSRUFfTElHSFRTIF07XFxuI2VuZGlmXFxuI2lmIE5VTV9IRU1JX0xJR0hUUyA+IDBcXG5cXHRzdHJ1Y3QgSGVtaXNwaGVyZUxpZ2h0IHtcXG5cXHRcXHR2ZWMzIGRpcmVjdGlvbjtcXG5cXHRcXHR2ZWMzIHNreUNvbG9yO1xcblxcdFxcdHZlYzMgZ3JvdW5kQ29sb3I7XFxuXFx0fTtcXG5cXHR1bmlmb3JtIEhlbWlzcGhlcmVMaWdodCBoZW1pc3BoZXJlTGlnaHRzWyBOVU1fSEVNSV9MSUdIVFMgXTtcXG5cXHR2ZWMzIGdldEhlbWlzcGhlcmVMaWdodElycmFkaWFuY2UoIGNvbnN0IGluIEhlbWlzcGhlcmVMaWdodCBoZW1pTGlnaHQsIGNvbnN0IGluIEdlb21ldHJpY0NvbnRleHQgZ2VvbWV0cnkgKSB7XFxuXFx0XFx0ZmxvYXQgZG90TkwgPSBkb3QoIGdlb21ldHJ5Lm5vcm1hbCwgaGVtaUxpZ2h0LmRpcmVjdGlvbiApO1xcblxcdFxcdGZsb2F0IGhlbWlEaWZmdXNlV2VpZ2h0ID0gMC41ICogZG90TkwgKyAwLjU7XFxuXFx0XFx0dmVjMyBpcnJhZGlhbmNlID0gbWl4KCBoZW1pTGlnaHQuZ3JvdW5kQ29sb3IsIGhlbWlMaWdodC5za3lDb2xvciwgaGVtaURpZmZ1c2VXZWlnaHQgKTtcXG5cXHRcXHQjaWZuZGVmIFBIWVNJQ0FMTFlfQ09SUkVDVF9MSUdIVFNcXG5cXHRcXHRcXHRpcnJhZGlhbmNlICo9IFBJO1xcblxcdFxcdCNlbmRpZlxcblxcdFxcdHJldHVybiBpcnJhZGlhbmNlO1xcblxcdH1cXG4jZW5kaWZcXG4jaWYgZGVmaW5lZCggVVNFX0VOVk1BUCApICYmIGRlZmluZWQoIFBIWVNJQ0FMIClcXG5cXHR2ZWMzIGdldExpZ2h0UHJvYmVJbmRpcmVjdElycmFkaWFuY2UoIGNvbnN0IGluIEdlb21ldHJpY0NvbnRleHQgZ2VvbWV0cnksIGNvbnN0IGluIGludCBtYXhNSVBMZXZlbCApIHtcXG5cXHRcXHR2ZWMzIHdvcmxkTm9ybWFsID0gaW52ZXJzZVRyYW5zZm9ybURpcmVjdGlvbiggZ2VvbWV0cnkubm9ybWFsLCB2aWV3TWF0cml4ICk7XFxuXFx0XFx0I2lmZGVmIEVOVk1BUF9UWVBFX0NVQkVcXG5cXHRcXHRcXHR2ZWMzIHF1ZXJ5VmVjID0gdmVjMyggZmxpcEVudk1hcCAqIHdvcmxkTm9ybWFsLngsIHdvcmxkTm9ybWFsLnl6ICk7XFxuXFx0XFx0XFx0I2lmZGVmIFRFWFRVUkVfTE9EX0VYVFxcblxcdFxcdFxcdFxcdHZlYzQgZW52TWFwQ29sb3IgPSB0ZXh0dXJlQ3ViZUxvZEVYVCggZW52TWFwLCBxdWVyeVZlYywgZmxvYXQoIG1heE1JUExldmVsICkgKTtcXG5cXHRcXHRcXHQjZWxzZVxcblxcdFxcdFxcdFxcdHZlYzQgZW52TWFwQ29sb3IgPSB0ZXh0dXJlQ3ViZSggZW52TWFwLCBxdWVyeVZlYywgZmxvYXQoIG1heE1JUExldmVsICkgKTtcXG5cXHRcXHRcXHQjZW5kaWZcXG5cXHRcXHRcXHRlbnZNYXBDb2xvci5yZ2IgPSBlbnZNYXBUZXhlbFRvTGluZWFyKCBlbnZNYXBDb2xvciApLnJnYjtcXG5cXHRcXHQjZWxpZiBkZWZpbmVkKCBFTlZNQVBfVFlQRV9DVUJFX1VWIClcXG5cXHRcXHRcXHR2ZWMzIHF1ZXJ5VmVjID0gdmVjMyggZmxpcEVudk1hcCAqIHdvcmxkTm9ybWFsLngsIHdvcmxkTm9ybWFsLnl6ICk7XFxuXFx0XFx0XFx0dmVjNCBlbnZNYXBDb2xvciA9IHRleHR1cmVDdWJlVVYoIHF1ZXJ5VmVjLCAxLjAgKTtcXG5cXHRcXHQjZWxzZVxcblxcdFxcdFxcdHZlYzQgZW52TWFwQ29sb3IgPSB2ZWM0KCAwLjAgKTtcXG5cXHRcXHQjZW5kaWZcXG5cXHRcXHRyZXR1cm4gUEkgKiBlbnZNYXBDb2xvci5yZ2IgKiBlbnZNYXBJbnRlbnNpdHk7XFxuXFx0fVxcblxcdGZsb2F0IGdldFNwZWN1bGFyTUlQTGV2ZWwoIGNvbnN0IGluIGZsb2F0IGJsaW5uU2hpbmluZXNzRXhwb25lbnQsIGNvbnN0IGluIGludCBtYXhNSVBMZXZlbCApIHtcXG5cXHRcXHRmbG9hdCBtYXhNSVBMZXZlbFNjYWxhciA9IGZsb2F0KCBtYXhNSVBMZXZlbCApO1xcblxcdFxcdGZsb2F0IGRlc2lyZWRNSVBMZXZlbCA9IG1heE1JUExldmVsU2NhbGFyIC0gMC43OTI0OCAtIDAuNSAqIGxvZzIoIHBvdzIoIGJsaW5uU2hpbmluZXNzRXhwb25lbnQgKSArIDEuMCApO1xcblxcdFxcdHJldHVybiBjbGFtcCggZGVzaXJlZE1JUExldmVsLCAwLjAsIG1heE1JUExldmVsU2NhbGFyICk7XFxuXFx0fVxcblxcdHZlYzMgZ2V0TGlnaHRQcm9iZUluZGlyZWN0UmFkaWFuY2UoIGNvbnN0IGluIEdlb21ldHJpY0NvbnRleHQgZ2VvbWV0cnksIGNvbnN0IGluIGZsb2F0IGJsaW5uU2hpbmluZXNzRXhwb25lbnQsIGNvbnN0IGluIGludCBtYXhNSVBMZXZlbCApIHtcXG5cXHRcXHQjaWZkZWYgRU5WTUFQX01PREVfUkVGTEVDVElPTlxcblxcdFxcdFxcdHZlYzMgcmVmbGVjdFZlYyA9IHJlZmxlY3QoIC1nZW9tZXRyeS52aWV3RGlyLCBnZW9tZXRyeS5ub3JtYWwgKTtcXG5cXHRcXHQjZWxzZVxcblxcdFxcdFxcdHZlYzMgcmVmbGVjdFZlYyA9IHJlZnJhY3QoIC1nZW9tZXRyeS52aWV3RGlyLCBnZW9tZXRyeS5ub3JtYWwsIHJlZnJhY3Rpb25SYXRpbyApO1xcblxcdFxcdCNlbmRpZlxcblxcdFxcdHJlZmxlY3RWZWMgPSBpbnZlcnNlVHJhbnNmb3JtRGlyZWN0aW9uKCByZWZsZWN0VmVjLCB2aWV3TWF0cml4ICk7XFxuXFx0XFx0ZmxvYXQgc3BlY3VsYXJNSVBMZXZlbCA9IGdldFNwZWN1bGFyTUlQTGV2ZWwoIGJsaW5uU2hpbmluZXNzRXhwb25lbnQsIG1heE1JUExldmVsICk7XFxuXFx0XFx0I2lmZGVmIEVOVk1BUF9UWVBFX0NVQkVcXG5cXHRcXHRcXHR2ZWMzIHF1ZXJ5UmVmbGVjdFZlYyA9IHZlYzMoIGZsaXBFbnZNYXAgKiByZWZsZWN0VmVjLngsIHJlZmxlY3RWZWMueXogKTtcXG5cXHRcXHRcXHQjaWZkZWYgVEVYVFVSRV9MT0RfRVhUXFxuXFx0XFx0XFx0XFx0dmVjNCBlbnZNYXBDb2xvciA9IHRleHR1cmVDdWJlTG9kRVhUKCBlbnZNYXAsIHF1ZXJ5UmVmbGVjdFZlYywgc3BlY3VsYXJNSVBMZXZlbCApO1xcblxcdFxcdFxcdCNlbHNlXFxuXFx0XFx0XFx0XFx0dmVjNCBlbnZNYXBDb2xvciA9IHRleHR1cmVDdWJlKCBlbnZNYXAsIHF1ZXJ5UmVmbGVjdFZlYywgc3BlY3VsYXJNSVBMZXZlbCApO1xcblxcdFxcdFxcdCNlbmRpZlxcblxcdFxcdFxcdGVudk1hcENvbG9yLnJnYiA9IGVudk1hcFRleGVsVG9MaW5lYXIoIGVudk1hcENvbG9yICkucmdiO1xcblxcdFxcdCNlbGlmIGRlZmluZWQoIEVOVk1BUF9UWVBFX0NVQkVfVVYgKVxcblxcdFxcdFxcdHZlYzMgcXVlcnlSZWZsZWN0VmVjID0gdmVjMyggZmxpcEVudk1hcCAqIHJlZmxlY3RWZWMueCwgcmVmbGVjdFZlYy55eiApO1xcblxcdFxcdFxcdHZlYzQgZW52TWFwQ29sb3IgPSB0ZXh0dXJlQ3ViZVVWKHF1ZXJ5UmVmbGVjdFZlYywgQmxpbm5FeHBvbmVudFRvR0dYUm91Z2huZXNzKGJsaW5uU2hpbmluZXNzRXhwb25lbnQpKTtcXG5cXHRcXHQjZWxpZiBkZWZpbmVkKCBFTlZNQVBfVFlQRV9FUVVJUkVDIClcXG5cXHRcXHRcXHR2ZWMyIHNhbXBsZVVWO1xcblxcdFxcdFxcdHNhbXBsZVVWLnkgPSBzYXR1cmF0ZSggcmVmbGVjdFZlYy55ICogMC41ICsgMC41ICk7XFxuXFx0XFx0XFx0c2FtcGxlVVYueCA9IGF0YW4oIHJlZmxlY3RWZWMueiwgcmVmbGVjdFZlYy54ICkgKiBSRUNJUFJPQ0FMX1BJMiArIDAuNTtcXG5cXHRcXHRcXHQjaWZkZWYgVEVYVFVSRV9MT0RfRVhUXFxuXFx0XFx0XFx0XFx0dmVjNCBlbnZNYXBDb2xvciA9IHRleHR1cmUyRExvZEVYVCggZW52TWFwLCBzYW1wbGVVViwgc3BlY3VsYXJNSVBMZXZlbCApO1xcblxcdFxcdFxcdCNlbHNlXFxuXFx0XFx0XFx0XFx0dmVjNCBlbnZNYXBDb2xvciA9IHRleHR1cmUyRCggZW52TWFwLCBzYW1wbGVVViwgc3BlY3VsYXJNSVBMZXZlbCApO1xcblxcdFxcdFxcdCNlbmRpZlxcblxcdFxcdFxcdGVudk1hcENvbG9yLnJnYiA9IGVudk1hcFRleGVsVG9MaW5lYXIoIGVudk1hcENvbG9yICkucmdiO1xcblxcdFxcdCNlbGlmIGRlZmluZWQoIEVOVk1BUF9UWVBFX1NQSEVSRSApXFxuXFx0XFx0XFx0dmVjMyByZWZsZWN0VmlldyA9IG5vcm1hbGl6ZSggKCB2aWV3TWF0cml4ICogdmVjNCggcmVmbGVjdFZlYywgMC4wICkgKS54eXogKyB2ZWMzKCAwLjAsMC4wLDEuMCApICk7XFxuXFx0XFx0XFx0I2lmZGVmIFRFWFRVUkVfTE9EX0VYVFxcblxcdFxcdFxcdFxcdHZlYzQgZW52TWFwQ29sb3IgPSB0ZXh0dXJlMkRMb2RFWFQoIGVudk1hcCwgcmVmbGVjdFZpZXcueHkgKiAwLjUgKyAwLjUsIHNwZWN1bGFyTUlQTGV2ZWwgKTtcXG5cXHRcXHRcXHQjZWxzZVxcblxcdFxcdFxcdFxcdHZlYzQgZW52TWFwQ29sb3IgPSB0ZXh0dXJlMkQoIGVudk1hcCwgcmVmbGVjdFZpZXcueHkgKiAwLjUgKyAwLjUsIHNwZWN1bGFyTUlQTGV2ZWwgKTtcXG5cXHRcXHRcXHQjZW5kaWZcXG5cXHRcXHRcXHRlbnZNYXBDb2xvci5yZ2IgPSBlbnZNYXBUZXhlbFRvTGluZWFyKCBlbnZNYXBDb2xvciApLnJnYjtcXG5cXHRcXHQjZW5kaWZcXG5cXHRcXHRyZXR1cm4gZW52TWFwQ29sb3IucmdiICogZW52TWFwSW50ZW5zaXR5O1xcblxcdH1cXG4jZW5kaWZcXG5cIjtcblxuXHR2YXIgbGlnaHRzX3Bob25nX2ZyYWdtZW50ID0gXCJCbGlublBob25nTWF0ZXJpYWwgbWF0ZXJpYWw7XFxubWF0ZXJpYWwuZGlmZnVzZUNvbG9yID0gZGlmZnVzZUNvbG9yLnJnYjtcXG5tYXRlcmlhbC5zcGVjdWxhckNvbG9yID0gc3BlY3VsYXI7XFxubWF0ZXJpYWwuc3BlY3VsYXJTaGluaW5lc3MgPSBzaGluaW5lc3M7XFxubWF0ZXJpYWwuc3BlY3VsYXJTdHJlbmd0aCA9IHNwZWN1bGFyU3RyZW5ndGg7XFxuXCI7XG5cblx0dmFyIGxpZ2h0c19waG9uZ19wYXJzX2ZyYWdtZW50ID0gXCJ2YXJ5aW5nIHZlYzMgdlZpZXdQb3NpdGlvbjtcXG4jaWZuZGVmIEZMQVRfU0hBREVEXFxuXFx0dmFyeWluZyB2ZWMzIHZOb3JtYWw7XFxuI2VuZGlmXFxuc3RydWN0IEJsaW5uUGhvbmdNYXRlcmlhbCB7XFxuXFx0dmVjM1xcdGRpZmZ1c2VDb2xvcjtcXG5cXHR2ZWMzXFx0c3BlY3VsYXJDb2xvcjtcXG5cXHRmbG9hdFxcdHNwZWN1bGFyU2hpbmluZXNzO1xcblxcdGZsb2F0XFx0c3BlY3VsYXJTdHJlbmd0aDtcXG59O1xcbiNpZiBOVU1fUkVDVF9BUkVBX0xJR0hUUyA+IDBcXG4gICAgdm9pZCBSRV9EaXJlY3RfUmVjdEFyZWFfQmxpbm5QaG9uZyggY29uc3QgaW4gUmVjdEFyZWFMaWdodCByZWN0QXJlYUxpZ2h0LCBjb25zdCBpbiBHZW9tZXRyaWNDb250ZXh0IGdlb21ldHJ5LCBjb25zdCBpbiBCbGlublBob25nTWF0ZXJpYWwgbWF0ZXJpYWwsIGlub3V0IFJlZmxlY3RlZExpZ2h0IHJlZmxlY3RlZExpZ2h0ICkge1xcbiAgICAgICAgdmVjMyBtYXREaWZmQ29sb3IgPSBtYXRlcmlhbC5kaWZmdXNlQ29sb3I7XFxuICAgICAgICB2ZWMzIG1hdFNwZWNDb2xvciA9IG1hdGVyaWFsLnNwZWN1bGFyQ29sb3I7XFxuICAgICAgICB2ZWMzIGxpZ2h0Q29sb3IgICA9IHJlY3RBcmVhTGlnaHQuY29sb3I7XFxuICAgICAgICBmbG9hdCByb3VnaG5lc3MgPSBCbGlubkV4cG9uZW50VG9HR1hSb3VnaG5lc3MoIG1hdGVyaWFsLnNwZWN1bGFyU2hpbmluZXNzICk7XFxuICAgICAgICB2ZWMzIHNwZWMgPSBSZWN0X0FyZWFfTGlnaHRfU3BlY3VsYXJfUmVmbGVjdGFuY2UoXFxuICAgICAgICAgICAgICAgIGdlb21ldHJ5LFxcbiAgICAgICAgICAgICAgICByZWN0QXJlYUxpZ2h0LnBvc2l0aW9uLCByZWN0QXJlYUxpZ2h0LmhhbGZXaWR0aCwgcmVjdEFyZWFMaWdodC5oYWxmSGVpZ2h0LFxcbiAgICAgICAgICAgICAgICByb3VnaG5lc3MsXFxuICAgICAgICAgICAgICAgIGx0Y01hdCwgbHRjTWFnICk7XFxuICAgICAgICB2ZWMzIGRpZmYgPSBSZWN0X0FyZWFfTGlnaHRfRGlmZnVzZV9SZWZsZWN0YW5jZShcXG4gICAgICAgICAgICAgICAgZ2VvbWV0cnksXFxuICAgICAgICAgICAgICAgIHJlY3RBcmVhTGlnaHQucG9zaXRpb24sIHJlY3RBcmVhTGlnaHQuaGFsZldpZHRoLCByZWN0QXJlYUxpZ2h0LmhhbGZIZWlnaHQgKTtcXG4gICAgICAgIHJlZmxlY3RlZExpZ2h0LmRpcmVjdFNwZWN1bGFyICs9IGxpZ2h0Q29sb3IgKiBtYXRTcGVjQ29sb3IgKiBzcGVjIC8gUEkyO1xcbiAgICAgICAgcmVmbGVjdGVkTGlnaHQuZGlyZWN0RGlmZnVzZSAgKz0gbGlnaHRDb2xvciAqIG1hdERpZmZDb2xvciAqIGRpZmYgLyBQSTI7XFxuICAgIH1cXG4jZW5kaWZcXG52b2lkIFJFX0RpcmVjdF9CbGlublBob25nKCBjb25zdCBpbiBJbmNpZGVudExpZ2h0IGRpcmVjdExpZ2h0LCBjb25zdCBpbiBHZW9tZXRyaWNDb250ZXh0IGdlb21ldHJ5LCBjb25zdCBpbiBCbGlublBob25nTWF0ZXJpYWwgbWF0ZXJpYWwsIGlub3V0IFJlZmxlY3RlZExpZ2h0IHJlZmxlY3RlZExpZ2h0ICkge1xcblxcdCNpZmRlZiBUT09OXFxuXFx0XFx0dmVjMyBpcnJhZGlhbmNlID0gZ2V0R3JhZGllbnRJcnJhZGlhbmNlKCBnZW9tZXRyeS5ub3JtYWwsIGRpcmVjdExpZ2h0LmRpcmVjdGlvbiApICogZGlyZWN0TGlnaHQuY29sb3I7XFxuXFx0I2Vsc2VcXG5cXHRcXHRmbG9hdCBkb3ROTCA9IHNhdHVyYXRlKCBkb3QoIGdlb21ldHJ5Lm5vcm1hbCwgZGlyZWN0TGlnaHQuZGlyZWN0aW9uICkgKTtcXG5cXHRcXHR2ZWMzIGlycmFkaWFuY2UgPSBkb3ROTCAqIGRpcmVjdExpZ2h0LmNvbG9yO1xcblxcdCNlbmRpZlxcblxcdCNpZm5kZWYgUEhZU0lDQUxMWV9DT1JSRUNUX0xJR0hUU1xcblxcdFxcdGlycmFkaWFuY2UgKj0gUEk7XFxuXFx0I2VuZGlmXFxuXFx0cmVmbGVjdGVkTGlnaHQuZGlyZWN0RGlmZnVzZSArPSBpcnJhZGlhbmNlICogQlJERl9EaWZmdXNlX0xhbWJlcnQoIG1hdGVyaWFsLmRpZmZ1c2VDb2xvciApO1xcblxcdHJlZmxlY3RlZExpZ2h0LmRpcmVjdFNwZWN1bGFyICs9IGlycmFkaWFuY2UgKiBCUkRGX1NwZWN1bGFyX0JsaW5uUGhvbmcoIGRpcmVjdExpZ2h0LCBnZW9tZXRyeSwgbWF0ZXJpYWwuc3BlY3VsYXJDb2xvciwgbWF0ZXJpYWwuc3BlY3VsYXJTaGluaW5lc3MgKSAqIG1hdGVyaWFsLnNwZWN1bGFyU3RyZW5ndGg7XFxufVxcbnZvaWQgUkVfSW5kaXJlY3REaWZmdXNlX0JsaW5uUGhvbmcoIGNvbnN0IGluIHZlYzMgaXJyYWRpYW5jZSwgY29uc3QgaW4gR2VvbWV0cmljQ29udGV4dCBnZW9tZXRyeSwgY29uc3QgaW4gQmxpbm5QaG9uZ01hdGVyaWFsIG1hdGVyaWFsLCBpbm91dCBSZWZsZWN0ZWRMaWdodCByZWZsZWN0ZWRMaWdodCApIHtcXG5cXHRyZWZsZWN0ZWRMaWdodC5pbmRpcmVjdERpZmZ1c2UgKz0gaXJyYWRpYW5jZSAqIEJSREZfRGlmZnVzZV9MYW1iZXJ0KCBtYXRlcmlhbC5kaWZmdXNlQ29sb3IgKTtcXG59XFxuI2RlZmluZSBSRV9EaXJlY3RcXHRcXHRcXHRcXHRSRV9EaXJlY3RfQmxpbm5QaG9uZ1xcbiNkZWZpbmUgUkVfRGlyZWN0X1JlY3RBcmVhXFx0XFx0UkVfRGlyZWN0X1JlY3RBcmVhX0JsaW5uUGhvbmdcXG4jZGVmaW5lIFJFX0luZGlyZWN0RGlmZnVzZVxcdFxcdFJFX0luZGlyZWN0RGlmZnVzZV9CbGlublBob25nXFxuI2RlZmluZSBNYXRlcmlhbF9MaWdodFByb2JlTE9EKCBtYXRlcmlhbCApXFx0KDApXFxuXCI7XG5cblx0dmFyIGxpZ2h0c19waHlzaWNhbF9mcmFnbWVudCA9IFwiUGh5c2ljYWxNYXRlcmlhbCBtYXRlcmlhbDtcXG5tYXRlcmlhbC5kaWZmdXNlQ29sb3IgPSBkaWZmdXNlQ29sb3IucmdiICogKCAxLjAgLSBtZXRhbG5lc3NGYWN0b3IgKTtcXG5tYXRlcmlhbC5zcGVjdWxhclJvdWdobmVzcyA9IGNsYW1wKCByb3VnaG5lc3NGYWN0b3IsIDAuMDQsIDEuMCApO1xcbiNpZmRlZiBTVEFOREFSRFxcblxcdG1hdGVyaWFsLnNwZWN1bGFyQ29sb3IgPSBtaXgoIHZlYzMoIERFRkFVTFRfU1BFQ1VMQVJfQ09FRkZJQ0lFTlQgKSwgZGlmZnVzZUNvbG9yLnJnYiwgbWV0YWxuZXNzRmFjdG9yICk7XFxuI2Vsc2VcXG5cXHRtYXRlcmlhbC5zcGVjdWxhckNvbG9yID0gbWl4KCB2ZWMzKCBNQVhJTVVNX1NQRUNVTEFSX0NPRUZGSUNJRU5UICogcG93MiggcmVmbGVjdGl2aXR5ICkgKSwgZGlmZnVzZUNvbG9yLnJnYiwgbWV0YWxuZXNzRmFjdG9yICk7XFxuXFx0bWF0ZXJpYWwuY2xlYXJDb2F0ID0gc2F0dXJhdGUoIGNsZWFyQ29hdCApO1xcdG1hdGVyaWFsLmNsZWFyQ29hdFJvdWdobmVzcyA9IGNsYW1wKCBjbGVhckNvYXRSb3VnaG5lc3MsIDAuMDQsIDEuMCApO1xcbiNlbmRpZlxcblwiO1xuXG5cdHZhciBsaWdodHNfcGh5c2ljYWxfcGFyc19mcmFnbWVudCA9IFwic3RydWN0IFBoeXNpY2FsTWF0ZXJpYWwge1xcblxcdHZlYzNcXHRkaWZmdXNlQ29sb3I7XFxuXFx0ZmxvYXRcXHRzcGVjdWxhclJvdWdobmVzcztcXG5cXHR2ZWMzXFx0c3BlY3VsYXJDb2xvcjtcXG5cXHQjaWZuZGVmIFNUQU5EQVJEXFxuXFx0XFx0ZmxvYXQgY2xlYXJDb2F0O1xcblxcdFxcdGZsb2F0IGNsZWFyQ29hdFJvdWdobmVzcztcXG5cXHQjZW5kaWZcXG59O1xcbiNkZWZpbmUgTUFYSU1VTV9TUEVDVUxBUl9DT0VGRklDSUVOVCAwLjE2XFxuI2RlZmluZSBERUZBVUxUX1NQRUNVTEFSX0NPRUZGSUNJRU5UIDAuMDRcXG5mbG9hdCBjbGVhckNvYXRESFJBcHByb3goIGNvbnN0IGluIGZsb2F0IHJvdWdobmVzcywgY29uc3QgaW4gZmxvYXQgZG90TkwgKSB7XFxuXFx0cmV0dXJuIERFRkFVTFRfU1BFQ1VMQVJfQ09FRkZJQ0lFTlQgKyAoIDEuMCAtIERFRkFVTFRfU1BFQ1VMQVJfQ09FRkZJQ0lFTlQgKSAqICggcG93KCAxLjAgLSBkb3ROTCwgNS4wICkgKiBwb3coIDEuMCAtIHJvdWdobmVzcywgMi4wICkgKTtcXG59XFxuI2lmIE5VTV9SRUNUX0FSRUFfTElHSFRTID4gMFxcbiAgICB2b2lkIFJFX0RpcmVjdF9SZWN0QXJlYV9QaHlzaWNhbCggY29uc3QgaW4gUmVjdEFyZWFMaWdodCByZWN0QXJlYUxpZ2h0LCBjb25zdCBpbiBHZW9tZXRyaWNDb250ZXh0IGdlb21ldHJ5LCBjb25zdCBpbiBQaHlzaWNhbE1hdGVyaWFsIG1hdGVyaWFsLCBpbm91dCBSZWZsZWN0ZWRMaWdodCByZWZsZWN0ZWRMaWdodCApIHtcXG4gICAgICAgIHZlYzMgbWF0RGlmZkNvbG9yID0gbWF0ZXJpYWwuZGlmZnVzZUNvbG9yO1xcbiAgICAgICAgdmVjMyBtYXRTcGVjQ29sb3IgPSBtYXRlcmlhbC5zcGVjdWxhckNvbG9yO1xcbiAgICAgICAgdmVjMyBsaWdodENvbG9yICAgPSByZWN0QXJlYUxpZ2h0LmNvbG9yO1xcbiAgICAgICAgZmxvYXQgcm91Z2huZXNzID0gbWF0ZXJpYWwuc3BlY3VsYXJSb3VnaG5lc3M7XFxuICAgICAgICB2ZWMzIHNwZWMgPSBSZWN0X0FyZWFfTGlnaHRfU3BlY3VsYXJfUmVmbGVjdGFuY2UoXFxuICAgICAgICAgICAgICAgIGdlb21ldHJ5LFxcbiAgICAgICAgICAgICAgICByZWN0QXJlYUxpZ2h0LnBvc2l0aW9uLCByZWN0QXJlYUxpZ2h0LmhhbGZXaWR0aCwgcmVjdEFyZWFMaWdodC5oYWxmSGVpZ2h0LFxcbiAgICAgICAgICAgICAgICByb3VnaG5lc3MsXFxuICAgICAgICAgICAgICAgIGx0Y01hdCwgbHRjTWFnICk7XFxuICAgICAgICB2ZWMzIGRpZmYgPSBSZWN0X0FyZWFfTGlnaHRfRGlmZnVzZV9SZWZsZWN0YW5jZShcXG4gICAgICAgICAgICAgICAgZ2VvbWV0cnksXFxuICAgICAgICAgICAgICAgIHJlY3RBcmVhTGlnaHQucG9zaXRpb24sIHJlY3RBcmVhTGlnaHQuaGFsZldpZHRoLCByZWN0QXJlYUxpZ2h0LmhhbGZIZWlnaHQgKTtcXG4gICAgICAgIHJlZmxlY3RlZExpZ2h0LmRpcmVjdFNwZWN1bGFyICs9IGxpZ2h0Q29sb3IgKiBtYXRTcGVjQ29sb3IgKiBzcGVjO1xcbiAgICAgICAgcmVmbGVjdGVkTGlnaHQuZGlyZWN0RGlmZnVzZSAgKz0gbGlnaHRDb2xvciAqIG1hdERpZmZDb2xvciAqIGRpZmY7XFxuICAgIH1cXG4jZW5kaWZcXG52b2lkIFJFX0RpcmVjdF9QaHlzaWNhbCggY29uc3QgaW4gSW5jaWRlbnRMaWdodCBkaXJlY3RMaWdodCwgY29uc3QgaW4gR2VvbWV0cmljQ29udGV4dCBnZW9tZXRyeSwgY29uc3QgaW4gUGh5c2ljYWxNYXRlcmlhbCBtYXRlcmlhbCwgaW5vdXQgUmVmbGVjdGVkTGlnaHQgcmVmbGVjdGVkTGlnaHQgKSB7XFxuXFx0ZmxvYXQgZG90TkwgPSBzYXR1cmF0ZSggZG90KCBnZW9tZXRyeS5ub3JtYWwsIGRpcmVjdExpZ2h0LmRpcmVjdGlvbiApICk7XFxuXFx0dmVjMyBpcnJhZGlhbmNlID0gZG90TkwgKiBkaXJlY3RMaWdodC5jb2xvcjtcXG5cXHQjaWZuZGVmIFBIWVNJQ0FMTFlfQ09SUkVDVF9MSUdIVFNcXG5cXHRcXHRpcnJhZGlhbmNlICo9IFBJO1xcblxcdCNlbmRpZlxcblxcdCNpZm5kZWYgU1RBTkRBUkRcXG5cXHRcXHRmbG9hdCBjbGVhckNvYXRESFIgPSBtYXRlcmlhbC5jbGVhckNvYXQgKiBjbGVhckNvYXRESFJBcHByb3goIG1hdGVyaWFsLmNsZWFyQ29hdFJvdWdobmVzcywgZG90TkwgKTtcXG5cXHQjZWxzZVxcblxcdFxcdGZsb2F0IGNsZWFyQ29hdERIUiA9IDAuMDtcXG5cXHQjZW5kaWZcXG5cXHRyZWZsZWN0ZWRMaWdodC5kaXJlY3RTcGVjdWxhciArPSAoIDEuMCAtIGNsZWFyQ29hdERIUiApICogaXJyYWRpYW5jZSAqIEJSREZfU3BlY3VsYXJfR0dYKCBkaXJlY3RMaWdodCwgZ2VvbWV0cnksIG1hdGVyaWFsLnNwZWN1bGFyQ29sb3IsIG1hdGVyaWFsLnNwZWN1bGFyUm91Z2huZXNzICk7XFxuXFx0cmVmbGVjdGVkTGlnaHQuZGlyZWN0RGlmZnVzZSArPSAoIDEuMCAtIGNsZWFyQ29hdERIUiApICogaXJyYWRpYW5jZSAqIEJSREZfRGlmZnVzZV9MYW1iZXJ0KCBtYXRlcmlhbC5kaWZmdXNlQ29sb3IgKTtcXG5cXHQjaWZuZGVmIFNUQU5EQVJEXFxuXFx0XFx0cmVmbGVjdGVkTGlnaHQuZGlyZWN0U3BlY3VsYXIgKz0gaXJyYWRpYW5jZSAqIG1hdGVyaWFsLmNsZWFyQ29hdCAqIEJSREZfU3BlY3VsYXJfR0dYKCBkaXJlY3RMaWdodCwgZ2VvbWV0cnksIHZlYzMoIERFRkFVTFRfU1BFQ1VMQVJfQ09FRkZJQ0lFTlQgKSwgbWF0ZXJpYWwuY2xlYXJDb2F0Um91Z2huZXNzICk7XFxuXFx0I2VuZGlmXFxufVxcbnZvaWQgUkVfSW5kaXJlY3REaWZmdXNlX1BoeXNpY2FsKCBjb25zdCBpbiB2ZWMzIGlycmFkaWFuY2UsIGNvbnN0IGluIEdlb21ldHJpY0NvbnRleHQgZ2VvbWV0cnksIGNvbnN0IGluIFBoeXNpY2FsTWF0ZXJpYWwgbWF0ZXJpYWwsIGlub3V0IFJlZmxlY3RlZExpZ2h0IHJlZmxlY3RlZExpZ2h0ICkge1xcblxcdHJlZmxlY3RlZExpZ2h0LmluZGlyZWN0RGlmZnVzZSArPSBpcnJhZGlhbmNlICogQlJERl9EaWZmdXNlX0xhbWJlcnQoIG1hdGVyaWFsLmRpZmZ1c2VDb2xvciApO1xcbn1cXG52b2lkIFJFX0luZGlyZWN0U3BlY3VsYXJfUGh5c2ljYWwoIGNvbnN0IGluIHZlYzMgcmFkaWFuY2UsIGNvbnN0IGluIHZlYzMgY2xlYXJDb2F0UmFkaWFuY2UsIGNvbnN0IGluIEdlb21ldHJpY0NvbnRleHQgZ2VvbWV0cnksIGNvbnN0IGluIFBoeXNpY2FsTWF0ZXJpYWwgbWF0ZXJpYWwsIGlub3V0IFJlZmxlY3RlZExpZ2h0IHJlZmxlY3RlZExpZ2h0ICkge1xcblxcdCNpZm5kZWYgU1RBTkRBUkRcXG5cXHRcXHRmbG9hdCBkb3ROViA9IHNhdHVyYXRlKCBkb3QoIGdlb21ldHJ5Lm5vcm1hbCwgZ2VvbWV0cnkudmlld0RpciApICk7XFxuXFx0XFx0ZmxvYXQgZG90TkwgPSBkb3ROVjtcXG5cXHRcXHRmbG9hdCBjbGVhckNvYXRESFIgPSBtYXRlcmlhbC5jbGVhckNvYXQgKiBjbGVhckNvYXRESFJBcHByb3goIG1hdGVyaWFsLmNsZWFyQ29hdFJvdWdobmVzcywgZG90TkwgKTtcXG5cXHQjZWxzZVxcblxcdFxcdGZsb2F0IGNsZWFyQ29hdERIUiA9IDAuMDtcXG5cXHQjZW5kaWZcXG5cXHRyZWZsZWN0ZWRMaWdodC5pbmRpcmVjdFNwZWN1bGFyICs9ICggMS4wIC0gY2xlYXJDb2F0REhSICkgKiByYWRpYW5jZSAqIEJSREZfU3BlY3VsYXJfR0dYX0Vudmlyb25tZW50KCBnZW9tZXRyeSwgbWF0ZXJpYWwuc3BlY3VsYXJDb2xvciwgbWF0ZXJpYWwuc3BlY3VsYXJSb3VnaG5lc3MgKTtcXG5cXHQjaWZuZGVmIFNUQU5EQVJEXFxuXFx0XFx0cmVmbGVjdGVkTGlnaHQuaW5kaXJlY3RTcGVjdWxhciArPSBjbGVhckNvYXRSYWRpYW5jZSAqIG1hdGVyaWFsLmNsZWFyQ29hdCAqIEJSREZfU3BlY3VsYXJfR0dYX0Vudmlyb25tZW50KCBnZW9tZXRyeSwgdmVjMyggREVGQVVMVF9TUEVDVUxBUl9DT0VGRklDSUVOVCApLCBtYXRlcmlhbC5jbGVhckNvYXRSb3VnaG5lc3MgKTtcXG5cXHQjZW5kaWZcXG59XFxuI2RlZmluZSBSRV9EaXJlY3RcXHRcXHRcXHRcXHRSRV9EaXJlY3RfUGh5c2ljYWxcXG4jZGVmaW5lIFJFX0RpcmVjdF9SZWN0QXJlYVxcdFxcdFJFX0RpcmVjdF9SZWN0QXJlYV9QaHlzaWNhbFxcbiNkZWZpbmUgUkVfSW5kaXJlY3REaWZmdXNlXFx0XFx0UkVfSW5kaXJlY3REaWZmdXNlX1BoeXNpY2FsXFxuI2RlZmluZSBSRV9JbmRpcmVjdFNwZWN1bGFyXFx0XFx0UkVfSW5kaXJlY3RTcGVjdWxhcl9QaHlzaWNhbFxcbiNkZWZpbmUgTWF0ZXJpYWxfQmxpbm5TaGluaW5lc3NFeHBvbmVudCggbWF0ZXJpYWwgKSAgIEdHWFJvdWdobmVzc1RvQmxpbm5FeHBvbmVudCggbWF0ZXJpYWwuc3BlY3VsYXJSb3VnaG5lc3MgKVxcbiNkZWZpbmUgTWF0ZXJpYWxfQ2xlYXJDb2F0X0JsaW5uU2hpbmluZXNzRXhwb25lbnQoIG1hdGVyaWFsICkgICBHR1hSb3VnaG5lc3NUb0JsaW5uRXhwb25lbnQoIG1hdGVyaWFsLmNsZWFyQ29hdFJvdWdobmVzcyApXFxuZmxvYXQgY29tcHV0ZVNwZWN1bGFyT2NjbHVzaW9uKCBjb25zdCBpbiBmbG9hdCBkb3ROViwgY29uc3QgaW4gZmxvYXQgYW1iaWVudE9jY2x1c2lvbiwgY29uc3QgaW4gZmxvYXQgcm91Z2huZXNzICkge1xcblxcdHJldHVybiBzYXR1cmF0ZSggcG93KCBkb3ROViArIGFtYmllbnRPY2NsdXNpb24sIGV4cDIoIC0gMTYuMCAqIHJvdWdobmVzcyAtIDEuMCApICkgLSAxLjAgKyBhbWJpZW50T2NjbHVzaW9uICk7XFxufVxcblwiO1xuXG5cdHZhciBsaWdodHNfdGVtcGxhdGUgPSBcIlxcbkdlb21ldHJpY0NvbnRleHQgZ2VvbWV0cnk7XFxuZ2VvbWV0cnkucG9zaXRpb24gPSAtIHZWaWV3UG9zaXRpb247XFxuZ2VvbWV0cnkubm9ybWFsID0gbm9ybWFsO1xcbmdlb21ldHJ5LnZpZXdEaXIgPSBub3JtYWxpemUoIHZWaWV3UG9zaXRpb24gKTtcXG5JbmNpZGVudExpZ2h0IGRpcmVjdExpZ2h0O1xcbiNpZiAoIE5VTV9QT0lOVF9MSUdIVFMgPiAwICkgJiYgZGVmaW5lZCggUkVfRGlyZWN0IClcXG5cXHRQb2ludExpZ2h0IHBvaW50TGlnaHQ7XFxuXFx0Zm9yICggaW50IGkgPSAwOyBpIDwgTlVNX1BPSU5UX0xJR0hUUzsgaSArKyApIHtcXG5cXHRcXHRwb2ludExpZ2h0ID0gcG9pbnRMaWdodHNbIGkgXTtcXG5cXHRcXHRnZXRQb2ludERpcmVjdExpZ2h0SXJyYWRpYW5jZSggcG9pbnRMaWdodCwgZ2VvbWV0cnksIGRpcmVjdExpZ2h0ICk7XFxuXFx0XFx0I2lmZGVmIFVTRV9TSEFET1dNQVBcXG5cXHRcXHRkaXJlY3RMaWdodC5jb2xvciAqPSBhbGwoIGJ2ZWMyKCBwb2ludExpZ2h0LnNoYWRvdywgZGlyZWN0TGlnaHQudmlzaWJsZSApICkgPyBnZXRQb2ludFNoYWRvdyggcG9pbnRTaGFkb3dNYXBbIGkgXSwgcG9pbnRMaWdodC5zaGFkb3dNYXBTaXplLCBwb2ludExpZ2h0LnNoYWRvd0JpYXMsIHBvaW50TGlnaHQuc2hhZG93UmFkaXVzLCB2UG9pbnRTaGFkb3dDb29yZFsgaSBdICkgOiAxLjA7XFxuXFx0XFx0I2VuZGlmXFxuXFx0XFx0UkVfRGlyZWN0KCBkaXJlY3RMaWdodCwgZ2VvbWV0cnksIG1hdGVyaWFsLCByZWZsZWN0ZWRMaWdodCApO1xcblxcdH1cXG4jZW5kaWZcXG4jaWYgKCBOVU1fU1BPVF9MSUdIVFMgPiAwICkgJiYgZGVmaW5lZCggUkVfRGlyZWN0IClcXG5cXHRTcG90TGlnaHQgc3BvdExpZ2h0O1xcblxcdGZvciAoIGludCBpID0gMDsgaSA8IE5VTV9TUE9UX0xJR0hUUzsgaSArKyApIHtcXG5cXHRcXHRzcG90TGlnaHQgPSBzcG90TGlnaHRzWyBpIF07XFxuXFx0XFx0Z2V0U3BvdERpcmVjdExpZ2h0SXJyYWRpYW5jZSggc3BvdExpZ2h0LCBnZW9tZXRyeSwgZGlyZWN0TGlnaHQgKTtcXG5cXHRcXHQjaWZkZWYgVVNFX1NIQURPV01BUFxcblxcdFxcdGRpcmVjdExpZ2h0LmNvbG9yICo9IGFsbCggYnZlYzIoIHNwb3RMaWdodC5zaGFkb3csIGRpcmVjdExpZ2h0LnZpc2libGUgKSApID8gZ2V0U2hhZG93KCBzcG90U2hhZG93TWFwWyBpIF0sIHNwb3RMaWdodC5zaGFkb3dNYXBTaXplLCBzcG90TGlnaHQuc2hhZG93Qmlhcywgc3BvdExpZ2h0LnNoYWRvd1JhZGl1cywgdlNwb3RTaGFkb3dDb29yZFsgaSBdICkgOiAxLjA7XFxuXFx0XFx0I2VuZGlmXFxuXFx0XFx0UkVfRGlyZWN0KCBkaXJlY3RMaWdodCwgZ2VvbWV0cnksIG1hdGVyaWFsLCByZWZsZWN0ZWRMaWdodCApO1xcblxcdH1cXG4jZW5kaWZcXG4jaWYgKCBOVU1fRElSX0xJR0hUUyA+IDAgKSAmJiBkZWZpbmVkKCBSRV9EaXJlY3QgKVxcblxcdERpcmVjdGlvbmFsTGlnaHQgZGlyZWN0aW9uYWxMaWdodDtcXG5cXHRmb3IgKCBpbnQgaSA9IDA7IGkgPCBOVU1fRElSX0xJR0hUUzsgaSArKyApIHtcXG5cXHRcXHRkaXJlY3Rpb25hbExpZ2h0ID0gZGlyZWN0aW9uYWxMaWdodHNbIGkgXTtcXG5cXHRcXHRnZXREaXJlY3Rpb25hbERpcmVjdExpZ2h0SXJyYWRpYW5jZSggZGlyZWN0aW9uYWxMaWdodCwgZ2VvbWV0cnksIGRpcmVjdExpZ2h0ICk7XFxuXFx0XFx0I2lmZGVmIFVTRV9TSEFET1dNQVBcXG5cXHRcXHRkaXJlY3RMaWdodC5jb2xvciAqPSBhbGwoIGJ2ZWMyKCBkaXJlY3Rpb25hbExpZ2h0LnNoYWRvdywgZGlyZWN0TGlnaHQudmlzaWJsZSApICkgPyBnZXRTaGFkb3coIGRpcmVjdGlvbmFsU2hhZG93TWFwWyBpIF0sIGRpcmVjdGlvbmFsTGlnaHQuc2hhZG93TWFwU2l6ZSwgZGlyZWN0aW9uYWxMaWdodC5zaGFkb3dCaWFzLCBkaXJlY3Rpb25hbExpZ2h0LnNoYWRvd1JhZGl1cywgdkRpcmVjdGlvbmFsU2hhZG93Q29vcmRbIGkgXSApIDogMS4wO1xcblxcdFxcdCNlbmRpZlxcblxcdFxcdFJFX0RpcmVjdCggZGlyZWN0TGlnaHQsIGdlb21ldHJ5LCBtYXRlcmlhbCwgcmVmbGVjdGVkTGlnaHQgKTtcXG5cXHR9XFxuI2VuZGlmXFxuI2lmICggTlVNX1JFQ1RfQVJFQV9MSUdIVFMgPiAwICkgJiYgZGVmaW5lZCggUkVfRGlyZWN0X1JlY3RBcmVhIClcXG5cXHRSZWN0QXJlYUxpZ2h0IHJlY3RBcmVhTGlnaHQ7XFxuXFx0Zm9yICggaW50IGkgPSAwOyBpIDwgTlVNX1JFQ1RfQVJFQV9MSUdIVFM7IGkgKysgKSB7XFxuXFx0XFx0cmVjdEFyZWFMaWdodCA9IHJlY3RBcmVhTGlnaHRzWyBpIF07XFxuXFx0XFx0UkVfRGlyZWN0X1JlY3RBcmVhKCByZWN0QXJlYUxpZ2h0LCBnZW9tZXRyeSwgbWF0ZXJpYWwsIHJlZmxlY3RlZExpZ2h0ICk7XFxuXFx0fVxcbiNlbmRpZlxcbiNpZiBkZWZpbmVkKCBSRV9JbmRpcmVjdERpZmZ1c2UgKVxcblxcdHZlYzMgaXJyYWRpYW5jZSA9IGdldEFtYmllbnRMaWdodElycmFkaWFuY2UoIGFtYmllbnRMaWdodENvbG9yICk7XFxuXFx0I2lmZGVmIFVTRV9MSUdIVE1BUFxcblxcdFxcdHZlYzMgbGlnaHRNYXBJcnJhZGlhbmNlID0gdGV4dHVyZTJEKCBsaWdodE1hcCwgdlV2MiApLnh5eiAqIGxpZ2h0TWFwSW50ZW5zaXR5O1xcblxcdFxcdCNpZm5kZWYgUEhZU0lDQUxMWV9DT1JSRUNUX0xJR0hUU1xcblxcdFxcdFxcdGxpZ2h0TWFwSXJyYWRpYW5jZSAqPSBQSTtcXG5cXHRcXHQjZW5kaWZcXG5cXHRcXHRpcnJhZGlhbmNlICs9IGxpZ2h0TWFwSXJyYWRpYW5jZTtcXG5cXHQjZW5kaWZcXG5cXHQjaWYgKCBOVU1fSEVNSV9MSUdIVFMgPiAwIClcXG5cXHRcXHRmb3IgKCBpbnQgaSA9IDA7IGkgPCBOVU1fSEVNSV9MSUdIVFM7IGkgKysgKSB7XFxuXFx0XFx0XFx0aXJyYWRpYW5jZSArPSBnZXRIZW1pc3BoZXJlTGlnaHRJcnJhZGlhbmNlKCBoZW1pc3BoZXJlTGlnaHRzWyBpIF0sIGdlb21ldHJ5ICk7XFxuXFx0XFx0fVxcblxcdCNlbmRpZlxcblxcdCNpZiBkZWZpbmVkKCBVU0VfRU5WTUFQICkgJiYgZGVmaW5lZCggUEhZU0lDQUwgKSAmJiBkZWZpbmVkKCBFTlZNQVBfVFlQRV9DVUJFX1VWIClcXG5cXHQgXFx0aXJyYWRpYW5jZSArPSBnZXRMaWdodFByb2JlSW5kaXJlY3RJcnJhZGlhbmNlKCBnZW9tZXRyeSwgOCApO1xcblxcdCNlbmRpZlxcblxcdFJFX0luZGlyZWN0RGlmZnVzZSggaXJyYWRpYW5jZSwgZ2VvbWV0cnksIG1hdGVyaWFsLCByZWZsZWN0ZWRMaWdodCApO1xcbiNlbmRpZlxcbiNpZiBkZWZpbmVkKCBVU0VfRU5WTUFQICkgJiYgZGVmaW5lZCggUkVfSW5kaXJlY3RTcGVjdWxhciApXFxuXFx0dmVjMyByYWRpYW5jZSA9IGdldExpZ2h0UHJvYmVJbmRpcmVjdFJhZGlhbmNlKCBnZW9tZXRyeSwgTWF0ZXJpYWxfQmxpbm5TaGluaW5lc3NFeHBvbmVudCggbWF0ZXJpYWwgKSwgOCApO1xcblxcdCNpZm5kZWYgU1RBTkRBUkRcXG5cXHRcXHR2ZWMzIGNsZWFyQ29hdFJhZGlhbmNlID0gZ2V0TGlnaHRQcm9iZUluZGlyZWN0UmFkaWFuY2UoIGdlb21ldHJ5LCBNYXRlcmlhbF9DbGVhckNvYXRfQmxpbm5TaGluaW5lc3NFeHBvbmVudCggbWF0ZXJpYWwgKSwgOCApO1xcblxcdCNlbHNlXFxuXFx0XFx0dmVjMyBjbGVhckNvYXRSYWRpYW5jZSA9IHZlYzMoIDAuMCApO1xcblxcdCNlbmRpZlxcblxcdFxcdFxcblxcdFJFX0luZGlyZWN0U3BlY3VsYXIoIHJhZGlhbmNlLCBjbGVhckNvYXRSYWRpYW5jZSwgZ2VvbWV0cnksIG1hdGVyaWFsLCByZWZsZWN0ZWRMaWdodCApO1xcbiNlbmRpZlxcblwiO1xuXG5cdHZhciBsb2dkZXB0aGJ1Zl9mcmFnbWVudCA9IFwiI2lmIGRlZmluZWQoVVNFX0xPR0RFUFRIQlVGKSAmJiBkZWZpbmVkKFVTRV9MT0dERVBUSEJVRl9FWFQpXFxuXFx0Z2xfRnJhZ0RlcHRoRVhUID0gbG9nMih2RnJhZ0RlcHRoKSAqIGxvZ0RlcHRoQnVmRkMgKiAwLjU7XFxuI2VuZGlmXCI7XG5cblx0dmFyIGxvZ2RlcHRoYnVmX3BhcnNfZnJhZ21lbnQgPSBcIiNpZmRlZiBVU0VfTE9HREVQVEhCVUZcXG5cXHR1bmlmb3JtIGZsb2F0IGxvZ0RlcHRoQnVmRkM7XFxuXFx0I2lmZGVmIFVTRV9MT0dERVBUSEJVRl9FWFRcXG5cXHRcXHR2YXJ5aW5nIGZsb2F0IHZGcmFnRGVwdGg7XFxuXFx0I2VuZGlmXFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIGxvZ2RlcHRoYnVmX3BhcnNfdmVydGV4ID0gXCIjaWZkZWYgVVNFX0xPR0RFUFRIQlVGXFxuXFx0I2lmZGVmIFVTRV9MT0dERVBUSEJVRl9FWFRcXG5cXHRcXHR2YXJ5aW5nIGZsb2F0IHZGcmFnRGVwdGg7XFxuXFx0I2VuZGlmXFxuXFx0dW5pZm9ybSBmbG9hdCBsb2dEZXB0aEJ1ZkZDO1xcbiNlbmRpZlwiO1xuXG5cdHZhciBsb2dkZXB0aGJ1Zl92ZXJ0ZXggPSBcIiNpZmRlZiBVU0VfTE9HREVQVEhCVUZcXG5cXHRnbF9Qb3NpdGlvbi56ID0gbG9nMihtYXgoIEVQU0lMT04sIGdsX1Bvc2l0aW9uLncgKyAxLjAgKSkgKiBsb2dEZXB0aEJ1ZkZDO1xcblxcdCNpZmRlZiBVU0VfTE9HREVQVEhCVUZfRVhUXFxuXFx0XFx0dkZyYWdEZXB0aCA9IDEuMCArIGdsX1Bvc2l0aW9uLnc7XFxuXFx0I2Vsc2VcXG5cXHRcXHRnbF9Qb3NpdGlvbi56ID0gKGdsX1Bvc2l0aW9uLnogLSAxLjApICogZ2xfUG9zaXRpb24udztcXG5cXHQjZW5kaWZcXG4jZW5kaWZcXG5cIjtcblxuXHR2YXIgbWFwX2ZyYWdtZW50ID0gXCIjaWZkZWYgVVNFX01BUFxcblxcdHZlYzQgdGV4ZWxDb2xvciA9IHRleHR1cmUyRCggbWFwLCB2VXYgKTtcXG5cXHR0ZXhlbENvbG9yID0gbWFwVGV4ZWxUb0xpbmVhciggdGV4ZWxDb2xvciApO1xcblxcdGRpZmZ1c2VDb2xvciAqPSB0ZXhlbENvbG9yO1xcbiNlbmRpZlxcblwiO1xuXG5cdHZhciBtYXBfcGFyc19mcmFnbWVudCA9IFwiI2lmZGVmIFVTRV9NQVBcXG5cXHR1bmlmb3JtIHNhbXBsZXIyRCBtYXA7XFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIG1hcF9wYXJ0aWNsZV9mcmFnbWVudCA9IFwiI2lmZGVmIFVTRV9NQVBcXG5cXHR2ZWM0IG1hcFRleGVsID0gdGV4dHVyZTJEKCBtYXAsIHZlYzIoIGdsX1BvaW50Q29vcmQueCwgMS4wIC0gZ2xfUG9pbnRDb29yZC55ICkgKiBvZmZzZXRSZXBlYXQuencgKyBvZmZzZXRSZXBlYXQueHkgKTtcXG5cXHRkaWZmdXNlQ29sb3IgKj0gbWFwVGV4ZWxUb0xpbmVhciggbWFwVGV4ZWwgKTtcXG4jZW5kaWZcXG5cIjtcblxuXHR2YXIgbWFwX3BhcnRpY2xlX3BhcnNfZnJhZ21lbnQgPSBcIiNpZmRlZiBVU0VfTUFQXFxuXFx0dW5pZm9ybSB2ZWM0IG9mZnNldFJlcGVhdDtcXG5cXHR1bmlmb3JtIHNhbXBsZXIyRCBtYXA7XFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIG1ldGFsbmVzc21hcF9mcmFnbWVudCA9IFwiZmxvYXQgbWV0YWxuZXNzRmFjdG9yID0gbWV0YWxuZXNzO1xcbiNpZmRlZiBVU0VfTUVUQUxORVNTTUFQXFxuXFx0dmVjNCB0ZXhlbE1ldGFsbmVzcyA9IHRleHR1cmUyRCggbWV0YWxuZXNzTWFwLCB2VXYgKTtcXG5cXHRtZXRhbG5lc3NGYWN0b3IgKj0gdGV4ZWxNZXRhbG5lc3MucjtcXG4jZW5kaWZcXG5cIjtcblxuXHR2YXIgbWV0YWxuZXNzbWFwX3BhcnNfZnJhZ21lbnQgPSBcIiNpZmRlZiBVU0VfTUVUQUxORVNTTUFQXFxuXFx0dW5pZm9ybSBzYW1wbGVyMkQgbWV0YWxuZXNzTWFwO1xcbiNlbmRpZlwiO1xuXG5cdHZhciBtb3JwaG5vcm1hbF92ZXJ0ZXggPSBcIiNpZmRlZiBVU0VfTU9SUEhOT1JNQUxTXFxuXFx0b2JqZWN0Tm9ybWFsICs9ICggbW9ycGhOb3JtYWwwIC0gbm9ybWFsICkgKiBtb3JwaFRhcmdldEluZmx1ZW5jZXNbIDAgXTtcXG5cXHRvYmplY3ROb3JtYWwgKz0gKCBtb3JwaE5vcm1hbDEgLSBub3JtYWwgKSAqIG1vcnBoVGFyZ2V0SW5mbHVlbmNlc1sgMSBdO1xcblxcdG9iamVjdE5vcm1hbCArPSAoIG1vcnBoTm9ybWFsMiAtIG5vcm1hbCApICogbW9ycGhUYXJnZXRJbmZsdWVuY2VzWyAyIF07XFxuXFx0b2JqZWN0Tm9ybWFsICs9ICggbW9ycGhOb3JtYWwzIC0gbm9ybWFsICkgKiBtb3JwaFRhcmdldEluZmx1ZW5jZXNbIDMgXTtcXG4jZW5kaWZcXG5cIjtcblxuXHR2YXIgbW9ycGh0YXJnZXRfcGFyc192ZXJ0ZXggPSBcIiNpZmRlZiBVU0VfTU9SUEhUQVJHRVRTXFxuXFx0I2lmbmRlZiBVU0VfTU9SUEhOT1JNQUxTXFxuXFx0dW5pZm9ybSBmbG9hdCBtb3JwaFRhcmdldEluZmx1ZW5jZXNbIDggXTtcXG5cXHQjZWxzZVxcblxcdHVuaWZvcm0gZmxvYXQgbW9ycGhUYXJnZXRJbmZsdWVuY2VzWyA0IF07XFxuXFx0I2VuZGlmXFxuI2VuZGlmXCI7XG5cblx0dmFyIG1vcnBodGFyZ2V0X3ZlcnRleCA9IFwiI2lmZGVmIFVTRV9NT1JQSFRBUkdFVFNcXG5cXHR0cmFuc2Zvcm1lZCArPSAoIG1vcnBoVGFyZ2V0MCAtIHBvc2l0aW9uICkgKiBtb3JwaFRhcmdldEluZmx1ZW5jZXNbIDAgXTtcXG5cXHR0cmFuc2Zvcm1lZCArPSAoIG1vcnBoVGFyZ2V0MSAtIHBvc2l0aW9uICkgKiBtb3JwaFRhcmdldEluZmx1ZW5jZXNbIDEgXTtcXG5cXHR0cmFuc2Zvcm1lZCArPSAoIG1vcnBoVGFyZ2V0MiAtIHBvc2l0aW9uICkgKiBtb3JwaFRhcmdldEluZmx1ZW5jZXNbIDIgXTtcXG5cXHR0cmFuc2Zvcm1lZCArPSAoIG1vcnBoVGFyZ2V0MyAtIHBvc2l0aW9uICkgKiBtb3JwaFRhcmdldEluZmx1ZW5jZXNbIDMgXTtcXG5cXHQjaWZuZGVmIFVTRV9NT1JQSE5PUk1BTFNcXG5cXHR0cmFuc2Zvcm1lZCArPSAoIG1vcnBoVGFyZ2V0NCAtIHBvc2l0aW9uICkgKiBtb3JwaFRhcmdldEluZmx1ZW5jZXNbIDQgXTtcXG5cXHR0cmFuc2Zvcm1lZCArPSAoIG1vcnBoVGFyZ2V0NSAtIHBvc2l0aW9uICkgKiBtb3JwaFRhcmdldEluZmx1ZW5jZXNbIDUgXTtcXG5cXHR0cmFuc2Zvcm1lZCArPSAoIG1vcnBoVGFyZ2V0NiAtIHBvc2l0aW9uICkgKiBtb3JwaFRhcmdldEluZmx1ZW5jZXNbIDYgXTtcXG5cXHR0cmFuc2Zvcm1lZCArPSAoIG1vcnBoVGFyZ2V0NyAtIHBvc2l0aW9uICkgKiBtb3JwaFRhcmdldEluZmx1ZW5jZXNbIDcgXTtcXG5cXHQjZW5kaWZcXG4jZW5kaWZcXG5cIjtcblxuXHR2YXIgbm9ybWFsX2ZsaXAgPSBcIiNpZmRlZiBET1VCTEVfU0lERURcXG5cXHRmbG9hdCBmbGlwTm9ybWFsID0gKCBmbG9hdCggZ2xfRnJvbnRGYWNpbmcgKSAqIDIuMCAtIDEuMCApO1xcbiNlbHNlXFxuXFx0ZmxvYXQgZmxpcE5vcm1hbCA9IDEuMDtcXG4jZW5kaWZcXG5cIjtcblxuXHR2YXIgbm9ybWFsX2ZyYWdtZW50ID0gXCIjaWZkZWYgRkxBVF9TSEFERURcXG5cXHR2ZWMzIGZkeCA9IHZlYzMoIGRGZHgoIHZWaWV3UG9zaXRpb24ueCApLCBkRmR4KCB2Vmlld1Bvc2l0aW9uLnkgKSwgZEZkeCggdlZpZXdQb3NpdGlvbi56ICkgKTtcXG5cXHR2ZWMzIGZkeSA9IHZlYzMoIGRGZHkoIHZWaWV3UG9zaXRpb24ueCApLCBkRmR5KCB2Vmlld1Bvc2l0aW9uLnkgKSwgZEZkeSggdlZpZXdQb3NpdGlvbi56ICkgKTtcXG5cXHR2ZWMzIG5vcm1hbCA9IG5vcm1hbGl6ZSggY3Jvc3MoIGZkeCwgZmR5ICkgKTtcXG4jZWxzZVxcblxcdHZlYzMgbm9ybWFsID0gbm9ybWFsaXplKCB2Tm9ybWFsICkgKiBmbGlwTm9ybWFsO1xcbiNlbmRpZlxcbiNpZmRlZiBVU0VfTk9STUFMTUFQXFxuXFx0bm9ybWFsID0gcGVydHVyYk5vcm1hbDJBcmIoIC12Vmlld1Bvc2l0aW9uLCBub3JtYWwgKTtcXG4jZWxpZiBkZWZpbmVkKCBVU0VfQlVNUE1BUCApXFxuXFx0bm9ybWFsID0gcGVydHVyYk5vcm1hbEFyYiggLXZWaWV3UG9zaXRpb24sIG5vcm1hbCwgZEhkeHlfZndkKCkgKTtcXG4jZW5kaWZcXG5cIjtcblxuXHR2YXIgbm9ybWFsbWFwX3BhcnNfZnJhZ21lbnQgPSBcIiNpZmRlZiBVU0VfTk9STUFMTUFQXFxuXFx0dW5pZm9ybSBzYW1wbGVyMkQgbm9ybWFsTWFwO1xcblxcdHVuaWZvcm0gdmVjMiBub3JtYWxTY2FsZTtcXG5cXHR2ZWMzIHBlcnR1cmJOb3JtYWwyQXJiKCB2ZWMzIGV5ZV9wb3MsIHZlYzMgc3VyZl9ub3JtICkge1xcblxcdFxcdHZlYzMgcTAgPSBkRmR4KCBleWVfcG9zLnh5eiApO1xcblxcdFxcdHZlYzMgcTEgPSBkRmR5KCBleWVfcG9zLnh5eiApO1xcblxcdFxcdHZlYzIgc3QwID0gZEZkeCggdlV2LnN0ICk7XFxuXFx0XFx0dmVjMiBzdDEgPSBkRmR5KCB2VXYuc3QgKTtcXG5cXHRcXHR2ZWMzIFMgPSBub3JtYWxpemUoIHEwICogc3QxLnQgLSBxMSAqIHN0MC50ICk7XFxuXFx0XFx0dmVjMyBUID0gbm9ybWFsaXplKCAtcTAgKiBzdDEucyArIHExICogc3QwLnMgKTtcXG5cXHRcXHR2ZWMzIE4gPSBub3JtYWxpemUoIHN1cmZfbm9ybSApO1xcblxcdFxcdHZlYzMgbWFwTiA9IHRleHR1cmUyRCggbm9ybWFsTWFwLCB2VXYgKS54eXogKiAyLjAgLSAxLjA7XFxuXFx0XFx0bWFwTi54eSA9IG5vcm1hbFNjYWxlICogbWFwTi54eTtcXG5cXHRcXHRtYXQzIHRzbiA9IG1hdDMoIFMsIFQsIE4gKTtcXG5cXHRcXHRyZXR1cm4gbm9ybWFsaXplKCB0c24gKiBtYXBOICk7XFxuXFx0fVxcbiNlbmRpZlxcblwiO1xuXG5cdHZhciBwYWNraW5nID0gXCJ2ZWMzIHBhY2tOb3JtYWxUb1JHQiggY29uc3QgaW4gdmVjMyBub3JtYWwgKSB7XFxuICByZXR1cm4gbm9ybWFsaXplKCBub3JtYWwgKSAqIDAuNSArIDAuNTtcXG59XFxudmVjMyB1bnBhY2tSR0JUb05vcm1hbCggY29uc3QgaW4gdmVjMyByZ2IgKSB7XFxuICByZXR1cm4gMS4wIC0gMi4wICogcmdiLnh5ejtcXG59XFxuY29uc3QgZmxvYXQgUGFja1Vwc2NhbGUgPSAyNTYuIC8gMjU1Ljtjb25zdCBmbG9hdCBVbnBhY2tEb3duc2NhbGUgPSAyNTUuIC8gMjU2LjtcXG5jb25zdCB2ZWMzIFBhY2tGYWN0b3JzID0gdmVjMyggMjU2LiAqIDI1Ni4gKiAyNTYuLCAyNTYuICogMjU2LiwgIDI1Ni4gKTtcXG5jb25zdCB2ZWM0IFVucGFja0ZhY3RvcnMgPSBVbnBhY2tEb3duc2NhbGUgLyB2ZWM0KCBQYWNrRmFjdG9ycywgMS4gKTtcXG5jb25zdCBmbG9hdCBTaGlmdFJpZ2h0OCA9IDEuIC8gMjU2LjtcXG52ZWM0IHBhY2tEZXB0aFRvUkdCQSggY29uc3QgaW4gZmxvYXQgdiApIHtcXG5cXHR2ZWM0IHIgPSB2ZWM0KCBmcmFjdCggdiAqIFBhY2tGYWN0b3JzICksIHYgKTtcXG5cXHRyLnl6dyAtPSByLnh5eiAqIFNoaWZ0UmlnaHQ4O1xcdHJldHVybiByICogUGFja1Vwc2NhbGU7XFxufVxcbmZsb2F0IHVucGFja1JHQkFUb0RlcHRoKCBjb25zdCBpbiB2ZWM0IHYgKSB7XFxuXFx0cmV0dXJuIGRvdCggdiwgVW5wYWNrRmFjdG9ycyApO1xcbn1cXG5mbG9hdCB2aWV3WlRvT3J0aG9ncmFwaGljRGVwdGgoIGNvbnN0IGluIGZsb2F0IHZpZXdaLCBjb25zdCBpbiBmbG9hdCBuZWFyLCBjb25zdCBpbiBmbG9hdCBmYXIgKSB7XFxuICByZXR1cm4gKCB2aWV3WiArIG5lYXIgKSAvICggbmVhciAtIGZhciApO1xcbn1cXG5mbG9hdCBvcnRob2dyYXBoaWNEZXB0aFRvVmlld1ooIGNvbnN0IGluIGZsb2F0IGxpbmVhckNsaXBaLCBjb25zdCBpbiBmbG9hdCBuZWFyLCBjb25zdCBpbiBmbG9hdCBmYXIgKSB7XFxuICByZXR1cm4gbGluZWFyQ2xpcFogKiAoIG5lYXIgLSBmYXIgKSAtIG5lYXI7XFxufVxcbmZsb2F0IHZpZXdaVG9QZXJzcGVjdGl2ZURlcHRoKCBjb25zdCBpbiBmbG9hdCB2aWV3WiwgY29uc3QgaW4gZmxvYXQgbmVhciwgY29uc3QgaW4gZmxvYXQgZmFyICkge1xcbiAgcmV0dXJuICgoIG5lYXIgKyB2aWV3WiApICogZmFyICkgLyAoKCBmYXIgLSBuZWFyICkgKiB2aWV3WiApO1xcbn1cXG5mbG9hdCBwZXJzcGVjdGl2ZURlcHRoVG9WaWV3WiggY29uc3QgaW4gZmxvYXQgaW52Q2xpcFosIGNvbnN0IGluIGZsb2F0IG5lYXIsIGNvbnN0IGluIGZsb2F0IGZhciApIHtcXG4gIHJldHVybiAoIG5lYXIgKiBmYXIgKSAvICggKCBmYXIgLSBuZWFyICkgKiBpbnZDbGlwWiAtIGZhciApO1xcbn1cXG5cIjtcblxuXHR2YXIgcHJlbXVsdGlwbGllZF9hbHBoYV9mcmFnbWVudCA9IFwiI2lmZGVmIFBSRU1VTFRJUExJRURfQUxQSEFcXG5cXHRnbF9GcmFnQ29sb3IucmdiICo9IGdsX0ZyYWdDb2xvci5hO1xcbiNlbmRpZlxcblwiO1xuXG5cdHZhciBwcm9qZWN0X3ZlcnRleCA9IFwiI2lmZGVmIFVTRV9TS0lOTklOR1xcblxcdHZlYzQgbXZQb3NpdGlvbiA9IG1vZGVsVmlld01hdHJpeCAqIHNraW5uZWQ7XFxuI2Vsc2VcXG5cXHR2ZWM0IG12UG9zaXRpb24gPSBtb2RlbFZpZXdNYXRyaXggKiB2ZWM0KCB0cmFuc2Zvcm1lZCwgMS4wICk7XFxuI2VuZGlmXFxuZ2xfUG9zaXRpb24gPSBwcm9qZWN0aW9uTWF0cml4ICogbXZQb3NpdGlvbjtcXG5cIjtcblxuXHR2YXIgcm91Z2huZXNzbWFwX2ZyYWdtZW50ID0gXCJmbG9hdCByb3VnaG5lc3NGYWN0b3IgPSByb3VnaG5lc3M7XFxuI2lmZGVmIFVTRV9ST1VHSE5FU1NNQVBcXG5cXHR2ZWM0IHRleGVsUm91Z2huZXNzID0gdGV4dHVyZTJEKCByb3VnaG5lc3NNYXAsIHZVdiApO1xcblxcdHJvdWdobmVzc0ZhY3RvciAqPSB0ZXhlbFJvdWdobmVzcy5yO1xcbiNlbmRpZlxcblwiO1xuXG5cdHZhciByb3VnaG5lc3NtYXBfcGFyc19mcmFnbWVudCA9IFwiI2lmZGVmIFVTRV9ST1VHSE5FU1NNQVBcXG5cXHR1bmlmb3JtIHNhbXBsZXIyRCByb3VnaG5lc3NNYXA7XFxuI2VuZGlmXCI7XG5cblx0dmFyIHNoYWRvd21hcF9wYXJzX2ZyYWdtZW50ID0gXCIjaWZkZWYgVVNFX1NIQURPV01BUFxcblxcdCNpZiBOVU1fRElSX0xJR0hUUyA+IDBcXG5cXHRcXHR1bmlmb3JtIHNhbXBsZXIyRCBkaXJlY3Rpb25hbFNoYWRvd01hcFsgTlVNX0RJUl9MSUdIVFMgXTtcXG5cXHRcXHR2YXJ5aW5nIHZlYzQgdkRpcmVjdGlvbmFsU2hhZG93Q29vcmRbIE5VTV9ESVJfTElHSFRTIF07XFxuXFx0I2VuZGlmXFxuXFx0I2lmIE5VTV9TUE9UX0xJR0hUUyA+IDBcXG5cXHRcXHR1bmlmb3JtIHNhbXBsZXIyRCBzcG90U2hhZG93TWFwWyBOVU1fU1BPVF9MSUdIVFMgXTtcXG5cXHRcXHR2YXJ5aW5nIHZlYzQgdlNwb3RTaGFkb3dDb29yZFsgTlVNX1NQT1RfTElHSFRTIF07XFxuXFx0I2VuZGlmXFxuXFx0I2lmIE5VTV9QT0lOVF9MSUdIVFMgPiAwXFxuXFx0XFx0dW5pZm9ybSBzYW1wbGVyMkQgcG9pbnRTaGFkb3dNYXBbIE5VTV9QT0lOVF9MSUdIVFMgXTtcXG5cXHRcXHR2YXJ5aW5nIHZlYzQgdlBvaW50U2hhZG93Q29vcmRbIE5VTV9QT0lOVF9MSUdIVFMgXTtcXG5cXHQjZW5kaWZcXG4gICAgI2lmIE5VTV9SRUNUX0FSRUFfTElHSFRTID4gMFxcbiAgICAjZW5kaWZcXG5cXHRmbG9hdCB0ZXh0dXJlMkRDb21wYXJlKCBzYW1wbGVyMkQgZGVwdGhzLCB2ZWMyIHV2LCBmbG9hdCBjb21wYXJlICkge1xcblxcdFxcdHJldHVybiBzdGVwKCBjb21wYXJlLCB1bnBhY2tSR0JBVG9EZXB0aCggdGV4dHVyZTJEKCBkZXB0aHMsIHV2ICkgKSApO1xcblxcdH1cXG5cXHRmbG9hdCB0ZXh0dXJlMkRTaGFkb3dMZXJwKCBzYW1wbGVyMkQgZGVwdGhzLCB2ZWMyIHNpemUsIHZlYzIgdXYsIGZsb2F0IGNvbXBhcmUgKSB7XFxuXFx0XFx0Y29uc3QgdmVjMiBvZmZzZXQgPSB2ZWMyKCAwLjAsIDEuMCApO1xcblxcdFxcdHZlYzIgdGV4ZWxTaXplID0gdmVjMiggMS4wICkgLyBzaXplO1xcblxcdFxcdHZlYzIgY2VudHJvaWRVViA9IGZsb29yKCB1diAqIHNpemUgKyAwLjUgKSAvIHNpemU7XFxuXFx0XFx0ZmxvYXQgbGIgPSB0ZXh0dXJlMkRDb21wYXJlKCBkZXB0aHMsIGNlbnRyb2lkVVYgKyB0ZXhlbFNpemUgKiBvZmZzZXQueHgsIGNvbXBhcmUgKTtcXG5cXHRcXHRmbG9hdCBsdCA9IHRleHR1cmUyRENvbXBhcmUoIGRlcHRocywgY2VudHJvaWRVViArIHRleGVsU2l6ZSAqIG9mZnNldC54eSwgY29tcGFyZSApO1xcblxcdFxcdGZsb2F0IHJiID0gdGV4dHVyZTJEQ29tcGFyZSggZGVwdGhzLCBjZW50cm9pZFVWICsgdGV4ZWxTaXplICogb2Zmc2V0Lnl4LCBjb21wYXJlICk7XFxuXFx0XFx0ZmxvYXQgcnQgPSB0ZXh0dXJlMkRDb21wYXJlKCBkZXB0aHMsIGNlbnRyb2lkVVYgKyB0ZXhlbFNpemUgKiBvZmZzZXQueXksIGNvbXBhcmUgKTtcXG5cXHRcXHR2ZWMyIGYgPSBmcmFjdCggdXYgKiBzaXplICsgMC41ICk7XFxuXFx0XFx0ZmxvYXQgYSA9IG1peCggbGIsIGx0LCBmLnkgKTtcXG5cXHRcXHRmbG9hdCBiID0gbWl4KCByYiwgcnQsIGYueSApO1xcblxcdFxcdGZsb2F0IGMgPSBtaXgoIGEsIGIsIGYueCApO1xcblxcdFxcdHJldHVybiBjO1xcblxcdH1cXG5cXHRmbG9hdCBnZXRTaGFkb3coIHNhbXBsZXIyRCBzaGFkb3dNYXAsIHZlYzIgc2hhZG93TWFwU2l6ZSwgZmxvYXQgc2hhZG93QmlhcywgZmxvYXQgc2hhZG93UmFkaXVzLCB2ZWM0IHNoYWRvd0Nvb3JkICkge1xcblxcdFxcdHNoYWRvd0Nvb3JkLnh5eiAvPSBzaGFkb3dDb29yZC53O1xcblxcdFxcdHNoYWRvd0Nvb3JkLnogKz0gc2hhZG93QmlhcztcXG5cXHRcXHRidmVjNCBpbkZydXN0dW1WZWMgPSBidmVjNCAoIHNoYWRvd0Nvb3JkLnggPj0gMC4wLCBzaGFkb3dDb29yZC54IDw9IDEuMCwgc2hhZG93Q29vcmQueSA+PSAwLjAsIHNoYWRvd0Nvb3JkLnkgPD0gMS4wICk7XFxuXFx0XFx0Ym9vbCBpbkZydXN0dW0gPSBhbGwoIGluRnJ1c3R1bVZlYyApO1xcblxcdFxcdGJ2ZWMyIGZydXN0dW1UZXN0VmVjID0gYnZlYzIoIGluRnJ1c3R1bSwgc2hhZG93Q29vcmQueiA8PSAxLjAgKTtcXG5cXHRcXHRib29sIGZydXN0dW1UZXN0ID0gYWxsKCBmcnVzdHVtVGVzdFZlYyApO1xcblxcdFxcdGlmICggZnJ1c3R1bVRlc3QgKSB7XFxuXFx0XFx0I2lmIGRlZmluZWQoIFNIQURPV01BUF9UWVBFX1BDRiApXFxuXFx0XFx0XFx0dmVjMiB0ZXhlbFNpemUgPSB2ZWMyKCAxLjAgKSAvIHNoYWRvd01hcFNpemU7XFxuXFx0XFx0XFx0ZmxvYXQgZHgwID0gLSB0ZXhlbFNpemUueCAqIHNoYWRvd1JhZGl1cztcXG5cXHRcXHRcXHRmbG9hdCBkeTAgPSAtIHRleGVsU2l6ZS55ICogc2hhZG93UmFkaXVzO1xcblxcdFxcdFxcdGZsb2F0IGR4MSA9ICsgdGV4ZWxTaXplLnggKiBzaGFkb3dSYWRpdXM7XFxuXFx0XFx0XFx0ZmxvYXQgZHkxID0gKyB0ZXhlbFNpemUueSAqIHNoYWRvd1JhZGl1cztcXG5cXHRcXHRcXHRyZXR1cm4gKFxcblxcdFxcdFxcdFxcdHRleHR1cmUyRENvbXBhcmUoIHNoYWRvd01hcCwgc2hhZG93Q29vcmQueHkgKyB2ZWMyKCBkeDAsIGR5MCApLCBzaGFkb3dDb29yZC56ICkgK1xcblxcdFxcdFxcdFxcdHRleHR1cmUyRENvbXBhcmUoIHNoYWRvd01hcCwgc2hhZG93Q29vcmQueHkgKyB2ZWMyKCAwLjAsIGR5MCApLCBzaGFkb3dDb29yZC56ICkgK1xcblxcdFxcdFxcdFxcdHRleHR1cmUyRENvbXBhcmUoIHNoYWRvd01hcCwgc2hhZG93Q29vcmQueHkgKyB2ZWMyKCBkeDEsIGR5MCApLCBzaGFkb3dDb29yZC56ICkgK1xcblxcdFxcdFxcdFxcdHRleHR1cmUyRENvbXBhcmUoIHNoYWRvd01hcCwgc2hhZG93Q29vcmQueHkgKyB2ZWMyKCBkeDAsIDAuMCApLCBzaGFkb3dDb29yZC56ICkgK1xcblxcdFxcdFxcdFxcdHRleHR1cmUyRENvbXBhcmUoIHNoYWRvd01hcCwgc2hhZG93Q29vcmQueHksIHNoYWRvd0Nvb3JkLnogKSArXFxuXFx0XFx0XFx0XFx0dGV4dHVyZTJEQ29tcGFyZSggc2hhZG93TWFwLCBzaGFkb3dDb29yZC54eSArIHZlYzIoIGR4MSwgMC4wICksIHNoYWRvd0Nvb3JkLnogKSArXFxuXFx0XFx0XFx0XFx0dGV4dHVyZTJEQ29tcGFyZSggc2hhZG93TWFwLCBzaGFkb3dDb29yZC54eSArIHZlYzIoIGR4MCwgZHkxICksIHNoYWRvd0Nvb3JkLnogKSArXFxuXFx0XFx0XFx0XFx0dGV4dHVyZTJEQ29tcGFyZSggc2hhZG93TWFwLCBzaGFkb3dDb29yZC54eSArIHZlYzIoIDAuMCwgZHkxICksIHNoYWRvd0Nvb3JkLnogKSArXFxuXFx0XFx0XFx0XFx0dGV4dHVyZTJEQ29tcGFyZSggc2hhZG93TWFwLCBzaGFkb3dDb29yZC54eSArIHZlYzIoIGR4MSwgZHkxICksIHNoYWRvd0Nvb3JkLnogKVxcblxcdFxcdFxcdCkgKiAoIDEuMCAvIDkuMCApO1xcblxcdFxcdCNlbGlmIGRlZmluZWQoIFNIQURPV01BUF9UWVBFX1BDRl9TT0ZUIClcXG5cXHRcXHRcXHR2ZWMyIHRleGVsU2l6ZSA9IHZlYzIoIDEuMCApIC8gc2hhZG93TWFwU2l6ZTtcXG5cXHRcXHRcXHRmbG9hdCBkeDAgPSAtIHRleGVsU2l6ZS54ICogc2hhZG93UmFkaXVzO1xcblxcdFxcdFxcdGZsb2F0IGR5MCA9IC0gdGV4ZWxTaXplLnkgKiBzaGFkb3dSYWRpdXM7XFxuXFx0XFx0XFx0ZmxvYXQgZHgxID0gKyB0ZXhlbFNpemUueCAqIHNoYWRvd1JhZGl1cztcXG5cXHRcXHRcXHRmbG9hdCBkeTEgPSArIHRleGVsU2l6ZS55ICogc2hhZG93UmFkaXVzO1xcblxcdFxcdFxcdHJldHVybiAoXFxuXFx0XFx0XFx0XFx0dGV4dHVyZTJEU2hhZG93TGVycCggc2hhZG93TWFwLCBzaGFkb3dNYXBTaXplLCBzaGFkb3dDb29yZC54eSArIHZlYzIoIGR4MCwgZHkwICksIHNoYWRvd0Nvb3JkLnogKSArXFxuXFx0XFx0XFx0XFx0dGV4dHVyZTJEU2hhZG93TGVycCggc2hhZG93TWFwLCBzaGFkb3dNYXBTaXplLCBzaGFkb3dDb29yZC54eSArIHZlYzIoIDAuMCwgZHkwICksIHNoYWRvd0Nvb3JkLnogKSArXFxuXFx0XFx0XFx0XFx0dGV4dHVyZTJEU2hhZG93TGVycCggc2hhZG93TWFwLCBzaGFkb3dNYXBTaXplLCBzaGFkb3dDb29yZC54eSArIHZlYzIoIGR4MSwgZHkwICksIHNoYWRvd0Nvb3JkLnogKSArXFxuXFx0XFx0XFx0XFx0dGV4dHVyZTJEU2hhZG93TGVycCggc2hhZG93TWFwLCBzaGFkb3dNYXBTaXplLCBzaGFkb3dDb29yZC54eSArIHZlYzIoIGR4MCwgMC4wICksIHNoYWRvd0Nvb3JkLnogKSArXFxuXFx0XFx0XFx0XFx0dGV4dHVyZTJEU2hhZG93TGVycCggc2hhZG93TWFwLCBzaGFkb3dNYXBTaXplLCBzaGFkb3dDb29yZC54eSwgc2hhZG93Q29vcmQueiApICtcXG5cXHRcXHRcXHRcXHR0ZXh0dXJlMkRTaGFkb3dMZXJwKCBzaGFkb3dNYXAsIHNoYWRvd01hcFNpemUsIHNoYWRvd0Nvb3JkLnh5ICsgdmVjMiggZHgxLCAwLjAgKSwgc2hhZG93Q29vcmQueiApICtcXG5cXHRcXHRcXHRcXHR0ZXh0dXJlMkRTaGFkb3dMZXJwKCBzaGFkb3dNYXAsIHNoYWRvd01hcFNpemUsIHNoYWRvd0Nvb3JkLnh5ICsgdmVjMiggZHgwLCBkeTEgKSwgc2hhZG93Q29vcmQueiApICtcXG5cXHRcXHRcXHRcXHR0ZXh0dXJlMkRTaGFkb3dMZXJwKCBzaGFkb3dNYXAsIHNoYWRvd01hcFNpemUsIHNoYWRvd0Nvb3JkLnh5ICsgdmVjMiggMC4wLCBkeTEgKSwgc2hhZG93Q29vcmQueiApICtcXG5cXHRcXHRcXHRcXHR0ZXh0dXJlMkRTaGFkb3dMZXJwKCBzaGFkb3dNYXAsIHNoYWRvd01hcFNpemUsIHNoYWRvd0Nvb3JkLnh5ICsgdmVjMiggZHgxLCBkeTEgKSwgc2hhZG93Q29vcmQueiApXFxuXFx0XFx0XFx0KSAqICggMS4wIC8gOS4wICk7XFxuXFx0XFx0I2Vsc2VcXG5cXHRcXHRcXHRyZXR1cm4gdGV4dHVyZTJEQ29tcGFyZSggc2hhZG93TWFwLCBzaGFkb3dDb29yZC54eSwgc2hhZG93Q29vcmQueiApO1xcblxcdFxcdCNlbmRpZlxcblxcdFxcdH1cXG5cXHRcXHRyZXR1cm4gMS4wO1xcblxcdH1cXG5cXHR2ZWMyIGN1YmVUb1VWKCB2ZWMzIHYsIGZsb2F0IHRleGVsU2l6ZVkgKSB7XFxuXFx0XFx0dmVjMyBhYnNWID0gYWJzKCB2ICk7XFxuXFx0XFx0ZmxvYXQgc2NhbGVUb0N1YmUgPSAxLjAgLyBtYXgoIGFic1YueCwgbWF4KCBhYnNWLnksIGFic1YueiApICk7XFxuXFx0XFx0YWJzViAqPSBzY2FsZVRvQ3ViZTtcXG5cXHRcXHR2ICo9IHNjYWxlVG9DdWJlICogKCAxLjAgLSAyLjAgKiB0ZXhlbFNpemVZICk7XFxuXFx0XFx0dmVjMiBwbGFuYXIgPSB2Lnh5O1xcblxcdFxcdGZsb2F0IGFsbW9zdEFUZXhlbCA9IDEuNSAqIHRleGVsU2l6ZVk7XFxuXFx0XFx0ZmxvYXQgYWxtb3N0T25lID0gMS4wIC0gYWxtb3N0QVRleGVsO1xcblxcdFxcdGlmICggYWJzVi56ID49IGFsbW9zdE9uZSApIHtcXG5cXHRcXHRcXHRpZiAoIHYueiA+IDAuMCApXFxuXFx0XFx0XFx0XFx0cGxhbmFyLnggPSA0LjAgLSB2Lng7XFxuXFx0XFx0fSBlbHNlIGlmICggYWJzVi54ID49IGFsbW9zdE9uZSApIHtcXG5cXHRcXHRcXHRmbG9hdCBzaWduWCA9IHNpZ24oIHYueCApO1xcblxcdFxcdFxcdHBsYW5hci54ID0gdi56ICogc2lnblggKyAyLjAgKiBzaWduWDtcXG5cXHRcXHR9IGVsc2UgaWYgKCBhYnNWLnkgPj0gYWxtb3N0T25lICkge1xcblxcdFxcdFxcdGZsb2F0IHNpZ25ZID0gc2lnbiggdi55ICk7XFxuXFx0XFx0XFx0cGxhbmFyLnggPSB2LnggKyAyLjAgKiBzaWduWSArIDIuMDtcXG5cXHRcXHRcXHRwbGFuYXIueSA9IHYueiAqIHNpZ25ZIC0gMi4wO1xcblxcdFxcdH1cXG5cXHRcXHRyZXR1cm4gdmVjMiggMC4xMjUsIDAuMjUgKSAqIHBsYW5hciArIHZlYzIoIDAuMzc1LCAwLjc1ICk7XFxuXFx0fVxcblxcdGZsb2F0IGdldFBvaW50U2hhZG93KCBzYW1wbGVyMkQgc2hhZG93TWFwLCB2ZWMyIHNoYWRvd01hcFNpemUsIGZsb2F0IHNoYWRvd0JpYXMsIGZsb2F0IHNoYWRvd1JhZGl1cywgdmVjNCBzaGFkb3dDb29yZCApIHtcXG5cXHRcXHR2ZWMyIHRleGVsU2l6ZSA9IHZlYzIoIDEuMCApIC8gKCBzaGFkb3dNYXBTaXplICogdmVjMiggNC4wLCAyLjAgKSApO1xcblxcdFxcdHZlYzMgbGlnaHRUb1Bvc2l0aW9uID0gc2hhZG93Q29vcmQueHl6O1xcblxcdFxcdHZlYzMgYmQzRCA9IG5vcm1hbGl6ZSggbGlnaHRUb1Bvc2l0aW9uICk7XFxuXFx0XFx0ZmxvYXQgZHAgPSAoIGxlbmd0aCggbGlnaHRUb1Bvc2l0aW9uICkgLSBzaGFkb3dCaWFzICkgLyAxMDAwLjA7XFxuXFx0XFx0I2lmIGRlZmluZWQoIFNIQURPV01BUF9UWVBFX1BDRiApIHx8IGRlZmluZWQoIFNIQURPV01BUF9UWVBFX1BDRl9TT0ZUIClcXG5cXHRcXHRcXHR2ZWMyIG9mZnNldCA9IHZlYzIoIC0gMSwgMSApICogc2hhZG93UmFkaXVzICogdGV4ZWxTaXplLnk7XFxuXFx0XFx0XFx0cmV0dXJuIChcXG5cXHRcXHRcXHRcXHR0ZXh0dXJlMkRDb21wYXJlKCBzaGFkb3dNYXAsIGN1YmVUb1VWKCBiZDNEICsgb2Zmc2V0Lnh5eSwgdGV4ZWxTaXplLnkgKSwgZHAgKSArXFxuXFx0XFx0XFx0XFx0dGV4dHVyZTJEQ29tcGFyZSggc2hhZG93TWFwLCBjdWJlVG9VViggYmQzRCArIG9mZnNldC55eXksIHRleGVsU2l6ZS55ICksIGRwICkgK1xcblxcdFxcdFxcdFxcdHRleHR1cmUyRENvbXBhcmUoIHNoYWRvd01hcCwgY3ViZVRvVVYoIGJkM0QgKyBvZmZzZXQueHl4LCB0ZXhlbFNpemUueSApLCBkcCApICtcXG5cXHRcXHRcXHRcXHR0ZXh0dXJlMkRDb21wYXJlKCBzaGFkb3dNYXAsIGN1YmVUb1VWKCBiZDNEICsgb2Zmc2V0Lnl5eCwgdGV4ZWxTaXplLnkgKSwgZHAgKSArXFxuXFx0XFx0XFx0XFx0dGV4dHVyZTJEQ29tcGFyZSggc2hhZG93TWFwLCBjdWJlVG9VViggYmQzRCwgdGV4ZWxTaXplLnkgKSwgZHAgKSArXFxuXFx0XFx0XFx0XFx0dGV4dHVyZTJEQ29tcGFyZSggc2hhZG93TWFwLCBjdWJlVG9VViggYmQzRCArIG9mZnNldC54eHksIHRleGVsU2l6ZS55ICksIGRwICkgK1xcblxcdFxcdFxcdFxcdHRleHR1cmUyRENvbXBhcmUoIHNoYWRvd01hcCwgY3ViZVRvVVYoIGJkM0QgKyBvZmZzZXQueXh5LCB0ZXhlbFNpemUueSApLCBkcCApICtcXG5cXHRcXHRcXHRcXHR0ZXh0dXJlMkRDb21wYXJlKCBzaGFkb3dNYXAsIGN1YmVUb1VWKCBiZDNEICsgb2Zmc2V0Lnh4eCwgdGV4ZWxTaXplLnkgKSwgZHAgKSArXFxuXFx0XFx0XFx0XFx0dGV4dHVyZTJEQ29tcGFyZSggc2hhZG93TWFwLCBjdWJlVG9VViggYmQzRCArIG9mZnNldC55eHgsIHRleGVsU2l6ZS55ICksIGRwIClcXG5cXHRcXHRcXHQpICogKCAxLjAgLyA5LjAgKTtcXG5cXHRcXHQjZWxzZVxcblxcdFxcdFxcdHJldHVybiB0ZXh0dXJlMkRDb21wYXJlKCBzaGFkb3dNYXAsIGN1YmVUb1VWKCBiZDNELCB0ZXhlbFNpemUueSApLCBkcCApO1xcblxcdFxcdCNlbmRpZlxcblxcdH1cXG4jZW5kaWZcXG5cIjtcblxuXHR2YXIgc2hhZG93bWFwX3BhcnNfdmVydGV4ID0gXCIjaWZkZWYgVVNFX1NIQURPV01BUFxcblxcdCNpZiBOVU1fRElSX0xJR0hUUyA+IDBcXG5cXHRcXHR1bmlmb3JtIG1hdDQgZGlyZWN0aW9uYWxTaGFkb3dNYXRyaXhbIE5VTV9ESVJfTElHSFRTIF07XFxuXFx0XFx0dmFyeWluZyB2ZWM0IHZEaXJlY3Rpb25hbFNoYWRvd0Nvb3JkWyBOVU1fRElSX0xJR0hUUyBdO1xcblxcdCNlbmRpZlxcblxcdCNpZiBOVU1fU1BPVF9MSUdIVFMgPiAwXFxuXFx0XFx0dW5pZm9ybSBtYXQ0IHNwb3RTaGFkb3dNYXRyaXhbIE5VTV9TUE9UX0xJR0hUUyBdO1xcblxcdFxcdHZhcnlpbmcgdmVjNCB2U3BvdFNoYWRvd0Nvb3JkWyBOVU1fU1BPVF9MSUdIVFMgXTtcXG5cXHQjZW5kaWZcXG5cXHQjaWYgTlVNX1BPSU5UX0xJR0hUUyA+IDBcXG5cXHRcXHR1bmlmb3JtIG1hdDQgcG9pbnRTaGFkb3dNYXRyaXhbIE5VTV9QT0lOVF9MSUdIVFMgXTtcXG5cXHRcXHR2YXJ5aW5nIHZlYzQgdlBvaW50U2hhZG93Q29vcmRbIE5VTV9QT0lOVF9MSUdIVFMgXTtcXG5cXHQjZW5kaWZcXG4gICAgI2lmIE5VTV9SRUNUX0FSRUFfTElHSFRTID4gMFxcbiAgICAjZW5kaWZcXG4jZW5kaWZcXG5cIjtcblxuXHR2YXIgc2hhZG93bWFwX3ZlcnRleCA9IFwiI2lmZGVmIFVTRV9TSEFET1dNQVBcXG5cXHQjaWYgTlVNX0RJUl9MSUdIVFMgPiAwXFxuXFx0Zm9yICggaW50IGkgPSAwOyBpIDwgTlVNX0RJUl9MSUdIVFM7IGkgKysgKSB7XFxuXFx0XFx0dkRpcmVjdGlvbmFsU2hhZG93Q29vcmRbIGkgXSA9IGRpcmVjdGlvbmFsU2hhZG93TWF0cml4WyBpIF0gKiB3b3JsZFBvc2l0aW9uO1xcblxcdH1cXG5cXHQjZW5kaWZcXG5cXHQjaWYgTlVNX1NQT1RfTElHSFRTID4gMFxcblxcdGZvciAoIGludCBpID0gMDsgaSA8IE5VTV9TUE9UX0xJR0hUUzsgaSArKyApIHtcXG5cXHRcXHR2U3BvdFNoYWRvd0Nvb3JkWyBpIF0gPSBzcG90U2hhZG93TWF0cml4WyBpIF0gKiB3b3JsZFBvc2l0aW9uO1xcblxcdH1cXG5cXHQjZW5kaWZcXG5cXHQjaWYgTlVNX1BPSU5UX0xJR0hUUyA+IDBcXG5cXHRmb3IgKCBpbnQgaSA9IDA7IGkgPCBOVU1fUE9JTlRfTElHSFRTOyBpICsrICkge1xcblxcdFxcdHZQb2ludFNoYWRvd0Nvb3JkWyBpIF0gPSBwb2ludFNoYWRvd01hdHJpeFsgaSBdICogd29ybGRQb3NpdGlvbjtcXG5cXHR9XFxuXFx0I2VuZGlmXFxuICAgICNpZiBOVU1fUkVDVF9BUkVBX0xJR0hUUyA+IDBcXG4gICAgI2VuZGlmXFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIHNoYWRvd21hc2tfcGFyc19mcmFnbWVudCA9IFwiZmxvYXQgZ2V0U2hhZG93TWFzaygpIHtcXG5cXHRmbG9hdCBzaGFkb3cgPSAxLjA7XFxuXFx0I2lmZGVmIFVTRV9TSEFET1dNQVBcXG5cXHQjaWYgTlVNX0RJUl9MSUdIVFMgPiAwXFxuXFx0RGlyZWN0aW9uYWxMaWdodCBkaXJlY3Rpb25hbExpZ2h0O1xcblxcdGZvciAoIGludCBpID0gMDsgaSA8IE5VTV9ESVJfTElHSFRTOyBpICsrICkge1xcblxcdFxcdGRpcmVjdGlvbmFsTGlnaHQgPSBkaXJlY3Rpb25hbExpZ2h0c1sgaSBdO1xcblxcdFxcdHNoYWRvdyAqPSBib29sKCBkaXJlY3Rpb25hbExpZ2h0LnNoYWRvdyApID8gZ2V0U2hhZG93KCBkaXJlY3Rpb25hbFNoYWRvd01hcFsgaSBdLCBkaXJlY3Rpb25hbExpZ2h0LnNoYWRvd01hcFNpemUsIGRpcmVjdGlvbmFsTGlnaHQuc2hhZG93QmlhcywgZGlyZWN0aW9uYWxMaWdodC5zaGFkb3dSYWRpdXMsIHZEaXJlY3Rpb25hbFNoYWRvd0Nvb3JkWyBpIF0gKSA6IDEuMDtcXG5cXHR9XFxuXFx0I2VuZGlmXFxuXFx0I2lmIE5VTV9TUE9UX0xJR0hUUyA+IDBcXG5cXHRTcG90TGlnaHQgc3BvdExpZ2h0O1xcblxcdGZvciAoIGludCBpID0gMDsgaSA8IE5VTV9TUE9UX0xJR0hUUzsgaSArKyApIHtcXG5cXHRcXHRzcG90TGlnaHQgPSBzcG90TGlnaHRzWyBpIF07XFxuXFx0XFx0c2hhZG93ICo9IGJvb2woIHNwb3RMaWdodC5zaGFkb3cgKSA/IGdldFNoYWRvdyggc3BvdFNoYWRvd01hcFsgaSBdLCBzcG90TGlnaHQuc2hhZG93TWFwU2l6ZSwgc3BvdExpZ2h0LnNoYWRvd0JpYXMsIHNwb3RMaWdodC5zaGFkb3dSYWRpdXMsIHZTcG90U2hhZG93Q29vcmRbIGkgXSApIDogMS4wO1xcblxcdH1cXG5cXHQjZW5kaWZcXG5cXHQjaWYgTlVNX1BPSU5UX0xJR0hUUyA+IDBcXG5cXHRQb2ludExpZ2h0IHBvaW50TGlnaHQ7XFxuXFx0Zm9yICggaW50IGkgPSAwOyBpIDwgTlVNX1BPSU5UX0xJR0hUUzsgaSArKyApIHtcXG5cXHRcXHRwb2ludExpZ2h0ID0gcG9pbnRMaWdodHNbIGkgXTtcXG5cXHRcXHRzaGFkb3cgKj0gYm9vbCggcG9pbnRMaWdodC5zaGFkb3cgKSA/IGdldFBvaW50U2hhZG93KCBwb2ludFNoYWRvd01hcFsgaSBdLCBwb2ludExpZ2h0LnNoYWRvd01hcFNpemUsIHBvaW50TGlnaHQuc2hhZG93QmlhcywgcG9pbnRMaWdodC5zaGFkb3dSYWRpdXMsIHZQb2ludFNoYWRvd0Nvb3JkWyBpIF0gKSA6IDEuMDtcXG5cXHR9XFxuXFx0I2VuZGlmXFxuXFx0I2lmIE5VTV9SRUNUX0FSRUFfTElHSFRTID4gMFxcblxcdCNlbmRpZlxcblxcdCNlbmRpZlxcblxcdHJldHVybiBzaGFkb3c7XFxufVxcblwiO1xuXG5cdHZhciBza2luYmFzZV92ZXJ0ZXggPSBcIiNpZmRlZiBVU0VfU0tJTk5JTkdcXG5cXHRtYXQ0IGJvbmVNYXRYID0gZ2V0Qm9uZU1hdHJpeCggc2tpbkluZGV4LnggKTtcXG5cXHRtYXQ0IGJvbmVNYXRZID0gZ2V0Qm9uZU1hdHJpeCggc2tpbkluZGV4LnkgKTtcXG5cXHRtYXQ0IGJvbmVNYXRaID0gZ2V0Qm9uZU1hdHJpeCggc2tpbkluZGV4LnogKTtcXG5cXHRtYXQ0IGJvbmVNYXRXID0gZ2V0Qm9uZU1hdHJpeCggc2tpbkluZGV4LncgKTtcXG4jZW5kaWZcIjtcblxuXHR2YXIgc2tpbm5pbmdfcGFyc192ZXJ0ZXggPSBcIiNpZmRlZiBVU0VfU0tJTk5JTkdcXG5cXHR1bmlmb3JtIG1hdDQgYmluZE1hdHJpeDtcXG5cXHR1bmlmb3JtIG1hdDQgYmluZE1hdHJpeEludmVyc2U7XFxuXFx0I2lmZGVmIEJPTkVfVEVYVFVSRVxcblxcdFxcdHVuaWZvcm0gc2FtcGxlcjJEIGJvbmVUZXh0dXJlO1xcblxcdFxcdHVuaWZvcm0gaW50IGJvbmVUZXh0dXJlV2lkdGg7XFxuXFx0XFx0dW5pZm9ybSBpbnQgYm9uZVRleHR1cmVIZWlnaHQ7XFxuXFx0XFx0bWF0NCBnZXRCb25lTWF0cml4KCBjb25zdCBpbiBmbG9hdCBpICkge1xcblxcdFxcdFxcdGZsb2F0IGogPSBpICogNC4wO1xcblxcdFxcdFxcdGZsb2F0IHggPSBtb2QoIGosIGZsb2F0KCBib25lVGV4dHVyZVdpZHRoICkgKTtcXG5cXHRcXHRcXHRmbG9hdCB5ID0gZmxvb3IoIGogLyBmbG9hdCggYm9uZVRleHR1cmVXaWR0aCApICk7XFxuXFx0XFx0XFx0ZmxvYXQgZHggPSAxLjAgLyBmbG9hdCggYm9uZVRleHR1cmVXaWR0aCApO1xcblxcdFxcdFxcdGZsb2F0IGR5ID0gMS4wIC8gZmxvYXQoIGJvbmVUZXh0dXJlSGVpZ2h0ICk7XFxuXFx0XFx0XFx0eSA9IGR5ICogKCB5ICsgMC41ICk7XFxuXFx0XFx0XFx0dmVjNCB2MSA9IHRleHR1cmUyRCggYm9uZVRleHR1cmUsIHZlYzIoIGR4ICogKCB4ICsgMC41ICksIHkgKSApO1xcblxcdFxcdFxcdHZlYzQgdjIgPSB0ZXh0dXJlMkQoIGJvbmVUZXh0dXJlLCB2ZWMyKCBkeCAqICggeCArIDEuNSApLCB5ICkgKTtcXG5cXHRcXHRcXHR2ZWM0IHYzID0gdGV4dHVyZTJEKCBib25lVGV4dHVyZSwgdmVjMiggZHggKiAoIHggKyAyLjUgKSwgeSApICk7XFxuXFx0XFx0XFx0dmVjNCB2NCA9IHRleHR1cmUyRCggYm9uZVRleHR1cmUsIHZlYzIoIGR4ICogKCB4ICsgMy41ICksIHkgKSApO1xcblxcdFxcdFxcdG1hdDQgYm9uZSA9IG1hdDQoIHYxLCB2MiwgdjMsIHY0ICk7XFxuXFx0XFx0XFx0cmV0dXJuIGJvbmU7XFxuXFx0XFx0fVxcblxcdCNlbHNlXFxuXFx0XFx0dW5pZm9ybSBtYXQ0IGJvbmVNYXRyaWNlc1sgTUFYX0JPTkVTIF07XFxuXFx0XFx0bWF0NCBnZXRCb25lTWF0cml4KCBjb25zdCBpbiBmbG9hdCBpICkge1xcblxcdFxcdFxcdG1hdDQgYm9uZSA9IGJvbmVNYXRyaWNlc1sgaW50KGkpIF07XFxuXFx0XFx0XFx0cmV0dXJuIGJvbmU7XFxuXFx0XFx0fVxcblxcdCNlbmRpZlxcbiNlbmRpZlxcblwiO1xuXG5cdHZhciBza2lubmluZ192ZXJ0ZXggPSBcIiNpZmRlZiBVU0VfU0tJTk5JTkdcXG5cXHR2ZWM0IHNraW5WZXJ0ZXggPSBiaW5kTWF0cml4ICogdmVjNCggdHJhbnNmb3JtZWQsIDEuMCApO1xcblxcdHZlYzQgc2tpbm5lZCA9IHZlYzQoIDAuMCApO1xcblxcdHNraW5uZWQgKz0gYm9uZU1hdFggKiBza2luVmVydGV4ICogc2tpbldlaWdodC54O1xcblxcdHNraW5uZWQgKz0gYm9uZU1hdFkgKiBza2luVmVydGV4ICogc2tpbldlaWdodC55O1xcblxcdHNraW5uZWQgKz0gYm9uZU1hdFogKiBza2luVmVydGV4ICogc2tpbldlaWdodC56O1xcblxcdHNraW5uZWQgKz0gYm9uZU1hdFcgKiBza2luVmVydGV4ICogc2tpbldlaWdodC53O1xcblxcdHNraW5uZWQgID0gYmluZE1hdHJpeEludmVyc2UgKiBza2lubmVkO1xcbiNlbmRpZlxcblwiO1xuXG5cdHZhciBza2lubm9ybWFsX3ZlcnRleCA9IFwiI2lmZGVmIFVTRV9TS0lOTklOR1xcblxcdG1hdDQgc2tpbk1hdHJpeCA9IG1hdDQoIDAuMCApO1xcblxcdHNraW5NYXRyaXggKz0gc2tpbldlaWdodC54ICogYm9uZU1hdFg7XFxuXFx0c2tpbk1hdHJpeCArPSBza2luV2VpZ2h0LnkgKiBib25lTWF0WTtcXG5cXHRza2luTWF0cml4ICs9IHNraW5XZWlnaHQueiAqIGJvbmVNYXRaO1xcblxcdHNraW5NYXRyaXggKz0gc2tpbldlaWdodC53ICogYm9uZU1hdFc7XFxuXFx0c2tpbk1hdHJpeCAgPSBiaW5kTWF0cml4SW52ZXJzZSAqIHNraW5NYXRyaXggKiBiaW5kTWF0cml4O1xcblxcdG9iamVjdE5vcm1hbCA9IHZlYzQoIHNraW5NYXRyaXggKiB2ZWM0KCBvYmplY3ROb3JtYWwsIDAuMCApICkueHl6O1xcbiNlbmRpZlxcblwiO1xuXG5cdHZhciBzcGVjdWxhcm1hcF9mcmFnbWVudCA9IFwiZmxvYXQgc3BlY3VsYXJTdHJlbmd0aDtcXG4jaWZkZWYgVVNFX1NQRUNVTEFSTUFQXFxuXFx0dmVjNCB0ZXhlbFNwZWN1bGFyID0gdGV4dHVyZTJEKCBzcGVjdWxhck1hcCwgdlV2ICk7XFxuXFx0c3BlY3VsYXJTdHJlbmd0aCA9IHRleGVsU3BlY3VsYXIucjtcXG4jZWxzZVxcblxcdHNwZWN1bGFyU3RyZW5ndGggPSAxLjA7XFxuI2VuZGlmXCI7XG5cblx0dmFyIHNwZWN1bGFybWFwX3BhcnNfZnJhZ21lbnQgPSBcIiNpZmRlZiBVU0VfU1BFQ1VMQVJNQVBcXG5cXHR1bmlmb3JtIHNhbXBsZXIyRCBzcGVjdWxhck1hcDtcXG4jZW5kaWZcIjtcblxuXHR2YXIgdG9uZW1hcHBpbmdfZnJhZ21lbnQgPSBcIiNpZiBkZWZpbmVkKCBUT05FX01BUFBJTkcgKVxcbiAgZ2xfRnJhZ0NvbG9yLnJnYiA9IHRvbmVNYXBwaW5nKCBnbF9GcmFnQ29sb3IucmdiICk7XFxuI2VuZGlmXFxuXCI7XG5cblx0dmFyIHRvbmVtYXBwaW5nX3BhcnNfZnJhZ21lbnQgPSBcIiNkZWZpbmUgc2F0dXJhdGUoYSkgY2xhbXAoIGEsIDAuMCwgMS4wIClcXG51bmlmb3JtIGZsb2F0IHRvbmVNYXBwaW5nRXhwb3N1cmU7XFxudW5pZm9ybSBmbG9hdCB0b25lTWFwcGluZ1doaXRlUG9pbnQ7XFxudmVjMyBMaW5lYXJUb25lTWFwcGluZyggdmVjMyBjb2xvciApIHtcXG4gIHJldHVybiB0b25lTWFwcGluZ0V4cG9zdXJlICogY29sb3I7XFxufVxcbnZlYzMgUmVpbmhhcmRUb25lTWFwcGluZyggdmVjMyBjb2xvciApIHtcXG4gIGNvbG9yICo9IHRvbmVNYXBwaW5nRXhwb3N1cmU7XFxuICByZXR1cm4gc2F0dXJhdGUoIGNvbG9yIC8gKCB2ZWMzKCAxLjAgKSArIGNvbG9yICkgKTtcXG59XFxuI2RlZmluZSBVbmNoYXJ0ZWQySGVscGVyKCB4ICkgbWF4KCAoICggeCAqICggMC4xNSAqIHggKyAwLjEwICogMC41MCApICsgMC4yMCAqIDAuMDIgKSAvICggeCAqICggMC4xNSAqIHggKyAwLjUwICkgKyAwLjIwICogMC4zMCApICkgLSAwLjAyIC8gMC4zMCwgdmVjMyggMC4wICkgKVxcbnZlYzMgVW5jaGFydGVkMlRvbmVNYXBwaW5nKCB2ZWMzIGNvbG9yICkge1xcbiAgY29sb3IgKj0gdG9uZU1hcHBpbmdFeHBvc3VyZTtcXG4gIHJldHVybiBzYXR1cmF0ZSggVW5jaGFydGVkMkhlbHBlciggY29sb3IgKSAvIFVuY2hhcnRlZDJIZWxwZXIoIHZlYzMoIHRvbmVNYXBwaW5nV2hpdGVQb2ludCApICkgKTtcXG59XFxudmVjMyBPcHRpbWl6ZWRDaW5lb25Ub25lTWFwcGluZyggdmVjMyBjb2xvciApIHtcXG4gIGNvbG9yICo9IHRvbmVNYXBwaW5nRXhwb3N1cmU7XFxuICBjb2xvciA9IG1heCggdmVjMyggMC4wICksIGNvbG9yIC0gMC4wMDQgKTtcXG4gIHJldHVybiBwb3coICggY29sb3IgKiAoIDYuMiAqIGNvbG9yICsgMC41ICkgKSAvICggY29sb3IgKiAoIDYuMiAqIGNvbG9yICsgMS43ICkgKyAwLjA2ICksIHZlYzMoIDIuMiApICk7XFxufVxcblwiO1xuXG5cdHZhciB1dl9wYXJzX2ZyYWdtZW50ID0gXCIjaWYgZGVmaW5lZCggVVNFX01BUCApIHx8IGRlZmluZWQoIFVTRV9CVU1QTUFQICkgfHwgZGVmaW5lZCggVVNFX05PUk1BTE1BUCApIHx8IGRlZmluZWQoIFVTRV9TUEVDVUxBUk1BUCApIHx8IGRlZmluZWQoIFVTRV9BTFBIQU1BUCApIHx8IGRlZmluZWQoIFVTRV9FTUlTU0lWRU1BUCApIHx8IGRlZmluZWQoIFVTRV9ST1VHSE5FU1NNQVAgKSB8fCBkZWZpbmVkKCBVU0VfTUVUQUxORVNTTUFQIClcXG5cXHR2YXJ5aW5nIHZlYzIgdlV2O1xcbiNlbmRpZlwiO1xuXG5cdHZhciB1dl9wYXJzX3ZlcnRleCA9IFwiI2lmIGRlZmluZWQoIFVTRV9NQVAgKSB8fCBkZWZpbmVkKCBVU0VfQlVNUE1BUCApIHx8IGRlZmluZWQoIFVTRV9OT1JNQUxNQVAgKSB8fCBkZWZpbmVkKCBVU0VfU1BFQ1VMQVJNQVAgKSB8fCBkZWZpbmVkKCBVU0VfQUxQSEFNQVAgKSB8fCBkZWZpbmVkKCBVU0VfRU1JU1NJVkVNQVAgKSB8fCBkZWZpbmVkKCBVU0VfUk9VR0hORVNTTUFQICkgfHwgZGVmaW5lZCggVVNFX01FVEFMTkVTU01BUCApXFxuXFx0dmFyeWluZyB2ZWMyIHZVdjtcXG5cXHR1bmlmb3JtIHZlYzQgb2Zmc2V0UmVwZWF0O1xcbiNlbmRpZlxcblwiO1xuXG5cdHZhciB1dl92ZXJ0ZXggPSBcIiNpZiBkZWZpbmVkKCBVU0VfTUFQICkgfHwgZGVmaW5lZCggVVNFX0JVTVBNQVAgKSB8fCBkZWZpbmVkKCBVU0VfTk9STUFMTUFQICkgfHwgZGVmaW5lZCggVVNFX1NQRUNVTEFSTUFQICkgfHwgZGVmaW5lZCggVVNFX0FMUEhBTUFQICkgfHwgZGVmaW5lZCggVVNFX0VNSVNTSVZFTUFQICkgfHwgZGVmaW5lZCggVVNFX1JPVUdITkVTU01BUCApIHx8IGRlZmluZWQoIFVTRV9NRVRBTE5FU1NNQVAgKVxcblxcdHZVdiA9IHV2ICogb2Zmc2V0UmVwZWF0Lnp3ICsgb2Zmc2V0UmVwZWF0Lnh5O1xcbiNlbmRpZlwiO1xuXG5cdHZhciB1djJfcGFyc19mcmFnbWVudCA9IFwiI2lmIGRlZmluZWQoIFVTRV9MSUdIVE1BUCApIHx8IGRlZmluZWQoIFVTRV9BT01BUCApXFxuXFx0dmFyeWluZyB2ZWMyIHZVdjI7XFxuI2VuZGlmXCI7XG5cblx0dmFyIHV2Ml9wYXJzX3ZlcnRleCA9IFwiI2lmIGRlZmluZWQoIFVTRV9MSUdIVE1BUCApIHx8IGRlZmluZWQoIFVTRV9BT01BUCApXFxuXFx0YXR0cmlidXRlIHZlYzIgdXYyO1xcblxcdHZhcnlpbmcgdmVjMiB2VXYyO1xcbiNlbmRpZlwiO1xuXG5cdHZhciB1djJfdmVydGV4ID0gXCIjaWYgZGVmaW5lZCggVVNFX0xJR0hUTUFQICkgfHwgZGVmaW5lZCggVVNFX0FPTUFQIClcXG5cXHR2VXYyID0gdXYyO1xcbiNlbmRpZlwiO1xuXG5cdHZhciB3b3JsZHBvc192ZXJ0ZXggPSBcIiNpZiBkZWZpbmVkKCBVU0VfRU5WTUFQICkgfHwgZGVmaW5lZCggUEhPTkcgKSB8fCBkZWZpbmVkKCBQSFlTSUNBTCApIHx8IGRlZmluZWQoIExBTUJFUlQgKSB8fCBkZWZpbmVkICggVVNFX1NIQURPV01BUCApXFxuXFx0I2lmZGVmIFVTRV9TS0lOTklOR1xcblxcdFxcdHZlYzQgd29ybGRQb3NpdGlvbiA9IG1vZGVsTWF0cml4ICogc2tpbm5lZDtcXG5cXHQjZWxzZVxcblxcdFxcdHZlYzQgd29ybGRQb3NpdGlvbiA9IG1vZGVsTWF0cml4ICogdmVjNCggdHJhbnNmb3JtZWQsIDEuMCApO1xcblxcdCNlbmRpZlxcbiNlbmRpZlxcblwiO1xuXG5cdHZhciBjdWJlX2ZyYWcgPSBcInVuaWZvcm0gc2FtcGxlckN1YmUgdEN1YmU7XFxudW5pZm9ybSBmbG9hdCB0RmxpcDtcXG51bmlmb3JtIGZsb2F0IG9wYWNpdHk7XFxudmFyeWluZyB2ZWMzIHZXb3JsZFBvc2l0aW9uO1xcbiNpbmNsdWRlIDxjb21tb24+XFxudm9pZCBtYWluKCkge1xcblxcdGdsX0ZyYWdDb2xvciA9IHRleHR1cmVDdWJlKCB0Q3ViZSwgdmVjMyggdEZsaXAgKiB2V29ybGRQb3NpdGlvbi54LCB2V29ybGRQb3NpdGlvbi55eiApICk7XFxuXFx0Z2xfRnJhZ0NvbG9yLmEgKj0gb3BhY2l0eTtcXG59XFxuXCI7XG5cblx0dmFyIGN1YmVfdmVydCA9IFwidmFyeWluZyB2ZWMzIHZXb3JsZFBvc2l0aW9uO1xcbiNpbmNsdWRlIDxjb21tb24+XFxudm9pZCBtYWluKCkge1xcblxcdHZXb3JsZFBvc2l0aW9uID0gdHJhbnNmb3JtRGlyZWN0aW9uKCBwb3NpdGlvbiwgbW9kZWxNYXRyaXggKTtcXG5cXHQjaW5jbHVkZSA8YmVnaW5fdmVydGV4PlxcblxcdCNpbmNsdWRlIDxwcm9qZWN0X3ZlcnRleD5cXG59XFxuXCI7XG5cblx0dmFyIGRlcHRoX2ZyYWcgPSBcIiNpZiBERVBUSF9QQUNLSU5HID09IDMyMDBcXG5cXHR1bmlmb3JtIGZsb2F0IG9wYWNpdHk7XFxuI2VuZGlmXFxuI2luY2x1ZGUgPGNvbW1vbj5cXG4jaW5jbHVkZSA8cGFja2luZz5cXG4jaW5jbHVkZSA8dXZfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8bWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGFscGhhbWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGxvZ2RlcHRoYnVmX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGNsaXBwaW5nX3BsYW5lc19wYXJzX2ZyYWdtZW50PlxcbnZvaWQgbWFpbigpIHtcXG5cXHQjaW5jbHVkZSA8Y2xpcHBpbmdfcGxhbmVzX2ZyYWdtZW50PlxcblxcdHZlYzQgZGlmZnVzZUNvbG9yID0gdmVjNCggMS4wICk7XFxuXFx0I2lmIERFUFRIX1BBQ0tJTkcgPT0gMzIwMFxcblxcdFxcdGRpZmZ1c2VDb2xvci5hID0gb3BhY2l0eTtcXG5cXHQjZW5kaWZcXG5cXHQjaW5jbHVkZSA8bWFwX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxhbHBoYW1hcF9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8YWxwaGF0ZXN0X2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxsb2dkZXB0aGJ1Zl9mcmFnbWVudD5cXG5cXHQjaWYgREVQVEhfUEFDS0lORyA9PSAzMjAwXFxuXFx0XFx0Z2xfRnJhZ0NvbG9yID0gdmVjNCggdmVjMyggZ2xfRnJhZ0Nvb3JkLnogKSwgb3BhY2l0eSApO1xcblxcdCNlbGlmIERFUFRIX1BBQ0tJTkcgPT0gMzIwMVxcblxcdFxcdGdsX0ZyYWdDb2xvciA9IHBhY2tEZXB0aFRvUkdCQSggZ2xfRnJhZ0Nvb3JkLnogKTtcXG5cXHQjZW5kaWZcXG59XFxuXCI7XG5cblx0dmFyIGRlcHRoX3ZlcnQgPSBcIiNpbmNsdWRlIDxjb21tb24+XFxuI2luY2x1ZGUgPHV2X3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxkaXNwbGFjZW1lbnRtYXBfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPG1vcnBodGFyZ2V0X3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxza2lubmluZ19wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8bG9nZGVwdGhidWZfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPGNsaXBwaW5nX3BsYW5lc19wYXJzX3ZlcnRleD5cXG52b2lkIG1haW4oKSB7XFxuXFx0I2luY2x1ZGUgPHV2X3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8c2tpbmJhc2VfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxiZWdpbl92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPGRpc3BsYWNlbWVudG1hcF92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPG1vcnBodGFyZ2V0X3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8c2tpbm5pbmdfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxwcm9qZWN0X3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8bG9nZGVwdGhidWZfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxjbGlwcGluZ19wbGFuZXNfdmVydGV4Plxcbn1cXG5cIjtcblxuXHR2YXIgZGlzdGFuY2VSR0JBX2ZyYWcgPSBcInVuaWZvcm0gdmVjMyBsaWdodFBvcztcXG52YXJ5aW5nIHZlYzQgdldvcmxkUG9zaXRpb247XFxuI2luY2x1ZGUgPGNvbW1vbj5cXG4jaW5jbHVkZSA8cGFja2luZz5cXG4jaW5jbHVkZSA8Y2xpcHBpbmdfcGxhbmVzX3BhcnNfZnJhZ21lbnQ+XFxudm9pZCBtYWluICgpIHtcXG5cXHQjaW5jbHVkZSA8Y2xpcHBpbmdfcGxhbmVzX2ZyYWdtZW50PlxcblxcdGdsX0ZyYWdDb2xvciA9IHBhY2tEZXB0aFRvUkdCQSggbGVuZ3RoKCB2V29ybGRQb3NpdGlvbi54eXogLSBsaWdodFBvcy54eXogKSAvIDEwMDAuMCApO1xcbn1cXG5cIjtcblxuXHR2YXIgZGlzdGFuY2VSR0JBX3ZlcnQgPSBcInZhcnlpbmcgdmVjNCB2V29ybGRQb3NpdGlvbjtcXG4jaW5jbHVkZSA8Y29tbW9uPlxcbiNpbmNsdWRlIDxtb3JwaHRhcmdldF9wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8c2tpbm5pbmdfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPGNsaXBwaW5nX3BsYW5lc19wYXJzX3ZlcnRleD5cXG52b2lkIG1haW4oKSB7XFxuXFx0I2luY2x1ZGUgPHNraW5iYXNlX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8YmVnaW5fdmVydGV4PlxcblxcdCNpbmNsdWRlIDxtb3JwaHRhcmdldF92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPHNraW5uaW5nX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8cHJvamVjdF92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPHdvcmxkcG9zX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8Y2xpcHBpbmdfcGxhbmVzX3ZlcnRleD5cXG5cXHR2V29ybGRQb3NpdGlvbiA9IHdvcmxkUG9zaXRpb247XFxufVxcblwiO1xuXG5cdHZhciBlcXVpcmVjdF9mcmFnID0gXCJ1bmlmb3JtIHNhbXBsZXIyRCB0RXF1aXJlY3Q7XFxudW5pZm9ybSBmbG9hdCB0RmxpcDtcXG52YXJ5aW5nIHZlYzMgdldvcmxkUG9zaXRpb247XFxuI2luY2x1ZGUgPGNvbW1vbj5cXG52b2lkIG1haW4oKSB7XFxuXFx0dmVjMyBkaXJlY3Rpb24gPSBub3JtYWxpemUoIHZXb3JsZFBvc2l0aW9uICk7XFxuXFx0dmVjMiBzYW1wbGVVVjtcXG5cXHRzYW1wbGVVVi55ID0gc2F0dXJhdGUoIHRGbGlwICogZGlyZWN0aW9uLnkgKiAtMC41ICsgMC41ICk7XFxuXFx0c2FtcGxlVVYueCA9IGF0YW4oIGRpcmVjdGlvbi56LCBkaXJlY3Rpb24ueCApICogUkVDSVBST0NBTF9QSTIgKyAwLjU7XFxuXFx0Z2xfRnJhZ0NvbG9yID0gdGV4dHVyZTJEKCB0RXF1aXJlY3QsIHNhbXBsZVVWICk7XFxufVxcblwiO1xuXG5cdHZhciBlcXVpcmVjdF92ZXJ0ID0gXCJ2YXJ5aW5nIHZlYzMgdldvcmxkUG9zaXRpb247XFxuI2luY2x1ZGUgPGNvbW1vbj5cXG52b2lkIG1haW4oKSB7XFxuXFx0dldvcmxkUG9zaXRpb24gPSB0cmFuc2Zvcm1EaXJlY3Rpb24oIHBvc2l0aW9uLCBtb2RlbE1hdHJpeCApO1xcblxcdCNpbmNsdWRlIDxiZWdpbl92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPHByb2plY3RfdmVydGV4Plxcbn1cXG5cIjtcblxuXHR2YXIgbGluZWRhc2hlZF9mcmFnID0gXCJ1bmlmb3JtIHZlYzMgZGlmZnVzZTtcXG51bmlmb3JtIGZsb2F0IG9wYWNpdHk7XFxudW5pZm9ybSBmbG9hdCBkYXNoU2l6ZTtcXG51bmlmb3JtIGZsb2F0IHRvdGFsU2l6ZTtcXG52YXJ5aW5nIGZsb2F0IHZMaW5lRGlzdGFuY2U7XFxuI2luY2x1ZGUgPGNvbW1vbj5cXG4jaW5jbHVkZSA8Y29sb3JfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8Zm9nX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGxvZ2RlcHRoYnVmX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGNsaXBwaW5nX3BsYW5lc19wYXJzX2ZyYWdtZW50PlxcbnZvaWQgbWFpbigpIHtcXG5cXHQjaW5jbHVkZSA8Y2xpcHBpbmdfcGxhbmVzX2ZyYWdtZW50PlxcblxcdGlmICggbW9kKCB2TGluZURpc3RhbmNlLCB0b3RhbFNpemUgKSA+IGRhc2hTaXplICkge1xcblxcdFxcdGRpc2NhcmQ7XFxuXFx0fVxcblxcdHZlYzMgb3V0Z29pbmdMaWdodCA9IHZlYzMoIDAuMCApO1xcblxcdHZlYzQgZGlmZnVzZUNvbG9yID0gdmVjNCggZGlmZnVzZSwgb3BhY2l0eSApO1xcblxcdCNpbmNsdWRlIDxsb2dkZXB0aGJ1Zl9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8Y29sb3JfZnJhZ21lbnQ+XFxuXFx0b3V0Z29pbmdMaWdodCA9IGRpZmZ1c2VDb2xvci5yZ2I7XFxuXFx0Z2xfRnJhZ0NvbG9yID0gdmVjNCggb3V0Z29pbmdMaWdodCwgZGlmZnVzZUNvbG9yLmEgKTtcXG5cXHQjaW5jbHVkZSA8cHJlbXVsdGlwbGllZF9hbHBoYV9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8dG9uZW1hcHBpbmdfZnJhZ21lbnQ+XFxuXFx0I2luY2x1ZGUgPGVuY29kaW5nc19mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8Zm9nX2ZyYWdtZW50Plxcbn1cXG5cIjtcblxuXHR2YXIgbGluZWRhc2hlZF92ZXJ0ID0gXCJ1bmlmb3JtIGZsb2F0IHNjYWxlO1xcbmF0dHJpYnV0ZSBmbG9hdCBsaW5lRGlzdGFuY2U7XFxudmFyeWluZyBmbG9hdCB2TGluZURpc3RhbmNlO1xcbiNpbmNsdWRlIDxjb21tb24+XFxuI2luY2x1ZGUgPGNvbG9yX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxsb2dkZXB0aGJ1Zl9wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8Y2xpcHBpbmdfcGxhbmVzX3BhcnNfdmVydGV4PlxcbnZvaWQgbWFpbigpIHtcXG5cXHQjaW5jbHVkZSA8Y29sb3JfdmVydGV4PlxcblxcdHZMaW5lRGlzdGFuY2UgPSBzY2FsZSAqIGxpbmVEaXN0YW5jZTtcXG5cXHR2ZWM0IG12UG9zaXRpb24gPSBtb2RlbFZpZXdNYXRyaXggKiB2ZWM0KCBwb3NpdGlvbiwgMS4wICk7XFxuXFx0Z2xfUG9zaXRpb24gPSBwcm9qZWN0aW9uTWF0cml4ICogbXZQb3NpdGlvbjtcXG5cXHQjaW5jbHVkZSA8bG9nZGVwdGhidWZfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxjbGlwcGluZ19wbGFuZXNfdmVydGV4Plxcbn1cXG5cIjtcblxuXHR2YXIgbWVzaGJhc2ljX2ZyYWcgPSBcInVuaWZvcm0gdmVjMyBkaWZmdXNlO1xcbnVuaWZvcm0gZmxvYXQgb3BhY2l0eTtcXG4jaWZuZGVmIEZMQVRfU0hBREVEXFxuXFx0dmFyeWluZyB2ZWMzIHZOb3JtYWw7XFxuI2VuZGlmXFxuI2luY2x1ZGUgPGNvbW1vbj5cXG4jaW5jbHVkZSA8Y29sb3JfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8dXZfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8dXYyX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPG1hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxhbHBoYW1hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxhb21hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxsaWdodG1hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxlbnZtYXBfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8Zm9nX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPHNwZWN1bGFybWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGxvZ2RlcHRoYnVmX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGNsaXBwaW5nX3BsYW5lc19wYXJzX2ZyYWdtZW50PlxcbnZvaWQgbWFpbigpIHtcXG5cXHQjaW5jbHVkZSA8Y2xpcHBpbmdfcGxhbmVzX2ZyYWdtZW50PlxcblxcdHZlYzQgZGlmZnVzZUNvbG9yID0gdmVjNCggZGlmZnVzZSwgb3BhY2l0eSApO1xcblxcdCNpbmNsdWRlIDxsb2dkZXB0aGJ1Zl9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8bWFwX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxjb2xvcl9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8YWxwaGFtYXBfZnJhZ21lbnQ+XFxuXFx0I2luY2x1ZGUgPGFscGhhdGVzdF9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8c3BlY3VsYXJtYXBfZnJhZ21lbnQ+XFxuXFx0UmVmbGVjdGVkTGlnaHQgcmVmbGVjdGVkTGlnaHQgPSBSZWZsZWN0ZWRMaWdodCggdmVjMyggMC4wICksIHZlYzMoIDAuMCApLCB2ZWMzKCAwLjAgKSwgdmVjMyggMC4wICkgKTtcXG5cXHQjaWZkZWYgVVNFX0xJR0hUTUFQXFxuXFx0XFx0cmVmbGVjdGVkTGlnaHQuaW5kaXJlY3REaWZmdXNlICs9IHRleHR1cmUyRCggbGlnaHRNYXAsIHZVdjIgKS54eXogKiBsaWdodE1hcEludGVuc2l0eTtcXG5cXHQjZWxzZVxcblxcdFxcdHJlZmxlY3RlZExpZ2h0LmluZGlyZWN0RGlmZnVzZSArPSB2ZWMzKCAxLjAgKTtcXG5cXHQjZW5kaWZcXG5cXHQjaW5jbHVkZSA8YW9tYXBfZnJhZ21lbnQ+XFxuXFx0cmVmbGVjdGVkTGlnaHQuaW5kaXJlY3REaWZmdXNlICo9IGRpZmZ1c2VDb2xvci5yZ2I7XFxuXFx0dmVjMyBvdXRnb2luZ0xpZ2h0ID0gcmVmbGVjdGVkTGlnaHQuaW5kaXJlY3REaWZmdXNlO1xcblxcdCNpbmNsdWRlIDxub3JtYWxfZmxpcD5cXG5cXHQjaW5jbHVkZSA8ZW52bWFwX2ZyYWdtZW50PlxcblxcdGdsX0ZyYWdDb2xvciA9IHZlYzQoIG91dGdvaW5nTGlnaHQsIGRpZmZ1c2VDb2xvci5hICk7XFxuXFx0I2luY2x1ZGUgPHByZW11bHRpcGxpZWRfYWxwaGFfZnJhZ21lbnQ+XFxuXFx0I2luY2x1ZGUgPHRvbmVtYXBwaW5nX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxlbmNvZGluZ3NfZnJhZ21lbnQ+XFxuXFx0I2luY2x1ZGUgPGZvZ19mcmFnbWVudD5cXG59XFxuXCI7XG5cblx0dmFyIG1lc2hiYXNpY192ZXJ0ID0gXCIjaW5jbHVkZSA8Y29tbW9uPlxcbiNpbmNsdWRlIDx1dl9wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8dXYyX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxlbnZtYXBfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPGNvbG9yX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxtb3JwaHRhcmdldF9wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8c2tpbm5pbmdfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPGxvZ2RlcHRoYnVmX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxjbGlwcGluZ19wbGFuZXNfcGFyc192ZXJ0ZXg+XFxudm9pZCBtYWluKCkge1xcblxcdCNpbmNsdWRlIDx1dl92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPHV2Ml92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPGNvbG9yX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8c2tpbmJhc2VfdmVydGV4PlxcblxcdCNpZmRlZiBVU0VfRU5WTUFQXFxuXFx0I2luY2x1ZGUgPGJlZ2lubm9ybWFsX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8bW9ycGhub3JtYWxfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxza2lubm9ybWFsX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8ZGVmYXVsdG5vcm1hbF92ZXJ0ZXg+XFxuXFx0I2VuZGlmXFxuXFx0I2luY2x1ZGUgPGJlZ2luX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8bW9ycGh0YXJnZXRfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxza2lubmluZ192ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPHByb2plY3RfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxsb2dkZXB0aGJ1Zl92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPHdvcmxkcG9zX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8Y2xpcHBpbmdfcGxhbmVzX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8ZW52bWFwX3ZlcnRleD5cXG59XFxuXCI7XG5cblx0dmFyIG1lc2hsYW1iZXJ0X2ZyYWcgPSBcInVuaWZvcm0gdmVjMyBkaWZmdXNlO1xcbnVuaWZvcm0gdmVjMyBlbWlzc2l2ZTtcXG51bmlmb3JtIGZsb2F0IG9wYWNpdHk7XFxudmFyeWluZyB2ZWMzIHZMaWdodEZyb250O1xcbiNpZmRlZiBET1VCTEVfU0lERURcXG5cXHR2YXJ5aW5nIHZlYzMgdkxpZ2h0QmFjaztcXG4jZW5kaWZcXG4jaW5jbHVkZSA8Y29tbW9uPlxcbiNpbmNsdWRlIDxwYWNraW5nPlxcbiNpbmNsdWRlIDxjb2xvcl9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDx1dl9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDx1djJfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8bWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGFscGhhbWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGFvbWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGxpZ2h0bWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGVtaXNzaXZlbWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGVudm1hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxic2Rmcz5cXG4jaW5jbHVkZSA8bGlnaHRzX3BhcnM+XFxuI2luY2x1ZGUgPGZvZ19wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxzaGFkb3dtYXBfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8c2hhZG93bWFza19wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxzcGVjdWxhcm1hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxsb2dkZXB0aGJ1Zl9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxjbGlwcGluZ19wbGFuZXNfcGFyc19mcmFnbWVudD5cXG52b2lkIG1haW4oKSB7XFxuXFx0I2luY2x1ZGUgPGNsaXBwaW5nX3BsYW5lc19mcmFnbWVudD5cXG5cXHR2ZWM0IGRpZmZ1c2VDb2xvciA9IHZlYzQoIGRpZmZ1c2UsIG9wYWNpdHkgKTtcXG5cXHRSZWZsZWN0ZWRMaWdodCByZWZsZWN0ZWRMaWdodCA9IFJlZmxlY3RlZExpZ2h0KCB2ZWMzKCAwLjAgKSwgdmVjMyggMC4wICksIHZlYzMoIDAuMCApLCB2ZWMzKCAwLjAgKSApO1xcblxcdHZlYzMgdG90YWxFbWlzc2l2ZVJhZGlhbmNlID0gZW1pc3NpdmU7XFxuXFx0I2luY2x1ZGUgPGxvZ2RlcHRoYnVmX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxtYXBfZnJhZ21lbnQ+XFxuXFx0I2luY2x1ZGUgPGNvbG9yX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxhbHBoYW1hcF9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8YWxwaGF0ZXN0X2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxzcGVjdWxhcm1hcF9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8ZW1pc3NpdmVtYXBfZnJhZ21lbnQ+XFxuXFx0cmVmbGVjdGVkTGlnaHQuaW5kaXJlY3REaWZmdXNlID0gZ2V0QW1iaWVudExpZ2h0SXJyYWRpYW5jZSggYW1iaWVudExpZ2h0Q29sb3IgKTtcXG5cXHQjaW5jbHVkZSA8bGlnaHRtYXBfZnJhZ21lbnQ+XFxuXFx0cmVmbGVjdGVkTGlnaHQuaW5kaXJlY3REaWZmdXNlICo9IEJSREZfRGlmZnVzZV9MYW1iZXJ0KCBkaWZmdXNlQ29sb3IucmdiICk7XFxuXFx0I2lmZGVmIERPVUJMRV9TSURFRFxcblxcdFxcdHJlZmxlY3RlZExpZ2h0LmRpcmVjdERpZmZ1c2UgPSAoIGdsX0Zyb250RmFjaW5nICkgPyB2TGlnaHRGcm9udCA6IHZMaWdodEJhY2s7XFxuXFx0I2Vsc2VcXG5cXHRcXHRyZWZsZWN0ZWRMaWdodC5kaXJlY3REaWZmdXNlID0gdkxpZ2h0RnJvbnQ7XFxuXFx0I2VuZGlmXFxuXFx0cmVmbGVjdGVkTGlnaHQuZGlyZWN0RGlmZnVzZSAqPSBCUkRGX0RpZmZ1c2VfTGFtYmVydCggZGlmZnVzZUNvbG9yLnJnYiApICogZ2V0U2hhZG93TWFzaygpO1xcblxcdCNpbmNsdWRlIDxhb21hcF9mcmFnbWVudD5cXG5cXHR2ZWMzIG91dGdvaW5nTGlnaHQgPSByZWZsZWN0ZWRMaWdodC5kaXJlY3REaWZmdXNlICsgcmVmbGVjdGVkTGlnaHQuaW5kaXJlY3REaWZmdXNlICsgdG90YWxFbWlzc2l2ZVJhZGlhbmNlO1xcblxcdCNpbmNsdWRlIDxub3JtYWxfZmxpcD5cXG5cXHQjaW5jbHVkZSA8ZW52bWFwX2ZyYWdtZW50PlxcblxcdGdsX0ZyYWdDb2xvciA9IHZlYzQoIG91dGdvaW5nTGlnaHQsIGRpZmZ1c2VDb2xvci5hICk7XFxuXFx0I2luY2x1ZGUgPHByZW11bHRpcGxpZWRfYWxwaGFfZnJhZ21lbnQ+XFxuXFx0I2luY2x1ZGUgPHRvbmVtYXBwaW5nX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxlbmNvZGluZ3NfZnJhZ21lbnQ+XFxuXFx0I2luY2x1ZGUgPGZvZ19mcmFnbWVudD5cXG59XFxuXCI7XG5cblx0dmFyIG1lc2hsYW1iZXJ0X3ZlcnQgPSBcIiNkZWZpbmUgTEFNQkVSVFxcbnZhcnlpbmcgdmVjMyB2TGlnaHRGcm9udDtcXG4jaWZkZWYgRE9VQkxFX1NJREVEXFxuXFx0dmFyeWluZyB2ZWMzIHZMaWdodEJhY2s7XFxuI2VuZGlmXFxuI2luY2x1ZGUgPGNvbW1vbj5cXG4jaW5jbHVkZSA8dXZfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPHV2Ml9wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8ZW52bWFwX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxic2Rmcz5cXG4jaW5jbHVkZSA8bGlnaHRzX3BhcnM+XFxuI2luY2x1ZGUgPGNvbG9yX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxtb3JwaHRhcmdldF9wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8c2tpbm5pbmdfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPHNoYWRvd21hcF9wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8bG9nZGVwdGhidWZfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPGNsaXBwaW5nX3BsYW5lc19wYXJzX3ZlcnRleD5cXG52b2lkIG1haW4oKSB7XFxuXFx0I2luY2x1ZGUgPHV2X3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8dXYyX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8Y29sb3JfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxiZWdpbm5vcm1hbF92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPG1vcnBobm9ybWFsX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8c2tpbmJhc2VfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxza2lubm9ybWFsX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8ZGVmYXVsdG5vcm1hbF92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPGJlZ2luX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8bW9ycGh0YXJnZXRfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxza2lubmluZ192ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPHByb2plY3RfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxsb2dkZXB0aGJ1Zl92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPGNsaXBwaW5nX3BsYW5lc192ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPHdvcmxkcG9zX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8ZW52bWFwX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8bGlnaHRzX2xhbWJlcnRfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxzaGFkb3dtYXBfdmVydGV4Plxcbn1cXG5cIjtcblxuXHR2YXIgbWVzaHBob25nX2ZyYWcgPSBcIiNkZWZpbmUgUEhPTkdcXG51bmlmb3JtIHZlYzMgZGlmZnVzZTtcXG51bmlmb3JtIHZlYzMgZW1pc3NpdmU7XFxudW5pZm9ybSB2ZWMzIHNwZWN1bGFyO1xcbnVuaWZvcm0gZmxvYXQgc2hpbmluZXNzO1xcbnVuaWZvcm0gZmxvYXQgb3BhY2l0eTtcXG4jaW5jbHVkZSA8Y29tbW9uPlxcbiNpbmNsdWRlIDxwYWNraW5nPlxcbiNpbmNsdWRlIDxjb2xvcl9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDx1dl9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDx1djJfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8bWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGFscGhhbWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGFvbWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGxpZ2h0bWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGVtaXNzaXZlbWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGVudm1hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxncmFkaWVudG1hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxmb2dfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8YnNkZnM+XFxuI2luY2x1ZGUgPGxpZ2h0c19wYXJzPlxcbiNpbmNsdWRlIDxsaWdodHNfcGhvbmdfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8c2hhZG93bWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGJ1bXBtYXBfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8bm9ybWFsbWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPHNwZWN1bGFybWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGxvZ2RlcHRoYnVmX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGNsaXBwaW5nX3BsYW5lc19wYXJzX2ZyYWdtZW50PlxcbnZvaWQgbWFpbigpIHtcXG5cXHQjaW5jbHVkZSA8Y2xpcHBpbmdfcGxhbmVzX2ZyYWdtZW50PlxcblxcdHZlYzQgZGlmZnVzZUNvbG9yID0gdmVjNCggZGlmZnVzZSwgb3BhY2l0eSApO1xcblxcdFJlZmxlY3RlZExpZ2h0IHJlZmxlY3RlZExpZ2h0ID0gUmVmbGVjdGVkTGlnaHQoIHZlYzMoIDAuMCApLCB2ZWMzKCAwLjAgKSwgdmVjMyggMC4wICksIHZlYzMoIDAuMCApICk7XFxuXFx0dmVjMyB0b3RhbEVtaXNzaXZlUmFkaWFuY2UgPSBlbWlzc2l2ZTtcXG5cXHQjaW5jbHVkZSA8bG9nZGVwdGhidWZfZnJhZ21lbnQ+XFxuXFx0I2luY2x1ZGUgPG1hcF9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8Y29sb3JfZnJhZ21lbnQ+XFxuXFx0I2luY2x1ZGUgPGFscGhhbWFwX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxhbHBoYXRlc3RfZnJhZ21lbnQ+XFxuXFx0I2luY2x1ZGUgPHNwZWN1bGFybWFwX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxub3JtYWxfZmxpcD5cXG5cXHQjaW5jbHVkZSA8bm9ybWFsX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxlbWlzc2l2ZW1hcF9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8bGlnaHRzX3Bob25nX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxsaWdodHNfdGVtcGxhdGU+XFxuXFx0I2luY2x1ZGUgPGFvbWFwX2ZyYWdtZW50PlxcblxcdHZlYzMgb3V0Z29pbmdMaWdodCA9IHJlZmxlY3RlZExpZ2h0LmRpcmVjdERpZmZ1c2UgKyByZWZsZWN0ZWRMaWdodC5pbmRpcmVjdERpZmZ1c2UgKyByZWZsZWN0ZWRMaWdodC5kaXJlY3RTcGVjdWxhciArIHJlZmxlY3RlZExpZ2h0LmluZGlyZWN0U3BlY3VsYXIgKyB0b3RhbEVtaXNzaXZlUmFkaWFuY2U7XFxuXFx0I2luY2x1ZGUgPGVudm1hcF9mcmFnbWVudD5cXG5cXHRnbF9GcmFnQ29sb3IgPSB2ZWM0KCBvdXRnb2luZ0xpZ2h0LCBkaWZmdXNlQ29sb3IuYSApO1xcblxcdCNpbmNsdWRlIDxwcmVtdWx0aXBsaWVkX2FscGhhX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDx0b25lbWFwcGluZ19mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8ZW5jb2RpbmdzX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxmb2dfZnJhZ21lbnQ+XFxufVxcblwiO1xuXG5cdHZhciBtZXNocGhvbmdfdmVydCA9IFwiI2RlZmluZSBQSE9OR1xcbnZhcnlpbmcgdmVjMyB2Vmlld1Bvc2l0aW9uO1xcbiNpZm5kZWYgRkxBVF9TSEFERURcXG5cXHR2YXJ5aW5nIHZlYzMgdk5vcm1hbDtcXG4jZW5kaWZcXG4jaW5jbHVkZSA8Y29tbW9uPlxcbiNpbmNsdWRlIDx1dl9wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8dXYyX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxkaXNwbGFjZW1lbnRtYXBfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPGVudm1hcF9wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8Y29sb3JfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPG1vcnBodGFyZ2V0X3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxza2lubmluZ19wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8c2hhZG93bWFwX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxsb2dkZXB0aGJ1Zl9wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8Y2xpcHBpbmdfcGxhbmVzX3BhcnNfdmVydGV4PlxcbnZvaWQgbWFpbigpIHtcXG5cXHQjaW5jbHVkZSA8dXZfdmVydGV4PlxcblxcdCNpbmNsdWRlIDx1djJfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxjb2xvcl92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPGJlZ2lubm9ybWFsX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8bW9ycGhub3JtYWxfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxza2luYmFzZV92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPHNraW5ub3JtYWxfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxkZWZhdWx0bm9ybWFsX3ZlcnRleD5cXG4jaWZuZGVmIEZMQVRfU0hBREVEXFxuXFx0dk5vcm1hbCA9IG5vcm1hbGl6ZSggdHJhbnNmb3JtZWROb3JtYWwgKTtcXG4jZW5kaWZcXG5cXHQjaW5jbHVkZSA8YmVnaW5fdmVydGV4PlxcblxcdCNpbmNsdWRlIDxkaXNwbGFjZW1lbnRtYXBfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxtb3JwaHRhcmdldF92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPHNraW5uaW5nX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8cHJvamVjdF92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPGxvZ2RlcHRoYnVmX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8Y2xpcHBpbmdfcGxhbmVzX3ZlcnRleD5cXG5cXHR2Vmlld1Bvc2l0aW9uID0gLSBtdlBvc2l0aW9uLnh5ejtcXG5cXHQjaW5jbHVkZSA8d29ybGRwb3NfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxlbnZtYXBfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxzaGFkb3dtYXBfdmVydGV4Plxcbn1cXG5cIjtcblxuXHR2YXIgbWVzaHBoeXNpY2FsX2ZyYWcgPSBcIiNkZWZpbmUgUEhZU0lDQUxcXG51bmlmb3JtIHZlYzMgZGlmZnVzZTtcXG51bmlmb3JtIHZlYzMgZW1pc3NpdmU7XFxudW5pZm9ybSBmbG9hdCByb3VnaG5lc3M7XFxudW5pZm9ybSBmbG9hdCBtZXRhbG5lc3M7XFxudW5pZm9ybSBmbG9hdCBvcGFjaXR5O1xcbiNpZm5kZWYgU1RBTkRBUkRcXG5cXHR1bmlmb3JtIGZsb2F0IGNsZWFyQ29hdDtcXG5cXHR1bmlmb3JtIGZsb2F0IGNsZWFyQ29hdFJvdWdobmVzcztcXG4jZW5kaWZcXG52YXJ5aW5nIHZlYzMgdlZpZXdQb3NpdGlvbjtcXG4jaWZuZGVmIEZMQVRfU0hBREVEXFxuXFx0dmFyeWluZyB2ZWMzIHZOb3JtYWw7XFxuI2VuZGlmXFxuI2luY2x1ZGUgPGNvbW1vbj5cXG4jaW5jbHVkZSA8cGFja2luZz5cXG4jaW5jbHVkZSA8Y29sb3JfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8dXZfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8dXYyX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPG1hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxhbHBoYW1hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxhb21hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxsaWdodG1hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxlbWlzc2l2ZW1hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxlbnZtYXBfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8Zm9nX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGJzZGZzPlxcbiNpbmNsdWRlIDxjdWJlX3V2X3JlZmxlY3Rpb25fZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGxpZ2h0c19wYXJzPlxcbiNpbmNsdWRlIDxsaWdodHNfcGh5c2ljYWxfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8c2hhZG93bWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGJ1bXBtYXBfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8bm9ybWFsbWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPHJvdWdobmVzc21hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxtZXRhbG5lc3NtYXBfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8bG9nZGVwdGhidWZfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8Y2xpcHBpbmdfcGxhbmVzX3BhcnNfZnJhZ21lbnQ+XFxudm9pZCBtYWluKCkge1xcblxcdCNpbmNsdWRlIDxjbGlwcGluZ19wbGFuZXNfZnJhZ21lbnQ+XFxuXFx0dmVjNCBkaWZmdXNlQ29sb3IgPSB2ZWM0KCBkaWZmdXNlLCBvcGFjaXR5ICk7XFxuXFx0UmVmbGVjdGVkTGlnaHQgcmVmbGVjdGVkTGlnaHQgPSBSZWZsZWN0ZWRMaWdodCggdmVjMyggMC4wICksIHZlYzMoIDAuMCApLCB2ZWMzKCAwLjAgKSwgdmVjMyggMC4wICkgKTtcXG5cXHR2ZWMzIHRvdGFsRW1pc3NpdmVSYWRpYW5jZSA9IGVtaXNzaXZlO1xcblxcdCNpbmNsdWRlIDxsb2dkZXB0aGJ1Zl9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8bWFwX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxjb2xvcl9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8YWxwaGFtYXBfZnJhZ21lbnQ+XFxuXFx0I2luY2x1ZGUgPGFscGhhdGVzdF9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8c3BlY3VsYXJtYXBfZnJhZ21lbnQ+XFxuXFx0I2luY2x1ZGUgPHJvdWdobmVzc21hcF9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8bWV0YWxuZXNzbWFwX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxub3JtYWxfZmxpcD5cXG5cXHQjaW5jbHVkZSA8bm9ybWFsX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxlbWlzc2l2ZW1hcF9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8bGlnaHRzX3BoeXNpY2FsX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxsaWdodHNfdGVtcGxhdGU+XFxuXFx0I2luY2x1ZGUgPGFvbWFwX2ZyYWdtZW50PlxcblxcdHZlYzMgb3V0Z29pbmdMaWdodCA9IHJlZmxlY3RlZExpZ2h0LmRpcmVjdERpZmZ1c2UgKyByZWZsZWN0ZWRMaWdodC5pbmRpcmVjdERpZmZ1c2UgKyByZWZsZWN0ZWRMaWdodC5kaXJlY3RTcGVjdWxhciArIHJlZmxlY3RlZExpZ2h0LmluZGlyZWN0U3BlY3VsYXIgKyB0b3RhbEVtaXNzaXZlUmFkaWFuY2U7XFxuXFx0Z2xfRnJhZ0NvbG9yID0gdmVjNCggb3V0Z29pbmdMaWdodCwgZGlmZnVzZUNvbG9yLmEgKTtcXG5cXHQjaW5jbHVkZSA8cHJlbXVsdGlwbGllZF9hbHBoYV9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8dG9uZW1hcHBpbmdfZnJhZ21lbnQ+XFxuXFx0I2luY2x1ZGUgPGVuY29kaW5nc19mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8Zm9nX2ZyYWdtZW50Plxcbn1cXG5cIjtcblxuXHR2YXIgbWVzaHBoeXNpY2FsX3ZlcnQgPSBcIiNkZWZpbmUgUEhZU0lDQUxcXG52YXJ5aW5nIHZlYzMgdlZpZXdQb3NpdGlvbjtcXG4jaWZuZGVmIEZMQVRfU0hBREVEXFxuXFx0dmFyeWluZyB2ZWMzIHZOb3JtYWw7XFxuI2VuZGlmXFxuI2luY2x1ZGUgPGNvbW1vbj5cXG4jaW5jbHVkZSA8dXZfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPHV2Ml9wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8ZGlzcGxhY2VtZW50bWFwX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxjb2xvcl9wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8bW9ycGh0YXJnZXRfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPHNraW5uaW5nX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxzaGFkb3dtYXBfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPHNwZWN1bGFybWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGxvZ2RlcHRoYnVmX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxjbGlwcGluZ19wbGFuZXNfcGFyc192ZXJ0ZXg+XFxudm9pZCBtYWluKCkge1xcblxcdCNpbmNsdWRlIDx1dl92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPHV2Ml92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPGNvbG9yX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8YmVnaW5ub3JtYWxfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxtb3JwaG5vcm1hbF92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPHNraW5iYXNlX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8c2tpbm5vcm1hbF92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPGRlZmF1bHRub3JtYWxfdmVydGV4PlxcbiNpZm5kZWYgRkxBVF9TSEFERURcXG5cXHR2Tm9ybWFsID0gbm9ybWFsaXplKCB0cmFuc2Zvcm1lZE5vcm1hbCApO1xcbiNlbmRpZlxcblxcdCNpbmNsdWRlIDxiZWdpbl92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPGRpc3BsYWNlbWVudG1hcF92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPG1vcnBodGFyZ2V0X3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8c2tpbm5pbmdfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxwcm9qZWN0X3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8bG9nZGVwdGhidWZfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxjbGlwcGluZ19wbGFuZXNfdmVydGV4PlxcblxcdHZWaWV3UG9zaXRpb24gPSAtIG12UG9zaXRpb24ueHl6O1xcblxcdCNpbmNsdWRlIDx3b3JsZHBvc192ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPHNoYWRvd21hcF92ZXJ0ZXg+XFxufVxcblwiO1xuXG5cdHZhciBub3JtYWxfZnJhZyA9IFwiI2RlZmluZSBOT1JNQUxcXG51bmlmb3JtIGZsb2F0IG9wYWNpdHk7XFxuI2lmIGRlZmluZWQoIEZMQVRfU0hBREVEICApIHx8IGRlZmluZWQoIFVTRV9CVU1QTUFQICkgfHwgZGVmaW5lZCggVVNFX05PUk1BTE1BUCApXFxuXFx0dmFyeWluZyB2ZWMzIHZWaWV3UG9zaXRpb247XFxuI2VuZGlmXFxuI2lmbmRlZiBGTEFUX1NIQURFRFxcblxcdHZhcnlpbmcgdmVjMyB2Tm9ybWFsO1xcbiNlbmRpZlxcbiNpbmNsdWRlIDxwYWNraW5nPlxcbiNpbmNsdWRlIDx1dl9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxidW1wbWFwX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPG5vcm1hbG1hcF9wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxsb2dkZXB0aGJ1Zl9wYXJzX2ZyYWdtZW50PlxcbnZvaWQgbWFpbigpIHtcXG5cXHQjaW5jbHVkZSA8bG9nZGVwdGhidWZfZnJhZ21lbnQ+XFxuXFx0I2luY2x1ZGUgPG5vcm1hbF9mbGlwPlxcblxcdCNpbmNsdWRlIDxub3JtYWxfZnJhZ21lbnQ+XFxuXFx0Z2xfRnJhZ0NvbG9yID0gdmVjNCggcGFja05vcm1hbFRvUkdCKCBub3JtYWwgKSwgb3BhY2l0eSApO1xcblxcdCNpbmNsdWRlIDxwcmVtdWx0aXBsaWVkX2FscGhhX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxlbmNvZGluZ3NfZnJhZ21lbnQ+XFxufVxcblwiO1xuXG5cdHZhciBub3JtYWxfdmVydCA9IFwiI2RlZmluZSBOT1JNQUxcXG4jaWYgZGVmaW5lZCggRkxBVF9TSEFERUQgICkgfHwgZGVmaW5lZCggVVNFX0JVTVBNQVAgKSB8fCBkZWZpbmVkKCBVU0VfTk9STUFMTUFQIClcXG5cXHR2YXJ5aW5nIHZlYzMgdlZpZXdQb3NpdGlvbjtcXG4jZW5kaWZcXG4jaWZuZGVmIEZMQVRfU0hBREVEXFxuXFx0dmFyeWluZyB2ZWMzIHZOb3JtYWw7XFxuI2VuZGlmXFxuI2luY2x1ZGUgPHV2X3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxkaXNwbGFjZW1lbnRtYXBfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPG1vcnBodGFyZ2V0X3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxza2lubmluZ19wYXJzX3ZlcnRleD5cXG4jaW5jbHVkZSA8bG9nZGVwdGhidWZfcGFyc192ZXJ0ZXg+XFxudm9pZCBtYWluKCkge1xcblxcdCNpbmNsdWRlIDx1dl92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPGJlZ2lubm9ybWFsX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8bW9ycGhub3JtYWxfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxza2luYmFzZV92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPHNraW5ub3JtYWxfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxkZWZhdWx0bm9ybWFsX3ZlcnRleD5cXG4jaWZuZGVmIEZMQVRfU0hBREVEXFxuXFx0dk5vcm1hbCA9IG5vcm1hbGl6ZSggdHJhbnNmb3JtZWROb3JtYWwgKTtcXG4jZW5kaWZcXG5cXHQjaW5jbHVkZSA8YmVnaW5fdmVydGV4PlxcblxcdCNpbmNsdWRlIDxkaXNwbGFjZW1lbnRtYXBfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxtb3JwaHRhcmdldF92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPHNraW5uaW5nX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8cHJvamVjdF92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPGxvZ2RlcHRoYnVmX3ZlcnRleD5cXG4jaWYgZGVmaW5lZCggRkxBVF9TSEFERUQgICkgfHwgZGVmaW5lZCggVVNFX0JVTVBNQVAgKSB8fCBkZWZpbmVkKCBVU0VfTk9STUFMTUFQIClcXG5cXHR2Vmlld1Bvc2l0aW9uID0gLSBtdlBvc2l0aW9uLnh5ejtcXG4jZW5kaWZcXG59XFxuXCI7XG5cblx0dmFyIHBvaW50c19mcmFnID0gXCJ1bmlmb3JtIHZlYzMgZGlmZnVzZTtcXG51bmlmb3JtIGZsb2F0IG9wYWNpdHk7XFxuI2luY2x1ZGUgPGNvbW1vbj5cXG4jaW5jbHVkZSA8cGFja2luZz5cXG4jaW5jbHVkZSA8Y29sb3JfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8bWFwX3BhcnRpY2xlX3BhcnNfZnJhZ21lbnQ+XFxuI2luY2x1ZGUgPGZvZ19wYXJzX2ZyYWdtZW50PlxcbiNpbmNsdWRlIDxzaGFkb3dtYXBfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8bG9nZGVwdGhidWZfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8Y2xpcHBpbmdfcGxhbmVzX3BhcnNfZnJhZ21lbnQ+XFxudm9pZCBtYWluKCkge1xcblxcdCNpbmNsdWRlIDxjbGlwcGluZ19wbGFuZXNfZnJhZ21lbnQ+XFxuXFx0dmVjMyBvdXRnb2luZ0xpZ2h0ID0gdmVjMyggMC4wICk7XFxuXFx0dmVjNCBkaWZmdXNlQ29sb3IgPSB2ZWM0KCBkaWZmdXNlLCBvcGFjaXR5ICk7XFxuXFx0I2luY2x1ZGUgPGxvZ2RlcHRoYnVmX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxtYXBfcGFydGljbGVfZnJhZ21lbnQ+XFxuXFx0I2luY2x1ZGUgPGNvbG9yX2ZyYWdtZW50PlxcblxcdCNpbmNsdWRlIDxhbHBoYXRlc3RfZnJhZ21lbnQ+XFxuXFx0b3V0Z29pbmdMaWdodCA9IGRpZmZ1c2VDb2xvci5yZ2I7XFxuXFx0Z2xfRnJhZ0NvbG9yID0gdmVjNCggb3V0Z29pbmdMaWdodCwgZGlmZnVzZUNvbG9yLmEgKTtcXG5cXHQjaW5jbHVkZSA8cHJlbXVsdGlwbGllZF9hbHBoYV9mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8dG9uZW1hcHBpbmdfZnJhZ21lbnQ+XFxuXFx0I2luY2x1ZGUgPGVuY29kaW5nc19mcmFnbWVudD5cXG5cXHQjaW5jbHVkZSA8Zm9nX2ZyYWdtZW50Plxcbn1cXG5cIjtcblxuXHR2YXIgcG9pbnRzX3ZlcnQgPSBcInVuaWZvcm0gZmxvYXQgc2l6ZTtcXG51bmlmb3JtIGZsb2F0IHNjYWxlO1xcbiNpbmNsdWRlIDxjb21tb24+XFxuI2luY2x1ZGUgPGNvbG9yX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxzaGFkb3dtYXBfcGFyc192ZXJ0ZXg+XFxuI2luY2x1ZGUgPGxvZ2RlcHRoYnVmX3BhcnNfdmVydGV4PlxcbiNpbmNsdWRlIDxjbGlwcGluZ19wbGFuZXNfcGFyc192ZXJ0ZXg+XFxudm9pZCBtYWluKCkge1xcblxcdCNpbmNsdWRlIDxjb2xvcl92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPGJlZ2luX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8cHJvamVjdF92ZXJ0ZXg+XFxuXFx0I2lmZGVmIFVTRV9TSVpFQVRURU5VQVRJT05cXG5cXHRcXHRnbF9Qb2ludFNpemUgPSBzaXplICogKCBzY2FsZSAvIC0gbXZQb3NpdGlvbi56ICk7XFxuXFx0I2Vsc2VcXG5cXHRcXHRnbF9Qb2ludFNpemUgPSBzaXplO1xcblxcdCNlbmRpZlxcblxcdCNpbmNsdWRlIDxsb2dkZXB0aGJ1Zl92ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPGNsaXBwaW5nX3BsYW5lc192ZXJ0ZXg+XFxuXFx0I2luY2x1ZGUgPHdvcmxkcG9zX3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8c2hhZG93bWFwX3ZlcnRleD5cXG59XFxuXCI7XG5cblx0dmFyIHNoYWRvd19mcmFnID0gXCJ1bmlmb3JtIGZsb2F0IG9wYWNpdHk7XFxuI2luY2x1ZGUgPGNvbW1vbj5cXG4jaW5jbHVkZSA8cGFja2luZz5cXG4jaW5jbHVkZSA8YnNkZnM+XFxuI2luY2x1ZGUgPGxpZ2h0c19wYXJzPlxcbiNpbmNsdWRlIDxzaGFkb3dtYXBfcGFyc19mcmFnbWVudD5cXG4jaW5jbHVkZSA8c2hhZG93bWFza19wYXJzX2ZyYWdtZW50PlxcbnZvaWQgbWFpbigpIHtcXG5cXHRnbF9GcmFnQ29sb3IgPSB2ZWM0KCAwLjAsIDAuMCwgMC4wLCBvcGFjaXR5ICogKCAxLjAgIC0gZ2V0U2hhZG93TWFzaygpICkgKTtcXG59XFxuXCI7XG5cblx0dmFyIHNoYWRvd192ZXJ0ID0gXCIjaW5jbHVkZSA8c2hhZG93bWFwX3BhcnNfdmVydGV4PlxcbnZvaWQgbWFpbigpIHtcXG5cXHQjaW5jbHVkZSA8YmVnaW5fdmVydGV4PlxcblxcdCNpbmNsdWRlIDxwcm9qZWN0X3ZlcnRleD5cXG5cXHQjaW5jbHVkZSA8d29ybGRwb3NfdmVydGV4PlxcblxcdCNpbmNsdWRlIDxzaGFkb3dtYXBfdmVydGV4Plxcbn1cXG5cIjtcblxuXHR2YXIgU2hhZGVyQ2h1bmsgPSB7XG5cdFx0YWxwaGFtYXBfZnJhZ21lbnQ6IGFscGhhbWFwX2ZyYWdtZW50LFxuXHRcdGFscGhhbWFwX3BhcnNfZnJhZ21lbnQ6IGFscGhhbWFwX3BhcnNfZnJhZ21lbnQsXG5cdFx0YWxwaGF0ZXN0X2ZyYWdtZW50OiBhbHBoYXRlc3RfZnJhZ21lbnQsXG5cdFx0YW9tYXBfZnJhZ21lbnQ6IGFvbWFwX2ZyYWdtZW50LFxuXHRcdGFvbWFwX3BhcnNfZnJhZ21lbnQ6IGFvbWFwX3BhcnNfZnJhZ21lbnQsXG5cdFx0YmVnaW5fdmVydGV4OiBiZWdpbl92ZXJ0ZXgsXG5cdFx0YmVnaW5ub3JtYWxfdmVydGV4OiBiZWdpbm5vcm1hbF92ZXJ0ZXgsXG5cdFx0YnNkZnM6IGJzZGZzLFxuXHRcdGJ1bXBtYXBfcGFyc19mcmFnbWVudDogYnVtcG1hcF9wYXJzX2ZyYWdtZW50LFxuXHRcdGNsaXBwaW5nX3BsYW5lc19mcmFnbWVudDogY2xpcHBpbmdfcGxhbmVzX2ZyYWdtZW50LFxuXHRcdGNsaXBwaW5nX3BsYW5lc19wYXJzX2ZyYWdtZW50OiBjbGlwcGluZ19wbGFuZXNfcGFyc19mcmFnbWVudCxcblx0XHRjbGlwcGluZ19wbGFuZXNfcGFyc192ZXJ0ZXg6IGNsaXBwaW5nX3BsYW5lc19wYXJzX3ZlcnRleCxcblx0XHRjbGlwcGluZ19wbGFuZXNfdmVydGV4OiBjbGlwcGluZ19wbGFuZXNfdmVydGV4LFxuXHRcdGNvbG9yX2ZyYWdtZW50OiBjb2xvcl9mcmFnbWVudCxcblx0XHRjb2xvcl9wYXJzX2ZyYWdtZW50OiBjb2xvcl9wYXJzX2ZyYWdtZW50LFxuXHRcdGNvbG9yX3BhcnNfdmVydGV4OiBjb2xvcl9wYXJzX3ZlcnRleCxcblx0XHRjb2xvcl92ZXJ0ZXg6IGNvbG9yX3ZlcnRleCxcblx0XHRjb21tb246IGNvbW1vbixcblx0XHRjdWJlX3V2X3JlZmxlY3Rpb25fZnJhZ21lbnQ6IGN1YmVfdXZfcmVmbGVjdGlvbl9mcmFnbWVudCxcblx0XHRkZWZhdWx0bm9ybWFsX3ZlcnRleDogZGVmYXVsdG5vcm1hbF92ZXJ0ZXgsXG5cdFx0ZGlzcGxhY2VtZW50bWFwX3BhcnNfdmVydGV4OiBkaXNwbGFjZW1lbnRtYXBfcGFyc192ZXJ0ZXgsXG5cdFx0ZGlzcGxhY2VtZW50bWFwX3ZlcnRleDogZGlzcGxhY2VtZW50bWFwX3ZlcnRleCxcblx0XHRlbWlzc2l2ZW1hcF9mcmFnbWVudDogZW1pc3NpdmVtYXBfZnJhZ21lbnQsXG5cdFx0ZW1pc3NpdmVtYXBfcGFyc19mcmFnbWVudDogZW1pc3NpdmVtYXBfcGFyc19mcmFnbWVudCxcblx0XHRlbmNvZGluZ3NfZnJhZ21lbnQ6IGVuY29kaW5nc19mcmFnbWVudCxcblx0XHRlbmNvZGluZ3NfcGFyc19mcmFnbWVudDogZW5jb2RpbmdzX3BhcnNfZnJhZ21lbnQsXG5cdFx0ZW52bWFwX2ZyYWdtZW50OiBlbnZtYXBfZnJhZ21lbnQsXG5cdFx0ZW52bWFwX3BhcnNfZnJhZ21lbnQ6IGVudm1hcF9wYXJzX2ZyYWdtZW50LFxuXHRcdGVudm1hcF9wYXJzX3ZlcnRleDogZW52bWFwX3BhcnNfdmVydGV4LFxuXHRcdGVudm1hcF92ZXJ0ZXg6IGVudm1hcF92ZXJ0ZXgsXG5cdFx0Zm9nX2ZyYWdtZW50OiBmb2dfZnJhZ21lbnQsXG5cdFx0Zm9nX3BhcnNfZnJhZ21lbnQ6IGZvZ19wYXJzX2ZyYWdtZW50LFxuXHRcdGdyYWRpZW50bWFwX3BhcnNfZnJhZ21lbnQ6IGdyYWRpZW50bWFwX3BhcnNfZnJhZ21lbnQsXG5cdFx0bGlnaHRtYXBfZnJhZ21lbnQ6IGxpZ2h0bWFwX2ZyYWdtZW50LFxuXHRcdGxpZ2h0bWFwX3BhcnNfZnJhZ21lbnQ6IGxpZ2h0bWFwX3BhcnNfZnJhZ21lbnQsXG5cdFx0bGlnaHRzX2xhbWJlcnRfdmVydGV4OiBsaWdodHNfbGFtYmVydF92ZXJ0ZXgsXG5cdFx0bGlnaHRzX3BhcnM6IGxpZ2h0c19wYXJzLFxuXHRcdGxpZ2h0c19waG9uZ19mcmFnbWVudDogbGlnaHRzX3Bob25nX2ZyYWdtZW50LFxuXHRcdGxpZ2h0c19waG9uZ19wYXJzX2ZyYWdtZW50OiBsaWdodHNfcGhvbmdfcGFyc19mcmFnbWVudCxcblx0XHRsaWdodHNfcGh5c2ljYWxfZnJhZ21lbnQ6IGxpZ2h0c19waHlzaWNhbF9mcmFnbWVudCxcblx0XHRsaWdodHNfcGh5c2ljYWxfcGFyc19mcmFnbWVudDogbGlnaHRzX3BoeXNpY2FsX3BhcnNfZnJhZ21lbnQsXG5cdFx0bGlnaHRzX3RlbXBsYXRlOiBsaWdodHNfdGVtcGxhdGUsXG5cdFx0bG9nZGVwdGhidWZfZnJhZ21lbnQ6IGxvZ2RlcHRoYnVmX2ZyYWdtZW50LFxuXHRcdGxvZ2RlcHRoYnVmX3BhcnNfZnJhZ21lbnQ6IGxvZ2RlcHRoYnVmX3BhcnNfZnJhZ21lbnQsXG5cdFx0bG9nZGVwdGhidWZfcGFyc192ZXJ0ZXg6IGxvZ2RlcHRoYnVmX3BhcnNfdmVydGV4LFxuXHRcdGxvZ2RlcHRoYnVmX3ZlcnRleDogbG9nZGVwdGhidWZfdmVydGV4LFxuXHRcdG1hcF9mcmFnbWVudDogbWFwX2ZyYWdtZW50LFxuXHRcdG1hcF9wYXJzX2ZyYWdtZW50OiBtYXBfcGFyc19mcmFnbWVudCxcblx0XHRtYXBfcGFydGljbGVfZnJhZ21lbnQ6IG1hcF9wYXJ0aWNsZV9mcmFnbWVudCxcblx0XHRtYXBfcGFydGljbGVfcGFyc19mcmFnbWVudDogbWFwX3BhcnRpY2xlX3BhcnNfZnJhZ21lbnQsXG5cdFx0bWV0YWxuZXNzbWFwX2ZyYWdtZW50OiBtZXRhbG5lc3NtYXBfZnJhZ21lbnQsXG5cdFx0bWV0YWxuZXNzbWFwX3BhcnNfZnJhZ21lbnQ6IG1ldGFsbmVzc21hcF9wYXJzX2ZyYWdtZW50LFxuXHRcdG1vcnBobm9ybWFsX3ZlcnRleDogbW9ycGhub3JtYWxfdmVydGV4LFxuXHRcdG1vcnBodGFyZ2V0X3BhcnNfdmVydGV4OiBtb3JwaHRhcmdldF9wYXJzX3ZlcnRleCxcblx0XHRtb3JwaHRhcmdldF92ZXJ0ZXg6IG1vcnBodGFyZ2V0X3ZlcnRleCxcblx0XHRub3JtYWxfZmxpcDogbm9ybWFsX2ZsaXAsXG5cdFx0bm9ybWFsX2ZyYWdtZW50OiBub3JtYWxfZnJhZ21lbnQsXG5cdFx0bm9ybWFsbWFwX3BhcnNfZnJhZ21lbnQ6IG5vcm1hbG1hcF9wYXJzX2ZyYWdtZW50LFxuXHRcdHBhY2tpbmc6IHBhY2tpbmcsXG5cdFx0cHJlbXVsdGlwbGllZF9hbHBoYV9mcmFnbWVudDogcHJlbXVsdGlwbGllZF9hbHBoYV9mcmFnbWVudCxcblx0XHRwcm9qZWN0X3ZlcnRleDogcHJvamVjdF92ZXJ0ZXgsXG5cdFx0cm91Z2huZXNzbWFwX2ZyYWdtZW50OiByb3VnaG5lc3NtYXBfZnJhZ21lbnQsXG5cdFx0cm91Z2huZXNzbWFwX3BhcnNfZnJhZ21lbnQ6IHJvdWdobmVzc21hcF9wYXJzX2ZyYWdtZW50LFxuXHRcdHNoYWRvd21hcF9wYXJzX2ZyYWdtZW50OiBzaGFkb3dtYXBfcGFyc19mcmFnbWVudCxcblx0XHRzaGFkb3dtYXBfcGFyc192ZXJ0ZXg6IHNoYWRvd21hcF9wYXJzX3ZlcnRleCxcblx0XHRzaGFkb3dtYXBfdmVydGV4OiBzaGFkb3dtYXBfdmVydGV4LFxuXHRcdHNoYWRvd21hc2tfcGFyc19mcmFnbWVudDogc2hhZG93bWFza19wYXJzX2ZyYWdtZW50LFxuXHRcdHNraW5iYXNlX3ZlcnRleDogc2tpbmJhc2VfdmVydGV4LFxuXHRcdHNraW5uaW5nX3BhcnNfdmVydGV4OiBza2lubmluZ19wYXJzX3ZlcnRleCxcblx0XHRza2lubmluZ192ZXJ0ZXg6IHNraW5uaW5nX3ZlcnRleCxcblx0XHRza2lubm9ybWFsX3ZlcnRleDogc2tpbm5vcm1hbF92ZXJ0ZXgsXG5cdFx0c3BlY3VsYXJtYXBfZnJhZ21lbnQ6IHNwZWN1bGFybWFwX2ZyYWdtZW50LFxuXHRcdHNwZWN1bGFybWFwX3BhcnNfZnJhZ21lbnQ6IHNwZWN1bGFybWFwX3BhcnNfZnJhZ21lbnQsXG5cdFx0dG9uZW1hcHBpbmdfZnJhZ21lbnQ6IHRvbmVtYXBwaW5nX2ZyYWdtZW50LFxuXHRcdHRvbmVtYXBwaW5nX3BhcnNfZnJhZ21lbnQ6IHRvbmVtYXBwaW5nX3BhcnNfZnJhZ21lbnQsXG5cdFx0dXZfcGFyc19mcmFnbWVudDogdXZfcGFyc19mcmFnbWVudCxcblx0XHR1dl9wYXJzX3ZlcnRleDogdXZfcGFyc192ZXJ0ZXgsXG5cdFx0dXZfdmVydGV4OiB1dl92ZXJ0ZXgsXG5cdFx0dXYyX3BhcnNfZnJhZ21lbnQ6IHV2Ml9wYXJzX2ZyYWdtZW50LFxuXHRcdHV2Ml9wYXJzX3ZlcnRleDogdXYyX3BhcnNfdmVydGV4LFxuXHRcdHV2Ml92ZXJ0ZXg6IHV2Ml92ZXJ0ZXgsXG5cdFx0d29ybGRwb3NfdmVydGV4OiB3b3JsZHBvc192ZXJ0ZXgsXG5cblx0XHRjdWJlX2ZyYWc6IGN1YmVfZnJhZyxcblx0XHRjdWJlX3ZlcnQ6IGN1YmVfdmVydCxcblx0XHRkZXB0aF9mcmFnOiBkZXB0aF9mcmFnLFxuXHRcdGRlcHRoX3ZlcnQ6IGRlcHRoX3ZlcnQsXG5cdFx0ZGlzdGFuY2VSR0JBX2ZyYWc6IGRpc3RhbmNlUkdCQV9mcmFnLFxuXHRcdGRpc3RhbmNlUkdCQV92ZXJ0OiBkaXN0YW5jZVJHQkFfdmVydCxcblx0XHRlcXVpcmVjdF9mcmFnOiBlcXVpcmVjdF9mcmFnLFxuXHRcdGVxdWlyZWN0X3ZlcnQ6IGVxdWlyZWN0X3ZlcnQsXG5cdFx0bGluZWRhc2hlZF9mcmFnOiBsaW5lZGFzaGVkX2ZyYWcsXG5cdFx0bGluZWRhc2hlZF92ZXJ0OiBsaW5lZGFzaGVkX3ZlcnQsXG5cdFx0bWVzaGJhc2ljX2ZyYWc6IG1lc2hiYXNpY19mcmFnLFxuXHRcdG1lc2hiYXNpY192ZXJ0OiBtZXNoYmFzaWNfdmVydCxcblx0XHRtZXNobGFtYmVydF9mcmFnOiBtZXNobGFtYmVydF9mcmFnLFxuXHRcdG1lc2hsYW1iZXJ0X3ZlcnQ6IG1lc2hsYW1iZXJ0X3ZlcnQsXG5cdFx0bWVzaHBob25nX2ZyYWc6IG1lc2hwaG9uZ19mcmFnLFxuXHRcdG1lc2hwaG9uZ192ZXJ0OiBtZXNocGhvbmdfdmVydCxcblx0XHRtZXNocGh5c2ljYWxfZnJhZzogbWVzaHBoeXNpY2FsX2ZyYWcsXG5cdFx0bWVzaHBoeXNpY2FsX3ZlcnQ6IG1lc2hwaHlzaWNhbF92ZXJ0LFxuXHRcdG5vcm1hbF9mcmFnOiBub3JtYWxfZnJhZyxcblx0XHRub3JtYWxfdmVydDogbm9ybWFsX3ZlcnQsXG5cdFx0cG9pbnRzX2ZyYWc6IHBvaW50c19mcmFnLFxuXHRcdHBvaW50c192ZXJ0OiBwb2ludHNfdmVydCxcblx0XHRzaGFkb3dfZnJhZzogc2hhZG93X2ZyYWcsXG5cdFx0c2hhZG93X3ZlcnQ6IHNoYWRvd192ZXJ0XG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIENvbG9yKCByLCBnLCBiICkge1xuXG5cdFx0aWYgKCBnID09PSB1bmRlZmluZWQgJiYgYiA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHQvLyByIGlzIFRIUkVFLkNvbG9yLCBoZXggb3Igc3RyaW5nXG5cdFx0XHRyZXR1cm4gdGhpcy5zZXQoIHIgKTtcblxuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzLnNldFJHQiggciwgZywgYiApO1xuXG5cdH1cblxuXHRDb2xvci5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogQ29sb3IsXG5cblx0XHRpc0NvbG9yOiB0cnVlLFxuXG5cdFx0cjogMSwgZzogMSwgYjogMSxcblxuXHRcdHNldDogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0aWYgKCB2YWx1ZSAmJiB2YWx1ZS5pc0NvbG9yICkge1xuXG5cdFx0XHRcdHRoaXMuY29weSggdmFsdWUgKTtcblxuXHRcdFx0fSBlbHNlIGlmICggdHlwZW9mIHZhbHVlID09PSAnbnVtYmVyJyApIHtcblxuXHRcdFx0XHR0aGlzLnNldEhleCggdmFsdWUgKTtcblxuXHRcdFx0fSBlbHNlIGlmICggdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyApIHtcblxuXHRcdFx0XHR0aGlzLnNldFN0eWxlKCB2YWx1ZSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldFNjYWxhcjogZnVuY3Rpb24gKCBzY2FsYXIgKSB7XG5cblx0XHRcdHRoaXMuciA9IHNjYWxhcjtcblx0XHRcdHRoaXMuZyA9IHNjYWxhcjtcblx0XHRcdHRoaXMuYiA9IHNjYWxhcjtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0SGV4OiBmdW5jdGlvbiAoIGhleCApIHtcblxuXHRcdFx0aGV4ID0gTWF0aC5mbG9vciggaGV4ICk7XG5cblx0XHRcdHRoaXMuciA9ICggaGV4ID4+IDE2ICYgMjU1ICkgLyAyNTU7XG5cdFx0XHR0aGlzLmcgPSAoIGhleCA+PiA4ICYgMjU1ICkgLyAyNTU7XG5cdFx0XHR0aGlzLmIgPSAoIGhleCAmIDI1NSApIC8gMjU1O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRSR0I6IGZ1bmN0aW9uICggciwgZywgYiApIHtcblxuXHRcdFx0dGhpcy5yID0gcjtcblx0XHRcdHRoaXMuZyA9IGc7XG5cdFx0XHR0aGlzLmIgPSBiO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRIU0w6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0ZnVuY3Rpb24gaHVlMnJnYiggcCwgcSwgdCApIHtcblxuXHRcdFx0XHRpZiAoIHQgPCAwICkgdCArPSAxO1xuXHRcdFx0XHRpZiAoIHQgPiAxICkgdCAtPSAxO1xuXHRcdFx0XHRpZiAoIHQgPCAxIC8gNiApIHJldHVybiBwICsgKCBxIC0gcCApICogNiAqIHQ7XG5cdFx0XHRcdGlmICggdCA8IDEgLyAyICkgcmV0dXJuIHE7XG5cdFx0XHRcdGlmICggdCA8IDIgLyAzICkgcmV0dXJuIHAgKyAoIHEgLSBwICkgKiA2ICogKCAyIC8gMyAtIHQgKTtcblx0XHRcdFx0cmV0dXJuIHA7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIHNldEhTTCggaCwgcywgbCApIHtcblxuXHRcdFx0XHQvLyBoLHMsbCByYW5nZXMgYXJlIGluIDAuMCAtIDEuMFxuXHRcdFx0XHRoID0gX01hdGguZXVjbGlkZWFuTW9kdWxvKCBoLCAxICk7XG5cdFx0XHRcdHMgPSBfTWF0aC5jbGFtcCggcywgMCwgMSApO1xuXHRcdFx0XHRsID0gX01hdGguY2xhbXAoIGwsIDAsIDEgKTtcblxuXHRcdFx0XHRpZiAoIHMgPT09IDAgKSB7XG5cblx0XHRcdFx0XHR0aGlzLnIgPSB0aGlzLmcgPSB0aGlzLmIgPSBsO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHR2YXIgcCA9IGwgPD0gMC41ID8gbCAqICggMSArIHMgKSA6IGwgKyBzIC0gKCBsICogcyApO1xuXHRcdFx0XHRcdHZhciBxID0gKCAyICogbCApIC0gcDtcblxuXHRcdFx0XHRcdHRoaXMuciA9IGh1ZTJyZ2IoIHEsIHAsIGggKyAxIC8gMyApO1xuXHRcdFx0XHRcdHRoaXMuZyA9IGh1ZTJyZ2IoIHEsIHAsIGggKTtcblx0XHRcdFx0XHR0aGlzLmIgPSBodWUycmdiKCBxLCBwLCBoIC0gMSAvIDMgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRzZXRTdHlsZTogZnVuY3Rpb24gKCBzdHlsZSApIHtcblxuXHRcdFx0ZnVuY3Rpb24gaGFuZGxlQWxwaGEoIHN0cmluZyApIHtcblxuXHRcdFx0XHRpZiAoIHN0cmluZyA9PT0gdW5kZWZpbmVkICkgcmV0dXJuO1xuXG5cdFx0XHRcdGlmICggcGFyc2VGbG9hdCggc3RyaW5nICkgPCAxICkge1xuXG5cdFx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuQ29sb3I6IEFscGhhIGNvbXBvbmVudCBvZiAnICsgc3R5bGUgKyAnIHdpbGwgYmUgaWdub3JlZC4nICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblxuXHRcdFx0dmFyIG07XG5cblx0XHRcdGlmICggbSA9IC9eKCg/OnJnYnxoc2wpYT8pXFwoXFxzKihbXlxcKV0qKVxcKS8uZXhlYyggc3R5bGUgKSApIHtcblxuXHRcdFx0XHQvLyByZ2IgLyBoc2xcblxuXHRcdFx0XHR2YXIgY29sb3I7XG5cdFx0XHRcdHZhciBuYW1lID0gbVsgMSBdO1xuXHRcdFx0XHR2YXIgY29tcG9uZW50cyA9IG1bIDIgXTtcblxuXHRcdFx0XHRzd2l0Y2ggKCBuYW1lICkge1xuXG5cdFx0XHRcdFx0Y2FzZSAncmdiJzpcblx0XHRcdFx0XHRjYXNlICdyZ2JhJzpcblxuXHRcdFx0XHRcdFx0aWYgKCBjb2xvciA9IC9eKFxcZCspXFxzKixcXHMqKFxcZCspXFxzKixcXHMqKFxcZCspXFxzKigsXFxzKihbMC05XSpcXC4/WzAtOV0rKVxccyopPyQvLmV4ZWMoIGNvbXBvbmVudHMgKSApIHtcblxuXHRcdFx0XHRcdFx0XHQvLyByZ2IoMjU1LDAsMCkgcmdiYSgyNTUsMCwwLDAuNSlcblx0XHRcdFx0XHRcdFx0dGhpcy5yID0gTWF0aC5taW4oIDI1NSwgcGFyc2VJbnQoIGNvbG9yWyAxIF0sIDEwICkgKSAvIDI1NTtcblx0XHRcdFx0XHRcdFx0dGhpcy5nID0gTWF0aC5taW4oIDI1NSwgcGFyc2VJbnQoIGNvbG9yWyAyIF0sIDEwICkgKSAvIDI1NTtcblx0XHRcdFx0XHRcdFx0dGhpcy5iID0gTWF0aC5taW4oIDI1NSwgcGFyc2VJbnQoIGNvbG9yWyAzIF0sIDEwICkgKSAvIDI1NTtcblxuXHRcdFx0XHRcdFx0XHRoYW5kbGVBbHBoYSggY29sb3JbIDUgXSApO1xuXG5cdFx0XHRcdFx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdGlmICggY29sb3IgPSAvXihcXGQrKVxcJVxccyosXFxzKihcXGQrKVxcJVxccyosXFxzKihcXGQrKVxcJVxccyooLFxccyooWzAtOV0qXFwuP1swLTldKylcXHMqKT8kLy5leGVjKCBjb21wb25lbnRzICkgKSB7XG5cblx0XHRcdFx0XHRcdFx0Ly8gcmdiKDEwMCUsMCUsMCUpIHJnYmEoMTAwJSwwJSwwJSwwLjUpXG5cdFx0XHRcdFx0XHRcdHRoaXMuciA9IE1hdGgubWluKCAxMDAsIHBhcnNlSW50KCBjb2xvclsgMSBdLCAxMCApICkgLyAxMDA7XG5cdFx0XHRcdFx0XHRcdHRoaXMuZyA9IE1hdGgubWluKCAxMDAsIHBhcnNlSW50KCBjb2xvclsgMiBdLCAxMCApICkgLyAxMDA7XG5cdFx0XHRcdFx0XHRcdHRoaXMuYiA9IE1hdGgubWluKCAxMDAsIHBhcnNlSW50KCBjb2xvclsgMyBdLCAxMCApICkgLyAxMDA7XG5cblx0XHRcdFx0XHRcdFx0aGFuZGxlQWxwaGEoIGNvbG9yWyA1IF0gKTtcblxuXHRcdFx0XHRcdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdGNhc2UgJ2hzbCc6XG5cdFx0XHRcdFx0Y2FzZSAnaHNsYSc6XG5cblx0XHRcdFx0XHRcdGlmICggY29sb3IgPSAvXihbMC05XSpcXC4/WzAtOV0rKVxccyosXFxzKihcXGQrKVxcJVxccyosXFxzKihcXGQrKVxcJVxccyooLFxccyooWzAtOV0qXFwuP1swLTldKylcXHMqKT8kLy5leGVjKCBjb21wb25lbnRzICkgKSB7XG5cblx0XHRcdFx0XHRcdFx0Ly8gaHNsKDEyMCw1MCUsNTAlKSBoc2xhKDEyMCw1MCUsNTAlLDAuNSlcblx0XHRcdFx0XHRcdFx0dmFyIGggPSBwYXJzZUZsb2F0KCBjb2xvclsgMSBdICkgLyAzNjA7XG5cdFx0XHRcdFx0XHRcdHZhciBzID0gcGFyc2VJbnQoIGNvbG9yWyAyIF0sIDEwICkgLyAxMDA7XG5cdFx0XHRcdFx0XHRcdHZhciBsID0gcGFyc2VJbnQoIGNvbG9yWyAzIF0sIDEwICkgLyAxMDA7XG5cblx0XHRcdFx0XHRcdFx0aGFuZGxlQWxwaGEoIGNvbG9yWyA1IF0gKTtcblxuXHRcdFx0XHRcdFx0XHRyZXR1cm4gdGhpcy5zZXRIU0woIGgsIHMsIGwgKTtcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHR9XG5cblx0XHRcdH0gZWxzZSBpZiAoIG0gPSAvXlxcIyhbQS1GYS1mMC05XSspJC8uZXhlYyggc3R5bGUgKSApIHtcblxuXHRcdFx0XHQvLyBoZXggY29sb3JcblxuXHRcdFx0XHR2YXIgaGV4ID0gbVsgMSBdO1xuXHRcdFx0XHR2YXIgc2l6ZSA9IGhleC5sZW5ndGg7XG5cblx0XHRcdFx0aWYgKCBzaXplID09PSAzICkge1xuXG5cdFx0XHRcdFx0Ly8gI2ZmMFxuXHRcdFx0XHRcdHRoaXMuciA9IHBhcnNlSW50KCBoZXguY2hhckF0KCAwICkgKyBoZXguY2hhckF0KCAwICksIDE2ICkgLyAyNTU7XG5cdFx0XHRcdFx0dGhpcy5nID0gcGFyc2VJbnQoIGhleC5jaGFyQXQoIDEgKSArIGhleC5jaGFyQXQoIDEgKSwgMTYgKSAvIDI1NTtcblx0XHRcdFx0XHR0aGlzLmIgPSBwYXJzZUludCggaGV4LmNoYXJBdCggMiApICsgaGV4LmNoYXJBdCggMiApLCAxNiApIC8gMjU1O1xuXG5cdFx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHRcdFx0fSBlbHNlIGlmICggc2l6ZSA9PT0gNiApIHtcblxuXHRcdFx0XHRcdC8vICNmZjAwMDBcblx0XHRcdFx0XHR0aGlzLnIgPSBwYXJzZUludCggaGV4LmNoYXJBdCggMCApICsgaGV4LmNoYXJBdCggMSApLCAxNiApIC8gMjU1O1xuXHRcdFx0XHRcdHRoaXMuZyA9IHBhcnNlSW50KCBoZXguY2hhckF0KCAyICkgKyBoZXguY2hhckF0KCAzICksIDE2ICkgLyAyNTU7XG5cdFx0XHRcdFx0dGhpcy5iID0gcGFyc2VJbnQoIGhleC5jaGFyQXQoIDQgKSArIGhleC5jaGFyQXQoIDUgKSwgMTYgKSAvIDI1NTtcblxuXHRcdFx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHN0eWxlICYmIHN0eWxlLmxlbmd0aCA+IDAgKSB7XG5cblx0XHRcdFx0Ly8gY29sb3Iga2V5d29yZHNcblx0XHRcdFx0dmFyIGhleCA9IENvbG9yS2V5d29yZHNbIHN0eWxlIF07XG5cblx0XHRcdFx0aWYgKCBoZXggIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdC8vIHJlZFxuXHRcdFx0XHRcdHRoaXMuc2V0SGV4KCBoZXggKTtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0Ly8gdW5rbm93biBjb2xvclxuXHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkNvbG9yOiBVbmtub3duIGNvbG9yICcgKyBzdHlsZSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjbG9uZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IHRoaXMuY29uc3RydWN0b3IoIHRoaXMuciwgdGhpcy5nLCB0aGlzLmIgKTtcblxuXHRcdH0sXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIGNvbG9yICkge1xuXG5cdFx0XHR0aGlzLnIgPSBjb2xvci5yO1xuXHRcdFx0dGhpcy5nID0gY29sb3IuZztcblx0XHRcdHRoaXMuYiA9IGNvbG9yLmI7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGNvcHlHYW1tYVRvTGluZWFyOiBmdW5jdGlvbiAoIGNvbG9yLCBnYW1tYUZhY3RvciApIHtcblxuXHRcdFx0aWYgKCBnYW1tYUZhY3RvciA9PT0gdW5kZWZpbmVkICkgZ2FtbWFGYWN0b3IgPSAyLjA7XG5cblx0XHRcdHRoaXMuciA9IE1hdGgucG93KCBjb2xvci5yLCBnYW1tYUZhY3RvciApO1xuXHRcdFx0dGhpcy5nID0gTWF0aC5wb3coIGNvbG9yLmcsIGdhbW1hRmFjdG9yICk7XG5cdFx0XHR0aGlzLmIgPSBNYXRoLnBvdyggY29sb3IuYiwgZ2FtbWFGYWN0b3IgKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Y29weUxpbmVhclRvR2FtbWE6IGZ1bmN0aW9uICggY29sb3IsIGdhbW1hRmFjdG9yICkge1xuXG5cdFx0XHRpZiAoIGdhbW1hRmFjdG9yID09PSB1bmRlZmluZWQgKSBnYW1tYUZhY3RvciA9IDIuMDtcblxuXHRcdFx0dmFyIHNhZmVJbnZlcnNlID0gKCBnYW1tYUZhY3RvciA+IDAgKSA/ICggMS4wIC8gZ2FtbWFGYWN0b3IgKSA6IDEuMDtcblxuXHRcdFx0dGhpcy5yID0gTWF0aC5wb3coIGNvbG9yLnIsIHNhZmVJbnZlcnNlICk7XG5cdFx0XHR0aGlzLmcgPSBNYXRoLnBvdyggY29sb3IuZywgc2FmZUludmVyc2UgKTtcblx0XHRcdHRoaXMuYiA9IE1hdGgucG93KCBjb2xvci5iLCBzYWZlSW52ZXJzZSApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjb252ZXJ0R2FtbWFUb0xpbmVhcjogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgciA9IHRoaXMuciwgZyA9IHRoaXMuZywgYiA9IHRoaXMuYjtcblxuXHRcdFx0dGhpcy5yID0gciAqIHI7XG5cdFx0XHR0aGlzLmcgPSBnICogZztcblx0XHRcdHRoaXMuYiA9IGIgKiBiO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjb252ZXJ0TGluZWFyVG9HYW1tYTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR0aGlzLnIgPSBNYXRoLnNxcnQoIHRoaXMuciApO1xuXHRcdFx0dGhpcy5nID0gTWF0aC5zcXJ0KCB0aGlzLmcgKTtcblx0XHRcdHRoaXMuYiA9IE1hdGguc3FydCggdGhpcy5iICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGdldEhleDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gKCB0aGlzLnIgKiAyNTUgKSA8PCAxNiBeICggdGhpcy5nICogMjU1ICkgPDwgOCBeICggdGhpcy5iICogMjU1ICkgPDwgMDtcblxuXHRcdH0sXG5cblx0XHRnZXRIZXhTdHJpbmc6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuICggJzAwMDAwMCcgKyB0aGlzLmdldEhleCgpLnRvU3RyaW5nKCAxNiApICkuc2xpY2UoIC0gNiApO1xuXG5cdFx0fSxcblxuXHRcdGdldEhTTDogZnVuY3Rpb24gKCBvcHRpb25hbFRhcmdldCApIHtcblxuXHRcdFx0Ly8gaCxzLGwgcmFuZ2VzIGFyZSBpbiAwLjAgLSAxLjBcblxuXHRcdFx0dmFyIGhzbCA9IG9wdGlvbmFsVGFyZ2V0IHx8IHsgaDogMCwgczogMCwgbDogMCB9O1xuXG5cdFx0XHR2YXIgciA9IHRoaXMuciwgZyA9IHRoaXMuZywgYiA9IHRoaXMuYjtcblxuXHRcdFx0dmFyIG1heCA9IE1hdGgubWF4KCByLCBnLCBiICk7XG5cdFx0XHR2YXIgbWluID0gTWF0aC5taW4oIHIsIGcsIGIgKTtcblxuXHRcdFx0dmFyIGh1ZSwgc2F0dXJhdGlvbjtcblx0XHRcdHZhciBsaWdodG5lc3MgPSAoIG1pbiArIG1heCApIC8gMi4wO1xuXG5cdFx0XHRpZiAoIG1pbiA9PT0gbWF4ICkge1xuXG5cdFx0XHRcdGh1ZSA9IDA7XG5cdFx0XHRcdHNhdHVyYXRpb24gPSAwO1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdHZhciBkZWx0YSA9IG1heCAtIG1pbjtcblxuXHRcdFx0XHRzYXR1cmF0aW9uID0gbGlnaHRuZXNzIDw9IDAuNSA/IGRlbHRhIC8gKCBtYXggKyBtaW4gKSA6IGRlbHRhIC8gKCAyIC0gbWF4IC0gbWluICk7XG5cblx0XHRcdFx0c3dpdGNoICggbWF4ICkge1xuXG5cdFx0XHRcdFx0Y2FzZSByOiBodWUgPSAoIGcgLSBiICkgLyBkZWx0YSArICggZyA8IGIgPyA2IDogMCApOyBicmVhaztcblx0XHRcdFx0XHRjYXNlIGc6IGh1ZSA9ICggYiAtIHIgKSAvIGRlbHRhICsgMjsgYnJlYWs7XG5cdFx0XHRcdFx0Y2FzZSBiOiBodWUgPSAoIHIgLSBnICkgLyBkZWx0YSArIDQ7IGJyZWFrO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRodWUgLz0gNjtcblxuXHRcdFx0fVxuXG5cdFx0XHRoc2wuaCA9IGh1ZTtcblx0XHRcdGhzbC5zID0gc2F0dXJhdGlvbjtcblx0XHRcdGhzbC5sID0gbGlnaHRuZXNzO1xuXG5cdFx0XHRyZXR1cm4gaHNsO1xuXG5cdFx0fSxcblxuXHRcdGdldFN0eWxlOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiAncmdiKCcgKyAoICggdGhpcy5yICogMjU1ICkgfCAwICkgKyAnLCcgKyAoICggdGhpcy5nICogMjU1ICkgfCAwICkgKyAnLCcgKyAoICggdGhpcy5iICogMjU1ICkgfCAwICkgKyAnKSc7XG5cblx0XHR9LFxuXG5cdFx0b2Zmc2V0SFNMOiBmdW5jdGlvbiAoIGgsIHMsIGwgKSB7XG5cblx0XHRcdHZhciBoc2wgPSB0aGlzLmdldEhTTCgpO1xuXG5cdFx0XHRoc2wuaCArPSBoOyBoc2wucyArPSBzOyBoc2wubCArPSBsO1xuXG5cdFx0XHR0aGlzLnNldEhTTCggaHNsLmgsIGhzbC5zLCBoc2wubCApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRhZGQ6IGZ1bmN0aW9uICggY29sb3IgKSB7XG5cblx0XHRcdHRoaXMuciArPSBjb2xvci5yO1xuXHRcdFx0dGhpcy5nICs9IGNvbG9yLmc7XG5cdFx0XHR0aGlzLmIgKz0gY29sb3IuYjtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0YWRkQ29sb3JzOiBmdW5jdGlvbiAoIGNvbG9yMSwgY29sb3IyICkge1xuXG5cdFx0XHR0aGlzLnIgPSBjb2xvcjEuciArIGNvbG9yMi5yO1xuXHRcdFx0dGhpcy5nID0gY29sb3IxLmcgKyBjb2xvcjIuZztcblx0XHRcdHRoaXMuYiA9IGNvbG9yMS5iICsgY29sb3IyLmI7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGFkZFNjYWxhcjogZnVuY3Rpb24gKCBzICkge1xuXG5cdFx0XHR0aGlzLnIgKz0gcztcblx0XHRcdHRoaXMuZyArPSBzO1xuXHRcdFx0dGhpcy5iICs9IHM7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHN1YjogZnVuY3Rpb24oIGNvbG9yICkge1xuXG5cdFx0XHR0aGlzLnIgPSBNYXRoLm1heCggMCwgdGhpcy5yIC0gY29sb3IuciApO1xuXHRcdFx0dGhpcy5nID0gTWF0aC5tYXgoIDAsIHRoaXMuZyAtIGNvbG9yLmcgKTtcblx0XHRcdHRoaXMuYiA9IE1hdGgubWF4KCAwLCB0aGlzLmIgLSBjb2xvci5iICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdG11bHRpcGx5OiBmdW5jdGlvbiAoIGNvbG9yICkge1xuXG5cdFx0XHR0aGlzLnIgKj0gY29sb3Iucjtcblx0XHRcdHRoaXMuZyAqPSBjb2xvci5nO1xuXHRcdFx0dGhpcy5iICo9IGNvbG9yLmI7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdG11bHRpcGx5U2NhbGFyOiBmdW5jdGlvbiAoIHMgKSB7XG5cblx0XHRcdHRoaXMuciAqPSBzO1xuXHRcdFx0dGhpcy5nICo9IHM7XG5cdFx0XHR0aGlzLmIgKj0gcztcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0bGVycDogZnVuY3Rpb24gKCBjb2xvciwgYWxwaGEgKSB7XG5cblx0XHRcdHRoaXMuciArPSAoIGNvbG9yLnIgLSB0aGlzLnIgKSAqIGFscGhhO1xuXHRcdFx0dGhpcy5nICs9ICggY29sb3IuZyAtIHRoaXMuZyApICogYWxwaGE7XG5cdFx0XHR0aGlzLmIgKz0gKCBjb2xvci5iIC0gdGhpcy5iICkgKiBhbHBoYTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0ZXF1YWxzOiBmdW5jdGlvbiAoIGMgKSB7XG5cblx0XHRcdHJldHVybiAoIGMuciA9PT0gdGhpcy5yICkgJiYgKCBjLmcgPT09IHRoaXMuZyApICYmICggYy5iID09PSB0aGlzLmIgKTtcblxuXHRcdH0sXG5cblx0XHRmcm9tQXJyYXk6IGZ1bmN0aW9uICggYXJyYXksIG9mZnNldCApIHtcblxuXHRcdFx0aWYgKCBvZmZzZXQgPT09IHVuZGVmaW5lZCApIG9mZnNldCA9IDA7XG5cblx0XHRcdHRoaXMuciA9IGFycmF5WyBvZmZzZXQgXTtcblx0XHRcdHRoaXMuZyA9IGFycmF5WyBvZmZzZXQgKyAxIF07XG5cdFx0XHR0aGlzLmIgPSBhcnJheVsgb2Zmc2V0ICsgMiBdO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHR0b0FycmF5OiBmdW5jdGlvbiAoIGFycmF5LCBvZmZzZXQgKSB7XG5cblx0XHRcdGlmICggYXJyYXkgPT09IHVuZGVmaW5lZCApIGFycmF5ID0gW107XG5cdFx0XHRpZiAoIG9mZnNldCA9PT0gdW5kZWZpbmVkICkgb2Zmc2V0ID0gMDtcblxuXHRcdFx0YXJyYXlbIG9mZnNldCBdID0gdGhpcy5yO1xuXHRcdFx0YXJyYXlbIG9mZnNldCArIDEgXSA9IHRoaXMuZztcblx0XHRcdGFycmF5WyBvZmZzZXQgKyAyIF0gPSB0aGlzLmI7XG5cblx0XHRcdHJldHVybiBhcnJheTtcblxuXHRcdH0sXG5cblx0XHR0b0pTT046IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuZ2V0SGV4KCk7XG5cblx0XHR9XG5cblx0fTtcblxuXHR2YXIgQ29sb3JLZXl3b3JkcyA9IHsgJ2FsaWNlYmx1ZSc6IDB4RjBGOEZGLCAnYW50aXF1ZXdoaXRlJzogMHhGQUVCRDcsICdhcXVhJzogMHgwMEZGRkYsICdhcXVhbWFyaW5lJzogMHg3RkZGRDQsICdhenVyZSc6IDB4RjBGRkZGLFxuXHQnYmVpZ2UnOiAweEY1RjVEQywgJ2Jpc3F1ZSc6IDB4RkZFNEM0LCAnYmxhY2snOiAweDAwMDAwMCwgJ2JsYW5jaGVkYWxtb25kJzogMHhGRkVCQ0QsICdibHVlJzogMHgwMDAwRkYsICdibHVldmlvbGV0JzogMHg4QTJCRTIsXG5cdCdicm93bic6IDB4QTUyQTJBLCAnYnVybHl3b29kJzogMHhERUI4ODcsICdjYWRldGJsdWUnOiAweDVGOUVBMCwgJ2NoYXJ0cmV1c2UnOiAweDdGRkYwMCwgJ2Nob2NvbGF0ZSc6IDB4RDI2OTFFLCAnY29yYWwnOiAweEZGN0Y1MCxcblx0J2Nvcm5mbG93ZXJibHVlJzogMHg2NDk1RUQsICdjb3Juc2lsayc6IDB4RkZGOERDLCAnY3JpbXNvbic6IDB4REMxNDNDLCAnY3lhbic6IDB4MDBGRkZGLCAnZGFya2JsdWUnOiAweDAwMDA4QiwgJ2RhcmtjeWFuJzogMHgwMDhCOEIsXG5cdCdkYXJrZ29sZGVucm9kJzogMHhCODg2MEIsICdkYXJrZ3JheSc6IDB4QTlBOUE5LCAnZGFya2dyZWVuJzogMHgwMDY0MDAsICdkYXJrZ3JleSc6IDB4QTlBOUE5LCAnZGFya2toYWtpJzogMHhCREI3NkIsICdkYXJrbWFnZW50YSc6IDB4OEIwMDhCLFxuXHQnZGFya29saXZlZ3JlZW4nOiAweDU1NkIyRiwgJ2RhcmtvcmFuZ2UnOiAweEZGOEMwMCwgJ2RhcmtvcmNoaWQnOiAweDk5MzJDQywgJ2RhcmtyZWQnOiAweDhCMDAwMCwgJ2RhcmtzYWxtb24nOiAweEU5OTY3QSwgJ2RhcmtzZWFncmVlbic6IDB4OEZCQzhGLFxuXHQnZGFya3NsYXRlYmx1ZSc6IDB4NDgzRDhCLCAnZGFya3NsYXRlZ3JheSc6IDB4MkY0RjRGLCAnZGFya3NsYXRlZ3JleSc6IDB4MkY0RjRGLCAnZGFya3R1cnF1b2lzZSc6IDB4MDBDRUQxLCAnZGFya3Zpb2xldCc6IDB4OTQwMEQzLFxuXHQnZGVlcHBpbmsnOiAweEZGMTQ5MywgJ2RlZXBza3libHVlJzogMHgwMEJGRkYsICdkaW1ncmF5JzogMHg2OTY5NjksICdkaW1ncmV5JzogMHg2OTY5NjksICdkb2RnZXJibHVlJzogMHgxRTkwRkYsICdmaXJlYnJpY2snOiAweEIyMjIyMixcblx0J2Zsb3JhbHdoaXRlJzogMHhGRkZBRjAsICdmb3Jlc3RncmVlbic6IDB4MjI4QjIyLCAnZnVjaHNpYSc6IDB4RkYwMEZGLCAnZ2FpbnNib3JvJzogMHhEQ0RDREMsICdnaG9zdHdoaXRlJzogMHhGOEY4RkYsICdnb2xkJzogMHhGRkQ3MDAsXG5cdCdnb2xkZW5yb2QnOiAweERBQTUyMCwgJ2dyYXknOiAweDgwODA4MCwgJ2dyZWVuJzogMHgwMDgwMDAsICdncmVlbnllbGxvdyc6IDB4QURGRjJGLCAnZ3JleSc6IDB4ODA4MDgwLCAnaG9uZXlkZXcnOiAweEYwRkZGMCwgJ2hvdHBpbmsnOiAweEZGNjlCNCxcblx0J2luZGlhbnJlZCc6IDB4Q0Q1QzVDLCAnaW5kaWdvJzogMHg0QjAwODIsICdpdm9yeSc6IDB4RkZGRkYwLCAna2hha2knOiAweEYwRTY4QywgJ2xhdmVuZGVyJzogMHhFNkU2RkEsICdsYXZlbmRlcmJsdXNoJzogMHhGRkYwRjUsICdsYXduZ3JlZW4nOiAweDdDRkMwMCxcblx0J2xlbW9uY2hpZmZvbic6IDB4RkZGQUNELCAnbGlnaHRibHVlJzogMHhBREQ4RTYsICdsaWdodGNvcmFsJzogMHhGMDgwODAsICdsaWdodGN5YW4nOiAweEUwRkZGRiwgJ2xpZ2h0Z29sZGVucm9keWVsbG93JzogMHhGQUZBRDIsICdsaWdodGdyYXknOiAweEQzRDNEMyxcblx0J2xpZ2h0Z3JlZW4nOiAweDkwRUU5MCwgJ2xpZ2h0Z3JleSc6IDB4RDNEM0QzLCAnbGlnaHRwaW5rJzogMHhGRkI2QzEsICdsaWdodHNhbG1vbic6IDB4RkZBMDdBLCAnbGlnaHRzZWFncmVlbic6IDB4MjBCMkFBLCAnbGlnaHRza3libHVlJzogMHg4N0NFRkEsXG5cdCdsaWdodHNsYXRlZ3JheSc6IDB4Nzc4ODk5LCAnbGlnaHRzbGF0ZWdyZXknOiAweDc3ODg5OSwgJ2xpZ2h0c3RlZWxibHVlJzogMHhCMEM0REUsICdsaWdodHllbGxvdyc6IDB4RkZGRkUwLCAnbGltZSc6IDB4MDBGRjAwLCAnbGltZWdyZWVuJzogMHgzMkNEMzIsXG5cdCdsaW5lbic6IDB4RkFGMEU2LCAnbWFnZW50YSc6IDB4RkYwMEZGLCAnbWFyb29uJzogMHg4MDAwMDAsICdtZWRpdW1hcXVhbWFyaW5lJzogMHg2NkNEQUEsICdtZWRpdW1ibHVlJzogMHgwMDAwQ0QsICdtZWRpdW1vcmNoaWQnOiAweEJBNTVEMyxcblx0J21lZGl1bXB1cnBsZSc6IDB4OTM3MERCLCAnbWVkaXVtc2VhZ3JlZW4nOiAweDNDQjM3MSwgJ21lZGl1bXNsYXRlYmx1ZSc6IDB4N0I2OEVFLCAnbWVkaXVtc3ByaW5nZ3JlZW4nOiAweDAwRkE5QSwgJ21lZGl1bXR1cnF1b2lzZSc6IDB4NDhEMUNDLFxuXHQnbWVkaXVtdmlvbGV0cmVkJzogMHhDNzE1ODUsICdtaWRuaWdodGJsdWUnOiAweDE5MTk3MCwgJ21pbnRjcmVhbSc6IDB4RjVGRkZBLCAnbWlzdHlyb3NlJzogMHhGRkU0RTEsICdtb2NjYXNpbic6IDB4RkZFNEI1LCAnbmF2YWpvd2hpdGUnOiAweEZGREVBRCxcblx0J25hdnknOiAweDAwMDA4MCwgJ29sZGxhY2UnOiAweEZERjVFNiwgJ29saXZlJzogMHg4MDgwMDAsICdvbGl2ZWRyYWInOiAweDZCOEUyMywgJ29yYW5nZSc6IDB4RkZBNTAwLCAnb3JhbmdlcmVkJzogMHhGRjQ1MDAsICdvcmNoaWQnOiAweERBNzBENixcblx0J3BhbGVnb2xkZW5yb2QnOiAweEVFRThBQSwgJ3BhbGVncmVlbic6IDB4OThGQjk4LCAncGFsZXR1cnF1b2lzZSc6IDB4QUZFRUVFLCAncGFsZXZpb2xldHJlZCc6IDB4REI3MDkzLCAncGFwYXlhd2hpcCc6IDB4RkZFRkQ1LCAncGVhY2hwdWZmJzogMHhGRkRBQjksXG5cdCdwZXJ1JzogMHhDRDg1M0YsICdwaW5rJzogMHhGRkMwQ0IsICdwbHVtJzogMHhEREEwREQsICdwb3dkZXJibHVlJzogMHhCMEUwRTYsICdwdXJwbGUnOiAweDgwMDA4MCwgJ3JlZCc6IDB4RkYwMDAwLCAncm9zeWJyb3duJzogMHhCQzhGOEYsXG5cdCdyb3lhbGJsdWUnOiAweDQxNjlFMSwgJ3NhZGRsZWJyb3duJzogMHg4QjQ1MTMsICdzYWxtb24nOiAweEZBODA3MiwgJ3NhbmR5YnJvd24nOiAweEY0QTQ2MCwgJ3NlYWdyZWVuJzogMHgyRThCNTcsICdzZWFzaGVsbCc6IDB4RkZGNUVFLFxuXHQnc2llbm5hJzogMHhBMDUyMkQsICdzaWx2ZXInOiAweEMwQzBDMCwgJ3NreWJsdWUnOiAweDg3Q0VFQiwgJ3NsYXRlYmx1ZSc6IDB4NkE1QUNELCAnc2xhdGVncmF5JzogMHg3MDgwOTAsICdzbGF0ZWdyZXknOiAweDcwODA5MCwgJ3Nub3cnOiAweEZGRkFGQSxcblx0J3NwcmluZ2dyZWVuJzogMHgwMEZGN0YsICdzdGVlbGJsdWUnOiAweDQ2ODJCNCwgJ3Rhbic6IDB4RDJCNDhDLCAndGVhbCc6IDB4MDA4MDgwLCAndGhpc3RsZSc6IDB4RDhCRkQ4LCAndG9tYXRvJzogMHhGRjYzNDcsICd0dXJxdW9pc2UnOiAweDQwRTBEMCxcblx0J3Zpb2xldCc6IDB4RUU4MkVFLCAnd2hlYXQnOiAweEY1REVCMywgJ3doaXRlJzogMHhGRkZGRkYsICd3aGl0ZXNtb2tlJzogMHhGNUY1RjUsICd5ZWxsb3cnOiAweEZGRkYwMCwgJ3llbGxvd2dyZWVuJzogMHg5QUNEMzIgfTtcblxuXHQvKipcblx0ICogQGF1dGhvciBhbHRlcmVkcSAvIGh0dHA6Ly9hbHRlcmVkcXVhbGlhLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gRGF0YVRleHR1cmUoIGRhdGEsIHdpZHRoLCBoZWlnaHQsIGZvcm1hdCwgdHlwZSwgbWFwcGluZywgd3JhcFMsIHdyYXBULCBtYWdGaWx0ZXIsIG1pbkZpbHRlciwgYW5pc290cm9weSwgZW5jb2RpbmcgKSB7XG5cblx0XHRUZXh0dXJlLmNhbGwoIHRoaXMsIG51bGwsIG1hcHBpbmcsIHdyYXBTLCB3cmFwVCwgbWFnRmlsdGVyLCBtaW5GaWx0ZXIsIGZvcm1hdCwgdHlwZSwgYW5pc290cm9weSwgZW5jb2RpbmcgKTtcblxuXHRcdHRoaXMuaW1hZ2UgPSB7IGRhdGE6IGRhdGEsIHdpZHRoOiB3aWR0aCwgaGVpZ2h0OiBoZWlnaHQgfTtcblxuXHRcdHRoaXMubWFnRmlsdGVyID0gbWFnRmlsdGVyICE9PSB1bmRlZmluZWQgPyBtYWdGaWx0ZXIgOiBOZWFyZXN0RmlsdGVyO1xuXHRcdHRoaXMubWluRmlsdGVyID0gbWluRmlsdGVyICE9PSB1bmRlZmluZWQgPyBtaW5GaWx0ZXIgOiBOZWFyZXN0RmlsdGVyO1xuXG5cdFx0dGhpcy5nZW5lcmF0ZU1pcG1hcHMgID0gZmFsc2U7XG5cdFx0dGhpcy5mbGlwWSA9IGZhbHNlO1xuXHRcdHRoaXMudW5wYWNrQWxpZ25tZW50ID0gMTtcblxuXHR9XG5cblx0RGF0YVRleHR1cmUucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggVGV4dHVyZS5wcm90b3R5cGUgKTtcblx0RGF0YVRleHR1cmUucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gRGF0YVRleHR1cmU7XG5cblx0RGF0YVRleHR1cmUucHJvdG90eXBlLmlzRGF0YVRleHR1cmUgPSB0cnVlO1xuXG5cdC8qKlxuXHQgKiBVbmlmb3JtcyBsaWJyYXJ5IGZvciBzaGFyZWQgd2ViZ2wgc2hhZGVyc1xuXHQgKi9cblxuXHR2YXIgVW5pZm9ybXNMaWIgPSB7XG5cblx0XHRjb21tb246IHtcblxuXHRcdFx0ZGlmZnVzZTogeyB2YWx1ZTogbmV3IENvbG9yKCAweGVlZWVlZSApIH0sXG5cdFx0XHRvcGFjaXR5OiB7IHZhbHVlOiAxLjAgfSxcblxuXHRcdFx0bWFwOiB7IHZhbHVlOiBudWxsIH0sXG5cdFx0XHRvZmZzZXRSZXBlYXQ6IHsgdmFsdWU6IG5ldyBWZWN0b3I0KCAwLCAwLCAxLCAxICkgfSxcblxuXHRcdFx0c3BlY3VsYXJNYXA6IHsgdmFsdWU6IG51bGwgfSxcblx0XHRcdGFscGhhTWFwOiB7IHZhbHVlOiBudWxsIH0sXG5cblx0XHRcdGVudk1hcDogeyB2YWx1ZTogbnVsbCB9LFxuXHRcdFx0ZmxpcEVudk1hcDogeyB2YWx1ZTogLSAxIH0sXG5cdFx0XHRyZWZsZWN0aXZpdHk6IHsgdmFsdWU6IDEuMCB9LFxuXHRcdFx0cmVmcmFjdGlvblJhdGlvOiB7IHZhbHVlOiAwLjk4IH1cblxuXHRcdH0sXG5cblx0XHRhb21hcDoge1xuXG5cdFx0XHRhb01hcDogeyB2YWx1ZTogbnVsbCB9LFxuXHRcdFx0YW9NYXBJbnRlbnNpdHk6IHsgdmFsdWU6IDEgfVxuXG5cdFx0fSxcblxuXHRcdGxpZ2h0bWFwOiB7XG5cblx0XHRcdGxpZ2h0TWFwOiB7IHZhbHVlOiBudWxsIH0sXG5cdFx0XHRsaWdodE1hcEludGVuc2l0eTogeyB2YWx1ZTogMSB9XG5cblx0XHR9LFxuXG5cdFx0ZW1pc3NpdmVtYXA6IHtcblxuXHRcdFx0ZW1pc3NpdmVNYXA6IHsgdmFsdWU6IG51bGwgfVxuXG5cdFx0fSxcblxuXHRcdGJ1bXBtYXA6IHtcblxuXHRcdFx0YnVtcE1hcDogeyB2YWx1ZTogbnVsbCB9LFxuXHRcdFx0YnVtcFNjYWxlOiB7IHZhbHVlOiAxIH1cblxuXHRcdH0sXG5cblx0XHRub3JtYWxtYXA6IHtcblxuXHRcdFx0bm9ybWFsTWFwOiB7IHZhbHVlOiBudWxsIH0sXG5cdFx0XHRub3JtYWxTY2FsZTogeyB2YWx1ZTogbmV3IFZlY3RvcjIoIDEsIDEgKSB9XG5cblx0XHR9LFxuXG5cdFx0ZGlzcGxhY2VtZW50bWFwOiB7XG5cblx0XHRcdGRpc3BsYWNlbWVudE1hcDogeyB2YWx1ZTogbnVsbCB9LFxuXHRcdFx0ZGlzcGxhY2VtZW50U2NhbGU6IHsgdmFsdWU6IDEgfSxcblx0XHRcdGRpc3BsYWNlbWVudEJpYXM6IHsgdmFsdWU6IDAgfVxuXG5cdFx0fSxcblxuXHRcdHJvdWdobmVzc21hcDoge1xuXG5cdFx0XHRyb3VnaG5lc3NNYXA6IHsgdmFsdWU6IG51bGwgfVxuXG5cdFx0fSxcblxuXHRcdG1ldGFsbmVzc21hcDoge1xuXG5cdFx0XHRtZXRhbG5lc3NNYXA6IHsgdmFsdWU6IG51bGwgfVxuXG5cdFx0fSxcblxuXHRcdGdyYWRpZW50bWFwOiB7XG5cblx0XHRcdGdyYWRpZW50TWFwOiB7IHZhbHVlOiBudWxsIH1cblxuXHRcdH0sXG5cblx0XHRmb2c6IHtcblxuXHRcdFx0Zm9nRGVuc2l0eTogeyB2YWx1ZTogMC4wMDAyNSB9LFxuXHRcdFx0Zm9nTmVhcjogeyB2YWx1ZTogMSB9LFxuXHRcdFx0Zm9nRmFyOiB7IHZhbHVlOiAyMDAwIH0sXG5cdFx0XHRmb2dDb2xvcjogeyB2YWx1ZTogbmV3IENvbG9yKCAweGZmZmZmZiApIH1cblxuXHRcdH0sXG5cblx0XHRsaWdodHM6IHtcblxuXHRcdFx0YW1iaWVudExpZ2h0Q29sb3I6IHsgdmFsdWU6IFtdIH0sXG5cblx0XHRcdGRpcmVjdGlvbmFsTGlnaHRzOiB7IHZhbHVlOiBbXSwgcHJvcGVydGllczoge1xuXHRcdFx0XHRkaXJlY3Rpb246IHt9LFxuXHRcdFx0XHRjb2xvcjoge30sXG5cblx0XHRcdFx0c2hhZG93OiB7fSxcblx0XHRcdFx0c2hhZG93Qmlhczoge30sXG5cdFx0XHRcdHNoYWRvd1JhZGl1czoge30sXG5cdFx0XHRcdHNoYWRvd01hcFNpemU6IHt9XG5cdFx0XHR9IH0sXG5cblx0XHRcdGRpcmVjdGlvbmFsU2hhZG93TWFwOiB7IHZhbHVlOiBbXSB9LFxuXHRcdFx0ZGlyZWN0aW9uYWxTaGFkb3dNYXRyaXg6IHsgdmFsdWU6IFtdIH0sXG5cblx0XHRcdHNwb3RMaWdodHM6IHsgdmFsdWU6IFtdLCBwcm9wZXJ0aWVzOiB7XG5cdFx0XHRcdGNvbG9yOiB7fSxcblx0XHRcdFx0cG9zaXRpb246IHt9LFxuXHRcdFx0XHRkaXJlY3Rpb246IHt9LFxuXHRcdFx0XHRkaXN0YW5jZToge30sXG5cdFx0XHRcdGNvbmVDb3M6IHt9LFxuXHRcdFx0XHRwZW51bWJyYUNvczoge30sXG5cdFx0XHRcdGRlY2F5OiB7fSxcblxuXHRcdFx0XHRzaGFkb3c6IHt9LFxuXHRcdFx0XHRzaGFkb3dCaWFzOiB7fSxcblx0XHRcdFx0c2hhZG93UmFkaXVzOiB7fSxcblx0XHRcdFx0c2hhZG93TWFwU2l6ZToge31cblx0XHRcdH0gfSxcblxuXHRcdFx0c3BvdFNoYWRvd01hcDogeyB2YWx1ZTogW10gfSxcblx0XHRcdHNwb3RTaGFkb3dNYXRyaXg6IHsgdmFsdWU6IFtdIH0sXG5cblx0XHRcdHBvaW50TGlnaHRzOiB7IHZhbHVlOiBbXSwgcHJvcGVydGllczoge1xuXHRcdFx0XHRjb2xvcjoge30sXG5cdFx0XHRcdHBvc2l0aW9uOiB7fSxcblx0XHRcdFx0ZGVjYXk6IHt9LFxuXHRcdFx0XHRkaXN0YW5jZToge30sXG5cblx0XHRcdFx0c2hhZG93OiB7fSxcblx0XHRcdFx0c2hhZG93Qmlhczoge30sXG5cdFx0XHRcdHNoYWRvd1JhZGl1czoge30sXG5cdFx0XHRcdHNoYWRvd01hcFNpemU6IHt9XG5cdFx0XHR9IH0sXG5cblx0XHRcdHBvaW50U2hhZG93TWFwOiB7IHZhbHVlOiBbXSB9LFxuXHRcdFx0cG9pbnRTaGFkb3dNYXRyaXg6IHsgdmFsdWU6IFtdIH0sXG5cblx0XHRcdGhlbWlzcGhlcmVMaWdodHM6IHsgdmFsdWU6IFtdLCBwcm9wZXJ0aWVzOiB7XG5cdFx0XHRcdGRpcmVjdGlvbjoge30sXG5cdFx0XHRcdHNreUNvbG9yOiB7fSxcblx0XHRcdFx0Z3JvdW5kQ29sb3I6IHt9XG5cdFx0XHR9IH0sXG5cblx0ICAgICAgICAvLyBUT0RPIChhYmVsbmF0aW9uKTogUmVjdEFyZWFMaWdodCBCUkRGIGRhdGEgbmVlZHMgdG8gYmUgbW92ZWQgZnJvbSBleGFtcGxlIHRvIG1haW4gc3JjXG5cdCAgICAgICAgcmVjdEFyZWFMaWdodHM6IHsgdmFsdWU6IFtdLCBwcm9wZXJ0aWVzOiB7XG5cdCAgICAgICAgICAgIGNvbG9yOiB7fSxcblx0ICAgICAgICAgICAgcG9zaXRpb246IHt9LFxuXHQgICAgICAgICAgICB3aWR0aDoge30sXG5cdCAgICAgICAgICAgIGhlaWdodDoge30sXG5cdCAgICAgICAgfSB9XG5cblx0XHR9LFxuXG5cdFx0cG9pbnRzOiB7XG5cblx0XHRcdGRpZmZ1c2U6IHsgdmFsdWU6IG5ldyBDb2xvciggMHhlZWVlZWUgKSB9LFxuXHRcdFx0b3BhY2l0eTogeyB2YWx1ZTogMS4wIH0sXG5cdFx0XHRzaXplOiB7IHZhbHVlOiAxLjAgfSxcblx0XHRcdHNjYWxlOiB7IHZhbHVlOiAxLjAgfSxcblx0XHRcdG1hcDogeyB2YWx1ZTogbnVsbCB9LFxuXHRcdFx0b2Zmc2V0UmVwZWF0OiB7IHZhbHVlOiBuZXcgVmVjdG9yNCggMCwgMCwgMSwgMSApIH1cblxuXHRcdH1cblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yIG1pa2FlbCBlbXRpbmdlciAvIGh0dHA6Ly9nb21vLnNlL1xuXHQgKi9cblxuXHR2YXIgU2hhZGVyTGliID0ge1xuXG5cdFx0YmFzaWM6IHtcblxuXHRcdFx0dW5pZm9ybXM6IFVuaWZvcm1zVXRpbHMubWVyZ2UoIFtcblx0XHRcdFx0VW5pZm9ybXNMaWIuY29tbW9uLFxuXHRcdFx0XHRVbmlmb3Jtc0xpYi5hb21hcCxcblx0XHRcdFx0VW5pZm9ybXNMaWIubGlnaHRtYXAsXG5cdFx0XHRcdFVuaWZvcm1zTGliLmZvZ1xuXHRcdFx0XSApLFxuXG5cdFx0XHR2ZXJ0ZXhTaGFkZXI6IFNoYWRlckNodW5rLm1lc2hiYXNpY192ZXJ0LFxuXHRcdFx0ZnJhZ21lbnRTaGFkZXI6IFNoYWRlckNodW5rLm1lc2hiYXNpY19mcmFnXG5cblx0XHR9LFxuXG5cdFx0bGFtYmVydDoge1xuXG5cdFx0XHR1bmlmb3JtczogVW5pZm9ybXNVdGlscy5tZXJnZSggW1xuXHRcdFx0XHRVbmlmb3Jtc0xpYi5jb21tb24sXG5cdFx0XHRcdFVuaWZvcm1zTGliLmFvbWFwLFxuXHRcdFx0XHRVbmlmb3Jtc0xpYi5saWdodG1hcCxcblx0XHRcdFx0VW5pZm9ybXNMaWIuZW1pc3NpdmVtYXAsXG5cdFx0XHRcdFVuaWZvcm1zTGliLmZvZyxcblx0XHRcdFx0VW5pZm9ybXNMaWIubGlnaHRzLFxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0ZW1pc3NpdmU6IHsgdmFsdWU6IG5ldyBDb2xvciggMHgwMDAwMDAgKSB9XG5cdFx0XHRcdH1cblx0XHRcdF0gKSxcblxuXHRcdFx0dmVydGV4U2hhZGVyOiBTaGFkZXJDaHVuay5tZXNobGFtYmVydF92ZXJ0LFxuXHRcdFx0ZnJhZ21lbnRTaGFkZXI6IFNoYWRlckNodW5rLm1lc2hsYW1iZXJ0X2ZyYWdcblxuXHRcdH0sXG5cblx0XHRwaG9uZzoge1xuXG5cdFx0XHR1bmlmb3JtczogVW5pZm9ybXNVdGlscy5tZXJnZSggW1xuXHRcdFx0XHRVbmlmb3Jtc0xpYi5jb21tb24sXG5cdFx0XHRcdFVuaWZvcm1zTGliLmFvbWFwLFxuXHRcdFx0XHRVbmlmb3Jtc0xpYi5saWdodG1hcCxcblx0XHRcdFx0VW5pZm9ybXNMaWIuZW1pc3NpdmVtYXAsXG5cdFx0XHRcdFVuaWZvcm1zTGliLmJ1bXBtYXAsXG5cdFx0XHRcdFVuaWZvcm1zTGliLm5vcm1hbG1hcCxcblx0XHRcdFx0VW5pZm9ybXNMaWIuZGlzcGxhY2VtZW50bWFwLFxuXHRcdFx0XHRVbmlmb3Jtc0xpYi5ncmFkaWVudG1hcCxcblx0XHRcdFx0VW5pZm9ybXNMaWIuZm9nLFxuXHRcdFx0XHRVbmlmb3Jtc0xpYi5saWdodHMsXG5cdFx0XHRcdHtcblx0XHRcdFx0XHRlbWlzc2l2ZTogeyB2YWx1ZTogbmV3IENvbG9yKCAweDAwMDAwMCApIH0sXG5cdFx0XHRcdFx0c3BlY3VsYXI6IHsgdmFsdWU6IG5ldyBDb2xvciggMHgxMTExMTEgKSB9LFxuXHRcdFx0XHRcdHNoaW5pbmVzczogeyB2YWx1ZTogMzAgfVxuXHRcdFx0XHR9XG5cdFx0XHRdICksXG5cblx0XHRcdHZlcnRleFNoYWRlcjogU2hhZGVyQ2h1bmsubWVzaHBob25nX3ZlcnQsXG5cdFx0XHRmcmFnbWVudFNoYWRlcjogU2hhZGVyQ2h1bmsubWVzaHBob25nX2ZyYWdcblxuXHRcdH0sXG5cblx0XHRzdGFuZGFyZDoge1xuXG5cdFx0XHR1bmlmb3JtczogVW5pZm9ybXNVdGlscy5tZXJnZSggW1xuXHRcdFx0XHRVbmlmb3Jtc0xpYi5jb21tb24sXG5cdFx0XHRcdFVuaWZvcm1zTGliLmFvbWFwLFxuXHRcdFx0XHRVbmlmb3Jtc0xpYi5saWdodG1hcCxcblx0XHRcdFx0VW5pZm9ybXNMaWIuZW1pc3NpdmVtYXAsXG5cdFx0XHRcdFVuaWZvcm1zTGliLmJ1bXBtYXAsXG5cdFx0XHRcdFVuaWZvcm1zTGliLm5vcm1hbG1hcCxcblx0XHRcdFx0VW5pZm9ybXNMaWIuZGlzcGxhY2VtZW50bWFwLFxuXHRcdFx0XHRVbmlmb3Jtc0xpYi5yb3VnaG5lc3NtYXAsXG5cdFx0XHRcdFVuaWZvcm1zTGliLm1ldGFsbmVzc21hcCxcblx0XHRcdFx0VW5pZm9ybXNMaWIuZm9nLFxuXHRcdFx0XHRVbmlmb3Jtc0xpYi5saWdodHMsXG5cdFx0XHRcdHtcblx0XHRcdFx0XHRlbWlzc2l2ZTogeyB2YWx1ZTogbmV3IENvbG9yKCAweDAwMDAwMCApIH0sXG5cdFx0XHRcdFx0cm91Z2huZXNzOiB7IHZhbHVlOiAwLjUgfSxcblx0XHRcdFx0XHRtZXRhbG5lc3M6IHsgdmFsdWU6IDAgfSxcblx0XHRcdFx0XHRlbnZNYXBJbnRlbnNpdHk6IHsgdmFsdWU6IDEgfSAvLyB0ZW1wb3Jhcnlcblx0XHRcdFx0fVxuXHRcdFx0XSApLFxuXG5cdFx0XHR2ZXJ0ZXhTaGFkZXI6IFNoYWRlckNodW5rLm1lc2hwaHlzaWNhbF92ZXJ0LFxuXHRcdFx0ZnJhZ21lbnRTaGFkZXI6IFNoYWRlckNodW5rLm1lc2hwaHlzaWNhbF9mcmFnXG5cblx0XHR9LFxuXG5cdFx0cG9pbnRzOiB7XG5cblx0XHRcdHVuaWZvcm1zOiBVbmlmb3Jtc1V0aWxzLm1lcmdlKCBbXG5cdFx0XHRcdFVuaWZvcm1zTGliLnBvaW50cyxcblx0XHRcdFx0VW5pZm9ybXNMaWIuZm9nXG5cdFx0XHRdICksXG5cblx0XHRcdHZlcnRleFNoYWRlcjogU2hhZGVyQ2h1bmsucG9pbnRzX3ZlcnQsXG5cdFx0XHRmcmFnbWVudFNoYWRlcjogU2hhZGVyQ2h1bmsucG9pbnRzX2ZyYWdcblxuXHRcdH0sXG5cblx0XHRkYXNoZWQ6IHtcblxuXHRcdFx0dW5pZm9ybXM6IFVuaWZvcm1zVXRpbHMubWVyZ2UoIFtcblx0XHRcdFx0VW5pZm9ybXNMaWIuY29tbW9uLFxuXHRcdFx0XHRVbmlmb3Jtc0xpYi5mb2csXG5cdFx0XHRcdHtcblx0XHRcdFx0XHRzY2FsZTogeyB2YWx1ZTogMSB9LFxuXHRcdFx0XHRcdGRhc2hTaXplOiB7IHZhbHVlOiAxIH0sXG5cdFx0XHRcdFx0dG90YWxTaXplOiB7IHZhbHVlOiAyIH1cblx0XHRcdFx0fVxuXHRcdFx0XSApLFxuXG5cdFx0XHR2ZXJ0ZXhTaGFkZXI6IFNoYWRlckNodW5rLmxpbmVkYXNoZWRfdmVydCxcblx0XHRcdGZyYWdtZW50U2hhZGVyOiBTaGFkZXJDaHVuay5saW5lZGFzaGVkX2ZyYWdcblxuXHRcdH0sXG5cblx0XHRkZXB0aDoge1xuXG5cdFx0XHR1bmlmb3JtczogVW5pZm9ybXNVdGlscy5tZXJnZSggW1xuXHRcdFx0XHRVbmlmb3Jtc0xpYi5jb21tb24sXG5cdFx0XHRcdFVuaWZvcm1zTGliLmRpc3BsYWNlbWVudG1hcFxuXHRcdFx0XSApLFxuXG5cdFx0XHR2ZXJ0ZXhTaGFkZXI6IFNoYWRlckNodW5rLmRlcHRoX3ZlcnQsXG5cdFx0XHRmcmFnbWVudFNoYWRlcjogU2hhZGVyQ2h1bmsuZGVwdGhfZnJhZ1xuXG5cdFx0fSxcblxuXHRcdG5vcm1hbDoge1xuXG5cdFx0XHR1bmlmb3JtczogVW5pZm9ybXNVdGlscy5tZXJnZSggW1xuXHRcdFx0XHRVbmlmb3Jtc0xpYi5jb21tb24sXG5cdFx0XHRcdFVuaWZvcm1zTGliLmJ1bXBtYXAsXG5cdFx0XHRcdFVuaWZvcm1zTGliLm5vcm1hbG1hcCxcblx0XHRcdFx0VW5pZm9ybXNMaWIuZGlzcGxhY2VtZW50bWFwLFxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0b3BhY2l0eTogeyB2YWx1ZTogMS4wIH1cblx0XHRcdFx0fVxuXHRcdFx0XSApLFxuXG5cdFx0XHR2ZXJ0ZXhTaGFkZXI6IFNoYWRlckNodW5rLm5vcm1hbF92ZXJ0LFxuXHRcdFx0ZnJhZ21lbnRTaGFkZXI6IFNoYWRlckNodW5rLm5vcm1hbF9mcmFnXG5cblx0XHR9LFxuXG5cdFx0LyogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXHRcdC8vXHRDdWJlIG1hcCBzaGFkZXJcblx0XHQgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAqL1xuXG5cdFx0Y3ViZToge1xuXG5cdFx0XHR1bmlmb3Jtczoge1xuXHRcdFx0XHR0Q3ViZTogeyB2YWx1ZTogbnVsbCB9LFxuXHRcdFx0XHR0RmxpcDogeyB2YWx1ZTogLSAxIH0sXG5cdFx0XHRcdG9wYWNpdHk6IHsgdmFsdWU6IDEuMCB9XG5cdFx0XHR9LFxuXG5cdFx0XHR2ZXJ0ZXhTaGFkZXI6IFNoYWRlckNodW5rLmN1YmVfdmVydCxcblx0XHRcdGZyYWdtZW50U2hhZGVyOiBTaGFkZXJDaHVuay5jdWJlX2ZyYWdcblxuXHRcdH0sXG5cblx0XHQvKiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cdFx0Ly9cdEN1YmUgbWFwIHNoYWRlclxuXHRcdCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICovXG5cblx0XHRlcXVpcmVjdDoge1xuXG5cdFx0XHR1bmlmb3Jtczoge1xuXHRcdFx0XHR0RXF1aXJlY3Q6IHsgdmFsdWU6IG51bGwgfSxcblx0XHRcdFx0dEZsaXA6IHsgdmFsdWU6IC0gMSB9XG5cdFx0XHR9LFxuXG5cdFx0XHR2ZXJ0ZXhTaGFkZXI6IFNoYWRlckNodW5rLmVxdWlyZWN0X3ZlcnQsXG5cdFx0XHRmcmFnbWVudFNoYWRlcjogU2hhZGVyQ2h1bmsuZXF1aXJlY3RfZnJhZ1xuXG5cdFx0fSxcblxuXHRcdGRpc3RhbmNlUkdCQToge1xuXG5cdFx0XHR1bmlmb3Jtczoge1xuXHRcdFx0XHRsaWdodFBvczogeyB2YWx1ZTogbmV3IFZlY3RvcjMoKSB9XG5cdFx0XHR9LFxuXG5cdFx0XHR2ZXJ0ZXhTaGFkZXI6IFNoYWRlckNodW5rLmRpc3RhbmNlUkdCQV92ZXJ0LFxuXHRcdFx0ZnJhZ21lbnRTaGFkZXI6IFNoYWRlckNodW5rLmRpc3RhbmNlUkdCQV9mcmFnXG5cblx0XHR9XG5cblx0fTtcblxuXHRTaGFkZXJMaWIucGh5c2ljYWwgPSB7XG5cblx0XHR1bmlmb3JtczogVW5pZm9ybXNVdGlscy5tZXJnZSggW1xuXHRcdFx0U2hhZGVyTGliLnN0YW5kYXJkLnVuaWZvcm1zLFxuXHRcdFx0e1xuXHRcdFx0XHRjbGVhckNvYXQ6IHsgdmFsdWU6IDAgfSxcblx0XHRcdFx0Y2xlYXJDb2F0Um91Z2huZXNzOiB7IHZhbHVlOiAwIH1cblx0XHRcdH1cblx0XHRdICksXG5cblx0XHR2ZXJ0ZXhTaGFkZXI6IFNoYWRlckNodW5rLm1lc2hwaHlzaWNhbF92ZXJ0LFxuXHRcdGZyYWdtZW50U2hhZGVyOiBTaGFkZXJDaHVuay5tZXNocGh5c2ljYWxfZnJhZ1xuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgYmhvdXN0b24gLyBodHRwOi8vY2xhcmEuaW9cblx0ICovXG5cblx0ZnVuY3Rpb24gQm94MiggbWluLCBtYXggKSB7XG5cblx0XHR0aGlzLm1pbiA9ICggbWluICE9PSB1bmRlZmluZWQgKSA/IG1pbiA6IG5ldyBWZWN0b3IyKCArIEluZmluaXR5LCArIEluZmluaXR5ICk7XG5cdFx0dGhpcy5tYXggPSAoIG1heCAhPT0gdW5kZWZpbmVkICkgPyBtYXggOiBuZXcgVmVjdG9yMiggLSBJbmZpbml0eSwgLSBJbmZpbml0eSApO1xuXG5cdH1cblxuXHRCb3gyLnByb3RvdHlwZSA9IHtcblxuXHRcdGNvbnN0cnVjdG9yOiBCb3gyLFxuXG5cdFx0c2V0OiBmdW5jdGlvbiAoIG1pbiwgbWF4ICkge1xuXG5cdFx0XHR0aGlzLm1pbi5jb3B5KCBtaW4gKTtcblx0XHRcdHRoaXMubWF4LmNvcHkoIG1heCApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRGcm9tUG9pbnRzOiBmdW5jdGlvbiAoIHBvaW50cyApIHtcblxuXHRcdFx0dGhpcy5tYWtlRW1wdHkoKTtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBpbCA9IHBvaW50cy5sZW5ndGg7IGkgPCBpbDsgaSArKyApIHtcblxuXHRcdFx0XHR0aGlzLmV4cGFuZEJ5UG9pbnQoIHBvaW50c1sgaSBdICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0RnJvbUNlbnRlckFuZFNpemU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIHYxID0gbmV3IFZlY3RvcjIoKTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIHNldEZyb21DZW50ZXJBbmRTaXplKCBjZW50ZXIsIHNpemUgKSB7XG5cblx0XHRcdFx0dmFyIGhhbGZTaXplID0gdjEuY29weSggc2l6ZSApLm11bHRpcGx5U2NhbGFyKCAwLjUgKTtcblx0XHRcdFx0dGhpcy5taW4uY29weSggY2VudGVyICkuc3ViKCBoYWxmU2l6ZSApO1xuXHRcdFx0XHR0aGlzLm1heC5jb3B5KCBjZW50ZXIgKS5hZGQoIGhhbGZTaXplICk7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRjbG9uZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IHRoaXMuY29uc3RydWN0b3IoKS5jb3B5KCB0aGlzICk7XG5cblx0XHR9LFxuXG5cdFx0Y29weTogZnVuY3Rpb24gKCBib3ggKSB7XG5cblx0XHRcdHRoaXMubWluLmNvcHkoIGJveC5taW4gKTtcblx0XHRcdHRoaXMubWF4LmNvcHkoIGJveC5tYXggKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0bWFrZUVtcHR5OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMubWluLnggPSB0aGlzLm1pbi55ID0gKyBJbmZpbml0eTtcblx0XHRcdHRoaXMubWF4LnggPSB0aGlzLm1heC55ID0gLSBJbmZpbml0eTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0aXNFbXB0eTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHQvLyB0aGlzIGlzIGEgbW9yZSByb2J1c3QgY2hlY2sgZm9yIGVtcHR5IHRoYW4gKCB2b2x1bWUgPD0gMCApIGJlY2F1c2Ugdm9sdW1lIGNhbiBnZXQgcG9zaXRpdmUgd2l0aCB0d28gbmVnYXRpdmUgYXhlc1xuXG5cdFx0XHRyZXR1cm4gKCB0aGlzLm1heC54IDwgdGhpcy5taW4ueCApIHx8ICggdGhpcy5tYXgueSA8IHRoaXMubWluLnkgKTtcblxuXHRcdH0sXG5cblx0XHRnZXRDZW50ZXI6IGZ1bmN0aW9uICggb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdHZhciByZXN1bHQgPSBvcHRpb25hbFRhcmdldCB8fCBuZXcgVmVjdG9yMigpO1xuXHRcdFx0cmV0dXJuIHRoaXMuaXNFbXB0eSgpID8gcmVzdWx0LnNldCggMCwgMCApIDogcmVzdWx0LmFkZFZlY3RvcnMoIHRoaXMubWluLCB0aGlzLm1heCApLm11bHRpcGx5U2NhbGFyKCAwLjUgKTtcblxuXHRcdH0sXG5cblx0XHRnZXRTaXplOiBmdW5jdGlvbiAoIG9wdGlvbmFsVGFyZ2V0ICkge1xuXG5cdFx0XHR2YXIgcmVzdWx0ID0gb3B0aW9uYWxUYXJnZXQgfHwgbmV3IFZlY3RvcjIoKTtcblx0XHRcdHJldHVybiB0aGlzLmlzRW1wdHkoKSA/IHJlc3VsdC5zZXQoIDAsIDAgKSA6IHJlc3VsdC5zdWJWZWN0b3JzKCB0aGlzLm1heCwgdGhpcy5taW4gKTtcblxuXHRcdH0sXG5cblx0XHRleHBhbmRCeVBvaW50OiBmdW5jdGlvbiAoIHBvaW50ICkge1xuXG5cdFx0XHR0aGlzLm1pbi5taW4oIHBvaW50ICk7XG5cdFx0XHR0aGlzLm1heC5tYXgoIHBvaW50ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGV4cGFuZEJ5VmVjdG9yOiBmdW5jdGlvbiAoIHZlY3RvciApIHtcblxuXHRcdFx0dGhpcy5taW4uc3ViKCB2ZWN0b3IgKTtcblx0XHRcdHRoaXMubWF4LmFkZCggdmVjdG9yICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGV4cGFuZEJ5U2NhbGFyOiBmdW5jdGlvbiAoIHNjYWxhciApIHtcblxuXHRcdFx0dGhpcy5taW4uYWRkU2NhbGFyKCAtIHNjYWxhciApO1xuXHRcdFx0dGhpcy5tYXguYWRkU2NhbGFyKCBzY2FsYXIgKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Y29udGFpbnNQb2ludDogZnVuY3Rpb24gKCBwb2ludCApIHtcblxuXHRcdFx0cmV0dXJuIHBvaW50LnggPCB0aGlzLm1pbi54IHx8IHBvaW50LnggPiB0aGlzLm1heC54IHx8XG5cdFx0XHRcdHBvaW50LnkgPCB0aGlzLm1pbi55IHx8IHBvaW50LnkgPiB0aGlzLm1heC55ID8gZmFsc2UgOiB0cnVlO1xuXG5cdFx0fSxcblxuXHRcdGNvbnRhaW5zQm94OiBmdW5jdGlvbiAoIGJveCApIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMubWluLnggPD0gYm94Lm1pbi54ICYmIGJveC5tYXgueCA8PSB0aGlzLm1heC54ICYmXG5cdFx0XHRcdHRoaXMubWluLnkgPD0gYm94Lm1pbi55ICYmIGJveC5tYXgueSA8PSB0aGlzLm1heC55O1xuXG5cdFx0fSxcblxuXHRcdGdldFBhcmFtZXRlcjogZnVuY3Rpb24gKCBwb2ludCwgb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdC8vIFRoaXMgY2FuIHBvdGVudGlhbGx5IGhhdmUgYSBkaXZpZGUgYnkgemVybyBpZiB0aGUgYm94XG5cdFx0XHQvLyBoYXMgYSBzaXplIGRpbWVuc2lvbiBvZiAwLlxuXG5cdFx0XHR2YXIgcmVzdWx0ID0gb3B0aW9uYWxUYXJnZXQgfHwgbmV3IFZlY3RvcjIoKTtcblxuXHRcdFx0cmV0dXJuIHJlc3VsdC5zZXQoXG5cdFx0XHRcdCggcG9pbnQueCAtIHRoaXMubWluLnggKSAvICggdGhpcy5tYXgueCAtIHRoaXMubWluLnggKSxcblx0XHRcdFx0KCBwb2ludC55IC0gdGhpcy5taW4ueSApIC8gKCB0aGlzLm1heC55IC0gdGhpcy5taW4ueSApXG5cdFx0XHQpO1xuXG5cdFx0fSxcblxuXHRcdGludGVyc2VjdHNCb3g6IGZ1bmN0aW9uICggYm94ICkge1xuXG5cdFx0XHQvLyB1c2luZyA2IHNwbGl0dGluZyBwbGFuZXMgdG8gcnVsZSBvdXQgaW50ZXJzZWN0aW9ucy5cblx0XHRcdHJldHVybiBib3gubWF4LnggPCB0aGlzLm1pbi54IHx8IGJveC5taW4ueCA+IHRoaXMubWF4LnggfHxcblx0XHRcdFx0Ym94Lm1heC55IDwgdGhpcy5taW4ueSB8fCBib3gubWluLnkgPiB0aGlzLm1heC55ID8gZmFsc2UgOiB0cnVlO1xuXG5cdFx0fSxcblxuXHRcdGNsYW1wUG9pbnQ6IGZ1bmN0aW9uICggcG9pbnQsIG9wdGlvbmFsVGFyZ2V0ICkge1xuXG5cdFx0XHR2YXIgcmVzdWx0ID0gb3B0aW9uYWxUYXJnZXQgfHwgbmV3IFZlY3RvcjIoKTtcblx0XHRcdHJldHVybiByZXN1bHQuY29weSggcG9pbnQgKS5jbGFtcCggdGhpcy5taW4sIHRoaXMubWF4ICk7XG5cblx0XHR9LFxuXG5cdFx0ZGlzdGFuY2VUb1BvaW50OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB2MSA9IG5ldyBWZWN0b3IyKCk7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBkaXN0YW5jZVRvUG9pbnQoIHBvaW50ICkge1xuXG5cdFx0XHRcdHZhciBjbGFtcGVkUG9pbnQgPSB2MS5jb3B5KCBwb2ludCApLmNsYW1wKCB0aGlzLm1pbiwgdGhpcy5tYXggKTtcblx0XHRcdFx0cmV0dXJuIGNsYW1wZWRQb2ludC5zdWIoIHBvaW50ICkubGVuZ3RoKCk7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRpbnRlcnNlY3Q6IGZ1bmN0aW9uICggYm94ICkge1xuXG5cdFx0XHR0aGlzLm1pbi5tYXgoIGJveC5taW4gKTtcblx0XHRcdHRoaXMubWF4Lm1pbiggYm94Lm1heCApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHR1bmlvbjogZnVuY3Rpb24gKCBib3ggKSB7XG5cblx0XHRcdHRoaXMubWluLm1pbiggYm94Lm1pbiApO1xuXHRcdFx0dGhpcy5tYXgubWF4KCBib3gubWF4ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHRyYW5zbGF0ZTogZnVuY3Rpb24gKCBvZmZzZXQgKSB7XG5cblx0XHRcdHRoaXMubWluLmFkZCggb2Zmc2V0ICk7XG5cdFx0XHR0aGlzLm1heC5hZGQoIG9mZnNldCApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRlcXVhbHM6IGZ1bmN0aW9uICggYm94ICkge1xuXG5cdFx0XHRyZXR1cm4gYm94Lm1pbi5lcXVhbHMoIHRoaXMubWluICkgJiYgYm94Lm1heC5lcXVhbHMoIHRoaXMubWF4ICk7XG5cblx0XHR9XG5cblx0fTtcblxuXHQvKipcblx0ICogQGF1dGhvciBtaWthZWwgZW10aW5nZXIgLyBodHRwOi8vZ29tby5zZS9cblx0ICogQGF1dGhvciBhbHRlcmVkcSAvIGh0dHA6Ly9hbHRlcmVkcXVhbGlhLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gTGVuc0ZsYXJlUGx1Z2luKCByZW5kZXJlciwgZmxhcmVzICkge1xuXG5cdFx0dmFyIGdsID0gcmVuZGVyZXIuY29udGV4dDtcblx0XHR2YXIgc3RhdGUgPSByZW5kZXJlci5zdGF0ZTtcblxuXHRcdHZhciB2ZXJ0ZXhCdWZmZXIsIGVsZW1lbnRCdWZmZXI7XG5cdFx0dmFyIHNoYWRlciwgcHJvZ3JhbSwgYXR0cmlidXRlcywgdW5pZm9ybXM7XG5cblx0XHR2YXIgdGVtcFRleHR1cmUsIG9jY2x1c2lvblRleHR1cmU7XG5cblx0XHRmdW5jdGlvbiBpbml0KCkge1xuXG5cdFx0XHR2YXIgdmVydGljZXMgPSBuZXcgRmxvYXQzMkFycmF5KCBbXG5cdFx0XHRcdC0gMSwgLSAxLCAgMCwgMCxcblx0XHRcdFx0IDEsIC0gMSwgIDEsIDAsXG5cdFx0XHRcdCAxLCAgMSwgIDEsIDEsXG5cdFx0XHRcdC0gMSwgIDEsICAwLCAxXG5cdFx0XHRdICk7XG5cblx0XHRcdHZhciBmYWNlcyA9IG5ldyBVaW50MTZBcnJheSggW1xuXHRcdFx0XHQwLCAxLCAyLFxuXHRcdFx0XHQwLCAyLCAzXG5cdFx0XHRdICk7XG5cblx0XHRcdC8vIGJ1ZmZlcnNcblxuXHRcdFx0dmVydGV4QnVmZmVyICAgICA9IGdsLmNyZWF0ZUJ1ZmZlcigpO1xuXHRcdFx0ZWxlbWVudEJ1ZmZlciAgICA9IGdsLmNyZWF0ZUJ1ZmZlcigpO1xuXG5cdFx0XHRnbC5iaW5kQnVmZmVyKCBnbC5BUlJBWV9CVUZGRVIsIHZlcnRleEJ1ZmZlciApO1xuXHRcdFx0Z2wuYnVmZmVyRGF0YSggZ2wuQVJSQVlfQlVGRkVSLCB2ZXJ0aWNlcywgZ2wuU1RBVElDX0RSQVcgKTtcblxuXHRcdFx0Z2wuYmluZEJ1ZmZlciggZ2wuRUxFTUVOVF9BUlJBWV9CVUZGRVIsIGVsZW1lbnRCdWZmZXIgKTtcblx0XHRcdGdsLmJ1ZmZlckRhdGEoIGdsLkVMRU1FTlRfQVJSQVlfQlVGRkVSLCBmYWNlcywgZ2wuU1RBVElDX0RSQVcgKTtcblxuXHRcdFx0Ly8gdGV4dHVyZXNcblxuXHRcdFx0dGVtcFRleHR1cmUgICAgICA9IGdsLmNyZWF0ZVRleHR1cmUoKTtcblx0XHRcdG9jY2x1c2lvblRleHR1cmUgPSBnbC5jcmVhdGVUZXh0dXJlKCk7XG5cblx0XHRcdHN0YXRlLmJpbmRUZXh0dXJlKCBnbC5URVhUVVJFXzJELCB0ZW1wVGV4dHVyZSApO1xuXHRcdFx0Z2wudGV4SW1hZ2UyRCggZ2wuVEVYVFVSRV8yRCwgMCwgZ2wuUkdCLCAxNiwgMTYsIDAsIGdsLlJHQiwgZ2wuVU5TSUdORURfQllURSwgbnVsbCApO1xuXHRcdFx0Z2wudGV4UGFyYW1ldGVyaSggZ2wuVEVYVFVSRV8yRCwgZ2wuVEVYVFVSRV9XUkFQX1MsIGdsLkNMQU1QX1RPX0VER0UgKTtcblx0XHRcdGdsLnRleFBhcmFtZXRlcmkoIGdsLlRFWFRVUkVfMkQsIGdsLlRFWFRVUkVfV1JBUF9ULCBnbC5DTEFNUF9UT19FREdFICk7XG5cdFx0XHRnbC50ZXhQYXJhbWV0ZXJpKCBnbC5URVhUVVJFXzJELCBnbC5URVhUVVJFX01BR19GSUxURVIsIGdsLk5FQVJFU1QgKTtcblx0XHRcdGdsLnRleFBhcmFtZXRlcmkoIGdsLlRFWFRVUkVfMkQsIGdsLlRFWFRVUkVfTUlOX0ZJTFRFUiwgZ2wuTkVBUkVTVCApO1xuXG5cdFx0XHRzdGF0ZS5iaW5kVGV4dHVyZSggZ2wuVEVYVFVSRV8yRCwgb2NjbHVzaW9uVGV4dHVyZSApO1xuXHRcdFx0Z2wudGV4SW1hZ2UyRCggZ2wuVEVYVFVSRV8yRCwgMCwgZ2wuUkdCQSwgMTYsIDE2LCAwLCBnbC5SR0JBLCBnbC5VTlNJR05FRF9CWVRFLCBudWxsICk7XG5cdFx0XHRnbC50ZXhQYXJhbWV0ZXJpKCBnbC5URVhUVVJFXzJELCBnbC5URVhUVVJFX1dSQVBfUywgZ2wuQ0xBTVBfVE9fRURHRSApO1xuXHRcdFx0Z2wudGV4UGFyYW1ldGVyaSggZ2wuVEVYVFVSRV8yRCwgZ2wuVEVYVFVSRV9XUkFQX1QsIGdsLkNMQU1QX1RPX0VER0UgKTtcblx0XHRcdGdsLnRleFBhcmFtZXRlcmkoIGdsLlRFWFRVUkVfMkQsIGdsLlRFWFRVUkVfTUFHX0ZJTFRFUiwgZ2wuTkVBUkVTVCApO1xuXHRcdFx0Z2wudGV4UGFyYW1ldGVyaSggZ2wuVEVYVFVSRV8yRCwgZ2wuVEVYVFVSRV9NSU5fRklMVEVSLCBnbC5ORUFSRVNUICk7XG5cblx0XHRcdHNoYWRlciA9IHtcblxuXHRcdFx0XHR2ZXJ0ZXhTaGFkZXI6IFtcblxuXHRcdFx0XHRcdFwidW5pZm9ybSBsb3dwIGludCByZW5kZXJUeXBlO1wiLFxuXG5cdFx0XHRcdFx0XCJ1bmlmb3JtIHZlYzMgc2NyZWVuUG9zaXRpb247XCIsXG5cdFx0XHRcdFx0XCJ1bmlmb3JtIHZlYzIgc2NhbGU7XCIsXG5cdFx0XHRcdFx0XCJ1bmlmb3JtIGZsb2F0IHJvdGF0aW9uO1wiLFxuXG5cdFx0XHRcdFx0XCJ1bmlmb3JtIHNhbXBsZXIyRCBvY2NsdXNpb25NYXA7XCIsXG5cblx0XHRcdFx0XHRcImF0dHJpYnV0ZSB2ZWMyIHBvc2l0aW9uO1wiLFxuXHRcdFx0XHRcdFwiYXR0cmlidXRlIHZlYzIgdXY7XCIsXG5cblx0XHRcdFx0XHRcInZhcnlpbmcgdmVjMiB2VVY7XCIsXG5cdFx0XHRcdFx0XCJ2YXJ5aW5nIGZsb2F0IHZWaXNpYmlsaXR5O1wiLFxuXG5cdFx0XHRcdFx0XCJ2b2lkIG1haW4oKSB7XCIsXG5cblx0XHRcdFx0XHRcdFwidlVWID0gdXY7XCIsXG5cblx0XHRcdFx0XHRcdFwidmVjMiBwb3MgPSBwb3NpdGlvbjtcIixcblxuXHRcdFx0XHRcdFx0XCJpZiAoIHJlbmRlclR5cGUgPT0gMiApIHtcIixcblxuXHRcdFx0XHRcdFx0XHRcInZlYzQgdmlzaWJpbGl0eSA9IHRleHR1cmUyRCggb2NjbHVzaW9uTWFwLCB2ZWMyKCAwLjEsIDAuMSApICk7XCIsXG5cdFx0XHRcdFx0XHRcdFwidmlzaWJpbGl0eSArPSB0ZXh0dXJlMkQoIG9jY2x1c2lvbk1hcCwgdmVjMiggMC41LCAwLjEgKSApO1wiLFxuXHRcdFx0XHRcdFx0XHRcInZpc2liaWxpdHkgKz0gdGV4dHVyZTJEKCBvY2NsdXNpb25NYXAsIHZlYzIoIDAuOSwgMC4xICkgKTtcIixcblx0XHRcdFx0XHRcdFx0XCJ2aXNpYmlsaXR5ICs9IHRleHR1cmUyRCggb2NjbHVzaW9uTWFwLCB2ZWMyKCAwLjksIDAuNSApICk7XCIsXG5cdFx0XHRcdFx0XHRcdFwidmlzaWJpbGl0eSArPSB0ZXh0dXJlMkQoIG9jY2x1c2lvbk1hcCwgdmVjMiggMC45LCAwLjkgKSApO1wiLFxuXHRcdFx0XHRcdFx0XHRcInZpc2liaWxpdHkgKz0gdGV4dHVyZTJEKCBvY2NsdXNpb25NYXAsIHZlYzIoIDAuNSwgMC45ICkgKTtcIixcblx0XHRcdFx0XHRcdFx0XCJ2aXNpYmlsaXR5ICs9IHRleHR1cmUyRCggb2NjbHVzaW9uTWFwLCB2ZWMyKCAwLjEsIDAuOSApICk7XCIsXG5cdFx0XHRcdFx0XHRcdFwidmlzaWJpbGl0eSArPSB0ZXh0dXJlMkQoIG9jY2x1c2lvbk1hcCwgdmVjMiggMC4xLCAwLjUgKSApO1wiLFxuXHRcdFx0XHRcdFx0XHRcInZpc2liaWxpdHkgKz0gdGV4dHVyZTJEKCBvY2NsdXNpb25NYXAsIHZlYzIoIDAuNSwgMC41ICkgKTtcIixcblxuXHRcdFx0XHRcdFx0XHRcInZWaXNpYmlsaXR5ID0gICAgICAgIHZpc2liaWxpdHkuciAvIDkuMDtcIixcblx0XHRcdFx0XHRcdFx0XCJ2VmlzaWJpbGl0eSAqPSAxLjAgLSB2aXNpYmlsaXR5LmcgLyA5LjA7XCIsXG5cdFx0XHRcdFx0XHRcdFwidlZpc2liaWxpdHkgKj0gICAgICAgdmlzaWJpbGl0eS5iIC8gOS4wO1wiLFxuXHRcdFx0XHRcdFx0XHRcInZWaXNpYmlsaXR5ICo9IDEuMCAtIHZpc2liaWxpdHkuYSAvIDkuMDtcIixcblxuXHRcdFx0XHRcdFx0XHRcInBvcy54ID0gY29zKCByb3RhdGlvbiApICogcG9zaXRpb24ueCAtIHNpbiggcm90YXRpb24gKSAqIHBvc2l0aW9uLnk7XCIsXG5cdFx0XHRcdFx0XHRcdFwicG9zLnkgPSBzaW4oIHJvdGF0aW9uICkgKiBwb3NpdGlvbi54ICsgY29zKCByb3RhdGlvbiApICogcG9zaXRpb24ueTtcIixcblxuXHRcdFx0XHRcdFx0XCJ9XCIsXG5cblx0XHRcdFx0XHRcdFwiZ2xfUG9zaXRpb24gPSB2ZWM0KCAoIHBvcyAqIHNjYWxlICsgc2NyZWVuUG9zaXRpb24ueHkgKS54eSwgc2NyZWVuUG9zaXRpb24ueiwgMS4wICk7XCIsXG5cblx0XHRcdFx0XHRcIn1cIlxuXG5cdFx0XHRcdF0uam9pbiggXCJcXG5cIiApLFxuXG5cdFx0XHRcdGZyYWdtZW50U2hhZGVyOiBbXG5cblx0XHRcdFx0XHRcInVuaWZvcm0gbG93cCBpbnQgcmVuZGVyVHlwZTtcIixcblxuXHRcdFx0XHRcdFwidW5pZm9ybSBzYW1wbGVyMkQgbWFwO1wiLFxuXHRcdFx0XHRcdFwidW5pZm9ybSBmbG9hdCBvcGFjaXR5O1wiLFxuXHRcdFx0XHRcdFwidW5pZm9ybSB2ZWMzIGNvbG9yO1wiLFxuXG5cdFx0XHRcdFx0XCJ2YXJ5aW5nIHZlYzIgdlVWO1wiLFxuXHRcdFx0XHRcdFwidmFyeWluZyBmbG9hdCB2VmlzaWJpbGl0eTtcIixcblxuXHRcdFx0XHRcdFwidm9pZCBtYWluKCkge1wiLFxuXG5cdFx0XHRcdFx0XHQvLyBwaW5rIHNxdWFyZVxuXG5cdFx0XHRcdFx0XHRcImlmICggcmVuZGVyVHlwZSA9PSAwICkge1wiLFxuXG5cdFx0XHRcdFx0XHRcdFwiZ2xfRnJhZ0NvbG9yID0gdmVjNCggMS4wLCAwLjAsIDEuMCwgMC4wICk7XCIsXG5cblx0XHRcdFx0XHRcdC8vIHJlc3RvcmVcblxuXHRcdFx0XHRcdFx0XCJ9IGVsc2UgaWYgKCByZW5kZXJUeXBlID09IDEgKSB7XCIsXG5cblx0XHRcdFx0XHRcdFx0XCJnbF9GcmFnQ29sb3IgPSB0ZXh0dXJlMkQoIG1hcCwgdlVWICk7XCIsXG5cblx0XHRcdFx0XHRcdC8vIGZsYXJlXG5cblx0XHRcdFx0XHRcdFwifSBlbHNlIHtcIixcblxuXHRcdFx0XHRcdFx0XHRcInZlYzQgdGV4dHVyZSA9IHRleHR1cmUyRCggbWFwLCB2VVYgKTtcIixcblx0XHRcdFx0XHRcdFx0XCJ0ZXh0dXJlLmEgKj0gb3BhY2l0eSAqIHZWaXNpYmlsaXR5O1wiLFxuXHRcdFx0XHRcdFx0XHRcImdsX0ZyYWdDb2xvciA9IHRleHR1cmU7XCIsXG5cdFx0XHRcdFx0XHRcdFwiZ2xfRnJhZ0NvbG9yLnJnYiAqPSBjb2xvcjtcIixcblxuXHRcdFx0XHRcdFx0XCJ9XCIsXG5cblx0XHRcdFx0XHRcIn1cIlxuXG5cdFx0XHRcdF0uam9pbiggXCJcXG5cIiApXG5cblx0XHRcdH07XG5cblx0XHRcdHByb2dyYW0gPSBjcmVhdGVQcm9ncmFtKCBzaGFkZXIgKTtcblxuXHRcdFx0YXR0cmlidXRlcyA9IHtcblx0XHRcdFx0dmVydGV4OiBnbC5nZXRBdHRyaWJMb2NhdGlvbiAoIHByb2dyYW0sIFwicG9zaXRpb25cIiApLFxuXHRcdFx0XHR1djogICAgIGdsLmdldEF0dHJpYkxvY2F0aW9uICggcHJvZ3JhbSwgXCJ1dlwiIClcblx0XHRcdH07XG5cblx0XHRcdHVuaWZvcm1zID0ge1xuXHRcdFx0XHRyZW5kZXJUeXBlOiAgICAgZ2wuZ2V0VW5pZm9ybUxvY2F0aW9uKCBwcm9ncmFtLCBcInJlbmRlclR5cGVcIiApLFxuXHRcdFx0XHRtYXA6ICAgICAgICAgICAgZ2wuZ2V0VW5pZm9ybUxvY2F0aW9uKCBwcm9ncmFtLCBcIm1hcFwiICksXG5cdFx0XHRcdG9jY2x1c2lvbk1hcDogICBnbC5nZXRVbmlmb3JtTG9jYXRpb24oIHByb2dyYW0sIFwib2NjbHVzaW9uTWFwXCIgKSxcblx0XHRcdFx0b3BhY2l0eTogICAgICAgIGdsLmdldFVuaWZvcm1Mb2NhdGlvbiggcHJvZ3JhbSwgXCJvcGFjaXR5XCIgKSxcblx0XHRcdFx0Y29sb3I6ICAgICAgICAgIGdsLmdldFVuaWZvcm1Mb2NhdGlvbiggcHJvZ3JhbSwgXCJjb2xvclwiICksXG5cdFx0XHRcdHNjYWxlOiAgICAgICAgICBnbC5nZXRVbmlmb3JtTG9jYXRpb24oIHByb2dyYW0sIFwic2NhbGVcIiApLFxuXHRcdFx0XHRyb3RhdGlvbjogICAgICAgZ2wuZ2V0VW5pZm9ybUxvY2F0aW9uKCBwcm9ncmFtLCBcInJvdGF0aW9uXCIgKSxcblx0XHRcdFx0c2NyZWVuUG9zaXRpb246IGdsLmdldFVuaWZvcm1Mb2NhdGlvbiggcHJvZ3JhbSwgXCJzY3JlZW5Qb3NpdGlvblwiIClcblx0XHRcdH07XG5cblx0XHR9XG5cblx0XHQvKlxuXHRcdCAqIFJlbmRlciBsZW5zIGZsYXJlc1xuXHRcdCAqIE1ldGhvZDogcmVuZGVycyAxNngxNiAweGZmMDBmZi1jb2xvcmVkIHBvaW50cyBzY2F0dGVyZWQgb3ZlciB0aGUgbGlnaHQgc291cmNlIGFyZWEsXG5cdFx0ICogICAgICAgICByZWFkcyB0aGVzZSBiYWNrIGFuZCBjYWxjdWxhdGVzIG9jY2x1c2lvbi5cblx0XHQgKi9cblxuXHRcdHRoaXMucmVuZGVyID0gZnVuY3Rpb24gKCBzY2VuZSwgY2FtZXJhLCB2aWV3cG9ydCApIHtcblxuXHRcdFx0aWYgKCBmbGFyZXMubGVuZ3RoID09PSAwICkgcmV0dXJuO1xuXG5cdFx0XHR2YXIgdGVtcFBvc2l0aW9uID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0dmFyIGludkFzcGVjdCA9IHZpZXdwb3J0LncgLyB2aWV3cG9ydC56LFxuXHRcdFx0XHRoYWxmVmlld3BvcnRXaWR0aCA9IHZpZXdwb3J0LnogKiAwLjUsXG5cdFx0XHRcdGhhbGZWaWV3cG9ydEhlaWdodCA9IHZpZXdwb3J0LncgKiAwLjU7XG5cblx0XHRcdHZhciBzaXplID0gMTYgLyB2aWV3cG9ydC53LFxuXHRcdFx0XHRzY2FsZSA9IG5ldyBWZWN0b3IyKCBzaXplICogaW52QXNwZWN0LCBzaXplICk7XG5cblx0XHRcdHZhciBzY3JlZW5Qb3NpdGlvbiA9IG5ldyBWZWN0b3IzKCAxLCAxLCAwICksXG5cdFx0XHRcdHNjcmVlblBvc2l0aW9uUGl4ZWxzID0gbmV3IFZlY3RvcjIoIDEsIDEgKTtcblxuXHRcdFx0dmFyIHZhbGlkQXJlYSA9IG5ldyBCb3gyKCk7XG5cblx0XHRcdHZhbGlkQXJlYS5taW4uc2V0KCB2aWV3cG9ydC54LCB2aWV3cG9ydC55ICk7XG5cdFx0XHR2YWxpZEFyZWEubWF4LnNldCggdmlld3BvcnQueCArICggdmlld3BvcnQueiAtIDE2ICksIHZpZXdwb3J0LnkgKyAoIHZpZXdwb3J0LncgLSAxNiApICk7XG5cblx0XHRcdGlmICggcHJvZ3JhbSA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdGluaXQoKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRnbC51c2VQcm9ncmFtKCBwcm9ncmFtICk7XG5cblx0XHRcdHN0YXRlLmluaXRBdHRyaWJ1dGVzKCk7XG5cdFx0XHRzdGF0ZS5lbmFibGVBdHRyaWJ1dGUoIGF0dHJpYnV0ZXMudmVydGV4ICk7XG5cdFx0XHRzdGF0ZS5lbmFibGVBdHRyaWJ1dGUoIGF0dHJpYnV0ZXMudXYgKTtcblx0XHRcdHN0YXRlLmRpc2FibGVVbnVzZWRBdHRyaWJ1dGVzKCk7XG5cblx0XHRcdC8vIGxvb3AgdGhyb3VnaCBhbGwgbGVucyBmbGFyZXMgdG8gdXBkYXRlIHRoZWlyIG9jY2x1c2lvbiBhbmQgcG9zaXRpb25zXG5cdFx0XHQvLyBzZXR1cCBnbCBhbmQgY29tbW9uIHVzZWQgYXR0cmlicy91bmlmb3Jtc1xuXG5cdFx0XHRnbC51bmlmb3JtMWkoIHVuaWZvcm1zLm9jY2x1c2lvbk1hcCwgMCApO1xuXHRcdFx0Z2wudW5pZm9ybTFpKCB1bmlmb3Jtcy5tYXAsIDEgKTtcblxuXHRcdFx0Z2wuYmluZEJ1ZmZlciggZ2wuQVJSQVlfQlVGRkVSLCB2ZXJ0ZXhCdWZmZXIgKTtcblx0XHRcdGdsLnZlcnRleEF0dHJpYlBvaW50ZXIoIGF0dHJpYnV0ZXMudmVydGV4LCAyLCBnbC5GTE9BVCwgZmFsc2UsIDIgKiA4LCAwICk7XG5cdFx0XHRnbC52ZXJ0ZXhBdHRyaWJQb2ludGVyKCBhdHRyaWJ1dGVzLnV2LCAyLCBnbC5GTE9BVCwgZmFsc2UsIDIgKiA4LCA4ICk7XG5cblx0XHRcdGdsLmJpbmRCdWZmZXIoIGdsLkVMRU1FTlRfQVJSQVlfQlVGRkVSLCBlbGVtZW50QnVmZmVyICk7XG5cblx0XHRcdHN0YXRlLmRpc2FibGUoIGdsLkNVTExfRkFDRSApO1xuXHRcdFx0c3RhdGUuc2V0RGVwdGhXcml0ZSggZmFsc2UgKTtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBsID0gZmxhcmVzLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0c2l6ZSA9IDE2IC8gdmlld3BvcnQudztcblx0XHRcdFx0c2NhbGUuc2V0KCBzaXplICogaW52QXNwZWN0LCBzaXplICk7XG5cblx0XHRcdFx0Ly8gY2FsYyBvYmplY3Qgc2NyZWVuIHBvc2l0aW9uXG5cblx0XHRcdFx0dmFyIGZsYXJlID0gZmxhcmVzWyBpIF07XG5cblx0XHRcdFx0dGVtcFBvc2l0aW9uLnNldCggZmxhcmUubWF0cml4V29ybGQuZWxlbWVudHNbIDEyIF0sIGZsYXJlLm1hdHJpeFdvcmxkLmVsZW1lbnRzWyAxMyBdLCBmbGFyZS5tYXRyaXhXb3JsZC5lbGVtZW50c1sgMTQgXSApO1xuXG5cdFx0XHRcdHRlbXBQb3NpdGlvbi5hcHBseU1hdHJpeDQoIGNhbWVyYS5tYXRyaXhXb3JsZEludmVyc2UgKTtcblx0XHRcdFx0dGVtcFBvc2l0aW9uLmFwcGx5UHJvamVjdGlvbiggY2FtZXJhLnByb2plY3Rpb25NYXRyaXggKTtcblxuXHRcdFx0XHQvLyBzZXR1cCBhcnJheXMgZm9yIGdsIHByb2dyYW1zXG5cblx0XHRcdFx0c2NyZWVuUG9zaXRpb24uY29weSggdGVtcFBvc2l0aW9uICk7XG5cblx0XHRcdFx0Ly8gaG9yaXpvbnRhbCBhbmQgdmVydGljYWwgY29vcmRpbmF0ZSBvZiB0aGUgbG93ZXIgbGVmdCBjb3JuZXIgb2YgdGhlIHBpeGVscyB0byBjb3B5XG5cblx0XHRcdFx0c2NyZWVuUG9zaXRpb25QaXhlbHMueCA9IHZpZXdwb3J0LnggKyAoIHNjcmVlblBvc2l0aW9uLnggKiBoYWxmVmlld3BvcnRXaWR0aCApICsgaGFsZlZpZXdwb3J0V2lkdGggLSA4O1xuXHRcdFx0XHRzY3JlZW5Qb3NpdGlvblBpeGVscy55ID0gdmlld3BvcnQueSArICggc2NyZWVuUG9zaXRpb24ueSAqIGhhbGZWaWV3cG9ydEhlaWdodCApICsgaGFsZlZpZXdwb3J0SGVpZ2h0IC0gODtcblxuXHRcdFx0XHQvLyBzY3JlZW4gY3VsbFxuXG5cdFx0XHRcdGlmICggdmFsaWRBcmVhLmNvbnRhaW5zUG9pbnQoIHNjcmVlblBvc2l0aW9uUGl4ZWxzICkgPT09IHRydWUgKSB7XG5cblx0XHRcdFx0XHQvLyBzYXZlIGN1cnJlbnQgUkdCIHRvIHRlbXAgdGV4dHVyZVxuXG5cdFx0XHRcdFx0c3RhdGUuYWN0aXZlVGV4dHVyZSggZ2wuVEVYVFVSRTAgKTtcblx0XHRcdFx0XHRzdGF0ZS5iaW5kVGV4dHVyZSggZ2wuVEVYVFVSRV8yRCwgbnVsbCApO1xuXHRcdFx0XHRcdHN0YXRlLmFjdGl2ZVRleHR1cmUoIGdsLlRFWFRVUkUxICk7XG5cdFx0XHRcdFx0c3RhdGUuYmluZFRleHR1cmUoIGdsLlRFWFRVUkVfMkQsIHRlbXBUZXh0dXJlICk7XG5cdFx0XHRcdFx0Z2wuY29weVRleEltYWdlMkQoIGdsLlRFWFRVUkVfMkQsIDAsIGdsLlJHQiwgc2NyZWVuUG9zaXRpb25QaXhlbHMueCwgc2NyZWVuUG9zaXRpb25QaXhlbHMueSwgMTYsIDE2LCAwICk7XG5cblxuXHRcdFx0XHRcdC8vIHJlbmRlciBwaW5rIHF1YWRcblxuXHRcdFx0XHRcdGdsLnVuaWZvcm0xaSggdW5pZm9ybXMucmVuZGVyVHlwZSwgMCApO1xuXHRcdFx0XHRcdGdsLnVuaWZvcm0yZiggdW5pZm9ybXMuc2NhbGUsIHNjYWxlLngsIHNjYWxlLnkgKTtcblx0XHRcdFx0XHRnbC51bmlmb3JtM2YoIHVuaWZvcm1zLnNjcmVlblBvc2l0aW9uLCBzY3JlZW5Qb3NpdGlvbi54LCBzY3JlZW5Qb3NpdGlvbi55LCBzY3JlZW5Qb3NpdGlvbi56ICk7XG5cblx0XHRcdFx0XHRzdGF0ZS5kaXNhYmxlKCBnbC5CTEVORCApO1xuXHRcdFx0XHRcdHN0YXRlLmVuYWJsZSggZ2wuREVQVEhfVEVTVCApO1xuXG5cdFx0XHRcdFx0Z2wuZHJhd0VsZW1lbnRzKCBnbC5UUklBTkdMRVMsIDYsIGdsLlVOU0lHTkVEX1NIT1JULCAwICk7XG5cblxuXHRcdFx0XHRcdC8vIGNvcHkgcmVzdWx0IHRvIG9jY2x1c2lvbk1hcFxuXG5cdFx0XHRcdFx0c3RhdGUuYWN0aXZlVGV4dHVyZSggZ2wuVEVYVFVSRTAgKTtcblx0XHRcdFx0XHRzdGF0ZS5iaW5kVGV4dHVyZSggZ2wuVEVYVFVSRV8yRCwgb2NjbHVzaW9uVGV4dHVyZSApO1xuXHRcdFx0XHRcdGdsLmNvcHlUZXhJbWFnZTJEKCBnbC5URVhUVVJFXzJELCAwLCBnbC5SR0JBLCBzY3JlZW5Qb3NpdGlvblBpeGVscy54LCBzY3JlZW5Qb3NpdGlvblBpeGVscy55LCAxNiwgMTYsIDAgKTtcblxuXG5cdFx0XHRcdFx0Ly8gcmVzdG9yZSBncmFwaGljc1xuXG5cdFx0XHRcdFx0Z2wudW5pZm9ybTFpKCB1bmlmb3Jtcy5yZW5kZXJUeXBlLCAxICk7XG5cdFx0XHRcdFx0c3RhdGUuZGlzYWJsZSggZ2wuREVQVEhfVEVTVCApO1xuXG5cdFx0XHRcdFx0c3RhdGUuYWN0aXZlVGV4dHVyZSggZ2wuVEVYVFVSRTEgKTtcblx0XHRcdFx0XHRzdGF0ZS5iaW5kVGV4dHVyZSggZ2wuVEVYVFVSRV8yRCwgdGVtcFRleHR1cmUgKTtcblx0XHRcdFx0XHRnbC5kcmF3RWxlbWVudHMoIGdsLlRSSUFOR0xFUywgNiwgZ2wuVU5TSUdORURfU0hPUlQsIDAgKTtcblxuXG5cdFx0XHRcdFx0Ly8gdXBkYXRlIG9iamVjdCBwb3NpdGlvbnNcblxuXHRcdFx0XHRcdGZsYXJlLnBvc2l0aW9uU2NyZWVuLmNvcHkoIHNjcmVlblBvc2l0aW9uICk7XG5cblx0XHRcdFx0XHRpZiAoIGZsYXJlLmN1c3RvbVVwZGF0ZUNhbGxiYWNrICkge1xuXG5cdFx0XHRcdFx0XHRmbGFyZS5jdXN0b21VcGRhdGVDYWxsYmFjayggZmxhcmUgKTtcblxuXHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdGZsYXJlLnVwZGF0ZUxlbnNGbGFyZXMoKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vIHJlbmRlciBmbGFyZXNcblxuXHRcdFx0XHRcdGdsLnVuaWZvcm0xaSggdW5pZm9ybXMucmVuZGVyVHlwZSwgMiApO1xuXHRcdFx0XHRcdHN0YXRlLmVuYWJsZSggZ2wuQkxFTkQgKTtcblxuXHRcdFx0XHRcdGZvciAoIHZhciBqID0gMCwgamwgPSBmbGFyZS5sZW5zRmxhcmVzLmxlbmd0aDsgaiA8IGpsOyBqICsrICkge1xuXG5cdFx0XHRcdFx0XHR2YXIgc3ByaXRlID0gZmxhcmUubGVuc0ZsYXJlc1sgaiBdO1xuXG5cdFx0XHRcdFx0XHRpZiAoIHNwcml0ZS5vcGFjaXR5ID4gMC4wMDEgJiYgc3ByaXRlLnNjYWxlID4gMC4wMDEgKSB7XG5cblx0XHRcdFx0XHRcdFx0c2NyZWVuUG9zaXRpb24ueCA9IHNwcml0ZS54O1xuXHRcdFx0XHRcdFx0XHRzY3JlZW5Qb3NpdGlvbi55ID0gc3ByaXRlLnk7XG5cdFx0XHRcdFx0XHRcdHNjcmVlblBvc2l0aW9uLnogPSBzcHJpdGUuejtcblxuXHRcdFx0XHRcdFx0XHRzaXplID0gc3ByaXRlLnNpemUgKiBzcHJpdGUuc2NhbGUgLyB2aWV3cG9ydC53O1xuXG5cdFx0XHRcdFx0XHRcdHNjYWxlLnggPSBzaXplICogaW52QXNwZWN0O1xuXHRcdFx0XHRcdFx0XHRzY2FsZS55ID0gc2l6ZTtcblxuXHRcdFx0XHRcdFx0XHRnbC51bmlmb3JtM2YoIHVuaWZvcm1zLnNjcmVlblBvc2l0aW9uLCBzY3JlZW5Qb3NpdGlvbi54LCBzY3JlZW5Qb3NpdGlvbi55LCBzY3JlZW5Qb3NpdGlvbi56ICk7XG5cdFx0XHRcdFx0XHRcdGdsLnVuaWZvcm0yZiggdW5pZm9ybXMuc2NhbGUsIHNjYWxlLngsIHNjYWxlLnkgKTtcblx0XHRcdFx0XHRcdFx0Z2wudW5pZm9ybTFmKCB1bmlmb3Jtcy5yb3RhdGlvbiwgc3ByaXRlLnJvdGF0aW9uICk7XG5cblx0XHRcdFx0XHRcdFx0Z2wudW5pZm9ybTFmKCB1bmlmb3Jtcy5vcGFjaXR5LCBzcHJpdGUub3BhY2l0eSApO1xuXHRcdFx0XHRcdFx0XHRnbC51bmlmb3JtM2YoIHVuaWZvcm1zLmNvbG9yLCBzcHJpdGUuY29sb3Iuciwgc3ByaXRlLmNvbG9yLmcsIHNwcml0ZS5jb2xvci5iICk7XG5cblx0XHRcdFx0XHRcdFx0c3RhdGUuc2V0QmxlbmRpbmcoIHNwcml0ZS5ibGVuZGluZywgc3ByaXRlLmJsZW5kRXF1YXRpb24sIHNwcml0ZS5ibGVuZFNyYywgc3ByaXRlLmJsZW5kRHN0ICk7XG5cdFx0XHRcdFx0XHRcdHJlbmRlcmVyLnNldFRleHR1cmUyRCggc3ByaXRlLnRleHR1cmUsIDEgKTtcblxuXHRcdFx0XHRcdFx0XHRnbC5kcmF3RWxlbWVudHMoIGdsLlRSSUFOR0xFUywgNiwgZ2wuVU5TSUdORURfU0hPUlQsIDAgKTtcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHQvLyByZXN0b3JlIGdsXG5cblx0XHRcdHN0YXRlLmVuYWJsZSggZ2wuQ1VMTF9GQUNFICk7XG5cdFx0XHRzdGF0ZS5lbmFibGUoIGdsLkRFUFRIX1RFU1QgKTtcblx0XHRcdHN0YXRlLnNldERlcHRoV3JpdGUoIHRydWUgKTtcblxuXHRcdFx0cmVuZGVyZXIucmVzZXRHTFN0YXRlKCk7XG5cblx0XHR9O1xuXG5cdFx0ZnVuY3Rpb24gY3JlYXRlUHJvZ3JhbSggc2hhZGVyICkge1xuXG5cdFx0XHR2YXIgcHJvZ3JhbSA9IGdsLmNyZWF0ZVByb2dyYW0oKTtcblxuXHRcdFx0dmFyIGZyYWdtZW50U2hhZGVyID0gZ2wuY3JlYXRlU2hhZGVyKCBnbC5GUkFHTUVOVF9TSEFERVIgKTtcblx0XHRcdHZhciB2ZXJ0ZXhTaGFkZXIgPSBnbC5jcmVhdGVTaGFkZXIoIGdsLlZFUlRFWF9TSEFERVIgKTtcblxuXHRcdFx0dmFyIHByZWZpeCA9IFwicHJlY2lzaW9uIFwiICsgcmVuZGVyZXIuZ2V0UHJlY2lzaW9uKCkgKyBcIiBmbG9hdDtcXG5cIjtcblxuXHRcdFx0Z2wuc2hhZGVyU291cmNlKCBmcmFnbWVudFNoYWRlciwgcHJlZml4ICsgc2hhZGVyLmZyYWdtZW50U2hhZGVyICk7XG5cdFx0XHRnbC5zaGFkZXJTb3VyY2UoIHZlcnRleFNoYWRlciwgcHJlZml4ICsgc2hhZGVyLnZlcnRleFNoYWRlciApO1xuXG5cdFx0XHRnbC5jb21waWxlU2hhZGVyKCBmcmFnbWVudFNoYWRlciApO1xuXHRcdFx0Z2wuY29tcGlsZVNoYWRlciggdmVydGV4U2hhZGVyICk7XG5cblx0XHRcdGdsLmF0dGFjaFNoYWRlciggcHJvZ3JhbSwgZnJhZ21lbnRTaGFkZXIgKTtcblx0XHRcdGdsLmF0dGFjaFNoYWRlciggcHJvZ3JhbSwgdmVydGV4U2hhZGVyICk7XG5cblx0XHRcdGdsLmxpbmtQcm9ncmFtKCBwcm9ncmFtICk7XG5cblx0XHRcdHJldHVybiBwcm9ncmFtO1xuXG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogQGF1dGhvciBtaWthZWwgZW10aW5nZXIgLyBodHRwOi8vZ29tby5zZS9cblx0ICogQGF1dGhvciBhbHRlcmVkcSAvIGh0dHA6Ly9hbHRlcmVkcXVhbGlhLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gU3ByaXRlUGx1Z2luKCByZW5kZXJlciwgc3ByaXRlcyApIHtcblxuXHRcdHZhciBnbCA9IHJlbmRlcmVyLmNvbnRleHQ7XG5cdFx0dmFyIHN0YXRlID0gcmVuZGVyZXIuc3RhdGU7XG5cblx0XHR2YXIgdmVydGV4QnVmZmVyLCBlbGVtZW50QnVmZmVyO1xuXHRcdHZhciBwcm9ncmFtLCBhdHRyaWJ1dGVzLCB1bmlmb3JtcztcblxuXHRcdHZhciB0ZXh0dXJlO1xuXG5cdFx0Ly8gZGVjb21wb3NlIG1hdHJpeFdvcmxkXG5cblx0XHR2YXIgc3ByaXRlUG9zaXRpb24gPSBuZXcgVmVjdG9yMygpO1xuXHRcdHZhciBzcHJpdGVSb3RhdGlvbiA9IG5ldyBRdWF0ZXJuaW9uKCk7XG5cdFx0dmFyIHNwcml0ZVNjYWxlID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdGZ1bmN0aW9uIGluaXQoKSB7XG5cblx0XHRcdHZhciB2ZXJ0aWNlcyA9IG5ldyBGbG9hdDMyQXJyYXkoIFtcblx0XHRcdFx0LSAwLjUsIC0gMC41LCAgMCwgMCxcblx0XHRcdFx0ICAwLjUsIC0gMC41LCAgMSwgMCxcblx0XHRcdFx0ICAwLjUsICAgMC41LCAgMSwgMSxcblx0XHRcdFx0LSAwLjUsICAgMC41LCAgMCwgMVxuXHRcdFx0XSApO1xuXG5cdFx0XHR2YXIgZmFjZXMgPSBuZXcgVWludDE2QXJyYXkoIFtcblx0XHRcdFx0MCwgMSwgMixcblx0XHRcdFx0MCwgMiwgM1xuXHRcdFx0XSApO1xuXG5cdFx0XHR2ZXJ0ZXhCdWZmZXIgID0gZ2wuY3JlYXRlQnVmZmVyKCk7XG5cdFx0XHRlbGVtZW50QnVmZmVyID0gZ2wuY3JlYXRlQnVmZmVyKCk7XG5cblx0XHRcdGdsLmJpbmRCdWZmZXIoIGdsLkFSUkFZX0JVRkZFUiwgdmVydGV4QnVmZmVyICk7XG5cdFx0XHRnbC5idWZmZXJEYXRhKCBnbC5BUlJBWV9CVUZGRVIsIHZlcnRpY2VzLCBnbC5TVEFUSUNfRFJBVyApO1xuXG5cdFx0XHRnbC5iaW5kQnVmZmVyKCBnbC5FTEVNRU5UX0FSUkFZX0JVRkZFUiwgZWxlbWVudEJ1ZmZlciApO1xuXHRcdFx0Z2wuYnVmZmVyRGF0YSggZ2wuRUxFTUVOVF9BUlJBWV9CVUZGRVIsIGZhY2VzLCBnbC5TVEFUSUNfRFJBVyApO1xuXG5cdFx0XHRwcm9ncmFtID0gY3JlYXRlUHJvZ3JhbSgpO1xuXG5cdFx0XHRhdHRyaWJ1dGVzID0ge1xuXHRcdFx0XHRwb3NpdGlvbjpcdFx0XHRnbC5nZXRBdHRyaWJMb2NhdGlvbiAoIHByb2dyYW0sICdwb3NpdGlvbicgKSxcblx0XHRcdFx0dXY6XHRcdFx0XHRcdGdsLmdldEF0dHJpYkxvY2F0aW9uICggcHJvZ3JhbSwgJ3V2JyApXG5cdFx0XHR9O1xuXG5cdFx0XHR1bmlmb3JtcyA9IHtcblx0XHRcdFx0dXZPZmZzZXQ6XHRcdFx0Z2wuZ2V0VW5pZm9ybUxvY2F0aW9uKCBwcm9ncmFtLCAndXZPZmZzZXQnICksXG5cdFx0XHRcdHV2U2NhbGU6XHRcdFx0Z2wuZ2V0VW5pZm9ybUxvY2F0aW9uKCBwcm9ncmFtLCAndXZTY2FsZScgKSxcblxuXHRcdFx0XHRyb3RhdGlvbjpcdFx0XHRnbC5nZXRVbmlmb3JtTG9jYXRpb24oIHByb2dyYW0sICdyb3RhdGlvbicgKSxcblx0XHRcdFx0c2NhbGU6XHRcdFx0XHRnbC5nZXRVbmlmb3JtTG9jYXRpb24oIHByb2dyYW0sICdzY2FsZScgKSxcblxuXHRcdFx0XHRjb2xvcjpcdFx0XHRcdGdsLmdldFVuaWZvcm1Mb2NhdGlvbiggcHJvZ3JhbSwgJ2NvbG9yJyApLFxuXHRcdFx0XHRtYXA6XHRcdFx0XHRnbC5nZXRVbmlmb3JtTG9jYXRpb24oIHByb2dyYW0sICdtYXAnICksXG5cdFx0XHRcdG9wYWNpdHk6XHRcdFx0Z2wuZ2V0VW5pZm9ybUxvY2F0aW9uKCBwcm9ncmFtLCAnb3BhY2l0eScgKSxcblxuXHRcdFx0XHRtb2RlbFZpZXdNYXRyaXg6IFx0Z2wuZ2V0VW5pZm9ybUxvY2F0aW9uKCBwcm9ncmFtLCAnbW9kZWxWaWV3TWF0cml4JyApLFxuXHRcdFx0XHRwcm9qZWN0aW9uTWF0cml4Olx0Z2wuZ2V0VW5pZm9ybUxvY2F0aW9uKCBwcm9ncmFtLCAncHJvamVjdGlvbk1hdHJpeCcgKSxcblxuXHRcdFx0XHRmb2dUeXBlOlx0XHRcdGdsLmdldFVuaWZvcm1Mb2NhdGlvbiggcHJvZ3JhbSwgJ2ZvZ1R5cGUnICksXG5cdFx0XHRcdGZvZ0RlbnNpdHk6XHRcdFx0Z2wuZ2V0VW5pZm9ybUxvY2F0aW9uKCBwcm9ncmFtLCAnZm9nRGVuc2l0eScgKSxcblx0XHRcdFx0Zm9nTmVhcjpcdFx0XHRnbC5nZXRVbmlmb3JtTG9jYXRpb24oIHByb2dyYW0sICdmb2dOZWFyJyApLFxuXHRcdFx0XHRmb2dGYXI6XHRcdFx0XHRnbC5nZXRVbmlmb3JtTG9jYXRpb24oIHByb2dyYW0sICdmb2dGYXInICksXG5cdFx0XHRcdGZvZ0NvbG9yOlx0XHRcdGdsLmdldFVuaWZvcm1Mb2NhdGlvbiggcHJvZ3JhbSwgJ2ZvZ0NvbG9yJyApLFxuXG5cdFx0XHRcdGFscGhhVGVzdDpcdFx0XHRnbC5nZXRVbmlmb3JtTG9jYXRpb24oIHByb2dyYW0sICdhbHBoYVRlc3QnIClcblx0XHRcdH07XG5cblx0XHRcdHZhciBjYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMoICdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sJywgJ2NhbnZhcycgKTtcblx0XHRcdGNhbnZhcy53aWR0aCA9IDg7XG5cdFx0XHRjYW52YXMuaGVpZ2h0ID0gODtcblxuXHRcdFx0dmFyIGNvbnRleHQgPSBjYW52YXMuZ2V0Q29udGV4dCggJzJkJyApO1xuXHRcdFx0Y29udGV4dC5maWxsU3R5bGUgPSAnd2hpdGUnO1xuXHRcdFx0Y29udGV4dC5maWxsUmVjdCggMCwgMCwgOCwgOCApO1xuXG5cdFx0XHR0ZXh0dXJlID0gbmV3IFRleHR1cmUoIGNhbnZhcyApO1xuXHRcdFx0dGV4dHVyZS5uZWVkc1VwZGF0ZSA9IHRydWU7XG5cblx0XHR9XG5cblx0XHR0aGlzLnJlbmRlciA9IGZ1bmN0aW9uICggc2NlbmUsIGNhbWVyYSApIHtcblxuXHRcdFx0aWYgKCBzcHJpdGVzLmxlbmd0aCA9PT0gMCApIHJldHVybjtcblxuXHRcdFx0Ly8gc2V0dXAgZ2xcblxuXHRcdFx0aWYgKCBwcm9ncmFtID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0aW5pdCgpO1xuXG5cdFx0XHR9XG5cblx0XHRcdGdsLnVzZVByb2dyYW0oIHByb2dyYW0gKTtcblxuXHRcdFx0c3RhdGUuaW5pdEF0dHJpYnV0ZXMoKTtcblx0XHRcdHN0YXRlLmVuYWJsZUF0dHJpYnV0ZSggYXR0cmlidXRlcy5wb3NpdGlvbiApO1xuXHRcdFx0c3RhdGUuZW5hYmxlQXR0cmlidXRlKCBhdHRyaWJ1dGVzLnV2ICk7XG5cdFx0XHRzdGF0ZS5kaXNhYmxlVW51c2VkQXR0cmlidXRlcygpO1xuXG5cdFx0XHRzdGF0ZS5kaXNhYmxlKCBnbC5DVUxMX0ZBQ0UgKTtcblx0XHRcdHN0YXRlLmVuYWJsZSggZ2wuQkxFTkQgKTtcblxuXHRcdFx0Z2wuYmluZEJ1ZmZlciggZ2wuQVJSQVlfQlVGRkVSLCB2ZXJ0ZXhCdWZmZXIgKTtcblx0XHRcdGdsLnZlcnRleEF0dHJpYlBvaW50ZXIoIGF0dHJpYnV0ZXMucG9zaXRpb24sIDIsIGdsLkZMT0FULCBmYWxzZSwgMiAqIDgsIDAgKTtcblx0XHRcdGdsLnZlcnRleEF0dHJpYlBvaW50ZXIoIGF0dHJpYnV0ZXMudXYsIDIsIGdsLkZMT0FULCBmYWxzZSwgMiAqIDgsIDggKTtcblxuXHRcdFx0Z2wuYmluZEJ1ZmZlciggZ2wuRUxFTUVOVF9BUlJBWV9CVUZGRVIsIGVsZW1lbnRCdWZmZXIgKTtcblxuXHRcdFx0Z2wudW5pZm9ybU1hdHJpeDRmdiggdW5pZm9ybXMucHJvamVjdGlvbk1hdHJpeCwgZmFsc2UsIGNhbWVyYS5wcm9qZWN0aW9uTWF0cml4LmVsZW1lbnRzICk7XG5cblx0XHRcdHN0YXRlLmFjdGl2ZVRleHR1cmUoIGdsLlRFWFRVUkUwICk7XG5cdFx0XHRnbC51bmlmb3JtMWkoIHVuaWZvcm1zLm1hcCwgMCApO1xuXG5cdFx0XHR2YXIgb2xkRm9nVHlwZSA9IDA7XG5cdFx0XHR2YXIgc2NlbmVGb2dUeXBlID0gMDtcblx0XHRcdHZhciBmb2cgPSBzY2VuZS5mb2c7XG5cblx0XHRcdGlmICggZm9nICkge1xuXG5cdFx0XHRcdGdsLnVuaWZvcm0zZiggdW5pZm9ybXMuZm9nQ29sb3IsIGZvZy5jb2xvci5yLCBmb2cuY29sb3IuZywgZm9nLmNvbG9yLmIgKTtcblxuXHRcdFx0XHRpZiAoIGZvZy5pc0ZvZyApIHtcblxuXHRcdFx0XHRcdGdsLnVuaWZvcm0xZiggdW5pZm9ybXMuZm9nTmVhciwgZm9nLm5lYXIgKTtcblx0XHRcdFx0XHRnbC51bmlmb3JtMWYoIHVuaWZvcm1zLmZvZ0ZhciwgZm9nLmZhciApO1xuXG5cdFx0XHRcdFx0Z2wudW5pZm9ybTFpKCB1bmlmb3Jtcy5mb2dUeXBlLCAxICk7XG5cdFx0XHRcdFx0b2xkRm9nVHlwZSA9IDE7XG5cdFx0XHRcdFx0c2NlbmVGb2dUeXBlID0gMTtcblxuXHRcdFx0XHR9IGVsc2UgaWYgKCBmb2cuaXNGb2dFeHAyICkge1xuXG5cdFx0XHRcdFx0Z2wudW5pZm9ybTFmKCB1bmlmb3Jtcy5mb2dEZW5zaXR5LCBmb2cuZGVuc2l0eSApO1xuXG5cdFx0XHRcdFx0Z2wudW5pZm9ybTFpKCB1bmlmb3Jtcy5mb2dUeXBlLCAyICk7XG5cdFx0XHRcdFx0b2xkRm9nVHlwZSA9IDI7XG5cdFx0XHRcdFx0c2NlbmVGb2dUeXBlID0gMjtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0Z2wudW5pZm9ybTFpKCB1bmlmb3Jtcy5mb2dUeXBlLCAwICk7XG5cdFx0XHRcdG9sZEZvZ1R5cGUgPSAwO1xuXHRcdFx0XHRzY2VuZUZvZ1R5cGUgPSAwO1xuXG5cdFx0XHR9XG5cblxuXHRcdFx0Ly8gdXBkYXRlIHBvc2l0aW9ucyBhbmQgc29ydFxuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBzcHJpdGVzLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0dmFyIHNwcml0ZSA9IHNwcml0ZXNbIGkgXTtcblxuXHRcdFx0XHRzcHJpdGUubW9kZWxWaWV3TWF0cml4Lm11bHRpcGx5TWF0cmljZXMoIGNhbWVyYS5tYXRyaXhXb3JsZEludmVyc2UsIHNwcml0ZS5tYXRyaXhXb3JsZCApO1xuXHRcdFx0XHRzcHJpdGUueiA9IC0gc3ByaXRlLm1vZGVsVmlld01hdHJpeC5lbGVtZW50c1sgMTQgXTtcblxuXHRcdFx0fVxuXG5cdFx0XHRzcHJpdGVzLnNvcnQoIHBhaW50ZXJTb3J0U3RhYmxlICk7XG5cblx0XHRcdC8vIHJlbmRlciBhbGwgc3ByaXRlc1xuXG5cdFx0XHR2YXIgc2NhbGUgPSBbXTtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBsID0gc3ByaXRlcy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdHZhciBzcHJpdGUgPSBzcHJpdGVzWyBpIF07XG5cdFx0XHRcdHZhciBtYXRlcmlhbCA9IHNwcml0ZS5tYXRlcmlhbDtcblxuXHRcdFx0XHRpZiAoIG1hdGVyaWFsLnZpc2libGUgPT09IGZhbHNlICkgY29udGludWU7XG5cblx0XHRcdFx0Z2wudW5pZm9ybTFmKCB1bmlmb3Jtcy5hbHBoYVRlc3QsIG1hdGVyaWFsLmFscGhhVGVzdCApO1xuXHRcdFx0XHRnbC51bmlmb3JtTWF0cml4NGZ2KCB1bmlmb3Jtcy5tb2RlbFZpZXdNYXRyaXgsIGZhbHNlLCBzcHJpdGUubW9kZWxWaWV3TWF0cml4LmVsZW1lbnRzICk7XG5cblx0XHRcdFx0c3ByaXRlLm1hdHJpeFdvcmxkLmRlY29tcG9zZSggc3ByaXRlUG9zaXRpb24sIHNwcml0ZVJvdGF0aW9uLCBzcHJpdGVTY2FsZSApO1xuXG5cdFx0XHRcdHNjYWxlWyAwIF0gPSBzcHJpdGVTY2FsZS54O1xuXHRcdFx0XHRzY2FsZVsgMSBdID0gc3ByaXRlU2NhbGUueTtcblxuXHRcdFx0XHR2YXIgZm9nVHlwZSA9IDA7XG5cblx0XHRcdFx0aWYgKCBzY2VuZS5mb2cgJiYgbWF0ZXJpYWwuZm9nICkge1xuXG5cdFx0XHRcdFx0Zm9nVHlwZSA9IHNjZW5lRm9nVHlwZTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCBvbGRGb2dUeXBlICE9PSBmb2dUeXBlICkge1xuXG5cdFx0XHRcdFx0Z2wudW5pZm9ybTFpKCB1bmlmb3Jtcy5mb2dUeXBlLCBmb2dUeXBlICk7XG5cdFx0XHRcdFx0b2xkRm9nVHlwZSA9IGZvZ1R5cGU7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggbWF0ZXJpYWwubWFwICE9PSBudWxsICkge1xuXG5cdFx0XHRcdFx0Z2wudW5pZm9ybTJmKCB1bmlmb3Jtcy51dk9mZnNldCwgbWF0ZXJpYWwubWFwLm9mZnNldC54LCBtYXRlcmlhbC5tYXAub2Zmc2V0LnkgKTtcblx0XHRcdFx0XHRnbC51bmlmb3JtMmYoIHVuaWZvcm1zLnV2U2NhbGUsIG1hdGVyaWFsLm1hcC5yZXBlYXQueCwgbWF0ZXJpYWwubWFwLnJlcGVhdC55ICk7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdGdsLnVuaWZvcm0yZiggdW5pZm9ybXMudXZPZmZzZXQsIDAsIDAgKTtcblx0XHRcdFx0XHRnbC51bmlmb3JtMmYoIHVuaWZvcm1zLnV2U2NhbGUsIDEsIDEgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Z2wudW5pZm9ybTFmKCB1bmlmb3Jtcy5vcGFjaXR5LCBtYXRlcmlhbC5vcGFjaXR5ICk7XG5cdFx0XHRcdGdsLnVuaWZvcm0zZiggdW5pZm9ybXMuY29sb3IsIG1hdGVyaWFsLmNvbG9yLnIsIG1hdGVyaWFsLmNvbG9yLmcsIG1hdGVyaWFsLmNvbG9yLmIgKTtcblxuXHRcdFx0XHRnbC51bmlmb3JtMWYoIHVuaWZvcm1zLnJvdGF0aW9uLCBtYXRlcmlhbC5yb3RhdGlvbiApO1xuXHRcdFx0XHRnbC51bmlmb3JtMmZ2KCB1bmlmb3Jtcy5zY2FsZSwgc2NhbGUgKTtcblxuXHRcdFx0XHRzdGF0ZS5zZXRCbGVuZGluZyggbWF0ZXJpYWwuYmxlbmRpbmcsIG1hdGVyaWFsLmJsZW5kRXF1YXRpb24sIG1hdGVyaWFsLmJsZW5kU3JjLCBtYXRlcmlhbC5ibGVuZERzdCApO1xuXHRcdFx0XHRzdGF0ZS5zZXREZXB0aFRlc3QoIG1hdGVyaWFsLmRlcHRoVGVzdCApO1xuXHRcdFx0XHRzdGF0ZS5zZXREZXB0aFdyaXRlKCBtYXRlcmlhbC5kZXB0aFdyaXRlICk7XG5cblx0XHRcdFx0aWYgKCBtYXRlcmlhbC5tYXAgKSB7XG5cblx0XHRcdFx0XHRyZW5kZXJlci5zZXRUZXh0dXJlMkQoIG1hdGVyaWFsLm1hcCwgMCApO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRyZW5kZXJlci5zZXRUZXh0dXJlMkQoIHRleHR1cmUsIDAgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Z2wuZHJhd0VsZW1lbnRzKCBnbC5UUklBTkdMRVMsIDYsIGdsLlVOU0lHTkVEX1NIT1JULCAwICk7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gcmVzdG9yZSBnbFxuXG5cdFx0XHRzdGF0ZS5lbmFibGUoIGdsLkNVTExfRkFDRSApO1xuXG5cdFx0XHRyZW5kZXJlci5yZXNldEdMU3RhdGUoKTtcblxuXHRcdH07XG5cblx0XHRmdW5jdGlvbiBjcmVhdGVQcm9ncmFtKCkge1xuXG5cdFx0XHR2YXIgcHJvZ3JhbSA9IGdsLmNyZWF0ZVByb2dyYW0oKTtcblxuXHRcdFx0dmFyIHZlcnRleFNoYWRlciA9IGdsLmNyZWF0ZVNoYWRlciggZ2wuVkVSVEVYX1NIQURFUiApO1xuXHRcdFx0dmFyIGZyYWdtZW50U2hhZGVyID0gZ2wuY3JlYXRlU2hhZGVyKCBnbC5GUkFHTUVOVF9TSEFERVIgKTtcblxuXHRcdFx0Z2wuc2hhZGVyU291cmNlKCB2ZXJ0ZXhTaGFkZXIsIFtcblxuXHRcdFx0XHQncHJlY2lzaW9uICcgKyByZW5kZXJlci5nZXRQcmVjaXNpb24oKSArICcgZmxvYXQ7JyxcblxuXHRcdFx0XHQndW5pZm9ybSBtYXQ0IG1vZGVsVmlld01hdHJpeDsnLFxuXHRcdFx0XHQndW5pZm9ybSBtYXQ0IHByb2plY3Rpb25NYXRyaXg7Jyxcblx0XHRcdFx0J3VuaWZvcm0gZmxvYXQgcm90YXRpb247Jyxcblx0XHRcdFx0J3VuaWZvcm0gdmVjMiBzY2FsZTsnLFxuXHRcdFx0XHQndW5pZm9ybSB2ZWMyIHV2T2Zmc2V0OycsXG5cdFx0XHRcdCd1bmlmb3JtIHZlYzIgdXZTY2FsZTsnLFxuXG5cdFx0XHRcdCdhdHRyaWJ1dGUgdmVjMiBwb3NpdGlvbjsnLFxuXHRcdFx0XHQnYXR0cmlidXRlIHZlYzIgdXY7JyxcblxuXHRcdFx0XHQndmFyeWluZyB2ZWMyIHZVVjsnLFxuXG5cdFx0XHRcdCd2b2lkIG1haW4oKSB7JyxcblxuXHRcdFx0XHRcdCd2VVYgPSB1dk9mZnNldCArIHV2ICogdXZTY2FsZTsnLFxuXG5cdFx0XHRcdFx0J3ZlYzIgYWxpZ25lZFBvc2l0aW9uID0gcG9zaXRpb24gKiBzY2FsZTsnLFxuXG5cdFx0XHRcdFx0J3ZlYzIgcm90YXRlZFBvc2l0aW9uOycsXG5cdFx0XHRcdFx0J3JvdGF0ZWRQb3NpdGlvbi54ID0gY29zKCByb3RhdGlvbiApICogYWxpZ25lZFBvc2l0aW9uLnggLSBzaW4oIHJvdGF0aW9uICkgKiBhbGlnbmVkUG9zaXRpb24ueTsnLFxuXHRcdFx0XHRcdCdyb3RhdGVkUG9zaXRpb24ueSA9IHNpbiggcm90YXRpb24gKSAqIGFsaWduZWRQb3NpdGlvbi54ICsgY29zKCByb3RhdGlvbiApICogYWxpZ25lZFBvc2l0aW9uLnk7JyxcblxuXHRcdFx0XHRcdCd2ZWM0IGZpbmFsUG9zaXRpb247JyxcblxuXHRcdFx0XHRcdCdmaW5hbFBvc2l0aW9uID0gbW9kZWxWaWV3TWF0cml4ICogdmVjNCggMC4wLCAwLjAsIDAuMCwgMS4wICk7Jyxcblx0XHRcdFx0XHQnZmluYWxQb3NpdGlvbi54eSArPSByb3RhdGVkUG9zaXRpb247Jyxcblx0XHRcdFx0XHQnZmluYWxQb3NpdGlvbiA9IHByb2plY3Rpb25NYXRyaXggKiBmaW5hbFBvc2l0aW9uOycsXG5cblx0XHRcdFx0XHQnZ2xfUG9zaXRpb24gPSBmaW5hbFBvc2l0aW9uOycsXG5cblx0XHRcdFx0J30nXG5cblx0XHRcdF0uam9pbiggJ1xcbicgKSApO1xuXG5cdFx0XHRnbC5zaGFkZXJTb3VyY2UoIGZyYWdtZW50U2hhZGVyLCBbXG5cblx0XHRcdFx0J3ByZWNpc2lvbiAnICsgcmVuZGVyZXIuZ2V0UHJlY2lzaW9uKCkgKyAnIGZsb2F0OycsXG5cblx0XHRcdFx0J3VuaWZvcm0gdmVjMyBjb2xvcjsnLFxuXHRcdFx0XHQndW5pZm9ybSBzYW1wbGVyMkQgbWFwOycsXG5cdFx0XHRcdCd1bmlmb3JtIGZsb2F0IG9wYWNpdHk7JyxcblxuXHRcdFx0XHQndW5pZm9ybSBpbnQgZm9nVHlwZTsnLFxuXHRcdFx0XHQndW5pZm9ybSB2ZWMzIGZvZ0NvbG9yOycsXG5cdFx0XHRcdCd1bmlmb3JtIGZsb2F0IGZvZ0RlbnNpdHk7Jyxcblx0XHRcdFx0J3VuaWZvcm0gZmxvYXQgZm9nTmVhcjsnLFxuXHRcdFx0XHQndW5pZm9ybSBmbG9hdCBmb2dGYXI7Jyxcblx0XHRcdFx0J3VuaWZvcm0gZmxvYXQgYWxwaGFUZXN0OycsXG5cblx0XHRcdFx0J3ZhcnlpbmcgdmVjMiB2VVY7JyxcblxuXHRcdFx0XHQndm9pZCBtYWluKCkgeycsXG5cblx0XHRcdFx0XHQndmVjNCB0ZXh0dXJlID0gdGV4dHVyZTJEKCBtYXAsIHZVViApOycsXG5cblx0XHRcdFx0XHQnaWYgKCB0ZXh0dXJlLmEgPCBhbHBoYVRlc3QgKSBkaXNjYXJkOycsXG5cblx0XHRcdFx0XHQnZ2xfRnJhZ0NvbG9yID0gdmVjNCggY29sb3IgKiB0ZXh0dXJlLnh5eiwgdGV4dHVyZS5hICogb3BhY2l0eSApOycsXG5cblx0XHRcdFx0XHQnaWYgKCBmb2dUeXBlID4gMCApIHsnLFxuXG5cdFx0XHRcdFx0XHQnZmxvYXQgZGVwdGggPSBnbF9GcmFnQ29vcmQueiAvIGdsX0ZyYWdDb29yZC53OycsXG5cdFx0XHRcdFx0XHQnZmxvYXQgZm9nRmFjdG9yID0gMC4wOycsXG5cblx0XHRcdFx0XHRcdCdpZiAoIGZvZ1R5cGUgPT0gMSApIHsnLFxuXG5cdFx0XHRcdFx0XHRcdCdmb2dGYWN0b3IgPSBzbW9vdGhzdGVwKCBmb2dOZWFyLCBmb2dGYXIsIGRlcHRoICk7JyxcblxuXHRcdFx0XHRcdFx0J30gZWxzZSB7JyxcblxuXHRcdFx0XHRcdFx0XHQnY29uc3QgZmxvYXQgTE9HMiA9IDEuNDQyNjk1OycsXG5cdFx0XHRcdFx0XHRcdCdmb2dGYWN0b3IgPSBleHAyKCAtIGZvZ0RlbnNpdHkgKiBmb2dEZW5zaXR5ICogZGVwdGggKiBkZXB0aCAqIExPRzIgKTsnLFxuXHRcdFx0XHRcdFx0XHQnZm9nRmFjdG9yID0gMS4wIC0gY2xhbXAoIGZvZ0ZhY3RvciwgMC4wLCAxLjAgKTsnLFxuXG5cdFx0XHRcdFx0XHQnfScsXG5cblx0XHRcdFx0XHRcdCdnbF9GcmFnQ29sb3IgPSBtaXgoIGdsX0ZyYWdDb2xvciwgdmVjNCggZm9nQ29sb3IsIGdsX0ZyYWdDb2xvci53ICksIGZvZ0ZhY3RvciApOycsXG5cblx0XHRcdFx0XHQnfScsXG5cblx0XHRcdFx0J30nXG5cblx0XHRcdF0uam9pbiggJ1xcbicgKSApO1xuXG5cdFx0XHRnbC5jb21waWxlU2hhZGVyKCB2ZXJ0ZXhTaGFkZXIgKTtcblx0XHRcdGdsLmNvbXBpbGVTaGFkZXIoIGZyYWdtZW50U2hhZGVyICk7XG5cblx0XHRcdGdsLmF0dGFjaFNoYWRlciggcHJvZ3JhbSwgdmVydGV4U2hhZGVyICk7XG5cdFx0XHRnbC5hdHRhY2hTaGFkZXIoIHByb2dyYW0sIGZyYWdtZW50U2hhZGVyICk7XG5cblx0XHRcdGdsLmxpbmtQcm9ncmFtKCBwcm9ncmFtICk7XG5cblx0XHRcdHJldHVybiBwcm9ncmFtO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gcGFpbnRlclNvcnRTdGFibGUoIGEsIGIgKSB7XG5cblx0XHRcdGlmICggYS5yZW5kZXJPcmRlciAhPT0gYi5yZW5kZXJPcmRlciApIHtcblxuXHRcdFx0XHRyZXR1cm4gYS5yZW5kZXJPcmRlciAtIGIucmVuZGVyT3JkZXI7XG5cblx0XHRcdH0gZWxzZSBpZiAoIGEueiAhPT0gYi56ICkge1xuXG5cdFx0XHRcdHJldHVybiBiLnogLSBhLno7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0cmV0dXJuIGIuaWQgLSBhLmlkO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKi9cblxuXHR2YXIgbWF0ZXJpYWxJZCA9IDA7XG5cblx0ZnVuY3Rpb24gTWF0ZXJpYWwoKSB7XG5cblx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoIHRoaXMsICdpZCcsIHsgdmFsdWU6IG1hdGVyaWFsSWQgKysgfSApO1xuXG5cdFx0dGhpcy51dWlkID0gX01hdGguZ2VuZXJhdGVVVUlEKCk7XG5cblx0XHR0aGlzLm5hbWUgPSAnJztcblx0XHR0aGlzLnR5cGUgPSAnTWF0ZXJpYWwnO1xuXG5cdFx0dGhpcy5mb2cgPSB0cnVlO1xuXHRcdHRoaXMubGlnaHRzID0gdHJ1ZTtcblxuXHRcdHRoaXMuYmxlbmRpbmcgPSBOb3JtYWxCbGVuZGluZztcblx0XHR0aGlzLnNpZGUgPSBGcm9udFNpZGU7XG5cdFx0dGhpcy5zaGFkaW5nID0gU21vb3RoU2hhZGluZzsgLy8gVEhSRUUuRmxhdFNoYWRpbmcsIFRIUkVFLlNtb290aFNoYWRpbmdcblx0XHR0aGlzLnZlcnRleENvbG9ycyA9IE5vQ29sb3JzOyAvLyBUSFJFRS5Ob0NvbG9ycywgVEhSRUUuVmVydGV4Q29sb3JzLCBUSFJFRS5GYWNlQ29sb3JzXG5cblx0XHR0aGlzLm9wYWNpdHkgPSAxO1xuXHRcdHRoaXMudHJhbnNwYXJlbnQgPSBmYWxzZTtcblxuXHRcdHRoaXMuYmxlbmRTcmMgPSBTcmNBbHBoYUZhY3Rvcjtcblx0XHR0aGlzLmJsZW5kRHN0ID0gT25lTWludXNTcmNBbHBoYUZhY3Rvcjtcblx0XHR0aGlzLmJsZW5kRXF1YXRpb24gPSBBZGRFcXVhdGlvbjtcblx0XHR0aGlzLmJsZW5kU3JjQWxwaGEgPSBudWxsO1xuXHRcdHRoaXMuYmxlbmREc3RBbHBoYSA9IG51bGw7XG5cdFx0dGhpcy5ibGVuZEVxdWF0aW9uQWxwaGEgPSBudWxsO1xuXG5cdFx0dGhpcy5kZXB0aEZ1bmMgPSBMZXNzRXF1YWxEZXB0aDtcblx0XHR0aGlzLmRlcHRoVGVzdCA9IHRydWU7XG5cdFx0dGhpcy5kZXB0aFdyaXRlID0gdHJ1ZTtcblxuXHRcdHRoaXMuY2xpcHBpbmdQbGFuZXMgPSBudWxsO1xuXHRcdHRoaXMuY2xpcEludGVyc2VjdGlvbiA9IGZhbHNlO1xuXHRcdHRoaXMuY2xpcFNoYWRvd3MgPSBmYWxzZTtcblxuXHRcdHRoaXMuY29sb3JXcml0ZSA9IHRydWU7XG5cblx0XHR0aGlzLnByZWNpc2lvbiA9IG51bGw7IC8vIG92ZXJyaWRlIHRoZSByZW5kZXJlcidzIGRlZmF1bHQgcHJlY2lzaW9uIGZvciB0aGlzIG1hdGVyaWFsXG5cblx0XHR0aGlzLnBvbHlnb25PZmZzZXQgPSBmYWxzZTtcblx0XHR0aGlzLnBvbHlnb25PZmZzZXRGYWN0b3IgPSAwO1xuXHRcdHRoaXMucG9seWdvbk9mZnNldFVuaXRzID0gMDtcblxuXHRcdHRoaXMuYWxwaGFUZXN0ID0gMDtcblx0XHR0aGlzLnByZW11bHRpcGxpZWRBbHBoYSA9IGZhbHNlO1xuXG5cdFx0dGhpcy5vdmVyZHJhdyA9IDA7IC8vIE92ZXJkcmF3biBwaXhlbHMgKHR5cGljYWxseSBiZXR3ZWVuIDAgYW5kIDEpIGZvciBmaXhpbmcgYW50aWFsaWFzaW5nIGdhcHMgaW4gQ2FudmFzUmVuZGVyZXJcblxuXHRcdHRoaXMudmlzaWJsZSA9IHRydWU7XG5cblx0XHR0aGlzLl9uZWVkc1VwZGF0ZSA9IHRydWU7XG5cblx0fVxuXG5cdE1hdGVyaWFsLnByb3RvdHlwZSA9IHtcblxuXHRcdGNvbnN0cnVjdG9yOiBNYXRlcmlhbCxcblxuXHRcdGlzTWF0ZXJpYWw6IHRydWUsXG5cblx0XHRnZXQgbmVlZHNVcGRhdGUoKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLl9uZWVkc1VwZGF0ZTtcblxuXHRcdH0sXG5cblx0XHRzZXQgbmVlZHNVcGRhdGUoIHZhbHVlICkge1xuXG5cdFx0XHRpZiAoIHZhbHVlID09PSB0cnVlICkgdGhpcy51cGRhdGUoKTtcblx0XHRcdHRoaXMuX25lZWRzVXBkYXRlID0gdmFsdWU7XG5cblx0XHR9LFxuXG5cdFx0c2V0VmFsdWVzOiBmdW5jdGlvbiAoIHZhbHVlcyApIHtcblxuXHRcdFx0aWYgKCB2YWx1ZXMgPT09IHVuZGVmaW5lZCApIHJldHVybjtcblxuXHRcdFx0Zm9yICggdmFyIGtleSBpbiB2YWx1ZXMgKSB7XG5cblx0XHRcdFx0dmFyIG5ld1ZhbHVlID0gdmFsdWVzWyBrZXkgXTtcblxuXHRcdFx0XHRpZiAoIG5ld1ZhbHVlID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRjb25zb2xlLndhcm4oIFwiVEhSRUUuTWF0ZXJpYWw6ICdcIiArIGtleSArIFwiJyBwYXJhbWV0ZXIgaXMgdW5kZWZpbmVkLlwiICk7XG5cdFx0XHRcdFx0Y29udGludWU7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBjdXJyZW50VmFsdWUgPSB0aGlzWyBrZXkgXTtcblxuXHRcdFx0XHRpZiAoIGN1cnJlbnRWYWx1ZSA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0Y29uc29sZS53YXJuKCBcIlRIUkVFLlwiICsgdGhpcy50eXBlICsgXCI6ICdcIiArIGtleSArIFwiJyBpcyBub3QgYSBwcm9wZXJ0eSBvZiB0aGlzIG1hdGVyaWFsLlwiICk7XG5cdFx0XHRcdFx0Y29udGludWU7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggY3VycmVudFZhbHVlICYmIGN1cnJlbnRWYWx1ZS5pc0NvbG9yICkge1xuXG5cdFx0XHRcdFx0Y3VycmVudFZhbHVlLnNldCggbmV3VmFsdWUgKTtcblxuXHRcdFx0XHR9IGVsc2UgaWYgKCAoY3VycmVudFZhbHVlICYmIGN1cnJlbnRWYWx1ZS5pc1ZlY3RvcjMpICYmIChuZXdWYWx1ZSAmJiBuZXdWYWx1ZS5pc1ZlY3RvcjMpICkge1xuXG5cdFx0XHRcdFx0Y3VycmVudFZhbHVlLmNvcHkoIG5ld1ZhbHVlICk7XG5cblx0XHRcdFx0fSBlbHNlIGlmICgga2V5ID09PSAnb3ZlcmRyYXcnICkge1xuXG5cdFx0XHRcdFx0Ly8gZW5zdXJlIG92ZXJkcmF3IGlzIGJhY2t3YXJkcy1jb21wYXRpYmxlIHdpdGggbGVnYWN5IGJvb2xlYW4gdHlwZVxuXHRcdFx0XHRcdHRoaXNbIGtleSBdID0gTnVtYmVyKCBuZXdWYWx1ZSApO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHR0aGlzWyBrZXkgXSA9IG5ld1ZhbHVlO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0fSxcblxuXHRcdHRvSlNPTjogZnVuY3Rpb24gKCBtZXRhICkge1xuXG5cdFx0XHR2YXIgaXNSb290ID0gbWV0YSA9PT0gdW5kZWZpbmVkO1xuXG5cdFx0XHRpZiAoIGlzUm9vdCApIHtcblxuXHRcdFx0XHRtZXRhID0ge1xuXHRcdFx0XHRcdHRleHR1cmVzOiB7fSxcblx0XHRcdFx0XHRpbWFnZXM6IHt9XG5cdFx0XHRcdH07XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIGRhdGEgPSB7XG5cdFx0XHRcdG1ldGFkYXRhOiB7XG5cdFx0XHRcdFx0dmVyc2lvbjogNC40LFxuXHRcdFx0XHRcdHR5cGU6ICdNYXRlcmlhbCcsXG5cdFx0XHRcdFx0Z2VuZXJhdG9yOiAnTWF0ZXJpYWwudG9KU09OJ1xuXHRcdFx0XHR9XG5cdFx0XHR9O1xuXG5cdFx0XHQvLyBzdGFuZGFyZCBNYXRlcmlhbCBzZXJpYWxpemF0aW9uXG5cdFx0XHRkYXRhLnV1aWQgPSB0aGlzLnV1aWQ7XG5cdFx0XHRkYXRhLnR5cGUgPSB0aGlzLnR5cGU7XG5cblx0XHRcdGlmICggdGhpcy5uYW1lICE9PSAnJyApIGRhdGEubmFtZSA9IHRoaXMubmFtZTtcblxuXHRcdFx0aWYgKCB0aGlzLmNvbG9yICYmIHRoaXMuY29sb3IuaXNDb2xvciApIGRhdGEuY29sb3IgPSB0aGlzLmNvbG9yLmdldEhleCgpO1xuXG5cdFx0XHRpZiAoIHRoaXMucm91Z2huZXNzICE9PSB1bmRlZmluZWQgKSBkYXRhLnJvdWdobmVzcyA9IHRoaXMucm91Z2huZXNzO1xuXHRcdFx0aWYgKCB0aGlzLm1ldGFsbmVzcyAhPT0gdW5kZWZpbmVkICkgZGF0YS5tZXRhbG5lc3MgPSB0aGlzLm1ldGFsbmVzcztcblxuXHRcdFx0aWYgKCB0aGlzLmVtaXNzaXZlICYmIHRoaXMuZW1pc3NpdmUuaXNDb2xvciApIGRhdGEuZW1pc3NpdmUgPSB0aGlzLmVtaXNzaXZlLmdldEhleCgpO1xuXHRcdFx0aWYgKCB0aGlzLnNwZWN1bGFyICYmIHRoaXMuc3BlY3VsYXIuaXNDb2xvciApIGRhdGEuc3BlY3VsYXIgPSB0aGlzLnNwZWN1bGFyLmdldEhleCgpO1xuXHRcdFx0aWYgKCB0aGlzLnNoaW5pbmVzcyAhPT0gdW5kZWZpbmVkICkgZGF0YS5zaGluaW5lc3MgPSB0aGlzLnNoaW5pbmVzcztcblx0XHRcdGlmICggdGhpcy5jbGVhckNvYXQgIT09IHVuZGVmaW5lZCApIGRhdGEuY2xlYXJDb2F0ID0gdGhpcy5jbGVhckNvYXQ7XG5cdFx0XHRpZiAoIHRoaXMuY2xlYXJDb2F0Um91Z2huZXNzICE9PSB1bmRlZmluZWQgKSBkYXRhLmNsZWFyQ29hdFJvdWdobmVzcyA9IHRoaXMuY2xlYXJDb2F0Um91Z2huZXNzO1xuXG5cdFx0XHRpZiAoIHRoaXMubWFwICYmIHRoaXMubWFwLmlzVGV4dHVyZSApIGRhdGEubWFwID0gdGhpcy5tYXAudG9KU09OKCBtZXRhICkudXVpZDtcblx0XHRcdGlmICggdGhpcy5hbHBoYU1hcCAmJiB0aGlzLmFscGhhTWFwLmlzVGV4dHVyZSApIGRhdGEuYWxwaGFNYXAgPSB0aGlzLmFscGhhTWFwLnRvSlNPTiggbWV0YSApLnV1aWQ7XG5cdFx0XHRpZiAoIHRoaXMubGlnaHRNYXAgJiYgdGhpcy5saWdodE1hcC5pc1RleHR1cmUgKSBkYXRhLmxpZ2h0TWFwID0gdGhpcy5saWdodE1hcC50b0pTT04oIG1ldGEgKS51dWlkO1xuXHRcdFx0aWYgKCB0aGlzLmJ1bXBNYXAgJiYgdGhpcy5idW1wTWFwLmlzVGV4dHVyZSApIHtcblxuXHRcdFx0XHRkYXRhLmJ1bXBNYXAgPSB0aGlzLmJ1bXBNYXAudG9KU09OKCBtZXRhICkudXVpZDtcblx0XHRcdFx0ZGF0YS5idW1wU2NhbGUgPSB0aGlzLmJ1bXBTY2FsZTtcblxuXHRcdFx0fVxuXHRcdFx0aWYgKCB0aGlzLm5vcm1hbE1hcCAmJiB0aGlzLm5vcm1hbE1hcC5pc1RleHR1cmUgKSB7XG5cblx0XHRcdFx0ZGF0YS5ub3JtYWxNYXAgPSB0aGlzLm5vcm1hbE1hcC50b0pTT04oIG1ldGEgKS51dWlkO1xuXHRcdFx0XHRkYXRhLm5vcm1hbFNjYWxlID0gdGhpcy5ub3JtYWxTY2FsZS50b0FycmF5KCk7XG5cblx0XHRcdH1cblx0XHRcdGlmICggdGhpcy5kaXNwbGFjZW1lbnRNYXAgJiYgdGhpcy5kaXNwbGFjZW1lbnRNYXAuaXNUZXh0dXJlICkge1xuXG5cdFx0XHRcdGRhdGEuZGlzcGxhY2VtZW50TWFwID0gdGhpcy5kaXNwbGFjZW1lbnRNYXAudG9KU09OKCBtZXRhICkudXVpZDtcblx0XHRcdFx0ZGF0YS5kaXNwbGFjZW1lbnRTY2FsZSA9IHRoaXMuZGlzcGxhY2VtZW50U2NhbGU7XG5cdFx0XHRcdGRhdGEuZGlzcGxhY2VtZW50QmlhcyA9IHRoaXMuZGlzcGxhY2VtZW50QmlhcztcblxuXHRcdFx0fVxuXHRcdFx0aWYgKCB0aGlzLnJvdWdobmVzc01hcCAmJiB0aGlzLnJvdWdobmVzc01hcC5pc1RleHR1cmUgKSBkYXRhLnJvdWdobmVzc01hcCA9IHRoaXMucm91Z2huZXNzTWFwLnRvSlNPTiggbWV0YSApLnV1aWQ7XG5cdFx0XHRpZiAoIHRoaXMubWV0YWxuZXNzTWFwICYmIHRoaXMubWV0YWxuZXNzTWFwLmlzVGV4dHVyZSApIGRhdGEubWV0YWxuZXNzTWFwID0gdGhpcy5tZXRhbG5lc3NNYXAudG9KU09OKCBtZXRhICkudXVpZDtcblxuXHRcdFx0aWYgKCB0aGlzLmVtaXNzaXZlTWFwICYmIHRoaXMuZW1pc3NpdmVNYXAuaXNUZXh0dXJlICkgZGF0YS5lbWlzc2l2ZU1hcCA9IHRoaXMuZW1pc3NpdmVNYXAudG9KU09OKCBtZXRhICkudXVpZDtcblx0XHRcdGlmICggdGhpcy5zcGVjdWxhck1hcCAmJiB0aGlzLnNwZWN1bGFyTWFwLmlzVGV4dHVyZSApIGRhdGEuc3BlY3VsYXJNYXAgPSB0aGlzLnNwZWN1bGFyTWFwLnRvSlNPTiggbWV0YSApLnV1aWQ7XG5cblx0XHRcdGlmICggdGhpcy5lbnZNYXAgJiYgdGhpcy5lbnZNYXAuaXNUZXh0dXJlICkge1xuXG5cdFx0XHRcdGRhdGEuZW52TWFwID0gdGhpcy5lbnZNYXAudG9KU09OKCBtZXRhICkudXVpZDtcblx0XHRcdFx0ZGF0YS5yZWZsZWN0aXZpdHkgPSB0aGlzLnJlZmxlY3Rpdml0eTsgLy8gU2NhbGUgYmVoaW5kIGVudk1hcFxuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggdGhpcy5ncmFkaWVudE1hcCAmJiB0aGlzLmdyYWRpZW50TWFwLmlzVGV4dHVyZSApIHtcblxuXHRcdFx0XHRkYXRhLmdyYWRpZW50TWFwID0gdGhpcy5ncmFkaWVudE1hcC50b0pTT04oIG1ldGEgKS51dWlkO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggdGhpcy5zaXplICE9PSB1bmRlZmluZWQgKSBkYXRhLnNpemUgPSB0aGlzLnNpemU7XG5cdFx0XHRpZiAoIHRoaXMuc2l6ZUF0dGVudWF0aW9uICE9PSB1bmRlZmluZWQgKSBkYXRhLnNpemVBdHRlbnVhdGlvbiA9IHRoaXMuc2l6ZUF0dGVudWF0aW9uO1xuXG5cdFx0XHRpZiAoIHRoaXMuYmxlbmRpbmcgIT09IE5vcm1hbEJsZW5kaW5nICkgZGF0YS5ibGVuZGluZyA9IHRoaXMuYmxlbmRpbmc7XG5cdFx0XHRpZiAoIHRoaXMuc2hhZGluZyAhPT0gU21vb3RoU2hhZGluZyApIGRhdGEuc2hhZGluZyA9IHRoaXMuc2hhZGluZztcblx0XHRcdGlmICggdGhpcy5zaWRlICE9PSBGcm9udFNpZGUgKSBkYXRhLnNpZGUgPSB0aGlzLnNpZGU7XG5cdFx0XHRpZiAoIHRoaXMudmVydGV4Q29sb3JzICE9PSBOb0NvbG9ycyApIGRhdGEudmVydGV4Q29sb3JzID0gdGhpcy52ZXJ0ZXhDb2xvcnM7XG5cblx0XHRcdGlmICggdGhpcy5vcGFjaXR5IDwgMSApIGRhdGEub3BhY2l0eSA9IHRoaXMub3BhY2l0eTtcblx0XHRcdGlmICggdGhpcy50cmFuc3BhcmVudCA9PT0gdHJ1ZSApIGRhdGEudHJhbnNwYXJlbnQgPSB0aGlzLnRyYW5zcGFyZW50O1xuXG5cdFx0XHRkYXRhLmRlcHRoRnVuYyA9IHRoaXMuZGVwdGhGdW5jO1xuXHRcdFx0ZGF0YS5kZXB0aFRlc3QgPSB0aGlzLmRlcHRoVGVzdDtcblx0XHRcdGRhdGEuZGVwdGhXcml0ZSA9IHRoaXMuZGVwdGhXcml0ZTtcblxuXHRcdFx0aWYgKCB0aGlzLmFscGhhVGVzdCA+IDAgKSBkYXRhLmFscGhhVGVzdCA9IHRoaXMuYWxwaGFUZXN0O1xuXHRcdFx0aWYgKCB0aGlzLnByZW11bHRpcGxpZWRBbHBoYSA9PT0gdHJ1ZSApIGRhdGEucHJlbXVsdGlwbGllZEFscGhhID0gdGhpcy5wcmVtdWx0aXBsaWVkQWxwaGE7XG5cdFx0XHRpZiAoIHRoaXMud2lyZWZyYW1lID09PSB0cnVlICkgZGF0YS53aXJlZnJhbWUgPSB0aGlzLndpcmVmcmFtZTtcblx0XHRcdGlmICggdGhpcy53aXJlZnJhbWVMaW5ld2lkdGggPiAxICkgZGF0YS53aXJlZnJhbWVMaW5ld2lkdGggPSB0aGlzLndpcmVmcmFtZUxpbmV3aWR0aDtcblx0XHRcdGlmICggdGhpcy53aXJlZnJhbWVMaW5lY2FwICE9PSAncm91bmQnICkgZGF0YS53aXJlZnJhbWVMaW5lY2FwID0gdGhpcy53aXJlZnJhbWVMaW5lY2FwO1xuXHRcdFx0aWYgKCB0aGlzLndpcmVmcmFtZUxpbmVqb2luICE9PSAncm91bmQnICkgZGF0YS53aXJlZnJhbWVMaW5lam9pbiA9IHRoaXMud2lyZWZyYW1lTGluZWpvaW47XG5cblx0XHRcdGRhdGEuc2tpbm5pbmcgPSB0aGlzLnNraW5uaW5nO1xuXHRcdFx0ZGF0YS5tb3JwaFRhcmdldHMgPSB0aGlzLm1vcnBoVGFyZ2V0cztcblxuXHRcdFx0Ly8gVE9ETzogQ29waWVkIGZyb20gT2JqZWN0M0QudG9KU09OXG5cblx0XHRcdGZ1bmN0aW9uIGV4dHJhY3RGcm9tQ2FjaGUoIGNhY2hlICkge1xuXG5cdFx0XHRcdHZhciB2YWx1ZXMgPSBbXTtcblxuXHRcdFx0XHRmb3IgKCB2YXIga2V5IGluIGNhY2hlICkge1xuXG5cdFx0XHRcdFx0dmFyIGRhdGEgPSBjYWNoZVsga2V5IF07XG5cdFx0XHRcdFx0ZGVsZXRlIGRhdGEubWV0YWRhdGE7XG5cdFx0XHRcdFx0dmFsdWVzLnB1c2goIGRhdGEgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuIHZhbHVlcztcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIGlzUm9vdCApIHtcblxuXHRcdFx0XHR2YXIgdGV4dHVyZXMgPSBleHRyYWN0RnJvbUNhY2hlKCBtZXRhLnRleHR1cmVzICk7XG5cdFx0XHRcdHZhciBpbWFnZXMgPSBleHRyYWN0RnJvbUNhY2hlKCBtZXRhLmltYWdlcyApO1xuXG5cdFx0XHRcdGlmICggdGV4dHVyZXMubGVuZ3RoID4gMCApIGRhdGEudGV4dHVyZXMgPSB0ZXh0dXJlcztcblx0XHRcdFx0aWYgKCBpbWFnZXMubGVuZ3RoID4gMCApIGRhdGEuaW1hZ2VzID0gaW1hZ2VzO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBkYXRhO1xuXG5cdFx0fSxcblxuXHRcdGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiBuZXcgdGhpcy5jb25zdHJ1Y3RvcigpLmNvcHkoIHRoaXMgKTtcblxuXHRcdH0sXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIHNvdXJjZSApIHtcblxuXHRcdFx0dGhpcy5uYW1lID0gc291cmNlLm5hbWU7XG5cblx0XHRcdHRoaXMuZm9nID0gc291cmNlLmZvZztcblx0XHRcdHRoaXMubGlnaHRzID0gc291cmNlLmxpZ2h0cztcblxuXHRcdFx0dGhpcy5ibGVuZGluZyA9IHNvdXJjZS5ibGVuZGluZztcblx0XHRcdHRoaXMuc2lkZSA9IHNvdXJjZS5zaWRlO1xuXHRcdFx0dGhpcy5zaGFkaW5nID0gc291cmNlLnNoYWRpbmc7XG5cdFx0XHR0aGlzLnZlcnRleENvbG9ycyA9IHNvdXJjZS52ZXJ0ZXhDb2xvcnM7XG5cblx0XHRcdHRoaXMub3BhY2l0eSA9IHNvdXJjZS5vcGFjaXR5O1xuXHRcdFx0dGhpcy50cmFuc3BhcmVudCA9IHNvdXJjZS50cmFuc3BhcmVudDtcblxuXHRcdFx0dGhpcy5ibGVuZFNyYyA9IHNvdXJjZS5ibGVuZFNyYztcblx0XHRcdHRoaXMuYmxlbmREc3QgPSBzb3VyY2UuYmxlbmREc3Q7XG5cdFx0XHR0aGlzLmJsZW5kRXF1YXRpb24gPSBzb3VyY2UuYmxlbmRFcXVhdGlvbjtcblx0XHRcdHRoaXMuYmxlbmRTcmNBbHBoYSA9IHNvdXJjZS5ibGVuZFNyY0FscGhhO1xuXHRcdFx0dGhpcy5ibGVuZERzdEFscGhhID0gc291cmNlLmJsZW5kRHN0QWxwaGE7XG5cdFx0XHR0aGlzLmJsZW5kRXF1YXRpb25BbHBoYSA9IHNvdXJjZS5ibGVuZEVxdWF0aW9uQWxwaGE7XG5cblx0XHRcdHRoaXMuZGVwdGhGdW5jID0gc291cmNlLmRlcHRoRnVuYztcblx0XHRcdHRoaXMuZGVwdGhUZXN0ID0gc291cmNlLmRlcHRoVGVzdDtcblx0XHRcdHRoaXMuZGVwdGhXcml0ZSA9IHNvdXJjZS5kZXB0aFdyaXRlO1xuXG5cdFx0XHR0aGlzLmNvbG9yV3JpdGUgPSBzb3VyY2UuY29sb3JXcml0ZTtcblxuXHRcdFx0dGhpcy5wcmVjaXNpb24gPSBzb3VyY2UucHJlY2lzaW9uO1xuXG5cdFx0XHR0aGlzLnBvbHlnb25PZmZzZXQgPSBzb3VyY2UucG9seWdvbk9mZnNldDtcblx0XHRcdHRoaXMucG9seWdvbk9mZnNldEZhY3RvciA9IHNvdXJjZS5wb2x5Z29uT2Zmc2V0RmFjdG9yO1xuXHRcdFx0dGhpcy5wb2x5Z29uT2Zmc2V0VW5pdHMgPSBzb3VyY2UucG9seWdvbk9mZnNldFVuaXRzO1xuXG5cdFx0XHR0aGlzLmFscGhhVGVzdCA9IHNvdXJjZS5hbHBoYVRlc3Q7XG5cblx0XHRcdHRoaXMucHJlbXVsdGlwbGllZEFscGhhID0gc291cmNlLnByZW11bHRpcGxpZWRBbHBoYTtcblxuXHRcdFx0dGhpcy5vdmVyZHJhdyA9IHNvdXJjZS5vdmVyZHJhdztcblxuXHRcdFx0dGhpcy52aXNpYmxlID0gc291cmNlLnZpc2libGU7XG5cdFx0XHR0aGlzLmNsaXBTaGFkb3dzID0gc291cmNlLmNsaXBTaGFkb3dzO1xuXHRcdFx0dGhpcy5jbGlwSW50ZXJzZWN0aW9uID0gc291cmNlLmNsaXBJbnRlcnNlY3Rpb247XG5cblx0XHRcdHZhciBzcmNQbGFuZXMgPSBzb3VyY2UuY2xpcHBpbmdQbGFuZXMsXG5cdFx0XHRcdGRzdFBsYW5lcyA9IG51bGw7XG5cblx0XHRcdGlmICggc3JjUGxhbmVzICE9PSBudWxsICkge1xuXG5cdFx0XHRcdHZhciBuID0gc3JjUGxhbmVzLmxlbmd0aDtcblx0XHRcdFx0ZHN0UGxhbmVzID0gbmV3IEFycmF5KCBuICk7XG5cblx0XHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpICE9PSBuOyArKyBpIClcblx0XHRcdFx0XHRkc3RQbGFuZXNbIGkgXSA9IHNyY1BsYW5lc1sgaSBdLmNsb25lKCk7XG5cblx0XHRcdH1cblxuXHRcdFx0dGhpcy5jbGlwcGluZ1BsYW5lcyA9IGRzdFBsYW5lcztcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0dXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMuZGlzcGF0Y2hFdmVudCggeyB0eXBlOiAndXBkYXRlJyB9ICk7XG5cblx0XHR9LFxuXG5cdFx0ZGlzcG9zZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR0aGlzLmRpc3BhdGNoRXZlbnQoIHsgdHlwZTogJ2Rpc3Bvc2UnIH0gKTtcblxuXHRcdH1cblxuXHR9O1xuXG5cdE9iamVjdC5hc3NpZ24oIE1hdGVyaWFsLnByb3RvdHlwZSwgRXZlbnREaXNwYXRjaGVyLnByb3RvdHlwZSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKlxuXHQgKiBwYXJhbWV0ZXJzID0ge1xuXHQgKiAgZGVmaW5lczogeyBcImxhYmVsXCIgOiBcInZhbHVlXCIgfSxcblx0ICogIHVuaWZvcm1zOiB7IFwicGFyYW1ldGVyMVwiOiB7IHZhbHVlOiAxLjAgfSwgXCJwYXJhbWV0ZXIyXCI6IHsgdmFsdWUyOiAyIH0gfSxcblx0ICpcblx0ICogIGZyYWdtZW50U2hhZGVyOiA8c3RyaW5nPixcblx0ICogIHZlcnRleFNoYWRlcjogPHN0cmluZz4sXG5cdCAqXG5cdCAqICB3aXJlZnJhbWU6IDxib29sZWFuPixcblx0ICogIHdpcmVmcmFtZUxpbmV3aWR0aDogPGZsb2F0Pixcblx0ICpcblx0ICogIGxpZ2h0czogPGJvb2w+LFxuXHQgKlxuXHQgKiAgc2tpbm5pbmc6IDxib29sPixcblx0ICogIG1vcnBoVGFyZ2V0czogPGJvb2w+LFxuXHQgKiAgbW9ycGhOb3JtYWxzOiA8Ym9vbD5cblx0ICogfVxuXHQgKi9cblxuXHRmdW5jdGlvbiBTaGFkZXJNYXRlcmlhbCggcGFyYW1ldGVycyApIHtcblxuXHRcdE1hdGVyaWFsLmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdTaGFkZXJNYXRlcmlhbCc7XG5cblx0XHR0aGlzLmRlZmluZXMgPSB7fTtcblx0XHR0aGlzLnVuaWZvcm1zID0ge307XG5cblx0XHR0aGlzLnZlcnRleFNoYWRlciA9ICd2b2lkIG1haW4oKSB7XFxuXFx0Z2xfUG9zaXRpb24gPSBwcm9qZWN0aW9uTWF0cml4ICogbW9kZWxWaWV3TWF0cml4ICogdmVjNCggcG9zaXRpb24sIDEuMCApO1xcbn0nO1xuXHRcdHRoaXMuZnJhZ21lbnRTaGFkZXIgPSAndm9pZCBtYWluKCkge1xcblxcdGdsX0ZyYWdDb2xvciA9IHZlYzQoIDEuMCwgMC4wLCAwLjAsIDEuMCApO1xcbn0nO1xuXG5cdFx0dGhpcy5saW5ld2lkdGggPSAxO1xuXG5cdFx0dGhpcy53aXJlZnJhbWUgPSBmYWxzZTtcblx0XHR0aGlzLndpcmVmcmFtZUxpbmV3aWR0aCA9IDE7XG5cblx0XHR0aGlzLmZvZyA9IGZhbHNlOyAvLyBzZXQgdG8gdXNlIHNjZW5lIGZvZ1xuXHRcdHRoaXMubGlnaHRzID0gZmFsc2U7IC8vIHNldCB0byB1c2Ugc2NlbmUgbGlnaHRzXG5cdFx0dGhpcy5jbGlwcGluZyA9IGZhbHNlOyAvLyBzZXQgdG8gdXNlIHVzZXItZGVmaW5lZCBjbGlwcGluZyBwbGFuZXNcblxuXHRcdHRoaXMuc2tpbm5pbmcgPSBmYWxzZTsgLy8gc2V0IHRvIHVzZSBza2lubmluZyBhdHRyaWJ1dGUgc3RyZWFtc1xuXHRcdHRoaXMubW9ycGhUYXJnZXRzID0gZmFsc2U7IC8vIHNldCB0byB1c2UgbW9ycGggdGFyZ2V0c1xuXHRcdHRoaXMubW9ycGhOb3JtYWxzID0gZmFsc2U7IC8vIHNldCB0byB1c2UgbW9ycGggbm9ybWFsc1xuXG5cdFx0dGhpcy5leHRlbnNpb25zID0ge1xuXHRcdFx0ZGVyaXZhdGl2ZXM6IGZhbHNlLCAvLyBzZXQgdG8gdXNlIGRlcml2YXRpdmVzXG5cdFx0XHRmcmFnRGVwdGg6IGZhbHNlLCAvLyBzZXQgdG8gdXNlIGZyYWdtZW50IGRlcHRoIHZhbHVlc1xuXHRcdFx0ZHJhd0J1ZmZlcnM6IGZhbHNlLCAvLyBzZXQgdG8gdXNlIGRyYXcgYnVmZmVyc1xuXHRcdFx0c2hhZGVyVGV4dHVyZUxPRDogZmFsc2UgLy8gc2V0IHRvIHVzZSBzaGFkZXIgdGV4dHVyZSBMT0Rcblx0XHR9O1xuXG5cdFx0Ly8gV2hlbiByZW5kZXJlZCBnZW9tZXRyeSBkb2Vzbid0IGluY2x1ZGUgdGhlc2UgYXR0cmlidXRlcyBidXQgdGhlIG1hdGVyaWFsIGRvZXMsXG5cdFx0Ly8gdXNlIHRoZXNlIGRlZmF1bHQgdmFsdWVzIGluIFdlYkdMLiBUaGlzIGF2b2lkcyBlcnJvcnMgd2hlbiBidWZmZXIgZGF0YSBpcyBtaXNzaW5nLlxuXHRcdHRoaXMuZGVmYXVsdEF0dHJpYnV0ZVZhbHVlcyA9IHtcblx0XHRcdCdjb2xvcic6IFsgMSwgMSwgMSBdLFxuXHRcdFx0J3V2JzogWyAwLCAwIF0sXG5cdFx0XHQndXYyJzogWyAwLCAwIF1cblx0XHR9O1xuXG5cdFx0dGhpcy5pbmRleDBBdHRyaWJ1dGVOYW1lID0gdW5kZWZpbmVkO1xuXG5cdFx0aWYgKCBwYXJhbWV0ZXJzICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdGlmICggcGFyYW1ldGVycy5hdHRyaWJ1dGVzICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0Y29uc29sZS5lcnJvciggJ1RIUkVFLlNoYWRlck1hdGVyaWFsOiBhdHRyaWJ1dGVzIHNob3VsZCBub3cgYmUgZGVmaW5lZCBpbiBUSFJFRS5CdWZmZXJHZW9tZXRyeSBpbnN0ZWFkLicgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLnNldFZhbHVlcyggcGFyYW1ldGVycyApO1xuXG5cdFx0fVxuXG5cdH1cblxuXHRTaGFkZXJNYXRlcmlhbC5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBNYXRlcmlhbC5wcm90b3R5cGUgKTtcblx0U2hhZGVyTWF0ZXJpYWwucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gU2hhZGVyTWF0ZXJpYWw7XG5cblx0U2hhZGVyTWF0ZXJpYWwucHJvdG90eXBlLmlzU2hhZGVyTWF0ZXJpYWwgPSB0cnVlO1xuXG5cdFNoYWRlck1hdGVyaWFsLnByb3RvdHlwZS5jb3B5ID0gZnVuY3Rpb24gKCBzb3VyY2UgKSB7XG5cblx0XHRNYXRlcmlhbC5wcm90b3R5cGUuY29weS5jYWxsKCB0aGlzLCBzb3VyY2UgKTtcblxuXHRcdHRoaXMuZnJhZ21lbnRTaGFkZXIgPSBzb3VyY2UuZnJhZ21lbnRTaGFkZXI7XG5cdFx0dGhpcy52ZXJ0ZXhTaGFkZXIgPSBzb3VyY2UudmVydGV4U2hhZGVyO1xuXG5cdFx0dGhpcy51bmlmb3JtcyA9IFVuaWZvcm1zVXRpbHMuY2xvbmUoIHNvdXJjZS51bmlmb3JtcyApO1xuXG5cdFx0dGhpcy5kZWZpbmVzID0gc291cmNlLmRlZmluZXM7XG5cblx0XHR0aGlzLndpcmVmcmFtZSA9IHNvdXJjZS53aXJlZnJhbWU7XG5cdFx0dGhpcy53aXJlZnJhbWVMaW5ld2lkdGggPSBzb3VyY2Uud2lyZWZyYW1lTGluZXdpZHRoO1xuXG5cdFx0dGhpcy5saWdodHMgPSBzb3VyY2UubGlnaHRzO1xuXHRcdHRoaXMuY2xpcHBpbmcgPSBzb3VyY2UuY2xpcHBpbmc7XG5cblx0XHR0aGlzLnNraW5uaW5nID0gc291cmNlLnNraW5uaW5nO1xuXG5cdFx0dGhpcy5tb3JwaFRhcmdldHMgPSBzb3VyY2UubW9ycGhUYXJnZXRzO1xuXHRcdHRoaXMubW9ycGhOb3JtYWxzID0gc291cmNlLm1vcnBoTm9ybWFscztcblxuXHRcdHRoaXMuZXh0ZW5zaW9ucyA9IHNvdXJjZS5leHRlbnNpb25zO1xuXG5cdFx0cmV0dXJuIHRoaXM7XG5cblx0fTtcblxuXHRTaGFkZXJNYXRlcmlhbC5wcm90b3R5cGUudG9KU09OID0gZnVuY3Rpb24gKCBtZXRhICkge1xuXG5cdFx0dmFyIGRhdGEgPSBNYXRlcmlhbC5wcm90b3R5cGUudG9KU09OLmNhbGwoIHRoaXMsIG1ldGEgKTtcblxuXHRcdGRhdGEudW5pZm9ybXMgPSB0aGlzLnVuaWZvcm1zO1xuXHRcdGRhdGEudmVydGV4U2hhZGVyID0gdGhpcy52ZXJ0ZXhTaGFkZXI7XG5cdFx0ZGF0YS5mcmFnbWVudFNoYWRlciA9IHRoaXMuZnJhZ21lbnRTaGFkZXI7XG5cblx0XHRyZXR1cm4gZGF0YTtcblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKiBAYXV0aG9yIGJob3VzdG9uIC8gaHR0cHM6Ly9jbGFyYS5pb1xuXHQgKiBAYXV0aG9yIFdlc3RMYW5nbGV5IC8gaHR0cDovL2dpdGh1Yi5jb20vV2VzdExhbmdsZXlcblx0ICpcblx0ICogcGFyYW1ldGVycyA9IHtcblx0ICpcblx0ICogIG9wYWNpdHk6IDxmbG9hdD4sXG5cdCAqXG5cdCAqICBtYXA6IG5ldyBUSFJFRS5UZXh0dXJlKCA8SW1hZ2U+ICksXG5cdCAqXG5cdCAqICBhbHBoYU1hcDogbmV3IFRIUkVFLlRleHR1cmUoIDxJbWFnZT4gKSxcblx0ICpcblx0ICogIGRpc3BsYWNlbWVudE1hcDogbmV3IFRIUkVFLlRleHR1cmUoIDxJbWFnZT4gKSxcblx0ICogIGRpc3BsYWNlbWVudFNjYWxlOiA8ZmxvYXQ+LFxuXHQgKiAgZGlzcGxhY2VtZW50QmlhczogPGZsb2F0Pixcblx0ICpcblx0ICogIHdpcmVmcmFtZTogPGJvb2xlYW4+LFxuXHQgKiAgd2lyZWZyYW1lTGluZXdpZHRoOiA8ZmxvYXQ+XG5cdCAqIH1cblx0ICovXG5cblx0ZnVuY3Rpb24gTWVzaERlcHRoTWF0ZXJpYWwoIHBhcmFtZXRlcnMgKSB7XG5cblx0XHRNYXRlcmlhbC5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnTWVzaERlcHRoTWF0ZXJpYWwnO1xuXG5cdFx0dGhpcy5kZXB0aFBhY2tpbmcgPSBCYXNpY0RlcHRoUGFja2luZztcblxuXHRcdHRoaXMuc2tpbm5pbmcgPSBmYWxzZTtcblx0XHR0aGlzLm1vcnBoVGFyZ2V0cyA9IGZhbHNlO1xuXG5cdFx0dGhpcy5tYXAgPSBudWxsO1xuXG5cdFx0dGhpcy5hbHBoYU1hcCA9IG51bGw7XG5cblx0XHR0aGlzLmRpc3BsYWNlbWVudE1hcCA9IG51bGw7XG5cdFx0dGhpcy5kaXNwbGFjZW1lbnRTY2FsZSA9IDE7XG5cdFx0dGhpcy5kaXNwbGFjZW1lbnRCaWFzID0gMDtcblxuXHRcdHRoaXMud2lyZWZyYW1lID0gZmFsc2U7XG5cdFx0dGhpcy53aXJlZnJhbWVMaW5ld2lkdGggPSAxO1xuXG5cdFx0dGhpcy5mb2cgPSBmYWxzZTtcblx0XHR0aGlzLmxpZ2h0cyA9IGZhbHNlO1xuXG5cdFx0dGhpcy5zZXRWYWx1ZXMoIHBhcmFtZXRlcnMgKTtcblxuXHR9XG5cblx0TWVzaERlcHRoTWF0ZXJpYWwucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggTWF0ZXJpYWwucHJvdG90eXBlICk7XG5cdE1lc2hEZXB0aE1hdGVyaWFsLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IE1lc2hEZXB0aE1hdGVyaWFsO1xuXG5cdE1lc2hEZXB0aE1hdGVyaWFsLnByb3RvdHlwZS5pc01lc2hEZXB0aE1hdGVyaWFsID0gdHJ1ZTtcblxuXHRNZXNoRGVwdGhNYXRlcmlhbC5wcm90b3R5cGUuY29weSA9IGZ1bmN0aW9uICggc291cmNlICkge1xuXG5cdFx0TWF0ZXJpYWwucHJvdG90eXBlLmNvcHkuY2FsbCggdGhpcywgc291cmNlICk7XG5cblx0XHR0aGlzLmRlcHRoUGFja2luZyA9IHNvdXJjZS5kZXB0aFBhY2tpbmc7XG5cblx0XHR0aGlzLnNraW5uaW5nID0gc291cmNlLnNraW5uaW5nO1xuXHRcdHRoaXMubW9ycGhUYXJnZXRzID0gc291cmNlLm1vcnBoVGFyZ2V0cztcblxuXHRcdHRoaXMubWFwID0gc291cmNlLm1hcDtcblxuXHRcdHRoaXMuYWxwaGFNYXAgPSBzb3VyY2UuYWxwaGFNYXA7XG5cblx0XHR0aGlzLmRpc3BsYWNlbWVudE1hcCA9IHNvdXJjZS5kaXNwbGFjZW1lbnRNYXA7XG5cdFx0dGhpcy5kaXNwbGFjZW1lbnRTY2FsZSA9IHNvdXJjZS5kaXNwbGFjZW1lbnRTY2FsZTtcblx0XHR0aGlzLmRpc3BsYWNlbWVudEJpYXMgPSBzb3VyY2UuZGlzcGxhY2VtZW50QmlhcztcblxuXHRcdHRoaXMud2lyZWZyYW1lID0gc291cmNlLndpcmVmcmFtZTtcblx0XHR0aGlzLndpcmVmcmFtZUxpbmV3aWR0aCA9IHNvdXJjZS53aXJlZnJhbWVMaW5ld2lkdGg7XG5cblx0XHRyZXR1cm4gdGhpcztcblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGJob3VzdG9uIC8gaHR0cDovL2NsYXJhLmlvXG5cdCAqIEBhdXRob3IgV2VzdExhbmdsZXkgLyBodHRwOi8vZ2l0aHViLmNvbS9XZXN0TGFuZ2xleVxuXHQgKi9cblxuXHRmdW5jdGlvbiBCb3gzKCBtaW4sIG1heCApIHtcblxuXHRcdHRoaXMubWluID0gKCBtaW4gIT09IHVuZGVmaW5lZCApID8gbWluIDogbmV3IFZlY3RvcjMoICsgSW5maW5pdHksICsgSW5maW5pdHksICsgSW5maW5pdHkgKTtcblx0XHR0aGlzLm1heCA9ICggbWF4ICE9PSB1bmRlZmluZWQgKSA/IG1heCA6IG5ldyBWZWN0b3IzKCAtIEluZmluaXR5LCAtIEluZmluaXR5LCAtIEluZmluaXR5ICk7XG5cblx0fVxuXG5cdEJveDMucHJvdG90eXBlID0ge1xuXG5cdFx0Y29uc3RydWN0b3I6IEJveDMsXG5cblx0XHRpc0JveDM6IHRydWUsXG5cblx0XHRzZXQ6IGZ1bmN0aW9uICggbWluLCBtYXggKSB7XG5cblx0XHRcdHRoaXMubWluLmNvcHkoIG1pbiApO1xuXHRcdFx0dGhpcy5tYXguY29weSggbWF4ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldEZyb21BcnJheTogZnVuY3Rpb24gKCBhcnJheSApIHtcblxuXHRcdFx0dmFyIG1pblggPSArIEluZmluaXR5O1xuXHRcdFx0dmFyIG1pblkgPSArIEluZmluaXR5O1xuXHRcdFx0dmFyIG1pblogPSArIEluZmluaXR5O1xuXG5cdFx0XHR2YXIgbWF4WCA9IC0gSW5maW5pdHk7XG5cdFx0XHR2YXIgbWF4WSA9IC0gSW5maW5pdHk7XG5cdFx0XHR2YXIgbWF4WiA9IC0gSW5maW5pdHk7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGFycmF5Lmxlbmd0aDsgaSA8IGw7IGkgKz0gMyApIHtcblxuXHRcdFx0XHR2YXIgeCA9IGFycmF5WyBpIF07XG5cdFx0XHRcdHZhciB5ID0gYXJyYXlbIGkgKyAxIF07XG5cdFx0XHRcdHZhciB6ID0gYXJyYXlbIGkgKyAyIF07XG5cblx0XHRcdFx0aWYgKCB4IDwgbWluWCApIG1pblggPSB4O1xuXHRcdFx0XHRpZiAoIHkgPCBtaW5ZICkgbWluWSA9IHk7XG5cdFx0XHRcdGlmICggeiA8IG1pblogKSBtaW5aID0gejtcblxuXHRcdFx0XHRpZiAoIHggPiBtYXhYICkgbWF4WCA9IHg7XG5cdFx0XHRcdGlmICggeSA+IG1heFkgKSBtYXhZID0geTtcblx0XHRcdFx0aWYgKCB6ID4gbWF4WiApIG1heFogPSB6O1xuXG5cdFx0XHR9XG5cblx0XHRcdHRoaXMubWluLnNldCggbWluWCwgbWluWSwgbWluWiApO1xuXHRcdFx0dGhpcy5tYXguc2V0KCBtYXhYLCBtYXhZLCBtYXhaICk7XG5cblx0XHR9LFxuXG5cdFx0c2V0RnJvbUJ1ZmZlckF0dHJpYnV0ZTogZnVuY3Rpb24gKCBhdHRyaWJ1dGUgKSB7XG5cblx0XHRcdHZhciBtaW5YID0gKyBJbmZpbml0eTtcblx0XHRcdHZhciBtaW5ZID0gKyBJbmZpbml0eTtcblx0XHRcdHZhciBtaW5aID0gKyBJbmZpbml0eTtcblxuXHRcdFx0dmFyIG1heFggPSAtIEluZmluaXR5O1xuXHRcdFx0dmFyIG1heFkgPSAtIEluZmluaXR5O1xuXHRcdFx0dmFyIG1heFogPSAtIEluZmluaXR5O1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBhdHRyaWJ1dGUuY291bnQ7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdHZhciB4ID0gYXR0cmlidXRlLmdldFgoIGkgKTtcblx0XHRcdFx0dmFyIHkgPSBhdHRyaWJ1dGUuZ2V0WSggaSApO1xuXHRcdFx0XHR2YXIgeiA9IGF0dHJpYnV0ZS5nZXRaKCBpICk7XG5cblx0XHRcdFx0aWYgKCB4IDwgbWluWCApIG1pblggPSB4O1xuXHRcdFx0XHRpZiAoIHkgPCBtaW5ZICkgbWluWSA9IHk7XG5cdFx0XHRcdGlmICggeiA8IG1pblogKSBtaW5aID0gejtcblxuXHRcdFx0XHRpZiAoIHggPiBtYXhYICkgbWF4WCA9IHg7XG5cdFx0XHRcdGlmICggeSA+IG1heFkgKSBtYXhZID0geTtcblx0XHRcdFx0aWYgKCB6ID4gbWF4WiApIG1heFogPSB6O1xuXG5cdFx0XHR9XG5cblx0XHRcdHRoaXMubWluLnNldCggbWluWCwgbWluWSwgbWluWiApO1xuXHRcdFx0dGhpcy5tYXguc2V0KCBtYXhYLCBtYXhZLCBtYXhaICk7XG5cblx0XHR9LFxuXG5cdFx0c2V0RnJvbVBvaW50czogZnVuY3Rpb24gKCBwb2ludHMgKSB7XG5cblx0XHRcdHRoaXMubWFrZUVtcHR5KCk7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgaWwgPSBwb2ludHMubGVuZ3RoOyBpIDwgaWw7IGkgKysgKSB7XG5cblx0XHRcdFx0dGhpcy5leHBhbmRCeVBvaW50KCBwb2ludHNbIGkgXSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldEZyb21DZW50ZXJBbmRTaXplOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB2MSA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBzZXRGcm9tQ2VudGVyQW5kU2l6ZSggY2VudGVyLCBzaXplICkge1xuXG5cdFx0XHRcdHZhciBoYWxmU2l6ZSA9IHYxLmNvcHkoIHNpemUgKS5tdWx0aXBseVNjYWxhciggMC41ICk7XG5cblx0XHRcdFx0dGhpcy5taW4uY29weSggY2VudGVyICkuc3ViKCBoYWxmU2l6ZSApO1xuXHRcdFx0XHR0aGlzLm1heC5jb3B5KCBjZW50ZXIgKS5hZGQoIGhhbGZTaXplICk7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRzZXRGcm9tT2JqZWN0OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdC8vIENvbXB1dGVzIHRoZSB3b3JsZC1heGlzLWFsaWduZWQgYm91bmRpbmcgYm94IG9mIGFuIG9iamVjdCAoaW5jbHVkaW5nIGl0cyBjaGlsZHJlbiksXG5cdFx0XHQvLyBhY2NvdW50aW5nIGZvciBib3RoIHRoZSBvYmplY3QncywgYW5kIGNoaWxkcmVuJ3MsIHdvcmxkIHRyYW5zZm9ybXNcblxuXHRcdFx0dmFyIHYxID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIHNldEZyb21PYmplY3QoIG9iamVjdCApIHtcblxuXHRcdFx0XHR2YXIgc2NvcGUgPSB0aGlzO1xuXG5cdFx0XHRcdG9iamVjdC51cGRhdGVNYXRyaXhXb3JsZCggdHJ1ZSApO1xuXG5cdFx0XHRcdHRoaXMubWFrZUVtcHR5KCk7XG5cblx0XHRcdFx0b2JqZWN0LnRyYXZlcnNlKCBmdW5jdGlvbiAoIG5vZGUgKSB7XG5cblx0XHRcdFx0XHR2YXIgaSwgbDtcblxuXHRcdFx0XHRcdHZhciBnZW9tZXRyeSA9IG5vZGUuZ2VvbWV0cnk7XG5cblx0XHRcdFx0XHRpZiAoIGdlb21ldHJ5ICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRcdGlmICggZ2VvbWV0cnkuaXNHZW9tZXRyeSApIHtcblxuXHRcdFx0XHRcdFx0XHR2YXIgdmVydGljZXMgPSBnZW9tZXRyeS52ZXJ0aWNlcztcblxuXHRcdFx0XHRcdFx0XHRmb3IgKCBpID0gMCwgbCA9IHZlcnRpY2VzLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHR2MS5jb3B5KCB2ZXJ0aWNlc1sgaSBdICk7XG5cdFx0XHRcdFx0XHRcdFx0djEuYXBwbHlNYXRyaXg0KCBub2RlLm1hdHJpeFdvcmxkICk7XG5cblx0XHRcdFx0XHRcdFx0XHRzY29wZS5leHBhbmRCeVBvaW50KCB2MSApO1xuXG5cdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0fSBlbHNlIGlmICggZ2VvbWV0cnkuaXNCdWZmZXJHZW9tZXRyeSApIHtcblxuXHRcdFx0XHRcdFx0XHR2YXIgYXR0cmlidXRlID0gZ2VvbWV0cnkuYXR0cmlidXRlcy5wb3NpdGlvbjtcblxuXHRcdFx0XHRcdFx0XHRpZiAoIGF0dHJpYnV0ZSAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0Zm9yICggaSA9IDAsIGwgPSBhdHRyaWJ1dGUuY291bnQ7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0XHR2MS5mcm9tQXR0cmlidXRlKCBhdHRyaWJ1dGUsIGkgKS5hcHBseU1hdHJpeDQoIG5vZGUubWF0cml4V29ybGQgKTtcblxuXHRcdFx0XHRcdFx0XHRcdFx0c2NvcGUuZXhwYW5kQnlQb2ludCggdjEgKTtcblxuXHRcdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9ICk7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRjbG9uZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IHRoaXMuY29uc3RydWN0b3IoKS5jb3B5KCB0aGlzICk7XG5cblx0XHR9LFxuXG5cdFx0Y29weTogZnVuY3Rpb24gKCBib3ggKSB7XG5cblx0XHRcdHRoaXMubWluLmNvcHkoIGJveC5taW4gKTtcblx0XHRcdHRoaXMubWF4LmNvcHkoIGJveC5tYXggKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0bWFrZUVtcHR5OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMubWluLnggPSB0aGlzLm1pbi55ID0gdGhpcy5taW4ueiA9ICsgSW5maW5pdHk7XG5cdFx0XHR0aGlzLm1heC54ID0gdGhpcy5tYXgueSA9IHRoaXMubWF4LnogPSAtIEluZmluaXR5O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRpc0VtcHR5OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdC8vIHRoaXMgaXMgYSBtb3JlIHJvYnVzdCBjaGVjayBmb3IgZW1wdHkgdGhhbiAoIHZvbHVtZSA8PSAwICkgYmVjYXVzZSB2b2x1bWUgY2FuIGdldCBwb3NpdGl2ZSB3aXRoIHR3byBuZWdhdGl2ZSBheGVzXG5cblx0XHRcdHJldHVybiAoIHRoaXMubWF4LnggPCB0aGlzLm1pbi54ICkgfHwgKCB0aGlzLm1heC55IDwgdGhpcy5taW4ueSApIHx8ICggdGhpcy5tYXgueiA8IHRoaXMubWluLnogKTtcblxuXHRcdH0sXG5cblx0XHRnZXRDZW50ZXI6IGZ1bmN0aW9uICggb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdHZhciByZXN1bHQgPSBvcHRpb25hbFRhcmdldCB8fCBuZXcgVmVjdG9yMygpO1xuXHRcdFx0cmV0dXJuIHRoaXMuaXNFbXB0eSgpID8gcmVzdWx0LnNldCggMCwgMCwgMCApIDogcmVzdWx0LmFkZFZlY3RvcnMoIHRoaXMubWluLCB0aGlzLm1heCApLm11bHRpcGx5U2NhbGFyKCAwLjUgKTtcblxuXHRcdH0sXG5cblx0XHRnZXRTaXplOiBmdW5jdGlvbiAoIG9wdGlvbmFsVGFyZ2V0ICkge1xuXG5cdFx0XHR2YXIgcmVzdWx0ID0gb3B0aW9uYWxUYXJnZXQgfHwgbmV3IFZlY3RvcjMoKTtcblx0XHRcdHJldHVybiB0aGlzLmlzRW1wdHkoKSA/IHJlc3VsdC5zZXQoIDAsIDAsIDAgKSA6IHJlc3VsdC5zdWJWZWN0b3JzKCB0aGlzLm1heCwgdGhpcy5taW4gKTtcblxuXHRcdH0sXG5cblx0XHRleHBhbmRCeVBvaW50OiBmdW5jdGlvbiAoIHBvaW50ICkge1xuXG5cdFx0XHR0aGlzLm1pbi5taW4oIHBvaW50ICk7XG5cdFx0XHR0aGlzLm1heC5tYXgoIHBvaW50ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGV4cGFuZEJ5VmVjdG9yOiBmdW5jdGlvbiAoIHZlY3RvciApIHtcblxuXHRcdFx0dGhpcy5taW4uc3ViKCB2ZWN0b3IgKTtcblx0XHRcdHRoaXMubWF4LmFkZCggdmVjdG9yICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGV4cGFuZEJ5U2NhbGFyOiBmdW5jdGlvbiAoIHNjYWxhciApIHtcblxuXHRcdFx0dGhpcy5taW4uYWRkU2NhbGFyKCAtIHNjYWxhciApO1xuXHRcdFx0dGhpcy5tYXguYWRkU2NhbGFyKCBzY2FsYXIgKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Y29udGFpbnNQb2ludDogZnVuY3Rpb24gKCBwb2ludCApIHtcblxuXHRcdFx0cmV0dXJuIHBvaW50LnggPCB0aGlzLm1pbi54IHx8IHBvaW50LnggPiB0aGlzLm1heC54IHx8XG5cdFx0XHRcdHBvaW50LnkgPCB0aGlzLm1pbi55IHx8IHBvaW50LnkgPiB0aGlzLm1heC55IHx8XG5cdFx0XHRcdHBvaW50LnogPCB0aGlzLm1pbi56IHx8IHBvaW50LnogPiB0aGlzLm1heC56ID8gZmFsc2UgOiB0cnVlO1xuXG5cdFx0fSxcblxuXHRcdGNvbnRhaW5zQm94OiBmdW5jdGlvbiAoIGJveCApIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMubWluLnggPD0gYm94Lm1pbi54ICYmIGJveC5tYXgueCA8PSB0aGlzLm1heC54ICYmXG5cdFx0XHRcdHRoaXMubWluLnkgPD0gYm94Lm1pbi55ICYmIGJveC5tYXgueSA8PSB0aGlzLm1heC55ICYmXG5cdFx0XHRcdHRoaXMubWluLnogPD0gYm94Lm1pbi56ICYmIGJveC5tYXgueiA8PSB0aGlzLm1heC56O1xuXG5cdFx0fSxcblxuXHRcdGdldFBhcmFtZXRlcjogZnVuY3Rpb24gKCBwb2ludCwgb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdC8vIFRoaXMgY2FuIHBvdGVudGlhbGx5IGhhdmUgYSBkaXZpZGUgYnkgemVybyBpZiB0aGUgYm94XG5cdFx0XHQvLyBoYXMgYSBzaXplIGRpbWVuc2lvbiBvZiAwLlxuXG5cdFx0XHR2YXIgcmVzdWx0ID0gb3B0aW9uYWxUYXJnZXQgfHwgbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0cmV0dXJuIHJlc3VsdC5zZXQoXG5cdFx0XHRcdCggcG9pbnQueCAtIHRoaXMubWluLnggKSAvICggdGhpcy5tYXgueCAtIHRoaXMubWluLnggKSxcblx0XHRcdFx0KCBwb2ludC55IC0gdGhpcy5taW4ueSApIC8gKCB0aGlzLm1heC55IC0gdGhpcy5taW4ueSApLFxuXHRcdFx0XHQoIHBvaW50LnogLSB0aGlzLm1pbi56ICkgLyAoIHRoaXMubWF4LnogLSB0aGlzLm1pbi56IClcblx0XHRcdCk7XG5cblx0XHR9LFxuXG5cdFx0aW50ZXJzZWN0c0JveDogZnVuY3Rpb24gKCBib3ggKSB7XG5cblx0XHRcdC8vIHVzaW5nIDYgc3BsaXR0aW5nIHBsYW5lcyB0byBydWxlIG91dCBpbnRlcnNlY3Rpb25zLlxuXHRcdFx0cmV0dXJuIGJveC5tYXgueCA8IHRoaXMubWluLnggfHwgYm94Lm1pbi54ID4gdGhpcy5tYXgueCB8fFxuXHRcdFx0XHRib3gubWF4LnkgPCB0aGlzLm1pbi55IHx8IGJveC5taW4ueSA+IHRoaXMubWF4LnkgfHxcblx0XHRcdFx0Ym94Lm1heC56IDwgdGhpcy5taW4ueiB8fCBib3gubWluLnogPiB0aGlzLm1heC56ID8gZmFsc2UgOiB0cnVlO1xuXG5cdFx0fSxcblxuXHRcdGludGVyc2VjdHNTcGhlcmU6ICggZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgY2xvc2VzdFBvaW50O1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gaW50ZXJzZWN0c1NwaGVyZSggc3BoZXJlICkge1xuXG5cdFx0XHRcdGlmICggY2xvc2VzdFBvaW50ID09PSB1bmRlZmluZWQgKSBjbG9zZXN0UG9pbnQgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRcdC8vIEZpbmQgdGhlIHBvaW50IG9uIHRoZSBBQUJCIGNsb3Nlc3QgdG8gdGhlIHNwaGVyZSBjZW50ZXIuXG5cdFx0XHRcdHRoaXMuY2xhbXBQb2ludCggc3BoZXJlLmNlbnRlciwgY2xvc2VzdFBvaW50ICk7XG5cblx0XHRcdFx0Ly8gSWYgdGhhdCBwb2ludCBpcyBpbnNpZGUgdGhlIHNwaGVyZSwgdGhlIEFBQkIgYW5kIHNwaGVyZSBpbnRlcnNlY3QuXG5cdFx0XHRcdHJldHVybiBjbG9zZXN0UG9pbnQuZGlzdGFuY2VUb1NxdWFyZWQoIHNwaGVyZS5jZW50ZXIgKSA8PSAoIHNwaGVyZS5yYWRpdXMgKiBzcGhlcmUucmFkaXVzICk7XG5cblx0XHRcdH07XG5cblx0XHR9ICkoKSxcblxuXHRcdGludGVyc2VjdHNQbGFuZTogZnVuY3Rpb24gKCBwbGFuZSApIHtcblxuXHRcdFx0Ly8gV2UgY29tcHV0ZSB0aGUgbWluaW11bSBhbmQgbWF4aW11bSBkb3QgcHJvZHVjdCB2YWx1ZXMuIElmIHRob3NlIHZhbHVlc1xuXHRcdFx0Ly8gYXJlIG9uIHRoZSBzYW1lIHNpZGUgKGJhY2sgb3IgZnJvbnQpIG9mIHRoZSBwbGFuZSwgdGhlbiB0aGVyZSBpcyBubyBpbnRlcnNlY3Rpb24uXG5cblx0XHRcdHZhciBtaW4sIG1heDtcblxuXHRcdFx0aWYgKCBwbGFuZS5ub3JtYWwueCA+IDAgKSB7XG5cblx0XHRcdFx0bWluID0gcGxhbmUubm9ybWFsLnggKiB0aGlzLm1pbi54O1xuXHRcdFx0XHRtYXggPSBwbGFuZS5ub3JtYWwueCAqIHRoaXMubWF4Lng7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0bWluID0gcGxhbmUubm9ybWFsLnggKiB0aGlzLm1heC54O1xuXHRcdFx0XHRtYXggPSBwbGFuZS5ub3JtYWwueCAqIHRoaXMubWluLng7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBwbGFuZS5ub3JtYWwueSA+IDAgKSB7XG5cblx0XHRcdFx0bWluICs9IHBsYW5lLm5vcm1hbC55ICogdGhpcy5taW4ueTtcblx0XHRcdFx0bWF4ICs9IHBsYW5lLm5vcm1hbC55ICogdGhpcy5tYXgueTtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRtaW4gKz0gcGxhbmUubm9ybWFsLnkgKiB0aGlzLm1heC55O1xuXHRcdFx0XHRtYXggKz0gcGxhbmUubm9ybWFsLnkgKiB0aGlzLm1pbi55O1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggcGxhbmUubm9ybWFsLnogPiAwICkge1xuXG5cdFx0XHRcdG1pbiArPSBwbGFuZS5ub3JtYWwueiAqIHRoaXMubWluLno7XG5cdFx0XHRcdG1heCArPSBwbGFuZS5ub3JtYWwueiAqIHRoaXMubWF4Lno7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0bWluICs9IHBsYW5lLm5vcm1hbC56ICogdGhpcy5tYXguejtcblx0XHRcdFx0bWF4ICs9IHBsYW5lLm5vcm1hbC56ICogdGhpcy5taW4uejtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gKCBtaW4gPD0gcGxhbmUuY29uc3RhbnQgJiYgbWF4ID49IHBsYW5lLmNvbnN0YW50ICk7XG5cblx0XHR9LFxuXG5cdFx0Y2xhbXBQb2ludDogZnVuY3Rpb24gKCBwb2ludCwgb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdHZhciByZXN1bHQgPSBvcHRpb25hbFRhcmdldCB8fCBuZXcgVmVjdG9yMygpO1xuXHRcdFx0cmV0dXJuIHJlc3VsdC5jb3B5KCBwb2ludCApLmNsYW1wKCB0aGlzLm1pbiwgdGhpcy5tYXggKTtcblxuXHRcdH0sXG5cblx0XHRkaXN0YW5jZVRvUG9pbnQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIHYxID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIGRpc3RhbmNlVG9Qb2ludCggcG9pbnQgKSB7XG5cblx0XHRcdFx0dmFyIGNsYW1wZWRQb2ludCA9IHYxLmNvcHkoIHBvaW50ICkuY2xhbXAoIHRoaXMubWluLCB0aGlzLm1heCApO1xuXHRcdFx0XHRyZXR1cm4gY2xhbXBlZFBvaW50LnN1YiggcG9pbnQgKS5sZW5ndGgoKTtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdGdldEJvdW5kaW5nU3BoZXJlOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB2MSA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBnZXRCb3VuZGluZ1NwaGVyZSggb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdFx0dmFyIHJlc3VsdCA9IG9wdGlvbmFsVGFyZ2V0IHx8IG5ldyBTcGhlcmUoKTtcblxuXHRcdFx0XHR0aGlzLmdldENlbnRlciggcmVzdWx0LmNlbnRlciApO1xuXG5cdFx0XHRcdHJlc3VsdC5yYWRpdXMgPSB0aGlzLmdldFNpemUoIHYxICkubGVuZ3RoKCkgKiAwLjU7XG5cblx0XHRcdFx0cmV0dXJuIHJlc3VsdDtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdGludGVyc2VjdDogZnVuY3Rpb24gKCBib3ggKSB7XG5cblx0XHRcdHRoaXMubWluLm1heCggYm94Lm1pbiApO1xuXHRcdFx0dGhpcy5tYXgubWluKCBib3gubWF4ICk7XG5cblx0XHRcdC8vIGVuc3VyZSB0aGF0IGlmIHRoZXJlIGlzIG5vIG92ZXJsYXAsIHRoZSByZXN1bHQgaXMgZnVsbHkgZW1wdHksIG5vdCBzbGlnaHRseSBlbXB0eSB3aXRoIG5vbi1pbmYvK2luZiB2YWx1ZXMgdGhhdCB3aWxsIGNhdXNlIHN1YnNlcXVlbmNlIGludGVyc2VjdHMgdG8gZXJyb25lb3VzbHkgcmV0dXJuIHZhbGlkIHZhbHVlcy5cblx0XHRcdGlmKCB0aGlzLmlzRW1wdHkoKSApIHRoaXMubWFrZUVtcHR5KCk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHVuaW9uOiBmdW5jdGlvbiAoIGJveCApIHtcblxuXHRcdFx0dGhpcy5taW4ubWluKCBib3gubWluICk7XG5cdFx0XHR0aGlzLm1heC5tYXgoIGJveC5tYXggKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0YXBwbHlNYXRyaXg0OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBwb2ludHMgPSBbXG5cdFx0XHRcdG5ldyBWZWN0b3IzKCksXG5cdFx0XHRcdG5ldyBWZWN0b3IzKCksXG5cdFx0XHRcdG5ldyBWZWN0b3IzKCksXG5cdFx0XHRcdG5ldyBWZWN0b3IzKCksXG5cdFx0XHRcdG5ldyBWZWN0b3IzKCksXG5cdFx0XHRcdG5ldyBWZWN0b3IzKCksXG5cdFx0XHRcdG5ldyBWZWN0b3IzKCksXG5cdFx0XHRcdG5ldyBWZWN0b3IzKClcblx0XHRcdF07XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBhcHBseU1hdHJpeDQoIG1hdHJpeCApIHtcblxuXHRcdFx0XHQvLyB0cmFuc2Zvcm0gb2YgZW1wdHkgYm94IGlzIGFuIGVtcHR5IGJveC5cblx0XHRcdFx0aWYoIHRoaXMuaXNFbXB0eSgpICkgcmV0dXJuIHRoaXM7XG5cblx0XHRcdFx0Ly8gTk9URTogSSBhbSB1c2luZyBhIGJpbmFyeSBwYXR0ZXJuIHRvIHNwZWNpZnkgYWxsIDJeMyBjb21iaW5hdGlvbnMgYmVsb3dcblx0XHRcdFx0cG9pbnRzWyAwIF0uc2V0KCB0aGlzLm1pbi54LCB0aGlzLm1pbi55LCB0aGlzLm1pbi56ICkuYXBwbHlNYXRyaXg0KCBtYXRyaXggKTsgLy8gMDAwXG5cdFx0XHRcdHBvaW50c1sgMSBdLnNldCggdGhpcy5taW4ueCwgdGhpcy5taW4ueSwgdGhpcy5tYXgueiApLmFwcGx5TWF0cml4NCggbWF0cml4ICk7IC8vIDAwMVxuXHRcdFx0XHRwb2ludHNbIDIgXS5zZXQoIHRoaXMubWluLngsIHRoaXMubWF4LnksIHRoaXMubWluLnogKS5hcHBseU1hdHJpeDQoIG1hdHJpeCApOyAvLyAwMTBcblx0XHRcdFx0cG9pbnRzWyAzIF0uc2V0KCB0aGlzLm1pbi54LCB0aGlzLm1heC55LCB0aGlzLm1heC56ICkuYXBwbHlNYXRyaXg0KCBtYXRyaXggKTsgLy8gMDExXG5cdFx0XHRcdHBvaW50c1sgNCBdLnNldCggdGhpcy5tYXgueCwgdGhpcy5taW4ueSwgdGhpcy5taW4ueiApLmFwcGx5TWF0cml4NCggbWF0cml4ICk7IC8vIDEwMFxuXHRcdFx0XHRwb2ludHNbIDUgXS5zZXQoIHRoaXMubWF4LngsIHRoaXMubWluLnksIHRoaXMubWF4LnogKS5hcHBseU1hdHJpeDQoIG1hdHJpeCApOyAvLyAxMDFcblx0XHRcdFx0cG9pbnRzWyA2IF0uc2V0KCB0aGlzLm1heC54LCB0aGlzLm1heC55LCB0aGlzLm1pbi56ICkuYXBwbHlNYXRyaXg0KCBtYXRyaXggKTsgLy8gMTEwXG5cdFx0XHRcdHBvaW50c1sgNyBdLnNldCggdGhpcy5tYXgueCwgdGhpcy5tYXgueSwgdGhpcy5tYXgueiApLmFwcGx5TWF0cml4NCggbWF0cml4ICk7XHQvLyAxMTFcblxuXHRcdFx0XHR0aGlzLnNldEZyb21Qb2ludHMoIHBvaW50cyApO1xuXG5cdFx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0dHJhbnNsYXRlOiBmdW5jdGlvbiAoIG9mZnNldCApIHtcblxuXHRcdFx0dGhpcy5taW4uYWRkKCBvZmZzZXQgKTtcblx0XHRcdHRoaXMubWF4LmFkZCggb2Zmc2V0ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGVxdWFsczogZnVuY3Rpb24gKCBib3ggKSB7XG5cblx0XHRcdHJldHVybiBib3gubWluLmVxdWFscyggdGhpcy5taW4gKSAmJiBib3gubWF4LmVxdWFscyggdGhpcy5tYXggKTtcblxuXHRcdH1cblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGJob3VzdG9uIC8gaHR0cDovL2NsYXJhLmlvXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFNwaGVyZSggY2VudGVyLCByYWRpdXMgKSB7XG5cblx0XHR0aGlzLmNlbnRlciA9ICggY2VudGVyICE9PSB1bmRlZmluZWQgKSA/IGNlbnRlciA6IG5ldyBWZWN0b3IzKCk7XG5cdFx0dGhpcy5yYWRpdXMgPSAoIHJhZGl1cyAhPT0gdW5kZWZpbmVkICkgPyByYWRpdXMgOiAwO1xuXG5cdH1cblxuXHRTcGhlcmUucHJvdG90eXBlID0ge1xuXG5cdFx0Y29uc3RydWN0b3I6IFNwaGVyZSxcblxuXHRcdHNldDogZnVuY3Rpb24gKCBjZW50ZXIsIHJhZGl1cyApIHtcblxuXHRcdFx0dGhpcy5jZW50ZXIuY29weSggY2VudGVyICk7XG5cdFx0XHR0aGlzLnJhZGl1cyA9IHJhZGl1cztcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0RnJvbVBvaW50czogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgYm94ID0gbmV3IEJveDMoKTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIHNldEZyb21Qb2ludHMoIHBvaW50cywgb3B0aW9uYWxDZW50ZXIgKSB7XG5cblx0XHRcdFx0dmFyIGNlbnRlciA9IHRoaXMuY2VudGVyO1xuXG5cdFx0XHRcdGlmICggb3B0aW9uYWxDZW50ZXIgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdGNlbnRlci5jb3B5KCBvcHRpb25hbENlbnRlciApO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRib3guc2V0RnJvbVBvaW50cyggcG9pbnRzICkuZ2V0Q2VudGVyKCBjZW50ZXIgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0dmFyIG1heFJhZGl1c1NxID0gMDtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGlsID0gcG9pbnRzLmxlbmd0aDsgaSA8IGlsOyBpICsrICkge1xuXG5cdFx0XHRcdFx0bWF4UmFkaXVzU3EgPSBNYXRoLm1heCggbWF4UmFkaXVzU3EsIGNlbnRlci5kaXN0YW5jZVRvU3F1YXJlZCggcG9pbnRzWyBpIF0gKSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR0aGlzLnJhZGl1cyA9IE1hdGguc3FydCggbWF4UmFkaXVzU3EgKTtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiBuZXcgdGhpcy5jb25zdHJ1Y3RvcigpLmNvcHkoIHRoaXMgKTtcblxuXHRcdH0sXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIHNwaGVyZSApIHtcblxuXHRcdFx0dGhpcy5jZW50ZXIuY29weSggc3BoZXJlLmNlbnRlciApO1xuXHRcdFx0dGhpcy5yYWRpdXMgPSBzcGhlcmUucmFkaXVzO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRlbXB0eTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gKCB0aGlzLnJhZGl1cyA8PSAwICk7XG5cblx0XHR9LFxuXG5cdFx0Y29udGFpbnNQb2ludDogZnVuY3Rpb24gKCBwb2ludCApIHtcblxuXHRcdFx0cmV0dXJuICggcG9pbnQuZGlzdGFuY2VUb1NxdWFyZWQoIHRoaXMuY2VudGVyICkgPD0gKCB0aGlzLnJhZGl1cyAqIHRoaXMucmFkaXVzICkgKTtcblxuXHRcdH0sXG5cblx0XHRkaXN0YW5jZVRvUG9pbnQ6IGZ1bmN0aW9uICggcG9pbnQgKSB7XG5cblx0XHRcdHJldHVybiAoIHBvaW50LmRpc3RhbmNlVG8oIHRoaXMuY2VudGVyICkgLSB0aGlzLnJhZGl1cyApO1xuXG5cdFx0fSxcblxuXHRcdGludGVyc2VjdHNTcGhlcmU6IGZ1bmN0aW9uICggc3BoZXJlICkge1xuXG5cdFx0XHR2YXIgcmFkaXVzU3VtID0gdGhpcy5yYWRpdXMgKyBzcGhlcmUucmFkaXVzO1xuXG5cdFx0XHRyZXR1cm4gc3BoZXJlLmNlbnRlci5kaXN0YW5jZVRvU3F1YXJlZCggdGhpcy5jZW50ZXIgKSA8PSAoIHJhZGl1c1N1bSAqIHJhZGl1c1N1bSApO1xuXG5cdFx0fSxcblxuXHRcdGludGVyc2VjdHNCb3g6IGZ1bmN0aW9uICggYm94ICkge1xuXG5cdFx0XHRyZXR1cm4gYm94LmludGVyc2VjdHNTcGhlcmUoIHRoaXMgKTtcblxuXHRcdH0sXG5cblx0XHRpbnRlcnNlY3RzUGxhbmU6IGZ1bmN0aW9uICggcGxhbmUgKSB7XG5cblx0XHRcdC8vIFdlIHVzZSB0aGUgZm9sbG93aW5nIGVxdWF0aW9uIHRvIGNvbXB1dGUgdGhlIHNpZ25lZCBkaXN0YW5jZSBmcm9tXG5cdFx0XHQvLyB0aGUgY2VudGVyIG9mIHRoZSBzcGhlcmUgdG8gdGhlIHBsYW5lLlxuXHRcdFx0Ly9cblx0XHRcdC8vIGRpc3RhbmNlID0gcSAqIG4gLSBkXG5cdFx0XHQvL1xuXHRcdFx0Ly8gSWYgdGhpcyBkaXN0YW5jZSBpcyBncmVhdGVyIHRoYW4gdGhlIHJhZGl1cyBvZiB0aGUgc3BoZXJlLFxuXHRcdFx0Ly8gdGhlbiB0aGVyZSBpcyBubyBpbnRlcnNlY3Rpb24uXG5cblx0XHRcdHJldHVybiBNYXRoLmFicyggdGhpcy5jZW50ZXIuZG90KCBwbGFuZS5ub3JtYWwgKSAtIHBsYW5lLmNvbnN0YW50ICkgPD0gdGhpcy5yYWRpdXM7XG5cblx0XHR9LFxuXG5cdFx0Y2xhbXBQb2ludDogZnVuY3Rpb24gKCBwb2ludCwgb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdHZhciBkZWx0YUxlbmd0aFNxID0gdGhpcy5jZW50ZXIuZGlzdGFuY2VUb1NxdWFyZWQoIHBvaW50ICk7XG5cblx0XHRcdHZhciByZXN1bHQgPSBvcHRpb25hbFRhcmdldCB8fCBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRyZXN1bHQuY29weSggcG9pbnQgKTtcblxuXHRcdFx0aWYgKCBkZWx0YUxlbmd0aFNxID4gKCB0aGlzLnJhZGl1cyAqIHRoaXMucmFkaXVzICkgKSB7XG5cblx0XHRcdFx0cmVzdWx0LnN1YiggdGhpcy5jZW50ZXIgKS5ub3JtYWxpemUoKTtcblx0XHRcdFx0cmVzdWx0Lm11bHRpcGx5U2NhbGFyKCB0aGlzLnJhZGl1cyApLmFkZCggdGhpcy5jZW50ZXIgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gcmVzdWx0O1xuXG5cdFx0fSxcblxuXHRcdGdldEJvdW5kaW5nQm94OiBmdW5jdGlvbiAoIG9wdGlvbmFsVGFyZ2V0ICkge1xuXG5cdFx0XHR2YXIgYm94ID0gb3B0aW9uYWxUYXJnZXQgfHwgbmV3IEJveDMoKTtcblxuXHRcdFx0Ym94LnNldCggdGhpcy5jZW50ZXIsIHRoaXMuY2VudGVyICk7XG5cdFx0XHRib3guZXhwYW5kQnlTY2FsYXIoIHRoaXMucmFkaXVzICk7XG5cblx0XHRcdHJldHVybiBib3g7XG5cblx0XHR9LFxuXG5cdFx0YXBwbHlNYXRyaXg0OiBmdW5jdGlvbiAoIG1hdHJpeCApIHtcblxuXHRcdFx0dGhpcy5jZW50ZXIuYXBwbHlNYXRyaXg0KCBtYXRyaXggKTtcblx0XHRcdHRoaXMucmFkaXVzID0gdGhpcy5yYWRpdXMgKiBtYXRyaXguZ2V0TWF4U2NhbGVPbkF4aXMoKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0dHJhbnNsYXRlOiBmdW5jdGlvbiAoIG9mZnNldCApIHtcblxuXHRcdFx0dGhpcy5jZW50ZXIuYWRkKCBvZmZzZXQgKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0ZXF1YWxzOiBmdW5jdGlvbiAoIHNwaGVyZSApIHtcblxuXHRcdFx0cmV0dXJuIHNwaGVyZS5jZW50ZXIuZXF1YWxzKCB0aGlzLmNlbnRlciApICYmICggc3BoZXJlLnJhZGl1cyA9PT0gdGhpcy5yYWRpdXMgKTtcblxuXHRcdH1cblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKiBAYXV0aG9yIFdlc3RMYW5nbGV5IC8gaHR0cDovL2dpdGh1Yi5jb20vV2VzdExhbmdsZXlcblx0ICogQGF1dGhvciBiaG91c3RvbiAvIGh0dHA6Ly9jbGFyYS5pb1xuXHQgKiBAYXV0aG9yIHRzY2h3XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIE1hdHJpeDMoKSB7XG5cblx0XHR0aGlzLmVsZW1lbnRzID0gbmV3IEZsb2F0MzJBcnJheSggW1xuXG5cdFx0XHQxLCAwLCAwLFxuXHRcdFx0MCwgMSwgMCxcblx0XHRcdDAsIDAsIDFcblxuXHRcdF0gKTtcblxuXHRcdGlmICggYXJndW1lbnRzLmxlbmd0aCA+IDAgKSB7XG5cblx0XHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5NYXRyaXgzOiB0aGUgY29uc3RydWN0b3Igbm8gbG9uZ2VyIHJlYWRzIGFyZ3VtZW50cy4gdXNlIC5zZXQoKSBpbnN0ZWFkLicgKTtcblxuXHRcdH1cblxuXHR9XG5cblx0TWF0cml4My5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogTWF0cml4MyxcblxuXHRcdGlzTWF0cml4MzogdHJ1ZSxcblxuXHRcdHNldDogZnVuY3Rpb24gKCBuMTEsIG4xMiwgbjEzLCBuMjEsIG4yMiwgbjIzLCBuMzEsIG4zMiwgbjMzICkge1xuXG5cdFx0XHR2YXIgdGUgPSB0aGlzLmVsZW1lbnRzO1xuXG5cdFx0XHR0ZVsgMCBdID0gbjExOyB0ZVsgMSBdID0gbjIxOyB0ZVsgMiBdID0gbjMxO1xuXHRcdFx0dGVbIDMgXSA9IG4xMjsgdGVbIDQgXSA9IG4yMjsgdGVbIDUgXSA9IG4zMjtcblx0XHRcdHRlWyA2IF0gPSBuMTM7IHRlWyA3IF0gPSBuMjM7IHRlWyA4IF0gPSBuMzM7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGlkZW50aXR5OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMuc2V0KFxuXG5cdFx0XHRcdDEsIDAsIDAsXG5cdFx0XHRcdDAsIDEsIDAsXG5cdFx0XHRcdDAsIDAsIDFcblxuXHRcdFx0KTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Y2xvbmU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIG5ldyB0aGlzLmNvbnN0cnVjdG9yKCkuZnJvbUFycmF5KCB0aGlzLmVsZW1lbnRzICk7XG5cblx0XHR9LFxuXG5cdFx0Y29weTogZnVuY3Rpb24gKCBtICkge1xuXG5cdFx0XHR2YXIgbWUgPSBtLmVsZW1lbnRzO1xuXG5cdFx0XHR0aGlzLnNldChcblxuXHRcdFx0XHRtZVsgMCBdLCBtZVsgMyBdLCBtZVsgNiBdLFxuXHRcdFx0XHRtZVsgMSBdLCBtZVsgNCBdLCBtZVsgNyBdLFxuXHRcdFx0XHRtZVsgMiBdLCBtZVsgNSBdLCBtZVsgOCBdXG5cblx0XHRcdCk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldEZyb21NYXRyaXg0OiBmdW5jdGlvbiggbSApIHtcblxuXHRcdFx0dmFyIG1lID0gbS5lbGVtZW50cztcblxuXHRcdFx0dGhpcy5zZXQoXG5cblx0XHRcdFx0bWVbIDAgXSwgbWVbIDQgXSwgbWVbICA4IF0sXG5cdFx0XHRcdG1lWyAxIF0sIG1lWyA1IF0sIG1lWyAgOSBdLFxuXHRcdFx0XHRtZVsgMiBdLCBtZVsgNiBdLCBtZVsgMTAgXVxuXG5cdFx0XHQpO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRhcHBseVRvVmVjdG9yM0FycmF5OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB2MTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIGFwcGx5VG9WZWN0b3IzQXJyYXkoIGFycmF5LCBvZmZzZXQsIGxlbmd0aCApIHtcblxuXHRcdFx0XHRpZiAoIHYxID09PSB1bmRlZmluZWQgKSB2MSA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHRcdGlmICggb2Zmc2V0ID09PSB1bmRlZmluZWQgKSBvZmZzZXQgPSAwO1xuXHRcdFx0XHRpZiAoIGxlbmd0aCA9PT0gdW5kZWZpbmVkICkgbGVuZ3RoID0gYXJyYXkubGVuZ3RoO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMCwgaiA9IG9mZnNldDsgaSA8IGxlbmd0aDsgaSArPSAzLCBqICs9IDMgKSB7XG5cblx0XHRcdFx0XHR2MS5mcm9tQXJyYXkoIGFycmF5LCBqICk7XG5cdFx0XHRcdFx0djEuYXBwbHlNYXRyaXgzKCB0aGlzICk7XG5cdFx0XHRcdFx0djEudG9BcnJheSggYXJyYXksIGogKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuIGFycmF5O1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0YXBwbHlUb0J1ZmZlckF0dHJpYnV0ZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgdjE7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBhcHBseVRvQnVmZmVyQXR0cmlidXRlKCBhdHRyaWJ1dGUgKSB7XG5cblx0XHRcdFx0aWYgKCB2MSA9PT0gdW5kZWZpbmVkICkgdjEgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGF0dHJpYnV0ZS5jb3VudDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0XHR2MS54ID0gYXR0cmlidXRlLmdldFgoIGkgKTtcblx0XHRcdFx0XHR2MS55ID0gYXR0cmlidXRlLmdldFkoIGkgKTtcblx0XHRcdFx0XHR2MS56ID0gYXR0cmlidXRlLmdldFooIGkgKTtcblxuXHRcdFx0XHRcdHYxLmFwcGx5TWF0cml4MyggdGhpcyApO1xuXG5cdFx0XHRcdFx0YXR0cmlidXRlLnNldFhZWiggaSwgdjEueCwgdjEueSwgdjEueiApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRyZXR1cm4gYXR0cmlidXRlO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0bXVsdGlwbHlTY2FsYXI6IGZ1bmN0aW9uICggcyApIHtcblxuXHRcdFx0dmFyIHRlID0gdGhpcy5lbGVtZW50cztcblxuXHRcdFx0dGVbIDAgXSAqPSBzOyB0ZVsgMyBdICo9IHM7IHRlWyA2IF0gKj0gcztcblx0XHRcdHRlWyAxIF0gKj0gczsgdGVbIDQgXSAqPSBzOyB0ZVsgNyBdICo9IHM7XG5cdFx0XHR0ZVsgMiBdICo9IHM7IHRlWyA1IF0gKj0gczsgdGVbIDggXSAqPSBzO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRkZXRlcm1pbmFudDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgdGUgPSB0aGlzLmVsZW1lbnRzO1xuXG5cdFx0XHR2YXIgYSA9IHRlWyAwIF0sIGIgPSB0ZVsgMSBdLCBjID0gdGVbIDIgXSxcblx0XHRcdFx0ZCA9IHRlWyAzIF0sIGUgPSB0ZVsgNCBdLCBmID0gdGVbIDUgXSxcblx0XHRcdFx0ZyA9IHRlWyA2IF0sIGggPSB0ZVsgNyBdLCBpID0gdGVbIDggXTtcblxuXHRcdFx0cmV0dXJuIGEgKiBlICogaSAtIGEgKiBmICogaCAtIGIgKiBkICogaSArIGIgKiBmICogZyArIGMgKiBkICogaCAtIGMgKiBlICogZztcblxuXHRcdH0sXG5cblx0XHRnZXRJbnZlcnNlOiBmdW5jdGlvbiAoIG1hdHJpeCwgdGhyb3dPbkRlZ2VuZXJhdGUgKSB7XG5cblx0XHRcdGlmICggbWF0cml4ICYmIG1hdHJpeC5pc01hdHJpeDQgKSB7XG5cblx0XHRcdFx0Y29uc29sZS5lcnJvciggXCJUSFJFRS5NYXRyaXgzLmdldEludmVyc2Ugbm8gbG9uZ2VyIHRha2VzIGEgTWF0cml4NCBhcmd1bWVudC5cIiApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBtZSA9IG1hdHJpeC5lbGVtZW50cyxcblx0XHRcdFx0dGUgPSB0aGlzLmVsZW1lbnRzLFxuXG5cdFx0XHRcdG4xMSA9IG1lWyAwIF0sIG4yMSA9IG1lWyAxIF0sIG4zMSA9IG1lWyAyIF0sXG5cdFx0XHRcdG4xMiA9IG1lWyAzIF0sIG4yMiA9IG1lWyA0IF0sIG4zMiA9IG1lWyA1IF0sXG5cdFx0XHRcdG4xMyA9IG1lWyA2IF0sIG4yMyA9IG1lWyA3IF0sIG4zMyA9IG1lWyA4IF0sXG5cblx0XHRcdFx0dDExID0gbjMzICogbjIyIC0gbjMyICogbjIzLFxuXHRcdFx0XHR0MTIgPSBuMzIgKiBuMTMgLSBuMzMgKiBuMTIsXG5cdFx0XHRcdHQxMyA9IG4yMyAqIG4xMiAtIG4yMiAqIG4xMyxcblxuXHRcdFx0XHRkZXQgPSBuMTEgKiB0MTEgKyBuMjEgKiB0MTIgKyBuMzEgKiB0MTM7XG5cblx0XHRcdGlmICggZGV0ID09PSAwICkge1xuXG5cdFx0XHRcdHZhciBtc2cgPSBcIlRIUkVFLk1hdHJpeDMuZ2V0SW52ZXJzZSgpOiBjYW4ndCBpbnZlcnQgbWF0cml4LCBkZXRlcm1pbmFudCBpcyAwXCI7XG5cblx0XHRcdFx0aWYgKCB0aHJvd09uRGVnZW5lcmF0ZSA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRcdHRocm93IG5ldyBFcnJvciggbXNnICk7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdGNvbnNvbGUud2FybiggbXNnICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJldHVybiB0aGlzLmlkZW50aXR5KCk7XG5cdFx0XHR9XG5cblx0XHRcdHZhciBkZXRJbnYgPSAxIC8gZGV0O1xuXG5cdFx0XHR0ZVsgMCBdID0gdDExICogZGV0SW52O1xuXHRcdFx0dGVbIDEgXSA9ICggbjMxICogbjIzIC0gbjMzICogbjIxICkgKiBkZXRJbnY7XG5cdFx0XHR0ZVsgMiBdID0gKCBuMzIgKiBuMjEgLSBuMzEgKiBuMjIgKSAqIGRldEludjtcblxuXHRcdFx0dGVbIDMgXSA9IHQxMiAqIGRldEludjtcblx0XHRcdHRlWyA0IF0gPSAoIG4zMyAqIG4xMSAtIG4zMSAqIG4xMyApICogZGV0SW52O1xuXHRcdFx0dGVbIDUgXSA9ICggbjMxICogbjEyIC0gbjMyICogbjExICkgKiBkZXRJbnY7XG5cblx0XHRcdHRlWyA2IF0gPSB0MTMgKiBkZXRJbnY7XG5cdFx0XHR0ZVsgNyBdID0gKCBuMjEgKiBuMTMgLSBuMjMgKiBuMTEgKSAqIGRldEludjtcblx0XHRcdHRlWyA4IF0gPSAoIG4yMiAqIG4xMSAtIG4yMSAqIG4xMiApICogZGV0SW52O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHR0cmFuc3Bvc2U6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIHRtcCwgbSA9IHRoaXMuZWxlbWVudHM7XG5cblx0XHRcdHRtcCA9IG1bIDEgXTsgbVsgMSBdID0gbVsgMyBdOyBtWyAzIF0gPSB0bXA7XG5cdFx0XHR0bXAgPSBtWyAyIF07IG1bIDIgXSA9IG1bIDYgXTsgbVsgNiBdID0gdG1wO1xuXHRcdFx0dG1wID0gbVsgNSBdOyBtWyA1IF0gPSBtWyA3IF07IG1bIDcgXSA9IHRtcDtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Z2V0Tm9ybWFsTWF0cml4OiBmdW5jdGlvbiAoIG1hdHJpeDQgKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLnNldEZyb21NYXRyaXg0KCBtYXRyaXg0ICkuZ2V0SW52ZXJzZSggdGhpcyApLnRyYW5zcG9zZSgpO1xuXG5cdFx0fSxcblxuXHRcdHRyYW5zcG9zZUludG9BcnJheTogZnVuY3Rpb24gKCByICkge1xuXG5cdFx0XHR2YXIgbSA9IHRoaXMuZWxlbWVudHM7XG5cblx0XHRcdHJbIDAgXSA9IG1bIDAgXTtcblx0XHRcdHJbIDEgXSA9IG1bIDMgXTtcblx0XHRcdHJbIDIgXSA9IG1bIDYgXTtcblx0XHRcdHJbIDMgXSA9IG1bIDEgXTtcblx0XHRcdHJbIDQgXSA9IG1bIDQgXTtcblx0XHRcdHJbIDUgXSA9IG1bIDcgXTtcblx0XHRcdHJbIDYgXSA9IG1bIDIgXTtcblx0XHRcdHJbIDcgXSA9IG1bIDUgXTtcblx0XHRcdHJbIDggXSA9IG1bIDggXTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0ZnJvbUFycmF5OiBmdW5jdGlvbiAoIGFycmF5LCBvZmZzZXQgKSB7XG5cblx0XHRcdGlmICggb2Zmc2V0ID09PSB1bmRlZmluZWQgKSBvZmZzZXQgPSAwO1xuXG5cdFx0XHRmb3IoIHZhciBpID0gMDsgaSA8IDk7IGkgKysgKSB7XG5cblx0XHRcdFx0dGhpcy5lbGVtZW50c1sgaSBdID0gYXJyYXlbIGkgKyBvZmZzZXQgXTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHR0b0FycmF5OiBmdW5jdGlvbiAoIGFycmF5LCBvZmZzZXQgKSB7XG5cblx0XHRcdGlmICggYXJyYXkgPT09IHVuZGVmaW5lZCApIGFycmF5ID0gW107XG5cdFx0XHRpZiAoIG9mZnNldCA9PT0gdW5kZWZpbmVkICkgb2Zmc2V0ID0gMDtcblxuXHRcdFx0dmFyIHRlID0gdGhpcy5lbGVtZW50cztcblxuXHRcdFx0YXJyYXlbIG9mZnNldCBdID0gdGVbIDAgXTtcblx0XHRcdGFycmF5WyBvZmZzZXQgKyAxIF0gPSB0ZVsgMSBdO1xuXHRcdFx0YXJyYXlbIG9mZnNldCArIDIgXSA9IHRlWyAyIF07XG5cblx0XHRcdGFycmF5WyBvZmZzZXQgKyAzIF0gPSB0ZVsgMyBdO1xuXHRcdFx0YXJyYXlbIG9mZnNldCArIDQgXSA9IHRlWyA0IF07XG5cdFx0XHRhcnJheVsgb2Zmc2V0ICsgNSBdID0gdGVbIDUgXTtcblxuXHRcdFx0YXJyYXlbIG9mZnNldCArIDYgXSA9IHRlWyA2IF07XG5cdFx0XHRhcnJheVsgb2Zmc2V0ICsgNyBdID0gdGVbIDcgXTtcblx0XHRcdGFycmF5WyBvZmZzZXQgKyA4IF0gID0gdGVbIDggXTtcblxuXHRcdFx0cmV0dXJuIGFycmF5O1xuXG5cdFx0fVxuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgYmhvdXN0b24gLyBodHRwOi8vY2xhcmEuaW9cblx0ICovXG5cblx0ZnVuY3Rpb24gUGxhbmUoIG5vcm1hbCwgY29uc3RhbnQgKSB7XG5cblx0XHR0aGlzLm5vcm1hbCA9ICggbm9ybWFsICE9PSB1bmRlZmluZWQgKSA/IG5vcm1hbCA6IG5ldyBWZWN0b3IzKCAxLCAwLCAwICk7XG5cdFx0dGhpcy5jb25zdGFudCA9ICggY29uc3RhbnQgIT09IHVuZGVmaW5lZCApID8gY29uc3RhbnQgOiAwO1xuXG5cdH1cblxuXHRQbGFuZS5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogUGxhbmUsXG5cblx0XHRzZXQ6IGZ1bmN0aW9uICggbm9ybWFsLCBjb25zdGFudCApIHtcblxuXHRcdFx0dGhpcy5ub3JtYWwuY29weSggbm9ybWFsICk7XG5cdFx0XHR0aGlzLmNvbnN0YW50ID0gY29uc3RhbnQ7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldENvbXBvbmVudHM6IGZ1bmN0aW9uICggeCwgeSwgeiwgdyApIHtcblxuXHRcdFx0dGhpcy5ub3JtYWwuc2V0KCB4LCB5LCB6ICk7XG5cdFx0XHR0aGlzLmNvbnN0YW50ID0gdztcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0RnJvbU5vcm1hbEFuZENvcGxhbmFyUG9pbnQ6IGZ1bmN0aW9uICggbm9ybWFsLCBwb2ludCApIHtcblxuXHRcdFx0dGhpcy5ub3JtYWwuY29weSggbm9ybWFsICk7XG5cdFx0XHR0aGlzLmNvbnN0YW50ID0gLSBwb2ludC5kb3QoIHRoaXMubm9ybWFsICk7XHQvLyBtdXN0IGJlIHRoaXMubm9ybWFsLCBub3Qgbm9ybWFsLCBhcyB0aGlzLm5vcm1hbCBpcyBub3JtYWxpemVkXG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldEZyb21Db3BsYW5hclBvaW50czogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgdjEgPSBuZXcgVmVjdG9yMygpO1xuXHRcdFx0dmFyIHYyID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIHNldEZyb21Db3BsYW5hclBvaW50cyggYSwgYiwgYyApIHtcblxuXHRcdFx0XHR2YXIgbm9ybWFsID0gdjEuc3ViVmVjdG9ycyggYywgYiApLmNyb3NzKCB2Mi5zdWJWZWN0b3JzKCBhLCBiICkgKS5ub3JtYWxpemUoKTtcblxuXHRcdFx0XHQvLyBROiBzaG91bGQgYW4gZXJyb3IgYmUgdGhyb3duIGlmIG5vcm1hbCBpcyB6ZXJvIChlLmcuIGRlZ2VuZXJhdGUgcGxhbmUpP1xuXG5cdFx0XHRcdHRoaXMuc2V0RnJvbU5vcm1hbEFuZENvcGxhbmFyUG9pbnQoIG5vcm1hbCwgYSApO1xuXG5cdFx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0Y2xvbmU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIG5ldyB0aGlzLmNvbnN0cnVjdG9yKCkuY29weSggdGhpcyApO1xuXG5cdFx0fSxcblxuXHRcdGNvcHk6IGZ1bmN0aW9uICggcGxhbmUgKSB7XG5cblx0XHRcdHRoaXMubm9ybWFsLmNvcHkoIHBsYW5lLm5vcm1hbCApO1xuXHRcdFx0dGhpcy5jb25zdGFudCA9IHBsYW5lLmNvbnN0YW50O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRub3JtYWxpemU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Ly8gTm90ZTogd2lsbCBsZWFkIHRvIGEgZGl2aWRlIGJ5IHplcm8gaWYgdGhlIHBsYW5lIGlzIGludmFsaWQuXG5cblx0XHRcdHZhciBpbnZlcnNlTm9ybWFsTGVuZ3RoID0gMS4wIC8gdGhpcy5ub3JtYWwubGVuZ3RoKCk7XG5cdFx0XHR0aGlzLm5vcm1hbC5tdWx0aXBseVNjYWxhciggaW52ZXJzZU5vcm1hbExlbmd0aCApO1xuXHRcdFx0dGhpcy5jb25zdGFudCAqPSBpbnZlcnNlTm9ybWFsTGVuZ3RoO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRuZWdhdGU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dGhpcy5jb25zdGFudCAqPSAtIDE7XG5cdFx0XHR0aGlzLm5vcm1hbC5uZWdhdGUoKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0ZGlzdGFuY2VUb1BvaW50OiBmdW5jdGlvbiAoIHBvaW50ICkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5ub3JtYWwuZG90KCBwb2ludCApICsgdGhpcy5jb25zdGFudDtcblxuXHRcdH0sXG5cblx0XHRkaXN0YW5jZVRvU3BoZXJlOiBmdW5jdGlvbiAoIHNwaGVyZSApIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuZGlzdGFuY2VUb1BvaW50KCBzcGhlcmUuY2VudGVyICkgLSBzcGhlcmUucmFkaXVzO1xuXG5cdFx0fSxcblxuXHRcdHByb2plY3RQb2ludDogZnVuY3Rpb24gKCBwb2ludCwgb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLm9ydGhvUG9pbnQoIHBvaW50LCBvcHRpb25hbFRhcmdldCApLnN1YiggcG9pbnQgKS5uZWdhdGUoKTtcblxuXHRcdH0sXG5cblx0XHRvcnRob1BvaW50OiBmdW5jdGlvbiAoIHBvaW50LCBvcHRpb25hbFRhcmdldCApIHtcblxuXHRcdFx0dmFyIHBlcnBlbmRpY3VsYXJNYWduaXR1ZGUgPSB0aGlzLmRpc3RhbmNlVG9Qb2ludCggcG9pbnQgKTtcblxuXHRcdFx0dmFyIHJlc3VsdCA9IG9wdGlvbmFsVGFyZ2V0IHx8IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHRyZXR1cm4gcmVzdWx0LmNvcHkoIHRoaXMubm9ybWFsICkubXVsdGlwbHlTY2FsYXIoIHBlcnBlbmRpY3VsYXJNYWduaXR1ZGUgKTtcblxuXHRcdH0sXG5cblx0XHRpbnRlcnNlY3RMaW5lOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB2MSA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBpbnRlcnNlY3RMaW5lKCBsaW5lLCBvcHRpb25hbFRhcmdldCApIHtcblxuXHRcdFx0XHR2YXIgcmVzdWx0ID0gb3B0aW9uYWxUYXJnZXQgfHwgbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0XHR2YXIgZGlyZWN0aW9uID0gbGluZS5kZWx0YSggdjEgKTtcblxuXHRcdFx0XHR2YXIgZGVub21pbmF0b3IgPSB0aGlzLm5vcm1hbC5kb3QoIGRpcmVjdGlvbiApO1xuXG5cdFx0XHRcdGlmICggZGVub21pbmF0b3IgPT09IDAgKSB7XG5cblx0XHRcdFx0XHQvLyBsaW5lIGlzIGNvcGxhbmFyLCByZXR1cm4gb3JpZ2luXG5cdFx0XHRcdFx0aWYgKCB0aGlzLmRpc3RhbmNlVG9Qb2ludCggbGluZS5zdGFydCApID09PSAwICkge1xuXG5cdFx0XHRcdFx0XHRyZXR1cm4gcmVzdWx0LmNvcHkoIGxpbmUuc3RhcnQgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vIFVuc3VyZSBpZiB0aGlzIGlzIHRoZSBjb3JyZWN0IG1ldGhvZCB0byBoYW5kbGUgdGhpcyBjYXNlLlxuXHRcdFx0XHRcdHJldHVybiB1bmRlZmluZWQ7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciB0ID0gLSAoIGxpbmUuc3RhcnQuZG90KCB0aGlzLm5vcm1hbCApICsgdGhpcy5jb25zdGFudCApIC8gZGVub21pbmF0b3I7XG5cblx0XHRcdFx0aWYgKCB0IDwgMCB8fCB0ID4gMSApIHtcblxuXHRcdFx0XHRcdHJldHVybiB1bmRlZmluZWQ7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJldHVybiByZXN1bHQuY29weSggZGlyZWN0aW9uICkubXVsdGlwbHlTY2FsYXIoIHQgKS5hZGQoIGxpbmUuc3RhcnQgKTtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdGludGVyc2VjdHNMaW5lOiBmdW5jdGlvbiAoIGxpbmUgKSB7XG5cblx0XHRcdC8vIE5vdGU6IHRoaXMgdGVzdHMgaWYgYSBsaW5lIGludGVyc2VjdHMgdGhlIHBsYW5lLCBub3Qgd2hldGhlciBpdCAob3IgaXRzIGVuZC1wb2ludHMpIGFyZSBjb3BsYW5hciB3aXRoIGl0LlxuXG5cdFx0XHR2YXIgc3RhcnRTaWduID0gdGhpcy5kaXN0YW5jZVRvUG9pbnQoIGxpbmUuc3RhcnQgKTtcblx0XHRcdHZhciBlbmRTaWduID0gdGhpcy5kaXN0YW5jZVRvUG9pbnQoIGxpbmUuZW5kICk7XG5cblx0XHRcdHJldHVybiAoIHN0YXJ0U2lnbiA8IDAgJiYgZW5kU2lnbiA+IDAgKSB8fCAoIGVuZFNpZ24gPCAwICYmIHN0YXJ0U2lnbiA+IDAgKTtcblxuXHRcdH0sXG5cblx0XHRpbnRlcnNlY3RzQm94OiBmdW5jdGlvbiAoIGJveCApIHtcblxuXHRcdFx0cmV0dXJuIGJveC5pbnRlcnNlY3RzUGxhbmUoIHRoaXMgKTtcblxuXHRcdH0sXG5cblx0XHRpbnRlcnNlY3RzU3BoZXJlOiBmdW5jdGlvbiAoIHNwaGVyZSApIHtcblxuXHRcdFx0cmV0dXJuIHNwaGVyZS5pbnRlcnNlY3RzUGxhbmUoIHRoaXMgKTtcblxuXHRcdH0sXG5cblx0XHRjb3BsYW5hclBvaW50OiBmdW5jdGlvbiAoIG9wdGlvbmFsVGFyZ2V0ICkge1xuXG5cdFx0XHR2YXIgcmVzdWx0ID0gb3B0aW9uYWxUYXJnZXQgfHwgbmV3IFZlY3RvcjMoKTtcblx0XHRcdHJldHVybiByZXN1bHQuY29weSggdGhpcy5ub3JtYWwgKS5tdWx0aXBseVNjYWxhciggLSB0aGlzLmNvbnN0YW50ICk7XG5cblx0XHR9LFxuXG5cdFx0YXBwbHlNYXRyaXg0OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB2MSA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHR2YXIgbTEgPSBuZXcgTWF0cml4MygpO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gYXBwbHlNYXRyaXg0KCBtYXRyaXgsIG9wdGlvbmFsTm9ybWFsTWF0cml4ICkge1xuXG5cdFx0XHRcdHZhciByZWZlcmVuY2VQb2ludCA9IHRoaXMuY29wbGFuYXJQb2ludCggdjEgKS5hcHBseU1hdHJpeDQoIG1hdHJpeCApO1xuXG5cdFx0XHRcdC8vIHRyYW5zZm9ybSBub3JtYWwgYmFzZWQgb24gdGhlb3J5IGhlcmU6XG5cdFx0XHRcdC8vIGh0dHA6Ly93d3cuc29uZ2hvLmNhL29wZW5nbC9nbF9ub3JtYWx0cmFuc2Zvcm0uaHRtbFxuXHRcdFx0XHR2YXIgbm9ybWFsTWF0cml4ID0gb3B0aW9uYWxOb3JtYWxNYXRyaXggfHwgbTEuZ2V0Tm9ybWFsTWF0cml4KCBtYXRyaXggKTtcblx0XHRcdFx0dmFyIG5vcm1hbCA9IHRoaXMubm9ybWFsLmFwcGx5TWF0cml4Myggbm9ybWFsTWF0cml4ICkubm9ybWFsaXplKCk7XG5cblx0XHRcdFx0Ly8gcmVjYWxjdWxhdGUgY29uc3RhbnQgKGxpa2UgaW4gc2V0RnJvbU5vcm1hbEFuZENvcGxhbmFyUG9pbnQpXG5cdFx0XHRcdHRoaXMuY29uc3RhbnQgPSAtIHJlZmVyZW5jZVBvaW50LmRvdCggbm9ybWFsICk7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHR0cmFuc2xhdGU6IGZ1bmN0aW9uICggb2Zmc2V0ICkge1xuXG5cdFx0XHR0aGlzLmNvbnN0YW50ID0gdGhpcy5jb25zdGFudCAtIG9mZnNldC5kb3QoIHRoaXMubm9ybWFsICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGVxdWFsczogZnVuY3Rpb24gKCBwbGFuZSApIHtcblxuXHRcdFx0cmV0dXJuIHBsYW5lLm5vcm1hbC5lcXVhbHMoIHRoaXMubm9ybWFsICkgJiYgKCBwbGFuZS5jb25zdGFudCA9PT0gdGhpcy5jb25zdGFudCApO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqIEBhdXRob3IgYmhvdXN0b24gLyBodHRwOi8vY2xhcmEuaW9cblx0ICovXG5cblx0ZnVuY3Rpb24gRnJ1c3R1bSggcDAsIHAxLCBwMiwgcDMsIHA0LCBwNSApIHtcblxuXHRcdHRoaXMucGxhbmVzID0gW1xuXG5cdFx0XHQoIHAwICE9PSB1bmRlZmluZWQgKSA/IHAwIDogbmV3IFBsYW5lKCksXG5cdFx0XHQoIHAxICE9PSB1bmRlZmluZWQgKSA/IHAxIDogbmV3IFBsYW5lKCksXG5cdFx0XHQoIHAyICE9PSB1bmRlZmluZWQgKSA/IHAyIDogbmV3IFBsYW5lKCksXG5cdFx0XHQoIHAzICE9PSB1bmRlZmluZWQgKSA/IHAzIDogbmV3IFBsYW5lKCksXG5cdFx0XHQoIHA0ICE9PSB1bmRlZmluZWQgKSA/IHA0IDogbmV3IFBsYW5lKCksXG5cdFx0XHQoIHA1ICE9PSB1bmRlZmluZWQgKSA/IHA1IDogbmV3IFBsYW5lKClcblxuXHRcdF07XG5cblx0fVxuXG5cdEZydXN0dW0ucHJvdG90eXBlID0ge1xuXG5cdFx0Y29uc3RydWN0b3I6IEZydXN0dW0sXG5cblx0XHRzZXQ6IGZ1bmN0aW9uICggcDAsIHAxLCBwMiwgcDMsIHA0LCBwNSApIHtcblxuXHRcdFx0dmFyIHBsYW5lcyA9IHRoaXMucGxhbmVzO1xuXG5cdFx0XHRwbGFuZXNbIDAgXS5jb3B5KCBwMCApO1xuXHRcdFx0cGxhbmVzWyAxIF0uY29weSggcDEgKTtcblx0XHRcdHBsYW5lc1sgMiBdLmNvcHkoIHAyICk7XG5cdFx0XHRwbGFuZXNbIDMgXS5jb3B5KCBwMyApO1xuXHRcdFx0cGxhbmVzWyA0IF0uY29weSggcDQgKTtcblx0XHRcdHBsYW5lc1sgNSBdLmNvcHkoIHA1ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiBuZXcgdGhpcy5jb25zdHJ1Y3RvcigpLmNvcHkoIHRoaXMgKTtcblxuXHRcdH0sXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIGZydXN0dW0gKSB7XG5cblx0XHRcdHZhciBwbGFuZXMgPSB0aGlzLnBsYW5lcztcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgNjsgaSArKyApIHtcblxuXHRcdFx0XHRwbGFuZXNbIGkgXS5jb3B5KCBmcnVzdHVtLnBsYW5lc1sgaSBdICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0RnJvbU1hdHJpeDogZnVuY3Rpb24gKCBtICkge1xuXG5cdFx0XHR2YXIgcGxhbmVzID0gdGhpcy5wbGFuZXM7XG5cdFx0XHR2YXIgbWUgPSBtLmVsZW1lbnRzO1xuXHRcdFx0dmFyIG1lMCA9IG1lWyAwIF0sIG1lMSA9IG1lWyAxIF0sIG1lMiA9IG1lWyAyIF0sIG1lMyA9IG1lWyAzIF07XG5cdFx0XHR2YXIgbWU0ID0gbWVbIDQgXSwgbWU1ID0gbWVbIDUgXSwgbWU2ID0gbWVbIDYgXSwgbWU3ID0gbWVbIDcgXTtcblx0XHRcdHZhciBtZTggPSBtZVsgOCBdLCBtZTkgPSBtZVsgOSBdLCBtZTEwID0gbWVbIDEwIF0sIG1lMTEgPSBtZVsgMTEgXTtcblx0XHRcdHZhciBtZTEyID0gbWVbIDEyIF0sIG1lMTMgPSBtZVsgMTMgXSwgbWUxNCA9IG1lWyAxNCBdLCBtZTE1ID0gbWVbIDE1IF07XG5cblx0XHRcdHBsYW5lc1sgMCBdLnNldENvbXBvbmVudHMoIG1lMyAtIG1lMCwgbWU3IC0gbWU0LCBtZTExIC0gbWU4LCBtZTE1IC0gbWUxMiApLm5vcm1hbGl6ZSgpO1xuXHRcdFx0cGxhbmVzWyAxIF0uc2V0Q29tcG9uZW50cyggbWUzICsgbWUwLCBtZTcgKyBtZTQsIG1lMTEgKyBtZTgsIG1lMTUgKyBtZTEyICkubm9ybWFsaXplKCk7XG5cdFx0XHRwbGFuZXNbIDIgXS5zZXRDb21wb25lbnRzKCBtZTMgKyBtZTEsIG1lNyArIG1lNSwgbWUxMSArIG1lOSwgbWUxNSArIG1lMTMgKS5ub3JtYWxpemUoKTtcblx0XHRcdHBsYW5lc1sgMyBdLnNldENvbXBvbmVudHMoIG1lMyAtIG1lMSwgbWU3IC0gbWU1LCBtZTExIC0gbWU5LCBtZTE1IC0gbWUxMyApLm5vcm1hbGl6ZSgpO1xuXHRcdFx0cGxhbmVzWyA0IF0uc2V0Q29tcG9uZW50cyggbWUzIC0gbWUyLCBtZTcgLSBtZTYsIG1lMTEgLSBtZTEwLCBtZTE1IC0gbWUxNCApLm5vcm1hbGl6ZSgpO1xuXHRcdFx0cGxhbmVzWyA1IF0uc2V0Q29tcG9uZW50cyggbWUzICsgbWUyLCBtZTcgKyBtZTYsIG1lMTEgKyBtZTEwLCBtZTE1ICsgbWUxNCApLm5vcm1hbGl6ZSgpO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRpbnRlcnNlY3RzT2JqZWN0OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBzcGhlcmUgPSBuZXcgU3BoZXJlKCk7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBpbnRlcnNlY3RzT2JqZWN0KCBvYmplY3QgKSB7XG5cblx0XHRcdFx0dmFyIGdlb21ldHJ5ID0gb2JqZWN0Lmdlb21ldHJ5O1xuXG5cdFx0XHRcdGlmICggZ2VvbWV0cnkuYm91bmRpbmdTcGhlcmUgPT09IG51bGwgKVxuXHRcdFx0XHRcdGdlb21ldHJ5LmNvbXB1dGVCb3VuZGluZ1NwaGVyZSgpO1xuXG5cdFx0XHRcdHNwaGVyZS5jb3B5KCBnZW9tZXRyeS5ib3VuZGluZ1NwaGVyZSApXG5cdFx0XHRcdFx0LmFwcGx5TWF0cml4NCggb2JqZWN0Lm1hdHJpeFdvcmxkICk7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXMuaW50ZXJzZWN0c1NwaGVyZSggc3BoZXJlICk7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRpbnRlcnNlY3RzU3ByaXRlOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBzcGhlcmUgPSBuZXcgU3BoZXJlKCk7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBpbnRlcnNlY3RzU3ByaXRlKCBzcHJpdGUgKSB7XG5cblx0XHRcdFx0c3BoZXJlLmNlbnRlci5zZXQoIDAsIDAsIDAgKTtcblx0XHRcdFx0c3BoZXJlLnJhZGl1cyA9IDAuNzA3MTA2NzgxMTg2NTQ3Njtcblx0XHRcdFx0c3BoZXJlLmFwcGx5TWF0cml4NCggc3ByaXRlLm1hdHJpeFdvcmxkICk7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXMuaW50ZXJzZWN0c1NwaGVyZSggc3BoZXJlICk7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRpbnRlcnNlY3RzU3BoZXJlOiBmdW5jdGlvbiAoIHNwaGVyZSApIHtcblxuXHRcdFx0dmFyIHBsYW5lcyA9IHRoaXMucGxhbmVzO1xuXHRcdFx0dmFyIGNlbnRlciA9IHNwaGVyZS5jZW50ZXI7XG5cdFx0XHR2YXIgbmVnUmFkaXVzID0gLSBzcGhlcmUucmFkaXVzO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCA2OyBpICsrICkge1xuXG5cdFx0XHRcdHZhciBkaXN0YW5jZSA9IHBsYW5lc1sgaSBdLmRpc3RhbmNlVG9Qb2ludCggY2VudGVyICk7XG5cblx0XHRcdFx0aWYgKCBkaXN0YW5jZSA8IG5lZ1JhZGl1cyApIHtcblxuXHRcdFx0XHRcdHJldHVybiBmYWxzZTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRydWU7XG5cblx0XHR9LFxuXG5cdFx0aW50ZXJzZWN0c0JveDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgcDEgPSBuZXcgVmVjdG9yMygpLFxuXHRcdFx0XHRwMiA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBpbnRlcnNlY3RzQm94KCBib3ggKSB7XG5cblx0XHRcdFx0dmFyIHBsYW5lcyA9IHRoaXMucGxhbmVzO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IDYgOyBpICsrICkge1xuXG5cdFx0XHRcdFx0dmFyIHBsYW5lID0gcGxhbmVzWyBpIF07XG5cblx0XHRcdFx0XHRwMS54ID0gcGxhbmUubm9ybWFsLnggPiAwID8gYm94Lm1pbi54IDogYm94Lm1heC54O1xuXHRcdFx0XHRcdHAyLnggPSBwbGFuZS5ub3JtYWwueCA+IDAgPyBib3gubWF4LnggOiBib3gubWluLng7XG5cdFx0XHRcdFx0cDEueSA9IHBsYW5lLm5vcm1hbC55ID4gMCA/IGJveC5taW4ueSA6IGJveC5tYXgueTtcblx0XHRcdFx0XHRwMi55ID0gcGxhbmUubm9ybWFsLnkgPiAwID8gYm94Lm1heC55IDogYm94Lm1pbi55O1xuXHRcdFx0XHRcdHAxLnogPSBwbGFuZS5ub3JtYWwueiA+IDAgPyBib3gubWluLnogOiBib3gubWF4Lno7XG5cdFx0XHRcdFx0cDIueiA9IHBsYW5lLm5vcm1hbC56ID4gMCA/IGJveC5tYXgueiA6IGJveC5taW4uejtcblxuXHRcdFx0XHRcdHZhciBkMSA9IHBsYW5lLmRpc3RhbmNlVG9Qb2ludCggcDEgKTtcblx0XHRcdFx0XHR2YXIgZDIgPSBwbGFuZS5kaXN0YW5jZVRvUG9pbnQoIHAyICk7XG5cblx0XHRcdFx0XHQvLyBpZiBib3RoIG91dHNpZGUgcGxhbmUsIG5vIGludGVyc2VjdGlvblxuXG5cdFx0XHRcdFx0aWYgKCBkMSA8IDAgJiYgZDIgPCAwICkge1xuXG5cdFx0XHRcdFx0XHRyZXR1cm4gZmFsc2U7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJldHVybiB0cnVlO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cblx0XHRjb250YWluc1BvaW50OiBmdW5jdGlvbiAoIHBvaW50ICkge1xuXG5cdFx0XHR2YXIgcGxhbmVzID0gdGhpcy5wbGFuZXM7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IDY7IGkgKysgKSB7XG5cblx0XHRcdFx0aWYgKCBwbGFuZXNbIGkgXS5kaXN0YW5jZVRvUG9pbnQoIHBvaW50ICkgPCAwICkge1xuXG5cdFx0XHRcdFx0cmV0dXJuIGZhbHNlO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdHJ1ZTtcblxuXHRcdH1cblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBXZWJHTFNoYWRvd01hcCggX3JlbmRlcmVyLCBfbGlnaHRzLCBfb2JqZWN0cywgY2FwYWJpbGl0aWVzICkge1xuXG5cdFx0dmFyIF9nbCA9IF9yZW5kZXJlci5jb250ZXh0LFxuXHRcdF9zdGF0ZSA9IF9yZW5kZXJlci5zdGF0ZSxcblx0XHRfZnJ1c3R1bSA9IG5ldyBGcnVzdHVtKCksXG5cdFx0X3Byb2pTY3JlZW5NYXRyaXggPSBuZXcgTWF0cml4NCgpLFxuXG5cdFx0X2xpZ2h0U2hhZG93cyA9IF9saWdodHMuc2hhZG93cyxcblxuXHRcdF9zaGFkb3dNYXBTaXplID0gbmV3IFZlY3RvcjIoKSxcblx0XHRfbWF4U2hhZG93TWFwU2l6ZSA9IG5ldyBWZWN0b3IyKCBjYXBhYmlsaXRpZXMubWF4VGV4dHVyZVNpemUsIGNhcGFiaWxpdGllcy5tYXhUZXh0dXJlU2l6ZSApLFxuXG5cdFx0X2xvb2tUYXJnZXQgPSBuZXcgVmVjdG9yMygpLFxuXHRcdF9saWdodFBvc2l0aW9uV29ybGQgPSBuZXcgVmVjdG9yMygpLFxuXG5cdFx0X3JlbmRlckxpc3QgPSBbXSxcblxuXHRcdF9Nb3JwaGluZ0ZsYWcgPSAxLFxuXHRcdF9Ta2lubmluZ0ZsYWcgPSAyLFxuXG5cdFx0X051bWJlck9mTWF0ZXJpYWxWYXJpYW50cyA9ICggX01vcnBoaW5nRmxhZyB8IF9Ta2lubmluZ0ZsYWcgKSArIDEsXG5cblx0XHRfZGVwdGhNYXRlcmlhbHMgPSBuZXcgQXJyYXkoIF9OdW1iZXJPZk1hdGVyaWFsVmFyaWFudHMgKSxcblx0XHRfZGlzdGFuY2VNYXRlcmlhbHMgPSBuZXcgQXJyYXkoIF9OdW1iZXJPZk1hdGVyaWFsVmFyaWFudHMgKSxcblxuXHRcdF9tYXRlcmlhbENhY2hlID0ge307XG5cblx0XHR2YXIgY3ViZURpcmVjdGlvbnMgPSBbXG5cdFx0XHRuZXcgVmVjdG9yMyggMSwgMCwgMCApLCBuZXcgVmVjdG9yMyggLSAxLCAwLCAwICksIG5ldyBWZWN0b3IzKCAwLCAwLCAxICksXG5cdFx0XHRuZXcgVmVjdG9yMyggMCwgMCwgLSAxICksIG5ldyBWZWN0b3IzKCAwLCAxLCAwICksIG5ldyBWZWN0b3IzKCAwLCAtIDEsIDAgKVxuXHRcdF07XG5cblx0XHR2YXIgY3ViZVVwcyA9IFtcblx0XHRcdG5ldyBWZWN0b3IzKCAwLCAxLCAwICksIG5ldyBWZWN0b3IzKCAwLCAxLCAwICksIG5ldyBWZWN0b3IzKCAwLCAxLCAwICksXG5cdFx0XHRuZXcgVmVjdG9yMyggMCwgMSwgMCApLCBuZXcgVmVjdG9yMyggMCwgMCwgMSApLFx0bmV3IFZlY3RvcjMoIDAsIDAsIC0gMSApXG5cdFx0XTtcblxuXHRcdHZhciBjdWJlMkRWaWV3UG9ydHMgPSBbXG5cdFx0XHRuZXcgVmVjdG9yNCgpLCBuZXcgVmVjdG9yNCgpLCBuZXcgVmVjdG9yNCgpLFxuXHRcdFx0bmV3IFZlY3RvcjQoKSwgbmV3IFZlY3RvcjQoKSwgbmV3IFZlY3RvcjQoKVxuXHRcdF07XG5cblx0XHQvLyBpbml0XG5cblx0XHR2YXIgZGVwdGhNYXRlcmlhbFRlbXBsYXRlID0gbmV3IE1lc2hEZXB0aE1hdGVyaWFsKCk7XG5cdFx0ZGVwdGhNYXRlcmlhbFRlbXBsYXRlLmRlcHRoUGFja2luZyA9IFJHQkFEZXB0aFBhY2tpbmc7XG5cdFx0ZGVwdGhNYXRlcmlhbFRlbXBsYXRlLmNsaXBwaW5nID0gdHJ1ZTtcblxuXHRcdHZhciBkaXN0YW5jZVNoYWRlciA9IFNoYWRlckxpYlsgXCJkaXN0YW5jZVJHQkFcIiBdO1xuXHRcdHZhciBkaXN0YW5jZVVuaWZvcm1zID0gVW5pZm9ybXNVdGlscy5jbG9uZSggZGlzdGFuY2VTaGFkZXIudW5pZm9ybXMgKTtcblxuXHRcdGZvciAoIHZhciBpID0gMDsgaSAhPT0gX051bWJlck9mTWF0ZXJpYWxWYXJpYW50czsgKysgaSApIHtcblxuXHRcdFx0dmFyIHVzZU1vcnBoaW5nID0gKCBpICYgX01vcnBoaW5nRmxhZyApICE9PSAwO1xuXHRcdFx0dmFyIHVzZVNraW5uaW5nID0gKCBpICYgX1NraW5uaW5nRmxhZyApICE9PSAwO1xuXG5cdFx0XHR2YXIgZGVwdGhNYXRlcmlhbCA9IGRlcHRoTWF0ZXJpYWxUZW1wbGF0ZS5jbG9uZSgpO1xuXHRcdFx0ZGVwdGhNYXRlcmlhbC5tb3JwaFRhcmdldHMgPSB1c2VNb3JwaGluZztcblx0XHRcdGRlcHRoTWF0ZXJpYWwuc2tpbm5pbmcgPSB1c2VTa2lubmluZztcblxuXHRcdFx0X2RlcHRoTWF0ZXJpYWxzWyBpIF0gPSBkZXB0aE1hdGVyaWFsO1xuXG5cdFx0XHR2YXIgZGlzdGFuY2VNYXRlcmlhbCA9IG5ldyBTaGFkZXJNYXRlcmlhbCgge1xuXHRcdFx0XHRkZWZpbmVzOiB7XG5cdFx0XHRcdFx0J1VTRV9TSEFET1dNQVAnOiAnJ1xuXHRcdFx0XHR9LFxuXHRcdFx0XHR1bmlmb3JtczogZGlzdGFuY2VVbmlmb3Jtcyxcblx0XHRcdFx0dmVydGV4U2hhZGVyOiBkaXN0YW5jZVNoYWRlci52ZXJ0ZXhTaGFkZXIsXG5cdFx0XHRcdGZyYWdtZW50U2hhZGVyOiBkaXN0YW5jZVNoYWRlci5mcmFnbWVudFNoYWRlcixcblx0XHRcdFx0bW9ycGhUYXJnZXRzOiB1c2VNb3JwaGluZyxcblx0XHRcdFx0c2tpbm5pbmc6IHVzZVNraW5uaW5nLFxuXHRcdFx0XHRjbGlwcGluZzogdHJ1ZVxuXHRcdFx0fSApO1xuXG5cdFx0XHRfZGlzdGFuY2VNYXRlcmlhbHNbIGkgXSA9IGRpc3RhbmNlTWF0ZXJpYWw7XG5cblx0XHR9XG5cblx0XHQvL1xuXG5cdFx0dmFyIHNjb3BlID0gdGhpcztcblxuXHRcdHRoaXMuZW5hYmxlZCA9IGZhbHNlO1xuXG5cdFx0dGhpcy5hdXRvVXBkYXRlID0gdHJ1ZTtcblx0XHR0aGlzLm5lZWRzVXBkYXRlID0gZmFsc2U7XG5cblx0XHR0aGlzLnR5cGUgPSBQQ0ZTaGFkb3dNYXA7XG5cblx0XHR0aGlzLnJlbmRlclJldmVyc2VTaWRlZCA9IHRydWU7XG5cdFx0dGhpcy5yZW5kZXJTaW5nbGVTaWRlZCA9IHRydWU7XG5cblx0XHR0aGlzLnJlbmRlciA9IGZ1bmN0aW9uICggc2NlbmUsIGNhbWVyYSApIHtcblxuXHRcdFx0aWYgKCBzY29wZS5lbmFibGVkID09PSBmYWxzZSApIHJldHVybjtcblx0XHRcdGlmICggc2NvcGUuYXV0b1VwZGF0ZSA9PT0gZmFsc2UgJiYgc2NvcGUubmVlZHNVcGRhdGUgPT09IGZhbHNlICkgcmV0dXJuO1xuXG5cdFx0XHRpZiAoIF9saWdodFNoYWRvd3MubGVuZ3RoID09PSAwICkgcmV0dXJuO1xuXG5cdFx0XHQvLyBTZXQgR0wgc3RhdGUgZm9yIGRlcHRoIG1hcC5cblx0XHRcdF9zdGF0ZS5idWZmZXJzLmNvbG9yLnNldENsZWFyKCAxLCAxLCAxLCAxICk7XG5cdFx0XHRfc3RhdGUuZGlzYWJsZSggX2dsLkJMRU5EICk7XG5cdFx0XHRfc3RhdGUuc2V0RGVwdGhUZXN0KCB0cnVlICk7XG5cdFx0XHRfc3RhdGUuc2V0U2Npc3NvclRlc3QoIGZhbHNlICk7XG5cblx0XHRcdC8vIHJlbmRlciBkZXB0aCBtYXBcblxuXHRcdFx0dmFyIGZhY2VDb3VudCwgaXNQb2ludExpZ2h0O1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGlsID0gX2xpZ2h0U2hhZG93cy5sZW5ndGg7IGkgPCBpbDsgaSArKyApIHtcblxuXHRcdFx0XHR2YXIgbGlnaHQgPSBfbGlnaHRTaGFkb3dzWyBpIF07XG5cdFx0XHRcdHZhciBzaGFkb3cgPSBsaWdodC5zaGFkb3c7XG5cblx0XHRcdFx0aWYgKCBzaGFkb3cgPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMU2hhZG93TWFwOicsIGxpZ2h0LCAnaGFzIG5vIHNoYWRvdy4nICk7XG5cdFx0XHRcdFx0Y29udGludWU7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBzaGFkb3dDYW1lcmEgPSBzaGFkb3cuY2FtZXJhO1xuXG5cdFx0XHRcdF9zaGFkb3dNYXBTaXplLmNvcHkoIHNoYWRvdy5tYXBTaXplICk7XG5cdFx0XHRcdF9zaGFkb3dNYXBTaXplLm1pbiggX21heFNoYWRvd01hcFNpemUgKTtcblxuXHRcdFx0XHRpZiAoIGxpZ2h0ICYmIGxpZ2h0LmlzUG9pbnRMaWdodCApIHtcblxuXHRcdFx0XHRcdGZhY2VDb3VudCA9IDY7XG5cdFx0XHRcdFx0aXNQb2ludExpZ2h0ID0gdHJ1ZTtcblxuXHRcdFx0XHRcdHZhciB2cFdpZHRoID0gX3NoYWRvd01hcFNpemUueDtcblx0XHRcdFx0XHR2YXIgdnBIZWlnaHQgPSBfc2hhZG93TWFwU2l6ZS55O1xuXG5cdFx0XHRcdFx0Ly8gVGhlc2Ugdmlld3BvcnRzIG1hcCBhIGN1YmUtbWFwIG9udG8gYSAyRCB0ZXh0dXJlIHdpdGggdGhlXG5cdFx0XHRcdFx0Ly8gZm9sbG93aW5nIG9yaWVudGF0aW9uOlxuXHRcdFx0XHRcdC8vXG5cdFx0XHRcdFx0Ly8gIHh6WFpcblx0XHRcdFx0XHQvLyAgIHkgWVxuXHRcdFx0XHRcdC8vXG5cdFx0XHRcdFx0Ly8gWCAtIFBvc2l0aXZlIHggZGlyZWN0aW9uXG5cdFx0XHRcdFx0Ly8geCAtIE5lZ2F0aXZlIHggZGlyZWN0aW9uXG5cdFx0XHRcdFx0Ly8gWSAtIFBvc2l0aXZlIHkgZGlyZWN0aW9uXG5cdFx0XHRcdFx0Ly8geSAtIE5lZ2F0aXZlIHkgZGlyZWN0aW9uXG5cdFx0XHRcdFx0Ly8gWiAtIFBvc2l0aXZlIHogZGlyZWN0aW9uXG5cdFx0XHRcdFx0Ly8geiAtIE5lZ2F0aXZlIHogZGlyZWN0aW9uXG5cblx0XHRcdFx0XHQvLyBwb3NpdGl2ZSBYXG5cdFx0XHRcdFx0Y3ViZTJEVmlld1BvcnRzWyAwIF0uc2V0KCB2cFdpZHRoICogMiwgdnBIZWlnaHQsIHZwV2lkdGgsIHZwSGVpZ2h0ICk7XG5cdFx0XHRcdFx0Ly8gbmVnYXRpdmUgWFxuXHRcdFx0XHRcdGN1YmUyRFZpZXdQb3J0c1sgMSBdLnNldCggMCwgdnBIZWlnaHQsIHZwV2lkdGgsIHZwSGVpZ2h0ICk7XG5cdFx0XHRcdFx0Ly8gcG9zaXRpdmUgWlxuXHRcdFx0XHRcdGN1YmUyRFZpZXdQb3J0c1sgMiBdLnNldCggdnBXaWR0aCAqIDMsIHZwSGVpZ2h0LCB2cFdpZHRoLCB2cEhlaWdodCApO1xuXHRcdFx0XHRcdC8vIG5lZ2F0aXZlIFpcblx0XHRcdFx0XHRjdWJlMkRWaWV3UG9ydHNbIDMgXS5zZXQoIHZwV2lkdGgsIHZwSGVpZ2h0LCB2cFdpZHRoLCB2cEhlaWdodCApO1xuXHRcdFx0XHRcdC8vIHBvc2l0aXZlIFlcblx0XHRcdFx0XHRjdWJlMkRWaWV3UG9ydHNbIDQgXS5zZXQoIHZwV2lkdGggKiAzLCAwLCB2cFdpZHRoLCB2cEhlaWdodCApO1xuXHRcdFx0XHRcdC8vIG5lZ2F0aXZlIFlcblx0XHRcdFx0XHRjdWJlMkRWaWV3UG9ydHNbIDUgXS5zZXQoIHZwV2lkdGgsIDAsIHZwV2lkdGgsIHZwSGVpZ2h0ICk7XG5cblx0XHRcdFx0XHRfc2hhZG93TWFwU2l6ZS54ICo9IDQuMDtcblx0XHRcdFx0XHRfc2hhZG93TWFwU2l6ZS55ICo9IDIuMDtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0ZmFjZUNvdW50ID0gMTtcblx0XHRcdFx0XHRpc1BvaW50TGlnaHQgPSBmYWxzZTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCBzaGFkb3cubWFwID09PSBudWxsICkge1xuXG5cdFx0XHRcdFx0dmFyIHBhcnMgPSB7IG1pbkZpbHRlcjogTmVhcmVzdEZpbHRlciwgbWFnRmlsdGVyOiBOZWFyZXN0RmlsdGVyLCBmb3JtYXQ6IFJHQkFGb3JtYXQgfTtcblxuXHRcdFx0XHRcdHNoYWRvdy5tYXAgPSBuZXcgV2ViR0xSZW5kZXJUYXJnZXQoIF9zaGFkb3dNYXBTaXplLngsIF9zaGFkb3dNYXBTaXplLnksIHBhcnMgKTtcblxuXHRcdFx0XHRcdHNoYWRvd0NhbWVyYS51cGRhdGVQcm9qZWN0aW9uTWF0cml4KCk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggc2hhZG93LmlzU3BvdExpZ2h0U2hhZG93ICkge1xuXG5cdFx0XHRcdFx0c2hhZG93LnVwZGF0ZSggbGlnaHQgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gVE9ETyAoYWJlbG5hdGlvbiAvIHNhbS1nLXN0ZWVsKTogaXMgdGhpcyBuZWVkZWQ/XG5cdFx0XHRcdGlmIChzaGFkb3cgJiYgc2hhZG93LmlzUmVjdEFyZWFMaWdodFNoYWRvdyApIHtcblxuXHRcdFx0XHRcdHNoYWRvdy51cGRhdGUoIGxpZ2h0ICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBzaGFkb3dNYXAgPSBzaGFkb3cubWFwO1xuXHRcdFx0XHR2YXIgc2hhZG93TWF0cml4ID0gc2hhZG93Lm1hdHJpeDtcblxuXHRcdFx0XHRfbGlnaHRQb3NpdGlvbldvcmxkLnNldEZyb21NYXRyaXhQb3NpdGlvbiggbGlnaHQubWF0cml4V29ybGQgKTtcblx0XHRcdFx0c2hhZG93Q2FtZXJhLnBvc2l0aW9uLmNvcHkoIF9saWdodFBvc2l0aW9uV29ybGQgKTtcblxuXHRcdFx0XHRfcmVuZGVyZXIuc2V0UmVuZGVyVGFyZ2V0KCBzaGFkb3dNYXAgKTtcblx0XHRcdFx0X3JlbmRlcmVyLmNsZWFyKCk7XG5cblx0XHRcdFx0Ly8gcmVuZGVyIHNoYWRvdyBtYXAgZm9yIGVhY2ggY3ViZSBmYWNlIChpZiBvbW5pLWRpcmVjdGlvbmFsKSBvclxuXHRcdFx0XHQvLyBydW4gYSBzaW5nbGUgcGFzcyBpZiBub3RcblxuXHRcdFx0XHRmb3IgKCB2YXIgZmFjZSA9IDA7IGZhY2UgPCBmYWNlQ291bnQ7IGZhY2UgKysgKSB7XG5cblx0XHRcdFx0XHRpZiAoIGlzUG9pbnRMaWdodCApIHtcblxuXHRcdFx0XHRcdFx0X2xvb2tUYXJnZXQuY29weSggc2hhZG93Q2FtZXJhLnBvc2l0aW9uICk7XG5cdFx0XHRcdFx0XHRfbG9va1RhcmdldC5hZGQoIGN1YmVEaXJlY3Rpb25zWyBmYWNlIF0gKTtcblx0XHRcdFx0XHRcdHNoYWRvd0NhbWVyYS51cC5jb3B5KCBjdWJlVXBzWyBmYWNlIF0gKTtcblx0XHRcdFx0XHRcdHNoYWRvd0NhbWVyYS5sb29rQXQoIF9sb29rVGFyZ2V0ICk7XG5cblx0XHRcdFx0XHRcdHZhciB2cERpbWVuc2lvbnMgPSBjdWJlMkRWaWV3UG9ydHNbIGZhY2UgXTtcblx0XHRcdFx0XHRcdF9zdGF0ZS52aWV3cG9ydCggdnBEaW1lbnNpb25zICk7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRfbG9va1RhcmdldC5zZXRGcm9tTWF0cml4UG9zaXRpb24oIGxpZ2h0LnRhcmdldC5tYXRyaXhXb3JsZCApO1xuXHRcdFx0XHRcdFx0c2hhZG93Q2FtZXJhLmxvb2tBdCggX2xvb2tUYXJnZXQgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdHNoYWRvd0NhbWVyYS51cGRhdGVNYXRyaXhXb3JsZCgpO1xuXHRcdFx0XHRcdHNoYWRvd0NhbWVyYS5tYXRyaXhXb3JsZEludmVyc2UuZ2V0SW52ZXJzZSggc2hhZG93Q2FtZXJhLm1hdHJpeFdvcmxkICk7XG5cblx0XHRcdFx0XHQvLyBjb21wdXRlIHNoYWRvdyBtYXRyaXhcblxuXHRcdFx0XHRcdHNoYWRvd01hdHJpeC5zZXQoXG5cdFx0XHRcdFx0XHQwLjUsIDAuMCwgMC4wLCAwLjUsXG5cdFx0XHRcdFx0XHQwLjAsIDAuNSwgMC4wLCAwLjUsXG5cdFx0XHRcdFx0XHQwLjAsIDAuMCwgMC41LCAwLjUsXG5cdFx0XHRcdFx0XHQwLjAsIDAuMCwgMC4wLCAxLjBcblx0XHRcdFx0XHQpO1xuXG5cdFx0XHRcdFx0c2hhZG93TWF0cml4Lm11bHRpcGx5KCBzaGFkb3dDYW1lcmEucHJvamVjdGlvbk1hdHJpeCApO1xuXHRcdFx0XHRcdHNoYWRvd01hdHJpeC5tdWx0aXBseSggc2hhZG93Q2FtZXJhLm1hdHJpeFdvcmxkSW52ZXJzZSApO1xuXG5cdFx0XHRcdFx0Ly8gdXBkYXRlIGNhbWVyYSBtYXRyaWNlcyBhbmQgZnJ1c3R1bVxuXG5cdFx0XHRcdFx0X3Byb2pTY3JlZW5NYXRyaXgubXVsdGlwbHlNYXRyaWNlcyggc2hhZG93Q2FtZXJhLnByb2plY3Rpb25NYXRyaXgsIHNoYWRvd0NhbWVyYS5tYXRyaXhXb3JsZEludmVyc2UgKTtcblx0XHRcdFx0XHRfZnJ1c3R1bS5zZXRGcm9tTWF0cml4KCBfcHJvalNjcmVlbk1hdHJpeCApO1xuXG5cdFx0XHRcdFx0Ly8gc2V0IG9iamVjdCBtYXRyaWNlcyAmIGZydXN0dW0gY3VsbGluZ1xuXG5cdFx0XHRcdFx0X3JlbmRlckxpc3QubGVuZ3RoID0gMDtcblxuXHRcdFx0XHRcdHByb2plY3RPYmplY3QoIHNjZW5lLCBjYW1lcmEsIHNoYWRvd0NhbWVyYSApO1xuXG5cdFx0XHRcdFx0Ly8gcmVuZGVyIHNoYWRvdyBtYXBcblx0XHRcdFx0XHQvLyByZW5kZXIgcmVndWxhciBvYmplY3RzXG5cblx0XHRcdFx0XHRmb3IgKCB2YXIgaiA9IDAsIGpsID0gX3JlbmRlckxpc3QubGVuZ3RoOyBqIDwgamw7IGogKysgKSB7XG5cblx0XHRcdFx0XHRcdHZhciBvYmplY3QgPSBfcmVuZGVyTGlzdFsgaiBdO1xuXHRcdFx0XHRcdFx0dmFyIGdlb21ldHJ5ID0gX29iamVjdHMudXBkYXRlKCBvYmplY3QgKTtcblx0XHRcdFx0XHRcdHZhciBtYXRlcmlhbCA9IG9iamVjdC5tYXRlcmlhbDtcblxuXHRcdFx0XHRcdFx0aWYgKCBtYXRlcmlhbCAmJiBtYXRlcmlhbC5pc011bHRpTWF0ZXJpYWwgKSB7XG5cblx0XHRcdFx0XHRcdFx0dmFyIGdyb3VwcyA9IGdlb21ldHJ5Lmdyb3Vwcztcblx0XHRcdFx0XHRcdFx0dmFyIG1hdGVyaWFscyA9IG1hdGVyaWFsLm1hdGVyaWFscztcblxuXHRcdFx0XHRcdFx0XHRmb3IgKCB2YXIgayA9IDAsIGtsID0gZ3JvdXBzLmxlbmd0aDsgayA8IGtsOyBrICsrICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0dmFyIGdyb3VwID0gZ3JvdXBzWyBrIF07XG5cdFx0XHRcdFx0XHRcdFx0dmFyIGdyb3VwTWF0ZXJpYWwgPSBtYXRlcmlhbHNbIGdyb3VwLm1hdGVyaWFsSW5kZXggXTtcblxuXHRcdFx0XHRcdFx0XHRcdGlmICggZ3JvdXBNYXRlcmlhbC52aXNpYmxlID09PSB0cnVlICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0XHR2YXIgZGVwdGhNYXRlcmlhbCA9IGdldERlcHRoTWF0ZXJpYWwoIG9iamVjdCwgZ3JvdXBNYXRlcmlhbCwgaXNQb2ludExpZ2h0LCBfbGlnaHRQb3NpdGlvbldvcmxkICk7XG5cdFx0XHRcdFx0XHRcdFx0XHRfcmVuZGVyZXIucmVuZGVyQnVmZmVyRGlyZWN0KCBzaGFkb3dDYW1lcmEsIG51bGwsIGdlb21ldHJ5LCBkZXB0aE1hdGVyaWFsLCBvYmplY3QsIGdyb3VwICk7XG5cblx0XHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRcdHZhciBkZXB0aE1hdGVyaWFsID0gZ2V0RGVwdGhNYXRlcmlhbCggb2JqZWN0LCBtYXRlcmlhbCwgaXNQb2ludExpZ2h0LCBfbGlnaHRQb3NpdGlvbldvcmxkICk7XG5cdFx0XHRcdFx0XHRcdF9yZW5kZXJlci5yZW5kZXJCdWZmZXJEaXJlY3QoIHNoYWRvd0NhbWVyYSwgbnVsbCwgZ2VvbWV0cnksIGRlcHRoTWF0ZXJpYWwsIG9iamVjdCwgbnVsbCApO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdC8vIFJlc3RvcmUgR0wgc3RhdGUuXG5cdFx0XHR2YXIgY2xlYXJDb2xvciA9IF9yZW5kZXJlci5nZXRDbGVhckNvbG9yKCksXG5cdFx0XHRjbGVhckFscGhhID0gX3JlbmRlcmVyLmdldENsZWFyQWxwaGEoKTtcblx0XHRcdF9yZW5kZXJlci5zZXRDbGVhckNvbG9yKCBjbGVhckNvbG9yLCBjbGVhckFscGhhICk7XG5cblx0XHRcdHNjb3BlLm5lZWRzVXBkYXRlID0gZmFsc2U7XG5cblx0XHR9O1xuXG5cdFx0ZnVuY3Rpb24gZ2V0RGVwdGhNYXRlcmlhbCggb2JqZWN0LCBtYXRlcmlhbCwgaXNQb2ludExpZ2h0LCBsaWdodFBvc2l0aW9uV29ybGQgKSB7XG5cblx0XHRcdHZhciBnZW9tZXRyeSA9IG9iamVjdC5nZW9tZXRyeTtcblxuXHRcdFx0dmFyIHJlc3VsdCA9IG51bGw7XG5cblx0XHRcdHZhciBtYXRlcmlhbFZhcmlhbnRzID0gX2RlcHRoTWF0ZXJpYWxzO1xuXHRcdFx0dmFyIGN1c3RvbU1hdGVyaWFsID0gb2JqZWN0LmN1c3RvbURlcHRoTWF0ZXJpYWw7XG5cblx0XHRcdGlmICggaXNQb2ludExpZ2h0ICkge1xuXG5cdFx0XHRcdG1hdGVyaWFsVmFyaWFudHMgPSBfZGlzdGFuY2VNYXRlcmlhbHM7XG5cdFx0XHRcdGN1c3RvbU1hdGVyaWFsID0gb2JqZWN0LmN1c3RvbURpc3RhbmNlTWF0ZXJpYWw7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCAhIGN1c3RvbU1hdGVyaWFsICkge1xuXG5cdFx0XHRcdHZhciB1c2VNb3JwaGluZyA9IGZhbHNlO1xuXG5cdFx0XHRcdGlmICggbWF0ZXJpYWwubW9ycGhUYXJnZXRzICkge1xuXG5cdFx0XHRcdFx0aWYgKCBnZW9tZXRyeSAmJiBnZW9tZXRyeS5pc0J1ZmZlckdlb21ldHJ5ICkge1xuXG5cdFx0XHRcdFx0XHR1c2VNb3JwaGluZyA9IGdlb21ldHJ5Lm1vcnBoQXR0cmlidXRlcyAmJiBnZW9tZXRyeS5tb3JwaEF0dHJpYnV0ZXMucG9zaXRpb24gJiYgZ2VvbWV0cnkubW9ycGhBdHRyaWJ1dGVzLnBvc2l0aW9uLmxlbmd0aCA+IDA7XG5cblx0XHRcdFx0XHR9IGVsc2UgaWYgKCBnZW9tZXRyeSAmJiBnZW9tZXRyeS5pc0dlb21ldHJ5ICkge1xuXG5cdFx0XHRcdFx0XHR1c2VNb3JwaGluZyA9IGdlb21ldHJ5Lm1vcnBoVGFyZ2V0cyAmJiBnZW9tZXRyeS5tb3JwaFRhcmdldHMubGVuZ3RoID4gMDtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0dmFyIHVzZVNraW5uaW5nID0gb2JqZWN0LmlzU2tpbm5lZE1lc2ggJiYgbWF0ZXJpYWwuc2tpbm5pbmc7XG5cblx0XHRcdFx0dmFyIHZhcmlhbnRJbmRleCA9IDA7XG5cblx0XHRcdFx0aWYgKCB1c2VNb3JwaGluZyApIHZhcmlhbnRJbmRleCB8PSBfTW9ycGhpbmdGbGFnO1xuXHRcdFx0XHRpZiAoIHVzZVNraW5uaW5nICkgdmFyaWFudEluZGV4IHw9IF9Ta2lubmluZ0ZsYWc7XG5cblx0XHRcdFx0cmVzdWx0ID0gbWF0ZXJpYWxWYXJpYW50c1sgdmFyaWFudEluZGV4IF07XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0cmVzdWx0ID0gY3VzdG9tTWF0ZXJpYWw7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBfcmVuZGVyZXIubG9jYWxDbGlwcGluZ0VuYWJsZWQgJiZcblx0XHRcdFx0IG1hdGVyaWFsLmNsaXBTaGFkb3dzID09PSB0cnVlICYmXG5cdFx0XHRcdFx0bWF0ZXJpYWwuY2xpcHBpbmdQbGFuZXMubGVuZ3RoICE9PSAwICkge1xuXG5cdFx0XHRcdC8vIGluIHRoaXMgY2FzZSB3ZSBuZWVkIGEgdW5pcXVlIG1hdGVyaWFsIGluc3RhbmNlIHJlZmxlY3RpbmcgdGhlXG5cdFx0XHRcdC8vIGFwcHJvcHJpYXRlIHN0YXRlXG5cblx0XHRcdFx0dmFyIGtleUEgPSByZXN1bHQudXVpZCwga2V5QiA9IG1hdGVyaWFsLnV1aWQ7XG5cblx0XHRcdFx0dmFyIG1hdGVyaWFsc0ZvclZhcmlhbnQgPSBfbWF0ZXJpYWxDYWNoZVsga2V5QSBdO1xuXG5cdFx0XHRcdGlmICggbWF0ZXJpYWxzRm9yVmFyaWFudCA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0bWF0ZXJpYWxzRm9yVmFyaWFudCA9IHt9O1xuXHRcdFx0XHRcdF9tYXRlcmlhbENhY2hlWyBrZXlBIF0gPSBtYXRlcmlhbHNGb3JWYXJpYW50O1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR2YXIgY2FjaGVkTWF0ZXJpYWwgPSBtYXRlcmlhbHNGb3JWYXJpYW50WyBrZXlCIF07XG5cblx0XHRcdFx0aWYgKCBjYWNoZWRNYXRlcmlhbCA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0Y2FjaGVkTWF0ZXJpYWwgPSByZXN1bHQuY2xvbmUoKTtcblx0XHRcdFx0XHRtYXRlcmlhbHNGb3JWYXJpYW50WyBrZXlCIF0gPSBjYWNoZWRNYXRlcmlhbDtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmVzdWx0ID0gY2FjaGVkTWF0ZXJpYWw7XG5cblx0XHRcdH1cblxuXHRcdFx0cmVzdWx0LnZpc2libGUgPSBtYXRlcmlhbC52aXNpYmxlO1xuXHRcdFx0cmVzdWx0LndpcmVmcmFtZSA9IG1hdGVyaWFsLndpcmVmcmFtZTtcblxuXHRcdFx0dmFyIHNpZGUgPSBtYXRlcmlhbC5zaWRlO1xuXG5cdFx0XHRpZiAoIHNjb3BlLnJlbmRlclNpbmdsZVNpZGVkICYmIHNpZGUgPT0gRG91YmxlU2lkZSApIHtcblxuXHRcdFx0XHRzaWRlID0gRnJvbnRTaWRlO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggc2NvcGUucmVuZGVyUmV2ZXJzZVNpZGVkICkge1xuXG5cdFx0XHRcdGlmICggc2lkZSA9PT0gRnJvbnRTaWRlICkgc2lkZSA9IEJhY2tTaWRlO1xuXHRcdFx0XHRlbHNlIGlmICggc2lkZSA9PT0gQmFja1NpZGUgKSBzaWRlID0gRnJvbnRTaWRlO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJlc3VsdC5zaWRlID0gc2lkZTtcblxuXHRcdFx0cmVzdWx0LmNsaXBTaGFkb3dzID0gbWF0ZXJpYWwuY2xpcFNoYWRvd3M7XG5cdFx0XHRyZXN1bHQuY2xpcHBpbmdQbGFuZXMgPSBtYXRlcmlhbC5jbGlwcGluZ1BsYW5lcztcblxuXHRcdFx0cmVzdWx0LndpcmVmcmFtZUxpbmV3aWR0aCA9IG1hdGVyaWFsLndpcmVmcmFtZUxpbmV3aWR0aDtcblx0XHRcdHJlc3VsdC5saW5ld2lkdGggPSBtYXRlcmlhbC5saW5ld2lkdGg7XG5cblx0XHRcdGlmICggaXNQb2ludExpZ2h0ICYmIHJlc3VsdC51bmlmb3Jtcy5saWdodFBvcyAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdHJlc3VsdC51bmlmb3Jtcy5saWdodFBvcy52YWx1ZS5jb3B5KCBsaWdodFBvc2l0aW9uV29ybGQgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gcmVzdWx0O1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gcHJvamVjdE9iamVjdCggb2JqZWN0LCBjYW1lcmEsIHNoYWRvd0NhbWVyYSApIHtcblxuXHRcdFx0aWYgKCBvYmplY3QudmlzaWJsZSA9PT0gZmFsc2UgKSByZXR1cm47XG5cblx0XHRcdHZhciB2aXNpYmxlID0gKCBvYmplY3QubGF5ZXJzLm1hc2sgJiBjYW1lcmEubGF5ZXJzLm1hc2sgKSAhPT0gMDtcblxuXHRcdFx0aWYgKCB2aXNpYmxlICYmICggb2JqZWN0LmlzTWVzaCB8fCBvYmplY3QuaXNMaW5lIHx8IG9iamVjdC5pc1BvaW50cyApICkge1xuXG5cdFx0XHRcdGlmICggb2JqZWN0LmNhc3RTaGFkb3cgJiYgKCBvYmplY3QuZnJ1c3R1bUN1bGxlZCA9PT0gZmFsc2UgfHwgX2ZydXN0dW0uaW50ZXJzZWN0c09iamVjdCggb2JqZWN0ICkgPT09IHRydWUgKSApIHtcblxuXHRcdFx0XHRcdHZhciBtYXRlcmlhbCA9IG9iamVjdC5tYXRlcmlhbDtcblxuXHRcdFx0XHRcdGlmICggbWF0ZXJpYWwudmlzaWJsZSA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRcdFx0b2JqZWN0Lm1vZGVsVmlld01hdHJpeC5tdWx0aXBseU1hdHJpY2VzKCBzaGFkb3dDYW1lcmEubWF0cml4V29ybGRJbnZlcnNlLCBvYmplY3QubWF0cml4V29ybGQgKTtcblx0XHRcdFx0XHRcdF9yZW5kZXJMaXN0LnB1c2goIG9iamVjdCApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgY2hpbGRyZW4gPSBvYmplY3QuY2hpbGRyZW47XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGNoaWxkcmVuLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0cHJvamVjdE9iamVjdCggY2hpbGRyZW5bIGkgXSwgY2FtZXJhLCBzaGFkb3dDYW1lcmEgKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogQGF1dGhvciBiaG91c3RvbiAvIGh0dHA6Ly9jbGFyYS5pb1xuXHQgKi9cblxuXHRmdW5jdGlvbiBSYXkoIG9yaWdpbiwgZGlyZWN0aW9uICkge1xuXG5cdFx0dGhpcy5vcmlnaW4gPSAoIG9yaWdpbiAhPT0gdW5kZWZpbmVkICkgPyBvcmlnaW4gOiBuZXcgVmVjdG9yMygpO1xuXHRcdHRoaXMuZGlyZWN0aW9uID0gKCBkaXJlY3Rpb24gIT09IHVuZGVmaW5lZCApID8gZGlyZWN0aW9uIDogbmV3IFZlY3RvcjMoKTtcblxuXHR9XG5cblx0UmF5LnByb3RvdHlwZSA9IHtcblxuXHRcdGNvbnN0cnVjdG9yOiBSYXksXG5cblx0XHRzZXQ6IGZ1bmN0aW9uICggb3JpZ2luLCBkaXJlY3Rpb24gKSB7XG5cblx0XHRcdHRoaXMub3JpZ2luLmNvcHkoIG9yaWdpbiApO1xuXHRcdFx0dGhpcy5kaXJlY3Rpb24uY29weSggZGlyZWN0aW9uICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiBuZXcgdGhpcy5jb25zdHJ1Y3RvcigpLmNvcHkoIHRoaXMgKTtcblxuXHRcdH0sXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIHJheSApIHtcblxuXHRcdFx0dGhpcy5vcmlnaW4uY29weSggcmF5Lm9yaWdpbiApO1xuXHRcdFx0dGhpcy5kaXJlY3Rpb24uY29weSggcmF5LmRpcmVjdGlvbiApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRhdDogZnVuY3Rpb24gKCB0LCBvcHRpb25hbFRhcmdldCApIHtcblxuXHRcdFx0dmFyIHJlc3VsdCA9IG9wdGlvbmFsVGFyZ2V0IHx8IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdHJldHVybiByZXN1bHQuY29weSggdGhpcy5kaXJlY3Rpb24gKS5tdWx0aXBseVNjYWxhciggdCApLmFkZCggdGhpcy5vcmlnaW4gKTtcblxuXHRcdH0sXG5cblx0XHRsb29rQXQ6IGZ1bmN0aW9uICggdiApIHtcblxuXHRcdFx0dGhpcy5kaXJlY3Rpb24uY29weSggdiApLnN1YiggdGhpcy5vcmlnaW4gKS5ub3JtYWxpemUoKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0cmVjYXN0OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB2MSA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiByZWNhc3QoIHQgKSB7XG5cblx0XHRcdFx0dGhpcy5vcmlnaW4uY29weSggdGhpcy5hdCggdCwgdjEgKSApO1xuXG5cdFx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0Y2xvc2VzdFBvaW50VG9Qb2ludDogZnVuY3Rpb24gKCBwb2ludCwgb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdHZhciByZXN1bHQgPSBvcHRpb25hbFRhcmdldCB8fCBuZXcgVmVjdG9yMygpO1xuXHRcdFx0cmVzdWx0LnN1YlZlY3RvcnMoIHBvaW50LCB0aGlzLm9yaWdpbiApO1xuXHRcdFx0dmFyIGRpcmVjdGlvbkRpc3RhbmNlID0gcmVzdWx0LmRvdCggdGhpcy5kaXJlY3Rpb24gKTtcblxuXHRcdFx0aWYgKCBkaXJlY3Rpb25EaXN0YW5jZSA8IDAgKSB7XG5cblx0XHRcdFx0cmV0dXJuIHJlc3VsdC5jb3B5KCB0aGlzLm9yaWdpbiApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiByZXN1bHQuY29weSggdGhpcy5kaXJlY3Rpb24gKS5tdWx0aXBseVNjYWxhciggZGlyZWN0aW9uRGlzdGFuY2UgKS5hZGQoIHRoaXMub3JpZ2luICk7XG5cblx0XHR9LFxuXG5cdFx0ZGlzdGFuY2VUb1BvaW50OiBmdW5jdGlvbiAoIHBvaW50ICkge1xuXG5cdFx0XHRyZXR1cm4gTWF0aC5zcXJ0KCB0aGlzLmRpc3RhbmNlU3FUb1BvaW50KCBwb2ludCApICk7XG5cblx0XHR9LFxuXG5cdFx0ZGlzdGFuY2VTcVRvUG9pbnQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIHYxID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIGRpc3RhbmNlU3FUb1BvaW50KCBwb2ludCApIHtcblxuXHRcdFx0XHR2YXIgZGlyZWN0aW9uRGlzdGFuY2UgPSB2MS5zdWJWZWN0b3JzKCBwb2ludCwgdGhpcy5vcmlnaW4gKS5kb3QoIHRoaXMuZGlyZWN0aW9uICk7XG5cblx0XHRcdFx0Ly8gcG9pbnQgYmVoaW5kIHRoZSByYXlcblxuXHRcdFx0XHRpZiAoIGRpcmVjdGlvbkRpc3RhbmNlIDwgMCApIHtcblxuXHRcdFx0XHRcdHJldHVybiB0aGlzLm9yaWdpbi5kaXN0YW5jZVRvU3F1YXJlZCggcG9pbnQgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0djEuY29weSggdGhpcy5kaXJlY3Rpb24gKS5tdWx0aXBseVNjYWxhciggZGlyZWN0aW9uRGlzdGFuY2UgKS5hZGQoIHRoaXMub3JpZ2luICk7XG5cblx0XHRcdFx0cmV0dXJuIHYxLmRpc3RhbmNlVG9TcXVhcmVkKCBwb2ludCApO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0ZGlzdGFuY2VTcVRvU2VnbWVudDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgc2VnQ2VudGVyID0gbmV3IFZlY3RvcjMoKTtcblx0XHRcdHZhciBzZWdEaXIgPSBuZXcgVmVjdG9yMygpO1xuXHRcdFx0dmFyIGRpZmYgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gZGlzdGFuY2VTcVRvU2VnbWVudCggdjAsIHYxLCBvcHRpb25hbFBvaW50T25SYXksIG9wdGlvbmFsUG9pbnRPblNlZ21lbnQgKSB7XG5cblx0XHRcdFx0Ly8gZnJvbSBodHRwOi8vd3d3Lmdlb21ldHJpY3Rvb2xzLmNvbS9HVEVuZ2luZS9JbmNsdWRlL01hdGhlbWF0aWNzL0d0ZURpc3RSYXlTZWdtZW50Lmhcblx0XHRcdFx0Ly8gSXQgcmV0dXJucyB0aGUgbWluIGRpc3RhbmNlIGJldHdlZW4gdGhlIHJheSBhbmQgdGhlIHNlZ21lbnRcblx0XHRcdFx0Ly8gZGVmaW5lZCBieSB2MCBhbmQgdjFcblx0XHRcdFx0Ly8gSXQgY2FuIGFsc28gc2V0IHR3byBvcHRpb25hbCB0YXJnZXRzIDpcblx0XHRcdFx0Ly8gLSBUaGUgY2xvc2VzdCBwb2ludCBvbiB0aGUgcmF5XG5cdFx0XHRcdC8vIC0gVGhlIGNsb3Nlc3QgcG9pbnQgb24gdGhlIHNlZ21lbnRcblxuXHRcdFx0XHRzZWdDZW50ZXIuY29weSggdjAgKS5hZGQoIHYxICkubXVsdGlwbHlTY2FsYXIoIDAuNSApO1xuXHRcdFx0XHRzZWdEaXIuY29weSggdjEgKS5zdWIoIHYwICkubm9ybWFsaXplKCk7XG5cdFx0XHRcdGRpZmYuY29weSggdGhpcy5vcmlnaW4gKS5zdWIoIHNlZ0NlbnRlciApO1xuXG5cdFx0XHRcdHZhciBzZWdFeHRlbnQgPSB2MC5kaXN0YW5jZVRvKCB2MSApICogMC41O1xuXHRcdFx0XHR2YXIgYTAxID0gLSB0aGlzLmRpcmVjdGlvbi5kb3QoIHNlZ0RpciApO1xuXHRcdFx0XHR2YXIgYjAgPSBkaWZmLmRvdCggdGhpcy5kaXJlY3Rpb24gKTtcblx0XHRcdFx0dmFyIGIxID0gLSBkaWZmLmRvdCggc2VnRGlyICk7XG5cdFx0XHRcdHZhciBjID0gZGlmZi5sZW5ndGhTcSgpO1xuXHRcdFx0XHR2YXIgZGV0ID0gTWF0aC5hYnMoIDEgLSBhMDEgKiBhMDEgKTtcblx0XHRcdFx0dmFyIHMwLCBzMSwgc3FyRGlzdCwgZXh0RGV0O1xuXG5cdFx0XHRcdGlmICggZGV0ID4gMCApIHtcblxuXHRcdFx0XHRcdC8vIFRoZSByYXkgYW5kIHNlZ21lbnQgYXJlIG5vdCBwYXJhbGxlbC5cblxuXHRcdFx0XHRcdHMwID0gYTAxICogYjEgLSBiMDtcblx0XHRcdFx0XHRzMSA9IGEwMSAqIGIwIC0gYjE7XG5cdFx0XHRcdFx0ZXh0RGV0ID0gc2VnRXh0ZW50ICogZGV0O1xuXG5cdFx0XHRcdFx0aWYgKCBzMCA+PSAwICkge1xuXG5cdFx0XHRcdFx0XHRpZiAoIHMxID49IC0gZXh0RGV0ICkge1xuXG5cdFx0XHRcdFx0XHRcdGlmICggczEgPD0gZXh0RGV0ICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0Ly8gcmVnaW9uIDBcblx0XHRcdFx0XHRcdFx0XHQvLyBNaW5pbXVtIGF0IGludGVyaW9yIHBvaW50cyBvZiByYXkgYW5kIHNlZ21lbnQuXG5cblx0XHRcdFx0XHRcdFx0XHR2YXIgaW52RGV0ID0gMSAvIGRldDtcblx0XHRcdFx0XHRcdFx0XHRzMCAqPSBpbnZEZXQ7XG5cdFx0XHRcdFx0XHRcdFx0czEgKj0gaW52RGV0O1xuXHRcdFx0XHRcdFx0XHRcdHNxckRpc3QgPSBzMCAqICggczAgKyBhMDEgKiBzMSArIDIgKiBiMCApICsgczEgKiAoIGEwMSAqIHMwICsgczEgKyAyICogYjEgKSArIGM7XG5cblx0XHRcdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0XHRcdC8vIHJlZ2lvbiAxXG5cblx0XHRcdFx0XHRcdFx0XHRzMSA9IHNlZ0V4dGVudDtcblx0XHRcdFx0XHRcdFx0XHRzMCA9IE1hdGgubWF4KCAwLCAtICggYTAxICogczEgKyBiMCApICk7XG5cdFx0XHRcdFx0XHRcdFx0c3FyRGlzdCA9IC0gczAgKiBzMCArIHMxICogKCBzMSArIDIgKiBiMSApICsgYztcblxuXHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdFx0Ly8gcmVnaW9uIDVcblxuXHRcdFx0XHRcdFx0XHRzMSA9IC0gc2VnRXh0ZW50O1xuXHRcdFx0XHRcdFx0XHRzMCA9IE1hdGgubWF4KCAwLCAtICggYTAxICogczEgKyBiMCApICk7XG5cdFx0XHRcdFx0XHRcdHNxckRpc3QgPSAtIHMwICogczAgKyBzMSAqICggczEgKyAyICogYjEgKSArIGM7XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdGlmICggczEgPD0gLSBleHREZXQgKSB7XG5cblx0XHRcdFx0XHRcdFx0Ly8gcmVnaW9uIDRcblxuXHRcdFx0XHRcdFx0XHRzMCA9IE1hdGgubWF4KCAwLCAtICggLSBhMDEgKiBzZWdFeHRlbnQgKyBiMCApICk7XG5cdFx0XHRcdFx0XHRcdHMxID0gKCBzMCA+IDAgKSA/IC0gc2VnRXh0ZW50IDogTWF0aC5taW4oIE1hdGgubWF4KCAtIHNlZ0V4dGVudCwgLSBiMSApLCBzZWdFeHRlbnQgKTtcblx0XHRcdFx0XHRcdFx0c3FyRGlzdCA9IC0gczAgKiBzMCArIHMxICogKCBzMSArIDIgKiBiMSApICsgYztcblxuXHRcdFx0XHRcdFx0fSBlbHNlIGlmICggczEgPD0gZXh0RGV0ICkge1xuXG5cdFx0XHRcdFx0XHRcdC8vIHJlZ2lvbiAzXG5cblx0XHRcdFx0XHRcdFx0czAgPSAwO1xuXHRcdFx0XHRcdFx0XHRzMSA9IE1hdGgubWluKCBNYXRoLm1heCggLSBzZWdFeHRlbnQsIC0gYjEgKSwgc2VnRXh0ZW50ICk7XG5cdFx0XHRcdFx0XHRcdHNxckRpc3QgPSBzMSAqICggczEgKyAyICogYjEgKSArIGM7XG5cblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdFx0Ly8gcmVnaW9uIDJcblxuXHRcdFx0XHRcdFx0XHRzMCA9IE1hdGgubWF4KCAwLCAtICggYTAxICogc2VnRXh0ZW50ICsgYjAgKSApO1xuXHRcdFx0XHRcdFx0XHRzMSA9ICggczAgPiAwICkgPyBzZWdFeHRlbnQgOiBNYXRoLm1pbiggTWF0aC5tYXgoIC0gc2VnRXh0ZW50LCAtIGIxICksIHNlZ0V4dGVudCApO1xuXHRcdFx0XHRcdFx0XHRzcXJEaXN0ID0gLSBzMCAqIHMwICsgczEgKiAoIHMxICsgMiAqIGIxICkgKyBjO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdC8vIFJheSBhbmQgc2VnbWVudCBhcmUgcGFyYWxsZWwuXG5cblx0XHRcdFx0XHRzMSA9ICggYTAxID4gMCApID8gLSBzZWdFeHRlbnQgOiBzZWdFeHRlbnQ7XG5cdFx0XHRcdFx0czAgPSBNYXRoLm1heCggMCwgLSAoIGEwMSAqIHMxICsgYjAgKSApO1xuXHRcdFx0XHRcdHNxckRpc3QgPSAtIHMwICogczAgKyBzMSAqICggczEgKyAyICogYjEgKSArIGM7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggb3B0aW9uYWxQb2ludE9uUmF5ICkge1xuXG5cdFx0XHRcdFx0b3B0aW9uYWxQb2ludE9uUmF5LmNvcHkoIHRoaXMuZGlyZWN0aW9uICkubXVsdGlwbHlTY2FsYXIoIHMwICkuYWRkKCB0aGlzLm9yaWdpbiApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoIG9wdGlvbmFsUG9pbnRPblNlZ21lbnQgKSB7XG5cblx0XHRcdFx0XHRvcHRpb25hbFBvaW50T25TZWdtZW50LmNvcHkoIHNlZ0RpciApLm11bHRpcGx5U2NhbGFyKCBzMSApLmFkZCggc2VnQ2VudGVyICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJldHVybiBzcXJEaXN0O1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0aW50ZXJzZWN0U3BoZXJlOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB2MSA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBpbnRlcnNlY3RTcGhlcmUoIHNwaGVyZSwgb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdFx0djEuc3ViVmVjdG9ycyggc3BoZXJlLmNlbnRlciwgdGhpcy5vcmlnaW4gKTtcblx0XHRcdFx0dmFyIHRjYSA9IHYxLmRvdCggdGhpcy5kaXJlY3Rpb24gKTtcblx0XHRcdFx0dmFyIGQyID0gdjEuZG90KCB2MSApIC0gdGNhICogdGNhO1xuXHRcdFx0XHR2YXIgcmFkaXVzMiA9IHNwaGVyZS5yYWRpdXMgKiBzcGhlcmUucmFkaXVzO1xuXG5cdFx0XHRcdGlmICggZDIgPiByYWRpdXMyICkgcmV0dXJuIG51bGw7XG5cblx0XHRcdFx0dmFyIHRoYyA9IE1hdGguc3FydCggcmFkaXVzMiAtIGQyICk7XG5cblx0XHRcdFx0Ly8gdDAgPSBmaXJzdCBpbnRlcnNlY3QgcG9pbnQgLSBlbnRyYW5jZSBvbiBmcm9udCBvZiBzcGhlcmVcblx0XHRcdFx0dmFyIHQwID0gdGNhIC0gdGhjO1xuXG5cdFx0XHRcdC8vIHQxID0gc2Vjb25kIGludGVyc2VjdCBwb2ludCAtIGV4aXQgcG9pbnQgb24gYmFjayBvZiBzcGhlcmVcblx0XHRcdFx0dmFyIHQxID0gdGNhICsgdGhjO1xuXG5cdFx0XHRcdC8vIHRlc3QgdG8gc2VlIGlmIGJvdGggdDAgYW5kIHQxIGFyZSBiZWhpbmQgdGhlIHJheSAtIGlmIHNvLCByZXR1cm4gbnVsbFxuXHRcdFx0XHRpZiAoIHQwIDwgMCAmJiB0MSA8IDAgKSByZXR1cm4gbnVsbDtcblxuXHRcdFx0XHQvLyB0ZXN0IHRvIHNlZSBpZiB0MCBpcyBiZWhpbmQgdGhlIHJheTpcblx0XHRcdFx0Ly8gaWYgaXQgaXMsIHRoZSByYXkgaXMgaW5zaWRlIHRoZSBzcGhlcmUsIHNvIHJldHVybiB0aGUgc2Vjb25kIGV4aXQgcG9pbnQgc2NhbGVkIGJ5IHQxLFxuXHRcdFx0XHQvLyBpbiBvcmRlciB0byBhbHdheXMgcmV0dXJuIGFuIGludGVyc2VjdCBwb2ludCB0aGF0IGlzIGluIGZyb250IG9mIHRoZSByYXkuXG5cdFx0XHRcdGlmICggdDAgPCAwICkgcmV0dXJuIHRoaXMuYXQoIHQxLCBvcHRpb25hbFRhcmdldCApO1xuXG5cdFx0XHRcdC8vIGVsc2UgdDAgaXMgaW4gZnJvbnQgb2YgdGhlIHJheSwgc28gcmV0dXJuIHRoZSBmaXJzdCBjb2xsaXNpb24gcG9pbnQgc2NhbGVkIGJ5IHQwXG5cdFx0XHRcdHJldHVybiB0aGlzLmF0KCB0MCwgb3B0aW9uYWxUYXJnZXQgKTtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdGludGVyc2VjdHNTcGhlcmU6IGZ1bmN0aW9uICggc3BoZXJlICkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5kaXN0YW5jZVRvUG9pbnQoIHNwaGVyZS5jZW50ZXIgKSA8PSBzcGhlcmUucmFkaXVzO1xuXG5cdFx0fSxcblxuXHRcdGRpc3RhbmNlVG9QbGFuZTogZnVuY3Rpb24gKCBwbGFuZSApIHtcblxuXHRcdFx0dmFyIGRlbm9taW5hdG9yID0gcGxhbmUubm9ybWFsLmRvdCggdGhpcy5kaXJlY3Rpb24gKTtcblxuXHRcdFx0aWYgKCBkZW5vbWluYXRvciA9PT0gMCApIHtcblxuXHRcdFx0XHQvLyBsaW5lIGlzIGNvcGxhbmFyLCByZXR1cm4gb3JpZ2luXG5cdFx0XHRcdGlmICggcGxhbmUuZGlzdGFuY2VUb1BvaW50KCB0aGlzLm9yaWdpbiApID09PSAwICkge1xuXG5cdFx0XHRcdFx0cmV0dXJuIDA7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIE51bGwgaXMgcHJlZmVyYWJsZSB0byB1bmRlZmluZWQgc2luY2UgdW5kZWZpbmVkIG1lYW5zLi4uLiBpdCBpcyB1bmRlZmluZWRcblxuXHRcdFx0XHRyZXR1cm4gbnVsbDtcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgdCA9IC0gKCB0aGlzLm9yaWdpbi5kb3QoIHBsYW5lLm5vcm1hbCApICsgcGxhbmUuY29uc3RhbnQgKSAvIGRlbm9taW5hdG9yO1xuXG5cdFx0XHQvLyBSZXR1cm4gaWYgdGhlIHJheSBuZXZlciBpbnRlcnNlY3RzIHRoZSBwbGFuZVxuXG5cdFx0XHRyZXR1cm4gdCA+PSAwID8gdCA6ICBudWxsO1xuXG5cdFx0fSxcblxuXHRcdGludGVyc2VjdFBsYW5lOiBmdW5jdGlvbiAoIHBsYW5lLCBvcHRpb25hbFRhcmdldCApIHtcblxuXHRcdFx0dmFyIHQgPSB0aGlzLmRpc3RhbmNlVG9QbGFuZSggcGxhbmUgKTtcblxuXHRcdFx0aWYgKCB0ID09PSBudWxsICkge1xuXG5cdFx0XHRcdHJldHVybiBudWxsO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzLmF0KCB0LCBvcHRpb25hbFRhcmdldCApO1xuXG5cdFx0fSxcblxuXG5cblx0XHRpbnRlcnNlY3RzUGxhbmU6IGZ1bmN0aW9uICggcGxhbmUgKSB7XG5cblx0XHRcdC8vIGNoZWNrIGlmIHRoZSByYXkgbGllcyBvbiB0aGUgcGxhbmUgZmlyc3RcblxuXHRcdFx0dmFyIGRpc3RUb1BvaW50ID0gcGxhbmUuZGlzdGFuY2VUb1BvaW50KCB0aGlzLm9yaWdpbiApO1xuXG5cdFx0XHRpZiAoIGRpc3RUb1BvaW50ID09PSAwICkge1xuXG5cdFx0XHRcdHJldHVybiB0cnVlO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBkZW5vbWluYXRvciA9IHBsYW5lLm5vcm1hbC5kb3QoIHRoaXMuZGlyZWN0aW9uICk7XG5cblx0XHRcdGlmICggZGVub21pbmF0b3IgKiBkaXN0VG9Qb2ludCA8IDAgKSB7XG5cblx0XHRcdFx0cmV0dXJuIHRydWU7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gcmF5IG9yaWdpbiBpcyBiZWhpbmQgdGhlIHBsYW5lIChhbmQgaXMgcG9pbnRpbmcgYmVoaW5kIGl0KVxuXG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cblx0XHR9LFxuXG5cdFx0aW50ZXJzZWN0Qm94OiBmdW5jdGlvbiAoIGJveCwgb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdHZhciB0bWluLCB0bWF4LCB0eW1pbiwgdHltYXgsIHR6bWluLCB0em1heDtcblxuXHRcdFx0dmFyIGludmRpcnggPSAxIC8gdGhpcy5kaXJlY3Rpb24ueCxcblx0XHRcdFx0aW52ZGlyeSA9IDEgLyB0aGlzLmRpcmVjdGlvbi55LFxuXHRcdFx0XHRpbnZkaXJ6ID0gMSAvIHRoaXMuZGlyZWN0aW9uLno7XG5cblx0XHRcdHZhciBvcmlnaW4gPSB0aGlzLm9yaWdpbjtcblxuXHRcdFx0aWYgKCBpbnZkaXJ4ID49IDAgKSB7XG5cblx0XHRcdFx0dG1pbiA9ICggYm94Lm1pbi54IC0gb3JpZ2luLnggKSAqIGludmRpcng7XG5cdFx0XHRcdHRtYXggPSAoIGJveC5tYXgueCAtIG9yaWdpbi54ICkgKiBpbnZkaXJ4O1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdHRtaW4gPSAoIGJveC5tYXgueCAtIG9yaWdpbi54ICkgKiBpbnZkaXJ4O1xuXHRcdFx0XHR0bWF4ID0gKCBib3gubWluLnggLSBvcmlnaW4ueCApICogaW52ZGlyeDtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIGludmRpcnkgPj0gMCApIHtcblxuXHRcdFx0XHR0eW1pbiA9ICggYm94Lm1pbi55IC0gb3JpZ2luLnkgKSAqIGludmRpcnk7XG5cdFx0XHRcdHR5bWF4ID0gKCBib3gubWF4LnkgLSBvcmlnaW4ueSApICogaW52ZGlyeTtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHR0eW1pbiA9ICggYm94Lm1heC55IC0gb3JpZ2luLnkgKSAqIGludmRpcnk7XG5cdFx0XHRcdHR5bWF4ID0gKCBib3gubWluLnkgLSBvcmlnaW4ueSApICogaW52ZGlyeTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoICggdG1pbiA+IHR5bWF4ICkgfHwgKCB0eW1pbiA+IHRtYXggKSApIHJldHVybiBudWxsO1xuXG5cdFx0XHQvLyBUaGVzZSBsaW5lcyBhbHNvIGhhbmRsZSB0aGUgY2FzZSB3aGVyZSB0bWluIG9yIHRtYXggaXMgTmFOXG5cdFx0XHQvLyAocmVzdWx0IG9mIDAgKiBJbmZpbml0eSkuIHggIT09IHggcmV0dXJucyB0cnVlIGlmIHggaXMgTmFOXG5cblx0XHRcdGlmICggdHltaW4gPiB0bWluIHx8IHRtaW4gIT09IHRtaW4gKSB0bWluID0gdHltaW47XG5cblx0XHRcdGlmICggdHltYXggPCB0bWF4IHx8IHRtYXggIT09IHRtYXggKSB0bWF4ID0gdHltYXg7XG5cblx0XHRcdGlmICggaW52ZGlyeiA+PSAwICkge1xuXG5cdFx0XHRcdHR6bWluID0gKCBib3gubWluLnogLSBvcmlnaW4ueiApICogaW52ZGlyejtcblx0XHRcdFx0dHptYXggPSAoIGJveC5tYXgueiAtIG9yaWdpbi56ICkgKiBpbnZkaXJ6O1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdHR6bWluID0gKCBib3gubWF4LnogLSBvcmlnaW4ueiApICogaW52ZGlyejtcblx0XHRcdFx0dHptYXggPSAoIGJveC5taW4ueiAtIG9yaWdpbi56ICkgKiBpbnZkaXJ6O1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggKCB0bWluID4gdHptYXggKSB8fCAoIHR6bWluID4gdG1heCApICkgcmV0dXJuIG51bGw7XG5cblx0XHRcdGlmICggdHptaW4gPiB0bWluIHx8IHRtaW4gIT09IHRtaW4gKSB0bWluID0gdHptaW47XG5cblx0XHRcdGlmICggdHptYXggPCB0bWF4IHx8IHRtYXggIT09IHRtYXggKSB0bWF4ID0gdHptYXg7XG5cblx0XHRcdC8vcmV0dXJuIHBvaW50IGNsb3Nlc3QgdG8gdGhlIHJheSAocG9zaXRpdmUgc2lkZSlcblxuXHRcdFx0aWYgKCB0bWF4IDwgMCApIHJldHVybiBudWxsO1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5hdCggdG1pbiA+PSAwID8gdG1pbiA6IHRtYXgsIG9wdGlvbmFsVGFyZ2V0ICk7XG5cblx0XHR9LFxuXG5cdFx0aW50ZXJzZWN0c0JveDogKCBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB2ID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIGludGVyc2VjdHNCb3goIGJveCApIHtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcy5pbnRlcnNlY3RCb3goIGJveCwgdiApICE9PSBudWxsO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSApKCksXG5cblx0XHRpbnRlcnNlY3RUcmlhbmdsZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHQvLyBDb21wdXRlIHRoZSBvZmZzZXQgb3JpZ2luLCBlZGdlcywgYW5kIG5vcm1hbC5cblx0XHRcdHZhciBkaWZmID0gbmV3IFZlY3RvcjMoKTtcblx0XHRcdHZhciBlZGdlMSA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHR2YXIgZWRnZTIgPSBuZXcgVmVjdG9yMygpO1xuXHRcdFx0dmFyIG5vcm1hbCA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBpbnRlcnNlY3RUcmlhbmdsZSggYSwgYiwgYywgYmFja2ZhY2VDdWxsaW5nLCBvcHRpb25hbFRhcmdldCApIHtcblxuXHRcdFx0XHQvLyBmcm9tIGh0dHA6Ly93d3cuZ2VvbWV0cmljdG9vbHMuY29tL0dURW5naW5lL0luY2x1ZGUvTWF0aGVtYXRpY3MvR3RlSW50clJheTNUcmlhbmdsZTMuaFxuXG5cdFx0XHRcdGVkZ2UxLnN1YlZlY3RvcnMoIGIsIGEgKTtcblx0XHRcdFx0ZWRnZTIuc3ViVmVjdG9ycyggYywgYSApO1xuXHRcdFx0XHRub3JtYWwuY3Jvc3NWZWN0b3JzKCBlZGdlMSwgZWRnZTIgKTtcblxuXHRcdFx0XHQvLyBTb2x2ZSBRICsgdCpEID0gYjEqRTEgKyBiMipFMiAoUSA9IGtEaWZmLCBEID0gcmF5IGRpcmVjdGlvbixcblx0XHRcdFx0Ly8gRTEgPSBrRWRnZTEsIEUyID0ga0VkZ2UyLCBOID0gQ3Jvc3MoRTEsRTIpKSBieVxuXHRcdFx0XHQvLyAgIHxEb3QoRCxOKXwqYjEgPSBzaWduKERvdChELE4pKSpEb3QoRCxDcm9zcyhRLEUyKSlcblx0XHRcdFx0Ly8gICB8RG90KEQsTil8KmIyID0gc2lnbihEb3QoRCxOKSkqRG90KEQsQ3Jvc3MoRTEsUSkpXG5cdFx0XHRcdC8vICAgfERvdChELE4pfCp0ID0gLXNpZ24oRG90KEQsTikpKkRvdChRLE4pXG5cdFx0XHRcdHZhciBEZE4gPSB0aGlzLmRpcmVjdGlvbi5kb3QoIG5vcm1hbCApO1xuXHRcdFx0XHR2YXIgc2lnbjtcblxuXHRcdFx0XHRpZiAoIERkTiA+IDAgKSB7XG5cblx0XHRcdFx0XHRpZiAoIGJhY2tmYWNlQ3VsbGluZyApIHJldHVybiBudWxsO1xuXHRcdFx0XHRcdHNpZ24gPSAxO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAoIERkTiA8IDAgKSB7XG5cblx0XHRcdFx0XHRzaWduID0gLSAxO1xuXHRcdFx0XHRcdERkTiA9IC0gRGROO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRyZXR1cm4gbnVsbDtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0ZGlmZi5zdWJWZWN0b3JzKCB0aGlzLm9yaWdpbiwgYSApO1xuXHRcdFx0XHR2YXIgRGRReEUyID0gc2lnbiAqIHRoaXMuZGlyZWN0aW9uLmRvdCggZWRnZTIuY3Jvc3NWZWN0b3JzKCBkaWZmLCBlZGdlMiApICk7XG5cblx0XHRcdFx0Ly8gYjEgPCAwLCBubyBpbnRlcnNlY3Rpb25cblx0XHRcdFx0aWYgKCBEZFF4RTIgPCAwICkge1xuXG5cdFx0XHRcdFx0cmV0dXJuIG51bGw7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBEZEUxeFEgPSBzaWduICogdGhpcy5kaXJlY3Rpb24uZG90KCBlZGdlMS5jcm9zcyggZGlmZiApICk7XG5cblx0XHRcdFx0Ly8gYjIgPCAwLCBubyBpbnRlcnNlY3Rpb25cblx0XHRcdFx0aWYgKCBEZEUxeFEgPCAwICkge1xuXG5cdFx0XHRcdFx0cmV0dXJuIG51bGw7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIGIxK2IyID4gMSwgbm8gaW50ZXJzZWN0aW9uXG5cdFx0XHRcdGlmICggRGRReEUyICsgRGRFMXhRID4gRGROICkge1xuXG5cdFx0XHRcdFx0cmV0dXJuIG51bGw7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIExpbmUgaW50ZXJzZWN0cyB0cmlhbmdsZSwgY2hlY2sgaWYgcmF5IGRvZXMuXG5cdFx0XHRcdHZhciBRZE4gPSAtIHNpZ24gKiBkaWZmLmRvdCggbm9ybWFsICk7XG5cblx0XHRcdFx0Ly8gdCA8IDAsIG5vIGludGVyc2VjdGlvblxuXHRcdFx0XHRpZiAoIFFkTiA8IDAgKSB7XG5cblx0XHRcdFx0XHRyZXR1cm4gbnVsbDtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gUmF5IGludGVyc2VjdHMgdHJpYW5nbGUuXG5cdFx0XHRcdHJldHVybiB0aGlzLmF0KCBRZE4gLyBEZE4sIG9wdGlvbmFsVGFyZ2V0ICk7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRhcHBseU1hdHJpeDQ6IGZ1bmN0aW9uICggbWF0cml4NCApIHtcblxuXHRcdFx0dGhpcy5kaXJlY3Rpb24uYWRkKCB0aGlzLm9yaWdpbiApLmFwcGx5TWF0cml4NCggbWF0cml4NCApO1xuXHRcdFx0dGhpcy5vcmlnaW4uYXBwbHlNYXRyaXg0KCBtYXRyaXg0ICk7XG5cdFx0XHR0aGlzLmRpcmVjdGlvbi5zdWIoIHRoaXMub3JpZ2luICk7XG5cdFx0XHR0aGlzLmRpcmVjdGlvbi5ub3JtYWxpemUoKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0ZXF1YWxzOiBmdW5jdGlvbiAoIHJheSApIHtcblxuXHRcdFx0cmV0dXJuIHJheS5vcmlnaW4uZXF1YWxzKCB0aGlzLm9yaWdpbiApICYmIHJheS5kaXJlY3Rpb24uZXF1YWxzKCB0aGlzLmRpcmVjdGlvbiApO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqIEBhdXRob3IgV2VzdExhbmdsZXkgLyBodHRwOi8vZ2l0aHViLmNvbS9XZXN0TGFuZ2xleVxuXHQgKiBAYXV0aG9yIGJob3VzdG9uIC8gaHR0cDovL2NsYXJhLmlvXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEV1bGVyKCB4LCB5LCB6LCBvcmRlciApIHtcblxuXHRcdHRoaXMuX3ggPSB4IHx8IDA7XG5cdFx0dGhpcy5feSA9IHkgfHwgMDtcblx0XHR0aGlzLl96ID0geiB8fCAwO1xuXHRcdHRoaXMuX29yZGVyID0gb3JkZXIgfHwgRXVsZXIuRGVmYXVsdE9yZGVyO1xuXG5cdH1cblxuXHRFdWxlci5Sb3RhdGlvbk9yZGVycyA9IFsgJ1hZWicsICdZWlgnLCAnWlhZJywgJ1haWScsICdZWFonLCAnWllYJyBdO1xuXG5cdEV1bGVyLkRlZmF1bHRPcmRlciA9ICdYWVonO1xuXG5cdEV1bGVyLnByb3RvdHlwZSA9IHtcblxuXHRcdGNvbnN0cnVjdG9yOiBFdWxlcixcblxuXHRcdGlzRXVsZXI6IHRydWUsXG5cblx0XHRnZXQgeCAoKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLl94O1xuXG5cdFx0fSxcblxuXHRcdHNldCB4ICggdmFsdWUgKSB7XG5cblx0XHRcdHRoaXMuX3ggPSB2YWx1ZTtcblx0XHRcdHRoaXMub25DaGFuZ2VDYWxsYmFjaygpO1xuXG5cdFx0fSxcblxuXHRcdGdldCB5ICgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuX3k7XG5cblx0XHR9LFxuXG5cdFx0c2V0IHkgKCB2YWx1ZSApIHtcblxuXHRcdFx0dGhpcy5feSA9IHZhbHVlO1xuXHRcdFx0dGhpcy5vbkNoYW5nZUNhbGxiYWNrKCk7XG5cblx0XHR9LFxuXG5cdFx0Z2V0IHogKCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5fejtcblxuXHRcdH0sXG5cblx0XHRzZXQgeiAoIHZhbHVlICkge1xuXG5cdFx0XHR0aGlzLl96ID0gdmFsdWU7XG5cdFx0XHR0aGlzLm9uQ2hhbmdlQ2FsbGJhY2soKTtcblxuXHRcdH0sXG5cblx0XHRnZXQgb3JkZXIgKCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5fb3JkZXI7XG5cblx0XHR9LFxuXG5cdFx0c2V0IG9yZGVyICggdmFsdWUgKSB7XG5cblx0XHRcdHRoaXMuX29yZGVyID0gdmFsdWU7XG5cdFx0XHR0aGlzLm9uQ2hhbmdlQ2FsbGJhY2soKTtcblxuXHRcdH0sXG5cblx0XHRzZXQ6IGZ1bmN0aW9uICggeCwgeSwgeiwgb3JkZXIgKSB7XG5cblx0XHRcdHRoaXMuX3ggPSB4O1xuXHRcdFx0dGhpcy5feSA9IHk7XG5cdFx0XHR0aGlzLl96ID0gejtcblx0XHRcdHRoaXMuX29yZGVyID0gb3JkZXIgfHwgdGhpcy5fb3JkZXI7XG5cblx0XHRcdHRoaXMub25DaGFuZ2VDYWxsYmFjaygpO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjbG9uZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IHRoaXMuY29uc3RydWN0b3IoIHRoaXMuX3gsIHRoaXMuX3ksIHRoaXMuX3osIHRoaXMuX29yZGVyICk7XG5cblx0XHR9LFxuXG5cdFx0Y29weTogZnVuY3Rpb24gKCBldWxlciApIHtcblxuXHRcdFx0dGhpcy5feCA9IGV1bGVyLl94O1xuXHRcdFx0dGhpcy5feSA9IGV1bGVyLl95O1xuXHRcdFx0dGhpcy5feiA9IGV1bGVyLl96O1xuXHRcdFx0dGhpcy5fb3JkZXIgPSBldWxlci5fb3JkZXI7XG5cblx0XHRcdHRoaXMub25DaGFuZ2VDYWxsYmFjaygpO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRGcm9tUm90YXRpb25NYXRyaXg6IGZ1bmN0aW9uICggbSwgb3JkZXIsIHVwZGF0ZSApIHtcblxuXHRcdFx0dmFyIGNsYW1wID0gX01hdGguY2xhbXA7XG5cblx0XHRcdC8vIGFzc3VtZXMgdGhlIHVwcGVyIDN4MyBvZiBtIGlzIGEgcHVyZSByb3RhdGlvbiBtYXRyaXggKGkuZSwgdW5zY2FsZWQpXG5cblx0XHRcdHZhciB0ZSA9IG0uZWxlbWVudHM7XG5cdFx0XHR2YXIgbTExID0gdGVbIDAgXSwgbTEyID0gdGVbIDQgXSwgbTEzID0gdGVbIDggXTtcblx0XHRcdHZhciBtMjEgPSB0ZVsgMSBdLCBtMjIgPSB0ZVsgNSBdLCBtMjMgPSB0ZVsgOSBdO1xuXHRcdFx0dmFyIG0zMSA9IHRlWyAyIF0sIG0zMiA9IHRlWyA2IF0sIG0zMyA9IHRlWyAxMCBdO1xuXG5cdFx0XHRvcmRlciA9IG9yZGVyIHx8IHRoaXMuX29yZGVyO1xuXG5cdFx0XHRpZiAoIG9yZGVyID09PSAnWFlaJyApIHtcblxuXHRcdFx0XHR0aGlzLl95ID0gTWF0aC5hc2luKCBjbGFtcCggbTEzLCAtIDEsIDEgKSApO1xuXG5cdFx0XHRcdGlmICggTWF0aC5hYnMoIG0xMyApIDwgMC45OTk5OSApIHtcblxuXHRcdFx0XHRcdHRoaXMuX3ggPSBNYXRoLmF0YW4yKCAtIG0yMywgbTMzICk7XG5cdFx0XHRcdFx0dGhpcy5feiA9IE1hdGguYXRhbjIoIC0gbTEyLCBtMTEgKTtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0dGhpcy5feCA9IE1hdGguYXRhbjIoIG0zMiwgbTIyICk7XG5cdFx0XHRcdFx0dGhpcy5feiA9IDA7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9IGVsc2UgaWYgKCBvcmRlciA9PT0gJ1lYWicgKSB7XG5cblx0XHRcdFx0dGhpcy5feCA9IE1hdGguYXNpbiggLSBjbGFtcCggbTIzLCAtIDEsIDEgKSApO1xuXG5cdFx0XHRcdGlmICggTWF0aC5hYnMoIG0yMyApIDwgMC45OTk5OSApIHtcblxuXHRcdFx0XHRcdHRoaXMuX3kgPSBNYXRoLmF0YW4yKCBtMTMsIG0zMyApO1xuXHRcdFx0XHRcdHRoaXMuX3ogPSBNYXRoLmF0YW4yKCBtMjEsIG0yMiApO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHR0aGlzLl95ID0gTWF0aC5hdGFuMiggLSBtMzEsIG0xMSApO1xuXHRcdFx0XHRcdHRoaXMuX3ogPSAwO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fSBlbHNlIGlmICggb3JkZXIgPT09ICdaWFknICkge1xuXG5cdFx0XHRcdHRoaXMuX3ggPSBNYXRoLmFzaW4oIGNsYW1wKCBtMzIsIC0gMSwgMSApICk7XG5cblx0XHRcdFx0aWYgKCBNYXRoLmFicyggbTMyICkgPCAwLjk5OTk5ICkge1xuXG5cdFx0XHRcdFx0dGhpcy5feSA9IE1hdGguYXRhbjIoIC0gbTMxLCBtMzMgKTtcblx0XHRcdFx0XHR0aGlzLl96ID0gTWF0aC5hdGFuMiggLSBtMTIsIG0yMiApO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHR0aGlzLl95ID0gMDtcblx0XHRcdFx0XHR0aGlzLl96ID0gTWF0aC5hdGFuMiggbTIxLCBtMTEgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH0gZWxzZSBpZiAoIG9yZGVyID09PSAnWllYJyApIHtcblxuXHRcdFx0XHR0aGlzLl95ID0gTWF0aC5hc2luKCAtIGNsYW1wKCBtMzEsIC0gMSwgMSApICk7XG5cblx0XHRcdFx0aWYgKCBNYXRoLmFicyggbTMxICkgPCAwLjk5OTk5ICkge1xuXG5cdFx0XHRcdFx0dGhpcy5feCA9IE1hdGguYXRhbjIoIG0zMiwgbTMzICk7XG5cdFx0XHRcdFx0dGhpcy5feiA9IE1hdGguYXRhbjIoIG0yMSwgbTExICk7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdHRoaXMuX3ggPSAwO1xuXHRcdFx0XHRcdHRoaXMuX3ogPSBNYXRoLmF0YW4yKCAtIG0xMiwgbTIyICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9IGVsc2UgaWYgKCBvcmRlciA9PT0gJ1laWCcgKSB7XG5cblx0XHRcdFx0dGhpcy5feiA9IE1hdGguYXNpbiggY2xhbXAoIG0yMSwgLSAxLCAxICkgKTtcblxuXHRcdFx0XHRpZiAoIE1hdGguYWJzKCBtMjEgKSA8IDAuOTk5OTkgKSB7XG5cblx0XHRcdFx0XHR0aGlzLl94ID0gTWF0aC5hdGFuMiggLSBtMjMsIG0yMiApO1xuXHRcdFx0XHRcdHRoaXMuX3kgPSBNYXRoLmF0YW4yKCAtIG0zMSwgbTExICk7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdHRoaXMuX3ggPSAwO1xuXHRcdFx0XHRcdHRoaXMuX3kgPSBNYXRoLmF0YW4yKCBtMTMsIG0zMyApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fSBlbHNlIGlmICggb3JkZXIgPT09ICdYWlknICkge1xuXG5cdFx0XHRcdHRoaXMuX3ogPSBNYXRoLmFzaW4oIC0gY2xhbXAoIG0xMiwgLSAxLCAxICkgKTtcblxuXHRcdFx0XHRpZiAoIE1hdGguYWJzKCBtMTIgKSA8IDAuOTk5OTkgKSB7XG5cblx0XHRcdFx0XHR0aGlzLl94ID0gTWF0aC5hdGFuMiggbTMyLCBtMjIgKTtcblx0XHRcdFx0XHR0aGlzLl95ID0gTWF0aC5hdGFuMiggbTEzLCBtMTEgKTtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0dGhpcy5feCA9IE1hdGguYXRhbjIoIC0gbTIzLCBtMzMgKTtcblx0XHRcdFx0XHR0aGlzLl95ID0gMDtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuRXVsZXI6IC5zZXRGcm9tUm90YXRpb25NYXRyaXgoKSBnaXZlbiB1bnN1cHBvcnRlZCBvcmRlcjogJyArIG9yZGVyICk7XG5cblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fb3JkZXIgPSBvcmRlcjtcblxuXHRcdFx0aWYgKCB1cGRhdGUgIT09IGZhbHNlICkgdGhpcy5vbkNoYW5nZUNhbGxiYWNrKCk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldEZyb21RdWF0ZXJuaW9uOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBtYXRyaXg7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBzZXRGcm9tUXVhdGVybmlvbiggcSwgb3JkZXIsIHVwZGF0ZSApIHtcblxuXHRcdFx0XHRpZiAoIG1hdHJpeCA9PT0gdW5kZWZpbmVkICkgbWF0cml4ID0gbmV3IE1hdHJpeDQoKTtcblxuXHRcdFx0XHRtYXRyaXgubWFrZVJvdGF0aW9uRnJvbVF1YXRlcm5pb24oIHEgKTtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcy5zZXRGcm9tUm90YXRpb25NYXRyaXgoIG1hdHJpeCwgb3JkZXIsIHVwZGF0ZSApO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0c2V0RnJvbVZlY3RvcjM6IGZ1bmN0aW9uICggdiwgb3JkZXIgKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLnNldCggdi54LCB2LnksIHYueiwgb3JkZXIgfHwgdGhpcy5fb3JkZXIgKTtcblxuXHRcdH0sXG5cblx0XHRyZW9yZGVyOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdC8vIFdBUk5JTkc6IHRoaXMgZGlzY2FyZHMgcmV2b2x1dGlvbiBpbmZvcm1hdGlvbiAtYmhvdXN0b25cblxuXHRcdFx0dmFyIHEgPSBuZXcgUXVhdGVybmlvbigpO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gcmVvcmRlciggbmV3T3JkZXIgKSB7XG5cblx0XHRcdFx0cS5zZXRGcm9tRXVsZXIoIHRoaXMgKTtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcy5zZXRGcm9tUXVhdGVybmlvbiggcSwgbmV3T3JkZXIgKTtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdGVxdWFsczogZnVuY3Rpb24gKCBldWxlciApIHtcblxuXHRcdFx0cmV0dXJuICggZXVsZXIuX3ggPT09IHRoaXMuX3ggKSAmJiAoIGV1bGVyLl95ID09PSB0aGlzLl95ICkgJiYgKCBldWxlci5feiA9PT0gdGhpcy5feiApICYmICggZXVsZXIuX29yZGVyID09PSB0aGlzLl9vcmRlciApO1xuXG5cdFx0fSxcblxuXHRcdGZyb21BcnJheTogZnVuY3Rpb24gKCBhcnJheSApIHtcblxuXHRcdFx0dGhpcy5feCA9IGFycmF5WyAwIF07XG5cdFx0XHR0aGlzLl95ID0gYXJyYXlbIDEgXTtcblx0XHRcdHRoaXMuX3ogPSBhcnJheVsgMiBdO1xuXHRcdFx0aWYgKCBhcnJheVsgMyBdICE9PSB1bmRlZmluZWQgKSB0aGlzLl9vcmRlciA9IGFycmF5WyAzIF07XG5cblx0XHRcdHRoaXMub25DaGFuZ2VDYWxsYmFjaygpO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHR0b0FycmF5OiBmdW5jdGlvbiAoIGFycmF5LCBvZmZzZXQgKSB7XG5cblx0XHRcdGlmICggYXJyYXkgPT09IHVuZGVmaW5lZCApIGFycmF5ID0gW107XG5cdFx0XHRpZiAoIG9mZnNldCA9PT0gdW5kZWZpbmVkICkgb2Zmc2V0ID0gMDtcblxuXHRcdFx0YXJyYXlbIG9mZnNldCBdID0gdGhpcy5feDtcblx0XHRcdGFycmF5WyBvZmZzZXQgKyAxIF0gPSB0aGlzLl95O1xuXHRcdFx0YXJyYXlbIG9mZnNldCArIDIgXSA9IHRoaXMuX3o7XG5cdFx0XHRhcnJheVsgb2Zmc2V0ICsgMyBdID0gdGhpcy5fb3JkZXI7XG5cblx0XHRcdHJldHVybiBhcnJheTtcblxuXHRcdH0sXG5cblx0XHR0b1ZlY3RvcjM6IGZ1bmN0aW9uICggb3B0aW9uYWxSZXN1bHQgKSB7XG5cblx0XHRcdGlmICggb3B0aW9uYWxSZXN1bHQgKSB7XG5cblx0XHRcdFx0cmV0dXJuIG9wdGlvbmFsUmVzdWx0LnNldCggdGhpcy5feCwgdGhpcy5feSwgdGhpcy5feiApO1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdHJldHVybiBuZXcgVmVjdG9yMyggdGhpcy5feCwgdGhpcy5feSwgdGhpcy5feiApO1xuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0b25DaGFuZ2U6IGZ1bmN0aW9uICggY2FsbGJhY2sgKSB7XG5cblx0XHRcdHRoaXMub25DaGFuZ2VDYWxsYmFjayA9IGNhbGxiYWNrO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRvbkNoYW5nZUNhbGxiYWNrOiBmdW5jdGlvbiAoKSB7fVxuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIExheWVycygpIHtcblxuXHRcdHRoaXMubWFzayA9IDE7XG5cblx0fVxuXG5cdExheWVycy5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogTGF5ZXJzLFxuXG5cdFx0c2V0OiBmdW5jdGlvbiAoIGNoYW5uZWwgKSB7XG5cblx0XHRcdHRoaXMubWFzayA9IDEgPDwgY2hhbm5lbDtcblxuXHRcdH0sXG5cblx0XHRlbmFibGU6IGZ1bmN0aW9uICggY2hhbm5lbCApIHtcblxuXHRcdFx0dGhpcy5tYXNrIHw9IDEgPDwgY2hhbm5lbDtcblxuXHRcdH0sXG5cblx0XHR0b2dnbGU6IGZ1bmN0aW9uICggY2hhbm5lbCApIHtcblxuXHRcdFx0dGhpcy5tYXNrIF49IDEgPDwgY2hhbm5lbDtcblxuXHRcdH0sXG5cblx0XHRkaXNhYmxlOiBmdW5jdGlvbiAoIGNoYW5uZWwgKSB7XG5cblx0XHRcdHRoaXMubWFzayAmPSB+ICggMSA8PCBjaGFubmVsICk7XG5cblx0XHR9LFxuXG5cdFx0dGVzdDogZnVuY3Rpb24gKCBsYXllcnMgKSB7XG5cblx0XHRcdHJldHVybiAoIHRoaXMubWFzayAmIGxheWVycy5tYXNrICkgIT09IDA7XG5cblx0XHR9XG5cblx0fTtcblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICogQGF1dGhvciBtaWthZWwgZW10aW5nZXIgLyBodHRwOi8vZ29tby5zZS9cblx0ICogQGF1dGhvciBhbHRlcmVkcSAvIGh0dHA6Ly9hbHRlcmVkcXVhbGlhLmNvbS9cblx0ICogQGF1dGhvciBXZXN0TGFuZ2xleSAvIGh0dHA6Ly9naXRodWIuY29tL1dlc3RMYW5nbGV5XG5cdCAqIEBhdXRob3IgZWxlcGhhbnRhdHdvcmsgLyB3d3cuZWxlcGhhbnRhdHdvcmsuY2hcblx0ICovXG5cblx0dmFyIG9iamVjdDNESWQgPSAwO1xuXG5cdGZ1bmN0aW9uIE9iamVjdDNEKCkge1xuXG5cdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KCB0aGlzLCAnaWQnLCB7IHZhbHVlOiBvYmplY3QzRElkICsrIH0gKTtcblxuXHRcdHRoaXMudXVpZCA9IF9NYXRoLmdlbmVyYXRlVVVJRCgpO1xuXG5cdFx0dGhpcy5uYW1lID0gJyc7XG5cdFx0dGhpcy50eXBlID0gJ09iamVjdDNEJztcblxuXHRcdHRoaXMucGFyZW50ID0gbnVsbDtcblx0XHR0aGlzLmNoaWxkcmVuID0gW107XG5cblx0XHR0aGlzLnVwID0gT2JqZWN0M0QuRGVmYXVsdFVwLmNsb25lKCk7XG5cblx0XHR2YXIgcG9zaXRpb24gPSBuZXcgVmVjdG9yMygpO1xuXHRcdHZhciByb3RhdGlvbiA9IG5ldyBFdWxlcigpO1xuXHRcdHZhciBxdWF0ZXJuaW9uID0gbmV3IFF1YXRlcm5pb24oKTtcblx0XHR2YXIgc2NhbGUgPSBuZXcgVmVjdG9yMyggMSwgMSwgMSApO1xuXG5cdFx0ZnVuY3Rpb24gb25Sb3RhdGlvbkNoYW5nZSgpIHtcblxuXHRcdFx0cXVhdGVybmlvbi5zZXRGcm9tRXVsZXIoIHJvdGF0aW9uLCBmYWxzZSApO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gb25RdWF0ZXJuaW9uQ2hhbmdlKCkge1xuXG5cdFx0XHRyb3RhdGlvbi5zZXRGcm9tUXVhdGVybmlvbiggcXVhdGVybmlvbiwgdW5kZWZpbmVkLCBmYWxzZSApO1xuXG5cdFx0fVxuXG5cdFx0cm90YXRpb24ub25DaGFuZ2UoIG9uUm90YXRpb25DaGFuZ2UgKTtcblx0XHRxdWF0ZXJuaW9uLm9uQ2hhbmdlKCBvblF1YXRlcm5pb25DaGFuZ2UgKTtcblxuXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKCB0aGlzLCB7XG5cdFx0XHRwb3NpdGlvbjoge1xuXHRcdFx0XHRlbnVtZXJhYmxlOiB0cnVlLFxuXHRcdFx0XHR2YWx1ZTogcG9zaXRpb25cblx0XHRcdH0sXG5cdFx0XHRyb3RhdGlvbjoge1xuXHRcdFx0XHRlbnVtZXJhYmxlOiB0cnVlLFxuXHRcdFx0XHR2YWx1ZTogcm90YXRpb25cblx0XHRcdH0sXG5cdFx0XHRxdWF0ZXJuaW9uOiB7XG5cdFx0XHRcdGVudW1lcmFibGU6IHRydWUsXG5cdFx0XHRcdHZhbHVlOiBxdWF0ZXJuaW9uXG5cdFx0XHR9LFxuXHRcdFx0c2NhbGU6IHtcblx0XHRcdFx0ZW51bWVyYWJsZTogdHJ1ZSxcblx0XHRcdFx0dmFsdWU6IHNjYWxlXG5cdFx0XHR9LFxuXHRcdFx0bW9kZWxWaWV3TWF0cml4OiB7XG5cdFx0XHRcdHZhbHVlOiBuZXcgTWF0cml4NCgpXG5cdFx0XHR9LFxuXHRcdFx0bm9ybWFsTWF0cml4OiB7XG5cdFx0XHRcdHZhbHVlOiBuZXcgTWF0cml4MygpXG5cdFx0XHR9XG5cdFx0fSApO1xuXG5cdFx0dGhpcy5tYXRyaXggPSBuZXcgTWF0cml4NCgpO1xuXHRcdHRoaXMubWF0cml4V29ybGQgPSBuZXcgTWF0cml4NCgpO1xuXG5cdFx0dGhpcy5tYXRyaXhBdXRvVXBkYXRlID0gT2JqZWN0M0QuRGVmYXVsdE1hdHJpeEF1dG9VcGRhdGU7XG5cdFx0dGhpcy5tYXRyaXhXb3JsZE5lZWRzVXBkYXRlID0gZmFsc2U7XG5cblx0XHR0aGlzLmxheWVycyA9IG5ldyBMYXllcnMoKTtcblx0XHR0aGlzLnZpc2libGUgPSB0cnVlO1xuXG5cdFx0dGhpcy5jYXN0U2hhZG93ID0gZmFsc2U7XG5cdFx0dGhpcy5yZWNlaXZlU2hhZG93ID0gZmFsc2U7XG5cblx0XHR0aGlzLmZydXN0dW1DdWxsZWQgPSB0cnVlO1xuXHRcdHRoaXMucmVuZGVyT3JkZXIgPSAwO1xuXG5cdFx0dGhpcy51c2VyRGF0YSA9IHt9O1xuXG5cdFx0dGhpcy5vbkJlZm9yZVJlbmRlciA9IGZ1bmN0aW9uICgpIHt9O1xuXHRcdHRoaXMub25BZnRlclJlbmRlciA9IGZ1bmN0aW9uICgpIHt9O1xuXG5cdH1cblxuXHRPYmplY3QzRC5EZWZhdWx0VXAgPSBuZXcgVmVjdG9yMyggMCwgMSwgMCApO1xuXHRPYmplY3QzRC5EZWZhdWx0TWF0cml4QXV0b1VwZGF0ZSA9IHRydWU7XG5cblx0T2JqZWN0LmFzc2lnbiggT2JqZWN0M0QucHJvdG90eXBlLCBFdmVudERpc3BhdGNoZXIucHJvdG90eXBlLCB7XG5cblx0XHRpc09iamVjdDNEOiB0cnVlLFxuXG5cdFx0YXBwbHlNYXRyaXg6IGZ1bmN0aW9uICggbWF0cml4ICkge1xuXG5cdFx0XHR0aGlzLm1hdHJpeC5tdWx0aXBseU1hdHJpY2VzKCBtYXRyaXgsIHRoaXMubWF0cml4ICk7XG5cblx0XHRcdHRoaXMubWF0cml4LmRlY29tcG9zZSggdGhpcy5wb3NpdGlvbiwgdGhpcy5xdWF0ZXJuaW9uLCB0aGlzLnNjYWxlICk7XG5cblx0XHR9LFxuXG5cdFx0c2V0Um90YXRpb25Gcm9tQXhpc0FuZ2xlOiBmdW5jdGlvbiAoIGF4aXMsIGFuZ2xlICkge1xuXG5cdFx0XHQvLyBhc3N1bWVzIGF4aXMgaXMgbm9ybWFsaXplZFxuXG5cdFx0XHR0aGlzLnF1YXRlcm5pb24uc2V0RnJvbUF4aXNBbmdsZSggYXhpcywgYW5nbGUgKTtcblxuXHRcdH0sXG5cblx0XHRzZXRSb3RhdGlvbkZyb21FdWxlcjogZnVuY3Rpb24gKCBldWxlciApIHtcblxuXHRcdFx0dGhpcy5xdWF0ZXJuaW9uLnNldEZyb21FdWxlciggZXVsZXIsIHRydWUgKTtcblxuXHRcdH0sXG5cblx0XHRzZXRSb3RhdGlvbkZyb21NYXRyaXg6IGZ1bmN0aW9uICggbSApIHtcblxuXHRcdFx0Ly8gYXNzdW1lcyB0aGUgdXBwZXIgM3gzIG9mIG0gaXMgYSBwdXJlIHJvdGF0aW9uIG1hdHJpeCAoaS5lLCB1bnNjYWxlZClcblxuXHRcdFx0dGhpcy5xdWF0ZXJuaW9uLnNldEZyb21Sb3RhdGlvbk1hdHJpeCggbSApO1xuXG5cdFx0fSxcblxuXHRcdHNldFJvdGF0aW9uRnJvbVF1YXRlcm5pb246IGZ1bmN0aW9uICggcSApIHtcblxuXHRcdFx0Ly8gYXNzdW1lcyBxIGlzIG5vcm1hbGl6ZWRcblxuXHRcdFx0dGhpcy5xdWF0ZXJuaW9uLmNvcHkoIHEgKTtcblxuXHRcdH0sXG5cblx0XHRyb3RhdGVPbkF4aXM6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Ly8gcm90YXRlIG9iamVjdCBvbiBheGlzIGluIG9iamVjdCBzcGFjZVxuXHRcdFx0Ly8gYXhpcyBpcyBhc3N1bWVkIHRvIGJlIG5vcm1hbGl6ZWRcblxuXHRcdFx0dmFyIHExID0gbmV3IFF1YXRlcm5pb24oKTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIHJvdGF0ZU9uQXhpcyggYXhpcywgYW5nbGUgKSB7XG5cblx0XHRcdFx0cTEuc2V0RnJvbUF4aXNBbmdsZSggYXhpcywgYW5nbGUgKTtcblxuXHRcdFx0XHR0aGlzLnF1YXRlcm5pb24ubXVsdGlwbHkoIHExICk7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRyb3RhdGVYOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB2MSA9IG5ldyBWZWN0b3IzKCAxLCAwLCAwICk7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiByb3RhdGVYKCBhbmdsZSApIHtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcy5yb3RhdGVPbkF4aXMoIHYxLCBhbmdsZSApO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0cm90YXRlWTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgdjEgPSBuZXcgVmVjdG9yMyggMCwgMSwgMCApO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gcm90YXRlWSggYW5nbGUgKSB7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXMucm90YXRlT25BeGlzKCB2MSwgYW5nbGUgKTtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdHJvdGF0ZVo6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIHYxID0gbmV3IFZlY3RvcjMoIDAsIDAsIDEgKTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIHJvdGF0ZVooIGFuZ2xlICkge1xuXG5cdFx0XHRcdHJldHVybiB0aGlzLnJvdGF0ZU9uQXhpcyggdjEsIGFuZ2xlICk7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHR0cmFuc2xhdGVPbkF4aXM6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Ly8gdHJhbnNsYXRlIG9iamVjdCBieSBkaXN0YW5jZSBhbG9uZyBheGlzIGluIG9iamVjdCBzcGFjZVxuXHRcdFx0Ly8gYXhpcyBpcyBhc3N1bWVkIHRvIGJlIG5vcm1hbGl6ZWRcblxuXHRcdFx0dmFyIHYxID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIHRyYW5zbGF0ZU9uQXhpcyggYXhpcywgZGlzdGFuY2UgKSB7XG5cblx0XHRcdFx0djEuY29weSggYXhpcyApLmFwcGx5UXVhdGVybmlvbiggdGhpcy5xdWF0ZXJuaW9uICk7XG5cblx0XHRcdFx0dGhpcy5wb3NpdGlvbi5hZGQoIHYxLm11bHRpcGx5U2NhbGFyKCBkaXN0YW5jZSApICk7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHR0cmFuc2xhdGVYOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB2MSA9IG5ldyBWZWN0b3IzKCAxLCAwLCAwICk7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiB0cmFuc2xhdGVYKCBkaXN0YW5jZSApIHtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcy50cmFuc2xhdGVPbkF4aXMoIHYxLCBkaXN0YW5jZSApO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0dHJhbnNsYXRlWTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgdjEgPSBuZXcgVmVjdG9yMyggMCwgMSwgMCApO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gdHJhbnNsYXRlWSggZGlzdGFuY2UgKSB7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXMudHJhbnNsYXRlT25BeGlzKCB2MSwgZGlzdGFuY2UgKTtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdHRyYW5zbGF0ZVo6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIHYxID0gbmV3IFZlY3RvcjMoIDAsIDAsIDEgKTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIHRyYW5zbGF0ZVooIGRpc3RhbmNlICkge1xuXG5cdFx0XHRcdHJldHVybiB0aGlzLnRyYW5zbGF0ZU9uQXhpcyggdjEsIGRpc3RhbmNlICk7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRsb2NhbFRvV29ybGQ6IGZ1bmN0aW9uICggdmVjdG9yICkge1xuXG5cdFx0XHRyZXR1cm4gdmVjdG9yLmFwcGx5TWF0cml4NCggdGhpcy5tYXRyaXhXb3JsZCApO1xuXG5cdFx0fSxcblxuXHRcdHdvcmxkVG9Mb2NhbDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgbTEgPSBuZXcgTWF0cml4NCgpO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gd29ybGRUb0xvY2FsKCB2ZWN0b3IgKSB7XG5cblx0XHRcdFx0cmV0dXJuIHZlY3Rvci5hcHBseU1hdHJpeDQoIG0xLmdldEludmVyc2UoIHRoaXMubWF0cml4V29ybGQgKSApO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0bG9va0F0OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdC8vIFRoaXMgcm91dGluZSBkb2VzIG5vdCBzdXBwb3J0IG9iamVjdHMgd2l0aCByb3RhdGVkIGFuZC9vciB0cmFuc2xhdGVkIHBhcmVudChzKVxuXG5cdFx0XHR2YXIgbTEgPSBuZXcgTWF0cml4NCgpO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gbG9va0F0KCB2ZWN0b3IgKSB7XG5cblx0XHRcdFx0bTEubG9va0F0KCB2ZWN0b3IsIHRoaXMucG9zaXRpb24sIHRoaXMudXAgKTtcblxuXHRcdFx0XHR0aGlzLnF1YXRlcm5pb24uc2V0RnJvbVJvdGF0aW9uTWF0cml4KCBtMSApO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0YWRkOiBmdW5jdGlvbiAoIG9iamVjdCApIHtcblxuXHRcdFx0aWYgKCBhcmd1bWVudHMubGVuZ3RoID4gMSApIHtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBhcmd1bWVudHMubGVuZ3RoOyBpICsrICkge1xuXG5cdFx0XHRcdFx0dGhpcy5hZGQoIGFyZ3VtZW50c1sgaSBdICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggb2JqZWN0ID09PSB0aGlzICkge1xuXG5cdFx0XHRcdGNvbnNvbGUuZXJyb3IoIFwiVEhSRUUuT2JqZWN0M0QuYWRkOiBvYmplY3QgY2FuJ3QgYmUgYWRkZWQgYXMgYSBjaGlsZCBvZiBpdHNlbGYuXCIsIG9iamVjdCApO1xuXHRcdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoICggb2JqZWN0ICYmIG9iamVjdC5pc09iamVjdDNEICkgKSB7XG5cblx0XHRcdFx0aWYgKCBvYmplY3QucGFyZW50ICE9PSBudWxsICkge1xuXG5cdFx0XHRcdFx0b2JqZWN0LnBhcmVudC5yZW1vdmUoIG9iamVjdCApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRvYmplY3QucGFyZW50ID0gdGhpcztcblx0XHRcdFx0b2JqZWN0LmRpc3BhdGNoRXZlbnQoIHsgdHlwZTogJ2FkZGVkJyB9ICk7XG5cblx0XHRcdFx0dGhpcy5jaGlsZHJlbi5wdXNoKCBvYmplY3QgKTtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRjb25zb2xlLmVycm9yKCBcIlRIUkVFLk9iamVjdDNELmFkZDogb2JqZWN0IG5vdCBhbiBpbnN0YW5jZSBvZiBUSFJFRS5PYmplY3QzRC5cIiwgb2JqZWN0ICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0cmVtb3ZlOiBmdW5jdGlvbiAoIG9iamVjdCApIHtcblxuXHRcdFx0aWYgKCBhcmd1bWVudHMubGVuZ3RoID4gMSApIHtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBhcmd1bWVudHMubGVuZ3RoOyBpICsrICkge1xuXG5cdFx0XHRcdFx0dGhpcy5yZW1vdmUoIGFyZ3VtZW50c1sgaSBdICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBpbmRleCA9IHRoaXMuY2hpbGRyZW4uaW5kZXhPZiggb2JqZWN0ICk7XG5cblx0XHRcdGlmICggaW5kZXggIT09IC0gMSApIHtcblxuXHRcdFx0XHRvYmplY3QucGFyZW50ID0gbnVsbDtcblxuXHRcdFx0XHRvYmplY3QuZGlzcGF0Y2hFdmVudCggeyB0eXBlOiAncmVtb3ZlZCcgfSApO1xuXG5cdFx0XHRcdHRoaXMuY2hpbGRyZW4uc3BsaWNlKCBpbmRleCwgMSApO1xuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0Z2V0T2JqZWN0QnlJZDogZnVuY3Rpb24gKCBpZCApIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuZ2V0T2JqZWN0QnlQcm9wZXJ0eSggJ2lkJywgaWQgKTtcblxuXHRcdH0sXG5cblx0XHRnZXRPYmplY3RCeU5hbWU6IGZ1bmN0aW9uICggbmFtZSApIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuZ2V0T2JqZWN0QnlQcm9wZXJ0eSggJ25hbWUnLCBuYW1lICk7XG5cblx0XHR9LFxuXG5cdFx0Z2V0T2JqZWN0QnlQcm9wZXJ0eTogZnVuY3Rpb24gKCBuYW1lLCB2YWx1ZSApIHtcblxuXHRcdFx0aWYgKCB0aGlzWyBuYW1lIF0gPT09IHZhbHVlICkgcmV0dXJuIHRoaXM7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IHRoaXMuY2hpbGRyZW4ubGVuZ3RoOyBpIDwgbDsgaSArKyApIHtcblxuXHRcdFx0XHR2YXIgY2hpbGQgPSB0aGlzLmNoaWxkcmVuWyBpIF07XG5cdFx0XHRcdHZhciBvYmplY3QgPSBjaGlsZC5nZXRPYmplY3RCeVByb3BlcnR5KCBuYW1lLCB2YWx1ZSApO1xuXG5cdFx0XHRcdGlmICggb2JqZWN0ICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRyZXR1cm4gb2JqZWN0O1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdW5kZWZpbmVkO1xuXG5cdFx0fSxcblxuXHRcdGdldFdvcmxkUG9zaXRpb246IGZ1bmN0aW9uICggb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdHZhciByZXN1bHQgPSBvcHRpb25hbFRhcmdldCB8fCBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHR0aGlzLnVwZGF0ZU1hdHJpeFdvcmxkKCB0cnVlICk7XG5cblx0XHRcdHJldHVybiByZXN1bHQuc2V0RnJvbU1hdHJpeFBvc2l0aW9uKCB0aGlzLm1hdHJpeFdvcmxkICk7XG5cblx0XHR9LFxuXG5cdFx0Z2V0V29ybGRRdWF0ZXJuaW9uOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBwb3NpdGlvbiA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHR2YXIgc2NhbGUgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gZ2V0V29ybGRRdWF0ZXJuaW9uKCBvcHRpb25hbFRhcmdldCApIHtcblxuXHRcdFx0XHR2YXIgcmVzdWx0ID0gb3B0aW9uYWxUYXJnZXQgfHwgbmV3IFF1YXRlcm5pb24oKTtcblxuXHRcdFx0XHR0aGlzLnVwZGF0ZU1hdHJpeFdvcmxkKCB0cnVlICk7XG5cblx0XHRcdFx0dGhpcy5tYXRyaXhXb3JsZC5kZWNvbXBvc2UoIHBvc2l0aW9uLCByZXN1bHQsIHNjYWxlICk7XG5cblx0XHRcdFx0cmV0dXJuIHJlc3VsdDtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdGdldFdvcmxkUm90YXRpb246IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIHF1YXRlcm5pb24gPSBuZXcgUXVhdGVybmlvbigpO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gZ2V0V29ybGRSb3RhdGlvbiggb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdFx0dmFyIHJlc3VsdCA9IG9wdGlvbmFsVGFyZ2V0IHx8IG5ldyBFdWxlcigpO1xuXG5cdFx0XHRcdHRoaXMuZ2V0V29ybGRRdWF0ZXJuaW9uKCBxdWF0ZXJuaW9uICk7XG5cblx0XHRcdFx0cmV0dXJuIHJlc3VsdC5zZXRGcm9tUXVhdGVybmlvbiggcXVhdGVybmlvbiwgdGhpcy5yb3RhdGlvbi5vcmRlciwgZmFsc2UgKTtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdGdldFdvcmxkU2NhbGU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIHBvc2l0aW9uID0gbmV3IFZlY3RvcjMoKTtcblx0XHRcdHZhciBxdWF0ZXJuaW9uID0gbmV3IFF1YXRlcm5pb24oKTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIGdldFdvcmxkU2NhbGUoIG9wdGlvbmFsVGFyZ2V0ICkge1xuXG5cdFx0XHRcdHZhciByZXN1bHQgPSBvcHRpb25hbFRhcmdldCB8fCBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRcdHRoaXMudXBkYXRlTWF0cml4V29ybGQoIHRydWUgKTtcblxuXHRcdFx0XHR0aGlzLm1hdHJpeFdvcmxkLmRlY29tcG9zZSggcG9zaXRpb24sIHF1YXRlcm5pb24sIHJlc3VsdCApO1xuXG5cdFx0XHRcdHJldHVybiByZXN1bHQ7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRnZXRXb3JsZERpcmVjdGlvbjogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgcXVhdGVybmlvbiA9IG5ldyBRdWF0ZXJuaW9uKCk7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBnZXRXb3JsZERpcmVjdGlvbiggb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdFx0dmFyIHJlc3VsdCA9IG9wdGlvbmFsVGFyZ2V0IHx8IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdFx0dGhpcy5nZXRXb3JsZFF1YXRlcm5pb24oIHF1YXRlcm5pb24gKTtcblxuXHRcdFx0XHRyZXR1cm4gcmVzdWx0LnNldCggMCwgMCwgMSApLmFwcGx5UXVhdGVybmlvbiggcXVhdGVybmlvbiApO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0cmF5Y2FzdDogZnVuY3Rpb24gKCkge30sXG5cblx0XHR0cmF2ZXJzZTogZnVuY3Rpb24gKCBjYWxsYmFjayApIHtcblxuXHRcdFx0Y2FsbGJhY2soIHRoaXMgKTtcblxuXHRcdFx0dmFyIGNoaWxkcmVuID0gdGhpcy5jaGlsZHJlbjtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBsID0gY2hpbGRyZW4ubGVuZ3RoOyBpIDwgbDsgaSArKyApIHtcblxuXHRcdFx0XHRjaGlsZHJlblsgaSBdLnRyYXZlcnNlKCBjYWxsYmFjayApO1xuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0dHJhdmVyc2VWaXNpYmxlOiBmdW5jdGlvbiAoIGNhbGxiYWNrICkge1xuXG5cdFx0XHRpZiAoIHRoaXMudmlzaWJsZSA9PT0gZmFsc2UgKSByZXR1cm47XG5cblx0XHRcdGNhbGxiYWNrKCB0aGlzICk7XG5cblx0XHRcdHZhciBjaGlsZHJlbiA9IHRoaXMuY2hpbGRyZW47XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGNoaWxkcmVuLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0Y2hpbGRyZW5bIGkgXS50cmF2ZXJzZVZpc2libGUoIGNhbGxiYWNrICk7XG5cblx0XHRcdH1cblxuXHRcdH0sXG5cblx0XHR0cmF2ZXJzZUFuY2VzdG9yczogZnVuY3Rpb24gKCBjYWxsYmFjayApIHtcblxuXHRcdFx0dmFyIHBhcmVudCA9IHRoaXMucGFyZW50O1xuXG5cdFx0XHRpZiAoIHBhcmVudCAhPT0gbnVsbCApIHtcblxuXHRcdFx0XHRjYWxsYmFjayggcGFyZW50ICk7XG5cblx0XHRcdFx0cGFyZW50LnRyYXZlcnNlQW5jZXN0b3JzKCBjYWxsYmFjayApO1xuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0dXBkYXRlTWF0cml4OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMubWF0cml4LmNvbXBvc2UoIHRoaXMucG9zaXRpb24sIHRoaXMucXVhdGVybmlvbiwgdGhpcy5zY2FsZSApO1xuXG5cdFx0XHR0aGlzLm1hdHJpeFdvcmxkTmVlZHNVcGRhdGUgPSB0cnVlO1xuXG5cdFx0fSxcblxuXHRcdHVwZGF0ZU1hdHJpeFdvcmxkOiBmdW5jdGlvbiAoIGZvcmNlICkge1xuXG5cdFx0XHRpZiAoIHRoaXMubWF0cml4QXV0b1VwZGF0ZSA9PT0gdHJ1ZSApIHRoaXMudXBkYXRlTWF0cml4KCk7XG5cblx0XHRcdGlmICggdGhpcy5tYXRyaXhXb3JsZE5lZWRzVXBkYXRlID09PSB0cnVlIHx8IGZvcmNlID09PSB0cnVlICkge1xuXG5cdFx0XHRcdGlmICggdGhpcy5wYXJlbnQgPT09IG51bGwgKSB7XG5cblx0XHRcdFx0XHR0aGlzLm1hdHJpeFdvcmxkLmNvcHkoIHRoaXMubWF0cml4ICk7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdHRoaXMubWF0cml4V29ybGQubXVsdGlwbHlNYXRyaWNlcyggdGhpcy5wYXJlbnQubWF0cml4V29ybGQsIHRoaXMubWF0cml4ICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHRoaXMubWF0cml4V29ybGROZWVkc1VwZGF0ZSA9IGZhbHNlO1xuXG5cdFx0XHRcdGZvcmNlID0gdHJ1ZTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyB1cGRhdGUgY2hpbGRyZW5cblxuXHRcdFx0dmFyIGNoaWxkcmVuID0gdGhpcy5jaGlsZHJlbjtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBsID0gY2hpbGRyZW4ubGVuZ3RoOyBpIDwgbDsgaSArKyApIHtcblxuXHRcdFx0XHRjaGlsZHJlblsgaSBdLnVwZGF0ZU1hdHJpeFdvcmxkKCBmb3JjZSApO1xuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0dG9KU09OOiBmdW5jdGlvbiAoIG1ldGEgKSB7XG5cblx0XHRcdC8vIG1ldGEgaXMgJycgd2hlbiBjYWxsZWQgZnJvbSBKU09OLnN0cmluZ2lmeVxuXHRcdFx0dmFyIGlzUm9vdE9iamVjdCA9ICggbWV0YSA9PT0gdW5kZWZpbmVkIHx8IG1ldGEgPT09ICcnICk7XG5cblx0XHRcdHZhciBvdXRwdXQgPSB7fTtcblxuXHRcdFx0Ly8gbWV0YSBpcyBhIGhhc2ggdXNlZCB0byBjb2xsZWN0IGdlb21ldHJpZXMsIG1hdGVyaWFscy5cblx0XHRcdC8vIG5vdCBwcm92aWRpbmcgaXQgaW1wbGllcyB0aGF0IHRoaXMgaXMgdGhlIHJvb3Qgb2JqZWN0XG5cdFx0XHQvLyBiZWluZyBzZXJpYWxpemVkLlxuXHRcdFx0aWYgKCBpc1Jvb3RPYmplY3QgKSB7XG5cblx0XHRcdFx0Ly8gaW5pdGlhbGl6ZSBtZXRhIG9ialxuXHRcdFx0XHRtZXRhID0ge1xuXHRcdFx0XHRcdGdlb21ldHJpZXM6IHt9LFxuXHRcdFx0XHRcdG1hdGVyaWFsczoge30sXG5cdFx0XHRcdFx0dGV4dHVyZXM6IHt9LFxuXHRcdFx0XHRcdGltYWdlczoge31cblx0XHRcdFx0fTtcblxuXHRcdFx0XHRvdXRwdXQubWV0YWRhdGEgPSB7XG5cdFx0XHRcdFx0dmVyc2lvbjogNC40LFxuXHRcdFx0XHRcdHR5cGU6ICdPYmplY3QnLFxuXHRcdFx0XHRcdGdlbmVyYXRvcjogJ09iamVjdDNELnRvSlNPTidcblx0XHRcdFx0fTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyBzdGFuZGFyZCBPYmplY3QzRCBzZXJpYWxpemF0aW9uXG5cblx0XHRcdHZhciBvYmplY3QgPSB7fTtcblxuXHRcdFx0b2JqZWN0LnV1aWQgPSB0aGlzLnV1aWQ7XG5cdFx0XHRvYmplY3QudHlwZSA9IHRoaXMudHlwZTtcblxuXHRcdFx0aWYgKCB0aGlzLm5hbWUgIT09ICcnICkgb2JqZWN0Lm5hbWUgPSB0aGlzLm5hbWU7XG5cdFx0XHRpZiAoIEpTT04uc3RyaW5naWZ5KCB0aGlzLnVzZXJEYXRhICkgIT09ICd7fScgKSBvYmplY3QudXNlckRhdGEgPSB0aGlzLnVzZXJEYXRhO1xuXHRcdFx0aWYgKCB0aGlzLmNhc3RTaGFkb3cgPT09IHRydWUgKSBvYmplY3QuY2FzdFNoYWRvdyA9IHRydWU7XG5cdFx0XHRpZiAoIHRoaXMucmVjZWl2ZVNoYWRvdyA9PT0gdHJ1ZSApIG9iamVjdC5yZWNlaXZlU2hhZG93ID0gdHJ1ZTtcblx0XHRcdGlmICggdGhpcy52aXNpYmxlID09PSBmYWxzZSApIG9iamVjdC52aXNpYmxlID0gZmFsc2U7XG5cblx0XHRcdG9iamVjdC5tYXRyaXggPSB0aGlzLm1hdHJpeC50b0FycmF5KCk7XG5cblx0XHRcdC8vXG5cblx0XHRcdGlmICggdGhpcy5nZW9tZXRyeSAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdGlmICggbWV0YS5nZW9tZXRyaWVzWyB0aGlzLmdlb21ldHJ5LnV1aWQgXSA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0bWV0YS5nZW9tZXRyaWVzWyB0aGlzLmdlb21ldHJ5LnV1aWQgXSA9IHRoaXMuZ2VvbWV0cnkudG9KU09OKCBtZXRhICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdG9iamVjdC5nZW9tZXRyeSA9IHRoaXMuZ2VvbWV0cnkudXVpZDtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHRoaXMubWF0ZXJpYWwgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRpZiAoIG1ldGEubWF0ZXJpYWxzWyB0aGlzLm1hdGVyaWFsLnV1aWQgXSA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0bWV0YS5tYXRlcmlhbHNbIHRoaXMubWF0ZXJpYWwudXVpZCBdID0gdGhpcy5tYXRlcmlhbC50b0pTT04oIG1ldGEgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0b2JqZWN0Lm1hdGVyaWFsID0gdGhpcy5tYXRlcmlhbC51dWlkO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vXG5cblx0XHRcdGlmICggdGhpcy5jaGlsZHJlbi5sZW5ndGggPiAwICkge1xuXG5cdFx0XHRcdG9iamVjdC5jaGlsZHJlbiA9IFtdO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IHRoaXMuY2hpbGRyZW4ubGVuZ3RoOyBpICsrICkge1xuXG5cdFx0XHRcdFx0b2JqZWN0LmNoaWxkcmVuLnB1c2goIHRoaXMuY2hpbGRyZW5bIGkgXS50b0pTT04oIG1ldGEgKS5vYmplY3QgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBpc1Jvb3RPYmplY3QgKSB7XG5cblx0XHRcdFx0dmFyIGdlb21ldHJpZXMgPSBleHRyYWN0RnJvbUNhY2hlKCBtZXRhLmdlb21ldHJpZXMgKTtcblx0XHRcdFx0dmFyIG1hdGVyaWFscyA9IGV4dHJhY3RGcm9tQ2FjaGUoIG1ldGEubWF0ZXJpYWxzICk7XG5cdFx0XHRcdHZhciB0ZXh0dXJlcyA9IGV4dHJhY3RGcm9tQ2FjaGUoIG1ldGEudGV4dHVyZXMgKTtcblx0XHRcdFx0dmFyIGltYWdlcyA9IGV4dHJhY3RGcm9tQ2FjaGUoIG1ldGEuaW1hZ2VzICk7XG5cblx0XHRcdFx0aWYgKCBnZW9tZXRyaWVzLmxlbmd0aCA+IDAgKSBvdXRwdXQuZ2VvbWV0cmllcyA9IGdlb21ldHJpZXM7XG5cdFx0XHRcdGlmICggbWF0ZXJpYWxzLmxlbmd0aCA+IDAgKSBvdXRwdXQubWF0ZXJpYWxzID0gbWF0ZXJpYWxzO1xuXHRcdFx0XHRpZiAoIHRleHR1cmVzLmxlbmd0aCA+IDAgKSBvdXRwdXQudGV4dHVyZXMgPSB0ZXh0dXJlcztcblx0XHRcdFx0aWYgKCBpbWFnZXMubGVuZ3RoID4gMCApIG91dHB1dC5pbWFnZXMgPSBpbWFnZXM7XG5cblx0XHRcdH1cblxuXHRcdFx0b3V0cHV0Lm9iamVjdCA9IG9iamVjdDtcblxuXHRcdFx0cmV0dXJuIG91dHB1dDtcblxuXHRcdFx0Ly8gZXh0cmFjdCBkYXRhIGZyb20gdGhlIGNhY2hlIGhhc2hcblx0XHRcdC8vIHJlbW92ZSBtZXRhZGF0YSBvbiBlYWNoIGl0ZW1cblx0XHRcdC8vIGFuZCByZXR1cm4gYXMgYXJyYXlcblx0XHRcdGZ1bmN0aW9uIGV4dHJhY3RGcm9tQ2FjaGUoIGNhY2hlICkge1xuXG5cdFx0XHRcdHZhciB2YWx1ZXMgPSBbXTtcblx0XHRcdFx0Zm9yICggdmFyIGtleSBpbiBjYWNoZSApIHtcblxuXHRcdFx0XHRcdHZhciBkYXRhID0gY2FjaGVbIGtleSBdO1xuXHRcdFx0XHRcdGRlbGV0ZSBkYXRhLm1ldGFkYXRhO1xuXHRcdFx0XHRcdHZhbHVlcy5wdXNoKCBkYXRhICk7XG5cblx0XHRcdFx0fVxuXHRcdFx0XHRyZXR1cm4gdmFsdWVzO1xuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0Y2xvbmU6IGZ1bmN0aW9uICggcmVjdXJzaXZlICkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IHRoaXMuY29uc3RydWN0b3IoKS5jb3B5KCB0aGlzLCByZWN1cnNpdmUgKTtcblxuXHRcdH0sXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIHNvdXJjZSwgcmVjdXJzaXZlICkge1xuXG5cdFx0XHRpZiAoIHJlY3Vyc2l2ZSA9PT0gdW5kZWZpbmVkICkgcmVjdXJzaXZlID0gdHJ1ZTtcblxuXHRcdFx0dGhpcy5uYW1lID0gc291cmNlLm5hbWU7XG5cblx0XHRcdHRoaXMudXAuY29weSggc291cmNlLnVwICk7XG5cblx0XHRcdHRoaXMucG9zaXRpb24uY29weSggc291cmNlLnBvc2l0aW9uICk7XG5cdFx0XHR0aGlzLnF1YXRlcm5pb24uY29weSggc291cmNlLnF1YXRlcm5pb24gKTtcblx0XHRcdHRoaXMuc2NhbGUuY29weSggc291cmNlLnNjYWxlICk7XG5cblx0XHRcdHRoaXMubWF0cml4LmNvcHkoIHNvdXJjZS5tYXRyaXggKTtcblx0XHRcdHRoaXMubWF0cml4V29ybGQuY29weSggc291cmNlLm1hdHJpeFdvcmxkICk7XG5cblx0XHRcdHRoaXMubWF0cml4QXV0b1VwZGF0ZSA9IHNvdXJjZS5tYXRyaXhBdXRvVXBkYXRlO1xuXHRcdFx0dGhpcy5tYXRyaXhXb3JsZE5lZWRzVXBkYXRlID0gc291cmNlLm1hdHJpeFdvcmxkTmVlZHNVcGRhdGU7XG5cblx0XHRcdHRoaXMubGF5ZXJzLm1hc2sgPSBzb3VyY2UubGF5ZXJzLm1hc2s7XG5cdFx0XHR0aGlzLnZpc2libGUgPSBzb3VyY2UudmlzaWJsZTtcblxuXHRcdFx0dGhpcy5jYXN0U2hhZG93ID0gc291cmNlLmNhc3RTaGFkb3c7XG5cdFx0XHR0aGlzLnJlY2VpdmVTaGFkb3cgPSBzb3VyY2UucmVjZWl2ZVNoYWRvdztcblxuXHRcdFx0dGhpcy5mcnVzdHVtQ3VsbGVkID0gc291cmNlLmZydXN0dW1DdWxsZWQ7XG5cdFx0XHR0aGlzLnJlbmRlck9yZGVyID0gc291cmNlLnJlbmRlck9yZGVyO1xuXG5cdFx0XHR0aGlzLnVzZXJEYXRhID0gSlNPTi5wYXJzZSggSlNPTi5zdHJpbmdpZnkoIHNvdXJjZS51c2VyRGF0YSApICk7XG5cblx0XHRcdGlmICggcmVjdXJzaXZlID09PSB0cnVlICkge1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IHNvdXJjZS5jaGlsZHJlbi5sZW5ndGg7IGkgKysgKSB7XG5cblx0XHRcdFx0XHR2YXIgY2hpbGQgPSBzb3VyY2UuY2hpbGRyZW5bIGkgXTtcblx0XHRcdFx0XHR0aGlzLmFkZCggY2hpbGQuY2xvbmUoKSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgYmhvdXN0b24gLyBodHRwOi8vY2xhcmEuaW9cblx0ICovXG5cblx0ZnVuY3Rpb24gTGluZTMoIHN0YXJ0LCBlbmQgKSB7XG5cblx0XHR0aGlzLnN0YXJ0ID0gKCBzdGFydCAhPT0gdW5kZWZpbmVkICkgPyBzdGFydCA6IG5ldyBWZWN0b3IzKCk7XG5cdFx0dGhpcy5lbmQgPSAoIGVuZCAhPT0gdW5kZWZpbmVkICkgPyBlbmQgOiBuZXcgVmVjdG9yMygpO1xuXG5cdH1cblxuXHRMaW5lMy5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogTGluZTMsXG5cblx0XHRzZXQ6IGZ1bmN0aW9uICggc3RhcnQsIGVuZCApIHtcblxuXHRcdFx0dGhpcy5zdGFydC5jb3B5KCBzdGFydCApO1xuXHRcdFx0dGhpcy5lbmQuY29weSggZW5kICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiBuZXcgdGhpcy5jb25zdHJ1Y3RvcigpLmNvcHkoIHRoaXMgKTtcblxuXHRcdH0sXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIGxpbmUgKSB7XG5cblx0XHRcdHRoaXMuc3RhcnQuY29weSggbGluZS5zdGFydCApO1xuXHRcdFx0dGhpcy5lbmQuY29weSggbGluZS5lbmQgKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Z2V0Q2VudGVyOiBmdW5jdGlvbiAoIG9wdGlvbmFsVGFyZ2V0ICkge1xuXG5cdFx0XHR2YXIgcmVzdWx0ID0gb3B0aW9uYWxUYXJnZXQgfHwgbmV3IFZlY3RvcjMoKTtcblx0XHRcdHJldHVybiByZXN1bHQuYWRkVmVjdG9ycyggdGhpcy5zdGFydCwgdGhpcy5lbmQgKS5tdWx0aXBseVNjYWxhciggMC41ICk7XG5cblx0XHR9LFxuXG5cdFx0ZGVsdGE6IGZ1bmN0aW9uICggb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdHZhciByZXN1bHQgPSBvcHRpb25hbFRhcmdldCB8fCBuZXcgVmVjdG9yMygpO1xuXHRcdFx0cmV0dXJuIHJlc3VsdC5zdWJWZWN0b3JzKCB0aGlzLmVuZCwgdGhpcy5zdGFydCApO1xuXG5cdFx0fSxcblxuXHRcdGRpc3RhbmNlU3E6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuc3RhcnQuZGlzdGFuY2VUb1NxdWFyZWQoIHRoaXMuZW5kICk7XG5cblx0XHR9LFxuXG5cdFx0ZGlzdGFuY2U6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuc3RhcnQuZGlzdGFuY2VUbyggdGhpcy5lbmQgKTtcblxuXHRcdH0sXG5cblx0XHRhdDogZnVuY3Rpb24gKCB0LCBvcHRpb25hbFRhcmdldCApIHtcblxuXHRcdFx0dmFyIHJlc3VsdCA9IG9wdGlvbmFsVGFyZ2V0IHx8IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdHJldHVybiB0aGlzLmRlbHRhKCByZXN1bHQgKS5tdWx0aXBseVNjYWxhciggdCApLmFkZCggdGhpcy5zdGFydCApO1xuXG5cdFx0fSxcblxuXHRcdGNsb3Nlc3RQb2ludFRvUG9pbnRQYXJhbWV0ZXI6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIHN0YXJ0UCA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHR2YXIgc3RhcnRFbmQgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gY2xvc2VzdFBvaW50VG9Qb2ludFBhcmFtZXRlciggcG9pbnQsIGNsYW1wVG9MaW5lICkge1xuXG5cdFx0XHRcdHN0YXJ0UC5zdWJWZWN0b3JzKCBwb2ludCwgdGhpcy5zdGFydCApO1xuXHRcdFx0XHRzdGFydEVuZC5zdWJWZWN0b3JzKCB0aGlzLmVuZCwgdGhpcy5zdGFydCApO1xuXG5cdFx0XHRcdHZhciBzdGFydEVuZDIgPSBzdGFydEVuZC5kb3QoIHN0YXJ0RW5kICk7XG5cdFx0XHRcdHZhciBzdGFydEVuZF9zdGFydFAgPSBzdGFydEVuZC5kb3QoIHN0YXJ0UCApO1xuXG5cdFx0XHRcdHZhciB0ID0gc3RhcnRFbmRfc3RhcnRQIC8gc3RhcnRFbmQyO1xuXG5cdFx0XHRcdGlmICggY2xhbXBUb0xpbmUgKSB7XG5cblx0XHRcdFx0XHR0ID0gX01hdGguY2xhbXAoIHQsIDAsIDEgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuIHQ7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRjbG9zZXN0UG9pbnRUb1BvaW50OiBmdW5jdGlvbiAoIHBvaW50LCBjbGFtcFRvTGluZSwgb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdHZhciB0ID0gdGhpcy5jbG9zZXN0UG9pbnRUb1BvaW50UGFyYW1ldGVyKCBwb2ludCwgY2xhbXBUb0xpbmUgKTtcblxuXHRcdFx0dmFyIHJlc3VsdCA9IG9wdGlvbmFsVGFyZ2V0IHx8IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdHJldHVybiB0aGlzLmRlbHRhKCByZXN1bHQgKS5tdWx0aXBseVNjYWxhciggdCApLmFkZCggdGhpcy5zdGFydCApO1xuXG5cdFx0fSxcblxuXHRcdGFwcGx5TWF0cml4NDogZnVuY3Rpb24gKCBtYXRyaXggKSB7XG5cblx0XHRcdHRoaXMuc3RhcnQuYXBwbHlNYXRyaXg0KCBtYXRyaXggKTtcblx0XHRcdHRoaXMuZW5kLmFwcGx5TWF0cml4NCggbWF0cml4ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGVxdWFsczogZnVuY3Rpb24gKCBsaW5lICkge1xuXG5cdFx0XHRyZXR1cm4gbGluZS5zdGFydC5lcXVhbHMoIHRoaXMuc3RhcnQgKSAmJiBsaW5lLmVuZC5lcXVhbHMoIHRoaXMuZW5kICk7XG5cblx0XHR9XG5cblx0fTtcblxuXHQvKipcblx0ICogQGF1dGhvciBiaG91c3RvbiAvIGh0dHA6Ly9jbGFyYS5pb1xuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBUcmlhbmdsZSggYSwgYiwgYyApIHtcblxuXHRcdHRoaXMuYSA9ICggYSAhPT0gdW5kZWZpbmVkICkgPyBhIDogbmV3IFZlY3RvcjMoKTtcblx0XHR0aGlzLmIgPSAoIGIgIT09IHVuZGVmaW5lZCApID8gYiA6IG5ldyBWZWN0b3IzKCk7XG5cdFx0dGhpcy5jID0gKCBjICE9PSB1bmRlZmluZWQgKSA/IGMgOiBuZXcgVmVjdG9yMygpO1xuXG5cdH1cblxuXHRUcmlhbmdsZS5ub3JtYWwgPSBmdW5jdGlvbiAoKSB7XG5cblx0XHR2YXIgdjAgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0cmV0dXJuIGZ1bmN0aW9uIG5vcm1hbCggYSwgYiwgYywgb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdHZhciByZXN1bHQgPSBvcHRpb25hbFRhcmdldCB8fCBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRyZXN1bHQuc3ViVmVjdG9ycyggYywgYiApO1xuXHRcdFx0djAuc3ViVmVjdG9ycyggYSwgYiApO1xuXHRcdFx0cmVzdWx0LmNyb3NzKCB2MCApO1xuXG5cdFx0XHR2YXIgcmVzdWx0TGVuZ3RoU3EgPSByZXN1bHQubGVuZ3RoU3EoKTtcblx0XHRcdGlmICggcmVzdWx0TGVuZ3RoU3EgPiAwICkge1xuXG5cdFx0XHRcdHJldHVybiByZXN1bHQubXVsdGlwbHlTY2FsYXIoIDEgLyBNYXRoLnNxcnQoIHJlc3VsdExlbmd0aFNxICkgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gcmVzdWx0LnNldCggMCwgMCwgMCApO1xuXG5cdFx0fTtcblxuXHR9KCk7XG5cblx0Ly8gc3RhdGljL2luc3RhbmNlIG1ldGhvZCB0byBjYWxjdWxhdGUgYmFyeWNlbnRyaWMgY29vcmRpbmF0ZXNcblx0Ly8gYmFzZWQgb246IGh0dHA6Ly93d3cuYmxhY2twYXduLmNvbS90ZXh0cy9wb2ludGlucG9seS9kZWZhdWx0Lmh0bWxcblx0VHJpYW5nbGUuYmFyeWNvb3JkRnJvbVBvaW50ID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0dmFyIHYwID0gbmV3IFZlY3RvcjMoKTtcblx0XHR2YXIgdjEgPSBuZXcgVmVjdG9yMygpO1xuXHRcdHZhciB2MiA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRyZXR1cm4gZnVuY3Rpb24gYmFyeWNvb3JkRnJvbVBvaW50KCBwb2ludCwgYSwgYiwgYywgb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdHYwLnN1YlZlY3RvcnMoIGMsIGEgKTtcblx0XHRcdHYxLnN1YlZlY3RvcnMoIGIsIGEgKTtcblx0XHRcdHYyLnN1YlZlY3RvcnMoIHBvaW50LCBhICk7XG5cblx0XHRcdHZhciBkb3QwMCA9IHYwLmRvdCggdjAgKTtcblx0XHRcdHZhciBkb3QwMSA9IHYwLmRvdCggdjEgKTtcblx0XHRcdHZhciBkb3QwMiA9IHYwLmRvdCggdjIgKTtcblx0XHRcdHZhciBkb3QxMSA9IHYxLmRvdCggdjEgKTtcblx0XHRcdHZhciBkb3QxMiA9IHYxLmRvdCggdjIgKTtcblxuXHRcdFx0dmFyIGRlbm9tID0gKCBkb3QwMCAqIGRvdDExIC0gZG90MDEgKiBkb3QwMSApO1xuXG5cdFx0XHR2YXIgcmVzdWx0ID0gb3B0aW9uYWxUYXJnZXQgfHwgbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0Ly8gY29sbGluZWFyIG9yIHNpbmd1bGFyIHRyaWFuZ2xlXG5cdFx0XHRpZiAoIGRlbm9tID09PSAwICkge1xuXG5cdFx0XHRcdC8vIGFyYml0cmFyeSBsb2NhdGlvbiBvdXRzaWRlIG9mIHRyaWFuZ2xlP1xuXHRcdFx0XHQvLyBub3Qgc3VyZSBpZiB0aGlzIGlzIHRoZSBiZXN0IGlkZWEsIG1heWJlIHNob3VsZCBiZSByZXR1cm5pbmcgdW5kZWZpbmVkXG5cdFx0XHRcdHJldHVybiByZXN1bHQuc2V0KCAtIDIsIC0gMSwgLSAxICk7XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIGludkRlbm9tID0gMSAvIGRlbm9tO1xuXHRcdFx0dmFyIHUgPSAoIGRvdDExICogZG90MDIgLSBkb3QwMSAqIGRvdDEyICkgKiBpbnZEZW5vbTtcblx0XHRcdHZhciB2ID0gKCBkb3QwMCAqIGRvdDEyIC0gZG90MDEgKiBkb3QwMiApICogaW52RGVub207XG5cblx0XHRcdC8vIGJhcnljZW50cmljIGNvb3JkaW5hdGVzIG11c3QgYWx3YXlzIHN1bSB0byAxXG5cdFx0XHRyZXR1cm4gcmVzdWx0LnNldCggMSAtIHUgLSB2LCB2LCB1ICk7XG5cblx0XHR9O1xuXG5cdH0oKTtcblxuXHRUcmlhbmdsZS5jb250YWluc1BvaW50ID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0dmFyIHYxID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdHJldHVybiBmdW5jdGlvbiBjb250YWluc1BvaW50KCBwb2ludCwgYSwgYiwgYyApIHtcblxuXHRcdFx0dmFyIHJlc3VsdCA9IFRyaWFuZ2xlLmJhcnljb29yZEZyb21Qb2ludCggcG9pbnQsIGEsIGIsIGMsIHYxICk7XG5cblx0XHRcdHJldHVybiAoIHJlc3VsdC54ID49IDAgKSAmJiAoIHJlc3VsdC55ID49IDAgKSAmJiAoICggcmVzdWx0LnggKyByZXN1bHQueSApIDw9IDEgKTtcblxuXHRcdH07XG5cblx0fSgpO1xuXG5cdFRyaWFuZ2xlLnByb3RvdHlwZSA9IHtcblxuXHRcdGNvbnN0cnVjdG9yOiBUcmlhbmdsZSxcblxuXHRcdHNldDogZnVuY3Rpb24gKCBhLCBiLCBjICkge1xuXG5cdFx0XHR0aGlzLmEuY29weSggYSApO1xuXHRcdFx0dGhpcy5iLmNvcHkoIGIgKTtcblx0XHRcdHRoaXMuYy5jb3B5KCBjICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldEZyb21Qb2ludHNBbmRJbmRpY2VzOiBmdW5jdGlvbiAoIHBvaW50cywgaTAsIGkxLCBpMiApIHtcblxuXHRcdFx0dGhpcy5hLmNvcHkoIHBvaW50c1sgaTAgXSApO1xuXHRcdFx0dGhpcy5iLmNvcHkoIHBvaW50c1sgaTEgXSApO1xuXHRcdFx0dGhpcy5jLmNvcHkoIHBvaW50c1sgaTIgXSApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjbG9uZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IHRoaXMuY29uc3RydWN0b3IoKS5jb3B5KCB0aGlzICk7XG5cblx0XHR9LFxuXG5cdFx0Y29weTogZnVuY3Rpb24gKCB0cmlhbmdsZSApIHtcblxuXHRcdFx0dGhpcy5hLmNvcHkoIHRyaWFuZ2xlLmEgKTtcblx0XHRcdHRoaXMuYi5jb3B5KCB0cmlhbmdsZS5iICk7XG5cdFx0XHR0aGlzLmMuY29weSggdHJpYW5nbGUuYyApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRhcmVhOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB2MCA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHR2YXIgdjEgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gYXJlYSgpIHtcblxuXHRcdFx0XHR2MC5zdWJWZWN0b3JzKCB0aGlzLmMsIHRoaXMuYiApO1xuXHRcdFx0XHR2MS5zdWJWZWN0b3JzKCB0aGlzLmEsIHRoaXMuYiApO1xuXG5cdFx0XHRcdHJldHVybiB2MC5jcm9zcyggdjEgKS5sZW5ndGgoKSAqIDAuNTtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdG1pZHBvaW50OiBmdW5jdGlvbiAoIG9wdGlvbmFsVGFyZ2V0ICkge1xuXG5cdFx0XHR2YXIgcmVzdWx0ID0gb3B0aW9uYWxUYXJnZXQgfHwgbmV3IFZlY3RvcjMoKTtcblx0XHRcdHJldHVybiByZXN1bHQuYWRkVmVjdG9ycyggdGhpcy5hLCB0aGlzLmIgKS5hZGQoIHRoaXMuYyApLm11bHRpcGx5U2NhbGFyKCAxIC8gMyApO1xuXG5cdFx0fSxcblxuXHRcdG5vcm1hbDogZnVuY3Rpb24gKCBvcHRpb25hbFRhcmdldCApIHtcblxuXHRcdFx0cmV0dXJuIFRyaWFuZ2xlLm5vcm1hbCggdGhpcy5hLCB0aGlzLmIsIHRoaXMuYywgb3B0aW9uYWxUYXJnZXQgKTtcblxuXHRcdH0sXG5cblx0XHRwbGFuZTogZnVuY3Rpb24gKCBvcHRpb25hbFRhcmdldCApIHtcblxuXHRcdFx0dmFyIHJlc3VsdCA9IG9wdGlvbmFsVGFyZ2V0IHx8IG5ldyBQbGFuZSgpO1xuXG5cdFx0XHRyZXR1cm4gcmVzdWx0LnNldEZyb21Db3BsYW5hclBvaW50cyggdGhpcy5hLCB0aGlzLmIsIHRoaXMuYyApO1xuXG5cdFx0fSxcblxuXHRcdGJhcnljb29yZEZyb21Qb2ludDogZnVuY3Rpb24gKCBwb2ludCwgb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdHJldHVybiBUcmlhbmdsZS5iYXJ5Y29vcmRGcm9tUG9pbnQoIHBvaW50LCB0aGlzLmEsIHRoaXMuYiwgdGhpcy5jLCBvcHRpb25hbFRhcmdldCApO1xuXG5cdFx0fSxcblxuXHRcdGNvbnRhaW5zUG9pbnQ6IGZ1bmN0aW9uICggcG9pbnQgKSB7XG5cblx0XHRcdHJldHVybiBUcmlhbmdsZS5jb250YWluc1BvaW50KCBwb2ludCwgdGhpcy5hLCB0aGlzLmIsIHRoaXMuYyApO1xuXG5cdFx0fSxcblxuXHRcdGNsb3Nlc3RQb2ludFRvUG9pbnQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIHBsYW5lLCBlZGdlTGlzdCwgcHJvamVjdGVkUG9pbnQsIGNsb3Nlc3RQb2ludDtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIGNsb3Nlc3RQb2ludFRvUG9pbnQoIHBvaW50LCBvcHRpb25hbFRhcmdldCApIHtcblxuXHRcdFx0XHRpZiAoIHBsYW5lID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRwbGFuZSA9IG5ldyBQbGFuZSgpO1xuXHRcdFx0XHRcdGVkZ2VMaXN0ID0gWyBuZXcgTGluZTMoKSwgbmV3IExpbmUzKCksIG5ldyBMaW5lMygpIF07XG5cdFx0XHRcdFx0cHJvamVjdGVkUG9pbnQgPSBuZXcgVmVjdG9yMygpO1xuXHRcdFx0XHRcdGNsb3Nlc3RQb2ludCA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciByZXN1bHQgPSBvcHRpb25hbFRhcmdldCB8fCBuZXcgVmVjdG9yMygpO1xuXHRcdFx0XHR2YXIgbWluRGlzdGFuY2UgPSBJbmZpbml0eTtcblxuXHRcdFx0XHQvLyBwcm9qZWN0IHRoZSBwb2ludCBvbnRvIHRoZSBwbGFuZSBvZiB0aGUgdHJpYW5nbGVcblxuXHRcdFx0XHRwbGFuZS5zZXRGcm9tQ29wbGFuYXJQb2ludHMoIHRoaXMuYSwgdGhpcy5iLCB0aGlzLmMgKTtcblx0XHRcdFx0cGxhbmUucHJvamVjdFBvaW50KCBwb2ludCwgcHJvamVjdGVkUG9pbnQgKTtcblxuXHRcdFx0XHQvLyBjaGVjayBpZiB0aGUgcHJvamVjdGlvbiBsaWVzIHdpdGhpbiB0aGUgdHJpYW5nbGVcblxuXHRcdFx0XHRpZiggdGhpcy5jb250YWluc1BvaW50KCBwcm9qZWN0ZWRQb2ludCApID09PSB0cnVlICkge1xuXG5cdFx0XHRcdFx0Ly8gaWYgc28sIHRoaXMgaXMgdGhlIGNsb3Nlc3QgcG9pbnRcblxuXHRcdFx0XHRcdHJlc3VsdC5jb3B5KCBwcm9qZWN0ZWRQb2ludCApO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHQvLyBpZiBub3QsIHRoZSBwb2ludCBmYWxscyBvdXRzaWRlIHRoZSB0cmlhbmdsZS4gdGhlIHJlc3VsdCBpcyB0aGUgY2xvc2VzdCBwb2ludCB0byB0aGUgdHJpYW5nbGUncyBlZGdlcyBvciB2ZXJ0aWNlc1xuXG5cdFx0XHRcdFx0ZWRnZUxpc3RbIDAgXS5zZXQoIHRoaXMuYSwgdGhpcy5iICk7XG5cdFx0XHRcdFx0ZWRnZUxpc3RbIDEgXS5zZXQoIHRoaXMuYiwgdGhpcy5jICk7XG5cdFx0XHRcdFx0ZWRnZUxpc3RbIDIgXS5zZXQoIHRoaXMuYywgdGhpcy5hICk7XG5cblx0XHRcdFx0XHRmb3IoIHZhciBpID0gMDsgaSA8IGVkZ2VMaXN0Lmxlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHRcdFx0ZWRnZUxpc3RbIGkgXS5jbG9zZXN0UG9pbnRUb1BvaW50KCBwcm9qZWN0ZWRQb2ludCwgdHJ1ZSwgY2xvc2VzdFBvaW50ICk7XG5cblx0XHRcdFx0XHRcdHZhciBkaXN0YW5jZSA9IHByb2plY3RlZFBvaW50LmRpc3RhbmNlVG9TcXVhcmVkKCBjbG9zZXN0UG9pbnQgKTtcblxuXHRcdFx0XHRcdFx0aWYoIGRpc3RhbmNlIDwgbWluRGlzdGFuY2UgKSB7XG5cblx0XHRcdFx0XHRcdFx0bWluRGlzdGFuY2UgPSBkaXN0YW5jZTtcblxuXHRcdFx0XHRcdFx0XHRyZXN1bHQuY29weSggY2xvc2VzdFBvaW50ICk7XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuIHJlc3VsdDtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdGVxdWFsczogZnVuY3Rpb24gKCB0cmlhbmdsZSApIHtcblxuXHRcdFx0cmV0dXJuIHRyaWFuZ2xlLmEuZXF1YWxzKCB0aGlzLmEgKSAmJiB0cmlhbmdsZS5iLmVxdWFscyggdGhpcy5iICkgJiYgdHJpYW5nbGUuYy5lcXVhbHMoIHRoaXMuYyApO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEZhY2UzKCBhLCBiLCBjLCBub3JtYWwsIGNvbG9yLCBtYXRlcmlhbEluZGV4ICkge1xuXG5cdFx0dGhpcy5hID0gYTtcblx0XHR0aGlzLmIgPSBiO1xuXHRcdHRoaXMuYyA9IGM7XG5cblx0XHR0aGlzLm5vcm1hbCA9IChub3JtYWwgJiYgbm9ybWFsLmlzVmVjdG9yMykgPyBub3JtYWwgOiBuZXcgVmVjdG9yMygpO1xuXHRcdHRoaXMudmVydGV4Tm9ybWFscyA9IEFycmF5LmlzQXJyYXkoIG5vcm1hbCApID8gbm9ybWFsIDogW107XG5cblx0XHR0aGlzLmNvbG9yID0gKGNvbG9yICYmIGNvbG9yLmlzQ29sb3IpID8gY29sb3IgOiBuZXcgQ29sb3IoKTtcblx0XHR0aGlzLnZlcnRleENvbG9ycyA9IEFycmF5LmlzQXJyYXkoIGNvbG9yICkgPyBjb2xvciA6IFtdO1xuXG5cdFx0dGhpcy5tYXRlcmlhbEluZGV4ID0gbWF0ZXJpYWxJbmRleCAhPT0gdW5kZWZpbmVkID8gbWF0ZXJpYWxJbmRleCA6IDA7XG5cblx0fVxuXG5cdEZhY2UzLnByb3RvdHlwZSA9IHtcblxuXHRcdGNvbnN0cnVjdG9yOiBGYWNlMyxcblxuXHRcdGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiBuZXcgdGhpcy5jb25zdHJ1Y3RvcigpLmNvcHkoIHRoaXMgKTtcblxuXHRcdH0sXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIHNvdXJjZSApIHtcblxuXHRcdFx0dGhpcy5hID0gc291cmNlLmE7XG5cdFx0XHR0aGlzLmIgPSBzb3VyY2UuYjtcblx0XHRcdHRoaXMuYyA9IHNvdXJjZS5jO1xuXG5cdFx0XHR0aGlzLm5vcm1hbC5jb3B5KCBzb3VyY2Uubm9ybWFsICk7XG5cdFx0XHR0aGlzLmNvbG9yLmNvcHkoIHNvdXJjZS5jb2xvciApO1xuXG5cdFx0XHR0aGlzLm1hdGVyaWFsSW5kZXggPSBzb3VyY2UubWF0ZXJpYWxJbmRleDtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBpbCA9IHNvdXJjZS52ZXJ0ZXhOb3JtYWxzLmxlbmd0aDsgaSA8IGlsOyBpICsrICkge1xuXG5cdFx0XHRcdHRoaXMudmVydGV4Tm9ybWFsc1sgaSBdID0gc291cmNlLnZlcnRleE5vcm1hbHNbIGkgXS5jbG9uZSgpO1xuXG5cdFx0XHR9XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgaWwgPSBzb3VyY2UudmVydGV4Q29sb3JzLmxlbmd0aDsgaSA8IGlsOyBpICsrICkge1xuXG5cdFx0XHRcdHRoaXMudmVydGV4Q29sb3JzWyBpIF0gPSBzb3VyY2UudmVydGV4Q29sb3JzWyBpIF0uY2xvbmUoKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH1cblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKlxuXHQgKiBwYXJhbWV0ZXJzID0ge1xuXHQgKiAgY29sb3I6IDxoZXg+LFxuXHQgKiAgb3BhY2l0eTogPGZsb2F0Pixcblx0ICogIG1hcDogbmV3IFRIUkVFLlRleHR1cmUoIDxJbWFnZT4gKSxcblx0ICpcblx0ICogIGxpZ2h0TWFwOiBuZXcgVEhSRUUuVGV4dHVyZSggPEltYWdlPiApLFxuXHQgKiAgbGlnaHRNYXBJbnRlbnNpdHk6IDxmbG9hdD5cblx0ICpcblx0ICogIGFvTWFwOiBuZXcgVEhSRUUuVGV4dHVyZSggPEltYWdlPiApLFxuXHQgKiAgYW9NYXBJbnRlbnNpdHk6IDxmbG9hdD5cblx0ICpcblx0ICogIHNwZWN1bGFyTWFwOiBuZXcgVEhSRUUuVGV4dHVyZSggPEltYWdlPiApLFxuXHQgKlxuXHQgKiAgYWxwaGFNYXA6IG5ldyBUSFJFRS5UZXh0dXJlKCA8SW1hZ2U+ICksXG5cdCAqXG5cdCAqICBlbnZNYXA6IG5ldyBUSFJFRS5UZXh0dXJlQ3ViZSggW3Bvc3gsIG5lZ3gsIHBvc3ksIG5lZ3ksIHBvc3osIG5lZ3pdICksXG5cdCAqICBjb21iaW5lOiBUSFJFRS5NdWx0aXBseSxcblx0ICogIHJlZmxlY3Rpdml0eTogPGZsb2F0Pixcblx0ICogIHJlZnJhY3Rpb25SYXRpbzogPGZsb2F0Pixcblx0ICpcblx0ICogIHNoYWRpbmc6IFRIUkVFLlNtb290aFNoYWRpbmcsXG5cdCAqICBkZXB0aFRlc3Q6IDxib29sPixcblx0ICogIGRlcHRoV3JpdGU6IDxib29sPixcblx0ICpcblx0ICogIHdpcmVmcmFtZTogPGJvb2xlYW4+LFxuXHQgKiAgd2lyZWZyYW1lTGluZXdpZHRoOiA8ZmxvYXQ+LFxuXHQgKlxuXHQgKiAgc2tpbm5pbmc6IDxib29sPixcblx0ICogIG1vcnBoVGFyZ2V0czogPGJvb2w+XG5cdCAqIH1cblx0ICovXG5cblx0ZnVuY3Rpb24gTWVzaEJhc2ljTWF0ZXJpYWwoIHBhcmFtZXRlcnMgKSB7XG5cblx0XHRNYXRlcmlhbC5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnTWVzaEJhc2ljTWF0ZXJpYWwnO1xuXG5cdFx0dGhpcy5jb2xvciA9IG5ldyBDb2xvciggMHhmZmZmZmYgKTsgLy8gZW1pc3NpdmVcblxuXHRcdHRoaXMubWFwID0gbnVsbDtcblxuXHRcdHRoaXMubGlnaHRNYXAgPSBudWxsO1xuXHRcdHRoaXMubGlnaHRNYXBJbnRlbnNpdHkgPSAxLjA7XG5cblx0XHR0aGlzLmFvTWFwID0gbnVsbDtcblx0XHR0aGlzLmFvTWFwSW50ZW5zaXR5ID0gMS4wO1xuXG5cdFx0dGhpcy5zcGVjdWxhck1hcCA9IG51bGw7XG5cblx0XHR0aGlzLmFscGhhTWFwID0gbnVsbDtcblxuXHRcdHRoaXMuZW52TWFwID0gbnVsbDtcblx0XHR0aGlzLmNvbWJpbmUgPSBNdWx0aXBseU9wZXJhdGlvbjtcblx0XHR0aGlzLnJlZmxlY3Rpdml0eSA9IDE7XG5cdFx0dGhpcy5yZWZyYWN0aW9uUmF0aW8gPSAwLjk4O1xuXG5cdFx0dGhpcy53aXJlZnJhbWUgPSBmYWxzZTtcblx0XHR0aGlzLndpcmVmcmFtZUxpbmV3aWR0aCA9IDE7XG5cdFx0dGhpcy53aXJlZnJhbWVMaW5lY2FwID0gJ3JvdW5kJztcblx0XHR0aGlzLndpcmVmcmFtZUxpbmVqb2luID0gJ3JvdW5kJztcblxuXHRcdHRoaXMuc2tpbm5pbmcgPSBmYWxzZTtcblx0XHR0aGlzLm1vcnBoVGFyZ2V0cyA9IGZhbHNlO1xuXG5cdFx0dGhpcy5saWdodHMgPSBmYWxzZTtcblxuXHRcdHRoaXMuc2V0VmFsdWVzKCBwYXJhbWV0ZXJzICk7XG5cblx0fVxuXG5cdE1lc2hCYXNpY01hdGVyaWFsLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIE1hdGVyaWFsLnByb3RvdHlwZSApO1xuXHRNZXNoQmFzaWNNYXRlcmlhbC5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBNZXNoQmFzaWNNYXRlcmlhbDtcblxuXHRNZXNoQmFzaWNNYXRlcmlhbC5wcm90b3R5cGUuaXNNZXNoQmFzaWNNYXRlcmlhbCA9IHRydWU7XG5cblx0TWVzaEJhc2ljTWF0ZXJpYWwucHJvdG90eXBlLmNvcHkgPSBmdW5jdGlvbiAoIHNvdXJjZSApIHtcblxuXHRcdE1hdGVyaWFsLnByb3RvdHlwZS5jb3B5LmNhbGwoIHRoaXMsIHNvdXJjZSApO1xuXG5cdFx0dGhpcy5jb2xvci5jb3B5KCBzb3VyY2UuY29sb3IgKTtcblxuXHRcdHRoaXMubWFwID0gc291cmNlLm1hcDtcblxuXHRcdHRoaXMubGlnaHRNYXAgPSBzb3VyY2UubGlnaHRNYXA7XG5cdFx0dGhpcy5saWdodE1hcEludGVuc2l0eSA9IHNvdXJjZS5saWdodE1hcEludGVuc2l0eTtcblxuXHRcdHRoaXMuYW9NYXAgPSBzb3VyY2UuYW9NYXA7XG5cdFx0dGhpcy5hb01hcEludGVuc2l0eSA9IHNvdXJjZS5hb01hcEludGVuc2l0eTtcblxuXHRcdHRoaXMuc3BlY3VsYXJNYXAgPSBzb3VyY2Uuc3BlY3VsYXJNYXA7XG5cblx0XHR0aGlzLmFscGhhTWFwID0gc291cmNlLmFscGhhTWFwO1xuXG5cdFx0dGhpcy5lbnZNYXAgPSBzb3VyY2UuZW52TWFwO1xuXHRcdHRoaXMuY29tYmluZSA9IHNvdXJjZS5jb21iaW5lO1xuXHRcdHRoaXMucmVmbGVjdGl2aXR5ID0gc291cmNlLnJlZmxlY3Rpdml0eTtcblx0XHR0aGlzLnJlZnJhY3Rpb25SYXRpbyA9IHNvdXJjZS5yZWZyYWN0aW9uUmF0aW87XG5cblx0XHR0aGlzLndpcmVmcmFtZSA9IHNvdXJjZS53aXJlZnJhbWU7XG5cdFx0dGhpcy53aXJlZnJhbWVMaW5ld2lkdGggPSBzb3VyY2Uud2lyZWZyYW1lTGluZXdpZHRoO1xuXHRcdHRoaXMud2lyZWZyYW1lTGluZWNhcCA9IHNvdXJjZS53aXJlZnJhbWVMaW5lY2FwO1xuXHRcdHRoaXMud2lyZWZyYW1lTGluZWpvaW4gPSBzb3VyY2Uud2lyZWZyYW1lTGluZWpvaW47XG5cblx0XHR0aGlzLnNraW5uaW5nID0gc291cmNlLnNraW5uaW5nO1xuXHRcdHRoaXMubW9ycGhUYXJnZXRzID0gc291cmNlLm1vcnBoVGFyZ2V0cztcblxuXHRcdHJldHVybiB0aGlzO1xuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEJ1ZmZlckF0dHJpYnV0ZSggYXJyYXksIGl0ZW1TaXplLCBub3JtYWxpemVkICkge1xuXG5cdFx0aWYgKCBBcnJheS5pc0FycmF5KCBhcnJheSApICkge1xuXG5cdFx0XHR0aHJvdyBuZXcgVHlwZUVycm9yKCAnVEhSRUUuQnVmZmVyQXR0cmlidXRlOiBhcnJheSBzaG91bGQgYmUgYSBUeXBlZCBBcnJheS4nICk7XG5cblx0XHR9XG5cblx0XHR0aGlzLnV1aWQgPSBfTWF0aC5nZW5lcmF0ZVVVSUQoKTtcblxuXHRcdHRoaXMuYXJyYXkgPSBhcnJheTtcblx0XHR0aGlzLml0ZW1TaXplID0gaXRlbVNpemU7XG5cdFx0dGhpcy5jb3VudCA9IGFycmF5ICE9PSB1bmRlZmluZWQgPyBhcnJheS5sZW5ndGggLyBpdGVtU2l6ZSA6IDA7XG5cdFx0dGhpcy5ub3JtYWxpemVkID0gbm9ybWFsaXplZCA9PT0gdHJ1ZTtcblxuXHRcdHRoaXMuZHluYW1pYyA9IGZhbHNlO1xuXHRcdHRoaXMudXBkYXRlUmFuZ2UgPSB7IG9mZnNldDogMCwgY291bnQ6IC0gMSB9O1xuXG5cdFx0dGhpcy5vblVwbG9hZENhbGxiYWNrID0gZnVuY3Rpb24gKCkge307XG5cblx0XHR0aGlzLnZlcnNpb24gPSAwO1xuXG5cdH1cblxuXHRCdWZmZXJBdHRyaWJ1dGUucHJvdG90eXBlID0ge1xuXG5cdFx0Y29uc3RydWN0b3I6IEJ1ZmZlckF0dHJpYnV0ZSxcblxuXHRcdGlzQnVmZmVyQXR0cmlidXRlOiB0cnVlLFxuXG5cdFx0c2V0IG5lZWRzVXBkYXRlKCB2YWx1ZSApIHtcblxuXHRcdFx0aWYgKCB2YWx1ZSA9PT0gdHJ1ZSApIHRoaXMudmVyc2lvbiArKztcblxuXHRcdH0sXG5cblx0XHRzZXRBcnJheTogZnVuY3Rpb24gKCBhcnJheSApIHtcblxuXHRcdFx0aWYgKCBBcnJheS5pc0FycmF5KCBhcnJheSApICkge1xuXG5cdFx0XHRcdHRocm93IG5ldyBUeXBlRXJyb3IoICdUSFJFRS5CdWZmZXJBdHRyaWJ1dGU6IGFycmF5IHNob3VsZCBiZSBhIFR5cGVkIEFycmF5LicgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLmNvdW50ID0gYXJyYXkgIT09IHVuZGVmaW5lZCA/IGFycmF5Lmxlbmd0aCAvIHRoaXMuaXRlbVNpemUgOiAwO1xuXHRcdFx0dGhpcy5hcnJheSA9IGFycmF5O1xuXG5cdFx0fSxcblxuXHRcdHNldER5bmFtaWM6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdHRoaXMuZHluYW1pYyA9IHZhbHVlO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIHNvdXJjZSApIHtcblxuXHRcdFx0dGhpcy5hcnJheSA9IG5ldyBzb3VyY2UuYXJyYXkuY29uc3RydWN0b3IoIHNvdXJjZS5hcnJheSApO1xuXHRcdFx0dGhpcy5pdGVtU2l6ZSA9IHNvdXJjZS5pdGVtU2l6ZTtcblx0XHRcdHRoaXMuY291bnQgPSBzb3VyY2UuY291bnQ7XG5cdFx0XHR0aGlzLm5vcm1hbGl6ZWQgPSBzb3VyY2Uubm9ybWFsaXplZDtcblxuXHRcdFx0dGhpcy5keW5hbWljID0gc291cmNlLmR5bmFtaWM7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGNvcHlBdDogZnVuY3Rpb24gKCBpbmRleDEsIGF0dHJpYnV0ZSwgaW5kZXgyICkge1xuXG5cdFx0XHRpbmRleDEgKj0gdGhpcy5pdGVtU2l6ZTtcblx0XHRcdGluZGV4MiAqPSBhdHRyaWJ1dGUuaXRlbVNpemU7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IHRoaXMuaXRlbVNpemU7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdHRoaXMuYXJyYXlbIGluZGV4MSArIGkgXSA9IGF0dHJpYnV0ZS5hcnJheVsgaW5kZXgyICsgaSBdO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGNvcHlBcnJheTogZnVuY3Rpb24gKCBhcnJheSApIHtcblxuXHRcdFx0dGhpcy5hcnJheS5zZXQoIGFycmF5ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGNvcHlDb2xvcnNBcnJheTogZnVuY3Rpb24gKCBjb2xvcnMgKSB7XG5cblx0XHRcdHZhciBhcnJheSA9IHRoaXMuYXJyYXksIG9mZnNldCA9IDA7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGNvbG9ycy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdHZhciBjb2xvciA9IGNvbG9yc1sgaSBdO1xuXG5cdFx0XHRcdGlmICggY29sb3IgPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkJ1ZmZlckF0dHJpYnV0ZS5jb3B5Q29sb3JzQXJyYXkoKTogY29sb3IgaXMgdW5kZWZpbmVkJywgaSApO1xuXHRcdFx0XHRcdGNvbG9yID0gbmV3IENvbG9yKCk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGFycmF5WyBvZmZzZXQgKysgXSA9IGNvbG9yLnI7XG5cdFx0XHRcdGFycmF5WyBvZmZzZXQgKysgXSA9IGNvbG9yLmc7XG5cdFx0XHRcdGFycmF5WyBvZmZzZXQgKysgXSA9IGNvbG9yLmI7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Y29weUluZGljZXNBcnJheTogZnVuY3Rpb24gKCBpbmRpY2VzICkge1xuXG5cdFx0XHR2YXIgYXJyYXkgPSB0aGlzLmFycmF5LCBvZmZzZXQgPSAwO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBpbmRpY2VzLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0dmFyIGluZGV4ID0gaW5kaWNlc1sgaSBdO1xuXG5cdFx0XHRcdGFycmF5WyBvZmZzZXQgKysgXSA9IGluZGV4LmE7XG5cdFx0XHRcdGFycmF5WyBvZmZzZXQgKysgXSA9IGluZGV4LmI7XG5cdFx0XHRcdGFycmF5WyBvZmZzZXQgKysgXSA9IGluZGV4LmM7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Y29weVZlY3RvcjJzQXJyYXk6IGZ1bmN0aW9uICggdmVjdG9ycyApIHtcblxuXHRcdFx0dmFyIGFycmF5ID0gdGhpcy5hcnJheSwgb2Zmc2V0ID0gMDtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBsID0gdmVjdG9ycy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdHZhciB2ZWN0b3IgPSB2ZWN0b3JzWyBpIF07XG5cblx0XHRcdFx0aWYgKCB2ZWN0b3IgPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkJ1ZmZlckF0dHJpYnV0ZS5jb3B5VmVjdG9yMnNBcnJheSgpOiB2ZWN0b3IgaXMgdW5kZWZpbmVkJywgaSApO1xuXHRcdFx0XHRcdHZlY3RvciA9IG5ldyBWZWN0b3IyKCk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGFycmF5WyBvZmZzZXQgKysgXSA9IHZlY3Rvci54O1xuXHRcdFx0XHRhcnJheVsgb2Zmc2V0ICsrIF0gPSB2ZWN0b3IueTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjb3B5VmVjdG9yM3NBcnJheTogZnVuY3Rpb24gKCB2ZWN0b3JzICkge1xuXG5cdFx0XHR2YXIgYXJyYXkgPSB0aGlzLmFycmF5LCBvZmZzZXQgPSAwO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSB2ZWN0b3JzLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0dmFyIHZlY3RvciA9IHZlY3RvcnNbIGkgXTtcblxuXHRcdFx0XHRpZiAoIHZlY3RvciA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuQnVmZmVyQXR0cmlidXRlLmNvcHlWZWN0b3Izc0FycmF5KCk6IHZlY3RvciBpcyB1bmRlZmluZWQnLCBpICk7XG5cdFx0XHRcdFx0dmVjdG9yID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0YXJyYXlbIG9mZnNldCArKyBdID0gdmVjdG9yLng7XG5cdFx0XHRcdGFycmF5WyBvZmZzZXQgKysgXSA9IHZlY3Rvci55O1xuXHRcdFx0XHRhcnJheVsgb2Zmc2V0ICsrIF0gPSB2ZWN0b3IuejtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjb3B5VmVjdG9yNHNBcnJheTogZnVuY3Rpb24gKCB2ZWN0b3JzICkge1xuXG5cdFx0XHR2YXIgYXJyYXkgPSB0aGlzLmFycmF5LCBvZmZzZXQgPSAwO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSB2ZWN0b3JzLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0dmFyIHZlY3RvciA9IHZlY3RvcnNbIGkgXTtcblxuXHRcdFx0XHRpZiAoIHZlY3RvciA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuQnVmZmVyQXR0cmlidXRlLmNvcHlWZWN0b3I0c0FycmF5KCk6IHZlY3RvciBpcyB1bmRlZmluZWQnLCBpICk7XG5cdFx0XHRcdFx0dmVjdG9yID0gbmV3IFZlY3RvcjQoKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0YXJyYXlbIG9mZnNldCArKyBdID0gdmVjdG9yLng7XG5cdFx0XHRcdGFycmF5WyBvZmZzZXQgKysgXSA9IHZlY3Rvci55O1xuXHRcdFx0XHRhcnJheVsgb2Zmc2V0ICsrIF0gPSB2ZWN0b3Iuejtcblx0XHRcdFx0YXJyYXlbIG9mZnNldCArKyBdID0gdmVjdG9yLnc7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0OiBmdW5jdGlvbiAoIHZhbHVlLCBvZmZzZXQgKSB7XG5cblx0XHRcdGlmICggb2Zmc2V0ID09PSB1bmRlZmluZWQgKSBvZmZzZXQgPSAwO1xuXG5cdFx0XHR0aGlzLmFycmF5LnNldCggdmFsdWUsIG9mZnNldCApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRnZXRYOiBmdW5jdGlvbiAoIGluZGV4ICkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5hcnJheVsgaW5kZXggKiB0aGlzLml0ZW1TaXplIF07XG5cblx0XHR9LFxuXG5cdFx0c2V0WDogZnVuY3Rpb24gKCBpbmRleCwgeCApIHtcblxuXHRcdFx0dGhpcy5hcnJheVsgaW5kZXggKiB0aGlzLml0ZW1TaXplIF0gPSB4O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRnZXRZOiBmdW5jdGlvbiAoIGluZGV4ICkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5hcnJheVsgaW5kZXggKiB0aGlzLml0ZW1TaXplICsgMSBdO1xuXG5cdFx0fSxcblxuXHRcdHNldFk6IGZ1bmN0aW9uICggaW5kZXgsIHkgKSB7XG5cblx0XHRcdHRoaXMuYXJyYXlbIGluZGV4ICogdGhpcy5pdGVtU2l6ZSArIDEgXSA9IHk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGdldFo6IGZ1bmN0aW9uICggaW5kZXggKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLmFycmF5WyBpbmRleCAqIHRoaXMuaXRlbVNpemUgKyAyIF07XG5cblx0XHR9LFxuXG5cdFx0c2V0WjogZnVuY3Rpb24gKCBpbmRleCwgeiApIHtcblxuXHRcdFx0dGhpcy5hcnJheVsgaW5kZXggKiB0aGlzLml0ZW1TaXplICsgMiBdID0gejtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Z2V0VzogZnVuY3Rpb24gKCBpbmRleCApIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuYXJyYXlbIGluZGV4ICogdGhpcy5pdGVtU2l6ZSArIDMgXTtcblxuXHRcdH0sXG5cblx0XHRzZXRXOiBmdW5jdGlvbiAoIGluZGV4LCB3ICkge1xuXG5cdFx0XHR0aGlzLmFycmF5WyBpbmRleCAqIHRoaXMuaXRlbVNpemUgKyAzIF0gPSB3O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRYWTogZnVuY3Rpb24gKCBpbmRleCwgeCwgeSApIHtcblxuXHRcdFx0aW5kZXggKj0gdGhpcy5pdGVtU2l6ZTtcblxuXHRcdFx0dGhpcy5hcnJheVsgaW5kZXggKyAwIF0gPSB4O1xuXHRcdFx0dGhpcy5hcnJheVsgaW5kZXggKyAxIF0gPSB5O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRYWVo6IGZ1bmN0aW9uICggaW5kZXgsIHgsIHksIHogKSB7XG5cblx0XHRcdGluZGV4ICo9IHRoaXMuaXRlbVNpemU7XG5cblx0XHRcdHRoaXMuYXJyYXlbIGluZGV4ICsgMCBdID0geDtcblx0XHRcdHRoaXMuYXJyYXlbIGluZGV4ICsgMSBdID0geTtcblx0XHRcdHRoaXMuYXJyYXlbIGluZGV4ICsgMiBdID0gejtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0WFlaVzogZnVuY3Rpb24gKCBpbmRleCwgeCwgeSwgeiwgdyApIHtcblxuXHRcdFx0aW5kZXggKj0gdGhpcy5pdGVtU2l6ZTtcblxuXHRcdFx0dGhpcy5hcnJheVsgaW5kZXggKyAwIF0gPSB4O1xuXHRcdFx0dGhpcy5hcnJheVsgaW5kZXggKyAxIF0gPSB5O1xuXHRcdFx0dGhpcy5hcnJheVsgaW5kZXggKyAyIF0gPSB6O1xuXHRcdFx0dGhpcy5hcnJheVsgaW5kZXggKyAzIF0gPSB3O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRvblVwbG9hZDogZnVuY3Rpb24gKCBjYWxsYmFjayApIHtcblxuXHRcdFx0dGhpcy5vblVwbG9hZENhbGxiYWNrID0gY2FsbGJhY2s7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiBuZXcgdGhpcy5jb25zdHJ1Y3RvcigpLmNvcHkoIHRoaXMgKTtcblxuXHRcdH1cblxuXHR9O1xuXG5cdC8vXG5cblx0ZnVuY3Rpb24gSW50OEJ1ZmZlckF0dHJpYnV0ZSggYXJyYXksIGl0ZW1TaXplICkge1xuXG5cdFx0QnVmZmVyQXR0cmlidXRlLmNhbGwoIHRoaXMsIG5ldyBJbnQ4QXJyYXkoIGFycmF5ICksIGl0ZW1TaXplICk7XG5cblx0fVxuXG5cdEludDhCdWZmZXJBdHRyaWJ1dGUucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggQnVmZmVyQXR0cmlidXRlLnByb3RvdHlwZSApO1xuXHRJbnQ4QnVmZmVyQXR0cmlidXRlLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IEludDhCdWZmZXJBdHRyaWJ1dGU7XG5cblxuXHRmdW5jdGlvbiBVaW50OEJ1ZmZlckF0dHJpYnV0ZSggYXJyYXksIGl0ZW1TaXplICkge1xuXG5cdFx0QnVmZmVyQXR0cmlidXRlLmNhbGwoIHRoaXMsIG5ldyBVaW50OEFycmF5KCBhcnJheSApLCBpdGVtU2l6ZSApO1xuXG5cdH1cblxuXHRVaW50OEJ1ZmZlckF0dHJpYnV0ZS5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBCdWZmZXJBdHRyaWJ1dGUucHJvdG90eXBlICk7XG5cdFVpbnQ4QnVmZmVyQXR0cmlidXRlLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFVpbnQ4QnVmZmVyQXR0cmlidXRlO1xuXG5cblx0ZnVuY3Rpb24gVWludDhDbGFtcGVkQnVmZmVyQXR0cmlidXRlKCBhcnJheSwgaXRlbVNpemUgKSB7XG5cblx0XHRCdWZmZXJBdHRyaWJ1dGUuY2FsbCggdGhpcywgbmV3IFVpbnQ4Q2xhbXBlZEFycmF5KCBhcnJheSApLCBpdGVtU2l6ZSApO1xuXG5cdH1cblxuXHRVaW50OENsYW1wZWRCdWZmZXJBdHRyaWJ1dGUucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggQnVmZmVyQXR0cmlidXRlLnByb3RvdHlwZSApO1xuXHRVaW50OENsYW1wZWRCdWZmZXJBdHRyaWJ1dGUucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gVWludDhDbGFtcGVkQnVmZmVyQXR0cmlidXRlO1xuXG5cblx0ZnVuY3Rpb24gSW50MTZCdWZmZXJBdHRyaWJ1dGUoIGFycmF5LCBpdGVtU2l6ZSApIHtcblxuXHRcdEJ1ZmZlckF0dHJpYnV0ZS5jYWxsKCB0aGlzLCBuZXcgSW50MTZBcnJheSggYXJyYXkgKSwgaXRlbVNpemUgKTtcblxuXHR9XG5cblx0SW50MTZCdWZmZXJBdHRyaWJ1dGUucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggQnVmZmVyQXR0cmlidXRlLnByb3RvdHlwZSApO1xuXHRJbnQxNkJ1ZmZlckF0dHJpYnV0ZS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBJbnQxNkJ1ZmZlckF0dHJpYnV0ZTtcblxuXG5cdGZ1bmN0aW9uIFVpbnQxNkJ1ZmZlckF0dHJpYnV0ZSggYXJyYXksIGl0ZW1TaXplICkge1xuXG5cdFx0QnVmZmVyQXR0cmlidXRlLmNhbGwoIHRoaXMsIG5ldyBVaW50MTZBcnJheSggYXJyYXkgKSwgaXRlbVNpemUgKTtcblxuXHR9XG5cblx0VWludDE2QnVmZmVyQXR0cmlidXRlLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEJ1ZmZlckF0dHJpYnV0ZS5wcm90b3R5cGUgKTtcblx0VWludDE2QnVmZmVyQXR0cmlidXRlLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFVpbnQxNkJ1ZmZlckF0dHJpYnV0ZTtcblxuXG5cdGZ1bmN0aW9uIEludDMyQnVmZmVyQXR0cmlidXRlKCBhcnJheSwgaXRlbVNpemUgKSB7XG5cblx0XHRCdWZmZXJBdHRyaWJ1dGUuY2FsbCggdGhpcywgbmV3IEludDMyQXJyYXkoIGFycmF5ICksIGl0ZW1TaXplICk7XG5cblx0fVxuXG5cdEludDMyQnVmZmVyQXR0cmlidXRlLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEJ1ZmZlckF0dHJpYnV0ZS5wcm90b3R5cGUgKTtcblx0SW50MzJCdWZmZXJBdHRyaWJ1dGUucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gSW50MzJCdWZmZXJBdHRyaWJ1dGU7XG5cblxuXHRmdW5jdGlvbiBVaW50MzJCdWZmZXJBdHRyaWJ1dGUoIGFycmF5LCBpdGVtU2l6ZSApIHtcblxuXHRcdEJ1ZmZlckF0dHJpYnV0ZS5jYWxsKCB0aGlzLCBuZXcgVWludDMyQXJyYXkoIGFycmF5ICksIGl0ZW1TaXplICk7XG5cblx0fVxuXG5cdFVpbnQzMkJ1ZmZlckF0dHJpYnV0ZS5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBCdWZmZXJBdHRyaWJ1dGUucHJvdG90eXBlICk7XG5cdFVpbnQzMkJ1ZmZlckF0dHJpYnV0ZS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBVaW50MzJCdWZmZXJBdHRyaWJ1dGU7XG5cblxuXHRmdW5jdGlvbiBGbG9hdDMyQnVmZmVyQXR0cmlidXRlKCBhcnJheSwgaXRlbVNpemUgKSB7XG5cblx0XHRCdWZmZXJBdHRyaWJ1dGUuY2FsbCggdGhpcywgbmV3IEZsb2F0MzJBcnJheSggYXJyYXkgKSwgaXRlbVNpemUgKTtcblxuXHR9XG5cblx0RmxvYXQzMkJ1ZmZlckF0dHJpYnV0ZS5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBCdWZmZXJBdHRyaWJ1dGUucHJvdG90eXBlICk7XG5cdEZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gRmxvYXQzMkJ1ZmZlckF0dHJpYnV0ZTtcblxuXG5cdGZ1bmN0aW9uIEZsb2F0NjRCdWZmZXJBdHRyaWJ1dGUoIGFycmF5LCBpdGVtU2l6ZSApIHtcblxuXHRcdEJ1ZmZlckF0dHJpYnV0ZS5jYWxsKCB0aGlzLCBuZXcgRmxvYXQ2NEFycmF5KCBhcnJheSApLCBpdGVtU2l6ZSApO1xuXG5cdH1cblxuXHRGbG9hdDY0QnVmZmVyQXR0cmlidXRlLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEJ1ZmZlckF0dHJpYnV0ZS5wcm90b3R5cGUgKTtcblx0RmxvYXQ2NEJ1ZmZlckF0dHJpYnV0ZS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBGbG9hdDY0QnVmZmVyQXR0cmlidXRlO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBEaXJlY3RHZW9tZXRyeSgpIHtcblxuXHRcdHRoaXMuaW5kaWNlcyA9IFtdO1xuXHRcdHRoaXMudmVydGljZXMgPSBbXTtcblx0XHR0aGlzLm5vcm1hbHMgPSBbXTtcblx0XHR0aGlzLmNvbG9ycyA9IFtdO1xuXHRcdHRoaXMudXZzID0gW107XG5cdFx0dGhpcy51dnMyID0gW107XG5cblx0XHR0aGlzLmdyb3VwcyA9IFtdO1xuXG5cdFx0dGhpcy5tb3JwaFRhcmdldHMgPSB7fTtcblxuXHRcdHRoaXMuc2tpbldlaWdodHMgPSBbXTtcblx0XHR0aGlzLnNraW5JbmRpY2VzID0gW107XG5cblx0XHQvLyB0aGlzLmxpbmVEaXN0YW5jZXMgPSBbXTtcblxuXHRcdHRoaXMuYm91bmRpbmdCb3ggPSBudWxsO1xuXHRcdHRoaXMuYm91bmRpbmdTcGhlcmUgPSBudWxsO1xuXG5cdFx0Ly8gdXBkYXRlIGZsYWdzXG5cblx0XHR0aGlzLnZlcnRpY2VzTmVlZFVwZGF0ZSA9IGZhbHNlO1xuXHRcdHRoaXMubm9ybWFsc05lZWRVcGRhdGUgPSBmYWxzZTtcblx0XHR0aGlzLmNvbG9yc05lZWRVcGRhdGUgPSBmYWxzZTtcblx0XHR0aGlzLnV2c05lZWRVcGRhdGUgPSBmYWxzZTtcblx0XHR0aGlzLmdyb3Vwc05lZWRVcGRhdGUgPSBmYWxzZTtcblxuXHR9XG5cblx0T2JqZWN0LmFzc2lnbiggRGlyZWN0R2VvbWV0cnkucHJvdG90eXBlLCB7XG5cblx0XHRjb21wdXRlR3JvdXBzOiBmdW5jdGlvbiAoIGdlb21ldHJ5ICkge1xuXG5cdFx0XHR2YXIgZ3JvdXA7XG5cdFx0XHR2YXIgZ3JvdXBzID0gW107XG5cdFx0XHR2YXIgbWF0ZXJpYWxJbmRleCA9IHVuZGVmaW5lZDtcblxuXHRcdFx0dmFyIGZhY2VzID0gZ2VvbWV0cnkuZmFjZXM7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IGZhY2VzLmxlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHR2YXIgZmFjZSA9IGZhY2VzWyBpIF07XG5cblx0XHRcdFx0Ly8gbWF0ZXJpYWxzXG5cblx0XHRcdFx0aWYgKCBmYWNlLm1hdGVyaWFsSW5kZXggIT09IG1hdGVyaWFsSW5kZXggKSB7XG5cblx0XHRcdFx0XHRtYXRlcmlhbEluZGV4ID0gZmFjZS5tYXRlcmlhbEluZGV4O1xuXG5cdFx0XHRcdFx0aWYgKCBncm91cCAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0XHRncm91cC5jb3VudCA9ICggaSAqIDMgKSAtIGdyb3VwLnN0YXJ0O1xuXHRcdFx0XHRcdFx0Z3JvdXBzLnB1c2goIGdyb3VwICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRncm91cCA9IHtcblx0XHRcdFx0XHRcdHN0YXJ0OiBpICogMyxcblx0XHRcdFx0XHRcdG1hdGVyaWFsSW5kZXg6IG1hdGVyaWFsSW5kZXhcblx0XHRcdFx0XHR9O1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIGdyb3VwICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0Z3JvdXAuY291bnQgPSAoIGkgKiAzICkgLSBncm91cC5zdGFydDtcblx0XHRcdFx0Z3JvdXBzLnB1c2goIGdyb3VwICk7XG5cblx0XHRcdH1cblxuXHRcdFx0dGhpcy5ncm91cHMgPSBncm91cHM7XG5cblx0XHR9LFxuXG5cdFx0ZnJvbUdlb21ldHJ5OiBmdW5jdGlvbiAoIGdlb21ldHJ5ICkge1xuXG5cdFx0XHR2YXIgZmFjZXMgPSBnZW9tZXRyeS5mYWNlcztcblx0XHRcdHZhciB2ZXJ0aWNlcyA9IGdlb21ldHJ5LnZlcnRpY2VzO1xuXHRcdFx0dmFyIGZhY2VWZXJ0ZXhVdnMgPSBnZW9tZXRyeS5mYWNlVmVydGV4VXZzO1xuXG5cdFx0XHR2YXIgaGFzRmFjZVZlcnRleFV2ID0gZmFjZVZlcnRleFV2c1sgMCBdICYmIGZhY2VWZXJ0ZXhVdnNbIDAgXS5sZW5ndGggPiAwO1xuXHRcdFx0dmFyIGhhc0ZhY2VWZXJ0ZXhVdjIgPSBmYWNlVmVydGV4VXZzWyAxIF0gJiYgZmFjZVZlcnRleFV2c1sgMSBdLmxlbmd0aCA+IDA7XG5cblx0XHRcdC8vIG1vcnBoc1xuXG5cdFx0XHR2YXIgbW9ycGhUYXJnZXRzID0gZ2VvbWV0cnkubW9ycGhUYXJnZXRzO1xuXHRcdFx0dmFyIG1vcnBoVGFyZ2V0c0xlbmd0aCA9IG1vcnBoVGFyZ2V0cy5sZW5ndGg7XG5cblx0XHRcdHZhciBtb3JwaFRhcmdldHNQb3NpdGlvbjtcblxuXHRcdFx0aWYgKCBtb3JwaFRhcmdldHNMZW5ndGggPiAwICkge1xuXG5cdFx0XHRcdG1vcnBoVGFyZ2V0c1Bvc2l0aW9uID0gW107XG5cblx0XHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgbW9ycGhUYXJnZXRzTGVuZ3RoOyBpICsrICkge1xuXG5cdFx0XHRcdFx0bW9ycGhUYXJnZXRzUG9zaXRpb25bIGkgXSA9IFtdO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR0aGlzLm1vcnBoVGFyZ2V0cy5wb3NpdGlvbiA9IG1vcnBoVGFyZ2V0c1Bvc2l0aW9uO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBtb3JwaE5vcm1hbHMgPSBnZW9tZXRyeS5tb3JwaE5vcm1hbHM7XG5cdFx0XHR2YXIgbW9ycGhOb3JtYWxzTGVuZ3RoID0gbW9ycGhOb3JtYWxzLmxlbmd0aDtcblxuXHRcdFx0dmFyIG1vcnBoVGFyZ2V0c05vcm1hbDtcblxuXHRcdFx0aWYgKCBtb3JwaE5vcm1hbHNMZW5ndGggPiAwICkge1xuXG5cdFx0XHRcdG1vcnBoVGFyZ2V0c05vcm1hbCA9IFtdO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IG1vcnBoTm9ybWFsc0xlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHRcdG1vcnBoVGFyZ2V0c05vcm1hbFsgaSBdID0gW107XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHRoaXMubW9ycGhUYXJnZXRzLm5vcm1hbCA9IG1vcnBoVGFyZ2V0c05vcm1hbDtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyBza2luc1xuXG5cdFx0XHR2YXIgc2tpbkluZGljZXMgPSBnZW9tZXRyeS5za2luSW5kaWNlcztcblx0XHRcdHZhciBza2luV2VpZ2h0cyA9IGdlb21ldHJ5LnNraW5XZWlnaHRzO1xuXG5cdFx0XHR2YXIgaGFzU2tpbkluZGljZXMgPSBza2luSW5kaWNlcy5sZW5ndGggPT09IHZlcnRpY2VzLmxlbmd0aDtcblx0XHRcdHZhciBoYXNTa2luV2VpZ2h0cyA9IHNraW5XZWlnaHRzLmxlbmd0aCA9PT0gdmVydGljZXMubGVuZ3RoO1xuXG5cdFx0XHQvL1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBmYWNlcy5sZW5ndGg7IGkgKysgKSB7XG5cblx0XHRcdFx0dmFyIGZhY2UgPSBmYWNlc1sgaSBdO1xuXG5cdFx0XHRcdHRoaXMudmVydGljZXMucHVzaCggdmVydGljZXNbIGZhY2UuYSBdLCB2ZXJ0aWNlc1sgZmFjZS5iIF0sIHZlcnRpY2VzWyBmYWNlLmMgXSApO1xuXG5cdFx0XHRcdHZhciB2ZXJ0ZXhOb3JtYWxzID0gZmFjZS52ZXJ0ZXhOb3JtYWxzO1xuXG5cdFx0XHRcdGlmICggdmVydGV4Tm9ybWFscy5sZW5ndGggPT09IDMgKSB7XG5cblx0XHRcdFx0XHR0aGlzLm5vcm1hbHMucHVzaCggdmVydGV4Tm9ybWFsc1sgMCBdLCB2ZXJ0ZXhOb3JtYWxzWyAxIF0sIHZlcnRleE5vcm1hbHNbIDIgXSApO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHR2YXIgbm9ybWFsID0gZmFjZS5ub3JtYWw7XG5cblx0XHRcdFx0XHR0aGlzLm5vcm1hbHMucHVzaCggbm9ybWFsLCBub3JtYWwsIG5vcm1hbCApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR2YXIgdmVydGV4Q29sb3JzID0gZmFjZS52ZXJ0ZXhDb2xvcnM7XG5cblx0XHRcdFx0aWYgKCB2ZXJ0ZXhDb2xvcnMubGVuZ3RoID09PSAzICkge1xuXG5cdFx0XHRcdFx0dGhpcy5jb2xvcnMucHVzaCggdmVydGV4Q29sb3JzWyAwIF0sIHZlcnRleENvbG9yc1sgMSBdLCB2ZXJ0ZXhDb2xvcnNbIDIgXSApO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHR2YXIgY29sb3IgPSBmYWNlLmNvbG9yO1xuXG5cdFx0XHRcdFx0dGhpcy5jb2xvcnMucHVzaCggY29sb3IsIGNvbG9yLCBjb2xvciApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoIGhhc0ZhY2VWZXJ0ZXhVdiA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRcdHZhciB2ZXJ0ZXhVdnMgPSBmYWNlVmVydGV4VXZzWyAwIF1bIGkgXTtcblxuXHRcdFx0XHRcdGlmICggdmVydGV4VXZzICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRcdHRoaXMudXZzLnB1c2goIHZlcnRleFV2c1sgMCBdLCB2ZXJ0ZXhVdnNbIDEgXSwgdmVydGV4VXZzWyAyIF0gKTtcblxuXHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkRpcmVjdEdlb21ldHJ5LmZyb21HZW9tZXRyeSgpOiBVbmRlZmluZWQgdmVydGV4VXYgJywgaSApO1xuXG5cdFx0XHRcdFx0XHR0aGlzLnV2cy5wdXNoKCBuZXcgVmVjdG9yMigpLCBuZXcgVmVjdG9yMigpLCBuZXcgVmVjdG9yMigpICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggaGFzRmFjZVZlcnRleFV2MiA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRcdHZhciB2ZXJ0ZXhVdnMgPSBmYWNlVmVydGV4VXZzWyAxIF1bIGkgXTtcblxuXHRcdFx0XHRcdGlmICggdmVydGV4VXZzICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRcdHRoaXMudXZzMi5wdXNoKCB2ZXJ0ZXhVdnNbIDAgXSwgdmVydGV4VXZzWyAxIF0sIHZlcnRleFV2c1sgMiBdICk7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5EaXJlY3RHZW9tZXRyeS5mcm9tR2VvbWV0cnkoKTogVW5kZWZpbmVkIHZlcnRleFV2MiAnLCBpICk7XG5cblx0XHRcdFx0XHRcdHRoaXMudXZzMi5wdXNoKCBuZXcgVmVjdG9yMigpLCBuZXcgVmVjdG9yMigpLCBuZXcgVmVjdG9yMigpICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIG1vcnBoc1xuXG5cdFx0XHRcdGZvciAoIHZhciBqID0gMDsgaiA8IG1vcnBoVGFyZ2V0c0xlbmd0aDsgaiArKyApIHtcblxuXHRcdFx0XHRcdHZhciBtb3JwaFRhcmdldCA9IG1vcnBoVGFyZ2V0c1sgaiBdLnZlcnRpY2VzO1xuXG5cdFx0XHRcdFx0bW9ycGhUYXJnZXRzUG9zaXRpb25bIGogXS5wdXNoKCBtb3JwaFRhcmdldFsgZmFjZS5hIF0sIG1vcnBoVGFyZ2V0WyBmYWNlLmIgXSwgbW9ycGhUYXJnZXRbIGZhY2UuYyBdICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGZvciAoIHZhciBqID0gMDsgaiA8IG1vcnBoTm9ybWFsc0xlbmd0aDsgaiArKyApIHtcblxuXHRcdFx0XHRcdHZhciBtb3JwaE5vcm1hbCA9IG1vcnBoTm9ybWFsc1sgaiBdLnZlcnRleE5vcm1hbHNbIGkgXTtcblxuXHRcdFx0XHRcdG1vcnBoVGFyZ2V0c05vcm1hbFsgaiBdLnB1c2goIG1vcnBoTm9ybWFsLmEsIG1vcnBoTm9ybWFsLmIsIG1vcnBoTm9ybWFsLmMgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gc2tpbnNcblxuXHRcdFx0XHRpZiAoIGhhc1NraW5JbmRpY2VzICkge1xuXG5cdFx0XHRcdFx0dGhpcy5za2luSW5kaWNlcy5wdXNoKCBza2luSW5kaWNlc1sgZmFjZS5hIF0sIHNraW5JbmRpY2VzWyBmYWNlLmIgXSwgc2tpbkluZGljZXNbIGZhY2UuYyBdICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggaGFzU2tpbldlaWdodHMgKSB7XG5cblx0XHRcdFx0XHR0aGlzLnNraW5XZWlnaHRzLnB1c2goIHNraW5XZWlnaHRzWyBmYWNlLmEgXSwgc2tpbldlaWdodHNbIGZhY2UuYiBdLCBza2luV2VpZ2h0c1sgZmFjZS5jIF0gKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0dGhpcy5jb21wdXRlR3JvdXBzKCBnZW9tZXRyeSApO1xuXG5cdFx0XHR0aGlzLnZlcnRpY2VzTmVlZFVwZGF0ZSA9IGdlb21ldHJ5LnZlcnRpY2VzTmVlZFVwZGF0ZTtcblx0XHRcdHRoaXMubm9ybWFsc05lZWRVcGRhdGUgPSBnZW9tZXRyeS5ub3JtYWxzTmVlZFVwZGF0ZTtcblx0XHRcdHRoaXMuY29sb3JzTmVlZFVwZGF0ZSA9IGdlb21ldHJ5LmNvbG9yc05lZWRVcGRhdGU7XG5cdFx0XHR0aGlzLnV2c05lZWRVcGRhdGUgPSBnZW9tZXRyeS51dnNOZWVkVXBkYXRlO1xuXHRcdFx0dGhpcy5ncm91cHNOZWVkVXBkYXRlID0gZ2VvbWV0cnkuZ3JvdXBzTmVlZFVwZGF0ZTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yIGtpbGUgLyBodHRwOi8va2lsZS5zdHJhdmFnYW56YS5vcmcvXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqIEBhdXRob3IgbWlrYWVsIGVtdGluZ2VyIC8gaHR0cDovL2dvbW8uc2UvXG5cdCAqIEBhdXRob3Igeno4NSAvIGh0dHA6Ly93d3cubGFiNGdhbWVzLm5ldC96ejg1L2Jsb2dcblx0ICogQGF1dGhvciBiaG91c3RvbiAvIGh0dHA6Ly9jbGFyYS5pb1xuXHQgKi9cblxuXHRmdW5jdGlvbiBHZW9tZXRyeSgpIHtcblxuXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eSggdGhpcywgJ2lkJywgeyB2YWx1ZTogR2VvbWV0cnlJZENvdW50KCkgfSApO1xuXG5cdFx0dGhpcy51dWlkID0gX01hdGguZ2VuZXJhdGVVVUlEKCk7XG5cblx0XHR0aGlzLm5hbWUgPSAnJztcblx0XHR0aGlzLnR5cGUgPSAnR2VvbWV0cnknO1xuXG5cdFx0dGhpcy52ZXJ0aWNlcyA9IFtdO1xuXHRcdHRoaXMuY29sb3JzID0gW107XG5cdFx0dGhpcy5mYWNlcyA9IFtdO1xuXHRcdHRoaXMuZmFjZVZlcnRleFV2cyA9IFsgW10gXTtcblxuXHRcdHRoaXMubW9ycGhUYXJnZXRzID0gW107XG5cdFx0dGhpcy5tb3JwaE5vcm1hbHMgPSBbXTtcblxuXHRcdHRoaXMuc2tpbldlaWdodHMgPSBbXTtcblx0XHR0aGlzLnNraW5JbmRpY2VzID0gW107XG5cblx0XHR0aGlzLmxpbmVEaXN0YW5jZXMgPSBbXTtcblxuXHRcdHRoaXMuYm91bmRpbmdCb3ggPSBudWxsO1xuXHRcdHRoaXMuYm91bmRpbmdTcGhlcmUgPSBudWxsO1xuXG5cdFx0Ly8gdXBkYXRlIGZsYWdzXG5cblx0XHR0aGlzLmVsZW1lbnRzTmVlZFVwZGF0ZSA9IGZhbHNlO1xuXHRcdHRoaXMudmVydGljZXNOZWVkVXBkYXRlID0gZmFsc2U7XG5cdFx0dGhpcy51dnNOZWVkVXBkYXRlID0gZmFsc2U7XG5cdFx0dGhpcy5ub3JtYWxzTmVlZFVwZGF0ZSA9IGZhbHNlO1xuXHRcdHRoaXMuY29sb3JzTmVlZFVwZGF0ZSA9IGZhbHNlO1xuXHRcdHRoaXMubGluZURpc3RhbmNlc05lZWRVcGRhdGUgPSBmYWxzZTtcblx0XHR0aGlzLmdyb3Vwc05lZWRVcGRhdGUgPSBmYWxzZTtcblxuXHR9XG5cblx0T2JqZWN0LmFzc2lnbiggR2VvbWV0cnkucHJvdG90eXBlLCBFdmVudERpc3BhdGNoZXIucHJvdG90eXBlLCB7XG5cblx0XHRpc0dlb21ldHJ5OiB0cnVlLFxuXG5cdFx0YXBwbHlNYXRyaXg6IGZ1bmN0aW9uICggbWF0cml4ICkge1xuXG5cdFx0XHR2YXIgbm9ybWFsTWF0cml4ID0gbmV3IE1hdHJpeDMoKS5nZXROb3JtYWxNYXRyaXgoIG1hdHJpeCApO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGlsID0gdGhpcy52ZXJ0aWNlcy5sZW5ndGg7IGkgPCBpbDsgaSArKyApIHtcblxuXHRcdFx0XHR2YXIgdmVydGV4ID0gdGhpcy52ZXJ0aWNlc1sgaSBdO1xuXHRcdFx0XHR2ZXJ0ZXguYXBwbHlNYXRyaXg0KCBtYXRyaXggKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGlsID0gdGhpcy5mYWNlcy5sZW5ndGg7IGkgPCBpbDsgaSArKyApIHtcblxuXHRcdFx0XHR2YXIgZmFjZSA9IHRoaXMuZmFjZXNbIGkgXTtcblx0XHRcdFx0ZmFjZS5ub3JtYWwuYXBwbHlNYXRyaXgzKCBub3JtYWxNYXRyaXggKS5ub3JtYWxpemUoKTtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaiA9IDAsIGpsID0gZmFjZS52ZXJ0ZXhOb3JtYWxzLmxlbmd0aDsgaiA8IGpsOyBqICsrICkge1xuXG5cdFx0XHRcdFx0ZmFjZS52ZXJ0ZXhOb3JtYWxzWyBqIF0uYXBwbHlNYXRyaXgzKCBub3JtYWxNYXRyaXggKS5ub3JtYWxpemUoKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCB0aGlzLmJvdW5kaW5nQm94ICE9PSBudWxsICkge1xuXG5cdFx0XHRcdHRoaXMuY29tcHV0ZUJvdW5kaW5nQm94KCk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCB0aGlzLmJvdW5kaW5nU3BoZXJlICE9PSBudWxsICkge1xuXG5cdFx0XHRcdHRoaXMuY29tcHV0ZUJvdW5kaW5nU3BoZXJlKCk7XG5cblx0XHRcdH1cblxuXHRcdFx0dGhpcy52ZXJ0aWNlc05lZWRVcGRhdGUgPSB0cnVlO1xuXHRcdFx0dGhpcy5ub3JtYWxzTmVlZFVwZGF0ZSA9IHRydWU7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHJvdGF0ZVg6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Ly8gcm90YXRlIGdlb21ldHJ5IGFyb3VuZCB3b3JsZCB4LWF4aXNcblxuXHRcdFx0dmFyIG0xO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gcm90YXRlWCggYW5nbGUgKSB7XG5cblx0XHRcdFx0aWYgKCBtMSA9PT0gdW5kZWZpbmVkICkgbTEgPSBuZXcgTWF0cml4NCgpO1xuXG5cdFx0XHRcdG0xLm1ha2VSb3RhdGlvblgoIGFuZ2xlICk7XG5cblx0XHRcdFx0dGhpcy5hcHBseU1hdHJpeCggbTEgKTtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdHJvdGF0ZVk6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Ly8gcm90YXRlIGdlb21ldHJ5IGFyb3VuZCB3b3JsZCB5LWF4aXNcblxuXHRcdFx0dmFyIG0xO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gcm90YXRlWSggYW5nbGUgKSB7XG5cblx0XHRcdFx0aWYgKCBtMSA9PT0gdW5kZWZpbmVkICkgbTEgPSBuZXcgTWF0cml4NCgpO1xuXG5cdFx0XHRcdG0xLm1ha2VSb3RhdGlvblkoIGFuZ2xlICk7XG5cblx0XHRcdFx0dGhpcy5hcHBseU1hdHJpeCggbTEgKTtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdHJvdGF0ZVo6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Ly8gcm90YXRlIGdlb21ldHJ5IGFyb3VuZCB3b3JsZCB6LWF4aXNcblxuXHRcdFx0dmFyIG0xO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gcm90YXRlWiggYW5nbGUgKSB7XG5cblx0XHRcdFx0aWYgKCBtMSA9PT0gdW5kZWZpbmVkICkgbTEgPSBuZXcgTWF0cml4NCgpO1xuXG5cdFx0XHRcdG0xLm1ha2VSb3RhdGlvblooIGFuZ2xlICk7XG5cblx0XHRcdFx0dGhpcy5hcHBseU1hdHJpeCggbTEgKTtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdHRyYW5zbGF0ZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHQvLyB0cmFuc2xhdGUgZ2VvbWV0cnlcblxuXHRcdFx0dmFyIG0xO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gdHJhbnNsYXRlKCB4LCB5LCB6ICkge1xuXG5cdFx0XHRcdGlmICggbTEgPT09IHVuZGVmaW5lZCApIG0xID0gbmV3IE1hdHJpeDQoKTtcblxuXHRcdFx0XHRtMS5tYWtlVHJhbnNsYXRpb24oIHgsIHksIHogKTtcblxuXHRcdFx0XHR0aGlzLmFwcGx5TWF0cml4KCBtMSApO1xuXG5cdFx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0c2NhbGU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Ly8gc2NhbGUgZ2VvbWV0cnlcblxuXHRcdFx0dmFyIG0xO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gc2NhbGUoIHgsIHksIHogKSB7XG5cblx0XHRcdFx0aWYgKCBtMSA9PT0gdW5kZWZpbmVkICkgbTEgPSBuZXcgTWF0cml4NCgpO1xuXG5cdFx0XHRcdG0xLm1ha2VTY2FsZSggeCwgeSwgeiApO1xuXG5cdFx0XHRcdHRoaXMuYXBwbHlNYXRyaXgoIG0xICk7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRsb29rQXQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIG9iajtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIGxvb2tBdCggdmVjdG9yICkge1xuXG5cdFx0XHRcdGlmICggb2JqID09PSB1bmRlZmluZWQgKSBvYmogPSBuZXcgT2JqZWN0M0QoKTtcblxuXHRcdFx0XHRvYmoubG9va0F0KCB2ZWN0b3IgKTtcblxuXHRcdFx0XHRvYmoudXBkYXRlTWF0cml4KCk7XG5cblx0XHRcdFx0dGhpcy5hcHBseU1hdHJpeCggb2JqLm1hdHJpeCApO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0ZnJvbUJ1ZmZlckdlb21ldHJ5OiBmdW5jdGlvbiAoIGdlb21ldHJ5ICkge1xuXG5cdFx0XHR2YXIgc2NvcGUgPSB0aGlzO1xuXG5cdFx0XHR2YXIgaW5kaWNlcyA9IGdlb21ldHJ5LmluZGV4ICE9PSBudWxsID8gZ2VvbWV0cnkuaW5kZXguYXJyYXkgOiB1bmRlZmluZWQ7XG5cdFx0XHR2YXIgYXR0cmlidXRlcyA9IGdlb21ldHJ5LmF0dHJpYnV0ZXM7XG5cblx0XHRcdHZhciBwb3NpdGlvbnMgPSBhdHRyaWJ1dGVzLnBvc2l0aW9uLmFycmF5O1xuXHRcdFx0dmFyIG5vcm1hbHMgPSBhdHRyaWJ1dGVzLm5vcm1hbCAhPT0gdW5kZWZpbmVkID8gYXR0cmlidXRlcy5ub3JtYWwuYXJyYXkgOiB1bmRlZmluZWQ7XG5cdFx0XHR2YXIgY29sb3JzID0gYXR0cmlidXRlcy5jb2xvciAhPT0gdW5kZWZpbmVkID8gYXR0cmlidXRlcy5jb2xvci5hcnJheSA6IHVuZGVmaW5lZDtcblx0XHRcdHZhciB1dnMgPSBhdHRyaWJ1dGVzLnV2ICE9PSB1bmRlZmluZWQgPyBhdHRyaWJ1dGVzLnV2LmFycmF5IDogdW5kZWZpbmVkO1xuXHRcdFx0dmFyIHV2czIgPSBhdHRyaWJ1dGVzLnV2MiAhPT0gdW5kZWZpbmVkID8gYXR0cmlidXRlcy51djIuYXJyYXkgOiB1bmRlZmluZWQ7XG5cblx0XHRcdGlmICggdXZzMiAhPT0gdW5kZWZpbmVkICkgdGhpcy5mYWNlVmVydGV4VXZzWyAxIF0gPSBbXTtcblxuXHRcdFx0dmFyIHRlbXBOb3JtYWxzID0gW107XG5cdFx0XHR2YXIgdGVtcFVWcyA9IFtdO1xuXHRcdFx0dmFyIHRlbXBVVnMyID0gW107XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgaiA9IDA7IGkgPCBwb3NpdGlvbnMubGVuZ3RoOyBpICs9IDMsIGogKz0gMiApIHtcblxuXHRcdFx0XHRzY29wZS52ZXJ0aWNlcy5wdXNoKCBuZXcgVmVjdG9yMyggcG9zaXRpb25zWyBpIF0sIHBvc2l0aW9uc1sgaSArIDEgXSwgcG9zaXRpb25zWyBpICsgMiBdICkgKTtcblxuXHRcdFx0XHRpZiAoIG5vcm1hbHMgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdHRlbXBOb3JtYWxzLnB1c2goIG5ldyBWZWN0b3IzKCBub3JtYWxzWyBpIF0sIG5vcm1hbHNbIGkgKyAxIF0sIG5vcm1hbHNbIGkgKyAyIF0gKSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoIGNvbG9ycyAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0c2NvcGUuY29sb3JzLnB1c2goIG5ldyBDb2xvciggY29sb3JzWyBpIF0sIGNvbG9yc1sgaSArIDEgXSwgY29sb3JzWyBpICsgMiBdICkgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCB1dnMgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdHRlbXBVVnMucHVzaCggbmV3IFZlY3RvcjIoIHV2c1sgaiBdLCB1dnNbIGogKyAxIF0gKSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoIHV2czIgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdHRlbXBVVnMyLnB1c2goIG5ldyBWZWN0b3IyKCB1dnMyWyBqIF0sIHV2czJbIGogKyAxIF0gKSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRmdW5jdGlvbiBhZGRGYWNlKCBhLCBiLCBjLCBtYXRlcmlhbEluZGV4ICkge1xuXG5cdFx0XHRcdHZhciB2ZXJ0ZXhOb3JtYWxzID0gbm9ybWFscyAhPT0gdW5kZWZpbmVkID8gWyB0ZW1wTm9ybWFsc1sgYSBdLmNsb25lKCksIHRlbXBOb3JtYWxzWyBiIF0uY2xvbmUoKSwgdGVtcE5vcm1hbHNbIGMgXS5jbG9uZSgpIF0gOiBbXTtcblx0XHRcdFx0dmFyIHZlcnRleENvbG9ycyA9IGNvbG9ycyAhPT0gdW5kZWZpbmVkID8gWyBzY29wZS5jb2xvcnNbIGEgXS5jbG9uZSgpLCBzY29wZS5jb2xvcnNbIGIgXS5jbG9uZSgpLCBzY29wZS5jb2xvcnNbIGMgXS5jbG9uZSgpIF0gOiBbXTtcblxuXHRcdFx0XHR2YXIgZmFjZSA9IG5ldyBGYWNlMyggYSwgYiwgYywgdmVydGV4Tm9ybWFscywgdmVydGV4Q29sb3JzLCBtYXRlcmlhbEluZGV4ICk7XG5cblx0XHRcdFx0c2NvcGUuZmFjZXMucHVzaCggZmFjZSApO1xuXG5cdFx0XHRcdGlmICggdXZzICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRzY29wZS5mYWNlVmVydGV4VXZzWyAwIF0ucHVzaCggWyB0ZW1wVVZzWyBhIF0uY2xvbmUoKSwgdGVtcFVWc1sgYiBdLmNsb25lKCksIHRlbXBVVnNbIGMgXS5jbG9uZSgpIF0gKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCB1dnMyICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRzY29wZS5mYWNlVmVydGV4VXZzWyAxIF0ucHVzaCggWyB0ZW1wVVZzMlsgYSBdLmNsb25lKCksIHRlbXBVVnMyWyBiIF0uY2xvbmUoKSwgdGVtcFVWczJbIGMgXS5jbG9uZSgpIF0gKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBpbmRpY2VzICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0dmFyIGdyb3VwcyA9IGdlb21ldHJ5Lmdyb3VwcztcblxuXHRcdFx0XHRpZiAoIGdyb3Vwcy5sZW5ndGggPiAwICkge1xuXG5cdFx0XHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgZ3JvdXBzLmxlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHRcdFx0dmFyIGdyb3VwID0gZ3JvdXBzWyBpIF07XG5cblx0XHRcdFx0XHRcdHZhciBzdGFydCA9IGdyb3VwLnN0YXJ0O1xuXHRcdFx0XHRcdFx0dmFyIGNvdW50ID0gZ3JvdXAuY291bnQ7XG5cblx0XHRcdFx0XHRcdGZvciAoIHZhciBqID0gc3RhcnQsIGpsID0gc3RhcnQgKyBjb3VudDsgaiA8IGpsOyBqICs9IDMgKSB7XG5cblx0XHRcdFx0XHRcdFx0YWRkRmFjZSggaW5kaWNlc1sgaiBdLCBpbmRpY2VzWyBqICsgMSBdLCBpbmRpY2VzWyBqICsgMiBdLCBncm91cC5tYXRlcmlhbEluZGV4ICApO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IGluZGljZXMubGVuZ3RoOyBpICs9IDMgKSB7XG5cblx0XHRcdFx0XHRcdGFkZEZhY2UoIGluZGljZXNbIGkgXSwgaW5kaWNlc1sgaSArIDEgXSwgaW5kaWNlc1sgaSArIDIgXSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBwb3NpdGlvbnMubGVuZ3RoIC8gMzsgaSArPSAzICkge1xuXG5cdFx0XHRcdFx0YWRkRmFjZSggaSwgaSArIDEsIGkgKyAyICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuY29tcHV0ZUZhY2VOb3JtYWxzKCk7XG5cblx0XHRcdGlmICggZ2VvbWV0cnkuYm91bmRpbmdCb3ggIT09IG51bGwgKSB7XG5cblx0XHRcdFx0dGhpcy5ib3VuZGluZ0JveCA9IGdlb21ldHJ5LmJvdW5kaW5nQm94LmNsb25lKCk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBnZW9tZXRyeS5ib3VuZGluZ1NwaGVyZSAhPT0gbnVsbCApIHtcblxuXHRcdFx0XHR0aGlzLmJvdW5kaW5nU3BoZXJlID0gZ2VvbWV0cnkuYm91bmRpbmdTcGhlcmUuY2xvbmUoKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjZW50ZXI6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dGhpcy5jb21wdXRlQm91bmRpbmdCb3goKTtcblxuXHRcdFx0dmFyIG9mZnNldCA9IHRoaXMuYm91bmRpbmdCb3guZ2V0Q2VudGVyKCkubmVnYXRlKCk7XG5cblx0XHRcdHRoaXMudHJhbnNsYXRlKCBvZmZzZXQueCwgb2Zmc2V0LnksIG9mZnNldC56ICk7XG5cblx0XHRcdHJldHVybiBvZmZzZXQ7XG5cblx0XHR9LFxuXG5cdFx0bm9ybWFsaXplOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMuY29tcHV0ZUJvdW5kaW5nU3BoZXJlKCk7XG5cblx0XHRcdHZhciBjZW50ZXIgPSB0aGlzLmJvdW5kaW5nU3BoZXJlLmNlbnRlcjtcblx0XHRcdHZhciByYWRpdXMgPSB0aGlzLmJvdW5kaW5nU3BoZXJlLnJhZGl1cztcblxuXHRcdFx0dmFyIHMgPSByYWRpdXMgPT09IDAgPyAxIDogMS4wIC8gcmFkaXVzO1xuXG5cdFx0XHR2YXIgbWF0cml4ID0gbmV3IE1hdHJpeDQoKTtcblx0XHRcdG1hdHJpeC5zZXQoXG5cdFx0XHRcdHMsIDAsIDAsIC0gcyAqIGNlbnRlci54LFxuXHRcdFx0XHQwLCBzLCAwLCAtIHMgKiBjZW50ZXIueSxcblx0XHRcdFx0MCwgMCwgcywgLSBzICogY2VudGVyLnosXG5cdFx0XHRcdDAsIDAsIDAsIDFcblx0XHRcdCk7XG5cblx0XHRcdHRoaXMuYXBwbHlNYXRyaXgoIG1hdHJpeCApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjb21wdXRlRmFjZU5vcm1hbHM6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIGNiID0gbmV3IFZlY3RvcjMoKSwgYWIgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRmb3IgKCB2YXIgZiA9IDAsIGZsID0gdGhpcy5mYWNlcy5sZW5ndGg7IGYgPCBmbDsgZiArKyApIHtcblxuXHRcdFx0XHR2YXIgZmFjZSA9IHRoaXMuZmFjZXNbIGYgXTtcblxuXHRcdFx0XHR2YXIgdkEgPSB0aGlzLnZlcnRpY2VzWyBmYWNlLmEgXTtcblx0XHRcdFx0dmFyIHZCID0gdGhpcy52ZXJ0aWNlc1sgZmFjZS5iIF07XG5cdFx0XHRcdHZhciB2QyA9IHRoaXMudmVydGljZXNbIGZhY2UuYyBdO1xuXG5cdFx0XHRcdGNiLnN1YlZlY3RvcnMoIHZDLCB2QiApO1xuXHRcdFx0XHRhYi5zdWJWZWN0b3JzKCB2QSwgdkIgKTtcblx0XHRcdFx0Y2IuY3Jvc3MoIGFiICk7XG5cblx0XHRcdFx0Y2Iubm9ybWFsaXplKCk7XG5cblx0XHRcdFx0ZmFjZS5ub3JtYWwuY29weSggY2IgKTtcblxuXHRcdFx0fVxuXG5cdFx0fSxcblxuXHRcdGNvbXB1dGVWZXJ0ZXhOb3JtYWxzOiBmdW5jdGlvbiAoIGFyZWFXZWlnaHRlZCApIHtcblxuXHRcdFx0aWYgKCBhcmVhV2VpZ2h0ZWQgPT09IHVuZGVmaW5lZCApIGFyZWFXZWlnaHRlZCA9IHRydWU7XG5cblx0XHRcdHZhciB2LCB2bCwgZiwgZmwsIGZhY2UsIHZlcnRpY2VzO1xuXG5cdFx0XHR2ZXJ0aWNlcyA9IG5ldyBBcnJheSggdGhpcy52ZXJ0aWNlcy5sZW5ndGggKTtcblxuXHRcdFx0Zm9yICggdiA9IDAsIHZsID0gdGhpcy52ZXJ0aWNlcy5sZW5ndGg7IHYgPCB2bDsgdiArKyApIHtcblxuXHRcdFx0XHR2ZXJ0aWNlc1sgdiBdID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIGFyZWFXZWlnaHRlZCApIHtcblxuXHRcdFx0XHQvLyB2ZXJ0ZXggbm9ybWFscyB3ZWlnaHRlZCBieSB0cmlhbmdsZSBhcmVhc1xuXHRcdFx0XHQvLyBodHRwOi8vd3d3LmlxdWlsZXpsZXMub3JnL3d3dy9hcnRpY2xlcy9ub3JtYWxzL25vcm1hbHMuaHRtXG5cblx0XHRcdFx0dmFyIHZBLCB2QiwgdkM7XG5cdFx0XHRcdHZhciBjYiA9IG5ldyBWZWN0b3IzKCksIGFiID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0XHRmb3IgKCBmID0gMCwgZmwgPSB0aGlzLmZhY2VzLmxlbmd0aDsgZiA8IGZsOyBmICsrICkge1xuXG5cdFx0XHRcdFx0ZmFjZSA9IHRoaXMuZmFjZXNbIGYgXTtcblxuXHRcdFx0XHRcdHZBID0gdGhpcy52ZXJ0aWNlc1sgZmFjZS5hIF07XG5cdFx0XHRcdFx0dkIgPSB0aGlzLnZlcnRpY2VzWyBmYWNlLmIgXTtcblx0XHRcdFx0XHR2QyA9IHRoaXMudmVydGljZXNbIGZhY2UuYyBdO1xuXG5cdFx0XHRcdFx0Y2Iuc3ViVmVjdG9ycyggdkMsIHZCICk7XG5cdFx0XHRcdFx0YWIuc3ViVmVjdG9ycyggdkEsIHZCICk7XG5cdFx0XHRcdFx0Y2IuY3Jvc3MoIGFiICk7XG5cblx0XHRcdFx0XHR2ZXJ0aWNlc1sgZmFjZS5hIF0uYWRkKCBjYiApO1xuXHRcdFx0XHRcdHZlcnRpY2VzWyBmYWNlLmIgXS5hZGQoIGNiICk7XG5cdFx0XHRcdFx0dmVydGljZXNbIGZhY2UuYyBdLmFkZCggY2IgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0dGhpcy5jb21wdXRlRmFjZU5vcm1hbHMoKTtcblxuXHRcdFx0XHRmb3IgKCBmID0gMCwgZmwgPSB0aGlzLmZhY2VzLmxlbmd0aDsgZiA8IGZsOyBmICsrICkge1xuXG5cdFx0XHRcdFx0ZmFjZSA9IHRoaXMuZmFjZXNbIGYgXTtcblxuXHRcdFx0XHRcdHZlcnRpY2VzWyBmYWNlLmEgXS5hZGQoIGZhY2Uubm9ybWFsICk7XG5cdFx0XHRcdFx0dmVydGljZXNbIGZhY2UuYiBdLmFkZCggZmFjZS5ub3JtYWwgKTtcblx0XHRcdFx0XHR2ZXJ0aWNlc1sgZmFjZS5jIF0uYWRkKCBmYWNlLm5vcm1hbCApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRmb3IgKCB2ID0gMCwgdmwgPSB0aGlzLnZlcnRpY2VzLmxlbmd0aDsgdiA8IHZsOyB2ICsrICkge1xuXG5cdFx0XHRcdHZlcnRpY2VzWyB2IF0ubm9ybWFsaXplKCk7XG5cblx0XHRcdH1cblxuXHRcdFx0Zm9yICggZiA9IDAsIGZsID0gdGhpcy5mYWNlcy5sZW5ndGg7IGYgPCBmbDsgZiArKyApIHtcblxuXHRcdFx0XHRmYWNlID0gdGhpcy5mYWNlc1sgZiBdO1xuXG5cdFx0XHRcdHZhciB2ZXJ0ZXhOb3JtYWxzID0gZmFjZS52ZXJ0ZXhOb3JtYWxzO1xuXG5cdFx0XHRcdGlmICggdmVydGV4Tm9ybWFscy5sZW5ndGggPT09IDMgKSB7XG5cblx0XHRcdFx0XHR2ZXJ0ZXhOb3JtYWxzWyAwIF0uY29weSggdmVydGljZXNbIGZhY2UuYSBdICk7XG5cdFx0XHRcdFx0dmVydGV4Tm9ybWFsc1sgMSBdLmNvcHkoIHZlcnRpY2VzWyBmYWNlLmIgXSApO1xuXHRcdFx0XHRcdHZlcnRleE5vcm1hbHNbIDIgXS5jb3B5KCB2ZXJ0aWNlc1sgZmFjZS5jIF0gKTtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0dmVydGV4Tm9ybWFsc1sgMCBdID0gdmVydGljZXNbIGZhY2UuYSBdLmNsb25lKCk7XG5cdFx0XHRcdFx0dmVydGV4Tm9ybWFsc1sgMSBdID0gdmVydGljZXNbIGZhY2UuYiBdLmNsb25lKCk7XG5cdFx0XHRcdFx0dmVydGV4Tm9ybWFsc1sgMiBdID0gdmVydGljZXNbIGZhY2UuYyBdLmNsb25lKCk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggdGhpcy5mYWNlcy5sZW5ndGggPiAwICkge1xuXG5cdFx0XHRcdHRoaXMubm9ybWFsc05lZWRVcGRhdGUgPSB0cnVlO1xuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0Y29tcHV0ZUZsYXRWZXJ0ZXhOb3JtYWxzOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBmLCBmbCwgZmFjZTtcblxuXHRcdFx0dGhpcy5jb21wdXRlRmFjZU5vcm1hbHMoKTtcblxuXHRcdFx0Zm9yICggZiA9IDAsIGZsID0gdGhpcy5mYWNlcy5sZW5ndGg7IGYgPCBmbDsgZiArKyApIHtcblxuXHRcdFx0XHRmYWNlID0gdGhpcy5mYWNlc1sgZiBdO1xuXG5cdFx0XHRcdHZhciB2ZXJ0ZXhOb3JtYWxzID0gZmFjZS52ZXJ0ZXhOb3JtYWxzO1xuXG5cdFx0XHRcdGlmICggdmVydGV4Tm9ybWFscy5sZW5ndGggPT09IDMgKSB7XG5cblx0XHRcdFx0XHR2ZXJ0ZXhOb3JtYWxzWyAwIF0uY29weSggZmFjZS5ub3JtYWwgKTtcblx0XHRcdFx0XHR2ZXJ0ZXhOb3JtYWxzWyAxIF0uY29weSggZmFjZS5ub3JtYWwgKTtcblx0XHRcdFx0XHR2ZXJ0ZXhOb3JtYWxzWyAyIF0uY29weSggZmFjZS5ub3JtYWwgKTtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0dmVydGV4Tm9ybWFsc1sgMCBdID0gZmFjZS5ub3JtYWwuY2xvbmUoKTtcblx0XHRcdFx0XHR2ZXJ0ZXhOb3JtYWxzWyAxIF0gPSBmYWNlLm5vcm1hbC5jbG9uZSgpO1xuXHRcdFx0XHRcdHZlcnRleE5vcm1hbHNbIDIgXSA9IGZhY2Uubm9ybWFsLmNsb25lKCk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggdGhpcy5mYWNlcy5sZW5ndGggPiAwICkge1xuXG5cdFx0XHRcdHRoaXMubm9ybWFsc05lZWRVcGRhdGUgPSB0cnVlO1xuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0Y29tcHV0ZU1vcnBoTm9ybWFsczogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgaSwgaWwsIGYsIGZsLCBmYWNlO1xuXG5cdFx0XHQvLyBzYXZlIG9yaWdpbmFsIG5vcm1hbHNcblx0XHRcdC8vIC0gY3JlYXRlIHRlbXAgdmFyaWFibGVzIG9uIGZpcnN0IGFjY2Vzc1xuXHRcdFx0Ly8gICBvdGhlcndpc2UganVzdCBjb3B5IChmb3IgZmFzdGVyIHJlcGVhdGVkIGNhbGxzKVxuXG5cdFx0XHRmb3IgKCBmID0gMCwgZmwgPSB0aGlzLmZhY2VzLmxlbmd0aDsgZiA8IGZsOyBmICsrICkge1xuXG5cdFx0XHRcdGZhY2UgPSB0aGlzLmZhY2VzWyBmIF07XG5cblx0XHRcdFx0aWYgKCAhIGZhY2UuX19vcmlnaW5hbEZhY2VOb3JtYWwgKSB7XG5cblx0XHRcdFx0XHRmYWNlLl9fb3JpZ2luYWxGYWNlTm9ybWFsID0gZmFjZS5ub3JtYWwuY2xvbmUoKTtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0ZmFjZS5fX29yaWdpbmFsRmFjZU5vcm1hbC5jb3B5KCBmYWNlLm5vcm1hbCApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoICEgZmFjZS5fX29yaWdpbmFsVmVydGV4Tm9ybWFscyApIGZhY2UuX19vcmlnaW5hbFZlcnRleE5vcm1hbHMgPSBbXTtcblxuXHRcdFx0XHRmb3IgKCBpID0gMCwgaWwgPSBmYWNlLnZlcnRleE5vcm1hbHMubGVuZ3RoOyBpIDwgaWw7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRpZiAoICEgZmFjZS5fX29yaWdpbmFsVmVydGV4Tm9ybWFsc1sgaSBdICkge1xuXG5cdFx0XHRcdFx0XHRmYWNlLl9fb3JpZ2luYWxWZXJ0ZXhOb3JtYWxzWyBpIF0gPSBmYWNlLnZlcnRleE5vcm1hbHNbIGkgXS5jbG9uZSgpO1xuXG5cdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0ZmFjZS5fX29yaWdpbmFsVmVydGV4Tm9ybWFsc1sgaSBdLmNvcHkoIGZhY2UudmVydGV4Tm9ybWFsc1sgaSBdICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdC8vIHVzZSB0ZW1wIGdlb21ldHJ5IHRvIGNvbXB1dGUgZmFjZSBhbmQgdmVydGV4IG5vcm1hbHMgZm9yIGVhY2ggbW9ycGhcblxuXHRcdFx0dmFyIHRtcEdlbyA9IG5ldyBHZW9tZXRyeSgpO1xuXHRcdFx0dG1wR2VvLmZhY2VzID0gdGhpcy5mYWNlcztcblxuXHRcdFx0Zm9yICggaSA9IDAsIGlsID0gdGhpcy5tb3JwaFRhcmdldHMubGVuZ3RoOyBpIDwgaWw7IGkgKysgKSB7XG5cblx0XHRcdFx0Ly8gY3JlYXRlIG9uIGZpcnN0IGFjY2Vzc1xuXG5cdFx0XHRcdGlmICggISB0aGlzLm1vcnBoTm9ybWFsc1sgaSBdICkge1xuXG5cdFx0XHRcdFx0dGhpcy5tb3JwaE5vcm1hbHNbIGkgXSA9IHt9O1xuXHRcdFx0XHRcdHRoaXMubW9ycGhOb3JtYWxzWyBpIF0uZmFjZU5vcm1hbHMgPSBbXTtcblx0XHRcdFx0XHR0aGlzLm1vcnBoTm9ybWFsc1sgaSBdLnZlcnRleE5vcm1hbHMgPSBbXTtcblxuXHRcdFx0XHRcdHZhciBkc3ROb3JtYWxzRmFjZSA9IHRoaXMubW9ycGhOb3JtYWxzWyBpIF0uZmFjZU5vcm1hbHM7XG5cdFx0XHRcdFx0dmFyIGRzdE5vcm1hbHNWZXJ0ZXggPSB0aGlzLm1vcnBoTm9ybWFsc1sgaSBdLnZlcnRleE5vcm1hbHM7XG5cblx0XHRcdFx0XHR2YXIgZmFjZU5vcm1hbCwgdmVydGV4Tm9ybWFscztcblxuXHRcdFx0XHRcdGZvciAoIGYgPSAwLCBmbCA9IHRoaXMuZmFjZXMubGVuZ3RoOyBmIDwgZmw7IGYgKysgKSB7XG5cblx0XHRcdFx0XHRcdGZhY2VOb3JtYWwgPSBuZXcgVmVjdG9yMygpO1xuXHRcdFx0XHRcdFx0dmVydGV4Tm9ybWFscyA9IHsgYTogbmV3IFZlY3RvcjMoKSwgYjogbmV3IFZlY3RvcjMoKSwgYzogbmV3IFZlY3RvcjMoKSB9O1xuXG5cdFx0XHRcdFx0XHRkc3ROb3JtYWxzRmFjZS5wdXNoKCBmYWNlTm9ybWFsICk7XG5cdFx0XHRcdFx0XHRkc3ROb3JtYWxzVmVydGV4LnB1c2goIHZlcnRleE5vcm1hbHMgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0dmFyIG1vcnBoTm9ybWFscyA9IHRoaXMubW9ycGhOb3JtYWxzWyBpIF07XG5cblx0XHRcdFx0Ly8gc2V0IHZlcnRpY2VzIHRvIG1vcnBoIHRhcmdldFxuXG5cdFx0XHRcdHRtcEdlby52ZXJ0aWNlcyA9IHRoaXMubW9ycGhUYXJnZXRzWyBpIF0udmVydGljZXM7XG5cblx0XHRcdFx0Ly8gY29tcHV0ZSBtb3JwaCBub3JtYWxzXG5cblx0XHRcdFx0dG1wR2VvLmNvbXB1dGVGYWNlTm9ybWFscygpO1xuXHRcdFx0XHR0bXBHZW8uY29tcHV0ZVZlcnRleE5vcm1hbHMoKTtcblxuXHRcdFx0XHQvLyBzdG9yZSBtb3JwaCBub3JtYWxzXG5cblx0XHRcdFx0dmFyIGZhY2VOb3JtYWwsIHZlcnRleE5vcm1hbHM7XG5cblx0XHRcdFx0Zm9yICggZiA9IDAsIGZsID0gdGhpcy5mYWNlcy5sZW5ndGg7IGYgPCBmbDsgZiArKyApIHtcblxuXHRcdFx0XHRcdGZhY2UgPSB0aGlzLmZhY2VzWyBmIF07XG5cblx0XHRcdFx0XHRmYWNlTm9ybWFsID0gbW9ycGhOb3JtYWxzLmZhY2VOb3JtYWxzWyBmIF07XG5cdFx0XHRcdFx0dmVydGV4Tm9ybWFscyA9IG1vcnBoTm9ybWFscy52ZXJ0ZXhOb3JtYWxzWyBmIF07XG5cblx0XHRcdFx0XHRmYWNlTm9ybWFsLmNvcHkoIGZhY2Uubm9ybWFsICk7XG5cblx0XHRcdFx0XHR2ZXJ0ZXhOb3JtYWxzLmEuY29weSggZmFjZS52ZXJ0ZXhOb3JtYWxzWyAwIF0gKTtcblx0XHRcdFx0XHR2ZXJ0ZXhOb3JtYWxzLmIuY29weSggZmFjZS52ZXJ0ZXhOb3JtYWxzWyAxIF0gKTtcblx0XHRcdFx0XHR2ZXJ0ZXhOb3JtYWxzLmMuY29weSggZmFjZS52ZXJ0ZXhOb3JtYWxzWyAyIF0gKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gcmVzdG9yZSBvcmlnaW5hbCBub3JtYWxzXG5cblx0XHRcdGZvciAoIGYgPSAwLCBmbCA9IHRoaXMuZmFjZXMubGVuZ3RoOyBmIDwgZmw7IGYgKysgKSB7XG5cblx0XHRcdFx0ZmFjZSA9IHRoaXMuZmFjZXNbIGYgXTtcblxuXHRcdFx0XHRmYWNlLm5vcm1hbCA9IGZhY2UuX19vcmlnaW5hbEZhY2VOb3JtYWw7XG5cdFx0XHRcdGZhY2UudmVydGV4Tm9ybWFscyA9IGZhY2UuX19vcmlnaW5hbFZlcnRleE5vcm1hbHM7XG5cblx0XHRcdH1cblxuXHRcdH0sXG5cblx0XHRjb21wdXRlTGluZURpc3RhbmNlczogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgZCA9IDA7XG5cdFx0XHR2YXIgdmVydGljZXMgPSB0aGlzLnZlcnRpY2VzO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGlsID0gdmVydGljZXMubGVuZ3RoOyBpIDwgaWw7IGkgKysgKSB7XG5cblx0XHRcdFx0aWYgKCBpID4gMCApIHtcblxuXHRcdFx0XHRcdGQgKz0gdmVydGljZXNbIGkgXS5kaXN0YW5jZVRvKCB2ZXJ0aWNlc1sgaSAtIDEgXSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR0aGlzLmxpbmVEaXN0YW5jZXNbIGkgXSA9IGQ7XG5cblx0XHRcdH1cblxuXHRcdH0sXG5cblx0XHRjb21wdXRlQm91bmRpbmdCb3g6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0aWYgKCB0aGlzLmJvdW5kaW5nQm94ID09PSBudWxsICkge1xuXG5cdFx0XHRcdHRoaXMuYm91bmRpbmdCb3ggPSBuZXcgQm94MygpO1xuXG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuYm91bmRpbmdCb3guc2V0RnJvbVBvaW50cyggdGhpcy52ZXJ0aWNlcyApO1xuXG5cdFx0fSxcblxuXHRcdGNvbXB1dGVCb3VuZGluZ1NwaGVyZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRpZiAoIHRoaXMuYm91bmRpbmdTcGhlcmUgPT09IG51bGwgKSB7XG5cblx0XHRcdFx0dGhpcy5ib3VuZGluZ1NwaGVyZSA9IG5ldyBTcGhlcmUoKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLmJvdW5kaW5nU3BoZXJlLnNldEZyb21Qb2ludHMoIHRoaXMudmVydGljZXMgKTtcblxuXHRcdH0sXG5cblx0XHRtZXJnZTogZnVuY3Rpb24gKCBnZW9tZXRyeSwgbWF0cml4LCBtYXRlcmlhbEluZGV4T2Zmc2V0ICkge1xuXG5cdFx0XHRpZiAoIChnZW9tZXRyeSAmJiBnZW9tZXRyeS5pc0dlb21ldHJ5KSA9PT0gZmFsc2UgKSB7XG5cblx0XHRcdFx0Y29uc29sZS5lcnJvciggJ1RIUkVFLkdlb21ldHJ5Lm1lcmdlKCk6IGdlb21ldHJ5IG5vdCBhbiBpbnN0YW5jZSBvZiBUSFJFRS5HZW9tZXRyeS4nLCBnZW9tZXRyeSApO1xuXHRcdFx0XHRyZXR1cm47XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIG5vcm1hbE1hdHJpeCxcblx0XHRcdHZlcnRleE9mZnNldCA9IHRoaXMudmVydGljZXMubGVuZ3RoLFxuXHRcdFx0dmVydGljZXMxID0gdGhpcy52ZXJ0aWNlcyxcblx0XHRcdHZlcnRpY2VzMiA9IGdlb21ldHJ5LnZlcnRpY2VzLFxuXHRcdFx0ZmFjZXMxID0gdGhpcy5mYWNlcyxcblx0XHRcdGZhY2VzMiA9IGdlb21ldHJ5LmZhY2VzLFxuXHRcdFx0dXZzMSA9IHRoaXMuZmFjZVZlcnRleFV2c1sgMCBdLFxuXHRcdFx0dXZzMiA9IGdlb21ldHJ5LmZhY2VWZXJ0ZXhVdnNbIDAgXSxcblx0XHRcdGNvbG9yczEgPSB0aGlzLmNvbG9ycyxcblx0XHRcdGNvbG9yczIgPSBnZW9tZXRyeS5jb2xvcnM7XG5cblx0XHRcdGlmICggbWF0ZXJpYWxJbmRleE9mZnNldCA9PT0gdW5kZWZpbmVkICkgbWF0ZXJpYWxJbmRleE9mZnNldCA9IDA7XG5cblx0XHRcdGlmICggbWF0cml4ICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0bm9ybWFsTWF0cml4ID0gbmV3IE1hdHJpeDMoKS5nZXROb3JtYWxNYXRyaXgoIG1hdHJpeCApO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIHZlcnRpY2VzXG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgaWwgPSB2ZXJ0aWNlczIubGVuZ3RoOyBpIDwgaWw7IGkgKysgKSB7XG5cblx0XHRcdFx0dmFyIHZlcnRleCA9IHZlcnRpY2VzMlsgaSBdO1xuXG5cdFx0XHRcdHZhciB2ZXJ0ZXhDb3B5ID0gdmVydGV4LmNsb25lKCk7XG5cblx0XHRcdFx0aWYgKCBtYXRyaXggIT09IHVuZGVmaW5lZCApIHZlcnRleENvcHkuYXBwbHlNYXRyaXg0KCBtYXRyaXggKTtcblxuXHRcdFx0XHR2ZXJ0aWNlczEucHVzaCggdmVydGV4Q29weSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIGNvbG9yc1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGlsID0gY29sb3JzMi5sZW5ndGg7IGkgPCBpbDsgaSArKyApIHtcblxuXHRcdFx0XHRjb2xvcnMxLnB1c2goIGNvbG9yczJbIGkgXS5jbG9uZSgpICk7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gZmFjZXNcblxuXHRcdFx0Zm9yICggaSA9IDAsIGlsID0gZmFjZXMyLmxlbmd0aDsgaSA8IGlsOyBpICsrICkge1xuXG5cdFx0XHRcdHZhciBmYWNlID0gZmFjZXMyWyBpIF0sIGZhY2VDb3B5LCBub3JtYWwsIGNvbG9yLFxuXHRcdFx0XHRmYWNlVmVydGV4Tm9ybWFscyA9IGZhY2UudmVydGV4Tm9ybWFscyxcblx0XHRcdFx0ZmFjZVZlcnRleENvbG9ycyA9IGZhY2UudmVydGV4Q29sb3JzO1xuXG5cdFx0XHRcdGZhY2VDb3B5ID0gbmV3IEZhY2UzKCBmYWNlLmEgKyB2ZXJ0ZXhPZmZzZXQsIGZhY2UuYiArIHZlcnRleE9mZnNldCwgZmFjZS5jICsgdmVydGV4T2Zmc2V0ICk7XG5cdFx0XHRcdGZhY2VDb3B5Lm5vcm1hbC5jb3B5KCBmYWNlLm5vcm1hbCApO1xuXG5cdFx0XHRcdGlmICggbm9ybWFsTWF0cml4ICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRmYWNlQ29weS5ub3JtYWwuYXBwbHlNYXRyaXgzKCBub3JtYWxNYXRyaXggKS5ub3JtYWxpemUoKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Zm9yICggdmFyIGogPSAwLCBqbCA9IGZhY2VWZXJ0ZXhOb3JtYWxzLmxlbmd0aDsgaiA8IGpsOyBqICsrICkge1xuXG5cdFx0XHRcdFx0bm9ybWFsID0gZmFjZVZlcnRleE5vcm1hbHNbIGogXS5jbG9uZSgpO1xuXG5cdFx0XHRcdFx0aWYgKCBub3JtYWxNYXRyaXggIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdFx0bm9ybWFsLmFwcGx5TWF0cml4Myggbm9ybWFsTWF0cml4ICkubm9ybWFsaXplKCk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRmYWNlQ29weS52ZXJ0ZXhOb3JtYWxzLnB1c2goIG5vcm1hbCApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRmYWNlQ29weS5jb2xvci5jb3B5KCBmYWNlLmNvbG9yICk7XG5cblx0XHRcdFx0Zm9yICggdmFyIGogPSAwLCBqbCA9IGZhY2VWZXJ0ZXhDb2xvcnMubGVuZ3RoOyBqIDwgamw7IGogKysgKSB7XG5cblx0XHRcdFx0XHRjb2xvciA9IGZhY2VWZXJ0ZXhDb2xvcnNbIGogXTtcblx0XHRcdFx0XHRmYWNlQ29weS52ZXJ0ZXhDb2xvcnMucHVzaCggY29sb3IuY2xvbmUoKSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRmYWNlQ29weS5tYXRlcmlhbEluZGV4ID0gZmFjZS5tYXRlcmlhbEluZGV4ICsgbWF0ZXJpYWxJbmRleE9mZnNldDtcblxuXHRcdFx0XHRmYWNlczEucHVzaCggZmFjZUNvcHkgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyB1dnNcblxuXHRcdFx0Zm9yICggaSA9IDAsIGlsID0gdXZzMi5sZW5ndGg7IGkgPCBpbDsgaSArKyApIHtcblxuXHRcdFx0XHR2YXIgdXYgPSB1dnMyWyBpIF0sIHV2Q29weSA9IFtdO1xuXG5cdFx0XHRcdGlmICggdXYgPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdGNvbnRpbnVlO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRmb3IgKCB2YXIgaiA9IDAsIGpsID0gdXYubGVuZ3RoOyBqIDwgamw7IGogKysgKSB7XG5cblx0XHRcdFx0XHR1dkNvcHkucHVzaCggdXZbIGogXS5jbG9uZSgpICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHV2czEucHVzaCggdXZDb3B5ICk7XG5cblx0XHRcdH1cblxuXHRcdH0sXG5cblx0XHRtZXJnZU1lc2g6IGZ1bmN0aW9uICggbWVzaCApIHtcblxuXHRcdFx0aWYgKCAobWVzaCAmJiBtZXNoLmlzTWVzaCkgPT09IGZhbHNlICkge1xuXG5cdFx0XHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5HZW9tZXRyeS5tZXJnZU1lc2goKTogbWVzaCBub3QgYW4gaW5zdGFuY2Ugb2YgVEhSRUUuTWVzaC4nLCBtZXNoICk7XG5cdFx0XHRcdHJldHVybjtcblxuXHRcdFx0fVxuXG5cdFx0XHRtZXNoLm1hdHJpeEF1dG9VcGRhdGUgJiYgbWVzaC51cGRhdGVNYXRyaXgoKTtcblxuXHRcdFx0dGhpcy5tZXJnZSggbWVzaC5nZW9tZXRyeSwgbWVzaC5tYXRyaXggKTtcblxuXHRcdH0sXG5cblx0XHQvKlxuXHRcdCAqIENoZWNrcyBmb3IgZHVwbGljYXRlIHZlcnRpY2VzIHdpdGggaGFzaG1hcC5cblx0XHQgKiBEdXBsaWNhdGVkIHZlcnRpY2VzIGFyZSByZW1vdmVkXG5cdFx0ICogYW5kIGZhY2VzJyB2ZXJ0aWNlcyBhcmUgdXBkYXRlZC5cblx0XHQgKi9cblxuXHRcdG1lcmdlVmVydGljZXM6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIHZlcnRpY2VzTWFwID0ge307IC8vIEhhc2htYXAgZm9yIGxvb2tpbmcgdXAgdmVydGljZXMgYnkgcG9zaXRpb24gY29vcmRpbmF0ZXMgKGFuZCBtYWtpbmcgc3VyZSB0aGV5IGFyZSB1bmlxdWUpXG5cdFx0XHR2YXIgdW5pcXVlID0gW10sIGNoYW5nZXMgPSBbXTtcblxuXHRcdFx0dmFyIHYsIGtleTtcblx0XHRcdHZhciBwcmVjaXNpb25Qb2ludHMgPSA0OyAvLyBudW1iZXIgb2YgZGVjaW1hbCBwb2ludHMsIGUuZy4gNCBmb3IgZXBzaWxvbiBvZiAwLjAwMDFcblx0XHRcdHZhciBwcmVjaXNpb24gPSBNYXRoLnBvdyggMTAsIHByZWNpc2lvblBvaW50cyApO1xuXHRcdFx0dmFyIGksIGlsLCBmYWNlO1xuXHRcdFx0dmFyIGluZGljZXMsIGosIGpsO1xuXG5cdFx0XHRmb3IgKCBpID0gMCwgaWwgPSB0aGlzLnZlcnRpY2VzLmxlbmd0aDsgaSA8IGlsOyBpICsrICkge1xuXG5cdFx0XHRcdHYgPSB0aGlzLnZlcnRpY2VzWyBpIF07XG5cdFx0XHRcdGtleSA9IE1hdGgucm91bmQoIHYueCAqIHByZWNpc2lvbiApICsgJ18nICsgTWF0aC5yb3VuZCggdi55ICogcHJlY2lzaW9uICkgKyAnXycgKyBNYXRoLnJvdW5kKCB2LnogKiBwcmVjaXNpb24gKTtcblxuXHRcdFx0XHRpZiAoIHZlcnRpY2VzTWFwWyBrZXkgXSA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0dmVydGljZXNNYXBbIGtleSBdID0gaTtcblx0XHRcdFx0XHR1bmlxdWUucHVzaCggdGhpcy52ZXJ0aWNlc1sgaSBdICk7XG5cdFx0XHRcdFx0Y2hhbmdlc1sgaSBdID0gdW5pcXVlLmxlbmd0aCAtIDE7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdC8vY29uc29sZS5sb2coJ0R1cGxpY2F0ZSB2ZXJ0ZXggZm91bmQuICcsIGksICcgY291bGQgYmUgdXNpbmcgJywgdmVydGljZXNNYXBba2V5XSk7XG5cdFx0XHRcdFx0Y2hhbmdlc1sgaSBdID0gY2hhbmdlc1sgdmVydGljZXNNYXBbIGtleSBdIF07XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblxuXHRcdFx0Ly8gaWYgZmFjZXMgYXJlIGNvbXBsZXRlbHkgZGVnZW5lcmF0ZSBhZnRlciBtZXJnaW5nIHZlcnRpY2VzLCB3ZVxuXHRcdFx0Ly8gaGF2ZSB0byByZW1vdmUgdGhlbSBmcm9tIHRoZSBnZW9tZXRyeS5cblx0XHRcdHZhciBmYWNlSW5kaWNlc1RvUmVtb3ZlID0gW107XG5cblx0XHRcdGZvciAoIGkgPSAwLCBpbCA9IHRoaXMuZmFjZXMubGVuZ3RoOyBpIDwgaWw7IGkgKysgKSB7XG5cblx0XHRcdFx0ZmFjZSA9IHRoaXMuZmFjZXNbIGkgXTtcblxuXHRcdFx0XHRmYWNlLmEgPSBjaGFuZ2VzWyBmYWNlLmEgXTtcblx0XHRcdFx0ZmFjZS5iID0gY2hhbmdlc1sgZmFjZS5iIF07XG5cdFx0XHRcdGZhY2UuYyA9IGNoYW5nZXNbIGZhY2UuYyBdO1xuXG5cdFx0XHRcdGluZGljZXMgPSBbIGZhY2UuYSwgZmFjZS5iLCBmYWNlLmMgXTtcblxuXHRcdFx0XHR2YXIgZHVwSW5kZXggPSAtIDE7XG5cblx0XHRcdFx0Ly8gaWYgYW55IGR1cGxpY2F0ZSB2ZXJ0aWNlcyBhcmUgZm91bmQgaW4gYSBGYWNlM1xuXHRcdFx0XHQvLyB3ZSBoYXZlIHRvIHJlbW92ZSB0aGUgZmFjZSBhcyBub3RoaW5nIGNhbiBiZSBzYXZlZFxuXHRcdFx0XHRmb3IgKCB2YXIgbiA9IDA7IG4gPCAzOyBuICsrICkge1xuXG5cdFx0XHRcdFx0aWYgKCBpbmRpY2VzWyBuIF0gPT09IGluZGljZXNbICggbiArIDEgKSAlIDMgXSApIHtcblxuXHRcdFx0XHRcdFx0ZHVwSW5kZXggPSBuO1xuXHRcdFx0XHRcdFx0ZmFjZUluZGljZXNUb1JlbW92ZS5wdXNoKCBpICk7XG5cdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0Zm9yICggaSA9IGZhY2VJbmRpY2VzVG9SZW1vdmUubGVuZ3RoIC0gMTsgaSA+PSAwOyBpIC0tICkge1xuXG5cdFx0XHRcdHZhciBpZHggPSBmYWNlSW5kaWNlc1RvUmVtb3ZlWyBpIF07XG5cblx0XHRcdFx0dGhpcy5mYWNlcy5zcGxpY2UoIGlkeCwgMSApO1xuXG5cdFx0XHRcdGZvciAoIGogPSAwLCBqbCA9IHRoaXMuZmFjZVZlcnRleFV2cy5sZW5ndGg7IGogPCBqbDsgaiArKyApIHtcblxuXHRcdFx0XHRcdHRoaXMuZmFjZVZlcnRleFV2c1sgaiBdLnNwbGljZSggaWR4LCAxICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdC8vIFVzZSB1bmlxdWUgc2V0IG9mIHZlcnRpY2VzXG5cblx0XHRcdHZhciBkaWZmID0gdGhpcy52ZXJ0aWNlcy5sZW5ndGggLSB1bmlxdWUubGVuZ3RoO1xuXHRcdFx0dGhpcy52ZXJ0aWNlcyA9IHVuaXF1ZTtcblx0XHRcdHJldHVybiBkaWZmO1xuXG5cdFx0fSxcblxuXHRcdHNvcnRGYWNlc0J5TWF0ZXJpYWxJbmRleDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgZmFjZXMgPSB0aGlzLmZhY2VzO1xuXHRcdFx0dmFyIGxlbmd0aCA9IGZhY2VzLmxlbmd0aDtcblxuXHRcdFx0Ly8gdGFnIGZhY2VzXG5cblx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IGxlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHRmYWNlc1sgaSBdLl9pZCA9IGk7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gc29ydCBmYWNlc1xuXG5cdFx0XHRmdW5jdGlvbiBtYXRlcmlhbEluZGV4U29ydCggYSwgYiApIHtcblxuXHRcdFx0XHRyZXR1cm4gYS5tYXRlcmlhbEluZGV4IC0gYi5tYXRlcmlhbEluZGV4O1xuXG5cdFx0XHR9XG5cblx0XHRcdGZhY2VzLnNvcnQoIG1hdGVyaWFsSW5kZXhTb3J0ICk7XG5cblx0XHRcdC8vIHNvcnQgdXZzXG5cblx0XHRcdHZhciB1dnMxID0gdGhpcy5mYWNlVmVydGV4VXZzWyAwIF07XG5cdFx0XHR2YXIgdXZzMiA9IHRoaXMuZmFjZVZlcnRleFV2c1sgMSBdO1xuXG5cdFx0XHR2YXIgbmV3VXZzMSwgbmV3VXZzMjtcblxuXHRcdFx0aWYgKCB1dnMxICYmIHV2czEubGVuZ3RoID09PSBsZW5ndGggKSBuZXdVdnMxID0gW107XG5cdFx0XHRpZiAoIHV2czIgJiYgdXZzMi5sZW5ndGggPT09IGxlbmd0aCApIG5ld1V2czIgPSBbXTtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgbGVuZ3RoOyBpICsrICkge1xuXG5cdFx0XHRcdHZhciBpZCA9IGZhY2VzWyBpIF0uX2lkO1xuXG5cdFx0XHRcdGlmICggbmV3VXZzMSApIG5ld1V2czEucHVzaCggdXZzMVsgaWQgXSApO1xuXHRcdFx0XHRpZiAoIG5ld1V2czIgKSBuZXdVdnMyLnB1c2goIHV2czJbIGlkIF0gKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIG5ld1V2czEgKSB0aGlzLmZhY2VWZXJ0ZXhVdnNbIDAgXSA9IG5ld1V2czE7XG5cdFx0XHRpZiAoIG5ld1V2czIgKSB0aGlzLmZhY2VWZXJ0ZXhVdnNbIDEgXSA9IG5ld1V2czI7XG5cblx0XHR9LFxuXG5cdFx0dG9KU09OOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBkYXRhID0ge1xuXHRcdFx0XHRtZXRhZGF0YToge1xuXHRcdFx0XHRcdHZlcnNpb246IDQuNCxcblx0XHRcdFx0XHR0eXBlOiAnR2VvbWV0cnknLFxuXHRcdFx0XHRcdGdlbmVyYXRvcjogJ0dlb21ldHJ5LnRvSlNPTidcblx0XHRcdFx0fVxuXHRcdFx0fTtcblxuXHRcdFx0Ly8gc3RhbmRhcmQgR2VvbWV0cnkgc2VyaWFsaXphdGlvblxuXG5cdFx0XHRkYXRhLnV1aWQgPSB0aGlzLnV1aWQ7XG5cdFx0XHRkYXRhLnR5cGUgPSB0aGlzLnR5cGU7XG5cdFx0XHRpZiAoIHRoaXMubmFtZSAhPT0gJycgKSBkYXRhLm5hbWUgPSB0aGlzLm5hbWU7XG5cblx0XHRcdGlmICggdGhpcy5wYXJhbWV0ZXJzICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0dmFyIHBhcmFtZXRlcnMgPSB0aGlzLnBhcmFtZXRlcnM7XG5cblx0XHRcdFx0Zm9yICggdmFyIGtleSBpbiBwYXJhbWV0ZXJzICkge1xuXG5cdFx0XHRcdFx0aWYgKCBwYXJhbWV0ZXJzWyBrZXkgXSAhPT0gdW5kZWZpbmVkICkgZGF0YVsga2V5IF0gPSBwYXJhbWV0ZXJzWyBrZXkgXTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuIGRhdGE7XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIHZlcnRpY2VzID0gW107XG5cblx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IHRoaXMudmVydGljZXMubGVuZ3RoOyBpICsrICkge1xuXG5cdFx0XHRcdHZhciB2ZXJ0ZXggPSB0aGlzLnZlcnRpY2VzWyBpIF07XG5cdFx0XHRcdHZlcnRpY2VzLnB1c2goIHZlcnRleC54LCB2ZXJ0ZXgueSwgdmVydGV4LnogKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgZmFjZXMgPSBbXTtcblx0XHRcdHZhciBub3JtYWxzID0gW107XG5cdFx0XHR2YXIgbm9ybWFsc0hhc2ggPSB7fTtcblx0XHRcdHZhciBjb2xvcnMgPSBbXTtcblx0XHRcdHZhciBjb2xvcnNIYXNoID0ge307XG5cdFx0XHR2YXIgdXZzID0gW107XG5cdFx0XHR2YXIgdXZzSGFzaCA9IHt9O1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCB0aGlzLmZhY2VzLmxlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHR2YXIgZmFjZSA9IHRoaXMuZmFjZXNbIGkgXTtcblxuXHRcdFx0XHR2YXIgaGFzTWF0ZXJpYWwgPSB0cnVlO1xuXHRcdFx0XHR2YXIgaGFzRmFjZVV2ID0gZmFsc2U7IC8vIGRlcHJlY2F0ZWRcblx0XHRcdFx0dmFyIGhhc0ZhY2VWZXJ0ZXhVdiA9IHRoaXMuZmFjZVZlcnRleFV2c1sgMCBdWyBpIF0gIT09IHVuZGVmaW5lZDtcblx0XHRcdFx0dmFyIGhhc0ZhY2VOb3JtYWwgPSBmYWNlLm5vcm1hbC5sZW5ndGgoKSA+IDA7XG5cdFx0XHRcdHZhciBoYXNGYWNlVmVydGV4Tm9ybWFsID0gZmFjZS52ZXJ0ZXhOb3JtYWxzLmxlbmd0aCA+IDA7XG5cdFx0XHRcdHZhciBoYXNGYWNlQ29sb3IgPSBmYWNlLmNvbG9yLnIgIT09IDEgfHwgZmFjZS5jb2xvci5nICE9PSAxIHx8IGZhY2UuY29sb3IuYiAhPT0gMTtcblx0XHRcdFx0dmFyIGhhc0ZhY2VWZXJ0ZXhDb2xvciA9IGZhY2UudmVydGV4Q29sb3JzLmxlbmd0aCA+IDA7XG5cblx0XHRcdFx0dmFyIGZhY2VUeXBlID0gMDtcblxuXHRcdFx0XHRmYWNlVHlwZSA9IHNldEJpdCggZmFjZVR5cGUsIDAsIDAgKTsgLy8gaXNRdWFkXG5cdFx0XHRcdGZhY2VUeXBlID0gc2V0Qml0KCBmYWNlVHlwZSwgMSwgaGFzTWF0ZXJpYWwgKTtcblx0XHRcdFx0ZmFjZVR5cGUgPSBzZXRCaXQoIGZhY2VUeXBlLCAyLCBoYXNGYWNlVXYgKTtcblx0XHRcdFx0ZmFjZVR5cGUgPSBzZXRCaXQoIGZhY2VUeXBlLCAzLCBoYXNGYWNlVmVydGV4VXYgKTtcblx0XHRcdFx0ZmFjZVR5cGUgPSBzZXRCaXQoIGZhY2VUeXBlLCA0LCBoYXNGYWNlTm9ybWFsICk7XG5cdFx0XHRcdGZhY2VUeXBlID0gc2V0Qml0KCBmYWNlVHlwZSwgNSwgaGFzRmFjZVZlcnRleE5vcm1hbCApO1xuXHRcdFx0XHRmYWNlVHlwZSA9IHNldEJpdCggZmFjZVR5cGUsIDYsIGhhc0ZhY2VDb2xvciApO1xuXHRcdFx0XHRmYWNlVHlwZSA9IHNldEJpdCggZmFjZVR5cGUsIDcsIGhhc0ZhY2VWZXJ0ZXhDb2xvciApO1xuXG5cdFx0XHRcdGZhY2VzLnB1c2goIGZhY2VUeXBlICk7XG5cdFx0XHRcdGZhY2VzLnB1c2goIGZhY2UuYSwgZmFjZS5iLCBmYWNlLmMgKTtcblx0XHRcdFx0ZmFjZXMucHVzaCggZmFjZS5tYXRlcmlhbEluZGV4ICk7XG5cblx0XHRcdFx0aWYgKCBoYXNGYWNlVmVydGV4VXYgKSB7XG5cblx0XHRcdFx0XHR2YXIgZmFjZVZlcnRleFV2cyA9IHRoaXMuZmFjZVZlcnRleFV2c1sgMCBdWyBpIF07XG5cblx0XHRcdFx0XHRmYWNlcy5wdXNoKFxuXHRcdFx0XHRcdFx0Z2V0VXZJbmRleCggZmFjZVZlcnRleFV2c1sgMCBdICksXG5cdFx0XHRcdFx0XHRnZXRVdkluZGV4KCBmYWNlVmVydGV4VXZzWyAxIF0gKSxcblx0XHRcdFx0XHRcdGdldFV2SW5kZXgoIGZhY2VWZXJ0ZXhVdnNbIDIgXSApXG5cdFx0XHRcdFx0KTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCBoYXNGYWNlTm9ybWFsICkge1xuXG5cdFx0XHRcdFx0ZmFjZXMucHVzaCggZ2V0Tm9ybWFsSW5kZXgoIGZhY2Uubm9ybWFsICkgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCBoYXNGYWNlVmVydGV4Tm9ybWFsICkge1xuXG5cdFx0XHRcdFx0dmFyIHZlcnRleE5vcm1hbHMgPSBmYWNlLnZlcnRleE5vcm1hbHM7XG5cblx0XHRcdFx0XHRmYWNlcy5wdXNoKFxuXHRcdFx0XHRcdFx0Z2V0Tm9ybWFsSW5kZXgoIHZlcnRleE5vcm1hbHNbIDAgXSApLFxuXHRcdFx0XHRcdFx0Z2V0Tm9ybWFsSW5kZXgoIHZlcnRleE5vcm1hbHNbIDEgXSApLFxuXHRcdFx0XHRcdFx0Z2V0Tm9ybWFsSW5kZXgoIHZlcnRleE5vcm1hbHNbIDIgXSApXG5cdFx0XHRcdFx0KTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCBoYXNGYWNlQ29sb3IgKSB7XG5cblx0XHRcdFx0XHRmYWNlcy5wdXNoKCBnZXRDb2xvckluZGV4KCBmYWNlLmNvbG9yICkgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCBoYXNGYWNlVmVydGV4Q29sb3IgKSB7XG5cblx0XHRcdFx0XHR2YXIgdmVydGV4Q29sb3JzID0gZmFjZS52ZXJ0ZXhDb2xvcnM7XG5cblx0XHRcdFx0XHRmYWNlcy5wdXNoKFxuXHRcdFx0XHRcdFx0Z2V0Q29sb3JJbmRleCggdmVydGV4Q29sb3JzWyAwIF0gKSxcblx0XHRcdFx0XHRcdGdldENvbG9ySW5kZXgoIHZlcnRleENvbG9yc1sgMSBdICksXG5cdFx0XHRcdFx0XHRnZXRDb2xvckluZGV4KCB2ZXJ0ZXhDb2xvcnNbIDIgXSApXG5cdFx0XHRcdFx0KTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0ZnVuY3Rpb24gc2V0Qml0KCB2YWx1ZSwgcG9zaXRpb24sIGVuYWJsZWQgKSB7XG5cblx0XHRcdFx0cmV0dXJuIGVuYWJsZWQgPyB2YWx1ZSB8ICggMSA8PCBwb3NpdGlvbiApIDogdmFsdWUgJiAoIH4gKCAxIDw8IHBvc2l0aW9uICkgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRmdW5jdGlvbiBnZXROb3JtYWxJbmRleCggbm9ybWFsICkge1xuXG5cdFx0XHRcdHZhciBoYXNoID0gbm9ybWFsLngudG9TdHJpbmcoKSArIG5vcm1hbC55LnRvU3RyaW5nKCkgKyBub3JtYWwuei50b1N0cmluZygpO1xuXG5cdFx0XHRcdGlmICggbm9ybWFsc0hhc2hbIGhhc2ggXSAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0cmV0dXJuIG5vcm1hbHNIYXNoWyBoYXNoIF07XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdG5vcm1hbHNIYXNoWyBoYXNoIF0gPSBub3JtYWxzLmxlbmd0aCAvIDM7XG5cdFx0XHRcdG5vcm1hbHMucHVzaCggbm9ybWFsLngsIG5vcm1hbC55LCBub3JtYWwueiApO1xuXG5cdFx0XHRcdHJldHVybiBub3JtYWxzSGFzaFsgaGFzaCBdO1xuXG5cdFx0XHR9XG5cblx0XHRcdGZ1bmN0aW9uIGdldENvbG9ySW5kZXgoIGNvbG9yICkge1xuXG5cdFx0XHRcdHZhciBoYXNoID0gY29sb3Iuci50b1N0cmluZygpICsgY29sb3IuZy50b1N0cmluZygpICsgY29sb3IuYi50b1N0cmluZygpO1xuXG5cdFx0XHRcdGlmICggY29sb3JzSGFzaFsgaGFzaCBdICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRyZXR1cm4gY29sb3JzSGFzaFsgaGFzaCBdO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRjb2xvcnNIYXNoWyBoYXNoIF0gPSBjb2xvcnMubGVuZ3RoO1xuXHRcdFx0XHRjb2xvcnMucHVzaCggY29sb3IuZ2V0SGV4KCkgKTtcblxuXHRcdFx0XHRyZXR1cm4gY29sb3JzSGFzaFsgaGFzaCBdO1xuXG5cdFx0XHR9XG5cblx0XHRcdGZ1bmN0aW9uIGdldFV2SW5kZXgoIHV2ICkge1xuXG5cdFx0XHRcdHZhciBoYXNoID0gdXYueC50b1N0cmluZygpICsgdXYueS50b1N0cmluZygpO1xuXG5cdFx0XHRcdGlmICggdXZzSGFzaFsgaGFzaCBdICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRyZXR1cm4gdXZzSGFzaFsgaGFzaCBdO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR1dnNIYXNoWyBoYXNoIF0gPSB1dnMubGVuZ3RoIC8gMjtcblx0XHRcdFx0dXZzLnB1c2goIHV2LngsIHV2LnkgKTtcblxuXHRcdFx0XHRyZXR1cm4gdXZzSGFzaFsgaGFzaCBdO1xuXG5cdFx0XHR9XG5cblx0XHRcdGRhdGEuZGF0YSA9IHt9O1xuXG5cdFx0XHRkYXRhLmRhdGEudmVydGljZXMgPSB2ZXJ0aWNlcztcblx0XHRcdGRhdGEuZGF0YS5ub3JtYWxzID0gbm9ybWFscztcblx0XHRcdGlmICggY29sb3JzLmxlbmd0aCA+IDAgKSBkYXRhLmRhdGEuY29sb3JzID0gY29sb3JzO1xuXHRcdFx0aWYgKCB1dnMubGVuZ3RoID4gMCApIGRhdGEuZGF0YS51dnMgPSBbIHV2cyBdOyAvLyB0ZW1wb3JhbCBiYWNrd2FyZCBjb21wYXRpYmlsaXR5XG5cdFx0XHRkYXRhLmRhdGEuZmFjZXMgPSBmYWNlcztcblxuXHRcdFx0cmV0dXJuIGRhdGE7XG5cblx0XHR9LFxuXG5cdFx0Y2xvbmU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Lypcblx0XHRcdC8vIEhhbmRsZSBwcmltaXRpdmVzXG5cblx0XHRcdHZhciBwYXJhbWV0ZXJzID0gdGhpcy5wYXJhbWV0ZXJzO1xuXG5cdFx0XHRpZiAoIHBhcmFtZXRlcnMgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHR2YXIgdmFsdWVzID0gW107XG5cblx0XHRcdFx0Zm9yICggdmFyIGtleSBpbiBwYXJhbWV0ZXJzICkge1xuXG5cdFx0XHRcdFx0dmFsdWVzLnB1c2goIHBhcmFtZXRlcnNbIGtleSBdICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBnZW9tZXRyeSA9IE9iamVjdC5jcmVhdGUoIHRoaXMuY29uc3RydWN0b3IucHJvdG90eXBlICk7XG5cdFx0XHRcdHRoaXMuY29uc3RydWN0b3IuYXBwbHkoIGdlb21ldHJ5LCB2YWx1ZXMgKTtcblx0XHRcdFx0cmV0dXJuIGdlb21ldHJ5O1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBuZXcgdGhpcy5jb25zdHJ1Y3RvcigpLmNvcHkoIHRoaXMgKTtcblx0XHRcdCovXG5cblx0XHRcdHJldHVybiBuZXcgR2VvbWV0cnkoKS5jb3B5KCB0aGlzICk7XG5cblx0XHR9LFxuXG5cdFx0Y29weTogZnVuY3Rpb24gKCBzb3VyY2UgKSB7XG5cblx0XHRcdHRoaXMudmVydGljZXMgPSBbXTtcblx0XHRcdHRoaXMuZmFjZXMgPSBbXTtcblx0XHRcdHRoaXMuZmFjZVZlcnRleFV2cyA9IFsgW10gXTtcblx0XHRcdHRoaXMuY29sb3JzID0gW107XG5cblx0XHRcdHZhciB2ZXJ0aWNlcyA9IHNvdXJjZS52ZXJ0aWNlcztcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBpbCA9IHZlcnRpY2VzLmxlbmd0aDsgaSA8IGlsOyBpICsrICkge1xuXG5cdFx0XHRcdHRoaXMudmVydGljZXMucHVzaCggdmVydGljZXNbIGkgXS5jbG9uZSgpICk7XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIGNvbG9ycyA9IHNvdXJjZS5jb2xvcnM7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgaWwgPSBjb2xvcnMubGVuZ3RoOyBpIDwgaWw7IGkgKysgKSB7XG5cblx0XHRcdFx0dGhpcy5jb2xvcnMucHVzaCggY29sb3JzWyBpIF0uY2xvbmUoKSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBmYWNlcyA9IHNvdXJjZS5mYWNlcztcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBpbCA9IGZhY2VzLmxlbmd0aDsgaSA8IGlsOyBpICsrICkge1xuXG5cdFx0XHRcdHRoaXMuZmFjZXMucHVzaCggZmFjZXNbIGkgXS5jbG9uZSgpICk7XG5cblx0XHRcdH1cblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBpbCA9IHNvdXJjZS5mYWNlVmVydGV4VXZzLmxlbmd0aDsgaSA8IGlsOyBpICsrICkge1xuXG5cdFx0XHRcdHZhciBmYWNlVmVydGV4VXZzID0gc291cmNlLmZhY2VWZXJ0ZXhVdnNbIGkgXTtcblxuXHRcdFx0XHRpZiAoIHRoaXMuZmFjZVZlcnRleFV2c1sgaSBdID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHR0aGlzLmZhY2VWZXJ0ZXhVdnNbIGkgXSA9IFtdO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRmb3IgKCB2YXIgaiA9IDAsIGpsID0gZmFjZVZlcnRleFV2cy5sZW5ndGg7IGogPCBqbDsgaiArKyApIHtcblxuXHRcdFx0XHRcdHZhciB1dnMgPSBmYWNlVmVydGV4VXZzWyBqIF0sIHV2c0NvcHkgPSBbXTtcblxuXHRcdFx0XHRcdGZvciAoIHZhciBrID0gMCwga2wgPSB1dnMubGVuZ3RoOyBrIDwga2w7IGsgKysgKSB7XG5cblx0XHRcdFx0XHRcdHZhciB1diA9IHV2c1sgayBdO1xuXG5cdFx0XHRcdFx0XHR1dnNDb3B5LnB1c2goIHV2LmNsb25lKCkgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdHRoaXMuZmFjZVZlcnRleFV2c1sgaSBdLnB1c2goIHV2c0NvcHkgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0ZGlzcG9zZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR0aGlzLmRpc3BhdGNoRXZlbnQoIHsgdHlwZTogJ2Rpc3Bvc2UnIH0gKTtcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0dmFyIGNvdW50ID0gMDtcblx0ZnVuY3Rpb24gR2VvbWV0cnlJZENvdW50KCkgeyByZXR1cm4gY291bnQrKzsgfVxuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBCdWZmZXJHZW9tZXRyeSgpIHtcblxuXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eSggdGhpcywgJ2lkJywgeyB2YWx1ZTogR2VvbWV0cnlJZENvdW50KCkgfSApO1xuXG5cdFx0dGhpcy51dWlkID0gX01hdGguZ2VuZXJhdGVVVUlEKCk7XG5cblx0XHR0aGlzLm5hbWUgPSAnJztcblx0XHR0aGlzLnR5cGUgPSAnQnVmZmVyR2VvbWV0cnknO1xuXG5cdFx0dGhpcy5pbmRleCA9IG51bGw7XG5cdFx0dGhpcy5hdHRyaWJ1dGVzID0ge307XG5cblx0XHR0aGlzLm1vcnBoQXR0cmlidXRlcyA9IHt9O1xuXG5cdFx0dGhpcy5ncm91cHMgPSBbXTtcblxuXHRcdHRoaXMuYm91bmRpbmdCb3ggPSBudWxsO1xuXHRcdHRoaXMuYm91bmRpbmdTcGhlcmUgPSBudWxsO1xuXG5cdFx0dGhpcy5kcmF3UmFuZ2UgPSB7IHN0YXJ0OiAwLCBjb3VudDogSW5maW5pdHkgfTtcblxuXHR9XG5cblx0T2JqZWN0LmFzc2lnbiggQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlLCBFdmVudERpc3BhdGNoZXIucHJvdG90eXBlLCB7XG5cblx0XHRpc0J1ZmZlckdlb21ldHJ5OiB0cnVlLFxuXG5cdFx0Z2V0SW5kZXg6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuaW5kZXg7XG5cblx0XHR9LFxuXG5cdFx0c2V0SW5kZXg6IGZ1bmN0aW9uICggaW5kZXggKSB7XG5cblx0XHRcdHRoaXMuaW5kZXggPSBpbmRleDtcblxuXHRcdH0sXG5cblx0XHRhZGRBdHRyaWJ1dGU6IGZ1bmN0aW9uICggbmFtZSwgYXR0cmlidXRlICkge1xuXG5cdFx0XHRpZiAoICggYXR0cmlidXRlICYmIGF0dHJpYnV0ZS5pc0J1ZmZlckF0dHJpYnV0ZSApID09PSBmYWxzZSAmJiAoIGF0dHJpYnV0ZSAmJiBhdHRyaWJ1dGUuaXNJbnRlcmxlYXZlZEJ1ZmZlckF0dHJpYnV0ZSApID09PSBmYWxzZSApIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5CdWZmZXJHZW9tZXRyeTogLmFkZEF0dHJpYnV0ZSgpIG5vdyBleHBlY3RzICggbmFtZSwgYXR0cmlidXRlICkuJyApO1xuXG5cdFx0XHRcdHRoaXMuYWRkQXR0cmlidXRlKCBuYW1lLCBuZXcgQnVmZmVyQXR0cmlidXRlKCBhcmd1bWVudHNbIDEgXSwgYXJndW1lbnRzWyAyIF0gKSApO1xuXG5cdFx0XHRcdHJldHVybjtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIG5hbWUgPT09ICdpbmRleCcgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuQnVmZmVyR2VvbWV0cnkuYWRkQXR0cmlidXRlOiBVc2UgLnNldEluZGV4KCkgZm9yIGluZGV4IGF0dHJpYnV0ZS4nICk7XG5cdFx0XHRcdHRoaXMuc2V0SW5kZXgoIGF0dHJpYnV0ZSApO1xuXG5cdFx0XHRcdHJldHVybjtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLmF0dHJpYnV0ZXNbIG5hbWUgXSA9IGF0dHJpYnV0ZTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Z2V0QXR0cmlidXRlOiBmdW5jdGlvbiAoIG5hbWUgKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLmF0dHJpYnV0ZXNbIG5hbWUgXTtcblxuXHRcdH0sXG5cblx0XHRyZW1vdmVBdHRyaWJ1dGU6IGZ1bmN0aW9uICggbmFtZSApIHtcblxuXHRcdFx0ZGVsZXRlIHRoaXMuYXR0cmlidXRlc1sgbmFtZSBdO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRhZGRHcm91cDogZnVuY3Rpb24gKCBzdGFydCwgY291bnQsIG1hdGVyaWFsSW5kZXggKSB7XG5cblx0XHRcdHRoaXMuZ3JvdXBzLnB1c2goIHtcblxuXHRcdFx0XHRzdGFydDogc3RhcnQsXG5cdFx0XHRcdGNvdW50OiBjb3VudCxcblx0XHRcdFx0bWF0ZXJpYWxJbmRleDogbWF0ZXJpYWxJbmRleCAhPT0gdW5kZWZpbmVkID8gbWF0ZXJpYWxJbmRleCA6IDBcblxuXHRcdFx0fSApO1xuXG5cdFx0fSxcblxuXHRcdGNsZWFyR3JvdXBzOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMuZ3JvdXBzID0gW107XG5cblx0XHR9LFxuXG5cdFx0c2V0RHJhd1JhbmdlOiBmdW5jdGlvbiAoIHN0YXJ0LCBjb3VudCApIHtcblxuXHRcdFx0dGhpcy5kcmF3UmFuZ2Uuc3RhcnQgPSBzdGFydDtcblx0XHRcdHRoaXMuZHJhd1JhbmdlLmNvdW50ID0gY291bnQ7XG5cblx0XHR9LFxuXG5cdFx0YXBwbHlNYXRyaXg6IGZ1bmN0aW9uICggbWF0cml4ICkge1xuXG5cdFx0XHR2YXIgcG9zaXRpb24gPSB0aGlzLmF0dHJpYnV0ZXMucG9zaXRpb247XG5cblx0XHRcdGlmICggcG9zaXRpb24gIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRtYXRyaXguYXBwbHlUb1ZlY3RvcjNBcnJheSggcG9zaXRpb24uYXJyYXkgKTtcblx0XHRcdFx0cG9zaXRpb24ubmVlZHNVcGRhdGUgPSB0cnVlO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBub3JtYWwgPSB0aGlzLmF0dHJpYnV0ZXMubm9ybWFsO1xuXG5cdFx0XHRpZiAoIG5vcm1hbCAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdHZhciBub3JtYWxNYXRyaXggPSBuZXcgTWF0cml4MygpLmdldE5vcm1hbE1hdHJpeCggbWF0cml4ICk7XG5cblx0XHRcdFx0bm9ybWFsTWF0cml4LmFwcGx5VG9WZWN0b3IzQXJyYXkoIG5vcm1hbC5hcnJheSApO1xuXHRcdFx0XHRub3JtYWwubmVlZHNVcGRhdGUgPSB0cnVlO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggdGhpcy5ib3VuZGluZ0JveCAhPT0gbnVsbCApIHtcblxuXHRcdFx0XHR0aGlzLmNvbXB1dGVCb3VuZGluZ0JveCgpO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggdGhpcy5ib3VuZGluZ1NwaGVyZSAhPT0gbnVsbCApIHtcblxuXHRcdFx0XHR0aGlzLmNvbXB1dGVCb3VuZGluZ1NwaGVyZSgpO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHJvdGF0ZVg6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Ly8gcm90YXRlIGdlb21ldHJ5IGFyb3VuZCB3b3JsZCB4LWF4aXNcblxuXHRcdFx0dmFyIG0xO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gcm90YXRlWCggYW5nbGUgKSB7XG5cblx0XHRcdFx0aWYgKCBtMSA9PT0gdW5kZWZpbmVkICkgbTEgPSBuZXcgTWF0cml4NCgpO1xuXG5cdFx0XHRcdG0xLm1ha2VSb3RhdGlvblgoIGFuZ2xlICk7XG5cblx0XHRcdFx0dGhpcy5hcHBseU1hdHJpeCggbTEgKTtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdHJvdGF0ZVk6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Ly8gcm90YXRlIGdlb21ldHJ5IGFyb3VuZCB3b3JsZCB5LWF4aXNcblxuXHRcdFx0dmFyIG0xO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gcm90YXRlWSggYW5nbGUgKSB7XG5cblx0XHRcdFx0aWYgKCBtMSA9PT0gdW5kZWZpbmVkICkgbTEgPSBuZXcgTWF0cml4NCgpO1xuXG5cdFx0XHRcdG0xLm1ha2VSb3RhdGlvblkoIGFuZ2xlICk7XG5cblx0XHRcdFx0dGhpcy5hcHBseU1hdHJpeCggbTEgKTtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdHJvdGF0ZVo6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Ly8gcm90YXRlIGdlb21ldHJ5IGFyb3VuZCB3b3JsZCB6LWF4aXNcblxuXHRcdFx0dmFyIG0xO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gcm90YXRlWiggYW5nbGUgKSB7XG5cblx0XHRcdFx0aWYgKCBtMSA9PT0gdW5kZWZpbmVkICkgbTEgPSBuZXcgTWF0cml4NCgpO1xuXG5cdFx0XHRcdG0xLm1ha2VSb3RhdGlvblooIGFuZ2xlICk7XG5cblx0XHRcdFx0dGhpcy5hcHBseU1hdHJpeCggbTEgKTtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblxuXHRcdHRyYW5zbGF0ZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHQvLyB0cmFuc2xhdGUgZ2VvbWV0cnlcblxuXHRcdFx0dmFyIG0xO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gdHJhbnNsYXRlKCB4LCB5LCB6ICkge1xuXG5cdFx0XHRcdGlmICggbTEgPT09IHVuZGVmaW5lZCApIG0xID0gbmV3IE1hdHJpeDQoKTtcblxuXHRcdFx0XHRtMS5tYWtlVHJhbnNsYXRpb24oIHgsIHksIHogKTtcblxuXHRcdFx0XHR0aGlzLmFwcGx5TWF0cml4KCBtMSApO1xuXG5cdFx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0c2NhbGU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Ly8gc2NhbGUgZ2VvbWV0cnlcblxuXHRcdFx0dmFyIG0xO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gc2NhbGUoIHgsIHksIHogKSB7XG5cblx0XHRcdFx0aWYgKCBtMSA9PT0gdW5kZWZpbmVkICkgbTEgPSBuZXcgTWF0cml4NCgpO1xuXG5cdFx0XHRcdG0xLm1ha2VTY2FsZSggeCwgeSwgeiApO1xuXG5cdFx0XHRcdHRoaXMuYXBwbHlNYXRyaXgoIG0xICk7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRsb29rQXQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIG9iajtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIGxvb2tBdCggdmVjdG9yICkge1xuXG5cdFx0XHRcdGlmICggb2JqID09PSB1bmRlZmluZWQgKSBvYmogPSBuZXcgT2JqZWN0M0QoKTtcblxuXHRcdFx0XHRvYmoubG9va0F0KCB2ZWN0b3IgKTtcblxuXHRcdFx0XHRvYmoudXBkYXRlTWF0cml4KCk7XG5cblx0XHRcdFx0dGhpcy5hcHBseU1hdHJpeCggb2JqLm1hdHJpeCApO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0Y2VudGVyOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMuY29tcHV0ZUJvdW5kaW5nQm94KCk7XG5cblx0XHRcdHZhciBvZmZzZXQgPSB0aGlzLmJvdW5kaW5nQm94LmdldENlbnRlcigpLm5lZ2F0ZSgpO1xuXG5cdFx0XHR0aGlzLnRyYW5zbGF0ZSggb2Zmc2V0LngsIG9mZnNldC55LCBvZmZzZXQueiApO1xuXG5cdFx0XHRyZXR1cm4gb2Zmc2V0O1xuXG5cdFx0fSxcblxuXHRcdHNldEZyb21PYmplY3Q6IGZ1bmN0aW9uICggb2JqZWN0ICkge1xuXG5cdFx0XHQvLyBjb25zb2xlLmxvZyggJ1RIUkVFLkJ1ZmZlckdlb21ldHJ5LnNldEZyb21PYmplY3QoKS4gQ29udmVydGluZycsIG9iamVjdCwgdGhpcyApO1xuXG5cdFx0XHR2YXIgZ2VvbWV0cnkgPSBvYmplY3QuZ2VvbWV0cnk7XG5cblx0XHRcdGlmICggb2JqZWN0LmlzUG9pbnRzIHx8IG9iamVjdC5pc0xpbmUgKSB7XG5cblx0XHRcdFx0dmFyIHBvc2l0aW9ucyA9IG5ldyBGbG9hdDMyQnVmZmVyQXR0cmlidXRlKCBnZW9tZXRyeS52ZXJ0aWNlcy5sZW5ndGggKiAzLCAzICk7XG5cdFx0XHRcdHZhciBjb2xvcnMgPSBuZXcgRmxvYXQzMkJ1ZmZlckF0dHJpYnV0ZSggZ2VvbWV0cnkuY29sb3JzLmxlbmd0aCAqIDMsIDMgKTtcblxuXHRcdFx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ3Bvc2l0aW9uJywgcG9zaXRpb25zLmNvcHlWZWN0b3Izc0FycmF5KCBnZW9tZXRyeS52ZXJ0aWNlcyApICk7XG5cdFx0XHRcdHRoaXMuYWRkQXR0cmlidXRlKCAnY29sb3InLCBjb2xvcnMuY29weUNvbG9yc0FycmF5KCBnZW9tZXRyeS5jb2xvcnMgKSApO1xuXG5cdFx0XHRcdGlmICggZ2VvbWV0cnkubGluZURpc3RhbmNlcyAmJiBnZW9tZXRyeS5saW5lRGlzdGFuY2VzLmxlbmd0aCA9PT0gZ2VvbWV0cnkudmVydGljZXMubGVuZ3RoICkge1xuXG5cdFx0XHRcdFx0dmFyIGxpbmVEaXN0YW5jZXMgPSBuZXcgRmxvYXQzMkJ1ZmZlckF0dHJpYnV0ZSggZ2VvbWV0cnkubGluZURpc3RhbmNlcy5sZW5ndGgsIDEgKTtcblxuXHRcdFx0XHRcdHRoaXMuYWRkQXR0cmlidXRlKCAnbGluZURpc3RhbmNlJywgbGluZURpc3RhbmNlcy5jb3B5QXJyYXkoIGdlb21ldHJ5LmxpbmVEaXN0YW5jZXMgKSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoIGdlb21ldHJ5LmJvdW5kaW5nU3BoZXJlICE9PSBudWxsICkge1xuXG5cdFx0XHRcdFx0dGhpcy5ib3VuZGluZ1NwaGVyZSA9IGdlb21ldHJ5LmJvdW5kaW5nU3BoZXJlLmNsb25lKCk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggZ2VvbWV0cnkuYm91bmRpbmdCb3ggIT09IG51bGwgKSB7XG5cblx0XHRcdFx0XHR0aGlzLmJvdW5kaW5nQm94ID0gZ2VvbWV0cnkuYm91bmRpbmdCb3guY2xvbmUoKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH0gZWxzZSBpZiAoIG9iamVjdC5pc01lc2ggKSB7XG5cblx0XHRcdFx0aWYgKCBnZW9tZXRyeSAmJiBnZW9tZXRyeS5pc0dlb21ldHJ5ICkge1xuXG5cdFx0XHRcdFx0dGhpcy5mcm9tR2VvbWV0cnkoIGdlb21ldHJ5ICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHVwZGF0ZUZyb21PYmplY3Q6IGZ1bmN0aW9uICggb2JqZWN0ICkge1xuXG5cdFx0XHR2YXIgZ2VvbWV0cnkgPSBvYmplY3QuZ2VvbWV0cnk7XG5cblx0XHRcdGlmICggb2JqZWN0LmlzTWVzaCApIHtcblxuXHRcdFx0XHR2YXIgZGlyZWN0ID0gZ2VvbWV0cnkuX19kaXJlY3RHZW9tZXRyeTtcblxuXHRcdFx0XHRpZiAoIGdlb21ldHJ5LmVsZW1lbnRzTmVlZFVwZGF0ZSA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRcdGRpcmVjdCA9IHVuZGVmaW5lZDtcblx0XHRcdFx0XHRnZW9tZXRyeS5lbGVtZW50c05lZWRVcGRhdGUgPSBmYWxzZTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCBkaXJlY3QgPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdHJldHVybiB0aGlzLmZyb21HZW9tZXRyeSggZ2VvbWV0cnkgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0ZGlyZWN0LnZlcnRpY2VzTmVlZFVwZGF0ZSA9IGdlb21ldHJ5LnZlcnRpY2VzTmVlZFVwZGF0ZTtcblx0XHRcdFx0ZGlyZWN0Lm5vcm1hbHNOZWVkVXBkYXRlID0gZ2VvbWV0cnkubm9ybWFsc05lZWRVcGRhdGU7XG5cdFx0XHRcdGRpcmVjdC5jb2xvcnNOZWVkVXBkYXRlID0gZ2VvbWV0cnkuY29sb3JzTmVlZFVwZGF0ZTtcblx0XHRcdFx0ZGlyZWN0LnV2c05lZWRVcGRhdGUgPSBnZW9tZXRyeS51dnNOZWVkVXBkYXRlO1xuXHRcdFx0XHRkaXJlY3QuZ3JvdXBzTmVlZFVwZGF0ZSA9IGdlb21ldHJ5Lmdyb3Vwc05lZWRVcGRhdGU7XG5cblx0XHRcdFx0Z2VvbWV0cnkudmVydGljZXNOZWVkVXBkYXRlID0gZmFsc2U7XG5cdFx0XHRcdGdlb21ldHJ5Lm5vcm1hbHNOZWVkVXBkYXRlID0gZmFsc2U7XG5cdFx0XHRcdGdlb21ldHJ5LmNvbG9yc05lZWRVcGRhdGUgPSBmYWxzZTtcblx0XHRcdFx0Z2VvbWV0cnkudXZzTmVlZFVwZGF0ZSA9IGZhbHNlO1xuXHRcdFx0XHRnZW9tZXRyeS5ncm91cHNOZWVkVXBkYXRlID0gZmFsc2U7XG5cblx0XHRcdFx0Z2VvbWV0cnkgPSBkaXJlY3Q7XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIGF0dHJpYnV0ZTtcblxuXHRcdFx0aWYgKCBnZW9tZXRyeS52ZXJ0aWNlc05lZWRVcGRhdGUgPT09IHRydWUgKSB7XG5cblx0XHRcdFx0YXR0cmlidXRlID0gdGhpcy5hdHRyaWJ1dGVzLnBvc2l0aW9uO1xuXG5cdFx0XHRcdGlmICggYXR0cmlidXRlICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRhdHRyaWJ1dGUuY29weVZlY3RvcjNzQXJyYXkoIGdlb21ldHJ5LnZlcnRpY2VzICk7XG5cdFx0XHRcdFx0YXR0cmlidXRlLm5lZWRzVXBkYXRlID0gdHJ1ZTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Z2VvbWV0cnkudmVydGljZXNOZWVkVXBkYXRlID0gZmFsc2U7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBnZW9tZXRyeS5ub3JtYWxzTmVlZFVwZGF0ZSA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRhdHRyaWJ1dGUgPSB0aGlzLmF0dHJpYnV0ZXMubm9ybWFsO1xuXG5cdFx0XHRcdGlmICggYXR0cmlidXRlICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRhdHRyaWJ1dGUuY29weVZlY3RvcjNzQXJyYXkoIGdlb21ldHJ5Lm5vcm1hbHMgKTtcblx0XHRcdFx0XHRhdHRyaWJ1dGUubmVlZHNVcGRhdGUgPSB0cnVlO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRnZW9tZXRyeS5ub3JtYWxzTmVlZFVwZGF0ZSA9IGZhbHNlO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggZ2VvbWV0cnkuY29sb3JzTmVlZFVwZGF0ZSA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRhdHRyaWJ1dGUgPSB0aGlzLmF0dHJpYnV0ZXMuY29sb3I7XG5cblx0XHRcdFx0aWYgKCBhdHRyaWJ1dGUgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdGF0dHJpYnV0ZS5jb3B5Q29sb3JzQXJyYXkoIGdlb21ldHJ5LmNvbG9ycyApO1xuXHRcdFx0XHRcdGF0dHJpYnV0ZS5uZWVkc1VwZGF0ZSA9IHRydWU7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGdlb21ldHJ5LmNvbG9yc05lZWRVcGRhdGUgPSBmYWxzZTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIGdlb21ldHJ5LnV2c05lZWRVcGRhdGUgKSB7XG5cblx0XHRcdFx0YXR0cmlidXRlID0gdGhpcy5hdHRyaWJ1dGVzLnV2O1xuXG5cdFx0XHRcdGlmICggYXR0cmlidXRlICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRhdHRyaWJ1dGUuY29weVZlY3RvcjJzQXJyYXkoIGdlb21ldHJ5LnV2cyApO1xuXHRcdFx0XHRcdGF0dHJpYnV0ZS5uZWVkc1VwZGF0ZSA9IHRydWU7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGdlb21ldHJ5LnV2c05lZWRVcGRhdGUgPSBmYWxzZTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIGdlb21ldHJ5LmxpbmVEaXN0YW5jZXNOZWVkVXBkYXRlICkge1xuXG5cdFx0XHRcdGF0dHJpYnV0ZSA9IHRoaXMuYXR0cmlidXRlcy5saW5lRGlzdGFuY2U7XG5cblx0XHRcdFx0aWYgKCBhdHRyaWJ1dGUgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdGF0dHJpYnV0ZS5jb3B5QXJyYXkoIGdlb21ldHJ5LmxpbmVEaXN0YW5jZXMgKTtcblx0XHRcdFx0XHRhdHRyaWJ1dGUubmVlZHNVcGRhdGUgPSB0cnVlO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRnZW9tZXRyeS5saW5lRGlzdGFuY2VzTmVlZFVwZGF0ZSA9IGZhbHNlO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggZ2VvbWV0cnkuZ3JvdXBzTmVlZFVwZGF0ZSApIHtcblxuXHRcdFx0XHRnZW9tZXRyeS5jb21wdXRlR3JvdXBzKCBvYmplY3QuZ2VvbWV0cnkgKTtcblx0XHRcdFx0dGhpcy5ncm91cHMgPSBnZW9tZXRyeS5ncm91cHM7XG5cblx0XHRcdFx0Z2VvbWV0cnkuZ3JvdXBzTmVlZFVwZGF0ZSA9IGZhbHNlO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGZyb21HZW9tZXRyeTogZnVuY3Rpb24gKCBnZW9tZXRyeSApIHtcblxuXHRcdFx0Z2VvbWV0cnkuX19kaXJlY3RHZW9tZXRyeSA9IG5ldyBEaXJlY3RHZW9tZXRyeSgpLmZyb21HZW9tZXRyeSggZ2VvbWV0cnkgKTtcblxuXHRcdFx0cmV0dXJuIHRoaXMuZnJvbURpcmVjdEdlb21ldHJ5KCBnZW9tZXRyeS5fX2RpcmVjdEdlb21ldHJ5ICk7XG5cblx0XHR9LFxuXG5cdFx0ZnJvbURpcmVjdEdlb21ldHJ5OiBmdW5jdGlvbiAoIGdlb21ldHJ5ICkge1xuXG5cdFx0XHR2YXIgcG9zaXRpb25zID0gbmV3IEZsb2F0MzJBcnJheSggZ2VvbWV0cnkudmVydGljZXMubGVuZ3RoICogMyApO1xuXHRcdFx0dGhpcy5hZGRBdHRyaWJ1dGUoICdwb3NpdGlvbicsIG5ldyBCdWZmZXJBdHRyaWJ1dGUoIHBvc2l0aW9ucywgMyApLmNvcHlWZWN0b3Izc0FycmF5KCBnZW9tZXRyeS52ZXJ0aWNlcyApICk7XG5cblx0XHRcdGlmICggZ2VvbWV0cnkubm9ybWFscy5sZW5ndGggPiAwICkge1xuXG5cdFx0XHRcdHZhciBub3JtYWxzID0gbmV3IEZsb2F0MzJBcnJheSggZ2VvbWV0cnkubm9ybWFscy5sZW5ndGggKiAzICk7XG5cdFx0XHRcdHRoaXMuYWRkQXR0cmlidXRlKCAnbm9ybWFsJywgbmV3IEJ1ZmZlckF0dHJpYnV0ZSggbm9ybWFscywgMyApLmNvcHlWZWN0b3Izc0FycmF5KCBnZW9tZXRyeS5ub3JtYWxzICkgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIGdlb21ldHJ5LmNvbG9ycy5sZW5ndGggPiAwICkge1xuXG5cdFx0XHRcdHZhciBjb2xvcnMgPSBuZXcgRmxvYXQzMkFycmF5KCBnZW9tZXRyeS5jb2xvcnMubGVuZ3RoICogMyApO1xuXHRcdFx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ2NvbG9yJywgbmV3IEJ1ZmZlckF0dHJpYnV0ZSggY29sb3JzLCAzICkuY29weUNvbG9yc0FycmF5KCBnZW9tZXRyeS5jb2xvcnMgKSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggZ2VvbWV0cnkudXZzLmxlbmd0aCA+IDAgKSB7XG5cblx0XHRcdFx0dmFyIHV2cyA9IG5ldyBGbG9hdDMyQXJyYXkoIGdlb21ldHJ5LnV2cy5sZW5ndGggKiAyICk7XG5cdFx0XHRcdHRoaXMuYWRkQXR0cmlidXRlKCAndXYnLCBuZXcgQnVmZmVyQXR0cmlidXRlKCB1dnMsIDIgKS5jb3B5VmVjdG9yMnNBcnJheSggZ2VvbWV0cnkudXZzICkgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIGdlb21ldHJ5LnV2czIubGVuZ3RoID4gMCApIHtcblxuXHRcdFx0XHR2YXIgdXZzMiA9IG5ldyBGbG9hdDMyQXJyYXkoIGdlb21ldHJ5LnV2czIubGVuZ3RoICogMiApO1xuXHRcdFx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ3V2MicsIG5ldyBCdWZmZXJBdHRyaWJ1dGUoIHV2czIsIDIgKS5jb3B5VmVjdG9yMnNBcnJheSggZ2VvbWV0cnkudXZzMiApICk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBnZW9tZXRyeS5pbmRpY2VzLmxlbmd0aCA+IDAgKSB7XG5cblx0XHRcdFx0dmFyIFR5cGVBcnJheSA9IGdlb21ldHJ5LnZlcnRpY2VzLmxlbmd0aCA+IDY1NTM1ID8gVWludDMyQXJyYXkgOiBVaW50MTZBcnJheTtcblx0XHRcdFx0dmFyIGluZGljZXMgPSBuZXcgVHlwZUFycmF5KCBnZW9tZXRyeS5pbmRpY2VzLmxlbmd0aCAqIDMgKTtcblx0XHRcdFx0dGhpcy5zZXRJbmRleCggbmV3IEJ1ZmZlckF0dHJpYnV0ZSggaW5kaWNlcywgMSApLmNvcHlJbmRpY2VzQXJyYXkoIGdlb21ldHJ5LmluZGljZXMgKSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIGdyb3Vwc1xuXG5cdFx0XHR0aGlzLmdyb3VwcyA9IGdlb21ldHJ5Lmdyb3VwcztcblxuXHRcdFx0Ly8gbW9ycGhzXG5cblx0XHRcdGZvciAoIHZhciBuYW1lIGluIGdlb21ldHJ5Lm1vcnBoVGFyZ2V0cyApIHtcblxuXHRcdFx0XHR2YXIgYXJyYXkgPSBbXTtcblx0XHRcdFx0dmFyIG1vcnBoVGFyZ2V0cyA9IGdlb21ldHJ5Lm1vcnBoVGFyZ2V0c1sgbmFtZSBdO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IG1vcnBoVGFyZ2V0cy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdFx0dmFyIG1vcnBoVGFyZ2V0ID0gbW9ycGhUYXJnZXRzWyBpIF07XG5cblx0XHRcdFx0XHR2YXIgYXR0cmlidXRlID0gbmV3IEZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUoIG1vcnBoVGFyZ2V0Lmxlbmd0aCAqIDMsIDMgKTtcblxuXHRcdFx0XHRcdGFycmF5LnB1c2goIGF0dHJpYnV0ZS5jb3B5VmVjdG9yM3NBcnJheSggbW9ycGhUYXJnZXQgKSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR0aGlzLm1vcnBoQXR0cmlidXRlc1sgbmFtZSBdID0gYXJyYXk7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gc2tpbm5pbmdcblxuXHRcdFx0aWYgKCBnZW9tZXRyeS5za2luSW5kaWNlcy5sZW5ndGggPiAwICkge1xuXG5cdFx0XHRcdHZhciBza2luSW5kaWNlcyA9IG5ldyBGbG9hdDMyQnVmZmVyQXR0cmlidXRlKCBnZW9tZXRyeS5za2luSW5kaWNlcy5sZW5ndGggKiA0LCA0ICk7XG5cdFx0XHRcdHRoaXMuYWRkQXR0cmlidXRlKCAnc2tpbkluZGV4Jywgc2tpbkluZGljZXMuY29weVZlY3RvcjRzQXJyYXkoIGdlb21ldHJ5LnNraW5JbmRpY2VzICkgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIGdlb21ldHJ5LnNraW5XZWlnaHRzLmxlbmd0aCA+IDAgKSB7XG5cblx0XHRcdFx0dmFyIHNraW5XZWlnaHRzID0gbmV3IEZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUoIGdlb21ldHJ5LnNraW5XZWlnaHRzLmxlbmd0aCAqIDQsIDQgKTtcblx0XHRcdFx0dGhpcy5hZGRBdHRyaWJ1dGUoICdza2luV2VpZ2h0Jywgc2tpbldlaWdodHMuY29weVZlY3RvcjRzQXJyYXkoIGdlb21ldHJ5LnNraW5XZWlnaHRzICkgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvL1xuXG5cdFx0XHRpZiAoIGdlb21ldHJ5LmJvdW5kaW5nU3BoZXJlICE9PSBudWxsICkge1xuXG5cdFx0XHRcdHRoaXMuYm91bmRpbmdTcGhlcmUgPSBnZW9tZXRyeS5ib3VuZGluZ1NwaGVyZS5jbG9uZSgpO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggZ2VvbWV0cnkuYm91bmRpbmdCb3ggIT09IG51bGwgKSB7XG5cblx0XHRcdFx0dGhpcy5ib3VuZGluZ0JveCA9IGdlb21ldHJ5LmJvdW5kaW5nQm94LmNsb25lKCk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Y29tcHV0ZUJvdW5kaW5nQm94OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdGlmICggdGhpcy5ib3VuZGluZ0JveCA9PT0gbnVsbCApIHtcblxuXHRcdFx0XHR0aGlzLmJvdW5kaW5nQm94ID0gbmV3IEJveDMoKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgcG9zaXRpb24gPSB0aGlzLmF0dHJpYnV0ZXMucG9zaXRpb247XG5cblx0XHRcdGlmICggcG9zaXRpb24gIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHR0aGlzLmJvdW5kaW5nQm94LnNldEZyb21CdWZmZXJBdHRyaWJ1dGUoIHBvc2l0aW9uICk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0dGhpcy5ib3VuZGluZ0JveC5tYWtlRW1wdHkoKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIGlzTmFOKCB0aGlzLmJvdW5kaW5nQm94Lm1pbi54ICkgfHwgaXNOYU4oIHRoaXMuYm91bmRpbmdCb3gubWluLnkgKSB8fCBpc05hTiggdGhpcy5ib3VuZGluZ0JveC5taW4ueiApICkge1xuXG5cdFx0XHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5CdWZmZXJHZW9tZXRyeS5jb21wdXRlQm91bmRpbmdCb3g6IENvbXB1dGVkIG1pbi9tYXggaGF2ZSBOYU4gdmFsdWVzLiBUaGUgXCJwb3NpdGlvblwiIGF0dHJpYnV0ZSBpcyBsaWtlbHkgdG8gaGF2ZSBOYU4gdmFsdWVzLicsIHRoaXMgKTtcblxuXHRcdFx0fVxuXG5cdFx0fSxcblxuXHRcdGNvbXB1dGVCb3VuZGluZ1NwaGVyZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgYm94ID0gbmV3IEJveDMoKTtcblx0XHRcdHZhciB2ZWN0b3IgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gY29tcHV0ZUJvdW5kaW5nU3BoZXJlKCkge1xuXG5cdFx0XHRcdGlmICggdGhpcy5ib3VuZGluZ1NwaGVyZSA9PT0gbnVsbCApIHtcblxuXHRcdFx0XHRcdHRoaXMuYm91bmRpbmdTcGhlcmUgPSBuZXcgU3BoZXJlKCk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBwb3NpdGlvbiA9IHRoaXMuYXR0cmlidXRlcy5wb3NpdGlvbjtcblxuXHRcdFx0XHRpZiAoIHBvc2l0aW9uICkge1xuXG5cdFx0XHRcdFx0dmFyIGNlbnRlciA9IHRoaXMuYm91bmRpbmdTcGhlcmUuY2VudGVyO1xuXG5cdFx0XHRcdFx0Ym94LnNldEZyb21CdWZmZXJBdHRyaWJ1dGUoIHBvc2l0aW9uICk7XG5cdFx0XHRcdFx0Ym94LmdldENlbnRlciggY2VudGVyICk7XG5cblx0XHRcdFx0XHQvLyBob3BpbmcgdG8gZmluZCBhIGJvdW5kaW5nU3BoZXJlIHdpdGggYSByYWRpdXMgc21hbGxlciB0aGFuIHRoZVxuXHRcdFx0XHRcdC8vIGJvdW5kaW5nU3BoZXJlIG9mIHRoZSBib3VuZGluZ0JveDogc3FydCgzKSBzbWFsbGVyIGluIHRoZSBiZXN0IGNhc2VcblxuXHRcdFx0XHRcdHZhciBtYXhSYWRpdXNTcSA9IDA7XG5cblx0XHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGlsID0gcG9zaXRpb24uY291bnQ7IGkgPCBpbDsgaSArKyApIHtcblxuXHRcdFx0XHRcdFx0dmVjdG9yLnggPSBwb3NpdGlvbi5nZXRYKCBpICk7XG5cdFx0XHRcdFx0XHR2ZWN0b3IueSA9IHBvc2l0aW9uLmdldFkoIGkgKTtcblx0XHRcdFx0XHRcdHZlY3Rvci56ID0gcG9zaXRpb24uZ2V0WiggaSApO1xuXHRcdFx0XHRcdFx0bWF4UmFkaXVzU3EgPSBNYXRoLm1heCggbWF4UmFkaXVzU3EsIGNlbnRlci5kaXN0YW5jZVRvU3F1YXJlZCggdmVjdG9yICkgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdHRoaXMuYm91bmRpbmdTcGhlcmUucmFkaXVzID0gTWF0aC5zcXJ0KCBtYXhSYWRpdXNTcSApO1xuXG5cdFx0XHRcdFx0aWYgKCBpc05hTiggdGhpcy5ib3VuZGluZ1NwaGVyZS5yYWRpdXMgKSApIHtcblxuXHRcdFx0XHRcdFx0Y29uc29sZS5lcnJvciggJ1RIUkVFLkJ1ZmZlckdlb21ldHJ5LmNvbXB1dGVCb3VuZGluZ1NwaGVyZSgpOiBDb21wdXRlZCByYWRpdXMgaXMgTmFOLiBUaGUgXCJwb3NpdGlvblwiIGF0dHJpYnV0ZSBpcyBsaWtlbHkgdG8gaGF2ZSBOYU4gdmFsdWVzLicsIHRoaXMgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdH07XG5cblx0XHR9KCksXG5cblx0XHRjb21wdXRlRmFjZU5vcm1hbHM6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Ly8gYmFja3dhcmRzIGNvbXBhdGliaWxpdHlcblxuXHRcdH0sXG5cblx0XHRjb21wdXRlVmVydGV4Tm9ybWFsczogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgaW5kZXggPSB0aGlzLmluZGV4O1xuXHRcdFx0dmFyIGF0dHJpYnV0ZXMgPSB0aGlzLmF0dHJpYnV0ZXM7XG5cdFx0XHR2YXIgZ3JvdXBzID0gdGhpcy5ncm91cHM7XG5cblx0XHRcdGlmICggYXR0cmlidXRlcy5wb3NpdGlvbiApIHtcblxuXHRcdFx0XHR2YXIgcG9zaXRpb25zID0gYXR0cmlidXRlcy5wb3NpdGlvbi5hcnJheTtcblxuXHRcdFx0XHRpZiAoIGF0dHJpYnV0ZXMubm9ybWFsID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ25vcm1hbCcsIG5ldyBCdWZmZXJBdHRyaWJ1dGUoIG5ldyBGbG9hdDMyQXJyYXkoIHBvc2l0aW9ucy5sZW5ndGggKSwgMyApICk7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdC8vIHJlc2V0IGV4aXN0aW5nIG5vcm1hbHMgdG8gemVyb1xuXG5cdFx0XHRcdFx0dmFyIGFycmF5ID0gYXR0cmlidXRlcy5ub3JtYWwuYXJyYXk7XG5cblx0XHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGlsID0gYXJyYXkubGVuZ3RoOyBpIDwgaWw7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRcdGFycmF5WyBpIF0gPSAwO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR2YXIgbm9ybWFscyA9IGF0dHJpYnV0ZXMubm9ybWFsLmFycmF5O1xuXG5cdFx0XHRcdHZhciB2QSwgdkIsIHZDO1xuXHRcdFx0XHR2YXIgcEEgPSBuZXcgVmVjdG9yMygpLCBwQiA9IG5ldyBWZWN0b3IzKCksIHBDID0gbmV3IFZlY3RvcjMoKTtcblx0XHRcdFx0dmFyIGNiID0gbmV3IFZlY3RvcjMoKSwgYWIgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRcdC8vIGluZGV4ZWQgZWxlbWVudHNcblxuXHRcdFx0XHRpZiAoIGluZGV4ICkge1xuXG5cdFx0XHRcdFx0dmFyIGluZGljZXMgPSBpbmRleC5hcnJheTtcblxuXHRcdFx0XHRcdGlmICggZ3JvdXBzLmxlbmd0aCA9PT0gMCApIHtcblxuXHRcdFx0XHRcdFx0dGhpcy5hZGRHcm91cCggMCwgaW5kaWNlcy5sZW5ndGggKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGZvciAoIHZhciBqID0gMCwgamwgPSBncm91cHMubGVuZ3RoOyBqIDwgamw7ICsrIGogKSB7XG5cblx0XHRcdFx0XHRcdHZhciBncm91cCA9IGdyb3Vwc1sgaiBdO1xuXG5cdFx0XHRcdFx0XHR2YXIgc3RhcnQgPSBncm91cC5zdGFydDtcblx0XHRcdFx0XHRcdHZhciBjb3VudCA9IGdyb3VwLmNvdW50O1xuXG5cdFx0XHRcdFx0XHRmb3IgKCB2YXIgaSA9IHN0YXJ0LCBpbCA9IHN0YXJ0ICsgY291bnQ7IGkgPCBpbDsgaSArPSAzICkge1xuXG5cdFx0XHRcdFx0XHRcdHZBID0gaW5kaWNlc1sgaSArIDAgXSAqIDM7XG5cdFx0XHRcdFx0XHRcdHZCID0gaW5kaWNlc1sgaSArIDEgXSAqIDM7XG5cdFx0XHRcdFx0XHRcdHZDID0gaW5kaWNlc1sgaSArIDIgXSAqIDM7XG5cblx0XHRcdFx0XHRcdFx0cEEuZnJvbUFycmF5KCBwb3NpdGlvbnMsIHZBICk7XG5cdFx0XHRcdFx0XHRcdHBCLmZyb21BcnJheSggcG9zaXRpb25zLCB2QiApO1xuXHRcdFx0XHRcdFx0XHRwQy5mcm9tQXJyYXkoIHBvc2l0aW9ucywgdkMgKTtcblxuXHRcdFx0XHRcdFx0XHRjYi5zdWJWZWN0b3JzKCBwQywgcEIgKTtcblx0XHRcdFx0XHRcdFx0YWIuc3ViVmVjdG9ycyggcEEsIHBCICk7XG5cdFx0XHRcdFx0XHRcdGNiLmNyb3NzKCBhYiApO1xuXG5cdFx0XHRcdFx0XHRcdG5vcm1hbHNbIHZBIF0gKz0gY2IueDtcblx0XHRcdFx0XHRcdFx0bm9ybWFsc1sgdkEgKyAxIF0gKz0gY2IueTtcblx0XHRcdFx0XHRcdFx0bm9ybWFsc1sgdkEgKyAyIF0gKz0gY2IuejtcblxuXHRcdFx0XHRcdFx0XHRub3JtYWxzWyB2QiBdICs9IGNiLng7XG5cdFx0XHRcdFx0XHRcdG5vcm1hbHNbIHZCICsgMSBdICs9IGNiLnk7XG5cdFx0XHRcdFx0XHRcdG5vcm1hbHNbIHZCICsgMiBdICs9IGNiLno7XG5cblx0XHRcdFx0XHRcdFx0bm9ybWFsc1sgdkMgXSArPSBjYi54O1xuXHRcdFx0XHRcdFx0XHRub3JtYWxzWyB2QyArIDEgXSArPSBjYi55O1xuXHRcdFx0XHRcdFx0XHRub3JtYWxzWyB2QyArIDIgXSArPSBjYi56O1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdC8vIG5vbi1pbmRleGVkIGVsZW1lbnRzICh1bmNvbm5lY3RlZCB0cmlhbmdsZSBzb3VwKVxuXG5cdFx0XHRcdFx0Zm9yICggdmFyIGkgPSAwLCBpbCA9IHBvc2l0aW9ucy5sZW5ndGg7IGkgPCBpbDsgaSArPSA5ICkge1xuXG5cdFx0XHRcdFx0XHRwQS5mcm9tQXJyYXkoIHBvc2l0aW9ucywgaSApO1xuXHRcdFx0XHRcdFx0cEIuZnJvbUFycmF5KCBwb3NpdGlvbnMsIGkgKyAzICk7XG5cdFx0XHRcdFx0XHRwQy5mcm9tQXJyYXkoIHBvc2l0aW9ucywgaSArIDYgKTtcblxuXHRcdFx0XHRcdFx0Y2Iuc3ViVmVjdG9ycyggcEMsIHBCICk7XG5cdFx0XHRcdFx0XHRhYi5zdWJWZWN0b3JzKCBwQSwgcEIgKTtcblx0XHRcdFx0XHRcdGNiLmNyb3NzKCBhYiApO1xuXG5cdFx0XHRcdFx0XHRub3JtYWxzWyBpIF0gPSBjYi54O1xuXHRcdFx0XHRcdFx0bm9ybWFsc1sgaSArIDEgXSA9IGNiLnk7XG5cdFx0XHRcdFx0XHRub3JtYWxzWyBpICsgMiBdID0gY2IuejtcblxuXHRcdFx0XHRcdFx0bm9ybWFsc1sgaSArIDMgXSA9IGNiLng7XG5cdFx0XHRcdFx0XHRub3JtYWxzWyBpICsgNCBdID0gY2IueTtcblx0XHRcdFx0XHRcdG5vcm1hbHNbIGkgKyA1IF0gPSBjYi56O1xuXG5cdFx0XHRcdFx0XHRub3JtYWxzWyBpICsgNiBdID0gY2IueDtcblx0XHRcdFx0XHRcdG5vcm1hbHNbIGkgKyA3IF0gPSBjYi55O1xuXHRcdFx0XHRcdFx0bm9ybWFsc1sgaSArIDggXSA9IGNiLno7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHRoaXMubm9ybWFsaXplTm9ybWFscygpO1xuXG5cdFx0XHRcdGF0dHJpYnV0ZXMubm9ybWFsLm5lZWRzVXBkYXRlID0gdHJ1ZTtcblxuXHRcdFx0fVxuXG5cdFx0fSxcblxuXHRcdG1lcmdlOiBmdW5jdGlvbiAoIGdlb21ldHJ5LCBvZmZzZXQgKSB7XG5cblx0XHRcdGlmICggKCBnZW9tZXRyeSAmJiBnZW9tZXRyeS5pc0J1ZmZlckdlb21ldHJ5ICkgPT09IGZhbHNlICkge1xuXG5cdFx0XHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5CdWZmZXJHZW9tZXRyeS5tZXJnZSgpOiBnZW9tZXRyeSBub3QgYW4gaW5zdGFuY2Ugb2YgVEhSRUUuQnVmZmVyR2VvbWV0cnkuJywgZ2VvbWV0cnkgKTtcblx0XHRcdFx0cmV0dXJuO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggb2Zmc2V0ID09PSB1bmRlZmluZWQgKSBvZmZzZXQgPSAwO1xuXG5cdFx0XHR2YXIgYXR0cmlidXRlcyA9IHRoaXMuYXR0cmlidXRlcztcblxuXHRcdFx0Zm9yICggdmFyIGtleSBpbiBhdHRyaWJ1dGVzICkge1xuXG5cdFx0XHRcdGlmICggZ2VvbWV0cnkuYXR0cmlidXRlc1sga2V5IF0gPT09IHVuZGVmaW5lZCApIGNvbnRpbnVlO1xuXG5cdFx0XHRcdHZhciBhdHRyaWJ1dGUxID0gYXR0cmlidXRlc1sga2V5IF07XG5cdFx0XHRcdHZhciBhdHRyaWJ1dGVBcnJheTEgPSBhdHRyaWJ1dGUxLmFycmF5O1xuXG5cdFx0XHRcdHZhciBhdHRyaWJ1dGUyID0gZ2VvbWV0cnkuYXR0cmlidXRlc1sga2V5IF07XG5cdFx0XHRcdHZhciBhdHRyaWJ1dGVBcnJheTIgPSBhdHRyaWJ1dGUyLmFycmF5O1xuXG5cdFx0XHRcdHZhciBhdHRyaWJ1dGVTaXplID0gYXR0cmlidXRlMi5pdGVtU2l6ZTtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGogPSBhdHRyaWJ1dGVTaXplICogb2Zmc2V0OyBpIDwgYXR0cmlidXRlQXJyYXkyLmxlbmd0aDsgaSArKywgaiArKyApIHtcblxuXHRcdFx0XHRcdGF0dHJpYnV0ZUFycmF5MVsgaiBdID0gYXR0cmlidXRlQXJyYXkyWyBpIF07XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdG5vcm1hbGl6ZU5vcm1hbHM6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIG5vcm1hbHMgPSB0aGlzLmF0dHJpYnV0ZXMubm9ybWFsLmFycmF5O1xuXG5cdFx0XHR2YXIgeCwgeSwgeiwgbjtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBpbCA9IG5vcm1hbHMubGVuZ3RoOyBpIDwgaWw7IGkgKz0gMyApIHtcblxuXHRcdFx0XHR4ID0gbm9ybWFsc1sgaSBdO1xuXHRcdFx0XHR5ID0gbm9ybWFsc1sgaSArIDEgXTtcblx0XHRcdFx0eiA9IG5vcm1hbHNbIGkgKyAyIF07XG5cblx0XHRcdFx0biA9IDEuMCAvIE1hdGguc3FydCggeCAqIHggKyB5ICogeSArIHogKiB6ICk7XG5cblx0XHRcdFx0bm9ybWFsc1sgaSBdICo9IG47XG5cdFx0XHRcdG5vcm1hbHNbIGkgKyAxIF0gKj0gbjtcblx0XHRcdFx0bm9ybWFsc1sgaSArIDIgXSAqPSBuO1xuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0dG9Ob25JbmRleGVkOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdGlmICggdGhpcy5pbmRleCA9PT0gbnVsbCApIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5CdWZmZXJHZW9tZXRyeS50b05vbkluZGV4ZWQoKTogR2VvbWV0cnkgaXMgYWxyZWFkeSBub24taW5kZXhlZC4nICk7XG5cdFx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBnZW9tZXRyeTIgPSBuZXcgQnVmZmVyR2VvbWV0cnkoKTtcblxuXHRcdFx0dmFyIGluZGljZXMgPSB0aGlzLmluZGV4LmFycmF5O1xuXHRcdFx0dmFyIGF0dHJpYnV0ZXMgPSB0aGlzLmF0dHJpYnV0ZXM7XG5cblx0XHRcdGZvciAoIHZhciBuYW1lIGluIGF0dHJpYnV0ZXMgKSB7XG5cblx0XHRcdFx0dmFyIGF0dHJpYnV0ZSA9IGF0dHJpYnV0ZXNbIG5hbWUgXTtcblxuXHRcdFx0XHR2YXIgYXJyYXkgPSBhdHRyaWJ1dGUuYXJyYXk7XG5cdFx0XHRcdHZhciBpdGVtU2l6ZSA9IGF0dHJpYnV0ZS5pdGVtU2l6ZTtcblxuXHRcdFx0XHR2YXIgYXJyYXkyID0gbmV3IGFycmF5LmNvbnN0cnVjdG9yKCBpbmRpY2VzLmxlbmd0aCAqIGl0ZW1TaXplICk7XG5cblx0XHRcdFx0dmFyIGluZGV4ID0gMCwgaW5kZXgyID0gMDtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBpbmRpY2VzLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRpbmRleCA9IGluZGljZXNbIGkgXSAqIGl0ZW1TaXplO1xuXG5cdFx0XHRcdFx0Zm9yICggdmFyIGogPSAwOyBqIDwgaXRlbVNpemU7IGogKysgKSB7XG5cblx0XHRcdFx0XHRcdGFycmF5MlsgaW5kZXgyICsrIF0gPSBhcnJheVsgaW5kZXggKysgXTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Z2VvbWV0cnkyLmFkZEF0dHJpYnV0ZSggbmFtZSwgbmV3IEJ1ZmZlckF0dHJpYnV0ZSggYXJyYXkyLCBpdGVtU2l6ZSApICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIGdlb21ldHJ5MjtcblxuXHRcdH0sXG5cblx0XHR0b0pTT046IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIGRhdGEgPSB7XG5cdFx0XHRcdG1ldGFkYXRhOiB7XG5cdFx0XHRcdFx0dmVyc2lvbjogNC40LFxuXHRcdFx0XHRcdHR5cGU6ICdCdWZmZXJHZW9tZXRyeScsXG5cdFx0XHRcdFx0Z2VuZXJhdG9yOiAnQnVmZmVyR2VvbWV0cnkudG9KU09OJ1xuXHRcdFx0XHR9XG5cdFx0XHR9O1xuXG5cdFx0XHQvLyBzdGFuZGFyZCBCdWZmZXJHZW9tZXRyeSBzZXJpYWxpemF0aW9uXG5cblx0XHRcdGRhdGEudXVpZCA9IHRoaXMudXVpZDtcblx0XHRcdGRhdGEudHlwZSA9IHRoaXMudHlwZTtcblx0XHRcdGlmICggdGhpcy5uYW1lICE9PSAnJyApIGRhdGEubmFtZSA9IHRoaXMubmFtZTtcblxuXHRcdFx0aWYgKCB0aGlzLnBhcmFtZXRlcnMgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHR2YXIgcGFyYW1ldGVycyA9IHRoaXMucGFyYW1ldGVycztcblxuXHRcdFx0XHRmb3IgKCB2YXIga2V5IGluIHBhcmFtZXRlcnMgKSB7XG5cblx0XHRcdFx0XHRpZiAoIHBhcmFtZXRlcnNbIGtleSBdICE9PSB1bmRlZmluZWQgKSBkYXRhWyBrZXkgXSA9IHBhcmFtZXRlcnNbIGtleSBdO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRyZXR1cm4gZGF0YTtcblxuXHRcdFx0fVxuXG5cdFx0XHRkYXRhLmRhdGEgPSB7IGF0dHJpYnV0ZXM6IHt9IH07XG5cblx0XHRcdHZhciBpbmRleCA9IHRoaXMuaW5kZXg7XG5cblx0XHRcdGlmICggaW5kZXggIT09IG51bGwgKSB7XG5cblx0XHRcdFx0dmFyIGFycmF5ID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoIGluZGV4LmFycmF5ICk7XG5cblx0XHRcdFx0ZGF0YS5kYXRhLmluZGV4ID0ge1xuXHRcdFx0XHRcdHR5cGU6IGluZGV4LmFycmF5LmNvbnN0cnVjdG9yLm5hbWUsXG5cdFx0XHRcdFx0YXJyYXk6IGFycmF5XG5cdFx0XHRcdH07XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIGF0dHJpYnV0ZXMgPSB0aGlzLmF0dHJpYnV0ZXM7XG5cblx0XHRcdGZvciAoIHZhciBrZXkgaW4gYXR0cmlidXRlcyApIHtcblxuXHRcdFx0XHR2YXIgYXR0cmlidXRlID0gYXR0cmlidXRlc1sga2V5IF07XG5cblx0XHRcdFx0dmFyIGFycmF5ID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoIGF0dHJpYnV0ZS5hcnJheSApO1xuXG5cdFx0XHRcdGRhdGEuZGF0YS5hdHRyaWJ1dGVzWyBrZXkgXSA9IHtcblx0XHRcdFx0XHRpdGVtU2l6ZTogYXR0cmlidXRlLml0ZW1TaXplLFxuXHRcdFx0XHRcdHR5cGU6IGF0dHJpYnV0ZS5hcnJheS5jb25zdHJ1Y3Rvci5uYW1lLFxuXHRcdFx0XHRcdGFycmF5OiBhcnJheSxcblx0XHRcdFx0XHRub3JtYWxpemVkOiBhdHRyaWJ1dGUubm9ybWFsaXplZFxuXHRcdFx0XHR9O1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBncm91cHMgPSB0aGlzLmdyb3VwcztcblxuXHRcdFx0aWYgKCBncm91cHMubGVuZ3RoID4gMCApIHtcblxuXHRcdFx0XHRkYXRhLmRhdGEuZ3JvdXBzID0gSlNPTi5wYXJzZSggSlNPTi5zdHJpbmdpZnkoIGdyb3VwcyApICk7XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIGJvdW5kaW5nU3BoZXJlID0gdGhpcy5ib3VuZGluZ1NwaGVyZTtcblxuXHRcdFx0aWYgKCBib3VuZGluZ1NwaGVyZSAhPT0gbnVsbCApIHtcblxuXHRcdFx0XHRkYXRhLmRhdGEuYm91bmRpbmdTcGhlcmUgPSB7XG5cdFx0XHRcdFx0Y2VudGVyOiBib3VuZGluZ1NwaGVyZS5jZW50ZXIudG9BcnJheSgpLFxuXHRcdFx0XHRcdHJhZGl1czogYm91bmRpbmdTcGhlcmUucmFkaXVzXG5cdFx0XHRcdH07XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIGRhdGE7XG5cblx0XHR9LFxuXG5cdFx0Y2xvbmU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Lypcblx0XHRcdC8vIEhhbmRsZSBwcmltaXRpdmVzXG5cblx0XHRcdHZhciBwYXJhbWV0ZXJzID0gdGhpcy5wYXJhbWV0ZXJzO1xuXG5cdFx0XHRpZiAoIHBhcmFtZXRlcnMgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHR2YXIgdmFsdWVzID0gW107XG5cblx0XHRcdFx0Zm9yICggdmFyIGtleSBpbiBwYXJhbWV0ZXJzICkge1xuXG5cdFx0XHRcdFx0dmFsdWVzLnB1c2goIHBhcmFtZXRlcnNbIGtleSBdICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBnZW9tZXRyeSA9IE9iamVjdC5jcmVhdGUoIHRoaXMuY29uc3RydWN0b3IucHJvdG90eXBlICk7XG5cdFx0XHRcdHRoaXMuY29uc3RydWN0b3IuYXBwbHkoIGdlb21ldHJ5LCB2YWx1ZXMgKTtcblx0XHRcdFx0cmV0dXJuIGdlb21ldHJ5O1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBuZXcgdGhpcy5jb25zdHJ1Y3RvcigpLmNvcHkoIHRoaXMgKTtcblx0XHRcdCovXG5cblx0XHRcdHJldHVybiBuZXcgQnVmZmVyR2VvbWV0cnkoKS5jb3B5KCB0aGlzICk7XG5cblx0XHR9LFxuXG5cdFx0Y29weTogZnVuY3Rpb24gKCBzb3VyY2UgKSB7XG5cblx0XHRcdHZhciBpbmRleCA9IHNvdXJjZS5pbmRleDtcblxuXHRcdFx0aWYgKCBpbmRleCAhPT0gbnVsbCApIHtcblxuXHRcdFx0XHR0aGlzLnNldEluZGV4KCBpbmRleC5jbG9uZSgpICk7XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIGF0dHJpYnV0ZXMgPSBzb3VyY2UuYXR0cmlidXRlcztcblxuXHRcdFx0Zm9yICggdmFyIG5hbWUgaW4gYXR0cmlidXRlcyApIHtcblxuXHRcdFx0XHR2YXIgYXR0cmlidXRlID0gYXR0cmlidXRlc1sgbmFtZSBdO1xuXHRcdFx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggbmFtZSwgYXR0cmlidXRlLmNsb25lKCkgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgZ3JvdXBzID0gc291cmNlLmdyb3VwcztcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBsID0gZ3JvdXBzLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0dmFyIGdyb3VwID0gZ3JvdXBzWyBpIF07XG5cdFx0XHRcdHRoaXMuYWRkR3JvdXAoIGdyb3VwLnN0YXJ0LCBncm91cC5jb3VudCwgZ3JvdXAubWF0ZXJpYWxJbmRleCApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGRpc3Bvc2U6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dGhpcy5kaXNwYXRjaEV2ZW50KCB7IHR5cGU6ICdkaXNwb3NlJyB9ICk7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdEJ1ZmZlckdlb21ldHJ5Lk1heEluZGV4ID0gNjU1MzU7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqIEBhdXRob3IgbWlrYWVsIGVtdGluZ2VyIC8gaHR0cDovL2dvbW8uc2UvXG5cdCAqIEBhdXRob3Igam9ub2JyMSAvIGh0dHA6Ly9qb25vYnIxLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gTWVzaCggZ2VvbWV0cnksIG1hdGVyaWFsICkge1xuXG5cdFx0T2JqZWN0M0QuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ01lc2gnO1xuXG5cdFx0dGhpcy5nZW9tZXRyeSA9IGdlb21ldHJ5ICE9PSB1bmRlZmluZWQgPyBnZW9tZXRyeSA6IG5ldyBCdWZmZXJHZW9tZXRyeSgpO1xuXHRcdHRoaXMubWF0ZXJpYWwgPSBtYXRlcmlhbCAhPT0gdW5kZWZpbmVkID8gbWF0ZXJpYWwgOiBuZXcgTWVzaEJhc2ljTWF0ZXJpYWwoIHsgY29sb3I6IE1hdGgucmFuZG9tKCkgKiAweGZmZmZmZiB9ICk7XG5cblx0XHR0aGlzLmRyYXdNb2RlID0gVHJpYW5nbGVzRHJhd01vZGU7XG5cblx0XHR0aGlzLnVwZGF0ZU1vcnBoVGFyZ2V0cygpO1xuXG5cdH1cblxuXHRNZXNoLnByb3RvdHlwZSA9IE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIE9iamVjdDNELnByb3RvdHlwZSApLCB7XG5cblx0XHRjb25zdHJ1Y3RvcjogTWVzaCxcblxuXHRcdGlzTWVzaDogdHJ1ZSxcblxuXHRcdHNldERyYXdNb2RlOiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHR0aGlzLmRyYXdNb2RlID0gdmFsdWU7XG5cblx0XHR9LFxuXG5cdFx0Y29weTogZnVuY3Rpb24gKCBzb3VyY2UgKSB7XG5cblx0XHRcdE9iamVjdDNELnByb3RvdHlwZS5jb3B5LmNhbGwoIHRoaXMsIHNvdXJjZSApO1xuXG5cdFx0XHR0aGlzLmRyYXdNb2RlID0gc291cmNlLmRyYXdNb2RlO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHR1cGRhdGVNb3JwaFRhcmdldHM6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIG1vcnBoVGFyZ2V0cyA9IHRoaXMuZ2VvbWV0cnkubW9ycGhUYXJnZXRzO1xuXG5cdFx0XHRpZiAoIG1vcnBoVGFyZ2V0cyAhPT0gdW5kZWZpbmVkICYmIG1vcnBoVGFyZ2V0cy5sZW5ndGggPiAwICkge1xuXG5cdFx0XHRcdHRoaXMubW9ycGhUYXJnZXRJbmZsdWVuY2VzID0gW107XG5cdFx0XHRcdHRoaXMubW9ycGhUYXJnZXREaWN0aW9uYXJ5ID0ge307XG5cblx0XHRcdFx0Zm9yICggdmFyIG0gPSAwLCBtbCA9IG1vcnBoVGFyZ2V0cy5sZW5ndGg7IG0gPCBtbDsgbSArKyApIHtcblxuXHRcdFx0XHRcdHRoaXMubW9ycGhUYXJnZXRJbmZsdWVuY2VzLnB1c2goIDAgKTtcblx0XHRcdFx0XHR0aGlzLm1vcnBoVGFyZ2V0RGljdGlvbmFyeVsgbW9ycGhUYXJnZXRzWyBtIF0ubmFtZSBdID0gbTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdH0sXG5cblx0XHRyYXljYXN0OiAoIGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIGludmVyc2VNYXRyaXggPSBuZXcgTWF0cml4NCgpO1xuXHRcdFx0dmFyIHJheSA9IG5ldyBSYXkoKTtcblx0XHRcdHZhciBzcGhlcmUgPSBuZXcgU3BoZXJlKCk7XG5cblx0XHRcdHZhciB2QSA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHR2YXIgdkIgPSBuZXcgVmVjdG9yMygpO1xuXHRcdFx0dmFyIHZDID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0dmFyIHRlbXBBID0gbmV3IFZlY3RvcjMoKTtcblx0XHRcdHZhciB0ZW1wQiA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHR2YXIgdGVtcEMgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHR2YXIgdXZBID0gbmV3IFZlY3RvcjIoKTtcblx0XHRcdHZhciB1dkIgPSBuZXcgVmVjdG9yMigpO1xuXHRcdFx0dmFyIHV2QyA9IG5ldyBWZWN0b3IyKCk7XG5cblx0XHRcdHZhciBiYXJ5Y29vcmQgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHR2YXIgaW50ZXJzZWN0aW9uUG9pbnQgPSBuZXcgVmVjdG9yMygpO1xuXHRcdFx0dmFyIGludGVyc2VjdGlvblBvaW50V29ybGQgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRmdW5jdGlvbiB1dkludGVyc2VjdGlvbiggcG9pbnQsIHAxLCBwMiwgcDMsIHV2MSwgdXYyLCB1djMgKSB7XG5cblx0XHRcdFx0VHJpYW5nbGUuYmFyeWNvb3JkRnJvbVBvaW50KCBwb2ludCwgcDEsIHAyLCBwMywgYmFyeWNvb3JkICk7XG5cblx0XHRcdFx0dXYxLm11bHRpcGx5U2NhbGFyKCBiYXJ5Y29vcmQueCApO1xuXHRcdFx0XHR1djIubXVsdGlwbHlTY2FsYXIoIGJhcnljb29yZC55ICk7XG5cdFx0XHRcdHV2My5tdWx0aXBseVNjYWxhciggYmFyeWNvb3JkLnogKTtcblxuXHRcdFx0XHR1djEuYWRkKCB1djIgKS5hZGQoIHV2MyApO1xuXG5cdFx0XHRcdHJldHVybiB1djEuY2xvbmUoKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRmdW5jdGlvbiBjaGVja0ludGVyc2VjdGlvbiggb2JqZWN0LCByYXljYXN0ZXIsIHJheSwgcEEsIHBCLCBwQywgcG9pbnQgKSB7XG5cblx0XHRcdFx0dmFyIGludGVyc2VjdDtcblx0XHRcdFx0dmFyIG1hdGVyaWFsID0gb2JqZWN0Lm1hdGVyaWFsO1xuXG5cdFx0XHRcdGlmICggbWF0ZXJpYWwuc2lkZSA9PT0gQmFja1NpZGUgKSB7XG5cblx0XHRcdFx0XHRpbnRlcnNlY3QgPSByYXkuaW50ZXJzZWN0VHJpYW5nbGUoIHBDLCBwQiwgcEEsIHRydWUsIHBvaW50ICk7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdGludGVyc2VjdCA9IHJheS5pbnRlcnNlY3RUcmlhbmdsZSggcEEsIHBCLCBwQywgbWF0ZXJpYWwuc2lkZSAhPT0gRG91YmxlU2lkZSwgcG9pbnQgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCBpbnRlcnNlY3QgPT09IG51bGwgKSByZXR1cm4gbnVsbDtcblxuXHRcdFx0XHRpbnRlcnNlY3Rpb25Qb2ludFdvcmxkLmNvcHkoIHBvaW50ICk7XG5cdFx0XHRcdGludGVyc2VjdGlvblBvaW50V29ybGQuYXBwbHlNYXRyaXg0KCBvYmplY3QubWF0cml4V29ybGQgKTtcblxuXHRcdFx0XHR2YXIgZGlzdGFuY2UgPSByYXljYXN0ZXIucmF5Lm9yaWdpbi5kaXN0YW5jZVRvKCBpbnRlcnNlY3Rpb25Qb2ludFdvcmxkICk7XG5cblx0XHRcdFx0aWYgKCBkaXN0YW5jZSA8IHJheWNhc3Rlci5uZWFyIHx8IGRpc3RhbmNlID4gcmF5Y2FzdGVyLmZhciApIHJldHVybiBudWxsO1xuXG5cdFx0XHRcdHJldHVybiB7XG5cdFx0XHRcdFx0ZGlzdGFuY2U6IGRpc3RhbmNlLFxuXHRcdFx0XHRcdHBvaW50OiBpbnRlcnNlY3Rpb25Qb2ludFdvcmxkLmNsb25lKCksXG5cdFx0XHRcdFx0b2JqZWN0OiBvYmplY3Rcblx0XHRcdFx0fTtcblxuXHRcdFx0fVxuXG5cdFx0XHRmdW5jdGlvbiBjaGVja0J1ZmZlckdlb21ldHJ5SW50ZXJzZWN0aW9uKCBvYmplY3QsIHJheWNhc3RlciwgcmF5LCBwb3NpdGlvbnMsIHV2cywgYSwgYiwgYyApIHtcblxuXHRcdFx0XHR2QS5mcm9tQXJyYXkoIHBvc2l0aW9ucywgYSAqIDMgKTtcblx0XHRcdFx0dkIuZnJvbUFycmF5KCBwb3NpdGlvbnMsIGIgKiAzICk7XG5cdFx0XHRcdHZDLmZyb21BcnJheSggcG9zaXRpb25zLCBjICogMyApO1xuXG5cdFx0XHRcdHZhciBpbnRlcnNlY3Rpb24gPSBjaGVja0ludGVyc2VjdGlvbiggb2JqZWN0LCByYXljYXN0ZXIsIHJheSwgdkEsIHZCLCB2QywgaW50ZXJzZWN0aW9uUG9pbnQgKTtcblxuXHRcdFx0XHRpZiAoIGludGVyc2VjdGlvbiApIHtcblxuXHRcdFx0XHRcdGlmICggdXZzICkge1xuXG5cdFx0XHRcdFx0XHR1dkEuZnJvbUFycmF5KCB1dnMsIGEgKiAyICk7XG5cdFx0XHRcdFx0XHR1dkIuZnJvbUFycmF5KCB1dnMsIGIgKiAyICk7XG5cdFx0XHRcdFx0XHR1dkMuZnJvbUFycmF5KCB1dnMsIGMgKiAyICk7XG5cblx0XHRcdFx0XHRcdGludGVyc2VjdGlvbi51diA9IHV2SW50ZXJzZWN0aW9uKCBpbnRlcnNlY3Rpb25Qb2ludCwgIHZBLCB2QiwgdkMsICB1dkEsIHV2QiwgdXZDICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRpbnRlcnNlY3Rpb24uZmFjZSA9IG5ldyBGYWNlMyggYSwgYiwgYywgVHJpYW5nbGUubm9ybWFsKCB2QSwgdkIsIHZDICkgKTtcblx0XHRcdFx0XHRpbnRlcnNlY3Rpb24uZmFjZUluZGV4ID0gYTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuIGludGVyc2VjdGlvbjtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gcmF5Y2FzdCggcmF5Y2FzdGVyLCBpbnRlcnNlY3RzICkge1xuXG5cdFx0XHRcdHZhciBnZW9tZXRyeSA9IHRoaXMuZ2VvbWV0cnk7XG5cdFx0XHRcdHZhciBtYXRlcmlhbCA9IHRoaXMubWF0ZXJpYWw7XG5cdFx0XHRcdHZhciBtYXRyaXhXb3JsZCA9IHRoaXMubWF0cml4V29ybGQ7XG5cblx0XHRcdFx0aWYgKCBtYXRlcmlhbCA9PT0gdW5kZWZpbmVkICkgcmV0dXJuO1xuXG5cdFx0XHRcdC8vIENoZWNraW5nIGJvdW5kaW5nU3BoZXJlIGRpc3RhbmNlIHRvIHJheVxuXG5cdFx0XHRcdGlmICggZ2VvbWV0cnkuYm91bmRpbmdTcGhlcmUgPT09IG51bGwgKSBnZW9tZXRyeS5jb21wdXRlQm91bmRpbmdTcGhlcmUoKTtcblxuXHRcdFx0XHRzcGhlcmUuY29weSggZ2VvbWV0cnkuYm91bmRpbmdTcGhlcmUgKTtcblx0XHRcdFx0c3BoZXJlLmFwcGx5TWF0cml4NCggbWF0cml4V29ybGQgKTtcblxuXHRcdFx0XHRpZiAoIHJheWNhc3Rlci5yYXkuaW50ZXJzZWN0c1NwaGVyZSggc3BoZXJlICkgPT09IGZhbHNlICkgcmV0dXJuO1xuXG5cdFx0XHRcdC8vXG5cblx0XHRcdFx0aW52ZXJzZU1hdHJpeC5nZXRJbnZlcnNlKCBtYXRyaXhXb3JsZCApO1xuXHRcdFx0XHRyYXkuY29weSggcmF5Y2FzdGVyLnJheSApLmFwcGx5TWF0cml4NCggaW52ZXJzZU1hdHJpeCApO1xuXG5cdFx0XHRcdC8vIENoZWNrIGJvdW5kaW5nQm94IGJlZm9yZSBjb250aW51aW5nXG5cblx0XHRcdFx0aWYgKCBnZW9tZXRyeS5ib3VuZGluZ0JveCAhPT0gbnVsbCApIHtcblxuXHRcdFx0XHRcdGlmICggcmF5LmludGVyc2VjdHNCb3goIGdlb21ldHJ5LmJvdW5kaW5nQm94ICkgPT09IGZhbHNlICkgcmV0dXJuO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR2YXIgdXZzLCBpbnRlcnNlY3Rpb247XG5cblx0XHRcdFx0aWYgKCBnZW9tZXRyeS5pc0J1ZmZlckdlb21ldHJ5ICkge1xuXG5cdFx0XHRcdFx0dmFyIGEsIGIsIGM7XG5cdFx0XHRcdFx0dmFyIGluZGV4ID0gZ2VvbWV0cnkuaW5kZXg7XG5cdFx0XHRcdFx0dmFyIGF0dHJpYnV0ZXMgPSBnZW9tZXRyeS5hdHRyaWJ1dGVzO1xuXHRcdFx0XHRcdHZhciBwb3NpdGlvbnMgPSBhdHRyaWJ1dGVzLnBvc2l0aW9uLmFycmF5O1xuXG5cdFx0XHRcdFx0aWYgKCBhdHRyaWJ1dGVzLnV2ICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRcdHV2cyA9IGF0dHJpYnV0ZXMudXYuYXJyYXk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRpZiAoIGluZGV4ICE9PSBudWxsICkge1xuXG5cdFx0XHRcdFx0XHR2YXIgaW5kaWNlcyA9IGluZGV4LmFycmF5O1xuXG5cdFx0XHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBpbmRpY2VzLmxlbmd0aDsgaSA8IGw7IGkgKz0gMyApIHtcblxuXHRcdFx0XHRcdFx0XHRhID0gaW5kaWNlc1sgaSBdO1xuXHRcdFx0XHRcdFx0XHRiID0gaW5kaWNlc1sgaSArIDEgXTtcblx0XHRcdFx0XHRcdFx0YyA9IGluZGljZXNbIGkgKyAyIF07XG5cblx0XHRcdFx0XHRcdFx0aW50ZXJzZWN0aW9uID0gY2hlY2tCdWZmZXJHZW9tZXRyeUludGVyc2VjdGlvbiggdGhpcywgcmF5Y2FzdGVyLCByYXksIHBvc2l0aW9ucywgdXZzLCBhLCBiLCBjICk7XG5cblx0XHRcdFx0XHRcdFx0aWYgKCBpbnRlcnNlY3Rpb24gKSB7XG5cblx0XHRcdFx0XHRcdFx0XHRpbnRlcnNlY3Rpb24uZmFjZUluZGV4ID0gTWF0aC5mbG9vciggaSAvIDMgKTsgLy8gdHJpYW5nbGUgbnVtYmVyIGluIGluZGljZXMgYnVmZmVyIHNlbWFudGljc1xuXHRcdFx0XHRcdFx0XHRcdGludGVyc2VjdHMucHVzaCggaW50ZXJzZWN0aW9uICk7XG5cblx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cblx0XHRcdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IHBvc2l0aW9ucy5sZW5ndGg7IGkgPCBsOyBpICs9IDkgKSB7XG5cblx0XHRcdFx0XHRcdFx0YSA9IGkgLyAzO1xuXHRcdFx0XHRcdFx0XHRiID0gYSArIDE7XG5cdFx0XHRcdFx0XHRcdGMgPSBhICsgMjtcblxuXHRcdFx0XHRcdFx0XHRpbnRlcnNlY3Rpb24gPSBjaGVja0J1ZmZlckdlb21ldHJ5SW50ZXJzZWN0aW9uKCB0aGlzLCByYXljYXN0ZXIsIHJheSwgcG9zaXRpb25zLCB1dnMsIGEsIGIsIGMgKTtcblxuXHRcdFx0XHRcdFx0XHRpZiAoIGludGVyc2VjdGlvbiApIHtcblxuXHRcdFx0XHRcdFx0XHRcdGludGVyc2VjdGlvbi5pbmRleCA9IGE7IC8vIHRyaWFuZ2xlIG51bWJlciBpbiBwb3NpdGlvbnMgYnVmZmVyIHNlbWFudGljc1xuXHRcdFx0XHRcdFx0XHRcdGludGVyc2VjdHMucHVzaCggaW50ZXJzZWN0aW9uICk7XG5cblx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSBlbHNlIGlmICggZ2VvbWV0cnkuaXNHZW9tZXRyeSApIHtcblxuXHRcdFx0XHRcdHZhciBmdkEsIGZ2QiwgZnZDO1xuXHRcdFx0XHRcdHZhciBpc0ZhY2VNYXRlcmlhbCA9IChtYXRlcmlhbCAmJiBtYXRlcmlhbC5pc011bHRpTWF0ZXJpYWwpO1xuXHRcdFx0XHRcdHZhciBtYXRlcmlhbHMgPSBpc0ZhY2VNYXRlcmlhbCA9PT0gdHJ1ZSA/IG1hdGVyaWFsLm1hdGVyaWFscyA6IG51bGw7XG5cblx0XHRcdFx0XHR2YXIgdmVydGljZXMgPSBnZW9tZXRyeS52ZXJ0aWNlcztcblx0XHRcdFx0XHR2YXIgZmFjZXMgPSBnZW9tZXRyeS5mYWNlcztcblx0XHRcdFx0XHR2YXIgZmFjZVZlcnRleFV2cyA9IGdlb21ldHJ5LmZhY2VWZXJ0ZXhVdnNbIDAgXTtcblx0XHRcdFx0XHRpZiAoIGZhY2VWZXJ0ZXhVdnMubGVuZ3RoID4gMCApIHV2cyA9IGZhY2VWZXJ0ZXhVdnM7XG5cblx0XHRcdFx0XHRmb3IgKCB2YXIgZiA9IDAsIGZsID0gZmFjZXMubGVuZ3RoOyBmIDwgZmw7IGYgKysgKSB7XG5cblx0XHRcdFx0XHRcdHZhciBmYWNlID0gZmFjZXNbIGYgXTtcblx0XHRcdFx0XHRcdHZhciBmYWNlTWF0ZXJpYWwgPSBpc0ZhY2VNYXRlcmlhbCA9PT0gdHJ1ZSA/IG1hdGVyaWFsc1sgZmFjZS5tYXRlcmlhbEluZGV4IF0gOiBtYXRlcmlhbDtcblxuXHRcdFx0XHRcdFx0aWYgKCBmYWNlTWF0ZXJpYWwgPT09IHVuZGVmaW5lZCApIGNvbnRpbnVlO1xuXG5cdFx0XHRcdFx0XHRmdkEgPSB2ZXJ0aWNlc1sgZmFjZS5hIF07XG5cdFx0XHRcdFx0XHRmdkIgPSB2ZXJ0aWNlc1sgZmFjZS5iIF07XG5cdFx0XHRcdFx0XHRmdkMgPSB2ZXJ0aWNlc1sgZmFjZS5jIF07XG5cblx0XHRcdFx0XHRcdGlmICggZmFjZU1hdGVyaWFsLm1vcnBoVGFyZ2V0cyA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRcdFx0XHR2YXIgbW9ycGhUYXJnZXRzID0gZ2VvbWV0cnkubW9ycGhUYXJnZXRzO1xuXHRcdFx0XHRcdFx0XHR2YXIgbW9ycGhJbmZsdWVuY2VzID0gdGhpcy5tb3JwaFRhcmdldEluZmx1ZW5jZXM7XG5cblx0XHRcdFx0XHRcdFx0dkEuc2V0KCAwLCAwLCAwICk7XG5cdFx0XHRcdFx0XHRcdHZCLnNldCggMCwgMCwgMCApO1xuXHRcdFx0XHRcdFx0XHR2Qy5zZXQoIDAsIDAsIDAgKTtcblxuXHRcdFx0XHRcdFx0XHRmb3IgKCB2YXIgdCA9IDAsIHRsID0gbW9ycGhUYXJnZXRzLmxlbmd0aDsgdCA8IHRsOyB0ICsrICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0dmFyIGluZmx1ZW5jZSA9IG1vcnBoSW5mbHVlbmNlc1sgdCBdO1xuXG5cdFx0XHRcdFx0XHRcdFx0aWYgKCBpbmZsdWVuY2UgPT09IDAgKSBjb250aW51ZTtcblxuXHRcdFx0XHRcdFx0XHRcdHZhciB0YXJnZXRzID0gbW9ycGhUYXJnZXRzWyB0IF0udmVydGljZXM7XG5cblx0XHRcdFx0XHRcdFx0XHR2QS5hZGRTY2FsZWRWZWN0b3IoIHRlbXBBLnN1YlZlY3RvcnMoIHRhcmdldHNbIGZhY2UuYSBdLCBmdkEgKSwgaW5mbHVlbmNlICk7XG5cdFx0XHRcdFx0XHRcdFx0dkIuYWRkU2NhbGVkVmVjdG9yKCB0ZW1wQi5zdWJWZWN0b3JzKCB0YXJnZXRzWyBmYWNlLmIgXSwgZnZCICksIGluZmx1ZW5jZSApO1xuXHRcdFx0XHRcdFx0XHRcdHZDLmFkZFNjYWxlZFZlY3RvciggdGVtcEMuc3ViVmVjdG9ycyggdGFyZ2V0c1sgZmFjZS5jIF0sIGZ2QyApLCBpbmZsdWVuY2UgKTtcblxuXHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdFx0dkEuYWRkKCBmdkEgKTtcblx0XHRcdFx0XHRcdFx0dkIuYWRkKCBmdkIgKTtcblx0XHRcdFx0XHRcdFx0dkMuYWRkKCBmdkMgKTtcblxuXHRcdFx0XHRcdFx0XHRmdkEgPSB2QTtcblx0XHRcdFx0XHRcdFx0ZnZCID0gdkI7XG5cdFx0XHRcdFx0XHRcdGZ2QyA9IHZDO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdGludGVyc2VjdGlvbiA9IGNoZWNrSW50ZXJzZWN0aW9uKCB0aGlzLCByYXljYXN0ZXIsIHJheSwgZnZBLCBmdkIsIGZ2QywgaW50ZXJzZWN0aW9uUG9pbnQgKTtcblxuXHRcdFx0XHRcdFx0aWYgKCBpbnRlcnNlY3Rpb24gKSB7XG5cblx0XHRcdFx0XHRcdFx0aWYgKCB1dnMgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHR2YXIgdXZzX2YgPSB1dnNbIGYgXTtcblx0XHRcdFx0XHRcdFx0XHR1dkEuY29weSggdXZzX2ZbIDAgXSApO1xuXHRcdFx0XHRcdFx0XHRcdHV2Qi5jb3B5KCB1dnNfZlsgMSBdICk7XG5cdFx0XHRcdFx0XHRcdFx0dXZDLmNvcHkoIHV2c19mWyAyIF0gKTtcblxuXHRcdFx0XHRcdFx0XHRcdGludGVyc2VjdGlvbi51diA9IHV2SW50ZXJzZWN0aW9uKCBpbnRlcnNlY3Rpb25Qb2ludCwgZnZBLCBmdkIsIGZ2QywgdXZBLCB1dkIsIHV2QyApO1xuXG5cdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0XHRpbnRlcnNlY3Rpb24uZmFjZSA9IGZhY2U7XG5cdFx0XHRcdFx0XHRcdGludGVyc2VjdGlvbi5mYWNlSW5kZXggPSBmO1xuXHRcdFx0XHRcdFx0XHRpbnRlcnNlY3RzLnB1c2goIGludGVyc2VjdGlvbiApO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9O1xuXG5cdFx0fSgpICksXG5cblx0XHRjbG9uZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IHRoaXMuY29uc3RydWN0b3IoIHRoaXMuZ2VvbWV0cnksIHRoaXMubWF0ZXJpYWwgKS5jb3B5KCB0aGlzICk7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIE11Z2VuODcgLyBodHRwczovL2dpdGh1Yi5jb20vTXVnZW44N1xuXHQgKi9cblxuXHRmdW5jdGlvbiBCb3hCdWZmZXJHZW9tZXRyeSggd2lkdGgsIGhlaWdodCwgZGVwdGgsIHdpZHRoU2VnbWVudHMsIGhlaWdodFNlZ21lbnRzLCBkZXB0aFNlZ21lbnRzICkge1xuXG5cdFx0QnVmZmVyR2VvbWV0cnkuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ0JveEJ1ZmZlckdlb21ldHJ5JztcblxuXHRcdHRoaXMucGFyYW1ldGVycyA9IHtcblx0XHRcdHdpZHRoOiB3aWR0aCxcblx0XHRcdGhlaWdodDogaGVpZ2h0LFxuXHRcdFx0ZGVwdGg6IGRlcHRoLFxuXHRcdFx0d2lkdGhTZWdtZW50czogd2lkdGhTZWdtZW50cyxcblx0XHRcdGhlaWdodFNlZ21lbnRzOiBoZWlnaHRTZWdtZW50cyxcblx0XHRcdGRlcHRoU2VnbWVudHM6IGRlcHRoU2VnbWVudHNcblx0XHR9O1xuXG5cdFx0dmFyIHNjb3BlID0gdGhpcztcblxuXHRcdC8vIHNlZ21lbnRzXG5cdFx0d2lkdGhTZWdtZW50cyA9IE1hdGguZmxvb3IoIHdpZHRoU2VnbWVudHMgKSB8fCAxO1xuXHRcdGhlaWdodFNlZ21lbnRzID0gTWF0aC5mbG9vciggaGVpZ2h0U2VnbWVudHMgKSB8fCAxO1xuXHRcdGRlcHRoU2VnbWVudHMgPSBNYXRoLmZsb29yKCBkZXB0aFNlZ21lbnRzICkgfHwgMTtcblxuXHRcdC8vIHRoZXNlIGFyZSB1c2VkIHRvIGNhbGN1bGF0ZSBidWZmZXIgbGVuZ3RoXG5cdFx0dmFyIHZlcnRleENvdW50ID0gY2FsY3VsYXRlVmVydGV4Q291bnQoIHdpZHRoU2VnbWVudHMsIGhlaWdodFNlZ21lbnRzLCBkZXB0aFNlZ21lbnRzICk7XG5cdFx0dmFyIGluZGV4Q291bnQgPSBjYWxjdWxhdGVJbmRleENvdW50KCB3aWR0aFNlZ21lbnRzLCBoZWlnaHRTZWdtZW50cywgZGVwdGhTZWdtZW50cyApO1xuXG5cdFx0Ly8gYnVmZmVyc1xuXHRcdHZhciBpbmRpY2VzID0gbmV3ICggaW5kZXhDb3VudCA+IDY1NTM1ID8gVWludDMyQXJyYXkgOiBVaW50MTZBcnJheSApKCBpbmRleENvdW50ICk7XG5cdFx0dmFyIHZlcnRpY2VzID0gbmV3IEZsb2F0MzJBcnJheSggdmVydGV4Q291bnQgKiAzICk7XG5cdFx0dmFyIG5vcm1hbHMgPSBuZXcgRmxvYXQzMkFycmF5KCB2ZXJ0ZXhDb3VudCAqIDMgKTtcblx0XHR2YXIgdXZzID0gbmV3IEZsb2F0MzJBcnJheSggdmVydGV4Q291bnQgKiAyICk7XG5cblx0XHQvLyBvZmZzZXQgdmFyaWFibGVzXG5cdFx0dmFyIHZlcnRleEJ1ZmZlck9mZnNldCA9IDA7XG5cdFx0dmFyIHV2QnVmZmVyT2Zmc2V0ID0gMDtcblx0XHR2YXIgaW5kZXhCdWZmZXJPZmZzZXQgPSAwO1xuXHRcdHZhciBudW1iZXJPZlZlcnRpY2VzID0gMDtcblxuXHRcdC8vIGdyb3VwIHZhcmlhYmxlc1xuXHRcdHZhciBncm91cFN0YXJ0ID0gMDtcblxuXHRcdC8vIGJ1aWxkIGVhY2ggc2lkZSBvZiB0aGUgYm94IGdlb21ldHJ5XG5cdFx0YnVpbGRQbGFuZSggJ3onLCAneScsICd4JywgLSAxLCAtIDEsIGRlcHRoLCBoZWlnaHQsICAgd2lkdGgsICBkZXB0aFNlZ21lbnRzLCBoZWlnaHRTZWdtZW50cywgMCApOyAvLyBweFxuXHRcdGJ1aWxkUGxhbmUoICd6JywgJ3knLCAneCcsICAgMSwgLSAxLCBkZXB0aCwgaGVpZ2h0LCAtIHdpZHRoLCAgZGVwdGhTZWdtZW50cywgaGVpZ2h0U2VnbWVudHMsIDEgKTsgLy8gbnhcblx0XHRidWlsZFBsYW5lKCAneCcsICd6JywgJ3knLCAgIDEsICAgMSwgd2lkdGgsIGRlcHRoLCAgICBoZWlnaHQsIHdpZHRoU2VnbWVudHMsIGRlcHRoU2VnbWVudHMsICAyICk7IC8vIHB5XG5cdFx0YnVpbGRQbGFuZSggJ3gnLCAneicsICd5JywgICAxLCAtIDEsIHdpZHRoLCBkZXB0aCwgIC0gaGVpZ2h0LCB3aWR0aFNlZ21lbnRzLCBkZXB0aFNlZ21lbnRzLCAgMyApOyAvLyBueVxuXHRcdGJ1aWxkUGxhbmUoICd4JywgJ3knLCAneicsICAgMSwgLSAxLCB3aWR0aCwgaGVpZ2h0LCAgIGRlcHRoLCAgd2lkdGhTZWdtZW50cywgaGVpZ2h0U2VnbWVudHMsIDQgKTsgLy8gcHpcblx0XHRidWlsZFBsYW5lKCAneCcsICd5JywgJ3onLCAtIDEsIC0gMSwgd2lkdGgsIGhlaWdodCwgLSBkZXB0aCwgIHdpZHRoU2VnbWVudHMsIGhlaWdodFNlZ21lbnRzLCA1ICk7IC8vIG56XG5cblx0XHQvLyBidWlsZCBnZW9tZXRyeVxuXHRcdHRoaXMuc2V0SW5kZXgoIG5ldyBCdWZmZXJBdHRyaWJ1dGUoIGluZGljZXMsIDEgKSApO1xuXHRcdHRoaXMuYWRkQXR0cmlidXRlKCAncG9zaXRpb24nLCBuZXcgQnVmZmVyQXR0cmlidXRlKCB2ZXJ0aWNlcywgMyApICk7XG5cdFx0dGhpcy5hZGRBdHRyaWJ1dGUoICdub3JtYWwnLCBuZXcgQnVmZmVyQXR0cmlidXRlKCBub3JtYWxzLCAzICkgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ3V2JywgbmV3IEJ1ZmZlckF0dHJpYnV0ZSggdXZzLCAyICkgKTtcblxuXHRcdC8vIGhlbHBlciBmdW5jdGlvbnNcblxuXHRcdGZ1bmN0aW9uIGNhbGN1bGF0ZVZlcnRleENvdW50KCB3LCBoLCBkICkge1xuXG5cdFx0XHR2YXIgdmVydGljZXMgPSAwO1xuXG5cdFx0XHQvLyBjYWxjdWxhdGUgdGhlIGFtb3VudCBvZiB2ZXJ0aWNlcyBmb3IgZWFjaCBzaWRlIChwbGFuZSlcblx0XHRcdHZlcnRpY2VzICs9ICh3ICsgMSkgKiAoaCArIDEpICogMjsgLy8geHlcblx0XHRcdHZlcnRpY2VzICs9ICh3ICsgMSkgKiAoZCArIDEpICogMjsgLy8geHpcblx0XHRcdHZlcnRpY2VzICs9IChkICsgMSkgKiAoaCArIDEpICogMjsgLy8genlcblxuXHRcdFx0cmV0dXJuIHZlcnRpY2VzO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gY2FsY3VsYXRlSW5kZXhDb3VudCggdywgaCwgZCApIHtcblxuXHRcdFx0dmFyIGluZGV4ID0gMDtcblxuXHRcdFx0Ly8gY2FsY3VsYXRlIHRoZSBhbW91bnQgb2Ygc3F1YXJlcyBmb3IgZWFjaCBzaWRlXG5cdFx0XHRpbmRleCArPSB3ICogaCAqIDI7IC8vIHh5XG5cdFx0XHRpbmRleCArPSB3ICogZCAqIDI7IC8vIHh6XG5cdFx0XHRpbmRleCArPSBkICogaCAqIDI7IC8vIHp5XG5cblx0XHRcdHJldHVybiBpbmRleCAqIDY7IC8vIHR3byB0cmlhbmdsZXMgcGVyIHNxdWFyZSA9PiBzaXggdmVydGljZXMgcGVyIHNxdWFyZVxuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gYnVpbGRQbGFuZSggdSwgdiwgdywgdWRpciwgdmRpciwgd2lkdGgsIGhlaWdodCwgZGVwdGgsIGdyaWRYLCBncmlkWSwgbWF0ZXJpYWxJbmRleCApIHtcblxuXHRcdFx0dmFyIHNlZ21lbnRXaWR0aFx0PSB3aWR0aCAvIGdyaWRYO1xuXHRcdFx0dmFyIHNlZ21lbnRIZWlnaHQgPSBoZWlnaHQgLyBncmlkWTtcblxuXHRcdFx0dmFyIHdpZHRoSGFsZiA9IHdpZHRoIC8gMjtcblx0XHRcdHZhciBoZWlnaHRIYWxmID0gaGVpZ2h0IC8gMjtcblx0XHRcdHZhciBkZXB0aEhhbGYgPSBkZXB0aCAvIDI7XG5cblx0XHRcdHZhciBncmlkWDEgPSBncmlkWCArIDE7XG5cdFx0XHR2YXIgZ3JpZFkxID0gZ3JpZFkgKyAxO1xuXG5cdFx0XHR2YXIgdmVydGV4Q291bnRlciA9IDA7XG5cdFx0XHR2YXIgZ3JvdXBDb3VudCA9IDA7XG5cblx0XHRcdHZhciB2ZWN0b3IgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHQvLyBnZW5lcmF0ZSB2ZXJ0aWNlcywgbm9ybWFscyBhbmQgdXZzXG5cblx0XHRcdGZvciAoIHZhciBpeSA9IDA7IGl5IDwgZ3JpZFkxOyBpeSArKyApIHtcblxuXHRcdFx0XHR2YXIgeSA9IGl5ICogc2VnbWVudEhlaWdodCAtIGhlaWdodEhhbGY7XG5cblx0XHRcdFx0Zm9yICggdmFyIGl4ID0gMDsgaXggPCBncmlkWDE7IGl4ICsrICkge1xuXG5cdFx0XHRcdFx0dmFyIHggPSBpeCAqIHNlZ21lbnRXaWR0aCAtIHdpZHRoSGFsZjtcblxuXHRcdFx0XHRcdC8vIHNldCB2YWx1ZXMgdG8gY29ycmVjdCB2ZWN0b3IgY29tcG9uZW50XG5cdFx0XHRcdFx0dmVjdG9yWyB1IF0gPSB4ICogdWRpcjtcblx0XHRcdFx0XHR2ZWN0b3JbIHYgXSA9IHkgKiB2ZGlyO1xuXHRcdFx0XHRcdHZlY3RvclsgdyBdID0gZGVwdGhIYWxmO1xuXG5cdFx0XHRcdFx0Ly8gbm93IGFwcGx5IHZlY3RvciB0byB2ZXJ0ZXggYnVmZmVyXG5cdFx0XHRcdFx0dmVydGljZXNbIHZlcnRleEJ1ZmZlck9mZnNldCBdID0gdmVjdG9yLng7XG5cdFx0XHRcdFx0dmVydGljZXNbIHZlcnRleEJ1ZmZlck9mZnNldCArIDEgXSA9IHZlY3Rvci55O1xuXHRcdFx0XHRcdHZlcnRpY2VzWyB2ZXJ0ZXhCdWZmZXJPZmZzZXQgKyAyIF0gPSB2ZWN0b3IuejtcblxuXHRcdFx0XHRcdC8vIHNldCB2YWx1ZXMgdG8gY29ycmVjdCB2ZWN0b3IgY29tcG9uZW50XG5cdFx0XHRcdFx0dmVjdG9yWyB1IF0gPSAwO1xuXHRcdFx0XHRcdHZlY3RvclsgdiBdID0gMDtcblx0XHRcdFx0XHR2ZWN0b3JbIHcgXSA9IGRlcHRoID4gMCA/IDEgOiAtIDE7XG5cblx0XHRcdFx0XHQvLyBub3cgYXBwbHkgdmVjdG9yIHRvIG5vcm1hbCBidWZmZXJcblx0XHRcdFx0XHRub3JtYWxzWyB2ZXJ0ZXhCdWZmZXJPZmZzZXQgXSA9IHZlY3Rvci54O1xuXHRcdFx0XHRcdG5vcm1hbHNbIHZlcnRleEJ1ZmZlck9mZnNldCArIDEgXSA9IHZlY3Rvci55O1xuXHRcdFx0XHRcdG5vcm1hbHNbIHZlcnRleEJ1ZmZlck9mZnNldCArIDIgXSA9IHZlY3Rvci56O1xuXG5cdFx0XHRcdFx0Ly8gdXZzXG5cdFx0XHRcdFx0dXZzWyB1dkJ1ZmZlck9mZnNldCBdID0gaXggLyBncmlkWDtcblx0XHRcdFx0XHR1dnNbIHV2QnVmZmVyT2Zmc2V0ICsgMSBdID0gMSAtICggaXkgLyBncmlkWSApO1xuXG5cdFx0XHRcdFx0Ly8gdXBkYXRlIG9mZnNldHMgYW5kIGNvdW50ZXJzXG5cdFx0XHRcdFx0dmVydGV4QnVmZmVyT2Zmc2V0ICs9IDM7XG5cdFx0XHRcdFx0dXZCdWZmZXJPZmZzZXQgKz0gMjtcblx0XHRcdFx0XHR2ZXJ0ZXhDb3VudGVyICs9IDE7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdC8vIDEuIHlvdSBuZWVkIHRocmVlIGluZGljZXMgdG8gZHJhdyBhIHNpbmdsZSBmYWNlXG5cdFx0XHQvLyAyLiBhIHNpbmdsZSBzZWdtZW50IGNvbnNpc3RzIG9mIHR3byBmYWNlc1xuXHRcdFx0Ly8gMy4gc28gd2UgbmVlZCB0byBnZW5lcmF0ZSBzaXggKDIqMykgaW5kaWNlcyBwZXIgc2VnbWVudFxuXG5cdFx0XHRmb3IgKCBpeSA9IDA7IGl5IDwgZ3JpZFk7IGl5ICsrICkge1xuXG5cdFx0XHRcdGZvciAoIGl4ID0gMDsgaXggPCBncmlkWDsgaXggKysgKSB7XG5cblx0XHRcdFx0XHQvLyBpbmRpY2VzXG5cdFx0XHRcdFx0dmFyIGEgPSBudW1iZXJPZlZlcnRpY2VzICsgaXggKyBncmlkWDEgKiBpeTtcblx0XHRcdFx0XHR2YXIgYiA9IG51bWJlck9mVmVydGljZXMgKyBpeCArIGdyaWRYMSAqICggaXkgKyAxICk7XG5cdFx0XHRcdFx0dmFyIGMgPSBudW1iZXJPZlZlcnRpY2VzICsgKCBpeCArIDEgKSArIGdyaWRYMSAqICggaXkgKyAxICk7XG5cdFx0XHRcdFx0dmFyIGQgPSBudW1iZXJPZlZlcnRpY2VzICsgKCBpeCArIDEgKSArIGdyaWRYMSAqIGl5O1xuXG5cdFx0XHRcdFx0Ly8gZmFjZSBvbmVcblx0XHRcdFx0XHRpbmRpY2VzWyBpbmRleEJ1ZmZlck9mZnNldCBdID0gYTtcblx0XHRcdFx0XHRpbmRpY2VzWyBpbmRleEJ1ZmZlck9mZnNldCArIDEgXSA9IGI7XG5cdFx0XHRcdFx0aW5kaWNlc1sgaW5kZXhCdWZmZXJPZmZzZXQgKyAyIF0gPSBkO1xuXG5cdFx0XHRcdFx0Ly8gZmFjZSB0d29cblx0XHRcdFx0XHRpbmRpY2VzWyBpbmRleEJ1ZmZlck9mZnNldCArIDMgXSA9IGI7XG5cdFx0XHRcdFx0aW5kaWNlc1sgaW5kZXhCdWZmZXJPZmZzZXQgKyA0IF0gPSBjO1xuXHRcdFx0XHRcdGluZGljZXNbIGluZGV4QnVmZmVyT2Zmc2V0ICsgNSBdID0gZDtcblxuXHRcdFx0XHRcdC8vIHVwZGF0ZSBvZmZzZXRzIGFuZCBjb3VudGVyc1xuXHRcdFx0XHRcdGluZGV4QnVmZmVyT2Zmc2V0ICs9IDY7XG5cdFx0XHRcdFx0Z3JvdXBDb3VudCArPSA2O1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHQvLyBhZGQgYSBncm91cCB0byB0aGUgZ2VvbWV0cnkuIHRoaXMgd2lsbCBlbnN1cmUgbXVsdGkgbWF0ZXJpYWwgc3VwcG9ydFxuXHRcdFx0c2NvcGUuYWRkR3JvdXAoIGdyb3VwU3RhcnQsIGdyb3VwQ291bnQsIG1hdGVyaWFsSW5kZXggKTtcblxuXHRcdFx0Ly8gY2FsY3VsYXRlIG5ldyBzdGFydCB2YWx1ZSBmb3IgZ3JvdXBzXG5cdFx0XHRncm91cFN0YXJ0ICs9IGdyb3VwQ291bnQ7XG5cblx0XHRcdC8vIHVwZGF0ZSB0b3RhbCBudW1iZXIgb2YgdmVydGljZXNcblx0XHRcdG51bWJlck9mVmVydGljZXMgKz0gdmVydGV4Q291bnRlcjtcblxuXHRcdH1cblxuXHR9XG5cblx0Qm94QnVmZmVyR2VvbWV0cnkucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlICk7XG5cdEJveEJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IEJveEJ1ZmZlckdlb21ldHJ5O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBiYXNlZCBvbiBodHRwOi8vcGFwZXJ2aXNpb24zZC5nb29nbGVjb2RlLmNvbS9zdm4vdHJ1bmsvYXMzL3RydW5rL3NyYy9vcmcvcGFwZXJ2aXNpb24zZC9vYmplY3RzL3ByaW1pdGl2ZXMvUGxhbmUuYXNcblx0ICovXG5cblx0ZnVuY3Rpb24gUGxhbmVCdWZmZXJHZW9tZXRyeSggd2lkdGgsIGhlaWdodCwgd2lkdGhTZWdtZW50cywgaGVpZ2h0U2VnbWVudHMgKSB7XG5cblx0XHRCdWZmZXJHZW9tZXRyeS5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnUGxhbmVCdWZmZXJHZW9tZXRyeSc7XG5cblx0XHR0aGlzLnBhcmFtZXRlcnMgPSB7XG5cdFx0XHR3aWR0aDogd2lkdGgsXG5cdFx0XHRoZWlnaHQ6IGhlaWdodCxcblx0XHRcdHdpZHRoU2VnbWVudHM6IHdpZHRoU2VnbWVudHMsXG5cdFx0XHRoZWlnaHRTZWdtZW50czogaGVpZ2h0U2VnbWVudHNcblx0XHR9O1xuXG5cdFx0dmFyIHdpZHRoX2hhbGYgPSB3aWR0aCAvIDI7XG5cdFx0dmFyIGhlaWdodF9oYWxmID0gaGVpZ2h0IC8gMjtcblxuXHRcdHZhciBncmlkWCA9IE1hdGguZmxvb3IoIHdpZHRoU2VnbWVudHMgKSB8fCAxO1xuXHRcdHZhciBncmlkWSA9IE1hdGguZmxvb3IoIGhlaWdodFNlZ21lbnRzICkgfHwgMTtcblxuXHRcdHZhciBncmlkWDEgPSBncmlkWCArIDE7XG5cdFx0dmFyIGdyaWRZMSA9IGdyaWRZICsgMTtcblxuXHRcdHZhciBzZWdtZW50X3dpZHRoID0gd2lkdGggLyBncmlkWDtcblx0XHR2YXIgc2VnbWVudF9oZWlnaHQgPSBoZWlnaHQgLyBncmlkWTtcblxuXHRcdHZhciB2ZXJ0aWNlcyA9IG5ldyBGbG9hdDMyQXJyYXkoIGdyaWRYMSAqIGdyaWRZMSAqIDMgKTtcblx0XHR2YXIgbm9ybWFscyA9IG5ldyBGbG9hdDMyQXJyYXkoIGdyaWRYMSAqIGdyaWRZMSAqIDMgKTtcblx0XHR2YXIgdXZzID0gbmV3IEZsb2F0MzJBcnJheSggZ3JpZFgxICogZ3JpZFkxICogMiApO1xuXG5cdFx0dmFyIG9mZnNldCA9IDA7XG5cdFx0dmFyIG9mZnNldDIgPSAwO1xuXG5cdFx0Zm9yICggdmFyIGl5ID0gMDsgaXkgPCBncmlkWTE7IGl5ICsrICkge1xuXG5cdFx0XHR2YXIgeSA9IGl5ICogc2VnbWVudF9oZWlnaHQgLSBoZWlnaHRfaGFsZjtcblxuXHRcdFx0Zm9yICggdmFyIGl4ID0gMDsgaXggPCBncmlkWDE7IGl4ICsrICkge1xuXG5cdFx0XHRcdHZhciB4ID0gaXggKiBzZWdtZW50X3dpZHRoIC0gd2lkdGhfaGFsZjtcblxuXHRcdFx0XHR2ZXJ0aWNlc1sgb2Zmc2V0IF0gPSB4O1xuXHRcdFx0XHR2ZXJ0aWNlc1sgb2Zmc2V0ICsgMSBdID0gLSB5O1xuXG5cdFx0XHRcdG5vcm1hbHNbIG9mZnNldCArIDIgXSA9IDE7XG5cblx0XHRcdFx0dXZzWyBvZmZzZXQyIF0gPSBpeCAvIGdyaWRYO1xuXHRcdFx0XHR1dnNbIG9mZnNldDIgKyAxIF0gPSAxIC0gKCBpeSAvIGdyaWRZICk7XG5cblx0XHRcdFx0b2Zmc2V0ICs9IDM7XG5cdFx0XHRcdG9mZnNldDIgKz0gMjtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0b2Zmc2V0ID0gMDtcblxuXHRcdHZhciBpbmRpY2VzID0gbmV3ICggKCB2ZXJ0aWNlcy5sZW5ndGggLyAzICkgPiA2NTUzNSA/IFVpbnQzMkFycmF5IDogVWludDE2QXJyYXkgKSggZ3JpZFggKiBncmlkWSAqIDYgKTtcblxuXHRcdGZvciAoIHZhciBpeSA9IDA7IGl5IDwgZ3JpZFk7IGl5ICsrICkge1xuXG5cdFx0XHRmb3IgKCB2YXIgaXggPSAwOyBpeCA8IGdyaWRYOyBpeCArKyApIHtcblxuXHRcdFx0XHR2YXIgYSA9IGl4ICsgZ3JpZFgxICogaXk7XG5cdFx0XHRcdHZhciBiID0gaXggKyBncmlkWDEgKiAoIGl5ICsgMSApO1xuXHRcdFx0XHR2YXIgYyA9ICggaXggKyAxICkgKyBncmlkWDEgKiAoIGl5ICsgMSApO1xuXHRcdFx0XHR2YXIgZCA9ICggaXggKyAxICkgKyBncmlkWDEgKiBpeTtcblxuXHRcdFx0XHRpbmRpY2VzWyBvZmZzZXQgXSA9IGE7XG5cdFx0XHRcdGluZGljZXNbIG9mZnNldCArIDEgXSA9IGI7XG5cdFx0XHRcdGluZGljZXNbIG9mZnNldCArIDIgXSA9IGQ7XG5cblx0XHRcdFx0aW5kaWNlc1sgb2Zmc2V0ICsgMyBdID0gYjtcblx0XHRcdFx0aW5kaWNlc1sgb2Zmc2V0ICsgNCBdID0gYztcblx0XHRcdFx0aW5kaWNlc1sgb2Zmc2V0ICsgNSBdID0gZDtcblxuXHRcdFx0XHRvZmZzZXQgKz0gNjtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0dGhpcy5zZXRJbmRleCggbmV3IEJ1ZmZlckF0dHJpYnV0ZSggaW5kaWNlcywgMSApICk7XG5cdFx0dGhpcy5hZGRBdHRyaWJ1dGUoICdwb3NpdGlvbicsIG5ldyBCdWZmZXJBdHRyaWJ1dGUoIHZlcnRpY2VzLCAzICkgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ25vcm1hbCcsIG5ldyBCdWZmZXJBdHRyaWJ1dGUoIG5vcm1hbHMsIDMgKSApO1xuXHRcdHRoaXMuYWRkQXR0cmlidXRlKCAndXYnLCBuZXcgQnVmZmVyQXR0cmlidXRlKCB1dnMsIDIgKSApO1xuXG5cdH1cblxuXHRQbGFuZUJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRQbGFuZUJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFBsYW5lQnVmZmVyR2VvbWV0cnk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqIEBhdXRob3IgbWlrYWVsIGVtdGluZ2VyIC8gaHR0cDovL2dvbW8uc2UvXG5cdCAqIEBhdXRob3IgV2VzdExhbmdsZXkgLyBodHRwOi8vZ2l0aHViLmNvbS9XZXN0TGFuZ2xleVxuXHQqL1xuXG5cdGZ1bmN0aW9uIENhbWVyYSgpIHtcblxuXHRcdE9iamVjdDNELmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdDYW1lcmEnO1xuXG5cdFx0dGhpcy5tYXRyaXhXb3JsZEludmVyc2UgPSBuZXcgTWF0cml4NCgpO1xuXHRcdHRoaXMucHJvamVjdGlvbk1hdHJpeCA9IG5ldyBNYXRyaXg0KCk7XG5cblx0fVxuXG5cdENhbWVyYS5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBPYmplY3QzRC5wcm90b3R5cGUgKTtcblx0Q2FtZXJhLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IENhbWVyYTtcblxuXHRDYW1lcmEucHJvdG90eXBlLmlzQ2FtZXJhID0gdHJ1ZTtcblxuXHRDYW1lcmEucHJvdG90eXBlLmdldFdvcmxkRGlyZWN0aW9uID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0dmFyIHF1YXRlcm5pb24gPSBuZXcgUXVhdGVybmlvbigpO1xuXG5cdFx0cmV0dXJuIGZ1bmN0aW9uIGdldFdvcmxkRGlyZWN0aW9uKCBvcHRpb25hbFRhcmdldCApIHtcblxuXHRcdFx0dmFyIHJlc3VsdCA9IG9wdGlvbmFsVGFyZ2V0IHx8IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdHRoaXMuZ2V0V29ybGRRdWF0ZXJuaW9uKCBxdWF0ZXJuaW9uICk7XG5cblx0XHRcdHJldHVybiByZXN1bHQuc2V0KCAwLCAwLCAtIDEgKS5hcHBseVF1YXRlcm5pb24oIHF1YXRlcm5pb24gKTtcblxuXHRcdH07XG5cblx0fSgpO1xuXG5cdENhbWVyYS5wcm90b3R5cGUubG9va0F0ID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0Ly8gVGhpcyByb3V0aW5lIGRvZXMgbm90IHN1cHBvcnQgY2FtZXJhcyB3aXRoIHJvdGF0ZWQgYW5kL29yIHRyYW5zbGF0ZWQgcGFyZW50KHMpXG5cblx0XHR2YXIgbTEgPSBuZXcgTWF0cml4NCgpO1xuXG5cdFx0cmV0dXJuIGZ1bmN0aW9uIGxvb2tBdCggdmVjdG9yICkge1xuXG5cdFx0XHRtMS5sb29rQXQoIHRoaXMucG9zaXRpb24sIHZlY3RvciwgdGhpcy51cCApO1xuXG5cdFx0XHR0aGlzLnF1YXRlcm5pb24uc2V0RnJvbVJvdGF0aW9uTWF0cml4KCBtMSApO1xuXG5cdFx0fTtcblxuXHR9KCk7XG5cblx0Q2FtZXJhLnByb3RvdHlwZS5jbG9uZSA9IGZ1bmN0aW9uICgpIHtcblxuXHRcdHJldHVybiBuZXcgdGhpcy5jb25zdHJ1Y3RvcigpLmNvcHkoIHRoaXMgKTtcblxuXHR9O1xuXG5cdENhbWVyYS5wcm90b3R5cGUuY29weSA9IGZ1bmN0aW9uICggc291cmNlICkge1xuXG5cdFx0T2JqZWN0M0QucHJvdG90eXBlLmNvcHkuY2FsbCggdGhpcywgc291cmNlICk7XG5cblx0XHR0aGlzLm1hdHJpeFdvcmxkSW52ZXJzZS5jb3B5KCBzb3VyY2UubWF0cml4V29ybGRJbnZlcnNlICk7XG5cdFx0dGhpcy5wcm9qZWN0aW9uTWF0cml4LmNvcHkoIHNvdXJjZS5wcm9qZWN0aW9uTWF0cml4ICk7XG5cblx0XHRyZXR1cm4gdGhpcztcblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yIGdyZWdnbWFuIC8gaHR0cDovL2dhbWVzLmdyZWdnbWFuLmNvbS9cblx0ICogQGF1dGhvciB6ejg1IC8gaHR0cDovL3d3dy5sYWI0Z2FtZXMubmV0L3p6ODUvYmxvZ1xuXHQgKiBAYXV0aG9yIHRzY2h3XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFBlcnNwZWN0aXZlQ2FtZXJhKCBmb3YsIGFzcGVjdCwgbmVhciwgZmFyICkge1xuXG5cdFx0Q2FtZXJhLmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdQZXJzcGVjdGl2ZUNhbWVyYSc7XG5cblx0XHR0aGlzLmZvdiA9IGZvdiAhPT0gdW5kZWZpbmVkID8gZm92IDogNTA7XG5cdFx0dGhpcy56b29tID0gMTtcblxuXHRcdHRoaXMubmVhciA9IG5lYXIgIT09IHVuZGVmaW5lZCA/IG5lYXIgOiAwLjE7XG5cdFx0dGhpcy5mYXIgPSBmYXIgIT09IHVuZGVmaW5lZCA/IGZhciA6IDIwMDA7XG5cdFx0dGhpcy5mb2N1cyA9IDEwO1xuXG5cdFx0dGhpcy5hc3BlY3QgPSBhc3BlY3QgIT09IHVuZGVmaW5lZCA/IGFzcGVjdCA6IDE7XG5cdFx0dGhpcy52aWV3ID0gbnVsbDtcblxuXHRcdHRoaXMuZmlsbUdhdWdlID0gMzU7XHQvLyB3aWR0aCBvZiB0aGUgZmlsbSAoZGVmYXVsdCBpbiBtaWxsaW1ldGVycylcblx0XHR0aGlzLmZpbG1PZmZzZXQgPSAwO1x0Ly8gaG9yaXpvbnRhbCBmaWxtIG9mZnNldCAoc2FtZSB1bml0IGFzIGdhdWdlKVxuXG5cdFx0dGhpcy51cGRhdGVQcm9qZWN0aW9uTWF0cml4KCk7XG5cblx0fVxuXG5cdFBlcnNwZWN0aXZlQ2FtZXJhLnByb3RvdHlwZSA9IE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIENhbWVyYS5wcm90b3R5cGUgKSwge1xuXG5cdFx0Y29uc3RydWN0b3I6IFBlcnNwZWN0aXZlQ2FtZXJhLFxuXG5cdFx0aXNQZXJzcGVjdGl2ZUNhbWVyYTogdHJ1ZSxcblxuXHRcdGNvcHk6IGZ1bmN0aW9uICggc291cmNlICkge1xuXG5cdFx0XHRDYW1lcmEucHJvdG90eXBlLmNvcHkuY2FsbCggdGhpcywgc291cmNlICk7XG5cblx0XHRcdHRoaXMuZm92ID0gc291cmNlLmZvdjtcblx0XHRcdHRoaXMuem9vbSA9IHNvdXJjZS56b29tO1xuXG5cdFx0XHR0aGlzLm5lYXIgPSBzb3VyY2UubmVhcjtcblx0XHRcdHRoaXMuZmFyID0gc291cmNlLmZhcjtcblx0XHRcdHRoaXMuZm9jdXMgPSBzb3VyY2UuZm9jdXM7XG5cblx0XHRcdHRoaXMuYXNwZWN0ID0gc291cmNlLmFzcGVjdDtcblx0XHRcdHRoaXMudmlldyA9IHNvdXJjZS52aWV3ID09PSBudWxsID8gbnVsbCA6IE9iamVjdC5hc3NpZ24oIHt9LCBzb3VyY2UudmlldyApO1xuXG5cdFx0XHR0aGlzLmZpbG1HYXVnZSA9IHNvdXJjZS5maWxtR2F1Z2U7XG5cdFx0XHR0aGlzLmZpbG1PZmZzZXQgPSBzb3VyY2UuZmlsbU9mZnNldDtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0LyoqXG5cdFx0ICogU2V0cyB0aGUgRk9WIGJ5IGZvY2FsIGxlbmd0aCBpbiByZXNwZWN0IHRvIHRoZSBjdXJyZW50IC5maWxtR2F1Z2UuXG5cdFx0ICpcblx0XHQgKiBUaGUgZGVmYXVsdCBmaWxtIGdhdWdlIGlzIDM1LCBzbyB0aGF0IHRoZSBmb2NhbCBsZW5ndGggY2FuIGJlIHNwZWNpZmllZCBmb3Jcblx0XHQgKiBhIDM1bW0gKGZ1bGwgZnJhbWUpIGNhbWVyYS5cblx0XHQgKlxuXHRcdCAqIFZhbHVlcyBmb3IgZm9jYWwgbGVuZ3RoIGFuZCBmaWxtIGdhdWdlIG11c3QgaGF2ZSB0aGUgc2FtZSB1bml0LlxuXHRcdCAqL1xuXHRcdHNldEZvY2FsTGVuZ3RoOiBmdW5jdGlvbiAoIGZvY2FsTGVuZ3RoICkge1xuXG5cdFx0XHQvLyBzZWUgaHR0cDovL3d3dy5ib2JhdGtpbnMuY29tL3Bob3RvZ3JhcGh5L3RlY2huaWNhbC9maWVsZF9vZl92aWV3Lmh0bWxcblx0XHRcdHZhciB2RXh0ZW50U2xvcGUgPSAwLjUgKiB0aGlzLmdldEZpbG1IZWlnaHQoKSAvIGZvY2FsTGVuZ3RoO1xuXG5cdFx0XHR0aGlzLmZvdiA9IF9NYXRoLlJBRDJERUcgKiAyICogTWF0aC5hdGFuKCB2RXh0ZW50U2xvcGUgKTtcblx0XHRcdHRoaXMudXBkYXRlUHJvamVjdGlvbk1hdHJpeCgpO1xuXG5cdFx0fSxcblxuXHRcdC8qKlxuXHRcdCAqIENhbGN1bGF0ZXMgdGhlIGZvY2FsIGxlbmd0aCBmcm9tIHRoZSBjdXJyZW50IC5mb3YgYW5kIC5maWxtR2F1Z2UuXG5cdFx0ICovXG5cdFx0Z2V0Rm9jYWxMZW5ndGg6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIHZFeHRlbnRTbG9wZSA9IE1hdGgudGFuKCBfTWF0aC5ERUcyUkFEICogMC41ICogdGhpcy5mb3YgKTtcblxuXHRcdFx0cmV0dXJuIDAuNSAqIHRoaXMuZ2V0RmlsbUhlaWdodCgpIC8gdkV4dGVudFNsb3BlO1xuXG5cdFx0fSxcblxuXHRcdGdldEVmZmVjdGl2ZUZPVjogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gX01hdGguUkFEMkRFRyAqIDIgKiBNYXRoLmF0YW4oXG5cdFx0XHRcdFx0TWF0aC50YW4oIF9NYXRoLkRFRzJSQUQgKiAwLjUgKiB0aGlzLmZvdiApIC8gdGhpcy56b29tICk7XG5cblx0XHR9LFxuXG5cdFx0Z2V0RmlsbVdpZHRoOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdC8vIGZpbG0gbm90IGNvbXBsZXRlbHkgY292ZXJlZCBpbiBwb3J0cmFpdCBmb3JtYXQgKGFzcGVjdCA8IDEpXG5cdFx0XHRyZXR1cm4gdGhpcy5maWxtR2F1Z2UgKiBNYXRoLm1pbiggdGhpcy5hc3BlY3QsIDEgKTtcblxuXHRcdH0sXG5cblx0XHRnZXRGaWxtSGVpZ2h0OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdC8vIGZpbG0gbm90IGNvbXBsZXRlbHkgY292ZXJlZCBpbiBsYW5kc2NhcGUgZm9ybWF0IChhc3BlY3QgPiAxKVxuXHRcdFx0cmV0dXJuIHRoaXMuZmlsbUdhdWdlIC8gTWF0aC5tYXgoIHRoaXMuYXNwZWN0LCAxICk7XG5cblx0XHR9LFxuXG5cdFx0LyoqXG5cdFx0ICogU2V0cyBhbiBvZmZzZXQgaW4gYSBsYXJnZXIgZnJ1c3R1bS4gVGhpcyBpcyB1c2VmdWwgZm9yIG11bHRpLXdpbmRvdyBvclxuXHRcdCAqIG11bHRpLW1vbml0b3IvbXVsdGktbWFjaGluZSBzZXR1cHMuXG5cdFx0ICpcblx0XHQgKiBGb3IgZXhhbXBsZSwgaWYgeW91IGhhdmUgM3gyIG1vbml0b3JzIGFuZCBlYWNoIG1vbml0b3IgaXMgMTkyMHgxMDgwIGFuZFxuXHRcdCAqIHRoZSBtb25pdG9ycyBhcmUgaW4gZ3JpZCBsaWtlIHRoaXNcblx0XHQgKlxuXHRcdCAqICAgKy0tLSstLS0rLS0tK1xuXHRcdCAqICAgfCBBIHwgQiB8IEMgfFxuXHRcdCAqICAgKy0tLSstLS0rLS0tK1xuXHRcdCAqICAgfCBEIHwgRSB8IEYgfFxuXHRcdCAqICAgKy0tLSstLS0rLS0tK1xuXHRcdCAqXG5cdFx0ICogdGhlbiBmb3IgZWFjaCBtb25pdG9yIHlvdSB3b3VsZCBjYWxsIGl0IGxpa2UgdGhpc1xuXHRcdCAqXG5cdFx0ICogICB2YXIgdyA9IDE5MjA7XG5cdFx0ICogICB2YXIgaCA9IDEwODA7XG5cdFx0ICogICB2YXIgZnVsbFdpZHRoID0gdyAqIDM7XG5cdFx0ICogICB2YXIgZnVsbEhlaWdodCA9IGggKiAyO1xuXHRcdCAqXG5cdFx0ICogICAtLUEtLVxuXHRcdCAqICAgY2FtZXJhLnNldE9mZnNldCggZnVsbFdpZHRoLCBmdWxsSGVpZ2h0LCB3ICogMCwgaCAqIDAsIHcsIGggKTtcblx0XHQgKiAgIC0tQi0tXG5cdFx0ICogICBjYW1lcmEuc2V0T2Zmc2V0KCBmdWxsV2lkdGgsIGZ1bGxIZWlnaHQsIHcgKiAxLCBoICogMCwgdywgaCApO1xuXHRcdCAqICAgLS1DLS1cblx0XHQgKiAgIGNhbWVyYS5zZXRPZmZzZXQoIGZ1bGxXaWR0aCwgZnVsbEhlaWdodCwgdyAqIDIsIGggKiAwLCB3LCBoICk7XG5cdFx0ICogICAtLUQtLVxuXHRcdCAqICAgY2FtZXJhLnNldE9mZnNldCggZnVsbFdpZHRoLCBmdWxsSGVpZ2h0LCB3ICogMCwgaCAqIDEsIHcsIGggKTtcblx0XHQgKiAgIC0tRS0tXG5cdFx0ICogICBjYW1lcmEuc2V0T2Zmc2V0KCBmdWxsV2lkdGgsIGZ1bGxIZWlnaHQsIHcgKiAxLCBoICogMSwgdywgaCApO1xuXHRcdCAqICAgLS1GLS1cblx0XHQgKiAgIGNhbWVyYS5zZXRPZmZzZXQoIGZ1bGxXaWR0aCwgZnVsbEhlaWdodCwgdyAqIDIsIGggKiAxLCB3LCBoICk7XG5cdFx0ICpcblx0XHQgKiAgIE5vdGUgdGhlcmUgaXMgbm8gcmVhc29uIG1vbml0b3JzIGhhdmUgdG8gYmUgdGhlIHNhbWUgc2l6ZSBvciBpbiBhIGdyaWQuXG5cdFx0ICovXG5cdFx0c2V0Vmlld09mZnNldDogZnVuY3Rpb24gKCBmdWxsV2lkdGgsIGZ1bGxIZWlnaHQsIHgsIHksIHdpZHRoLCBoZWlnaHQgKSB7XG5cblx0XHRcdHRoaXMuYXNwZWN0ID0gZnVsbFdpZHRoIC8gZnVsbEhlaWdodDtcblxuXHRcdFx0dGhpcy52aWV3ID0ge1xuXHRcdFx0XHRmdWxsV2lkdGg6IGZ1bGxXaWR0aCxcblx0XHRcdFx0ZnVsbEhlaWdodDogZnVsbEhlaWdodCxcblx0XHRcdFx0b2Zmc2V0WDogeCxcblx0XHRcdFx0b2Zmc2V0WTogeSxcblx0XHRcdFx0d2lkdGg6IHdpZHRoLFxuXHRcdFx0XHRoZWlnaHQ6IGhlaWdodFxuXHRcdFx0fTtcblxuXHRcdFx0dGhpcy51cGRhdGVQcm9qZWN0aW9uTWF0cml4KCk7XG5cblx0XHR9LFxuXG5cdFx0Y2xlYXJWaWV3T2Zmc2V0OiBmdW5jdGlvbigpIHtcblxuXHRcdFx0dGhpcy52aWV3ID0gbnVsbDtcblx0XHRcdHRoaXMudXBkYXRlUHJvamVjdGlvbk1hdHJpeCgpO1xuXG5cdFx0fSxcblxuXHRcdHVwZGF0ZVByb2plY3Rpb25NYXRyaXg6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIG5lYXIgPSB0aGlzLm5lYXIsXG5cdFx0XHRcdHRvcCA9IG5lYXIgKiBNYXRoLnRhbihcblx0XHRcdFx0XHRcdF9NYXRoLkRFRzJSQUQgKiAwLjUgKiB0aGlzLmZvdiApIC8gdGhpcy56b29tLFxuXHRcdFx0XHRoZWlnaHQgPSAyICogdG9wLFxuXHRcdFx0XHR3aWR0aCA9IHRoaXMuYXNwZWN0ICogaGVpZ2h0LFxuXHRcdFx0XHRsZWZ0ID0gLSAwLjUgKiB3aWR0aCxcblx0XHRcdFx0dmlldyA9IHRoaXMudmlldztcblxuXHRcdFx0aWYgKCB2aWV3ICE9PSBudWxsICkge1xuXG5cdFx0XHRcdHZhciBmdWxsV2lkdGggPSB2aWV3LmZ1bGxXaWR0aCxcblx0XHRcdFx0XHRmdWxsSGVpZ2h0ID0gdmlldy5mdWxsSGVpZ2h0O1xuXG5cdFx0XHRcdGxlZnQgKz0gdmlldy5vZmZzZXRYICogd2lkdGggLyBmdWxsV2lkdGg7XG5cdFx0XHRcdHRvcCAtPSB2aWV3Lm9mZnNldFkgKiBoZWlnaHQgLyBmdWxsSGVpZ2h0O1xuXHRcdFx0XHR3aWR0aCAqPSB2aWV3LndpZHRoIC8gZnVsbFdpZHRoO1xuXHRcdFx0XHRoZWlnaHQgKj0gdmlldy5oZWlnaHQgLyBmdWxsSGVpZ2h0O1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBza2V3ID0gdGhpcy5maWxtT2Zmc2V0O1xuXHRcdFx0aWYgKCBza2V3ICE9PSAwICkgbGVmdCArPSBuZWFyICogc2tldyAvIHRoaXMuZ2V0RmlsbVdpZHRoKCk7XG5cblx0XHRcdHRoaXMucHJvamVjdGlvbk1hdHJpeC5tYWtlRnJ1c3R1bShcblx0XHRcdFx0XHRsZWZ0LCBsZWZ0ICsgd2lkdGgsIHRvcCAtIGhlaWdodCwgdG9wLCBuZWFyLCB0aGlzLmZhciApO1xuXG5cdFx0fSxcblxuXHRcdHRvSlNPTjogZnVuY3Rpb24gKCBtZXRhICkge1xuXG5cdFx0XHR2YXIgZGF0YSA9IE9iamVjdDNELnByb3RvdHlwZS50b0pTT04uY2FsbCggdGhpcywgbWV0YSApO1xuXG5cdFx0XHRkYXRhLm9iamVjdC5mb3YgPSB0aGlzLmZvdjtcblx0XHRcdGRhdGEub2JqZWN0Lnpvb20gPSB0aGlzLnpvb207XG5cblx0XHRcdGRhdGEub2JqZWN0Lm5lYXIgPSB0aGlzLm5lYXI7XG5cdFx0XHRkYXRhLm9iamVjdC5mYXIgPSB0aGlzLmZhcjtcblx0XHRcdGRhdGEub2JqZWN0LmZvY3VzID0gdGhpcy5mb2N1cztcblxuXHRcdFx0ZGF0YS5vYmplY3QuYXNwZWN0ID0gdGhpcy5hc3BlY3Q7XG5cblx0XHRcdGlmICggdGhpcy52aWV3ICE9PSBudWxsICkgZGF0YS5vYmplY3QudmlldyA9IE9iamVjdC5hc3NpZ24oIHt9LCB0aGlzLnZpZXcgKTtcblxuXHRcdFx0ZGF0YS5vYmplY3QuZmlsbUdhdWdlID0gdGhpcy5maWxtR2F1Z2U7XG5cdFx0XHRkYXRhLm9iamVjdC5maWxtT2Zmc2V0ID0gdGhpcy5maWxtT2Zmc2V0O1xuXG5cdFx0XHRyZXR1cm4gZGF0YTtcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqIEBhdXRob3IgYXJvc2UgLyBodHRwOi8vZ2l0aHViLmNvbS9hcm9zZVxuXHQgKi9cblxuXHRmdW5jdGlvbiBPcnRob2dyYXBoaWNDYW1lcmEoIGxlZnQsIHJpZ2h0LCB0b3AsIGJvdHRvbSwgbmVhciwgZmFyICkge1xuXG5cdFx0Q2FtZXJhLmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdPcnRob2dyYXBoaWNDYW1lcmEnO1xuXG5cdFx0dGhpcy56b29tID0gMTtcblx0XHR0aGlzLnZpZXcgPSBudWxsO1xuXG5cdFx0dGhpcy5sZWZ0ID0gbGVmdDtcblx0XHR0aGlzLnJpZ2h0ID0gcmlnaHQ7XG5cdFx0dGhpcy50b3AgPSB0b3A7XG5cdFx0dGhpcy5ib3R0b20gPSBib3R0b207XG5cblx0XHR0aGlzLm5lYXIgPSAoIG5lYXIgIT09IHVuZGVmaW5lZCApID8gbmVhciA6IDAuMTtcblx0XHR0aGlzLmZhciA9ICggZmFyICE9PSB1bmRlZmluZWQgKSA/IGZhciA6IDIwMDA7XG5cblx0XHR0aGlzLnVwZGF0ZVByb2plY3Rpb25NYXRyaXgoKTtcblxuXHR9XG5cblx0T3J0aG9ncmFwaGljQ2FtZXJhLnByb3RvdHlwZSA9IE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIENhbWVyYS5wcm90b3R5cGUgKSwge1xuXG5cdFx0Y29uc3RydWN0b3I6IE9ydGhvZ3JhcGhpY0NhbWVyYSxcblxuXHRcdGlzT3J0aG9ncmFwaGljQ2FtZXJhOiB0cnVlLFxuXG5cdFx0Y29weTogZnVuY3Rpb24gKCBzb3VyY2UgKSB7XG5cblx0XHRcdENhbWVyYS5wcm90b3R5cGUuY29weS5jYWxsKCB0aGlzLCBzb3VyY2UgKTtcblxuXHRcdFx0dGhpcy5sZWZ0ID0gc291cmNlLmxlZnQ7XG5cdFx0XHR0aGlzLnJpZ2h0ID0gc291cmNlLnJpZ2h0O1xuXHRcdFx0dGhpcy50b3AgPSBzb3VyY2UudG9wO1xuXHRcdFx0dGhpcy5ib3R0b20gPSBzb3VyY2UuYm90dG9tO1xuXHRcdFx0dGhpcy5uZWFyID0gc291cmNlLm5lYXI7XG5cdFx0XHR0aGlzLmZhciA9IHNvdXJjZS5mYXI7XG5cblx0XHRcdHRoaXMuem9vbSA9IHNvdXJjZS56b29tO1xuXHRcdFx0dGhpcy52aWV3ID0gc291cmNlLnZpZXcgPT09IG51bGwgPyBudWxsIDogT2JqZWN0LmFzc2lnbigge30sIHNvdXJjZS52aWV3ICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldFZpZXdPZmZzZXQ6IGZ1bmN0aW9uKCBmdWxsV2lkdGgsIGZ1bGxIZWlnaHQsIHgsIHksIHdpZHRoLCBoZWlnaHQgKSB7XG5cblx0XHRcdHRoaXMudmlldyA9IHtcblx0XHRcdFx0ZnVsbFdpZHRoOiBmdWxsV2lkdGgsXG5cdFx0XHRcdGZ1bGxIZWlnaHQ6IGZ1bGxIZWlnaHQsXG5cdFx0XHRcdG9mZnNldFg6IHgsXG5cdFx0XHRcdG9mZnNldFk6IHksXG5cdFx0XHRcdHdpZHRoOiB3aWR0aCxcblx0XHRcdFx0aGVpZ2h0OiBoZWlnaHRcblx0XHRcdH07XG5cblx0XHRcdHRoaXMudXBkYXRlUHJvamVjdGlvbk1hdHJpeCgpO1xuXG5cdFx0fSxcblxuXHRcdGNsZWFyVmlld09mZnNldDogZnVuY3Rpb24oKSB7XG5cblx0XHRcdHRoaXMudmlldyA9IG51bGw7XG5cdFx0XHR0aGlzLnVwZGF0ZVByb2plY3Rpb25NYXRyaXgoKTtcblxuXHRcdH0sXG5cblx0XHR1cGRhdGVQcm9qZWN0aW9uTWF0cml4OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBkeCA9ICggdGhpcy5yaWdodCAtIHRoaXMubGVmdCApIC8gKCAyICogdGhpcy56b29tICk7XG5cdFx0XHR2YXIgZHkgPSAoIHRoaXMudG9wIC0gdGhpcy5ib3R0b20gKSAvICggMiAqIHRoaXMuem9vbSApO1xuXHRcdFx0dmFyIGN4ID0gKCB0aGlzLnJpZ2h0ICsgdGhpcy5sZWZ0ICkgLyAyO1xuXHRcdFx0dmFyIGN5ID0gKCB0aGlzLnRvcCArIHRoaXMuYm90dG9tICkgLyAyO1xuXG5cdFx0XHR2YXIgbGVmdCA9IGN4IC0gZHg7XG5cdFx0XHR2YXIgcmlnaHQgPSBjeCArIGR4O1xuXHRcdFx0dmFyIHRvcCA9IGN5ICsgZHk7XG5cdFx0XHR2YXIgYm90dG9tID0gY3kgLSBkeTtcblxuXHRcdFx0aWYgKCB0aGlzLnZpZXcgIT09IG51bGwgKSB7XG5cblx0XHRcdFx0dmFyIHpvb21XID0gdGhpcy56b29tIC8gKCB0aGlzLnZpZXcud2lkdGggLyB0aGlzLnZpZXcuZnVsbFdpZHRoICk7XG5cdFx0XHRcdHZhciB6b29tSCA9IHRoaXMuem9vbSAvICggdGhpcy52aWV3LmhlaWdodCAvIHRoaXMudmlldy5mdWxsSGVpZ2h0ICk7XG5cdFx0XHRcdHZhciBzY2FsZVcgPSAoIHRoaXMucmlnaHQgLSB0aGlzLmxlZnQgKSAvIHRoaXMudmlldy53aWR0aDtcblx0XHRcdFx0dmFyIHNjYWxlSCA9ICggdGhpcy50b3AgLSB0aGlzLmJvdHRvbSApIC8gdGhpcy52aWV3LmhlaWdodDtcblxuXHRcdFx0XHRsZWZ0ICs9IHNjYWxlVyAqICggdGhpcy52aWV3Lm9mZnNldFggLyB6b29tVyApO1xuXHRcdFx0XHRyaWdodCA9IGxlZnQgKyBzY2FsZVcgKiAoIHRoaXMudmlldy53aWR0aCAvIHpvb21XICk7XG5cdFx0XHRcdHRvcCAtPSBzY2FsZUggKiAoIHRoaXMudmlldy5vZmZzZXRZIC8gem9vbUggKTtcblx0XHRcdFx0Ym90dG9tID0gdG9wIC0gc2NhbGVIICogKCB0aGlzLnZpZXcuaGVpZ2h0IC8gem9vbUggKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLnByb2plY3Rpb25NYXRyaXgubWFrZU9ydGhvZ3JhcGhpYyggbGVmdCwgcmlnaHQsIHRvcCwgYm90dG9tLCB0aGlzLm5lYXIsIHRoaXMuZmFyICk7XG5cblx0XHR9LFxuXG5cdFx0dG9KU09OOiBmdW5jdGlvbiAoIG1ldGEgKSB7XG5cblx0XHRcdHZhciBkYXRhID0gT2JqZWN0M0QucHJvdG90eXBlLnRvSlNPTi5jYWxsKCB0aGlzLCBtZXRhICk7XG5cblx0XHRcdGRhdGEub2JqZWN0Lnpvb20gPSB0aGlzLnpvb207XG5cdFx0XHRkYXRhLm9iamVjdC5sZWZ0ID0gdGhpcy5sZWZ0O1xuXHRcdFx0ZGF0YS5vYmplY3QucmlnaHQgPSB0aGlzLnJpZ2h0O1xuXHRcdFx0ZGF0YS5vYmplY3QudG9wID0gdGhpcy50b3A7XG5cdFx0XHRkYXRhLm9iamVjdC5ib3R0b20gPSB0aGlzLmJvdHRvbTtcblx0XHRcdGRhdGEub2JqZWN0Lm5lYXIgPSB0aGlzLm5lYXI7XG5cdFx0XHRkYXRhLm9iamVjdC5mYXIgPSB0aGlzLmZhcjtcblxuXHRcdFx0aWYgKCB0aGlzLnZpZXcgIT09IG51bGwgKSBkYXRhLm9iamVjdC52aWV3ID0gT2JqZWN0LmFzc2lnbigge30sIHRoaXMudmlldyApO1xuXG5cdFx0XHRyZXR1cm4gZGF0YTtcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFdlYkdMSW5kZXhlZEJ1ZmZlclJlbmRlcmVyKCBnbCwgZXh0ZW5zaW9ucywgaW5mb1JlbmRlciApIHtcblxuXHRcdHZhciBtb2RlO1xuXG5cdFx0ZnVuY3Rpb24gc2V0TW9kZSggdmFsdWUgKSB7XG5cblx0XHRcdG1vZGUgPSB2YWx1ZTtcblxuXHRcdH1cblxuXHRcdHZhciB0eXBlLCBzaXplO1xuXG5cdFx0ZnVuY3Rpb24gc2V0SW5kZXgoIGluZGV4ICkge1xuXG5cdFx0XHRpZiAoIGluZGV4LmFycmF5IGluc3RhbmNlb2YgVWludDMyQXJyYXkgJiYgZXh0ZW5zaW9ucy5nZXQoICdPRVNfZWxlbWVudF9pbmRleF91aW50JyApICkge1xuXG5cdFx0XHRcdHR5cGUgPSBnbC5VTlNJR05FRF9JTlQ7XG5cdFx0XHRcdHNpemUgPSA0O1xuXG5cdFx0XHR9IGVsc2UgaWYgKCBpbmRleC5hcnJheSBpbnN0YW5jZW9mIFVpbnQxNkFycmF5ICkge1xuXG5cdFx0XHRcdHR5cGUgPSBnbC5VTlNJR05FRF9TSE9SVDtcblx0XHRcdFx0c2l6ZSA9IDI7XG5cblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFxuXHRcdFx0XHR0eXBlID0gZ2wuVU5TSUdORURfQllURTtcblx0XHRcdFx0c2l6ZSA9IDE7XG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiByZW5kZXIoIHN0YXJ0LCBjb3VudCApIHtcblxuXHRcdFx0Z2wuZHJhd0VsZW1lbnRzKCBtb2RlLCBjb3VudCwgdHlwZSwgc3RhcnQgKiBzaXplICk7XG5cblx0XHRcdGluZm9SZW5kZXIuY2FsbHMgKys7XG5cdFx0XHRpbmZvUmVuZGVyLnZlcnRpY2VzICs9IGNvdW50O1xuXG5cdFx0XHRpZiAoIG1vZGUgPT09IGdsLlRSSUFOR0xFUyApIGluZm9SZW5kZXIuZmFjZXMgKz0gY291bnQgLyAzO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gcmVuZGVySW5zdGFuY2VzKCBnZW9tZXRyeSwgc3RhcnQsIGNvdW50ICkge1xuXG5cdFx0XHR2YXIgZXh0ZW5zaW9uID0gZXh0ZW5zaW9ucy5nZXQoICdBTkdMRV9pbnN0YW5jZWRfYXJyYXlzJyApO1xuXG5cdFx0XHRpZiAoIGV4dGVuc2lvbiA9PT0gbnVsbCApIHtcblxuXHRcdFx0XHRjb25zb2xlLmVycm9yKCAnVEhSRUUuV2ViR0xCdWZmZXJSZW5kZXJlcjogdXNpbmcgVEhSRUUuSW5zdGFuY2VkQnVmZmVyR2VvbWV0cnkgYnV0IGhhcmR3YXJlIGRvZXMgbm90IHN1cHBvcnQgZXh0ZW5zaW9uIEFOR0xFX2luc3RhbmNlZF9hcnJheXMuJyApO1xuXHRcdFx0XHRyZXR1cm47XG5cblx0XHRcdH1cblxuXHRcdFx0ZXh0ZW5zaW9uLmRyYXdFbGVtZW50c0luc3RhbmNlZEFOR0xFKCBtb2RlLCBjb3VudCwgdHlwZSwgc3RhcnQgKiBzaXplLCBnZW9tZXRyeS5tYXhJbnN0YW5jZWRDb3VudCApO1xuXG5cdFx0XHRpbmZvUmVuZGVyLmNhbGxzICsrO1xuXHRcdFx0aW5mb1JlbmRlci52ZXJ0aWNlcyArPSBjb3VudCAqIGdlb21ldHJ5Lm1heEluc3RhbmNlZENvdW50O1xuXG5cdFx0XHRpZiAoIG1vZGUgPT09IGdsLlRSSUFOR0xFUyApIGluZm9SZW5kZXIuZmFjZXMgKz0gZ2VvbWV0cnkubWF4SW5zdGFuY2VkQ291bnQgKiBjb3VudCAvIDM7XG5cblx0XHR9XG5cblx0XHRyZXR1cm4ge1xuXG5cdFx0XHRzZXRNb2RlOiBzZXRNb2RlLFxuXHRcdFx0c2V0SW5kZXg6IHNldEluZGV4LFxuXHRcdFx0cmVuZGVyOiByZW5kZXIsXG5cdFx0XHRyZW5kZXJJbnN0YW5jZXM6IHJlbmRlckluc3RhbmNlc1xuXG5cdFx0fTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFdlYkdMQnVmZmVyUmVuZGVyZXIoIGdsLCBleHRlbnNpb25zLCBpbmZvUmVuZGVyICkge1xuXG5cdFx0dmFyIG1vZGU7XG5cblx0XHRmdW5jdGlvbiBzZXRNb2RlKCB2YWx1ZSApIHtcblxuXHRcdFx0bW9kZSA9IHZhbHVlO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gcmVuZGVyKCBzdGFydCwgY291bnQgKSB7XG5cblx0XHRcdGdsLmRyYXdBcnJheXMoIG1vZGUsIHN0YXJ0LCBjb3VudCApO1xuXG5cdFx0XHRpbmZvUmVuZGVyLmNhbGxzICsrO1xuXHRcdFx0aW5mb1JlbmRlci52ZXJ0aWNlcyArPSBjb3VudDtcblxuXHRcdFx0aWYgKCBtb2RlID09PSBnbC5UUklBTkdMRVMgKSBpbmZvUmVuZGVyLmZhY2VzICs9IGNvdW50IC8gMztcblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHJlbmRlckluc3RhbmNlcyggZ2VvbWV0cnkgKSB7XG5cblx0XHRcdHZhciBleHRlbnNpb24gPSBleHRlbnNpb25zLmdldCggJ0FOR0xFX2luc3RhbmNlZF9hcnJheXMnICk7XG5cblx0XHRcdGlmICggZXh0ZW5zaW9uID09PSBudWxsICkge1xuXG5cdFx0XHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5XZWJHTEJ1ZmZlclJlbmRlcmVyOiB1c2luZyBUSFJFRS5JbnN0YW5jZWRCdWZmZXJHZW9tZXRyeSBidXQgaGFyZHdhcmUgZG9lcyBub3Qgc3VwcG9ydCBleHRlbnNpb24gQU5HTEVfaW5zdGFuY2VkX2FycmF5cy4nICk7XG5cdFx0XHRcdHJldHVybjtcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgcG9zaXRpb24gPSBnZW9tZXRyeS5hdHRyaWJ1dGVzLnBvc2l0aW9uO1xuXG5cdFx0XHR2YXIgY291bnQgPSAwO1xuXG5cdFx0XHRpZiAoIHBvc2l0aW9uLmlzSW50ZXJsZWF2ZWRCdWZmZXJBdHRyaWJ1dGUgKSB7XG5cblx0XHRcdFx0Y291bnQgPSBwb3NpdGlvbi5kYXRhLmNvdW50O1xuXG5cdFx0XHRcdGV4dGVuc2lvbi5kcmF3QXJyYXlzSW5zdGFuY2VkQU5HTEUoIG1vZGUsIDAsIGNvdW50LCBnZW9tZXRyeS5tYXhJbnN0YW5jZWRDb3VudCApO1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdGNvdW50ID0gcG9zaXRpb24uY291bnQ7XG5cblx0XHRcdFx0ZXh0ZW5zaW9uLmRyYXdBcnJheXNJbnN0YW5jZWRBTkdMRSggbW9kZSwgMCwgY291bnQsIGdlb21ldHJ5Lm1heEluc3RhbmNlZENvdW50ICk7XG5cblx0XHRcdH1cblxuXHRcdFx0aW5mb1JlbmRlci5jYWxscyArKztcblx0XHRcdGluZm9SZW5kZXIudmVydGljZXMgKz0gY291bnQgKiBnZW9tZXRyeS5tYXhJbnN0YW5jZWRDb3VudDtcblxuXHRcdFx0aWYgKCBtb2RlID09PSBnbC5UUklBTkdMRVMgKSBpbmZvUmVuZGVyLmZhY2VzICs9IGdlb21ldHJ5Lm1heEluc3RhbmNlZENvdW50ICogY291bnQgLyAzO1xuXG5cdFx0fVxuXG5cdFx0cmV0dXJuIHtcblx0XHRcdHNldE1vZGU6IHNldE1vZGUsXG5cdFx0XHRyZW5kZXI6IHJlbmRlcixcblx0XHRcdHJlbmRlckluc3RhbmNlczogcmVuZGVySW5zdGFuY2VzXG5cdFx0fTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFdlYkdMTGlnaHRzKCkge1xuXG5cdFx0dmFyIGxpZ2h0cyA9IHt9O1xuXG5cdFx0cmV0dXJuIHtcblxuXHRcdFx0Z2V0OiBmdW5jdGlvbiAoIGxpZ2h0ICkge1xuXG5cdFx0XHRcdGlmICggbGlnaHRzWyBsaWdodC5pZCBdICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRyZXR1cm4gbGlnaHRzWyBsaWdodC5pZCBdO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR2YXIgdW5pZm9ybXM7XG5cblx0XHRcdFx0c3dpdGNoICggbGlnaHQudHlwZSApIHtcblxuXHRcdFx0XHRcdGNhc2UgJ0RpcmVjdGlvbmFsTGlnaHQnOlxuXHRcdFx0XHRcdFx0dW5pZm9ybXMgPSB7XG5cdFx0XHRcdFx0XHRcdGRpcmVjdGlvbjogbmV3IFZlY3RvcjMoKSxcblx0XHRcdFx0XHRcdFx0Y29sb3I6IG5ldyBDb2xvcigpLFxuXG5cdFx0XHRcdFx0XHRcdHNoYWRvdzogZmFsc2UsXG5cdFx0XHRcdFx0XHRcdHNoYWRvd0JpYXM6IDAsXG5cdFx0XHRcdFx0XHRcdHNoYWRvd1JhZGl1czogMSxcblx0XHRcdFx0XHRcdFx0c2hhZG93TWFwU2l6ZTogbmV3IFZlY3RvcjIoKVxuXHRcdFx0XHRcdFx0fTtcblx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0Y2FzZSAnU3BvdExpZ2h0Jzpcblx0XHRcdFx0XHRcdHVuaWZvcm1zID0ge1xuXHRcdFx0XHRcdFx0XHRwb3NpdGlvbjogbmV3IFZlY3RvcjMoKSxcblx0XHRcdFx0XHRcdFx0ZGlyZWN0aW9uOiBuZXcgVmVjdG9yMygpLFxuXHRcdFx0XHRcdFx0XHRjb2xvcjogbmV3IENvbG9yKCksXG5cdFx0XHRcdFx0XHRcdGRpc3RhbmNlOiAwLFxuXHRcdFx0XHRcdFx0XHRjb25lQ29zOiAwLFxuXHRcdFx0XHRcdFx0XHRwZW51bWJyYUNvczogMCxcblx0XHRcdFx0XHRcdFx0ZGVjYXk6IDAsXG5cblx0XHRcdFx0XHRcdFx0c2hhZG93OiBmYWxzZSxcblx0XHRcdFx0XHRcdFx0c2hhZG93QmlhczogMCxcblx0XHRcdFx0XHRcdFx0c2hhZG93UmFkaXVzOiAxLFxuXHRcdFx0XHRcdFx0XHRzaGFkb3dNYXBTaXplOiBuZXcgVmVjdG9yMigpXG5cdFx0XHRcdFx0XHR9O1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRjYXNlICdQb2ludExpZ2h0Jzpcblx0XHRcdFx0XHRcdHVuaWZvcm1zID0ge1xuXHRcdFx0XHRcdFx0XHRwb3NpdGlvbjogbmV3IFZlY3RvcjMoKSxcblx0XHRcdFx0XHRcdFx0Y29sb3I6IG5ldyBDb2xvcigpLFxuXHRcdFx0XHRcdFx0XHRkaXN0YW5jZTogMCxcblx0XHRcdFx0XHRcdFx0ZGVjYXk6IDAsXG5cblx0XHRcdFx0XHRcdFx0c2hhZG93OiBmYWxzZSxcblx0XHRcdFx0XHRcdFx0c2hhZG93QmlhczogMCxcblx0XHRcdFx0XHRcdFx0c2hhZG93UmFkaXVzOiAxLFxuXHRcdFx0XHRcdFx0XHRzaGFkb3dNYXBTaXplOiBuZXcgVmVjdG9yMigpXG5cdFx0XHRcdFx0XHR9O1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRjYXNlICdIZW1pc3BoZXJlTGlnaHQnOlxuXHRcdFx0XHRcdFx0dW5pZm9ybXMgPSB7XG5cdFx0XHRcdFx0XHRcdGRpcmVjdGlvbjogbmV3IFZlY3RvcjMoKSxcblx0XHRcdFx0XHRcdFx0c2t5Q29sb3I6IG5ldyBDb2xvcigpLFxuXHRcdFx0XHRcdFx0XHRncm91bmRDb2xvcjogbmV3IENvbG9yKClcblx0XHRcdFx0XHRcdH07XG5cdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdGNhc2UgJ1JlY3RBcmVhTGlnaHQnOlxuXHRcdFx0XHRcdFx0dW5pZm9ybXMgPSB7XG5cdFx0XHRcdFx0XHRcdGNvbG9yOiBuZXcgQ29sb3IoKSxcblx0XHRcdFx0XHRcdFx0cG9zaXRpb246IG5ldyBWZWN0b3IzKCksXG5cdFx0XHRcdFx0XHRcdGhhbGZXaWR0aDogbmV3IFZlY3RvcjMoKSxcblx0XHRcdFx0XHRcdFx0aGFsZkhlaWdodDogbmV3IFZlY3RvcjMoKVxuXHRcdFx0XHRcdFx0XHQvLyBUT0RPIChhYmVsbmF0aW9uKTogc2V0IFJlY3RBcmVhTGlnaHQgc2hhZG93IHVuaWZvcm1zXG5cdFx0XHRcdFx0XHR9O1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGxpZ2h0c1sgbGlnaHQuaWQgXSA9IHVuaWZvcm1zO1xuXG5cdFx0XHRcdHJldHVybiB1bmlmb3JtcztcblxuXHRcdFx0fVxuXG5cdFx0fTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIGFkZExpbmVOdW1iZXJzKCBzdHJpbmcgKSB7XG5cblx0XHR2YXIgbGluZXMgPSBzdHJpbmcuc3BsaXQoICdcXG4nICk7XG5cblx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBsaW5lcy5sZW5ndGg7IGkgKysgKSB7XG5cblx0XHRcdGxpbmVzWyBpIF0gPSAoIGkgKyAxICkgKyAnOiAnICsgbGluZXNbIGkgXTtcblxuXHRcdH1cblxuXHRcdHJldHVybiBsaW5lcy5qb2luKCAnXFxuJyApO1xuXG5cdH1cblxuXHRmdW5jdGlvbiBXZWJHTFNoYWRlciggZ2wsIHR5cGUsIHN0cmluZyApIHtcblxuXHRcdHZhciBzaGFkZXIgPSBnbC5jcmVhdGVTaGFkZXIoIHR5cGUgKTtcblxuXHRcdGdsLnNoYWRlclNvdXJjZSggc2hhZGVyLCBzdHJpbmcgKTtcblx0XHRnbC5jb21waWxlU2hhZGVyKCBzaGFkZXIgKTtcblxuXHRcdGlmICggZ2wuZ2V0U2hhZGVyUGFyYW1ldGVyKCBzaGFkZXIsIGdsLkNPTVBJTEVfU1RBVFVTICkgPT09IGZhbHNlICkge1xuXG5cdFx0XHRjb25zb2xlLmVycm9yKCAnVEhSRUUuV2ViR0xTaGFkZXI6IFNoYWRlciBjb3VsZG5cXCd0IGNvbXBpbGUuJyApO1xuXG5cdFx0fVxuXG5cdFx0aWYgKCBnbC5nZXRTaGFkZXJJbmZvTG9nKCBzaGFkZXIgKSAhPT0gJycgKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMU2hhZGVyOiBnbC5nZXRTaGFkZXJJbmZvTG9nKCknLCB0eXBlID09PSBnbC5WRVJURVhfU0hBREVSID8gJ3ZlcnRleCcgOiAnZnJhZ21lbnQnLCBnbC5nZXRTaGFkZXJJbmZvTG9nKCBzaGFkZXIgKSwgYWRkTGluZU51bWJlcnMoIHN0cmluZyApICk7XG5cblx0XHR9XG5cblx0XHQvLyAtLWVuYWJsZS1wcml2aWxlZ2VkLXdlYmdsLWV4dGVuc2lvblxuXHRcdC8vIGNvbnNvbGUubG9nKCB0eXBlLCBnbC5nZXRFeHRlbnNpb24oICdXRUJHTF9kZWJ1Z19zaGFkZXJzJyApLmdldFRyYW5zbGF0ZWRTaGFkZXJTb3VyY2UoIHNoYWRlciApICk7XG5cblx0XHRyZXR1cm4gc2hhZGVyO1xuXG5cdH1cblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICovXG5cblx0dmFyIHByb2dyYW1JZENvdW50ID0gMDtcblxuXHRmdW5jdGlvbiBnZXRFbmNvZGluZ0NvbXBvbmVudHMoIGVuY29kaW5nICkge1xuXG5cdFx0c3dpdGNoICggZW5jb2RpbmcgKSB7XG5cblx0XHRcdGNhc2UgTGluZWFyRW5jb2Rpbmc6XG5cdFx0XHRcdHJldHVybiBbICdMaW5lYXInLCcoIHZhbHVlICknIF07XG5cdFx0XHRjYXNlIHNSR0JFbmNvZGluZzpcblx0XHRcdFx0cmV0dXJuIFsgJ3NSR0InLCcoIHZhbHVlICknIF07XG5cdFx0XHRjYXNlIFJHQkVFbmNvZGluZzpcblx0XHRcdFx0cmV0dXJuIFsgJ1JHQkUnLCcoIHZhbHVlICknIF07XG5cdFx0XHRjYXNlIFJHQk03RW5jb2Rpbmc6XG5cdFx0XHRcdHJldHVybiBbICdSR0JNJywnKCB2YWx1ZSwgNy4wICknIF07XG5cdFx0XHRjYXNlIFJHQk0xNkVuY29kaW5nOlxuXHRcdFx0XHRyZXR1cm4gWyAnUkdCTScsJyggdmFsdWUsIDE2LjAgKScgXTtcblx0XHRcdGNhc2UgUkdCREVuY29kaW5nOlxuXHRcdFx0XHRyZXR1cm4gWyAnUkdCRCcsJyggdmFsdWUsIDI1Ni4wICknIF07XG5cdFx0XHRjYXNlIEdhbW1hRW5jb2Rpbmc6XG5cdFx0XHRcdHJldHVybiBbICdHYW1tYScsJyggdmFsdWUsIGZsb2F0KCBHQU1NQV9GQUNUT1IgKSApJyBdO1xuXHRcdFx0ZGVmYXVsdDpcblx0XHRcdFx0dGhyb3cgbmV3IEVycm9yKCAndW5zdXBwb3J0ZWQgZW5jb2Rpbmc6ICcgKyBlbmNvZGluZyApO1xuXG5cdFx0fVxuXG5cdH1cblxuXHRmdW5jdGlvbiBnZXRUZXhlbERlY29kaW5nRnVuY3Rpb24oIGZ1bmN0aW9uTmFtZSwgZW5jb2RpbmcgKSB7XG5cblx0XHR2YXIgY29tcG9uZW50cyA9IGdldEVuY29kaW5nQ29tcG9uZW50cyggZW5jb2RpbmcgKTtcblx0XHRyZXR1cm4gXCJ2ZWM0IFwiICsgZnVuY3Rpb25OYW1lICsgXCIoIHZlYzQgdmFsdWUgKSB7IHJldHVybiBcIiArIGNvbXBvbmVudHNbIDAgXSArIFwiVG9MaW5lYXJcIiArIGNvbXBvbmVudHNbIDEgXSArIFwiOyB9XCI7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIGdldFRleGVsRW5jb2RpbmdGdW5jdGlvbiggZnVuY3Rpb25OYW1lLCBlbmNvZGluZyApIHtcblxuXHRcdHZhciBjb21wb25lbnRzID0gZ2V0RW5jb2RpbmdDb21wb25lbnRzKCBlbmNvZGluZyApO1xuXHRcdHJldHVybiBcInZlYzQgXCIgKyBmdW5jdGlvbk5hbWUgKyBcIiggdmVjNCB2YWx1ZSApIHsgcmV0dXJuIExpbmVhclRvXCIgKyBjb21wb25lbnRzWyAwIF0gKyBjb21wb25lbnRzWyAxIF0gKyBcIjsgfVwiO1xuXG5cdH1cblxuXHRmdW5jdGlvbiBnZXRUb25lTWFwcGluZ0Z1bmN0aW9uKCBmdW5jdGlvbk5hbWUsIHRvbmVNYXBwaW5nICkge1xuXG5cdFx0dmFyIHRvbmVNYXBwaW5nTmFtZTtcblxuXHRcdHN3aXRjaCAoIHRvbmVNYXBwaW5nICkge1xuXG5cdFx0XHRjYXNlIExpbmVhclRvbmVNYXBwaW5nOlxuXHRcdFx0XHR0b25lTWFwcGluZ05hbWUgPSBcIkxpbmVhclwiO1xuXHRcdFx0XHRicmVhaztcblxuXHRcdFx0Y2FzZSBSZWluaGFyZFRvbmVNYXBwaW5nOlxuXHRcdFx0XHR0b25lTWFwcGluZ05hbWUgPSBcIlJlaW5oYXJkXCI7XG5cdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRjYXNlIFVuY2hhcnRlZDJUb25lTWFwcGluZzpcblx0XHRcdFx0dG9uZU1hcHBpbmdOYW1lID0gXCJVbmNoYXJ0ZWQyXCI7XG5cdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRjYXNlIENpbmVvblRvbmVNYXBwaW5nOlxuXHRcdFx0XHR0b25lTWFwcGluZ05hbWUgPSBcIk9wdGltaXplZENpbmVvblwiO1xuXHRcdFx0XHRicmVhaztcblxuXHRcdFx0ZGVmYXVsdDpcblx0XHRcdFx0dGhyb3cgbmV3IEVycm9yKCAndW5zdXBwb3J0ZWQgdG9uZU1hcHBpbmc6ICcgKyB0b25lTWFwcGluZyApO1xuXG5cdFx0fVxuXG5cdFx0cmV0dXJuIFwidmVjMyBcIiArIGZ1bmN0aW9uTmFtZSArIFwiKCB2ZWMzIGNvbG9yICkgeyByZXR1cm4gXCIgKyB0b25lTWFwcGluZ05hbWUgKyBcIlRvbmVNYXBwaW5nKCBjb2xvciApOyB9XCI7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIGdlbmVyYXRlRXh0ZW5zaW9ucyggZXh0ZW5zaW9ucywgcGFyYW1ldGVycywgcmVuZGVyZXJFeHRlbnNpb25zICkge1xuXG5cdFx0ZXh0ZW5zaW9ucyA9IGV4dGVuc2lvbnMgfHwge307XG5cblx0XHR2YXIgY2h1bmtzID0gW1xuXHRcdFx0KCBleHRlbnNpb25zLmRlcml2YXRpdmVzIHx8IHBhcmFtZXRlcnMuZW52TWFwQ3ViZVVWIHx8IHBhcmFtZXRlcnMuYnVtcE1hcCB8fCBwYXJhbWV0ZXJzLm5vcm1hbE1hcCB8fCBwYXJhbWV0ZXJzLmZsYXRTaGFkaW5nICkgPyAnI2V4dGVuc2lvbiBHTF9PRVNfc3RhbmRhcmRfZGVyaXZhdGl2ZXMgOiBlbmFibGUnIDogJycsXG5cdFx0XHQoIGV4dGVuc2lvbnMuZnJhZ0RlcHRoIHx8IHBhcmFtZXRlcnMubG9nYXJpdGhtaWNEZXB0aEJ1ZmZlciApICYmIHJlbmRlcmVyRXh0ZW5zaW9ucy5nZXQoICdFWFRfZnJhZ19kZXB0aCcgKSA/ICcjZXh0ZW5zaW9uIEdMX0VYVF9mcmFnX2RlcHRoIDogZW5hYmxlJyA6ICcnLFxuXHRcdFx0KCBleHRlbnNpb25zLmRyYXdCdWZmZXJzICkgJiYgcmVuZGVyZXJFeHRlbnNpb25zLmdldCggJ1dFQkdMX2RyYXdfYnVmZmVycycgKSA/ICcjZXh0ZW5zaW9uIEdMX0VYVF9kcmF3X2J1ZmZlcnMgOiByZXF1aXJlJyA6ICcnLFxuXHRcdFx0KCBleHRlbnNpb25zLnNoYWRlclRleHR1cmVMT0QgfHwgcGFyYW1ldGVycy5lbnZNYXAgKSAmJiByZW5kZXJlckV4dGVuc2lvbnMuZ2V0KCAnRVhUX3NoYWRlcl90ZXh0dXJlX2xvZCcgKSA/ICcjZXh0ZW5zaW9uIEdMX0VYVF9zaGFkZXJfdGV4dHVyZV9sb2QgOiBlbmFibGUnIDogJydcblx0XHRdO1xuXG5cdFx0cmV0dXJuIGNodW5rcy5maWx0ZXIoIGZpbHRlckVtcHR5TGluZSApLmpvaW4oICdcXG4nICk7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIGdlbmVyYXRlRGVmaW5lcyggZGVmaW5lcyApIHtcblxuXHRcdHZhciBjaHVua3MgPSBbXTtcblxuXHRcdGZvciAoIHZhciBuYW1lIGluIGRlZmluZXMgKSB7XG5cblx0XHRcdHZhciB2YWx1ZSA9IGRlZmluZXNbIG5hbWUgXTtcblxuXHRcdFx0aWYgKCB2YWx1ZSA9PT0gZmFsc2UgKSBjb250aW51ZTtcblxuXHRcdFx0Y2h1bmtzLnB1c2goICcjZGVmaW5lICcgKyBuYW1lICsgJyAnICsgdmFsdWUgKTtcblxuXHRcdH1cblxuXHRcdHJldHVybiBjaHVua3Muam9pbiggJ1xcbicgKTtcblxuXHR9XG5cblx0ZnVuY3Rpb24gZmV0Y2hBdHRyaWJ1dGVMb2NhdGlvbnMoIGdsLCBwcm9ncmFtLCBpZGVudGlmaWVycyApIHtcblxuXHRcdHZhciBhdHRyaWJ1dGVzID0ge307XG5cblx0XHR2YXIgbiA9IGdsLmdldFByb2dyYW1QYXJhbWV0ZXIoIHByb2dyYW0sIGdsLkFDVElWRV9BVFRSSUJVVEVTICk7XG5cblx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBuOyBpICsrICkge1xuXG5cdFx0XHR2YXIgaW5mbyA9IGdsLmdldEFjdGl2ZUF0dHJpYiggcHJvZ3JhbSwgaSApO1xuXHRcdFx0dmFyIG5hbWUgPSBpbmZvLm5hbWU7XG5cblx0XHRcdC8vIGNvbnNvbGUubG9nKFwiVEhSRUUuV2ViR0xQcm9ncmFtOiBBQ1RJVkUgVkVSVEVYIEFUVFJJQlVURTpcIiwgbmFtZSwgaSApO1xuXG5cdFx0XHRhdHRyaWJ1dGVzWyBuYW1lIF0gPSBnbC5nZXRBdHRyaWJMb2NhdGlvbiggcHJvZ3JhbSwgbmFtZSApO1xuXG5cdFx0fVxuXG5cdFx0cmV0dXJuIGF0dHJpYnV0ZXM7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIGZpbHRlckVtcHR5TGluZSggc3RyaW5nICkge1xuXG5cdFx0cmV0dXJuIHN0cmluZyAhPT0gJyc7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIHJlcGxhY2VMaWdodE51bXMoIHN0cmluZywgcGFyYW1ldGVycyApIHtcblxuXHRcdHJldHVybiBzdHJpbmdcblx0XHRcdC5yZXBsYWNlKCAvTlVNX0RJUl9MSUdIVFMvZywgcGFyYW1ldGVycy5udW1EaXJMaWdodHMgKVxuXHRcdFx0LnJlcGxhY2UoIC9OVU1fU1BPVF9MSUdIVFMvZywgcGFyYW1ldGVycy5udW1TcG90TGlnaHRzIClcblx0XHRcdC5yZXBsYWNlKCAvTlVNX1JFQ1RfQVJFQV9MSUdIVFMvZywgcGFyYW1ldGVycy5udW1SZWN0QXJlYUxpZ2h0cyApXG5cdFx0XHQucmVwbGFjZSggL05VTV9QT0lOVF9MSUdIVFMvZywgcGFyYW1ldGVycy5udW1Qb2ludExpZ2h0cyApXG5cdFx0XHQucmVwbGFjZSggL05VTV9IRU1JX0xJR0hUUy9nLCBwYXJhbWV0ZXJzLm51bUhlbWlMaWdodHMgKTtcblxuXHR9XG5cblx0ZnVuY3Rpb24gcGFyc2VJbmNsdWRlcyggc3RyaW5nICkge1xuXG5cdFx0dmFyIHBhdHRlcm4gPSAvI2luY2x1ZGUgKzwoW1xcd1xcZC5dKyk+L2c7XG5cblx0XHRmdW5jdGlvbiByZXBsYWNlKCBtYXRjaCwgaW5jbHVkZSApIHtcblxuXHRcdFx0dmFyIHJlcGxhY2UgPSBTaGFkZXJDaHVua1sgaW5jbHVkZSBdO1xuXG5cdFx0XHRpZiAoIHJlcGxhY2UgPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHR0aHJvdyBuZXcgRXJyb3IoICdDYW4gbm90IHJlc29sdmUgI2luY2x1ZGUgPCcgKyBpbmNsdWRlICsgJz4nICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHBhcnNlSW5jbHVkZXMoIHJlcGxhY2UgKTtcblxuXHRcdH1cblxuXHRcdHJldHVybiBzdHJpbmcucmVwbGFjZSggcGF0dGVybiwgcmVwbGFjZSApO1xuXG5cdH1cblxuXHRmdW5jdGlvbiB1bnJvbGxMb29wcyggc3RyaW5nICkge1xuXG5cdFx0dmFyIHBhdHRlcm4gPSAvZm9yIFxcKCBpbnQgaSBcXD0gKFxcZCspXFw7IGkgPCAoXFxkKylcXDsgaSBcXCtcXCsgXFwpIFxceyhbXFxzXFxTXSs/KSg/PVxcfSlcXH0vZztcblxuXHRcdGZ1bmN0aW9uIHJlcGxhY2UoIG1hdGNoLCBzdGFydCwgZW5kLCBzbmlwcGV0ICkge1xuXG5cdFx0XHR2YXIgdW5yb2xsID0gJyc7XG5cblx0XHRcdGZvciAoIHZhciBpID0gcGFyc2VJbnQoIHN0YXJ0ICk7IGkgPCBwYXJzZUludCggZW5kICk7IGkgKysgKSB7XG5cblx0XHRcdFx0dW5yb2xsICs9IHNuaXBwZXQucmVwbGFjZSggL1xcWyBpIFxcXS9nLCAnWyAnICsgaSArICcgXScgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdW5yb2xsO1xuXG5cdFx0fVxuXG5cdFx0cmV0dXJuIHN0cmluZy5yZXBsYWNlKCBwYXR0ZXJuLCByZXBsYWNlICk7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIFdlYkdMUHJvZ3JhbSggcmVuZGVyZXIsIGNvZGUsIG1hdGVyaWFsLCBwYXJhbWV0ZXJzICkge1xuXG5cdFx0dmFyIGdsID0gcmVuZGVyZXIuY29udGV4dDtcblxuXHRcdHZhciBleHRlbnNpb25zID0gbWF0ZXJpYWwuZXh0ZW5zaW9ucztcblx0XHR2YXIgZGVmaW5lcyA9IG1hdGVyaWFsLmRlZmluZXM7XG5cblx0XHR2YXIgdmVydGV4U2hhZGVyID0gbWF0ZXJpYWwuX193ZWJnbFNoYWRlci52ZXJ0ZXhTaGFkZXI7XG5cdFx0dmFyIGZyYWdtZW50U2hhZGVyID0gbWF0ZXJpYWwuX193ZWJnbFNoYWRlci5mcmFnbWVudFNoYWRlcjtcblxuXHRcdHZhciBzaGFkb3dNYXBUeXBlRGVmaW5lID0gJ1NIQURPV01BUF9UWVBFX0JBU0lDJztcblxuXHRcdGlmICggcGFyYW1ldGVycy5zaGFkb3dNYXBUeXBlID09PSBQQ0ZTaGFkb3dNYXAgKSB7XG5cblx0XHRcdHNoYWRvd01hcFR5cGVEZWZpbmUgPSAnU0hBRE9XTUFQX1RZUEVfUENGJztcblxuXHRcdH0gZWxzZSBpZiAoIHBhcmFtZXRlcnMuc2hhZG93TWFwVHlwZSA9PT0gUENGU29mdFNoYWRvd01hcCApIHtcblxuXHRcdFx0c2hhZG93TWFwVHlwZURlZmluZSA9ICdTSEFET1dNQVBfVFlQRV9QQ0ZfU09GVCc7XG5cblx0XHR9XG5cblx0XHR2YXIgZW52TWFwVHlwZURlZmluZSA9ICdFTlZNQVBfVFlQRV9DVUJFJztcblx0XHR2YXIgZW52TWFwTW9kZURlZmluZSA9ICdFTlZNQVBfTU9ERV9SRUZMRUNUSU9OJztcblx0XHR2YXIgZW52TWFwQmxlbmRpbmdEZWZpbmUgPSAnRU5WTUFQX0JMRU5ESU5HX01VTFRJUExZJztcblxuXHRcdGlmICggcGFyYW1ldGVycy5lbnZNYXAgKSB7XG5cblx0XHRcdHN3aXRjaCAoIG1hdGVyaWFsLmVudk1hcC5tYXBwaW5nICkge1xuXG5cdFx0XHRcdGNhc2UgQ3ViZVJlZmxlY3Rpb25NYXBwaW5nOlxuXHRcdFx0XHRjYXNlIEN1YmVSZWZyYWN0aW9uTWFwcGluZzpcblx0XHRcdFx0XHRlbnZNYXBUeXBlRGVmaW5lID0gJ0VOVk1BUF9UWVBFX0NVQkUnO1xuXHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdGNhc2UgQ3ViZVVWUmVmbGVjdGlvbk1hcHBpbmc6XG5cdFx0XHRcdGNhc2UgQ3ViZVVWUmVmcmFjdGlvbk1hcHBpbmc6XG5cdFx0XHRcdFx0ZW52TWFwVHlwZURlZmluZSA9ICdFTlZNQVBfVFlQRV9DVUJFX1VWJztcblx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRjYXNlIEVxdWlyZWN0YW5ndWxhclJlZmxlY3Rpb25NYXBwaW5nOlxuXHRcdFx0XHRjYXNlIEVxdWlyZWN0YW5ndWxhclJlZnJhY3Rpb25NYXBwaW5nOlxuXHRcdFx0XHRcdGVudk1hcFR5cGVEZWZpbmUgPSAnRU5WTUFQX1RZUEVfRVFVSVJFQyc7XG5cdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0Y2FzZSBTcGhlcmljYWxSZWZsZWN0aW9uTWFwcGluZzpcblx0XHRcdFx0XHRlbnZNYXBUeXBlRGVmaW5lID0gJ0VOVk1BUF9UWVBFX1NQSEVSRSc7XG5cdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdH1cblxuXHRcdFx0c3dpdGNoICggbWF0ZXJpYWwuZW52TWFwLm1hcHBpbmcgKSB7XG5cblx0XHRcdFx0Y2FzZSBDdWJlUmVmcmFjdGlvbk1hcHBpbmc6XG5cdFx0XHRcdGNhc2UgRXF1aXJlY3Rhbmd1bGFyUmVmcmFjdGlvbk1hcHBpbmc6XG5cdFx0XHRcdFx0ZW52TWFwTW9kZURlZmluZSA9ICdFTlZNQVBfTU9ERV9SRUZSQUNUSU9OJztcblx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0fVxuXG5cdFx0XHRzd2l0Y2ggKCBtYXRlcmlhbC5jb21iaW5lICkge1xuXG5cdFx0XHRcdGNhc2UgTXVsdGlwbHlPcGVyYXRpb246XG5cdFx0XHRcdFx0ZW52TWFwQmxlbmRpbmdEZWZpbmUgPSAnRU5WTUFQX0JMRU5ESU5HX01VTFRJUExZJztcblx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRjYXNlIE1peE9wZXJhdGlvbjpcblx0XHRcdFx0XHRlbnZNYXBCbGVuZGluZ0RlZmluZSA9ICdFTlZNQVBfQkxFTkRJTkdfTUlYJztcblx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRjYXNlIEFkZE9wZXJhdGlvbjpcblx0XHRcdFx0XHRlbnZNYXBCbGVuZGluZ0RlZmluZSA9ICdFTlZNQVBfQkxFTkRJTkdfQUREJztcblx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0dmFyIGdhbW1hRmFjdG9yRGVmaW5lID0gKCByZW5kZXJlci5nYW1tYUZhY3RvciA+IDAgKSA/IHJlbmRlcmVyLmdhbW1hRmFjdG9yIDogMS4wO1xuXG5cdFx0Ly8gY29uc29sZS5sb2coICdidWlsZGluZyBuZXcgcHJvZ3JhbSAnICk7XG5cblx0XHQvL1xuXG5cdFx0dmFyIGN1c3RvbUV4dGVuc2lvbnMgPSBnZW5lcmF0ZUV4dGVuc2lvbnMoIGV4dGVuc2lvbnMsIHBhcmFtZXRlcnMsIHJlbmRlcmVyLmV4dGVuc2lvbnMgKTtcblxuXHRcdHZhciBjdXN0b21EZWZpbmVzID0gZ2VuZXJhdGVEZWZpbmVzKCBkZWZpbmVzICk7XG5cblx0XHQvL1xuXG5cdFx0dmFyIHByb2dyYW0gPSBnbC5jcmVhdGVQcm9ncmFtKCk7XG5cblx0XHR2YXIgcHJlZml4VmVydGV4LCBwcmVmaXhGcmFnbWVudDtcblxuXHRcdGlmICggbWF0ZXJpYWwuaXNSYXdTaGFkZXJNYXRlcmlhbCApIHtcblxuXHRcdFx0cHJlZml4VmVydGV4ID0gW1xuXG5cdFx0XHRcdGN1c3RvbURlZmluZXMsXG5cblx0XHRcdFx0J1xcbidcblxuXHRcdFx0XS5maWx0ZXIoIGZpbHRlckVtcHR5TGluZSApLmpvaW4oICdcXG4nICk7XG5cblx0XHRcdHByZWZpeEZyYWdtZW50ID0gW1xuXG5cdFx0XHRcdGN1c3RvbUV4dGVuc2lvbnMsXG5cdFx0XHRcdGN1c3RvbURlZmluZXMsXG5cblx0XHRcdFx0J1xcbidcblxuXHRcdFx0XS5maWx0ZXIoIGZpbHRlckVtcHR5TGluZSApLmpvaW4oICdcXG4nICk7XG5cblx0XHR9IGVsc2Uge1xuXG5cdFx0XHRwcmVmaXhWZXJ0ZXggPSBbXG5cblx0XHRcdFx0J3ByZWNpc2lvbiAnICsgcGFyYW1ldGVycy5wcmVjaXNpb24gKyAnIGZsb2F0OycsXG5cdFx0XHRcdCdwcmVjaXNpb24gJyArIHBhcmFtZXRlcnMucHJlY2lzaW9uICsgJyBpbnQ7JyxcblxuXHRcdFx0XHQnI2RlZmluZSBTSEFERVJfTkFNRSAnICsgbWF0ZXJpYWwuX193ZWJnbFNoYWRlci5uYW1lLFxuXG5cdFx0XHRcdGN1c3RvbURlZmluZXMsXG5cblx0XHRcdFx0cGFyYW1ldGVycy5zdXBwb3J0c1ZlcnRleFRleHR1cmVzID8gJyNkZWZpbmUgVkVSVEVYX1RFWFRVUkVTJyA6ICcnLFxuXG5cdFx0XHRcdCcjZGVmaW5lIEdBTU1BX0ZBQ1RPUiAnICsgZ2FtbWFGYWN0b3JEZWZpbmUsXG5cblx0XHRcdFx0JyNkZWZpbmUgTUFYX0JPTkVTICcgKyBwYXJhbWV0ZXJzLm1heEJvbmVzLFxuXG5cdFx0XHRcdHBhcmFtZXRlcnMubWFwID8gJyNkZWZpbmUgVVNFX01BUCcgOiAnJyxcblx0XHRcdFx0cGFyYW1ldGVycy5lbnZNYXAgPyAnI2RlZmluZSBVU0VfRU5WTUFQJyA6ICcnLFxuXHRcdFx0XHRwYXJhbWV0ZXJzLmVudk1hcCA/ICcjZGVmaW5lICcgKyBlbnZNYXBNb2RlRGVmaW5lIDogJycsXG5cdFx0XHRcdHBhcmFtZXRlcnMubGlnaHRNYXAgPyAnI2RlZmluZSBVU0VfTElHSFRNQVAnIDogJycsXG5cdFx0XHRcdHBhcmFtZXRlcnMuYW9NYXAgPyAnI2RlZmluZSBVU0VfQU9NQVAnIDogJycsXG5cdFx0XHRcdHBhcmFtZXRlcnMuZW1pc3NpdmVNYXAgPyAnI2RlZmluZSBVU0VfRU1JU1NJVkVNQVAnIDogJycsXG5cdFx0XHRcdHBhcmFtZXRlcnMuYnVtcE1hcCA/ICcjZGVmaW5lIFVTRV9CVU1QTUFQJyA6ICcnLFxuXHRcdFx0XHRwYXJhbWV0ZXJzLm5vcm1hbE1hcCA/ICcjZGVmaW5lIFVTRV9OT1JNQUxNQVAnIDogJycsXG5cdFx0XHRcdHBhcmFtZXRlcnMuZGlzcGxhY2VtZW50TWFwICYmIHBhcmFtZXRlcnMuc3VwcG9ydHNWZXJ0ZXhUZXh0dXJlcyA/ICcjZGVmaW5lIFVTRV9ESVNQTEFDRU1FTlRNQVAnIDogJycsXG5cdFx0XHRcdHBhcmFtZXRlcnMuc3BlY3VsYXJNYXAgPyAnI2RlZmluZSBVU0VfU1BFQ1VMQVJNQVAnIDogJycsXG5cdFx0XHRcdHBhcmFtZXRlcnMucm91Z2huZXNzTWFwID8gJyNkZWZpbmUgVVNFX1JPVUdITkVTU01BUCcgOiAnJyxcblx0XHRcdFx0cGFyYW1ldGVycy5tZXRhbG5lc3NNYXAgPyAnI2RlZmluZSBVU0VfTUVUQUxORVNTTUFQJyA6ICcnLFxuXHRcdFx0XHRwYXJhbWV0ZXJzLmFscGhhTWFwID8gJyNkZWZpbmUgVVNFX0FMUEhBTUFQJyA6ICcnLFxuXHRcdFx0XHRwYXJhbWV0ZXJzLnZlcnRleENvbG9ycyA/ICcjZGVmaW5lIFVTRV9DT0xPUicgOiAnJyxcblxuXHRcdFx0XHRwYXJhbWV0ZXJzLmZsYXRTaGFkaW5nID8gJyNkZWZpbmUgRkxBVF9TSEFERUQnIDogJycsXG5cblx0XHRcdFx0cGFyYW1ldGVycy5za2lubmluZyA/ICcjZGVmaW5lIFVTRV9TS0lOTklORycgOiAnJyxcblx0XHRcdFx0cGFyYW1ldGVycy51c2VWZXJ0ZXhUZXh0dXJlID8gJyNkZWZpbmUgQk9ORV9URVhUVVJFJyA6ICcnLFxuXG5cdFx0XHRcdHBhcmFtZXRlcnMubW9ycGhUYXJnZXRzID8gJyNkZWZpbmUgVVNFX01PUlBIVEFSR0VUUycgOiAnJyxcblx0XHRcdFx0cGFyYW1ldGVycy5tb3JwaE5vcm1hbHMgJiYgcGFyYW1ldGVycy5mbGF0U2hhZGluZyA9PT0gZmFsc2UgPyAnI2RlZmluZSBVU0VfTU9SUEhOT1JNQUxTJyA6ICcnLFxuXHRcdFx0XHRwYXJhbWV0ZXJzLmRvdWJsZVNpZGVkID8gJyNkZWZpbmUgRE9VQkxFX1NJREVEJyA6ICcnLFxuXHRcdFx0XHRwYXJhbWV0ZXJzLmZsaXBTaWRlZCA/ICcjZGVmaW5lIEZMSVBfU0lERUQnIDogJycsXG5cblx0XHRcdFx0JyNkZWZpbmUgTlVNX0NMSVBQSU5HX1BMQU5FUyAnICsgcGFyYW1ldGVycy5udW1DbGlwcGluZ1BsYW5lcyxcblxuXHRcdFx0XHRwYXJhbWV0ZXJzLnNoYWRvd01hcEVuYWJsZWQgPyAnI2RlZmluZSBVU0VfU0hBRE9XTUFQJyA6ICcnLFxuXHRcdFx0XHRwYXJhbWV0ZXJzLnNoYWRvd01hcEVuYWJsZWQgPyAnI2RlZmluZSAnICsgc2hhZG93TWFwVHlwZURlZmluZSA6ICcnLFxuXG5cdFx0XHRcdHBhcmFtZXRlcnMuc2l6ZUF0dGVudWF0aW9uID8gJyNkZWZpbmUgVVNFX1NJWkVBVFRFTlVBVElPTicgOiAnJyxcblxuXHRcdFx0XHRwYXJhbWV0ZXJzLmxvZ2FyaXRobWljRGVwdGhCdWZmZXIgPyAnI2RlZmluZSBVU0VfTE9HREVQVEhCVUYnIDogJycsXG5cdFx0XHRcdHBhcmFtZXRlcnMubG9nYXJpdGhtaWNEZXB0aEJ1ZmZlciAmJiByZW5kZXJlci5leHRlbnNpb25zLmdldCggJ0VYVF9mcmFnX2RlcHRoJyApID8gJyNkZWZpbmUgVVNFX0xPR0RFUFRIQlVGX0VYVCcgOiAnJyxcblxuXHRcdFx0XHQndW5pZm9ybSBtYXQ0IG1vZGVsTWF0cml4OycsXG5cdFx0XHRcdCd1bmlmb3JtIG1hdDQgbW9kZWxWaWV3TWF0cml4OycsXG5cdFx0XHRcdCd1bmlmb3JtIG1hdDQgcHJvamVjdGlvbk1hdHJpeDsnLFxuXHRcdFx0XHQndW5pZm9ybSBtYXQ0IHZpZXdNYXRyaXg7Jyxcblx0XHRcdFx0J3VuaWZvcm0gbWF0MyBub3JtYWxNYXRyaXg7Jyxcblx0XHRcdFx0J3VuaWZvcm0gdmVjMyBjYW1lcmFQb3NpdGlvbjsnLFxuXG5cdFx0XHRcdCdhdHRyaWJ1dGUgdmVjMyBwb3NpdGlvbjsnLFxuXHRcdFx0XHQnYXR0cmlidXRlIHZlYzMgbm9ybWFsOycsXG5cdFx0XHRcdCdhdHRyaWJ1dGUgdmVjMiB1djsnLFxuXG5cdFx0XHRcdCcjaWZkZWYgVVNFX0NPTE9SJyxcblxuXHRcdFx0XHQnXHRhdHRyaWJ1dGUgdmVjMyBjb2xvcjsnLFxuXG5cdFx0XHRcdCcjZW5kaWYnLFxuXG5cdFx0XHRcdCcjaWZkZWYgVVNFX01PUlBIVEFSR0VUUycsXG5cblx0XHRcdFx0J1x0YXR0cmlidXRlIHZlYzMgbW9ycGhUYXJnZXQwOycsXG5cdFx0XHRcdCdcdGF0dHJpYnV0ZSB2ZWMzIG1vcnBoVGFyZ2V0MTsnLFxuXHRcdFx0XHQnXHRhdHRyaWJ1dGUgdmVjMyBtb3JwaFRhcmdldDI7Jyxcblx0XHRcdFx0J1x0YXR0cmlidXRlIHZlYzMgbW9ycGhUYXJnZXQzOycsXG5cblx0XHRcdFx0J1x0I2lmZGVmIFVTRV9NT1JQSE5PUk1BTFMnLFxuXG5cdFx0XHRcdCdcdFx0YXR0cmlidXRlIHZlYzMgbW9ycGhOb3JtYWwwOycsXG5cdFx0XHRcdCdcdFx0YXR0cmlidXRlIHZlYzMgbW9ycGhOb3JtYWwxOycsXG5cdFx0XHRcdCdcdFx0YXR0cmlidXRlIHZlYzMgbW9ycGhOb3JtYWwyOycsXG5cdFx0XHRcdCdcdFx0YXR0cmlidXRlIHZlYzMgbW9ycGhOb3JtYWwzOycsXG5cblx0XHRcdFx0J1x0I2Vsc2UnLFxuXG5cdFx0XHRcdCdcdFx0YXR0cmlidXRlIHZlYzMgbW9ycGhUYXJnZXQ0OycsXG5cdFx0XHRcdCdcdFx0YXR0cmlidXRlIHZlYzMgbW9ycGhUYXJnZXQ1OycsXG5cdFx0XHRcdCdcdFx0YXR0cmlidXRlIHZlYzMgbW9ycGhUYXJnZXQ2OycsXG5cdFx0XHRcdCdcdFx0YXR0cmlidXRlIHZlYzMgbW9ycGhUYXJnZXQ3OycsXG5cblx0XHRcdFx0J1x0I2VuZGlmJyxcblxuXHRcdFx0XHQnI2VuZGlmJyxcblxuXHRcdFx0XHQnI2lmZGVmIFVTRV9TS0lOTklORycsXG5cblx0XHRcdFx0J1x0YXR0cmlidXRlIHZlYzQgc2tpbkluZGV4OycsXG5cdFx0XHRcdCdcdGF0dHJpYnV0ZSB2ZWM0IHNraW5XZWlnaHQ7JyxcblxuXHRcdFx0XHQnI2VuZGlmJyxcblxuXHRcdFx0XHQnXFxuJ1xuXG5cdFx0XHRdLmZpbHRlciggZmlsdGVyRW1wdHlMaW5lICkuam9pbiggJ1xcbicgKTtcblxuXHRcdFx0cHJlZml4RnJhZ21lbnQgPSBbXG5cblx0XHRcdFx0Y3VzdG9tRXh0ZW5zaW9ucyxcblxuXHRcdFx0XHQncHJlY2lzaW9uICcgKyBwYXJhbWV0ZXJzLnByZWNpc2lvbiArICcgZmxvYXQ7Jyxcblx0XHRcdFx0J3ByZWNpc2lvbiAnICsgcGFyYW1ldGVycy5wcmVjaXNpb24gKyAnIGludDsnLFxuXG5cdFx0XHRcdCcjZGVmaW5lIFNIQURFUl9OQU1FICcgKyBtYXRlcmlhbC5fX3dlYmdsU2hhZGVyLm5hbWUsXG5cblx0XHRcdFx0Y3VzdG9tRGVmaW5lcyxcblxuXHRcdFx0XHRwYXJhbWV0ZXJzLmFscGhhVGVzdCA/ICcjZGVmaW5lIEFMUEhBVEVTVCAnICsgcGFyYW1ldGVycy5hbHBoYVRlc3QgOiAnJyxcblxuXHRcdFx0XHQnI2RlZmluZSBHQU1NQV9GQUNUT1IgJyArIGdhbW1hRmFjdG9yRGVmaW5lLFxuXG5cdFx0XHRcdCggcGFyYW1ldGVycy51c2VGb2cgJiYgcGFyYW1ldGVycy5mb2cgKSA/ICcjZGVmaW5lIFVTRV9GT0cnIDogJycsXG5cdFx0XHRcdCggcGFyYW1ldGVycy51c2VGb2cgJiYgcGFyYW1ldGVycy5mb2dFeHAgKSA/ICcjZGVmaW5lIEZPR19FWFAyJyA6ICcnLFxuXG5cdFx0XHRcdHBhcmFtZXRlcnMubWFwID8gJyNkZWZpbmUgVVNFX01BUCcgOiAnJyxcblx0XHRcdFx0cGFyYW1ldGVycy5lbnZNYXAgPyAnI2RlZmluZSBVU0VfRU5WTUFQJyA6ICcnLFxuXHRcdFx0XHRwYXJhbWV0ZXJzLmVudk1hcCA/ICcjZGVmaW5lICcgKyBlbnZNYXBUeXBlRGVmaW5lIDogJycsXG5cdFx0XHRcdHBhcmFtZXRlcnMuZW52TWFwID8gJyNkZWZpbmUgJyArIGVudk1hcE1vZGVEZWZpbmUgOiAnJyxcblx0XHRcdFx0cGFyYW1ldGVycy5lbnZNYXAgPyAnI2RlZmluZSAnICsgZW52TWFwQmxlbmRpbmdEZWZpbmUgOiAnJyxcblx0XHRcdFx0cGFyYW1ldGVycy5saWdodE1hcCA/ICcjZGVmaW5lIFVTRV9MSUdIVE1BUCcgOiAnJyxcblx0XHRcdFx0cGFyYW1ldGVycy5hb01hcCA/ICcjZGVmaW5lIFVTRV9BT01BUCcgOiAnJyxcblx0XHRcdFx0cGFyYW1ldGVycy5lbWlzc2l2ZU1hcCA/ICcjZGVmaW5lIFVTRV9FTUlTU0lWRU1BUCcgOiAnJyxcblx0XHRcdFx0cGFyYW1ldGVycy5idW1wTWFwID8gJyNkZWZpbmUgVVNFX0JVTVBNQVAnIDogJycsXG5cdFx0XHRcdHBhcmFtZXRlcnMubm9ybWFsTWFwID8gJyNkZWZpbmUgVVNFX05PUk1BTE1BUCcgOiAnJyxcblx0XHRcdFx0cGFyYW1ldGVycy5zcGVjdWxhck1hcCA/ICcjZGVmaW5lIFVTRV9TUEVDVUxBUk1BUCcgOiAnJyxcblx0XHRcdFx0cGFyYW1ldGVycy5yb3VnaG5lc3NNYXAgPyAnI2RlZmluZSBVU0VfUk9VR0hORVNTTUFQJyA6ICcnLFxuXHRcdFx0XHRwYXJhbWV0ZXJzLm1ldGFsbmVzc01hcCA/ICcjZGVmaW5lIFVTRV9NRVRBTE5FU1NNQVAnIDogJycsXG5cdFx0XHRcdHBhcmFtZXRlcnMuYWxwaGFNYXAgPyAnI2RlZmluZSBVU0VfQUxQSEFNQVAnIDogJycsXG5cdFx0XHRcdHBhcmFtZXRlcnMudmVydGV4Q29sb3JzID8gJyNkZWZpbmUgVVNFX0NPTE9SJyA6ICcnLFxuXG5cdFx0XHRcdHBhcmFtZXRlcnMuZ3JhZGllbnRNYXAgPyAnI2RlZmluZSBVU0VfR1JBRElFTlRNQVAnIDogJycsXG5cblx0XHRcdFx0cGFyYW1ldGVycy5mbGF0U2hhZGluZyA/ICcjZGVmaW5lIEZMQVRfU0hBREVEJyA6ICcnLFxuXG5cdFx0XHRcdHBhcmFtZXRlcnMuZG91YmxlU2lkZWQgPyAnI2RlZmluZSBET1VCTEVfU0lERUQnIDogJycsXG5cdFx0XHRcdHBhcmFtZXRlcnMuZmxpcFNpZGVkID8gJyNkZWZpbmUgRkxJUF9TSURFRCcgOiAnJyxcblxuXHRcdFx0XHQnI2RlZmluZSBOVU1fQ0xJUFBJTkdfUExBTkVTICcgKyBwYXJhbWV0ZXJzLm51bUNsaXBwaW5nUGxhbmVzLFxuXHRcdFx0XHQnI2RlZmluZSBVTklPTl9DTElQUElOR19QTEFORVMgJyArIChwYXJhbWV0ZXJzLm51bUNsaXBwaW5nUGxhbmVzIC0gcGFyYW1ldGVycy5udW1DbGlwSW50ZXJzZWN0aW9uKSxcblxuXHRcdFx0XHRwYXJhbWV0ZXJzLnNoYWRvd01hcEVuYWJsZWQgPyAnI2RlZmluZSBVU0VfU0hBRE9XTUFQJyA6ICcnLFxuXHRcdFx0XHRwYXJhbWV0ZXJzLnNoYWRvd01hcEVuYWJsZWQgPyAnI2RlZmluZSAnICsgc2hhZG93TWFwVHlwZURlZmluZSA6ICcnLFxuXG5cdFx0XHRcdHBhcmFtZXRlcnMucHJlbXVsdGlwbGllZEFscGhhID8gXCIjZGVmaW5lIFBSRU1VTFRJUExJRURfQUxQSEFcIiA6ICcnLFxuXG5cdFx0XHRcdHBhcmFtZXRlcnMucGh5c2ljYWxseUNvcnJlY3RMaWdodHMgPyBcIiNkZWZpbmUgUEhZU0lDQUxMWV9DT1JSRUNUX0xJR0hUU1wiIDogJycsXG5cblx0XHRcdFx0cGFyYW1ldGVycy5sb2dhcml0aG1pY0RlcHRoQnVmZmVyID8gJyNkZWZpbmUgVVNFX0xPR0RFUFRIQlVGJyA6ICcnLFxuXHRcdFx0XHRwYXJhbWV0ZXJzLmxvZ2FyaXRobWljRGVwdGhCdWZmZXIgJiYgcmVuZGVyZXIuZXh0ZW5zaW9ucy5nZXQoICdFWFRfZnJhZ19kZXB0aCcgKSA/ICcjZGVmaW5lIFVTRV9MT0dERVBUSEJVRl9FWFQnIDogJycsXG5cblx0XHRcdFx0cGFyYW1ldGVycy5lbnZNYXAgJiYgcmVuZGVyZXIuZXh0ZW5zaW9ucy5nZXQoICdFWFRfc2hhZGVyX3RleHR1cmVfbG9kJyApID8gJyNkZWZpbmUgVEVYVFVSRV9MT0RfRVhUJyA6ICcnLFxuXG5cdFx0XHRcdCd1bmlmb3JtIG1hdDQgdmlld01hdHJpeDsnLFxuXHRcdFx0XHQndW5pZm9ybSB2ZWMzIGNhbWVyYVBvc2l0aW9uOycsXG5cblx0XHRcdFx0KCBwYXJhbWV0ZXJzLnRvbmVNYXBwaW5nICE9PSBOb1RvbmVNYXBwaW5nICkgPyBcIiNkZWZpbmUgVE9ORV9NQVBQSU5HXCIgOiAnJyxcblx0XHRcdFx0KCBwYXJhbWV0ZXJzLnRvbmVNYXBwaW5nICE9PSBOb1RvbmVNYXBwaW5nICkgPyBTaGFkZXJDaHVua1sgJ3RvbmVtYXBwaW5nX3BhcnNfZnJhZ21lbnQnIF0gOiAnJywgIC8vIHRoaXMgY29kZSBpcyByZXF1aXJlZCBoZXJlIGJlY2F1c2UgaXQgaXMgdXNlZCBieSB0aGUgdG9uZU1hcHBpbmcoKSBmdW5jdGlvbiBkZWZpbmVkIGJlbG93XG5cdFx0XHRcdCggcGFyYW1ldGVycy50b25lTWFwcGluZyAhPT0gTm9Ub25lTWFwcGluZyApID8gZ2V0VG9uZU1hcHBpbmdGdW5jdGlvbiggXCJ0b25lTWFwcGluZ1wiLCBwYXJhbWV0ZXJzLnRvbmVNYXBwaW5nICkgOiAnJyxcblxuXHRcdFx0XHQoIHBhcmFtZXRlcnMub3V0cHV0RW5jb2RpbmcgfHwgcGFyYW1ldGVycy5tYXBFbmNvZGluZyB8fCBwYXJhbWV0ZXJzLmVudk1hcEVuY29kaW5nIHx8IHBhcmFtZXRlcnMuZW1pc3NpdmVNYXBFbmNvZGluZyApID8gU2hhZGVyQ2h1bmtbICdlbmNvZGluZ3NfcGFyc19mcmFnbWVudCcgXSA6ICcnLCAvLyB0aGlzIGNvZGUgaXMgcmVxdWlyZWQgaGVyZSBiZWNhdXNlIGl0IGlzIHVzZWQgYnkgdGhlIHZhcmlvdXMgZW5jb2RpbmcvZGVjb2RpbmcgZnVuY3Rpb24gZGVmaW5lZCBiZWxvd1xuXHRcdFx0XHRwYXJhbWV0ZXJzLm1hcEVuY29kaW5nID8gZ2V0VGV4ZWxEZWNvZGluZ0Z1bmN0aW9uKCAnbWFwVGV4ZWxUb0xpbmVhcicsIHBhcmFtZXRlcnMubWFwRW5jb2RpbmcgKSA6ICcnLFxuXHRcdFx0XHRwYXJhbWV0ZXJzLmVudk1hcEVuY29kaW5nID8gZ2V0VGV4ZWxEZWNvZGluZ0Z1bmN0aW9uKCAnZW52TWFwVGV4ZWxUb0xpbmVhcicsIHBhcmFtZXRlcnMuZW52TWFwRW5jb2RpbmcgKSA6ICcnLFxuXHRcdFx0XHRwYXJhbWV0ZXJzLmVtaXNzaXZlTWFwRW5jb2RpbmcgPyBnZXRUZXhlbERlY29kaW5nRnVuY3Rpb24oICdlbWlzc2l2ZU1hcFRleGVsVG9MaW5lYXInLCBwYXJhbWV0ZXJzLmVtaXNzaXZlTWFwRW5jb2RpbmcgKSA6ICcnLFxuXHRcdFx0XHRwYXJhbWV0ZXJzLm91dHB1dEVuY29kaW5nID8gZ2V0VGV4ZWxFbmNvZGluZ0Z1bmN0aW9uKCBcImxpbmVhclRvT3V0cHV0VGV4ZWxcIiwgcGFyYW1ldGVycy5vdXRwdXRFbmNvZGluZyApIDogJycsXG5cblx0XHRcdFx0cGFyYW1ldGVycy5kZXB0aFBhY2tpbmcgPyBcIiNkZWZpbmUgREVQVEhfUEFDS0lORyBcIiArIG1hdGVyaWFsLmRlcHRoUGFja2luZyA6ICcnLFxuXG5cdFx0XHRcdCdcXG4nXG5cblx0XHRcdF0uZmlsdGVyKCBmaWx0ZXJFbXB0eUxpbmUgKS5qb2luKCAnXFxuJyApO1xuXG5cdFx0fVxuXG5cdFx0dmVydGV4U2hhZGVyID0gcGFyc2VJbmNsdWRlcyggdmVydGV4U2hhZGVyLCBwYXJhbWV0ZXJzICk7XG5cdFx0dmVydGV4U2hhZGVyID0gcmVwbGFjZUxpZ2h0TnVtcyggdmVydGV4U2hhZGVyLCBwYXJhbWV0ZXJzICk7XG5cblx0XHRmcmFnbWVudFNoYWRlciA9IHBhcnNlSW5jbHVkZXMoIGZyYWdtZW50U2hhZGVyLCBwYXJhbWV0ZXJzICk7XG5cdFx0ZnJhZ21lbnRTaGFkZXIgPSByZXBsYWNlTGlnaHROdW1zKCBmcmFnbWVudFNoYWRlciwgcGFyYW1ldGVycyApO1xuXG5cdFx0aWYgKCAhIG1hdGVyaWFsLmlzU2hhZGVyTWF0ZXJpYWwgKSB7XG5cblx0XHRcdHZlcnRleFNoYWRlciA9IHVucm9sbExvb3BzKCB2ZXJ0ZXhTaGFkZXIgKTtcblx0XHRcdGZyYWdtZW50U2hhZGVyID0gdW5yb2xsTG9vcHMoIGZyYWdtZW50U2hhZGVyICk7XG5cblx0XHR9XG5cblx0XHR2YXIgdmVydGV4R2xzbCA9IHByZWZpeFZlcnRleCArIHZlcnRleFNoYWRlcjtcblx0XHR2YXIgZnJhZ21lbnRHbHNsID0gcHJlZml4RnJhZ21lbnQgKyBmcmFnbWVudFNoYWRlcjtcblxuXHRcdC8vIGNvbnNvbGUubG9nKCAnKlZFUlRFWConLCB2ZXJ0ZXhHbHNsICk7XG5cdFx0Ly8gY29uc29sZS5sb2coICcqRlJBR01FTlQqJywgZnJhZ21lbnRHbHNsICk7XG5cblx0XHR2YXIgZ2xWZXJ0ZXhTaGFkZXIgPSBXZWJHTFNoYWRlciggZ2wsIGdsLlZFUlRFWF9TSEFERVIsIHZlcnRleEdsc2wgKTtcblx0XHR2YXIgZ2xGcmFnbWVudFNoYWRlciA9IFdlYkdMU2hhZGVyKCBnbCwgZ2wuRlJBR01FTlRfU0hBREVSLCBmcmFnbWVudEdsc2wgKTtcblxuXHRcdGdsLmF0dGFjaFNoYWRlciggcHJvZ3JhbSwgZ2xWZXJ0ZXhTaGFkZXIgKTtcblx0XHRnbC5hdHRhY2hTaGFkZXIoIHByb2dyYW0sIGdsRnJhZ21lbnRTaGFkZXIgKTtcblxuXHRcdC8vIEZvcmNlIGEgcGFydGljdWxhciBhdHRyaWJ1dGUgdG8gaW5kZXggMC5cblxuXHRcdGlmICggbWF0ZXJpYWwuaW5kZXgwQXR0cmlidXRlTmFtZSAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRnbC5iaW5kQXR0cmliTG9jYXRpb24oIHByb2dyYW0sIDAsIG1hdGVyaWFsLmluZGV4MEF0dHJpYnV0ZU5hbWUgKTtcblxuXHRcdH0gZWxzZSBpZiAoIHBhcmFtZXRlcnMubW9ycGhUYXJnZXRzID09PSB0cnVlICkge1xuXG5cdFx0XHQvLyBwcm9ncmFtcyB3aXRoIG1vcnBoVGFyZ2V0cyBkaXNwbGFjZSBwb3NpdGlvbiBvdXQgb2YgYXR0cmlidXRlIDBcblx0XHRcdGdsLmJpbmRBdHRyaWJMb2NhdGlvbiggcHJvZ3JhbSwgMCwgJ3Bvc2l0aW9uJyApO1xuXG5cdFx0fVxuXG5cdFx0Z2wubGlua1Byb2dyYW0oIHByb2dyYW0gKTtcblxuXHRcdHZhciBwcm9ncmFtTG9nID0gZ2wuZ2V0UHJvZ3JhbUluZm9Mb2coIHByb2dyYW0gKTtcblx0XHR2YXIgdmVydGV4TG9nID0gZ2wuZ2V0U2hhZGVySW5mb0xvZyggZ2xWZXJ0ZXhTaGFkZXIgKTtcblx0XHR2YXIgZnJhZ21lbnRMb2cgPSBnbC5nZXRTaGFkZXJJbmZvTG9nKCBnbEZyYWdtZW50U2hhZGVyICk7XG5cblx0XHR2YXIgcnVubmFibGUgPSB0cnVlO1xuXHRcdHZhciBoYXZlRGlhZ25vc3RpY3MgPSB0cnVlO1xuXG5cdFx0Ly8gY29uc29sZS5sb2coICcqKlZFUlRFWCoqJywgZ2wuZ2V0RXh0ZW5zaW9uKCAnV0VCR0xfZGVidWdfc2hhZGVycycgKS5nZXRUcmFuc2xhdGVkU2hhZGVyU291cmNlKCBnbFZlcnRleFNoYWRlciApICk7XG5cdFx0Ly8gY29uc29sZS5sb2coICcqKkZSQUdNRU5UKionLCBnbC5nZXRFeHRlbnNpb24oICdXRUJHTF9kZWJ1Z19zaGFkZXJzJyApLmdldFRyYW5zbGF0ZWRTaGFkZXJTb3VyY2UoIGdsRnJhZ21lbnRTaGFkZXIgKSApO1xuXG5cdFx0aWYgKCBnbC5nZXRQcm9ncmFtUGFyYW1ldGVyKCBwcm9ncmFtLCBnbC5MSU5LX1NUQVRVUyApID09PSBmYWxzZSApIHtcblxuXHRcdFx0cnVubmFibGUgPSBmYWxzZTtcblxuXHRcdFx0Y29uc29sZS5lcnJvciggJ1RIUkVFLldlYkdMUHJvZ3JhbTogc2hhZGVyIGVycm9yOiAnLCBnbC5nZXRFcnJvcigpLCAnZ2wuVkFMSURBVEVfU1RBVFVTJywgZ2wuZ2V0UHJvZ3JhbVBhcmFtZXRlciggcHJvZ3JhbSwgZ2wuVkFMSURBVEVfU1RBVFVTICksICdnbC5nZXRQcm9ncmFtSW5mb0xvZycsIHByb2dyYW1Mb2csIHZlcnRleExvZywgZnJhZ21lbnRMb2cgKTtcblxuXHRcdH0gZWxzZSBpZiAoIHByb2dyYW1Mb2cgIT09ICcnICkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5XZWJHTFByb2dyYW06IGdsLmdldFByb2dyYW1JbmZvTG9nKCknLCBwcm9ncmFtTG9nICk7XG5cblx0XHR9IGVsc2UgaWYgKCB2ZXJ0ZXhMb2cgPT09ICcnIHx8IGZyYWdtZW50TG9nID09PSAnJyApIHtcblxuXHRcdFx0aGF2ZURpYWdub3N0aWNzID0gZmFsc2U7XG5cblx0XHR9XG5cblx0XHRpZiAoIGhhdmVEaWFnbm9zdGljcyApIHtcblxuXHRcdFx0dGhpcy5kaWFnbm9zdGljcyA9IHtcblxuXHRcdFx0XHRydW5uYWJsZTogcnVubmFibGUsXG5cdFx0XHRcdG1hdGVyaWFsOiBtYXRlcmlhbCxcblxuXHRcdFx0XHRwcm9ncmFtTG9nOiBwcm9ncmFtTG9nLFxuXG5cdFx0XHRcdHZlcnRleFNoYWRlcjoge1xuXG5cdFx0XHRcdFx0bG9nOiB2ZXJ0ZXhMb2csXG5cdFx0XHRcdFx0cHJlZml4OiBwcmVmaXhWZXJ0ZXhcblxuXHRcdFx0XHR9LFxuXG5cdFx0XHRcdGZyYWdtZW50U2hhZGVyOiB7XG5cblx0XHRcdFx0XHRsb2c6IGZyYWdtZW50TG9nLFxuXHRcdFx0XHRcdHByZWZpeDogcHJlZml4RnJhZ21lbnRcblxuXHRcdFx0XHR9XG5cblx0XHRcdH07XG5cblx0XHR9XG5cblx0XHQvLyBjbGVhbiB1cFxuXG5cdFx0Z2wuZGVsZXRlU2hhZGVyKCBnbFZlcnRleFNoYWRlciApO1xuXHRcdGdsLmRlbGV0ZVNoYWRlciggZ2xGcmFnbWVudFNoYWRlciApO1xuXG5cdFx0Ly8gc2V0IHVwIGNhY2hpbmcgZm9yIHVuaWZvcm0gbG9jYXRpb25zXG5cblx0XHR2YXIgY2FjaGVkVW5pZm9ybXM7XG5cblx0XHR0aGlzLmdldFVuaWZvcm1zID0gZnVuY3Rpb24oKSB7XG5cblx0XHRcdGlmICggY2FjaGVkVW5pZm9ybXMgPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRjYWNoZWRVbmlmb3JtcyA9XG5cdFx0XHRcdFx0bmV3IFdlYkdMVW5pZm9ybXMoIGdsLCBwcm9ncmFtLCByZW5kZXJlciApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBjYWNoZWRVbmlmb3JtcztcblxuXHRcdH07XG5cblx0XHQvLyBzZXQgdXAgY2FjaGluZyBmb3IgYXR0cmlidXRlIGxvY2F0aW9uc1xuXG5cdFx0dmFyIGNhY2hlZEF0dHJpYnV0ZXM7XG5cblx0XHR0aGlzLmdldEF0dHJpYnV0ZXMgPSBmdW5jdGlvbigpIHtcblxuXHRcdFx0aWYgKCBjYWNoZWRBdHRyaWJ1dGVzID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0Y2FjaGVkQXR0cmlidXRlcyA9IGZldGNoQXR0cmlidXRlTG9jYXRpb25zKCBnbCwgcHJvZ3JhbSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBjYWNoZWRBdHRyaWJ1dGVzO1xuXG5cdFx0fTtcblxuXHRcdC8vIGZyZWUgcmVzb3VyY2VcblxuXHRcdHRoaXMuZGVzdHJveSA9IGZ1bmN0aW9uKCkge1xuXG5cdFx0XHRnbC5kZWxldGVQcm9ncmFtKCBwcm9ncmFtICk7XG5cdFx0XHR0aGlzLnByb2dyYW0gPSB1bmRlZmluZWQ7XG5cblx0XHR9O1xuXG5cdFx0Ly8gREVQUkVDQVRFRFxuXG5cdFx0T2JqZWN0LmRlZmluZVByb3BlcnRpZXMoIHRoaXMsIHtcblxuXHRcdFx0dW5pZm9ybXM6IHtcblx0XHRcdFx0Z2V0OiBmdW5jdGlvbigpIHtcblxuXHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUHJvZ3JhbTogLnVuaWZvcm1zIGlzIG5vdyAuZ2V0VW5pZm9ybXMoKS4nICk7XG5cdFx0XHRcdFx0cmV0dXJuIHRoaXMuZ2V0VW5pZm9ybXMoKTtcblxuXHRcdFx0XHR9XG5cdFx0XHR9LFxuXG5cdFx0XHRhdHRyaWJ1dGVzOiB7XG5cdFx0XHRcdGdldDogZnVuY3Rpb24oKSB7XG5cblx0XHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5XZWJHTFByb2dyYW06IC5hdHRyaWJ1dGVzIGlzIG5vdyAuZ2V0QXR0cmlidXRlcygpLicgKTtcblx0XHRcdFx0XHRyZXR1cm4gdGhpcy5nZXRBdHRyaWJ1dGVzKCk7XG5cblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0fSApO1xuXG5cblx0XHQvL1xuXG5cdFx0dGhpcy5pZCA9IHByb2dyYW1JZENvdW50ICsrO1xuXHRcdHRoaXMuY29kZSA9IGNvZGU7XG5cdFx0dGhpcy51c2VkVGltZXMgPSAxO1xuXHRcdHRoaXMucHJvZ3JhbSA9IHByb2dyYW07XG5cdFx0dGhpcy52ZXJ0ZXhTaGFkZXIgPSBnbFZlcnRleFNoYWRlcjtcblx0XHR0aGlzLmZyYWdtZW50U2hhZGVyID0gZ2xGcmFnbWVudFNoYWRlcjtcblxuXHRcdHJldHVybiB0aGlzO1xuXG5cdH1cblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gV2ViR0xQcm9ncmFtcyggcmVuZGVyZXIsIGNhcGFiaWxpdGllcyApIHtcblxuXHRcdHZhciBwcm9ncmFtcyA9IFtdO1xuXG5cdFx0dmFyIHNoYWRlcklEcyA9IHtcblx0XHRcdE1lc2hEZXB0aE1hdGVyaWFsOiAnZGVwdGgnLFxuXHRcdFx0TWVzaE5vcm1hbE1hdGVyaWFsOiAnbm9ybWFsJyxcblx0XHRcdE1lc2hCYXNpY01hdGVyaWFsOiAnYmFzaWMnLFxuXHRcdFx0TWVzaExhbWJlcnRNYXRlcmlhbDogJ2xhbWJlcnQnLFxuXHRcdFx0TWVzaFBob25nTWF0ZXJpYWw6ICdwaG9uZycsXG5cdFx0XHRNZXNoVG9vbk1hdGVyaWFsOiAncGhvbmcnLFxuXHRcdFx0TWVzaFN0YW5kYXJkTWF0ZXJpYWw6ICdwaHlzaWNhbCcsXG5cdFx0XHRNZXNoUGh5c2ljYWxNYXRlcmlhbDogJ3BoeXNpY2FsJyxcblx0XHRcdExpbmVCYXNpY01hdGVyaWFsOiAnYmFzaWMnLFxuXHRcdFx0TGluZURhc2hlZE1hdGVyaWFsOiAnZGFzaGVkJyxcblx0XHRcdFBvaW50c01hdGVyaWFsOiAncG9pbnRzJ1xuXHRcdH07XG5cblx0XHR2YXIgcGFyYW1ldGVyTmFtZXMgPSBbXG5cdFx0XHRcInByZWNpc2lvblwiLCBcInN1cHBvcnRzVmVydGV4VGV4dHVyZXNcIiwgXCJtYXBcIiwgXCJtYXBFbmNvZGluZ1wiLCBcImVudk1hcFwiLCBcImVudk1hcE1vZGVcIiwgXCJlbnZNYXBFbmNvZGluZ1wiLFxuXHRcdFx0XCJsaWdodE1hcFwiLCBcImFvTWFwXCIsIFwiZW1pc3NpdmVNYXBcIiwgXCJlbWlzc2l2ZU1hcEVuY29kaW5nXCIsIFwiYnVtcE1hcFwiLCBcIm5vcm1hbE1hcFwiLCBcImRpc3BsYWNlbWVudE1hcFwiLCBcInNwZWN1bGFyTWFwXCIsXG5cdFx0XHRcInJvdWdobmVzc01hcFwiLCBcIm1ldGFsbmVzc01hcFwiLCBcImdyYWRpZW50TWFwXCIsXG5cdFx0XHRcImFscGhhTWFwXCIsIFwiY29tYmluZVwiLCBcInZlcnRleENvbG9yc1wiLCBcImZvZ1wiLCBcInVzZUZvZ1wiLCBcImZvZ0V4cFwiLFxuXHRcdFx0XCJmbGF0U2hhZGluZ1wiLCBcInNpemVBdHRlbnVhdGlvblwiLCBcImxvZ2FyaXRobWljRGVwdGhCdWZmZXJcIiwgXCJza2lubmluZ1wiLFxuXHRcdFx0XCJtYXhCb25lc1wiLCBcInVzZVZlcnRleFRleHR1cmVcIiwgXCJtb3JwaFRhcmdldHNcIiwgXCJtb3JwaE5vcm1hbHNcIixcblx0XHRcdFwibWF4TW9ycGhUYXJnZXRzXCIsIFwibWF4TW9ycGhOb3JtYWxzXCIsIFwicHJlbXVsdGlwbGllZEFscGhhXCIsXG5cdFx0XHRcIm51bURpckxpZ2h0c1wiLCBcIm51bVBvaW50TGlnaHRzXCIsIFwibnVtU3BvdExpZ2h0c1wiLCBcIm51bUhlbWlMaWdodHNcIiwgXCJudW1SZWN0QXJlYUxpZ2h0c1wiLFxuXHRcdFx0XCJzaGFkb3dNYXBFbmFibGVkXCIsIFwic2hhZG93TWFwVHlwZVwiLCBcInRvbmVNYXBwaW5nXCIsICdwaHlzaWNhbGx5Q29ycmVjdExpZ2h0cycsXG5cdFx0XHRcImFscGhhVGVzdFwiLCBcImRvdWJsZVNpZGVkXCIsIFwiZmxpcFNpZGVkXCIsIFwibnVtQ2xpcHBpbmdQbGFuZXNcIiwgXCJudW1DbGlwSW50ZXJzZWN0aW9uXCIsIFwiZGVwdGhQYWNraW5nXCJcblx0XHRdO1xuXG5cblx0XHRmdW5jdGlvbiBhbGxvY2F0ZUJvbmVzKCBvYmplY3QgKSB7XG5cblx0XHRcdGlmICggY2FwYWJpbGl0aWVzLmZsb2F0VmVydGV4VGV4dHVyZXMgJiYgb2JqZWN0ICYmIG9iamVjdC5za2VsZXRvbiAmJiBvYmplY3Quc2tlbGV0b24udXNlVmVydGV4VGV4dHVyZSApIHtcblxuXHRcdFx0XHRyZXR1cm4gMTAyNDtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHQvLyBkZWZhdWx0IGZvciB3aGVuIG9iamVjdCBpcyBub3Qgc3BlY2lmaWVkXG5cdFx0XHRcdC8vICggZm9yIGV4YW1wbGUgd2hlbiBwcmVidWlsZGluZyBzaGFkZXIgdG8gYmUgdXNlZCB3aXRoIG11bHRpcGxlIG9iamVjdHMgKVxuXHRcdFx0XHQvL1xuXHRcdFx0XHQvLyAgLSBsZWF2ZSBzb21lIGV4dHJhIHNwYWNlIGZvciBvdGhlciB1bmlmb3Jtc1xuXHRcdFx0XHQvLyAgLSBsaW1pdCBoZXJlIGlzIEFOR0xFJ3MgMjU0IG1heCB1bmlmb3JtIHZlY3RvcnNcblx0XHRcdFx0Ly8gICAgKHVwIHRvIDU0IHNob3VsZCBiZSBzYWZlKVxuXG5cdFx0XHRcdHZhciBuVmVydGV4VW5pZm9ybXMgPSBjYXBhYmlsaXRpZXMubWF4VmVydGV4VW5pZm9ybXM7XG5cdFx0XHRcdHZhciBuVmVydGV4TWF0cmljZXMgPSBNYXRoLmZsb29yKCAoIG5WZXJ0ZXhVbmlmb3JtcyAtIDIwICkgLyA0ICk7XG5cblx0XHRcdFx0dmFyIG1heEJvbmVzID0gblZlcnRleE1hdHJpY2VzO1xuXG5cdFx0XHRcdGlmICggb2JqZWN0ICE9PSB1bmRlZmluZWQgJiYgKG9iamVjdCAmJiBvYmplY3QuaXNTa2lubmVkTWVzaCkgKSB7XG5cblx0XHRcdFx0XHRtYXhCb25lcyA9IE1hdGgubWluKCBvYmplY3Quc2tlbGV0b24uYm9uZXMubGVuZ3RoLCBtYXhCb25lcyApO1xuXG5cdFx0XHRcdFx0aWYgKCBtYXhCb25lcyA8IG9iamVjdC5za2VsZXRvbi5ib25lcy5sZW5ndGggKSB7XG5cblx0XHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1dlYkdMUmVuZGVyZXI6IHRvbyBtYW55IGJvbmVzIC0gJyArIG9iamVjdC5za2VsZXRvbi5ib25lcy5sZW5ndGggKyAnLCB0aGlzIEdQVSBzdXBwb3J0cyBqdXN0ICcgKyBtYXhCb25lcyArICcgKHRyeSBPcGVuR0wgaW5zdGVhZCBvZiBBTkdMRSknICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJldHVybiBtYXhCb25lcztcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gZ2V0VGV4dHVyZUVuY29kaW5nRnJvbU1hcCggbWFwLCBnYW1tYU92ZXJyaWRlTGluZWFyICkge1xuXG5cdFx0XHR2YXIgZW5jb2Rpbmc7XG5cblx0XHRcdGlmICggISBtYXAgKSB7XG5cblx0XHRcdFx0ZW5jb2RpbmcgPSBMaW5lYXJFbmNvZGluZztcblxuXHRcdFx0fSBlbHNlIGlmICggbWFwLmlzVGV4dHVyZSApIHtcblxuXHRcdFx0XHRlbmNvZGluZyA9IG1hcC5lbmNvZGluZztcblxuXHRcdFx0fSBlbHNlIGlmICggbWFwLmlzV2ViR0xSZW5kZXJUYXJnZXQgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCBcIlRIUkVFLldlYkdMUHJvZ3JhbXMuZ2V0VGV4dHVyZUVuY29kaW5nRnJvbU1hcDogZG9uJ3QgdXNlIHJlbmRlciB0YXJnZXRzIGFzIHRleHR1cmVzLiBVc2UgdGhlaXIgLnRleHR1cmUgcHJvcGVydHkgaW5zdGVhZC5cIiApO1xuXHRcdFx0XHRlbmNvZGluZyA9IG1hcC50ZXh0dXJlLmVuY29kaW5nO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIGFkZCBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eSBmb3IgV2ViR0xSZW5kZXJlci5nYW1tYUlucHV0L2dhbW1hT3V0cHV0IHBhcmFtZXRlciwgc2hvdWxkIHByb2JhYmx5IGJlIHJlbW92ZWQgYXQgc29tZSBwb2ludC5cblx0XHRcdGlmICggZW5jb2RpbmcgPT09IExpbmVhckVuY29kaW5nICYmIGdhbW1hT3ZlcnJpZGVMaW5lYXIgKSB7XG5cblx0XHRcdFx0ZW5jb2RpbmcgPSBHYW1tYUVuY29kaW5nO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBlbmNvZGluZztcblxuXHRcdH1cblxuXHRcdHRoaXMuZ2V0UGFyYW1ldGVycyA9IGZ1bmN0aW9uICggbWF0ZXJpYWwsIGxpZ2h0cywgZm9nLCBuQ2xpcFBsYW5lcywgbkNsaXBJbnRlcnNlY3Rpb24sIG9iamVjdCApIHtcblxuXHRcdFx0dmFyIHNoYWRlcklEID0gc2hhZGVySURzWyBtYXRlcmlhbC50eXBlIF07XG5cblx0XHRcdC8vIGhldXJpc3RpY3MgdG8gY3JlYXRlIHNoYWRlciBwYXJhbWV0ZXJzIGFjY29yZGluZyB0byBsaWdodHMgaW4gdGhlIHNjZW5lXG5cdFx0XHQvLyAobm90IHRvIGJsb3cgb3ZlciBtYXhMaWdodHMgYnVkZ2V0KVxuXG5cdFx0XHR2YXIgbWF4Qm9uZXMgPSBhbGxvY2F0ZUJvbmVzKCBvYmplY3QgKTtcblx0XHRcdHZhciBwcmVjaXNpb24gPSByZW5kZXJlci5nZXRQcmVjaXNpb24oKTtcblxuXHRcdFx0aWYgKCBtYXRlcmlhbC5wcmVjaXNpb24gIT09IG51bGwgKSB7XG5cblx0XHRcdFx0cHJlY2lzaW9uID0gY2FwYWJpbGl0aWVzLmdldE1heFByZWNpc2lvbiggbWF0ZXJpYWwucHJlY2lzaW9uICk7XG5cblx0XHRcdFx0aWYgKCBwcmVjaXNpb24gIT09IG1hdGVyaWFsLnByZWNpc2lvbiApIHtcblxuXHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUHJvZ3JhbS5nZXRQYXJhbWV0ZXJzOicsIG1hdGVyaWFsLnByZWNpc2lvbiwgJ25vdCBzdXBwb3J0ZWQsIHVzaW5nJywgcHJlY2lzaW9uLCAnaW5zdGVhZC4nICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBjdXJyZW50UmVuZGVyVGFyZ2V0ID0gcmVuZGVyZXIuZ2V0Q3VycmVudFJlbmRlclRhcmdldCgpO1xuXG5cdFx0XHR2YXIgcGFyYW1ldGVycyA9IHtcblxuXHRcdFx0XHRzaGFkZXJJRDogc2hhZGVySUQsXG5cblx0XHRcdFx0cHJlY2lzaW9uOiBwcmVjaXNpb24sXG5cdFx0XHRcdHN1cHBvcnRzVmVydGV4VGV4dHVyZXM6IGNhcGFiaWxpdGllcy52ZXJ0ZXhUZXh0dXJlcyxcblx0XHRcdFx0b3V0cHV0RW5jb2Rpbmc6IGdldFRleHR1cmVFbmNvZGluZ0Zyb21NYXAoICggISBjdXJyZW50UmVuZGVyVGFyZ2V0ICkgPyBudWxsIDogY3VycmVudFJlbmRlclRhcmdldC50ZXh0dXJlLCByZW5kZXJlci5nYW1tYU91dHB1dCApLFxuXHRcdFx0XHRtYXA6ICEhIG1hdGVyaWFsLm1hcCxcblx0XHRcdFx0bWFwRW5jb2Rpbmc6IGdldFRleHR1cmVFbmNvZGluZ0Zyb21NYXAoIG1hdGVyaWFsLm1hcCwgcmVuZGVyZXIuZ2FtbWFJbnB1dCApLFxuXHRcdFx0XHRlbnZNYXA6ICEhIG1hdGVyaWFsLmVudk1hcCxcblx0XHRcdFx0ZW52TWFwTW9kZTogbWF0ZXJpYWwuZW52TWFwICYmIG1hdGVyaWFsLmVudk1hcC5tYXBwaW5nLFxuXHRcdFx0XHRlbnZNYXBFbmNvZGluZzogZ2V0VGV4dHVyZUVuY29kaW5nRnJvbU1hcCggbWF0ZXJpYWwuZW52TWFwLCByZW5kZXJlci5nYW1tYUlucHV0ICksXG5cdFx0XHRcdGVudk1hcEN1YmVVVjogKCAhISBtYXRlcmlhbC5lbnZNYXAgKSAmJiAoICggbWF0ZXJpYWwuZW52TWFwLm1hcHBpbmcgPT09IEN1YmVVVlJlZmxlY3Rpb25NYXBwaW5nICkgfHwgKCBtYXRlcmlhbC5lbnZNYXAubWFwcGluZyA9PT0gQ3ViZVVWUmVmcmFjdGlvbk1hcHBpbmcgKSApLFxuXHRcdFx0XHRsaWdodE1hcDogISEgbWF0ZXJpYWwubGlnaHRNYXAsXG5cdFx0XHRcdGFvTWFwOiAhISBtYXRlcmlhbC5hb01hcCxcblx0XHRcdFx0ZW1pc3NpdmVNYXA6ICEhIG1hdGVyaWFsLmVtaXNzaXZlTWFwLFxuXHRcdFx0XHRlbWlzc2l2ZU1hcEVuY29kaW5nOiBnZXRUZXh0dXJlRW5jb2RpbmdGcm9tTWFwKCBtYXRlcmlhbC5lbWlzc2l2ZU1hcCwgcmVuZGVyZXIuZ2FtbWFJbnB1dCApLFxuXHRcdFx0XHRidW1wTWFwOiAhISBtYXRlcmlhbC5idW1wTWFwLFxuXHRcdFx0XHRub3JtYWxNYXA6ICEhIG1hdGVyaWFsLm5vcm1hbE1hcCxcblx0XHRcdFx0ZGlzcGxhY2VtZW50TWFwOiAhISBtYXRlcmlhbC5kaXNwbGFjZW1lbnRNYXAsXG5cdFx0XHRcdHJvdWdobmVzc01hcDogISEgbWF0ZXJpYWwucm91Z2huZXNzTWFwLFxuXHRcdFx0XHRtZXRhbG5lc3NNYXA6ICEhIG1hdGVyaWFsLm1ldGFsbmVzc01hcCxcblx0XHRcdFx0c3BlY3VsYXJNYXA6ICEhIG1hdGVyaWFsLnNwZWN1bGFyTWFwLFxuXHRcdFx0XHRhbHBoYU1hcDogISEgbWF0ZXJpYWwuYWxwaGFNYXAsXG5cblx0XHRcdFx0Z3JhZGllbnRNYXA6ICEhIG1hdGVyaWFsLmdyYWRpZW50TWFwLFxuXG5cdFx0XHRcdGNvbWJpbmU6IG1hdGVyaWFsLmNvbWJpbmUsXG5cblx0XHRcdFx0dmVydGV4Q29sb3JzOiBtYXRlcmlhbC52ZXJ0ZXhDb2xvcnMsXG5cblx0XHRcdFx0Zm9nOiAhISBmb2csXG5cdFx0XHRcdHVzZUZvZzogbWF0ZXJpYWwuZm9nLFxuXHRcdFx0XHRmb2dFeHA6IChmb2cgJiYgZm9nLmlzRm9nRXhwMiksXG5cblx0XHRcdFx0ZmxhdFNoYWRpbmc6IG1hdGVyaWFsLnNoYWRpbmcgPT09IEZsYXRTaGFkaW5nLFxuXG5cdFx0XHRcdHNpemVBdHRlbnVhdGlvbjogbWF0ZXJpYWwuc2l6ZUF0dGVudWF0aW9uLFxuXHRcdFx0XHRsb2dhcml0aG1pY0RlcHRoQnVmZmVyOiBjYXBhYmlsaXRpZXMubG9nYXJpdGhtaWNEZXB0aEJ1ZmZlcixcblxuXHRcdFx0XHRza2lubmluZzogbWF0ZXJpYWwuc2tpbm5pbmcsXG5cdFx0XHRcdG1heEJvbmVzOiBtYXhCb25lcyxcblx0XHRcdFx0dXNlVmVydGV4VGV4dHVyZTogY2FwYWJpbGl0aWVzLmZsb2F0VmVydGV4VGV4dHVyZXMgJiYgb2JqZWN0ICYmIG9iamVjdC5za2VsZXRvbiAmJiBvYmplY3Quc2tlbGV0b24udXNlVmVydGV4VGV4dHVyZSxcblxuXHRcdFx0XHRtb3JwaFRhcmdldHM6IG1hdGVyaWFsLm1vcnBoVGFyZ2V0cyxcblx0XHRcdFx0bW9ycGhOb3JtYWxzOiBtYXRlcmlhbC5tb3JwaE5vcm1hbHMsXG5cdFx0XHRcdG1heE1vcnBoVGFyZ2V0czogcmVuZGVyZXIubWF4TW9ycGhUYXJnZXRzLFxuXHRcdFx0XHRtYXhNb3JwaE5vcm1hbHM6IHJlbmRlcmVyLm1heE1vcnBoTm9ybWFscyxcblxuXHRcdFx0XHRudW1EaXJMaWdodHM6IGxpZ2h0cy5kaXJlY3Rpb25hbC5sZW5ndGgsXG5cdFx0XHRcdG51bVBvaW50TGlnaHRzOiBsaWdodHMucG9pbnQubGVuZ3RoLFxuXHRcdFx0XHRudW1TcG90TGlnaHRzOiBsaWdodHMuc3BvdC5sZW5ndGgsXG5cdFx0XHRcdG51bVJlY3RBcmVhTGlnaHRzOiBsaWdodHMucmVjdEFyZWEubGVuZ3RoLFxuXHRcdFx0XHRudW1IZW1pTGlnaHRzOiBsaWdodHMuaGVtaS5sZW5ndGgsXG5cblx0XHRcdFx0bnVtQ2xpcHBpbmdQbGFuZXM6IG5DbGlwUGxhbmVzLFxuXHRcdFx0XHRudW1DbGlwSW50ZXJzZWN0aW9uOiBuQ2xpcEludGVyc2VjdGlvbixcblxuXHRcdFx0XHRzaGFkb3dNYXBFbmFibGVkOiByZW5kZXJlci5zaGFkb3dNYXAuZW5hYmxlZCAmJiBvYmplY3QucmVjZWl2ZVNoYWRvdyAmJiBsaWdodHMuc2hhZG93cy5sZW5ndGggPiAwLFxuXHRcdFx0XHRzaGFkb3dNYXBUeXBlOiByZW5kZXJlci5zaGFkb3dNYXAudHlwZSxcblxuXHRcdFx0XHR0b25lTWFwcGluZzogcmVuZGVyZXIudG9uZU1hcHBpbmcsXG5cdFx0XHRcdHBoeXNpY2FsbHlDb3JyZWN0TGlnaHRzOiByZW5kZXJlci5waHlzaWNhbGx5Q29ycmVjdExpZ2h0cyxcblxuXHRcdFx0XHRwcmVtdWx0aXBsaWVkQWxwaGE6IG1hdGVyaWFsLnByZW11bHRpcGxpZWRBbHBoYSxcblxuXHRcdFx0XHRhbHBoYVRlc3Q6IG1hdGVyaWFsLmFscGhhVGVzdCxcblx0XHRcdFx0ZG91YmxlU2lkZWQ6IG1hdGVyaWFsLnNpZGUgPT09IERvdWJsZVNpZGUsXG5cdFx0XHRcdGZsaXBTaWRlZDogbWF0ZXJpYWwuc2lkZSA9PT0gQmFja1NpZGUsXG5cblx0XHRcdFx0ZGVwdGhQYWNraW5nOiAoIG1hdGVyaWFsLmRlcHRoUGFja2luZyAhPT0gdW5kZWZpbmVkICkgPyBtYXRlcmlhbC5kZXB0aFBhY2tpbmcgOiBmYWxzZVxuXG5cdFx0XHR9O1xuXG5cdFx0XHRyZXR1cm4gcGFyYW1ldGVycztcblxuXHRcdH07XG5cblx0XHR0aGlzLmdldFByb2dyYW1Db2RlID0gZnVuY3Rpb24gKCBtYXRlcmlhbCwgcGFyYW1ldGVycyApIHtcblxuXHRcdFx0dmFyIGFycmF5ID0gW107XG5cblx0XHRcdGlmICggcGFyYW1ldGVycy5zaGFkZXJJRCApIHtcblxuXHRcdFx0XHRhcnJheS5wdXNoKCBwYXJhbWV0ZXJzLnNoYWRlcklEICk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0YXJyYXkucHVzaCggbWF0ZXJpYWwuZnJhZ21lbnRTaGFkZXIgKTtcblx0XHRcdFx0YXJyYXkucHVzaCggbWF0ZXJpYWwudmVydGV4U2hhZGVyICk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBtYXRlcmlhbC5kZWZpbmVzICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0Zm9yICggdmFyIG5hbWUgaW4gbWF0ZXJpYWwuZGVmaW5lcyApIHtcblxuXHRcdFx0XHRcdGFycmF5LnB1c2goIG5hbWUgKTtcblx0XHRcdFx0XHRhcnJheS5wdXNoKCBtYXRlcmlhbC5kZWZpbmVzWyBuYW1lIF0gKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgcGFyYW1ldGVyTmFtZXMubGVuZ3RoOyBpICsrICkge1xuXG5cdFx0XHRcdGFycmF5LnB1c2goIHBhcmFtZXRlcnNbIHBhcmFtZXRlck5hbWVzWyBpIF0gXSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBhcnJheS5qb2luKCk7XG5cblx0XHR9O1xuXG5cdFx0dGhpcy5hY3F1aXJlUHJvZ3JhbSA9IGZ1bmN0aW9uICggbWF0ZXJpYWwsIHBhcmFtZXRlcnMsIGNvZGUgKSB7XG5cblx0XHRcdHZhciBwcm9ncmFtO1xuXG5cdFx0XHQvLyBDaGVjayBpZiBjb2RlIGhhcyBiZWVuIGFscmVhZHkgY29tcGlsZWRcblx0XHRcdGZvciAoIHZhciBwID0gMCwgcGwgPSBwcm9ncmFtcy5sZW5ndGg7IHAgPCBwbDsgcCArKyApIHtcblxuXHRcdFx0XHR2YXIgcHJvZ3JhbUluZm8gPSBwcm9ncmFtc1sgcCBdO1xuXG5cdFx0XHRcdGlmICggcHJvZ3JhbUluZm8uY29kZSA9PT0gY29kZSApIHtcblxuXHRcdFx0XHRcdHByb2dyYW0gPSBwcm9ncmFtSW5mbztcblx0XHRcdFx0XHQrKyBwcm9ncmFtLnVzZWRUaW1lcztcblxuXHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHByb2dyYW0gPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRwcm9ncmFtID0gbmV3IFdlYkdMUHJvZ3JhbSggcmVuZGVyZXIsIGNvZGUsIG1hdGVyaWFsLCBwYXJhbWV0ZXJzICk7XG5cdFx0XHRcdHByb2dyYW1zLnB1c2goIHByb2dyYW0gKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gcHJvZ3JhbTtcblxuXHRcdH07XG5cblx0XHR0aGlzLnJlbGVhc2VQcm9ncmFtID0gZnVuY3Rpb24oIHByb2dyYW0gKSB7XG5cblx0XHRcdGlmICggLS0gcHJvZ3JhbS51c2VkVGltZXMgPT09IDAgKSB7XG5cblx0XHRcdFx0Ly8gUmVtb3ZlIGZyb20gdW5vcmRlcmVkIHNldFxuXHRcdFx0XHR2YXIgaSA9IHByb2dyYW1zLmluZGV4T2YoIHByb2dyYW0gKTtcblx0XHRcdFx0cHJvZ3JhbXNbIGkgXSA9IHByb2dyYW1zWyBwcm9ncmFtcy5sZW5ndGggLSAxIF07XG5cdFx0XHRcdHByb2dyYW1zLnBvcCgpO1xuXG5cdFx0XHRcdC8vIEZyZWUgV2ViR0wgcmVzb3VyY2VzXG5cdFx0XHRcdHByb2dyYW0uZGVzdHJveSgpO1xuXG5cdFx0XHR9XG5cblx0XHR9O1xuXG5cdFx0Ly8gRXhwb3NlZCBmb3IgcmVzb3VyY2UgbW9uaXRvcmluZyAmIGVycm9yIGZlZWRiYWNrIHZpYSByZW5kZXJlci5pbmZvOlxuXHRcdHRoaXMucHJvZ3JhbXMgPSBwcm9ncmFtcztcblxuXHR9XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFdlYkdMR2VvbWV0cmllcyggZ2wsIHByb3BlcnRpZXMsIGluZm8gKSB7XG5cblx0XHR2YXIgZ2VvbWV0cmllcyA9IHt9O1xuXG5cdFx0ZnVuY3Rpb24gb25HZW9tZXRyeURpc3Bvc2UoIGV2ZW50ICkge1xuXG5cdFx0XHR2YXIgZ2VvbWV0cnkgPSBldmVudC50YXJnZXQ7XG5cdFx0XHR2YXIgYnVmZmVyZ2VvbWV0cnkgPSBnZW9tZXRyaWVzWyBnZW9tZXRyeS5pZCBdO1xuXG5cdFx0XHRpZiAoIGJ1ZmZlcmdlb21ldHJ5LmluZGV4ICE9PSBudWxsICkge1xuXG5cdFx0XHRcdGRlbGV0ZUF0dHJpYnV0ZSggYnVmZmVyZ2VvbWV0cnkuaW5kZXggKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRkZWxldGVBdHRyaWJ1dGVzKCBidWZmZXJnZW9tZXRyeS5hdHRyaWJ1dGVzICk7XG5cblx0XHRcdGdlb21ldHJ5LnJlbW92ZUV2ZW50TGlzdGVuZXIoICdkaXNwb3NlJywgb25HZW9tZXRyeURpc3Bvc2UgKTtcblxuXHRcdFx0ZGVsZXRlIGdlb21ldHJpZXNbIGdlb21ldHJ5LmlkIF07XG5cblx0XHRcdC8vIFRPRE9cblxuXHRcdFx0dmFyIHByb3BlcnR5ID0gcHJvcGVydGllcy5nZXQoIGdlb21ldHJ5ICk7XG5cblx0XHRcdGlmICggcHJvcGVydHkud2lyZWZyYW1lICkge1xuXG5cdFx0XHRcdGRlbGV0ZUF0dHJpYnV0ZSggcHJvcGVydHkud2lyZWZyYW1lICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cHJvcGVydGllcy5kZWxldGUoIGdlb21ldHJ5ICk7XG5cblx0XHRcdHZhciBidWZmZXJwcm9wZXJ0eSA9IHByb3BlcnRpZXMuZ2V0KCBidWZmZXJnZW9tZXRyeSApO1xuXG5cdFx0XHRpZiAoIGJ1ZmZlcnByb3BlcnR5LndpcmVmcmFtZSApIHtcblxuXHRcdFx0XHRkZWxldGVBdHRyaWJ1dGUoIGJ1ZmZlcnByb3BlcnR5LndpcmVmcmFtZSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHByb3BlcnRpZXMuZGVsZXRlKCBidWZmZXJnZW9tZXRyeSApO1xuXG5cdFx0XHQvL1xuXG5cdFx0XHRpbmZvLm1lbW9yeS5nZW9tZXRyaWVzIC0tO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gZ2V0QXR0cmlidXRlQnVmZmVyKCBhdHRyaWJ1dGUgKSB7XG5cblx0XHRcdGlmICggYXR0cmlidXRlLmlzSW50ZXJsZWF2ZWRCdWZmZXJBdHRyaWJ1dGUgKSB7XG5cblx0XHRcdFx0cmV0dXJuIHByb3BlcnRpZXMuZ2V0KCBhdHRyaWJ1dGUuZGF0YSApLl9fd2ViZ2xCdWZmZXI7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHByb3BlcnRpZXMuZ2V0KCBhdHRyaWJ1dGUgKS5fX3dlYmdsQnVmZmVyO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gZGVsZXRlQXR0cmlidXRlKCBhdHRyaWJ1dGUgKSB7XG5cblx0XHRcdHZhciBidWZmZXIgPSBnZXRBdHRyaWJ1dGVCdWZmZXIoIGF0dHJpYnV0ZSApO1xuXG5cdFx0XHRpZiAoIGJ1ZmZlciAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdGdsLmRlbGV0ZUJ1ZmZlciggYnVmZmVyICk7XG5cdFx0XHRcdHJlbW92ZUF0dHJpYnV0ZUJ1ZmZlciggYXR0cmlidXRlICk7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIGRlbGV0ZUF0dHJpYnV0ZXMoIGF0dHJpYnV0ZXMgKSB7XG5cblx0XHRcdGZvciAoIHZhciBuYW1lIGluIGF0dHJpYnV0ZXMgKSB7XG5cblx0XHRcdFx0ZGVsZXRlQXR0cmlidXRlKCBhdHRyaWJ1dGVzWyBuYW1lIF0gKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gcmVtb3ZlQXR0cmlidXRlQnVmZmVyKCBhdHRyaWJ1dGUgKSB7XG5cblx0XHRcdGlmICggYXR0cmlidXRlLmlzSW50ZXJsZWF2ZWRCdWZmZXJBdHRyaWJ1dGUgKSB7XG5cblx0XHRcdFx0cHJvcGVydGllcy5kZWxldGUoIGF0dHJpYnV0ZS5kYXRhICk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0cHJvcGVydGllcy5kZWxldGUoIGF0dHJpYnV0ZSApO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHRyZXR1cm4ge1xuXG5cdFx0XHRnZXQ6IGZ1bmN0aW9uICggb2JqZWN0ICkge1xuXG5cdFx0XHRcdHZhciBnZW9tZXRyeSA9IG9iamVjdC5nZW9tZXRyeTtcblxuXHRcdFx0XHRpZiAoIGdlb21ldHJpZXNbIGdlb21ldHJ5LmlkIF0gIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdHJldHVybiBnZW9tZXRyaWVzWyBnZW9tZXRyeS5pZCBdO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRnZW9tZXRyeS5hZGRFdmVudExpc3RlbmVyKCAnZGlzcG9zZScsIG9uR2VvbWV0cnlEaXNwb3NlICk7XG5cblx0XHRcdFx0dmFyIGJ1ZmZlcmdlb21ldHJ5O1xuXG5cdFx0XHRcdGlmICggZ2VvbWV0cnkuaXNCdWZmZXJHZW9tZXRyeSApIHtcblxuXHRcdFx0XHRcdGJ1ZmZlcmdlb21ldHJ5ID0gZ2VvbWV0cnk7XG5cblx0XHRcdFx0fSBlbHNlIGlmICggZ2VvbWV0cnkuaXNHZW9tZXRyeSApIHtcblxuXHRcdFx0XHRcdGlmICggZ2VvbWV0cnkuX2J1ZmZlckdlb21ldHJ5ID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRcdGdlb21ldHJ5Ll9idWZmZXJHZW9tZXRyeSA9IG5ldyBCdWZmZXJHZW9tZXRyeSgpLnNldEZyb21PYmplY3QoIG9iamVjdCApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0YnVmZmVyZ2VvbWV0cnkgPSBnZW9tZXRyeS5fYnVmZmVyR2VvbWV0cnk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGdlb21ldHJpZXNbIGdlb21ldHJ5LmlkIF0gPSBidWZmZXJnZW9tZXRyeTtcblxuXHRcdFx0XHRpbmZvLm1lbW9yeS5nZW9tZXRyaWVzICsrO1xuXG5cdFx0XHRcdHJldHVybiBidWZmZXJnZW9tZXRyeTtcblxuXHRcdFx0fVxuXG5cdFx0fTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFdlYkdMT2JqZWN0cyggZ2wsIHByb3BlcnRpZXMsIGluZm8gKSB7XG5cblx0XHR2YXIgZ2VvbWV0cmllcyA9IG5ldyBXZWJHTEdlb21ldHJpZXMoIGdsLCBwcm9wZXJ0aWVzLCBpbmZvICk7XG5cblx0XHQvL1xuXG5cdFx0ZnVuY3Rpb24gdXBkYXRlKCBvYmplY3QgKSB7XG5cblx0XHRcdC8vIFRPRE86IEF2b2lkIHVwZGF0aW5nIHR3aWNlICh3aGVuIHVzaW5nIHNoYWRvd01hcCkuIE1heWJlIGFkZCBmcmFtZSBjb3VudGVyLlxuXG5cdFx0XHR2YXIgZ2VvbWV0cnkgPSBnZW9tZXRyaWVzLmdldCggb2JqZWN0ICk7XG5cblx0XHRcdGlmICggb2JqZWN0Lmdlb21ldHJ5LmlzR2VvbWV0cnkgKSB7XG5cblx0XHRcdFx0Z2VvbWV0cnkudXBkYXRlRnJvbU9iamVjdCggb2JqZWN0ICk7XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIGluZGV4ID0gZ2VvbWV0cnkuaW5kZXg7XG5cdFx0XHR2YXIgYXR0cmlidXRlcyA9IGdlb21ldHJ5LmF0dHJpYnV0ZXM7XG5cblx0XHRcdGlmICggaW5kZXggIT09IG51bGwgKSB7XG5cblx0XHRcdFx0dXBkYXRlQXR0cmlidXRlKCBpbmRleCwgZ2wuRUxFTUVOVF9BUlJBWV9CVUZGRVIgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRmb3IgKCB2YXIgbmFtZSBpbiBhdHRyaWJ1dGVzICkge1xuXG5cdFx0XHRcdHVwZGF0ZUF0dHJpYnV0ZSggYXR0cmlidXRlc1sgbmFtZSBdLCBnbC5BUlJBWV9CVUZGRVIgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyBtb3JwaCB0YXJnZXRzXG5cblx0XHRcdHZhciBtb3JwaEF0dHJpYnV0ZXMgPSBnZW9tZXRyeS5tb3JwaEF0dHJpYnV0ZXM7XG5cblx0XHRcdGZvciAoIHZhciBuYW1lIGluIG1vcnBoQXR0cmlidXRlcyApIHtcblxuXHRcdFx0XHR2YXIgYXJyYXkgPSBtb3JwaEF0dHJpYnV0ZXNbIG5hbWUgXTtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBhcnJheS5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdFx0dXBkYXRlQXR0cmlidXRlKCBhcnJheVsgaSBdLCBnbC5BUlJBWV9CVUZGRVIgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIGdlb21ldHJ5O1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gdXBkYXRlQXR0cmlidXRlKCBhdHRyaWJ1dGUsIGJ1ZmZlclR5cGUgKSB7XG5cblx0XHRcdHZhciBkYXRhID0gKCBhdHRyaWJ1dGUuaXNJbnRlcmxlYXZlZEJ1ZmZlckF0dHJpYnV0ZSApID8gYXR0cmlidXRlLmRhdGEgOiBhdHRyaWJ1dGU7XG5cblx0XHRcdHZhciBhdHRyaWJ1dGVQcm9wZXJ0aWVzID0gcHJvcGVydGllcy5nZXQoIGRhdGEgKTtcblxuXHRcdFx0aWYgKCBhdHRyaWJ1dGVQcm9wZXJ0aWVzLl9fd2ViZ2xCdWZmZXIgPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRjcmVhdGVCdWZmZXIoIGF0dHJpYnV0ZVByb3BlcnRpZXMsIGRhdGEsIGJ1ZmZlclR5cGUgKTtcblxuXHRcdFx0fSBlbHNlIGlmICggYXR0cmlidXRlUHJvcGVydGllcy52ZXJzaW9uICE9PSBkYXRhLnZlcnNpb24gKSB7XG5cblx0XHRcdFx0dXBkYXRlQnVmZmVyKCBhdHRyaWJ1dGVQcm9wZXJ0aWVzLCBkYXRhLCBidWZmZXJUeXBlICk7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIGNyZWF0ZUJ1ZmZlciggYXR0cmlidXRlUHJvcGVydGllcywgZGF0YSwgYnVmZmVyVHlwZSApIHtcblxuXHRcdFx0YXR0cmlidXRlUHJvcGVydGllcy5fX3dlYmdsQnVmZmVyID0gZ2wuY3JlYXRlQnVmZmVyKCk7XG5cdFx0XHRnbC5iaW5kQnVmZmVyKCBidWZmZXJUeXBlLCBhdHRyaWJ1dGVQcm9wZXJ0aWVzLl9fd2ViZ2xCdWZmZXIgKTtcblxuXHRcdFx0dmFyIHVzYWdlID0gZGF0YS5keW5hbWljID8gZ2wuRFlOQU1JQ19EUkFXIDogZ2wuU1RBVElDX0RSQVc7XG5cblx0XHRcdGdsLmJ1ZmZlckRhdGEoIGJ1ZmZlclR5cGUsIGRhdGEuYXJyYXksIHVzYWdlICk7XG5cblx0XHRcdHZhciB0eXBlID0gZ2wuRkxPQVQ7XG5cdFx0XHR2YXIgYXJyYXkgPSBkYXRhLmFycmF5O1xuXG5cdFx0XHRpZiAoIGFycmF5IGluc3RhbmNlb2YgRmxvYXQzMkFycmF5ICkge1xuXG5cdFx0XHRcdHR5cGUgPSBnbC5GTE9BVDtcblxuXHRcdFx0fSBlbHNlIGlmICggYXJyYXkgaW5zdGFuY2VvZiBGbG9hdDY0QXJyYXkgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCBcIlVuc3VwcG9ydGVkIGRhdGEgYnVmZmVyIGZvcm1hdDogRmxvYXQ2NEFycmF5XCIgKTtcblxuXHRcdFx0fSBlbHNlIGlmICggYXJyYXkgaW5zdGFuY2VvZiBVaW50MTZBcnJheSApIHtcblxuXHRcdFx0XHR0eXBlID0gZ2wuVU5TSUdORURfU0hPUlQ7XG5cblx0XHRcdH0gZWxzZSBpZiAoIGFycmF5IGluc3RhbmNlb2YgSW50MTZBcnJheSApIHtcblxuXHRcdFx0XHR0eXBlID0gZ2wuU0hPUlQ7XG5cblx0XHRcdH0gZWxzZSBpZiAoIGFycmF5IGluc3RhbmNlb2YgVWludDMyQXJyYXkgKSB7XG5cblx0XHRcdFx0dHlwZSA9IGdsLlVOU0lHTkVEX0lOVDtcblxuXHRcdFx0fSBlbHNlIGlmICggYXJyYXkgaW5zdGFuY2VvZiBJbnQzMkFycmF5ICkge1xuXG5cdFx0XHRcdHR5cGUgPSBnbC5JTlQ7XG5cblx0XHRcdH0gZWxzZSBpZiAoIGFycmF5IGluc3RhbmNlb2YgSW50OEFycmF5ICkge1xuXG5cdFx0XHRcdHR5cGUgPSBnbC5CWVRFO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCBhcnJheSBpbnN0YW5jZW9mIFVpbnQ4QXJyYXkgKSB7XG5cblx0XHRcdFx0dHlwZSA9IGdsLlVOU0lHTkVEX0JZVEU7XG5cblx0XHRcdH1cblxuXHRcdFx0YXR0cmlidXRlUHJvcGVydGllcy5ieXRlc1BlckVsZW1lbnQgPSBhcnJheS5CWVRFU19QRVJfRUxFTUVOVDtcblx0XHRcdGF0dHJpYnV0ZVByb3BlcnRpZXMudHlwZSA9IHR5cGU7XG5cdFx0XHRhdHRyaWJ1dGVQcm9wZXJ0aWVzLnZlcnNpb24gPSBkYXRhLnZlcnNpb247XG5cblx0XHRcdGRhdGEub25VcGxvYWRDYWxsYmFjaygpO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gdXBkYXRlQnVmZmVyKCBhdHRyaWJ1dGVQcm9wZXJ0aWVzLCBkYXRhLCBidWZmZXJUeXBlICkge1xuXG5cdFx0XHRnbC5iaW5kQnVmZmVyKCBidWZmZXJUeXBlLCBhdHRyaWJ1dGVQcm9wZXJ0aWVzLl9fd2ViZ2xCdWZmZXIgKTtcblxuXHRcdFx0aWYgKCBkYXRhLmR5bmFtaWMgPT09IGZhbHNlICkge1xuXG5cdFx0XHRcdGdsLmJ1ZmZlckRhdGEoIGJ1ZmZlclR5cGUsIGRhdGEuYXJyYXksIGdsLlNUQVRJQ19EUkFXICk7XG5cblx0XHRcdH0gZWxzZSBpZiAoIGRhdGEudXBkYXRlUmFuZ2UuY291bnQgPT09IC0gMSApIHtcblxuXHRcdFx0XHQvLyBOb3QgdXNpbmcgdXBkYXRlIHJhbmdlc1xuXG5cdFx0XHRcdGdsLmJ1ZmZlclN1YkRhdGEoIGJ1ZmZlclR5cGUsIDAsIGRhdGEuYXJyYXkgKTtcblxuXHRcdFx0fSBlbHNlIGlmICggZGF0YS51cGRhdGVSYW5nZS5jb3VudCA9PT0gMCApIHtcblxuXHRcdFx0XHRjb25zb2xlLmVycm9yKCAnVEhSRUUuV2ViR0xPYmplY3RzLnVwZGF0ZUJ1ZmZlcjogZHluYW1pYyBUSFJFRS5CdWZmZXJBdHRyaWJ1dGUgbWFya2VkIGFzIG5lZWRzVXBkYXRlIGJ1dCB1cGRhdGVSYW5nZS5jb3VudCBpcyAwLCBlbnN1cmUgeW91IGFyZSB1c2luZyBzZXQgbWV0aG9kcyBvciB1cGRhdGluZyBtYW51YWxseS4nICk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0Z2wuYnVmZmVyU3ViRGF0YSggYnVmZmVyVHlwZSwgZGF0YS51cGRhdGVSYW5nZS5vZmZzZXQgKiBkYXRhLmFycmF5LkJZVEVTX1BFUl9FTEVNRU5ULFxuXHRcdFx0XHRcdFx0XHRcdCAgZGF0YS5hcnJheS5zdWJhcnJheSggZGF0YS51cGRhdGVSYW5nZS5vZmZzZXQsIGRhdGEudXBkYXRlUmFuZ2Uub2Zmc2V0ICsgZGF0YS51cGRhdGVSYW5nZS5jb3VudCApICk7XG5cblx0XHRcdFx0ZGF0YS51cGRhdGVSYW5nZS5jb3VudCA9IDA7IC8vIHJlc2V0IHJhbmdlXG5cblx0XHRcdH1cblxuXHRcdFx0YXR0cmlidXRlUHJvcGVydGllcy52ZXJzaW9uID0gZGF0YS52ZXJzaW9uO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gZ2V0QXR0cmlidXRlQnVmZmVyKCBhdHRyaWJ1dGUgKSB7XG5cblx0XHRcdGlmICggYXR0cmlidXRlLmlzSW50ZXJsZWF2ZWRCdWZmZXJBdHRyaWJ1dGUgKSB7XG5cblx0XHRcdFx0cmV0dXJuIHByb3BlcnRpZXMuZ2V0KCBhdHRyaWJ1dGUuZGF0YSApLl9fd2ViZ2xCdWZmZXI7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHByb3BlcnRpZXMuZ2V0KCBhdHRyaWJ1dGUgKS5fX3dlYmdsQnVmZmVyO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gZ2V0QXR0cmlidXRlUHJvcGVydGllcyggYXR0cmlidXRlICkge1xuXG5cdFx0XHRpZiAoIGF0dHJpYnV0ZS5pc0ludGVybGVhdmVkQnVmZmVyQXR0cmlidXRlICkge1xuXG5cdFx0XHRcdHJldHVybiBwcm9wZXJ0aWVzLmdldCggYXR0cmlidXRlLmRhdGEgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gcHJvcGVydGllcy5nZXQoIGF0dHJpYnV0ZSApO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gZ2V0V2lyZWZyYW1lQXR0cmlidXRlKCBnZW9tZXRyeSApIHtcblxuXHRcdFx0dmFyIHByb3BlcnR5ID0gcHJvcGVydGllcy5nZXQoIGdlb21ldHJ5ICk7XG5cblx0XHRcdGlmICggcHJvcGVydHkud2lyZWZyYW1lICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0cmV0dXJuIHByb3BlcnR5LndpcmVmcmFtZTtcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgaW5kaWNlcyA9IFtdO1xuXG5cdFx0XHR2YXIgaW5kZXggPSBnZW9tZXRyeS5pbmRleDtcblx0XHRcdHZhciBhdHRyaWJ1dGVzID0gZ2VvbWV0cnkuYXR0cmlidXRlcztcblx0XHRcdHZhciBwb3NpdGlvbiA9IGF0dHJpYnV0ZXMucG9zaXRpb247XG5cblx0XHRcdC8vIGNvbnNvbGUudGltZSggJ3dpcmVmcmFtZScgKTtcblxuXHRcdFx0aWYgKCBpbmRleCAhPT0gbnVsbCApIHtcblxuXHRcdFx0XHR2YXIgZWRnZXMgPSB7fTtcblx0XHRcdFx0dmFyIGFycmF5ID0gaW5kZXguYXJyYXk7XG5cblx0XHRcdFx0Zm9yICggdmFyIGkgPSAwLCBsID0gYXJyYXkubGVuZ3RoOyBpIDwgbDsgaSArPSAzICkge1xuXG5cdFx0XHRcdFx0dmFyIGEgPSBhcnJheVsgaSArIDAgXTtcblx0XHRcdFx0XHR2YXIgYiA9IGFycmF5WyBpICsgMSBdO1xuXHRcdFx0XHRcdHZhciBjID0gYXJyYXlbIGkgKyAyIF07XG5cblx0XHRcdFx0XHRpbmRpY2VzLnB1c2goIGEsIGIsIGIsIGMsIGMsIGEgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0dmFyIGFycmF5ID0gYXR0cmlidXRlcy5wb3NpdGlvbi5hcnJheTtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSAoIGFycmF5Lmxlbmd0aCAvIDMgKSAtIDE7IGkgPCBsOyBpICs9IDMgKSB7XG5cblx0XHRcdFx0XHR2YXIgYSA9IGkgKyAwO1xuXHRcdFx0XHRcdHZhciBiID0gaSArIDE7XG5cdFx0XHRcdFx0dmFyIGMgPSBpICsgMjtcblxuXHRcdFx0XHRcdGluZGljZXMucHVzaCggYSwgYiwgYiwgYywgYywgYSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHQvLyBjb25zb2xlLnRpbWVFbmQoICd3aXJlZnJhbWUnICk7XG5cblx0XHRcdHZhciBUeXBlQXJyYXkgPSBwb3NpdGlvbi5jb3VudCA+IDY1NTM1ID8gVWludDMyQXJyYXkgOiBVaW50MTZBcnJheTtcblx0XHRcdHZhciBhdHRyaWJ1dGUgPSBuZXcgQnVmZmVyQXR0cmlidXRlKCBuZXcgVHlwZUFycmF5KCBpbmRpY2VzICksIDEgKTtcblxuXHRcdFx0dXBkYXRlQXR0cmlidXRlKCBhdHRyaWJ1dGUsIGdsLkVMRU1FTlRfQVJSQVlfQlVGRkVSICk7XG5cblx0XHRcdHByb3BlcnR5LndpcmVmcmFtZSA9IGF0dHJpYnV0ZTtcblxuXHRcdFx0cmV0dXJuIGF0dHJpYnV0ZTtcblxuXHRcdH1cblxuXHRcdHJldHVybiB7XG5cblx0XHRcdGdldEF0dHJpYnV0ZUJ1ZmZlcjogZ2V0QXR0cmlidXRlQnVmZmVyLFxuXHRcdFx0Z2V0QXR0cmlidXRlUHJvcGVydGllczogZ2V0QXR0cmlidXRlUHJvcGVydGllcyxcblx0XHRcdGdldFdpcmVmcmFtZUF0dHJpYnV0ZTogZ2V0V2lyZWZyYW1lQXR0cmlidXRlLFxuXG5cdFx0XHR1cGRhdGU6IHVwZGF0ZVxuXG5cdFx0fTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFdlYkdMVGV4dHVyZXMoIF9nbCwgZXh0ZW5zaW9ucywgc3RhdGUsIHByb3BlcnRpZXMsIGNhcGFiaWxpdGllcywgcGFyYW1UaHJlZVRvR0wsIGluZm8gKSB7XG5cblx0XHR2YXIgX2luZm9NZW1vcnkgPSBpbmZvLm1lbW9yeTtcblx0XHR2YXIgX2lzV2ViR0wyID0gKCB0eXBlb2YgV2ViR0wyUmVuZGVyaW5nQ29udGV4dCAhPT0gJ3VuZGVmaW5lZCcgJiYgX2dsIGluc3RhbmNlb2YgV2ViR0wyUmVuZGVyaW5nQ29udGV4dCApO1xuXG5cdFx0Ly9cblxuXHRcdGZ1bmN0aW9uIGNsYW1wVG9NYXhTaXplKCBpbWFnZSwgbWF4U2l6ZSApIHtcblxuXHRcdFx0aWYgKCBpbWFnZS53aWR0aCA+IG1heFNpemUgfHwgaW1hZ2UuaGVpZ2h0ID4gbWF4U2l6ZSApIHtcblxuXHRcdFx0XHQvLyBXYXJuaW5nOiBTY2FsaW5nIHRocm91Z2ggdGhlIGNhbnZhcyB3aWxsIG9ubHkgd29yayB3aXRoIGltYWdlcyB0aGF0IHVzZVxuXHRcdFx0XHQvLyBwcmVtdWx0aXBsaWVkIGFscGhhLlxuXG5cdFx0XHRcdHZhciBzY2FsZSA9IG1heFNpemUgLyBNYXRoLm1heCggaW1hZ2Uud2lkdGgsIGltYWdlLmhlaWdodCApO1xuXG5cdFx0XHRcdHZhciBjYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMoICdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sJywgJ2NhbnZhcycgKTtcblx0XHRcdFx0Y2FudmFzLndpZHRoID0gTWF0aC5mbG9vciggaW1hZ2Uud2lkdGggKiBzY2FsZSApO1xuXHRcdFx0XHRjYW52YXMuaGVpZ2h0ID0gTWF0aC5mbG9vciggaW1hZ2UuaGVpZ2h0ICogc2NhbGUgKTtcblxuXHRcdFx0XHR2YXIgY29udGV4dCA9IGNhbnZhcy5nZXRDb250ZXh0KCAnMmQnICk7XG5cdFx0XHRcdGNvbnRleHQuZHJhd0ltYWdlKCBpbWFnZSwgMCwgMCwgaW1hZ2Uud2lkdGgsIGltYWdlLmhlaWdodCwgMCwgMCwgY2FudmFzLndpZHRoLCBjYW52YXMuaGVpZ2h0ICk7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuV2ViR0xSZW5kZXJlcjogaW1hZ2UgaXMgdG9vIGJpZyAoJyArIGltYWdlLndpZHRoICsgJ3gnICsgaW1hZ2UuaGVpZ2h0ICsgJykuIFJlc2l6ZWQgdG8gJyArIGNhbnZhcy53aWR0aCArICd4JyArIGNhbnZhcy5oZWlnaHQsIGltYWdlICk7XG5cblx0XHRcdFx0cmV0dXJuIGNhbnZhcztcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gaW1hZ2U7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBpc1Bvd2VyT2ZUd28oIGltYWdlICkge1xuXG5cdFx0XHRyZXR1cm4gX01hdGguaXNQb3dlck9mVHdvKCBpbWFnZS53aWR0aCApICYmIF9NYXRoLmlzUG93ZXJPZlR3byggaW1hZ2UuaGVpZ2h0ICk7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBtYWtlUG93ZXJPZlR3byggaW1hZ2UgKSB7XG5cblx0XHRcdGlmICggaW1hZ2UgaW5zdGFuY2VvZiBIVE1MSW1hZ2VFbGVtZW50IHx8IGltYWdlIGluc3RhbmNlb2YgSFRNTENhbnZhc0VsZW1lbnQgKSB7XG5cblx0XHRcdFx0dmFyIGNhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnROUyggJ2h0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwnLCAnY2FudmFzJyApO1xuXHRcdFx0XHRjYW52YXMud2lkdGggPSBfTWF0aC5uZWFyZXN0UG93ZXJPZlR3byggaW1hZ2Uud2lkdGggKTtcblx0XHRcdFx0Y2FudmFzLmhlaWdodCA9IF9NYXRoLm5lYXJlc3RQb3dlck9mVHdvKCBpbWFnZS5oZWlnaHQgKTtcblxuXHRcdFx0XHR2YXIgY29udGV4dCA9IGNhbnZhcy5nZXRDb250ZXh0KCAnMmQnICk7XG5cdFx0XHRcdGNvbnRleHQuZHJhd0ltYWdlKCBpbWFnZSwgMCwgMCwgY2FudmFzLndpZHRoLCBjYW52YXMuaGVpZ2h0ICk7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuV2ViR0xSZW5kZXJlcjogaW1hZ2UgaXMgbm90IHBvd2VyIG9mIHR3byAoJyArIGltYWdlLndpZHRoICsgJ3gnICsgaW1hZ2UuaGVpZ2h0ICsgJykuIFJlc2l6ZWQgdG8gJyArIGNhbnZhcy53aWR0aCArICd4JyArIGNhbnZhcy5oZWlnaHQsIGltYWdlICk7XG5cblx0XHRcdFx0cmV0dXJuIGNhbnZhcztcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gaW1hZ2U7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiB0ZXh0dXJlTmVlZHNQb3dlck9mVHdvKCB0ZXh0dXJlICkge1xuXG5cdFx0XHRyZXR1cm4gKCB0ZXh0dXJlLndyYXBTICE9PSBDbGFtcFRvRWRnZVdyYXBwaW5nIHx8IHRleHR1cmUud3JhcFQgIT09IENsYW1wVG9FZGdlV3JhcHBpbmcgKSB8fFxuXHRcdFx0XHQoIHRleHR1cmUubWluRmlsdGVyICE9PSBOZWFyZXN0RmlsdGVyICYmIHRleHR1cmUubWluRmlsdGVyICE9PSBMaW5lYXJGaWx0ZXIgKTtcblxuXHRcdH1cblxuXHRcdC8vIEZhbGxiYWNrIGZpbHRlcnMgZm9yIG5vbi1wb3dlci1vZi0yIHRleHR1cmVzXG5cblx0XHRmdW5jdGlvbiBmaWx0ZXJGYWxsYmFjayggZiApIHtcblxuXHRcdFx0aWYgKCBmID09PSBOZWFyZXN0RmlsdGVyIHx8IGYgPT09IE5lYXJlc3RNaXBNYXBOZWFyZXN0RmlsdGVyIHx8IGYgPT09IE5lYXJlc3RNaXBNYXBMaW5lYXJGaWx0ZXIgKSB7XG5cblx0XHRcdFx0cmV0dXJuIF9nbC5ORUFSRVNUO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBfZ2wuTElORUFSO1xuXG5cdFx0fVxuXG5cdFx0Ly9cblxuXHRcdGZ1bmN0aW9uIG9uVGV4dHVyZURpc3Bvc2UoIGV2ZW50ICkge1xuXG5cdFx0XHR2YXIgdGV4dHVyZSA9IGV2ZW50LnRhcmdldDtcblxuXHRcdFx0dGV4dHVyZS5yZW1vdmVFdmVudExpc3RlbmVyKCAnZGlzcG9zZScsIG9uVGV4dHVyZURpc3Bvc2UgKTtcblxuXHRcdFx0ZGVhbGxvY2F0ZVRleHR1cmUoIHRleHR1cmUgKTtcblxuXHRcdFx0X2luZm9NZW1vcnkudGV4dHVyZXMgLS07XG5cblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIG9uUmVuZGVyVGFyZ2V0RGlzcG9zZSggZXZlbnQgKSB7XG5cblx0XHRcdHZhciByZW5kZXJUYXJnZXQgPSBldmVudC50YXJnZXQ7XG5cblx0XHRcdHJlbmRlclRhcmdldC5yZW1vdmVFdmVudExpc3RlbmVyKCAnZGlzcG9zZScsIG9uUmVuZGVyVGFyZ2V0RGlzcG9zZSApO1xuXG5cdFx0XHRkZWFsbG9jYXRlUmVuZGVyVGFyZ2V0KCByZW5kZXJUYXJnZXQgKTtcblxuXHRcdFx0X2luZm9NZW1vcnkudGV4dHVyZXMgLS07XG5cblx0XHR9XG5cblx0XHQvL1xuXG5cdFx0ZnVuY3Rpb24gZGVhbGxvY2F0ZVRleHR1cmUoIHRleHR1cmUgKSB7XG5cblx0XHRcdHZhciB0ZXh0dXJlUHJvcGVydGllcyA9IHByb3BlcnRpZXMuZ2V0KCB0ZXh0dXJlICk7XG5cblx0XHRcdGlmICggdGV4dHVyZS5pbWFnZSAmJiB0ZXh0dXJlUHJvcGVydGllcy5fX2ltYWdlX193ZWJnbFRleHR1cmVDdWJlICkge1xuXG5cdFx0XHRcdC8vIGN1YmUgdGV4dHVyZVxuXG5cdFx0XHRcdF9nbC5kZWxldGVUZXh0dXJlKCB0ZXh0dXJlUHJvcGVydGllcy5fX2ltYWdlX193ZWJnbFRleHR1cmVDdWJlICk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0Ly8gMkQgdGV4dHVyZVxuXG5cdFx0XHRcdGlmICggdGV4dHVyZVByb3BlcnRpZXMuX193ZWJnbEluaXQgPT09IHVuZGVmaW5lZCApIHJldHVybjtcblxuXHRcdFx0XHRfZ2wuZGVsZXRlVGV4dHVyZSggdGV4dHVyZVByb3BlcnRpZXMuX193ZWJnbFRleHR1cmUgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyByZW1vdmUgYWxsIHdlYmdsIHByb3BlcnRpZXNcblx0XHRcdHByb3BlcnRpZXMuZGVsZXRlKCB0ZXh0dXJlICk7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBkZWFsbG9jYXRlUmVuZGVyVGFyZ2V0KCByZW5kZXJUYXJnZXQgKSB7XG5cblx0XHRcdHZhciByZW5kZXJUYXJnZXRQcm9wZXJ0aWVzID0gcHJvcGVydGllcy5nZXQoIHJlbmRlclRhcmdldCApO1xuXHRcdFx0dmFyIHRleHR1cmVQcm9wZXJ0aWVzID0gcHJvcGVydGllcy5nZXQoIHJlbmRlclRhcmdldC50ZXh0dXJlICk7XG5cblx0XHRcdGlmICggISByZW5kZXJUYXJnZXQgKSByZXR1cm47XG5cblx0XHRcdGlmICggdGV4dHVyZVByb3BlcnRpZXMuX193ZWJnbFRleHR1cmUgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRfZ2wuZGVsZXRlVGV4dHVyZSggdGV4dHVyZVByb3BlcnRpZXMuX193ZWJnbFRleHR1cmUgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHJlbmRlclRhcmdldC5kZXB0aFRleHR1cmUgKSB7XG5cblx0XHRcdFx0cmVuZGVyVGFyZ2V0LmRlcHRoVGV4dHVyZS5kaXNwb3NlKCk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCByZW5kZXJUYXJnZXQuaXNXZWJHTFJlbmRlclRhcmdldEN1YmUgKSB7XG5cblx0XHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgNjsgaSArKyApIHtcblxuXHRcdFx0XHRcdF9nbC5kZWxldGVGcmFtZWJ1ZmZlciggcmVuZGVyVGFyZ2V0UHJvcGVydGllcy5fX3dlYmdsRnJhbWVidWZmZXJbIGkgXSApO1xuXHRcdFx0XHRcdGlmICggcmVuZGVyVGFyZ2V0UHJvcGVydGllcy5fX3dlYmdsRGVwdGhidWZmZXIgKSBfZ2wuZGVsZXRlUmVuZGVyYnVmZmVyKCByZW5kZXJUYXJnZXRQcm9wZXJ0aWVzLl9fd2ViZ2xEZXB0aGJ1ZmZlclsgaSBdICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdF9nbC5kZWxldGVGcmFtZWJ1ZmZlciggcmVuZGVyVGFyZ2V0UHJvcGVydGllcy5fX3dlYmdsRnJhbWVidWZmZXIgKTtcblx0XHRcdFx0aWYgKCByZW5kZXJUYXJnZXRQcm9wZXJ0aWVzLl9fd2ViZ2xEZXB0aGJ1ZmZlciApIF9nbC5kZWxldGVSZW5kZXJidWZmZXIoIHJlbmRlclRhcmdldFByb3BlcnRpZXMuX193ZWJnbERlcHRoYnVmZmVyICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cHJvcGVydGllcy5kZWxldGUoIHJlbmRlclRhcmdldC50ZXh0dXJlICk7XG5cdFx0XHRwcm9wZXJ0aWVzLmRlbGV0ZSggcmVuZGVyVGFyZ2V0ICk7XG5cblx0XHR9XG5cblx0XHQvL1xuXG5cblxuXHRcdGZ1bmN0aW9uIHNldFRleHR1cmUyRCggdGV4dHVyZSwgc2xvdCApIHtcblxuXHRcdFx0dmFyIHRleHR1cmVQcm9wZXJ0aWVzID0gcHJvcGVydGllcy5nZXQoIHRleHR1cmUgKTtcblxuXHRcdFx0aWYgKCB0ZXh0dXJlLnZlcnNpb24gPiAwICYmIHRleHR1cmVQcm9wZXJ0aWVzLl9fdmVyc2lvbiAhPT0gdGV4dHVyZS52ZXJzaW9uICkge1xuXG5cdFx0XHRcdHZhciBpbWFnZSA9IHRleHR1cmUuaW1hZ2U7XG5cblx0XHRcdFx0aWYgKCBpbWFnZSA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuV2ViR0xSZW5kZXJlcjogVGV4dHVyZSBtYXJrZWQgZm9yIHVwZGF0ZSBidXQgaW1hZ2UgaXMgdW5kZWZpbmVkJywgdGV4dHVyZSApO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAoIGltYWdlLmNvbXBsZXRlID09PSBmYWxzZSApIHtcblxuXHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyZXI6IFRleHR1cmUgbWFya2VkIGZvciB1cGRhdGUgYnV0IGltYWdlIGlzIGluY29tcGxldGUnLCB0ZXh0dXJlICk7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdHVwbG9hZFRleHR1cmUoIHRleHR1cmVQcm9wZXJ0aWVzLCB0ZXh0dXJlLCBzbG90ICk7XG5cdFx0XHRcdFx0cmV0dXJuO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRzdGF0ZS5hY3RpdmVUZXh0dXJlKCBfZ2wuVEVYVFVSRTAgKyBzbG90ICk7XG5cdFx0XHRzdGF0ZS5iaW5kVGV4dHVyZSggX2dsLlRFWFRVUkVfMkQsIHRleHR1cmVQcm9wZXJ0aWVzLl9fd2ViZ2xUZXh0dXJlICk7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBzZXRUZXh0dXJlQ3ViZSggdGV4dHVyZSwgc2xvdCApIHtcblxuXHRcdFx0dmFyIHRleHR1cmVQcm9wZXJ0aWVzID0gcHJvcGVydGllcy5nZXQoIHRleHR1cmUgKTtcblxuXHRcdFx0aWYgKCB0ZXh0dXJlLmltYWdlLmxlbmd0aCA9PT0gNiApIHtcblxuXHRcdFx0XHRpZiAoIHRleHR1cmUudmVyc2lvbiA+IDAgJiYgdGV4dHVyZVByb3BlcnRpZXMuX192ZXJzaW9uICE9PSB0ZXh0dXJlLnZlcnNpb24gKSB7XG5cblx0XHRcdFx0XHRpZiAoICEgdGV4dHVyZVByb3BlcnRpZXMuX19pbWFnZV9fd2ViZ2xUZXh0dXJlQ3ViZSApIHtcblxuXHRcdFx0XHRcdFx0dGV4dHVyZS5hZGRFdmVudExpc3RlbmVyKCAnZGlzcG9zZScsIG9uVGV4dHVyZURpc3Bvc2UgKTtcblxuXHRcdFx0XHRcdFx0dGV4dHVyZVByb3BlcnRpZXMuX19pbWFnZV9fd2ViZ2xUZXh0dXJlQ3ViZSA9IF9nbC5jcmVhdGVUZXh0dXJlKCk7XG5cblx0XHRcdFx0XHRcdF9pbmZvTWVtb3J5LnRleHR1cmVzICsrO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0c3RhdGUuYWN0aXZlVGV4dHVyZSggX2dsLlRFWFRVUkUwICsgc2xvdCApO1xuXHRcdFx0XHRcdHN0YXRlLmJpbmRUZXh0dXJlKCBfZ2wuVEVYVFVSRV9DVUJFX01BUCwgdGV4dHVyZVByb3BlcnRpZXMuX19pbWFnZV9fd2ViZ2xUZXh0dXJlQ3ViZSApO1xuXG5cdFx0XHRcdFx0X2dsLnBpeGVsU3RvcmVpKCBfZ2wuVU5QQUNLX0ZMSVBfWV9XRUJHTCwgdGV4dHVyZS5mbGlwWSApO1xuXG5cdFx0XHRcdFx0dmFyIGlzQ29tcHJlc3NlZCA9ICggdGV4dHVyZSAmJiB0ZXh0dXJlLmlzQ29tcHJlc3NlZFRleHR1cmUgKTtcblx0XHRcdFx0XHR2YXIgaXNEYXRhVGV4dHVyZSA9ICggdGV4dHVyZS5pbWFnZVsgMCBdICYmIHRleHR1cmUuaW1hZ2VbIDAgXS5pc0RhdGFUZXh0dXJlICk7XG5cblx0XHRcdFx0XHR2YXIgY3ViZUltYWdlID0gW107XG5cblx0XHRcdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCA2OyBpICsrICkge1xuXG5cdFx0XHRcdFx0XHRpZiAoICEgaXNDb21wcmVzc2VkICYmICEgaXNEYXRhVGV4dHVyZSApIHtcblxuXHRcdFx0XHRcdFx0XHRjdWJlSW1hZ2VbIGkgXSA9IGNsYW1wVG9NYXhTaXplKCB0ZXh0dXJlLmltYWdlWyBpIF0sIGNhcGFiaWxpdGllcy5tYXhDdWJlbWFwU2l6ZSApO1xuXG5cdFx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRcdGN1YmVJbWFnZVsgaSBdID0gaXNEYXRhVGV4dHVyZSA/IHRleHR1cmUuaW1hZ2VbIGkgXS5pbWFnZSA6IHRleHR1cmUuaW1hZ2VbIGkgXTtcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0dmFyIGltYWdlID0gY3ViZUltYWdlWyAwIF0sXG5cdFx0XHRcdFx0aXNQb3dlck9mVHdvSW1hZ2UgPSBpc1Bvd2VyT2ZUd28oIGltYWdlICksXG5cdFx0XHRcdFx0Z2xGb3JtYXQgPSBwYXJhbVRocmVlVG9HTCggdGV4dHVyZS5mb3JtYXQgKSxcblx0XHRcdFx0XHRnbFR5cGUgPSBwYXJhbVRocmVlVG9HTCggdGV4dHVyZS50eXBlICk7XG5cblx0XHRcdFx0XHRzZXRUZXh0dXJlUGFyYW1ldGVycyggX2dsLlRFWFRVUkVfQ1VCRV9NQVAsIHRleHR1cmUsIGlzUG93ZXJPZlR3b0ltYWdlICk7XG5cblx0XHRcdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCA2OyBpICsrICkge1xuXG5cdFx0XHRcdFx0XHRpZiAoICEgaXNDb21wcmVzc2VkICkge1xuXG5cdFx0XHRcdFx0XHRcdGlmICggaXNEYXRhVGV4dHVyZSApIHtcblxuXHRcdFx0XHRcdFx0XHRcdHN0YXRlLnRleEltYWdlMkQoIF9nbC5URVhUVVJFX0NVQkVfTUFQX1BPU0lUSVZFX1ggKyBpLCAwLCBnbEZvcm1hdCwgY3ViZUltYWdlWyBpIF0ud2lkdGgsIGN1YmVJbWFnZVsgaSBdLmhlaWdodCwgMCwgZ2xGb3JtYXQsIGdsVHlwZSwgY3ViZUltYWdlWyBpIF0uZGF0YSApO1xuXG5cdFx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdFx0XHRzdGF0ZS50ZXhJbWFnZTJEKCBfZ2wuVEVYVFVSRV9DVUJFX01BUF9QT1NJVElWRV9YICsgaSwgMCwgZ2xGb3JtYXQsIGdsRm9ybWF0LCBnbFR5cGUsIGN1YmVJbWFnZVsgaSBdICk7XG5cblx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRcdHZhciBtaXBtYXAsIG1pcG1hcHMgPSBjdWJlSW1hZ2VbIGkgXS5taXBtYXBzO1xuXG5cdFx0XHRcdFx0XHRcdGZvciAoIHZhciBqID0gMCwgamwgPSBtaXBtYXBzLmxlbmd0aDsgaiA8IGpsOyBqICsrICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0bWlwbWFwID0gbWlwbWFwc1sgaiBdO1xuXG5cdFx0XHRcdFx0XHRcdFx0aWYgKCB0ZXh0dXJlLmZvcm1hdCAhPT0gUkdCQUZvcm1hdCAmJiB0ZXh0dXJlLmZvcm1hdCAhPT0gUkdCRm9ybWF0ICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0XHRpZiAoIHN0YXRlLmdldENvbXByZXNzZWRUZXh0dXJlRm9ybWF0cygpLmluZGV4T2YoIGdsRm9ybWF0ICkgPiAtIDEgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHRcdFx0c3RhdGUuY29tcHJlc3NlZFRleEltYWdlMkQoIF9nbC5URVhUVVJFX0NVQkVfTUFQX1BPU0lUSVZFX1ggKyBpLCBqLCBnbEZvcm1hdCwgbWlwbWFwLndpZHRoLCBtaXBtYXAuaGVpZ2h0LCAwLCBtaXBtYXAuZGF0YSApO1xuXG5cdFx0XHRcdFx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRcdFx0XHRcdGNvbnNvbGUud2FybiggXCJUSFJFRS5XZWJHTFJlbmRlcmVyOiBBdHRlbXB0IHRvIGxvYWQgdW5zdXBwb3J0ZWQgY29tcHJlc3NlZCB0ZXh0dXJlIGZvcm1hdCBpbiAuc2V0VGV4dHVyZUN1YmUoKVwiICk7XG5cblx0XHRcdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdFx0XHRcdHN0YXRlLnRleEltYWdlMkQoIF9nbC5URVhUVVJFX0NVQkVfTUFQX1BPU0lUSVZFX1ggKyBpLCBqLCBnbEZvcm1hdCwgbWlwbWFwLndpZHRoLCBtaXBtYXAuaGVpZ2h0LCAwLCBnbEZvcm1hdCwgZ2xUeXBlLCBtaXBtYXAuZGF0YSApO1xuXG5cdFx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0aWYgKCB0ZXh0dXJlLmdlbmVyYXRlTWlwbWFwcyAmJiBpc1Bvd2VyT2ZUd29JbWFnZSApIHtcblxuXHRcdFx0XHRcdFx0X2dsLmdlbmVyYXRlTWlwbWFwKCBfZ2wuVEVYVFVSRV9DVUJFX01BUCApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0dGV4dHVyZVByb3BlcnRpZXMuX192ZXJzaW9uID0gdGV4dHVyZS52ZXJzaW9uO1xuXG5cdFx0XHRcdFx0aWYgKCB0ZXh0dXJlLm9uVXBkYXRlICkgdGV4dHVyZS5vblVwZGF0ZSggdGV4dHVyZSApO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRzdGF0ZS5hY3RpdmVUZXh0dXJlKCBfZ2wuVEVYVFVSRTAgKyBzbG90ICk7XG5cdFx0XHRcdFx0c3RhdGUuYmluZFRleHR1cmUoIF9nbC5URVhUVVJFX0NVQkVfTUFQLCB0ZXh0dXJlUHJvcGVydGllcy5fX2ltYWdlX193ZWJnbFRleHR1cmVDdWJlICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBzZXRUZXh0dXJlQ3ViZUR5bmFtaWMoIHRleHR1cmUsIHNsb3QgKSB7XG5cblx0XHRcdHN0YXRlLmFjdGl2ZVRleHR1cmUoIF9nbC5URVhUVVJFMCArIHNsb3QgKTtcblx0XHRcdHN0YXRlLmJpbmRUZXh0dXJlKCBfZ2wuVEVYVFVSRV9DVUJFX01BUCwgcHJvcGVydGllcy5nZXQoIHRleHR1cmUgKS5fX3dlYmdsVGV4dHVyZSApO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gc2V0VGV4dHVyZVBhcmFtZXRlcnMoIHRleHR1cmVUeXBlLCB0ZXh0dXJlLCBpc1Bvd2VyT2ZUd29JbWFnZSApIHtcblxuXHRcdFx0dmFyIGV4dGVuc2lvbjtcblxuXHRcdFx0aWYgKCBpc1Bvd2VyT2ZUd29JbWFnZSApIHtcblxuXHRcdFx0XHRfZ2wudGV4UGFyYW1ldGVyaSggdGV4dHVyZVR5cGUsIF9nbC5URVhUVVJFX1dSQVBfUywgcGFyYW1UaHJlZVRvR0woIHRleHR1cmUud3JhcFMgKSApO1xuXHRcdFx0XHRfZ2wudGV4UGFyYW1ldGVyaSggdGV4dHVyZVR5cGUsIF9nbC5URVhUVVJFX1dSQVBfVCwgcGFyYW1UaHJlZVRvR0woIHRleHR1cmUud3JhcFQgKSApO1xuXG5cdFx0XHRcdF9nbC50ZXhQYXJhbWV0ZXJpKCB0ZXh0dXJlVHlwZSwgX2dsLlRFWFRVUkVfTUFHX0ZJTFRFUiwgcGFyYW1UaHJlZVRvR0woIHRleHR1cmUubWFnRmlsdGVyICkgKTtcblx0XHRcdFx0X2dsLnRleFBhcmFtZXRlcmkoIHRleHR1cmVUeXBlLCBfZ2wuVEVYVFVSRV9NSU5fRklMVEVSLCBwYXJhbVRocmVlVG9HTCggdGV4dHVyZS5taW5GaWx0ZXIgKSApO1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdF9nbC50ZXhQYXJhbWV0ZXJpKCB0ZXh0dXJlVHlwZSwgX2dsLlRFWFRVUkVfV1JBUF9TLCBfZ2wuQ0xBTVBfVE9fRURHRSApO1xuXHRcdFx0XHRfZ2wudGV4UGFyYW1ldGVyaSggdGV4dHVyZVR5cGUsIF9nbC5URVhUVVJFX1dSQVBfVCwgX2dsLkNMQU1QX1RPX0VER0UgKTtcblxuXHRcdFx0XHRpZiAoIHRleHR1cmUud3JhcFMgIT09IENsYW1wVG9FZGdlV3JhcHBpbmcgfHwgdGV4dHVyZS53cmFwVCAhPT0gQ2xhbXBUb0VkZ2VXcmFwcGluZyApIHtcblxuXHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyZXI6IFRleHR1cmUgaXMgbm90IHBvd2VyIG9mIHR3by4gVGV4dHVyZS53cmFwUyBhbmQgVGV4dHVyZS53cmFwVCBzaG91bGQgYmUgc2V0IHRvIFRIUkVFLkNsYW1wVG9FZGdlV3JhcHBpbmcuJywgdGV4dHVyZSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRfZ2wudGV4UGFyYW1ldGVyaSggdGV4dHVyZVR5cGUsIF9nbC5URVhUVVJFX01BR19GSUxURVIsIGZpbHRlckZhbGxiYWNrKCB0ZXh0dXJlLm1hZ0ZpbHRlciApICk7XG5cdFx0XHRcdF9nbC50ZXhQYXJhbWV0ZXJpKCB0ZXh0dXJlVHlwZSwgX2dsLlRFWFRVUkVfTUlOX0ZJTFRFUiwgZmlsdGVyRmFsbGJhY2soIHRleHR1cmUubWluRmlsdGVyICkgKTtcblxuXHRcdFx0XHRpZiAoIHRleHR1cmUubWluRmlsdGVyICE9PSBOZWFyZXN0RmlsdGVyICYmIHRleHR1cmUubWluRmlsdGVyICE9PSBMaW5lYXJGaWx0ZXIgKSB7XG5cblx0XHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5XZWJHTFJlbmRlcmVyOiBUZXh0dXJlIGlzIG5vdCBwb3dlciBvZiB0d28uIFRleHR1cmUubWluRmlsdGVyIHNob3VsZCBiZSBzZXQgdG8gVEhSRUUuTmVhcmVzdEZpbHRlciBvciBUSFJFRS5MaW5lYXJGaWx0ZXIuJywgdGV4dHVyZSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRleHRlbnNpb24gPSBleHRlbnNpb25zLmdldCggJ0VYVF90ZXh0dXJlX2ZpbHRlcl9hbmlzb3Ryb3BpYycgKTtcblxuXHRcdFx0aWYgKCBleHRlbnNpb24gKSB7XG5cblx0XHRcdFx0aWYgKCB0ZXh0dXJlLnR5cGUgPT09IEZsb2F0VHlwZSAmJiBleHRlbnNpb25zLmdldCggJ09FU190ZXh0dXJlX2Zsb2F0X2xpbmVhcicgKSA9PT0gbnVsbCApIHJldHVybjtcblx0XHRcdFx0aWYgKCB0ZXh0dXJlLnR5cGUgPT09IEhhbGZGbG9hdFR5cGUgJiYgZXh0ZW5zaW9ucy5nZXQoICdPRVNfdGV4dHVyZV9oYWxmX2Zsb2F0X2xpbmVhcicgKSA9PT0gbnVsbCApIHJldHVybjtcblxuXHRcdFx0XHRpZiAoIHRleHR1cmUuYW5pc290cm9weSA+IDEgfHwgcHJvcGVydGllcy5nZXQoIHRleHR1cmUgKS5fX2N1cnJlbnRBbmlzb3Ryb3B5ICkge1xuXG5cdFx0XHRcdFx0X2dsLnRleFBhcmFtZXRlcmYoIHRleHR1cmVUeXBlLCBleHRlbnNpb24uVEVYVFVSRV9NQVhfQU5JU09UUk9QWV9FWFQsIE1hdGgubWluKCB0ZXh0dXJlLmFuaXNvdHJvcHksIGNhcGFiaWxpdGllcy5nZXRNYXhBbmlzb3Ryb3B5KCkgKSApO1xuXHRcdFx0XHRcdHByb3BlcnRpZXMuZ2V0KCB0ZXh0dXJlICkuX19jdXJyZW50QW5pc290cm9weSA9IHRleHR1cmUuYW5pc290cm9weTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHVwbG9hZFRleHR1cmUoIHRleHR1cmVQcm9wZXJ0aWVzLCB0ZXh0dXJlLCBzbG90ICkge1xuXG5cdFx0XHRpZiAoIHRleHR1cmVQcm9wZXJ0aWVzLl9fd2ViZ2xJbml0ID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0dGV4dHVyZVByb3BlcnRpZXMuX193ZWJnbEluaXQgPSB0cnVlO1xuXG5cdFx0XHRcdHRleHR1cmUuYWRkRXZlbnRMaXN0ZW5lciggJ2Rpc3Bvc2UnLCBvblRleHR1cmVEaXNwb3NlICk7XG5cblx0XHRcdFx0dGV4dHVyZVByb3BlcnRpZXMuX193ZWJnbFRleHR1cmUgPSBfZ2wuY3JlYXRlVGV4dHVyZSgpO1xuXG5cdFx0XHRcdF9pbmZvTWVtb3J5LnRleHR1cmVzICsrO1xuXG5cdFx0XHR9XG5cblx0XHRcdHN0YXRlLmFjdGl2ZVRleHR1cmUoIF9nbC5URVhUVVJFMCArIHNsb3QgKTtcblx0XHRcdHN0YXRlLmJpbmRUZXh0dXJlKCBfZ2wuVEVYVFVSRV8yRCwgdGV4dHVyZVByb3BlcnRpZXMuX193ZWJnbFRleHR1cmUgKTtcblxuXHRcdFx0X2dsLnBpeGVsU3RvcmVpKCBfZ2wuVU5QQUNLX0ZMSVBfWV9XRUJHTCwgdGV4dHVyZS5mbGlwWSApO1xuXHRcdFx0X2dsLnBpeGVsU3RvcmVpKCBfZ2wuVU5QQUNLX1BSRU1VTFRJUExZX0FMUEhBX1dFQkdMLCB0ZXh0dXJlLnByZW11bHRpcGx5QWxwaGEgKTtcblx0XHRcdF9nbC5waXhlbFN0b3JlaSggX2dsLlVOUEFDS19BTElHTk1FTlQsIHRleHR1cmUudW5wYWNrQWxpZ25tZW50ICk7XG5cblx0XHRcdHZhciBpbWFnZSA9IGNsYW1wVG9NYXhTaXplKCB0ZXh0dXJlLmltYWdlLCBjYXBhYmlsaXRpZXMubWF4VGV4dHVyZVNpemUgKTtcblxuXHRcdFx0aWYgKCB0ZXh0dXJlTmVlZHNQb3dlck9mVHdvKCB0ZXh0dXJlICkgJiYgaXNQb3dlck9mVHdvKCBpbWFnZSApID09PSBmYWxzZSApIHtcblxuXHRcdFx0XHRpbWFnZSA9IG1ha2VQb3dlck9mVHdvKCBpbWFnZSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBpc1Bvd2VyT2ZUd29JbWFnZSA9IGlzUG93ZXJPZlR3byggaW1hZ2UgKSxcblx0XHRcdGdsRm9ybWF0ID0gcGFyYW1UaHJlZVRvR0woIHRleHR1cmUuZm9ybWF0ICksXG5cdFx0XHRnbFR5cGUgPSBwYXJhbVRocmVlVG9HTCggdGV4dHVyZS50eXBlICk7XG5cblx0XHRcdHNldFRleHR1cmVQYXJhbWV0ZXJzKCBfZ2wuVEVYVFVSRV8yRCwgdGV4dHVyZSwgaXNQb3dlck9mVHdvSW1hZ2UgKTtcblxuXHRcdFx0dmFyIG1pcG1hcCwgbWlwbWFwcyA9IHRleHR1cmUubWlwbWFwcztcblxuXHRcdFx0aWYgKCB0ZXh0dXJlLmlzRGVwdGhUZXh0dXJlICkge1xuXG5cdFx0XHRcdC8vIHBvcHVsYXRlIGRlcHRoIHRleHR1cmUgd2l0aCBkdW1teSBkYXRhXG5cblx0XHRcdFx0dmFyIGludGVybmFsRm9ybWF0ID0gX2dsLkRFUFRIX0NPTVBPTkVOVDtcblxuXHRcdFx0XHRpZiAoIHRleHR1cmUudHlwZSA9PT0gRmxvYXRUeXBlICkge1xuXG5cdFx0XHRcdFx0aWYgKCAhX2lzV2ViR0wyICkgdGhyb3cgbmV3IEVycm9yKCdGbG9hdCBEZXB0aCBUZXh0dXJlIG9ubHkgc3VwcG9ydGVkIGluIFdlYkdMMi4wJyk7XG5cdFx0XHRcdFx0aW50ZXJuYWxGb3JtYXQgPSBfZ2wuREVQVEhfQ09NUE9ORU5UMzJGO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAoIF9pc1dlYkdMMiApIHtcblxuXHRcdFx0XHRcdC8vIFdlYkdMIDIuMCByZXF1aXJlcyBzaWduZWQgaW50ZXJuYWxmb3JtYXQgZm9yIGdsVGV4SW1hZ2UyRFxuXHRcdFx0XHRcdGludGVybmFsRm9ybWF0ID0gX2dsLkRFUFRIX0NPTVBPTkVOVDE2O1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoIHRleHR1cmUuZm9ybWF0ID09PSBEZXB0aEZvcm1hdCAmJiBpbnRlcm5hbEZvcm1hdCA9PT0gX2dsLkRFUFRIX0NPTVBPTkVOVCApIHtcblxuXHRcdFx0XHRcdC8vIFRoZSBlcnJvciBJTlZBTElEX09QRVJBVElPTiBpcyBnZW5lcmF0ZWQgYnkgdGV4SW1hZ2UyRCBpZiBmb3JtYXQgYW5kIGludGVybmFsZm9ybWF0IGFyZVxuXHRcdFx0XHRcdC8vIERFUFRIX0NPTVBPTkVOVCBhbmQgdHlwZSBpcyBub3QgVU5TSUdORURfU0hPUlQgb3IgVU5TSUdORURfSU5UXG5cdFx0XHRcdFx0Ly8gKGh0dHBzOi8vd3d3Lmtocm9ub3Mub3JnL3JlZ2lzdHJ5L3dlYmdsL2V4dGVuc2lvbnMvV0VCR0xfZGVwdGhfdGV4dHVyZS8pXG5cdFx0XHRcdFx0aWYgKCB0ZXh0dXJlLnR5cGUgIT09IFVuc2lnbmVkU2hvcnRUeXBlICYmIHRleHR1cmUudHlwZSAhPT0gVW5zaWduZWRJbnRUeXBlICkge1xuXG5cdFx0XHRcdFx0ICAgICAgICBjb25zb2xlLndhcm4oICdUSFJFRS5XZWJHTFJlbmRlcmVyOiBVc2UgVW5zaWduZWRTaG9ydFR5cGUgb3IgVW5zaWduZWRJbnRUeXBlIGZvciBEZXB0aEZvcm1hdCBEZXB0aFRleHR1cmUuJyApO1xuXG5cdFx0XHRcdFx0XHR0ZXh0dXJlLnR5cGUgPSBVbnNpZ25lZFNob3J0VHlwZTtcblx0XHRcdFx0XHRcdGdsVHlwZSA9IHBhcmFtVGhyZWVUb0dMKCB0ZXh0dXJlLnR5cGUgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gRGVwdGggc3RlbmNpbCB0ZXh0dXJlcyBuZWVkIHRoZSBERVBUSF9TVEVOQ0lMIGludGVybmFsIGZvcm1hdFxuXHRcdFx0XHQvLyAoaHR0cHM6Ly93d3cua2hyb25vcy5vcmcvcmVnaXN0cnkvd2ViZ2wvZXh0ZW5zaW9ucy9XRUJHTF9kZXB0aF90ZXh0dXJlLylcblx0XHRcdFx0aWYgKCB0ZXh0dXJlLmZvcm1hdCA9PT0gRGVwdGhTdGVuY2lsRm9ybWF0ICkge1xuXG5cdFx0XHRcdFx0aW50ZXJuYWxGb3JtYXQgPSBfZ2wuREVQVEhfU1RFTkNJTDtcblxuXHRcdFx0XHRcdC8vIFRoZSBlcnJvciBJTlZBTElEX09QRVJBVElPTiBpcyBnZW5lcmF0ZWQgYnkgdGV4SW1hZ2UyRCBpZiBmb3JtYXQgYW5kIGludGVybmFsZm9ybWF0IGFyZVxuXHRcdFx0XHRcdC8vIERFUFRIX1NURU5DSUwgYW5kIHR5cGUgaXMgbm90IFVOU0lHTkVEX0lOVF8yNF84X1dFQkdMLlxuXHRcdFx0XHRcdC8vIChodHRwczovL3d3dy5raHJvbm9zLm9yZy9yZWdpc3RyeS93ZWJnbC9leHRlbnNpb25zL1dFQkdMX2RlcHRoX3RleHR1cmUvKVxuXHRcdFx0XHRcdGlmICggdGV4dHVyZS50eXBlICE9PSBVbnNpZ25lZEludDI0OFR5cGUgKSB7XG5cblx0XHRcdFx0XHQgICAgICAgIGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyZXI6IFVzZSBVbnNpZ25lZEludDI0OFR5cGUgZm9yIERlcHRoU3RlbmNpbEZvcm1hdCBEZXB0aFRleHR1cmUuJyApO1xuXG5cdFx0XHRcdFx0XHR0ZXh0dXJlLnR5cGUgPSBVbnNpZ25lZEludDI0OFR5cGU7XG5cdFx0XHRcdFx0XHRnbFR5cGUgPSBwYXJhbVRocmVlVG9HTCggdGV4dHVyZS50eXBlICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHN0YXRlLnRleEltYWdlMkQoIF9nbC5URVhUVVJFXzJELCAwLCBpbnRlcm5hbEZvcm1hdCwgaW1hZ2Uud2lkdGgsIGltYWdlLmhlaWdodCwgMCwgZ2xGb3JtYXQsIGdsVHlwZSwgbnVsbCApO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCB0ZXh0dXJlLmlzRGF0YVRleHR1cmUgKSB7XG5cblx0XHRcdFx0Ly8gdXNlIG1hbnVhbGx5IGNyZWF0ZWQgbWlwbWFwcyBpZiBhdmFpbGFibGVcblx0XHRcdFx0Ly8gaWYgdGhlcmUgYXJlIG5vIG1hbnVhbCBtaXBtYXBzXG5cdFx0XHRcdC8vIHNldCAwIGxldmVsIG1pcG1hcCBhbmQgdGhlbiB1c2UgR0wgdG8gZ2VuZXJhdGUgb3RoZXIgbWlwbWFwIGxldmVsc1xuXG5cdFx0XHRcdGlmICggbWlwbWFwcy5sZW5ndGggPiAwICYmIGlzUG93ZXJPZlR3b0ltYWdlICkge1xuXG5cdFx0XHRcdFx0Zm9yICggdmFyIGkgPSAwLCBpbCA9IG1pcG1hcHMubGVuZ3RoOyBpIDwgaWw7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRcdG1pcG1hcCA9IG1pcG1hcHNbIGkgXTtcblx0XHRcdFx0XHRcdHN0YXRlLnRleEltYWdlMkQoIF9nbC5URVhUVVJFXzJELCBpLCBnbEZvcm1hdCwgbWlwbWFwLndpZHRoLCBtaXBtYXAuaGVpZ2h0LCAwLCBnbEZvcm1hdCwgZ2xUeXBlLCBtaXBtYXAuZGF0YSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0dGV4dHVyZS5nZW5lcmF0ZU1pcG1hcHMgPSBmYWxzZTtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0c3RhdGUudGV4SW1hZ2UyRCggX2dsLlRFWFRVUkVfMkQsIDAsIGdsRm9ybWF0LCBpbWFnZS53aWR0aCwgaW1hZ2UuaGVpZ2h0LCAwLCBnbEZvcm1hdCwgZ2xUeXBlLCBpbWFnZS5kYXRhICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9IGVsc2UgaWYgKCB0ZXh0dXJlLmlzQ29tcHJlc3NlZFRleHR1cmUgKSB7XG5cblx0XHRcdFx0Zm9yICggdmFyIGkgPSAwLCBpbCA9IG1pcG1hcHMubGVuZ3RoOyBpIDwgaWw7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRtaXBtYXAgPSBtaXBtYXBzWyBpIF07XG5cblx0XHRcdFx0XHRpZiAoIHRleHR1cmUuZm9ybWF0ICE9PSBSR0JBRm9ybWF0ICYmIHRleHR1cmUuZm9ybWF0ICE9PSBSR0JGb3JtYXQgKSB7XG5cblx0XHRcdFx0XHRcdGlmICggc3RhdGUuZ2V0Q29tcHJlc3NlZFRleHR1cmVGb3JtYXRzKCkuaW5kZXhPZiggZ2xGb3JtYXQgKSA+IC0gMSApIHtcblxuXHRcdFx0XHRcdFx0XHRzdGF0ZS5jb21wcmVzc2VkVGV4SW1hZ2UyRCggX2dsLlRFWFRVUkVfMkQsIGksIGdsRm9ybWF0LCBtaXBtYXAud2lkdGgsIG1pcG1hcC5oZWlnaHQsIDAsIG1pcG1hcC5kYXRhICk7XG5cblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdFx0Y29uc29sZS53YXJuKCBcIlRIUkVFLldlYkdMUmVuZGVyZXI6IEF0dGVtcHQgdG8gbG9hZCB1bnN1cHBvcnRlZCBjb21wcmVzc2VkIHRleHR1cmUgZm9ybWF0IGluIC51cGxvYWRUZXh0dXJlKClcIiApO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRzdGF0ZS50ZXhJbWFnZTJEKCBfZ2wuVEVYVFVSRV8yRCwgaSwgZ2xGb3JtYXQsIG1pcG1hcC53aWR0aCwgbWlwbWFwLmhlaWdodCwgMCwgZ2xGb3JtYXQsIGdsVHlwZSwgbWlwbWFwLmRhdGEgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0Ly8gcmVndWxhciBUZXh0dXJlIChpbWFnZSwgdmlkZW8sIGNhbnZhcylcblxuXHRcdFx0XHQvLyB1c2UgbWFudWFsbHkgY3JlYXRlZCBtaXBtYXBzIGlmIGF2YWlsYWJsZVxuXHRcdFx0XHQvLyBpZiB0aGVyZSBhcmUgbm8gbWFudWFsIG1pcG1hcHNcblx0XHRcdFx0Ly8gc2V0IDAgbGV2ZWwgbWlwbWFwIGFuZCB0aGVuIHVzZSBHTCB0byBnZW5lcmF0ZSBvdGhlciBtaXBtYXAgbGV2ZWxzXG5cblx0XHRcdFx0aWYgKCBtaXBtYXBzLmxlbmd0aCA+IDAgJiYgaXNQb3dlck9mVHdvSW1hZ2UgKSB7XG5cblx0XHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGlsID0gbWlwbWFwcy5sZW5ndGg7IGkgPCBpbDsgaSArKyApIHtcblxuXHRcdFx0XHRcdFx0bWlwbWFwID0gbWlwbWFwc1sgaSBdO1xuXHRcdFx0XHRcdFx0c3RhdGUudGV4SW1hZ2UyRCggX2dsLlRFWFRVUkVfMkQsIGksIGdsRm9ybWF0LCBnbEZvcm1hdCwgZ2xUeXBlLCBtaXBtYXAgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdHRleHR1cmUuZ2VuZXJhdGVNaXBtYXBzID0gZmFsc2U7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdHN0YXRlLnRleEltYWdlMkQoIF9nbC5URVhUVVJFXzJELCAwLCBnbEZvcm1hdCwgZ2xGb3JtYXQsIGdsVHlwZSwgaW1hZ2UgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCB0ZXh0dXJlLmdlbmVyYXRlTWlwbWFwcyAmJiBpc1Bvd2VyT2ZUd29JbWFnZSApIF9nbC5nZW5lcmF0ZU1pcG1hcCggX2dsLlRFWFRVUkVfMkQgKTtcblxuXHRcdFx0dGV4dHVyZVByb3BlcnRpZXMuX192ZXJzaW9uID0gdGV4dHVyZS52ZXJzaW9uO1xuXG5cdFx0XHRpZiAoIHRleHR1cmUub25VcGRhdGUgKSB0ZXh0dXJlLm9uVXBkYXRlKCB0ZXh0dXJlICk7XG5cblx0XHR9XG5cblx0XHQvLyBSZW5kZXIgdGFyZ2V0c1xuXG5cdFx0Ly8gU2V0dXAgc3RvcmFnZSBmb3IgdGFyZ2V0IHRleHR1cmUgYW5kIGJpbmQgaXQgdG8gY29ycmVjdCBmcmFtZWJ1ZmZlclxuXHRcdGZ1bmN0aW9uIHNldHVwRnJhbWVCdWZmZXJUZXh0dXJlKCBmcmFtZWJ1ZmZlciwgcmVuZGVyVGFyZ2V0LCBhdHRhY2htZW50LCB0ZXh0dXJlVGFyZ2V0ICkge1xuXG5cdFx0XHR2YXIgZ2xGb3JtYXQgPSBwYXJhbVRocmVlVG9HTCggcmVuZGVyVGFyZ2V0LnRleHR1cmUuZm9ybWF0ICk7XG5cdFx0XHR2YXIgZ2xUeXBlID0gcGFyYW1UaHJlZVRvR0woIHJlbmRlclRhcmdldC50ZXh0dXJlLnR5cGUgKTtcblx0XHRcdHN0YXRlLnRleEltYWdlMkQoIHRleHR1cmVUYXJnZXQsIDAsIGdsRm9ybWF0LCByZW5kZXJUYXJnZXQud2lkdGgsIHJlbmRlclRhcmdldC5oZWlnaHQsIDAsIGdsRm9ybWF0LCBnbFR5cGUsIG51bGwgKTtcblx0XHRcdF9nbC5iaW5kRnJhbWVidWZmZXIoIF9nbC5GUkFNRUJVRkZFUiwgZnJhbWVidWZmZXIgKTtcblx0XHRcdF9nbC5mcmFtZWJ1ZmZlclRleHR1cmUyRCggX2dsLkZSQU1FQlVGRkVSLCBhdHRhY2htZW50LCB0ZXh0dXJlVGFyZ2V0LCBwcm9wZXJ0aWVzLmdldCggcmVuZGVyVGFyZ2V0LnRleHR1cmUgKS5fX3dlYmdsVGV4dHVyZSwgMCApO1xuXHRcdFx0X2dsLmJpbmRGcmFtZWJ1ZmZlciggX2dsLkZSQU1FQlVGRkVSLCBudWxsICk7XG5cblx0XHR9XG5cblx0XHQvLyBTZXR1cCBzdG9yYWdlIGZvciBpbnRlcm5hbCBkZXB0aC9zdGVuY2lsIGJ1ZmZlcnMgYW5kIGJpbmQgdG8gY29ycmVjdCBmcmFtZWJ1ZmZlclxuXHRcdGZ1bmN0aW9uIHNldHVwUmVuZGVyQnVmZmVyU3RvcmFnZSggcmVuZGVyYnVmZmVyLCByZW5kZXJUYXJnZXQgKSB7XG5cblx0XHRcdF9nbC5iaW5kUmVuZGVyYnVmZmVyKCBfZ2wuUkVOREVSQlVGRkVSLCByZW5kZXJidWZmZXIgKTtcblxuXHRcdFx0aWYgKCByZW5kZXJUYXJnZXQuZGVwdGhCdWZmZXIgJiYgISByZW5kZXJUYXJnZXQuc3RlbmNpbEJ1ZmZlciApIHtcblxuXHRcdFx0XHRfZ2wucmVuZGVyYnVmZmVyU3RvcmFnZSggX2dsLlJFTkRFUkJVRkZFUiwgX2dsLkRFUFRIX0NPTVBPTkVOVDE2LCByZW5kZXJUYXJnZXQud2lkdGgsIHJlbmRlclRhcmdldC5oZWlnaHQgKTtcblx0XHRcdFx0X2dsLmZyYW1lYnVmZmVyUmVuZGVyYnVmZmVyKCBfZ2wuRlJBTUVCVUZGRVIsIF9nbC5ERVBUSF9BVFRBQ0hNRU5ULCBfZ2wuUkVOREVSQlVGRkVSLCByZW5kZXJidWZmZXIgKTtcblxuXHRcdFx0fSBlbHNlIGlmICggcmVuZGVyVGFyZ2V0LmRlcHRoQnVmZmVyICYmIHJlbmRlclRhcmdldC5zdGVuY2lsQnVmZmVyICkge1xuXG5cdFx0XHRcdF9nbC5yZW5kZXJidWZmZXJTdG9yYWdlKCBfZ2wuUkVOREVSQlVGRkVSLCBfZ2wuREVQVEhfU1RFTkNJTCwgcmVuZGVyVGFyZ2V0LndpZHRoLCByZW5kZXJUYXJnZXQuaGVpZ2h0ICk7XG5cdFx0XHRcdF9nbC5mcmFtZWJ1ZmZlclJlbmRlcmJ1ZmZlciggX2dsLkZSQU1FQlVGRkVSLCBfZ2wuREVQVEhfU1RFTkNJTF9BVFRBQ0hNRU5ULCBfZ2wuUkVOREVSQlVGRkVSLCByZW5kZXJidWZmZXIgKTtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHQvLyBGSVhNRTogV2UgZG9uJ3Qgc3VwcG9ydCAhZGVwdGggIXN0ZW5jaWxcblx0XHRcdFx0X2dsLnJlbmRlcmJ1ZmZlclN0b3JhZ2UoIF9nbC5SRU5ERVJCVUZGRVIsIF9nbC5SR0JBNCwgcmVuZGVyVGFyZ2V0LndpZHRoLCByZW5kZXJUYXJnZXQuaGVpZ2h0ICk7XG5cblx0XHRcdH1cblxuXHRcdFx0X2dsLmJpbmRSZW5kZXJidWZmZXIoIF9nbC5SRU5ERVJCVUZGRVIsIG51bGwgKTtcblxuXHRcdH1cblxuXHRcdC8vIFNldHVwIHJlc291cmNlcyBmb3IgYSBEZXB0aCBUZXh0dXJlIGZvciBhIEZCTyAobmVlZHMgYW4gZXh0ZW5zaW9uKVxuXHRcdGZ1bmN0aW9uIHNldHVwRGVwdGhUZXh0dXJlKCBmcmFtZWJ1ZmZlciwgcmVuZGVyVGFyZ2V0ICkge1xuXG5cdFx0XHR2YXIgaXNDdWJlID0gKCByZW5kZXJUYXJnZXQgJiYgcmVuZGVyVGFyZ2V0LmlzV2ViR0xSZW5kZXJUYXJnZXRDdWJlICk7XG5cdFx0XHRpZiAoIGlzQ3ViZSApIHRocm93IG5ldyBFcnJvcignRGVwdGggVGV4dHVyZSB3aXRoIGN1YmUgcmVuZGVyIHRhcmdldHMgaXMgbm90IHN1cHBvcnRlZCEnKTtcblxuXHRcdFx0X2dsLmJpbmRGcmFtZWJ1ZmZlciggX2dsLkZSQU1FQlVGRkVSLCBmcmFtZWJ1ZmZlciApO1xuXG5cdFx0XHRpZiAoICEoIHJlbmRlclRhcmdldC5kZXB0aFRleHR1cmUgJiYgcmVuZGVyVGFyZ2V0LmRlcHRoVGV4dHVyZS5pc0RlcHRoVGV4dHVyZSApICkge1xuXG5cdFx0XHRcdHRocm93IG5ldyBFcnJvcigncmVuZGVyVGFyZ2V0LmRlcHRoVGV4dHVyZSBtdXN0IGJlIGFuIGluc3RhbmNlIG9mIFRIUkVFLkRlcHRoVGV4dHVyZScpO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIHVwbG9hZCBhbiBlbXB0eSBkZXB0aCB0ZXh0dXJlIHdpdGggZnJhbWVidWZmZXIgc2l6ZVxuXHRcdFx0aWYgKCAhcHJvcGVydGllcy5nZXQoIHJlbmRlclRhcmdldC5kZXB0aFRleHR1cmUgKS5fX3dlYmdsVGV4dHVyZSB8fFxuXHRcdFx0XHRcdHJlbmRlclRhcmdldC5kZXB0aFRleHR1cmUuaW1hZ2Uud2lkdGggIT09IHJlbmRlclRhcmdldC53aWR0aCB8fFxuXHRcdFx0XHRcdHJlbmRlclRhcmdldC5kZXB0aFRleHR1cmUuaW1hZ2UuaGVpZ2h0ICE9PSByZW5kZXJUYXJnZXQuaGVpZ2h0ICkge1xuXHRcdFx0XHRyZW5kZXJUYXJnZXQuZGVwdGhUZXh0dXJlLmltYWdlLndpZHRoID0gcmVuZGVyVGFyZ2V0LndpZHRoO1xuXHRcdFx0XHRyZW5kZXJUYXJnZXQuZGVwdGhUZXh0dXJlLmltYWdlLmhlaWdodCA9IHJlbmRlclRhcmdldC5oZWlnaHQ7XG5cdFx0XHRcdHJlbmRlclRhcmdldC5kZXB0aFRleHR1cmUubmVlZHNVcGRhdGUgPSB0cnVlO1xuXHRcdFx0fVxuXG5cdFx0XHRzZXRUZXh0dXJlMkQoIHJlbmRlclRhcmdldC5kZXB0aFRleHR1cmUsIDAgKTtcblxuXHRcdFx0dmFyIHdlYmdsRGVwdGhUZXh0dXJlID0gcHJvcGVydGllcy5nZXQoIHJlbmRlclRhcmdldC5kZXB0aFRleHR1cmUgKS5fX3dlYmdsVGV4dHVyZTtcblxuXHRcdFx0aWYgKCByZW5kZXJUYXJnZXQuZGVwdGhUZXh0dXJlLmZvcm1hdCA9PT0gRGVwdGhGb3JtYXQgKSB7XG5cblx0XHRcdFx0X2dsLmZyYW1lYnVmZmVyVGV4dHVyZTJEKCBfZ2wuRlJBTUVCVUZGRVIsIF9nbC5ERVBUSF9BVFRBQ0hNRU5ULCBfZ2wuVEVYVFVSRV8yRCwgd2ViZ2xEZXB0aFRleHR1cmUsIDAgKTtcblxuXHRcdFx0fSBlbHNlIGlmICggcmVuZGVyVGFyZ2V0LmRlcHRoVGV4dHVyZS5mb3JtYXQgPT09IERlcHRoU3RlbmNpbEZvcm1hdCApIHtcblxuXHRcdFx0XHRfZ2wuZnJhbWVidWZmZXJUZXh0dXJlMkQoIF9nbC5GUkFNRUJVRkZFUiwgX2dsLkRFUFRIX1NURU5DSUxfQVRUQUNITUVOVCwgX2dsLlRFWFRVUkVfMkQsIHdlYmdsRGVwdGhUZXh0dXJlLCAwICk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0dGhyb3cgbmV3IEVycm9yKCdVbmtub3duIGRlcHRoVGV4dHVyZSBmb3JtYXQnKVxuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHQvLyBTZXR1cCBHTCByZXNvdXJjZXMgZm9yIGEgbm9uLXRleHR1cmUgZGVwdGggYnVmZmVyXG5cdFx0ZnVuY3Rpb24gc2V0dXBEZXB0aFJlbmRlcmJ1ZmZlciggcmVuZGVyVGFyZ2V0ICkge1xuXG5cdFx0XHR2YXIgcmVuZGVyVGFyZ2V0UHJvcGVydGllcyA9IHByb3BlcnRpZXMuZ2V0KCByZW5kZXJUYXJnZXQgKTtcblxuXHRcdFx0dmFyIGlzQ3ViZSA9ICggcmVuZGVyVGFyZ2V0LmlzV2ViR0xSZW5kZXJUYXJnZXRDdWJlID09PSB0cnVlICk7XG5cblx0XHRcdGlmICggcmVuZGVyVGFyZ2V0LmRlcHRoVGV4dHVyZSApIHtcblxuXHRcdFx0XHRpZiAoIGlzQ3ViZSApIHRocm93IG5ldyBFcnJvcigndGFyZ2V0LmRlcHRoVGV4dHVyZSBub3Qgc3VwcG9ydGVkIGluIEN1YmUgcmVuZGVyIHRhcmdldHMnKTtcblxuXHRcdFx0XHRzZXR1cERlcHRoVGV4dHVyZSggcmVuZGVyVGFyZ2V0UHJvcGVydGllcy5fX3dlYmdsRnJhbWVidWZmZXIsIHJlbmRlclRhcmdldCApO1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdGlmICggaXNDdWJlICkge1xuXG5cdFx0XHRcdFx0cmVuZGVyVGFyZ2V0UHJvcGVydGllcy5fX3dlYmdsRGVwdGhidWZmZXIgPSBbXTtcblxuXHRcdFx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IDY7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRcdF9nbC5iaW5kRnJhbWVidWZmZXIoIF9nbC5GUkFNRUJVRkZFUiwgcmVuZGVyVGFyZ2V0UHJvcGVydGllcy5fX3dlYmdsRnJhbWVidWZmZXJbIGkgXSApO1xuXHRcdFx0XHRcdFx0cmVuZGVyVGFyZ2V0UHJvcGVydGllcy5fX3dlYmdsRGVwdGhidWZmZXJbIGkgXSA9IF9nbC5jcmVhdGVSZW5kZXJidWZmZXIoKTtcblx0XHRcdFx0XHRcdHNldHVwUmVuZGVyQnVmZmVyU3RvcmFnZSggcmVuZGVyVGFyZ2V0UHJvcGVydGllcy5fX3dlYmdsRGVwdGhidWZmZXJbIGkgXSwgcmVuZGVyVGFyZ2V0ICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdF9nbC5iaW5kRnJhbWVidWZmZXIoIF9nbC5GUkFNRUJVRkZFUiwgcmVuZGVyVGFyZ2V0UHJvcGVydGllcy5fX3dlYmdsRnJhbWVidWZmZXIgKTtcblx0XHRcdFx0XHRyZW5kZXJUYXJnZXRQcm9wZXJ0aWVzLl9fd2ViZ2xEZXB0aGJ1ZmZlciA9IF9nbC5jcmVhdGVSZW5kZXJidWZmZXIoKTtcblx0XHRcdFx0XHRzZXR1cFJlbmRlckJ1ZmZlclN0b3JhZ2UoIHJlbmRlclRhcmdldFByb3BlcnRpZXMuX193ZWJnbERlcHRoYnVmZmVyLCByZW5kZXJUYXJnZXQgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0X2dsLmJpbmRGcmFtZWJ1ZmZlciggX2dsLkZSQU1FQlVGRkVSLCBudWxsICk7XG5cblx0XHR9XG5cblx0XHQvLyBTZXQgdXAgR0wgcmVzb3VyY2VzIGZvciB0aGUgcmVuZGVyIHRhcmdldFxuXHRcdGZ1bmN0aW9uIHNldHVwUmVuZGVyVGFyZ2V0KCByZW5kZXJUYXJnZXQgKSB7XG5cblx0XHRcdHZhciByZW5kZXJUYXJnZXRQcm9wZXJ0aWVzID0gcHJvcGVydGllcy5nZXQoIHJlbmRlclRhcmdldCApO1xuXHRcdFx0dmFyIHRleHR1cmVQcm9wZXJ0aWVzID0gcHJvcGVydGllcy5nZXQoIHJlbmRlclRhcmdldC50ZXh0dXJlICk7XG5cblx0XHRcdHJlbmRlclRhcmdldC5hZGRFdmVudExpc3RlbmVyKCAnZGlzcG9zZScsIG9uUmVuZGVyVGFyZ2V0RGlzcG9zZSApO1xuXG5cdFx0XHR0ZXh0dXJlUHJvcGVydGllcy5fX3dlYmdsVGV4dHVyZSA9IF9nbC5jcmVhdGVUZXh0dXJlKCk7XG5cblx0XHRcdF9pbmZvTWVtb3J5LnRleHR1cmVzICsrO1xuXG5cdFx0XHR2YXIgaXNDdWJlID0gKCByZW5kZXJUYXJnZXQuaXNXZWJHTFJlbmRlclRhcmdldEN1YmUgPT09IHRydWUgKTtcblx0XHRcdHZhciBpc1RhcmdldFBvd2VyT2ZUd28gPSBpc1Bvd2VyT2ZUd28oIHJlbmRlclRhcmdldCApO1xuXG5cdFx0XHQvLyBTZXR1cCBmcmFtZWJ1ZmZlclxuXG5cdFx0XHRpZiAoIGlzQ3ViZSApIHtcblxuXHRcdFx0XHRyZW5kZXJUYXJnZXRQcm9wZXJ0aWVzLl9fd2ViZ2xGcmFtZWJ1ZmZlciA9IFtdO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IDY7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRyZW5kZXJUYXJnZXRQcm9wZXJ0aWVzLl9fd2ViZ2xGcmFtZWJ1ZmZlclsgaSBdID0gX2dsLmNyZWF0ZUZyYW1lYnVmZmVyKCk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdHJlbmRlclRhcmdldFByb3BlcnRpZXMuX193ZWJnbEZyYW1lYnVmZmVyID0gX2dsLmNyZWF0ZUZyYW1lYnVmZmVyKCk7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gU2V0dXAgY29sb3IgYnVmZmVyXG5cblx0XHRcdGlmICggaXNDdWJlICkge1xuXG5cdFx0XHRcdHN0YXRlLmJpbmRUZXh0dXJlKCBfZ2wuVEVYVFVSRV9DVUJFX01BUCwgdGV4dHVyZVByb3BlcnRpZXMuX193ZWJnbFRleHR1cmUgKTtcblx0XHRcdFx0c2V0VGV4dHVyZVBhcmFtZXRlcnMoIF9nbC5URVhUVVJFX0NVQkVfTUFQLCByZW5kZXJUYXJnZXQudGV4dHVyZSwgaXNUYXJnZXRQb3dlck9mVHdvICk7XG5cblx0XHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgNjsgaSArKyApIHtcblxuXHRcdFx0XHRcdHNldHVwRnJhbWVCdWZmZXJUZXh0dXJlKCByZW5kZXJUYXJnZXRQcm9wZXJ0aWVzLl9fd2ViZ2xGcmFtZWJ1ZmZlclsgaSBdLCByZW5kZXJUYXJnZXQsIF9nbC5DT0xPUl9BVFRBQ0hNRU5UMCwgX2dsLlRFWFRVUkVfQ1VCRV9NQVBfUE9TSVRJVkVfWCArIGkgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCByZW5kZXJUYXJnZXQudGV4dHVyZS5nZW5lcmF0ZU1pcG1hcHMgJiYgaXNUYXJnZXRQb3dlck9mVHdvICkgX2dsLmdlbmVyYXRlTWlwbWFwKCBfZ2wuVEVYVFVSRV9DVUJFX01BUCApO1xuXHRcdFx0XHRzdGF0ZS5iaW5kVGV4dHVyZSggX2dsLlRFWFRVUkVfQ1VCRV9NQVAsIG51bGwgKTtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRzdGF0ZS5iaW5kVGV4dHVyZSggX2dsLlRFWFRVUkVfMkQsIHRleHR1cmVQcm9wZXJ0aWVzLl9fd2ViZ2xUZXh0dXJlICk7XG5cdFx0XHRcdHNldFRleHR1cmVQYXJhbWV0ZXJzKCBfZ2wuVEVYVFVSRV8yRCwgcmVuZGVyVGFyZ2V0LnRleHR1cmUsIGlzVGFyZ2V0UG93ZXJPZlR3byApO1xuXHRcdFx0XHRzZXR1cEZyYW1lQnVmZmVyVGV4dHVyZSggcmVuZGVyVGFyZ2V0UHJvcGVydGllcy5fX3dlYmdsRnJhbWVidWZmZXIsIHJlbmRlclRhcmdldCwgX2dsLkNPTE9SX0FUVEFDSE1FTlQwLCBfZ2wuVEVYVFVSRV8yRCApO1xuXG5cdFx0XHRcdGlmICggcmVuZGVyVGFyZ2V0LnRleHR1cmUuZ2VuZXJhdGVNaXBtYXBzICYmIGlzVGFyZ2V0UG93ZXJPZlR3byApIF9nbC5nZW5lcmF0ZU1pcG1hcCggX2dsLlRFWFRVUkVfMkQgKTtcblx0XHRcdFx0c3RhdGUuYmluZFRleHR1cmUoIF9nbC5URVhUVVJFXzJELCBudWxsICk7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gU2V0dXAgZGVwdGggYW5kIHN0ZW5jaWwgYnVmZmVyc1xuXG5cdFx0XHRpZiAoIHJlbmRlclRhcmdldC5kZXB0aEJ1ZmZlciApIHtcblxuXHRcdFx0XHRzZXR1cERlcHRoUmVuZGVyYnVmZmVyKCByZW5kZXJUYXJnZXQgKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gdXBkYXRlUmVuZGVyVGFyZ2V0TWlwbWFwKCByZW5kZXJUYXJnZXQgKSB7XG5cblx0XHRcdHZhciB0ZXh0dXJlID0gcmVuZGVyVGFyZ2V0LnRleHR1cmU7XG5cblx0XHRcdGlmICggdGV4dHVyZS5nZW5lcmF0ZU1pcG1hcHMgJiYgaXNQb3dlck9mVHdvKCByZW5kZXJUYXJnZXQgKSAmJlxuXHRcdFx0XHRcdHRleHR1cmUubWluRmlsdGVyICE9PSBOZWFyZXN0RmlsdGVyICYmXG5cdFx0XHRcdFx0dGV4dHVyZS5taW5GaWx0ZXIgIT09IExpbmVhckZpbHRlciApIHtcblxuXHRcdFx0XHR2YXIgdGFyZ2V0ID0gKHJlbmRlclRhcmdldCAmJiByZW5kZXJUYXJnZXQuaXNXZWJHTFJlbmRlclRhcmdldEN1YmUpID8gX2dsLlRFWFRVUkVfQ1VCRV9NQVAgOiBfZ2wuVEVYVFVSRV8yRDtcblx0XHRcdFx0dmFyIHdlYmdsVGV4dHVyZSA9IHByb3BlcnRpZXMuZ2V0KCB0ZXh0dXJlICkuX193ZWJnbFRleHR1cmU7XG5cblx0XHRcdFx0c3RhdGUuYmluZFRleHR1cmUoIHRhcmdldCwgd2ViZ2xUZXh0dXJlICk7XG5cdFx0XHRcdF9nbC5nZW5lcmF0ZU1pcG1hcCggdGFyZ2V0ICk7XG5cdFx0XHRcdHN0YXRlLmJpbmRUZXh0dXJlKCB0YXJnZXQsIG51bGwgKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0dGhpcy5zZXRUZXh0dXJlMkQgPSBzZXRUZXh0dXJlMkQ7XG5cdFx0dGhpcy5zZXRUZXh0dXJlQ3ViZSA9IHNldFRleHR1cmVDdWJlO1xuXHRcdHRoaXMuc2V0VGV4dHVyZUN1YmVEeW5hbWljID0gc2V0VGV4dHVyZUN1YmVEeW5hbWljO1xuXHRcdHRoaXMuc2V0dXBSZW5kZXJUYXJnZXQgPSBzZXR1cFJlbmRlclRhcmdldDtcblx0XHR0aGlzLnVwZGF0ZVJlbmRlclRhcmdldE1pcG1hcCA9IHVwZGF0ZVJlbmRlclRhcmdldE1pcG1hcDtcblxuXHR9XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgZm9yZGFjaW91cyAvIGZvcmRhY2lvdXMuZ2l0aHViLmlvXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFdlYkdMUHJvcGVydGllcygpIHtcblxuXHRcdHZhciBwcm9wZXJ0aWVzID0ge307XG5cblx0XHRyZXR1cm4ge1xuXG5cdFx0XHRnZXQ6IGZ1bmN0aW9uICggb2JqZWN0ICkge1xuXG5cdFx0XHRcdHZhciB1dWlkID0gb2JqZWN0LnV1aWQ7XG5cdFx0XHRcdHZhciBtYXAgPSBwcm9wZXJ0aWVzWyB1dWlkIF07XG5cblx0XHRcdFx0aWYgKCBtYXAgPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdG1hcCA9IHt9O1xuXHRcdFx0XHRcdHByb3BlcnRpZXNbIHV1aWQgXSA9IG1hcDtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuIG1hcDtcblxuXHRcdFx0fSxcblxuXHRcdFx0ZGVsZXRlOiBmdW5jdGlvbiAoIG9iamVjdCApIHtcblxuXHRcdFx0XHRkZWxldGUgcHJvcGVydGllc1sgb2JqZWN0LnV1aWQgXTtcblxuXHRcdFx0fSxcblxuXHRcdFx0Y2xlYXI6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0XHRwcm9wZXJ0aWVzID0ge307XG5cblx0XHRcdH1cblxuXHRcdH07XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBXZWJHTFN0YXRlKCBnbCwgZXh0ZW5zaW9ucywgcGFyYW1UaHJlZVRvR0wgKSB7XG5cblx0XHRmdW5jdGlvbiBDb2xvckJ1ZmZlcigpIHtcblxuXHRcdFx0dmFyIGxvY2tlZCA9IGZhbHNlO1xuXG5cdFx0XHR2YXIgY29sb3IgPSBuZXcgVmVjdG9yNCgpO1xuXHRcdFx0dmFyIGN1cnJlbnRDb2xvck1hc2sgPSBudWxsO1xuXHRcdFx0dmFyIGN1cnJlbnRDb2xvckNsZWFyID0gbmV3IFZlY3RvcjQoKTtcblxuXHRcdFx0cmV0dXJuIHtcblxuXHRcdFx0XHRzZXRNYXNrOiBmdW5jdGlvbiAoIGNvbG9yTWFzayApIHtcblxuXHRcdFx0XHRcdGlmICggY3VycmVudENvbG9yTWFzayAhPT0gY29sb3JNYXNrICYmICEgbG9ja2VkICkge1xuXG5cdFx0XHRcdFx0XHRnbC5jb2xvck1hc2soIGNvbG9yTWFzaywgY29sb3JNYXNrLCBjb2xvck1hc2ssIGNvbG9yTWFzayApO1xuXHRcdFx0XHRcdFx0Y3VycmVudENvbG9yTWFzayA9IGNvbG9yTWFzaztcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9LFxuXG5cdFx0XHRcdHNldExvY2tlZDogZnVuY3Rpb24gKCBsb2NrICkge1xuXG5cdFx0XHRcdFx0bG9ja2VkID0gbG9jaztcblxuXHRcdFx0XHR9LFxuXG5cdFx0XHRcdHNldENsZWFyOiBmdW5jdGlvbiAoIHIsIGcsIGIsIGEsIHByZW11bHRpcGxpZWRBbHBoYSApIHtcblxuXHRcdFx0XHRcdGlmICggcHJlbXVsdGlwbGllZEFscGhhID09PSB0cnVlICkge1xuXG5cdFx0XHRcdFx0XHRyICo9IGE7IGcgKj0gYTsgYiAqPSBhO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0Y29sb3Iuc2V0KCByLCBnLCBiLCBhICk7XG5cblx0XHRcdFx0XHRpZiAoIGN1cnJlbnRDb2xvckNsZWFyLmVxdWFscyggY29sb3IgKSA9PT0gZmFsc2UgKSB7XG5cblx0XHRcdFx0XHRcdGdsLmNsZWFyQ29sb3IoIHIsIGcsIGIsIGEgKTtcblx0XHRcdFx0XHRcdGN1cnJlbnRDb2xvckNsZWFyLmNvcHkoIGNvbG9yICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSxcblxuXHRcdFx0XHRyZXNldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdFx0bG9ja2VkID0gZmFsc2U7XG5cblx0XHRcdFx0XHRjdXJyZW50Q29sb3JNYXNrID0gbnVsbDtcblx0XHRcdFx0XHRjdXJyZW50Q29sb3JDbGVhci5zZXQoIDAsIDAsIDAsIDEgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH07XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBEZXB0aEJ1ZmZlcigpIHtcblxuXHRcdFx0dmFyIGxvY2tlZCA9IGZhbHNlO1xuXG5cdFx0XHR2YXIgY3VycmVudERlcHRoTWFzayA9IG51bGw7XG5cdFx0XHR2YXIgY3VycmVudERlcHRoRnVuYyA9IG51bGw7XG5cdFx0XHR2YXIgY3VycmVudERlcHRoQ2xlYXIgPSBudWxsO1xuXG5cdFx0XHRyZXR1cm4ge1xuXG5cdFx0XHRcdHNldFRlc3Q6IGZ1bmN0aW9uICggZGVwdGhUZXN0ICkge1xuXG5cdFx0XHRcdFx0aWYgKCBkZXB0aFRlc3QgKSB7XG5cblx0XHRcdFx0XHRcdGVuYWJsZSggZ2wuREVQVEhfVEVTVCApO1xuXG5cdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0ZGlzYWJsZSggZ2wuREVQVEhfVEVTVCApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH0sXG5cblx0XHRcdFx0c2V0TWFzazogZnVuY3Rpb24gKCBkZXB0aE1hc2sgKSB7XG5cblx0XHRcdFx0XHRpZiAoIGN1cnJlbnREZXB0aE1hc2sgIT09IGRlcHRoTWFzayAmJiAhIGxvY2tlZCApIHtcblxuXHRcdFx0XHRcdFx0Z2wuZGVwdGhNYXNrKCBkZXB0aE1hc2sgKTtcblx0XHRcdFx0XHRcdGN1cnJlbnREZXB0aE1hc2sgPSBkZXB0aE1hc2s7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSxcblxuXHRcdFx0XHRzZXRGdW5jOiBmdW5jdGlvbiAoIGRlcHRoRnVuYyApIHtcblxuXHRcdFx0XHRcdGlmICggY3VycmVudERlcHRoRnVuYyAhPT0gZGVwdGhGdW5jICkge1xuXG5cdFx0XHRcdFx0XHRpZiAoIGRlcHRoRnVuYyApIHtcblxuXHRcdFx0XHRcdFx0XHRzd2l0Y2ggKCBkZXB0aEZ1bmMgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHRjYXNlIE5ldmVyRGVwdGg6XG5cblx0XHRcdFx0XHRcdFx0XHRcdGdsLmRlcHRoRnVuYyggZ2wuTkVWRVIgKTtcblx0XHRcdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRcdFx0Y2FzZSBBbHdheXNEZXB0aDpcblxuXHRcdFx0XHRcdFx0XHRcdFx0Z2wuZGVwdGhGdW5jKCBnbC5BTFdBWVMgKTtcblx0XHRcdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRcdFx0Y2FzZSBMZXNzRGVwdGg6XG5cblx0XHRcdFx0XHRcdFx0XHRcdGdsLmRlcHRoRnVuYyggZ2wuTEVTUyApO1xuXHRcdFx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRcdFx0XHRjYXNlIExlc3NFcXVhbERlcHRoOlxuXG5cdFx0XHRcdFx0XHRcdFx0XHRnbC5kZXB0aEZ1bmMoIGdsLkxFUVVBTCApO1xuXHRcdFx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRcdFx0XHRjYXNlIEVxdWFsRGVwdGg6XG5cblx0XHRcdFx0XHRcdFx0XHRcdGdsLmRlcHRoRnVuYyggZ2wuRVFVQUwgKTtcblx0XHRcdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRcdFx0Y2FzZSBHcmVhdGVyRXF1YWxEZXB0aDpcblxuXHRcdFx0XHRcdFx0XHRcdFx0Z2wuZGVwdGhGdW5jKCBnbC5HRVFVQUwgKTtcblx0XHRcdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRcdFx0Y2FzZSBHcmVhdGVyRGVwdGg6XG5cblx0XHRcdFx0XHRcdFx0XHRcdGdsLmRlcHRoRnVuYyggZ2wuR1JFQVRFUiApO1xuXHRcdFx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRcdFx0XHRjYXNlIE5vdEVxdWFsRGVwdGg6XG5cblx0XHRcdFx0XHRcdFx0XHRcdGdsLmRlcHRoRnVuYyggZ2wuTk9URVFVQUwgKTtcblx0XHRcdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRcdFx0ZGVmYXVsdDpcblxuXHRcdFx0XHRcdFx0XHRcdFx0Z2wuZGVwdGhGdW5jKCBnbC5MRVFVQUwgKTtcblxuXHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdFx0Z2wuZGVwdGhGdW5jKCBnbC5MRVFVQUwgKTtcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRjdXJyZW50RGVwdGhGdW5jID0gZGVwdGhGdW5jO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH0sXG5cblx0XHRcdFx0c2V0TG9ja2VkOiBmdW5jdGlvbiAoIGxvY2sgKSB7XG5cblx0XHRcdFx0XHRsb2NrZWQgPSBsb2NrO1xuXG5cdFx0XHRcdH0sXG5cblx0XHRcdFx0c2V0Q2xlYXI6IGZ1bmN0aW9uICggZGVwdGggKSB7XG5cblx0XHRcdFx0XHRpZiAoIGN1cnJlbnREZXB0aENsZWFyICE9PSBkZXB0aCApIHtcblxuXHRcdFx0XHRcdFx0Z2wuY2xlYXJEZXB0aCggZGVwdGggKTtcblx0XHRcdFx0XHRcdGN1cnJlbnREZXB0aENsZWFyID0gZGVwdGg7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSxcblxuXHRcdFx0XHRyZXNldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdFx0bG9ja2VkID0gZmFsc2U7XG5cblx0XHRcdFx0XHRjdXJyZW50RGVwdGhNYXNrID0gbnVsbDtcblx0XHRcdFx0XHRjdXJyZW50RGVwdGhGdW5jID0gbnVsbDtcblx0XHRcdFx0XHRjdXJyZW50RGVwdGhDbGVhciA9IG51bGw7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9O1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gU3RlbmNpbEJ1ZmZlcigpIHtcblxuXHRcdFx0dmFyIGxvY2tlZCA9IGZhbHNlO1xuXG5cdFx0XHR2YXIgY3VycmVudFN0ZW5jaWxNYXNrID0gbnVsbDtcblx0XHRcdHZhciBjdXJyZW50U3RlbmNpbEZ1bmMgPSBudWxsO1xuXHRcdFx0dmFyIGN1cnJlbnRTdGVuY2lsUmVmID0gbnVsbDtcblx0XHRcdHZhciBjdXJyZW50U3RlbmNpbEZ1bmNNYXNrID0gbnVsbDtcblx0XHRcdHZhciBjdXJyZW50U3RlbmNpbEZhaWwgPSBudWxsO1xuXHRcdFx0dmFyIGN1cnJlbnRTdGVuY2lsWkZhaWwgPSBudWxsO1xuXHRcdFx0dmFyIGN1cnJlbnRTdGVuY2lsWlBhc3MgPSBudWxsO1xuXHRcdFx0dmFyIGN1cnJlbnRTdGVuY2lsQ2xlYXIgPSBudWxsO1xuXG5cdFx0XHRyZXR1cm4ge1xuXG5cdFx0XHRcdHNldFRlc3Q6IGZ1bmN0aW9uICggc3RlbmNpbFRlc3QgKSB7XG5cblx0XHRcdFx0XHRpZiAoIHN0ZW5jaWxUZXN0ICkge1xuXG5cdFx0XHRcdFx0XHRlbmFibGUoIGdsLlNURU5DSUxfVEVTVCApO1xuXG5cdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0ZGlzYWJsZSggZ2wuU1RFTkNJTF9URVNUICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSxcblxuXHRcdFx0XHRzZXRNYXNrOiBmdW5jdGlvbiAoIHN0ZW5jaWxNYXNrICkge1xuXG5cdFx0XHRcdFx0aWYgKCBjdXJyZW50U3RlbmNpbE1hc2sgIT09IHN0ZW5jaWxNYXNrICYmICEgbG9ja2VkICkge1xuXG5cdFx0XHRcdFx0XHRnbC5zdGVuY2lsTWFzayggc3RlbmNpbE1hc2sgKTtcblx0XHRcdFx0XHRcdGN1cnJlbnRTdGVuY2lsTWFzayA9IHN0ZW5jaWxNYXNrO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH0sXG5cblx0XHRcdFx0c2V0RnVuYzogZnVuY3Rpb24gKCBzdGVuY2lsRnVuYywgc3RlbmNpbFJlZiwgc3RlbmNpbE1hc2sgKSB7XG5cblx0XHRcdFx0XHRpZiAoIGN1cnJlbnRTdGVuY2lsRnVuYyAhPT0gc3RlbmNpbEZ1bmMgfHxcblx0XHRcdFx0XHQgICAgIGN1cnJlbnRTdGVuY2lsUmVmIFx0IT09IHN0ZW5jaWxSZWYgXHR8fFxuXHRcdFx0XHRcdCAgICAgY3VycmVudFN0ZW5jaWxGdW5jTWFzayAhPT0gc3RlbmNpbE1hc2sgKSB7XG5cblx0XHRcdFx0XHRcdGdsLnN0ZW5jaWxGdW5jKCBzdGVuY2lsRnVuYywgc3RlbmNpbFJlZiwgc3RlbmNpbE1hc2sgKTtcblxuXHRcdFx0XHRcdFx0Y3VycmVudFN0ZW5jaWxGdW5jID0gc3RlbmNpbEZ1bmM7XG5cdFx0XHRcdFx0XHRjdXJyZW50U3RlbmNpbFJlZiA9IHN0ZW5jaWxSZWY7XG5cdFx0XHRcdFx0XHRjdXJyZW50U3RlbmNpbEZ1bmNNYXNrID0gc3RlbmNpbE1hc2s7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSxcblxuXHRcdFx0XHRzZXRPcDogZnVuY3Rpb24gKCBzdGVuY2lsRmFpbCwgc3RlbmNpbFpGYWlsLCBzdGVuY2lsWlBhc3MgKSB7XG5cblx0XHRcdFx0XHRpZiAoIGN1cnJlbnRTdGVuY2lsRmFpbFx0ICE9PSBzdGVuY2lsRmFpbCBcdHx8XG5cdFx0XHRcdFx0ICAgICBjdXJyZW50U3RlbmNpbFpGYWlsICE9PSBzdGVuY2lsWkZhaWwgfHxcblx0XHRcdFx0XHQgICAgIGN1cnJlbnRTdGVuY2lsWlBhc3MgIT09IHN0ZW5jaWxaUGFzcyApIHtcblxuXHRcdFx0XHRcdFx0Z2wuc3RlbmNpbE9wKCBzdGVuY2lsRmFpbCwgc3RlbmNpbFpGYWlsLCBzdGVuY2lsWlBhc3MgKTtcblxuXHRcdFx0XHRcdFx0Y3VycmVudFN0ZW5jaWxGYWlsID0gc3RlbmNpbEZhaWw7XG5cdFx0XHRcdFx0XHRjdXJyZW50U3RlbmNpbFpGYWlsID0gc3RlbmNpbFpGYWlsO1xuXHRcdFx0XHRcdFx0Y3VycmVudFN0ZW5jaWxaUGFzcyA9IHN0ZW5jaWxaUGFzcztcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9LFxuXG5cdFx0XHRcdHNldExvY2tlZDogZnVuY3Rpb24gKCBsb2NrICkge1xuXG5cdFx0XHRcdFx0bG9ja2VkID0gbG9jaztcblxuXHRcdFx0XHR9LFxuXG5cdFx0XHRcdHNldENsZWFyOiBmdW5jdGlvbiAoIHN0ZW5jaWwgKSB7XG5cblx0XHRcdFx0XHRpZiAoIGN1cnJlbnRTdGVuY2lsQ2xlYXIgIT09IHN0ZW5jaWwgKSB7XG5cblx0XHRcdFx0XHRcdGdsLmNsZWFyU3RlbmNpbCggc3RlbmNpbCApO1xuXHRcdFx0XHRcdFx0Y3VycmVudFN0ZW5jaWxDbGVhciA9IHN0ZW5jaWw7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSxcblxuXHRcdFx0XHRyZXNldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdFx0bG9ja2VkID0gZmFsc2U7XG5cblx0XHRcdFx0XHRjdXJyZW50U3RlbmNpbE1hc2sgPSBudWxsO1xuXHRcdFx0XHRcdGN1cnJlbnRTdGVuY2lsRnVuYyA9IG51bGw7XG5cdFx0XHRcdFx0Y3VycmVudFN0ZW5jaWxSZWYgPSBudWxsO1xuXHRcdFx0XHRcdGN1cnJlbnRTdGVuY2lsRnVuY01hc2sgPSBudWxsO1xuXHRcdFx0XHRcdGN1cnJlbnRTdGVuY2lsRmFpbCA9IG51bGw7XG5cdFx0XHRcdFx0Y3VycmVudFN0ZW5jaWxaRmFpbCA9IG51bGw7XG5cdFx0XHRcdFx0Y3VycmVudFN0ZW5jaWxaUGFzcyA9IG51bGw7XG5cdFx0XHRcdFx0Y3VycmVudFN0ZW5jaWxDbGVhciA9IG51bGw7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9O1xuXG5cdFx0fVxuXG5cdFx0Ly9cblxuXHRcdHZhciBjb2xvckJ1ZmZlciA9IG5ldyBDb2xvckJ1ZmZlcigpO1xuXHRcdHZhciBkZXB0aEJ1ZmZlciA9IG5ldyBEZXB0aEJ1ZmZlcigpO1xuXHRcdHZhciBzdGVuY2lsQnVmZmVyID0gbmV3IFN0ZW5jaWxCdWZmZXIoKTtcblxuXHRcdHZhciBtYXhWZXJ0ZXhBdHRyaWJ1dGVzID0gZ2wuZ2V0UGFyYW1ldGVyKCBnbC5NQVhfVkVSVEVYX0FUVFJJQlMgKTtcblx0XHR2YXIgbmV3QXR0cmlidXRlcyA9IG5ldyBVaW50OEFycmF5KCBtYXhWZXJ0ZXhBdHRyaWJ1dGVzICk7XG5cdFx0dmFyIGVuYWJsZWRBdHRyaWJ1dGVzID0gbmV3IFVpbnQ4QXJyYXkoIG1heFZlcnRleEF0dHJpYnV0ZXMgKTtcblx0XHR2YXIgYXR0cmlidXRlRGl2aXNvcnMgPSBuZXcgVWludDhBcnJheSggbWF4VmVydGV4QXR0cmlidXRlcyApO1xuXG5cdFx0dmFyIGNhcGFiaWxpdGllcyA9IHt9O1xuXG5cdFx0dmFyIGNvbXByZXNzZWRUZXh0dXJlRm9ybWF0cyA9IG51bGw7XG5cblx0XHR2YXIgY3VycmVudEJsZW5kaW5nID0gbnVsbDtcblx0XHR2YXIgY3VycmVudEJsZW5kRXF1YXRpb24gPSBudWxsO1xuXHRcdHZhciBjdXJyZW50QmxlbmRTcmMgPSBudWxsO1xuXHRcdHZhciBjdXJyZW50QmxlbmREc3QgPSBudWxsO1xuXHRcdHZhciBjdXJyZW50QmxlbmRFcXVhdGlvbkFscGhhID0gbnVsbDtcblx0XHR2YXIgY3VycmVudEJsZW5kU3JjQWxwaGEgPSBudWxsO1xuXHRcdHZhciBjdXJyZW50QmxlbmREc3RBbHBoYSA9IG51bGw7XG5cdFx0dmFyIGN1cnJlbnRQcmVtdWx0aXBsZWRBbHBoYSA9IGZhbHNlO1xuXG5cdFx0dmFyIGN1cnJlbnRGbGlwU2lkZWQgPSBudWxsO1xuXHRcdHZhciBjdXJyZW50Q3VsbEZhY2UgPSBudWxsO1xuXG5cdFx0dmFyIGN1cnJlbnRMaW5lV2lkdGggPSBudWxsO1xuXG5cdFx0dmFyIGN1cnJlbnRQb2x5Z29uT2Zmc2V0RmFjdG9yID0gbnVsbDtcblx0XHR2YXIgY3VycmVudFBvbHlnb25PZmZzZXRVbml0cyA9IG51bGw7XG5cblx0XHR2YXIgY3VycmVudFNjaXNzb3JUZXN0ID0gbnVsbDtcblxuXHRcdHZhciBtYXhUZXh0dXJlcyA9IGdsLmdldFBhcmFtZXRlciggZ2wuTUFYX1RFWFRVUkVfSU1BR0VfVU5JVFMgKTtcblxuXHRcdHZhciB2ZXJzaW9uID0gcGFyc2VGbG9hdCggL15XZWJHTFxcIChbMC05XSkvLmV4ZWMoIGdsLmdldFBhcmFtZXRlciggZ2wuVkVSU0lPTiApIClbIDEgXSApO1xuXHRcdHZhciBsaW5lV2lkdGhBdmFpbGFibGUgPSBwYXJzZUZsb2F0KCB2ZXJzaW9uICkgPj0gMS4wO1xuXG5cdFx0dmFyIGN1cnJlbnRUZXh0dXJlU2xvdCA9IG51bGw7XG5cdFx0dmFyIGN1cnJlbnRCb3VuZFRleHR1cmVzID0ge307XG5cblx0XHR2YXIgY3VycmVudFNjaXNzb3IgPSBuZXcgVmVjdG9yNCgpO1xuXHRcdHZhciBjdXJyZW50Vmlld3BvcnQgPSBuZXcgVmVjdG9yNCgpO1xuXG5cdFx0ZnVuY3Rpb24gY3JlYXRlVGV4dHVyZSggdHlwZSwgdGFyZ2V0LCBjb3VudCApIHtcblxuXHRcdFx0dmFyIGRhdGEgPSBuZXcgVWludDhBcnJheSggNCApOyAvLyA0IGlzIHJlcXVpcmVkIHRvIG1hdGNoIGRlZmF1bHQgdW5wYWNrIGFsaWdubWVudCBvZiA0LlxuXHRcdFx0dmFyIHRleHR1cmUgPSBnbC5jcmVhdGVUZXh0dXJlKCk7XG5cblx0XHRcdGdsLmJpbmRUZXh0dXJlKCB0eXBlLCB0ZXh0dXJlICk7XG5cdFx0XHRnbC50ZXhQYXJhbWV0ZXJpKCB0eXBlLCBnbC5URVhUVVJFX01JTl9GSUxURVIsIGdsLk5FQVJFU1QgKTtcblx0XHRcdGdsLnRleFBhcmFtZXRlcmkoIHR5cGUsIGdsLlRFWFRVUkVfTUFHX0ZJTFRFUiwgZ2wuTkVBUkVTVCApO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBjb3VudDsgaSArKyApIHtcblxuXHRcdFx0XHRnbC50ZXhJbWFnZTJEKCB0YXJnZXQgKyBpLCAwLCBnbC5SR0JBLCAxLCAxLCAwLCBnbC5SR0JBLCBnbC5VTlNJR05FRF9CWVRFLCBkYXRhICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRleHR1cmU7XG5cblx0XHR9XG5cblx0XHR2YXIgZW1wdHlUZXh0dXJlcyA9IHt9O1xuXHRcdGVtcHR5VGV4dHVyZXNbIGdsLlRFWFRVUkVfMkQgXSA9IGNyZWF0ZVRleHR1cmUoIGdsLlRFWFRVUkVfMkQsIGdsLlRFWFRVUkVfMkQsIDEgKTtcblx0XHRlbXB0eVRleHR1cmVzWyBnbC5URVhUVVJFX0NVQkVfTUFQIF0gPSBjcmVhdGVUZXh0dXJlKCBnbC5URVhUVVJFX0NVQkVfTUFQLCBnbC5URVhUVVJFX0NVQkVfTUFQX1BPU0lUSVZFX1gsIDYgKTtcblxuXHRcdC8vXG5cblx0XHRmdW5jdGlvbiBpbml0KCkge1xuXG5cdFx0XHRjb2xvckJ1ZmZlci5zZXRDbGVhciggMCwgMCwgMCwgMSApO1xuXHRcdFx0ZGVwdGhCdWZmZXIuc2V0Q2xlYXIoIDEgKTtcblx0XHRcdHN0ZW5jaWxCdWZmZXIuc2V0Q2xlYXIoIDAgKTtcblxuXHRcdFx0ZW5hYmxlKCBnbC5ERVBUSF9URVNUICk7XG5cdFx0XHRzZXREZXB0aEZ1bmMoIExlc3NFcXVhbERlcHRoICk7XG5cblx0XHRcdHNldEZsaXBTaWRlZCggZmFsc2UgKTtcblx0XHRcdHNldEN1bGxGYWNlKCBDdWxsRmFjZUJhY2sgKTtcblx0XHRcdGVuYWJsZSggZ2wuQ1VMTF9GQUNFICk7XG5cblx0XHRcdGVuYWJsZSggZ2wuQkxFTkQgKTtcblx0XHRcdHNldEJsZW5kaW5nKCBOb3JtYWxCbGVuZGluZyApO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gaW5pdEF0dHJpYnV0ZXMoKSB7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IG5ld0F0dHJpYnV0ZXMubGVuZ3RoOyBpIDwgbDsgaSArKyApIHtcblxuXHRcdFx0XHRuZXdBdHRyaWJ1dGVzWyBpIF0gPSAwO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBlbmFibGVBdHRyaWJ1dGUoIGF0dHJpYnV0ZSApIHtcblxuXHRcdFx0bmV3QXR0cmlidXRlc1sgYXR0cmlidXRlIF0gPSAxO1xuXG5cdFx0XHRpZiAoIGVuYWJsZWRBdHRyaWJ1dGVzWyBhdHRyaWJ1dGUgXSA9PT0gMCApIHtcblxuXHRcdFx0XHRnbC5lbmFibGVWZXJ0ZXhBdHRyaWJBcnJheSggYXR0cmlidXRlICk7XG5cdFx0XHRcdGVuYWJsZWRBdHRyaWJ1dGVzWyBhdHRyaWJ1dGUgXSA9IDE7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBhdHRyaWJ1dGVEaXZpc29yc1sgYXR0cmlidXRlIF0gIT09IDAgKSB7XG5cblx0XHRcdFx0dmFyIGV4dGVuc2lvbiA9IGV4dGVuc2lvbnMuZ2V0KCAnQU5HTEVfaW5zdGFuY2VkX2FycmF5cycgKTtcblxuXHRcdFx0XHRleHRlbnNpb24udmVydGV4QXR0cmliRGl2aXNvckFOR0xFKCBhdHRyaWJ1dGUsIDAgKTtcblx0XHRcdFx0YXR0cmlidXRlRGl2aXNvcnNbIGF0dHJpYnV0ZSBdID0gMDtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gZW5hYmxlQXR0cmlidXRlQW5kRGl2aXNvciggYXR0cmlidXRlLCBtZXNoUGVyQXR0cmlidXRlLCBleHRlbnNpb24gKSB7XG5cblx0XHRcdG5ld0F0dHJpYnV0ZXNbIGF0dHJpYnV0ZSBdID0gMTtcblxuXHRcdFx0aWYgKCBlbmFibGVkQXR0cmlidXRlc1sgYXR0cmlidXRlIF0gPT09IDAgKSB7XG5cblx0XHRcdFx0Z2wuZW5hYmxlVmVydGV4QXR0cmliQXJyYXkoIGF0dHJpYnV0ZSApO1xuXHRcdFx0XHRlbmFibGVkQXR0cmlidXRlc1sgYXR0cmlidXRlIF0gPSAxO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggYXR0cmlidXRlRGl2aXNvcnNbIGF0dHJpYnV0ZSBdICE9PSBtZXNoUGVyQXR0cmlidXRlICkge1xuXG5cdFx0XHRcdGV4dGVuc2lvbi52ZXJ0ZXhBdHRyaWJEaXZpc29yQU5HTEUoIGF0dHJpYnV0ZSwgbWVzaFBlckF0dHJpYnV0ZSApO1xuXHRcdFx0XHRhdHRyaWJ1dGVEaXZpc29yc1sgYXR0cmlidXRlIF0gPSBtZXNoUGVyQXR0cmlidXRlO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBkaXNhYmxlVW51c2VkQXR0cmlidXRlcygpIHtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBsID0gZW5hYmxlZEF0dHJpYnV0ZXMubGVuZ3RoOyBpICE9PSBsOyArKyBpICkge1xuXG5cdFx0XHRcdGlmICggZW5hYmxlZEF0dHJpYnV0ZXNbIGkgXSAhPT0gbmV3QXR0cmlidXRlc1sgaSBdICkge1xuXG5cdFx0XHRcdFx0Z2wuZGlzYWJsZVZlcnRleEF0dHJpYkFycmF5KCBpICk7XG5cdFx0XHRcdFx0ZW5hYmxlZEF0dHJpYnV0ZXNbIGkgXSA9IDA7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBlbmFibGUoIGlkICkge1xuXG5cdFx0XHRpZiAoIGNhcGFiaWxpdGllc1sgaWQgXSAhPT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRnbC5lbmFibGUoIGlkICk7XG5cdFx0XHRcdGNhcGFiaWxpdGllc1sgaWQgXSA9IHRydWU7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIGRpc2FibGUoIGlkICkge1xuXG5cdFx0XHRpZiAoIGNhcGFiaWxpdGllc1sgaWQgXSAhPT0gZmFsc2UgKSB7XG5cblx0XHRcdFx0Z2wuZGlzYWJsZSggaWQgKTtcblx0XHRcdFx0Y2FwYWJpbGl0aWVzWyBpZCBdID0gZmFsc2U7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIGdldENvbXByZXNzZWRUZXh0dXJlRm9ybWF0cygpIHtcblxuXHRcdFx0aWYgKCBjb21wcmVzc2VkVGV4dHVyZUZvcm1hdHMgPT09IG51bGwgKSB7XG5cblx0XHRcdFx0Y29tcHJlc3NlZFRleHR1cmVGb3JtYXRzID0gW107XG5cblx0XHRcdFx0aWYgKCBleHRlbnNpb25zLmdldCggJ1dFQkdMX2NvbXByZXNzZWRfdGV4dHVyZV9wdnJ0YycgKSB8fFxuXHRcdFx0XHQgICAgIGV4dGVuc2lvbnMuZ2V0KCAnV0VCR0xfY29tcHJlc3NlZF90ZXh0dXJlX3MzdGMnICkgfHxcblx0XHRcdFx0ICAgICBleHRlbnNpb25zLmdldCggJ1dFQkdMX2NvbXByZXNzZWRfdGV4dHVyZV9ldGMxJyApICkge1xuXG5cdFx0XHRcdFx0dmFyIGZvcm1hdHMgPSBnbC5nZXRQYXJhbWV0ZXIoIGdsLkNPTVBSRVNTRURfVEVYVFVSRV9GT1JNQVRTICk7XG5cblx0XHRcdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBmb3JtYXRzLmxlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHRcdFx0Y29tcHJlc3NlZFRleHR1cmVGb3JtYXRzLnB1c2goIGZvcm1hdHNbIGkgXSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gY29tcHJlc3NlZFRleHR1cmVGb3JtYXRzO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gc2V0QmxlbmRpbmcoIGJsZW5kaW5nLCBibGVuZEVxdWF0aW9uLCBibGVuZFNyYywgYmxlbmREc3QsIGJsZW5kRXF1YXRpb25BbHBoYSwgYmxlbmRTcmNBbHBoYSwgYmxlbmREc3RBbHBoYSwgcHJlbXVsdGlwbGllZEFscGhhICkge1xuXG5cdFx0XHRpZiAoIGJsZW5kaW5nICE9PSBOb0JsZW5kaW5nICkge1xuXG5cdFx0XHRcdGVuYWJsZSggZ2wuQkxFTkQgKTtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRkaXNhYmxlKCBnbC5CTEVORCApO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggYmxlbmRpbmcgIT09IGN1cnJlbnRCbGVuZGluZyB8fCBwcmVtdWx0aXBsaWVkQWxwaGEgIT09IGN1cnJlbnRQcmVtdWx0aXBsZWRBbHBoYSApIHtcblxuXHRcdFx0XHRpZiAoIGJsZW5kaW5nID09PSBBZGRpdGl2ZUJsZW5kaW5nICkge1xuXG5cdFx0XHRcdFx0aWYgKCBwcmVtdWx0aXBsaWVkQWxwaGEgKSB7XG5cblx0XHRcdFx0XHRcdGdsLmJsZW5kRXF1YXRpb25TZXBhcmF0ZSggZ2wuRlVOQ19BREQsIGdsLkZVTkNfQUREICk7XG5cdFx0XHRcdFx0XHRnbC5ibGVuZEZ1bmNTZXBhcmF0ZSggZ2wuT05FLCBnbC5PTkUsIGdsLk9ORSwgZ2wuT05FICk7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRnbC5ibGVuZEVxdWF0aW9uKCBnbC5GVU5DX0FERCApO1xuXHRcdFx0XHRcdFx0Z2wuYmxlbmRGdW5jKCBnbC5TUkNfQUxQSEEsIGdsLk9ORSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH0gZWxzZSBpZiAoIGJsZW5kaW5nID09PSBTdWJ0cmFjdGl2ZUJsZW5kaW5nICkge1xuXG5cdFx0XHRcdFx0aWYgKCBwcmVtdWx0aXBsaWVkQWxwaGEgKSB7XG5cblx0XHRcdFx0XHRcdGdsLmJsZW5kRXF1YXRpb25TZXBhcmF0ZSggZ2wuRlVOQ19BREQsIGdsLkZVTkNfQUREICk7XG5cdFx0XHRcdFx0XHRnbC5ibGVuZEZ1bmNTZXBhcmF0ZSggZ2wuWkVSTywgZ2wuWkVSTywgZ2wuT05FX01JTlVTX1NSQ19DT0xPUiwgZ2wuT05FX01JTlVTX1NSQ19BTFBIQSApO1xuXG5cdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0Z2wuYmxlbmRFcXVhdGlvbiggZ2wuRlVOQ19BREQgKTtcblx0XHRcdFx0XHRcdGdsLmJsZW5kRnVuYyggZ2wuWkVSTywgZ2wuT05FX01JTlVTX1NSQ19DT0xPUiApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH0gZWxzZSBpZiAoIGJsZW5kaW5nID09PSBNdWx0aXBseUJsZW5kaW5nICkge1xuXG5cdFx0XHRcdFx0aWYgKCBwcmVtdWx0aXBsaWVkQWxwaGEgKSB7XG5cblx0XHRcdFx0XHRcdGdsLmJsZW5kRXF1YXRpb25TZXBhcmF0ZSggZ2wuRlVOQ19BREQsIGdsLkZVTkNfQUREICk7XG5cdFx0XHRcdFx0XHRnbC5ibGVuZEZ1bmNTZXBhcmF0ZSggZ2wuWkVSTywgZ2wuU1JDX0NPTE9SLCBnbC5aRVJPLCBnbC5TUkNfQUxQSEEgKTtcblxuXHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdGdsLmJsZW5kRXF1YXRpb24oIGdsLkZVTkNfQUREICk7XG5cdFx0XHRcdFx0XHRnbC5ibGVuZEZ1bmMoIGdsLlpFUk8sIGdsLlNSQ19DT0xPUiApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRpZiAoIHByZW11bHRpcGxpZWRBbHBoYSApIHtcblxuXHRcdFx0XHRcdFx0Z2wuYmxlbmRFcXVhdGlvblNlcGFyYXRlKCBnbC5GVU5DX0FERCwgZ2wuRlVOQ19BREQgKTtcblx0XHRcdFx0XHRcdGdsLmJsZW5kRnVuY1NlcGFyYXRlKCBnbC5PTkUsIGdsLk9ORV9NSU5VU19TUkNfQUxQSEEsIGdsLk9ORSwgZ2wuT05FX01JTlVTX1NSQ19BTFBIQSApO1xuXG5cdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0Z2wuYmxlbmRFcXVhdGlvblNlcGFyYXRlKCBnbC5GVU5DX0FERCwgZ2wuRlVOQ19BREQgKTtcblx0XHRcdFx0XHRcdGdsLmJsZW5kRnVuY1NlcGFyYXRlKCBnbC5TUkNfQUxQSEEsIGdsLk9ORV9NSU5VU19TUkNfQUxQSEEsIGdsLk9ORSwgZ2wuT05FX01JTlVTX1NSQ19BTFBIQSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRjdXJyZW50QmxlbmRpbmcgPSBibGVuZGluZztcblx0XHRcdFx0Y3VycmVudFByZW11bHRpcGxlZEFscGhhID0gcHJlbXVsdGlwbGllZEFscGhhO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggYmxlbmRpbmcgPT09IEN1c3RvbUJsZW5kaW5nICkge1xuXG5cdFx0XHRcdGJsZW5kRXF1YXRpb25BbHBoYSA9IGJsZW5kRXF1YXRpb25BbHBoYSB8fCBibGVuZEVxdWF0aW9uO1xuXHRcdFx0XHRibGVuZFNyY0FscGhhID0gYmxlbmRTcmNBbHBoYSB8fCBibGVuZFNyYztcblx0XHRcdFx0YmxlbmREc3RBbHBoYSA9IGJsZW5kRHN0QWxwaGEgfHwgYmxlbmREc3Q7XG5cblx0XHRcdFx0aWYgKCBibGVuZEVxdWF0aW9uICE9PSBjdXJyZW50QmxlbmRFcXVhdGlvbiB8fCBibGVuZEVxdWF0aW9uQWxwaGEgIT09IGN1cnJlbnRCbGVuZEVxdWF0aW9uQWxwaGEgKSB7XG5cblx0XHRcdFx0XHRnbC5ibGVuZEVxdWF0aW9uU2VwYXJhdGUoIHBhcmFtVGhyZWVUb0dMKCBibGVuZEVxdWF0aW9uICksIHBhcmFtVGhyZWVUb0dMKCBibGVuZEVxdWF0aW9uQWxwaGEgKSApO1xuXG5cdFx0XHRcdFx0Y3VycmVudEJsZW5kRXF1YXRpb24gPSBibGVuZEVxdWF0aW9uO1xuXHRcdFx0XHRcdGN1cnJlbnRCbGVuZEVxdWF0aW9uQWxwaGEgPSBibGVuZEVxdWF0aW9uQWxwaGE7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggYmxlbmRTcmMgIT09IGN1cnJlbnRCbGVuZFNyYyB8fCBibGVuZERzdCAhPT0gY3VycmVudEJsZW5kRHN0IHx8IGJsZW5kU3JjQWxwaGEgIT09IGN1cnJlbnRCbGVuZFNyY0FscGhhIHx8IGJsZW5kRHN0QWxwaGEgIT09IGN1cnJlbnRCbGVuZERzdEFscGhhICkge1xuXG5cdFx0XHRcdFx0Z2wuYmxlbmRGdW5jU2VwYXJhdGUoIHBhcmFtVGhyZWVUb0dMKCBibGVuZFNyYyApLCBwYXJhbVRocmVlVG9HTCggYmxlbmREc3QgKSwgcGFyYW1UaHJlZVRvR0woIGJsZW5kU3JjQWxwaGEgKSwgcGFyYW1UaHJlZVRvR0woIGJsZW5kRHN0QWxwaGEgKSApO1xuXG5cdFx0XHRcdFx0Y3VycmVudEJsZW5kU3JjID0gYmxlbmRTcmM7XG5cdFx0XHRcdFx0Y3VycmVudEJsZW5kRHN0ID0gYmxlbmREc3Q7XG5cdFx0XHRcdFx0Y3VycmVudEJsZW5kU3JjQWxwaGEgPSBibGVuZFNyY0FscGhhO1xuXHRcdFx0XHRcdGN1cnJlbnRCbGVuZERzdEFscGhhID0gYmxlbmREc3RBbHBoYTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0Y3VycmVudEJsZW5kRXF1YXRpb24gPSBudWxsO1xuXHRcdFx0XHRjdXJyZW50QmxlbmRTcmMgPSBudWxsO1xuXHRcdFx0XHRjdXJyZW50QmxlbmREc3QgPSBudWxsO1xuXHRcdFx0XHRjdXJyZW50QmxlbmRFcXVhdGlvbkFscGhhID0gbnVsbDtcblx0XHRcdFx0Y3VycmVudEJsZW5kU3JjQWxwaGEgPSBudWxsO1xuXHRcdFx0XHRjdXJyZW50QmxlbmREc3RBbHBoYSA9IG51bGw7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdC8vIFRPRE8gRGVwcmVjYXRlXG5cblx0XHRmdW5jdGlvbiBzZXRDb2xvcldyaXRlKCBjb2xvcldyaXRlICkge1xuXG5cdFx0XHRjb2xvckJ1ZmZlci5zZXRNYXNrKCBjb2xvcldyaXRlICk7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBzZXREZXB0aFRlc3QoIGRlcHRoVGVzdCApIHtcblxuXHRcdFx0ZGVwdGhCdWZmZXIuc2V0VGVzdCggZGVwdGhUZXN0ICk7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBzZXREZXB0aFdyaXRlKCBkZXB0aFdyaXRlICkge1xuXG5cdFx0XHRkZXB0aEJ1ZmZlci5zZXRNYXNrKCBkZXB0aFdyaXRlICk7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBzZXREZXB0aEZ1bmMoIGRlcHRoRnVuYyApIHtcblxuXHRcdFx0ZGVwdGhCdWZmZXIuc2V0RnVuYyggZGVwdGhGdW5jICk7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBzZXRTdGVuY2lsVGVzdCggc3RlbmNpbFRlc3QgKSB7XG5cblx0XHRcdHN0ZW5jaWxCdWZmZXIuc2V0VGVzdCggc3RlbmNpbFRlc3QgKTtcblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHNldFN0ZW5jaWxXcml0ZSggc3RlbmNpbFdyaXRlICkge1xuXG5cdFx0XHRzdGVuY2lsQnVmZmVyLnNldE1hc2soIHN0ZW5jaWxXcml0ZSApO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gc2V0U3RlbmNpbEZ1bmMoIHN0ZW5jaWxGdW5jLCBzdGVuY2lsUmVmLCBzdGVuY2lsTWFzayApIHtcblxuXHRcdFx0c3RlbmNpbEJ1ZmZlci5zZXRGdW5jKCBzdGVuY2lsRnVuYywgc3RlbmNpbFJlZiwgc3RlbmNpbE1hc2sgKTtcblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHNldFN0ZW5jaWxPcCggc3RlbmNpbEZhaWwsIHN0ZW5jaWxaRmFpbCwgc3RlbmNpbFpQYXNzICkge1xuXG5cdFx0XHRzdGVuY2lsQnVmZmVyLnNldE9wKCBzdGVuY2lsRmFpbCwgc3RlbmNpbFpGYWlsLCBzdGVuY2lsWlBhc3MgKTtcblxuXHRcdH1cblxuXHRcdC8vXG5cblx0XHRmdW5jdGlvbiBzZXRGbGlwU2lkZWQoIGZsaXBTaWRlZCApIHtcblxuXHRcdFx0aWYgKCBjdXJyZW50RmxpcFNpZGVkICE9PSBmbGlwU2lkZWQgKSB7XG5cblx0XHRcdFx0aWYgKCBmbGlwU2lkZWQgKSB7XG5cblx0XHRcdFx0XHRnbC5mcm9udEZhY2UoIGdsLkNXICk7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdGdsLmZyb250RmFjZSggZ2wuQ0NXICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGN1cnJlbnRGbGlwU2lkZWQgPSBmbGlwU2lkZWQ7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHNldEN1bGxGYWNlKCBjdWxsRmFjZSApIHtcblxuXHRcdFx0aWYgKCBjdWxsRmFjZSAhPT0gQ3VsbEZhY2VOb25lICkge1xuXG5cdFx0XHRcdGVuYWJsZSggZ2wuQ1VMTF9GQUNFICk7XG5cblx0XHRcdFx0aWYgKCBjdWxsRmFjZSAhPT0gY3VycmVudEN1bGxGYWNlICkge1xuXG5cdFx0XHRcdFx0aWYgKCBjdWxsRmFjZSA9PT0gQ3VsbEZhY2VCYWNrICkge1xuXG5cdFx0XHRcdFx0XHRnbC5jdWxsRmFjZSggZ2wuQkFDSyApO1xuXG5cdFx0XHRcdFx0fSBlbHNlIGlmICggY3VsbEZhY2UgPT09IEN1bGxGYWNlRnJvbnQgKSB7XG5cblx0XHRcdFx0XHRcdGdsLmN1bGxGYWNlKCBnbC5GUk9OVCApO1xuXG5cdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0Z2wuY3VsbEZhY2UoIGdsLkZST05UX0FORF9CQUNLICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdGRpc2FibGUoIGdsLkNVTExfRkFDRSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdGN1cnJlbnRDdWxsRmFjZSA9IGN1bGxGYWNlO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gc2V0TGluZVdpZHRoKCB3aWR0aCApIHtcblxuXHRcdFx0aWYgKCB3aWR0aCAhPT0gY3VycmVudExpbmVXaWR0aCApIHtcblxuXHRcdFx0XHRpZiAoIGxpbmVXaWR0aEF2YWlsYWJsZSApIGdsLmxpbmVXaWR0aCggd2lkdGggKTtcblxuXHRcdFx0XHRjdXJyZW50TGluZVdpZHRoID0gd2lkdGg7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHNldFBvbHlnb25PZmZzZXQoIHBvbHlnb25PZmZzZXQsIGZhY3RvciwgdW5pdHMgKSB7XG5cblx0XHRcdGlmICggcG9seWdvbk9mZnNldCApIHtcblxuXHRcdFx0XHRlbmFibGUoIGdsLlBPTFlHT05fT0ZGU0VUX0ZJTEwgKTtcblxuXHRcdFx0XHRpZiAoIGN1cnJlbnRQb2x5Z29uT2Zmc2V0RmFjdG9yICE9PSBmYWN0b3IgfHwgY3VycmVudFBvbHlnb25PZmZzZXRVbml0cyAhPT0gdW5pdHMgKSB7XG5cblx0XHRcdFx0XHRnbC5wb2x5Z29uT2Zmc2V0KCBmYWN0b3IsIHVuaXRzICk7XG5cblx0XHRcdFx0XHRjdXJyZW50UG9seWdvbk9mZnNldEZhY3RvciA9IGZhY3Rvcjtcblx0XHRcdFx0XHRjdXJyZW50UG9seWdvbk9mZnNldFVuaXRzID0gdW5pdHM7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdGRpc2FibGUoIGdsLlBPTFlHT05fT0ZGU0VUX0ZJTEwgKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gZ2V0U2Npc3NvclRlc3QoKSB7XG5cblx0XHRcdHJldHVybiBjdXJyZW50U2Npc3NvclRlc3Q7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBzZXRTY2lzc29yVGVzdCggc2Npc3NvclRlc3QgKSB7XG5cblx0XHRcdGN1cnJlbnRTY2lzc29yVGVzdCA9IHNjaXNzb3JUZXN0O1xuXG5cdFx0XHRpZiAoIHNjaXNzb3JUZXN0ICkge1xuXG5cdFx0XHRcdGVuYWJsZSggZ2wuU0NJU1NPUl9URVNUICk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0ZGlzYWJsZSggZ2wuU0NJU1NPUl9URVNUICk7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdC8vIHRleHR1cmVcblxuXHRcdGZ1bmN0aW9uIGFjdGl2ZVRleHR1cmUoIHdlYmdsU2xvdCApIHtcblxuXHRcdFx0aWYgKCB3ZWJnbFNsb3QgPT09IHVuZGVmaW5lZCApIHdlYmdsU2xvdCA9IGdsLlRFWFRVUkUwICsgbWF4VGV4dHVyZXMgLSAxO1xuXG5cdFx0XHRpZiAoIGN1cnJlbnRUZXh0dXJlU2xvdCAhPT0gd2ViZ2xTbG90ICkge1xuXG5cdFx0XHRcdGdsLmFjdGl2ZVRleHR1cmUoIHdlYmdsU2xvdCApO1xuXHRcdFx0XHRjdXJyZW50VGV4dHVyZVNsb3QgPSB3ZWJnbFNsb3Q7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIGJpbmRUZXh0dXJlKCB3ZWJnbFR5cGUsIHdlYmdsVGV4dHVyZSApIHtcblxuXHRcdFx0aWYgKCBjdXJyZW50VGV4dHVyZVNsb3QgPT09IG51bGwgKSB7XG5cblx0XHRcdFx0YWN0aXZlVGV4dHVyZSgpO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBib3VuZFRleHR1cmUgPSBjdXJyZW50Qm91bmRUZXh0dXJlc1sgY3VycmVudFRleHR1cmVTbG90IF07XG5cblx0XHRcdGlmICggYm91bmRUZXh0dXJlID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0Ym91bmRUZXh0dXJlID0geyB0eXBlOiB1bmRlZmluZWQsIHRleHR1cmU6IHVuZGVmaW5lZCB9O1xuXHRcdFx0XHRjdXJyZW50Qm91bmRUZXh0dXJlc1sgY3VycmVudFRleHR1cmVTbG90IF0gPSBib3VuZFRleHR1cmU7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBib3VuZFRleHR1cmUudHlwZSAhPT0gd2ViZ2xUeXBlIHx8IGJvdW5kVGV4dHVyZS50ZXh0dXJlICE9PSB3ZWJnbFRleHR1cmUgKSB7XG5cblx0XHRcdFx0Z2wuYmluZFRleHR1cmUoIHdlYmdsVHlwZSwgd2ViZ2xUZXh0dXJlIHx8IGVtcHR5VGV4dHVyZXNbIHdlYmdsVHlwZSBdICk7XG5cblx0XHRcdFx0Ym91bmRUZXh0dXJlLnR5cGUgPSB3ZWJnbFR5cGU7XG5cdFx0XHRcdGJvdW5kVGV4dHVyZS50ZXh0dXJlID0gd2ViZ2xUZXh0dXJlO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBjb21wcmVzc2VkVGV4SW1hZ2UyRCgpIHtcblxuXHRcdFx0dHJ5IHtcblxuXHRcdFx0XHRnbC5jb21wcmVzc2VkVGV4SW1hZ2UyRC5hcHBseSggZ2wsIGFyZ3VtZW50cyApO1xuXG5cdFx0XHR9IGNhdGNoICggZXJyb3IgKSB7XG5cblx0XHRcdFx0Y29uc29sZS5lcnJvciggZXJyb3IgKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gdGV4SW1hZ2UyRCgpIHtcblxuXHRcdFx0dHJ5IHtcblxuXHRcdFx0XHRnbC50ZXhJbWFnZTJELmFwcGx5KCBnbCwgYXJndW1lbnRzICk7XG5cblx0XHRcdH0gY2F0Y2ggKCBlcnJvciApIHtcblxuXHRcdFx0XHRjb25zb2xlLmVycm9yKCBlcnJvciApO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHQvL1xuXG5cdFx0ZnVuY3Rpb24gc2Npc3Nvciggc2Npc3NvciApIHtcblxuXHRcdFx0aWYgKCBjdXJyZW50U2Npc3Nvci5lcXVhbHMoIHNjaXNzb3IgKSA9PT0gZmFsc2UgKSB7XG5cblx0XHRcdFx0Z2wuc2Npc3Nvciggc2Npc3Nvci54LCBzY2lzc29yLnksIHNjaXNzb3Iueiwgc2Npc3Nvci53ICk7XG5cdFx0XHRcdGN1cnJlbnRTY2lzc29yLmNvcHkoIHNjaXNzb3IgKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gdmlld3BvcnQoIHZpZXdwb3J0ICkge1xuXG5cdFx0XHRpZiAoIGN1cnJlbnRWaWV3cG9ydC5lcXVhbHMoIHZpZXdwb3J0ICkgPT09IGZhbHNlICkge1xuXG5cdFx0XHRcdGdsLnZpZXdwb3J0KCB2aWV3cG9ydC54LCB2aWV3cG9ydC55LCB2aWV3cG9ydC56LCB2aWV3cG9ydC53ICk7XG5cdFx0XHRcdGN1cnJlbnRWaWV3cG9ydC5jb3B5KCB2aWV3cG9ydCApO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHQvL1xuXG5cdFx0ZnVuY3Rpb24gcmVzZXQoKSB7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IGVuYWJsZWRBdHRyaWJ1dGVzLmxlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHRpZiAoIGVuYWJsZWRBdHRyaWJ1dGVzWyBpIF0gPT09IDEgKSB7XG5cblx0XHRcdFx0XHRnbC5kaXNhYmxlVmVydGV4QXR0cmliQXJyYXkoIGkgKTtcblx0XHRcdFx0XHRlbmFibGVkQXR0cmlidXRlc1sgaSBdID0gMDtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0Y2FwYWJpbGl0aWVzID0ge307XG5cblx0XHRcdGNvbXByZXNzZWRUZXh0dXJlRm9ybWF0cyA9IG51bGw7XG5cblx0XHRcdGN1cnJlbnRUZXh0dXJlU2xvdCA9IG51bGw7XG5cdFx0XHRjdXJyZW50Qm91bmRUZXh0dXJlcyA9IHt9O1xuXG5cdFx0XHRjdXJyZW50QmxlbmRpbmcgPSBudWxsO1xuXG5cdFx0XHRjdXJyZW50RmxpcFNpZGVkID0gbnVsbDtcblx0XHRcdGN1cnJlbnRDdWxsRmFjZSA9IG51bGw7XG5cblx0XHRcdGNvbG9yQnVmZmVyLnJlc2V0KCk7XG5cdFx0XHRkZXB0aEJ1ZmZlci5yZXNldCgpO1xuXHRcdFx0c3RlbmNpbEJ1ZmZlci5yZXNldCgpO1xuXG5cdFx0fVxuXG5cdFx0cmV0dXJuIHtcblxuXHRcdFx0YnVmZmVyczoge1xuXHRcdFx0XHRjb2xvcjogY29sb3JCdWZmZXIsXG5cdFx0XHRcdGRlcHRoOiBkZXB0aEJ1ZmZlcixcblx0XHRcdFx0c3RlbmNpbDogc3RlbmNpbEJ1ZmZlclxuXHRcdFx0fSxcblxuXHRcdFx0aW5pdDogaW5pdCxcblx0XHRcdGluaXRBdHRyaWJ1dGVzOiBpbml0QXR0cmlidXRlcyxcblx0XHRcdGVuYWJsZUF0dHJpYnV0ZTogZW5hYmxlQXR0cmlidXRlLFxuXHRcdFx0ZW5hYmxlQXR0cmlidXRlQW5kRGl2aXNvcjogZW5hYmxlQXR0cmlidXRlQW5kRGl2aXNvcixcblx0XHRcdGRpc2FibGVVbnVzZWRBdHRyaWJ1dGVzOiBkaXNhYmxlVW51c2VkQXR0cmlidXRlcyxcblx0XHRcdGVuYWJsZTogZW5hYmxlLFxuXHRcdFx0ZGlzYWJsZTogZGlzYWJsZSxcblx0XHRcdGdldENvbXByZXNzZWRUZXh0dXJlRm9ybWF0czogZ2V0Q29tcHJlc3NlZFRleHR1cmVGb3JtYXRzLFxuXG5cdFx0XHRzZXRCbGVuZGluZzogc2V0QmxlbmRpbmcsXG5cblx0XHRcdHNldENvbG9yV3JpdGU6IHNldENvbG9yV3JpdGUsXG5cdFx0XHRzZXREZXB0aFRlc3Q6IHNldERlcHRoVGVzdCxcblx0XHRcdHNldERlcHRoV3JpdGU6IHNldERlcHRoV3JpdGUsXG5cdFx0XHRzZXREZXB0aEZ1bmM6IHNldERlcHRoRnVuYyxcblx0XHRcdHNldFN0ZW5jaWxUZXN0OiBzZXRTdGVuY2lsVGVzdCxcblx0XHRcdHNldFN0ZW5jaWxXcml0ZTogc2V0U3RlbmNpbFdyaXRlLFxuXHRcdFx0c2V0U3RlbmNpbEZ1bmM6IHNldFN0ZW5jaWxGdW5jLFxuXHRcdFx0c2V0U3RlbmNpbE9wOiBzZXRTdGVuY2lsT3AsXG5cblx0XHRcdHNldEZsaXBTaWRlZDogc2V0RmxpcFNpZGVkLFxuXHRcdFx0c2V0Q3VsbEZhY2U6IHNldEN1bGxGYWNlLFxuXG5cdFx0XHRzZXRMaW5lV2lkdGg6IHNldExpbmVXaWR0aCxcblx0XHRcdHNldFBvbHlnb25PZmZzZXQ6IHNldFBvbHlnb25PZmZzZXQsXG5cblx0XHRcdGdldFNjaXNzb3JUZXN0OiBnZXRTY2lzc29yVGVzdCxcblx0XHRcdHNldFNjaXNzb3JUZXN0OiBzZXRTY2lzc29yVGVzdCxcblxuXHRcdFx0YWN0aXZlVGV4dHVyZTogYWN0aXZlVGV4dHVyZSxcblx0XHRcdGJpbmRUZXh0dXJlOiBiaW5kVGV4dHVyZSxcblx0XHRcdGNvbXByZXNzZWRUZXhJbWFnZTJEOiBjb21wcmVzc2VkVGV4SW1hZ2UyRCxcblx0XHRcdHRleEltYWdlMkQ6IHRleEltYWdlMkQsXG5cblx0XHRcdHNjaXNzb3I6IHNjaXNzb3IsXG5cdFx0XHR2aWV3cG9ydDogdmlld3BvcnQsXG5cblx0XHRcdHJlc2V0OiByZXNldFxuXG5cdFx0fTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFdlYkdMQ2FwYWJpbGl0aWVzKCBnbCwgZXh0ZW5zaW9ucywgcGFyYW1ldGVycyApIHtcblxuXHRcdHZhciBtYXhBbmlzb3Ryb3B5O1xuXG5cdFx0ZnVuY3Rpb24gZ2V0TWF4QW5pc290cm9weSgpIHtcblxuXHRcdFx0aWYgKCBtYXhBbmlzb3Ryb3B5ICE9PSB1bmRlZmluZWQgKSByZXR1cm4gbWF4QW5pc290cm9weTtcblxuXHRcdFx0dmFyIGV4dGVuc2lvbiA9IGV4dGVuc2lvbnMuZ2V0KCAnRVhUX3RleHR1cmVfZmlsdGVyX2FuaXNvdHJvcGljJyApO1xuXG5cdFx0XHRpZiAoIGV4dGVuc2lvbiAhPT0gbnVsbCApIHtcblxuXHRcdFx0XHRtYXhBbmlzb3Ryb3B5ID0gZ2wuZ2V0UGFyYW1ldGVyKCBleHRlbnNpb24uTUFYX1RFWFRVUkVfTUFYX0FOSVNPVFJPUFlfRVhUICk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0bWF4QW5pc290cm9weSA9IDA7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIG1heEFuaXNvdHJvcHk7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBnZXRNYXhQcmVjaXNpb24oIHByZWNpc2lvbiApIHtcblxuXHRcdFx0aWYgKCBwcmVjaXNpb24gPT09ICdoaWdocCcgKSB7XG5cblx0XHRcdFx0aWYgKCBnbC5nZXRTaGFkZXJQcmVjaXNpb25Gb3JtYXQoIGdsLlZFUlRFWF9TSEFERVIsIGdsLkhJR0hfRkxPQVQgKS5wcmVjaXNpb24gPiAwICYmXG5cdFx0XHRcdCAgICAgZ2wuZ2V0U2hhZGVyUHJlY2lzaW9uRm9ybWF0KCBnbC5GUkFHTUVOVF9TSEFERVIsIGdsLkhJR0hfRkxPQVQgKS5wcmVjaXNpb24gPiAwICkge1xuXG5cdFx0XHRcdFx0cmV0dXJuICdoaWdocCc7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHByZWNpc2lvbiA9ICdtZWRpdW1wJztcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHByZWNpc2lvbiA9PT0gJ21lZGl1bXAnICkge1xuXG5cdFx0XHRcdGlmICggZ2wuZ2V0U2hhZGVyUHJlY2lzaW9uRm9ybWF0KCBnbC5WRVJURVhfU0hBREVSLCBnbC5NRURJVU1fRkxPQVQgKS5wcmVjaXNpb24gPiAwICYmXG5cdFx0XHRcdCAgICAgZ2wuZ2V0U2hhZGVyUHJlY2lzaW9uRm9ybWF0KCBnbC5GUkFHTUVOVF9TSEFERVIsIGdsLk1FRElVTV9GTE9BVCApLnByZWNpc2lvbiA+IDAgKSB7XG5cblx0XHRcdFx0XHRyZXR1cm4gJ21lZGl1bXAnO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gJ2xvd3AnO1xuXG5cdFx0fVxuXG5cdFx0dmFyIHByZWNpc2lvbiA9IHBhcmFtZXRlcnMucHJlY2lzaW9uICE9PSB1bmRlZmluZWQgPyBwYXJhbWV0ZXJzLnByZWNpc2lvbiA6ICdoaWdocCc7XG5cdFx0dmFyIG1heFByZWNpc2lvbiA9IGdldE1heFByZWNpc2lvbiggcHJlY2lzaW9uICk7XG5cblx0XHRpZiAoIG1heFByZWNpc2lvbiAhPT0gcHJlY2lzaW9uICkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5XZWJHTFJlbmRlcmVyOicsIHByZWNpc2lvbiwgJ25vdCBzdXBwb3J0ZWQsIHVzaW5nJywgbWF4UHJlY2lzaW9uLCAnaW5zdGVhZC4nICk7XG5cdFx0XHRwcmVjaXNpb24gPSBtYXhQcmVjaXNpb247XG5cblx0XHR9XG5cblx0XHR2YXIgbG9nYXJpdGhtaWNEZXB0aEJ1ZmZlciA9IHBhcmFtZXRlcnMubG9nYXJpdGhtaWNEZXB0aEJ1ZmZlciA9PT0gdHJ1ZSAmJiAhISBleHRlbnNpb25zLmdldCggJ0VYVF9mcmFnX2RlcHRoJyApO1xuXG5cdFx0dmFyIG1heFRleHR1cmVzID0gZ2wuZ2V0UGFyYW1ldGVyKCBnbC5NQVhfVEVYVFVSRV9JTUFHRV9VTklUUyApO1xuXHRcdHZhciBtYXhWZXJ0ZXhUZXh0dXJlcyA9IGdsLmdldFBhcmFtZXRlciggZ2wuTUFYX1ZFUlRFWF9URVhUVVJFX0lNQUdFX1VOSVRTICk7XG5cdFx0dmFyIG1heFRleHR1cmVTaXplID0gZ2wuZ2V0UGFyYW1ldGVyKCBnbC5NQVhfVEVYVFVSRV9TSVpFICk7XG5cdFx0dmFyIG1heEN1YmVtYXBTaXplID0gZ2wuZ2V0UGFyYW1ldGVyKCBnbC5NQVhfQ1VCRV9NQVBfVEVYVFVSRV9TSVpFICk7XG5cblx0XHR2YXIgbWF4QXR0cmlidXRlcyA9IGdsLmdldFBhcmFtZXRlciggZ2wuTUFYX1ZFUlRFWF9BVFRSSUJTICk7XG5cdFx0dmFyIG1heFZlcnRleFVuaWZvcm1zID0gZ2wuZ2V0UGFyYW1ldGVyKCBnbC5NQVhfVkVSVEVYX1VOSUZPUk1fVkVDVE9SUyApO1xuXHRcdHZhciBtYXhWYXJ5aW5ncyA9IGdsLmdldFBhcmFtZXRlciggZ2wuTUFYX1ZBUllJTkdfVkVDVE9SUyApO1xuXHRcdHZhciBtYXhGcmFnbWVudFVuaWZvcm1zID0gZ2wuZ2V0UGFyYW1ldGVyKCBnbC5NQVhfRlJBR01FTlRfVU5JRk9STV9WRUNUT1JTICk7XG5cblx0XHR2YXIgdmVydGV4VGV4dHVyZXMgPSBtYXhWZXJ0ZXhUZXh0dXJlcyA+IDA7XG5cdFx0dmFyIGZsb2F0RnJhZ21lbnRUZXh0dXJlcyA9ICEhIGV4dGVuc2lvbnMuZ2V0KCAnT0VTX3RleHR1cmVfZmxvYXQnICk7XG5cdFx0dmFyIGZsb2F0VmVydGV4VGV4dHVyZXMgPSB2ZXJ0ZXhUZXh0dXJlcyAmJiBmbG9hdEZyYWdtZW50VGV4dHVyZXM7XG5cblx0XHRyZXR1cm4ge1xuXG5cdFx0XHRnZXRNYXhBbmlzb3Ryb3B5OiBnZXRNYXhBbmlzb3Ryb3B5LFxuXHRcdFx0Z2V0TWF4UHJlY2lzaW9uOiBnZXRNYXhQcmVjaXNpb24sXG5cblx0XHRcdHByZWNpc2lvbjogcHJlY2lzaW9uLFxuXHRcdFx0bG9nYXJpdGhtaWNEZXB0aEJ1ZmZlcjogbG9nYXJpdGhtaWNEZXB0aEJ1ZmZlcixcblxuXHRcdFx0bWF4VGV4dHVyZXM6IG1heFRleHR1cmVzLFxuXHRcdFx0bWF4VmVydGV4VGV4dHVyZXM6IG1heFZlcnRleFRleHR1cmVzLFxuXHRcdFx0bWF4VGV4dHVyZVNpemU6IG1heFRleHR1cmVTaXplLFxuXHRcdFx0bWF4Q3ViZW1hcFNpemU6IG1heEN1YmVtYXBTaXplLFxuXG5cdFx0XHRtYXhBdHRyaWJ1dGVzOiBtYXhBdHRyaWJ1dGVzLFxuXHRcdFx0bWF4VmVydGV4VW5pZm9ybXM6IG1heFZlcnRleFVuaWZvcm1zLFxuXHRcdFx0bWF4VmFyeWluZ3M6IG1heFZhcnlpbmdzLFxuXHRcdFx0bWF4RnJhZ21lbnRVbmlmb3JtczogbWF4RnJhZ21lbnRVbmlmb3JtcyxcblxuXHRcdFx0dmVydGV4VGV4dHVyZXM6IHZlcnRleFRleHR1cmVzLFxuXHRcdFx0ZmxvYXRGcmFnbWVudFRleHR1cmVzOiBmbG9hdEZyYWdtZW50VGV4dHVyZXMsXG5cdFx0XHRmbG9hdFZlcnRleFRleHR1cmVzOiBmbG9hdFZlcnRleFRleHR1cmVzXG5cblx0XHR9O1xuXG5cdH1cblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gV2ViR0xFeHRlbnNpb25zKCBnbCApIHtcblxuXHRcdHZhciBleHRlbnNpb25zID0ge307XG5cblx0XHRyZXR1cm4ge1xuXG5cdFx0XHRnZXQ6IGZ1bmN0aW9uICggbmFtZSApIHtcblxuXHRcdFx0XHRpZiAoIGV4dGVuc2lvbnNbIG5hbWUgXSAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0cmV0dXJuIGV4dGVuc2lvbnNbIG5hbWUgXTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0dmFyIGV4dGVuc2lvbjtcblxuXHRcdFx0XHRzd2l0Y2ggKCBuYW1lICkge1xuXG5cdFx0XHRcdFx0Y2FzZSAnV0VCR0xfZGVwdGhfdGV4dHVyZSc6XG5cdFx0XHRcdFx0XHRleHRlbnNpb24gPSBnbC5nZXRFeHRlbnNpb24oICdXRUJHTF9kZXB0aF90ZXh0dXJlJyApIHx8IGdsLmdldEV4dGVuc2lvbiggJ01PWl9XRUJHTF9kZXB0aF90ZXh0dXJlJyApIHx8IGdsLmdldEV4dGVuc2lvbiggJ1dFQktJVF9XRUJHTF9kZXB0aF90ZXh0dXJlJyApO1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRjYXNlICdFWFRfdGV4dHVyZV9maWx0ZXJfYW5pc290cm9waWMnOlxuXHRcdFx0XHRcdFx0ZXh0ZW5zaW9uID0gZ2wuZ2V0RXh0ZW5zaW9uKCAnRVhUX3RleHR1cmVfZmlsdGVyX2FuaXNvdHJvcGljJyApIHx8IGdsLmdldEV4dGVuc2lvbiggJ01PWl9FWFRfdGV4dHVyZV9maWx0ZXJfYW5pc290cm9waWMnICkgfHwgZ2wuZ2V0RXh0ZW5zaW9uKCAnV0VCS0lUX0VYVF90ZXh0dXJlX2ZpbHRlcl9hbmlzb3Ryb3BpYycgKTtcblx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0Y2FzZSAnV0VCR0xfY29tcHJlc3NlZF90ZXh0dXJlX3MzdGMnOlxuXHRcdFx0XHRcdFx0ZXh0ZW5zaW9uID0gZ2wuZ2V0RXh0ZW5zaW9uKCAnV0VCR0xfY29tcHJlc3NlZF90ZXh0dXJlX3MzdGMnICkgfHwgZ2wuZ2V0RXh0ZW5zaW9uKCAnTU9aX1dFQkdMX2NvbXByZXNzZWRfdGV4dHVyZV9zM3RjJyApIHx8IGdsLmdldEV4dGVuc2lvbiggJ1dFQktJVF9XRUJHTF9jb21wcmVzc2VkX3RleHR1cmVfczN0YycgKTtcblx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0Y2FzZSAnV0VCR0xfY29tcHJlc3NlZF90ZXh0dXJlX3B2cnRjJzpcblx0XHRcdFx0XHRcdGV4dGVuc2lvbiA9IGdsLmdldEV4dGVuc2lvbiggJ1dFQkdMX2NvbXByZXNzZWRfdGV4dHVyZV9wdnJ0YycgKSB8fCBnbC5nZXRFeHRlbnNpb24oICdXRUJLSVRfV0VCR0xfY29tcHJlc3NlZF90ZXh0dXJlX3B2cnRjJyApO1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRjYXNlICdXRUJHTF9jb21wcmVzc2VkX3RleHR1cmVfZXRjMSc6XG5cdFx0XHRcdFx0XHRleHRlbnNpb24gPSBnbC5nZXRFeHRlbnNpb24oICdXRUJHTF9jb21wcmVzc2VkX3RleHR1cmVfZXRjMScgKTtcblx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0ZGVmYXVsdDpcblx0XHRcdFx0XHRcdGV4dGVuc2lvbiA9IGdsLmdldEV4dGVuc2lvbiggbmFtZSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoIGV4dGVuc2lvbiA9PT0gbnVsbCApIHtcblxuXHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyZXI6ICcgKyBuYW1lICsgJyBleHRlbnNpb24gbm90IHN1cHBvcnRlZC4nICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGV4dGVuc2lvbnNbIG5hbWUgXSA9IGV4dGVuc2lvbjtcblxuXHRcdFx0XHRyZXR1cm4gZXh0ZW5zaW9uO1xuXG5cdFx0XHR9XG5cblx0XHR9O1xuXG5cdH1cblxuXHQvKipcblx0ICogQGF1dGhvciB0c2Nod1xuXHQgKi9cblxuXHRmdW5jdGlvbiBXZWJHTENsaXBwaW5nKCkge1xuXG5cdFx0dmFyIHNjb3BlID0gdGhpcyxcblxuXHRcdFx0Z2xvYmFsU3RhdGUgPSBudWxsLFxuXHRcdFx0bnVtR2xvYmFsUGxhbmVzID0gMCxcblx0XHRcdGxvY2FsQ2xpcHBpbmdFbmFibGVkID0gZmFsc2UsXG5cdFx0XHRyZW5kZXJpbmdTaGFkb3dzID0gZmFsc2UsXG5cblx0XHRcdHBsYW5lID0gbmV3IFBsYW5lKCksXG5cdFx0XHR2aWV3Tm9ybWFsTWF0cml4ID0gbmV3IE1hdHJpeDMoKSxcblxuXHRcdFx0dW5pZm9ybSA9IHsgdmFsdWU6IG51bGwsIG5lZWRzVXBkYXRlOiBmYWxzZSB9O1xuXG5cdFx0dGhpcy51bmlmb3JtID0gdW5pZm9ybTtcblx0XHR0aGlzLm51bVBsYW5lcyA9IDA7XG5cdFx0dGhpcy5udW1JbnRlcnNlY3Rpb24gPSAwO1xuXG5cdFx0dGhpcy5pbml0ID0gZnVuY3Rpb24oIHBsYW5lcywgZW5hYmxlTG9jYWxDbGlwcGluZywgY2FtZXJhICkge1xuXG5cdFx0XHR2YXIgZW5hYmxlZCA9XG5cdFx0XHRcdHBsYW5lcy5sZW5ndGggIT09IDAgfHxcblx0XHRcdFx0ZW5hYmxlTG9jYWxDbGlwcGluZyB8fFxuXHRcdFx0XHQvLyBlbmFibGUgc3RhdGUgb2YgcHJldmlvdXMgZnJhbWUgLSB0aGUgY2xpcHBpbmcgY29kZSBoYXMgdG9cblx0XHRcdFx0Ly8gcnVuIGFub3RoZXIgZnJhbWUgaW4gb3JkZXIgdG8gcmVzZXQgdGhlIHN0YXRlOlxuXHRcdFx0XHRudW1HbG9iYWxQbGFuZXMgIT09IDAgfHxcblx0XHRcdFx0bG9jYWxDbGlwcGluZ0VuYWJsZWQ7XG5cblx0XHRcdGxvY2FsQ2xpcHBpbmdFbmFibGVkID0gZW5hYmxlTG9jYWxDbGlwcGluZztcblxuXHRcdFx0Z2xvYmFsU3RhdGUgPSBwcm9qZWN0UGxhbmVzKCBwbGFuZXMsIGNhbWVyYSwgMCApO1xuXHRcdFx0bnVtR2xvYmFsUGxhbmVzID0gcGxhbmVzLmxlbmd0aDtcblxuXHRcdFx0cmV0dXJuIGVuYWJsZWQ7XG5cblx0XHR9O1xuXG5cdFx0dGhpcy5iZWdpblNoYWRvd3MgPSBmdW5jdGlvbigpIHtcblxuXHRcdFx0cmVuZGVyaW5nU2hhZG93cyA9IHRydWU7XG5cdFx0XHRwcm9qZWN0UGxhbmVzKCBudWxsICk7XG5cblx0XHR9O1xuXG5cdFx0dGhpcy5lbmRTaGFkb3dzID0gZnVuY3Rpb24oKSB7XG5cblx0XHRcdHJlbmRlcmluZ1NoYWRvd3MgPSBmYWxzZTtcblx0XHRcdHJlc2V0R2xvYmFsU3RhdGUoKTtcblxuXHRcdH07XG5cblx0XHR0aGlzLnNldFN0YXRlID0gZnVuY3Rpb24oIHBsYW5lcywgY2xpcEludGVyc2VjdGlvbiwgY2xpcFNoYWRvd3MsIGNhbWVyYSwgY2FjaGUsIGZyb21DYWNoZSApIHtcblxuXHRcdFx0aWYgKCAhIGxvY2FsQ2xpcHBpbmdFbmFibGVkIHx8XG5cdFx0XHRcdFx0cGxhbmVzID09PSBudWxsIHx8IHBsYW5lcy5sZW5ndGggPT09IDAgfHxcblx0XHRcdFx0XHRyZW5kZXJpbmdTaGFkb3dzICYmICEgY2xpcFNoYWRvd3MgKSB7XG5cdFx0XHRcdC8vIHRoZXJlJ3Mgbm8gbG9jYWwgY2xpcHBpbmdcblxuXHRcdFx0XHRpZiAoIHJlbmRlcmluZ1NoYWRvd3MgKSB7XG5cdFx0XHRcdFx0Ly8gdGhlcmUncyBubyBnbG9iYWwgY2xpcHBpbmdcblxuXHRcdFx0XHRcdHByb2plY3RQbGFuZXMoIG51bGwgKTtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0cmVzZXRHbG9iYWxTdGF0ZSgpO1xuXHRcdFx0XHR9XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0dmFyIG5HbG9iYWwgPSByZW5kZXJpbmdTaGFkb3dzID8gMCA6IG51bUdsb2JhbFBsYW5lcyxcblx0XHRcdFx0XHRsR2xvYmFsID0gbkdsb2JhbCAqIDQsXG5cblx0XHRcdFx0XHRkc3RBcnJheSA9IGNhY2hlLmNsaXBwaW5nU3RhdGUgfHwgbnVsbDtcblxuXHRcdFx0XHR1bmlmb3JtLnZhbHVlID0gZHN0QXJyYXk7IC8vIGVuc3VyZSB1bmlxdWUgc3RhdGVcblxuXHRcdFx0XHRkc3RBcnJheSA9IHByb2plY3RQbGFuZXMoIHBsYW5lcywgY2FtZXJhLCBsR2xvYmFsLCBmcm9tQ2FjaGUgKTtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgIT09IGxHbG9iYWw7ICsrIGkgKSB7XG5cblx0XHRcdFx0XHRkc3RBcnJheVsgaSBdID0gZ2xvYmFsU3RhdGVbIGkgXTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Y2FjaGUuY2xpcHBpbmdTdGF0ZSA9IGRzdEFycmF5O1xuXHRcdFx0XHR0aGlzLm51bUludGVyc2VjdGlvbiA9IGNsaXBJbnRlcnNlY3Rpb24gPyB0aGlzLm51bVBsYW5lcyA6IDA7XG5cdFx0XHRcdHRoaXMubnVtUGxhbmVzICs9IG5HbG9iYWw7XG5cblx0XHRcdH1cblxuXG5cdFx0fTtcblxuXHRcdGZ1bmN0aW9uIHJlc2V0R2xvYmFsU3RhdGUoKSB7XG5cblx0XHRcdGlmICggdW5pZm9ybS52YWx1ZSAhPT0gZ2xvYmFsU3RhdGUgKSB7XG5cblx0XHRcdFx0dW5pZm9ybS52YWx1ZSA9IGdsb2JhbFN0YXRlO1xuXHRcdFx0XHR1bmlmb3JtLm5lZWRzVXBkYXRlID0gbnVtR2xvYmFsUGxhbmVzID4gMDtcblxuXHRcdFx0fVxuXG5cdFx0XHRzY29wZS5udW1QbGFuZXMgPSBudW1HbG9iYWxQbGFuZXM7XG5cdFx0XHRzY29wZS5udW1JbnRlcnNlY3Rpb24gPSAwO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gcHJvamVjdFBsYW5lcyggcGxhbmVzLCBjYW1lcmEsIGRzdE9mZnNldCwgc2tpcFRyYW5zZm9ybSApIHtcblxuXHRcdFx0dmFyIG5QbGFuZXMgPSBwbGFuZXMgIT09IG51bGwgPyBwbGFuZXMubGVuZ3RoIDogMCxcblx0XHRcdFx0ZHN0QXJyYXkgPSBudWxsO1xuXG5cdFx0XHRpZiAoIG5QbGFuZXMgIT09IDAgKSB7XG5cblx0XHRcdFx0ZHN0QXJyYXkgPSB1bmlmb3JtLnZhbHVlO1xuXG5cdFx0XHRcdGlmICggc2tpcFRyYW5zZm9ybSAhPT0gdHJ1ZSB8fCBkc3RBcnJheSA9PT0gbnVsbCApIHtcblxuXHRcdFx0XHRcdHZhciBmbGF0U2l6ZSA9IGRzdE9mZnNldCArIG5QbGFuZXMgKiA0LFxuXHRcdFx0XHRcdFx0dmlld01hdHJpeCA9IGNhbWVyYS5tYXRyaXhXb3JsZEludmVyc2U7XG5cblx0XHRcdFx0XHR2aWV3Tm9ybWFsTWF0cml4LmdldE5vcm1hbE1hdHJpeCggdmlld01hdHJpeCApO1xuXG5cdFx0XHRcdFx0aWYgKCBkc3RBcnJheSA9PT0gbnVsbCB8fCBkc3RBcnJheS5sZW5ndGggPCBmbGF0U2l6ZSApIHtcblxuXHRcdFx0XHRcdFx0ZHN0QXJyYXkgPSBuZXcgRmxvYXQzMkFycmF5KCBmbGF0U2l6ZSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0Zm9yICggdmFyIGkgPSAwLCBpNCA9IGRzdE9mZnNldDtcblx0XHRcdFx0XHRcdFx0XHRcdFx0aSAhPT0gblBsYW5lczsgKysgaSwgaTQgKz0gNCApIHtcblxuXHRcdFx0XHRcdFx0cGxhbmUuY29weSggcGxhbmVzWyBpIF0gKS5cblx0XHRcdFx0XHRcdFx0XHRhcHBseU1hdHJpeDQoIHZpZXdNYXRyaXgsIHZpZXdOb3JtYWxNYXRyaXggKTtcblxuXHRcdFx0XHRcdFx0cGxhbmUubm9ybWFsLnRvQXJyYXkoIGRzdEFycmF5LCBpNCApO1xuXHRcdFx0XHRcdFx0ZHN0QXJyYXlbIGk0ICsgMyBdID0gcGxhbmUuY29uc3RhbnQ7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHVuaWZvcm0udmFsdWUgPSBkc3RBcnJheTtcblx0XHRcdFx0dW5pZm9ybS5uZWVkc1VwZGF0ZSA9IHRydWU7XG5cblx0XHRcdH1cblxuXHRcdFx0c2NvcGUubnVtUGxhbmVzID0gblBsYW5lcztcblx0XHRcdFxuXHRcdFx0cmV0dXJuIGRzdEFycmF5O1xuXG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogQGF1dGhvciBzdXBlcmVnZ2JlcnQgLyBodHRwOi8vd3d3LnBhdWxicnVudC5jby51ay9cblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICogQGF1dGhvciBhbHRlcmVkcSAvIGh0dHA6Ly9hbHRlcmVkcXVhbGlhLmNvbS9cblx0ICogQGF1dGhvciBzemltZWsgLyBodHRwczovL2dpdGh1Yi5jb20vc3ppbWVrL1xuXHQgKiBAYXV0aG9yIHRzY2h3XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFdlYkdMUmVuZGVyZXIoIHBhcmFtZXRlcnMgKSB7XG5cblx0XHRjb25zb2xlLmxvZyggJ1RIUkVFLldlYkdMUmVuZGVyZXInLCBSRVZJU0lPTiApO1xuXG5cdFx0cGFyYW1ldGVycyA9IHBhcmFtZXRlcnMgfHwge307XG5cblx0XHR2YXIgX2NhbnZhcyA9IHBhcmFtZXRlcnMuY2FudmFzICE9PSB1bmRlZmluZWQgPyBwYXJhbWV0ZXJzLmNhbnZhcyA6IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnROUyggJ2h0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwnLCAnY2FudmFzJyApLFxuXHRcdFx0X2NvbnRleHQgPSBwYXJhbWV0ZXJzLmNvbnRleHQgIT09IHVuZGVmaW5lZCA/IHBhcmFtZXRlcnMuY29udGV4dCA6IG51bGwsXG5cblx0XHRcdF9hbHBoYSA9IHBhcmFtZXRlcnMuYWxwaGEgIT09IHVuZGVmaW5lZCA/IHBhcmFtZXRlcnMuYWxwaGEgOiBmYWxzZSxcblx0XHRcdF9kZXB0aCA9IHBhcmFtZXRlcnMuZGVwdGggIT09IHVuZGVmaW5lZCA/IHBhcmFtZXRlcnMuZGVwdGggOiB0cnVlLFxuXHRcdFx0X3N0ZW5jaWwgPSBwYXJhbWV0ZXJzLnN0ZW5jaWwgIT09IHVuZGVmaW5lZCA/IHBhcmFtZXRlcnMuc3RlbmNpbCA6IHRydWUsXG5cdFx0XHRfYW50aWFsaWFzID0gcGFyYW1ldGVycy5hbnRpYWxpYXMgIT09IHVuZGVmaW5lZCA/IHBhcmFtZXRlcnMuYW50aWFsaWFzIDogZmFsc2UsXG5cdFx0XHRfcHJlbXVsdGlwbGllZEFscGhhID0gcGFyYW1ldGVycy5wcmVtdWx0aXBsaWVkQWxwaGEgIT09IHVuZGVmaW5lZCA/IHBhcmFtZXRlcnMucHJlbXVsdGlwbGllZEFscGhhIDogdHJ1ZSxcblx0XHRcdF9wcmVzZXJ2ZURyYXdpbmdCdWZmZXIgPSBwYXJhbWV0ZXJzLnByZXNlcnZlRHJhd2luZ0J1ZmZlciAhPT0gdW5kZWZpbmVkID8gcGFyYW1ldGVycy5wcmVzZXJ2ZURyYXdpbmdCdWZmZXIgOiBmYWxzZTtcblxuXHRcdHZhciBsaWdodHMgPSBbXTtcblxuXHRcdHZhciBvcGFxdWVPYmplY3RzID0gW107XG5cdFx0dmFyIG9wYXF1ZU9iamVjdHNMYXN0SW5kZXggPSAtIDE7XG5cdFx0dmFyIHRyYW5zcGFyZW50T2JqZWN0cyA9IFtdO1xuXHRcdHZhciB0cmFuc3BhcmVudE9iamVjdHNMYXN0SW5kZXggPSAtIDE7XG5cblx0XHR2YXIgbW9ycGhJbmZsdWVuY2VzID0gbmV3IEZsb2F0MzJBcnJheSggOCApO1xuXG5cdFx0dmFyIHNwcml0ZXMgPSBbXTtcblx0XHR2YXIgbGVuc0ZsYXJlcyA9IFtdO1xuXG5cdFx0Ly8gcHVibGljIHByb3BlcnRpZXNcblxuXHRcdHRoaXMuZG9tRWxlbWVudCA9IF9jYW52YXM7XG5cdFx0dGhpcy5jb250ZXh0ID0gbnVsbDtcblxuXHRcdC8vIGNsZWFyaW5nXG5cblx0XHR0aGlzLmF1dG9DbGVhciA9IHRydWU7XG5cdFx0dGhpcy5hdXRvQ2xlYXJDb2xvciA9IHRydWU7XG5cdFx0dGhpcy5hdXRvQ2xlYXJEZXB0aCA9IHRydWU7XG5cdFx0dGhpcy5hdXRvQ2xlYXJTdGVuY2lsID0gdHJ1ZTtcblxuXHRcdC8vIHNjZW5lIGdyYXBoXG5cblx0XHR0aGlzLnNvcnRPYmplY3RzID0gdHJ1ZTtcblxuXHRcdC8vIHVzZXItZGVmaW5lZCBjbGlwcGluZ1xuXG5cdFx0dGhpcy5jbGlwcGluZ1BsYW5lcyA9IFtdO1xuXHRcdHRoaXMubG9jYWxDbGlwcGluZ0VuYWJsZWQgPSBmYWxzZTtcblxuXHRcdC8vIHBoeXNpY2FsbHkgYmFzZWQgc2hhZGluZ1xuXG5cdFx0dGhpcy5nYW1tYUZhY3RvciA9IDIuMDtcdC8vIGZvciBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eVxuXHRcdHRoaXMuZ2FtbWFJbnB1dCA9IGZhbHNlO1xuXHRcdHRoaXMuZ2FtbWFPdXRwdXQgPSBmYWxzZTtcblxuXHRcdC8vIHBoeXNpY2FsIGxpZ2h0c1xuXG5cdFx0dGhpcy5waHlzaWNhbGx5Q29ycmVjdExpZ2h0cyA9IGZhbHNlO1xuXG5cdFx0Ly8gdG9uZSBtYXBwaW5nXG5cblx0XHR0aGlzLnRvbmVNYXBwaW5nID0gTGluZWFyVG9uZU1hcHBpbmc7XG5cdFx0dGhpcy50b25lTWFwcGluZ0V4cG9zdXJlID0gMS4wO1xuXHRcdHRoaXMudG9uZU1hcHBpbmdXaGl0ZVBvaW50ID0gMS4wO1xuXG5cdFx0Ly8gbW9ycGhzXG5cblx0XHR0aGlzLm1heE1vcnBoVGFyZ2V0cyA9IDg7XG5cdFx0dGhpcy5tYXhNb3JwaE5vcm1hbHMgPSA0O1xuXG5cdFx0Ly8gaW50ZXJuYWwgcHJvcGVydGllc1xuXG5cdFx0dmFyIF90aGlzID0gdGhpcyxcblxuXHRcdFx0Ly8gaW50ZXJuYWwgc3RhdGUgY2FjaGVcblxuXHRcdFx0X2N1cnJlbnRQcm9ncmFtID0gbnVsbCxcblx0XHRcdF9jdXJyZW50UmVuZGVyVGFyZ2V0ID0gbnVsbCxcblx0XHRcdF9jdXJyZW50RnJhbWVidWZmZXIgPSBudWxsLFxuXHRcdFx0X2N1cnJlbnRNYXRlcmlhbElkID0gLSAxLFxuXHRcdFx0X2N1cnJlbnRHZW9tZXRyeVByb2dyYW0gPSAnJyxcblx0XHRcdF9jdXJyZW50Q2FtZXJhID0gbnVsbCxcblxuXHRcdFx0X2N1cnJlbnRTY2lzc29yID0gbmV3IFZlY3RvcjQoKSxcblx0XHRcdF9jdXJyZW50U2Npc3NvclRlc3QgPSBudWxsLFxuXG5cdFx0XHRfY3VycmVudFZpZXdwb3J0ID0gbmV3IFZlY3RvcjQoKSxcblxuXHRcdFx0Ly9cblxuXHRcdFx0X3VzZWRUZXh0dXJlVW5pdHMgPSAwLFxuXG5cdFx0XHQvL1xuXG5cdFx0XHRfY2xlYXJDb2xvciA9IG5ldyBDb2xvciggMHgwMDAwMDAgKSxcblx0XHRcdF9jbGVhckFscGhhID0gMCxcblxuXHRcdFx0X3dpZHRoID0gX2NhbnZhcy53aWR0aCxcblx0XHRcdF9oZWlnaHQgPSBfY2FudmFzLmhlaWdodCxcblxuXHRcdFx0X3BpeGVsUmF0aW8gPSAxLFxuXG5cdFx0XHRfc2Npc3NvciA9IG5ldyBWZWN0b3I0KCAwLCAwLCBfd2lkdGgsIF9oZWlnaHQgKSxcblx0XHRcdF9zY2lzc29yVGVzdCA9IGZhbHNlLFxuXG5cdFx0XHRfdmlld3BvcnQgPSBuZXcgVmVjdG9yNCggMCwgMCwgX3dpZHRoLCBfaGVpZ2h0ICksXG5cblx0XHRcdC8vIGZydXN0dW1cblxuXHRcdFx0X2ZydXN0dW0gPSBuZXcgRnJ1c3R1bSgpLFxuXG5cdFx0XHQvLyBjbGlwcGluZ1xuXG5cdFx0XHRfY2xpcHBpbmcgPSBuZXcgV2ViR0xDbGlwcGluZygpLFxuXHRcdFx0X2NsaXBwaW5nRW5hYmxlZCA9IGZhbHNlLFxuXHRcdFx0X2xvY2FsQ2xpcHBpbmdFbmFibGVkID0gZmFsc2UsXG5cblx0XHRcdF9zcGhlcmUgPSBuZXcgU3BoZXJlKCksXG5cblx0XHRcdC8vIGNhbWVyYSBtYXRyaWNlcyBjYWNoZVxuXG5cdFx0XHRfcHJvalNjcmVlbk1hdHJpeCA9IG5ldyBNYXRyaXg0KCksXG5cblx0XHRcdF92ZWN0b3IzID0gbmV3IFZlY3RvcjMoKSxcblx0XHRcdF9tYXRyaXg0ID0gbmV3IE1hdHJpeDQoKSxcblx0XHRcdF9tYXRyaXg0MiA9IG5ldyBNYXRyaXg0KCksXG5cblx0XHRcdC8vIGxpZ2h0IGFycmF5cyBjYWNoZVxuXG5cdFx0XHRfbGlnaHRzID0ge1xuXG5cdFx0XHRcdGhhc2g6ICcnLFxuXG5cdFx0XHRhbWJpZW50OiBbIDAsIDAsIDAgXSxcblx0XHRcdGRpcmVjdGlvbmFsOiBbXSxcblx0XHRcdGRpcmVjdGlvbmFsU2hhZG93TWFwOiBbXSxcblx0XHRcdGRpcmVjdGlvbmFsU2hhZG93TWF0cml4OiBbXSxcblx0XHRcdHNwb3Q6IFtdLFxuXHRcdFx0c3BvdFNoYWRvd01hcDogW10sXG5cdFx0XHRzcG90U2hhZG93TWF0cml4OiBbXSxcblx0XHRcdHJlY3RBcmVhOiBbXSxcblx0XHRcdHBvaW50OiBbXSxcblx0XHRcdHBvaW50U2hhZG93TWFwOiBbXSxcblx0XHRcdHBvaW50U2hhZG93TWF0cml4OiBbXSxcblx0XHRcdGhlbWk6IFtdLFxuXG5cdFx0XHRcdHNoYWRvd3M6IFtdXG5cblx0XHRcdH0sXG5cblx0XHRcdC8vIGluZm9cblxuXHRcdFx0X2luZm9SZW5kZXIgPSB7XG5cblx0XHRcdFx0Y2FsbHM6IDAsXG5cdFx0XHRcdHZlcnRpY2VzOiAwLFxuXHRcdFx0XHRmYWNlczogMCxcblx0XHRcdFx0cG9pbnRzOiAwXG5cblx0XHRcdH07XG5cblx0XHR0aGlzLmluZm8gPSB7XG5cblx0XHRcdHJlbmRlcjogX2luZm9SZW5kZXIsXG5cdFx0XHRtZW1vcnk6IHtcblxuXHRcdFx0XHRnZW9tZXRyaWVzOiAwLFxuXHRcdFx0XHR0ZXh0dXJlczogMFxuXG5cdFx0XHR9LFxuXHRcdFx0cHJvZ3JhbXM6IG51bGxcblxuXHRcdH07XG5cblxuXHRcdC8vIGluaXRpYWxpemVcblxuXHRcdHZhciBfZ2w7XG5cblx0XHR0cnkge1xuXG5cdFx0XHR2YXIgYXR0cmlidXRlcyA9IHtcblx0XHRcdFx0YWxwaGE6IF9hbHBoYSxcblx0XHRcdFx0ZGVwdGg6IF9kZXB0aCxcblx0XHRcdFx0c3RlbmNpbDogX3N0ZW5jaWwsXG5cdFx0XHRcdGFudGlhbGlhczogX2FudGlhbGlhcyxcblx0XHRcdFx0cHJlbXVsdGlwbGllZEFscGhhOiBfcHJlbXVsdGlwbGllZEFscGhhLFxuXHRcdFx0XHRwcmVzZXJ2ZURyYXdpbmdCdWZmZXI6IF9wcmVzZXJ2ZURyYXdpbmdCdWZmZXJcblx0XHRcdH07XG5cblx0XHRcdF9nbCA9IF9jb250ZXh0IHx8IF9jYW52YXMuZ2V0Q29udGV4dCggJ3dlYmdsJywgYXR0cmlidXRlcyApIHx8IF9jYW52YXMuZ2V0Q29udGV4dCggJ2V4cGVyaW1lbnRhbC13ZWJnbCcsIGF0dHJpYnV0ZXMgKTtcblxuXHRcdFx0aWYgKCBfZ2wgPT09IG51bGwgKSB7XG5cblx0XHRcdFx0aWYgKCBfY2FudmFzLmdldENvbnRleHQoICd3ZWJnbCcgKSAhPT0gbnVsbCApIHtcblxuXHRcdFx0XHRcdHRocm93ICdFcnJvciBjcmVhdGluZyBXZWJHTCBjb250ZXh0IHdpdGggeW91ciBzZWxlY3RlZCBhdHRyaWJ1dGVzLic7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdHRocm93ICdFcnJvciBjcmVhdGluZyBXZWJHTCBjb250ZXh0Lic7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdC8vIFNvbWUgZXhwZXJpbWVudGFsLXdlYmdsIGltcGxlbWVudGF0aW9ucyBkbyBub3QgaGF2ZSBnZXRTaGFkZXJQcmVjaXNpb25Gb3JtYXRcblxuXHRcdFx0aWYgKCBfZ2wuZ2V0U2hhZGVyUHJlY2lzaW9uRm9ybWF0ID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0X2dsLmdldFNoYWRlclByZWNpc2lvbkZvcm1hdCA9IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0XHRcdHJldHVybiB7ICdyYW5nZU1pbic6IDEsICdyYW5nZU1heCc6IDEsICdwcmVjaXNpb24nOiAxIH07XG5cblx0XHRcdFx0fTtcblxuXHRcdFx0fVxuXG5cdFx0XHRfY2FudmFzLmFkZEV2ZW50TGlzdGVuZXIoICd3ZWJnbGNvbnRleHRsb3N0Jywgb25Db250ZXh0TG9zdCwgZmFsc2UgKTtcblxuXHRcdH0gY2F0Y2ggKCBlcnJvciApIHtcblxuXHRcdFx0Y29uc29sZS5lcnJvciggJ1RIUkVFLldlYkdMUmVuZGVyZXI6ICcgKyBlcnJvciApO1xuXG5cdFx0fVxuXG5cdFx0dmFyIGV4dGVuc2lvbnMgPSBuZXcgV2ViR0xFeHRlbnNpb25zKCBfZ2wgKTtcblxuXHRcdGV4dGVuc2lvbnMuZ2V0KCAnV0VCR0xfZGVwdGhfdGV4dHVyZScgKTtcblx0XHRleHRlbnNpb25zLmdldCggJ09FU190ZXh0dXJlX2Zsb2F0JyApO1xuXHRcdGV4dGVuc2lvbnMuZ2V0KCAnT0VTX3RleHR1cmVfZmxvYXRfbGluZWFyJyApO1xuXHRcdGV4dGVuc2lvbnMuZ2V0KCAnT0VTX3RleHR1cmVfaGFsZl9mbG9hdCcgKTtcblx0XHRleHRlbnNpb25zLmdldCggJ09FU190ZXh0dXJlX2hhbGZfZmxvYXRfbGluZWFyJyApO1xuXHRcdGV4dGVuc2lvbnMuZ2V0KCAnT0VTX3N0YW5kYXJkX2Rlcml2YXRpdmVzJyApO1xuXHRcdGV4dGVuc2lvbnMuZ2V0KCAnQU5HTEVfaW5zdGFuY2VkX2FycmF5cycgKTtcblxuXHRcdGlmICggZXh0ZW5zaW9ucy5nZXQoICdPRVNfZWxlbWVudF9pbmRleF91aW50JyApICkge1xuXG5cdFx0XHRCdWZmZXJHZW9tZXRyeS5NYXhJbmRleCA9IDQyOTQ5NjcyOTY7XG5cblx0XHR9XG5cblx0XHR2YXIgY2FwYWJpbGl0aWVzID0gbmV3IFdlYkdMQ2FwYWJpbGl0aWVzKCBfZ2wsIGV4dGVuc2lvbnMsIHBhcmFtZXRlcnMgKTtcblxuXHRcdHZhciBzdGF0ZSA9IG5ldyBXZWJHTFN0YXRlKCBfZ2wsIGV4dGVuc2lvbnMsIHBhcmFtVGhyZWVUb0dMICk7XG5cdFx0dmFyIHByb3BlcnRpZXMgPSBuZXcgV2ViR0xQcm9wZXJ0aWVzKCk7XG5cdFx0dmFyIHRleHR1cmVzID0gbmV3IFdlYkdMVGV4dHVyZXMoIF9nbCwgZXh0ZW5zaW9ucywgc3RhdGUsIHByb3BlcnRpZXMsIGNhcGFiaWxpdGllcywgcGFyYW1UaHJlZVRvR0wsIHRoaXMuaW5mbyApO1xuXHRcdHZhciBvYmplY3RzID0gbmV3IFdlYkdMT2JqZWN0cyggX2dsLCBwcm9wZXJ0aWVzLCB0aGlzLmluZm8gKTtcblx0XHR2YXIgcHJvZ3JhbUNhY2hlID0gbmV3IFdlYkdMUHJvZ3JhbXMoIHRoaXMsIGNhcGFiaWxpdGllcyApO1xuXHRcdHZhciBsaWdodENhY2hlID0gbmV3IFdlYkdMTGlnaHRzKCk7XG5cblx0XHR0aGlzLmluZm8ucHJvZ3JhbXMgPSBwcm9ncmFtQ2FjaGUucHJvZ3JhbXM7XG5cblx0XHR2YXIgYnVmZmVyUmVuZGVyZXIgPSBuZXcgV2ViR0xCdWZmZXJSZW5kZXJlciggX2dsLCBleHRlbnNpb25zLCBfaW5mb1JlbmRlciApO1xuXHRcdHZhciBpbmRleGVkQnVmZmVyUmVuZGVyZXIgPSBuZXcgV2ViR0xJbmRleGVkQnVmZmVyUmVuZGVyZXIoIF9nbCwgZXh0ZW5zaW9ucywgX2luZm9SZW5kZXIgKTtcblxuXHRcdC8vXG5cblx0XHR2YXIgYmFja2dyb3VuZENhbWVyYSA9IG5ldyBPcnRob2dyYXBoaWNDYW1lcmEoIC0gMSwgMSwgMSwgLSAxLCAwLCAxICk7XG5cdFx0dmFyIGJhY2tncm91bmRDYW1lcmEyID0gbmV3IFBlcnNwZWN0aXZlQ2FtZXJhKCk7XG5cdFx0dmFyIGJhY2tncm91bmRQbGFuZU1lc2ggPSBuZXcgTWVzaChcblx0XHRcdG5ldyBQbGFuZUJ1ZmZlckdlb21ldHJ5KCAyLCAyICksXG5cdFx0XHRuZXcgTWVzaEJhc2ljTWF0ZXJpYWwoIHsgZGVwdGhUZXN0OiBmYWxzZSwgZGVwdGhXcml0ZTogZmFsc2UsIGZvZzogZmFsc2UgfSApXG5cdFx0KTtcblx0XHR2YXIgYmFja2dyb3VuZEJveFNoYWRlciA9IFNoYWRlckxpYlsgJ2N1YmUnIF07XG5cdFx0dmFyIGJhY2tncm91bmRCb3hNZXNoID0gbmV3IE1lc2goXG5cdFx0XHRuZXcgQm94QnVmZmVyR2VvbWV0cnkoIDUsIDUsIDUgKSxcblx0XHRcdG5ldyBTaGFkZXJNYXRlcmlhbCgge1xuXHRcdFx0XHR1bmlmb3JtczogYmFja2dyb3VuZEJveFNoYWRlci51bmlmb3Jtcyxcblx0XHRcdFx0dmVydGV4U2hhZGVyOiBiYWNrZ3JvdW5kQm94U2hhZGVyLnZlcnRleFNoYWRlcixcblx0XHRcdFx0ZnJhZ21lbnRTaGFkZXI6IGJhY2tncm91bmRCb3hTaGFkZXIuZnJhZ21lbnRTaGFkZXIsXG5cdFx0XHRcdHNpZGU6IEJhY2tTaWRlLFxuXHRcdFx0XHRkZXB0aFRlc3Q6IGZhbHNlLFxuXHRcdFx0XHRkZXB0aFdyaXRlOiBmYWxzZSxcblx0XHRcdFx0Zm9nOiBmYWxzZVxuXHRcdFx0fSApXG5cdFx0KTtcblxuXHRcdC8vXG5cblx0XHRmdW5jdGlvbiBnZXRUYXJnZXRQaXhlbFJhdGlvKCkge1xuXG5cdFx0XHRyZXR1cm4gX2N1cnJlbnRSZW5kZXJUYXJnZXQgPT09IG51bGwgPyBfcGl4ZWxSYXRpbyA6IDE7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBzZXREZWZhdWx0R0xTdGF0ZSgpIHtcblxuXHRcdFx0c3RhdGUuaW5pdCgpO1xuXG5cdFx0XHRzdGF0ZS5zY2lzc29yKCBfY3VycmVudFNjaXNzb3IuY29weSggX3NjaXNzb3IgKS5tdWx0aXBseVNjYWxhciggX3BpeGVsUmF0aW8gKSApO1xuXHRcdFx0c3RhdGUudmlld3BvcnQoIF9jdXJyZW50Vmlld3BvcnQuY29weSggX3ZpZXdwb3J0ICkubXVsdGlwbHlTY2FsYXIoIF9waXhlbFJhdGlvICkgKTtcblxuXHRcdFx0c3RhdGUuYnVmZmVycy5jb2xvci5zZXRDbGVhciggX2NsZWFyQ29sb3IuciwgX2NsZWFyQ29sb3IuZywgX2NsZWFyQ29sb3IuYiwgX2NsZWFyQWxwaGEsIF9wcmVtdWx0aXBsaWVkQWxwaGEgKTtcblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHJlc2V0R0xTdGF0ZSgpIHtcblxuXHRcdFx0X2N1cnJlbnRQcm9ncmFtID0gbnVsbDtcblx0XHRcdF9jdXJyZW50Q2FtZXJhID0gbnVsbDtcblxuXHRcdFx0X2N1cnJlbnRHZW9tZXRyeVByb2dyYW0gPSAnJztcblx0XHRcdF9jdXJyZW50TWF0ZXJpYWxJZCA9IC0gMTtcblxuXHRcdFx0c3RhdGUucmVzZXQoKTtcblxuXHRcdH1cblxuXHRcdHNldERlZmF1bHRHTFN0YXRlKCk7XG5cblx0XHR0aGlzLmNvbnRleHQgPSBfZ2w7XG5cdFx0dGhpcy5jYXBhYmlsaXRpZXMgPSBjYXBhYmlsaXRpZXM7XG5cdFx0dGhpcy5leHRlbnNpb25zID0gZXh0ZW5zaW9ucztcblx0XHR0aGlzLnByb3BlcnRpZXMgPSBwcm9wZXJ0aWVzO1xuXHRcdHRoaXMuc3RhdGUgPSBzdGF0ZTtcblxuXHRcdC8vIHNoYWRvdyBtYXBcblxuXHRcdHZhciBzaGFkb3dNYXAgPSBuZXcgV2ViR0xTaGFkb3dNYXAoIHRoaXMsIF9saWdodHMsIG9iamVjdHMsIGNhcGFiaWxpdGllcyApO1xuXG5cdFx0dGhpcy5zaGFkb3dNYXAgPSBzaGFkb3dNYXA7XG5cblxuXHRcdC8vIFBsdWdpbnNcblxuXHRcdHZhciBzcHJpdGVQbHVnaW4gPSBuZXcgU3ByaXRlUGx1Z2luKCB0aGlzLCBzcHJpdGVzICk7XG5cdFx0dmFyIGxlbnNGbGFyZVBsdWdpbiA9IG5ldyBMZW5zRmxhcmVQbHVnaW4oIHRoaXMsIGxlbnNGbGFyZXMgKTtcblxuXHRcdC8vIEFQSVxuXG5cdFx0dGhpcy5nZXRDb250ZXh0ID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gX2dsO1xuXG5cdFx0fTtcblxuXHRcdHRoaXMuZ2V0Q29udGV4dEF0dHJpYnV0ZXMgPSBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiBfZ2wuZ2V0Q29udGV4dEF0dHJpYnV0ZXMoKTtcblxuXHRcdH07XG5cblx0XHR0aGlzLmZvcmNlQ29udGV4dExvc3MgPSBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdGV4dGVuc2lvbnMuZ2V0KCAnV0VCR0xfbG9zZV9jb250ZXh0JyApLmxvc2VDb250ZXh0KCk7XG5cblx0XHR9O1xuXG5cdFx0dGhpcy5nZXRNYXhBbmlzb3Ryb3B5ID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gY2FwYWJpbGl0aWVzLmdldE1heEFuaXNvdHJvcHkoKTtcblxuXHRcdH07XG5cblx0XHR0aGlzLmdldFByZWNpc2lvbiA9IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIGNhcGFiaWxpdGllcy5wcmVjaXNpb247XG5cblx0XHR9O1xuXG5cdFx0dGhpcy5nZXRQaXhlbFJhdGlvID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gX3BpeGVsUmF0aW87XG5cblx0XHR9O1xuXG5cdFx0dGhpcy5zZXRQaXhlbFJhdGlvID0gZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0aWYgKCB2YWx1ZSA9PT0gdW5kZWZpbmVkICkgcmV0dXJuO1xuXG5cdFx0XHRfcGl4ZWxSYXRpbyA9IHZhbHVlO1xuXG5cdFx0XHR0aGlzLnNldFNpemUoIF92aWV3cG9ydC56LCBfdmlld3BvcnQudywgZmFsc2UgKTtcblxuXHRcdH07XG5cblx0XHR0aGlzLmdldFNpemUgPSBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiB7XG5cdFx0XHRcdHdpZHRoOiBfd2lkdGgsXG5cdFx0XHRcdGhlaWdodDogX2hlaWdodFxuXHRcdFx0fTtcblxuXHRcdH07XG5cblx0XHR0aGlzLnNldFNpemUgPSBmdW5jdGlvbiAoIHdpZHRoLCBoZWlnaHQsIHVwZGF0ZVN0eWxlICkge1xuXG5cdFx0XHRfd2lkdGggPSB3aWR0aDtcblx0XHRcdF9oZWlnaHQgPSBoZWlnaHQ7XG5cblx0XHRcdF9jYW52YXMud2lkdGggPSB3aWR0aCAqIF9waXhlbFJhdGlvO1xuXHRcdFx0X2NhbnZhcy5oZWlnaHQgPSBoZWlnaHQgKiBfcGl4ZWxSYXRpbztcblxuXHRcdFx0aWYgKCB1cGRhdGVTdHlsZSAhPT0gZmFsc2UgKSB7XG5cblx0XHRcdFx0X2NhbnZhcy5zdHlsZS53aWR0aCA9IHdpZHRoICsgJ3B4Jztcblx0XHRcdFx0X2NhbnZhcy5zdHlsZS5oZWlnaHQgPSBoZWlnaHQgKyAncHgnO1xuXG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuc2V0Vmlld3BvcnQoIDAsIDAsIHdpZHRoLCBoZWlnaHQgKTtcblxuXHRcdH07XG5cblx0XHR0aGlzLnNldFZpZXdwb3J0ID0gZnVuY3Rpb24gKCB4LCB5LCB3aWR0aCwgaGVpZ2h0ICkge1xuXG5cdFx0XHRzdGF0ZS52aWV3cG9ydCggX3ZpZXdwb3J0LnNldCggeCwgeSwgd2lkdGgsIGhlaWdodCApICk7XG5cblx0XHR9O1xuXG5cdFx0dGhpcy5zZXRTY2lzc29yID0gZnVuY3Rpb24gKCB4LCB5LCB3aWR0aCwgaGVpZ2h0ICkge1xuXG5cdFx0XHRzdGF0ZS5zY2lzc29yKCBfc2Npc3Nvci5zZXQoIHgsIHksIHdpZHRoLCBoZWlnaHQgKSApO1xuXG5cdFx0fTtcblxuXHRcdHRoaXMuc2V0U2Npc3NvclRlc3QgPSBmdW5jdGlvbiAoIGJvb2xlYW4gKSB7XG5cblx0XHRcdHN0YXRlLnNldFNjaXNzb3JUZXN0KCBfc2Npc3NvclRlc3QgPSBib29sZWFuICk7XG5cblx0XHR9O1xuXG5cdFx0Ly8gQ2xlYXJpbmdcblxuXHRcdHRoaXMuZ2V0Q2xlYXJDb2xvciA9IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIF9jbGVhckNvbG9yO1xuXG5cdFx0fTtcblxuXHRcdHRoaXMuc2V0Q2xlYXJDb2xvciA9IGZ1bmN0aW9uICggY29sb3IsIGFscGhhICkge1xuXG5cdFx0XHRfY2xlYXJDb2xvci5zZXQoIGNvbG9yICk7XG5cblx0XHRcdF9jbGVhckFscGhhID0gYWxwaGEgIT09IHVuZGVmaW5lZCA/IGFscGhhIDogMTtcblxuXHRcdFx0c3RhdGUuYnVmZmVycy5jb2xvci5zZXRDbGVhciggX2NsZWFyQ29sb3IuciwgX2NsZWFyQ29sb3IuZywgX2NsZWFyQ29sb3IuYiwgX2NsZWFyQWxwaGEsIF9wcmVtdWx0aXBsaWVkQWxwaGEgKTtcblxuXHRcdH07XG5cblx0XHR0aGlzLmdldENsZWFyQWxwaGEgPSBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiBfY2xlYXJBbHBoYTtcblxuXHRcdH07XG5cblx0XHR0aGlzLnNldENsZWFyQWxwaGEgPSBmdW5jdGlvbiAoIGFscGhhICkge1xuXG5cdFx0XHRfY2xlYXJBbHBoYSA9IGFscGhhO1xuXG5cdFx0XHRzdGF0ZS5idWZmZXJzLmNvbG9yLnNldENsZWFyKCBfY2xlYXJDb2xvci5yLCBfY2xlYXJDb2xvci5nLCBfY2xlYXJDb2xvci5iLCBfY2xlYXJBbHBoYSwgX3ByZW11bHRpcGxpZWRBbHBoYSApO1xuXG5cdFx0fTtcblxuXHRcdHRoaXMuY2xlYXIgPSBmdW5jdGlvbiAoIGNvbG9yLCBkZXB0aCwgc3RlbmNpbCApIHtcblxuXHRcdFx0dmFyIGJpdHMgPSAwO1xuXG5cdFx0XHRpZiAoIGNvbG9yID09PSB1bmRlZmluZWQgfHwgY29sb3IgKSBiaXRzIHw9IF9nbC5DT0xPUl9CVUZGRVJfQklUO1xuXHRcdFx0aWYgKCBkZXB0aCA9PT0gdW5kZWZpbmVkIHx8IGRlcHRoICkgYml0cyB8PSBfZ2wuREVQVEhfQlVGRkVSX0JJVDtcblx0XHRcdGlmICggc3RlbmNpbCA9PT0gdW5kZWZpbmVkIHx8IHN0ZW5jaWwgKSBiaXRzIHw9IF9nbC5TVEVOQ0lMX0JVRkZFUl9CSVQ7XG5cblx0XHRcdF9nbC5jbGVhciggYml0cyApO1xuXG5cdFx0fTtcblxuXHRcdHRoaXMuY2xlYXJDb2xvciA9IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dGhpcy5jbGVhciggdHJ1ZSwgZmFsc2UsIGZhbHNlICk7XG5cblx0XHR9O1xuXG5cdFx0dGhpcy5jbGVhckRlcHRoID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR0aGlzLmNsZWFyKCBmYWxzZSwgdHJ1ZSwgZmFsc2UgKTtcblxuXHRcdH07XG5cblx0XHR0aGlzLmNsZWFyU3RlbmNpbCA9IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dGhpcy5jbGVhciggZmFsc2UsIGZhbHNlLCB0cnVlICk7XG5cblx0XHR9O1xuXG5cdFx0dGhpcy5jbGVhclRhcmdldCA9IGZ1bmN0aW9uICggcmVuZGVyVGFyZ2V0LCBjb2xvciwgZGVwdGgsIHN0ZW5jaWwgKSB7XG5cblx0XHRcdHRoaXMuc2V0UmVuZGVyVGFyZ2V0KCByZW5kZXJUYXJnZXQgKTtcblx0XHRcdHRoaXMuY2xlYXIoIGNvbG9yLCBkZXB0aCwgc3RlbmNpbCApO1xuXG5cdFx0fTtcblxuXHRcdC8vIFJlc2V0XG5cblx0XHR0aGlzLnJlc2V0R0xTdGF0ZSA9IHJlc2V0R0xTdGF0ZTtcblxuXHRcdHRoaXMuZGlzcG9zZSA9IGZ1bmN0aW9uKCkge1xuXG5cdFx0XHR0cmFuc3BhcmVudE9iamVjdHMgPSBbXTtcblx0XHRcdHRyYW5zcGFyZW50T2JqZWN0c0xhc3RJbmRleCA9IC0xO1xuXHRcdFx0b3BhcXVlT2JqZWN0cyA9IFtdO1xuXHRcdFx0b3BhcXVlT2JqZWN0c0xhc3RJbmRleCA9IC0xO1xuXG5cdFx0XHRfY2FudmFzLnJlbW92ZUV2ZW50TGlzdGVuZXIoICd3ZWJnbGNvbnRleHRsb3N0Jywgb25Db250ZXh0TG9zdCwgZmFsc2UgKTtcblxuXHRcdH07XG5cblx0XHQvLyBFdmVudHNcblxuXHRcdGZ1bmN0aW9uIG9uQ29udGV4dExvc3QoIGV2ZW50ICkge1xuXG5cdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXG5cdFx0XHRyZXNldEdMU3RhdGUoKTtcblx0XHRcdHNldERlZmF1bHRHTFN0YXRlKCk7XG5cblx0XHRcdHByb3BlcnRpZXMuY2xlYXIoKTtcblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIG9uTWF0ZXJpYWxEaXNwb3NlKCBldmVudCApIHtcblxuXHRcdFx0dmFyIG1hdGVyaWFsID0gZXZlbnQudGFyZ2V0O1xuXG5cdFx0XHRtYXRlcmlhbC5yZW1vdmVFdmVudExpc3RlbmVyKCAnZGlzcG9zZScsIG9uTWF0ZXJpYWxEaXNwb3NlICk7XG5cblx0XHRcdGRlYWxsb2NhdGVNYXRlcmlhbCggbWF0ZXJpYWwgKTtcblxuXHRcdH1cblxuXHRcdC8vIEJ1ZmZlciBkZWFsbG9jYXRpb25cblxuXHRcdGZ1bmN0aW9uIGRlYWxsb2NhdGVNYXRlcmlhbCggbWF0ZXJpYWwgKSB7XG5cblx0XHRcdHJlbGVhc2VNYXRlcmlhbFByb2dyYW1SZWZlcmVuY2UoIG1hdGVyaWFsICk7XG5cblx0XHRcdHByb3BlcnRpZXMuZGVsZXRlKCBtYXRlcmlhbCApO1xuXG5cdFx0fVxuXG5cblx0XHRmdW5jdGlvbiByZWxlYXNlTWF0ZXJpYWxQcm9ncmFtUmVmZXJlbmNlKCBtYXRlcmlhbCApIHtcblxuXHRcdFx0dmFyIHByb2dyYW1JbmZvID0gcHJvcGVydGllcy5nZXQoIG1hdGVyaWFsICkucHJvZ3JhbTtcblxuXHRcdFx0bWF0ZXJpYWwucHJvZ3JhbSA9IHVuZGVmaW5lZDtcblxuXHRcdFx0aWYgKCBwcm9ncmFtSW5mbyAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdHByb2dyYW1DYWNoZS5yZWxlYXNlUHJvZ3JhbSggcHJvZ3JhbUluZm8gKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0Ly8gQnVmZmVyIHJlbmRlcmluZ1xuXG5cdFx0dGhpcy5yZW5kZXJCdWZmZXJJbW1lZGlhdGUgPSBmdW5jdGlvbiAoIG9iamVjdCwgcHJvZ3JhbSwgbWF0ZXJpYWwgKSB7XG5cblx0XHRcdHN0YXRlLmluaXRBdHRyaWJ1dGVzKCk7XG5cblx0XHRcdHZhciBidWZmZXJzID0gcHJvcGVydGllcy5nZXQoIG9iamVjdCApO1xuXG5cdFx0XHRpZiAoIG9iamVjdC5oYXNQb3NpdGlvbnMgJiYgISBidWZmZXJzLnBvc2l0aW9uICkgYnVmZmVycy5wb3NpdGlvbiA9IF9nbC5jcmVhdGVCdWZmZXIoKTtcblx0XHRcdGlmICggb2JqZWN0Lmhhc05vcm1hbHMgJiYgISBidWZmZXJzLm5vcm1hbCApIGJ1ZmZlcnMubm9ybWFsID0gX2dsLmNyZWF0ZUJ1ZmZlcigpO1xuXHRcdFx0aWYgKCBvYmplY3QuaGFzVXZzICYmICEgYnVmZmVycy51diApIGJ1ZmZlcnMudXYgPSBfZ2wuY3JlYXRlQnVmZmVyKCk7XG5cdFx0XHRpZiAoIG9iamVjdC5oYXNDb2xvcnMgJiYgISBidWZmZXJzLmNvbG9yICkgYnVmZmVycy5jb2xvciA9IF9nbC5jcmVhdGVCdWZmZXIoKTtcblxuXHRcdFx0dmFyIGF0dHJpYnV0ZXMgPSBwcm9ncmFtLmdldEF0dHJpYnV0ZXMoKTtcblxuXHRcdFx0aWYgKCBvYmplY3QuaGFzUG9zaXRpb25zICkge1xuXG5cdFx0XHRcdF9nbC5iaW5kQnVmZmVyKCBfZ2wuQVJSQVlfQlVGRkVSLCBidWZmZXJzLnBvc2l0aW9uICk7XG5cdFx0XHRcdF9nbC5idWZmZXJEYXRhKCBfZ2wuQVJSQVlfQlVGRkVSLCBvYmplY3QucG9zaXRpb25BcnJheSwgX2dsLkRZTkFNSUNfRFJBVyApO1xuXG5cdFx0XHRcdHN0YXRlLmVuYWJsZUF0dHJpYnV0ZSggYXR0cmlidXRlcy5wb3NpdGlvbiApO1xuXHRcdFx0XHRfZ2wudmVydGV4QXR0cmliUG9pbnRlciggYXR0cmlidXRlcy5wb3NpdGlvbiwgMywgX2dsLkZMT0FULCBmYWxzZSwgMCwgMCApO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggb2JqZWN0Lmhhc05vcm1hbHMgKSB7XG5cblx0XHRcdFx0X2dsLmJpbmRCdWZmZXIoIF9nbC5BUlJBWV9CVUZGRVIsIGJ1ZmZlcnMubm9ybWFsICk7XG5cblx0XHRcdFx0aWYgKCAhIG1hdGVyaWFsLmlzTWVzaFBob25nTWF0ZXJpYWwgJiZcblx0XHRcdFx0XHQhIG1hdGVyaWFsLmlzTWVzaFN0YW5kYXJkTWF0ZXJpYWwgJiZcblx0XHRcdFx0XHQhIG1hdGVyaWFsLmlzTWVzaE5vcm1hbE1hdGVyaWFsICYmXG5cdFx0XHRcdFx0bWF0ZXJpYWwuc2hhZGluZyA9PT0gRmxhdFNoYWRpbmcgKSB7XG5cblx0XHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBvYmplY3QuY291bnQgKiAzOyBpIDwgbDsgaSArPSA5ICkge1xuXG5cdFx0XHRcdFx0XHR2YXIgYXJyYXkgPSBvYmplY3Qubm9ybWFsQXJyYXk7XG5cblx0XHRcdFx0XHRcdHZhciBueCA9ICggYXJyYXlbIGkgKyAwIF0gKyBhcnJheVsgaSArIDMgXSArIGFycmF5WyBpICsgNiBdICkgLyAzO1xuXHRcdFx0XHRcdFx0dmFyIG55ID0gKCBhcnJheVsgaSArIDEgXSArIGFycmF5WyBpICsgNCBdICsgYXJyYXlbIGkgKyA3IF0gKSAvIDM7XG5cdFx0XHRcdFx0XHR2YXIgbnogPSAoIGFycmF5WyBpICsgMiBdICsgYXJyYXlbIGkgKyA1IF0gKyBhcnJheVsgaSArIDggXSApIC8gMztcblxuXHRcdFx0XHRcdFx0YXJyYXlbIGkgKyAwIF0gPSBueDtcblx0XHRcdFx0XHRcdGFycmF5WyBpICsgMSBdID0gbnk7XG5cdFx0XHRcdFx0XHRhcnJheVsgaSArIDIgXSA9IG56O1xuXG5cdFx0XHRcdFx0XHRhcnJheVsgaSArIDMgXSA9IG54O1xuXHRcdFx0XHRcdFx0YXJyYXlbIGkgKyA0IF0gPSBueTtcblx0XHRcdFx0XHRcdGFycmF5WyBpICsgNSBdID0gbno7XG5cblx0XHRcdFx0XHRcdGFycmF5WyBpICsgNiBdID0gbng7XG5cdFx0XHRcdFx0XHRhcnJheVsgaSArIDcgXSA9IG55O1xuXHRcdFx0XHRcdFx0YXJyYXlbIGkgKyA4IF0gPSBuejtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0X2dsLmJ1ZmZlckRhdGEoIF9nbC5BUlJBWV9CVUZGRVIsIG9iamVjdC5ub3JtYWxBcnJheSwgX2dsLkRZTkFNSUNfRFJBVyApO1xuXG5cdFx0XHRcdHN0YXRlLmVuYWJsZUF0dHJpYnV0ZSggYXR0cmlidXRlcy5ub3JtYWwgKTtcblxuXHRcdFx0XHRfZ2wudmVydGV4QXR0cmliUG9pbnRlciggYXR0cmlidXRlcy5ub3JtYWwsIDMsIF9nbC5GTE9BVCwgZmFsc2UsIDAsIDAgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIG9iamVjdC5oYXNVdnMgJiYgbWF0ZXJpYWwubWFwICkge1xuXG5cdFx0XHRcdF9nbC5iaW5kQnVmZmVyKCBfZ2wuQVJSQVlfQlVGRkVSLCBidWZmZXJzLnV2ICk7XG5cdFx0XHRcdF9nbC5idWZmZXJEYXRhKCBfZ2wuQVJSQVlfQlVGRkVSLCBvYmplY3QudXZBcnJheSwgX2dsLkRZTkFNSUNfRFJBVyApO1xuXG5cdFx0XHRcdHN0YXRlLmVuYWJsZUF0dHJpYnV0ZSggYXR0cmlidXRlcy51diApO1xuXG5cdFx0XHRcdF9nbC52ZXJ0ZXhBdHRyaWJQb2ludGVyKCBhdHRyaWJ1dGVzLnV2LCAyLCBfZ2wuRkxPQVQsIGZhbHNlLCAwLCAwICk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBvYmplY3QuaGFzQ29sb3JzICYmIG1hdGVyaWFsLnZlcnRleENvbG9ycyAhPT0gTm9Db2xvcnMgKSB7XG5cblx0XHRcdFx0X2dsLmJpbmRCdWZmZXIoIF9nbC5BUlJBWV9CVUZGRVIsIGJ1ZmZlcnMuY29sb3IgKTtcblx0XHRcdFx0X2dsLmJ1ZmZlckRhdGEoIF9nbC5BUlJBWV9CVUZGRVIsIG9iamVjdC5jb2xvckFycmF5LCBfZ2wuRFlOQU1JQ19EUkFXICk7XG5cblx0XHRcdFx0c3RhdGUuZW5hYmxlQXR0cmlidXRlKCBhdHRyaWJ1dGVzLmNvbG9yICk7XG5cblx0XHRcdFx0X2dsLnZlcnRleEF0dHJpYlBvaW50ZXIoIGF0dHJpYnV0ZXMuY29sb3IsIDMsIF9nbC5GTE9BVCwgZmFsc2UsIDAsIDAgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRzdGF0ZS5kaXNhYmxlVW51c2VkQXR0cmlidXRlcygpO1xuXG5cdFx0XHRfZ2wuZHJhd0FycmF5cyggX2dsLlRSSUFOR0xFUywgMCwgb2JqZWN0LmNvdW50ICk7XG5cblx0XHRcdG9iamVjdC5jb3VudCA9IDA7XG5cblx0XHR9O1xuXG5cdFx0dGhpcy5yZW5kZXJCdWZmZXJEaXJlY3QgPSBmdW5jdGlvbiAoIGNhbWVyYSwgZm9nLCBnZW9tZXRyeSwgbWF0ZXJpYWwsIG9iamVjdCwgZ3JvdXAgKSB7XG5cblx0XHRcdHNldE1hdGVyaWFsKCBtYXRlcmlhbCApO1xuXG5cdFx0XHR2YXIgcHJvZ3JhbSA9IHNldFByb2dyYW0oIGNhbWVyYSwgZm9nLCBtYXRlcmlhbCwgb2JqZWN0ICk7XG5cblx0XHRcdHZhciB1cGRhdGVCdWZmZXJzID0gZmFsc2U7XG5cdFx0XHR2YXIgZ2VvbWV0cnlQcm9ncmFtID0gZ2VvbWV0cnkuaWQgKyAnXycgKyBwcm9ncmFtLmlkICsgJ18nICsgbWF0ZXJpYWwud2lyZWZyYW1lO1xuXG5cdFx0XHRpZiAoIGdlb21ldHJ5UHJvZ3JhbSAhPT0gX2N1cnJlbnRHZW9tZXRyeVByb2dyYW0gKSB7XG5cblx0XHRcdFx0X2N1cnJlbnRHZW9tZXRyeVByb2dyYW0gPSBnZW9tZXRyeVByb2dyYW07XG5cdFx0XHRcdHVwZGF0ZUJ1ZmZlcnMgPSB0cnVlO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIG1vcnBoIHRhcmdldHNcblxuXHRcdFx0dmFyIG1vcnBoVGFyZ2V0SW5mbHVlbmNlcyA9IG9iamVjdC5tb3JwaFRhcmdldEluZmx1ZW5jZXM7XG5cblx0XHRcdGlmICggbW9ycGhUYXJnZXRJbmZsdWVuY2VzICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0dmFyIGFjdGl2ZUluZmx1ZW5jZXMgPSBbXTtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBtb3JwaFRhcmdldEluZmx1ZW5jZXMubGVuZ3RoOyBpIDwgbDsgaSArKyApIHtcblxuXHRcdFx0XHRcdHZhciBpbmZsdWVuY2UgPSBtb3JwaFRhcmdldEluZmx1ZW5jZXNbIGkgXTtcblx0XHRcdFx0XHRhY3RpdmVJbmZsdWVuY2VzLnB1c2goIFsgaW5mbHVlbmNlLCBpIF0gKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0YWN0aXZlSW5mbHVlbmNlcy5zb3J0KCBhYnNOdW1lcmljYWxTb3J0ICk7XG5cblx0XHRcdFx0aWYgKCBhY3RpdmVJbmZsdWVuY2VzLmxlbmd0aCA+IDggKSB7XG5cblx0XHRcdFx0XHRhY3RpdmVJbmZsdWVuY2VzLmxlbmd0aCA9IDg7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBtb3JwaEF0dHJpYnV0ZXMgPSBnZW9tZXRyeS5tb3JwaEF0dHJpYnV0ZXM7XG5cblx0XHRcdFx0Zm9yICggdmFyIGkgPSAwLCBsID0gYWN0aXZlSW5mbHVlbmNlcy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdFx0dmFyIGluZmx1ZW5jZSA9IGFjdGl2ZUluZmx1ZW5jZXNbIGkgXTtcblx0XHRcdFx0XHRtb3JwaEluZmx1ZW5jZXNbIGkgXSA9IGluZmx1ZW5jZVsgMCBdO1xuXG5cdFx0XHRcdFx0aWYgKCBpbmZsdWVuY2VbIDAgXSAhPT0gMCApIHtcblxuXHRcdFx0XHRcdFx0dmFyIGluZGV4ID0gaW5mbHVlbmNlWyAxIF07XG5cblx0XHRcdFx0XHRcdGlmICggbWF0ZXJpYWwubW9ycGhUYXJnZXRzID09PSB0cnVlICYmIG1vcnBoQXR0cmlidXRlcy5wb3NpdGlvbiApIGdlb21ldHJ5LmFkZEF0dHJpYnV0ZSggJ21vcnBoVGFyZ2V0JyArIGksIG1vcnBoQXR0cmlidXRlcy5wb3NpdGlvblsgaW5kZXggXSApO1xuXHRcdFx0XHRcdFx0aWYgKCBtYXRlcmlhbC5tb3JwaE5vcm1hbHMgPT09IHRydWUgJiYgbW9ycGhBdHRyaWJ1dGVzLm5vcm1hbCApIGdlb21ldHJ5LmFkZEF0dHJpYnV0ZSggJ21vcnBoTm9ybWFsJyArIGksIG1vcnBoQXR0cmlidXRlcy5ub3JtYWxbIGluZGV4IF0gKTtcblxuXHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdGlmICggbWF0ZXJpYWwubW9ycGhUYXJnZXRzID09PSB0cnVlICkgZ2VvbWV0cnkucmVtb3ZlQXR0cmlidXRlKCAnbW9ycGhUYXJnZXQnICsgaSApO1xuXHRcdFx0XHRcdFx0aWYgKCBtYXRlcmlhbC5tb3JwaE5vcm1hbHMgPT09IHRydWUgKSBnZW9tZXRyeS5yZW1vdmVBdHRyaWJ1dGUoICdtb3JwaE5vcm1hbCcgKyBpICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gYWN0aXZlSW5mbHVlbmNlcy5sZW5ndGgsIGlsID0gbW9ycGhJbmZsdWVuY2VzLmxlbmd0aDsgaSA8IGlsOyBpICsrICkge1xuXG5cdFx0XHRcdFx0bW9ycGhJbmZsdWVuY2VzWyBpIF0gPSAwLjA7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHByb2dyYW0uZ2V0VW5pZm9ybXMoKS5zZXRWYWx1ZShcblx0XHRcdFx0XHRfZ2wsICdtb3JwaFRhcmdldEluZmx1ZW5jZXMnLCBtb3JwaEluZmx1ZW5jZXMgKTtcblxuXHRcdFx0XHR1cGRhdGVCdWZmZXJzID0gdHJ1ZTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvL1xuXG5cdFx0XHR2YXIgaW5kZXggPSBnZW9tZXRyeS5pbmRleDtcblx0XHRcdHZhciBwb3NpdGlvbiA9IGdlb21ldHJ5LmF0dHJpYnV0ZXMucG9zaXRpb247XG5cdFx0XHR2YXIgcmFuZ2VGYWN0b3IgPSAxO1xuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLndpcmVmcmFtZSA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRpbmRleCA9IG9iamVjdHMuZ2V0V2lyZWZyYW1lQXR0cmlidXRlKCBnZW9tZXRyeSApO1xuXHRcdFx0XHRyYW5nZUZhY3RvciA9IDI7XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIHJlbmRlcmVyO1xuXG5cdFx0XHRpZiAoIGluZGV4ICE9PSBudWxsICkge1xuXG5cdFx0XHRcdHJlbmRlcmVyID0gaW5kZXhlZEJ1ZmZlclJlbmRlcmVyO1xuXHRcdFx0XHRyZW5kZXJlci5zZXRJbmRleCggaW5kZXggKTtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRyZW5kZXJlciA9IGJ1ZmZlclJlbmRlcmVyO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggdXBkYXRlQnVmZmVycyApIHtcblxuXHRcdFx0XHRzZXR1cFZlcnRleEF0dHJpYnV0ZXMoIG1hdGVyaWFsLCBwcm9ncmFtLCBnZW9tZXRyeSApO1xuXG5cdFx0XHRcdGlmICggaW5kZXggIT09IG51bGwgKSB7XG5cblx0XHRcdFx0XHRfZ2wuYmluZEJ1ZmZlciggX2dsLkVMRU1FTlRfQVJSQVlfQlVGRkVSLCBvYmplY3RzLmdldEF0dHJpYnV0ZUJ1ZmZlciggaW5kZXggKSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHQvL1xuXG5cdFx0XHR2YXIgZGF0YUNvdW50ID0gMDtcblxuXHRcdFx0aWYgKCBpbmRleCAhPT0gbnVsbCApIHtcblxuXHRcdFx0XHRkYXRhQ291bnQgPSBpbmRleC5jb3VudDtcblxuXHRcdFx0fSBlbHNlIGlmICggcG9zaXRpb24gIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRkYXRhQ291bnQgPSBwb3NpdGlvbi5jb3VudDtcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgcmFuZ2VTdGFydCA9IGdlb21ldHJ5LmRyYXdSYW5nZS5zdGFydCAqIHJhbmdlRmFjdG9yO1xuXHRcdFx0dmFyIHJhbmdlQ291bnQgPSBnZW9tZXRyeS5kcmF3UmFuZ2UuY291bnQgKiByYW5nZUZhY3RvcjtcblxuXHRcdFx0dmFyIGdyb3VwU3RhcnQgPSBncm91cCAhPT0gbnVsbCA/IGdyb3VwLnN0YXJ0ICogcmFuZ2VGYWN0b3IgOiAwO1xuXHRcdFx0dmFyIGdyb3VwQ291bnQgPSBncm91cCAhPT0gbnVsbCA/IGdyb3VwLmNvdW50ICogcmFuZ2VGYWN0b3IgOiBJbmZpbml0eTtcblxuXHRcdFx0dmFyIGRyYXdTdGFydCA9IE1hdGgubWF4KCByYW5nZVN0YXJ0LCBncm91cFN0YXJ0ICk7XG5cdFx0XHR2YXIgZHJhd0VuZCA9IE1hdGgubWluKCBkYXRhQ291bnQsIHJhbmdlU3RhcnQgKyByYW5nZUNvdW50LCBncm91cFN0YXJ0ICsgZ3JvdXBDb3VudCApIC0gMTtcblxuXHRcdFx0dmFyIGRyYXdDb3VudCA9IE1hdGgubWF4KCAwLCBkcmF3RW5kIC0gZHJhd1N0YXJ0ICsgMSApO1xuXG5cdFx0XHRpZiAoIGRyYXdDb3VudCA9PT0gMCApIHJldHVybjtcblxuXHRcdFx0Ly9cblxuXHRcdFx0aWYgKCBvYmplY3QuaXNNZXNoICkge1xuXG5cdFx0XHRcdGlmICggbWF0ZXJpYWwud2lyZWZyYW1lID09PSB0cnVlICkge1xuXG5cdFx0XHRcdFx0c3RhdGUuc2V0TGluZVdpZHRoKCBtYXRlcmlhbC53aXJlZnJhbWVMaW5ld2lkdGggKiBnZXRUYXJnZXRQaXhlbFJhdGlvKCkgKTtcblx0XHRcdFx0XHRyZW5kZXJlci5zZXRNb2RlKCBfZ2wuTElORVMgKTtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0c3dpdGNoICggb2JqZWN0LmRyYXdNb2RlICkge1xuXG5cdFx0XHRcdFx0XHRjYXNlIFRyaWFuZ2xlc0RyYXdNb2RlOlxuXHRcdFx0XHRcdFx0XHRyZW5kZXJlci5zZXRNb2RlKCBfZ2wuVFJJQU5HTEVTICk7XG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRjYXNlIFRyaWFuZ2xlU3RyaXBEcmF3TW9kZTpcblx0XHRcdFx0XHRcdFx0cmVuZGVyZXIuc2V0TW9kZSggX2dsLlRSSUFOR0xFX1NUUklQICk7XG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRjYXNlIFRyaWFuZ2xlRmFuRHJhd01vZGU6XG5cdFx0XHRcdFx0XHRcdHJlbmRlcmVyLnNldE1vZGUoIF9nbC5UUklBTkdMRV9GQU4gKTtcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cblx0XHRcdH0gZWxzZSBpZiAoIG9iamVjdC5pc0xpbmUgKSB7XG5cblx0XHRcdFx0dmFyIGxpbmVXaWR0aCA9IG1hdGVyaWFsLmxpbmV3aWR0aDtcblxuXHRcdFx0XHRpZiAoIGxpbmVXaWR0aCA9PT0gdW5kZWZpbmVkICkgbGluZVdpZHRoID0gMTsgLy8gTm90IHVzaW5nIExpbmUqTWF0ZXJpYWxcblxuXHRcdFx0XHRzdGF0ZS5zZXRMaW5lV2lkdGgoIGxpbmVXaWR0aCAqIGdldFRhcmdldFBpeGVsUmF0aW8oKSApO1xuXG5cdFx0XHRcdGlmICggb2JqZWN0LmlzTGluZVNlZ21lbnRzICkge1xuXG5cdFx0XHRcdFx0cmVuZGVyZXIuc2V0TW9kZSggX2dsLkxJTkVTICk7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdHJlbmRlcmVyLnNldE1vZGUoIF9nbC5MSU5FX1NUUklQICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9IGVsc2UgaWYgKCBvYmplY3QuaXNQb2ludHMgKSB7XG5cblx0XHRcdFx0cmVuZGVyZXIuc2V0TW9kZSggX2dsLlBPSU5UUyApO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggZ2VvbWV0cnkgJiYgZ2VvbWV0cnkuaXNJbnN0YW5jZWRCdWZmZXJHZW9tZXRyeSApIHtcblxuXHRcdFx0XHRpZiAoIGdlb21ldHJ5Lm1heEluc3RhbmNlZENvdW50ID4gMCApIHtcblxuXHRcdFx0XHRcdHJlbmRlcmVyLnJlbmRlckluc3RhbmNlcyggZ2VvbWV0cnksIGRyYXdTdGFydCwgZHJhd0NvdW50ICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdHJlbmRlcmVyLnJlbmRlciggZHJhd1N0YXJ0LCBkcmF3Q291bnQgKTtcblxuXHRcdFx0fVxuXG5cdFx0fTtcblxuXHRcdGZ1bmN0aW9uIHNldHVwVmVydGV4QXR0cmlidXRlcyggbWF0ZXJpYWwsIHByb2dyYW0sIGdlb21ldHJ5LCBzdGFydEluZGV4ICkge1xuXG5cdFx0XHR2YXIgZXh0ZW5zaW9uO1xuXG5cdFx0XHRpZiAoIGdlb21ldHJ5ICYmIGdlb21ldHJ5LmlzSW5zdGFuY2VkQnVmZmVyR2VvbWV0cnkgKSB7XG5cblx0XHRcdFx0ZXh0ZW5zaW9uID0gZXh0ZW5zaW9ucy5nZXQoICdBTkdMRV9pbnN0YW5jZWRfYXJyYXlzJyApO1xuXG5cdFx0XHRcdGlmICggZXh0ZW5zaW9uID09PSBudWxsICkge1xuXG5cdFx0XHRcdFx0Y29uc29sZS5lcnJvciggJ1RIUkVFLldlYkdMUmVuZGVyZXIuc2V0dXBWZXJ0ZXhBdHRyaWJ1dGVzOiB1c2luZyBUSFJFRS5JbnN0YW5jZWRCdWZmZXJHZW9tZXRyeSBidXQgaGFyZHdhcmUgZG9lcyBub3Qgc3VwcG9ydCBleHRlbnNpb24gQU5HTEVfaW5zdGFuY2VkX2FycmF5cy4nICk7XG5cdFx0XHRcdFx0cmV0dXJuO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHN0YXJ0SW5kZXggPT09IHVuZGVmaW5lZCApIHN0YXJ0SW5kZXggPSAwO1xuXG5cdFx0XHRzdGF0ZS5pbml0QXR0cmlidXRlcygpO1xuXG5cdFx0XHR2YXIgZ2VvbWV0cnlBdHRyaWJ1dGVzID0gZ2VvbWV0cnkuYXR0cmlidXRlcztcblxuXHRcdFx0dmFyIHByb2dyYW1BdHRyaWJ1dGVzID0gcHJvZ3JhbS5nZXRBdHRyaWJ1dGVzKCk7XG5cblx0XHRcdHZhciBtYXRlcmlhbERlZmF1bHRBdHRyaWJ1dGVWYWx1ZXMgPSBtYXRlcmlhbC5kZWZhdWx0QXR0cmlidXRlVmFsdWVzO1xuXG5cdFx0XHRmb3IgKCB2YXIgbmFtZSBpbiBwcm9ncmFtQXR0cmlidXRlcyApIHtcblxuXHRcdFx0XHR2YXIgcHJvZ3JhbUF0dHJpYnV0ZSA9IHByb2dyYW1BdHRyaWJ1dGVzWyBuYW1lIF07XG5cblx0XHRcdFx0aWYgKCBwcm9ncmFtQXR0cmlidXRlID49IDAgKSB7XG5cblx0XHRcdFx0XHR2YXIgZ2VvbWV0cnlBdHRyaWJ1dGUgPSBnZW9tZXRyeUF0dHJpYnV0ZXNbIG5hbWUgXTtcblxuXHRcdFx0XHRcdGlmICggZ2VvbWV0cnlBdHRyaWJ1dGUgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdFx0dmFyIG5vcm1hbGl6ZWQgPSBnZW9tZXRyeUF0dHJpYnV0ZS5ub3JtYWxpemVkO1xuXHRcdFx0XHRcdFx0dmFyIHNpemUgPSBnZW9tZXRyeUF0dHJpYnV0ZS5pdGVtU2l6ZTtcblxuXHRcdFx0XHRcdFx0dmFyIGF0dHJpYnV0ZVByb3BlcnRpZXMgPSBvYmplY3RzLmdldEF0dHJpYnV0ZVByb3BlcnRpZXMoIGdlb21ldHJ5QXR0cmlidXRlICk7XG5cblx0XHRcdFx0XHRcdHZhciBidWZmZXIgPSBhdHRyaWJ1dGVQcm9wZXJ0aWVzLl9fd2ViZ2xCdWZmZXI7XG5cdFx0XHRcdFx0XHR2YXIgdHlwZSA9IGF0dHJpYnV0ZVByb3BlcnRpZXMudHlwZTtcblx0XHRcdFx0XHRcdHZhciBieXRlc1BlckVsZW1lbnQgPSBhdHRyaWJ1dGVQcm9wZXJ0aWVzLmJ5dGVzUGVyRWxlbWVudDtcblxuXHRcdFx0XHRcdFx0aWYgKCBnZW9tZXRyeUF0dHJpYnV0ZS5pc0ludGVybGVhdmVkQnVmZmVyQXR0cmlidXRlICkge1xuXG5cdFx0XHRcdFx0XHRcdHZhciBkYXRhID0gZ2VvbWV0cnlBdHRyaWJ1dGUuZGF0YTtcblx0XHRcdFx0XHRcdFx0dmFyIHN0cmlkZSA9IGRhdGEuc3RyaWRlO1xuXHRcdFx0XHRcdFx0XHR2YXIgb2Zmc2V0ID0gZ2VvbWV0cnlBdHRyaWJ1dGUub2Zmc2V0O1xuXG5cdFx0XHRcdFx0XHRcdGlmICggZGF0YSAmJiBkYXRhLmlzSW5zdGFuY2VkSW50ZXJsZWF2ZWRCdWZmZXIgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHRzdGF0ZS5lbmFibGVBdHRyaWJ1dGVBbmREaXZpc29yKCBwcm9ncmFtQXR0cmlidXRlLCBkYXRhLm1lc2hQZXJBdHRyaWJ1dGUsIGV4dGVuc2lvbiApO1xuXG5cdFx0XHRcdFx0XHRcdFx0aWYgKCBnZW9tZXRyeS5tYXhJbnN0YW5jZWRDb3VudCA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0XHRnZW9tZXRyeS5tYXhJbnN0YW5jZWRDb3VudCA9IGRhdGEubWVzaFBlckF0dHJpYnV0ZSAqIGRhdGEuY291bnQ7XG5cblx0XHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0XHRcdHN0YXRlLmVuYWJsZUF0dHJpYnV0ZSggcHJvZ3JhbUF0dHJpYnV0ZSApO1xuXG5cdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0XHRfZ2wuYmluZEJ1ZmZlciggX2dsLkFSUkFZX0JVRkZFUiwgYnVmZmVyICk7XG5cdFx0XHRcdFx0XHRcdF9nbC52ZXJ0ZXhBdHRyaWJQb2ludGVyKCBwcm9ncmFtQXR0cmlidXRlLCBzaXplLCB0eXBlLCBub3JtYWxpemVkLCBzdHJpZGUgKiBieXRlc1BlckVsZW1lbnQsICggc3RhcnRJbmRleCAqIHN0cmlkZSArIG9mZnNldCApICogYnl0ZXNQZXJFbGVtZW50ICk7XG5cblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdFx0aWYgKCBnZW9tZXRyeUF0dHJpYnV0ZS5pc0luc3RhbmNlZEJ1ZmZlckF0dHJpYnV0ZSApIHtcblxuXHRcdFx0XHRcdFx0XHRcdHN0YXRlLmVuYWJsZUF0dHJpYnV0ZUFuZERpdmlzb3IoIHByb2dyYW1BdHRyaWJ1dGUsIGdlb21ldHJ5QXR0cmlidXRlLm1lc2hQZXJBdHRyaWJ1dGUsIGV4dGVuc2lvbiApO1xuXG5cdFx0XHRcdFx0XHRcdFx0aWYgKCBnZW9tZXRyeS5tYXhJbnN0YW5jZWRDb3VudCA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0XHRnZW9tZXRyeS5tYXhJbnN0YW5jZWRDb3VudCA9IGdlb21ldHJ5QXR0cmlidXRlLm1lc2hQZXJBdHRyaWJ1dGUgKiBnZW9tZXRyeUF0dHJpYnV0ZS5jb3VudDtcblxuXHRcdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRcdFx0c3RhdGUuZW5hYmxlQXR0cmlidXRlKCBwcm9ncmFtQXR0cmlidXRlICk7XG5cblx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRcdF9nbC5iaW5kQnVmZmVyKCBfZ2wuQVJSQVlfQlVGRkVSLCBidWZmZXIgKTtcblx0XHRcdFx0XHRcdFx0X2dsLnZlcnRleEF0dHJpYlBvaW50ZXIoIHByb2dyYW1BdHRyaWJ1dGUsIHNpemUsIHR5cGUsIG5vcm1hbGl6ZWQsIDAsIHN0YXJ0SW5kZXggKiBzaXplICogYnl0ZXNQZXJFbGVtZW50ICk7XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdH0gZWxzZSBpZiAoIG1hdGVyaWFsRGVmYXVsdEF0dHJpYnV0ZVZhbHVlcyAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0XHR2YXIgdmFsdWUgPSBtYXRlcmlhbERlZmF1bHRBdHRyaWJ1dGVWYWx1ZXNbIG5hbWUgXTtcblxuXHRcdFx0XHRcdFx0aWYgKCB2YWx1ZSAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0XHRcdHN3aXRjaCAoIHZhbHVlLmxlbmd0aCApIHtcblxuXHRcdFx0XHRcdFx0XHRcdGNhc2UgMjpcblx0XHRcdFx0XHRcdFx0XHRcdF9nbC52ZXJ0ZXhBdHRyaWIyZnYoIHByb2dyYW1BdHRyaWJ1dGUsIHZhbHVlICk7XG5cdFx0XHRcdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdFx0XHRcdGNhc2UgMzpcblx0XHRcdFx0XHRcdFx0XHRcdF9nbC52ZXJ0ZXhBdHRyaWIzZnYoIHByb2dyYW1BdHRyaWJ1dGUsIHZhbHVlICk7XG5cdFx0XHRcdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdFx0XHRcdGNhc2UgNDpcblx0XHRcdFx0XHRcdFx0XHRcdF9nbC52ZXJ0ZXhBdHRyaWI0ZnYoIHByb2dyYW1BdHRyaWJ1dGUsIHZhbHVlICk7XG5cdFx0XHRcdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdFx0XHRcdGRlZmF1bHQ6XG5cdFx0XHRcdFx0XHRcdFx0XHRfZ2wudmVydGV4QXR0cmliMWZ2KCBwcm9ncmFtQXR0cmlidXRlLCB2YWx1ZSApO1xuXG5cdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRzdGF0ZS5kaXNhYmxlVW51c2VkQXR0cmlidXRlcygpO1xuXG5cdFx0fVxuXG5cdFx0Ly8gU29ydGluZ1xuXG5cdFx0ZnVuY3Rpb24gYWJzTnVtZXJpY2FsU29ydCggYSwgYiApIHtcblxuXHRcdFx0cmV0dXJuIE1hdGguYWJzKCBiWyAwIF0gKSAtIE1hdGguYWJzKCBhWyAwIF0gKTtcblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHBhaW50ZXJTb3J0U3RhYmxlKCBhLCBiICkge1xuXG5cdFx0XHRpZiAoIGEub2JqZWN0LnJlbmRlck9yZGVyICE9PSBiLm9iamVjdC5yZW5kZXJPcmRlciApIHtcblxuXHRcdFx0XHRyZXR1cm4gYS5vYmplY3QucmVuZGVyT3JkZXIgLSBiLm9iamVjdC5yZW5kZXJPcmRlcjtcblxuXHRcdFx0fSBlbHNlIGlmICggYS5tYXRlcmlhbC5wcm9ncmFtICYmIGIubWF0ZXJpYWwucHJvZ3JhbSAmJiBhLm1hdGVyaWFsLnByb2dyYW0gIT09IGIubWF0ZXJpYWwucHJvZ3JhbSApIHtcblxuXHRcdFx0XHRyZXR1cm4gYS5tYXRlcmlhbC5wcm9ncmFtLmlkIC0gYi5tYXRlcmlhbC5wcm9ncmFtLmlkO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCBhLm1hdGVyaWFsLmlkICE9PSBiLm1hdGVyaWFsLmlkICkge1xuXG5cdFx0XHRcdHJldHVybiBhLm1hdGVyaWFsLmlkIC0gYi5tYXRlcmlhbC5pZDtcblxuXHRcdFx0fSBlbHNlIGlmICggYS56ICE9PSBiLnogKSB7XG5cblx0XHRcdFx0cmV0dXJuIGEueiAtIGIuejtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRyZXR1cm4gYS5pZCAtIGIuaWQ7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHJldmVyc2VQYWludGVyU29ydFN0YWJsZSggYSwgYiApIHtcblxuXHRcdFx0aWYgKCBhLm9iamVjdC5yZW5kZXJPcmRlciAhPT0gYi5vYmplY3QucmVuZGVyT3JkZXIgKSB7XG5cblx0XHRcdFx0cmV0dXJuIGEub2JqZWN0LnJlbmRlck9yZGVyIC0gYi5vYmplY3QucmVuZGVyT3JkZXI7XG5cblx0XHRcdH0gaWYgKCBhLnogIT09IGIueiApIHtcblxuXHRcdFx0XHRyZXR1cm4gYi56IC0gYS56O1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdHJldHVybiBhLmlkIC0gYi5pZDtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0Ly8gUmVuZGVyaW5nXG5cblx0XHR0aGlzLnJlbmRlciA9IGZ1bmN0aW9uICggc2NlbmUsIGNhbWVyYSwgcmVuZGVyVGFyZ2V0LCBmb3JjZUNsZWFyICkge1xuXG5cdFx0XHRpZiAoIGNhbWVyYSAhPT0gdW5kZWZpbmVkICYmIGNhbWVyYS5pc0NhbWVyYSAhPT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRjb25zb2xlLmVycm9yKCAnVEhSRUUuV2ViR0xSZW5kZXJlci5yZW5kZXI6IGNhbWVyYSBpcyBub3QgYW4gaW5zdGFuY2Ugb2YgVEhSRUUuQ2FtZXJhLicgKTtcblx0XHRcdFx0cmV0dXJuO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIHJlc2V0IGNhY2hpbmcgZm9yIHRoaXMgZnJhbWVcblxuXHRcdFx0X2N1cnJlbnRHZW9tZXRyeVByb2dyYW0gPSAnJztcblx0XHRcdF9jdXJyZW50TWF0ZXJpYWxJZCA9IC0gMTtcblx0XHRcdF9jdXJyZW50Q2FtZXJhID0gbnVsbDtcblxuXHRcdFx0Ly8gdXBkYXRlIHNjZW5lIGdyYXBoXG5cblx0XHRcdGlmICggc2NlbmUuYXV0b1VwZGF0ZSA9PT0gdHJ1ZSApIHNjZW5lLnVwZGF0ZU1hdHJpeFdvcmxkKCk7XG5cblx0XHRcdC8vIHVwZGF0ZSBjYW1lcmEgbWF0cmljZXMgYW5kIGZydXN0dW1cblxuXHRcdFx0aWYgKCBjYW1lcmEucGFyZW50ID09PSBudWxsICkgY2FtZXJhLnVwZGF0ZU1hdHJpeFdvcmxkKCk7XG5cblx0XHRcdGNhbWVyYS5tYXRyaXhXb3JsZEludmVyc2UuZ2V0SW52ZXJzZSggY2FtZXJhLm1hdHJpeFdvcmxkICk7XG5cblx0XHRcdF9wcm9qU2NyZWVuTWF0cml4Lm11bHRpcGx5TWF0cmljZXMoIGNhbWVyYS5wcm9qZWN0aW9uTWF0cml4LCBjYW1lcmEubWF0cml4V29ybGRJbnZlcnNlICk7XG5cdFx0XHRfZnJ1c3R1bS5zZXRGcm9tTWF0cml4KCBfcHJvalNjcmVlbk1hdHJpeCApO1xuXG5cdFx0XHRsaWdodHMubGVuZ3RoID0gMDtcblxuXHRcdFx0b3BhcXVlT2JqZWN0c0xhc3RJbmRleCA9IC0gMTtcblx0XHRcdHRyYW5zcGFyZW50T2JqZWN0c0xhc3RJbmRleCA9IC0gMTtcblxuXHRcdFx0c3ByaXRlcy5sZW5ndGggPSAwO1xuXHRcdFx0bGVuc0ZsYXJlcy5sZW5ndGggPSAwO1xuXG5cdFx0XHRfbG9jYWxDbGlwcGluZ0VuYWJsZWQgPSB0aGlzLmxvY2FsQ2xpcHBpbmdFbmFibGVkO1xuXHRcdFx0X2NsaXBwaW5nRW5hYmxlZCA9IF9jbGlwcGluZy5pbml0KCB0aGlzLmNsaXBwaW5nUGxhbmVzLCBfbG9jYWxDbGlwcGluZ0VuYWJsZWQsIGNhbWVyYSApO1xuXG5cdFx0XHRwcm9qZWN0T2JqZWN0KCBzY2VuZSwgY2FtZXJhICk7XG5cblx0XHRcdG9wYXF1ZU9iamVjdHMubGVuZ3RoID0gb3BhcXVlT2JqZWN0c0xhc3RJbmRleCArIDE7XG5cdFx0XHR0cmFuc3BhcmVudE9iamVjdHMubGVuZ3RoID0gdHJhbnNwYXJlbnRPYmplY3RzTGFzdEluZGV4ICsgMTtcblxuXHRcdFx0aWYgKCBfdGhpcy5zb3J0T2JqZWN0cyA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRvcGFxdWVPYmplY3RzLnNvcnQoIHBhaW50ZXJTb3J0U3RhYmxlICk7XG5cdFx0XHRcdHRyYW5zcGFyZW50T2JqZWN0cy5zb3J0KCByZXZlcnNlUGFpbnRlclNvcnRTdGFibGUgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvL1xuXG5cdFx0XHRpZiAoIF9jbGlwcGluZ0VuYWJsZWQgKSBfY2xpcHBpbmcuYmVnaW5TaGFkb3dzKCk7XG5cblx0XHRcdHNldHVwU2hhZG93cyggbGlnaHRzICk7XG5cblx0XHRcdHNoYWRvd01hcC5yZW5kZXIoIHNjZW5lLCBjYW1lcmEgKTtcblxuXHRcdFx0c2V0dXBMaWdodHMoIGxpZ2h0cywgY2FtZXJhICk7XG5cblx0XHRcdGlmICggX2NsaXBwaW5nRW5hYmxlZCApIF9jbGlwcGluZy5lbmRTaGFkb3dzKCk7XG5cblx0XHRcdC8vXG5cblx0XHRcdF9pbmZvUmVuZGVyLmNhbGxzID0gMDtcblx0XHRcdF9pbmZvUmVuZGVyLnZlcnRpY2VzID0gMDtcblx0XHRcdF9pbmZvUmVuZGVyLmZhY2VzID0gMDtcblx0XHRcdF9pbmZvUmVuZGVyLnBvaW50cyA9IDA7XG5cblx0XHRcdGlmICggcmVuZGVyVGFyZ2V0ID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0cmVuZGVyVGFyZ2V0ID0gbnVsbDtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLnNldFJlbmRlclRhcmdldCggcmVuZGVyVGFyZ2V0ICk7XG5cblx0XHRcdC8vXG5cblx0XHRcdHZhciBiYWNrZ3JvdW5kID0gc2NlbmUuYmFja2dyb3VuZDtcblxuXHRcdFx0aWYgKCBiYWNrZ3JvdW5kID09PSBudWxsICkge1xuXG5cdFx0XHRcdHN0YXRlLmJ1ZmZlcnMuY29sb3Iuc2V0Q2xlYXIoIF9jbGVhckNvbG9yLnIsIF9jbGVhckNvbG9yLmcsIF9jbGVhckNvbG9yLmIsIF9jbGVhckFscGhhLCBfcHJlbXVsdGlwbGllZEFscGhhICk7XG5cblx0XHRcdH0gZWxzZSBpZiAoIGJhY2tncm91bmQgJiYgYmFja2dyb3VuZC5pc0NvbG9yICkge1xuXG5cdFx0XHRcdHN0YXRlLmJ1ZmZlcnMuY29sb3Iuc2V0Q2xlYXIoIGJhY2tncm91bmQuciwgYmFja2dyb3VuZC5nLCBiYWNrZ3JvdW5kLmIsIDEsIF9wcmVtdWx0aXBsaWVkQWxwaGEgKTtcblx0XHRcdFx0Zm9yY2VDbGVhciA9IHRydWU7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCB0aGlzLmF1dG9DbGVhciB8fCBmb3JjZUNsZWFyICkge1xuXG5cdFx0XHRcdHRoaXMuY2xlYXIoIHRoaXMuYXV0b0NsZWFyQ29sb3IsIHRoaXMuYXV0b0NsZWFyRGVwdGgsIHRoaXMuYXV0b0NsZWFyU3RlbmNpbCApO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggYmFja2dyb3VuZCAmJiBiYWNrZ3JvdW5kLmlzQ3ViZVRleHR1cmUgKSB7XG5cblx0XHRcdFx0YmFja2dyb3VuZENhbWVyYTIucHJvamVjdGlvbk1hdHJpeC5jb3B5KCBjYW1lcmEucHJvamVjdGlvbk1hdHJpeCApO1xuXG5cdFx0XHRcdGJhY2tncm91bmRDYW1lcmEyLm1hdHJpeFdvcmxkLmV4dHJhY3RSb3RhdGlvbiggY2FtZXJhLm1hdHJpeFdvcmxkICk7XG5cdFx0XHRcdGJhY2tncm91bmRDYW1lcmEyLm1hdHJpeFdvcmxkSW52ZXJzZS5nZXRJbnZlcnNlKCBiYWNrZ3JvdW5kQ2FtZXJhMi5tYXRyaXhXb3JsZCApO1xuXG5cdFx0XHRcdGJhY2tncm91bmRCb3hNZXNoLm1hdGVyaWFsLnVuaWZvcm1zWyBcInRDdWJlXCIgXS52YWx1ZSA9IGJhY2tncm91bmQ7XG5cdFx0XHRcdGJhY2tncm91bmRCb3hNZXNoLm1vZGVsVmlld01hdHJpeC5tdWx0aXBseU1hdHJpY2VzKCBiYWNrZ3JvdW5kQ2FtZXJhMi5tYXRyaXhXb3JsZEludmVyc2UsIGJhY2tncm91bmRCb3hNZXNoLm1hdHJpeFdvcmxkICk7XG5cblx0XHRcdFx0b2JqZWN0cy51cGRhdGUoIGJhY2tncm91bmRCb3hNZXNoICk7XG5cblx0XHRcdFx0X3RoaXMucmVuZGVyQnVmZmVyRGlyZWN0KCBiYWNrZ3JvdW5kQ2FtZXJhMiwgbnVsbCwgYmFja2dyb3VuZEJveE1lc2guZ2VvbWV0cnksIGJhY2tncm91bmRCb3hNZXNoLm1hdGVyaWFsLCBiYWNrZ3JvdW5kQm94TWVzaCwgbnVsbCApO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCBiYWNrZ3JvdW5kICYmIGJhY2tncm91bmQuaXNUZXh0dXJlICkge1xuXG5cdFx0XHRcdGJhY2tncm91bmRQbGFuZU1lc2gubWF0ZXJpYWwubWFwID0gYmFja2dyb3VuZDtcblxuXHRcdFx0XHRvYmplY3RzLnVwZGF0ZSggYmFja2dyb3VuZFBsYW5lTWVzaCApO1xuXG5cdFx0XHRcdF90aGlzLnJlbmRlckJ1ZmZlckRpcmVjdCggYmFja2dyb3VuZENhbWVyYSwgbnVsbCwgYmFja2dyb3VuZFBsYW5lTWVzaC5nZW9tZXRyeSwgYmFja2dyb3VuZFBsYW5lTWVzaC5tYXRlcmlhbCwgYmFja2dyb3VuZFBsYW5lTWVzaCwgbnVsbCApO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vXG5cblx0XHRcdGlmICggc2NlbmUub3ZlcnJpZGVNYXRlcmlhbCApIHtcblxuXHRcdFx0XHR2YXIgb3ZlcnJpZGVNYXRlcmlhbCA9IHNjZW5lLm92ZXJyaWRlTWF0ZXJpYWw7XG5cblx0XHRcdFx0cmVuZGVyT2JqZWN0cyggb3BhcXVlT2JqZWN0cywgc2NlbmUsIGNhbWVyYSwgb3ZlcnJpZGVNYXRlcmlhbCApO1xuXHRcdFx0XHRyZW5kZXJPYmplY3RzKCB0cmFuc3BhcmVudE9iamVjdHMsIHNjZW5lLCBjYW1lcmEsIG92ZXJyaWRlTWF0ZXJpYWwgKTtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHQvLyBvcGFxdWUgcGFzcyAoZnJvbnQtdG8tYmFjayBvcmRlcilcblxuXHRcdFx0XHRzdGF0ZS5zZXRCbGVuZGluZyggTm9CbGVuZGluZyApO1xuXHRcdFx0XHRyZW5kZXJPYmplY3RzKCBvcGFxdWVPYmplY3RzLCBzY2VuZSwgY2FtZXJhICk7XG5cblx0XHRcdFx0Ly8gdHJhbnNwYXJlbnQgcGFzcyAoYmFjay10by1mcm9udCBvcmRlcilcblxuXHRcdFx0XHRyZW5kZXJPYmplY3RzKCB0cmFuc3BhcmVudE9iamVjdHMsIHNjZW5lLCBjYW1lcmEgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyBjdXN0b20gcmVuZGVyIHBsdWdpbnMgKHBvc3QgcGFzcylcblxuXHRcdFx0c3ByaXRlUGx1Z2luLnJlbmRlciggc2NlbmUsIGNhbWVyYSApO1xuXHRcdFx0bGVuc0ZsYXJlUGx1Z2luLnJlbmRlciggc2NlbmUsIGNhbWVyYSwgX2N1cnJlbnRWaWV3cG9ydCApO1xuXG5cdFx0XHQvLyBHZW5lcmF0ZSBtaXBtYXAgaWYgd2UncmUgdXNpbmcgYW55IGtpbmQgb2YgbWlwbWFwIGZpbHRlcmluZ1xuXG5cdFx0XHRpZiAoIHJlbmRlclRhcmdldCApIHtcblxuXHRcdFx0XHR0ZXh0dXJlcy51cGRhdGVSZW5kZXJUYXJnZXRNaXBtYXAoIHJlbmRlclRhcmdldCApO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIEVuc3VyZSBkZXB0aCBidWZmZXIgd3JpdGluZyBpcyBlbmFibGVkIHNvIGl0IGNhbiBiZSBjbGVhcmVkIG9uIG5leHQgcmVuZGVyXG5cblx0XHRcdHN0YXRlLnNldERlcHRoVGVzdCggdHJ1ZSApO1xuXHRcdFx0c3RhdGUuc2V0RGVwdGhXcml0ZSggdHJ1ZSApO1xuXHRcdFx0c3RhdGUuc2V0Q29sb3JXcml0ZSggdHJ1ZSApO1xuXG5cdFx0XHQvLyBfZ2wuZmluaXNoKCk7XG5cblx0XHR9O1xuXG5cdFx0ZnVuY3Rpb24gcHVzaFJlbmRlckl0ZW0oIG9iamVjdCwgZ2VvbWV0cnksIG1hdGVyaWFsLCB6LCBncm91cCApIHtcblxuXHRcdFx0dmFyIGFycmF5LCBpbmRleDtcblxuXHRcdFx0Ly8gYWxsb2NhdGUgdGhlIG5leHQgcG9zaXRpb24gaW4gdGhlIGFwcHJvcHJpYXRlIGFycmF5XG5cblx0XHRcdGlmICggbWF0ZXJpYWwudHJhbnNwYXJlbnQgKSB7XG5cblx0XHRcdFx0YXJyYXkgPSB0cmFuc3BhcmVudE9iamVjdHM7XG5cdFx0XHRcdGluZGV4ID0gKysgdHJhbnNwYXJlbnRPYmplY3RzTGFzdEluZGV4O1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdGFycmF5ID0gb3BhcXVlT2JqZWN0cztcblx0XHRcdFx0aW5kZXggPSArKyBvcGFxdWVPYmplY3RzTGFzdEluZGV4O1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIHJlY3ljbGUgZXhpc3RpbmcgcmVuZGVyIGl0ZW0gb3IgZ3JvdyB0aGUgYXJyYXlcblxuXHRcdFx0dmFyIHJlbmRlckl0ZW0gPSBhcnJheVsgaW5kZXggXTtcblxuXHRcdFx0aWYgKCByZW5kZXJJdGVtICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0cmVuZGVySXRlbS5pZCA9IG9iamVjdC5pZDtcblx0XHRcdFx0cmVuZGVySXRlbS5vYmplY3QgPSBvYmplY3Q7XG5cdFx0XHRcdHJlbmRlckl0ZW0uZ2VvbWV0cnkgPSBnZW9tZXRyeTtcblx0XHRcdFx0cmVuZGVySXRlbS5tYXRlcmlhbCA9IG1hdGVyaWFsO1xuXHRcdFx0XHRyZW5kZXJJdGVtLnogPSBfdmVjdG9yMy56O1xuXHRcdFx0XHRyZW5kZXJJdGVtLmdyb3VwID0gZ3JvdXA7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0cmVuZGVySXRlbSA9IHtcblx0XHRcdFx0XHRpZDogb2JqZWN0LmlkLFxuXHRcdFx0XHRcdG9iamVjdDogb2JqZWN0LFxuXHRcdFx0XHRcdGdlb21ldHJ5OiBnZW9tZXRyeSxcblx0XHRcdFx0XHRtYXRlcmlhbDogbWF0ZXJpYWwsXG5cdFx0XHRcdFx0ejogX3ZlY3RvcjMueixcblx0XHRcdFx0XHRncm91cDogZ3JvdXBcblx0XHRcdFx0fTtcblxuXHRcdFx0XHQvLyBhc3NlcnQoIGluZGV4ID09PSBhcnJheS5sZW5ndGggKTtcblx0XHRcdFx0YXJyYXkucHVzaCggcmVuZGVySXRlbSApO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHQvLyBUT0RPIER1cGxpY2F0ZWQgY29kZSAoRnJ1c3R1bSlcblxuXHRcdGZ1bmN0aW9uIGlzT2JqZWN0Vmlld2FibGUoIG9iamVjdCApIHtcblxuXHRcdFx0dmFyIGdlb21ldHJ5ID0gb2JqZWN0Lmdlb21ldHJ5O1xuXG5cdFx0XHRpZiAoIGdlb21ldHJ5LmJvdW5kaW5nU3BoZXJlID09PSBudWxsIClcblx0XHRcdFx0Z2VvbWV0cnkuY29tcHV0ZUJvdW5kaW5nU3BoZXJlKCk7XG5cblx0XHRcdF9zcGhlcmUuY29weSggZ2VvbWV0cnkuYm91bmRpbmdTcGhlcmUgKS5cblx0XHRcdGFwcGx5TWF0cml4NCggb2JqZWN0Lm1hdHJpeFdvcmxkICk7XG5cblx0XHRcdHJldHVybiBpc1NwaGVyZVZpZXdhYmxlKCBfc3BoZXJlICk7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBpc1Nwcml0ZVZpZXdhYmxlKCBzcHJpdGUgKSB7XG5cblx0XHRcdF9zcGhlcmUuY2VudGVyLnNldCggMCwgMCwgMCApO1xuXHRcdFx0X3NwaGVyZS5yYWRpdXMgPSAwLjcwNzEwNjc4MTE4NjU0NzY7XG5cdFx0XHRfc3BoZXJlLmFwcGx5TWF0cml4NCggc3ByaXRlLm1hdHJpeFdvcmxkICk7XG5cblx0XHRcdHJldHVybiBpc1NwaGVyZVZpZXdhYmxlKCBfc3BoZXJlICk7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBpc1NwaGVyZVZpZXdhYmxlKCBzcGhlcmUgKSB7XG5cblx0XHRcdGlmICggISBfZnJ1c3R1bS5pbnRlcnNlY3RzU3BoZXJlKCBzcGhlcmUgKSApIHJldHVybiBmYWxzZTtcblxuXHRcdFx0dmFyIG51bVBsYW5lcyA9IF9jbGlwcGluZy5udW1QbGFuZXM7XG5cblx0XHRcdGlmICggbnVtUGxhbmVzID09PSAwICkgcmV0dXJuIHRydWU7XG5cblx0XHRcdHZhciBwbGFuZXMgPSBfdGhpcy5jbGlwcGluZ1BsYW5lcyxcblxuXHRcdFx0XHRjZW50ZXIgPSBzcGhlcmUuY2VudGVyLFxuXHRcdFx0XHRuZWdSYWQgPSAtIHNwaGVyZS5yYWRpdXMsXG5cdFx0XHRcdGkgPSAwO1xuXG5cdFx0XHRkbyB7XG5cblx0XHRcdFx0Ly8gb3V0IHdoZW4gZGVlcGVyIHRoYW4gcmFkaXVzIGluIHRoZSBuZWdhdGl2ZSBoYWxmc3BhY2Vcblx0XHRcdFx0aWYgKCBwbGFuZXNbIGkgXS5kaXN0YW5jZVRvUG9pbnQoIGNlbnRlciApIDwgbmVnUmFkICkgcmV0dXJuIGZhbHNlO1xuXG5cdFx0XHR9IHdoaWxlICggKysgaSAhPT0gbnVtUGxhbmVzICk7XG5cblx0XHRcdHJldHVybiB0cnVlO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gcHJvamVjdE9iamVjdCggb2JqZWN0LCBjYW1lcmEgKSB7XG5cblx0XHRcdGlmICggb2JqZWN0LnZpc2libGUgPT09IGZhbHNlICkgcmV0dXJuO1xuXG5cdFx0XHR2YXIgdmlzaWJsZSA9ICggb2JqZWN0LmxheWVycy5tYXNrICYgY2FtZXJhLmxheWVycy5tYXNrICkgIT09IDA7XG5cblx0XHRcdGlmICggdmlzaWJsZSApIHtcblxuXHRcdFx0XHRpZiAoIG9iamVjdC5pc0xpZ2h0ICkge1xuXG5cdFx0XHRcdFx0bGlnaHRzLnB1c2goIG9iamVjdCApO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAoIG9iamVjdC5pc1Nwcml0ZSApIHtcblxuXHRcdFx0XHRcdGlmICggb2JqZWN0LmZydXN0dW1DdWxsZWQgPT09IGZhbHNlIHx8IGlzU3ByaXRlVmlld2FibGUoIG9iamVjdCApID09PSB0cnVlICkge1xuXG5cdFx0XHRcdFx0XHRzcHJpdGVzLnB1c2goIG9iamVjdCApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH0gZWxzZSBpZiAoIG9iamVjdC5pc0xlbnNGbGFyZSApIHtcblxuXHRcdFx0XHRcdGxlbnNGbGFyZXMucHVzaCggb2JqZWN0ICk7XG5cblx0XHRcdFx0fSBlbHNlIGlmICggb2JqZWN0LmlzSW1tZWRpYXRlUmVuZGVyT2JqZWN0ICkge1xuXG5cdFx0XHRcdFx0aWYgKCBfdGhpcy5zb3J0T2JqZWN0cyA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRcdFx0X3ZlY3RvcjMuc2V0RnJvbU1hdHJpeFBvc2l0aW9uKCBvYmplY3QubWF0cml4V29ybGQgKTtcblx0XHRcdFx0XHRcdF92ZWN0b3IzLmFwcGx5UHJvamVjdGlvbiggX3Byb2pTY3JlZW5NYXRyaXggKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdHB1c2hSZW5kZXJJdGVtKCBvYmplY3QsIG51bGwsIG9iamVjdC5tYXRlcmlhbCwgX3ZlY3RvcjMueiwgbnVsbCApO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAoIG9iamVjdC5pc01lc2ggfHwgb2JqZWN0LmlzTGluZSB8fCBvYmplY3QuaXNQb2ludHMgKSB7XG5cblx0XHRcdFx0XHRpZiAoIG9iamVjdC5pc1NraW5uZWRNZXNoICkge1xuXG5cdFx0XHRcdFx0XHRvYmplY3Quc2tlbGV0b24udXBkYXRlKCk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRpZiAoIG9iamVjdC5mcnVzdHVtQ3VsbGVkID09PSBmYWxzZSB8fCBpc09iamVjdFZpZXdhYmxlKCBvYmplY3QgKSA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRcdFx0dmFyIG1hdGVyaWFsID0gb2JqZWN0Lm1hdGVyaWFsO1xuXG5cdFx0XHRcdFx0XHRpZiAoIG1hdGVyaWFsLnZpc2libGUgPT09IHRydWUgKSB7XG5cblx0XHRcdFx0XHRcdFx0aWYgKCBfdGhpcy5zb3J0T2JqZWN0cyA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRcdFx0XHRcdF92ZWN0b3IzLnNldEZyb21NYXRyaXhQb3NpdGlvbiggb2JqZWN0Lm1hdHJpeFdvcmxkICk7XG5cdFx0XHRcdFx0XHRcdFx0X3ZlY3RvcjMuYXBwbHlQcm9qZWN0aW9uKCBfcHJvalNjcmVlbk1hdHJpeCApO1xuXG5cdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0XHR2YXIgZ2VvbWV0cnkgPSBvYmplY3RzLnVwZGF0ZSggb2JqZWN0ICk7XG5cblx0XHRcdFx0XHRcdFx0aWYgKCBtYXRlcmlhbC5pc011bHRpTWF0ZXJpYWwgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHR2YXIgZ3JvdXBzID0gZ2VvbWV0cnkuZ3JvdXBzO1xuXHRcdFx0XHRcdFx0XHRcdHZhciBtYXRlcmlhbHMgPSBtYXRlcmlhbC5tYXRlcmlhbHM7XG5cblx0XHRcdFx0XHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBncm91cHMubGVuZ3RoOyBpIDwgbDsgaSArKyApIHtcblxuXHRcdFx0XHRcdFx0XHRcdFx0dmFyIGdyb3VwID0gZ3JvdXBzWyBpIF07XG5cdFx0XHRcdFx0XHRcdFx0XHR2YXIgZ3JvdXBNYXRlcmlhbCA9IG1hdGVyaWFsc1sgZ3JvdXAubWF0ZXJpYWxJbmRleCBdO1xuXG5cdFx0XHRcdFx0XHRcdFx0XHRpZiAoIGdyb3VwTWF0ZXJpYWwudmlzaWJsZSA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRcdFx0XHRcdFx0XHRwdXNoUmVuZGVySXRlbSggb2JqZWN0LCBnZW9tZXRyeSwgZ3JvdXBNYXRlcmlhbCwgX3ZlY3RvcjMueiwgZ3JvdXAgKTtcblxuXHRcdFx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdFx0XHRwdXNoUmVuZGVySXRlbSggb2JqZWN0LCBnZW9tZXRyeSwgbWF0ZXJpYWwsIF92ZWN0b3IzLnosIG51bGwgKTtcblxuXHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIGNoaWxkcmVuID0gb2JqZWN0LmNoaWxkcmVuO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBjaGlsZHJlbi5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdHByb2plY3RPYmplY3QoIGNoaWxkcmVuWyBpIF0sIGNhbWVyYSApO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiByZW5kZXJPYmplY3RzKCByZW5kZXJMaXN0LCBzY2VuZSwgY2FtZXJhLCBvdmVycmlkZU1hdGVyaWFsICkge1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSByZW5kZXJMaXN0Lmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0dmFyIHJlbmRlckl0ZW0gPSByZW5kZXJMaXN0WyBpIF07XG5cblx0XHRcdFx0dmFyIG9iamVjdCA9IHJlbmRlckl0ZW0ub2JqZWN0O1xuXHRcdFx0XHR2YXIgZ2VvbWV0cnkgPSByZW5kZXJJdGVtLmdlb21ldHJ5O1xuXHRcdFx0XHR2YXIgbWF0ZXJpYWwgPSBvdmVycmlkZU1hdGVyaWFsID09PSB1bmRlZmluZWQgPyByZW5kZXJJdGVtLm1hdGVyaWFsIDogb3ZlcnJpZGVNYXRlcmlhbDtcblx0XHRcdFx0dmFyIGdyb3VwID0gcmVuZGVySXRlbS5ncm91cDtcblxuXHRcdFx0XHRvYmplY3QubW9kZWxWaWV3TWF0cml4Lm11bHRpcGx5TWF0cmljZXMoIGNhbWVyYS5tYXRyaXhXb3JsZEludmVyc2UsIG9iamVjdC5tYXRyaXhXb3JsZCApO1xuXHRcdFx0XHRvYmplY3Qubm9ybWFsTWF0cml4LmdldE5vcm1hbE1hdHJpeCggb2JqZWN0Lm1vZGVsVmlld01hdHJpeCApO1xuXG5cdFx0XHRcdG9iamVjdC5vbkJlZm9yZVJlbmRlciggX3RoaXMsIHNjZW5lLCBjYW1lcmEsIGdlb21ldHJ5LCBtYXRlcmlhbCwgZ3JvdXAgKTtcblxuXHRcdFx0XHRpZiAoIG9iamVjdC5pc0ltbWVkaWF0ZVJlbmRlck9iamVjdCApIHtcblxuXHRcdFx0XHRcdHNldE1hdGVyaWFsKCBtYXRlcmlhbCApO1xuXG5cdFx0XHRcdFx0dmFyIHByb2dyYW0gPSBzZXRQcm9ncmFtKCBjYW1lcmEsIHNjZW5lLmZvZywgbWF0ZXJpYWwsIG9iamVjdCApO1xuXG5cdFx0XHRcdFx0X2N1cnJlbnRHZW9tZXRyeVByb2dyYW0gPSAnJztcblxuXHRcdFx0XHRcdG9iamVjdC5yZW5kZXIoIGZ1bmN0aW9uICggb2JqZWN0ICkge1xuXG5cdFx0XHRcdFx0XHRfdGhpcy5yZW5kZXJCdWZmZXJJbW1lZGlhdGUoIG9iamVjdCwgcHJvZ3JhbSwgbWF0ZXJpYWwgKTtcblxuXHRcdFx0XHRcdH0gKTtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0X3RoaXMucmVuZGVyQnVmZmVyRGlyZWN0KCBjYW1lcmEsIHNjZW5lLmZvZywgZ2VvbWV0cnksIG1hdGVyaWFsLCBvYmplY3QsIGdyb3VwICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdG9iamVjdC5vbkFmdGVyUmVuZGVyKCBfdGhpcywgc2NlbmUsIGNhbWVyYSwgZ2VvbWV0cnksIG1hdGVyaWFsLCBncm91cCApO1xuXG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIGluaXRNYXRlcmlhbCggbWF0ZXJpYWwsIGZvZywgb2JqZWN0ICkge1xuXG5cdFx0XHR2YXIgbWF0ZXJpYWxQcm9wZXJ0aWVzID0gcHJvcGVydGllcy5nZXQoIG1hdGVyaWFsICk7XG5cblx0XHRcdHZhciBwYXJhbWV0ZXJzID0gcHJvZ3JhbUNhY2hlLmdldFBhcmFtZXRlcnMoXG5cdFx0XHRcdG1hdGVyaWFsLCBfbGlnaHRzLCBmb2csIF9jbGlwcGluZy5udW1QbGFuZXMsIF9jbGlwcGluZy5udW1JbnRlcnNlY3Rpb24sIG9iamVjdCApO1xuXG5cdFx0XHR2YXIgY29kZSA9IHByb2dyYW1DYWNoZS5nZXRQcm9ncmFtQ29kZSggbWF0ZXJpYWwsIHBhcmFtZXRlcnMgKTtcblxuXHRcdFx0dmFyIHByb2dyYW0gPSBtYXRlcmlhbFByb3BlcnRpZXMucHJvZ3JhbTtcblx0XHRcdHZhciBwcm9ncmFtQ2hhbmdlID0gdHJ1ZTtcblxuXHRcdFx0aWYgKCBwcm9ncmFtID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0Ly8gbmV3IG1hdGVyaWFsXG5cdFx0XHRcdG1hdGVyaWFsLmFkZEV2ZW50TGlzdGVuZXIoICdkaXNwb3NlJywgb25NYXRlcmlhbERpc3Bvc2UgKTtcblxuXHRcdFx0fSBlbHNlIGlmICggcHJvZ3JhbS5jb2RlICE9PSBjb2RlICkge1xuXG5cdFx0XHRcdC8vIGNoYW5nZWQgZ2xzbCBvciBwYXJhbWV0ZXJzXG5cdFx0XHRcdHJlbGVhc2VNYXRlcmlhbFByb2dyYW1SZWZlcmVuY2UoIG1hdGVyaWFsICk7XG5cblx0XHRcdH0gZWxzZSBpZiAoIHBhcmFtZXRlcnMuc2hhZGVySUQgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHQvLyBzYW1lIGdsc2wgYW5kIHVuaWZvcm0gbGlzdFxuXHRcdFx0XHRyZXR1cm47XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0Ly8gb25seSByZWJ1aWxkIHVuaWZvcm0gbGlzdFxuXHRcdFx0XHRwcm9ncmFtQ2hhbmdlID0gZmFsc2U7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBwcm9ncmFtQ2hhbmdlICkge1xuXG5cdFx0XHRcdGlmICggcGFyYW1ldGVycy5zaGFkZXJJRCApIHtcblxuXHRcdFx0XHRcdHZhciBzaGFkZXIgPSBTaGFkZXJMaWJbIHBhcmFtZXRlcnMuc2hhZGVySUQgXTtcblxuXHRcdFx0XHRcdG1hdGVyaWFsUHJvcGVydGllcy5fX3dlYmdsU2hhZGVyID0ge1xuXHRcdFx0XHRcdFx0bmFtZTogbWF0ZXJpYWwudHlwZSxcblx0XHRcdFx0XHRcdHVuaWZvcm1zOiBVbmlmb3Jtc1V0aWxzLmNsb25lKCBzaGFkZXIudW5pZm9ybXMgKSxcblx0XHRcdFx0XHRcdHZlcnRleFNoYWRlcjogc2hhZGVyLnZlcnRleFNoYWRlcixcblx0XHRcdFx0XHRcdGZyYWdtZW50U2hhZGVyOiBzaGFkZXIuZnJhZ21lbnRTaGFkZXJcblx0XHRcdFx0XHR9O1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRtYXRlcmlhbFByb3BlcnRpZXMuX193ZWJnbFNoYWRlciA9IHtcblx0XHRcdFx0XHRcdG5hbWU6IG1hdGVyaWFsLnR5cGUsXG5cdFx0XHRcdFx0XHR1bmlmb3JtczogbWF0ZXJpYWwudW5pZm9ybXMsXG5cdFx0XHRcdFx0XHR2ZXJ0ZXhTaGFkZXI6IG1hdGVyaWFsLnZlcnRleFNoYWRlcixcblx0XHRcdFx0XHRcdGZyYWdtZW50U2hhZGVyOiBtYXRlcmlhbC5mcmFnbWVudFNoYWRlclxuXHRcdFx0XHRcdH07XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdG1hdGVyaWFsLl9fd2ViZ2xTaGFkZXIgPSBtYXRlcmlhbFByb3BlcnRpZXMuX193ZWJnbFNoYWRlcjtcblxuXHRcdFx0XHRwcm9ncmFtID0gcHJvZ3JhbUNhY2hlLmFjcXVpcmVQcm9ncmFtKCBtYXRlcmlhbCwgcGFyYW1ldGVycywgY29kZSApO1xuXG5cdFx0XHRcdG1hdGVyaWFsUHJvcGVydGllcy5wcm9ncmFtID0gcHJvZ3JhbTtcblx0XHRcdFx0bWF0ZXJpYWwucHJvZ3JhbSA9IHByb2dyYW07XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIGF0dHJpYnV0ZXMgPSBwcm9ncmFtLmdldEF0dHJpYnV0ZXMoKTtcblxuXHRcdFx0aWYgKCBtYXRlcmlhbC5tb3JwaFRhcmdldHMgKSB7XG5cblx0XHRcdFx0bWF0ZXJpYWwubnVtU3VwcG9ydGVkTW9ycGhUYXJnZXRzID0gMDtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBfdGhpcy5tYXhNb3JwaFRhcmdldHM7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRpZiAoIGF0dHJpYnV0ZXNbICdtb3JwaFRhcmdldCcgKyBpIF0gPj0gMCApIHtcblxuXHRcdFx0XHRcdFx0bWF0ZXJpYWwubnVtU3VwcG9ydGVkTW9ycGhUYXJnZXRzICsrO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLm1vcnBoTm9ybWFscyApIHtcblxuXHRcdFx0XHRtYXRlcmlhbC5udW1TdXBwb3J0ZWRNb3JwaE5vcm1hbHMgPSAwO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IF90aGlzLm1heE1vcnBoTm9ybWFsczsgaSArKyApIHtcblxuXHRcdFx0XHRcdGlmICggYXR0cmlidXRlc1sgJ21vcnBoTm9ybWFsJyArIGkgXSA+PSAwICkge1xuXG5cdFx0XHRcdFx0XHRtYXRlcmlhbC5udW1TdXBwb3J0ZWRNb3JwaE5vcm1hbHMgKys7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdHZhciB1bmlmb3JtcyA9IG1hdGVyaWFsUHJvcGVydGllcy5fX3dlYmdsU2hhZGVyLnVuaWZvcm1zO1xuXG5cdFx0XHRpZiAoICEgbWF0ZXJpYWwuaXNTaGFkZXJNYXRlcmlhbCAmJlxuXHRcdFx0XHQhIG1hdGVyaWFsLmlzUmF3U2hhZGVyTWF0ZXJpYWwgfHxcblx0XHRcdFx0bWF0ZXJpYWwuY2xpcHBpbmcgPT09IHRydWUgKSB7XG5cblx0XHRcdFx0bWF0ZXJpYWxQcm9wZXJ0aWVzLm51bUNsaXBwaW5nUGxhbmVzID0gX2NsaXBwaW5nLm51bVBsYW5lcztcblx0XHRcdFx0bWF0ZXJpYWxQcm9wZXJ0aWVzLm51bUludGVyc2VjdGlvbiA9IF9jbGlwcGluZy5udW1JbnRlcnNlY3Rpb247XG5cdFx0XHRcdHVuaWZvcm1zLmNsaXBwaW5nUGxhbmVzID0gX2NsaXBwaW5nLnVuaWZvcm07XG5cblx0XHRcdH1cblxuXHRcdFx0bWF0ZXJpYWxQcm9wZXJ0aWVzLmZvZyA9IGZvZztcblxuXHRcdFx0Ly8gc3RvcmUgdGhlIGxpZ2h0IHNldHVwIGl0IHdhcyBjcmVhdGVkIGZvclxuXG5cdFx0XHRtYXRlcmlhbFByb3BlcnRpZXMubGlnaHRzSGFzaCA9IF9saWdodHMuaGFzaDtcblxuXHRcdFx0aWYgKCBtYXRlcmlhbC5saWdodHMgKSB7XG5cblx0XHRcdFx0Ly8gd2lyZSB1cCB0aGUgbWF0ZXJpYWwgdG8gdGhpcyByZW5kZXJlcidzIGxpZ2h0aW5nIHN0YXRlXG5cblx0XHRcdFx0dW5pZm9ybXMuYW1iaWVudExpZ2h0Q29sb3IudmFsdWUgPSBfbGlnaHRzLmFtYmllbnQ7XG5cdFx0XHRcdHVuaWZvcm1zLmRpcmVjdGlvbmFsTGlnaHRzLnZhbHVlID0gX2xpZ2h0cy5kaXJlY3Rpb25hbDtcblx0XHRcdFx0dW5pZm9ybXMuc3BvdExpZ2h0cy52YWx1ZSA9IF9saWdodHMuc3BvdDtcblx0XHRcdFx0dW5pZm9ybXMucmVjdEFyZWFMaWdodHMudmFsdWUgPSBfbGlnaHRzLnJlY3RBcmVhO1xuXHRcdFx0XHR1bmlmb3Jtcy5wb2ludExpZ2h0cy52YWx1ZSA9IF9saWdodHMucG9pbnQ7XG5cdFx0XHRcdHVuaWZvcm1zLmhlbWlzcGhlcmVMaWdodHMudmFsdWUgPSBfbGlnaHRzLmhlbWk7XG5cblx0XHRcdFx0dW5pZm9ybXMuZGlyZWN0aW9uYWxTaGFkb3dNYXAudmFsdWUgPSBfbGlnaHRzLmRpcmVjdGlvbmFsU2hhZG93TWFwO1xuXHRcdFx0XHR1bmlmb3Jtcy5kaXJlY3Rpb25hbFNoYWRvd01hdHJpeC52YWx1ZSA9IF9saWdodHMuZGlyZWN0aW9uYWxTaGFkb3dNYXRyaXg7XG5cdFx0XHRcdHVuaWZvcm1zLnNwb3RTaGFkb3dNYXAudmFsdWUgPSBfbGlnaHRzLnNwb3RTaGFkb3dNYXA7XG5cdFx0XHRcdHVuaWZvcm1zLnNwb3RTaGFkb3dNYXRyaXgudmFsdWUgPSBfbGlnaHRzLnNwb3RTaGFkb3dNYXRyaXg7XG5cdFx0XHRcdHVuaWZvcm1zLnBvaW50U2hhZG93TWFwLnZhbHVlID0gX2xpZ2h0cy5wb2ludFNoYWRvd01hcDtcblx0XHRcdFx0dW5pZm9ybXMucG9pbnRTaGFkb3dNYXRyaXgudmFsdWUgPSBfbGlnaHRzLnBvaW50U2hhZG93TWF0cml4O1xuXHRcdFx0XHQvLyBUT0RPIChhYmVsbmF0aW9uKTogYWRkIGFyZWEgbGlnaHRzIHNoYWRvdyBpbmZvIHRvIHVuaWZvcm1zXG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIHByb2dVbmlmb3JtcyA9IG1hdGVyaWFsUHJvcGVydGllcy5wcm9ncmFtLmdldFVuaWZvcm1zKCksXG5cdFx0XHRcdHVuaWZvcm1zTGlzdCA9XG5cdFx0XHRcdFx0V2ViR0xVbmlmb3Jtcy5zZXFXaXRoVmFsdWUoIHByb2dVbmlmb3Jtcy5zZXEsIHVuaWZvcm1zICk7XG5cblx0XHRcdG1hdGVyaWFsUHJvcGVydGllcy51bmlmb3Jtc0xpc3QgPSB1bmlmb3Jtc0xpc3Q7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBzZXRNYXRlcmlhbCggbWF0ZXJpYWwgKSB7XG5cblx0XHRcdG1hdGVyaWFsLnNpZGUgPT09IERvdWJsZVNpZGVcblx0XHRcdFx0PyBzdGF0ZS5kaXNhYmxlKCBfZ2wuQ1VMTF9GQUNFIClcblx0XHRcdFx0OiBzdGF0ZS5lbmFibGUoIF9nbC5DVUxMX0ZBQ0UgKTtcblxuXHRcdFx0c3RhdGUuc2V0RmxpcFNpZGVkKCBtYXRlcmlhbC5zaWRlID09PSBCYWNrU2lkZSApO1xuXG5cdFx0XHRtYXRlcmlhbC50cmFuc3BhcmVudCA9PT0gdHJ1ZVxuXHRcdFx0XHQ/IHN0YXRlLnNldEJsZW5kaW5nKCBtYXRlcmlhbC5ibGVuZGluZywgbWF0ZXJpYWwuYmxlbmRFcXVhdGlvbiwgbWF0ZXJpYWwuYmxlbmRTcmMsIG1hdGVyaWFsLmJsZW5kRHN0LCBtYXRlcmlhbC5ibGVuZEVxdWF0aW9uQWxwaGEsIG1hdGVyaWFsLmJsZW5kU3JjQWxwaGEsIG1hdGVyaWFsLmJsZW5kRHN0QWxwaGEsIG1hdGVyaWFsLnByZW11bHRpcGxpZWRBbHBoYSApXG5cdFx0XHRcdDogc3RhdGUuc2V0QmxlbmRpbmcoIE5vQmxlbmRpbmcgKTtcblxuXHRcdFx0c3RhdGUuc2V0RGVwdGhGdW5jKCBtYXRlcmlhbC5kZXB0aEZ1bmMgKTtcblx0XHRcdHN0YXRlLnNldERlcHRoVGVzdCggbWF0ZXJpYWwuZGVwdGhUZXN0ICk7XG5cdFx0XHRzdGF0ZS5zZXREZXB0aFdyaXRlKCBtYXRlcmlhbC5kZXB0aFdyaXRlICk7XG5cdFx0XHRzdGF0ZS5zZXRDb2xvcldyaXRlKCBtYXRlcmlhbC5jb2xvcldyaXRlICk7XG5cdFx0XHRzdGF0ZS5zZXRQb2x5Z29uT2Zmc2V0KCBtYXRlcmlhbC5wb2x5Z29uT2Zmc2V0LCBtYXRlcmlhbC5wb2x5Z29uT2Zmc2V0RmFjdG9yLCBtYXRlcmlhbC5wb2x5Z29uT2Zmc2V0VW5pdHMgKTtcblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHNldFByb2dyYW0oIGNhbWVyYSwgZm9nLCBtYXRlcmlhbCwgb2JqZWN0ICkge1xuXG5cdFx0XHRfdXNlZFRleHR1cmVVbml0cyA9IDA7XG5cblx0XHRcdHZhciBtYXRlcmlhbFByb3BlcnRpZXMgPSBwcm9wZXJ0aWVzLmdldCggbWF0ZXJpYWwgKTtcblxuXHRcdFx0aWYgKCBfY2xpcHBpbmdFbmFibGVkICkge1xuXG5cdFx0XHRcdGlmICggX2xvY2FsQ2xpcHBpbmdFbmFibGVkIHx8IGNhbWVyYSAhPT0gX2N1cnJlbnRDYW1lcmEgKSB7XG5cblx0XHRcdFx0XHR2YXIgdXNlQ2FjaGUgPVxuXHRcdFx0XHRcdFx0Y2FtZXJhID09PSBfY3VycmVudENhbWVyYSAmJlxuXHRcdFx0XHRcdFx0bWF0ZXJpYWwuaWQgPT09IF9jdXJyZW50TWF0ZXJpYWxJZDtcblxuXHRcdFx0XHRcdC8vIHdlIG1pZ2h0IHdhbnQgdG8gY2FsbCB0aGlzIGZ1bmN0aW9uIHdpdGggc29tZSBDbGlwcGluZ0dyb3VwXG5cdFx0XHRcdFx0Ly8gb2JqZWN0IGluc3RlYWQgb2YgdGhlIG1hdGVyaWFsLCBvbmNlIGl0IGJlY29tZXMgZmVhc2libGVcblx0XHRcdFx0XHQvLyAoIzg0NjUsICM4Mzc5KVxuXHRcdFx0XHRcdF9jbGlwcGluZy5zZXRTdGF0ZShcblx0XHRcdFx0XHRcdG1hdGVyaWFsLmNsaXBwaW5nUGxhbmVzLCBtYXRlcmlhbC5jbGlwSW50ZXJzZWN0aW9uLCBtYXRlcmlhbC5jbGlwU2hhZG93cyxcblx0XHRcdFx0XHRcdGNhbWVyYSwgbWF0ZXJpYWxQcm9wZXJ0aWVzLCB1c2VDYWNoZSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLm5lZWRzVXBkYXRlID09PSBmYWxzZSApIHtcblxuXHRcdFx0XHRpZiAoIG1hdGVyaWFsUHJvcGVydGllcy5wcm9ncmFtID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRtYXRlcmlhbC5uZWVkc1VwZGF0ZSA9IHRydWU7XG5cblx0XHRcdFx0fSBlbHNlIGlmICggbWF0ZXJpYWwuZm9nICYmIG1hdGVyaWFsUHJvcGVydGllcy5mb2cgIT09IGZvZyApIHtcblxuXHRcdFx0XHRcdG1hdGVyaWFsLm5lZWRzVXBkYXRlID0gdHJ1ZTtcblxuXHRcdFx0XHR9IGVsc2UgaWYgKCBtYXRlcmlhbC5saWdodHMgJiYgbWF0ZXJpYWxQcm9wZXJ0aWVzLmxpZ2h0c0hhc2ggIT09IF9saWdodHMuaGFzaCApIHtcblxuXHRcdFx0XHRcdG1hdGVyaWFsLm5lZWRzVXBkYXRlID0gdHJ1ZTtcblxuXHRcdFx0XHR9IGVsc2UgaWYgKCBtYXRlcmlhbFByb3BlcnRpZXMubnVtQ2xpcHBpbmdQbGFuZXMgIT09IHVuZGVmaW5lZCAmJlxuXHRcdFx0XHRcdCggbWF0ZXJpYWxQcm9wZXJ0aWVzLm51bUNsaXBwaW5nUGxhbmVzICE9PSBfY2xpcHBpbmcubnVtUGxhbmVzIHx8XG5cdFx0XHRcdFx0bWF0ZXJpYWxQcm9wZXJ0aWVzLm51bUludGVyc2VjdGlvbiAgIT09IF9jbGlwcGluZy5udW1JbnRlcnNlY3Rpb24gKSApIHtcblxuXHRcdFx0XHRcdG1hdGVyaWFsLm5lZWRzVXBkYXRlID0gdHJ1ZTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBtYXRlcmlhbC5uZWVkc1VwZGF0ZSApIHtcblxuXHRcdFx0XHRpbml0TWF0ZXJpYWwoIG1hdGVyaWFsLCBmb2csIG9iamVjdCApO1xuXHRcdFx0XHRtYXRlcmlhbC5uZWVkc1VwZGF0ZSA9IGZhbHNlO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciByZWZyZXNoUHJvZ3JhbSA9IGZhbHNlO1xuXHRcdFx0dmFyIHJlZnJlc2hNYXRlcmlhbCA9IGZhbHNlO1xuXHRcdFx0dmFyIHJlZnJlc2hMaWdodHMgPSBmYWxzZTtcblxuXHRcdFx0dmFyIHByb2dyYW0gPSBtYXRlcmlhbFByb3BlcnRpZXMucHJvZ3JhbSxcblx0XHRcdFx0cF91bmlmb3JtcyA9IHByb2dyYW0uZ2V0VW5pZm9ybXMoKSxcblx0XHRcdFx0bV91bmlmb3JtcyA9IG1hdGVyaWFsUHJvcGVydGllcy5fX3dlYmdsU2hhZGVyLnVuaWZvcm1zO1xuXG5cdFx0XHRpZiAoIHByb2dyYW0uaWQgIT09IF9jdXJyZW50UHJvZ3JhbSApIHtcblxuXHRcdFx0XHRfZ2wudXNlUHJvZ3JhbSggcHJvZ3JhbS5wcm9ncmFtICk7XG5cdFx0XHRcdF9jdXJyZW50UHJvZ3JhbSA9IHByb2dyYW0uaWQ7XG5cblx0XHRcdFx0cmVmcmVzaFByb2dyYW0gPSB0cnVlO1xuXHRcdFx0XHRyZWZyZXNoTWF0ZXJpYWwgPSB0cnVlO1xuXHRcdFx0XHRyZWZyZXNoTGlnaHRzID0gdHJ1ZTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLmlkICE9PSBfY3VycmVudE1hdGVyaWFsSWQgKSB7XG5cblx0XHRcdFx0X2N1cnJlbnRNYXRlcmlhbElkID0gbWF0ZXJpYWwuaWQ7XG5cblx0XHRcdFx0cmVmcmVzaE1hdGVyaWFsID0gdHJ1ZTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHJlZnJlc2hQcm9ncmFtIHx8IGNhbWVyYSAhPT0gX2N1cnJlbnRDYW1lcmEgKSB7XG5cblx0XHRcdFx0cF91bmlmb3Jtcy5zZXQoIF9nbCwgY2FtZXJhLCAncHJvamVjdGlvbk1hdHJpeCcgKTtcblxuXHRcdFx0XHRpZiAoIGNhcGFiaWxpdGllcy5sb2dhcml0aG1pY0RlcHRoQnVmZmVyICkge1xuXG5cdFx0XHRcdFx0cF91bmlmb3Jtcy5zZXRWYWx1ZSggX2dsLCAnbG9nRGVwdGhCdWZGQycsXG5cdFx0XHRcdFx0XHQyLjAgLyAoIE1hdGgubG9nKCBjYW1lcmEuZmFyICsgMS4wICkgLyBNYXRoLkxOMiApICk7XG5cblx0XHRcdFx0fVxuXG5cblx0XHRcdFx0aWYgKCBjYW1lcmEgIT09IF9jdXJyZW50Q2FtZXJhICkge1xuXG5cdFx0XHRcdFx0X2N1cnJlbnRDYW1lcmEgPSBjYW1lcmE7XG5cblx0XHRcdFx0XHQvLyBsaWdodGluZyB1bmlmb3JtcyBkZXBlbmQgb24gdGhlIGNhbWVyYSBzbyBlbmZvcmNlIGFuIHVwZGF0ZVxuXHRcdFx0XHRcdC8vIG5vdywgaW4gY2FzZSB0aGlzIG1hdGVyaWFsIHN1cHBvcnRzIGxpZ2h0cyAtIG9yIGxhdGVyLCB3aGVuXG5cdFx0XHRcdFx0Ly8gdGhlIG5leHQgbWF0ZXJpYWwgdGhhdCBkb2VzIGdldHMgYWN0aXZhdGVkOlxuXG5cdFx0XHRcdFx0cmVmcmVzaE1hdGVyaWFsID0gdHJ1ZTtcdFx0Ly8gc2V0IHRvIHRydWUgb24gbWF0ZXJpYWwgY2hhbmdlXG5cdFx0XHRcdFx0cmVmcmVzaExpZ2h0cyA9IHRydWU7XHRcdC8vIHJlbWFpbnMgc2V0IHVudGlsIHVwZGF0ZSBkb25lXG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIGxvYWQgbWF0ZXJpYWwgc3BlY2lmaWMgdW5pZm9ybXNcblx0XHRcdFx0Ly8gKHNoYWRlciBtYXRlcmlhbCBhbHNvIGdldHMgdGhlbSBmb3IgdGhlIHNha2Ugb2YgZ2VuZXJpY2l0eSlcblxuXHRcdFx0XHRpZiAoIG1hdGVyaWFsLmlzU2hhZGVyTWF0ZXJpYWwgfHxcblx0XHRcdFx0XHRtYXRlcmlhbC5pc01lc2hQaG9uZ01hdGVyaWFsIHx8XG5cdFx0XHRcdFx0bWF0ZXJpYWwuaXNNZXNoU3RhbmRhcmRNYXRlcmlhbCB8fFxuXHRcdFx0XHRcdG1hdGVyaWFsLmVudk1hcCApIHtcblxuXHRcdFx0XHRcdHZhciB1Q2FtUG9zID0gcF91bmlmb3Jtcy5tYXAuY2FtZXJhUG9zaXRpb247XG5cblx0XHRcdFx0XHRpZiAoIHVDYW1Qb3MgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdFx0dUNhbVBvcy5zZXRWYWx1ZSggX2dsLFxuXHRcdFx0XHRcdFx0XHRfdmVjdG9yMy5zZXRGcm9tTWF0cml4UG9zaXRpb24oIGNhbWVyYS5tYXRyaXhXb3JsZCApICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggbWF0ZXJpYWwuaXNNZXNoUGhvbmdNYXRlcmlhbCB8fFxuXHRcdFx0XHRcdG1hdGVyaWFsLmlzTWVzaExhbWJlcnRNYXRlcmlhbCB8fFxuXHRcdFx0XHRcdG1hdGVyaWFsLmlzTWVzaEJhc2ljTWF0ZXJpYWwgfHxcblx0XHRcdFx0XHRtYXRlcmlhbC5pc01lc2hTdGFuZGFyZE1hdGVyaWFsIHx8XG5cdFx0XHRcdFx0bWF0ZXJpYWwuaXNTaGFkZXJNYXRlcmlhbCB8fFxuXHRcdFx0XHRcdG1hdGVyaWFsLnNraW5uaW5nICkge1xuXG5cdFx0XHRcdFx0cF91bmlmb3Jtcy5zZXRWYWx1ZSggX2dsLCAndmlld01hdHJpeCcsIGNhbWVyYS5tYXRyaXhXb3JsZEludmVyc2UgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cF91bmlmb3Jtcy5zZXQoIF9nbCwgX3RoaXMsICd0b25lTWFwcGluZ0V4cG9zdXJlJyApO1xuXHRcdFx0XHRwX3VuaWZvcm1zLnNldCggX2dsLCBfdGhpcywgJ3RvbmVNYXBwaW5nV2hpdGVQb2ludCcgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyBza2lubmluZyB1bmlmb3JtcyBtdXN0IGJlIHNldCBldmVuIGlmIG1hdGVyaWFsIGRpZG4ndCBjaGFuZ2Vcblx0XHRcdC8vIGF1dG8tc2V0dGluZyBvZiB0ZXh0dXJlIHVuaXQgZm9yIGJvbmUgdGV4dHVyZSBtdXN0IGdvIGJlZm9yZSBvdGhlciB0ZXh0dXJlc1xuXHRcdFx0Ly8gbm90IHN1cmUgd2h5LCBidXQgb3RoZXJ3aXNlIHdlaXJkIHRoaW5ncyBoYXBwZW5cblxuXHRcdFx0aWYgKCBtYXRlcmlhbC5za2lubmluZyApIHtcblxuXHRcdFx0XHRwX3VuaWZvcm1zLnNldE9wdGlvbmFsKCBfZ2wsIG9iamVjdCwgJ2JpbmRNYXRyaXgnICk7XG5cdFx0XHRcdHBfdW5pZm9ybXMuc2V0T3B0aW9uYWwoIF9nbCwgb2JqZWN0LCAnYmluZE1hdHJpeEludmVyc2UnICk7XG5cblx0XHRcdFx0dmFyIHNrZWxldG9uID0gb2JqZWN0LnNrZWxldG9uO1xuXG5cdFx0XHRcdGlmICggc2tlbGV0b24gKSB7XG5cblx0XHRcdFx0XHRpZiAoIGNhcGFiaWxpdGllcy5mbG9hdFZlcnRleFRleHR1cmVzICYmIHNrZWxldG9uLnVzZVZlcnRleFRleHR1cmUgKSB7XG5cblx0XHRcdFx0XHRcdHBfdW5pZm9ybXMuc2V0KCBfZ2wsIHNrZWxldG9uLCAnYm9uZVRleHR1cmUnICk7XG5cdFx0XHRcdFx0XHRwX3VuaWZvcm1zLnNldCggX2dsLCBza2VsZXRvbiwgJ2JvbmVUZXh0dXJlV2lkdGgnICk7XG5cdFx0XHRcdFx0XHRwX3VuaWZvcm1zLnNldCggX2dsLCBza2VsZXRvbiwgJ2JvbmVUZXh0dXJlSGVpZ2h0JyApO1xuXG5cdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0cF91bmlmb3Jtcy5zZXRPcHRpb25hbCggX2dsLCBza2VsZXRvbiwgJ2JvbmVNYXRyaWNlcycgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCByZWZyZXNoTWF0ZXJpYWwgKSB7XG5cblx0XHRcdFx0aWYgKCBtYXRlcmlhbC5saWdodHMgKSB7XG5cblx0XHRcdFx0XHQvLyB0aGUgY3VycmVudCBtYXRlcmlhbCByZXF1aXJlcyBsaWdodGluZyBpbmZvXG5cblx0XHRcdFx0XHQvLyBub3RlOiBhbGwgbGlnaHRpbmcgdW5pZm9ybXMgYXJlIGFsd2F5cyBzZXQgY29ycmVjdGx5XG5cdFx0XHRcdFx0Ly8gdGhleSBzaW1wbHkgcmVmZXJlbmNlIHRoZSByZW5kZXJlcidzIHN0YXRlIGZvciB0aGVpclxuXHRcdFx0XHRcdC8vIHZhbHVlc1xuXHRcdFx0XHRcdC8vXG5cdFx0XHRcdFx0Ly8gdXNlIHRoZSBjdXJyZW50IG1hdGVyaWFsJ3MgLm5lZWRzVXBkYXRlIGZsYWdzIHRvIHNldFxuXHRcdFx0XHRcdC8vIHRoZSBHTCBzdGF0ZSB3aGVuIHJlcXVpcmVkXG5cblx0XHRcdFx0XHRtYXJrVW5pZm9ybXNMaWdodHNOZWVkc1VwZGF0ZSggbV91bmlmb3JtcywgcmVmcmVzaExpZ2h0cyApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyByZWZyZXNoIHVuaWZvcm1zIGNvbW1vbiB0byBzZXZlcmFsIG1hdGVyaWFsc1xuXG5cdFx0XHRcdGlmICggZm9nICYmIG1hdGVyaWFsLmZvZyApIHtcblxuXHRcdFx0XHRcdHJlZnJlc2hVbmlmb3Jtc0ZvZyggbV91bmlmb3JtcywgZm9nICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggbWF0ZXJpYWwuaXNNZXNoQmFzaWNNYXRlcmlhbCB8fFxuXHRcdFx0XHRcdG1hdGVyaWFsLmlzTWVzaExhbWJlcnRNYXRlcmlhbCB8fFxuXHRcdFx0XHRcdG1hdGVyaWFsLmlzTWVzaFBob25nTWF0ZXJpYWwgfHxcblx0XHRcdFx0XHRtYXRlcmlhbC5pc01lc2hTdGFuZGFyZE1hdGVyaWFsIHx8XG5cdFx0XHRcdFx0bWF0ZXJpYWwuaXNNZXNoTm9ybWFsTWF0ZXJpYWwgfHxcblx0XHRcdFx0XHRtYXRlcmlhbC5pc01lc2hEZXB0aE1hdGVyaWFsICkge1xuXG5cdFx0XHRcdFx0cmVmcmVzaFVuaWZvcm1zQ29tbW9uKCBtX3VuaWZvcm1zLCBtYXRlcmlhbCApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyByZWZyZXNoIHNpbmdsZSBtYXRlcmlhbCBzcGVjaWZpYyB1bmlmb3Jtc1xuXG5cdFx0XHRcdGlmICggbWF0ZXJpYWwuaXNMaW5lQmFzaWNNYXRlcmlhbCApIHtcblxuXHRcdFx0XHRcdHJlZnJlc2hVbmlmb3Jtc0xpbmUoIG1fdW5pZm9ybXMsIG1hdGVyaWFsICk7XG5cblx0XHRcdFx0fSBlbHNlIGlmICggbWF0ZXJpYWwuaXNMaW5lRGFzaGVkTWF0ZXJpYWwgKSB7XG5cblx0XHRcdFx0XHRyZWZyZXNoVW5pZm9ybXNMaW5lKCBtX3VuaWZvcm1zLCBtYXRlcmlhbCApO1xuXHRcdFx0XHRcdHJlZnJlc2hVbmlmb3Jtc0Rhc2goIG1fdW5pZm9ybXMsIG1hdGVyaWFsICk7XG5cblx0XHRcdFx0fSBlbHNlIGlmICggbWF0ZXJpYWwuaXNQb2ludHNNYXRlcmlhbCApIHtcblxuXHRcdFx0XHRcdHJlZnJlc2hVbmlmb3Jtc1BvaW50cyggbV91bmlmb3JtcywgbWF0ZXJpYWwgKTtcblxuXHRcdFx0XHR9IGVsc2UgaWYgKCBtYXRlcmlhbC5pc01lc2hMYW1iZXJ0TWF0ZXJpYWwgKSB7XG5cblx0XHRcdFx0XHRyZWZyZXNoVW5pZm9ybXNMYW1iZXJ0KCBtX3VuaWZvcm1zLCBtYXRlcmlhbCApO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAoIG1hdGVyaWFsLmlzTWVzaFRvb25NYXRlcmlhbCApIHtcblxuXHRcdFx0XHRcdHJlZnJlc2hVbmlmb3Jtc1Rvb24oIG1fdW5pZm9ybXMsIG1hdGVyaWFsICk7XG5cblx0XHRcdFx0fSBlbHNlIGlmICggbWF0ZXJpYWwuaXNNZXNoUGhvbmdNYXRlcmlhbCApIHtcblxuXHRcdFx0XHRcdHJlZnJlc2hVbmlmb3Jtc1Bob25nKCBtX3VuaWZvcm1zLCBtYXRlcmlhbCApO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAoIG1hdGVyaWFsLmlzTWVzaFBoeXNpY2FsTWF0ZXJpYWwgKSB7XG5cblx0XHRcdFx0XHRyZWZyZXNoVW5pZm9ybXNQaHlzaWNhbCggbV91bmlmb3JtcywgbWF0ZXJpYWwgKTtcblxuXHRcdFx0XHR9IGVsc2UgaWYgKCBtYXRlcmlhbC5pc01lc2hTdGFuZGFyZE1hdGVyaWFsICkge1xuXG5cdFx0XHRcdFx0cmVmcmVzaFVuaWZvcm1zU3RhbmRhcmQoIG1fdW5pZm9ybXMsIG1hdGVyaWFsICk7XG5cblx0XHRcdFx0fSBlbHNlIGlmICggbWF0ZXJpYWwuaXNNZXNoRGVwdGhNYXRlcmlhbCApIHtcblxuXHRcdFx0XHRcdGlmICggbWF0ZXJpYWwuZGlzcGxhY2VtZW50TWFwICkge1xuXG5cdFx0XHRcdFx0XHRtX3VuaWZvcm1zLmRpc3BsYWNlbWVudE1hcC52YWx1ZSA9IG1hdGVyaWFsLmRpc3BsYWNlbWVudE1hcDtcblx0XHRcdFx0XHRcdG1fdW5pZm9ybXMuZGlzcGxhY2VtZW50U2NhbGUudmFsdWUgPSBtYXRlcmlhbC5kaXNwbGFjZW1lbnRTY2FsZTtcblx0XHRcdFx0XHRcdG1fdW5pZm9ybXMuZGlzcGxhY2VtZW50Qmlhcy52YWx1ZSA9IG1hdGVyaWFsLmRpc3BsYWNlbWVudEJpYXM7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSBlbHNlIGlmICggbWF0ZXJpYWwuaXNNZXNoTm9ybWFsTWF0ZXJpYWwgKSB7XG5cblx0XHRcdFx0XHRyZWZyZXNoVW5pZm9ybXNOb3JtYWwoIG1fdW5pZm9ybXMsIG1hdGVyaWFsICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIFJlY3RBcmVhTGlnaHQgVGV4dHVyZVxuXHRcdFx0XHQvLyBUT0RPIChtcmRvb2IpOiBGaW5kIGEgbmljZXIgaW1wbGVtZW50YXRpb25cblxuXHRcdFx0XHRpZiAoIG1fdW5pZm9ybXMubHRjTWF0ICE9PSB1bmRlZmluZWQgKSBtX3VuaWZvcm1zLmx0Y01hdC52YWx1ZSA9IFRIUkVFLlVuaWZvcm1zTGliLkxUQ19NQVRfVEVYVFVSRTtcblx0XHRcdFx0aWYgKCBtX3VuaWZvcm1zLmx0Y01hZyAhPT0gdW5kZWZpbmVkICkgbV91bmlmb3Jtcy5sdGNNYWcudmFsdWUgPSBUSFJFRS5Vbmlmb3Jtc0xpYi5MVENfTUFHX1RFWFRVUkU7XG5cblx0XHRcdFx0V2ViR0xVbmlmb3Jtcy51cGxvYWQoXG5cdFx0XHRcdFx0X2dsLCBtYXRlcmlhbFByb3BlcnRpZXMudW5pZm9ybXNMaXN0LCBtX3VuaWZvcm1zLCBfdGhpcyApO1xuXG5cdFx0XHR9XG5cblxuXHRcdFx0Ly8gY29tbW9uIG1hdHJpY2VzXG5cblx0XHRcdHBfdW5pZm9ybXMuc2V0KCBfZ2wsIG9iamVjdCwgJ21vZGVsVmlld01hdHJpeCcgKTtcblx0XHRcdHBfdW5pZm9ybXMuc2V0KCBfZ2wsIG9iamVjdCwgJ25vcm1hbE1hdHJpeCcgKTtcblx0XHRcdHBfdW5pZm9ybXMuc2V0VmFsdWUoIF9nbCwgJ21vZGVsTWF0cml4Jywgb2JqZWN0Lm1hdHJpeFdvcmxkICk7XG5cblx0XHRcdHJldHVybiBwcm9ncmFtO1xuXG5cdFx0fVxuXG5cdFx0Ly8gVW5pZm9ybXMgKHJlZnJlc2ggdW5pZm9ybXMgb2JqZWN0cylcblxuXHRcdGZ1bmN0aW9uIHJlZnJlc2hVbmlmb3Jtc0NvbW1vbiggdW5pZm9ybXMsIG1hdGVyaWFsICkge1xuXG5cdFx0XHR1bmlmb3Jtcy5vcGFjaXR5LnZhbHVlID0gbWF0ZXJpYWwub3BhY2l0eTtcblxuXHRcdFx0dW5pZm9ybXMuZGlmZnVzZS52YWx1ZSA9IG1hdGVyaWFsLmNvbG9yO1xuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLmVtaXNzaXZlICkge1xuXG5cdFx0XHRcdHVuaWZvcm1zLmVtaXNzaXZlLnZhbHVlLmNvcHkoIG1hdGVyaWFsLmVtaXNzaXZlICkubXVsdGlwbHlTY2FsYXIoIG1hdGVyaWFsLmVtaXNzaXZlSW50ZW5zaXR5ICk7XG5cblx0XHRcdH1cblxuXHRcdFx0dW5pZm9ybXMubWFwLnZhbHVlID0gbWF0ZXJpYWwubWFwO1xuXHRcdFx0dW5pZm9ybXMuc3BlY3VsYXJNYXAudmFsdWUgPSBtYXRlcmlhbC5zcGVjdWxhck1hcDtcblx0XHRcdHVuaWZvcm1zLmFscGhhTWFwLnZhbHVlID0gbWF0ZXJpYWwuYWxwaGFNYXA7XG5cblx0XHRcdGlmICggbWF0ZXJpYWwubGlnaHRNYXAgKSB7XG5cblx0XHRcdFx0dW5pZm9ybXMubGlnaHRNYXAudmFsdWUgPSBtYXRlcmlhbC5saWdodE1hcDtcblx0XHRcdFx0dW5pZm9ybXMubGlnaHRNYXBJbnRlbnNpdHkudmFsdWUgPSBtYXRlcmlhbC5saWdodE1hcEludGVuc2l0eTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLmFvTWFwICkge1xuXG5cdFx0XHRcdHVuaWZvcm1zLmFvTWFwLnZhbHVlID0gbWF0ZXJpYWwuYW9NYXA7XG5cdFx0XHRcdHVuaWZvcm1zLmFvTWFwSW50ZW5zaXR5LnZhbHVlID0gbWF0ZXJpYWwuYW9NYXBJbnRlbnNpdHk7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gdXYgcmVwZWF0IGFuZCBvZmZzZXQgc2V0dGluZyBwcmlvcml0aWVzXG5cdFx0XHQvLyAxLiBjb2xvciBtYXBcblx0XHRcdC8vIDIuIHNwZWN1bGFyIG1hcFxuXHRcdFx0Ly8gMy4gbm9ybWFsIG1hcFxuXHRcdFx0Ly8gNC4gYnVtcCBtYXBcblx0XHRcdC8vIDUuIGFscGhhIG1hcFxuXHRcdFx0Ly8gNi4gZW1pc3NpdmUgbWFwXG5cblx0XHRcdHZhciB1dlNjYWxlTWFwO1xuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLm1hcCApIHtcblxuXHRcdFx0XHR1dlNjYWxlTWFwID0gbWF0ZXJpYWwubWFwO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCBtYXRlcmlhbC5zcGVjdWxhck1hcCApIHtcblxuXHRcdFx0XHR1dlNjYWxlTWFwID0gbWF0ZXJpYWwuc3BlY3VsYXJNYXA7XG5cblx0XHRcdH0gZWxzZSBpZiAoIG1hdGVyaWFsLmRpc3BsYWNlbWVudE1hcCApIHtcblxuXHRcdFx0XHR1dlNjYWxlTWFwID0gbWF0ZXJpYWwuZGlzcGxhY2VtZW50TWFwO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCBtYXRlcmlhbC5ub3JtYWxNYXAgKSB7XG5cblx0XHRcdFx0dXZTY2FsZU1hcCA9IG1hdGVyaWFsLm5vcm1hbE1hcDtcblxuXHRcdFx0fSBlbHNlIGlmICggbWF0ZXJpYWwuYnVtcE1hcCApIHtcblxuXHRcdFx0XHR1dlNjYWxlTWFwID0gbWF0ZXJpYWwuYnVtcE1hcDtcblxuXHRcdFx0fSBlbHNlIGlmICggbWF0ZXJpYWwucm91Z2huZXNzTWFwICkge1xuXG5cdFx0XHRcdHV2U2NhbGVNYXAgPSBtYXRlcmlhbC5yb3VnaG5lc3NNYXA7XG5cblx0XHRcdH0gZWxzZSBpZiAoIG1hdGVyaWFsLm1ldGFsbmVzc01hcCApIHtcblxuXHRcdFx0XHR1dlNjYWxlTWFwID0gbWF0ZXJpYWwubWV0YWxuZXNzTWFwO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCBtYXRlcmlhbC5hbHBoYU1hcCApIHtcblxuXHRcdFx0XHR1dlNjYWxlTWFwID0gbWF0ZXJpYWwuYWxwaGFNYXA7XG5cblx0XHRcdH0gZWxzZSBpZiAoIG1hdGVyaWFsLmVtaXNzaXZlTWFwICkge1xuXG5cdFx0XHRcdHV2U2NhbGVNYXAgPSBtYXRlcmlhbC5lbWlzc2l2ZU1hcDtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHV2U2NhbGVNYXAgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHQvLyBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eVxuXHRcdFx0XHRpZiAoIHV2U2NhbGVNYXAuaXNXZWJHTFJlbmRlclRhcmdldCApIHtcblxuXHRcdFx0XHRcdHV2U2NhbGVNYXAgPSB1dlNjYWxlTWFwLnRleHR1cmU7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBvZmZzZXQgPSB1dlNjYWxlTWFwLm9mZnNldDtcblx0XHRcdFx0dmFyIHJlcGVhdCA9IHV2U2NhbGVNYXAucmVwZWF0O1xuXG5cdFx0XHRcdHVuaWZvcm1zLm9mZnNldFJlcGVhdC52YWx1ZS5zZXQoIG9mZnNldC54LCBvZmZzZXQueSwgcmVwZWF0LngsIHJlcGVhdC55ICk7XG5cblx0XHRcdH1cblxuXHRcdFx0dW5pZm9ybXMuZW52TWFwLnZhbHVlID0gbWF0ZXJpYWwuZW52TWFwO1xuXG5cdFx0XHQvLyBkb24ndCBmbGlwIEN1YmVUZXh0dXJlIGVudk1hcHMsIGZsaXAgZXZlcnl0aGluZyBlbHNlOlxuXHRcdFx0Ly8gIFdlYkdMUmVuZGVyVGFyZ2V0Q3ViZSB3aWxsIGJlIGZsaXBwZWQgZm9yIGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5XG5cdFx0XHQvLyAgV2ViR0xSZW5kZXJUYXJnZXRDdWJlLnRleHR1cmUgd2lsbCBiZSBmbGlwcGVkIGJlY2F1c2UgaXQncyBhIFRleHR1cmUgYW5kIE5PVCBhIEN1YmVUZXh0dXJlXG5cdFx0XHQvLyB0aGlzIGNoZWNrIG11c3QgYmUgaGFuZGxlZCBkaWZmZXJlbnRseSwgb3IgcmVtb3ZlZCBlbnRpcmVseSwgaWYgV2ViR0xSZW5kZXJUYXJnZXRDdWJlIHVzZXMgYSBDdWJlVGV4dHVyZSBpbiB0aGUgZnV0dXJlXG5cdFx0XHR1bmlmb3Jtcy5mbGlwRW52TWFwLnZhbHVlID0gKCAhICggbWF0ZXJpYWwuZW52TWFwICYmIG1hdGVyaWFsLmVudk1hcC5pc0N1YmVUZXh0dXJlICkgKSA/IDEgOiAtIDE7XG5cblx0XHRcdHVuaWZvcm1zLnJlZmxlY3Rpdml0eS52YWx1ZSA9IG1hdGVyaWFsLnJlZmxlY3Rpdml0eTtcblx0XHRcdHVuaWZvcm1zLnJlZnJhY3Rpb25SYXRpby52YWx1ZSA9IG1hdGVyaWFsLnJlZnJhY3Rpb25SYXRpbztcblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHJlZnJlc2hVbmlmb3Jtc0xpbmUoIHVuaWZvcm1zLCBtYXRlcmlhbCApIHtcblxuXHRcdFx0dW5pZm9ybXMuZGlmZnVzZS52YWx1ZSA9IG1hdGVyaWFsLmNvbG9yO1xuXHRcdFx0dW5pZm9ybXMub3BhY2l0eS52YWx1ZSA9IG1hdGVyaWFsLm9wYWNpdHk7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiByZWZyZXNoVW5pZm9ybXNEYXNoKCB1bmlmb3JtcywgbWF0ZXJpYWwgKSB7XG5cblx0XHRcdHVuaWZvcm1zLmRhc2hTaXplLnZhbHVlID0gbWF0ZXJpYWwuZGFzaFNpemU7XG5cdFx0XHR1bmlmb3Jtcy50b3RhbFNpemUudmFsdWUgPSBtYXRlcmlhbC5kYXNoU2l6ZSArIG1hdGVyaWFsLmdhcFNpemU7XG5cdFx0XHR1bmlmb3Jtcy5zY2FsZS52YWx1ZSA9IG1hdGVyaWFsLnNjYWxlO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gcmVmcmVzaFVuaWZvcm1zUG9pbnRzKCB1bmlmb3JtcywgbWF0ZXJpYWwgKSB7XG5cblx0XHRcdHVuaWZvcm1zLmRpZmZ1c2UudmFsdWUgPSBtYXRlcmlhbC5jb2xvcjtcblx0XHRcdHVuaWZvcm1zLm9wYWNpdHkudmFsdWUgPSBtYXRlcmlhbC5vcGFjaXR5O1xuXHRcdFx0dW5pZm9ybXMuc2l6ZS52YWx1ZSA9IG1hdGVyaWFsLnNpemUgKiBfcGl4ZWxSYXRpbztcblx0XHRcdHVuaWZvcm1zLnNjYWxlLnZhbHVlID0gX2hlaWdodCAqIDAuNTtcblxuXHRcdFx0dW5pZm9ybXMubWFwLnZhbHVlID0gbWF0ZXJpYWwubWFwO1xuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLm1hcCAhPT0gbnVsbCApIHtcblxuXHRcdFx0XHR2YXIgb2Zmc2V0ID0gbWF0ZXJpYWwubWFwLm9mZnNldDtcblx0XHRcdFx0dmFyIHJlcGVhdCA9IG1hdGVyaWFsLm1hcC5yZXBlYXQ7XG5cblx0XHRcdFx0dW5pZm9ybXMub2Zmc2V0UmVwZWF0LnZhbHVlLnNldCggb2Zmc2V0LngsIG9mZnNldC55LCByZXBlYXQueCwgcmVwZWF0LnkgKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gcmVmcmVzaFVuaWZvcm1zRm9nKCB1bmlmb3JtcywgZm9nICkge1xuXG5cdFx0XHR1bmlmb3Jtcy5mb2dDb2xvci52YWx1ZSA9IGZvZy5jb2xvcjtcblxuXHRcdFx0aWYgKCBmb2cuaXNGb2cgKSB7XG5cblx0XHRcdFx0dW5pZm9ybXMuZm9nTmVhci52YWx1ZSA9IGZvZy5uZWFyO1xuXHRcdFx0XHR1bmlmb3Jtcy5mb2dGYXIudmFsdWUgPSBmb2cuZmFyO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCBmb2cuaXNGb2dFeHAyICkge1xuXG5cdFx0XHRcdHVuaWZvcm1zLmZvZ0RlbnNpdHkudmFsdWUgPSBmb2cuZGVuc2l0eTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gcmVmcmVzaFVuaWZvcm1zTGFtYmVydCggdW5pZm9ybXMsIG1hdGVyaWFsICkge1xuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLmVtaXNzaXZlTWFwICkge1xuXG5cdFx0XHRcdHVuaWZvcm1zLmVtaXNzaXZlTWFwLnZhbHVlID0gbWF0ZXJpYWwuZW1pc3NpdmVNYXA7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHJlZnJlc2hVbmlmb3Jtc1Bob25nKCB1bmlmb3JtcywgbWF0ZXJpYWwgKSB7XG5cblx0XHRcdHVuaWZvcm1zLnNwZWN1bGFyLnZhbHVlID0gbWF0ZXJpYWwuc3BlY3VsYXI7XG5cdFx0XHR1bmlmb3Jtcy5zaGluaW5lc3MudmFsdWUgPSBNYXRoLm1heCggbWF0ZXJpYWwuc2hpbmluZXNzLCAxZS00ICk7IC8vIHRvIHByZXZlbnQgcG93KCAwLjAsIDAuMCApXG5cblx0XHRcdGlmICggbWF0ZXJpYWwuZW1pc3NpdmVNYXAgKSB7XG5cblx0XHRcdFx0dW5pZm9ybXMuZW1pc3NpdmVNYXAudmFsdWUgPSBtYXRlcmlhbC5lbWlzc2l2ZU1hcDtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLmJ1bXBNYXAgKSB7XG5cblx0XHRcdFx0dW5pZm9ybXMuYnVtcE1hcC52YWx1ZSA9IG1hdGVyaWFsLmJ1bXBNYXA7XG5cdFx0XHRcdHVuaWZvcm1zLmJ1bXBTY2FsZS52YWx1ZSA9IG1hdGVyaWFsLmJ1bXBTY2FsZTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLm5vcm1hbE1hcCApIHtcblxuXHRcdFx0XHR1bmlmb3Jtcy5ub3JtYWxNYXAudmFsdWUgPSBtYXRlcmlhbC5ub3JtYWxNYXA7XG5cdFx0XHRcdHVuaWZvcm1zLm5vcm1hbFNjYWxlLnZhbHVlLmNvcHkoIG1hdGVyaWFsLm5vcm1hbFNjYWxlICk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBtYXRlcmlhbC5kaXNwbGFjZW1lbnRNYXAgKSB7XG5cblx0XHRcdFx0dW5pZm9ybXMuZGlzcGxhY2VtZW50TWFwLnZhbHVlID0gbWF0ZXJpYWwuZGlzcGxhY2VtZW50TWFwO1xuXHRcdFx0XHR1bmlmb3Jtcy5kaXNwbGFjZW1lbnRTY2FsZS52YWx1ZSA9IG1hdGVyaWFsLmRpc3BsYWNlbWVudFNjYWxlO1xuXHRcdFx0XHR1bmlmb3Jtcy5kaXNwbGFjZW1lbnRCaWFzLnZhbHVlID0gbWF0ZXJpYWwuZGlzcGxhY2VtZW50QmlhcztcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gcmVmcmVzaFVuaWZvcm1zVG9vbiggdW5pZm9ybXMsIG1hdGVyaWFsICkge1xuXG5cdFx0XHRyZWZyZXNoVW5pZm9ybXNQaG9uZyggdW5pZm9ybXMsIG1hdGVyaWFsICk7XG5cblx0XHRcdGlmICggbWF0ZXJpYWwuZ3JhZGllbnRNYXAgKSB7XG5cblx0XHRcdFx0dW5pZm9ybXMuZ3JhZGllbnRNYXAudmFsdWUgPSBtYXRlcmlhbC5ncmFkaWVudE1hcDtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gcmVmcmVzaFVuaWZvcm1zU3RhbmRhcmQoIHVuaWZvcm1zLCBtYXRlcmlhbCApIHtcblxuXHRcdFx0dW5pZm9ybXMucm91Z2huZXNzLnZhbHVlID0gbWF0ZXJpYWwucm91Z2huZXNzO1xuXHRcdFx0dW5pZm9ybXMubWV0YWxuZXNzLnZhbHVlID0gbWF0ZXJpYWwubWV0YWxuZXNzO1xuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLnJvdWdobmVzc01hcCApIHtcblxuXHRcdFx0XHR1bmlmb3Jtcy5yb3VnaG5lc3NNYXAudmFsdWUgPSBtYXRlcmlhbC5yb3VnaG5lc3NNYXA7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBtYXRlcmlhbC5tZXRhbG5lc3NNYXAgKSB7XG5cblx0XHRcdFx0dW5pZm9ybXMubWV0YWxuZXNzTWFwLnZhbHVlID0gbWF0ZXJpYWwubWV0YWxuZXNzTWFwO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggbWF0ZXJpYWwuZW1pc3NpdmVNYXAgKSB7XG5cblx0XHRcdFx0dW5pZm9ybXMuZW1pc3NpdmVNYXAudmFsdWUgPSBtYXRlcmlhbC5lbWlzc2l2ZU1hcDtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLmJ1bXBNYXAgKSB7XG5cblx0XHRcdFx0dW5pZm9ybXMuYnVtcE1hcC52YWx1ZSA9IG1hdGVyaWFsLmJ1bXBNYXA7XG5cdFx0XHRcdHVuaWZvcm1zLmJ1bXBTY2FsZS52YWx1ZSA9IG1hdGVyaWFsLmJ1bXBTY2FsZTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLm5vcm1hbE1hcCApIHtcblxuXHRcdFx0XHR1bmlmb3Jtcy5ub3JtYWxNYXAudmFsdWUgPSBtYXRlcmlhbC5ub3JtYWxNYXA7XG5cdFx0XHRcdHVuaWZvcm1zLm5vcm1hbFNjYWxlLnZhbHVlLmNvcHkoIG1hdGVyaWFsLm5vcm1hbFNjYWxlICk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBtYXRlcmlhbC5kaXNwbGFjZW1lbnRNYXAgKSB7XG5cblx0XHRcdFx0dW5pZm9ybXMuZGlzcGxhY2VtZW50TWFwLnZhbHVlID0gbWF0ZXJpYWwuZGlzcGxhY2VtZW50TWFwO1xuXHRcdFx0XHR1bmlmb3Jtcy5kaXNwbGFjZW1lbnRTY2FsZS52YWx1ZSA9IG1hdGVyaWFsLmRpc3BsYWNlbWVudFNjYWxlO1xuXHRcdFx0XHR1bmlmb3Jtcy5kaXNwbGFjZW1lbnRCaWFzLnZhbHVlID0gbWF0ZXJpYWwuZGlzcGxhY2VtZW50QmlhcztcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLmVudk1hcCApIHtcblxuXHRcdFx0XHQvL3VuaWZvcm1zLmVudk1hcC52YWx1ZSA9IG1hdGVyaWFsLmVudk1hcDsgLy8gcGFydCBvZiB1bmlmb3JtcyBjb21tb25cblx0XHRcdFx0dW5pZm9ybXMuZW52TWFwSW50ZW5zaXR5LnZhbHVlID0gbWF0ZXJpYWwuZW52TWFwSW50ZW5zaXR5O1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiByZWZyZXNoVW5pZm9ybXNQaHlzaWNhbCggdW5pZm9ybXMsIG1hdGVyaWFsICkge1xuXG5cdFx0XHR1bmlmb3Jtcy5jbGVhckNvYXQudmFsdWUgPSBtYXRlcmlhbC5jbGVhckNvYXQ7XG5cdFx0XHR1bmlmb3Jtcy5jbGVhckNvYXRSb3VnaG5lc3MudmFsdWUgPSBtYXRlcmlhbC5jbGVhckNvYXRSb3VnaG5lc3M7XG5cblx0XHRcdHJlZnJlc2hVbmlmb3Jtc1N0YW5kYXJkKCB1bmlmb3JtcywgbWF0ZXJpYWwgKTtcblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHJlZnJlc2hVbmlmb3Jtc05vcm1hbCggdW5pZm9ybXMsIG1hdGVyaWFsICkge1xuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLmJ1bXBNYXAgKSB7XG5cblx0XHRcdFx0dW5pZm9ybXMuYnVtcE1hcC52YWx1ZSA9IG1hdGVyaWFsLmJ1bXBNYXA7XG5cdFx0XHRcdHVuaWZvcm1zLmJ1bXBTY2FsZS52YWx1ZSA9IG1hdGVyaWFsLmJ1bXBTY2FsZTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIG1hdGVyaWFsLm5vcm1hbE1hcCApIHtcblxuXHRcdFx0XHR1bmlmb3Jtcy5ub3JtYWxNYXAudmFsdWUgPSBtYXRlcmlhbC5ub3JtYWxNYXA7XG5cdFx0XHRcdHVuaWZvcm1zLm5vcm1hbFNjYWxlLnZhbHVlLmNvcHkoIG1hdGVyaWFsLm5vcm1hbFNjYWxlICk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBtYXRlcmlhbC5kaXNwbGFjZW1lbnRNYXAgKSB7XG5cblx0XHRcdFx0dW5pZm9ybXMuZGlzcGxhY2VtZW50TWFwLnZhbHVlID0gbWF0ZXJpYWwuZGlzcGxhY2VtZW50TWFwO1xuXHRcdFx0XHR1bmlmb3Jtcy5kaXNwbGFjZW1lbnRTY2FsZS52YWx1ZSA9IG1hdGVyaWFsLmRpc3BsYWNlbWVudFNjYWxlO1xuXHRcdFx0XHR1bmlmb3Jtcy5kaXNwbGFjZW1lbnRCaWFzLnZhbHVlID0gbWF0ZXJpYWwuZGlzcGxhY2VtZW50QmlhcztcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0Ly8gSWYgdW5pZm9ybXMgYXJlIG1hcmtlZCBhcyBjbGVhbiwgdGhleSBkb24ndCBuZWVkIHRvIGJlIGxvYWRlZCB0byB0aGUgR1BVLlxuXG5cdFx0ZnVuY3Rpb24gbWFya1VuaWZvcm1zTGlnaHRzTmVlZHNVcGRhdGUoIHVuaWZvcm1zLCB2YWx1ZSApIHtcblxuXHRcdFx0dW5pZm9ybXMuYW1iaWVudExpZ2h0Q29sb3IubmVlZHNVcGRhdGUgPSB2YWx1ZTtcblxuXHRcdFx0dW5pZm9ybXMuZGlyZWN0aW9uYWxMaWdodHMubmVlZHNVcGRhdGUgPSB2YWx1ZTtcblx0XHRcdHVuaWZvcm1zLnBvaW50TGlnaHRzLm5lZWRzVXBkYXRlID0gdmFsdWU7XG5cdFx0XHR1bmlmb3Jtcy5zcG90TGlnaHRzLm5lZWRzVXBkYXRlID0gdmFsdWU7XG5cdFx0XHR1bmlmb3Jtcy5yZWN0QXJlYUxpZ2h0cy5uZWVkc1VwZGF0ZSA9IHZhbHVlO1xuXHRcdFx0dW5pZm9ybXMuaGVtaXNwaGVyZUxpZ2h0cy5uZWVkc1VwZGF0ZSA9IHZhbHVlO1xuXG5cdFx0fVxuXG5cdFx0Ly8gTGlnaHRpbmdcblxuXHRcdGZ1bmN0aW9uIHNldHVwU2hhZG93cyggbGlnaHRzICkge1xuXG5cdFx0XHR2YXIgbGlnaHRTaGFkb3dzTGVuZ3RoID0gMDtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBsID0gbGlnaHRzLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0dmFyIGxpZ2h0ID0gbGlnaHRzWyBpIF07XG5cblx0XHRcdFx0aWYgKCBsaWdodC5jYXN0U2hhZG93ICkge1xuXG5cdFx0XHRcdFx0X2xpZ2h0cy5zaGFkb3dzWyBsaWdodFNoYWRvd3NMZW5ndGggKysgXSA9IGxpZ2h0O1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRfbGlnaHRzLnNoYWRvd3MubGVuZ3RoID0gbGlnaHRTaGFkb3dzTGVuZ3RoO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gc2V0dXBMaWdodHMoIGxpZ2h0cywgY2FtZXJhICkge1xuXG5cdFx0XHR2YXIgbCwgbGwsIGxpZ2h0LFxuXHRcdFx0XHRyID0gMCwgZyA9IDAsIGIgPSAwLFxuXHRcdFx0XHRjb2xvcixcblx0XHRcdFx0aW50ZW5zaXR5LFxuXHRcdFx0XHRkaXN0YW5jZSxcblx0XHRcdFx0c2hhZG93TWFwLFxuXG5cdFx0XHRcdHZpZXdNYXRyaXggPSBjYW1lcmEubWF0cml4V29ybGRJbnZlcnNlLFxuXG5cdFx0XHRkaXJlY3Rpb25hbExlbmd0aCA9IDAsXG5cdFx0XHRwb2ludExlbmd0aCA9IDAsXG5cdFx0XHRzcG90TGVuZ3RoID0gMCxcblx0XHRcdHJlY3RBcmVhTGVuZ3RoID0gMCxcblx0XHRcdGhlbWlMZW5ndGggPSAwO1xuXG5cdFx0XHRmb3IgKCBsID0gMCwgbGwgPSBsaWdodHMubGVuZ3RoOyBsIDwgbGw7IGwgKysgKSB7XG5cblx0XHRcdFx0bGlnaHQgPSBsaWdodHNbIGwgXTtcblxuXHRcdFx0XHRjb2xvciA9IGxpZ2h0LmNvbG9yO1xuXHRcdFx0XHRpbnRlbnNpdHkgPSBsaWdodC5pbnRlbnNpdHk7XG5cdFx0XHRcdGRpc3RhbmNlID0gbGlnaHQuZGlzdGFuY2U7XG5cblx0XHRcdFx0c2hhZG93TWFwID0gKCBsaWdodC5zaGFkb3cgJiYgbGlnaHQuc2hhZG93Lm1hcCApID8gbGlnaHQuc2hhZG93Lm1hcC50ZXh0dXJlIDogbnVsbDtcblxuXHRcdFx0XHRpZiAoIGxpZ2h0LmlzQW1iaWVudExpZ2h0ICkge1xuXG5cdFx0XHRcdFx0ciArPSBjb2xvci5yICogaW50ZW5zaXR5O1xuXHRcdFx0XHRcdGcgKz0gY29sb3IuZyAqIGludGVuc2l0eTtcblx0XHRcdFx0XHRiICs9IGNvbG9yLmIgKiBpbnRlbnNpdHk7XG5cblx0XHRcdFx0fSBlbHNlIGlmICggbGlnaHQuaXNEaXJlY3Rpb25hbExpZ2h0ICkge1xuXG5cdFx0XHRcdFx0dmFyIHVuaWZvcm1zID0gbGlnaHRDYWNoZS5nZXQoIGxpZ2h0ICk7XG5cblx0XHRcdFx0XHR1bmlmb3Jtcy5jb2xvci5jb3B5KCBsaWdodC5jb2xvciApLm11bHRpcGx5U2NhbGFyKCBsaWdodC5pbnRlbnNpdHkgKTtcblx0XHRcdFx0XHR1bmlmb3Jtcy5kaXJlY3Rpb24uc2V0RnJvbU1hdHJpeFBvc2l0aW9uKCBsaWdodC5tYXRyaXhXb3JsZCApO1xuXHRcdFx0XHRcdF92ZWN0b3IzLnNldEZyb21NYXRyaXhQb3NpdGlvbiggbGlnaHQudGFyZ2V0Lm1hdHJpeFdvcmxkICk7XG5cdFx0XHRcdFx0dW5pZm9ybXMuZGlyZWN0aW9uLnN1YiggX3ZlY3RvcjMgKTtcblx0XHRcdFx0XHR1bmlmb3Jtcy5kaXJlY3Rpb24udHJhbnNmb3JtRGlyZWN0aW9uKCB2aWV3TWF0cml4ICk7XG5cblx0XHRcdFx0XHR1bmlmb3Jtcy5zaGFkb3cgPSBsaWdodC5jYXN0U2hhZG93O1xuXG5cdFx0XHRcdFx0aWYgKCBsaWdodC5jYXN0U2hhZG93ICkge1xuXG5cdFx0XHRcdFx0XHR1bmlmb3Jtcy5zaGFkb3dCaWFzID0gbGlnaHQuc2hhZG93LmJpYXM7XG5cdFx0XHRcdFx0XHR1bmlmb3Jtcy5zaGFkb3dSYWRpdXMgPSBsaWdodC5zaGFkb3cucmFkaXVzO1xuXHRcdFx0XHRcdFx0dW5pZm9ybXMuc2hhZG93TWFwU2l6ZSA9IGxpZ2h0LnNoYWRvdy5tYXBTaXplO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0X2xpZ2h0cy5kaXJlY3Rpb25hbFNoYWRvd01hcFsgZGlyZWN0aW9uYWxMZW5ndGggXSA9IHNoYWRvd01hcDtcblx0XHRcdFx0XHRfbGlnaHRzLmRpcmVjdGlvbmFsU2hhZG93TWF0cml4WyBkaXJlY3Rpb25hbExlbmd0aCBdID0gbGlnaHQuc2hhZG93Lm1hdHJpeDtcblx0XHRcdFx0XHRfbGlnaHRzLmRpcmVjdGlvbmFsWyBkaXJlY3Rpb25hbExlbmd0aCArKyBdID0gdW5pZm9ybXM7XG5cblx0XHRcdFx0fSBlbHNlIGlmICggbGlnaHQuaXNTcG90TGlnaHQgKSB7XG5cblx0XHRcdFx0XHR2YXIgdW5pZm9ybXMgPSBsaWdodENhY2hlLmdldCggbGlnaHQgKTtcblxuXHRcdFx0XHRcdHVuaWZvcm1zLnBvc2l0aW9uLnNldEZyb21NYXRyaXhQb3NpdGlvbiggbGlnaHQubWF0cml4V29ybGQgKTtcblx0XHRcdFx0XHR1bmlmb3Jtcy5wb3NpdGlvbi5hcHBseU1hdHJpeDQoIHZpZXdNYXRyaXggKTtcblxuXHRcdFx0XHRcdHVuaWZvcm1zLmNvbG9yLmNvcHkoIGNvbG9yICkubXVsdGlwbHlTY2FsYXIoIGludGVuc2l0eSApO1xuXHRcdFx0XHRcdHVuaWZvcm1zLmRpc3RhbmNlID0gZGlzdGFuY2U7XG5cblx0XHRcdFx0XHR1bmlmb3Jtcy5kaXJlY3Rpb24uc2V0RnJvbU1hdHJpeFBvc2l0aW9uKCBsaWdodC5tYXRyaXhXb3JsZCApO1xuXHRcdFx0XHRcdF92ZWN0b3IzLnNldEZyb21NYXRyaXhQb3NpdGlvbiggbGlnaHQudGFyZ2V0Lm1hdHJpeFdvcmxkICk7XG5cdFx0XHRcdFx0dW5pZm9ybXMuZGlyZWN0aW9uLnN1YiggX3ZlY3RvcjMgKTtcblx0XHRcdFx0XHR1bmlmb3Jtcy5kaXJlY3Rpb24udHJhbnNmb3JtRGlyZWN0aW9uKCB2aWV3TWF0cml4ICk7XG5cblx0XHRcdFx0XHR1bmlmb3Jtcy5jb25lQ29zID0gTWF0aC5jb3MoIGxpZ2h0LmFuZ2xlICk7XG5cdFx0XHRcdFx0dW5pZm9ybXMucGVudW1icmFDb3MgPSBNYXRoLmNvcyggbGlnaHQuYW5nbGUgKiAoIDEgLSBsaWdodC5wZW51bWJyYSApICk7XG5cdFx0XHRcdFx0dW5pZm9ybXMuZGVjYXkgPSAoIGxpZ2h0LmRpc3RhbmNlID09PSAwICkgPyAwLjAgOiBsaWdodC5kZWNheTtcblxuXHRcdFx0XHRcdHVuaWZvcm1zLnNoYWRvdyA9IGxpZ2h0LmNhc3RTaGFkb3c7XG5cblx0XHRcdFx0XHRpZiAoIGxpZ2h0LmNhc3RTaGFkb3cgKSB7XG5cblx0XHRcdFx0XHRcdHVuaWZvcm1zLnNoYWRvd0JpYXMgPSBsaWdodC5zaGFkb3cuYmlhcztcblx0XHRcdFx0XHRcdHVuaWZvcm1zLnNoYWRvd1JhZGl1cyA9IGxpZ2h0LnNoYWRvdy5yYWRpdXM7XG5cdFx0XHRcdFx0XHR1bmlmb3Jtcy5zaGFkb3dNYXBTaXplID0gbGlnaHQuc2hhZG93Lm1hcFNpemU7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRfbGlnaHRzLnNwb3RTaGFkb3dNYXBbIHNwb3RMZW5ndGggXSA9IHNoYWRvd01hcDtcblx0XHRcdFx0XHRfbGlnaHRzLnNwb3RTaGFkb3dNYXRyaXhbIHNwb3RMZW5ndGggXSA9IGxpZ2h0LnNoYWRvdy5tYXRyaXg7XG5cdFx0XHRcdFx0X2xpZ2h0cy5zcG90WyBzcG90TGVuZ3RoICsrIF0gPSB1bmlmb3JtcztcblxuXHRcdFx0XHR9IGVsc2UgaWYgKCBsaWdodC5pc1JlY3RBcmVhTGlnaHQgKSB7XG5cblx0XHRcdFx0XHR2YXIgdW5pZm9ybXMgPSBsaWdodENhY2hlLmdldCggbGlnaHQgKTtcblxuXHRcdFx0XHRcdC8vIChhKSBpbnRlbnNpdHkgY29udHJvbHMgaXJyYWRpYW5jZSBvZiBlbnRpcmUgbGlnaHRcblx0XHRcdFx0XHR1bmlmb3Jtcy5jb2xvclxuXHRcdFx0XHRcdFx0LmNvcHkoIGNvbG9yIClcblx0XHRcdFx0XHRcdC5tdWx0aXBseVNjYWxhciggaW50ZW5zaXR5IC8gKCBsaWdodC53aWR0aCAqIGxpZ2h0LmhlaWdodCApICk7XG5cblx0XHRcdFx0XHQvLyAoYikgaW50ZW5zaXR5IGNvbnRyb2xzIHRoZSByYWRpYW5jZSBwZXIgbGlnaHQgYXJlYVxuXHRcdFx0XHRcdC8vIHVuaWZvcm1zLmNvbG9yLmNvcHkoIGNvbG9yICkubXVsdGlwbHlTY2FsYXIoIGludGVuc2l0eSApO1xuXG5cdFx0XHRcdFx0dW5pZm9ybXMucG9zaXRpb24uc2V0RnJvbU1hdHJpeFBvc2l0aW9uKCBsaWdodC5tYXRyaXhXb3JsZCApO1xuXHRcdFx0XHRcdHVuaWZvcm1zLnBvc2l0aW9uLmFwcGx5TWF0cml4NCggdmlld01hdHJpeCApO1xuXG5cdFx0XHRcdFx0Ly8gZXh0cmFjdCBsb2NhbCByb3RhdGlvbiBvZiBsaWdodCB0byBkZXJpdmUgd2lkdGgvaGVpZ2h0IGhhbGYgdmVjdG9yc1xuXHRcdFx0XHRcdF9tYXRyaXg0Mi5pZGVudGl0eSgpO1xuXHRcdFx0XHRcdF9tYXRyaXg0LmNvcHkoIGxpZ2h0Lm1hdHJpeFdvcmxkICk7XG5cdFx0XHRcdFx0X21hdHJpeDQucHJlbXVsdGlwbHkoIHZpZXdNYXRyaXggKTtcblx0XHRcdFx0XHRfbWF0cml4NDIuZXh0cmFjdFJvdGF0aW9uKCBfbWF0cml4NCApO1xuXG5cdFx0XHRcdFx0dW5pZm9ybXMuaGFsZldpZHRoLnNldCggbGlnaHQud2lkdGggKiAwLjUsICAgICAgICAgICAgICAgIDAuMCwgMC4wICk7XG5cdFx0XHRcdFx0dW5pZm9ybXMuaGFsZkhlaWdodC5zZXQoICAgICAgICAgICAgICAwLjAsIGxpZ2h0LmhlaWdodCAqIDAuNSwgMC4wICk7XG5cblx0XHRcdFx0XHR1bmlmb3Jtcy5oYWxmV2lkdGguYXBwbHlNYXRyaXg0KCBfbWF0cml4NDIgKTtcblx0XHRcdFx0XHR1bmlmb3Jtcy5oYWxmSGVpZ2h0LmFwcGx5TWF0cml4NCggX21hdHJpeDQyICk7XG5cblx0XHRcdFx0XHQvLyBUT0RPIChhYmVsbmF0aW9uKTogUmVjdEFyZWFMaWdodCBkaXN0YW5jZT9cblx0XHRcdFx0XHQvLyB1bmlmb3Jtcy5kaXN0YW5jZSA9IGRpc3RhbmNlO1xuXG5cdFx0XHRcdFx0X2xpZ2h0cy5yZWN0QXJlYVsgcmVjdEFyZWFMZW5ndGggKysgXSA9IHVuaWZvcm1zO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAoIGxpZ2h0LmlzUG9pbnRMaWdodCApIHtcblxuXHRcdFx0XHRcdHZhciB1bmlmb3JtcyA9IGxpZ2h0Q2FjaGUuZ2V0KCBsaWdodCApO1xuXG5cdFx0XHRcdFx0dW5pZm9ybXMucG9zaXRpb24uc2V0RnJvbU1hdHJpeFBvc2l0aW9uKCBsaWdodC5tYXRyaXhXb3JsZCApO1xuXHRcdFx0XHRcdHVuaWZvcm1zLnBvc2l0aW9uLmFwcGx5TWF0cml4NCggdmlld01hdHJpeCApO1xuXG5cdFx0XHRcdFx0dW5pZm9ybXMuY29sb3IuY29weSggbGlnaHQuY29sb3IgKS5tdWx0aXBseVNjYWxhciggbGlnaHQuaW50ZW5zaXR5ICk7XG5cdFx0XHRcdFx0dW5pZm9ybXMuZGlzdGFuY2UgPSBsaWdodC5kaXN0YW5jZTtcblx0XHRcdFx0XHR1bmlmb3Jtcy5kZWNheSA9ICggbGlnaHQuZGlzdGFuY2UgPT09IDAgKSA/IDAuMCA6IGxpZ2h0LmRlY2F5O1xuXG5cdFx0XHRcdFx0dW5pZm9ybXMuc2hhZG93ID0gbGlnaHQuY2FzdFNoYWRvdztcblxuXHRcdFx0XHRcdGlmICggbGlnaHQuY2FzdFNoYWRvdyApIHtcblxuXHRcdFx0XHRcdFx0dW5pZm9ybXMuc2hhZG93QmlhcyA9IGxpZ2h0LnNoYWRvdy5iaWFzO1xuXHRcdFx0XHRcdFx0dW5pZm9ybXMuc2hhZG93UmFkaXVzID0gbGlnaHQuc2hhZG93LnJhZGl1cztcblx0XHRcdFx0XHRcdHVuaWZvcm1zLnNoYWRvd01hcFNpemUgPSBsaWdodC5zaGFkb3cubWFwU2l6ZTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdF9saWdodHMucG9pbnRTaGFkb3dNYXBbIHBvaW50TGVuZ3RoIF0gPSBzaGFkb3dNYXA7XG5cblx0XHRcdFx0XHRpZiAoIF9saWdodHMucG9pbnRTaGFkb3dNYXRyaXhbIHBvaW50TGVuZ3RoIF0gPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdFx0X2xpZ2h0cy5wb2ludFNoYWRvd01hdHJpeFsgcG9pbnRMZW5ndGggXSA9IG5ldyBNYXRyaXg0KCk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHQvLyBmb3IgcG9pbnQgbGlnaHRzIHdlIHNldCB0aGUgc2hhZG93IG1hdHJpeCB0byBiZSBhIHRyYW5zbGF0aW9uLW9ubHkgbWF0cml4XG5cdFx0XHRcdFx0Ly8gZXF1YWwgdG8gaW52ZXJzZSBvZiB0aGUgbGlnaHQncyBwb3NpdGlvblxuXHRcdFx0XHRcdF92ZWN0b3IzLnNldEZyb21NYXRyaXhQb3NpdGlvbiggbGlnaHQubWF0cml4V29ybGQgKS5uZWdhdGUoKTtcblx0XHRcdFx0XHRfbGlnaHRzLnBvaW50U2hhZG93TWF0cml4WyBwb2ludExlbmd0aCBdLmlkZW50aXR5KCkuc2V0UG9zaXRpb24oIF92ZWN0b3IzICk7XG5cblx0XHRcdFx0XHRfbGlnaHRzLnBvaW50WyBwb2ludExlbmd0aCArKyBdID0gdW5pZm9ybXM7XG5cblx0XHRcdFx0fSBlbHNlIGlmICggbGlnaHQuaXNIZW1pc3BoZXJlTGlnaHQgKSB7XG5cblx0XHRcdFx0XHR2YXIgdW5pZm9ybXMgPSBsaWdodENhY2hlLmdldCggbGlnaHQgKTtcblxuXHRcdFx0XHRcdHVuaWZvcm1zLmRpcmVjdGlvbi5zZXRGcm9tTWF0cml4UG9zaXRpb24oIGxpZ2h0Lm1hdHJpeFdvcmxkICk7XG5cdFx0XHRcdFx0dW5pZm9ybXMuZGlyZWN0aW9uLnRyYW5zZm9ybURpcmVjdGlvbiggdmlld01hdHJpeCApO1xuXHRcdFx0XHRcdHVuaWZvcm1zLmRpcmVjdGlvbi5ub3JtYWxpemUoKTtcblxuXHRcdFx0XHRcdHVuaWZvcm1zLnNreUNvbG9yLmNvcHkoIGxpZ2h0LmNvbG9yICkubXVsdGlwbHlTY2FsYXIoIGludGVuc2l0eSApO1xuXHRcdFx0XHRcdHVuaWZvcm1zLmdyb3VuZENvbG9yLmNvcHkoIGxpZ2h0Lmdyb3VuZENvbG9yICkubXVsdGlwbHlTY2FsYXIoIGludGVuc2l0eSApO1xuXG5cdFx0XHRcdFx0X2xpZ2h0cy5oZW1pWyBoZW1pTGVuZ3RoICsrIF0gPSB1bmlmb3JtcztcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0X2xpZ2h0cy5hbWJpZW50WyAwIF0gPSByO1xuXHRcdFx0X2xpZ2h0cy5hbWJpZW50WyAxIF0gPSBnO1xuXHRcdFx0X2xpZ2h0cy5hbWJpZW50WyAyIF0gPSBiO1xuXG5cdFx0XHRfbGlnaHRzLmRpcmVjdGlvbmFsLmxlbmd0aCA9IGRpcmVjdGlvbmFsTGVuZ3RoO1xuXHRcdFx0X2xpZ2h0cy5zcG90Lmxlbmd0aCA9IHNwb3RMZW5ndGg7XG5cdFx0XHRfbGlnaHRzLnJlY3RBcmVhLmxlbmd0aCA9IHJlY3RBcmVhTGVuZ3RoO1xuXHRcdFx0X2xpZ2h0cy5wb2ludC5sZW5ndGggPSBwb2ludExlbmd0aDtcblx0XHRcdF9saWdodHMuaGVtaS5sZW5ndGggPSBoZW1pTGVuZ3RoO1xuXG5cdFx0XHQvLyBUT0RPIChzYW0tZy1zdGVlbCkgd2h5IGFyZW4ndCB3ZSB1c2luZyBqb2luXG5cdFx0XHRfbGlnaHRzLmhhc2ggPSBkaXJlY3Rpb25hbExlbmd0aCArICcsJyArIHBvaW50TGVuZ3RoICsgJywnICsgc3BvdExlbmd0aCArICcsJyArIHJlY3RBcmVhTGVuZ3RoICsgJywnICsgaGVtaUxlbmd0aCArICcsJyArIF9saWdodHMuc2hhZG93cy5sZW5ndGg7XG5cblx0XHR9XG5cblx0XHQvLyBHTCBzdGF0ZSBzZXR0aW5nXG5cblx0XHR0aGlzLnNldEZhY2VDdWxsaW5nID0gZnVuY3Rpb24gKCBjdWxsRmFjZSwgZnJvbnRGYWNlRGlyZWN0aW9uICkge1xuXG5cdFx0XHRzdGF0ZS5zZXRDdWxsRmFjZSggY3VsbEZhY2UgKTtcblx0XHRcdHN0YXRlLnNldEZsaXBTaWRlZCggZnJvbnRGYWNlRGlyZWN0aW9uID09PSBGcm9udEZhY2VEaXJlY3Rpb25DVyApO1xuXG5cdFx0fTtcblxuXHRcdC8vIFRleHR1cmVzXG5cblx0XHRmdW5jdGlvbiBhbGxvY1RleHR1cmVVbml0KCkge1xuXG5cdFx0XHR2YXIgdGV4dHVyZVVuaXQgPSBfdXNlZFRleHR1cmVVbml0cztcblxuXHRcdFx0aWYgKCB0ZXh0dXJlVW5pdCA+PSBjYXBhYmlsaXRpZXMubWF4VGV4dHVyZXMgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnV2ViR0xSZW5kZXJlcjogdHJ5aW5nIHRvIHVzZSAnICsgdGV4dHVyZVVuaXQgKyAnIHRleHR1cmUgdW5pdHMgd2hpbGUgdGhpcyBHUFUgc3VwcG9ydHMgb25seSAnICsgY2FwYWJpbGl0aWVzLm1heFRleHR1cmVzICk7XG5cblx0XHRcdH1cblxuXHRcdFx0X3VzZWRUZXh0dXJlVW5pdHMgKz0gMTtcblxuXHRcdFx0cmV0dXJuIHRleHR1cmVVbml0O1xuXG5cdFx0fVxuXG5cdFx0dGhpcy5hbGxvY1RleHR1cmVVbml0ID0gYWxsb2NUZXh0dXJlVW5pdDtcblxuXHRcdC8vIHRoaXMuc2V0VGV4dHVyZTJEID0gc2V0VGV4dHVyZTJEO1xuXHRcdHRoaXMuc2V0VGV4dHVyZTJEID0gKCBmdW5jdGlvbigpIHtcblxuXHRcdFx0dmFyIHdhcm5lZCA9IGZhbHNlO1xuXG5cdFx0XHQvLyBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eTogcGVlbCB0ZXh0dXJlLnRleHR1cmVcblx0XHRcdHJldHVybiBmdW5jdGlvbiBzZXRUZXh0dXJlMkQoIHRleHR1cmUsIHNsb3QgKSB7XG5cblx0XHRcdFx0aWYgKCB0ZXh0dXJlICYmIHRleHR1cmUuaXNXZWJHTFJlbmRlclRhcmdldCApIHtcblxuXHRcdFx0XHRcdGlmICggISB3YXJuZWQgKSB7XG5cblx0XHRcdFx0XHRcdGNvbnNvbGUud2FybiggXCJUSFJFRS5XZWJHTFJlbmRlcmVyLnNldFRleHR1cmUyRDogZG9uJ3QgdXNlIHJlbmRlciB0YXJnZXRzIGFzIHRleHR1cmVzLiBVc2UgdGhlaXIgLnRleHR1cmUgcHJvcGVydHkgaW5zdGVhZC5cIiApO1xuXHRcdFx0XHRcdFx0d2FybmVkID0gdHJ1ZTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdHRleHR1cmUgPSB0ZXh0dXJlLnRleHR1cmU7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHRleHR1cmVzLnNldFRleHR1cmUyRCggdGV4dHVyZSwgc2xvdCApO1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpICk7XG5cblx0XHR0aGlzLnNldFRleHR1cmUgPSAoIGZ1bmN0aW9uKCkge1xuXG5cdFx0XHR2YXIgd2FybmVkID0gZmFsc2U7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBzZXRUZXh0dXJlKCB0ZXh0dXJlLCBzbG90ICkge1xuXG5cdFx0XHRcdGlmICggISB3YXJuZWQgKSB7XG5cblx0XHRcdFx0XHRjb25zb2xlLndhcm4oIFwiVEhSRUUuV2ViR0xSZW5kZXJlcjogLnNldFRleHR1cmUgaXMgZGVwcmVjYXRlZCwgdXNlIHNldFRleHR1cmUyRCBpbnN0ZWFkLlwiICk7XG5cdFx0XHRcdFx0d2FybmVkID0gdHJ1ZTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0dGV4dHVyZXMuc2V0VGV4dHVyZTJEKCB0ZXh0dXJlLCBzbG90ICk7XG5cblx0XHRcdH07XG5cblx0XHR9KCkgKTtcblxuXHRcdHRoaXMuc2V0VGV4dHVyZUN1YmUgPSAoIGZ1bmN0aW9uKCkge1xuXG5cdFx0XHR2YXIgd2FybmVkID0gZmFsc2U7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBzZXRUZXh0dXJlQ3ViZSggdGV4dHVyZSwgc2xvdCApIHtcblxuXHRcdFx0XHQvLyBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eTogcGVlbCB0ZXh0dXJlLnRleHR1cmVcblx0XHRcdFx0aWYgKCB0ZXh0dXJlICYmIHRleHR1cmUuaXNXZWJHTFJlbmRlclRhcmdldEN1YmUgKSB7XG5cblx0XHRcdFx0XHRpZiAoICEgd2FybmVkICkge1xuXG5cdFx0XHRcdFx0XHRjb25zb2xlLndhcm4oIFwiVEhSRUUuV2ViR0xSZW5kZXJlci5zZXRUZXh0dXJlQ3ViZTogZG9uJ3QgdXNlIGN1YmUgcmVuZGVyIHRhcmdldHMgYXMgdGV4dHVyZXMuIFVzZSB0aGVpciAudGV4dHVyZSBwcm9wZXJ0eSBpbnN0ZWFkLlwiICk7XG5cdFx0XHRcdFx0XHR3YXJuZWQgPSB0cnVlO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0dGV4dHVyZSA9IHRleHR1cmUudGV4dHVyZTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gY3VycmVudGx5IHJlbHlpbmcgb24gdGhlIGZhY3QgdGhhdCBXZWJHTFJlbmRlclRhcmdldEN1YmUudGV4dHVyZSBpcyBhIFRleHR1cmUgYW5kIE5PVCBhIEN1YmVUZXh0dXJlXG5cdFx0XHRcdC8vIFRPRE86IHVuaWZ5IHRoZXNlIGNvZGUgcGF0aHNcblx0XHRcdFx0aWYgKCAoIHRleHR1cmUgJiYgdGV4dHVyZS5pc0N1YmVUZXh0dXJlICkgfHxcblx0XHRcdFx0XHQoIEFycmF5LmlzQXJyYXkoIHRleHR1cmUuaW1hZ2UgKSAmJiB0ZXh0dXJlLmltYWdlLmxlbmd0aCA9PT0gNiApICkge1xuXG5cdFx0XHRcdFx0Ly8gQ29tcHJlc3NlZFRleHR1cmUgY2FuIGhhdmUgQXJyYXkgaW4gaW1hZ2UgOi9cblxuXHRcdFx0XHRcdC8vIHRoaXMgZnVuY3Rpb24gYWxvbmUgc2hvdWxkIHRha2UgY2FyZSBvZiBjdWJlIHRleHR1cmVzXG5cdFx0XHRcdFx0dGV4dHVyZXMuc2V0VGV4dHVyZUN1YmUoIHRleHR1cmUsIHNsb3QgKTtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0Ly8gYXNzdW1lZDogdGV4dHVyZSBwcm9wZXJ0eSBvZiBUSFJFRS5XZWJHTFJlbmRlclRhcmdldEN1YmVcblxuXHRcdFx0XHRcdHRleHR1cmVzLnNldFRleHR1cmVDdWJlRHluYW1pYyggdGV4dHVyZSwgc2xvdCApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fTtcblxuXHRcdH0oKSApO1xuXG5cdFx0dGhpcy5nZXRDdXJyZW50UmVuZGVyVGFyZ2V0ID0gZnVuY3Rpb24oKSB7XG5cblx0XHRcdHJldHVybiBfY3VycmVudFJlbmRlclRhcmdldDtcblxuXHRcdH07XG5cblx0XHR0aGlzLnNldFJlbmRlclRhcmdldCA9IGZ1bmN0aW9uICggcmVuZGVyVGFyZ2V0ICkge1xuXG5cdFx0XHRfY3VycmVudFJlbmRlclRhcmdldCA9IHJlbmRlclRhcmdldDtcblxuXHRcdFx0aWYgKCByZW5kZXJUYXJnZXQgJiYgcHJvcGVydGllcy5nZXQoIHJlbmRlclRhcmdldCApLl9fd2ViZ2xGcmFtZWJ1ZmZlciA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdHRleHR1cmVzLnNldHVwUmVuZGVyVGFyZ2V0KCByZW5kZXJUYXJnZXQgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgaXNDdWJlID0gKCByZW5kZXJUYXJnZXQgJiYgcmVuZGVyVGFyZ2V0LmlzV2ViR0xSZW5kZXJUYXJnZXRDdWJlICk7XG5cdFx0XHR2YXIgZnJhbWVidWZmZXI7XG5cblx0XHRcdGlmICggcmVuZGVyVGFyZ2V0ICkge1xuXG5cdFx0XHRcdHZhciByZW5kZXJUYXJnZXRQcm9wZXJ0aWVzID0gcHJvcGVydGllcy5nZXQoIHJlbmRlclRhcmdldCApO1xuXG5cdFx0XHRcdGlmICggaXNDdWJlICkge1xuXG5cdFx0XHRcdFx0ZnJhbWVidWZmZXIgPSByZW5kZXJUYXJnZXRQcm9wZXJ0aWVzLl9fd2ViZ2xGcmFtZWJ1ZmZlclsgcmVuZGVyVGFyZ2V0LmFjdGl2ZUN1YmVGYWNlIF07XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdGZyYW1lYnVmZmVyID0gcmVuZGVyVGFyZ2V0UHJvcGVydGllcy5fX3dlYmdsRnJhbWVidWZmZXI7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdF9jdXJyZW50U2Npc3Nvci5jb3B5KCByZW5kZXJUYXJnZXQuc2Npc3NvciApO1xuXHRcdFx0XHRfY3VycmVudFNjaXNzb3JUZXN0ID0gcmVuZGVyVGFyZ2V0LnNjaXNzb3JUZXN0O1xuXG5cdFx0XHRcdF9jdXJyZW50Vmlld3BvcnQuY29weSggcmVuZGVyVGFyZ2V0LnZpZXdwb3J0ICk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0ZnJhbWVidWZmZXIgPSBudWxsO1xuXG5cdFx0XHRcdF9jdXJyZW50U2Npc3Nvci5jb3B5KCBfc2Npc3NvciApLm11bHRpcGx5U2NhbGFyKCBfcGl4ZWxSYXRpbyApO1xuXHRcdFx0XHRfY3VycmVudFNjaXNzb3JUZXN0ID0gX3NjaXNzb3JUZXN0O1xuXG5cdFx0XHRcdF9jdXJyZW50Vmlld3BvcnQuY29weSggX3ZpZXdwb3J0ICkubXVsdGlwbHlTY2FsYXIoIF9waXhlbFJhdGlvICk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBfY3VycmVudEZyYW1lYnVmZmVyICE9PSBmcmFtZWJ1ZmZlciApIHtcblxuXHRcdFx0XHRfZ2wuYmluZEZyYW1lYnVmZmVyKCBfZ2wuRlJBTUVCVUZGRVIsIGZyYW1lYnVmZmVyICk7XG5cdFx0XHRcdF9jdXJyZW50RnJhbWVidWZmZXIgPSBmcmFtZWJ1ZmZlcjtcblxuXHRcdFx0fVxuXG5cdFx0XHRzdGF0ZS5zY2lzc29yKCBfY3VycmVudFNjaXNzb3IgKTtcblx0XHRcdHN0YXRlLnNldFNjaXNzb3JUZXN0KCBfY3VycmVudFNjaXNzb3JUZXN0ICk7XG5cblx0XHRcdHN0YXRlLnZpZXdwb3J0KCBfY3VycmVudFZpZXdwb3J0ICk7XG5cblx0XHRcdGlmICggaXNDdWJlICkge1xuXG5cdFx0XHRcdHZhciB0ZXh0dXJlUHJvcGVydGllcyA9IHByb3BlcnRpZXMuZ2V0KCByZW5kZXJUYXJnZXQudGV4dHVyZSApO1xuXHRcdFx0XHRfZ2wuZnJhbWVidWZmZXJUZXh0dXJlMkQoIF9nbC5GUkFNRUJVRkZFUiwgX2dsLkNPTE9SX0FUVEFDSE1FTlQwLCBfZ2wuVEVYVFVSRV9DVUJFX01BUF9QT1NJVElWRV9YICsgcmVuZGVyVGFyZ2V0LmFjdGl2ZUN1YmVGYWNlLCB0ZXh0dXJlUHJvcGVydGllcy5fX3dlYmdsVGV4dHVyZSwgcmVuZGVyVGFyZ2V0LmFjdGl2ZU1pcE1hcExldmVsICk7XG5cblx0XHRcdH1cblxuXHRcdH07XG5cblx0XHR0aGlzLnJlYWRSZW5kZXJUYXJnZXRQaXhlbHMgPSBmdW5jdGlvbiAoIHJlbmRlclRhcmdldCwgeCwgeSwgd2lkdGgsIGhlaWdodCwgYnVmZmVyICkge1xuXG5cdFx0XHRpZiAoICggcmVuZGVyVGFyZ2V0ICYmIHJlbmRlclRhcmdldC5pc1dlYkdMUmVuZGVyVGFyZ2V0ICkgPT09IGZhbHNlICkge1xuXG5cdFx0XHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5XZWJHTFJlbmRlcmVyLnJlYWRSZW5kZXJUYXJnZXRQaXhlbHM6IHJlbmRlclRhcmdldCBpcyBub3QgVEhSRUUuV2ViR0xSZW5kZXJUYXJnZXQuJyApO1xuXHRcdFx0XHRyZXR1cm47XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIGZyYW1lYnVmZmVyID0gcHJvcGVydGllcy5nZXQoIHJlbmRlclRhcmdldCApLl9fd2ViZ2xGcmFtZWJ1ZmZlcjtcblxuXHRcdFx0aWYgKCBmcmFtZWJ1ZmZlciApIHtcblxuXHRcdFx0XHR2YXIgcmVzdG9yZSA9IGZhbHNlO1xuXG5cdFx0XHRcdGlmICggZnJhbWVidWZmZXIgIT09IF9jdXJyZW50RnJhbWVidWZmZXIgKSB7XG5cblx0XHRcdFx0XHRfZ2wuYmluZEZyYW1lYnVmZmVyKCBfZ2wuRlJBTUVCVUZGRVIsIGZyYW1lYnVmZmVyICk7XG5cblx0XHRcdFx0XHRyZXN0b3JlID0gdHJ1ZTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0dHJ5IHtcblxuXHRcdFx0XHRcdHZhciB0ZXh0dXJlID0gcmVuZGVyVGFyZ2V0LnRleHR1cmU7XG5cdFx0XHRcdFx0dmFyIHRleHR1cmVGb3JtYXQgPSB0ZXh0dXJlLmZvcm1hdDtcblx0XHRcdFx0XHR2YXIgdGV4dHVyZVR5cGUgPSB0ZXh0dXJlLnR5cGU7XG5cblx0XHRcdFx0XHRpZiAoIHRleHR1cmVGb3JtYXQgIT09IFJHQkFGb3JtYXQgJiYgcGFyYW1UaHJlZVRvR0woIHRleHR1cmVGb3JtYXQgKSAhPT0gX2dsLmdldFBhcmFtZXRlciggX2dsLklNUExFTUVOVEFUSU9OX0NPTE9SX1JFQURfRk9STUFUICkgKSB7XG5cblx0XHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5XZWJHTFJlbmRlcmVyLnJlYWRSZW5kZXJUYXJnZXRQaXhlbHM6IHJlbmRlclRhcmdldCBpcyBub3QgaW4gUkdCQSBvciBpbXBsZW1lbnRhdGlvbiBkZWZpbmVkIGZvcm1hdC4nICk7XG5cdFx0XHRcdFx0XHRyZXR1cm47XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRpZiAoIHRleHR1cmVUeXBlICE9PSBVbnNpZ25lZEJ5dGVUeXBlICYmIHBhcmFtVGhyZWVUb0dMKCB0ZXh0dXJlVHlwZSApICE9PSBfZ2wuZ2V0UGFyYW1ldGVyKCBfZ2wuSU1QTEVNRU5UQVRJT05fQ09MT1JfUkVBRF9UWVBFICkgJiYgLy8gSUUxMSwgRWRnZSBhbmQgQ2hyb21lIE1hYyA8IDUyICgjOTUxMylcblx0XHRcdFx0XHRcdCEgKCB0ZXh0dXJlVHlwZSA9PT0gRmxvYXRUeXBlICYmICggZXh0ZW5zaW9ucy5nZXQoICdPRVNfdGV4dHVyZV9mbG9hdCcgKSB8fCBleHRlbnNpb25zLmdldCggJ1dFQkdMX2NvbG9yX2J1ZmZlcl9mbG9hdCcgKSApICkgJiYgLy8gQ2hyb21lIE1hYyA+PSA1MiBhbmQgRmlyZWZveFxuXHRcdFx0XHRcdFx0ISAoIHRleHR1cmVUeXBlID09PSBIYWxmRmxvYXRUeXBlICYmIGV4dGVuc2lvbnMuZ2V0KCAnRVhUX2NvbG9yX2J1ZmZlcl9oYWxmX2Zsb2F0JyApICkgKSB7XG5cblx0XHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5XZWJHTFJlbmRlcmVyLnJlYWRSZW5kZXJUYXJnZXRQaXhlbHM6IHJlbmRlclRhcmdldCBpcyBub3QgaW4gVW5zaWduZWRCeXRlVHlwZSBvciBpbXBsZW1lbnRhdGlvbiBkZWZpbmVkIHR5cGUuJyApO1xuXHRcdFx0XHRcdFx0cmV0dXJuO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0aWYgKCBfZ2wuY2hlY2tGcmFtZWJ1ZmZlclN0YXR1cyggX2dsLkZSQU1FQlVGRkVSICkgPT09IF9nbC5GUkFNRUJVRkZFUl9DT01QTEVURSApIHtcblxuXHRcdFx0XHRcdFx0Ly8gdGhlIGZvbGxvd2luZyBpZiBzdGF0ZW1lbnQgZW5zdXJlcyB2YWxpZCByZWFkIHJlcXVlc3RzIChubyBvdXQtb2YtYm91bmRzIHBpeGVscywgc2VlICM4NjA0KVxuXG5cdFx0XHRcdFx0XHRpZiAoICggeCA+PSAwICYmIHggPD0gKCByZW5kZXJUYXJnZXQud2lkdGggLSB3aWR0aCApICkgJiYgKCB5ID49IDAgJiYgeSA8PSAoIHJlbmRlclRhcmdldC5oZWlnaHQgLSBoZWlnaHQgKSApICkge1xuXG5cdFx0XHRcdFx0XHRcdF9nbC5yZWFkUGl4ZWxzKCB4LCB5LCB3aWR0aCwgaGVpZ2h0LCBwYXJhbVRocmVlVG9HTCggdGV4dHVyZUZvcm1hdCApLCBwYXJhbVRocmVlVG9HTCggdGV4dHVyZVR5cGUgKSwgYnVmZmVyICk7XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5XZWJHTFJlbmRlcmVyLnJlYWRSZW5kZXJUYXJnZXRQaXhlbHM6IHJlYWRQaXhlbHMgZnJvbSByZW5kZXJUYXJnZXQgZmFpbGVkLiBGcmFtZWJ1ZmZlciBub3QgY29tcGxldGUuJyApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH0gZmluYWxseSB7XG5cblx0XHRcdFx0XHRpZiAoIHJlc3RvcmUgKSB7XG5cblx0XHRcdFx0XHRcdF9nbC5iaW5kRnJhbWVidWZmZXIoIF9nbC5GUkFNRUJVRkZFUiwgX2N1cnJlbnRGcmFtZWJ1ZmZlciApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0fTtcblxuXHRcdC8vIE1hcCB0aHJlZS5qcyBjb25zdGFudHMgdG8gV2ViR0wgY29uc3RhbnRzXG5cblx0XHRmdW5jdGlvbiBwYXJhbVRocmVlVG9HTCggcCApIHtcblxuXHRcdFx0dmFyIGV4dGVuc2lvbjtcblxuXHRcdFx0aWYgKCBwID09PSBSZXBlYXRXcmFwcGluZyApIHJldHVybiBfZ2wuUkVQRUFUO1xuXHRcdFx0aWYgKCBwID09PSBDbGFtcFRvRWRnZVdyYXBwaW5nICkgcmV0dXJuIF9nbC5DTEFNUF9UT19FREdFO1xuXHRcdFx0aWYgKCBwID09PSBNaXJyb3JlZFJlcGVhdFdyYXBwaW5nICkgcmV0dXJuIF9nbC5NSVJST1JFRF9SRVBFQVQ7XG5cblx0XHRcdGlmICggcCA9PT0gTmVhcmVzdEZpbHRlciApIHJldHVybiBfZ2wuTkVBUkVTVDtcblx0XHRcdGlmICggcCA9PT0gTmVhcmVzdE1pcE1hcE5lYXJlc3RGaWx0ZXIgKSByZXR1cm4gX2dsLk5FQVJFU1RfTUlQTUFQX05FQVJFU1Q7XG5cdFx0XHRpZiAoIHAgPT09IE5lYXJlc3RNaXBNYXBMaW5lYXJGaWx0ZXIgKSByZXR1cm4gX2dsLk5FQVJFU1RfTUlQTUFQX0xJTkVBUjtcblxuXHRcdFx0aWYgKCBwID09PSBMaW5lYXJGaWx0ZXIgKSByZXR1cm4gX2dsLkxJTkVBUjtcblx0XHRcdGlmICggcCA9PT0gTGluZWFyTWlwTWFwTmVhcmVzdEZpbHRlciApIHJldHVybiBfZ2wuTElORUFSX01JUE1BUF9ORUFSRVNUO1xuXHRcdFx0aWYgKCBwID09PSBMaW5lYXJNaXBNYXBMaW5lYXJGaWx0ZXIgKSByZXR1cm4gX2dsLkxJTkVBUl9NSVBNQVBfTElORUFSO1xuXG5cdFx0XHRpZiAoIHAgPT09IFVuc2lnbmVkQnl0ZVR5cGUgKSByZXR1cm4gX2dsLlVOU0lHTkVEX0JZVEU7XG5cdFx0XHRpZiAoIHAgPT09IFVuc2lnbmVkU2hvcnQ0NDQ0VHlwZSApIHJldHVybiBfZ2wuVU5TSUdORURfU0hPUlRfNF80XzRfNDtcblx0XHRcdGlmICggcCA9PT0gVW5zaWduZWRTaG9ydDU1NTFUeXBlICkgcmV0dXJuIF9nbC5VTlNJR05FRF9TSE9SVF81XzVfNV8xO1xuXHRcdFx0aWYgKCBwID09PSBVbnNpZ25lZFNob3J0NTY1VHlwZSApIHJldHVybiBfZ2wuVU5TSUdORURfU0hPUlRfNV82XzU7XG5cblx0XHRcdGlmICggcCA9PT0gQnl0ZVR5cGUgKSByZXR1cm4gX2dsLkJZVEU7XG5cdFx0XHRpZiAoIHAgPT09IFNob3J0VHlwZSApIHJldHVybiBfZ2wuU0hPUlQ7XG5cdFx0XHRpZiAoIHAgPT09IFVuc2lnbmVkU2hvcnRUeXBlICkgcmV0dXJuIF9nbC5VTlNJR05FRF9TSE9SVDtcblx0XHRcdGlmICggcCA9PT0gSW50VHlwZSApIHJldHVybiBfZ2wuSU5UO1xuXHRcdFx0aWYgKCBwID09PSBVbnNpZ25lZEludFR5cGUgKSByZXR1cm4gX2dsLlVOU0lHTkVEX0lOVDtcblx0XHRcdGlmICggcCA9PT0gRmxvYXRUeXBlICkgcmV0dXJuIF9nbC5GTE9BVDtcblxuXHRcdFx0aWYgKCBwID09PSBIYWxmRmxvYXRUeXBlICkge1xuXG5cdFx0XHRcdGV4dGVuc2lvbiA9IGV4dGVuc2lvbnMuZ2V0KCAnT0VTX3RleHR1cmVfaGFsZl9mbG9hdCcgKTtcblxuXHRcdFx0XHRpZiAoIGV4dGVuc2lvbiAhPT0gbnVsbCApIHJldHVybiBleHRlbnNpb24uSEFMRl9GTE9BVF9PRVM7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBwID09PSBBbHBoYUZvcm1hdCApIHJldHVybiBfZ2wuQUxQSEE7XG5cdFx0XHRpZiAoIHAgPT09IFJHQkZvcm1hdCApIHJldHVybiBfZ2wuUkdCO1xuXHRcdFx0aWYgKCBwID09PSBSR0JBRm9ybWF0ICkgcmV0dXJuIF9nbC5SR0JBO1xuXHRcdFx0aWYgKCBwID09PSBMdW1pbmFuY2VGb3JtYXQgKSByZXR1cm4gX2dsLkxVTUlOQU5DRTtcblx0XHRcdGlmICggcCA9PT0gTHVtaW5hbmNlQWxwaGFGb3JtYXQgKSByZXR1cm4gX2dsLkxVTUlOQU5DRV9BTFBIQTtcblx0XHRcdGlmICggcCA9PT0gRGVwdGhGb3JtYXQgKSByZXR1cm4gX2dsLkRFUFRIX0NPTVBPTkVOVDtcblx0XHRcdGlmICggcCA9PT0gRGVwdGhTdGVuY2lsRm9ybWF0ICkgcmV0dXJuIF9nbC5ERVBUSF9TVEVOQ0lMO1xuXG5cdFx0XHRpZiAoIHAgPT09IEFkZEVxdWF0aW9uICkgcmV0dXJuIF9nbC5GVU5DX0FERDtcblx0XHRcdGlmICggcCA9PT0gU3VidHJhY3RFcXVhdGlvbiApIHJldHVybiBfZ2wuRlVOQ19TVUJUUkFDVDtcblx0XHRcdGlmICggcCA9PT0gUmV2ZXJzZVN1YnRyYWN0RXF1YXRpb24gKSByZXR1cm4gX2dsLkZVTkNfUkVWRVJTRV9TVUJUUkFDVDtcblxuXHRcdFx0aWYgKCBwID09PSBaZXJvRmFjdG9yICkgcmV0dXJuIF9nbC5aRVJPO1xuXHRcdFx0aWYgKCBwID09PSBPbmVGYWN0b3IgKSByZXR1cm4gX2dsLk9ORTtcblx0XHRcdGlmICggcCA9PT0gU3JjQ29sb3JGYWN0b3IgKSByZXR1cm4gX2dsLlNSQ19DT0xPUjtcblx0XHRcdGlmICggcCA9PT0gT25lTWludXNTcmNDb2xvckZhY3RvciApIHJldHVybiBfZ2wuT05FX01JTlVTX1NSQ19DT0xPUjtcblx0XHRcdGlmICggcCA9PT0gU3JjQWxwaGFGYWN0b3IgKSByZXR1cm4gX2dsLlNSQ19BTFBIQTtcblx0XHRcdGlmICggcCA9PT0gT25lTWludXNTcmNBbHBoYUZhY3RvciApIHJldHVybiBfZ2wuT05FX01JTlVTX1NSQ19BTFBIQTtcblx0XHRcdGlmICggcCA9PT0gRHN0QWxwaGFGYWN0b3IgKSByZXR1cm4gX2dsLkRTVF9BTFBIQTtcblx0XHRcdGlmICggcCA9PT0gT25lTWludXNEc3RBbHBoYUZhY3RvciApIHJldHVybiBfZ2wuT05FX01JTlVTX0RTVF9BTFBIQTtcblxuXHRcdFx0aWYgKCBwID09PSBEc3RDb2xvckZhY3RvciApIHJldHVybiBfZ2wuRFNUX0NPTE9SO1xuXHRcdFx0aWYgKCBwID09PSBPbmVNaW51c0RzdENvbG9yRmFjdG9yICkgcmV0dXJuIF9nbC5PTkVfTUlOVVNfRFNUX0NPTE9SO1xuXHRcdFx0aWYgKCBwID09PSBTcmNBbHBoYVNhdHVyYXRlRmFjdG9yICkgcmV0dXJuIF9nbC5TUkNfQUxQSEFfU0FUVVJBVEU7XG5cblx0XHRcdGlmICggcCA9PT0gUkdCX1MzVENfRFhUMV9Gb3JtYXQgfHwgcCA9PT0gUkdCQV9TM1RDX0RYVDFfRm9ybWF0IHx8XG5cdFx0XHRcdHAgPT09IFJHQkFfUzNUQ19EWFQzX0Zvcm1hdCB8fCBwID09PSBSR0JBX1MzVENfRFhUNV9Gb3JtYXQgKSB7XG5cblx0XHRcdFx0ZXh0ZW5zaW9uID0gZXh0ZW5zaW9ucy5nZXQoICdXRUJHTF9jb21wcmVzc2VkX3RleHR1cmVfczN0YycgKTtcblxuXHRcdFx0XHRpZiAoIGV4dGVuc2lvbiAhPT0gbnVsbCApIHtcblxuXHRcdFx0XHRcdGlmICggcCA9PT0gUkdCX1MzVENfRFhUMV9Gb3JtYXQgKSByZXR1cm4gZXh0ZW5zaW9uLkNPTVBSRVNTRURfUkdCX1MzVENfRFhUMV9FWFQ7XG5cdFx0XHRcdFx0aWYgKCBwID09PSBSR0JBX1MzVENfRFhUMV9Gb3JtYXQgKSByZXR1cm4gZXh0ZW5zaW9uLkNPTVBSRVNTRURfUkdCQV9TM1RDX0RYVDFfRVhUO1xuXHRcdFx0XHRcdGlmICggcCA9PT0gUkdCQV9TM1RDX0RYVDNfRm9ybWF0ICkgcmV0dXJuIGV4dGVuc2lvbi5DT01QUkVTU0VEX1JHQkFfUzNUQ19EWFQzX0VYVDtcblx0XHRcdFx0XHRpZiAoIHAgPT09IFJHQkFfUzNUQ19EWFQ1X0Zvcm1hdCApIHJldHVybiBleHRlbnNpb24uQ09NUFJFU1NFRF9SR0JBX1MzVENfRFhUNV9FWFQ7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggcCA9PT0gUkdCX1BWUlRDXzRCUFBWMV9Gb3JtYXQgfHwgcCA9PT0gUkdCX1BWUlRDXzJCUFBWMV9Gb3JtYXQgfHxcblx0XHRcdFx0cCA9PT0gUkdCQV9QVlJUQ180QlBQVjFfRm9ybWF0IHx8IHAgPT09IFJHQkFfUFZSVENfMkJQUFYxX0Zvcm1hdCApIHtcblxuXHRcdFx0XHRleHRlbnNpb24gPSBleHRlbnNpb25zLmdldCggJ1dFQkdMX2NvbXByZXNzZWRfdGV4dHVyZV9wdnJ0YycgKTtcblxuXHRcdFx0XHRpZiAoIGV4dGVuc2lvbiAhPT0gbnVsbCApIHtcblxuXHRcdFx0XHRcdGlmICggcCA9PT0gUkdCX1BWUlRDXzRCUFBWMV9Gb3JtYXQgKSByZXR1cm4gZXh0ZW5zaW9uLkNPTVBSRVNTRURfUkdCX1BWUlRDXzRCUFBWMV9JTUc7XG5cdFx0XHRcdFx0aWYgKCBwID09PSBSR0JfUFZSVENfMkJQUFYxX0Zvcm1hdCApIHJldHVybiBleHRlbnNpb24uQ09NUFJFU1NFRF9SR0JfUFZSVENfMkJQUFYxX0lNRztcblx0XHRcdFx0XHRpZiAoIHAgPT09IFJHQkFfUFZSVENfNEJQUFYxX0Zvcm1hdCApIHJldHVybiBleHRlbnNpb24uQ09NUFJFU1NFRF9SR0JBX1BWUlRDXzRCUFBWMV9JTUc7XG5cdFx0XHRcdFx0aWYgKCBwID09PSBSR0JBX1BWUlRDXzJCUFBWMV9Gb3JtYXQgKSByZXR1cm4gZXh0ZW5zaW9uLkNPTVBSRVNTRURfUkdCQV9QVlJUQ18yQlBQVjFfSU1HO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHAgPT09IFJHQl9FVEMxX0Zvcm1hdCApIHtcblxuXHRcdFx0XHRleHRlbnNpb24gPSBleHRlbnNpb25zLmdldCggJ1dFQkdMX2NvbXByZXNzZWRfdGV4dHVyZV9ldGMxJyApO1xuXG5cdFx0XHRcdGlmICggZXh0ZW5zaW9uICE9PSBudWxsICkgcmV0dXJuIGV4dGVuc2lvbi5DT01QUkVTU0VEX1JHQl9FVEMxX1dFQkdMO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggcCA9PT0gTWluRXF1YXRpb24gfHwgcCA9PT0gTWF4RXF1YXRpb24gKSB7XG5cblx0XHRcdFx0ZXh0ZW5zaW9uID0gZXh0ZW5zaW9ucy5nZXQoICdFWFRfYmxlbmRfbWlubWF4JyApO1xuXG5cdFx0XHRcdGlmICggZXh0ZW5zaW9uICE9PSBudWxsICkge1xuXG5cdFx0XHRcdFx0aWYgKCBwID09PSBNaW5FcXVhdGlvbiApIHJldHVybiBleHRlbnNpb24uTUlOX0VYVDtcblx0XHRcdFx0XHRpZiAoIHAgPT09IE1heEVxdWF0aW9uICkgcmV0dXJuIGV4dGVuc2lvbi5NQVhfRVhUO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHAgPT09IFVuc2lnbmVkSW50MjQ4VHlwZSApIHtcblxuXHRcdFx0XHRleHRlbnNpb24gPSBleHRlbnNpb25zLmdldCggJ1dFQkdMX2RlcHRoX3RleHR1cmUnICk7XG5cblx0XHRcdFx0aWYgKCBleHRlbnNpb24gIT09IG51bGwgKSByZXR1cm4gZXh0ZW5zaW9uLlVOU0lHTkVEX0lOVF8yNF84X1dFQkdMO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiAwO1xuXG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICogQGF1dGhvciBhbHRlcmVkcSAvIGh0dHA6Ly9hbHRlcmVkcXVhbGlhLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gRm9nRXhwMiAoIGNvbG9yLCBkZW5zaXR5ICkge1xuXG5cdFx0dGhpcy5uYW1lID0gJyc7XG5cblx0XHR0aGlzLmNvbG9yID0gbmV3IENvbG9yKCBjb2xvciApO1xuXHRcdHRoaXMuZGVuc2l0eSA9ICggZGVuc2l0eSAhPT0gdW5kZWZpbmVkICkgPyBkZW5zaXR5IDogMC4wMDAyNTtcblxuXHR9XG5cblx0Rm9nRXhwMi5wcm90b3R5cGUuaXNGb2dFeHAyID0gdHJ1ZTtcblxuXHRGb2dFeHAyLnByb3RvdHlwZS5jbG9uZSA9IGZ1bmN0aW9uICgpIHtcblxuXHRcdHJldHVybiBuZXcgRm9nRXhwMiggdGhpcy5jb2xvci5nZXRIZXgoKSwgdGhpcy5kZW5zaXR5ICk7XG5cblx0fTtcblxuXHRGb2dFeHAyLnByb3RvdHlwZS50b0pTT04gPSBmdW5jdGlvbiAoIG1ldGEgKSB7XG5cblx0XHRyZXR1cm4ge1xuXHRcdFx0dHlwZTogJ0ZvZ0V4cDInLFxuXHRcdFx0Y29sb3I6IHRoaXMuY29sb3IuZ2V0SGV4KCksXG5cdFx0XHRkZW5zaXR5OiB0aGlzLmRlbnNpdHlcblx0XHR9O1xuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEZvZyAoIGNvbG9yLCBuZWFyLCBmYXIgKSB7XG5cblx0XHR0aGlzLm5hbWUgPSAnJztcblxuXHRcdHRoaXMuY29sb3IgPSBuZXcgQ29sb3IoIGNvbG9yICk7XG5cblx0XHR0aGlzLm5lYXIgPSAoIG5lYXIgIT09IHVuZGVmaW5lZCApID8gbmVhciA6IDE7XG5cdFx0dGhpcy5mYXIgPSAoIGZhciAhPT0gdW5kZWZpbmVkICkgPyBmYXIgOiAxMDAwO1xuXG5cdH1cblxuXHRGb2cucHJvdG90eXBlLmlzRm9nID0gdHJ1ZTtcblxuXHRGb2cucHJvdG90eXBlLmNsb25lID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0cmV0dXJuIG5ldyBGb2coIHRoaXMuY29sb3IuZ2V0SGV4KCksIHRoaXMubmVhciwgdGhpcy5mYXIgKTtcblxuXHR9O1xuXG5cdEZvZy5wcm90b3R5cGUudG9KU09OID0gZnVuY3Rpb24gKCBtZXRhICkge1xuXG5cdFx0cmV0dXJuIHtcblx0XHRcdHR5cGU6ICdGb2cnLFxuXHRcdFx0Y29sb3I6IHRoaXMuY29sb3IuZ2V0SGV4KCksXG5cdFx0XHRuZWFyOiB0aGlzLm5lYXIsXG5cdFx0XHRmYXI6IHRoaXMuZmFyXG5cdFx0fTtcblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBTY2VuZSAoKSB7XG5cblx0XHRPYmplY3QzRC5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnU2NlbmUnO1xuXG5cdFx0dGhpcy5iYWNrZ3JvdW5kID0gbnVsbDtcblx0XHR0aGlzLmZvZyA9IG51bGw7XG5cdFx0dGhpcy5vdmVycmlkZU1hdGVyaWFsID0gbnVsbDtcblxuXHRcdHRoaXMuYXV0b1VwZGF0ZSA9IHRydWU7IC8vIGNoZWNrZWQgYnkgdGhlIHJlbmRlcmVyXG5cblx0fVxuXG5cdFNjZW5lLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIE9iamVjdDNELnByb3RvdHlwZSApO1xuXG5cdFNjZW5lLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFNjZW5lO1xuXG5cdFNjZW5lLnByb3RvdHlwZS5jb3B5ID0gZnVuY3Rpb24gKCBzb3VyY2UsIHJlY3Vyc2l2ZSApIHtcblxuXHRcdE9iamVjdDNELnByb3RvdHlwZS5jb3B5LmNhbGwoIHRoaXMsIHNvdXJjZSwgcmVjdXJzaXZlICk7XG5cblx0XHRpZiAoIHNvdXJjZS5iYWNrZ3JvdW5kICE9PSBudWxsICkgdGhpcy5iYWNrZ3JvdW5kID0gc291cmNlLmJhY2tncm91bmQuY2xvbmUoKTtcblx0XHRpZiAoIHNvdXJjZS5mb2cgIT09IG51bGwgKSB0aGlzLmZvZyA9IHNvdXJjZS5mb2cuY2xvbmUoKTtcblx0XHRpZiAoIHNvdXJjZS5vdmVycmlkZU1hdGVyaWFsICE9PSBudWxsICkgdGhpcy5vdmVycmlkZU1hdGVyaWFsID0gc291cmNlLm92ZXJyaWRlTWF0ZXJpYWwuY2xvbmUoKTtcblxuXHRcdHRoaXMuYXV0b1VwZGF0ZSA9IHNvdXJjZS5hdXRvVXBkYXRlO1xuXHRcdHRoaXMubWF0cml4QXV0b1VwZGF0ZSA9IHNvdXJjZS5tYXRyaXhBdXRvVXBkYXRlO1xuXG5cdFx0cmV0dXJuIHRoaXM7XG5cblx0fTtcblxuXHRTY2VuZS5wcm90b3R5cGUudG9KU09OID0gZnVuY3Rpb24gKCBtZXRhICkge1xuXG5cdFx0dmFyIGRhdGEgPSBPYmplY3QzRC5wcm90b3R5cGUudG9KU09OLmNhbGwoIHRoaXMsIG1ldGEgKTtcblxuXHRcdGlmICggdGhpcy5iYWNrZ3JvdW5kICE9PSBudWxsICkgZGF0YS5vYmplY3QuYmFja2dyb3VuZCA9IHRoaXMuYmFja2dyb3VuZC50b0pTT04oIG1ldGEgKTtcblx0XHRpZiAoIHRoaXMuZm9nICE9PSBudWxsICkgZGF0YS5vYmplY3QuZm9nID0gdGhpcy5mb2cudG9KU09OKCk7XG5cblx0XHRyZXR1cm4gZGF0YTtcblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1pa2FlbCBlbXRpbmdlciAvIGh0dHA6Ly9nb21vLnNlL1xuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBMZW5zRmxhcmUoIHRleHR1cmUsIHNpemUsIGRpc3RhbmNlLCBibGVuZGluZywgY29sb3IgKSB7XG5cblx0XHRPYmplY3QzRC5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLmxlbnNGbGFyZXMgPSBbXTtcblxuXHRcdHRoaXMucG9zaXRpb25TY3JlZW4gPSBuZXcgVmVjdG9yMygpO1xuXHRcdHRoaXMuY3VzdG9tVXBkYXRlQ2FsbGJhY2sgPSB1bmRlZmluZWQ7XG5cblx0XHRpZiAoIHRleHR1cmUgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0dGhpcy5hZGQoIHRleHR1cmUsIHNpemUsIGRpc3RhbmNlLCBibGVuZGluZywgY29sb3IgKTtcblxuXHRcdH1cblxuXHR9XG5cblx0TGVuc0ZsYXJlLnByb3RvdHlwZSA9IE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIE9iamVjdDNELnByb3RvdHlwZSApLCB7XG5cblx0XHRjb25zdHJ1Y3RvcjogTGVuc0ZsYXJlLFxuXG5cdFx0aXNMZW5zRmxhcmU6IHRydWUsXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIHNvdXJjZSApIHtcblxuXHRcdFx0T2JqZWN0M0QucHJvdG90eXBlLmNvcHkuY2FsbCggdGhpcywgc291cmNlICk7XG5cblx0XHRcdHRoaXMucG9zaXRpb25TY3JlZW4uY29weSggc291cmNlLnBvc2l0aW9uU2NyZWVuICk7XG5cdFx0XHR0aGlzLmN1c3RvbVVwZGF0ZUNhbGxiYWNrID0gc291cmNlLmN1c3RvbVVwZGF0ZUNhbGxiYWNrO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBzb3VyY2UubGVuc0ZsYXJlcy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdHRoaXMubGVuc0ZsYXJlcy5wdXNoKCBzb3VyY2UubGVuc0ZsYXJlc1sgaSBdICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0YWRkOiBmdW5jdGlvbiAoIHRleHR1cmUsIHNpemUsIGRpc3RhbmNlLCBibGVuZGluZywgY29sb3IsIG9wYWNpdHkgKSB7XG5cblx0XHRcdGlmICggc2l6ZSA9PT0gdW5kZWZpbmVkICkgc2l6ZSA9IC0gMTtcblx0XHRcdGlmICggZGlzdGFuY2UgPT09IHVuZGVmaW5lZCApIGRpc3RhbmNlID0gMDtcblx0XHRcdGlmICggb3BhY2l0eSA9PT0gdW5kZWZpbmVkICkgb3BhY2l0eSA9IDE7XG5cdFx0XHRpZiAoIGNvbG9yID09PSB1bmRlZmluZWQgKSBjb2xvciA9IG5ldyBDb2xvciggMHhmZmZmZmYgKTtcblx0XHRcdGlmICggYmxlbmRpbmcgPT09IHVuZGVmaW5lZCApIGJsZW5kaW5nID0gTm9ybWFsQmxlbmRpbmc7XG5cblx0XHRcdGRpc3RhbmNlID0gTWF0aC5taW4oIGRpc3RhbmNlLCBNYXRoLm1heCggMCwgZGlzdGFuY2UgKSApO1xuXG5cdFx0XHR0aGlzLmxlbnNGbGFyZXMucHVzaCgge1xuXHRcdFx0XHR0ZXh0dXJlOiB0ZXh0dXJlLFx0Ly8gVEhSRUUuVGV4dHVyZVxuXHRcdFx0XHRzaXplOiBzaXplLCBcdFx0Ly8gc2l6ZSBpbiBwaXhlbHMgKC0xID0gdXNlIHRleHR1cmUud2lkdGgpXG5cdFx0XHRcdGRpc3RhbmNlOiBkaXN0YW5jZSwgXHQvLyBkaXN0YW5jZSAoMC0xKSBmcm9tIGxpZ2h0IHNvdXJjZSAoMD1hdCBsaWdodCBzb3VyY2UpXG5cdFx0XHRcdHg6IDAsIHk6IDAsIHo6IDAsXHQvLyBzY3JlZW4gcG9zaXRpb24gKC0xID0+IDEpIHogPSAwIGlzIGluIGZyb250IHogPSAxIGlzIGJhY2tcblx0XHRcdFx0c2NhbGU6IDEsIFx0XHQvLyBzY2FsZVxuXHRcdFx0XHRyb3RhdGlvbjogMCwgXHRcdC8vIHJvdGF0aW9uXG5cdFx0XHRcdG9wYWNpdHk6IG9wYWNpdHksXHQvLyBvcGFjaXR5XG5cdFx0XHRcdGNvbG9yOiBjb2xvcixcdFx0Ly8gY29sb3Jcblx0XHRcdFx0YmxlbmRpbmc6IGJsZW5kaW5nXHQvLyBibGVuZGluZ1xuXHRcdFx0fSApO1xuXG5cdFx0fSxcblxuXHRcdC8qXG5cdFx0ICogVXBkYXRlIGxlbnMgZmxhcmVzIHVwZGF0ZSBwb3NpdGlvbnMgb24gYWxsIGZsYXJlcyBiYXNlZCBvbiB0aGUgc2NyZWVuIHBvc2l0aW9uXG5cdFx0ICogU2V0IG15TGVuc0ZsYXJlLmN1c3RvbVVwZGF0ZUNhbGxiYWNrIHRvIGFsdGVyIHRoZSBmbGFyZXMgaW4geW91ciBwcm9qZWN0IHNwZWNpZmljIHdheS5cblx0XHQgKi9cblxuXHRcdHVwZGF0ZUxlbnNGbGFyZXM6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIGYsIGZsID0gdGhpcy5sZW5zRmxhcmVzLmxlbmd0aDtcblx0XHRcdHZhciBmbGFyZTtcblx0XHRcdHZhciB2ZWNYID0gLSB0aGlzLnBvc2l0aW9uU2NyZWVuLnggKiAyO1xuXHRcdFx0dmFyIHZlY1kgPSAtIHRoaXMucG9zaXRpb25TY3JlZW4ueSAqIDI7XG5cblx0XHRcdGZvciAoIGYgPSAwOyBmIDwgZmw7IGYgKysgKSB7XG5cblx0XHRcdFx0ZmxhcmUgPSB0aGlzLmxlbnNGbGFyZXNbIGYgXTtcblxuXHRcdFx0XHRmbGFyZS54ID0gdGhpcy5wb3NpdGlvblNjcmVlbi54ICsgdmVjWCAqIGZsYXJlLmRpc3RhbmNlO1xuXHRcdFx0XHRmbGFyZS55ID0gdGhpcy5wb3NpdGlvblNjcmVlbi55ICsgdmVjWSAqIGZsYXJlLmRpc3RhbmNlO1xuXG5cdFx0XHRcdGZsYXJlLndhbnRlZFJvdGF0aW9uID0gZmxhcmUueCAqIE1hdGguUEkgKiAwLjI1O1xuXHRcdFx0XHRmbGFyZS5yb3RhdGlvbiArPSAoIGZsYXJlLndhbnRlZFJvdGF0aW9uIC0gZmxhcmUucm90YXRpb24gKSAqIDAuMjU7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqXG5cdCAqIHBhcmFtZXRlcnMgPSB7XG5cdCAqICBjb2xvcjogPGhleD4sXG5cdCAqICBvcGFjaXR5OiA8ZmxvYXQ+LFxuXHQgKiAgbWFwOiBuZXcgVEhSRUUuVGV4dHVyZSggPEltYWdlPiApLFxuXHQgKlxuXHQgKlx0dXZPZmZzZXQ6IG5ldyBUSFJFRS5WZWN0b3IyKCksXG5cdCAqXHR1dlNjYWxlOiBuZXcgVEhSRUUuVmVjdG9yMigpXG5cdCAqIH1cblx0ICovXG5cblx0ZnVuY3Rpb24gU3ByaXRlTWF0ZXJpYWwoIHBhcmFtZXRlcnMgKSB7XG5cblx0XHRNYXRlcmlhbC5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnU3ByaXRlTWF0ZXJpYWwnO1xuXG5cdFx0dGhpcy5jb2xvciA9IG5ldyBDb2xvciggMHhmZmZmZmYgKTtcblx0XHR0aGlzLm1hcCA9IG51bGw7XG5cblx0XHR0aGlzLnJvdGF0aW9uID0gMDtcblxuXHRcdHRoaXMuZm9nID0gZmFsc2U7XG5cdFx0dGhpcy5saWdodHMgPSBmYWxzZTtcblxuXHRcdHRoaXMuc2V0VmFsdWVzKCBwYXJhbWV0ZXJzICk7XG5cblx0fVxuXG5cdFNwcml0ZU1hdGVyaWFsLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIE1hdGVyaWFsLnByb3RvdHlwZSApO1xuXHRTcHJpdGVNYXRlcmlhbC5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBTcHJpdGVNYXRlcmlhbDtcblxuXHRTcHJpdGVNYXRlcmlhbC5wcm90b3R5cGUuY29weSA9IGZ1bmN0aW9uICggc291cmNlICkge1xuXG5cdFx0TWF0ZXJpYWwucHJvdG90eXBlLmNvcHkuY2FsbCggdGhpcywgc291cmNlICk7XG5cblx0XHR0aGlzLmNvbG9yLmNvcHkoIHNvdXJjZS5jb2xvciApO1xuXHRcdHRoaXMubWFwID0gc291cmNlLm1hcDtcblxuXHRcdHRoaXMucm90YXRpb24gPSBzb3VyY2Uucm90YXRpb247XG5cblx0XHRyZXR1cm4gdGhpcztcblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1pa2FlbCBlbXRpbmdlciAvIGh0dHA6Ly9nb21vLnNlL1xuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBTcHJpdGUoIG1hdGVyaWFsICkge1xuXG5cdFx0T2JqZWN0M0QuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ1Nwcml0ZSc7XG5cblx0XHR0aGlzLm1hdGVyaWFsID0gKCBtYXRlcmlhbCAhPT0gdW5kZWZpbmVkICkgPyBtYXRlcmlhbCA6IG5ldyBTcHJpdGVNYXRlcmlhbCgpO1xuXG5cdH1cblxuXHRTcHJpdGUucHJvdG90eXBlID0gT2JqZWN0LmFzc2lnbiggT2JqZWN0LmNyZWF0ZSggT2JqZWN0M0QucHJvdG90eXBlICksIHtcblxuXHRcdGNvbnN0cnVjdG9yOiBTcHJpdGUsXG5cblx0XHRpc1Nwcml0ZTogdHJ1ZSxcblxuXHRcdHJheWNhc3Q6ICggZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgbWF0cml4UG9zaXRpb24gPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gcmF5Y2FzdCggcmF5Y2FzdGVyLCBpbnRlcnNlY3RzICkge1xuXG5cdFx0XHRcdG1hdHJpeFBvc2l0aW9uLnNldEZyb21NYXRyaXhQb3NpdGlvbiggdGhpcy5tYXRyaXhXb3JsZCApO1xuXG5cdFx0XHRcdHZhciBkaXN0YW5jZVNxID0gcmF5Y2FzdGVyLnJheS5kaXN0YW5jZVNxVG9Qb2ludCggbWF0cml4UG9zaXRpb24gKTtcblx0XHRcdFx0dmFyIGd1ZXNzU2l6ZVNxID0gdGhpcy5zY2FsZS54ICogdGhpcy5zY2FsZS55IC8gNDtcblxuXHRcdFx0XHRpZiAoIGRpc3RhbmNlU3EgPiBndWVzc1NpemVTcSApIHtcblxuXHRcdFx0XHRcdHJldHVybjtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aW50ZXJzZWN0cy5wdXNoKCB7XG5cblx0XHRcdFx0XHRkaXN0YW5jZTogTWF0aC5zcXJ0KCBkaXN0YW5jZVNxICksXG5cdFx0XHRcdFx0cG9pbnQ6IHRoaXMucG9zaXRpb24sXG5cdFx0XHRcdFx0ZmFjZTogbnVsbCxcblx0XHRcdFx0XHRvYmplY3Q6IHRoaXNcblxuXHRcdFx0XHR9ICk7XG5cblx0XHRcdH07XG5cblx0XHR9KCkgKSxcblxuXHRcdGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiBuZXcgdGhpcy5jb25zdHJ1Y3RvciggdGhpcy5tYXRlcmlhbCApLmNvcHkoIHRoaXMgKTtcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbWlrYWVsIGVtdGluZ2VyIC8gaHR0cDovL2dvbW8uc2UvXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIExPRCgpIHtcblxuXHRcdE9iamVjdDNELmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdMT0QnO1xuXG5cdFx0T2JqZWN0LmRlZmluZVByb3BlcnRpZXMoIHRoaXMsIHtcblx0XHRcdGxldmVsczoge1xuXHRcdFx0XHRlbnVtZXJhYmxlOiB0cnVlLFxuXHRcdFx0XHR2YWx1ZTogW11cblx0XHRcdH1cblx0XHR9ICk7XG5cblx0fVxuXG5cblx0TE9ELnByb3RvdHlwZSA9IE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIE9iamVjdDNELnByb3RvdHlwZSApLCB7XG5cblx0XHRjb25zdHJ1Y3RvcjogTE9ELFxuXG5cdFx0Y29weTogZnVuY3Rpb24gKCBzb3VyY2UgKSB7XG5cblx0XHRcdE9iamVjdDNELnByb3RvdHlwZS5jb3B5LmNhbGwoIHRoaXMsIHNvdXJjZSwgZmFsc2UgKTtcblxuXHRcdFx0dmFyIGxldmVscyA9IHNvdXJjZS5sZXZlbHM7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGxldmVscy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdHZhciBsZXZlbCA9IGxldmVsc1sgaSBdO1xuXG5cdFx0XHRcdHRoaXMuYWRkTGV2ZWwoIGxldmVsLm9iamVjdC5jbG9uZSgpLCBsZXZlbC5kaXN0YW5jZSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGFkZExldmVsOiBmdW5jdGlvbiAoIG9iamVjdCwgZGlzdGFuY2UgKSB7XG5cblx0XHRcdGlmICggZGlzdGFuY2UgPT09IHVuZGVmaW5lZCApIGRpc3RhbmNlID0gMDtcblxuXHRcdFx0ZGlzdGFuY2UgPSBNYXRoLmFicyggZGlzdGFuY2UgKTtcblxuXHRcdFx0dmFyIGxldmVscyA9IHRoaXMubGV2ZWxzO1xuXG5cdFx0XHRmb3IgKCB2YXIgbCA9IDA7IGwgPCBsZXZlbHMubGVuZ3RoOyBsICsrICkge1xuXG5cdFx0XHRcdGlmICggZGlzdGFuY2UgPCBsZXZlbHNbIGwgXS5kaXN0YW5jZSApIHtcblxuXHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRsZXZlbHMuc3BsaWNlKCBsLCAwLCB7IGRpc3RhbmNlOiBkaXN0YW5jZSwgb2JqZWN0OiBvYmplY3QgfSApO1xuXG5cdFx0XHR0aGlzLmFkZCggb2JqZWN0ICk7XG5cblx0XHR9LFxuXG5cdFx0Z2V0T2JqZWN0Rm9yRGlzdGFuY2U6IGZ1bmN0aW9uICggZGlzdGFuY2UgKSB7XG5cblx0XHRcdHZhciBsZXZlbHMgPSB0aGlzLmxldmVscztcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAxLCBsID0gbGV2ZWxzLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0aWYgKCBkaXN0YW5jZSA8IGxldmVsc1sgaSBdLmRpc3RhbmNlICkge1xuXG5cdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBsZXZlbHNbIGkgLSAxIF0ub2JqZWN0O1xuXG5cdFx0fSxcblxuXHRcdHJheWNhc3Q6ICggZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgbWF0cml4UG9zaXRpb24gPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gcmF5Y2FzdCggcmF5Y2FzdGVyLCBpbnRlcnNlY3RzICkge1xuXG5cdFx0XHRcdG1hdHJpeFBvc2l0aW9uLnNldEZyb21NYXRyaXhQb3NpdGlvbiggdGhpcy5tYXRyaXhXb3JsZCApO1xuXG5cdFx0XHRcdHZhciBkaXN0YW5jZSA9IHJheWNhc3Rlci5yYXkub3JpZ2luLmRpc3RhbmNlVG8oIG1hdHJpeFBvc2l0aW9uICk7XG5cblx0XHRcdFx0dGhpcy5nZXRPYmplY3RGb3JEaXN0YW5jZSggZGlzdGFuY2UgKS5yYXljYXN0KCByYXljYXN0ZXIsIGludGVyc2VjdHMgKTtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSApLFxuXG5cdFx0dXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB2MSA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHR2YXIgdjIgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gdXBkYXRlKCBjYW1lcmEgKSB7XG5cblx0XHRcdFx0dmFyIGxldmVscyA9IHRoaXMubGV2ZWxzO1xuXG5cdFx0XHRcdGlmICggbGV2ZWxzLmxlbmd0aCA+IDEgKSB7XG5cblx0XHRcdFx0XHR2MS5zZXRGcm9tTWF0cml4UG9zaXRpb24oIGNhbWVyYS5tYXRyaXhXb3JsZCApO1xuXHRcdFx0XHRcdHYyLnNldEZyb21NYXRyaXhQb3NpdGlvbiggdGhpcy5tYXRyaXhXb3JsZCApO1xuXG5cdFx0XHRcdFx0dmFyIGRpc3RhbmNlID0gdjEuZGlzdGFuY2VUbyggdjIgKTtcblxuXHRcdFx0XHRcdGxldmVsc1sgMCBdLm9iamVjdC52aXNpYmxlID0gdHJ1ZTtcblxuXHRcdFx0XHRcdGZvciAoIHZhciBpID0gMSwgbCA9IGxldmVscy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdFx0XHRpZiAoIGRpc3RhbmNlID49IGxldmVsc1sgaSBdLmRpc3RhbmNlICkge1xuXG5cdFx0XHRcdFx0XHRcdGxldmVsc1sgaSAtIDEgXS5vYmplY3QudmlzaWJsZSA9IGZhbHNlO1xuXHRcdFx0XHRcdFx0XHRsZXZlbHNbIGkgXS5vYmplY3QudmlzaWJsZSA9IHRydWU7XG5cblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGZvciAoIDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRcdGxldmVsc1sgaSBdLm9iamVjdC52aXNpYmxlID0gZmFsc2U7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9O1xuXG5cdFx0fSgpLFxuXG5cdFx0dG9KU09OOiBmdW5jdGlvbiAoIG1ldGEgKSB7XG5cblx0XHRcdHZhciBkYXRhID0gT2JqZWN0M0QucHJvdG90eXBlLnRvSlNPTi5jYWxsKCB0aGlzLCBtZXRhICk7XG5cblx0XHRcdGRhdGEub2JqZWN0LmxldmVscyA9IFtdO1xuXG5cdFx0XHR2YXIgbGV2ZWxzID0gdGhpcy5sZXZlbHM7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGxldmVscy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdHZhciBsZXZlbCA9IGxldmVsc1sgaSBdO1xuXG5cdFx0XHRcdGRhdGEub2JqZWN0LmxldmVscy5wdXNoKCB7XG5cdFx0XHRcdFx0b2JqZWN0OiBsZXZlbC5vYmplY3QudXVpZCxcblx0XHRcdFx0XHRkaXN0YW5jZTogbGV2ZWwuZGlzdGFuY2Vcblx0XHRcdFx0fSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBkYXRhO1xuXG5cdFx0fVxuXG5cdH0gKTtcblxuXHQvKipcblx0ICogQGF1dGhvciBtaWthZWwgZW10aW5nZXIgLyBodHRwOi8vZ29tby5zZS9cblx0ICogQGF1dGhvciBhbHRlcmVkcSAvIGh0dHA6Ly9hbHRlcmVkcXVhbGlhLmNvbS9cblx0ICogQGF1dGhvciBtaWNoYWVsIGd1ZXJyZXJvIC8gaHR0cDovL3JlYWxpdHltZWx0ZG93bi5jb21cblx0ICogQGF1dGhvciBpa2VyciAvIGh0dHA6Ly92ZXJvbGQuY29tXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFNrZWxldG9uKCBib25lcywgYm9uZUludmVyc2VzLCB1c2VWZXJ0ZXhUZXh0dXJlICkge1xuXG5cdFx0dGhpcy51c2VWZXJ0ZXhUZXh0dXJlID0gdXNlVmVydGV4VGV4dHVyZSAhPT0gdW5kZWZpbmVkID8gdXNlVmVydGV4VGV4dHVyZSA6IHRydWU7XG5cblx0XHR0aGlzLmlkZW50aXR5TWF0cml4ID0gbmV3IE1hdHJpeDQoKTtcblxuXHRcdC8vIGNvcHkgdGhlIGJvbmUgYXJyYXlcblxuXHRcdGJvbmVzID0gYm9uZXMgfHwgW107XG5cblx0XHR0aGlzLmJvbmVzID0gYm9uZXMuc2xpY2UoIDAgKTtcblxuXHRcdC8vIGNyZWF0ZSBhIGJvbmUgdGV4dHVyZSBvciBhbiBhcnJheSBvZiBmbG9hdHNcblxuXHRcdGlmICggdGhpcy51c2VWZXJ0ZXhUZXh0dXJlICkge1xuXG5cdFx0XHQvLyBsYXlvdXQgKDEgbWF0cml4ID0gNCBwaXhlbHMpXG5cdFx0XHQvLyAgICAgIFJHQkEgUkdCQSBSR0JBIFJHQkEgKD0+IGNvbHVtbjEsIGNvbHVtbjIsIGNvbHVtbjMsIGNvbHVtbjQpXG5cdFx0XHQvLyAgd2l0aCAgOHg4ICBwaXhlbCB0ZXh0dXJlIG1heCAgIDE2IGJvbmVzICogNCBwaXhlbHMgPSAgKDggKiA4KVxuXHRcdFx0Ly8gICAgICAgMTZ4MTYgcGl4ZWwgdGV4dHVyZSBtYXggICA2NCBib25lcyAqIDQgcGl4ZWxzID0gKDE2ICogMTYpXG5cdFx0XHQvLyAgICAgICAzMngzMiBwaXhlbCB0ZXh0dXJlIG1heCAgMjU2IGJvbmVzICogNCBwaXhlbHMgPSAoMzIgKiAzMilcblx0XHRcdC8vICAgICAgIDY0eDY0IHBpeGVsIHRleHR1cmUgbWF4IDEwMjQgYm9uZXMgKiA0IHBpeGVscyA9ICg2NCAqIDY0KVxuXG5cblx0XHRcdHZhciBzaXplID0gTWF0aC5zcXJ0KCB0aGlzLmJvbmVzLmxlbmd0aCAqIDQgKTsgLy8gNCBwaXhlbHMgbmVlZGVkIGZvciAxIG1hdHJpeFxuXHRcdFx0c2l6ZSA9IF9NYXRoLm5leHRQb3dlck9mVHdvKCBNYXRoLmNlaWwoIHNpemUgKSApO1xuXHRcdFx0c2l6ZSA9IE1hdGgubWF4KCBzaXplLCA0ICk7XG5cblx0XHRcdHRoaXMuYm9uZVRleHR1cmVXaWR0aCA9IHNpemU7XG5cdFx0XHR0aGlzLmJvbmVUZXh0dXJlSGVpZ2h0ID0gc2l6ZTtcblxuXHRcdFx0dGhpcy5ib25lTWF0cmljZXMgPSBuZXcgRmxvYXQzMkFycmF5KCB0aGlzLmJvbmVUZXh0dXJlV2lkdGggKiB0aGlzLmJvbmVUZXh0dXJlSGVpZ2h0ICogNCApOyAvLyA0IGZsb2F0cyBwZXIgUkdCQSBwaXhlbFxuXHRcdFx0dGhpcy5ib25lVGV4dHVyZSA9IG5ldyBEYXRhVGV4dHVyZSggdGhpcy5ib25lTWF0cmljZXMsIHRoaXMuYm9uZVRleHR1cmVXaWR0aCwgdGhpcy5ib25lVGV4dHVyZUhlaWdodCwgUkdCQUZvcm1hdCwgRmxvYXRUeXBlICk7XG5cblx0XHR9IGVsc2Uge1xuXG5cdFx0XHR0aGlzLmJvbmVNYXRyaWNlcyA9IG5ldyBGbG9hdDMyQXJyYXkoIDE2ICogdGhpcy5ib25lcy5sZW5ndGggKTtcblxuXHRcdH1cblxuXHRcdC8vIHVzZSB0aGUgc3VwcGxpZWQgYm9uZSBpbnZlcnNlcyBvciBjYWxjdWxhdGUgdGhlIGludmVyc2VzXG5cblx0XHRpZiAoIGJvbmVJbnZlcnNlcyA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHR0aGlzLmNhbGN1bGF0ZUludmVyc2VzKCk7XG5cblx0XHR9IGVsc2Uge1xuXG5cdFx0XHRpZiAoIHRoaXMuYm9uZXMubGVuZ3RoID09PSBib25lSW52ZXJzZXMubGVuZ3RoICkge1xuXG5cdFx0XHRcdHRoaXMuYm9uZUludmVyc2VzID0gYm9uZUludmVyc2VzLnNsaWNlKCAwICk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuU2tlbGV0b24gYm9uSW52ZXJzZXMgaXMgdGhlIHdyb25nIGxlbmd0aC4nICk7XG5cblx0XHRcdFx0dGhpcy5ib25lSW52ZXJzZXMgPSBbXTtcblxuXHRcdFx0XHRmb3IgKCB2YXIgYiA9IDAsIGJsID0gdGhpcy5ib25lcy5sZW5ndGg7IGIgPCBibDsgYiArKyApIHtcblxuXHRcdFx0XHRcdHRoaXMuYm9uZUludmVyc2VzLnB1c2goIG5ldyBNYXRyaXg0KCkgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHR9XG5cblx0T2JqZWN0LmFzc2lnbiggU2tlbGV0b24ucHJvdG90eXBlLCB7XG5cblx0XHRjYWxjdWxhdGVJbnZlcnNlczogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR0aGlzLmJvbmVJbnZlcnNlcyA9IFtdO1xuXG5cdFx0XHRmb3IgKCB2YXIgYiA9IDAsIGJsID0gdGhpcy5ib25lcy5sZW5ndGg7IGIgPCBibDsgYiArKyApIHtcblxuXHRcdFx0XHR2YXIgaW52ZXJzZSA9IG5ldyBNYXRyaXg0KCk7XG5cblx0XHRcdFx0aWYgKCB0aGlzLmJvbmVzWyBiIF0gKSB7XG5cblx0XHRcdFx0XHRpbnZlcnNlLmdldEludmVyc2UoIHRoaXMuYm9uZXNbIGIgXS5tYXRyaXhXb3JsZCApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR0aGlzLmJvbmVJbnZlcnNlcy5wdXNoKCBpbnZlcnNlICk7XG5cblx0XHRcdH1cblxuXHRcdH0sXG5cblx0XHRwb3NlOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBib25lO1xuXG5cdFx0XHQvLyByZWNvdmVyIHRoZSBiaW5kLXRpbWUgd29ybGQgbWF0cmljZXNcblxuXHRcdFx0Zm9yICggdmFyIGIgPSAwLCBibCA9IHRoaXMuYm9uZXMubGVuZ3RoOyBiIDwgYmw7IGIgKysgKSB7XG5cblx0XHRcdFx0Ym9uZSA9IHRoaXMuYm9uZXNbIGIgXTtcblxuXHRcdFx0XHRpZiAoIGJvbmUgKSB7XG5cblx0XHRcdFx0XHRib25lLm1hdHJpeFdvcmxkLmdldEludmVyc2UoIHRoaXMuYm9uZUludmVyc2VzWyBiIF0gKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gY29tcHV0ZSB0aGUgbG9jYWwgbWF0cmljZXMsIHBvc2l0aW9ucywgcm90YXRpb25zIGFuZCBzY2FsZXNcblxuXHRcdFx0Zm9yICggdmFyIGIgPSAwLCBibCA9IHRoaXMuYm9uZXMubGVuZ3RoOyBiIDwgYmw7IGIgKysgKSB7XG5cblx0XHRcdFx0Ym9uZSA9IHRoaXMuYm9uZXNbIGIgXTtcblxuXHRcdFx0XHRpZiAoIGJvbmUgKSB7XG5cblx0XHRcdFx0XHRpZiAoIGJvbmUucGFyZW50ICYmIGJvbmUucGFyZW50LmlzQm9uZSApIHtcblxuXHRcdFx0XHRcdFx0Ym9uZS5tYXRyaXguZ2V0SW52ZXJzZSggYm9uZS5wYXJlbnQubWF0cml4V29ybGQgKTtcblx0XHRcdFx0XHRcdGJvbmUubWF0cml4Lm11bHRpcGx5KCBib25lLm1hdHJpeFdvcmxkICk7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRib25lLm1hdHJpeC5jb3B5KCBib25lLm1hdHJpeFdvcmxkICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRib25lLm1hdHJpeC5kZWNvbXBvc2UoIGJvbmUucG9zaXRpb24sIGJvbmUucXVhdGVybmlvbiwgYm9uZS5zY2FsZSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0fSxcblxuXHRcdHVwZGF0ZTogKCBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBvZmZzZXRNYXRyaXggPSBuZXcgTWF0cml4NCgpO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gdXBkYXRlKCkge1xuXG5cdFx0XHRcdC8vIGZsYXR0ZW4gYm9uZSBtYXRyaWNlcyB0byBhcnJheVxuXG5cdFx0XHRcdGZvciAoIHZhciBiID0gMCwgYmwgPSB0aGlzLmJvbmVzLmxlbmd0aDsgYiA8IGJsOyBiICsrICkge1xuXG5cdFx0XHRcdFx0Ly8gY29tcHV0ZSB0aGUgb2Zmc2V0IGJldHdlZW4gdGhlIGN1cnJlbnQgYW5kIHRoZSBvcmlnaW5hbCB0cmFuc2Zvcm1cblxuXHRcdFx0XHRcdHZhciBtYXRyaXggPSB0aGlzLmJvbmVzWyBiIF0gPyB0aGlzLmJvbmVzWyBiIF0ubWF0cml4V29ybGQgOiB0aGlzLmlkZW50aXR5TWF0cml4O1xuXG5cdFx0XHRcdFx0b2Zmc2V0TWF0cml4Lm11bHRpcGx5TWF0cmljZXMoIG1hdHJpeCwgdGhpcy5ib25lSW52ZXJzZXNbIGIgXSApO1xuXHRcdFx0XHRcdG9mZnNldE1hdHJpeC50b0FycmF5KCB0aGlzLmJvbmVNYXRyaWNlcywgYiAqIDE2ICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggdGhpcy51c2VWZXJ0ZXhUZXh0dXJlICkge1xuXG5cdFx0XHRcdFx0dGhpcy5ib25lVGV4dHVyZS5uZWVkc1VwZGF0ZSA9IHRydWU7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9O1xuXG5cdFx0fSApKCksXG5cblx0XHRjbG9uZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IFNrZWxldG9uKCB0aGlzLmJvbmVzLCB0aGlzLmJvbmVJbnZlcnNlcywgdGhpcy51c2VWZXJ0ZXhUZXh0dXJlICk7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1pa2FlbCBlbXRpbmdlciAvIGh0dHA6Ly9nb21vLnNlL1xuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKiBAYXV0aG9yIGlrZXJyIC8gaHR0cDovL3Zlcm9sZC5jb21cblx0ICovXG5cblx0ZnVuY3Rpb24gQm9uZSgpIHtcblxuXHRcdE9iamVjdDNELmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdCb25lJztcblxuXHR9XG5cblx0Qm9uZS5wcm90b3R5cGUgPSBPYmplY3QuYXNzaWduKCBPYmplY3QuY3JlYXRlKCBPYmplY3QzRC5wcm90b3R5cGUgKSwge1xuXG5cdFx0Y29uc3RydWN0b3I6IEJvbmUsXG5cblx0XHRpc0JvbmU6IHRydWVcblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbWlrYWVsIGVtdGluZ2VyIC8gaHR0cDovL2dvbW8uc2UvXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqIEBhdXRob3IgaWtlcnIgLyBodHRwOi8vdmVyb2xkLmNvbVxuXHQgKi9cblxuXHRmdW5jdGlvbiBTa2lubmVkTWVzaCggZ2VvbWV0cnksIG1hdGVyaWFsLCB1c2VWZXJ0ZXhUZXh0dXJlICkge1xuXG5cdFx0TWVzaC5jYWxsKCB0aGlzLCBnZW9tZXRyeSwgbWF0ZXJpYWwgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdTa2lubmVkTWVzaCc7XG5cblx0XHR0aGlzLmJpbmRNb2RlID0gXCJhdHRhY2hlZFwiO1xuXHRcdHRoaXMuYmluZE1hdHJpeCA9IG5ldyBNYXRyaXg0KCk7XG5cdFx0dGhpcy5iaW5kTWF0cml4SW52ZXJzZSA9IG5ldyBNYXRyaXg0KCk7XG5cblx0XHQvLyBpbml0IGJvbmVzXG5cblx0XHQvLyBUT0RPOiByZW1vdmUgYm9uZSBjcmVhdGlvbiBhcyB0aGVyZSBpcyBubyByZWFzb24gKG90aGVyIHRoYW5cblx0XHQvLyBjb252ZW5pZW5jZSkgZm9yIFRIUkVFLlNraW5uZWRNZXNoIHRvIGRvIHRoaXMuXG5cblx0XHR2YXIgYm9uZXMgPSBbXTtcblxuXHRcdGlmICggdGhpcy5nZW9tZXRyeSAmJiB0aGlzLmdlb21ldHJ5LmJvbmVzICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdHZhciBib25lLCBnYm9uZTtcblxuXHRcdFx0Zm9yICggdmFyIGIgPSAwLCBibCA9IHRoaXMuZ2VvbWV0cnkuYm9uZXMubGVuZ3RoOyBiIDwgYmw7ICsrIGIgKSB7XG5cblx0XHRcdFx0Z2JvbmUgPSB0aGlzLmdlb21ldHJ5LmJvbmVzWyBiIF07XG5cblx0XHRcdFx0Ym9uZSA9IG5ldyBCb25lKCk7XG5cdFx0XHRcdGJvbmVzLnB1c2goIGJvbmUgKTtcblxuXHRcdFx0XHRib25lLm5hbWUgPSBnYm9uZS5uYW1lO1xuXHRcdFx0XHRib25lLnBvc2l0aW9uLmZyb21BcnJheSggZ2JvbmUucG9zICk7XG5cdFx0XHRcdGJvbmUucXVhdGVybmlvbi5mcm9tQXJyYXkoIGdib25lLnJvdHEgKTtcblx0XHRcdFx0aWYgKCBnYm9uZS5zY2wgIT09IHVuZGVmaW5lZCApIGJvbmUuc2NhbGUuZnJvbUFycmF5KCBnYm9uZS5zY2wgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRmb3IgKCB2YXIgYiA9IDAsIGJsID0gdGhpcy5nZW9tZXRyeS5ib25lcy5sZW5ndGg7IGIgPCBibDsgKysgYiApIHtcblxuXHRcdFx0XHRnYm9uZSA9IHRoaXMuZ2VvbWV0cnkuYm9uZXNbIGIgXTtcblxuXHRcdFx0XHRpZiAoIGdib25lLnBhcmVudCAhPT0gLSAxICYmIGdib25lLnBhcmVudCAhPT0gbnVsbCAmJlxuXHRcdFx0XHRcdFx0Ym9uZXNbIGdib25lLnBhcmVudCBdICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRib25lc1sgZ2JvbmUucGFyZW50IF0uYWRkKCBib25lc1sgYiBdICk7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdHRoaXMuYWRkKCBib25lc1sgYiBdICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHR0aGlzLm5vcm1hbGl6ZVNraW5XZWlnaHRzKCk7XG5cblx0XHR0aGlzLnVwZGF0ZU1hdHJpeFdvcmxkKCB0cnVlICk7XG5cdFx0dGhpcy5iaW5kKCBuZXcgU2tlbGV0b24oIGJvbmVzLCB1bmRlZmluZWQsIHVzZVZlcnRleFRleHR1cmUgKSwgdGhpcy5tYXRyaXhXb3JsZCApO1xuXG5cdH1cblxuXG5cdFNraW5uZWRNZXNoLnByb3RvdHlwZSA9IE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIE1lc2gucHJvdG90eXBlICksIHtcblxuXHRcdGNvbnN0cnVjdG9yOiBTa2lubmVkTWVzaCxcblxuXHRcdGlzU2tpbm5lZE1lc2g6IHRydWUsXG5cblx0XHRiaW5kOiBmdW5jdGlvbiggc2tlbGV0b24sIGJpbmRNYXRyaXggKSB7XG5cblx0XHRcdHRoaXMuc2tlbGV0b24gPSBza2VsZXRvbjtcblxuXHRcdFx0aWYgKCBiaW5kTWF0cml4ID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0dGhpcy51cGRhdGVNYXRyaXhXb3JsZCggdHJ1ZSApO1xuXG5cdFx0XHRcdHRoaXMuc2tlbGV0b24uY2FsY3VsYXRlSW52ZXJzZXMoKTtcblxuXHRcdFx0XHRiaW5kTWF0cml4ID0gdGhpcy5tYXRyaXhXb3JsZDtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLmJpbmRNYXRyaXguY29weSggYmluZE1hdHJpeCApO1xuXHRcdFx0dGhpcy5iaW5kTWF0cml4SW52ZXJzZS5nZXRJbnZlcnNlKCBiaW5kTWF0cml4ICk7XG5cblx0XHR9LFxuXG5cdFx0cG9zZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR0aGlzLnNrZWxldG9uLnBvc2UoKTtcblxuXHRcdH0sXG5cblx0XHRub3JtYWxpemVTa2luV2VpZ2h0czogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRpZiAoIHRoaXMuZ2VvbWV0cnkgJiYgdGhpcy5nZW9tZXRyeS5pc0dlb21ldHJ5ICkge1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IHRoaXMuZ2VvbWV0cnkuc2tpbldlaWdodHMubGVuZ3RoOyBpICsrICkge1xuXG5cdFx0XHRcdFx0dmFyIHN3ID0gdGhpcy5nZW9tZXRyeS5za2luV2VpZ2h0c1sgaSBdO1xuXG5cdFx0XHRcdFx0dmFyIHNjYWxlID0gMS4wIC8gc3cubGVuZ3RoTWFuaGF0dGFuKCk7XG5cblx0XHRcdFx0XHRpZiAoIHNjYWxlICE9PSBJbmZpbml0eSApIHtcblxuXHRcdFx0XHRcdFx0c3cubXVsdGlwbHlTY2FsYXIoIHNjYWxlICk7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRzdy5zZXQoIDEsIDAsIDAsIDAgKTsgLy8gZG8gc29tZXRoaW5nIHJlYXNvbmFibGVcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdH0gZWxzZSBpZiAoIHRoaXMuZ2VvbWV0cnkgJiYgdGhpcy5nZW9tZXRyeS5pc0J1ZmZlckdlb21ldHJ5ICkge1xuXG5cdFx0XHRcdHZhciB2ZWMgPSBuZXcgVmVjdG9yNCgpO1xuXG5cdFx0XHRcdHZhciBza2luV2VpZ2h0ID0gdGhpcy5nZW9tZXRyeS5hdHRyaWJ1dGVzLnNraW5XZWlnaHQ7XG5cblx0XHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgc2tpbldlaWdodC5jb3VudDsgaSArKyApIHtcblxuXHRcdFx0XHRcdHZlYy54ID0gc2tpbldlaWdodC5nZXRYKCBpICk7XG5cdFx0XHRcdFx0dmVjLnkgPSBza2luV2VpZ2h0LmdldFkoIGkgKTtcblx0XHRcdFx0XHR2ZWMueiA9IHNraW5XZWlnaHQuZ2V0WiggaSApO1xuXHRcdFx0XHRcdHZlYy53ID0gc2tpbldlaWdodC5nZXRXKCBpICk7XG5cblx0XHRcdFx0XHR2YXIgc2NhbGUgPSAxLjAgLyB2ZWMubGVuZ3RoTWFuaGF0dGFuKCk7XG5cblx0XHRcdFx0XHRpZiAoIHNjYWxlICE9PSBJbmZpbml0eSApIHtcblxuXHRcdFx0XHRcdFx0dmVjLm11bHRpcGx5U2NhbGFyKCBzY2FsZSApO1xuXG5cdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0dmVjLnNldCggMSwgMCwgMCwgMCApOyAvLyBkbyBzb21ldGhpbmcgcmVhc29uYWJsZVxuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0c2tpbldlaWdodC5zZXRYWVpXKCBpLCB2ZWMueCwgdmVjLnksIHZlYy56LCB2ZWMudyApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0fSxcblxuXHRcdHVwZGF0ZU1hdHJpeFdvcmxkOiBmdW5jdGlvbiggZm9yY2UgKSB7XG5cblx0XHRcdE1lc2gucHJvdG90eXBlLnVwZGF0ZU1hdHJpeFdvcmxkLmNhbGwoIHRoaXMsIHRydWUgKTtcblxuXHRcdFx0aWYgKCB0aGlzLmJpbmRNb2RlID09PSBcImF0dGFjaGVkXCIgKSB7XG5cblx0XHRcdFx0dGhpcy5iaW5kTWF0cml4SW52ZXJzZS5nZXRJbnZlcnNlKCB0aGlzLm1hdHJpeFdvcmxkICk7XG5cblx0XHRcdH0gZWxzZSBpZiAoIHRoaXMuYmluZE1vZGUgPT09IFwiZGV0YWNoZWRcIiApIHtcblxuXHRcdFx0XHR0aGlzLmJpbmRNYXRyaXhJbnZlcnNlLmdldEludmVyc2UoIHRoaXMuYmluZE1hdHJpeCApO1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLlNraW5uZWRNZXNoIHVucmVjb2duaXplZCBiaW5kTW9kZTogJyArIHRoaXMuYmluZE1vZGUgKTtcblxuXHRcdFx0fVxuXG5cdFx0fSxcblxuXHRcdGNsb25lOiBmdW5jdGlvbigpIHtcblxuXHRcdFx0cmV0dXJuIG5ldyB0aGlzLmNvbnN0cnVjdG9yKCB0aGlzLmdlb21ldHJ5LCB0aGlzLm1hdGVyaWFsLCB0aGlzLnNrZWxldG9uLnVzZVZlcnRleFRleHR1cmUgKS5jb3B5KCB0aGlzICk7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKlxuXHQgKiBwYXJhbWV0ZXJzID0ge1xuXHQgKiAgY29sb3I6IDxoZXg+LFxuXHQgKiAgb3BhY2l0eTogPGZsb2F0Pixcblx0ICpcblx0ICogIGxpbmV3aWR0aDogPGZsb2F0Pixcblx0ICogIGxpbmVjYXA6IFwicm91bmRcIixcblx0ICogIGxpbmVqb2luOiBcInJvdW5kXCJcblx0ICogfVxuXHQgKi9cblxuXHRmdW5jdGlvbiBMaW5lQmFzaWNNYXRlcmlhbCggcGFyYW1ldGVycyApIHtcblxuXHRcdE1hdGVyaWFsLmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdMaW5lQmFzaWNNYXRlcmlhbCc7XG5cblx0XHR0aGlzLmNvbG9yID0gbmV3IENvbG9yKCAweGZmZmZmZiApO1xuXG5cdFx0dGhpcy5saW5ld2lkdGggPSAxO1xuXHRcdHRoaXMubGluZWNhcCA9ICdyb3VuZCc7XG5cdFx0dGhpcy5saW5lam9pbiA9ICdyb3VuZCc7XG5cblx0XHR0aGlzLmxpZ2h0cyA9IGZhbHNlO1xuXG5cdFx0dGhpcy5zZXRWYWx1ZXMoIHBhcmFtZXRlcnMgKTtcblxuXHR9XG5cblx0TGluZUJhc2ljTWF0ZXJpYWwucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggTWF0ZXJpYWwucHJvdG90eXBlICk7XG5cdExpbmVCYXNpY01hdGVyaWFsLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IExpbmVCYXNpY01hdGVyaWFsO1xuXG5cdExpbmVCYXNpY01hdGVyaWFsLnByb3RvdHlwZS5pc0xpbmVCYXNpY01hdGVyaWFsID0gdHJ1ZTtcblxuXHRMaW5lQmFzaWNNYXRlcmlhbC5wcm90b3R5cGUuY29weSA9IGZ1bmN0aW9uICggc291cmNlICkge1xuXG5cdFx0TWF0ZXJpYWwucHJvdG90eXBlLmNvcHkuY2FsbCggdGhpcywgc291cmNlICk7XG5cblx0XHR0aGlzLmNvbG9yLmNvcHkoIHNvdXJjZS5jb2xvciApO1xuXG5cdFx0dGhpcy5saW5ld2lkdGggPSBzb3VyY2UubGluZXdpZHRoO1xuXHRcdHRoaXMubGluZWNhcCA9IHNvdXJjZS5saW5lY2FwO1xuXHRcdHRoaXMubGluZWpvaW4gPSBzb3VyY2UubGluZWpvaW47XG5cblx0XHRyZXR1cm4gdGhpcztcblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBMaW5lKCBnZW9tZXRyeSwgbWF0ZXJpYWwsIG1vZGUgKSB7XG5cblx0XHRpZiAoIG1vZGUgPT09IDEgKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkxpbmU6IHBhcmFtZXRlciBUSFJFRS5MaW5lUGllY2VzIG5vIGxvbmdlciBzdXBwb3J0ZWQuIENyZWF0ZWQgVEhSRUUuTGluZVNlZ21lbnRzIGluc3RlYWQuJyApO1xuXHRcdFx0cmV0dXJuIG5ldyBMaW5lU2VnbWVudHMoIGdlb21ldHJ5LCBtYXRlcmlhbCApO1xuXG5cdFx0fVxuXG5cdFx0T2JqZWN0M0QuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ0xpbmUnO1xuXG5cdFx0dGhpcy5nZW9tZXRyeSA9IGdlb21ldHJ5ICE9PSB1bmRlZmluZWQgPyBnZW9tZXRyeSA6IG5ldyBCdWZmZXJHZW9tZXRyeSgpO1xuXHRcdHRoaXMubWF0ZXJpYWwgPSBtYXRlcmlhbCAhPT0gdW5kZWZpbmVkID8gbWF0ZXJpYWwgOiBuZXcgTGluZUJhc2ljTWF0ZXJpYWwoIHsgY29sb3I6IE1hdGgucmFuZG9tKCkgKiAweGZmZmZmZiB9ICk7XG5cblx0fVxuXG5cdExpbmUucHJvdG90eXBlID0gT2JqZWN0LmFzc2lnbiggT2JqZWN0LmNyZWF0ZSggT2JqZWN0M0QucHJvdG90eXBlICksIHtcblxuXHRcdGNvbnN0cnVjdG9yOiBMaW5lLFxuXG5cdFx0aXNMaW5lOiB0cnVlLFxuXG5cdFx0cmF5Y2FzdDogKCBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBpbnZlcnNlTWF0cml4ID0gbmV3IE1hdHJpeDQoKTtcblx0XHRcdHZhciByYXkgPSBuZXcgUmF5KCk7XG5cdFx0XHR2YXIgc3BoZXJlID0gbmV3IFNwaGVyZSgpO1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gcmF5Y2FzdCggcmF5Y2FzdGVyLCBpbnRlcnNlY3RzICkge1xuXG5cdFx0XHRcdHZhciBwcmVjaXNpb24gPSByYXljYXN0ZXIubGluZVByZWNpc2lvbjtcblx0XHRcdFx0dmFyIHByZWNpc2lvblNxID0gcHJlY2lzaW9uICogcHJlY2lzaW9uO1xuXG5cdFx0XHRcdHZhciBnZW9tZXRyeSA9IHRoaXMuZ2VvbWV0cnk7XG5cdFx0XHRcdHZhciBtYXRyaXhXb3JsZCA9IHRoaXMubWF0cml4V29ybGQ7XG5cblx0XHRcdFx0Ly8gQ2hlY2tpbmcgYm91bmRpbmdTcGhlcmUgZGlzdGFuY2UgdG8gcmF5XG5cblx0XHRcdFx0aWYgKCBnZW9tZXRyeS5ib3VuZGluZ1NwaGVyZSA9PT0gbnVsbCApIGdlb21ldHJ5LmNvbXB1dGVCb3VuZGluZ1NwaGVyZSgpO1xuXG5cdFx0XHRcdHNwaGVyZS5jb3B5KCBnZW9tZXRyeS5ib3VuZGluZ1NwaGVyZSApO1xuXHRcdFx0XHRzcGhlcmUuYXBwbHlNYXRyaXg0KCBtYXRyaXhXb3JsZCApO1xuXG5cdFx0XHRcdGlmICggcmF5Y2FzdGVyLnJheS5pbnRlcnNlY3RzU3BoZXJlKCBzcGhlcmUgKSA9PT0gZmFsc2UgKSByZXR1cm47XG5cblx0XHRcdFx0Ly9cblxuXHRcdFx0XHRpbnZlcnNlTWF0cml4LmdldEludmVyc2UoIG1hdHJpeFdvcmxkICk7XG5cdFx0XHRcdHJheS5jb3B5KCByYXljYXN0ZXIucmF5ICkuYXBwbHlNYXRyaXg0KCBpbnZlcnNlTWF0cml4ICk7XG5cblx0XHRcdFx0dmFyIHZTdGFydCA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHRcdHZhciB2RW5kID0gbmV3IFZlY3RvcjMoKTtcblx0XHRcdFx0dmFyIGludGVyU2VnbWVudCA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHRcdHZhciBpbnRlclJheSA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHRcdHZhciBzdGVwID0gKHRoaXMgJiYgdGhpcy5pc0xpbmVTZWdtZW50cykgPyAyIDogMTtcblxuXHRcdFx0XHRpZiAoIGdlb21ldHJ5LmlzQnVmZmVyR2VvbWV0cnkgKSB7XG5cblx0XHRcdFx0XHR2YXIgaW5kZXggPSBnZW9tZXRyeS5pbmRleDtcblx0XHRcdFx0XHR2YXIgYXR0cmlidXRlcyA9IGdlb21ldHJ5LmF0dHJpYnV0ZXM7XG5cdFx0XHRcdFx0dmFyIHBvc2l0aW9ucyA9IGF0dHJpYnV0ZXMucG9zaXRpb24uYXJyYXk7XG5cblx0XHRcdFx0XHRpZiAoIGluZGV4ICE9PSBudWxsICkge1xuXG5cdFx0XHRcdFx0XHR2YXIgaW5kaWNlcyA9IGluZGV4LmFycmF5O1xuXG5cdFx0XHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBpbmRpY2VzLmxlbmd0aCAtIDE7IGkgPCBsOyBpICs9IHN0ZXAgKSB7XG5cblx0XHRcdFx0XHRcdFx0dmFyIGEgPSBpbmRpY2VzWyBpIF07XG5cdFx0XHRcdFx0XHRcdHZhciBiID0gaW5kaWNlc1sgaSArIDEgXTtcblxuXHRcdFx0XHRcdFx0XHR2U3RhcnQuZnJvbUFycmF5KCBwb3NpdGlvbnMsIGEgKiAzICk7XG5cdFx0XHRcdFx0XHRcdHZFbmQuZnJvbUFycmF5KCBwb3NpdGlvbnMsIGIgKiAzICk7XG5cblx0XHRcdFx0XHRcdFx0dmFyIGRpc3RTcSA9IHJheS5kaXN0YW5jZVNxVG9TZWdtZW50KCB2U3RhcnQsIHZFbmQsIGludGVyUmF5LCBpbnRlclNlZ21lbnQgKTtcblxuXHRcdFx0XHRcdFx0XHRpZiAoIGRpc3RTcSA+IHByZWNpc2lvblNxICkgY29udGludWU7XG5cblx0XHRcdFx0XHRcdFx0aW50ZXJSYXkuYXBwbHlNYXRyaXg0KCB0aGlzLm1hdHJpeFdvcmxkICk7IC8vTW92ZSBiYWNrIHRvIHdvcmxkIHNwYWNlIGZvciBkaXN0YW5jZSBjYWxjdWxhdGlvblxuXG5cdFx0XHRcdFx0XHRcdHZhciBkaXN0YW5jZSA9IHJheWNhc3Rlci5yYXkub3JpZ2luLmRpc3RhbmNlVG8oIGludGVyUmF5ICk7XG5cblx0XHRcdFx0XHRcdFx0aWYgKCBkaXN0YW5jZSA8IHJheWNhc3Rlci5uZWFyIHx8IGRpc3RhbmNlID4gcmF5Y2FzdGVyLmZhciApIGNvbnRpbnVlO1xuXG5cdFx0XHRcdFx0XHRcdGludGVyc2VjdHMucHVzaCgge1xuXG5cdFx0XHRcdFx0XHRcdFx0ZGlzdGFuY2U6IGRpc3RhbmNlLFxuXHRcdFx0XHRcdFx0XHRcdC8vIFdoYXQgZG8gd2Ugd2FudD8gaW50ZXJzZWN0aW9uIHBvaW50IG9uIHRoZSByYXkgb3Igb24gdGhlIHNlZ21lbnQ/P1xuXHRcdFx0XHRcdFx0XHRcdC8vIHBvaW50OiByYXljYXN0ZXIucmF5LmF0KCBkaXN0YW5jZSApLFxuXHRcdFx0XHRcdFx0XHRcdHBvaW50OiBpbnRlclNlZ21lbnQuY2xvbmUoKS5hcHBseU1hdHJpeDQoIHRoaXMubWF0cml4V29ybGQgKSxcblx0XHRcdFx0XHRcdFx0XHRpbmRleDogaSxcblx0XHRcdFx0XHRcdFx0XHRmYWNlOiBudWxsLFxuXHRcdFx0XHRcdFx0XHRcdGZhY2VJbmRleDogbnVsbCxcblx0XHRcdFx0XHRcdFx0XHRvYmplY3Q6IHRoaXNcblxuXHRcdFx0XHRcdFx0XHR9ICk7XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IHBvc2l0aW9ucy5sZW5ndGggLyAzIC0gMTsgaSA8IGw7IGkgKz0gc3RlcCApIHtcblxuXHRcdFx0XHRcdFx0XHR2U3RhcnQuZnJvbUFycmF5KCBwb3NpdGlvbnMsIDMgKiBpICk7XG5cdFx0XHRcdFx0XHRcdHZFbmQuZnJvbUFycmF5KCBwb3NpdGlvbnMsIDMgKiBpICsgMyApO1xuXG5cdFx0XHRcdFx0XHRcdHZhciBkaXN0U3EgPSByYXkuZGlzdGFuY2VTcVRvU2VnbWVudCggdlN0YXJ0LCB2RW5kLCBpbnRlclJheSwgaW50ZXJTZWdtZW50ICk7XG5cblx0XHRcdFx0XHRcdFx0aWYgKCBkaXN0U3EgPiBwcmVjaXNpb25TcSApIGNvbnRpbnVlO1xuXG5cdFx0XHRcdFx0XHRcdGludGVyUmF5LmFwcGx5TWF0cml4NCggdGhpcy5tYXRyaXhXb3JsZCApOyAvL01vdmUgYmFjayB0byB3b3JsZCBzcGFjZSBmb3IgZGlzdGFuY2UgY2FsY3VsYXRpb25cblxuXHRcdFx0XHRcdFx0XHR2YXIgZGlzdGFuY2UgPSByYXljYXN0ZXIucmF5Lm9yaWdpbi5kaXN0YW5jZVRvKCBpbnRlclJheSApO1xuXG5cdFx0XHRcdFx0XHRcdGlmICggZGlzdGFuY2UgPCByYXljYXN0ZXIubmVhciB8fCBkaXN0YW5jZSA+IHJheWNhc3Rlci5mYXIgKSBjb250aW51ZTtcblxuXHRcdFx0XHRcdFx0XHRpbnRlcnNlY3RzLnB1c2goIHtcblxuXHRcdFx0XHRcdFx0XHRcdGRpc3RhbmNlOiBkaXN0YW5jZSxcblx0XHRcdFx0XHRcdFx0XHQvLyBXaGF0IGRvIHdlIHdhbnQ/IGludGVyc2VjdGlvbiBwb2ludCBvbiB0aGUgcmF5IG9yIG9uIHRoZSBzZWdtZW50Pz9cblx0XHRcdFx0XHRcdFx0XHQvLyBwb2ludDogcmF5Y2FzdGVyLnJheS5hdCggZGlzdGFuY2UgKSxcblx0XHRcdFx0XHRcdFx0XHRwb2ludDogaW50ZXJTZWdtZW50LmNsb25lKCkuYXBwbHlNYXRyaXg0KCB0aGlzLm1hdHJpeFdvcmxkICksXG5cdFx0XHRcdFx0XHRcdFx0aW5kZXg6IGksXG5cdFx0XHRcdFx0XHRcdFx0ZmFjZTogbnVsbCxcblx0XHRcdFx0XHRcdFx0XHRmYWNlSW5kZXg6IG51bGwsXG5cdFx0XHRcdFx0XHRcdFx0b2JqZWN0OiB0aGlzXG5cblx0XHRcdFx0XHRcdFx0fSApO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSBlbHNlIGlmICggZ2VvbWV0cnkuaXNHZW9tZXRyeSApIHtcblxuXHRcdFx0XHRcdHZhciB2ZXJ0aWNlcyA9IGdlb21ldHJ5LnZlcnRpY2VzO1xuXHRcdFx0XHRcdHZhciBuYlZlcnRpY2VzID0gdmVydGljZXMubGVuZ3RoO1xuXG5cdFx0XHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgbmJWZXJ0aWNlcyAtIDE7IGkgKz0gc3RlcCApIHtcblxuXHRcdFx0XHRcdFx0dmFyIGRpc3RTcSA9IHJheS5kaXN0YW5jZVNxVG9TZWdtZW50KCB2ZXJ0aWNlc1sgaSBdLCB2ZXJ0aWNlc1sgaSArIDEgXSwgaW50ZXJSYXksIGludGVyU2VnbWVudCApO1xuXG5cdFx0XHRcdFx0XHRpZiAoIGRpc3RTcSA+IHByZWNpc2lvblNxICkgY29udGludWU7XG5cblx0XHRcdFx0XHRcdGludGVyUmF5LmFwcGx5TWF0cml4NCggdGhpcy5tYXRyaXhXb3JsZCApOyAvL01vdmUgYmFjayB0byB3b3JsZCBzcGFjZSBmb3IgZGlzdGFuY2UgY2FsY3VsYXRpb25cblxuXHRcdFx0XHRcdFx0dmFyIGRpc3RhbmNlID0gcmF5Y2FzdGVyLnJheS5vcmlnaW4uZGlzdGFuY2VUbyggaW50ZXJSYXkgKTtcblxuXHRcdFx0XHRcdFx0aWYgKCBkaXN0YW5jZSA8IHJheWNhc3Rlci5uZWFyIHx8IGRpc3RhbmNlID4gcmF5Y2FzdGVyLmZhciApIGNvbnRpbnVlO1xuXG5cdFx0XHRcdFx0XHRpbnRlcnNlY3RzLnB1c2goIHtcblxuXHRcdFx0XHRcdFx0XHRkaXN0YW5jZTogZGlzdGFuY2UsXG5cdFx0XHRcdFx0XHRcdC8vIFdoYXQgZG8gd2Ugd2FudD8gaW50ZXJzZWN0aW9uIHBvaW50IG9uIHRoZSByYXkgb3Igb24gdGhlIHNlZ21lbnQ/P1xuXHRcdFx0XHRcdFx0XHQvLyBwb2ludDogcmF5Y2FzdGVyLnJheS5hdCggZGlzdGFuY2UgKSxcblx0XHRcdFx0XHRcdFx0cG9pbnQ6IGludGVyU2VnbWVudC5jbG9uZSgpLmFwcGx5TWF0cml4NCggdGhpcy5tYXRyaXhXb3JsZCApLFxuXHRcdFx0XHRcdFx0XHRpbmRleDogaSxcblx0XHRcdFx0XHRcdFx0ZmFjZTogbnVsbCxcblx0XHRcdFx0XHRcdFx0ZmFjZUluZGV4OiBudWxsLFxuXHRcdFx0XHRcdFx0XHRvYmplY3Q6IHRoaXNcblxuXHRcdFx0XHRcdFx0fSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0fTtcblxuXHRcdH0oKSApLFxuXG5cdFx0Y2xvbmU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIG5ldyB0aGlzLmNvbnN0cnVjdG9yKCB0aGlzLmdlb21ldHJ5LCB0aGlzLm1hdGVyaWFsICkuY29weSggdGhpcyApO1xuXG5cdFx0fVxuXG5cdH0gKTtcblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gTGluZVNlZ21lbnRzKCBnZW9tZXRyeSwgbWF0ZXJpYWwgKSB7XG5cblx0XHRMaW5lLmNhbGwoIHRoaXMsIGdlb21ldHJ5LCBtYXRlcmlhbCApO1xuXG5cdFx0dGhpcy50eXBlID0gJ0xpbmVTZWdtZW50cyc7XG5cblx0fVxuXG5cdExpbmVTZWdtZW50cy5wcm90b3R5cGUgPSBPYmplY3QuYXNzaWduKCBPYmplY3QuY3JlYXRlKCBMaW5lLnByb3RvdHlwZSApLCB7XG5cblx0XHRjb25zdHJ1Y3RvcjogTGluZVNlZ21lbnRzLFxuXG5cdFx0aXNMaW5lU2VnbWVudHM6IHRydWVcblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqXG5cdCAqIHBhcmFtZXRlcnMgPSB7XG5cdCAqICBjb2xvcjogPGhleD4sXG5cdCAqICBvcGFjaXR5OiA8ZmxvYXQ+LFxuXHQgKiAgbWFwOiBuZXcgVEhSRUUuVGV4dHVyZSggPEltYWdlPiApLFxuXHQgKlxuXHQgKiAgc2l6ZTogPGZsb2F0Pixcblx0ICogIHNpemVBdHRlbnVhdGlvbjogPGJvb2w+XG5cdCAqIH1cblx0ICovXG5cblx0ZnVuY3Rpb24gUG9pbnRzTWF0ZXJpYWwoIHBhcmFtZXRlcnMgKSB7XG5cblx0XHRNYXRlcmlhbC5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnUG9pbnRzTWF0ZXJpYWwnO1xuXG5cdFx0dGhpcy5jb2xvciA9IG5ldyBDb2xvciggMHhmZmZmZmYgKTtcblxuXHRcdHRoaXMubWFwID0gbnVsbDtcblxuXHRcdHRoaXMuc2l6ZSA9IDE7XG5cdFx0dGhpcy5zaXplQXR0ZW51YXRpb24gPSB0cnVlO1xuXG5cdFx0dGhpcy5saWdodHMgPSBmYWxzZTtcblxuXHRcdHRoaXMuc2V0VmFsdWVzKCBwYXJhbWV0ZXJzICk7XG5cblx0fVxuXG5cdFBvaW50c01hdGVyaWFsLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIE1hdGVyaWFsLnByb3RvdHlwZSApO1xuXHRQb2ludHNNYXRlcmlhbC5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBQb2ludHNNYXRlcmlhbDtcblxuXHRQb2ludHNNYXRlcmlhbC5wcm90b3R5cGUuaXNQb2ludHNNYXRlcmlhbCA9IHRydWU7XG5cblx0UG9pbnRzTWF0ZXJpYWwucHJvdG90eXBlLmNvcHkgPSBmdW5jdGlvbiAoIHNvdXJjZSApIHtcblxuXHRcdE1hdGVyaWFsLnByb3RvdHlwZS5jb3B5LmNhbGwoIHRoaXMsIHNvdXJjZSApO1xuXG5cdFx0dGhpcy5jb2xvci5jb3B5KCBzb3VyY2UuY29sb3IgKTtcblxuXHRcdHRoaXMubWFwID0gc291cmNlLm1hcDtcblxuXHRcdHRoaXMuc2l6ZSA9IHNvdXJjZS5zaXplO1xuXHRcdHRoaXMuc2l6ZUF0dGVudWF0aW9uID0gc291cmNlLnNpemVBdHRlbnVhdGlvbjtcblxuXHRcdHJldHVybiB0aGlzO1xuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFBvaW50cyggZ2VvbWV0cnksIG1hdGVyaWFsICkge1xuXG5cdFx0T2JqZWN0M0QuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ1BvaW50cyc7XG5cblx0XHR0aGlzLmdlb21ldHJ5ID0gZ2VvbWV0cnkgIT09IHVuZGVmaW5lZCA/IGdlb21ldHJ5IDogbmV3IEJ1ZmZlckdlb21ldHJ5KCk7XG5cdFx0dGhpcy5tYXRlcmlhbCA9IG1hdGVyaWFsICE9PSB1bmRlZmluZWQgPyBtYXRlcmlhbCA6IG5ldyBQb2ludHNNYXRlcmlhbCggeyBjb2xvcjogTWF0aC5yYW5kb20oKSAqIDB4ZmZmZmZmIH0gKTtcblxuXHR9XG5cblx0UG9pbnRzLnByb3RvdHlwZSA9IE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIE9iamVjdDNELnByb3RvdHlwZSApLCB7XG5cblx0XHRjb25zdHJ1Y3RvcjogUG9pbnRzLFxuXG5cdFx0aXNQb2ludHM6IHRydWUsXG5cblx0XHRyYXljYXN0OiAoIGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIGludmVyc2VNYXRyaXggPSBuZXcgTWF0cml4NCgpO1xuXHRcdFx0dmFyIHJheSA9IG5ldyBSYXkoKTtcblx0XHRcdHZhciBzcGhlcmUgPSBuZXcgU3BoZXJlKCk7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiByYXljYXN0KCByYXljYXN0ZXIsIGludGVyc2VjdHMgKSB7XG5cblx0XHRcdFx0dmFyIG9iamVjdCA9IHRoaXM7XG5cdFx0XHRcdHZhciBnZW9tZXRyeSA9IHRoaXMuZ2VvbWV0cnk7XG5cdFx0XHRcdHZhciBtYXRyaXhXb3JsZCA9IHRoaXMubWF0cml4V29ybGQ7XG5cdFx0XHRcdHZhciB0aHJlc2hvbGQgPSByYXljYXN0ZXIucGFyYW1zLlBvaW50cy50aHJlc2hvbGQ7XG5cblx0XHRcdFx0Ly8gQ2hlY2tpbmcgYm91bmRpbmdTcGhlcmUgZGlzdGFuY2UgdG8gcmF5XG5cblx0XHRcdFx0aWYgKCBnZW9tZXRyeS5ib3VuZGluZ1NwaGVyZSA9PT0gbnVsbCApIGdlb21ldHJ5LmNvbXB1dGVCb3VuZGluZ1NwaGVyZSgpO1xuXG5cdFx0XHRcdHNwaGVyZS5jb3B5KCBnZW9tZXRyeS5ib3VuZGluZ1NwaGVyZSApO1xuXHRcdFx0XHRzcGhlcmUuYXBwbHlNYXRyaXg0KCBtYXRyaXhXb3JsZCApO1xuXG5cdFx0XHRcdGlmICggcmF5Y2FzdGVyLnJheS5pbnRlcnNlY3RzU3BoZXJlKCBzcGhlcmUgKSA9PT0gZmFsc2UgKSByZXR1cm47XG5cblx0XHRcdFx0Ly9cblxuXHRcdFx0XHRpbnZlcnNlTWF0cml4LmdldEludmVyc2UoIG1hdHJpeFdvcmxkICk7XG5cdFx0XHRcdHJheS5jb3B5KCByYXljYXN0ZXIucmF5ICkuYXBwbHlNYXRyaXg0KCBpbnZlcnNlTWF0cml4ICk7XG5cblx0XHRcdFx0dmFyIGxvY2FsVGhyZXNob2xkID0gdGhyZXNob2xkIC8gKCAoIHRoaXMuc2NhbGUueCArIHRoaXMuc2NhbGUueSArIHRoaXMuc2NhbGUueiApIC8gMyApO1xuXHRcdFx0XHR2YXIgbG9jYWxUaHJlc2hvbGRTcSA9IGxvY2FsVGhyZXNob2xkICogbG9jYWxUaHJlc2hvbGQ7XG5cdFx0XHRcdHZhciBwb3NpdGlvbiA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdFx0ZnVuY3Rpb24gdGVzdFBvaW50KCBwb2ludCwgaW5kZXggKSB7XG5cblx0XHRcdFx0XHR2YXIgcmF5UG9pbnREaXN0YW5jZVNxID0gcmF5LmRpc3RhbmNlU3FUb1BvaW50KCBwb2ludCApO1xuXG5cdFx0XHRcdFx0aWYgKCByYXlQb2ludERpc3RhbmNlU3EgPCBsb2NhbFRocmVzaG9sZFNxICkge1xuXG5cdFx0XHRcdFx0XHR2YXIgaW50ZXJzZWN0UG9pbnQgPSByYXkuY2xvc2VzdFBvaW50VG9Qb2ludCggcG9pbnQgKTtcblx0XHRcdFx0XHRcdGludGVyc2VjdFBvaW50LmFwcGx5TWF0cml4NCggbWF0cml4V29ybGQgKTtcblxuXHRcdFx0XHRcdFx0dmFyIGRpc3RhbmNlID0gcmF5Y2FzdGVyLnJheS5vcmlnaW4uZGlzdGFuY2VUbyggaW50ZXJzZWN0UG9pbnQgKTtcblxuXHRcdFx0XHRcdFx0aWYgKCBkaXN0YW5jZSA8IHJheWNhc3Rlci5uZWFyIHx8IGRpc3RhbmNlID4gcmF5Y2FzdGVyLmZhciApIHJldHVybjtcblxuXHRcdFx0XHRcdFx0aW50ZXJzZWN0cy5wdXNoKCB7XG5cblx0XHRcdFx0XHRcdFx0ZGlzdGFuY2U6IGRpc3RhbmNlLFxuXHRcdFx0XHRcdFx0XHRkaXN0YW5jZVRvUmF5OiBNYXRoLnNxcnQoIHJheVBvaW50RGlzdGFuY2VTcSApLFxuXHRcdFx0XHRcdFx0XHRwb2ludDogaW50ZXJzZWN0UG9pbnQuY2xvbmUoKSxcblx0XHRcdFx0XHRcdFx0aW5kZXg6IGluZGV4LFxuXHRcdFx0XHRcdFx0XHRmYWNlOiBudWxsLFxuXHRcdFx0XHRcdFx0XHRvYmplY3Q6IG9iamVjdFxuXG5cdFx0XHRcdFx0XHR9ICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggZ2VvbWV0cnkuaXNCdWZmZXJHZW9tZXRyeSApIHtcblxuXHRcdFx0XHRcdHZhciBpbmRleCA9IGdlb21ldHJ5LmluZGV4O1xuXHRcdFx0XHRcdHZhciBhdHRyaWJ1dGVzID0gZ2VvbWV0cnkuYXR0cmlidXRlcztcblx0XHRcdFx0XHR2YXIgcG9zaXRpb25zID0gYXR0cmlidXRlcy5wb3NpdGlvbi5hcnJheTtcblxuXHRcdFx0XHRcdGlmICggaW5kZXggIT09IG51bGwgKSB7XG5cblx0XHRcdFx0XHRcdHZhciBpbmRpY2VzID0gaW5kZXguYXJyYXk7XG5cblx0XHRcdFx0XHRcdGZvciAoIHZhciBpID0gMCwgaWwgPSBpbmRpY2VzLmxlbmd0aDsgaSA8IGlsOyBpICsrICkge1xuXG5cdFx0XHRcdFx0XHRcdHZhciBhID0gaW5kaWNlc1sgaSBdO1xuXG5cdFx0XHRcdFx0XHRcdHBvc2l0aW9uLmZyb21BcnJheSggcG9zaXRpb25zLCBhICogMyApO1xuXG5cdFx0XHRcdFx0XHRcdHRlc3RQb2ludCggcG9zaXRpb24sIGEgKTtcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0Zm9yICggdmFyIGkgPSAwLCBsID0gcG9zaXRpb25zLmxlbmd0aCAvIDM7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdFx0XHRcdHBvc2l0aW9uLmZyb21BcnJheSggcG9zaXRpb25zLCBpICogMyApO1xuXG5cdFx0XHRcdFx0XHRcdHRlc3RQb2ludCggcG9zaXRpb24sIGkgKTtcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHR2YXIgdmVydGljZXMgPSBnZW9tZXRyeS52ZXJ0aWNlcztcblxuXHRcdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IHZlcnRpY2VzLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRcdHRlc3RQb2ludCggdmVydGljZXNbIGkgXSwgaSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0fTtcblxuXHRcdH0oKSApLFxuXG5cdFx0Y2xvbmU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIG5ldyB0aGlzLmNvbnN0cnVjdG9yKCB0aGlzLmdlb21ldHJ5LCB0aGlzLm1hdGVyaWFsICkuY29weSggdGhpcyApO1xuXG5cdFx0fVxuXG5cdH0gKTtcblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gR3JvdXAoKSB7XG5cblx0XHRPYmplY3QzRC5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnR3JvdXAnO1xuXG5cdH1cblxuXHRHcm91cC5wcm90b3R5cGUgPSBPYmplY3QuYXNzaWduKCBPYmplY3QuY3JlYXRlKCBPYmplY3QzRC5wcm90b3R5cGUgKSwge1xuXG5cdFx0Y29uc3RydWN0b3I6IEdyb3VwXG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBWaWRlb1RleHR1cmUoIHZpZGVvLCBtYXBwaW5nLCB3cmFwUywgd3JhcFQsIG1hZ0ZpbHRlciwgbWluRmlsdGVyLCBmb3JtYXQsIHR5cGUsIGFuaXNvdHJvcHkgKSB7XG5cblx0XHRUZXh0dXJlLmNhbGwoIHRoaXMsIHZpZGVvLCBtYXBwaW5nLCB3cmFwUywgd3JhcFQsIG1hZ0ZpbHRlciwgbWluRmlsdGVyLCBmb3JtYXQsIHR5cGUsIGFuaXNvdHJvcHkgKTtcblxuXHRcdHRoaXMuZ2VuZXJhdGVNaXBtYXBzID0gZmFsc2U7XG5cblx0XHR2YXIgc2NvcGUgPSB0aGlzO1xuXG5cdFx0ZnVuY3Rpb24gdXBkYXRlKCkge1xuXG5cdFx0XHRyZXF1ZXN0QW5pbWF0aW9uRnJhbWUoIHVwZGF0ZSApO1xuXG5cdFx0XHRpZiAoIHZpZGVvLnJlYWR5U3RhdGUgPj0gdmlkZW8uSEFWRV9DVVJSRU5UX0RBVEEgKSB7XG5cblx0XHRcdFx0c2NvcGUubmVlZHNVcGRhdGUgPSB0cnVlO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHR1cGRhdGUoKTtcblxuXHR9XG5cblx0VmlkZW9UZXh0dXJlLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIFRleHR1cmUucHJvdG90eXBlICk7XG5cdFZpZGVvVGV4dHVyZS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBWaWRlb1RleHR1cmU7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIENvbXByZXNzZWRUZXh0dXJlKCBtaXBtYXBzLCB3aWR0aCwgaGVpZ2h0LCBmb3JtYXQsIHR5cGUsIG1hcHBpbmcsIHdyYXBTLCB3cmFwVCwgbWFnRmlsdGVyLCBtaW5GaWx0ZXIsIGFuaXNvdHJvcHksIGVuY29kaW5nICkge1xuXG5cdFx0VGV4dHVyZS5jYWxsKCB0aGlzLCBudWxsLCBtYXBwaW5nLCB3cmFwUywgd3JhcFQsIG1hZ0ZpbHRlciwgbWluRmlsdGVyLCBmb3JtYXQsIHR5cGUsIGFuaXNvdHJvcHksIGVuY29kaW5nICk7XG5cblx0XHR0aGlzLmltYWdlID0geyB3aWR0aDogd2lkdGgsIGhlaWdodDogaGVpZ2h0IH07XG5cdFx0dGhpcy5taXBtYXBzID0gbWlwbWFwcztcblxuXHRcdC8vIG5vIGZsaXBwaW5nIGZvciBjdWJlIHRleHR1cmVzXG5cdFx0Ly8gKGFsc28gZmxpcHBpbmcgZG9lc24ndCB3b3JrIGZvciBjb21wcmVzc2VkIHRleHR1cmVzIClcblxuXHRcdHRoaXMuZmxpcFkgPSBmYWxzZTtcblxuXHRcdC8vIGNhbid0IGdlbmVyYXRlIG1pcG1hcHMgZm9yIGNvbXByZXNzZWQgdGV4dHVyZXNcblx0XHQvLyBtaXBzIG11c3QgYmUgZW1iZWRkZWQgaW4gRERTIGZpbGVzXG5cblx0XHR0aGlzLmdlbmVyYXRlTWlwbWFwcyA9IGZhbHNlO1xuXG5cdH1cblxuXHRDb21wcmVzc2VkVGV4dHVyZS5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBUZXh0dXJlLnByb3RvdHlwZSApO1xuXHRDb21wcmVzc2VkVGV4dHVyZS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBDb21wcmVzc2VkVGV4dHVyZTtcblxuXHRDb21wcmVzc2VkVGV4dHVyZS5wcm90b3R5cGUuaXNDb21wcmVzc2VkVGV4dHVyZSA9IHRydWU7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIENhbnZhc1RleHR1cmUoIGNhbnZhcywgbWFwcGluZywgd3JhcFMsIHdyYXBULCBtYWdGaWx0ZXIsIG1pbkZpbHRlciwgZm9ybWF0LCB0eXBlLCBhbmlzb3Ryb3B5ICkge1xuXG5cdFx0VGV4dHVyZS5jYWxsKCB0aGlzLCBjYW52YXMsIG1hcHBpbmcsIHdyYXBTLCB3cmFwVCwgbWFnRmlsdGVyLCBtaW5GaWx0ZXIsIGZvcm1hdCwgdHlwZSwgYW5pc290cm9weSApO1xuXG5cdFx0dGhpcy5uZWVkc1VwZGF0ZSA9IHRydWU7XG5cblx0fVxuXG5cdENhbnZhc1RleHR1cmUucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggVGV4dHVyZS5wcm90b3R5cGUgKTtcblx0Q2FudmFzVGV4dHVyZS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBDYW52YXNUZXh0dXJlO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIE1hdHQgRGVzTGF1cmllcnMgLyBAbWF0dGRlc2xcblx0ICogQGF1dGhvciBhdGl4IC8gYXJ0aHVyc2lsYmVyLmRlXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIERlcHRoVGV4dHVyZSggd2lkdGgsIGhlaWdodCwgdHlwZSwgbWFwcGluZywgd3JhcFMsIHdyYXBULCBtYWdGaWx0ZXIsIG1pbkZpbHRlciwgYW5pc290cm9weSwgZm9ybWF0ICkge1xuXG5cdFx0Zm9ybWF0ID0gZm9ybWF0ICE9PSB1bmRlZmluZWQgPyBmb3JtYXQgOiBEZXB0aEZvcm1hdDtcblxuXHRcdGlmICggZm9ybWF0ICE9PSBEZXB0aEZvcm1hdCAmJiBmb3JtYXQgIT09IERlcHRoU3RlbmNpbEZvcm1hdCApIHtcblxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCAnRGVwdGhUZXh0dXJlIGZvcm1hdCBtdXN0IGJlIGVpdGhlciBUSFJFRS5EZXB0aEZvcm1hdCBvciBUSFJFRS5EZXB0aFN0ZW5jaWxGb3JtYXQnIClcblxuXHRcdH1cblxuXHRcdGlmICggdHlwZSA9PT0gdW5kZWZpbmVkICYmIGZvcm1hdCA9PT0gRGVwdGhGb3JtYXQgKSB0eXBlID0gVW5zaWduZWRTaG9ydFR5cGU7XG5cdFx0aWYgKCB0eXBlID09PSB1bmRlZmluZWQgJiYgZm9ybWF0ID09PSBEZXB0aFN0ZW5jaWxGb3JtYXQgKSB0eXBlID0gVW5zaWduZWRJbnQyNDhUeXBlO1xuXG5cdFx0VGV4dHVyZS5jYWxsKCB0aGlzLCBudWxsLCBtYXBwaW5nLCB3cmFwUywgd3JhcFQsIG1hZ0ZpbHRlciwgbWluRmlsdGVyLCBmb3JtYXQsIHR5cGUsIGFuaXNvdHJvcHkgKTtcblxuXHRcdHRoaXMuaW1hZ2UgPSB7IHdpZHRoOiB3aWR0aCwgaGVpZ2h0OiBoZWlnaHQgfTtcblxuXHRcdHRoaXMubWFnRmlsdGVyID0gbWFnRmlsdGVyICE9PSB1bmRlZmluZWQgPyBtYWdGaWx0ZXIgOiBOZWFyZXN0RmlsdGVyO1xuXHRcdHRoaXMubWluRmlsdGVyID0gbWluRmlsdGVyICE9PSB1bmRlZmluZWQgPyBtaW5GaWx0ZXIgOiBOZWFyZXN0RmlsdGVyO1xuXG5cdFx0dGhpcy5mbGlwWSA9IGZhbHNlO1xuXHRcdHRoaXMuZ2VuZXJhdGVNaXBtYXBzXHQ9IGZhbHNlO1xuXG5cdH1cblxuXHREZXB0aFRleHR1cmUucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggVGV4dHVyZS5wcm90b3R5cGUgKTtcblx0RGVwdGhUZXh0dXJlLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IERlcHRoVGV4dHVyZTtcblx0RGVwdGhUZXh0dXJlLnByb3RvdHlwZS5pc0RlcHRoVGV4dHVyZSA9IHRydWU7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFdpcmVmcmFtZUdlb21ldHJ5KCBnZW9tZXRyeSApIHtcblxuXHRcdEJ1ZmZlckdlb21ldHJ5LmNhbGwoIHRoaXMgKTtcblxuXHRcdHZhciBlZGdlID0gWyAwLCAwIF0sIGhhc2ggPSB7fTtcblxuXHRcdGZ1bmN0aW9uIHNvcnRGdW5jdGlvbiggYSwgYiApIHtcblxuXHRcdFx0cmV0dXJuIGEgLSBiO1xuXG5cdFx0fVxuXG5cdFx0dmFyIGtleXMgPSBbICdhJywgJ2InLCAnYycgXTtcblxuXHRcdGlmICggZ2VvbWV0cnkgJiYgZ2VvbWV0cnkuaXNHZW9tZXRyeSApIHtcblxuXHRcdFx0dmFyIHZlcnRpY2VzID0gZ2VvbWV0cnkudmVydGljZXM7XG5cdFx0XHR2YXIgZmFjZXMgPSBnZW9tZXRyeS5mYWNlcztcblx0XHRcdHZhciBudW1FZGdlcyA9IDA7XG5cblx0XHRcdC8vIGFsbG9jYXRlIG1heGltYWwgc2l6ZVxuXHRcdFx0dmFyIGVkZ2VzID0gbmV3IFVpbnQzMkFycmF5KCA2ICogZmFjZXMubGVuZ3RoICk7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGZhY2VzLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0dmFyIGZhY2UgPSBmYWNlc1sgaSBdO1xuXG5cdFx0XHRcdGZvciAoIHZhciBqID0gMDsgaiA8IDM7IGogKysgKSB7XG5cblx0XHRcdFx0XHRlZGdlWyAwIF0gPSBmYWNlWyBrZXlzWyBqIF0gXTtcblx0XHRcdFx0XHRlZGdlWyAxIF0gPSBmYWNlWyBrZXlzWyAoIGogKyAxICkgJSAzIF0gXTtcblx0XHRcdFx0XHRlZGdlLnNvcnQoIHNvcnRGdW5jdGlvbiApO1xuXG5cdFx0XHRcdFx0dmFyIGtleSA9IGVkZ2UudG9TdHJpbmcoKTtcblxuXHRcdFx0XHRcdGlmICggaGFzaFsga2V5IF0gPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdFx0ZWRnZXNbIDIgKiBudW1FZGdlcyBdID0gZWRnZVsgMCBdO1xuXHRcdFx0XHRcdFx0ZWRnZXNbIDIgKiBudW1FZGdlcyArIDEgXSA9IGVkZ2VbIDEgXTtcblx0XHRcdFx0XHRcdGhhc2hbIGtleSBdID0gdHJ1ZTtcblx0XHRcdFx0XHRcdG51bUVkZ2VzICsrO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgY29vcmRzID0gbmV3IEZsb2F0MzJBcnJheSggbnVtRWRnZXMgKiAyICogMyApO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBudW1FZGdlczsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0Zm9yICggdmFyIGogPSAwOyBqIDwgMjsgaiArKyApIHtcblxuXHRcdFx0XHRcdHZhciB2ZXJ0ZXggPSB2ZXJ0aWNlc1sgZWRnZXMgWyAyICogaSArIGogXSBdO1xuXG5cdFx0XHRcdFx0dmFyIGluZGV4ID0gNiAqIGkgKyAzICogajtcblx0XHRcdFx0XHRjb29yZHNbIGluZGV4ICsgMCBdID0gdmVydGV4Lng7XG5cdFx0XHRcdFx0Y29vcmRzWyBpbmRleCArIDEgXSA9IHZlcnRleC55O1xuXHRcdFx0XHRcdGNvb3Jkc1sgaW5kZXggKyAyIF0gPSB2ZXJ0ZXguejtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0dGhpcy5hZGRBdHRyaWJ1dGUoICdwb3NpdGlvbicsIG5ldyBCdWZmZXJBdHRyaWJ1dGUoIGNvb3JkcywgMyApICk7XG5cblx0XHR9IGVsc2UgaWYgKCBnZW9tZXRyeSAmJiBnZW9tZXRyeS5pc0J1ZmZlckdlb21ldHJ5ICkge1xuXG5cdFx0XHRpZiAoIGdlb21ldHJ5LmluZGV4ICE9PSBudWxsICkge1xuXG5cdFx0XHRcdC8vIEluZGV4ZWQgQnVmZmVyR2VvbWV0cnlcblxuXHRcdFx0XHR2YXIgaW5kaWNlcyA9IGdlb21ldHJ5LmluZGV4LmFycmF5O1xuXHRcdFx0XHR2YXIgdmVydGljZXMgPSBnZW9tZXRyeS5hdHRyaWJ1dGVzLnBvc2l0aW9uO1xuXHRcdFx0XHR2YXIgZ3JvdXBzID0gZ2VvbWV0cnkuZ3JvdXBzO1xuXHRcdFx0XHR2YXIgbnVtRWRnZXMgPSAwO1xuXG5cdFx0XHRcdGlmICggZ3JvdXBzLmxlbmd0aCA9PT0gMCApIHtcblxuXHRcdFx0XHRcdGdlb21ldHJ5LmFkZEdyb3VwKCAwLCBpbmRpY2VzLmxlbmd0aCApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBhbGxvY2F0ZSBtYXhpbWFsIHNpemVcblx0XHRcdFx0dmFyIGVkZ2VzID0gbmV3IFVpbnQzMkFycmF5KCAyICogaW5kaWNlcy5sZW5ndGggKTtcblxuXHRcdFx0XHRmb3IgKCB2YXIgbyA9IDAsIG9sID0gZ3JvdXBzLmxlbmd0aDsgbyA8IG9sOyArKyBvICkge1xuXG5cdFx0XHRcdFx0dmFyIGdyb3VwID0gZ3JvdXBzWyBvIF07XG5cblx0XHRcdFx0XHR2YXIgc3RhcnQgPSBncm91cC5zdGFydDtcblx0XHRcdFx0XHR2YXIgY291bnQgPSBncm91cC5jb3VudDtcblxuXHRcdFx0XHRcdGZvciAoIHZhciBpID0gc3RhcnQsIGlsID0gc3RhcnQgKyBjb3VudDsgaSA8IGlsOyBpICs9IDMgKSB7XG5cblx0XHRcdFx0XHRcdGZvciAoIHZhciBqID0gMDsgaiA8IDM7IGogKysgKSB7XG5cblx0XHRcdFx0XHRcdFx0ZWRnZVsgMCBdID0gaW5kaWNlc1sgaSArIGogXTtcblx0XHRcdFx0XHRcdFx0ZWRnZVsgMSBdID0gaW5kaWNlc1sgaSArICggaiArIDEgKSAlIDMgXTtcblx0XHRcdFx0XHRcdFx0ZWRnZS5zb3J0KCBzb3J0RnVuY3Rpb24gKTtcblxuXHRcdFx0XHRcdFx0XHR2YXIga2V5ID0gZWRnZS50b1N0cmluZygpO1xuXG5cdFx0XHRcdFx0XHRcdGlmICggaGFzaFsga2V5IF0gPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdFx0XHRcdGVkZ2VzWyAyICogbnVtRWRnZXMgXSA9IGVkZ2VbIDAgXTtcblx0XHRcdFx0XHRcdFx0XHRlZGdlc1sgMiAqIG51bUVkZ2VzICsgMSBdID0gZWRnZVsgMSBdO1xuXHRcdFx0XHRcdFx0XHRcdGhhc2hbIGtleSBdID0gdHJ1ZTtcblx0XHRcdFx0XHRcdFx0XHRudW1FZGdlcyArKztcblxuXHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0dmFyIGNvb3JkcyA9IG5ldyBGbG9hdDMyQXJyYXkoIG51bUVkZ2VzICogMiAqIDMgKTtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBudW1FZGdlczsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRmb3IgKCB2YXIgaiA9IDA7IGogPCAyOyBqICsrICkge1xuXG5cdFx0XHRcdFx0XHR2YXIgaW5kZXggPSA2ICogaSArIDMgKiBqO1xuXHRcdFx0XHRcdFx0dmFyIGluZGV4MiA9IGVkZ2VzWyAyICogaSArIGogXTtcblxuXHRcdFx0XHRcdFx0Y29vcmRzWyBpbmRleCArIDAgXSA9IHZlcnRpY2VzLmdldFgoIGluZGV4MiApO1xuXHRcdFx0XHRcdFx0Y29vcmRzWyBpbmRleCArIDEgXSA9IHZlcnRpY2VzLmdldFkoIGluZGV4MiApO1xuXHRcdFx0XHRcdFx0Y29vcmRzWyBpbmRleCArIDIgXSA9IHZlcnRpY2VzLmdldFooIGluZGV4MiApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ3Bvc2l0aW9uJywgbmV3IEJ1ZmZlckF0dHJpYnV0ZSggY29vcmRzLCAzICkgKTtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHQvLyBub24taW5kZXhlZCBCdWZmZXJHZW9tZXRyeVxuXG5cdFx0XHRcdHZhciB2ZXJ0aWNlcyA9IGdlb21ldHJ5LmF0dHJpYnV0ZXMucG9zaXRpb24uYXJyYXk7XG5cdFx0XHRcdHZhciBudW1FZGdlcyA9IHZlcnRpY2VzLmxlbmd0aCAvIDM7XG5cdFx0XHRcdHZhciBudW1UcmlzID0gbnVtRWRnZXMgLyAzO1xuXG5cdFx0XHRcdHZhciBjb29yZHMgPSBuZXcgRmxvYXQzMkFycmF5KCBudW1FZGdlcyAqIDIgKiAzICk7XG5cblx0XHRcdFx0Zm9yICggdmFyIGkgPSAwLCBsID0gbnVtVHJpczsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRmb3IgKCB2YXIgaiA9IDA7IGogPCAzOyBqICsrICkge1xuXG5cdFx0XHRcdFx0XHR2YXIgaW5kZXggPSAxOCAqIGkgKyA2ICogajtcblxuXHRcdFx0XHRcdFx0dmFyIGluZGV4MSA9IDkgKiBpICsgMyAqIGo7XG5cdFx0XHRcdFx0XHRjb29yZHNbIGluZGV4ICsgMCBdID0gdmVydGljZXNbIGluZGV4MSBdO1xuXHRcdFx0XHRcdFx0Y29vcmRzWyBpbmRleCArIDEgXSA9IHZlcnRpY2VzWyBpbmRleDEgKyAxIF07XG5cdFx0XHRcdFx0XHRjb29yZHNbIGluZGV4ICsgMiBdID0gdmVydGljZXNbIGluZGV4MSArIDIgXTtcblxuXHRcdFx0XHRcdFx0dmFyIGluZGV4MiA9IDkgKiBpICsgMyAqICggKCBqICsgMSApICUgMyApO1xuXHRcdFx0XHRcdFx0Y29vcmRzWyBpbmRleCArIDMgXSA9IHZlcnRpY2VzWyBpbmRleDIgXTtcblx0XHRcdFx0XHRcdGNvb3Jkc1sgaW5kZXggKyA0IF0gPSB2ZXJ0aWNlc1sgaW5kZXgyICsgMSBdO1xuXHRcdFx0XHRcdFx0Y29vcmRzWyBpbmRleCArIDUgXSA9IHZlcnRpY2VzWyBpbmRleDIgKyAyIF07XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHRoaXMuYWRkQXR0cmlidXRlKCAncG9zaXRpb24nLCBuZXcgQnVmZmVyQXR0cmlidXRlKCBjb29yZHMsIDMgKSApO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0fVxuXG5cdFdpcmVmcmFtZUdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRXaXJlZnJhbWVHZW9tZXRyeS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBXaXJlZnJhbWVHZW9tZXRyeTtcblxuXHQvKipcblx0ICogQGF1dGhvciBNdWdlbjg3IC8gaHR0cHM6Ly9naXRodWIuY29tL011Z2VuODdcblx0ICpcblx0ICogUGFyYW1ldHJpYyBTdXJmYWNlcyBHZW9tZXRyeVxuXHQgKiBiYXNlZCBvbiB0aGUgYnJpbGxpYW50IGFydGljbGUgYnkgQHByaWRlb3V0IGh0dHA6Ly9wcmlkZW91dC5uZXQvYmxvZy8/cD00NFxuXHQgKi9cblxuXHRmdW5jdGlvbiBQYXJhbWV0cmljQnVmZmVyR2VvbWV0cnkoIGZ1bmMsIHNsaWNlcywgc3RhY2tzICkge1xuXG5cdFx0QnVmZmVyR2VvbWV0cnkuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ1BhcmFtZXRyaWNCdWZmZXJHZW9tZXRyeSc7XG5cblx0XHR0aGlzLnBhcmFtZXRlcnMgPSB7XG5cdFx0XHRmdW5jOiBmdW5jLFxuXHRcdFx0c2xpY2VzOiBzbGljZXMsXG5cdFx0XHRzdGFja3M6IHN0YWNrc1xuXHRcdH07XG5cblx0XHQvLyBnZW5lcmF0ZSB2ZXJ0aWNlcyBhbmQgdXZzXG5cblx0XHR2YXIgdmVydGljZXMgPSBbXTtcblx0XHR2YXIgdXZzID0gW107XG5cblx0XHR2YXIgaSwgaiwgcDtcblx0XHR2YXIgdSwgdjtcblxuXHRcdHZhciBzbGljZUNvdW50ID0gc2xpY2VzICsgMTtcblxuXHRcdGZvciAoIGkgPSAwOyBpIDw9IHN0YWNrczsgaSArKyApIHtcblxuXHRcdFx0diA9IGkgLyBzdGFja3M7XG5cblx0XHRcdGZvciAoIGogPSAwOyBqIDw9IHNsaWNlczsgaiArKyApIHtcblxuXHRcdFx0XHR1ID0gaiAvIHNsaWNlcztcblxuXHRcdFx0XHRwID0gZnVuYyggdSwgdiApO1xuXHRcdFx0XHR2ZXJ0aWNlcy5wdXNoKCBwLngsIHAueSwgcC56ICk7XG5cblx0XHRcdFx0dXZzLnB1c2goIHUsIHYgKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0Ly8gZ2VuZXJhdGUgaW5kaWNlc1xuXG5cdFx0dmFyIGluZGljZXMgPSBbXTtcblx0XHR2YXIgYSwgYiwgYywgZDtcblxuXHRcdGZvciAoIGkgPSAwOyBpIDwgc3RhY2tzOyBpICsrICkge1xuXG5cdFx0XHRmb3IgKCBqID0gMDsgaiA8IHNsaWNlczsgaiArKyApIHtcblxuXHRcdFx0XHRhID0gaSAqIHNsaWNlQ291bnQgKyBqO1xuXHRcdFx0XHRiID0gaSAqIHNsaWNlQ291bnQgKyBqICsgMTtcblx0XHRcdFx0YyA9ICggaSArIDEgKSAqIHNsaWNlQ291bnQgKyBqICsgMTtcblx0XHRcdFx0ZCA9ICggaSArIDEgKSAqIHNsaWNlQ291bnQgKyBqO1xuXG5cdFx0XHRcdC8vIGZhY2VzIG9uZSBhbmQgdHdvXG5cblx0XHRcdFx0aW5kaWNlcy5wdXNoKCBhLCBiLCBkICk7XG5cdFx0XHRcdGluZGljZXMucHVzaCggYiwgYywgZCApO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHQvLyBidWlsZCBnZW9tZXRyeVxuXG5cdFx0dGhpcy5zZXRJbmRleCggbmV3ICggaW5kaWNlcy5sZW5ndGggPiA2NTUzNSA/IFVpbnQzMkJ1ZmZlckF0dHJpYnV0ZSA6IFVpbnQxNkJ1ZmZlckF0dHJpYnV0ZSApKCBpbmRpY2VzLCAxICkgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ3Bvc2l0aW9uJywgbmV3IEZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUoIHZlcnRpY2VzLCAzICkgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ3V2JywgbmV3IEZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUoIHV2cywgMiApICk7XG5cblx0XHQvLyBnZW5lcmF0ZSBub3JtYWxzXG5cblx0XHR0aGlzLmNvbXB1dGVWZXJ0ZXhOb3JtYWxzKCk7XG5cblx0fVxuXG5cdFBhcmFtZXRyaWNCdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBCdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUgKTtcblx0UGFyYW1ldHJpY0J1ZmZlckdlb21ldHJ5LnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFBhcmFtZXRyaWNCdWZmZXJHZW9tZXRyeTtcblxuXHQvKipcblx0ICogQGF1dGhvciB6ejg1IC8gaHR0cHM6Ly9naXRodWIuY29tL3p6ODVcblx0ICpcblx0ICogUGFyYW1ldHJpYyBTdXJmYWNlcyBHZW9tZXRyeVxuXHQgKiBiYXNlZCBvbiB0aGUgYnJpbGxpYW50IGFydGljbGUgYnkgQHByaWRlb3V0IGh0dHA6Ly9wcmlkZW91dC5uZXQvYmxvZy8/cD00NFxuXHQgKi9cblxuXHRmdW5jdGlvbiBQYXJhbWV0cmljR2VvbWV0cnkoIGZ1bmMsIHNsaWNlcywgc3RhY2tzICkge1xuXG5cdFx0R2VvbWV0cnkuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ1BhcmFtZXRyaWNHZW9tZXRyeSc7XG5cblx0XHR0aGlzLnBhcmFtZXRlcnMgPSB7XG5cdFx0XHRmdW5jOiBmdW5jLFxuXHRcdFx0c2xpY2VzOiBzbGljZXMsXG5cdFx0XHRzdGFja3M6IHN0YWNrc1xuXHRcdH07XG5cblx0XHR0aGlzLmZyb21CdWZmZXJHZW9tZXRyeSggbmV3IFBhcmFtZXRyaWNCdWZmZXJHZW9tZXRyeSggZnVuYywgc2xpY2VzLCBzdGFja3MgKSApO1xuXHRcdHRoaXMubWVyZ2VWZXJ0aWNlcygpO1xuXG5cdH1cblxuXHRQYXJhbWV0cmljR2VvbWV0cnkucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggR2VvbWV0cnkucHJvdG90eXBlICk7XG5cdFBhcmFtZXRyaWNHZW9tZXRyeS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBQYXJhbWV0cmljR2VvbWV0cnk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgTXVnZW44NyAvIGh0dHBzOi8vZ2l0aHViLmNvbS9NdWdlbjg3XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFBvbHloZWRyb25CdWZmZXJHZW9tZXRyeSggdmVydGljZXMsIGluZGljZXMsIHJhZGl1cywgZGV0YWlsICkge1xuXG5cdFx0QnVmZmVyR2VvbWV0cnkuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ1BvbHloZWRyb25CdWZmZXJHZW9tZXRyeSc7XG5cblx0XHR0aGlzLnBhcmFtZXRlcnMgPSB7XG5cdFx0XHR2ZXJ0aWNlczogdmVydGljZXMsXG5cdFx0XHRpbmRpY2VzOiBpbmRpY2VzLFxuXHRcdFx0cmFkaXVzOiByYWRpdXMsXG5cdFx0XHRkZXRhaWw6IGRldGFpbFxuXHRcdH07XG5cblx0XHRyYWRpdXMgPSByYWRpdXMgfHwgMTtcblx0XHRkZXRhaWwgPSBkZXRhaWwgfHwgMDtcblxuXHRcdC8vIGRlZmF1bHQgYnVmZmVyIGRhdGFcblxuXHRcdHZhciB2ZXJ0ZXhCdWZmZXIgPSBbXTtcblx0XHR2YXIgdXZCdWZmZXIgPSBbXTtcblxuXHRcdC8vIHRoZSBzdWJkaXZpc2lvbiBjcmVhdGVzIHRoZSB2ZXJ0ZXggYnVmZmVyIGRhdGFcblxuXHRcdHN1YmRpdmlkZSggZGV0YWlsICk7XG5cblx0XHQvLyBhbGwgdmVydGljZXMgc2hvdWxkIGxpZSBvbiBhIGNvbmNlcHR1YWwgc3BoZXJlIHdpdGggYSBnaXZlbiByYWRpdXNcblxuXHRcdGFwcHBseVJhZGl1cyggcmFkaXVzICk7XG5cblx0XHQvLyBmaW5hbGx5LCBjcmVhdGUgdGhlIHV2IGRhdGFcblxuXHRcdGdlbmVyYXRlVVZzKCk7XG5cblx0XHQvLyBidWlsZCBub24taW5kZXhlZCBnZW9tZXRyeVxuXG5cdFx0dGhpcy5hZGRBdHRyaWJ1dGUoICdwb3NpdGlvbicsIG5ldyBGbG9hdDMyQnVmZmVyQXR0cmlidXRlKCB2ZXJ0ZXhCdWZmZXIsIDMgKSApO1xuXHRcdHRoaXMuYWRkQXR0cmlidXRlKCAnbm9ybWFsJywgbmV3IEZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUoIHZlcnRleEJ1ZmZlci5zbGljZSgpLCAzICkgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ3V2JywgbmV3IEZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUoIHV2QnVmZmVyLCAyICkgKTtcblx0XHR0aGlzLm5vcm1hbGl6ZU5vcm1hbHMoKTtcblxuXHRcdHRoaXMuYm91bmRpbmdTcGhlcmUgPSBuZXcgU3BoZXJlKCBuZXcgVmVjdG9yMygpLCByYWRpdXMgKTtcblxuXHRcdC8vIGhlbHBlciBmdW5jdGlvbnNcblxuXHRcdGZ1bmN0aW9uIHN1YmRpdmlkZSggZGV0YWlsICkge1xuXG5cdFx0XHR2YXIgYSA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHR2YXIgYiA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHR2YXIgYyA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdC8vIGl0ZXJhdGUgb3ZlciBhbGwgZmFjZXMgYW5kIGFwcGx5IGEgc3ViZGl2aXNvbiB3aXRoIHRoZSBnaXZlbiBkZXRhaWwgdmFsdWVcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgaW5kaWNlcy5sZW5ndGg7IGkgKz0gMyApIHtcblxuXHRcdFx0XHQvLyBnZXQgdGhlIHZlcnRpY2VzIG9mIHRoZSBmYWNlXG5cblx0XHRcdFx0Z2V0VmVydGV4QnlJbmRleCggaW5kaWNlc1sgaSArIDAgXSwgYSApO1xuXHRcdFx0XHRnZXRWZXJ0ZXhCeUluZGV4KCBpbmRpY2VzWyBpICsgMSBdLCBiICk7XG5cdFx0XHRcdGdldFZlcnRleEJ5SW5kZXgoIGluZGljZXNbIGkgKyAyIF0sIGMgKTtcblxuXHRcdFx0XHQvLyBwZXJmb3JtIHN1YmRpdmlzaW9uXG5cblx0XHRcdFx0c3ViZGl2aWRlRmFjZSggYSwgYiwgYywgZGV0YWlsICk7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHN1YmRpdmlkZUZhY2UoIGEsIGIsIGMsIGRldGFpbCApIHtcblxuXHRcdFx0dmFyIGNvbHMgPSBNYXRoLnBvdyggMiwgZGV0YWlsICk7XG5cblx0XHRcdC8vIHdlIHVzZSB0aGlzIG11bHRpZGltZW5zaW9uYWwgYXJyYXkgYXMgYSBkYXRhIHN0cnVjdHVyZSBmb3IgY3JlYXRpbmcgdGhlIHN1YmRpdmlzaW9uXG5cblx0XHRcdHZhciB2ID0gW107XG5cblx0XHRcdHZhciBpLCBqO1xuXG5cdFx0XHQvLyBjb25zdHJ1Y3QgYWxsIG9mIHRoZSB2ZXJ0aWNlcyBmb3IgdGhpcyBzdWJkaXZpc2lvblxuXG5cdFx0XHRmb3IgKCBpID0gMCA7IGkgPD0gY29sczsgaSArKyApIHtcblxuXHRcdFx0XHR2WyBpIF0gPSBbXTtcblxuXHRcdFx0XHR2YXIgYWogPSBhLmNsb25lKCkubGVycCggYywgaSAvIGNvbHMgKTtcblx0XHRcdFx0dmFyIGJqID0gYi5jbG9uZSgpLmxlcnAoIGMsIGkgLyBjb2xzICk7XG5cblx0XHRcdFx0dmFyIHJvd3MgPSBjb2xzIC0gaTtcblxuXHRcdFx0XHRmb3IgKCBqID0gMDsgaiA8PSByb3dzOyBqICsrICkge1xuXG5cdFx0XHRcdFx0aWYgKCBqID09PSAwICYmIGkgPT09IGNvbHMgKSB7XG5cblx0XHRcdFx0XHRcdHZbIGkgXVsgaiBdID0gYWo7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHR2WyBpIF1bIGogXSA9IGFqLmNsb25lKCkubGVycCggYmosIGogLyByb3dzICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdC8vIGNvbnN0cnVjdCBhbGwgb2YgdGhlIGZhY2VzXG5cblx0XHRcdGZvciAoIGkgPSAwOyBpIDwgY29scyA7IGkgKysgKSB7XG5cblx0XHRcdFx0Zm9yICggaiA9IDA7IGogPCAyICogKCBjb2xzIC0gaSApIC0gMTsgaiArKyApIHtcblxuXHRcdFx0XHRcdHZhciBrID0gTWF0aC5mbG9vciggaiAvIDIgKTtcblxuXHRcdFx0XHRcdGlmICggaiAlIDIgPT09IDAgKSB7XG5cblx0XHRcdFx0XHRcdHB1c2hWZXJ0ZXgoIHZbIGkgXVsgayArIDEgXSApO1xuXHRcdFx0XHRcdFx0cHVzaFZlcnRleCggdlsgaSArIDEgXVsgayBdICk7XG5cdFx0XHRcdFx0XHRwdXNoVmVydGV4KCB2WyBpIF1bIGsgXSApO1xuXG5cdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0cHVzaFZlcnRleCggdlsgaSBdWyBrICsgMSBdICk7XG5cdFx0XHRcdFx0XHRwdXNoVmVydGV4KCB2WyBpICsgMSBdWyBrICsgMSBdICk7XG5cdFx0XHRcdFx0XHRwdXNoVmVydGV4KCB2WyBpICsgMSBdWyBrIF0gKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIGFwcHBseVJhZGl1cyggcmFkaXVzICkge1xuXG5cdFx0XHR2YXIgdmVydGV4ID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0Ly8gaXRlcmF0ZSBvdmVyIHRoZSBlbnRpcmUgYnVmZmVyIGFuZCBhcHBseSB0aGUgcmFkaXVzIHRvIGVhY2ggdmVydGV4XG5cblx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IHZlcnRleEJ1ZmZlci5sZW5ndGg7IGkgKz0gMyApIHtcblxuXHRcdFx0XHR2ZXJ0ZXgueCA9IHZlcnRleEJ1ZmZlclsgaSArIDAgXTtcblx0XHRcdFx0dmVydGV4LnkgPSB2ZXJ0ZXhCdWZmZXJbIGkgKyAxIF07XG5cdFx0XHRcdHZlcnRleC56ID0gdmVydGV4QnVmZmVyWyBpICsgMiBdO1xuXG5cdFx0XHRcdHZlcnRleC5ub3JtYWxpemUoKS5tdWx0aXBseVNjYWxhciggcmFkaXVzICk7XG5cblx0XHRcdFx0dmVydGV4QnVmZmVyWyBpICsgMCBdID0gdmVydGV4Lng7XG5cdFx0XHRcdHZlcnRleEJ1ZmZlclsgaSArIDEgXSA9IHZlcnRleC55O1xuXHRcdFx0XHR2ZXJ0ZXhCdWZmZXJbIGkgKyAyIF0gPSB2ZXJ0ZXguejtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gZ2VuZXJhdGVVVnMoKSB7XG5cblx0XHRcdHZhciB2ZXJ0ZXggPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCB2ZXJ0ZXhCdWZmZXIubGVuZ3RoOyBpICs9IDMgKSB7XG5cblx0XHRcdFx0dmVydGV4LnggPSB2ZXJ0ZXhCdWZmZXJbIGkgKyAwIF07XG5cdFx0XHRcdHZlcnRleC55ID0gdmVydGV4QnVmZmVyWyBpICsgMSBdO1xuXHRcdFx0XHR2ZXJ0ZXgueiA9IHZlcnRleEJ1ZmZlclsgaSArIDIgXTtcblxuXHRcdFx0XHR2YXIgdSA9IGF6aW11dGgoIHZlcnRleCApIC8gMiAvIE1hdGguUEkgKyAwLjU7XG5cdFx0XHRcdHZhciB2ID0gaW5jbGluYXRpb24oIHZlcnRleCApIC8gTWF0aC5QSSArIDAuNTtcblx0XHRcdFx0dXZCdWZmZXIucHVzaCggdSwgMSAtIHYgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRjb3JyZWN0VVZzKCk7XG5cblx0XHRcdGNvcnJlY3RTZWFtKCk7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBjb3JyZWN0U2VhbSgpIHtcblxuXHRcdFx0Ly8gaGFuZGxlIGNhc2Ugd2hlbiBmYWNlIHN0cmFkZGxlcyB0aGUgc2VhbSwgc2VlICMzMjY5XG5cblx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IHV2QnVmZmVyLmxlbmd0aDsgaSArPSA2ICkge1xuXG5cdFx0XHRcdC8vIHV2IGRhdGEgb2YgYSBzaW5nbGUgZmFjZVxuXG5cdFx0XHRcdHZhciB4MCA9IHV2QnVmZmVyWyBpICsgMCBdO1xuXHRcdFx0XHR2YXIgeDEgPSB1dkJ1ZmZlclsgaSArIDIgXTtcblx0XHRcdFx0dmFyIHgyID0gdXZCdWZmZXJbIGkgKyA0IF07XG5cblx0XHRcdFx0dmFyIG1heCA9IE1hdGgubWF4KCB4MCwgeDEsIHgyICk7XG5cdFx0XHRcdHZhciBtaW4gPSBNYXRoLm1pbiggeDAsIHgxLCB4MiApO1xuXG5cdFx0XHRcdC8vIDAuOSBpcyBzb21ld2hhdCBhcmJpdHJhcnlcblxuXHRcdFx0XHRpZiAoIG1heCA+IDAuOSAmJiBtaW4gPCAwLjEgKSB7XG5cblx0XHRcdFx0XHRpZiAoIHgwIDwgMC4yICkgdXZCdWZmZXJbIGkgKyAwIF0gKz0gMTtcblx0XHRcdFx0XHRpZiAoIHgxIDwgMC4yICkgdXZCdWZmZXJbIGkgKyAyIF0gKz0gMTtcblx0XHRcdFx0XHRpZiAoIHgyIDwgMC4yICkgdXZCdWZmZXJbIGkgKyA0IF0gKz0gMTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHB1c2hWZXJ0ZXgoIHZlcnRleCApIHtcblxuXHRcdFx0dmVydGV4QnVmZmVyLnB1c2goIHZlcnRleC54LCB2ZXJ0ZXgueSwgdmVydGV4LnogKTtcblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIGdldFZlcnRleEJ5SW5kZXgoIGluZGV4LCB2ZXJ0ZXggKSB7XG5cblx0XHRcdHZhciBzdHJpZGUgPSBpbmRleCAqIDM7XG5cblx0XHRcdHZlcnRleC54ID0gdmVydGljZXNbIHN0cmlkZSArIDAgXTtcblx0XHRcdHZlcnRleC55ID0gdmVydGljZXNbIHN0cmlkZSArIDEgXTtcblx0XHRcdHZlcnRleC56ID0gdmVydGljZXNbIHN0cmlkZSArIDIgXTtcblxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIGNvcnJlY3RVVnMoKSB7XG5cblx0XHRcdHZhciBhID0gbmV3IFZlY3RvcjMoKTtcblx0XHRcdHZhciBiID0gbmV3IFZlY3RvcjMoKTtcblx0XHRcdHZhciBjID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0dmFyIGNlbnRyb2lkID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0dmFyIHV2QSA9IG5ldyBWZWN0b3IyKCk7XG5cdFx0XHR2YXIgdXZCID0gbmV3IFZlY3RvcjIoKTtcblx0XHRcdHZhciB1dkMgPSBuZXcgVmVjdG9yMigpO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGogPSAwOyBpIDwgdmVydGV4QnVmZmVyLmxlbmd0aDsgaSArPSA5LCBqICs9IDYgKSB7XG5cblx0XHRcdFx0YS5zZXQoIHZlcnRleEJ1ZmZlclsgaSArIDAgXSwgdmVydGV4QnVmZmVyWyBpICsgMSBdLCB2ZXJ0ZXhCdWZmZXJbIGkgKyAyIF0gKTtcblx0XHRcdFx0Yi5zZXQoIHZlcnRleEJ1ZmZlclsgaSArIDMgXSwgdmVydGV4QnVmZmVyWyBpICsgNCBdLCB2ZXJ0ZXhCdWZmZXJbIGkgKyA1IF0gKTtcblx0XHRcdFx0Yy5zZXQoIHZlcnRleEJ1ZmZlclsgaSArIDYgXSwgdmVydGV4QnVmZmVyWyBpICsgNyBdLCB2ZXJ0ZXhCdWZmZXJbIGkgKyA4IF0gKTtcblxuXHRcdFx0XHR1dkEuc2V0KCB1dkJ1ZmZlclsgaiArIDAgXSwgdXZCdWZmZXJbIGogKyAxIF0gKTtcblx0XHRcdFx0dXZCLnNldCggdXZCdWZmZXJbIGogKyAyIF0sIHV2QnVmZmVyWyBqICsgMyBdICk7XG5cdFx0XHRcdHV2Qy5zZXQoIHV2QnVmZmVyWyBqICsgNCBdLCB1dkJ1ZmZlclsgaiArIDUgXSApO1xuXG5cdFx0XHRcdGNlbnRyb2lkLmNvcHkoIGEgKS5hZGQoIGIgKS5hZGQoIGMgKS5kaXZpZGVTY2FsYXIoIDMgKTtcblxuXHRcdFx0XHR2YXIgYXppID0gYXppbXV0aCggY2VudHJvaWQgKTtcblxuXHRcdFx0XHRjb3JyZWN0VVYoIHV2QSwgaiArIDAsIGEsIGF6aSApO1xuXHRcdFx0XHRjb3JyZWN0VVYoIHV2QiwgaiArIDIsIGIsIGF6aSApO1xuXHRcdFx0XHRjb3JyZWN0VVYoIHV2QywgaiArIDQsIGMsIGF6aSApO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBjb3JyZWN0VVYoIHV2LCBzdHJpZGUsIHZlY3RvciwgYXppbXV0aCAgKSB7XG5cblx0XHRcdGlmICggKCBhemltdXRoIDwgMCApICYmICggdXYueCA9PT0gMSApICkge1xuXG5cdFx0XHRcdHV2QnVmZmVyWyBzdHJpZGUgXSA9ICB1di54IC0gMTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoICggdmVjdG9yLnggPT09IDAgKSAmJiAoIHZlY3Rvci56ID09PSAwICkgKSB7XG5cblx0XHRcdFx0dXZCdWZmZXJbIHN0cmlkZSBdID0gYXppbXV0aCAvIDIgLyBNYXRoLlBJICsgMC41O1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHQvLyBBbmdsZSBhcm91bmQgdGhlIFkgYXhpcywgY291bnRlci1jbG9ja3dpc2Ugd2hlbiBsb29raW5nIGZyb20gYWJvdmUuXG5cblx0XHRmdW5jdGlvbiBhemltdXRoKCB2ZWN0b3IgKSB7XG5cblx0XHRcdHJldHVybiBNYXRoLmF0YW4yKCB2ZWN0b3IueiwgLSB2ZWN0b3IueCApO1xuXG5cdFx0fVxuXG5cblx0XHQvLyBBbmdsZSBhYm92ZSB0aGUgWFogcGxhbmUuXG5cblx0XHRmdW5jdGlvbiBpbmNsaW5hdGlvbiggdmVjdG9yICkge1xuXG5cdFx0XHRyZXR1cm4gTWF0aC5hdGFuMiggLSB2ZWN0b3IueSwgTWF0aC5zcXJ0KCAoIHZlY3Rvci54ICogdmVjdG9yLnggKSArICggdmVjdG9yLnogKiB2ZWN0b3IueiApICkgKTtcblxuXHRcdH1cblxuXHR9XG5cblx0UG9seWhlZHJvbkJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRQb2x5aGVkcm9uQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gUG9seWhlZHJvbkJ1ZmZlckdlb21ldHJ5O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIE11Z2VuODcgLyBodHRwczovL2dpdGh1Yi5jb20vTXVnZW44N1xuXHQgKi9cblxuXHRmdW5jdGlvbiBUZXRyYWhlZHJvbkJ1ZmZlckdlb21ldHJ5KCByYWRpdXMsIGRldGFpbCApIHtcblxuXHRcdHZhciB2ZXJ0aWNlcyA9IFtcblx0XHRcdDEsICAxLCAgMSwgICAtIDEsIC0gMSwgIDEsICAgLSAxLCAgMSwgLSAxLCAgICAxLCAtIDEsIC0gMVxuXHRcdF07XG5cblx0XHR2YXIgaW5kaWNlcyA9IFtcblx0XHRcdDIsICAxLCAgMCwgICAgMCwgIDMsICAyLCAgICAxLCAgMywgIDAsICAgIDIsICAzLCAgMVxuXHRcdF07XG5cblx0XHRQb2x5aGVkcm9uQnVmZmVyR2VvbWV0cnkuY2FsbCggdGhpcywgdmVydGljZXMsIGluZGljZXMsIHJhZGl1cywgZGV0YWlsICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnVGV0cmFoZWRyb25CdWZmZXJHZW9tZXRyeSc7XG5cblx0XHR0aGlzLnBhcmFtZXRlcnMgPSB7XG5cdFx0XHRyYWRpdXM6IHJhZGl1cyxcblx0XHRcdGRldGFpbDogZGV0YWlsXG5cdFx0fTtcblxuXHR9XG5cblx0VGV0cmFoZWRyb25CdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBQb2x5aGVkcm9uQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlICk7XG5cdFRldHJhaGVkcm9uQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gVGV0cmFoZWRyb25CdWZmZXJHZW9tZXRyeTtcblxuXHQvKipcblx0ICogQGF1dGhvciB0aW1vdGh5cHJhdGxleSAvIGh0dHBzOi8vZ2l0aHViLmNvbS90aW1vdGh5cHJhdGxleVxuXHQgKi9cblxuXHRmdW5jdGlvbiBUZXRyYWhlZHJvbkdlb21ldHJ5KCByYWRpdXMsIGRldGFpbCApIHtcblxuXHRcdEdlb21ldHJ5LmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdUZXRyYWhlZHJvbkdlb21ldHJ5JztcblxuXHRcdHRoaXMucGFyYW1ldGVycyA9IHtcblx0XHRcdHJhZGl1czogcmFkaXVzLFxuXHRcdFx0ZGV0YWlsOiBkZXRhaWxcblx0XHR9O1xuXG5cdFx0dGhpcy5mcm9tQnVmZmVyR2VvbWV0cnkoIG5ldyBUZXRyYWhlZHJvbkJ1ZmZlckdlb21ldHJ5KCByYWRpdXMsIGRldGFpbCApICk7XG5cdFx0dGhpcy5tZXJnZVZlcnRpY2VzKCk7XG5cblx0fVxuXG5cdFRldHJhaGVkcm9uR2VvbWV0cnkucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggR2VvbWV0cnkucHJvdG90eXBlICk7XG5cdFRldHJhaGVkcm9uR2VvbWV0cnkucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gVGV0cmFoZWRyb25HZW9tZXRyeTtcblxuXHQvKipcblx0ICogQGF1dGhvciBNdWdlbjg3IC8gaHR0cHM6Ly9naXRodWIuY29tL011Z2VuODdcblx0ICovXG5cblx0ZnVuY3Rpb24gT2N0YWhlZHJvbkJ1ZmZlckdlb21ldHJ5KCByYWRpdXMsZGV0YWlsICkge1xuXG5cdFx0dmFyIHZlcnRpY2VzID0gW1xuXHRcdFx0MSwgMCwgMCwgICAtIDEsIDAsIDAsICAgIDAsIDEsIDAsICAgIDAsIC0gMSwgMCwgICAgMCwgMCwgMSwgICAgMCwgMCwgLSAxXG5cdFx0XTtcblxuXHRcdHZhciBpbmRpY2VzID0gW1xuXHRcdFx0MCwgMiwgNCwgICAgMCwgNCwgMywgICAgMCwgMywgNSwgICAgMCwgNSwgMiwgICAgMSwgMiwgNSwgICAgMSwgNSwgMywgICAgMSwgMywgNCwgICAgMSwgNCwgMlxuXHRcdF07XG5cblx0XHRQb2x5aGVkcm9uQnVmZmVyR2VvbWV0cnkuY2FsbCggdGhpcywgdmVydGljZXMsIGluZGljZXMsIHJhZGl1cywgZGV0YWlsICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnT2N0YWhlZHJvbkJ1ZmZlckdlb21ldHJ5JztcblxuXHRcdHRoaXMucGFyYW1ldGVycyA9IHtcblx0XHRcdHJhZGl1czogcmFkaXVzLFxuXHRcdFx0ZGV0YWlsOiBkZXRhaWxcblx0XHR9O1xuXG5cdH1cblxuXHRPY3RhaGVkcm9uQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggUG9seWhlZHJvbkJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRPY3RhaGVkcm9uQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gT2N0YWhlZHJvbkJ1ZmZlckdlb21ldHJ5O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIHRpbW90aHlwcmF0bGV5IC8gaHR0cHM6Ly9naXRodWIuY29tL3RpbW90aHlwcmF0bGV5XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIE9jdGFoZWRyb25HZW9tZXRyeSggcmFkaXVzLCBkZXRhaWwgKSB7XG5cblx0XHRHZW9tZXRyeS5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnT2N0YWhlZHJvbkdlb21ldHJ5JztcblxuXHRcdHRoaXMucGFyYW1ldGVycyA9IHtcblx0XHRcdHJhZGl1czogcmFkaXVzLFxuXHRcdFx0ZGV0YWlsOiBkZXRhaWxcblx0XHR9O1xuXG5cdFx0dGhpcy5mcm9tQnVmZmVyR2VvbWV0cnkoIG5ldyBPY3RhaGVkcm9uQnVmZmVyR2VvbWV0cnkoIHJhZGl1cywgZGV0YWlsICkgKTtcblx0XHR0aGlzLm1lcmdlVmVydGljZXMoKTtcblxuXHR9XG5cblx0T2N0YWhlZHJvbkdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRPY3RhaGVkcm9uR2VvbWV0cnkucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gT2N0YWhlZHJvbkdlb21ldHJ5O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIE11Z2VuODcgLyBodHRwczovL2dpdGh1Yi5jb20vTXVnZW44N1xuXHQgKi9cblxuXHRmdW5jdGlvbiBJY29zYWhlZHJvbkJ1ZmZlckdlb21ldHJ5KCByYWRpdXMsIGRldGFpbCApIHtcblxuXHRcdHZhciB0ID0gKCAxICsgTWF0aC5zcXJ0KCA1ICkgKSAvIDI7XG5cblx0XHR2YXIgdmVydGljZXMgPSBbXG5cdFx0XHQtIDEsICB0LCAgMCwgICAgMSwgIHQsICAwLCAgIC0gMSwgLSB0LCAgMCwgICAgMSwgLSB0LCAgMCxcblx0XHRcdCAwLCAtIDEsICB0LCAgICAwLCAgMSwgIHQsICAgIDAsIC0gMSwgLSB0LCAgICAwLCAgMSwgLSB0LFxuXHRcdFx0IHQsICAwLCAtIDEsICAgIHQsICAwLCAgMSwgICAtIHQsICAwLCAtIDEsICAgLSB0LCAgMCwgIDFcblx0XHRdO1xuXG5cdFx0dmFyIGluZGljZXMgPSBbXG5cdFx0XHQgMCwgMTEsICA1LCAgICAwLCAgNSwgIDEsICAgIDAsICAxLCAgNywgICAgMCwgIDcsIDEwLCAgICAwLCAxMCwgMTEsXG5cdFx0XHQgMSwgIDUsICA5LCAgICA1LCAxMSwgIDQsICAgMTEsIDEwLCAgMiwgICAxMCwgIDcsICA2LCAgICA3LCAgMSwgIDgsXG5cdFx0XHQgMywgIDksICA0LCAgICAzLCAgNCwgIDIsICAgIDMsICAyLCAgNiwgICAgMywgIDYsICA4LCAgICAzLCAgOCwgIDksXG5cdFx0XHQgNCwgIDksICA1LCAgICAyLCAgNCwgMTEsICAgIDYsICAyLCAxMCwgICAgOCwgIDYsICA3LCAgICA5LCAgOCwgIDFcblx0XHRdO1xuXG5cdFx0UG9seWhlZHJvbkJ1ZmZlckdlb21ldHJ5LmNhbGwoIHRoaXMsIHZlcnRpY2VzLCBpbmRpY2VzLCByYWRpdXMsIGRldGFpbCApO1xuXG5cdFx0dGhpcy50eXBlID0gJ0ljb3NhaGVkcm9uQnVmZmVyR2VvbWV0cnknO1xuXG5cdFx0dGhpcy5wYXJhbWV0ZXJzID0ge1xuXHRcdFx0cmFkaXVzOiByYWRpdXMsXG5cdFx0XHRkZXRhaWw6IGRldGFpbFxuXHRcdH07XG5cblx0fVxuXG5cdEljb3NhaGVkcm9uQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggUG9seWhlZHJvbkJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRJY29zYWhlZHJvbkJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IEljb3NhaGVkcm9uQnVmZmVyR2VvbWV0cnk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgdGltb3RoeXByYXRsZXkgLyBodHRwczovL2dpdGh1Yi5jb20vdGltb3RoeXByYXRsZXlcblx0ICovXG5cblx0ZnVuY3Rpb24gSWNvc2FoZWRyb25HZW9tZXRyeSggcmFkaXVzLCBkZXRhaWwgKSB7XG5cblx0IFx0R2VvbWV0cnkuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ0ljb3NhaGVkcm9uR2VvbWV0cnknO1xuXG5cdFx0dGhpcy5wYXJhbWV0ZXJzID0ge1xuXHRcdFx0cmFkaXVzOiByYWRpdXMsXG5cdFx0XHRkZXRhaWw6IGRldGFpbFxuXHRcdH07XG5cblx0XHR0aGlzLmZyb21CdWZmZXJHZW9tZXRyeSggbmV3IEljb3NhaGVkcm9uQnVmZmVyR2VvbWV0cnkoIHJhZGl1cywgZGV0YWlsICkgKTtcblx0XHR0aGlzLm1lcmdlVmVydGljZXMoKTtcblxuXHR9XG5cblx0SWNvc2FoZWRyb25HZW9tZXRyeS5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBHZW9tZXRyeS5wcm90b3R5cGUgKTtcblx0SWNvc2FoZWRyb25HZW9tZXRyeS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBJY29zYWhlZHJvbkdlb21ldHJ5O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIE11Z2VuODcgLyBodHRwczovL2dpdGh1Yi5jb20vTXVnZW44N1xuXHQgKi9cblxuXHRmdW5jdGlvbiBEb2RlY2FoZWRyb25CdWZmZXJHZW9tZXRyeSggcmFkaXVzLCBkZXRhaWwgKSB7XG5cblx0XHR2YXIgdCA9ICggMSArIE1hdGguc3FydCggNSApICkgLyAyO1xuXHRcdHZhciByID0gMSAvIHQ7XG5cblx0XHR2YXIgdmVydGljZXMgPSBbXG5cblx0XHRcdC8vICjCsTEsIMKxMSwgwrExKVxuXHRcdFx0LSAxLCAtIDEsIC0gMSwgICAgLSAxLCAtIDEsICAxLFxuXHRcdFx0LSAxLCAgMSwgLSAxLCAgICAtIDEsICAxLCAgMSxcblx0XHRcdCAxLCAtIDEsIC0gMSwgICAgIDEsIC0gMSwgIDEsXG5cdFx0XHQgMSwgIDEsIC0gMSwgICAgIDEsICAxLCAgMSxcblxuXHRcdFx0Ly8gKDAsIMKxMS/PhiwgwrHPhilcblx0XHRcdCAwLCAtIHIsIC0gdCwgICAgIDAsIC0gciwgIHQsXG5cdFx0XHQgMCwgIHIsIC0gdCwgICAgIDAsICByLCAgdCxcblxuXHRcdFx0Ly8gKMKxMS/PhiwgwrHPhiwgMClcblx0XHRcdC0gciwgLSB0LCAgMCwgICAgLSByLCAgdCwgIDAsXG5cdFx0XHQgciwgLSB0LCAgMCwgICAgIHIsICB0LCAgMCxcblxuXHRcdFx0Ly8gKMKxz4YsIDAsIMKxMS/Philcblx0XHRcdC0gdCwgIDAsIC0gciwgICAgIHQsICAwLCAtIHIsXG5cdFx0XHQtIHQsICAwLCAgciwgICAgIHQsICAwLCAgclxuXHRcdF07XG5cblx0XHR2YXIgaW5kaWNlcyA9IFtcblx0XHRcdCAzLCAxMSwgIDcsICAgICAgMywgIDcsIDE1LCAgICAgIDMsIDE1LCAxMyxcblx0XHRcdCA3LCAxOSwgMTcsICAgICAgNywgMTcsICA2LCAgICAgIDcsICA2LCAxNSxcblx0XHRcdDE3LCAgNCwgIDgsICAgICAxNywgIDgsIDEwLCAgICAgMTcsIDEwLCAgNixcblx0XHRcdCA4LCAgMCwgMTYsICAgICAgOCwgMTYsICAyLCAgICAgIDgsICAyLCAxMCxcblx0XHRcdCAwLCAxMiwgIDEsICAgICAgMCwgIDEsIDE4LCAgICAgIDAsIDE4LCAxNixcblx0XHRcdCA2LCAxMCwgIDIsICAgICAgNiwgIDIsIDEzLCAgICAgIDYsIDEzLCAxNSxcblx0XHRcdCAyLCAxNiwgMTgsICAgICAgMiwgMTgsICAzLCAgICAgIDIsICAzLCAxMyxcblx0XHRcdDE4LCAgMSwgIDksICAgICAxOCwgIDksIDExLCAgICAgMTgsIDExLCAgMyxcblx0XHRcdCA0LCAxNCwgMTIsICAgICAgNCwgMTIsICAwLCAgICAgIDQsICAwLCAgOCxcblx0XHRcdDExLCAgOSwgIDUsICAgICAxMSwgIDUsIDE5LCAgICAgMTEsIDE5LCAgNyxcblx0XHRcdDE5LCAgNSwgMTQsICAgICAxOSwgMTQsICA0LCAgICAgMTksICA0LCAxNyxcblx0XHRcdCAxLCAxMiwgMTQsICAgICAgMSwgMTQsICA1LCAgICAgIDEsICA1LCAgOVxuXHRcdF07XG5cblx0XHRQb2x5aGVkcm9uQnVmZmVyR2VvbWV0cnkuY2FsbCggdGhpcywgdmVydGljZXMsIGluZGljZXMsIHJhZGl1cywgZGV0YWlsICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnRG9kZWNhaGVkcm9uQnVmZmVyR2VvbWV0cnknO1xuXG5cdFx0dGhpcy5wYXJhbWV0ZXJzID0ge1xuXHRcdFx0cmFkaXVzOiByYWRpdXMsXG5cdFx0XHRkZXRhaWw6IGRldGFpbFxuXHRcdH07XG5cblx0fVxuXG5cdERvZGVjYWhlZHJvbkJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIFBvbHloZWRyb25CdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUgKTtcblx0RG9kZWNhaGVkcm9uQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gRG9kZWNhaGVkcm9uQnVmZmVyR2VvbWV0cnk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgQWJlIFBhem9zIC8gaHR0cHM6Ly9oYW1vaWQuY29tXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIERvZGVjYWhlZHJvbkdlb21ldHJ5KCByYWRpdXMsIGRldGFpbCApIHtcblxuXHRcdEdlb21ldHJ5LmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdEb2RlY2FoZWRyb25HZW9tZXRyeSc7XG5cblx0XHR0aGlzLnBhcmFtZXRlcnMgPSB7XG5cdFx0XHRyYWRpdXM6IHJhZGl1cyxcblx0XHRcdGRldGFpbDogZGV0YWlsXG5cdFx0fTtcblxuXHRcdHRoaXMuZnJvbUJ1ZmZlckdlb21ldHJ5KCBuZXcgRG9kZWNhaGVkcm9uQnVmZmVyR2VvbWV0cnkoIHJhZGl1cywgZGV0YWlsICkgKTtcblx0XHR0aGlzLm1lcmdlVmVydGljZXMoKTtcblxuXHR9XG5cblx0RG9kZWNhaGVkcm9uR2VvbWV0cnkucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggR2VvbWV0cnkucHJvdG90eXBlICk7XG5cdERvZGVjYWhlZHJvbkdlb21ldHJ5LnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IERvZGVjYWhlZHJvbkdlb21ldHJ5O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGNsb2Nrd29ya2dlZWsgLyBodHRwczovL2dpdGh1Yi5jb20vY2xvY2t3b3JrZ2Vla1xuXHQgKiBAYXV0aG9yIHRpbW90aHlwcmF0bGV5IC8gaHR0cHM6Ly9naXRodWIuY29tL3RpbW90aHlwcmF0bGV5XG5cdCAqIEBhdXRob3IgV2VzdExhbmdsZXkgLyBodHRwOi8vZ2l0aHViLmNvbS9XZXN0TGFuZ2xleVxuXHQqL1xuXG5cdGZ1bmN0aW9uIFBvbHloZWRyb25HZW9tZXRyeSggdmVydGljZXMsIGluZGljZXMsIHJhZGl1cywgZGV0YWlsICkge1xuXG5cdFx0R2VvbWV0cnkuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ1BvbHloZWRyb25HZW9tZXRyeSc7XG5cblx0XHR0aGlzLnBhcmFtZXRlcnMgPSB7XG5cdFx0XHR2ZXJ0aWNlczogdmVydGljZXMsXG5cdFx0XHRpbmRpY2VzOiBpbmRpY2VzLFxuXHRcdFx0cmFkaXVzOiByYWRpdXMsXG5cdFx0XHRkZXRhaWw6IGRldGFpbFxuXHRcdH07XG5cblx0XHR0aGlzLmZyb21CdWZmZXJHZW9tZXRyeSggbmV3IFBvbHloZWRyb25CdWZmZXJHZW9tZXRyeSggdmVydGljZXMsIGluZGljZXMsIHJhZGl1cywgZGV0YWlsICkgKTtcblx0XHR0aGlzLm1lcmdlVmVydGljZXMoKTtcblxuXHR9XG5cblx0UG9seWhlZHJvbkdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRQb2x5aGVkcm9uR2VvbWV0cnkucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gUG9seWhlZHJvbkdlb21ldHJ5O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIE11Z2VuODcgLyBodHRwczovL2dpdGh1Yi5jb20vTXVnZW44N1xuXHQgKlxuXHQgKiBDcmVhdGVzIGEgdHViZSB3aGljaCBleHRydWRlcyBhbG9uZyBhIDNkIHNwbGluZS5cblx0ICpcblx0ICovXG5cblx0ZnVuY3Rpb24gVHViZUJ1ZmZlckdlb21ldHJ5KCBwYXRoLCB0dWJ1bGFyU2VnbWVudHMsIHJhZGl1cywgcmFkaWFsU2VnbWVudHMsIGNsb3NlZCApIHtcblxuXHRcdEJ1ZmZlckdlb21ldHJ5LmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdUdWJlQnVmZmVyR2VvbWV0cnknO1xuXG5cdFx0dGhpcy5wYXJhbWV0ZXJzID0ge1xuXHRcdFx0cGF0aDogcGF0aCxcblx0XHRcdHR1YnVsYXJTZWdtZW50czogdHVidWxhclNlZ21lbnRzLFxuXHRcdFx0cmFkaXVzOiByYWRpdXMsXG5cdFx0XHRyYWRpYWxTZWdtZW50czogcmFkaWFsU2VnbWVudHMsXG5cdFx0XHRjbG9zZWQ6IGNsb3NlZFxuXHRcdH07XG5cblx0XHR0dWJ1bGFyU2VnbWVudHMgPSB0dWJ1bGFyU2VnbWVudHMgfHwgNjQ7XG5cdFx0cmFkaXVzID0gcmFkaXVzIHx8IDE7XG5cdFx0cmFkaWFsU2VnbWVudHMgPSByYWRpYWxTZWdtZW50cyB8fCA4O1xuXHRcdGNsb3NlZCA9IGNsb3NlZCB8fCBmYWxzZTtcblxuXHRcdHZhciBmcmFtZXMgPSBwYXRoLmNvbXB1dGVGcmVuZXRGcmFtZXMoIHR1YnVsYXJTZWdtZW50cywgY2xvc2VkICk7XG5cblx0XHQvLyBleHBvc2UgaW50ZXJuYWxzXG5cblx0XHR0aGlzLnRhbmdlbnRzID0gZnJhbWVzLnRhbmdlbnRzO1xuXHRcdHRoaXMubm9ybWFscyA9IGZyYW1lcy5ub3JtYWxzO1xuXHRcdHRoaXMuYmlub3JtYWxzID0gZnJhbWVzLmJpbm9ybWFscztcblxuXHRcdC8vIGhlbHBlciB2YXJpYWJsZXNcblxuXHRcdHZhciB2ZXJ0ZXggPSBuZXcgVmVjdG9yMygpO1xuXHRcdHZhciBub3JtYWwgPSBuZXcgVmVjdG9yMygpO1xuXHRcdHZhciB1diA9IG5ldyBWZWN0b3IyKCk7XG5cblx0XHR2YXIgaSwgajtcblxuXHRcdC8vIGJ1ZmZlclxuXG5cdFx0dmFyIHZlcnRpY2VzID0gW107XG5cdFx0dmFyIG5vcm1hbHMgPSBbXTtcblx0XHR2YXIgdXZzID0gW107XG5cdFx0dmFyIGluZGljZXMgPSBbXTtcblxuXHRcdC8vIGNyZWF0ZSBidWZmZXIgZGF0YVxuXG5cdFx0Z2VuZXJhdGVCdWZmZXJEYXRhKCk7XG5cblx0XHQvLyBidWlsZCBnZW9tZXRyeVxuXG5cdFx0dGhpcy5zZXRJbmRleCggbmV3ICggaW5kaWNlcy5sZW5ndGggPiA2NTUzNSA/IFVpbnQzMkJ1ZmZlckF0dHJpYnV0ZSA6IFVpbnQxNkJ1ZmZlckF0dHJpYnV0ZSApKCBpbmRpY2VzLCAxICkgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ3Bvc2l0aW9uJywgbmV3IEZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUoIHZlcnRpY2VzLCAzICkgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ25vcm1hbCcsIG5ldyBGbG9hdDMyQnVmZmVyQXR0cmlidXRlKCBub3JtYWxzLCAzICkgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ3V2JywgbmV3IEZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUoIHV2cywgMiApICk7XG5cblx0XHQvLyBmdW5jdGlvbnNcblxuXHRcdGZ1bmN0aW9uIGdlbmVyYXRlQnVmZmVyRGF0YSgpIHtcblxuXHRcdFx0Zm9yICggaSA9IDA7IGkgPCB0dWJ1bGFyU2VnbWVudHM7IGkgKysgKSB7XG5cblx0XHRcdFx0Z2VuZXJhdGVTZWdtZW50KCBpICk7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gaWYgdGhlIGdlb21ldHJ5IGlzIG5vdCBjbG9zZWQsIGdlbmVyYXRlIHRoZSBsYXN0IHJvdyBvZiB2ZXJ0aWNlcyBhbmQgbm9ybWFsc1xuXHRcdFx0Ly8gYXQgdGhlIHJlZ3VsYXIgcG9zaXRpb24gb24gdGhlIGdpdmVuIHBhdGhcblx0XHRcdC8vXG5cdFx0XHQvLyBpZiB0aGUgZ2VvbWV0cnkgaXMgY2xvc2VkLCBkdXBsaWNhdGUgdGhlIGZpcnN0IHJvdyBvZiB2ZXJ0aWNlcyBhbmQgbm9ybWFscyAodXZzIHdpbGwgZGlmZmVyKVxuXG5cdFx0XHRnZW5lcmF0ZVNlZ21lbnQoICggY2xvc2VkID09PSBmYWxzZSApID8gdHVidWxhclNlZ21lbnRzIDogMCApO1xuXG5cdFx0XHQvLyB1dnMgYXJlIGdlbmVyYXRlZCBpbiBhIHNlcGFyYXRlIGZ1bmN0aW9uLlxuXHRcdFx0Ly8gdGhpcyBtYWtlcyBpdCBlYXN5IGNvbXB1dGUgY29ycmVjdCB2YWx1ZXMgZm9yIGNsb3NlZCBnZW9tZXRyaWVzXG5cblx0XHRcdGdlbmVyYXRlVVZzKCk7XG5cblx0XHRcdC8vIGZpbmFsbHkgY3JlYXRlIGZhY2VzXG5cblx0XHRcdGdlbmVyYXRlSW5kaWNlcygpO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gZ2VuZXJhdGVTZWdtZW50KCBpICkge1xuXG5cdFx0XHQvLyB3ZSB1c2UgZ2V0UG9pbnRBdCB0byBzYW1wbGUgZXZlbmx5IGRpc3RyaWJ1dGVkIHBvaW50cyBmcm9tIHRoZSBnaXZlbiBwYXRoXG5cblx0XHRcdHZhciBQID0gcGF0aC5nZXRQb2ludEF0KCBpIC8gdHVidWxhclNlZ21lbnRzICk7XG5cblx0XHRcdC8vIHJldHJpZXZlIGNvcnJlc3BvbmRpbmcgbm9ybWFsIGFuZCBiaW5vcm1hbFxuXG5cdFx0XHR2YXIgTiA9IGZyYW1lcy5ub3JtYWxzWyBpIF07XG5cdFx0XHR2YXIgQiA9IGZyYW1lcy5iaW5vcm1hbHNbIGkgXTtcblxuXHRcdFx0Ly8gZ2VuZXJhdGUgbm9ybWFscyBhbmQgdmVydGljZXMgZm9yIHRoZSBjdXJyZW50IHNlZ21lbnRcblxuXHRcdFx0Zm9yICggaiA9IDA7IGogPD0gcmFkaWFsU2VnbWVudHM7IGogKysgKSB7XG5cblx0XHRcdFx0dmFyIHYgPSBqIC8gcmFkaWFsU2VnbWVudHMgKiBNYXRoLlBJICogMjtcblxuXHRcdFx0XHR2YXIgc2luID0gICBNYXRoLnNpbiggdiApO1xuXHRcdFx0XHR2YXIgY29zID0gLSBNYXRoLmNvcyggdiApO1xuXG5cdFx0XHRcdC8vIG5vcm1hbFxuXG5cdFx0XHRcdG5vcm1hbC54ID0gKCBjb3MgKiBOLnggKyBzaW4gKiBCLnggKTtcblx0XHRcdFx0bm9ybWFsLnkgPSAoIGNvcyAqIE4ueSArIHNpbiAqIEIueSApO1xuXHRcdFx0XHRub3JtYWwueiA9ICggY29zICogTi56ICsgc2luICogQi56ICk7XG5cdFx0XHRcdG5vcm1hbC5ub3JtYWxpemUoKTtcblxuXHRcdFx0XHRub3JtYWxzLnB1c2goIG5vcm1hbC54LCBub3JtYWwueSwgbm9ybWFsLnogKTtcblxuXHRcdFx0XHQvLyB2ZXJ0ZXhcblxuXHRcdFx0XHR2ZXJ0ZXgueCA9IFAueCArIHJhZGl1cyAqIG5vcm1hbC54O1xuXHRcdFx0XHR2ZXJ0ZXgueSA9IFAueSArIHJhZGl1cyAqIG5vcm1hbC55O1xuXHRcdFx0XHR2ZXJ0ZXgueiA9IFAueiArIHJhZGl1cyAqIG5vcm1hbC56O1xuXG5cdFx0XHRcdHZlcnRpY2VzLnB1c2goIHZlcnRleC54LCB2ZXJ0ZXgueSwgdmVydGV4LnogKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gZ2VuZXJhdGVJbmRpY2VzKCkge1xuXG5cdFx0XHRmb3IgKCBqID0gMTsgaiA8PSB0dWJ1bGFyU2VnbWVudHM7IGogKysgKSB7XG5cblx0XHRcdFx0Zm9yICggaSA9IDE7IGkgPD0gcmFkaWFsU2VnbWVudHM7IGkgKysgKSB7XG5cblx0XHRcdFx0XHR2YXIgYSA9ICggcmFkaWFsU2VnbWVudHMgKyAxICkgKiAoIGogLSAxICkgKyAoIGkgLSAxICk7XG5cdFx0XHRcdFx0dmFyIGIgPSAoIHJhZGlhbFNlZ21lbnRzICsgMSApICogaiArICggaSAtIDEgKTtcblx0XHRcdFx0XHR2YXIgYyA9ICggcmFkaWFsU2VnbWVudHMgKyAxICkgKiBqICsgaTtcblx0XHRcdFx0XHR2YXIgZCA9ICggcmFkaWFsU2VnbWVudHMgKyAxICkgKiAoIGogLSAxICkgKyBpO1xuXG5cdFx0XHRcdFx0Ly8gZmFjZXNcblxuXHRcdFx0XHRcdGluZGljZXMucHVzaCggYSwgYiwgZCApO1xuXHRcdFx0XHRcdGluZGljZXMucHVzaCggYiwgYywgZCApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gZ2VuZXJhdGVVVnMoKSB7XG5cblx0XHRcdGZvciAoIGkgPSAwOyBpIDw9IHR1YnVsYXJTZWdtZW50czsgaSArKyApIHtcblxuXHRcdFx0XHRmb3IgKCBqID0gMDsgaiA8PSByYWRpYWxTZWdtZW50czsgaiArKyApIHtcblxuXHRcdFx0XHRcdHV2LnggPSBpIC8gdHVidWxhclNlZ21lbnRzO1xuXHRcdFx0XHRcdHV2LnkgPSBqIC8gcmFkaWFsU2VnbWVudHM7XG5cblx0XHRcdFx0XHR1dnMucHVzaCggdXYueCwgdXYueSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdH1cblxuXHRUdWJlQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlICk7XG5cdFR1YmVCdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBUdWJlQnVmZmVyR2VvbWV0cnk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3Igb29zbW94aWVjb2RlIC8gaHR0cHM6Ly9naXRodWIuY29tL29vc21veGllY29kZVxuXHQgKiBAYXV0aG9yIFdlc3RMYW5nbGV5IC8gaHR0cHM6Ly9naXRodWIuY29tL1dlc3RMYW5nbGV5XG5cdCAqIEBhdXRob3Igeno4NSAvIGh0dHBzOi8vZ2l0aHViLmNvbS96ejg1XG5cdCAqIEBhdXRob3IgbWluaW5nb2xkIC8gaHR0cHM6Ly9naXRodWIuY29tL21pbmluZ29sZFxuXHQgKiBAYXV0aG9yIGpvbm9icjEgLyBodHRwczovL2dpdGh1Yi5jb20vam9ub2JyMVxuXHQgKlxuXHQgKiBDcmVhdGVzIGEgdHViZSB3aGljaCBleHRydWRlcyBhbG9uZyBhIDNkIHNwbGluZS5cblx0ICovXG5cblx0ZnVuY3Rpb24gVHViZUdlb21ldHJ5KCBwYXRoLCB0dWJ1bGFyU2VnbWVudHMsIHJhZGl1cywgcmFkaWFsU2VnbWVudHMsIGNsb3NlZCwgdGFwZXIgKSB7XG5cblx0XHRHZW9tZXRyeS5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnVHViZUdlb21ldHJ5JztcblxuXHRcdHRoaXMucGFyYW1ldGVycyA9IHtcblx0XHRcdHBhdGg6IHBhdGgsXG5cdFx0XHR0dWJ1bGFyU2VnbWVudHM6IHR1YnVsYXJTZWdtZW50cyxcblx0XHRcdHJhZGl1czogcmFkaXVzLFxuXHRcdFx0cmFkaWFsU2VnbWVudHM6IHJhZGlhbFNlZ21lbnRzLFxuXHRcdFx0Y2xvc2VkOiBjbG9zZWRcblx0XHR9O1xuXG5cdFx0aWYgKCB0YXBlciAhPT0gdW5kZWZpbmVkICkgY29uc29sZS53YXJuKCAnVEhSRUUuVHViZUdlb21ldHJ5OiB0YXBlciBoYXMgYmVlbiByZW1vdmVkLicgKTtcblxuXHRcdHZhciBidWZmZXJHZW9tZXRyeSA9IG5ldyBUdWJlQnVmZmVyR2VvbWV0cnkoIHBhdGgsIHR1YnVsYXJTZWdtZW50cywgcmFkaXVzLCByYWRpYWxTZWdtZW50cywgY2xvc2VkICk7XG5cblx0XHQvLyBleHBvc2UgaW50ZXJuYWxzXG5cblx0XHR0aGlzLnRhbmdlbnRzID0gYnVmZmVyR2VvbWV0cnkudGFuZ2VudHM7XG5cdFx0dGhpcy5ub3JtYWxzID0gYnVmZmVyR2VvbWV0cnkubm9ybWFscztcblx0XHR0aGlzLmJpbm9ybWFscyA9IGJ1ZmZlckdlb21ldHJ5LmJpbm9ybWFscztcblxuXHRcdC8vIGNyZWF0ZSBnZW9tZXRyeVxuXG5cdFx0dGhpcy5mcm9tQnVmZmVyR2VvbWV0cnkoIGJ1ZmZlckdlb21ldHJ5ICk7XG5cdFx0dGhpcy5tZXJnZVZlcnRpY2VzKCk7XG5cblx0fVxuXG5cdFR1YmVHZW9tZXRyeS5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBHZW9tZXRyeS5wcm90b3R5cGUgKTtcblx0VHViZUdlb21ldHJ5LnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFR1YmVHZW9tZXRyeTtcblxuXHQvKipcblx0ICogQGF1dGhvciBNdWdlbjg3IC8gaHR0cHM6Ly9naXRodWIuY29tL011Z2VuODdcblx0ICpcblx0ICogc2VlOiBodHRwOi8vd3d3LmJsYWNrcGF3bi5jb20vdGV4dHMvcHF0b3J1cy9cblx0ICovXG5cdGZ1bmN0aW9uIFRvcnVzS25vdEJ1ZmZlckdlb21ldHJ5KCByYWRpdXMsIHR1YmUsIHR1YnVsYXJTZWdtZW50cywgcmFkaWFsU2VnbWVudHMsIHAsIHEgKSB7XG5cblx0XHRCdWZmZXJHZW9tZXRyeS5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnVG9ydXNLbm90QnVmZmVyR2VvbWV0cnknO1xuXG5cdFx0dGhpcy5wYXJhbWV0ZXJzID0ge1xuXHRcdFx0cmFkaXVzOiByYWRpdXMsXG5cdFx0XHR0dWJlOiB0dWJlLFxuXHRcdFx0dHVidWxhclNlZ21lbnRzOiB0dWJ1bGFyU2VnbWVudHMsXG5cdFx0XHRyYWRpYWxTZWdtZW50czogcmFkaWFsU2VnbWVudHMsXG5cdFx0XHRwOiBwLFxuXHRcdFx0cTogcVxuXHRcdH07XG5cblx0XHRyYWRpdXMgPSByYWRpdXMgfHwgMTAwO1xuXHRcdHR1YmUgPSB0dWJlIHx8IDQwO1xuXHRcdHR1YnVsYXJTZWdtZW50cyA9IE1hdGguZmxvb3IoIHR1YnVsYXJTZWdtZW50cyApIHx8IDY0O1xuXHRcdHJhZGlhbFNlZ21lbnRzID0gTWF0aC5mbG9vciggcmFkaWFsU2VnbWVudHMgKSB8fCA4O1xuXHRcdHAgPSBwIHx8IDI7XG5cdFx0cSA9IHEgfHwgMztcblxuXHRcdC8vIHVzZWQgdG8gY2FsY3VsYXRlIGJ1ZmZlciBsZW5ndGhcblx0XHR2YXIgdmVydGV4Q291bnQgPSAoICggcmFkaWFsU2VnbWVudHMgKyAxICkgKiAoIHR1YnVsYXJTZWdtZW50cyArIDEgKSApO1xuXHRcdHZhciBpbmRleENvdW50ID0gcmFkaWFsU2VnbWVudHMgKiB0dWJ1bGFyU2VnbWVudHMgKiAyICogMztcblxuXHRcdC8vIGJ1ZmZlcnNcblx0XHR2YXIgaW5kaWNlcyA9IG5ldyBCdWZmZXJBdHRyaWJ1dGUoIG5ldyAoIGluZGV4Q291bnQgPiA2NTUzNSA/IFVpbnQzMkFycmF5IDogVWludDE2QXJyYXkgKSggaW5kZXhDb3VudCApICwgMSApO1xuXHRcdHZhciB2ZXJ0aWNlcyA9IG5ldyBCdWZmZXJBdHRyaWJ1dGUoIG5ldyBGbG9hdDMyQXJyYXkoIHZlcnRleENvdW50ICogMyApLCAzICk7XG5cdFx0dmFyIG5vcm1hbHMgPSBuZXcgQnVmZmVyQXR0cmlidXRlKCBuZXcgRmxvYXQzMkFycmF5KCB2ZXJ0ZXhDb3VudCAqIDMgKSwgMyApO1xuXHRcdHZhciB1dnMgPSBuZXcgQnVmZmVyQXR0cmlidXRlKCBuZXcgRmxvYXQzMkFycmF5KCB2ZXJ0ZXhDb3VudCAqIDIgKSwgMiApO1xuXG5cdFx0Ly8gaGVscGVyIHZhcmlhYmxlc1xuXHRcdHZhciBpLCBqLCBpbmRleCA9IDAsIGluZGV4T2Zmc2V0ID0gMDtcblxuXHRcdHZhciB2ZXJ0ZXggPSBuZXcgVmVjdG9yMygpO1xuXHRcdHZhciBub3JtYWwgPSBuZXcgVmVjdG9yMygpO1xuXHRcdHZhciB1diA9IG5ldyBWZWN0b3IyKCk7XG5cblx0XHR2YXIgUDEgPSBuZXcgVmVjdG9yMygpO1xuXHRcdHZhciBQMiA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHR2YXIgQiA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0dmFyIFQgPSBuZXcgVmVjdG9yMygpO1xuXHRcdHZhciBOID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdC8vIGdlbmVyYXRlIHZlcnRpY2VzLCBub3JtYWxzIGFuZCB1dnNcblxuXHRcdGZvciAoIGkgPSAwOyBpIDw9IHR1YnVsYXJTZWdtZW50czsgKysgaSApIHtcblxuXHRcdFx0Ly8gdGhlIHJhZGlhbiBcInVcIiBpcyB1c2VkIHRvIGNhbGN1bGF0ZSB0aGUgcG9zaXRpb24gb24gdGhlIHRvcnVzIGN1cnZlIG9mIHRoZSBjdXJyZW50IHR1YnVsYXIgc2VnZW1lbnRcblxuXHRcdFx0dmFyIHUgPSBpIC8gdHVidWxhclNlZ21lbnRzICogcCAqIE1hdGguUEkgKiAyO1xuXG5cdFx0XHQvLyBub3cgd2UgY2FsY3VsYXRlIHR3byBwb2ludHMuIFAxIGlzIG91ciBjdXJyZW50IHBvc2l0aW9uIG9uIHRoZSBjdXJ2ZSwgUDIgaXMgYSBsaXR0bGUgZmFydGhlciBhaGVhZC5cblx0XHRcdC8vIHRoZXNlIHBvaW50cyBhcmUgdXNlZCB0byBjcmVhdGUgYSBzcGVjaWFsIFwiY29vcmRpbmF0ZSBzcGFjZVwiLCB3aGljaCBpcyBuZWNlc3NhcnkgdG8gY2FsY3VsYXRlIHRoZSBjb3JyZWN0IHZlcnRleCBwb3NpdGlvbnNcblxuXHRcdFx0Y2FsY3VsYXRlUG9zaXRpb25PbkN1cnZlKCB1LCBwLCBxLCByYWRpdXMsIFAxICk7XG5cdFx0XHRjYWxjdWxhdGVQb3NpdGlvbk9uQ3VydmUoIHUgKyAwLjAxLCBwLCBxLCByYWRpdXMsIFAyICk7XG5cblx0XHRcdC8vIGNhbGN1bGF0ZSBvcnRob25vcm1hbCBiYXNpc1xuXG5cdFx0XHRULnN1YlZlY3RvcnMoIFAyLCBQMSApO1xuXHRcdFx0Ti5hZGRWZWN0b3JzKCBQMiwgUDEgKTtcblx0XHRcdEIuY3Jvc3NWZWN0b3JzKCBULCBOICk7XG5cdFx0XHROLmNyb3NzVmVjdG9ycyggQiwgVCApO1xuXG5cdFx0XHQvLyBub3JtYWxpemUgQiwgTi4gVCBjYW4gYmUgaWdub3JlZCwgd2UgZG9uJ3QgdXNlIGl0XG5cblx0XHRcdEIubm9ybWFsaXplKCk7XG5cdFx0XHROLm5vcm1hbGl6ZSgpO1xuXG5cdFx0XHRmb3IgKCBqID0gMDsgaiA8PSByYWRpYWxTZWdtZW50czsgKysgaiApIHtcblxuXHRcdFx0XHQvLyBub3cgY2FsY3VsYXRlIHRoZSB2ZXJ0aWNlcy4gdGhleSBhcmUgbm90aGluZyBtb3JlIHRoYW4gYW4gZXh0cnVzaW9uIG9mIHRoZSB0b3J1cyBjdXJ2ZS5cblx0XHRcdFx0Ly8gYmVjYXVzZSB3ZSBleHRydWRlIGEgc2hhcGUgaW4gdGhlIHh5LXBsYW5lLCB0aGVyZSBpcyBubyBuZWVkIHRvIGNhbGN1bGF0ZSBhIHotdmFsdWUuXG5cblx0XHRcdFx0dmFyIHYgPSBqIC8gcmFkaWFsU2VnbWVudHMgKiBNYXRoLlBJICogMjtcblx0XHRcdFx0dmFyIGN4ID0gLSB0dWJlICogTWF0aC5jb3MoIHYgKTtcblx0XHRcdFx0dmFyIGN5ID0gdHViZSAqIE1hdGguc2luKCB2ICk7XG5cblx0XHRcdFx0Ly8gbm93IGNhbGN1bGF0ZSB0aGUgZmluYWwgdmVydGV4IHBvc2l0aW9uLlxuXHRcdFx0XHQvLyBmaXJzdCB3ZSBvcmllbnQgdGhlIGV4dHJ1c2lvbiB3aXRoIG91ciBiYXNpcyB2ZWN0b3MsIHRoZW4gd2UgYWRkIGl0IHRvIHRoZSBjdXJyZW50IHBvc2l0aW9uIG9uIHRoZSBjdXJ2ZVxuXG5cdFx0XHRcdHZlcnRleC54ID0gUDEueCArICggY3ggKiBOLnggKyBjeSAqIEIueCApO1xuXHRcdFx0XHR2ZXJ0ZXgueSA9IFAxLnkgKyAoIGN4ICogTi55ICsgY3kgKiBCLnkgKTtcblx0XHRcdFx0dmVydGV4LnogPSBQMS56ICsgKCBjeCAqIE4ueiArIGN5ICogQi56ICk7XG5cblx0XHRcdFx0Ly8gdmVydGV4XG5cdFx0XHRcdHZlcnRpY2VzLnNldFhZWiggaW5kZXgsIHZlcnRleC54LCB2ZXJ0ZXgueSwgdmVydGV4LnogKTtcblxuXHRcdFx0XHQvLyBub3JtYWwgKFAxIGlzIGFsd2F5cyB0aGUgY2VudGVyL29yaWdpbiBvZiB0aGUgZXh0cnVzaW9uLCB0aHVzIHdlIGNhbiB1c2UgaXQgdG8gY2FsY3VsYXRlIHRoZSBub3JtYWwpXG5cdFx0XHRcdG5vcm1hbC5zdWJWZWN0b3JzKCB2ZXJ0ZXgsIFAxICkubm9ybWFsaXplKCk7XG5cdFx0XHRcdG5vcm1hbHMuc2V0WFlaKCBpbmRleCwgbm9ybWFsLngsIG5vcm1hbC55LCBub3JtYWwueiApO1xuXG5cdFx0XHRcdC8vIHV2XG5cdFx0XHRcdHV2LnggPSBpIC8gdHVidWxhclNlZ21lbnRzO1xuXHRcdFx0XHR1di55ID0gaiAvIHJhZGlhbFNlZ21lbnRzO1xuXHRcdFx0XHR1dnMuc2V0WFkoIGluZGV4LCB1di54LCB1di55ICk7XG5cblx0XHRcdFx0Ly8gaW5jcmVhc2UgaW5kZXhcblx0XHRcdFx0aW5kZXggKys7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdC8vIGdlbmVyYXRlIGluZGljZXNcblxuXHRcdGZvciAoIGogPSAxOyBqIDw9IHR1YnVsYXJTZWdtZW50czsgaiArKyApIHtcblxuXHRcdFx0Zm9yICggaSA9IDE7IGkgPD0gcmFkaWFsU2VnbWVudHM7IGkgKysgKSB7XG5cblx0XHRcdFx0Ly8gaW5kaWNlc1xuXHRcdFx0XHR2YXIgYSA9ICggcmFkaWFsU2VnbWVudHMgKyAxICkgKiAoIGogLSAxICkgKyAoIGkgLSAxICk7XG5cdFx0XHRcdHZhciBiID0gKCByYWRpYWxTZWdtZW50cyArIDEgKSAqIGogKyAoIGkgLSAxICk7XG5cdFx0XHRcdHZhciBjID0gKCByYWRpYWxTZWdtZW50cyArIDEgKSAqIGogKyBpO1xuXHRcdFx0XHR2YXIgZCA9ICggcmFkaWFsU2VnbWVudHMgKyAxICkgKiAoIGogLSAxICkgKyBpO1xuXG5cdFx0XHRcdC8vIGZhY2Ugb25lXG5cdFx0XHRcdGluZGljZXMuc2V0WCggaW5kZXhPZmZzZXQsIGEgKTsgaW5kZXhPZmZzZXQrKztcblx0XHRcdFx0aW5kaWNlcy5zZXRYKCBpbmRleE9mZnNldCwgYiApOyBpbmRleE9mZnNldCsrO1xuXHRcdFx0XHRpbmRpY2VzLnNldFgoIGluZGV4T2Zmc2V0LCBkICk7IGluZGV4T2Zmc2V0Kys7XG5cblx0XHRcdFx0Ly8gZmFjZSB0d29cblx0XHRcdFx0aW5kaWNlcy5zZXRYKCBpbmRleE9mZnNldCwgYiApOyBpbmRleE9mZnNldCsrO1xuXHRcdFx0XHRpbmRpY2VzLnNldFgoIGluZGV4T2Zmc2V0LCBjICk7IGluZGV4T2Zmc2V0Kys7XG5cdFx0XHRcdGluZGljZXMuc2V0WCggaW5kZXhPZmZzZXQsIGQgKTsgaW5kZXhPZmZzZXQrKztcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0Ly8gYnVpbGQgZ2VvbWV0cnlcblxuXHRcdHRoaXMuc2V0SW5kZXgoIGluZGljZXMgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ3Bvc2l0aW9uJywgdmVydGljZXMgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ25vcm1hbCcsIG5vcm1hbHMgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ3V2JywgdXZzICk7XG5cblx0XHQvLyB0aGlzIGZ1bmN0aW9uIGNhbGN1bGF0ZXMgdGhlIGN1cnJlbnQgcG9zaXRpb24gb24gdGhlIHRvcnVzIGN1cnZlXG5cblx0XHRmdW5jdGlvbiBjYWxjdWxhdGVQb3NpdGlvbk9uQ3VydmUoIHUsIHAsIHEsIHJhZGl1cywgcG9zaXRpb24gKSB7XG5cblx0XHRcdHZhciBjdSA9IE1hdGguY29zKCB1ICk7XG5cdFx0XHR2YXIgc3UgPSBNYXRoLnNpbiggdSApO1xuXHRcdFx0dmFyIHF1T3ZlclAgPSBxIC8gcCAqIHU7XG5cdFx0XHR2YXIgY3MgPSBNYXRoLmNvcyggcXVPdmVyUCApO1xuXG5cdFx0XHRwb3NpdGlvbi54ID0gcmFkaXVzICogKCAyICsgY3MgKSAqIDAuNSAqIGN1O1xuXHRcdFx0cG9zaXRpb24ueSA9IHJhZGl1cyAqICggMiArIGNzICkgKiBzdSAqIDAuNTtcblx0XHRcdHBvc2l0aW9uLnogPSByYWRpdXMgKiBNYXRoLnNpbiggcXVPdmVyUCApICogMC41O1xuXG5cdFx0fVxuXG5cdH1cblxuXHRUb3J1c0tub3RCdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBCdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUgKTtcblx0VG9ydXNLbm90QnVmZmVyR2VvbWV0cnkucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gVG9ydXNLbm90QnVmZmVyR2VvbWV0cnk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3Igb29zbW94aWVjb2RlXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFRvcnVzS25vdEdlb21ldHJ5KCByYWRpdXMsIHR1YmUsIHR1YnVsYXJTZWdtZW50cywgcmFkaWFsU2VnbWVudHMsIHAsIHEsIGhlaWdodFNjYWxlICkge1xuXG5cdFx0R2VvbWV0cnkuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ1RvcnVzS25vdEdlb21ldHJ5JztcblxuXHRcdHRoaXMucGFyYW1ldGVycyA9IHtcblx0XHRcdHJhZGl1czogcmFkaXVzLFxuXHRcdFx0dHViZTogdHViZSxcblx0XHRcdHR1YnVsYXJTZWdtZW50czogdHVidWxhclNlZ21lbnRzLFxuXHRcdFx0cmFkaWFsU2VnbWVudHM6IHJhZGlhbFNlZ21lbnRzLFxuXHRcdFx0cDogcCxcblx0XHRcdHE6IHFcblx0XHR9O1xuXG5cdFx0aWYoIGhlaWdodFNjYWxlICE9PSB1bmRlZmluZWQgKSBjb25zb2xlLndhcm4oICdUSFJFRS5Ub3J1c0tub3RHZW9tZXRyeTogaGVpZ2h0U2NhbGUgaGFzIGJlZW4gZGVwcmVjYXRlZC4gVXNlIC5zY2FsZSggeCwgeSwgeiApIGluc3RlYWQuJyApO1xuXG5cdFx0dGhpcy5mcm9tQnVmZmVyR2VvbWV0cnkoIG5ldyBUb3J1c0tub3RCdWZmZXJHZW9tZXRyeSggcmFkaXVzLCB0dWJlLCB0dWJ1bGFyU2VnbWVudHMsIHJhZGlhbFNlZ21lbnRzLCBwLCBxICkgKTtcblx0XHR0aGlzLm1lcmdlVmVydGljZXMoKTtcblxuXHR9XG5cblx0VG9ydXNLbm90R2VvbWV0cnkucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggR2VvbWV0cnkucHJvdG90eXBlICk7XG5cdFRvcnVzS25vdEdlb21ldHJ5LnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFRvcnVzS25vdEdlb21ldHJ5O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIE11Z2VuODcgLyBodHRwczovL2dpdGh1Yi5jb20vTXVnZW44N1xuXHQgKi9cblxuXHRmdW5jdGlvbiBUb3J1c0J1ZmZlckdlb21ldHJ5KCByYWRpdXMsIHR1YmUsIHJhZGlhbFNlZ21lbnRzLCB0dWJ1bGFyU2VnbWVudHMsIGFyYyApIHtcblxuXHRcdEJ1ZmZlckdlb21ldHJ5LmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdUb3J1c0J1ZmZlckdlb21ldHJ5JztcblxuXHRcdHRoaXMucGFyYW1ldGVycyA9IHtcblx0XHRcdHJhZGl1czogcmFkaXVzLFxuXHRcdFx0dHViZTogdHViZSxcblx0XHRcdHJhZGlhbFNlZ21lbnRzOiByYWRpYWxTZWdtZW50cyxcblx0XHRcdHR1YnVsYXJTZWdtZW50czogdHVidWxhclNlZ21lbnRzLFxuXHRcdFx0YXJjOiBhcmNcblx0XHR9O1xuXG5cdFx0cmFkaXVzID0gcmFkaXVzIHx8IDEwMDtcblx0XHR0dWJlID0gdHViZSB8fCA0MDtcblx0XHRyYWRpYWxTZWdtZW50cyA9IE1hdGguZmxvb3IoIHJhZGlhbFNlZ21lbnRzICkgfHwgODtcblx0XHR0dWJ1bGFyU2VnbWVudHMgPSBNYXRoLmZsb29yKCB0dWJ1bGFyU2VnbWVudHMgKSB8fCA2O1xuXHRcdGFyYyA9IGFyYyB8fCBNYXRoLlBJICogMjtcblxuXHRcdC8vIHVzZWQgdG8gY2FsY3VsYXRlIGJ1ZmZlciBsZW5ndGhcblx0XHR2YXIgdmVydGV4Q291bnQgPSAoICggcmFkaWFsU2VnbWVudHMgKyAxICkgKiAoIHR1YnVsYXJTZWdtZW50cyArIDEgKSApO1xuXHRcdHZhciBpbmRleENvdW50ID0gcmFkaWFsU2VnbWVudHMgKiB0dWJ1bGFyU2VnbWVudHMgKiAyICogMztcblxuXHRcdC8vIGJ1ZmZlcnNcblx0XHR2YXIgaW5kaWNlcyA9IG5ldyAoIGluZGV4Q291bnQgPiA2NTUzNSA/IFVpbnQzMkFycmF5IDogVWludDE2QXJyYXkgKSggaW5kZXhDb3VudCApO1xuXHRcdHZhciB2ZXJ0aWNlcyA9IG5ldyBGbG9hdDMyQXJyYXkoIHZlcnRleENvdW50ICogMyApO1xuXHRcdHZhciBub3JtYWxzID0gbmV3IEZsb2F0MzJBcnJheSggdmVydGV4Q291bnQgKiAzICk7XG5cdFx0dmFyIHV2cyA9IG5ldyBGbG9hdDMyQXJyYXkoIHZlcnRleENvdW50ICogMiApO1xuXG5cdFx0Ly8gb2Zmc2V0IHZhcmlhYmxlc1xuXHRcdHZhciB2ZXJ0ZXhCdWZmZXJPZmZzZXQgPSAwO1xuXHRcdHZhciB1dkJ1ZmZlck9mZnNldCA9IDA7XG5cdFx0dmFyIGluZGV4QnVmZmVyT2Zmc2V0ID0gMDtcblxuXHRcdC8vIGhlbHBlciB2YXJpYWJsZXNcblx0XHR2YXIgY2VudGVyID0gbmV3IFZlY3RvcjMoKTtcblx0XHR2YXIgdmVydGV4ID0gbmV3IFZlY3RvcjMoKTtcblx0XHR2YXIgbm9ybWFsID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdHZhciBqLCBpO1xuXG5cdFx0Ly8gZ2VuZXJhdGUgdmVydGljZXMsIG5vcm1hbHMgYW5kIHV2c1xuXG5cdFx0Zm9yICggaiA9IDA7IGogPD0gcmFkaWFsU2VnbWVudHM7IGogKysgKSB7XG5cblx0XHRcdGZvciAoIGkgPSAwOyBpIDw9IHR1YnVsYXJTZWdtZW50czsgaSArKyApIHtcblxuXHRcdFx0XHR2YXIgdSA9IGkgLyB0dWJ1bGFyU2VnbWVudHMgKiBhcmM7XG5cdFx0XHRcdHZhciB2ID0gaiAvIHJhZGlhbFNlZ21lbnRzICogTWF0aC5QSSAqIDI7XG5cblx0XHRcdFx0Ly8gdmVydGV4XG5cdFx0XHRcdHZlcnRleC54ID0gKCByYWRpdXMgKyB0dWJlICogTWF0aC5jb3MoIHYgKSApICogTWF0aC5jb3MoIHUgKTtcblx0XHRcdFx0dmVydGV4LnkgPSAoIHJhZGl1cyArIHR1YmUgKiBNYXRoLmNvcyggdiApICkgKiBNYXRoLnNpbiggdSApO1xuXHRcdFx0XHR2ZXJ0ZXgueiA9IHR1YmUgKiBNYXRoLnNpbiggdiApO1xuXG5cdFx0XHRcdHZlcnRpY2VzWyB2ZXJ0ZXhCdWZmZXJPZmZzZXQgXSA9IHZlcnRleC54O1xuXHRcdFx0XHR2ZXJ0aWNlc1sgdmVydGV4QnVmZmVyT2Zmc2V0ICsgMSBdID0gdmVydGV4Lnk7XG5cdFx0XHRcdHZlcnRpY2VzWyB2ZXJ0ZXhCdWZmZXJPZmZzZXQgKyAyIF0gPSB2ZXJ0ZXguejtcblxuXHRcdFx0XHQvLyB0aGlzIHZlY3RvciBpcyB1c2VkIHRvIGNhbGN1bGF0ZSB0aGUgbm9ybWFsXG5cdFx0XHRcdGNlbnRlci54ID0gcmFkaXVzICogTWF0aC5jb3MoIHUgKTtcblx0XHRcdFx0Y2VudGVyLnkgPSByYWRpdXMgKiBNYXRoLnNpbiggdSApO1xuXG5cdFx0XHRcdC8vIG5vcm1hbFxuXHRcdFx0XHRub3JtYWwuc3ViVmVjdG9ycyggdmVydGV4LCBjZW50ZXIgKS5ub3JtYWxpemUoKTtcblxuXHRcdFx0XHRub3JtYWxzWyB2ZXJ0ZXhCdWZmZXJPZmZzZXQgXSA9IG5vcm1hbC54O1xuXHRcdFx0XHRub3JtYWxzWyB2ZXJ0ZXhCdWZmZXJPZmZzZXQgKyAxIF0gPSBub3JtYWwueTtcblx0XHRcdFx0bm9ybWFsc1sgdmVydGV4QnVmZmVyT2Zmc2V0ICsgMiBdID0gbm9ybWFsLno7XG5cblx0XHRcdFx0Ly8gdXZcblx0XHRcdFx0dXZzWyB1dkJ1ZmZlck9mZnNldCBdID0gaSAvIHR1YnVsYXJTZWdtZW50cztcblx0XHRcdFx0dXZzWyB1dkJ1ZmZlck9mZnNldCArIDEgXSA9IGogLyByYWRpYWxTZWdtZW50cztcblxuXHRcdFx0XHQvLyB1cGRhdGUgb2Zmc2V0c1xuXHRcdFx0XHR2ZXJ0ZXhCdWZmZXJPZmZzZXQgKz0gMztcblx0XHRcdFx0dXZCdWZmZXJPZmZzZXQgKz0gMjtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0Ly8gZ2VuZXJhdGUgaW5kaWNlc1xuXG5cdFx0Zm9yICggaiA9IDE7IGogPD0gcmFkaWFsU2VnbWVudHM7IGogKysgKSB7XG5cblx0XHRcdGZvciAoIGkgPSAxOyBpIDw9IHR1YnVsYXJTZWdtZW50czsgaSArKyApIHtcblxuXHRcdFx0XHQvLyBpbmRpY2VzXG5cdFx0XHRcdHZhciBhID0gKCB0dWJ1bGFyU2VnbWVudHMgKyAxICkgKiBqICsgaSAtIDE7XG5cdFx0XHRcdHZhciBiID0gKCB0dWJ1bGFyU2VnbWVudHMgKyAxICkgKiAoIGogLSAxICkgKyBpIC0gMTtcblx0XHRcdFx0dmFyIGMgPSAoIHR1YnVsYXJTZWdtZW50cyArIDEgKSAqICggaiAtIDEgKSArIGk7XG5cdFx0XHRcdHZhciBkID0gKCB0dWJ1bGFyU2VnbWVudHMgKyAxICkgKiBqICsgaTtcblxuXHRcdFx0XHQvLyBmYWNlIG9uZVxuXHRcdFx0XHRpbmRpY2VzWyBpbmRleEJ1ZmZlck9mZnNldCBdID0gYTtcblx0XHRcdFx0aW5kaWNlc1sgaW5kZXhCdWZmZXJPZmZzZXQgKyAxIF0gPSBiO1xuXHRcdFx0XHRpbmRpY2VzWyBpbmRleEJ1ZmZlck9mZnNldCArIDIgXSA9IGQ7XG5cblx0XHRcdFx0Ly8gZmFjZSB0d29cblx0XHRcdFx0aW5kaWNlc1sgaW5kZXhCdWZmZXJPZmZzZXQgKyAzIF0gPSBiO1xuXHRcdFx0XHRpbmRpY2VzWyBpbmRleEJ1ZmZlck9mZnNldCArIDQgXSA9IGM7XG5cdFx0XHRcdGluZGljZXNbIGluZGV4QnVmZmVyT2Zmc2V0ICsgNSBdID0gZDtcblxuXHRcdFx0XHQvLyB1cGRhdGUgb2Zmc2V0XG5cdFx0XHRcdGluZGV4QnVmZmVyT2Zmc2V0ICs9IDY7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdC8vIGJ1aWxkIGdlb21ldHJ5XG5cdFx0dGhpcy5zZXRJbmRleCggbmV3IEJ1ZmZlckF0dHJpYnV0ZSggaW5kaWNlcywgMSApICk7XG5cdFx0dGhpcy5hZGRBdHRyaWJ1dGUoICdwb3NpdGlvbicsIG5ldyBCdWZmZXJBdHRyaWJ1dGUoIHZlcnRpY2VzLCAzICkgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ25vcm1hbCcsIG5ldyBCdWZmZXJBdHRyaWJ1dGUoIG5vcm1hbHMsIDMgKSApO1xuXHRcdHRoaXMuYWRkQXR0cmlidXRlKCAndXYnLCBuZXcgQnVmZmVyQXR0cmlidXRlKCB1dnMsIDIgKSApO1xuXG5cdH1cblxuXHRUb3J1c0J1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRUb3J1c0J1ZmZlckdlb21ldHJ5LnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFRvcnVzQnVmZmVyR2VvbWV0cnk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3Igb29zbW94aWVjb2RlXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqIGJhc2VkIG9uIGh0dHA6Ly9jb2RlLmdvb2dsZS5jb20vcC9hd2F5M2Qvc291cmNlL2Jyb3dzZS90cnVuay9mcDEwL0F3YXkzRExpdGUvc3JjL2F3YXkzZGxpdGUvcHJpbWl0aXZlcy9Ub3J1cy5hcz9yPTI4ODhcblx0ICovXG5cblx0ZnVuY3Rpb24gVG9ydXNHZW9tZXRyeSggcmFkaXVzLCB0dWJlLCByYWRpYWxTZWdtZW50cywgdHVidWxhclNlZ21lbnRzLCBhcmMgKSB7XG5cblx0XHRHZW9tZXRyeS5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnVG9ydXNHZW9tZXRyeSc7XG5cblx0XHR0aGlzLnBhcmFtZXRlcnMgPSB7XG5cdFx0XHRyYWRpdXM6IHJhZGl1cyxcblx0XHRcdHR1YmU6IHR1YmUsXG5cdFx0XHRyYWRpYWxTZWdtZW50czogcmFkaWFsU2VnbWVudHMsXG5cdFx0XHR0dWJ1bGFyU2VnbWVudHM6IHR1YnVsYXJTZWdtZW50cyxcblx0XHRcdGFyYzogYXJjXG5cdFx0fTtcblxuXHRcdHRoaXMuZnJvbUJ1ZmZlckdlb21ldHJ5KCBuZXcgVG9ydXNCdWZmZXJHZW9tZXRyeSggcmFkaXVzLCB0dWJlLCByYWRpYWxTZWdtZW50cywgdHVidWxhclNlZ21lbnRzLCBhcmMgKSApO1xuXG5cdH1cblxuXHRUb3J1c0dlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRUb3J1c0dlb21ldHJ5LnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFRvcnVzR2VvbWV0cnk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3Igeno4NSAvIGh0dHA6Ly93d3cubGFiNGdhbWVzLm5ldC96ejg1L2Jsb2dcblx0ICovXG5cblx0dmFyIFNoYXBlVXRpbHMgPSB7XG5cblx0XHQvLyBjYWxjdWxhdGUgYXJlYSBvZiB0aGUgY29udG91ciBwb2x5Z29uXG5cblx0XHRhcmVhOiBmdW5jdGlvbiAoIGNvbnRvdXIgKSB7XG5cblx0XHRcdHZhciBuID0gY29udG91ci5sZW5ndGg7XG5cdFx0XHR2YXIgYSA9IDAuMDtcblxuXHRcdFx0Zm9yICggdmFyIHAgPSBuIC0gMSwgcSA9IDA7IHEgPCBuOyBwID0gcSArKyApIHtcblxuXHRcdFx0XHRhICs9IGNvbnRvdXJbIHAgXS54ICogY29udG91clsgcSBdLnkgLSBjb250b3VyWyBxIF0ueCAqIGNvbnRvdXJbIHAgXS55O1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBhICogMC41O1xuXG5cdFx0fSxcblxuXHRcdHRyaWFuZ3VsYXRlOiAoIGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0LyoqXG5cdFx0XHQgKiBUaGlzIGNvZGUgaXMgYSBxdWljayBwb3J0IG9mIGNvZGUgd3JpdHRlbiBpbiBDKysgd2hpY2ggd2FzIHN1Ym1pdHRlZCB0b1xuXHRcdFx0ICogZmxpcGNvZGUuY29tIGJ5IEpvaG4gVy4gUmF0Y2xpZmYgIC8vIEp1bHkgMjIsIDIwMDBcblx0XHRcdCAqIFNlZSBvcmlnaW5hbCBjb2RlIGFuZCBtb3JlIGluZm9ybWF0aW9uIGhlcmU6XG5cdFx0XHQgKiBodHRwOi8vd3d3LmZsaXBjb2RlLmNvbS9hcmNoaXZlcy9FZmZpY2llbnRfUG9seWdvbl9Ucmlhbmd1bGF0aW9uLnNodG1sXG5cdFx0XHQgKlxuXHRcdFx0ICogcG9ydGVkIHRvIGFjdGlvbnNjcmlwdCBieSBaZXZhbiBSb3NzZXJcblx0XHRcdCAqIHd3dy5hY3Rpb25zbmlwcGV0LmNvbVxuXHRcdFx0ICpcblx0XHRcdCAqIHBvcnRlZCB0byBqYXZhc2NyaXB0IGJ5IEpvc2h1YSBLb29cblx0XHRcdCAqIGh0dHA6Ly93d3cubGFiNGdhbWVzLm5ldC96ejg1L2Jsb2dcblx0XHRcdCAqXG5cdFx0XHQgKi9cblxuXHRcdFx0ZnVuY3Rpb24gc25pcCggY29udG91ciwgdSwgdiwgdywgbiwgdmVydHMgKSB7XG5cblx0XHRcdFx0dmFyIHA7XG5cdFx0XHRcdHZhciBheCwgYXksIGJ4LCBieTtcblx0XHRcdFx0dmFyIGN4LCBjeSwgcHgsIHB5O1xuXG5cdFx0XHRcdGF4ID0gY29udG91clsgdmVydHNbIHUgXSBdLng7XG5cdFx0XHRcdGF5ID0gY29udG91clsgdmVydHNbIHUgXSBdLnk7XG5cblx0XHRcdFx0YnggPSBjb250b3VyWyB2ZXJ0c1sgdiBdIF0ueDtcblx0XHRcdFx0YnkgPSBjb250b3VyWyB2ZXJ0c1sgdiBdIF0ueTtcblxuXHRcdFx0XHRjeCA9IGNvbnRvdXJbIHZlcnRzWyB3IF0gXS54O1xuXHRcdFx0XHRjeSA9IGNvbnRvdXJbIHZlcnRzWyB3IF0gXS55O1xuXG5cdFx0XHRcdGlmICggKCBieCAtIGF4ICkgKiAoIGN5IC0gYXkgKSAtICggYnkgLSBheSApICogKCBjeCAtIGF4ICkgPD0gMCApIHJldHVybiBmYWxzZTtcblxuXHRcdFx0XHR2YXIgYVgsIGFZLCBiWCwgYlksIGNYLCBjWTtcblx0XHRcdFx0dmFyIGFweCwgYXB5LCBicHgsIGJweSwgY3B4LCBjcHk7XG5cdFx0XHRcdHZhciBjQ1JPU1NhcCwgYkNST1NTY3AsIGFDUk9TU2JwO1xuXG5cdFx0XHRcdGFYID0gY3ggLSBieDsgIGFZID0gY3kgLSBieTtcblx0XHRcdFx0YlggPSBheCAtIGN4OyAgYlkgPSBheSAtIGN5O1xuXHRcdFx0XHRjWCA9IGJ4IC0gYXg7ICBjWSA9IGJ5IC0gYXk7XG5cblx0XHRcdFx0Zm9yICggcCA9IDA7IHAgPCBuOyBwICsrICkge1xuXG5cdFx0XHRcdFx0cHggPSBjb250b3VyWyB2ZXJ0c1sgcCBdIF0ueDtcblx0XHRcdFx0XHRweSA9IGNvbnRvdXJbIHZlcnRzWyBwIF0gXS55O1xuXG5cdFx0XHRcdFx0aWYgKCAoICggcHggPT09IGF4ICkgJiYgKCBweSA9PT0gYXkgKSApIHx8XG5cdFx0XHRcdFx0XHQgKCAoIHB4ID09PSBieCApICYmICggcHkgPT09IGJ5ICkgKSB8fFxuXHRcdFx0XHRcdFx0ICggKCBweCA9PT0gY3ggKSAmJiAoIHB5ID09PSBjeSApICkgKVx0Y29udGludWU7XG5cblx0XHRcdFx0XHRhcHggPSBweCAtIGF4OyAgYXB5ID0gcHkgLSBheTtcblx0XHRcdFx0XHRicHggPSBweCAtIGJ4OyAgYnB5ID0gcHkgLSBieTtcblx0XHRcdFx0XHRjcHggPSBweCAtIGN4OyAgY3B5ID0gcHkgLSBjeTtcblxuXHRcdFx0XHRcdC8vIHNlZSBpZiBwIGlzIGluc2lkZSB0cmlhbmdsZSBhYmNcblxuXHRcdFx0XHRcdGFDUk9TU2JwID0gYVggKiBicHkgLSBhWSAqIGJweDtcblx0XHRcdFx0XHRjQ1JPU1NhcCA9IGNYICogYXB5IC0gY1kgKiBhcHg7XG5cdFx0XHRcdFx0YkNST1NTY3AgPSBiWCAqIGNweSAtIGJZICogY3B4O1xuXG5cdFx0XHRcdFx0aWYgKCAoIGFDUk9TU2JwID49IC0gTnVtYmVyLkVQU0lMT04gKSAmJiAoIGJDUk9TU2NwID49IC0gTnVtYmVyLkVQU0lMT04gKSAmJiAoIGNDUk9TU2FwID49IC0gTnVtYmVyLkVQU0lMT04gKSApIHJldHVybiBmYWxzZTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuIHRydWU7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gdGFrZXMgaW4gYW4gY29udG91ciBhcnJheSBhbmQgcmV0dXJuc1xuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gdHJpYW5ndWxhdGUoIGNvbnRvdXIsIGluZGljZXMgKSB7XG5cblx0XHRcdFx0dmFyIG4gPSBjb250b3VyLmxlbmd0aDtcblxuXHRcdFx0XHRpZiAoIG4gPCAzICkgcmV0dXJuIG51bGw7XG5cblx0XHRcdFx0dmFyIHJlc3VsdCA9IFtdLFxuXHRcdFx0XHRcdHZlcnRzID0gW10sXG5cdFx0XHRcdFx0dmVydEluZGljZXMgPSBbXTtcblxuXHRcdFx0XHQvKiB3ZSB3YW50IGEgY291bnRlci1jbG9ja3dpc2UgcG9seWdvbiBpbiB2ZXJ0cyAqL1xuXG5cdFx0XHRcdHZhciB1LCB2LCB3O1xuXG5cdFx0XHRcdGlmICggU2hhcGVVdGlscy5hcmVhKCBjb250b3VyICkgPiAwLjAgKSB7XG5cblx0XHRcdFx0XHRmb3IgKCB2ID0gMDsgdiA8IG47IHYgKysgKSB2ZXJ0c1sgdiBdID0gdjtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0Zm9yICggdiA9IDA7IHYgPCBuOyB2ICsrICkgdmVydHNbIHYgXSA9ICggbiAtIDEgKSAtIHY7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBudiA9IG47XG5cblx0XHRcdFx0LyogIHJlbW92ZSBudiAtIDIgdmVydGljZXMsIGNyZWF0aW5nIDEgdHJpYW5nbGUgZXZlcnkgdGltZSAqL1xuXG5cdFx0XHRcdHZhciBjb3VudCA9IDIgKiBudjsgICAvKiBlcnJvciBkZXRlY3Rpb24gKi9cblxuXHRcdFx0XHRmb3IgKCB2ID0gbnYgLSAxOyBudiA+IDI7ICkge1xuXG5cdFx0XHRcdFx0LyogaWYgd2UgbG9vcCwgaXQgaXMgcHJvYmFibHkgYSBub24tc2ltcGxlIHBvbHlnb24gKi9cblxuXHRcdFx0XHRcdGlmICggKCBjb3VudCAtLSApIDw9IDAgKSB7XG5cblx0XHRcdFx0XHRcdC8vKiogVHJpYW5ndWxhdGU6IEVSUk9SIC0gcHJvYmFibGUgYmFkIHBvbHlnb24hXG5cblx0XHRcdFx0XHRcdC8vdGhyb3cgKCBcIldhcm5pbmcsIHVuYWJsZSB0byB0cmlhbmd1bGF0ZSBwb2x5Z29uIVwiICk7XG5cdFx0XHRcdFx0XHQvL3JldHVybiBudWxsO1xuXHRcdFx0XHRcdFx0Ly8gU29tZXRpbWVzIHdhcm5pbmcgaXMgZmluZSwgZXNwZWNpYWxseSBwb2x5Z29ucyBhcmUgdHJpYW5ndWxhdGVkIGluIHJldmVyc2UuXG5cdFx0XHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5TaGFwZVV0aWxzOiBVbmFibGUgdG8gdHJpYW5ndWxhdGUgcG9seWdvbiEgaW4gdHJpYW5ndWxhdGUoKScgKTtcblxuXHRcdFx0XHRcdFx0aWYgKCBpbmRpY2VzICkgcmV0dXJuIHZlcnRJbmRpY2VzO1xuXHRcdFx0XHRcdFx0cmV0dXJuIHJlc3VsdDtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8qIHRocmVlIGNvbnNlY3V0aXZlIHZlcnRpY2VzIGluIGN1cnJlbnQgcG9seWdvbiwgPHUsdix3PiAqL1xuXG5cdFx0XHRcdFx0dSA9IHY7IFx0IFx0aWYgKCBudiA8PSB1ICkgdSA9IDA7ICAgICAvKiBwcmV2aW91cyAqL1xuXHRcdFx0XHRcdHYgPSB1ICsgMTsgIGlmICggbnYgPD0gdiApIHYgPSAwOyAgICAgLyogbmV3IHYgICAgKi9cblx0XHRcdFx0XHR3ID0gdiArIDE7ICBpZiAoIG52IDw9IHcgKSB3ID0gMDsgICAgIC8qIG5leHQgICAgICovXG5cblx0XHRcdFx0XHRpZiAoIHNuaXAoIGNvbnRvdXIsIHUsIHYsIHcsIG52LCB2ZXJ0cyApICkge1xuXG5cdFx0XHRcdFx0XHR2YXIgYSwgYiwgYywgcywgdDtcblxuXHRcdFx0XHRcdFx0LyogdHJ1ZSBuYW1lcyBvZiB0aGUgdmVydGljZXMgKi9cblxuXHRcdFx0XHRcdFx0YSA9IHZlcnRzWyB1IF07XG5cdFx0XHRcdFx0XHRiID0gdmVydHNbIHYgXTtcblx0XHRcdFx0XHRcdGMgPSB2ZXJ0c1sgdyBdO1xuXG5cdFx0XHRcdFx0XHQvKiBvdXRwdXQgVHJpYW5nbGUgKi9cblxuXHRcdFx0XHRcdFx0cmVzdWx0LnB1c2goIFsgY29udG91clsgYSBdLFxuXHRcdFx0XHRcdFx0XHRjb250b3VyWyBiIF0sXG5cdFx0XHRcdFx0XHRcdGNvbnRvdXJbIGMgXSBdICk7XG5cblxuXHRcdFx0XHRcdFx0dmVydEluZGljZXMucHVzaCggWyB2ZXJ0c1sgdSBdLCB2ZXJ0c1sgdiBdLCB2ZXJ0c1sgdyBdIF0gKTtcblxuXHRcdFx0XHRcdFx0LyogcmVtb3ZlIHYgZnJvbSB0aGUgcmVtYWluaW5nIHBvbHlnb24gKi9cblxuXHRcdFx0XHRcdFx0Zm9yICggcyA9IHYsIHQgPSB2ICsgMTsgdCA8IG52OyBzICsrLCB0ICsrICkge1xuXG5cdFx0XHRcdFx0XHRcdHZlcnRzWyBzIF0gPSB2ZXJ0c1sgdCBdO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdG52IC0tO1xuXG5cdFx0XHRcdFx0XHQvKiByZXNldCBlcnJvciBkZXRlY3Rpb24gY291bnRlciAqL1xuXG5cdFx0XHRcdFx0XHRjb3VudCA9IDIgKiBudjtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCBpbmRpY2VzICkgcmV0dXJuIHZlcnRJbmRpY2VzO1xuXHRcdFx0XHRyZXR1cm4gcmVzdWx0O1xuXG5cdFx0XHR9XG5cblx0XHR9ICkoKSxcblxuXHRcdHRyaWFuZ3VsYXRlU2hhcGU6IGZ1bmN0aW9uICggY29udG91ciwgaG9sZXMgKSB7XG5cblx0XHRcdGZ1bmN0aW9uIHJlbW92ZUR1cEVuZFB0cyhwb2ludHMpIHtcblxuXHRcdFx0XHR2YXIgbCA9IHBvaW50cy5sZW5ndGg7XG5cblx0XHRcdFx0aWYgKCBsID4gMiAmJiBwb2ludHNbIGwgLSAxIF0uZXF1YWxzKCBwb2ludHNbIDAgXSApICkge1xuXG5cdFx0XHRcdFx0cG9pbnRzLnBvcCgpO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRyZW1vdmVEdXBFbmRQdHMoIGNvbnRvdXIgKTtcblx0XHRcdGhvbGVzLmZvckVhY2goIHJlbW92ZUR1cEVuZFB0cyApO1xuXG5cdFx0XHRmdW5jdGlvbiBwb2ludF9pbl9zZWdtZW50XzJEX2NvbGluKCBpblNlZ1B0MSwgaW5TZWdQdDIsIGluT3RoZXJQdCApIHtcblxuXHRcdFx0XHQvLyBpbk90aGVyUHQgbmVlZHMgdG8gYmUgY29sbGluZWFyIHRvIHRoZSBpblNlZ21lbnRcblx0XHRcdFx0aWYgKCBpblNlZ1B0MS54ICE9PSBpblNlZ1B0Mi54ICkge1xuXG5cdFx0XHRcdFx0aWYgKCBpblNlZ1B0MS54IDwgaW5TZWdQdDIueCApIHtcblxuXHRcdFx0XHRcdFx0cmV0dXJuXHQoICggaW5TZWdQdDEueCA8PSBpbk90aGVyUHQueCApICYmICggaW5PdGhlclB0LnggPD0gaW5TZWdQdDIueCApICk7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRyZXR1cm5cdCggKCBpblNlZ1B0Mi54IDw9IGluT3RoZXJQdC54ICkgJiYgKCBpbk90aGVyUHQueCA8PSBpblNlZ1B0MS54ICkgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0aWYgKCBpblNlZ1B0MS55IDwgaW5TZWdQdDIueSApIHtcblxuXHRcdFx0XHRcdFx0cmV0dXJuXHQoICggaW5TZWdQdDEueSA8PSBpbk90aGVyUHQueSApICYmICggaW5PdGhlclB0LnkgPD0gaW5TZWdQdDIueSApICk7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRyZXR1cm5cdCggKCBpblNlZ1B0Mi55IDw9IGluT3RoZXJQdC55ICkgJiYgKCBpbk90aGVyUHQueSA8PSBpblNlZ1B0MS55ICkgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0ZnVuY3Rpb24gaW50ZXJzZWN0X3NlZ21lbnRzXzJEKCBpblNlZzFQdDEsIGluU2VnMVB0MiwgaW5TZWcyUHQxLCBpblNlZzJQdDIsIGluRXhjbHVkZUFkamFjZW50U2VncyApIHtcblxuXHRcdFx0XHR2YXIgc2VnMWR4ID0gaW5TZWcxUHQyLnggLSBpblNlZzFQdDEueCwgICBzZWcxZHkgPSBpblNlZzFQdDIueSAtIGluU2VnMVB0MS55O1xuXHRcdFx0XHR2YXIgc2VnMmR4ID0gaW5TZWcyUHQyLnggLSBpblNlZzJQdDEueCwgICBzZWcyZHkgPSBpblNlZzJQdDIueSAtIGluU2VnMlB0MS55O1xuXG5cdFx0XHRcdHZhciBzZWcxc2VnMmR4ID0gaW5TZWcxUHQxLnggLSBpblNlZzJQdDEueDtcblx0XHRcdFx0dmFyIHNlZzFzZWcyZHkgPSBpblNlZzFQdDEueSAtIGluU2VnMlB0MS55O1xuXG5cdFx0XHRcdHZhciBsaW1pdFx0XHQ9IHNlZzFkeSAqIHNlZzJkeCAtIHNlZzFkeCAqIHNlZzJkeTtcblx0XHRcdFx0dmFyIHBlcnBTZWcxXHQ9IHNlZzFkeSAqIHNlZzFzZWcyZHggLSBzZWcxZHggKiBzZWcxc2VnMmR5O1xuXG5cdFx0XHRcdGlmICggTWF0aC5hYnMoIGxpbWl0ICkgPiBOdW1iZXIuRVBTSUxPTiApIHtcblxuXHRcdFx0XHRcdC8vIG5vdCBwYXJhbGxlbFxuXG5cdFx0XHRcdFx0dmFyIHBlcnBTZWcyO1xuXHRcdFx0XHRcdGlmICggbGltaXQgPiAwICkge1xuXG5cdFx0XHRcdFx0XHRpZiAoICggcGVycFNlZzEgPCAwICkgfHwgKCBwZXJwU2VnMSA+IGxpbWl0ICkgKSBcdFx0cmV0dXJuIFtdO1xuXHRcdFx0XHRcdFx0cGVycFNlZzIgPSBzZWcyZHkgKiBzZWcxc2VnMmR4IC0gc2VnMmR4ICogc2VnMXNlZzJkeTtcblx0XHRcdFx0XHRcdGlmICggKCBwZXJwU2VnMiA8IDAgKSB8fCAoIHBlcnBTZWcyID4gbGltaXQgKSApIFx0XHRyZXR1cm4gW107XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRpZiAoICggcGVycFNlZzEgPiAwICkgfHwgKCBwZXJwU2VnMSA8IGxpbWl0ICkgKSBcdFx0cmV0dXJuIFtdO1xuXHRcdFx0XHRcdFx0cGVycFNlZzIgPSBzZWcyZHkgKiBzZWcxc2VnMmR4IC0gc2VnMmR4ICogc2VnMXNlZzJkeTtcblx0XHRcdFx0XHRcdGlmICggKCBwZXJwU2VnMiA+IDAgKSB8fCAoIHBlcnBTZWcyIDwgbGltaXQgKSApIFx0XHRyZXR1cm4gW107XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHQvLyBpLmUuIHRvIHJlZHVjZSByb3VuZGluZyBlcnJvcnNcblx0XHRcdFx0XHQvLyBpbnRlcnNlY3Rpb24gYXQgZW5kcG9pbnQgb2Ygc2VnbWVudCMxP1xuXHRcdFx0XHRcdGlmICggcGVycFNlZzIgPT09IDAgKSB7XG5cblx0XHRcdFx0XHRcdGlmICggKCBpbkV4Y2x1ZGVBZGphY2VudFNlZ3MgKSAmJlxuXHRcdFx0XHRcdFx0XHQgKCAoIHBlcnBTZWcxID09PSAwICkgfHwgKCBwZXJwU2VnMSA9PT0gbGltaXQgKSApIClcdFx0cmV0dXJuIFtdO1xuXHRcdFx0XHRcdFx0cmV0dXJuIFsgaW5TZWcxUHQxIF07XG5cblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0aWYgKCBwZXJwU2VnMiA9PT0gbGltaXQgKSB7XG5cblx0XHRcdFx0XHRcdGlmICggKCBpbkV4Y2x1ZGVBZGphY2VudFNlZ3MgKSAmJlxuXHRcdFx0XHRcdFx0XHQgKCAoIHBlcnBTZWcxID09PSAwICkgfHwgKCBwZXJwU2VnMSA9PT0gbGltaXQgKSApIClcdFx0cmV0dXJuIFtdO1xuXHRcdFx0XHRcdFx0cmV0dXJuIFsgaW5TZWcxUHQyIF07XG5cblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0Ly8gaW50ZXJzZWN0aW9uIGF0IGVuZHBvaW50IG9mIHNlZ21lbnQjMj9cblx0XHRcdFx0XHRpZiAoIHBlcnBTZWcxID09PSAwIClcdFx0cmV0dXJuIFsgaW5TZWcyUHQxIF07XG5cdFx0XHRcdFx0aWYgKCBwZXJwU2VnMSA9PT0gbGltaXQgKVx0cmV0dXJuIFsgaW5TZWcyUHQyIF07XG5cblx0XHRcdFx0XHQvLyByZXR1cm4gcmVhbCBpbnRlcnNlY3Rpb24gcG9pbnRcblx0XHRcdFx0XHR2YXIgZmFjdG9yU2VnMSA9IHBlcnBTZWcyIC8gbGltaXQ7XG5cdFx0XHRcdFx0cmV0dXJuXHRbIHsgeDogaW5TZWcxUHQxLnggKyBmYWN0b3JTZWcxICogc2VnMWR4LFxuXHRcdFx0XHRcdFx0XHRcdHk6IGluU2VnMVB0MS55ICsgZmFjdG9yU2VnMSAqIHNlZzFkeSB9IF07XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdC8vIHBhcmFsbGVsIG9yIGNvbGxpbmVhclxuXHRcdFx0XHRcdGlmICggKCBwZXJwU2VnMSAhPT0gMCApIHx8XG5cdFx0XHRcdFx0XHQgKCBzZWcyZHkgKiBzZWcxc2VnMmR4ICE9PSBzZWcyZHggKiBzZWcxc2VnMmR5ICkgKSBcdFx0XHRyZXR1cm4gW107XG5cblx0XHRcdFx0XHQvLyB0aGV5IGFyZSBjb2xsaW5lYXIgb3IgZGVnZW5lcmF0ZVxuXHRcdFx0XHRcdHZhciBzZWcxUHQgPSAoICggc2VnMWR4ID09PSAwICkgJiYgKCBzZWcxZHkgPT09IDAgKSApO1x0Ly8gc2VnbWVudDEgaXMganVzdCBhIHBvaW50P1xuXHRcdFx0XHRcdHZhciBzZWcyUHQgPSAoICggc2VnMmR4ID09PSAwICkgJiYgKCBzZWcyZHkgPT09IDAgKSApO1x0Ly8gc2VnbWVudDIgaXMganVzdCBhIHBvaW50P1xuXHRcdFx0XHRcdC8vIGJvdGggc2VnbWVudHMgYXJlIHBvaW50c1xuXHRcdFx0XHRcdGlmICggc2VnMVB0ICYmIHNlZzJQdCApIHtcblxuXHRcdFx0XHRcdFx0aWYgKCAoIGluU2VnMVB0MS54ICE9PSBpblNlZzJQdDEueCApIHx8XG5cdFx0XHRcdFx0XHRcdCAoIGluU2VnMVB0MS55ICE9PSBpblNlZzJQdDEueSApIClcdFx0cmV0dXJuIFtdO1x0Ly8gdGhleSBhcmUgZGlzdGluY3QgIHBvaW50c1xuXHRcdFx0XHRcdFx0cmV0dXJuIFsgaW5TZWcxUHQxIF07ICAgICAgICAgICAgICAgICBcdFx0XHRcdFx0XHQvLyB0aGV5IGFyZSB0aGUgc2FtZSBwb2ludFxuXG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdC8vIHNlZ21lbnQjMSAgaXMgYSBzaW5nbGUgcG9pbnRcblx0XHRcdFx0XHRpZiAoIHNlZzFQdCApIHtcblxuXHRcdFx0XHRcdFx0aWYgKCAhIHBvaW50X2luX3NlZ21lbnRfMkRfY29saW4oIGluU2VnMlB0MSwgaW5TZWcyUHQyLCBpblNlZzFQdDEgKSApXHRcdHJldHVybiBbXTtcdFx0Ly8gYnV0IG5vdCBpbiBzZWdtZW50IzJcblx0XHRcdFx0XHRcdHJldHVybiBbIGluU2VnMVB0MSBdO1xuXG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdC8vIHNlZ21lbnQjMiAgaXMgYSBzaW5nbGUgcG9pbnRcblx0XHRcdFx0XHRpZiAoIHNlZzJQdCApIHtcblxuXHRcdFx0XHRcdFx0aWYgKCAhIHBvaW50X2luX3NlZ21lbnRfMkRfY29saW4oIGluU2VnMVB0MSwgaW5TZWcxUHQyLCBpblNlZzJQdDEgKSApXHRcdHJldHVybiBbXTtcdFx0Ly8gYnV0IG5vdCBpbiBzZWdtZW50IzFcblx0XHRcdFx0XHRcdHJldHVybiBbIGluU2VnMlB0MSBdO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0Ly8gdGhleSBhcmUgY29sbGluZWFyIHNlZ21lbnRzLCB3aGljaCBtaWdodCBvdmVybGFwXG5cdFx0XHRcdFx0dmFyIHNlZzFtaW4sIHNlZzFtYXgsIHNlZzFtaW5WYWwsIHNlZzFtYXhWYWw7XG5cdFx0XHRcdFx0dmFyIHNlZzJtaW4sIHNlZzJtYXgsIHNlZzJtaW5WYWwsIHNlZzJtYXhWYWw7XG5cdFx0XHRcdFx0aWYgKCBzZWcxZHggIT09IDAgKSB7XG5cblx0XHRcdFx0XHRcdC8vIHRoZSBzZWdtZW50cyBhcmUgTk9UIG9uIGEgdmVydGljYWwgbGluZVxuXHRcdFx0XHRcdFx0aWYgKCBpblNlZzFQdDEueCA8IGluU2VnMVB0Mi54ICkge1xuXG5cdFx0XHRcdFx0XHRcdHNlZzFtaW4gPSBpblNlZzFQdDE7IHNlZzFtaW5WYWwgPSBpblNlZzFQdDEueDtcblx0XHRcdFx0XHRcdFx0c2VnMW1heCA9IGluU2VnMVB0Mjsgc2VnMW1heFZhbCA9IGluU2VnMVB0Mi54O1xuXG5cdFx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRcdHNlZzFtaW4gPSBpblNlZzFQdDI7IHNlZzFtaW5WYWwgPSBpblNlZzFQdDIueDtcblx0XHRcdFx0XHRcdFx0c2VnMW1heCA9IGluU2VnMVB0MTsgc2VnMW1heFZhbCA9IGluU2VnMVB0MS54O1xuXG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRpZiAoIGluU2VnMlB0MS54IDwgaW5TZWcyUHQyLnggKSB7XG5cblx0XHRcdFx0XHRcdFx0c2VnMm1pbiA9IGluU2VnMlB0MTsgc2VnMm1pblZhbCA9IGluU2VnMlB0MS54O1xuXHRcdFx0XHRcdFx0XHRzZWcybWF4ID0gaW5TZWcyUHQyOyBzZWcybWF4VmFsID0gaW5TZWcyUHQyLng7XG5cblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdFx0c2VnMm1pbiA9IGluU2VnMlB0Mjsgc2VnMm1pblZhbCA9IGluU2VnMlB0Mi54O1xuXHRcdFx0XHRcdFx0XHRzZWcybWF4ID0gaW5TZWcyUHQxOyBzZWcybWF4VmFsID0gaW5TZWcyUHQxLng7XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdC8vIHRoZSBzZWdtZW50cyBhcmUgb24gYSB2ZXJ0aWNhbCBsaW5lXG5cdFx0XHRcdFx0XHRpZiAoIGluU2VnMVB0MS55IDwgaW5TZWcxUHQyLnkgKSB7XG5cblx0XHRcdFx0XHRcdFx0c2VnMW1pbiA9IGluU2VnMVB0MTsgc2VnMW1pblZhbCA9IGluU2VnMVB0MS55O1xuXHRcdFx0XHRcdFx0XHRzZWcxbWF4ID0gaW5TZWcxUHQyOyBzZWcxbWF4VmFsID0gaW5TZWcxUHQyLnk7XG5cblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdFx0c2VnMW1pbiA9IGluU2VnMVB0Mjsgc2VnMW1pblZhbCA9IGluU2VnMVB0Mi55O1xuXHRcdFx0XHRcdFx0XHRzZWcxbWF4ID0gaW5TZWcxUHQxOyBzZWcxbWF4VmFsID0gaW5TZWcxUHQxLnk7XG5cblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdGlmICggaW5TZWcyUHQxLnkgPCBpblNlZzJQdDIueSApIHtcblxuXHRcdFx0XHRcdFx0XHRzZWcybWluID0gaW5TZWcyUHQxOyBzZWcybWluVmFsID0gaW5TZWcyUHQxLnk7XG5cdFx0XHRcdFx0XHRcdHNlZzJtYXggPSBpblNlZzJQdDI7IHNlZzJtYXhWYWwgPSBpblNlZzJQdDIueTtcblxuXHRcdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0XHRzZWcybWluID0gaW5TZWcyUHQyOyBzZWcybWluVmFsID0gaW5TZWcyUHQyLnk7XG5cdFx0XHRcdFx0XHRcdHNlZzJtYXggPSBpblNlZzJQdDE7IHNlZzJtYXhWYWwgPSBpblNlZzJQdDEueTtcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGlmICggc2VnMW1pblZhbCA8PSBzZWcybWluVmFsICkge1xuXG5cdFx0XHRcdFx0XHRpZiAoIHNlZzFtYXhWYWwgPCAgc2VnMm1pblZhbCApXHRyZXR1cm4gW107XG5cdFx0XHRcdFx0XHRpZiAoIHNlZzFtYXhWYWwgPT09IHNlZzJtaW5WYWwgKVx0e1xuXG5cdFx0XHRcdFx0XHRcdGlmICggaW5FeGNsdWRlQWRqYWNlbnRTZWdzIClcdFx0cmV0dXJuIFtdO1xuXHRcdFx0XHRcdFx0XHRyZXR1cm4gWyBzZWcybWluIF07XG5cblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdGlmICggc2VnMW1heFZhbCA8PSBzZWcybWF4VmFsIClcdHJldHVybiBbIHNlZzJtaW4sIHNlZzFtYXggXTtcblx0XHRcdFx0XHRcdHJldHVyblx0WyBzZWcybWluLCBzZWcybWF4IF07XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRpZiAoIHNlZzFtaW5WYWwgPiAgc2VnMm1heFZhbCApXHRyZXR1cm4gW107XG5cdFx0XHRcdFx0XHRpZiAoIHNlZzFtaW5WYWwgPT09IHNlZzJtYXhWYWwgKVx0e1xuXG5cdFx0XHRcdFx0XHRcdGlmICggaW5FeGNsdWRlQWRqYWNlbnRTZWdzIClcdFx0cmV0dXJuIFtdO1xuXHRcdFx0XHRcdFx0XHRyZXR1cm4gWyBzZWcxbWluIF07XG5cblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdGlmICggc2VnMW1heFZhbCA8PSBzZWcybWF4VmFsIClcdHJldHVybiBbIHNlZzFtaW4sIHNlZzFtYXggXTtcblx0XHRcdFx0XHRcdHJldHVyblx0WyBzZWcxbWluLCBzZWcybWF4IF07XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdGZ1bmN0aW9uIGlzUG9pbnRJbnNpZGVBbmdsZSggaW5WZXJ0ZXgsIGluTGVnRnJvbVB0LCBpbkxlZ1RvUHQsIGluT3RoZXJQdCApIHtcblxuXHRcdFx0XHQvLyBUaGUgb3JkZXIgb2YgbGVncyBpcyBpbXBvcnRhbnRcblxuXHRcdFx0XHQvLyB0cmFuc2xhdGlvbiBvZiBhbGwgcG9pbnRzLCBzbyB0aGF0IFZlcnRleCBpcyBhdCAoMCwwKVxuXHRcdFx0XHR2YXIgbGVnRnJvbVB0WFx0PSBpbkxlZ0Zyb21QdC54IC0gaW5WZXJ0ZXgueCwgIGxlZ0Zyb21QdFlcdD0gaW5MZWdGcm9tUHQueSAtIGluVmVydGV4Lnk7XG5cdFx0XHRcdHZhciBsZWdUb1B0WFx0PSBpbkxlZ1RvUHQueFx0LSBpblZlcnRleC54LCAgbGVnVG9QdFlcdFx0PSBpbkxlZ1RvUHQueVx0LSBpblZlcnRleC55O1xuXHRcdFx0XHR2YXIgb3RoZXJQdFhcdD0gaW5PdGhlclB0LnhcdC0gaW5WZXJ0ZXgueCwgIG90aGVyUHRZXHRcdD0gaW5PdGhlclB0LnlcdC0gaW5WZXJ0ZXgueTtcblxuXHRcdFx0XHQvLyBtYWluIGFuZ2xlID4wOiA8IDE4MCBkZWcuOyAwOiAxODAgZGVnLjsgPDA6ID4gMTgwIGRlZy5cblx0XHRcdFx0dmFyIGZyb20ydG9BbmdsZVx0PSBsZWdGcm9tUHRYICogbGVnVG9QdFkgLSBsZWdGcm9tUHRZICogbGVnVG9QdFg7XG5cdFx0XHRcdHZhciBmcm9tMm90aGVyQW5nbGVcdD0gbGVnRnJvbVB0WCAqIG90aGVyUHRZIC0gbGVnRnJvbVB0WSAqIG90aGVyUHRYO1xuXG5cdFx0XHRcdGlmICggTWF0aC5hYnMoIGZyb20ydG9BbmdsZSApID4gTnVtYmVyLkVQU0lMT04gKSB7XG5cblx0XHRcdFx0XHQvLyBhbmdsZSAhPSAxODAgZGVnLlxuXG5cdFx0XHRcdFx0dmFyIG90aGVyMnRvQW5nbGVcdFx0PSBvdGhlclB0WCAqIGxlZ1RvUHRZIC0gb3RoZXJQdFkgKiBsZWdUb1B0WDtcblx0XHRcdFx0XHQvLyBjb25zb2xlLmxvZyggXCJmcm9tMnRvOiBcIiArIGZyb20ydG9BbmdsZSArIFwiLCBmcm9tMm90aGVyOiBcIiArIGZyb20yb3RoZXJBbmdsZSArIFwiLCBvdGhlcjJ0bzogXCIgKyBvdGhlcjJ0b0FuZ2xlICk7XG5cblx0XHRcdFx0XHRpZiAoIGZyb20ydG9BbmdsZSA+IDAgKSB7XG5cblx0XHRcdFx0XHRcdC8vIG1haW4gYW5nbGUgPCAxODAgZGVnLlxuXHRcdFx0XHRcdFx0cmV0dXJuXHQoICggZnJvbTJvdGhlckFuZ2xlID49IDAgKSAmJiAoIG90aGVyMnRvQW5nbGUgPj0gMCApICk7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHQvLyBtYWluIGFuZ2xlID4gMTgwIGRlZy5cblx0XHRcdFx0XHRcdHJldHVyblx0KCAoIGZyb20yb3RoZXJBbmdsZSA+PSAwICkgfHwgKCBvdGhlcjJ0b0FuZ2xlID49IDAgKSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHQvLyBhbmdsZSA9PSAxODAgZGVnLlxuXHRcdFx0XHRcdC8vIGNvbnNvbGUubG9nKCBcImZyb20ydG86IDE4MCBkZWcuLCBmcm9tMm90aGVyOiBcIiArIGZyb20yb3RoZXJBbmdsZSAgKTtcblx0XHRcdFx0XHRyZXR1cm5cdCggZnJvbTJvdGhlckFuZ2xlID4gMCApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cblx0XHRcdGZ1bmN0aW9uIHJlbW92ZUhvbGVzKCBjb250b3VyLCBob2xlcyApIHtcblxuXHRcdFx0XHR2YXIgc2hhcGUgPSBjb250b3VyLmNvbmNhdCgpOyAvLyB3b3JrIG9uIHRoaXMgc2hhcGVcblx0XHRcdFx0dmFyIGhvbGU7XG5cblx0XHRcdFx0ZnVuY3Rpb24gaXNDdXRMaW5lSW5zaWRlQW5nbGVzKCBpblNoYXBlSWR4LCBpbkhvbGVJZHggKSB7XG5cblx0XHRcdFx0XHQvLyBDaGVjayBpZiBob2xlIHBvaW50IGxpZXMgd2l0aGluIGFuZ2xlIGFyb3VuZCBzaGFwZSBwb2ludFxuXHRcdFx0XHRcdHZhciBsYXN0U2hhcGVJZHggPSBzaGFwZS5sZW5ndGggLSAxO1xuXG5cdFx0XHRcdFx0dmFyIHByZXZTaGFwZUlkeCA9IGluU2hhcGVJZHggLSAxO1xuXHRcdFx0XHRcdGlmICggcHJldlNoYXBlSWR4IDwgMCApXHRcdFx0cHJldlNoYXBlSWR4ID0gbGFzdFNoYXBlSWR4O1xuXG5cdFx0XHRcdFx0dmFyIG5leHRTaGFwZUlkeCA9IGluU2hhcGVJZHggKyAxO1xuXHRcdFx0XHRcdGlmICggbmV4dFNoYXBlSWR4ID4gbGFzdFNoYXBlSWR4IClcdG5leHRTaGFwZUlkeCA9IDA7XG5cblx0XHRcdFx0XHR2YXIgaW5zaWRlQW5nbGUgPSBpc1BvaW50SW5zaWRlQW5nbGUoIHNoYXBlWyBpblNoYXBlSWR4IF0sIHNoYXBlWyBwcmV2U2hhcGVJZHggXSwgc2hhcGVbIG5leHRTaGFwZUlkeCBdLCBob2xlWyBpbkhvbGVJZHggXSApO1xuXHRcdFx0XHRcdGlmICggISBpbnNpZGVBbmdsZSApIHtcblxuXHRcdFx0XHRcdFx0Ly8gY29uc29sZS5sb2coIFwiVmVydGV4IChTaGFwZSk6IFwiICsgaW5TaGFwZUlkeCArIFwiLCBQb2ludDogXCIgKyBob2xlW2luSG9sZUlkeF0ueCArIFwiL1wiICsgaG9sZVtpbkhvbGVJZHhdLnkgKTtcblx0XHRcdFx0XHRcdHJldHVyblx0ZmFsc2U7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHQvLyBDaGVjayBpZiBzaGFwZSBwb2ludCBsaWVzIHdpdGhpbiBhbmdsZSBhcm91bmQgaG9sZSBwb2ludFxuXHRcdFx0XHRcdHZhciBsYXN0SG9sZUlkeCA9IGhvbGUubGVuZ3RoIC0gMTtcblxuXHRcdFx0XHRcdHZhciBwcmV2SG9sZUlkeCA9IGluSG9sZUlkeCAtIDE7XG5cdFx0XHRcdFx0aWYgKCBwcmV2SG9sZUlkeCA8IDAgKVx0XHRcdHByZXZIb2xlSWR4ID0gbGFzdEhvbGVJZHg7XG5cblx0XHRcdFx0XHR2YXIgbmV4dEhvbGVJZHggPSBpbkhvbGVJZHggKyAxO1xuXHRcdFx0XHRcdGlmICggbmV4dEhvbGVJZHggPiBsYXN0SG9sZUlkeCApXHRuZXh0SG9sZUlkeCA9IDA7XG5cblx0XHRcdFx0XHRpbnNpZGVBbmdsZSA9IGlzUG9pbnRJbnNpZGVBbmdsZSggaG9sZVsgaW5Ib2xlSWR4IF0sIGhvbGVbIHByZXZIb2xlSWR4IF0sIGhvbGVbIG5leHRIb2xlSWR4IF0sIHNoYXBlWyBpblNoYXBlSWR4IF0gKTtcblx0XHRcdFx0XHRpZiAoICEgaW5zaWRlQW5nbGUgKSB7XG5cblx0XHRcdFx0XHRcdC8vIGNvbnNvbGUubG9nKCBcIlZlcnRleCAoSG9sZSk6IFwiICsgaW5Ib2xlSWR4ICsgXCIsIFBvaW50OiBcIiArIHNoYXBlW2luU2hhcGVJZHhdLnggKyBcIi9cIiArIHNoYXBlW2luU2hhcGVJZHhdLnkgKTtcblx0XHRcdFx0XHRcdHJldHVyblx0ZmFsc2U7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRyZXR1cm5cdHRydWU7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGZ1bmN0aW9uIGludGVyc2VjdHNTaGFwZUVkZ2UoIGluU2hhcGVQdCwgaW5Ib2xlUHQgKSB7XG5cblx0XHRcdFx0XHQvLyBjaGVja3MgZm9yIGludGVyc2VjdGlvbnMgd2l0aCBzaGFwZSBlZGdlc1xuXHRcdFx0XHRcdHZhciBzSWR4LCBuZXh0SWR4LCBpbnRlcnNlY3Rpb247XG5cdFx0XHRcdFx0Zm9yICggc0lkeCA9IDA7IHNJZHggPCBzaGFwZS5sZW5ndGg7IHNJZHggKysgKSB7XG5cblx0XHRcdFx0XHRcdG5leHRJZHggPSBzSWR4ICsgMTsgbmV4dElkeCAlPSBzaGFwZS5sZW5ndGg7XG5cdFx0XHRcdFx0XHRpbnRlcnNlY3Rpb24gPSBpbnRlcnNlY3Rfc2VnbWVudHNfMkQoIGluU2hhcGVQdCwgaW5Ib2xlUHQsIHNoYXBlWyBzSWR4IF0sIHNoYXBlWyBuZXh0SWR4IF0sIHRydWUgKTtcblx0XHRcdFx0XHRcdGlmICggaW50ZXJzZWN0aW9uLmxlbmd0aCA+IDAgKVx0XHRyZXR1cm5cdHRydWU7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRyZXR1cm5cdGZhbHNlO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR2YXIgaW5kZXBIb2xlcyA9IFtdO1xuXG5cdFx0XHRcdGZ1bmN0aW9uIGludGVyc2VjdHNIb2xlRWRnZSggaW5TaGFwZVB0LCBpbkhvbGVQdCApIHtcblxuXHRcdFx0XHRcdC8vIGNoZWNrcyBmb3IgaW50ZXJzZWN0aW9ucyB3aXRoIGhvbGUgZWRnZXNcblx0XHRcdFx0XHR2YXIgaWhJZHgsIGNoa0hvbGUsXG5cdFx0XHRcdFx0XHRoSWR4LCBuZXh0SWR4LCBpbnRlcnNlY3Rpb247XG5cdFx0XHRcdFx0Zm9yICggaWhJZHggPSAwOyBpaElkeCA8IGluZGVwSG9sZXMubGVuZ3RoOyBpaElkeCArKyApIHtcblxuXHRcdFx0XHRcdFx0Y2hrSG9sZSA9IGhvbGVzWyBpbmRlcEhvbGVzWyBpaElkeCBdXTtcblx0XHRcdFx0XHRcdGZvciAoIGhJZHggPSAwOyBoSWR4IDwgY2hrSG9sZS5sZW5ndGg7IGhJZHggKysgKSB7XG5cblx0XHRcdFx0XHRcdFx0bmV4dElkeCA9IGhJZHggKyAxOyBuZXh0SWR4ICU9IGNoa0hvbGUubGVuZ3RoO1xuXHRcdFx0XHRcdFx0XHRpbnRlcnNlY3Rpb24gPSBpbnRlcnNlY3Rfc2VnbWVudHNfMkQoIGluU2hhcGVQdCwgaW5Ib2xlUHQsIGNoa0hvbGVbIGhJZHggXSwgY2hrSG9sZVsgbmV4dElkeCBdLCB0cnVlICk7XG5cdFx0XHRcdFx0XHRcdGlmICggaW50ZXJzZWN0aW9uLmxlbmd0aCA+IDAgKVx0XHRyZXR1cm5cdHRydWU7XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRyZXR1cm5cdGZhbHNlO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR2YXIgaG9sZUluZGV4LCBzaGFwZUluZGV4LFxuXHRcdFx0XHRcdHNoYXBlUHQsIGhvbGVQdCxcblx0XHRcdFx0XHRob2xlSWR4LCBjdXRLZXksIGZhaWxlZEN1dHMgPSBbXSxcblx0XHRcdFx0XHR0bXBTaGFwZTEsIHRtcFNoYXBlMixcblx0XHRcdFx0XHR0bXBIb2xlMSwgdG1wSG9sZTI7XG5cblx0XHRcdFx0Zm9yICggdmFyIGggPSAwLCBobCA9IGhvbGVzLmxlbmd0aDsgaCA8IGhsOyBoICsrICkge1xuXG5cdFx0XHRcdFx0aW5kZXBIb2xlcy5wdXNoKCBoICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBtaW5TaGFwZUluZGV4ID0gMDtcblx0XHRcdFx0dmFyIGNvdW50ZXIgPSBpbmRlcEhvbGVzLmxlbmd0aCAqIDI7XG5cdFx0XHRcdHdoaWxlICggaW5kZXBIb2xlcy5sZW5ndGggPiAwICkge1xuXG5cdFx0XHRcdFx0Y291bnRlciAtLTtcblx0XHRcdFx0XHRpZiAoIGNvdW50ZXIgPCAwICkge1xuXG5cdFx0XHRcdFx0XHRjb25zb2xlLmxvZyggXCJJbmZpbml0ZSBMb29wISBIb2xlcyBsZWZ0OlwiICsgaW5kZXBIb2xlcy5sZW5ndGggKyBcIiwgUHJvYmFibHkgSG9sZSBvdXRzaWRlIFNoYXBlIVwiICk7XG5cdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vIHNlYXJjaCBmb3Igc2hhcGUtdmVydGV4IGFuZCBob2xlLXZlcnRleCxcblx0XHRcdFx0XHQvLyB3aGljaCBjYW4gYmUgY29ubmVjdGVkIHdpdGhvdXQgaW50ZXJzZWN0aW9uc1xuXHRcdFx0XHRcdGZvciAoIHNoYXBlSW5kZXggPSBtaW5TaGFwZUluZGV4OyBzaGFwZUluZGV4IDwgc2hhcGUubGVuZ3RoOyBzaGFwZUluZGV4ICsrICkge1xuXG5cdFx0XHRcdFx0XHRzaGFwZVB0ID0gc2hhcGVbIHNoYXBlSW5kZXggXTtcblx0XHRcdFx0XHRcdGhvbGVJbmRleFx0PSAtIDE7XG5cblx0XHRcdFx0XHRcdC8vIHNlYXJjaCBmb3IgaG9sZSB3aGljaCBjYW4gYmUgcmVhY2hlZCB3aXRob3V0IGludGVyc2VjdGlvbnNcblx0XHRcdFx0XHRcdGZvciAoIHZhciBoID0gMDsgaCA8IGluZGVwSG9sZXMubGVuZ3RoOyBoICsrICkge1xuXG5cdFx0XHRcdFx0XHRcdGhvbGVJZHggPSBpbmRlcEhvbGVzWyBoIF07XG5cblx0XHRcdFx0XHRcdFx0Ly8gcHJldmVudCBtdWx0aXBsZSBjaGVja3Ncblx0XHRcdFx0XHRcdFx0Y3V0S2V5ID0gc2hhcGVQdC54ICsgXCI6XCIgKyBzaGFwZVB0LnkgKyBcIjpcIiArIGhvbGVJZHg7XG5cdFx0XHRcdFx0XHRcdGlmICggZmFpbGVkQ3V0c1sgY3V0S2V5IF0gIT09IHVuZGVmaW5lZCApXHRcdFx0Y29udGludWU7XG5cblx0XHRcdFx0XHRcdFx0aG9sZSA9IGhvbGVzWyBob2xlSWR4IF07XG5cdFx0XHRcdFx0XHRcdGZvciAoIHZhciBoMiA9IDA7IGgyIDwgaG9sZS5sZW5ndGg7IGgyICsrICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0aG9sZVB0ID0gaG9sZVsgaDIgXTtcblx0XHRcdFx0XHRcdFx0XHRpZiAoICEgaXNDdXRMaW5lSW5zaWRlQW5nbGVzKCBzaGFwZUluZGV4LCBoMiApIClcdFx0Y29udGludWU7XG5cdFx0XHRcdFx0XHRcdFx0aWYgKCBpbnRlcnNlY3RzU2hhcGVFZGdlKCBzaGFwZVB0LCBob2xlUHQgKSApXHRcdGNvbnRpbnVlO1xuXHRcdFx0XHRcdFx0XHRcdGlmICggaW50ZXJzZWN0c0hvbGVFZGdlKCBzaGFwZVB0LCBob2xlUHQgKSApXHRcdGNvbnRpbnVlO1xuXG5cdFx0XHRcdFx0XHRcdFx0aG9sZUluZGV4ID0gaDI7XG5cdFx0XHRcdFx0XHRcdFx0aW5kZXBIb2xlcy5zcGxpY2UoIGgsIDEgKTtcblxuXHRcdFx0XHRcdFx0XHRcdHRtcFNoYXBlMSA9IHNoYXBlLnNsaWNlKCAwLCBzaGFwZUluZGV4ICsgMSApO1xuXHRcdFx0XHRcdFx0XHRcdHRtcFNoYXBlMiA9IHNoYXBlLnNsaWNlKCBzaGFwZUluZGV4ICk7XG5cdFx0XHRcdFx0XHRcdFx0dG1wSG9sZTEgPSBob2xlLnNsaWNlKCBob2xlSW5kZXggKTtcblx0XHRcdFx0XHRcdFx0XHR0bXBIb2xlMiA9IGhvbGUuc2xpY2UoIDAsIGhvbGVJbmRleCArIDEgKTtcblxuXHRcdFx0XHRcdFx0XHRcdHNoYXBlID0gdG1wU2hhcGUxLmNvbmNhdCggdG1wSG9sZTEgKS5jb25jYXQoIHRtcEhvbGUyICkuY29uY2F0KCB0bXBTaGFwZTIgKTtcblxuXHRcdFx0XHRcdFx0XHRcdG1pblNoYXBlSW5kZXggPSBzaGFwZUluZGV4O1xuXG5cdFx0XHRcdFx0XHRcdFx0Ly8gRGVidWcgb25seSwgdG8gc2hvdyB0aGUgc2VsZWN0ZWQgY3V0c1xuXHRcdFx0XHRcdFx0XHRcdC8vIGdsb2JfQ3V0TGluZXMucHVzaCggWyBzaGFwZVB0LCBob2xlUHQgXSApO1xuXG5cdFx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0XHRpZiAoIGhvbGVJbmRleCA+PSAwIClcdGJyZWFrO1x0XHQvLyBob2xlLXZlcnRleCBmb3VuZFxuXG5cdFx0XHRcdFx0XHRcdGZhaWxlZEN1dHNbIGN1dEtleSBdID0gdHJ1ZTtcdFx0XHQvLyByZW1lbWJlciBmYWlsdXJlXG5cblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdGlmICggaG9sZUluZGV4ID49IDAgKVx0YnJlYWs7XHRcdC8vIGhvbGUtdmVydGV4IGZvdW5kXG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJldHVybiBzaGFwZTsgXHRcdFx0Lyogc2hhcGUgd2l0aCBubyBob2xlcyAqL1xuXG5cdFx0XHR9XG5cblxuXHRcdFx0dmFyIGksIGlsLCBmLCBmYWNlLFxuXHRcdFx0XHRrZXksIGluZGV4LFxuXHRcdFx0XHRhbGxQb2ludHNNYXAgPSB7fTtcblxuXHRcdFx0Ly8gVG8gbWFpbnRhaW4gcmVmZXJlbmNlIHRvIG9sZCBzaGFwZSwgb25lIG11c3QgbWF0Y2ggY29vcmRpbmF0ZXMsIG9yIG9mZnNldCB0aGUgaW5kaWNlcyBmcm9tIG9yaWdpbmFsIGFycmF5cy4gSXQncyBwcm9iYWJseSBlYXNpZXIgdG8gZG8gdGhlIGZpcnN0LlxuXG5cdFx0XHR2YXIgYWxscG9pbnRzID0gY29udG91ci5jb25jYXQoKTtcblxuXHRcdFx0Zm9yICggdmFyIGggPSAwLCBobCA9IGhvbGVzLmxlbmd0aDsgaCA8IGhsOyBoICsrICkge1xuXG5cdFx0XHRcdEFycmF5LnByb3RvdHlwZS5wdXNoLmFwcGx5KCBhbGxwb2ludHMsIGhvbGVzWyBoIF0gKTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvL2NvbnNvbGUubG9nKCBcImFsbHBvaW50c1wiLGFsbHBvaW50cywgYWxscG9pbnRzLmxlbmd0aCApO1xuXG5cdFx0XHQvLyBwcmVwYXJlIGFsbCBwb2ludHMgbWFwXG5cblx0XHRcdGZvciAoIGkgPSAwLCBpbCA9IGFsbHBvaW50cy5sZW5ndGg7IGkgPCBpbDsgaSArKyApIHtcblxuXHRcdFx0XHRrZXkgPSBhbGxwb2ludHNbIGkgXS54ICsgXCI6XCIgKyBhbGxwb2ludHNbIGkgXS55O1xuXG5cdFx0XHRcdGlmICggYWxsUG9pbnRzTWFwWyBrZXkgXSAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0Y29uc29sZS53YXJuKCBcIlRIUkVFLlNoYXBlVXRpbHM6IER1cGxpY2F0ZSBwb2ludFwiLCBrZXksIGkgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0YWxsUG9pbnRzTWFwWyBrZXkgXSA9IGk7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gcmVtb3ZlIGhvbGVzIGJ5IGN1dHRpbmcgcGF0aHMgdG8gaG9sZXMgYW5kIGFkZGluZyB0aGVtIHRvIHRoZSBzaGFwZVxuXHRcdFx0dmFyIHNoYXBlV2l0aG91dEhvbGVzID0gcmVtb3ZlSG9sZXMoIGNvbnRvdXIsIGhvbGVzICk7XG5cblx0XHRcdHZhciB0cmlhbmdsZXMgPSBTaGFwZVV0aWxzLnRyaWFuZ3VsYXRlKCBzaGFwZVdpdGhvdXRIb2xlcywgZmFsc2UgKTsgLy8gVHJ1ZSByZXR1cm5zIGluZGljZXMgZm9yIHBvaW50cyBvZiBzcG9vbGVkIHNoYXBlXG5cdFx0XHQvL2NvbnNvbGUubG9nKCBcInRyaWFuZ2xlc1wiLHRyaWFuZ2xlcywgdHJpYW5nbGVzLmxlbmd0aCApO1xuXG5cdFx0XHQvLyBjaGVjayBhbGwgZmFjZSB2ZXJ0aWNlcyBhZ2FpbnN0IGFsbCBwb2ludHMgbWFwXG5cblx0XHRcdGZvciAoIGkgPSAwLCBpbCA9IHRyaWFuZ2xlcy5sZW5ndGg7IGkgPCBpbDsgaSArKyApIHtcblxuXHRcdFx0XHRmYWNlID0gdHJpYW5nbGVzWyBpIF07XG5cblx0XHRcdFx0Zm9yICggZiA9IDA7IGYgPCAzOyBmICsrICkge1xuXG5cdFx0XHRcdFx0a2V5ID0gZmFjZVsgZiBdLnggKyBcIjpcIiArIGZhY2VbIGYgXS55O1xuXG5cdFx0XHRcdFx0aW5kZXggPSBhbGxQb2ludHNNYXBbIGtleSBdO1xuXG5cdFx0XHRcdFx0aWYgKCBpbmRleCAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0XHRmYWNlWyBmIF0gPSBpbmRleDtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRyaWFuZ2xlcy5jb25jYXQoKTtcblxuXHRcdH0sXG5cblx0XHRpc0Nsb2NrV2lzZTogZnVuY3Rpb24gKCBwdHMgKSB7XG5cblx0XHRcdHJldHVybiBTaGFwZVV0aWxzLmFyZWEoIHB0cyApIDwgMDtcblxuXHRcdH0sXG5cblx0XHQvLyBCZXppZXIgQ3VydmVzIGZvcm11bGFzIG9idGFpbmVkIGZyb21cblx0XHQvLyBodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0IlQzMlQTl6aWVyX2N1cnZlXG5cblx0XHQvLyBRdWFkIEJlemllciBGdW5jdGlvbnNcblxuXHRcdGIyOiAoIGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0ZnVuY3Rpb24gYjJwMCggdCwgcCApIHtcblxuXHRcdFx0XHR2YXIgayA9IDEgLSB0O1xuXHRcdFx0XHRyZXR1cm4gayAqIGsgKiBwO1xuXG5cdFx0XHR9XG5cblx0XHRcdGZ1bmN0aW9uIGIycDEoIHQsIHAgKSB7XG5cblx0XHRcdFx0cmV0dXJuIDIgKiAoIDEgLSB0ICkgKiB0ICogcDtcblxuXHRcdFx0fVxuXG5cdFx0XHRmdW5jdGlvbiBiMnAyKCB0LCBwICkge1xuXG5cdFx0XHRcdHJldHVybiB0ICogdCAqIHA7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIGIyKCB0LCBwMCwgcDEsIHAyICkge1xuXG5cdFx0XHRcdHJldHVybiBiMnAwKCB0LCBwMCApICsgYjJwMSggdCwgcDEgKSArIGIycDIoIHQsIHAyICk7XG5cblx0XHRcdH07XG5cblx0XHR9ICkoKSxcblxuXHRcdC8vIEN1YmljIEJlemllciBGdW5jdGlvbnNcblxuXHRcdGIzOiAoIGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0ZnVuY3Rpb24gYjNwMCggdCwgcCApIHtcblxuXHRcdFx0XHR2YXIgayA9IDEgLSB0O1xuXHRcdFx0XHRyZXR1cm4gayAqIGsgKiBrICogcDtcblxuXHRcdFx0fVxuXG5cdFx0XHRmdW5jdGlvbiBiM3AxKCB0LCBwICkge1xuXG5cdFx0XHRcdHZhciBrID0gMSAtIHQ7XG5cdFx0XHRcdHJldHVybiAzICogayAqIGsgKiB0ICogcDtcblxuXHRcdFx0fVxuXG5cdFx0XHRmdW5jdGlvbiBiM3AyKCB0LCBwICkge1xuXG5cdFx0XHRcdHZhciBrID0gMSAtIHQ7XG5cdFx0XHRcdHJldHVybiAzICogayAqIHQgKiB0ICogcDtcblxuXHRcdFx0fVxuXG5cdFx0XHRmdW5jdGlvbiBiM3AzKCB0LCBwICkge1xuXG5cdFx0XHRcdHJldHVybiB0ICogdCAqIHQgKiBwO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBiMyggdCwgcDAsIHAxLCBwMiwgcDMgKSB7XG5cblx0XHRcdFx0cmV0dXJuIGIzcDAoIHQsIHAwICkgKyBiM3AxKCB0LCBwMSApICsgYjNwMiggdCwgcDIgKSArIGIzcDMoIHQsIHAzICk7XG5cblx0XHRcdH07XG5cblx0XHR9ICkoKVxuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3Igeno4NSAvIGh0dHA6Ly93d3cubGFiNGdhbWVzLm5ldC96ejg1L2Jsb2dcblx0ICpcblx0ICogQ3JlYXRlcyBleHRydWRlZCBnZW9tZXRyeSBmcm9tIGEgcGF0aCBzaGFwZS5cblx0ICpcblx0ICogcGFyYW1ldGVycyA9IHtcblx0ICpcblx0ICogIGN1cnZlU2VnbWVudHM6IDxpbnQ+LCAvLyBudW1iZXIgb2YgcG9pbnRzIG9uIHRoZSBjdXJ2ZXNcblx0ICogIHN0ZXBzOiA8aW50PiwgLy8gbnVtYmVyIG9mIHBvaW50cyBmb3Igei1zaWRlIGV4dHJ1c2lvbnMgLyB1c2VkIGZvciBzdWJkaXZpZGluZyBzZWdtZW50cyBvZiBleHRydWRlIHNwbGluZSB0b29cblx0ICogIGFtb3VudDogPGludD4sIC8vIERlcHRoIHRvIGV4dHJ1ZGUgdGhlIHNoYXBlXG5cdCAqXG5cdCAqICBiZXZlbEVuYWJsZWQ6IDxib29sPiwgLy8gdHVybiBvbiBiZXZlbFxuXHQgKiAgYmV2ZWxUaGlja25lc3M6IDxmbG9hdD4sIC8vIGhvdyBkZWVwIGludG8gdGhlIG9yaWdpbmFsIHNoYXBlIGJldmVsIGdvZXNcblx0ICogIGJldmVsU2l6ZTogPGZsb2F0PiwgLy8gaG93IGZhciBmcm9tIHNoYXBlIG91dGxpbmUgaXMgYmV2ZWxcblx0ICogIGJldmVsU2VnbWVudHM6IDxpbnQ+LCAvLyBudW1iZXIgb2YgYmV2ZWwgbGF5ZXJzXG5cdCAqXG5cdCAqICBleHRydWRlUGF0aDogPFRIUkVFLkN1cnZlPiAvLyBjdXJ2ZSB0byBleHRydWRlIHNoYXBlIGFsb25nXG5cdCAqICBmcmFtZXM6IDxPYmplY3Q+IC8vIGNvbnRhaW5pbmcgYXJyYXlzIG9mIHRhbmdlbnRzLCBub3JtYWxzLCBiaW5vcm1hbHNcblx0ICpcblx0ICogIHV2R2VuZXJhdG9yOiA8T2JqZWN0PiAvLyBvYmplY3QgdGhhdCBwcm92aWRlcyBVViBnZW5lcmF0b3IgZnVuY3Rpb25zXG5cdCAqXG5cdCAqIH1cblx0ICoqL1xuXG5cdGZ1bmN0aW9uIEV4dHJ1ZGVHZW9tZXRyeSggc2hhcGVzLCBvcHRpb25zICkge1xuXG5cdFx0aWYgKCB0eXBlb2YoIHNoYXBlcyApID09PSBcInVuZGVmaW5lZFwiICkge1xuXG5cdFx0XHRzaGFwZXMgPSBbXTtcblx0XHRcdHJldHVybjtcblxuXHRcdH1cblxuXHRcdEdlb21ldHJ5LmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdFeHRydWRlR2VvbWV0cnknO1xuXG5cdFx0c2hhcGVzID0gQXJyYXkuaXNBcnJheSggc2hhcGVzICkgPyBzaGFwZXMgOiBbIHNoYXBlcyBdO1xuXG5cdFx0dGhpcy5hZGRTaGFwZUxpc3QoIHNoYXBlcywgb3B0aW9ucyApO1xuXG5cdFx0dGhpcy5jb21wdXRlRmFjZU5vcm1hbHMoKTtcblxuXHRcdC8vIGNhbid0IHJlYWxseSB1c2UgYXV0b21hdGljIHZlcnRleCBub3JtYWxzXG5cdFx0Ly8gYXMgdGhlbiBmcm9udCBhbmQgYmFjayBzaWRlcyBnZXQgc21vb3RoZWQgdG9vXG5cdFx0Ly8gc2hvdWxkIGRvIHNlcGFyYXRlIHNtb290aGluZyBqdXN0IGZvciBzaWRlc1xuXG5cdFx0Ly90aGlzLmNvbXB1dGVWZXJ0ZXhOb3JtYWxzKCk7XG5cblx0XHQvL2NvbnNvbGUubG9nKCBcInRvb2tcIiwgKCBEYXRlLm5vdygpIC0gc3RhcnRUaW1lICkgKTtcblxuXHR9XG5cblx0RXh0cnVkZUdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRFeHRydWRlR2VvbWV0cnkucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gRXh0cnVkZUdlb21ldHJ5O1xuXG5cdEV4dHJ1ZGVHZW9tZXRyeS5wcm90b3R5cGUuYWRkU2hhcGVMaXN0ID0gZnVuY3Rpb24gKCBzaGFwZXMsIG9wdGlvbnMgKSB7XG5cblx0XHR2YXIgc2wgPSBzaGFwZXMubGVuZ3RoO1xuXG5cdFx0Zm9yICggdmFyIHMgPSAwOyBzIDwgc2w7IHMgKysgKSB7XG5cblx0XHRcdHZhciBzaGFwZSA9IHNoYXBlc1sgcyBdO1xuXHRcdFx0dGhpcy5hZGRTaGFwZSggc2hhcGUsIG9wdGlvbnMgKTtcblxuXHRcdH1cblxuXHR9O1xuXG5cdEV4dHJ1ZGVHZW9tZXRyeS5wcm90b3R5cGUuYWRkU2hhcGUgPSBmdW5jdGlvbiAoIHNoYXBlLCBvcHRpb25zICkge1xuXG5cdFx0dmFyIGFtb3VudCA9IG9wdGlvbnMuYW1vdW50ICE9PSB1bmRlZmluZWQgPyBvcHRpb25zLmFtb3VudCA6IDEwMDtcblxuXHRcdHZhciBiZXZlbFRoaWNrbmVzcyA9IG9wdGlvbnMuYmV2ZWxUaGlja25lc3MgIT09IHVuZGVmaW5lZCA/IG9wdGlvbnMuYmV2ZWxUaGlja25lc3MgOiA2OyAvLyAxMFxuXHRcdHZhciBiZXZlbFNpemUgPSBvcHRpb25zLmJldmVsU2l6ZSAhPT0gdW5kZWZpbmVkID8gb3B0aW9ucy5iZXZlbFNpemUgOiBiZXZlbFRoaWNrbmVzcyAtIDI7IC8vIDhcblx0XHR2YXIgYmV2ZWxTZWdtZW50cyA9IG9wdGlvbnMuYmV2ZWxTZWdtZW50cyAhPT0gdW5kZWZpbmVkID8gb3B0aW9ucy5iZXZlbFNlZ21lbnRzIDogMztcblxuXHRcdHZhciBiZXZlbEVuYWJsZWQgPSBvcHRpb25zLmJldmVsRW5hYmxlZCAhPT0gdW5kZWZpbmVkID8gb3B0aW9ucy5iZXZlbEVuYWJsZWQgOiB0cnVlOyAvLyBmYWxzZVxuXG5cdFx0dmFyIGN1cnZlU2VnbWVudHMgPSBvcHRpb25zLmN1cnZlU2VnbWVudHMgIT09IHVuZGVmaW5lZCA/IG9wdGlvbnMuY3VydmVTZWdtZW50cyA6IDEyO1xuXG5cdFx0dmFyIHN0ZXBzID0gb3B0aW9ucy5zdGVwcyAhPT0gdW5kZWZpbmVkID8gb3B0aW9ucy5zdGVwcyA6IDE7XG5cblx0XHR2YXIgZXh0cnVkZVBhdGggPSBvcHRpb25zLmV4dHJ1ZGVQYXRoO1xuXHRcdHZhciBleHRydWRlUHRzLCBleHRydWRlQnlQYXRoID0gZmFsc2U7XG5cblx0XHQvLyBVc2UgZGVmYXVsdCBXb3JsZFVWR2VuZXJhdG9yIGlmIG5vIFVWIGdlbmVyYXRvcnMgYXJlIHNwZWNpZmllZC5cblx0XHR2YXIgdXZnZW4gPSBvcHRpb25zLlVWR2VuZXJhdG9yICE9PSB1bmRlZmluZWQgPyBvcHRpb25zLlVWR2VuZXJhdG9yIDogRXh0cnVkZUdlb21ldHJ5LldvcmxkVVZHZW5lcmF0b3I7XG5cblx0XHR2YXIgc3BsaW5lVHViZSwgYmlub3JtYWwsIG5vcm1hbCwgcG9zaXRpb24yO1xuXHRcdGlmICggZXh0cnVkZVBhdGggKSB7XG5cblx0XHRcdGV4dHJ1ZGVQdHMgPSBleHRydWRlUGF0aC5nZXRTcGFjZWRQb2ludHMoIHN0ZXBzICk7XG5cblx0XHRcdGV4dHJ1ZGVCeVBhdGggPSB0cnVlO1xuXHRcdFx0YmV2ZWxFbmFibGVkID0gZmFsc2U7IC8vIGJldmVscyBub3Qgc3VwcG9ydGVkIGZvciBwYXRoIGV4dHJ1c2lvblxuXG5cdFx0XHQvLyBTRVRVUCBUTkIgdmFyaWFibGVzXG5cblx0XHRcdC8vIFRPRE8xIC0gaGF2ZSBhIC5pc0Nsb3NlZCBpbiBzcGxpbmU/XG5cblx0XHRcdHNwbGluZVR1YmUgPSBvcHRpb25zLmZyYW1lcyAhPT0gdW5kZWZpbmVkID8gb3B0aW9ucy5mcmFtZXMgOiBleHRydWRlUGF0aC5jb21wdXRlRnJlbmV0RnJhbWVzKCBzdGVwcywgZmFsc2UgKTtcblxuXHRcdFx0Ly8gY29uc29sZS5sb2coc3BsaW5lVHViZSwgJ3NwbGluZVR1YmUnLCBzcGxpbmVUdWJlLm5vcm1hbHMubGVuZ3RoLCAnc3RlcHMnLCBzdGVwcywgJ2V4dHJ1ZGVQdHMnLCBleHRydWRlUHRzLmxlbmd0aCk7XG5cblx0XHRcdGJpbm9ybWFsID0gbmV3IFZlY3RvcjMoKTtcblx0XHRcdG5vcm1hbCA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHRwb3NpdGlvbjIgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0fVxuXG5cdFx0Ly8gU2FmZWd1YXJkcyBpZiBiZXZlbHMgYXJlIG5vdCBlbmFibGVkXG5cblx0XHRpZiAoICEgYmV2ZWxFbmFibGVkICkge1xuXG5cdFx0XHRiZXZlbFNlZ21lbnRzID0gMDtcblx0XHRcdGJldmVsVGhpY2tuZXNzID0gMDtcblx0XHRcdGJldmVsU2l6ZSA9IDA7XG5cblx0XHR9XG5cblx0XHQvLyBWYXJpYWJsZXMgaW5pdGlhbGl6YXRpb25cblxuXHRcdHZhciBhaG9sZSwgaCwgaGw7IC8vIGxvb3Bpbmcgb2YgaG9sZXNcblx0XHR2YXIgc2NvcGUgPSB0aGlzO1xuXG5cdFx0dmFyIHNoYXBlc09mZnNldCA9IHRoaXMudmVydGljZXMubGVuZ3RoO1xuXG5cdFx0dmFyIHNoYXBlUG9pbnRzID0gc2hhcGUuZXh0cmFjdFBvaW50cyggY3VydmVTZWdtZW50cyApO1xuXG5cdFx0dmFyIHZlcnRpY2VzID0gc2hhcGVQb2ludHMuc2hhcGU7XG5cdFx0dmFyIGhvbGVzID0gc2hhcGVQb2ludHMuaG9sZXM7XG5cblx0XHR2YXIgcmV2ZXJzZSA9ICEgU2hhcGVVdGlscy5pc0Nsb2NrV2lzZSggdmVydGljZXMgKTtcblxuXHRcdGlmICggcmV2ZXJzZSApIHtcblxuXHRcdFx0dmVydGljZXMgPSB2ZXJ0aWNlcy5yZXZlcnNlKCk7XG5cblx0XHRcdC8vIE1heWJlIHdlIHNob3VsZCBhbHNvIGNoZWNrIGlmIGhvbGVzIGFyZSBpbiB0aGUgb3Bwb3NpdGUgZGlyZWN0aW9uLCBqdXN0IHRvIGJlIHNhZmUgLi4uXG5cblx0XHRcdGZvciAoIGggPSAwLCBobCA9IGhvbGVzLmxlbmd0aDsgaCA8IGhsOyBoICsrICkge1xuXG5cdFx0XHRcdGFob2xlID0gaG9sZXNbIGggXTtcblxuXHRcdFx0XHRpZiAoIFNoYXBlVXRpbHMuaXNDbG9ja1dpc2UoIGFob2xlICkgKSB7XG5cblx0XHRcdFx0XHRob2xlc1sgaCBdID0gYWhvbGUucmV2ZXJzZSgpO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRyZXZlcnNlID0gZmFsc2U7IC8vIElmIHZlcnRpY2VzIGFyZSBpbiBvcmRlciBub3csIHdlIHNob3VsZG4ndCBuZWVkIHRvIHdvcnJ5IGFib3V0IHRoZW0gYWdhaW4gKGhvcGVmdWxseSkhXG5cblx0XHR9XG5cblxuXHRcdHZhciBmYWNlcyA9IFNoYXBlVXRpbHMudHJpYW5ndWxhdGVTaGFwZSggdmVydGljZXMsIGhvbGVzICk7XG5cblx0XHQvKiBWZXJ0aWNlcyAqL1xuXG5cdFx0dmFyIGNvbnRvdXIgPSB2ZXJ0aWNlczsgLy8gdmVydGljZXMgaGFzIGFsbCBwb2ludHMgYnV0IGNvbnRvdXIgaGFzIG9ubHkgcG9pbnRzIG9mIGNpcmN1bWZlcmVuY2VcblxuXHRcdGZvciAoIGggPSAwLCBobCA9IGhvbGVzLmxlbmd0aDsgaCA8IGhsOyBoICsrICkge1xuXG5cdFx0XHRhaG9sZSA9IGhvbGVzWyBoIF07XG5cblx0XHRcdHZlcnRpY2VzID0gdmVydGljZXMuY29uY2F0KCBhaG9sZSApO1xuXG5cdFx0fVxuXG5cblx0XHRmdW5jdGlvbiBzY2FsZVB0MiggcHQsIHZlYywgc2l6ZSApIHtcblxuXHRcdFx0aWYgKCAhIHZlYyApIGNvbnNvbGUuZXJyb3IoIFwiVEhSRUUuRXh0cnVkZUdlb21ldHJ5OiB2ZWMgZG9lcyBub3QgZXhpc3RcIiApO1xuXG5cdFx0XHRyZXR1cm4gdmVjLmNsb25lKCkubXVsdGlwbHlTY2FsYXIoIHNpemUgKS5hZGQoIHB0ICk7XG5cblx0XHR9XG5cblx0XHR2YXIgYiwgYnMsIHQsIHosXG5cdFx0XHR2ZXJ0LCB2bGVuID0gdmVydGljZXMubGVuZ3RoLFxuXHRcdFx0ZmFjZSwgZmxlbiA9IGZhY2VzLmxlbmd0aDtcblxuXG5cdFx0Ly8gRmluZCBkaXJlY3Rpb25zIGZvciBwb2ludCBtb3ZlbWVudFxuXG5cblx0XHRmdW5jdGlvbiBnZXRCZXZlbFZlYyggaW5QdCwgaW5QcmV2LCBpbk5leHQgKSB7XG5cblx0XHRcdC8vIGNvbXB1dGVzIGZvciBpblB0IHRoZSBjb3JyZXNwb25kaW5nIHBvaW50IGluUHQnIG9uIGEgbmV3IGNvbnRvdXJcblx0XHRcdC8vICAgc2hpZnRlZCBieSAxIHVuaXQgKGxlbmd0aCBvZiBub3JtYWxpemVkIHZlY3RvcikgdG8gdGhlIGxlZnRcblx0XHRcdC8vIGlmIHdlIHdhbGsgYWxvbmcgY29udG91ciBjbG9ja3dpc2UsIHRoaXMgbmV3IGNvbnRvdXIgaXMgb3V0c2lkZSB0aGUgb2xkIG9uZVxuXHRcdFx0Ly9cblx0XHRcdC8vIGluUHQnIGlzIHRoZSBpbnRlcnNlY3Rpb24gb2YgdGhlIHR3byBsaW5lcyBwYXJhbGxlbCB0byB0aGUgdHdvXG5cdFx0XHQvLyAgYWRqYWNlbnQgZWRnZXMgb2YgaW5QdCBhdCBhIGRpc3RhbmNlIG9mIDEgdW5pdCBvbiB0aGUgbGVmdCBzaWRlLlxuXG5cdFx0XHR2YXIgdl90cmFuc194LCB2X3RyYW5zX3ksIHNocmlua19ieSA9IDE7XHRcdC8vIHJlc3VsdGluZyB0cmFuc2xhdGlvbiB2ZWN0b3IgZm9yIGluUHRcblxuXHRcdFx0Ly8gZ29vZCByZWFkaW5nIGZvciBnZW9tZXRyeSBhbGdvcml0aG1zIChoZXJlOiBsaW5lLWxpbmUgaW50ZXJzZWN0aW9uKVxuXHRcdFx0Ly8gaHR0cDovL2dlb21hbGdvcml0aG1zLmNvbS9hMDUtX2ludGVyc2VjdC0xLmh0bWxcblxuXHRcdFx0dmFyIHZfcHJldl94ID0gaW5QdC54IC0gaW5QcmV2LngsIHZfcHJldl95ID0gaW5QdC55IC0gaW5QcmV2Lnk7XG5cdFx0XHR2YXIgdl9uZXh0X3ggPSBpbk5leHQueCAtIGluUHQueCwgdl9uZXh0X3kgPSBpbk5leHQueSAtIGluUHQueTtcblxuXHRcdFx0dmFyIHZfcHJldl9sZW5zcSA9ICggdl9wcmV2X3ggKiB2X3ByZXZfeCArIHZfcHJldl95ICogdl9wcmV2X3kgKTtcblxuXHRcdFx0Ly8gY2hlY2sgZm9yIGNvbGxpbmVhciBlZGdlc1xuXHRcdFx0dmFyIGNvbGxpbmVhcjAgPSAoIHZfcHJldl94ICogdl9uZXh0X3kgLSB2X3ByZXZfeSAqIHZfbmV4dF94ICk7XG5cblx0XHRcdGlmICggTWF0aC5hYnMoIGNvbGxpbmVhcjAgKSA+IE51bWJlci5FUFNJTE9OICkge1xuXG5cdFx0XHRcdC8vIG5vdCBjb2xsaW5lYXJcblxuXHRcdFx0XHQvLyBsZW5ndGggb2YgdmVjdG9ycyBmb3Igbm9ybWFsaXppbmdcblxuXHRcdFx0XHR2YXIgdl9wcmV2X2xlbiA9IE1hdGguc3FydCggdl9wcmV2X2xlbnNxICk7XG5cdFx0XHRcdHZhciB2X25leHRfbGVuID0gTWF0aC5zcXJ0KCB2X25leHRfeCAqIHZfbmV4dF94ICsgdl9uZXh0X3kgKiB2X25leHRfeSApO1xuXG5cdFx0XHRcdC8vIHNoaWZ0IGFkamFjZW50IHBvaW50cyBieSB1bml0IHZlY3RvcnMgdG8gdGhlIGxlZnRcblxuXHRcdFx0XHR2YXIgcHRQcmV2U2hpZnRfeCA9ICggaW5QcmV2LnggLSB2X3ByZXZfeSAvIHZfcHJldl9sZW4gKTtcblx0XHRcdFx0dmFyIHB0UHJldlNoaWZ0X3kgPSAoIGluUHJldi55ICsgdl9wcmV2X3ggLyB2X3ByZXZfbGVuICk7XG5cblx0XHRcdFx0dmFyIHB0TmV4dFNoaWZ0X3ggPSAoIGluTmV4dC54IC0gdl9uZXh0X3kgLyB2X25leHRfbGVuICk7XG5cdFx0XHRcdHZhciBwdE5leHRTaGlmdF95ID0gKCBpbk5leHQueSArIHZfbmV4dF94IC8gdl9uZXh0X2xlbiApO1xuXG5cdFx0XHRcdC8vIHNjYWxpbmcgZmFjdG9yIGZvciB2X3ByZXYgdG8gaW50ZXJzZWN0aW9uIHBvaW50XG5cblx0XHRcdFx0dmFyIHNmID0gKCAgKCBwdE5leHRTaGlmdF94IC0gcHRQcmV2U2hpZnRfeCApICogdl9uZXh0X3kgLVxuXHRcdFx0XHRcdFx0XHQoIHB0TmV4dFNoaWZ0X3kgLSBwdFByZXZTaGlmdF95ICkgKiB2X25leHRfeCAgICApIC9cblx0XHRcdFx0XHRcdCAgKCB2X3ByZXZfeCAqIHZfbmV4dF95IC0gdl9wcmV2X3kgKiB2X25leHRfeCApO1xuXG5cdFx0XHRcdC8vIHZlY3RvciBmcm9tIGluUHQgdG8gaW50ZXJzZWN0aW9uIHBvaW50XG5cblx0XHRcdFx0dl90cmFuc194ID0gKCBwdFByZXZTaGlmdF94ICsgdl9wcmV2X3ggKiBzZiAtIGluUHQueCApO1xuXHRcdFx0XHR2X3RyYW5zX3kgPSAoIHB0UHJldlNoaWZ0X3kgKyB2X3ByZXZfeSAqIHNmIC0gaW5QdC55ICk7XG5cblx0XHRcdFx0Ly8gRG9uJ3Qgbm9ybWFsaXplISwgb3RoZXJ3aXNlIHNoYXJwIGNvcm5lcnMgYmVjb21lIHVnbHlcblx0XHRcdFx0Ly8gIGJ1dCBwcmV2ZW50IGNyYXp5IHNwaWtlc1xuXHRcdFx0XHR2YXIgdl90cmFuc19sZW5zcSA9ICggdl90cmFuc194ICogdl90cmFuc194ICsgdl90cmFuc195ICogdl90cmFuc195ICk7XG5cdFx0XHRcdGlmICggdl90cmFuc19sZW5zcSA8PSAyICkge1xuXG5cdFx0XHRcdFx0cmV0dXJuXHRuZXcgVmVjdG9yMiggdl90cmFuc194LCB2X3RyYW5zX3kgKTtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0c2hyaW5rX2J5ID0gTWF0aC5zcXJ0KCB2X3RyYW5zX2xlbnNxIC8gMiApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHQvLyBoYW5kbGUgc3BlY2lhbCBjYXNlIG9mIGNvbGxpbmVhciBlZGdlc1xuXG5cdFx0XHRcdHZhciBkaXJlY3Rpb25fZXEgPSBmYWxzZTtcdFx0Ly8gYXNzdW1lczogb3Bwb3NpdGVcblx0XHRcdFx0aWYgKCB2X3ByZXZfeCA+IE51bWJlci5FUFNJTE9OICkge1xuXG5cdFx0XHRcdFx0aWYgKCB2X25leHRfeCA+IE51bWJlci5FUFNJTE9OICkge1xuXG5cdFx0XHRcdFx0XHRkaXJlY3Rpb25fZXEgPSB0cnVlO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRpZiAoIHZfcHJldl94IDwgLSBOdW1iZXIuRVBTSUxPTiApIHtcblxuXHRcdFx0XHRcdFx0aWYgKCB2X25leHRfeCA8IC0gTnVtYmVyLkVQU0lMT04gKSB7XG5cblx0XHRcdFx0XHRcdFx0ZGlyZWN0aW9uX2VxID0gdHJ1ZTtcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0aWYgKCBNYXRoLnNpZ24oIHZfcHJldl95ICkgPT09IE1hdGguc2lnbiggdl9uZXh0X3kgKSApIHtcblxuXHRcdFx0XHRcdFx0XHRkaXJlY3Rpb25fZXEgPSB0cnVlO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggZGlyZWN0aW9uX2VxICkge1xuXG5cdFx0XHRcdFx0Ly8gY29uc29sZS5sb2coXCJXYXJuaW5nOiBsaW5lcyBhcmUgYSBzdHJhaWdodCBzZXF1ZW5jZVwiKTtcblx0XHRcdFx0XHR2X3RyYW5zX3ggPSAtIHZfcHJldl95O1xuXHRcdFx0XHRcdHZfdHJhbnNfeSA9ICB2X3ByZXZfeDtcblx0XHRcdFx0XHRzaHJpbmtfYnkgPSBNYXRoLnNxcnQoIHZfcHJldl9sZW5zcSApO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHQvLyBjb25zb2xlLmxvZyhcIldhcm5pbmc6IGxpbmVzIGFyZSBhIHN0cmFpZ2h0IHNwaWtlXCIpO1xuXHRcdFx0XHRcdHZfdHJhbnNfeCA9IHZfcHJldl94O1xuXHRcdFx0XHRcdHZfdHJhbnNfeSA9IHZfcHJldl95O1xuXHRcdFx0XHRcdHNocmlua19ieSA9IE1hdGguc3FydCggdl9wcmV2X2xlbnNxIC8gMiApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm5cdG5ldyBWZWN0b3IyKCB2X3RyYW5zX3ggLyBzaHJpbmtfYnksIHZfdHJhbnNfeSAvIHNocmlua19ieSApO1xuXG5cdFx0fVxuXG5cblx0XHR2YXIgY29udG91ck1vdmVtZW50cyA9IFtdO1xuXG5cdFx0Zm9yICggdmFyIGkgPSAwLCBpbCA9IGNvbnRvdXIubGVuZ3RoLCBqID0gaWwgLSAxLCBrID0gaSArIDE7IGkgPCBpbDsgaSArKywgaiArKywgayArKyApIHtcblxuXHRcdFx0aWYgKCBqID09PSBpbCApIGogPSAwO1xuXHRcdFx0aWYgKCBrID09PSBpbCApIGsgPSAwO1xuXG5cdFx0XHQvLyAgKGopLS0tKGkpLS0tKGspXG5cdFx0XHQvLyBjb25zb2xlLmxvZygnaSxqLGsnLCBpLCBqICwgaylcblxuXHRcdFx0Y29udG91ck1vdmVtZW50c1sgaSBdID0gZ2V0QmV2ZWxWZWMoIGNvbnRvdXJbIGkgXSwgY29udG91clsgaiBdLCBjb250b3VyWyBrIF0gKTtcblxuXHRcdH1cblxuXHRcdHZhciBob2xlc01vdmVtZW50cyA9IFtdLCBvbmVIb2xlTW92ZW1lbnRzLCB2ZXJ0aWNlc01vdmVtZW50cyA9IGNvbnRvdXJNb3ZlbWVudHMuY29uY2F0KCk7XG5cblx0XHRmb3IgKCBoID0gMCwgaGwgPSBob2xlcy5sZW5ndGg7IGggPCBobDsgaCArKyApIHtcblxuXHRcdFx0YWhvbGUgPSBob2xlc1sgaCBdO1xuXG5cdFx0XHRvbmVIb2xlTW92ZW1lbnRzID0gW107XG5cblx0XHRcdGZvciAoIGkgPSAwLCBpbCA9IGFob2xlLmxlbmd0aCwgaiA9IGlsIC0gMSwgayA9IGkgKyAxOyBpIDwgaWw7IGkgKyssIGogKyssIGsgKysgKSB7XG5cblx0XHRcdFx0aWYgKCBqID09PSBpbCApIGogPSAwO1xuXHRcdFx0XHRpZiAoIGsgPT09IGlsICkgayA9IDA7XG5cblx0XHRcdFx0Ly8gIChqKS0tLShpKS0tLShrKVxuXHRcdFx0XHRvbmVIb2xlTW92ZW1lbnRzWyBpIF0gPSBnZXRCZXZlbFZlYyggYWhvbGVbIGkgXSwgYWhvbGVbIGogXSwgYWhvbGVbIGsgXSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdGhvbGVzTW92ZW1lbnRzLnB1c2goIG9uZUhvbGVNb3ZlbWVudHMgKTtcblx0XHRcdHZlcnRpY2VzTW92ZW1lbnRzID0gdmVydGljZXNNb3ZlbWVudHMuY29uY2F0KCBvbmVIb2xlTW92ZW1lbnRzICk7XG5cblx0XHR9XG5cblxuXHRcdC8vIExvb3AgYmV2ZWxTZWdtZW50cywgMSBmb3IgdGhlIGZyb250LCAxIGZvciB0aGUgYmFja1xuXG5cdFx0Zm9yICggYiA9IDA7IGIgPCBiZXZlbFNlZ21lbnRzOyBiICsrICkge1xuXG5cdFx0XHQvL2ZvciAoIGIgPSBiZXZlbFNlZ21lbnRzOyBiID4gMDsgYiAtLSApIHtcblxuXHRcdFx0dCA9IGIgLyBiZXZlbFNlZ21lbnRzO1xuXHRcdFx0eiA9IGJldmVsVGhpY2tuZXNzICogTWF0aC5jb3MoIHQgKiBNYXRoLlBJIC8gMiApO1xuXHRcdFx0YnMgPSBiZXZlbFNpemUgKiBNYXRoLnNpbiggdCAqIE1hdGguUEkgLyAyICk7XG5cblx0XHRcdC8vIGNvbnRyYWN0IHNoYXBlXG5cblx0XHRcdGZvciAoIGkgPSAwLCBpbCA9IGNvbnRvdXIubGVuZ3RoOyBpIDwgaWw7IGkgKysgKSB7XG5cblx0XHRcdFx0dmVydCA9IHNjYWxlUHQyKCBjb250b3VyWyBpIF0sIGNvbnRvdXJNb3ZlbWVudHNbIGkgXSwgYnMgKTtcblxuXHRcdFx0XHR2KCB2ZXJ0LngsIHZlcnQueSwgIC0geiApO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIGV4cGFuZCBob2xlc1xuXG5cdFx0XHRmb3IgKCBoID0gMCwgaGwgPSBob2xlcy5sZW5ndGg7IGggPCBobDsgaCArKyApIHtcblxuXHRcdFx0XHRhaG9sZSA9IGhvbGVzWyBoIF07XG5cdFx0XHRcdG9uZUhvbGVNb3ZlbWVudHMgPSBob2xlc01vdmVtZW50c1sgaCBdO1xuXG5cdFx0XHRcdGZvciAoIGkgPSAwLCBpbCA9IGFob2xlLmxlbmd0aDsgaSA8IGlsOyBpICsrICkge1xuXG5cdFx0XHRcdFx0dmVydCA9IHNjYWxlUHQyKCBhaG9sZVsgaSBdLCBvbmVIb2xlTW92ZW1lbnRzWyBpIF0sIGJzICk7XG5cblx0XHRcdFx0XHR2KCB2ZXJ0LngsIHZlcnQueSwgIC0geiApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0YnMgPSBiZXZlbFNpemU7XG5cblx0XHQvLyBCYWNrIGZhY2luZyB2ZXJ0aWNlc1xuXG5cdFx0Zm9yICggaSA9IDA7IGkgPCB2bGVuOyBpICsrICkge1xuXG5cdFx0XHR2ZXJ0ID0gYmV2ZWxFbmFibGVkID8gc2NhbGVQdDIoIHZlcnRpY2VzWyBpIF0sIHZlcnRpY2VzTW92ZW1lbnRzWyBpIF0sIGJzICkgOiB2ZXJ0aWNlc1sgaSBdO1xuXG5cdFx0XHRpZiAoICEgZXh0cnVkZUJ5UGF0aCApIHtcblxuXHRcdFx0XHR2KCB2ZXJ0LngsIHZlcnQueSwgMCApO1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdC8vIHYoIHZlcnQueCwgdmVydC55ICsgZXh0cnVkZVB0c1sgMCBdLnksIGV4dHJ1ZGVQdHNbIDAgXS54ICk7XG5cblx0XHRcdFx0bm9ybWFsLmNvcHkoIHNwbGluZVR1YmUubm9ybWFsc1sgMCBdICkubXVsdGlwbHlTY2FsYXIoIHZlcnQueCApO1xuXHRcdFx0XHRiaW5vcm1hbC5jb3B5KCBzcGxpbmVUdWJlLmJpbm9ybWFsc1sgMCBdICkubXVsdGlwbHlTY2FsYXIoIHZlcnQueSApO1xuXG5cdFx0XHRcdHBvc2l0aW9uMi5jb3B5KCBleHRydWRlUHRzWyAwIF0gKS5hZGQoIG5vcm1hbCApLmFkZCggYmlub3JtYWwgKTtcblxuXHRcdFx0XHR2KCBwb3NpdGlvbjIueCwgcG9zaXRpb24yLnksIHBvc2l0aW9uMi56ICk7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdC8vIEFkZCBzdGVwcGVkIHZlcnRpY2VzLi4uXG5cdFx0Ly8gSW5jbHVkaW5nIGZyb250IGZhY2luZyB2ZXJ0aWNlc1xuXG5cdFx0dmFyIHM7XG5cblx0XHRmb3IgKCBzID0gMTsgcyA8PSBzdGVwczsgcyArKyApIHtcblxuXHRcdFx0Zm9yICggaSA9IDA7IGkgPCB2bGVuOyBpICsrICkge1xuXG5cdFx0XHRcdHZlcnQgPSBiZXZlbEVuYWJsZWQgPyBzY2FsZVB0MiggdmVydGljZXNbIGkgXSwgdmVydGljZXNNb3ZlbWVudHNbIGkgXSwgYnMgKSA6IHZlcnRpY2VzWyBpIF07XG5cblx0XHRcdFx0aWYgKCAhIGV4dHJ1ZGVCeVBhdGggKSB7XG5cblx0XHRcdFx0XHR2KCB2ZXJ0LngsIHZlcnQueSwgYW1vdW50IC8gc3RlcHMgKiBzICk7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdC8vIHYoIHZlcnQueCwgdmVydC55ICsgZXh0cnVkZVB0c1sgcyAtIDEgXS55LCBleHRydWRlUHRzWyBzIC0gMSBdLnggKTtcblxuXHRcdFx0XHRcdG5vcm1hbC5jb3B5KCBzcGxpbmVUdWJlLm5vcm1hbHNbIHMgXSApLm11bHRpcGx5U2NhbGFyKCB2ZXJ0LnggKTtcblx0XHRcdFx0XHRiaW5vcm1hbC5jb3B5KCBzcGxpbmVUdWJlLmJpbm9ybWFsc1sgcyBdICkubXVsdGlwbHlTY2FsYXIoIHZlcnQueSApO1xuXG5cdFx0XHRcdFx0cG9zaXRpb24yLmNvcHkoIGV4dHJ1ZGVQdHNbIHMgXSApLmFkZCggbm9ybWFsICkuYWRkKCBiaW5vcm1hbCApO1xuXG5cdFx0XHRcdFx0diggcG9zaXRpb24yLngsIHBvc2l0aW9uMi55LCBwb3NpdGlvbjIueiApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cblx0XHQvLyBBZGQgYmV2ZWwgc2VnbWVudHMgcGxhbmVzXG5cblx0XHQvL2ZvciAoIGIgPSAxOyBiIDw9IGJldmVsU2VnbWVudHM7IGIgKysgKSB7XG5cdFx0Zm9yICggYiA9IGJldmVsU2VnbWVudHMgLSAxOyBiID49IDA7IGIgLS0gKSB7XG5cblx0XHRcdHQgPSBiIC8gYmV2ZWxTZWdtZW50cztcblx0XHRcdHogPSBiZXZlbFRoaWNrbmVzcyAqIE1hdGguY29zICggdCAqIE1hdGguUEkgLyAyICk7XG5cdFx0XHRicyA9IGJldmVsU2l6ZSAqIE1hdGguc2luKCB0ICogTWF0aC5QSSAvIDIgKTtcblxuXHRcdFx0Ly8gY29udHJhY3Qgc2hhcGVcblxuXHRcdFx0Zm9yICggaSA9IDAsIGlsID0gY29udG91ci5sZW5ndGg7IGkgPCBpbDsgaSArKyApIHtcblxuXHRcdFx0XHR2ZXJ0ID0gc2NhbGVQdDIoIGNvbnRvdXJbIGkgXSwgY29udG91ck1vdmVtZW50c1sgaSBdLCBicyApO1xuXHRcdFx0XHR2KCB2ZXJ0LngsIHZlcnQueSwgIGFtb3VudCArIHogKTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyBleHBhbmQgaG9sZXNcblxuXHRcdFx0Zm9yICggaCA9IDAsIGhsID0gaG9sZXMubGVuZ3RoOyBoIDwgaGw7IGggKysgKSB7XG5cblx0XHRcdFx0YWhvbGUgPSBob2xlc1sgaCBdO1xuXHRcdFx0XHRvbmVIb2xlTW92ZW1lbnRzID0gaG9sZXNNb3ZlbWVudHNbIGggXTtcblxuXHRcdFx0XHRmb3IgKCBpID0gMCwgaWwgPSBhaG9sZS5sZW5ndGg7IGkgPCBpbDsgaSArKyApIHtcblxuXHRcdFx0XHRcdHZlcnQgPSBzY2FsZVB0MiggYWhvbGVbIGkgXSwgb25lSG9sZU1vdmVtZW50c1sgaSBdLCBicyApO1xuXG5cdFx0XHRcdFx0aWYgKCAhIGV4dHJ1ZGVCeVBhdGggKSB7XG5cblx0XHRcdFx0XHRcdHYoIHZlcnQueCwgdmVydC55LCAgYW1vdW50ICsgeiApO1xuXG5cdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0diggdmVydC54LCB2ZXJ0LnkgKyBleHRydWRlUHRzWyBzdGVwcyAtIDEgXS55LCBleHRydWRlUHRzWyBzdGVwcyAtIDEgXS54ICsgeiApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0LyogRmFjZXMgKi9cblxuXHRcdC8vIFRvcCBhbmQgYm90dG9tIGZhY2VzXG5cblx0XHRidWlsZExpZEZhY2VzKCk7XG5cblx0XHQvLyBTaWRlcyBmYWNlc1xuXG5cdFx0YnVpbGRTaWRlRmFjZXMoKTtcblxuXG5cdFx0Ly8vLy8gIEludGVybmFsIGZ1bmN0aW9uc1xuXG5cdFx0ZnVuY3Rpb24gYnVpbGRMaWRGYWNlcygpIHtcblxuXHRcdFx0aWYgKCBiZXZlbEVuYWJsZWQgKSB7XG5cblx0XHRcdFx0dmFyIGxheWVyID0gMDsgLy8gc3RlcHMgKyAxXG5cdFx0XHRcdHZhciBvZmZzZXQgPSB2bGVuICogbGF5ZXI7XG5cblx0XHRcdFx0Ly8gQm90dG9tIGZhY2VzXG5cblx0XHRcdFx0Zm9yICggaSA9IDA7IGkgPCBmbGVuOyBpICsrICkge1xuXG5cdFx0XHRcdFx0ZmFjZSA9IGZhY2VzWyBpIF07XG5cdFx0XHRcdFx0ZjMoIGZhY2VbIDIgXSArIG9mZnNldCwgZmFjZVsgMSBdICsgb2Zmc2V0LCBmYWNlWyAwIF0gKyBvZmZzZXQgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0bGF5ZXIgPSBzdGVwcyArIGJldmVsU2VnbWVudHMgKiAyO1xuXHRcdFx0XHRvZmZzZXQgPSB2bGVuICogbGF5ZXI7XG5cblx0XHRcdFx0Ly8gVG9wIGZhY2VzXG5cblx0XHRcdFx0Zm9yICggaSA9IDA7IGkgPCBmbGVuOyBpICsrICkge1xuXG5cdFx0XHRcdFx0ZmFjZSA9IGZhY2VzWyBpIF07XG5cdFx0XHRcdFx0ZjMoIGZhY2VbIDAgXSArIG9mZnNldCwgZmFjZVsgMSBdICsgb2Zmc2V0LCBmYWNlWyAyIF0gKyBvZmZzZXQgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0Ly8gQm90dG9tIGZhY2VzXG5cblx0XHRcdFx0Zm9yICggaSA9IDA7IGkgPCBmbGVuOyBpICsrICkge1xuXG5cdFx0XHRcdFx0ZmFjZSA9IGZhY2VzWyBpIF07XG5cdFx0XHRcdFx0ZjMoIGZhY2VbIDIgXSwgZmFjZVsgMSBdLCBmYWNlWyAwIF0gKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gVG9wIGZhY2VzXG5cblx0XHRcdFx0Zm9yICggaSA9IDA7IGkgPCBmbGVuOyBpICsrICkge1xuXG5cdFx0XHRcdFx0ZmFjZSA9IGZhY2VzWyBpIF07XG5cdFx0XHRcdFx0ZjMoIGZhY2VbIDAgXSArIHZsZW4gKiBzdGVwcywgZmFjZVsgMSBdICsgdmxlbiAqIHN0ZXBzLCBmYWNlWyAyIF0gKyB2bGVuICogc3RlcHMgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdC8vIENyZWF0ZSBmYWNlcyBmb3IgdGhlIHotc2lkZXMgb2YgdGhlIHNoYXBlXG5cblx0XHRmdW5jdGlvbiBidWlsZFNpZGVGYWNlcygpIHtcblxuXHRcdFx0dmFyIGxheWVyb2Zmc2V0ID0gMDtcblx0XHRcdHNpZGV3YWxscyggY29udG91ciwgbGF5ZXJvZmZzZXQgKTtcblx0XHRcdGxheWVyb2Zmc2V0ICs9IGNvbnRvdXIubGVuZ3RoO1xuXG5cdFx0XHRmb3IgKCBoID0gMCwgaGwgPSBob2xlcy5sZW5ndGg7IGggPCBobDsgaCArKyApIHtcblxuXHRcdFx0XHRhaG9sZSA9IGhvbGVzWyBoIF07XG5cdFx0XHRcdHNpZGV3YWxscyggYWhvbGUsIGxheWVyb2Zmc2V0ICk7XG5cblx0XHRcdFx0Ly8sIHRydWVcblx0XHRcdFx0bGF5ZXJvZmZzZXQgKz0gYWhvbGUubGVuZ3RoO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBzaWRld2FsbHMoIGNvbnRvdXIsIGxheWVyb2Zmc2V0ICkge1xuXG5cdFx0XHR2YXIgaiwgaztcblx0XHRcdGkgPSBjb250b3VyLmxlbmd0aDtcblxuXHRcdFx0d2hpbGUgKCAtLSBpID49IDAgKSB7XG5cblx0XHRcdFx0aiA9IGk7XG5cdFx0XHRcdGsgPSBpIC0gMTtcblx0XHRcdFx0aWYgKCBrIDwgMCApIGsgPSBjb250b3VyLmxlbmd0aCAtIDE7XG5cblx0XHRcdFx0Ly9jb25zb2xlLmxvZygnYicsIGksaiwgaS0xLCBrLHZlcnRpY2VzLmxlbmd0aCk7XG5cblx0XHRcdFx0dmFyIHMgPSAwLCBzbCA9IHN0ZXBzICArIGJldmVsU2VnbWVudHMgKiAyO1xuXG5cdFx0XHRcdGZvciAoIHMgPSAwOyBzIDwgc2w7IHMgKysgKSB7XG5cblx0XHRcdFx0XHR2YXIgc2xlbjEgPSB2bGVuICogcztcblx0XHRcdFx0XHR2YXIgc2xlbjIgPSB2bGVuICogKCBzICsgMSApO1xuXG5cdFx0XHRcdFx0dmFyIGEgPSBsYXllcm9mZnNldCArIGogKyBzbGVuMSxcblx0XHRcdFx0XHRcdGIgPSBsYXllcm9mZnNldCArIGsgKyBzbGVuMSxcblx0XHRcdFx0XHRcdGMgPSBsYXllcm9mZnNldCArIGsgKyBzbGVuMixcblx0XHRcdFx0XHRcdGQgPSBsYXllcm9mZnNldCArIGogKyBzbGVuMjtcblxuXHRcdFx0XHRcdGY0KCBhLCBiLCBjLCBkLCBjb250b3VyLCBzLCBzbCwgaiwgayApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cblx0XHRmdW5jdGlvbiB2KCB4LCB5LCB6ICkge1xuXG5cdFx0XHRzY29wZS52ZXJ0aWNlcy5wdXNoKCBuZXcgVmVjdG9yMyggeCwgeSwgeiApICk7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBmMyggYSwgYiwgYyApIHtcblxuXHRcdFx0YSArPSBzaGFwZXNPZmZzZXQ7XG5cdFx0XHRiICs9IHNoYXBlc09mZnNldDtcblx0XHRcdGMgKz0gc2hhcGVzT2Zmc2V0O1xuXG5cdFx0XHRzY29wZS5mYWNlcy5wdXNoKCBuZXcgRmFjZTMoIGEsIGIsIGMsIG51bGwsIG51bGwsIDAgKSApO1xuXG5cdFx0XHR2YXIgdXZzID0gdXZnZW4uZ2VuZXJhdGVUb3BVViggc2NvcGUsIGEsIGIsIGMgKTtcblxuXHRcdFx0c2NvcGUuZmFjZVZlcnRleFV2c1sgMCBdLnB1c2goIHV2cyApO1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gZjQoIGEsIGIsIGMsIGQsIHdhbGxDb250b3VyLCBzdGVwSW5kZXgsIHN0ZXBzTGVuZ3RoLCBjb250b3VySW5kZXgxLCBjb250b3VySW5kZXgyICkge1xuXG5cdFx0XHRhICs9IHNoYXBlc09mZnNldDtcblx0XHRcdGIgKz0gc2hhcGVzT2Zmc2V0O1xuXHRcdFx0YyArPSBzaGFwZXNPZmZzZXQ7XG5cdFx0XHRkICs9IHNoYXBlc09mZnNldDtcblxuXHRcdFx0c2NvcGUuZmFjZXMucHVzaCggbmV3IEZhY2UzKCBhLCBiLCBkLCBudWxsLCBudWxsLCAxICkgKTtcblx0XHRcdHNjb3BlLmZhY2VzLnB1c2goIG5ldyBGYWNlMyggYiwgYywgZCwgbnVsbCwgbnVsbCwgMSApICk7XG5cblx0XHRcdHZhciB1dnMgPSB1dmdlbi5nZW5lcmF0ZVNpZGVXYWxsVVYoIHNjb3BlLCBhLCBiLCBjLCBkICk7XG5cblx0XHRcdHNjb3BlLmZhY2VWZXJ0ZXhVdnNbIDAgXS5wdXNoKCBbIHV2c1sgMCBdLCB1dnNbIDEgXSwgdXZzWyAzIF0gXSApO1xuXHRcdFx0c2NvcGUuZmFjZVZlcnRleFV2c1sgMCBdLnB1c2goIFsgdXZzWyAxIF0sIHV2c1sgMiBdLCB1dnNbIDMgXSBdICk7XG5cblx0XHR9XG5cblx0fTtcblxuXHRFeHRydWRlR2VvbWV0cnkuV29ybGRVVkdlbmVyYXRvciA9IHtcblxuXHRcdGdlbmVyYXRlVG9wVVY6IGZ1bmN0aW9uICggZ2VvbWV0cnksIGluZGV4QSwgaW5kZXhCLCBpbmRleEMgKSB7XG5cblx0XHRcdHZhciB2ZXJ0aWNlcyA9IGdlb21ldHJ5LnZlcnRpY2VzO1xuXG5cdFx0XHR2YXIgYSA9IHZlcnRpY2VzWyBpbmRleEEgXTtcblx0XHRcdHZhciBiID0gdmVydGljZXNbIGluZGV4QiBdO1xuXHRcdFx0dmFyIGMgPSB2ZXJ0aWNlc1sgaW5kZXhDIF07XG5cblx0XHRcdHJldHVybiBbXG5cdFx0XHRcdG5ldyBWZWN0b3IyKCBhLngsIGEueSApLFxuXHRcdFx0XHRuZXcgVmVjdG9yMiggYi54LCBiLnkgKSxcblx0XHRcdFx0bmV3IFZlY3RvcjIoIGMueCwgYy55IClcblx0XHRcdF07XG5cblx0XHR9LFxuXG5cdFx0Z2VuZXJhdGVTaWRlV2FsbFVWOiBmdW5jdGlvbiAoIGdlb21ldHJ5LCBpbmRleEEsIGluZGV4QiwgaW5kZXhDLCBpbmRleEQgKSB7XG5cblx0XHRcdHZhciB2ZXJ0aWNlcyA9IGdlb21ldHJ5LnZlcnRpY2VzO1xuXG5cdFx0XHR2YXIgYSA9IHZlcnRpY2VzWyBpbmRleEEgXTtcblx0XHRcdHZhciBiID0gdmVydGljZXNbIGluZGV4QiBdO1xuXHRcdFx0dmFyIGMgPSB2ZXJ0aWNlc1sgaW5kZXhDIF07XG5cdFx0XHR2YXIgZCA9IHZlcnRpY2VzWyBpbmRleEQgXTtcblxuXHRcdFx0aWYgKCBNYXRoLmFicyggYS55IC0gYi55ICkgPCAwLjAxICkge1xuXG5cdFx0XHRcdHJldHVybiBbXG5cdFx0XHRcdFx0bmV3IFZlY3RvcjIoIGEueCwgMSAtIGEueiApLFxuXHRcdFx0XHRcdG5ldyBWZWN0b3IyKCBiLngsIDEgLSBiLnogKSxcblx0XHRcdFx0XHRuZXcgVmVjdG9yMiggYy54LCAxIC0gYy56ICksXG5cdFx0XHRcdFx0bmV3IFZlY3RvcjIoIGQueCwgMSAtIGQueiApXG5cdFx0XHRcdF07XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0cmV0dXJuIFtcblx0XHRcdFx0XHRuZXcgVmVjdG9yMiggYS55LCAxIC0gYS56ICksXG5cdFx0XHRcdFx0bmV3IFZlY3RvcjIoIGIueSwgMSAtIGIueiApLFxuXHRcdFx0XHRcdG5ldyBWZWN0b3IyKCBjLnksIDEgLSBjLnogKSxcblx0XHRcdFx0XHRuZXcgVmVjdG9yMiggZC55LCAxIC0gZC56IClcblx0XHRcdFx0XTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIHp6ODUgLyBodHRwOi8vd3d3LmxhYjRnYW1lcy5uZXQveno4NS9ibG9nXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqXG5cdCAqIFRleHQgPSAzRCBUZXh0XG5cdCAqXG5cdCAqIHBhcmFtZXRlcnMgPSB7XG5cdCAqICBmb250OiA8VEhSRUUuRm9udD4sIC8vIGZvbnRcblx0ICpcblx0ICogIHNpemU6IDxmbG9hdD4sIC8vIHNpemUgb2YgdGhlIHRleHRcblx0ICogIGhlaWdodDogPGZsb2F0PiwgLy8gdGhpY2tuZXNzIHRvIGV4dHJ1ZGUgdGV4dFxuXHQgKiAgY3VydmVTZWdtZW50czogPGludD4sIC8vIG51bWJlciBvZiBwb2ludHMgb24gdGhlIGN1cnZlc1xuXHQgKlxuXHQgKiAgYmV2ZWxFbmFibGVkOiA8Ym9vbD4sIC8vIHR1cm4gb24gYmV2ZWxcblx0ICogIGJldmVsVGhpY2tuZXNzOiA8ZmxvYXQ+LCAvLyBob3cgZGVlcCBpbnRvIHRleHQgYmV2ZWwgZ29lc1xuXHQgKiAgYmV2ZWxTaXplOiA8ZmxvYXQ+IC8vIGhvdyBmYXIgZnJvbSB0ZXh0IG91dGxpbmUgaXMgYmV2ZWxcblx0ICogfVxuXHQgKi9cblxuXHRmdW5jdGlvbiBUZXh0R2VvbWV0cnkoIHRleHQsIHBhcmFtZXRlcnMgKSB7XG5cblx0XHRwYXJhbWV0ZXJzID0gcGFyYW1ldGVycyB8fCB7fTtcblxuXHRcdHZhciBmb250ID0gcGFyYW1ldGVycy5mb250O1xuXG5cdFx0aWYgKCAoZm9udCAmJiBmb250LmlzRm9udCkgPT09IGZhbHNlICkge1xuXG5cdFx0XHRjb25zb2xlLmVycm9yKCAnVEhSRUUuVGV4dEdlb21ldHJ5OiBmb250IHBhcmFtZXRlciBpcyBub3QgYW4gaW5zdGFuY2Ugb2YgVEhSRUUuRm9udC4nICk7XG5cdFx0XHRyZXR1cm4gbmV3IEdlb21ldHJ5KCk7XG5cblx0XHR9XG5cblx0XHR2YXIgc2hhcGVzID0gZm9udC5nZW5lcmF0ZVNoYXBlcyggdGV4dCwgcGFyYW1ldGVycy5zaXplLCBwYXJhbWV0ZXJzLmN1cnZlU2VnbWVudHMgKTtcblxuXHRcdC8vIHRyYW5zbGF0ZSBwYXJhbWV0ZXJzIHRvIEV4dHJ1ZGVHZW9tZXRyeSBBUElcblxuXHRcdHBhcmFtZXRlcnMuYW1vdW50ID0gcGFyYW1ldGVycy5oZWlnaHQgIT09IHVuZGVmaW5lZCA/IHBhcmFtZXRlcnMuaGVpZ2h0IDogNTA7XG5cblx0XHQvLyBkZWZhdWx0c1xuXG5cdFx0aWYgKCBwYXJhbWV0ZXJzLmJldmVsVGhpY2tuZXNzID09PSB1bmRlZmluZWQgKSBwYXJhbWV0ZXJzLmJldmVsVGhpY2tuZXNzID0gMTA7XG5cdFx0aWYgKCBwYXJhbWV0ZXJzLmJldmVsU2l6ZSA9PT0gdW5kZWZpbmVkICkgcGFyYW1ldGVycy5iZXZlbFNpemUgPSA4O1xuXHRcdGlmICggcGFyYW1ldGVycy5iZXZlbEVuYWJsZWQgPT09IHVuZGVmaW5lZCApIHBhcmFtZXRlcnMuYmV2ZWxFbmFibGVkID0gZmFsc2U7XG5cblx0XHRFeHRydWRlR2VvbWV0cnkuY2FsbCggdGhpcywgc2hhcGVzLCBwYXJhbWV0ZXJzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnVGV4dEdlb21ldHJ5JztcblxuXHR9XG5cblx0VGV4dEdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEV4dHJ1ZGVHZW9tZXRyeS5wcm90b3R5cGUgKTtcblx0VGV4dEdlb21ldHJ5LnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFRleHRHZW9tZXRyeTtcblxuXHQvKipcblx0ICogQGF1dGhvciBiZW5hYWRhbXMgLyBodHRwczovL3R3aXR0ZXIuY29tL2Jlbl9hX2FkYW1zXG5cdCAqIGJhc2VkIG9uIFRIUkVFLlNwaGVyZUdlb21ldHJ5XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFNwaGVyZUJ1ZmZlckdlb21ldHJ5KCByYWRpdXMsIHdpZHRoU2VnbWVudHMsIGhlaWdodFNlZ21lbnRzLCBwaGlTdGFydCwgcGhpTGVuZ3RoLCB0aGV0YVN0YXJ0LCB0aGV0YUxlbmd0aCApIHtcblxuXHRcdEJ1ZmZlckdlb21ldHJ5LmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdTcGhlcmVCdWZmZXJHZW9tZXRyeSc7XG5cblx0XHR0aGlzLnBhcmFtZXRlcnMgPSB7XG5cdFx0XHRyYWRpdXM6IHJhZGl1cyxcblx0XHRcdHdpZHRoU2VnbWVudHM6IHdpZHRoU2VnbWVudHMsXG5cdFx0XHRoZWlnaHRTZWdtZW50czogaGVpZ2h0U2VnbWVudHMsXG5cdFx0XHRwaGlTdGFydDogcGhpU3RhcnQsXG5cdFx0XHRwaGlMZW5ndGg6IHBoaUxlbmd0aCxcblx0XHRcdHRoZXRhU3RhcnQ6IHRoZXRhU3RhcnQsXG5cdFx0XHR0aGV0YUxlbmd0aDogdGhldGFMZW5ndGhcblx0XHR9O1xuXG5cdFx0cmFkaXVzID0gcmFkaXVzIHx8IDUwO1xuXG5cdFx0d2lkdGhTZWdtZW50cyA9IE1hdGgubWF4KCAzLCBNYXRoLmZsb29yKCB3aWR0aFNlZ21lbnRzICkgfHwgOCApO1xuXHRcdGhlaWdodFNlZ21lbnRzID0gTWF0aC5tYXgoIDIsIE1hdGguZmxvb3IoIGhlaWdodFNlZ21lbnRzICkgfHwgNiApO1xuXG5cdFx0cGhpU3RhcnQgPSBwaGlTdGFydCAhPT0gdW5kZWZpbmVkID8gcGhpU3RhcnQgOiAwO1xuXHRcdHBoaUxlbmd0aCA9IHBoaUxlbmd0aCAhPT0gdW5kZWZpbmVkID8gcGhpTGVuZ3RoIDogTWF0aC5QSSAqIDI7XG5cblx0XHR0aGV0YVN0YXJ0ID0gdGhldGFTdGFydCAhPT0gdW5kZWZpbmVkID8gdGhldGFTdGFydCA6IDA7XG5cdFx0dGhldGFMZW5ndGggPSB0aGV0YUxlbmd0aCAhPT0gdW5kZWZpbmVkID8gdGhldGFMZW5ndGggOiBNYXRoLlBJO1xuXG5cdFx0dmFyIHRoZXRhRW5kID0gdGhldGFTdGFydCArIHRoZXRhTGVuZ3RoO1xuXG5cdFx0dmFyIHZlcnRleENvdW50ID0gKCAoIHdpZHRoU2VnbWVudHMgKyAxICkgKiAoIGhlaWdodFNlZ21lbnRzICsgMSApICk7XG5cblx0XHR2YXIgcG9zaXRpb25zID0gbmV3IEJ1ZmZlckF0dHJpYnV0ZSggbmV3IEZsb2F0MzJBcnJheSggdmVydGV4Q291bnQgKiAzICksIDMgKTtcblx0XHR2YXIgbm9ybWFscyA9IG5ldyBCdWZmZXJBdHRyaWJ1dGUoIG5ldyBGbG9hdDMyQXJyYXkoIHZlcnRleENvdW50ICogMyApLCAzICk7XG5cdFx0dmFyIHV2cyA9IG5ldyBCdWZmZXJBdHRyaWJ1dGUoIG5ldyBGbG9hdDMyQXJyYXkoIHZlcnRleENvdW50ICogMiApLCAyICk7XG5cblx0XHR2YXIgaW5kZXggPSAwLCB2ZXJ0aWNlcyA9IFtdLCBub3JtYWwgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0Zm9yICggdmFyIHkgPSAwOyB5IDw9IGhlaWdodFNlZ21lbnRzOyB5ICsrICkge1xuXG5cdFx0XHR2YXIgdmVydGljZXNSb3cgPSBbXTtcblxuXHRcdFx0dmFyIHYgPSB5IC8gaGVpZ2h0U2VnbWVudHM7XG5cblx0XHRcdGZvciAoIHZhciB4ID0gMDsgeCA8PSB3aWR0aFNlZ21lbnRzOyB4ICsrICkge1xuXG5cdFx0XHRcdHZhciB1ID0geCAvIHdpZHRoU2VnbWVudHM7XG5cblx0XHRcdFx0dmFyIHB4ID0gLSByYWRpdXMgKiBNYXRoLmNvcyggcGhpU3RhcnQgKyB1ICogcGhpTGVuZ3RoICkgKiBNYXRoLnNpbiggdGhldGFTdGFydCArIHYgKiB0aGV0YUxlbmd0aCApO1xuXHRcdFx0XHR2YXIgcHkgPSByYWRpdXMgKiBNYXRoLmNvcyggdGhldGFTdGFydCArIHYgKiB0aGV0YUxlbmd0aCApO1xuXHRcdFx0XHR2YXIgcHogPSByYWRpdXMgKiBNYXRoLnNpbiggcGhpU3RhcnQgKyB1ICogcGhpTGVuZ3RoICkgKiBNYXRoLnNpbiggdGhldGFTdGFydCArIHYgKiB0aGV0YUxlbmd0aCApO1xuXG5cdFx0XHRcdG5vcm1hbC5zZXQoIHB4LCBweSwgcHogKS5ub3JtYWxpemUoKTtcblxuXHRcdFx0XHRwb3NpdGlvbnMuc2V0WFlaKCBpbmRleCwgcHgsIHB5LCBweiApO1xuXHRcdFx0XHRub3JtYWxzLnNldFhZWiggaW5kZXgsIG5vcm1hbC54LCBub3JtYWwueSwgbm9ybWFsLnogKTtcblx0XHRcdFx0dXZzLnNldFhZKCBpbmRleCwgdSwgMSAtIHYgKTtcblxuXHRcdFx0XHR2ZXJ0aWNlc1Jvdy5wdXNoKCBpbmRleCApO1xuXG5cdFx0XHRcdGluZGV4ICsrO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZlcnRpY2VzLnB1c2goIHZlcnRpY2VzUm93ICk7XG5cblx0XHR9XG5cblx0XHR2YXIgaW5kaWNlcyA9IFtdO1xuXG5cdFx0Zm9yICggdmFyIHkgPSAwOyB5IDwgaGVpZ2h0U2VnbWVudHM7IHkgKysgKSB7XG5cblx0XHRcdGZvciAoIHZhciB4ID0gMDsgeCA8IHdpZHRoU2VnbWVudHM7IHggKysgKSB7XG5cblx0XHRcdFx0dmFyIHYxID0gdmVydGljZXNbIHkgXVsgeCArIDEgXTtcblx0XHRcdFx0dmFyIHYyID0gdmVydGljZXNbIHkgXVsgeCBdO1xuXHRcdFx0XHR2YXIgdjMgPSB2ZXJ0aWNlc1sgeSArIDEgXVsgeCBdO1xuXHRcdFx0XHR2YXIgdjQgPSB2ZXJ0aWNlc1sgeSArIDEgXVsgeCArIDEgXTtcblxuXHRcdFx0XHRpZiAoIHkgIT09IDAgfHwgdGhldGFTdGFydCA+IDAgKSBpbmRpY2VzLnB1c2goIHYxLCB2MiwgdjQgKTtcblx0XHRcdFx0aWYgKCB5ICE9PSBoZWlnaHRTZWdtZW50cyAtIDEgfHwgdGhldGFFbmQgPCBNYXRoLlBJICkgaW5kaWNlcy5wdXNoKCB2MiwgdjMsIHY0ICk7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdHRoaXMuc2V0SW5kZXgoIG5ldyAoIHBvc2l0aW9ucy5jb3VudCA+IDY1NTM1ID8gVWludDMyQnVmZmVyQXR0cmlidXRlIDogVWludDE2QnVmZmVyQXR0cmlidXRlICkoIGluZGljZXMsIDEgKSApO1xuXHRcdHRoaXMuYWRkQXR0cmlidXRlKCAncG9zaXRpb24nLCBwb3NpdGlvbnMgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ25vcm1hbCcsIG5vcm1hbHMgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ3V2JywgdXZzICk7XG5cblx0XHR0aGlzLmJvdW5kaW5nU3BoZXJlID0gbmV3IFNwaGVyZSggbmV3IFZlY3RvcjMoKSwgcmFkaXVzICk7XG5cblx0fVxuXG5cdFNwaGVyZUJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRTcGhlcmVCdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBTcGhlcmVCdWZmZXJHZW9tZXRyeTtcblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gU3BoZXJlR2VvbWV0cnkoIHJhZGl1cywgd2lkdGhTZWdtZW50cywgaGVpZ2h0U2VnbWVudHMsIHBoaVN0YXJ0LCBwaGlMZW5ndGgsIHRoZXRhU3RhcnQsIHRoZXRhTGVuZ3RoICkge1xuXG5cdFx0R2VvbWV0cnkuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ1NwaGVyZUdlb21ldHJ5JztcblxuXHRcdHRoaXMucGFyYW1ldGVycyA9IHtcblx0XHRcdHJhZGl1czogcmFkaXVzLFxuXHRcdFx0d2lkdGhTZWdtZW50czogd2lkdGhTZWdtZW50cyxcblx0XHRcdGhlaWdodFNlZ21lbnRzOiBoZWlnaHRTZWdtZW50cyxcblx0XHRcdHBoaVN0YXJ0OiBwaGlTdGFydCxcblx0XHRcdHBoaUxlbmd0aDogcGhpTGVuZ3RoLFxuXHRcdFx0dGhldGFTdGFydDogdGhldGFTdGFydCxcblx0XHRcdHRoZXRhTGVuZ3RoOiB0aGV0YUxlbmd0aFxuXHRcdH07XG5cblx0XHR0aGlzLmZyb21CdWZmZXJHZW9tZXRyeSggbmV3IFNwaGVyZUJ1ZmZlckdlb21ldHJ5KCByYWRpdXMsIHdpZHRoU2VnbWVudHMsIGhlaWdodFNlZ21lbnRzLCBwaGlTdGFydCwgcGhpTGVuZ3RoLCB0aGV0YVN0YXJ0LCB0aGV0YUxlbmd0aCApICk7XG5cblx0fVxuXG5cdFNwaGVyZUdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRTcGhlcmVHZW9tZXRyeS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBTcGhlcmVHZW9tZXRyeTtcblxuXHQvKipcblx0ICogQGF1dGhvciBNdWdlbjg3IC8gaHR0cHM6Ly9naXRodWIuY29tL011Z2VuODdcblx0ICovXG5cblx0ZnVuY3Rpb24gUmluZ0J1ZmZlckdlb21ldHJ5KCBpbm5lclJhZGl1cywgb3V0ZXJSYWRpdXMsIHRoZXRhU2VnbWVudHMsIHBoaVNlZ21lbnRzLCB0aGV0YVN0YXJ0LCB0aGV0YUxlbmd0aCApIHtcblxuXHRcdEJ1ZmZlckdlb21ldHJ5LmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdSaW5nQnVmZmVyR2VvbWV0cnknO1xuXG5cdFx0dGhpcy5wYXJhbWV0ZXJzID0ge1xuXHRcdFx0aW5uZXJSYWRpdXM6IGlubmVyUmFkaXVzLFxuXHRcdFx0b3V0ZXJSYWRpdXM6IG91dGVyUmFkaXVzLFxuXHRcdFx0dGhldGFTZWdtZW50czogdGhldGFTZWdtZW50cyxcblx0XHRcdHBoaVNlZ21lbnRzOiBwaGlTZWdtZW50cyxcblx0XHRcdHRoZXRhU3RhcnQ6IHRoZXRhU3RhcnQsXG5cdFx0XHR0aGV0YUxlbmd0aDogdGhldGFMZW5ndGhcblx0XHR9O1xuXG5cdFx0aW5uZXJSYWRpdXMgPSBpbm5lclJhZGl1cyB8fCAyMDtcblx0XHRvdXRlclJhZGl1cyA9IG91dGVyUmFkaXVzIHx8IDUwO1xuXG5cdFx0dGhldGFTdGFydCA9IHRoZXRhU3RhcnQgIT09IHVuZGVmaW5lZCA/IHRoZXRhU3RhcnQgOiAwO1xuXHRcdHRoZXRhTGVuZ3RoID0gdGhldGFMZW5ndGggIT09IHVuZGVmaW5lZCA/IHRoZXRhTGVuZ3RoIDogTWF0aC5QSSAqIDI7XG5cblx0XHR0aGV0YVNlZ21lbnRzID0gdGhldGFTZWdtZW50cyAhPT0gdW5kZWZpbmVkID8gTWF0aC5tYXgoIDMsIHRoZXRhU2VnbWVudHMgKSA6IDg7XG5cdFx0cGhpU2VnbWVudHMgPSBwaGlTZWdtZW50cyAhPT0gdW5kZWZpbmVkID8gTWF0aC5tYXgoIDEsIHBoaVNlZ21lbnRzICkgOiAxO1xuXG5cdFx0Ly8gdGhlc2UgYXJlIHVzZWQgdG8gY2FsY3VsYXRlIGJ1ZmZlciBsZW5ndGhcblx0XHR2YXIgdmVydGV4Q291bnQgPSAoIHRoZXRhU2VnbWVudHMgKyAxICkgKiAoIHBoaVNlZ21lbnRzICsgMSApO1xuXHRcdHZhciBpbmRleENvdW50ID0gdGhldGFTZWdtZW50cyAqIHBoaVNlZ21lbnRzICogMiAqIDM7XG5cblx0XHQvLyBidWZmZXJzXG5cdFx0dmFyIGluZGljZXMgPSBuZXcgQnVmZmVyQXR0cmlidXRlKCBuZXcgKCBpbmRleENvdW50ID4gNjU1MzUgPyBVaW50MzJBcnJheSA6IFVpbnQxNkFycmF5ICkoIGluZGV4Q291bnQgKSAsIDEgKTtcblx0XHR2YXIgdmVydGljZXMgPSBuZXcgQnVmZmVyQXR0cmlidXRlKCBuZXcgRmxvYXQzMkFycmF5KCB2ZXJ0ZXhDb3VudCAqIDMgKSwgMyApO1xuXHRcdHZhciBub3JtYWxzID0gbmV3IEJ1ZmZlckF0dHJpYnV0ZSggbmV3IEZsb2F0MzJBcnJheSggdmVydGV4Q291bnQgKiAzICksIDMgKTtcblx0XHR2YXIgdXZzID0gbmV3IEJ1ZmZlckF0dHJpYnV0ZSggbmV3IEZsb2F0MzJBcnJheSggdmVydGV4Q291bnQgKiAyICksIDIgKTtcblxuXHRcdC8vIHNvbWUgaGVscGVyIHZhcmlhYmxlc1xuXHRcdHZhciBpbmRleCA9IDAsIGluZGV4T2Zmc2V0ID0gMCwgc2VnbWVudDtcblx0XHR2YXIgcmFkaXVzID0gaW5uZXJSYWRpdXM7XG5cdFx0dmFyIHJhZGl1c1N0ZXAgPSAoICggb3V0ZXJSYWRpdXMgLSBpbm5lclJhZGl1cyApIC8gcGhpU2VnbWVudHMgKTtcblx0XHR2YXIgdmVydGV4ID0gbmV3IFZlY3RvcjMoKTtcblx0XHR2YXIgdXYgPSBuZXcgVmVjdG9yMigpO1xuXHRcdHZhciBqLCBpO1xuXG5cdFx0Ly8gZ2VuZXJhdGUgdmVydGljZXMsIG5vcm1hbHMgYW5kIHV2c1xuXG5cdFx0Ly8gdmFsdWVzIGFyZSBnZW5lcmF0ZSBmcm9tIHRoZSBpbnNpZGUgb2YgdGhlIHJpbmcgdG8gdGhlIG91dHNpZGVcblxuXHRcdGZvciAoIGogPSAwOyBqIDw9IHBoaVNlZ21lbnRzOyBqICsrICkge1xuXG5cdFx0XHRmb3IgKCBpID0gMDsgaSA8PSB0aGV0YVNlZ21lbnRzOyBpICsrICkge1xuXG5cdFx0XHRcdHNlZ21lbnQgPSB0aGV0YVN0YXJ0ICsgaSAvIHRoZXRhU2VnbWVudHMgKiB0aGV0YUxlbmd0aDtcblxuXHRcdFx0XHQvLyB2ZXJ0ZXhcblx0XHRcdFx0dmVydGV4LnggPSByYWRpdXMgKiBNYXRoLmNvcyggc2VnbWVudCApO1xuXHRcdFx0XHR2ZXJ0ZXgueSA9IHJhZGl1cyAqIE1hdGguc2luKCBzZWdtZW50ICk7XG5cdFx0XHRcdHZlcnRpY2VzLnNldFhZWiggaW5kZXgsIHZlcnRleC54LCB2ZXJ0ZXgueSwgdmVydGV4LnogKTtcblxuXHRcdFx0XHQvLyBub3JtYWxcblx0XHRcdFx0bm9ybWFscy5zZXRYWVooIGluZGV4LCAwLCAwLCAxICk7XG5cblx0XHRcdFx0Ly8gdXZcblx0XHRcdFx0dXYueCA9ICggdmVydGV4LnggLyBvdXRlclJhZGl1cyArIDEgKSAvIDI7XG5cdFx0XHRcdHV2LnkgPSAoIHZlcnRleC55IC8gb3V0ZXJSYWRpdXMgKyAxICkgLyAyO1xuXHRcdFx0XHR1dnMuc2V0WFkoIGluZGV4LCB1di54LCB1di55ICk7XG5cblx0XHRcdFx0Ly8gaW5jcmVhc2UgaW5kZXhcblx0XHRcdFx0aW5kZXgrKztcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyBpbmNyZWFzZSB0aGUgcmFkaXVzIGZvciBuZXh0IHJvdyBvZiB2ZXJ0aWNlc1xuXHRcdFx0cmFkaXVzICs9IHJhZGl1c1N0ZXA7XG5cblx0XHR9XG5cblx0XHQvLyBnZW5lcmF0ZSBpbmRpY2VzXG5cblx0XHRmb3IgKCBqID0gMDsgaiA8IHBoaVNlZ21lbnRzOyBqICsrICkge1xuXG5cdFx0XHR2YXIgdGhldGFTZWdtZW50TGV2ZWwgPSBqICogKCB0aGV0YVNlZ21lbnRzICsgMSApO1xuXG5cdFx0XHRmb3IgKCBpID0gMDsgaSA8IHRoZXRhU2VnbWVudHM7IGkgKysgKSB7XG5cblx0XHRcdFx0c2VnbWVudCA9IGkgKyB0aGV0YVNlZ21lbnRMZXZlbDtcblxuXHRcdFx0XHQvLyBpbmRpY2VzXG5cdFx0XHRcdHZhciBhID0gc2VnbWVudDtcblx0XHRcdFx0dmFyIGIgPSBzZWdtZW50ICsgdGhldGFTZWdtZW50cyArIDE7XG5cdFx0XHRcdHZhciBjID0gc2VnbWVudCArIHRoZXRhU2VnbWVudHMgKyAyO1xuXHRcdFx0XHR2YXIgZCA9IHNlZ21lbnQgKyAxO1xuXG5cdFx0XHRcdC8vIGZhY2Ugb25lXG5cdFx0XHRcdGluZGljZXMuc2V0WCggaW5kZXhPZmZzZXQsIGEgKTsgaW5kZXhPZmZzZXQrKztcblx0XHRcdFx0aW5kaWNlcy5zZXRYKCBpbmRleE9mZnNldCwgYiApOyBpbmRleE9mZnNldCsrO1xuXHRcdFx0XHRpbmRpY2VzLnNldFgoIGluZGV4T2Zmc2V0LCBjICk7IGluZGV4T2Zmc2V0Kys7XG5cblx0XHRcdFx0Ly8gZmFjZSB0d29cblx0XHRcdFx0aW5kaWNlcy5zZXRYKCBpbmRleE9mZnNldCwgYSApOyBpbmRleE9mZnNldCsrO1xuXHRcdFx0XHRpbmRpY2VzLnNldFgoIGluZGV4T2Zmc2V0LCBjICk7IGluZGV4T2Zmc2V0Kys7XG5cdFx0XHRcdGluZGljZXMuc2V0WCggaW5kZXhPZmZzZXQsIGQgKTsgaW5kZXhPZmZzZXQrKztcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0Ly8gYnVpbGQgZ2VvbWV0cnlcblxuXHRcdHRoaXMuc2V0SW5kZXgoIGluZGljZXMgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ3Bvc2l0aW9uJywgdmVydGljZXMgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ25vcm1hbCcsIG5vcm1hbHMgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ3V2JywgdXZzICk7XG5cblx0fVxuXG5cdFJpbmdCdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBCdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUgKTtcblx0UmluZ0J1ZmZlckdlb21ldHJ5LnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFJpbmdCdWZmZXJHZW9tZXRyeTtcblxuXHQvKipcblx0ICogQGF1dGhvciBLYWxlYiBNdXJwaHlcblx0ICovXG5cblx0ZnVuY3Rpb24gUmluZ0dlb21ldHJ5KCBpbm5lclJhZGl1cywgb3V0ZXJSYWRpdXMsIHRoZXRhU2VnbWVudHMsIHBoaVNlZ21lbnRzLCB0aGV0YVN0YXJ0LCB0aGV0YUxlbmd0aCApIHtcblxuXHRcdEdlb21ldHJ5LmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdSaW5nR2VvbWV0cnknO1xuXG5cdFx0dGhpcy5wYXJhbWV0ZXJzID0ge1xuXHRcdFx0aW5uZXJSYWRpdXM6IGlubmVyUmFkaXVzLFxuXHRcdFx0b3V0ZXJSYWRpdXM6IG91dGVyUmFkaXVzLFxuXHRcdFx0dGhldGFTZWdtZW50czogdGhldGFTZWdtZW50cyxcblx0XHRcdHBoaVNlZ21lbnRzOiBwaGlTZWdtZW50cyxcblx0XHRcdHRoZXRhU3RhcnQ6IHRoZXRhU3RhcnQsXG5cdFx0XHR0aGV0YUxlbmd0aDogdGhldGFMZW5ndGhcblx0XHR9O1xuXG5cdFx0dGhpcy5mcm9tQnVmZmVyR2VvbWV0cnkoIG5ldyBSaW5nQnVmZmVyR2VvbWV0cnkoIGlubmVyUmFkaXVzLCBvdXRlclJhZGl1cywgdGhldGFTZWdtZW50cywgcGhpU2VnbWVudHMsIHRoZXRhU3RhcnQsIHRoZXRhTGVuZ3RoICkgKTtcblxuXHR9XG5cblx0UmluZ0dlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRSaW5nR2VvbWV0cnkucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gUmluZ0dlb21ldHJ5O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBiYXNlZCBvbiBodHRwOi8vcGFwZXJ2aXNpb24zZC5nb29nbGVjb2RlLmNvbS9zdm4vdHJ1bmsvYXMzL3RydW5rL3NyYy9vcmcvcGFwZXJ2aXNpb24zZC9vYmplY3RzL3ByaW1pdGl2ZXMvUGxhbmUuYXNcblx0ICovXG5cblx0ZnVuY3Rpb24gUGxhbmVHZW9tZXRyeSggd2lkdGgsIGhlaWdodCwgd2lkdGhTZWdtZW50cywgaGVpZ2h0U2VnbWVudHMgKSB7XG5cblx0XHRHZW9tZXRyeS5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnUGxhbmVHZW9tZXRyeSc7XG5cblx0XHR0aGlzLnBhcmFtZXRlcnMgPSB7XG5cdFx0XHR3aWR0aDogd2lkdGgsXG5cdFx0XHRoZWlnaHQ6IGhlaWdodCxcblx0XHRcdHdpZHRoU2VnbWVudHM6IHdpZHRoU2VnbWVudHMsXG5cdFx0XHRoZWlnaHRTZWdtZW50czogaGVpZ2h0U2VnbWVudHNcblx0XHR9O1xuXG5cdFx0dGhpcy5mcm9tQnVmZmVyR2VvbWV0cnkoIG5ldyBQbGFuZUJ1ZmZlckdlb21ldHJ5KCB3aWR0aCwgaGVpZ2h0LCB3aWR0aFNlZ21lbnRzLCBoZWlnaHRTZWdtZW50cyApICk7XG5cblx0fVxuXG5cdFBsYW5lR2VvbWV0cnkucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggR2VvbWV0cnkucHJvdG90eXBlICk7XG5cdFBsYW5lR2VvbWV0cnkucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gUGxhbmVHZW9tZXRyeTtcblxuXHQvKipcblx0ICogQGF1dGhvciBNdWdlbjg3IC8gaHR0cHM6Ly9naXRodWIuY29tL011Z2VuODdcblx0ICovXG5cblx0IC8vIHBvaW50cyAtIHRvIGNyZWF0ZSBhIGNsb3NlZCB0b3J1cywgb25lIG11c3QgdXNlIGEgc2V0IG9mIHBvaW50c1xuXHQgLy8gICAgbGlrZSBzbzogWyBhLCBiLCBjLCBkLCBhIF0sIHNlZSBmaXJzdCBpcyB0aGUgc2FtZSBhcyBsYXN0LlxuXHQgLy8gc2VnbWVudHMgLSB0aGUgbnVtYmVyIG9mIGNpcmN1bWZlcmVuY2Ugc2VnbWVudHMgdG8gY3JlYXRlXG5cdCAvLyBwaGlTdGFydCAtIHRoZSBzdGFydGluZyByYWRpYW5cblx0IC8vIHBoaUxlbmd0aCAtIHRoZSByYWRpYW4gKDAgdG8gMlBJKSByYW5nZSBvZiB0aGUgbGF0aGVkIHNlY3Rpb25cblx0IC8vICAgIDJQSSBpcyBhIGNsb3NlZCBsYXRoZSwgbGVzcyB0aGFuIDJQSSBpcyBhIHBvcnRpb24uXG5cblx0ZnVuY3Rpb24gTGF0aGVCdWZmZXJHZW9tZXRyeSggcG9pbnRzLCBzZWdtZW50cywgcGhpU3RhcnQsIHBoaUxlbmd0aCApIHtcblxuXHRcdEJ1ZmZlckdlb21ldHJ5LmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdMYXRoZUJ1ZmZlckdlb21ldHJ5JztcblxuXHRcdHRoaXMucGFyYW1ldGVycyA9IHtcblx0XHRcdHBvaW50czogcG9pbnRzLFxuXHRcdFx0c2VnbWVudHM6IHNlZ21lbnRzLFxuXHRcdFx0cGhpU3RhcnQ6IHBoaVN0YXJ0LFxuXHRcdFx0cGhpTGVuZ3RoOiBwaGlMZW5ndGhcblx0XHR9O1xuXG5cdFx0c2VnbWVudHMgPSBNYXRoLmZsb29yKCBzZWdtZW50cyApIHx8IDEyO1xuXHRcdHBoaVN0YXJ0ID0gcGhpU3RhcnQgfHwgMDtcblx0XHRwaGlMZW5ndGggPSBwaGlMZW5ndGggfHwgTWF0aC5QSSAqIDI7XG5cblx0XHQvLyBjbGFtcCBwaGlMZW5ndGggc28gaXQncyBpbiByYW5nZSBvZiBbIDAsIDJQSSBdXG5cdFx0cGhpTGVuZ3RoID0gX01hdGguY2xhbXAoIHBoaUxlbmd0aCwgMCwgTWF0aC5QSSAqIDIgKTtcblxuXHRcdC8vIHRoZXNlIGFyZSB1c2VkIHRvIGNhbGN1bGF0ZSBidWZmZXIgbGVuZ3RoXG5cdFx0dmFyIHZlcnRleENvdW50ID0gKCBzZWdtZW50cyArIDEgKSAqIHBvaW50cy5sZW5ndGg7XG5cdFx0dmFyIGluZGV4Q291bnQgPSBzZWdtZW50cyAqIHBvaW50cy5sZW5ndGggKiAyICogMztcblxuXHRcdC8vIGJ1ZmZlcnNcblx0XHR2YXIgaW5kaWNlcyA9IG5ldyBCdWZmZXJBdHRyaWJ1dGUoIG5ldyAoIGluZGV4Q291bnQgPiA2NTUzNSA/IFVpbnQzMkFycmF5IDogVWludDE2QXJyYXkgKSggaW5kZXhDb3VudCApICwgMSApO1xuXHRcdHZhciB2ZXJ0aWNlcyA9IG5ldyBCdWZmZXJBdHRyaWJ1dGUoIG5ldyBGbG9hdDMyQXJyYXkoIHZlcnRleENvdW50ICogMyApLCAzICk7XG5cdFx0dmFyIHV2cyA9IG5ldyBCdWZmZXJBdHRyaWJ1dGUoIG5ldyBGbG9hdDMyQXJyYXkoIHZlcnRleENvdW50ICogMiApLCAyICk7XG5cblx0XHQvLyBoZWxwZXIgdmFyaWFibGVzXG5cdFx0dmFyIGluZGV4ID0gMCwgaW5kZXhPZmZzZXQgPSAwLCBiYXNlO1xuXHRcdHZhciBpbnZlcnNlU2VnbWVudHMgPSAxLjAgLyBzZWdtZW50cztcblx0XHR2YXIgdmVydGV4ID0gbmV3IFZlY3RvcjMoKTtcblx0XHR2YXIgdXYgPSBuZXcgVmVjdG9yMigpO1xuXHRcdHZhciBpLCBqO1xuXG5cdFx0Ly8gZ2VuZXJhdGUgdmVydGljZXMgYW5kIHV2c1xuXG5cdFx0Zm9yICggaSA9IDA7IGkgPD0gc2VnbWVudHM7IGkgKysgKSB7XG5cblx0XHRcdHZhciBwaGkgPSBwaGlTdGFydCArIGkgKiBpbnZlcnNlU2VnbWVudHMgKiBwaGlMZW5ndGg7XG5cblx0XHRcdHZhciBzaW4gPSBNYXRoLnNpbiggcGhpICk7XG5cdFx0XHR2YXIgY29zID0gTWF0aC5jb3MoIHBoaSApO1xuXG5cdFx0XHRmb3IgKCBqID0gMDsgaiA8PSAoIHBvaW50cy5sZW5ndGggLSAxICk7IGogKysgKSB7XG5cblx0XHRcdFx0Ly8gdmVydGV4XG5cdFx0XHRcdHZlcnRleC54ID0gcG9pbnRzWyBqIF0ueCAqIHNpbjtcblx0XHRcdFx0dmVydGV4LnkgPSBwb2ludHNbIGogXS55O1xuXHRcdFx0XHR2ZXJ0ZXgueiA9IHBvaW50c1sgaiBdLnggKiBjb3M7XG5cdFx0XHRcdHZlcnRpY2VzLnNldFhZWiggaW5kZXgsIHZlcnRleC54LCB2ZXJ0ZXgueSwgdmVydGV4LnogKTtcblxuXHRcdFx0XHQvLyB1dlxuXHRcdFx0XHR1di54ID0gaSAvIHNlZ21lbnRzO1xuXHRcdFx0XHR1di55ID0gaiAvICggcG9pbnRzLmxlbmd0aCAtIDEgKTtcblx0XHRcdFx0dXZzLnNldFhZKCBpbmRleCwgdXYueCwgdXYueSApO1xuXG5cdFx0XHRcdC8vIGluY3JlYXNlIGluZGV4XG5cdFx0XHRcdGluZGV4ICsrO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHQvLyBnZW5lcmF0ZSBpbmRpY2VzXG5cblx0XHRmb3IgKCBpID0gMDsgaSA8IHNlZ21lbnRzOyBpICsrICkge1xuXG5cdFx0XHRmb3IgKCBqID0gMDsgaiA8ICggcG9pbnRzLmxlbmd0aCAtIDEgKTsgaiArKyApIHtcblxuXHRcdFx0XHRiYXNlID0gaiArIGkgKiBwb2ludHMubGVuZ3RoO1xuXG5cdFx0XHRcdC8vIGluZGljZXNcblx0XHRcdFx0dmFyIGEgPSBiYXNlO1xuXHRcdFx0XHR2YXIgYiA9IGJhc2UgKyBwb2ludHMubGVuZ3RoO1xuXHRcdFx0XHR2YXIgYyA9IGJhc2UgKyBwb2ludHMubGVuZ3RoICsgMTtcblx0XHRcdFx0dmFyIGQgPSBiYXNlICsgMTtcblxuXHRcdFx0XHQvLyBmYWNlIG9uZVxuXHRcdFx0XHRpbmRpY2VzLnNldFgoIGluZGV4T2Zmc2V0LCBhICk7IGluZGV4T2Zmc2V0Kys7XG5cdFx0XHRcdGluZGljZXMuc2V0WCggaW5kZXhPZmZzZXQsIGIgKTsgaW5kZXhPZmZzZXQrKztcblx0XHRcdFx0aW5kaWNlcy5zZXRYKCBpbmRleE9mZnNldCwgZCApOyBpbmRleE9mZnNldCsrO1xuXG5cdFx0XHRcdC8vIGZhY2UgdHdvXG5cdFx0XHRcdGluZGljZXMuc2V0WCggaW5kZXhPZmZzZXQsIGIgKTsgaW5kZXhPZmZzZXQrKztcblx0XHRcdFx0aW5kaWNlcy5zZXRYKCBpbmRleE9mZnNldCwgYyApOyBpbmRleE9mZnNldCsrO1xuXHRcdFx0XHRpbmRpY2VzLnNldFgoIGluZGV4T2Zmc2V0LCBkICk7IGluZGV4T2Zmc2V0Kys7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdC8vIGJ1aWxkIGdlb21ldHJ5XG5cblx0XHR0aGlzLnNldEluZGV4KCBpbmRpY2VzICk7XG5cdFx0dGhpcy5hZGRBdHRyaWJ1dGUoICdwb3NpdGlvbicsIHZlcnRpY2VzICk7XG5cdFx0dGhpcy5hZGRBdHRyaWJ1dGUoICd1dicsIHV2cyApO1xuXG5cdFx0Ly8gZ2VuZXJhdGUgbm9ybWFsc1xuXG5cdFx0dGhpcy5jb21wdXRlVmVydGV4Tm9ybWFscygpO1xuXG5cdFx0Ly8gaWYgdGhlIGdlb21ldHJ5IGlzIGNsb3NlZCwgd2UgbmVlZCB0byBhdmVyYWdlIHRoZSBub3JtYWxzIGFsb25nIHRoZSBzZWFtLlxuXHRcdC8vIGJlY2F1c2UgdGhlIGNvcnJlc3BvbmRpbmcgdmVydGljZXMgYXJlIGlkZW50aWNhbCAoYnV0IHN0aWxsIGhhdmUgZGlmZmVyZW50IFVWcykuXG5cblx0XHRpZiggcGhpTGVuZ3RoID09PSBNYXRoLlBJICogMiApIHtcblxuXHRcdFx0dmFyIG5vcm1hbHMgPSB0aGlzLmF0dHJpYnV0ZXMubm9ybWFsLmFycmF5O1xuXHRcdFx0dmFyIG4xID0gbmV3IFZlY3RvcjMoKTtcblx0XHRcdHZhciBuMiA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0XHR2YXIgbiA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdC8vIHRoaXMgaXMgdGhlIGJ1ZmZlciBvZmZzZXQgZm9yIHRoZSBsYXN0IGxpbmUgb2YgdmVydGljZXNcblx0XHRcdGJhc2UgPSBzZWdtZW50cyAqIHBvaW50cy5sZW5ndGggKiAzO1xuXG5cdFx0XHRmb3IoIGkgPSAwLCBqID0gMDsgaSA8IHBvaW50cy5sZW5ndGg7IGkgKyssIGogKz0gMyApIHtcblxuXHRcdFx0XHQvLyBzZWxlY3QgdGhlIG5vcm1hbCBvZiB0aGUgdmVydGV4IGluIHRoZSBmaXJzdCBsaW5lXG5cdFx0XHRcdG4xLnggPSBub3JtYWxzWyBqICsgMCBdO1xuXHRcdFx0XHRuMS55ID0gbm9ybWFsc1sgaiArIDEgXTtcblx0XHRcdFx0bjEueiA9IG5vcm1hbHNbIGogKyAyIF07XG5cblx0XHRcdFx0Ly8gc2VsZWN0IHRoZSBub3JtYWwgb2YgdGhlIHZlcnRleCBpbiB0aGUgbGFzdCBsaW5lXG5cdFx0XHRcdG4yLnggPSBub3JtYWxzWyBiYXNlICsgaiArIDAgXTtcblx0XHRcdFx0bjIueSA9IG5vcm1hbHNbIGJhc2UgKyBqICsgMSBdO1xuXHRcdFx0XHRuMi56ID0gbm9ybWFsc1sgYmFzZSArIGogKyAyIF07XG5cblx0XHRcdFx0Ly8gYXZlcmFnZSBub3JtYWxzXG5cdFx0XHRcdG4uYWRkVmVjdG9ycyggbjEsIG4yICkubm9ybWFsaXplKCk7XG5cblx0XHRcdFx0Ly8gYXNzaWduIHRoZSBuZXcgdmFsdWVzIHRvIGJvdGggbm9ybWFsc1xuXHRcdFx0XHRub3JtYWxzWyBqICsgMCBdID0gbm9ybWFsc1sgYmFzZSArIGogKyAwIF0gPSBuLng7XG5cdFx0XHRcdG5vcm1hbHNbIGogKyAxIF0gPSBub3JtYWxzWyBiYXNlICsgaiArIDEgXSA9IG4ueTtcblx0XHRcdFx0bm9ybWFsc1sgaiArIDIgXSA9IG5vcm1hbHNbIGJhc2UgKyBqICsgMiBdID0gbi56O1xuXG5cdFx0XHR9IC8vIG5leHQgcm93XG5cblx0XHR9XG5cblx0fVxuXG5cdExhdGhlQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlICk7XG5cdExhdGhlQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gTGF0aGVCdWZmZXJHZW9tZXRyeTtcblxuXHQvKipcblx0ICogQGF1dGhvciBhc3Ryb2R1ZCAvIGh0dHA6Ly9hc3Ryb2R1ZC5pc2dyZWF0Lm9yZy9cblx0ICogQGF1dGhvciB6ejg1IC8gaHR0cHM6Ly9naXRodWIuY29tL3p6ODVcblx0ICogQGF1dGhvciBiaG91c3RvbiAvIGh0dHA6Ly9jbGFyYS5pb1xuXHQgKi9cblxuXHQvLyBwb2ludHMgLSB0byBjcmVhdGUgYSBjbG9zZWQgdG9ydXMsIG9uZSBtdXN0IHVzZSBhIHNldCBvZiBwb2ludHNcblx0Ly8gICAgbGlrZSBzbzogWyBhLCBiLCBjLCBkLCBhIF0sIHNlZSBmaXJzdCBpcyB0aGUgc2FtZSBhcyBsYXN0LlxuXHQvLyBzZWdtZW50cyAtIHRoZSBudW1iZXIgb2YgY2lyY3VtZmVyZW5jZSBzZWdtZW50cyB0byBjcmVhdGVcblx0Ly8gcGhpU3RhcnQgLSB0aGUgc3RhcnRpbmcgcmFkaWFuXG5cdC8vIHBoaUxlbmd0aCAtIHRoZSByYWRpYW4gKDAgdG8gMlBJKSByYW5nZSBvZiB0aGUgbGF0aGVkIHNlY3Rpb25cblx0Ly8gICAgMlBJIGlzIGEgY2xvc2VkIGxhdGhlLCBsZXNzIHRoYW4gMlBJIGlzIGEgcG9ydGlvbi5cblxuXHRmdW5jdGlvbiBMYXRoZUdlb21ldHJ5KCBwb2ludHMsIHNlZ21lbnRzLCBwaGlTdGFydCwgcGhpTGVuZ3RoICkge1xuXG5cdFx0R2VvbWV0cnkuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ0xhdGhlR2VvbWV0cnknO1xuXG5cdFx0dGhpcy5wYXJhbWV0ZXJzID0ge1xuXHRcdFx0cG9pbnRzOiBwb2ludHMsXG5cdFx0XHRzZWdtZW50czogc2VnbWVudHMsXG5cdFx0XHRwaGlTdGFydDogcGhpU3RhcnQsXG5cdFx0XHRwaGlMZW5ndGg6IHBoaUxlbmd0aFxuXHRcdH07XG5cblx0XHR0aGlzLmZyb21CdWZmZXJHZW9tZXRyeSggbmV3IExhdGhlQnVmZmVyR2VvbWV0cnkoIHBvaW50cywgc2VnbWVudHMsIHBoaVN0YXJ0LCBwaGlMZW5ndGggKSApO1xuXHRcdHRoaXMubWVyZ2VWZXJ0aWNlcygpO1xuXG5cdH1cblxuXHRMYXRoZUdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRMYXRoZUdlb21ldHJ5LnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IExhdGhlR2VvbWV0cnk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgTXVnZW44NyAvIGh0dHBzOi8vZ2l0aHViLmNvbS9NdWdlbjg3XG5cdCAqXG5cdCAqIENyZWF0ZXMgYSBvbmUtc2lkZWQgcG9seWdvbmFsIGdlb21ldHJ5IGZyb20gb25lIG9yIG1vcmUgc2hhcGVzLlxuXHQgKlxuXHQgKiovXG5cblx0ZnVuY3Rpb24gU2hhcGVCdWZmZXJHZW9tZXRyeSggc2hhcGVzLCBjdXJ2ZVNlZ21lbnRzICkge1xuXG5cdFx0QnVmZmVyR2VvbWV0cnkuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ1NoYXBlQnVmZmVyR2VvbWV0cnknO1xuXG5cdFx0dGhpcy5wYXJhbWV0ZXJzID0ge1xuXHRcdFx0c2hhcGVzOiBzaGFwZXMsXG5cdFx0XHRjdXJ2ZVNlZ21lbnRzOiBjdXJ2ZVNlZ21lbnRzXG5cdFx0fTtcblxuXHRcdGN1cnZlU2VnbWVudHMgPSBjdXJ2ZVNlZ21lbnRzIHx8IDEyO1xuXG5cdFx0dmFyIHZlcnRpY2VzID0gW107XG5cdFx0dmFyIG5vcm1hbHMgPSBbXTtcblx0XHR2YXIgdXZzID0gW107XG5cdFx0dmFyIGluZGljZXMgPSBbXTtcblxuXHRcdHZhciBncm91cFN0YXJ0ID0gMDtcblx0XHR2YXIgZ3JvdXBDb3VudCA9IDA7XG5cblx0XHQvLyBhbGxvdyBzaW5nbGUgYW5kIGFycmF5IHZhbHVlcyBmb3IgXCJzaGFwZXNcIiBwYXJhbWV0ZXJcblxuXHRcdGlmICggQXJyYXkuaXNBcnJheSggc2hhcGVzICkgPT09IGZhbHNlICkge1xuXG5cdFx0XHRhZGRTaGFwZSggc2hhcGVzICk7XG5cblx0XHR9IGVsc2Uge1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBzaGFwZXMubGVuZ3RoOyBpKysgKSB7XG5cblx0XHRcdFx0YWRkU2hhcGUoIHNoYXBlc1sgaSBdICk7XG5cblx0XHRcdFx0dGhpcy5hZGRHcm91cCggZ3JvdXBTdGFydCwgZ3JvdXBDb3VudCwgaSApOyAvLyBlbmFibGVzIE11bHRpTWF0ZXJpYWwgc3VwcG9ydFxuXG5cdFx0XHRcdGdyb3VwU3RhcnQgKz0gZ3JvdXBDb3VudDtcblx0XHRcdFx0Z3JvdXBDb3VudCA9IDA7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdC8vIGJ1aWxkIGdlb21ldHJ5XG5cblx0XHR0aGlzLnNldEluZGV4KCBuZXcgKCBpbmRpY2VzLmxlbmd0aCA+IDY1NTM1ID8gVWludDMyQnVmZmVyQXR0cmlidXRlIDogVWludDE2QnVmZmVyQXR0cmlidXRlICkoIGluZGljZXMsIDEgKSApO1xuXHRcdHRoaXMuYWRkQXR0cmlidXRlKCAncG9zaXRpb24nLCBuZXcgRmxvYXQzMkJ1ZmZlckF0dHJpYnV0ZSggdmVydGljZXMsIDMgKSApO1xuXHRcdHRoaXMuYWRkQXR0cmlidXRlKCAnbm9ybWFsJywgbmV3IEZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUoIG5vcm1hbHMsIDMgKSApO1xuXHRcdHRoaXMuYWRkQXR0cmlidXRlKCAndXYnLCBuZXcgRmxvYXQzMkJ1ZmZlckF0dHJpYnV0ZSggdXZzLCAyICkgKTtcblxuXG5cdFx0Ly8gaGVscGVyIGZ1bmN0aW9uc1xuXG5cdFx0ZnVuY3Rpb24gYWRkU2hhcGUoIHNoYXBlICkge1xuXG5cdFx0XHR2YXIgaSwgbCwgc2hhcGVIb2xlO1xuXG5cdFx0XHR2YXIgaW5kZXhPZmZzZXQgPSB2ZXJ0aWNlcy5sZW5ndGggLyAzO1xuXHRcdFx0dmFyIHBvaW50cyA9IHNoYXBlLmV4dHJhY3RQb2ludHMoIGN1cnZlU2VnbWVudHMgKTtcblxuXHRcdFx0dmFyIHNoYXBlVmVydGljZXMgPSBwb2ludHMuc2hhcGU7XG5cdFx0XHR2YXIgc2hhcGVIb2xlcyA9IHBvaW50cy5ob2xlcztcblxuXHRcdFx0Ly8gY2hlY2sgZGlyZWN0aW9uIG9mIHZlcnRpY2VzXG5cblx0XHRcdGlmICggU2hhcGVVdGlscy5pc0Nsb2NrV2lzZSggc2hhcGVWZXJ0aWNlcyApID09PSBmYWxzZSApIHtcblxuXHRcdFx0XHRzaGFwZVZlcnRpY2VzID0gc2hhcGVWZXJ0aWNlcy5yZXZlcnNlKCk7XG5cblx0XHRcdFx0Ly8gYWxzbyBjaGVjayBpZiBob2xlcyBhcmUgaW4gdGhlIG9wcG9zaXRlIGRpcmVjdGlvblxuXG5cdFx0XHRcdGZvciAoIGkgPSAwLCBsID0gc2hhcGVIb2xlcy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdFx0c2hhcGVIb2xlID0gc2hhcGVIb2xlc1sgaSBdO1xuXG5cdFx0XHRcdFx0aWYgKCBTaGFwZVV0aWxzLmlzQ2xvY2tXaXNlKCBzaGFwZUhvbGUgKSA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRcdFx0c2hhcGVIb2xlc1sgaSBdID0gc2hhcGVIb2xlLnJldmVyc2UoKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIGZhY2VzID0gU2hhcGVVdGlscy50cmlhbmd1bGF0ZVNoYXBlKCBzaGFwZVZlcnRpY2VzLCBzaGFwZUhvbGVzICk7XG5cblx0XHRcdC8vIGpvaW4gdmVydGljZXMgb2YgaW5uZXIgYW5kIG91dGVyIHBhdGhzIHRvIGEgc2luZ2xlIGFycmF5XG5cblx0XHRcdGZvciAoIGkgPSAwLCBsID0gc2hhcGVIb2xlcy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdHNoYXBlSG9sZSA9IHNoYXBlSG9sZXNbIGkgXTtcblx0XHRcdFx0c2hhcGVWZXJ0aWNlcyA9IHNoYXBlVmVydGljZXMuY29uY2F0KCBzaGFwZUhvbGUgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyB2ZXJ0aWNlcywgbm9ybWFscywgdXZzXG5cblx0XHRcdGZvciAoIGkgPSAwLCBsID0gc2hhcGVWZXJ0aWNlcy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdHZhciB2ZXJ0ZXggPSBzaGFwZVZlcnRpY2VzWyBpIF07XG5cblx0XHRcdFx0dmVydGljZXMucHVzaCggdmVydGV4LngsIHZlcnRleC55LCAwICk7XG5cdFx0XHRcdG5vcm1hbHMucHVzaCggMCwgMCwgMSApO1xuXHRcdFx0XHR1dnMucHVzaCggdmVydGV4LngsIHZlcnRleC55ICk7IC8vIHdvcmxkIHV2c1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIGluY2lkZXNcblxuXHRcdFx0Zm9yICggaSA9IDAsIGwgPSBmYWNlcy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdHZhciBmYWNlID0gZmFjZXNbIGkgXTtcblxuXHRcdFx0XHR2YXIgYSA9IGZhY2VbIDAgXSArIGluZGV4T2Zmc2V0O1xuXHRcdFx0XHR2YXIgYiA9IGZhY2VbIDEgXSArIGluZGV4T2Zmc2V0O1xuXHRcdFx0XHR2YXIgYyA9IGZhY2VbIDIgXSArIGluZGV4T2Zmc2V0O1xuXG5cdFx0XHRcdGluZGljZXMucHVzaCggYSwgYiwgYyApO1xuXHRcdFx0XHRncm91cENvdW50ICs9IDM7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHR9XG5cblx0U2hhcGVCdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBCdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUgKTtcblx0U2hhcGVCdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBTaGFwZUJ1ZmZlckdlb21ldHJ5O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGpvbm9icjEgLyBodHRwOi8vam9ub2JyMS5jb21cblx0ICpcblx0ICogQ3JlYXRlcyBhIG9uZS1zaWRlZCBwb2x5Z29uYWwgZ2VvbWV0cnkgZnJvbSBhIHBhdGggc2hhcGUuXG5cdCAqXG5cdCAqKi9cblxuXHRmdW5jdGlvbiBTaGFwZUdlb21ldHJ5KCBzaGFwZXMsIGN1cnZlU2VnbWVudHMgKSB7XG5cblx0XHRHZW9tZXRyeS5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnU2hhcGVHZW9tZXRyeSc7XG5cblx0XHRpZiAoIHR5cGVvZiBjdXJ2ZVNlZ21lbnRzID09PSAnb2JqZWN0JyApIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuU2hhcGVHZW9tZXRyeTogT3B0aW9ucyBwYXJhbWV0ZXIgaGFzIGJlZW4gcmVtb3ZlZC4nICk7XG5cblx0XHRcdGN1cnZlU2VnbWVudHMgPSBjdXJ2ZVNlZ21lbnRzLmN1cnZlU2VnbWVudHM7XG5cblx0XHR9XG5cblx0XHR0aGlzLnBhcmFtZXRlcnMgPSB7XG5cdFx0XHRzaGFwZXM6IHNoYXBlcyxcblx0XHRcdGN1cnZlU2VnbWVudHM6IGN1cnZlU2VnbWVudHNcblx0XHR9O1xuXG5cdFx0dGhpcy5mcm9tQnVmZmVyR2VvbWV0cnkoIG5ldyBTaGFwZUJ1ZmZlckdlb21ldHJ5KCBzaGFwZXMsIGN1cnZlU2VnbWVudHMgKSApO1xuXHRcdHRoaXMubWVyZ2VWZXJ0aWNlcygpO1xuXG5cdH1cblxuXHRTaGFwZUdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRTaGFwZUdlb21ldHJ5LnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFNoYXBlR2VvbWV0cnk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgV2VzdExhbmdsZXkgLyBodHRwOi8vZ2l0aHViLmNvbS9XZXN0TGFuZ2xleVxuXHQgKi9cblxuXHRmdW5jdGlvbiBFZGdlc0dlb21ldHJ5KCBnZW9tZXRyeSwgdGhyZXNob2xkQW5nbGUgKSB7XG5cblx0XHRCdWZmZXJHZW9tZXRyeS5jYWxsKCB0aGlzICk7XG5cblx0XHR0aHJlc2hvbGRBbmdsZSA9ICggdGhyZXNob2xkQW5nbGUgIT09IHVuZGVmaW5lZCApID8gdGhyZXNob2xkQW5nbGUgOiAxO1xuXG5cdFx0dmFyIHRocmVzaG9sZERvdCA9IE1hdGguY29zKCBfTWF0aC5ERUcyUkFEICogdGhyZXNob2xkQW5nbGUgKTtcblxuXHRcdHZhciBlZGdlID0gWyAwLCAwIF0sIGhhc2ggPSB7fTtcblxuXHRcdGZ1bmN0aW9uIHNvcnRGdW5jdGlvbiggYSwgYiApIHtcblxuXHRcdFx0cmV0dXJuIGEgLSBiO1xuXG5cdFx0fVxuXG5cdFx0dmFyIGtleXMgPSBbICdhJywgJ2InLCAnYycgXTtcblxuXHRcdHZhciBnZW9tZXRyeTI7XG5cblx0XHRpZiAoIGdlb21ldHJ5LmlzQnVmZmVyR2VvbWV0cnkgKSB7XG5cblx0XHRcdGdlb21ldHJ5MiA9IG5ldyBHZW9tZXRyeSgpO1xuXHRcdFx0Z2VvbWV0cnkyLmZyb21CdWZmZXJHZW9tZXRyeSggZ2VvbWV0cnkgKTtcblxuXHRcdH0gZWxzZSB7XG5cblx0XHRcdGdlb21ldHJ5MiA9IGdlb21ldHJ5LmNsb25lKCk7XG5cblx0XHR9XG5cblx0XHRnZW9tZXRyeTIubWVyZ2VWZXJ0aWNlcygpO1xuXHRcdGdlb21ldHJ5Mi5jb21wdXRlRmFjZU5vcm1hbHMoKTtcblxuXHRcdHZhciB2ZXJ0aWNlcyA9IGdlb21ldHJ5Mi52ZXJ0aWNlcztcblx0XHR2YXIgZmFjZXMgPSBnZW9tZXRyeTIuZmFjZXM7XG5cblx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBmYWNlcy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHR2YXIgZmFjZSA9IGZhY2VzWyBpIF07XG5cblx0XHRcdGZvciAoIHZhciBqID0gMDsgaiA8IDM7IGogKysgKSB7XG5cblx0XHRcdFx0ZWRnZVsgMCBdID0gZmFjZVsga2V5c1sgaiBdIF07XG5cdFx0XHRcdGVkZ2VbIDEgXSA9IGZhY2VbIGtleXNbICggaiArIDEgKSAlIDMgXSBdO1xuXHRcdFx0XHRlZGdlLnNvcnQoIHNvcnRGdW5jdGlvbiApO1xuXG5cdFx0XHRcdHZhciBrZXkgPSBlZGdlLnRvU3RyaW5nKCk7XG5cblx0XHRcdFx0aWYgKCBoYXNoWyBrZXkgXSA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0aGFzaFsga2V5IF0gPSB7IHZlcnQxOiBlZGdlWyAwIF0sIHZlcnQyOiBlZGdlWyAxIF0sIGZhY2UxOiBpLCBmYWNlMjogdW5kZWZpbmVkIH07XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdGhhc2hbIGtleSBdLmZhY2UyID0gaTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdHZhciBjb29yZHMgPSBbXTtcblxuXHRcdGZvciAoIHZhciBrZXkgaW4gaGFzaCApIHtcblxuXHRcdFx0dmFyIGggPSBoYXNoWyBrZXkgXTtcblxuXHRcdFx0Ly8gQW4gZWRnZSBpcyBvbmx5IHJlbmRlcmVkIGlmIHRoZSBhbmdsZSAoaW4gZGVncmVlcykgYmV0d2VlbiB0aGUgZmFjZSBub3JtYWxzIG9mIHRoZSBhZGpvaW5pbmcgZmFjZXMgZXhjZWVkcyB0aGlzIHZhbHVlLiBkZWZhdWx0ID0gMSBkZWdyZWUuXG5cblx0XHRcdGlmICggaC5mYWNlMiA9PT0gdW5kZWZpbmVkIHx8IGZhY2VzWyBoLmZhY2UxIF0ubm9ybWFsLmRvdCggZmFjZXNbIGguZmFjZTIgXS5ub3JtYWwgKSA8PSB0aHJlc2hvbGREb3QgKSB7XG5cblx0XHRcdFx0dmFyIHZlcnRleCA9IHZlcnRpY2VzWyBoLnZlcnQxIF07XG5cdFx0XHRcdGNvb3Jkcy5wdXNoKCB2ZXJ0ZXgueCApO1xuXHRcdFx0XHRjb29yZHMucHVzaCggdmVydGV4LnkgKTtcblx0XHRcdFx0Y29vcmRzLnB1c2goIHZlcnRleC56ICk7XG5cblx0XHRcdFx0dmVydGV4ID0gdmVydGljZXNbIGgudmVydDIgXTtcblx0XHRcdFx0Y29vcmRzLnB1c2goIHZlcnRleC54ICk7XG5cdFx0XHRcdGNvb3Jkcy5wdXNoKCB2ZXJ0ZXgueSApO1xuXHRcdFx0XHRjb29yZHMucHVzaCggdmVydGV4LnogKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0dGhpcy5hZGRBdHRyaWJ1dGUoICdwb3NpdGlvbicsIG5ldyBGbG9hdDMyQnVmZmVyQXR0cmlidXRlKCBjb29yZHMsIDMgKSApO1xuXG5cdH1cblxuXHRFZGdlc0dlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRFZGdlc0dlb21ldHJ5LnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IEVkZ2VzR2VvbWV0cnk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgTXVnZW44NyAvIGh0dHBzOi8vZ2l0aHViLmNvbS9NdWdlbjg3XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEN5bGluZGVyQnVmZmVyR2VvbWV0cnkoIHJhZGl1c1RvcCwgcmFkaXVzQm90dG9tLCBoZWlnaHQsIHJhZGlhbFNlZ21lbnRzLCBoZWlnaHRTZWdtZW50cywgb3BlbkVuZGVkLCB0aGV0YVN0YXJ0LCB0aGV0YUxlbmd0aCApIHtcblxuXHRcdEJ1ZmZlckdlb21ldHJ5LmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdDeWxpbmRlckJ1ZmZlckdlb21ldHJ5JztcblxuXHRcdHRoaXMucGFyYW1ldGVycyA9IHtcblx0XHRcdHJhZGl1c1RvcDogcmFkaXVzVG9wLFxuXHRcdFx0cmFkaXVzQm90dG9tOiByYWRpdXNCb3R0b20sXG5cdFx0XHRoZWlnaHQ6IGhlaWdodCxcblx0XHRcdHJhZGlhbFNlZ21lbnRzOiByYWRpYWxTZWdtZW50cyxcblx0XHRcdGhlaWdodFNlZ21lbnRzOiBoZWlnaHRTZWdtZW50cyxcblx0XHRcdG9wZW5FbmRlZDogb3BlbkVuZGVkLFxuXHRcdFx0dGhldGFTdGFydDogdGhldGFTdGFydCxcblx0XHRcdHRoZXRhTGVuZ3RoOiB0aGV0YUxlbmd0aFxuXHRcdH07XG5cblx0XHR2YXIgc2NvcGUgPSB0aGlzO1xuXG5cdFx0cmFkaXVzVG9wID0gcmFkaXVzVG9wICE9PSB1bmRlZmluZWQgPyByYWRpdXNUb3AgOiAyMDtcblx0XHRyYWRpdXNCb3R0b20gPSByYWRpdXNCb3R0b20gIT09IHVuZGVmaW5lZCA/IHJhZGl1c0JvdHRvbSA6IDIwO1xuXHRcdGhlaWdodCA9IGhlaWdodCAhPT0gdW5kZWZpbmVkID8gaGVpZ2h0IDogMTAwO1xuXG5cdFx0cmFkaWFsU2VnbWVudHMgPSBNYXRoLmZsb29yKCByYWRpYWxTZWdtZW50cyApIHx8IDg7XG5cdFx0aGVpZ2h0U2VnbWVudHMgPSBNYXRoLmZsb29yKCBoZWlnaHRTZWdtZW50cyApIHx8IDE7XG5cblx0XHRvcGVuRW5kZWQgPSBvcGVuRW5kZWQgIT09IHVuZGVmaW5lZCA/IG9wZW5FbmRlZCA6IGZhbHNlO1xuXHRcdHRoZXRhU3RhcnQgPSB0aGV0YVN0YXJ0ICE9PSB1bmRlZmluZWQgPyB0aGV0YVN0YXJ0IDogMC4wO1xuXHRcdHRoZXRhTGVuZ3RoID0gdGhldGFMZW5ndGggIT09IHVuZGVmaW5lZCA/IHRoZXRhTGVuZ3RoIDogMi4wICogTWF0aC5QSTtcblxuXHRcdC8vIHVzZWQgdG8gY2FsY3VsYXRlIGJ1ZmZlciBsZW5ndGhcblxuXHRcdHZhciBuYkNhcCA9IDA7XG5cblx0XHRpZiAoIG9wZW5FbmRlZCA9PT0gZmFsc2UgKSB7XG5cblx0XHRcdGlmICggcmFkaXVzVG9wID4gMCApIG5iQ2FwICsrO1xuXHRcdFx0aWYgKCByYWRpdXNCb3R0b20gPiAwICkgbmJDYXAgKys7XG5cblx0XHR9XG5cblx0XHR2YXIgdmVydGV4Q291bnQgPSBjYWxjdWxhdGVWZXJ0ZXhDb3VudCgpO1xuXHRcdHZhciBpbmRleENvdW50ID0gY2FsY3VsYXRlSW5kZXhDb3VudCgpO1xuXG5cdFx0Ly8gYnVmZmVyc1xuXG5cdFx0dmFyIGluZGljZXMgPSBuZXcgQnVmZmVyQXR0cmlidXRlKCBuZXcgKCBpbmRleENvdW50ID4gNjU1MzUgPyBVaW50MzJBcnJheSA6IFVpbnQxNkFycmF5ICkoIGluZGV4Q291bnQgKSwgMSApO1xuXHRcdHZhciB2ZXJ0aWNlcyA9IG5ldyBCdWZmZXJBdHRyaWJ1dGUoIG5ldyBGbG9hdDMyQXJyYXkoIHZlcnRleENvdW50ICogMyApLCAzICk7XG5cdFx0dmFyIG5vcm1hbHMgPSBuZXcgQnVmZmVyQXR0cmlidXRlKCBuZXcgRmxvYXQzMkFycmF5KCB2ZXJ0ZXhDb3VudCAqIDMgKSwgMyApO1xuXHRcdHZhciB1dnMgPSBuZXcgQnVmZmVyQXR0cmlidXRlKCBuZXcgRmxvYXQzMkFycmF5KCB2ZXJ0ZXhDb3VudCAqIDIgKSwgMiApO1xuXG5cdFx0Ly8gaGVscGVyIHZhcmlhYmxlc1xuXG5cdFx0dmFyIGluZGV4ID0gMCxcblx0XHQgICAgaW5kZXhPZmZzZXQgPSAwLFxuXHRcdCAgICBpbmRleEFycmF5ID0gW10sXG5cdFx0ICAgIGhhbGZIZWlnaHQgPSBoZWlnaHQgLyAyO1xuXG5cdFx0Ly8gZ3JvdXAgdmFyaWFibGVzXG5cdFx0dmFyIGdyb3VwU3RhcnQgPSAwO1xuXG5cdFx0Ly8gZ2VuZXJhdGUgZ2VvbWV0cnlcblxuXHRcdGdlbmVyYXRlVG9yc28oKTtcblxuXHRcdGlmICggb3BlbkVuZGVkID09PSBmYWxzZSApIHtcblxuXHRcdFx0aWYgKCByYWRpdXNUb3AgPiAwICkgZ2VuZXJhdGVDYXAoIHRydWUgKTtcblx0XHRcdGlmICggcmFkaXVzQm90dG9tID4gMCApIGdlbmVyYXRlQ2FwKCBmYWxzZSApO1xuXG5cdFx0fVxuXG5cdFx0Ly8gYnVpbGQgZ2VvbWV0cnlcblxuXHRcdHRoaXMuc2V0SW5kZXgoIGluZGljZXMgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ3Bvc2l0aW9uJywgdmVydGljZXMgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ25vcm1hbCcsIG5vcm1hbHMgKTtcblx0XHR0aGlzLmFkZEF0dHJpYnV0ZSggJ3V2JywgdXZzICk7XG5cblx0XHQvLyBoZWxwZXIgZnVuY3Rpb25zXG5cblx0XHRmdW5jdGlvbiBjYWxjdWxhdGVWZXJ0ZXhDb3VudCgpIHtcblxuXHRcdFx0dmFyIGNvdW50ID0gKCByYWRpYWxTZWdtZW50cyArIDEgKSAqICggaGVpZ2h0U2VnbWVudHMgKyAxICk7XG5cblx0XHRcdGlmICggb3BlbkVuZGVkID09PSBmYWxzZSApIHtcblxuXHRcdFx0XHRjb3VudCArPSAoICggcmFkaWFsU2VnbWVudHMgKyAxICkgKiBuYkNhcCApICsgKCByYWRpYWxTZWdtZW50cyAqIG5iQ2FwICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIGNvdW50O1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gY2FsY3VsYXRlSW5kZXhDb3VudCgpIHtcblxuXHRcdFx0dmFyIGNvdW50ID0gcmFkaWFsU2VnbWVudHMgKiBoZWlnaHRTZWdtZW50cyAqIDIgKiAzO1xuXG5cdFx0XHRpZiAoIG9wZW5FbmRlZCA9PT0gZmFsc2UgKSB7XG5cblx0XHRcdFx0Y291bnQgKz0gcmFkaWFsU2VnbWVudHMgKiBuYkNhcCAqIDM7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIGNvdW50O1xuXG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gZ2VuZXJhdGVUb3JzbygpIHtcblxuXHRcdFx0dmFyIHgsIHk7XG5cdFx0XHR2YXIgbm9ybWFsID0gbmV3IFZlY3RvcjMoKTtcblx0XHRcdHZhciB2ZXJ0ZXggPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHR2YXIgZ3JvdXBDb3VudCA9IDA7XG5cblx0XHRcdC8vIHRoaXMgd2lsbCBiZSB1c2VkIHRvIGNhbGN1bGF0ZSB0aGUgbm9ybWFsXG5cdFx0XHR2YXIgc2xvcGUgPSAoIHJhZGl1c0JvdHRvbSAtIHJhZGl1c1RvcCApIC8gaGVpZ2h0O1xuXG5cdFx0XHQvLyBnZW5lcmF0ZSB2ZXJ0aWNlcywgbm9ybWFscyBhbmQgdXZzXG5cblx0XHRcdGZvciAoIHkgPSAwOyB5IDw9IGhlaWdodFNlZ21lbnRzOyB5ICsrICkge1xuXG5cdFx0XHRcdHZhciBpbmRleFJvdyA9IFtdO1xuXG5cdFx0XHRcdHZhciB2ID0geSAvIGhlaWdodFNlZ21lbnRzO1xuXG5cdFx0XHRcdC8vIGNhbGN1bGF0ZSB0aGUgcmFkaXVzIG9mIHRoZSBjdXJyZW50IHJvd1xuXHRcdFx0XHR2YXIgcmFkaXVzID0gdiAqICggcmFkaXVzQm90dG9tIC0gcmFkaXVzVG9wICkgKyByYWRpdXNUb3A7XG5cblx0XHRcdFx0Zm9yICggeCA9IDA7IHggPD0gcmFkaWFsU2VnbWVudHM7IHggKysgKSB7XG5cblx0XHRcdFx0XHR2YXIgdSA9IHggLyByYWRpYWxTZWdtZW50cztcblxuXHRcdFx0XHRcdHZhciB0aGV0YSA9IHUgKiB0aGV0YUxlbmd0aCArIHRoZXRhU3RhcnQ7XG5cblx0XHRcdFx0XHR2YXIgc2luVGhldGEgPSBNYXRoLnNpbiggdGhldGEgKTtcblx0XHRcdFx0XHR2YXIgY29zVGhldGEgPSBNYXRoLmNvcyggdGhldGEgKTtcblxuXHRcdFx0XHRcdC8vIHZlcnRleFxuXHRcdFx0XHRcdHZlcnRleC54ID0gcmFkaXVzICogc2luVGhldGE7XG5cdFx0XHRcdFx0dmVydGV4LnkgPSAtIHYgKiBoZWlnaHQgKyBoYWxmSGVpZ2h0O1xuXHRcdFx0XHRcdHZlcnRleC56ID0gcmFkaXVzICogY29zVGhldGE7XG5cdFx0XHRcdFx0dmVydGljZXMuc2V0WFlaKCBpbmRleCwgdmVydGV4LngsIHZlcnRleC55LCB2ZXJ0ZXgueiApO1xuXG5cdFx0XHRcdFx0Ly8gbm9ybWFsXG5cdFx0XHRcdFx0bm9ybWFsLnNldCggc2luVGhldGEsIHNsb3BlLCBjb3NUaGV0YSApLm5vcm1hbGl6ZSgpO1xuXHRcdFx0XHRcdG5vcm1hbHMuc2V0WFlaKCBpbmRleCwgbm9ybWFsLngsIG5vcm1hbC55LCBub3JtYWwueiApO1xuXG5cdFx0XHRcdFx0Ly8gdXZcblx0XHRcdFx0XHR1dnMuc2V0WFkoIGluZGV4LCB1LCAxIC0gdiApO1xuXG5cdFx0XHRcdFx0Ly8gc2F2ZSBpbmRleCBvZiB2ZXJ0ZXggaW4gcmVzcGVjdGl2ZSByb3dcblx0XHRcdFx0XHRpbmRleFJvdy5wdXNoKCBpbmRleCApO1xuXG5cdFx0XHRcdFx0Ly8gaW5jcmVhc2UgaW5kZXhcblx0XHRcdFx0XHRpbmRleCArKztcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gbm93IHNhdmUgdmVydGljZXMgb2YgdGhlIHJvdyBpbiBvdXIgaW5kZXggYXJyYXlcblx0XHRcdFx0aW5kZXhBcnJheS5wdXNoKCBpbmRleFJvdyApO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIGdlbmVyYXRlIGluZGljZXNcblxuXHRcdFx0Zm9yICggeCA9IDA7IHggPCByYWRpYWxTZWdtZW50czsgeCArKyApIHtcblxuXHRcdFx0XHRmb3IgKCB5ID0gMDsgeSA8IGhlaWdodFNlZ21lbnRzOyB5ICsrICkge1xuXG5cdFx0XHRcdFx0Ly8gd2UgdXNlIHRoZSBpbmRleCBhcnJheSB0byBhY2Nlc3MgdGhlIGNvcnJlY3QgaW5kaWNlc1xuXHRcdFx0XHRcdHZhciBpMSA9IGluZGV4QXJyYXlbIHkgXVsgeCBdO1xuXHRcdFx0XHRcdHZhciBpMiA9IGluZGV4QXJyYXlbIHkgKyAxIF1bIHggXTtcblx0XHRcdFx0XHR2YXIgaTMgPSBpbmRleEFycmF5WyB5ICsgMSBdWyB4ICsgMSBdO1xuXHRcdFx0XHRcdHZhciBpNCA9IGluZGV4QXJyYXlbIHkgXVsgeCArIDEgXTtcblxuXHRcdFx0XHRcdC8vIGZhY2Ugb25lXG5cdFx0XHRcdFx0aW5kaWNlcy5zZXRYKCBpbmRleE9mZnNldCwgaTEgKTsgaW5kZXhPZmZzZXQgKys7XG5cdFx0XHRcdFx0aW5kaWNlcy5zZXRYKCBpbmRleE9mZnNldCwgaTIgKTsgaW5kZXhPZmZzZXQgKys7XG5cdFx0XHRcdFx0aW5kaWNlcy5zZXRYKCBpbmRleE9mZnNldCwgaTQgKTsgaW5kZXhPZmZzZXQgKys7XG5cblx0XHRcdFx0XHQvLyBmYWNlIHR3b1xuXHRcdFx0XHRcdGluZGljZXMuc2V0WCggaW5kZXhPZmZzZXQsIGkyICk7IGluZGV4T2Zmc2V0ICsrO1xuXHRcdFx0XHRcdGluZGljZXMuc2V0WCggaW5kZXhPZmZzZXQsIGkzICk7IGluZGV4T2Zmc2V0ICsrO1xuXHRcdFx0XHRcdGluZGljZXMuc2V0WCggaW5kZXhPZmZzZXQsIGk0ICk7IGluZGV4T2Zmc2V0ICsrO1xuXG5cdFx0XHRcdFx0Ly8gdXBkYXRlIGNvdW50ZXJzXG5cdFx0XHRcdFx0Z3JvdXBDb3VudCArPSA2O1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHQvLyBhZGQgYSBncm91cCB0byB0aGUgZ2VvbWV0cnkuIHRoaXMgd2lsbCBlbnN1cmUgbXVsdGkgbWF0ZXJpYWwgc3VwcG9ydFxuXHRcdFx0c2NvcGUuYWRkR3JvdXAoIGdyb3VwU3RhcnQsIGdyb3VwQ291bnQsIDAgKTtcblxuXHRcdFx0Ly8gY2FsY3VsYXRlIG5ldyBzdGFydCB2YWx1ZSBmb3IgZ3JvdXBzXG5cdFx0XHRncm91cFN0YXJ0ICs9IGdyb3VwQ291bnQ7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBnZW5lcmF0ZUNhcCggdG9wICkge1xuXG5cdFx0XHR2YXIgeCwgY2VudGVySW5kZXhTdGFydCwgY2VudGVySW5kZXhFbmQ7XG5cblx0XHRcdHZhciB1diA9IG5ldyBWZWN0b3IyKCk7XG5cdFx0XHR2YXIgdmVydGV4ID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0dmFyIGdyb3VwQ291bnQgPSAwO1xuXG5cdFx0XHR2YXIgcmFkaXVzID0gKCB0b3AgPT09IHRydWUgKSA/IHJhZGl1c1RvcCA6IHJhZGl1c0JvdHRvbTtcblx0XHRcdHZhciBzaWduID0gKCB0b3AgPT09IHRydWUgKSA/IDEgOiAtIDE7XG5cblx0XHRcdC8vIHNhdmUgdGhlIGluZGV4IG9mIHRoZSBmaXJzdCBjZW50ZXIgdmVydGV4XG5cdFx0XHRjZW50ZXJJbmRleFN0YXJ0ID0gaW5kZXg7XG5cblx0XHRcdC8vIGZpcnN0IHdlIGdlbmVyYXRlIHRoZSBjZW50ZXIgdmVydGV4IGRhdGEgb2YgdGhlIGNhcC5cblx0XHRcdC8vIGJlY2F1c2UgdGhlIGdlb21ldHJ5IG5lZWRzIG9uZSBzZXQgb2YgdXZzIHBlciBmYWNlLFxuXHRcdFx0Ly8gd2UgbXVzdCBnZW5lcmF0ZSBhIGNlbnRlciB2ZXJ0ZXggcGVyIGZhY2Uvc2VnbWVudFxuXG5cdFx0XHRmb3IgKCB4ID0gMTsgeCA8PSByYWRpYWxTZWdtZW50czsgeCArKyApIHtcblxuXHRcdFx0XHQvLyB2ZXJ0ZXhcblx0XHRcdFx0dmVydGljZXMuc2V0WFlaKCBpbmRleCwgMCwgaGFsZkhlaWdodCAqIHNpZ24sIDAgKTtcblxuXHRcdFx0XHQvLyBub3JtYWxcblx0XHRcdFx0bm9ybWFscy5zZXRYWVooIGluZGV4LCAwLCBzaWduLCAwICk7XG5cblx0XHRcdFx0Ly8gdXZcblx0XHRcdFx0dXYueCA9IDAuNTtcblx0XHRcdFx0dXYueSA9IDAuNTtcblxuXHRcdFx0XHR1dnMuc2V0WFkoIGluZGV4LCB1di54LCB1di55ICk7XG5cblx0XHRcdFx0Ly8gaW5jcmVhc2UgaW5kZXhcblx0XHRcdFx0aW5kZXggKys7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gc2F2ZSB0aGUgaW5kZXggb2YgdGhlIGxhc3QgY2VudGVyIHZlcnRleFxuXHRcdFx0Y2VudGVySW5kZXhFbmQgPSBpbmRleDtcblxuXHRcdFx0Ly8gbm93IHdlIGdlbmVyYXRlIHRoZSBzdXJyb3VuZGluZyB2ZXJ0aWNlcywgbm9ybWFscyBhbmQgdXZzXG5cblx0XHRcdGZvciAoIHggPSAwOyB4IDw9IHJhZGlhbFNlZ21lbnRzOyB4ICsrICkge1xuXG5cdFx0XHRcdHZhciB1ID0geCAvIHJhZGlhbFNlZ21lbnRzO1xuXHRcdFx0XHR2YXIgdGhldGEgPSB1ICogdGhldGFMZW5ndGggKyB0aGV0YVN0YXJ0O1xuXG5cdFx0XHRcdHZhciBjb3NUaGV0YSA9IE1hdGguY29zKCB0aGV0YSApO1xuXHRcdFx0XHR2YXIgc2luVGhldGEgPSBNYXRoLnNpbiggdGhldGEgKTtcblxuXHRcdFx0XHQvLyB2ZXJ0ZXhcblx0XHRcdFx0dmVydGV4LnggPSByYWRpdXMgKiBzaW5UaGV0YTtcblx0XHRcdFx0dmVydGV4LnkgPSBoYWxmSGVpZ2h0ICogc2lnbjtcblx0XHRcdFx0dmVydGV4LnogPSByYWRpdXMgKiBjb3NUaGV0YTtcblx0XHRcdFx0dmVydGljZXMuc2V0WFlaKCBpbmRleCwgdmVydGV4LngsIHZlcnRleC55LCB2ZXJ0ZXgueiApO1xuXG5cdFx0XHRcdC8vIG5vcm1hbFxuXHRcdFx0XHRub3JtYWxzLnNldFhZWiggaW5kZXgsIDAsIHNpZ24sIDAgKTtcblxuXHRcdFx0XHQvLyB1dlxuXHRcdFx0XHR1di54ID0gKCBjb3NUaGV0YSAqIDAuNSApICsgMC41O1xuXHRcdFx0XHR1di55ID0gKCBzaW5UaGV0YSAqIDAuNSAqIHNpZ24gKSArIDAuNTtcblx0XHRcdFx0dXZzLnNldFhZKCBpbmRleCwgdXYueCwgdXYueSApO1xuXG5cdFx0XHRcdC8vIGluY3JlYXNlIGluZGV4XG5cdFx0XHRcdGluZGV4ICsrO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIGdlbmVyYXRlIGluZGljZXNcblxuXHRcdFx0Zm9yICggeCA9IDA7IHggPCByYWRpYWxTZWdtZW50czsgeCArKyApIHtcblxuXHRcdFx0XHR2YXIgYyA9IGNlbnRlckluZGV4U3RhcnQgKyB4O1xuXHRcdFx0XHR2YXIgaSA9IGNlbnRlckluZGV4RW5kICsgeDtcblxuXHRcdFx0XHRpZiAoIHRvcCA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHRcdC8vIGZhY2UgdG9wXG5cdFx0XHRcdFx0aW5kaWNlcy5zZXRYKCBpbmRleE9mZnNldCwgaSApOyBpbmRleE9mZnNldCArKztcblx0XHRcdFx0XHRpbmRpY2VzLnNldFgoIGluZGV4T2Zmc2V0LCBpICsgMSApOyBpbmRleE9mZnNldCArKztcblx0XHRcdFx0XHRpbmRpY2VzLnNldFgoIGluZGV4T2Zmc2V0LCBjICk7IGluZGV4T2Zmc2V0ICsrO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHQvLyBmYWNlIGJvdHRvbVxuXHRcdFx0XHRcdGluZGljZXMuc2V0WCggaW5kZXhPZmZzZXQsIGkgKyAxICk7IGluZGV4T2Zmc2V0ICsrO1xuXHRcdFx0XHRcdGluZGljZXMuc2V0WCggaW5kZXhPZmZzZXQsIGkgKTsgaW5kZXhPZmZzZXQgKys7XG5cdFx0XHRcdFx0aW5kaWNlcy5zZXRYKCBpbmRleE9mZnNldCwgYyApOyBpbmRleE9mZnNldCArKztcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gdXBkYXRlIGNvdW50ZXJzXG5cdFx0XHRcdGdyb3VwQ291bnQgKz0gMztcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyBhZGQgYSBncm91cCB0byB0aGUgZ2VvbWV0cnkuIHRoaXMgd2lsbCBlbnN1cmUgbXVsdGkgbWF0ZXJpYWwgc3VwcG9ydFxuXHRcdFx0c2NvcGUuYWRkR3JvdXAoIGdyb3VwU3RhcnQsIGdyb3VwQ291bnQsIHRvcCA9PT0gdHJ1ZSA/IDEgOiAyICk7XG5cblx0XHRcdC8vIGNhbGN1bGF0ZSBuZXcgc3RhcnQgdmFsdWUgZm9yIGdyb3Vwc1xuXHRcdFx0Z3JvdXBTdGFydCArPSBncm91cENvdW50O1xuXG5cdFx0fVxuXG5cdH1cblxuXHRDeWxpbmRlckJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRDeWxpbmRlckJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IEN5bGluZGVyQnVmZmVyR2VvbWV0cnk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEN5bGluZGVyR2VvbWV0cnkoIHJhZGl1c1RvcCwgcmFkaXVzQm90dG9tLCBoZWlnaHQsIHJhZGlhbFNlZ21lbnRzLCBoZWlnaHRTZWdtZW50cywgb3BlbkVuZGVkLCB0aGV0YVN0YXJ0LCB0aGV0YUxlbmd0aCApIHtcblxuXHRcdEdlb21ldHJ5LmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdDeWxpbmRlckdlb21ldHJ5JztcblxuXHRcdHRoaXMucGFyYW1ldGVycyA9IHtcblx0XHRcdHJhZGl1c1RvcDogcmFkaXVzVG9wLFxuXHRcdFx0cmFkaXVzQm90dG9tOiByYWRpdXNCb3R0b20sXG5cdFx0XHRoZWlnaHQ6IGhlaWdodCxcblx0XHRcdHJhZGlhbFNlZ21lbnRzOiByYWRpYWxTZWdtZW50cyxcblx0XHRcdGhlaWdodFNlZ21lbnRzOiBoZWlnaHRTZWdtZW50cyxcblx0XHRcdG9wZW5FbmRlZDogb3BlbkVuZGVkLFxuXHRcdFx0dGhldGFTdGFydDogdGhldGFTdGFydCxcblx0XHRcdHRoZXRhTGVuZ3RoOiB0aGV0YUxlbmd0aFxuXHRcdH07XG5cblx0XHR0aGlzLmZyb21CdWZmZXJHZW9tZXRyeSggbmV3IEN5bGluZGVyQnVmZmVyR2VvbWV0cnkoIHJhZGl1c1RvcCwgcmFkaXVzQm90dG9tLCBoZWlnaHQsIHJhZGlhbFNlZ21lbnRzLCBoZWlnaHRTZWdtZW50cywgb3BlbkVuZGVkLCB0aGV0YVN0YXJ0LCB0aGV0YUxlbmd0aCApICk7XG5cdFx0dGhpcy5tZXJnZVZlcnRpY2VzKCk7XG5cblx0fVxuXG5cdEN5bGluZGVyR2VvbWV0cnkucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggR2VvbWV0cnkucHJvdG90eXBlICk7XG5cdEN5bGluZGVyR2VvbWV0cnkucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gQ3lsaW5kZXJHZW9tZXRyeTtcblxuXHQvKipcblx0ICogQGF1dGhvciBhYmVsbmF0aW9uIC8gaHR0cDovL2dpdGh1Yi5jb20vYWJlbG5hdGlvblxuXHQgKi9cblxuXHRmdW5jdGlvbiBDb25lR2VvbWV0cnkoIHJhZGl1cywgaGVpZ2h0LCByYWRpYWxTZWdtZW50cywgaGVpZ2h0U2VnbWVudHMsIG9wZW5FbmRlZCwgdGhldGFTdGFydCwgdGhldGFMZW5ndGggKSB7XG5cblx0XHRDeWxpbmRlckdlb21ldHJ5LmNhbGwoIHRoaXMsIDAsIHJhZGl1cywgaGVpZ2h0LCByYWRpYWxTZWdtZW50cywgaGVpZ2h0U2VnbWVudHMsIG9wZW5FbmRlZCwgdGhldGFTdGFydCwgdGhldGFMZW5ndGggKTtcblxuXHRcdHRoaXMudHlwZSA9ICdDb25lR2VvbWV0cnknO1xuXG5cdFx0dGhpcy5wYXJhbWV0ZXJzID0ge1xuXHRcdFx0cmFkaXVzOiByYWRpdXMsXG5cdFx0XHRoZWlnaHQ6IGhlaWdodCxcblx0XHRcdHJhZGlhbFNlZ21lbnRzOiByYWRpYWxTZWdtZW50cyxcblx0XHRcdGhlaWdodFNlZ21lbnRzOiBoZWlnaHRTZWdtZW50cyxcblx0XHRcdG9wZW5FbmRlZDogb3BlbkVuZGVkLFxuXHRcdFx0dGhldGFTdGFydDogdGhldGFTdGFydCxcblx0XHRcdHRoZXRhTGVuZ3RoOiB0aGV0YUxlbmd0aFxuXHRcdH07XG5cblx0fVxuXG5cdENvbmVHZW9tZXRyeS5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBDeWxpbmRlckdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRDb25lR2VvbWV0cnkucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gQ29uZUdlb21ldHJ5O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yOiBhYmVsbmF0aW9uIC8gaHR0cDovL2dpdGh1Yi5jb20vYWJlbG5hdGlvblxuXHQgKi9cblxuXHRmdW5jdGlvbiBDb25lQnVmZmVyR2VvbWV0cnkoIHJhZGl1cywgaGVpZ2h0LCByYWRpYWxTZWdtZW50cywgaGVpZ2h0U2VnbWVudHMsIG9wZW5FbmRlZCwgdGhldGFTdGFydCwgdGhldGFMZW5ndGggKSB7XG5cblx0XHRDeWxpbmRlckJ1ZmZlckdlb21ldHJ5LmNhbGwoIHRoaXMsIDAsIHJhZGl1cywgaGVpZ2h0LCByYWRpYWxTZWdtZW50cywgaGVpZ2h0U2VnbWVudHMsIG9wZW5FbmRlZCwgdGhldGFTdGFydCwgdGhldGFMZW5ndGggKTtcblxuXHRcdHRoaXMudHlwZSA9ICdDb25lQnVmZmVyR2VvbWV0cnknO1xuXG5cdFx0dGhpcy5wYXJhbWV0ZXJzID0ge1xuXHRcdFx0cmFkaXVzOiByYWRpdXMsXG5cdFx0XHRoZWlnaHQ6IGhlaWdodCxcblx0XHRcdHJhZGlhbFNlZ21lbnRzOiByYWRpYWxTZWdtZW50cyxcblx0XHRcdGhlaWdodFNlZ21lbnRzOiBoZWlnaHRTZWdtZW50cyxcblx0XHRcdG9wZW5FbmRlZDogb3BlbkVuZGVkLFxuXHRcdFx0dGhldGFTdGFydDogdGhldGFTdGFydCxcblx0XHRcdHRoZXRhTGVuZ3RoOiB0aGV0YUxlbmd0aFxuXHRcdH07XG5cblx0fVxuXG5cdENvbmVCdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBDeWxpbmRlckJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRDb25lQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gQ29uZUJ1ZmZlckdlb21ldHJ5O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGJlbmFhZGFtcyAvIGh0dHBzOi8vdHdpdHRlci5jb20vYmVuX2FfYWRhbXNcblx0ICovXG5cblx0ZnVuY3Rpb24gQ2lyY2xlQnVmZmVyR2VvbWV0cnkoIHJhZGl1cywgc2VnbWVudHMsIHRoZXRhU3RhcnQsIHRoZXRhTGVuZ3RoICkge1xuXG5cdFx0QnVmZmVyR2VvbWV0cnkuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ0NpcmNsZUJ1ZmZlckdlb21ldHJ5JztcblxuXHRcdHRoaXMucGFyYW1ldGVycyA9IHtcblx0XHRcdHJhZGl1czogcmFkaXVzLFxuXHRcdFx0c2VnbWVudHM6IHNlZ21lbnRzLFxuXHRcdFx0dGhldGFTdGFydDogdGhldGFTdGFydCxcblx0XHRcdHRoZXRhTGVuZ3RoOiB0aGV0YUxlbmd0aFxuXHRcdH07XG5cblx0XHRyYWRpdXMgPSByYWRpdXMgfHwgNTA7XG5cdFx0c2VnbWVudHMgPSBzZWdtZW50cyAhPT0gdW5kZWZpbmVkID8gTWF0aC5tYXgoIDMsIHNlZ21lbnRzICkgOiA4O1xuXG5cdFx0dGhldGFTdGFydCA9IHRoZXRhU3RhcnQgIT09IHVuZGVmaW5lZCA/IHRoZXRhU3RhcnQgOiAwO1xuXHRcdHRoZXRhTGVuZ3RoID0gdGhldGFMZW5ndGggIT09IHVuZGVmaW5lZCA/IHRoZXRhTGVuZ3RoIDogTWF0aC5QSSAqIDI7XG5cblx0XHR2YXIgdmVydGljZXMgPSBzZWdtZW50cyArIDI7XG5cblx0XHR2YXIgcG9zaXRpb25zID0gbmV3IEZsb2F0MzJBcnJheSggdmVydGljZXMgKiAzICk7XG5cdFx0dmFyIG5vcm1hbHMgPSBuZXcgRmxvYXQzMkFycmF5KCB2ZXJ0aWNlcyAqIDMgKTtcblx0XHR2YXIgdXZzID0gbmV3IEZsb2F0MzJBcnJheSggdmVydGljZXMgKiAyICk7XG5cblx0XHQvLyBjZW50ZXIgZGF0YSBpcyBhbHJlYWR5IHplcm8sIGJ1dCBuZWVkIHRvIHNldCBhIGZldyBleHRyYXNcblx0XHRub3JtYWxzWyAyIF0gPSAxLjA7XG5cdFx0dXZzWyAwIF0gPSAwLjU7XG5cdFx0dXZzWyAxIF0gPSAwLjU7XG5cblx0XHRmb3IgKCB2YXIgcyA9IDAsIGkgPSAzLCBpaSA9IDIgOyBzIDw9IHNlZ21lbnRzOyBzICsrLCBpICs9IDMsIGlpICs9IDIgKSB7XG5cblx0XHRcdHZhciBzZWdtZW50ID0gdGhldGFTdGFydCArIHMgLyBzZWdtZW50cyAqIHRoZXRhTGVuZ3RoO1xuXG5cdFx0XHRwb3NpdGlvbnNbIGkgXSA9IHJhZGl1cyAqIE1hdGguY29zKCBzZWdtZW50ICk7XG5cdFx0XHRwb3NpdGlvbnNbIGkgKyAxIF0gPSByYWRpdXMgKiBNYXRoLnNpbiggc2VnbWVudCApO1xuXG5cdFx0XHRub3JtYWxzWyBpICsgMiBdID0gMTsgLy8gbm9ybWFsIHpcblxuXHRcdFx0dXZzWyBpaSBdID0gKCBwb3NpdGlvbnNbIGkgXSAvIHJhZGl1cyArIDEgKSAvIDI7XG5cdFx0XHR1dnNbIGlpICsgMSBdID0gKCBwb3NpdGlvbnNbIGkgKyAxIF0gLyByYWRpdXMgKyAxICkgLyAyO1xuXG5cdFx0fVxuXG5cdFx0dmFyIGluZGljZXMgPSBbXTtcblxuXHRcdGZvciAoIHZhciBpID0gMTsgaSA8PSBzZWdtZW50czsgaSArKyApIHtcblxuXHRcdFx0aW5kaWNlcy5wdXNoKCBpLCBpICsgMSwgMCApO1xuXG5cdFx0fVxuXG5cdFx0dGhpcy5zZXRJbmRleCggbmV3IEJ1ZmZlckF0dHJpYnV0ZSggbmV3IFVpbnQxNkFycmF5KCBpbmRpY2VzICksIDEgKSApO1xuXHRcdHRoaXMuYWRkQXR0cmlidXRlKCAncG9zaXRpb24nLCBuZXcgQnVmZmVyQXR0cmlidXRlKCBwb3NpdGlvbnMsIDMgKSApO1xuXHRcdHRoaXMuYWRkQXR0cmlidXRlKCAnbm9ybWFsJywgbmV3IEJ1ZmZlckF0dHJpYnV0ZSggbm9ybWFscywgMyApICk7XG5cdFx0dGhpcy5hZGRBdHRyaWJ1dGUoICd1dicsIG5ldyBCdWZmZXJBdHRyaWJ1dGUoIHV2cywgMiApICk7XG5cblx0XHR0aGlzLmJvdW5kaW5nU3BoZXJlID0gbmV3IFNwaGVyZSggbmV3IFZlY3RvcjMoKSwgcmFkaXVzICk7XG5cblx0fVxuXG5cdENpcmNsZUJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRDaXJjbGVCdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBDaXJjbGVCdWZmZXJHZW9tZXRyeTtcblxuXHQvKipcblx0ICogQGF1dGhvciBodWdoZXNcblx0ICovXG5cblx0ZnVuY3Rpb24gQ2lyY2xlR2VvbWV0cnkoIHJhZGl1cywgc2VnbWVudHMsIHRoZXRhU3RhcnQsIHRoZXRhTGVuZ3RoICkge1xuXG5cdFx0R2VvbWV0cnkuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ0NpcmNsZUdlb21ldHJ5JztcblxuXHRcdHRoaXMucGFyYW1ldGVycyA9IHtcblx0XHRcdHJhZGl1czogcmFkaXVzLFxuXHRcdFx0c2VnbWVudHM6IHNlZ21lbnRzLFxuXHRcdFx0dGhldGFTdGFydDogdGhldGFTdGFydCxcblx0XHRcdHRoZXRhTGVuZ3RoOiB0aGV0YUxlbmd0aFxuXHRcdH07XG5cblx0XHR0aGlzLmZyb21CdWZmZXJHZW9tZXRyeSggbmV3IENpcmNsZUJ1ZmZlckdlb21ldHJ5KCByYWRpdXMsIHNlZ21lbnRzLCB0aGV0YVN0YXJ0LCB0aGV0YUxlbmd0aCApICk7XG5cblx0fVxuXG5cdENpcmNsZUdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRDaXJjbGVHZW9tZXRyeS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBDaXJjbGVHZW9tZXRyeTtcblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICogYmFzZWQgb24gaHR0cDovL3BhcGVydmlzaW9uM2QuZ29vZ2xlY29kZS5jb20vc3ZuL3RydW5rL2FzMy90cnVuay9zcmMvb3JnL3BhcGVydmlzaW9uM2Qvb2JqZWN0cy9wcmltaXRpdmVzL0N1YmUuYXNcblx0ICovXG5cblx0ZnVuY3Rpb24gQm94R2VvbWV0cnkoIHdpZHRoLCBoZWlnaHQsIGRlcHRoLCB3aWR0aFNlZ21lbnRzLCBoZWlnaHRTZWdtZW50cywgZGVwdGhTZWdtZW50cyApIHtcblxuXHRcdEdlb21ldHJ5LmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdCb3hHZW9tZXRyeSc7XG5cblx0XHR0aGlzLnBhcmFtZXRlcnMgPSB7XG5cdFx0XHR3aWR0aDogd2lkdGgsXG5cdFx0XHRoZWlnaHQ6IGhlaWdodCxcblx0XHRcdGRlcHRoOiBkZXB0aCxcblx0XHRcdHdpZHRoU2VnbWVudHM6IHdpZHRoU2VnbWVudHMsXG5cdFx0XHRoZWlnaHRTZWdtZW50czogaGVpZ2h0U2VnbWVudHMsXG5cdFx0XHRkZXB0aFNlZ21lbnRzOiBkZXB0aFNlZ21lbnRzXG5cdFx0fTtcblxuXHRcdHRoaXMuZnJvbUJ1ZmZlckdlb21ldHJ5KCBuZXcgQm94QnVmZmVyR2VvbWV0cnkoIHdpZHRoLCBoZWlnaHQsIGRlcHRoLCB3aWR0aFNlZ21lbnRzLCBoZWlnaHRTZWdtZW50cywgZGVwdGhTZWdtZW50cyApICk7XG5cdFx0dGhpcy5tZXJnZVZlcnRpY2VzKCk7XG5cblx0fVxuXG5cdEJveEdlb21ldHJ5LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEdlb21ldHJ5LnByb3RvdHlwZSApO1xuXHRCb3hHZW9tZXRyeS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBCb3hHZW9tZXRyeTtcblxuXG5cblx0dmFyIEdlb21ldHJpZXMgPSBPYmplY3QuZnJlZXplKHtcblx0XHRXaXJlZnJhbWVHZW9tZXRyeTogV2lyZWZyYW1lR2VvbWV0cnksXG5cdFx0UGFyYW1ldHJpY0dlb21ldHJ5OiBQYXJhbWV0cmljR2VvbWV0cnksXG5cdFx0UGFyYW1ldHJpY0J1ZmZlckdlb21ldHJ5OiBQYXJhbWV0cmljQnVmZmVyR2VvbWV0cnksXG5cdFx0VGV0cmFoZWRyb25HZW9tZXRyeTogVGV0cmFoZWRyb25HZW9tZXRyeSxcblx0XHRUZXRyYWhlZHJvbkJ1ZmZlckdlb21ldHJ5OiBUZXRyYWhlZHJvbkJ1ZmZlckdlb21ldHJ5LFxuXHRcdE9jdGFoZWRyb25HZW9tZXRyeTogT2N0YWhlZHJvbkdlb21ldHJ5LFxuXHRcdE9jdGFoZWRyb25CdWZmZXJHZW9tZXRyeTogT2N0YWhlZHJvbkJ1ZmZlckdlb21ldHJ5LFxuXHRcdEljb3NhaGVkcm9uR2VvbWV0cnk6IEljb3NhaGVkcm9uR2VvbWV0cnksXG5cdFx0SWNvc2FoZWRyb25CdWZmZXJHZW9tZXRyeTogSWNvc2FoZWRyb25CdWZmZXJHZW9tZXRyeSxcblx0XHREb2RlY2FoZWRyb25HZW9tZXRyeTogRG9kZWNhaGVkcm9uR2VvbWV0cnksXG5cdFx0RG9kZWNhaGVkcm9uQnVmZmVyR2VvbWV0cnk6IERvZGVjYWhlZHJvbkJ1ZmZlckdlb21ldHJ5LFxuXHRcdFBvbHloZWRyb25HZW9tZXRyeTogUG9seWhlZHJvbkdlb21ldHJ5LFxuXHRcdFBvbHloZWRyb25CdWZmZXJHZW9tZXRyeTogUG9seWhlZHJvbkJ1ZmZlckdlb21ldHJ5LFxuXHRcdFR1YmVHZW9tZXRyeTogVHViZUdlb21ldHJ5LFxuXHRcdFR1YmVCdWZmZXJHZW9tZXRyeTogVHViZUJ1ZmZlckdlb21ldHJ5LFxuXHRcdFRvcnVzS25vdEdlb21ldHJ5OiBUb3J1c0tub3RHZW9tZXRyeSxcblx0XHRUb3J1c0tub3RCdWZmZXJHZW9tZXRyeTogVG9ydXNLbm90QnVmZmVyR2VvbWV0cnksXG5cdFx0VG9ydXNHZW9tZXRyeTogVG9ydXNHZW9tZXRyeSxcblx0XHRUb3J1c0J1ZmZlckdlb21ldHJ5OiBUb3J1c0J1ZmZlckdlb21ldHJ5LFxuXHRcdFRleHRHZW9tZXRyeTogVGV4dEdlb21ldHJ5LFxuXHRcdFNwaGVyZUJ1ZmZlckdlb21ldHJ5OiBTcGhlcmVCdWZmZXJHZW9tZXRyeSxcblx0XHRTcGhlcmVHZW9tZXRyeTogU3BoZXJlR2VvbWV0cnksXG5cdFx0UmluZ0dlb21ldHJ5OiBSaW5nR2VvbWV0cnksXG5cdFx0UmluZ0J1ZmZlckdlb21ldHJ5OiBSaW5nQnVmZmVyR2VvbWV0cnksXG5cdFx0UGxhbmVCdWZmZXJHZW9tZXRyeTogUGxhbmVCdWZmZXJHZW9tZXRyeSxcblx0XHRQbGFuZUdlb21ldHJ5OiBQbGFuZUdlb21ldHJ5LFxuXHRcdExhdGhlR2VvbWV0cnk6IExhdGhlR2VvbWV0cnksXG5cdFx0TGF0aGVCdWZmZXJHZW9tZXRyeTogTGF0aGVCdWZmZXJHZW9tZXRyeSxcblx0XHRTaGFwZUdlb21ldHJ5OiBTaGFwZUdlb21ldHJ5LFxuXHRcdFNoYXBlQnVmZmVyR2VvbWV0cnk6IFNoYXBlQnVmZmVyR2VvbWV0cnksXG5cdFx0RXh0cnVkZUdlb21ldHJ5OiBFeHRydWRlR2VvbWV0cnksXG5cdFx0RWRnZXNHZW9tZXRyeTogRWRnZXNHZW9tZXRyeSxcblx0XHRDb25lR2VvbWV0cnk6IENvbmVHZW9tZXRyeSxcblx0XHRDb25lQnVmZmVyR2VvbWV0cnk6IENvbmVCdWZmZXJHZW9tZXRyeSxcblx0XHRDeWxpbmRlckdlb21ldHJ5OiBDeWxpbmRlckdlb21ldHJ5LFxuXHRcdEN5bGluZGVyQnVmZmVyR2VvbWV0cnk6IEN5bGluZGVyQnVmZmVyR2VvbWV0cnksXG5cdFx0Q2lyY2xlQnVmZmVyR2VvbWV0cnk6IENpcmNsZUJ1ZmZlckdlb21ldHJ5LFxuXHRcdENpcmNsZUdlb21ldHJ5OiBDaXJjbGVHZW9tZXRyeSxcblx0XHRCb3hCdWZmZXJHZW9tZXRyeTogQm94QnVmZmVyR2VvbWV0cnksXG5cdFx0Qm94R2VvbWV0cnk6IEJveEdlb21ldHJ5XG5cdH0pO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBTaGFkb3dNYXRlcmlhbCgpIHtcblxuXHRcdFNoYWRlck1hdGVyaWFsLmNhbGwoIHRoaXMsIHtcblx0XHRcdHVuaWZvcm1zOiBVbmlmb3Jtc1V0aWxzLm1lcmdlKCBbXG5cdFx0XHRcdFVuaWZvcm1zTGliLmxpZ2h0cyxcblx0XHRcdFx0e1xuXHRcdFx0XHRcdG9wYWNpdHk6IHsgdmFsdWU6IDEuMCB9XG5cdFx0XHRcdH1cblx0XHRcdF0gKSxcblx0XHRcdHZlcnRleFNoYWRlcjogU2hhZGVyQ2h1bmtbICdzaGFkb3dfdmVydCcgXSxcblx0XHRcdGZyYWdtZW50U2hhZGVyOiBTaGFkZXJDaHVua1sgJ3NoYWRvd19mcmFnJyBdXG5cdFx0fSApO1xuXG5cdFx0dGhpcy5saWdodHMgPSB0cnVlO1xuXHRcdHRoaXMudHJhbnNwYXJlbnQgPSB0cnVlO1xuXG5cdFx0T2JqZWN0LmRlZmluZVByb3BlcnRpZXMoIHRoaXMsIHtcblx0XHRcdG9wYWNpdHk6IHtcblx0XHRcdFx0ZW51bWVyYWJsZTogdHJ1ZSxcblx0XHRcdFx0Z2V0OiBmdW5jdGlvbiAoKSB7XG5cdFx0XHRcdFx0cmV0dXJuIHRoaXMudW5pZm9ybXMub3BhY2l0eS52YWx1ZTtcblx0XHRcdFx0fSxcblx0XHRcdFx0c2V0OiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXHRcdFx0XHRcdHRoaXMudW5pZm9ybXMub3BhY2l0eS52YWx1ZSA9IHZhbHVlO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fSApO1xuXG5cdH1cblxuXHRTaGFkb3dNYXRlcmlhbC5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBTaGFkZXJNYXRlcmlhbC5wcm90b3R5cGUgKTtcblx0U2hhZG93TWF0ZXJpYWwucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gU2hhZG93TWF0ZXJpYWw7XG5cblx0U2hhZG93TWF0ZXJpYWwucHJvdG90eXBlLmlzU2hhZG93TWF0ZXJpYWwgPSB0cnVlO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBSYXdTaGFkZXJNYXRlcmlhbCggcGFyYW1ldGVycyApIHtcblxuXHRcdFNoYWRlck1hdGVyaWFsLmNhbGwoIHRoaXMsIHBhcmFtZXRlcnMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdSYXdTaGFkZXJNYXRlcmlhbCc7XG5cblx0fVxuXG5cdFJhd1NoYWRlck1hdGVyaWFsLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIFNoYWRlck1hdGVyaWFsLnByb3RvdHlwZSApO1xuXHRSYXdTaGFkZXJNYXRlcmlhbC5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBSYXdTaGFkZXJNYXRlcmlhbDtcblxuXHRSYXdTaGFkZXJNYXRlcmlhbC5wcm90b3R5cGUuaXNSYXdTaGFkZXJNYXRlcmlhbCA9IHRydWU7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIE11bHRpTWF0ZXJpYWwoIG1hdGVyaWFscyApIHtcblxuXHRcdHRoaXMudXVpZCA9IF9NYXRoLmdlbmVyYXRlVVVJRCgpO1xuXG5cdFx0dGhpcy50eXBlID0gJ011bHRpTWF0ZXJpYWwnO1xuXG5cdFx0dGhpcy5tYXRlcmlhbHMgPSBBcnJheS5pc0FycmF5KCBtYXRlcmlhbHMgKSA/IG1hdGVyaWFscyA6IFtdO1xuXG5cdFx0dGhpcy52aXNpYmxlID0gdHJ1ZTtcblxuXHR9XG5cblx0TXVsdGlNYXRlcmlhbC5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogTXVsdGlNYXRlcmlhbCxcblxuXHRcdGlzTXVsdGlNYXRlcmlhbDogdHJ1ZSxcblxuXHRcdHRvSlNPTjogZnVuY3Rpb24gKCBtZXRhICkge1xuXG5cdFx0XHR2YXIgb3V0cHV0ID0ge1xuXHRcdFx0XHRtZXRhZGF0YToge1xuXHRcdFx0XHRcdHZlcnNpb246IDQuMixcblx0XHRcdFx0XHR0eXBlOiAnbWF0ZXJpYWwnLFxuXHRcdFx0XHRcdGdlbmVyYXRvcjogJ01hdGVyaWFsRXhwb3J0ZXInXG5cdFx0XHRcdH0sXG5cdFx0XHRcdHV1aWQ6IHRoaXMudXVpZCxcblx0XHRcdFx0dHlwZTogdGhpcy50eXBlLFxuXHRcdFx0XHRtYXRlcmlhbHM6IFtdXG5cdFx0XHR9O1xuXG5cdFx0XHR2YXIgbWF0ZXJpYWxzID0gdGhpcy5tYXRlcmlhbHM7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IG1hdGVyaWFscy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdHZhciBtYXRlcmlhbCA9IG1hdGVyaWFsc1sgaSBdLnRvSlNPTiggbWV0YSApO1xuXHRcdFx0XHRkZWxldGUgbWF0ZXJpYWwubWV0YWRhdGE7XG5cblx0XHRcdFx0b3V0cHV0Lm1hdGVyaWFscy5wdXNoKCBtYXRlcmlhbCApO1xuXG5cdFx0XHR9XG5cblx0XHRcdG91dHB1dC52aXNpYmxlID0gdGhpcy52aXNpYmxlO1xuXG5cdFx0XHRyZXR1cm4gb3V0cHV0O1xuXG5cdFx0fSxcblxuXHRcdGNsb25lOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBtYXRlcmlhbCA9IG5ldyB0aGlzLmNvbnN0cnVjdG9yKCk7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IHRoaXMubWF0ZXJpYWxzLmxlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHRtYXRlcmlhbC5tYXRlcmlhbHMucHVzaCggdGhpcy5tYXRlcmlhbHNbIGkgXS5jbG9uZSgpICk7XG5cblx0XHRcdH1cblxuXHRcdFx0bWF0ZXJpYWwudmlzaWJsZSA9IHRoaXMudmlzaWJsZTtcblxuXHRcdFx0cmV0dXJuIG1hdGVyaWFsO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgV2VzdExhbmdsZXkgLyBodHRwOi8vZ2l0aHViLmNvbS9XZXN0TGFuZ2xleVxuXHQgKlxuXHQgKiBwYXJhbWV0ZXJzID0ge1xuXHQgKiAgY29sb3I6IDxoZXg+LFxuXHQgKiAgcm91Z2huZXNzOiA8ZmxvYXQ+LFxuXHQgKiAgbWV0YWxuZXNzOiA8ZmxvYXQ+LFxuXHQgKiAgb3BhY2l0eTogPGZsb2F0Pixcblx0ICpcblx0ICogIG1hcDogbmV3IFRIUkVFLlRleHR1cmUoIDxJbWFnZT4gKSxcblx0ICpcblx0ICogIGxpZ2h0TWFwOiBuZXcgVEhSRUUuVGV4dHVyZSggPEltYWdlPiApLFxuXHQgKiAgbGlnaHRNYXBJbnRlbnNpdHk6IDxmbG9hdD5cblx0ICpcblx0ICogIGFvTWFwOiBuZXcgVEhSRUUuVGV4dHVyZSggPEltYWdlPiApLFxuXHQgKiAgYW9NYXBJbnRlbnNpdHk6IDxmbG9hdD5cblx0ICpcblx0ICogIGVtaXNzaXZlOiA8aGV4Pixcblx0ICogIGVtaXNzaXZlSW50ZW5zaXR5OiA8ZmxvYXQ+XG5cdCAqICBlbWlzc2l2ZU1hcDogbmV3IFRIUkVFLlRleHR1cmUoIDxJbWFnZT4gKSxcblx0ICpcblx0ICogIGJ1bXBNYXA6IG5ldyBUSFJFRS5UZXh0dXJlKCA8SW1hZ2U+ICksXG5cdCAqICBidW1wU2NhbGU6IDxmbG9hdD4sXG5cdCAqXG5cdCAqICBub3JtYWxNYXA6IG5ldyBUSFJFRS5UZXh0dXJlKCA8SW1hZ2U+ICksXG5cdCAqICBub3JtYWxTY2FsZTogPFZlY3RvcjI+LFxuXHQgKlxuXHQgKiAgZGlzcGxhY2VtZW50TWFwOiBuZXcgVEhSRUUuVGV4dHVyZSggPEltYWdlPiApLFxuXHQgKiAgZGlzcGxhY2VtZW50U2NhbGU6IDxmbG9hdD4sXG5cdCAqICBkaXNwbGFjZW1lbnRCaWFzOiA8ZmxvYXQ+LFxuXHQgKlxuXHQgKiAgcm91Z2huZXNzTWFwOiBuZXcgVEhSRUUuVGV4dHVyZSggPEltYWdlPiApLFxuXHQgKlxuXHQgKiAgbWV0YWxuZXNzTWFwOiBuZXcgVEhSRUUuVGV4dHVyZSggPEltYWdlPiApLFxuXHQgKlxuXHQgKiAgYWxwaGFNYXA6IG5ldyBUSFJFRS5UZXh0dXJlKCA8SW1hZ2U+ICksXG5cdCAqXG5cdCAqICBlbnZNYXA6IG5ldyBUSFJFRS5DdWJlVGV4dHVyZSggW3Bvc3gsIG5lZ3gsIHBvc3ksIG5lZ3ksIHBvc3osIG5lZ3pdICksXG5cdCAqICBlbnZNYXBJbnRlbnNpdHk6IDxmbG9hdD5cblx0ICpcblx0ICogIHJlZnJhY3Rpb25SYXRpbzogPGZsb2F0Pixcblx0ICpcblx0ICogIHdpcmVmcmFtZTogPGJvb2xlYW4+LFxuXHQgKiAgd2lyZWZyYW1lTGluZXdpZHRoOiA8ZmxvYXQ+LFxuXHQgKlxuXHQgKiAgc2tpbm5pbmc6IDxib29sPixcblx0ICogIG1vcnBoVGFyZ2V0czogPGJvb2w+LFxuXHQgKiAgbW9ycGhOb3JtYWxzOiA8Ym9vbD5cblx0ICogfVxuXHQgKi9cblxuXHRmdW5jdGlvbiBNZXNoU3RhbmRhcmRNYXRlcmlhbCggcGFyYW1ldGVycyApIHtcblxuXHRcdE1hdGVyaWFsLmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMuZGVmaW5lcyA9IHsgJ1NUQU5EQVJEJzogJycgfTtcblxuXHRcdHRoaXMudHlwZSA9ICdNZXNoU3RhbmRhcmRNYXRlcmlhbCc7XG5cblx0XHR0aGlzLmNvbG9yID0gbmV3IENvbG9yKCAweGZmZmZmZiApOyAvLyBkaWZmdXNlXG5cdFx0dGhpcy5yb3VnaG5lc3MgPSAwLjU7XG5cdFx0dGhpcy5tZXRhbG5lc3MgPSAwLjU7XG5cblx0XHR0aGlzLm1hcCA9IG51bGw7XG5cblx0XHR0aGlzLmxpZ2h0TWFwID0gbnVsbDtcblx0XHR0aGlzLmxpZ2h0TWFwSW50ZW5zaXR5ID0gMS4wO1xuXG5cdFx0dGhpcy5hb01hcCA9IG51bGw7XG5cdFx0dGhpcy5hb01hcEludGVuc2l0eSA9IDEuMDtcblxuXHRcdHRoaXMuZW1pc3NpdmUgPSBuZXcgQ29sb3IoIDB4MDAwMDAwICk7XG5cdFx0dGhpcy5lbWlzc2l2ZUludGVuc2l0eSA9IDEuMDtcblx0XHR0aGlzLmVtaXNzaXZlTWFwID0gbnVsbDtcblxuXHRcdHRoaXMuYnVtcE1hcCA9IG51bGw7XG5cdFx0dGhpcy5idW1wU2NhbGUgPSAxO1xuXG5cdFx0dGhpcy5ub3JtYWxNYXAgPSBudWxsO1xuXHRcdHRoaXMubm9ybWFsU2NhbGUgPSBuZXcgVmVjdG9yMiggMSwgMSApO1xuXG5cdFx0dGhpcy5kaXNwbGFjZW1lbnRNYXAgPSBudWxsO1xuXHRcdHRoaXMuZGlzcGxhY2VtZW50U2NhbGUgPSAxO1xuXHRcdHRoaXMuZGlzcGxhY2VtZW50QmlhcyA9IDA7XG5cblx0XHR0aGlzLnJvdWdobmVzc01hcCA9IG51bGw7XG5cblx0XHR0aGlzLm1ldGFsbmVzc01hcCA9IG51bGw7XG5cblx0XHR0aGlzLmFscGhhTWFwID0gbnVsbDtcblxuXHRcdHRoaXMuZW52TWFwID0gbnVsbDtcblx0XHR0aGlzLmVudk1hcEludGVuc2l0eSA9IDEuMDtcblxuXHRcdHRoaXMucmVmcmFjdGlvblJhdGlvID0gMC45ODtcblxuXHRcdHRoaXMud2lyZWZyYW1lID0gZmFsc2U7XG5cdFx0dGhpcy53aXJlZnJhbWVMaW5ld2lkdGggPSAxO1xuXHRcdHRoaXMud2lyZWZyYW1lTGluZWNhcCA9ICdyb3VuZCc7XG5cdFx0dGhpcy53aXJlZnJhbWVMaW5lam9pbiA9ICdyb3VuZCc7XG5cblx0XHR0aGlzLnNraW5uaW5nID0gZmFsc2U7XG5cdFx0dGhpcy5tb3JwaFRhcmdldHMgPSBmYWxzZTtcblx0XHR0aGlzLm1vcnBoTm9ybWFscyA9IGZhbHNlO1xuXG5cdFx0dGhpcy5zZXRWYWx1ZXMoIHBhcmFtZXRlcnMgKTtcblxuXHR9XG5cblx0TWVzaFN0YW5kYXJkTWF0ZXJpYWwucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggTWF0ZXJpYWwucHJvdG90eXBlICk7XG5cdE1lc2hTdGFuZGFyZE1hdGVyaWFsLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IE1lc2hTdGFuZGFyZE1hdGVyaWFsO1xuXG5cdE1lc2hTdGFuZGFyZE1hdGVyaWFsLnByb3RvdHlwZS5pc01lc2hTdGFuZGFyZE1hdGVyaWFsID0gdHJ1ZTtcblxuXHRNZXNoU3RhbmRhcmRNYXRlcmlhbC5wcm90b3R5cGUuY29weSA9IGZ1bmN0aW9uICggc291cmNlICkge1xuXG5cdFx0TWF0ZXJpYWwucHJvdG90eXBlLmNvcHkuY2FsbCggdGhpcywgc291cmNlICk7XG5cblx0XHR0aGlzLmRlZmluZXMgPSB7ICdTVEFOREFSRCc6ICcnIH07XG5cblx0XHR0aGlzLmNvbG9yLmNvcHkoIHNvdXJjZS5jb2xvciApO1xuXHRcdHRoaXMucm91Z2huZXNzID0gc291cmNlLnJvdWdobmVzcztcblx0XHR0aGlzLm1ldGFsbmVzcyA9IHNvdXJjZS5tZXRhbG5lc3M7XG5cblx0XHR0aGlzLm1hcCA9IHNvdXJjZS5tYXA7XG5cblx0XHR0aGlzLmxpZ2h0TWFwID0gc291cmNlLmxpZ2h0TWFwO1xuXHRcdHRoaXMubGlnaHRNYXBJbnRlbnNpdHkgPSBzb3VyY2UubGlnaHRNYXBJbnRlbnNpdHk7XG5cblx0XHR0aGlzLmFvTWFwID0gc291cmNlLmFvTWFwO1xuXHRcdHRoaXMuYW9NYXBJbnRlbnNpdHkgPSBzb3VyY2UuYW9NYXBJbnRlbnNpdHk7XG5cblx0XHR0aGlzLmVtaXNzaXZlLmNvcHkoIHNvdXJjZS5lbWlzc2l2ZSApO1xuXHRcdHRoaXMuZW1pc3NpdmVNYXAgPSBzb3VyY2UuZW1pc3NpdmVNYXA7XG5cdFx0dGhpcy5lbWlzc2l2ZUludGVuc2l0eSA9IHNvdXJjZS5lbWlzc2l2ZUludGVuc2l0eTtcblxuXHRcdHRoaXMuYnVtcE1hcCA9IHNvdXJjZS5idW1wTWFwO1xuXHRcdHRoaXMuYnVtcFNjYWxlID0gc291cmNlLmJ1bXBTY2FsZTtcblxuXHRcdHRoaXMubm9ybWFsTWFwID0gc291cmNlLm5vcm1hbE1hcDtcblx0XHR0aGlzLm5vcm1hbFNjYWxlLmNvcHkoIHNvdXJjZS5ub3JtYWxTY2FsZSApO1xuXG5cdFx0dGhpcy5kaXNwbGFjZW1lbnRNYXAgPSBzb3VyY2UuZGlzcGxhY2VtZW50TWFwO1xuXHRcdHRoaXMuZGlzcGxhY2VtZW50U2NhbGUgPSBzb3VyY2UuZGlzcGxhY2VtZW50U2NhbGU7XG5cdFx0dGhpcy5kaXNwbGFjZW1lbnRCaWFzID0gc291cmNlLmRpc3BsYWNlbWVudEJpYXM7XG5cblx0XHR0aGlzLnJvdWdobmVzc01hcCA9IHNvdXJjZS5yb3VnaG5lc3NNYXA7XG5cblx0XHR0aGlzLm1ldGFsbmVzc01hcCA9IHNvdXJjZS5tZXRhbG5lc3NNYXA7XG5cblx0XHR0aGlzLmFscGhhTWFwID0gc291cmNlLmFscGhhTWFwO1xuXG5cdFx0dGhpcy5lbnZNYXAgPSBzb3VyY2UuZW52TWFwO1xuXHRcdHRoaXMuZW52TWFwSW50ZW5zaXR5ID0gc291cmNlLmVudk1hcEludGVuc2l0eTtcblxuXHRcdHRoaXMucmVmcmFjdGlvblJhdGlvID0gc291cmNlLnJlZnJhY3Rpb25SYXRpbztcblxuXHRcdHRoaXMud2lyZWZyYW1lID0gc291cmNlLndpcmVmcmFtZTtcblx0XHR0aGlzLndpcmVmcmFtZUxpbmV3aWR0aCA9IHNvdXJjZS53aXJlZnJhbWVMaW5ld2lkdGg7XG5cdFx0dGhpcy53aXJlZnJhbWVMaW5lY2FwID0gc291cmNlLndpcmVmcmFtZUxpbmVjYXA7XG5cdFx0dGhpcy53aXJlZnJhbWVMaW5lam9pbiA9IHNvdXJjZS53aXJlZnJhbWVMaW5lam9pbjtcblxuXHRcdHRoaXMuc2tpbm5pbmcgPSBzb3VyY2Uuc2tpbm5pbmc7XG5cdFx0dGhpcy5tb3JwaFRhcmdldHMgPSBzb3VyY2UubW9ycGhUYXJnZXRzO1xuXHRcdHRoaXMubW9ycGhOb3JtYWxzID0gc291cmNlLm1vcnBoTm9ybWFscztcblxuXHRcdHJldHVybiB0aGlzO1xuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgV2VzdExhbmdsZXkgLyBodHRwOi8vZ2l0aHViLmNvbS9XZXN0TGFuZ2xleVxuXHQgKlxuXHQgKiBwYXJhbWV0ZXJzID0ge1xuXHQgKiAgcmVmbGVjdGl2aXR5OiA8ZmxvYXQ+XG5cdCAqIH1cblx0ICovXG5cblx0ZnVuY3Rpb24gTWVzaFBoeXNpY2FsTWF0ZXJpYWwoIHBhcmFtZXRlcnMgKSB7XG5cblx0XHRNZXNoU3RhbmRhcmRNYXRlcmlhbC5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLmRlZmluZXMgPSB7ICdQSFlTSUNBTCc6ICcnIH07XG5cblx0XHR0aGlzLnR5cGUgPSAnTWVzaFBoeXNpY2FsTWF0ZXJpYWwnO1xuXG5cdFx0dGhpcy5yZWZsZWN0aXZpdHkgPSAwLjU7IC8vIG1hcHMgdG8gRjAgPSAwLjA0XG5cblx0XHR0aGlzLmNsZWFyQ29hdCA9IDAuMDtcblx0XHR0aGlzLmNsZWFyQ29hdFJvdWdobmVzcyA9IDAuMDtcblxuXHRcdHRoaXMuc2V0VmFsdWVzKCBwYXJhbWV0ZXJzICk7XG5cblx0fVxuXG5cdE1lc2hQaHlzaWNhbE1hdGVyaWFsLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIE1lc2hTdGFuZGFyZE1hdGVyaWFsLnByb3RvdHlwZSApO1xuXHRNZXNoUGh5c2ljYWxNYXRlcmlhbC5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBNZXNoUGh5c2ljYWxNYXRlcmlhbDtcblxuXHRNZXNoUGh5c2ljYWxNYXRlcmlhbC5wcm90b3R5cGUuaXNNZXNoUGh5c2ljYWxNYXRlcmlhbCA9IHRydWU7XG5cblx0TWVzaFBoeXNpY2FsTWF0ZXJpYWwucHJvdG90eXBlLmNvcHkgPSBmdW5jdGlvbiAoIHNvdXJjZSApIHtcblxuXHRcdE1lc2hTdGFuZGFyZE1hdGVyaWFsLnByb3RvdHlwZS5jb3B5LmNhbGwoIHRoaXMsIHNvdXJjZSApO1xuXG5cdFx0dGhpcy5kZWZpbmVzID0geyAnUEhZU0lDQUwnOiAnJyB9O1xuXG5cdFx0dGhpcy5yZWZsZWN0aXZpdHkgPSBzb3VyY2UucmVmbGVjdGl2aXR5O1xuXG5cdFx0dGhpcy5jbGVhckNvYXQgPSBzb3VyY2UuY2xlYXJDb2F0O1xuXHRcdHRoaXMuY2xlYXJDb2F0Um91Z2huZXNzID0gc291cmNlLmNsZWFyQ29hdFJvdWdobmVzcztcblxuXHRcdHJldHVybiB0aGlzO1xuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqXG5cdCAqIHBhcmFtZXRlcnMgPSB7XG5cdCAqICBjb2xvcjogPGhleD4sXG5cdCAqICBzcGVjdWxhcjogPGhleD4sXG5cdCAqICBzaGluaW5lc3M6IDxmbG9hdD4sXG5cdCAqICBvcGFjaXR5OiA8ZmxvYXQ+LFxuXHQgKlxuXHQgKiAgbWFwOiBuZXcgVEhSRUUuVGV4dHVyZSggPEltYWdlPiApLFxuXHQgKlxuXHQgKiAgbGlnaHRNYXA6IG5ldyBUSFJFRS5UZXh0dXJlKCA8SW1hZ2U+ICksXG5cdCAqICBsaWdodE1hcEludGVuc2l0eTogPGZsb2F0PlxuXHQgKlxuXHQgKiAgYW9NYXA6IG5ldyBUSFJFRS5UZXh0dXJlKCA8SW1hZ2U+ICksXG5cdCAqICBhb01hcEludGVuc2l0eTogPGZsb2F0PlxuXHQgKlxuXHQgKiAgZW1pc3NpdmU6IDxoZXg+LFxuXHQgKiAgZW1pc3NpdmVJbnRlbnNpdHk6IDxmbG9hdD5cblx0ICogIGVtaXNzaXZlTWFwOiBuZXcgVEhSRUUuVGV4dHVyZSggPEltYWdlPiApLFxuXHQgKlxuXHQgKiAgYnVtcE1hcDogbmV3IFRIUkVFLlRleHR1cmUoIDxJbWFnZT4gKSxcblx0ICogIGJ1bXBTY2FsZTogPGZsb2F0Pixcblx0ICpcblx0ICogIG5vcm1hbE1hcDogbmV3IFRIUkVFLlRleHR1cmUoIDxJbWFnZT4gKSxcblx0ICogIG5vcm1hbFNjYWxlOiA8VmVjdG9yMj4sXG5cdCAqXG5cdCAqICBkaXNwbGFjZW1lbnRNYXA6IG5ldyBUSFJFRS5UZXh0dXJlKCA8SW1hZ2U+ICksXG5cdCAqICBkaXNwbGFjZW1lbnRTY2FsZTogPGZsb2F0Pixcblx0ICogIGRpc3BsYWNlbWVudEJpYXM6IDxmbG9hdD4sXG5cdCAqXG5cdCAqICBzcGVjdWxhck1hcDogbmV3IFRIUkVFLlRleHR1cmUoIDxJbWFnZT4gKSxcblx0ICpcblx0ICogIGFscGhhTWFwOiBuZXcgVEhSRUUuVGV4dHVyZSggPEltYWdlPiApLFxuXHQgKlxuXHQgKiAgZW52TWFwOiBuZXcgVEhSRUUuVGV4dHVyZUN1YmUoIFtwb3N4LCBuZWd4LCBwb3N5LCBuZWd5LCBwb3N6LCBuZWd6XSApLFxuXHQgKiAgY29tYmluZTogVEhSRUUuTXVsdGlwbHksXG5cdCAqICByZWZsZWN0aXZpdHk6IDxmbG9hdD4sXG5cdCAqICByZWZyYWN0aW9uUmF0aW86IDxmbG9hdD4sXG5cdCAqXG5cdCAqICB3aXJlZnJhbWU6IDxib29sZWFuPixcblx0ICogIHdpcmVmcmFtZUxpbmV3aWR0aDogPGZsb2F0Pixcblx0ICpcblx0ICogIHNraW5uaW5nOiA8Ym9vbD4sXG5cdCAqICBtb3JwaFRhcmdldHM6IDxib29sPixcblx0ICogIG1vcnBoTm9ybWFsczogPGJvb2w+XG5cdCAqIH1cblx0ICovXG5cblx0ZnVuY3Rpb24gTWVzaFBob25nTWF0ZXJpYWwoIHBhcmFtZXRlcnMgKSB7XG5cblx0XHRNYXRlcmlhbC5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnTWVzaFBob25nTWF0ZXJpYWwnO1xuXG5cdFx0dGhpcy5jb2xvciA9IG5ldyBDb2xvciggMHhmZmZmZmYgKTsgLy8gZGlmZnVzZVxuXHRcdHRoaXMuc3BlY3VsYXIgPSBuZXcgQ29sb3IoIDB4MTExMTExICk7XG5cdFx0dGhpcy5zaGluaW5lc3MgPSAzMDtcblxuXHRcdHRoaXMubWFwID0gbnVsbDtcblxuXHRcdHRoaXMubGlnaHRNYXAgPSBudWxsO1xuXHRcdHRoaXMubGlnaHRNYXBJbnRlbnNpdHkgPSAxLjA7XG5cblx0XHR0aGlzLmFvTWFwID0gbnVsbDtcblx0XHR0aGlzLmFvTWFwSW50ZW5zaXR5ID0gMS4wO1xuXG5cdFx0dGhpcy5lbWlzc2l2ZSA9IG5ldyBDb2xvciggMHgwMDAwMDAgKTtcblx0XHR0aGlzLmVtaXNzaXZlSW50ZW5zaXR5ID0gMS4wO1xuXHRcdHRoaXMuZW1pc3NpdmVNYXAgPSBudWxsO1xuXG5cdFx0dGhpcy5idW1wTWFwID0gbnVsbDtcblx0XHR0aGlzLmJ1bXBTY2FsZSA9IDE7XG5cblx0XHR0aGlzLm5vcm1hbE1hcCA9IG51bGw7XG5cdFx0dGhpcy5ub3JtYWxTY2FsZSA9IG5ldyBWZWN0b3IyKCAxLCAxICk7XG5cblx0XHR0aGlzLmRpc3BsYWNlbWVudE1hcCA9IG51bGw7XG5cdFx0dGhpcy5kaXNwbGFjZW1lbnRTY2FsZSA9IDE7XG5cdFx0dGhpcy5kaXNwbGFjZW1lbnRCaWFzID0gMDtcblxuXHRcdHRoaXMuc3BlY3VsYXJNYXAgPSBudWxsO1xuXG5cdFx0dGhpcy5hbHBoYU1hcCA9IG51bGw7XG5cblx0XHR0aGlzLmVudk1hcCA9IG51bGw7XG5cdFx0dGhpcy5jb21iaW5lID0gTXVsdGlwbHlPcGVyYXRpb247XG5cdFx0dGhpcy5yZWZsZWN0aXZpdHkgPSAxO1xuXHRcdHRoaXMucmVmcmFjdGlvblJhdGlvID0gMC45ODtcblxuXHRcdHRoaXMud2lyZWZyYW1lID0gZmFsc2U7XG5cdFx0dGhpcy53aXJlZnJhbWVMaW5ld2lkdGggPSAxO1xuXHRcdHRoaXMud2lyZWZyYW1lTGluZWNhcCA9ICdyb3VuZCc7XG5cdFx0dGhpcy53aXJlZnJhbWVMaW5lam9pbiA9ICdyb3VuZCc7XG5cblx0XHR0aGlzLnNraW5uaW5nID0gZmFsc2U7XG5cdFx0dGhpcy5tb3JwaFRhcmdldHMgPSBmYWxzZTtcblx0XHR0aGlzLm1vcnBoTm9ybWFscyA9IGZhbHNlO1xuXG5cdFx0dGhpcy5zZXRWYWx1ZXMoIHBhcmFtZXRlcnMgKTtcblxuXHR9XG5cblx0TWVzaFBob25nTWF0ZXJpYWwucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggTWF0ZXJpYWwucHJvdG90eXBlICk7XG5cdE1lc2hQaG9uZ01hdGVyaWFsLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IE1lc2hQaG9uZ01hdGVyaWFsO1xuXG5cdE1lc2hQaG9uZ01hdGVyaWFsLnByb3RvdHlwZS5pc01lc2hQaG9uZ01hdGVyaWFsID0gdHJ1ZTtcblxuXHRNZXNoUGhvbmdNYXRlcmlhbC5wcm90b3R5cGUuY29weSA9IGZ1bmN0aW9uICggc291cmNlICkge1xuXG5cdFx0TWF0ZXJpYWwucHJvdG90eXBlLmNvcHkuY2FsbCggdGhpcywgc291cmNlICk7XG5cblx0XHR0aGlzLmNvbG9yLmNvcHkoIHNvdXJjZS5jb2xvciApO1xuXHRcdHRoaXMuc3BlY3VsYXIuY29weSggc291cmNlLnNwZWN1bGFyICk7XG5cdFx0dGhpcy5zaGluaW5lc3MgPSBzb3VyY2Uuc2hpbmluZXNzO1xuXG5cdFx0dGhpcy5tYXAgPSBzb3VyY2UubWFwO1xuXG5cdFx0dGhpcy5saWdodE1hcCA9IHNvdXJjZS5saWdodE1hcDtcblx0XHR0aGlzLmxpZ2h0TWFwSW50ZW5zaXR5ID0gc291cmNlLmxpZ2h0TWFwSW50ZW5zaXR5O1xuXG5cdFx0dGhpcy5hb01hcCA9IHNvdXJjZS5hb01hcDtcblx0XHR0aGlzLmFvTWFwSW50ZW5zaXR5ID0gc291cmNlLmFvTWFwSW50ZW5zaXR5O1xuXG5cdFx0dGhpcy5lbWlzc2l2ZS5jb3B5KCBzb3VyY2UuZW1pc3NpdmUgKTtcblx0XHR0aGlzLmVtaXNzaXZlTWFwID0gc291cmNlLmVtaXNzaXZlTWFwO1xuXHRcdHRoaXMuZW1pc3NpdmVJbnRlbnNpdHkgPSBzb3VyY2UuZW1pc3NpdmVJbnRlbnNpdHk7XG5cblx0XHR0aGlzLmJ1bXBNYXAgPSBzb3VyY2UuYnVtcE1hcDtcblx0XHR0aGlzLmJ1bXBTY2FsZSA9IHNvdXJjZS5idW1wU2NhbGU7XG5cblx0XHR0aGlzLm5vcm1hbE1hcCA9IHNvdXJjZS5ub3JtYWxNYXA7XG5cdFx0dGhpcy5ub3JtYWxTY2FsZS5jb3B5KCBzb3VyY2Uubm9ybWFsU2NhbGUgKTtcblxuXHRcdHRoaXMuZGlzcGxhY2VtZW50TWFwID0gc291cmNlLmRpc3BsYWNlbWVudE1hcDtcblx0XHR0aGlzLmRpc3BsYWNlbWVudFNjYWxlID0gc291cmNlLmRpc3BsYWNlbWVudFNjYWxlO1xuXHRcdHRoaXMuZGlzcGxhY2VtZW50QmlhcyA9IHNvdXJjZS5kaXNwbGFjZW1lbnRCaWFzO1xuXG5cdFx0dGhpcy5zcGVjdWxhck1hcCA9IHNvdXJjZS5zcGVjdWxhck1hcDtcblxuXHRcdHRoaXMuYWxwaGFNYXAgPSBzb3VyY2UuYWxwaGFNYXA7XG5cblx0XHR0aGlzLmVudk1hcCA9IHNvdXJjZS5lbnZNYXA7XG5cdFx0dGhpcy5jb21iaW5lID0gc291cmNlLmNvbWJpbmU7XG5cdFx0dGhpcy5yZWZsZWN0aXZpdHkgPSBzb3VyY2UucmVmbGVjdGl2aXR5O1xuXHRcdHRoaXMucmVmcmFjdGlvblJhdGlvID0gc291cmNlLnJlZnJhY3Rpb25SYXRpbztcblxuXHRcdHRoaXMud2lyZWZyYW1lID0gc291cmNlLndpcmVmcmFtZTtcblx0XHR0aGlzLndpcmVmcmFtZUxpbmV3aWR0aCA9IHNvdXJjZS53aXJlZnJhbWVMaW5ld2lkdGg7XG5cdFx0dGhpcy53aXJlZnJhbWVMaW5lY2FwID0gc291cmNlLndpcmVmcmFtZUxpbmVjYXA7XG5cdFx0dGhpcy53aXJlZnJhbWVMaW5lam9pbiA9IHNvdXJjZS53aXJlZnJhbWVMaW5lam9pbjtcblxuXHRcdHRoaXMuc2tpbm5pbmcgPSBzb3VyY2Uuc2tpbm5pbmc7XG5cdFx0dGhpcy5tb3JwaFRhcmdldHMgPSBzb3VyY2UubW9ycGhUYXJnZXRzO1xuXHRcdHRoaXMubW9ycGhOb3JtYWxzID0gc291cmNlLm1vcnBoTm9ybWFscztcblxuXHRcdHJldHVybiB0aGlzO1xuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgdGFrYWhpcm94IC8gaHR0cDovL2dpdGh1Yi5jb20vdGFrYWhpcm94XG5cdCAqXG5cdCAqIHBhcmFtZXRlcnMgPSB7XG5cdCAqICBncmFkaWVudE1hcDogbmV3IFRIUkVFLlRleHR1cmUoIDxJbWFnZT4gKVxuXHQgKiB9XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIE1lc2hUb29uTWF0ZXJpYWwoIHBhcmFtZXRlcnMgKSB7XG5cblx0XHRNZXNoUGhvbmdNYXRlcmlhbC5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLmRlZmluZXMgPSB7ICdUT09OJzogJycgfTtcblxuXHRcdHRoaXMudHlwZSA9ICdNZXNoVG9vbk1hdGVyaWFsJztcblxuXHRcdHRoaXMuZ3JhZGllbnRNYXAgPSBudWxsO1xuXG5cdFx0dGhpcy5zZXRWYWx1ZXMoIHBhcmFtZXRlcnMgKTtcblxuXHR9XG5cblx0TWVzaFRvb25NYXRlcmlhbC5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBNZXNoUGhvbmdNYXRlcmlhbC5wcm90b3R5cGUgKTtcblx0TWVzaFRvb25NYXRlcmlhbC5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBNZXNoVG9vbk1hdGVyaWFsO1xuXG5cdE1lc2hUb29uTWF0ZXJpYWwucHJvdG90eXBlLmlzTWVzaFRvb25NYXRlcmlhbCA9IHRydWU7XG5cblx0TWVzaFRvb25NYXRlcmlhbC5wcm90b3R5cGUuY29weSA9IGZ1bmN0aW9uICggc291cmNlICkge1xuXG5cdFx0TWVzaFBob25nTWF0ZXJpYWwucHJvdG90eXBlLmNvcHkuY2FsbCggdGhpcywgc291cmNlICk7XG5cblx0XHR0aGlzLmdyYWRpZW50TWFwID0gc291cmNlLmdyYWRpZW50TWFwO1xuXG5cdFx0cmV0dXJuIHRoaXM7XG5cblx0fTtcblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICogQGF1dGhvciBXZXN0TGFuZ2xleSAvIGh0dHA6Ly9naXRodWIuY29tL1dlc3RMYW5nbGV5XG5cdCAqXG5cdCAqIHBhcmFtZXRlcnMgPSB7XG5cdCAqICBvcGFjaXR5OiA8ZmxvYXQ+LFxuXHQgKlxuXHQgKiAgYnVtcE1hcDogbmV3IFRIUkVFLlRleHR1cmUoIDxJbWFnZT4gKSxcblx0ICogIGJ1bXBTY2FsZTogPGZsb2F0Pixcblx0ICpcblx0ICogIG5vcm1hbE1hcDogbmV3IFRIUkVFLlRleHR1cmUoIDxJbWFnZT4gKSxcblx0ICogIG5vcm1hbFNjYWxlOiA8VmVjdG9yMj4sXG5cdCAqXG5cdCAqICBkaXNwbGFjZW1lbnRNYXA6IG5ldyBUSFJFRS5UZXh0dXJlKCA8SW1hZ2U+ICksXG5cdCAqICBkaXNwbGFjZW1lbnRTY2FsZTogPGZsb2F0Pixcblx0ICogIGRpc3BsYWNlbWVudEJpYXM6IDxmbG9hdD4sXG5cdCAqXG5cdCAqICB3aXJlZnJhbWU6IDxib29sZWFuPixcblx0ICogIHdpcmVmcmFtZUxpbmV3aWR0aDogPGZsb2F0PlxuXHQgKlxuXHQgKiAgc2tpbm5pbmc6IDxib29sPixcblx0ICogIG1vcnBoVGFyZ2V0czogPGJvb2w+LFxuXHQgKiAgbW9ycGhOb3JtYWxzOiA8Ym9vbD5cblx0ICogfVxuXHQgKi9cblxuXHRmdW5jdGlvbiBNZXNoTm9ybWFsTWF0ZXJpYWwoIHBhcmFtZXRlcnMgKSB7XG5cblx0XHRNYXRlcmlhbC5jYWxsKCB0aGlzLCBwYXJhbWV0ZXJzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnTWVzaE5vcm1hbE1hdGVyaWFsJztcblxuXHRcdHRoaXMuYnVtcE1hcCA9IG51bGw7XG5cdFx0dGhpcy5idW1wU2NhbGUgPSAxO1xuXG5cdFx0dGhpcy5ub3JtYWxNYXAgPSBudWxsO1xuXHRcdHRoaXMubm9ybWFsU2NhbGUgPSBuZXcgVmVjdG9yMiggMSwgMSApO1xuXG5cdFx0dGhpcy5kaXNwbGFjZW1lbnRNYXAgPSBudWxsO1xuXHRcdHRoaXMuZGlzcGxhY2VtZW50U2NhbGUgPSAxO1xuXHRcdHRoaXMuZGlzcGxhY2VtZW50QmlhcyA9IDA7XG5cblx0XHR0aGlzLndpcmVmcmFtZSA9IGZhbHNlO1xuXHRcdHRoaXMud2lyZWZyYW1lTGluZXdpZHRoID0gMTtcblxuXHRcdHRoaXMuZm9nID0gZmFsc2U7XG5cdFx0dGhpcy5saWdodHMgPSBmYWxzZTtcblxuXHRcdHRoaXMuc2tpbm5pbmcgPSBmYWxzZTtcblx0XHR0aGlzLm1vcnBoVGFyZ2V0cyA9IGZhbHNlO1xuXHRcdHRoaXMubW9ycGhOb3JtYWxzID0gZmFsc2U7XG5cblx0XHR0aGlzLnNldFZhbHVlcyggcGFyYW1ldGVycyApO1xuXG5cdH1cblxuXHRNZXNoTm9ybWFsTWF0ZXJpYWwucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggTWF0ZXJpYWwucHJvdG90eXBlICk7XG5cdE1lc2hOb3JtYWxNYXRlcmlhbC5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBNZXNoTm9ybWFsTWF0ZXJpYWw7XG5cblx0TWVzaE5vcm1hbE1hdGVyaWFsLnByb3RvdHlwZS5pc01lc2hOb3JtYWxNYXRlcmlhbCA9IHRydWU7XG5cblx0TWVzaE5vcm1hbE1hdGVyaWFsLnByb3RvdHlwZS5jb3B5ID0gZnVuY3Rpb24gKCBzb3VyY2UgKSB7XG5cblx0XHRNYXRlcmlhbC5wcm90b3R5cGUuY29weS5jYWxsKCB0aGlzLCBzb3VyY2UgKTtcblxuXHRcdHRoaXMuYnVtcE1hcCA9IHNvdXJjZS5idW1wTWFwO1xuXHRcdHRoaXMuYnVtcFNjYWxlID0gc291cmNlLmJ1bXBTY2FsZTtcblxuXHRcdHRoaXMubm9ybWFsTWFwID0gc291cmNlLm5vcm1hbE1hcDtcblx0XHR0aGlzLm5vcm1hbFNjYWxlLmNvcHkoIHNvdXJjZS5ub3JtYWxTY2FsZSApO1xuXG5cdFx0dGhpcy5kaXNwbGFjZW1lbnRNYXAgPSBzb3VyY2UuZGlzcGxhY2VtZW50TWFwO1xuXHRcdHRoaXMuZGlzcGxhY2VtZW50U2NhbGUgPSBzb3VyY2UuZGlzcGxhY2VtZW50U2NhbGU7XG5cdFx0dGhpcy5kaXNwbGFjZW1lbnRCaWFzID0gc291cmNlLmRpc3BsYWNlbWVudEJpYXM7XG5cblx0XHR0aGlzLndpcmVmcmFtZSA9IHNvdXJjZS53aXJlZnJhbWU7XG5cdFx0dGhpcy53aXJlZnJhbWVMaW5ld2lkdGggPSBzb3VyY2Uud2lyZWZyYW1lTGluZXdpZHRoO1xuXG5cdFx0dGhpcy5za2lubmluZyA9IHNvdXJjZS5za2lubmluZztcblx0XHR0aGlzLm1vcnBoVGFyZ2V0cyA9IHNvdXJjZS5tb3JwaFRhcmdldHM7XG5cdFx0dGhpcy5tb3JwaE5vcm1hbHMgPSBzb3VyY2UubW9ycGhOb3JtYWxzO1xuXG5cdFx0cmV0dXJuIHRoaXM7XG5cblx0fTtcblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICogQGF1dGhvciBhbHRlcmVkcSAvIGh0dHA6Ly9hbHRlcmVkcXVhbGlhLmNvbS9cblx0ICpcblx0ICogcGFyYW1ldGVycyA9IHtcblx0ICogIGNvbG9yOiA8aGV4Pixcblx0ICogIG9wYWNpdHk6IDxmbG9hdD4sXG5cdCAqXG5cdCAqICBtYXA6IG5ldyBUSFJFRS5UZXh0dXJlKCA8SW1hZ2U+ICksXG5cdCAqXG5cdCAqICBsaWdodE1hcDogbmV3IFRIUkVFLlRleHR1cmUoIDxJbWFnZT4gKSxcblx0ICogIGxpZ2h0TWFwSW50ZW5zaXR5OiA8ZmxvYXQ+XG5cdCAqXG5cdCAqICBhb01hcDogbmV3IFRIUkVFLlRleHR1cmUoIDxJbWFnZT4gKSxcblx0ICogIGFvTWFwSW50ZW5zaXR5OiA8ZmxvYXQ+XG5cdCAqXG5cdCAqICBlbWlzc2l2ZTogPGhleD4sXG5cdCAqICBlbWlzc2l2ZUludGVuc2l0eTogPGZsb2F0PlxuXHQgKiAgZW1pc3NpdmVNYXA6IG5ldyBUSFJFRS5UZXh0dXJlKCA8SW1hZ2U+ICksXG5cdCAqXG5cdCAqICBzcGVjdWxhck1hcDogbmV3IFRIUkVFLlRleHR1cmUoIDxJbWFnZT4gKSxcblx0ICpcblx0ICogIGFscGhhTWFwOiBuZXcgVEhSRUUuVGV4dHVyZSggPEltYWdlPiApLFxuXHQgKlxuXHQgKiAgZW52TWFwOiBuZXcgVEhSRUUuVGV4dHVyZUN1YmUoIFtwb3N4LCBuZWd4LCBwb3N5LCBuZWd5LCBwb3N6LCBuZWd6XSApLFxuXHQgKiAgY29tYmluZTogVEhSRUUuTXVsdGlwbHksXG5cdCAqICByZWZsZWN0aXZpdHk6IDxmbG9hdD4sXG5cdCAqICByZWZyYWN0aW9uUmF0aW86IDxmbG9hdD4sXG5cdCAqXG5cdCAqICB3aXJlZnJhbWU6IDxib29sZWFuPixcblx0ICogIHdpcmVmcmFtZUxpbmV3aWR0aDogPGZsb2F0Pixcblx0ICpcblx0ICogIHNraW5uaW5nOiA8Ym9vbD4sXG5cdCAqICBtb3JwaFRhcmdldHM6IDxib29sPixcblx0ICogIG1vcnBoTm9ybWFsczogPGJvb2w+XG5cdCAqIH1cblx0ICovXG5cblx0ZnVuY3Rpb24gTWVzaExhbWJlcnRNYXRlcmlhbCggcGFyYW1ldGVycyApIHtcblxuXHRcdE1hdGVyaWFsLmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdNZXNoTGFtYmVydE1hdGVyaWFsJztcblxuXHRcdHRoaXMuY29sb3IgPSBuZXcgQ29sb3IoIDB4ZmZmZmZmICk7IC8vIGRpZmZ1c2VcblxuXHRcdHRoaXMubWFwID0gbnVsbDtcblxuXHRcdHRoaXMubGlnaHRNYXAgPSBudWxsO1xuXHRcdHRoaXMubGlnaHRNYXBJbnRlbnNpdHkgPSAxLjA7XG5cblx0XHR0aGlzLmFvTWFwID0gbnVsbDtcblx0XHR0aGlzLmFvTWFwSW50ZW5zaXR5ID0gMS4wO1xuXG5cdFx0dGhpcy5lbWlzc2l2ZSA9IG5ldyBDb2xvciggMHgwMDAwMDAgKTtcblx0XHR0aGlzLmVtaXNzaXZlSW50ZW5zaXR5ID0gMS4wO1xuXHRcdHRoaXMuZW1pc3NpdmVNYXAgPSBudWxsO1xuXG5cdFx0dGhpcy5zcGVjdWxhck1hcCA9IG51bGw7XG5cblx0XHR0aGlzLmFscGhhTWFwID0gbnVsbDtcblxuXHRcdHRoaXMuZW52TWFwID0gbnVsbDtcblx0XHR0aGlzLmNvbWJpbmUgPSBNdWx0aXBseU9wZXJhdGlvbjtcblx0XHR0aGlzLnJlZmxlY3Rpdml0eSA9IDE7XG5cdFx0dGhpcy5yZWZyYWN0aW9uUmF0aW8gPSAwLjk4O1xuXG5cdFx0dGhpcy53aXJlZnJhbWUgPSBmYWxzZTtcblx0XHR0aGlzLndpcmVmcmFtZUxpbmV3aWR0aCA9IDE7XG5cdFx0dGhpcy53aXJlZnJhbWVMaW5lY2FwID0gJ3JvdW5kJztcblx0XHR0aGlzLndpcmVmcmFtZUxpbmVqb2luID0gJ3JvdW5kJztcblxuXHRcdHRoaXMuc2tpbm5pbmcgPSBmYWxzZTtcblx0XHR0aGlzLm1vcnBoVGFyZ2V0cyA9IGZhbHNlO1xuXHRcdHRoaXMubW9ycGhOb3JtYWxzID0gZmFsc2U7XG5cblx0XHR0aGlzLnNldFZhbHVlcyggcGFyYW1ldGVycyApO1xuXG5cdH1cblxuXHRNZXNoTGFtYmVydE1hdGVyaWFsLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIE1hdGVyaWFsLnByb3RvdHlwZSApO1xuXHRNZXNoTGFtYmVydE1hdGVyaWFsLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IE1lc2hMYW1iZXJ0TWF0ZXJpYWw7XG5cblx0TWVzaExhbWJlcnRNYXRlcmlhbC5wcm90b3R5cGUuaXNNZXNoTGFtYmVydE1hdGVyaWFsID0gdHJ1ZTtcblxuXHRNZXNoTGFtYmVydE1hdGVyaWFsLnByb3RvdHlwZS5jb3B5ID0gZnVuY3Rpb24gKCBzb3VyY2UgKSB7XG5cblx0XHRNYXRlcmlhbC5wcm90b3R5cGUuY29weS5jYWxsKCB0aGlzLCBzb3VyY2UgKTtcblxuXHRcdHRoaXMuY29sb3IuY29weSggc291cmNlLmNvbG9yICk7XG5cblx0XHR0aGlzLm1hcCA9IHNvdXJjZS5tYXA7XG5cblx0XHR0aGlzLmxpZ2h0TWFwID0gc291cmNlLmxpZ2h0TWFwO1xuXHRcdHRoaXMubGlnaHRNYXBJbnRlbnNpdHkgPSBzb3VyY2UubGlnaHRNYXBJbnRlbnNpdHk7XG5cblx0XHR0aGlzLmFvTWFwID0gc291cmNlLmFvTWFwO1xuXHRcdHRoaXMuYW9NYXBJbnRlbnNpdHkgPSBzb3VyY2UuYW9NYXBJbnRlbnNpdHk7XG5cblx0XHR0aGlzLmVtaXNzaXZlLmNvcHkoIHNvdXJjZS5lbWlzc2l2ZSApO1xuXHRcdHRoaXMuZW1pc3NpdmVNYXAgPSBzb3VyY2UuZW1pc3NpdmVNYXA7XG5cdFx0dGhpcy5lbWlzc2l2ZUludGVuc2l0eSA9IHNvdXJjZS5lbWlzc2l2ZUludGVuc2l0eTtcblxuXHRcdHRoaXMuc3BlY3VsYXJNYXAgPSBzb3VyY2Uuc3BlY3VsYXJNYXA7XG5cblx0XHR0aGlzLmFscGhhTWFwID0gc291cmNlLmFscGhhTWFwO1xuXG5cdFx0dGhpcy5lbnZNYXAgPSBzb3VyY2UuZW52TWFwO1xuXHRcdHRoaXMuY29tYmluZSA9IHNvdXJjZS5jb21iaW5lO1xuXHRcdHRoaXMucmVmbGVjdGl2aXR5ID0gc291cmNlLnJlZmxlY3Rpdml0eTtcblx0XHR0aGlzLnJlZnJhY3Rpb25SYXRpbyA9IHNvdXJjZS5yZWZyYWN0aW9uUmF0aW87XG5cblx0XHR0aGlzLndpcmVmcmFtZSA9IHNvdXJjZS53aXJlZnJhbWU7XG5cdFx0dGhpcy53aXJlZnJhbWVMaW5ld2lkdGggPSBzb3VyY2Uud2lyZWZyYW1lTGluZXdpZHRoO1xuXHRcdHRoaXMud2lyZWZyYW1lTGluZWNhcCA9IHNvdXJjZS53aXJlZnJhbWVMaW5lY2FwO1xuXHRcdHRoaXMud2lyZWZyYW1lTGluZWpvaW4gPSBzb3VyY2Uud2lyZWZyYW1lTGluZWpvaW47XG5cblx0XHR0aGlzLnNraW5uaW5nID0gc291cmNlLnNraW5uaW5nO1xuXHRcdHRoaXMubW9ycGhUYXJnZXRzID0gc291cmNlLm1vcnBoVGFyZ2V0cztcblx0XHR0aGlzLm1vcnBoTm9ybWFscyA9IHNvdXJjZS5tb3JwaE5vcm1hbHM7XG5cblx0XHRyZXR1cm4gdGhpcztcblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKlxuXHQgKiBwYXJhbWV0ZXJzID0ge1xuXHQgKiAgY29sb3I6IDxoZXg+LFxuXHQgKiAgb3BhY2l0eTogPGZsb2F0Pixcblx0ICpcblx0ICogIGxpbmV3aWR0aDogPGZsb2F0Pixcblx0ICpcblx0ICogIHNjYWxlOiA8ZmxvYXQ+LFxuXHQgKiAgZGFzaFNpemU6IDxmbG9hdD4sXG5cdCAqICBnYXBTaXplOiA8ZmxvYXQ+XG5cdCAqIH1cblx0ICovXG5cblx0ZnVuY3Rpb24gTGluZURhc2hlZE1hdGVyaWFsKCBwYXJhbWV0ZXJzICkge1xuXG5cdFx0TWF0ZXJpYWwuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ0xpbmVEYXNoZWRNYXRlcmlhbCc7XG5cblx0XHR0aGlzLmNvbG9yID0gbmV3IENvbG9yKCAweGZmZmZmZiApO1xuXG5cdFx0dGhpcy5saW5ld2lkdGggPSAxO1xuXG5cdFx0dGhpcy5zY2FsZSA9IDE7XG5cdFx0dGhpcy5kYXNoU2l6ZSA9IDM7XG5cdFx0dGhpcy5nYXBTaXplID0gMTtcblxuXHRcdHRoaXMubGlnaHRzID0gZmFsc2U7XG5cblx0XHR0aGlzLnNldFZhbHVlcyggcGFyYW1ldGVycyApO1xuXG5cdH1cblxuXHRMaW5lRGFzaGVkTWF0ZXJpYWwucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggTWF0ZXJpYWwucHJvdG90eXBlICk7XG5cdExpbmVEYXNoZWRNYXRlcmlhbC5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBMaW5lRGFzaGVkTWF0ZXJpYWw7XG5cblx0TGluZURhc2hlZE1hdGVyaWFsLnByb3RvdHlwZS5pc0xpbmVEYXNoZWRNYXRlcmlhbCA9IHRydWU7XG5cblx0TGluZURhc2hlZE1hdGVyaWFsLnByb3RvdHlwZS5jb3B5ID0gZnVuY3Rpb24gKCBzb3VyY2UgKSB7XG5cblx0XHRNYXRlcmlhbC5wcm90b3R5cGUuY29weS5jYWxsKCB0aGlzLCBzb3VyY2UgKTtcblxuXHRcdHRoaXMuY29sb3IuY29weSggc291cmNlLmNvbG9yICk7XG5cblx0XHR0aGlzLmxpbmV3aWR0aCA9IHNvdXJjZS5saW5ld2lkdGg7XG5cblx0XHR0aGlzLnNjYWxlID0gc291cmNlLnNjYWxlO1xuXHRcdHRoaXMuZGFzaFNpemUgPSBzb3VyY2UuZGFzaFNpemU7XG5cdFx0dGhpcy5nYXBTaXplID0gc291cmNlLmdhcFNpemU7XG5cblx0XHRyZXR1cm4gdGhpcztcblxuXHR9O1xuXG5cblxuXHR2YXIgTWF0ZXJpYWxzID0gT2JqZWN0LmZyZWV6ZSh7XG5cdFx0U2hhZG93TWF0ZXJpYWw6IFNoYWRvd01hdGVyaWFsLFxuXHRcdFNwcml0ZU1hdGVyaWFsOiBTcHJpdGVNYXRlcmlhbCxcblx0XHRSYXdTaGFkZXJNYXRlcmlhbDogUmF3U2hhZGVyTWF0ZXJpYWwsXG5cdFx0U2hhZGVyTWF0ZXJpYWw6IFNoYWRlck1hdGVyaWFsLFxuXHRcdFBvaW50c01hdGVyaWFsOiBQb2ludHNNYXRlcmlhbCxcblx0XHRNdWx0aU1hdGVyaWFsOiBNdWx0aU1hdGVyaWFsLFxuXHRcdE1lc2hQaHlzaWNhbE1hdGVyaWFsOiBNZXNoUGh5c2ljYWxNYXRlcmlhbCxcblx0XHRNZXNoU3RhbmRhcmRNYXRlcmlhbDogTWVzaFN0YW5kYXJkTWF0ZXJpYWwsXG5cdFx0TWVzaFBob25nTWF0ZXJpYWw6IE1lc2hQaG9uZ01hdGVyaWFsLFxuXHRcdE1lc2hUb29uTWF0ZXJpYWw6IE1lc2hUb29uTWF0ZXJpYWwsXG5cdFx0TWVzaE5vcm1hbE1hdGVyaWFsOiBNZXNoTm9ybWFsTWF0ZXJpYWwsXG5cdFx0TWVzaExhbWJlcnRNYXRlcmlhbDogTWVzaExhbWJlcnRNYXRlcmlhbCxcblx0XHRNZXNoRGVwdGhNYXRlcmlhbDogTWVzaERlcHRoTWF0ZXJpYWwsXG5cdFx0TWVzaEJhc2ljTWF0ZXJpYWw6IE1lc2hCYXNpY01hdGVyaWFsLFxuXHRcdExpbmVEYXNoZWRNYXRlcmlhbDogTGluZURhc2hlZE1hdGVyaWFsLFxuXHRcdExpbmVCYXNpY01hdGVyaWFsOiBMaW5lQmFzaWNNYXRlcmlhbCxcblx0XHRNYXRlcmlhbDogTWF0ZXJpYWxcblx0fSk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdHZhciBDYWNoZSA9IHtcblxuXHRcdGVuYWJsZWQ6IGZhbHNlLFxuXG5cdFx0ZmlsZXM6IHt9LFxuXG5cdFx0YWRkOiBmdW5jdGlvbiAoIGtleSwgZmlsZSApIHtcblxuXHRcdFx0aWYgKCB0aGlzLmVuYWJsZWQgPT09IGZhbHNlICkgcmV0dXJuO1xuXG5cdFx0XHQvLyBjb25zb2xlLmxvZyggJ1RIUkVFLkNhY2hlJywgJ0FkZGluZyBrZXk6Jywga2V5ICk7XG5cblx0XHRcdHRoaXMuZmlsZXNbIGtleSBdID0gZmlsZTtcblxuXHRcdH0sXG5cblx0XHRnZXQ6IGZ1bmN0aW9uICgga2V5ICkge1xuXG5cdFx0XHRpZiAoIHRoaXMuZW5hYmxlZCA9PT0gZmFsc2UgKSByZXR1cm47XG5cblx0XHRcdC8vIGNvbnNvbGUubG9nKCAnVEhSRUUuQ2FjaGUnLCAnQ2hlY2tpbmcga2V5OicsIGtleSApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5maWxlc1sga2V5IF07XG5cblx0XHR9LFxuXG5cdFx0cmVtb3ZlOiBmdW5jdGlvbiAoIGtleSApIHtcblxuXHRcdFx0ZGVsZXRlIHRoaXMuZmlsZXNbIGtleSBdO1xuXG5cdFx0fSxcblxuXHRcdGNsZWFyOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMuZmlsZXMgPSB7fTtcblxuXHRcdH1cblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBMb2FkaW5nTWFuYWdlciggb25Mb2FkLCBvblByb2dyZXNzLCBvbkVycm9yICkge1xuXG5cdFx0dmFyIHNjb3BlID0gdGhpcztcblxuXHRcdHZhciBpc0xvYWRpbmcgPSBmYWxzZSwgaXRlbXNMb2FkZWQgPSAwLCBpdGVtc1RvdGFsID0gMDtcblxuXHRcdHRoaXMub25TdGFydCA9IHVuZGVmaW5lZDtcblx0XHR0aGlzLm9uTG9hZCA9IG9uTG9hZDtcblx0XHR0aGlzLm9uUHJvZ3Jlc3MgPSBvblByb2dyZXNzO1xuXHRcdHRoaXMub25FcnJvciA9IG9uRXJyb3I7XG5cblx0XHR0aGlzLml0ZW1TdGFydCA9IGZ1bmN0aW9uICggdXJsICkge1xuXG5cdFx0XHRpdGVtc1RvdGFsICsrO1xuXG5cdFx0XHRpZiAoIGlzTG9hZGluZyA9PT0gZmFsc2UgKSB7XG5cblx0XHRcdFx0aWYgKCBzY29wZS5vblN0YXJ0ICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRzY29wZS5vblN0YXJ0KCB1cmwsIGl0ZW1zTG9hZGVkLCBpdGVtc1RvdGFsICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdGlzTG9hZGluZyA9IHRydWU7XG5cblx0XHR9O1xuXG5cdFx0dGhpcy5pdGVtRW5kID0gZnVuY3Rpb24gKCB1cmwgKSB7XG5cblx0XHRcdGl0ZW1zTG9hZGVkICsrO1xuXG5cdFx0XHRpZiAoIHNjb3BlLm9uUHJvZ3Jlc3MgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRzY29wZS5vblByb2dyZXNzKCB1cmwsIGl0ZW1zTG9hZGVkLCBpdGVtc1RvdGFsICk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBpdGVtc0xvYWRlZCA9PT0gaXRlbXNUb3RhbCApIHtcblxuXHRcdFx0XHRpc0xvYWRpbmcgPSBmYWxzZTtcblxuXHRcdFx0XHRpZiAoIHNjb3BlLm9uTG9hZCAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0c2NvcGUub25Mb2FkKCk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHR9O1xuXG5cdFx0dGhpcy5pdGVtRXJyb3IgPSBmdW5jdGlvbiAoIHVybCApIHtcblxuXHRcdFx0aWYgKCBzY29wZS5vbkVycm9yICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0c2NvcGUub25FcnJvciggdXJsICk7XG5cblx0XHRcdH1cblxuXHRcdH07XG5cblx0fVxuXG5cdHZhciBEZWZhdWx0TG9hZGluZ01hbmFnZXIgPSBuZXcgTG9hZGluZ01hbmFnZXIoKTtcblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gRmlsZUxvYWRlciggbWFuYWdlciApIHtcblxuXHRcdHRoaXMubWFuYWdlciA9ICggbWFuYWdlciAhPT0gdW5kZWZpbmVkICkgPyBtYW5hZ2VyIDogRGVmYXVsdExvYWRpbmdNYW5hZ2VyO1xuXG5cdH1cblxuXHRPYmplY3QuYXNzaWduKCBGaWxlTG9hZGVyLnByb3RvdHlwZSwge1xuXG5cdFx0bG9hZDogZnVuY3Rpb24gKCB1cmwsIG9uTG9hZCwgb25Qcm9ncmVzcywgb25FcnJvciApIHtcblxuXHRcdFx0aWYgKCB1cmwgPT09IHVuZGVmaW5lZCApIHVybCA9ICcnO1xuXG5cdFx0XHRpZiAoIHRoaXMucGF0aCAhPT0gdW5kZWZpbmVkICkgdXJsID0gdGhpcy5wYXRoICsgdXJsO1xuXG5cdFx0XHR2YXIgc2NvcGUgPSB0aGlzO1xuXG5cdFx0XHR2YXIgY2FjaGVkID0gQ2FjaGUuZ2V0KCB1cmwgKTtcblxuXHRcdFx0aWYgKCBjYWNoZWQgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRzY29wZS5tYW5hZ2VyLml0ZW1TdGFydCggdXJsICk7XG5cblx0XHRcdFx0c2V0VGltZW91dCggZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdFx0aWYgKCBvbkxvYWQgKSBvbkxvYWQoIGNhY2hlZCApO1xuXG5cdFx0XHRcdFx0c2NvcGUubWFuYWdlci5pdGVtRW5kKCB1cmwgKTtcblxuXHRcdFx0XHR9LCAwICk7XG5cblx0XHRcdFx0cmV0dXJuIGNhY2hlZDtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyBDaGVjayBmb3IgZGF0YTogVVJJXG5cdFx0XHR2YXIgZGF0YVVyaVJlZ2V4ID0gL15kYXRhOiguKj8pKDtiYXNlNjQpPywoLiopJC87XG5cdFx0XHR2YXIgZGF0YVVyaVJlZ2V4UmVzdWx0ID0gdXJsLm1hdGNoKCBkYXRhVXJpUmVnZXggKTtcblxuXHRcdFx0Ly8gU2FmYXJpIGNhbiBub3QgaGFuZGxlIERhdGEgVVJJcyB0aHJvdWdoIFhNTEh0dHBSZXF1ZXN0IHNvIHByb2Nlc3MgbWFudWFsbHlcblx0XHRcdGlmICggZGF0YVVyaVJlZ2V4UmVzdWx0ICkge1xuXG5cdFx0XHRcdHZhciBtaW1lVHlwZSA9IGRhdGFVcmlSZWdleFJlc3VsdFsgMSBdO1xuXHRcdFx0XHR2YXIgaXNCYXNlNjQgPSAhISBkYXRhVXJpUmVnZXhSZXN1bHRbIDIgXTtcblx0XHRcdFx0dmFyIGRhdGEgPSBkYXRhVXJpUmVnZXhSZXN1bHRbIDMgXTtcblxuXHRcdFx0XHRkYXRhID0gd2luZG93LmRlY29kZVVSSUNvbXBvbmVudCggZGF0YSApO1xuXG5cdFx0XHRcdGlmICggaXNCYXNlNjQgKSBkYXRhID0gd2luZG93LmF0b2IoIGRhdGEgKTtcblxuXHRcdFx0XHR0cnkge1xuXG5cdFx0XHRcdFx0dmFyIHJlc3BvbnNlO1xuXHRcdFx0XHRcdHZhciByZXNwb25zZVR5cGUgPSAoIHRoaXMucmVzcG9uc2VUeXBlIHx8ICcnICkudG9Mb3dlckNhc2UoKTtcblxuXHRcdFx0XHRcdHN3aXRjaCAoIHJlc3BvbnNlVHlwZSApIHtcblxuXHRcdFx0XHRcdFx0Y2FzZSAnYXJyYXlidWZmZXInOlxuXHRcdFx0XHRcdFx0Y2FzZSAnYmxvYic6XG5cblx0XHRcdFx0XHRcdCBcdHJlc3BvbnNlID0gbmV3IEFycmF5QnVmZmVyKCBkYXRhLmxlbmd0aCApO1xuXG5cdFx0XHRcdFx0XHRcdHZhciB2aWV3ID0gbmV3IFVpbnQ4QXJyYXkoIHJlc3BvbnNlICk7XG5cblx0XHRcdFx0XHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgZGF0YS5sZW5ndGg7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHR2aWV3WyBpIF0gPSBkYXRhLmNoYXJDb2RlQXQoIGkgKTtcblxuXHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdFx0aWYgKCByZXNwb25zZVR5cGUgPT09ICdibG9iJyApIHtcblxuXHRcdFx0XHRcdFx0XHRcdHJlc3BvbnNlID0gbmV3IEJsb2IoIFsgcmVzcG9uc2UgXSwgeyB0eXBlOiBtaW1lVHlwZSB9ICk7XG5cblx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRjYXNlICdkb2N1bWVudCc6XG5cblx0XHRcdFx0XHRcdFx0dmFyIHBhcnNlciA9IG5ldyBET01QYXJzZXIoKTtcblx0XHRcdFx0XHRcdFx0cmVzcG9uc2UgPSBwYXJzZXIucGFyc2VGcm9tU3RyaW5nKCBkYXRhLCBtaW1lVHlwZSApO1xuXG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRjYXNlICdqc29uJzpcblxuXHRcdFx0XHRcdFx0XHRyZXNwb25zZSA9IEpTT04ucGFyc2UoIGRhdGEgKTtcblxuXHRcdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdFx0ZGVmYXVsdDogLy8gJ3RleHQnIG9yIG90aGVyXG5cblx0XHRcdFx0XHRcdFx0cmVzcG9uc2UgPSBkYXRhO1xuXG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0Ly8gV2FpdCBmb3IgbmV4dCBicm93c2VyIHRpY2tcblx0XHRcdFx0XHR3aW5kb3cuc2V0VGltZW91dCggZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdFx0XHRpZiAoIG9uTG9hZCApIG9uTG9hZCggcmVzcG9uc2UgKTtcblxuXHRcdFx0XHRcdFx0c2NvcGUubWFuYWdlci5pdGVtRW5kKCB1cmwgKTtcblxuXHRcdFx0XHRcdH0sIDAgKTtcblxuXHRcdFx0XHR9IGNhdGNoICggZXJyb3IgKSB7XG5cblx0XHRcdFx0XHQvLyBXYWl0IGZvciBuZXh0IGJyb3dzZXIgdGlja1xuXHRcdFx0XHRcdHdpbmRvdy5zZXRUaW1lb3V0KCBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdFx0XHRcdGlmICggb25FcnJvciApIG9uRXJyb3IoIGVycm9yICk7XG5cblx0XHRcdFx0XHRcdHNjb3BlLm1hbmFnZXIuaXRlbUVycm9yKCB1cmwgKTtcblxuXHRcdFx0XHRcdH0sIDAgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0dmFyIHJlcXVlc3QgPSBuZXcgWE1MSHR0cFJlcXVlc3QoKTtcblx0XHRcdFx0cmVxdWVzdC5vcGVuKCAnR0VUJywgdXJsLCB0cnVlICk7XG5cblx0XHRcdFx0cmVxdWVzdC5hZGRFdmVudExpc3RlbmVyKCAnbG9hZCcsIGZ1bmN0aW9uICggZXZlbnQgKSB7XG5cblx0XHRcdFx0XHR2YXIgcmVzcG9uc2UgPSBldmVudC50YXJnZXQucmVzcG9uc2U7XG5cblx0XHRcdFx0XHRDYWNoZS5hZGQoIHVybCwgcmVzcG9uc2UgKTtcblxuXHRcdFx0XHRcdGlmICggdGhpcy5zdGF0dXMgPT09IDIwMCApIHtcblxuXHRcdFx0XHRcdFx0aWYgKCBvbkxvYWQgKSBvbkxvYWQoIHJlc3BvbnNlICk7XG5cblx0XHRcdFx0XHRcdHNjb3BlLm1hbmFnZXIuaXRlbUVuZCggdXJsICk7XG5cblx0XHRcdFx0XHR9IGVsc2UgaWYgKCB0aGlzLnN0YXR1cyA9PT0gMCApIHtcblxuXHRcdFx0XHRcdFx0Ly8gU29tZSBicm93c2VycyByZXR1cm4gSFRUUCBTdGF0dXMgMCB3aGVuIHVzaW5nIG5vbi1odHRwIHByb3RvY29sXG5cdFx0XHRcdFx0XHQvLyBlLmcuICdmaWxlOi8vJyBvciAnZGF0YTovLycuIEhhbmRsZSBhcyBzdWNjZXNzLlxuXG5cdFx0XHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5GaWxlTG9hZGVyOiBIVFRQIFN0YXR1cyAwIHJlY2VpdmVkLicgKTtcblxuXHRcdFx0XHRcdFx0aWYgKCBvbkxvYWQgKSBvbkxvYWQoIHJlc3BvbnNlICk7XG5cblx0XHRcdFx0XHRcdHNjb3BlLm1hbmFnZXIuaXRlbUVuZCggdXJsICk7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRpZiAoIG9uRXJyb3IgKSBvbkVycm9yKCBldmVudCApO1xuXG5cdFx0XHRcdFx0XHRzY29wZS5tYW5hZ2VyLml0ZW1FcnJvciggdXJsICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSwgZmFsc2UgKTtcblxuXHRcdFx0XHRpZiAoIG9uUHJvZ3Jlc3MgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdHJlcXVlc3QuYWRkRXZlbnRMaXN0ZW5lciggJ3Byb2dyZXNzJywgZnVuY3Rpb24gKCBldmVudCApIHtcblxuXHRcdFx0XHRcdFx0b25Qcm9ncmVzcyggZXZlbnQgKTtcblxuXHRcdFx0XHRcdH0sIGZhbHNlICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJlcXVlc3QuYWRkRXZlbnRMaXN0ZW5lciggJ2Vycm9yJywgZnVuY3Rpb24gKCBldmVudCApIHtcblxuXHRcdFx0XHRcdGlmICggb25FcnJvciApIG9uRXJyb3IoIGV2ZW50ICk7XG5cblx0XHRcdFx0XHRzY29wZS5tYW5hZ2VyLml0ZW1FcnJvciggdXJsICk7XG5cblx0XHRcdFx0fSwgZmFsc2UgKTtcblxuXHRcdFx0XHRpZiAoIHRoaXMucmVzcG9uc2VUeXBlICE9PSB1bmRlZmluZWQgKSByZXF1ZXN0LnJlc3BvbnNlVHlwZSA9IHRoaXMucmVzcG9uc2VUeXBlO1xuXHRcdFx0XHRpZiAoIHRoaXMud2l0aENyZWRlbnRpYWxzICE9PSB1bmRlZmluZWQgKSByZXF1ZXN0LndpdGhDcmVkZW50aWFscyA9IHRoaXMud2l0aENyZWRlbnRpYWxzO1xuXG5cdFx0XHRcdGlmICggcmVxdWVzdC5vdmVycmlkZU1pbWVUeXBlICkgcmVxdWVzdC5vdmVycmlkZU1pbWVUeXBlKCB0aGlzLm1pbWVUeXBlICE9PSB1bmRlZmluZWQgPyB0aGlzLm1pbWVUeXBlIDogJ3RleHQvcGxhaW4nICk7XG5cblx0XHRcdFx0cmVxdWVzdC5zZW5kKCBudWxsICk7XG5cblx0XHRcdH1cblxuXHRcdFx0c2NvcGUubWFuYWdlci5pdGVtU3RhcnQoIHVybCApO1xuXG5cdFx0XHRyZXR1cm4gcmVxdWVzdDtcblxuXHRcdH0sXG5cblx0XHRzZXRQYXRoOiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHR0aGlzLnBhdGggPSB2YWx1ZTtcblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldFJlc3BvbnNlVHlwZTogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0dGhpcy5yZXNwb25zZVR5cGUgPSB2YWx1ZTtcblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldFdpdGhDcmVkZW50aWFsczogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0dGhpcy53aXRoQ3JlZGVudGlhbHMgPSB2YWx1ZTtcblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldE1pbWVUeXBlOiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHR0aGlzLm1pbWVUeXBlID0gdmFsdWU7XG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqXG5cdCAqIEFic3RyYWN0IEJhc2UgY2xhc3MgdG8gYmxvY2sgYmFzZWQgdGV4dHVyZXMgbG9hZGVyIChkZHMsIHB2ciwgLi4uKVxuXHQgKi9cblxuXHRmdW5jdGlvbiBDb21wcmVzc2VkVGV4dHVyZUxvYWRlciggbWFuYWdlciApIHtcblxuXHRcdHRoaXMubWFuYWdlciA9ICggbWFuYWdlciAhPT0gdW5kZWZpbmVkICkgPyBtYW5hZ2VyIDogRGVmYXVsdExvYWRpbmdNYW5hZ2VyO1xuXG5cdFx0Ly8gb3ZlcnJpZGUgaW4gc3ViIGNsYXNzZXNcblx0XHR0aGlzLl9wYXJzZXIgPSBudWxsO1xuXG5cdH1cblxuXHRPYmplY3QuYXNzaWduKCBDb21wcmVzc2VkVGV4dHVyZUxvYWRlci5wcm90b3R5cGUsIHtcblxuXHRcdGxvYWQ6IGZ1bmN0aW9uICggdXJsLCBvbkxvYWQsIG9uUHJvZ3Jlc3MsIG9uRXJyb3IgKSB7XG5cblx0XHRcdHZhciBzY29wZSA9IHRoaXM7XG5cblx0XHRcdHZhciBpbWFnZXMgPSBbXTtcblxuXHRcdFx0dmFyIHRleHR1cmUgPSBuZXcgQ29tcHJlc3NlZFRleHR1cmUoKTtcblx0XHRcdHRleHR1cmUuaW1hZ2UgPSBpbWFnZXM7XG5cblx0XHRcdHZhciBsb2FkZXIgPSBuZXcgRmlsZUxvYWRlciggdGhpcy5tYW5hZ2VyICk7XG5cdFx0XHRsb2FkZXIuc2V0UGF0aCggdGhpcy5wYXRoICk7XG5cdFx0XHRsb2FkZXIuc2V0UmVzcG9uc2VUeXBlKCAnYXJyYXlidWZmZXInICk7XG5cblx0XHRcdGZ1bmN0aW9uIGxvYWRUZXh0dXJlKCBpICkge1xuXG5cdFx0XHRcdGxvYWRlci5sb2FkKCB1cmxbIGkgXSwgZnVuY3Rpb24gKCBidWZmZXIgKSB7XG5cblx0XHRcdFx0XHR2YXIgdGV4RGF0YXMgPSBzY29wZS5fcGFyc2VyKCBidWZmZXIsIHRydWUgKTtcblxuXHRcdFx0XHRcdGltYWdlc1sgaSBdID0ge1xuXHRcdFx0XHRcdFx0d2lkdGg6IHRleERhdGFzLndpZHRoLFxuXHRcdFx0XHRcdFx0aGVpZ2h0OiB0ZXhEYXRhcy5oZWlnaHQsXG5cdFx0XHRcdFx0XHRmb3JtYXQ6IHRleERhdGFzLmZvcm1hdCxcblx0XHRcdFx0XHRcdG1pcG1hcHM6IHRleERhdGFzLm1pcG1hcHNcblx0XHRcdFx0XHR9O1xuXG5cdFx0XHRcdFx0bG9hZGVkICs9IDE7XG5cblx0XHRcdFx0XHRpZiAoIGxvYWRlZCA9PT0gNiApIHtcblxuXHRcdFx0XHRcdFx0aWYgKCB0ZXhEYXRhcy5taXBtYXBDb3VudCA9PT0gMSApXG5cdFx0XHRcdFx0XHRcdHRleHR1cmUubWluRmlsdGVyID0gTGluZWFyRmlsdGVyO1xuXG5cdFx0XHRcdFx0XHR0ZXh0dXJlLmZvcm1hdCA9IHRleERhdGFzLmZvcm1hdDtcblx0XHRcdFx0XHRcdHRleHR1cmUubmVlZHNVcGRhdGUgPSB0cnVlO1xuXG5cdFx0XHRcdFx0XHRpZiAoIG9uTG9hZCApIG9uTG9hZCggdGV4dHVyZSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH0sIG9uUHJvZ3Jlc3MsIG9uRXJyb3IgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIEFycmF5LmlzQXJyYXkoIHVybCApICkge1xuXG5cdFx0XHRcdHZhciBsb2FkZWQgPSAwO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMCwgaWwgPSB1cmwubGVuZ3RoOyBpIDwgaWw7ICsrIGkgKSB7XG5cblx0XHRcdFx0XHRsb2FkVGV4dHVyZSggaSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHQvLyBjb21wcmVzc2VkIGN1YmVtYXAgdGV4dHVyZSBzdG9yZWQgaW4gYSBzaW5nbGUgRERTIGZpbGVcblxuXHRcdFx0XHRsb2FkZXIubG9hZCggdXJsLCBmdW5jdGlvbiAoIGJ1ZmZlciApIHtcblxuXHRcdFx0XHRcdHZhciB0ZXhEYXRhcyA9IHNjb3BlLl9wYXJzZXIoIGJ1ZmZlciwgdHJ1ZSApO1xuXG5cdFx0XHRcdFx0aWYgKCB0ZXhEYXRhcy5pc0N1YmVtYXAgKSB7XG5cblx0XHRcdFx0XHRcdHZhciBmYWNlcyA9IHRleERhdGFzLm1pcG1hcHMubGVuZ3RoIC8gdGV4RGF0YXMubWlwbWFwQ291bnQ7XG5cblx0XHRcdFx0XHRcdGZvciAoIHZhciBmID0gMDsgZiA8IGZhY2VzOyBmICsrICkge1xuXG5cdFx0XHRcdFx0XHRcdGltYWdlc1sgZiBdID0geyBtaXBtYXBzIDogW10gfTtcblxuXHRcdFx0XHRcdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCB0ZXhEYXRhcy5taXBtYXBDb3VudDsgaSArKyApIHtcblxuXHRcdFx0XHRcdFx0XHRcdGltYWdlc1sgZiBdLm1pcG1hcHMucHVzaCggdGV4RGF0YXMubWlwbWFwc1sgZiAqIHRleERhdGFzLm1pcG1hcENvdW50ICsgaSBdICk7XG5cdFx0XHRcdFx0XHRcdFx0aW1hZ2VzWyBmIF0uZm9ybWF0ID0gdGV4RGF0YXMuZm9ybWF0O1xuXHRcdFx0XHRcdFx0XHRcdGltYWdlc1sgZiBdLndpZHRoID0gdGV4RGF0YXMud2lkdGg7XG5cdFx0XHRcdFx0XHRcdFx0aW1hZ2VzWyBmIF0uaGVpZ2h0ID0gdGV4RGF0YXMuaGVpZ2h0O1xuXG5cdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0dGV4dHVyZS5pbWFnZS53aWR0aCA9IHRleERhdGFzLndpZHRoO1xuXHRcdFx0XHRcdFx0dGV4dHVyZS5pbWFnZS5oZWlnaHQgPSB0ZXhEYXRhcy5oZWlnaHQ7XG5cdFx0XHRcdFx0XHR0ZXh0dXJlLm1pcG1hcHMgPSB0ZXhEYXRhcy5taXBtYXBzO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0aWYgKCB0ZXhEYXRhcy5taXBtYXBDb3VudCA9PT0gMSApIHtcblxuXHRcdFx0XHRcdFx0dGV4dHVyZS5taW5GaWx0ZXIgPSBMaW5lYXJGaWx0ZXI7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR0ZXh0dXJlLmZvcm1hdCA9IHRleERhdGFzLmZvcm1hdDtcblx0XHRcdFx0XHR0ZXh0dXJlLm5lZWRzVXBkYXRlID0gdHJ1ZTtcblxuXHRcdFx0XHRcdGlmICggb25Mb2FkICkgb25Mb2FkKCB0ZXh0dXJlICk7XG5cblx0XHRcdFx0fSwgb25Qcm9ncmVzcywgb25FcnJvciApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0ZXh0dXJlO1xuXG5cdFx0fSxcblxuXHRcdHNldFBhdGg6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdHRoaXMucGF0aCA9IHZhbHVlO1xuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIE5pa29zIE0uIC8gaHR0cHM6Ly9naXRodWIuY29tL2ZvbzEyMy9cblx0ICpcblx0ICogQWJzdHJhY3QgQmFzZSBjbGFzcyB0byBsb2FkIGdlbmVyaWMgYmluYXJ5IHRleHR1cmVzIGZvcm1hdHMgKHJnYmUsIGhkciwgLi4uKVxuXHQgKi9cblxuXHR2YXIgRGF0YVRleHR1cmVMb2FkZXIgPSBCaW5hcnlUZXh0dXJlTG9hZGVyO1xuXHRmdW5jdGlvbiBCaW5hcnlUZXh0dXJlTG9hZGVyKCBtYW5hZ2VyICkge1xuXG5cdFx0dGhpcy5tYW5hZ2VyID0gKCBtYW5hZ2VyICE9PSB1bmRlZmluZWQgKSA/IG1hbmFnZXIgOiBEZWZhdWx0TG9hZGluZ01hbmFnZXI7XG5cblx0XHQvLyBvdmVycmlkZSBpbiBzdWIgY2xhc3Nlc1xuXHRcdHRoaXMuX3BhcnNlciA9IG51bGw7XG5cblx0fVxuXG5cdE9iamVjdC5hc3NpZ24oIEJpbmFyeVRleHR1cmVMb2FkZXIucHJvdG90eXBlLCB7XG5cblx0XHRsb2FkOiBmdW5jdGlvbiAoIHVybCwgb25Mb2FkLCBvblByb2dyZXNzLCBvbkVycm9yICkge1xuXG5cdFx0XHR2YXIgc2NvcGUgPSB0aGlzO1xuXG5cdFx0XHR2YXIgdGV4dHVyZSA9IG5ldyBEYXRhVGV4dHVyZSgpO1xuXG5cdFx0XHR2YXIgbG9hZGVyID0gbmV3IEZpbGVMb2FkZXIoIHRoaXMubWFuYWdlciApO1xuXHRcdFx0bG9hZGVyLnNldFJlc3BvbnNlVHlwZSggJ2FycmF5YnVmZmVyJyApO1xuXG5cdFx0XHRsb2FkZXIubG9hZCggdXJsLCBmdW5jdGlvbiAoIGJ1ZmZlciApIHtcblxuXHRcdFx0XHR2YXIgdGV4RGF0YSA9IHNjb3BlLl9wYXJzZXIoIGJ1ZmZlciApO1xuXG5cdFx0XHRcdGlmICggISB0ZXhEYXRhICkgcmV0dXJuO1xuXG5cdFx0XHRcdGlmICggdW5kZWZpbmVkICE9PSB0ZXhEYXRhLmltYWdlICkge1xuXG5cdFx0XHRcdFx0dGV4dHVyZS5pbWFnZSA9IHRleERhdGEuaW1hZ2U7XG5cblx0XHRcdFx0fSBlbHNlIGlmICggdW5kZWZpbmVkICE9PSB0ZXhEYXRhLmRhdGEgKSB7XG5cblx0XHRcdFx0XHR0ZXh0dXJlLmltYWdlLndpZHRoID0gdGV4RGF0YS53aWR0aDtcblx0XHRcdFx0XHR0ZXh0dXJlLmltYWdlLmhlaWdodCA9IHRleERhdGEuaGVpZ2h0O1xuXHRcdFx0XHRcdHRleHR1cmUuaW1hZ2UuZGF0YSA9IHRleERhdGEuZGF0YTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0dGV4dHVyZS53cmFwUyA9IHVuZGVmaW5lZCAhPT0gdGV4RGF0YS53cmFwUyA/IHRleERhdGEud3JhcFMgOiBDbGFtcFRvRWRnZVdyYXBwaW5nO1xuXHRcdFx0XHR0ZXh0dXJlLndyYXBUID0gdW5kZWZpbmVkICE9PSB0ZXhEYXRhLndyYXBUID8gdGV4RGF0YS53cmFwVCA6IENsYW1wVG9FZGdlV3JhcHBpbmc7XG5cblx0XHRcdFx0dGV4dHVyZS5tYWdGaWx0ZXIgPSB1bmRlZmluZWQgIT09IHRleERhdGEubWFnRmlsdGVyID8gdGV4RGF0YS5tYWdGaWx0ZXIgOiBMaW5lYXJGaWx0ZXI7XG5cdFx0XHRcdHRleHR1cmUubWluRmlsdGVyID0gdW5kZWZpbmVkICE9PSB0ZXhEYXRhLm1pbkZpbHRlciA/IHRleERhdGEubWluRmlsdGVyIDogTGluZWFyTWlwTWFwTGluZWFyRmlsdGVyO1xuXG5cdFx0XHRcdHRleHR1cmUuYW5pc290cm9weSA9IHVuZGVmaW5lZCAhPT0gdGV4RGF0YS5hbmlzb3Ryb3B5ID8gdGV4RGF0YS5hbmlzb3Ryb3B5IDogMTtcblxuXHRcdFx0XHRpZiAoIHVuZGVmaW5lZCAhPT0gdGV4RGF0YS5mb3JtYXQgKSB7XG5cblx0XHRcdFx0XHR0ZXh0dXJlLmZvcm1hdCA9IHRleERhdGEuZm9ybWF0O1xuXG5cdFx0XHRcdH1cblx0XHRcdFx0aWYgKCB1bmRlZmluZWQgIT09IHRleERhdGEudHlwZSApIHtcblxuXHRcdFx0XHRcdHRleHR1cmUudHlwZSA9IHRleERhdGEudHlwZTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCB1bmRlZmluZWQgIT09IHRleERhdGEubWlwbWFwcyApIHtcblxuXHRcdFx0XHRcdHRleHR1cmUubWlwbWFwcyA9IHRleERhdGEubWlwbWFwcztcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCAxID09PSB0ZXhEYXRhLm1pcG1hcENvdW50ICkge1xuXG5cdFx0XHRcdFx0dGV4dHVyZS5taW5GaWx0ZXIgPSBMaW5lYXJGaWx0ZXI7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHRleHR1cmUubmVlZHNVcGRhdGUgPSB0cnVlO1xuXG5cdFx0XHRcdGlmICggb25Mb2FkICkgb25Mb2FkKCB0ZXh0dXJlLCB0ZXhEYXRhICk7XG5cblx0XHRcdH0sIG9uUHJvZ3Jlc3MsIG9uRXJyb3IgKTtcblxuXG5cdFx0XHRyZXR1cm4gdGV4dHVyZTtcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEltYWdlTG9hZGVyKCBtYW5hZ2VyICkge1xuXG5cdFx0dGhpcy5tYW5hZ2VyID0gKCBtYW5hZ2VyICE9PSB1bmRlZmluZWQgKSA/IG1hbmFnZXIgOiBEZWZhdWx0TG9hZGluZ01hbmFnZXI7XG5cblx0fVxuXG5cdE9iamVjdC5hc3NpZ24oIEltYWdlTG9hZGVyLnByb3RvdHlwZSwge1xuXG5cdFx0bG9hZDogZnVuY3Rpb24gKCB1cmwsIG9uTG9hZCwgb25Qcm9ncmVzcywgb25FcnJvciApIHtcblxuXHRcdFx0dmFyIHNjb3BlID0gdGhpcztcblxuXHRcdFx0dmFyIGltYWdlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKCAnaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCcsICdpbWcnICk7XG5cdFx0XHRpbWFnZS5vbmxvYWQgPSBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdFx0aW1hZ2Uub25sb2FkID0gbnVsbDtcblxuXHRcdFx0XHRVUkwucmV2b2tlT2JqZWN0VVJMKCBpbWFnZS5zcmMgKTtcblxuXHRcdFx0XHRpZiAoIG9uTG9hZCApIG9uTG9hZCggaW1hZ2UgKTtcblxuXHRcdFx0XHRzY29wZS5tYW5hZ2VyLml0ZW1FbmQoIHVybCApO1xuXG5cdFx0XHR9O1xuXHRcdFx0aW1hZ2Uub25lcnJvciA9IG9uRXJyb3I7XG5cblx0XHRcdGlmICggdXJsLmluZGV4T2YoICdkYXRhOicgKSA9PT0gMCApIHtcblxuXHRcdFx0XHRpbWFnZS5zcmMgPSB1cmw7XG5cblx0XHRcdH0gZWxzZSBpZiAoIHRoaXMuY3Jvc3NPcmlnaW4gIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHQvLyBjcm9zc09yaWdpbiBkb2Vzbid0IHdvcmsgd2l0aCBVUkwuY3JlYXRlT2JqZWN0VVJMKCk/XG5cblx0XHRcdFx0aW1hZ2UuY3Jvc3NPcmlnaW4gPSB0aGlzLmNyb3NzT3JpZ2luO1xuXHRcdFx0XHRpbWFnZS5zcmMgPSB1cmw7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0dmFyIGxvYWRlciA9IG5ldyBGaWxlTG9hZGVyKCk7XG5cdFx0XHRcdGxvYWRlci5zZXRQYXRoKCB0aGlzLnBhdGggKTtcblx0XHRcdFx0bG9hZGVyLnNldFJlc3BvbnNlVHlwZSggJ2Jsb2InICk7XG5cdFx0XHRcdGxvYWRlci5zZXRXaXRoQ3JlZGVudGlhbHMoIHRoaXMud2l0aENyZWRlbnRpYWxzICk7XG5cdFx0XHRcdGxvYWRlci5sb2FkKCB1cmwsIGZ1bmN0aW9uICggYmxvYiApIHtcblxuXHRcdFx0XHRcdGltYWdlLnNyYyA9IFVSTC5jcmVhdGVPYmplY3RVUkwoIGJsb2IgKTtcblxuXHRcdFx0XHR9LCBvblByb2dyZXNzLCBvbkVycm9yICk7XG5cblx0XHRcdH1cblxuXHRcdFx0c2NvcGUubWFuYWdlci5pdGVtU3RhcnQoIHVybCApO1xuXG5cdFx0XHRyZXR1cm4gaW1hZ2U7XG5cblx0XHR9LFxuXG5cdFx0c2V0Q3Jvc3NPcmlnaW46IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdHRoaXMuY3Jvc3NPcmlnaW4gPSB2YWx1ZTtcblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldFdpdGhDcmVkZW50aWFsczogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0dGhpcy53aXRoQ3JlZGVudGlhbHMgPSB2YWx1ZTtcblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldFBhdGg6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdHRoaXMucGF0aCA9IHZhbHVlO1xuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBDdWJlVGV4dHVyZUxvYWRlciggbWFuYWdlciApIHtcblxuXHRcdHRoaXMubWFuYWdlciA9ICggbWFuYWdlciAhPT0gdW5kZWZpbmVkICkgPyBtYW5hZ2VyIDogRGVmYXVsdExvYWRpbmdNYW5hZ2VyO1xuXG5cdH1cblxuXHRPYmplY3QuYXNzaWduKCBDdWJlVGV4dHVyZUxvYWRlci5wcm90b3R5cGUsIHtcblxuXHRcdGxvYWQ6IGZ1bmN0aW9uICggdXJscywgb25Mb2FkLCBvblByb2dyZXNzLCBvbkVycm9yICkge1xuXG5cdFx0XHR2YXIgdGV4dHVyZSA9IG5ldyBDdWJlVGV4dHVyZSgpO1xuXG5cdFx0XHR2YXIgbG9hZGVyID0gbmV3IEltYWdlTG9hZGVyKCB0aGlzLm1hbmFnZXIgKTtcblx0XHRcdGxvYWRlci5zZXRDcm9zc09yaWdpbiggdGhpcy5jcm9zc09yaWdpbiApO1xuXHRcdFx0bG9hZGVyLnNldFBhdGgoIHRoaXMucGF0aCApO1xuXG5cdFx0XHR2YXIgbG9hZGVkID0gMDtcblxuXHRcdFx0ZnVuY3Rpb24gbG9hZFRleHR1cmUoIGkgKSB7XG5cblx0XHRcdFx0bG9hZGVyLmxvYWQoIHVybHNbIGkgXSwgZnVuY3Rpb24gKCBpbWFnZSApIHtcblxuXHRcdFx0XHRcdHRleHR1cmUuaW1hZ2VzWyBpIF0gPSBpbWFnZTtcblxuXHRcdFx0XHRcdGxvYWRlZCArKztcblxuXHRcdFx0XHRcdGlmICggbG9hZGVkID09PSA2ICkge1xuXG5cdFx0XHRcdFx0XHR0ZXh0dXJlLm5lZWRzVXBkYXRlID0gdHJ1ZTtcblxuXHRcdFx0XHRcdFx0aWYgKCBvbkxvYWQgKSBvbkxvYWQoIHRleHR1cmUgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9LCB1bmRlZmluZWQsIG9uRXJyb3IgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCB1cmxzLmxlbmd0aDsgKysgaSApIHtcblxuXHRcdFx0XHRsb2FkVGV4dHVyZSggaSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0ZXh0dXJlO1xuXG5cdFx0fSxcblxuXHRcdHNldENyb3NzT3JpZ2luOiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHR0aGlzLmNyb3NzT3JpZ2luID0gdmFsdWU7XG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRQYXRoOiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHR0aGlzLnBhdGggPSB2YWx1ZTtcblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fVxuXG5cdH0gKTtcblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gVGV4dHVyZUxvYWRlciggbWFuYWdlciApIHtcblxuXHRcdHRoaXMubWFuYWdlciA9ICggbWFuYWdlciAhPT0gdW5kZWZpbmVkICkgPyBtYW5hZ2VyIDogRGVmYXVsdExvYWRpbmdNYW5hZ2VyO1xuXG5cdH1cblxuXHRPYmplY3QuYXNzaWduKCBUZXh0dXJlTG9hZGVyLnByb3RvdHlwZSwge1xuXG5cdFx0bG9hZDogZnVuY3Rpb24gKCB1cmwsIG9uTG9hZCwgb25Qcm9ncmVzcywgb25FcnJvciApIHtcblxuXHRcdFx0dmFyIHRleHR1cmUgPSBuZXcgVGV4dHVyZSgpO1xuXG5cdFx0XHR2YXIgbG9hZGVyID0gbmV3IEltYWdlTG9hZGVyKCB0aGlzLm1hbmFnZXIgKTtcblx0XHRcdGxvYWRlci5zZXRDcm9zc09yaWdpbiggdGhpcy5jcm9zc09yaWdpbiApO1xuXHRcdFx0bG9hZGVyLnNldFdpdGhDcmVkZW50aWFscyggdGhpcy53aXRoQ3JlZGVudGlhbHMgKTtcblx0XHRcdGxvYWRlci5zZXRQYXRoKCB0aGlzLnBhdGggKTtcblx0XHRcdGxvYWRlci5sb2FkKCB1cmwsIGZ1bmN0aW9uICggaW1hZ2UgKSB7XG5cblx0XHRcdFx0Ly8gSlBFR3MgY2FuJ3QgaGF2ZSBhbiBhbHBoYSBjaGFubmVsLCBzbyBtZW1vcnkgY2FuIGJlIHNhdmVkIGJ5IHN0b3JpbmcgdGhlbSBhcyBSR0IuXG5cdFx0XHRcdHZhciBpc0pQRUcgPSB1cmwuc2VhcmNoKCAvXFwuKGpwZ3xqcGVnKSQvICkgPiAwIHx8IHVybC5zZWFyY2goIC9eZGF0YVxcOmltYWdlXFwvanBlZy8gKSA9PT0gMDtcblxuXHRcdFx0XHR0ZXh0dXJlLmZvcm1hdCA9IGlzSlBFRyA/IFJHQkZvcm1hdCA6IFJHQkFGb3JtYXQ7XG5cdFx0XHRcdHRleHR1cmUuaW1hZ2UgPSBpbWFnZTtcblx0XHRcdFx0dGV4dHVyZS5uZWVkc1VwZGF0ZSA9IHRydWU7XG5cblx0XHRcdFx0aWYgKCBvbkxvYWQgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdG9uTG9hZCggdGV4dHVyZSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fSwgb25Qcm9ncmVzcywgb25FcnJvciApO1xuXG5cdFx0XHRyZXR1cm4gdGV4dHVyZTtcblxuXHRcdH0sXG5cblx0XHRzZXRDcm9zc09yaWdpbjogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0dGhpcy5jcm9zc09yaWdpbiA9IHZhbHVlO1xuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0V2l0aENyZWRlbnRpYWxzOiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHR0aGlzLndpdGhDcmVkZW50aWFscyA9IHZhbHVlO1xuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0UGF0aDogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0dGhpcy5wYXRoID0gdmFsdWU7XG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH1cblxuXG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBMaWdodCggY29sb3IsIGludGVuc2l0eSApIHtcblxuXHRcdE9iamVjdDNELmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdMaWdodCc7XG5cblx0XHR0aGlzLmNvbG9yID0gbmV3IENvbG9yKCBjb2xvciApO1xuXHRcdHRoaXMuaW50ZW5zaXR5ID0gaW50ZW5zaXR5ICE9PSB1bmRlZmluZWQgPyBpbnRlbnNpdHkgOiAxO1xuXG5cdFx0dGhpcy5yZWNlaXZlU2hhZG93ID0gdW5kZWZpbmVkO1xuXG5cdH1cblxuXHRMaWdodC5wcm90b3R5cGUgPSBPYmplY3QuYXNzaWduKCBPYmplY3QuY3JlYXRlKCBPYmplY3QzRC5wcm90b3R5cGUgKSwge1xuXG5cdFx0Y29uc3RydWN0b3I6IExpZ2h0LFxuXG5cdFx0aXNMaWdodDogdHJ1ZSxcblxuXHRcdGNvcHk6IGZ1bmN0aW9uICggc291cmNlICkge1xuXG5cdFx0XHRPYmplY3QzRC5wcm90b3R5cGUuY29weS5jYWxsKCB0aGlzLCBzb3VyY2UgKTtcblxuXHRcdFx0dGhpcy5jb2xvci5jb3B5KCBzb3VyY2UuY29sb3IgKTtcblx0XHRcdHRoaXMuaW50ZW5zaXR5ID0gc291cmNlLmludGVuc2l0eTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0dG9KU09OOiBmdW5jdGlvbiAoIG1ldGEgKSB7XG5cblx0XHRcdHZhciBkYXRhID0gT2JqZWN0M0QucHJvdG90eXBlLnRvSlNPTi5jYWxsKCB0aGlzLCBtZXRhICk7XG5cblx0XHRcdGRhdGEub2JqZWN0LmNvbG9yID0gdGhpcy5jb2xvci5nZXRIZXgoKTtcblx0XHRcdGRhdGEub2JqZWN0LmludGVuc2l0eSA9IHRoaXMuaW50ZW5zaXR5O1xuXG5cdFx0XHRpZiAoIHRoaXMuZ3JvdW5kQ29sb3IgIT09IHVuZGVmaW5lZCApIGRhdGEub2JqZWN0Lmdyb3VuZENvbG9yID0gdGhpcy5ncm91bmRDb2xvci5nZXRIZXgoKTtcblxuXHRcdFx0aWYgKCB0aGlzLmRpc3RhbmNlICE9PSB1bmRlZmluZWQgKSBkYXRhLm9iamVjdC5kaXN0YW5jZSA9IHRoaXMuZGlzdGFuY2U7XG5cdFx0XHRpZiAoIHRoaXMuYW5nbGUgIT09IHVuZGVmaW5lZCApIGRhdGEub2JqZWN0LmFuZ2xlID0gdGhpcy5hbmdsZTtcblx0XHRcdGlmICggdGhpcy5kZWNheSAhPT0gdW5kZWZpbmVkICkgZGF0YS5vYmplY3QuZGVjYXkgPSB0aGlzLmRlY2F5O1xuXHRcdFx0aWYgKCB0aGlzLnBlbnVtYnJhICE9PSB1bmRlZmluZWQgKSBkYXRhLm9iamVjdC5wZW51bWJyYSA9IHRoaXMucGVudW1icmE7XG5cblx0XHRcdGlmICggdGhpcy5zaGFkb3cgIT09IHVuZGVmaW5lZCApIGRhdGEub2JqZWN0LnNoYWRvdyA9IHRoaXMuc2hhZG93LnRvSlNPTigpO1xuXG5cdFx0XHRyZXR1cm4gZGF0YTtcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEhlbWlzcGhlcmVMaWdodCggc2t5Q29sb3IsIGdyb3VuZENvbG9yLCBpbnRlbnNpdHkgKSB7XG5cblx0XHRMaWdodC5jYWxsKCB0aGlzLCBza3lDb2xvciwgaW50ZW5zaXR5ICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnSGVtaXNwaGVyZUxpZ2h0JztcblxuXHRcdHRoaXMuY2FzdFNoYWRvdyA9IHVuZGVmaW5lZDtcblxuXHRcdHRoaXMucG9zaXRpb24uY29weSggT2JqZWN0M0QuRGVmYXVsdFVwICk7XG5cdFx0dGhpcy51cGRhdGVNYXRyaXgoKTtcblxuXHRcdHRoaXMuZ3JvdW5kQ29sb3IgPSBuZXcgQ29sb3IoIGdyb3VuZENvbG9yICk7XG5cblx0fVxuXG5cdEhlbWlzcGhlcmVMaWdodC5wcm90b3R5cGUgPSBPYmplY3QuYXNzaWduKCBPYmplY3QuY3JlYXRlKCBMaWdodC5wcm90b3R5cGUgKSwge1xuXG5cdFx0Y29uc3RydWN0b3I6IEhlbWlzcGhlcmVMaWdodCxcblxuXHRcdGlzSGVtaXNwaGVyZUxpZ2h0OiB0cnVlLFxuXG5cdFx0Y29weTogZnVuY3Rpb24gKCBzb3VyY2UgKSB7XG5cblx0XHRcdExpZ2h0LnByb3RvdHlwZS5jb3B5LmNhbGwoIHRoaXMsIHNvdXJjZSApO1xuXG5cdFx0XHR0aGlzLmdyb3VuZENvbG9yLmNvcHkoIHNvdXJjZS5ncm91bmRDb2xvciApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIExpZ2h0U2hhZG93KCBjYW1lcmEgKSB7XG5cblx0XHR0aGlzLmNhbWVyYSA9IGNhbWVyYTtcblxuXHRcdHRoaXMuYmlhcyA9IDA7XG5cdFx0dGhpcy5yYWRpdXMgPSAxO1xuXG5cdFx0dGhpcy5tYXBTaXplID0gbmV3IFZlY3RvcjIoIDUxMiwgNTEyICk7XG5cblx0XHR0aGlzLm1hcCA9IG51bGw7XG5cdFx0dGhpcy5tYXRyaXggPSBuZXcgTWF0cml4NCgpO1xuXG5cdH1cblxuXHRPYmplY3QuYXNzaWduKCBMaWdodFNoYWRvdy5wcm90b3R5cGUsIHtcblxuXHRcdGNvcHk6IGZ1bmN0aW9uICggc291cmNlICkge1xuXG5cdFx0XHR0aGlzLmNhbWVyYSA9IHNvdXJjZS5jYW1lcmEuY2xvbmUoKTtcblxuXHRcdFx0dGhpcy5iaWFzID0gc291cmNlLmJpYXM7XG5cdFx0XHR0aGlzLnJhZGl1cyA9IHNvdXJjZS5yYWRpdXM7XG5cblx0XHRcdHRoaXMubWFwU2l6ZS5jb3B5KCBzb3VyY2UubWFwU2l6ZSApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjbG9uZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IHRoaXMuY29uc3RydWN0b3IoKS5jb3B5KCB0aGlzICk7XG5cblx0XHR9LFxuXG5cdFx0dG9KU09OOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBvYmplY3QgPSB7fTtcblxuXHRcdFx0aWYgKCB0aGlzLmJpYXMgIT09IDAgKSBvYmplY3QuYmlhcyA9IHRoaXMuYmlhcztcblx0XHRcdGlmICggdGhpcy5yYWRpdXMgIT09IDEgKSBvYmplY3QucmFkaXVzID0gdGhpcy5yYWRpdXM7XG5cdFx0XHRpZiAoIHRoaXMubWFwU2l6ZS54ICE9PSA1MTIgfHwgdGhpcy5tYXBTaXplLnkgIT09IDUxMiApIG9iamVjdC5tYXBTaXplID0gdGhpcy5tYXBTaXplLnRvQXJyYXkoKTtcblxuXHRcdFx0b2JqZWN0LmNhbWVyYSA9IHRoaXMuY2FtZXJhLnRvSlNPTiggZmFsc2UgKS5vYmplY3Q7XG5cdFx0XHRkZWxldGUgb2JqZWN0LmNhbWVyYS5tYXRyaXg7XG5cblx0XHRcdHJldHVybiBvYmplY3Q7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBTcG90TGlnaHRTaGFkb3coKSB7XG5cblx0XHRMaWdodFNoYWRvdy5jYWxsKCB0aGlzLCBuZXcgUGVyc3BlY3RpdmVDYW1lcmEoIDUwLCAxLCAwLjUsIDUwMCApICk7XG5cblx0fVxuXG5cdFNwb3RMaWdodFNoYWRvdy5wcm90b3R5cGUgPSBPYmplY3QuYXNzaWduKCBPYmplY3QuY3JlYXRlKCBMaWdodFNoYWRvdy5wcm90b3R5cGUgKSwge1xuXG5cdFx0Y29uc3RydWN0b3I6IFNwb3RMaWdodFNoYWRvdyxcblxuXHRcdGlzU3BvdExpZ2h0U2hhZG93OiB0cnVlLFxuXG5cdFx0dXBkYXRlOiBmdW5jdGlvbiAoIGxpZ2h0ICkge1xuXG5cdFx0XHR2YXIgZm92ID0gX01hdGguUkFEMkRFRyAqIDIgKiBsaWdodC5hbmdsZTtcblx0XHRcdHZhciBhc3BlY3QgPSB0aGlzLm1hcFNpemUud2lkdGggLyB0aGlzLm1hcFNpemUuaGVpZ2h0O1xuXHRcdFx0dmFyIGZhciA9IGxpZ2h0LmRpc3RhbmNlIHx8IDUwMDtcblxuXHRcdFx0dmFyIGNhbWVyYSA9IHRoaXMuY2FtZXJhO1xuXG5cdFx0XHRpZiAoIGZvdiAhPT0gY2FtZXJhLmZvdiB8fCBhc3BlY3QgIT09IGNhbWVyYS5hc3BlY3QgfHwgZmFyICE9PSBjYW1lcmEuZmFyICkge1xuXG5cdFx0XHRcdGNhbWVyYS5mb3YgPSBmb3Y7XG5cdFx0XHRcdGNhbWVyYS5hc3BlY3QgPSBhc3BlY3Q7XG5cdFx0XHRcdGNhbWVyYS5mYXIgPSBmYXI7XG5cdFx0XHRcdGNhbWVyYS51cGRhdGVQcm9qZWN0aW9uTWF0cml4KCk7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFNwb3RMaWdodCggY29sb3IsIGludGVuc2l0eSwgZGlzdGFuY2UsIGFuZ2xlLCBwZW51bWJyYSwgZGVjYXkgKSB7XG5cblx0XHRMaWdodC5jYWxsKCB0aGlzLCBjb2xvciwgaW50ZW5zaXR5ICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnU3BvdExpZ2h0JztcblxuXHRcdHRoaXMucG9zaXRpb24uY29weSggT2JqZWN0M0QuRGVmYXVsdFVwICk7XG5cdFx0dGhpcy51cGRhdGVNYXRyaXgoKTtcblxuXHRcdHRoaXMudGFyZ2V0ID0gbmV3IE9iamVjdDNEKCk7XG5cblx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoIHRoaXMsICdwb3dlcicsIHtcblx0XHRcdGdldDogZnVuY3Rpb24gKCkge1xuXHRcdFx0XHQvLyBpbnRlbnNpdHkgPSBwb3dlciBwZXIgc29saWQgYW5nbGUuXG5cdFx0XHRcdC8vIHJlZjogZXF1YXRpb24gKDE3KSBmcm9tIGh0dHA6Ly93d3cuZnJvc3RiaXRlLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNC8xMS9jb3Vyc2Vfbm90ZXNfbW92aW5nX2Zyb3N0Yml0ZV90b19wYnIucGRmXG5cdFx0XHRcdHJldHVybiB0aGlzLmludGVuc2l0eSAqIE1hdGguUEk7XG5cdFx0XHR9LFxuXHRcdFx0c2V0OiBmdW5jdGlvbiAoIHBvd2VyICkge1xuXHRcdFx0XHQvLyBpbnRlbnNpdHkgPSBwb3dlciBwZXIgc29saWQgYW5nbGUuXG5cdFx0XHRcdC8vIHJlZjogZXF1YXRpb24gKDE3KSBmcm9tIGh0dHA6Ly93d3cuZnJvc3RiaXRlLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNC8xMS9jb3Vyc2Vfbm90ZXNfbW92aW5nX2Zyb3N0Yml0ZV90b19wYnIucGRmXG5cdFx0XHRcdHRoaXMuaW50ZW5zaXR5ID0gcG93ZXIgLyBNYXRoLlBJO1xuXHRcdFx0fVxuXHRcdH0gKTtcblxuXHRcdHRoaXMuZGlzdGFuY2UgPSAoIGRpc3RhbmNlICE9PSB1bmRlZmluZWQgKSA/IGRpc3RhbmNlIDogMDtcblx0XHR0aGlzLmFuZ2xlID0gKCBhbmdsZSAhPT0gdW5kZWZpbmVkICkgPyBhbmdsZSA6IE1hdGguUEkgLyAzO1xuXHRcdHRoaXMucGVudW1icmEgPSAoIHBlbnVtYnJhICE9PSB1bmRlZmluZWQgKSA/IHBlbnVtYnJhIDogMDtcblx0XHR0aGlzLmRlY2F5ID0gKCBkZWNheSAhPT0gdW5kZWZpbmVkICkgPyBkZWNheSA6IDE7XHQvLyBmb3IgcGh5c2ljYWxseSBjb3JyZWN0IGxpZ2h0cywgc2hvdWxkIGJlIDIuXG5cblx0XHR0aGlzLnNoYWRvdyA9IG5ldyBTcG90TGlnaHRTaGFkb3coKTtcblxuXHR9XG5cblx0U3BvdExpZ2h0LnByb3RvdHlwZSA9IE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIExpZ2h0LnByb3RvdHlwZSApLCB7XG5cblx0XHRjb25zdHJ1Y3RvcjogU3BvdExpZ2h0LFxuXG5cdFx0aXNTcG90TGlnaHQ6IHRydWUsXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIHNvdXJjZSApIHtcblxuXHRcdFx0TGlnaHQucHJvdG90eXBlLmNvcHkuY2FsbCggdGhpcywgc291cmNlICk7XG5cblx0XHRcdHRoaXMuZGlzdGFuY2UgPSBzb3VyY2UuZGlzdGFuY2U7XG5cdFx0XHR0aGlzLmFuZ2xlID0gc291cmNlLmFuZ2xlO1xuXHRcdFx0dGhpcy5wZW51bWJyYSA9IHNvdXJjZS5wZW51bWJyYTtcblx0XHRcdHRoaXMuZGVjYXkgPSBzb3VyY2UuZGVjYXk7XG5cblx0XHRcdHRoaXMudGFyZ2V0ID0gc291cmNlLnRhcmdldC5jbG9uZSgpO1xuXG5cdFx0XHR0aGlzLnNoYWRvdyA9IHNvdXJjZS5zaGFkb3cuY2xvbmUoKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXG5cdGZ1bmN0aW9uIFBvaW50TGlnaHQoIGNvbG9yLCBpbnRlbnNpdHksIGRpc3RhbmNlLCBkZWNheSApIHtcblxuXHRcdExpZ2h0LmNhbGwoIHRoaXMsIGNvbG9yLCBpbnRlbnNpdHkgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdQb2ludExpZ2h0JztcblxuXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eSggdGhpcywgJ3Bvd2VyJywge1xuXHRcdFx0Z2V0OiBmdW5jdGlvbiAoKSB7XG5cdFx0XHRcdC8vIGludGVuc2l0eSA9IHBvd2VyIHBlciBzb2xpZCBhbmdsZS5cblx0XHRcdFx0Ly8gcmVmOiBlcXVhdGlvbiAoMTUpIGZyb20gaHR0cDovL3d3dy5mcm9zdGJpdGUuY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE0LzExL2NvdXJzZV9ub3Rlc19tb3ZpbmdfZnJvc3RiaXRlX3RvX3Bici5wZGZcblx0XHRcdFx0cmV0dXJuIHRoaXMuaW50ZW5zaXR5ICogNCAqIE1hdGguUEk7XG5cblx0XHRcdH0sXG5cdFx0XHRzZXQ6IGZ1bmN0aW9uICggcG93ZXIgKSB7XG5cdFx0XHRcdC8vIGludGVuc2l0eSA9IHBvd2VyIHBlciBzb2xpZCBhbmdsZS5cblx0XHRcdFx0Ly8gcmVmOiBlcXVhdGlvbiAoMTUpIGZyb20gaHR0cDovL3d3dy5mcm9zdGJpdGUuY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE0LzExL2NvdXJzZV9ub3Rlc19tb3ZpbmdfZnJvc3RiaXRlX3RvX3Bici5wZGZcblx0XHRcdFx0dGhpcy5pbnRlbnNpdHkgPSBwb3dlciAvICggNCAqIE1hdGguUEkgKTtcblx0XHRcdH1cblx0XHR9ICk7XG5cblx0XHR0aGlzLmRpc3RhbmNlID0gKCBkaXN0YW5jZSAhPT0gdW5kZWZpbmVkICkgPyBkaXN0YW5jZSA6IDA7XG5cdFx0dGhpcy5kZWNheSA9ICggZGVjYXkgIT09IHVuZGVmaW5lZCApID8gZGVjYXkgOiAxO1x0Ly8gZm9yIHBoeXNpY2FsbHkgY29ycmVjdCBsaWdodHMsIHNob3VsZCBiZSAyLlxuXG5cdFx0dGhpcy5zaGFkb3cgPSBuZXcgTGlnaHRTaGFkb3coIG5ldyBQZXJzcGVjdGl2ZUNhbWVyYSggOTAsIDEsIDAuNSwgNTAwICkgKTtcblxuXHR9XG5cblx0UG9pbnRMaWdodC5wcm90b3R5cGUgPSBPYmplY3QuYXNzaWduKCBPYmplY3QuY3JlYXRlKCBMaWdodC5wcm90b3R5cGUgKSwge1xuXG5cdFx0Y29uc3RydWN0b3I6IFBvaW50TGlnaHQsXG5cblx0XHRpc1BvaW50TGlnaHQ6IHRydWUsXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIHNvdXJjZSApIHtcblxuXHRcdFx0TGlnaHQucHJvdG90eXBlLmNvcHkuY2FsbCggdGhpcywgc291cmNlICk7XG5cblx0XHRcdHRoaXMuZGlzdGFuY2UgPSBzb3VyY2UuZGlzdGFuY2U7XG5cdFx0XHR0aGlzLmRlY2F5ID0gc291cmNlLmRlY2F5O1xuXG5cdFx0XHR0aGlzLnNoYWRvdyA9IHNvdXJjZS5zaGFkb3cuY2xvbmUoKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBEaXJlY3Rpb25hbExpZ2h0U2hhZG93KCBsaWdodCApIHtcblxuXHRcdExpZ2h0U2hhZG93LmNhbGwoIHRoaXMsIG5ldyBPcnRob2dyYXBoaWNDYW1lcmEoIC0gNSwgNSwgNSwgLSA1LCAwLjUsIDUwMCApICk7XG5cblx0fVxuXG5cdERpcmVjdGlvbmFsTGlnaHRTaGFkb3cucHJvdG90eXBlID0gT2JqZWN0LmFzc2lnbiggT2JqZWN0LmNyZWF0ZSggTGlnaHRTaGFkb3cucHJvdG90eXBlICksIHtcblxuXHRcdGNvbnN0cnVjdG9yOiBEaXJlY3Rpb25hbExpZ2h0U2hhZG93XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBEaXJlY3Rpb25hbExpZ2h0KCBjb2xvciwgaW50ZW5zaXR5ICkge1xuXG5cdFx0TGlnaHQuY2FsbCggdGhpcywgY29sb3IsIGludGVuc2l0eSApO1xuXG5cdFx0dGhpcy50eXBlID0gJ0RpcmVjdGlvbmFsTGlnaHQnO1xuXG5cdFx0dGhpcy5wb3NpdGlvbi5jb3B5KCBPYmplY3QzRC5EZWZhdWx0VXAgKTtcblx0XHR0aGlzLnVwZGF0ZU1hdHJpeCgpO1xuXG5cdFx0dGhpcy50YXJnZXQgPSBuZXcgT2JqZWN0M0QoKTtcblxuXHRcdHRoaXMuc2hhZG93ID0gbmV3IERpcmVjdGlvbmFsTGlnaHRTaGFkb3coKTtcblxuXHR9XG5cblx0RGlyZWN0aW9uYWxMaWdodC5wcm90b3R5cGUgPSBPYmplY3QuYXNzaWduKCBPYmplY3QuY3JlYXRlKCBMaWdodC5wcm90b3R5cGUgKSwge1xuXG5cdFx0Y29uc3RydWN0b3I6IERpcmVjdGlvbmFsTGlnaHQsXG5cblx0XHRpc0RpcmVjdGlvbmFsTGlnaHQ6IHRydWUsXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIHNvdXJjZSApIHtcblxuXHRcdFx0TGlnaHQucHJvdG90eXBlLmNvcHkuY2FsbCggdGhpcywgc291cmNlICk7XG5cblx0XHRcdHRoaXMudGFyZ2V0ID0gc291cmNlLnRhcmdldC5jbG9uZSgpO1xuXG5cdFx0XHR0aGlzLnNoYWRvdyA9IHNvdXJjZS5zaGFkb3cuY2xvbmUoKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBBbWJpZW50TGlnaHQoIGNvbG9yLCBpbnRlbnNpdHkgKSB7XG5cblx0XHRMaWdodC5jYWxsKCB0aGlzLCBjb2xvciwgaW50ZW5zaXR5ICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnQW1iaWVudExpZ2h0JztcblxuXHRcdHRoaXMuY2FzdFNoYWRvdyA9IHVuZGVmaW5lZDtcblxuXHR9XG5cblx0QW1iaWVudExpZ2h0LnByb3RvdHlwZSA9IE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIExpZ2h0LnByb3RvdHlwZSApLCB7XG5cblx0XHRjb25zdHJ1Y3RvcjogQW1iaWVudExpZ2h0LFxuXG5cdFx0aXNBbWJpZW50TGlnaHQ6IHRydWVcblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgdHNjaHdcblx0ICogQGF1dGhvciBCZW4gSG91c3RvbiAvIGh0dHA6Ly9jbGFyYS5pby9cblx0ICogQGF1dGhvciBEYXZpZCBTYXJubyAvIGh0dHA6Ly9saWdodGhhdXMudXMvXG5cdCAqL1xuXG5cdHZhciBBbmltYXRpb25VdGlscyA9IHtcblxuXHRcdC8vIHNhbWUgYXMgQXJyYXkucHJvdG90eXBlLnNsaWNlLCBidXQgYWxzbyB3b3JrcyBvbiB0eXBlZCBhcnJheXNcblx0XHRhcnJheVNsaWNlOiBmdW5jdGlvbiggYXJyYXksIGZyb20sIHRvICkge1xuXG5cdFx0XHRpZiAoIEFuaW1hdGlvblV0aWxzLmlzVHlwZWRBcnJheSggYXJyYXkgKSApIHtcblxuXHRcdFx0XHRyZXR1cm4gbmV3IGFycmF5LmNvbnN0cnVjdG9yKCBhcnJheS5zdWJhcnJheSggZnJvbSwgdG8gKSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBhcnJheS5zbGljZSggZnJvbSwgdG8gKTtcblxuXHRcdH0sXG5cblx0XHQvLyBjb252ZXJ0cyBhbiBhcnJheSB0byBhIHNwZWNpZmljIHR5cGVcblx0XHRjb252ZXJ0QXJyYXk6IGZ1bmN0aW9uKCBhcnJheSwgdHlwZSwgZm9yY2VDbG9uZSApIHtcblxuXHRcdFx0aWYgKCAhIGFycmF5IHx8IC8vIGxldCAndW5kZWZpbmVkJyBhbmQgJ251bGwnIHBhc3Ncblx0XHRcdFx0XHQhIGZvcmNlQ2xvbmUgJiYgYXJyYXkuY29uc3RydWN0b3IgPT09IHR5cGUgKSByZXR1cm4gYXJyYXk7XG5cblx0XHRcdGlmICggdHlwZW9mIHR5cGUuQllURVNfUEVSX0VMRU1FTlQgPT09ICdudW1iZXInICkge1xuXG5cdFx0XHRcdHJldHVybiBuZXcgdHlwZSggYXJyYXkgKTsgLy8gY3JlYXRlIHR5cGVkIGFycmF5XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKCBhcnJheSApOyAvLyBjcmVhdGUgQXJyYXlcblxuXHRcdH0sXG5cblx0XHRpc1R5cGVkQXJyYXk6IGZ1bmN0aW9uKCBvYmplY3QgKSB7XG5cblx0XHRcdHJldHVybiBBcnJheUJ1ZmZlci5pc1ZpZXcoIG9iamVjdCApICYmXG5cdFx0XHRcdFx0ISAoIG9iamVjdCBpbnN0YW5jZW9mIERhdGFWaWV3ICk7XG5cblx0XHR9LFxuXG5cdFx0Ly8gcmV0dXJucyBhbiBhcnJheSBieSB3aGljaCB0aW1lcyBhbmQgdmFsdWVzIGNhbiBiZSBzb3J0ZWRcblx0XHRnZXRLZXlmcmFtZU9yZGVyOiBmdW5jdGlvbiggdGltZXMgKSB7XG5cblx0XHRcdGZ1bmN0aW9uIGNvbXBhcmVUaW1lKCBpLCBqICkge1xuXG5cdFx0XHRcdHJldHVybiB0aW1lc1sgaSBdIC0gdGltZXNbIGogXTtcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgbiA9IHRpbWVzLmxlbmd0aDtcblx0XHRcdHZhciByZXN1bHQgPSBuZXcgQXJyYXkoIG4gKTtcblx0XHRcdGZvciAoIHZhciBpID0gMDsgaSAhPT0gbjsgKysgaSApIHJlc3VsdFsgaSBdID0gaTtcblxuXHRcdFx0cmVzdWx0LnNvcnQoIGNvbXBhcmVUaW1lICk7XG5cblx0XHRcdHJldHVybiByZXN1bHQ7XG5cblx0XHR9LFxuXG5cdFx0Ly8gdXNlcyB0aGUgYXJyYXkgcHJldmlvdXNseSByZXR1cm5lZCBieSAnZ2V0S2V5ZnJhbWVPcmRlcicgdG8gc29ydCBkYXRhXG5cdFx0c29ydGVkQXJyYXk6IGZ1bmN0aW9uKCB2YWx1ZXMsIHN0cmlkZSwgb3JkZXIgKSB7XG5cblx0XHRcdHZhciBuVmFsdWVzID0gdmFsdWVzLmxlbmd0aDtcblx0XHRcdHZhciByZXN1bHQgPSBuZXcgdmFsdWVzLmNvbnN0cnVjdG9yKCBuVmFsdWVzICk7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgZHN0T2Zmc2V0ID0gMDsgZHN0T2Zmc2V0ICE9PSBuVmFsdWVzOyArKyBpICkge1xuXG5cdFx0XHRcdHZhciBzcmNPZmZzZXQgPSBvcmRlclsgaSBdICogc3RyaWRlO1xuXG5cdFx0XHRcdGZvciAoIHZhciBqID0gMDsgaiAhPT0gc3RyaWRlOyArKyBqICkge1xuXG5cdFx0XHRcdFx0cmVzdWx0WyBkc3RPZmZzZXQgKysgXSA9IHZhbHVlc1sgc3JjT2Zmc2V0ICsgaiBdO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gcmVzdWx0O1xuXG5cdFx0fSxcblxuXHRcdC8vIGZ1bmN0aW9uIGZvciBwYXJzaW5nIEFPUyBrZXlmcmFtZSBmb3JtYXRzXG5cdFx0ZmxhdHRlbkpTT046IGZ1bmN0aW9uKCBqc29uS2V5cywgdGltZXMsIHZhbHVlcywgdmFsdWVQcm9wZXJ0eU5hbWUgKSB7XG5cblx0XHRcdHZhciBpID0gMSwga2V5ID0ganNvbktleXNbIDAgXTtcblxuXHRcdFx0d2hpbGUgKCBrZXkgIT09IHVuZGVmaW5lZCAmJiBrZXlbIHZhbHVlUHJvcGVydHlOYW1lIF0gPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRrZXkgPSBqc29uS2V5c1sgaSArKyBdO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICgga2V5ID09PSB1bmRlZmluZWQgKSByZXR1cm47IC8vIG5vIGRhdGFcblxuXHRcdFx0dmFyIHZhbHVlID0ga2V5WyB2YWx1ZVByb3BlcnR5TmFtZSBdO1xuXHRcdFx0aWYgKCB2YWx1ZSA9PT0gdW5kZWZpbmVkICkgcmV0dXJuOyAvLyBubyBkYXRhXG5cblx0XHRcdGlmICggQXJyYXkuaXNBcnJheSggdmFsdWUgKSApIHtcblxuXHRcdFx0XHRkbyB7XG5cblx0XHRcdFx0XHR2YWx1ZSA9IGtleVsgdmFsdWVQcm9wZXJ0eU5hbWUgXTtcblxuXHRcdFx0XHRcdGlmICggdmFsdWUgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdFx0dGltZXMucHVzaCgga2V5LnRpbWUgKTtcblx0XHRcdFx0XHRcdHZhbHVlcy5wdXNoLmFwcGx5KCB2YWx1ZXMsIHZhbHVlICk7IC8vIHB1c2ggYWxsIGVsZW1lbnRzXG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRrZXkgPSBqc29uS2V5c1sgaSArKyBdO1xuXG5cdFx0XHRcdH0gd2hpbGUgKCBrZXkgIT09IHVuZGVmaW5lZCApO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCB2YWx1ZS50b0FycmF5ICE9PSB1bmRlZmluZWQgKSB7XG5cdFx0XHRcdC8vIC4uLmFzc3VtZSBUSFJFRS5NYXRoLWlzaFxuXG5cdFx0XHRcdGRvIHtcblxuXHRcdFx0XHRcdHZhbHVlID0ga2V5WyB2YWx1ZVByb3BlcnR5TmFtZSBdO1xuXG5cdFx0XHRcdFx0aWYgKCB2YWx1ZSAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0XHR0aW1lcy5wdXNoKCBrZXkudGltZSApO1xuXHRcdFx0XHRcdFx0dmFsdWUudG9BcnJheSggdmFsdWVzLCB2YWx1ZXMubGVuZ3RoICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRrZXkgPSBqc29uS2V5c1sgaSArKyBdO1xuXG5cdFx0XHRcdH0gd2hpbGUgKCBrZXkgIT09IHVuZGVmaW5lZCApO1xuXG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHQvLyBvdGhlcndpc2UgcHVzaCBhcy1pc1xuXG5cdFx0XHRcdGRvIHtcblxuXHRcdFx0XHRcdHZhbHVlID0ga2V5WyB2YWx1ZVByb3BlcnR5TmFtZSBdO1xuXG5cdFx0XHRcdFx0aWYgKCB2YWx1ZSAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0XHR0aW1lcy5wdXNoKCBrZXkudGltZSApO1xuXHRcdFx0XHRcdFx0dmFsdWVzLnB1c2goIHZhbHVlICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRrZXkgPSBqc29uS2V5c1sgaSArKyBdO1xuXG5cdFx0XHRcdH0gd2hpbGUgKCBrZXkgIT09IHVuZGVmaW5lZCApO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0fTtcblxuXHQvKipcblx0ICogQWJzdHJhY3QgYmFzZSBjbGFzcyBvZiBpbnRlcnBvbGFudHMgb3ZlciBwYXJhbWV0cmljIHNhbXBsZXMuXG5cdCAqXG5cdCAqIFRoZSBwYXJhbWV0ZXIgZG9tYWluIGlzIG9uZSBkaW1lbnNpb25hbCwgdHlwaWNhbGx5IHRoZSB0aW1lIG9yIGEgcGF0aFxuXHQgKiBhbG9uZyBhIGN1cnZlIGRlZmluZWQgYnkgdGhlIGRhdGEuXG5cdCAqXG5cdCAqIFRoZSBzYW1wbGUgdmFsdWVzIGNhbiBoYXZlIGFueSBkaW1lbnNpb25hbGl0eSBhbmQgZGVyaXZlZCBjbGFzc2VzIG1heVxuXHQgKiBhcHBseSBzcGVjaWFsIGludGVycHJldGF0aW9ucyB0byB0aGUgZGF0YS5cblx0ICpcblx0ICogVGhpcyBjbGFzcyBwcm92aWRlcyB0aGUgaW50ZXJ2YWwgc2VlayBpbiBhIFRlbXBsYXRlIE1ldGhvZCwgZGVmZXJyaW5nXG5cdCAqIHRoZSBhY3R1YWwgaW50ZXJwb2xhdGlvbiB0byBkZXJpdmVkIGNsYXNzZXMuXG5cdCAqXG5cdCAqIFRpbWUgY29tcGxleGl0eSBpcyBPKDEpIGZvciBsaW5lYXIgYWNjZXNzIGNyb3NzaW5nIGF0IG1vc3QgdHdvIHBvaW50c1xuXHQgKiBhbmQgTyhsb2cgTikgZm9yIHJhbmRvbSBhY2Nlc3MsIHdoZXJlIE4gaXMgdGhlIG51bWJlciBvZiBwb3NpdGlvbnMuXG5cdCAqXG5cdCAqIFJlZmVyZW5jZXM6XG5cdCAqXG5cdCAqIFx0XHRodHRwOi8vd3d3Lm9vZGVzaWduLmNvbS90ZW1wbGF0ZS1tZXRob2QtcGF0dGVybi5odG1sXG5cdCAqXG5cdCAqIEBhdXRob3IgdHNjaHdcblx0ICovXG5cblx0ZnVuY3Rpb24gSW50ZXJwb2xhbnQoXG5cdFx0XHRwYXJhbWV0ZXJQb3NpdGlvbnMsIHNhbXBsZVZhbHVlcywgc2FtcGxlU2l6ZSwgcmVzdWx0QnVmZmVyICkge1xuXG5cdFx0dGhpcy5wYXJhbWV0ZXJQb3NpdGlvbnMgPSBwYXJhbWV0ZXJQb3NpdGlvbnM7XG5cdFx0dGhpcy5fY2FjaGVkSW5kZXggPSAwO1xuXG5cdFx0dGhpcy5yZXN1bHRCdWZmZXIgPSByZXN1bHRCdWZmZXIgIT09IHVuZGVmaW5lZCA/XG5cdFx0XHRcdHJlc3VsdEJ1ZmZlciA6IG5ldyBzYW1wbGVWYWx1ZXMuY29uc3RydWN0b3IoIHNhbXBsZVNpemUgKTtcblx0XHR0aGlzLnNhbXBsZVZhbHVlcyA9IHNhbXBsZVZhbHVlcztcblx0XHR0aGlzLnZhbHVlU2l6ZSA9IHNhbXBsZVNpemU7XG5cblx0fVxuXG5cdEludGVycG9sYW50LnByb3RvdHlwZSA9IHtcblxuXHRcdGNvbnN0cnVjdG9yOiBJbnRlcnBvbGFudCxcblxuXHRcdGV2YWx1YXRlOiBmdW5jdGlvbiggdCApIHtcblxuXHRcdFx0dmFyIHBwID0gdGhpcy5wYXJhbWV0ZXJQb3NpdGlvbnMsXG5cdFx0XHRcdGkxID0gdGhpcy5fY2FjaGVkSW5kZXgsXG5cblx0XHRcdFx0dDEgPSBwcFsgICBpMSAgIF0sXG5cdFx0XHRcdHQwID0gcHBbIGkxIC0gMSBdO1xuXG5cdFx0XHR2YWxpZGF0ZV9pbnRlcnZhbDoge1xuXG5cdFx0XHRcdHNlZWs6IHtcblxuXHRcdFx0XHRcdHZhciByaWdodDtcblxuXHRcdFx0XHRcdGxpbmVhcl9zY2FuOiB7XG5cdC8vLSBTZWUgaHR0cDovL2pzcGVyZi5jb20vY29tcGFyaXNvbi10by11bmRlZmluZWQvM1xuXHQvLy0gc2xvd2VyIGNvZGU6XG5cdC8vLVxuXHQvLy0gXHRcdFx0XHRpZiAoIHQgPj0gdDEgfHwgdDEgPT09IHVuZGVmaW5lZCApIHtcblx0XHRcdFx0XHRcdGZvcndhcmRfc2NhbjogaWYgKCAhICggdCA8IHQxICkgKSB7XG5cblx0XHRcdFx0XHRcdFx0Zm9yICggdmFyIGdpdmVVcEF0ID0gaTEgKyAyOyA7KSB7XG5cblx0XHRcdFx0XHRcdFx0XHRpZiAoIHQxID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHRcdGlmICggdCA8IHQwICkgYnJlYWsgZm9yd2FyZF9zY2FuO1xuXG5cdFx0XHRcdFx0XHRcdFx0XHQvLyBhZnRlciBlbmRcblxuXHRcdFx0XHRcdFx0XHRcdFx0aTEgPSBwcC5sZW5ndGg7XG5cdFx0XHRcdFx0XHRcdFx0XHR0aGlzLl9jYWNoZWRJbmRleCA9IGkxO1xuXHRcdFx0XHRcdFx0XHRcdFx0cmV0dXJuIHRoaXMuYWZ0ZXJFbmRfKCBpMSAtIDEsIHQsIHQwICk7XG5cblx0XHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdFx0XHRpZiAoIGkxID09PSBnaXZlVXBBdCApIGJyZWFrOyAvLyB0aGlzIGxvb3BcblxuXHRcdFx0XHRcdFx0XHRcdHQwID0gdDE7XG5cdFx0XHRcdFx0XHRcdFx0dDEgPSBwcFsgKysgaTEgXTtcblxuXHRcdFx0XHRcdFx0XHRcdGlmICggdCA8IHQxICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0XHQvLyB3ZSBoYXZlIGFycml2ZWQgYXQgdGhlIHNvdWdodCBpbnRlcnZhbFxuXHRcdFx0XHRcdFx0XHRcdFx0YnJlYWsgc2VlaztcblxuXHRcdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdFx0Ly8gcHJlcGFyZSBiaW5hcnkgc2VhcmNoIG9uIHRoZSByaWdodCBzaWRlIG9mIHRoZSBpbmRleFxuXHRcdFx0XHRcdFx0XHRyaWdodCA9IHBwLmxlbmd0aDtcblx0XHRcdFx0XHRcdFx0YnJlYWsgbGluZWFyX3NjYW47XG5cblx0XHRcdFx0XHRcdH1cblxuXHQvLy0gc2xvd2VyIGNvZGU6XG5cdC8vLVx0XHRcdFx0XHRpZiAoIHQgPCB0MCB8fCB0MCA9PT0gdW5kZWZpbmVkICkge1xuXHRcdFx0XHRcdFx0aWYgKCAhICggdCA+PSB0MCApICkge1xuXG5cdFx0XHRcdFx0XHRcdC8vIGxvb3Bpbmc/XG5cblx0XHRcdFx0XHRcdFx0dmFyIHQxZ2xvYmFsID0gcHBbIDEgXTtcblxuXHRcdFx0XHRcdFx0XHRpZiAoIHQgPCB0MWdsb2JhbCApIHtcblxuXHRcdFx0XHRcdFx0XHRcdGkxID0gMjsgLy8gKyAxLCB1c2luZyB0aGUgc2NhbiBmb3IgdGhlIGRldGFpbHNcblx0XHRcdFx0XHRcdFx0XHR0MCA9IHQxZ2xvYmFsO1xuXG5cdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0XHQvLyBsaW5lYXIgcmV2ZXJzZSBzY2FuXG5cblx0XHRcdFx0XHRcdFx0Zm9yICggdmFyIGdpdmVVcEF0ID0gaTEgLSAyOyA7KSB7XG5cblx0XHRcdFx0XHRcdFx0XHRpZiAoIHQwID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHRcdC8vIGJlZm9yZSBzdGFydFxuXG5cdFx0XHRcdFx0XHRcdFx0XHR0aGlzLl9jYWNoZWRJbmRleCA9IDA7XG5cdFx0XHRcdFx0XHRcdFx0XHRyZXR1cm4gdGhpcy5iZWZvcmVTdGFydF8oIDAsIHQsIHQxICk7XG5cblx0XHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdFx0XHRpZiAoIGkxID09PSBnaXZlVXBBdCApIGJyZWFrOyAvLyB0aGlzIGxvb3BcblxuXHRcdFx0XHRcdFx0XHRcdHQxID0gdDA7XG5cdFx0XHRcdFx0XHRcdFx0dDAgPSBwcFsgLS0gaTEgLSAxIF07XG5cblx0XHRcdFx0XHRcdFx0XHRpZiAoIHQgPj0gdDAgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHRcdC8vIHdlIGhhdmUgYXJyaXZlZCBhdCB0aGUgc291Z2h0IGludGVydmFsXG5cdFx0XHRcdFx0XHRcdFx0XHRicmVhayBzZWVrO1xuXG5cdFx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0XHQvLyBwcmVwYXJlIGJpbmFyeSBzZWFyY2ggb24gdGhlIGxlZnQgc2lkZSBvZiB0aGUgaW5kZXhcblx0XHRcdFx0XHRcdFx0cmlnaHQgPSBpMTtcblx0XHRcdFx0XHRcdFx0aTEgPSAwO1xuXHRcdFx0XHRcdFx0XHRicmVhayBsaW5lYXJfc2NhbjtcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHQvLyB0aGUgaW50ZXJ2YWwgaXMgdmFsaWRcblxuXHRcdFx0XHRcdFx0YnJlYWsgdmFsaWRhdGVfaW50ZXJ2YWw7XG5cblx0XHRcdFx0XHR9IC8vIGxpbmVhciBzY2FuXG5cblx0XHRcdFx0XHQvLyBiaW5hcnkgc2VhcmNoXG5cblx0XHRcdFx0XHR3aGlsZSAoIGkxIDwgcmlnaHQgKSB7XG5cblx0XHRcdFx0XHRcdHZhciBtaWQgPSAoIGkxICsgcmlnaHQgKSA+Pj4gMTtcblxuXHRcdFx0XHRcdFx0aWYgKCB0IDwgcHBbIG1pZCBdICkge1xuXG5cdFx0XHRcdFx0XHRcdHJpZ2h0ID0gbWlkO1xuXG5cdFx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRcdGkxID0gbWlkICsgMTtcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0dDEgPSBwcFsgICBpMSAgIF07XG5cdFx0XHRcdFx0dDAgPSBwcFsgaTEgLSAxIF07XG5cblx0XHRcdFx0XHQvLyBjaGVjayBib3VuZGFyeSBjYXNlcywgYWdhaW5cblxuXHRcdFx0XHRcdGlmICggdDAgPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdFx0dGhpcy5fY2FjaGVkSW5kZXggPSAwO1xuXHRcdFx0XHRcdFx0cmV0dXJuIHRoaXMuYmVmb3JlU3RhcnRfKCAwLCB0LCB0MSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0aWYgKCB0MSA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0XHRpMSA9IHBwLmxlbmd0aDtcblx0XHRcdFx0XHRcdHRoaXMuX2NhY2hlZEluZGV4ID0gaTE7XG5cdFx0XHRcdFx0XHRyZXR1cm4gdGhpcy5hZnRlckVuZF8oIGkxIC0gMSwgdDAsIHQgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9IC8vIHNlZWtcblxuXHRcdFx0XHR0aGlzLl9jYWNoZWRJbmRleCA9IGkxO1xuXG5cdFx0XHRcdHRoaXMuaW50ZXJ2YWxDaGFuZ2VkXyggaTEsIHQwLCB0MSApO1xuXG5cdFx0XHR9IC8vIHZhbGlkYXRlX2ludGVydmFsXG5cblx0XHRcdHJldHVybiB0aGlzLmludGVycG9sYXRlXyggaTEsIHQwLCB0LCB0MSApO1xuXG5cdFx0fSxcblxuXHRcdHNldHRpbmdzOiBudWxsLCAvLyBvcHRpb25hbCwgc3ViY2xhc3Mtc3BlY2lmaWMgc2V0dGluZ3Mgc3RydWN0dXJlXG5cdFx0Ly8gTm90ZTogVGhlIGluZGlyZWN0aW9uIGFsbG93cyBjZW50cmFsIGNvbnRyb2wgb2YgbWFueSBpbnRlcnBvbGFudHMuXG5cblx0XHQvLyAtLS0gUHJvdGVjdGVkIGludGVyZmFjZVxuXG5cdFx0RGVmYXVsdFNldHRpbmdzXzoge30sXG5cblx0XHRnZXRTZXR0aW5nc186IGZ1bmN0aW9uKCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5zZXR0aW5ncyB8fCB0aGlzLkRlZmF1bHRTZXR0aW5nc187XG5cblx0XHR9LFxuXG5cdFx0Y29weVNhbXBsZVZhbHVlXzogZnVuY3Rpb24oIGluZGV4ICkge1xuXG5cdFx0XHQvLyBjb3BpZXMgYSBzYW1wbGUgdmFsdWUgdG8gdGhlIHJlc3VsdCBidWZmZXJcblxuXHRcdFx0dmFyIHJlc3VsdCA9IHRoaXMucmVzdWx0QnVmZmVyLFxuXHRcdFx0XHR2YWx1ZXMgPSB0aGlzLnNhbXBsZVZhbHVlcyxcblx0XHRcdFx0c3RyaWRlID0gdGhpcy52YWx1ZVNpemUsXG5cdFx0XHRcdG9mZnNldCA9IGluZGV4ICogc3RyaWRlO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgIT09IHN0cmlkZTsgKysgaSApIHtcblxuXHRcdFx0XHRyZXN1bHRbIGkgXSA9IHZhbHVlc1sgb2Zmc2V0ICsgaSBdO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiByZXN1bHQ7XG5cblx0XHR9LFxuXG5cdFx0Ly8gVGVtcGxhdGUgbWV0aG9kcyBmb3IgZGVyaXZlZCBjbGFzc2VzOlxuXG5cdFx0aW50ZXJwb2xhdGVfOiBmdW5jdGlvbiggaTEsIHQwLCB0LCB0MSApIHtcblxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCBcImNhbGwgdG8gYWJzdHJhY3QgbWV0aG9kXCIgKTtcblx0XHRcdC8vIGltcGxlbWVudGF0aW9ucyBzaGFsbCByZXR1cm4gdGhpcy5yZXN1bHRCdWZmZXJcblxuXHRcdH0sXG5cblx0XHRpbnRlcnZhbENoYW5nZWRfOiBmdW5jdGlvbiggaTEsIHQwLCB0MSApIHtcblxuXHRcdFx0Ly8gZW1wdHlcblxuXHRcdH1cblxuXHR9O1xuXG5cdE9iamVjdC5hc3NpZ24oIEludGVycG9sYW50LnByb3RvdHlwZSwge1xuXG5cdFx0YmVmb3JlU3RhcnRfOiAvLyggMCwgdCwgdDAgKSwgcmV0dXJucyB0aGlzLnJlc3VsdEJ1ZmZlclxuXHRcdFx0SW50ZXJwb2xhbnQucHJvdG90eXBlLmNvcHlTYW1wbGVWYWx1ZV8sXG5cblx0XHRhZnRlckVuZF86IC8vKCBOLTEsIHROLTEsIHQgKSwgcmV0dXJucyB0aGlzLnJlc3VsdEJ1ZmZlclxuXHRcdFx0SW50ZXJwb2xhbnQucHJvdG90eXBlLmNvcHlTYW1wbGVWYWx1ZV9cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEZhc3QgYW5kIHNpbXBsZSBjdWJpYyBzcGxpbmUgaW50ZXJwb2xhbnQuXG5cdCAqXG5cdCAqIEl0IHdhcyBkZXJpdmVkIGZyb20gYSBIZXJtaXRpYW4gY29uc3RydWN0aW9uIHNldHRpbmcgdGhlIGZpcnN0IGRlcml2YXRpdmVcblx0ICogYXQgZWFjaCBzYW1wbGUgcG9zaXRpb24gdG8gdGhlIGxpbmVhciBzbG9wZSBiZXR3ZWVuIG5laWdoYm9yaW5nIHBvc2l0aW9uc1xuXHQgKiBvdmVyIHRoZWlyIHBhcmFtZXRlciBpbnRlcnZhbC5cblx0ICpcblx0ICogQGF1dGhvciB0c2Nod1xuXHQgKi9cblxuXHRmdW5jdGlvbiBDdWJpY0ludGVycG9sYW50KFxuXHRcdFx0cGFyYW1ldGVyUG9zaXRpb25zLCBzYW1wbGVWYWx1ZXMsIHNhbXBsZVNpemUsIHJlc3VsdEJ1ZmZlciApIHtcblxuXHRcdEludGVycG9sYW50LmNhbGwoXG5cdFx0XHRcdHRoaXMsIHBhcmFtZXRlclBvc2l0aW9ucywgc2FtcGxlVmFsdWVzLCBzYW1wbGVTaXplLCByZXN1bHRCdWZmZXIgKTtcblxuXHRcdHRoaXMuX3dlaWdodFByZXYgPSAtMDtcblx0XHR0aGlzLl9vZmZzZXRQcmV2ID0gLTA7XG5cdFx0dGhpcy5fd2VpZ2h0TmV4dCA9IC0wO1xuXHRcdHRoaXMuX29mZnNldE5leHQgPSAtMDtcblxuXHR9XG5cblx0Q3ViaWNJbnRlcnBvbGFudC5wcm90b3R5cGUgPVxuXHRcdFx0T2JqZWN0LmFzc2lnbiggT2JqZWN0LmNyZWF0ZSggSW50ZXJwb2xhbnQucHJvdG90eXBlICksIHtcblxuXHRcdGNvbnN0cnVjdG9yOiBDdWJpY0ludGVycG9sYW50LFxuXG5cdFx0RGVmYXVsdFNldHRpbmdzXzoge1xuXG5cdFx0XHRlbmRpbmdTdGFydDogXHRaZXJvQ3VydmF0dXJlRW5kaW5nLFxuXHRcdFx0ZW5kaW5nRW5kOlx0XHRaZXJvQ3VydmF0dXJlRW5kaW5nXG5cblx0XHR9LFxuXG5cdFx0aW50ZXJ2YWxDaGFuZ2VkXzogZnVuY3Rpb24oIGkxLCB0MCwgdDEgKSB7XG5cblx0XHRcdHZhciBwcCA9IHRoaXMucGFyYW1ldGVyUG9zaXRpb25zLFxuXHRcdFx0XHRpUHJldiA9IGkxIC0gMixcblx0XHRcdFx0aU5leHQgPSBpMSArIDEsXG5cblx0XHRcdFx0dFByZXYgPSBwcFsgaVByZXYgXSxcblx0XHRcdFx0dE5leHQgPSBwcFsgaU5leHQgXTtcblxuXHRcdFx0aWYgKCB0UHJldiA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdHN3aXRjaCAoIHRoaXMuZ2V0U2V0dGluZ3NfKCkuZW5kaW5nU3RhcnQgKSB7XG5cblx0XHRcdFx0XHRjYXNlIFplcm9TbG9wZUVuZGluZzpcblxuXHRcdFx0XHRcdFx0Ly8gZicodDApID0gMFxuXHRcdFx0XHRcdFx0aVByZXYgPSBpMTtcblx0XHRcdFx0XHRcdHRQcmV2ID0gMiAqIHQwIC0gdDE7XG5cblx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0Y2FzZSBXcmFwQXJvdW5kRW5kaW5nOlxuXG5cdFx0XHRcdFx0XHQvLyB1c2UgdGhlIG90aGVyIGVuZCBvZiB0aGUgY3VydmVcblx0XHRcdFx0XHRcdGlQcmV2ID0gcHAubGVuZ3RoIC0gMjtcblx0XHRcdFx0XHRcdHRQcmV2ID0gdDAgKyBwcFsgaVByZXYgXSAtIHBwWyBpUHJldiArIDEgXTtcblxuXHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRkZWZhdWx0OiAvLyBaZXJvQ3VydmF0dXJlRW5kaW5nXG5cblx0XHRcdFx0XHRcdC8vIGYnJyh0MCkgPSAwIGEuay5hLiBOYXR1cmFsIFNwbGluZVxuXHRcdFx0XHRcdFx0aVByZXYgPSBpMTtcblx0XHRcdFx0XHRcdHRQcmV2ID0gdDE7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggdE5leHQgPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRzd2l0Y2ggKCB0aGlzLmdldFNldHRpbmdzXygpLmVuZGluZ0VuZCApIHtcblxuXHRcdFx0XHRcdGNhc2UgWmVyb1Nsb3BlRW5kaW5nOlxuXG5cdFx0XHRcdFx0XHQvLyBmJyh0TikgPSAwXG5cdFx0XHRcdFx0XHRpTmV4dCA9IGkxO1xuXHRcdFx0XHRcdFx0dE5leHQgPSAyICogdDEgLSB0MDtcblxuXHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRjYXNlIFdyYXBBcm91bmRFbmRpbmc6XG5cblx0XHRcdFx0XHRcdC8vIHVzZSB0aGUgb3RoZXIgZW5kIG9mIHRoZSBjdXJ2ZVxuXHRcdFx0XHRcdFx0aU5leHQgPSAxO1xuXHRcdFx0XHRcdFx0dE5leHQgPSB0MSArIHBwWyAxIF0gLSBwcFsgMCBdO1xuXG5cdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdGRlZmF1bHQ6IC8vIFplcm9DdXJ2YXR1cmVFbmRpbmdcblxuXHRcdFx0XHRcdFx0Ly8gZicnKHROKSA9IDAsIGEuay5hLiBOYXR1cmFsIFNwbGluZVxuXHRcdFx0XHRcdFx0aU5leHQgPSBpMSAtIDE7XG5cdFx0XHRcdFx0XHR0TmV4dCA9IHQwO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgaGFsZkR0ID0gKCB0MSAtIHQwICkgKiAwLjUsXG5cdFx0XHRcdHN0cmlkZSA9IHRoaXMudmFsdWVTaXplO1xuXG5cdFx0XHR0aGlzLl93ZWlnaHRQcmV2ID0gaGFsZkR0IC8gKCB0MCAtIHRQcmV2ICk7XG5cdFx0XHR0aGlzLl93ZWlnaHROZXh0ID0gaGFsZkR0IC8gKCB0TmV4dCAtIHQxICk7XG5cdFx0XHR0aGlzLl9vZmZzZXRQcmV2ID0gaVByZXYgKiBzdHJpZGU7XG5cdFx0XHR0aGlzLl9vZmZzZXROZXh0ID0gaU5leHQgKiBzdHJpZGU7XG5cblx0XHR9LFxuXG5cdFx0aW50ZXJwb2xhdGVfOiBmdW5jdGlvbiggaTEsIHQwLCB0LCB0MSApIHtcblxuXHRcdFx0dmFyIHJlc3VsdCA9IHRoaXMucmVzdWx0QnVmZmVyLFxuXHRcdFx0XHR2YWx1ZXMgPSB0aGlzLnNhbXBsZVZhbHVlcyxcblx0XHRcdFx0c3RyaWRlID0gdGhpcy52YWx1ZVNpemUsXG5cblx0XHRcdFx0bzEgPSBpMSAqIHN0cmlkZSxcdFx0bzAgPSBvMSAtIHN0cmlkZSxcblx0XHRcdFx0b1AgPSB0aGlzLl9vZmZzZXRQcmV2LCBcdG9OID0gdGhpcy5fb2Zmc2V0TmV4dCxcblx0XHRcdFx0d1AgPSB0aGlzLl93ZWlnaHRQcmV2LFx0d04gPSB0aGlzLl93ZWlnaHROZXh0LFxuXG5cdFx0XHRcdHAgPSAoIHQgLSB0MCApIC8gKCB0MSAtIHQwICksXG5cdFx0XHRcdHBwID0gcCAqIHAsXG5cdFx0XHRcdHBwcCA9IHBwICogcDtcblxuXHRcdFx0Ly8gZXZhbHVhdGUgcG9seW5vbWlhbHNcblxuXHRcdFx0dmFyIHNQID0gICAgIC0gd1AgICAqIHBwcCAgICsgICAgICAgICAyICogd1AgICAgKiBwcCAgICAtICAgICAgICAgIHdQICAgKiBwO1xuXHRcdFx0dmFyIHMwID0gKCAxICsgd1AgKSAqIHBwcCAgICsgKC0xLjUgLSAyICogd1AgKSAgKiBwcCAgICArICggLTAuNSArIHdQICkgKiBwICAgICArIDE7XG5cdFx0XHR2YXIgczEgPSAoLTEgLSB3TiApICogcHBwICAgKyAoIDEuNSArICAgd04gICApICAqIHBwICAgICsgICAgMC41ICAgICAgICAqIHA7XG5cdFx0XHR2YXIgc04gPSAgICAgICB3TiAgICogcHBwICAgLSAgICAgICAgICAgd04gICAgICAqIHBwO1xuXG5cdFx0XHQvLyBjb21iaW5lIGRhdGEgbGluZWFybHlcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpICE9PSBzdHJpZGU7ICsrIGkgKSB7XG5cblx0XHRcdFx0cmVzdWx0WyBpIF0gPVxuXHRcdFx0XHRcdFx0c1AgKiB2YWx1ZXNbIG9QICsgaSBdICtcblx0XHRcdFx0XHRcdHMwICogdmFsdWVzWyBvMCArIGkgXSArXG5cdFx0XHRcdFx0XHRzMSAqIHZhbHVlc1sgbzEgKyBpIF0gK1xuXHRcdFx0XHRcdFx0c04gKiB2YWx1ZXNbIG9OICsgaSBdO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiByZXN1bHQ7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIHRzY2h3XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIExpbmVhckludGVycG9sYW50KFxuXHRcdFx0cGFyYW1ldGVyUG9zaXRpb25zLCBzYW1wbGVWYWx1ZXMsIHNhbXBsZVNpemUsIHJlc3VsdEJ1ZmZlciApIHtcblxuXHRcdEludGVycG9sYW50LmNhbGwoXG5cdFx0XHRcdHRoaXMsIHBhcmFtZXRlclBvc2l0aW9ucywgc2FtcGxlVmFsdWVzLCBzYW1wbGVTaXplLCByZXN1bHRCdWZmZXIgKTtcblxuXHR9XG5cblx0TGluZWFySW50ZXJwb2xhbnQucHJvdG90eXBlID1cblx0XHRcdE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIEludGVycG9sYW50LnByb3RvdHlwZSApLCB7XG5cblx0XHRjb25zdHJ1Y3RvcjogTGluZWFySW50ZXJwb2xhbnQsXG5cblx0XHRpbnRlcnBvbGF0ZV86IGZ1bmN0aW9uKCBpMSwgdDAsIHQsIHQxICkge1xuXG5cdFx0XHR2YXIgcmVzdWx0ID0gdGhpcy5yZXN1bHRCdWZmZXIsXG5cdFx0XHRcdHZhbHVlcyA9IHRoaXMuc2FtcGxlVmFsdWVzLFxuXHRcdFx0XHRzdHJpZGUgPSB0aGlzLnZhbHVlU2l6ZSxcblxuXHRcdFx0XHRvZmZzZXQxID0gaTEgKiBzdHJpZGUsXG5cdFx0XHRcdG9mZnNldDAgPSBvZmZzZXQxIC0gc3RyaWRlLFxuXG5cdFx0XHRcdHdlaWdodDEgPSAoIHQgLSB0MCApIC8gKCB0MSAtIHQwICksXG5cdFx0XHRcdHdlaWdodDAgPSAxIC0gd2VpZ2h0MTtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpICE9PSBzdHJpZGU7ICsrIGkgKSB7XG5cblx0XHRcdFx0cmVzdWx0WyBpIF0gPVxuXHRcdFx0XHRcdFx0dmFsdWVzWyBvZmZzZXQwICsgaSBdICogd2VpZ2h0MCArXG5cdFx0XHRcdFx0XHR2YWx1ZXNbIG9mZnNldDEgKyBpIF0gKiB3ZWlnaHQxO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiByZXN1bHQ7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKlxuXHQgKiBJbnRlcnBvbGFudCB0aGF0IGV2YWx1YXRlcyB0byB0aGUgc2FtcGxlIHZhbHVlIGF0IHRoZSBwb3NpdGlvbiBwcmVjZWVkaW5nXG5cdCAqIHRoZSBwYXJhbWV0ZXIuXG5cdCAqXG5cdCAqIEBhdXRob3IgdHNjaHdcblx0ICovXG5cblx0ZnVuY3Rpb24gRGlzY3JldGVJbnRlcnBvbGFudChcblx0XHRcdHBhcmFtZXRlclBvc2l0aW9ucywgc2FtcGxlVmFsdWVzLCBzYW1wbGVTaXplLCByZXN1bHRCdWZmZXIgKSB7XG5cblx0XHRJbnRlcnBvbGFudC5jYWxsKFxuXHRcdFx0XHR0aGlzLCBwYXJhbWV0ZXJQb3NpdGlvbnMsIHNhbXBsZVZhbHVlcywgc2FtcGxlU2l6ZSwgcmVzdWx0QnVmZmVyICk7XG5cblx0fVxuXG5cdERpc2NyZXRlSW50ZXJwb2xhbnQucHJvdG90eXBlID1cblx0XHRcdE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIEludGVycG9sYW50LnByb3RvdHlwZSApLCB7XG5cblx0XHRjb25zdHJ1Y3RvcjogRGlzY3JldGVJbnRlcnBvbGFudCxcblxuXHRcdGludGVycG9sYXRlXzogZnVuY3Rpb24oIGkxLCB0MCwgdCwgdDEgKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLmNvcHlTYW1wbGVWYWx1ZV8oIGkxIC0gMSApO1xuXG5cdFx0fVxuXG5cdH0gKTtcblxuXHR2YXIgS2V5ZnJhbWVUcmFja1Byb3RvdHlwZTtcblxuXHRLZXlmcmFtZVRyYWNrUHJvdG90eXBlID0ge1xuXG5cdFx0VGltZUJ1ZmZlclR5cGU6IEZsb2F0MzJBcnJheSxcblx0XHRWYWx1ZUJ1ZmZlclR5cGU6IEZsb2F0MzJBcnJheSxcblxuXHRcdERlZmF1bHRJbnRlcnBvbGF0aW9uOiBJbnRlcnBvbGF0ZUxpbmVhcixcblxuXHRcdEludGVycG9sYW50RmFjdG9yeU1ldGhvZERpc2NyZXRlOiBmdW5jdGlvbiAoIHJlc3VsdCApIHtcblxuXHRcdFx0cmV0dXJuIG5ldyBEaXNjcmV0ZUludGVycG9sYW50KFxuXHRcdFx0XHRcdHRoaXMudGltZXMsIHRoaXMudmFsdWVzLCB0aGlzLmdldFZhbHVlU2l6ZSgpLCByZXN1bHQgKTtcblxuXHRcdH0sXG5cblx0XHRJbnRlcnBvbGFudEZhY3RvcnlNZXRob2RMaW5lYXI6IGZ1bmN0aW9uICggcmVzdWx0ICkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IExpbmVhckludGVycG9sYW50KFxuXHRcdFx0XHRcdHRoaXMudGltZXMsIHRoaXMudmFsdWVzLCB0aGlzLmdldFZhbHVlU2l6ZSgpLCByZXN1bHQgKTtcblxuXHRcdH0sXG5cblx0XHRJbnRlcnBvbGFudEZhY3RvcnlNZXRob2RTbW9vdGg6IGZ1bmN0aW9uICggcmVzdWx0ICkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IEN1YmljSW50ZXJwb2xhbnQoXG5cdFx0XHRcdFx0dGhpcy50aW1lcywgdGhpcy52YWx1ZXMsIHRoaXMuZ2V0VmFsdWVTaXplKCksIHJlc3VsdCApO1xuXG5cdFx0fSxcblxuXHRcdHNldEludGVycG9sYXRpb246IGZ1bmN0aW9uICggaW50ZXJwb2xhdGlvbiApIHtcblxuXHRcdFx0dmFyIGZhY3RvcnlNZXRob2Q7XG5cblx0XHRcdHN3aXRjaCAoIGludGVycG9sYXRpb24gKSB7XG5cblx0XHRcdFx0Y2FzZSBJbnRlcnBvbGF0ZURpc2NyZXRlOlxuXG5cdFx0XHRcdFx0ZmFjdG9yeU1ldGhvZCA9IHRoaXMuSW50ZXJwb2xhbnRGYWN0b3J5TWV0aG9kRGlzY3JldGU7XG5cblx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRjYXNlIEludGVycG9sYXRlTGluZWFyOlxuXG5cdFx0XHRcdFx0ZmFjdG9yeU1ldGhvZCA9IHRoaXMuSW50ZXJwb2xhbnRGYWN0b3J5TWV0aG9kTGluZWFyO1xuXG5cdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0Y2FzZSBJbnRlcnBvbGF0ZVNtb290aDpcblxuXHRcdFx0XHRcdGZhY3RvcnlNZXRob2QgPSB0aGlzLkludGVycG9sYW50RmFjdG9yeU1ldGhvZFNtb290aDtcblxuXHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggZmFjdG9yeU1ldGhvZCA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdHZhciBtZXNzYWdlID0gXCJ1bnN1cHBvcnRlZCBpbnRlcnBvbGF0aW9uIGZvciBcIiArXG5cdFx0XHRcdFx0XHR0aGlzLlZhbHVlVHlwZU5hbWUgKyBcIiBrZXlmcmFtZSB0cmFjayBuYW1lZCBcIiArIHRoaXMubmFtZTtcblxuXHRcdFx0XHRpZiAoIHRoaXMuY3JlYXRlSW50ZXJwb2xhbnQgPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdC8vIGZhbGwgYmFjayB0byBkZWZhdWx0LCB1bmxlc3MgdGhlIGRlZmF1bHQgaXRzZWxmIGlzIG1lc3NlZCB1cFxuXHRcdFx0XHRcdGlmICggaW50ZXJwb2xhdGlvbiAhPT0gdGhpcy5EZWZhdWx0SW50ZXJwb2xhdGlvbiApIHtcblxuXHRcdFx0XHRcdFx0dGhpcy5zZXRJbnRlcnBvbGF0aW9uKCB0aGlzLkRlZmF1bHRJbnRlcnBvbGF0aW9uICk7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHR0aHJvdyBuZXcgRXJyb3IoIG1lc3NhZ2UgKTsgLy8gZmF0YWwsIGluIHRoaXMgY2FzZVxuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRjb25zb2xlLndhcm4oIG1lc3NhZ2UgKTtcblx0XHRcdFx0cmV0dXJuO1xuXG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuY3JlYXRlSW50ZXJwb2xhbnQgPSBmYWN0b3J5TWV0aG9kO1xuXG5cdFx0fSxcblxuXHRcdGdldEludGVycG9sYXRpb246IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0c3dpdGNoICggdGhpcy5jcmVhdGVJbnRlcnBvbGFudCApIHtcblxuXHRcdFx0XHRjYXNlIHRoaXMuSW50ZXJwb2xhbnRGYWN0b3J5TWV0aG9kRGlzY3JldGU6XG5cblx0XHRcdFx0XHRyZXR1cm4gSW50ZXJwb2xhdGVEaXNjcmV0ZTtcblxuXHRcdFx0XHRjYXNlIHRoaXMuSW50ZXJwb2xhbnRGYWN0b3J5TWV0aG9kTGluZWFyOlxuXG5cdFx0XHRcdFx0cmV0dXJuIEludGVycG9sYXRlTGluZWFyO1xuXG5cdFx0XHRcdGNhc2UgdGhpcy5JbnRlcnBvbGFudEZhY3RvcnlNZXRob2RTbW9vdGg6XG5cblx0XHRcdFx0XHRyZXR1cm4gSW50ZXJwb2xhdGVTbW9vdGg7XG5cblx0XHRcdH1cblxuXHRcdH0sXG5cblx0XHRnZXRWYWx1ZVNpemU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMudmFsdWVzLmxlbmd0aCAvIHRoaXMudGltZXMubGVuZ3RoO1xuXG5cdFx0fSxcblxuXHRcdC8vIG1vdmUgYWxsIGtleWZyYW1lcyBlaXRoZXIgZm9yd2FyZHMgb3IgYmFja3dhcmRzIGluIHRpbWVcblx0XHRzaGlmdDogZnVuY3Rpb24gKCB0aW1lT2Zmc2V0ICkge1xuXG5cdFx0XHRpZiAoIHRpbWVPZmZzZXQgIT09IDAuMCApIHtcblxuXHRcdFx0XHR2YXIgdGltZXMgPSB0aGlzLnRpbWVzO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbiA9IHRpbWVzLmxlbmd0aDsgaSAhPT0gbjsgKysgaSApIHtcblxuXHRcdFx0XHRcdHRpbWVzWyBpIF0gKz0gdGltZU9mZnNldDtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Ly8gc2NhbGUgYWxsIGtleWZyYW1lIHRpbWVzIGJ5IGEgZmFjdG9yICh1c2VmdWwgZm9yIGZyYW1lIDwtPiBzZWNvbmRzIGNvbnZlcnNpb25zKVxuXHRcdHNjYWxlOiBmdW5jdGlvbiAoIHRpbWVTY2FsZSApIHtcblxuXHRcdFx0aWYgKCB0aW1lU2NhbGUgIT09IDEuMCApIHtcblxuXHRcdFx0XHR2YXIgdGltZXMgPSB0aGlzLnRpbWVzO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbiA9IHRpbWVzLmxlbmd0aDsgaSAhPT0gbjsgKysgaSApIHtcblxuXHRcdFx0XHRcdHRpbWVzWyBpIF0gKj0gdGltZVNjYWxlO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHQvLyByZW1vdmVzIGtleWZyYW1lcyBiZWZvcmUgYW5kIGFmdGVyIGFuaW1hdGlvbiB3aXRob3V0IGNoYW5naW5nIGFueSB2YWx1ZXMgd2l0aGluIHRoZSByYW5nZSBbc3RhcnRUaW1lLCBlbmRUaW1lXS5cblx0XHQvLyBJTVBPUlRBTlQ6IFdlIGRvIG5vdCBzaGlmdCBhcm91bmQga2V5cyB0byB0aGUgc3RhcnQgb2YgdGhlIHRyYWNrIHRpbWUsIGJlY2F1c2UgZm9yIGludGVycG9sYXRlZCBrZXlzIHRoaXMgd2lsbCBjaGFuZ2UgdGhlaXIgdmFsdWVzXG5cdFx0dHJpbTogZnVuY3Rpb24gKCBzdGFydFRpbWUsIGVuZFRpbWUgKSB7XG5cblx0XHRcdHZhciB0aW1lcyA9IHRoaXMudGltZXMsXG5cdFx0XHRcdG5LZXlzID0gdGltZXMubGVuZ3RoLFxuXHRcdFx0XHRmcm9tID0gMCxcblx0XHRcdFx0dG8gPSBuS2V5cyAtIDE7XG5cblx0XHRcdHdoaWxlICggZnJvbSAhPT0gbktleXMgJiYgdGltZXNbIGZyb20gXSA8IHN0YXJ0VGltZSApICsrIGZyb207XG5cdFx0XHR3aGlsZSAoIHRvICE9PSAtIDEgJiYgdGltZXNbIHRvIF0gPiBlbmRUaW1lICkgLS0gdG87XG5cblx0XHRcdCsrIHRvOyAvLyBpbmNsdXNpdmUgLT4gZXhjbHVzaXZlIGJvdW5kXG5cblx0XHRcdGlmICggZnJvbSAhPT0gMCB8fCB0byAhPT0gbktleXMgKSB7XG5cblx0XHRcdFx0Ly8gZW1wdHkgdHJhY2tzIGFyZSBmb3JiaWRkZW4sIHNvIGtlZXAgYXQgbGVhc3Qgb25lIGtleWZyYW1lXG5cdFx0XHRcdGlmICggZnJvbSA+PSB0byApIHRvID0gTWF0aC5tYXgoIHRvLCAxICksIGZyb20gPSB0byAtIDE7XG5cblx0XHRcdFx0dmFyIHN0cmlkZSA9IHRoaXMuZ2V0VmFsdWVTaXplKCk7XG5cdFx0XHRcdHRoaXMudGltZXMgPSBBbmltYXRpb25VdGlscy5hcnJheVNsaWNlKCB0aW1lcywgZnJvbSwgdG8gKTtcblx0XHRcdFx0dGhpcy52YWx1ZXMgPSBBbmltYXRpb25VdGlscy5cblx0XHRcdFx0XHRcdGFycmF5U2xpY2UoIHRoaXMudmFsdWVzLCBmcm9tICogc3RyaWRlLCB0byAqIHN0cmlkZSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdC8vIGVuc3VyZSB3ZSBkbyBub3QgZ2V0IGEgR2FyYmFnZUluR2FyYmFnZU91dCBzaXR1YXRpb24sIG1ha2Ugc3VyZSB0cmFja3MgYXJlIGF0IGxlYXN0IG1pbmltYWxseSB2aWFibGVcblx0XHR2YWxpZGF0ZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgdmFsaWQgPSB0cnVlO1xuXG5cdFx0XHR2YXIgdmFsdWVTaXplID0gdGhpcy5nZXRWYWx1ZVNpemUoKTtcblx0XHRcdGlmICggdmFsdWVTaXplIC0gTWF0aC5mbG9vciggdmFsdWVTaXplICkgIT09IDAgKSB7XG5cblx0XHRcdFx0Y29uc29sZS5lcnJvciggXCJpbnZhbGlkIHZhbHVlIHNpemUgaW4gdHJhY2tcIiwgdGhpcyApO1xuXHRcdFx0XHR2YWxpZCA9IGZhbHNlO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciB0aW1lcyA9IHRoaXMudGltZXMsXG5cdFx0XHRcdHZhbHVlcyA9IHRoaXMudmFsdWVzLFxuXG5cdFx0XHRcdG5LZXlzID0gdGltZXMubGVuZ3RoO1xuXG5cdFx0XHRpZiAoIG5LZXlzID09PSAwICkge1xuXG5cdFx0XHRcdGNvbnNvbGUuZXJyb3IoIFwidHJhY2sgaXMgZW1wdHlcIiwgdGhpcyApO1xuXHRcdFx0XHR2YWxpZCA9IGZhbHNlO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBwcmV2VGltZSA9IG51bGw7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMDsgaSAhPT0gbktleXM7IGkgKysgKSB7XG5cblx0XHRcdFx0dmFyIGN1cnJUaW1lID0gdGltZXNbIGkgXTtcblxuXHRcdFx0XHRpZiAoIHR5cGVvZiBjdXJyVGltZSA9PT0gJ251bWJlcicgJiYgaXNOYU4oIGN1cnJUaW1lICkgKSB7XG5cblx0XHRcdFx0XHRjb25zb2xlLmVycm9yKCBcInRpbWUgaXMgbm90IGEgdmFsaWQgbnVtYmVyXCIsIHRoaXMsIGksIGN1cnJUaW1lICk7XG5cdFx0XHRcdFx0dmFsaWQgPSBmYWxzZTtcblx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCBwcmV2VGltZSAhPT0gbnVsbCAmJiBwcmV2VGltZSA+IGN1cnJUaW1lICkge1xuXG5cdFx0XHRcdFx0Y29uc29sZS5lcnJvciggXCJvdXQgb2Ygb3JkZXIga2V5c1wiLCB0aGlzLCBpLCBjdXJyVGltZSwgcHJldlRpbWUgKTtcblx0XHRcdFx0XHR2YWxpZCA9IGZhbHNlO1xuXHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRwcmV2VGltZSA9IGN1cnJUaW1lO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggdmFsdWVzICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0aWYgKCBBbmltYXRpb25VdGlscy5pc1R5cGVkQXJyYXkoIHZhbHVlcyApICkge1xuXG5cdFx0XHRcdFx0Zm9yICggdmFyIGkgPSAwLCBuID0gdmFsdWVzLmxlbmd0aDsgaSAhPT0gbjsgKysgaSApIHtcblxuXHRcdFx0XHRcdFx0dmFyIHZhbHVlID0gdmFsdWVzWyBpIF07XG5cblx0XHRcdFx0XHRcdGlmICggaXNOYU4oIHZhbHVlICkgKSB7XG5cblx0XHRcdFx0XHRcdFx0Y29uc29sZS5lcnJvciggXCJ2YWx1ZSBpcyBub3QgYSB2YWxpZCBudW1iZXJcIiwgdGhpcywgaSwgdmFsdWUgKTtcblx0XHRcdFx0XHRcdFx0dmFsaWQgPSBmYWxzZTtcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHZhbGlkO1xuXG5cdFx0fSxcblxuXHRcdC8vIHJlbW92ZXMgZXF1aXZhbGVudCBzZXF1ZW50aWFsIGtleXMgYXMgY29tbW9uIGluIG1vcnBoIHRhcmdldCBzZXF1ZW5jZXNcblx0XHQvLyAoMCwwLDAsMCwxLDEsMSwwLDAsMCwwLDAsMCwwKSAtLT4gKDAsMCwxLDEsMCwwKVxuXHRcdG9wdGltaXplOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB0aW1lcyA9IHRoaXMudGltZXMsXG5cdFx0XHRcdHZhbHVlcyA9IHRoaXMudmFsdWVzLFxuXHRcdFx0XHRzdHJpZGUgPSB0aGlzLmdldFZhbHVlU2l6ZSgpLFxuXG5cdFx0XHRcdHNtb290aEludGVycG9sYXRpb24gPSB0aGlzLmdldEludGVycG9sYXRpb24oKSA9PT0gSW50ZXJwb2xhdGVTbW9vdGgsXG5cblx0XHRcdFx0d3JpdGVJbmRleCA9IDEsXG5cdFx0XHRcdGxhc3RJbmRleCA9IHRpbWVzLmxlbmd0aCAtIDE7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMTsgaSA8IGxhc3RJbmRleDsgKysgaSApIHtcblxuXHRcdFx0XHR2YXIga2VlcCA9IGZhbHNlO1xuXG5cdFx0XHRcdHZhciB0aW1lID0gdGltZXNbIGkgXTtcblx0XHRcdFx0dmFyIHRpbWVOZXh0ID0gdGltZXNbIGkgKyAxIF07XG5cblx0XHRcdFx0Ly8gcmVtb3ZlIGFkamFjZW50IGtleWZyYW1lcyBzY2hlZHVsZWQgYXQgdGhlIHNhbWUgdGltZVxuXG5cdFx0XHRcdGlmICggdGltZSAhPT0gdGltZU5leHQgJiYgKCBpICE9PSAxIHx8IHRpbWUgIT09IHRpbWVbIDAgXSApICkge1xuXG5cdFx0XHRcdFx0aWYgKCAhIHNtb290aEludGVycG9sYXRpb24gKSB7XG5cblx0XHRcdFx0XHRcdC8vIHJlbW92ZSB1bm5lY2Vzc2FyeSBrZXlmcmFtZXMgc2FtZSBhcyB0aGVpciBuZWlnaGJvcnNcblxuXHRcdFx0XHRcdFx0dmFyIG9mZnNldCA9IGkgKiBzdHJpZGUsXG5cdFx0XHRcdFx0XHRcdG9mZnNldFAgPSBvZmZzZXQgLSBzdHJpZGUsXG5cdFx0XHRcdFx0XHRcdG9mZnNldE4gPSBvZmZzZXQgKyBzdHJpZGU7XG5cblx0XHRcdFx0XHRcdGZvciAoIHZhciBqID0gMDsgaiAhPT0gc3RyaWRlOyArKyBqICkge1xuXG5cdFx0XHRcdFx0XHRcdHZhciB2YWx1ZSA9IHZhbHVlc1sgb2Zmc2V0ICsgaiBdO1xuXG5cdFx0XHRcdFx0XHRcdGlmICggdmFsdWUgIT09IHZhbHVlc1sgb2Zmc2V0UCArIGogXSB8fFxuXHRcdFx0XHRcdFx0XHRcdFx0dmFsdWUgIT09IHZhbHVlc1sgb2Zmc2V0TiArIGogXSApIHtcblxuXHRcdFx0XHRcdFx0XHRcdGtlZXAgPSB0cnVlO1xuXHRcdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fSBlbHNlIGtlZXAgPSB0cnVlO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBpbi1wbGFjZSBjb21wYWN0aW9uXG5cblx0XHRcdFx0aWYgKCBrZWVwICkge1xuXG5cdFx0XHRcdFx0aWYgKCBpICE9PSB3cml0ZUluZGV4ICkge1xuXG5cdFx0XHRcdFx0XHR0aW1lc1sgd3JpdGVJbmRleCBdID0gdGltZXNbIGkgXTtcblxuXHRcdFx0XHRcdFx0dmFyIHJlYWRPZmZzZXQgPSBpICogc3RyaWRlLFxuXHRcdFx0XHRcdFx0XHR3cml0ZU9mZnNldCA9IHdyaXRlSW5kZXggKiBzdHJpZGU7XG5cblx0XHRcdFx0XHRcdGZvciAoIHZhciBqID0gMDsgaiAhPT0gc3RyaWRlOyArKyBqIClcblxuXHRcdFx0XHRcdFx0XHR2YWx1ZXNbIHdyaXRlT2Zmc2V0ICsgaiBdID0gdmFsdWVzWyByZWFkT2Zmc2V0ICsgaiBdO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0Kysgd3JpdGVJbmRleDtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gZmx1c2ggbGFzdCBrZXlmcmFtZSAoY29tcGFjdGlvbiBsb29rcyBhaGVhZClcblxuXHRcdFx0aWYgKCBsYXN0SW5kZXggPiAwICkge1xuXG5cdFx0XHRcdHRpbWVzWyB3cml0ZUluZGV4IF0gPSB0aW1lc1sgbGFzdEluZGV4IF07XG5cblx0XHRcdFx0Zm9yICggdmFyIHJlYWRPZmZzZXQgPSBsYXN0SW5kZXggKiBzdHJpZGUsIHdyaXRlT2Zmc2V0ID0gd3JpdGVJbmRleCAqIHN0cmlkZSwgaiA9IDA7IGogIT09IHN0cmlkZTsgKysgaiApXG5cblx0XHRcdFx0XHR2YWx1ZXNbIHdyaXRlT2Zmc2V0ICsgaiBdID0gdmFsdWVzWyByZWFkT2Zmc2V0ICsgaiBdO1xuXG5cdFx0XHRcdCsrIHdyaXRlSW5kZXg7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCB3cml0ZUluZGV4ICE9PSB0aW1lcy5sZW5ndGggKSB7XG5cblx0XHRcdFx0dGhpcy50aW1lcyA9IEFuaW1hdGlvblV0aWxzLmFycmF5U2xpY2UoIHRpbWVzLCAwLCB3cml0ZUluZGV4ICk7XG5cdFx0XHRcdHRoaXMudmFsdWVzID0gQW5pbWF0aW9uVXRpbHMuYXJyYXlTbGljZSggdmFsdWVzLCAwLCB3cml0ZUluZGV4ICogc3RyaWRlICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9XG5cblx0fTtcblxuXHRmdW5jdGlvbiBLZXlmcmFtZVRyYWNrQ29uc3RydWN0b3IoIG5hbWUsIHRpbWVzLCB2YWx1ZXMsIGludGVycG9sYXRpb24gKSB7XG5cblx0XHRpZiggbmFtZSA9PT0gdW5kZWZpbmVkICkgdGhyb3cgbmV3IEVycm9yKCBcInRyYWNrIG5hbWUgaXMgdW5kZWZpbmVkXCIgKTtcblxuXHRcdGlmKCB0aW1lcyA9PT0gdW5kZWZpbmVkIHx8IHRpbWVzLmxlbmd0aCA9PT0gMCApIHtcblxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCBcIm5vIGtleWZyYW1lcyBpbiB0cmFjayBuYW1lZCBcIiArIG5hbWUgKTtcblxuXHRcdH1cblxuXHRcdHRoaXMubmFtZSA9IG5hbWU7XG5cblx0XHR0aGlzLnRpbWVzID0gQW5pbWF0aW9uVXRpbHMuY29udmVydEFycmF5KCB0aW1lcywgdGhpcy5UaW1lQnVmZmVyVHlwZSApO1xuXHRcdHRoaXMudmFsdWVzID0gQW5pbWF0aW9uVXRpbHMuY29udmVydEFycmF5KCB2YWx1ZXMsIHRoaXMuVmFsdWVCdWZmZXJUeXBlICk7XG5cblx0XHR0aGlzLnNldEludGVycG9sYXRpb24oIGludGVycG9sYXRpb24gfHwgdGhpcy5EZWZhdWx0SW50ZXJwb2xhdGlvbiApO1xuXG5cdFx0dGhpcy52YWxpZGF0ZSgpO1xuXHRcdHRoaXMub3B0aW1pemUoKTtcblxuXHR9XG5cblx0LyoqXG5cdCAqXG5cdCAqIEEgVHJhY2sgb2YgdmVjdG9yZWQga2V5ZnJhbWUgdmFsdWVzLlxuXHQgKlxuXHQgKlxuXHQgKiBAYXV0aG9yIEJlbiBIb3VzdG9uIC8gaHR0cDovL2NsYXJhLmlvL1xuXHQgKiBAYXV0aG9yIERhdmlkIFNhcm5vIC8gaHR0cDovL2xpZ2h0aGF1cy51cy9cblx0ICogQGF1dGhvciB0c2Nod1xuXHQgKi9cblxuXHRmdW5jdGlvbiBWZWN0b3JLZXlmcmFtZVRyYWNrKCBuYW1lLCB0aW1lcywgdmFsdWVzLCBpbnRlcnBvbGF0aW9uICkge1xuXG5cdFx0S2V5ZnJhbWVUcmFja0NvbnN0cnVjdG9yLmNhbGwoIHRoaXMsIG5hbWUsIHRpbWVzLCB2YWx1ZXMsIGludGVycG9sYXRpb24gKTtcblxuXHR9XG5cblx0VmVjdG9yS2V5ZnJhbWVUcmFjay5wcm90b3R5cGUgPVxuXHRcdFx0T2JqZWN0LmFzc2lnbiggT2JqZWN0LmNyZWF0ZSggS2V5ZnJhbWVUcmFja1Byb3RvdHlwZSApLCB7XG5cblx0XHRjb25zdHJ1Y3RvcjogVmVjdG9yS2V5ZnJhbWVUcmFjayxcblxuXHRcdFZhbHVlVHlwZU5hbWU6ICd2ZWN0b3InXG5cblx0XHQvLyBWYWx1ZUJ1ZmZlclR5cGUgaXMgaW5oZXJpdGVkXG5cblx0XHQvLyBEZWZhdWx0SW50ZXJwb2xhdGlvbiBpcyBpbmhlcml0ZWRcblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIFNwaGVyaWNhbCBsaW5lYXIgdW5pdCBxdWF0ZXJuaW9uIGludGVycG9sYW50LlxuXHQgKlxuXHQgKiBAYXV0aG9yIHRzY2h3XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFF1YXRlcm5pb25MaW5lYXJJbnRlcnBvbGFudChcblx0XHRcdHBhcmFtZXRlclBvc2l0aW9ucywgc2FtcGxlVmFsdWVzLCBzYW1wbGVTaXplLCByZXN1bHRCdWZmZXIgKSB7XG5cblx0XHRJbnRlcnBvbGFudC5jYWxsKFxuXHRcdFx0XHR0aGlzLCBwYXJhbWV0ZXJQb3NpdGlvbnMsIHNhbXBsZVZhbHVlcywgc2FtcGxlU2l6ZSwgcmVzdWx0QnVmZmVyICk7XG5cblx0fVxuXG5cdFF1YXRlcm5pb25MaW5lYXJJbnRlcnBvbGFudC5wcm90b3R5cGUgPVxuXHRcdFx0T2JqZWN0LmFzc2lnbiggT2JqZWN0LmNyZWF0ZSggSW50ZXJwb2xhbnQucHJvdG90eXBlICksIHtcblxuXHRcdGNvbnN0cnVjdG9yOiBRdWF0ZXJuaW9uTGluZWFySW50ZXJwb2xhbnQsXG5cblx0XHRpbnRlcnBvbGF0ZV86IGZ1bmN0aW9uKCBpMSwgdDAsIHQsIHQxICkge1xuXG5cdFx0XHR2YXIgcmVzdWx0ID0gdGhpcy5yZXN1bHRCdWZmZXIsXG5cdFx0XHRcdHZhbHVlcyA9IHRoaXMuc2FtcGxlVmFsdWVzLFxuXHRcdFx0XHRzdHJpZGUgPSB0aGlzLnZhbHVlU2l6ZSxcblxuXHRcdFx0XHRvZmZzZXQgPSBpMSAqIHN0cmlkZSxcblxuXHRcdFx0XHRhbHBoYSA9ICggdCAtIHQwICkgLyAoIHQxIC0gdDAgKTtcblxuXHRcdFx0Zm9yICggdmFyIGVuZCA9IG9mZnNldCArIHN0cmlkZTsgb2Zmc2V0ICE9PSBlbmQ7IG9mZnNldCArPSA0ICkge1xuXG5cdFx0XHRcdFF1YXRlcm5pb24uc2xlcnBGbGF0KCByZXN1bHQsIDAsXG5cdFx0XHRcdFx0XHR2YWx1ZXMsIG9mZnNldCAtIHN0cmlkZSwgdmFsdWVzLCBvZmZzZXQsIGFscGhhICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHJlc3VsdDtcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqXG5cdCAqIEEgVHJhY2sgb2YgcXVhdGVybmlvbiBrZXlmcmFtZSB2YWx1ZXMuXG5cdCAqXG5cdCAqIEBhdXRob3IgQmVuIEhvdXN0b24gLyBodHRwOi8vY2xhcmEuaW8vXG5cdCAqIEBhdXRob3IgRGF2aWQgU2Fybm8gLyBodHRwOi8vbGlnaHRoYXVzLnVzL1xuXHQgKiBAYXV0aG9yIHRzY2h3XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFF1YXRlcm5pb25LZXlmcmFtZVRyYWNrKCBuYW1lLCB0aW1lcywgdmFsdWVzLCBpbnRlcnBvbGF0aW9uICkge1xuXG5cdFx0S2V5ZnJhbWVUcmFja0NvbnN0cnVjdG9yLmNhbGwoIHRoaXMsIG5hbWUsIHRpbWVzLCB2YWx1ZXMsIGludGVycG9sYXRpb24gKTtcblxuXHR9XG5cblx0UXVhdGVybmlvbktleWZyYW1lVHJhY2sucHJvdG90eXBlID1cblx0XHRcdE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIEtleWZyYW1lVHJhY2tQcm90b3R5cGUgKSwge1xuXG5cdFx0Y29uc3RydWN0b3I6IFF1YXRlcm5pb25LZXlmcmFtZVRyYWNrLFxuXG5cdFx0VmFsdWVUeXBlTmFtZTogJ3F1YXRlcm5pb24nLFxuXG5cdFx0Ly8gVmFsdWVCdWZmZXJUeXBlIGlzIGluaGVyaXRlZFxuXG5cdFx0RGVmYXVsdEludGVycG9sYXRpb246IEludGVycG9sYXRlTGluZWFyLFxuXG5cdFx0SW50ZXJwb2xhbnRGYWN0b3J5TWV0aG9kTGluZWFyOiBmdW5jdGlvbiggcmVzdWx0ICkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IFF1YXRlcm5pb25MaW5lYXJJbnRlcnBvbGFudChcblx0XHRcdFx0XHR0aGlzLnRpbWVzLCB0aGlzLnZhbHVlcywgdGhpcy5nZXRWYWx1ZVNpemUoKSwgcmVzdWx0ICk7XG5cblx0XHR9LFxuXG5cdFx0SW50ZXJwb2xhbnRGYWN0b3J5TWV0aG9kU21vb3RoOiB1bmRlZmluZWQgLy8gbm90IHlldCBpbXBsZW1lbnRlZFxuXG5cdH0gKTtcblxuXHQvKipcblx0ICpcblx0ICogQSBUcmFjayBvZiBudW1lcmljIGtleWZyYW1lIHZhbHVlcy5cblx0ICpcblx0ICogQGF1dGhvciBCZW4gSG91c3RvbiAvIGh0dHA6Ly9jbGFyYS5pby9cblx0ICogQGF1dGhvciBEYXZpZCBTYXJubyAvIGh0dHA6Ly9saWdodGhhdXMudXMvXG5cdCAqIEBhdXRob3IgdHNjaHdcblx0ICovXG5cblx0ZnVuY3Rpb24gTnVtYmVyS2V5ZnJhbWVUcmFjayggbmFtZSwgdGltZXMsIHZhbHVlcywgaW50ZXJwb2xhdGlvbiApIHtcblxuXHRcdEtleWZyYW1lVHJhY2tDb25zdHJ1Y3Rvci5jYWxsKCB0aGlzLCBuYW1lLCB0aW1lcywgdmFsdWVzLCBpbnRlcnBvbGF0aW9uICk7XG5cblx0fVxuXG5cdE51bWJlcktleWZyYW1lVHJhY2sucHJvdG90eXBlID1cblx0XHRcdE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIEtleWZyYW1lVHJhY2tQcm90b3R5cGUgKSwge1xuXG5cdFx0Y29uc3RydWN0b3I6IE51bWJlcktleWZyYW1lVHJhY2ssXG5cblx0XHRWYWx1ZVR5cGVOYW1lOiAnbnVtYmVyJ1xuXG5cdFx0Ly8gVmFsdWVCdWZmZXJUeXBlIGlzIGluaGVyaXRlZFxuXG5cdFx0Ly8gRGVmYXVsdEludGVycG9sYXRpb24gaXMgaW5oZXJpdGVkXG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKlxuXHQgKiBBIFRyYWNrIHRoYXQgaW50ZXJwb2xhdGVzIFN0cmluZ3Ncblx0ICpcblx0ICpcblx0ICogQGF1dGhvciBCZW4gSG91c3RvbiAvIGh0dHA6Ly9jbGFyYS5pby9cblx0ICogQGF1dGhvciBEYXZpZCBTYXJubyAvIGh0dHA6Ly9saWdodGhhdXMudXMvXG5cdCAqIEBhdXRob3IgdHNjaHdcblx0ICovXG5cblx0ZnVuY3Rpb24gU3RyaW5nS2V5ZnJhbWVUcmFjayggbmFtZSwgdGltZXMsIHZhbHVlcywgaW50ZXJwb2xhdGlvbiApIHtcblxuXHRcdEtleWZyYW1lVHJhY2tDb25zdHJ1Y3Rvci5jYWxsKCB0aGlzLCBuYW1lLCB0aW1lcywgdmFsdWVzLCBpbnRlcnBvbGF0aW9uICk7XG5cblx0fVxuXG5cdFN0cmluZ0tleWZyYW1lVHJhY2sucHJvdG90eXBlID1cblx0XHRcdE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIEtleWZyYW1lVHJhY2tQcm90b3R5cGUgKSwge1xuXG5cdFx0Y29uc3RydWN0b3I6IFN0cmluZ0tleWZyYW1lVHJhY2ssXG5cblx0XHRWYWx1ZVR5cGVOYW1lOiAnc3RyaW5nJyxcblx0XHRWYWx1ZUJ1ZmZlclR5cGU6IEFycmF5LFxuXG5cdFx0RGVmYXVsdEludGVycG9sYXRpb246IEludGVycG9sYXRlRGlzY3JldGUsXG5cblx0XHRJbnRlcnBvbGFudEZhY3RvcnlNZXRob2RMaW5lYXI6IHVuZGVmaW5lZCxcblxuXHRcdEludGVycG9sYW50RmFjdG9yeU1ldGhvZFNtb290aDogdW5kZWZpbmVkXG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKlxuXHQgKiBBIFRyYWNrIG9mIEJvb2xlYW4ga2V5ZnJhbWUgdmFsdWVzLlxuXHQgKlxuXHQgKlxuXHQgKiBAYXV0aG9yIEJlbiBIb3VzdG9uIC8gaHR0cDovL2NsYXJhLmlvL1xuXHQgKiBAYXV0aG9yIERhdmlkIFNhcm5vIC8gaHR0cDovL2xpZ2h0aGF1cy51cy9cblx0ICogQGF1dGhvciB0c2Nod1xuXHQgKi9cblxuXHRmdW5jdGlvbiBCb29sZWFuS2V5ZnJhbWVUcmFjayggbmFtZSwgdGltZXMsIHZhbHVlcyApIHtcblxuXHRcdEtleWZyYW1lVHJhY2tDb25zdHJ1Y3Rvci5jYWxsKCB0aGlzLCBuYW1lLCB0aW1lcywgdmFsdWVzICk7XG5cblx0fVxuXG5cdEJvb2xlYW5LZXlmcmFtZVRyYWNrLnByb3RvdHlwZSA9XG5cdFx0XHRPYmplY3QuYXNzaWduKCBPYmplY3QuY3JlYXRlKCBLZXlmcmFtZVRyYWNrUHJvdG90eXBlICksIHtcblxuXHRcdGNvbnN0cnVjdG9yOiBCb29sZWFuS2V5ZnJhbWVUcmFjayxcblxuXHRcdFZhbHVlVHlwZU5hbWU6ICdib29sJyxcblx0XHRWYWx1ZUJ1ZmZlclR5cGU6IEFycmF5LFxuXG5cdFx0RGVmYXVsdEludGVycG9sYXRpb246IEludGVycG9sYXRlRGlzY3JldGUsXG5cblx0XHRJbnRlcnBvbGFudEZhY3RvcnlNZXRob2RMaW5lYXI6IHVuZGVmaW5lZCxcblx0XHRJbnRlcnBvbGFudEZhY3RvcnlNZXRob2RTbW9vdGg6IHVuZGVmaW5lZFxuXG5cdFx0Ly8gTm90ZTogQWN0dWFsbHkgdGhpcyB0cmFjayBjb3VsZCBoYXZlIGEgb3B0aW1pemVkIC8gY29tcHJlc3NlZFxuXHRcdC8vIHJlcHJlc2VudGF0aW9uIG9mIGEgc2luZ2xlIHZhbHVlIGFuZCBhIGN1c3RvbSBpbnRlcnBvbGFudCB0aGF0XG5cdFx0Ly8gY29tcHV0ZXMgXCJmaXJzdFZhbHVlIF4gaXNPZGQoIGluZGV4IClcIi5cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqXG5cdCAqIEEgVHJhY2sgb2Yga2V5ZnJhbWUgdmFsdWVzIHRoYXQgcmVwcmVzZW50IGNvbG9yLlxuXHQgKlxuXHQgKlxuXHQgKiBAYXV0aG9yIEJlbiBIb3VzdG9uIC8gaHR0cDovL2NsYXJhLmlvL1xuXHQgKiBAYXV0aG9yIERhdmlkIFNhcm5vIC8gaHR0cDovL2xpZ2h0aGF1cy51cy9cblx0ICogQGF1dGhvciB0c2Nod1xuXHQgKi9cblxuXHRmdW5jdGlvbiBDb2xvcktleWZyYW1lVHJhY2soIG5hbWUsIHRpbWVzLCB2YWx1ZXMsIGludGVycG9sYXRpb24gKSB7XG5cblx0XHRLZXlmcmFtZVRyYWNrQ29uc3RydWN0b3IuY2FsbCggdGhpcywgbmFtZSwgdGltZXMsIHZhbHVlcywgaW50ZXJwb2xhdGlvbiApO1xuXG5cdH1cblxuXHRDb2xvcktleWZyYW1lVHJhY2sucHJvdG90eXBlID1cblx0XHRcdE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIEtleWZyYW1lVHJhY2tQcm90b3R5cGUgKSwge1xuXG5cdFx0Y29uc3RydWN0b3I6IENvbG9yS2V5ZnJhbWVUcmFjayxcblxuXHRcdFZhbHVlVHlwZU5hbWU6ICdjb2xvcidcblxuXHRcdC8vIFZhbHVlQnVmZmVyVHlwZSBpcyBpbmhlcml0ZWRcblxuXHRcdC8vIERlZmF1bHRJbnRlcnBvbGF0aW9uIGlzIGluaGVyaXRlZFxuXG5cblx0XHQvLyBOb3RlOiBWZXJ5IGJhc2ljIGltcGxlbWVudGF0aW9uIGFuZCBub3RoaW5nIHNwZWNpYWwgeWV0LlxuXHRcdC8vIEhvd2V2ZXIsIHRoaXMgaXMgdGhlIHBsYWNlIGZvciBjb2xvciBzcGFjZSBwYXJhbWV0ZXJpemF0aW9uLlxuXG5cdH0gKTtcblxuXHQvKipcblx0ICpcblx0ICogQSB0aW1lZCBzZXF1ZW5jZSBvZiBrZXlmcmFtZXMgZm9yIGEgc3BlY2lmaWMgcHJvcGVydHkuXG5cdCAqXG5cdCAqXG5cdCAqIEBhdXRob3IgQmVuIEhvdXN0b24gLyBodHRwOi8vY2xhcmEuaW8vXG5cdCAqIEBhdXRob3IgRGF2aWQgU2Fybm8gLyBodHRwOi8vbGlnaHRoYXVzLnVzL1xuXHQgKiBAYXV0aG9yIHRzY2h3XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEtleWZyYW1lVHJhY2soIG5hbWUsIHRpbWVzLCB2YWx1ZXMsIGludGVycG9sYXRpb24gKSB7XG5cblx0XHRLZXlmcmFtZVRyYWNrQ29uc3RydWN0b3IuYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApO1xuXG5cdH1cblxuXHRLZXlmcmFtZVRyYWNrLnByb3RvdHlwZSA9IEtleWZyYW1lVHJhY2tQcm90b3R5cGU7XG5cdEtleWZyYW1lVHJhY2tQcm90b3R5cGUuY29uc3RydWN0b3IgPSBLZXlmcmFtZVRyYWNrO1xuXG5cdC8vIFN0YXRpYyBtZXRob2RzOlxuXG5cdE9iamVjdC5hc3NpZ24oIEtleWZyYW1lVHJhY2ssIHtcblxuXHRcdC8vIFNlcmlhbGl6YXRpb24gKGluIHN0YXRpYyBjb250ZXh0LCBiZWNhdXNlIG9mIGNvbnN0cnVjdG9yIGludm9jYXRpb25cblx0XHQvLyBhbmQgYXV0b21hdGljIGludm9jYXRpb24gb2YgLnRvSlNPTik6XG5cblx0XHRwYXJzZTogZnVuY3Rpb24oIGpzb24gKSB7XG5cblx0XHRcdGlmKCBqc29uLnR5cGUgPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHR0aHJvdyBuZXcgRXJyb3IoIFwidHJhY2sgdHlwZSB1bmRlZmluZWQsIGNhbiBub3QgcGFyc2VcIiApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciB0cmFja1R5cGUgPSBLZXlmcmFtZVRyYWNrLl9nZXRUcmFja1R5cGVGb3JWYWx1ZVR5cGVOYW1lKCBqc29uLnR5cGUgKTtcblxuXHRcdFx0aWYgKCBqc29uLnRpbWVzID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0dmFyIHRpbWVzID0gW10sIHZhbHVlcyA9IFtdO1xuXG5cdFx0XHRcdEFuaW1hdGlvblV0aWxzLmZsYXR0ZW5KU09OKCBqc29uLmtleXMsIHRpbWVzLCB2YWx1ZXMsICd2YWx1ZScgKTtcblxuXHRcdFx0XHRqc29uLnRpbWVzID0gdGltZXM7XG5cdFx0XHRcdGpzb24udmFsdWVzID0gdmFsdWVzO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIGRlcml2ZWQgY2xhc3NlcyBjYW4gZGVmaW5lIGEgc3RhdGljIHBhcnNlIG1ldGhvZFxuXHRcdFx0aWYgKCB0cmFja1R5cGUucGFyc2UgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRyZXR1cm4gdHJhY2tUeXBlLnBhcnNlKCBqc29uICk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0Ly8gYnkgZGVmYXVsdCwgd2UgYXNzc3VtZSBhIGNvbnN0cnVjdG9yIGNvbXBhdGlibGUgd2l0aCB0aGUgYmFzZVxuXHRcdFx0XHRyZXR1cm4gbmV3IHRyYWNrVHlwZShcblx0XHRcdFx0XHRcdGpzb24ubmFtZSwganNvbi50aW1lcywganNvbi52YWx1ZXMsIGpzb24uaW50ZXJwb2xhdGlvbiApO1xuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0dG9KU09OOiBmdW5jdGlvbiggdHJhY2sgKSB7XG5cblx0XHRcdHZhciB0cmFja1R5cGUgPSB0cmFjay5jb25zdHJ1Y3RvcjtcblxuXHRcdFx0dmFyIGpzb247XG5cblx0XHRcdC8vIGRlcml2ZWQgY2xhc3NlcyBjYW4gZGVmaW5lIGEgc3RhdGljIHRvSlNPTiBtZXRob2Rcblx0XHRcdGlmICggdHJhY2tUeXBlLnRvSlNPTiAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdGpzb24gPSB0cmFja1R5cGUudG9KU09OKCB0cmFjayApO1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdC8vIGJ5IGRlZmF1bHQsIHdlIGFzc3VtZSB0aGUgZGF0YSBjYW4gYmUgc2VyaWFsaXplZCBhcy1pc1xuXHRcdFx0XHRqc29uID0ge1xuXG5cdFx0XHRcdFx0J25hbWUnOiB0cmFjay5uYW1lLFxuXHRcdFx0XHRcdCd0aW1lcyc6IEFuaW1hdGlvblV0aWxzLmNvbnZlcnRBcnJheSggdHJhY2sudGltZXMsIEFycmF5ICksXG5cdFx0XHRcdFx0J3ZhbHVlcyc6IEFuaW1hdGlvblV0aWxzLmNvbnZlcnRBcnJheSggdHJhY2sudmFsdWVzLCBBcnJheSApXG5cblx0XHRcdFx0fTtcblxuXHRcdFx0XHR2YXIgaW50ZXJwb2xhdGlvbiA9IHRyYWNrLmdldEludGVycG9sYXRpb24oKTtcblxuXHRcdFx0XHRpZiAoIGludGVycG9sYXRpb24gIT09IHRyYWNrLkRlZmF1bHRJbnRlcnBvbGF0aW9uICkge1xuXG5cdFx0XHRcdFx0anNvbi5pbnRlcnBvbGF0aW9uID0gaW50ZXJwb2xhdGlvbjtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0anNvbi50eXBlID0gdHJhY2suVmFsdWVUeXBlTmFtZTsgLy8gbWFuZGF0b3J5XG5cblx0XHRcdHJldHVybiBqc29uO1xuXG5cdFx0fSxcblxuXHRcdF9nZXRUcmFja1R5cGVGb3JWYWx1ZVR5cGVOYW1lOiBmdW5jdGlvbiggdHlwZU5hbWUgKSB7XG5cblx0XHRcdHN3aXRjaCggdHlwZU5hbWUudG9Mb3dlckNhc2UoKSApIHtcblxuXHRcdFx0XHRjYXNlIFwic2NhbGFyXCI6XG5cdFx0XHRcdGNhc2UgXCJkb3VibGVcIjpcblx0XHRcdFx0Y2FzZSBcImZsb2F0XCI6XG5cdFx0XHRcdGNhc2UgXCJudW1iZXJcIjpcblx0XHRcdFx0Y2FzZSBcImludGVnZXJcIjpcblxuXHRcdFx0XHRcdHJldHVybiBOdW1iZXJLZXlmcmFtZVRyYWNrO1xuXG5cdFx0XHRcdGNhc2UgXCJ2ZWN0b3JcIjpcblx0XHRcdFx0Y2FzZSBcInZlY3RvcjJcIjpcblx0XHRcdFx0Y2FzZSBcInZlY3RvcjNcIjpcblx0XHRcdFx0Y2FzZSBcInZlY3RvcjRcIjpcblxuXHRcdFx0XHRcdHJldHVybiBWZWN0b3JLZXlmcmFtZVRyYWNrO1xuXG5cdFx0XHRcdGNhc2UgXCJjb2xvclwiOlxuXG5cdFx0XHRcdFx0cmV0dXJuIENvbG9yS2V5ZnJhbWVUcmFjaztcblxuXHRcdFx0XHRjYXNlIFwicXVhdGVybmlvblwiOlxuXG5cdFx0XHRcdFx0cmV0dXJuIFF1YXRlcm5pb25LZXlmcmFtZVRyYWNrO1xuXG5cdFx0XHRcdGNhc2UgXCJib29sXCI6XG5cdFx0XHRcdGNhc2UgXCJib29sZWFuXCI6XG5cblx0XHRcdFx0XHRyZXR1cm4gQm9vbGVhbktleWZyYW1lVHJhY2s7XG5cblx0XHRcdFx0Y2FzZSBcInN0cmluZ1wiOlxuXG5cdFx0XHRcdFx0cmV0dXJuIFN0cmluZ0tleWZyYW1lVHJhY2s7XG5cblx0XHRcdH1cblxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCBcIlVuc3VwcG9ydGVkIHR5cGVOYW1lOiBcIiArIHR5cGVOYW1lICk7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKlxuXHQgKiBSZXVzYWJsZSBzZXQgb2YgVHJhY2tzIHRoYXQgcmVwcmVzZW50IGFuIGFuaW1hdGlvbi5cblx0ICpcblx0ICogQGF1dGhvciBCZW4gSG91c3RvbiAvIGh0dHA6Ly9jbGFyYS5pby9cblx0ICogQGF1dGhvciBEYXZpZCBTYXJubyAvIGh0dHA6Ly9saWdodGhhdXMudXMvXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEFuaW1hdGlvbkNsaXAoIG5hbWUsIGR1cmF0aW9uLCB0cmFja3MgKSB7XG5cblx0XHR0aGlzLm5hbWUgPSBuYW1lO1xuXHRcdHRoaXMudHJhY2tzID0gdHJhY2tzO1xuXHRcdHRoaXMuZHVyYXRpb24gPSAoIGR1cmF0aW9uICE9PSB1bmRlZmluZWQgKSA/IGR1cmF0aW9uIDogLTE7XG5cblx0XHR0aGlzLnV1aWQgPSBfTWF0aC5nZW5lcmF0ZVVVSUQoKTtcblxuXHRcdC8vIHRoaXMgbWVhbnMgaXQgc2hvdWxkIGZpZ3VyZSBvdXQgaXRzIGR1cmF0aW9uIGJ5IHNjYW5uaW5nIHRoZSB0cmFja3Ncblx0XHRpZiAoIHRoaXMuZHVyYXRpb24gPCAwICkge1xuXG5cdFx0XHR0aGlzLnJlc2V0RHVyYXRpb24oKTtcblxuXHRcdH1cblxuXHRcdHRoaXMub3B0aW1pemUoKTtcblxuXHR9XG5cblx0QW5pbWF0aW9uQ2xpcC5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogQW5pbWF0aW9uQ2xpcCxcblxuXHRcdHJlc2V0RHVyYXRpb246IGZ1bmN0aW9uKCkge1xuXG5cdFx0XHR2YXIgdHJhY2tzID0gdGhpcy50cmFja3MsXG5cdFx0XHRcdGR1cmF0aW9uID0gMDtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBuID0gdHJhY2tzLmxlbmd0aDsgaSAhPT0gbjsgKysgaSApIHtcblxuXHRcdFx0XHR2YXIgdHJhY2sgPSB0aGlzLnRyYWNrc1sgaSBdO1xuXG5cdFx0XHRcdGR1cmF0aW9uID0gTWF0aC5tYXgoIGR1cmF0aW9uLCB0cmFjay50aW1lc1sgdHJhY2sudGltZXMubGVuZ3RoIC0gMSBdICk7XG5cblx0XHRcdH1cblxuXHRcdFx0dGhpcy5kdXJhdGlvbiA9IGR1cmF0aW9uO1xuXG5cdFx0fSxcblxuXHRcdHRyaW06IGZ1bmN0aW9uKCkge1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCB0aGlzLnRyYWNrcy5sZW5ndGg7IGkgKysgKSB7XG5cblx0XHRcdFx0dGhpcy50cmFja3NbIGkgXS50cmltKCAwLCB0aGlzLmR1cmF0aW9uICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0b3B0aW1pemU6IGZ1bmN0aW9uKCkge1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCB0aGlzLnRyYWNrcy5sZW5ndGg7IGkgKysgKSB7XG5cblx0XHRcdFx0dGhpcy50cmFja3NbIGkgXS5vcHRpbWl6ZSgpO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0Ly8gU3RhdGljIG1ldGhvZHM6XG5cblx0T2JqZWN0LmFzc2lnbiggQW5pbWF0aW9uQ2xpcCwge1xuXG5cdFx0cGFyc2U6IGZ1bmN0aW9uKCBqc29uICkge1xuXG5cdFx0XHR2YXIgdHJhY2tzID0gW10sXG5cdFx0XHRcdGpzb25UcmFja3MgPSBqc29uLnRyYWNrcyxcblx0XHRcdFx0ZnJhbWVUaW1lID0gMS4wIC8gKCBqc29uLmZwcyB8fCAxLjAgKTtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBuID0ganNvblRyYWNrcy5sZW5ndGg7IGkgIT09IG47ICsrIGkgKSB7XG5cblx0XHRcdFx0dHJhY2tzLnB1c2goIEtleWZyYW1lVHJhY2sucGFyc2UoIGpzb25UcmFja3NbIGkgXSApLnNjYWxlKCBmcmFtZVRpbWUgKSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBuZXcgQW5pbWF0aW9uQ2xpcCgganNvbi5uYW1lLCBqc29uLmR1cmF0aW9uLCB0cmFja3MgKTtcblxuXHRcdH0sXG5cblxuXHRcdHRvSlNPTjogZnVuY3Rpb24oIGNsaXAgKSB7XG5cblx0XHRcdHZhciB0cmFja3MgPSBbXSxcblx0XHRcdFx0Y2xpcFRyYWNrcyA9IGNsaXAudHJhY2tzO1xuXG5cdFx0XHR2YXIganNvbiA9IHtcblxuXHRcdFx0XHQnbmFtZSc6IGNsaXAubmFtZSxcblx0XHRcdFx0J2R1cmF0aW9uJzogY2xpcC5kdXJhdGlvbixcblx0XHRcdFx0J3RyYWNrcyc6IHRyYWNrc1xuXG5cdFx0XHR9O1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIG4gPSBjbGlwVHJhY2tzLmxlbmd0aDsgaSAhPT0gbjsgKysgaSApIHtcblxuXHRcdFx0XHR0cmFja3MucHVzaCggS2V5ZnJhbWVUcmFjay50b0pTT04oIGNsaXBUcmFja3NbIGkgXSApICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIGpzb247XG5cblx0XHR9LFxuXG5cblx0XHRDcmVhdGVGcm9tTW9ycGhUYXJnZXRTZXF1ZW5jZTogZnVuY3Rpb24oIG5hbWUsIG1vcnBoVGFyZ2V0U2VxdWVuY2UsIGZwcywgbm9Mb29wICkge1xuXG5cdFx0XHR2YXIgbnVtTW9ycGhUYXJnZXRzID0gbW9ycGhUYXJnZXRTZXF1ZW5jZS5sZW5ndGg7XG5cdFx0XHR2YXIgdHJhY2tzID0gW107XG5cblx0XHRcdGZvciAoIHZhciBpID0gMDsgaSA8IG51bU1vcnBoVGFyZ2V0czsgaSArKyApIHtcblxuXHRcdFx0XHR2YXIgdGltZXMgPSBbXTtcblx0XHRcdFx0dmFyIHZhbHVlcyA9IFtdO1xuXG5cdFx0XHRcdHRpbWVzLnB1c2goXG5cdFx0XHRcdFx0XHQoIGkgKyBudW1Nb3JwaFRhcmdldHMgLSAxICkgJSBudW1Nb3JwaFRhcmdldHMsXG5cdFx0XHRcdFx0XHRpLFxuXHRcdFx0XHRcdFx0KCBpICsgMSApICUgbnVtTW9ycGhUYXJnZXRzICk7XG5cblx0XHRcdFx0dmFsdWVzLnB1c2goIDAsIDEsIDAgKTtcblxuXHRcdFx0XHR2YXIgb3JkZXIgPSBBbmltYXRpb25VdGlscy5nZXRLZXlmcmFtZU9yZGVyKCB0aW1lcyApO1xuXHRcdFx0XHR0aW1lcyA9IEFuaW1hdGlvblV0aWxzLnNvcnRlZEFycmF5KCB0aW1lcywgMSwgb3JkZXIgKTtcblx0XHRcdFx0dmFsdWVzID0gQW5pbWF0aW9uVXRpbHMuc29ydGVkQXJyYXkoIHZhbHVlcywgMSwgb3JkZXIgKTtcblxuXHRcdFx0XHQvLyBpZiB0aGVyZSBpcyBhIGtleSBhdCB0aGUgZmlyc3QgZnJhbWUsIGR1cGxpY2F0ZSBpdCBhcyB0aGVcblx0XHRcdFx0Ly8gbGFzdCBmcmFtZSBhcyB3ZWxsIGZvciBwZXJmZWN0IGxvb3AuXG5cdFx0XHRcdGlmICggISBub0xvb3AgJiYgdGltZXNbIDAgXSA9PT0gMCApIHtcblxuXHRcdFx0XHRcdHRpbWVzLnB1c2goIG51bU1vcnBoVGFyZ2V0cyApO1xuXHRcdFx0XHRcdHZhbHVlcy5wdXNoKCB2YWx1ZXNbIDAgXSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHR0cmFja3MucHVzaChcblx0XHRcdFx0XHRcdG5ldyBOdW1iZXJLZXlmcmFtZVRyYWNrKFxuXHRcdFx0XHRcdFx0XHQnLm1vcnBoVGFyZ2V0SW5mbHVlbmNlc1snICsgbW9ycGhUYXJnZXRTZXF1ZW5jZVsgaSBdLm5hbWUgKyAnXScsXG5cdFx0XHRcdFx0XHRcdHRpbWVzLCB2YWx1ZXNcblx0XHRcdFx0XHRcdCkuc2NhbGUoIDEuMCAvIGZwcyApICk7XG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBuZXcgQW5pbWF0aW9uQ2xpcCggbmFtZSwgLTEsIHRyYWNrcyApO1xuXG5cdFx0fSxcblxuXHRcdGZpbmRCeU5hbWU6IGZ1bmN0aW9uKCBvYmplY3RPckNsaXBBcnJheSwgbmFtZSApIHtcblxuXHRcdFx0dmFyIGNsaXBBcnJheSA9IG9iamVjdE9yQ2xpcEFycmF5O1xuXG5cdFx0XHRpZiAoICEgQXJyYXkuaXNBcnJheSggb2JqZWN0T3JDbGlwQXJyYXkgKSApIHtcblxuXHRcdFx0XHR2YXIgbyA9IG9iamVjdE9yQ2xpcEFycmF5O1xuXHRcdFx0XHRjbGlwQXJyYXkgPSBvLmdlb21ldHJ5ICYmIG8uZ2VvbWV0cnkuYW5pbWF0aW9ucyB8fCBvLmFuaW1hdGlvbnM7XG5cblx0XHRcdH1cblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgY2xpcEFycmF5Lmxlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHRpZiAoIGNsaXBBcnJheVsgaSBdLm5hbWUgPT09IG5hbWUgKSB7XG5cblx0XHRcdFx0XHRyZXR1cm4gY2xpcEFycmF5WyBpIF07XG5cblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gbnVsbDtcblxuXHRcdH0sXG5cblx0XHRDcmVhdGVDbGlwc0Zyb21Nb3JwaFRhcmdldFNlcXVlbmNlczogZnVuY3Rpb24oIG1vcnBoVGFyZ2V0cywgZnBzLCBub0xvb3AgKSB7XG5cblx0XHRcdHZhciBhbmltYXRpb25Ub01vcnBoVGFyZ2V0cyA9IHt9O1xuXG5cdFx0XHQvLyB0ZXN0ZWQgd2l0aCBodHRwczovL3JlZ2V4MTAxLmNvbS8gb24gdHJpY2sgc2VxdWVuY2VzXG5cdFx0XHQvLyBzdWNoIGZsYW1pbmdvX2ZseUFfMDAzLCBmbGFtaW5nb19ydW4xXzAwMywgY3JkZWF0aDAwNTlcblx0XHRcdHZhciBwYXR0ZXJuID0gL14oW1xcdy1dKj8pKFtcXGRdKykkLztcblxuXHRcdFx0Ly8gc29ydCBtb3JwaCB0YXJnZXQgbmFtZXMgaW50byBhbmltYXRpb24gZ3JvdXBzIGJhc2VkXG5cdFx0XHQvLyBwYXR0ZXJucyBsaWtlIFdhbGtfMDAxLCBXYWxrXzAwMiwgUnVuXzAwMSwgUnVuXzAwMlxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBpbCA9IG1vcnBoVGFyZ2V0cy5sZW5ndGg7IGkgPCBpbDsgaSArKyApIHtcblxuXHRcdFx0XHR2YXIgbW9ycGhUYXJnZXQgPSBtb3JwaFRhcmdldHNbIGkgXTtcblx0XHRcdFx0dmFyIHBhcnRzID0gbW9ycGhUYXJnZXQubmFtZS5tYXRjaCggcGF0dGVybiApO1xuXG5cdFx0XHRcdGlmICggcGFydHMgJiYgcGFydHMubGVuZ3RoID4gMSApIHtcblxuXHRcdFx0XHRcdHZhciBuYW1lID0gcGFydHNbIDEgXTtcblxuXHRcdFx0XHRcdHZhciBhbmltYXRpb25Nb3JwaFRhcmdldHMgPSBhbmltYXRpb25Ub01vcnBoVGFyZ2V0c1sgbmFtZSBdO1xuXHRcdFx0XHRcdGlmICggISBhbmltYXRpb25Nb3JwaFRhcmdldHMgKSB7XG5cblx0XHRcdFx0XHRcdGFuaW1hdGlvblRvTW9ycGhUYXJnZXRzWyBuYW1lIF0gPSBhbmltYXRpb25Nb3JwaFRhcmdldHMgPSBbXTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGFuaW1hdGlvbk1vcnBoVGFyZ2V0cy5wdXNoKCBtb3JwaFRhcmdldCApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgY2xpcHMgPSBbXTtcblxuXHRcdFx0Zm9yICggdmFyIG5hbWUgaW4gYW5pbWF0aW9uVG9Nb3JwaFRhcmdldHMgKSB7XG5cblx0XHRcdFx0Y2xpcHMucHVzaCggQW5pbWF0aW9uQ2xpcC5DcmVhdGVGcm9tTW9ycGhUYXJnZXRTZXF1ZW5jZSggbmFtZSwgYW5pbWF0aW9uVG9Nb3JwaFRhcmdldHNbIG5hbWUgXSwgZnBzLCBub0xvb3AgKSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBjbGlwcztcblxuXHRcdH0sXG5cblx0XHQvLyBwYXJzZSB0aGUgYW5pbWF0aW9uLmhpZXJhcmNoeSBmb3JtYXRcblx0XHRwYXJzZUFuaW1hdGlvbjogZnVuY3Rpb24oIGFuaW1hdGlvbiwgYm9uZXMgKSB7XG5cblx0XHRcdGlmICggISBhbmltYXRpb24gKSB7XG5cblx0XHRcdFx0Y29uc29sZS5lcnJvciggXCIgIG5vIGFuaW1hdGlvbiBpbiBKU09OTG9hZGVyIGRhdGFcIiApO1xuXHRcdFx0XHRyZXR1cm4gbnVsbDtcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgYWRkTm9uZW1wdHlUcmFjayA9IGZ1bmN0aW9uKFxuXHRcdFx0XHRcdHRyYWNrVHlwZSwgdHJhY2tOYW1lLCBhbmltYXRpb25LZXlzLCBwcm9wZXJ0eU5hbWUsIGRlc3RUcmFja3MgKSB7XG5cblx0XHRcdFx0Ly8gb25seSByZXR1cm4gdHJhY2sgaWYgdGhlcmUgYXJlIGFjdHVhbGx5IGtleXMuXG5cdFx0XHRcdGlmICggYW5pbWF0aW9uS2V5cy5sZW5ndGggIT09IDAgKSB7XG5cblx0XHRcdFx0XHR2YXIgdGltZXMgPSBbXTtcblx0XHRcdFx0XHR2YXIgdmFsdWVzID0gW107XG5cblx0XHRcdFx0XHRBbmltYXRpb25VdGlscy5mbGF0dGVuSlNPTihcblx0XHRcdFx0XHRcdFx0YW5pbWF0aW9uS2V5cywgdGltZXMsIHZhbHVlcywgcHJvcGVydHlOYW1lICk7XG5cblx0XHRcdFx0XHQvLyBlbXB0eSBrZXlzIGFyZSBmaWx0ZXJlZCBvdXQsIHNvIGNoZWNrIGFnYWluXG5cdFx0XHRcdFx0aWYgKCB0aW1lcy5sZW5ndGggIT09IDAgKSB7XG5cblx0XHRcdFx0XHRcdGRlc3RUcmFja3MucHVzaCggbmV3IHRyYWNrVHlwZSggdHJhY2tOYW1lLCB0aW1lcywgdmFsdWVzICkgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdH07XG5cblx0XHRcdHZhciB0cmFja3MgPSBbXTtcblxuXHRcdFx0dmFyIGNsaXBOYW1lID0gYW5pbWF0aW9uLm5hbWUgfHwgJ2RlZmF1bHQnO1xuXHRcdFx0Ly8gYXV0b21hdGljIGxlbmd0aCBkZXRlcm1pbmF0aW9uIGluIEFuaW1hdGlvbkNsaXAuXG5cdFx0XHR2YXIgZHVyYXRpb24gPSBhbmltYXRpb24ubGVuZ3RoIHx8IC0xO1xuXHRcdFx0dmFyIGZwcyA9IGFuaW1hdGlvbi5mcHMgfHwgMzA7XG5cblx0XHRcdHZhciBoaWVyYXJjaHlUcmFja3MgPSBhbmltYXRpb24uaGllcmFyY2h5IHx8IFtdO1xuXG5cdFx0XHRmb3IgKCB2YXIgaCA9IDA7IGggPCBoaWVyYXJjaHlUcmFja3MubGVuZ3RoOyBoICsrICkge1xuXG5cdFx0XHRcdHZhciBhbmltYXRpb25LZXlzID0gaGllcmFyY2h5VHJhY2tzWyBoIF0ua2V5cztcblxuXHRcdFx0XHQvLyBza2lwIGVtcHR5IHRyYWNrc1xuXHRcdFx0XHRpZiAoICEgYW5pbWF0aW9uS2V5cyB8fCBhbmltYXRpb25LZXlzLmxlbmd0aCA9PT0gMCApIGNvbnRpbnVlO1xuXG5cdFx0XHRcdC8vIHByb2Nlc3MgbW9ycGggdGFyZ2V0cyBpbiBhIHdheSBleGFjdGx5IGNvbXBhdGlibGVcblx0XHRcdFx0Ly8gd2l0aCBBbmltYXRpb25IYW5kbGVyLmluaXQoIGFuaW1hdGlvbiApXG5cdFx0XHRcdGlmICggYW5pbWF0aW9uS2V5c1swXS5tb3JwaFRhcmdldHMgKSB7XG5cblx0XHRcdFx0XHQvLyBmaWd1cmUgb3V0IGFsbCBtb3JwaCB0YXJnZXRzIHVzZWQgaW4gdGhpcyB0cmFja1xuXHRcdFx0XHRcdHZhciBtb3JwaFRhcmdldE5hbWVzID0ge307XG5cdFx0XHRcdFx0Zm9yICggdmFyIGsgPSAwOyBrIDwgYW5pbWF0aW9uS2V5cy5sZW5ndGg7IGsgKysgKSB7XG5cblx0XHRcdFx0XHRcdGlmICggYW5pbWF0aW9uS2V5c1trXS5tb3JwaFRhcmdldHMgKSB7XG5cblx0XHRcdFx0XHRcdFx0Zm9yICggdmFyIG0gPSAwOyBtIDwgYW5pbWF0aW9uS2V5c1trXS5tb3JwaFRhcmdldHMubGVuZ3RoOyBtICsrICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0bW9ycGhUYXJnZXROYW1lc1sgYW5pbWF0aW9uS2V5c1trXS5tb3JwaFRhcmdldHNbbV0gXSA9IC0xO1xuXHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vIGNyZWF0ZSBhIHRyYWNrIGZvciBlYWNoIG1vcnBoIHRhcmdldCB3aXRoIGFsbCB6ZXJvXG5cdFx0XHRcdFx0Ly8gbW9ycGhUYXJnZXRJbmZsdWVuY2VzIGV4Y2VwdCBmb3IgdGhlIGtleXMgaW4gd2hpY2hcblx0XHRcdFx0XHQvLyB0aGUgbW9ycGhUYXJnZXQgaXMgbmFtZWQuXG5cdFx0XHRcdFx0Zm9yICggdmFyIG1vcnBoVGFyZ2V0TmFtZSBpbiBtb3JwaFRhcmdldE5hbWVzICkge1xuXG5cdFx0XHRcdFx0XHR2YXIgdGltZXMgPSBbXTtcblx0XHRcdFx0XHRcdHZhciB2YWx1ZXMgPSBbXTtcblxuXHRcdFx0XHRcdFx0Zm9yICggdmFyIG0gPSAwOyBtICE9PSBhbmltYXRpb25LZXlzW2tdLm1vcnBoVGFyZ2V0cy5sZW5ndGg7ICsrIG0gKSB7XG5cblx0XHRcdFx0XHRcdFx0dmFyIGFuaW1hdGlvbktleSA9IGFuaW1hdGlvbktleXNba107XG5cblx0XHRcdFx0XHRcdFx0dGltZXMucHVzaCggYW5pbWF0aW9uS2V5LnRpbWUgKTtcblx0XHRcdFx0XHRcdFx0dmFsdWVzLnB1c2goICggYW5pbWF0aW9uS2V5Lm1vcnBoVGFyZ2V0ID09PSBtb3JwaFRhcmdldE5hbWUgKSA/IDEgOiAwICk7XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0dHJhY2tzLnB1c2goIG5ldyBOdW1iZXJLZXlmcmFtZVRyYWNrKCcubW9ycGhUYXJnZXRJbmZsdWVuY2VbJyArIG1vcnBoVGFyZ2V0TmFtZSArICddJywgdGltZXMsIHZhbHVlcyApICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRkdXJhdGlvbiA9IG1vcnBoVGFyZ2V0TmFtZXMubGVuZ3RoICogKCBmcHMgfHwgMS4wICk7XG5cblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHQvLyAuLi5hc3N1bWUgc2tlbGV0YWwgYW5pbWF0aW9uXG5cblx0XHRcdFx0XHR2YXIgYm9uZU5hbWUgPSAnLmJvbmVzWycgKyBib25lc1sgaCBdLm5hbWUgKyAnXSc7XG5cblx0XHRcdFx0XHRhZGROb25lbXB0eVRyYWNrKFxuXHRcdFx0XHRcdFx0XHRWZWN0b3JLZXlmcmFtZVRyYWNrLCBib25lTmFtZSArICcucG9zaXRpb24nLFxuXHRcdFx0XHRcdFx0XHRhbmltYXRpb25LZXlzLCAncG9zJywgdHJhY2tzICk7XG5cblx0XHRcdFx0XHRhZGROb25lbXB0eVRyYWNrKFxuXHRcdFx0XHRcdFx0XHRRdWF0ZXJuaW9uS2V5ZnJhbWVUcmFjaywgYm9uZU5hbWUgKyAnLnF1YXRlcm5pb24nLFxuXHRcdFx0XHRcdFx0XHRhbmltYXRpb25LZXlzLCAncm90JywgdHJhY2tzICk7XG5cblx0XHRcdFx0XHRhZGROb25lbXB0eVRyYWNrKFxuXHRcdFx0XHRcdFx0XHRWZWN0b3JLZXlmcmFtZVRyYWNrLCBib25lTmFtZSArICcuc2NhbGUnLFxuXHRcdFx0XHRcdFx0XHRhbmltYXRpb25LZXlzLCAnc2NsJywgdHJhY2tzICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggdHJhY2tzLmxlbmd0aCA9PT0gMCApIHtcblxuXHRcdFx0XHRyZXR1cm4gbnVsbDtcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgY2xpcCA9IG5ldyBBbmltYXRpb25DbGlwKCBjbGlwTmFtZSwgZHVyYXRpb24sIHRyYWNrcyApO1xuXG5cdFx0XHRyZXR1cm4gY2xpcDtcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIE1hdGVyaWFsTG9hZGVyKCBtYW5hZ2VyICkge1xuXG5cdFx0dGhpcy5tYW5hZ2VyID0gKCBtYW5hZ2VyICE9PSB1bmRlZmluZWQgKSA/IG1hbmFnZXIgOiBEZWZhdWx0TG9hZGluZ01hbmFnZXI7XG5cdFx0dGhpcy50ZXh0dXJlcyA9IHt9O1xuXG5cdH1cblxuXHRPYmplY3QuYXNzaWduKCBNYXRlcmlhbExvYWRlci5wcm90b3R5cGUsIHtcblxuXHRcdGxvYWQ6IGZ1bmN0aW9uICggdXJsLCBvbkxvYWQsIG9uUHJvZ3Jlc3MsIG9uRXJyb3IgKSB7XG5cblx0XHRcdHZhciBzY29wZSA9IHRoaXM7XG5cblx0XHRcdHZhciBsb2FkZXIgPSBuZXcgRmlsZUxvYWRlciggc2NvcGUubWFuYWdlciApO1xuXHRcdFx0bG9hZGVyLmxvYWQoIHVybCwgZnVuY3Rpb24gKCB0ZXh0ICkge1xuXG5cdFx0XHRcdG9uTG9hZCggc2NvcGUucGFyc2UoIEpTT04ucGFyc2UoIHRleHQgKSApICk7XG5cblx0XHRcdH0sIG9uUHJvZ3Jlc3MsIG9uRXJyb3IgKTtcblxuXHRcdH0sXG5cblx0XHRzZXRUZXh0dXJlczogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0dGhpcy50ZXh0dXJlcyA9IHZhbHVlO1xuXG5cdFx0fSxcblxuXHRcdHBhcnNlOiBmdW5jdGlvbiAoIGpzb24gKSB7XG5cblx0XHRcdHZhciB0ZXh0dXJlcyA9IHRoaXMudGV4dHVyZXM7XG5cblx0XHRcdGZ1bmN0aW9uIGdldFRleHR1cmUoIG5hbWUgKSB7XG5cblx0XHRcdFx0aWYgKCB0ZXh0dXJlc1sgbmFtZSBdID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5NYXRlcmlhbExvYWRlcjogVW5kZWZpbmVkIHRleHR1cmUnLCBuYW1lICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJldHVybiB0ZXh0dXJlc1sgbmFtZSBdO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBtYXRlcmlhbCA9IG5ldyBNYXRlcmlhbHNbIGpzb24udHlwZSBdKCk7XG5cblx0XHRcdGlmICgganNvbi51dWlkICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC51dWlkID0ganNvbi51dWlkO1xuXHRcdFx0aWYgKCBqc29uLm5hbWUgIT09IHVuZGVmaW5lZCApIG1hdGVyaWFsLm5hbWUgPSBqc29uLm5hbWU7XG5cdFx0XHRpZiAoIGpzb24uY29sb3IgIT09IHVuZGVmaW5lZCApIG1hdGVyaWFsLmNvbG9yLnNldEhleCgganNvbi5jb2xvciApO1xuXHRcdFx0aWYgKCBqc29uLnJvdWdobmVzcyAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwucm91Z2huZXNzID0ganNvbi5yb3VnaG5lc3M7XG5cdFx0XHRpZiAoIGpzb24ubWV0YWxuZXNzICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC5tZXRhbG5lc3MgPSBqc29uLm1ldGFsbmVzcztcblx0XHRcdGlmICgganNvbi5lbWlzc2l2ZSAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwuZW1pc3NpdmUuc2V0SGV4KCBqc29uLmVtaXNzaXZlICk7XG5cdFx0XHRpZiAoIGpzb24uc3BlY3VsYXIgIT09IHVuZGVmaW5lZCApIG1hdGVyaWFsLnNwZWN1bGFyLnNldEhleCgganNvbi5zcGVjdWxhciApO1xuXHRcdFx0aWYgKCBqc29uLnNoaW5pbmVzcyAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwuc2hpbmluZXNzID0ganNvbi5zaGluaW5lc3M7XG5cdFx0XHRpZiAoIGpzb24uY2xlYXJDb2F0ICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC5jbGVhckNvYXQgPSBqc29uLmNsZWFyQ29hdDtcblx0XHRcdGlmICgganNvbi5jbGVhckNvYXRSb3VnaG5lc3MgIT09IHVuZGVmaW5lZCApIG1hdGVyaWFsLmNsZWFyQ29hdFJvdWdobmVzcyA9IGpzb24uY2xlYXJDb2F0Um91Z2huZXNzO1xuXHRcdFx0aWYgKCBqc29uLnVuaWZvcm1zICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC51bmlmb3JtcyA9IGpzb24udW5pZm9ybXM7XG5cdFx0XHRpZiAoIGpzb24udmVydGV4U2hhZGVyICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC52ZXJ0ZXhTaGFkZXIgPSBqc29uLnZlcnRleFNoYWRlcjtcblx0XHRcdGlmICgganNvbi5mcmFnbWVudFNoYWRlciAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwuZnJhZ21lbnRTaGFkZXIgPSBqc29uLmZyYWdtZW50U2hhZGVyO1xuXHRcdFx0aWYgKCBqc29uLnZlcnRleENvbG9ycyAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwudmVydGV4Q29sb3JzID0ganNvbi52ZXJ0ZXhDb2xvcnM7XG5cdFx0XHRpZiAoIGpzb24uZm9nICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC5mb2cgPSBqc29uLmZvZztcblx0XHRcdGlmICgganNvbi5zaGFkaW5nICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC5zaGFkaW5nID0ganNvbi5zaGFkaW5nO1xuXHRcdFx0aWYgKCBqc29uLmJsZW5kaW5nICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC5ibGVuZGluZyA9IGpzb24uYmxlbmRpbmc7XG5cdFx0XHRpZiAoIGpzb24uc2lkZSAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwuc2lkZSA9IGpzb24uc2lkZTtcblx0XHRcdGlmICgganNvbi5vcGFjaXR5ICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC5vcGFjaXR5ID0ganNvbi5vcGFjaXR5O1xuXHRcdFx0aWYgKCBqc29uLnRyYW5zcGFyZW50ICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC50cmFuc3BhcmVudCA9IGpzb24udHJhbnNwYXJlbnQ7XG5cdFx0XHRpZiAoIGpzb24uYWxwaGFUZXN0ICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC5hbHBoYVRlc3QgPSBqc29uLmFscGhhVGVzdDtcblx0XHRcdGlmICgganNvbi5kZXB0aFRlc3QgIT09IHVuZGVmaW5lZCApIG1hdGVyaWFsLmRlcHRoVGVzdCA9IGpzb24uZGVwdGhUZXN0O1xuXHRcdFx0aWYgKCBqc29uLmRlcHRoV3JpdGUgIT09IHVuZGVmaW5lZCApIG1hdGVyaWFsLmRlcHRoV3JpdGUgPSBqc29uLmRlcHRoV3JpdGU7XG5cdFx0XHRpZiAoIGpzb24uY29sb3JXcml0ZSAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwuY29sb3JXcml0ZSA9IGpzb24uY29sb3JXcml0ZTtcblx0XHRcdGlmICgganNvbi53aXJlZnJhbWUgIT09IHVuZGVmaW5lZCApIG1hdGVyaWFsLndpcmVmcmFtZSA9IGpzb24ud2lyZWZyYW1lO1xuXHRcdFx0aWYgKCBqc29uLndpcmVmcmFtZUxpbmV3aWR0aCAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwud2lyZWZyYW1lTGluZXdpZHRoID0ganNvbi53aXJlZnJhbWVMaW5ld2lkdGg7XG5cdFx0XHRpZiAoIGpzb24ud2lyZWZyYW1lTGluZWNhcCAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwud2lyZWZyYW1lTGluZWNhcCA9IGpzb24ud2lyZWZyYW1lTGluZWNhcDtcblx0XHRcdGlmICgganNvbi53aXJlZnJhbWVMaW5lam9pbiAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwud2lyZWZyYW1lTGluZWpvaW4gPSBqc29uLndpcmVmcmFtZUxpbmVqb2luO1xuXHRcdFx0aWYgKCBqc29uLnNraW5uaW5nICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC5za2lubmluZyA9IGpzb24uc2tpbm5pbmc7XG5cdFx0XHRpZiAoIGpzb24ubW9ycGhUYXJnZXRzICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC5tb3JwaFRhcmdldHMgPSBqc29uLm1vcnBoVGFyZ2V0cztcblxuXHRcdFx0Ly8gZm9yIFBvaW50c01hdGVyaWFsXG5cblx0XHRcdGlmICgganNvbi5zaXplICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC5zaXplID0ganNvbi5zaXplO1xuXHRcdFx0aWYgKCBqc29uLnNpemVBdHRlbnVhdGlvbiAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwuc2l6ZUF0dGVudWF0aW9uID0ganNvbi5zaXplQXR0ZW51YXRpb247XG5cblx0XHRcdC8vIG1hcHNcblxuXHRcdFx0aWYgKCBqc29uLm1hcCAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwubWFwID0gZ2V0VGV4dHVyZSgganNvbi5tYXAgKTtcblxuXHRcdFx0aWYgKCBqc29uLmFscGhhTWFwICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0bWF0ZXJpYWwuYWxwaGFNYXAgPSBnZXRUZXh0dXJlKCBqc29uLmFscGhhTWFwICk7XG5cdFx0XHRcdG1hdGVyaWFsLnRyYW5zcGFyZW50ID0gdHJ1ZTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIGpzb24uYnVtcE1hcCAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwuYnVtcE1hcCA9IGdldFRleHR1cmUoIGpzb24uYnVtcE1hcCApO1xuXHRcdFx0aWYgKCBqc29uLmJ1bXBTY2FsZSAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwuYnVtcFNjYWxlID0ganNvbi5idW1wU2NhbGU7XG5cblx0XHRcdGlmICgganNvbi5ub3JtYWxNYXAgIT09IHVuZGVmaW5lZCApIG1hdGVyaWFsLm5vcm1hbE1hcCA9IGdldFRleHR1cmUoIGpzb24ubm9ybWFsTWFwICk7XG5cdFx0XHRpZiAoIGpzb24ubm9ybWFsU2NhbGUgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHR2YXIgbm9ybWFsU2NhbGUgPSBqc29uLm5vcm1hbFNjYWxlO1xuXG5cdFx0XHRcdGlmICggQXJyYXkuaXNBcnJheSggbm9ybWFsU2NhbGUgKSA9PT0gZmFsc2UgKSB7XG5cblx0XHRcdFx0XHQvLyBCbGVuZGVyIGV4cG9ydGVyIHVzZWQgdG8gZXhwb3J0IGEgc2NhbGFyLiBTZWUgIzc0NTlcblxuXHRcdFx0XHRcdG5vcm1hbFNjYWxlID0gWyBub3JtYWxTY2FsZSwgbm9ybWFsU2NhbGUgXTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0bWF0ZXJpYWwubm9ybWFsU2NhbGUgPSBuZXcgVmVjdG9yMigpLmZyb21BcnJheSggbm9ybWFsU2NhbGUgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIGpzb24uZGlzcGxhY2VtZW50TWFwICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC5kaXNwbGFjZW1lbnRNYXAgPSBnZXRUZXh0dXJlKCBqc29uLmRpc3BsYWNlbWVudE1hcCApO1xuXHRcdFx0aWYgKCBqc29uLmRpc3BsYWNlbWVudFNjYWxlICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC5kaXNwbGFjZW1lbnRTY2FsZSA9IGpzb24uZGlzcGxhY2VtZW50U2NhbGU7XG5cdFx0XHRpZiAoIGpzb24uZGlzcGxhY2VtZW50QmlhcyAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwuZGlzcGxhY2VtZW50QmlhcyA9IGpzb24uZGlzcGxhY2VtZW50QmlhcztcblxuXHRcdFx0aWYgKCBqc29uLnJvdWdobmVzc01hcCAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwucm91Z2huZXNzTWFwID0gZ2V0VGV4dHVyZSgganNvbi5yb3VnaG5lc3NNYXAgKTtcblx0XHRcdGlmICgganNvbi5tZXRhbG5lc3NNYXAgIT09IHVuZGVmaW5lZCApIG1hdGVyaWFsLm1ldGFsbmVzc01hcCA9IGdldFRleHR1cmUoIGpzb24ubWV0YWxuZXNzTWFwICk7XG5cblx0XHRcdGlmICgganNvbi5lbWlzc2l2ZU1hcCAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwuZW1pc3NpdmVNYXAgPSBnZXRUZXh0dXJlKCBqc29uLmVtaXNzaXZlTWFwICk7XG5cdFx0XHRpZiAoIGpzb24uZW1pc3NpdmVJbnRlbnNpdHkgIT09IHVuZGVmaW5lZCApIG1hdGVyaWFsLmVtaXNzaXZlSW50ZW5zaXR5ID0ganNvbi5lbWlzc2l2ZUludGVuc2l0eTtcblxuXHRcdFx0aWYgKCBqc29uLnNwZWN1bGFyTWFwICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC5zcGVjdWxhck1hcCA9IGdldFRleHR1cmUoIGpzb24uc3BlY3VsYXJNYXAgKTtcblxuXHRcdFx0aWYgKCBqc29uLmVudk1hcCAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwuZW52TWFwID0gZ2V0VGV4dHVyZSgganNvbi5lbnZNYXAgKTtcblxuXHRcdFx0aWYgKCBqc29uLnJlZmxlY3Rpdml0eSAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwucmVmbGVjdGl2aXR5ID0ganNvbi5yZWZsZWN0aXZpdHk7XG5cblx0XHRcdGlmICgganNvbi5saWdodE1hcCAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwubGlnaHRNYXAgPSBnZXRUZXh0dXJlKCBqc29uLmxpZ2h0TWFwICk7XG5cdFx0XHRpZiAoIGpzb24ubGlnaHRNYXBJbnRlbnNpdHkgIT09IHVuZGVmaW5lZCApIG1hdGVyaWFsLmxpZ2h0TWFwSW50ZW5zaXR5ID0ganNvbi5saWdodE1hcEludGVuc2l0eTtcblxuXHRcdFx0aWYgKCBqc29uLmFvTWFwICE9PSB1bmRlZmluZWQgKSBtYXRlcmlhbC5hb01hcCA9IGdldFRleHR1cmUoIGpzb24uYW9NYXAgKTtcblx0XHRcdGlmICgganNvbi5hb01hcEludGVuc2l0eSAhPT0gdW5kZWZpbmVkICkgbWF0ZXJpYWwuYW9NYXBJbnRlbnNpdHkgPSBqc29uLmFvTWFwSW50ZW5zaXR5O1xuXG5cdFx0XHRpZiAoIGpzb24uZ3JhZGllbnRNYXAgIT09IHVuZGVmaW5lZCApIG1hdGVyaWFsLmdyYWRpZW50TWFwID0gZ2V0VGV4dHVyZSgganNvbi5ncmFkaWVudE1hcCApO1xuXG5cdFx0XHQvLyBNdWx0aU1hdGVyaWFsXG5cblx0XHRcdGlmICgganNvbi5tYXRlcmlhbHMgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBqc29uLm1hdGVyaWFscy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdFx0bWF0ZXJpYWwubWF0ZXJpYWxzLnB1c2goIHRoaXMucGFyc2UoIGpzb24ubWF0ZXJpYWxzWyBpIF0gKSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gbWF0ZXJpYWw7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBCdWZmZXJHZW9tZXRyeUxvYWRlciggbWFuYWdlciApIHtcblxuXHRcdHRoaXMubWFuYWdlciA9ICggbWFuYWdlciAhPT0gdW5kZWZpbmVkICkgPyBtYW5hZ2VyIDogRGVmYXVsdExvYWRpbmdNYW5hZ2VyO1xuXG5cdH1cblxuXHRPYmplY3QuYXNzaWduKCBCdWZmZXJHZW9tZXRyeUxvYWRlci5wcm90b3R5cGUsIHtcblxuXHRcdGxvYWQ6IGZ1bmN0aW9uICggdXJsLCBvbkxvYWQsIG9uUHJvZ3Jlc3MsIG9uRXJyb3IgKSB7XG5cblx0XHRcdHZhciBzY29wZSA9IHRoaXM7XG5cblx0XHRcdHZhciBsb2FkZXIgPSBuZXcgRmlsZUxvYWRlciggc2NvcGUubWFuYWdlciApO1xuXHRcdFx0bG9hZGVyLmxvYWQoIHVybCwgZnVuY3Rpb24gKCB0ZXh0ICkge1xuXG5cdFx0XHRcdG9uTG9hZCggc2NvcGUucGFyc2UoIEpTT04ucGFyc2UoIHRleHQgKSApICk7XG5cblx0XHRcdH0sIG9uUHJvZ3Jlc3MsIG9uRXJyb3IgKTtcblxuXHRcdH0sXG5cblx0XHRwYXJzZTogZnVuY3Rpb24gKCBqc29uICkge1xuXG5cdFx0XHR2YXIgZ2VvbWV0cnkgPSBuZXcgQnVmZmVyR2VvbWV0cnkoKTtcblxuXHRcdFx0dmFyIGluZGV4ID0ganNvbi5kYXRhLmluZGV4O1xuXG5cdFx0XHR2YXIgVFlQRURfQVJSQVlTID0ge1xuXHRcdFx0XHQnSW50OEFycmF5JzogSW50OEFycmF5LFxuXHRcdFx0XHQnVWludDhBcnJheSc6IFVpbnQ4QXJyYXksXG5cdFx0XHRcdCdVaW50OENsYW1wZWRBcnJheSc6IFVpbnQ4Q2xhbXBlZEFycmF5LFxuXHRcdFx0XHQnSW50MTZBcnJheSc6IEludDE2QXJyYXksXG5cdFx0XHRcdCdVaW50MTZBcnJheSc6IFVpbnQxNkFycmF5LFxuXHRcdFx0XHQnSW50MzJBcnJheSc6IEludDMyQXJyYXksXG5cdFx0XHRcdCdVaW50MzJBcnJheSc6IFVpbnQzMkFycmF5LFxuXHRcdFx0XHQnRmxvYXQzMkFycmF5JzogRmxvYXQzMkFycmF5LFxuXHRcdFx0XHQnRmxvYXQ2NEFycmF5JzogRmxvYXQ2NEFycmF5XG5cdFx0XHR9O1xuXG5cdFx0XHRpZiAoIGluZGV4ICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0dmFyIHR5cGVkQXJyYXkgPSBuZXcgVFlQRURfQVJSQVlTWyBpbmRleC50eXBlIF0oIGluZGV4LmFycmF5ICk7XG5cdFx0XHRcdGdlb21ldHJ5LnNldEluZGV4KCBuZXcgQnVmZmVyQXR0cmlidXRlKCB0eXBlZEFycmF5LCAxICkgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgYXR0cmlidXRlcyA9IGpzb24uZGF0YS5hdHRyaWJ1dGVzO1xuXG5cdFx0XHRmb3IgKCB2YXIga2V5IGluIGF0dHJpYnV0ZXMgKSB7XG5cblx0XHRcdFx0dmFyIGF0dHJpYnV0ZSA9IGF0dHJpYnV0ZXNbIGtleSBdO1xuXHRcdFx0XHR2YXIgdHlwZWRBcnJheSA9IG5ldyBUWVBFRF9BUlJBWVNbIGF0dHJpYnV0ZS50eXBlIF0oIGF0dHJpYnV0ZS5hcnJheSApO1xuXG5cdFx0XHRcdGdlb21ldHJ5LmFkZEF0dHJpYnV0ZSgga2V5LCBuZXcgQnVmZmVyQXR0cmlidXRlKCB0eXBlZEFycmF5LCBhdHRyaWJ1dGUuaXRlbVNpemUsIGF0dHJpYnV0ZS5ub3JtYWxpemVkICkgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgZ3JvdXBzID0ganNvbi5kYXRhLmdyb3VwcyB8fCBqc29uLmRhdGEuZHJhd2NhbGxzIHx8IGpzb24uZGF0YS5vZmZzZXRzO1xuXG5cdFx0XHRpZiAoIGdyb3VwcyAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbiA9IGdyb3Vwcy5sZW5ndGg7IGkgIT09IG47ICsrIGkgKSB7XG5cblx0XHRcdFx0XHR2YXIgZ3JvdXAgPSBncm91cHNbIGkgXTtcblxuXHRcdFx0XHRcdGdlb21ldHJ5LmFkZEdyb3VwKCBncm91cC5zdGFydCwgZ3JvdXAuY291bnQsIGdyb3VwLm1hdGVyaWFsSW5kZXggKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIGJvdW5kaW5nU3BoZXJlID0ganNvbi5kYXRhLmJvdW5kaW5nU3BoZXJlO1xuXG5cdFx0XHRpZiAoIGJvdW5kaW5nU3BoZXJlICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0dmFyIGNlbnRlciA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdFx0aWYgKCBib3VuZGluZ1NwaGVyZS5jZW50ZXIgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdGNlbnRlci5mcm9tQXJyYXkoIGJvdW5kaW5nU3BoZXJlLmNlbnRlciApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRnZW9tZXRyeS5ib3VuZGluZ1NwaGVyZSA9IG5ldyBTcGhlcmUoIGNlbnRlciwgYm91bmRpbmdTcGhlcmUucmFkaXVzICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIGdlb21ldHJ5O1xuXG5cdFx0fVxuXG5cdH0gKTtcblxuXHQvKipcblx0ICogQGF1dGhvciBhbHRlcmVkcSAvIGh0dHA6Ly9hbHRlcmVkcXVhbGlhLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gTG9hZGVyKCkge1xuXG5cdFx0dGhpcy5vbkxvYWRTdGFydCA9IGZ1bmN0aW9uICgpIHt9O1xuXHRcdHRoaXMub25Mb2FkUHJvZ3Jlc3MgPSBmdW5jdGlvbiAoKSB7fTtcblx0XHR0aGlzLm9uTG9hZENvbXBsZXRlID0gZnVuY3Rpb24gKCkge307XG5cblx0fVxuXG5cdExvYWRlci5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogTG9hZGVyLFxuXG5cdFx0Y3Jvc3NPcmlnaW46IHVuZGVmaW5lZCxcblxuXHRcdGV4dHJhY3RVcmxCYXNlOiBmdW5jdGlvbiAoIHVybCApIHtcblxuXHRcdFx0dmFyIHBhcnRzID0gdXJsLnNwbGl0KCAnLycgKTtcblxuXHRcdFx0aWYgKCBwYXJ0cy5sZW5ndGggPT09IDEgKSByZXR1cm4gJy4vJztcblxuXHRcdFx0cGFydHMucG9wKCk7XG5cblx0XHRcdHJldHVybiBwYXJ0cy5qb2luKCAnLycgKSArICcvJztcblxuXHRcdH0sXG5cblx0XHRpbml0TWF0ZXJpYWxzOiBmdW5jdGlvbiAoIG1hdGVyaWFscywgdGV4dHVyZVBhdGgsIGNyb3NzT3JpZ2luICkge1xuXG5cdFx0XHR2YXIgYXJyYXkgPSBbXTtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwgbWF0ZXJpYWxzLmxlbmd0aDsgKysgaSApIHtcblxuXHRcdFx0XHRhcnJheVsgaSBdID0gdGhpcy5jcmVhdGVNYXRlcmlhbCggbWF0ZXJpYWxzWyBpIF0sIHRleHR1cmVQYXRoLCBjcm9zc09yaWdpbiApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBhcnJheTtcblxuXHRcdH0sXG5cblx0XHRjcmVhdGVNYXRlcmlhbDogKCBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBjb2xvciwgdGV4dHVyZUxvYWRlciwgbWF0ZXJpYWxMb2FkZXI7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiBjcmVhdGVNYXRlcmlhbCggbSwgdGV4dHVyZVBhdGgsIGNyb3NzT3JpZ2luICkge1xuXG5cdFx0XHRcdGlmICggY29sb3IgPT09IHVuZGVmaW5lZCApIGNvbG9yID0gbmV3IENvbG9yKCk7XG5cdFx0XHRcdGlmICggdGV4dHVyZUxvYWRlciA9PT0gdW5kZWZpbmVkICkgdGV4dHVyZUxvYWRlciA9IG5ldyBUZXh0dXJlTG9hZGVyKCk7XG5cdFx0XHRcdGlmICggbWF0ZXJpYWxMb2FkZXIgPT09IHVuZGVmaW5lZCApIG1hdGVyaWFsTG9hZGVyID0gbmV3IE1hdGVyaWFsTG9hZGVyKCk7XG5cblx0XHRcdFx0Ly8gY29udmVydCBmcm9tIG9sZCBtYXRlcmlhbCBmb3JtYXRcblxuXHRcdFx0XHR2YXIgdGV4dHVyZXMgPSB7fTtcblxuXHRcdFx0XHRmdW5jdGlvbiBsb2FkVGV4dHVyZSggcGF0aCwgcmVwZWF0LCBvZmZzZXQsIHdyYXAsIGFuaXNvdHJvcHkgKSB7XG5cblx0XHRcdFx0XHR2YXIgZnVsbFBhdGggPSB0ZXh0dXJlUGF0aCArIHBhdGg7XG5cdFx0XHRcdFx0dmFyIGxvYWRlciA9IExvYWRlci5IYW5kbGVycy5nZXQoIGZ1bGxQYXRoICk7XG5cblx0XHRcdFx0XHR2YXIgdGV4dHVyZTtcblxuXHRcdFx0XHRcdGlmICggbG9hZGVyICE9PSBudWxsICkge1xuXG5cdFx0XHRcdFx0XHR0ZXh0dXJlID0gbG9hZGVyLmxvYWQoIGZ1bGxQYXRoICk7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHR0ZXh0dXJlTG9hZGVyLnNldENyb3NzT3JpZ2luKCBjcm9zc09yaWdpbiApO1xuXHRcdFx0XHRcdFx0dGV4dHVyZSA9IHRleHR1cmVMb2FkZXIubG9hZCggZnVsbFBhdGggKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGlmICggcmVwZWF0ICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRcdHRleHR1cmUucmVwZWF0LmZyb21BcnJheSggcmVwZWF0ICk7XG5cblx0XHRcdFx0XHRcdGlmICggcmVwZWF0WyAwIF0gIT09IDEgKSB0ZXh0dXJlLndyYXBTID0gUmVwZWF0V3JhcHBpbmc7XG5cdFx0XHRcdFx0XHRpZiAoIHJlcGVhdFsgMSBdICE9PSAxICkgdGV4dHVyZS53cmFwVCA9IFJlcGVhdFdyYXBwaW5nO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0aWYgKCBvZmZzZXQgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdFx0dGV4dHVyZS5vZmZzZXQuZnJvbUFycmF5KCBvZmZzZXQgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGlmICggd3JhcCAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0XHRpZiAoIHdyYXBbIDAgXSA9PT0gJ3JlcGVhdCcgKSB0ZXh0dXJlLndyYXBTID0gUmVwZWF0V3JhcHBpbmc7XG5cdFx0XHRcdFx0XHRpZiAoIHdyYXBbIDAgXSA9PT0gJ21pcnJvcicgKSB0ZXh0dXJlLndyYXBTID0gTWlycm9yZWRSZXBlYXRXcmFwcGluZztcblxuXHRcdFx0XHRcdFx0aWYgKCB3cmFwWyAxIF0gPT09ICdyZXBlYXQnICkgdGV4dHVyZS53cmFwVCA9IFJlcGVhdFdyYXBwaW5nO1xuXHRcdFx0XHRcdFx0aWYgKCB3cmFwWyAxIF0gPT09ICdtaXJyb3InICkgdGV4dHVyZS53cmFwVCA9IE1pcnJvcmVkUmVwZWF0V3JhcHBpbmc7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRpZiAoIGFuaXNvdHJvcHkgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdFx0dGV4dHVyZS5hbmlzb3Ryb3B5ID0gYW5pc290cm9weTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdHZhciB1dWlkID0gX01hdGguZ2VuZXJhdGVVVUlEKCk7XG5cblx0XHRcdFx0XHR0ZXh0dXJlc1sgdXVpZCBdID0gdGV4dHVyZTtcblxuXHRcdFx0XHRcdHJldHVybiB1dWlkO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvL1xuXG5cdFx0XHRcdHZhciBqc29uID0ge1xuXHRcdFx0XHRcdHV1aWQ6IF9NYXRoLmdlbmVyYXRlVVVJRCgpLFxuXHRcdFx0XHRcdHR5cGU6ICdNZXNoTGFtYmVydE1hdGVyaWFsJ1xuXHRcdFx0XHR9O1xuXG5cdFx0XHRcdGZvciAoIHZhciBuYW1lIGluIG0gKSB7XG5cblx0XHRcdFx0XHR2YXIgdmFsdWUgPSBtWyBuYW1lIF07XG5cblx0XHRcdFx0XHRzd2l0Y2ggKCBuYW1lICkge1xuXHRcdFx0XHRcdFx0Y2FzZSAnRGJnQ29sb3InOlxuXHRcdFx0XHRcdFx0Y2FzZSAnRGJnSW5kZXgnOlxuXHRcdFx0XHRcdFx0Y2FzZSAnb3B0aWNhbERlbnNpdHknOlxuXHRcdFx0XHRcdFx0Y2FzZSAnaWxsdW1pbmF0aW9uJzpcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlICdEYmdOYW1lJzpcblx0XHRcdFx0XHRcdFx0anNvbi5uYW1lID0gdmFsdWU7XG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdFx0Y2FzZSAnYmxlbmRpbmcnOlxuXHRcdFx0XHRcdFx0XHRqc29uLmJsZW5kaW5nID0gQmxlbmRpbmdNb2RlWyB2YWx1ZSBdO1xuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdGNhc2UgJ2NvbG9yQW1iaWVudCc6XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBBbWJpZW50Jzpcblx0XHRcdFx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuTG9hZGVyLmNyZWF0ZU1hdGVyaWFsOicsIG5hbWUsICdpcyBubyBsb25nZXIgc3VwcG9ydGVkLicgKTtcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlICdjb2xvckRpZmZ1c2UnOlxuXHRcdFx0XHRcdFx0XHRqc29uLmNvbG9yID0gY29sb3IuZnJvbUFycmF5KCB2YWx1ZSApLmdldEhleCgpO1xuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdGNhc2UgJ2NvbG9yU3BlY3VsYXInOlxuXHRcdFx0XHRcdFx0XHRqc29uLnNwZWN1bGFyID0gY29sb3IuZnJvbUFycmF5KCB2YWx1ZSApLmdldEhleCgpO1xuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdGNhc2UgJ2NvbG9yRW1pc3NpdmUnOlxuXHRcdFx0XHRcdFx0XHRqc29uLmVtaXNzaXZlID0gY29sb3IuZnJvbUFycmF5KCB2YWx1ZSApLmdldEhleCgpO1xuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdGNhc2UgJ3NwZWN1bGFyQ29lZic6XG5cdFx0XHRcdFx0XHRcdGpzb24uc2hpbmluZXNzID0gdmFsdWU7XG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdFx0Y2FzZSAnc2hhZGluZyc6XG5cdFx0XHRcdFx0XHRcdGlmICggdmFsdWUudG9Mb3dlckNhc2UoKSA9PT0gJ2Jhc2ljJyApIGpzb24udHlwZSA9ICdNZXNoQmFzaWNNYXRlcmlhbCc7XG5cdFx0XHRcdFx0XHRcdGlmICggdmFsdWUudG9Mb3dlckNhc2UoKSA9PT0gJ3Bob25nJyApIGpzb24udHlwZSA9ICdNZXNoUGhvbmdNYXRlcmlhbCc7XG5cdFx0XHRcdFx0XHRcdGlmICggdmFsdWUudG9Mb3dlckNhc2UoKSA9PT0gJ3N0YW5kYXJkJyApIGpzb24udHlwZSA9ICdNZXNoU3RhbmRhcmRNYXRlcmlhbCc7XG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdFx0Y2FzZSAnbWFwRGlmZnVzZSc6XG5cdFx0XHRcdFx0XHRcdGpzb24ubWFwID0gbG9hZFRleHR1cmUoIHZhbHVlLCBtLm1hcERpZmZ1c2VSZXBlYXQsIG0ubWFwRGlmZnVzZU9mZnNldCwgbS5tYXBEaWZmdXNlV3JhcCwgbS5tYXBEaWZmdXNlQW5pc290cm9weSApO1xuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdGNhc2UgJ21hcERpZmZ1c2VSZXBlYXQnOlxuXHRcdFx0XHRcdFx0Y2FzZSAnbWFwRGlmZnVzZU9mZnNldCc6XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBEaWZmdXNlV3JhcCc6XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBEaWZmdXNlQW5pc290cm9weSc6XG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdFx0Y2FzZSAnbWFwRW1pc3NpdmUnOlxuXHRcdFx0XHRcdFx0XHRqc29uLmVtaXNzaXZlTWFwID0gbG9hZFRleHR1cmUoIHZhbHVlLCBtLm1hcEVtaXNzaXZlUmVwZWF0LCBtLm1hcEVtaXNzaXZlT2Zmc2V0LCBtLm1hcEVtaXNzaXZlV3JhcCwgbS5tYXBFbWlzc2l2ZUFuaXNvdHJvcHkgKTtcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBFbWlzc2l2ZVJlcGVhdCc6XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBFbWlzc2l2ZU9mZnNldCc6XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBFbWlzc2l2ZVdyYXAnOlxuXHRcdFx0XHRcdFx0Y2FzZSAnbWFwRW1pc3NpdmVBbmlzb3Ryb3B5Jzpcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBMaWdodCc6XG5cdFx0XHRcdFx0XHRcdGpzb24ubGlnaHRNYXAgPSBsb2FkVGV4dHVyZSggdmFsdWUsIG0ubWFwTGlnaHRSZXBlYXQsIG0ubWFwTGlnaHRPZmZzZXQsIG0ubWFwTGlnaHRXcmFwLCBtLm1hcExpZ2h0QW5pc290cm9weSApO1xuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdGNhc2UgJ21hcExpZ2h0UmVwZWF0Jzpcblx0XHRcdFx0XHRcdGNhc2UgJ21hcExpZ2h0T2Zmc2V0Jzpcblx0XHRcdFx0XHRcdGNhc2UgJ21hcExpZ2h0V3JhcCc6XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBMaWdodEFuaXNvdHJvcHknOlxuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdGNhc2UgJ21hcEFPJzpcblx0XHRcdFx0XHRcdFx0anNvbi5hb01hcCA9IGxvYWRUZXh0dXJlKCB2YWx1ZSwgbS5tYXBBT1JlcGVhdCwgbS5tYXBBT09mZnNldCwgbS5tYXBBT1dyYXAsIG0ubWFwQU9Bbmlzb3Ryb3B5ICk7XG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdFx0Y2FzZSAnbWFwQU9SZXBlYXQnOlxuXHRcdFx0XHRcdFx0Y2FzZSAnbWFwQU9PZmZzZXQnOlxuXHRcdFx0XHRcdFx0Y2FzZSAnbWFwQU9XcmFwJzpcblx0XHRcdFx0XHRcdGNhc2UgJ21hcEFPQW5pc290cm9weSc6XG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdFx0Y2FzZSAnbWFwQnVtcCc6XG5cdFx0XHRcdFx0XHRcdGpzb24uYnVtcE1hcCA9IGxvYWRUZXh0dXJlKCB2YWx1ZSwgbS5tYXBCdW1wUmVwZWF0LCBtLm1hcEJ1bXBPZmZzZXQsIG0ubWFwQnVtcFdyYXAsIG0ubWFwQnVtcEFuaXNvdHJvcHkgKTtcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBCdW1wU2NhbGUnOlxuXHRcdFx0XHRcdFx0XHRqc29uLmJ1bXBTY2FsZSA9IHZhbHVlO1xuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdGNhc2UgJ21hcEJ1bXBSZXBlYXQnOlxuXHRcdFx0XHRcdFx0Y2FzZSAnbWFwQnVtcE9mZnNldCc6XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBCdW1wV3JhcCc6XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBCdW1wQW5pc290cm9weSc6XG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdFx0Y2FzZSAnbWFwTm9ybWFsJzpcblx0XHRcdFx0XHRcdFx0anNvbi5ub3JtYWxNYXAgPSBsb2FkVGV4dHVyZSggdmFsdWUsIG0ubWFwTm9ybWFsUmVwZWF0LCBtLm1hcE5vcm1hbE9mZnNldCwgbS5tYXBOb3JtYWxXcmFwLCBtLm1hcE5vcm1hbEFuaXNvdHJvcHkgKTtcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBOb3JtYWxGYWN0b3InOlxuXHRcdFx0XHRcdFx0XHRqc29uLm5vcm1hbFNjYWxlID0gWyB2YWx1ZSwgdmFsdWUgXTtcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBOb3JtYWxSZXBlYXQnOlxuXHRcdFx0XHRcdFx0Y2FzZSAnbWFwTm9ybWFsT2Zmc2V0Jzpcblx0XHRcdFx0XHRcdGNhc2UgJ21hcE5vcm1hbFdyYXAnOlxuXHRcdFx0XHRcdFx0Y2FzZSAnbWFwTm9ybWFsQW5pc290cm9weSc6XG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdFx0Y2FzZSAnbWFwU3BlY3VsYXInOlxuXHRcdFx0XHRcdFx0XHRqc29uLnNwZWN1bGFyTWFwID0gbG9hZFRleHR1cmUoIHZhbHVlLCBtLm1hcFNwZWN1bGFyUmVwZWF0LCBtLm1hcFNwZWN1bGFyT2Zmc2V0LCBtLm1hcFNwZWN1bGFyV3JhcCwgbS5tYXBTcGVjdWxhckFuaXNvdHJvcHkgKTtcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBTcGVjdWxhclJlcGVhdCc6XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBTcGVjdWxhck9mZnNldCc6XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBTcGVjdWxhcldyYXAnOlxuXHRcdFx0XHRcdFx0Y2FzZSAnbWFwU3BlY3VsYXJBbmlzb3Ryb3B5Jzpcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBNZXRhbG5lc3MnOlxuXHRcdFx0XHRcdFx0XHRqc29uLm1ldGFsbmVzc01hcCA9IGxvYWRUZXh0dXJlKCB2YWx1ZSwgbS5tYXBNZXRhbG5lc3NSZXBlYXQsIG0ubWFwTWV0YWxuZXNzT2Zmc2V0LCBtLm1hcE1ldGFsbmVzc1dyYXAsIG0ubWFwTWV0YWxuZXNzQW5pc290cm9weSApO1xuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdGNhc2UgJ21hcE1ldGFsbmVzc1JlcGVhdCc6XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBNZXRhbG5lc3NPZmZzZXQnOlxuXHRcdFx0XHRcdFx0Y2FzZSAnbWFwTWV0YWxuZXNzV3JhcCc6XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBNZXRhbG5lc3NBbmlzb3Ryb3B5Jzpcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBSb3VnaG5lc3MnOlxuXHRcdFx0XHRcdFx0XHRqc29uLnJvdWdobmVzc01hcCA9IGxvYWRUZXh0dXJlKCB2YWx1ZSwgbS5tYXBSb3VnaG5lc3NSZXBlYXQsIG0ubWFwUm91Z2huZXNzT2Zmc2V0LCBtLm1hcFJvdWdobmVzc1dyYXAsIG0ubWFwUm91Z2huZXNzQW5pc290cm9weSApO1xuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdGNhc2UgJ21hcFJvdWdobmVzc1JlcGVhdCc6XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBSb3VnaG5lc3NPZmZzZXQnOlxuXHRcdFx0XHRcdFx0Y2FzZSAnbWFwUm91Z2huZXNzV3JhcCc6XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBSb3VnaG5lc3NBbmlzb3Ryb3B5Jzpcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBBbHBoYSc6XG5cdFx0XHRcdFx0XHRcdGpzb24uYWxwaGFNYXAgPSBsb2FkVGV4dHVyZSggdmFsdWUsIG0ubWFwQWxwaGFSZXBlYXQsIG0ubWFwQWxwaGFPZmZzZXQsIG0ubWFwQWxwaGFXcmFwLCBtLm1hcEFscGhhQW5pc290cm9weSApO1xuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdGNhc2UgJ21hcEFscGhhUmVwZWF0Jzpcblx0XHRcdFx0XHRcdGNhc2UgJ21hcEFscGhhT2Zmc2V0Jzpcblx0XHRcdFx0XHRcdGNhc2UgJ21hcEFscGhhV3JhcCc6XG5cdFx0XHRcdFx0XHRjYXNlICdtYXBBbHBoYUFuaXNvdHJvcHknOlxuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdGNhc2UgJ2ZsaXBTaWRlZCc6XG5cdFx0XHRcdFx0XHRcdGpzb24uc2lkZSA9IEJhY2tTaWRlO1xuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdGNhc2UgJ2RvdWJsZVNpZGVkJzpcblx0XHRcdFx0XHRcdFx0anNvbi5zaWRlID0gRG91YmxlU2lkZTtcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlICd0cmFuc3BhcmVuY3knOlxuXHRcdFx0XHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5Mb2FkZXIuY3JlYXRlTWF0ZXJpYWw6IHRyYW5zcGFyZW5jeSBoYXMgYmVlbiByZW5hbWVkIHRvIG9wYWNpdHknICk7XG5cdFx0XHRcdFx0XHRcdGpzb24ub3BhY2l0eSA9IHZhbHVlO1xuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdGNhc2UgJ2RlcHRoVGVzdCc6XG5cdFx0XHRcdFx0XHRjYXNlICdkZXB0aFdyaXRlJzpcblx0XHRcdFx0XHRcdGNhc2UgJ2NvbG9yV3JpdGUnOlxuXHRcdFx0XHRcdFx0Y2FzZSAnb3BhY2l0eSc6XG5cdFx0XHRcdFx0XHRjYXNlICdyZWZsZWN0aXZpdHknOlxuXHRcdFx0XHRcdFx0Y2FzZSAndHJhbnNwYXJlbnQnOlxuXHRcdFx0XHRcdFx0Y2FzZSAndmlzaWJsZSc6XG5cdFx0XHRcdFx0XHRjYXNlICd3aXJlZnJhbWUnOlxuXHRcdFx0XHRcdFx0XHRqc29uWyBuYW1lIF0gPSB2YWx1ZTtcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlICd2ZXJ0ZXhDb2xvcnMnOlxuXHRcdFx0XHRcdFx0XHRpZiAoIHZhbHVlID09PSB0cnVlICkganNvbi52ZXJ0ZXhDb2xvcnMgPSBWZXJ0ZXhDb2xvcnM7XG5cdFx0XHRcdFx0XHRcdGlmICggdmFsdWUgPT09ICdmYWNlJyApIGpzb24udmVydGV4Q29sb3JzID0gRmFjZUNvbG9ycztcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRkZWZhdWx0OlxuXHRcdFx0XHRcdFx0XHRjb25zb2xlLmVycm9yKCAnVEhSRUUuTG9hZGVyLmNyZWF0ZU1hdGVyaWFsOiBVbnN1cHBvcnRlZCcsIG5hbWUsIHZhbHVlICk7XG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCBqc29uLnR5cGUgPT09ICdNZXNoQmFzaWNNYXRlcmlhbCcgKSBkZWxldGUganNvbi5lbWlzc2l2ZTtcblx0XHRcdFx0aWYgKCBqc29uLnR5cGUgIT09ICdNZXNoUGhvbmdNYXRlcmlhbCcgKSBkZWxldGUganNvbi5zcGVjdWxhcjtcblxuXHRcdFx0XHRpZiAoIGpzb24ub3BhY2l0eSA8IDEgKSBqc29uLnRyYW5zcGFyZW50ID0gdHJ1ZTtcblxuXHRcdFx0XHRtYXRlcmlhbExvYWRlci5zZXRUZXh0dXJlcyggdGV4dHVyZXMgKTtcblxuXHRcdFx0XHRyZXR1cm4gbWF0ZXJpYWxMb2FkZXIucGFyc2UoIGpzb24gKTtcblxuXHRcdFx0fTtcblxuXHRcdH0gKSgpXG5cblx0fTtcblxuXHRMb2FkZXIuSGFuZGxlcnMgPSB7XG5cblx0XHRoYW5kbGVyczogW10sXG5cblx0XHRhZGQ6IGZ1bmN0aW9uICggcmVnZXgsIGxvYWRlciApIHtcblxuXHRcdFx0dGhpcy5oYW5kbGVycy5wdXNoKCByZWdleCwgbG9hZGVyICk7XG5cblx0XHR9LFxuXG5cdFx0Z2V0OiBmdW5jdGlvbiAoIGZpbGUgKSB7XG5cblx0XHRcdHZhciBoYW5kbGVycyA9IHRoaXMuaGFuZGxlcnM7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGhhbmRsZXJzLmxlbmd0aDsgaSA8IGw7IGkgKz0gMiApIHtcblxuXHRcdFx0XHR2YXIgcmVnZXggPSBoYW5kbGVyc1sgaSBdO1xuXHRcdFx0XHR2YXIgbG9hZGVyICA9IGhhbmRsZXJzWyBpICsgMSBdO1xuXG5cdFx0XHRcdGlmICggcmVnZXgudGVzdCggZmlsZSApICkge1xuXG5cdFx0XHRcdFx0cmV0dXJuIGxvYWRlcjtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIG51bGw7XG5cblx0XHR9XG5cblx0fTtcblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICogQGF1dGhvciBhbHRlcmVkcSAvIGh0dHA6Ly9hbHRlcmVkcXVhbGlhLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gSlNPTkxvYWRlciggbWFuYWdlciApIHtcblxuXHRcdGlmICggdHlwZW9mIG1hbmFnZXIgPT09ICdib29sZWFuJyApIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuSlNPTkxvYWRlcjogc2hvd1N0YXR1cyBwYXJhbWV0ZXIgaGFzIGJlZW4gcmVtb3ZlZCBmcm9tIGNvbnN0cnVjdG9yLicgKTtcblx0XHRcdG1hbmFnZXIgPSB1bmRlZmluZWQ7XG5cblx0XHR9XG5cblx0XHR0aGlzLm1hbmFnZXIgPSAoIG1hbmFnZXIgIT09IHVuZGVmaW5lZCApID8gbWFuYWdlciA6IERlZmF1bHRMb2FkaW5nTWFuYWdlcjtcblxuXHRcdHRoaXMud2l0aENyZWRlbnRpYWxzID0gZmFsc2U7XG5cblx0fVxuXG5cdE9iamVjdC5hc3NpZ24oIEpTT05Mb2FkZXIucHJvdG90eXBlLCB7XG5cblx0XHRsb2FkOiBmdW5jdGlvbiggdXJsLCBvbkxvYWQsIG9uUHJvZ3Jlc3MsIG9uRXJyb3IgKSB7XG5cblx0XHRcdHZhciBzY29wZSA9IHRoaXM7XG5cblx0XHRcdHZhciB0ZXh0dXJlUGF0aCA9IHRoaXMudGV4dHVyZVBhdGggJiYgKCB0eXBlb2YgdGhpcy50ZXh0dXJlUGF0aCA9PT0gXCJzdHJpbmdcIiApID8gdGhpcy50ZXh0dXJlUGF0aCA6IExvYWRlci5wcm90b3R5cGUuZXh0cmFjdFVybEJhc2UoIHVybCApO1xuXG5cdFx0XHR2YXIgbG9hZGVyID0gbmV3IEZpbGVMb2FkZXIoIHRoaXMubWFuYWdlciApO1xuXHRcdFx0bG9hZGVyLnNldFdpdGhDcmVkZW50aWFscyggdGhpcy53aXRoQ3JlZGVudGlhbHMgKTtcblx0XHRcdGxvYWRlci5sb2FkKCB1cmwsIGZ1bmN0aW9uICggdGV4dCApIHtcblxuXHRcdFx0XHR2YXIganNvbiA9IEpTT04ucGFyc2UoIHRleHQgKTtcblx0XHRcdFx0dmFyIG1ldGFkYXRhID0ganNvbi5tZXRhZGF0YTtcblxuXHRcdFx0XHRpZiAoIG1ldGFkYXRhICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHR2YXIgdHlwZSA9IG1ldGFkYXRhLnR5cGU7XG5cblx0XHRcdFx0XHRpZiAoIHR5cGUgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdFx0aWYgKCB0eXBlLnRvTG93ZXJDYXNlKCkgPT09ICdvYmplY3QnICkge1xuXG5cdFx0XHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5KU09OTG9hZGVyOiAnICsgdXJsICsgJyBzaG91bGQgYmUgbG9hZGVkIHdpdGggVEhSRUUuT2JqZWN0TG9hZGVyIGluc3RlYWQuJyApO1xuXHRcdFx0XHRcdFx0XHRyZXR1cm47XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0aWYgKCB0eXBlLnRvTG93ZXJDYXNlKCkgPT09ICdzY2VuZScgKSB7XG5cblx0XHRcdFx0XHRcdFx0Y29uc29sZS5lcnJvciggJ1RIUkVFLkpTT05Mb2FkZXI6ICcgKyB1cmwgKyAnIHNob3VsZCBiZSBsb2FkZWQgd2l0aCBUSFJFRS5TY2VuZUxvYWRlciBpbnN0ZWFkLicgKTtcblx0XHRcdFx0XHRcdFx0cmV0dXJuO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBvYmplY3QgPSBzY29wZS5wYXJzZSgganNvbiwgdGV4dHVyZVBhdGggKTtcblx0XHRcdFx0b25Mb2FkKCBvYmplY3QuZ2VvbWV0cnksIG9iamVjdC5tYXRlcmlhbHMgKTtcblxuXHRcdFx0fSwgb25Qcm9ncmVzcywgb25FcnJvciApO1xuXG5cdFx0fSxcblxuXHRcdHNldFRleHR1cmVQYXRoOiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHR0aGlzLnRleHR1cmVQYXRoID0gdmFsdWU7XG5cblx0XHR9LFxuXG5cdFx0cGFyc2U6IGZ1bmN0aW9uICgganNvbiwgdGV4dHVyZVBhdGggKSB7XG5cblx0XHRcdHZhciBnZW9tZXRyeSA9IG5ldyBHZW9tZXRyeSgpLFxuXHRcdFx0c2NhbGUgPSAoIGpzb24uc2NhbGUgIT09IHVuZGVmaW5lZCApID8gMS4wIC8ganNvbi5zY2FsZSA6IDEuMDtcblxuXHRcdFx0cGFyc2VNb2RlbCggc2NhbGUgKTtcblxuXHRcdFx0cGFyc2VTa2luKCk7XG5cdFx0XHRwYXJzZU1vcnBoaW5nKCBzY2FsZSApO1xuXHRcdFx0cGFyc2VBbmltYXRpb25zKCk7XG5cblx0XHRcdGdlb21ldHJ5LmNvbXB1dGVGYWNlTm9ybWFscygpO1xuXHRcdFx0Z2VvbWV0cnkuY29tcHV0ZUJvdW5kaW5nU3BoZXJlKCk7XG5cblx0XHRcdGZ1bmN0aW9uIHBhcnNlTW9kZWwoIHNjYWxlICkge1xuXG5cdFx0XHRcdGZ1bmN0aW9uIGlzQml0U2V0KCB2YWx1ZSwgcG9zaXRpb24gKSB7XG5cblx0XHRcdFx0XHRyZXR1cm4gdmFsdWUgJiAoIDEgPDwgcG9zaXRpb24gKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0dmFyIGksIGosIGZpLFxuXG5cdFx0XHRcdG9mZnNldCwgekxlbmd0aCxcblxuXHRcdFx0Y29sb3JJbmRleCwgbm9ybWFsSW5kZXgsIHV2SW5kZXgsIG1hdGVyaWFsSW5kZXgsXG5cblx0XHRcdFx0dHlwZSxcblx0XHRcdFx0aXNRdWFkLFxuXHRcdFx0XHRoYXNNYXRlcmlhbCxcblx0XHRcdFx0aGFzRmFjZVZlcnRleFV2LFxuXHRcdFx0XHRoYXNGYWNlTm9ybWFsLCBoYXNGYWNlVmVydGV4Tm9ybWFsLFxuXHRcdFx0XHRoYXNGYWNlQ29sb3IsIGhhc0ZhY2VWZXJ0ZXhDb2xvcixcblxuXHRcdFx0dmVydGV4LCBmYWNlLCBmYWNlQSwgZmFjZUIsIGhleCwgbm9ybWFsLFxuXG5cdFx0XHRcdHV2TGF5ZXIsIHV2LCB1LCB2LFxuXG5cdFx0XHRcdGZhY2VzID0ganNvbi5mYWNlcyxcblx0XHRcdFx0dmVydGljZXMgPSBqc29uLnZlcnRpY2VzLFxuXHRcdFx0XHRub3JtYWxzID0ganNvbi5ub3JtYWxzLFxuXHRcdFx0XHRjb2xvcnMgPSBqc29uLmNvbG9ycyxcblxuXHRcdFx0XHRuVXZMYXllcnMgPSAwO1xuXG5cdFx0XHRcdGlmICgganNvbi51dnMgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdC8vIGRpc3JlZ2FyZCBlbXB0eSBhcnJheXNcblxuXHRcdFx0XHRcdGZvciAoIGkgPSAwOyBpIDwganNvbi51dnMubGVuZ3RoOyBpICsrICkge1xuXG5cdFx0XHRcdFx0XHRpZiAoIGpzb24udXZzWyBpIF0ubGVuZ3RoICkgblV2TGF5ZXJzICsrO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0Zm9yICggaSA9IDA7IGkgPCBuVXZMYXllcnM7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRcdGdlb21ldHJ5LmZhY2VWZXJ0ZXhVdnNbIGkgXSA9IFtdO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRvZmZzZXQgPSAwO1xuXHRcdFx0XHR6TGVuZ3RoID0gdmVydGljZXMubGVuZ3RoO1xuXG5cdFx0XHRcdHdoaWxlICggb2Zmc2V0IDwgekxlbmd0aCApIHtcblxuXHRcdFx0XHRcdHZlcnRleCA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdFx0XHR2ZXJ0ZXgueCA9IHZlcnRpY2VzWyBvZmZzZXQgKysgXSAqIHNjYWxlO1xuXHRcdFx0XHRcdHZlcnRleC55ID0gdmVydGljZXNbIG9mZnNldCArKyBdICogc2NhbGU7XG5cdFx0XHRcdFx0dmVydGV4LnogPSB2ZXJ0aWNlc1sgb2Zmc2V0ICsrIF0gKiBzY2FsZTtcblxuXHRcdFx0XHRcdGdlb21ldHJ5LnZlcnRpY2VzLnB1c2goIHZlcnRleCApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRvZmZzZXQgPSAwO1xuXHRcdFx0XHR6TGVuZ3RoID0gZmFjZXMubGVuZ3RoO1xuXG5cdFx0XHRcdHdoaWxlICggb2Zmc2V0IDwgekxlbmd0aCApIHtcblxuXHRcdFx0XHRcdHR5cGUgPSBmYWNlc1sgb2Zmc2V0ICsrIF07XG5cblxuXHRcdFx0XHRcdGlzUXVhZCAgICAgICAgICAgICAgPSBpc0JpdFNldCggdHlwZSwgMCApO1xuXHRcdFx0XHRcdGhhc01hdGVyaWFsICAgICAgICAgPSBpc0JpdFNldCggdHlwZSwgMSApO1xuXHRcdFx0XHRcdGhhc0ZhY2VWZXJ0ZXhVdiAgICAgPSBpc0JpdFNldCggdHlwZSwgMyApO1xuXHRcdFx0XHRcdGhhc0ZhY2VOb3JtYWwgICAgICAgPSBpc0JpdFNldCggdHlwZSwgNCApO1xuXHRcdFx0XHRcdGhhc0ZhY2VWZXJ0ZXhOb3JtYWwgPSBpc0JpdFNldCggdHlwZSwgNSApO1xuXHRcdFx0XHRcdGhhc0ZhY2VDb2xvclx0ICAgICA9IGlzQml0U2V0KCB0eXBlLCA2ICk7XG5cdFx0XHRcdFx0aGFzRmFjZVZlcnRleENvbG9yICA9IGlzQml0U2V0KCB0eXBlLCA3ICk7XG5cblx0XHRcdFx0XHQvLyBjb25zb2xlLmxvZyhcInR5cGVcIiwgdHlwZSwgXCJiaXRzXCIsIGlzUXVhZCwgaGFzTWF0ZXJpYWwsIGhhc0ZhY2VWZXJ0ZXhVdiwgaGFzRmFjZU5vcm1hbCwgaGFzRmFjZVZlcnRleE5vcm1hbCwgaGFzRmFjZUNvbG9yLCBoYXNGYWNlVmVydGV4Q29sb3IpO1xuXG5cdFx0XHRcdFx0aWYgKCBpc1F1YWQgKSB7XG5cblx0XHRcdFx0XHRcdGZhY2VBID0gbmV3IEZhY2UzKCk7XG5cdFx0XHRcdFx0XHRmYWNlQS5hID0gZmFjZXNbIG9mZnNldCBdO1xuXHRcdFx0XHRcdFx0ZmFjZUEuYiA9IGZhY2VzWyBvZmZzZXQgKyAxIF07XG5cdFx0XHRcdFx0XHRmYWNlQS5jID0gZmFjZXNbIG9mZnNldCArIDMgXTtcblxuXHRcdFx0XHRcdFx0ZmFjZUIgPSBuZXcgRmFjZTMoKTtcblx0XHRcdFx0XHRcdGZhY2VCLmEgPSBmYWNlc1sgb2Zmc2V0ICsgMSBdO1xuXHRcdFx0XHRcdFx0ZmFjZUIuYiA9IGZhY2VzWyBvZmZzZXQgKyAyIF07XG5cdFx0XHRcdFx0XHRmYWNlQi5jID0gZmFjZXNbIG9mZnNldCArIDMgXTtcblxuXHRcdFx0XHRcdFx0b2Zmc2V0ICs9IDQ7XG5cblx0XHRcdFx0XHRcdGlmICggaGFzTWF0ZXJpYWwgKSB7XG5cblx0XHRcdFx0XHRcdFx0bWF0ZXJpYWxJbmRleCA9IGZhY2VzWyBvZmZzZXQgKysgXTtcblx0XHRcdFx0XHRcdFx0ZmFjZUEubWF0ZXJpYWxJbmRleCA9IG1hdGVyaWFsSW5kZXg7XG5cdFx0XHRcdFx0XHRcdGZhY2VCLm1hdGVyaWFsSW5kZXggPSBtYXRlcmlhbEluZGV4O1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdC8vIHRvIGdldCBmYWNlIDw9PiB1diBpbmRleCBjb3JyZXNwb25kZW5jZVxuXG5cdFx0XHRcdFx0XHRmaSA9IGdlb21ldHJ5LmZhY2VzLmxlbmd0aDtcblxuXHRcdFx0XHRcdFx0aWYgKCBoYXNGYWNlVmVydGV4VXYgKSB7XG5cblx0XHRcdFx0XHRcdFx0Zm9yICggaSA9IDA7IGkgPCBuVXZMYXllcnM7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHR1dkxheWVyID0ganNvbi51dnNbIGkgXTtcblxuXHRcdFx0XHRcdFx0XHRcdGdlb21ldHJ5LmZhY2VWZXJ0ZXhVdnNbIGkgXVsgZmkgXSA9IFtdO1xuXHRcdFx0XHRcdFx0XHRcdGdlb21ldHJ5LmZhY2VWZXJ0ZXhVdnNbIGkgXVsgZmkgKyAxIF0gPSBbXTtcblxuXHRcdFx0XHRcdFx0XHRcdGZvciAoIGogPSAwOyBqIDwgNDsgaiArKyApIHtcblxuXHRcdFx0XHRcdFx0XHRcdFx0dXZJbmRleCA9IGZhY2VzWyBvZmZzZXQgKysgXTtcblxuXHRcdFx0XHRcdFx0XHRcdFx0dSA9IHV2TGF5ZXJbIHV2SW5kZXggKiAyIF07XG5cdFx0XHRcdFx0XHRcdFx0XHR2ID0gdXZMYXllclsgdXZJbmRleCAqIDIgKyAxIF07XG5cblx0XHRcdFx0XHRcdFx0XHRcdHV2ID0gbmV3IFZlY3RvcjIoIHUsIHYgKTtcblxuXHRcdFx0XHRcdFx0XHRcdFx0aWYgKCBqICE9PSAyICkgZ2VvbWV0cnkuZmFjZVZlcnRleFV2c1sgaSBdWyBmaSBdLnB1c2goIHV2ICk7XG5cdFx0XHRcdFx0XHRcdFx0XHRpZiAoIGogIT09IDAgKSBnZW9tZXRyeS5mYWNlVmVydGV4VXZzWyBpIF1bIGZpICsgMSBdLnB1c2goIHV2ICk7XG5cblx0XHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdGlmICggaGFzRmFjZU5vcm1hbCApIHtcblxuXHRcdFx0XHRcdFx0XHRub3JtYWxJbmRleCA9IGZhY2VzWyBvZmZzZXQgKysgXSAqIDM7XG5cblx0XHRcdFx0XHRcdFx0ZmFjZUEubm9ybWFsLnNldChcblx0XHRcdFx0XHRcdFx0XHRub3JtYWxzWyBub3JtYWxJbmRleCArKyBdLFxuXHRcdFx0XHRcdFx0XHRcdG5vcm1hbHNbIG5vcm1hbEluZGV4ICsrIF0sXG5cdFx0XHRcdFx0XHRcdFx0bm9ybWFsc1sgbm9ybWFsSW5kZXggXVxuXHRcdFx0XHRcdFx0XHQpO1xuXG5cdFx0XHRcdFx0XHRcdGZhY2VCLm5vcm1hbC5jb3B5KCBmYWNlQS5ub3JtYWwgKTtcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRpZiAoIGhhc0ZhY2VWZXJ0ZXhOb3JtYWwgKSB7XG5cblx0XHRcdFx0XHRcdFx0Zm9yICggaSA9IDA7IGkgPCA0OyBpICsrICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0bm9ybWFsSW5kZXggPSBmYWNlc1sgb2Zmc2V0ICsrIF0gKiAzO1xuXG5cdFx0XHRcdFx0XHRcdFx0bm9ybWFsID0gbmV3IFZlY3RvcjMoXG5cdFx0XHRcdFx0XHRcdFx0XHRub3JtYWxzWyBub3JtYWxJbmRleCArKyBdLFxuXHRcdFx0XHRcdFx0XHRcdFx0bm9ybWFsc1sgbm9ybWFsSW5kZXggKysgXSxcblx0XHRcdFx0XHRcdFx0XHRcdG5vcm1hbHNbIG5vcm1hbEluZGV4IF1cblx0XHRcdFx0XHRcdFx0XHQpO1xuXG5cblx0XHRcdFx0XHRcdFx0XHRpZiAoIGkgIT09IDIgKSBmYWNlQS52ZXJ0ZXhOb3JtYWxzLnB1c2goIG5vcm1hbCApO1xuXHRcdFx0XHRcdFx0XHRcdGlmICggaSAhPT0gMCApIGZhY2VCLnZlcnRleE5vcm1hbHMucHVzaCggbm9ybWFsICk7XG5cblx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHR9XG5cblxuXHRcdFx0XHRcdFx0aWYgKCBoYXNGYWNlQ29sb3IgKSB7XG5cblx0XHRcdFx0XHRcdFx0Y29sb3JJbmRleCA9IGZhY2VzWyBvZmZzZXQgKysgXTtcblx0XHRcdFx0XHRcdFx0aGV4ID0gY29sb3JzWyBjb2xvckluZGV4IF07XG5cblx0XHRcdFx0XHRcdFx0ZmFjZUEuY29sb3Iuc2V0SGV4KCBoZXggKTtcblx0XHRcdFx0XHRcdFx0ZmFjZUIuY29sb3Iuc2V0SGV4KCBoZXggKTtcblxuXHRcdFx0XHRcdFx0fVxuXG5cblx0XHRcdFx0XHRcdGlmICggaGFzRmFjZVZlcnRleENvbG9yICkge1xuXG5cdFx0XHRcdFx0XHRcdGZvciAoIGkgPSAwOyBpIDwgNDsgaSArKyApIHtcblxuXHRcdFx0XHRcdFx0XHRcdGNvbG9ySW5kZXggPSBmYWNlc1sgb2Zmc2V0ICsrIF07XG5cdFx0XHRcdFx0XHRcdFx0aGV4ID0gY29sb3JzWyBjb2xvckluZGV4IF07XG5cblx0XHRcdFx0XHRcdFx0XHRpZiAoIGkgIT09IDIgKSBmYWNlQS52ZXJ0ZXhDb2xvcnMucHVzaCggbmV3IENvbG9yKCBoZXggKSApO1xuXHRcdFx0XHRcdFx0XHRcdGlmICggaSAhPT0gMCApIGZhY2VCLnZlcnRleENvbG9ycy5wdXNoKCBuZXcgQ29sb3IoIGhleCApICk7XG5cblx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdGdlb21ldHJ5LmZhY2VzLnB1c2goIGZhY2VBICk7XG5cdFx0XHRcdFx0XHRnZW9tZXRyeS5mYWNlcy5wdXNoKCBmYWNlQiApO1xuXG5cdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0ZmFjZSA9IG5ldyBGYWNlMygpO1xuXHRcdFx0XHRcdFx0ZmFjZS5hID0gZmFjZXNbIG9mZnNldCArKyBdO1xuXHRcdFx0XHRcdFx0ZmFjZS5iID0gZmFjZXNbIG9mZnNldCArKyBdO1xuXHRcdFx0XHRcdFx0ZmFjZS5jID0gZmFjZXNbIG9mZnNldCArKyBdO1xuXG5cdFx0XHRcdFx0XHRpZiAoIGhhc01hdGVyaWFsICkge1xuXG5cdFx0XHRcdFx0XHRcdG1hdGVyaWFsSW5kZXggPSBmYWNlc1sgb2Zmc2V0ICsrIF07XG5cdFx0XHRcdFx0XHRcdGZhY2UubWF0ZXJpYWxJbmRleCA9IG1hdGVyaWFsSW5kZXg7XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0Ly8gdG8gZ2V0IGZhY2UgPD0+IHV2IGluZGV4IGNvcnJlc3BvbmRlbmNlXG5cblx0XHRcdFx0XHRcdGZpID0gZ2VvbWV0cnkuZmFjZXMubGVuZ3RoO1xuXG5cdFx0XHRcdFx0XHRpZiAoIGhhc0ZhY2VWZXJ0ZXhVdiApIHtcblxuXHRcdFx0XHRcdFx0XHRmb3IgKCBpID0gMDsgaSA8IG5VdkxheWVyczsgaSArKyApIHtcblxuXHRcdFx0XHRcdFx0XHRcdHV2TGF5ZXIgPSBqc29uLnV2c1sgaSBdO1xuXG5cdFx0XHRcdFx0XHRcdFx0Z2VvbWV0cnkuZmFjZVZlcnRleFV2c1sgaSBdWyBmaSBdID0gW107XG5cblx0XHRcdFx0XHRcdFx0XHRmb3IgKCBqID0gMDsgaiA8IDM7IGogKysgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHRcdHV2SW5kZXggPSBmYWNlc1sgb2Zmc2V0ICsrIF07XG5cblx0XHRcdFx0XHRcdFx0XHRcdHUgPSB1dkxheWVyWyB1dkluZGV4ICogMiBdO1xuXHRcdFx0XHRcdFx0XHRcdFx0diA9IHV2TGF5ZXJbIHV2SW5kZXggKiAyICsgMSBdO1xuXG5cdFx0XHRcdFx0XHRcdFx0XHR1diA9IG5ldyBWZWN0b3IyKCB1LCB2ICk7XG5cblx0XHRcdFx0XHRcdFx0XHRcdGdlb21ldHJ5LmZhY2VWZXJ0ZXhVdnNbIGkgXVsgZmkgXS5wdXNoKCB1diApO1xuXG5cdFx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRpZiAoIGhhc0ZhY2VOb3JtYWwgKSB7XG5cblx0XHRcdFx0XHRcdFx0bm9ybWFsSW5kZXggPSBmYWNlc1sgb2Zmc2V0ICsrIF0gKiAzO1xuXG5cdFx0XHRcdFx0XHRcdGZhY2Uubm9ybWFsLnNldChcblx0XHRcdFx0XHRcdFx0XHRub3JtYWxzWyBub3JtYWxJbmRleCArKyBdLFxuXHRcdFx0XHRcdFx0XHRcdG5vcm1hbHNbIG5vcm1hbEluZGV4ICsrIF0sXG5cdFx0XHRcdFx0XHRcdFx0bm9ybWFsc1sgbm9ybWFsSW5kZXggXVxuXHRcdFx0XHRcdFx0XHQpO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdGlmICggaGFzRmFjZVZlcnRleE5vcm1hbCApIHtcblxuXHRcdFx0XHRcdFx0XHRmb3IgKCBpID0gMDsgaSA8IDM7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHRub3JtYWxJbmRleCA9IGZhY2VzWyBvZmZzZXQgKysgXSAqIDM7XG5cblx0XHRcdFx0XHRcdFx0XHRub3JtYWwgPSBuZXcgVmVjdG9yMyhcblx0XHRcdFx0XHRcdFx0XHRcdG5vcm1hbHNbIG5vcm1hbEluZGV4ICsrIF0sXG5cdFx0XHRcdFx0XHRcdFx0XHRub3JtYWxzWyBub3JtYWxJbmRleCArKyBdLFxuXHRcdFx0XHRcdFx0XHRcdFx0bm9ybWFsc1sgbm9ybWFsSW5kZXggXVxuXHRcdFx0XHRcdFx0XHRcdCk7XG5cblx0XHRcdFx0XHRcdFx0XHRmYWNlLnZlcnRleE5vcm1hbHMucHVzaCggbm9ybWFsICk7XG5cblx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHR9XG5cblxuXHRcdFx0XHRcdFx0aWYgKCBoYXNGYWNlQ29sb3IgKSB7XG5cblx0XHRcdFx0XHRcdFx0Y29sb3JJbmRleCA9IGZhY2VzWyBvZmZzZXQgKysgXTtcblx0XHRcdFx0XHRcdFx0ZmFjZS5jb2xvci5zZXRIZXgoIGNvbG9yc1sgY29sb3JJbmRleCBdICk7XG5cblx0XHRcdFx0XHRcdH1cblxuXG5cdFx0XHRcdFx0XHRpZiAoIGhhc0ZhY2VWZXJ0ZXhDb2xvciApIHtcblxuXHRcdFx0XHRcdFx0XHRmb3IgKCBpID0gMDsgaSA8IDM7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHRjb2xvckluZGV4ID0gZmFjZXNbIG9mZnNldCArKyBdO1xuXHRcdFx0XHRcdFx0XHRcdGZhY2UudmVydGV4Q29sb3JzLnB1c2goIG5ldyBDb2xvciggY29sb3JzWyBjb2xvckluZGV4IF0gKSApO1xuXG5cdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRnZW9tZXRyeS5mYWNlcy5wdXNoKCBmYWNlICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdGZ1bmN0aW9uIHBhcnNlU2tpbigpIHtcblxuXHRcdFx0XHR2YXIgaW5mbHVlbmNlc1BlclZlcnRleCA9ICgganNvbi5pbmZsdWVuY2VzUGVyVmVydGV4ICE9PSB1bmRlZmluZWQgKSA/IGpzb24uaW5mbHVlbmNlc1BlclZlcnRleCA6IDI7XG5cblx0XHRcdFx0aWYgKCBqc29uLnNraW5XZWlnaHRzICkge1xuXG5cdFx0XHRcdFx0Zm9yICggdmFyIGkgPSAwLCBsID0ganNvbi5za2luV2VpZ2h0cy5sZW5ndGg7IGkgPCBsOyBpICs9IGluZmx1ZW5jZXNQZXJWZXJ0ZXggKSB7XG5cblx0XHRcdFx0XHRcdHZhciB4ID0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAganNvbi5za2luV2VpZ2h0c1sgaSBdO1xuXHRcdFx0XHRcdFx0dmFyIHkgPSAoIGluZmx1ZW5jZXNQZXJWZXJ0ZXggPiAxICkgPyBqc29uLnNraW5XZWlnaHRzWyBpICsgMSBdIDogMDtcblx0XHRcdFx0XHRcdHZhciB6ID0gKCBpbmZsdWVuY2VzUGVyVmVydGV4ID4gMiApID8ganNvbi5za2luV2VpZ2h0c1sgaSArIDIgXSA6IDA7XG5cdFx0XHRcdFx0XHR2YXIgdyA9ICggaW5mbHVlbmNlc1BlclZlcnRleCA+IDMgKSA/IGpzb24uc2tpbldlaWdodHNbIGkgKyAzIF0gOiAwO1xuXG5cdFx0XHRcdFx0XHRnZW9tZXRyeS5za2luV2VpZ2h0cy5wdXNoKCBuZXcgVmVjdG9yNCggeCwgeSwgeiwgdyApICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICgganNvbi5za2luSW5kaWNlcyApIHtcblxuXHRcdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGpzb24uc2tpbkluZGljZXMubGVuZ3RoOyBpIDwgbDsgaSArPSBpbmZsdWVuY2VzUGVyVmVydGV4ICkge1xuXG5cdFx0XHRcdFx0XHR2YXIgYSA9ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGpzb24uc2tpbkluZGljZXNbIGkgXTtcblx0XHRcdFx0XHRcdHZhciBiID0gKCBpbmZsdWVuY2VzUGVyVmVydGV4ID4gMSApID8ganNvbi5za2luSW5kaWNlc1sgaSArIDEgXSA6IDA7XG5cdFx0XHRcdFx0XHR2YXIgYyA9ICggaW5mbHVlbmNlc1BlclZlcnRleCA+IDIgKSA/IGpzb24uc2tpbkluZGljZXNbIGkgKyAyIF0gOiAwO1xuXHRcdFx0XHRcdFx0dmFyIGQgPSAoIGluZmx1ZW5jZXNQZXJWZXJ0ZXggPiAzICkgPyBqc29uLnNraW5JbmRpY2VzWyBpICsgMyBdIDogMDtcblxuXHRcdFx0XHRcdFx0Z2VvbWV0cnkuc2tpbkluZGljZXMucHVzaCggbmV3IFZlY3RvcjQoIGEsIGIsIGMsIGQgKSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRnZW9tZXRyeS5ib25lcyA9IGpzb24uYm9uZXM7XG5cblx0XHRcdFx0aWYgKCBnZW9tZXRyeS5ib25lcyAmJiBnZW9tZXRyeS5ib25lcy5sZW5ndGggPiAwICYmICggZ2VvbWV0cnkuc2tpbldlaWdodHMubGVuZ3RoICE9PSBnZW9tZXRyeS5za2luSW5kaWNlcy5sZW5ndGggfHwgZ2VvbWV0cnkuc2tpbkluZGljZXMubGVuZ3RoICE9PSBnZW9tZXRyeS52ZXJ0aWNlcy5sZW5ndGggKSApIHtcblxuXHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1doZW4gc2tpbm5pbmcsIG51bWJlciBvZiB2ZXJ0aWNlcyAoJyArIGdlb21ldHJ5LnZlcnRpY2VzLmxlbmd0aCArICcpLCBza2luSW5kaWNlcyAoJyArXG5cdFx0XHRcdFx0XHRnZW9tZXRyeS5za2luSW5kaWNlcy5sZW5ndGggKyAnKSwgYW5kIHNraW5XZWlnaHRzICgnICsgZ2VvbWV0cnkuc2tpbldlaWdodHMubGVuZ3RoICsgJykgc2hvdWxkIG1hdGNoLicgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0ZnVuY3Rpb24gcGFyc2VNb3JwaGluZyggc2NhbGUgKSB7XG5cblx0XHRcdFx0aWYgKCBqc29uLm1vcnBoVGFyZ2V0cyAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0Zm9yICggdmFyIGkgPSAwLCBsID0ganNvbi5tb3JwaFRhcmdldHMubGVuZ3RoOyBpIDwgbDsgaSArKyApIHtcblxuXHRcdFx0XHRcdFx0Z2VvbWV0cnkubW9ycGhUYXJnZXRzWyBpIF0gPSB7fTtcblx0XHRcdFx0XHRcdGdlb21ldHJ5Lm1vcnBoVGFyZ2V0c1sgaSBdLm5hbWUgPSBqc29uLm1vcnBoVGFyZ2V0c1sgaSBdLm5hbWU7XG5cdFx0XHRcdFx0XHRnZW9tZXRyeS5tb3JwaFRhcmdldHNbIGkgXS52ZXJ0aWNlcyA9IFtdO1xuXG5cdFx0XHRcdFx0XHR2YXIgZHN0VmVydGljZXMgPSBnZW9tZXRyeS5tb3JwaFRhcmdldHNbIGkgXS52ZXJ0aWNlcztcblx0XHRcdFx0XHRcdHZhciBzcmNWZXJ0aWNlcyA9IGpzb24ubW9ycGhUYXJnZXRzWyBpIF0udmVydGljZXM7XG5cblx0XHRcdFx0XHRcdGZvciAoIHZhciB2ID0gMCwgdmwgPSBzcmNWZXJ0aWNlcy5sZW5ndGg7IHYgPCB2bDsgdiArPSAzICkge1xuXG5cdFx0XHRcdFx0XHRcdHZhciB2ZXJ0ZXggPSBuZXcgVmVjdG9yMygpO1xuXHRcdFx0XHRcdFx0XHR2ZXJ0ZXgueCA9IHNyY1ZlcnRpY2VzWyB2IF0gKiBzY2FsZTtcblx0XHRcdFx0XHRcdFx0dmVydGV4LnkgPSBzcmNWZXJ0aWNlc1sgdiArIDEgXSAqIHNjYWxlO1xuXHRcdFx0XHRcdFx0XHR2ZXJ0ZXgueiA9IHNyY1ZlcnRpY2VzWyB2ICsgMiBdICogc2NhbGU7XG5cblx0XHRcdFx0XHRcdFx0ZHN0VmVydGljZXMucHVzaCggdmVydGV4ICk7XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCBqc29uLm1vcnBoQ29sb3JzICE9PSB1bmRlZmluZWQgJiYganNvbi5tb3JwaENvbG9ycy5sZW5ndGggPiAwICkge1xuXG5cdFx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuSlNPTkxvYWRlcjogXCJtb3JwaENvbG9yc1wiIG5vIGxvbmdlciBzdXBwb3J0ZWQuIFVzaW5nIHRoZW0gYXMgZmFjZSBjb2xvcnMuJyApO1xuXG5cdFx0XHRcdFx0dmFyIGZhY2VzID0gZ2VvbWV0cnkuZmFjZXM7XG5cdFx0XHRcdFx0dmFyIG1vcnBoQ29sb3JzID0ganNvbi5tb3JwaENvbG9yc1sgMCBdLmNvbG9ycztcblxuXHRcdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGZhY2VzLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRcdGZhY2VzWyBpIF0uY29sb3IuZnJvbUFycmF5KCBtb3JwaENvbG9ycywgaSAqIDMgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0ZnVuY3Rpb24gcGFyc2VBbmltYXRpb25zKCkge1xuXG5cdFx0XHRcdHZhciBvdXRwdXRBbmltYXRpb25zID0gW107XG5cblx0XHRcdFx0Ly8gcGFyc2Ugb2xkIHN0eWxlIEJvbmUvSGllcmFyY2h5IGFuaW1hdGlvbnNcblx0XHRcdFx0dmFyIGFuaW1hdGlvbnMgPSBbXTtcblxuXHRcdFx0XHRpZiAoIGpzb24uYW5pbWF0aW9uICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRhbmltYXRpb25zLnB1c2goIGpzb24uYW5pbWF0aW9uICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICgganNvbi5hbmltYXRpb25zICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRpZiAoIGpzb24uYW5pbWF0aW9ucy5sZW5ndGggKSB7XG5cblx0XHRcdFx0XHRcdGFuaW1hdGlvbnMgPSBhbmltYXRpb25zLmNvbmNhdCgganNvbi5hbmltYXRpb25zICk7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRhbmltYXRpb25zLnB1c2goIGpzb24uYW5pbWF0aW9ucyApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBhbmltYXRpb25zLmxlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHRcdHZhciBjbGlwID0gQW5pbWF0aW9uQ2xpcC5wYXJzZUFuaW1hdGlvbiggYW5pbWF0aW9uc1sgaSBdLCBnZW9tZXRyeS5ib25lcyApO1xuXHRcdFx0XHRcdGlmICggY2xpcCApIG91dHB1dEFuaW1hdGlvbnMucHVzaCggY2xpcCApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBwYXJzZSBpbXBsaWNpdCBtb3JwaCBhbmltYXRpb25zXG5cdFx0XHRcdGlmICggZ2VvbWV0cnkubW9ycGhUYXJnZXRzICkge1xuXG5cdFx0XHRcdFx0Ly8gVE9ETzogRmlndXJlIG91dCB3aGF0IGFuIGFwcHJvcHJhaXRlIEZQUyBpcyBmb3IgbW9ycGggdGFyZ2V0IGFuaW1hdGlvbnMgLS0gZGVmYXVsdGluZyB0byAxMCwgYnV0IHJlYWxseSBpdCBpcyBjb21wbGV0ZWx5IGFyYml0cmFyeS5cblx0XHRcdFx0XHR2YXIgbW9ycGhBbmltYXRpb25DbGlwcyA9IEFuaW1hdGlvbkNsaXAuQ3JlYXRlQ2xpcHNGcm9tTW9ycGhUYXJnZXRTZXF1ZW5jZXMoIGdlb21ldHJ5Lm1vcnBoVGFyZ2V0cywgMTAgKTtcblx0XHRcdFx0XHRvdXRwdXRBbmltYXRpb25zID0gb3V0cHV0QW5pbWF0aW9ucy5jb25jYXQoIG1vcnBoQW5pbWF0aW9uQ2xpcHMgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCBvdXRwdXRBbmltYXRpb25zLmxlbmd0aCA+IDAgKSBnZW9tZXRyeS5hbmltYXRpb25zID0gb3V0cHV0QW5pbWF0aW9ucztcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIGpzb24ubWF0ZXJpYWxzID09PSB1bmRlZmluZWQgfHwganNvbi5tYXRlcmlhbHMubGVuZ3RoID09PSAwICkge1xuXG5cdFx0XHRcdHJldHVybiB7IGdlb21ldHJ5OiBnZW9tZXRyeSB9O1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdHZhciBtYXRlcmlhbHMgPSBMb2FkZXIucHJvdG90eXBlLmluaXRNYXRlcmlhbHMoIGpzb24ubWF0ZXJpYWxzLCB0ZXh0dXJlUGF0aCwgdGhpcy5jcm9zc09yaWdpbiApO1xuXG5cdFx0XHRcdHJldHVybiB7IGdlb21ldHJ5OiBnZW9tZXRyeSwgbWF0ZXJpYWxzOiBtYXRlcmlhbHMgfTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdH0gKTtcblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gT2JqZWN0TG9hZGVyKCBtYW5hZ2VyICkge1xuXG5cdFx0dGhpcy5tYW5hZ2VyID0gKCBtYW5hZ2VyICE9PSB1bmRlZmluZWQgKSA/IG1hbmFnZXIgOiBEZWZhdWx0TG9hZGluZ01hbmFnZXI7XG5cdFx0dGhpcy50ZXh0dXJlUGF0aCA9ICcnO1xuXG5cdH1cblxuXHRPYmplY3QuYXNzaWduKCBPYmplY3RMb2FkZXIucHJvdG90eXBlLCB7XG5cblx0XHRsb2FkOiBmdW5jdGlvbiAoIHVybCwgb25Mb2FkLCBvblByb2dyZXNzLCBvbkVycm9yICkge1xuXG5cdFx0XHRpZiAoIHRoaXMudGV4dHVyZVBhdGggPT09ICcnICkge1xuXG5cdFx0XHRcdHRoaXMudGV4dHVyZVBhdGggPSB1cmwuc3Vic3RyaW5nKCAwLCB1cmwubGFzdEluZGV4T2YoICcvJyApICsgMSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBzY29wZSA9IHRoaXM7XG5cblx0XHRcdHZhciBsb2FkZXIgPSBuZXcgRmlsZUxvYWRlciggc2NvcGUubWFuYWdlciApO1xuXHRcdFx0bG9hZGVyLmxvYWQoIHVybCwgZnVuY3Rpb24gKCB0ZXh0ICkge1xuXG5cdFx0XHRcdHZhciBqc29uID0gbnVsbDtcblxuXHRcdFx0XHR0cnkge1xuXG5cdFx0XHRcdFx0anNvbiA9IEpTT04ucGFyc2UoIHRleHQgKTtcblxuXHRcdFx0XHR9IGNhdGNoICggZXJyb3IgKSB7XG5cblx0XHRcdFx0XHRjb25zb2xlLmVycm9yKCAnVEhSRUU6T2JqZWN0TG9hZGVyOiBDYW5cXCd0IHBhcnNlICcgKyB1cmwgKyAnLicsIGVycm9yLm1lc3NhZ2UgKTtcblx0XHRcdFx0XHRyZXR1cm47XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBtZXRhZGF0YSA9IGpzb24ubWV0YWRhdGE7XG5cblx0XHRcdFx0aWYgKCBtZXRhZGF0YSA9PT0gdW5kZWZpbmVkIHx8IG1ldGFkYXRhLnR5cGUgPT09IHVuZGVmaW5lZCB8fCBtZXRhZGF0YS50eXBlLnRvTG93ZXJDYXNlKCkgPT09ICdnZW9tZXRyeScgKSB7XG5cblx0XHRcdFx0XHRjb25zb2xlLmVycm9yKCAnVEhSRUUuT2JqZWN0TG9hZGVyOiBDYW5cXCd0IGxvYWQgJyArIHVybCArICcuIFVzZSBUSFJFRS5KU09OTG9hZGVyIGluc3RlYWQuJyApO1xuXHRcdFx0XHRcdHJldHVybjtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0c2NvcGUucGFyc2UoIGpzb24sIG9uTG9hZCApO1xuXG5cdFx0XHR9LCBvblByb2dyZXNzLCBvbkVycm9yICk7XG5cblx0XHR9LFxuXG5cdFx0c2V0VGV4dHVyZVBhdGg6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdHRoaXMudGV4dHVyZVBhdGggPSB2YWx1ZTtcblxuXHRcdH0sXG5cblx0XHRzZXRDcm9zc09yaWdpbjogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0dGhpcy5jcm9zc09yaWdpbiA9IHZhbHVlO1xuXG5cdFx0fSxcblxuXHRcdHBhcnNlOiBmdW5jdGlvbiAoIGpzb24sIG9uTG9hZCApIHtcblxuXHRcdFx0dmFyIGdlb21ldHJpZXMgPSB0aGlzLnBhcnNlR2VvbWV0cmllcygganNvbi5nZW9tZXRyaWVzICk7XG5cblx0XHRcdHZhciBpbWFnZXMgPSB0aGlzLnBhcnNlSW1hZ2VzKCBqc29uLmltYWdlcywgZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdGlmICggb25Mb2FkICE9PSB1bmRlZmluZWQgKSBvbkxvYWQoIG9iamVjdCApO1xuXG5cdFx0XHR9ICk7XG5cblx0XHRcdHZhciB0ZXh0dXJlcyA9IHRoaXMucGFyc2VUZXh0dXJlcygganNvbi50ZXh0dXJlcywgaW1hZ2VzICk7XG5cdFx0XHR2YXIgbWF0ZXJpYWxzID0gdGhpcy5wYXJzZU1hdGVyaWFscygganNvbi5tYXRlcmlhbHMsIHRleHR1cmVzICk7XG5cblx0XHRcdHZhciBvYmplY3QgPSB0aGlzLnBhcnNlT2JqZWN0KCBqc29uLm9iamVjdCwgZ2VvbWV0cmllcywgbWF0ZXJpYWxzICk7XG5cblx0XHRcdGlmICgganNvbi5hbmltYXRpb25zICkge1xuXG5cdFx0XHRcdG9iamVjdC5hbmltYXRpb25zID0gdGhpcy5wYXJzZUFuaW1hdGlvbnMoIGpzb24uYW5pbWF0aW9ucyApO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICgganNvbi5pbWFnZXMgPT09IHVuZGVmaW5lZCB8fCBqc29uLmltYWdlcy5sZW5ndGggPT09IDAgKSB7XG5cblx0XHRcdFx0aWYgKCBvbkxvYWQgIT09IHVuZGVmaW5lZCApIG9uTG9hZCggb2JqZWN0ICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIG9iamVjdDtcblxuXHRcdH0sXG5cblx0XHRwYXJzZUdlb21ldHJpZXM6IGZ1bmN0aW9uICgganNvbiApIHtcblxuXHRcdFx0dmFyIGdlb21ldHJpZXMgPSB7fTtcblxuXHRcdFx0aWYgKCBqc29uICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0dmFyIGdlb21ldHJ5TG9hZGVyID0gbmV3IEpTT05Mb2FkZXIoKTtcblx0XHRcdFx0dmFyIGJ1ZmZlckdlb21ldHJ5TG9hZGVyID0gbmV3IEJ1ZmZlckdlb21ldHJ5TG9hZGVyKCk7XG5cblx0XHRcdFx0Zm9yICggdmFyIGkgPSAwLCBsID0ganNvbi5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdFx0dmFyIGdlb21ldHJ5O1xuXHRcdFx0XHRcdHZhciBkYXRhID0ganNvblsgaSBdO1xuXG5cdFx0XHRcdFx0c3dpdGNoICggZGF0YS50eXBlICkge1xuXG5cdFx0XHRcdFx0XHRjYXNlICdQbGFuZUdlb21ldHJ5Jzpcblx0XHRcdFx0XHRcdGNhc2UgJ1BsYW5lQnVmZmVyR2VvbWV0cnknOlxuXG5cdFx0XHRcdFx0XHRcdGdlb21ldHJ5ID0gbmV3IEdlb21ldHJpZXNbIGRhdGEudHlwZSBdKFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEud2lkdGgsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS5oZWlnaHQsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS53aWR0aFNlZ21lbnRzLFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEuaGVpZ2h0U2VnbWVudHNcblx0XHRcdFx0XHRcdFx0KTtcblxuXHRcdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdFx0Y2FzZSAnQm94R2VvbWV0cnknOlxuXHRcdFx0XHRcdFx0Y2FzZSAnQm94QnVmZmVyR2VvbWV0cnknOlxuXHRcdFx0XHRcdFx0Y2FzZSAnQ3ViZUdlb21ldHJ5JzogLy8gYmFja3dhcmRzIGNvbXBhdGlibGVcblxuXHRcdFx0XHRcdFx0XHRnZW9tZXRyeSA9IG5ldyBHZW9tZXRyaWVzWyBkYXRhLnR5cGUgXShcblx0XHRcdFx0XHRcdFx0XHRkYXRhLndpZHRoLFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEuaGVpZ2h0LFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEuZGVwdGgsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS53aWR0aFNlZ21lbnRzLFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEuaGVpZ2h0U2VnbWVudHMsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS5kZXB0aFNlZ21lbnRzXG5cdFx0XHRcdFx0XHRcdCk7XG5cblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRcdGNhc2UgJ0NpcmNsZUdlb21ldHJ5Jzpcblx0XHRcdFx0XHRcdGNhc2UgJ0NpcmNsZUJ1ZmZlckdlb21ldHJ5JzpcblxuXHRcdFx0XHRcdFx0XHRnZW9tZXRyeSA9IG5ldyBHZW9tZXRyaWVzWyBkYXRhLnR5cGUgXShcblx0XHRcdFx0XHRcdFx0XHRkYXRhLnJhZGl1cyxcblx0XHRcdFx0XHRcdFx0XHRkYXRhLnNlZ21lbnRzLFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEudGhldGFTdGFydCxcblx0XHRcdFx0XHRcdFx0XHRkYXRhLnRoZXRhTGVuZ3RoXG5cdFx0XHRcdFx0XHRcdCk7XG5cblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRcdGNhc2UgJ0N5bGluZGVyR2VvbWV0cnknOlxuXHRcdFx0XHRcdFx0Y2FzZSAnQ3lsaW5kZXJCdWZmZXJHZW9tZXRyeSc6XG5cblx0XHRcdFx0XHRcdFx0Z2VvbWV0cnkgPSBuZXcgR2VvbWV0cmllc1sgZGF0YS50eXBlIF0oXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS5yYWRpdXNUb3AsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS5yYWRpdXNCb3R0b20sXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS5oZWlnaHQsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS5yYWRpYWxTZWdtZW50cyxcblx0XHRcdFx0XHRcdFx0XHRkYXRhLmhlaWdodFNlZ21lbnRzLFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEub3BlbkVuZGVkLFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEudGhldGFTdGFydCxcblx0XHRcdFx0XHRcdFx0XHRkYXRhLnRoZXRhTGVuZ3RoXG5cdFx0XHRcdFx0XHRcdCk7XG5cblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRcdGNhc2UgJ0NvbmVHZW9tZXRyeSc6XG5cdFx0XHRcdFx0XHRjYXNlICdDb25lQnVmZmVyR2VvbWV0cnknOlxuXG5cdFx0XHRcdFx0XHRcdGdlb21ldHJ5ID0gbmV3IEdlb21ldHJpZXNbIGRhdGEudHlwZSBdKFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEucmFkaXVzLFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEuaGVpZ2h0LFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEucmFkaWFsU2VnbWVudHMsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS5oZWlnaHRTZWdtZW50cyxcblx0XHRcdFx0XHRcdFx0XHRkYXRhLm9wZW5FbmRlZCxcblx0XHRcdFx0XHRcdFx0XHRkYXRhLnRoZXRhU3RhcnQsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS50aGV0YUxlbmd0aFxuXHRcdFx0XHRcdFx0XHQpO1xuXG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRjYXNlICdTcGhlcmVHZW9tZXRyeSc6XG5cdFx0XHRcdFx0XHRjYXNlICdTcGhlcmVCdWZmZXJHZW9tZXRyeSc6XG5cblx0XHRcdFx0XHRcdFx0Z2VvbWV0cnkgPSBuZXcgR2VvbWV0cmllc1sgZGF0YS50eXBlIF0oXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS5yYWRpdXMsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS53aWR0aFNlZ21lbnRzLFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEuaGVpZ2h0U2VnbWVudHMsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS5waGlTdGFydCxcblx0XHRcdFx0XHRcdFx0XHRkYXRhLnBoaUxlbmd0aCxcblx0XHRcdFx0XHRcdFx0XHRkYXRhLnRoZXRhU3RhcnQsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS50aGV0YUxlbmd0aFxuXHRcdFx0XHRcdFx0XHQpO1xuXG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRjYXNlICdEb2RlY2FoZWRyb25HZW9tZXRyeSc6XG5cdFx0XHRcdFx0XHRjYXNlICdJY29zYWhlZHJvbkdlb21ldHJ5Jzpcblx0XHRcdFx0XHRcdGNhc2UgJ09jdGFoZWRyb25HZW9tZXRyeSc6XG5cdFx0XHRcdFx0XHRjYXNlICdUZXRyYWhlZHJvbkdlb21ldHJ5JzpcblxuXHRcdFx0XHRcdFx0XHRnZW9tZXRyeSA9IG5ldyBHZW9tZXRyaWVzWyBkYXRhLnR5cGUgXShcblx0XHRcdFx0XHRcdFx0XHRkYXRhLnJhZGl1cyxcblx0XHRcdFx0XHRcdFx0XHRkYXRhLmRldGFpbFxuXHRcdFx0XHRcdFx0XHQpO1xuXG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRjYXNlICdSaW5nR2VvbWV0cnknOlxuXHRcdFx0XHRcdFx0Y2FzZSAnUmluZ0J1ZmZlckdlb21ldHJ5JzpcblxuXHRcdFx0XHRcdFx0XHRnZW9tZXRyeSA9IG5ldyBHZW9tZXRyaWVzWyBkYXRhLnR5cGUgXShcblx0XHRcdFx0XHRcdFx0XHRkYXRhLmlubmVyUmFkaXVzLFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEub3V0ZXJSYWRpdXMsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS50aGV0YVNlZ21lbnRzLFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEucGhpU2VnbWVudHMsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS50aGV0YVN0YXJ0LFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEudGhldGFMZW5ndGhcblx0XHRcdFx0XHRcdFx0KTtcblxuXHRcdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdFx0Y2FzZSAnVG9ydXNHZW9tZXRyeSc6XG5cdFx0XHRcdFx0XHRjYXNlICdUb3J1c0J1ZmZlckdlb21ldHJ5JzpcblxuXHRcdFx0XHRcdFx0XHRnZW9tZXRyeSA9IG5ldyBHZW9tZXRyaWVzWyBkYXRhLnR5cGUgXShcblx0XHRcdFx0XHRcdFx0XHRkYXRhLnJhZGl1cyxcblx0XHRcdFx0XHRcdFx0XHRkYXRhLnR1YmUsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS5yYWRpYWxTZWdtZW50cyxcblx0XHRcdFx0XHRcdFx0XHRkYXRhLnR1YnVsYXJTZWdtZW50cyxcblx0XHRcdFx0XHRcdFx0XHRkYXRhLmFyY1xuXHRcdFx0XHRcdFx0XHQpO1xuXG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRjYXNlICdUb3J1c0tub3RHZW9tZXRyeSc6XG5cdFx0XHRcdFx0XHRjYXNlICdUb3J1c0tub3RCdWZmZXJHZW9tZXRyeSc6XG5cblx0XHRcdFx0XHRcdFx0Z2VvbWV0cnkgPSBuZXcgR2VvbWV0cmllc1sgZGF0YS50eXBlIF0oXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS5yYWRpdXMsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS50dWJlLFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEudHVidWxhclNlZ21lbnRzLFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEucmFkaWFsU2VnbWVudHMsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS5wLFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEucVxuXHRcdFx0XHRcdFx0XHQpO1xuXG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRjYXNlICdMYXRoZUdlb21ldHJ5Jzpcblx0XHRcdFx0XHRcdGNhc2UgJ0xhdGhlQnVmZmVyR2VvbWV0cnknOlxuXG5cdFx0XHRcdFx0XHRcdGdlb21ldHJ5ID0gbmV3IEdlb21ldHJpZXNbIGRhdGEudHlwZSBdKFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEucG9pbnRzLFxuXHRcdFx0XHRcdFx0XHRcdGRhdGEuc2VnbWVudHMsXG5cdFx0XHRcdFx0XHRcdFx0ZGF0YS5waGlTdGFydCxcblx0XHRcdFx0XHRcdFx0XHRkYXRhLnBoaUxlbmd0aFxuXHRcdFx0XHRcdFx0XHQpO1xuXG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRjYXNlICdCdWZmZXJHZW9tZXRyeSc6XG5cblx0XHRcdFx0XHRcdFx0Z2VvbWV0cnkgPSBidWZmZXJHZW9tZXRyeUxvYWRlci5wYXJzZSggZGF0YSApO1xuXG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRjYXNlICdHZW9tZXRyeSc6XG5cblx0XHRcdFx0XHRcdFx0Z2VvbWV0cnkgPSBnZW9tZXRyeUxvYWRlci5wYXJzZSggZGF0YS5kYXRhLCB0aGlzLnRleHR1cmVQYXRoICkuZ2VvbWV0cnk7XG5cblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRcdGRlZmF1bHQ6XG5cblx0XHRcdFx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuT2JqZWN0TG9hZGVyOiBVbnN1cHBvcnRlZCBnZW9tZXRyeSB0eXBlIFwiJyArIGRhdGEudHlwZSArICdcIicgKTtcblxuXHRcdFx0XHRcdFx0XHRjb250aW51ZTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGdlb21ldHJ5LnV1aWQgPSBkYXRhLnV1aWQ7XG5cblx0XHRcdFx0XHRpZiAoIGRhdGEubmFtZSAhPT0gdW5kZWZpbmVkICkgZ2VvbWV0cnkubmFtZSA9IGRhdGEubmFtZTtcblxuXHRcdFx0XHRcdGdlb21ldHJpZXNbIGRhdGEudXVpZCBdID0gZ2VvbWV0cnk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBnZW9tZXRyaWVzO1xuXG5cdFx0fSxcblxuXHRcdHBhcnNlTWF0ZXJpYWxzOiBmdW5jdGlvbiAoIGpzb24sIHRleHR1cmVzICkge1xuXG5cdFx0XHR2YXIgbWF0ZXJpYWxzID0ge307XG5cblx0XHRcdGlmICgganNvbiAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdHZhciBsb2FkZXIgPSBuZXcgTWF0ZXJpYWxMb2FkZXIoKTtcblx0XHRcdFx0bG9hZGVyLnNldFRleHR1cmVzKCB0ZXh0dXJlcyApO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGpzb24ubGVuZ3RoOyBpIDwgbDsgaSArKyApIHtcblxuXHRcdFx0XHRcdHZhciBtYXRlcmlhbCA9IGxvYWRlci5wYXJzZSgganNvblsgaSBdICk7XG5cdFx0XHRcdFx0bWF0ZXJpYWxzWyBtYXRlcmlhbC51dWlkIF0gPSBtYXRlcmlhbDtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIG1hdGVyaWFscztcblxuXHRcdH0sXG5cblx0XHRwYXJzZUFuaW1hdGlvbnM6IGZ1bmN0aW9uICgganNvbiApIHtcblxuXHRcdFx0dmFyIGFuaW1hdGlvbnMgPSBbXTtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpIDwganNvbi5sZW5ndGg7IGkgKysgKSB7XG5cblx0XHRcdFx0dmFyIGNsaXAgPSBBbmltYXRpb25DbGlwLnBhcnNlKCBqc29uWyBpIF0gKTtcblxuXHRcdFx0XHRhbmltYXRpb25zLnB1c2goIGNsaXAgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gYW5pbWF0aW9ucztcblxuXHRcdH0sXG5cblx0XHRwYXJzZUltYWdlczogZnVuY3Rpb24gKCBqc29uLCBvbkxvYWQgKSB7XG5cblx0XHRcdHZhciBzY29wZSA9IHRoaXM7XG5cdFx0XHR2YXIgaW1hZ2VzID0ge307XG5cblx0XHRcdGZ1bmN0aW9uIGxvYWRJbWFnZSggdXJsICkge1xuXG5cdFx0XHRcdHNjb3BlLm1hbmFnZXIuaXRlbVN0YXJ0KCB1cmwgKTtcblxuXHRcdFx0XHRyZXR1cm4gbG9hZGVyLmxvYWQoIHVybCwgZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdFx0c2NvcGUubWFuYWdlci5pdGVtRW5kKCB1cmwgKTtcblxuXHRcdFx0XHR9LCB1bmRlZmluZWQsIGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0XHRcdHNjb3BlLm1hbmFnZXIuaXRlbUVycm9yKCB1cmwgKTtcblxuXHRcdFx0XHR9ICk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBqc29uICE9PSB1bmRlZmluZWQgJiYganNvbi5sZW5ndGggPiAwICkge1xuXG5cdFx0XHRcdHZhciBtYW5hZ2VyID0gbmV3IExvYWRpbmdNYW5hZ2VyKCBvbkxvYWQgKTtcblxuXHRcdFx0XHR2YXIgbG9hZGVyID0gbmV3IEltYWdlTG9hZGVyKCBtYW5hZ2VyICk7XG5cdFx0XHRcdGxvYWRlci5zZXRDcm9zc09yaWdpbiggdGhpcy5jcm9zc09yaWdpbiApO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGpzb24ubGVuZ3RoOyBpIDwgbDsgaSArKyApIHtcblxuXHRcdFx0XHRcdHZhciBpbWFnZSA9IGpzb25bIGkgXTtcblx0XHRcdFx0XHR2YXIgcGF0aCA9IC9eKFxcL1xcLyl8KFthLXpdKzooXFwvXFwvKT8pL2kudGVzdCggaW1hZ2UudXJsICkgPyBpbWFnZS51cmwgOiBzY29wZS50ZXh0dXJlUGF0aCArIGltYWdlLnVybDtcblxuXHRcdFx0XHRcdGltYWdlc1sgaW1hZ2UudXVpZCBdID0gbG9hZEltYWdlKCBwYXRoICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBpbWFnZXM7XG5cblx0XHR9LFxuXG5cdFx0cGFyc2VUZXh0dXJlczogZnVuY3Rpb24gKCBqc29uLCBpbWFnZXMgKSB7XG5cblx0XHRcdGZ1bmN0aW9uIHBhcnNlQ29uc3RhbnQoIHZhbHVlLCB0eXBlICkge1xuXG5cdFx0XHRcdGlmICggdHlwZW9mKCB2YWx1ZSApID09PSAnbnVtYmVyJyApIHJldHVybiB2YWx1ZTtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5PYmplY3RMb2FkZXIucGFyc2VUZXh0dXJlOiBDb25zdGFudCBzaG91bGQgYmUgaW4gbnVtZXJpYyBmb3JtLicsIHZhbHVlICk7XG5cblx0XHRcdFx0cmV0dXJuIHR5cGVbIHZhbHVlIF07XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIHRleHR1cmVzID0ge307XG5cblx0XHRcdGlmICgganNvbiAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGpzb24ubGVuZ3RoOyBpIDwgbDsgaSArKyApIHtcblxuXHRcdFx0XHRcdHZhciBkYXRhID0ganNvblsgaSBdO1xuXG5cdFx0XHRcdFx0aWYgKCBkYXRhLmltYWdlID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLk9iamVjdExvYWRlcjogTm8gXCJpbWFnZVwiIHNwZWNpZmllZCBmb3InLCBkYXRhLnV1aWQgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGlmICggaW1hZ2VzWyBkYXRhLmltYWdlIF0gPT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuT2JqZWN0TG9hZGVyOiBVbmRlZmluZWQgaW1hZ2UnLCBkYXRhLmltYWdlICk7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR2YXIgdGV4dHVyZSA9IG5ldyBUZXh0dXJlKCBpbWFnZXNbIGRhdGEuaW1hZ2UgXSApO1xuXHRcdFx0XHRcdHRleHR1cmUubmVlZHNVcGRhdGUgPSB0cnVlO1xuXG5cdFx0XHRcdFx0dGV4dHVyZS51dWlkID0gZGF0YS51dWlkO1xuXG5cdFx0XHRcdFx0aWYgKCBkYXRhLm5hbWUgIT09IHVuZGVmaW5lZCApIHRleHR1cmUubmFtZSA9IGRhdGEubmFtZTtcblxuXHRcdFx0XHRcdGlmICggZGF0YS5tYXBwaW5nICE9PSB1bmRlZmluZWQgKSB0ZXh0dXJlLm1hcHBpbmcgPSBwYXJzZUNvbnN0YW50KCBkYXRhLm1hcHBpbmcsIFRleHR1cmVNYXBwaW5nICk7XG5cblx0XHRcdFx0XHRpZiAoIGRhdGEub2Zmc2V0ICE9PSB1bmRlZmluZWQgKSB0ZXh0dXJlLm9mZnNldC5mcm9tQXJyYXkoIGRhdGEub2Zmc2V0ICk7XG5cdFx0XHRcdFx0aWYgKCBkYXRhLnJlcGVhdCAhPT0gdW5kZWZpbmVkICkgdGV4dHVyZS5yZXBlYXQuZnJvbUFycmF5KCBkYXRhLnJlcGVhdCApO1xuXHRcdFx0XHRcdGlmICggZGF0YS53cmFwICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRcdHRleHR1cmUud3JhcFMgPSBwYXJzZUNvbnN0YW50KCBkYXRhLndyYXBbIDAgXSwgVGV4dHVyZVdyYXBwaW5nICk7XG5cdFx0XHRcdFx0XHR0ZXh0dXJlLndyYXBUID0gcGFyc2VDb25zdGFudCggZGF0YS53cmFwWyAxIF0sIFRleHR1cmVXcmFwcGluZyApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0aWYgKCBkYXRhLm1pbkZpbHRlciAhPT0gdW5kZWZpbmVkICkgdGV4dHVyZS5taW5GaWx0ZXIgPSBwYXJzZUNvbnN0YW50KCBkYXRhLm1pbkZpbHRlciwgVGV4dHVyZUZpbHRlciApO1xuXHRcdFx0XHRcdGlmICggZGF0YS5tYWdGaWx0ZXIgIT09IHVuZGVmaW5lZCApIHRleHR1cmUubWFnRmlsdGVyID0gcGFyc2VDb25zdGFudCggZGF0YS5tYWdGaWx0ZXIsIFRleHR1cmVGaWx0ZXIgKTtcblx0XHRcdFx0XHRpZiAoIGRhdGEuYW5pc290cm9weSAhPT0gdW5kZWZpbmVkICkgdGV4dHVyZS5hbmlzb3Ryb3B5ID0gZGF0YS5hbmlzb3Ryb3B5O1xuXG5cdFx0XHRcdFx0aWYgKCBkYXRhLmZsaXBZICE9PSB1bmRlZmluZWQgKSB0ZXh0dXJlLmZsaXBZID0gZGF0YS5mbGlwWTtcblxuXHRcdFx0XHRcdHRleHR1cmVzWyBkYXRhLnV1aWQgXSA9IHRleHR1cmU7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0ZXh0dXJlcztcblxuXHRcdH0sXG5cblx0XHRwYXJzZU9iamVjdDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgbWF0cml4ID0gbmV3IE1hdHJpeDQoKTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIHBhcnNlT2JqZWN0KCBkYXRhLCBnZW9tZXRyaWVzLCBtYXRlcmlhbHMgKSB7XG5cblx0XHRcdFx0dmFyIG9iamVjdDtcblxuXHRcdFx0XHRmdW5jdGlvbiBnZXRHZW9tZXRyeSggbmFtZSApIHtcblxuXHRcdFx0XHRcdGlmICggZ2VvbWV0cmllc1sgbmFtZSBdID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLk9iamVjdExvYWRlcjogVW5kZWZpbmVkIGdlb21ldHJ5JywgbmFtZSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0cmV0dXJuIGdlb21ldHJpZXNbIG5hbWUgXTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0ZnVuY3Rpb24gZ2V0TWF0ZXJpYWwoIG5hbWUgKSB7XG5cblx0XHRcdFx0XHRpZiAoIG5hbWUgPT09IHVuZGVmaW5lZCApIHJldHVybiB1bmRlZmluZWQ7XG5cblx0XHRcdFx0XHRpZiAoIG1hdGVyaWFsc1sgbmFtZSBdID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLk9iamVjdExvYWRlcjogVW5kZWZpbmVkIG1hdGVyaWFsJywgbmFtZSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0cmV0dXJuIG1hdGVyaWFsc1sgbmFtZSBdO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRzd2l0Y2ggKCBkYXRhLnR5cGUgKSB7XG5cblx0XHRcdFx0XHRjYXNlICdTY2VuZSc6XG5cblx0XHRcdFx0XHRcdG9iamVjdCA9IG5ldyBTY2VuZSgpO1xuXG5cdFx0XHRcdFx0XHRpZiAoIGRhdGEuYmFja2dyb3VuZCAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0XHRcdGlmICggTnVtYmVyLmlzSW50ZWdlciggZGF0YS5iYWNrZ3JvdW5kICkgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHRvYmplY3QuYmFja2dyb3VuZCA9IG5ldyBDb2xvciggZGF0YS5iYWNrZ3JvdW5kICk7XG5cblx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdGlmICggZGF0YS5mb2cgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdFx0XHRpZiAoIGRhdGEuZm9nLnR5cGUgPT09ICdGb2cnICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0b2JqZWN0LmZvZyA9IG5ldyBGb2coIGRhdGEuZm9nLmNvbG9yLCBkYXRhLmZvZy5uZWFyLCBkYXRhLmZvZy5mYXIgKTtcblxuXHRcdFx0XHRcdFx0XHR9IGVsc2UgaWYgKCBkYXRhLmZvZy50eXBlID09PSAnRm9nRXhwMicgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHRvYmplY3QuZm9nID0gbmV3IEZvZ0V4cDIoIGRhdGEuZm9nLmNvbG9yLCBkYXRhLmZvZy5kZW5zaXR5ICk7XG5cblx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0Y2FzZSAnUGVyc3BlY3RpdmVDYW1lcmEnOlxuXG5cdFx0XHRcdFx0XHRvYmplY3QgPSBuZXcgUGVyc3BlY3RpdmVDYW1lcmEoIGRhdGEuZm92LCBkYXRhLmFzcGVjdCwgZGF0YS5uZWFyLCBkYXRhLmZhciApO1xuXG5cdFx0XHRcdFx0XHRpZiAoIGRhdGEuZm9jdXMgIT09IHVuZGVmaW5lZCApIG9iamVjdC5mb2N1cyA9IGRhdGEuZm9jdXM7XG5cdFx0XHRcdFx0XHRpZiAoIGRhdGEuem9vbSAhPT0gdW5kZWZpbmVkICkgb2JqZWN0Lnpvb20gPSBkYXRhLnpvb207XG5cdFx0XHRcdFx0XHRpZiAoIGRhdGEuZmlsbUdhdWdlICE9PSB1bmRlZmluZWQgKSBvYmplY3QuZmlsbUdhdWdlID0gZGF0YS5maWxtR2F1Z2U7XG5cdFx0XHRcdFx0XHRpZiAoIGRhdGEuZmlsbU9mZnNldCAhPT0gdW5kZWZpbmVkICkgb2JqZWN0LmZpbG1PZmZzZXQgPSBkYXRhLmZpbG1PZmZzZXQ7XG5cdFx0XHRcdFx0XHRpZiAoIGRhdGEudmlldyAhPT0gdW5kZWZpbmVkICkgb2JqZWN0LnZpZXcgPSBPYmplY3QuYXNzaWduKCB7fSwgZGF0YS52aWV3ICk7XG5cblx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0Y2FzZSAnT3J0aG9ncmFwaGljQ2FtZXJhJzpcblxuXHRcdFx0XHRcdFx0b2JqZWN0ID0gbmV3IE9ydGhvZ3JhcGhpY0NhbWVyYSggZGF0YS5sZWZ0LCBkYXRhLnJpZ2h0LCBkYXRhLnRvcCwgZGF0YS5ib3R0b20sIGRhdGEubmVhciwgZGF0YS5mYXIgKTtcblxuXHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRjYXNlICdBbWJpZW50TGlnaHQnOlxuXG5cdFx0XHRcdFx0XHRvYmplY3QgPSBuZXcgQW1iaWVudExpZ2h0KCBkYXRhLmNvbG9yLCBkYXRhLmludGVuc2l0eSApO1xuXG5cdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdGNhc2UgJ0RpcmVjdGlvbmFsTGlnaHQnOlxuXG5cdFx0XHRcdFx0XHRvYmplY3QgPSBuZXcgRGlyZWN0aW9uYWxMaWdodCggZGF0YS5jb2xvciwgZGF0YS5pbnRlbnNpdHkgKTtcblxuXHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRjYXNlICdQb2ludExpZ2h0JzpcblxuXHRcdFx0XHRcdFx0b2JqZWN0ID0gbmV3IFBvaW50TGlnaHQoIGRhdGEuY29sb3IsIGRhdGEuaW50ZW5zaXR5LCBkYXRhLmRpc3RhbmNlLCBkYXRhLmRlY2F5ICk7XG5cblx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0Y2FzZSAnU3BvdExpZ2h0JzpcblxuXHRcdFx0XHRcdFx0b2JqZWN0ID0gbmV3IFNwb3RMaWdodCggZGF0YS5jb2xvciwgZGF0YS5pbnRlbnNpdHksIGRhdGEuZGlzdGFuY2UsIGRhdGEuYW5nbGUsIGRhdGEucGVudW1icmEsIGRhdGEuZGVjYXkgKTtcblxuXHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRjYXNlICdIZW1pc3BoZXJlTGlnaHQnOlxuXG5cdFx0XHRcdFx0XHRvYmplY3QgPSBuZXcgSGVtaXNwaGVyZUxpZ2h0KCBkYXRhLmNvbG9yLCBkYXRhLmdyb3VuZENvbG9yLCBkYXRhLmludGVuc2l0eSApO1xuXG5cdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdGNhc2UgJ01lc2gnOlxuXG5cdFx0XHRcdFx0XHR2YXIgZ2VvbWV0cnkgPSBnZXRHZW9tZXRyeSggZGF0YS5nZW9tZXRyeSApO1xuXHRcdFx0XHRcdFx0dmFyIG1hdGVyaWFsID0gZ2V0TWF0ZXJpYWwoIGRhdGEubWF0ZXJpYWwgKTtcblxuXHRcdFx0XHRcdFx0aWYgKCBnZW9tZXRyeS5ib25lcyAmJiBnZW9tZXRyeS5ib25lcy5sZW5ndGggPiAwICkge1xuXG5cdFx0XHRcdFx0XHRcdG9iamVjdCA9IG5ldyBTa2lubmVkTWVzaCggZ2VvbWV0cnksIG1hdGVyaWFsICk7XG5cblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdFx0b2JqZWN0ID0gbmV3IE1lc2goIGdlb21ldHJ5LCBtYXRlcmlhbCApO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0Y2FzZSAnTE9EJzpcblxuXHRcdFx0XHRcdFx0b2JqZWN0ID0gbmV3IExPRCgpO1xuXG5cdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdGNhc2UgJ0xpbmUnOlxuXG5cdFx0XHRcdFx0XHRvYmplY3QgPSBuZXcgTGluZSggZ2V0R2VvbWV0cnkoIGRhdGEuZ2VvbWV0cnkgKSwgZ2V0TWF0ZXJpYWwoIGRhdGEubWF0ZXJpYWwgKSwgZGF0YS5tb2RlICk7XG5cblx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0Y2FzZSAnTGluZVNlZ21lbnRzJzpcblxuXHRcdFx0XHRcdFx0b2JqZWN0ID0gbmV3IExpbmVTZWdtZW50cyggZ2V0R2VvbWV0cnkoIGRhdGEuZ2VvbWV0cnkgKSwgZ2V0TWF0ZXJpYWwoIGRhdGEubWF0ZXJpYWwgKSApO1xuXG5cdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdGNhc2UgJ1BvaW50Q2xvdWQnOlxuXHRcdFx0XHRcdGNhc2UgJ1BvaW50cyc6XG5cblx0XHRcdFx0XHRcdG9iamVjdCA9IG5ldyBQb2ludHMoIGdldEdlb21ldHJ5KCBkYXRhLmdlb21ldHJ5ICksIGdldE1hdGVyaWFsKCBkYXRhLm1hdGVyaWFsICkgKTtcblxuXHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRjYXNlICdTcHJpdGUnOlxuXG5cdFx0XHRcdFx0XHRvYmplY3QgPSBuZXcgU3ByaXRlKCBnZXRNYXRlcmlhbCggZGF0YS5tYXRlcmlhbCApICk7XG5cblx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0Y2FzZSAnR3JvdXAnOlxuXG5cdFx0XHRcdFx0XHRvYmplY3QgPSBuZXcgR3JvdXAoKTtcblxuXHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRjYXNlICdTa2lubmVkTWVzaCc6XG5cblx0XHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLk9iamVjdExvYWRlci5wYXJzZU9iamVjdCgpIGRvZXMgbm90IHN1cHBvcnQgU2tpbm5lZE1lc2ggdHlwZS4gSW5zdGFudGlhdGVzIE9iamVjdDNEIGluc3RlYWQuJyApO1xuXG5cdFx0XHRcdFx0ZGVmYXVsdDpcblxuXHRcdFx0XHRcdFx0b2JqZWN0ID0gbmV3IE9iamVjdDNEKCk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdG9iamVjdC51dWlkID0gZGF0YS51dWlkO1xuXG5cdFx0XHRcdGlmICggZGF0YS5uYW1lICE9PSB1bmRlZmluZWQgKSBvYmplY3QubmFtZSA9IGRhdGEubmFtZTtcblx0XHRcdFx0aWYgKCBkYXRhLm1hdHJpeCAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0bWF0cml4LmZyb21BcnJheSggZGF0YS5tYXRyaXggKTtcblx0XHRcdFx0XHRtYXRyaXguZGVjb21wb3NlKCBvYmplY3QucG9zaXRpb24sIG9iamVjdC5xdWF0ZXJuaW9uLCBvYmplY3Quc2NhbGUgKTtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0aWYgKCBkYXRhLnBvc2l0aW9uICE9PSB1bmRlZmluZWQgKSBvYmplY3QucG9zaXRpb24uZnJvbUFycmF5KCBkYXRhLnBvc2l0aW9uICk7XG5cdFx0XHRcdFx0aWYgKCBkYXRhLnJvdGF0aW9uICE9PSB1bmRlZmluZWQgKSBvYmplY3Qucm90YXRpb24uZnJvbUFycmF5KCBkYXRhLnJvdGF0aW9uICk7XG5cdFx0XHRcdFx0aWYgKCBkYXRhLnF1YXRlcm5pb24gIT09IHVuZGVmaW5lZCApIG9iamVjdC5xdWF0ZXJuaW9uLmZyb21BcnJheSggZGF0YS5xdWF0ZXJuaW9uICk7XG5cdFx0XHRcdFx0aWYgKCBkYXRhLnNjYWxlICE9PSB1bmRlZmluZWQgKSBvYmplY3Quc2NhbGUuZnJvbUFycmF5KCBkYXRhLnNjYWxlICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggZGF0YS5jYXN0U2hhZG93ICE9PSB1bmRlZmluZWQgKSBvYmplY3QuY2FzdFNoYWRvdyA9IGRhdGEuY2FzdFNoYWRvdztcblx0XHRcdFx0aWYgKCBkYXRhLnJlY2VpdmVTaGFkb3cgIT09IHVuZGVmaW5lZCApIG9iamVjdC5yZWNlaXZlU2hhZG93ID0gZGF0YS5yZWNlaXZlU2hhZG93O1xuXG5cdFx0XHRcdGlmICggZGF0YS5zaGFkb3cgKSB7XG5cblx0XHRcdFx0XHRpZiAoIGRhdGEuc2hhZG93LmJpYXMgIT09IHVuZGVmaW5lZCApIG9iamVjdC5zaGFkb3cuYmlhcyA9IGRhdGEuc2hhZG93LmJpYXM7XG5cdFx0XHRcdFx0aWYgKCBkYXRhLnNoYWRvdy5yYWRpdXMgIT09IHVuZGVmaW5lZCApIG9iamVjdC5zaGFkb3cucmFkaXVzID0gZGF0YS5zaGFkb3cucmFkaXVzO1xuXHRcdFx0XHRcdGlmICggZGF0YS5zaGFkb3cubWFwU2l6ZSAhPT0gdW5kZWZpbmVkICkgb2JqZWN0LnNoYWRvdy5tYXBTaXplLmZyb21BcnJheSggZGF0YS5zaGFkb3cubWFwU2l6ZSApO1xuXHRcdFx0XHRcdGlmICggZGF0YS5zaGFkb3cuY2FtZXJhICE9PSB1bmRlZmluZWQgKSBvYmplY3Quc2hhZG93LmNhbWVyYSA9IHRoaXMucGFyc2VPYmplY3QoIGRhdGEuc2hhZG93LmNhbWVyYSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoIGRhdGEudmlzaWJsZSAhPT0gdW5kZWZpbmVkICkgb2JqZWN0LnZpc2libGUgPSBkYXRhLnZpc2libGU7XG5cdFx0XHRcdGlmICggZGF0YS51c2VyRGF0YSAhPT0gdW5kZWZpbmVkICkgb2JqZWN0LnVzZXJEYXRhID0gZGF0YS51c2VyRGF0YTtcblxuXHRcdFx0XHRpZiAoIGRhdGEuY2hpbGRyZW4gIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdGZvciAoIHZhciBjaGlsZCBpbiBkYXRhLmNoaWxkcmVuICkge1xuXG5cdFx0XHRcdFx0XHRvYmplY3QuYWRkKCB0aGlzLnBhcnNlT2JqZWN0KCBkYXRhLmNoaWxkcmVuWyBjaGlsZCBdLCBnZW9tZXRyaWVzLCBtYXRlcmlhbHMgKSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoIGRhdGEudHlwZSA9PT0gJ0xPRCcgKSB7XG5cblx0XHRcdFx0XHR2YXIgbGV2ZWxzID0gZGF0YS5sZXZlbHM7XG5cblx0XHRcdFx0XHRmb3IgKCB2YXIgbCA9IDA7IGwgPCBsZXZlbHMubGVuZ3RoOyBsICsrICkge1xuXG5cdFx0XHRcdFx0XHR2YXIgbGV2ZWwgPSBsZXZlbHNbIGwgXTtcblx0XHRcdFx0XHRcdHZhciBjaGlsZCA9IG9iamVjdC5nZXRPYmplY3RCeVByb3BlcnR5KCAndXVpZCcsIGxldmVsLm9iamVjdCApO1xuXG5cdFx0XHRcdFx0XHRpZiAoIGNoaWxkICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRcdFx0b2JqZWN0LmFkZExldmVsKCBjaGlsZCwgbGV2ZWwuZGlzdGFuY2UgKTtcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRyZXR1cm4gb2JqZWN0O1xuXG5cdFx0XHR9O1xuXG5cdFx0fSgpXG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIHp6ODUgLyBodHRwOi8vd3d3LmxhYjRnYW1lcy5uZXQveno4NS9ibG9nXG5cdCAqIEV4dGVuc2libGUgY3VydmUgb2JqZWN0XG5cdCAqXG5cdCAqIFNvbWUgY29tbW9uIG9mIEN1cnZlIG1ldGhvZHNcblx0ICogLmdldFBvaW50KHQpLCBnZXRUYW5nZW50KHQpXG5cdCAqIC5nZXRQb2ludEF0KHUpLCBnZXRUYW5nZW50QXQodSlcblx0ICogLmdldFBvaW50cygpLCAuZ2V0U3BhY2VkUG9pbnRzKClcblx0ICogLmdldExlbmd0aCgpXG5cdCAqIC51cGRhdGVBcmNMZW5ndGhzKClcblx0ICpcblx0ICogVGhpcyBmb2xsb3dpbmcgY2xhc3NlcyBzdWJjbGFzc2VzIFRIUkVFLkN1cnZlOlxuXHQgKlxuXHQgKiAtLSAyZCBjbGFzc2VzIC0tXG5cdCAqIFRIUkVFLkxpbmVDdXJ2ZVxuXHQgKiBUSFJFRS5RdWFkcmF0aWNCZXppZXJDdXJ2ZVxuXHQgKiBUSFJFRS5DdWJpY0JlemllckN1cnZlXG5cdCAqIFRIUkVFLlNwbGluZUN1cnZlXG5cdCAqIFRIUkVFLkFyY0N1cnZlXG5cdCAqIFRIUkVFLkVsbGlwc2VDdXJ2ZVxuXHQgKlxuXHQgKiAtLSAzZCBjbGFzc2VzIC0tXG5cdCAqIFRIUkVFLkxpbmVDdXJ2ZTNcblx0ICogVEhSRUUuUXVhZHJhdGljQmV6aWVyQ3VydmUzXG5cdCAqIFRIUkVFLkN1YmljQmV6aWVyQ3VydmUzXG5cdCAqIFRIUkVFLlNwbGluZUN1cnZlM1xuXHQgKlxuXHQgKiBBIHNlcmllcyBvZiBjdXJ2ZXMgY2FuIGJlIHJlcHJlc2VudGVkIGFzIGEgVEhSRUUuQ3VydmVQYXRoXG5cdCAqXG5cdCAqKi9cblxuXHQvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblx0ICpcdEFic3RyYWN0IEN1cnZlIGJhc2UgY2xhc3Ncblx0ICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5cdGZ1bmN0aW9uIEN1cnZlKCkge31cblxuXHRDdXJ2ZS5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogQ3VydmUsXG5cblx0XHQvLyBWaXJ0dWFsIGJhc2UgY2xhc3MgbWV0aG9kIHRvIG92ZXJ3cml0ZSBhbmQgaW1wbGVtZW50IGluIHN1YmNsYXNzZXNcblx0XHQvL1x0LSB0IFswIC4uIDFdXG5cblx0XHRnZXRQb2ludDogZnVuY3Rpb24gKCB0ICkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oIFwiVEhSRUUuQ3VydmU6IFdhcm5pbmcsIGdldFBvaW50KCkgbm90IGltcGxlbWVudGVkIVwiICk7XG5cdFx0XHRyZXR1cm4gbnVsbDtcblxuXHRcdH0sXG5cblx0XHQvLyBHZXQgcG9pbnQgYXQgcmVsYXRpdmUgcG9zaXRpb24gaW4gY3VydmUgYWNjb3JkaW5nIHRvIGFyYyBsZW5ndGhcblx0XHQvLyAtIHUgWzAgLi4gMV1cblxuXHRcdGdldFBvaW50QXQ6IGZ1bmN0aW9uICggdSApIHtcblxuXHRcdFx0dmFyIHQgPSB0aGlzLmdldFV0b1RtYXBwaW5nKCB1ICk7XG5cdFx0XHRyZXR1cm4gdGhpcy5nZXRQb2ludCggdCApO1xuXG5cdFx0fSxcblxuXHRcdC8vIEdldCBzZXF1ZW5jZSBvZiBwb2ludHMgdXNpbmcgZ2V0UG9pbnQoIHQgKVxuXG5cdFx0Z2V0UG9pbnRzOiBmdW5jdGlvbiAoIGRpdmlzaW9ucyApIHtcblxuXHRcdFx0aWYgKCAhIGRpdmlzaW9ucyApIGRpdmlzaW9ucyA9IDU7XG5cblx0XHRcdHZhciBwb2ludHMgPSBbXTtcblxuXHRcdFx0Zm9yICggdmFyIGQgPSAwOyBkIDw9IGRpdmlzaW9uczsgZCArKyApIHtcblxuXHRcdFx0XHRwb2ludHMucHVzaCggdGhpcy5nZXRQb2ludCggZCAvIGRpdmlzaW9ucyApICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHBvaW50cztcblxuXHRcdH0sXG5cblx0XHQvLyBHZXQgc2VxdWVuY2Ugb2YgcG9pbnRzIHVzaW5nIGdldFBvaW50QXQoIHUgKVxuXG5cdFx0Z2V0U3BhY2VkUG9pbnRzOiBmdW5jdGlvbiAoIGRpdmlzaW9ucyApIHtcblxuXHRcdFx0aWYgKCAhIGRpdmlzaW9ucyApIGRpdmlzaW9ucyA9IDU7XG5cblx0XHRcdHZhciBwb2ludHMgPSBbXTtcblxuXHRcdFx0Zm9yICggdmFyIGQgPSAwOyBkIDw9IGRpdmlzaW9uczsgZCArKyApIHtcblxuXHRcdFx0XHRwb2ludHMucHVzaCggdGhpcy5nZXRQb2ludEF0KCBkIC8gZGl2aXNpb25zICkgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gcG9pbnRzO1xuXG5cdFx0fSxcblxuXHRcdC8vIEdldCB0b3RhbCBjdXJ2ZSBhcmMgbGVuZ3RoXG5cblx0XHRnZXRMZW5ndGg6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIGxlbmd0aHMgPSB0aGlzLmdldExlbmd0aHMoKTtcblx0XHRcdHJldHVybiBsZW5ndGhzWyBsZW5ndGhzLmxlbmd0aCAtIDEgXTtcblxuXHRcdH0sXG5cblx0XHQvLyBHZXQgbGlzdCBvZiBjdW11bGF0aXZlIHNlZ21lbnQgbGVuZ3Roc1xuXG5cdFx0Z2V0TGVuZ3RoczogZnVuY3Rpb24gKCBkaXZpc2lvbnMgKSB7XG5cblx0XHRcdGlmICggISBkaXZpc2lvbnMgKSBkaXZpc2lvbnMgPSAoIHRoaXMuX19hcmNMZW5ndGhEaXZpc2lvbnMgKSA/ICggdGhpcy5fX2FyY0xlbmd0aERpdmlzaW9ucyApIDogMjAwO1xuXG5cdFx0XHRpZiAoIHRoaXMuY2FjaGVBcmNMZW5ndGhzXG5cdFx0XHRcdCYmICggdGhpcy5jYWNoZUFyY0xlbmd0aHMubGVuZ3RoID09PSBkaXZpc2lvbnMgKyAxIClcblx0XHRcdFx0JiYgISB0aGlzLm5lZWRzVXBkYXRlICkge1xuXG5cdFx0XHRcdC8vY29uc29sZS5sb2coIFwiY2FjaGVkXCIsIHRoaXMuY2FjaGVBcmNMZW5ndGhzICk7XG5cdFx0XHRcdHJldHVybiB0aGlzLmNhY2hlQXJjTGVuZ3RocztcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLm5lZWRzVXBkYXRlID0gZmFsc2U7XG5cblx0XHRcdHZhciBjYWNoZSA9IFtdO1xuXHRcdFx0dmFyIGN1cnJlbnQsIGxhc3QgPSB0aGlzLmdldFBvaW50KCAwICk7XG5cdFx0XHR2YXIgcCwgc3VtID0gMDtcblxuXHRcdFx0Y2FjaGUucHVzaCggMCApO1xuXG5cdFx0XHRmb3IgKCBwID0gMTsgcCA8PSBkaXZpc2lvbnM7IHAgKysgKSB7XG5cblx0XHRcdFx0Y3VycmVudCA9IHRoaXMuZ2V0UG9pbnQgKCBwIC8gZGl2aXNpb25zICk7XG5cdFx0XHRcdHN1bSArPSBjdXJyZW50LmRpc3RhbmNlVG8oIGxhc3QgKTtcblx0XHRcdFx0Y2FjaGUucHVzaCggc3VtICk7XG5cdFx0XHRcdGxhc3QgPSBjdXJyZW50O1xuXG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuY2FjaGVBcmNMZW5ndGhzID0gY2FjaGU7XG5cblx0XHRcdHJldHVybiBjYWNoZTsgLy8geyBzdW1zOiBjYWNoZSwgc3VtOnN1bSB9OyBTdW0gaXMgaW4gdGhlIGxhc3QgZWxlbWVudC5cblxuXHRcdH0sXG5cblx0XHR1cGRhdGVBcmNMZW5ndGhzOiBmdW5jdGlvbigpIHtcblxuXHRcdFx0dGhpcy5uZWVkc1VwZGF0ZSA9IHRydWU7XG5cdFx0XHR0aGlzLmdldExlbmd0aHMoKTtcblxuXHRcdH0sXG5cblx0XHQvLyBHaXZlbiB1ICggMCAuLiAxICksIGdldCBhIHQgdG8gZmluZCBwLiBUaGlzIGdpdmVzIHlvdSBwb2ludHMgd2hpY2ggYXJlIGVxdWlkaXN0YW50XG5cblx0XHRnZXRVdG9UbWFwcGluZzogZnVuY3Rpb24gKCB1LCBkaXN0YW5jZSApIHtcblxuXHRcdFx0dmFyIGFyY0xlbmd0aHMgPSB0aGlzLmdldExlbmd0aHMoKTtcblxuXHRcdFx0dmFyIGkgPSAwLCBpbCA9IGFyY0xlbmd0aHMubGVuZ3RoO1xuXG5cdFx0XHR2YXIgdGFyZ2V0QXJjTGVuZ3RoOyAvLyBUaGUgdGFyZ2V0ZWQgdSBkaXN0YW5jZSB2YWx1ZSB0byBnZXRcblxuXHRcdFx0aWYgKCBkaXN0YW5jZSApIHtcblxuXHRcdFx0XHR0YXJnZXRBcmNMZW5ndGggPSBkaXN0YW5jZTtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHR0YXJnZXRBcmNMZW5ndGggPSB1ICogYXJjTGVuZ3Roc1sgaWwgLSAxIF07XG5cblx0XHRcdH1cblxuXHRcdFx0Ly92YXIgdGltZSA9IERhdGUubm93KCk7XG5cblx0XHRcdC8vIGJpbmFyeSBzZWFyY2ggZm9yIHRoZSBpbmRleCB3aXRoIGxhcmdlc3QgdmFsdWUgc21hbGxlciB0aGFuIHRhcmdldCB1IGRpc3RhbmNlXG5cblx0XHRcdHZhciBsb3cgPSAwLCBoaWdoID0gaWwgLSAxLCBjb21wYXJpc29uO1xuXG5cdFx0XHR3aGlsZSAoIGxvdyA8PSBoaWdoICkge1xuXG5cdFx0XHRcdGkgPSBNYXRoLmZsb29yKCBsb3cgKyAoIGhpZ2ggLSBsb3cgKSAvIDIgKTsgLy8gbGVzcyBsaWtlbHkgdG8gb3ZlcmZsb3csIHRob3VnaCBwcm9iYWJseSBub3QgaXNzdWUgaGVyZSwgSlMgZG9lc24ndCByZWFsbHkgaGF2ZSBpbnRlZ2VycywgYWxsIG51bWJlcnMgYXJlIGZsb2F0c1xuXG5cdFx0XHRcdGNvbXBhcmlzb24gPSBhcmNMZW5ndGhzWyBpIF0gLSB0YXJnZXRBcmNMZW5ndGg7XG5cblx0XHRcdFx0aWYgKCBjb21wYXJpc29uIDwgMCApIHtcblxuXHRcdFx0XHRcdGxvdyA9IGkgKyAxO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAoIGNvbXBhcmlzb24gPiAwICkge1xuXG5cdFx0XHRcdFx0aGlnaCA9IGkgLSAxO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRoaWdoID0gaTtcblx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdC8vIERPTkVcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0aSA9IGhpZ2g7XG5cblx0XHRcdC8vY29uc29sZS5sb2coJ2InICwgaSwgbG93LCBoaWdoLCBEYXRlLm5vdygpLSB0aW1lKTtcblxuXHRcdFx0aWYgKCBhcmNMZW5ndGhzWyBpIF0gPT09IHRhcmdldEFyY0xlbmd0aCApIHtcblxuXHRcdFx0XHR2YXIgdCA9IGkgLyAoIGlsIC0gMSApO1xuXHRcdFx0XHRyZXR1cm4gdDtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyB3ZSBjb3VsZCBnZXQgZmluZXIgZ3JhaW4gYXQgbGVuZ3Rocywgb3IgdXNlIHNpbXBsZSBpbnRlcnBvbGF0aW9uIGJldHdlZW4gdHdvIHBvaW50c1xuXG5cdFx0XHR2YXIgbGVuZ3RoQmVmb3JlID0gYXJjTGVuZ3Roc1sgaSBdO1xuXHRcdFx0dmFyIGxlbmd0aEFmdGVyID0gYXJjTGVuZ3Roc1sgaSArIDEgXTtcblxuXHRcdFx0dmFyIHNlZ21lbnRMZW5ndGggPSBsZW5ndGhBZnRlciAtIGxlbmd0aEJlZm9yZTtcblxuXHRcdFx0Ly8gZGV0ZXJtaW5lIHdoZXJlIHdlIGFyZSBiZXR3ZWVuIHRoZSAnYmVmb3JlJyBhbmQgJ2FmdGVyJyBwb2ludHNcblxuXHRcdFx0dmFyIHNlZ21lbnRGcmFjdGlvbiA9ICggdGFyZ2V0QXJjTGVuZ3RoIC0gbGVuZ3RoQmVmb3JlICkgLyBzZWdtZW50TGVuZ3RoO1xuXG5cdFx0XHQvLyBhZGQgdGhhdCBmcmFjdGlvbmFsIGFtb3VudCB0byB0XG5cblx0XHRcdHZhciB0ID0gKCBpICsgc2VnbWVudEZyYWN0aW9uICkgLyAoIGlsIC0gMSApO1xuXG5cdFx0XHRyZXR1cm4gdDtcblxuXHRcdH0sXG5cblx0XHQvLyBSZXR1cm5zIGEgdW5pdCB2ZWN0b3IgdGFuZ2VudCBhdCB0XG5cdFx0Ly8gSW4gY2FzZSBhbnkgc3ViIGN1cnZlIGRvZXMgbm90IGltcGxlbWVudCBpdHMgdGFuZ2VudCBkZXJpdmF0aW9uLFxuXHRcdC8vIDIgcG9pbnRzIGEgc21hbGwgZGVsdGEgYXBhcnQgd2lsbCBiZSB1c2VkIHRvIGZpbmQgaXRzIGdyYWRpZW50XG5cdFx0Ly8gd2hpY2ggc2VlbXMgdG8gZ2l2ZSBhIHJlYXNvbmFibGUgYXBwcm94aW1hdGlvblxuXG5cdFx0Z2V0VGFuZ2VudDogZnVuY3Rpb24oIHQgKSB7XG5cblx0XHRcdHZhciBkZWx0YSA9IDAuMDAwMTtcblx0XHRcdHZhciB0MSA9IHQgLSBkZWx0YTtcblx0XHRcdHZhciB0MiA9IHQgKyBkZWx0YTtcblxuXHRcdFx0Ly8gQ2FwcGluZyBpbiBjYXNlIG9mIGRhbmdlclxuXG5cdFx0XHRpZiAoIHQxIDwgMCApIHQxID0gMDtcblx0XHRcdGlmICggdDIgPiAxICkgdDIgPSAxO1xuXG5cdFx0XHR2YXIgcHQxID0gdGhpcy5nZXRQb2ludCggdDEgKTtcblx0XHRcdHZhciBwdDIgPSB0aGlzLmdldFBvaW50KCB0MiApO1xuXG5cdFx0XHR2YXIgdmVjID0gcHQyLmNsb25lKCkuc3ViKCBwdDEgKTtcblx0XHRcdHJldHVybiB2ZWMubm9ybWFsaXplKCk7XG5cblx0XHR9LFxuXG5cdFx0Z2V0VGFuZ2VudEF0OiBmdW5jdGlvbiAoIHUgKSB7XG5cblx0XHRcdHZhciB0ID0gdGhpcy5nZXRVdG9UbWFwcGluZyggdSApO1xuXHRcdFx0cmV0dXJuIHRoaXMuZ2V0VGFuZ2VudCggdCApO1xuXG5cdFx0fSxcblxuXHRcdGNvbXB1dGVGcmVuZXRGcmFtZXM6IGZ1bmN0aW9uICggc2VnbWVudHMsIGNsb3NlZCApIHtcblxuXHRcdFx0Ly8gc2VlIGh0dHA6Ly93d3cuY3MuaW5kaWFuYS5lZHUvcHViL3RlY2hyZXBvcnRzL1RSNDI1LnBkZlxuXG5cdFx0XHR2YXIgbm9ybWFsID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0dmFyIHRhbmdlbnRzID0gW107XG5cdFx0XHR2YXIgbm9ybWFscyA9IFtdO1xuXHRcdFx0dmFyIGJpbm9ybWFscyA9IFtdO1xuXG5cdFx0XHR2YXIgdmVjID0gbmV3IFZlY3RvcjMoKTtcblx0XHRcdHZhciBtYXQgPSBuZXcgTWF0cml4NCgpO1xuXG5cdFx0XHR2YXIgaSwgdSwgdGhldGE7XG5cblx0XHRcdC8vIGNvbXB1dGUgdGhlIHRhbmdlbnQgdmVjdG9ycyBmb3IgZWFjaCBzZWdtZW50IG9uIHRoZSBjdXJ2ZVxuXG5cdFx0XHRmb3IgKCBpID0gMDsgaSA8PSBzZWdtZW50czsgaSArKyApIHtcblxuXHRcdFx0XHR1ID0gaSAvIHNlZ21lbnRzO1xuXG5cdFx0XHRcdHRhbmdlbnRzWyBpIF0gPSB0aGlzLmdldFRhbmdlbnRBdCggdSApO1xuXHRcdFx0XHR0YW5nZW50c1sgaSBdLm5vcm1hbGl6ZSgpO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIHNlbGVjdCBhbiBpbml0aWFsIG5vcm1hbCB2ZWN0b3IgcGVycGVuZGljdWxhciB0byB0aGUgZmlyc3QgdGFuZ2VudCB2ZWN0b3IsXG5cdFx0XHQvLyBhbmQgaW4gdGhlIGRpcmVjdGlvbiBvZiB0aGUgbWluaW11bSB0YW5nZW50IHh5eiBjb21wb25lbnRcblxuXHRcdFx0bm9ybWFsc1sgMCBdID0gbmV3IFZlY3RvcjMoKTtcblx0XHRcdGJpbm9ybWFsc1sgMCBdID0gbmV3IFZlY3RvcjMoKTtcblx0XHRcdHZhciBtaW4gPSBOdW1iZXIuTUFYX1ZBTFVFO1xuXHRcdFx0dmFyIHR4ID0gTWF0aC5hYnMoIHRhbmdlbnRzWyAwIF0ueCApO1xuXHRcdFx0dmFyIHR5ID0gTWF0aC5hYnMoIHRhbmdlbnRzWyAwIF0ueSApO1xuXHRcdFx0dmFyIHR6ID0gTWF0aC5hYnMoIHRhbmdlbnRzWyAwIF0ueiApO1xuXG5cdFx0XHRpZiAoIHR4IDw9IG1pbiApIHtcblxuXHRcdFx0XHRtaW4gPSB0eDtcblx0XHRcdFx0bm9ybWFsLnNldCggMSwgMCwgMCApO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggdHkgPD0gbWluICkge1xuXG5cdFx0XHRcdG1pbiA9IHR5O1xuXHRcdFx0XHRub3JtYWwuc2V0KCAwLCAxLCAwICk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCB0eiA8PSBtaW4gKSB7XG5cblx0XHRcdFx0bm9ybWFsLnNldCggMCwgMCwgMSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZlYy5jcm9zc1ZlY3RvcnMoIHRhbmdlbnRzWyAwIF0sIG5vcm1hbCApLm5vcm1hbGl6ZSgpO1xuXG5cdFx0XHRub3JtYWxzWyAwIF0uY3Jvc3NWZWN0b3JzKCB0YW5nZW50c1sgMCBdLCB2ZWMgKTtcblx0XHRcdGJpbm9ybWFsc1sgMCBdLmNyb3NzVmVjdG9ycyggdGFuZ2VudHNbIDAgXSwgbm9ybWFsc1sgMCBdICk7XG5cblxuXHRcdFx0Ly8gY29tcHV0ZSB0aGUgc2xvd2x5LXZhcnlpbmcgbm9ybWFsIGFuZCBiaW5vcm1hbCB2ZWN0b3JzIGZvciBlYWNoIHNlZ21lbnQgb24gdGhlIGN1cnZlXG5cblx0XHRcdGZvciAoIGkgPSAxOyBpIDw9IHNlZ21lbnRzOyBpICsrICkge1xuXG5cdFx0XHRcdG5vcm1hbHNbIGkgXSA9IG5vcm1hbHNbIGkgLSAxIF0uY2xvbmUoKTtcblxuXHRcdFx0XHRiaW5vcm1hbHNbIGkgXSA9IGJpbm9ybWFsc1sgaSAtIDEgXS5jbG9uZSgpO1xuXG5cdFx0XHRcdHZlYy5jcm9zc1ZlY3RvcnMoIHRhbmdlbnRzWyBpIC0gMSBdLCB0YW5nZW50c1sgaSBdICk7XG5cblx0XHRcdFx0aWYgKCB2ZWMubGVuZ3RoKCkgPiBOdW1iZXIuRVBTSUxPTiApIHtcblxuXHRcdFx0XHRcdHZlYy5ub3JtYWxpemUoKTtcblxuXHRcdFx0XHRcdHRoZXRhID0gTWF0aC5hY29zKCBfTWF0aC5jbGFtcCggdGFuZ2VudHNbIGkgLSAxIF0uZG90KCB0YW5nZW50c1sgaSBdICksIC0gMSwgMSApICk7IC8vIGNsYW1wIGZvciBmbG9hdGluZyBwdCBlcnJvcnNcblxuXHRcdFx0XHRcdG5vcm1hbHNbIGkgXS5hcHBseU1hdHJpeDQoIG1hdC5tYWtlUm90YXRpb25BeGlzKCB2ZWMsIHRoZXRhICkgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ymlub3JtYWxzWyBpIF0uY3Jvc3NWZWN0b3JzKCB0YW5nZW50c1sgaSBdLCBub3JtYWxzWyBpIF0gKTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyBpZiB0aGUgY3VydmUgaXMgY2xvc2VkLCBwb3N0cHJvY2VzcyB0aGUgdmVjdG9ycyBzbyB0aGUgZmlyc3QgYW5kIGxhc3Qgbm9ybWFsIHZlY3RvcnMgYXJlIHRoZSBzYW1lXG5cblx0XHRcdGlmICggY2xvc2VkID09PSB0cnVlICkge1xuXG5cdFx0XHRcdHRoZXRhID0gTWF0aC5hY29zKCBfTWF0aC5jbGFtcCggbm9ybWFsc1sgMCBdLmRvdCggbm9ybWFsc1sgc2VnbWVudHMgXSApLCAtIDEsIDEgKSApO1xuXHRcdFx0XHR0aGV0YSAvPSBzZWdtZW50cztcblxuXHRcdFx0XHRpZiAoIHRhbmdlbnRzWyAwIF0uZG90KCB2ZWMuY3Jvc3NWZWN0b3JzKCBub3JtYWxzWyAwIF0sIG5vcm1hbHNbIHNlZ21lbnRzIF0gKSApID4gMCApIHtcblxuXHRcdFx0XHRcdHRoZXRhID0gLSB0aGV0YTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Zm9yICggaSA9IDE7IGkgPD0gc2VnbWVudHM7IGkgKysgKSB7XG5cblx0XHRcdFx0XHQvLyB0d2lzdCBhIGxpdHRsZS4uLlxuXHRcdFx0XHRcdG5vcm1hbHNbIGkgXS5hcHBseU1hdHJpeDQoIG1hdC5tYWtlUm90YXRpb25BeGlzKCB0YW5nZW50c1sgaSBdLCB0aGV0YSAqIGkgKSApO1xuXHRcdFx0XHRcdGJpbm9ybWFsc1sgaSBdLmNyb3NzVmVjdG9ycyggdGFuZ2VudHNbIGkgXSwgbm9ybWFsc1sgaSBdICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB7XG5cdFx0XHRcdHRhbmdlbnRzOiB0YW5nZW50cyxcblx0XHRcdFx0bm9ybWFsczogbm9ybWFscyxcblx0XHRcdFx0Ymlub3JtYWxzOiBiaW5vcm1hbHNcblx0XHRcdH07XG5cblx0XHR9XG5cblx0fTtcblxuXHQvLyBUT0RPOiBUcmFuc2Zvcm1hdGlvbiBmb3IgQ3VydmVzP1xuXG5cdC8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuXHQgKlx0M0QgQ3VydmVzXG5cdCAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cblxuXHQvLyBBIEZhY3RvcnkgbWV0aG9kIGZvciBjcmVhdGluZyBuZXcgY3VydmUgc3ViY2xhc3Nlc1xuXG5cdEN1cnZlLmNyZWF0ZSA9IGZ1bmN0aW9uICggY29uc3RydWN0b3IsIGdldFBvaW50RnVuYyApIHtcblxuXHRcdGNvbnN0cnVjdG9yLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEN1cnZlLnByb3RvdHlwZSApO1xuXHRcdGNvbnN0cnVjdG9yLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IGNvbnN0cnVjdG9yO1xuXHRcdGNvbnN0cnVjdG9yLnByb3RvdHlwZS5nZXRQb2ludCA9IGdldFBvaW50RnVuYztcblxuXHRcdHJldHVybiBjb25zdHJ1Y3RvcjtcblxuXHR9O1xuXG5cdC8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuXHQgKlx0TGluZVxuXHQgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cblx0ZnVuY3Rpb24gTGluZUN1cnZlKCB2MSwgdjIgKSB7XG5cblx0XHR0aGlzLnYxID0gdjE7XG5cdFx0dGhpcy52MiA9IHYyO1xuXG5cdH1cblxuXHRMaW5lQ3VydmUucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggQ3VydmUucHJvdG90eXBlICk7XG5cdExpbmVDdXJ2ZS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBMaW5lQ3VydmU7XG5cblx0TGluZUN1cnZlLnByb3RvdHlwZS5pc0xpbmVDdXJ2ZSA9IHRydWU7XG5cblx0TGluZUN1cnZlLnByb3RvdHlwZS5nZXRQb2ludCA9IGZ1bmN0aW9uICggdCApIHtcblxuXHRcdGlmICggdCA9PT0gMSApIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMudjIuY2xvbmUoKTtcblxuXHRcdH1cblxuXHRcdHZhciBwb2ludCA9IHRoaXMudjIuY2xvbmUoKS5zdWIoIHRoaXMudjEgKTtcblx0XHRwb2ludC5tdWx0aXBseVNjYWxhciggdCApLmFkZCggdGhpcy52MSApO1xuXG5cdFx0cmV0dXJuIHBvaW50O1xuXG5cdH07XG5cblx0Ly8gTGluZSBjdXJ2ZSBpcyBsaW5lYXIsIHNvIHdlIGNhbiBvdmVyd3JpdGUgZGVmYXVsdCBnZXRQb2ludEF0XG5cblx0TGluZUN1cnZlLnByb3RvdHlwZS5nZXRQb2ludEF0ID0gZnVuY3Rpb24gKCB1ICkge1xuXG5cdFx0cmV0dXJuIHRoaXMuZ2V0UG9pbnQoIHUgKTtcblxuXHR9O1xuXG5cdExpbmVDdXJ2ZS5wcm90b3R5cGUuZ2V0VGFuZ2VudCA9IGZ1bmN0aW9uKCB0ICkge1xuXG5cdFx0dmFyIHRhbmdlbnQgPSB0aGlzLnYyLmNsb25lKCkuc3ViKCB0aGlzLnYxICk7XG5cblx0XHRyZXR1cm4gdGFuZ2VudC5ub3JtYWxpemUoKTtcblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIHp6ODUgLyBodHRwOi8vd3d3LmxhYjRnYW1lcy5uZXQveno4NS9ibG9nXG5cdCAqXG5cdCAqKi9cblxuXHQvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblx0ICpcdEN1cnZlZCBQYXRoIC0gYSBjdXJ2ZSBwYXRoIGlzIHNpbXBseSBhIGFycmF5IG9mIGNvbm5lY3RlZFxuXHQgKiAgY3VydmVzLCBidXQgcmV0YWlucyB0aGUgYXBpIG9mIGEgY3VydmVcblx0ICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5cdGZ1bmN0aW9uIEN1cnZlUGF0aCgpIHtcblxuXHRcdHRoaXMuY3VydmVzID0gW107XG5cblx0XHR0aGlzLmF1dG9DbG9zZSA9IGZhbHNlOyAvLyBBdXRvbWF0aWNhbGx5IGNsb3NlcyB0aGUgcGF0aFxuXG5cdH1cblxuXHRDdXJ2ZVBhdGgucHJvdG90eXBlID0gT2JqZWN0LmFzc2lnbiggT2JqZWN0LmNyZWF0ZSggQ3VydmUucHJvdG90eXBlICksIHtcblxuXHRcdGNvbnN0cnVjdG9yOiBDdXJ2ZVBhdGgsXG5cblx0XHRhZGQ6IGZ1bmN0aW9uICggY3VydmUgKSB7XG5cblx0XHRcdHRoaXMuY3VydmVzLnB1c2goIGN1cnZlICk7XG5cblx0XHR9LFxuXG5cdFx0Y2xvc2VQYXRoOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdC8vIEFkZCBhIGxpbmUgY3VydmUgaWYgc3RhcnQgYW5kIGVuZCBvZiBsaW5lcyBhcmUgbm90IGNvbm5lY3RlZFxuXHRcdFx0dmFyIHN0YXJ0UG9pbnQgPSB0aGlzLmN1cnZlc1sgMCBdLmdldFBvaW50KCAwICk7XG5cdFx0XHR2YXIgZW5kUG9pbnQgPSB0aGlzLmN1cnZlc1sgdGhpcy5jdXJ2ZXMubGVuZ3RoIC0gMSBdLmdldFBvaW50KCAxICk7XG5cblx0XHRcdGlmICggISBzdGFydFBvaW50LmVxdWFscyggZW5kUG9pbnQgKSApIHtcblxuXHRcdFx0XHR0aGlzLmN1cnZlcy5wdXNoKCBuZXcgTGluZUN1cnZlKCBlbmRQb2ludCwgc3RhcnRQb2ludCApICk7XG5cblx0XHRcdH1cblxuXHRcdH0sXG5cblx0XHQvLyBUbyBnZXQgYWNjdXJhdGUgcG9pbnQgd2l0aCByZWZlcmVuY2UgdG9cblx0XHQvLyBlbnRpcmUgcGF0aCBkaXN0YW5jZSBhdCB0aW1lIHQsXG5cdFx0Ly8gZm9sbG93aW5nIGhhcyB0byBiZSBkb25lOlxuXG5cdFx0Ly8gMS4gTGVuZ3RoIG9mIGVhY2ggc3ViIHBhdGggaGF2ZSB0byBiZSBrbm93blxuXHRcdC8vIDIuIExvY2F0ZSBhbmQgaWRlbnRpZnkgdHlwZSBvZiBjdXJ2ZVxuXHRcdC8vIDMuIEdldCB0IGZvciB0aGUgY3VydmVcblx0XHQvLyA0LiBSZXR1cm4gY3VydmUuZ2V0UG9pbnRBdCh0JylcblxuXHRcdGdldFBvaW50OiBmdW5jdGlvbiAoIHQgKSB7XG5cblx0XHRcdHZhciBkID0gdCAqIHRoaXMuZ2V0TGVuZ3RoKCk7XG5cdFx0XHR2YXIgY3VydmVMZW5ndGhzID0gdGhpcy5nZXRDdXJ2ZUxlbmd0aHMoKTtcblx0XHRcdHZhciBpID0gMDtcblxuXHRcdFx0Ly8gVG8gdGhpbmsgYWJvdXQgYm91bmRhcmllcyBwb2ludHMuXG5cblx0XHRcdHdoaWxlICggaSA8IGN1cnZlTGVuZ3Rocy5sZW5ndGggKSB7XG5cblx0XHRcdFx0aWYgKCBjdXJ2ZUxlbmd0aHNbIGkgXSA+PSBkICkge1xuXG5cdFx0XHRcdFx0dmFyIGRpZmYgPSBjdXJ2ZUxlbmd0aHNbIGkgXSAtIGQ7XG5cdFx0XHRcdFx0dmFyIGN1cnZlID0gdGhpcy5jdXJ2ZXNbIGkgXTtcblxuXHRcdFx0XHRcdHZhciBzZWdtZW50TGVuZ3RoID0gY3VydmUuZ2V0TGVuZ3RoKCk7XG5cdFx0XHRcdFx0dmFyIHUgPSBzZWdtZW50TGVuZ3RoID09PSAwID8gMCA6IDEgLSBkaWZmIC8gc2VnbWVudExlbmd0aDtcblxuXHRcdFx0XHRcdHJldHVybiBjdXJ2ZS5nZXRQb2ludEF0KCB1ICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGkgKys7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIG51bGw7XG5cblx0XHRcdC8vIGxvb3Agd2hlcmUgc3VtICE9IDAsIHN1bSA+IGQgLCBzdW0rMSA8ZFxuXG5cdFx0fSxcblxuXHRcdC8vIFdlIGNhbm5vdCB1c2UgdGhlIGRlZmF1bHQgVEhSRUUuQ3VydmUgZ2V0UG9pbnQoKSB3aXRoIGdldExlbmd0aCgpIGJlY2F1c2UgaW5cblx0XHQvLyBUSFJFRS5DdXJ2ZSwgZ2V0TGVuZ3RoKCkgZGVwZW5kcyBvbiBnZXRQb2ludCgpIGJ1dCBpbiBUSFJFRS5DdXJ2ZVBhdGhcblx0XHQvLyBnZXRQb2ludCgpIGRlcGVuZHMgb24gZ2V0TGVuZ3RoXG5cblx0XHRnZXRMZW5ndGg6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIGxlbnMgPSB0aGlzLmdldEN1cnZlTGVuZ3RocygpO1xuXHRcdFx0cmV0dXJuIGxlbnNbIGxlbnMubGVuZ3RoIC0gMSBdO1xuXG5cdFx0fSxcblxuXHRcdC8vIGNhY2hlTGVuZ3RocyBtdXN0IGJlIHJlY2FsY3VsYXRlZC5cblx0XHR1cGRhdGVBcmNMZW5ndGhzOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMubmVlZHNVcGRhdGUgPSB0cnVlO1xuXHRcdFx0dGhpcy5jYWNoZUxlbmd0aHMgPSBudWxsO1xuXHRcdFx0dGhpcy5nZXRMZW5ndGhzKCk7XG5cblx0XHR9LFxuXG5cdFx0Ly8gQ29tcHV0ZSBsZW5ndGhzIGFuZCBjYWNoZSB0aGVtXG5cdFx0Ly8gV2UgY2Fubm90IG92ZXJ3cml0ZSBnZXRMZW5ndGhzKCkgYmVjYXVzZSBVdG9UIG1hcHBpbmcgdXNlcyBpdC5cblxuXHRcdGdldEN1cnZlTGVuZ3RoczogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHQvLyBXZSB1c2UgY2FjaGUgdmFsdWVzIGlmIGN1cnZlcyBhbmQgY2FjaGUgYXJyYXkgYXJlIHNhbWUgbGVuZ3RoXG5cblx0XHRcdGlmICggdGhpcy5jYWNoZUxlbmd0aHMgJiYgdGhpcy5jYWNoZUxlbmd0aHMubGVuZ3RoID09PSB0aGlzLmN1cnZlcy5sZW5ndGggKSB7XG5cblx0XHRcdFx0cmV0dXJuIHRoaXMuY2FjaGVMZW5ndGhzO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIEdldCBsZW5ndGggb2Ygc3ViLWN1cnZlXG5cdFx0XHQvLyBQdXNoIHN1bXMgaW50byBjYWNoZWQgYXJyYXlcblxuXHRcdFx0dmFyIGxlbmd0aHMgPSBbXSwgc3VtcyA9IDA7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IHRoaXMuY3VydmVzLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0c3VtcyArPSB0aGlzLmN1cnZlc1sgaSBdLmdldExlbmd0aCgpO1xuXHRcdFx0XHRsZW5ndGhzLnB1c2goIHN1bXMgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLmNhY2hlTGVuZ3RocyA9IGxlbmd0aHM7XG5cblx0XHRcdHJldHVybiBsZW5ndGhzO1xuXG5cdFx0fSxcblxuXHRcdGdldFNwYWNlZFBvaW50czogZnVuY3Rpb24gKCBkaXZpc2lvbnMgKSB7XG5cblx0XHRcdGlmICggISBkaXZpc2lvbnMgKSBkaXZpc2lvbnMgPSA0MDtcblxuXHRcdFx0dmFyIHBvaW50cyA9IFtdO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPD0gZGl2aXNpb25zOyBpICsrICkge1xuXG5cdFx0XHRcdHBvaW50cy5wdXNoKCB0aGlzLmdldFBvaW50KCBpIC8gZGl2aXNpb25zICkgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHRoaXMuYXV0b0Nsb3NlICkge1xuXG5cdFx0XHRcdHBvaW50cy5wdXNoKCBwb2ludHNbIDAgXSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBwb2ludHM7XG5cblx0XHR9LFxuXG5cdFx0Z2V0UG9pbnRzOiBmdW5jdGlvbiAoIGRpdmlzaW9ucyApIHtcblxuXHRcdFx0ZGl2aXNpb25zID0gZGl2aXNpb25zIHx8IDEyO1xuXG5cdFx0XHR2YXIgcG9pbnRzID0gW10sIGxhc3Q7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgY3VydmVzID0gdGhpcy5jdXJ2ZXM7IGkgPCBjdXJ2ZXMubGVuZ3RoOyBpICsrICkge1xuXG5cdFx0XHRcdHZhciBjdXJ2ZSA9IGN1cnZlc1sgaSBdO1xuXHRcdFx0XHR2YXIgcmVzb2x1dGlvbiA9IChjdXJ2ZSAmJiBjdXJ2ZS5pc0VsbGlwc2VDdXJ2ZSkgPyBkaXZpc2lvbnMgKiAyXG5cdFx0XHRcdFx0OiAoY3VydmUgJiYgY3VydmUuaXNMaW5lQ3VydmUpID8gMVxuXHRcdFx0XHRcdDogKGN1cnZlICYmIGN1cnZlLmlzU3BsaW5lQ3VydmUpID8gZGl2aXNpb25zICogY3VydmUucG9pbnRzLmxlbmd0aFxuXHRcdFx0XHRcdDogZGl2aXNpb25zO1xuXG5cdFx0XHRcdHZhciBwdHMgPSBjdXJ2ZS5nZXRQb2ludHMoIHJlc29sdXRpb24gKTtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaiA9IDA7IGogPCBwdHMubGVuZ3RoOyBqKysgKSB7XG5cblx0XHRcdFx0XHR2YXIgcG9pbnQgPSBwdHNbIGogXTtcblxuXHRcdFx0XHRcdGlmICggbGFzdCAmJiBsYXN0LmVxdWFscyggcG9pbnQgKSApIGNvbnRpbnVlOyAvLyBlbnN1cmVzIG5vIGNvbnNlY3V0aXZlIHBvaW50cyBhcmUgZHVwbGljYXRlc1xuXG5cdFx0XHRcdFx0cG9pbnRzLnB1c2goIHBvaW50ICk7XG5cdFx0XHRcdFx0bGFzdCA9IHBvaW50O1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHRoaXMuYXV0b0Nsb3NlICYmIHBvaW50cy5sZW5ndGggPiAxICYmICFwb2ludHNbIHBvaW50cy5sZW5ndGggLSAxIF0uZXF1YWxzKCBwb2ludHNbIDAgXSApICkge1xuXG5cdFx0XHRcdHBvaW50cy5wdXNoKCBwb2ludHNbIDAgXSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBwb2ludHM7XG5cblx0XHR9LFxuXG5cdFx0LyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cdFx0ICpcdENyZWF0ZSBHZW9tZXRyaWVzIEhlbHBlcnNcblx0XHQgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cblx0XHQvLy8gR2VuZXJhdGUgZ2VvbWV0cnkgZnJvbSBwYXRoIHBvaW50cyAoZm9yIExpbmUgb3IgUG9pbnRzIG9iamVjdHMpXG5cblx0XHRjcmVhdGVQb2ludHNHZW9tZXRyeTogZnVuY3Rpb24gKCBkaXZpc2lvbnMgKSB7XG5cblx0XHRcdHZhciBwdHMgPSB0aGlzLmdldFBvaW50cyggZGl2aXNpb25zICk7XG5cdFx0XHRyZXR1cm4gdGhpcy5jcmVhdGVHZW9tZXRyeSggcHRzICk7XG5cblx0XHR9LFxuXG5cdFx0Ly8gR2VuZXJhdGUgZ2VvbWV0cnkgZnJvbSBlcXVpZGlzdGFudCBzYW1wbGluZyBhbG9uZyB0aGUgcGF0aFxuXG5cdFx0Y3JlYXRlU3BhY2VkUG9pbnRzR2VvbWV0cnk6IGZ1bmN0aW9uICggZGl2aXNpb25zICkge1xuXG5cdFx0XHR2YXIgcHRzID0gdGhpcy5nZXRTcGFjZWRQb2ludHMoIGRpdmlzaW9ucyApO1xuXHRcdFx0cmV0dXJuIHRoaXMuY3JlYXRlR2VvbWV0cnkoIHB0cyApO1xuXG5cdFx0fSxcblxuXHRcdGNyZWF0ZUdlb21ldHJ5OiBmdW5jdGlvbiAoIHBvaW50cyApIHtcblxuXHRcdFx0dmFyIGdlb21ldHJ5ID0gbmV3IEdlb21ldHJ5KCk7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IHBvaW50cy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdHZhciBwb2ludCA9IHBvaW50c1sgaSBdO1xuXHRcdFx0XHRnZW9tZXRyeS52ZXJ0aWNlcy5wdXNoKCBuZXcgVmVjdG9yMyggcG9pbnQueCwgcG9pbnQueSwgcG9pbnQueiB8fCAwICkgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gZ2VvbWV0cnk7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuXHQgKlx0RWxsaXBzZSBjdXJ2ZVxuXHQgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cblx0ZnVuY3Rpb24gRWxsaXBzZUN1cnZlKCBhWCwgYVksIHhSYWRpdXMsIHlSYWRpdXMsIGFTdGFydEFuZ2xlLCBhRW5kQW5nbGUsIGFDbG9ja3dpc2UsIGFSb3RhdGlvbiApIHtcblxuXHRcdHRoaXMuYVggPSBhWDtcblx0XHR0aGlzLmFZID0gYVk7XG5cblx0XHR0aGlzLnhSYWRpdXMgPSB4UmFkaXVzO1xuXHRcdHRoaXMueVJhZGl1cyA9IHlSYWRpdXM7XG5cblx0XHR0aGlzLmFTdGFydEFuZ2xlID0gYVN0YXJ0QW5nbGU7XG5cdFx0dGhpcy5hRW5kQW5nbGUgPSBhRW5kQW5nbGU7XG5cblx0XHR0aGlzLmFDbG9ja3dpc2UgPSBhQ2xvY2t3aXNlO1xuXG5cdFx0dGhpcy5hUm90YXRpb24gPSBhUm90YXRpb24gfHwgMDtcblxuXHR9XG5cblx0RWxsaXBzZUN1cnZlLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEN1cnZlLnByb3RvdHlwZSApO1xuXHRFbGxpcHNlQ3VydmUucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gRWxsaXBzZUN1cnZlO1xuXG5cdEVsbGlwc2VDdXJ2ZS5wcm90b3R5cGUuaXNFbGxpcHNlQ3VydmUgPSB0cnVlO1xuXG5cdEVsbGlwc2VDdXJ2ZS5wcm90b3R5cGUuZ2V0UG9pbnQgPSBmdW5jdGlvbiggdCApIHtcblxuXHRcdHZhciB0d29QaSA9IE1hdGguUEkgKiAyO1xuXHRcdHZhciBkZWx0YUFuZ2xlID0gdGhpcy5hRW5kQW5nbGUgLSB0aGlzLmFTdGFydEFuZ2xlO1xuXHRcdHZhciBzYW1lUG9pbnRzID0gTWF0aC5hYnMoIGRlbHRhQW5nbGUgKSA8IE51bWJlci5FUFNJTE9OO1xuXG5cdFx0Ly8gZW5zdXJlcyB0aGF0IGRlbHRhQW5nbGUgaXMgMCAuLiAyIFBJXG5cdFx0d2hpbGUgKCBkZWx0YUFuZ2xlIDwgMCApIGRlbHRhQW5nbGUgKz0gdHdvUGk7XG5cdFx0d2hpbGUgKCBkZWx0YUFuZ2xlID4gdHdvUGkgKSBkZWx0YUFuZ2xlIC09IHR3b1BpO1xuXG5cdFx0aWYgKCBkZWx0YUFuZ2xlIDwgTnVtYmVyLkVQU0lMT04gKSB7XG5cblx0XHRcdGlmICggc2FtZVBvaW50cyApIHtcblxuXHRcdFx0XHRkZWx0YUFuZ2xlID0gMDtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRkZWx0YUFuZ2xlID0gdHdvUGk7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGlmICggdGhpcy5hQ2xvY2t3aXNlID09PSB0cnVlICYmICEgc2FtZVBvaW50cyApIHtcblxuXHRcdFx0aWYgKCBkZWx0YUFuZ2xlID09PSB0d29QaSApIHtcblxuXHRcdFx0XHRkZWx0YUFuZ2xlID0gLSB0d29QaTtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRkZWx0YUFuZ2xlID0gZGVsdGFBbmdsZSAtIHR3b1BpO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHR2YXIgYW5nbGUgPSB0aGlzLmFTdGFydEFuZ2xlICsgdCAqIGRlbHRhQW5nbGU7XG5cdFx0dmFyIHggPSB0aGlzLmFYICsgdGhpcy54UmFkaXVzICogTWF0aC5jb3MoIGFuZ2xlICk7XG5cdFx0dmFyIHkgPSB0aGlzLmFZICsgdGhpcy55UmFkaXVzICogTWF0aC5zaW4oIGFuZ2xlICk7XG5cblx0XHRpZiAoIHRoaXMuYVJvdGF0aW9uICE9PSAwICkge1xuXG5cdFx0XHR2YXIgY29zID0gTWF0aC5jb3MoIHRoaXMuYVJvdGF0aW9uICk7XG5cdFx0XHR2YXIgc2luID0gTWF0aC5zaW4oIHRoaXMuYVJvdGF0aW9uICk7XG5cblx0XHRcdHZhciB0eCA9IHggLSB0aGlzLmFYO1xuXHRcdFx0dmFyIHR5ID0geSAtIHRoaXMuYVk7XG5cblx0XHRcdC8vIFJvdGF0ZSB0aGUgcG9pbnQgYWJvdXQgdGhlIGNlbnRlciBvZiB0aGUgZWxsaXBzZS5cblx0XHRcdHggPSB0eCAqIGNvcyAtIHR5ICogc2luICsgdGhpcy5hWDtcblx0XHRcdHkgPSB0eCAqIHNpbiArIHR5ICogY29zICsgdGhpcy5hWTtcblxuXHRcdH1cblxuXHRcdHJldHVybiBuZXcgVmVjdG9yMiggeCwgeSApO1xuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3Igeno4NSAvIGh0dHA6Ly93d3cubGFiNGdhbWVzLm5ldC96ejg1L2Jsb2dcblx0ICovXG5cblx0dmFyIEN1cnZlVXRpbHMgPSB7XG5cblx0XHR0YW5nZW50UXVhZHJhdGljQmV6aWVyOiBmdW5jdGlvbiAoIHQsIHAwLCBwMSwgcDIgKSB7XG5cblx0XHRcdHJldHVybiAyICogKCAxIC0gdCApICogKCBwMSAtIHAwICkgKyAyICogdCAqICggcDIgLSBwMSApO1xuXG5cdFx0fSxcblxuXHRcdC8vIFB1YXkgQmluZywgdGhhbmtzIGZvciBoZWxwaW5nIHdpdGggdGhpcyBkZXJpdmF0aXZlIVxuXG5cdFx0dGFuZ2VudEN1YmljQmV6aWVyOiBmdW5jdGlvbiAoIHQsIHAwLCBwMSwgcDIsIHAzICkge1xuXG5cdFx0XHRyZXR1cm4gLSAzICogcDAgKiAoIDEgLSB0ICkgKiAoIDEgLSB0ICkgICtcblx0XHRcdFx0MyAqIHAxICogKCAxIC0gdCApICogKCAxIC0gdCApIC0gNiAqIHQgKiBwMSAqICggMSAtIHQgKSArXG5cdFx0XHRcdDYgKiB0ICogIHAyICogKCAxIC0gdCApIC0gMyAqIHQgKiB0ICogcDIgK1xuXHRcdFx0XHQzICogdCAqIHQgKiBwMztcblxuXHRcdH0sXG5cblx0XHR0YW5nZW50U3BsaW5lOiBmdW5jdGlvbiAoIHQsIHAwLCBwMSwgcDIsIHAzICkge1xuXG5cdFx0XHQvLyBUbyBjaGVjayBpZiBteSBmb3JtdWxhcyBhcmUgY29ycmVjdFxuXG5cdFx0XHR2YXIgaDAwID0gNiAqIHQgKiB0IC0gNiAqIHQ7IFx0Ly8gZGVyaXZlZCBmcm9tIDJ0XjMg4oiSIDN0XjIgKyAxXG5cdFx0XHR2YXIgaDEwID0gMyAqIHQgKiB0IC0gNCAqIHQgKyAxOyAvLyB0XjMg4oiSIDJ0XjIgKyB0XG5cdFx0XHR2YXIgaDAxID0gLSA2ICogdCAqIHQgKyA2ICogdDsgXHQvLyDiiJIgMnQzICsgM3QyXG5cdFx0XHR2YXIgaDExID0gMyAqIHQgKiB0IC0gMiAqIHQ7XHQvLyB0MyDiiJIgdDJcblxuXHRcdFx0cmV0dXJuIGgwMCArIGgxMCArIGgwMSArIGgxMTtcblxuXHRcdH0sXG5cblx0XHQvLyBDYXRtdWxsLVJvbVxuXG5cdFx0aW50ZXJwb2xhdGU6IGZ1bmN0aW9uKCBwMCwgcDEsIHAyLCBwMywgdCApIHtcblxuXHRcdFx0dmFyIHYwID0gKCBwMiAtIHAwICkgKiAwLjU7XG5cdFx0XHR2YXIgdjEgPSAoIHAzIC0gcDEgKSAqIDAuNTtcblx0XHRcdHZhciB0MiA9IHQgKiB0O1xuXHRcdFx0dmFyIHQzID0gdCAqIHQyO1xuXHRcdFx0cmV0dXJuICggMiAqIHAxIC0gMiAqIHAyICsgdjAgKyB2MSApICogdDMgKyAoIC0gMyAqIHAxICsgMyAqIHAyIC0gMiAqIHYwIC0gdjEgKSAqIHQyICsgdjAgKiB0ICsgcDE7XG5cblx0XHR9XG5cblx0fTtcblxuXHQvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblx0ICpcdFNwbGluZSBjdXJ2ZVxuXHQgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cblx0ZnVuY3Rpb24gU3BsaW5lQ3VydmUoIHBvaW50cyAvKiBhcnJheSBvZiBWZWN0b3IyICovICkge1xuXG5cdFx0dGhpcy5wb2ludHMgPSAoIHBvaW50cyA9PT0gdW5kZWZpbmVkICkgPyBbXSA6IHBvaW50cztcblxuXHR9XG5cblx0U3BsaW5lQ3VydmUucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggQ3VydmUucHJvdG90eXBlICk7XG5cdFNwbGluZUN1cnZlLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFNwbGluZUN1cnZlO1xuXG5cdFNwbGluZUN1cnZlLnByb3RvdHlwZS5pc1NwbGluZUN1cnZlID0gdHJ1ZTtcblxuXHRTcGxpbmVDdXJ2ZS5wcm90b3R5cGUuZ2V0UG9pbnQgPSBmdW5jdGlvbiAoIHQgKSB7XG5cblx0XHR2YXIgcG9pbnRzID0gdGhpcy5wb2ludHM7XG5cdFx0dmFyIHBvaW50ID0gKCBwb2ludHMubGVuZ3RoIC0gMSApICogdDtcblxuXHRcdHZhciBpbnRQb2ludCA9IE1hdGguZmxvb3IoIHBvaW50ICk7XG5cdFx0dmFyIHdlaWdodCA9IHBvaW50IC0gaW50UG9pbnQ7XG5cblx0XHR2YXIgcG9pbnQwID0gcG9pbnRzWyBpbnRQb2ludCA9PT0gMCA/IGludFBvaW50IDogaW50UG9pbnQgLSAxIF07XG5cdFx0dmFyIHBvaW50MSA9IHBvaW50c1sgaW50UG9pbnQgXTtcblx0XHR2YXIgcG9pbnQyID0gcG9pbnRzWyBpbnRQb2ludCA+IHBvaW50cy5sZW5ndGggLSAyID8gcG9pbnRzLmxlbmd0aCAtIDEgOiBpbnRQb2ludCArIDEgXTtcblx0XHR2YXIgcG9pbnQzID0gcG9pbnRzWyBpbnRQb2ludCA+IHBvaW50cy5sZW5ndGggLSAzID8gcG9pbnRzLmxlbmd0aCAtIDEgOiBpbnRQb2ludCArIDIgXTtcblxuXHRcdHZhciBpbnRlcnBvbGF0ZSA9IEN1cnZlVXRpbHMuaW50ZXJwb2xhdGU7XG5cblx0XHRyZXR1cm4gbmV3IFZlY3RvcjIoXG5cdFx0XHRpbnRlcnBvbGF0ZSggcG9pbnQwLngsIHBvaW50MS54LCBwb2ludDIueCwgcG9pbnQzLngsIHdlaWdodCApLFxuXHRcdFx0aW50ZXJwb2xhdGUoIHBvaW50MC55LCBwb2ludDEueSwgcG9pbnQyLnksIHBvaW50My55LCB3ZWlnaHQgKVxuXHRcdCk7XG5cblx0fTtcblxuXHQvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblx0ICpcdEN1YmljIEJlemllciBjdXJ2ZVxuXHQgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cblx0ZnVuY3Rpb24gQ3ViaWNCZXppZXJDdXJ2ZSggdjAsIHYxLCB2MiwgdjMgKSB7XG5cblx0XHR0aGlzLnYwID0gdjA7XG5cdFx0dGhpcy52MSA9IHYxO1xuXHRcdHRoaXMudjIgPSB2Mjtcblx0XHR0aGlzLnYzID0gdjM7XG5cblx0fVxuXG5cdEN1YmljQmV6aWVyQ3VydmUucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggQ3VydmUucHJvdG90eXBlICk7XG5cdEN1YmljQmV6aWVyQ3VydmUucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gQ3ViaWNCZXppZXJDdXJ2ZTtcblxuXHRDdWJpY0JlemllckN1cnZlLnByb3RvdHlwZS5nZXRQb2ludCA9IGZ1bmN0aW9uICggdCApIHtcblxuXHRcdHZhciBiMyA9IFNoYXBlVXRpbHMuYjM7XG5cblx0XHRyZXR1cm4gbmV3IFZlY3RvcjIoXG5cdFx0XHRiMyggdCwgdGhpcy52MC54LCB0aGlzLnYxLngsIHRoaXMudjIueCwgdGhpcy52My54ICksXG5cdFx0XHRiMyggdCwgdGhpcy52MC55LCB0aGlzLnYxLnksIHRoaXMudjIueSwgdGhpcy52My55IClcblx0XHQpO1xuXG5cdH07XG5cblx0Q3ViaWNCZXppZXJDdXJ2ZS5wcm90b3R5cGUuZ2V0VGFuZ2VudCA9IGZ1bmN0aW9uKCB0ICkge1xuXG5cdFx0dmFyIHRhbmdlbnRDdWJpY0JlemllciA9IEN1cnZlVXRpbHMudGFuZ2VudEN1YmljQmV6aWVyO1xuXG5cdFx0cmV0dXJuIG5ldyBWZWN0b3IyKFxuXHRcdFx0dGFuZ2VudEN1YmljQmV6aWVyKCB0LCB0aGlzLnYwLngsIHRoaXMudjEueCwgdGhpcy52Mi54LCB0aGlzLnYzLnggKSxcblx0XHRcdHRhbmdlbnRDdWJpY0JlemllciggdCwgdGhpcy52MC55LCB0aGlzLnYxLnksIHRoaXMudjIueSwgdGhpcy52My55IClcblx0XHQpLm5vcm1hbGl6ZSgpO1xuXG5cdH07XG5cblx0LyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cdCAqXHRRdWFkcmF0aWMgQmV6aWVyIGN1cnZlXG5cdCAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cblxuXG5cdGZ1bmN0aW9uIFF1YWRyYXRpY0JlemllckN1cnZlKCB2MCwgdjEsIHYyICkge1xuXG5cdFx0dGhpcy52MCA9IHYwO1xuXHRcdHRoaXMudjEgPSB2MTtcblx0XHR0aGlzLnYyID0gdjI7XG5cblx0fVxuXG5cdFF1YWRyYXRpY0JlemllckN1cnZlLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEN1cnZlLnByb3RvdHlwZSApO1xuXHRRdWFkcmF0aWNCZXppZXJDdXJ2ZS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBRdWFkcmF0aWNCZXppZXJDdXJ2ZTtcblxuXG5cdFF1YWRyYXRpY0JlemllckN1cnZlLnByb3RvdHlwZS5nZXRQb2ludCA9IGZ1bmN0aW9uICggdCApIHtcblxuXHRcdHZhciBiMiA9IFNoYXBlVXRpbHMuYjI7XG5cblx0XHRyZXR1cm4gbmV3IFZlY3RvcjIoXG5cdFx0XHRiMiggdCwgdGhpcy52MC54LCB0aGlzLnYxLngsIHRoaXMudjIueCApLFxuXHRcdFx0YjIoIHQsIHRoaXMudjAueSwgdGhpcy52MS55LCB0aGlzLnYyLnkgKVxuXHRcdCk7XG5cblx0fTtcblxuXG5cdFF1YWRyYXRpY0JlemllckN1cnZlLnByb3RvdHlwZS5nZXRUYW5nZW50ID0gZnVuY3Rpb24oIHQgKSB7XG5cblx0XHR2YXIgdGFuZ2VudFF1YWRyYXRpY0JlemllciA9IEN1cnZlVXRpbHMudGFuZ2VudFF1YWRyYXRpY0JlemllcjtcblxuXHRcdHJldHVybiBuZXcgVmVjdG9yMihcblx0XHRcdHRhbmdlbnRRdWFkcmF0aWNCZXppZXIoIHQsIHRoaXMudjAueCwgdGhpcy52MS54LCB0aGlzLnYyLnggKSxcblx0XHRcdHRhbmdlbnRRdWFkcmF0aWNCZXppZXIoIHQsIHRoaXMudjAueSwgdGhpcy52MS55LCB0aGlzLnYyLnkgKVxuXHRcdCkubm9ybWFsaXplKCk7XG5cblx0fTtcblxuXHR2YXIgUGF0aFByb3RvdHlwZSA9IE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIEN1cnZlUGF0aC5wcm90b3R5cGUgKSwge1xuXG5cdFx0ZnJvbVBvaW50czogZnVuY3Rpb24gKCB2ZWN0b3JzICkge1xuXG5cdFx0XHR0aGlzLm1vdmVUbyggdmVjdG9yc1sgMCBdLngsIHZlY3RvcnNbIDAgXS55ICk7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMSwgbCA9IHZlY3RvcnMubGVuZ3RoOyBpIDwgbDsgaSArKyApIHtcblxuXHRcdFx0XHR0aGlzLmxpbmVUbyggdmVjdG9yc1sgaSBdLngsIHZlY3RvcnNbIGkgXS55ICk7XG5cblx0XHRcdH1cblxuXHRcdH0sXG5cblx0XHRtb3ZlVG86IGZ1bmN0aW9uICggeCwgeSApIHtcblxuXHRcdFx0dGhpcy5jdXJyZW50UG9pbnQuc2V0KCB4LCB5ICk7IC8vIFRPRE8gY29uc2lkZXIgcmVmZXJlbmNpbmcgdmVjdG9ycyBpbnN0ZWFkIG9mIGNvcHlpbmc/XG5cblx0XHR9LFxuXG5cdFx0bGluZVRvOiBmdW5jdGlvbiAoIHgsIHkgKSB7XG5cblx0XHRcdHZhciBjdXJ2ZSA9IG5ldyBMaW5lQ3VydmUoIHRoaXMuY3VycmVudFBvaW50LmNsb25lKCksIG5ldyBWZWN0b3IyKCB4LCB5ICkgKTtcblx0XHRcdHRoaXMuY3VydmVzLnB1c2goIGN1cnZlICk7XG5cblx0XHRcdHRoaXMuY3VycmVudFBvaW50LnNldCggeCwgeSApO1xuXG5cdFx0fSxcblxuXHRcdHF1YWRyYXRpY0N1cnZlVG86IGZ1bmN0aW9uICggYUNQeCwgYUNQeSwgYVgsIGFZICkge1xuXG5cdFx0XHR2YXIgY3VydmUgPSBuZXcgUXVhZHJhdGljQmV6aWVyQ3VydmUoXG5cdFx0XHRcdHRoaXMuY3VycmVudFBvaW50LmNsb25lKCksXG5cdFx0XHRcdG5ldyBWZWN0b3IyKCBhQ1B4LCBhQ1B5ICksXG5cdFx0XHRcdG5ldyBWZWN0b3IyKCBhWCwgYVkgKVxuXHRcdFx0KTtcblxuXHRcdFx0dGhpcy5jdXJ2ZXMucHVzaCggY3VydmUgKTtcblxuXHRcdFx0dGhpcy5jdXJyZW50UG9pbnQuc2V0KCBhWCwgYVkgKTtcblxuXHRcdH0sXG5cblx0XHRiZXppZXJDdXJ2ZVRvOiBmdW5jdGlvbiAoIGFDUDF4LCBhQ1AxeSwgYUNQMngsIGFDUDJ5LCBhWCwgYVkgKSB7XG5cblx0XHRcdHZhciBjdXJ2ZSA9IG5ldyBDdWJpY0JlemllckN1cnZlKFxuXHRcdFx0XHR0aGlzLmN1cnJlbnRQb2ludC5jbG9uZSgpLFxuXHRcdFx0XHRuZXcgVmVjdG9yMiggYUNQMXgsIGFDUDF5ICksXG5cdFx0XHRcdG5ldyBWZWN0b3IyKCBhQ1AyeCwgYUNQMnkgKSxcblx0XHRcdFx0bmV3IFZlY3RvcjIoIGFYLCBhWSApXG5cdFx0XHQpO1xuXG5cdFx0XHR0aGlzLmN1cnZlcy5wdXNoKCBjdXJ2ZSApO1xuXG5cdFx0XHR0aGlzLmN1cnJlbnRQb2ludC5zZXQoIGFYLCBhWSApO1xuXG5cdFx0fSxcblxuXHRcdHNwbGluZVRocnU6IGZ1bmN0aW9uICggcHRzIC8qQXJyYXkgb2YgVmVjdG9yKi8gKSB7XG5cblx0XHRcdHZhciBucHRzID0gWyB0aGlzLmN1cnJlbnRQb2ludC5jbG9uZSgpIF0uY29uY2F0KCBwdHMgKTtcblxuXHRcdFx0dmFyIGN1cnZlID0gbmV3IFNwbGluZUN1cnZlKCBucHRzICk7XG5cdFx0XHR0aGlzLmN1cnZlcy5wdXNoKCBjdXJ2ZSApO1xuXG5cdFx0XHR0aGlzLmN1cnJlbnRQb2ludC5jb3B5KCBwdHNbIHB0cy5sZW5ndGggLSAxIF0gKTtcblxuXHRcdH0sXG5cblx0XHRhcmM6IGZ1bmN0aW9uICggYVgsIGFZLCBhUmFkaXVzLCBhU3RhcnRBbmdsZSwgYUVuZEFuZ2xlLCBhQ2xvY2t3aXNlICkge1xuXG5cdFx0XHR2YXIgeDAgPSB0aGlzLmN1cnJlbnRQb2ludC54O1xuXHRcdFx0dmFyIHkwID0gdGhpcy5jdXJyZW50UG9pbnQueTtcblxuXHRcdFx0dGhpcy5hYnNhcmMoIGFYICsgeDAsIGFZICsgeTAsIGFSYWRpdXMsXG5cdFx0XHRcdGFTdGFydEFuZ2xlLCBhRW5kQW5nbGUsIGFDbG9ja3dpc2UgKTtcblxuXHRcdH0sXG5cblx0XHRhYnNhcmM6IGZ1bmN0aW9uICggYVgsIGFZLCBhUmFkaXVzLCBhU3RhcnRBbmdsZSwgYUVuZEFuZ2xlLCBhQ2xvY2t3aXNlICkge1xuXG5cdFx0XHR0aGlzLmFic2VsbGlwc2UoIGFYLCBhWSwgYVJhZGl1cywgYVJhZGl1cywgYVN0YXJ0QW5nbGUsIGFFbmRBbmdsZSwgYUNsb2Nrd2lzZSApO1xuXG5cdFx0fSxcblxuXHRcdGVsbGlwc2U6IGZ1bmN0aW9uICggYVgsIGFZLCB4UmFkaXVzLCB5UmFkaXVzLCBhU3RhcnRBbmdsZSwgYUVuZEFuZ2xlLCBhQ2xvY2t3aXNlLCBhUm90YXRpb24gKSB7XG5cblx0XHRcdHZhciB4MCA9IHRoaXMuY3VycmVudFBvaW50Lng7XG5cdFx0XHR2YXIgeTAgPSB0aGlzLmN1cnJlbnRQb2ludC55O1xuXG5cdFx0XHR0aGlzLmFic2VsbGlwc2UoIGFYICsgeDAsIGFZICsgeTAsIHhSYWRpdXMsIHlSYWRpdXMsIGFTdGFydEFuZ2xlLCBhRW5kQW5nbGUsIGFDbG9ja3dpc2UsIGFSb3RhdGlvbiApO1xuXG5cdFx0fSxcblxuXHRcdGFic2VsbGlwc2U6IGZ1bmN0aW9uICggYVgsIGFZLCB4UmFkaXVzLCB5UmFkaXVzLCBhU3RhcnRBbmdsZSwgYUVuZEFuZ2xlLCBhQ2xvY2t3aXNlLCBhUm90YXRpb24gKSB7XG5cblx0XHRcdHZhciBjdXJ2ZSA9IG5ldyBFbGxpcHNlQ3VydmUoIGFYLCBhWSwgeFJhZGl1cywgeVJhZGl1cywgYVN0YXJ0QW5nbGUsIGFFbmRBbmdsZSwgYUNsb2Nrd2lzZSwgYVJvdGF0aW9uICk7XG5cblx0XHRcdGlmICggdGhpcy5jdXJ2ZXMubGVuZ3RoID4gMCApIHtcblxuXHRcdFx0XHQvLyBpZiBhIHByZXZpb3VzIGN1cnZlIGlzIHByZXNlbnQsIGF0dGVtcHQgdG8gam9pblxuXHRcdFx0XHR2YXIgZmlyc3RQb2ludCA9IGN1cnZlLmdldFBvaW50KCAwICk7XG5cblx0XHRcdFx0aWYgKCAhIGZpcnN0UG9pbnQuZXF1YWxzKCB0aGlzLmN1cnJlbnRQb2ludCApICkge1xuXG5cdFx0XHRcdFx0dGhpcy5saW5lVG8oIGZpcnN0UG9pbnQueCwgZmlyc3RQb2ludC55ICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuY3VydmVzLnB1c2goIGN1cnZlICk7XG5cblx0XHRcdHZhciBsYXN0UG9pbnQgPSBjdXJ2ZS5nZXRQb2ludCggMSApO1xuXHRcdFx0dGhpcy5jdXJyZW50UG9pbnQuY29weSggbGFzdFBvaW50ICk7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIHp6ODUgLyBodHRwOi8vd3d3LmxhYjRnYW1lcy5uZXQveno4NS9ibG9nXG5cdCAqIERlZmluZXMgYSAyZCBzaGFwZSBwbGFuZSB1c2luZyBwYXRocy5cblx0ICoqL1xuXG5cdC8vIFNURVAgMSBDcmVhdGUgYSBwYXRoLlxuXHQvLyBTVEVQIDIgVHVybiBwYXRoIGludG8gc2hhcGUuXG5cdC8vIFNURVAgMyBFeHRydWRlR2VvbWV0cnkgdGFrZXMgaW4gU2hhcGUvU2hhcGVzXG5cdC8vIFNURVAgM2EgLSBFeHRyYWN0IHBvaW50cyBmcm9tIGVhY2ggc2hhcGUsIHR1cm4gdG8gdmVydGljZXNcblx0Ly8gU1RFUCAzYiAtIFRyaWFuZ3VsYXRlIGVhY2ggc2hhcGUsIGFkZCBmYWNlcy5cblxuXHRmdW5jdGlvbiBTaGFwZSgpIHtcblxuXHRcdFBhdGguYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApO1xuXG5cdFx0dGhpcy5ob2xlcyA9IFtdO1xuXG5cdH1cblxuXHRTaGFwZS5wcm90b3R5cGUgPSBPYmplY3QuYXNzaWduKCBPYmplY3QuY3JlYXRlKCBQYXRoUHJvdG90eXBlICksIHtcblxuXHRcdGNvbnN0cnVjdG9yOiBTaGFwZSxcblxuXHRcdGdldFBvaW50c0hvbGVzOiBmdW5jdGlvbiAoIGRpdmlzaW9ucyApIHtcblxuXHRcdFx0dmFyIGhvbGVzUHRzID0gW107XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IHRoaXMuaG9sZXMubGVuZ3RoOyBpIDwgbDsgaSArKyApIHtcblxuXHRcdFx0XHRob2xlc1B0c1sgaSBdID0gdGhpcy5ob2xlc1sgaSBdLmdldFBvaW50cyggZGl2aXNpb25zICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIGhvbGVzUHRzO1xuXG5cdFx0fSxcblxuXHRcdC8vIEdldCBwb2ludHMgb2Ygc2hhcGUgYW5kIGhvbGVzIChrZXlwb2ludHMgYmFzZWQgb24gc2VnbWVudHMgcGFyYW1ldGVyKVxuXG5cdFx0ZXh0cmFjdEFsbFBvaW50czogZnVuY3Rpb24gKCBkaXZpc2lvbnMgKSB7XG5cblx0XHRcdHJldHVybiB7XG5cblx0XHRcdFx0c2hhcGU6IHRoaXMuZ2V0UG9pbnRzKCBkaXZpc2lvbnMgKSxcblx0XHRcdFx0aG9sZXM6IHRoaXMuZ2V0UG9pbnRzSG9sZXMoIGRpdmlzaW9ucyApXG5cblx0XHRcdH07XG5cblx0XHR9LFxuXG5cdFx0ZXh0cmFjdFBvaW50czogZnVuY3Rpb24gKCBkaXZpc2lvbnMgKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLmV4dHJhY3RBbGxQb2ludHMoIGRpdmlzaW9ucyApO1xuXG5cdFx0fVxuXG5cdH0gKTtcblxuXHQvKipcblx0ICogQGF1dGhvciB6ejg1IC8gaHR0cDovL3d3dy5sYWI0Z2FtZXMubmV0L3p6ODUvYmxvZ1xuXHQgKiBDcmVhdGVzIGZyZWUgZm9ybSAyZCBwYXRoIHVzaW5nIHNlcmllcyBvZiBwb2ludHMsIGxpbmVzIG9yIGN1cnZlcy5cblx0ICpcblx0ICoqL1xuXG5cdGZ1bmN0aW9uIFBhdGgoIHBvaW50cyApIHtcblxuXHRcdEN1cnZlUGF0aC5jYWxsKCB0aGlzICk7XG5cdFx0dGhpcy5jdXJyZW50UG9pbnQgPSBuZXcgVmVjdG9yMigpO1xuXG5cdFx0aWYgKCBwb2ludHMgKSB7XG5cblx0XHRcdHRoaXMuZnJvbVBvaW50cyggcG9pbnRzICk7XG5cblx0XHR9XG5cblx0fVxuXG5cdFBhdGgucHJvdG90eXBlID0gUGF0aFByb3RvdHlwZTtcblx0UGF0aFByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFBhdGg7XG5cblxuXHQvLyBtaW5pbWFsIGNsYXNzIGZvciBwcm94aW5nIGZ1bmN0aW9ucyB0byBQYXRoLiBSZXBsYWNlcyBvbGQgXCJleHRyYWN0U3VicGF0aHMoKVwiXG5cdGZ1bmN0aW9uIFNoYXBlUGF0aCgpIHtcblx0XHR0aGlzLnN1YlBhdGhzID0gW107XG5cdFx0dGhpcy5jdXJyZW50UGF0aCA9IG51bGw7XG5cdH1cblxuXHRTaGFwZVBhdGgucHJvdG90eXBlID0ge1xuXHRcdG1vdmVUbzogZnVuY3Rpb24gKCB4LCB5ICkge1xuXHRcdFx0dGhpcy5jdXJyZW50UGF0aCA9IG5ldyBQYXRoKCk7XG5cdFx0XHR0aGlzLnN1YlBhdGhzLnB1c2godGhpcy5jdXJyZW50UGF0aCk7XG5cdFx0XHR0aGlzLmN1cnJlbnRQYXRoLm1vdmVUbyggeCwgeSApO1xuXHRcdH0sXG5cdFx0bGluZVRvOiBmdW5jdGlvbiAoIHgsIHkgKSB7XG5cdFx0XHR0aGlzLmN1cnJlbnRQYXRoLmxpbmVUbyggeCwgeSApO1xuXHRcdH0sXG5cdFx0cXVhZHJhdGljQ3VydmVUbzogZnVuY3Rpb24gKCBhQ1B4LCBhQ1B5LCBhWCwgYVkgKSB7XG5cdFx0XHR0aGlzLmN1cnJlbnRQYXRoLnF1YWRyYXRpY0N1cnZlVG8oIGFDUHgsIGFDUHksIGFYLCBhWSApO1xuXHRcdH0sXG5cdFx0YmV6aWVyQ3VydmVUbzogZnVuY3Rpb24gKCBhQ1AxeCwgYUNQMXksIGFDUDJ4LCBhQ1AyeSwgYVgsIGFZICkge1xuXHRcdFx0dGhpcy5jdXJyZW50UGF0aC5iZXppZXJDdXJ2ZVRvKCBhQ1AxeCwgYUNQMXksIGFDUDJ4LCBhQ1AyeSwgYVgsIGFZICk7XG5cdFx0fSxcblx0XHRzcGxpbmVUaHJ1OiBmdW5jdGlvbiAoIHB0cyApIHtcblx0XHRcdHRoaXMuY3VycmVudFBhdGguc3BsaW5lVGhydSggcHRzICk7XG5cdFx0fSxcblxuXHRcdHRvU2hhcGVzOiBmdW5jdGlvbiAoIGlzQ0NXLCBub0hvbGVzICkge1xuXG5cdFx0XHRmdW5jdGlvbiB0b1NoYXBlc05vSG9sZXMoIGluU3VicGF0aHMgKSB7XG5cblx0XHRcdFx0dmFyIHNoYXBlcyA9IFtdO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGluU3VicGF0aHMubGVuZ3RoOyBpIDwgbDsgaSArKyApIHtcblxuXHRcdFx0XHRcdHZhciB0bXBQYXRoID0gaW5TdWJwYXRoc1sgaSBdO1xuXG5cdFx0XHRcdFx0dmFyIHRtcFNoYXBlID0gbmV3IFNoYXBlKCk7XG5cdFx0XHRcdFx0dG1wU2hhcGUuY3VydmVzID0gdG1wUGF0aC5jdXJ2ZXM7XG5cblx0XHRcdFx0XHRzaGFwZXMucHVzaCggdG1wU2hhcGUgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuIHNoYXBlcztcblxuXHRcdFx0fVxuXG5cdFx0XHRmdW5jdGlvbiBpc1BvaW50SW5zaWRlUG9seWdvbiggaW5QdCwgaW5Qb2x5Z29uICkge1xuXG5cdFx0XHRcdHZhciBwb2x5TGVuID0gaW5Qb2x5Z29uLmxlbmd0aDtcblxuXHRcdFx0XHQvLyBpblB0IG9uIHBvbHlnb24gY29udG91ciA9PiBpbW1lZGlhdGUgc3VjY2VzcyAgICBvclxuXHRcdFx0XHQvLyB0b2dnbGluZyBvZiBpbnNpZGUvb3V0c2lkZSBhdCBldmVyeSBzaW5nbGUhIGludGVyc2VjdGlvbiBwb2ludCBvZiBhbiBlZGdlXG5cdFx0XHRcdC8vICB3aXRoIHRoZSBob3Jpem9udGFsIGxpbmUgdGhyb3VnaCBpblB0LCBsZWZ0IG9mIGluUHRcblx0XHRcdFx0Ly8gIG5vdCBjb3VudGluZyBsb3dlclkgZW5kcG9pbnRzIG9mIGVkZ2VzIGFuZCB3aG9sZSBlZGdlcyBvbiB0aGF0IGxpbmVcblx0XHRcdFx0dmFyIGluc2lkZSA9IGZhbHNlO1xuXHRcdFx0XHRmb3IgKCB2YXIgcCA9IHBvbHlMZW4gLSAxLCBxID0gMDsgcSA8IHBvbHlMZW47IHAgPSBxICsrICkge1xuXG5cdFx0XHRcdFx0dmFyIGVkZ2VMb3dQdCAgPSBpblBvbHlnb25bIHAgXTtcblx0XHRcdFx0XHR2YXIgZWRnZUhpZ2hQdCA9IGluUG9seWdvblsgcSBdO1xuXG5cdFx0XHRcdFx0dmFyIGVkZ2VEeCA9IGVkZ2VIaWdoUHQueCAtIGVkZ2VMb3dQdC54O1xuXHRcdFx0XHRcdHZhciBlZGdlRHkgPSBlZGdlSGlnaFB0LnkgLSBlZGdlTG93UHQueTtcblxuXHRcdFx0XHRcdGlmICggTWF0aC5hYnMoIGVkZ2VEeSApID4gTnVtYmVyLkVQU0lMT04gKSB7XG5cblx0XHRcdFx0XHRcdC8vIG5vdCBwYXJhbGxlbFxuXHRcdFx0XHRcdFx0aWYgKCBlZGdlRHkgPCAwICkge1xuXG5cdFx0XHRcdFx0XHRcdGVkZ2VMb3dQdCAgPSBpblBvbHlnb25bIHEgXTsgZWRnZUR4ID0gLSBlZGdlRHg7XG5cdFx0XHRcdFx0XHRcdGVkZ2VIaWdoUHQgPSBpblBvbHlnb25bIHAgXTsgZWRnZUR5ID0gLSBlZGdlRHk7XG5cblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdGlmICggKCBpblB0LnkgPCBlZGdlTG93UHQueSApIHx8ICggaW5QdC55ID4gZWRnZUhpZ2hQdC55ICkgKSBcdFx0Y29udGludWU7XG5cblx0XHRcdFx0XHRcdGlmICggaW5QdC55ID09PSBlZGdlTG93UHQueSApIHtcblxuXHRcdFx0XHRcdFx0XHRpZiAoIGluUHQueCA9PT0gZWRnZUxvd1B0LnggKVx0XHRyZXR1cm5cdHRydWU7XHRcdC8vIGluUHQgaXMgb24gY29udG91ciA/XG5cdFx0XHRcdFx0XHRcdC8vIGNvbnRpbnVlO1x0XHRcdFx0Ly8gbm8gaW50ZXJzZWN0aW9uIG9yIGVkZ2VMb3dQdCA9PiBkb2Vzbid0IGNvdW50ICEhIVxuXG5cdFx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHRcdHZhciBwZXJwRWRnZSA9IGVkZ2VEeSAqICggaW5QdC54IC0gZWRnZUxvd1B0LnggKSAtIGVkZ2VEeCAqICggaW5QdC55IC0gZWRnZUxvd1B0LnkgKTtcblx0XHRcdFx0XHRcdFx0aWYgKCBwZXJwRWRnZSA9PT0gMCApXHRcdFx0XHRyZXR1cm5cdHRydWU7XHRcdC8vIGluUHQgaXMgb24gY29udG91ciA/XG5cdFx0XHRcdFx0XHRcdGlmICggcGVycEVkZ2UgPCAwICkgXHRcdFx0XHRjb250aW51ZTtcblx0XHRcdFx0XHRcdFx0aW5zaWRlID0gISBpbnNpZGU7XHRcdC8vIHRydWUgaW50ZXJzZWN0aW9uIGxlZnQgb2YgaW5QdFxuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0XHQvLyBwYXJhbGxlbCBvciBjb2xsaW5lYXJcblx0XHRcdFx0XHRcdGlmICggaW5QdC55ICE9PSBlZGdlTG93UHQueSApIFx0XHRjb250aW51ZTtcdFx0XHQvLyBwYXJhbGxlbFxuXHRcdFx0XHRcdFx0Ly8gZWRnZSBsaWVzIG9uIHRoZSBzYW1lIGhvcml6b250YWwgbGluZSBhcyBpblB0XG5cdFx0XHRcdFx0XHRpZiAoICggKCBlZGdlSGlnaFB0LnggPD0gaW5QdC54ICkgJiYgKCBpblB0LnggPD0gZWRnZUxvd1B0LnggKSApIHx8XG5cdFx0XHRcdFx0XHRcdCAoICggZWRnZUxvd1B0LnggPD0gaW5QdC54ICkgJiYgKCBpblB0LnggPD0gZWRnZUhpZ2hQdC54ICkgKSApXHRcdHJldHVyblx0dHJ1ZTtcdC8vIGluUHQ6IFBvaW50IG9uIGNvbnRvdXIgIVxuXHRcdFx0XHRcdFx0Ly8gY29udGludWU7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJldHVyblx0aW5zaWRlO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBpc0Nsb2NrV2lzZSA9IFNoYXBlVXRpbHMuaXNDbG9ja1dpc2U7XG5cblx0XHRcdHZhciBzdWJQYXRocyA9IHRoaXMuc3ViUGF0aHM7XG5cdFx0XHRpZiAoIHN1YlBhdGhzLmxlbmd0aCA9PT0gMCApIHJldHVybiBbXTtcblxuXHRcdFx0aWYgKCBub0hvbGVzID09PSB0cnVlIClcdHJldHVyblx0dG9TaGFwZXNOb0hvbGVzKCBzdWJQYXRocyApO1xuXG5cblx0XHRcdHZhciBzb2xpZCwgdG1wUGF0aCwgdG1wU2hhcGUsIHNoYXBlcyA9IFtdO1xuXG5cdFx0XHRpZiAoIHN1YlBhdGhzLmxlbmd0aCA9PT0gMSApIHtcblxuXHRcdFx0XHR0bXBQYXRoID0gc3ViUGF0aHNbIDAgXTtcblx0XHRcdFx0dG1wU2hhcGUgPSBuZXcgU2hhcGUoKTtcblx0XHRcdFx0dG1wU2hhcGUuY3VydmVzID0gdG1wUGF0aC5jdXJ2ZXM7XG5cdFx0XHRcdHNoYXBlcy5wdXNoKCB0bXBTaGFwZSApO1xuXHRcdFx0XHRyZXR1cm4gc2hhcGVzO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBob2xlc0ZpcnN0ID0gISBpc0Nsb2NrV2lzZSggc3ViUGF0aHNbIDAgXS5nZXRQb2ludHMoKSApO1xuXHRcdFx0aG9sZXNGaXJzdCA9IGlzQ0NXID8gISBob2xlc0ZpcnN0IDogaG9sZXNGaXJzdDtcblxuXHRcdFx0Ly8gY29uc29sZS5sb2coXCJIb2xlcyBmaXJzdFwiLCBob2xlc0ZpcnN0KTtcblxuXHRcdFx0dmFyIGJldHRlclNoYXBlSG9sZXMgPSBbXTtcblx0XHRcdHZhciBuZXdTaGFwZXMgPSBbXTtcblx0XHRcdHZhciBuZXdTaGFwZUhvbGVzID0gW107XG5cdFx0XHR2YXIgbWFpbklkeCA9IDA7XG5cdFx0XHR2YXIgdG1wUG9pbnRzO1xuXG5cdFx0XHRuZXdTaGFwZXNbIG1haW5JZHggXSA9IHVuZGVmaW5lZDtcblx0XHRcdG5ld1NoYXBlSG9sZXNbIG1haW5JZHggXSA9IFtdO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBzdWJQYXRocy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdHRtcFBhdGggPSBzdWJQYXRoc1sgaSBdO1xuXHRcdFx0XHR0bXBQb2ludHMgPSB0bXBQYXRoLmdldFBvaW50cygpO1xuXHRcdFx0XHRzb2xpZCA9IGlzQ2xvY2tXaXNlKCB0bXBQb2ludHMgKTtcblx0XHRcdFx0c29saWQgPSBpc0NDVyA/ICEgc29saWQgOiBzb2xpZDtcblxuXHRcdFx0XHRpZiAoIHNvbGlkICkge1xuXG5cdFx0XHRcdFx0aWYgKCAoICEgaG9sZXNGaXJzdCApICYmICggbmV3U2hhcGVzWyBtYWluSWR4IF0gKSApXHRtYWluSWR4ICsrO1xuXG5cdFx0XHRcdFx0bmV3U2hhcGVzWyBtYWluSWR4IF0gPSB7IHM6IG5ldyBTaGFwZSgpLCBwOiB0bXBQb2ludHMgfTtcblx0XHRcdFx0XHRuZXdTaGFwZXNbIG1haW5JZHggXS5zLmN1cnZlcyA9IHRtcFBhdGguY3VydmVzO1xuXG5cdFx0XHRcdFx0aWYgKCBob2xlc0ZpcnN0IClcdG1haW5JZHggKys7XG5cdFx0XHRcdFx0bmV3U2hhcGVIb2xlc1sgbWFpbklkeCBdID0gW107XG5cblx0XHRcdFx0XHQvL2NvbnNvbGUubG9nKCdjdycsIGkpO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRuZXdTaGFwZUhvbGVzWyBtYWluSWR4IF0ucHVzaCggeyBoOiB0bXBQYXRoLCBwOiB0bXBQb2ludHNbIDAgXSB9ICk7XG5cblx0XHRcdFx0XHQvL2NvbnNvbGUubG9nKCdjY3cnLCBpKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gb25seSBIb2xlcz8gLT4gcHJvYmFibHkgYWxsIFNoYXBlcyB3aXRoIHdyb25nIG9yaWVudGF0aW9uXG5cdFx0XHRpZiAoICEgbmV3U2hhcGVzWyAwIF0gKVx0cmV0dXJuXHR0b1NoYXBlc05vSG9sZXMoIHN1YlBhdGhzICk7XG5cblxuXHRcdFx0aWYgKCBuZXdTaGFwZXMubGVuZ3RoID4gMSApIHtcblxuXHRcdFx0XHR2YXIgYW1iaWd1b3VzID0gZmFsc2U7XG5cdFx0XHRcdHZhciB0b0NoYW5nZSA9IFtdO1xuXG5cdFx0XHRcdGZvciAoIHZhciBzSWR4ID0gMCwgc0xlbiA9IG5ld1NoYXBlcy5sZW5ndGg7IHNJZHggPCBzTGVuOyBzSWR4ICsrICkge1xuXG5cdFx0XHRcdFx0YmV0dGVyU2hhcGVIb2xlc1sgc0lkeCBdID0gW107XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGZvciAoIHZhciBzSWR4ID0gMCwgc0xlbiA9IG5ld1NoYXBlcy5sZW5ndGg7IHNJZHggPCBzTGVuOyBzSWR4ICsrICkge1xuXG5cdFx0XHRcdFx0dmFyIHNobyA9IG5ld1NoYXBlSG9sZXNbIHNJZHggXTtcblxuXHRcdFx0XHRcdGZvciAoIHZhciBoSWR4ID0gMDsgaElkeCA8IHNoby5sZW5ndGg7IGhJZHggKysgKSB7XG5cblx0XHRcdFx0XHRcdHZhciBobyA9IHNob1sgaElkeCBdO1xuXHRcdFx0XHRcdFx0dmFyIGhvbGVfdW5hc3NpZ25lZCA9IHRydWU7XG5cblx0XHRcdFx0XHRcdGZvciAoIHZhciBzMklkeCA9IDA7IHMySWR4IDwgbmV3U2hhcGVzLmxlbmd0aDsgczJJZHggKysgKSB7XG5cblx0XHRcdFx0XHRcdFx0aWYgKCBpc1BvaW50SW5zaWRlUG9seWdvbiggaG8ucCwgbmV3U2hhcGVzWyBzMklkeCBdLnAgKSApIHtcblxuXHRcdFx0XHRcdFx0XHRcdGlmICggc0lkeCAhPT0gczJJZHggKVx0dG9DaGFuZ2UucHVzaCggeyBmcm9tczogc0lkeCwgdG9zOiBzMklkeCwgaG9sZTogaElkeCB9ICk7XG5cdFx0XHRcdFx0XHRcdFx0aWYgKCBob2xlX3VuYXNzaWduZWQgKSB7XG5cblx0XHRcdFx0XHRcdFx0XHRcdGhvbGVfdW5hc3NpZ25lZCA9IGZhbHNlO1xuXHRcdFx0XHRcdFx0XHRcdFx0YmV0dGVyU2hhcGVIb2xlc1sgczJJZHggXS5wdXNoKCBobyApO1xuXG5cdFx0XHRcdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0XHRcdFx0YW1iaWd1b3VzID0gdHJ1ZTtcblxuXHRcdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdGlmICggaG9sZV91bmFzc2lnbmVkICkge1xuXG5cdFx0XHRcdFx0XHRcdGJldHRlclNoYXBlSG9sZXNbIHNJZHggXS5wdXNoKCBobyApO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXHRcdFx0XHQvLyBjb25zb2xlLmxvZyhcImFtYmlndW91czogXCIsIGFtYmlndW91cyk7XG5cdFx0XHRcdGlmICggdG9DaGFuZ2UubGVuZ3RoID4gMCApIHtcblxuXHRcdFx0XHRcdC8vIGNvbnNvbGUubG9nKFwidG8gY2hhbmdlOiBcIiwgdG9DaGFuZ2UpO1xuXHRcdFx0XHRcdGlmICggISBhbWJpZ3VvdXMgKVx0bmV3U2hhcGVIb2xlcyA9IGJldHRlclNoYXBlSG9sZXM7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdHZhciB0bXBIb2xlcztcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwLCBpbCA9IG5ld1NoYXBlcy5sZW5ndGg7IGkgPCBpbDsgaSArKyApIHtcblxuXHRcdFx0XHR0bXBTaGFwZSA9IG5ld1NoYXBlc1sgaSBdLnM7XG5cdFx0XHRcdHNoYXBlcy5wdXNoKCB0bXBTaGFwZSApO1xuXHRcdFx0XHR0bXBIb2xlcyA9IG5ld1NoYXBlSG9sZXNbIGkgXTtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaiA9IDAsIGpsID0gdG1wSG9sZXMubGVuZ3RoOyBqIDwgamw7IGogKysgKSB7XG5cblx0XHRcdFx0XHR0bXBTaGFwZS5ob2xlcy5wdXNoKCB0bXBIb2xlc1sgaiBdLmggKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0Ly9jb25zb2xlLmxvZyhcInNoYXBlXCIsIHNoYXBlcyk7XG5cblx0XHRcdHJldHVybiBzaGFwZXM7XG5cblx0XHR9XG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3Igeno4NSAvIGh0dHA6Ly93d3cubGFiNGdhbWVzLm5ldC96ejg1L2Jsb2dcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gRm9udCggZGF0YSApIHtcblxuXHRcdHRoaXMuZGF0YSA9IGRhdGE7XG5cblx0fVxuXG5cdE9iamVjdC5hc3NpZ24oIEZvbnQucHJvdG90eXBlLCB7XG5cblx0XHRpc0ZvbnQ6IHRydWUsXG5cblx0XHRnZW5lcmF0ZVNoYXBlczogZnVuY3Rpb24gKCB0ZXh0LCBzaXplLCBkaXZpc2lvbnMgKSB7XG5cblx0XHRcdGZ1bmN0aW9uIGNyZWF0ZVBhdGhzKCB0ZXh0ICkge1xuXG5cdFx0XHRcdHZhciBjaGFycyA9IFN0cmluZyggdGV4dCApLnNwbGl0KCAnJyApO1xuXHRcdFx0XHR2YXIgc2NhbGUgPSBzaXplIC8gZGF0YS5yZXNvbHV0aW9uO1xuXHRcdFx0XHR2YXIgb2Zmc2V0ID0gMDtcblxuXHRcdFx0XHR2YXIgcGF0aHMgPSBbXTtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBjaGFycy5sZW5ndGg7IGkgKysgKSB7XG5cblx0XHRcdFx0XHR2YXIgcmV0ID0gY3JlYXRlUGF0aCggY2hhcnNbIGkgXSwgc2NhbGUsIG9mZnNldCApO1xuXHRcdFx0XHRcdG9mZnNldCArPSByZXQub2Zmc2V0O1xuXG5cdFx0XHRcdFx0cGF0aHMucHVzaCggcmV0LnBhdGggKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuIHBhdGhzO1xuXG5cdFx0XHR9XG5cblx0XHRcdGZ1bmN0aW9uIGNyZWF0ZVBhdGgoIGMsIHNjYWxlLCBvZmZzZXQgKSB7XG5cblx0XHRcdFx0dmFyIGdseXBoID0gZGF0YS5nbHlwaHNbIGMgXSB8fCBkYXRhLmdseXBoc1sgJz8nIF07XG5cblx0XHRcdFx0aWYgKCAhIGdseXBoICkgcmV0dXJuO1xuXG5cdFx0XHRcdHZhciBwYXRoID0gbmV3IFNoYXBlUGF0aCgpO1xuXG5cdFx0XHRcdHZhciBwdHMgPSBbXSwgYjIgPSBTaGFwZVV0aWxzLmIyLCBiMyA9IFNoYXBlVXRpbHMuYjM7XG5cdFx0XHRcdHZhciB4LCB5LCBjcHgsIGNweSwgY3B4MCwgY3B5MCwgY3B4MSwgY3B5MSwgY3B4MiwgY3B5MiwgbGFzdGU7XG5cblx0XHRcdFx0aWYgKCBnbHlwaC5vICkge1xuXG5cdFx0XHRcdFx0dmFyIG91dGxpbmUgPSBnbHlwaC5fY2FjaGVkT3V0bGluZSB8fCAoIGdseXBoLl9jYWNoZWRPdXRsaW5lID0gZ2x5cGguby5zcGxpdCggJyAnICkgKTtcblxuXHRcdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IG91dGxpbmUubGVuZ3RoOyBpIDwgbDsgKSB7XG5cblx0XHRcdFx0XHRcdHZhciBhY3Rpb24gPSBvdXRsaW5lWyBpICsrIF07XG5cblx0XHRcdFx0XHRcdHN3aXRjaCAoIGFjdGlvbiApIHtcblxuXHRcdFx0XHRcdFx0XHRjYXNlICdtJzogLy8gbW92ZVRvXG5cblx0XHRcdFx0XHRcdFx0XHR4ID0gb3V0bGluZVsgaSArKyBdICogc2NhbGUgKyBvZmZzZXQ7XG5cdFx0XHRcdFx0XHRcdFx0eSA9IG91dGxpbmVbIGkgKysgXSAqIHNjYWxlO1xuXG5cdFx0XHRcdFx0XHRcdFx0cGF0aC5tb3ZlVG8oIHgsIHkgKTtcblxuXHRcdFx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0XHRcdGNhc2UgJ2wnOiAvLyBsaW5lVG9cblxuXHRcdFx0XHRcdFx0XHRcdHggPSBvdXRsaW5lWyBpICsrIF0gKiBzY2FsZSArIG9mZnNldDtcblx0XHRcdFx0XHRcdFx0XHR5ID0gb3V0bGluZVsgaSArKyBdICogc2NhbGU7XG5cblx0XHRcdFx0XHRcdFx0XHRwYXRoLmxpbmVUbyggeCwgeSApO1xuXG5cdFx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRcdFx0Y2FzZSAncSc6IC8vIHF1YWRyYXRpY0N1cnZlVG9cblxuXHRcdFx0XHRcdFx0XHRcdGNweCAgPSBvdXRsaW5lWyBpICsrIF0gKiBzY2FsZSArIG9mZnNldDtcblx0XHRcdFx0XHRcdFx0XHRjcHkgID0gb3V0bGluZVsgaSArKyBdICogc2NhbGU7XG5cdFx0XHRcdFx0XHRcdFx0Y3B4MSA9IG91dGxpbmVbIGkgKysgXSAqIHNjYWxlICsgb2Zmc2V0O1xuXHRcdFx0XHRcdFx0XHRcdGNweTEgPSBvdXRsaW5lWyBpICsrIF0gKiBzY2FsZTtcblxuXHRcdFx0XHRcdFx0XHRcdHBhdGgucXVhZHJhdGljQ3VydmVUbyggY3B4MSwgY3B5MSwgY3B4LCBjcHkgKTtcblxuXHRcdFx0XHRcdFx0XHRcdGxhc3RlID0gcHRzWyBwdHMubGVuZ3RoIC0gMSBdO1xuXG5cdFx0XHRcdFx0XHRcdFx0aWYgKCBsYXN0ZSApIHtcblxuXHRcdFx0XHRcdFx0XHRcdFx0Y3B4MCA9IGxhc3RlLng7XG5cdFx0XHRcdFx0XHRcdFx0XHRjcHkwID0gbGFzdGUueTtcblxuXHRcdFx0XHRcdFx0XHRcdFx0Zm9yICggdmFyIGkyID0gMTsgaTIgPD0gZGl2aXNpb25zOyBpMiArKyApIHtcblxuXHRcdFx0XHRcdFx0XHRcdFx0XHR2YXIgdCA9IGkyIC8gZGl2aXNpb25zO1xuXHRcdFx0XHRcdFx0XHRcdFx0XHRiMiggdCwgY3B4MCwgY3B4MSwgY3B4ICk7XG5cdFx0XHRcdFx0XHRcdFx0XHRcdGIyKCB0LCBjcHkwLCBjcHkxLCBjcHkgKTtcblxuXHRcdFx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRcdFx0Y2FzZSAnYic6IC8vIGJlemllckN1cnZlVG9cblxuXHRcdFx0XHRcdFx0XHRcdGNweCAgPSBvdXRsaW5lWyBpICsrIF0gKiBzY2FsZSArIG9mZnNldDtcblx0XHRcdFx0XHRcdFx0XHRjcHkgID0gb3V0bGluZVsgaSArKyBdICogc2NhbGU7XG5cdFx0XHRcdFx0XHRcdFx0Y3B4MSA9IG91dGxpbmVbIGkgKysgXSAqIHNjYWxlICsgb2Zmc2V0O1xuXHRcdFx0XHRcdFx0XHRcdGNweTEgPSBvdXRsaW5lWyBpICsrIF0gKiBzY2FsZTtcblx0XHRcdFx0XHRcdFx0XHRjcHgyID0gb3V0bGluZVsgaSArKyBdICogc2NhbGUgKyBvZmZzZXQ7XG5cdFx0XHRcdFx0XHRcdFx0Y3B5MiA9IG91dGxpbmVbIGkgKysgXSAqIHNjYWxlO1xuXG5cdFx0XHRcdFx0XHRcdFx0cGF0aC5iZXppZXJDdXJ2ZVRvKCBjcHgxLCBjcHkxLCBjcHgyLCBjcHkyLCBjcHgsIGNweSApO1xuXG5cdFx0XHRcdFx0XHRcdFx0bGFzdGUgPSBwdHNbIHB0cy5sZW5ndGggLSAxIF07XG5cblx0XHRcdFx0XHRcdFx0XHRpZiAoIGxhc3RlICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0XHRjcHgwID0gbGFzdGUueDtcblx0XHRcdFx0XHRcdFx0XHRcdGNweTAgPSBsYXN0ZS55O1xuXG5cdFx0XHRcdFx0XHRcdFx0XHRmb3IgKCB2YXIgaTIgPSAxOyBpMiA8PSBkaXZpc2lvbnM7IGkyICsrICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0XHRcdHZhciB0ID0gaTIgLyBkaXZpc2lvbnM7XG5cdFx0XHRcdFx0XHRcdFx0XHRcdGIzKCB0LCBjcHgwLCBjcHgxLCBjcHgyLCBjcHggKTtcblx0XHRcdFx0XHRcdFx0XHRcdFx0YjMoIHQsIGNweTAsIGNweTEsIGNweTIsIGNweSApO1xuXG5cdFx0XHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdFx0XHRicmVhaztcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRyZXR1cm4geyBvZmZzZXQ6IGdseXBoLmhhICogc2NhbGUsIHBhdGg6IHBhdGggfTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvL1xuXG5cdFx0XHRpZiAoIHNpemUgPT09IHVuZGVmaW5lZCApIHNpemUgPSAxMDA7XG5cdFx0XHRpZiAoIGRpdmlzaW9ucyA9PT0gdW5kZWZpbmVkICkgZGl2aXNpb25zID0gNDtcblxuXHRcdFx0dmFyIGRhdGEgPSB0aGlzLmRhdGE7XG5cblx0XHRcdHZhciBwYXRocyA9IGNyZWF0ZVBhdGhzKCB0ZXh0ICk7XG5cdFx0XHR2YXIgc2hhcGVzID0gW107XG5cblx0XHRcdGZvciAoIHZhciBwID0gMCwgcGwgPSBwYXRocy5sZW5ndGg7IHAgPCBwbDsgcCArKyApIHtcblxuXHRcdFx0XHRBcnJheS5wcm90b3R5cGUucHVzaC5hcHBseSggc2hhcGVzLCBwYXRoc1sgcCBdLnRvU2hhcGVzKCkgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gc2hhcGVzO1xuXG5cdFx0fVxuXG5cdH0gKTtcblxuXHQvKipcblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gRm9udExvYWRlciggbWFuYWdlciApIHtcblxuXHRcdHRoaXMubWFuYWdlciA9ICggbWFuYWdlciAhPT0gdW5kZWZpbmVkICkgPyBtYW5hZ2VyIDogRGVmYXVsdExvYWRpbmdNYW5hZ2VyO1xuXG5cdH1cblxuXHRPYmplY3QuYXNzaWduKCBGb250TG9hZGVyLnByb3RvdHlwZSwge1xuXG5cdFx0bG9hZDogZnVuY3Rpb24gKCB1cmwsIG9uTG9hZCwgb25Qcm9ncmVzcywgb25FcnJvciApIHtcblxuXHRcdFx0dmFyIHNjb3BlID0gdGhpcztcblxuXHRcdFx0dmFyIGxvYWRlciA9IG5ldyBGaWxlTG9hZGVyKCB0aGlzLm1hbmFnZXIgKTtcblx0XHRcdGxvYWRlci5sb2FkKCB1cmwsIGZ1bmN0aW9uICggdGV4dCApIHtcblxuXHRcdFx0XHR2YXIganNvbjtcblxuXHRcdFx0XHR0cnkge1xuXG5cdFx0XHRcdFx0anNvbiA9IEpTT04ucGFyc2UoIHRleHQgKTtcblxuXHRcdFx0XHR9IGNhdGNoICggZSApIHtcblxuXHRcdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkZvbnRMb2FkZXI6IHR5cGVmYWNlLmpzIHN1cHBvcnQgaXMgYmVpbmcgZGVwcmVjYXRlZC4gVXNlIHR5cGVmYWNlLmpzb24gaW5zdGVhZC4nICk7XG5cdFx0XHRcdFx0anNvbiA9IEpTT04ucGFyc2UoIHRleHQuc3Vic3RyaW5nKCA2NSwgdGV4dC5sZW5ndGggLSAyICkgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0dmFyIGZvbnQgPSBzY29wZS5wYXJzZSgganNvbiApO1xuXG5cdFx0XHRcdGlmICggb25Mb2FkICkgb25Mb2FkKCBmb250ICk7XG5cblx0XHRcdH0sIG9uUHJvZ3Jlc3MsIG9uRXJyb3IgKTtcblxuXHRcdH0sXG5cblx0XHRwYXJzZTogZnVuY3Rpb24gKCBqc29uICkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IEZvbnQoIGpzb24gKTtcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0dmFyIGNvbnRleHQ7XG5cblx0dmFyIEF1ZGlvQ29udGV4dCA9IHtcblxuXHRcdGdldENvbnRleHQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0aWYgKCBjb250ZXh0ID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0Y29udGV4dCA9IG5ldyAoIHdpbmRvdy5BdWRpb0NvbnRleHQgfHwgd2luZG93LndlYmtpdEF1ZGlvQ29udGV4dCApKCk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIGNvbnRleHQ7XG5cblx0XHR9LFxuXG5cdFx0c2V0Q29udGV4dDogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0Y29udGV4dCA9IHZhbHVlO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgUmVlY2UgQWFyb24gTGVjcml2YWluIC8gaHR0cDovL3JlZWNlbm90ZXMuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBBdWRpb0xvYWRlciggbWFuYWdlciApIHtcblxuXHRcdHRoaXMubWFuYWdlciA9ICggbWFuYWdlciAhPT0gdW5kZWZpbmVkICkgPyBtYW5hZ2VyIDogRGVmYXVsdExvYWRpbmdNYW5hZ2VyO1xuXG5cdH1cblxuXHRPYmplY3QuYXNzaWduKCBBdWRpb0xvYWRlci5wcm90b3R5cGUsIHtcblxuXHRcdGxvYWQ6IGZ1bmN0aW9uICggdXJsLCBvbkxvYWQsIG9uUHJvZ3Jlc3MsIG9uRXJyb3IgKSB7XG5cblx0XHRcdHZhciBsb2FkZXIgPSBuZXcgRmlsZUxvYWRlciggdGhpcy5tYW5hZ2VyICk7XG5cdFx0XHRsb2FkZXIuc2V0UmVzcG9uc2VUeXBlKCAnYXJyYXlidWZmZXInICk7XG5cdFx0XHRsb2FkZXIubG9hZCggdXJsLCBmdW5jdGlvbiAoIGJ1ZmZlciApIHtcblxuXHRcdFx0XHR2YXIgY29udGV4dCA9IEF1ZGlvQ29udGV4dC5nZXRDb250ZXh0KCk7XG5cblx0XHRcdFx0Y29udGV4dC5kZWNvZGVBdWRpb0RhdGEoIGJ1ZmZlciwgZnVuY3Rpb24gKCBhdWRpb0J1ZmZlciApIHtcblxuXHRcdFx0XHRcdG9uTG9hZCggYXVkaW9CdWZmZXIgKTtcblxuXHRcdFx0XHR9ICk7XG5cblx0XHRcdH0sIG9uUHJvZ3Jlc3MsIG9uRXJyb3IgKTtcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgYWJlbG5hdGlvbiAvIGh0dHA6Ly9naXRodWIuY29tL2FiZWxuYXRpb25cblx0ICovXG5cblx0ZnVuY3Rpb24gUmVjdEFyZWFMaWdodCAoIGNvbG9yLCBpbnRlbnNpdHksIHdpZHRoLCBoZWlnaHQgKSB7XG5cblx0XHRMaWdodC5jYWxsKCB0aGlzLCBjb2xvciwgaW50ZW5zaXR5ICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnUmVjdEFyZWFMaWdodCc7XG5cblx0XHR0aGlzLnBvc2l0aW9uLnNldCggMCwgMSwgMCApO1xuXHRcdHRoaXMudXBkYXRlTWF0cml4KCk7XG5cblx0XHR0aGlzLndpZHRoID0gKCB3aWR0aCAhPT0gdW5kZWZpbmVkICkgPyB3aWR0aCA6IDEwO1xuXHRcdHRoaXMuaGVpZ2h0ID0gKCBoZWlnaHQgIT09IHVuZGVmaW5lZCApID8gaGVpZ2h0IDogMTA7XG5cblx0XHQvLyBUT0RPIChhYmVsbmF0aW9uKTogZGlzdGFuY2UvZGVjYXlcblxuXHRcdC8vIFRPRE8gKGFiZWxuYXRpb24pOiB1cGRhdGUgbWV0aG9kIGZvciBSZWN0QXJlYUxpZ2h0IHRvIHVwZGF0ZSB0cmFuc2Zvcm0gdG8gbG9va2F0IHRhcmdldFxuXG5cdFx0Ly8gVE9ETyAoYWJlbG5hdGlvbik6IHNoYWRvd3Ncblx0XHQvLyB0aGlzLnNoYWRvdyA9IG5ldyBUSFJFRS5SZWN0QXJlYUxpZ2h0U2hhZG93KCBuZXcgVEhSRUUuUGVyc3BlY3RpdmVDYW1lcmEoIDkwLCAxLCAwLjUsIDUwMCApICk7XG5cblx0fVxuXG5cdC8vIFRPRE8gKGFiZWxuYXRpb24pOiBSZWN0QXJlYUxpZ2h0IHVwZGF0ZSB3aGVuIGxpZ2h0IHNoYXBlIGlzIGNoYW5nZWRcblx0UmVjdEFyZWFMaWdodC5wcm90b3R5cGUgPSBPYmplY3QuYXNzaWduKCBPYmplY3QuY3JlYXRlKCBMaWdodC5wcm90b3R5cGUgKSwge1xuXG5cdFx0Y29uc3RydWN0b3I6IFJlY3RBcmVhTGlnaHQsXG5cblx0XHRpc1JlY3RBcmVhTGlnaHQ6IHRydWUsXG5cblx0XHRjb3B5OiBmdW5jdGlvbiAoIHNvdXJjZSApIHtcblxuXHRcdFx0TGlnaHQucHJvdG90eXBlLmNvcHkuY2FsbCggdGhpcywgc291cmNlICk7XG5cblx0XHRcdHRoaXMud2lkdGggPSBzb3VyY2Uud2lkdGg7XG5cdFx0XHR0aGlzLmhlaWdodCA9IHNvdXJjZS5oZWlnaHQ7XG5cblx0XHRcdC8vIHRoaXMuc2hhZG93ID0gc291cmNlLnNoYWRvdy5jbG9uZSgpO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFN0ZXJlb0NhbWVyYSgpIHtcblxuXHRcdHRoaXMudHlwZSA9ICdTdGVyZW9DYW1lcmEnO1xuXG5cdFx0dGhpcy5hc3BlY3QgPSAxO1xuXG5cdFx0dGhpcy5leWVTZXAgPSAwLjA2NDtcblxuXHRcdHRoaXMuY2FtZXJhTCA9IG5ldyBQZXJzcGVjdGl2ZUNhbWVyYSgpO1xuXHRcdHRoaXMuY2FtZXJhTC5sYXllcnMuZW5hYmxlKCAxICk7XG5cdFx0dGhpcy5jYW1lcmFMLm1hdHJpeEF1dG9VcGRhdGUgPSBmYWxzZTtcblxuXHRcdHRoaXMuY2FtZXJhUiA9IG5ldyBQZXJzcGVjdGl2ZUNhbWVyYSgpO1xuXHRcdHRoaXMuY2FtZXJhUi5sYXllcnMuZW5hYmxlKCAyICk7XG5cdFx0dGhpcy5jYW1lcmFSLm1hdHJpeEF1dG9VcGRhdGUgPSBmYWxzZTtcblxuXHR9XG5cblx0T2JqZWN0LmFzc2lnbiggU3RlcmVvQ2FtZXJhLnByb3RvdHlwZSwge1xuXG5cdFx0dXBkYXRlOiAoIGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIGluc3RhbmNlLCBmb2N1cywgZm92LCBhc3BlY3QsIG5lYXIsIGZhciwgem9vbTtcblxuXHRcdFx0dmFyIGV5ZVJpZ2h0ID0gbmV3IE1hdHJpeDQoKTtcblx0XHRcdHZhciBleWVMZWZ0ID0gbmV3IE1hdHJpeDQoKTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIHVwZGF0ZSggY2FtZXJhICkge1xuXG5cdFx0XHRcdHZhciBuZWVkc1VwZGF0ZSA9IGluc3RhbmNlICE9PSB0aGlzIHx8IGZvY3VzICE9PSBjYW1lcmEuZm9jdXMgfHwgZm92ICE9PSBjYW1lcmEuZm92IHx8XG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdGFzcGVjdCAhPT0gY2FtZXJhLmFzcGVjdCAqIHRoaXMuYXNwZWN0IHx8IG5lYXIgIT09IGNhbWVyYS5uZWFyIHx8XG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdGZhciAhPT0gY2FtZXJhLmZhciB8fCB6b29tICE9PSBjYW1lcmEuem9vbTtcblxuXHRcdFx0XHRpZiAoIG5lZWRzVXBkYXRlICkge1xuXG5cdFx0XHRcdFx0aW5zdGFuY2UgPSB0aGlzO1xuXHRcdFx0XHRcdGZvY3VzID0gY2FtZXJhLmZvY3VzO1xuXHRcdFx0XHRcdGZvdiA9IGNhbWVyYS5mb3Y7XG5cdFx0XHRcdFx0YXNwZWN0ID0gY2FtZXJhLmFzcGVjdCAqIHRoaXMuYXNwZWN0O1xuXHRcdFx0XHRcdG5lYXIgPSBjYW1lcmEubmVhcjtcblx0XHRcdFx0XHRmYXIgPSBjYW1lcmEuZmFyO1xuXHRcdFx0XHRcdHpvb20gPSBjYW1lcmEuem9vbTtcblxuXHRcdFx0XHRcdC8vIE9mZi1heGlzIHN0ZXJlb3Njb3BpYyBlZmZlY3QgYmFzZWQgb25cblx0XHRcdFx0XHQvLyBodHRwOi8vcGF1bGJvdXJrZS5uZXQvc3RlcmVvZ3JhcGhpY3Mvc3RlcmVvcmVuZGVyL1xuXG5cdFx0XHRcdFx0dmFyIHByb2plY3Rpb25NYXRyaXggPSBjYW1lcmEucHJvamVjdGlvbk1hdHJpeC5jbG9uZSgpO1xuXHRcdFx0XHRcdHZhciBleWVTZXAgPSB0aGlzLmV5ZVNlcCAvIDI7XG5cdFx0XHRcdFx0dmFyIGV5ZVNlcE9uUHJvamVjdGlvbiA9IGV5ZVNlcCAqIG5lYXIgLyBmb2N1cztcblx0XHRcdFx0XHR2YXIgeW1heCA9ICggbmVhciAqIE1hdGgudGFuKCBfTWF0aC5ERUcyUkFEICogZm92ICogMC41ICkgKSAvIHpvb207XG5cdFx0XHRcdFx0dmFyIHhtaW4sIHhtYXg7XG5cblx0XHRcdFx0XHQvLyB0cmFuc2xhdGUgeE9mZnNldFxuXG5cdFx0XHRcdFx0ZXllTGVmdC5lbGVtZW50c1sgMTIgXSA9IC0gZXllU2VwO1xuXHRcdFx0XHRcdGV5ZVJpZ2h0LmVsZW1lbnRzWyAxMiBdID0gZXllU2VwO1xuXG5cdFx0XHRcdFx0Ly8gZm9yIGxlZnQgZXllXG5cblx0XHRcdFx0XHR4bWluID0gLSB5bWF4ICogYXNwZWN0ICsgZXllU2VwT25Qcm9qZWN0aW9uO1xuXHRcdFx0XHRcdHhtYXggPSB5bWF4ICogYXNwZWN0ICsgZXllU2VwT25Qcm9qZWN0aW9uO1xuXG5cdFx0XHRcdFx0cHJvamVjdGlvbk1hdHJpeC5lbGVtZW50c1sgMCBdID0gMiAqIG5lYXIgLyAoIHhtYXggLSB4bWluICk7XG5cdFx0XHRcdFx0cHJvamVjdGlvbk1hdHJpeC5lbGVtZW50c1sgOCBdID0gKCB4bWF4ICsgeG1pbiApIC8gKCB4bWF4IC0geG1pbiApO1xuXG5cdFx0XHRcdFx0dGhpcy5jYW1lcmFMLnByb2plY3Rpb25NYXRyaXguY29weSggcHJvamVjdGlvbk1hdHJpeCApO1xuXG5cdFx0XHRcdFx0Ly8gZm9yIHJpZ2h0IGV5ZVxuXG5cdFx0XHRcdFx0eG1pbiA9IC0geW1heCAqIGFzcGVjdCAtIGV5ZVNlcE9uUHJvamVjdGlvbjtcblx0XHRcdFx0XHR4bWF4ID0geW1heCAqIGFzcGVjdCAtIGV5ZVNlcE9uUHJvamVjdGlvbjtcblxuXHRcdFx0XHRcdHByb2plY3Rpb25NYXRyaXguZWxlbWVudHNbIDAgXSA9IDIgKiBuZWFyIC8gKCB4bWF4IC0geG1pbiApO1xuXHRcdFx0XHRcdHByb2plY3Rpb25NYXRyaXguZWxlbWVudHNbIDggXSA9ICggeG1heCArIHhtaW4gKSAvICggeG1heCAtIHhtaW4gKTtcblxuXHRcdFx0XHRcdHRoaXMuY2FtZXJhUi5wcm9qZWN0aW9uTWF0cml4LmNvcHkoIHByb2plY3Rpb25NYXRyaXggKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0dGhpcy5jYW1lcmFMLm1hdHJpeFdvcmxkLmNvcHkoIGNhbWVyYS5tYXRyaXhXb3JsZCApLm11bHRpcGx5KCBleWVMZWZ0ICk7XG5cdFx0XHRcdHRoaXMuY2FtZXJhUi5tYXRyaXhXb3JsZC5jb3B5KCBjYW1lcmEubWF0cml4V29ybGQgKS5tdWx0aXBseSggZXllUmlnaHQgKTtcblxuXHRcdFx0fTtcblxuXHRcdH0gKSgpXG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBDYW1lcmEgZm9yIHJlbmRlcmluZyBjdWJlIG1hcHNcblx0ICpcdC0gcmVuZGVycyBzY2VuZSBpbnRvIGF4aXMtYWxpZ25lZCBjdWJlXG5cdCAqXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEN1YmVDYW1lcmEoIG5lYXIsIGZhciwgY3ViZVJlc29sdXRpb24gKSB7XG5cblx0XHRPYmplY3QzRC5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnQ3ViZUNhbWVyYSc7XG5cblx0XHR2YXIgZm92ID0gOTAsIGFzcGVjdCA9IDE7XG5cblx0XHR2YXIgY2FtZXJhUFggPSBuZXcgUGVyc3BlY3RpdmVDYW1lcmEoIGZvdiwgYXNwZWN0LCBuZWFyLCBmYXIgKTtcblx0XHRjYW1lcmFQWC51cC5zZXQoIDAsIC0gMSwgMCApO1xuXHRcdGNhbWVyYVBYLmxvb2tBdCggbmV3IFZlY3RvcjMoIDEsIDAsIDAgKSApO1xuXHRcdHRoaXMuYWRkKCBjYW1lcmFQWCApO1xuXG5cdFx0dmFyIGNhbWVyYU5YID0gbmV3IFBlcnNwZWN0aXZlQ2FtZXJhKCBmb3YsIGFzcGVjdCwgbmVhciwgZmFyICk7XG5cdFx0Y2FtZXJhTlgudXAuc2V0KCAwLCAtIDEsIDAgKTtcblx0XHRjYW1lcmFOWC5sb29rQXQoIG5ldyBWZWN0b3IzKCAtIDEsIDAsIDAgKSApO1xuXHRcdHRoaXMuYWRkKCBjYW1lcmFOWCApO1xuXG5cdFx0dmFyIGNhbWVyYVBZID0gbmV3IFBlcnNwZWN0aXZlQ2FtZXJhKCBmb3YsIGFzcGVjdCwgbmVhciwgZmFyICk7XG5cdFx0Y2FtZXJhUFkudXAuc2V0KCAwLCAwLCAxICk7XG5cdFx0Y2FtZXJhUFkubG9va0F0KCBuZXcgVmVjdG9yMyggMCwgMSwgMCApICk7XG5cdFx0dGhpcy5hZGQoIGNhbWVyYVBZICk7XG5cblx0XHR2YXIgY2FtZXJhTlkgPSBuZXcgUGVyc3BlY3RpdmVDYW1lcmEoIGZvdiwgYXNwZWN0LCBuZWFyLCBmYXIgKTtcblx0XHRjYW1lcmFOWS51cC5zZXQoIDAsIDAsIC0gMSApO1xuXHRcdGNhbWVyYU5ZLmxvb2tBdCggbmV3IFZlY3RvcjMoIDAsIC0gMSwgMCApICk7XG5cdFx0dGhpcy5hZGQoIGNhbWVyYU5ZICk7XG5cblx0XHR2YXIgY2FtZXJhUFogPSBuZXcgUGVyc3BlY3RpdmVDYW1lcmEoIGZvdiwgYXNwZWN0LCBuZWFyLCBmYXIgKTtcblx0XHRjYW1lcmFQWi51cC5zZXQoIDAsIC0gMSwgMCApO1xuXHRcdGNhbWVyYVBaLmxvb2tBdCggbmV3IFZlY3RvcjMoIDAsIDAsIDEgKSApO1xuXHRcdHRoaXMuYWRkKCBjYW1lcmFQWiApO1xuXG5cdFx0dmFyIGNhbWVyYU5aID0gbmV3IFBlcnNwZWN0aXZlQ2FtZXJhKCBmb3YsIGFzcGVjdCwgbmVhciwgZmFyICk7XG5cdFx0Y2FtZXJhTloudXAuc2V0KCAwLCAtIDEsIDAgKTtcblx0XHRjYW1lcmFOWi5sb29rQXQoIG5ldyBWZWN0b3IzKCAwLCAwLCAtIDEgKSApO1xuXHRcdHRoaXMuYWRkKCBjYW1lcmFOWiApO1xuXG5cdFx0dmFyIG9wdGlvbnMgPSB7IGZvcm1hdDogUkdCRm9ybWF0LCBtYWdGaWx0ZXI6IExpbmVhckZpbHRlciwgbWluRmlsdGVyOiBMaW5lYXJGaWx0ZXIgfTtcblxuXHRcdHRoaXMucmVuZGVyVGFyZ2V0ID0gbmV3IFdlYkdMUmVuZGVyVGFyZ2V0Q3ViZSggY3ViZVJlc29sdXRpb24sIGN1YmVSZXNvbHV0aW9uLCBvcHRpb25zICk7XG5cblx0XHR0aGlzLnVwZGF0ZUN1YmVNYXAgPSBmdW5jdGlvbiAoIHJlbmRlcmVyLCBzY2VuZSApIHtcblxuXHRcdFx0aWYgKCB0aGlzLnBhcmVudCA9PT0gbnVsbCApIHRoaXMudXBkYXRlTWF0cml4V29ybGQoKTtcblxuXHRcdFx0dmFyIHJlbmRlclRhcmdldCA9IHRoaXMucmVuZGVyVGFyZ2V0O1xuXHRcdFx0dmFyIGdlbmVyYXRlTWlwbWFwcyA9IHJlbmRlclRhcmdldC50ZXh0dXJlLmdlbmVyYXRlTWlwbWFwcztcblxuXHRcdFx0cmVuZGVyVGFyZ2V0LnRleHR1cmUuZ2VuZXJhdGVNaXBtYXBzID0gZmFsc2U7XG5cblx0XHRcdHJlbmRlclRhcmdldC5hY3RpdmVDdWJlRmFjZSA9IDA7XG5cdFx0XHRyZW5kZXJlci5yZW5kZXIoIHNjZW5lLCBjYW1lcmFQWCwgcmVuZGVyVGFyZ2V0ICk7XG5cblx0XHRcdHJlbmRlclRhcmdldC5hY3RpdmVDdWJlRmFjZSA9IDE7XG5cdFx0XHRyZW5kZXJlci5yZW5kZXIoIHNjZW5lLCBjYW1lcmFOWCwgcmVuZGVyVGFyZ2V0ICk7XG5cblx0XHRcdHJlbmRlclRhcmdldC5hY3RpdmVDdWJlRmFjZSA9IDI7XG5cdFx0XHRyZW5kZXJlci5yZW5kZXIoIHNjZW5lLCBjYW1lcmFQWSwgcmVuZGVyVGFyZ2V0ICk7XG5cblx0XHRcdHJlbmRlclRhcmdldC5hY3RpdmVDdWJlRmFjZSA9IDM7XG5cdFx0XHRyZW5kZXJlci5yZW5kZXIoIHNjZW5lLCBjYW1lcmFOWSwgcmVuZGVyVGFyZ2V0ICk7XG5cblx0XHRcdHJlbmRlclRhcmdldC5hY3RpdmVDdWJlRmFjZSA9IDQ7XG5cdFx0XHRyZW5kZXJlci5yZW5kZXIoIHNjZW5lLCBjYW1lcmFQWiwgcmVuZGVyVGFyZ2V0ICk7XG5cblx0XHRcdHJlbmRlclRhcmdldC50ZXh0dXJlLmdlbmVyYXRlTWlwbWFwcyA9IGdlbmVyYXRlTWlwbWFwcztcblxuXHRcdFx0cmVuZGVyVGFyZ2V0LmFjdGl2ZUN1YmVGYWNlID0gNTtcblx0XHRcdHJlbmRlcmVyLnJlbmRlciggc2NlbmUsIGNhbWVyYU5aLCByZW5kZXJUYXJnZXQgKTtcblxuXHRcdFx0cmVuZGVyZXIuc2V0UmVuZGVyVGFyZ2V0KCBudWxsICk7XG5cblx0XHR9O1xuXG5cdH1cblxuXHRDdWJlQ2FtZXJhLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIE9iamVjdDNELnByb3RvdHlwZSApO1xuXHRDdWJlQ2FtZXJhLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IEN1YmVDYW1lcmE7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEF1ZGlvTGlzdGVuZXIoKSB7XG5cblx0XHRPYmplY3QzRC5jYWxsKCB0aGlzICk7XG5cblx0XHR0aGlzLnR5cGUgPSAnQXVkaW9MaXN0ZW5lcic7XG5cblx0XHR0aGlzLmNvbnRleHQgPSBBdWRpb0NvbnRleHQuZ2V0Q29udGV4dCgpO1xuXG5cdFx0dGhpcy5nYWluID0gdGhpcy5jb250ZXh0LmNyZWF0ZUdhaW4oKTtcblx0XHR0aGlzLmdhaW4uY29ubmVjdCggdGhpcy5jb250ZXh0LmRlc3RpbmF0aW9uICk7XG5cblx0XHR0aGlzLmZpbHRlciA9IG51bGw7XG5cblx0fVxuXG5cdEF1ZGlvTGlzdGVuZXIucHJvdG90eXBlID0gT2JqZWN0LmFzc2lnbiggT2JqZWN0LmNyZWF0ZSggT2JqZWN0M0QucHJvdG90eXBlICksIHtcblxuXHRcdGNvbnN0cnVjdG9yOiBBdWRpb0xpc3RlbmVyLFxuXG5cdFx0Z2V0SW5wdXQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuZ2FpbjtcblxuXHRcdH0sXG5cblx0XHRyZW1vdmVGaWx0ZXI6IGZ1bmN0aW9uICggKSB7XG5cblx0XHRcdGlmICggdGhpcy5maWx0ZXIgIT09IG51bGwgKSB7XG5cblx0XHRcdFx0dGhpcy5nYWluLmRpc2Nvbm5lY3QoIHRoaXMuZmlsdGVyICk7XG5cdFx0XHRcdHRoaXMuZmlsdGVyLmRpc2Nvbm5lY3QoIHRoaXMuY29udGV4dC5kZXN0aW5hdGlvbiApO1xuXHRcdFx0XHR0aGlzLmdhaW4uY29ubmVjdCggdGhpcy5jb250ZXh0LmRlc3RpbmF0aW9uICk7XG5cdFx0XHRcdHRoaXMuZmlsdGVyID0gbnVsbDtcblxuXHRcdFx0fVxuXG5cdFx0fSxcblxuXHRcdGdldEZpbHRlcjogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5maWx0ZXI7XG5cblx0XHR9LFxuXG5cdFx0c2V0RmlsdGVyOiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHRpZiAoIHRoaXMuZmlsdGVyICE9PSBudWxsICkge1xuXG5cdFx0XHRcdHRoaXMuZ2Fpbi5kaXNjb25uZWN0KCB0aGlzLmZpbHRlciApO1xuXHRcdFx0XHR0aGlzLmZpbHRlci5kaXNjb25uZWN0KCB0aGlzLmNvbnRleHQuZGVzdGluYXRpb24gKTtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHR0aGlzLmdhaW4uZGlzY29ubmVjdCggdGhpcy5jb250ZXh0LmRlc3RpbmF0aW9uICk7XG5cblx0XHRcdH1cblxuXHRcdFx0dGhpcy5maWx0ZXIgPSB2YWx1ZTtcblx0XHRcdHRoaXMuZ2Fpbi5jb25uZWN0KCB0aGlzLmZpbHRlciApO1xuXHRcdFx0dGhpcy5maWx0ZXIuY29ubmVjdCggdGhpcy5jb250ZXh0LmRlc3RpbmF0aW9uICk7XG5cblx0XHR9LFxuXG5cdFx0Z2V0TWFzdGVyVm9sdW1lOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLmdhaW4uZ2Fpbi52YWx1ZTtcblxuXHRcdH0sXG5cblx0XHRzZXRNYXN0ZXJWb2x1bWU6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdHRoaXMuZ2Fpbi5nYWluLnZhbHVlID0gdmFsdWU7XG5cblx0XHR9LFxuXG5cdFx0dXBkYXRlTWF0cml4V29ybGQ6ICggZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgcG9zaXRpb24gPSBuZXcgVmVjdG9yMygpO1xuXHRcdFx0dmFyIHF1YXRlcm5pb24gPSBuZXcgUXVhdGVybmlvbigpO1xuXHRcdFx0dmFyIHNjYWxlID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0dmFyIG9yaWVudGF0aW9uID0gbmV3IFZlY3RvcjMoKTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIHVwZGF0ZU1hdHJpeFdvcmxkKCBmb3JjZSApIHtcblxuXHRcdFx0XHRPYmplY3QzRC5wcm90b3R5cGUudXBkYXRlTWF0cml4V29ybGQuY2FsbCggdGhpcywgZm9yY2UgKTtcblxuXHRcdFx0XHR2YXIgbGlzdGVuZXIgPSB0aGlzLmNvbnRleHQubGlzdGVuZXI7XG5cdFx0XHRcdHZhciB1cCA9IHRoaXMudXA7XG5cblx0XHRcdFx0dGhpcy5tYXRyaXhXb3JsZC5kZWNvbXBvc2UoIHBvc2l0aW9uLCBxdWF0ZXJuaW9uLCBzY2FsZSApO1xuXG5cdFx0XHRcdG9yaWVudGF0aW9uLnNldCggMCwgMCwgLSAxICkuYXBwbHlRdWF0ZXJuaW9uKCBxdWF0ZXJuaW9uICk7XG5cblx0XHRcdFx0aWYgKCBsaXN0ZW5lci5wb3NpdGlvblggKSB7XG5cblx0XHRcdFx0XHRsaXN0ZW5lci5wb3NpdGlvblguc2V0VmFsdWVBdFRpbWUoIHBvc2l0aW9uLngsIHRoaXMuY29udGV4dC5jdXJyZW50VGltZSApO1xuXHRcdFx0XHRcdGxpc3RlbmVyLnBvc2l0aW9uWS5zZXRWYWx1ZUF0VGltZSggcG9zaXRpb24ueSwgdGhpcy5jb250ZXh0LmN1cnJlbnRUaW1lICk7XG5cdFx0XHRcdFx0bGlzdGVuZXIucG9zaXRpb25aLnNldFZhbHVlQXRUaW1lKCBwb3NpdGlvbi56LCB0aGlzLmNvbnRleHQuY3VycmVudFRpbWUgKTtcblx0XHRcdFx0XHRsaXN0ZW5lci5mb3J3YXJkWC5zZXRWYWx1ZUF0VGltZSggb3JpZW50YXRpb24ueCwgdGhpcy5jb250ZXh0LmN1cnJlbnRUaW1lICk7XG5cdFx0XHRcdFx0bGlzdGVuZXIuZm9yd2FyZFkuc2V0VmFsdWVBdFRpbWUoIG9yaWVudGF0aW9uLnksIHRoaXMuY29udGV4dC5jdXJyZW50VGltZSApO1xuXHRcdFx0XHRcdGxpc3RlbmVyLmZvcndhcmRaLnNldFZhbHVlQXRUaW1lKCBvcmllbnRhdGlvbi56LCB0aGlzLmNvbnRleHQuY3VycmVudFRpbWUgKTtcblx0XHRcdFx0XHRsaXN0ZW5lci51cFguc2V0VmFsdWVBdFRpbWUoIHVwLngsIHRoaXMuY29udGV4dC5jdXJyZW50VGltZSApO1xuXHRcdFx0XHRcdGxpc3RlbmVyLnVwWS5zZXRWYWx1ZUF0VGltZSggdXAueSwgdGhpcy5jb250ZXh0LmN1cnJlbnRUaW1lICk7XG5cdFx0XHRcdFx0bGlzdGVuZXIudXBaLnNldFZhbHVlQXRUaW1lKCB1cC56LCB0aGlzLmNvbnRleHQuY3VycmVudFRpbWUgKTtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0bGlzdGVuZXIuc2V0UG9zaXRpb24oIHBvc2l0aW9uLngsIHBvc2l0aW9uLnksIHBvc2l0aW9uLnogKTtcblx0XHRcdFx0XHRsaXN0ZW5lci5zZXRPcmllbnRhdGlvbiggb3JpZW50YXRpb24ueCwgb3JpZW50YXRpb24ueSwgb3JpZW50YXRpb24ueiwgdXAueCwgdXAueSwgdXAueiApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fTtcblxuXHRcdH0gKSgpXG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yIFJlZWNlIEFhcm9uIExlY3JpdmFpbiAvIGh0dHA6Ly9yZWVjZW5vdGVzLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gQXVkaW8oIGxpc3RlbmVyICkge1xuXG5cdFx0T2JqZWN0M0QuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy50eXBlID0gJ0F1ZGlvJztcblxuXHRcdHRoaXMuY29udGV4dCA9IGxpc3RlbmVyLmNvbnRleHQ7XG5cblx0XHR0aGlzLmdhaW4gPSB0aGlzLmNvbnRleHQuY3JlYXRlR2FpbigpO1xuXHRcdHRoaXMuZ2Fpbi5jb25uZWN0KCBsaXN0ZW5lci5nZXRJbnB1dCgpICk7XG5cblx0XHR0aGlzLmF1dG9wbGF5ID0gZmFsc2U7XG5cblx0XHR0aGlzLmJ1ZmZlciA9IG51bGw7XG5cdFx0dGhpcy5sb29wID0gZmFsc2U7XG5cdFx0dGhpcy5zdGFydFRpbWUgPSAwO1xuXHRcdHRoaXMucGxheWJhY2tSYXRlID0gMTtcblx0XHR0aGlzLmlzUGxheWluZyA9IGZhbHNlO1xuXHRcdHRoaXMuaGFzUGxheWJhY2tDb250cm9sID0gdHJ1ZTtcblx0XHR0aGlzLnNvdXJjZVR5cGUgPSAnZW1wdHknO1xuXG5cdFx0dGhpcy5maWx0ZXJzID0gW107XG5cblx0fVxuXG5cdEF1ZGlvLnByb3RvdHlwZSA9IE9iamVjdC5hc3NpZ24oIE9iamVjdC5jcmVhdGUoIE9iamVjdDNELnByb3RvdHlwZSApLCB7XG5cblx0XHRjb25zdHJ1Y3RvcjogQXVkaW8sXG5cblx0XHRnZXRPdXRwdXQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuZ2FpbjtcblxuXHRcdH0sXG5cblx0XHRzZXROb2RlU291cmNlOiBmdW5jdGlvbiAoIGF1ZGlvTm9kZSApIHtcblxuXHRcdFx0dGhpcy5oYXNQbGF5YmFja0NvbnRyb2wgPSBmYWxzZTtcblx0XHRcdHRoaXMuc291cmNlVHlwZSA9ICdhdWRpb05vZGUnO1xuXHRcdFx0dGhpcy5zb3VyY2UgPSBhdWRpb05vZGU7XG5cdFx0XHR0aGlzLmNvbm5lY3QoKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0QnVmZmVyOiBmdW5jdGlvbiAoIGF1ZGlvQnVmZmVyICkge1xuXG5cdFx0XHR0aGlzLmJ1ZmZlciA9IGF1ZGlvQnVmZmVyO1xuXHRcdFx0dGhpcy5zb3VyY2VUeXBlID0gJ2J1ZmZlcic7XG5cblx0XHRcdGlmICggdGhpcy5hdXRvcGxheSApIHRoaXMucGxheSgpO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRwbGF5OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdGlmICggdGhpcy5pc1BsYXlpbmcgPT09IHRydWUgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuQXVkaW86IEF1ZGlvIGlzIGFscmVhZHkgcGxheWluZy4nICk7XG5cdFx0XHRcdHJldHVybjtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHRoaXMuaGFzUGxheWJhY2tDb250cm9sID09PSBmYWxzZSApIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5BdWRpbzogdGhpcyBBdWRpbyBoYXMgbm8gcGxheWJhY2sgY29udHJvbC4nICk7XG5cdFx0XHRcdHJldHVybjtcblxuXHRcdFx0fVxuXG5cdFx0XHR2YXIgc291cmNlID0gdGhpcy5jb250ZXh0LmNyZWF0ZUJ1ZmZlclNvdXJjZSgpO1xuXG5cdFx0XHRzb3VyY2UuYnVmZmVyID0gdGhpcy5idWZmZXI7XG5cdFx0XHRzb3VyY2UubG9vcCA9IHRoaXMubG9vcDtcblx0XHRcdHNvdXJjZS5vbmVuZGVkID0gdGhpcy5vbkVuZGVkLmJpbmQoIHRoaXMgKTtcblx0XHRcdHNvdXJjZS5wbGF5YmFja1JhdGUuc2V0VmFsdWVBdFRpbWUoIHRoaXMucGxheWJhY2tSYXRlLCB0aGlzLnN0YXJ0VGltZSApO1xuXHRcdFx0c291cmNlLnN0YXJ0KCAwLCB0aGlzLnN0YXJ0VGltZSApO1xuXG5cdFx0XHR0aGlzLmlzUGxheWluZyA9IHRydWU7XG5cblx0XHRcdHRoaXMuc291cmNlID0gc291cmNlO1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5jb25uZWN0KCk7XG5cblx0XHR9LFxuXG5cdFx0cGF1c2U6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0aWYgKCB0aGlzLmhhc1BsYXliYWNrQ29udHJvbCA9PT0gZmFsc2UgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuQXVkaW86IHRoaXMgQXVkaW8gaGFzIG5vIHBsYXliYWNrIGNvbnRyb2wuJyApO1xuXHRcdFx0XHRyZXR1cm47XG5cblx0XHRcdH1cblxuXHRcdFx0dGhpcy5zb3VyY2Uuc3RvcCgpO1xuXHRcdFx0dGhpcy5zdGFydFRpbWUgPSB0aGlzLmNvbnRleHQuY3VycmVudFRpbWU7XG5cdFx0XHR0aGlzLmlzUGxheWluZyA9IGZhbHNlO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzdG9wOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdGlmICggdGhpcy5oYXNQbGF5YmFja0NvbnRyb2wgPT09IGZhbHNlICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkF1ZGlvOiB0aGlzIEF1ZGlvIGhhcyBubyBwbGF5YmFjayBjb250cm9sLicgKTtcblx0XHRcdFx0cmV0dXJuO1xuXG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuc291cmNlLnN0b3AoKTtcblx0XHRcdHRoaXMuc3RhcnRUaW1lID0gMDtcblx0XHRcdHRoaXMuaXNQbGF5aW5nID0gZmFsc2U7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGNvbm5lY3Q6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0aWYgKCB0aGlzLmZpbHRlcnMubGVuZ3RoID4gMCApIHtcblxuXHRcdFx0XHR0aGlzLnNvdXJjZS5jb25uZWN0KCB0aGlzLmZpbHRlcnNbIDAgXSApO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMSwgbCA9IHRoaXMuZmlsdGVycy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdFx0dGhpcy5maWx0ZXJzWyBpIC0gMSBdLmNvbm5lY3QoIHRoaXMuZmlsdGVyc1sgaSBdICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHRoaXMuZmlsdGVyc1sgdGhpcy5maWx0ZXJzLmxlbmd0aCAtIDEgXS5jb25uZWN0KCB0aGlzLmdldE91dHB1dCgpICk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0dGhpcy5zb3VyY2UuY29ubmVjdCggdGhpcy5nZXRPdXRwdXQoKSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGRpc2Nvbm5lY3Q6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0aWYgKCB0aGlzLmZpbHRlcnMubGVuZ3RoID4gMCApIHtcblxuXHRcdFx0XHR0aGlzLnNvdXJjZS5kaXNjb25uZWN0KCB0aGlzLmZpbHRlcnNbIDAgXSApO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMSwgbCA9IHRoaXMuZmlsdGVycy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdFx0dGhpcy5maWx0ZXJzWyBpIC0gMSBdLmRpc2Nvbm5lY3QoIHRoaXMuZmlsdGVyc1sgaSBdICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHRoaXMuZmlsdGVyc1sgdGhpcy5maWx0ZXJzLmxlbmd0aCAtIDEgXS5kaXNjb25uZWN0KCB0aGlzLmdldE91dHB1dCgpICk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0dGhpcy5zb3VyY2UuZGlzY29ubmVjdCggdGhpcy5nZXRPdXRwdXQoKSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdGdldEZpbHRlcnM6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuZmlsdGVycztcblxuXHRcdH0sXG5cblx0XHRzZXRGaWx0ZXJzOiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHRpZiAoICEgdmFsdWUgKSB2YWx1ZSA9IFtdO1xuXG5cdFx0XHRpZiAoIHRoaXMuaXNQbGF5aW5nID09PSB0cnVlICkge1xuXG5cdFx0XHRcdHRoaXMuZGlzY29ubmVjdCgpO1xuXHRcdFx0XHR0aGlzLmZpbHRlcnMgPSB2YWx1ZTtcblx0XHRcdFx0dGhpcy5jb25uZWN0KCk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0dGhpcy5maWx0ZXJzID0gdmFsdWU7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Z2V0RmlsdGVyOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLmdldEZpbHRlcnMoKVsgMCBdO1xuXG5cdFx0fSxcblxuXHRcdHNldEZpbHRlcjogZnVuY3Rpb24gKCBmaWx0ZXIgKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLnNldEZpbHRlcnMoIGZpbHRlciA/IFsgZmlsdGVyIF0gOiBbXSApO1xuXG5cdFx0fSxcblxuXHRcdHNldFBsYXliYWNrUmF0ZTogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0aWYgKCB0aGlzLmhhc1BsYXliYWNrQ29udHJvbCA9PT0gZmFsc2UgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuQXVkaW86IHRoaXMgQXVkaW8gaGFzIG5vIHBsYXliYWNrIGNvbnRyb2wuJyApO1xuXHRcdFx0XHRyZXR1cm47XG5cblx0XHRcdH1cblxuXHRcdFx0dGhpcy5wbGF5YmFja1JhdGUgPSB2YWx1ZTtcblxuXHRcdFx0aWYgKCB0aGlzLmlzUGxheWluZyA9PT0gdHJ1ZSApIHtcblxuXHRcdFx0XHR0aGlzLnNvdXJjZS5wbGF5YmFja1JhdGUuc2V0VmFsdWVBdFRpbWUoIHRoaXMucGxheWJhY2tSYXRlLCB0aGlzLmNvbnRleHQuY3VycmVudFRpbWUgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRnZXRQbGF5YmFja1JhdGU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMucGxheWJhY2tSYXRlO1xuXG5cdFx0fSxcblxuXHRcdG9uRW5kZWQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dGhpcy5pc1BsYXlpbmcgPSBmYWxzZTtcblxuXHRcdH0sXG5cblx0XHRnZXRMb29wOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdGlmICggdGhpcy5oYXNQbGF5YmFja0NvbnRyb2wgPT09IGZhbHNlICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkF1ZGlvOiB0aGlzIEF1ZGlvIGhhcyBubyBwbGF5YmFjayBjb250cm9sLicgKTtcblx0XHRcdFx0cmV0dXJuIGZhbHNlO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzLmxvb3A7XG5cblx0XHR9LFxuXG5cdFx0c2V0TG9vcDogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0aWYgKCB0aGlzLmhhc1BsYXliYWNrQ29udHJvbCA9PT0gZmFsc2UgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuQXVkaW86IHRoaXMgQXVkaW8gaGFzIG5vIHBsYXliYWNrIGNvbnRyb2wuJyApO1xuXHRcdFx0XHRyZXR1cm47XG5cblx0XHRcdH1cblxuXHRcdFx0dGhpcy5sb29wID0gdmFsdWU7XG5cblx0XHRcdGlmICggdGhpcy5pc1BsYXlpbmcgPT09IHRydWUgKSB7XG5cblx0XHRcdFx0dGhpcy5zb3VyY2UubG9vcCA9IHRoaXMubG9vcDtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRnZXRWb2x1bWU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuZ2Fpbi5nYWluLnZhbHVlO1xuXG5cdFx0fSxcblxuXG5cdFx0c2V0Vm9sdW1lOiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHR0aGlzLmdhaW4uZ2Fpbi52YWx1ZSA9IHZhbHVlO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFBvc2l0aW9uYWxBdWRpbyggbGlzdGVuZXIgKSB7XG5cblx0XHRBdWRpby5jYWxsKCB0aGlzLCBsaXN0ZW5lciApO1xuXG5cdFx0dGhpcy5wYW5uZXIgPSB0aGlzLmNvbnRleHQuY3JlYXRlUGFubmVyKCk7XG5cdFx0dGhpcy5wYW5uZXIuY29ubmVjdCggdGhpcy5nYWluICk7XG5cblx0fVxuXG5cdFBvc2l0aW9uYWxBdWRpby5wcm90b3R5cGUgPSBPYmplY3QuYXNzaWduKCBPYmplY3QuY3JlYXRlKCBBdWRpby5wcm90b3R5cGUgKSwge1xuXG5cdFx0Y29uc3RydWN0b3I6IFBvc2l0aW9uYWxBdWRpbyxcblxuXHRcdGdldE91dHB1dDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5wYW5uZXI7XG5cblx0XHR9LFxuXG5cdFx0Z2V0UmVmRGlzdGFuY2U6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMucGFubmVyLnJlZkRpc3RhbmNlO1xuXG5cdFx0fSxcblxuXHRcdHNldFJlZkRpc3RhbmNlOiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHR0aGlzLnBhbm5lci5yZWZEaXN0YW5jZSA9IHZhbHVlO1xuXG5cdFx0fSxcblxuXHRcdGdldFJvbGxvZmZGYWN0b3I6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMucGFubmVyLnJvbGxvZmZGYWN0b3I7XG5cblx0XHR9LFxuXG5cdFx0c2V0Um9sbG9mZkZhY3RvcjogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0dGhpcy5wYW5uZXIucm9sbG9mZkZhY3RvciA9IHZhbHVlO1xuXG5cdFx0fSxcblxuXHRcdGdldERpc3RhbmNlTW9kZWw6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMucGFubmVyLmRpc3RhbmNlTW9kZWw7XG5cblx0XHR9LFxuXG5cdFx0c2V0RGlzdGFuY2VNb2RlbDogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0dGhpcy5wYW5uZXIuZGlzdGFuY2VNb2RlbCA9IHZhbHVlO1xuXG5cdFx0fSxcblxuXHRcdGdldE1heERpc3RhbmNlOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLnBhbm5lci5tYXhEaXN0YW5jZTtcblxuXHRcdH0sXG5cblx0XHRzZXRNYXhEaXN0YW5jZTogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0dGhpcy5wYW5uZXIubWF4RGlzdGFuY2UgPSB2YWx1ZTtcblxuXHRcdH0sXG5cblx0XHR1cGRhdGVNYXRyaXhXb3JsZDogKCBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBwb3NpdGlvbiA9IG5ldyBWZWN0b3IzKCk7XG5cblx0XHRcdHJldHVybiBmdW5jdGlvbiB1cGRhdGVNYXRyaXhXb3JsZCggZm9yY2UgKSB7XG5cblx0XHRcdFx0T2JqZWN0M0QucHJvdG90eXBlLnVwZGF0ZU1hdHJpeFdvcmxkLmNhbGwoIHRoaXMsIGZvcmNlICk7XG5cblx0XHRcdFx0cG9zaXRpb24uc2V0RnJvbU1hdHJpeFBvc2l0aW9uKCB0aGlzLm1hdHJpeFdvcmxkICk7XG5cblx0XHRcdFx0dGhpcy5wYW5uZXIuc2V0UG9zaXRpb24oIHBvc2l0aW9uLngsIHBvc2l0aW9uLnksIHBvc2l0aW9uLnogKTtcblxuXHRcdFx0fTtcblxuXHRcdH0gKSgpXG5cblxuXHR9ICk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEF1ZGlvQW5hbHlzZXIoIGF1ZGlvLCBmZnRTaXplICkge1xuXG5cdFx0dGhpcy5hbmFseXNlciA9IGF1ZGlvLmNvbnRleHQuY3JlYXRlQW5hbHlzZXIoKTtcblx0XHR0aGlzLmFuYWx5c2VyLmZmdFNpemUgPSBmZnRTaXplICE9PSB1bmRlZmluZWQgPyBmZnRTaXplIDogMjA0ODtcblxuXHRcdHRoaXMuZGF0YSA9IG5ldyBVaW50OEFycmF5KCB0aGlzLmFuYWx5c2VyLmZyZXF1ZW5jeUJpbkNvdW50ICk7XG5cblx0XHRhdWRpby5nZXRPdXRwdXQoKS5jb25uZWN0KCB0aGlzLmFuYWx5c2VyICk7XG5cblx0fVxuXG5cdE9iamVjdC5hc3NpZ24oIEF1ZGlvQW5hbHlzZXIucHJvdG90eXBlLCB7XG5cblx0XHRnZXRGcmVxdWVuY3lEYXRhOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMuYW5hbHlzZXIuZ2V0Qnl0ZUZyZXF1ZW5jeURhdGEoIHRoaXMuZGF0YSApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5kYXRhO1xuXG5cdFx0fSxcblxuXHRcdGdldEF2ZXJhZ2VGcmVxdWVuY3k6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0dmFyIHZhbHVlID0gMCwgZGF0YSA9IHRoaXMuZ2V0RnJlcXVlbmN5RGF0YSgpO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBkYXRhLmxlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHR2YWx1ZSArPSBkYXRhWyBpIF07XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHZhbHVlIC8gZGF0YS5sZW5ndGg7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKlxuXHQgKiBCdWZmZXJlZCBzY2VuZSBncmFwaCBwcm9wZXJ0eSB0aGF0IGFsbG93cyB3ZWlnaHRlZCBhY2N1bXVsYXRpb24uXG5cdCAqXG5cdCAqXG5cdCAqIEBhdXRob3IgQmVuIEhvdXN0b24gLyBodHRwOi8vY2xhcmEuaW8vXG5cdCAqIEBhdXRob3IgRGF2aWQgU2Fybm8gLyBodHRwOi8vbGlnaHRoYXVzLnVzL1xuXHQgKiBAYXV0aG9yIHRzY2h3XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFByb3BlcnR5TWl4ZXIoIGJpbmRpbmcsIHR5cGVOYW1lLCB2YWx1ZVNpemUgKSB7XG5cblx0XHR0aGlzLmJpbmRpbmcgPSBiaW5kaW5nO1xuXHRcdHRoaXMudmFsdWVTaXplID0gdmFsdWVTaXplO1xuXG5cdFx0dmFyIGJ1ZmZlclR5cGUgPSBGbG9hdDY0QXJyYXksXG5cdFx0XHRtaXhGdW5jdGlvbjtcblxuXHRcdHN3aXRjaCAoIHR5cGVOYW1lICkge1xuXG5cdFx0XHRjYXNlICdxdWF0ZXJuaW9uJzpcblx0XHRcdFx0bWl4RnVuY3Rpb24gPSB0aGlzLl9zbGVycDtcblx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdGNhc2UgJ3N0cmluZyc6XG5cdFx0XHRjYXNlICdib29sJzpcblx0XHRcdFx0YnVmZmVyVHlwZSA9IEFycmF5O1xuXHRcdFx0XHRtaXhGdW5jdGlvbiA9IHRoaXMuX3NlbGVjdDtcblx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdGRlZmF1bHQ6XG5cdFx0XHRcdG1peEZ1bmN0aW9uID0gdGhpcy5fbGVycDtcblxuXHRcdH1cblxuXHRcdHRoaXMuYnVmZmVyID0gbmV3IGJ1ZmZlclR5cGUoIHZhbHVlU2l6ZSAqIDQgKTtcblx0XHQvLyBsYXlvdXQ6IFsgaW5jb21pbmcgfCBhY2N1MCB8IGFjY3UxIHwgb3JpZyBdXG5cdFx0Ly9cblx0XHQvLyBpbnRlcnBvbGF0b3JzIGNhbiB1c2UgLmJ1ZmZlciBhcyB0aGVpciAucmVzdWx0XG5cdFx0Ly8gdGhlIGRhdGEgdGhlbiBnb2VzIHRvICdpbmNvbWluZydcblx0XHQvL1xuXHRcdC8vICdhY2N1MCcgYW5kICdhY2N1MScgYXJlIHVzZWQgZnJhbWUtaW50ZXJsZWF2ZWQgZm9yXG5cdFx0Ly8gdGhlIGN1bXVsYXRpdmUgcmVzdWx0IGFuZCBhcmUgY29tcGFyZWQgdG8gZGV0ZWN0XG5cdFx0Ly8gY2hhbmdlc1xuXHRcdC8vXG5cdFx0Ly8gJ29yaWcnIHN0b3JlcyB0aGUgb3JpZ2luYWwgc3RhdGUgb2YgdGhlIHByb3BlcnR5XG5cblx0XHR0aGlzLl9taXhCdWZmZXJSZWdpb24gPSBtaXhGdW5jdGlvbjtcblxuXHRcdHRoaXMuY3VtdWxhdGl2ZVdlaWdodCA9IDA7XG5cblx0XHR0aGlzLnVzZUNvdW50ID0gMDtcblx0XHR0aGlzLnJlZmVyZW5jZUNvdW50ID0gMDtcblxuXHR9XG5cblx0UHJvcGVydHlNaXhlci5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogUHJvcGVydHlNaXhlcixcblxuXHRcdC8vIGFjY3VtdWxhdGUgZGF0YSBpbiB0aGUgJ2luY29taW5nJyByZWdpb24gaW50byAnYWNjdTxpPidcblx0XHRhY2N1bXVsYXRlOiBmdW5jdGlvbiggYWNjdUluZGV4LCB3ZWlnaHQgKSB7XG5cblx0XHRcdC8vIG5vdGU6IGhhcHBpbHkgYWNjdW11bGF0aW5nIG5vdGhpbmcgd2hlbiB3ZWlnaHQgPSAwLCB0aGUgY2FsbGVyIGtub3dzXG5cdFx0XHQvLyB0aGUgd2VpZ2h0IGFuZCBzaG91bGRuJ3QgaGF2ZSBtYWRlIHRoZSBjYWxsIGluIHRoZSBmaXJzdCBwbGFjZVxuXG5cdFx0XHR2YXIgYnVmZmVyID0gdGhpcy5idWZmZXIsXG5cdFx0XHRcdHN0cmlkZSA9IHRoaXMudmFsdWVTaXplLFxuXHRcdFx0XHRvZmZzZXQgPSBhY2N1SW5kZXggKiBzdHJpZGUgKyBzdHJpZGUsXG5cblx0XHRcdFx0Y3VycmVudFdlaWdodCA9IHRoaXMuY3VtdWxhdGl2ZVdlaWdodDtcblxuXHRcdFx0aWYgKCBjdXJyZW50V2VpZ2h0ID09PSAwICkge1xuXG5cdFx0XHRcdC8vIGFjY3VOIDo9IGluY29taW5nICogd2VpZ2h0XG5cblx0XHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpICE9PSBzdHJpZGU7ICsrIGkgKSB7XG5cblx0XHRcdFx0XHRidWZmZXJbIG9mZnNldCArIGkgXSA9IGJ1ZmZlclsgaSBdO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRjdXJyZW50V2VpZ2h0ID0gd2VpZ2h0O1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdC8vIGFjY3VOIDo9IGFjY3VOICsgaW5jb21pbmcgKiB3ZWlnaHRcblxuXHRcdFx0XHRjdXJyZW50V2VpZ2h0ICs9IHdlaWdodDtcblx0XHRcdFx0dmFyIG1peCA9IHdlaWdodCAvIGN1cnJlbnRXZWlnaHQ7XG5cdFx0XHRcdHRoaXMuX21peEJ1ZmZlclJlZ2lvbiggYnVmZmVyLCBvZmZzZXQsIDAsIG1peCwgc3RyaWRlICk7XG5cblx0XHRcdH1cblxuXHRcdFx0dGhpcy5jdW11bGF0aXZlV2VpZ2h0ID0gY3VycmVudFdlaWdodDtcblxuXHRcdH0sXG5cblx0XHQvLyBhcHBseSB0aGUgc3RhdGUgb2YgJ2FjY3U8aT4nIHRvIHRoZSBiaW5kaW5nIHdoZW4gYWNjdXMgZGlmZmVyXG5cdFx0YXBwbHk6IGZ1bmN0aW9uKCBhY2N1SW5kZXggKSB7XG5cblx0XHRcdHZhciBzdHJpZGUgPSB0aGlzLnZhbHVlU2l6ZSxcblx0XHRcdFx0YnVmZmVyID0gdGhpcy5idWZmZXIsXG5cdFx0XHRcdG9mZnNldCA9IGFjY3VJbmRleCAqIHN0cmlkZSArIHN0cmlkZSxcblxuXHRcdFx0XHR3ZWlnaHQgPSB0aGlzLmN1bXVsYXRpdmVXZWlnaHQsXG5cblx0XHRcdFx0YmluZGluZyA9IHRoaXMuYmluZGluZztcblxuXHRcdFx0dGhpcy5jdW11bGF0aXZlV2VpZ2h0ID0gMDtcblxuXHRcdFx0aWYgKCB3ZWlnaHQgPCAxICkge1xuXG5cdFx0XHRcdC8vIGFjY3VOIDo9IGFjY3VOICsgb3JpZ2luYWwgKiAoIDEgLSBjdW11bGF0aXZlV2VpZ2h0IClcblxuXHRcdFx0XHR2YXIgb3JpZ2luYWxWYWx1ZU9mZnNldCA9IHN0cmlkZSAqIDM7XG5cblx0XHRcdFx0dGhpcy5fbWl4QnVmZmVyUmVnaW9uKFxuXHRcdFx0XHRcdFx0YnVmZmVyLCBvZmZzZXQsIG9yaWdpbmFsVmFsdWVPZmZzZXQsIDEgLSB3ZWlnaHQsIHN0cmlkZSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdGZvciAoIHZhciBpID0gc3RyaWRlLCBlID0gc3RyaWRlICsgc3RyaWRlOyBpICE9PSBlOyArKyBpICkge1xuXG5cdFx0XHRcdGlmICggYnVmZmVyWyBpIF0gIT09IGJ1ZmZlclsgaSArIHN0cmlkZSBdICkge1xuXG5cdFx0XHRcdFx0Ly8gdmFsdWUgaGFzIGNoYW5nZWQgLT4gdXBkYXRlIHNjZW5lIGdyYXBoXG5cblx0XHRcdFx0XHRiaW5kaW5nLnNldFZhbHVlKCBidWZmZXIsIG9mZnNldCApO1xuXHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0fSxcblxuXHRcdC8vIHJlbWVtYmVyIHRoZSBzdGF0ZSBvZiB0aGUgYm91bmQgcHJvcGVydHkgYW5kIGNvcHkgaXQgdG8gYm90aCBhY2N1c1xuXHRcdHNhdmVPcmlnaW5hbFN0YXRlOiBmdW5jdGlvbigpIHtcblxuXHRcdFx0dmFyIGJpbmRpbmcgPSB0aGlzLmJpbmRpbmc7XG5cblx0XHRcdHZhciBidWZmZXIgPSB0aGlzLmJ1ZmZlcixcblx0XHRcdFx0c3RyaWRlID0gdGhpcy52YWx1ZVNpemUsXG5cblx0XHRcdFx0b3JpZ2luYWxWYWx1ZU9mZnNldCA9IHN0cmlkZSAqIDM7XG5cblx0XHRcdGJpbmRpbmcuZ2V0VmFsdWUoIGJ1ZmZlciwgb3JpZ2luYWxWYWx1ZU9mZnNldCApO1xuXG5cdFx0XHQvLyBhY2N1WzAuLjFdIDo9IG9yaWcgLS0gaW5pdGlhbGx5IGRldGVjdCBjaGFuZ2VzIGFnYWluc3QgdGhlIG9yaWdpbmFsXG5cdFx0XHRmb3IgKCB2YXIgaSA9IHN0cmlkZSwgZSA9IG9yaWdpbmFsVmFsdWVPZmZzZXQ7IGkgIT09IGU7ICsrIGkgKSB7XG5cblx0XHRcdFx0YnVmZmVyWyBpIF0gPSBidWZmZXJbIG9yaWdpbmFsVmFsdWVPZmZzZXQgKyAoIGkgJSBzdHJpZGUgKSBdO1xuXG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuY3VtdWxhdGl2ZVdlaWdodCA9IDA7XG5cblx0XHR9LFxuXG5cdFx0Ly8gYXBwbHkgdGhlIHN0YXRlIHByZXZpb3VzbHkgdGFrZW4gdmlhICdzYXZlT3JpZ2luYWxTdGF0ZScgdG8gdGhlIGJpbmRpbmdcblx0XHRyZXN0b3JlT3JpZ2luYWxTdGF0ZTogZnVuY3Rpb24oKSB7XG5cblx0XHRcdHZhciBvcmlnaW5hbFZhbHVlT2Zmc2V0ID0gdGhpcy52YWx1ZVNpemUgKiAzO1xuXHRcdFx0dGhpcy5iaW5kaW5nLnNldFZhbHVlKCB0aGlzLmJ1ZmZlciwgb3JpZ2luYWxWYWx1ZU9mZnNldCApO1xuXG5cdFx0fSxcblxuXG5cdFx0Ly8gbWl4IGZ1bmN0aW9uc1xuXG5cdFx0X3NlbGVjdDogZnVuY3Rpb24oIGJ1ZmZlciwgZHN0T2Zmc2V0LCBzcmNPZmZzZXQsIHQsIHN0cmlkZSApIHtcblxuXHRcdFx0aWYgKCB0ID49IDAuNSApIHtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgIT09IHN0cmlkZTsgKysgaSApIHtcblxuXHRcdFx0XHRcdGJ1ZmZlclsgZHN0T2Zmc2V0ICsgaSBdID0gYnVmZmVyWyBzcmNPZmZzZXQgKyBpIF07XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0X3NsZXJwOiBmdW5jdGlvbiggYnVmZmVyLCBkc3RPZmZzZXQsIHNyY09mZnNldCwgdCwgc3RyaWRlICkge1xuXG5cdFx0XHRRdWF0ZXJuaW9uLnNsZXJwRmxhdCggYnVmZmVyLCBkc3RPZmZzZXQsXG5cdFx0XHRcdFx0YnVmZmVyLCBkc3RPZmZzZXQsIGJ1ZmZlciwgc3JjT2Zmc2V0LCB0ICk7XG5cblx0XHR9LFxuXG5cdFx0X2xlcnA6IGZ1bmN0aW9uKCBidWZmZXIsIGRzdE9mZnNldCwgc3JjT2Zmc2V0LCB0LCBzdHJpZGUgKSB7XG5cblx0XHRcdHZhciBzID0gMSAtIHQ7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMDsgaSAhPT0gc3RyaWRlOyArKyBpICkge1xuXG5cdFx0XHRcdHZhciBqID0gZHN0T2Zmc2V0ICsgaTtcblxuXHRcdFx0XHRidWZmZXJbIGogXSA9IGJ1ZmZlclsgaiBdICogcyArIGJ1ZmZlclsgc3JjT2Zmc2V0ICsgaSBdICogdDtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdH07XG5cblx0LyoqXG5cdCAqXG5cdCAqIEEgcmVmZXJlbmNlIHRvIGEgcmVhbCBwcm9wZXJ0eSBpbiB0aGUgc2NlbmUgZ3JhcGguXG5cdCAqXG5cdCAqXG5cdCAqIEBhdXRob3IgQmVuIEhvdXN0b24gLyBodHRwOi8vY2xhcmEuaW8vXG5cdCAqIEBhdXRob3IgRGF2aWQgU2Fybm8gLyBodHRwOi8vbGlnaHRoYXVzLnVzL1xuXHQgKiBAYXV0aG9yIHRzY2h3XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFByb3BlcnR5QmluZGluZyggcm9vdE5vZGUsIHBhdGgsIHBhcnNlZFBhdGggKSB7XG5cblx0XHR0aGlzLnBhdGggPSBwYXRoO1xuXHRcdHRoaXMucGFyc2VkUGF0aCA9IHBhcnNlZFBhdGggfHxcblx0XHRcdFx0UHJvcGVydHlCaW5kaW5nLnBhcnNlVHJhY2tOYW1lKCBwYXRoICk7XG5cblx0XHR0aGlzLm5vZGUgPSBQcm9wZXJ0eUJpbmRpbmcuZmluZE5vZGUoXG5cdFx0XHRcdHJvb3ROb2RlLCB0aGlzLnBhcnNlZFBhdGgubm9kZU5hbWUgKSB8fCByb290Tm9kZTtcblxuXHRcdHRoaXMucm9vdE5vZGUgPSByb290Tm9kZTtcblxuXHR9XG5cblx0UHJvcGVydHlCaW5kaW5nLnByb3RvdHlwZSA9IHtcblxuXHRcdGNvbnN0cnVjdG9yOiBQcm9wZXJ0eUJpbmRpbmcsXG5cblx0XHRnZXRWYWx1ZTogZnVuY3Rpb24gZ2V0VmFsdWVfdW5ib3VuZCggdGFyZ2V0QXJyYXksIG9mZnNldCApIHtcblxuXHRcdFx0dGhpcy5iaW5kKCk7XG5cdFx0XHR0aGlzLmdldFZhbHVlKCB0YXJnZXRBcnJheSwgb2Zmc2V0ICk7XG5cblx0XHRcdC8vIE5vdGU6IFRoaXMgY2xhc3MgdXNlcyBhIFN0YXRlIHBhdHRlcm4gb24gYSBwZXItbWV0aG9kIGJhc2lzOlxuXHRcdFx0Ly8gJ2JpbmQnIHNldHMgJ3RoaXMuZ2V0VmFsdWUnIC8gJ3NldFZhbHVlJyBhbmQgc2hhZG93cyB0aGVcblx0XHRcdC8vIHByb3RvdHlwZSB2ZXJzaW9uIG9mIHRoZXNlIG1ldGhvZHMgd2l0aCBvbmUgdGhhdCByZXByZXNlbnRzXG5cdFx0XHQvLyB0aGUgYm91bmQgc3RhdGUuIFdoZW4gdGhlIHByb3BlcnR5IGlzIG5vdCBmb3VuZCwgdGhlIG1ldGhvZHNcblx0XHRcdC8vIGJlY29tZSBuby1vcHMuXG5cblx0XHR9LFxuXG5cdFx0c2V0VmFsdWU6IGZ1bmN0aW9uIGdldFZhbHVlX3VuYm91bmQoIHNvdXJjZUFycmF5LCBvZmZzZXQgKSB7XG5cblx0XHRcdHRoaXMuYmluZCgpO1xuXHRcdFx0dGhpcy5zZXRWYWx1ZSggc291cmNlQXJyYXksIG9mZnNldCApO1xuXG5cdFx0fSxcblxuXHRcdC8vIGNyZWF0ZSBnZXR0ZXIgLyBzZXR0ZXIgcGFpciBmb3IgYSBwcm9wZXJ0eSBpbiB0aGUgc2NlbmUgZ3JhcGhcblx0XHRiaW5kOiBmdW5jdGlvbigpIHtcblxuXHRcdFx0dmFyIHRhcmdldE9iamVjdCA9IHRoaXMubm9kZSxcblx0XHRcdFx0cGFyc2VkUGF0aCA9IHRoaXMucGFyc2VkUGF0aCxcblxuXHRcdFx0XHRvYmplY3ROYW1lID0gcGFyc2VkUGF0aC5vYmplY3ROYW1lLFxuXHRcdFx0XHRwcm9wZXJ0eU5hbWUgPSBwYXJzZWRQYXRoLnByb3BlcnR5TmFtZSxcblx0XHRcdFx0cHJvcGVydHlJbmRleCA9IHBhcnNlZFBhdGgucHJvcGVydHlJbmRleDtcblxuXHRcdFx0aWYgKCAhIHRhcmdldE9iamVjdCApIHtcblxuXHRcdFx0XHR0YXJnZXRPYmplY3QgPSBQcm9wZXJ0eUJpbmRpbmcuZmluZE5vZGUoXG5cdFx0XHRcdFx0XHR0aGlzLnJvb3ROb2RlLCBwYXJzZWRQYXRoLm5vZGVOYW1lICkgfHwgdGhpcy5yb290Tm9kZTtcblxuXHRcdFx0XHR0aGlzLm5vZGUgPSB0YXJnZXRPYmplY3Q7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gc2V0IGZhaWwgc3RhdGUgc28gd2UgY2FuIGp1c3QgJ3JldHVybicgb24gZXJyb3Jcblx0XHRcdHRoaXMuZ2V0VmFsdWUgPSB0aGlzLl9nZXRWYWx1ZV91bmF2YWlsYWJsZTtcblx0XHRcdHRoaXMuc2V0VmFsdWUgPSB0aGlzLl9zZXRWYWx1ZV91bmF2YWlsYWJsZTtcblxuXHQgXHRcdC8vIGVuc3VyZSB0aGVyZSBpcyBhIHZhbHVlIG5vZGVcblx0XHRcdGlmICggISB0YXJnZXRPYmplY3QgKSB7XG5cblx0XHRcdFx0Y29uc29sZS5lcnJvciggXCIgIHRyeWluZyB0byB1cGRhdGUgbm9kZSBmb3IgdHJhY2s6IFwiICsgdGhpcy5wYXRoICsgXCIgYnV0IGl0IHdhc24ndCBmb3VuZC5cIiApO1xuXHRcdFx0XHRyZXR1cm47XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBvYmplY3ROYW1lICkge1xuXG5cdFx0XHRcdHZhciBvYmplY3RJbmRleCA9IHBhcnNlZFBhdGgub2JqZWN0SW5kZXg7XG5cblx0XHRcdFx0Ly8gc3BlY2lhbCBjYXNlcyB3ZXJlIHdlIG5lZWQgdG8gcmVhY2ggZGVlcGVyIGludG8gdGhlIGhpZXJhcmNoeSB0byBnZXQgdGhlIGZhY2UgbWF0ZXJpYWxzLi4uLlxuXHRcdFx0XHRzd2l0Y2ggKCBvYmplY3ROYW1lICkge1xuXG5cdFx0XHRcdFx0Y2FzZSAnbWF0ZXJpYWxzJzpcblxuXHRcdFx0XHRcdFx0aWYgKCAhIHRhcmdldE9iamVjdC5tYXRlcmlhbCApIHtcblxuXHRcdFx0XHRcdFx0XHRjb25zb2xlLmVycm9yKCAnICBjYW4gbm90IGJpbmQgdG8gbWF0ZXJpYWwgYXMgbm9kZSBkb2VzIG5vdCBoYXZlIGEgbWF0ZXJpYWwnLCB0aGlzICk7XG5cdFx0XHRcdFx0XHRcdHJldHVybjtcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRpZiAoICEgdGFyZ2V0T2JqZWN0Lm1hdGVyaWFsLm1hdGVyaWFscyApIHtcblxuXHRcdFx0XHRcdFx0XHRjb25zb2xlLmVycm9yKCAnICBjYW4gbm90IGJpbmQgdG8gbWF0ZXJpYWwubWF0ZXJpYWxzIGFzIG5vZGUubWF0ZXJpYWwgZG9lcyBub3QgaGF2ZSBhIG1hdGVyaWFscyBhcnJheScsIHRoaXMgKTtcblx0XHRcdFx0XHRcdFx0cmV0dXJuO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdHRhcmdldE9iamVjdCA9IHRhcmdldE9iamVjdC5tYXRlcmlhbC5tYXRlcmlhbHM7XG5cblx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0Y2FzZSAnYm9uZXMnOlxuXG5cdFx0XHRcdFx0XHRpZiAoICEgdGFyZ2V0T2JqZWN0LnNrZWxldG9uICkge1xuXG5cdFx0XHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IoICcgIGNhbiBub3QgYmluZCB0byBib25lcyBhcyBub2RlIGRvZXMgbm90IGhhdmUgYSBza2VsZXRvbicsIHRoaXMgKTtcblx0XHRcdFx0XHRcdFx0cmV0dXJuO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdC8vIHBvdGVudGlhbCBmdXR1cmUgb3B0aW1pemF0aW9uOiBza2lwIHRoaXMgaWYgcHJvcGVydHlJbmRleCBpcyBhbHJlYWR5IGFuIGludGVnZXJcblx0XHRcdFx0XHRcdC8vIGFuZCBjb252ZXJ0IHRoZSBpbnRlZ2VyIHN0cmluZyB0byBhIHRydWUgaW50ZWdlci5cblxuXHRcdFx0XHRcdFx0dGFyZ2V0T2JqZWN0ID0gdGFyZ2V0T2JqZWN0LnNrZWxldG9uLmJvbmVzO1xuXG5cdFx0XHRcdFx0XHQvLyBzdXBwb3J0IHJlc29sdmluZyBtb3JwaFRhcmdldCBuYW1lcyBpbnRvIGluZGljZXMuXG5cdFx0XHRcdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCB0YXJnZXRPYmplY3QubGVuZ3RoOyBpICsrICkge1xuXG5cdFx0XHRcdFx0XHRcdGlmICggdGFyZ2V0T2JqZWN0WyBpIF0ubmFtZSA9PT0gb2JqZWN0SW5kZXggKSB7XG5cblx0XHRcdFx0XHRcdFx0XHRvYmplY3RJbmRleCA9IGk7XG5cdFx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdGJyZWFrO1xuXG5cdFx0XHRcdFx0ZGVmYXVsdDpcblxuXHRcdFx0XHRcdFx0aWYgKCB0YXJnZXRPYmplY3RbIG9iamVjdE5hbWUgXSA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IoICcgIGNhbiBub3QgYmluZCB0byBvYmplY3ROYW1lIG9mIG5vZGUsIHVuZGVmaW5lZCcsIHRoaXMgKTtcblx0XHRcdFx0XHRcdFx0cmV0dXJuO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdHRhcmdldE9iamVjdCA9IHRhcmdldE9iamVjdFsgb2JqZWN0TmFtZSBdO1xuXG5cdFx0XHRcdH1cblxuXG5cdFx0XHRcdGlmICggb2JqZWN0SW5kZXggIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdGlmICggdGFyZ2V0T2JqZWN0WyBvYmplY3RJbmRleCBdID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IoIFwiICB0cnlpbmcgdG8gYmluZCB0byBvYmplY3RJbmRleCBvZiBvYmplY3ROYW1lLCBidXQgaXMgdW5kZWZpbmVkOlwiLCB0aGlzLCB0YXJnZXRPYmplY3QgKTtcblx0XHRcdFx0XHRcdHJldHVybjtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdHRhcmdldE9iamVjdCA9IHRhcmdldE9iamVjdFsgb2JqZWN0SW5kZXggXTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gcmVzb2x2ZSBwcm9wZXJ0eVxuXHRcdFx0dmFyIG5vZGVQcm9wZXJ0eSA9IHRhcmdldE9iamVjdFsgcHJvcGVydHlOYW1lIF07XG5cblx0XHRcdGlmICggbm9kZVByb3BlcnR5ID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0dmFyIG5vZGVOYW1lID0gcGFyc2VkUGF0aC5ub2RlTmFtZTtcblxuXHRcdFx0XHRjb25zb2xlLmVycm9yKCBcIiAgdHJ5aW5nIHRvIHVwZGF0ZSBwcm9wZXJ0eSBmb3IgdHJhY2s6IFwiICsgbm9kZU5hbWUgK1xuXHRcdFx0XHRcdFx0Jy4nICsgcHJvcGVydHlOYW1lICsgXCIgYnV0IGl0IHdhc24ndCBmb3VuZC5cIiwgdGFyZ2V0T2JqZWN0ICk7XG5cdFx0XHRcdHJldHVybjtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyBkZXRlcm1pbmUgdmVyc2lvbmluZyBzY2hlbWVcblx0XHRcdHZhciB2ZXJzaW9uaW5nID0gdGhpcy5WZXJzaW9uaW5nLk5vbmU7XG5cblx0XHRcdGlmICggdGFyZ2V0T2JqZWN0Lm5lZWRzVXBkYXRlICE9PSB1bmRlZmluZWQgKSB7IC8vIG1hdGVyaWFsXG5cblx0XHRcdFx0dmVyc2lvbmluZyA9IHRoaXMuVmVyc2lvbmluZy5OZWVkc1VwZGF0ZTtcblx0XHRcdFx0dGhpcy50YXJnZXRPYmplY3QgPSB0YXJnZXRPYmplY3Q7XG5cblx0XHRcdH0gZWxzZSBpZiAoIHRhcmdldE9iamVjdC5tYXRyaXhXb3JsZE5lZWRzVXBkYXRlICE9PSB1bmRlZmluZWQgKSB7IC8vIG5vZGUgdHJhbnNmb3JtXG5cblx0XHRcdFx0dmVyc2lvbmluZyA9IHRoaXMuVmVyc2lvbmluZy5NYXRyaXhXb3JsZE5lZWRzVXBkYXRlO1xuXHRcdFx0XHR0aGlzLnRhcmdldE9iamVjdCA9IHRhcmdldE9iamVjdDtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyBkZXRlcm1pbmUgaG93IHRoZSBwcm9wZXJ0eSBnZXRzIGJvdW5kXG5cdFx0XHR2YXIgYmluZGluZ1R5cGUgPSB0aGlzLkJpbmRpbmdUeXBlLkRpcmVjdDtcblxuXHRcdFx0aWYgKCBwcm9wZXJ0eUluZGV4ICE9PSB1bmRlZmluZWQgKSB7XG5cdFx0XHRcdC8vIGFjY2VzcyBhIHN1YiBlbGVtZW50IG9mIHRoZSBwcm9wZXJ0eSBhcnJheSAob25seSBwcmltaXRpdmVzIGFyZSBzdXBwb3J0ZWQgcmlnaHQgbm93KVxuXG5cdFx0XHRcdGlmICggcHJvcGVydHlOYW1lID09PSBcIm1vcnBoVGFyZ2V0SW5mbHVlbmNlc1wiICkge1xuXHRcdFx0XHRcdC8vIHBvdGVudGlhbCBvcHRpbWl6YXRpb24sIHNraXAgdGhpcyBpZiBwcm9wZXJ0eUluZGV4IGlzIGFscmVhZHkgYW4gaW50ZWdlciwgYW5kIGNvbnZlcnQgdGhlIGludGVnZXIgc3RyaW5nIHRvIGEgdHJ1ZSBpbnRlZ2VyLlxuXG5cdFx0XHRcdFx0Ly8gc3VwcG9ydCByZXNvbHZpbmcgbW9ycGhUYXJnZXQgbmFtZXMgaW50byBpbmRpY2VzLlxuXHRcdFx0XHRcdGlmICggISB0YXJnZXRPYmplY3QuZ2VvbWV0cnkgKSB7XG5cblx0XHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IoICcgIGNhbiBub3QgYmluZCB0byBtb3JwaFRhcmdldEluZmx1ZW5jZXMgYmVjYXN1c2Ugbm9kZSBkb2VzIG5vdCBoYXZlIGEgZ2VvbWV0cnknLCB0aGlzICk7XG5cdFx0XHRcdFx0XHRyZXR1cm47XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRpZiAoICEgdGFyZ2V0T2JqZWN0Lmdlb21ldHJ5Lm1vcnBoVGFyZ2V0cyApIHtcblxuXHRcdFx0XHRcdFx0Y29uc29sZS5lcnJvciggJyAgY2FuIG5vdCBiaW5kIHRvIG1vcnBoVGFyZ2V0SW5mbHVlbmNlcyBiZWNhc3VzZSBub2RlIGRvZXMgbm90IGhhdmUgYSBnZW9tZXRyeS5tb3JwaFRhcmdldHMnLCB0aGlzICk7XG5cdFx0XHRcdFx0XHRyZXR1cm47XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCB0aGlzLm5vZGUuZ2VvbWV0cnkubW9ycGhUYXJnZXRzLmxlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHRcdFx0aWYgKCB0YXJnZXRPYmplY3QuZ2VvbWV0cnkubW9ycGhUYXJnZXRzWyBpIF0ubmFtZSA9PT0gcHJvcGVydHlJbmRleCApIHtcblxuXHRcdFx0XHRcdFx0XHRwcm9wZXJ0eUluZGV4ID0gaTtcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0YmluZGluZ1R5cGUgPSB0aGlzLkJpbmRpbmdUeXBlLkFycmF5RWxlbWVudDtcblxuXHRcdFx0XHR0aGlzLnJlc29sdmVkUHJvcGVydHkgPSBub2RlUHJvcGVydHk7XG5cdFx0XHRcdHRoaXMucHJvcGVydHlJbmRleCA9IHByb3BlcnR5SW5kZXg7XG5cblx0XHRcdH0gZWxzZSBpZiAoIG5vZGVQcm9wZXJ0eS5mcm9tQXJyYXkgIT09IHVuZGVmaW5lZCAmJiBub2RlUHJvcGVydHkudG9BcnJheSAhPT0gdW5kZWZpbmVkICkge1xuXHRcdFx0XHQvLyBtdXN0IHVzZSBjb3B5IGZvciBPYmplY3QzRC5FdWxlci9RdWF0ZXJuaW9uXG5cblx0XHRcdFx0YmluZGluZ1R5cGUgPSB0aGlzLkJpbmRpbmdUeXBlLkhhc0Zyb21Ub0FycmF5O1xuXG5cdFx0XHRcdHRoaXMucmVzb2x2ZWRQcm9wZXJ0eSA9IG5vZGVQcm9wZXJ0eTtcblxuXHRcdFx0fSBlbHNlIGlmICggbm9kZVByb3BlcnR5Lmxlbmd0aCAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdGJpbmRpbmdUeXBlID0gdGhpcy5CaW5kaW5nVHlwZS5FbnRpcmVBcnJheTtcblxuXHRcdFx0XHR0aGlzLnJlc29sdmVkUHJvcGVydHkgPSBub2RlUHJvcGVydHk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0dGhpcy5wcm9wZXJ0eU5hbWUgPSBwcm9wZXJ0eU5hbWU7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gc2VsZWN0IGdldHRlciAvIHNldHRlclxuXHRcdFx0dGhpcy5nZXRWYWx1ZSA9IHRoaXMuR2V0dGVyQnlCaW5kaW5nVHlwZVsgYmluZGluZ1R5cGUgXTtcblx0XHRcdHRoaXMuc2V0VmFsdWUgPSB0aGlzLlNldHRlckJ5QmluZGluZ1R5cGVBbmRWZXJzaW9uaW5nWyBiaW5kaW5nVHlwZSBdWyB2ZXJzaW9uaW5nIF07XG5cblx0XHR9LFxuXG5cdFx0dW5iaW5kOiBmdW5jdGlvbigpIHtcblxuXHRcdFx0dGhpcy5ub2RlID0gbnVsbDtcblxuXHRcdFx0Ly8gYmFjayB0byB0aGUgcHJvdG90eXBlIHZlcnNpb24gb2YgZ2V0VmFsdWUgLyBzZXRWYWx1ZVxuXHRcdFx0Ly8gbm90ZTogYXZvaWRpbmcgdG8gbXV0YXRlIHRoZSBzaGFwZSBvZiAndGhpcycgdmlhICdkZWxldGUnXG5cdFx0XHR0aGlzLmdldFZhbHVlID0gdGhpcy5fZ2V0VmFsdWVfdW5ib3VuZDtcblx0XHRcdHRoaXMuc2V0VmFsdWUgPSB0aGlzLl9zZXRWYWx1ZV91bmJvdW5kO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0T2JqZWN0LmFzc2lnbiggUHJvcGVydHlCaW5kaW5nLnByb3RvdHlwZSwgeyAvLyBwcm90b3R5cGUsIGNvbnRpbnVlZFxuXG5cdFx0Ly8gdGhlc2UgYXJlIHVzZWQgdG8gXCJiaW5kXCIgYSBub25leGlzdGVudCBwcm9wZXJ0eVxuXHRcdF9nZXRWYWx1ZV91bmF2YWlsYWJsZTogZnVuY3Rpb24oKSB7fSxcblx0XHRfc2V0VmFsdWVfdW5hdmFpbGFibGU6IGZ1bmN0aW9uKCkge30sXG5cblx0XHQvLyBpbml0aWFsIHN0YXRlIG9mIHRoZXNlIG1ldGhvZHMgdGhhdCBjYWxscyAnYmluZCdcblx0XHRfZ2V0VmFsdWVfdW5ib3VuZDogUHJvcGVydHlCaW5kaW5nLnByb3RvdHlwZS5nZXRWYWx1ZSxcblx0XHRfc2V0VmFsdWVfdW5ib3VuZDogUHJvcGVydHlCaW5kaW5nLnByb3RvdHlwZS5zZXRWYWx1ZSxcblxuXHRcdEJpbmRpbmdUeXBlOiB7XG5cdFx0XHREaXJlY3Q6IDAsXG5cdFx0XHRFbnRpcmVBcnJheTogMSxcblx0XHRcdEFycmF5RWxlbWVudDogMixcblx0XHRcdEhhc0Zyb21Ub0FycmF5OiAzXG5cdFx0fSxcblxuXHRcdFZlcnNpb25pbmc6IHtcblx0XHRcdE5vbmU6IDAsXG5cdFx0XHROZWVkc1VwZGF0ZTogMSxcblx0XHRcdE1hdHJpeFdvcmxkTmVlZHNVcGRhdGU6IDJcblx0XHR9LFxuXG5cdFx0R2V0dGVyQnlCaW5kaW5nVHlwZTogW1xuXG5cdFx0XHRmdW5jdGlvbiBnZXRWYWx1ZV9kaXJlY3QoIGJ1ZmZlciwgb2Zmc2V0ICkge1xuXG5cdFx0XHRcdGJ1ZmZlclsgb2Zmc2V0IF0gPSB0aGlzLm5vZGVbIHRoaXMucHJvcGVydHlOYW1lIF07XG5cblx0XHRcdH0sXG5cblx0XHRcdGZ1bmN0aW9uIGdldFZhbHVlX2FycmF5KCBidWZmZXIsIG9mZnNldCApIHtcblxuXHRcdFx0XHR2YXIgc291cmNlID0gdGhpcy5yZXNvbHZlZFByb3BlcnR5O1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbiA9IHNvdXJjZS5sZW5ndGg7IGkgIT09IG47ICsrIGkgKSB7XG5cblx0XHRcdFx0XHRidWZmZXJbIG9mZnNldCArKyBdID0gc291cmNlWyBpIF07XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9LFxuXG5cdFx0XHRmdW5jdGlvbiBnZXRWYWx1ZV9hcnJheUVsZW1lbnQoIGJ1ZmZlciwgb2Zmc2V0ICkge1xuXG5cdFx0XHRcdGJ1ZmZlclsgb2Zmc2V0IF0gPSB0aGlzLnJlc29sdmVkUHJvcGVydHlbIHRoaXMucHJvcGVydHlJbmRleCBdO1xuXG5cdFx0XHR9LFxuXG5cdFx0XHRmdW5jdGlvbiBnZXRWYWx1ZV90b0FycmF5KCBidWZmZXIsIG9mZnNldCApIHtcblxuXHRcdFx0XHR0aGlzLnJlc29sdmVkUHJvcGVydHkudG9BcnJheSggYnVmZmVyLCBvZmZzZXQgKTtcblxuXHRcdFx0fVxuXG5cdFx0XSxcblxuXHRcdFNldHRlckJ5QmluZGluZ1R5cGVBbmRWZXJzaW9uaW5nOiBbXG5cblx0XHRcdFtcblx0XHRcdFx0Ly8gRGlyZWN0XG5cblx0XHRcdFx0ZnVuY3Rpb24gc2V0VmFsdWVfZGlyZWN0KCBidWZmZXIsIG9mZnNldCApIHtcblxuXHRcdFx0XHRcdHRoaXMubm9kZVsgdGhpcy5wcm9wZXJ0eU5hbWUgXSA9IGJ1ZmZlclsgb2Zmc2V0IF07XG5cblx0XHRcdFx0fSxcblxuXHRcdFx0XHRmdW5jdGlvbiBzZXRWYWx1ZV9kaXJlY3Rfc2V0TmVlZHNVcGRhdGUoIGJ1ZmZlciwgb2Zmc2V0ICkge1xuXG5cdFx0XHRcdFx0dGhpcy5ub2RlWyB0aGlzLnByb3BlcnR5TmFtZSBdID0gYnVmZmVyWyBvZmZzZXQgXTtcblx0XHRcdFx0XHR0aGlzLnRhcmdldE9iamVjdC5uZWVkc1VwZGF0ZSA9IHRydWU7XG5cblx0XHRcdFx0fSxcblxuXHRcdFx0XHRmdW5jdGlvbiBzZXRWYWx1ZV9kaXJlY3Rfc2V0TWF0cml4V29ybGROZWVkc1VwZGF0ZSggYnVmZmVyLCBvZmZzZXQgKSB7XG5cblx0XHRcdFx0XHR0aGlzLm5vZGVbIHRoaXMucHJvcGVydHlOYW1lIF0gPSBidWZmZXJbIG9mZnNldCBdO1xuXHRcdFx0XHRcdHRoaXMudGFyZ2V0T2JqZWN0Lm1hdHJpeFdvcmxkTmVlZHNVcGRhdGUgPSB0cnVlO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XSwgW1xuXG5cdFx0XHRcdC8vIEVudGlyZUFycmF5XG5cblx0XHRcdFx0ZnVuY3Rpb24gc2V0VmFsdWVfYXJyYXkoIGJ1ZmZlciwgb2Zmc2V0ICkge1xuXG5cdFx0XHRcdFx0dmFyIGRlc3QgPSB0aGlzLnJlc29sdmVkUHJvcGVydHk7XG5cblx0XHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIG4gPSBkZXN0Lmxlbmd0aDsgaSAhPT0gbjsgKysgaSApIHtcblxuXHRcdFx0XHRcdFx0ZGVzdFsgaSBdID0gYnVmZmVyWyBvZmZzZXQgKysgXTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9LFxuXG5cdFx0XHRcdGZ1bmN0aW9uIHNldFZhbHVlX2FycmF5X3NldE5lZWRzVXBkYXRlKCBidWZmZXIsIG9mZnNldCApIHtcblxuXHRcdFx0XHRcdHZhciBkZXN0ID0gdGhpcy5yZXNvbHZlZFByb3BlcnR5O1xuXG5cdFx0XHRcdFx0Zm9yICggdmFyIGkgPSAwLCBuID0gZGVzdC5sZW5ndGg7IGkgIT09IG47ICsrIGkgKSB7XG5cblx0XHRcdFx0XHRcdGRlc3RbIGkgXSA9IGJ1ZmZlclsgb2Zmc2V0ICsrIF07XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR0aGlzLnRhcmdldE9iamVjdC5uZWVkc1VwZGF0ZSA9IHRydWU7XG5cblx0XHRcdFx0fSxcblxuXHRcdFx0XHRmdW5jdGlvbiBzZXRWYWx1ZV9hcnJheV9zZXRNYXRyaXhXb3JsZE5lZWRzVXBkYXRlKCBidWZmZXIsIG9mZnNldCApIHtcblxuXHRcdFx0XHRcdHZhciBkZXN0ID0gdGhpcy5yZXNvbHZlZFByb3BlcnR5O1xuXG5cdFx0XHRcdFx0Zm9yICggdmFyIGkgPSAwLCBuID0gZGVzdC5sZW5ndGg7IGkgIT09IG47ICsrIGkgKSB7XG5cblx0XHRcdFx0XHRcdGRlc3RbIGkgXSA9IGJ1ZmZlclsgb2Zmc2V0ICsrIF07XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR0aGlzLnRhcmdldE9iamVjdC5tYXRyaXhXb3JsZE5lZWRzVXBkYXRlID0gdHJ1ZTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdF0sIFtcblxuXHRcdFx0XHQvLyBBcnJheUVsZW1lbnRcblxuXHRcdFx0XHRmdW5jdGlvbiBzZXRWYWx1ZV9hcnJheUVsZW1lbnQoIGJ1ZmZlciwgb2Zmc2V0ICkge1xuXG5cdFx0XHRcdFx0dGhpcy5yZXNvbHZlZFByb3BlcnR5WyB0aGlzLnByb3BlcnR5SW5kZXggXSA9IGJ1ZmZlclsgb2Zmc2V0IF07XG5cblx0XHRcdFx0fSxcblxuXHRcdFx0XHRmdW5jdGlvbiBzZXRWYWx1ZV9hcnJheUVsZW1lbnRfc2V0TmVlZHNVcGRhdGUoIGJ1ZmZlciwgb2Zmc2V0ICkge1xuXG5cdFx0XHRcdFx0dGhpcy5yZXNvbHZlZFByb3BlcnR5WyB0aGlzLnByb3BlcnR5SW5kZXggXSA9IGJ1ZmZlclsgb2Zmc2V0IF07XG5cdFx0XHRcdFx0dGhpcy50YXJnZXRPYmplY3QubmVlZHNVcGRhdGUgPSB0cnVlO1xuXG5cdFx0XHRcdH0sXG5cblx0XHRcdFx0ZnVuY3Rpb24gc2V0VmFsdWVfYXJyYXlFbGVtZW50X3NldE1hdHJpeFdvcmxkTmVlZHNVcGRhdGUoIGJ1ZmZlciwgb2Zmc2V0ICkge1xuXG5cdFx0XHRcdFx0dGhpcy5yZXNvbHZlZFByb3BlcnR5WyB0aGlzLnByb3BlcnR5SW5kZXggXSA9IGJ1ZmZlclsgb2Zmc2V0IF07XG5cdFx0XHRcdFx0dGhpcy50YXJnZXRPYmplY3QubWF0cml4V29ybGROZWVkc1VwZGF0ZSA9IHRydWU7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRdLCBbXG5cblx0XHRcdFx0Ly8gSGFzVG9Gcm9tQXJyYXlcblxuXHRcdFx0XHRmdW5jdGlvbiBzZXRWYWx1ZV9mcm9tQXJyYXkoIGJ1ZmZlciwgb2Zmc2V0ICkge1xuXG5cdFx0XHRcdFx0dGhpcy5yZXNvbHZlZFByb3BlcnR5LmZyb21BcnJheSggYnVmZmVyLCBvZmZzZXQgKTtcblxuXHRcdFx0XHR9LFxuXG5cdFx0XHRcdGZ1bmN0aW9uIHNldFZhbHVlX2Zyb21BcnJheV9zZXROZWVkc1VwZGF0ZSggYnVmZmVyLCBvZmZzZXQgKSB7XG5cblx0XHRcdFx0XHR0aGlzLnJlc29sdmVkUHJvcGVydHkuZnJvbUFycmF5KCBidWZmZXIsIG9mZnNldCApO1xuXHRcdFx0XHRcdHRoaXMudGFyZ2V0T2JqZWN0Lm5lZWRzVXBkYXRlID0gdHJ1ZTtcblxuXHRcdFx0XHR9LFxuXG5cdFx0XHRcdGZ1bmN0aW9uIHNldFZhbHVlX2Zyb21BcnJheV9zZXRNYXRyaXhXb3JsZE5lZWRzVXBkYXRlKCBidWZmZXIsIG9mZnNldCApIHtcblxuXHRcdFx0XHRcdHRoaXMucmVzb2x2ZWRQcm9wZXJ0eS5mcm9tQXJyYXkoIGJ1ZmZlciwgb2Zmc2V0ICk7XG5cdFx0XHRcdFx0dGhpcy50YXJnZXRPYmplY3QubWF0cml4V29ybGROZWVkc1VwZGF0ZSA9IHRydWU7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRdXG5cblx0XHRdXG5cblx0fSApO1xuXG5cdFByb3BlcnR5QmluZGluZy5Db21wb3NpdGUgPVxuXHRcdFx0ZnVuY3Rpb24oIHRhcmdldEdyb3VwLCBwYXRoLCBvcHRpb25hbFBhcnNlZFBhdGggKSB7XG5cblx0XHR2YXIgcGFyc2VkUGF0aCA9IG9wdGlvbmFsUGFyc2VkUGF0aCB8fFxuXHRcdFx0XHRQcm9wZXJ0eUJpbmRpbmcucGFyc2VUcmFja05hbWUoIHBhdGggKTtcblxuXHRcdHRoaXMuX3RhcmdldEdyb3VwID0gdGFyZ2V0R3JvdXA7XG5cdFx0dGhpcy5fYmluZGluZ3MgPSB0YXJnZXRHcm91cC5zdWJzY3JpYmVfKCBwYXRoLCBwYXJzZWRQYXRoICk7XG5cblx0fTtcblxuXHRQcm9wZXJ0eUJpbmRpbmcuQ29tcG9zaXRlLnByb3RvdHlwZSA9IHtcblxuXHRcdGNvbnN0cnVjdG9yOiBQcm9wZXJ0eUJpbmRpbmcuQ29tcG9zaXRlLFxuXG5cdFx0Z2V0VmFsdWU6IGZ1bmN0aW9uKCBhcnJheSwgb2Zmc2V0ICkge1xuXG5cdFx0XHR0aGlzLmJpbmQoKTsgLy8gYmluZCBhbGwgYmluZGluZ1xuXG5cdFx0XHR2YXIgZmlyc3RWYWxpZEluZGV4ID0gdGhpcy5fdGFyZ2V0R3JvdXAubkNhY2hlZE9iamVjdHNfLFxuXHRcdFx0XHRiaW5kaW5nID0gdGhpcy5fYmluZGluZ3NbIGZpcnN0VmFsaWRJbmRleCBdO1xuXG5cdFx0XHQvLyBhbmQgb25seSBjYWxsIC5nZXRWYWx1ZSBvbiB0aGUgZmlyc3Rcblx0XHRcdGlmICggYmluZGluZyAhPT0gdW5kZWZpbmVkICkgYmluZGluZy5nZXRWYWx1ZSggYXJyYXksIG9mZnNldCApO1xuXG5cdFx0fSxcblxuXHRcdHNldFZhbHVlOiBmdW5jdGlvbiggYXJyYXksIG9mZnNldCApIHtcblxuXHRcdFx0dmFyIGJpbmRpbmdzID0gdGhpcy5fYmluZGluZ3M7XG5cblx0XHRcdGZvciAoIHZhciBpID0gdGhpcy5fdGFyZ2V0R3JvdXAubkNhY2hlZE9iamVjdHNfLFxuXHRcdFx0XHRcdG4gPSBiaW5kaW5ncy5sZW5ndGg7IGkgIT09IG47ICsrIGkgKSB7XG5cblx0XHRcdFx0YmluZGluZ3NbIGkgXS5zZXRWYWx1ZSggYXJyYXksIG9mZnNldCApO1xuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0YmluZDogZnVuY3Rpb24oKSB7XG5cblx0XHRcdHZhciBiaW5kaW5ncyA9IHRoaXMuX2JpbmRpbmdzO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IHRoaXMuX3RhcmdldEdyb3VwLm5DYWNoZWRPYmplY3RzXyxcblx0XHRcdFx0XHRuID0gYmluZGluZ3MubGVuZ3RoOyBpICE9PSBuOyArKyBpICkge1xuXG5cdFx0XHRcdGJpbmRpbmdzWyBpIF0uYmluZCgpO1xuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0dW5iaW5kOiBmdW5jdGlvbigpIHtcblxuXHRcdFx0dmFyIGJpbmRpbmdzID0gdGhpcy5fYmluZGluZ3M7XG5cblx0XHRcdGZvciAoIHZhciBpID0gdGhpcy5fdGFyZ2V0R3JvdXAubkNhY2hlZE9iamVjdHNfLFxuXHRcdFx0XHRcdG4gPSBiaW5kaW5ncy5sZW5ndGg7IGkgIT09IG47ICsrIGkgKSB7XG5cblx0XHRcdFx0YmluZGluZ3NbIGkgXS51bmJpbmQoKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdH07XG5cblx0UHJvcGVydHlCaW5kaW5nLmNyZWF0ZSA9IGZ1bmN0aW9uKCByb290LCBwYXRoLCBwYXJzZWRQYXRoICkge1xuXG5cdFx0aWYgKCAhICggcm9vdCAmJiByb290LmlzQW5pbWF0aW9uT2JqZWN0R3JvdXAgKSApIHtcblxuXHRcdFx0cmV0dXJuIG5ldyBQcm9wZXJ0eUJpbmRpbmcoIHJvb3QsIHBhdGgsIHBhcnNlZFBhdGggKTtcblxuXHRcdH0gZWxzZSB7XG5cblx0XHRcdHJldHVybiBuZXcgUHJvcGVydHlCaW5kaW5nLkNvbXBvc2l0ZSggcm9vdCwgcGF0aCwgcGFyc2VkUGF0aCApO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0UHJvcGVydHlCaW5kaW5nLnBhcnNlVHJhY2tOYW1lID0gZnVuY3Rpb24oIHRyYWNrTmFtZSApIHtcblxuXHRcdC8vIG1hdGNoZXMgc3RyaW5ncyBpbiB0aGUgZm9ybSBvZjpcblx0XHQvLyAgICBub2RlTmFtZS5wcm9wZXJ0eVxuXHRcdC8vICAgIG5vZGVOYW1lLnByb3BlcnR5W2FjY2Vzc29yXVxuXHRcdC8vICAgIG5vZGVOYW1lLm1hdGVyaWFsLnByb3BlcnR5W2FjY2Vzc29yXVxuXHRcdC8vICAgIHV1aWQucHJvcGVydHlbYWNjZXNzb3JdXG5cdFx0Ly8gICAgdXVpZC5vYmplY3ROYW1lW29iamVjdEluZGV4XS5wcm9wZXJ0eU5hbWVbcHJvcGVydHlJbmRleF1cblx0XHQvLyAgICBwYXJlbnROYW1lL25vZGVOYW1lLnByb3BlcnR5XG5cdFx0Ly8gICAgcGFyZW50TmFtZS9wYXJlbnROYW1lL25vZGVOYW1lLnByb3BlcnR5W2luZGV4XVxuXHRcdC8vICAgIC5ib25lW0FybWF0dXJlLkRFRl9jb2ddLnBvc2l0aW9uXG5cdFx0Ly8gICAgc2NlbmU6aGVsaXVtX2JhbGxvb25fbW9kZWw6aGVsaXVtX2JhbGxvb25fbW9kZWwucG9zaXRpb25cblx0XHQvLyBjcmVhdGVkIGFuZCB0ZXN0ZWQgdmlhIGh0dHBzOi8vcmVnZXgxMDEuY29tLyNqYXZhc2NyaXB0XG5cblx0XHR2YXIgcmUgPSAvXigoPzpbXFx3LV0rW1xcLzpdKSopKFtcXHctXSspPyg/OlxcLihbXFx3LV0rKSg/OlxcWyguKylcXF0pPyk/XFwuKFtcXHctXSspKD86XFxbKC4rKVxcXSk/JC87XG5cdFx0dmFyIG1hdGNoZXMgPSByZS5leGVjKCB0cmFja05hbWUgKTtcblxuXHRcdGlmICggISBtYXRjaGVzICkge1xuXG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoIFwiY2Fubm90IHBhcnNlIHRyYWNrTmFtZSBhdCBhbGw6IFwiICsgdHJhY2tOYW1lICk7XG5cblx0XHR9XG5cblx0XHR2YXIgcmVzdWx0cyA9IHtcblx0XHRcdC8vIGRpcmVjdG9yeU5hbWU6IG1hdGNoZXNbIDEgXSwgLy8gKHRzY2h3KSBjdXJyZW50bHkgdW51c2VkXG5cdFx0XHRub2RlTmFtZTogbWF0Y2hlc1sgMiBdLCBcdC8vIGFsbG93ZWQgdG8gYmUgbnVsbCwgc3BlY2lmaWVkIHJvb3Qgbm9kZS5cblx0XHRcdG9iamVjdE5hbWU6IG1hdGNoZXNbIDMgXSxcblx0XHRcdG9iamVjdEluZGV4OiBtYXRjaGVzWyA0IF0sXG5cdFx0XHRwcm9wZXJ0eU5hbWU6IG1hdGNoZXNbIDUgXSxcblx0XHRcdHByb3BlcnR5SW5kZXg6IG1hdGNoZXNbIDYgXVx0Ly8gYWxsb3dlZCB0byBiZSBudWxsLCBzcGVjaWZpZXMgdGhhdCB0aGUgd2hvbGUgcHJvcGVydHkgaXMgc2V0LlxuXHRcdH07XG5cblx0XHRpZiAoIHJlc3VsdHMucHJvcGVydHlOYW1lID09PSBudWxsIHx8IHJlc3VsdHMucHJvcGVydHlOYW1lLmxlbmd0aCA9PT0gMCApIHtcblxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCBcImNhbiBub3QgcGFyc2UgcHJvcGVydHlOYW1lIGZyb20gdHJhY2tOYW1lOiBcIiArIHRyYWNrTmFtZSApO1xuXG5cdFx0fVxuXG5cdFx0cmV0dXJuIHJlc3VsdHM7XG5cblx0fTtcblxuXHRQcm9wZXJ0eUJpbmRpbmcuZmluZE5vZGUgPSBmdW5jdGlvbiggcm9vdCwgbm9kZU5hbWUgKSB7XG5cblx0XHRpZiAoICEgbm9kZU5hbWUgfHwgbm9kZU5hbWUgPT09IFwiXCIgfHwgbm9kZU5hbWUgPT09IFwicm9vdFwiIHx8IG5vZGVOYW1lID09PSBcIi5cIiB8fCBub2RlTmFtZSA9PT0gLTEgfHwgbm9kZU5hbWUgPT09IHJvb3QubmFtZSB8fCBub2RlTmFtZSA9PT0gcm9vdC51dWlkICkge1xuXG5cdFx0XHRyZXR1cm4gcm9vdDtcblxuXHRcdH1cblxuXHRcdC8vIHNlYXJjaCBpbnRvIHNrZWxldG9uIGJvbmVzLlxuXHRcdGlmICggcm9vdC5za2VsZXRvbiApIHtcblxuXHRcdFx0dmFyIHNlYXJjaFNrZWxldG9uID0gZnVuY3Rpb24oIHNrZWxldG9uICkge1xuXG5cdFx0XHRcdGZvciggdmFyIGkgPSAwOyBpIDwgc2tlbGV0b24uYm9uZXMubGVuZ3RoOyBpICsrICkge1xuXG5cdFx0XHRcdFx0dmFyIGJvbmUgPSBza2VsZXRvbi5ib25lc1sgaSBdO1xuXG5cdFx0XHRcdFx0aWYgKCBib25lLm5hbWUgPT09IG5vZGVOYW1lICkge1xuXG5cdFx0XHRcdFx0XHRyZXR1cm4gYm9uZTtcblxuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJldHVybiBudWxsO1xuXG5cdFx0XHR9O1xuXG5cdFx0XHR2YXIgYm9uZSA9IHNlYXJjaFNrZWxldG9uKCByb290LnNrZWxldG9uICk7XG5cblx0XHRcdGlmICggYm9uZSApIHtcblxuXHRcdFx0XHRyZXR1cm4gYm9uZTtcblxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIHNlYXJjaCBpbnRvIG5vZGUgc3VidHJlZS5cblx0XHRpZiAoIHJvb3QuY2hpbGRyZW4gKSB7XG5cblx0XHRcdHZhciBzZWFyY2hOb2RlU3VidHJlZSA9IGZ1bmN0aW9uKCBjaGlsZHJlbiApIHtcblxuXHRcdFx0XHRmb3IoIHZhciBpID0gMDsgaSA8IGNoaWxkcmVuLmxlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHRcdHZhciBjaGlsZE5vZGUgPSBjaGlsZHJlblsgaSBdO1xuXG5cdFx0XHRcdFx0aWYgKCBjaGlsZE5vZGUubmFtZSA9PT0gbm9kZU5hbWUgfHwgY2hpbGROb2RlLnV1aWQgPT09IG5vZGVOYW1lICkge1xuXG5cdFx0XHRcdFx0XHRyZXR1cm4gY2hpbGROb2RlO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0dmFyIHJlc3VsdCA9IHNlYXJjaE5vZGVTdWJ0cmVlKCBjaGlsZE5vZGUuY2hpbGRyZW4gKTtcblxuXHRcdFx0XHRcdGlmICggcmVzdWx0ICkgcmV0dXJuIHJlc3VsdDtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuIG51bGw7XG5cblx0XHRcdH07XG5cblx0XHRcdHZhciBzdWJUcmVlTm9kZSA9IHNlYXJjaE5vZGVTdWJ0cmVlKCByb290LmNoaWxkcmVuICk7XG5cblx0XHRcdGlmICggc3ViVHJlZU5vZGUgKSB7XG5cblx0XHRcdFx0cmV0dXJuIHN1YlRyZWVOb2RlO1xuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHRyZXR1cm4gbnVsbDtcblxuXHR9O1xuXG5cdC8qKlxuXHQgKlxuXHQgKiBBIGdyb3VwIG9mIG9iamVjdHMgdGhhdCByZWNlaXZlcyBhIHNoYXJlZCBhbmltYXRpb24gc3RhdGUuXG5cdCAqXG5cdCAqIFVzYWdlOlxuXHQgKlxuXHQgKiBcdC1cdEFkZCBvYmplY3RzIHlvdSB3b3VsZCBvdGhlcndpc2UgcGFzcyBhcyAncm9vdCcgdG8gdGhlXG5cdCAqIFx0XHRjb25zdHJ1Y3RvciBvciB0aGUgLmNsaXBBY3Rpb24gbWV0aG9kIG9mIEFuaW1hdGlvbk1peGVyLlxuXHQgKlxuXHQgKiBcdC1cdEluc3RlYWQgcGFzcyB0aGlzIG9iamVjdCBhcyAncm9vdCcuXG5cdCAqXG5cdCAqIFx0LVx0WW91IGNhbiBhbHNvIGFkZCBhbmQgcmVtb3ZlIG9iamVjdHMgbGF0ZXIgd2hlbiB0aGUgbWl4ZXJcblx0ICogXHRcdGlzIHJ1bm5pbmcuXG5cdCAqXG5cdCAqIE5vdGU6XG5cdCAqXG5cdCAqICBcdE9iamVjdHMgb2YgdGhpcyBjbGFzcyBhcHBlYXIgYXMgb25lIG9iamVjdCB0byB0aGUgbWl4ZXIsXG5cdCAqICBcdHNvIGNhY2hlIGNvbnRyb2wgb2YgdGhlIGluZGl2aWR1YWwgb2JqZWN0cyBtdXN0IGJlIGRvbmVcblx0ICogIFx0b24gdGhlIGdyb3VwLlxuXHQgKlxuXHQgKiBMaW1pdGF0aW9uOlxuXHQgKlxuXHQgKiBcdC0gXHRUaGUgYW5pbWF0ZWQgcHJvcGVydGllcyBtdXN0IGJlIGNvbXBhdGlibGUgYW1vbmcgdGhlXG5cdCAqIFx0XHRhbGwgb2JqZWN0cyBpbiB0aGUgZ3JvdXAuXG5cdCAqXG5cdCAqICAtXHRBIHNpbmdsZSBwcm9wZXJ0eSBjYW4gZWl0aGVyIGJlIGNvbnRyb2xsZWQgdGhyb3VnaCBhXG5cdCAqICBcdHRhcmdldCBncm91cCBvciBkaXJlY3RseSwgYnV0IG5vdCBib3RoLlxuXHQgKlxuXHQgKiBAYXV0aG9yIHRzY2h3XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEFuaW1hdGlvbk9iamVjdEdyb3VwKCB2YXJfYXJncyApIHtcblxuXHRcdHRoaXMudXVpZCA9IF9NYXRoLmdlbmVyYXRlVVVJRCgpO1xuXG5cdFx0Ly8gY2FjaGVkIG9iamVjdHMgZm9sbG93ZWQgYnkgdGhlIGFjdGl2ZSBvbmVzXG5cdFx0dGhpcy5fb2JqZWN0cyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKCBhcmd1bWVudHMgKTtcblxuXHRcdHRoaXMubkNhY2hlZE9iamVjdHNfID0gMDtcdFx0XHQvLyB0aHJlc2hvbGRcblx0XHQvLyBub3RlOiByZWFkIGJ5IFByb3BlcnR5QmluZGluZy5Db21wb3NpdGVcblxuXHRcdHZhciBpbmRpY2VzID0ge307XG5cdFx0dGhpcy5faW5kaWNlc0J5VVVJRCA9IGluZGljZXM7XHRcdC8vIGZvciBib29ra2VlcGluZ1xuXG5cdFx0Zm9yICggdmFyIGkgPSAwLCBuID0gYXJndW1lbnRzLmxlbmd0aDsgaSAhPT0gbjsgKysgaSApIHtcblxuXHRcdFx0aW5kaWNlc1sgYXJndW1lbnRzWyBpIF0udXVpZCBdID0gaTtcblxuXHRcdH1cblxuXHRcdHRoaXMuX3BhdGhzID0gW107XHRcdFx0XHRcdC8vIGluc2lkZTogc3RyaW5nXG5cdFx0dGhpcy5fcGFyc2VkUGF0aHMgPSBbXTtcdFx0XHRcdC8vIGluc2lkZTogeyB3ZSBkb24ndCBjYXJlLCBoZXJlIH1cblx0XHR0aGlzLl9iaW5kaW5ncyA9IFtdOyBcdFx0XHRcdC8vIGluc2lkZTogQXJyYXk8IFByb3BlcnR5QmluZGluZyA+XG5cdFx0dGhpcy5fYmluZGluZ3NJbmRpY2VzQnlQYXRoID0ge307IFx0Ly8gaW5zaWRlOiBpbmRpY2VzIGluIHRoZXNlIGFycmF5c1xuXG5cdFx0dmFyIHNjb3BlID0gdGhpcztcblxuXHRcdHRoaXMuc3RhdHMgPSB7XG5cblx0XHRcdG9iamVjdHM6IHtcblx0XHRcdFx0Z2V0IHRvdGFsKCkgeyByZXR1cm4gc2NvcGUuX29iamVjdHMubGVuZ3RoOyB9LFxuXHRcdFx0XHRnZXQgaW5Vc2UoKSB7IHJldHVybiB0aGlzLnRvdGFsIC0gc2NvcGUubkNhY2hlZE9iamVjdHNfOyAgfVxuXHRcdFx0fSxcblxuXHRcdFx0Z2V0IGJpbmRpbmdzUGVyT2JqZWN0KCkgeyByZXR1cm4gc2NvcGUuX2JpbmRpbmdzLmxlbmd0aDsgfVxuXG5cdFx0fTtcblxuXHR9XG5cblx0QW5pbWF0aW9uT2JqZWN0R3JvdXAucHJvdG90eXBlID0ge1xuXG5cdFx0Y29uc3RydWN0b3I6IEFuaW1hdGlvbk9iamVjdEdyb3VwLFxuXG5cdFx0aXNBbmltYXRpb25PYmplY3RHcm91cDogdHJ1ZSxcblxuXHRcdGFkZDogZnVuY3Rpb24oIHZhcl9hcmdzICkge1xuXG5cdFx0XHR2YXIgb2JqZWN0cyA9IHRoaXMuX29iamVjdHMsXG5cdFx0XHRcdG5PYmplY3RzID0gb2JqZWN0cy5sZW5ndGgsXG5cdFx0XHRcdG5DYWNoZWRPYmplY3RzID0gdGhpcy5uQ2FjaGVkT2JqZWN0c18sXG5cdFx0XHRcdGluZGljZXNCeVVVSUQgPSB0aGlzLl9pbmRpY2VzQnlVVUlELFxuXHRcdFx0XHRwYXRocyA9IHRoaXMuX3BhdGhzLFxuXHRcdFx0XHRwYXJzZWRQYXRocyA9IHRoaXMuX3BhcnNlZFBhdGhzLFxuXHRcdFx0XHRiaW5kaW5ncyA9IHRoaXMuX2JpbmRpbmdzLFxuXHRcdFx0XHRuQmluZGluZ3MgPSBiaW5kaW5ncy5sZW5ndGg7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbiA9IGFyZ3VtZW50cy5sZW5ndGg7IGkgIT09IG47ICsrIGkgKSB7XG5cblx0XHRcdFx0dmFyIG9iamVjdCA9IGFyZ3VtZW50c1sgaSBdLFxuXHRcdFx0XHRcdHV1aWQgPSBvYmplY3QudXVpZCxcblx0XHRcdFx0XHRpbmRleCA9IGluZGljZXNCeVVVSURbIHV1aWQgXSxcblx0XHRcdFx0XHRrbm93bk9iamVjdCA9IHVuZGVmaW5lZDtcblxuXHRcdFx0XHRpZiAoIGluZGV4ID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHQvLyB1bmtub3duIG9iamVjdCAtPiBhZGQgaXQgdG8gdGhlIEFDVElWRSByZWdpb25cblxuXHRcdFx0XHRcdGluZGV4ID0gbk9iamVjdHMgKys7XG5cdFx0XHRcdFx0aW5kaWNlc0J5VVVJRFsgdXVpZCBdID0gaW5kZXg7XG5cdFx0XHRcdFx0b2JqZWN0cy5wdXNoKCBvYmplY3QgKTtcblxuXHRcdFx0XHRcdC8vIGFjY291bnRpbmcgaXMgZG9uZSwgbm93IGRvIHRoZSBzYW1lIGZvciBhbGwgYmluZGluZ3NcblxuXHRcdFx0XHRcdGZvciAoIHZhciBqID0gMCwgbSA9IG5CaW5kaW5nczsgaiAhPT0gbTsgKysgaiApIHtcblxuXHRcdFx0XHRcdFx0YmluZGluZ3NbIGogXS5wdXNoKFxuXHRcdFx0XHRcdFx0XHRcdG5ldyBQcm9wZXJ0eUJpbmRpbmcoXG5cdFx0XHRcdFx0XHRcdFx0XHRvYmplY3QsIHBhdGhzWyBqIF0sIHBhcnNlZFBhdGhzWyBqIF0gKSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH0gZWxzZSBpZiAoIGluZGV4IDwgbkNhY2hlZE9iamVjdHMgKSB7XG5cblx0XHRcdFx0XHRrbm93bk9iamVjdCA9IG9iamVjdHNbIGluZGV4IF07XG5cblx0XHRcdFx0XHQvLyBtb3ZlIGV4aXN0aW5nIG9iamVjdCB0byB0aGUgQUNUSVZFIHJlZ2lvblxuXG5cdFx0XHRcdFx0dmFyIGZpcnN0QWN0aXZlSW5kZXggPSAtLSBuQ2FjaGVkT2JqZWN0cyxcblx0XHRcdFx0XHRcdGxhc3RDYWNoZWRPYmplY3QgPSBvYmplY3RzWyBmaXJzdEFjdGl2ZUluZGV4IF07XG5cblx0XHRcdFx0XHRpbmRpY2VzQnlVVUlEWyBsYXN0Q2FjaGVkT2JqZWN0LnV1aWQgXSA9IGluZGV4O1xuXHRcdFx0XHRcdG9iamVjdHNbIGluZGV4IF0gPSBsYXN0Q2FjaGVkT2JqZWN0O1xuXG5cdFx0XHRcdFx0aW5kaWNlc0J5VVVJRFsgdXVpZCBdID0gZmlyc3RBY3RpdmVJbmRleDtcblx0XHRcdFx0XHRvYmplY3RzWyBmaXJzdEFjdGl2ZUluZGV4IF0gPSBvYmplY3Q7XG5cblx0XHRcdFx0XHQvLyBhY2NvdW50aW5nIGlzIGRvbmUsIG5vdyBkbyB0aGUgc2FtZSBmb3IgYWxsIGJpbmRpbmdzXG5cblx0XHRcdFx0XHRmb3IgKCB2YXIgaiA9IDAsIG0gPSBuQmluZGluZ3M7IGogIT09IG07ICsrIGogKSB7XG5cblx0XHRcdFx0XHRcdHZhciBiaW5kaW5nc0ZvclBhdGggPSBiaW5kaW5nc1sgaiBdLFxuXHRcdFx0XHRcdFx0XHRsYXN0Q2FjaGVkID0gYmluZGluZ3NGb3JQYXRoWyBmaXJzdEFjdGl2ZUluZGV4IF0sXG5cdFx0XHRcdFx0XHRcdGJpbmRpbmcgPSBiaW5kaW5nc0ZvclBhdGhbIGluZGV4IF07XG5cblx0XHRcdFx0XHRcdGJpbmRpbmdzRm9yUGF0aFsgaW5kZXggXSA9IGxhc3RDYWNoZWQ7XG5cblx0XHRcdFx0XHRcdGlmICggYmluZGluZyA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0XHRcdC8vIHNpbmNlIHdlIGRvIG5vdCBib3RoZXIgdG8gY3JlYXRlIG5ldyBiaW5kaW5nc1xuXHRcdFx0XHRcdFx0XHQvLyBmb3Igb2JqZWN0cyB0aGF0IGFyZSBjYWNoZWQsIHRoZSBiaW5kaW5nIG1heVxuXHRcdFx0XHRcdFx0XHQvLyBvciBtYXkgbm90IGV4aXN0XG5cblx0XHRcdFx0XHRcdFx0YmluZGluZyA9IG5ldyBQcm9wZXJ0eUJpbmRpbmcoXG5cdFx0XHRcdFx0XHRcdFx0XHRvYmplY3QsIHBhdGhzWyBqIF0sIHBhcnNlZFBhdGhzWyBqIF0gKTtcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRiaW5kaW5nc0ZvclBhdGhbIGZpcnN0QWN0aXZlSW5kZXggXSA9IGJpbmRpbmc7XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fSBlbHNlIGlmICggb2JqZWN0c1sgaW5kZXggXSAhPT0ga25vd25PYmplY3QpIHtcblxuXHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IoIFwiRGlmZmVyZW50IG9iamVjdHMgd2l0aCB0aGUgc2FtZSBVVUlEIFwiICtcblx0XHRcdFx0XHRcdFx0XCJkZXRlY3RlZC4gQ2xlYW4gdGhlIGNhY2hlcyBvciByZWNyZWF0ZSB5b3VyIFwiICtcblx0XHRcdFx0XHRcdFx0XCJpbmZyYXN0cnVjdHVyZSB3aGVuIHJlbG9hZGluZyBzY2VuZXMuLi5cIiApO1xuXG5cdFx0XHRcdH0gLy8gZWxzZSB0aGUgb2JqZWN0IGlzIGFscmVhZHkgd2hlcmUgd2Ugd2FudCBpdCB0byBiZVxuXG5cdFx0XHR9IC8vIGZvciBhcmd1bWVudHNcblxuXHRcdFx0dGhpcy5uQ2FjaGVkT2JqZWN0c18gPSBuQ2FjaGVkT2JqZWN0cztcblxuXHRcdH0sXG5cblx0XHRyZW1vdmU6IGZ1bmN0aW9uKCB2YXJfYXJncyApIHtcblxuXHRcdFx0dmFyIG9iamVjdHMgPSB0aGlzLl9vYmplY3RzLFxuXHRcdFx0XHRuQ2FjaGVkT2JqZWN0cyA9IHRoaXMubkNhY2hlZE9iamVjdHNfLFxuXHRcdFx0XHRpbmRpY2VzQnlVVUlEID0gdGhpcy5faW5kaWNlc0J5VVVJRCxcblx0XHRcdFx0YmluZGluZ3MgPSB0aGlzLl9iaW5kaW5ncyxcblx0XHRcdFx0bkJpbmRpbmdzID0gYmluZGluZ3MubGVuZ3RoO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIG4gPSBhcmd1bWVudHMubGVuZ3RoOyBpICE9PSBuOyArKyBpICkge1xuXG5cdFx0XHRcdHZhciBvYmplY3QgPSBhcmd1bWVudHNbIGkgXSxcblx0XHRcdFx0XHR1dWlkID0gb2JqZWN0LnV1aWQsXG5cdFx0XHRcdFx0aW5kZXggPSBpbmRpY2VzQnlVVUlEWyB1dWlkIF07XG5cblx0XHRcdFx0aWYgKCBpbmRleCAhPT0gdW5kZWZpbmVkICYmIGluZGV4ID49IG5DYWNoZWRPYmplY3RzICkge1xuXG5cdFx0XHRcdFx0Ly8gbW92ZSBleGlzdGluZyBvYmplY3QgaW50byB0aGUgQ0FDSEVEIHJlZ2lvblxuXG5cdFx0XHRcdFx0dmFyIGxhc3RDYWNoZWRJbmRleCA9IG5DYWNoZWRPYmplY3RzICsrLFxuXHRcdFx0XHRcdFx0Zmlyc3RBY3RpdmVPYmplY3QgPSBvYmplY3RzWyBsYXN0Q2FjaGVkSW5kZXggXTtcblxuXHRcdFx0XHRcdGluZGljZXNCeVVVSURbIGZpcnN0QWN0aXZlT2JqZWN0LnV1aWQgXSA9IGluZGV4O1xuXHRcdFx0XHRcdG9iamVjdHNbIGluZGV4IF0gPSBmaXJzdEFjdGl2ZU9iamVjdDtcblxuXHRcdFx0XHRcdGluZGljZXNCeVVVSURbIHV1aWQgXSA9IGxhc3RDYWNoZWRJbmRleDtcblx0XHRcdFx0XHRvYmplY3RzWyBsYXN0Q2FjaGVkSW5kZXggXSA9IG9iamVjdDtcblxuXHRcdFx0XHRcdC8vIGFjY291bnRpbmcgaXMgZG9uZSwgbm93IGRvIHRoZSBzYW1lIGZvciBhbGwgYmluZGluZ3NcblxuXHRcdFx0XHRcdGZvciAoIHZhciBqID0gMCwgbSA9IG5CaW5kaW5nczsgaiAhPT0gbTsgKysgaiApIHtcblxuXHRcdFx0XHRcdFx0dmFyIGJpbmRpbmdzRm9yUGF0aCA9IGJpbmRpbmdzWyBqIF0sXG5cdFx0XHRcdFx0XHRcdGZpcnN0QWN0aXZlID0gYmluZGluZ3NGb3JQYXRoWyBsYXN0Q2FjaGVkSW5kZXggXSxcblx0XHRcdFx0XHRcdFx0YmluZGluZyA9IGJpbmRpbmdzRm9yUGF0aFsgaW5kZXggXTtcblxuXHRcdFx0XHRcdFx0YmluZGluZ3NGb3JQYXRoWyBpbmRleCBdID0gZmlyc3RBY3RpdmU7XG5cdFx0XHRcdFx0XHRiaW5kaW5nc0ZvclBhdGhbIGxhc3RDYWNoZWRJbmRleCBdID0gYmluZGluZztcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdH0gLy8gZm9yIGFyZ3VtZW50c1xuXG5cdFx0XHR0aGlzLm5DYWNoZWRPYmplY3RzXyA9IG5DYWNoZWRPYmplY3RzO1xuXG5cdFx0fSxcblxuXHRcdC8vIHJlbW92ZSAmIGZvcmdldFxuXHRcdHVuY2FjaGU6IGZ1bmN0aW9uKCB2YXJfYXJncyApIHtcblxuXHRcdFx0dmFyIG9iamVjdHMgPSB0aGlzLl9vYmplY3RzLFxuXHRcdFx0XHRuT2JqZWN0cyA9IG9iamVjdHMubGVuZ3RoLFxuXHRcdFx0XHRuQ2FjaGVkT2JqZWN0cyA9IHRoaXMubkNhY2hlZE9iamVjdHNfLFxuXHRcdFx0XHRpbmRpY2VzQnlVVUlEID0gdGhpcy5faW5kaWNlc0J5VVVJRCxcblx0XHRcdFx0YmluZGluZ3MgPSB0aGlzLl9iaW5kaW5ncyxcblx0XHRcdFx0bkJpbmRpbmdzID0gYmluZGluZ3MubGVuZ3RoO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIG4gPSBhcmd1bWVudHMubGVuZ3RoOyBpICE9PSBuOyArKyBpICkge1xuXG5cdFx0XHRcdHZhciBvYmplY3QgPSBhcmd1bWVudHNbIGkgXSxcblx0XHRcdFx0XHR1dWlkID0gb2JqZWN0LnV1aWQsXG5cdFx0XHRcdFx0aW5kZXggPSBpbmRpY2VzQnlVVUlEWyB1dWlkIF07XG5cblx0XHRcdFx0aWYgKCBpbmRleCAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0ZGVsZXRlIGluZGljZXNCeVVVSURbIHV1aWQgXTtcblxuXHRcdFx0XHRcdGlmICggaW5kZXggPCBuQ2FjaGVkT2JqZWN0cyApIHtcblxuXHRcdFx0XHRcdFx0Ly8gb2JqZWN0IGlzIGNhY2hlZCwgc2hyaW5rIHRoZSBDQUNIRUQgcmVnaW9uXG5cblx0XHRcdFx0XHRcdHZhciBmaXJzdEFjdGl2ZUluZGV4ID0gLS0gbkNhY2hlZE9iamVjdHMsXG5cdFx0XHRcdFx0XHRcdGxhc3RDYWNoZWRPYmplY3QgPSBvYmplY3RzWyBmaXJzdEFjdGl2ZUluZGV4IF0sXG5cdFx0XHRcdFx0XHRcdGxhc3RJbmRleCA9IC0tIG5PYmplY3RzLFxuXHRcdFx0XHRcdFx0XHRsYXN0T2JqZWN0ID0gb2JqZWN0c1sgbGFzdEluZGV4IF07XG5cblx0XHRcdFx0XHRcdC8vIGxhc3QgY2FjaGVkIG9iamVjdCB0YWtlcyB0aGlzIG9iamVjdCdzIHBsYWNlXG5cdFx0XHRcdFx0XHRpbmRpY2VzQnlVVUlEWyBsYXN0Q2FjaGVkT2JqZWN0LnV1aWQgXSA9IGluZGV4O1xuXHRcdFx0XHRcdFx0b2JqZWN0c1sgaW5kZXggXSA9IGxhc3RDYWNoZWRPYmplY3Q7XG5cblx0XHRcdFx0XHRcdC8vIGxhc3Qgb2JqZWN0IGdvZXMgdG8gdGhlIGFjdGl2YXRlZCBzbG90IGFuZCBwb3Bcblx0XHRcdFx0XHRcdGluZGljZXNCeVVVSURbIGxhc3RPYmplY3QudXVpZCBdID0gZmlyc3RBY3RpdmVJbmRleDtcblx0XHRcdFx0XHRcdG9iamVjdHNbIGZpcnN0QWN0aXZlSW5kZXggXSA9IGxhc3RPYmplY3Q7XG5cdFx0XHRcdFx0XHRvYmplY3RzLnBvcCgpO1xuXG5cdFx0XHRcdFx0XHQvLyBhY2NvdW50aW5nIGlzIGRvbmUsIG5vdyBkbyB0aGUgc2FtZSBmb3IgYWxsIGJpbmRpbmdzXG5cblx0XHRcdFx0XHRcdGZvciAoIHZhciBqID0gMCwgbSA9IG5CaW5kaW5nczsgaiAhPT0gbTsgKysgaiApIHtcblxuXHRcdFx0XHRcdFx0XHR2YXIgYmluZGluZ3NGb3JQYXRoID0gYmluZGluZ3NbIGogXSxcblx0XHRcdFx0XHRcdFx0XHRsYXN0Q2FjaGVkID0gYmluZGluZ3NGb3JQYXRoWyBmaXJzdEFjdGl2ZUluZGV4IF0sXG5cdFx0XHRcdFx0XHRcdFx0bGFzdCA9IGJpbmRpbmdzRm9yUGF0aFsgbGFzdEluZGV4IF07XG5cblx0XHRcdFx0XHRcdFx0YmluZGluZ3NGb3JQYXRoWyBpbmRleCBdID0gbGFzdENhY2hlZDtcblx0XHRcdFx0XHRcdFx0YmluZGluZ3NGb3JQYXRoWyBmaXJzdEFjdGl2ZUluZGV4IF0gPSBsYXN0O1xuXHRcdFx0XHRcdFx0XHRiaW5kaW5nc0ZvclBhdGgucG9wKCk7XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdC8vIG9iamVjdCBpcyBhY3RpdmUsIGp1c3Qgc3dhcCB3aXRoIHRoZSBsYXN0IGFuZCBwb3BcblxuXHRcdFx0XHRcdFx0dmFyIGxhc3RJbmRleCA9IC0tIG5PYmplY3RzLFxuXHRcdFx0XHRcdFx0XHRsYXN0T2JqZWN0ID0gb2JqZWN0c1sgbGFzdEluZGV4IF07XG5cblx0XHRcdFx0XHRcdGluZGljZXNCeVVVSURbIGxhc3RPYmplY3QudXVpZCBdID0gaW5kZXg7XG5cdFx0XHRcdFx0XHRvYmplY3RzWyBpbmRleCBdID0gbGFzdE9iamVjdDtcblx0XHRcdFx0XHRcdG9iamVjdHMucG9wKCk7XG5cblx0XHRcdFx0XHRcdC8vIGFjY291bnRpbmcgaXMgZG9uZSwgbm93IGRvIHRoZSBzYW1lIGZvciBhbGwgYmluZGluZ3NcblxuXHRcdFx0XHRcdFx0Zm9yICggdmFyIGogPSAwLCBtID0gbkJpbmRpbmdzOyBqICE9PSBtOyArKyBqICkge1xuXG5cdFx0XHRcdFx0XHRcdHZhciBiaW5kaW5nc0ZvclBhdGggPSBiaW5kaW5nc1sgaiBdO1xuXG5cdFx0XHRcdFx0XHRcdGJpbmRpbmdzRm9yUGF0aFsgaW5kZXggXSA9IGJpbmRpbmdzRm9yUGF0aFsgbGFzdEluZGV4IF07XG5cdFx0XHRcdFx0XHRcdGJpbmRpbmdzRm9yUGF0aC5wb3AoKTtcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fSAvLyBjYWNoZWQgb3IgYWN0aXZlXG5cblx0XHRcdFx0fSAvLyBpZiBvYmplY3QgaXMga25vd25cblxuXHRcdFx0fSAvLyBmb3IgYXJndW1lbnRzXG5cblx0XHRcdHRoaXMubkNhY2hlZE9iamVjdHNfID0gbkNhY2hlZE9iamVjdHM7XG5cblx0XHR9LFxuXG5cdFx0Ly8gSW50ZXJuYWwgaW50ZXJmYWNlIHVzZWQgYnkgYmVmcmllbmRlZCBQcm9wZXJ0eUJpbmRpbmcuQ29tcG9zaXRlOlxuXG5cdFx0c3Vic2NyaWJlXzogZnVuY3Rpb24oIHBhdGgsIHBhcnNlZFBhdGggKSB7XG5cdFx0XHQvLyByZXR1cm5zIGFuIGFycmF5IG9mIGJpbmRpbmdzIGZvciB0aGUgZ2l2ZW4gcGF0aCB0aGF0IGlzIGNoYW5nZWRcblx0XHRcdC8vIGFjY29yZGluZyB0byB0aGUgY29udGFpbmVkIG9iamVjdHMgaW4gdGhlIGdyb3VwXG5cblx0XHRcdHZhciBpbmRpY2VzQnlQYXRoID0gdGhpcy5fYmluZGluZ3NJbmRpY2VzQnlQYXRoLFxuXHRcdFx0XHRpbmRleCA9IGluZGljZXNCeVBhdGhbIHBhdGggXSxcblx0XHRcdFx0YmluZGluZ3MgPSB0aGlzLl9iaW5kaW5ncztcblxuXHRcdFx0aWYgKCBpbmRleCAhPT0gdW5kZWZpbmVkICkgcmV0dXJuIGJpbmRpbmdzWyBpbmRleCBdO1xuXG5cdFx0XHR2YXIgcGF0aHMgPSB0aGlzLl9wYXRocyxcblx0XHRcdFx0cGFyc2VkUGF0aHMgPSB0aGlzLl9wYXJzZWRQYXRocyxcblx0XHRcdFx0b2JqZWN0cyA9IHRoaXMuX29iamVjdHMsXG5cdFx0XHRcdG5PYmplY3RzID0gb2JqZWN0cy5sZW5ndGgsXG5cdFx0XHRcdG5DYWNoZWRPYmplY3RzID0gdGhpcy5uQ2FjaGVkT2JqZWN0c18sXG5cdFx0XHRcdGJpbmRpbmdzRm9yUGF0aCA9IG5ldyBBcnJheSggbk9iamVjdHMgKTtcblxuXHRcdFx0aW5kZXggPSBiaW5kaW5ncy5sZW5ndGg7XG5cblx0XHRcdGluZGljZXNCeVBhdGhbIHBhdGggXSA9IGluZGV4O1xuXG5cdFx0XHRwYXRocy5wdXNoKCBwYXRoICk7XG5cdFx0XHRwYXJzZWRQYXRocy5wdXNoKCBwYXJzZWRQYXRoICk7XG5cdFx0XHRiaW5kaW5ncy5wdXNoKCBiaW5kaW5nc0ZvclBhdGggKTtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSBuQ2FjaGVkT2JqZWN0cyxcblx0XHRcdFx0XHRuID0gb2JqZWN0cy5sZW5ndGg7IGkgIT09IG47ICsrIGkgKSB7XG5cblx0XHRcdFx0dmFyIG9iamVjdCA9IG9iamVjdHNbIGkgXTtcblxuXHRcdFx0XHRiaW5kaW5nc0ZvclBhdGhbIGkgXSA9XG5cdFx0XHRcdFx0XHRuZXcgUHJvcGVydHlCaW5kaW5nKCBvYmplY3QsIHBhdGgsIHBhcnNlZFBhdGggKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gYmluZGluZ3NGb3JQYXRoO1xuXG5cdFx0fSxcblxuXHRcdHVuc3Vic2NyaWJlXzogZnVuY3Rpb24oIHBhdGggKSB7XG5cdFx0XHQvLyB0ZWxscyB0aGUgZ3JvdXAgdG8gZm9yZ2V0IGFib3V0IGEgcHJvcGVydHkgcGF0aCBhbmQgbm8gbG9uZ2VyXG5cdFx0XHQvLyB1cGRhdGUgdGhlIGFycmF5IHByZXZpb3VzbHkgb2J0YWluZWQgd2l0aCAnc3Vic2NyaWJlXydcblxuXHRcdFx0dmFyIGluZGljZXNCeVBhdGggPSB0aGlzLl9iaW5kaW5nc0luZGljZXNCeVBhdGgsXG5cdFx0XHRcdGluZGV4ID0gaW5kaWNlc0J5UGF0aFsgcGF0aCBdO1xuXG5cdFx0XHRpZiAoIGluZGV4ICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0dmFyIHBhdGhzID0gdGhpcy5fcGF0aHMsXG5cdFx0XHRcdFx0cGFyc2VkUGF0aHMgPSB0aGlzLl9wYXJzZWRQYXRocyxcblx0XHRcdFx0XHRiaW5kaW5ncyA9IHRoaXMuX2JpbmRpbmdzLFxuXHRcdFx0XHRcdGxhc3RCaW5kaW5nc0luZGV4ID0gYmluZGluZ3MubGVuZ3RoIC0gMSxcblx0XHRcdFx0XHRsYXN0QmluZGluZ3MgPSBiaW5kaW5nc1sgbGFzdEJpbmRpbmdzSW5kZXggXSxcblx0XHRcdFx0XHRsYXN0QmluZGluZ3NQYXRoID0gcGF0aFsgbGFzdEJpbmRpbmdzSW5kZXggXTtcblxuXHRcdFx0XHRpbmRpY2VzQnlQYXRoWyBsYXN0QmluZGluZ3NQYXRoIF0gPSBpbmRleDtcblxuXHRcdFx0XHRiaW5kaW5nc1sgaW5kZXggXSA9IGxhc3RCaW5kaW5ncztcblx0XHRcdFx0YmluZGluZ3MucG9wKCk7XG5cblx0XHRcdFx0cGFyc2VkUGF0aHNbIGluZGV4IF0gPSBwYXJzZWRQYXRoc1sgbGFzdEJpbmRpbmdzSW5kZXggXTtcblx0XHRcdFx0cGFyc2VkUGF0aHMucG9wKCk7XG5cblx0XHRcdFx0cGF0aHNbIGluZGV4IF0gPSBwYXRoc1sgbGFzdEJpbmRpbmdzSW5kZXggXTtcblx0XHRcdFx0cGF0aHMucG9wKCk7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHR9O1xuXG5cdC8qKlxuXHQgKlxuXHQgKiBBY3Rpb24gcHJvdmlkZWQgYnkgQW5pbWF0aW9uTWl4ZXIgZm9yIHNjaGVkdWxpbmcgY2xpcCBwbGF5YmFjayBvbiBzcGVjaWZpY1xuXHQgKiBvYmplY3RzLlxuXHQgKlxuXHQgKiBAYXV0aG9yIEJlbiBIb3VzdG9uIC8gaHR0cDovL2NsYXJhLmlvL1xuXHQgKiBAYXV0aG9yIERhdmlkIFNhcm5vIC8gaHR0cDovL2xpZ2h0aGF1cy51cy9cblx0ICogQGF1dGhvciB0c2Nod1xuXHQgKlxuXHQgKi9cblxuXHRmdW5jdGlvbiBBbmltYXRpb25BY3Rpb24oIG1peGVyLCBjbGlwLCBsb2NhbFJvb3QgKSB7XG5cblx0XHR0aGlzLl9taXhlciA9IG1peGVyO1xuXHRcdHRoaXMuX2NsaXAgPSBjbGlwO1xuXHRcdHRoaXMuX2xvY2FsUm9vdCA9IGxvY2FsUm9vdCB8fCBudWxsO1xuXG5cdFx0dmFyIHRyYWNrcyA9IGNsaXAudHJhY2tzLFxuXHRcdFx0blRyYWNrcyA9IHRyYWNrcy5sZW5ndGgsXG5cdFx0XHRpbnRlcnBvbGFudHMgPSBuZXcgQXJyYXkoIG5UcmFja3MgKTtcblxuXHRcdHZhciBpbnRlcnBvbGFudFNldHRpbmdzID0ge1xuXHRcdFx0XHRlbmRpbmdTdGFydDogXHRaZXJvQ3VydmF0dXJlRW5kaW5nLFxuXHRcdFx0XHRlbmRpbmdFbmQ6XHRcdFplcm9DdXJ2YXR1cmVFbmRpbmdcblx0XHR9O1xuXG5cdFx0Zm9yICggdmFyIGkgPSAwOyBpICE9PSBuVHJhY2tzOyArKyBpICkge1xuXG5cdFx0XHR2YXIgaW50ZXJwb2xhbnQgPSB0cmFja3NbIGkgXS5jcmVhdGVJbnRlcnBvbGFudCggbnVsbCApO1xuXHRcdFx0aW50ZXJwb2xhbnRzWyBpIF0gPSBpbnRlcnBvbGFudDtcblx0XHRcdGludGVycG9sYW50LnNldHRpbmdzID0gaW50ZXJwb2xhbnRTZXR0aW5ncztcblxuXHRcdH1cblxuXHRcdHRoaXMuX2ludGVycG9sYW50U2V0dGluZ3MgPSBpbnRlcnBvbGFudFNldHRpbmdzO1xuXG5cdFx0dGhpcy5faW50ZXJwb2xhbnRzID0gaW50ZXJwb2xhbnRzO1x0Ly8gYm91bmQgYnkgdGhlIG1peGVyXG5cblx0XHQvLyBpbnNpZGU6IFByb3BlcnR5TWl4ZXIgKG1hbmFnZWQgYnkgdGhlIG1peGVyKVxuXHRcdHRoaXMuX3Byb3BlcnR5QmluZGluZ3MgPSBuZXcgQXJyYXkoIG5UcmFja3MgKTtcblxuXHRcdHRoaXMuX2NhY2hlSW5kZXggPSBudWxsO1x0XHRcdC8vIGZvciB0aGUgbWVtb3J5IG1hbmFnZXJcblx0XHR0aGlzLl9ieUNsaXBDYWNoZUluZGV4ID0gbnVsbDtcdFx0Ly8gZm9yIHRoZSBtZW1vcnkgbWFuYWdlclxuXG5cdFx0dGhpcy5fdGltZVNjYWxlSW50ZXJwb2xhbnQgPSBudWxsO1xuXHRcdHRoaXMuX3dlaWdodEludGVycG9sYW50ID0gbnVsbDtcblxuXHRcdHRoaXMubG9vcCA9IExvb3BSZXBlYXQ7XG5cdFx0dGhpcy5fbG9vcENvdW50ID0gLTE7XG5cblx0XHQvLyBnbG9iYWwgbWl4ZXIgdGltZSB3aGVuIHRoZSBhY3Rpb24gaXMgdG8gYmUgc3RhcnRlZFxuXHRcdC8vIGl0J3Mgc2V0IGJhY2sgdG8gJ251bGwnIHVwb24gc3RhcnQgb2YgdGhlIGFjdGlvblxuXHRcdHRoaXMuX3N0YXJ0VGltZSA9IG51bGw7XG5cblx0XHQvLyBzY2FsZWQgbG9jYWwgdGltZSBvZiB0aGUgYWN0aW9uXG5cdFx0Ly8gZ2V0cyBjbGFtcGVkIG9yIHdyYXBwZWQgdG8gMC4uY2xpcC5kdXJhdGlvbiBhY2NvcmRpbmcgdG8gbG9vcFxuXHRcdHRoaXMudGltZSA9IDA7XG5cblx0XHR0aGlzLnRpbWVTY2FsZSA9IDE7XG5cdFx0dGhpcy5fZWZmZWN0aXZlVGltZVNjYWxlID0gMTtcblxuXHRcdHRoaXMud2VpZ2h0ID0gMTtcblx0XHR0aGlzLl9lZmZlY3RpdmVXZWlnaHQgPSAxO1xuXG5cdFx0dGhpcy5yZXBldGl0aW9ucyA9IEluZmluaXR5OyBcdFx0Ly8gbm8uIG9mIHJlcGV0aXRpb25zIHdoZW4gbG9vcGluZ1xuXG5cdFx0dGhpcy5wYXVzZWQgPSBmYWxzZTtcdFx0XHRcdC8vIGZhbHNlIC0+IHplcm8gZWZmZWN0aXZlIHRpbWUgc2NhbGVcblx0XHR0aGlzLmVuYWJsZWQgPSB0cnVlO1x0XHRcdFx0Ly8gdHJ1ZSAtPiB6ZXJvIGVmZmVjdGl2ZSB3ZWlnaHRcblxuXHRcdHRoaXMuY2xhbXBXaGVuRmluaXNoZWQgXHQ9IGZhbHNlO1x0Ly8ga2VlcCBmZWVkaW5nIHRoZSBsYXN0IGZyYW1lP1xuXG5cdFx0dGhpcy56ZXJvU2xvcGVBdFN0YXJ0IFx0PSB0cnVlO1x0XHQvLyBmb3Igc21vb3RoIGludGVycG9sYXRpb24gdy9vIHNlcGFyYXRlXG5cdFx0dGhpcy56ZXJvU2xvcGVBdEVuZFx0XHQ9IHRydWU7XHRcdC8vIGNsaXBzIGZvciBzdGFydCwgbG9vcCBhbmQgZW5kXG5cblx0fVxuXG5cdEFuaW1hdGlvbkFjdGlvbi5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogQW5pbWF0aW9uQWN0aW9uLFxuXG5cdFx0Ly8gU3RhdGUgJiBTY2hlZHVsaW5nXG5cblx0XHRwbGF5OiBmdW5jdGlvbigpIHtcblxuXHRcdFx0dGhpcy5fbWl4ZXIuX2FjdGl2YXRlQWN0aW9uKCB0aGlzICk7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHN0b3A6IGZ1bmN0aW9uKCkge1xuXG5cdFx0XHR0aGlzLl9taXhlci5fZGVhY3RpdmF0ZUFjdGlvbiggdGhpcyApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5yZXNldCgpO1xuXG5cdFx0fSxcblxuXHRcdHJlc2V0OiBmdW5jdGlvbigpIHtcblxuXHRcdFx0dGhpcy5wYXVzZWQgPSBmYWxzZTtcblx0XHRcdHRoaXMuZW5hYmxlZCA9IHRydWU7XG5cblx0XHRcdHRoaXMudGltZSA9IDA7XHRcdFx0Ly8gcmVzdGFydCBjbGlwXG5cdFx0XHR0aGlzLl9sb29wQ291bnQgPSAtMTtcdC8vIGZvcmdldCBwcmV2aW91cyBsb29wc1xuXHRcdFx0dGhpcy5fc3RhcnRUaW1lID0gbnVsbDtcdC8vIGZvcmdldCBzY2hlZHVsaW5nXG5cblx0XHRcdHJldHVybiB0aGlzLnN0b3BGYWRpbmcoKS5zdG9wV2FycGluZygpO1xuXG5cdFx0fSxcblxuXHRcdGlzUnVubmluZzogZnVuY3Rpb24oKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLmVuYWJsZWQgJiYgISB0aGlzLnBhdXNlZCAmJiB0aGlzLnRpbWVTY2FsZSAhPT0gMCAmJlxuXHRcdFx0XHRcdHRoaXMuX3N0YXJ0VGltZSA9PT0gbnVsbCAmJiB0aGlzLl9taXhlci5faXNBY3RpdmVBY3Rpb24oIHRoaXMgKTtcblxuXHRcdH0sXG5cblx0XHQvLyByZXR1cm4gdHJ1ZSB3aGVuIHBsYXkgaGFzIGJlZW4gY2FsbGVkXG5cdFx0aXNTY2hlZHVsZWQ6IGZ1bmN0aW9uKCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5fbWl4ZXIuX2lzQWN0aXZlQWN0aW9uKCB0aGlzICk7XG5cblx0XHR9LFxuXG5cdFx0c3RhcnRBdDogZnVuY3Rpb24oIHRpbWUgKSB7XG5cblx0XHRcdHRoaXMuX3N0YXJ0VGltZSA9IHRpbWU7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldExvb3A6IGZ1bmN0aW9uKCBtb2RlLCByZXBldGl0aW9ucyApIHtcblxuXHRcdFx0dGhpcy5sb29wID0gbW9kZTtcblx0XHRcdHRoaXMucmVwZXRpdGlvbnMgPSByZXBldGl0aW9ucztcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Ly8gV2VpZ2h0XG5cblx0XHQvLyBzZXQgdGhlIHdlaWdodCBzdG9wcGluZyBhbnkgc2NoZWR1bGVkIGZhZGluZ1xuXHRcdC8vIGFsdGhvdWdoIC5lbmFibGVkID0gZmFsc2UgeWllbGRzIGFuIGVmZmVjdGl2ZSB3ZWlnaHQgb2YgemVybywgdGhpc1xuXHRcdC8vIG1ldGhvZCBkb2VzICpub3QqIGNoYW5nZSAuZW5hYmxlZCwgYmVjYXVzZSBpdCB3b3VsZCBiZSBjb25mdXNpbmdcblx0XHRzZXRFZmZlY3RpdmVXZWlnaHQ6IGZ1bmN0aW9uKCB3ZWlnaHQgKSB7XG5cblx0XHRcdHRoaXMud2VpZ2h0ID0gd2VpZ2h0O1xuXG5cdFx0XHQvLyBub3RlOiBzYW1lIGxvZ2ljIGFzIHdoZW4gdXBkYXRlZCBhdCBydW50aW1lXG5cdFx0XHR0aGlzLl9lZmZlY3RpdmVXZWlnaHQgPSB0aGlzLmVuYWJsZWQgPyB3ZWlnaHQgOiAwO1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5zdG9wRmFkaW5nKCk7XG5cblx0XHR9LFxuXG5cdFx0Ly8gcmV0dXJuIHRoZSB3ZWlnaHQgY29uc2lkZXJpbmcgZmFkaW5nIGFuZCAuZW5hYmxlZFxuXHRcdGdldEVmZmVjdGl2ZVdlaWdodDogZnVuY3Rpb24oKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLl9lZmZlY3RpdmVXZWlnaHQ7XG5cblx0XHR9LFxuXG5cdFx0ZmFkZUluOiBmdW5jdGlvbiggZHVyYXRpb24gKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLl9zY2hlZHVsZUZhZGluZyggZHVyYXRpb24sIDAsIDEgKTtcblxuXHRcdH0sXG5cblx0XHRmYWRlT3V0OiBmdW5jdGlvbiggZHVyYXRpb24gKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLl9zY2hlZHVsZUZhZGluZyggZHVyYXRpb24sIDEsIDAgKTtcblxuXHRcdH0sXG5cblx0XHRjcm9zc0ZhZGVGcm9tOiBmdW5jdGlvbiggZmFkZU91dEFjdGlvbiwgZHVyYXRpb24sIHdhcnAgKSB7XG5cblx0XHRcdGZhZGVPdXRBY3Rpb24uZmFkZU91dCggZHVyYXRpb24gKTtcblx0XHRcdHRoaXMuZmFkZUluKCBkdXJhdGlvbiApO1xuXG5cdFx0XHRpZiggd2FycCApIHtcblxuXHRcdFx0XHR2YXIgZmFkZUluRHVyYXRpb24gPSB0aGlzLl9jbGlwLmR1cmF0aW9uLFxuXHRcdFx0XHRcdGZhZGVPdXREdXJhdGlvbiA9IGZhZGVPdXRBY3Rpb24uX2NsaXAuZHVyYXRpb24sXG5cblx0XHRcdFx0XHRzdGFydEVuZFJhdGlvID0gZmFkZU91dER1cmF0aW9uIC8gZmFkZUluRHVyYXRpb24sXG5cdFx0XHRcdFx0ZW5kU3RhcnRSYXRpbyA9IGZhZGVJbkR1cmF0aW9uIC8gZmFkZU91dER1cmF0aW9uO1xuXG5cdFx0XHRcdGZhZGVPdXRBY3Rpb24ud2FycCggMS4wLCBzdGFydEVuZFJhdGlvLCBkdXJhdGlvbiApO1xuXHRcdFx0XHR0aGlzLndhcnAoIGVuZFN0YXJ0UmF0aW8sIDEuMCwgZHVyYXRpb24gKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjcm9zc0ZhZGVUbzogZnVuY3Rpb24oIGZhZGVJbkFjdGlvbiwgZHVyYXRpb24sIHdhcnAgKSB7XG5cblx0XHRcdHJldHVybiBmYWRlSW5BY3Rpb24uY3Jvc3NGYWRlRnJvbSggdGhpcywgZHVyYXRpb24sIHdhcnAgKTtcblxuXHRcdH0sXG5cblx0XHRzdG9wRmFkaW5nOiBmdW5jdGlvbigpIHtcblxuXHRcdFx0dmFyIHdlaWdodEludGVycG9sYW50ID0gdGhpcy5fd2VpZ2h0SW50ZXJwb2xhbnQ7XG5cblx0XHRcdGlmICggd2VpZ2h0SW50ZXJwb2xhbnQgIT09IG51bGwgKSB7XG5cblx0XHRcdFx0dGhpcy5fd2VpZ2h0SW50ZXJwb2xhbnQgPSBudWxsO1xuXHRcdFx0XHR0aGlzLl9taXhlci5fdGFrZUJhY2tDb250cm9sSW50ZXJwb2xhbnQoIHdlaWdodEludGVycG9sYW50ICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Ly8gVGltZSBTY2FsZSBDb250cm9sXG5cblx0XHQvLyBzZXQgdGhlIHdlaWdodCBzdG9wcGluZyBhbnkgc2NoZWR1bGVkIHdhcnBpbmdcblx0XHQvLyBhbHRob3VnaCAucGF1c2VkID0gdHJ1ZSB5aWVsZHMgYW4gZWZmZWN0aXZlIHRpbWUgc2NhbGUgb2YgemVybywgdGhpc1xuXHRcdC8vIG1ldGhvZCBkb2VzICpub3QqIGNoYW5nZSAucGF1c2VkLCBiZWNhdXNlIGl0IHdvdWxkIGJlIGNvbmZ1c2luZ1xuXHRcdHNldEVmZmVjdGl2ZVRpbWVTY2FsZTogZnVuY3Rpb24oIHRpbWVTY2FsZSApIHtcblxuXHRcdFx0dGhpcy50aW1lU2NhbGUgPSB0aW1lU2NhbGU7XG5cdFx0XHR0aGlzLl9lZmZlY3RpdmVUaW1lU2NhbGUgPSB0aGlzLnBhdXNlZCA/IDAgOnRpbWVTY2FsZTtcblxuXHRcdFx0cmV0dXJuIHRoaXMuc3RvcFdhcnBpbmcoKTtcblxuXHRcdH0sXG5cblx0XHQvLyByZXR1cm4gdGhlIHRpbWUgc2NhbGUgY29uc2lkZXJpbmcgd2FycGluZyBhbmQgLnBhdXNlZFxuXHRcdGdldEVmZmVjdGl2ZVRpbWVTY2FsZTogZnVuY3Rpb24oKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLl9lZmZlY3RpdmVUaW1lU2NhbGU7XG5cblx0XHR9LFxuXG5cdFx0c2V0RHVyYXRpb246IGZ1bmN0aW9uKCBkdXJhdGlvbiApIHtcblxuXHRcdFx0dGhpcy50aW1lU2NhbGUgPSB0aGlzLl9jbGlwLmR1cmF0aW9uIC8gZHVyYXRpb247XG5cblx0XHRcdHJldHVybiB0aGlzLnN0b3BXYXJwaW5nKCk7XG5cblx0XHR9LFxuXG5cdFx0c3luY1dpdGg6IGZ1bmN0aW9uKCBhY3Rpb24gKSB7XG5cblx0XHRcdHRoaXMudGltZSA9IGFjdGlvbi50aW1lO1xuXHRcdFx0dGhpcy50aW1lU2NhbGUgPSBhY3Rpb24udGltZVNjYWxlO1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5zdG9wV2FycGluZygpO1xuXG5cdFx0fSxcblxuXHRcdGhhbHQ6IGZ1bmN0aW9uKCBkdXJhdGlvbiApIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMud2FycCggdGhpcy5fZWZmZWN0aXZlVGltZVNjYWxlLCAwLCBkdXJhdGlvbiApO1xuXG5cdFx0fSxcblxuXHRcdHdhcnA6IGZ1bmN0aW9uKCBzdGFydFRpbWVTY2FsZSwgZW5kVGltZVNjYWxlLCBkdXJhdGlvbiApIHtcblxuXHRcdFx0dmFyIG1peGVyID0gdGhpcy5fbWl4ZXIsIG5vdyA9IG1peGVyLnRpbWUsXG5cdFx0XHRcdGludGVycG9sYW50ID0gdGhpcy5fdGltZVNjYWxlSW50ZXJwb2xhbnQsXG5cblx0XHRcdFx0dGltZVNjYWxlID0gdGhpcy50aW1lU2NhbGU7XG5cblx0XHRcdGlmICggaW50ZXJwb2xhbnQgPT09IG51bGwgKSB7XG5cblx0XHRcdFx0aW50ZXJwb2xhbnQgPSBtaXhlci5fbGVuZENvbnRyb2xJbnRlcnBvbGFudCgpO1xuXHRcdFx0XHR0aGlzLl90aW1lU2NhbGVJbnRlcnBvbGFudCA9IGludGVycG9sYW50O1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciB0aW1lcyA9IGludGVycG9sYW50LnBhcmFtZXRlclBvc2l0aW9ucyxcblx0XHRcdFx0dmFsdWVzID0gaW50ZXJwb2xhbnQuc2FtcGxlVmFsdWVzO1xuXG5cdFx0XHR0aW1lc1sgMCBdID0gbm93O1xuXHRcdFx0dGltZXNbIDEgXSA9IG5vdyArIGR1cmF0aW9uO1xuXG5cdFx0XHR2YWx1ZXNbIDAgXSA9IHN0YXJ0VGltZVNjYWxlIC8gdGltZVNjYWxlO1xuXHRcdFx0dmFsdWVzWyAxIF0gPSBlbmRUaW1lU2NhbGUgLyB0aW1lU2NhbGU7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHN0b3BXYXJwaW5nOiBmdW5jdGlvbigpIHtcblxuXHRcdFx0dmFyIHRpbWVTY2FsZUludGVycG9sYW50ID0gdGhpcy5fdGltZVNjYWxlSW50ZXJwb2xhbnQ7XG5cblx0XHRcdGlmICggdGltZVNjYWxlSW50ZXJwb2xhbnQgIT09IG51bGwgKSB7XG5cblx0XHRcdFx0dGhpcy5fdGltZVNjYWxlSW50ZXJwb2xhbnQgPSBudWxsO1xuXHRcdFx0XHR0aGlzLl9taXhlci5fdGFrZUJhY2tDb250cm9sSW50ZXJwb2xhbnQoIHRpbWVTY2FsZUludGVycG9sYW50ICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Ly8gT2JqZWN0IEFjY2Vzc29yc1xuXG5cdFx0Z2V0TWl4ZXI6IGZ1bmN0aW9uKCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5fbWl4ZXI7XG5cblx0XHR9LFxuXG5cdFx0Z2V0Q2xpcDogZnVuY3Rpb24oKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLl9jbGlwO1xuXG5cdFx0fSxcblxuXHRcdGdldFJvb3Q6IGZ1bmN0aW9uKCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5fbG9jYWxSb290IHx8IHRoaXMuX21peGVyLl9yb290O1xuXG5cdFx0fSxcblxuXHRcdC8vIEludGVybmFcblxuXHRcdF91cGRhdGU6IGZ1bmN0aW9uKCB0aW1lLCBkZWx0YVRpbWUsIHRpbWVEaXJlY3Rpb24sIGFjY3VJbmRleCApIHtcblx0XHRcdC8vIGNhbGxlZCBieSB0aGUgbWl4ZXJcblxuXHRcdFx0dmFyIHN0YXJ0VGltZSA9IHRoaXMuX3N0YXJ0VGltZTtcblxuXHRcdFx0aWYgKCBzdGFydFRpbWUgIT09IG51bGwgKSB7XG5cblx0XHRcdFx0Ly8gY2hlY2sgZm9yIHNjaGVkdWxlZCBzdGFydCBvZiBhY3Rpb25cblxuXHRcdFx0XHR2YXIgdGltZVJ1bm5pbmcgPSAoIHRpbWUgLSBzdGFydFRpbWUgKSAqIHRpbWVEaXJlY3Rpb247XG5cdFx0XHRcdGlmICggdGltZVJ1bm5pbmcgPCAwIHx8IHRpbWVEaXJlY3Rpb24gPT09IDAgKSB7XG5cblx0XHRcdFx0XHRyZXR1cm47IC8vIHlldCB0byBjb21lIC8gZG9uJ3QgZGVjaWRlIHdoZW4gZGVsdGEgPSAwXG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIHN0YXJ0XG5cblx0XHRcdFx0dGhpcy5fc3RhcnRUaW1lID0gbnVsbDsgLy8gdW5zY2hlZHVsZVxuXHRcdFx0XHRkZWx0YVRpbWUgPSB0aW1lRGlyZWN0aW9uICogdGltZVJ1bm5pbmc7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gYXBwbHkgdGltZSBzY2FsZSBhbmQgYWR2YW5jZSB0aW1lXG5cblx0XHRcdGRlbHRhVGltZSAqPSB0aGlzLl91cGRhdGVUaW1lU2NhbGUoIHRpbWUgKTtcblx0XHRcdHZhciBjbGlwVGltZSA9IHRoaXMuX3VwZGF0ZVRpbWUoIGRlbHRhVGltZSApO1xuXG5cdFx0XHQvLyBub3RlOiBfdXBkYXRlVGltZSBtYXkgZGlzYWJsZSB0aGUgYWN0aW9uIHJlc3VsdGluZyBpblxuXHRcdFx0Ly8gYW4gZWZmZWN0aXZlIHdlaWdodCBvZiAwXG5cblx0XHRcdHZhciB3ZWlnaHQgPSB0aGlzLl91cGRhdGVXZWlnaHQoIHRpbWUgKTtcblxuXHRcdFx0aWYgKCB3ZWlnaHQgPiAwICkge1xuXG5cdFx0XHRcdHZhciBpbnRlcnBvbGFudHMgPSB0aGlzLl9pbnRlcnBvbGFudHM7XG5cdFx0XHRcdHZhciBwcm9wZXJ0eU1peGVycyA9IHRoaXMuX3Byb3BlcnR5QmluZGluZ3M7XG5cblx0XHRcdFx0Zm9yICggdmFyIGogPSAwLCBtID0gaW50ZXJwb2xhbnRzLmxlbmd0aDsgaiAhPT0gbTsgKysgaiApIHtcblxuXHRcdFx0XHRcdGludGVycG9sYW50c1sgaiBdLmV2YWx1YXRlKCBjbGlwVGltZSApO1xuXHRcdFx0XHRcdHByb3BlcnR5TWl4ZXJzWyBqIF0uYWNjdW11bGF0ZSggYWNjdUluZGV4LCB3ZWlnaHQgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdH0sXG5cblx0XHRfdXBkYXRlV2VpZ2h0OiBmdW5jdGlvbiggdGltZSApIHtcblxuXHRcdFx0dmFyIHdlaWdodCA9IDA7XG5cblx0XHRcdGlmICggdGhpcy5lbmFibGVkICkge1xuXG5cdFx0XHRcdHdlaWdodCA9IHRoaXMud2VpZ2h0O1xuXHRcdFx0XHR2YXIgaW50ZXJwb2xhbnQgPSB0aGlzLl93ZWlnaHRJbnRlcnBvbGFudDtcblxuXHRcdFx0XHRpZiAoIGludGVycG9sYW50ICE9PSBudWxsICkge1xuXG5cdFx0XHRcdFx0dmFyIGludGVycG9sYW50VmFsdWUgPSBpbnRlcnBvbGFudC5ldmFsdWF0ZSggdGltZSApWyAwIF07XG5cblx0XHRcdFx0XHR3ZWlnaHQgKj0gaW50ZXJwb2xhbnRWYWx1ZTtcblxuXHRcdFx0XHRcdGlmICggdGltZSA+IGludGVycG9sYW50LnBhcmFtZXRlclBvc2l0aW9uc1sgMSBdICkge1xuXG5cdFx0XHRcdFx0XHR0aGlzLnN0b3BGYWRpbmcoKTtcblxuXHRcdFx0XHRcdFx0aWYgKCBpbnRlcnBvbGFudFZhbHVlID09PSAwICkge1xuXG5cdFx0XHRcdFx0XHRcdC8vIGZhZGVkIG91dCwgZGlzYWJsZVxuXHRcdFx0XHRcdFx0XHR0aGlzLmVuYWJsZWQgPSBmYWxzZTtcblxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLl9lZmZlY3RpdmVXZWlnaHQgPSB3ZWlnaHQ7XG5cdFx0XHRyZXR1cm4gd2VpZ2h0O1xuXG5cdFx0fSxcblxuXHRcdF91cGRhdGVUaW1lU2NhbGU6IGZ1bmN0aW9uKCB0aW1lICkge1xuXG5cdFx0XHR2YXIgdGltZVNjYWxlID0gMDtcblxuXHRcdFx0aWYgKCAhIHRoaXMucGF1c2VkICkge1xuXG5cdFx0XHRcdHRpbWVTY2FsZSA9IHRoaXMudGltZVNjYWxlO1xuXG5cdFx0XHRcdHZhciBpbnRlcnBvbGFudCA9IHRoaXMuX3RpbWVTY2FsZUludGVycG9sYW50O1xuXG5cdFx0XHRcdGlmICggaW50ZXJwb2xhbnQgIT09IG51bGwgKSB7XG5cblx0XHRcdFx0XHR2YXIgaW50ZXJwb2xhbnRWYWx1ZSA9IGludGVycG9sYW50LmV2YWx1YXRlKCB0aW1lIClbIDAgXTtcblxuXHRcdFx0XHRcdHRpbWVTY2FsZSAqPSBpbnRlcnBvbGFudFZhbHVlO1xuXG5cdFx0XHRcdFx0aWYgKCB0aW1lID4gaW50ZXJwb2xhbnQucGFyYW1ldGVyUG9zaXRpb25zWyAxIF0gKSB7XG5cblx0XHRcdFx0XHRcdHRoaXMuc3RvcFdhcnBpbmcoKTtcblxuXHRcdFx0XHRcdFx0aWYgKCB0aW1lU2NhbGUgPT09IDAgKSB7XG5cblx0XHRcdFx0XHRcdFx0Ly8gbW90aW9uIGhhcyBoYWx0ZWQsIHBhdXNlXG5cdFx0XHRcdFx0XHRcdHRoaXMucGF1c2VkID0gdHJ1ZTtcblxuXHRcdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0XHQvLyB3YXJwIGRvbmUgLSBhcHBseSBmaW5hbCB0aW1lIHNjYWxlXG5cdFx0XHRcdFx0XHRcdHRoaXMudGltZVNjYWxlID0gdGltZVNjYWxlO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuX2VmZmVjdGl2ZVRpbWVTY2FsZSA9IHRpbWVTY2FsZTtcblx0XHRcdHJldHVybiB0aW1lU2NhbGU7XG5cblx0XHR9LFxuXG5cdFx0X3VwZGF0ZVRpbWU6IGZ1bmN0aW9uKCBkZWx0YVRpbWUgKSB7XG5cblx0XHRcdHZhciB0aW1lID0gdGhpcy50aW1lICsgZGVsdGFUaW1lO1xuXG5cdFx0XHRpZiAoIGRlbHRhVGltZSA9PT0gMCApIHJldHVybiB0aW1lO1xuXG5cdFx0XHR2YXIgZHVyYXRpb24gPSB0aGlzLl9jbGlwLmR1cmF0aW9uLFxuXG5cdFx0XHRcdGxvb3AgPSB0aGlzLmxvb3AsXG5cdFx0XHRcdGxvb3BDb3VudCA9IHRoaXMuX2xvb3BDb3VudDtcblxuXHRcdFx0aWYgKCBsb29wID09PSBMb29wT25jZSApIHtcblxuXHRcdFx0XHRpZiAoIGxvb3BDb3VudCA9PT0gLTEgKSB7XG5cdFx0XHRcdFx0Ly8ganVzdCBzdGFydGVkXG5cblx0XHRcdFx0XHR0aGlzLl9sb29wQ291bnQgPSAwO1xuXHRcdFx0XHRcdHRoaXMuX3NldEVuZGluZ3MoIHRydWUsIHRydWUsIGZhbHNlICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGhhbmRsZV9zdG9wOiB7XG5cblx0XHRcdFx0XHRpZiAoIHRpbWUgPj0gZHVyYXRpb24gKSB7XG5cblx0XHRcdFx0XHRcdHRpbWUgPSBkdXJhdGlvbjtcblxuXHRcdFx0XHRcdH0gZWxzZSBpZiAoIHRpbWUgPCAwICkge1xuXG5cdFx0XHRcdFx0XHR0aW1lID0gMDtcblxuXHRcdFx0XHRcdH0gZWxzZSBicmVhayBoYW5kbGVfc3RvcDtcblxuXHRcdFx0XHRcdGlmICggdGhpcy5jbGFtcFdoZW5GaW5pc2hlZCApIHRoaXMucGF1c2VkID0gdHJ1ZTtcblx0XHRcdFx0XHRlbHNlIHRoaXMuZW5hYmxlZCA9IGZhbHNlO1xuXG5cdFx0XHRcdFx0dGhpcy5fbWl4ZXIuZGlzcGF0Y2hFdmVudCgge1xuXHRcdFx0XHRcdFx0dHlwZTogJ2ZpbmlzaGVkJywgYWN0aW9uOiB0aGlzLFxuXHRcdFx0XHRcdFx0ZGlyZWN0aW9uOiBkZWx0YVRpbWUgPCAwID8gLTEgOiAxXG5cdFx0XHRcdFx0fSApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fSBlbHNlIHsgLy8gcmVwZXRpdGl2ZSBSZXBlYXQgb3IgUGluZ1BvbmdcblxuXHRcdFx0XHR2YXIgcGluZ1BvbmcgPSAoIGxvb3AgPT09IExvb3BQaW5nUG9uZyApO1xuXG5cdFx0XHRcdGlmICggbG9vcENvdW50ID09PSAtMSApIHtcblx0XHRcdFx0XHQvLyBqdXN0IHN0YXJ0ZWRcblxuXHRcdFx0XHRcdGlmICggZGVsdGFUaW1lID49IDAgKSB7XG5cblx0XHRcdFx0XHRcdGxvb3BDb3VudCA9IDA7XG5cblx0XHRcdFx0XHRcdHRoaXMuX3NldEVuZGluZ3MoXG5cdFx0XHRcdFx0XHRcdFx0dHJ1ZSwgdGhpcy5yZXBldGl0aW9ucyA9PT0gMCwgcGluZ1BvbmcgKTtcblxuXHRcdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRcdC8vIHdoZW4gbG9vcGluZyBpbiByZXZlcnNlIGRpcmVjdGlvbiwgdGhlIGluaXRpYWxcblx0XHRcdFx0XHRcdC8vIHRyYW5zaXRpb24gdGhyb3VnaCB6ZXJvIGNvdW50cyBhcyBhIHJlcGV0aXRpb24sXG5cdFx0XHRcdFx0XHQvLyBzbyBsZWF2ZSBsb29wQ291bnQgYXQgLTFcblxuXHRcdFx0XHRcdFx0dGhpcy5fc2V0RW5kaW5ncyhcblx0XHRcdFx0XHRcdFx0XHR0aGlzLnJlcGV0aXRpb25zID09PSAwLCB0cnVlLCBwaW5nUG9uZyApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoIHRpbWUgPj0gZHVyYXRpb24gfHwgdGltZSA8IDAgKSB7XG5cdFx0XHRcdFx0Ly8gd3JhcCBhcm91bmRcblxuXHRcdFx0XHRcdHZhciBsb29wRGVsdGEgPSBNYXRoLmZsb29yKCB0aW1lIC8gZHVyYXRpb24gKTsgLy8gc2lnbmVkXG5cdFx0XHRcdFx0dGltZSAtPSBkdXJhdGlvbiAqIGxvb3BEZWx0YTtcblxuXHRcdFx0XHRcdGxvb3BDb3VudCArPSBNYXRoLmFicyggbG9vcERlbHRhICk7XG5cblx0XHRcdFx0XHR2YXIgcGVuZGluZyA9IHRoaXMucmVwZXRpdGlvbnMgLSBsb29wQ291bnQ7XG5cblx0XHRcdFx0XHRpZiAoIHBlbmRpbmcgPCAwICkge1xuXHRcdFx0XHRcdFx0Ly8gaGF2ZSB0byBzdG9wIChzd2l0Y2ggc3RhdGUsIGNsYW1wIHRpbWUsIGZpcmUgZXZlbnQpXG5cblx0XHRcdFx0XHRcdGlmICggdGhpcy5jbGFtcFdoZW5GaW5pc2hlZCApIHRoaXMucGF1c2VkID0gdHJ1ZTtcblx0XHRcdFx0XHRcdGVsc2UgdGhpcy5lbmFibGVkID0gZmFsc2U7XG5cblx0XHRcdFx0XHRcdHRpbWUgPSBkZWx0YVRpbWUgPiAwID8gZHVyYXRpb24gOiAwO1xuXG5cdFx0XHRcdFx0XHR0aGlzLl9taXhlci5kaXNwYXRjaEV2ZW50KCB7XG5cdFx0XHRcdFx0XHRcdHR5cGU6ICdmaW5pc2hlZCcsIGFjdGlvbjogdGhpcyxcblx0XHRcdFx0XHRcdFx0ZGlyZWN0aW9uOiBkZWx0YVRpbWUgPiAwID8gMSA6IC0xXG5cdFx0XHRcdFx0XHR9ICk7XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0Ly8ga2VlcCBydW5uaW5nXG5cblx0XHRcdFx0XHRcdGlmICggcGVuZGluZyA9PT0gMCApIHtcblx0XHRcdFx0XHRcdFx0Ly8gZW50ZXJpbmcgdGhlIGxhc3Qgcm91bmRcblxuXHRcdFx0XHRcdFx0XHR2YXIgYXRTdGFydCA9IGRlbHRhVGltZSA8IDA7XG5cdFx0XHRcdFx0XHRcdHRoaXMuX3NldEVuZGluZ3MoIGF0U3RhcnQsICEgYXRTdGFydCwgcGluZ1BvbmcgKTtcblxuXHRcdFx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdFx0XHR0aGlzLl9zZXRFbmRpbmdzKCBmYWxzZSwgZmFsc2UsIHBpbmdQb25nICk7XG5cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0dGhpcy5fbG9vcENvdW50ID0gbG9vcENvdW50O1xuXG5cdFx0XHRcdFx0XHR0aGlzLl9taXhlci5kaXNwYXRjaEV2ZW50KCB7XG5cdFx0XHRcdFx0XHRcdHR5cGU6ICdsb29wJywgYWN0aW9uOiB0aGlzLCBsb29wRGVsdGE6IGxvb3BEZWx0YVxuXHRcdFx0XHRcdFx0fSApO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoIHBpbmdQb25nICYmICggbG9vcENvdW50ICYgMSApID09PSAxICkge1xuXHRcdFx0XHRcdC8vIGludmVydCB0aW1lIGZvciB0aGUgXCJwb25nIHJvdW5kXCJcblxuXHRcdFx0XHRcdHRoaXMudGltZSA9IHRpbWU7XG5cdFx0XHRcdFx0cmV0dXJuIGR1cmF0aW9uIC0gdGltZTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0dGhpcy50aW1lID0gdGltZTtcblx0XHRcdHJldHVybiB0aW1lO1xuXG5cdFx0fSxcblxuXHRcdF9zZXRFbmRpbmdzOiBmdW5jdGlvbiggYXRTdGFydCwgYXRFbmQsIHBpbmdQb25nICkge1xuXG5cdFx0XHR2YXIgc2V0dGluZ3MgPSB0aGlzLl9pbnRlcnBvbGFudFNldHRpbmdzO1xuXG5cdFx0XHRpZiAoIHBpbmdQb25nICkge1xuXG5cdFx0XHRcdHNldHRpbmdzLmVuZGluZ1N0YXJ0IFx0PSBaZXJvU2xvcGVFbmRpbmc7XG5cdFx0XHRcdHNldHRpbmdzLmVuZGluZ0VuZFx0XHQ9IFplcm9TbG9wZUVuZGluZztcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHQvLyBhc3N1bWluZyBmb3IgTG9vcE9uY2UgYXRTdGFydCA9PSBhdEVuZCA9PSB0cnVlXG5cblx0XHRcdFx0aWYgKCBhdFN0YXJ0ICkge1xuXG5cdFx0XHRcdFx0c2V0dGluZ3MuZW5kaW5nU3RhcnQgPSB0aGlzLnplcm9TbG9wZUF0U3RhcnQgP1xuXHRcdFx0XHRcdFx0XHRaZXJvU2xvcGVFbmRpbmcgOiBaZXJvQ3VydmF0dXJlRW5kaW5nO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHRzZXR0aW5ncy5lbmRpbmdTdGFydCA9IFdyYXBBcm91bmRFbmRpbmc7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggYXRFbmQgKSB7XG5cblx0XHRcdFx0XHRzZXR0aW5ncy5lbmRpbmdFbmQgPSB0aGlzLnplcm9TbG9wZUF0RW5kID9cblx0XHRcdFx0XHRcdFx0WmVyb1Nsb3BlRW5kaW5nIDogWmVyb0N1cnZhdHVyZUVuZGluZztcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0c2V0dGluZ3MuZW5kaW5nRW5kIFx0ID0gV3JhcEFyb3VuZEVuZGluZztcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdH0sXG5cblx0XHRfc2NoZWR1bGVGYWRpbmc6IGZ1bmN0aW9uKCBkdXJhdGlvbiwgd2VpZ2h0Tm93LCB3ZWlnaHRUaGVuICkge1xuXG5cdFx0XHR2YXIgbWl4ZXIgPSB0aGlzLl9taXhlciwgbm93ID0gbWl4ZXIudGltZSxcblx0XHRcdFx0aW50ZXJwb2xhbnQgPSB0aGlzLl93ZWlnaHRJbnRlcnBvbGFudDtcblxuXHRcdFx0aWYgKCBpbnRlcnBvbGFudCA9PT0gbnVsbCApIHtcblxuXHRcdFx0XHRpbnRlcnBvbGFudCA9IG1peGVyLl9sZW5kQ29udHJvbEludGVycG9sYW50KCk7XG5cdFx0XHRcdHRoaXMuX3dlaWdodEludGVycG9sYW50ID0gaW50ZXJwb2xhbnQ7XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIHRpbWVzID0gaW50ZXJwb2xhbnQucGFyYW1ldGVyUG9zaXRpb25zLFxuXHRcdFx0XHR2YWx1ZXMgPSBpbnRlcnBvbGFudC5zYW1wbGVWYWx1ZXM7XG5cblx0XHRcdHRpbWVzWyAwIF0gPSBub3c7IFx0XHRcdFx0dmFsdWVzWyAwIF0gPSB3ZWlnaHROb3c7XG5cdFx0XHR0aW1lc1sgMSBdID0gbm93ICsgZHVyYXRpb247XHR2YWx1ZXNbIDEgXSA9IHdlaWdodFRoZW47XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0LyoqXG5cdCAqXG5cdCAqIFBsYXllciBmb3IgQW5pbWF0aW9uQ2xpcHMuXG5cdCAqXG5cdCAqXG5cdCAqIEBhdXRob3IgQmVuIEhvdXN0b24gLyBodHRwOi8vY2xhcmEuaW8vXG5cdCAqIEBhdXRob3IgRGF2aWQgU2Fybm8gLyBodHRwOi8vbGlnaHRoYXVzLnVzL1xuXHQgKiBAYXV0aG9yIHRzY2h3XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEFuaW1hdGlvbk1peGVyKCByb290ICkge1xuXG5cdFx0dGhpcy5fcm9vdCA9IHJvb3Q7XG5cdFx0dGhpcy5faW5pdE1lbW9yeU1hbmFnZXIoKTtcblx0XHR0aGlzLl9hY2N1SW5kZXggPSAwO1xuXG5cdFx0dGhpcy50aW1lID0gMDtcblxuXHRcdHRoaXMudGltZVNjYWxlID0gMS4wO1xuXG5cdH1cblxuXHRPYmplY3QuYXNzaWduKCBBbmltYXRpb25NaXhlci5wcm90b3R5cGUsIEV2ZW50RGlzcGF0Y2hlci5wcm90b3R5cGUsIHtcblxuXHRcdC8vIHJldHVybiBhbiBhY3Rpb24gZm9yIGEgY2xpcCBvcHRpb25hbGx5IHVzaW5nIGEgY3VzdG9tIHJvb3QgdGFyZ2V0XG5cdFx0Ly8gb2JqZWN0ICh0aGlzIG1ldGhvZCBhbGxvY2F0ZXMgYSBsb3Qgb2YgZHluYW1pYyBtZW1vcnkgaW4gY2FzZSBhXG5cdFx0Ly8gcHJldmlvdXNseSB1bmtub3duIGNsaXAvcm9vdCBjb21iaW5hdGlvbiBpcyBzcGVjaWZpZWQpXG5cdFx0Y2xpcEFjdGlvbjogZnVuY3Rpb24oIGNsaXAsIG9wdGlvbmFsUm9vdCApIHtcblxuXHRcdFx0dmFyIHJvb3QgPSBvcHRpb25hbFJvb3QgfHwgdGhpcy5fcm9vdCxcblx0XHRcdFx0cm9vdFV1aWQgPSByb290LnV1aWQsXG5cblx0XHRcdFx0Y2xpcE9iamVjdCA9IHR5cGVvZiBjbGlwID09PSAnc3RyaW5nJyA/XG5cdFx0XHRcdFx0XHRBbmltYXRpb25DbGlwLmZpbmRCeU5hbWUoIHJvb3QsIGNsaXAgKSA6IGNsaXAsXG5cblx0XHRcdFx0Y2xpcFV1aWQgPSBjbGlwT2JqZWN0ICE9PSBudWxsID8gY2xpcE9iamVjdC51dWlkIDogY2xpcCxcblxuXHRcdFx0XHRhY3Rpb25zRm9yQ2xpcCA9IHRoaXMuX2FjdGlvbnNCeUNsaXBbIGNsaXBVdWlkIF0sXG5cdFx0XHRcdHByb3RvdHlwZUFjdGlvbiA9IG51bGw7XG5cblx0XHRcdGlmICggYWN0aW9uc0ZvckNsaXAgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHR2YXIgZXhpc3RpbmdBY3Rpb24gPVxuXHRcdFx0XHRcdFx0YWN0aW9uc0ZvckNsaXAuYWN0aW9uQnlSb290WyByb290VXVpZCBdO1xuXG5cdFx0XHRcdGlmICggZXhpc3RpbmdBY3Rpb24gIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdHJldHVybiBleGlzdGluZ0FjdGlvbjtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gd2Uga25vdyB0aGUgY2xpcCwgc28gd2UgZG9uJ3QgaGF2ZSB0byBwYXJzZSBhbGxcblx0XHRcdFx0Ly8gdGhlIGJpbmRpbmdzIGFnYWluIGJ1dCBjYW4ganVzdCBjb3B5XG5cdFx0XHRcdHByb3RvdHlwZUFjdGlvbiA9IGFjdGlvbnNGb3JDbGlwLmtub3duQWN0aW9uc1sgMCBdO1xuXG5cdFx0XHRcdC8vIGFsc28sIHRha2UgdGhlIGNsaXAgZnJvbSB0aGUgcHJvdG90eXBlIGFjdGlvblxuXHRcdFx0XHRpZiAoIGNsaXBPYmplY3QgPT09IG51bGwgKVxuXHRcdFx0XHRcdGNsaXBPYmplY3QgPSBwcm90b3R5cGVBY3Rpb24uX2NsaXA7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gY2xpcCBtdXN0IGJlIGtub3duIHdoZW4gc3BlY2lmaWVkIHZpYSBzdHJpbmdcblx0XHRcdGlmICggY2xpcE9iamVjdCA9PT0gbnVsbCApIHJldHVybiBudWxsO1xuXG5cdFx0XHQvLyBhbGxvY2F0ZSBhbGwgcmVzb3VyY2VzIHJlcXVpcmVkIHRvIHJ1biBpdFxuXHRcdFx0dmFyIG5ld0FjdGlvbiA9IG5ldyBBbmltYXRpb25BY3Rpb24oIHRoaXMsIGNsaXBPYmplY3QsIG9wdGlvbmFsUm9vdCApO1xuXG5cdFx0XHR0aGlzLl9iaW5kQWN0aW9uKCBuZXdBY3Rpb24sIHByb3RvdHlwZUFjdGlvbiApO1xuXG5cdFx0XHQvLyBhbmQgbWFrZSB0aGUgYWN0aW9uIGtub3duIHRvIHRoZSBtZW1vcnkgbWFuYWdlclxuXHRcdFx0dGhpcy5fYWRkSW5hY3RpdmVBY3Rpb24oIG5ld0FjdGlvbiwgY2xpcFV1aWQsIHJvb3RVdWlkICk7XG5cblx0XHRcdHJldHVybiBuZXdBY3Rpb247XG5cblx0XHR9LFxuXG5cdFx0Ly8gZ2V0IGFuIGV4aXN0aW5nIGFjdGlvblxuXHRcdGV4aXN0aW5nQWN0aW9uOiBmdW5jdGlvbiggY2xpcCwgb3B0aW9uYWxSb290ICkge1xuXG5cdFx0XHR2YXIgcm9vdCA9IG9wdGlvbmFsUm9vdCB8fCB0aGlzLl9yb290LFxuXHRcdFx0XHRyb290VXVpZCA9IHJvb3QudXVpZCxcblxuXHRcdFx0XHRjbGlwT2JqZWN0ID0gdHlwZW9mIGNsaXAgPT09ICdzdHJpbmcnID9cblx0XHRcdFx0XHRcdEFuaW1hdGlvbkNsaXAuZmluZEJ5TmFtZSggcm9vdCwgY2xpcCApIDogY2xpcCxcblxuXHRcdFx0XHRjbGlwVXVpZCA9IGNsaXBPYmplY3QgPyBjbGlwT2JqZWN0LnV1aWQgOiBjbGlwLFxuXG5cdFx0XHRcdGFjdGlvbnNGb3JDbGlwID0gdGhpcy5fYWN0aW9uc0J5Q2xpcFsgY2xpcFV1aWQgXTtcblxuXHRcdFx0aWYgKCBhY3Rpb25zRm9yQ2xpcCAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdHJldHVybiBhY3Rpb25zRm9yQ2xpcC5hY3Rpb25CeVJvb3RbIHJvb3RVdWlkIF0gfHwgbnVsbDtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gbnVsbDtcblxuXHRcdH0sXG5cblx0XHQvLyBkZWFjdGl2YXRlcyBhbGwgcHJldmlvdXNseSBzY2hlZHVsZWQgYWN0aW9uc1xuXHRcdHN0b3BBbGxBY3Rpb246IGZ1bmN0aW9uKCkge1xuXG5cdFx0XHR2YXIgYWN0aW9ucyA9IHRoaXMuX2FjdGlvbnMsXG5cdFx0XHRcdG5BY3Rpb25zID0gdGhpcy5fbkFjdGl2ZUFjdGlvbnMsXG5cdFx0XHRcdGJpbmRpbmdzID0gdGhpcy5fYmluZGluZ3MsXG5cdFx0XHRcdG5CaW5kaW5ncyA9IHRoaXMuX25BY3RpdmVCaW5kaW5ncztcblxuXHRcdFx0dGhpcy5fbkFjdGl2ZUFjdGlvbnMgPSAwO1xuXHRcdFx0dGhpcy5fbkFjdGl2ZUJpbmRpbmdzID0gMDtcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpICE9PSBuQWN0aW9uczsgKysgaSApIHtcblxuXHRcdFx0XHRhY3Rpb25zWyBpIF0ucmVzZXQoKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgIT09IG5CaW5kaW5nczsgKysgaSApIHtcblxuXHRcdFx0XHRiaW5kaW5nc1sgaSBdLnVzZUNvdW50ID0gMDtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHQvLyBhZHZhbmNlIHRoZSB0aW1lIGFuZCB1cGRhdGUgYXBwbHkgdGhlIGFuaW1hdGlvblxuXHRcdHVwZGF0ZTogZnVuY3Rpb24oIGRlbHRhVGltZSApIHtcblxuXHRcdFx0ZGVsdGFUaW1lICo9IHRoaXMudGltZVNjYWxlO1xuXG5cdFx0XHR2YXIgYWN0aW9ucyA9IHRoaXMuX2FjdGlvbnMsXG5cdFx0XHRcdG5BY3Rpb25zID0gdGhpcy5fbkFjdGl2ZUFjdGlvbnMsXG5cblx0XHRcdFx0dGltZSA9IHRoaXMudGltZSArPSBkZWx0YVRpbWUsXG5cdFx0XHRcdHRpbWVEaXJlY3Rpb24gPSBNYXRoLnNpZ24oIGRlbHRhVGltZSApLFxuXG5cdFx0XHRcdGFjY3VJbmRleCA9IHRoaXMuX2FjY3VJbmRleCBePSAxO1xuXG5cdFx0XHQvLyBydW4gYWN0aXZlIGFjdGlvbnNcblxuXHRcdFx0Zm9yICggdmFyIGkgPSAwOyBpICE9PSBuQWN0aW9uczsgKysgaSApIHtcblxuXHRcdFx0XHR2YXIgYWN0aW9uID0gYWN0aW9uc1sgaSBdO1xuXG5cdFx0XHRcdGlmICggYWN0aW9uLmVuYWJsZWQgKSB7XG5cblx0XHRcdFx0XHRhY3Rpb24uX3VwZGF0ZSggdGltZSwgZGVsdGFUaW1lLCB0aW1lRGlyZWN0aW9uLCBhY2N1SW5kZXggKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gdXBkYXRlIHNjZW5lIGdyYXBoXG5cblx0XHRcdHZhciBiaW5kaW5ncyA9IHRoaXMuX2JpbmRpbmdzLFxuXHRcdFx0XHRuQmluZGluZ3MgPSB0aGlzLl9uQWN0aXZlQmluZGluZ3M7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMDsgaSAhPT0gbkJpbmRpbmdzOyArKyBpICkge1xuXG5cdFx0XHRcdGJpbmRpbmdzWyBpIF0uYXBwbHkoIGFjY3VJbmRleCApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdC8vIHJldHVybiB0aGlzIG1peGVyJ3Mgcm9vdCB0YXJnZXQgb2JqZWN0XG5cdFx0Z2V0Um9vdDogZnVuY3Rpb24oKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLl9yb290O1xuXG5cdFx0fSxcblxuXHRcdC8vIGZyZWUgYWxsIHJlc291cmNlcyBzcGVjaWZpYyB0byBhIHBhcnRpY3VsYXIgY2xpcFxuXHRcdHVuY2FjaGVDbGlwOiBmdW5jdGlvbiggY2xpcCApIHtcblxuXHRcdFx0dmFyIGFjdGlvbnMgPSB0aGlzLl9hY3Rpb25zLFxuXHRcdFx0XHRjbGlwVXVpZCA9IGNsaXAudXVpZCxcblx0XHRcdFx0YWN0aW9uc0J5Q2xpcCA9IHRoaXMuX2FjdGlvbnNCeUNsaXAsXG5cdFx0XHRcdGFjdGlvbnNGb3JDbGlwID0gYWN0aW9uc0J5Q2xpcFsgY2xpcFV1aWQgXTtcblxuXHRcdFx0aWYgKCBhY3Rpb25zRm9yQ2xpcCAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdC8vIG5vdGU6IGp1c3QgY2FsbGluZyBfcmVtb3ZlSW5hY3RpdmVBY3Rpb24gd291bGQgbWVzcyB1cCB0aGVcblx0XHRcdFx0Ly8gaXRlcmF0aW9uIHN0YXRlIGFuZCBhbHNvIHJlcXVpcmUgdXBkYXRpbmcgdGhlIHN0YXRlIHdlIGNhblxuXHRcdFx0XHQvLyBqdXN0IHRocm93IGF3YXlcblxuXHRcdFx0XHR2YXIgYWN0aW9uc1RvUmVtb3ZlID0gYWN0aW9uc0ZvckNsaXAua25vd25BY3Rpb25zO1xuXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbiA9IGFjdGlvbnNUb1JlbW92ZS5sZW5ndGg7IGkgIT09IG47ICsrIGkgKSB7XG5cblx0XHRcdFx0XHR2YXIgYWN0aW9uID0gYWN0aW9uc1RvUmVtb3ZlWyBpIF07XG5cblx0XHRcdFx0XHR0aGlzLl9kZWFjdGl2YXRlQWN0aW9uKCBhY3Rpb24gKTtcblxuXHRcdFx0XHRcdHZhciBjYWNoZUluZGV4ID0gYWN0aW9uLl9jYWNoZUluZGV4LFxuXHRcdFx0XHRcdFx0bGFzdEluYWN0aXZlQWN0aW9uID0gYWN0aW9uc1sgYWN0aW9ucy5sZW5ndGggLSAxIF07XG5cblx0XHRcdFx0XHRhY3Rpb24uX2NhY2hlSW5kZXggPSBudWxsO1xuXHRcdFx0XHRcdGFjdGlvbi5fYnlDbGlwQ2FjaGVJbmRleCA9IG51bGw7XG5cblx0XHRcdFx0XHRsYXN0SW5hY3RpdmVBY3Rpb24uX2NhY2hlSW5kZXggPSBjYWNoZUluZGV4O1xuXHRcdFx0XHRcdGFjdGlvbnNbIGNhY2hlSW5kZXggXSA9IGxhc3RJbmFjdGl2ZUFjdGlvbjtcblx0XHRcdFx0XHRhY3Rpb25zLnBvcCgpO1xuXG5cdFx0XHRcdFx0dGhpcy5fcmVtb3ZlSW5hY3RpdmVCaW5kaW5nc0ZvckFjdGlvbiggYWN0aW9uICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGRlbGV0ZSBhY3Rpb25zQnlDbGlwWyBjbGlwVXVpZCBdO1xuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0Ly8gZnJlZSBhbGwgcmVzb3VyY2VzIHNwZWNpZmljIHRvIGEgcGFydGljdWxhciByb290IHRhcmdldCBvYmplY3Rcblx0XHR1bmNhY2hlUm9vdDogZnVuY3Rpb24oIHJvb3QgKSB7XG5cblx0XHRcdHZhciByb290VXVpZCA9IHJvb3QudXVpZCxcblx0XHRcdFx0YWN0aW9uc0J5Q2xpcCA9IHRoaXMuX2FjdGlvbnNCeUNsaXA7XG5cblx0XHRcdGZvciAoIHZhciBjbGlwVXVpZCBpbiBhY3Rpb25zQnlDbGlwICkge1xuXG5cdFx0XHRcdHZhciBhY3Rpb25CeVJvb3QgPSBhY3Rpb25zQnlDbGlwWyBjbGlwVXVpZCBdLmFjdGlvbkJ5Um9vdCxcblx0XHRcdFx0XHRhY3Rpb24gPSBhY3Rpb25CeVJvb3RbIHJvb3RVdWlkIF07XG5cblx0XHRcdFx0aWYgKCBhY3Rpb24gIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0XHRcdHRoaXMuX2RlYWN0aXZhdGVBY3Rpb24oIGFjdGlvbiApO1xuXHRcdFx0XHRcdHRoaXMuX3JlbW92ZUluYWN0aXZlQWN0aW9uKCBhY3Rpb24gKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0dmFyIGJpbmRpbmdzQnlSb290ID0gdGhpcy5fYmluZGluZ3NCeVJvb3RBbmROYW1lLFxuXHRcdFx0XHRiaW5kaW5nQnlOYW1lID0gYmluZGluZ3NCeVJvb3RbIHJvb3RVdWlkIF07XG5cblx0XHRcdGlmICggYmluZGluZ0J5TmFtZSAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdGZvciAoIHZhciB0cmFja05hbWUgaW4gYmluZGluZ0J5TmFtZSApIHtcblxuXHRcdFx0XHRcdHZhciBiaW5kaW5nID0gYmluZGluZ0J5TmFtZVsgdHJhY2tOYW1lIF07XG5cdFx0XHRcdFx0YmluZGluZy5yZXN0b3JlT3JpZ2luYWxTdGF0ZSgpO1xuXHRcdFx0XHRcdHRoaXMuX3JlbW92ZUluYWN0aXZlQmluZGluZyggYmluZGluZyApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0fSxcblxuXHRcdC8vIHJlbW92ZSBhIHRhcmdldGVkIGNsaXAgZnJvbSB0aGUgY2FjaGVcblx0XHR1bmNhY2hlQWN0aW9uOiBmdW5jdGlvbiggY2xpcCwgb3B0aW9uYWxSb290ICkge1xuXG5cdFx0XHR2YXIgYWN0aW9uID0gdGhpcy5leGlzdGluZ0FjdGlvbiggY2xpcCwgb3B0aW9uYWxSb290ICk7XG5cblx0XHRcdGlmICggYWN0aW9uICE9PSBudWxsICkge1xuXG5cdFx0XHRcdHRoaXMuX2RlYWN0aXZhdGVBY3Rpb24oIGFjdGlvbiApO1xuXHRcdFx0XHR0aGlzLl9yZW1vdmVJbmFjdGl2ZUFjdGlvbiggYWN0aW9uICk7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHR9ICk7XG5cblx0Ly8gSW1wbGVtZW50YXRpb24gZGV0YWlsczpcblxuXHRPYmplY3QuYXNzaWduKCBBbmltYXRpb25NaXhlci5wcm90b3R5cGUsIHtcblxuXHRcdF9iaW5kQWN0aW9uOiBmdW5jdGlvbiggYWN0aW9uLCBwcm90b3R5cGVBY3Rpb24gKSB7XG5cblx0XHRcdHZhciByb290ID0gYWN0aW9uLl9sb2NhbFJvb3QgfHwgdGhpcy5fcm9vdCxcblx0XHRcdFx0dHJhY2tzID0gYWN0aW9uLl9jbGlwLnRyYWNrcyxcblx0XHRcdFx0blRyYWNrcyA9IHRyYWNrcy5sZW5ndGgsXG5cdFx0XHRcdGJpbmRpbmdzID0gYWN0aW9uLl9wcm9wZXJ0eUJpbmRpbmdzLFxuXHRcdFx0XHRpbnRlcnBvbGFudHMgPSBhY3Rpb24uX2ludGVycG9sYW50cyxcblx0XHRcdFx0cm9vdFV1aWQgPSByb290LnV1aWQsXG5cdFx0XHRcdGJpbmRpbmdzQnlSb290ID0gdGhpcy5fYmluZGluZ3NCeVJvb3RBbmROYW1lLFxuXHRcdFx0XHRiaW5kaW5nc0J5TmFtZSA9IGJpbmRpbmdzQnlSb290WyByb290VXVpZCBdO1xuXG5cdFx0XHRpZiAoIGJpbmRpbmdzQnlOYW1lID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0YmluZGluZ3NCeU5hbWUgPSB7fTtcblx0XHRcdFx0YmluZGluZ3NCeVJvb3RbIHJvb3RVdWlkIF0gPSBiaW5kaW5nc0J5TmFtZTtcblxuXHRcdFx0fVxuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgIT09IG5UcmFja3M7ICsrIGkgKSB7XG5cblx0XHRcdFx0dmFyIHRyYWNrID0gdHJhY2tzWyBpIF0sXG5cdFx0XHRcdFx0dHJhY2tOYW1lID0gdHJhY2submFtZSxcblx0XHRcdFx0XHRiaW5kaW5nID0gYmluZGluZ3NCeU5hbWVbIHRyYWNrTmFtZSBdO1xuXG5cdFx0XHRcdGlmICggYmluZGluZyAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdFx0YmluZGluZ3NbIGkgXSA9IGJpbmRpbmc7XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdGJpbmRpbmcgPSBiaW5kaW5nc1sgaSBdO1xuXG5cdFx0XHRcdFx0aWYgKCBiaW5kaW5nICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0XHRcdC8vIGV4aXN0aW5nIGJpbmRpbmcsIG1ha2Ugc3VyZSB0aGUgY2FjaGUga25vd3NcblxuXHRcdFx0XHRcdFx0aWYgKCBiaW5kaW5nLl9jYWNoZUluZGV4ID09PSBudWxsICkge1xuXG5cdFx0XHRcdFx0XHRcdCsrIGJpbmRpbmcucmVmZXJlbmNlQ291bnQ7XG5cdFx0XHRcdFx0XHRcdHRoaXMuX2FkZEluYWN0aXZlQmluZGluZyggYmluZGluZywgcm9vdFV1aWQsIHRyYWNrTmFtZSApO1xuXG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdGNvbnRpbnVlO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0dmFyIHBhdGggPSBwcm90b3R5cGVBY3Rpb24gJiYgcHJvdG90eXBlQWN0aW9uLlxuXHRcdFx0XHRcdFx0XHRfcHJvcGVydHlCaW5kaW5nc1sgaSBdLmJpbmRpbmcucGFyc2VkUGF0aDtcblxuXHRcdFx0XHRcdGJpbmRpbmcgPSBuZXcgUHJvcGVydHlNaXhlcihcblx0XHRcdFx0XHRcdFx0UHJvcGVydHlCaW5kaW5nLmNyZWF0ZSggcm9vdCwgdHJhY2tOYW1lLCBwYXRoICksXG5cdFx0XHRcdFx0XHRcdHRyYWNrLlZhbHVlVHlwZU5hbWUsIHRyYWNrLmdldFZhbHVlU2l6ZSgpICk7XG5cblx0XHRcdFx0XHQrKyBiaW5kaW5nLnJlZmVyZW5jZUNvdW50O1xuXHRcdFx0XHRcdHRoaXMuX2FkZEluYWN0aXZlQmluZGluZyggYmluZGluZywgcm9vdFV1aWQsIHRyYWNrTmFtZSApO1xuXG5cdFx0XHRcdFx0YmluZGluZ3NbIGkgXSA9IGJpbmRpbmc7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGludGVycG9sYW50c1sgaSBdLnJlc3VsdEJ1ZmZlciA9IGJpbmRpbmcuYnVmZmVyO1xuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0X2FjdGl2YXRlQWN0aW9uOiBmdW5jdGlvbiggYWN0aW9uICkge1xuXG5cdFx0XHRpZiAoICEgdGhpcy5faXNBY3RpdmVBY3Rpb24oIGFjdGlvbiApICkge1xuXG5cdFx0XHRcdGlmICggYWN0aW9uLl9jYWNoZUluZGV4ID09PSBudWxsICkge1xuXG5cdFx0XHRcdFx0Ly8gdGhpcyBhY3Rpb24gaGFzIGJlZW4gZm9yZ290dGVuIGJ5IHRoZSBjYWNoZSwgYnV0IHRoZSB1c2VyXG5cdFx0XHRcdFx0Ly8gYXBwZWFycyB0byBiZSBzdGlsbCB1c2luZyBpdCAtPiByZWJpbmRcblxuXHRcdFx0XHRcdHZhciByb290VXVpZCA9ICggYWN0aW9uLl9sb2NhbFJvb3QgfHwgdGhpcy5fcm9vdCApLnV1aWQsXG5cdFx0XHRcdFx0XHRjbGlwVXVpZCA9IGFjdGlvbi5fY2xpcC51dWlkLFxuXHRcdFx0XHRcdFx0YWN0aW9uc0ZvckNsaXAgPSB0aGlzLl9hY3Rpb25zQnlDbGlwWyBjbGlwVXVpZCBdO1xuXG5cdFx0XHRcdFx0dGhpcy5fYmluZEFjdGlvbiggYWN0aW9uLFxuXHRcdFx0XHRcdFx0XHRhY3Rpb25zRm9yQ2xpcCAmJiBhY3Rpb25zRm9yQ2xpcC5rbm93bkFjdGlvbnNbIDAgXSApO1xuXG5cdFx0XHRcdFx0dGhpcy5fYWRkSW5hY3RpdmVBY3Rpb24oIGFjdGlvbiwgY2xpcFV1aWQsIHJvb3RVdWlkICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBiaW5kaW5ncyA9IGFjdGlvbi5fcHJvcGVydHlCaW5kaW5ncztcblxuXHRcdFx0XHQvLyBpbmNyZW1lbnQgcmVmZXJlbmNlIGNvdW50cyAvIHNvcnQgb3V0IHN0YXRlXG5cdFx0XHRcdGZvciAoIHZhciBpID0gMCwgbiA9IGJpbmRpbmdzLmxlbmd0aDsgaSAhPT0gbjsgKysgaSApIHtcblxuXHRcdFx0XHRcdHZhciBiaW5kaW5nID0gYmluZGluZ3NbIGkgXTtcblxuXHRcdFx0XHRcdGlmICggYmluZGluZy51c2VDb3VudCArKyA9PT0gMCApIHtcblxuXHRcdFx0XHRcdFx0dGhpcy5fbGVuZEJpbmRpbmcoIGJpbmRpbmcgKTtcblx0XHRcdFx0XHRcdGJpbmRpbmcuc2F2ZU9yaWdpbmFsU3RhdGUoKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0dGhpcy5fbGVuZEFjdGlvbiggYWN0aW9uICk7XG5cblx0XHRcdH1cblxuXHRcdH0sXG5cblx0XHRfZGVhY3RpdmF0ZUFjdGlvbjogZnVuY3Rpb24oIGFjdGlvbiApIHtcblxuXHRcdFx0aWYgKCB0aGlzLl9pc0FjdGl2ZUFjdGlvbiggYWN0aW9uICkgKSB7XG5cblx0XHRcdFx0dmFyIGJpbmRpbmdzID0gYWN0aW9uLl9wcm9wZXJ0eUJpbmRpbmdzO1xuXG5cdFx0XHRcdC8vIGRlY3JlbWVudCByZWZlcmVuY2UgY291bnRzIC8gc29ydCBvdXQgc3RhdGVcblx0XHRcdFx0Zm9yICggdmFyIGkgPSAwLCBuID0gYmluZGluZ3MubGVuZ3RoOyBpICE9PSBuOyArKyBpICkge1xuXG5cdFx0XHRcdFx0dmFyIGJpbmRpbmcgPSBiaW5kaW5nc1sgaSBdO1xuXG5cdFx0XHRcdFx0aWYgKCAtLSBiaW5kaW5nLnVzZUNvdW50ID09PSAwICkge1xuXG5cdFx0XHRcdFx0XHRiaW5kaW5nLnJlc3RvcmVPcmlnaW5hbFN0YXRlKCk7XG5cdFx0XHRcdFx0XHR0aGlzLl90YWtlQmFja0JpbmRpbmcoIGJpbmRpbmcgKTtcblxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0dGhpcy5fdGFrZUJhY2tBY3Rpb24oIGFjdGlvbiApO1xuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0Ly8gTWVtb3J5IG1hbmFnZXJcblxuXHRcdF9pbml0TWVtb3J5TWFuYWdlcjogZnVuY3Rpb24oKSB7XG5cblx0XHRcdHRoaXMuX2FjdGlvbnMgPSBbXTsgLy8gJ25BY3RpdmVBY3Rpb25zJyBmb2xsb3dlZCBieSBpbmFjdGl2ZSBvbmVzXG5cdFx0XHR0aGlzLl9uQWN0aXZlQWN0aW9ucyA9IDA7XG5cblx0XHRcdHRoaXMuX2FjdGlvbnNCeUNsaXAgPSB7fTtcblx0XHRcdC8vIGluc2lkZTpcblx0XHRcdC8vIHtcblx0XHRcdC8vIFx0XHRrbm93bkFjdGlvbnM6IEFycmF5PCBBbmltYXRpb25BY3Rpb24gPlx0LSB1c2VkIGFzIHByb3RvdHlwZXNcblx0XHRcdC8vIFx0XHRhY3Rpb25CeVJvb3Q6IEFuaW1hdGlvbkFjdGlvblx0XHRcdC0gbG9va3VwXG5cdFx0XHQvLyB9XG5cblxuXHRcdFx0dGhpcy5fYmluZGluZ3MgPSBbXTsgLy8gJ25BY3RpdmVCaW5kaW5ncycgZm9sbG93ZWQgYnkgaW5hY3RpdmUgb25lc1xuXHRcdFx0dGhpcy5fbkFjdGl2ZUJpbmRpbmdzID0gMDtcblxuXHRcdFx0dGhpcy5fYmluZGluZ3NCeVJvb3RBbmROYW1lID0ge307IC8vIGluc2lkZTogTWFwPCBuYW1lLCBQcm9wZXJ0eU1peGVyID5cblxuXG5cdFx0XHR0aGlzLl9jb250cm9sSW50ZXJwb2xhbnRzID0gW107IC8vIHNhbWUgZ2FtZSBhcyBhYm92ZVxuXHRcdFx0dGhpcy5fbkFjdGl2ZUNvbnRyb2xJbnRlcnBvbGFudHMgPSAwO1xuXG5cdFx0XHR2YXIgc2NvcGUgPSB0aGlzO1xuXG5cdFx0XHR0aGlzLnN0YXRzID0ge1xuXG5cdFx0XHRcdGFjdGlvbnM6IHtcblx0XHRcdFx0XHRnZXQgdG90YWwoKSB7IHJldHVybiBzY29wZS5fYWN0aW9ucy5sZW5ndGg7IH0sXG5cdFx0XHRcdFx0Z2V0IGluVXNlKCkgeyByZXR1cm4gc2NvcGUuX25BY3RpdmVBY3Rpb25zOyB9XG5cdFx0XHRcdH0sXG5cdFx0XHRcdGJpbmRpbmdzOiB7XG5cdFx0XHRcdFx0Z2V0IHRvdGFsKCkgeyByZXR1cm4gc2NvcGUuX2JpbmRpbmdzLmxlbmd0aDsgfSxcblx0XHRcdFx0XHRnZXQgaW5Vc2UoKSB7IHJldHVybiBzY29wZS5fbkFjdGl2ZUJpbmRpbmdzOyB9XG5cdFx0XHRcdH0sXG5cdFx0XHRcdGNvbnRyb2xJbnRlcnBvbGFudHM6IHtcblx0XHRcdFx0XHRnZXQgdG90YWwoKSB7IHJldHVybiBzY29wZS5fY29udHJvbEludGVycG9sYW50cy5sZW5ndGg7IH0sXG5cdFx0XHRcdFx0Z2V0IGluVXNlKCkgeyByZXR1cm4gc2NvcGUuX25BY3RpdmVDb250cm9sSW50ZXJwb2xhbnRzOyB9XG5cdFx0XHRcdH1cblxuXHRcdFx0fTtcblxuXHRcdH0sXG5cblx0XHQvLyBNZW1vcnkgbWFuYWdlbWVudCBmb3IgQW5pbWF0aW9uQWN0aW9uIG9iamVjdHNcblxuXHRcdF9pc0FjdGl2ZUFjdGlvbjogZnVuY3Rpb24oIGFjdGlvbiApIHtcblxuXHRcdFx0dmFyIGluZGV4ID0gYWN0aW9uLl9jYWNoZUluZGV4O1xuXHRcdFx0cmV0dXJuIGluZGV4ICE9PSBudWxsICYmIGluZGV4IDwgdGhpcy5fbkFjdGl2ZUFjdGlvbnM7XG5cblx0XHR9LFxuXG5cdFx0X2FkZEluYWN0aXZlQWN0aW9uOiBmdW5jdGlvbiggYWN0aW9uLCBjbGlwVXVpZCwgcm9vdFV1aWQgKSB7XG5cblx0XHRcdHZhciBhY3Rpb25zID0gdGhpcy5fYWN0aW9ucyxcblx0XHRcdFx0YWN0aW9uc0J5Q2xpcCA9IHRoaXMuX2FjdGlvbnNCeUNsaXAsXG5cdFx0XHRcdGFjdGlvbnNGb3JDbGlwID0gYWN0aW9uc0J5Q2xpcFsgY2xpcFV1aWQgXTtcblxuXHRcdFx0aWYgKCBhY3Rpb25zRm9yQ2xpcCA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdGFjdGlvbnNGb3JDbGlwID0ge1xuXG5cdFx0XHRcdFx0a25vd25BY3Rpb25zOiBbIGFjdGlvbiBdLFxuXHRcdFx0XHRcdGFjdGlvbkJ5Um9vdDoge31cblxuXHRcdFx0XHR9O1xuXG5cdFx0XHRcdGFjdGlvbi5fYnlDbGlwQ2FjaGVJbmRleCA9IDA7XG5cblx0XHRcdFx0YWN0aW9uc0J5Q2xpcFsgY2xpcFV1aWQgXSA9IGFjdGlvbnNGb3JDbGlwO1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdHZhciBrbm93bkFjdGlvbnMgPSBhY3Rpb25zRm9yQ2xpcC5rbm93bkFjdGlvbnM7XG5cblx0XHRcdFx0YWN0aW9uLl9ieUNsaXBDYWNoZUluZGV4ID0ga25vd25BY3Rpb25zLmxlbmd0aDtcblx0XHRcdFx0a25vd25BY3Rpb25zLnB1c2goIGFjdGlvbiApO1xuXG5cdFx0XHR9XG5cblx0XHRcdGFjdGlvbi5fY2FjaGVJbmRleCA9IGFjdGlvbnMubGVuZ3RoO1xuXHRcdFx0YWN0aW9ucy5wdXNoKCBhY3Rpb24gKTtcblxuXHRcdFx0YWN0aW9uc0ZvckNsaXAuYWN0aW9uQnlSb290WyByb290VXVpZCBdID0gYWN0aW9uO1xuXG5cdFx0fSxcblxuXHRcdF9yZW1vdmVJbmFjdGl2ZUFjdGlvbjogZnVuY3Rpb24oIGFjdGlvbiApIHtcblxuXHRcdFx0dmFyIGFjdGlvbnMgPSB0aGlzLl9hY3Rpb25zLFxuXHRcdFx0XHRsYXN0SW5hY3RpdmVBY3Rpb24gPSBhY3Rpb25zWyBhY3Rpb25zLmxlbmd0aCAtIDEgXSxcblx0XHRcdFx0Y2FjaGVJbmRleCA9IGFjdGlvbi5fY2FjaGVJbmRleDtcblxuXHRcdFx0bGFzdEluYWN0aXZlQWN0aW9uLl9jYWNoZUluZGV4ID0gY2FjaGVJbmRleDtcblx0XHRcdGFjdGlvbnNbIGNhY2hlSW5kZXggXSA9IGxhc3RJbmFjdGl2ZUFjdGlvbjtcblx0XHRcdGFjdGlvbnMucG9wKCk7XG5cblx0XHRcdGFjdGlvbi5fY2FjaGVJbmRleCA9IG51bGw7XG5cblxuXHRcdFx0dmFyIGNsaXBVdWlkID0gYWN0aW9uLl9jbGlwLnV1aWQsXG5cdFx0XHRcdGFjdGlvbnNCeUNsaXAgPSB0aGlzLl9hY3Rpb25zQnlDbGlwLFxuXHRcdFx0XHRhY3Rpb25zRm9yQ2xpcCA9IGFjdGlvbnNCeUNsaXBbIGNsaXBVdWlkIF0sXG5cdFx0XHRcdGtub3duQWN0aW9uc0ZvckNsaXAgPSBhY3Rpb25zRm9yQ2xpcC5rbm93bkFjdGlvbnMsXG5cblx0XHRcdFx0bGFzdEtub3duQWN0aW9uID1cblx0XHRcdFx0XHRrbm93bkFjdGlvbnNGb3JDbGlwWyBrbm93bkFjdGlvbnNGb3JDbGlwLmxlbmd0aCAtIDEgXSxcblxuXHRcdFx0XHRieUNsaXBDYWNoZUluZGV4ID0gYWN0aW9uLl9ieUNsaXBDYWNoZUluZGV4O1xuXG5cdFx0XHRsYXN0S25vd25BY3Rpb24uX2J5Q2xpcENhY2hlSW5kZXggPSBieUNsaXBDYWNoZUluZGV4O1xuXHRcdFx0a25vd25BY3Rpb25zRm9yQ2xpcFsgYnlDbGlwQ2FjaGVJbmRleCBdID0gbGFzdEtub3duQWN0aW9uO1xuXHRcdFx0a25vd25BY3Rpb25zRm9yQ2xpcC5wb3AoKTtcblxuXHRcdFx0YWN0aW9uLl9ieUNsaXBDYWNoZUluZGV4ID0gbnVsbDtcblxuXG5cdFx0XHR2YXIgYWN0aW9uQnlSb290ID0gYWN0aW9uc0ZvckNsaXAuYWN0aW9uQnlSb290LFxuXHRcdFx0XHRyb290VXVpZCA9ICggYWN0aW9ucy5fbG9jYWxSb290IHx8IHRoaXMuX3Jvb3QgKS51dWlkO1xuXG5cdFx0XHRkZWxldGUgYWN0aW9uQnlSb290WyByb290VXVpZCBdO1xuXG5cdFx0XHRpZiAoIGtub3duQWN0aW9uc0ZvckNsaXAubGVuZ3RoID09PSAwICkge1xuXG5cdFx0XHRcdGRlbGV0ZSBhY3Rpb25zQnlDbGlwWyBjbGlwVXVpZCBdO1xuXG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuX3JlbW92ZUluYWN0aXZlQmluZGluZ3NGb3JBY3Rpb24oIGFjdGlvbiApO1xuXG5cdFx0fSxcblxuXHRcdF9yZW1vdmVJbmFjdGl2ZUJpbmRpbmdzRm9yQWN0aW9uOiBmdW5jdGlvbiggYWN0aW9uICkge1xuXG5cdFx0XHR2YXIgYmluZGluZ3MgPSBhY3Rpb24uX3Byb3BlcnR5QmluZGluZ3M7XG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIG4gPSBiaW5kaW5ncy5sZW5ndGg7IGkgIT09IG47ICsrIGkgKSB7XG5cblx0XHRcdFx0dmFyIGJpbmRpbmcgPSBiaW5kaW5nc1sgaSBdO1xuXG5cdFx0XHRcdGlmICggLS0gYmluZGluZy5yZWZlcmVuY2VDb3VudCA9PT0gMCApIHtcblxuXHRcdFx0XHRcdHRoaXMuX3JlbW92ZUluYWN0aXZlQmluZGluZyggYmluZGluZyApO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0fSxcblxuXHRcdF9sZW5kQWN0aW9uOiBmdW5jdGlvbiggYWN0aW9uICkge1xuXG5cdFx0XHQvLyBbIGFjdGl2ZSBhY3Rpb25zIHwgIGluYWN0aXZlIGFjdGlvbnMgIF1cblx0XHRcdC8vIFsgIGFjdGl2ZSBhY3Rpb25zID58IGluYWN0aXZlIGFjdGlvbnMgXVxuXHRcdFx0Ly8gICAgICAgICAgICAgICAgIHMgICAgICAgIGFcblx0XHRcdC8vICAgICAgICAgICAgICAgICAgPC1zd2FwLT5cblx0XHRcdC8vICAgICAgICAgICAgICAgICBhICAgICAgICBzXG5cblx0XHRcdHZhciBhY3Rpb25zID0gdGhpcy5fYWN0aW9ucyxcblx0XHRcdFx0cHJldkluZGV4ID0gYWN0aW9uLl9jYWNoZUluZGV4LFxuXG5cdFx0XHRcdGxhc3RBY3RpdmVJbmRleCA9IHRoaXMuX25BY3RpdmVBY3Rpb25zICsrLFxuXG5cdFx0XHRcdGZpcnN0SW5hY3RpdmVBY3Rpb24gPSBhY3Rpb25zWyBsYXN0QWN0aXZlSW5kZXggXTtcblxuXHRcdFx0YWN0aW9uLl9jYWNoZUluZGV4ID0gbGFzdEFjdGl2ZUluZGV4O1xuXHRcdFx0YWN0aW9uc1sgbGFzdEFjdGl2ZUluZGV4IF0gPSBhY3Rpb247XG5cblx0XHRcdGZpcnN0SW5hY3RpdmVBY3Rpb24uX2NhY2hlSW5kZXggPSBwcmV2SW5kZXg7XG5cdFx0XHRhY3Rpb25zWyBwcmV2SW5kZXggXSA9IGZpcnN0SW5hY3RpdmVBY3Rpb247XG5cblx0XHR9LFxuXG5cdFx0X3Rha2VCYWNrQWN0aW9uOiBmdW5jdGlvbiggYWN0aW9uICkge1xuXG5cdFx0XHQvLyBbICBhY3RpdmUgYWN0aW9ucyAgfCBpbmFjdGl2ZSBhY3Rpb25zIF1cblx0XHRcdC8vIFsgYWN0aXZlIGFjdGlvbnMgfDwgaW5hY3RpdmUgYWN0aW9ucyAgXVxuXHRcdFx0Ly8gICAgICAgIGEgICAgICAgIHNcblx0XHRcdC8vICAgICAgICAgPC1zd2FwLT5cblx0XHRcdC8vICAgICAgICBzICAgICAgICBhXG5cblx0XHRcdHZhciBhY3Rpb25zID0gdGhpcy5fYWN0aW9ucyxcblx0XHRcdFx0cHJldkluZGV4ID0gYWN0aW9uLl9jYWNoZUluZGV4LFxuXG5cdFx0XHRcdGZpcnN0SW5hY3RpdmVJbmRleCA9IC0tIHRoaXMuX25BY3RpdmVBY3Rpb25zLFxuXG5cdFx0XHRcdGxhc3RBY3RpdmVBY3Rpb24gPSBhY3Rpb25zWyBmaXJzdEluYWN0aXZlSW5kZXggXTtcblxuXHRcdFx0YWN0aW9uLl9jYWNoZUluZGV4ID0gZmlyc3RJbmFjdGl2ZUluZGV4O1xuXHRcdFx0YWN0aW9uc1sgZmlyc3RJbmFjdGl2ZUluZGV4IF0gPSBhY3Rpb247XG5cblx0XHRcdGxhc3RBY3RpdmVBY3Rpb24uX2NhY2hlSW5kZXggPSBwcmV2SW5kZXg7XG5cdFx0XHRhY3Rpb25zWyBwcmV2SW5kZXggXSA9IGxhc3RBY3RpdmVBY3Rpb247XG5cblx0XHR9LFxuXG5cdFx0Ly8gTWVtb3J5IG1hbmFnZW1lbnQgZm9yIFByb3BlcnR5TWl4ZXIgb2JqZWN0c1xuXG5cdFx0X2FkZEluYWN0aXZlQmluZGluZzogZnVuY3Rpb24oIGJpbmRpbmcsIHJvb3RVdWlkLCB0cmFja05hbWUgKSB7XG5cblx0XHRcdHZhciBiaW5kaW5nc0J5Um9vdCA9IHRoaXMuX2JpbmRpbmdzQnlSb290QW5kTmFtZSxcblx0XHRcdFx0YmluZGluZ0J5TmFtZSA9IGJpbmRpbmdzQnlSb290WyByb290VXVpZCBdLFxuXG5cdFx0XHRcdGJpbmRpbmdzID0gdGhpcy5fYmluZGluZ3M7XG5cblx0XHRcdGlmICggYmluZGluZ0J5TmFtZSA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdGJpbmRpbmdCeU5hbWUgPSB7fTtcblx0XHRcdFx0YmluZGluZ3NCeVJvb3RbIHJvb3RVdWlkIF0gPSBiaW5kaW5nQnlOYW1lO1xuXG5cdFx0XHR9XG5cblx0XHRcdGJpbmRpbmdCeU5hbWVbIHRyYWNrTmFtZSBdID0gYmluZGluZztcblxuXHRcdFx0YmluZGluZy5fY2FjaGVJbmRleCA9IGJpbmRpbmdzLmxlbmd0aDtcblx0XHRcdGJpbmRpbmdzLnB1c2goIGJpbmRpbmcgKTtcblxuXHRcdH0sXG5cblx0XHRfcmVtb3ZlSW5hY3RpdmVCaW5kaW5nOiBmdW5jdGlvbiggYmluZGluZyApIHtcblxuXHRcdFx0dmFyIGJpbmRpbmdzID0gdGhpcy5fYmluZGluZ3MsXG5cdFx0XHRcdHByb3BCaW5kaW5nID0gYmluZGluZy5iaW5kaW5nLFxuXHRcdFx0XHRyb290VXVpZCA9IHByb3BCaW5kaW5nLnJvb3ROb2RlLnV1aWQsXG5cdFx0XHRcdHRyYWNrTmFtZSA9IHByb3BCaW5kaW5nLnBhdGgsXG5cdFx0XHRcdGJpbmRpbmdzQnlSb290ID0gdGhpcy5fYmluZGluZ3NCeVJvb3RBbmROYW1lLFxuXHRcdFx0XHRiaW5kaW5nQnlOYW1lID0gYmluZGluZ3NCeVJvb3RbIHJvb3RVdWlkIF0sXG5cblx0XHRcdFx0bGFzdEluYWN0aXZlQmluZGluZyA9IGJpbmRpbmdzWyBiaW5kaW5ncy5sZW5ndGggLSAxIF0sXG5cdFx0XHRcdGNhY2hlSW5kZXggPSBiaW5kaW5nLl9jYWNoZUluZGV4O1xuXG5cdFx0XHRsYXN0SW5hY3RpdmVCaW5kaW5nLl9jYWNoZUluZGV4ID0gY2FjaGVJbmRleDtcblx0XHRcdGJpbmRpbmdzWyBjYWNoZUluZGV4IF0gPSBsYXN0SW5hY3RpdmVCaW5kaW5nO1xuXHRcdFx0YmluZGluZ3MucG9wKCk7XG5cblx0XHRcdGRlbGV0ZSBiaW5kaW5nQnlOYW1lWyB0cmFja05hbWUgXTtcblxuXHRcdFx0cmVtb3ZlX2VtcHR5X21hcDoge1xuXG5cdFx0XHRcdGZvciAoIHZhciBfIGluIGJpbmRpbmdCeU5hbWUgKSBicmVhayByZW1vdmVfZW1wdHlfbWFwO1xuXG5cdFx0XHRcdGRlbGV0ZSBiaW5kaW5nc0J5Um9vdFsgcm9vdFV1aWQgXTtcblxuXHRcdFx0fVxuXG5cdFx0fSxcblxuXHRcdF9sZW5kQmluZGluZzogZnVuY3Rpb24oIGJpbmRpbmcgKSB7XG5cblx0XHRcdHZhciBiaW5kaW5ncyA9IHRoaXMuX2JpbmRpbmdzLFxuXHRcdFx0XHRwcmV2SW5kZXggPSBiaW5kaW5nLl9jYWNoZUluZGV4LFxuXG5cdFx0XHRcdGxhc3RBY3RpdmVJbmRleCA9IHRoaXMuX25BY3RpdmVCaW5kaW5ncyArKyxcblxuXHRcdFx0XHRmaXJzdEluYWN0aXZlQmluZGluZyA9IGJpbmRpbmdzWyBsYXN0QWN0aXZlSW5kZXggXTtcblxuXHRcdFx0YmluZGluZy5fY2FjaGVJbmRleCA9IGxhc3RBY3RpdmVJbmRleDtcblx0XHRcdGJpbmRpbmdzWyBsYXN0QWN0aXZlSW5kZXggXSA9IGJpbmRpbmc7XG5cblx0XHRcdGZpcnN0SW5hY3RpdmVCaW5kaW5nLl9jYWNoZUluZGV4ID0gcHJldkluZGV4O1xuXHRcdFx0YmluZGluZ3NbIHByZXZJbmRleCBdID0gZmlyc3RJbmFjdGl2ZUJpbmRpbmc7XG5cblx0XHR9LFxuXG5cdFx0X3Rha2VCYWNrQmluZGluZzogZnVuY3Rpb24oIGJpbmRpbmcgKSB7XG5cblx0XHRcdHZhciBiaW5kaW5ncyA9IHRoaXMuX2JpbmRpbmdzLFxuXHRcdFx0XHRwcmV2SW5kZXggPSBiaW5kaW5nLl9jYWNoZUluZGV4LFxuXG5cdFx0XHRcdGZpcnN0SW5hY3RpdmVJbmRleCA9IC0tIHRoaXMuX25BY3RpdmVCaW5kaW5ncyxcblxuXHRcdFx0XHRsYXN0QWN0aXZlQmluZGluZyA9IGJpbmRpbmdzWyBmaXJzdEluYWN0aXZlSW5kZXggXTtcblxuXHRcdFx0YmluZGluZy5fY2FjaGVJbmRleCA9IGZpcnN0SW5hY3RpdmVJbmRleDtcblx0XHRcdGJpbmRpbmdzWyBmaXJzdEluYWN0aXZlSW5kZXggXSA9IGJpbmRpbmc7XG5cblx0XHRcdGxhc3RBY3RpdmVCaW5kaW5nLl9jYWNoZUluZGV4ID0gcHJldkluZGV4O1xuXHRcdFx0YmluZGluZ3NbIHByZXZJbmRleCBdID0gbGFzdEFjdGl2ZUJpbmRpbmc7XG5cblx0XHR9LFxuXG5cblx0XHQvLyBNZW1vcnkgbWFuYWdlbWVudCBvZiBJbnRlcnBvbGFudHMgZm9yIHdlaWdodCBhbmQgdGltZSBzY2FsZVxuXG5cdFx0X2xlbmRDb250cm9sSW50ZXJwb2xhbnQ6IGZ1bmN0aW9uKCkge1xuXG5cdFx0XHR2YXIgaW50ZXJwb2xhbnRzID0gdGhpcy5fY29udHJvbEludGVycG9sYW50cyxcblx0XHRcdFx0bGFzdEFjdGl2ZUluZGV4ID0gdGhpcy5fbkFjdGl2ZUNvbnRyb2xJbnRlcnBvbGFudHMgKyssXG5cdFx0XHRcdGludGVycG9sYW50ID0gaW50ZXJwb2xhbnRzWyBsYXN0QWN0aXZlSW5kZXggXTtcblxuXHRcdFx0aWYgKCBpbnRlcnBvbGFudCA9PT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdGludGVycG9sYW50ID0gbmV3IExpbmVhckludGVycG9sYW50KFxuXHRcdFx0XHRcdFx0bmV3IEZsb2F0MzJBcnJheSggMiApLCBuZXcgRmxvYXQzMkFycmF5KCAyICksXG5cdFx0XHRcdFx0XHRcdDEsIHRoaXMuX2NvbnRyb2xJbnRlcnBvbGFudHNSZXN1bHRCdWZmZXIgKTtcblxuXHRcdFx0XHRpbnRlcnBvbGFudC5fX2NhY2hlSW5kZXggPSBsYXN0QWN0aXZlSW5kZXg7XG5cdFx0XHRcdGludGVycG9sYW50c1sgbGFzdEFjdGl2ZUluZGV4IF0gPSBpbnRlcnBvbGFudDtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gaW50ZXJwb2xhbnQ7XG5cblx0XHR9LFxuXG5cdFx0X3Rha2VCYWNrQ29udHJvbEludGVycG9sYW50OiBmdW5jdGlvbiggaW50ZXJwb2xhbnQgKSB7XG5cblx0XHRcdHZhciBpbnRlcnBvbGFudHMgPSB0aGlzLl9jb250cm9sSW50ZXJwb2xhbnRzLFxuXHRcdFx0XHRwcmV2SW5kZXggPSBpbnRlcnBvbGFudC5fX2NhY2hlSW5kZXgsXG5cblx0XHRcdFx0Zmlyc3RJbmFjdGl2ZUluZGV4ID0gLS0gdGhpcy5fbkFjdGl2ZUNvbnRyb2xJbnRlcnBvbGFudHMsXG5cblx0XHRcdFx0bGFzdEFjdGl2ZUludGVycG9sYW50ID0gaW50ZXJwb2xhbnRzWyBmaXJzdEluYWN0aXZlSW5kZXggXTtcblxuXHRcdFx0aW50ZXJwb2xhbnQuX19jYWNoZUluZGV4ID0gZmlyc3RJbmFjdGl2ZUluZGV4O1xuXHRcdFx0aW50ZXJwb2xhbnRzWyBmaXJzdEluYWN0aXZlSW5kZXggXSA9IGludGVycG9sYW50O1xuXG5cdFx0XHRsYXN0QWN0aXZlSW50ZXJwb2xhbnQuX19jYWNoZUluZGV4ID0gcHJldkluZGV4O1xuXHRcdFx0aW50ZXJwb2xhbnRzWyBwcmV2SW5kZXggXSA9IGxhc3RBY3RpdmVJbnRlcnBvbGFudDtcblxuXHRcdH0sXG5cblx0XHRfY29udHJvbEludGVycG9sYW50c1Jlc3VsdEJ1ZmZlcjogbmV3IEZsb2F0MzJBcnJheSggMSApXG5cblx0fSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBVbmlmb3JtKCB2YWx1ZSApIHtcblxuXHRcdGlmICggdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyApIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuVW5pZm9ybTogVHlwZSBwYXJhbWV0ZXIgaXMgbm8gbG9uZ2VyIG5lZWRlZC4nICk7XG5cdFx0XHR2YWx1ZSA9IGFyZ3VtZW50c1sgMSBdO1xuXG5cdFx0fVxuXG5cdFx0dGhpcy52YWx1ZSA9IHZhbHVlO1xuXG5cdH1cblxuXHRVbmlmb3JtLnByb3RvdHlwZS5jbG9uZSA9IGZ1bmN0aW9uICgpIHtcblxuXHRcdHJldHVybiBuZXcgVW5pZm9ybSggdGhpcy52YWx1ZS5jbG9uZSA9PT0gdW5kZWZpbmVkID8gdGhpcy52YWx1ZSA6IHRoaXMudmFsdWUuY2xvbmUoKSApO1xuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgYmVuYWFkYW1zIC8gaHR0cHM6Ly90d2l0dGVyLmNvbS9iZW5fYV9hZGFtc1xuXHQgKi9cblxuXHRmdW5jdGlvbiBJbnN0YW5jZWRCdWZmZXJHZW9tZXRyeSgpIHtcblxuXHRcdEJ1ZmZlckdlb21ldHJ5LmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMudHlwZSA9ICdJbnN0YW5jZWRCdWZmZXJHZW9tZXRyeSc7XG5cdFx0dGhpcy5tYXhJbnN0YW5jZWRDb3VudCA9IHVuZGVmaW5lZDtcblxuXHR9XG5cblx0SW5zdGFuY2VkQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlICk7XG5cdEluc3RhbmNlZEJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IEluc3RhbmNlZEJ1ZmZlckdlb21ldHJ5O1xuXG5cdEluc3RhbmNlZEJ1ZmZlckdlb21ldHJ5LnByb3RvdHlwZS5pc0luc3RhbmNlZEJ1ZmZlckdlb21ldHJ5ID0gdHJ1ZTtcblxuXHRJbnN0YW5jZWRCdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUuYWRkR3JvdXAgPSBmdW5jdGlvbiAoIHN0YXJ0LCBjb3VudCwgbWF0ZXJpYWxJbmRleCApIHtcblxuXHRcdHRoaXMuZ3JvdXBzLnB1c2goIHtcblxuXHRcdFx0c3RhcnQ6IHN0YXJ0LFxuXHRcdFx0Y291bnQ6IGNvdW50LFxuXHRcdFx0bWF0ZXJpYWxJbmRleDogbWF0ZXJpYWxJbmRleFxuXG5cdFx0fSApO1xuXG5cdH07XG5cblx0SW5zdGFuY2VkQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlLmNvcHkgPSBmdW5jdGlvbiAoIHNvdXJjZSApIHtcblxuXHRcdHZhciBpbmRleCA9IHNvdXJjZS5pbmRleDtcblxuXHRcdGlmICggaW5kZXggIT09IG51bGwgKSB7XG5cblx0XHRcdHRoaXMuc2V0SW5kZXgoIGluZGV4LmNsb25lKCkgKTtcblxuXHRcdH1cblxuXHRcdHZhciBhdHRyaWJ1dGVzID0gc291cmNlLmF0dHJpYnV0ZXM7XG5cblx0XHRmb3IgKCB2YXIgbmFtZSBpbiBhdHRyaWJ1dGVzICkge1xuXG5cdFx0XHR2YXIgYXR0cmlidXRlID0gYXR0cmlidXRlc1sgbmFtZSBdO1xuXHRcdFx0dGhpcy5hZGRBdHRyaWJ1dGUoIG5hbWUsIGF0dHJpYnV0ZS5jbG9uZSgpICk7XG5cblx0XHR9XG5cblx0XHR2YXIgZ3JvdXBzID0gc291cmNlLmdyb3VwcztcblxuXHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGdyb3Vwcy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHR2YXIgZ3JvdXAgPSBncm91cHNbIGkgXTtcblx0XHRcdHRoaXMuYWRkR3JvdXAoIGdyb3VwLnN0YXJ0LCBncm91cC5jb3VudCwgZ3JvdXAubWF0ZXJpYWxJbmRleCApO1xuXG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRoaXM7XG5cblx0fTtcblxuXHQvKipcblx0ICogQGF1dGhvciBiZW5hYWRhbXMgLyBodHRwczovL3R3aXR0ZXIuY29tL2Jlbl9hX2FkYW1zXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEludGVybGVhdmVkQnVmZmVyQXR0cmlidXRlKCBpbnRlcmxlYXZlZEJ1ZmZlciwgaXRlbVNpemUsIG9mZnNldCwgbm9ybWFsaXplZCApIHtcblxuXHRcdHRoaXMudXVpZCA9IF9NYXRoLmdlbmVyYXRlVVVJRCgpO1xuXG5cdFx0dGhpcy5kYXRhID0gaW50ZXJsZWF2ZWRCdWZmZXI7XG5cdFx0dGhpcy5pdGVtU2l6ZSA9IGl0ZW1TaXplO1xuXHRcdHRoaXMub2Zmc2V0ID0gb2Zmc2V0O1xuXG5cdFx0dGhpcy5ub3JtYWxpemVkID0gbm9ybWFsaXplZCA9PT0gdHJ1ZTtcblxuXHR9XG5cblxuXHRJbnRlcmxlYXZlZEJ1ZmZlckF0dHJpYnV0ZS5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogSW50ZXJsZWF2ZWRCdWZmZXJBdHRyaWJ1dGUsXG5cblx0XHRpc0ludGVybGVhdmVkQnVmZmVyQXR0cmlidXRlOiB0cnVlLFxuXG5cdFx0Z2V0IGNvdW50KCkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5kYXRhLmNvdW50O1xuXG5cdFx0fSxcblxuXHRcdGdldCBhcnJheSgpIHtcblxuXHRcdFx0cmV0dXJuIHRoaXMuZGF0YS5hcnJheTtcblxuXHRcdH0sXG5cblx0XHRzZXRYOiBmdW5jdGlvbiAoIGluZGV4LCB4ICkge1xuXG5cdFx0XHR0aGlzLmRhdGEuYXJyYXlbIGluZGV4ICogdGhpcy5kYXRhLnN0cmlkZSArIHRoaXMub2Zmc2V0IF0gPSB4O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRZOiBmdW5jdGlvbiAoIGluZGV4LCB5ICkge1xuXG5cdFx0XHR0aGlzLmRhdGEuYXJyYXlbIGluZGV4ICogdGhpcy5kYXRhLnN0cmlkZSArIHRoaXMub2Zmc2V0ICsgMSBdID0geTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0WjogZnVuY3Rpb24gKCBpbmRleCwgeiApIHtcblxuXHRcdFx0dGhpcy5kYXRhLmFycmF5WyBpbmRleCAqIHRoaXMuZGF0YS5zdHJpZGUgKyB0aGlzLm9mZnNldCArIDIgXSA9IHo7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdHNldFc6IGZ1bmN0aW9uICggaW5kZXgsIHcgKSB7XG5cblx0XHRcdHRoaXMuZGF0YS5hcnJheVsgaW5kZXggKiB0aGlzLmRhdGEuc3RyaWRlICsgdGhpcy5vZmZzZXQgKyAzIF0gPSB3O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRnZXRYOiBmdW5jdGlvbiAoIGluZGV4ICkge1xuXG5cdFx0XHRyZXR1cm4gdGhpcy5kYXRhLmFycmF5WyBpbmRleCAqIHRoaXMuZGF0YS5zdHJpZGUgKyB0aGlzLm9mZnNldCBdO1xuXG5cdFx0fSxcblxuXHRcdGdldFk6IGZ1bmN0aW9uICggaW5kZXggKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLmRhdGEuYXJyYXlbIGluZGV4ICogdGhpcy5kYXRhLnN0cmlkZSArIHRoaXMub2Zmc2V0ICsgMSBdO1xuXG5cdFx0fSxcblxuXHRcdGdldFo6IGZ1bmN0aW9uICggaW5kZXggKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLmRhdGEuYXJyYXlbIGluZGV4ICogdGhpcy5kYXRhLnN0cmlkZSArIHRoaXMub2Zmc2V0ICsgMiBdO1xuXG5cdFx0fSxcblxuXHRcdGdldFc6IGZ1bmN0aW9uICggaW5kZXggKSB7XG5cblx0XHRcdHJldHVybiB0aGlzLmRhdGEuYXJyYXlbIGluZGV4ICogdGhpcy5kYXRhLnN0cmlkZSArIHRoaXMub2Zmc2V0ICsgMyBdO1xuXG5cdFx0fSxcblxuXHRcdHNldFhZOiBmdW5jdGlvbiAoIGluZGV4LCB4LCB5ICkge1xuXG5cdFx0XHRpbmRleCA9IGluZGV4ICogdGhpcy5kYXRhLnN0cmlkZSArIHRoaXMub2Zmc2V0O1xuXG5cdFx0XHR0aGlzLmRhdGEuYXJyYXlbIGluZGV4ICsgMCBdID0geDtcblx0XHRcdHRoaXMuZGF0YS5hcnJheVsgaW5kZXggKyAxIF0gPSB5O1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRzZXRYWVo6IGZ1bmN0aW9uICggaW5kZXgsIHgsIHksIHogKSB7XG5cblx0XHRcdGluZGV4ID0gaW5kZXggKiB0aGlzLmRhdGEuc3RyaWRlICsgdGhpcy5vZmZzZXQ7XG5cblx0XHRcdHRoaXMuZGF0YS5hcnJheVsgaW5kZXggKyAwIF0gPSB4O1xuXHRcdFx0dGhpcy5kYXRhLmFycmF5WyBpbmRleCArIDEgXSA9IHk7XG5cdFx0XHR0aGlzLmRhdGEuYXJyYXlbIGluZGV4ICsgMiBdID0gejtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0WFlaVzogZnVuY3Rpb24gKCBpbmRleCwgeCwgeSwgeiwgdyApIHtcblxuXHRcdFx0aW5kZXggPSBpbmRleCAqIHRoaXMuZGF0YS5zdHJpZGUgKyB0aGlzLm9mZnNldDtcblxuXHRcdFx0dGhpcy5kYXRhLmFycmF5WyBpbmRleCArIDAgXSA9IHg7XG5cdFx0XHR0aGlzLmRhdGEuYXJyYXlbIGluZGV4ICsgMSBdID0geTtcblx0XHRcdHRoaXMuZGF0YS5hcnJheVsgaW5kZXggKyAyIF0gPSB6O1xuXHRcdFx0dGhpcy5kYXRhLmFycmF5WyBpbmRleCArIDMgXSA9IHc7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgYmVuYWFkYW1zIC8gaHR0cHM6Ly90d2l0dGVyLmNvbS9iZW5fYV9hZGFtc1xuXHQgKi9cblxuXHRmdW5jdGlvbiBJbnRlcmxlYXZlZEJ1ZmZlciggYXJyYXksIHN0cmlkZSApIHtcblxuXHRcdHRoaXMudXVpZCA9IF9NYXRoLmdlbmVyYXRlVVVJRCgpO1xuXG5cdFx0dGhpcy5hcnJheSA9IGFycmF5O1xuXHRcdHRoaXMuc3RyaWRlID0gc3RyaWRlO1xuXHRcdHRoaXMuY291bnQgPSBhcnJheSAhPT0gdW5kZWZpbmVkID8gYXJyYXkubGVuZ3RoIC8gc3RyaWRlIDogMDtcblxuXHRcdHRoaXMuZHluYW1pYyA9IGZhbHNlO1xuXHRcdHRoaXMudXBkYXRlUmFuZ2UgPSB7IG9mZnNldDogMCwgY291bnQ6IC0gMSB9O1xuXG5cdFx0dGhpcy5vblVwbG9hZENhbGxiYWNrID0gZnVuY3Rpb24gKCkge307XG5cblx0XHR0aGlzLnZlcnNpb24gPSAwO1xuXG5cdH1cblxuXHRJbnRlcmxlYXZlZEJ1ZmZlci5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogSW50ZXJsZWF2ZWRCdWZmZXIsXG5cblx0XHRpc0ludGVybGVhdmVkQnVmZmVyOiB0cnVlLFxuXG5cdFx0c2V0IG5lZWRzVXBkYXRlKCB2YWx1ZSApIHtcblxuXHRcdFx0aWYgKCB2YWx1ZSA9PT0gdHJ1ZSApIHRoaXMudmVyc2lvbiArKztcblxuXHRcdH0sXG5cblx0XHRzZXRBcnJheTogZnVuY3Rpb24gKCBhcnJheSApIHtcblxuXHRcdFx0aWYgKCBBcnJheS5pc0FycmF5KCBhcnJheSApICkge1xuXG5cdFx0XHRcdHRocm93IG5ldyBUeXBlRXJyb3IoICdUSFJFRS5CdWZmZXJBdHRyaWJ1dGU6IGFycmF5IHNob3VsZCBiZSBhIFR5cGVkIEFycmF5LicgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLmNvdW50ID0gYXJyYXkgIT09IHVuZGVmaW5lZCA/IGFycmF5Lmxlbmd0aCAvIHRoaXMuc3RyaWRlIDogMDtcblx0XHRcdHRoaXMuYXJyYXkgPSBhcnJheTtcblxuXHRcdH0sXG5cblx0XHRzZXREeW5hbWljOiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHR0aGlzLmR5bmFtaWMgPSB2YWx1ZTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Y29weTogZnVuY3Rpb24gKCBzb3VyY2UgKSB7XG5cblx0XHRcdHRoaXMuYXJyYXkgPSBuZXcgc291cmNlLmFycmF5LmNvbnN0cnVjdG9yKCBzb3VyY2UuYXJyYXkgKTtcblx0XHRcdHRoaXMuY291bnQgPSBzb3VyY2UuY291bnQ7XG5cdFx0XHR0aGlzLnN0cmlkZSA9IHNvdXJjZS5zdHJpZGU7XG5cdFx0XHR0aGlzLmR5bmFtaWMgPSBzb3VyY2UuZHluYW1pYztcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Y29weUF0OiBmdW5jdGlvbiAoIGluZGV4MSwgYXR0cmlidXRlLCBpbmRleDIgKSB7XG5cblx0XHRcdGluZGV4MSAqPSB0aGlzLnN0cmlkZTtcblx0XHRcdGluZGV4MiAqPSBhdHRyaWJ1dGUuc3RyaWRlO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSB0aGlzLnN0cmlkZTsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0dGhpcy5hcnJheVsgaW5kZXgxICsgaSBdID0gYXR0cmlidXRlLmFycmF5WyBpbmRleDIgKyBpIF07XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0OiBmdW5jdGlvbiAoIHZhbHVlLCBvZmZzZXQgKSB7XG5cblx0XHRcdGlmICggb2Zmc2V0ID09PSB1bmRlZmluZWQgKSBvZmZzZXQgPSAwO1xuXG5cdFx0XHR0aGlzLmFycmF5LnNldCggdmFsdWUsIG9mZnNldCApO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH0sXG5cblx0XHRjbG9uZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRyZXR1cm4gbmV3IHRoaXMuY29uc3RydWN0b3IoKS5jb3B5KCB0aGlzICk7XG5cblx0XHR9LFxuXG5cdFx0b25VcGxvYWQ6IGZ1bmN0aW9uICggY2FsbGJhY2sgKSB7XG5cblx0XHRcdHRoaXMub25VcGxvYWRDYWxsYmFjayA9IGNhbGxiYWNrO1xuXG5cdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdH1cblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGJlbmFhZGFtcyAvIGh0dHBzOi8vdHdpdHRlci5jb20vYmVuX2FfYWRhbXNcblx0ICovXG5cblx0ZnVuY3Rpb24gSW5zdGFuY2VkSW50ZXJsZWF2ZWRCdWZmZXIoIGFycmF5LCBzdHJpZGUsIG1lc2hQZXJBdHRyaWJ1dGUgKSB7XG5cblx0XHRJbnRlcmxlYXZlZEJ1ZmZlci5jYWxsKCB0aGlzLCBhcnJheSwgc3RyaWRlICk7XG5cblx0XHR0aGlzLm1lc2hQZXJBdHRyaWJ1dGUgPSBtZXNoUGVyQXR0cmlidXRlIHx8IDE7XG5cblx0fVxuXG5cdEluc3RhbmNlZEludGVybGVhdmVkQnVmZmVyLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEludGVybGVhdmVkQnVmZmVyLnByb3RvdHlwZSApO1xuXHRJbnN0YW5jZWRJbnRlcmxlYXZlZEJ1ZmZlci5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBJbnN0YW5jZWRJbnRlcmxlYXZlZEJ1ZmZlcjtcblxuXHRJbnN0YW5jZWRJbnRlcmxlYXZlZEJ1ZmZlci5wcm90b3R5cGUuaXNJbnN0YW5jZWRJbnRlcmxlYXZlZEJ1ZmZlciA9IHRydWU7XG5cblx0SW5zdGFuY2VkSW50ZXJsZWF2ZWRCdWZmZXIucHJvdG90eXBlLmNvcHkgPSBmdW5jdGlvbiAoIHNvdXJjZSApIHtcblxuXHRcdEludGVybGVhdmVkQnVmZmVyLnByb3RvdHlwZS5jb3B5LmNhbGwoIHRoaXMsIHNvdXJjZSApO1xuXG5cdFx0dGhpcy5tZXNoUGVyQXR0cmlidXRlID0gc291cmNlLm1lc2hQZXJBdHRyaWJ1dGU7XG5cblx0XHRyZXR1cm4gdGhpcztcblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGJlbmFhZGFtcyAvIGh0dHBzOi8vdHdpdHRlci5jb20vYmVuX2FfYWRhbXNcblx0ICovXG5cblx0ZnVuY3Rpb24gSW5zdGFuY2VkQnVmZmVyQXR0cmlidXRlKCBhcnJheSwgaXRlbVNpemUsIG1lc2hQZXJBdHRyaWJ1dGUgKSB7XG5cblx0XHRCdWZmZXJBdHRyaWJ1dGUuY2FsbCggdGhpcywgYXJyYXksIGl0ZW1TaXplICk7XG5cblx0XHR0aGlzLm1lc2hQZXJBdHRyaWJ1dGUgPSBtZXNoUGVyQXR0cmlidXRlIHx8IDE7XG5cblx0fVxuXG5cdEluc3RhbmNlZEJ1ZmZlckF0dHJpYnV0ZS5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBCdWZmZXJBdHRyaWJ1dGUucHJvdG90eXBlICk7XG5cdEluc3RhbmNlZEJ1ZmZlckF0dHJpYnV0ZS5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBJbnN0YW5jZWRCdWZmZXJBdHRyaWJ1dGU7XG5cblx0SW5zdGFuY2VkQnVmZmVyQXR0cmlidXRlLnByb3RvdHlwZS5pc0luc3RhbmNlZEJ1ZmZlckF0dHJpYnV0ZSA9IHRydWU7XG5cblx0SW5zdGFuY2VkQnVmZmVyQXR0cmlidXRlLnByb3RvdHlwZS5jb3B5ID0gZnVuY3Rpb24gKCBzb3VyY2UgKSB7XG5cblx0XHRCdWZmZXJBdHRyaWJ1dGUucHJvdG90eXBlLmNvcHkuY2FsbCggdGhpcywgc291cmNlICk7XG5cblx0XHR0aGlzLm1lc2hQZXJBdHRyaWJ1dGUgPSBzb3VyY2UubWVzaFBlckF0dHJpYnV0ZTtcblxuXHRcdHJldHVybiB0aGlzO1xuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqIEBhdXRob3IgYmhvdXN0b24gLyBodHRwOi8vY2xhcmEuaW8vXG5cdCAqIEBhdXRob3Igc3RlcGhvbWkgLyBodHRwOi8vc3RlcGhhbmVnaW5pZXIuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBSYXljYXN0ZXIoIG9yaWdpbiwgZGlyZWN0aW9uLCBuZWFyLCBmYXIgKSB7XG5cblx0XHR0aGlzLnJheSA9IG5ldyBSYXkoIG9yaWdpbiwgZGlyZWN0aW9uICk7XG5cdFx0Ly8gZGlyZWN0aW9uIGlzIGFzc3VtZWQgdG8gYmUgbm9ybWFsaXplZCAoZm9yIGFjY3VyYXRlIGRpc3RhbmNlIGNhbGN1bGF0aW9ucylcblxuXHRcdHRoaXMubmVhciA9IG5lYXIgfHwgMDtcblx0XHR0aGlzLmZhciA9IGZhciB8fCBJbmZpbml0eTtcblxuXHRcdHRoaXMucGFyYW1zID0ge1xuXHRcdFx0TWVzaDoge30sXG5cdFx0XHRMaW5lOiB7fSxcblx0XHRcdExPRDoge30sXG5cdFx0XHRQb2ludHM6IHsgdGhyZXNob2xkOiAxIH0sXG5cdFx0XHRTcHJpdGU6IHt9XG5cdFx0fTtcblxuXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKCB0aGlzLnBhcmFtcywge1xuXHRcdFx0UG9pbnRDbG91ZDoge1xuXHRcdFx0XHRnZXQ6IGZ1bmN0aW9uICgpIHtcblx0XHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5SYXljYXN0ZXI6IHBhcmFtcy5Qb2ludENsb3VkIGhhcyBiZWVuIHJlbmFtZWQgdG8gcGFyYW1zLlBvaW50cy4nICk7XG5cdFx0XHRcdFx0cmV0dXJuIHRoaXMuUG9pbnRzO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fSApO1xuXG5cdH1cblxuXHRmdW5jdGlvbiBhc2NTb3J0KCBhLCBiICkge1xuXG5cdFx0cmV0dXJuIGEuZGlzdGFuY2UgLSBiLmRpc3RhbmNlO1xuXG5cdH1cblxuXHRmdW5jdGlvbiBpbnRlcnNlY3RPYmplY3QoIG9iamVjdCwgcmF5Y2FzdGVyLCBpbnRlcnNlY3RzLCByZWN1cnNpdmUgKSB7XG5cblx0XHRpZiAoIG9iamVjdC52aXNpYmxlID09PSBmYWxzZSApIHJldHVybjtcblxuXHRcdG9iamVjdC5yYXljYXN0KCByYXljYXN0ZXIsIGludGVyc2VjdHMgKTtcblxuXHRcdGlmICggcmVjdXJzaXZlID09PSB0cnVlICkge1xuXG5cdFx0XHR2YXIgY2hpbGRyZW4gPSBvYmplY3QuY2hpbGRyZW47XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGNoaWxkcmVuLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0aW50ZXJzZWN0T2JqZWN0KCBjaGlsZHJlblsgaSBdLCByYXljYXN0ZXIsIGludGVyc2VjdHMsIHRydWUgKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdH1cblxuXHQvL1xuXG5cdFJheWNhc3Rlci5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogUmF5Y2FzdGVyLFxuXG5cdFx0bGluZVByZWNpc2lvbjogMSxcblxuXHRcdHNldDogZnVuY3Rpb24gKCBvcmlnaW4sIGRpcmVjdGlvbiApIHtcblxuXHRcdFx0Ly8gZGlyZWN0aW9uIGlzIGFzc3VtZWQgdG8gYmUgbm9ybWFsaXplZCAoZm9yIGFjY3VyYXRlIGRpc3RhbmNlIGNhbGN1bGF0aW9ucylcblxuXHRcdFx0dGhpcy5yYXkuc2V0KCBvcmlnaW4sIGRpcmVjdGlvbiApO1xuXG5cdFx0fSxcblxuXHRcdHNldEZyb21DYW1lcmE6IGZ1bmN0aW9uICggY29vcmRzLCBjYW1lcmEgKSB7XG5cblx0XHRcdGlmICggKGNhbWVyYSAmJiBjYW1lcmEuaXNQZXJzcGVjdGl2ZUNhbWVyYSkgKSB7XG5cblx0XHRcdFx0dGhpcy5yYXkub3JpZ2luLnNldEZyb21NYXRyaXhQb3NpdGlvbiggY2FtZXJhLm1hdHJpeFdvcmxkICk7XG5cdFx0XHRcdHRoaXMucmF5LmRpcmVjdGlvbi5zZXQoIGNvb3Jkcy54LCBjb29yZHMueSwgMC41ICkudW5wcm9qZWN0KCBjYW1lcmEgKS5zdWIoIHRoaXMucmF5Lm9yaWdpbiApLm5vcm1hbGl6ZSgpO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCAoY2FtZXJhICYmIGNhbWVyYS5pc09ydGhvZ3JhcGhpY0NhbWVyYSkgKSB7XG5cblx0XHRcdFx0dGhpcy5yYXkub3JpZ2luLnNldCggY29vcmRzLngsIGNvb3Jkcy55LCAoIGNhbWVyYS5uZWFyICsgY2FtZXJhLmZhciApIC8gKCBjYW1lcmEubmVhciAtIGNhbWVyYS5mYXIgKSApLnVucHJvamVjdCggY2FtZXJhICk7IC8vIHNldCBvcmlnaW4gaW4gcGxhbmUgb2YgY2FtZXJhXG5cdFx0XHRcdHRoaXMucmF5LmRpcmVjdGlvbi5zZXQoIDAsIDAsIC0gMSApLnRyYW5zZm9ybURpcmVjdGlvbiggY2FtZXJhLm1hdHJpeFdvcmxkICk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0Y29uc29sZS5lcnJvciggJ1RIUkVFLlJheWNhc3RlcjogVW5zdXBwb3J0ZWQgY2FtZXJhIHR5cGUuJyApO1xuXG5cdFx0XHR9XG5cblx0XHR9LFxuXG5cdFx0aW50ZXJzZWN0T2JqZWN0OiBmdW5jdGlvbiAoIG9iamVjdCwgcmVjdXJzaXZlICkge1xuXG5cdFx0XHR2YXIgaW50ZXJzZWN0cyA9IFtdO1xuXG5cdFx0XHRpbnRlcnNlY3RPYmplY3QoIG9iamVjdCwgdGhpcywgaW50ZXJzZWN0cywgcmVjdXJzaXZlICk7XG5cblx0XHRcdGludGVyc2VjdHMuc29ydCggYXNjU29ydCApO1xuXG5cdFx0XHRyZXR1cm4gaW50ZXJzZWN0cztcblxuXHRcdH0sXG5cblx0XHRpbnRlcnNlY3RPYmplY3RzOiBmdW5jdGlvbiAoIG9iamVjdHMsIHJlY3Vyc2l2ZSApIHtcblxuXHRcdFx0dmFyIGludGVyc2VjdHMgPSBbXTtcblxuXHRcdFx0aWYgKCBBcnJheS5pc0FycmF5KCBvYmplY3RzICkgPT09IGZhbHNlICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLlJheWNhc3Rlci5pbnRlcnNlY3RPYmplY3RzOiBvYmplY3RzIGlzIG5vdCBhbiBBcnJheS4nICk7XG5cdFx0XHRcdHJldHVybiBpbnRlcnNlY3RzO1xuXG5cdFx0XHR9XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IG9iamVjdHMubGVuZ3RoOyBpIDwgbDsgaSArKyApIHtcblxuXHRcdFx0XHRpbnRlcnNlY3RPYmplY3QoIG9iamVjdHNbIGkgXSwgdGhpcywgaW50ZXJzZWN0cywgcmVjdXJzaXZlICk7XG5cblx0XHRcdH1cblxuXHRcdFx0aW50ZXJzZWN0cy5zb3J0KCBhc2NTb3J0ICk7XG5cblx0XHRcdHJldHVybiBpbnRlcnNlY3RzO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgYWx0ZXJlZHEgLyBodHRwOi8vYWx0ZXJlZHF1YWxpYS5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIENsb2NrKCBhdXRvU3RhcnQgKSB7XG5cblx0XHR0aGlzLmF1dG9TdGFydCA9ICggYXV0b1N0YXJ0ICE9PSB1bmRlZmluZWQgKSA/IGF1dG9TdGFydCA6IHRydWU7XG5cblx0XHR0aGlzLnN0YXJ0VGltZSA9IDA7XG5cdFx0dGhpcy5vbGRUaW1lID0gMDtcblx0XHR0aGlzLmVsYXBzZWRUaW1lID0gMDtcblxuXHRcdHRoaXMucnVubmluZyA9IGZhbHNlO1xuXG5cdH1cblxuXHRDbG9jay5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogQ2xvY2ssXG5cblx0XHRzdGFydDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR0aGlzLnN0YXJ0VGltZSA9ICggcGVyZm9ybWFuY2UgfHwgRGF0ZSApLm5vdygpO1xuXG5cdFx0XHR0aGlzLm9sZFRpbWUgPSB0aGlzLnN0YXJ0VGltZTtcblx0XHRcdHRoaXMuZWxhcHNlZFRpbWUgPSAwO1xuXHRcdFx0dGhpcy5ydW5uaW5nID0gdHJ1ZTtcblxuXHRcdH0sXG5cblx0XHRzdG9wOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMuZ2V0RWxhcHNlZFRpbWUoKTtcblx0XHRcdHRoaXMucnVubmluZyA9IGZhbHNlO1xuXG5cdFx0fSxcblxuXHRcdGdldEVsYXBzZWRUaW1lOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHRoaXMuZ2V0RGVsdGEoKTtcblx0XHRcdHJldHVybiB0aGlzLmVsYXBzZWRUaW1lO1xuXG5cdFx0fSxcblxuXHRcdGdldERlbHRhOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciBkaWZmID0gMDtcblxuXHRcdFx0aWYgKCB0aGlzLmF1dG9TdGFydCAmJiAhIHRoaXMucnVubmluZyApIHtcblxuXHRcdFx0XHR0aGlzLnN0YXJ0KCk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCB0aGlzLnJ1bm5pbmcgKSB7XG5cblx0XHRcdFx0dmFyIG5ld1RpbWUgPSAoIHBlcmZvcm1hbmNlIHx8IERhdGUgKS5ub3coKTtcblxuXHRcdFx0XHRkaWZmID0gKCBuZXdUaW1lIC0gdGhpcy5vbGRUaW1lICkgLyAxMDAwO1xuXHRcdFx0XHR0aGlzLm9sZFRpbWUgPSBuZXdUaW1lO1xuXG5cdFx0XHRcdHRoaXMuZWxhcHNlZFRpbWUgKz0gZGlmZjtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gZGlmZjtcblxuXHRcdH1cblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBTcGxpbmUgZnJvbSBUd2Vlbi5qcywgc2xpZ2h0bHkgb3B0aW1pemVkIChhbmQgdHJhc2hlZClcblx0ICogaHR0cDovL3NvbGUuZ2l0aHViLmNvbS90d2Vlbi5qcy9leGFtcGxlcy8wNV9zcGxpbmUuaHRtbFxuXHQgKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBTcGxpbmUoIHBvaW50cyApIHtcblxuXHRcdHRoaXMucG9pbnRzID0gcG9pbnRzO1xuXG5cdFx0dmFyIGMgPSBbXSwgdjMgPSB7IHg6IDAsIHk6IDAsIHo6IDAgfSxcblx0XHRwb2ludCwgaW50UG9pbnQsIHdlaWdodCwgdzIsIHczLFxuXHRcdHBhLCBwYiwgcGMsIHBkO1xuXG5cdFx0dGhpcy5pbml0RnJvbUFycmF5ID0gZnVuY3Rpb24gKCBhICkge1xuXG5cdFx0XHR0aGlzLnBvaW50cyA9IFtdO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBhLmxlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHR0aGlzLnBvaW50c1sgaSBdID0geyB4OiBhWyBpIF1bIDAgXSwgeTogYVsgaSBdWyAxIF0sIHo6IGFbIGkgXVsgMiBdIH07XG5cblx0XHRcdH1cblxuXHRcdH07XG5cblx0XHR0aGlzLmdldFBvaW50ID0gZnVuY3Rpb24gKCBrICkge1xuXG5cdFx0XHRwb2ludCA9ICggdGhpcy5wb2ludHMubGVuZ3RoIC0gMSApICogaztcblx0XHRcdGludFBvaW50ID0gTWF0aC5mbG9vciggcG9pbnQgKTtcblx0XHRcdHdlaWdodCA9IHBvaW50IC0gaW50UG9pbnQ7XG5cblx0XHRcdGNbIDAgXSA9IGludFBvaW50ID09PSAwID8gaW50UG9pbnQgOiBpbnRQb2ludCAtIDE7XG5cdFx0XHRjWyAxIF0gPSBpbnRQb2ludDtcblx0XHRcdGNbIDIgXSA9IGludFBvaW50ICA+IHRoaXMucG9pbnRzLmxlbmd0aCAtIDIgPyB0aGlzLnBvaW50cy5sZW5ndGggLSAxIDogaW50UG9pbnQgKyAxO1xuXHRcdFx0Y1sgMyBdID0gaW50UG9pbnQgID4gdGhpcy5wb2ludHMubGVuZ3RoIC0gMyA/IHRoaXMucG9pbnRzLmxlbmd0aCAtIDEgOiBpbnRQb2ludCArIDI7XG5cblx0XHRcdHBhID0gdGhpcy5wb2ludHNbIGNbIDAgXSBdO1xuXHRcdFx0cGIgPSB0aGlzLnBvaW50c1sgY1sgMSBdIF07XG5cdFx0XHRwYyA9IHRoaXMucG9pbnRzWyBjWyAyIF0gXTtcblx0XHRcdHBkID0gdGhpcy5wb2ludHNbIGNbIDMgXSBdO1xuXG5cdFx0XHR3MiA9IHdlaWdodCAqIHdlaWdodDtcblx0XHRcdHczID0gd2VpZ2h0ICogdzI7XG5cblx0XHRcdHYzLnggPSBpbnRlcnBvbGF0ZSggcGEueCwgcGIueCwgcGMueCwgcGQueCwgd2VpZ2h0LCB3MiwgdzMgKTtcblx0XHRcdHYzLnkgPSBpbnRlcnBvbGF0ZSggcGEueSwgcGIueSwgcGMueSwgcGQueSwgd2VpZ2h0LCB3MiwgdzMgKTtcblx0XHRcdHYzLnogPSBpbnRlcnBvbGF0ZSggcGEueiwgcGIueiwgcGMueiwgcGQueiwgd2VpZ2h0LCB3MiwgdzMgKTtcblxuXHRcdFx0cmV0dXJuIHYzO1xuXG5cdFx0fTtcblxuXHRcdHRoaXMuZ2V0Q29udHJvbFBvaW50c0FycmF5ID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0XHR2YXIgaSwgcCwgbCA9IHRoaXMucG9pbnRzLmxlbmd0aCxcblx0XHRcdFx0Y29vcmRzID0gW107XG5cblx0XHRcdGZvciAoIGkgPSAwOyBpIDwgbDsgaSArKyApIHtcblxuXHRcdFx0XHRwID0gdGhpcy5wb2ludHNbIGkgXTtcblx0XHRcdFx0Y29vcmRzWyBpIF0gPSBbIHAueCwgcC55LCBwLnogXTtcblxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gY29vcmRzO1xuXG5cdFx0fTtcblxuXHRcdC8vIGFwcHJveGltYXRlIGxlbmd0aCBieSBzdW1taW5nIGxpbmVhciBzZWdtZW50c1xuXG5cdFx0dGhpcy5nZXRMZW5ndGggPSBmdW5jdGlvbiAoIG5TdWJEaXZpc2lvbnMgKSB7XG5cblx0XHRcdHZhciBpLCBpbmRleCwgblNhbXBsZXMsIHBvc2l0aW9uLFxuXHRcdFx0XHRwb2ludCA9IDAsIGludFBvaW50ID0gMCwgb2xkSW50UG9pbnQgPSAwLFxuXHRcdFx0XHRvbGRQb3NpdGlvbiA9IG5ldyBWZWN0b3IzKCksXG5cdFx0XHRcdHRtcFZlYyA9IG5ldyBWZWN0b3IzKCksXG5cdFx0XHRcdGNodW5rTGVuZ3RocyA9IFtdLFxuXHRcdFx0XHR0b3RhbExlbmd0aCA9IDA7XG5cblx0XHRcdC8vIGZpcnN0IHBvaW50IGhhcyAwIGxlbmd0aFxuXG5cdFx0XHRjaHVua0xlbmd0aHNbIDAgXSA9IDA7XG5cblx0XHRcdGlmICggISBuU3ViRGl2aXNpb25zICkgblN1YkRpdmlzaW9ucyA9IDEwMDtcblxuXHRcdFx0blNhbXBsZXMgPSB0aGlzLnBvaW50cy5sZW5ndGggKiBuU3ViRGl2aXNpb25zO1xuXG5cdFx0XHRvbGRQb3NpdGlvbi5jb3B5KCB0aGlzLnBvaW50c1sgMCBdICk7XG5cblx0XHRcdGZvciAoIGkgPSAxOyBpIDwgblNhbXBsZXM7IGkgKysgKSB7XG5cblx0XHRcdFx0aW5kZXggPSBpIC8gblNhbXBsZXM7XG5cblx0XHRcdFx0cG9zaXRpb24gPSB0aGlzLmdldFBvaW50KCBpbmRleCApO1xuXHRcdFx0XHR0bXBWZWMuY29weSggcG9zaXRpb24gKTtcblxuXHRcdFx0XHR0b3RhbExlbmd0aCArPSB0bXBWZWMuZGlzdGFuY2VUbyggb2xkUG9zaXRpb24gKTtcblxuXHRcdFx0XHRvbGRQb3NpdGlvbi5jb3B5KCBwb3NpdGlvbiApO1xuXG5cdFx0XHRcdHBvaW50ID0gKCB0aGlzLnBvaW50cy5sZW5ndGggLSAxICkgKiBpbmRleDtcblx0XHRcdFx0aW50UG9pbnQgPSBNYXRoLmZsb29yKCBwb2ludCApO1xuXG5cdFx0XHRcdGlmICggaW50UG9pbnQgIT09IG9sZEludFBvaW50ICkge1xuXG5cdFx0XHRcdFx0Y2h1bmtMZW5ndGhzWyBpbnRQb2ludCBdID0gdG90YWxMZW5ndGg7XG5cdFx0XHRcdFx0b2xkSW50UG9pbnQgPSBpbnRQb2ludDtcblxuXHRcdFx0XHR9XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gbGFzdCBwb2ludCBlbmRzIHdpdGggdG90YWwgbGVuZ3RoXG5cblx0XHRcdGNodW5rTGVuZ3Roc1sgY2h1bmtMZW5ndGhzLmxlbmd0aCBdID0gdG90YWxMZW5ndGg7XG5cblx0XHRcdHJldHVybiB7IGNodW5rczogY2h1bmtMZW5ndGhzLCB0b3RhbDogdG90YWxMZW5ndGggfTtcblxuXHRcdH07XG5cblx0XHR0aGlzLnJlcGFyYW1ldHJpemVCeUFyY0xlbmd0aCA9IGZ1bmN0aW9uICggc2FtcGxpbmdDb2VmICkge1xuXG5cdFx0XHR2YXIgaSwgaixcblx0XHRcdFx0aW5kZXgsIGluZGV4Q3VycmVudCwgaW5kZXhOZXh0LFxuXHRcdFx0XHRyZWFsRGlzdGFuY2UsXG5cdFx0XHRcdHNhbXBsaW5nLCBwb3NpdGlvbixcblx0XHRcdFx0bmV3cG9pbnRzID0gW10sXG5cdFx0XHRcdHRtcFZlYyA9IG5ldyBWZWN0b3IzKCksXG5cdFx0XHRcdHNsID0gdGhpcy5nZXRMZW5ndGgoKTtcblxuXHRcdFx0bmV3cG9pbnRzLnB1c2goIHRtcFZlYy5jb3B5KCB0aGlzLnBvaW50c1sgMCBdICkuY2xvbmUoKSApO1xuXG5cdFx0XHRmb3IgKCBpID0gMTsgaSA8IHRoaXMucG9pbnRzLmxlbmd0aDsgaSArKyApIHtcblxuXHRcdFx0XHQvL3RtcFZlYy5jb3B5KCB0aGlzLnBvaW50c1sgaSAtIDEgXSApO1xuXHRcdFx0XHQvL2xpbmVhckRpc3RhbmNlID0gdG1wVmVjLmRpc3RhbmNlVG8oIHRoaXMucG9pbnRzWyBpIF0gKTtcblxuXHRcdFx0XHRyZWFsRGlzdGFuY2UgPSBzbC5jaHVua3NbIGkgXSAtIHNsLmNodW5rc1sgaSAtIDEgXTtcblxuXHRcdFx0XHRzYW1wbGluZyA9IE1hdGguY2VpbCggc2FtcGxpbmdDb2VmICogcmVhbERpc3RhbmNlIC8gc2wudG90YWwgKTtcblxuXHRcdFx0XHRpbmRleEN1cnJlbnQgPSAoIGkgLSAxICkgLyAoIHRoaXMucG9pbnRzLmxlbmd0aCAtIDEgKTtcblx0XHRcdFx0aW5kZXhOZXh0ID0gaSAvICggdGhpcy5wb2ludHMubGVuZ3RoIC0gMSApO1xuXG5cdFx0XHRcdGZvciAoIGogPSAxOyBqIDwgc2FtcGxpbmcgLSAxOyBqICsrICkge1xuXG5cdFx0XHRcdFx0aW5kZXggPSBpbmRleEN1cnJlbnQgKyBqICogKCAxIC8gc2FtcGxpbmcgKSAqICggaW5kZXhOZXh0IC0gaW5kZXhDdXJyZW50ICk7XG5cblx0XHRcdFx0XHRwb3NpdGlvbiA9IHRoaXMuZ2V0UG9pbnQoIGluZGV4ICk7XG5cdFx0XHRcdFx0bmV3cG9pbnRzLnB1c2goIHRtcFZlYy5jb3B5KCBwb3NpdGlvbiApLmNsb25lKCkgKTtcblxuXHRcdFx0XHR9XG5cblx0XHRcdFx0bmV3cG9pbnRzLnB1c2goIHRtcFZlYy5jb3B5KCB0aGlzLnBvaW50c1sgaSBdICkuY2xvbmUoKSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdHRoaXMucG9pbnRzID0gbmV3cG9pbnRzO1xuXG5cdFx0fTtcblxuXHRcdC8vIENhdG11bGwtUm9tXG5cblx0XHRmdW5jdGlvbiBpbnRlcnBvbGF0ZSggcDAsIHAxLCBwMiwgcDMsIHQsIHQyLCB0MyApIHtcblxuXHRcdFx0dmFyIHYwID0gKCBwMiAtIHAwICkgKiAwLjUsXG5cdFx0XHRcdHYxID0gKCBwMyAtIHAxICkgKiAwLjU7XG5cblx0XHRcdHJldHVybiAoIDIgKiAoIHAxIC0gcDIgKSArIHYwICsgdjEgKSAqIHQzICsgKCAtIDMgKiAoIHAxIC0gcDIgKSAtIDIgKiB2MCAtIHYxICkgKiB0MiArIHYwICogdCArIHAxO1xuXG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogQGF1dGhvciBiaG91c3RvbiAvIGh0dHA6Ly9jbGFyYS5pb1xuXHQgKiBAYXV0aG9yIFdlc3RMYW5nbGV5IC8gaHR0cDovL2dpdGh1Yi5jb20vV2VzdExhbmdsZXlcblx0ICpcblx0ICogUmVmOiBodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TcGhlcmljYWxfY29vcmRpbmF0ZV9zeXN0ZW1cblx0ICpcblx0ICogVGhlIHBvbGVzIChwaGkpIGFyZSBhdCB0aGUgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHkgYXhpcy5cblx0ICogVGhlIGVxdWF0b3Igc3RhcnRzIGF0IHBvc2l0aXZlIHouXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFNwaGVyaWNhbCggcmFkaXVzLCBwaGksIHRoZXRhICkge1xuXG5cdFx0dGhpcy5yYWRpdXMgPSAoIHJhZGl1cyAhPT0gdW5kZWZpbmVkICkgPyByYWRpdXMgOiAxLjA7XG5cdFx0dGhpcy5waGkgPSAoIHBoaSAhPT0gdW5kZWZpbmVkICkgPyBwaGkgOiAwOyAvLyB1cCAvIGRvd24gdG93YXJkcyB0b3AgYW5kIGJvdHRvbSBwb2xlXG5cdFx0dGhpcy50aGV0YSA9ICggdGhldGEgIT09IHVuZGVmaW5lZCApID8gdGhldGEgOiAwOyAvLyBhcm91bmQgdGhlIGVxdWF0b3Igb2YgdGhlIHNwaGVyZVxuXG5cdFx0cmV0dXJuIHRoaXM7XG5cblx0fVxuXG5cdFNwaGVyaWNhbC5wcm90b3R5cGUgPSB7XG5cblx0XHRjb25zdHJ1Y3RvcjogU3BoZXJpY2FsLFxuXG5cdFx0c2V0OiBmdW5jdGlvbiAoIHJhZGl1cywgcGhpLCB0aGV0YSApIHtcblxuXHRcdFx0dGhpcy5yYWRpdXMgPSByYWRpdXM7XG5cdFx0XHR0aGlzLnBoaSA9IHBoaTtcblx0XHRcdHRoaXMudGhldGEgPSB0aGV0YTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Y2xvbmU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIG5ldyB0aGlzLmNvbnN0cnVjdG9yKCkuY29weSggdGhpcyApO1xuXG5cdFx0fSxcblxuXHRcdGNvcHk6IGZ1bmN0aW9uICggb3RoZXIgKSB7XG5cblx0XHRcdHRoaXMucmFkaXVzID0gb3RoZXIucmFkaXVzO1xuXHRcdFx0dGhpcy5waGkgPSBvdGhlci5waGk7XG5cdFx0XHR0aGlzLnRoZXRhID0gb3RoZXIudGhldGE7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fSxcblxuXHRcdC8vIHJlc3RyaWN0IHBoaSB0byBiZSBiZXR3ZWUgRVBTIGFuZCBQSS1FUFNcblx0XHRtYWtlU2FmZTogZnVuY3Rpb24oKSB7XG5cblx0XHRcdHZhciBFUFMgPSAwLjAwMDAwMTtcblx0XHRcdHRoaXMucGhpID0gTWF0aC5tYXgoIEVQUywgTWF0aC5taW4oIE1hdGguUEkgLSBFUFMsIHRoaXMucGhpICkgKTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0RnJvbVZlY3RvcjM6IGZ1bmN0aW9uKCB2ZWMzICkge1xuXG5cdFx0XHR0aGlzLnJhZGl1cyA9IHZlYzMubGVuZ3RoKCk7XG5cblx0XHRcdGlmICggdGhpcy5yYWRpdXMgPT09IDAgKSB7XG5cblx0XHRcdFx0dGhpcy50aGV0YSA9IDA7XG5cdFx0XHRcdHRoaXMucGhpID0gMDtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHR0aGlzLnRoZXRhID0gTWF0aC5hdGFuMiggdmVjMy54LCB2ZWMzLnogKTsgLy8gZXF1YXRvciBhbmdsZSBhcm91bmQgeS11cCBheGlzXG5cdFx0XHRcdHRoaXMucGhpID0gTWF0aC5hY29zKCBfTWF0aC5jbGFtcCggdmVjMy55IC8gdGhpcy5yYWRpdXMsIC0gMSwgMSApICk7IC8vIHBvbGFyIGFuZ2xlXG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9XG5cblx0fTtcblxuXHQvKipcblx0ICogQGF1dGhvciBNdWdlbjg3IC8gaHR0cHM6Ly9naXRodWIuY29tL011Z2VuODdcblx0ICpcblx0ICogUmVmOiBodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9DeWxpbmRyaWNhbF9jb29yZGluYXRlX3N5c3RlbVxuXHQgKlxuXHQgKi9cblxuXHRmdW5jdGlvbiBDeWxpbmRyaWNhbCggcmFkaXVzLCB0aGV0YSwgeSApIHtcblxuXHRcdHRoaXMucmFkaXVzID0gKCByYWRpdXMgIT09IHVuZGVmaW5lZCApID8gcmFkaXVzIDogMS4wOyAvLyBkaXN0YW5jZSBmcm9tIHRoZSBvcmlnaW4gdG8gYSBwb2ludCBpbiB0aGUgeC16IHBsYW5lXG5cdFx0dGhpcy50aGV0YSA9ICggdGhldGEgIT09IHVuZGVmaW5lZCApID8gdGhldGEgOiAwOyAvLyBjb3VudGVyY2xvY2t3aXNlIGFuZ2xlIGluIHRoZSB4LXogcGxhbmUgbWVhc3VyZWQgaW4gcmFkaWFucyBmcm9tIHRoZSBwb3NpdGl2ZSB6LWF4aXNcblx0XHR0aGlzLnkgPSAoIHkgIT09IHVuZGVmaW5lZCApID8geSA6IDA7IC8vIGhlaWdodCBhYm92ZSB0aGUgeC16IHBsYW5lXG5cblx0XHRyZXR1cm4gdGhpcztcblxuXHR9XG5cblx0Q3lsaW5kcmljYWwucHJvdG90eXBlID0ge1xuXG5cdFx0Y29uc3RydWN0b3I6IEN5bGluZHJpY2FsLFxuXG5cdFx0c2V0OiBmdW5jdGlvbiAoIHJhZGl1cywgdGhldGEsIHkgKSB7XG5cblx0XHRcdHRoaXMucmFkaXVzID0gcmFkaXVzO1xuXHRcdFx0dGhpcy50aGV0YSA9IHRoZXRhO1xuXHRcdFx0dGhpcy55ID0geTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0Y2xvbmU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0cmV0dXJuIG5ldyB0aGlzLmNvbnN0cnVjdG9yKCkuY29weSggdGhpcyApO1xuXG5cdFx0fSxcblxuXHRcdGNvcHk6IGZ1bmN0aW9uICggb3RoZXIgKSB7XG5cblx0XHRcdHRoaXMucmFkaXVzID0gb3RoZXIucmFkaXVzO1xuXHRcdFx0dGhpcy50aGV0YSA9IG90aGVyLnRoZXRhO1xuXHRcdFx0dGhpcy55ID0gb3RoZXIueTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9LFxuXG5cdFx0c2V0RnJvbVZlY3RvcjM6IGZ1bmN0aW9uKCB2ZWMzICkge1xuXG5cdFx0XHR0aGlzLnJhZGl1cyA9IE1hdGguc3FydCggdmVjMy54ICogdmVjMy54ICsgdmVjMy56ICogdmVjMy56ICk7XG5cdFx0XHR0aGlzLnRoZXRhID0gTWF0aC5hdGFuMiggdmVjMy54LCB2ZWMzLnogKTtcblx0XHRcdHRoaXMueSA9IHZlYzMueTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9XG5cblx0fTtcblxuXHQvKipcclxuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xyXG5cdCAqL1xyXG5cclxuXHRmdW5jdGlvbiBNb3JwaEJsZW5kTWVzaCggZ2VvbWV0cnksIG1hdGVyaWFsICkge1xuXHJcblx0XHRNZXNoLmNhbGwoIHRoaXMsIGdlb21ldHJ5LCBtYXRlcmlhbCApO1xyXG5cclxuXHRcdHRoaXMuYW5pbWF0aW9uc01hcCA9IHt9O1xyXG5cdFx0dGhpcy5hbmltYXRpb25zTGlzdCA9IFtdO1xyXG5cclxuXHRcdC8vIHByZXBhcmUgZGVmYXVsdCBhbmltYXRpb25cclxuXHRcdC8vIChhbGwgZnJhbWVzIHBsYXllZCB0b2dldGhlciBpbiAxIHNlY29uZClcclxuXHJcblx0XHR2YXIgbnVtRnJhbWVzID0gdGhpcy5nZW9tZXRyeS5tb3JwaFRhcmdldHMubGVuZ3RoO1xyXG5cclxuXHRcdHZhciBuYW1lID0gXCJfX2RlZmF1bHRcIjtcclxuXHJcblx0XHR2YXIgc3RhcnRGcmFtZSA9IDA7XHJcblx0XHR2YXIgZW5kRnJhbWUgPSBudW1GcmFtZXMgLSAxO1xyXG5cclxuXHRcdHZhciBmcHMgPSBudW1GcmFtZXMgLyAxO1xyXG5cclxuXHRcdHRoaXMuY3JlYXRlQW5pbWF0aW9uKCBuYW1lLCBzdGFydEZyYW1lLCBlbmRGcmFtZSwgZnBzICk7XHJcblx0XHR0aGlzLnNldEFuaW1hdGlvbldlaWdodCggbmFtZSwgMSApO1xyXG5cclxuXHR9XHJcblxyXG5cdE1vcnBoQmxlbmRNZXNoLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIE1lc2gucHJvdG90eXBlICk7XHJcblx0TW9ycGhCbGVuZE1lc2gucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gTW9ycGhCbGVuZE1lc2g7XHJcblxyXG5cdE1vcnBoQmxlbmRNZXNoLnByb3RvdHlwZS5jcmVhdGVBbmltYXRpb24gPSBmdW5jdGlvbiAoIG5hbWUsIHN0YXJ0LCBlbmQsIGZwcyApIHtcclxuXHJcblx0XHR2YXIgYW5pbWF0aW9uID0ge1xyXG5cclxuXHRcdFx0c3RhcnQ6IHN0YXJ0LFxyXG5cdFx0XHRlbmQ6IGVuZCxcclxuXHJcblx0XHRcdGxlbmd0aDogZW5kIC0gc3RhcnQgKyAxLFxyXG5cclxuXHRcdFx0ZnBzOiBmcHMsXHJcblx0XHRcdGR1cmF0aW9uOiAoIGVuZCAtIHN0YXJ0ICkgLyBmcHMsXHJcblxyXG5cdFx0XHRsYXN0RnJhbWU6IDAsXHJcblx0XHRcdGN1cnJlbnRGcmFtZTogMCxcclxuXHJcblx0XHRcdGFjdGl2ZTogZmFsc2UsXHJcblxyXG5cdFx0XHR0aW1lOiAwLFxyXG5cdFx0XHRkaXJlY3Rpb246IDEsXHJcblx0XHRcdHdlaWdodDogMSxcclxuXHJcblx0XHRcdGRpcmVjdGlvbkJhY2t3YXJkczogZmFsc2UsXHJcblx0XHRcdG1pcnJvcmVkTG9vcDogZmFsc2VcclxuXHJcblx0XHR9O1xyXG5cclxuXHRcdHRoaXMuYW5pbWF0aW9uc01hcFsgbmFtZSBdID0gYW5pbWF0aW9uO1xyXG5cdFx0dGhpcy5hbmltYXRpb25zTGlzdC5wdXNoKCBhbmltYXRpb24gKTtcclxuXHJcblx0fTtcclxuXHJcblx0TW9ycGhCbGVuZE1lc2gucHJvdG90eXBlLmF1dG9DcmVhdGVBbmltYXRpb25zID0gZnVuY3Rpb24gKCBmcHMgKSB7XHJcblxyXG5cdFx0dmFyIHBhdHRlcm4gPSAvKFthLXpdKylfPyhcXGQrKS9pO1xyXG5cclxuXHRcdHZhciBmaXJzdEFuaW1hdGlvbiwgZnJhbWVSYW5nZXMgPSB7fTtcclxuXHJcblx0XHR2YXIgZ2VvbWV0cnkgPSB0aGlzLmdlb21ldHJ5O1xyXG5cclxuXHRcdGZvciAoIHZhciBpID0gMCwgaWwgPSBnZW9tZXRyeS5tb3JwaFRhcmdldHMubGVuZ3RoOyBpIDwgaWw7IGkgKysgKSB7XHJcblxyXG5cdFx0XHR2YXIgbW9ycGggPSBnZW9tZXRyeS5tb3JwaFRhcmdldHNbIGkgXTtcclxuXHRcdFx0dmFyIGNodW5rcyA9IG1vcnBoLm5hbWUubWF0Y2goIHBhdHRlcm4gKTtcclxuXHJcblx0XHRcdGlmICggY2h1bmtzICYmIGNodW5rcy5sZW5ndGggPiAxICkge1xyXG5cclxuXHRcdFx0XHR2YXIgbmFtZSA9IGNodW5rc1sgMSBdO1xyXG5cclxuXHRcdFx0XHRpZiAoICEgZnJhbWVSYW5nZXNbIG5hbWUgXSApIGZyYW1lUmFuZ2VzWyBuYW1lIF0gPSB7IHN0YXJ0OiBJbmZpbml0eSwgZW5kOiAtIEluZmluaXR5IH07XHJcblxyXG5cdFx0XHRcdHZhciByYW5nZSA9IGZyYW1lUmFuZ2VzWyBuYW1lIF07XHJcblxyXG5cdFx0XHRcdGlmICggaSA8IHJhbmdlLnN0YXJ0ICkgcmFuZ2Uuc3RhcnQgPSBpO1xyXG5cdFx0XHRcdGlmICggaSA+IHJhbmdlLmVuZCApIHJhbmdlLmVuZCA9IGk7XHJcblxyXG5cdFx0XHRcdGlmICggISBmaXJzdEFuaW1hdGlvbiApIGZpcnN0QW5pbWF0aW9uID0gbmFtZTtcclxuXHJcblx0XHRcdH1cclxuXHJcblx0XHR9XHJcblxyXG5cdFx0Zm9yICggdmFyIG5hbWUgaW4gZnJhbWVSYW5nZXMgKSB7XHJcblxyXG5cdFx0XHR2YXIgcmFuZ2UgPSBmcmFtZVJhbmdlc1sgbmFtZSBdO1xyXG5cdFx0XHR0aGlzLmNyZWF0ZUFuaW1hdGlvbiggbmFtZSwgcmFuZ2Uuc3RhcnQsIHJhbmdlLmVuZCwgZnBzICk7XHJcblxyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuZmlyc3RBbmltYXRpb24gPSBmaXJzdEFuaW1hdGlvbjtcclxuXHJcblx0fTtcclxuXHJcblx0TW9ycGhCbGVuZE1lc2gucHJvdG90eXBlLnNldEFuaW1hdGlvbkRpcmVjdGlvbkZvcndhcmQgPSBmdW5jdGlvbiAoIG5hbWUgKSB7XHJcblxyXG5cdFx0dmFyIGFuaW1hdGlvbiA9IHRoaXMuYW5pbWF0aW9uc01hcFsgbmFtZSBdO1xyXG5cclxuXHRcdGlmICggYW5pbWF0aW9uICkge1xyXG5cclxuXHRcdFx0YW5pbWF0aW9uLmRpcmVjdGlvbiA9IDE7XHJcblx0XHRcdGFuaW1hdGlvbi5kaXJlY3Rpb25CYWNrd2FyZHMgPSBmYWxzZTtcclxuXHJcblx0XHR9XHJcblxyXG5cdH07XHJcblxyXG5cdE1vcnBoQmxlbmRNZXNoLnByb3RvdHlwZS5zZXRBbmltYXRpb25EaXJlY3Rpb25CYWNrd2FyZCA9IGZ1bmN0aW9uICggbmFtZSApIHtcclxuXHJcblx0XHR2YXIgYW5pbWF0aW9uID0gdGhpcy5hbmltYXRpb25zTWFwWyBuYW1lIF07XHJcblxyXG5cdFx0aWYgKCBhbmltYXRpb24gKSB7XHJcblxyXG5cdFx0XHRhbmltYXRpb24uZGlyZWN0aW9uID0gLSAxO1xyXG5cdFx0XHRhbmltYXRpb24uZGlyZWN0aW9uQmFja3dhcmRzID0gdHJ1ZTtcclxuXHJcblx0XHR9XHJcblxyXG5cdH07XHJcblxyXG5cdE1vcnBoQmxlbmRNZXNoLnByb3RvdHlwZS5zZXRBbmltYXRpb25GUFMgPSBmdW5jdGlvbiAoIG5hbWUsIGZwcyApIHtcclxuXHJcblx0XHR2YXIgYW5pbWF0aW9uID0gdGhpcy5hbmltYXRpb25zTWFwWyBuYW1lIF07XHJcblxyXG5cdFx0aWYgKCBhbmltYXRpb24gKSB7XHJcblxyXG5cdFx0XHRhbmltYXRpb24uZnBzID0gZnBzO1xyXG5cdFx0XHRhbmltYXRpb24uZHVyYXRpb24gPSAoIGFuaW1hdGlvbi5lbmQgLSBhbmltYXRpb24uc3RhcnQgKSAvIGFuaW1hdGlvbi5mcHM7XHJcblxyXG5cdFx0fVxyXG5cclxuXHR9O1xyXG5cclxuXHRNb3JwaEJsZW5kTWVzaC5wcm90b3R5cGUuc2V0QW5pbWF0aW9uRHVyYXRpb24gPSBmdW5jdGlvbiAoIG5hbWUsIGR1cmF0aW9uICkge1xyXG5cclxuXHRcdHZhciBhbmltYXRpb24gPSB0aGlzLmFuaW1hdGlvbnNNYXBbIG5hbWUgXTtcclxuXHJcblx0XHRpZiAoIGFuaW1hdGlvbiApIHtcclxuXHJcblx0XHRcdGFuaW1hdGlvbi5kdXJhdGlvbiA9IGR1cmF0aW9uO1xyXG5cdFx0XHRhbmltYXRpb24uZnBzID0gKCBhbmltYXRpb24uZW5kIC0gYW5pbWF0aW9uLnN0YXJ0ICkgLyBhbmltYXRpb24uZHVyYXRpb247XHJcblxyXG5cdFx0fVxyXG5cclxuXHR9O1xyXG5cclxuXHRNb3JwaEJsZW5kTWVzaC5wcm90b3R5cGUuc2V0QW5pbWF0aW9uV2VpZ2h0ID0gZnVuY3Rpb24gKCBuYW1lLCB3ZWlnaHQgKSB7XHJcblxyXG5cdFx0dmFyIGFuaW1hdGlvbiA9IHRoaXMuYW5pbWF0aW9uc01hcFsgbmFtZSBdO1xyXG5cclxuXHRcdGlmICggYW5pbWF0aW9uICkge1xyXG5cclxuXHRcdFx0YW5pbWF0aW9uLndlaWdodCA9IHdlaWdodDtcclxuXHJcblx0XHR9XHJcblxyXG5cdH07XHJcblxyXG5cdE1vcnBoQmxlbmRNZXNoLnByb3RvdHlwZS5zZXRBbmltYXRpb25UaW1lID0gZnVuY3Rpb24gKCBuYW1lLCB0aW1lICkge1xyXG5cclxuXHRcdHZhciBhbmltYXRpb24gPSB0aGlzLmFuaW1hdGlvbnNNYXBbIG5hbWUgXTtcclxuXHJcblx0XHRpZiAoIGFuaW1hdGlvbiApIHtcclxuXHJcblx0XHRcdGFuaW1hdGlvbi50aW1lID0gdGltZTtcclxuXHJcblx0XHR9XHJcblxyXG5cdH07XHJcblxyXG5cdE1vcnBoQmxlbmRNZXNoLnByb3RvdHlwZS5nZXRBbmltYXRpb25UaW1lID0gZnVuY3Rpb24gKCBuYW1lICkge1xyXG5cclxuXHRcdHZhciB0aW1lID0gMDtcclxuXHJcblx0XHR2YXIgYW5pbWF0aW9uID0gdGhpcy5hbmltYXRpb25zTWFwWyBuYW1lIF07XHJcblxyXG5cdFx0aWYgKCBhbmltYXRpb24gKSB7XHJcblxyXG5cdFx0XHR0aW1lID0gYW5pbWF0aW9uLnRpbWU7XHJcblxyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aW1lO1xyXG5cclxuXHR9O1xyXG5cclxuXHRNb3JwaEJsZW5kTWVzaC5wcm90b3R5cGUuZ2V0QW5pbWF0aW9uRHVyYXRpb24gPSBmdW5jdGlvbiAoIG5hbWUgKSB7XHJcblxyXG5cdFx0dmFyIGR1cmF0aW9uID0gLSAxO1xyXG5cclxuXHRcdHZhciBhbmltYXRpb24gPSB0aGlzLmFuaW1hdGlvbnNNYXBbIG5hbWUgXTtcclxuXHJcblx0XHRpZiAoIGFuaW1hdGlvbiApIHtcclxuXHJcblx0XHRcdGR1cmF0aW9uID0gYW5pbWF0aW9uLmR1cmF0aW9uO1xyXG5cclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gZHVyYXRpb247XHJcblxyXG5cdH07XHJcblxyXG5cdE1vcnBoQmxlbmRNZXNoLnByb3RvdHlwZS5wbGF5QW5pbWF0aW9uID0gZnVuY3Rpb24gKCBuYW1lICkge1xyXG5cclxuXHRcdHZhciBhbmltYXRpb24gPSB0aGlzLmFuaW1hdGlvbnNNYXBbIG5hbWUgXTtcclxuXHJcblx0XHRpZiAoIGFuaW1hdGlvbiApIHtcclxuXHJcblx0XHRcdGFuaW1hdGlvbi50aW1lID0gMDtcclxuXHRcdFx0YW5pbWF0aW9uLmFjdGl2ZSA9IHRydWU7XHJcblxyXG5cdFx0fSBlbHNlIHtcclxuXHJcblx0XHRcdGNvbnNvbGUud2FybiggXCJUSFJFRS5Nb3JwaEJsZW5kTWVzaDogYW5pbWF0aW9uW1wiICsgbmFtZSArIFwiXSB1bmRlZmluZWQgaW4gLnBsYXlBbmltYXRpb24oKVwiICk7XHJcblxyXG5cdFx0fVxyXG5cclxuXHR9O1xyXG5cclxuXHRNb3JwaEJsZW5kTWVzaC5wcm90b3R5cGUuc3RvcEFuaW1hdGlvbiA9IGZ1bmN0aW9uICggbmFtZSApIHtcclxuXHJcblx0XHR2YXIgYW5pbWF0aW9uID0gdGhpcy5hbmltYXRpb25zTWFwWyBuYW1lIF07XHJcblxyXG5cdFx0aWYgKCBhbmltYXRpb24gKSB7XHJcblxyXG5cdFx0XHRhbmltYXRpb24uYWN0aXZlID0gZmFsc2U7XHJcblxyXG5cdFx0fVxyXG5cclxuXHR9O1xyXG5cclxuXHRNb3JwaEJsZW5kTWVzaC5wcm90b3R5cGUudXBkYXRlID0gZnVuY3Rpb24gKCBkZWx0YSApIHtcclxuXHJcblx0XHRmb3IgKCB2YXIgaSA9IDAsIGlsID0gdGhpcy5hbmltYXRpb25zTGlzdC5sZW5ndGg7IGkgPCBpbDsgaSArKyApIHtcclxuXHJcblx0XHRcdHZhciBhbmltYXRpb24gPSB0aGlzLmFuaW1hdGlvbnNMaXN0WyBpIF07XHJcblxyXG5cdFx0XHRpZiAoICEgYW5pbWF0aW9uLmFjdGl2ZSApIGNvbnRpbnVlO1xyXG5cclxuXHRcdFx0dmFyIGZyYW1lVGltZSA9IGFuaW1hdGlvbi5kdXJhdGlvbiAvIGFuaW1hdGlvbi5sZW5ndGg7XHJcblxyXG5cdFx0XHRhbmltYXRpb24udGltZSArPSBhbmltYXRpb24uZGlyZWN0aW9uICogZGVsdGE7XHJcblxyXG5cdFx0XHRpZiAoIGFuaW1hdGlvbi5taXJyb3JlZExvb3AgKSB7XHJcblxyXG5cdFx0XHRcdGlmICggYW5pbWF0aW9uLnRpbWUgPiBhbmltYXRpb24uZHVyYXRpb24gfHwgYW5pbWF0aW9uLnRpbWUgPCAwICkge1xyXG5cclxuXHRcdFx0XHRcdGFuaW1hdGlvbi5kaXJlY3Rpb24gKj0gLSAxO1xyXG5cclxuXHRcdFx0XHRcdGlmICggYW5pbWF0aW9uLnRpbWUgPiBhbmltYXRpb24uZHVyYXRpb24gKSB7XHJcblxyXG5cdFx0XHRcdFx0XHRhbmltYXRpb24udGltZSA9IGFuaW1hdGlvbi5kdXJhdGlvbjtcclxuXHRcdFx0XHRcdFx0YW5pbWF0aW9uLmRpcmVjdGlvbkJhY2t3YXJkcyA9IHRydWU7XHJcblxyXG5cdFx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHRcdGlmICggYW5pbWF0aW9uLnRpbWUgPCAwICkge1xyXG5cclxuXHRcdFx0XHRcdFx0YW5pbWF0aW9uLnRpbWUgPSAwO1xyXG5cdFx0XHRcdFx0XHRhbmltYXRpb24uZGlyZWN0aW9uQmFja3dhcmRzID0gZmFsc2U7XHJcblxyXG5cdFx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHR9XHJcblxyXG5cdFx0XHR9IGVsc2Uge1xyXG5cclxuXHRcdFx0XHRhbmltYXRpb24udGltZSA9IGFuaW1hdGlvbi50aW1lICUgYW5pbWF0aW9uLmR1cmF0aW9uO1xyXG5cclxuXHRcdFx0XHRpZiAoIGFuaW1hdGlvbi50aW1lIDwgMCApIGFuaW1hdGlvbi50aW1lICs9IGFuaW1hdGlvbi5kdXJhdGlvbjtcclxuXHJcblx0XHRcdH1cclxuXHJcblx0XHRcdHZhciBrZXlmcmFtZSA9IGFuaW1hdGlvbi5zdGFydCArIF9NYXRoLmNsYW1wKCBNYXRoLmZsb29yKCBhbmltYXRpb24udGltZSAvIGZyYW1lVGltZSApLCAwLCBhbmltYXRpb24ubGVuZ3RoIC0gMSApO1xyXG5cdFx0XHR2YXIgd2VpZ2h0ID0gYW5pbWF0aW9uLndlaWdodDtcclxuXHJcblx0XHRcdGlmICgga2V5ZnJhbWUgIT09IGFuaW1hdGlvbi5jdXJyZW50RnJhbWUgKSB7XHJcblxyXG5cdFx0XHRcdHRoaXMubW9ycGhUYXJnZXRJbmZsdWVuY2VzWyBhbmltYXRpb24ubGFzdEZyYW1lIF0gPSAwO1xyXG5cdFx0XHRcdHRoaXMubW9ycGhUYXJnZXRJbmZsdWVuY2VzWyBhbmltYXRpb24uY3VycmVudEZyYW1lIF0gPSAxICogd2VpZ2h0O1xyXG5cclxuXHRcdFx0XHR0aGlzLm1vcnBoVGFyZ2V0SW5mbHVlbmNlc1sga2V5ZnJhbWUgXSA9IDA7XHJcblxyXG5cdFx0XHRcdGFuaW1hdGlvbi5sYXN0RnJhbWUgPSBhbmltYXRpb24uY3VycmVudEZyYW1lO1xyXG5cdFx0XHRcdGFuaW1hdGlvbi5jdXJyZW50RnJhbWUgPSBrZXlmcmFtZTtcclxuXHJcblx0XHRcdH1cclxuXHJcblx0XHRcdHZhciBtaXggPSAoIGFuaW1hdGlvbi50aW1lICUgZnJhbWVUaW1lICkgLyBmcmFtZVRpbWU7XHJcblxyXG5cdFx0XHRpZiAoIGFuaW1hdGlvbi5kaXJlY3Rpb25CYWNrd2FyZHMgKSBtaXggPSAxIC0gbWl4O1xyXG5cclxuXHRcdFx0aWYgKCBhbmltYXRpb24uY3VycmVudEZyYW1lICE9PSBhbmltYXRpb24ubGFzdEZyYW1lICkge1xyXG5cclxuXHRcdFx0XHR0aGlzLm1vcnBoVGFyZ2V0SW5mbHVlbmNlc1sgYW5pbWF0aW9uLmN1cnJlbnRGcmFtZSBdID0gbWl4ICogd2VpZ2h0O1xyXG5cdFx0XHRcdHRoaXMubW9ycGhUYXJnZXRJbmZsdWVuY2VzWyBhbmltYXRpb24ubGFzdEZyYW1lIF0gPSAoIDEgLSBtaXggKSAqIHdlaWdodDtcclxuXHJcblx0XHRcdH0gZWxzZSB7XHJcblxyXG5cdFx0XHRcdHRoaXMubW9ycGhUYXJnZXRJbmZsdWVuY2VzWyBhbmltYXRpb24uY3VycmVudEZyYW1lIF0gPSB3ZWlnaHQ7XHJcblxyXG5cdFx0XHR9XHJcblxyXG5cdFx0fVxyXG5cclxuXHR9O1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBJbW1lZGlhdGVSZW5kZXJPYmplY3QoIG1hdGVyaWFsICkge1xuXG5cdFx0T2JqZWN0M0QuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy5tYXRlcmlhbCA9IG1hdGVyaWFsO1xuXHRcdHRoaXMucmVuZGVyID0gZnVuY3Rpb24gKCByZW5kZXJDYWxsYmFjayApIHt9O1xuXG5cdH1cblxuXHRJbW1lZGlhdGVSZW5kZXJPYmplY3QucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggT2JqZWN0M0QucHJvdG90eXBlICk7XG5cdEltbWVkaWF0ZVJlbmRlck9iamVjdC5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBJbW1lZGlhdGVSZW5kZXJPYmplY3Q7XG5cblx0SW1tZWRpYXRlUmVuZGVyT2JqZWN0LnByb3RvdHlwZS5pc0ltbWVkaWF0ZVJlbmRlck9iamVjdCA9IHRydWU7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqIEBhdXRob3IgV2VzdExhbmdsZXkgLyBodHRwOi8vZ2l0aHViLmNvbS9XZXN0TGFuZ2xleVxuXHQqL1xuXG5cdGZ1bmN0aW9uIFZlcnRleE5vcm1hbHNIZWxwZXIoIG9iamVjdCwgc2l6ZSwgaGV4LCBsaW5ld2lkdGggKSB7XG5cblx0XHR0aGlzLm9iamVjdCA9IG9iamVjdDtcblxuXHRcdHRoaXMuc2l6ZSA9ICggc2l6ZSAhPT0gdW5kZWZpbmVkICkgPyBzaXplIDogMTtcblxuXHRcdHZhciBjb2xvciA9ICggaGV4ICE9PSB1bmRlZmluZWQgKSA/IGhleCA6IDB4ZmYwMDAwO1xuXG5cdFx0dmFyIHdpZHRoID0gKCBsaW5ld2lkdGggIT09IHVuZGVmaW5lZCApID8gbGluZXdpZHRoIDogMTtcblxuXHRcdC8vXG5cblx0XHR2YXIgbk5vcm1hbHMgPSAwO1xuXG5cdFx0dmFyIG9iakdlb21ldHJ5ID0gdGhpcy5vYmplY3QuZ2VvbWV0cnk7XG5cblx0XHRpZiAoIG9iakdlb21ldHJ5ICYmIG9iakdlb21ldHJ5LmlzR2VvbWV0cnkgKSB7XG5cblx0XHRcdG5Ob3JtYWxzID0gb2JqR2VvbWV0cnkuZmFjZXMubGVuZ3RoICogMztcblxuXHRcdH0gZWxzZSBpZiAoIG9iakdlb21ldHJ5ICYmIG9iakdlb21ldHJ5LmlzQnVmZmVyR2VvbWV0cnkgKSB7XG5cblx0XHRcdG5Ob3JtYWxzID0gb2JqR2VvbWV0cnkuYXR0cmlidXRlcy5ub3JtYWwuY291bnQ7XG5cblx0XHR9XG5cblx0XHQvL1xuXG5cdFx0dmFyIGdlb21ldHJ5ID0gbmV3IEJ1ZmZlckdlb21ldHJ5KCk7XG5cblx0XHR2YXIgcG9zaXRpb25zID0gbmV3IEZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUoIG5Ob3JtYWxzICogMiAqIDMsIDMgKTtcblxuXHRcdGdlb21ldHJ5LmFkZEF0dHJpYnV0ZSggJ3Bvc2l0aW9uJywgcG9zaXRpb25zICk7XG5cblx0XHRMaW5lU2VnbWVudHMuY2FsbCggdGhpcywgZ2VvbWV0cnksIG5ldyBMaW5lQmFzaWNNYXRlcmlhbCggeyBjb2xvcjogY29sb3IsIGxpbmV3aWR0aDogd2lkdGggfSApICk7XG5cblx0XHQvL1xuXG5cdFx0dGhpcy5tYXRyaXhBdXRvVXBkYXRlID0gZmFsc2U7XG5cblx0XHR0aGlzLnVwZGF0ZSgpO1xuXG5cdH1cblxuXHRWZXJ0ZXhOb3JtYWxzSGVscGVyLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIExpbmVTZWdtZW50cy5wcm90b3R5cGUgKTtcblx0VmVydGV4Tm9ybWFsc0hlbHBlci5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBWZXJ0ZXhOb3JtYWxzSGVscGVyO1xuXG5cdFZlcnRleE5vcm1hbHNIZWxwZXIucHJvdG90eXBlLnVwZGF0ZSA9ICggZnVuY3Rpb24gKCkge1xuXG5cdFx0dmFyIHYxID0gbmV3IFZlY3RvcjMoKTtcblx0XHR2YXIgdjIgPSBuZXcgVmVjdG9yMygpO1xuXHRcdHZhciBub3JtYWxNYXRyaXggPSBuZXcgTWF0cml4MygpO1xuXG5cdFx0cmV0dXJuIGZ1bmN0aW9uIHVwZGF0ZSgpIHtcblxuXHRcdFx0dmFyIGtleXMgPSBbICdhJywgJ2InLCAnYycgXTtcblxuXHRcdFx0dGhpcy5vYmplY3QudXBkYXRlTWF0cml4V29ybGQoIHRydWUgKTtcblxuXHRcdFx0bm9ybWFsTWF0cml4LmdldE5vcm1hbE1hdHJpeCggdGhpcy5vYmplY3QubWF0cml4V29ybGQgKTtcblxuXHRcdFx0dmFyIG1hdHJpeFdvcmxkID0gdGhpcy5vYmplY3QubWF0cml4V29ybGQ7XG5cblx0XHRcdHZhciBwb3NpdGlvbiA9IHRoaXMuZ2VvbWV0cnkuYXR0cmlidXRlcy5wb3NpdGlvbjtcblxuXHRcdFx0Ly9cblxuXHRcdFx0dmFyIG9iakdlb21ldHJ5ID0gdGhpcy5vYmplY3QuZ2VvbWV0cnk7XG5cblx0XHRcdGlmICggb2JqR2VvbWV0cnkgJiYgb2JqR2VvbWV0cnkuaXNHZW9tZXRyeSApIHtcblxuXHRcdFx0XHR2YXIgdmVydGljZXMgPSBvYmpHZW9tZXRyeS52ZXJ0aWNlcztcblxuXHRcdFx0XHR2YXIgZmFjZXMgPSBvYmpHZW9tZXRyeS5mYWNlcztcblxuXHRcdFx0XHR2YXIgaWR4ID0gMDtcblxuXHRcdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBmYWNlcy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdFx0dmFyIGZhY2UgPSBmYWNlc1sgaSBdO1xuXG5cdFx0XHRcdFx0Zm9yICggdmFyIGogPSAwLCBqbCA9IGZhY2UudmVydGV4Tm9ybWFscy5sZW5ndGg7IGogPCBqbDsgaiArKyApIHtcblxuXHRcdFx0XHRcdFx0dmFyIHZlcnRleCA9IHZlcnRpY2VzWyBmYWNlWyBrZXlzWyBqIF0gXSBdO1xuXG5cdFx0XHRcdFx0XHR2YXIgbm9ybWFsID0gZmFjZS52ZXJ0ZXhOb3JtYWxzWyBqIF07XG5cblx0XHRcdFx0XHRcdHYxLmNvcHkoIHZlcnRleCApLmFwcGx5TWF0cml4NCggbWF0cml4V29ybGQgKTtcblxuXHRcdFx0XHRcdFx0djIuY29weSggbm9ybWFsICkuYXBwbHlNYXRyaXgzKCBub3JtYWxNYXRyaXggKS5ub3JtYWxpemUoKS5tdWx0aXBseVNjYWxhciggdGhpcy5zaXplICkuYWRkKCB2MSApO1xuXG5cdFx0XHRcdFx0XHRwb3NpdGlvbi5zZXRYWVooIGlkeCwgdjEueCwgdjEueSwgdjEueiApO1xuXG5cdFx0XHRcdFx0XHRpZHggPSBpZHggKyAxO1xuXG5cdFx0XHRcdFx0XHRwb3NpdGlvbi5zZXRYWVooIGlkeCwgdjIueCwgdjIueSwgdjIueiApO1xuXG5cdFx0XHRcdFx0XHRpZHggPSBpZHggKyAxO1xuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0fSBlbHNlIGlmICggb2JqR2VvbWV0cnkgJiYgb2JqR2VvbWV0cnkuaXNCdWZmZXJHZW9tZXRyeSApIHtcblxuXHRcdFx0XHR2YXIgb2JqUG9zID0gb2JqR2VvbWV0cnkuYXR0cmlidXRlcy5wb3NpdGlvbjtcblxuXHRcdFx0XHR2YXIgb2JqTm9ybSA9IG9iakdlb21ldHJ5LmF0dHJpYnV0ZXMubm9ybWFsO1xuXG5cdFx0XHRcdHZhciBpZHggPSAwO1xuXG5cdFx0XHRcdC8vIGZvciBzaW1wbGljaXR5LCBpZ25vcmUgaW5kZXggYW5kIGRyYXdjYWxscywgYW5kIHJlbmRlciBldmVyeSBub3JtYWxcblxuXHRcdFx0XHRmb3IgKCB2YXIgaiA9IDAsIGpsID0gb2JqUG9zLmNvdW50OyBqIDwgamw7IGogKysgKSB7XG5cblx0XHRcdFx0XHR2MS5zZXQoIG9ialBvcy5nZXRYKCBqICksIG9ialBvcy5nZXRZKCBqICksIG9ialBvcy5nZXRaKCBqICkgKS5hcHBseU1hdHJpeDQoIG1hdHJpeFdvcmxkICk7XG5cblx0XHRcdFx0XHR2Mi5zZXQoIG9iak5vcm0uZ2V0WCggaiApLCBvYmpOb3JtLmdldFkoIGogKSwgb2JqTm9ybS5nZXRaKCBqICkgKTtcblxuXHRcdFx0XHRcdHYyLmFwcGx5TWF0cml4Myggbm9ybWFsTWF0cml4ICkubm9ybWFsaXplKCkubXVsdGlwbHlTY2FsYXIoIHRoaXMuc2l6ZSApLmFkZCggdjEgKTtcblxuXHRcdFx0XHRcdHBvc2l0aW9uLnNldFhZWiggaWR4LCB2MS54LCB2MS55LCB2MS56ICk7XG5cblx0XHRcdFx0XHRpZHggPSBpZHggKyAxO1xuXG5cdFx0XHRcdFx0cG9zaXRpb24uc2V0WFlaKCBpZHgsIHYyLngsIHYyLnksIHYyLnogKTtcblxuXHRcdFx0XHRcdGlkeCA9IGlkeCArIDE7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdHBvc2l0aW9uLm5lZWRzVXBkYXRlID0gdHJ1ZTtcblxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHR9O1xuXG5cdH0oKSApO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yIFdlc3RMYW5nbGV5IC8gaHR0cDovL2dpdGh1Yi5jb20vV2VzdExhbmdsZXlcblx0Ki9cblxuXHRmdW5jdGlvbiBTcG90TGlnaHRIZWxwZXIoIGxpZ2h0ICkge1xuXG5cdFx0T2JqZWN0M0QuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy5saWdodCA9IGxpZ2h0O1xuXHRcdHRoaXMubGlnaHQudXBkYXRlTWF0cml4V29ybGQoKTtcblxuXHRcdHRoaXMubWF0cml4ID0gbGlnaHQubWF0cml4V29ybGQ7XG5cdFx0dGhpcy5tYXRyaXhBdXRvVXBkYXRlID0gZmFsc2U7XG5cblx0XHR2YXIgZ2VvbWV0cnkgPSBuZXcgQnVmZmVyR2VvbWV0cnkoKTtcblxuXHRcdHZhciBwb3NpdGlvbnMgPSBbXG5cdFx0XHQwLCAwLCAwLCAgIDAsICAgMCwgICAxLFxuXHRcdFx0MCwgMCwgMCwgICAxLCAgIDAsICAgMSxcblx0XHRcdDAsIDAsIDAsIC0gMSwgICAwLCAgIDEsXG5cdFx0XHQwLCAwLCAwLCAgIDAsICAgMSwgICAxLFxuXHRcdFx0MCwgMCwgMCwgICAwLCAtIDEsICAgMVxuXHRcdF07XG5cblx0XHRmb3IgKCB2YXIgaSA9IDAsIGogPSAxLCBsID0gMzI7IGkgPCBsOyBpICsrLCBqICsrICkge1xuXG5cdFx0XHR2YXIgcDEgPSAoIGkgLyBsICkgKiBNYXRoLlBJICogMjtcblx0XHRcdHZhciBwMiA9ICggaiAvIGwgKSAqIE1hdGguUEkgKiAyO1xuXG5cdFx0XHRwb3NpdGlvbnMucHVzaChcblx0XHRcdFx0TWF0aC5jb3MoIHAxICksIE1hdGguc2luKCBwMSApLCAxLFxuXHRcdFx0XHRNYXRoLmNvcyggcDIgKSwgTWF0aC5zaW4oIHAyICksIDFcblx0XHRcdCk7XG5cblx0XHR9XG5cblx0XHRnZW9tZXRyeS5hZGRBdHRyaWJ1dGUoICdwb3NpdGlvbicsIG5ldyBGbG9hdDMyQnVmZmVyQXR0cmlidXRlKCBwb3NpdGlvbnMsIDMgKSApO1xuXG5cdFx0dmFyIG1hdGVyaWFsID0gbmV3IExpbmVCYXNpY01hdGVyaWFsKCB7IGZvZzogZmFsc2UgfSApO1xuXG5cdFx0dGhpcy5jb25lID0gbmV3IExpbmVTZWdtZW50cyggZ2VvbWV0cnksIG1hdGVyaWFsICk7XG5cdFx0dGhpcy5hZGQoIHRoaXMuY29uZSApO1xuXG5cdFx0dGhpcy51cGRhdGUoKTtcblxuXHR9XG5cblx0U3BvdExpZ2h0SGVscGVyLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIE9iamVjdDNELnByb3RvdHlwZSApO1xuXHRTcG90TGlnaHRIZWxwZXIucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gU3BvdExpZ2h0SGVscGVyO1xuXG5cdFNwb3RMaWdodEhlbHBlci5wcm90b3R5cGUuZGlzcG9zZSA9IGZ1bmN0aW9uICgpIHtcblxuXHRcdHRoaXMuY29uZS5nZW9tZXRyeS5kaXNwb3NlKCk7XG5cdFx0dGhpcy5jb25lLm1hdGVyaWFsLmRpc3Bvc2UoKTtcblxuXHR9O1xuXG5cdFNwb3RMaWdodEhlbHBlci5wcm90b3R5cGUudXBkYXRlID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0dmFyIHZlY3RvciA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0dmFyIHZlY3RvcjIgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0cmV0dXJuIGZ1bmN0aW9uIHVwZGF0ZSgpIHtcblxuXHRcdFx0dmFyIGNvbmVMZW5ndGggPSB0aGlzLmxpZ2h0LmRpc3RhbmNlID8gdGhpcy5saWdodC5kaXN0YW5jZSA6IDEwMDA7XG5cdFx0XHR2YXIgY29uZVdpZHRoID0gY29uZUxlbmd0aCAqIE1hdGgudGFuKCB0aGlzLmxpZ2h0LmFuZ2xlICk7XG5cblx0XHRcdHRoaXMuY29uZS5zY2FsZS5zZXQoIGNvbmVXaWR0aCwgY29uZVdpZHRoLCBjb25lTGVuZ3RoICk7XG5cblx0XHRcdHZlY3Rvci5zZXRGcm9tTWF0cml4UG9zaXRpb24oIHRoaXMubGlnaHQubWF0cml4V29ybGQgKTtcblx0XHRcdHZlY3RvcjIuc2V0RnJvbU1hdHJpeFBvc2l0aW9uKCB0aGlzLmxpZ2h0LnRhcmdldC5tYXRyaXhXb3JsZCApO1xuXG5cdFx0XHR0aGlzLmNvbmUubG9va0F0KCB2ZWN0b3IyLnN1YiggdmVjdG9yICkgKTtcblxuXHRcdFx0dGhpcy5jb25lLm1hdGVyaWFsLmNvbG9yLmNvcHkoIHRoaXMubGlnaHQuY29sb3IgKS5tdWx0aXBseVNjYWxhciggdGhpcy5saWdodC5pbnRlbnNpdHkgKTtcblxuXHRcdH07XG5cblx0fSgpO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIFNlYW4gR3JpZmZpbiAvIGh0dHA6Ly90d2l0dGVyLmNvbS9zZ3JpZlxuXHQgKiBAYXV0aG9yIE1pY2hhZWwgR3VlcnJlcm8gLyBodHRwOi8vcmVhbGl0eW1lbHRkb3duLmNvbVxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yIGlrZXJyIC8gaHR0cDovL3Zlcm9sZC5jb21cblx0ICogQGF1dGhvciBNdWdlbjg3IC8gaHR0cHM6Ly9naXRodWIuY29tL011Z2VuODdcblx0ICovXG5cblx0ZnVuY3Rpb24gU2tlbGV0b25IZWxwZXIoIG9iamVjdCApIHtcblxuXHRcdHRoaXMuYm9uZXMgPSB0aGlzLmdldEJvbmVMaXN0KCBvYmplY3QgKTtcblxuXHRcdHZhciBnZW9tZXRyeSA9IG5ldyBCdWZmZXJHZW9tZXRyeSgpO1xuXG5cdFx0dmFyIHZlcnRpY2VzID0gW107XG5cdFx0dmFyIGNvbG9ycyA9IFtdO1xuXG5cdFx0dmFyIGNvbG9yMSA9IG5ldyBDb2xvciggMCwgMCwgMSApO1xuXHRcdHZhciBjb2xvcjIgPSBuZXcgQ29sb3IoIDAsIDEsIDAgKTtcblxuXHRcdGZvciAoIHZhciBpID0gMDsgaSA8IHRoaXMuYm9uZXMubGVuZ3RoOyBpICsrICkge1xuXG5cdFx0XHR2YXIgYm9uZSA9IHRoaXMuYm9uZXNbIGkgXTtcblxuXHRcdFx0aWYgKCBib25lLnBhcmVudCAmJiBib25lLnBhcmVudC5pc0JvbmUgKSB7XG5cblx0XHRcdFx0dmVydGljZXMucHVzaCggMCwgMCwgMCApO1xuXHRcdFx0XHR2ZXJ0aWNlcy5wdXNoKCAwLCAwLCAwICk7XG5cdFx0XHRcdGNvbG9ycy5wdXNoKCBjb2xvcjEuciwgY29sb3IxLmcsIGNvbG9yMS5iICk7XG5cdFx0XHRcdGNvbG9ycy5wdXNoKCBjb2xvcjIuciwgY29sb3IyLmcsIGNvbG9yMi5iICk7XG5cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGdlb21ldHJ5LmFkZEF0dHJpYnV0ZSggJ3Bvc2l0aW9uJywgbmV3IEZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUoIHZlcnRpY2VzLCAzICkgKTtcblx0XHRnZW9tZXRyeS5hZGRBdHRyaWJ1dGUoICdjb2xvcicsIG5ldyBGbG9hdDMyQnVmZmVyQXR0cmlidXRlKCBjb2xvcnMsIDMgKSApO1xuXG5cdFx0dmFyIG1hdGVyaWFsID0gbmV3IExpbmVCYXNpY01hdGVyaWFsKCB7IHZlcnRleENvbG9yczogVmVydGV4Q29sb3JzLCBkZXB0aFRlc3Q6IGZhbHNlLCBkZXB0aFdyaXRlOiBmYWxzZSwgdHJhbnNwYXJlbnQ6IHRydWUgfSApO1xuXG5cdFx0TGluZVNlZ21lbnRzLmNhbGwoIHRoaXMsIGdlb21ldHJ5LCBtYXRlcmlhbCApO1xuXG5cdFx0dGhpcy5yb290ID0gb2JqZWN0O1xuXG5cdFx0dGhpcy5tYXRyaXggPSBvYmplY3QubWF0cml4V29ybGQ7XG5cdFx0dGhpcy5tYXRyaXhBdXRvVXBkYXRlID0gZmFsc2U7XG5cblx0XHR0aGlzLnVwZGF0ZSgpO1xuXG5cdH1cblxuXG5cdFNrZWxldG9uSGVscGVyLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIExpbmVTZWdtZW50cy5wcm90b3R5cGUgKTtcblx0U2tlbGV0b25IZWxwZXIucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gU2tlbGV0b25IZWxwZXI7XG5cblx0U2tlbGV0b25IZWxwZXIucHJvdG90eXBlLmdldEJvbmVMaXN0ID0gZnVuY3Rpb24oIG9iamVjdCApIHtcblxuXHRcdHZhciBib25lTGlzdCA9IFtdO1xuXG5cdFx0aWYgKCBvYmplY3QgJiYgb2JqZWN0LmlzQm9uZSApIHtcblxuXHRcdFx0Ym9uZUxpc3QucHVzaCggb2JqZWN0ICk7XG5cblx0XHR9XG5cblx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCBvYmplY3QuY2hpbGRyZW4ubGVuZ3RoOyBpICsrICkge1xuXG5cdFx0XHRib25lTGlzdC5wdXNoLmFwcGx5KCBib25lTGlzdCwgdGhpcy5nZXRCb25lTGlzdCggb2JqZWN0LmNoaWxkcmVuWyBpIF0gKSApO1xuXG5cdFx0fVxuXG5cdFx0cmV0dXJuIGJvbmVMaXN0O1xuXG5cdH07XG5cblx0U2tlbGV0b25IZWxwZXIucHJvdG90eXBlLnVwZGF0ZSA9IGZ1bmN0aW9uICgpIHtcblxuXHRcdHZhciB2ZWN0b3IgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0dmFyIGJvbmVNYXRyaXggPSBuZXcgTWF0cml4NCgpO1xuXHRcdHZhciBtYXRyaXhXb3JsZEludiA9IG5ldyBNYXRyaXg0KCk7XG5cblx0XHRyZXR1cm4gZnVuY3Rpb24gdXBkYXRlKCkge1xuXG5cdFx0XHR2YXIgZ2VvbWV0cnkgPSB0aGlzLmdlb21ldHJ5O1xuXHRcdFx0dmFyIHBvc2l0aW9uID0gZ2VvbWV0cnkuZ2V0QXR0cmlidXRlKCAncG9zaXRpb24nICk7XG5cblx0XHRcdG1hdHJpeFdvcmxkSW52LmdldEludmVyc2UoIHRoaXMucm9vdC5tYXRyaXhXb3JsZCApO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGogPSAwOyBpIDwgdGhpcy5ib25lcy5sZW5ndGg7IGkgKysgKSB7XG5cblx0XHRcdFx0dmFyIGJvbmUgPSB0aGlzLmJvbmVzWyBpIF07XG5cblx0XHRcdFx0aWYgKCBib25lLnBhcmVudCAmJiBib25lLnBhcmVudC5pc0JvbmUgKSB7XG5cblx0XHRcdFx0XHRib25lTWF0cml4Lm11bHRpcGx5TWF0cmljZXMoIG1hdHJpeFdvcmxkSW52LCBib25lLm1hdHJpeFdvcmxkICk7XG5cdFx0XHRcdFx0dmVjdG9yLnNldEZyb21NYXRyaXhQb3NpdGlvbiggYm9uZU1hdHJpeCApO1xuXHRcdFx0XHRcdHBvc2l0aW9uLnNldFhZWiggaiwgdmVjdG9yLngsIHZlY3Rvci55LCB2ZWN0b3IueiApO1xuXG5cdFx0XHRcdFx0Ym9uZU1hdHJpeC5tdWx0aXBseU1hdHJpY2VzKCBtYXRyaXhXb3JsZEludiwgYm9uZS5wYXJlbnQubWF0cml4V29ybGQgKTtcblx0XHRcdFx0XHR2ZWN0b3Iuc2V0RnJvbU1hdHJpeFBvc2l0aW9uKCBib25lTWF0cml4ICk7XG5cdFx0XHRcdFx0cG9zaXRpb24uc2V0WFlaKCBqICsgMSwgdmVjdG9yLngsIHZlY3Rvci55LCB2ZWN0b3IueiApO1xuXG5cdFx0XHRcdFx0aiArPSAyO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRnZW9tZXRyeS5nZXRBdHRyaWJ1dGUoICdwb3NpdGlvbicgKS5uZWVkc1VwZGF0ZSA9IHRydWU7XG5cblx0XHR9O1xuXG5cdH0oKTtcblxuXHQvKipcblx0ICogQGF1dGhvciBhbHRlcmVkcSAvIGh0dHA6Ly9hbHRlcmVkcXVhbGlhLmNvbS9cblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gUG9pbnRMaWdodEhlbHBlciggbGlnaHQsIHNwaGVyZVNpemUgKSB7XG5cblx0XHR0aGlzLmxpZ2h0ID0gbGlnaHQ7XG5cdFx0dGhpcy5saWdodC51cGRhdGVNYXRyaXhXb3JsZCgpO1xuXG5cdFx0dmFyIGdlb21ldHJ5ID0gbmV3IFNwaGVyZUJ1ZmZlckdlb21ldHJ5KCBzcGhlcmVTaXplLCA0LCAyICk7XG5cdFx0dmFyIG1hdGVyaWFsID0gbmV3IE1lc2hCYXNpY01hdGVyaWFsKCB7IHdpcmVmcmFtZTogdHJ1ZSwgZm9nOiBmYWxzZSB9ICk7XG5cdFx0bWF0ZXJpYWwuY29sb3IuY29weSggdGhpcy5saWdodC5jb2xvciApLm11bHRpcGx5U2NhbGFyKCB0aGlzLmxpZ2h0LmludGVuc2l0eSApO1xuXG5cdFx0TWVzaC5jYWxsKCB0aGlzLCBnZW9tZXRyeSwgbWF0ZXJpYWwgKTtcblxuXHRcdHRoaXMubWF0cml4ID0gdGhpcy5saWdodC5tYXRyaXhXb3JsZDtcblx0XHR0aGlzLm1hdHJpeEF1dG9VcGRhdGUgPSBmYWxzZTtcblxuXHRcdC8qXG5cdFx0dmFyIGRpc3RhbmNlR2VvbWV0cnkgPSBuZXcgVEhSRUUuSWNvc2FoZWRyb25HZW9tZXRyeSggMSwgMiApO1xuXHRcdHZhciBkaXN0YW5jZU1hdGVyaWFsID0gbmV3IFRIUkVFLk1lc2hCYXNpY01hdGVyaWFsKCB7IGNvbG9yOiBoZXhDb2xvciwgZm9nOiBmYWxzZSwgd2lyZWZyYW1lOiB0cnVlLCBvcGFjaXR5OiAwLjEsIHRyYW5zcGFyZW50OiB0cnVlIH0gKTtcblxuXHRcdHRoaXMubGlnaHRTcGhlcmUgPSBuZXcgVEhSRUUuTWVzaCggYnVsYkdlb21ldHJ5LCBidWxiTWF0ZXJpYWwgKTtcblx0XHR0aGlzLmxpZ2h0RGlzdGFuY2UgPSBuZXcgVEhSRUUuTWVzaCggZGlzdGFuY2VHZW9tZXRyeSwgZGlzdGFuY2VNYXRlcmlhbCApO1xuXG5cdFx0dmFyIGQgPSBsaWdodC5kaXN0YW5jZTtcblxuXHRcdGlmICggZCA9PT0gMC4wICkge1xuXG5cdFx0XHR0aGlzLmxpZ2h0RGlzdGFuY2UudmlzaWJsZSA9IGZhbHNlO1xuXG5cdFx0fSBlbHNlIHtcblxuXHRcdFx0dGhpcy5saWdodERpc3RhbmNlLnNjYWxlLnNldCggZCwgZCwgZCApO1xuXG5cdFx0fVxuXG5cdFx0dGhpcy5hZGQoIHRoaXMubGlnaHREaXN0YW5jZSApO1xuXHRcdCovXG5cblx0fVxuXG5cdFBvaW50TGlnaHRIZWxwZXIucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggTWVzaC5wcm90b3R5cGUgKTtcblx0UG9pbnRMaWdodEhlbHBlci5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBQb2ludExpZ2h0SGVscGVyO1xuXG5cdFBvaW50TGlnaHRIZWxwZXIucHJvdG90eXBlLmRpc3Bvc2UgPSBmdW5jdGlvbiAoKSB7XG5cblx0XHR0aGlzLmdlb21ldHJ5LmRpc3Bvc2UoKTtcblx0XHR0aGlzLm1hdGVyaWFsLmRpc3Bvc2UoKTtcblxuXHR9O1xuXG5cdFBvaW50TGlnaHRIZWxwZXIucHJvdG90eXBlLnVwZGF0ZSA9IGZ1bmN0aW9uICgpIHtcblxuXHRcdHRoaXMubWF0ZXJpYWwuY29sb3IuY29weSggdGhpcy5saWdodC5jb2xvciApLm11bHRpcGx5U2NhbGFyKCB0aGlzLmxpZ2h0LmludGVuc2l0eSApO1xuXG5cdFx0Lypcblx0XHR2YXIgZCA9IHRoaXMubGlnaHQuZGlzdGFuY2U7XG5cblx0XHRpZiAoIGQgPT09IDAuMCApIHtcblxuXHRcdFx0dGhpcy5saWdodERpc3RhbmNlLnZpc2libGUgPSBmYWxzZTtcblxuXHRcdH0gZWxzZSB7XG5cblx0XHRcdHRoaXMubGlnaHREaXN0YW5jZS52aXNpYmxlID0gdHJ1ZTtcblx0XHRcdHRoaXMubGlnaHREaXN0YW5jZS5zY2FsZS5zZXQoIGQsIGQsIGQgKTtcblxuXHRcdH1cblx0XHQqL1xuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgYWJlbG5hdGlvbiAvIGh0dHA6Ly9naXRodWIuY29tL2FiZWxuYXRpb25cblx0ICogQGF1dGhvciBNdWdlbjg3IC8gaHR0cDovL2dpdGh1Yi5jb20vTXVnZW44N1xuXHQgKi9cblxuXHRmdW5jdGlvbiBSZWN0QXJlYUxpZ2h0SGVscGVyKCBsaWdodCApIHtcblxuXHRcdE9iamVjdDNELmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMubGlnaHQgPSBsaWdodDtcblx0XHR0aGlzLmxpZ2h0LnVwZGF0ZU1hdHJpeFdvcmxkKCk7XG5cblx0XHR2YXIgbWF0ZXJpYWxGcm9udCA9IG5ldyBNZXNoQmFzaWNNYXRlcmlhbCgge1xuXHRcdFx0Y29sb3I6IGxpZ2h0LmNvbG9yLFxuXHRcdFx0Zm9nOiBmYWxzZVxuXHRcdH0gKTtcblxuXHRcdHZhciBtYXRlcmlhbEJhY2sgPSBuZXcgTWVzaEJhc2ljTWF0ZXJpYWwoIHtcblx0XHRcdGNvbG9yOiBsaWdodC5jb2xvcixcblx0XHRcdGZvZzogZmFsc2UsXG5cdFx0XHR3aXJlZnJhbWU6IHRydWVcblx0XHR9ICk7XG5cblx0XHR2YXIgZ2VvbWV0cnkgPSBuZXcgQnVmZmVyR2VvbWV0cnkoKTtcblxuXHRcdGdlb21ldHJ5LmFkZEF0dHJpYnV0ZSggJ3Bvc2l0aW9uJywgbmV3IEJ1ZmZlckF0dHJpYnV0ZSggbmV3IEZsb2F0MzJBcnJheSggNiAqIDMgKSwgMyApICk7XG5cblx0XHQvLyBzaG93cyB0aGUgXCJmcm9udFwiIG9mIHRoZSBsaWdodCwgZS5nLiB3aGVyZSBsaWdodCBjb21lcyBmcm9tXG5cblx0XHR0aGlzLmFkZCggbmV3IE1lc2goIGdlb21ldHJ5LCBtYXRlcmlhbEZyb250ICkgKTtcblxuXHRcdC8vIHNob3dzIHRoZSBcImJhY2tcIiBvZiB0aGUgbGlnaHQsIHdoaWNoIGRvZXMgbm90IGVtaXQgbGlnaHRcblxuXHRcdHRoaXMuYWRkKCBuZXcgTWVzaCggZ2VvbWV0cnksIG1hdGVyaWFsQmFjayApICk7XG5cblx0XHR0aGlzLnVwZGF0ZSgpO1xuXG5cdH1cblxuXHRSZWN0QXJlYUxpZ2h0SGVscGVyLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIE9iamVjdDNELnByb3RvdHlwZSApO1xuXHRSZWN0QXJlYUxpZ2h0SGVscGVyLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFJlY3RBcmVhTGlnaHRIZWxwZXI7XG5cblx0UmVjdEFyZWFMaWdodEhlbHBlci5wcm90b3R5cGUuZGlzcG9zZSA9IGZ1bmN0aW9uICgpIHtcblxuXHRcdHRoaXMuY2hpbGRyZW5bIDAgXS5nZW9tZXRyeS5kaXNwb3NlKCk7XG5cdFx0dGhpcy5jaGlsZHJlblsgMCBdLm1hdGVyaWFsLmRpc3Bvc2UoKTtcblx0XHR0aGlzLmNoaWxkcmVuWyAxIF0uZ2VvbWV0cnkuZGlzcG9zZSgpO1xuXHRcdHRoaXMuY2hpbGRyZW5bIDEgXS5tYXRlcmlhbC5kaXNwb3NlKCk7XG5cblx0fTtcblxuXHRSZWN0QXJlYUxpZ2h0SGVscGVyLnByb3RvdHlwZS51cGRhdGUgPSBmdW5jdGlvbiAoKSB7XG5cblx0XHR2YXIgdmVjdG9yMSA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0dmFyIHZlY3RvcjIgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0cmV0dXJuIGZ1bmN0aW9uIHVwZGF0ZSgpIHtcblxuXHRcdFx0dmFyIG1lc2gxID0gdGhpcy5jaGlsZHJlblsgMCBdO1xuXHRcdFx0dmFyIG1lc2gyID0gdGhpcy5jaGlsZHJlblsgMSBdO1xuXG5cdFx0XHRpZiAoIHRoaXMubGlnaHQudGFyZ2V0ICkge1xuXG5cdFx0XHRcdHZlY3RvcjEuc2V0RnJvbU1hdHJpeFBvc2l0aW9uKCB0aGlzLmxpZ2h0Lm1hdHJpeFdvcmxkICk7XG5cdFx0XHRcdHZlY3RvcjIuc2V0RnJvbU1hdHJpeFBvc2l0aW9uKCB0aGlzLmxpZ2h0LnRhcmdldC5tYXRyaXhXb3JsZCApO1xuXG5cdFx0XHRcdHZhciBsb29rVmVjID0gdmVjdG9yMi5jbG9uZSgpLnN1YiggdmVjdG9yMSApO1xuXHRcdFx0XHRtZXNoMS5sb29rQXQoIGxvb2tWZWMgKTtcblx0XHRcdFx0bWVzaDIubG9va0F0KCBsb29rVmVjICk7XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gdXBkYXRlIG1hdGVyaWFsc1xuXG5cdFx0XHRtZXNoMS5tYXRlcmlhbC5jb2xvci5jb3B5KCB0aGlzLmxpZ2h0LmNvbG9yICkubXVsdGlwbHlTY2FsYXIoIHRoaXMubGlnaHQuaW50ZW5zaXR5ICk7XG5cdFx0XHRtZXNoMi5tYXRlcmlhbC5jb2xvci5jb3B5KCB0aGlzLmxpZ2h0LmNvbG9yICkubXVsdGlwbHlTY2FsYXIoIHRoaXMubGlnaHQuaW50ZW5zaXR5ICk7XG5cblx0XHRcdC8vIGNhbGN1bGF0ZSBuZXcgZGltZW5zaW9ucyBvZiB0aGUgaGVscGVyXG5cblx0XHRcdHZhciBoeCA9IHRoaXMubGlnaHQud2lkdGggKiAwLjU7XG5cdFx0XHR2YXIgaHkgPSB0aGlzLmxpZ2h0LmhlaWdodCAqIDAuNTtcblxuXHRcdFx0Ly8gYmVjYXVzZSB0aGUgYnVmZmVyIGF0dHJpYnV0ZSBpcyBzaGFyZWQgb3ZlciBib3RoIGdlb21ldHJpZXMsIHdlIG9ubHkgaGF2ZSB0byB1cGRhdGUgb25jZVxuXG5cdFx0XHR2YXIgcG9zaXRpb24gPSBtZXNoMS5nZW9tZXRyeS5nZXRBdHRyaWJ1dGUoICdwb3NpdGlvbicgKTtcblx0XHRcdHZhciBhcnJheSA9IHBvc2l0aW9uLmFycmF5O1xuXG5cdFx0XHQvLyBmaXJzdCBmYWNlXG5cblx0XHRcdGFycmF5WyAgMCBdID0gICBoeDsgYXJyYXlbICAxIF0gPSAtIGh5OyBhcnJheVsgIDIgXSA9IDA7XG5cdFx0XHRhcnJheVsgIDMgXSA9ICAgaHg7IGFycmF5WyAgNCBdID0gICBoeTsgYXJyYXlbICA1IF0gPSAwO1xuXHRcdFx0YXJyYXlbICA2IF0gPSAtIGh4OyBhcnJheVsgIDcgXSA9ICAgaHk7IGFycmF5WyAgOCBdID0gMDtcblxuXHRcdFx0Ly8gc2Vjb25kIGZhY2VcblxuXHRcdFx0YXJyYXlbICA5IF0gPSAtIGh4OyBhcnJheVsgMTAgXSA9ICAgaHk7IGFycmF5WyAxMSBdID0gMDtcblx0XHRcdGFycmF5WyAxMiBdID0gLSBoeDsgYXJyYXlbIDEzIF0gPSAtIGh5OyBhcnJheVsgMTQgXSA9IDA7XG5cdFx0XHRhcnJheVsgMTUgXSA9ICAgaHg7IGFycmF5WyAxNiBdID0gLSBoeTsgYXJyYXlbIDE3IF0gPSAwO1xuXG5cdFx0XHRwb3NpdGlvbi5uZWVkc1VwZGF0ZSA9IHRydWU7XG5cblx0XHR9O1xuXG5cdH0oKTtcblxuXHQvKipcblx0ICogQGF1dGhvciBhbHRlcmVkcSAvIGh0dHA6Ly9hbHRlcmVkcXVhbGlhLmNvbS9cblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICogQGF1dGhvciBNdWdlbjg3IC8gaHR0cHM6Ly9naXRodWIuY29tL011Z2VuODdcblx0ICovXG5cblx0ZnVuY3Rpb24gSGVtaXNwaGVyZUxpZ2h0SGVscGVyKCBsaWdodCwgc2l6ZSApIHtcblxuXHRcdE9iamVjdDNELmNhbGwoIHRoaXMgKTtcblxuXHRcdHRoaXMubGlnaHQgPSBsaWdodDtcblx0XHR0aGlzLmxpZ2h0LnVwZGF0ZU1hdHJpeFdvcmxkKCk7XG5cblx0XHR0aGlzLm1hdHJpeCA9IGxpZ2h0Lm1hdHJpeFdvcmxkO1xuXHRcdHRoaXMubWF0cml4QXV0b1VwZGF0ZSA9IGZhbHNlO1xuXG5cdFx0dmFyIGdlb21ldHJ5ID0gbmV3IE9jdGFoZWRyb25CdWZmZXJHZW9tZXRyeSggc2l6ZSApO1xuXHRcdGdlb21ldHJ5LnJvdGF0ZVkoIE1hdGguUEkgKiAwLjUgKTtcblxuXHRcdHZhciBtYXRlcmlhbCA9IG5ldyBNZXNoQmFzaWNNYXRlcmlhbCggeyB2ZXJ0ZXhDb2xvcnM6IFZlcnRleENvbG9ycywgd2lyZWZyYW1lOiB0cnVlIH0gKTtcblxuXHRcdHZhciBwb3NpdGlvbiA9IGdlb21ldHJ5LmdldEF0dHJpYnV0ZSggJ3Bvc2l0aW9uJyApO1xuXHRcdHZhciBjb2xvcnMgPSBuZXcgRmxvYXQzMkFycmF5KCBwb3NpdGlvbi5jb3VudCAqIDMgKTtcblxuXHRcdGdlb21ldHJ5LmFkZEF0dHJpYnV0ZSggJ2NvbG9yJywgbmV3IEJ1ZmZlckF0dHJpYnV0ZSggY29sb3JzLCAzICkgKTtcblxuXHRcdHRoaXMuYWRkKCBuZXcgTWVzaCggZ2VvbWV0cnksIG1hdGVyaWFsICkgKTtcblxuXHRcdHRoaXMudXBkYXRlKCk7XG5cblx0fVxuXG5cdEhlbWlzcGhlcmVMaWdodEhlbHBlci5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBPYmplY3QzRC5wcm90b3R5cGUgKTtcblx0SGVtaXNwaGVyZUxpZ2h0SGVscGVyLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IEhlbWlzcGhlcmVMaWdodEhlbHBlcjtcblxuXHRIZW1pc3BoZXJlTGlnaHRIZWxwZXIucHJvdG90eXBlLmRpc3Bvc2UgPSBmdW5jdGlvbiAoKSB7XG5cblx0XHR0aGlzLmNoaWxkcmVuWyAwIF0uZ2VvbWV0cnkuZGlzcG9zZSgpO1xuXHRcdHRoaXMuY2hpbGRyZW5bIDAgXS5tYXRlcmlhbC5kaXNwb3NlKCk7XG5cblx0fTtcblxuXHRIZW1pc3BoZXJlTGlnaHRIZWxwZXIucHJvdG90eXBlLnVwZGF0ZSA9IGZ1bmN0aW9uICgpIHtcblxuXHRcdHZhciB2ZWN0b3IgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0dmFyIGNvbG9yMSA9IG5ldyBDb2xvcigpO1xuXHRcdHZhciBjb2xvcjIgPSBuZXcgQ29sb3IoKTtcblxuXHRcdHJldHVybiBmdW5jdGlvbiB1cGRhdGUoKSB7XG5cblx0XHRcdHZhciBtZXNoID0gdGhpcy5jaGlsZHJlblsgMCBdO1xuXG5cdFx0XHR2YXIgY29sb3JzID0gbWVzaC5nZW9tZXRyeS5nZXRBdHRyaWJ1dGUoICdjb2xvcicgKTtcblxuXHRcdFx0Y29sb3IxLmNvcHkoIHRoaXMubGlnaHQuY29sb3IgKS5tdWx0aXBseVNjYWxhciggdGhpcy5saWdodC5pbnRlbnNpdHkgKTtcblx0XHRcdGNvbG9yMi5jb3B5KCB0aGlzLmxpZ2h0Lmdyb3VuZENvbG9yICkubXVsdGlwbHlTY2FsYXIoIHRoaXMubGlnaHQuaW50ZW5zaXR5ICk7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IGNvbG9ycy5jb3VudDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0dmFyIGNvbG9yID0gKCBpIDwgKCBsIC8gMiApICkgPyBjb2xvcjEgOiBjb2xvcjI7XG5cblx0XHRcdFx0Y29sb3JzLnNldFhZWiggaSwgY29sb3IuciwgY29sb3IuZywgY29sb3IuYiApO1xuXG5cdFx0XHR9XG5cblx0XHRcdG1lc2gubG9va0F0KCB2ZWN0b3Iuc2V0RnJvbU1hdHJpeFBvc2l0aW9uKCB0aGlzLmxpZ2h0Lm1hdHJpeFdvcmxkICkubmVnYXRlKCkgKTtcblxuXHRcdFx0Y29sb3JzLm5lZWRzVXBkYXRlID0gdHJ1ZTtcblxuXHRcdH07XG5cblx0fSgpO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKi9cblxuXHRmdW5jdGlvbiBHcmlkSGVscGVyKCBzaXplLCBkaXZpc2lvbnMsIGNvbG9yMSwgY29sb3IyICkge1xuXG5cdFx0c2l6ZSA9IHNpemUgfHwgMTA7XG5cdFx0ZGl2aXNpb25zID0gZGl2aXNpb25zIHx8IDEwO1xuXHRcdGNvbG9yMSA9IG5ldyBDb2xvciggY29sb3IxICE9PSB1bmRlZmluZWQgPyBjb2xvcjEgOiAweDQ0NDQ0NCApO1xuXHRcdGNvbG9yMiA9IG5ldyBDb2xvciggY29sb3IyICE9PSB1bmRlZmluZWQgPyBjb2xvcjIgOiAweDg4ODg4OCApO1xuXG5cdFx0dmFyIGNlbnRlciA9IGRpdmlzaW9ucyAvIDI7XG5cdFx0dmFyIHN0ZXAgPSAoIHNpemUgKiAyICkgLyBkaXZpc2lvbnM7XG5cdFx0dmFyIHZlcnRpY2VzID0gW10sIGNvbG9ycyA9IFtdO1xuXG5cdFx0Zm9yICggdmFyIGkgPSAwLCBqID0gMCwgayA9IC0gc2l6ZTsgaSA8PSBkaXZpc2lvbnM7IGkgKyssIGsgKz0gc3RlcCApIHtcblxuXHRcdFx0dmVydGljZXMucHVzaCggLSBzaXplLCAwLCBrLCBzaXplLCAwLCBrICk7XG5cdFx0XHR2ZXJ0aWNlcy5wdXNoKCBrLCAwLCAtIHNpemUsIGssIDAsIHNpemUgKTtcblxuXHRcdFx0dmFyIGNvbG9yID0gaSA9PT0gY2VudGVyID8gY29sb3IxIDogY29sb3IyO1xuXG5cdFx0XHRjb2xvci50b0FycmF5KCBjb2xvcnMsIGogKTsgaiArPSAzO1xuXHRcdFx0Y29sb3IudG9BcnJheSggY29sb3JzLCBqICk7IGogKz0gMztcblx0XHRcdGNvbG9yLnRvQXJyYXkoIGNvbG9ycywgaiApOyBqICs9IDM7XG5cdFx0XHRjb2xvci50b0FycmF5KCBjb2xvcnMsIGogKTsgaiArPSAzO1xuXG5cdFx0fVxuXG5cdFx0dmFyIGdlb21ldHJ5ID0gbmV3IEJ1ZmZlckdlb21ldHJ5KCk7XG5cdFx0Z2VvbWV0cnkuYWRkQXR0cmlidXRlKCAncG9zaXRpb24nLCBuZXcgRmxvYXQzMkJ1ZmZlckF0dHJpYnV0ZSggdmVydGljZXMsIDMgKSApO1xuXHRcdGdlb21ldHJ5LmFkZEF0dHJpYnV0ZSggJ2NvbG9yJywgbmV3IEZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUoIGNvbG9ycywgMyApICk7XG5cblx0XHR2YXIgbWF0ZXJpYWwgPSBuZXcgTGluZUJhc2ljTWF0ZXJpYWwoIHsgdmVydGV4Q29sb3JzOiBWZXJ0ZXhDb2xvcnMgfSApO1xuXG5cdFx0TGluZVNlZ21lbnRzLmNhbGwoIHRoaXMsIGdlb21ldHJ5LCBtYXRlcmlhbCApO1xuXG5cdH1cblxuXHRHcmlkSGVscGVyLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIExpbmVTZWdtZW50cy5wcm90b3R5cGUgKTtcblx0R3JpZEhlbHBlci5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBHcmlkSGVscGVyO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yIE11Z2VuODcgLyBodHRwOi8vZ2l0aHViLmNvbS9NdWdlbjg3XG5cdCAqIEBhdXRob3IgSGVjdGF0ZSAvIGh0dHA6Ly93d3cuZ2l0aHViLmNvbS9IZWN0YXRlXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIFBvbGFyR3JpZEhlbHBlciggcmFkaXVzLCByYWRpYWxzLCBjaXJjbGVzLCBkaXZpc2lvbnMsIGNvbG9yMSwgY29sb3IyICkge1xuXG5cdFx0cmFkaXVzID0gcmFkaXVzIHx8IDEwO1xuXHRcdHJhZGlhbHMgPSByYWRpYWxzIHx8IDE2O1xuXHRcdGNpcmNsZXMgPSBjaXJjbGVzIHx8IDg7XG5cdFx0ZGl2aXNpb25zID0gZGl2aXNpb25zIHx8IDY0O1xuXHRcdGNvbG9yMSA9IG5ldyBDb2xvciggY29sb3IxICE9PSB1bmRlZmluZWQgPyBjb2xvcjEgOiAweDQ0NDQ0NCApO1xuXHRcdGNvbG9yMiA9IG5ldyBDb2xvciggY29sb3IyICE9PSB1bmRlZmluZWQgPyBjb2xvcjIgOiAweDg4ODg4OCApO1xuXG5cdFx0dmFyIHZlcnRpY2VzID0gW107XG5cdFx0dmFyIGNvbG9ycyA9IFtdO1xuXG5cdFx0dmFyIHgsIHo7XG5cdFx0dmFyIHYsIGksIGosIHIsIGNvbG9yO1xuXG5cdFx0Ly8gY3JlYXRlIHRoZSByYWRpYWxzXG5cblx0XHRmb3IgKCBpID0gMDsgaSA8PSByYWRpYWxzOyBpICsrICkge1xuXG5cdFx0XHR2ID0gKCBpIC8gcmFkaWFscyApICogKCBNYXRoLlBJICogMiApO1xuXG5cdFx0XHR4ID0gTWF0aC5zaW4oIHYgKSAqIHJhZGl1cztcblx0XHRcdHogPSBNYXRoLmNvcyggdiApICogcmFkaXVzO1xuXG5cdFx0XHR2ZXJ0aWNlcy5wdXNoKCAwLCAwLCAwICk7XG5cdFx0XHR2ZXJ0aWNlcy5wdXNoKCB4LCAwLCB6ICk7XG5cblx0XHRcdGNvbG9yID0gKCBpICYgMSApID8gY29sb3IxIDogY29sb3IyO1xuXG5cdFx0XHRjb2xvcnMucHVzaCggY29sb3IuciwgY29sb3IuZywgY29sb3IuYiApO1xuXHRcdFx0Y29sb3JzLnB1c2goIGNvbG9yLnIsIGNvbG9yLmcsIGNvbG9yLmIgKTtcblxuXHRcdH1cblxuXHRcdC8vIGNyZWF0ZSB0aGUgY2lyY2xlc1xuXG5cdFx0Zm9yICggaSA9IDA7IGkgPD0gY2lyY2xlczsgaSArKyApIHtcblxuXHRcdFx0Y29sb3IgPSAoIGkgJiAxICkgPyBjb2xvcjEgOiBjb2xvcjI7XG5cblx0XHRcdHIgPSByYWRpdXMgLSAoIHJhZGl1cyAvIGNpcmNsZXMgKiBpICk7XG5cblx0XHRcdGZvciAoIGogPSAwOyBqIDwgZGl2aXNpb25zOyBqICsrICkge1xuXG5cdFx0XHRcdC8vIGZpcnN0IHZlcnRleFxuXG5cdFx0XHRcdHYgPSAoIGogLyBkaXZpc2lvbnMgKSAqICggTWF0aC5QSSAqIDIgKTtcblxuXHRcdFx0XHR4ID0gTWF0aC5zaW4oIHYgKSAqIHI7XG5cdFx0XHRcdHogPSBNYXRoLmNvcyggdiApICogcjtcblxuXHRcdFx0XHR2ZXJ0aWNlcy5wdXNoKCB4LCAwLCB6ICk7XG5cdFx0XHRcdGNvbG9ycy5wdXNoKCBjb2xvci5yLCBjb2xvci5nLCBjb2xvci5iICk7XG5cblx0XHRcdFx0Ly8gc2Vjb25kIHZlcnRleFxuXG5cdFx0XHRcdHYgPSAoICggaiArIDEgKSAvIGRpdmlzaW9ucyApICogKCBNYXRoLlBJICogMiApO1xuXG5cdFx0XHRcdHggPSBNYXRoLnNpbiggdiApICogcjtcblx0XHRcdFx0eiA9IE1hdGguY29zKCB2ICkgKiByO1xuXG5cdFx0XHRcdHZlcnRpY2VzLnB1c2goIHgsIDAsIHogKTtcblx0XHRcdFx0Y29sb3JzLnB1c2goIGNvbG9yLnIsIGNvbG9yLmcsIGNvbG9yLmIgKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0dmFyIGdlb21ldHJ5ID0gbmV3IEJ1ZmZlckdlb21ldHJ5KCk7XG5cdFx0Z2VvbWV0cnkuYWRkQXR0cmlidXRlKCAncG9zaXRpb24nLCBuZXcgRmxvYXQzMkJ1ZmZlckF0dHJpYnV0ZSggdmVydGljZXMsIDMgKSApO1xuXHRcdGdlb21ldHJ5LmFkZEF0dHJpYnV0ZSggJ2NvbG9yJywgbmV3IEZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUoIGNvbG9ycywgMyApICk7XG5cblx0XHR2YXIgbWF0ZXJpYWwgPSBuZXcgTGluZUJhc2ljTWF0ZXJpYWwoIHsgdmVydGV4Q29sb3JzOiBWZXJ0ZXhDb2xvcnMgfSApO1xuXG5cdFx0TGluZVNlZ21lbnRzLmNhbGwoIHRoaXMsIGdlb21ldHJ5LCBtYXRlcmlhbCApO1xuXG5cdH1cblxuXHRQb2xhckdyaWRIZWxwZXIucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggTGluZVNlZ21lbnRzLnByb3RvdHlwZSApO1xuXHRQb2xhckdyaWRIZWxwZXIucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gUG9sYXJHcmlkSGVscGVyO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tL1xuXHQgKiBAYXV0aG9yIFdlc3RMYW5nbGV5IC8gaHR0cDovL2dpdGh1Yi5jb20vV2VzdExhbmdsZXlcblx0Ki9cblxuXHRmdW5jdGlvbiBGYWNlTm9ybWFsc0hlbHBlciggb2JqZWN0LCBzaXplLCBoZXgsIGxpbmV3aWR0aCApIHtcblxuXHRcdC8vIEZhY2VOb3JtYWxzSGVscGVyIG9ubHkgc3VwcG9ydHMgVEhSRUUuR2VvbWV0cnlcblxuXHRcdHRoaXMub2JqZWN0ID0gb2JqZWN0O1xuXG5cdFx0dGhpcy5zaXplID0gKCBzaXplICE9PSB1bmRlZmluZWQgKSA/IHNpemUgOiAxO1xuXG5cdFx0dmFyIGNvbG9yID0gKCBoZXggIT09IHVuZGVmaW5lZCApID8gaGV4IDogMHhmZmZmMDA7XG5cblx0XHR2YXIgd2lkdGggPSAoIGxpbmV3aWR0aCAhPT0gdW5kZWZpbmVkICkgPyBsaW5ld2lkdGggOiAxO1xuXG5cdFx0Ly9cblxuXHRcdHZhciBuTm9ybWFscyA9IDA7XG5cblx0XHR2YXIgb2JqR2VvbWV0cnkgPSB0aGlzLm9iamVjdC5nZW9tZXRyeTtcblxuXHRcdGlmICggb2JqR2VvbWV0cnkgJiYgb2JqR2VvbWV0cnkuaXNHZW9tZXRyeSApIHtcblxuXHRcdFx0bk5vcm1hbHMgPSBvYmpHZW9tZXRyeS5mYWNlcy5sZW5ndGg7XG5cblx0XHR9IGVsc2Uge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5GYWNlTm9ybWFsc0hlbHBlcjogb25seSBUSFJFRS5HZW9tZXRyeSBpcyBzdXBwb3J0ZWQuIFVzZSBUSFJFRS5WZXJ0ZXhOb3JtYWxzSGVscGVyLCBpbnN0ZWFkLicgKTtcblxuXHRcdH1cblxuXHRcdC8vXG5cblx0XHR2YXIgZ2VvbWV0cnkgPSBuZXcgQnVmZmVyR2VvbWV0cnkoKTtcblxuXHRcdHZhciBwb3NpdGlvbnMgPSBuZXcgRmxvYXQzMkJ1ZmZlckF0dHJpYnV0ZSggbk5vcm1hbHMgKiAyICogMywgMyApO1xuXG5cdFx0Z2VvbWV0cnkuYWRkQXR0cmlidXRlKCAncG9zaXRpb24nLCBwb3NpdGlvbnMgKTtcblxuXHRcdExpbmVTZWdtZW50cy5jYWxsKCB0aGlzLCBnZW9tZXRyeSwgbmV3IExpbmVCYXNpY01hdGVyaWFsKCB7IGNvbG9yOiBjb2xvciwgbGluZXdpZHRoOiB3aWR0aCB9ICkgKTtcblxuXHRcdC8vXG5cblx0XHR0aGlzLm1hdHJpeEF1dG9VcGRhdGUgPSBmYWxzZTtcblx0XHR0aGlzLnVwZGF0ZSgpO1xuXG5cdH1cblxuXHRGYWNlTm9ybWFsc0hlbHBlci5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBMaW5lU2VnbWVudHMucHJvdG90eXBlICk7XG5cdEZhY2VOb3JtYWxzSGVscGVyLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IEZhY2VOb3JtYWxzSGVscGVyO1xuXG5cdEZhY2VOb3JtYWxzSGVscGVyLnByb3RvdHlwZS51cGRhdGUgPSAoIGZ1bmN0aW9uICgpIHtcblxuXHRcdHZhciB2MSA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0dmFyIHYyID0gbmV3IFZlY3RvcjMoKTtcblx0XHR2YXIgbm9ybWFsTWF0cml4ID0gbmV3IE1hdHJpeDMoKTtcblxuXHRcdHJldHVybiBmdW5jdGlvbiB1cGRhdGUoKSB7XG5cblx0XHRcdHRoaXMub2JqZWN0LnVwZGF0ZU1hdHJpeFdvcmxkKCB0cnVlICk7XG5cblx0XHRcdG5vcm1hbE1hdHJpeC5nZXROb3JtYWxNYXRyaXgoIHRoaXMub2JqZWN0Lm1hdHJpeFdvcmxkICk7XG5cblx0XHRcdHZhciBtYXRyaXhXb3JsZCA9IHRoaXMub2JqZWN0Lm1hdHJpeFdvcmxkO1xuXG5cdFx0XHR2YXIgcG9zaXRpb24gPSB0aGlzLmdlb21ldHJ5LmF0dHJpYnV0ZXMucG9zaXRpb247XG5cblx0XHRcdC8vXG5cblx0XHRcdHZhciBvYmpHZW9tZXRyeSA9IHRoaXMub2JqZWN0Lmdlb21ldHJ5O1xuXG5cdFx0XHR2YXIgdmVydGljZXMgPSBvYmpHZW9tZXRyeS52ZXJ0aWNlcztcblxuXHRcdFx0dmFyIGZhY2VzID0gb2JqR2VvbWV0cnkuZmFjZXM7XG5cblx0XHRcdHZhciBpZHggPSAwO1xuXG5cdFx0XHRmb3IgKCB2YXIgaSA9IDAsIGwgPSBmYWNlcy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdHZhciBmYWNlID0gZmFjZXNbIGkgXTtcblxuXHRcdFx0XHR2YXIgbm9ybWFsID0gZmFjZS5ub3JtYWw7XG5cblx0XHRcdFx0djEuY29weSggdmVydGljZXNbIGZhY2UuYSBdIClcblx0XHRcdFx0XHQuYWRkKCB2ZXJ0aWNlc1sgZmFjZS5iIF0gKVxuXHRcdFx0XHRcdC5hZGQoIHZlcnRpY2VzWyBmYWNlLmMgXSApXG5cdFx0XHRcdFx0LmRpdmlkZVNjYWxhciggMyApXG5cdFx0XHRcdFx0LmFwcGx5TWF0cml4NCggbWF0cml4V29ybGQgKTtcblxuXHRcdFx0XHR2Mi5jb3B5KCBub3JtYWwgKS5hcHBseU1hdHJpeDMoIG5vcm1hbE1hdHJpeCApLm5vcm1hbGl6ZSgpLm11bHRpcGx5U2NhbGFyKCB0aGlzLnNpemUgKS5hZGQoIHYxICk7XG5cblx0XHRcdFx0cG9zaXRpb24uc2V0WFlaKCBpZHgsIHYxLngsIHYxLnksIHYxLnogKTtcblxuXHRcdFx0XHRpZHggPSBpZHggKyAxO1xuXG5cdFx0XHRcdHBvc2l0aW9uLnNldFhZWiggaWR4LCB2Mi54LCB2Mi55LCB2Mi56ICk7XG5cblx0XHRcdFx0aWR4ID0gaWR4ICsgMTtcblxuXHRcdFx0fVxuXG5cdFx0XHRwb3NpdGlvbi5uZWVkc1VwZGF0ZSA9IHRydWU7XG5cblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0fTtcblxuXHR9KCkgKTtcblxuXHQvKipcblx0ICogQGF1dGhvciBhbHRlcmVkcSAvIGh0dHA6Ly9hbHRlcmVkcXVhbGlhLmNvbS9cblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICogQGF1dGhvciBXZXN0TGFuZ2xleSAvIGh0dHA6Ly9naXRodWIuY29tL1dlc3RMYW5nbGV5XG5cdCAqL1xuXG5cdGZ1bmN0aW9uIERpcmVjdGlvbmFsTGlnaHRIZWxwZXIoIGxpZ2h0LCBzaXplICkge1xuXG5cdFx0T2JqZWN0M0QuY2FsbCggdGhpcyApO1xuXG5cdFx0dGhpcy5saWdodCA9IGxpZ2h0O1xuXHRcdHRoaXMubGlnaHQudXBkYXRlTWF0cml4V29ybGQoKTtcblxuXHRcdHRoaXMubWF0cml4ID0gbGlnaHQubWF0cml4V29ybGQ7XG5cdFx0dGhpcy5tYXRyaXhBdXRvVXBkYXRlID0gZmFsc2U7XG5cblx0XHRpZiAoIHNpemUgPT09IHVuZGVmaW5lZCApIHNpemUgPSAxO1xuXG5cdFx0dmFyIGdlb21ldHJ5ID0gbmV3IEJ1ZmZlckdlb21ldHJ5KCk7XG5cdFx0Z2VvbWV0cnkuYWRkQXR0cmlidXRlKCAncG9zaXRpb24nLCBuZXcgRmxvYXQzMkJ1ZmZlckF0dHJpYnV0ZSggW1xuXHRcdFx0LSBzaXplLCAgIHNpemUsIDAsXG5cdFx0XHQgIHNpemUsICAgc2l6ZSwgMCxcblx0XHRcdCAgc2l6ZSwgLSBzaXplLCAwLFxuXHRcdFx0LSBzaXplLCAtIHNpemUsIDAsXG5cdFx0XHQtIHNpemUsICAgc2l6ZSwgMFxuXHRcdF0sIDMgKSApO1xuXG5cdFx0dmFyIG1hdGVyaWFsID0gbmV3IExpbmVCYXNpY01hdGVyaWFsKCB7IGZvZzogZmFsc2UgfSApO1xuXG5cdFx0dGhpcy5hZGQoIG5ldyBMaW5lKCBnZW9tZXRyeSwgbWF0ZXJpYWwgKSApO1xuXG5cdFx0Z2VvbWV0cnkgPSBuZXcgQnVmZmVyR2VvbWV0cnkoKTtcblx0XHRnZW9tZXRyeS5hZGRBdHRyaWJ1dGUoICdwb3NpdGlvbicsIG5ldyBGbG9hdDMyQnVmZmVyQXR0cmlidXRlKCBbIDAsIDAsIDAsIDAsIDAsIDEgXSwgMyApICk7XG5cblx0XHR0aGlzLmFkZCggbmV3IExpbmUoIGdlb21ldHJ5LCBtYXRlcmlhbCApKTtcblxuXHRcdHRoaXMudXBkYXRlKCk7XG5cblx0fVxuXG5cdERpcmVjdGlvbmFsTGlnaHRIZWxwZXIucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggT2JqZWN0M0QucHJvdG90eXBlICk7XG5cdERpcmVjdGlvbmFsTGlnaHRIZWxwZXIucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gRGlyZWN0aW9uYWxMaWdodEhlbHBlcjtcblxuXHREaXJlY3Rpb25hbExpZ2h0SGVscGVyLnByb3RvdHlwZS5kaXNwb3NlID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0dmFyIGxpZ2h0UGxhbmUgPSB0aGlzLmNoaWxkcmVuWyAwIF07XG5cdFx0dmFyIHRhcmdldExpbmUgPSB0aGlzLmNoaWxkcmVuWyAxIF07XG5cblx0XHRsaWdodFBsYW5lLmdlb21ldHJ5LmRpc3Bvc2UoKTtcblx0XHRsaWdodFBsYW5lLm1hdGVyaWFsLmRpc3Bvc2UoKTtcblx0XHR0YXJnZXRMaW5lLmdlb21ldHJ5LmRpc3Bvc2UoKTtcblx0XHR0YXJnZXRMaW5lLm1hdGVyaWFsLmRpc3Bvc2UoKTtcblxuXHR9O1xuXG5cdERpcmVjdGlvbmFsTGlnaHRIZWxwZXIucHJvdG90eXBlLnVwZGF0ZSA9IGZ1bmN0aW9uICgpIHtcblxuXHRcdHZhciB2MSA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0dmFyIHYyID0gbmV3IFZlY3RvcjMoKTtcblx0XHR2YXIgdjMgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0cmV0dXJuIGZ1bmN0aW9uIHVwZGF0ZSgpIHtcblxuXHRcdFx0djEuc2V0RnJvbU1hdHJpeFBvc2l0aW9uKCB0aGlzLmxpZ2h0Lm1hdHJpeFdvcmxkICk7XG5cdFx0XHR2Mi5zZXRGcm9tTWF0cml4UG9zaXRpb24oIHRoaXMubGlnaHQudGFyZ2V0Lm1hdHJpeFdvcmxkICk7XG5cdFx0XHR2My5zdWJWZWN0b3JzKCB2MiwgdjEgKTtcblxuXHRcdFx0dmFyIGxpZ2h0UGxhbmUgPSB0aGlzLmNoaWxkcmVuWyAwIF07XG5cdFx0XHR2YXIgdGFyZ2V0TGluZSA9IHRoaXMuY2hpbGRyZW5bIDEgXTtcblxuXHRcdFx0bGlnaHRQbGFuZS5sb29rQXQoIHYzICk7XG5cdFx0XHRsaWdodFBsYW5lLm1hdGVyaWFsLmNvbG9yLmNvcHkoIHRoaXMubGlnaHQuY29sb3IgKS5tdWx0aXBseVNjYWxhciggdGhpcy5saWdodC5pbnRlbnNpdHkgKTtcblxuXHRcdFx0dGFyZ2V0TGluZS5sb29rQXQoIHYzICk7XG5cdFx0XHR0YXJnZXRMaW5lLnNjYWxlLnogPSB2My5sZW5ndGgoKTtcblxuXHRcdH07XG5cblx0fSgpO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKiBAYXV0aG9yIE11Z2VuODcgLyBodHRwczovL2dpdGh1Yi5jb20vTXVnZW44N1xuXHQgKlxuXHQgKlx0LSBzaG93cyBmcnVzdHVtLCBsaW5lIG9mIHNpZ2h0IGFuZCB1cCBvZiB0aGUgY2FtZXJhXG5cdCAqXHQtIHN1aXRhYmxlIGZvciBmYXN0IHVwZGF0ZXNcblx0ICogXHQtIGJhc2VkIG9uIGZydXN0dW0gdmlzdWFsaXphdGlvbiBpbiBsaWdodGdsLmpzIHNoYWRvd21hcCBleGFtcGxlXG5cdCAqXHRcdGh0dHA6Ly9ldmFudy5naXRodWIuY29tL2xpZ2h0Z2wuanMvdGVzdHMvc2hhZG93bWFwLmh0bWxcblx0ICovXG5cblx0ZnVuY3Rpb24gQ2FtZXJhSGVscGVyKCBjYW1lcmEgKSB7XG5cblx0XHR2YXIgZ2VvbWV0cnkgPSBuZXcgQnVmZmVyR2VvbWV0cnkoKTtcblx0XHR2YXIgbWF0ZXJpYWwgPSBuZXcgTGluZUJhc2ljTWF0ZXJpYWwoIHsgY29sb3I6IDB4ZmZmZmZmLCB2ZXJ0ZXhDb2xvcnM6IEZhY2VDb2xvcnMgfSApO1xuXG5cdFx0dmFyIHZlcnRpY2VzID0gW107XG5cdFx0dmFyIGNvbG9ycyA9IFtdO1xuXG5cdFx0dmFyIHBvaW50TWFwID0ge307XG5cblx0XHQvLyBjb2xvcnNcblxuXHRcdHZhciBjb2xvckZydXN0dW0gPSBuZXcgQ29sb3IoIDB4ZmZhYTAwICk7XG5cdFx0dmFyIGNvbG9yQ29uZSA9IG5ldyBDb2xvciggMHhmZjAwMDAgKTtcblx0XHR2YXIgY29sb3JVcCA9IG5ldyBDb2xvciggMHgwMGFhZmYgKTtcblx0XHR2YXIgY29sb3JUYXJnZXQgPSBuZXcgQ29sb3IoIDB4ZmZmZmZmICk7XG5cdFx0dmFyIGNvbG9yQ3Jvc3MgPSBuZXcgQ29sb3IoIDB4MzMzMzMzICk7XG5cblx0XHQvLyBuZWFyXG5cblx0XHRhZGRMaW5lKCBcIm4xXCIsIFwibjJcIiwgY29sb3JGcnVzdHVtICk7XG5cdFx0YWRkTGluZSggXCJuMlwiLCBcIm40XCIsIGNvbG9yRnJ1c3R1bSApO1xuXHRcdGFkZExpbmUoIFwibjRcIiwgXCJuM1wiLCBjb2xvckZydXN0dW0gKTtcblx0XHRhZGRMaW5lKCBcIm4zXCIsIFwibjFcIiwgY29sb3JGcnVzdHVtICk7XG5cblx0XHQvLyBmYXJcblxuXHRcdGFkZExpbmUoIFwiZjFcIiwgXCJmMlwiLCBjb2xvckZydXN0dW0gKTtcblx0XHRhZGRMaW5lKCBcImYyXCIsIFwiZjRcIiwgY29sb3JGcnVzdHVtICk7XG5cdFx0YWRkTGluZSggXCJmNFwiLCBcImYzXCIsIGNvbG9yRnJ1c3R1bSApO1xuXHRcdGFkZExpbmUoIFwiZjNcIiwgXCJmMVwiLCBjb2xvckZydXN0dW0gKTtcblxuXHRcdC8vIHNpZGVzXG5cblx0XHRhZGRMaW5lKCBcIm4xXCIsIFwiZjFcIiwgY29sb3JGcnVzdHVtICk7XG5cdFx0YWRkTGluZSggXCJuMlwiLCBcImYyXCIsIGNvbG9yRnJ1c3R1bSApO1xuXHRcdGFkZExpbmUoIFwibjNcIiwgXCJmM1wiLCBjb2xvckZydXN0dW0gKTtcblx0XHRhZGRMaW5lKCBcIm40XCIsIFwiZjRcIiwgY29sb3JGcnVzdHVtICk7XG5cblx0XHQvLyBjb25lXG5cblx0XHRhZGRMaW5lKCBcInBcIiwgXCJuMVwiLCBjb2xvckNvbmUgKTtcblx0XHRhZGRMaW5lKCBcInBcIiwgXCJuMlwiLCBjb2xvckNvbmUgKTtcblx0XHRhZGRMaW5lKCBcInBcIiwgXCJuM1wiLCBjb2xvckNvbmUgKTtcblx0XHRhZGRMaW5lKCBcInBcIiwgXCJuNFwiLCBjb2xvckNvbmUgKTtcblxuXHRcdC8vIHVwXG5cblx0XHRhZGRMaW5lKCBcInUxXCIsIFwidTJcIiwgY29sb3JVcCApO1xuXHRcdGFkZExpbmUoIFwidTJcIiwgXCJ1M1wiLCBjb2xvclVwICk7XG5cdFx0YWRkTGluZSggXCJ1M1wiLCBcInUxXCIsIGNvbG9yVXAgKTtcblxuXHRcdC8vIHRhcmdldFxuXG5cdFx0YWRkTGluZSggXCJjXCIsIFwidFwiLCBjb2xvclRhcmdldCApO1xuXHRcdGFkZExpbmUoIFwicFwiLCBcImNcIiwgY29sb3JDcm9zcyApO1xuXG5cdFx0Ly8gY3Jvc3NcblxuXHRcdGFkZExpbmUoIFwiY24xXCIsIFwiY24yXCIsIGNvbG9yQ3Jvc3MgKTtcblx0XHRhZGRMaW5lKCBcImNuM1wiLCBcImNuNFwiLCBjb2xvckNyb3NzICk7XG5cblx0XHRhZGRMaW5lKCBcImNmMVwiLCBcImNmMlwiLCBjb2xvckNyb3NzICk7XG5cdFx0YWRkTGluZSggXCJjZjNcIiwgXCJjZjRcIiwgY29sb3JDcm9zcyApO1xuXG5cdFx0ZnVuY3Rpb24gYWRkTGluZSggYSwgYiwgY29sb3IgKSB7XG5cblx0XHRcdGFkZFBvaW50KCBhLCBjb2xvciApO1xuXHRcdFx0YWRkUG9pbnQoIGIsIGNvbG9yICk7XG5cblx0XHR9XG5cblx0XHRmdW5jdGlvbiBhZGRQb2ludCggaWQsIGNvbG9yICkge1xuXG5cdFx0XHR2ZXJ0aWNlcy5wdXNoKCAwLCAwLCAwICk7XG5cdFx0XHRjb2xvcnMucHVzaCggY29sb3IuciwgY29sb3IuZywgY29sb3IuYiApO1xuXG5cdFx0XHRpZiAoIHBvaW50TWFwWyBpZCBdID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0cG9pbnRNYXBbIGlkIF0gPSBbXTtcblxuXHRcdFx0fVxuXG5cdFx0XHRwb2ludE1hcFsgaWQgXS5wdXNoKCAoIHZlcnRpY2VzLmxlbmd0aCAvIDMgKSAtIDEgKTtcblxuXHRcdH1cblxuXHRcdGdlb21ldHJ5LmFkZEF0dHJpYnV0ZSggJ3Bvc2l0aW9uJywgbmV3IEZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUoIHZlcnRpY2VzLCAzICkgKTtcblx0XHRnZW9tZXRyeS5hZGRBdHRyaWJ1dGUoICdjb2xvcicsIG5ldyBGbG9hdDMyQnVmZmVyQXR0cmlidXRlKCBjb2xvcnMsIDMgKSApO1xuXG5cdFx0TGluZVNlZ21lbnRzLmNhbGwoIHRoaXMsIGdlb21ldHJ5LCBtYXRlcmlhbCApO1xuXG5cdFx0dGhpcy5jYW1lcmEgPSBjYW1lcmE7XG5cdFx0aWYgKCB0aGlzLmNhbWVyYS51cGRhdGVQcm9qZWN0aW9uTWF0cml4ICkgdGhpcy5jYW1lcmEudXBkYXRlUHJvamVjdGlvbk1hdHJpeCgpO1xuXG5cdFx0dGhpcy5tYXRyaXggPSBjYW1lcmEubWF0cml4V29ybGQ7XG5cdFx0dGhpcy5tYXRyaXhBdXRvVXBkYXRlID0gZmFsc2U7XG5cblx0XHR0aGlzLnBvaW50TWFwID0gcG9pbnRNYXA7XG5cblx0XHR0aGlzLnVwZGF0ZSgpO1xuXG5cdH1cblxuXHRDYW1lcmFIZWxwZXIucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggTGluZVNlZ21lbnRzLnByb3RvdHlwZSApO1xuXHRDYW1lcmFIZWxwZXIucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gQ2FtZXJhSGVscGVyO1xuXG5cdENhbWVyYUhlbHBlci5wcm90b3R5cGUudXBkYXRlID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0dmFyIGdlb21ldHJ5LCBwb2ludE1hcDtcblxuXHRcdHZhciB2ZWN0b3IgPSBuZXcgVmVjdG9yMygpO1xuXHRcdHZhciBjYW1lcmEgPSBuZXcgQ2FtZXJhKCk7XG5cblx0XHRmdW5jdGlvbiBzZXRQb2ludCggcG9pbnQsIHgsIHksIHogKSB7XG5cblx0XHRcdHZlY3Rvci5zZXQoIHgsIHksIHogKS51bnByb2plY3QoIGNhbWVyYSApO1xuXG5cdFx0XHR2YXIgcG9pbnRzID0gcG9pbnRNYXBbIHBvaW50IF07XG5cblx0XHRcdGlmICggcG9pbnRzICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0dmFyIHBvc2l0aW9uID0gZ2VvbWV0cnkuZ2V0QXR0cmlidXRlKCAncG9zaXRpb24nICk7XG5cblx0XHRcdFx0Zm9yICggdmFyIGkgPSAwLCBsID0gcG9pbnRzLmxlbmd0aDsgaSA8IGw7IGkgKysgKSB7XG5cblx0XHRcdFx0XHRwb3NpdGlvbi5zZXRYWVooIHBvaW50c1sgaSBdLCB2ZWN0b3IueCwgdmVjdG9yLnksIHZlY3Rvci56ICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHRyZXR1cm4gZnVuY3Rpb24gdXBkYXRlKCkge1xuXG5cdFx0XHRnZW9tZXRyeSA9IHRoaXMuZ2VvbWV0cnk7XG5cdFx0XHRwb2ludE1hcCA9IHRoaXMucG9pbnRNYXA7XG5cblx0XHRcdHZhciB3ID0gMSwgaCA9IDE7XG5cblx0XHRcdC8vIHdlIG5lZWQganVzdCBjYW1lcmEgcHJvamVjdGlvbiBtYXRyaXhcblx0XHRcdC8vIHdvcmxkIG1hdHJpeCBtdXN0IGJlIGlkZW50aXR5XG5cblx0XHRcdGNhbWVyYS5wcm9qZWN0aW9uTWF0cml4LmNvcHkoIHRoaXMuY2FtZXJhLnByb2plY3Rpb25NYXRyaXggKTtcblxuXHRcdFx0Ly8gY2VudGVyIC8gdGFyZ2V0XG5cblx0XHRcdHNldFBvaW50KCBcImNcIiwgMCwgMCwgLSAxICk7XG5cdFx0XHRzZXRQb2ludCggXCJ0XCIsIDAsIDAsICAxICk7XG5cblx0XHRcdC8vIG5lYXJcblxuXHRcdFx0c2V0UG9pbnQoIFwibjFcIiwgLSB3LCAtIGgsIC0gMSApO1xuXHRcdFx0c2V0UG9pbnQoIFwibjJcIiwgICB3LCAtIGgsIC0gMSApO1xuXHRcdFx0c2V0UG9pbnQoIFwibjNcIiwgLSB3LCAgIGgsIC0gMSApO1xuXHRcdFx0c2V0UG9pbnQoIFwibjRcIiwgICB3LCAgIGgsIC0gMSApO1xuXG5cdFx0XHQvLyBmYXJcblxuXHRcdFx0c2V0UG9pbnQoIFwiZjFcIiwgLSB3LCAtIGgsIDEgKTtcblx0XHRcdHNldFBvaW50KCBcImYyXCIsICAgdywgLSBoLCAxICk7XG5cdFx0XHRzZXRQb2ludCggXCJmM1wiLCAtIHcsICAgaCwgMSApO1xuXHRcdFx0c2V0UG9pbnQoIFwiZjRcIiwgICB3LCAgIGgsIDEgKTtcblxuXHRcdFx0Ly8gdXBcblxuXHRcdFx0c2V0UG9pbnQoIFwidTFcIiwgICB3ICogMC43LCBoICogMS4xLCAtIDEgKTtcblx0XHRcdHNldFBvaW50KCBcInUyXCIsIC0gdyAqIDAuNywgaCAqIDEuMSwgLSAxICk7XG5cdFx0XHRzZXRQb2ludCggXCJ1M1wiLCAgICAgICAgIDAsIGggKiAyLCAgIC0gMSApO1xuXG5cdFx0XHQvLyBjcm9zc1xuXG5cdFx0XHRzZXRQb2ludCggXCJjZjFcIiwgLSB3LCAgIDAsIDEgKTtcblx0XHRcdHNldFBvaW50KCBcImNmMlwiLCAgIHcsICAgMCwgMSApO1xuXHRcdFx0c2V0UG9pbnQoIFwiY2YzXCIsICAgMCwgLSBoLCAxICk7XG5cdFx0XHRzZXRQb2ludCggXCJjZjRcIiwgICAwLCAgIGgsIDEgKTtcblxuXHRcdFx0c2V0UG9pbnQoIFwiY24xXCIsIC0gdywgICAwLCAtIDEgKTtcblx0XHRcdHNldFBvaW50KCBcImNuMlwiLCAgIHcsICAgMCwgLSAxICk7XG5cdFx0XHRzZXRQb2ludCggXCJjbjNcIiwgICAwLCAtIGgsIC0gMSApO1xuXHRcdFx0c2V0UG9pbnQoIFwiY240XCIsICAgMCwgICBoLCAtIDEgKTtcblxuXHRcdFx0Z2VvbWV0cnkuZ2V0QXR0cmlidXRlKCAncG9zaXRpb24nICkubmVlZHNVcGRhdGUgPSB0cnVlO1xuXG5cdFx0fTtcblxuXHR9KCk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEJveEhlbHBlciggb2JqZWN0LCBjb2xvciApIHtcblxuXHRcdGlmICggY29sb3IgPT09IHVuZGVmaW5lZCApIGNvbG9yID0gMHhmZmZmMDA7XG5cblx0XHR2YXIgaW5kaWNlcyA9IG5ldyBVaW50MTZBcnJheSggWyAwLCAxLCAxLCAyLCAyLCAzLCAzLCAwLCA0LCA1LCA1LCA2LCA2LCA3LCA3LCA0LCAwLCA0LCAxLCA1LCAyLCA2LCAzLCA3IF0gKTtcblx0XHR2YXIgcG9zaXRpb25zID0gbmV3IEZsb2F0MzJBcnJheSggOCAqIDMgKTtcblxuXHRcdHZhciBnZW9tZXRyeSA9IG5ldyBCdWZmZXJHZW9tZXRyeSgpO1xuXHRcdGdlb21ldHJ5LnNldEluZGV4KCBuZXcgQnVmZmVyQXR0cmlidXRlKCBpbmRpY2VzLCAxICkgKTtcblx0XHRnZW9tZXRyeS5hZGRBdHRyaWJ1dGUoICdwb3NpdGlvbicsIG5ldyBCdWZmZXJBdHRyaWJ1dGUoIHBvc2l0aW9ucywgMyApICk7XG5cblx0XHRMaW5lU2VnbWVudHMuY2FsbCggdGhpcywgZ2VvbWV0cnksIG5ldyBMaW5lQmFzaWNNYXRlcmlhbCggeyBjb2xvcjogY29sb3IgfSApICk7XG5cblx0XHRpZiAoIG9iamVjdCAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHR0aGlzLnVwZGF0ZSggb2JqZWN0ICk7XG5cblx0XHR9XG5cblx0fVxuXG5cdEJveEhlbHBlci5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBMaW5lU2VnbWVudHMucHJvdG90eXBlICk7XG5cdEJveEhlbHBlci5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBCb3hIZWxwZXI7XG5cblx0Qm94SGVscGVyLnByb3RvdHlwZS51cGRhdGUgPSAoIGZ1bmN0aW9uICgpIHtcblxuXHRcdHZhciBib3ggPSBuZXcgQm94MygpO1xuXG5cdFx0cmV0dXJuIGZ1bmN0aW9uIHVwZGF0ZSggb2JqZWN0ICkge1xuXG5cdFx0XHRpZiAoIG9iamVjdCAmJiBvYmplY3QuaXNCb3gzICkge1xuXG5cdFx0XHRcdGJveC5jb3B5KCBvYmplY3QgKTtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRib3guc2V0RnJvbU9iamVjdCggb2JqZWN0ICk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBib3guaXNFbXB0eSgpICkgcmV0dXJuO1xuXG5cdFx0XHR2YXIgbWluID0gYm94Lm1pbjtcblx0XHRcdHZhciBtYXggPSBib3gubWF4O1xuXG5cdFx0XHQvKlxuXHRcdFx0ICA1X19fXzRcblx0XHRcdDEvX19fMC98XG5cdFx0XHR8IDZfX3xfN1xuXHRcdFx0Mi9fX18zL1xuXG5cdFx0XHQwOiBtYXgueCwgbWF4LnksIG1heC56XG5cdFx0XHQxOiBtaW4ueCwgbWF4LnksIG1heC56XG5cdFx0XHQyOiBtaW4ueCwgbWluLnksIG1heC56XG5cdFx0XHQzOiBtYXgueCwgbWluLnksIG1heC56XG5cdFx0XHQ0OiBtYXgueCwgbWF4LnksIG1pbi56XG5cdFx0XHQ1OiBtaW4ueCwgbWF4LnksIG1pbi56XG5cdFx0XHQ2OiBtaW4ueCwgbWluLnksIG1pbi56XG5cdFx0XHQ3OiBtYXgueCwgbWluLnksIG1pbi56XG5cdFx0XHQqL1xuXG5cdFx0XHR2YXIgcG9zaXRpb24gPSB0aGlzLmdlb21ldHJ5LmF0dHJpYnV0ZXMucG9zaXRpb247XG5cdFx0XHR2YXIgYXJyYXkgPSBwb3NpdGlvbi5hcnJheTtcblxuXHRcdFx0YXJyYXlbICAwIF0gPSBtYXgueDsgYXJyYXlbICAxIF0gPSBtYXgueTsgYXJyYXlbICAyIF0gPSBtYXguejtcblx0XHRcdGFycmF5WyAgMyBdID0gbWluLng7IGFycmF5WyAgNCBdID0gbWF4Lnk7IGFycmF5WyAgNSBdID0gbWF4Lno7XG5cdFx0XHRhcnJheVsgIDYgXSA9IG1pbi54OyBhcnJheVsgIDcgXSA9IG1pbi55OyBhcnJheVsgIDggXSA9IG1heC56O1xuXHRcdFx0YXJyYXlbICA5IF0gPSBtYXgueDsgYXJyYXlbIDEwIF0gPSBtaW4ueTsgYXJyYXlbIDExIF0gPSBtYXguejtcblx0XHRcdGFycmF5WyAxMiBdID0gbWF4Lng7IGFycmF5WyAxMyBdID0gbWF4Lnk7IGFycmF5WyAxNCBdID0gbWluLno7XG5cdFx0XHRhcnJheVsgMTUgXSA9IG1pbi54OyBhcnJheVsgMTYgXSA9IG1heC55OyBhcnJheVsgMTcgXSA9IG1pbi56O1xuXHRcdFx0YXJyYXlbIDE4IF0gPSBtaW4ueDsgYXJyYXlbIDE5IF0gPSBtaW4ueTsgYXJyYXlbIDIwIF0gPSBtaW4uejtcblx0XHRcdGFycmF5WyAyMSBdID0gbWF4Lng7IGFycmF5WyAyMiBdID0gbWluLnk7IGFycmF5WyAyMyBdID0gbWluLno7XG5cblx0XHRcdHBvc2l0aW9uLm5lZWRzVXBkYXRlID0gdHJ1ZTtcblxuXHRcdFx0dGhpcy5nZW9tZXRyeS5jb21wdXRlQm91bmRpbmdTcGhlcmUoKTtcblxuXHRcdH07XG5cblx0fSApKCk7XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgV2VzdExhbmdsZXkgLyBodHRwOi8vZ2l0aHViLmNvbS9XZXN0TGFuZ2xleVxuXHQgKiBAYXV0aG9yIHp6ODUgLyBodHRwOi8vZ2l0aHViLmNvbS96ejg1XG5cdCAqIEBhdXRob3IgYmhvdXN0b24gLyBodHRwOi8vY2xhcmEuaW9cblx0ICpcblx0ICogQ3JlYXRlcyBhbiBhcnJvdyBmb3IgdmlzdWFsaXppbmcgZGlyZWN0aW9uc1xuXHQgKlxuXHQgKiBQYXJhbWV0ZXJzOlxuXHQgKiAgZGlyIC0gVmVjdG9yM1xuXHQgKiAgb3JpZ2luIC0gVmVjdG9yM1xuXHQgKiAgbGVuZ3RoIC0gTnVtYmVyXG5cdCAqICBjb2xvciAtIGNvbG9yIGluIGhleCB2YWx1ZVxuXHQgKiAgaGVhZExlbmd0aCAtIE51bWJlclxuXHQgKiAgaGVhZFdpZHRoIC0gTnVtYmVyXG5cdCAqL1xuXG5cdHZhciBsaW5lR2VvbWV0cnkgPSBuZXcgQnVmZmVyR2VvbWV0cnkoKTtcblx0bGluZUdlb21ldHJ5LmFkZEF0dHJpYnV0ZSggJ3Bvc2l0aW9uJywgbmV3IEZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUoIFsgMCwgMCwgMCwgMCwgMSwgMCBdLCAzICkgKTtcblxuXHR2YXIgY29uZUdlb21ldHJ5ID0gbmV3IEN5bGluZGVyQnVmZmVyR2VvbWV0cnkoIDAsIDAuNSwgMSwgNSwgMSApO1xuXHRjb25lR2VvbWV0cnkudHJhbnNsYXRlKCAwLCAtIDAuNSwgMCApO1xuXG5cdGZ1bmN0aW9uIEFycm93SGVscGVyKCBkaXIsIG9yaWdpbiwgbGVuZ3RoLCBjb2xvciwgaGVhZExlbmd0aCwgaGVhZFdpZHRoICkge1xuXG5cdFx0Ly8gZGlyIGlzIGFzc3VtZWQgdG8gYmUgbm9ybWFsaXplZFxuXG5cdFx0T2JqZWN0M0QuY2FsbCggdGhpcyApO1xuXG5cdFx0aWYgKCBjb2xvciA9PT0gdW5kZWZpbmVkICkgY29sb3IgPSAweGZmZmYwMDtcblx0XHRpZiAoIGxlbmd0aCA9PT0gdW5kZWZpbmVkICkgbGVuZ3RoID0gMTtcblx0XHRpZiAoIGhlYWRMZW5ndGggPT09IHVuZGVmaW5lZCApIGhlYWRMZW5ndGggPSAwLjIgKiBsZW5ndGg7XG5cdFx0aWYgKCBoZWFkV2lkdGggPT09IHVuZGVmaW5lZCApIGhlYWRXaWR0aCA9IDAuMiAqIGhlYWRMZW5ndGg7XG5cblx0XHR0aGlzLnBvc2l0aW9uLmNvcHkoIG9yaWdpbiApO1xuXG5cdFx0dGhpcy5saW5lID0gbmV3IExpbmUoIGxpbmVHZW9tZXRyeSwgbmV3IExpbmVCYXNpY01hdGVyaWFsKCB7IGNvbG9yOiBjb2xvciB9ICkgKTtcblx0XHR0aGlzLmxpbmUubWF0cml4QXV0b1VwZGF0ZSA9IGZhbHNlO1xuXHRcdHRoaXMuYWRkKCB0aGlzLmxpbmUgKTtcblxuXHRcdHRoaXMuY29uZSA9IG5ldyBNZXNoKCBjb25lR2VvbWV0cnksIG5ldyBNZXNoQmFzaWNNYXRlcmlhbCggeyBjb2xvcjogY29sb3IgfSApICk7XG5cdFx0dGhpcy5jb25lLm1hdHJpeEF1dG9VcGRhdGUgPSBmYWxzZTtcblx0XHR0aGlzLmFkZCggdGhpcy5jb25lICk7XG5cblx0XHR0aGlzLnNldERpcmVjdGlvbiggZGlyICk7XG5cdFx0dGhpcy5zZXRMZW5ndGgoIGxlbmd0aCwgaGVhZExlbmd0aCwgaGVhZFdpZHRoICk7XG5cblx0fVxuXG5cdEFycm93SGVscGVyLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIE9iamVjdDNELnByb3RvdHlwZSApO1xuXHRBcnJvd0hlbHBlci5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBBcnJvd0hlbHBlcjtcblxuXHRBcnJvd0hlbHBlci5wcm90b3R5cGUuc2V0RGlyZWN0aW9uID0gKCBmdW5jdGlvbiAoKSB7XG5cblx0XHR2YXIgYXhpcyA9IG5ldyBWZWN0b3IzKCk7XG5cdFx0dmFyIHJhZGlhbnM7XG5cblx0XHRyZXR1cm4gZnVuY3Rpb24gc2V0RGlyZWN0aW9uKCBkaXIgKSB7XG5cblx0XHRcdC8vIGRpciBpcyBhc3N1bWVkIHRvIGJlIG5vcm1hbGl6ZWRcblxuXHRcdFx0aWYgKCBkaXIueSA+IDAuOTk5OTkgKSB7XG5cblx0XHRcdFx0dGhpcy5xdWF0ZXJuaW9uLnNldCggMCwgMCwgMCwgMSApO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCBkaXIueSA8IC0gMC45OTk5OSApIHtcblxuXHRcdFx0XHR0aGlzLnF1YXRlcm5pb24uc2V0KCAxLCAwLCAwLCAwICk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0YXhpcy5zZXQoIGRpci56LCAwLCAtIGRpci54ICkubm9ybWFsaXplKCk7XG5cblx0XHRcdFx0cmFkaWFucyA9IE1hdGguYWNvcyggZGlyLnkgKTtcblxuXHRcdFx0XHR0aGlzLnF1YXRlcm5pb24uc2V0RnJvbUF4aXNBbmdsZSggYXhpcywgcmFkaWFucyApO1xuXG5cdFx0XHR9XG5cblx0XHR9O1xuXG5cdH0oKSApO1xuXG5cdEFycm93SGVscGVyLnByb3RvdHlwZS5zZXRMZW5ndGggPSBmdW5jdGlvbiAoIGxlbmd0aCwgaGVhZExlbmd0aCwgaGVhZFdpZHRoICkge1xuXG5cdFx0aWYgKCBoZWFkTGVuZ3RoID09PSB1bmRlZmluZWQgKSBoZWFkTGVuZ3RoID0gMC4yICogbGVuZ3RoO1xuXHRcdGlmICggaGVhZFdpZHRoID09PSB1bmRlZmluZWQgKSBoZWFkV2lkdGggPSAwLjIgKiBoZWFkTGVuZ3RoO1xuXG5cdFx0dGhpcy5saW5lLnNjYWxlLnNldCggMSwgTWF0aC5tYXgoIDAsIGxlbmd0aCAtIGhlYWRMZW5ndGggKSwgMSApO1xuXHRcdHRoaXMubGluZS51cGRhdGVNYXRyaXgoKTtcblxuXHRcdHRoaXMuY29uZS5zY2FsZS5zZXQoIGhlYWRXaWR0aCwgaGVhZExlbmd0aCwgaGVhZFdpZHRoICk7XG5cdFx0dGhpcy5jb25lLnBvc2l0aW9uLnkgPSBsZW5ndGg7XG5cdFx0dGhpcy5jb25lLnVwZGF0ZU1hdHJpeCgpO1xuXG5cdH07XG5cblx0QXJyb3dIZWxwZXIucHJvdG90eXBlLnNldENvbG9yID0gZnVuY3Rpb24gKCBjb2xvciApIHtcblxuXHRcdHRoaXMubGluZS5tYXRlcmlhbC5jb2xvci5jb3B5KCBjb2xvciApO1xuXHRcdHRoaXMuY29uZS5tYXRlcmlhbC5jb2xvci5jb3B5KCBjb2xvciApO1xuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3Igc3JvdWNoZXJheSAvIGh0dHA6Ly9zcm91Y2hlcmF5Lm9yZy9cblx0ICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbS9cblx0ICovXG5cblx0ZnVuY3Rpb24gQXhpc0hlbHBlciggc2l6ZSApIHtcblxuXHRcdHNpemUgPSBzaXplIHx8IDE7XG5cblx0XHR2YXIgdmVydGljZXMgPSBbXG5cdFx0XHQwLCAwLCAwLCAgc2l6ZSwgMCwgMCxcblx0XHRcdDAsIDAsIDAsICAwLCBzaXplLCAwLFxuXHRcdFx0MCwgMCwgMCwgIDAsIDAsIHNpemVcblx0XHRdO1xuXG5cdFx0dmFyIGNvbG9ycyA9IFtcblx0XHRcdDEsIDAsIDAsICAxLCAwLjYsIDAsXG5cdFx0XHQwLCAxLCAwLCAgMC42LCAxLCAwLFxuXHRcdFx0MCwgMCwgMSwgIDAsIDAuNiwgMVxuXHRcdF07XG5cblx0XHR2YXIgZ2VvbWV0cnkgPSBuZXcgQnVmZmVyR2VvbWV0cnkoKTtcblx0XHRnZW9tZXRyeS5hZGRBdHRyaWJ1dGUoICdwb3NpdGlvbicsIG5ldyBGbG9hdDMyQnVmZmVyQXR0cmlidXRlKCB2ZXJ0aWNlcywgMyApICk7XG5cdFx0Z2VvbWV0cnkuYWRkQXR0cmlidXRlKCAnY29sb3InLCBuZXcgRmxvYXQzMkJ1ZmZlckF0dHJpYnV0ZSggY29sb3JzLCAzICkgKTtcblxuXHRcdHZhciBtYXRlcmlhbCA9IG5ldyBMaW5lQmFzaWNNYXRlcmlhbCggeyB2ZXJ0ZXhDb2xvcnM6IFZlcnRleENvbG9ycyB9ICk7XG5cblx0XHRMaW5lU2VnbWVudHMuY2FsbCggdGhpcywgZ2VvbWV0cnksIG1hdGVyaWFsICk7XG5cblx0fVxuXG5cdEF4aXNIZWxwZXIucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggTGluZVNlZ21lbnRzLnByb3RvdHlwZSApO1xuXHRBeGlzSGVscGVyLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IEF4aXNIZWxwZXI7XG5cblx0LyoqXG5cdCAqIEBhdXRob3Igeno4NSBodHRwczovL2dpdGh1Yi5jb20veno4NVxuXHQgKlxuXHQgKiBDZW50cmlwZXRhbCBDYXRtdWxsUm9tIEN1cnZlIC0gd2hpY2ggaXMgdXNlZnVsIGZvciBhdm9pZGluZ1xuXHQgKiBjdXNwcyBhbmQgc2VsZi1pbnRlcnNlY3Rpb25zIGluIG5vbi11bmlmb3JtIGNhdG11bGwgcm9tIGN1cnZlcy5cblx0ICogaHR0cDovL3d3dy5jZW15dWtzZWwuY29tL3Jlc2VhcmNoL2NhdG11bGxyb21fcGFyYW0vY2F0bXVsbHJvbS5wZGZcblx0ICpcblx0ICogY3VydmUudHlwZSBhY2NlcHRzIGNlbnRyaXBldGFsKGRlZmF1bHQpLCBjaG9yZGFsIGFuZCBjYXRtdWxscm9tXG5cdCAqIGN1cnZlLnRlbnNpb24gaXMgdXNlZCBmb3IgY2F0bXVsbHJvbSB3aGljaCBkZWZhdWx0cyB0byAwLjVcblx0ICovXG5cblx0dmFyIENhdG11bGxSb21DdXJ2ZTMgPSAoIGZ1bmN0aW9uKCkge1xuXG5cdFx0dmFyXG5cdFx0XHR0bXAgPSBuZXcgVmVjdG9yMygpLFxuXHRcdFx0cHggPSBuZXcgQ3ViaWNQb2x5KCksXG5cdFx0XHRweSA9IG5ldyBDdWJpY1BvbHkoKSxcblx0XHRcdHB6ID0gbmV3IEN1YmljUG9seSgpO1xuXG5cdFx0Lypcblx0XHRCYXNlZCBvbiBhbiBvcHRpbWl6ZWQgYysrIHNvbHV0aW9uIGluXG5cdFx0IC0gaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy85NDg5NzM2L2NhdG11bGwtcm9tLWN1cnZlLXdpdGgtbm8tY3VzcHMtYW5kLW5vLXNlbGYtaW50ZXJzZWN0aW9ucy9cblx0XHQgLSBodHRwOi8vaWRlb25lLmNvbS9Ob0ViVk1cblxuXHRcdFRoaXMgQ3ViaWNQb2x5IGNsYXNzIGNvdWxkIGJlIHVzZWQgZm9yIHJldXNpbmcgc29tZSB2YXJpYWJsZXMgYW5kIGNhbGN1bGF0aW9ucyxcblx0XHRidXQgZm9yIHRocmVlLmpzIGN1cnZlIHVzZSwgaXQgY291bGQgYmUgcG9zc2libGUgaW5saW5lZCBhbmQgZmxhdHRlbiBpbnRvIGEgc2luZ2xlIGZ1bmN0aW9uIGNhbGxcblx0XHR3aGljaCBjYW4gYmUgcGxhY2VkIGluIEN1cnZlVXRpbHMuXG5cdFx0Ki9cblxuXHRcdGZ1bmN0aW9uIEN1YmljUG9seSgpIHt9XG5cblx0XHQvKlxuXHRcdCAqIENvbXB1dGUgY29lZmZpY2llbnRzIGZvciBhIGN1YmljIHBvbHlub21pYWxcblx0XHQgKiAgIHAocykgPSBjMCArIGMxKnMgKyBjMipzXjIgKyBjMypzXjNcblx0XHQgKiBzdWNoIHRoYXRcblx0XHQgKiAgIHAoMCkgPSB4MCwgcCgxKSA9IHgxXG5cdFx0ICogIGFuZFxuXHRcdCAqICAgcCcoMCkgPSB0MCwgcCcoMSkgPSB0MS5cblx0XHQgKi9cblx0XHRDdWJpY1BvbHkucHJvdG90eXBlLmluaXQgPSBmdW5jdGlvbiggeDAsIHgxLCB0MCwgdDEgKSB7XG5cblx0XHRcdHRoaXMuYzAgPSB4MDtcblx0XHRcdHRoaXMuYzEgPSB0MDtcblx0XHRcdHRoaXMuYzIgPSAtIDMgKiB4MCArIDMgKiB4MSAtIDIgKiB0MCAtIHQxO1xuXHRcdFx0dGhpcy5jMyA9IDIgKiB4MCAtIDIgKiB4MSArIHQwICsgdDE7XG5cblx0XHR9O1xuXG5cdFx0Q3ViaWNQb2x5LnByb3RvdHlwZS5pbml0Tm9udW5pZm9ybUNhdG11bGxSb20gPSBmdW5jdGlvbiggeDAsIHgxLCB4MiwgeDMsIGR0MCwgZHQxLCBkdDIgKSB7XG5cblx0XHRcdC8vIGNvbXB1dGUgdGFuZ2VudHMgd2hlbiBwYXJhbWV0ZXJpemVkIGluIFt0MSx0Ml1cblx0XHRcdHZhciB0MSA9ICggeDEgLSB4MCApIC8gZHQwIC0gKCB4MiAtIHgwICkgLyAoIGR0MCArIGR0MSApICsgKCB4MiAtIHgxICkgLyBkdDE7XG5cdFx0XHR2YXIgdDIgPSAoIHgyIC0geDEgKSAvIGR0MSAtICggeDMgLSB4MSApIC8gKCBkdDEgKyBkdDIgKSArICggeDMgLSB4MiApIC8gZHQyO1xuXG5cdFx0XHQvLyByZXNjYWxlIHRhbmdlbnRzIGZvciBwYXJhbWV0cml6YXRpb24gaW4gWzAsMV1cblx0XHRcdHQxICo9IGR0MTtcblx0XHRcdHQyICo9IGR0MTtcblxuXHRcdFx0Ly8gaW5pdEN1YmljUG9seVxuXHRcdFx0dGhpcy5pbml0KCB4MSwgeDIsIHQxLCB0MiApO1xuXG5cdFx0fTtcblxuXHRcdC8vIHN0YW5kYXJkIENhdG11bGwtUm9tIHNwbGluZTogaW50ZXJwb2xhdGUgYmV0d2VlbiB4MSBhbmQgeDIgd2l0aCBwcmV2aW91cy9mb2xsb3dpbmcgcG9pbnRzIHgxL3g0XG5cdFx0Q3ViaWNQb2x5LnByb3RvdHlwZS5pbml0Q2F0bXVsbFJvbSA9IGZ1bmN0aW9uKCB4MCwgeDEsIHgyLCB4MywgdGVuc2lvbiApIHtcblxuXHRcdFx0dGhpcy5pbml0KCB4MSwgeDIsIHRlbnNpb24gKiAoIHgyIC0geDAgKSwgdGVuc2lvbiAqICggeDMgLSB4MSApICk7XG5cblx0XHR9O1xuXG5cdFx0Q3ViaWNQb2x5LnByb3RvdHlwZS5jYWxjID0gZnVuY3Rpb24oIHQgKSB7XG5cblx0XHRcdHZhciB0MiA9IHQgKiB0O1xuXHRcdFx0dmFyIHQzID0gdDIgKiB0O1xuXHRcdFx0cmV0dXJuIHRoaXMuYzAgKyB0aGlzLmMxICogdCArIHRoaXMuYzIgKiB0MiArIHRoaXMuYzMgKiB0MztcblxuXHRcdH07XG5cblx0XHQvLyBTdWJjbGFzcyBUaHJlZS5qcyBjdXJ2ZVxuXHRcdHJldHVybiBDdXJ2ZS5jcmVhdGUoXG5cblx0XHRcdGZ1bmN0aW9uICggcCAvKiBhcnJheSBvZiBWZWN0b3IzICovICkge1xuXG5cdFx0XHRcdHRoaXMucG9pbnRzID0gcCB8fCBbXTtcblx0XHRcdFx0dGhpcy5jbG9zZWQgPSBmYWxzZTtcblxuXHRcdFx0fSxcblxuXHRcdFx0ZnVuY3Rpb24gKCB0ICkge1xuXG5cdFx0XHRcdHZhciBwb2ludHMgPSB0aGlzLnBvaW50cyxcblx0XHRcdFx0XHRwb2ludCwgaW50UG9pbnQsIHdlaWdodCwgbDtcblxuXHRcdFx0XHRsID0gcG9pbnRzLmxlbmd0aDtcblxuXHRcdFx0XHRpZiAoIGwgPCAyICkgY29uc29sZS5sb2coICdkdWgsIHlvdSBuZWVkIGF0IGxlYXN0IDIgcG9pbnRzJyApO1xuXG5cdFx0XHRcdHBvaW50ID0gKCBsIC0gKCB0aGlzLmNsb3NlZCA/IDAgOiAxICkgKSAqIHQ7XG5cdFx0XHRcdGludFBvaW50ID0gTWF0aC5mbG9vciggcG9pbnQgKTtcblx0XHRcdFx0d2VpZ2h0ID0gcG9pbnQgLSBpbnRQb2ludDtcblxuXHRcdFx0XHRpZiAoIHRoaXMuY2xvc2VkICkge1xuXG5cdFx0XHRcdFx0aW50UG9pbnQgKz0gaW50UG9pbnQgPiAwID8gMCA6ICggTWF0aC5mbG9vciggTWF0aC5hYnMoIGludFBvaW50ICkgLyBwb2ludHMubGVuZ3RoICkgKyAxICkgKiBwb2ludHMubGVuZ3RoO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAoIHdlaWdodCA9PT0gMCAmJiBpbnRQb2ludCA9PT0gbCAtIDEgKSB7XG5cblx0XHRcdFx0XHRpbnRQb2ludCA9IGwgLSAyO1xuXHRcdFx0XHRcdHdlaWdodCA9IDE7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBwMCwgcDEsIHAyLCBwMzsgLy8gNCBwb2ludHNcblxuXHRcdFx0XHRpZiAoIHRoaXMuY2xvc2VkIHx8IGludFBvaW50ID4gMCApIHtcblxuXHRcdFx0XHRcdHAwID0gcG9pbnRzWyAoIGludFBvaW50IC0gMSApICUgbCBdO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0XHQvLyBleHRyYXBvbGF0ZSBmaXJzdCBwb2ludFxuXHRcdFx0XHRcdHRtcC5zdWJWZWN0b3JzKCBwb2ludHNbIDAgXSwgcG9pbnRzWyAxIF0gKS5hZGQoIHBvaW50c1sgMCBdICk7XG5cdFx0XHRcdFx0cDAgPSB0bXA7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHAxID0gcG9pbnRzWyBpbnRQb2ludCAlIGwgXTtcblx0XHRcdFx0cDIgPSBwb2ludHNbICggaW50UG9pbnQgKyAxICkgJSBsIF07XG5cblx0XHRcdFx0aWYgKCB0aGlzLmNsb3NlZCB8fCBpbnRQb2ludCArIDIgPCBsICkge1xuXG5cdFx0XHRcdFx0cDMgPSBwb2ludHNbICggaW50UG9pbnQgKyAyICkgJSBsIF07XG5cblx0XHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRcdC8vIGV4dHJhcG9sYXRlIGxhc3QgcG9pbnRcblx0XHRcdFx0XHR0bXAuc3ViVmVjdG9ycyggcG9pbnRzWyBsIC0gMSBdLCBwb2ludHNbIGwgLSAyIF0gKS5hZGQoIHBvaW50c1sgbCAtIDEgXSApO1xuXHRcdFx0XHRcdHAzID0gdG1wO1xuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoIHRoaXMudHlwZSA9PT0gdW5kZWZpbmVkIHx8IHRoaXMudHlwZSA9PT0gJ2NlbnRyaXBldGFsJyB8fCB0aGlzLnR5cGUgPT09ICdjaG9yZGFsJyApIHtcblxuXHRcdFx0XHRcdC8vIGluaXQgQ2VudHJpcGV0YWwgLyBDaG9yZGFsIENhdG11bGwtUm9tXG5cdFx0XHRcdFx0dmFyIHBvdyA9IHRoaXMudHlwZSA9PT0gJ2Nob3JkYWwnID8gMC41IDogMC4yNTtcblx0XHRcdFx0XHR2YXIgZHQwID0gTWF0aC5wb3coIHAwLmRpc3RhbmNlVG9TcXVhcmVkKCBwMSApLCBwb3cgKTtcblx0XHRcdFx0XHR2YXIgZHQxID0gTWF0aC5wb3coIHAxLmRpc3RhbmNlVG9TcXVhcmVkKCBwMiApLCBwb3cgKTtcblx0XHRcdFx0XHR2YXIgZHQyID0gTWF0aC5wb3coIHAyLmRpc3RhbmNlVG9TcXVhcmVkKCBwMyApLCBwb3cgKTtcblxuXHRcdFx0XHRcdC8vIHNhZmV0eSBjaGVjayBmb3IgcmVwZWF0ZWQgcG9pbnRzXG5cdFx0XHRcdFx0aWYgKCBkdDEgPCAxZS00ICkgZHQxID0gMS4wO1xuXHRcdFx0XHRcdGlmICggZHQwIDwgMWUtNCApIGR0MCA9IGR0MTtcblx0XHRcdFx0XHRpZiAoIGR0MiA8IDFlLTQgKSBkdDIgPSBkdDE7XG5cblx0XHRcdFx0XHRweC5pbml0Tm9udW5pZm9ybUNhdG11bGxSb20oIHAwLngsIHAxLngsIHAyLngsIHAzLngsIGR0MCwgZHQxLCBkdDIgKTtcblx0XHRcdFx0XHRweS5pbml0Tm9udW5pZm9ybUNhdG11bGxSb20oIHAwLnksIHAxLnksIHAyLnksIHAzLnksIGR0MCwgZHQxLCBkdDIgKTtcblx0XHRcdFx0XHRwei5pbml0Tm9udW5pZm9ybUNhdG11bGxSb20oIHAwLnosIHAxLnosIHAyLnosIHAzLnosIGR0MCwgZHQxLCBkdDIgKTtcblxuXHRcdFx0XHR9IGVsc2UgaWYgKCB0aGlzLnR5cGUgPT09ICdjYXRtdWxscm9tJyApIHtcblxuXHRcdFx0XHRcdHZhciB0ZW5zaW9uID0gdGhpcy50ZW5zaW9uICE9PSB1bmRlZmluZWQgPyB0aGlzLnRlbnNpb24gOiAwLjU7XG5cdFx0XHRcdFx0cHguaW5pdENhdG11bGxSb20oIHAwLngsIHAxLngsIHAyLngsIHAzLngsIHRlbnNpb24gKTtcblx0XHRcdFx0XHRweS5pbml0Q2F0bXVsbFJvbSggcDAueSwgcDEueSwgcDIueSwgcDMueSwgdGVuc2lvbiApO1xuXHRcdFx0XHRcdHB6LmluaXRDYXRtdWxsUm9tKCBwMC56LCBwMS56LCBwMi56LCBwMy56LCB0ZW5zaW9uICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciB2ID0gbmV3IFZlY3RvcjMoXG5cdFx0XHRcdFx0cHguY2FsYyggd2VpZ2h0ICksXG5cdFx0XHRcdFx0cHkuY2FsYyggd2VpZ2h0ICksXG5cdFx0XHRcdFx0cHouY2FsYyggd2VpZ2h0IClcblx0XHRcdFx0KTtcblxuXHRcdFx0XHRyZXR1cm4gdjtcblxuXHRcdFx0fVxuXG5cdFx0KTtcblxuXHR9ICkoKTtcblxuXHQvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblx0ICpcdFNwbGluZSAzRCBjdXJ2ZVxuXHQgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cblxuXHR2YXIgU3BsaW5lQ3VydmUzID0gQ3VydmUuY3JlYXRlKFxuXG5cdFx0ZnVuY3Rpb24gKCBwb2ludHMgLyogYXJyYXkgb2YgVmVjdG9yMyAqLyApIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuU3BsaW5lQ3VydmUzIHdpbGwgYmUgZGVwcmVjYXRlZC4gUGxlYXNlIHVzZSBUSFJFRS5DYXRtdWxsUm9tQ3VydmUzJyApO1xuXHRcdFx0dGhpcy5wb2ludHMgPSAoIHBvaW50cyA9PT0gdW5kZWZpbmVkICkgPyBbXSA6IHBvaW50cztcblxuXHRcdH0sXG5cblx0XHRmdW5jdGlvbiAoIHQgKSB7XG5cblx0XHRcdHZhciBwb2ludHMgPSB0aGlzLnBvaW50cztcblx0XHRcdHZhciBwb2ludCA9ICggcG9pbnRzLmxlbmd0aCAtIDEgKSAqIHQ7XG5cblx0XHRcdHZhciBpbnRQb2ludCA9IE1hdGguZmxvb3IoIHBvaW50ICk7XG5cdFx0XHR2YXIgd2VpZ2h0ID0gcG9pbnQgLSBpbnRQb2ludDtcblxuXHRcdFx0dmFyIHBvaW50MCA9IHBvaW50c1sgaW50UG9pbnQgPT0gMCA/IGludFBvaW50IDogaW50UG9pbnQgLSAxIF07XG5cdFx0XHR2YXIgcG9pbnQxID0gcG9pbnRzWyBpbnRQb2ludCBdO1xuXHRcdFx0dmFyIHBvaW50MiA9IHBvaW50c1sgaW50UG9pbnQgPiBwb2ludHMubGVuZ3RoIC0gMiA/IHBvaW50cy5sZW5ndGggLSAxIDogaW50UG9pbnQgKyAxIF07XG5cdFx0XHR2YXIgcG9pbnQzID0gcG9pbnRzWyBpbnRQb2ludCA+IHBvaW50cy5sZW5ndGggLSAzID8gcG9pbnRzLmxlbmd0aCAtIDEgOiBpbnRQb2ludCArIDIgXTtcblxuXHRcdFx0dmFyIGludGVycG9sYXRlID0gQ3VydmVVdGlscy5pbnRlcnBvbGF0ZTtcblxuXHRcdFx0cmV0dXJuIG5ldyBWZWN0b3IzKFxuXHRcdFx0XHRpbnRlcnBvbGF0ZSggcG9pbnQwLngsIHBvaW50MS54LCBwb2ludDIueCwgcG9pbnQzLngsIHdlaWdodCApLFxuXHRcdFx0XHRpbnRlcnBvbGF0ZSggcG9pbnQwLnksIHBvaW50MS55LCBwb2ludDIueSwgcG9pbnQzLnksIHdlaWdodCApLFxuXHRcdFx0XHRpbnRlcnBvbGF0ZSggcG9pbnQwLnosIHBvaW50MS56LCBwb2ludDIueiwgcG9pbnQzLnosIHdlaWdodCApXG5cdFx0XHQpO1xuXG5cdFx0fVxuXG5cdCk7XG5cblx0LyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cdCAqXHRDdWJpYyBCZXppZXIgM0QgY3VydmVcblx0ICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5cdHZhciBDdWJpY0JlemllckN1cnZlMyA9IEN1cnZlLmNyZWF0ZShcblxuXHRcdGZ1bmN0aW9uICggdjAsIHYxLCB2MiwgdjMgKSB7XG5cblx0XHRcdHRoaXMudjAgPSB2MDtcblx0XHRcdHRoaXMudjEgPSB2MTtcblx0XHRcdHRoaXMudjIgPSB2Mjtcblx0XHRcdHRoaXMudjMgPSB2MztcblxuXHRcdH0sXG5cblx0XHRmdW5jdGlvbiAoIHQgKSB7XG5cblx0XHRcdHZhciBiMyA9IFNoYXBlVXRpbHMuYjM7XG5cblx0XHRcdHJldHVybiBuZXcgVmVjdG9yMyhcblx0XHRcdFx0YjMoIHQsIHRoaXMudjAueCwgdGhpcy52MS54LCB0aGlzLnYyLngsIHRoaXMudjMueCApLFxuXHRcdFx0XHRiMyggdCwgdGhpcy52MC55LCB0aGlzLnYxLnksIHRoaXMudjIueSwgdGhpcy52My55ICksXG5cdFx0XHRcdGIzKCB0LCB0aGlzLnYwLnosIHRoaXMudjEueiwgdGhpcy52Mi56LCB0aGlzLnYzLnogKVxuXHRcdFx0KTtcblxuXHRcdH1cblxuXHQpO1xuXG5cdC8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuXHQgKlx0UXVhZHJhdGljIEJlemllciAzRCBjdXJ2ZVxuXHQgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cblx0dmFyIFF1YWRyYXRpY0JlemllckN1cnZlMyA9IEN1cnZlLmNyZWF0ZShcblxuXHRcdGZ1bmN0aW9uICggdjAsIHYxLCB2MiApIHtcblxuXHRcdFx0dGhpcy52MCA9IHYwO1xuXHRcdFx0dGhpcy52MSA9IHYxO1xuXHRcdFx0dGhpcy52MiA9IHYyO1xuXG5cdFx0fSxcblxuXHRcdGZ1bmN0aW9uICggdCApIHtcblxuXHRcdFx0dmFyIGIyID0gU2hhcGVVdGlscy5iMjtcblxuXHRcdFx0cmV0dXJuIG5ldyBWZWN0b3IzKFxuXHRcdFx0XHRiMiggdCwgdGhpcy52MC54LCB0aGlzLnYxLngsIHRoaXMudjIueCApLFxuXHRcdFx0XHRiMiggdCwgdGhpcy52MC55LCB0aGlzLnYxLnksIHRoaXMudjIueSApLFxuXHRcdFx0XHRiMiggdCwgdGhpcy52MC56LCB0aGlzLnYxLnosIHRoaXMudjIueiApXG5cdFx0XHQpO1xuXG5cdFx0fVxuXG5cdCk7XG5cblx0LyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cdCAqXHRMaW5lM0Rcblx0ICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5cdHZhciBMaW5lQ3VydmUzID0gQ3VydmUuY3JlYXRlKFxuXG5cdFx0ZnVuY3Rpb24gKCB2MSwgdjIgKSB7XG5cblx0XHRcdHRoaXMudjEgPSB2MTtcblx0XHRcdHRoaXMudjIgPSB2MjtcblxuXHRcdH0sXG5cblx0XHRmdW5jdGlvbiAoIHQgKSB7XG5cblx0XHRcdGlmICggdCA9PT0gMSApIHtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcy52Mi5jbG9uZSgpO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciB2ZWN0b3IgPSBuZXcgVmVjdG9yMygpO1xuXG5cdFx0XHR2ZWN0b3Iuc3ViVmVjdG9ycyggdGhpcy52MiwgdGhpcy52MSApOyAvLyBkaWZmXG5cdFx0XHR2ZWN0b3IubXVsdGlwbHlTY2FsYXIoIHQgKTtcblx0XHRcdHZlY3Rvci5hZGQoIHRoaXMudjEgKTtcblxuXHRcdFx0cmV0dXJuIHZlY3RvcjtcblxuXHRcdH1cblxuXHQpO1xuXG5cdC8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuXHQgKlx0QXJjIGN1cnZlXG5cdCAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cblxuXHRmdW5jdGlvbiBBcmNDdXJ2ZSggYVgsIGFZLCBhUmFkaXVzLCBhU3RhcnRBbmdsZSwgYUVuZEFuZ2xlLCBhQ2xvY2t3aXNlICkge1xuXG5cdFx0RWxsaXBzZUN1cnZlLmNhbGwoIHRoaXMsIGFYLCBhWSwgYVJhZGl1cywgYVJhZGl1cywgYVN0YXJ0QW5nbGUsIGFFbmRBbmdsZSwgYUNsb2Nrd2lzZSApO1xuXG5cdH1cblxuXHRBcmNDdXJ2ZS5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBFbGxpcHNlQ3VydmUucHJvdG90eXBlICk7XG5cdEFyY0N1cnZlLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IEFyY0N1cnZlO1xuXG5cdC8qKlxuXHQgKiBAYXV0aG9yIGFsdGVyZWRxIC8gaHR0cDovL2FsdGVyZWRxdWFsaWEuY29tL1xuXHQgKi9cblxuXHR2YXIgU2NlbmVVdGlscyA9IHtcblxuXHRcdGNyZWF0ZU11bHRpTWF0ZXJpYWxPYmplY3Q6IGZ1bmN0aW9uICggZ2VvbWV0cnksIG1hdGVyaWFscyApIHtcblxuXHRcdFx0dmFyIGdyb3VwID0gbmV3IEdyb3VwKCk7XG5cblx0XHRcdGZvciAoIHZhciBpID0gMCwgbCA9IG1hdGVyaWFscy5sZW5ndGg7IGkgPCBsOyBpICsrICkge1xuXG5cdFx0XHRcdGdyb3VwLmFkZCggbmV3IE1lc2goIGdlb21ldHJ5LCBtYXRlcmlhbHNbIGkgXSApICk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIGdyb3VwO1xuXG5cdFx0fSxcblxuXHRcdGRldGFjaDogZnVuY3Rpb24gKCBjaGlsZCwgcGFyZW50LCBzY2VuZSApIHtcblxuXHRcdFx0Y2hpbGQuYXBwbHlNYXRyaXgoIHBhcmVudC5tYXRyaXhXb3JsZCApO1xuXHRcdFx0cGFyZW50LnJlbW92ZSggY2hpbGQgKTtcblx0XHRcdHNjZW5lLmFkZCggY2hpbGQgKTtcblxuXHRcdH0sXG5cblx0XHRhdHRhY2g6IGZ1bmN0aW9uICggY2hpbGQsIHNjZW5lLCBwYXJlbnQgKSB7XG5cblx0XHRcdHZhciBtYXRyaXhXb3JsZEludmVyc2UgPSBuZXcgTWF0cml4NCgpO1xuXHRcdFx0bWF0cml4V29ybGRJbnZlcnNlLmdldEludmVyc2UoIHBhcmVudC5tYXRyaXhXb3JsZCApO1xuXHRcdFx0Y2hpbGQuYXBwbHlNYXRyaXgoIG1hdHJpeFdvcmxkSW52ZXJzZSApO1xuXG5cdFx0XHRzY2VuZS5yZW1vdmUoIGNoaWxkICk7XG5cdFx0XHRwYXJlbnQuYWRkKCBjaGlsZCApO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0LyoqXG5cdCAqIEBhdXRob3IgbXJkb29iIC8gaHR0cDovL21yZG9vYi5jb20vXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIEZhY2U0KCBhLCBiLCBjLCBkLCBub3JtYWwsIGNvbG9yLCBtYXRlcmlhbEluZGV4ICkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuRmFjZTQgaGFzIGJlZW4gcmVtb3ZlZC4gQSBUSFJFRS5GYWNlMyB3aWxsIGJlIGNyZWF0ZWQgaW5zdGVhZC4nICk7XG5cdFx0cmV0dXJuIG5ldyBGYWNlMyggYSwgYiwgYywgbm9ybWFsLCBjb2xvciwgbWF0ZXJpYWxJbmRleCApO1xuXG5cdH1cblxuXHR2YXIgTGluZVN0cmlwID0gMDtcblxuXHR2YXIgTGluZVBpZWNlcyA9IDE7XG5cblx0ZnVuY3Rpb24gTWVzaEZhY2VNYXRlcmlhbCggbWF0ZXJpYWxzICkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuTWVzaEZhY2VNYXRlcmlhbCBoYXMgYmVlbiByZW5hbWVkIHRvIFRIUkVFLk11bHRpTWF0ZXJpYWwuJyApO1xuXHRcdHJldHVybiBuZXcgTXVsdGlNYXRlcmlhbCggbWF0ZXJpYWxzICk7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIFBvaW50Q2xvdWQoIGdlb21ldHJ5LCBtYXRlcmlhbCApIHtcblxuXHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLlBvaW50Q2xvdWQgaGFzIGJlZW4gcmVuYW1lZCB0byBUSFJFRS5Qb2ludHMuJyApO1xuXHRcdHJldHVybiBuZXcgUG9pbnRzKCBnZW9tZXRyeSwgbWF0ZXJpYWwgKTtcblxuXHR9XG5cblx0ZnVuY3Rpb24gUGFydGljbGUoIG1hdGVyaWFsICkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuUGFydGljbGUgaGFzIGJlZW4gcmVuYW1lZCB0byBUSFJFRS5TcHJpdGUuJyApO1xuXHRcdHJldHVybiBuZXcgU3ByaXRlKCBtYXRlcmlhbCApO1xuXG5cdH1cblxuXHRmdW5jdGlvbiBQYXJ0aWNsZVN5c3RlbSggZ2VvbWV0cnksIG1hdGVyaWFsICkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuUGFydGljbGVTeXN0ZW0gaGFzIGJlZW4gcmVuYW1lZCB0byBUSFJFRS5Qb2ludHMuJyApO1xuXHRcdHJldHVybiBuZXcgUG9pbnRzKCBnZW9tZXRyeSwgbWF0ZXJpYWwgKTtcblxuXHR9XG5cblx0ZnVuY3Rpb24gUG9pbnRDbG91ZE1hdGVyaWFsKCBwYXJhbWV0ZXJzICkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuUG9pbnRDbG91ZE1hdGVyaWFsIGhhcyBiZWVuIHJlbmFtZWQgdG8gVEhSRUUuUG9pbnRzTWF0ZXJpYWwuJyApO1xuXHRcdHJldHVybiBuZXcgUG9pbnRzTWF0ZXJpYWwoIHBhcmFtZXRlcnMgKTtcblxuXHR9XG5cblx0ZnVuY3Rpb24gUGFydGljbGVCYXNpY01hdGVyaWFsKCBwYXJhbWV0ZXJzICkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuUGFydGljbGVCYXNpY01hdGVyaWFsIGhhcyBiZWVuIHJlbmFtZWQgdG8gVEhSRUUuUG9pbnRzTWF0ZXJpYWwuJyApO1xuXHRcdHJldHVybiBuZXcgUG9pbnRzTWF0ZXJpYWwoIHBhcmFtZXRlcnMgKTtcblxuXHR9XG5cblx0ZnVuY3Rpb24gUGFydGljbGVTeXN0ZW1NYXRlcmlhbCggcGFyYW1ldGVycyApIHtcblxuXHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLlBhcnRpY2xlU3lzdGVtTWF0ZXJpYWwgaGFzIGJlZW4gcmVuYW1lZCB0byBUSFJFRS5Qb2ludHNNYXRlcmlhbC4nICk7XG5cdFx0cmV0dXJuIG5ldyBQb2ludHNNYXRlcmlhbCggcGFyYW1ldGVycyApO1xuXG5cdH1cblxuXHRmdW5jdGlvbiBWZXJ0ZXgoIHgsIHksIHogKSB7XG5cblx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5WZXJ0ZXggaGFzIGJlZW4gcmVtb3ZlZC4gVXNlIFRIUkVFLlZlY3RvcjMgaW5zdGVhZC4nICk7XG5cdFx0cmV0dXJuIG5ldyBWZWN0b3IzKCB4LCB5LCB6ICk7XG5cblx0fVxuXG5cdC8vXG5cblx0ZnVuY3Rpb24gRHluYW1pY0J1ZmZlckF0dHJpYnV0ZSggYXJyYXksIGl0ZW1TaXplICkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuRHluYW1pY0J1ZmZlckF0dHJpYnV0ZSBoYXMgYmVlbiByZW1vdmVkLiBVc2UgbmV3IFRIUkVFLkJ1ZmZlckF0dHJpYnV0ZSgpLnNldER5bmFtaWMoIHRydWUgKSBpbnN0ZWFkLicgKTtcblx0XHRyZXR1cm4gbmV3IEJ1ZmZlckF0dHJpYnV0ZSggYXJyYXksIGl0ZW1TaXplICkuc2V0RHluYW1pYyggdHJ1ZSApO1xuXG5cdH1cblxuXHRmdW5jdGlvbiBJbnQ4QXR0cmlidXRlKCBhcnJheSwgaXRlbVNpemUgKSB7XG5cblx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5JbnQ4QXR0cmlidXRlIGhhcyBiZWVuIHJlbW92ZWQuIFVzZSBuZXcgVEhSRUUuSW50OEJ1ZmZlckF0dHJpYnV0ZSgpIGluc3RlYWQuJyApO1xuXHRcdHJldHVybiBuZXcgSW50OEJ1ZmZlckF0dHJpYnV0ZSggYXJyYXksIGl0ZW1TaXplICk7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIFVpbnQ4QXR0cmlidXRlKCBhcnJheSwgaXRlbVNpemUgKSB7XG5cblx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5VaW50OEF0dHJpYnV0ZSBoYXMgYmVlbiByZW1vdmVkLiBVc2UgbmV3IFRIUkVFLlVpbnQ4QnVmZmVyQXR0cmlidXRlKCkgaW5zdGVhZC4nICk7XG5cdFx0cmV0dXJuIG5ldyBVaW50OEJ1ZmZlckF0dHJpYnV0ZSggYXJyYXksIGl0ZW1TaXplICk7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIFVpbnQ4Q2xhbXBlZEF0dHJpYnV0ZSggYXJyYXksIGl0ZW1TaXplICkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuVWludDhDbGFtcGVkQXR0cmlidXRlIGhhcyBiZWVuIHJlbW92ZWQuIFVzZSBuZXcgVEhSRUUuVWludDhDbGFtcGVkQnVmZmVyQXR0cmlidXRlKCkgaW5zdGVhZC4nICk7XG5cdFx0cmV0dXJuIG5ldyBVaW50OENsYW1wZWRCdWZmZXJBdHRyaWJ1dGUoIGFycmF5LCBpdGVtU2l6ZSApO1xuXG5cdH1cblxuXHRmdW5jdGlvbiBJbnQxNkF0dHJpYnV0ZSggYXJyYXksIGl0ZW1TaXplICkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuSW50MTZBdHRyaWJ1dGUgaGFzIGJlZW4gcmVtb3ZlZC4gVXNlIG5ldyBUSFJFRS5JbnQxNkJ1ZmZlckF0dHJpYnV0ZSgpIGluc3RlYWQuJyApO1xuXHRcdHJldHVybiBuZXcgSW50MTZCdWZmZXJBdHRyaWJ1dGUoIGFycmF5LCBpdGVtU2l6ZSApO1xuXG5cdH1cblxuXHRmdW5jdGlvbiBVaW50MTZBdHRyaWJ1dGUoIGFycmF5LCBpdGVtU2l6ZSApIHtcblxuXHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLlVpbnQxNkF0dHJpYnV0ZSBoYXMgYmVlbiByZW1vdmVkLiBVc2UgbmV3IFRIUkVFLlVpbnQxNkJ1ZmZlckF0dHJpYnV0ZSgpIGluc3RlYWQuJyApO1xuXHRcdHJldHVybiBuZXcgVWludDE2QnVmZmVyQXR0cmlidXRlKCBhcnJheSwgaXRlbVNpemUgKTtcblxuXHR9XG5cblx0ZnVuY3Rpb24gSW50MzJBdHRyaWJ1dGUoIGFycmF5LCBpdGVtU2l6ZSApIHtcblxuXHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkludDMyQXR0cmlidXRlIGhhcyBiZWVuIHJlbW92ZWQuIFVzZSBuZXcgVEhSRUUuSW50MzJCdWZmZXJBdHRyaWJ1dGUoKSBpbnN0ZWFkLicgKTtcblx0XHRyZXR1cm4gbmV3IEludDMyQnVmZmVyQXR0cmlidXRlKCBhcnJheSwgaXRlbVNpemUgKTtcblxuXHR9XG5cblx0ZnVuY3Rpb24gVWludDMyQXR0cmlidXRlKCBhcnJheSwgaXRlbVNpemUgKSB7XG5cblx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5VaW50MzJBdHRyaWJ1dGUgaGFzIGJlZW4gcmVtb3ZlZC4gVXNlIG5ldyBUSFJFRS5VaW50MzJCdWZmZXJBdHRyaWJ1dGUoKSBpbnN0ZWFkLicgKTtcblx0XHRyZXR1cm4gbmV3IFVpbnQzMkJ1ZmZlckF0dHJpYnV0ZSggYXJyYXksIGl0ZW1TaXplICk7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIEZsb2F0MzJBdHRyaWJ1dGUoIGFycmF5LCBpdGVtU2l6ZSApIHtcblxuXHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkZsb2F0MzJBdHRyaWJ1dGUgaGFzIGJlZW4gcmVtb3ZlZC4gVXNlIG5ldyBUSFJFRS5GbG9hdDMyQnVmZmVyQXR0cmlidXRlKCkgaW5zdGVhZC4nICk7XG5cdFx0cmV0dXJuIG5ldyBGbG9hdDMyQnVmZmVyQXR0cmlidXRlKCBhcnJheSwgaXRlbVNpemUgKTtcblxuXHR9XG5cblx0ZnVuY3Rpb24gRmxvYXQ2NEF0dHJpYnV0ZSggYXJyYXksIGl0ZW1TaXplICkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuRmxvYXQ2NEF0dHJpYnV0ZSBoYXMgYmVlbiByZW1vdmVkLiBVc2UgbmV3IFRIUkVFLkZsb2F0NjRCdWZmZXJBdHRyaWJ1dGUoKSBpbnN0ZWFkLicgKTtcblx0XHRyZXR1cm4gbmV3IEZsb2F0NjRCdWZmZXJBdHRyaWJ1dGUoIGFycmF5LCBpdGVtU2l6ZSApO1xuXG5cdH1cblxuXHQvL1xuXG5cdGZ1bmN0aW9uIENsb3NlZFNwbGluZUN1cnZlMyggcG9pbnRzICkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuQ2xvc2VkU3BsaW5lQ3VydmUzIGhhcyBiZWVuIGRlcHJlY2F0ZWQuIFVzZSBUSFJFRS5DYXRtdWxsUm9tQ3VydmUzIGluc3RlYWQuJyApO1xuXG5cdFx0Q2F0bXVsbFJvbUN1cnZlMy5jYWxsKCB0aGlzLCBwb2ludHMgKTtcblx0XHR0aGlzLnR5cGUgPSAnY2F0bXVsbHJvbSc7XG5cdFx0dGhpcy5jbG9zZWQgPSB0cnVlO1xuXG5cdH1cblxuXHRDbG9zZWRTcGxpbmVDdXJ2ZTMucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZSggQ2F0bXVsbFJvbUN1cnZlMy5wcm90b3R5cGUgKTtcblxuXG5cdC8vXG5cdGZ1bmN0aW9uIEJvdW5kaW5nQm94SGVscGVyKCBvYmplY3QsIGNvbG9yICkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuQm91bmRpbmdCb3hIZWxwZXIgaGFzIGJlZW4gZGVwcmVjYXRlZC4gQ3JlYXRpbmcgYSBUSFJFRS5Cb3hIZWxwZXIgaW5zdGVhZC4nICk7XG5cdFx0cmV0dXJuIG5ldyBCb3hIZWxwZXIoIG9iamVjdCwgY29sb3IgKTtcblxuXHR9XG5cblx0ZnVuY3Rpb24gRWRnZXNIZWxwZXIoIG9iamVjdCwgaGV4ICkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuRWRnZXNIZWxwZXIgaGFzIGJlZW4gcmVtb3ZlZC4gVXNlIFRIUkVFLkVkZ2VzR2VvbWV0cnkgaW5zdGVhZC4nICk7XG5cdFx0cmV0dXJuIG5ldyBMaW5lU2VnbWVudHMoIG5ldyBFZGdlc0dlb21ldHJ5KCBvYmplY3QuZ2VvbWV0cnkgKSwgbmV3IExpbmVCYXNpY01hdGVyaWFsKCB7IGNvbG9yOiBoZXggIT09IHVuZGVmaW5lZCA/IGhleCA6IDB4ZmZmZmZmIH0gKSApO1xuXG5cdH1cblxuXHRHcmlkSGVscGVyLnByb3RvdHlwZS5zZXRDb2xvcnMgPSBmdW5jdGlvbiAoKSB7XG5cblx0XHRjb25zb2xlLmVycm9yKCAnVEhSRUUuR3JpZEhlbHBlcjogc2V0Q29sb3JzKCkgaGFzIGJlZW4gZGVwcmVjYXRlZCwgcGFzcyB0aGVtIGluIHRoZSBjb25zdHJ1Y3RvciBpbnN0ZWFkLicgKTtcblxuXHR9O1xuXG5cdGZ1bmN0aW9uIFdpcmVmcmFtZUhlbHBlciggb2JqZWN0LCBoZXggKSB7XG5cblx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5XaXJlZnJhbWVIZWxwZXIgaGFzIGJlZW4gcmVtb3ZlZC4gVXNlIFRIUkVFLldpcmVmcmFtZUdlb21ldHJ5IGluc3RlYWQuJyApO1xuXHRcdHJldHVybiBuZXcgTGluZVNlZ21lbnRzKCBuZXcgV2lyZWZyYW1lR2VvbWV0cnkoIG9iamVjdC5nZW9tZXRyeSApLCBuZXcgTGluZUJhc2ljTWF0ZXJpYWwoIHsgY29sb3I6IGhleCAhPT0gdW5kZWZpbmVkID8gaGV4IDogMHhmZmZmZmYgfSApICk7XG5cblx0fVxuXG5cdC8vXG5cblx0ZnVuY3Rpb24gWEhSTG9hZGVyKCBtYW5hZ2VyICkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuWEhSTG9hZGVyIGhhcyBiZWVuIHJlbmFtZWQgdG8gVEhSRUUuRmlsZUxvYWRlci4nICk7XG5cdFx0cmV0dXJuIG5ldyBGaWxlTG9hZGVyKCBtYW5hZ2VyICk7XG5cblx0fVxuXG5cdC8vXG5cblx0T2JqZWN0LmFzc2lnbiggQm94Mi5wcm90b3R5cGUsIHtcblxuXHRcdGNlbnRlcjogZnVuY3Rpb24gKCBvcHRpb25hbFRhcmdldCApIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuQm94MjogLmNlbnRlcigpIGhhcyBiZWVuIHJlbmFtZWQgdG8gLmdldENlbnRlcigpLicgKTtcblx0XHRcdHJldHVybiB0aGlzLmdldENlbnRlciggb3B0aW9uYWxUYXJnZXQgKTtcblxuXHRcdH0sXG5cdFx0ZW1wdHk6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuQm94MjogLmVtcHR5KCkgaGFzIGJlZW4gcmVuYW1lZCB0byAuaXNFbXB0eSgpLicgKTtcblx0XHRcdHJldHVybiB0aGlzLmlzRW1wdHkoKTtcblxuXHRcdH0sXG5cdFx0aXNJbnRlcnNlY3Rpb25Cb3g6IGZ1bmN0aW9uICggYm94ICkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5Cb3gyOiAuaXNJbnRlcnNlY3Rpb25Cb3goKSBoYXMgYmVlbiByZW5hbWVkIHRvIC5pbnRlcnNlY3RzQm94KCkuJyApO1xuXHRcdFx0cmV0dXJuIHRoaXMuaW50ZXJzZWN0c0JveCggYm94ICk7XG5cblx0XHR9LFxuXHRcdHNpemU6IGZ1bmN0aW9uICggb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkJveDI6IC5zaXplKCkgaGFzIGJlZW4gcmVuYW1lZCB0byAuZ2V0U2l6ZSgpLicgKTtcblx0XHRcdHJldHVybiB0aGlzLmdldFNpemUoIG9wdGlvbmFsVGFyZ2V0ICk7XG5cblx0XHR9XG5cdH0gKTtcblxuXHRPYmplY3QuYXNzaWduKCBCb3gzLnByb3RvdHlwZSwge1xuXG5cdFx0Y2VudGVyOiBmdW5jdGlvbiAoIG9wdGlvbmFsVGFyZ2V0ICkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5Cb3gzOiAuY2VudGVyKCkgaGFzIGJlZW4gcmVuYW1lZCB0byAuZ2V0Q2VudGVyKCkuJyApO1xuXHRcdFx0cmV0dXJuIHRoaXMuZ2V0Q2VudGVyKCBvcHRpb25hbFRhcmdldCApO1xuXG5cdFx0fSxcblx0XHRlbXB0eTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5Cb3gzOiAuZW1wdHkoKSBoYXMgYmVlbiByZW5hbWVkIHRvIC5pc0VtcHR5KCkuJyApO1xuXHRcdFx0cmV0dXJuIHRoaXMuaXNFbXB0eSgpO1xuXG5cdFx0fSxcblx0XHRpc0ludGVyc2VjdGlvbkJveDogZnVuY3Rpb24gKCBib3ggKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkJveDM6IC5pc0ludGVyc2VjdGlvbkJveCgpIGhhcyBiZWVuIHJlbmFtZWQgdG8gLmludGVyc2VjdHNCb3goKS4nICk7XG5cdFx0XHRyZXR1cm4gdGhpcy5pbnRlcnNlY3RzQm94KCBib3ggKTtcblxuXHRcdH0sXG5cdFx0aXNJbnRlcnNlY3Rpb25TcGhlcmU6IGZ1bmN0aW9uICggc3BoZXJlICkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5Cb3gzOiAuaXNJbnRlcnNlY3Rpb25TcGhlcmUoKSBoYXMgYmVlbiByZW5hbWVkIHRvIC5pbnRlcnNlY3RzU3BoZXJlKCkuJyApO1xuXHRcdFx0cmV0dXJuIHRoaXMuaW50ZXJzZWN0c1NwaGVyZSggc3BoZXJlICk7XG5cblx0XHR9LFxuXHRcdHNpemU6IGZ1bmN0aW9uICggb3B0aW9uYWxUYXJnZXQgKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkJveDM6IC5zaXplKCkgaGFzIGJlZW4gcmVuYW1lZCB0byAuZ2V0U2l6ZSgpLicgKTtcblx0XHRcdHJldHVybiB0aGlzLmdldFNpemUoIG9wdGlvbmFsVGFyZ2V0ICk7XG5cblx0XHR9XG5cdH0gKTtcblxuXHRMaW5lMy5wcm90b3R5cGUuY2VudGVyID0gZnVuY3Rpb24gKCBvcHRpb25hbFRhcmdldCApIHtcblxuXHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkxpbmUzOiAuY2VudGVyKCkgaGFzIGJlZW4gcmVuYW1lZCB0byAuZ2V0Q2VudGVyKCkuJyApO1xuXHRcdHJldHVybiB0aGlzLmdldENlbnRlciggb3B0aW9uYWxUYXJnZXQgKTtcblxuXHR9O1xuXG5cdF9NYXRoLnJhbmRvbTE2ID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuTWF0aC5yYW5kb20xNigpIGhhcyBiZWVuIGRlcHJlY2F0ZWQuIFVzZSBNYXRoLnJhbmRvbSgpIGluc3RlYWQuJyApO1xuXHRcdHJldHVybiBNYXRoLnJhbmRvbSgpO1xuXG5cdH07XG5cblx0T2JqZWN0LmFzc2lnbiggTWF0cml4My5wcm90b3R5cGUsIHtcblxuXHRcdGZsYXR0ZW5Ub0FycmF5T2Zmc2V0OiBmdW5jdGlvbiAoIGFycmF5LCBvZmZzZXQgKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggXCJUSFJFRS5NYXRyaXgzOiAuZmxhdHRlblRvQXJyYXlPZmZzZXQoKSBoYXMgYmVlbiBkZXByZWNhdGVkLiBVc2UgLnRvQXJyYXkoKSBpbnN0ZWFkLlwiICk7XG5cdFx0XHRyZXR1cm4gdGhpcy50b0FycmF5KCBhcnJheSwgb2Zmc2V0ICk7XG5cblx0XHR9LFxuXHRcdG11bHRpcGx5VmVjdG9yMzogZnVuY3Rpb24gKCB2ZWN0b3IgKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLk1hdHJpeDM6IC5tdWx0aXBseVZlY3RvcjMoKSBoYXMgYmVlbiByZW1vdmVkLiBVc2UgdmVjdG9yLmFwcGx5TWF0cml4MyggbWF0cml4ICkgaW5zdGVhZC4nICk7XG5cdFx0XHRyZXR1cm4gdmVjdG9yLmFwcGx5TWF0cml4MyggdGhpcyApO1xuXG5cdFx0fSxcblx0XHRtdWx0aXBseVZlY3RvcjNBcnJheTogZnVuY3Rpb24gKCBhICkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5NYXRyaXgzOiAubXVsdGlwbHlWZWN0b3IzQXJyYXkoKSBoYXMgYmVlbiByZW5hbWVkLiBVc2UgbWF0cml4LmFwcGx5VG9WZWN0b3IzQXJyYXkoIGFycmF5ICkgaW5zdGVhZC4nICk7XG5cdFx0XHRyZXR1cm4gdGhpcy5hcHBseVRvVmVjdG9yM0FycmF5KCBhICk7XG5cblx0XHR9LFxuXHRcdGFwcGx5VG9CdWZmZXI6IGZ1bmN0aW9uKCBidWZmZXIsIG9mZnNldCwgbGVuZ3RoICkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5NYXRyaXgzOiAuYXBwbHlUb0J1ZmZlcigpIGhhcyBiZWVuIHJlbW92ZWQuIFVzZSBtYXRyaXguYXBwbHlUb0J1ZmZlckF0dHJpYnV0ZSggYXR0cmlidXRlICkgaW5zdGVhZC4nICk7XG5cdFx0XHRyZXR1cm4gdGhpcy5hcHBseVRvQnVmZmVyQXR0cmlidXRlKCBidWZmZXIgKTtcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0T2JqZWN0LmFzc2lnbiggTWF0cml4NC5wcm90b3R5cGUsIHtcblxuXHRcdGV4dHJhY3RQb3NpdGlvbjogZnVuY3Rpb24gKCBtICkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5NYXRyaXg0OiAuZXh0cmFjdFBvc2l0aW9uKCkgaGFzIGJlZW4gcmVuYW1lZCB0byAuY29weVBvc2l0aW9uKCkuJyApO1xuXHRcdFx0cmV0dXJuIHRoaXMuY29weVBvc2l0aW9uKCBtICk7XG5cblx0XHR9LFxuXHRcdGZsYXR0ZW5Ub0FycmF5T2Zmc2V0OiBmdW5jdGlvbiAoIGFycmF5LCBvZmZzZXQgKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggXCJUSFJFRS5NYXRyaXg0OiAuZmxhdHRlblRvQXJyYXlPZmZzZXQoKSBoYXMgYmVlbiBkZXByZWNhdGVkLiBVc2UgLnRvQXJyYXkoKSBpbnN0ZWFkLlwiICk7XG5cdFx0XHRyZXR1cm4gdGhpcy50b0FycmF5KCBhcnJheSwgb2Zmc2V0ICk7XG5cblx0XHR9LFxuXHRcdGdldFBvc2l0aW9uOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdHZhciB2MTtcblxuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIGdldFBvc2l0aW9uKCkge1xuXG5cdFx0XHRcdGlmICggdjEgPT09IHVuZGVmaW5lZCApIHYxID0gbmV3IFZlY3RvcjMoKTtcblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuTWF0cml4NDogLmdldFBvc2l0aW9uKCkgaGFzIGJlZW4gcmVtb3ZlZC4gVXNlIFZlY3RvcjMuc2V0RnJvbU1hdHJpeFBvc2l0aW9uKCBtYXRyaXggKSBpbnN0ZWFkLicgKTtcblx0XHRcdFx0cmV0dXJuIHYxLnNldEZyb21NYXRyaXhDb2x1bW4oIHRoaXMsIDMgKTtcblxuXHRcdFx0fTtcblxuXHRcdH0oKSxcblx0XHRzZXRSb3RhdGlvbkZyb21RdWF0ZXJuaW9uOiBmdW5jdGlvbiAoIHEgKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLk1hdHJpeDQ6IC5zZXRSb3RhdGlvbkZyb21RdWF0ZXJuaW9uKCkgaGFzIGJlZW4gcmVuYW1lZCB0byAubWFrZVJvdGF0aW9uRnJvbVF1YXRlcm5pb24oKS4nICk7XG5cdFx0XHRyZXR1cm4gdGhpcy5tYWtlUm90YXRpb25Gcm9tUXVhdGVybmlvbiggcSApO1xuXG5cdFx0fSxcblx0XHRtdWx0aXBseVZlY3RvcjM6IGZ1bmN0aW9uICggdmVjdG9yICkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5NYXRyaXg0OiAubXVsdGlwbHlWZWN0b3IzKCkgaGFzIGJlZW4gcmVtb3ZlZC4gVXNlIHZlY3Rvci5hcHBseU1hdHJpeDQoIG1hdHJpeCApIG9yIHZlY3Rvci5hcHBseVByb2plY3Rpb24oIG1hdHJpeCApIGluc3RlYWQuJyApO1xuXHRcdFx0cmV0dXJuIHZlY3Rvci5hcHBseVByb2plY3Rpb24oIHRoaXMgKTtcblxuXHRcdH0sXG5cdFx0bXVsdGlwbHlWZWN0b3I0OiBmdW5jdGlvbiAoIHZlY3RvciApIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuTWF0cml4NDogLm11bHRpcGx5VmVjdG9yNCgpIGhhcyBiZWVuIHJlbW92ZWQuIFVzZSB2ZWN0b3IuYXBwbHlNYXRyaXg0KCBtYXRyaXggKSBpbnN0ZWFkLicgKTtcblx0XHRcdHJldHVybiB2ZWN0b3IuYXBwbHlNYXRyaXg0KCB0aGlzICk7XG5cblx0XHR9LFxuXHRcdG11bHRpcGx5VmVjdG9yM0FycmF5OiBmdW5jdGlvbiAoIGEgKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLk1hdHJpeDQ6IC5tdWx0aXBseVZlY3RvcjNBcnJheSgpIGhhcyBiZWVuIHJlbmFtZWQuIFVzZSBtYXRyaXguYXBwbHlUb1ZlY3RvcjNBcnJheSggYXJyYXkgKSBpbnN0ZWFkLicgKTtcblx0XHRcdHJldHVybiB0aGlzLmFwcGx5VG9WZWN0b3IzQXJyYXkoIGEgKTtcblxuXHRcdH0sXG5cdFx0cm90YXRlQXhpczogZnVuY3Rpb24gKCB2ICkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5NYXRyaXg0OiAucm90YXRlQXhpcygpIGhhcyBiZWVuIHJlbW92ZWQuIFVzZSBWZWN0b3IzLnRyYW5zZm9ybURpcmVjdGlvbiggbWF0cml4ICkgaW5zdGVhZC4nICk7XG5cdFx0XHR2LnRyYW5zZm9ybURpcmVjdGlvbiggdGhpcyApO1xuXG5cdFx0fSxcblx0XHRjcm9zc1ZlY3RvcjogZnVuY3Rpb24gKCB2ZWN0b3IgKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLk1hdHJpeDQ6IC5jcm9zc1ZlY3RvcigpIGhhcyBiZWVuIHJlbW92ZWQuIFVzZSB2ZWN0b3IuYXBwbHlNYXRyaXg0KCBtYXRyaXggKSBpbnN0ZWFkLicgKTtcblx0XHRcdHJldHVybiB2ZWN0b3IuYXBwbHlNYXRyaXg0KCB0aGlzICk7XG5cblx0XHR9LFxuXHRcdHRyYW5zbGF0ZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRjb25zb2xlLmVycm9yKCAnVEhSRUUuTWF0cml4NDogLnRyYW5zbGF0ZSgpIGhhcyBiZWVuIHJlbW92ZWQuJyApO1xuXG5cdFx0fSxcblx0XHRyb3RhdGVYOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5NYXRyaXg0OiAucm90YXRlWCgpIGhhcyBiZWVuIHJlbW92ZWQuJyApO1xuXG5cdFx0fSxcblx0XHRyb3RhdGVZOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5NYXRyaXg0OiAucm90YXRlWSgpIGhhcyBiZWVuIHJlbW92ZWQuJyApO1xuXG5cdFx0fSxcblx0XHRyb3RhdGVaOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5NYXRyaXg0OiAucm90YXRlWigpIGhhcyBiZWVuIHJlbW92ZWQuJyApO1xuXG5cdFx0fSxcblx0XHRyb3RhdGVCeUF4aXM6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Y29uc29sZS5lcnJvciggJ1RIUkVFLk1hdHJpeDQ6IC5yb3RhdGVCeUF4aXMoKSBoYXMgYmVlbiByZW1vdmVkLicgKTtcblxuXHRcdH0sXG5cdFx0YXBwbHlUb0J1ZmZlcjogZnVuY3Rpb24oIGJ1ZmZlciwgb2Zmc2V0LCBsZW5ndGggKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLk1hdHJpeDQ6IC5hcHBseVRvQnVmZmVyKCkgaGFzIGJlZW4gcmVtb3ZlZC4gVXNlIG1hdHJpeC5hcHBseVRvQnVmZmVyQXR0cmlidXRlKCBhdHRyaWJ1dGUgKSBpbnN0ZWFkLicgKTtcblx0XHRcdHJldHVybiB0aGlzLmFwcGx5VG9CdWZmZXJBdHRyaWJ1dGUoIGJ1ZmZlciApO1xuXG5cdFx0fVxuXG5cdH0gKTtcblxuXHRQbGFuZS5wcm90b3R5cGUuaXNJbnRlcnNlY3Rpb25MaW5lID0gZnVuY3Rpb24gKCBsaW5lICkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuUGxhbmU6IC5pc0ludGVyc2VjdGlvbkxpbmUoKSBoYXMgYmVlbiByZW5hbWVkIHRvIC5pbnRlcnNlY3RzTGluZSgpLicgKTtcblx0XHRyZXR1cm4gdGhpcy5pbnRlcnNlY3RzTGluZSggbGluZSApO1xuXG5cdH07XG5cblx0UXVhdGVybmlvbi5wcm90b3R5cGUubXVsdGlwbHlWZWN0b3IzID0gZnVuY3Rpb24gKCB2ZWN0b3IgKSB7XG5cblx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5RdWF0ZXJuaW9uOiAubXVsdGlwbHlWZWN0b3IzKCkgaGFzIGJlZW4gcmVtb3ZlZC4gVXNlIGlzIG5vdyB2ZWN0b3IuYXBwbHlRdWF0ZXJuaW9uKCBxdWF0ZXJuaW9uICkgaW5zdGVhZC4nICk7XG5cdFx0cmV0dXJuIHZlY3Rvci5hcHBseVF1YXRlcm5pb24oIHRoaXMgKTtcblxuXHR9O1xuXG5cdE9iamVjdC5hc3NpZ24oIFJheS5wcm90b3R5cGUsIHtcblxuXHRcdGlzSW50ZXJzZWN0aW9uQm94OiBmdW5jdGlvbiAoIGJveCApIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuUmF5OiAuaXNJbnRlcnNlY3Rpb25Cb3goKSBoYXMgYmVlbiByZW5hbWVkIHRvIC5pbnRlcnNlY3RzQm94KCkuJyApO1xuXHRcdFx0cmV0dXJuIHRoaXMuaW50ZXJzZWN0c0JveCggYm94ICk7XG5cblx0XHR9LFxuXHRcdGlzSW50ZXJzZWN0aW9uUGxhbmU6IGZ1bmN0aW9uICggcGxhbmUgKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLlJheTogLmlzSW50ZXJzZWN0aW9uUGxhbmUoKSBoYXMgYmVlbiByZW5hbWVkIHRvIC5pbnRlcnNlY3RzUGxhbmUoKS4nICk7XG5cdFx0XHRyZXR1cm4gdGhpcy5pbnRlcnNlY3RzUGxhbmUoIHBsYW5lICk7XG5cblx0XHR9LFxuXHRcdGlzSW50ZXJzZWN0aW9uU3BoZXJlOiBmdW5jdGlvbiAoIHNwaGVyZSApIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuUmF5OiAuaXNJbnRlcnNlY3Rpb25TcGhlcmUoKSBoYXMgYmVlbiByZW5hbWVkIHRvIC5pbnRlcnNlY3RzU3BoZXJlKCkuJyApO1xuXHRcdFx0cmV0dXJuIHRoaXMuaW50ZXJzZWN0c1NwaGVyZSggc3BoZXJlICk7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdE9iamVjdC5hc3NpZ24oIFNoYXBlLnByb3RvdHlwZSwge1xuXG5cdFx0ZXh0cnVkZTogZnVuY3Rpb24gKCBvcHRpb25zICkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5TaGFwZTogLmV4dHJ1ZGUoKSBoYXMgYmVlbiByZW1vdmVkLiBVc2UgRXh0cnVkZUdlb21ldHJ5KCkgaW5zdGVhZC4nICk7XG5cdFx0XHRyZXR1cm4gbmV3IEV4dHJ1ZGVHZW9tZXRyeSggdGhpcywgb3B0aW9ucyApO1xuXG5cdFx0fSxcblx0XHRtYWtlR2VvbWV0cnk6IGZ1bmN0aW9uICggb3B0aW9ucyApIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuU2hhcGU6IC5tYWtlR2VvbWV0cnkoKSBoYXMgYmVlbiByZW1vdmVkLiBVc2UgU2hhcGVHZW9tZXRyeSgpIGluc3RlYWQuJyApO1xuXHRcdFx0cmV0dXJuIG5ldyBTaGFwZUdlb21ldHJ5KCB0aGlzLCBvcHRpb25zICk7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdE9iamVjdC5hc3NpZ24oIFZlY3RvcjMucHJvdG90eXBlLCB7XG5cblx0XHRzZXRFdWxlckZyb21Sb3RhdGlvbk1hdHJpeDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRjb25zb2xlLmVycm9yKCAnVEhSRUUuVmVjdG9yMzogLnNldEV1bGVyRnJvbVJvdGF0aW9uTWF0cml4KCkgaGFzIGJlZW4gcmVtb3ZlZC4gVXNlIEV1bGVyLnNldEZyb21Sb3RhdGlvbk1hdHJpeCgpIGluc3RlYWQuJyApO1xuXG5cdFx0fSxcblx0XHRzZXRFdWxlckZyb21RdWF0ZXJuaW9uOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5WZWN0b3IzOiAuc2V0RXVsZXJGcm9tUXVhdGVybmlvbigpIGhhcyBiZWVuIHJlbW92ZWQuIFVzZSBFdWxlci5zZXRGcm9tUXVhdGVybmlvbigpIGluc3RlYWQuJyApO1xuXG5cdFx0fSxcblx0XHRnZXRQb3NpdGlvbkZyb21NYXRyaXg6IGZ1bmN0aW9uICggbSApIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuVmVjdG9yMzogLmdldFBvc2l0aW9uRnJvbU1hdHJpeCgpIGhhcyBiZWVuIHJlbmFtZWQgdG8gLnNldEZyb21NYXRyaXhQb3NpdGlvbigpLicgKTtcblx0XHRcdHJldHVybiB0aGlzLnNldEZyb21NYXRyaXhQb3NpdGlvbiggbSApO1xuXG5cdFx0fSxcblx0XHRnZXRTY2FsZUZyb21NYXRyaXg6IGZ1bmN0aW9uICggbSApIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuVmVjdG9yMzogLmdldFNjYWxlRnJvbU1hdHJpeCgpIGhhcyBiZWVuIHJlbmFtZWQgdG8gLnNldEZyb21NYXRyaXhTY2FsZSgpLicgKTtcblx0XHRcdHJldHVybiB0aGlzLnNldEZyb21NYXRyaXhTY2FsZSggbSApO1xuXG5cdFx0fSxcblx0XHRnZXRDb2x1bW5Gcm9tTWF0cml4OiBmdW5jdGlvbiAoIGluZGV4LCBtYXRyaXggKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLlZlY3RvcjM6IC5nZXRDb2x1bW5Gcm9tTWF0cml4KCkgaGFzIGJlZW4gcmVuYW1lZCB0byAuc2V0RnJvbU1hdHJpeENvbHVtbigpLicgKTtcblx0XHRcdHJldHVybiB0aGlzLnNldEZyb21NYXRyaXhDb2x1bW4oIG1hdHJpeCwgaW5kZXggKTtcblxuXHRcdH1cblxuXHR9ICk7XG5cblx0Ly9cblxuXHRHZW9tZXRyeS5wcm90b3R5cGUuY29tcHV0ZVRhbmdlbnRzID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuR2VvbWV0cnk6IC5jb21wdXRlVGFuZ2VudHMoKSBoYXMgYmVlbiByZW1vdmVkLicgKTtcblxuXHR9O1xuXG5cdE9iamVjdC5hc3NpZ24oIE9iamVjdDNELnByb3RvdHlwZSwge1xuXG5cdFx0Z2V0Q2hpbGRCeU5hbWU6IGZ1bmN0aW9uICggbmFtZSApIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuT2JqZWN0M0Q6IC5nZXRDaGlsZEJ5TmFtZSgpIGhhcyBiZWVuIHJlbmFtZWQgdG8gLmdldE9iamVjdEJ5TmFtZSgpLicgKTtcblx0XHRcdHJldHVybiB0aGlzLmdldE9iamVjdEJ5TmFtZSggbmFtZSApO1xuXG5cdFx0fSxcblx0XHRyZW5kZXJEZXB0aDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5PYmplY3QzRDogLnJlbmRlckRlcHRoIGhhcyBiZWVuIHJlbW92ZWQuIFVzZSAucmVuZGVyT3JkZXIsIGluc3RlYWQuJyApO1xuXG5cdFx0fSxcblx0XHR0cmFuc2xhdGU6IGZ1bmN0aW9uICggZGlzdGFuY2UsIGF4aXMgKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLk9iamVjdDNEOiAudHJhbnNsYXRlKCkgaGFzIGJlZW4gcmVtb3ZlZC4gVXNlIC50cmFuc2xhdGVPbkF4aXMoIGF4aXMsIGRpc3RhbmNlICkgaW5zdGVhZC4nICk7XG5cdFx0XHRyZXR1cm4gdGhpcy50cmFuc2xhdGVPbkF4aXMoIGF4aXMsIGRpc3RhbmNlICk7XG5cblx0XHR9XG5cblx0fSApO1xuXG5cdE9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKCBPYmplY3QzRC5wcm90b3R5cGUsIHtcblxuXHRcdGV1bGVyT3JkZXI6IHtcblx0XHRcdGdldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLk9iamVjdDNEOiAuZXVsZXJPcmRlciBpcyBub3cgLnJvdGF0aW9uLm9yZGVyLicgKTtcblx0XHRcdFx0cmV0dXJuIHRoaXMucm90YXRpb24ub3JkZXI7XG5cblx0XHRcdH0sXG5cdFx0XHRzZXQ6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuT2JqZWN0M0Q6IC5ldWxlck9yZGVyIGlzIG5vdyAucm90YXRpb24ub3JkZXIuJyApO1xuXHRcdFx0XHR0aGlzLnJvdGF0aW9uLm9yZGVyID0gdmFsdWU7XG5cblx0XHRcdH1cblx0XHR9LFxuXHRcdHVzZVF1YXRlcm5pb246IHtcblx0XHRcdGdldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLk9iamVjdDNEOiAudXNlUXVhdGVybmlvbiBoYXMgYmVlbiByZW1vdmVkLiBUaGUgbGlicmFyeSBub3cgdXNlcyBxdWF0ZXJuaW9ucyBieSBkZWZhdWx0LicgKTtcblxuXHRcdFx0fSxcblx0XHRcdHNldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLk9iamVjdDNEOiAudXNlUXVhdGVybmlvbiBoYXMgYmVlbiByZW1vdmVkLiBUaGUgbGlicmFyeSBub3cgdXNlcyBxdWF0ZXJuaW9ucyBieSBkZWZhdWx0LicgKTtcblxuXHRcdFx0fVxuXHRcdH1cblxuXHR9ICk7XG5cblx0T2JqZWN0LmRlZmluZVByb3BlcnRpZXMoIExPRC5wcm90b3R5cGUsIHtcblxuXHRcdG9iamVjdHM6IHtcblx0XHRcdGdldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkxPRDogLm9iamVjdHMgaGFzIGJlZW4gcmVuYW1lZCB0byAubGV2ZWxzLicgKTtcblx0XHRcdFx0cmV0dXJuIHRoaXMubGV2ZWxzO1xuXG5cdFx0XHR9XG5cdFx0fVxuXG5cdH0gKTtcblxuXHQvL1xuXG5cdFBlcnNwZWN0aXZlQ2FtZXJhLnByb3RvdHlwZS5zZXRMZW5zID0gZnVuY3Rpb24gKCBmb2NhbExlbmd0aCwgZmlsbUdhdWdlICkge1xuXG5cdFx0Y29uc29sZS53YXJuKCBcIlRIUkVFLlBlcnNwZWN0aXZlQ2FtZXJhLnNldExlbnMgaXMgZGVwcmVjYXRlZC4gXCIgK1xuXHRcdFx0XHRcIlVzZSAuc2V0Rm9jYWxMZW5ndGggYW5kIC5maWxtR2F1Z2UgZm9yIGEgcGhvdG9ncmFwaGljIHNldHVwLlwiICk7XG5cblx0XHRpZiAoIGZpbG1HYXVnZSAhPT0gdW5kZWZpbmVkICkgdGhpcy5maWxtR2F1Z2UgPSBmaWxtR2F1Z2U7XG5cdFx0dGhpcy5zZXRGb2NhbExlbmd0aCggZm9jYWxMZW5ndGggKTtcblxuXHR9O1xuXG5cdC8vXG5cblx0T2JqZWN0LmRlZmluZVByb3BlcnRpZXMoIExpZ2h0LnByb3RvdHlwZSwge1xuXHRcdG9ubHlTaGFkb3c6IHtcblx0XHRcdHNldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkxpZ2h0OiAub25seVNoYWRvdyBoYXMgYmVlbiByZW1vdmVkLicgKTtcblxuXHRcdFx0fVxuXHRcdH0sXG5cdFx0c2hhZG93Q2FtZXJhRm92OiB7XG5cdFx0XHRzZXQ6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuTGlnaHQ6IC5zaGFkb3dDYW1lcmFGb3YgaXMgbm93IC5zaGFkb3cuY2FtZXJhLmZvdi4nICk7XG5cdFx0XHRcdHRoaXMuc2hhZG93LmNhbWVyYS5mb3YgPSB2YWx1ZTtcblxuXHRcdFx0fVxuXHRcdH0sXG5cdFx0c2hhZG93Q2FtZXJhTGVmdDoge1xuXHRcdFx0c2V0OiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkxpZ2h0OiAuc2hhZG93Q2FtZXJhTGVmdCBpcyBub3cgLnNoYWRvdy5jYW1lcmEubGVmdC4nICk7XG5cdFx0XHRcdHRoaXMuc2hhZG93LmNhbWVyYS5sZWZ0ID0gdmFsdWU7XG5cblx0XHRcdH1cblx0XHR9LFxuXHRcdHNoYWRvd0NhbWVyYVJpZ2h0OiB7XG5cdFx0XHRzZXQ6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuTGlnaHQ6IC5zaGFkb3dDYW1lcmFSaWdodCBpcyBub3cgLnNoYWRvdy5jYW1lcmEucmlnaHQuJyApO1xuXHRcdFx0XHR0aGlzLnNoYWRvdy5jYW1lcmEucmlnaHQgPSB2YWx1ZTtcblxuXHRcdFx0fVxuXHRcdH0sXG5cdFx0c2hhZG93Q2FtZXJhVG9wOiB7XG5cdFx0XHRzZXQ6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuTGlnaHQ6IC5zaGFkb3dDYW1lcmFUb3AgaXMgbm93IC5zaGFkb3cuY2FtZXJhLnRvcC4nICk7XG5cdFx0XHRcdHRoaXMuc2hhZG93LmNhbWVyYS50b3AgPSB2YWx1ZTtcblxuXHRcdFx0fVxuXHRcdH0sXG5cdFx0c2hhZG93Q2FtZXJhQm90dG9tOiB7XG5cdFx0XHRzZXQ6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuTGlnaHQ6IC5zaGFkb3dDYW1lcmFCb3R0b20gaXMgbm93IC5zaGFkb3cuY2FtZXJhLmJvdHRvbS4nICk7XG5cdFx0XHRcdHRoaXMuc2hhZG93LmNhbWVyYS5ib3R0b20gPSB2YWx1ZTtcblxuXHRcdFx0fVxuXHRcdH0sXG5cdFx0c2hhZG93Q2FtZXJhTmVhcjoge1xuXHRcdFx0c2V0OiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkxpZ2h0OiAuc2hhZG93Q2FtZXJhTmVhciBpcyBub3cgLnNoYWRvdy5jYW1lcmEubmVhci4nICk7XG5cdFx0XHRcdHRoaXMuc2hhZG93LmNhbWVyYS5uZWFyID0gdmFsdWU7XG5cblx0XHRcdH1cblx0XHR9LFxuXHRcdHNoYWRvd0NhbWVyYUZhcjoge1xuXHRcdFx0c2V0OiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkxpZ2h0OiAuc2hhZG93Q2FtZXJhRmFyIGlzIG5vdyAuc2hhZG93LmNhbWVyYS5mYXIuJyApO1xuXHRcdFx0XHR0aGlzLnNoYWRvdy5jYW1lcmEuZmFyID0gdmFsdWU7XG5cblx0XHRcdH1cblx0XHR9LFxuXHRcdHNoYWRvd0NhbWVyYVZpc2libGU6IHtcblx0XHRcdHNldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkxpZ2h0OiAuc2hhZG93Q2FtZXJhVmlzaWJsZSBoYXMgYmVlbiByZW1vdmVkLiBVc2UgbmV3IFRIUkVFLkNhbWVyYUhlbHBlciggbGlnaHQuc2hhZG93LmNhbWVyYSApIGluc3RlYWQuJyApO1xuXG5cdFx0XHR9XG5cdFx0fSxcblx0XHRzaGFkb3dCaWFzOiB7XG5cdFx0XHRzZXQ6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuTGlnaHQ6IC5zaGFkb3dCaWFzIGlzIG5vdyAuc2hhZG93LmJpYXMuJyApO1xuXHRcdFx0XHR0aGlzLnNoYWRvdy5iaWFzID0gdmFsdWU7XG5cblx0XHRcdH1cblx0XHR9LFxuXHRcdHNoYWRvd0RhcmtuZXNzOiB7XG5cdFx0XHRzZXQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5MaWdodDogLnNoYWRvd0RhcmtuZXNzIGhhcyBiZWVuIHJlbW92ZWQuJyApO1xuXG5cdFx0XHR9XG5cdFx0fSxcblx0XHRzaGFkb3dNYXBXaWR0aDoge1xuXHRcdFx0c2V0OiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkxpZ2h0OiAuc2hhZG93TWFwV2lkdGggaXMgbm93IC5zaGFkb3cubWFwU2l6ZS53aWR0aC4nICk7XG5cdFx0XHRcdHRoaXMuc2hhZG93Lm1hcFNpemUud2lkdGggPSB2YWx1ZTtcblxuXHRcdFx0fVxuXHRcdH0sXG5cdFx0c2hhZG93TWFwSGVpZ2h0OiB7XG5cdFx0XHRzZXQ6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuTGlnaHQ6IC5zaGFkb3dNYXBIZWlnaHQgaXMgbm93IC5zaGFkb3cubWFwU2l6ZS5oZWlnaHQuJyApO1xuXHRcdFx0XHR0aGlzLnNoYWRvdy5tYXBTaXplLmhlaWdodCA9IHZhbHVlO1xuXG5cdFx0XHR9XG5cdFx0fVxuXHR9ICk7XG5cblx0Ly9cblxuXHRPYmplY3QuZGVmaW5lUHJvcGVydGllcyggQnVmZmVyQXR0cmlidXRlLnByb3RvdHlwZSwge1xuXG5cdFx0bGVuZ3RoOiB7XG5cdFx0XHRnZXQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5CdWZmZXJBdHRyaWJ1dGU6IC5sZW5ndGggaGFzIGJlZW4gZGVwcmVjYXRlZC4gVXNlIC5jb3VudCBpbnN0ZWFkLicgKTtcblx0XHRcdFx0cmV0dXJuIHRoaXMuYXJyYXkubGVuZ3RoO1xuXG5cdFx0XHR9XG5cdFx0fVxuXG5cdH0gKTtcblxuXHRPYmplY3QuYXNzaWduKCBCdWZmZXJHZW9tZXRyeS5wcm90b3R5cGUsIHtcblxuXHRcdGFkZEluZGV4OiBmdW5jdGlvbiAoIGluZGV4ICkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5CdWZmZXJHZW9tZXRyeTogLmFkZEluZGV4KCkgaGFzIGJlZW4gcmVuYW1lZCB0byAuc2V0SW5kZXgoKS4nICk7XG5cdFx0XHR0aGlzLnNldEluZGV4KCBpbmRleCApO1xuXG5cdFx0fSxcblx0XHRhZGREcmF3Q2FsbDogZnVuY3Rpb24gKCBzdGFydCwgY291bnQsIGluZGV4T2Zmc2V0ICkge1xuXG5cdFx0XHRpZiAoIGluZGV4T2Zmc2V0ICE9PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuQnVmZmVyR2VvbWV0cnk6IC5hZGREcmF3Q2FsbCgpIG5vIGxvbmdlciBzdXBwb3J0cyBpbmRleE9mZnNldC4nICk7XG5cblx0XHRcdH1cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkJ1ZmZlckdlb21ldHJ5OiAuYWRkRHJhd0NhbGwoKSBpcyBub3cgLmFkZEdyb3VwKCkuJyApO1xuXHRcdFx0dGhpcy5hZGRHcm91cCggc3RhcnQsIGNvdW50ICk7XG5cblx0XHR9LFxuXHRcdGNsZWFyRHJhd0NhbGxzOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkJ1ZmZlckdlb21ldHJ5OiAuY2xlYXJEcmF3Q2FsbHMoKSBpcyBub3cgLmNsZWFyR3JvdXBzKCkuJyApO1xuXHRcdFx0dGhpcy5jbGVhckdyb3VwcygpO1xuXG5cdFx0fSxcblx0XHRjb21wdXRlVGFuZ2VudHM6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuQnVmZmVyR2VvbWV0cnk6IC5jb21wdXRlVGFuZ2VudHMoKSBoYXMgYmVlbiByZW1vdmVkLicgKTtcblxuXHRcdH0sXG5cdFx0Y29tcHV0ZU9mZnNldHM6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuQnVmZmVyR2VvbWV0cnk6IC5jb21wdXRlT2Zmc2V0cygpIGhhcyBiZWVuIHJlbW92ZWQuJyApO1xuXG5cdFx0fVxuXG5cdH0gKTtcblxuXHRPYmplY3QuZGVmaW5lUHJvcGVydGllcyggQnVmZmVyR2VvbWV0cnkucHJvdG90eXBlLCB7XG5cblx0XHRkcmF3Y2FsbHM6IHtcblx0XHRcdGdldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5CdWZmZXJHZW9tZXRyeTogLmRyYXdjYWxscyBoYXMgYmVlbiByZW5hbWVkIHRvIC5ncm91cHMuJyApO1xuXHRcdFx0XHRyZXR1cm4gdGhpcy5ncm91cHM7XG5cblx0XHRcdH1cblx0XHR9LFxuXHRcdG9mZnNldHM6IHtcblx0XHRcdGdldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkJ1ZmZlckdlb21ldHJ5OiAub2Zmc2V0cyBoYXMgYmVlbiByZW5hbWVkIHRvIC5ncm91cHMuJyApO1xuXHRcdFx0XHRyZXR1cm4gdGhpcy5ncm91cHM7XG5cblx0XHRcdH1cblx0XHR9XG5cblx0fSApO1xuXG5cdC8vXG5cblx0T2JqZWN0LmRlZmluZVByb3BlcnRpZXMoIFVuaWZvcm0ucHJvdG90eXBlLCB7XG5cblx0XHRkeW5hbWljOiB7XG5cdFx0XHRzZXQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5Vbmlmb3JtOiAuZHluYW1pYyBoYXMgYmVlbiByZW1vdmVkLiBVc2Ugb2JqZWN0Lm9uQmVmb3JlUmVuZGVyKCkgaW5zdGVhZC4nICk7XG5cblx0XHRcdH1cblx0XHR9LFxuXHRcdG9uVXBkYXRlOiB7XG5cdFx0XHR2YWx1ZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLlVuaWZvcm06IC5vblVwZGF0ZSgpIGhhcyBiZWVuIHJlbW92ZWQuIFVzZSBvYmplY3Qub25CZWZvcmVSZW5kZXIoKSBpbnN0ZWFkLicgKTtcblx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cblx0XHRcdH1cblx0XHR9XG5cblx0fSApO1xuXG5cdC8vXG5cblx0T2JqZWN0LmRlZmluZVByb3BlcnRpZXMoIE1hdGVyaWFsLnByb3RvdHlwZSwge1xuXG5cdFx0d3JhcEFyb3VuZDoge1xuXHRcdFx0Z2V0OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuJyArIHRoaXMudHlwZSArICc6IC53cmFwQXJvdW5kIGhhcyBiZWVuIHJlbW92ZWQuJyApO1xuXG5cdFx0XHR9LFxuXHRcdFx0c2V0OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuJyArIHRoaXMudHlwZSArICc6IC53cmFwQXJvdW5kIGhhcyBiZWVuIHJlbW92ZWQuJyApO1xuXG5cdFx0XHR9XG5cdFx0fSxcblx0XHR3cmFwUkdCOiB7XG5cdFx0XHRnZXQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS4nICsgdGhpcy50eXBlICsgJzogLndyYXBSR0IgaGFzIGJlZW4gcmVtb3ZlZC4nICk7XG5cdFx0XHRcdHJldHVybiBuZXcgQ29sb3IoKTtcblxuXHRcdFx0fVxuXHRcdH1cblxuXHR9ICk7XG5cblx0T2JqZWN0LmRlZmluZVByb3BlcnRpZXMoIE1lc2hQaG9uZ01hdGVyaWFsLnByb3RvdHlwZSwge1xuXG5cdFx0bWV0YWw6IHtcblx0XHRcdGdldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLk1lc2hQaG9uZ01hdGVyaWFsOiAubWV0YWwgaGFzIGJlZW4gcmVtb3ZlZC4gVXNlIFRIUkVFLk1lc2hTdGFuZGFyZE1hdGVyaWFsIGluc3RlYWQuJyApO1xuXHRcdFx0XHRyZXR1cm4gZmFsc2U7XG5cblx0XHRcdH0sXG5cdFx0XHRzZXQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5NZXNoUGhvbmdNYXRlcmlhbDogLm1ldGFsIGhhcyBiZWVuIHJlbW92ZWQuIFVzZSBUSFJFRS5NZXNoU3RhbmRhcmRNYXRlcmlhbCBpbnN0ZWFkJyApO1xuXG5cdFx0XHR9XG5cdFx0fVxuXG5cdH0gKTtcblxuXHRPYmplY3QuZGVmaW5lUHJvcGVydGllcyggU2hhZGVyTWF0ZXJpYWwucHJvdG90eXBlLCB7XG5cblx0XHRkZXJpdmF0aXZlczoge1xuXHRcdFx0Z2V0OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuU2hhZGVyTWF0ZXJpYWw6IC5kZXJpdmF0aXZlcyBoYXMgYmVlbiBtb3ZlZCB0byAuZXh0ZW5zaW9ucy5kZXJpdmF0aXZlcy4nICk7XG5cdFx0XHRcdHJldHVybiB0aGlzLmV4dGVuc2lvbnMuZGVyaXZhdGl2ZXM7XG5cblx0XHRcdH0sXG5cdFx0XHRzZXQ6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuIFNoYWRlck1hdGVyaWFsOiAuZGVyaXZhdGl2ZXMgaGFzIGJlZW4gbW92ZWQgdG8gLmV4dGVuc2lvbnMuZGVyaXZhdGl2ZXMuJyApO1xuXHRcdFx0XHR0aGlzLmV4dGVuc2lvbnMuZGVyaXZhdGl2ZXMgPSB2YWx1ZTtcblxuXHRcdFx0fVxuXHRcdH1cblxuXHR9ICk7XG5cblx0Ly9cblxuXHRFdmVudERpc3BhdGNoZXIucHJvdG90eXBlID0gT2JqZWN0LmFzc2lnbiggT2JqZWN0LmNyZWF0ZSgge1xuXG5cdFx0Ly8gTm90ZTogRXh0cmEgYmFzZSBlbnN1cmVzIHRoZXNlIHByb3BlcnRpZXMgYXJlIG5vdCAnYXNzaWduJ2VkLlxuXG5cdFx0Y29uc3RydWN0b3I6IEV2ZW50RGlzcGF0Y2hlcixcblxuXHRcdGFwcGx5OiBmdW5jdGlvbiAoIHRhcmdldCApIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCBcIlRIUkVFLkV2ZW50RGlzcGF0Y2hlcjogLmFwcGx5IGlzIGRlcHJlY2F0ZWQsIFwiICtcblx0XHRcdFx0XHRcImp1c3QgaW5oZXJpdCBvciBPYmplY3QuYXNzaWduIHRoZSBwcm90b3R5cGUgdG8gbWl4LWluLlwiICk7XG5cblx0XHRcdE9iamVjdC5hc3NpZ24oIHRhcmdldCwgdGhpcyApO1xuXG5cdFx0fVxuXG5cdH0gKSwgRXZlbnREaXNwYXRjaGVyLnByb3RvdHlwZSApO1xuXG5cdC8vXG5cblx0T2JqZWN0LmFzc2lnbiggV2ViR0xSZW5kZXJlci5wcm90b3R5cGUsIHtcblxuXHRcdHN1cHBvcnRzRmxvYXRUZXh0dXJlczogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5XZWJHTFJlbmRlcmVyOiAuc3VwcG9ydHNGbG9hdFRleHR1cmVzKCkgaXMgbm93IC5leHRlbnNpb25zLmdldCggXFwnT0VTX3RleHR1cmVfZmxvYXRcXCcgKS4nICk7XG5cdFx0XHRyZXR1cm4gdGhpcy5leHRlbnNpb25zLmdldCggJ09FU190ZXh0dXJlX2Zsb2F0JyApO1xuXG5cdFx0fSxcblx0XHRzdXBwb3J0c0hhbGZGbG9hdFRleHR1cmVzOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyZXI6IC5zdXBwb3J0c0hhbGZGbG9hdFRleHR1cmVzKCkgaXMgbm93IC5leHRlbnNpb25zLmdldCggXFwnT0VTX3RleHR1cmVfaGFsZl9mbG9hdFxcJyApLicgKTtcblx0XHRcdHJldHVybiB0aGlzLmV4dGVuc2lvbnMuZ2V0KCAnT0VTX3RleHR1cmVfaGFsZl9mbG9hdCcgKTtcblxuXHRcdH0sXG5cdFx0c3VwcG9ydHNTdGFuZGFyZERlcml2YXRpdmVzOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyZXI6IC5zdXBwb3J0c1N0YW5kYXJkRGVyaXZhdGl2ZXMoKSBpcyBub3cgLmV4dGVuc2lvbnMuZ2V0KCBcXCdPRVNfc3RhbmRhcmRfZGVyaXZhdGl2ZXNcXCcgKS4nICk7XG5cdFx0XHRyZXR1cm4gdGhpcy5leHRlbnNpb25zLmdldCggJ09FU19zdGFuZGFyZF9kZXJpdmF0aXZlcycgKTtcblxuXHRcdH0sXG5cdFx0c3VwcG9ydHNDb21wcmVzc2VkVGV4dHVyZVMzVEM6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuV2ViR0xSZW5kZXJlcjogLnN1cHBvcnRzQ29tcHJlc3NlZFRleHR1cmVTM1RDKCkgaXMgbm93IC5leHRlbnNpb25zLmdldCggXFwnV0VCR0xfY29tcHJlc3NlZF90ZXh0dXJlX3MzdGNcXCcgKS4nICk7XG5cdFx0XHRyZXR1cm4gdGhpcy5leHRlbnNpb25zLmdldCggJ1dFQkdMX2NvbXByZXNzZWRfdGV4dHVyZV9zM3RjJyApO1xuXG5cdFx0fSxcblx0XHRzdXBwb3J0c0NvbXByZXNzZWRUZXh0dXJlUFZSVEM6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuV2ViR0xSZW5kZXJlcjogLnN1cHBvcnRzQ29tcHJlc3NlZFRleHR1cmVQVlJUQygpIGlzIG5vdyAuZXh0ZW5zaW9ucy5nZXQoIFxcJ1dFQkdMX2NvbXByZXNzZWRfdGV4dHVyZV9wdnJ0Y1xcJyApLicgKTtcblx0XHRcdHJldHVybiB0aGlzLmV4dGVuc2lvbnMuZ2V0KCAnV0VCR0xfY29tcHJlc3NlZF90ZXh0dXJlX3B2cnRjJyApO1xuXG5cdFx0fSxcblx0XHRzdXBwb3J0c0JsZW5kTWluTWF4OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyZXI6IC5zdXBwb3J0c0JsZW5kTWluTWF4KCkgaXMgbm93IC5leHRlbnNpb25zLmdldCggXFwnRVhUX2JsZW5kX21pbm1heFxcJyApLicgKTtcblx0XHRcdHJldHVybiB0aGlzLmV4dGVuc2lvbnMuZ2V0KCAnRVhUX2JsZW5kX21pbm1heCcgKTtcblxuXHRcdH0sXG5cdFx0c3VwcG9ydHNWZXJ0ZXhUZXh0dXJlczogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5XZWJHTFJlbmRlcmVyOiAuc3VwcG9ydHNWZXJ0ZXhUZXh0dXJlcygpIGlzIG5vdyAuY2FwYWJpbGl0aWVzLnZlcnRleFRleHR1cmVzLicgKTtcblx0XHRcdHJldHVybiB0aGlzLmNhcGFiaWxpdGllcy52ZXJ0ZXhUZXh0dXJlcztcblxuXHRcdH0sXG5cdFx0c3VwcG9ydHNJbnN0YW5jZWRBcnJheXM6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuV2ViR0xSZW5kZXJlcjogLnN1cHBvcnRzSW5zdGFuY2VkQXJyYXlzKCkgaXMgbm93IC5leHRlbnNpb25zLmdldCggXFwnQU5HTEVfaW5zdGFuY2VkX2FycmF5c1xcJyApLicgKTtcblx0XHRcdHJldHVybiB0aGlzLmV4dGVuc2lvbnMuZ2V0KCAnQU5HTEVfaW5zdGFuY2VkX2FycmF5cycgKTtcblxuXHRcdH0sXG5cdFx0ZW5hYmxlU2Npc3NvclRlc3Q6IGZ1bmN0aW9uICggYm9vbGVhbiApIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuV2ViR0xSZW5kZXJlcjogLmVuYWJsZVNjaXNzb3JUZXN0KCkgaXMgbm93IC5zZXRTY2lzc29yVGVzdCgpLicgKTtcblx0XHRcdHRoaXMuc2V0U2Npc3NvclRlc3QoIGJvb2xlYW4gKTtcblxuXHRcdH0sXG5cdFx0aW5pdE1hdGVyaWFsOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyZXI6IC5pbml0TWF0ZXJpYWwoKSBoYXMgYmVlbiByZW1vdmVkLicgKTtcblxuXHRcdH0sXG5cdFx0YWRkUHJlUGx1Z2luOiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyZXI6IC5hZGRQcmVQbHVnaW4oKSBoYXMgYmVlbiByZW1vdmVkLicgKTtcblxuXHRcdH0sXG5cdFx0YWRkUG9zdFBsdWdpbjogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5XZWJHTFJlbmRlcmVyOiAuYWRkUG9zdFBsdWdpbigpIGhhcyBiZWVuIHJlbW92ZWQuJyApO1xuXG5cdFx0fSxcblx0XHR1cGRhdGVTaGFkb3dNYXA6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuV2ViR0xSZW5kZXJlcjogLnVwZGF0ZVNoYWRvd01hcCgpIGhhcyBiZWVuIHJlbW92ZWQuJyApO1xuXG5cdFx0fVxuXG5cdH0gKTtcblxuXHRPYmplY3QuZGVmaW5lUHJvcGVydGllcyggV2ViR0xSZW5kZXJlci5wcm90b3R5cGUsIHtcblxuXHRcdHNoYWRvd01hcEVuYWJsZWQ6IHtcblx0XHRcdGdldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdHJldHVybiB0aGlzLnNoYWRvd01hcC5lbmFibGVkO1xuXG5cdFx0XHR9LFxuXHRcdFx0c2V0OiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyZXI6IC5zaGFkb3dNYXBFbmFibGVkIGlzIG5vdyAuc2hhZG93TWFwLmVuYWJsZWQuJyApO1xuXHRcdFx0XHR0aGlzLnNoYWRvd01hcC5lbmFibGVkID0gdmFsdWU7XG5cblx0XHRcdH1cblx0XHR9LFxuXHRcdHNoYWRvd01hcFR5cGU6IHtcblx0XHRcdGdldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdHJldHVybiB0aGlzLnNoYWRvd01hcC50eXBlO1xuXG5cdFx0XHR9LFxuXHRcdFx0c2V0OiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyZXI6IC5zaGFkb3dNYXBUeXBlIGlzIG5vdyAuc2hhZG93TWFwLnR5cGUuJyApO1xuXHRcdFx0XHR0aGlzLnNoYWRvd01hcC50eXBlID0gdmFsdWU7XG5cblx0XHRcdH1cblx0XHR9LFxuXHRcdHNoYWRvd01hcEN1bGxGYWNlOiB7XG5cdFx0XHRnZXQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcy5zaGFkb3dNYXAuY3VsbEZhY2U7XG5cblx0XHRcdH0sXG5cdFx0XHRzZXQ6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuV2ViR0xSZW5kZXJlcjogLnNoYWRvd01hcEN1bGxGYWNlIGlzIG5vdyAuc2hhZG93TWFwLmN1bGxGYWNlLicgKTtcblx0XHRcdFx0dGhpcy5zaGFkb3dNYXAuY3VsbEZhY2UgPSB2YWx1ZTtcblxuXHRcdFx0fVxuXHRcdH1cblx0fSApO1xuXG5cdE9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKCBXZWJHTFNoYWRvd01hcC5wcm90b3R5cGUsIHtcblxuXHRcdGN1bGxGYWNlOiB7XG5cdFx0XHRnZXQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0XHRyZXR1cm4gdGhpcy5yZW5kZXJSZXZlcnNlU2lkZWQgPyBDdWxsRmFjZUZyb250IDogQ3VsbEZhY2VCYWNrO1xuXG5cdFx0XHR9LFxuXHRcdFx0c2V0OiBmdW5jdGlvbiAoIGN1bGxGYWNlICkge1xuXG5cdFx0XHRcdHZhciB2YWx1ZSA9ICggY3VsbEZhY2UgIT09IEN1bGxGYWNlQmFjayApO1xuXHRcdFx0XHRjb25zb2xlLndhcm4oIFwiV2ViR0xSZW5kZXJlcjogLnNoYWRvd01hcC5jdWxsRmFjZSBpcyBkZXByZWNhdGVkLiBTZXQgLnNoYWRvd01hcC5yZW5kZXJSZXZlcnNlU2lkZWQgdG8gXCIgKyB2YWx1ZSArIFwiLlwiICk7XG5cdFx0XHRcdHRoaXMucmVuZGVyUmV2ZXJzZVNpZGVkID0gdmFsdWU7XG5cblx0XHRcdH1cblx0XHR9XG5cblx0fSApO1xuXG5cdC8vXG5cblx0T2JqZWN0LmRlZmluZVByb3BlcnRpZXMoIFdlYkdMUmVuZGVyVGFyZ2V0LnByb3RvdHlwZSwge1xuXG5cdFx0d3JhcFM6IHtcblx0XHRcdGdldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyVGFyZ2V0OiAud3JhcFMgaXMgbm93IC50ZXh0dXJlLndyYXBTLicgKTtcblx0XHRcdFx0cmV0dXJuIHRoaXMudGV4dHVyZS53cmFwUztcblxuXHRcdFx0fSxcblx0XHRcdHNldDogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5XZWJHTFJlbmRlclRhcmdldDogLndyYXBTIGlzIG5vdyAudGV4dHVyZS53cmFwUy4nICk7XG5cdFx0XHRcdHRoaXMudGV4dHVyZS53cmFwUyA9IHZhbHVlO1xuXG5cdFx0XHR9XG5cdFx0fSxcblx0XHR3cmFwVDoge1xuXHRcdFx0Z2V0OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuV2ViR0xSZW5kZXJUYXJnZXQ6IC53cmFwVCBpcyBub3cgLnRleHR1cmUud3JhcFQuJyApO1xuXHRcdFx0XHRyZXR1cm4gdGhpcy50ZXh0dXJlLndyYXBUO1xuXG5cdFx0XHR9LFxuXHRcdFx0c2V0OiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyVGFyZ2V0OiAud3JhcFQgaXMgbm93IC50ZXh0dXJlLndyYXBULicgKTtcblx0XHRcdFx0dGhpcy50ZXh0dXJlLndyYXBUID0gdmFsdWU7XG5cblx0XHRcdH1cblx0XHR9LFxuXHRcdG1hZ0ZpbHRlcjoge1xuXHRcdFx0Z2V0OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuV2ViR0xSZW5kZXJUYXJnZXQ6IC5tYWdGaWx0ZXIgaXMgbm93IC50ZXh0dXJlLm1hZ0ZpbHRlci4nICk7XG5cdFx0XHRcdHJldHVybiB0aGlzLnRleHR1cmUubWFnRmlsdGVyO1xuXG5cdFx0XHR9LFxuXHRcdFx0c2V0OiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyVGFyZ2V0OiAubWFnRmlsdGVyIGlzIG5vdyAudGV4dHVyZS5tYWdGaWx0ZXIuJyApO1xuXHRcdFx0XHR0aGlzLnRleHR1cmUubWFnRmlsdGVyID0gdmFsdWU7XG5cblx0XHRcdH1cblx0XHR9LFxuXHRcdG1pbkZpbHRlcjoge1xuXHRcdFx0Z2V0OiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuV2ViR0xSZW5kZXJUYXJnZXQ6IC5taW5GaWx0ZXIgaXMgbm93IC50ZXh0dXJlLm1pbkZpbHRlci4nICk7XG5cdFx0XHRcdHJldHVybiB0aGlzLnRleHR1cmUubWluRmlsdGVyO1xuXG5cdFx0XHR9LFxuXHRcdFx0c2V0OiBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyVGFyZ2V0OiAubWluRmlsdGVyIGlzIG5vdyAudGV4dHVyZS5taW5GaWx0ZXIuJyApO1xuXHRcdFx0XHR0aGlzLnRleHR1cmUubWluRmlsdGVyID0gdmFsdWU7XG5cblx0XHRcdH1cblx0XHR9LFxuXHRcdGFuaXNvdHJvcHk6IHtcblx0XHRcdGdldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyVGFyZ2V0OiAuYW5pc290cm9weSBpcyBub3cgLnRleHR1cmUuYW5pc290cm9weS4nICk7XG5cdFx0XHRcdHJldHVybiB0aGlzLnRleHR1cmUuYW5pc290cm9weTtcblxuXHRcdFx0fSxcblx0XHRcdHNldDogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5XZWJHTFJlbmRlclRhcmdldDogLmFuaXNvdHJvcHkgaXMgbm93IC50ZXh0dXJlLmFuaXNvdHJvcHkuJyApO1xuXHRcdFx0XHR0aGlzLnRleHR1cmUuYW5pc290cm9weSA9IHZhbHVlO1xuXG5cdFx0XHR9XG5cdFx0fSxcblx0XHRvZmZzZXQ6IHtcblx0XHRcdGdldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyVGFyZ2V0OiAub2Zmc2V0IGlzIG5vdyAudGV4dHVyZS5vZmZzZXQuJyApO1xuXHRcdFx0XHRyZXR1cm4gdGhpcy50ZXh0dXJlLm9mZnNldDtcblxuXHRcdFx0fSxcblx0XHRcdHNldDogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5XZWJHTFJlbmRlclRhcmdldDogLm9mZnNldCBpcyBub3cgLnRleHR1cmUub2Zmc2V0LicgKTtcblx0XHRcdFx0dGhpcy50ZXh0dXJlLm9mZnNldCA9IHZhbHVlO1xuXG5cdFx0XHR9XG5cdFx0fSxcblx0XHRyZXBlYXQ6IHtcblx0XHRcdGdldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyVGFyZ2V0OiAucmVwZWF0IGlzIG5vdyAudGV4dHVyZS5yZXBlYXQuJyApO1xuXHRcdFx0XHRyZXR1cm4gdGhpcy50ZXh0dXJlLnJlcGVhdDtcblxuXHRcdFx0fSxcblx0XHRcdHNldDogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5XZWJHTFJlbmRlclRhcmdldDogLnJlcGVhdCBpcyBub3cgLnRleHR1cmUucmVwZWF0LicgKTtcblx0XHRcdFx0dGhpcy50ZXh0dXJlLnJlcGVhdCA9IHZhbHVlO1xuXG5cdFx0XHR9XG5cdFx0fSxcblx0XHRmb3JtYXQ6IHtcblx0XHRcdGdldDogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLldlYkdMUmVuZGVyVGFyZ2V0OiAuZm9ybWF0IGlzIG5vdyAudGV4dHVyZS5mb3JtYXQuJyApO1xuXHRcdFx0XHRyZXR1cm4gdGhpcy50ZXh0dXJlLmZvcm1hdDtcblxuXHRcdFx0fSxcblx0XHRcdHNldDogZnVuY3Rpb24gKCB2YWx1ZSApIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5XZWJHTFJlbmRlclRhcmdldDogLmZvcm1hdCBpcyBub3cgLnRleHR1cmUuZm9ybWF0LicgKTtcblx0XHRcdFx0dGhpcy50ZXh0dXJlLmZvcm1hdCA9IHZhbHVlO1xuXG5cdFx0XHR9XG5cdFx0fSxcblx0XHR0eXBlOiB7XG5cdFx0XHRnZXQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5XZWJHTFJlbmRlclRhcmdldDogLnR5cGUgaXMgbm93IC50ZXh0dXJlLnR5cGUuJyApO1xuXHRcdFx0XHRyZXR1cm4gdGhpcy50ZXh0dXJlLnR5cGU7XG5cblx0XHRcdH0sXG5cdFx0XHRzZXQ6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuV2ViR0xSZW5kZXJUYXJnZXQ6IC50eXBlIGlzIG5vdyAudGV4dHVyZS50eXBlLicgKTtcblx0XHRcdFx0dGhpcy50ZXh0dXJlLnR5cGUgPSB2YWx1ZTtcblxuXHRcdFx0fVxuXHRcdH0sXG5cdFx0Z2VuZXJhdGVNaXBtYXBzOiB7XG5cdFx0XHRnZXQ6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5XZWJHTFJlbmRlclRhcmdldDogLmdlbmVyYXRlTWlwbWFwcyBpcyBub3cgLnRleHR1cmUuZ2VuZXJhdGVNaXBtYXBzLicgKTtcblx0XHRcdFx0cmV0dXJuIHRoaXMudGV4dHVyZS5nZW5lcmF0ZU1pcG1hcHM7XG5cblx0XHRcdH0sXG5cdFx0XHRzZXQ6IGZ1bmN0aW9uICggdmFsdWUgKSB7XG5cblx0XHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuV2ViR0xSZW5kZXJUYXJnZXQ6IC5nZW5lcmF0ZU1pcG1hcHMgaXMgbm93IC50ZXh0dXJlLmdlbmVyYXRlTWlwbWFwcy4nICk7XG5cdFx0XHRcdHRoaXMudGV4dHVyZS5nZW5lcmF0ZU1pcG1hcHMgPSB2YWx1ZTtcblxuXHRcdFx0fVxuXHRcdH1cblxuXHR9ICk7XG5cblx0Ly9cblxuXHRBdWRpby5wcm90b3R5cGUubG9hZCA9IGZ1bmN0aW9uICggZmlsZSApIHtcblxuXHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkF1ZGlvOiAubG9hZCBoYXMgYmVlbiBkZXByZWNhdGVkLiBVc2UgVEhSRUUuQXVkaW9Mb2FkZXIgaW5zdGVhZC4nICk7XG5cdFx0dmFyIHNjb3BlID0gdGhpcztcblx0XHR2YXIgYXVkaW9Mb2FkZXIgPSBuZXcgQXVkaW9Mb2FkZXIoKTtcblx0XHRhdWRpb0xvYWRlci5sb2FkKCBmaWxlLCBmdW5jdGlvbiAoIGJ1ZmZlciApIHtcblxuXHRcdFx0c2NvcGUuc2V0QnVmZmVyKCBidWZmZXIgKTtcblxuXHRcdH0gKTtcblx0XHRyZXR1cm4gdGhpcztcblxuXHR9O1xuXG5cdEF1ZGlvQW5hbHlzZXIucHJvdG90eXBlLmdldERhdGEgPSBmdW5jdGlvbiAoKSB7XG5cblx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5BdWRpb0FuYWx5c2VyOiAuZ2V0RGF0YSgpIGlzIG5vdyAuZ2V0RnJlcXVlbmN5RGF0YSgpLicgKTtcblx0XHRyZXR1cm4gdGhpcy5nZXRGcmVxdWVuY3lEYXRhKCk7XG5cblx0fTtcblxuXHQvL1xuXG5cdHZhciBHZW9tZXRyeVV0aWxzID0ge1xuXG5cdFx0bWVyZ2U6IGZ1bmN0aW9uICggZ2VvbWV0cnkxLCBnZW9tZXRyeTIsIG1hdGVyaWFsSW5kZXhPZmZzZXQgKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLkdlb21ldHJ5VXRpbHM6IC5tZXJnZSgpIGhhcyBiZWVuIG1vdmVkIHRvIEdlb21ldHJ5LiBVc2UgZ2VvbWV0cnkubWVyZ2UoIGdlb21ldHJ5MiwgbWF0cml4LCBtYXRlcmlhbEluZGV4T2Zmc2V0ICkgaW5zdGVhZC4nICk7XG5cdFx0XHR2YXIgbWF0cml4O1xuXG5cdFx0XHRpZiAoIGdlb21ldHJ5Mi5pc01lc2ggKSB7XG5cblx0XHRcdFx0Z2VvbWV0cnkyLm1hdHJpeEF1dG9VcGRhdGUgJiYgZ2VvbWV0cnkyLnVwZGF0ZU1hdHJpeCgpO1xuXG5cdFx0XHRcdG1hdHJpeCA9IGdlb21ldHJ5Mi5tYXRyaXg7XG5cdFx0XHRcdGdlb21ldHJ5MiA9IGdlb21ldHJ5Mi5nZW9tZXRyeTtcblxuXHRcdFx0fVxuXG5cdFx0XHRnZW9tZXRyeTEubWVyZ2UoIGdlb21ldHJ5MiwgbWF0cml4LCBtYXRlcmlhbEluZGV4T2Zmc2V0ICk7XG5cblx0XHR9LFxuXG5cdFx0Y2VudGVyOiBmdW5jdGlvbiAoIGdlb21ldHJ5ICkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5HZW9tZXRyeVV0aWxzOiAuY2VudGVyKCkgaGFzIGJlZW4gbW92ZWQgdG8gR2VvbWV0cnkuIFVzZSBnZW9tZXRyeS5jZW50ZXIoKSBpbnN0ZWFkLicgKTtcblx0XHRcdHJldHVybiBnZW9tZXRyeS5jZW50ZXIoKTtcblxuXHRcdH1cblxuXHR9O1xuXG5cdHZhciBJbWFnZVV0aWxzID0ge1xuXG5cdFx0Y3Jvc3NPcmlnaW46IHVuZGVmaW5lZCxcblxuXHRcdGxvYWRUZXh0dXJlOiBmdW5jdGlvbiAoIHVybCwgbWFwcGluZywgb25Mb2FkLCBvbkVycm9yICkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5JbWFnZVV0aWxzLmxvYWRUZXh0dXJlIGhhcyBiZWVuIGRlcHJlY2F0ZWQuIFVzZSBUSFJFRS5UZXh0dXJlTG9hZGVyKCkgaW5zdGVhZC4nICk7XG5cblx0XHRcdHZhciBsb2FkZXIgPSBuZXcgVGV4dHVyZUxvYWRlcigpO1xuXHRcdFx0bG9hZGVyLnNldENyb3NzT3JpZ2luKCB0aGlzLmNyb3NzT3JpZ2luICk7XG5cblx0XHRcdHZhciB0ZXh0dXJlID0gbG9hZGVyLmxvYWQoIHVybCwgb25Mb2FkLCB1bmRlZmluZWQsIG9uRXJyb3IgKTtcblxuXHRcdFx0aWYgKCBtYXBwaW5nICkgdGV4dHVyZS5tYXBwaW5nID0gbWFwcGluZztcblxuXHRcdFx0cmV0dXJuIHRleHR1cmU7XG5cblx0XHR9LFxuXG5cdFx0bG9hZFRleHR1cmVDdWJlOiBmdW5jdGlvbiAoIHVybHMsIG1hcHBpbmcsIG9uTG9hZCwgb25FcnJvciApIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuSW1hZ2VVdGlscy5sb2FkVGV4dHVyZUN1YmUgaGFzIGJlZW4gZGVwcmVjYXRlZC4gVXNlIFRIUkVFLkN1YmVUZXh0dXJlTG9hZGVyKCkgaW5zdGVhZC4nICk7XG5cblx0XHRcdHZhciBsb2FkZXIgPSBuZXcgQ3ViZVRleHR1cmVMb2FkZXIoKTtcblx0XHRcdGxvYWRlci5zZXRDcm9zc09yaWdpbiggdGhpcy5jcm9zc09yaWdpbiApO1xuXG5cdFx0XHR2YXIgdGV4dHVyZSA9IGxvYWRlci5sb2FkKCB1cmxzLCBvbkxvYWQsIHVuZGVmaW5lZCwgb25FcnJvciApO1xuXG5cdFx0XHRpZiAoIG1hcHBpbmcgKSB0ZXh0dXJlLm1hcHBpbmcgPSBtYXBwaW5nO1xuXG5cdFx0XHRyZXR1cm4gdGV4dHVyZTtcblxuXHRcdH0sXG5cblx0XHRsb2FkQ29tcHJlc3NlZFRleHR1cmU6IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Y29uc29sZS5lcnJvciggJ1RIUkVFLkltYWdlVXRpbHMubG9hZENvbXByZXNzZWRUZXh0dXJlIGhhcyBiZWVuIHJlbW92ZWQuIFVzZSBUSFJFRS5ERFNMb2FkZXIgaW5zdGVhZC4nICk7XG5cblx0XHR9LFxuXG5cdFx0bG9hZENvbXByZXNzZWRUZXh0dXJlQ3ViZTogZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRjb25zb2xlLmVycm9yKCAnVEhSRUUuSW1hZ2VVdGlscy5sb2FkQ29tcHJlc3NlZFRleHR1cmVDdWJlIGhhcyBiZWVuIHJlbW92ZWQuIFVzZSBUSFJFRS5ERFNMb2FkZXIgaW5zdGVhZC4nICk7XG5cblx0XHR9XG5cblx0fTtcblxuXHQvL1xuXG5cdGZ1bmN0aW9uIFByb2plY3RvcigpIHtcblxuXHRcdGNvbnNvbGUuZXJyb3IoICdUSFJFRS5Qcm9qZWN0b3IgaGFzIGJlZW4gbW92ZWQgdG8gL2V4YW1wbGVzL2pzL3JlbmRlcmVycy9Qcm9qZWN0b3IuanMuJyApO1xuXG5cdFx0dGhpcy5wcm9qZWN0VmVjdG9yID0gZnVuY3Rpb24gKCB2ZWN0b3IsIGNhbWVyYSApIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuUHJvamVjdG9yOiAucHJvamVjdFZlY3RvcigpIGlzIG5vdyB2ZWN0b3IucHJvamVjdCgpLicgKTtcblx0XHRcdHZlY3Rvci5wcm9qZWN0KCBjYW1lcmEgKTtcblxuXHRcdH07XG5cblx0XHR0aGlzLnVucHJvamVjdFZlY3RvciA9IGZ1bmN0aW9uICggdmVjdG9yLCBjYW1lcmEgKSB7XG5cblx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLlByb2plY3RvcjogLnVucHJvamVjdFZlY3RvcigpIGlzIG5vdyB2ZWN0b3IudW5wcm9qZWN0KCkuJyApO1xuXHRcdFx0dmVjdG9yLnVucHJvamVjdCggY2FtZXJhICk7XG5cblx0XHR9O1xuXG5cdFx0dGhpcy5waWNraW5nUmF5ID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0XHRjb25zb2xlLmVycm9yKCAnVEhSRUUuUHJvamVjdG9yOiAucGlja2luZ1JheSgpIGlzIG5vdyByYXljYXN0ZXIuc2V0RnJvbUNhbWVyYSgpLicgKTtcblxuXHRcdH07XG5cblx0fVxuXG5cdC8vXG5cblx0ZnVuY3Rpb24gQ2FudmFzUmVuZGVyZXIoKSB7XG5cblx0XHRjb25zb2xlLmVycm9yKCAnVEhSRUUuQ2FudmFzUmVuZGVyZXIgaGFzIGJlZW4gbW92ZWQgdG8gL2V4YW1wbGVzL2pzL3JlbmRlcmVycy9DYW52YXNSZW5kZXJlci5qcycgKTtcblxuXHRcdHRoaXMuZG9tRWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnROUyggJ2h0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwnLCAnY2FudmFzJyApO1xuXHRcdHRoaXMuY2xlYXIgPSBmdW5jdGlvbiAoKSB7fTtcblx0XHR0aGlzLnJlbmRlciA9IGZ1bmN0aW9uICgpIHt9O1xuXHRcdHRoaXMuc2V0Q2xlYXJDb2xvciA9IGZ1bmN0aW9uICgpIHt9O1xuXHRcdHRoaXMuc2V0U2l6ZSA9IGZ1bmN0aW9uICgpIHt9O1xuXG5cdH1cblxuXHRleHBvcnRzLldlYkdMUmVuZGVyVGFyZ2V0Q3ViZSA9IFdlYkdMUmVuZGVyVGFyZ2V0Q3ViZTtcblx0ZXhwb3J0cy5XZWJHTFJlbmRlclRhcmdldCA9IFdlYkdMUmVuZGVyVGFyZ2V0O1xuXHRleHBvcnRzLldlYkdMUmVuZGVyZXIgPSBXZWJHTFJlbmRlcmVyO1xuXHRleHBvcnRzLlNoYWRlckxpYiA9IFNoYWRlckxpYjtcblx0ZXhwb3J0cy5Vbmlmb3Jtc0xpYiA9IFVuaWZvcm1zTGliO1xuXHRleHBvcnRzLlVuaWZvcm1zVXRpbHMgPSBVbmlmb3Jtc1V0aWxzO1xuXHRleHBvcnRzLlNoYWRlckNodW5rID0gU2hhZGVyQ2h1bms7XG5cdGV4cG9ydHMuRm9nRXhwMiA9IEZvZ0V4cDI7XG5cdGV4cG9ydHMuRm9nID0gRm9nO1xuXHRleHBvcnRzLlNjZW5lID0gU2NlbmU7XG5cdGV4cG9ydHMuTGVuc0ZsYXJlID0gTGVuc0ZsYXJlO1xuXHRleHBvcnRzLlNwcml0ZSA9IFNwcml0ZTtcblx0ZXhwb3J0cy5MT0QgPSBMT0Q7XG5cdGV4cG9ydHMuU2tpbm5lZE1lc2ggPSBTa2lubmVkTWVzaDtcblx0ZXhwb3J0cy5Ta2VsZXRvbiA9IFNrZWxldG9uO1xuXHRleHBvcnRzLkJvbmUgPSBCb25lO1xuXHRleHBvcnRzLk1lc2ggPSBNZXNoO1xuXHRleHBvcnRzLkxpbmVTZWdtZW50cyA9IExpbmVTZWdtZW50cztcblx0ZXhwb3J0cy5MaW5lID0gTGluZTtcblx0ZXhwb3J0cy5Qb2ludHMgPSBQb2ludHM7XG5cdGV4cG9ydHMuR3JvdXAgPSBHcm91cDtcblx0ZXhwb3J0cy5WaWRlb1RleHR1cmUgPSBWaWRlb1RleHR1cmU7XG5cdGV4cG9ydHMuRGF0YVRleHR1cmUgPSBEYXRhVGV4dHVyZTtcblx0ZXhwb3J0cy5Db21wcmVzc2VkVGV4dHVyZSA9IENvbXByZXNzZWRUZXh0dXJlO1xuXHRleHBvcnRzLkN1YmVUZXh0dXJlID0gQ3ViZVRleHR1cmU7XG5cdGV4cG9ydHMuQ2FudmFzVGV4dHVyZSA9IENhbnZhc1RleHR1cmU7XG5cdGV4cG9ydHMuRGVwdGhUZXh0dXJlID0gRGVwdGhUZXh0dXJlO1xuXHRleHBvcnRzLlRleHR1cmUgPSBUZXh0dXJlO1xuXHRleHBvcnRzLkNvbXByZXNzZWRUZXh0dXJlTG9hZGVyID0gQ29tcHJlc3NlZFRleHR1cmVMb2FkZXI7XG5cdGV4cG9ydHMuQmluYXJ5VGV4dHVyZUxvYWRlciA9IEJpbmFyeVRleHR1cmVMb2FkZXI7XG5cdGV4cG9ydHMuRGF0YVRleHR1cmVMb2FkZXIgPSBEYXRhVGV4dHVyZUxvYWRlcjtcblx0ZXhwb3J0cy5DdWJlVGV4dHVyZUxvYWRlciA9IEN1YmVUZXh0dXJlTG9hZGVyO1xuXHRleHBvcnRzLlRleHR1cmVMb2FkZXIgPSBUZXh0dXJlTG9hZGVyO1xuXHRleHBvcnRzLk9iamVjdExvYWRlciA9IE9iamVjdExvYWRlcjtcblx0ZXhwb3J0cy5NYXRlcmlhbExvYWRlciA9IE1hdGVyaWFsTG9hZGVyO1xuXHRleHBvcnRzLkJ1ZmZlckdlb21ldHJ5TG9hZGVyID0gQnVmZmVyR2VvbWV0cnlMb2FkZXI7XG5cdGV4cG9ydHMuRGVmYXVsdExvYWRpbmdNYW5hZ2VyID0gRGVmYXVsdExvYWRpbmdNYW5hZ2VyO1xuXHRleHBvcnRzLkxvYWRpbmdNYW5hZ2VyID0gTG9hZGluZ01hbmFnZXI7XG5cdGV4cG9ydHMuSlNPTkxvYWRlciA9IEpTT05Mb2FkZXI7XG5cdGV4cG9ydHMuSW1hZ2VMb2FkZXIgPSBJbWFnZUxvYWRlcjtcblx0ZXhwb3J0cy5Gb250TG9hZGVyID0gRm9udExvYWRlcjtcblx0ZXhwb3J0cy5GaWxlTG9hZGVyID0gRmlsZUxvYWRlcjtcblx0ZXhwb3J0cy5Mb2FkZXIgPSBMb2FkZXI7XG5cdGV4cG9ydHMuQ2FjaGUgPSBDYWNoZTtcblx0ZXhwb3J0cy5BdWRpb0xvYWRlciA9IEF1ZGlvTG9hZGVyO1xuXHRleHBvcnRzLlNwb3RMaWdodFNoYWRvdyA9IFNwb3RMaWdodFNoYWRvdztcblx0ZXhwb3J0cy5TcG90TGlnaHQgPSBTcG90TGlnaHQ7XG5cdGV4cG9ydHMuUG9pbnRMaWdodCA9IFBvaW50TGlnaHQ7XG5cdGV4cG9ydHMuUmVjdEFyZWFMaWdodCA9IFJlY3RBcmVhTGlnaHQ7XG5cdGV4cG9ydHMuSGVtaXNwaGVyZUxpZ2h0ID0gSGVtaXNwaGVyZUxpZ2h0O1xuXHRleHBvcnRzLkRpcmVjdGlvbmFsTGlnaHRTaGFkb3cgPSBEaXJlY3Rpb25hbExpZ2h0U2hhZG93O1xuXHRleHBvcnRzLkRpcmVjdGlvbmFsTGlnaHQgPSBEaXJlY3Rpb25hbExpZ2h0O1xuXHRleHBvcnRzLkFtYmllbnRMaWdodCA9IEFtYmllbnRMaWdodDtcblx0ZXhwb3J0cy5MaWdodFNoYWRvdyA9IExpZ2h0U2hhZG93O1xuXHRleHBvcnRzLkxpZ2h0ID0gTGlnaHQ7XG5cdGV4cG9ydHMuU3RlcmVvQ2FtZXJhID0gU3RlcmVvQ2FtZXJhO1xuXHRleHBvcnRzLlBlcnNwZWN0aXZlQ2FtZXJhID0gUGVyc3BlY3RpdmVDYW1lcmE7XG5cdGV4cG9ydHMuT3J0aG9ncmFwaGljQ2FtZXJhID0gT3J0aG9ncmFwaGljQ2FtZXJhO1xuXHRleHBvcnRzLkN1YmVDYW1lcmEgPSBDdWJlQ2FtZXJhO1xuXHRleHBvcnRzLkNhbWVyYSA9IENhbWVyYTtcblx0ZXhwb3J0cy5BdWRpb0xpc3RlbmVyID0gQXVkaW9MaXN0ZW5lcjtcblx0ZXhwb3J0cy5Qb3NpdGlvbmFsQXVkaW8gPSBQb3NpdGlvbmFsQXVkaW87XG5cdGV4cG9ydHMuQXVkaW9Db250ZXh0ID0gQXVkaW9Db250ZXh0O1xuXHRleHBvcnRzLkF1ZGlvQW5hbHlzZXIgPSBBdWRpb0FuYWx5c2VyO1xuXHRleHBvcnRzLkF1ZGlvID0gQXVkaW87XG5cdGV4cG9ydHMuVmVjdG9yS2V5ZnJhbWVUcmFjayA9IFZlY3RvcktleWZyYW1lVHJhY2s7XG5cdGV4cG9ydHMuU3RyaW5nS2V5ZnJhbWVUcmFjayA9IFN0cmluZ0tleWZyYW1lVHJhY2s7XG5cdGV4cG9ydHMuUXVhdGVybmlvbktleWZyYW1lVHJhY2sgPSBRdWF0ZXJuaW9uS2V5ZnJhbWVUcmFjaztcblx0ZXhwb3J0cy5OdW1iZXJLZXlmcmFtZVRyYWNrID0gTnVtYmVyS2V5ZnJhbWVUcmFjaztcblx0ZXhwb3J0cy5Db2xvcktleWZyYW1lVHJhY2sgPSBDb2xvcktleWZyYW1lVHJhY2s7XG5cdGV4cG9ydHMuQm9vbGVhbktleWZyYW1lVHJhY2sgPSBCb29sZWFuS2V5ZnJhbWVUcmFjaztcblx0ZXhwb3J0cy5Qcm9wZXJ0eU1peGVyID0gUHJvcGVydHlNaXhlcjtcblx0ZXhwb3J0cy5Qcm9wZXJ0eUJpbmRpbmcgPSBQcm9wZXJ0eUJpbmRpbmc7XG5cdGV4cG9ydHMuS2V5ZnJhbWVUcmFjayA9IEtleWZyYW1lVHJhY2s7XG5cdGV4cG9ydHMuQW5pbWF0aW9uVXRpbHMgPSBBbmltYXRpb25VdGlscztcblx0ZXhwb3J0cy5BbmltYXRpb25PYmplY3RHcm91cCA9IEFuaW1hdGlvbk9iamVjdEdyb3VwO1xuXHRleHBvcnRzLkFuaW1hdGlvbk1peGVyID0gQW5pbWF0aW9uTWl4ZXI7XG5cdGV4cG9ydHMuQW5pbWF0aW9uQ2xpcCA9IEFuaW1hdGlvbkNsaXA7XG5cdGV4cG9ydHMuVW5pZm9ybSA9IFVuaWZvcm07XG5cdGV4cG9ydHMuSW5zdGFuY2VkQnVmZmVyR2VvbWV0cnkgPSBJbnN0YW5jZWRCdWZmZXJHZW9tZXRyeTtcblx0ZXhwb3J0cy5CdWZmZXJHZW9tZXRyeSA9IEJ1ZmZlckdlb21ldHJ5O1xuXHRleHBvcnRzLkdlb21ldHJ5SWRDb3VudCA9IEdlb21ldHJ5SWRDb3VudDtcblx0ZXhwb3J0cy5HZW9tZXRyeSA9IEdlb21ldHJ5O1xuXHRleHBvcnRzLkludGVybGVhdmVkQnVmZmVyQXR0cmlidXRlID0gSW50ZXJsZWF2ZWRCdWZmZXJBdHRyaWJ1dGU7XG5cdGV4cG9ydHMuSW5zdGFuY2VkSW50ZXJsZWF2ZWRCdWZmZXIgPSBJbnN0YW5jZWRJbnRlcmxlYXZlZEJ1ZmZlcjtcblx0ZXhwb3J0cy5JbnRlcmxlYXZlZEJ1ZmZlciA9IEludGVybGVhdmVkQnVmZmVyO1xuXHRleHBvcnRzLkluc3RhbmNlZEJ1ZmZlckF0dHJpYnV0ZSA9IEluc3RhbmNlZEJ1ZmZlckF0dHJpYnV0ZTtcblx0ZXhwb3J0cy5GYWNlMyA9IEZhY2UzO1xuXHRleHBvcnRzLk9iamVjdDNEID0gT2JqZWN0M0Q7XG5cdGV4cG9ydHMuUmF5Y2FzdGVyID0gUmF5Y2FzdGVyO1xuXHRleHBvcnRzLkxheWVycyA9IExheWVycztcblx0ZXhwb3J0cy5FdmVudERpc3BhdGNoZXIgPSBFdmVudERpc3BhdGNoZXI7XG5cdGV4cG9ydHMuQ2xvY2sgPSBDbG9jaztcblx0ZXhwb3J0cy5RdWF0ZXJuaW9uTGluZWFySW50ZXJwb2xhbnQgPSBRdWF0ZXJuaW9uTGluZWFySW50ZXJwb2xhbnQ7XG5cdGV4cG9ydHMuTGluZWFySW50ZXJwb2xhbnQgPSBMaW5lYXJJbnRlcnBvbGFudDtcblx0ZXhwb3J0cy5EaXNjcmV0ZUludGVycG9sYW50ID0gRGlzY3JldGVJbnRlcnBvbGFudDtcblx0ZXhwb3J0cy5DdWJpY0ludGVycG9sYW50ID0gQ3ViaWNJbnRlcnBvbGFudDtcblx0ZXhwb3J0cy5JbnRlcnBvbGFudCA9IEludGVycG9sYW50O1xuXHRleHBvcnRzLlRyaWFuZ2xlID0gVHJpYW5nbGU7XG5cdGV4cG9ydHMuU3BsaW5lID0gU3BsaW5lO1xuXHRleHBvcnRzLk1hdGggPSBfTWF0aDtcblx0ZXhwb3J0cy5TcGhlcmljYWwgPSBTcGhlcmljYWw7XG5cdGV4cG9ydHMuQ3lsaW5kcmljYWwgPSBDeWxpbmRyaWNhbDtcblx0ZXhwb3J0cy5QbGFuZSA9IFBsYW5lO1xuXHRleHBvcnRzLkZydXN0dW0gPSBGcnVzdHVtO1xuXHRleHBvcnRzLlNwaGVyZSA9IFNwaGVyZTtcblx0ZXhwb3J0cy5SYXkgPSBSYXk7XG5cdGV4cG9ydHMuTWF0cml4NCA9IE1hdHJpeDQ7XG5cdGV4cG9ydHMuTWF0cml4MyA9IE1hdHJpeDM7XG5cdGV4cG9ydHMuQm94MyA9IEJveDM7XG5cdGV4cG9ydHMuQm94MiA9IEJveDI7XG5cdGV4cG9ydHMuTGluZTMgPSBMaW5lMztcblx0ZXhwb3J0cy5FdWxlciA9IEV1bGVyO1xuXHRleHBvcnRzLlZlY3RvcjQgPSBWZWN0b3I0O1xuXHRleHBvcnRzLlZlY3RvcjMgPSBWZWN0b3IzO1xuXHRleHBvcnRzLlZlY3RvcjIgPSBWZWN0b3IyO1xuXHRleHBvcnRzLlF1YXRlcm5pb24gPSBRdWF0ZXJuaW9uO1xuXHRleHBvcnRzLkNvbG9yID0gQ29sb3I7XG5cdGV4cG9ydHMuTW9ycGhCbGVuZE1lc2ggPSBNb3JwaEJsZW5kTWVzaDtcblx0ZXhwb3J0cy5JbW1lZGlhdGVSZW5kZXJPYmplY3QgPSBJbW1lZGlhdGVSZW5kZXJPYmplY3Q7XG5cdGV4cG9ydHMuVmVydGV4Tm9ybWFsc0hlbHBlciA9IFZlcnRleE5vcm1hbHNIZWxwZXI7XG5cdGV4cG9ydHMuU3BvdExpZ2h0SGVscGVyID0gU3BvdExpZ2h0SGVscGVyO1xuXHRleHBvcnRzLlNrZWxldG9uSGVscGVyID0gU2tlbGV0b25IZWxwZXI7XG5cdGV4cG9ydHMuUG9pbnRMaWdodEhlbHBlciA9IFBvaW50TGlnaHRIZWxwZXI7XG5cdGV4cG9ydHMuUmVjdEFyZWFMaWdodEhlbHBlciA9IFJlY3RBcmVhTGlnaHRIZWxwZXI7XG5cdGV4cG9ydHMuSGVtaXNwaGVyZUxpZ2h0SGVscGVyID0gSGVtaXNwaGVyZUxpZ2h0SGVscGVyO1xuXHRleHBvcnRzLkdyaWRIZWxwZXIgPSBHcmlkSGVscGVyO1xuXHRleHBvcnRzLlBvbGFyR3JpZEhlbHBlciA9IFBvbGFyR3JpZEhlbHBlcjtcblx0ZXhwb3J0cy5GYWNlTm9ybWFsc0hlbHBlciA9IEZhY2VOb3JtYWxzSGVscGVyO1xuXHRleHBvcnRzLkRpcmVjdGlvbmFsTGlnaHRIZWxwZXIgPSBEaXJlY3Rpb25hbExpZ2h0SGVscGVyO1xuXHRleHBvcnRzLkNhbWVyYUhlbHBlciA9IENhbWVyYUhlbHBlcjtcblx0ZXhwb3J0cy5Cb3hIZWxwZXIgPSBCb3hIZWxwZXI7XG5cdGV4cG9ydHMuQXJyb3dIZWxwZXIgPSBBcnJvd0hlbHBlcjtcblx0ZXhwb3J0cy5BeGlzSGVscGVyID0gQXhpc0hlbHBlcjtcblx0ZXhwb3J0cy5DYXRtdWxsUm9tQ3VydmUzID0gQ2F0bXVsbFJvbUN1cnZlMztcblx0ZXhwb3J0cy5TcGxpbmVDdXJ2ZTMgPSBTcGxpbmVDdXJ2ZTM7XG5cdGV4cG9ydHMuQ3ViaWNCZXppZXJDdXJ2ZTMgPSBDdWJpY0JlemllckN1cnZlMztcblx0ZXhwb3J0cy5RdWFkcmF0aWNCZXppZXJDdXJ2ZTMgPSBRdWFkcmF0aWNCZXppZXJDdXJ2ZTM7XG5cdGV4cG9ydHMuTGluZUN1cnZlMyA9IExpbmVDdXJ2ZTM7XG5cdGV4cG9ydHMuQXJjQ3VydmUgPSBBcmNDdXJ2ZTtcblx0ZXhwb3J0cy5FbGxpcHNlQ3VydmUgPSBFbGxpcHNlQ3VydmU7XG5cdGV4cG9ydHMuU3BsaW5lQ3VydmUgPSBTcGxpbmVDdXJ2ZTtcblx0ZXhwb3J0cy5DdWJpY0JlemllckN1cnZlID0gQ3ViaWNCZXppZXJDdXJ2ZTtcblx0ZXhwb3J0cy5RdWFkcmF0aWNCZXppZXJDdXJ2ZSA9IFF1YWRyYXRpY0JlemllckN1cnZlO1xuXHRleHBvcnRzLkxpbmVDdXJ2ZSA9IExpbmVDdXJ2ZTtcblx0ZXhwb3J0cy5TaGFwZSA9IFNoYXBlO1xuXHRleHBvcnRzLlNoYXBlUGF0aCA9IFNoYXBlUGF0aDtcblx0ZXhwb3J0cy5QYXRoID0gUGF0aDtcblx0ZXhwb3J0cy5Gb250ID0gRm9udDtcblx0ZXhwb3J0cy5DdXJ2ZVBhdGggPSBDdXJ2ZVBhdGg7XG5cdGV4cG9ydHMuQ3VydmUgPSBDdXJ2ZTtcblx0ZXhwb3J0cy5TaGFwZVV0aWxzID0gU2hhcGVVdGlscztcblx0ZXhwb3J0cy5TY2VuZVV0aWxzID0gU2NlbmVVdGlscztcblx0ZXhwb3J0cy5DdXJ2ZVV0aWxzID0gQ3VydmVVdGlscztcblx0ZXhwb3J0cy5XaXJlZnJhbWVHZW9tZXRyeSA9IFdpcmVmcmFtZUdlb21ldHJ5O1xuXHRleHBvcnRzLlBhcmFtZXRyaWNHZW9tZXRyeSA9IFBhcmFtZXRyaWNHZW9tZXRyeTtcblx0ZXhwb3J0cy5QYXJhbWV0cmljQnVmZmVyR2VvbWV0cnkgPSBQYXJhbWV0cmljQnVmZmVyR2VvbWV0cnk7XG5cdGV4cG9ydHMuVGV0cmFoZWRyb25HZW9tZXRyeSA9IFRldHJhaGVkcm9uR2VvbWV0cnk7XG5cdGV4cG9ydHMuVGV0cmFoZWRyb25CdWZmZXJHZW9tZXRyeSA9IFRldHJhaGVkcm9uQnVmZmVyR2VvbWV0cnk7XG5cdGV4cG9ydHMuT2N0YWhlZHJvbkdlb21ldHJ5ID0gT2N0YWhlZHJvbkdlb21ldHJ5O1xuXHRleHBvcnRzLk9jdGFoZWRyb25CdWZmZXJHZW9tZXRyeSA9IE9jdGFoZWRyb25CdWZmZXJHZW9tZXRyeTtcblx0ZXhwb3J0cy5JY29zYWhlZHJvbkdlb21ldHJ5ID0gSWNvc2FoZWRyb25HZW9tZXRyeTtcblx0ZXhwb3J0cy5JY29zYWhlZHJvbkJ1ZmZlckdlb21ldHJ5ID0gSWNvc2FoZWRyb25CdWZmZXJHZW9tZXRyeTtcblx0ZXhwb3J0cy5Eb2RlY2FoZWRyb25HZW9tZXRyeSA9IERvZGVjYWhlZHJvbkdlb21ldHJ5O1xuXHRleHBvcnRzLkRvZGVjYWhlZHJvbkJ1ZmZlckdlb21ldHJ5ID0gRG9kZWNhaGVkcm9uQnVmZmVyR2VvbWV0cnk7XG5cdGV4cG9ydHMuUG9seWhlZHJvbkdlb21ldHJ5ID0gUG9seWhlZHJvbkdlb21ldHJ5O1xuXHRleHBvcnRzLlBvbHloZWRyb25CdWZmZXJHZW9tZXRyeSA9IFBvbHloZWRyb25CdWZmZXJHZW9tZXRyeTtcblx0ZXhwb3J0cy5UdWJlR2VvbWV0cnkgPSBUdWJlR2VvbWV0cnk7XG5cdGV4cG9ydHMuVHViZUJ1ZmZlckdlb21ldHJ5ID0gVHViZUJ1ZmZlckdlb21ldHJ5O1xuXHRleHBvcnRzLlRvcnVzS25vdEdlb21ldHJ5ID0gVG9ydXNLbm90R2VvbWV0cnk7XG5cdGV4cG9ydHMuVG9ydXNLbm90QnVmZmVyR2VvbWV0cnkgPSBUb3J1c0tub3RCdWZmZXJHZW9tZXRyeTtcblx0ZXhwb3J0cy5Ub3J1c0dlb21ldHJ5ID0gVG9ydXNHZW9tZXRyeTtcblx0ZXhwb3J0cy5Ub3J1c0J1ZmZlckdlb21ldHJ5ID0gVG9ydXNCdWZmZXJHZW9tZXRyeTtcblx0ZXhwb3J0cy5UZXh0R2VvbWV0cnkgPSBUZXh0R2VvbWV0cnk7XG5cdGV4cG9ydHMuU3BoZXJlQnVmZmVyR2VvbWV0cnkgPSBTcGhlcmVCdWZmZXJHZW9tZXRyeTtcblx0ZXhwb3J0cy5TcGhlcmVHZW9tZXRyeSA9IFNwaGVyZUdlb21ldHJ5O1xuXHRleHBvcnRzLlJpbmdHZW9tZXRyeSA9IFJpbmdHZW9tZXRyeTtcblx0ZXhwb3J0cy5SaW5nQnVmZmVyR2VvbWV0cnkgPSBSaW5nQnVmZmVyR2VvbWV0cnk7XG5cdGV4cG9ydHMuUGxhbmVCdWZmZXJHZW9tZXRyeSA9IFBsYW5lQnVmZmVyR2VvbWV0cnk7XG5cdGV4cG9ydHMuUGxhbmVHZW9tZXRyeSA9IFBsYW5lR2VvbWV0cnk7XG5cdGV4cG9ydHMuTGF0aGVHZW9tZXRyeSA9IExhdGhlR2VvbWV0cnk7XG5cdGV4cG9ydHMuTGF0aGVCdWZmZXJHZW9tZXRyeSA9IExhdGhlQnVmZmVyR2VvbWV0cnk7XG5cdGV4cG9ydHMuU2hhcGVHZW9tZXRyeSA9IFNoYXBlR2VvbWV0cnk7XG5cdGV4cG9ydHMuU2hhcGVCdWZmZXJHZW9tZXRyeSA9IFNoYXBlQnVmZmVyR2VvbWV0cnk7XG5cdGV4cG9ydHMuRXh0cnVkZUdlb21ldHJ5ID0gRXh0cnVkZUdlb21ldHJ5O1xuXHRleHBvcnRzLkVkZ2VzR2VvbWV0cnkgPSBFZGdlc0dlb21ldHJ5O1xuXHRleHBvcnRzLkNvbmVHZW9tZXRyeSA9IENvbmVHZW9tZXRyeTtcblx0ZXhwb3J0cy5Db25lQnVmZmVyR2VvbWV0cnkgPSBDb25lQnVmZmVyR2VvbWV0cnk7XG5cdGV4cG9ydHMuQ3lsaW5kZXJHZW9tZXRyeSA9IEN5bGluZGVyR2VvbWV0cnk7XG5cdGV4cG9ydHMuQ3lsaW5kZXJCdWZmZXJHZW9tZXRyeSA9IEN5bGluZGVyQnVmZmVyR2VvbWV0cnk7XG5cdGV4cG9ydHMuQ2lyY2xlQnVmZmVyR2VvbWV0cnkgPSBDaXJjbGVCdWZmZXJHZW9tZXRyeTtcblx0ZXhwb3J0cy5DaXJjbGVHZW9tZXRyeSA9IENpcmNsZUdlb21ldHJ5O1xuXHRleHBvcnRzLkJveEJ1ZmZlckdlb21ldHJ5ID0gQm94QnVmZmVyR2VvbWV0cnk7XG5cdGV4cG9ydHMuQm94R2VvbWV0cnkgPSBCb3hHZW9tZXRyeTtcblx0ZXhwb3J0cy5TaGFkb3dNYXRlcmlhbCA9IFNoYWRvd01hdGVyaWFsO1xuXHRleHBvcnRzLlNwcml0ZU1hdGVyaWFsID0gU3ByaXRlTWF0ZXJpYWw7XG5cdGV4cG9ydHMuUmF3U2hhZGVyTWF0ZXJpYWwgPSBSYXdTaGFkZXJNYXRlcmlhbDtcblx0ZXhwb3J0cy5TaGFkZXJNYXRlcmlhbCA9IFNoYWRlck1hdGVyaWFsO1xuXHRleHBvcnRzLlBvaW50c01hdGVyaWFsID0gUG9pbnRzTWF0ZXJpYWw7XG5cdGV4cG9ydHMuTXVsdGlNYXRlcmlhbCA9IE11bHRpTWF0ZXJpYWw7XG5cdGV4cG9ydHMuTWVzaFBoeXNpY2FsTWF0ZXJpYWwgPSBNZXNoUGh5c2ljYWxNYXRlcmlhbDtcblx0ZXhwb3J0cy5NZXNoU3RhbmRhcmRNYXRlcmlhbCA9IE1lc2hTdGFuZGFyZE1hdGVyaWFsO1xuXHRleHBvcnRzLk1lc2hQaG9uZ01hdGVyaWFsID0gTWVzaFBob25nTWF0ZXJpYWw7XG5cdGV4cG9ydHMuTWVzaFRvb25NYXRlcmlhbCA9IE1lc2hUb29uTWF0ZXJpYWw7XG5cdGV4cG9ydHMuTWVzaE5vcm1hbE1hdGVyaWFsID0gTWVzaE5vcm1hbE1hdGVyaWFsO1xuXHRleHBvcnRzLk1lc2hMYW1iZXJ0TWF0ZXJpYWwgPSBNZXNoTGFtYmVydE1hdGVyaWFsO1xuXHRleHBvcnRzLk1lc2hEZXB0aE1hdGVyaWFsID0gTWVzaERlcHRoTWF0ZXJpYWw7XG5cdGV4cG9ydHMuTWVzaEJhc2ljTWF0ZXJpYWwgPSBNZXNoQmFzaWNNYXRlcmlhbDtcblx0ZXhwb3J0cy5MaW5lRGFzaGVkTWF0ZXJpYWwgPSBMaW5lRGFzaGVkTWF0ZXJpYWw7XG5cdGV4cG9ydHMuTGluZUJhc2ljTWF0ZXJpYWwgPSBMaW5lQmFzaWNNYXRlcmlhbDtcblx0ZXhwb3J0cy5NYXRlcmlhbCA9IE1hdGVyaWFsO1xuXHRleHBvcnRzLkZsb2F0NjRCdWZmZXJBdHRyaWJ1dGUgPSBGbG9hdDY0QnVmZmVyQXR0cmlidXRlO1xuXHRleHBvcnRzLkZsb2F0MzJCdWZmZXJBdHRyaWJ1dGUgPSBGbG9hdDMyQnVmZmVyQXR0cmlidXRlO1xuXHRleHBvcnRzLlVpbnQzMkJ1ZmZlckF0dHJpYnV0ZSA9IFVpbnQzMkJ1ZmZlckF0dHJpYnV0ZTtcblx0ZXhwb3J0cy5JbnQzMkJ1ZmZlckF0dHJpYnV0ZSA9IEludDMyQnVmZmVyQXR0cmlidXRlO1xuXHRleHBvcnRzLlVpbnQxNkJ1ZmZlckF0dHJpYnV0ZSA9IFVpbnQxNkJ1ZmZlckF0dHJpYnV0ZTtcblx0ZXhwb3J0cy5JbnQxNkJ1ZmZlckF0dHJpYnV0ZSA9IEludDE2QnVmZmVyQXR0cmlidXRlO1xuXHRleHBvcnRzLlVpbnQ4Q2xhbXBlZEJ1ZmZlckF0dHJpYnV0ZSA9IFVpbnQ4Q2xhbXBlZEJ1ZmZlckF0dHJpYnV0ZTtcblx0ZXhwb3J0cy5VaW50OEJ1ZmZlckF0dHJpYnV0ZSA9IFVpbnQ4QnVmZmVyQXR0cmlidXRlO1xuXHRleHBvcnRzLkludDhCdWZmZXJBdHRyaWJ1dGUgPSBJbnQ4QnVmZmVyQXR0cmlidXRlO1xuXHRleHBvcnRzLkJ1ZmZlckF0dHJpYnV0ZSA9IEJ1ZmZlckF0dHJpYnV0ZTtcblx0ZXhwb3J0cy5SRVZJU0lPTiA9IFJFVklTSU9OO1xuXHRleHBvcnRzLk1PVVNFID0gTU9VU0U7XG5cdGV4cG9ydHMuQ3VsbEZhY2VOb25lID0gQ3VsbEZhY2VOb25lO1xuXHRleHBvcnRzLkN1bGxGYWNlQmFjayA9IEN1bGxGYWNlQmFjaztcblx0ZXhwb3J0cy5DdWxsRmFjZUZyb250ID0gQ3VsbEZhY2VGcm9udDtcblx0ZXhwb3J0cy5DdWxsRmFjZUZyb250QmFjayA9IEN1bGxGYWNlRnJvbnRCYWNrO1xuXHRleHBvcnRzLkZyb250RmFjZURpcmVjdGlvbkNXID0gRnJvbnRGYWNlRGlyZWN0aW9uQ1c7XG5cdGV4cG9ydHMuRnJvbnRGYWNlRGlyZWN0aW9uQ0NXID0gRnJvbnRGYWNlRGlyZWN0aW9uQ0NXO1xuXHRleHBvcnRzLkJhc2ljU2hhZG93TWFwID0gQmFzaWNTaGFkb3dNYXA7XG5cdGV4cG9ydHMuUENGU2hhZG93TWFwID0gUENGU2hhZG93TWFwO1xuXHRleHBvcnRzLlBDRlNvZnRTaGFkb3dNYXAgPSBQQ0ZTb2Z0U2hhZG93TWFwO1xuXHRleHBvcnRzLkZyb250U2lkZSA9IEZyb250U2lkZTtcblx0ZXhwb3J0cy5CYWNrU2lkZSA9IEJhY2tTaWRlO1xuXHRleHBvcnRzLkRvdWJsZVNpZGUgPSBEb3VibGVTaWRlO1xuXHRleHBvcnRzLkZsYXRTaGFkaW5nID0gRmxhdFNoYWRpbmc7XG5cdGV4cG9ydHMuU21vb3RoU2hhZGluZyA9IFNtb290aFNoYWRpbmc7XG5cdGV4cG9ydHMuTm9Db2xvcnMgPSBOb0NvbG9ycztcblx0ZXhwb3J0cy5GYWNlQ29sb3JzID0gRmFjZUNvbG9ycztcblx0ZXhwb3J0cy5WZXJ0ZXhDb2xvcnMgPSBWZXJ0ZXhDb2xvcnM7XG5cdGV4cG9ydHMuTm9CbGVuZGluZyA9IE5vQmxlbmRpbmc7XG5cdGV4cG9ydHMuTm9ybWFsQmxlbmRpbmcgPSBOb3JtYWxCbGVuZGluZztcblx0ZXhwb3J0cy5BZGRpdGl2ZUJsZW5kaW5nID0gQWRkaXRpdmVCbGVuZGluZztcblx0ZXhwb3J0cy5TdWJ0cmFjdGl2ZUJsZW5kaW5nID0gU3VidHJhY3RpdmVCbGVuZGluZztcblx0ZXhwb3J0cy5NdWx0aXBseUJsZW5kaW5nID0gTXVsdGlwbHlCbGVuZGluZztcblx0ZXhwb3J0cy5DdXN0b21CbGVuZGluZyA9IEN1c3RvbUJsZW5kaW5nO1xuXHRleHBvcnRzLkJsZW5kaW5nTW9kZSA9IEJsZW5kaW5nTW9kZTtcblx0ZXhwb3J0cy5BZGRFcXVhdGlvbiA9IEFkZEVxdWF0aW9uO1xuXHRleHBvcnRzLlN1YnRyYWN0RXF1YXRpb24gPSBTdWJ0cmFjdEVxdWF0aW9uO1xuXHRleHBvcnRzLlJldmVyc2VTdWJ0cmFjdEVxdWF0aW9uID0gUmV2ZXJzZVN1YnRyYWN0RXF1YXRpb247XG5cdGV4cG9ydHMuTWluRXF1YXRpb24gPSBNaW5FcXVhdGlvbjtcblx0ZXhwb3J0cy5NYXhFcXVhdGlvbiA9IE1heEVxdWF0aW9uO1xuXHRleHBvcnRzLlplcm9GYWN0b3IgPSBaZXJvRmFjdG9yO1xuXHRleHBvcnRzLk9uZUZhY3RvciA9IE9uZUZhY3Rvcjtcblx0ZXhwb3J0cy5TcmNDb2xvckZhY3RvciA9IFNyY0NvbG9yRmFjdG9yO1xuXHRleHBvcnRzLk9uZU1pbnVzU3JjQ29sb3JGYWN0b3IgPSBPbmVNaW51c1NyY0NvbG9yRmFjdG9yO1xuXHRleHBvcnRzLlNyY0FscGhhRmFjdG9yID0gU3JjQWxwaGFGYWN0b3I7XG5cdGV4cG9ydHMuT25lTWludXNTcmNBbHBoYUZhY3RvciA9IE9uZU1pbnVzU3JjQWxwaGFGYWN0b3I7XG5cdGV4cG9ydHMuRHN0QWxwaGFGYWN0b3IgPSBEc3RBbHBoYUZhY3Rvcjtcblx0ZXhwb3J0cy5PbmVNaW51c0RzdEFscGhhRmFjdG9yID0gT25lTWludXNEc3RBbHBoYUZhY3Rvcjtcblx0ZXhwb3J0cy5Ec3RDb2xvckZhY3RvciA9IERzdENvbG9yRmFjdG9yO1xuXHRleHBvcnRzLk9uZU1pbnVzRHN0Q29sb3JGYWN0b3IgPSBPbmVNaW51c0RzdENvbG9yRmFjdG9yO1xuXHRleHBvcnRzLlNyY0FscGhhU2F0dXJhdGVGYWN0b3IgPSBTcmNBbHBoYVNhdHVyYXRlRmFjdG9yO1xuXHRleHBvcnRzLk5ldmVyRGVwdGggPSBOZXZlckRlcHRoO1xuXHRleHBvcnRzLkFsd2F5c0RlcHRoID0gQWx3YXlzRGVwdGg7XG5cdGV4cG9ydHMuTGVzc0RlcHRoID0gTGVzc0RlcHRoO1xuXHRleHBvcnRzLkxlc3NFcXVhbERlcHRoID0gTGVzc0VxdWFsRGVwdGg7XG5cdGV4cG9ydHMuRXF1YWxEZXB0aCA9IEVxdWFsRGVwdGg7XG5cdGV4cG9ydHMuR3JlYXRlckVxdWFsRGVwdGggPSBHcmVhdGVyRXF1YWxEZXB0aDtcblx0ZXhwb3J0cy5HcmVhdGVyRGVwdGggPSBHcmVhdGVyRGVwdGg7XG5cdGV4cG9ydHMuTm90RXF1YWxEZXB0aCA9IE5vdEVxdWFsRGVwdGg7XG5cdGV4cG9ydHMuTXVsdGlwbHlPcGVyYXRpb24gPSBNdWx0aXBseU9wZXJhdGlvbjtcblx0ZXhwb3J0cy5NaXhPcGVyYXRpb24gPSBNaXhPcGVyYXRpb247XG5cdGV4cG9ydHMuQWRkT3BlcmF0aW9uID0gQWRkT3BlcmF0aW9uO1xuXHRleHBvcnRzLk5vVG9uZU1hcHBpbmcgPSBOb1RvbmVNYXBwaW5nO1xuXHRleHBvcnRzLkxpbmVhclRvbmVNYXBwaW5nID0gTGluZWFyVG9uZU1hcHBpbmc7XG5cdGV4cG9ydHMuUmVpbmhhcmRUb25lTWFwcGluZyA9IFJlaW5oYXJkVG9uZU1hcHBpbmc7XG5cdGV4cG9ydHMuVW5jaGFydGVkMlRvbmVNYXBwaW5nID0gVW5jaGFydGVkMlRvbmVNYXBwaW5nO1xuXHRleHBvcnRzLkNpbmVvblRvbmVNYXBwaW5nID0gQ2luZW9uVG9uZU1hcHBpbmc7XG5cdGV4cG9ydHMuVVZNYXBwaW5nID0gVVZNYXBwaW5nO1xuXHRleHBvcnRzLkN1YmVSZWZsZWN0aW9uTWFwcGluZyA9IEN1YmVSZWZsZWN0aW9uTWFwcGluZztcblx0ZXhwb3J0cy5DdWJlUmVmcmFjdGlvbk1hcHBpbmcgPSBDdWJlUmVmcmFjdGlvbk1hcHBpbmc7XG5cdGV4cG9ydHMuRXF1aXJlY3Rhbmd1bGFyUmVmbGVjdGlvbk1hcHBpbmcgPSBFcXVpcmVjdGFuZ3VsYXJSZWZsZWN0aW9uTWFwcGluZztcblx0ZXhwb3J0cy5FcXVpcmVjdGFuZ3VsYXJSZWZyYWN0aW9uTWFwcGluZyA9IEVxdWlyZWN0YW5ndWxhclJlZnJhY3Rpb25NYXBwaW5nO1xuXHRleHBvcnRzLlNwaGVyaWNhbFJlZmxlY3Rpb25NYXBwaW5nID0gU3BoZXJpY2FsUmVmbGVjdGlvbk1hcHBpbmc7XG5cdGV4cG9ydHMuQ3ViZVVWUmVmbGVjdGlvbk1hcHBpbmcgPSBDdWJlVVZSZWZsZWN0aW9uTWFwcGluZztcblx0ZXhwb3J0cy5DdWJlVVZSZWZyYWN0aW9uTWFwcGluZyA9IEN1YmVVVlJlZnJhY3Rpb25NYXBwaW5nO1xuXHRleHBvcnRzLlRleHR1cmVNYXBwaW5nID0gVGV4dHVyZU1hcHBpbmc7XG5cdGV4cG9ydHMuUmVwZWF0V3JhcHBpbmcgPSBSZXBlYXRXcmFwcGluZztcblx0ZXhwb3J0cy5DbGFtcFRvRWRnZVdyYXBwaW5nID0gQ2xhbXBUb0VkZ2VXcmFwcGluZztcblx0ZXhwb3J0cy5NaXJyb3JlZFJlcGVhdFdyYXBwaW5nID0gTWlycm9yZWRSZXBlYXRXcmFwcGluZztcblx0ZXhwb3J0cy5UZXh0dXJlV3JhcHBpbmcgPSBUZXh0dXJlV3JhcHBpbmc7XG5cdGV4cG9ydHMuTmVhcmVzdEZpbHRlciA9IE5lYXJlc3RGaWx0ZXI7XG5cdGV4cG9ydHMuTmVhcmVzdE1pcE1hcE5lYXJlc3RGaWx0ZXIgPSBOZWFyZXN0TWlwTWFwTmVhcmVzdEZpbHRlcjtcblx0ZXhwb3J0cy5OZWFyZXN0TWlwTWFwTGluZWFyRmlsdGVyID0gTmVhcmVzdE1pcE1hcExpbmVhckZpbHRlcjtcblx0ZXhwb3J0cy5MaW5lYXJGaWx0ZXIgPSBMaW5lYXJGaWx0ZXI7XG5cdGV4cG9ydHMuTGluZWFyTWlwTWFwTmVhcmVzdEZpbHRlciA9IExpbmVhck1pcE1hcE5lYXJlc3RGaWx0ZXI7XG5cdGV4cG9ydHMuTGluZWFyTWlwTWFwTGluZWFyRmlsdGVyID0gTGluZWFyTWlwTWFwTGluZWFyRmlsdGVyO1xuXHRleHBvcnRzLlRleHR1cmVGaWx0ZXIgPSBUZXh0dXJlRmlsdGVyO1xuXHRleHBvcnRzLlVuc2lnbmVkQnl0ZVR5cGUgPSBVbnNpZ25lZEJ5dGVUeXBlO1xuXHRleHBvcnRzLkJ5dGVUeXBlID0gQnl0ZVR5cGU7XG5cdGV4cG9ydHMuU2hvcnRUeXBlID0gU2hvcnRUeXBlO1xuXHRleHBvcnRzLlVuc2lnbmVkU2hvcnRUeXBlID0gVW5zaWduZWRTaG9ydFR5cGU7XG5cdGV4cG9ydHMuSW50VHlwZSA9IEludFR5cGU7XG5cdGV4cG9ydHMuVW5zaWduZWRJbnRUeXBlID0gVW5zaWduZWRJbnRUeXBlO1xuXHRleHBvcnRzLkZsb2F0VHlwZSA9IEZsb2F0VHlwZTtcblx0ZXhwb3J0cy5IYWxmRmxvYXRUeXBlID0gSGFsZkZsb2F0VHlwZTtcblx0ZXhwb3J0cy5VbnNpZ25lZFNob3J0NDQ0NFR5cGUgPSBVbnNpZ25lZFNob3J0NDQ0NFR5cGU7XG5cdGV4cG9ydHMuVW5zaWduZWRTaG9ydDU1NTFUeXBlID0gVW5zaWduZWRTaG9ydDU1NTFUeXBlO1xuXHRleHBvcnRzLlVuc2lnbmVkU2hvcnQ1NjVUeXBlID0gVW5zaWduZWRTaG9ydDU2NVR5cGU7XG5cdGV4cG9ydHMuVW5zaWduZWRJbnQyNDhUeXBlID0gVW5zaWduZWRJbnQyNDhUeXBlO1xuXHRleHBvcnRzLkFscGhhRm9ybWF0ID0gQWxwaGFGb3JtYXQ7XG5cdGV4cG9ydHMuUkdCRm9ybWF0ID0gUkdCRm9ybWF0O1xuXHRleHBvcnRzLlJHQkFGb3JtYXQgPSBSR0JBRm9ybWF0O1xuXHRleHBvcnRzLkx1bWluYW5jZUZvcm1hdCA9IEx1bWluYW5jZUZvcm1hdDtcblx0ZXhwb3J0cy5MdW1pbmFuY2VBbHBoYUZvcm1hdCA9IEx1bWluYW5jZUFscGhhRm9ybWF0O1xuXHRleHBvcnRzLlJHQkVGb3JtYXQgPSBSR0JFRm9ybWF0O1xuXHRleHBvcnRzLkRlcHRoRm9ybWF0ID0gRGVwdGhGb3JtYXQ7XG5cdGV4cG9ydHMuRGVwdGhTdGVuY2lsRm9ybWF0ID0gRGVwdGhTdGVuY2lsRm9ybWF0O1xuXHRleHBvcnRzLlJHQl9TM1RDX0RYVDFfRm9ybWF0ID0gUkdCX1MzVENfRFhUMV9Gb3JtYXQ7XG5cdGV4cG9ydHMuUkdCQV9TM1RDX0RYVDFfRm9ybWF0ID0gUkdCQV9TM1RDX0RYVDFfRm9ybWF0O1xuXHRleHBvcnRzLlJHQkFfUzNUQ19EWFQzX0Zvcm1hdCA9IFJHQkFfUzNUQ19EWFQzX0Zvcm1hdDtcblx0ZXhwb3J0cy5SR0JBX1MzVENfRFhUNV9Gb3JtYXQgPSBSR0JBX1MzVENfRFhUNV9Gb3JtYXQ7XG5cdGV4cG9ydHMuUkdCX1BWUlRDXzRCUFBWMV9Gb3JtYXQgPSBSR0JfUFZSVENfNEJQUFYxX0Zvcm1hdDtcblx0ZXhwb3J0cy5SR0JfUFZSVENfMkJQUFYxX0Zvcm1hdCA9IFJHQl9QVlJUQ18yQlBQVjFfRm9ybWF0O1xuXHRleHBvcnRzLlJHQkFfUFZSVENfNEJQUFYxX0Zvcm1hdCA9IFJHQkFfUFZSVENfNEJQUFYxX0Zvcm1hdDtcblx0ZXhwb3J0cy5SR0JBX1BWUlRDXzJCUFBWMV9Gb3JtYXQgPSBSR0JBX1BWUlRDXzJCUFBWMV9Gb3JtYXQ7XG5cdGV4cG9ydHMuUkdCX0VUQzFfRm9ybWF0ID0gUkdCX0VUQzFfRm9ybWF0O1xuXHRleHBvcnRzLkxvb3BPbmNlID0gTG9vcE9uY2U7XG5cdGV4cG9ydHMuTG9vcFJlcGVhdCA9IExvb3BSZXBlYXQ7XG5cdGV4cG9ydHMuTG9vcFBpbmdQb25nID0gTG9vcFBpbmdQb25nO1xuXHRleHBvcnRzLkludGVycG9sYXRlRGlzY3JldGUgPSBJbnRlcnBvbGF0ZURpc2NyZXRlO1xuXHRleHBvcnRzLkludGVycG9sYXRlTGluZWFyID0gSW50ZXJwb2xhdGVMaW5lYXI7XG5cdGV4cG9ydHMuSW50ZXJwb2xhdGVTbW9vdGggPSBJbnRlcnBvbGF0ZVNtb290aDtcblx0ZXhwb3J0cy5aZXJvQ3VydmF0dXJlRW5kaW5nID0gWmVyb0N1cnZhdHVyZUVuZGluZztcblx0ZXhwb3J0cy5aZXJvU2xvcGVFbmRpbmcgPSBaZXJvU2xvcGVFbmRpbmc7XG5cdGV4cG9ydHMuV3JhcEFyb3VuZEVuZGluZyA9IFdyYXBBcm91bmRFbmRpbmc7XG5cdGV4cG9ydHMuVHJpYW5nbGVzRHJhd01vZGUgPSBUcmlhbmdsZXNEcmF3TW9kZTtcblx0ZXhwb3J0cy5UcmlhbmdsZVN0cmlwRHJhd01vZGUgPSBUcmlhbmdsZVN0cmlwRHJhd01vZGU7XG5cdGV4cG9ydHMuVHJpYW5nbGVGYW5EcmF3TW9kZSA9IFRyaWFuZ2xlRmFuRHJhd01vZGU7XG5cdGV4cG9ydHMuTGluZWFyRW5jb2RpbmcgPSBMaW5lYXJFbmNvZGluZztcblx0ZXhwb3J0cy5zUkdCRW5jb2RpbmcgPSBzUkdCRW5jb2Rpbmc7XG5cdGV4cG9ydHMuR2FtbWFFbmNvZGluZyA9IEdhbW1hRW5jb2Rpbmc7XG5cdGV4cG9ydHMuUkdCRUVuY29kaW5nID0gUkdCRUVuY29kaW5nO1xuXHRleHBvcnRzLkxvZ0x1dkVuY29kaW5nID0gTG9nTHV2RW5jb2Rpbmc7XG5cdGV4cG9ydHMuUkdCTTdFbmNvZGluZyA9IFJHQk03RW5jb2Rpbmc7XG5cdGV4cG9ydHMuUkdCTTE2RW5jb2RpbmcgPSBSR0JNMTZFbmNvZGluZztcblx0ZXhwb3J0cy5SR0JERW5jb2RpbmcgPSBSR0JERW5jb2Rpbmc7XG5cdGV4cG9ydHMuQmFzaWNEZXB0aFBhY2tpbmcgPSBCYXNpY0RlcHRoUGFja2luZztcblx0ZXhwb3J0cy5SR0JBRGVwdGhQYWNraW5nID0gUkdCQURlcHRoUGFja2luZztcblx0ZXhwb3J0cy5DdWJlR2VvbWV0cnkgPSBCb3hHZW9tZXRyeTtcblx0ZXhwb3J0cy5GYWNlNCA9IEZhY2U0O1xuXHRleHBvcnRzLkxpbmVTdHJpcCA9IExpbmVTdHJpcDtcblx0ZXhwb3J0cy5MaW5lUGllY2VzID0gTGluZVBpZWNlcztcblx0ZXhwb3J0cy5NZXNoRmFjZU1hdGVyaWFsID0gTWVzaEZhY2VNYXRlcmlhbDtcblx0ZXhwb3J0cy5Qb2ludENsb3VkID0gUG9pbnRDbG91ZDtcblx0ZXhwb3J0cy5QYXJ0aWNsZSA9IFBhcnRpY2xlO1xuXHRleHBvcnRzLlBhcnRpY2xlU3lzdGVtID0gUGFydGljbGVTeXN0ZW07XG5cdGV4cG9ydHMuUG9pbnRDbG91ZE1hdGVyaWFsID0gUG9pbnRDbG91ZE1hdGVyaWFsO1xuXHRleHBvcnRzLlBhcnRpY2xlQmFzaWNNYXRlcmlhbCA9IFBhcnRpY2xlQmFzaWNNYXRlcmlhbDtcblx0ZXhwb3J0cy5QYXJ0aWNsZVN5c3RlbU1hdGVyaWFsID0gUGFydGljbGVTeXN0ZW1NYXRlcmlhbDtcblx0ZXhwb3J0cy5WZXJ0ZXggPSBWZXJ0ZXg7XG5cdGV4cG9ydHMuRHluYW1pY0J1ZmZlckF0dHJpYnV0ZSA9IER5bmFtaWNCdWZmZXJBdHRyaWJ1dGU7XG5cdGV4cG9ydHMuSW50OEF0dHJpYnV0ZSA9IEludDhBdHRyaWJ1dGU7XG5cdGV4cG9ydHMuVWludDhBdHRyaWJ1dGUgPSBVaW50OEF0dHJpYnV0ZTtcblx0ZXhwb3J0cy5VaW50OENsYW1wZWRBdHRyaWJ1dGUgPSBVaW50OENsYW1wZWRBdHRyaWJ1dGU7XG5cdGV4cG9ydHMuSW50MTZBdHRyaWJ1dGUgPSBJbnQxNkF0dHJpYnV0ZTtcblx0ZXhwb3J0cy5VaW50MTZBdHRyaWJ1dGUgPSBVaW50MTZBdHRyaWJ1dGU7XG5cdGV4cG9ydHMuSW50MzJBdHRyaWJ1dGUgPSBJbnQzMkF0dHJpYnV0ZTtcblx0ZXhwb3J0cy5VaW50MzJBdHRyaWJ1dGUgPSBVaW50MzJBdHRyaWJ1dGU7XG5cdGV4cG9ydHMuRmxvYXQzMkF0dHJpYnV0ZSA9IEZsb2F0MzJBdHRyaWJ1dGU7XG5cdGV4cG9ydHMuRmxvYXQ2NEF0dHJpYnV0ZSA9IEZsb2F0NjRBdHRyaWJ1dGU7XG5cdGV4cG9ydHMuQ2xvc2VkU3BsaW5lQ3VydmUzID0gQ2xvc2VkU3BsaW5lQ3VydmUzO1xuXHRleHBvcnRzLkJvdW5kaW5nQm94SGVscGVyID0gQm91bmRpbmdCb3hIZWxwZXI7XG5cdGV4cG9ydHMuRWRnZXNIZWxwZXIgPSBFZGdlc0hlbHBlcjtcblx0ZXhwb3J0cy5XaXJlZnJhbWVIZWxwZXIgPSBXaXJlZnJhbWVIZWxwZXI7XG5cdGV4cG9ydHMuWEhSTG9hZGVyID0gWEhSTG9hZGVyO1xuXHRleHBvcnRzLkdlb21ldHJ5VXRpbHMgPSBHZW9tZXRyeVV0aWxzO1xuXHRleHBvcnRzLkltYWdlVXRpbHMgPSBJbWFnZVV0aWxzO1xuXHRleHBvcnRzLlByb2plY3RvciA9IFByb2plY3Rvcjtcblx0ZXhwb3J0cy5DYW52YXNSZW5kZXJlciA9IENhbnZhc1JlbmRlcmVyO1xuXG5cdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG5cbn0pKSk7XG4iLCIvKipcbiAqIEBhdXRob3IgZG1hcmNvcyAvIGh0dHBzOi8vZ2l0aHViLmNvbS9kbWFyY29zXG4gKiBAYXV0aG9yIG1yZG9vYiAvIGh0dHA6Ly9tcmRvb2IuY29tXG4gKi9cblxuVEhSRUUuVlJDb250cm9scyA9IGZ1bmN0aW9uICggb2JqZWN0LCBvbkVycm9yICkge1xuXG5cdHZhciBzY29wZSA9IHRoaXM7XG5cblx0dmFyIHZyRGlzcGxheSwgdnJEaXNwbGF5cztcblxuXHR2YXIgc3RhbmRpbmdNYXRyaXggPSBuZXcgVEhSRUUuTWF0cml4NCgpO1xuXG5cdHZhciBmcmFtZURhdGEgPSBudWxsO1xuXG5cdGlmICggJ1ZSRnJhbWVEYXRhJyBpbiB3aW5kb3cgKSB7XG5cblx0XHRmcmFtZURhdGEgPSBuZXcgVlJGcmFtZURhdGEoKTtcblxuXHR9XG5cblx0ZnVuY3Rpb24gZ290VlJEaXNwbGF5cyggZGlzcGxheXMgKSB7XG5cblx0XHR2ckRpc3BsYXlzID0gZGlzcGxheXM7XG5cblx0XHRpZiAoIGRpc3BsYXlzLmxlbmd0aCA+IDAgKSB7XG5cblx0XHRcdHZyRGlzcGxheSA9IGRpc3BsYXlzWyAwIF07XG5cblx0XHR9IGVsc2Uge1xuXG5cdFx0XHRpZiAoIG9uRXJyb3IgKSBvbkVycm9yKCAnVlIgaW5wdXQgbm90IGF2YWlsYWJsZS4nICk7XG5cblx0XHR9XG5cblx0fVxuXG5cdGlmICggbmF2aWdhdG9yLmdldFZSRGlzcGxheXMgKSB7XG5cblx0XHRuYXZpZ2F0b3IuZ2V0VlJEaXNwbGF5cygpLnRoZW4oIGdvdFZSRGlzcGxheXMgKS5jYXRjaCAoIGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuVlJDb250cm9sczogVW5hYmxlIHRvIGdldCBWUiBEaXNwbGF5cycgKTtcblxuXHRcdH0gKTtcblxuXHR9XG5cblx0Ly8gdGhlIFJpZnQgU0RLIHJldHVybnMgdGhlIHBvc2l0aW9uIGluIG1ldGVyc1xuXHQvLyB0aGlzIHNjYWxlIGZhY3RvciBhbGxvd3MgdGhlIHVzZXIgdG8gZGVmaW5lIGhvdyBtZXRlcnNcblx0Ly8gYXJlIGNvbnZlcnRlZCB0byBzY2VuZSB1bml0cy5cblxuXHR0aGlzLnNjYWxlID0gMTtcblxuXHQvLyBJZiB0cnVlIHdpbGwgdXNlIFwic3RhbmRpbmcgc3BhY2VcIiBjb29yZGluYXRlIHN5c3RlbSB3aGVyZSB5PTAgaXMgdGhlXG5cdC8vIGZsb29yIGFuZCB4PTAsIHo9MCBpcyB0aGUgY2VudGVyIG9mIHRoZSByb29tLlxuXHR0aGlzLnN0YW5kaW5nID0gZmFsc2U7XG5cblx0Ly8gRGlzdGFuY2UgZnJvbSB0aGUgdXNlcnMgZXllcyB0byB0aGUgZmxvb3IgaW4gbWV0ZXJzLiBVc2VkIHdoZW5cblx0Ly8gc3RhbmRpbmc9dHJ1ZSBidXQgdGhlIFZSRGlzcGxheSBkb2Vzbid0IHByb3ZpZGUgc3RhZ2VQYXJhbWV0ZXJzLlxuXHR0aGlzLnVzZXJIZWlnaHQgPSAxLjY7XG5cblx0dGhpcy5nZXRWUkRpc3BsYXkgPSBmdW5jdGlvbiAoKSB7XG5cblx0XHRyZXR1cm4gdnJEaXNwbGF5O1xuXG5cdH07XG5cblx0dGhpcy5zZXRWUkRpc3BsYXkgPSBmdW5jdGlvbiAoIHZhbHVlICkge1xuXG5cdFx0dnJEaXNwbGF5ID0gdmFsdWU7XG5cblx0fTtcblxuXHR0aGlzLmdldFZSRGlzcGxheXMgPSBmdW5jdGlvbiAoKSB7XG5cblx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5WUkNvbnRyb2xzOiBnZXRWUkRpc3BsYXlzKCkgaXMgYmVpbmcgZGVwcmVjYXRlZC4nICk7XG5cdFx0cmV0dXJuIHZyRGlzcGxheXM7XG5cblx0fTtcblxuXHR0aGlzLmdldFN0YW5kaW5nTWF0cml4ID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0cmV0dXJuIHN0YW5kaW5nTWF0cml4O1xuXG5cdH07XG5cblx0dGhpcy51cGRhdGUgPSBmdW5jdGlvbiAoKSB7XG5cblx0XHRpZiAoIHZyRGlzcGxheSApIHtcblxuXHRcdFx0dmFyIHBvc2U7XG5cblx0XHRcdGlmICggdnJEaXNwbGF5LmdldEZyYW1lRGF0YSApIHtcblxuXHRcdFx0XHR2ckRpc3BsYXkuZ2V0RnJhbWVEYXRhKCBmcmFtZURhdGEgKTtcblx0XHRcdFx0cG9zZSA9IGZyYW1lRGF0YS5wb3NlO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCB2ckRpc3BsYXkuZ2V0UG9zZSApIHtcblxuXHRcdFx0XHRwb3NlID0gdnJEaXNwbGF5LmdldFBvc2UoKTtcblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHBvc2Uub3JpZW50YXRpb24gIT09IG51bGwgKSB7XG5cblx0XHRcdFx0b2JqZWN0LnF1YXRlcm5pb24uZnJvbUFycmF5KCBwb3NlLm9yaWVudGF0aW9uICk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBwb3NlLnBvc2l0aW9uICE9PSBudWxsICkge1xuXG5cdFx0XHRcdG9iamVjdC5wb3NpdGlvbi5mcm9tQXJyYXkoIHBvc2UucG9zaXRpb24gKTtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRvYmplY3QucG9zaXRpb24uc2V0KCAwLCAwLCAwICk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCB0aGlzLnN0YW5kaW5nICkge1xuXG5cdFx0XHRcdGlmICggdnJEaXNwbGF5LnN0YWdlUGFyYW1ldGVycyApIHtcblxuXHRcdFx0XHRcdG9iamVjdC51cGRhdGVNYXRyaXgoKTtcblxuXHRcdFx0XHRcdHN0YW5kaW5nTWF0cml4LmZyb21BcnJheSggdnJEaXNwbGF5LnN0YWdlUGFyYW1ldGVycy5zaXR0aW5nVG9TdGFuZGluZ1RyYW5zZm9ybSApO1xuXHRcdFx0XHRcdG9iamVjdC5hcHBseU1hdHJpeCggc3RhbmRpbmdNYXRyaXggKTtcblxuXHRcdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdFx0b2JqZWN0LnBvc2l0aW9uLnNldFkoIG9iamVjdC5wb3NpdGlvbi55ICsgdGhpcy51c2VySGVpZ2h0ICk7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHRcdG9iamVjdC5wb3NpdGlvbi5tdWx0aXBseVNjYWxhciggc2NvcGUuc2NhbGUgKTtcblxuXHRcdH1cblxuXHR9O1xuXG5cdHRoaXMucmVzZXRQb3NlID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0aWYgKCB2ckRpc3BsYXkgKSB7XG5cblx0XHRcdHZyRGlzcGxheS5yZXNldFBvc2UoKTtcblxuXHRcdH1cblxuXHR9O1xuXG5cdHRoaXMucmVzZXRTZW5zb3IgPSBmdW5jdGlvbiAoKSB7XG5cblx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5WUkNvbnRyb2xzOiAucmVzZXRTZW5zb3IoKSBpcyBub3cgLnJlc2V0UG9zZSgpLicgKTtcblx0XHR0aGlzLnJlc2V0UG9zZSgpO1xuXG5cdH07XG5cblx0dGhpcy56ZXJvU2Vuc29yID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0Y29uc29sZS53YXJuKCAnVEhSRUUuVlJDb250cm9sczogLnplcm9TZW5zb3IoKSBpcyBub3cgLnJlc2V0UG9zZSgpLicgKTtcblx0XHR0aGlzLnJlc2V0UG9zZSgpO1xuXG5cdH07XG5cblx0dGhpcy5kaXNwb3NlID0gZnVuY3Rpb24gKCkge1xuXG5cdFx0dnJEaXNwbGF5ID0gbnVsbDtcblxuXHR9O1xuXG59O1xuIiwiLyoqXG4gKiBAYXV0aG9yIGRtYXJjb3MgLyBodHRwczovL2dpdGh1Yi5jb20vZG1hcmNvc1xuICogQGF1dGhvciBtcmRvb2IgLyBodHRwOi8vbXJkb29iLmNvbVxuICpcbiAqIFdlYlZSIFNwZWM6IGh0dHA6Ly9tb3p2ci5naXRodWIuaW8vd2VidnItc3BlYy93ZWJ2ci5odG1sXG4gKlxuICogRmlyZWZveDogaHR0cDovL21venZyLmNvbS9kb3dubG9hZHMvXG4gKiBDaHJvbWl1bTogaHR0cHM6Ly93ZWJ2ci5pbmZvL2dldC1jaHJvbWVcbiAqXG4gKi9cblxuVEhSRUUuVlJFZmZlY3QgPSBmdW5jdGlvbiggcmVuZGVyZXIsIG9uRXJyb3IgKSB7XG5cblx0dmFyIHZyRGlzcGxheSwgdnJEaXNwbGF5cztcblx0dmFyIGV5ZVRyYW5zbGF0aW9uTCA9IG5ldyBUSFJFRS5WZWN0b3IzKCk7XG5cdHZhciBleWVUcmFuc2xhdGlvblIgPSBuZXcgVEhSRUUuVmVjdG9yMygpO1xuXHR2YXIgcmVuZGVyUmVjdEwsIHJlbmRlclJlY3RSO1xuXG5cdHZhciBmcmFtZURhdGEgPSBudWxsO1xuXG5cdGlmICggJ1ZSRnJhbWVEYXRhJyBpbiB3aW5kb3cgKSB7XG5cblx0XHRmcmFtZURhdGEgPSBuZXcgd2luZG93LlZSRnJhbWVEYXRhKCk7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIGdvdFZSRGlzcGxheXMoIGRpc3BsYXlzICkge1xuXG5cdFx0dnJEaXNwbGF5cyA9IGRpc3BsYXlzO1xuXG5cdFx0aWYgKCBkaXNwbGF5cy5sZW5ndGggPiAwICkge1xuXG5cdFx0XHR2ckRpc3BsYXkgPSBkaXNwbGF5c1sgMCBdO1xuXG5cdFx0fSBlbHNlIHtcblxuXHRcdFx0aWYgKCBvbkVycm9yICkgb25FcnJvciggJ0hNRCBub3QgYXZhaWxhYmxlJyApO1xuXG5cdFx0fVxuXG5cdH1cblxuXHRpZiAoIG5hdmlnYXRvci5nZXRWUkRpc3BsYXlzICkge1xuXG5cdFx0bmF2aWdhdG9yLmdldFZSRGlzcGxheXMoKS50aGVuKCBnb3RWUkRpc3BsYXlzICkuY2F0Y2goIGZ1bmN0aW9uKCkge1xuXG5cdFx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5WUkVmZmVjdDogVW5hYmxlIHRvIGdldCBWUiBEaXNwbGF5cycgKTtcblxuXHRcdH0gKTtcblxuXHR9XG5cblx0Ly9cblxuXHR0aGlzLmlzUHJlc2VudGluZyA9IGZhbHNlO1xuXHR0aGlzLnNjYWxlID0gMTtcblxuXHR2YXIgc2NvcGUgPSB0aGlzO1xuXG5cdHZhciByZW5kZXJlclNpemUgPSByZW5kZXJlci5nZXRTaXplKCk7XG5cdHZhciByZW5kZXJlclVwZGF0ZVN0eWxlID0gZmFsc2U7XG5cdHZhciByZW5kZXJlclBpeGVsUmF0aW8gPSByZW5kZXJlci5nZXRQaXhlbFJhdGlvKCk7XG5cblx0dGhpcy5nZXRWUkRpc3BsYXkgPSBmdW5jdGlvbigpIHtcblxuXHRcdHJldHVybiB2ckRpc3BsYXk7XG5cblx0fTtcblxuXHR0aGlzLnNldFZSRGlzcGxheSA9IGZ1bmN0aW9uKCB2YWx1ZSApIHtcblxuXHRcdHZyRGlzcGxheSA9IHZhbHVlO1xuXG5cdH07XG5cblx0dGhpcy5nZXRWUkRpc3BsYXlzID0gZnVuY3Rpb24oKSB7XG5cblx0XHRjb25zb2xlLndhcm4oICdUSFJFRS5WUkVmZmVjdDogZ2V0VlJEaXNwbGF5cygpIGlzIGJlaW5nIGRlcHJlY2F0ZWQuJyApO1xuXHRcdHJldHVybiB2ckRpc3BsYXlzO1xuXG5cdH07XG5cblx0dGhpcy5zZXRTaXplID0gZnVuY3Rpb24oIHdpZHRoLCBoZWlnaHQsIHVwZGF0ZVN0eWxlICkge1xuXG5cdFx0cmVuZGVyZXJTaXplID0geyB3aWR0aDogd2lkdGgsIGhlaWdodDogaGVpZ2h0IH07XG5cdFx0cmVuZGVyZXJVcGRhdGVTdHlsZSA9IHVwZGF0ZVN0eWxlO1xuXG5cdFx0aWYgKCBzY29wZS5pc1ByZXNlbnRpbmcgKSB7XG5cblx0XHRcdHZhciBleWVQYXJhbXNMID0gdnJEaXNwbGF5LmdldEV5ZVBhcmFtZXRlcnMoICdsZWZ0JyApO1xuXHRcdFx0cmVuZGVyZXIuc2V0UGl4ZWxSYXRpbyggMSApO1xuXHRcdFx0cmVuZGVyZXIuc2V0U2l6ZSggZXllUGFyYW1zTC5yZW5kZXJXaWR0aCAqIDIsIGV5ZVBhcmFtc0wucmVuZGVySGVpZ2h0LCBmYWxzZSApO1xuXG5cdFx0fSBlbHNlIHtcblxuXHRcdFx0cmVuZGVyZXIuc2V0UGl4ZWxSYXRpbyggcmVuZGVyZXJQaXhlbFJhdGlvICk7XG5cdFx0XHRyZW5kZXJlci5zZXRTaXplKCB3aWR0aCwgaGVpZ2h0LCB1cGRhdGVTdHlsZSApO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0Ly8gVlIgcHJlc2VudGF0aW9uXG5cblx0dmFyIGNhbnZhcyA9IHJlbmRlcmVyLmRvbUVsZW1lbnQ7XG5cdHZhciBkZWZhdWx0TGVmdEJvdW5kcyA9IFsgMC4wLCAwLjAsIDAuNSwgMS4wIF07XG5cdHZhciBkZWZhdWx0UmlnaHRCb3VuZHMgPSBbIDAuNSwgMC4wLCAwLjUsIDEuMCBdO1xuXG5cdGZ1bmN0aW9uIG9uVlJEaXNwbGF5UHJlc2VudENoYW5nZSgpIHtcblxuXHRcdHZhciB3YXNQcmVzZW50aW5nID0gc2NvcGUuaXNQcmVzZW50aW5nO1xuXHRcdHNjb3BlLmlzUHJlc2VudGluZyA9IHZyRGlzcGxheSAhPT0gdW5kZWZpbmVkICYmIHZyRGlzcGxheS5pc1ByZXNlbnRpbmc7XG5cblx0XHRpZiAoIHNjb3BlLmlzUHJlc2VudGluZyApIHtcblxuXHRcdFx0dmFyIGV5ZVBhcmFtc0wgPSB2ckRpc3BsYXkuZ2V0RXllUGFyYW1ldGVycyggJ2xlZnQnICk7XG5cdFx0XHR2YXIgZXllV2lkdGggPSBleWVQYXJhbXNMLnJlbmRlcldpZHRoO1xuXHRcdFx0dmFyIGV5ZUhlaWdodCA9IGV5ZVBhcmFtc0wucmVuZGVySGVpZ2h0O1xuXG5cdFx0XHRpZiAoICEgd2FzUHJlc2VudGluZyApIHtcblxuXHRcdFx0XHRyZW5kZXJlclBpeGVsUmF0aW8gPSByZW5kZXJlci5nZXRQaXhlbFJhdGlvKCk7XG5cdFx0XHRcdHJlbmRlcmVyU2l6ZSA9IHJlbmRlcmVyLmdldFNpemUoKTtcblxuXHRcdFx0XHRyZW5kZXJlci5zZXRQaXhlbFJhdGlvKCAxICk7XG5cdFx0XHRcdHJlbmRlcmVyLnNldFNpemUoIGV5ZVdpZHRoICogMiwgZXllSGVpZ2h0LCBmYWxzZSApO1xuXG5cdFx0XHR9XG5cblx0XHR9IGVsc2UgaWYgKCB3YXNQcmVzZW50aW5nICkge1xuXG5cdFx0XHRyZW5kZXJlci5zZXRQaXhlbFJhdGlvKCByZW5kZXJlclBpeGVsUmF0aW8gKTtcblx0XHRcdHJlbmRlcmVyLnNldFNpemUoIHJlbmRlcmVyU2l6ZS53aWR0aCwgcmVuZGVyZXJTaXplLmhlaWdodCwgcmVuZGVyZXJVcGRhdGVTdHlsZSApO1xuXG5cdFx0fVxuXG5cdH1cblxuXHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lciggJ3ZyZGlzcGxheXByZXNlbnRjaGFuZ2UnLCBvblZSRGlzcGxheVByZXNlbnRDaGFuZ2UsIGZhbHNlICk7XG5cblx0dGhpcy5zZXRGdWxsU2NyZWVuID0gZnVuY3Rpb24oIGJvb2xlYW4gKSB7XG5cblx0XHRyZXR1cm4gbmV3IFByb21pc2UoIGZ1bmN0aW9uKCByZXNvbHZlLCByZWplY3QgKSB7XG5cblx0XHRcdGlmICggdnJEaXNwbGF5ID09PSB1bmRlZmluZWQgKSB7XG5cblx0XHRcdFx0cmVqZWN0KCBuZXcgRXJyb3IoICdObyBWUiBoYXJkd2FyZSBmb3VuZC4nICkgKTtcblx0XHRcdFx0cmV0dXJuO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggc2NvcGUuaXNQcmVzZW50aW5nID09PSBib29sZWFuICkge1xuXG5cdFx0XHRcdHJlc29sdmUoKTtcblx0XHRcdFx0cmV0dXJuO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggYm9vbGVhbiApIHtcblxuXHRcdFx0XHRyZXNvbHZlKCB2ckRpc3BsYXkucmVxdWVzdFByZXNlbnQoIFsgeyBzb3VyY2U6IGNhbnZhcyB9IF0gKSApO1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdHJlc29sdmUoIHZyRGlzcGxheS5leGl0UHJlc2VudCgpICk7XG5cblx0XHRcdH1cblxuXHRcdH0gKTtcblxuXHR9O1xuXG5cdHRoaXMucmVxdWVzdFByZXNlbnQgPSBmdW5jdGlvbigpIHtcblxuXHRcdHJldHVybiB0aGlzLnNldEZ1bGxTY3JlZW4oIHRydWUgKTtcblxuXHR9O1xuXG5cdHRoaXMuZXhpdFByZXNlbnQgPSBmdW5jdGlvbigpIHtcblxuXHRcdHJldHVybiB0aGlzLnNldEZ1bGxTY3JlZW4oIGZhbHNlICk7XG5cblx0fTtcblxuXHR0aGlzLnJlcXVlc3RBbmltYXRpb25GcmFtZSA9IGZ1bmN0aW9uKCBmICkge1xuXG5cdFx0aWYgKCB2ckRpc3BsYXkgIT09IHVuZGVmaW5lZCApIHtcblxuXHRcdFx0cmV0dXJuIHZyRGlzcGxheS5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUoIGYgKTtcblxuXHRcdH0gZWxzZSB7XG5cblx0XHRcdHJldHVybiB3aW5kb3cucmVxdWVzdEFuaW1hdGlvbkZyYW1lKCBmICk7XG5cblx0XHR9XG5cblx0fTtcblxuXHR0aGlzLmNhbmNlbEFuaW1hdGlvbkZyYW1lID0gZnVuY3Rpb24oIGggKSB7XG5cblx0XHRpZiAoIHZyRGlzcGxheSAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHR2ckRpc3BsYXkuY2FuY2VsQW5pbWF0aW9uRnJhbWUoIGggKTtcblxuXHRcdH0gZWxzZSB7XG5cblx0XHRcdHdpbmRvdy5jYW5jZWxBbmltYXRpb25GcmFtZSggaCApO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0dGhpcy5zdWJtaXRGcmFtZSA9IGZ1bmN0aW9uKCkge1xuXG5cdFx0aWYgKCB2ckRpc3BsYXkgIT09IHVuZGVmaW5lZCAmJiBzY29wZS5pc1ByZXNlbnRpbmcgKSB7XG5cblx0XHRcdHZyRGlzcGxheS5zdWJtaXRGcmFtZSgpO1xuXG5cdFx0fVxuXG5cdH07XG5cblx0dGhpcy5hdXRvU3VibWl0RnJhbWUgPSB0cnVlO1xuXG5cdC8vIHJlbmRlclxuXG5cdHZhciBjYW1lcmFMID0gbmV3IFRIUkVFLlBlcnNwZWN0aXZlQ2FtZXJhKCk7XG5cdGNhbWVyYUwubGF5ZXJzLmVuYWJsZSggMSApO1xuXG5cdHZhciBjYW1lcmFSID0gbmV3IFRIUkVFLlBlcnNwZWN0aXZlQ2FtZXJhKCk7XG5cdGNhbWVyYVIubGF5ZXJzLmVuYWJsZSggMiApO1xuXG5cdHRoaXMucmVuZGVyID0gZnVuY3Rpb24oIHNjZW5lLCBjYW1lcmEsIHJlbmRlclRhcmdldCwgZm9yY2VDbGVhciApIHtcblxuXHRcdGlmICggdnJEaXNwbGF5ICYmIHNjb3BlLmlzUHJlc2VudGluZyApIHtcblxuXHRcdFx0dmFyIGF1dG9VcGRhdGUgPSBzY2VuZS5hdXRvVXBkYXRlO1xuXG5cdFx0XHRpZiAoIGF1dG9VcGRhdGUgKSB7XG5cblx0XHRcdFx0c2NlbmUudXBkYXRlTWF0cml4V29ybGQoKTtcblx0XHRcdFx0c2NlbmUuYXV0b1VwZGF0ZSA9IGZhbHNlO1xuXG5cdFx0XHR9XG5cblx0XHRcdHZhciBleWVQYXJhbXNMID0gdnJEaXNwbGF5LmdldEV5ZVBhcmFtZXRlcnMoICdsZWZ0JyApO1xuXHRcdFx0dmFyIGV5ZVBhcmFtc1IgPSB2ckRpc3BsYXkuZ2V0RXllUGFyYW1ldGVycyggJ3JpZ2h0JyApO1xuXG5cdFx0XHRleWVUcmFuc2xhdGlvbkwuZnJvbUFycmF5KCBleWVQYXJhbXNMLm9mZnNldCApO1xuXHRcdFx0ZXllVHJhbnNsYXRpb25SLmZyb21BcnJheSggZXllUGFyYW1zUi5vZmZzZXQgKTtcblxuXHRcdFx0aWYgKCBBcnJheS5pc0FycmF5KCBzY2VuZSApICkge1xuXG5cdFx0XHRcdGNvbnNvbGUud2FybiggJ1RIUkVFLlZSRWZmZWN0LnJlbmRlcigpIG5vIGxvbmdlciBzdXBwb3J0cyBhcnJheXMuIFVzZSBvYmplY3QubGF5ZXJzIGluc3RlYWQuJyApO1xuXHRcdFx0XHRzY2VuZSA9IHNjZW5lWyAwIF07XG5cblx0XHRcdH1cblxuXHRcdFx0Ly8gV2hlbiByZW5kZXJpbmcgd2UgZG9uJ3QgY2FyZSB3aGF0IHRoZSByZWNvbW1lbmRlZCBzaXplIGlzLCBvbmx5IHdoYXQgdGhlIGFjdHVhbCBzaXplXG5cdFx0XHQvLyBvZiB0aGUgYmFja2J1ZmZlciBpcy5cblx0XHRcdHZhciBzaXplID0gcmVuZGVyZXIuZ2V0U2l6ZSgpO1xuXHRcdFx0dmFyIGxheWVycyA9IHZyRGlzcGxheS5nZXRMYXllcnMoKTtcblx0XHRcdHZhciBsZWZ0Qm91bmRzO1xuXHRcdFx0dmFyIHJpZ2h0Qm91bmRzO1xuXG5cdFx0XHRpZiAoIGxheWVycy5sZW5ndGggKSB7XG5cblx0XHRcdFx0dmFyIGxheWVyID0gbGF5ZXJzWyAwIF07XG5cblx0XHRcdFx0bGVmdEJvdW5kcyA9IGxheWVyLmxlZnRCb3VuZHMgIT09IG51bGwgJiYgbGF5ZXIubGVmdEJvdW5kcy5sZW5ndGggPT09IDQgPyBsYXllci5sZWZ0Qm91bmRzIDogZGVmYXVsdExlZnRCb3VuZHM7XG5cdFx0XHRcdHJpZ2h0Qm91bmRzID0gbGF5ZXIucmlnaHRCb3VuZHMgIT09IG51bGwgJiYgbGF5ZXIucmlnaHRCb3VuZHMubGVuZ3RoID09PSA0ID8gbGF5ZXIucmlnaHRCb3VuZHMgOiBkZWZhdWx0UmlnaHRCb3VuZHM7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0bGVmdEJvdW5kcyA9IGRlZmF1bHRMZWZ0Qm91bmRzO1xuXHRcdFx0XHRyaWdodEJvdW5kcyA9IGRlZmF1bHRSaWdodEJvdW5kcztcblxuXHRcdFx0fVxuXG5cdFx0XHRyZW5kZXJSZWN0TCA9IHtcblx0XHRcdFx0eDogTWF0aC5yb3VuZCggc2l6ZS53aWR0aCAqIGxlZnRCb3VuZHNbIDAgXSApLFxuXHRcdFx0XHR5OiBNYXRoLnJvdW5kKCBzaXplLmhlaWdodCAqIGxlZnRCb3VuZHNbIDEgXSApLFxuXHRcdFx0XHR3aWR0aDogTWF0aC5yb3VuZCggc2l6ZS53aWR0aCAqIGxlZnRCb3VuZHNbIDIgXSApLFxuXHRcdFx0XHRoZWlnaHQ6IE1hdGgucm91bmQoIHNpemUuaGVpZ2h0ICogbGVmdEJvdW5kc1sgMyBdIClcblx0XHRcdH07XG5cdFx0XHRyZW5kZXJSZWN0UiA9IHtcblx0XHRcdFx0eDogTWF0aC5yb3VuZCggc2l6ZS53aWR0aCAqIHJpZ2h0Qm91bmRzWyAwIF0gKSxcblx0XHRcdFx0eTogTWF0aC5yb3VuZCggc2l6ZS5oZWlnaHQgKiByaWdodEJvdW5kc1sgMSBdICksXG5cdFx0XHRcdHdpZHRoOiBNYXRoLnJvdW5kKCBzaXplLndpZHRoICogcmlnaHRCb3VuZHNbIDIgXSApLFxuXHRcdFx0XHRoZWlnaHQ6IE1hdGgucm91bmQoIHNpemUuaGVpZ2h0ICogcmlnaHRCb3VuZHNbIDMgXSApXG5cdFx0XHR9O1xuXG5cdFx0XHRpZiAoIHJlbmRlclRhcmdldCApIHtcblxuXHRcdFx0XHRyZW5kZXJlci5zZXRSZW5kZXJUYXJnZXQoIHJlbmRlclRhcmdldCApO1xuXHRcdFx0XHRyZW5kZXJUYXJnZXQuc2Npc3NvclRlc3QgPSB0cnVlO1xuXG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdHJlbmRlcmVyLnNldFJlbmRlclRhcmdldCggbnVsbCApO1xuXHRcdFx0XHRyZW5kZXJlci5zZXRTY2lzc29yVGVzdCggdHJ1ZSApO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggcmVuZGVyZXIuYXV0b0NsZWFyIHx8IGZvcmNlQ2xlYXIgKSByZW5kZXJlci5jbGVhcigpO1xuXG5cdFx0XHRpZiAoIGNhbWVyYS5wYXJlbnQgPT09IG51bGwgKSBjYW1lcmEudXBkYXRlTWF0cml4V29ybGQoKTtcblxuXHRcdFx0Y2FtZXJhLm1hdHJpeFdvcmxkLmRlY29tcG9zZSggY2FtZXJhTC5wb3NpdGlvbiwgY2FtZXJhTC5xdWF0ZXJuaW9uLCBjYW1lcmFMLnNjYWxlICk7XG5cdFx0XHRjYW1lcmEubWF0cml4V29ybGQuZGVjb21wb3NlKCBjYW1lcmFSLnBvc2l0aW9uLCBjYW1lcmFSLnF1YXRlcm5pb24sIGNhbWVyYVIuc2NhbGUgKTtcblxuXHRcdFx0dmFyIHNjYWxlID0gdGhpcy5zY2FsZTtcblx0XHRcdGNhbWVyYUwudHJhbnNsYXRlT25BeGlzKCBleWVUcmFuc2xhdGlvbkwsIHNjYWxlICk7XG5cdFx0XHRjYW1lcmFSLnRyYW5zbGF0ZU9uQXhpcyggZXllVHJhbnNsYXRpb25SLCBzY2FsZSApO1xuXG5cdFx0XHRpZiAoIHZyRGlzcGxheS5nZXRGcmFtZURhdGEgKSB7XG5cblx0XHRcdFx0dnJEaXNwbGF5LmRlcHRoTmVhciA9IGNhbWVyYS5uZWFyO1xuXHRcdFx0XHR2ckRpc3BsYXkuZGVwdGhGYXIgPSBjYW1lcmEuZmFyO1xuXG5cdFx0XHRcdHZyRGlzcGxheS5nZXRGcmFtZURhdGEoIGZyYW1lRGF0YSApO1xuXG5cdFx0XHRcdGNhbWVyYUwucHJvamVjdGlvbk1hdHJpeC5lbGVtZW50cyA9IGZyYW1lRGF0YS5sZWZ0UHJvamVjdGlvbk1hdHJpeDtcblx0XHRcdFx0Y2FtZXJhUi5wcm9qZWN0aW9uTWF0cml4LmVsZW1lbnRzID0gZnJhbWVEYXRhLnJpZ2h0UHJvamVjdGlvbk1hdHJpeDtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRjYW1lcmFMLnByb2plY3Rpb25NYXRyaXggPSBmb3ZUb1Byb2plY3Rpb24oIGV5ZVBhcmFtc0wuZmllbGRPZlZpZXcsIHRydWUsIGNhbWVyYS5uZWFyLCBjYW1lcmEuZmFyICk7XG5cdFx0XHRcdGNhbWVyYVIucHJvamVjdGlvbk1hdHJpeCA9IGZvdlRvUHJvamVjdGlvbiggZXllUGFyYW1zUi5maWVsZE9mVmlldywgdHJ1ZSwgY2FtZXJhLm5lYXIsIGNhbWVyYS5mYXIgKTtcblxuXHRcdFx0fVxuXG5cdFx0XHQvLyByZW5kZXIgbGVmdCBleWVcblx0XHRcdGlmICggcmVuZGVyVGFyZ2V0ICkge1xuXG5cdFx0XHRcdHJlbmRlclRhcmdldC52aWV3cG9ydC5zZXQoIHJlbmRlclJlY3RMLngsIHJlbmRlclJlY3RMLnksIHJlbmRlclJlY3RMLndpZHRoLCByZW5kZXJSZWN0TC5oZWlnaHQgKTtcblx0XHRcdFx0cmVuZGVyVGFyZ2V0LnNjaXNzb3Iuc2V0KCByZW5kZXJSZWN0TC54LCByZW5kZXJSZWN0TC55LCByZW5kZXJSZWN0TC53aWR0aCwgcmVuZGVyUmVjdEwuaGVpZ2h0ICk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0cmVuZGVyZXIuc2V0Vmlld3BvcnQoIHJlbmRlclJlY3RMLngsIHJlbmRlclJlY3RMLnksIHJlbmRlclJlY3RMLndpZHRoLCByZW5kZXJSZWN0TC5oZWlnaHQgKTtcblx0XHRcdFx0cmVuZGVyZXIuc2V0U2Npc3NvciggcmVuZGVyUmVjdEwueCwgcmVuZGVyUmVjdEwueSwgcmVuZGVyUmVjdEwud2lkdGgsIHJlbmRlclJlY3RMLmhlaWdodCApO1xuXG5cdFx0XHR9XG5cdFx0XHRyZW5kZXJlci5yZW5kZXIoIHNjZW5lLCBjYW1lcmFMLCByZW5kZXJUYXJnZXQsIGZvcmNlQ2xlYXIgKTtcblxuXHRcdFx0Ly8gcmVuZGVyIHJpZ2h0IGV5ZVxuXHRcdFx0aWYgKCByZW5kZXJUYXJnZXQgKSB7XG5cblx0XHRcdFx0cmVuZGVyVGFyZ2V0LnZpZXdwb3J0LnNldCggcmVuZGVyUmVjdFIueCwgcmVuZGVyUmVjdFIueSwgcmVuZGVyUmVjdFIud2lkdGgsIHJlbmRlclJlY3RSLmhlaWdodCApO1xuXHRcdFx0XHRyZW5kZXJUYXJnZXQuc2Npc3Nvci5zZXQoIHJlbmRlclJlY3RSLngsIHJlbmRlclJlY3RSLnksIHJlbmRlclJlY3RSLndpZHRoLCByZW5kZXJSZWN0Ui5oZWlnaHQgKTtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRyZW5kZXJlci5zZXRWaWV3cG9ydCggcmVuZGVyUmVjdFIueCwgcmVuZGVyUmVjdFIueSwgcmVuZGVyUmVjdFIud2lkdGgsIHJlbmRlclJlY3RSLmhlaWdodCApO1xuXHRcdFx0XHRyZW5kZXJlci5zZXRTY2lzc29yKCByZW5kZXJSZWN0Ui54LCByZW5kZXJSZWN0Ui55LCByZW5kZXJSZWN0Ui53aWR0aCwgcmVuZGVyUmVjdFIuaGVpZ2h0ICk7XG5cblx0XHRcdH1cblx0XHRcdHJlbmRlcmVyLnJlbmRlciggc2NlbmUsIGNhbWVyYVIsIHJlbmRlclRhcmdldCwgZm9yY2VDbGVhciApO1xuXG5cdFx0XHRpZiAoIHJlbmRlclRhcmdldCApIHtcblxuXHRcdFx0XHRyZW5kZXJUYXJnZXQudmlld3BvcnQuc2V0KCAwLCAwLCBzaXplLndpZHRoLCBzaXplLmhlaWdodCApO1xuXHRcdFx0XHRyZW5kZXJUYXJnZXQuc2Npc3Nvci5zZXQoIDAsIDAsIHNpemUud2lkdGgsIHNpemUuaGVpZ2h0ICk7XG5cdFx0XHRcdHJlbmRlclRhcmdldC5zY2lzc29yVGVzdCA9IGZhbHNlO1xuXHRcdFx0XHRyZW5kZXJlci5zZXRSZW5kZXJUYXJnZXQoIG51bGwgKTtcblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRyZW5kZXJlci5zZXRWaWV3cG9ydCggMCwgMCwgc2l6ZS53aWR0aCwgc2l6ZS5oZWlnaHQgKTtcblx0XHRcdFx0cmVuZGVyZXIuc2V0U2Npc3NvclRlc3QoIGZhbHNlICk7XG5cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBhdXRvVXBkYXRlICkge1xuXG5cdFx0XHRcdHNjZW5lLmF1dG9VcGRhdGUgPSB0cnVlO1xuXG5cdFx0XHR9XG5cblx0XHRcdGlmICggc2NvcGUuYXV0b1N1Ym1pdEZyYW1lICkge1xuXG5cdFx0XHRcdHNjb3BlLnN1Ym1pdEZyYW1lKCk7XG5cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuO1xuXG5cdFx0fVxuXG5cdFx0Ly8gUmVndWxhciByZW5kZXIgbW9kZSBpZiBub3QgSE1EXG5cblx0XHRyZW5kZXJlci5yZW5kZXIoIHNjZW5lLCBjYW1lcmEsIHJlbmRlclRhcmdldCwgZm9yY2VDbGVhciApO1xuXG5cdH07XG5cblx0dGhpcy5kaXNwb3NlID0gZnVuY3Rpb24oKSB7XG5cblx0XHR3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lciggJ3ZyZGlzcGxheXByZXNlbnRjaGFuZ2UnLCBvblZSRGlzcGxheVByZXNlbnRDaGFuZ2UsIGZhbHNlICk7XG5cblx0fTtcblxuXHQvL1xuXG5cdGZ1bmN0aW9uIGZvdlRvTkRDU2NhbGVPZmZzZXQoIGZvdiApIHtcblxuXHRcdHZhciBweHNjYWxlID0gMi4wIC8gKCBmb3YubGVmdFRhbiArIGZvdi5yaWdodFRhbiApO1xuXHRcdHZhciBweG9mZnNldCA9ICggZm92LmxlZnRUYW4gLSBmb3YucmlnaHRUYW4gKSAqIHB4c2NhbGUgKiAwLjU7XG5cdFx0dmFyIHB5c2NhbGUgPSAyLjAgLyAoIGZvdi51cFRhbiArIGZvdi5kb3duVGFuICk7XG5cdFx0dmFyIHB5b2Zmc2V0ID0gKCBmb3YudXBUYW4gLSBmb3YuZG93blRhbiApICogcHlzY2FsZSAqIDAuNTtcblx0XHRyZXR1cm4geyBzY2FsZTogWyBweHNjYWxlLCBweXNjYWxlIF0sIG9mZnNldDogWyBweG9mZnNldCwgcHlvZmZzZXQgXSB9O1xuXG5cdH1cblxuXHRmdW5jdGlvbiBmb3ZQb3J0VG9Qcm9qZWN0aW9uKCBmb3YsIHJpZ2h0SGFuZGVkLCB6TmVhciwgekZhciApIHtcblxuXHRcdHJpZ2h0SGFuZGVkID0gcmlnaHRIYW5kZWQgPT09IHVuZGVmaW5lZCA/IHRydWUgOiByaWdodEhhbmRlZDtcblx0XHR6TmVhciA9IHpOZWFyID09PSB1bmRlZmluZWQgPyAwLjAxIDogek5lYXI7XG5cdFx0ekZhciA9IHpGYXIgPT09IHVuZGVmaW5lZCA/IDEwMDAwLjAgOiB6RmFyO1xuXG5cdFx0dmFyIGhhbmRlZG5lc3NTY2FsZSA9IHJpZ2h0SGFuZGVkID8gLSAxLjAgOiAxLjA7XG5cblx0XHQvLyBzdGFydCB3aXRoIGFuIGlkZW50aXR5IG1hdHJpeFxuXHRcdHZhciBtb2JqID0gbmV3IFRIUkVFLk1hdHJpeDQoKTtcblx0XHR2YXIgbSA9IG1vYmouZWxlbWVudHM7XG5cblx0XHQvLyBhbmQgd2l0aCBzY2FsZS9vZmZzZXQgaW5mbyBmb3Igbm9ybWFsaXplZCBkZXZpY2UgY29vcmRzXG5cdFx0dmFyIHNjYWxlQW5kT2Zmc2V0ID0gZm92VG9ORENTY2FsZU9mZnNldCggZm92ICk7XG5cblx0XHQvLyBYIHJlc3VsdCwgbWFwIGNsaXAgZWRnZXMgdG8gWy13LCt3XVxuXHRcdG1bIDAgKiA0ICsgMCBdID0gc2NhbGVBbmRPZmZzZXQuc2NhbGVbIDAgXTtcblx0XHRtWyAwICogNCArIDEgXSA9IDAuMDtcblx0XHRtWyAwICogNCArIDIgXSA9IHNjYWxlQW5kT2Zmc2V0Lm9mZnNldFsgMCBdICogaGFuZGVkbmVzc1NjYWxlO1xuXHRcdG1bIDAgKiA0ICsgMyBdID0gMC4wO1xuXG5cdFx0Ly8gWSByZXN1bHQsIG1hcCBjbGlwIGVkZ2VzIHRvIFstdywrd11cblx0XHQvLyBZIG9mZnNldCBpcyBuZWdhdGVkIGJlY2F1c2UgdGhpcyBwcm9qIG1hdHJpeCB0cmFuc2Zvcm1zIGZyb20gd29ybGQgY29vcmRzIHdpdGggWT11cCxcblx0XHQvLyBidXQgdGhlIE5EQyBzY2FsaW5nIGhhcyBZPWRvd24gKHRoYW5rcyBEM0Q/KVxuXHRcdG1bIDEgKiA0ICsgMCBdID0gMC4wO1xuXHRcdG1bIDEgKiA0ICsgMSBdID0gc2NhbGVBbmRPZmZzZXQuc2NhbGVbIDEgXTtcblx0XHRtWyAxICogNCArIDIgXSA9IC0gc2NhbGVBbmRPZmZzZXQub2Zmc2V0WyAxIF0gKiBoYW5kZWRuZXNzU2NhbGU7XG5cdFx0bVsgMSAqIDQgKyAzIF0gPSAwLjA7XG5cblx0XHQvLyBaIHJlc3VsdCAodXAgdG8gdGhlIGFwcClcblx0XHRtWyAyICogNCArIDAgXSA9IDAuMDtcblx0XHRtWyAyICogNCArIDEgXSA9IDAuMDtcblx0XHRtWyAyICogNCArIDIgXSA9IHpGYXIgLyAoIHpOZWFyIC0gekZhciApICogLSBoYW5kZWRuZXNzU2NhbGU7XG5cdFx0bVsgMiAqIDQgKyAzIF0gPSAoIHpGYXIgKiB6TmVhciApIC8gKCB6TmVhciAtIHpGYXIgKTtcblxuXHRcdC8vIFcgcmVzdWx0ICg9IFogaW4pXG5cdFx0bVsgMyAqIDQgKyAwIF0gPSAwLjA7XG5cdFx0bVsgMyAqIDQgKyAxIF0gPSAwLjA7XG5cdFx0bVsgMyAqIDQgKyAyIF0gPSBoYW5kZWRuZXNzU2NhbGU7XG5cdFx0bVsgMyAqIDQgKyAzIF0gPSAwLjA7XG5cblx0XHRtb2JqLnRyYW5zcG9zZSgpO1xuXG5cdFx0cmV0dXJuIG1vYmo7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIGZvdlRvUHJvamVjdGlvbiggZm92LCByaWdodEhhbmRlZCwgek5lYXIsIHpGYXIgKSB7XG5cblx0XHR2YXIgREVHMlJBRCA9IE1hdGguUEkgLyAxODAuMDtcblxuXHRcdHZhciBmb3ZQb3J0ID0ge1xuXHRcdFx0dXBUYW46IE1hdGgudGFuKCBmb3YudXBEZWdyZWVzICogREVHMlJBRCApLFxuXHRcdFx0ZG93blRhbjogTWF0aC50YW4oIGZvdi5kb3duRGVncmVlcyAqIERFRzJSQUQgKSxcblx0XHRcdGxlZnRUYW46IE1hdGgudGFuKCBmb3YubGVmdERlZ3JlZXMgKiBERUcyUkFEICksXG5cdFx0XHRyaWdodFRhbjogTWF0aC50YW4oIGZvdi5yaWdodERlZ3JlZXMgKiBERUcyUkFEIClcblx0XHR9O1xuXG5cdFx0cmV0dXJuIGZvdlBvcnRUb1Byb2plY3Rpb24oIGZvdlBvcnQsIHJpZ2h0SGFuZGVkLCB6TmVhciwgekZhciApO1xuXG5cdH1cblxufTtcbiIsIihmdW5jdGlvbihmKXtpZih0eXBlb2YgZXhwb3J0cz09PVwib2JqZWN0XCImJnR5cGVvZiBtb2R1bGUhPT1cInVuZGVmaW5lZFwiKXttb2R1bGUuZXhwb3J0cz1mKCl9ZWxzZSBpZih0eXBlb2YgZGVmaW5lPT09XCJmdW5jdGlvblwiJiZkZWZpbmUuYW1kKXtkZWZpbmUoW10sZil9ZWxzZXt2YXIgZztpZih0eXBlb2Ygd2luZG93IT09XCJ1bmRlZmluZWRcIil7Zz13aW5kb3d9ZWxzZSBpZih0eXBlb2YgZ2xvYmFsIT09XCJ1bmRlZmluZWRcIil7Zz1nbG9iYWx9ZWxzZSBpZih0eXBlb2Ygc2VsZiE9PVwidW5kZWZpbmVkXCIpe2c9c2VsZn1lbHNle2c9dGhpc31nLldlYlZSTWFuYWdlciA9IGYoKX19KShmdW5jdGlvbigpe3ZhciBkZWZpbmUsbW9kdWxlLGV4cG9ydHM7cmV0dXJuIChmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pKHsxOltmdW5jdGlvbihfZGVyZXFfLG1vZHVsZSxleHBvcnRzKXtcbi8qXG4gKiBDb3B5cmlnaHQgMjAxNSBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICovXG5cbnZhciBFbWl0dGVyID0gX2RlcmVxXygnLi9lbWl0dGVyLmpzJyk7XG52YXIgTW9kZXMgPSBfZGVyZXFfKCcuL21vZGVzLmpzJyk7XG52YXIgVXRpbCA9IF9kZXJlcV8oJy4vdXRpbC5qcycpO1xuXG4vKipcbiAqIEV2ZXJ5dGhpbmcgaGF2aW5nIHRvIGRvIHdpdGggdGhlIFdlYlZSIGJ1dHRvbi5cbiAqIEVtaXRzIGEgJ2NsaWNrJyBldmVudCB3aGVuIGl0J3MgY2xpY2tlZC5cbiAqL1xuZnVuY3Rpb24gQnV0dG9uTWFuYWdlcihvcHRfcm9vdCkge1xuICB2YXIgcm9vdCA9IG9wdF9yb290IHx8IGRvY3VtZW50LmJvZHk7XG4gIHRoaXMubG9hZEljb25zXygpO1xuXG4gIC8vIE1ha2UgdGhlIGZ1bGxzY3JlZW4gYnV0dG9uLlxuICB2YXIgZnNCdXR0b24gPSB0aGlzLmNyZWF0ZUJ1dHRvbigpO1xuICBmc0J1dHRvbi5zcmMgPSB0aGlzLklDT05TLmZ1bGxzY3JlZW47XG4gIGZzQnV0dG9uLnRpdGxlID0gJ0Z1bGxzY3JlZW4gbW9kZSc7XG4gIHZhciBzID0gZnNCdXR0b24uc3R5bGU7XG4gIHMudG9wID0gMDtcbiAgcy5yaWdodCA9IDA7XG4gIGZzQnV0dG9uLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgdGhpcy5jcmVhdGVDbGlja0hhbmRsZXJfKCdmcycpKTtcbiAgcm9vdC5hcHBlbmRDaGlsZChmc0J1dHRvbik7XG4gIHRoaXMuZnNCdXR0b24gPSBmc0J1dHRvbjtcblxuICAvLyBNYWtlIHRoZSBWUiBidXR0b24uXG4gIHZhciB2ckJ1dHRvbiA9IHRoaXMuY3JlYXRlQnV0dG9uKCk7XG4gIHZyQnV0dG9uLnNyYyA9IHRoaXMuSUNPTlMuY2FyZGJvYXJkO1xuICB2ckJ1dHRvbi50aXRsZSA9ICdWaXJ0dWFsIHJlYWxpdHkgbW9kZSc7XG4gIHZhciBzID0gdnJCdXR0b24uc3R5bGU7XG4gIHMudG9wID0gMDtcbiAgcy5yaWdodCA9ICc0OHB4JztcbiAgdnJCdXR0b24uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCB0aGlzLmNyZWF0ZUNsaWNrSGFuZGxlcl8oJ3ZyJykpO1xuICByb290LmFwcGVuZENoaWxkKHZyQnV0dG9uKTtcbiAgdGhpcy52ckJ1dHRvbiA9IHZyQnV0dG9uO1xuXG4gIHRoaXMuaXNWaXNpYmxlID0gdHJ1ZTtcblxufVxuQnV0dG9uTWFuYWdlci5wcm90b3R5cGUgPSBuZXcgRW1pdHRlcigpO1xuXG5CdXR0b25NYW5hZ2VyLnByb3RvdHlwZS5jcmVhdGVCdXR0b24gPSBmdW5jdGlvbigpIHtcbiAgdmFyIGJ1dHRvbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2ltZycpO1xuICBidXR0b24uY2xhc3NOYW1lID0gJ3dlYnZyLWJ1dHRvbic7XG4gIHZhciBzID0gYnV0dG9uLnN0eWxlO1xuICBzLnBvc2l0aW9uID0gJ2Fic29sdXRlJztcbiAgcy53aWR0aCA9ICcyNHB4J1xuICBzLmhlaWdodCA9ICcyNHB4JztcbiAgcy5iYWNrZ3JvdW5kU2l6ZSA9ICdjb3Zlcic7XG4gIHMuYmFja2dyb3VuZENvbG9yID0gJ3RyYW5zcGFyZW50JztcbiAgcy5ib3JkZXIgPSAwO1xuICBzLnVzZXJTZWxlY3QgPSAnbm9uZSc7XG4gIHMud2Via2l0VXNlclNlbGVjdCA9ICdub25lJztcbiAgcy5Nb3pVc2VyU2VsZWN0ID0gJ25vbmUnO1xuICBzLmN1cnNvciA9ICdwb2ludGVyJztcbiAgcy5wYWRkaW5nID0gJzEycHgnO1xuICBzLnpJbmRleCA9IDE7XG4gIHMuZGlzcGxheSA9ICdub25lJztcbiAgcy5ib3hTaXppbmcgPSAnY29udGVudC1ib3gnO1xuXG4gIC8vIFByZXZlbnQgYnV0dG9uIGZyb20gYmVpbmcgc2VsZWN0ZWQgYW5kIGRyYWdnZWQuXG4gIGJ1dHRvbi5kcmFnZ2FibGUgPSBmYWxzZTtcbiAgYnV0dG9uLmFkZEV2ZW50TGlzdGVuZXIoJ2RyYWdzdGFydCcsIGZ1bmN0aW9uKGUpIHtcbiAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gIH0pO1xuXG4gIC8vIFN0eWxlIGl0IG9uIGhvdmVyLlxuICBidXR0b24uYWRkRXZlbnRMaXN0ZW5lcignbW91c2VlbnRlcicsIGZ1bmN0aW9uKGUpIHtcbiAgICBzLmZpbHRlciA9IHMud2Via2l0RmlsdGVyID0gJ2Ryb3Atc2hhZG93KDAgMCA1cHggcmdiYSgyNTUsMjU1LDI1NSwxKSknO1xuICB9KTtcbiAgYnV0dG9uLmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlbGVhdmUnLCBmdW5jdGlvbihlKSB7XG4gICAgcy5maWx0ZXIgPSBzLndlYmtpdEZpbHRlciA9ICcnO1xuICB9KTtcbiAgcmV0dXJuIGJ1dHRvbjtcbn07XG5cbkJ1dHRvbk1hbmFnZXIucHJvdG90eXBlLnNldE1vZGUgPSBmdW5jdGlvbihtb2RlLCBpc1ZSQ29tcGF0aWJsZSkge1xuICBpc1ZSQ29tcGF0aWJsZSA9IGlzVlJDb21wYXRpYmxlIHx8IFdlYlZSQ29uZmlnLkZPUkNFX0VOQUJMRV9WUjtcbiAgaWYgKCF0aGlzLmlzVmlzaWJsZSkge1xuICAgIHJldHVybjtcbiAgfVxuICBzd2l0Y2ggKG1vZGUpIHtcbiAgICBjYXNlIE1vZGVzLk5PUk1BTDpcbiAgICAgIHRoaXMuZnNCdXR0b24uc3R5bGUuZGlzcGxheSA9ICdibG9jayc7XG4gICAgICB0aGlzLmZzQnV0dG9uLnNyYyA9IHRoaXMuSUNPTlMuZnVsbHNjcmVlbjtcbiAgICAgIHRoaXMudnJCdXR0b24uc3R5bGUuZGlzcGxheSA9IChpc1ZSQ29tcGF0aWJsZSA/ICdibG9jaycgOiAnbm9uZScpO1xuICAgICAgYnJlYWs7XG4gICAgY2FzZSBNb2Rlcy5NQUdJQ19XSU5ET1c6XG4gICAgICB0aGlzLmZzQnV0dG9uLnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snO1xuICAgICAgdGhpcy5mc0J1dHRvbi5zcmMgPSB0aGlzLklDT05TLmV4aXRGdWxsc2NyZWVuO1xuICAgICAgdGhpcy52ckJ1dHRvbi5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuICAgICAgYnJlYWs7XG4gICAgY2FzZSBNb2Rlcy5WUjpcbiAgICAgIHRoaXMuZnNCdXR0b24uc3R5bGUuZGlzcGxheSA9ICdub25lJztcbiAgICAgIHRoaXMudnJCdXR0b24uc3R5bGUuZGlzcGxheSA9ICdub25lJztcbiAgICAgIGJyZWFrO1xuICB9XG5cbiAgLy8gSGFjayBmb3IgU2FmYXJpIE1hYy9pT1MgdG8gZm9yY2UgcmVsYXlvdXQgKHN2Zy1zcGVjaWZpYyBpc3N1ZSlcbiAgLy8gaHR0cDovL2dvby5nbC9oamdSNnJcbiAgdmFyIG9sZFZhbHVlID0gdGhpcy5mc0J1dHRvbi5zdHlsZS5kaXNwbGF5O1xuICB0aGlzLmZzQnV0dG9uLnN0eWxlLmRpc3BsYXkgPSAnaW5saW5lLWJsb2NrJztcbiAgdGhpcy5mc0J1dHRvbi5vZmZzZXRIZWlnaHQ7XG4gIHRoaXMuZnNCdXR0b24uc3R5bGUuZGlzcGxheSA9IG9sZFZhbHVlO1xufTtcblxuQnV0dG9uTWFuYWdlci5wcm90b3R5cGUuc2V0VmlzaWJpbGl0eSA9IGZ1bmN0aW9uKGlzVmlzaWJsZSkge1xuICB0aGlzLmlzVmlzaWJsZSA9IGlzVmlzaWJsZTtcbiAgdGhpcy5mc0J1dHRvbi5zdHlsZS5kaXNwbGF5ID0gaXNWaXNpYmxlID8gJ2Jsb2NrJyA6ICdub25lJztcbiAgdGhpcy52ckJ1dHRvbi5zdHlsZS5kaXNwbGF5ID0gaXNWaXNpYmxlID8gJ2Jsb2NrJyA6ICdub25lJztcbn07XG5cbkJ1dHRvbk1hbmFnZXIucHJvdG90eXBlLmNyZWF0ZUNsaWNrSGFuZGxlcl8gPSBmdW5jdGlvbihldmVudE5hbWUpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uKGUpIHtcbiAgICBlLnN0b3BQcm9wYWdhdGlvbigpO1xuICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICB0aGlzLmVtaXQoZXZlbnROYW1lKTtcbiAgfS5iaW5kKHRoaXMpO1xufTtcblxuQnV0dG9uTWFuYWdlci5wcm90b3R5cGUubG9hZEljb25zXyA9IGZ1bmN0aW9uKCkge1xuICAvLyBQcmVsb2FkIHNvbWUgaGFyZC1jb2RlZCBTVkcuXG4gIHRoaXMuSUNPTlMgPSB7fTtcbiAgdGhpcy5JQ09OUy5jYXJkYm9hcmQgPSBVdGlsLmJhc2U2NCgnaW1hZ2Uvc3ZnK3htbCcsICdQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0l5TkhCNElpQm9aV2xuYUhROUlqSTBjSGdpSUhacFpYZENiM2c5SWpBZ01DQXlOQ0F5TkNJZ1ptbHNiRDBpSTBaR1JrWkdSaUkrQ2lBZ0lDQThjR0YwYUNCa1BTSk5NakF1TnpRZ05rZ3pMakl4UXpJdU5UVWdOaUF5SURZdU5UY2dNaUEzTGpJNGRqRXdMalEwWXpBZ0xqY3VOVFVnTVM0eU9DQXhMakl6SURFdU1qaG9OQzQzT1dNdU5USWdNQ0F1T1RZdExqTXpJREV1TVRRdExqYzViREV1TkMwekxqUTRZeTR5TXkwdU5Ua3VOemt0TVM0d01TQXhMalEwTFRFdU1ERnpNUzR5TVM0ME1pQXhMalExSURFdU1ERnNNUzR6T1NBekxqUTRZeTR4T1M0ME5pNDJNeTQzT1NBeExqRXhMamM1YURRdU56bGpMamN4SURBZ01TNHlOaTB1TlRjZ01TNHlOaTB4TGpJNFZqY3VNamhqTUMwdU55MHVOVFV0TVM0eU9DMHhMakkyTFRFdU1qaDZUVGN1TlNBeE5DNDJNbU10TVM0eE55QXdMVEl1TVRNdExqazFMVEl1TVRNdE1pNHhNaUF3TFRFdU1UY3VPVFl0TWk0eE15QXlMakV6TFRJdU1UTWdNUzR4T0NBd0lESXVNVEl1T1RZZ01pNHhNaUF5TGpFemN5MHVPVFVnTWk0eE1pMHlMakV5SURJdU1USjZiVGtnTUdNdE1TNHhOeUF3TFRJdU1UTXRMamsxTFRJdU1UTXRNaTR4TWlBd0xURXVNVGN1T1RZdE1pNHhNeUF5TGpFekxUSXVNVE56TWk0eE1pNDVOaUF5TGpFeUlESXVNVE10TGprMUlESXVNVEl0TWk0eE1pQXlMakV5ZWlJdlBnb2dJQ0FnUEhCaGRHZ2dabWxzYkQwaWJtOXVaU0lnWkQwaVRUQWdNR2d5TkhZeU5FZ3dWakI2SWk4K0Nqd3ZjM1puUGdvPScpO1xuICB0aGlzLklDT05TLmZ1bGxzY3JlZW4gPSBVdGlsLmJhc2U2NCgnaW1hZ2Uvc3ZnK3htbCcsICdQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0l5TkhCNElpQm9aV2xuYUhROUlqSTBjSGdpSUhacFpYZENiM2c5SWpBZ01DQXlOQ0F5TkNJZ1ptbHNiRDBpSTBaR1JrWkdSaUkrQ2lBZ0lDQThjR0YwYUNCa1BTSk5NQ0F3YURJMGRqSTBTREI2SWlCbWFXeHNQU0p1YjI1bElpOCtDaUFnSUNBOGNHRjBhQ0JrUFNKTk55QXhORWcxZGpWb05YWXRNa2czZGkwemVtMHRNaTAwYURKV04yZ3pWalZJTlhZMWVtMHhNaUEzYUMwemRqSm9OWFl0TldndE1uWXplazB4TkNBMWRqSm9NM1l6YURKV05XZ3ROWG9pTHo0S1BDOXpkbWMrQ2c9PScpO1xuICB0aGlzLklDT05TLmV4aXRGdWxsc2NyZWVuID0gVXRpbC5iYXNlNjQoJ2ltYWdlL3N2Zyt4bWwnLCAnUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJeU5IQjRJaUJvWldsbmFIUTlJakkwY0hnaUlIWnBaWGRDYjNnOUlqQWdNQ0F5TkNBeU5DSWdabWxzYkQwaUkwWkdSa1pHUmlJK0NpQWdJQ0E4Y0dGMGFDQmtQU0pOTUNBd2FESTBkakkwU0RCNklpQm1hV3hzUFNKdWIyNWxJaTgrQ2lBZ0lDQThjR0YwYUNCa1BTSk5OU0F4Tm1nemRqTm9Nbll0TlVnMWRqSjZiVE10T0VnMWRqSm9OVlkxU0RoMk0zcHROaUF4TVdneWRpMHphRE4yTFRKb0xUVjJOWHB0TWkweE1WWTFhQzB5ZGpWb05WWTRhQzB6ZWlJdlBnbzhMM04yWno0SycpO1xuICB0aGlzLklDT05TLnNldHRpbmdzID0gVXRpbC5iYXNlNjQoJ2ltYWdlL3N2Zyt4bWwnLCAnUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJeU5IQjRJaUJvWldsbmFIUTlJakkwY0hnaUlIWnBaWGRDYjNnOUlqQWdNQ0F5TkNBeU5DSWdabWxzYkQwaUkwWkdSa1pHUmlJK0NpQWdJQ0E4Y0dGMGFDQmtQU0pOTUNBd2FESTBkakkwU0RCNklpQm1hV3hzUFNKdWIyNWxJaTgrQ2lBZ0lDQThjR0YwYUNCa1BTSk5NVGt1TkRNZ01USXVPVGhqTGpBMExTNHpNaTR3TnkwdU5qUXVNRGN0TGprNGN5MHVNRE10TGpZMkxTNHdOeTB1T1Roc01pNHhNUzB4TGpZMVl5NHhPUzB1TVRVdU1qUXRMalF5TGpFeUxTNDJOR3d0TWkwekxqUTJZeTB1TVRJdExqSXlMUzR6T1MwdU15MHVOakV0TGpJeWJDMHlMalE1SURGakxTNDFNaTB1TkMweExqQTRMUzQzTXkweExqWTVMUzQ1T0d3dExqTTRMVEl1TmpWRE1UUXVORFlnTWk0eE9DQXhOQzR5TlNBeUlERTBJREpvTFRSakxTNHlOU0F3TFM0ME5pNHhPQzB1TkRrdU5ESnNMUzR6T0NBeUxqWTFZeTB1TmpFdU1qVXRNUzR4Tnk0MU9TMHhMalk1TGprNGJDMHlMalE1TFRGakxTNHlNeTB1TURrdExqUTVJREF0TGpZeExqSXliQzB5SURNdU5EWmpMUzR4TXk0eU1pMHVNRGN1TkRrdU1USXVOalJzTWk0eE1TQXhMalkxWXkwdU1EUXVNekl0TGpBM0xqWTFMUzR3Tnk0NU9ITXVNRE11TmpZdU1EY3VPVGhzTFRJdU1URWdNUzQyTldNdExqRTVMakUxTFM0eU5DNDBNaTB1TVRJdU5qUnNNaUF6TGpRMll5NHhNaTR5TWk0ek9TNHpMall4TGpJeWJESXVORGt0TVdNdU5USXVOQ0F4TGpBNExqY3pJREV1TmprdU9UaHNMak00SURJdU5qVmpMakF6TGpJMExqSTBMalF5TGpRNUxqUXlhRFJqTGpJMUlEQWdMalEyTFM0eE9DNDBPUzB1TkRKc0xqTTRMVEl1TmpWakxqWXhMUzR5TlNBeExqRTNMUzQxT1NBeExqWTVMUzQ1T0d3eUxqUTVJREZqTGpJekxqQTVMalE1SURBZ0xqWXhMUzR5TW13eUxUTXVORFpqTGpFeUxTNHlNaTR3TnkwdU5Ea3RMakV5TFM0Mk5Hd3RNaTR4TVMweExqWTFlazB4TWlBeE5TNDFZeTB4TGpreklEQXRNeTQxTFRFdU5UY3RNeTQxTFRNdU5YTXhMalUzTFRNdU5TQXpMalV0TXk0MUlETXVOU0F4TGpVM0lETXVOU0F6TGpVdE1TNDFOeUF6TGpVdE15NDFJRE11TlhvaUx6NEtQQzl6ZG1jK0NnPT0nKTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gQnV0dG9uTWFuYWdlcjtcblxufSx7XCIuL2VtaXR0ZXIuanNcIjoyLFwiLi9tb2Rlcy5qc1wiOjMsXCIuL3V0aWwuanNcIjo0fV0sMjpbZnVuY3Rpb24oX2RlcmVxXyxtb2R1bGUsZXhwb3J0cyl7XG4vKlxuICogQ29weXJpZ2h0IDIwMTUgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqL1xuXG5mdW5jdGlvbiBFbWl0dGVyKCkge1xuICB0aGlzLmNhbGxiYWNrcyA9IHt9O1xufVxuXG5FbWl0dGVyLnByb3RvdHlwZS5lbWl0ID0gZnVuY3Rpb24oZXZlbnROYW1lKSB7XG4gIHZhciBjYWxsYmFja3MgPSB0aGlzLmNhbGxiYWNrc1tldmVudE5hbWVdO1xuICBpZiAoIWNhbGxiYWNrcykge1xuICAgIC8vY29uc29sZS5sb2coJ05vIHZhbGlkIGNhbGxiYWNrIHNwZWNpZmllZC4nKTtcbiAgICByZXR1cm47XG4gIH1cbiAgdmFyIGFyZ3MgPSBbXS5zbGljZS5jYWxsKGFyZ3VtZW50cyk7XG4gIC8vIEVsaW1pbmF0ZSB0aGUgZmlyc3QgcGFyYW0gKHRoZSBjYWxsYmFjaykuXG4gIGFyZ3Muc2hpZnQoKTtcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBjYWxsYmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICBjYWxsYmFja3NbaV0uYXBwbHkodGhpcywgYXJncyk7XG4gIH1cbn07XG5cbkVtaXR0ZXIucHJvdG90eXBlLm9uID0gZnVuY3Rpb24oZXZlbnROYW1lLCBjYWxsYmFjaykge1xuICBpZiAoZXZlbnROYW1lIGluIHRoaXMuY2FsbGJhY2tzKSB7XG4gICAgdGhpcy5jYWxsYmFja3NbZXZlbnROYW1lXS5wdXNoKGNhbGxiYWNrKTtcbiAgfSBlbHNlIHtcbiAgICB0aGlzLmNhbGxiYWNrc1tldmVudE5hbWVdID0gW2NhbGxiYWNrXTtcbiAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMgPSBFbWl0dGVyO1xuXG59LHt9XSwzOltmdW5jdGlvbihfZGVyZXFfLG1vZHVsZSxleHBvcnRzKXtcbi8qXG4gKiBDb3B5cmlnaHQgMjAxNSBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICovXG5cbnZhciBNb2RlcyA9IHtcbiAgVU5LTk9XTjogMCxcbiAgLy8gTm90IGZ1bGxzY3JlZW4sIGp1c3QgdHJhY2tpbmcuXG4gIE5PUk1BTDogMSxcbiAgLy8gTWFnaWMgd2luZG93IGltbWVyc2l2ZSBtb2RlLlxuICBNQUdJQ19XSU5ET1c6IDIsXG4gIC8vIEZ1bGwgc2NyZWVuIHNwbGl0IHNjcmVlbiBWUiBtb2RlLlxuICBWUjogMyxcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gTW9kZXM7XG5cbn0se31dLDQ6W2Z1bmN0aW9uKF9kZXJlcV8sbW9kdWxlLGV4cG9ydHMpe1xuLypcbiAqIENvcHlyaWdodCAyMDE1IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKi9cblxudmFyIFV0aWwgPSB7fTtcblxuVXRpbC5iYXNlNjQgPSBmdW5jdGlvbihtaW1lVHlwZSwgYmFzZTY0KSB7XG4gIHJldHVybiAnZGF0YTonICsgbWltZVR5cGUgKyAnO2Jhc2U2NCwnICsgYmFzZTY0O1xufTtcblxuVXRpbC5pc01vYmlsZSA9IGZ1bmN0aW9uKCkge1xuICB2YXIgY2hlY2sgPSBmYWxzZTtcbiAgKGZ1bmN0aW9uKGEpe2lmKC8oYW5kcm9pZHxiYlxcZCt8bWVlZ28pLittb2JpbGV8YXZhbnRnb3xiYWRhXFwvfGJsYWNrYmVycnl8YmxhemVyfGNvbXBhbHxlbGFpbmV8ZmVubmVjfGhpcHRvcHxpZW1vYmlsZXxpcChob25lfG9kKXxpcmlzfGtpbmRsZXxsZ2UgfG1hZW1vfG1pZHB8bW1wfG1vYmlsZS4rZmlyZWZveHxuZXRmcm9udHxvcGVyYSBtKG9ifGluKWl8cGFsbSggb3MpP3xwaG9uZXxwKGl4aXxyZSlcXC98cGx1Y2tlcnxwb2NrZXR8cHNwfHNlcmllcyg0fDYpMHxzeW1iaWFufHRyZW98dXBcXC4oYnJvd3NlcnxsaW5rKXx2b2RhZm9uZXx3YXB8d2luZG93cyBjZXx4ZGF8eGlpbm8vaS50ZXN0KGEpfHwvMTIwN3w2MzEwfDY1OTB8M2dzb3w0dGhwfDUwWzEtNl1pfDc3MHN8ODAyc3xhIHdhfGFiYWN8YWMoZXJ8b298c1xcLSl8YWkoa298cm4pfGFsKGF2fGNhfGNvKXxhbW9pfGFuKGV4fG55fHl3KXxhcHR1fGFyKGNofGdvKXxhcyh0ZXx1cyl8YXR0d3xhdShkaXxcXC1tfHIgfHMgKXxhdmFufGJlKGNrfGxsfG5xKXxiaShsYnxyZCl8YmwoYWN8YXopfGJyKGV8dil3fGJ1bWJ8YndcXC0obnx1KXxjNTVcXC98Y2FwaXxjY3dhfGNkbVxcLXxjZWxsfGNodG18Y2xkY3xjbWRcXC18Y28obXB8bmQpfGNyYXd8ZGEoaXR8bGx8bmcpfGRidGV8ZGNcXC1zfGRldml8ZGljYXxkbW9ifGRvKGN8cClvfGRzKDEyfFxcLWQpfGVsKDQ5fGFpKXxlbShsMnx1bCl8ZXIoaWN8azApfGVzbDh8ZXooWzQtN10wfG9zfHdhfHplKXxmZXRjfGZseShcXC18Xyl8ZzEgdXxnNTYwfGdlbmV8Z2ZcXC01fGdcXC1tb3xnbyhcXC53fG9kKXxncihhZHx1bil8aGFpZXxoY2l0fGhkXFwtKG18cHx0KXxoZWlcXC18aGkocHR8dGEpfGhwKCBpfGlwKXxoc1xcLWN8aHQoYyhcXC18IHxffGF8Z3xwfHN8dCl8dHApfGh1KGF3fHRjKXxpXFwtKDIwfGdvfG1hKXxpMjMwfGlhYyggfFxcLXxcXC8pfGlicm98aWRlYXxpZzAxfGlrb218aW0xa3xpbm5vfGlwYXF8aXJpc3xqYSh0fHYpYXxqYnJvfGplbXV8amlnc3xrZGRpfGtlaml8a2d0KCB8XFwvKXxrbG9ufGtwdCB8a3djXFwtfGt5byhjfGspfGxlKG5vfHhpKXxsZyggZ3xcXC8oa3xsfHUpfDUwfDU0fFxcLVthLXddKXxsaWJ3fGx5bnh8bTFcXC13fG0zZ2F8bTUwXFwvfG1hKHRlfHVpfHhvKXxtYygwMXwyMXxjYSl8bVxcLWNyfG1lKHJjfHJpKXxtaShvOHxvYXx0cyl8bW1lZnxtbygwMXwwMnxiaXxkZXxkb3x0KFxcLXwgfG98dil8enopfG10KDUwfHAxfHYgKXxtd2JwfG15d2F8bjEwWzAtMl18bjIwWzItM118bjMwKDB8Mil8bjUwKDB8Mnw1KXxuNygwKDB8MSl8MTApfG5lKChjfG0pXFwtfG9ufHRmfHdmfHdnfHd0KXxub2soNnxpKXxuenBofG8yaW18b3AodGl8d3YpfG9yYW58b3dnMXxwODAwfHBhbihhfGR8dCl8cGR4Z3xwZygxM3xcXC0oWzEtOF18YykpfHBoaWx8cGlyZXxwbChheXx1Yyl8cG5cXC0yfHBvKGNrfHJ0fHNlKXxwcm94fHBzaW98cHRcXC1nfHFhXFwtYXxxYygwN3wxMnwyMXwzMnw2MHxcXC1bMi03XXxpXFwtKXxxdGVrfHIzODB8cjYwMHxyYWtzfHJpbTl8cm8odmV8em8pfHM1NVxcL3xzYShnZXxtYXxtbXxtc3xueXx2YSl8c2MoMDF8aFxcLXxvb3xwXFwtKXxzZGtcXC98c2UoYyhcXC18MHwxKXw0N3xtY3xuZHxyaSl8c2doXFwtfHNoYXJ8c2llKFxcLXxtKXxza1xcLTB8c2woNDV8aWQpfHNtKGFsfGFyfGIzfGl0fHQ1KXxzbyhmdHxueSl8c3AoMDF8aFxcLXx2XFwtfHYgKXxzeSgwMXxtYil8dDIoMTh8NTApfHQ2KDAwfDEwfDE4KXx0YShndHxsayl8dGNsXFwtfHRkZ1xcLXx0ZWwoaXxtKXx0aW1cXC18dFxcLW1vfHRvKHBsfHNoKXx0cyg3MHxtXFwtfG0zfG01KXx0eFxcLTl8dXAoXFwuYnxnMXxzaSl8dXRzdHx2NDAwfHY3NTB8dmVyaXx2aShyZ3x0ZSl8dmsoNDB8NVswLTNdfFxcLXYpfHZtNDB8dm9kYXx2dWxjfHZ4KDUyfDUzfDYwfDYxfDcwfDgwfDgxfDgzfDg1fDk4KXx3M2MoXFwtfCApfHdlYmN8d2hpdHx3aShnIHxuY3xudyl8d21sYnx3b251fHg3MDB8eWFzXFwtfHlvdXJ8emV0b3x6dGVcXC0vaS50ZXN0KGEuc3Vic3RyKDAsNCkpKWNoZWNrID0gdHJ1ZX0pKG5hdmlnYXRvci51c2VyQWdlbnR8fG5hdmlnYXRvci52ZW5kb3J8fHdpbmRvdy5vcGVyYSk7XG4gIHJldHVybiBjaGVjaztcbn07XG5cblV0aWwuaXNGaXJlZm94ID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiAvZmlyZWZveC9pLnRlc3QobmF2aWdhdG9yLnVzZXJBZ2VudCk7XG59O1xuXG5VdGlsLmlzSU9TID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiAvKGlQYWR8aVBob25lfGlQb2QpL2cudGVzdChuYXZpZ2F0b3IudXNlckFnZW50KTtcbn07XG5cblV0aWwuaXNJRnJhbWUgPSBmdW5jdGlvbigpIHtcbiAgdHJ5IHtcbiAgICByZXR1cm4gd2luZG93LnNlbGYgIT09IHdpbmRvdy50b3A7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxufTtcblxuVXRpbC5hcHBlbmRRdWVyeVBhcmFtZXRlciA9IGZ1bmN0aW9uKHVybCwga2V5LCB2YWx1ZSkge1xuICAvLyBEZXRlcm1pbmUgZGVsaW1pdGVyIGJhc2VkIG9uIGlmIHRoZSBVUkwgYWxyZWFkeSBHRVQgcGFyYW1ldGVycyBpbiBpdC5cbiAgdmFyIGRlbGltaXRlciA9ICh1cmwuaW5kZXhPZignPycpIDwgMCA/ICc/JyA6ICcmJyk7XG4gIHVybCArPSBkZWxpbWl0ZXIgKyBrZXkgKyAnPScgKyB2YWx1ZTtcbiAgcmV0dXJuIHVybDtcbn07XG5cbi8vIEZyb20gaHR0cDovL2dvby5nbC80V1gzdGdcblV0aWwuZ2V0UXVlcnlQYXJhbWV0ZXIgPSBmdW5jdGlvbihuYW1lKSB7XG4gIHZhciBuYW1lID0gbmFtZS5yZXBsYWNlKC9bXFxbXS8sIFwiXFxcXFtcIikucmVwbGFjZSgvW1xcXV0vLCBcIlxcXFxdXCIpO1xuICB2YXIgcmVnZXggPSBuZXcgUmVnRXhwKFwiW1xcXFw/Jl1cIiArIG5hbWUgKyBcIj0oW14mI10qKVwiKSxcbiAgICAgIHJlc3VsdHMgPSByZWdleC5leGVjKGxvY2F0aW9uLnNlYXJjaCk7XG4gIHJldHVybiByZXN1bHRzID09PSBudWxsID8gXCJcIiA6IGRlY29kZVVSSUNvbXBvbmVudChyZXN1bHRzWzFdLnJlcGxhY2UoL1xcKy9nLCBcIiBcIikpO1xufTtcblxuVXRpbC5pc0xhbmRzY2FwZU1vZGUgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuICh3aW5kb3cub3JpZW50YXRpb24gPT0gOTAgfHwgd2luZG93Lm9yaWVudGF0aW9uID09IC05MCk7XG59O1xuXG5VdGlsLmdldFNjcmVlbldpZHRoID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiBNYXRoLm1heCh3aW5kb3cuc2NyZWVuLndpZHRoLCB3aW5kb3cuc2NyZWVuLmhlaWdodCkgKlxuICAgICAgd2luZG93LmRldmljZVBpeGVsUmF0aW87XG59O1xuXG5VdGlsLmdldFNjcmVlbkhlaWdodCA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gTWF0aC5taW4od2luZG93LnNjcmVlbi53aWR0aCwgd2luZG93LnNjcmVlbi5oZWlnaHQpICpcbiAgICAgIHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBVdGlsO1xuXG59LHt9XSw1OltmdW5jdGlvbihfZGVyZXFfLG1vZHVsZSxleHBvcnRzKXtcbi8qXG4gKiBDb3B5cmlnaHQgMjAxNSBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICovXG5cbnZhciBCdXR0b25NYW5hZ2VyID0gX2RlcmVxXygnLi9idXR0b24tbWFuYWdlci5qcycpO1xudmFyIEVtaXR0ZXIgPSBfZGVyZXFfKCcuL2VtaXR0ZXIuanMnKTtcbnZhciBNb2RlcyA9IF9kZXJlcV8oJy4vbW9kZXMuanMnKTtcbnZhciBVdGlsID0gX2RlcmVxXygnLi91dGlsLmpzJyk7XG5cbi8qKlxuICogSGVscGVyIGZvciBnZXR0aW5nIGluIGFuZCBvdXQgb2YgVlIgbW9kZS5cbiAqL1xuZnVuY3Rpb24gV2ViVlJNYW5hZ2VyKHJlbmRlcmVyLCBlZmZlY3QsIHBhcmFtcykge1xuICB0aGlzLnBhcmFtcyA9IHBhcmFtcyB8fCB7fTtcblxuICB0aGlzLm1vZGUgPSBNb2Rlcy5VTktOT1dOO1xuXG4gIC8vIFNldCBvcHRpb24gdG8gaGlkZSB0aGUgYnV0dG9uLlxuICB0aGlzLmhpZGVCdXR0b24gPSB0aGlzLnBhcmFtcy5oaWRlQnV0dG9uIHx8IGZhbHNlO1xuICAvLyBXaGV0aGVyIG9yIG5vdCB0aGUgRk9WIHNob3VsZCBiZSBkaXN0b3J0ZWQgb3IgdW4tZGlzdG9ydGVkLiBCeSBkZWZhdWx0LCBpdFxuICAvLyBzaG91bGQgYmUgZGlzdG9ydGVkLCBidXQgaW4gdGhlIGNhc2Ugb2YgdmVydGV4IHNoYWRlciBiYXNlZCBkaXN0b3J0aW9uLFxuICAvLyBlbnN1cmUgdGhhdCB3ZSB1c2UgdW5kaXN0b3J0ZWQgcGFyYW1ldGVycy5cbiAgdGhpcy5wcmVkaXN0b3J0ZWQgPSAhIXRoaXMucGFyYW1zLnByZWRpc3RvcnRlZDtcblxuICAvLyBTYXZlIHRoZSBUSFJFRS5qcyByZW5kZXJlciBhbmQgZWZmZWN0IGZvciBsYXRlci5cbiAgdGhpcy5yZW5kZXJlciA9IHJlbmRlcmVyO1xuICB0aGlzLmVmZmVjdCA9IGVmZmVjdDtcbiAgdmFyIHBvbHlmaWxsV3JhcHBlciA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy53ZWJ2ci1wb2x5ZmlsbC1mdWxsc2NyZWVuLXdyYXBwZXInKTtcbiAgdGhpcy5idXR0b24gPSBuZXcgQnV0dG9uTWFuYWdlcihwb2x5ZmlsbFdyYXBwZXIpO1xuXG4gIHRoaXMuaXNGdWxsc2NyZWVuRGlzYWJsZWQgPSAhIVV0aWwuZ2V0UXVlcnlQYXJhbWV0ZXIoJ25vX2Z1bGxzY3JlZW4nKTtcbiAgdGhpcy5zdGFydE1vZGUgPSBNb2Rlcy5OT1JNQUw7XG4gIHZhciBzdGFydE1vZGVQYXJhbSA9IHBhcnNlSW50KFV0aWwuZ2V0UXVlcnlQYXJhbWV0ZXIoJ3N0YXJ0X21vZGUnKSk7XG4gIGlmICghaXNOYU4oc3RhcnRNb2RlUGFyYW0pKSB7XG4gICAgdGhpcy5zdGFydE1vZGUgPSBzdGFydE1vZGVQYXJhbTtcbiAgfVxuXG4gIGlmICh0aGlzLmhpZGVCdXR0b24pIHtcbiAgICB0aGlzLmJ1dHRvbi5zZXRWaXNpYmlsaXR5KGZhbHNlKTtcbiAgfVxuXG4gIC8vIENoZWNrIGlmIHRoZSBicm93c2VyIGlzIGNvbXBhdGlibGUgd2l0aCBXZWJWUi5cbiAgdGhpcy5nZXREZXZpY2VCeVR5cGVfKFZSRGlzcGxheSkudGhlbihmdW5jdGlvbihobWQpIHtcbiAgICB0aGlzLmhtZCA9IGhtZDtcblxuICAgIC8vIE9ubHkgZW5hYmxlIFZSIG1vZGUgaWYgdGhlcmUncyBhIFZSIGRldmljZSBhdHRhY2hlZCBvciB3ZSBhcmUgcnVubmluZyB0aGVcbiAgICAvLyBwb2x5ZmlsbCBvbiBtb2JpbGUuXG4gICAgaWYgKCF0aGlzLmlzVlJDb21wYXRpYmxlT3ZlcnJpZGUpIHtcbiAgICAgIHRoaXMuaXNWUkNvbXBhdGlibGUgPSAgIWhtZC5pc1BvbHlmaWxsZWQgfHwgVXRpbC5pc01vYmlsZSgpO1xuICAgIH1cblxuICAgIHN3aXRjaCAodGhpcy5zdGFydE1vZGUpIHtcbiAgICAgIGNhc2UgTW9kZXMuTUFHSUNfV0lORE9XOlxuICAgICAgICB0aGlzLnNldE1vZGVfKE1vZGVzLk1BR0lDX1dJTkRPVyk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSBNb2Rlcy5WUjpcbiAgICAgICAgdGhpcy5lbnRlclZSTW9kZV8oKTtcbiAgICAgICAgdGhpcy5zZXRNb2RlXyhNb2Rlcy5WUik7XG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgdGhpcy5zZXRNb2RlXyhNb2Rlcy5OT1JNQUwpO1xuICAgIH1cblxuICAgIHRoaXMuZW1pdCgnaW5pdGlhbGl6ZWQnKTtcbiAgfS5iaW5kKHRoaXMpKTtcblxuICAvLyBIb29rIHVwIGJ1dHRvbiBsaXN0ZW5lcnMuXG4gIHRoaXMuYnV0dG9uLm9uKCdmcycsIHRoaXMub25GU0NsaWNrXy5iaW5kKHRoaXMpKTtcbiAgdGhpcy5idXR0b24ub24oJ3ZyJywgdGhpcy5vblZSQ2xpY2tfLmJpbmQodGhpcykpO1xuXG4gIC8vIEJpbmQgdG8gZnVsbHNjcmVlbiBldmVudHMuXG4gIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3dlYmtpdGZ1bGxzY3JlZW5jaGFuZ2UnLFxuICAgICAgdGhpcy5vbkZ1bGxzY3JlZW5DaGFuZ2VfLmJpbmQodGhpcykpO1xuICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdtb3pmdWxsc2NyZWVuY2hhbmdlJyxcbiAgICAgIHRoaXMub25GdWxsc2NyZWVuQ2hhbmdlXy5iaW5kKHRoaXMpKTtcbiAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignbXNmdWxsc2NyZWVuY2hhbmdlJyxcbiAgICAgIHRoaXMub25GdWxsc2NyZWVuQ2hhbmdlXy5iaW5kKHRoaXMpKTtcblxuICAvLyBCaW5kIHRvIFZSKiBzcGVjaWZpYyBldmVudHMuXG4gIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCd2cmRpc3BsYXlwcmVzZW50Y2hhbmdlJyxcbiAgICAgIHRoaXMub25WUkRpc3BsYXlQcmVzZW50Q2hhbmdlXy5iaW5kKHRoaXMpKTtcbiAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3ZyZGlzcGxheWRldmljZXBhcmFtc2NoYW5nZScsXG4gICAgICB0aGlzLm9uVlJEaXNwbGF5RGV2aWNlUGFyYW1zQ2hhbmdlXy5iaW5kKHRoaXMpKTtcbn1cblxuV2ViVlJNYW5hZ2VyLnByb3RvdHlwZSA9IG5ldyBFbWl0dGVyKCk7XG5cbi8vIEV4cG9zZSB0aGVzZSB2YWx1ZXMgZXh0ZXJuYWxseS5cbldlYlZSTWFuYWdlci5Nb2RlcyA9IE1vZGVzO1xuXG5XZWJWUk1hbmFnZXIucHJvdG90eXBlLnJlbmRlciA9IGZ1bmN0aW9uKHNjZW5lLCBjYW1lcmEsIHRpbWVzdGFtcCkge1xuICAvLyBTY2VuZSBtYXkgYmUgYW4gYXJyYXkgb2YgdHdvIHNjZW5lcywgb25lIGZvciBlYWNoIGV5ZS5cbiAgaWYgKHNjZW5lIGluc3RhbmNlb2YgQXJyYXkpIHtcbiAgICB0aGlzLmVmZmVjdC5yZW5kZXIoc2NlbmVbMF0sIGNhbWVyYSk7XG4gIH0gZWxzZSB7XG4gICAgdGhpcy5lZmZlY3QucmVuZGVyKHNjZW5lLCBjYW1lcmEpO1xuICB9XG59O1xuXG5XZWJWUk1hbmFnZXIucHJvdG90eXBlLnNldFZSQ29tcGF0aWJsZU92ZXJyaWRlID0gZnVuY3Rpb24oaXNWUkNvbXBhdGlibGUpIHtcbiAgdGhpcy5pc1ZSQ29tcGF0aWJsZSA9IGlzVlJDb21wYXRpYmxlO1xuICB0aGlzLmlzVlJDb21wYXRpYmxlT3ZlcnJpZGUgPSB0cnVlO1xuXG4gIC8vIERvbid0IGFjdHVhbGx5IGNoYW5nZSBtb2RlcywganVzdCB1cGRhdGUgdGhlIGJ1dHRvbnMuXG4gIHRoaXMuYnV0dG9uLnNldE1vZGUodGhpcy5tb2RlLCB0aGlzLmlzVlJDb21wYXRpYmxlKTtcbn07XG5cbldlYlZSTWFuYWdlci5wcm90b3R5cGUuc2V0RnVsbHNjcmVlbkNhbGxiYWNrID0gZnVuY3Rpb24oY2FsbGJhY2spIHtcbiAgdGhpcy5mdWxsc2NyZWVuQ2FsbGJhY2sgPSBjYWxsYmFjaztcbn07XG5cbldlYlZSTWFuYWdlci5wcm90b3R5cGUuc2V0VlJDYWxsYmFjayA9IGZ1bmN0aW9uKGNhbGxiYWNrKSB7XG4gIHRoaXMudnJDYWxsYmFjayA9IGNhbGxiYWNrO1xufTtcblxuV2ViVlJNYW5hZ2VyLnByb3RvdHlwZS5zZXRFeGl0RnVsbHNjcmVlbkNhbGxiYWNrID0gZnVuY3Rpb24oY2FsbGJhY2spIHtcbiAgdGhpcy5leGl0RnVsbHNjcmVlbkNhbGxiYWNrID0gY2FsbGJhY2s7XG59XG5cbi8qKlxuICogUHJvbWlzZSByZXR1cm5zIHRydWUgaWYgdGhlcmUgaXMgYXQgbGVhc3Qgb25lIEhNRCBkZXZpY2UgYXZhaWxhYmxlLlxuICovXG5XZWJWUk1hbmFnZXIucHJvdG90eXBlLmdldERldmljZUJ5VHlwZV8gPSBmdW5jdGlvbih0eXBlKSB7XG4gIHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbihyZXNvbHZlLCByZWplY3QpIHtcbiAgICBuYXZpZ2F0b3IuZ2V0VlJEaXNwbGF5cygpLnRoZW4oZnVuY3Rpb24oZGlzcGxheXMpIHtcbiAgICAgIC8vIFByb21pc2Ugc3VjY2VlZHMsIGJ1dCBjaGVjayBpZiB0aGVyZSBhcmUgYW55IGRpc3BsYXlzIGFjdHVhbGx5LlxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBkaXNwbGF5cy5sZW5ndGg7IGkrKykge1xuICAgICAgICBpZiAoZGlzcGxheXNbaV0gaW5zdGFuY2VvZiB0eXBlKSB7XG4gICAgICAgICAgcmVzb2x2ZShkaXNwbGF5c1tpXSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJlc29sdmUobnVsbCk7XG4gICAgfSwgZnVuY3Rpb24oKSB7XG4gICAgICAvLyBObyBkaXNwbGF5cyBhcmUgZm91bmQuXG4gICAgICByZXNvbHZlKG51bGwpO1xuICAgIH0pO1xuICB9KTtcbn07XG5cbi8qKlxuICogSGVscGVyIGZvciBlbnRlcmluZyBWUiBtb2RlLlxuICovXG5XZWJWUk1hbmFnZXIucHJvdG90eXBlLmVudGVyVlJNb2RlXyA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLmhtZC5yZXF1ZXN0UHJlc2VudChbe1xuICAgIHNvdXJjZTogdGhpcy5yZW5kZXJlci5kb21FbGVtZW50LFxuICAgIHByZWRpc3RvcnRlZDogdGhpcy5wcmVkaXN0b3J0ZWRcbiAgfV0pO1xufTtcblxuV2ViVlJNYW5hZ2VyLnByb3RvdHlwZS5zZXRNb2RlXyA9IGZ1bmN0aW9uKG1vZGUpIHtcbiAgdmFyIG9sZE1vZGUgPSB0aGlzLm1vZGU7XG4gIGlmIChtb2RlID09IHRoaXMubW9kZSkge1xuICAgIGNvbnNvbGUud2FybignTm90IGNoYW5naW5nIG1vZGVzLCBhbHJlYWR5IGluICVzJywgbW9kZSk7XG4gICAgcmV0dXJuO1xuICB9XG4gIC8vIGNvbnNvbGUubG9nKCdNb2RlIGNoYW5nZTogJXMgPT4gJXMnLCB0aGlzLm1vZGUsIG1vZGUpO1xuICB0aGlzLm1vZGUgPSBtb2RlO1xuICB0aGlzLmJ1dHRvbi5zZXRNb2RlKG1vZGUsIHRoaXMuaXNWUkNvbXBhdGlibGUpO1xuXG4gIC8vIEVtaXQgYW4gZXZlbnQgaW5kaWNhdGluZyB0aGUgbW9kZSBjaGFuZ2VkLlxuICB0aGlzLmVtaXQoJ21vZGVjaGFuZ2UnLCBtb2RlLCBvbGRNb2RlKTtcbn07XG5cbi8qKlxuICogTWFpbiBidXR0b24gd2FzIGNsaWNrZWQuXG4gKi9cbldlYlZSTWFuYWdlci5wcm90b3R5cGUub25GU0NsaWNrXyA9IGZ1bmN0aW9uKCkge1xuICBzd2l0Y2ggKHRoaXMubW9kZSkge1xuICAgIGNhc2UgTW9kZXMuTk9STUFMOlxuICAgICAgLy8gVE9ETzogUmVtb3ZlIHRoaXMgaGFjayBpZi93aGVuIGlPUyBnZXRzIHJlYWwgZnVsbHNjcmVlbiBtb2RlLlxuICAgICAgLy8gSWYgdGhpcyBpcyBhbiBpZnJhbWUgb24gaU9TLCBicmVhayBvdXQgYW5kIG9wZW4gaW4gbm9fZnVsbHNjcmVlbiBtb2RlLlxuICAgICAgaWYgKFV0aWwuaXNJT1MoKSAmJiBVdGlsLmlzSUZyYW1lKCkpIHtcbiAgICAgICAgaWYgKHRoaXMuZnVsbHNjcmVlbkNhbGxiYWNrKSB7XG4gICAgICAgICAgdGhpcy5mdWxsc2NyZWVuQ2FsbGJhY2soKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB2YXIgdXJsID0gd2luZG93LmxvY2F0aW9uLmhyZWY7XG4gICAgICAgICAgdXJsID0gVXRpbC5hcHBlbmRRdWVyeVBhcmFtZXRlcih1cmwsICdub19mdWxsc2NyZWVuJywgJ3RydWUnKTtcbiAgICAgICAgICB1cmwgPSBVdGlsLmFwcGVuZFF1ZXJ5UGFyYW1ldGVyKHVybCwgJ3N0YXJ0X21vZGUnLCBNb2Rlcy5NQUdJQ19XSU5ET1cpO1xuICAgICAgICAgIHRvcC5sb2NhdGlvbi5ocmVmID0gdXJsO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgdGhpcy5zZXRNb2RlXyhNb2Rlcy5NQUdJQ19XSU5ET1cpO1xuICAgICAgdGhpcy5yZXF1ZXN0RnVsbHNjcmVlbl8oKTtcbiAgICAgIGJyZWFrO1xuICAgIGNhc2UgTW9kZXMuTUFHSUNfV0lORE9XOlxuICAgICAgaWYgKHRoaXMuaXNGdWxsc2NyZWVuRGlzYWJsZWQpIHtcbiAgICAgICAgd2luZG93Lmhpc3RvcnkuYmFjaygpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpZiAodGhpcy5leGl0RnVsbHNjcmVlbkNhbGxiYWNrKSB7XG4gICAgICAgIHRoaXMuZXhpdEZ1bGxzY3JlZW5DYWxsYmFjaygpO1xuICAgICAgfVxuICAgICAgdGhpcy5zZXRNb2RlXyhNb2Rlcy5OT1JNQUwpO1xuICAgICAgdGhpcy5leGl0RnVsbHNjcmVlbl8oKTtcbiAgICAgIGJyZWFrO1xuICB9XG59O1xuXG4vKipcbiAqIFRoZSBWUiBidXR0b24gd2FzIGNsaWNrZWQuXG4gKi9cbldlYlZSTWFuYWdlci5wcm90b3R5cGUub25WUkNsaWNrXyA9IGZ1bmN0aW9uKCkge1xuICAvLyBUT0RPOiBSZW1vdmUgdGhpcyBoYWNrIHdoZW4gaU9TIGhhcyBmdWxsc2NyZWVuIG1vZGUuXG4gIC8vIElmIHRoaXMgaXMgYW4gaWZyYW1lIG9uIGlPUywgYnJlYWsgb3V0IGFuZCBvcGVuIGluIG5vX2Z1bGxzY3JlZW4gbW9kZS5cbiAgaWYgKHRoaXMubW9kZSA9PSBNb2Rlcy5OT1JNQUwgJiYgVXRpbC5pc0lPUygpICYmIFV0aWwuaXNJRnJhbWUoKSkge1xuICAgIGlmICh0aGlzLnZyQ2FsbGJhY2spIHtcbiAgICAgIHRoaXMudnJDYWxsYmFjaygpO1xuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgdXJsID0gd2luZG93LmxvY2F0aW9uLmhyZWY7XG4gICAgICB1cmwgPSBVdGlsLmFwcGVuZFF1ZXJ5UGFyYW1ldGVyKHVybCwgJ25vX2Z1bGxzY3JlZW4nLCAndHJ1ZScpO1xuICAgICAgdXJsID0gVXRpbC5hcHBlbmRRdWVyeVBhcmFtZXRlcih1cmwsICdzdGFydF9tb2RlJywgTW9kZXMuVlIpO1xuICAgICAgdG9wLmxvY2F0aW9uLmhyZWYgPSB1cmw7XG4gICAgICByZXR1cm47XG4gICAgfVxuICB9XG4gIHRoaXMuZW50ZXJWUk1vZGVfKCk7XG59O1xuXG5XZWJWUk1hbmFnZXIucHJvdG90eXBlLnJlcXVlc3RGdWxsc2NyZWVuXyA9IGZ1bmN0aW9uKCkge1xuICB2YXIgY2FudmFzID0gZG9jdW1lbnQuYm9keTtcbiAgLy92YXIgY2FudmFzID0gdGhpcy5yZW5kZXJlci5kb21FbGVtZW50O1xuICBpZiAoY2FudmFzLnJlcXVlc3RGdWxsc2NyZWVuKSB7XG4gICAgY2FudmFzLnJlcXVlc3RGdWxsc2NyZWVuKCk7XG4gIH0gZWxzZSBpZiAoY2FudmFzLm1velJlcXVlc3RGdWxsU2NyZWVuKSB7XG4gICAgY2FudmFzLm1velJlcXVlc3RGdWxsU2NyZWVuKCk7XG4gIH0gZWxzZSBpZiAoY2FudmFzLndlYmtpdFJlcXVlc3RGdWxsc2NyZWVuKSB7XG4gICAgY2FudmFzLndlYmtpdFJlcXVlc3RGdWxsc2NyZWVuKCk7XG4gIH0gZWxzZSBpZiAoY2FudmFzLm1zUmVxdWVzdEZ1bGxzY3JlZW4pIHtcbiAgICBjYW52YXMubXNSZXF1ZXN0RnVsbHNjcmVlbigpO1xuICB9XG59O1xuXG5XZWJWUk1hbmFnZXIucHJvdG90eXBlLmV4aXRGdWxsc2NyZWVuXyA9IGZ1bmN0aW9uKCkge1xuICBpZiAoZG9jdW1lbnQuZXhpdEZ1bGxzY3JlZW4pIHtcbiAgICBkb2N1bWVudC5leGl0RnVsbHNjcmVlbigpO1xuICB9IGVsc2UgaWYgKGRvY3VtZW50Lm1vekNhbmNlbEZ1bGxTY3JlZW4pIHtcbiAgICBkb2N1bWVudC5tb3pDYW5jZWxGdWxsU2NyZWVuKCk7XG4gIH0gZWxzZSBpZiAoZG9jdW1lbnQud2Via2l0RXhpdEZ1bGxzY3JlZW4pIHtcbiAgICBkb2N1bWVudC53ZWJraXRFeGl0RnVsbHNjcmVlbigpO1xuICB9IGVsc2UgaWYgKGRvY3VtZW50Lm1zRXhpdEZ1bGxzY3JlZW4pIHtcbiAgICBkb2N1bWVudC5tc0V4aXRGdWxsc2NyZWVuKCk7XG4gIH1cbn07XG5cbldlYlZSTWFuYWdlci5wcm90b3R5cGUub25WUkRpc3BsYXlQcmVzZW50Q2hhbmdlXyA9IGZ1bmN0aW9uKGUpIHtcbiAgY29uc29sZS5sb2coJ29uVlJEaXNwbGF5UHJlc2VudENoYW5nZV8nLCBlKTtcbiAgaWYgKHRoaXMuaG1kLmlzUHJlc2VudGluZykge1xuICAgIHRoaXMuc2V0TW9kZV8oTW9kZXMuVlIpO1xuICB9IGVsc2Uge1xuICAgIHRoaXMuc2V0TW9kZV8oTW9kZXMuTk9STUFMKTtcbiAgfVxufTtcblxuV2ViVlJNYW5hZ2VyLnByb3RvdHlwZS5vblZSRGlzcGxheURldmljZVBhcmFtc0NoYW5nZV8gPSBmdW5jdGlvbihlKSB7XG4gIGNvbnNvbGUubG9nKCdvblZSRGlzcGxheURldmljZVBhcmFtc0NoYW5nZV8nLCBlKTtcbn07XG5cbldlYlZSTWFuYWdlci5wcm90b3R5cGUub25GdWxsc2NyZWVuQ2hhbmdlXyA9IGZ1bmN0aW9uKGUpIHtcbiAgLy8gSWYgd2UgbGVhdmUgZnVsbC1zY3JlZW4sIGdvIGJhY2sgdG8gbm9ybWFsIG1vZGUuXG4gIGlmIChkb2N1bWVudC53ZWJraXRGdWxsc2NyZWVuRWxlbWVudCA9PT0gbnVsbCB8fFxuICAgICAgZG9jdW1lbnQubW96RnVsbFNjcmVlbkVsZW1lbnQgPT09IG51bGwpIHtcbiAgICB0aGlzLnNldE1vZGVfKE1vZGVzLk5PUk1BTCk7XG4gIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gV2ViVlJNYW5hZ2VyO1xuXG59LHtcIi4vYnV0dG9uLW1hbmFnZXIuanNcIjoxLFwiLi9lbWl0dGVyLmpzXCI6MixcIi4vbW9kZXMuanNcIjozLFwiLi91dGlsLmpzXCI6NH1dfSx7fSxbNV0pKDUpXG59KTsiLCJtb2R1bGUuZXhwb3J0cz17XG4gIFwibmFtZVwiOiBcIndlYnZyLXBvbHlmaWxsXCIsXG4gIFwidmVyc2lvblwiOiBcIjAuOS4zNVwiLFxuICBcImhvbWVwYWdlXCI6IFwiaHR0cHM6Ly9naXRodWIuY29tL2dvb2dsZXZyL3dlYnZyLXBvbHlmaWxsXCIsXG4gIFwiYXV0aG9yc1wiOiBbXG4gICAgXCJCb3JpcyBTbXVzIDxib3Jpc0BzbXVzLmNvbT5cIixcbiAgICBcIkJyYW5kb24gSm9uZXMgPHRvamlyb0BnbWFpbC5jb20+XCIsXG4gICAgXCJKb3JkYW4gU2FudGVsbCA8am9yZGFuQGpzYW50ZWxsLmNvbT5cIlxuICBdLFxuICBcImRlc2NyaXB0aW9uXCI6IFwiVXNlIFdlYlZSIHRvZGF5LCBvbiBtb2JpbGUgb3IgZGVza3RvcCwgd2l0aG91dCByZXF1aXJpbmcgYSBzcGVjaWFsIGJyb3dzZXIgYnVpbGQuXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcImNoYWlcIjogXCJeMy41LjBcIixcbiAgICBcImpzZG9tXCI6IFwiXjkuMTIuMFwiLFxuICAgIFwibW9jaGFcIjogXCJeMy4yLjBcIixcbiAgICBcInNlbXZlclwiOiBcIl41LjMuMFwiLFxuICAgIFwid2VicGFja1wiOiBcIl4yLjYuMVwiLFxuICAgIFwid2VicGFjay1kZXYtc2VydmVyXCI6IFwiXjIuNC41XCJcbiAgfSxcbiAgXCJtYWluXCI6IFwic3JjL25vZGUtZW50cnlcIixcbiAgXCJrZXl3b3Jkc1wiOiBbXG4gICAgXCJ2clwiLFxuICAgIFwid2VidnJcIlxuICBdLFxuICBcImxpY2Vuc2VcIjogXCJBcGFjaGUtMi4wXCIsXG4gIFwic2NyaXB0c1wiOiB7XG4gICAgXCJzdGFydFwiOiBcIm5wbSBydW4gd2F0Y2hcIixcbiAgICBcIndhdGNoXCI6IFwid2VicGFjay1kZXYtc2VydmVyXCIsXG4gICAgXCJidWlsZFwiOiBcIndlYnBhY2tcIixcbiAgICBcInRlc3RcIjogXCJtb2NoYVwiXG4gIH0sXG4gIFwicmVwb3NpdG9yeVwiOiBcImdvb2dsZXZyL3dlYnZyLXBvbHlmaWxsXCIsXG4gIFwiYnVnc1wiOiB7XG4gICAgXCJ1cmxcIjogXCJodHRwczovL2dpdGh1Yi5jb20vZ29vZ2xldnIvd2VidnItcG9seWZpbGwvaXNzdWVzXCJcbiAgfVxufSIsIi8qXG4gKiBDb3B5cmlnaHQgMjAxNSBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICovXG5cbnZhciBVdGlsID0gcmVxdWlyZSgnLi91dGlsLmpzJyk7XG52YXIgV2FrZUxvY2sgPSByZXF1aXJlKCcuL3dha2Vsb2NrLmpzJyk7XG5cbi8vIFN0YXJ0IGF0IGEgaGlnaGVyIG51bWJlciB0byByZWR1Y2UgY2hhbmNlIG9mIGNvbmZsaWN0LlxudmFyIG5leHREaXNwbGF5SWQgPSAxMDAwO1xudmFyIGhhc1Nob3dEZXByZWNhdGlvbldhcm5pbmcgPSBmYWxzZTtcblxudmFyIGRlZmF1bHRMZWZ0Qm91bmRzID0gWzAsIDAsIDAuNSwgMV07XG52YXIgZGVmYXVsdFJpZ2h0Qm91bmRzID0gWzAuNSwgMCwgMC41LCAxXTtcblxuLyoqXG4gKiBUaGUgYmFzZSBjbGFzcyBmb3IgYWxsIFZSIGZyYW1lIGRhdGEuXG4gKi9cblxuZnVuY3Rpb24gVlJGcmFtZURhdGEoKSB7XG4gIHRoaXMubGVmdFByb2plY3Rpb25NYXRyaXggPSBuZXcgRmxvYXQzMkFycmF5KDE2KTtcbiAgdGhpcy5sZWZ0Vmlld01hdHJpeCA9IG5ldyBGbG9hdDMyQXJyYXkoMTYpO1xuICB0aGlzLnJpZ2h0UHJvamVjdGlvbk1hdHJpeCA9IG5ldyBGbG9hdDMyQXJyYXkoMTYpO1xuICB0aGlzLnJpZ2h0Vmlld01hdHJpeCA9IG5ldyBGbG9hdDMyQXJyYXkoMTYpO1xuICB0aGlzLnBvc2UgPSBudWxsO1xufTtcblxuLyoqXG4gKiBUaGUgYmFzZSBjbGFzcyBmb3IgYWxsIFZSIGRpc3BsYXlzLlxuICovXG5mdW5jdGlvbiBWUkRpc3BsYXkoKSB7XG4gIHRoaXMuaXNQb2x5ZmlsbGVkID0gdHJ1ZTtcbiAgdGhpcy5kaXNwbGF5SWQgPSBuZXh0RGlzcGxheUlkKys7XG4gIHRoaXMuZGlzcGxheU5hbWUgPSAnd2VidnItcG9seWZpbGwgZGlzcGxheU5hbWUnO1xuXG4gIHRoaXMuZGVwdGhOZWFyID0gMC4wMTtcbiAgdGhpcy5kZXB0aEZhciA9IDEwMDAwLjA7XG5cbiAgdGhpcy5pc0Nvbm5lY3RlZCA9IHRydWU7XG4gIHRoaXMuaXNQcmVzZW50aW5nID0gZmFsc2U7XG4gIHRoaXMuY2FwYWJpbGl0aWVzID0ge1xuICAgIGhhc1Bvc2l0aW9uOiBmYWxzZSxcbiAgICBoYXNPcmllbnRhdGlvbjogZmFsc2UsXG4gICAgaGFzRXh0ZXJuYWxEaXNwbGF5OiBmYWxzZSxcbiAgICBjYW5QcmVzZW50OiBmYWxzZSxcbiAgICBtYXhMYXllcnM6IDFcbiAgfTtcbiAgdGhpcy5zdGFnZVBhcmFtZXRlcnMgPSBudWxsO1xuXG4gIC8vIFwiUHJpdmF0ZVwiIG1lbWJlcnMuXG4gIHRoaXMud2FpdGluZ0ZvclByZXNlbnRfID0gZmFsc2U7XG4gIHRoaXMubGF5ZXJfID0gbnVsbDtcblxuICB0aGlzLmZ1bGxzY3JlZW5FbGVtZW50XyA9IG51bGw7XG4gIHRoaXMuZnVsbHNjcmVlbldyYXBwZXJfID0gbnVsbDtcbiAgdGhpcy5mdWxsc2NyZWVuRWxlbWVudENhY2hlZFN0eWxlXyA9IG51bGw7XG5cbiAgdGhpcy5mdWxsc2NyZWVuRXZlbnRUYXJnZXRfID0gbnVsbDtcbiAgdGhpcy5mdWxsc2NyZWVuQ2hhbmdlSGFuZGxlcl8gPSBudWxsO1xuICB0aGlzLmZ1bGxzY3JlZW5FcnJvckhhbmRsZXJfID0gbnVsbDtcblxuICB0aGlzLndha2Vsb2NrXyA9IG5ldyBXYWtlTG9jaygpO1xufVxuXG5WUkRpc3BsYXkucHJvdG90eXBlLmdldEZyYW1lRGF0YSA9IGZ1bmN0aW9uKGZyYW1lRGF0YSkge1xuICAvLyBUT0RPOiBUZWNobmljYWxseSB0aGlzIHNob3VsZCByZXRhaW4gaXQncyB2YWx1ZSBmb3IgdGhlIGR1cmF0aW9uIG9mIGEgZnJhbWVcbiAgLy8gYnV0IEkgZG91YnQgdGhhdCdzIHByYWN0aWNhbCB0byBkbyBpbiBqYXZhc2NyaXB0LlxuICByZXR1cm4gVXRpbC5mcmFtZURhdGFGcm9tUG9zZShmcmFtZURhdGEsIHRoaXMuZ2V0UG9zZSgpLCB0aGlzKTtcbn07XG5cblZSRGlzcGxheS5wcm90b3R5cGUuZ2V0UG9zZSA9IGZ1bmN0aW9uKCkge1xuICAvLyBUT0RPOiBUZWNobmljYWxseSB0aGlzIHNob3VsZCByZXRhaW4gaXQncyB2YWx1ZSBmb3IgdGhlIGR1cmF0aW9uIG9mIGEgZnJhbWVcbiAgLy8gYnV0IEkgZG91YnQgdGhhdCdzIHByYWN0aWNhbCB0byBkbyBpbiBqYXZhc2NyaXB0LlxuICByZXR1cm4gdGhpcy5nZXRJbW1lZGlhdGVQb3NlKCk7XG59O1xuXG5WUkRpc3BsYXkucHJvdG90eXBlLnJlcXVlc3RBbmltYXRpb25GcmFtZSA9IGZ1bmN0aW9uKGNhbGxiYWNrKSB7XG4gIHJldHVybiB3aW5kb3cucmVxdWVzdEFuaW1hdGlvbkZyYW1lKGNhbGxiYWNrKTtcbn07XG5cblZSRGlzcGxheS5wcm90b3R5cGUuY2FuY2VsQW5pbWF0aW9uRnJhbWUgPSBmdW5jdGlvbihpZCkge1xuICByZXR1cm4gd2luZG93LmNhbmNlbEFuaW1hdGlvbkZyYW1lKGlkKTtcbn07XG5cblZSRGlzcGxheS5wcm90b3R5cGUud3JhcEZvckZ1bGxzY3JlZW4gPSBmdW5jdGlvbihlbGVtZW50KSB7XG4gIC8vIERvbid0IHdyYXAgaW4gaU9TLlxuICBpZiAoVXRpbC5pc0lPUygpKSB7XG4gICAgcmV0dXJuIGVsZW1lbnQ7XG4gIH1cbiAgaWYgKCF0aGlzLmZ1bGxzY3JlZW5XcmFwcGVyXykge1xuICAgIHRoaXMuZnVsbHNjcmVlbldyYXBwZXJfID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgdmFyIGNzc1Byb3BlcnRpZXMgPSBbXG4gICAgICAnaGVpZ2h0OiAnICsgTWF0aC5taW4oc2NyZWVuLmhlaWdodCwgc2NyZWVuLndpZHRoKSArICdweCAhaW1wb3J0YW50JyxcbiAgICAgICd0b3A6IDAgIWltcG9ydGFudCcsXG4gICAgICAnbGVmdDogMCAhaW1wb3J0YW50JyxcbiAgICAgICdyaWdodDogMCAhaW1wb3J0YW50JyxcbiAgICAgICdib3JkZXI6IDAnLFxuICAgICAgJ21hcmdpbjogMCcsXG4gICAgICAncGFkZGluZzogMCcsXG4gICAgICAnei1pbmRleDogOTk5OTk5ICFpbXBvcnRhbnQnLFxuICAgICAgJ3Bvc2l0aW9uOiBmaXhlZCcsXG4gICAgXTtcbiAgICB0aGlzLmZ1bGxzY3JlZW5XcmFwcGVyXy5zZXRBdHRyaWJ1dGUoJ3N0eWxlJywgY3NzUHJvcGVydGllcy5qb2luKCc7ICcpICsgJzsnKTtcbiAgICB0aGlzLmZ1bGxzY3JlZW5XcmFwcGVyXy5jbGFzc0xpc3QuYWRkKCd3ZWJ2ci1wb2x5ZmlsbC1mdWxsc2NyZWVuLXdyYXBwZXInKTtcbiAgfVxuXG4gIGlmICh0aGlzLmZ1bGxzY3JlZW5FbGVtZW50XyA9PSBlbGVtZW50KSB7XG4gICAgcmV0dXJuIHRoaXMuZnVsbHNjcmVlbldyYXBwZXJfO1xuICB9XG5cbiAgLy8gUmVtb3ZlIGFueSBwcmV2aW91c2x5IGFwcGxpZWQgd3JhcHBlcnNcbiAgdGhpcy5yZW1vdmVGdWxsc2NyZWVuV3JhcHBlcigpO1xuXG4gIHRoaXMuZnVsbHNjcmVlbkVsZW1lbnRfID0gZWxlbWVudDtcbiAgdmFyIHBhcmVudCA9IHRoaXMuZnVsbHNjcmVlbkVsZW1lbnRfLnBhcmVudEVsZW1lbnQ7XG4gIHBhcmVudC5pbnNlcnRCZWZvcmUodGhpcy5mdWxsc2NyZWVuV3JhcHBlcl8sIHRoaXMuZnVsbHNjcmVlbkVsZW1lbnRfKTtcbiAgcGFyZW50LnJlbW92ZUNoaWxkKHRoaXMuZnVsbHNjcmVlbkVsZW1lbnRfKTtcbiAgdGhpcy5mdWxsc2NyZWVuV3JhcHBlcl8uaW5zZXJ0QmVmb3JlKHRoaXMuZnVsbHNjcmVlbkVsZW1lbnRfLCB0aGlzLmZ1bGxzY3JlZW5XcmFwcGVyXy5maXJzdENoaWxkKTtcbiAgdGhpcy5mdWxsc2NyZWVuRWxlbWVudENhY2hlZFN0eWxlXyA9IHRoaXMuZnVsbHNjcmVlbkVsZW1lbnRfLmdldEF0dHJpYnV0ZSgnc3R5bGUnKTtcblxuICB2YXIgc2VsZiA9IHRoaXM7XG4gIGZ1bmN0aW9uIGFwcGx5RnVsbHNjcmVlbkVsZW1lbnRTdHlsZSgpIHtcbiAgICBpZiAoIXNlbGYuZnVsbHNjcmVlbkVsZW1lbnRfKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIGNzc1Byb3BlcnRpZXMgPSBbXG4gICAgICAncG9zaXRpb246IGFic29sdXRlJyxcbiAgICAgICd0b3A6IDAnLFxuICAgICAgJ2xlZnQ6IDAnLFxuICAgICAgJ3dpZHRoOiAnICsgTWF0aC5tYXgoc2NyZWVuLndpZHRoLCBzY3JlZW4uaGVpZ2h0KSArICdweCcsXG4gICAgICAnaGVpZ2h0OiAnICsgTWF0aC5taW4oc2NyZWVuLmhlaWdodCwgc2NyZWVuLndpZHRoKSArICdweCcsXG4gICAgICAnYm9yZGVyOiAwJyxcbiAgICAgICdtYXJnaW46IDAnLFxuICAgICAgJ3BhZGRpbmc6IDAnLFxuICAgIF07XG4gICAgc2VsZi5mdWxsc2NyZWVuRWxlbWVudF8uc2V0QXR0cmlidXRlKCdzdHlsZScsIGNzc1Byb3BlcnRpZXMuam9pbignOyAnKSArICc7Jyk7XG4gIH1cblxuICBhcHBseUZ1bGxzY3JlZW5FbGVtZW50U3R5bGUoKTtcblxuICByZXR1cm4gdGhpcy5mdWxsc2NyZWVuV3JhcHBlcl87XG59O1xuXG5WUkRpc3BsYXkucHJvdG90eXBlLnJlbW92ZUZ1bGxzY3JlZW5XcmFwcGVyID0gZnVuY3Rpb24oKSB7XG4gIGlmICghdGhpcy5mdWxsc2NyZWVuRWxlbWVudF8pIHtcbiAgICByZXR1cm47XG4gIH1cblxuICB2YXIgZWxlbWVudCA9IHRoaXMuZnVsbHNjcmVlbkVsZW1lbnRfO1xuICBpZiAodGhpcy5mdWxsc2NyZWVuRWxlbWVudENhY2hlZFN0eWxlXykge1xuICAgIGVsZW1lbnQuc2V0QXR0cmlidXRlKCdzdHlsZScsIHRoaXMuZnVsbHNjcmVlbkVsZW1lbnRDYWNoZWRTdHlsZV8pO1xuICB9IGVsc2Uge1xuICAgIGVsZW1lbnQucmVtb3ZlQXR0cmlidXRlKCdzdHlsZScpO1xuICB9XG4gIHRoaXMuZnVsbHNjcmVlbkVsZW1lbnRfID0gbnVsbDtcbiAgdGhpcy5mdWxsc2NyZWVuRWxlbWVudENhY2hlZFN0eWxlXyA9IG51bGw7XG5cbiAgdmFyIHBhcmVudCA9IHRoaXMuZnVsbHNjcmVlbldyYXBwZXJfLnBhcmVudEVsZW1lbnQ7XG4gIHRoaXMuZnVsbHNjcmVlbldyYXBwZXJfLnJlbW92ZUNoaWxkKGVsZW1lbnQpO1xuICBwYXJlbnQuaW5zZXJ0QmVmb3JlKGVsZW1lbnQsIHRoaXMuZnVsbHNjcmVlbldyYXBwZXJfKTtcbiAgcGFyZW50LnJlbW92ZUNoaWxkKHRoaXMuZnVsbHNjcmVlbldyYXBwZXJfKTtcblxuICByZXR1cm4gZWxlbWVudDtcbn07XG5cblZSRGlzcGxheS5wcm90b3R5cGUucmVxdWVzdFByZXNlbnQgPSBmdW5jdGlvbihsYXllcnMpIHtcbiAgdmFyIHdhc1ByZXNlbnRpbmcgPSB0aGlzLmlzUHJlc2VudGluZztcbiAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gIGlmICghKGxheWVycyBpbnN0YW5jZW9mIEFycmF5KSkge1xuICAgIGlmICghaGFzU2hvd0RlcHJlY2F0aW9uV2FybmluZykge1xuICAgICAgY29uc29sZS53YXJuKFwiVXNpbmcgYSBkZXByZWNhdGVkIGZvcm0gb2YgcmVxdWVzdFByZXNlbnQuIFNob3VsZCBwYXNzIGluIGFuIGFycmF5IG9mIFZSTGF5ZXJzLlwiKTtcbiAgICAgIGhhc1Nob3dEZXByZWNhdGlvbldhcm5pbmcgPSB0cnVlO1xuICAgIH1cbiAgICBsYXllcnMgPSBbbGF5ZXJzXTtcbiAgfVxuXG4gIHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbihyZXNvbHZlLCByZWplY3QpIHtcbiAgICBpZiAoIXNlbGYuY2FwYWJpbGl0aWVzLmNhblByZXNlbnQpIHtcbiAgICAgIHJlamVjdChuZXcgRXJyb3IoJ1ZSRGlzcGxheSBpcyBub3QgY2FwYWJsZSBvZiBwcmVzZW50aW5nLicpKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAobGF5ZXJzLmxlbmd0aCA9PSAwIHx8IGxheWVycy5sZW5ndGggPiBzZWxmLmNhcGFiaWxpdGllcy5tYXhMYXllcnMpIHtcbiAgICAgIHJlamVjdChuZXcgRXJyb3IoJ0ludmFsaWQgbnVtYmVyIG9mIGxheWVycy4nKSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIGluY29taW5nTGF5ZXIgPSBsYXllcnNbMF07XG4gICAgaWYgKCFpbmNvbWluZ0xheWVyLnNvdXJjZSkge1xuICAgICAgLypcbiAgICAgIHRvZG86IGZpZ3VyZSBvdXQgdGhlIGNvcnJlY3QgYmVoYXZpb3IgaWYgdGhlIHNvdXJjZSBpcyBub3QgcHJvdmlkZWQuXG4gICAgICBzZWUgaHR0cHM6Ly9naXRodWIuY29tL3czYy93ZWJ2ci9pc3N1ZXMvNThcbiAgICAgICovXG4gICAgICByZXNvbHZlKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIGxlZnRCb3VuZHMgPSBpbmNvbWluZ0xheWVyLmxlZnRCb3VuZHMgfHwgZGVmYXVsdExlZnRCb3VuZHM7XG4gICAgdmFyIHJpZ2h0Qm91bmRzID0gaW5jb21pbmdMYXllci5yaWdodEJvdW5kcyB8fCBkZWZhdWx0UmlnaHRCb3VuZHM7XG4gICAgaWYgKHdhc1ByZXNlbnRpbmcpIHtcbiAgICAgIC8vIEFscmVhZHkgcHJlc2VudGluZywganVzdCBjaGFuZ2luZyBjb25maWd1cmF0aW9uXG4gICAgICB2YXIgbGF5ZXIgPSBzZWxmLmxheWVyXztcbiAgICAgIGlmIChsYXllci5zb3VyY2UgIT09IGluY29taW5nTGF5ZXIuc291cmNlKSB7XG4gICAgICAgIGxheWVyLnNvdXJjZSA9IGluY29taW5nTGF5ZXIuc291cmNlO1xuICAgICAgfVxuXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IDQ7IGkrKykge1xuICAgICAgICBsYXllci5sZWZ0Qm91bmRzW2ldID0gbGVmdEJvdW5kc1tpXTtcbiAgICAgICAgbGF5ZXIucmlnaHRCb3VuZHNbaV0gPSByaWdodEJvdW5kc1tpXTtcbiAgICAgIH1cblxuICAgICAgcmVzb2x2ZSgpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIFdhcyBub3QgYWxyZWFkeSBwcmVzZW50aW5nLlxuICAgIHNlbGYubGF5ZXJfID0ge1xuICAgICAgcHJlZGlzdG9ydGVkOiBpbmNvbWluZ0xheWVyLnByZWRpc3RvcnRlZCxcbiAgICAgIHNvdXJjZTogaW5jb21pbmdMYXllci5zb3VyY2UsXG4gICAgICBsZWZ0Qm91bmRzOiBsZWZ0Qm91bmRzLnNsaWNlKDApLFxuICAgICAgcmlnaHRCb3VuZHM6IHJpZ2h0Qm91bmRzLnNsaWNlKDApXG4gICAgfTtcblxuICAgIHNlbGYud2FpdGluZ0ZvclByZXNlbnRfID0gZmFsc2U7XG4gICAgaWYgKHNlbGYubGF5ZXJfICYmIHNlbGYubGF5ZXJfLnNvdXJjZSkge1xuICAgICAgdmFyIGZ1bGxzY3JlZW5FbGVtZW50ID0gc2VsZi53cmFwRm9yRnVsbHNjcmVlbihzZWxmLmxheWVyXy5zb3VyY2UpO1xuXG4gICAgICB2YXIgb25GdWxsc2NyZWVuQ2hhbmdlID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciBhY3R1YWxGdWxsc2NyZWVuRWxlbWVudCA9IFV0aWwuZ2V0RnVsbHNjcmVlbkVsZW1lbnQoKTtcblxuICAgICAgICBzZWxmLmlzUHJlc2VudGluZyA9IChmdWxsc2NyZWVuRWxlbWVudCA9PT0gYWN0dWFsRnVsbHNjcmVlbkVsZW1lbnQpO1xuICAgICAgICBpZiAoc2VsZi5pc1ByZXNlbnRpbmcpIHtcbiAgICAgICAgICBpZiAoc2NyZWVuLm9yaWVudGF0aW9uICYmIHNjcmVlbi5vcmllbnRhdGlvbi5sb2NrKSB7XG4gICAgICAgICAgICBzY3JlZW4ub3JpZW50YXRpb24ubG9jaygnbGFuZHNjYXBlLXByaW1hcnknKS5jYXRjaChmdW5jdGlvbihlcnJvcil7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ3NjcmVlbi5vcmllbnRhdGlvbi5sb2NrKCkgZmFpbGVkIGR1ZSB0bycsIGVycm9yLm1lc3NhZ2UpXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgc2VsZi53YWl0aW5nRm9yUHJlc2VudF8gPSBmYWxzZTtcbiAgICAgICAgICBzZWxmLmJlZ2luUHJlc2VudF8oKTtcbiAgICAgICAgICByZXNvbHZlKCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaWYgKHNjcmVlbi5vcmllbnRhdGlvbiAmJiBzY3JlZW4ub3JpZW50YXRpb24udW5sb2NrKSB7XG4gICAgICAgICAgICBzY3JlZW4ub3JpZW50YXRpb24udW5sb2NrKCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHNlbGYucmVtb3ZlRnVsbHNjcmVlbldyYXBwZXIoKTtcbiAgICAgICAgICBzZWxmLndha2Vsb2NrXy5yZWxlYXNlKCk7XG4gICAgICAgICAgc2VsZi5lbmRQcmVzZW50XygpO1xuICAgICAgICAgIHNlbGYucmVtb3ZlRnVsbHNjcmVlbkxpc3RlbmVyc18oKTtcbiAgICAgICAgfVxuICAgICAgICBzZWxmLmZpcmVWUkRpc3BsYXlQcmVzZW50Q2hhbmdlXygpO1xuICAgICAgfVxuICAgICAgdmFyIG9uRnVsbHNjcmVlbkVycm9yID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIGlmICghc2VsZi53YWl0aW5nRm9yUHJlc2VudF8pIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBzZWxmLnJlbW92ZUZ1bGxzY3JlZW5XcmFwcGVyKCk7XG4gICAgICAgIHNlbGYucmVtb3ZlRnVsbHNjcmVlbkxpc3RlbmVyc18oKTtcblxuICAgICAgICBzZWxmLndha2Vsb2NrXy5yZWxlYXNlKCk7XG4gICAgICAgIHNlbGYud2FpdGluZ0ZvclByZXNlbnRfID0gZmFsc2U7XG4gICAgICAgIHNlbGYuaXNQcmVzZW50aW5nID0gZmFsc2U7XG5cbiAgICAgICAgcmVqZWN0KG5ldyBFcnJvcignVW5hYmxlIHRvIHByZXNlbnQuJykpO1xuICAgICAgfVxuXG4gICAgICBzZWxmLmFkZEZ1bGxzY3JlZW5MaXN0ZW5lcnNfKGZ1bGxzY3JlZW5FbGVtZW50LFxuICAgICAgICAgIG9uRnVsbHNjcmVlbkNoYW5nZSwgb25GdWxsc2NyZWVuRXJyb3IpO1xuXG4gICAgICBpZiAoVXRpbC5yZXF1ZXN0RnVsbHNjcmVlbihmdWxsc2NyZWVuRWxlbWVudCkpIHtcbiAgICAgICAgc2VsZi53YWtlbG9ja18ucmVxdWVzdCgpO1xuICAgICAgICBzZWxmLndhaXRpbmdGb3JQcmVzZW50XyA9IHRydWU7XG4gICAgICB9IGVsc2UgaWYgKFV0aWwuaXNJT1MoKSB8fCBVdGlsLmlzV2ViVmlld0FuZHJvaWQoKSkge1xuICAgICAgICAvLyAqc2lnaCogSnVzdCBmYWtlIGl0LlxuICAgICAgICBzZWxmLndha2Vsb2NrXy5yZXF1ZXN0KCk7XG4gICAgICAgIHNlbGYuaXNQcmVzZW50aW5nID0gdHJ1ZTtcbiAgICAgICAgc2VsZi5iZWdpblByZXNlbnRfKCk7XG4gICAgICAgIHNlbGYuZmlyZVZSRGlzcGxheVByZXNlbnRDaGFuZ2VfKCk7XG4gICAgICAgIHJlc29sdmUoKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoIXNlbGYud2FpdGluZ0ZvclByZXNlbnRfICYmICFVdGlsLmlzSU9TKCkpIHtcbiAgICAgIFV0aWwuZXhpdEZ1bGxzY3JlZW4oKTtcbiAgICAgIHJlamVjdChuZXcgRXJyb3IoJ1VuYWJsZSB0byBwcmVzZW50LicpKTtcbiAgICB9XG4gIH0pO1xufTtcblxuVlJEaXNwbGF5LnByb3RvdHlwZS5leGl0UHJlc2VudCA9IGZ1bmN0aW9uKCkge1xuICB2YXIgd2FzUHJlc2VudGluZyA9IHRoaXMuaXNQcmVzZW50aW5nO1xuICB2YXIgc2VsZiA9IHRoaXM7XG4gIHRoaXMuaXNQcmVzZW50aW5nID0gZmFsc2U7XG4gIHRoaXMubGF5ZXJfID0gbnVsbDtcbiAgdGhpcy53YWtlbG9ja18ucmVsZWFzZSgpO1xuXG4gIHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbihyZXNvbHZlLCByZWplY3QpIHtcbiAgICBpZiAod2FzUHJlc2VudGluZykge1xuICAgICAgaWYgKCFVdGlsLmV4aXRGdWxsc2NyZWVuKCkgJiYgVXRpbC5pc0lPUygpKSB7XG4gICAgICAgIHNlbGYuZW5kUHJlc2VudF8oKTtcbiAgICAgICAgc2VsZi5maXJlVlJEaXNwbGF5UHJlc2VudENoYW5nZV8oKTtcbiAgICAgIH1cblxuICAgICAgaWYgKFV0aWwuaXNXZWJWaWV3QW5kcm9pZCgpKSB7XG4gICAgICAgIHNlbGYucmVtb3ZlRnVsbHNjcmVlbldyYXBwZXIoKTtcbiAgICAgICAgc2VsZi5yZW1vdmVGdWxsc2NyZWVuTGlzdGVuZXJzXygpO1xuICAgICAgICBzZWxmLmVuZFByZXNlbnRfKCk7XG4gICAgICAgIHNlbGYuZmlyZVZSRGlzcGxheVByZXNlbnRDaGFuZ2VfKCk7XG4gICAgICB9XG5cbiAgICAgIHJlc29sdmUoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmVqZWN0KG5ldyBFcnJvcignV2FzIG5vdCBwcmVzZW50aW5nIHRvIFZSRGlzcGxheS4nKSk7XG4gICAgfVxuICB9KTtcbn07XG5cblZSRGlzcGxheS5wcm90b3R5cGUuZ2V0TGF5ZXJzID0gZnVuY3Rpb24oKSB7XG4gIGlmICh0aGlzLmxheWVyXykge1xuICAgIHJldHVybiBbdGhpcy5sYXllcl9dO1xuICB9XG4gIHJldHVybiBbXTtcbn07XG5cblZSRGlzcGxheS5wcm90b3R5cGUuZmlyZVZSRGlzcGxheVByZXNlbnRDaGFuZ2VfID0gZnVuY3Rpb24oKSB7XG4gIC8vIEltcG9ydGFudDogdW5mb3J0dW5hdGVseSB3ZSBjYW5ub3QgaGF2ZSBmdWxsIHNwZWMgY29tcGxpYW5jZSBoZXJlLlxuICAvLyBDdXN0b21FdmVudCBjdXN0b20gZmllbGRzIGFsbCBnbyB1bmRlciBlLmRldGFpbCAoc28gdGhlIFZSRGlzcGxheSBlbmRzIHVwXG4gIC8vIGJlaW5nIGUuZGV0YWlsLmRpc3BsYXksIGluc3RlYWQgb2YgZS5kaXNwbGF5IGFzIHBlciBXZWJWUiBzcGVjKS5cbiAgdmFyIGV2ZW50ID0gbmV3IEN1c3RvbUV2ZW50KCd2cmRpc3BsYXlwcmVzZW50Y2hhbmdlJywge2RldGFpbDoge2Rpc3BsYXk6IHRoaXN9fSk7XG4gIHdpbmRvdy5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbn07XG5cblZSRGlzcGxheS5wcm90b3R5cGUuZmlyZVZSRGlzcGxheUNvbm5lY3RfID0gZnVuY3Rpb24oKSB7XG4gIC8vIEltcG9ydGFudDogdW5mb3J0dW5hdGVseSB3ZSBjYW5ub3QgaGF2ZSBmdWxsIHNwZWMgY29tcGxpYW5jZSBoZXJlLlxuICAvLyBDdXN0b21FdmVudCBjdXN0b20gZmllbGRzIGFsbCBnbyB1bmRlciBlLmRldGFpbCAoc28gdGhlIFZSRGlzcGxheSBlbmRzIHVwXG4gIC8vIGJlaW5nIGUuZGV0YWlsLmRpc3BsYXksIGluc3RlYWQgb2YgZS5kaXNwbGF5IGFzIHBlciBXZWJWUiBzcGVjKS5cbiAgdmFyIGV2ZW50ID0gbmV3IEN1c3RvbUV2ZW50KCd2cmRpc3BsYXljb25uZWN0Jywge2RldGFpbDoge2Rpc3BsYXk6IHRoaXN9fSk7XG4gIHdpbmRvdy5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbn07XG5cblZSRGlzcGxheS5wcm90b3R5cGUuYWRkRnVsbHNjcmVlbkxpc3RlbmVyc18gPSBmdW5jdGlvbihlbGVtZW50LCBjaGFuZ2VIYW5kbGVyLCBlcnJvckhhbmRsZXIpIHtcbiAgdGhpcy5yZW1vdmVGdWxsc2NyZWVuTGlzdGVuZXJzXygpO1xuXG4gIHRoaXMuZnVsbHNjcmVlbkV2ZW50VGFyZ2V0XyA9IGVsZW1lbnQ7XG4gIHRoaXMuZnVsbHNjcmVlbkNoYW5nZUhhbmRsZXJfID0gY2hhbmdlSGFuZGxlcjtcbiAgdGhpcy5mdWxsc2NyZWVuRXJyb3JIYW5kbGVyXyA9IGVycm9ySGFuZGxlcjtcblxuICBpZiAoY2hhbmdlSGFuZGxlcikge1xuICAgIGlmIChkb2N1bWVudC5mdWxsc2NyZWVuRW5hYmxlZCkge1xuICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCdmdWxsc2NyZWVuY2hhbmdlJywgY2hhbmdlSGFuZGxlciwgZmFsc2UpO1xuICAgIH0gZWxzZSBpZiAoZG9jdW1lbnQud2Via2l0RnVsbHNjcmVlbkVuYWJsZWQpIHtcbiAgICAgIGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignd2Via2l0ZnVsbHNjcmVlbmNoYW5nZScsIGNoYW5nZUhhbmRsZXIsIGZhbHNlKTtcbiAgICB9IGVsc2UgaWYgKGRvY3VtZW50Lm1vekZ1bGxTY3JlZW5FbmFibGVkKSB7XG4gICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdtb3pmdWxsc2NyZWVuY2hhbmdlJywgY2hhbmdlSGFuZGxlciwgZmFsc2UpO1xuICAgIH0gZWxzZSBpZiAoZG9jdW1lbnQubXNGdWxsc2NyZWVuRW5hYmxlZCkge1xuICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCdtc2Z1bGxzY3JlZW5jaGFuZ2UnLCBjaGFuZ2VIYW5kbGVyLCBmYWxzZSk7XG4gICAgfVxuICB9XG5cbiAgaWYgKGVycm9ySGFuZGxlcikge1xuICAgIGlmIChkb2N1bWVudC5mdWxsc2NyZWVuRW5hYmxlZCkge1xuICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCdmdWxsc2NyZWVuZXJyb3InLCBlcnJvckhhbmRsZXIsIGZhbHNlKTtcbiAgICB9IGVsc2UgaWYgKGRvY3VtZW50LndlYmtpdEZ1bGxzY3JlZW5FbmFibGVkKSB7XG4gICAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3dlYmtpdGZ1bGxzY3JlZW5lcnJvcicsIGVycm9ySGFuZGxlciwgZmFsc2UpO1xuICAgIH0gZWxzZSBpZiAoZG9jdW1lbnQubW96RnVsbFNjcmVlbkVuYWJsZWQpIHtcbiAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ21vemZ1bGxzY3JlZW5lcnJvcicsIGVycm9ySGFuZGxlciwgZmFsc2UpO1xuICAgIH0gZWxzZSBpZiAoZG9jdW1lbnQubXNGdWxsc2NyZWVuRW5hYmxlZCkge1xuICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCdtc2Z1bGxzY3JlZW5lcnJvcicsIGVycm9ySGFuZGxlciwgZmFsc2UpO1xuICAgIH1cbiAgfVxufTtcblxuVlJEaXNwbGF5LnByb3RvdHlwZS5yZW1vdmVGdWxsc2NyZWVuTGlzdGVuZXJzXyA9IGZ1bmN0aW9uKCkge1xuICBpZiAoIXRoaXMuZnVsbHNjcmVlbkV2ZW50VGFyZ2V0XylcbiAgICByZXR1cm47XG5cbiAgdmFyIGVsZW1lbnQgPSB0aGlzLmZ1bGxzY3JlZW5FdmVudFRhcmdldF87XG5cbiAgaWYgKHRoaXMuZnVsbHNjcmVlbkNoYW5nZUhhbmRsZXJfKSB7XG4gICAgdmFyIGNoYW5nZUhhbmRsZXIgPSB0aGlzLmZ1bGxzY3JlZW5DaGFuZ2VIYW5kbGVyXztcbiAgICBlbGVtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2Z1bGxzY3JlZW5jaGFuZ2UnLCBjaGFuZ2VIYW5kbGVyLCBmYWxzZSk7XG4gICAgZWxlbWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCd3ZWJraXRmdWxsc2NyZWVuY2hhbmdlJywgY2hhbmdlSGFuZGxlciwgZmFsc2UpO1xuICAgIGRvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ21vemZ1bGxzY3JlZW5jaGFuZ2UnLCBjaGFuZ2VIYW5kbGVyLCBmYWxzZSk7XG4gICAgZWxlbWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCdtc2Z1bGxzY3JlZW5jaGFuZ2UnLCBjaGFuZ2VIYW5kbGVyLCBmYWxzZSk7XG4gIH1cblxuICBpZiAodGhpcy5mdWxsc2NyZWVuRXJyb3JIYW5kbGVyXykge1xuICAgIHZhciBlcnJvckhhbmRsZXIgPSB0aGlzLmZ1bGxzY3JlZW5FcnJvckhhbmRsZXJfO1xuICAgIGVsZW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcignZnVsbHNjcmVlbmVycm9yJywgZXJyb3JIYW5kbGVyLCBmYWxzZSk7XG4gICAgZWxlbWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCd3ZWJraXRmdWxsc2NyZWVuZXJyb3InLCBlcnJvckhhbmRsZXIsIGZhbHNlKTtcbiAgICBkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCdtb3pmdWxsc2NyZWVuZXJyb3InLCBlcnJvckhhbmRsZXIsIGZhbHNlKTtcbiAgICBlbGVtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ21zZnVsbHNjcmVlbmVycm9yJywgZXJyb3JIYW5kbGVyLCBmYWxzZSk7XG4gIH1cblxuICB0aGlzLmZ1bGxzY3JlZW5FdmVudFRhcmdldF8gPSBudWxsO1xuICB0aGlzLmZ1bGxzY3JlZW5DaGFuZ2VIYW5kbGVyXyA9IG51bGw7XG4gIHRoaXMuZnVsbHNjcmVlbkVycm9ySGFuZGxlcl8gPSBudWxsO1xufTtcblxuVlJEaXNwbGF5LnByb3RvdHlwZS5iZWdpblByZXNlbnRfID0gZnVuY3Rpb24oKSB7XG4gIC8vIE92ZXJyaWRlIHRvIGFkZCBjdXN0b20gYmVoYXZpb3Igd2hlbiBwcmVzZW50YXRpb24gYmVnaW5zLlxufTtcblxuVlJEaXNwbGF5LnByb3RvdHlwZS5lbmRQcmVzZW50XyA9IGZ1bmN0aW9uKCkge1xuICAvLyBPdmVycmlkZSB0byBhZGQgY3VzdG9tIGJlaGF2aW9yIHdoZW4gcHJlc2VudGF0aW9uIGVuZHMuXG59O1xuXG5WUkRpc3BsYXkucHJvdG90eXBlLnN1Ym1pdEZyYW1lID0gZnVuY3Rpb24ocG9zZSkge1xuICAvLyBPdmVycmlkZSB0byBhZGQgY3VzdG9tIGJlaGF2aW9yIGZvciBmcmFtZSBzdWJtaXNzaW9uLlxufTtcblxuVlJEaXNwbGF5LnByb3RvdHlwZS5nZXRFeWVQYXJhbWV0ZXJzID0gZnVuY3Rpb24od2hpY2hFeWUpIHtcbiAgLy8gT3ZlcnJpZGUgdG8gcmV0dXJuIGFjY3VyYXRlIGV5ZSBwYXJhbWV0ZXJzIGlmIGNhblByZXNlbnQgaXMgdHJ1ZS5cbiAgcmV0dXJuIG51bGw7XG59O1xuXG4vKlxuICogRGVwcmVjYXRlZCBjbGFzc2VzXG4gKi9cblxuLyoqXG4gKiBUaGUgYmFzZSBjbGFzcyBmb3IgYWxsIFZSIGRldmljZXMuIChEZXByZWNhdGVkKVxuICovXG5mdW5jdGlvbiBWUkRldmljZSgpIHtcbiAgdGhpcy5pc1BvbHlmaWxsZWQgPSB0cnVlO1xuICB0aGlzLmhhcmR3YXJlVW5pdElkID0gJ3dlYnZyLXBvbHlmaWxsIGhhcmR3YXJlVW5pdElkJztcbiAgdGhpcy5kZXZpY2VJZCA9ICd3ZWJ2ci1wb2x5ZmlsbCBkZXZpY2VJZCc7XG4gIHRoaXMuZGV2aWNlTmFtZSA9ICd3ZWJ2ci1wb2x5ZmlsbCBkZXZpY2VOYW1lJztcbn1cblxuLyoqXG4gKiBUaGUgYmFzZSBjbGFzcyBmb3IgYWxsIFZSIEhNRCBkZXZpY2VzLiAoRGVwcmVjYXRlZClcbiAqL1xuZnVuY3Rpb24gSE1EVlJEZXZpY2UoKSB7XG59XG5ITURWUkRldmljZS5wcm90b3R5cGUgPSBuZXcgVlJEZXZpY2UoKTtcblxuLyoqXG4gKiBUaGUgYmFzZSBjbGFzcyBmb3IgYWxsIFZSIHBvc2l0aW9uIHNlbnNvciBkZXZpY2VzLiAoRGVwcmVjYXRlZClcbiAqL1xuZnVuY3Rpb24gUG9zaXRpb25TZW5zb3JWUkRldmljZSgpIHtcbn1cblBvc2l0aW9uU2Vuc29yVlJEZXZpY2UucHJvdG90eXBlID0gbmV3IFZSRGV2aWNlKCk7XG5cbm1vZHVsZS5leHBvcnRzLlZSRnJhbWVEYXRhID0gVlJGcmFtZURhdGE7XG5tb2R1bGUuZXhwb3J0cy5WUkRpc3BsYXkgPSBWUkRpc3BsYXk7XG5tb2R1bGUuZXhwb3J0cy5WUkRldmljZSA9IFZSRGV2aWNlO1xubW9kdWxlLmV4cG9ydHMuSE1EVlJEZXZpY2UgPSBITURWUkRldmljZTtcbm1vZHVsZS5leHBvcnRzLlBvc2l0aW9uU2Vuc29yVlJEZXZpY2UgPSBQb3NpdGlvblNlbnNvclZSRGV2aWNlO1xuIiwiLypcbiAqIENvcHlyaWdodCAyMDE2IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKi9cblxudmFyIENhcmRib2FyZFVJID0gcmVxdWlyZSgnLi9jYXJkYm9hcmQtdWkuanMnKTtcbnZhciBVdGlsID0gcmVxdWlyZSgnLi91dGlsLmpzJyk7XG52YXIgV0dMVVByZXNlcnZlR0xTdGF0ZSA9IHJlcXVpcmUoJy4vZGVwcy93Z2x1LXByZXNlcnZlLXN0YXRlLmpzJyk7XG5cbnZhciBkaXN0b3J0aW9uVlMgPSBbXG4gICdhdHRyaWJ1dGUgdmVjMiBwb3NpdGlvbjsnLFxuICAnYXR0cmlidXRlIHZlYzMgdGV4Q29vcmQ7JyxcblxuICAndmFyeWluZyB2ZWMyIHZUZXhDb29yZDsnLFxuXG4gICd1bmlmb3JtIHZlYzQgdmlld3BvcnRPZmZzZXRTY2FsZVsyXTsnLFxuXG4gICd2b2lkIG1haW4oKSB7JyxcbiAgJyAgdmVjNCB2aWV3cG9ydCA9IHZpZXdwb3J0T2Zmc2V0U2NhbGVbaW50KHRleENvb3JkLnopXTsnLFxuICAnICB2VGV4Q29vcmQgPSAodGV4Q29vcmQueHkgKiB2aWV3cG9ydC56dykgKyB2aWV3cG9ydC54eTsnLFxuICAnICBnbF9Qb3NpdGlvbiA9IHZlYzQoIHBvc2l0aW9uLCAxLjAsIDEuMCApOycsXG4gICd9Jyxcbl0uam9pbignXFxuJyk7XG5cbnZhciBkaXN0b3J0aW9uRlMgPSBbXG4gICdwcmVjaXNpb24gbWVkaXVtcCBmbG9hdDsnLFxuICAndW5pZm9ybSBzYW1wbGVyMkQgZGlmZnVzZTsnLFxuXG4gICd2YXJ5aW5nIHZlYzIgdlRleENvb3JkOycsXG5cbiAgJ3ZvaWQgbWFpbigpIHsnLFxuICAnICBnbF9GcmFnQ29sb3IgPSB0ZXh0dXJlMkQoZGlmZnVzZSwgdlRleENvb3JkKTsnLFxuICAnfScsXG5dLmpvaW4oJ1xcbicpO1xuXG4vKipcbiAqIEEgbWVzaC1iYXNlZCBkaXN0b3J0ZXIuXG4gKi9cbmZ1bmN0aW9uIENhcmRib2FyZERpc3RvcnRlcihnbCkge1xuICB0aGlzLmdsID0gZ2w7XG4gIHRoaXMuY3R4QXR0cmlicyA9IGdsLmdldENvbnRleHRBdHRyaWJ1dGVzKCk7XG5cbiAgdGhpcy5tZXNoV2lkdGggPSAyMDtcbiAgdGhpcy5tZXNoSGVpZ2h0ID0gMjA7XG5cbiAgdGhpcy5idWZmZXJTY2FsZSA9IHdpbmRvdy5XZWJWUkNvbmZpZy5CVUZGRVJfU0NBTEU7XG5cbiAgdGhpcy5idWZmZXJXaWR0aCA9IGdsLmRyYXdpbmdCdWZmZXJXaWR0aDtcbiAgdGhpcy5idWZmZXJIZWlnaHQgPSBnbC5kcmF3aW5nQnVmZmVySGVpZ2h0O1xuXG4gIC8vIFBhdGNoaW5nIHN1cHBvcnRcbiAgdGhpcy5yZWFsQmluZEZyYW1lYnVmZmVyID0gZ2wuYmluZEZyYW1lYnVmZmVyO1xuICB0aGlzLnJlYWxFbmFibGUgPSBnbC5lbmFibGU7XG4gIHRoaXMucmVhbERpc2FibGUgPSBnbC5kaXNhYmxlO1xuICB0aGlzLnJlYWxDb2xvck1hc2sgPSBnbC5jb2xvck1hc2s7XG4gIHRoaXMucmVhbENsZWFyQ29sb3IgPSBnbC5jbGVhckNvbG9yO1xuICB0aGlzLnJlYWxWaWV3cG9ydCA9IGdsLnZpZXdwb3J0O1xuXG4gIGlmICghVXRpbC5pc0lPUygpKSB7XG4gICAgdGhpcy5yZWFsQ2FudmFzV2lkdGggPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKGdsLmNhbnZhcy5fX3Byb3RvX18sICd3aWR0aCcpO1xuICAgIHRoaXMucmVhbENhbnZhc0hlaWdodCA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IoZ2wuY2FudmFzLl9fcHJvdG9fXywgJ2hlaWdodCcpO1xuICB9XG5cbiAgdGhpcy5pc1BhdGNoZWQgPSBmYWxzZTtcblxuICAvLyBTdGF0ZSB0cmFja2luZ1xuICB0aGlzLmxhc3RCb3VuZEZyYW1lYnVmZmVyID0gbnVsbDtcbiAgdGhpcy5jdWxsRmFjZSA9IGZhbHNlO1xuICB0aGlzLmRlcHRoVGVzdCA9IGZhbHNlO1xuICB0aGlzLmJsZW5kID0gZmFsc2U7XG4gIHRoaXMuc2Npc3NvclRlc3QgPSBmYWxzZTtcbiAgdGhpcy5zdGVuY2lsVGVzdCA9IGZhbHNlO1xuICB0aGlzLnZpZXdwb3J0ID0gWzAsIDAsIDAsIDBdO1xuICB0aGlzLmNvbG9yTWFzayA9IFt0cnVlLCB0cnVlLCB0cnVlLCB0cnVlXTtcbiAgdGhpcy5jbGVhckNvbG9yID0gWzAsIDAsIDAsIDBdO1xuXG4gIHRoaXMuYXR0cmlicyA9IHtcbiAgICBwb3NpdGlvbjogMCxcbiAgICB0ZXhDb29yZDogMVxuICB9O1xuICB0aGlzLnByb2dyYW0gPSBVdGlsLmxpbmtQcm9ncmFtKGdsLCBkaXN0b3J0aW9uVlMsIGRpc3RvcnRpb25GUywgdGhpcy5hdHRyaWJzKTtcbiAgdGhpcy51bmlmb3JtcyA9IFV0aWwuZ2V0UHJvZ3JhbVVuaWZvcm1zKGdsLCB0aGlzLnByb2dyYW0pO1xuXG4gIHRoaXMudmlld3BvcnRPZmZzZXRTY2FsZSA9IG5ldyBGbG9hdDMyQXJyYXkoOCk7XG4gIHRoaXMuc2V0VGV4dHVyZUJvdW5kcygpO1xuXG4gIHRoaXMudmVydGV4QnVmZmVyID0gZ2wuY3JlYXRlQnVmZmVyKCk7XG4gIHRoaXMuaW5kZXhCdWZmZXIgPSBnbC5jcmVhdGVCdWZmZXIoKTtcbiAgdGhpcy5pbmRleENvdW50ID0gMDtcblxuICB0aGlzLnJlbmRlclRhcmdldCA9IGdsLmNyZWF0ZVRleHR1cmUoKTtcbiAgdGhpcy5mcmFtZWJ1ZmZlciA9IGdsLmNyZWF0ZUZyYW1lYnVmZmVyKCk7XG5cbiAgdGhpcy5kZXB0aFN0ZW5jaWxCdWZmZXIgPSBudWxsO1xuICB0aGlzLmRlcHRoQnVmZmVyID0gbnVsbDtcbiAgdGhpcy5zdGVuY2lsQnVmZmVyID0gbnVsbDtcblxuICBpZiAodGhpcy5jdHhBdHRyaWJzLmRlcHRoICYmIHRoaXMuY3R4QXR0cmlicy5zdGVuY2lsKSB7XG4gICAgdGhpcy5kZXB0aFN0ZW5jaWxCdWZmZXIgPSBnbC5jcmVhdGVSZW5kZXJidWZmZXIoKTtcbiAgfSBlbHNlIGlmICh0aGlzLmN0eEF0dHJpYnMuZGVwdGgpIHtcbiAgICB0aGlzLmRlcHRoQnVmZmVyID0gZ2wuY3JlYXRlUmVuZGVyYnVmZmVyKCk7XG4gIH0gZWxzZSBpZiAodGhpcy5jdHhBdHRyaWJzLnN0ZW5jaWwpIHtcbiAgICB0aGlzLnN0ZW5jaWxCdWZmZXIgPSBnbC5jcmVhdGVSZW5kZXJidWZmZXIoKTtcbiAgfVxuXG4gIHRoaXMucGF0Y2goKTtcblxuICB0aGlzLm9uUmVzaXplKCk7XG5cbiAgaWYgKCF3aW5kb3cuV2ViVlJDb25maWcuQ0FSREJPQVJEX1VJX0RJU0FCTEVEKSB7XG4gICAgdGhpcy5jYXJkYm9hcmRVSSA9IG5ldyBDYXJkYm9hcmRVSShnbCk7XG4gIH1cbn07XG5cbi8qKlxuICogVGVhcnMgZG93biBhbGwgdGhlIHJlc291cmNlcyBjcmVhdGVkIGJ5IHRoZSBkaXN0b3J0ZXIgYW5kIHJlbW92ZXMgYW55XG4gKiBwYXRjaGVzLlxuICovXG5DYXJkYm9hcmREaXN0b3J0ZXIucHJvdG90eXBlLmRlc3Ryb3kgPSBmdW5jdGlvbigpIHtcbiAgdmFyIGdsID0gdGhpcy5nbDtcblxuICB0aGlzLnVucGF0Y2goKTtcblxuICBnbC5kZWxldGVQcm9ncmFtKHRoaXMucHJvZ3JhbSk7XG4gIGdsLmRlbGV0ZUJ1ZmZlcih0aGlzLnZlcnRleEJ1ZmZlcik7XG4gIGdsLmRlbGV0ZUJ1ZmZlcih0aGlzLmluZGV4QnVmZmVyKTtcbiAgZ2wuZGVsZXRlVGV4dHVyZSh0aGlzLnJlbmRlclRhcmdldCk7XG4gIGdsLmRlbGV0ZUZyYW1lYnVmZmVyKHRoaXMuZnJhbWVidWZmZXIpO1xuICBpZiAodGhpcy5kZXB0aFN0ZW5jaWxCdWZmZXIpIHtcbiAgICBnbC5kZWxldGVSZW5kZXJidWZmZXIodGhpcy5kZXB0aFN0ZW5jaWxCdWZmZXIpO1xuICB9XG4gIGlmICh0aGlzLmRlcHRoQnVmZmVyKSB7XG4gICAgZ2wuZGVsZXRlUmVuZGVyYnVmZmVyKHRoaXMuZGVwdGhCdWZmZXIpO1xuICB9XG4gIGlmICh0aGlzLnN0ZW5jaWxCdWZmZXIpIHtcbiAgICBnbC5kZWxldGVSZW5kZXJidWZmZXIodGhpcy5zdGVuY2lsQnVmZmVyKTtcbiAgfVxuXG4gIGlmICh0aGlzLmNhcmRib2FyZFVJKSB7XG4gICAgdGhpcy5jYXJkYm9hcmRVSS5kZXN0cm95KCk7XG4gIH1cbn07XG5cblxuLyoqXG4gKiBSZXNpemVzIHRoZSBiYWNrYnVmZmVyIHRvIG1hdGNoIHRoZSBjYW52YXMgd2lkdGggYW5kIGhlaWdodC5cbiAqL1xuQ2FyZGJvYXJkRGlzdG9ydGVyLnByb3RvdHlwZS5vblJlc2l6ZSA9IGZ1bmN0aW9uKCkge1xuICB2YXIgZ2wgPSB0aGlzLmdsO1xuICB2YXIgc2VsZiA9IHRoaXM7XG5cbiAgdmFyIGdsU3RhdGUgPSBbXG4gICAgZ2wuUkVOREVSQlVGRkVSX0JJTkRJTkcsXG4gICAgZ2wuVEVYVFVSRV9CSU5ESU5HXzJELCBnbC5URVhUVVJFMFxuICBdO1xuXG4gIFdHTFVQcmVzZXJ2ZUdMU3RhdGUoZ2wsIGdsU3RhdGUsIGZ1bmN0aW9uKGdsKSB7XG4gICAgLy8gQmluZCByZWFsIGJhY2tidWZmZXIgYW5kIGNsZWFyIGl0IG9uY2UuIFdlIGRvbid0IG5lZWQgdG8gY2xlYXIgaXQgYWdhaW5cbiAgICAvLyBhZnRlciB0aGF0IGJlY2F1c2Ugd2UncmUgb3ZlcndyaXRpbmcgdGhlIHNhbWUgYXJlYSBldmVyeSBmcmFtZS5cbiAgICBzZWxmLnJlYWxCaW5kRnJhbWVidWZmZXIuY2FsbChnbCwgZ2wuRlJBTUVCVUZGRVIsIG51bGwpO1xuXG4gICAgLy8gUHV0IHRoaW5ncyBpbiBhIGdvb2Qgc3RhdGVcbiAgICBpZiAoc2VsZi5zY2lzc29yVGVzdCkgeyBzZWxmLnJlYWxEaXNhYmxlLmNhbGwoZ2wsIGdsLlNDSVNTT1JfVEVTVCk7IH1cbiAgICBzZWxmLnJlYWxDb2xvck1hc2suY2FsbChnbCwgdHJ1ZSwgdHJ1ZSwgdHJ1ZSwgdHJ1ZSk7XG4gICAgc2VsZi5yZWFsVmlld3BvcnQuY2FsbChnbCwgMCwgMCwgZ2wuZHJhd2luZ0J1ZmZlcldpZHRoLCBnbC5kcmF3aW5nQnVmZmVySGVpZ2h0KTtcbiAgICBzZWxmLnJlYWxDbGVhckNvbG9yLmNhbGwoZ2wsIDAsIDAsIDAsIDEpO1xuXG4gICAgZ2wuY2xlYXIoZ2wuQ09MT1JfQlVGRkVSX0JJVCk7XG5cbiAgICAvLyBOb3cgYmluZCBhbmQgcmVzaXplIHRoZSBmYWtlIGJhY2tidWZmZXJcbiAgICBzZWxmLnJlYWxCaW5kRnJhbWVidWZmZXIuY2FsbChnbCwgZ2wuRlJBTUVCVUZGRVIsIHNlbGYuZnJhbWVidWZmZXIpO1xuXG4gICAgZ2wuYmluZFRleHR1cmUoZ2wuVEVYVFVSRV8yRCwgc2VsZi5yZW5kZXJUYXJnZXQpO1xuICAgIGdsLnRleEltYWdlMkQoZ2wuVEVYVFVSRV8yRCwgMCwgc2VsZi5jdHhBdHRyaWJzLmFscGhhID8gZ2wuUkdCQSA6IGdsLlJHQixcbiAgICAgICAgc2VsZi5idWZmZXJXaWR0aCwgc2VsZi5idWZmZXJIZWlnaHQsIDAsXG4gICAgICAgIHNlbGYuY3R4QXR0cmlicy5hbHBoYSA/IGdsLlJHQkEgOiBnbC5SR0IsIGdsLlVOU0lHTkVEX0JZVEUsIG51bGwpO1xuICAgIGdsLnRleFBhcmFtZXRlcmkoZ2wuVEVYVFVSRV8yRCwgZ2wuVEVYVFVSRV9NQUdfRklMVEVSLCBnbC5MSU5FQVIpO1xuICAgIGdsLnRleFBhcmFtZXRlcmkoZ2wuVEVYVFVSRV8yRCwgZ2wuVEVYVFVSRV9NSU5fRklMVEVSLCBnbC5MSU5FQVIpO1xuICAgIGdsLnRleFBhcmFtZXRlcmkoZ2wuVEVYVFVSRV8yRCwgZ2wuVEVYVFVSRV9XUkFQX1MsIGdsLkNMQU1QX1RPX0VER0UpO1xuICAgIGdsLnRleFBhcmFtZXRlcmkoZ2wuVEVYVFVSRV8yRCwgZ2wuVEVYVFVSRV9XUkFQX1QsIGdsLkNMQU1QX1RPX0VER0UpO1xuICAgIGdsLmZyYW1lYnVmZmVyVGV4dHVyZTJEKGdsLkZSQU1FQlVGRkVSLCBnbC5DT0xPUl9BVFRBQ0hNRU5UMCwgZ2wuVEVYVFVSRV8yRCwgc2VsZi5yZW5kZXJUYXJnZXQsIDApO1xuXG4gICAgaWYgKHNlbGYuY3R4QXR0cmlicy5kZXB0aCAmJiBzZWxmLmN0eEF0dHJpYnMuc3RlbmNpbCkge1xuICAgICAgZ2wuYmluZFJlbmRlcmJ1ZmZlcihnbC5SRU5ERVJCVUZGRVIsIHNlbGYuZGVwdGhTdGVuY2lsQnVmZmVyKTtcbiAgICAgIGdsLnJlbmRlcmJ1ZmZlclN0b3JhZ2UoZ2wuUkVOREVSQlVGRkVSLCBnbC5ERVBUSF9TVEVOQ0lMLFxuICAgICAgICAgIHNlbGYuYnVmZmVyV2lkdGgsIHNlbGYuYnVmZmVySGVpZ2h0KTtcbiAgICAgIGdsLmZyYW1lYnVmZmVyUmVuZGVyYnVmZmVyKGdsLkZSQU1FQlVGRkVSLCBnbC5ERVBUSF9TVEVOQ0lMX0FUVEFDSE1FTlQsXG4gICAgICAgICAgZ2wuUkVOREVSQlVGRkVSLCBzZWxmLmRlcHRoU3RlbmNpbEJ1ZmZlcik7XG4gICAgfSBlbHNlIGlmIChzZWxmLmN0eEF0dHJpYnMuZGVwdGgpIHtcbiAgICAgIGdsLmJpbmRSZW5kZXJidWZmZXIoZ2wuUkVOREVSQlVGRkVSLCBzZWxmLmRlcHRoQnVmZmVyKTtcbiAgICAgIGdsLnJlbmRlcmJ1ZmZlclN0b3JhZ2UoZ2wuUkVOREVSQlVGRkVSLCBnbC5ERVBUSF9DT01QT05FTlQxNixcbiAgICAgICAgICBzZWxmLmJ1ZmZlcldpZHRoLCBzZWxmLmJ1ZmZlckhlaWdodCk7XG4gICAgICBnbC5mcmFtZWJ1ZmZlclJlbmRlcmJ1ZmZlcihnbC5GUkFNRUJVRkZFUiwgZ2wuREVQVEhfQVRUQUNITUVOVCxcbiAgICAgICAgICBnbC5SRU5ERVJCVUZGRVIsIHNlbGYuZGVwdGhCdWZmZXIpO1xuICAgIH0gZWxzZSBpZiAoc2VsZi5jdHhBdHRyaWJzLnN0ZW5jaWwpIHtcbiAgICAgIGdsLmJpbmRSZW5kZXJidWZmZXIoZ2wuUkVOREVSQlVGRkVSLCBzZWxmLnN0ZW5jaWxCdWZmZXIpO1xuICAgICAgZ2wucmVuZGVyYnVmZmVyU3RvcmFnZShnbC5SRU5ERVJCVUZGRVIsIGdsLlNURU5DSUxfSU5ERVg4LFxuICAgICAgICAgIHNlbGYuYnVmZmVyV2lkdGgsIHNlbGYuYnVmZmVySGVpZ2h0KTtcbiAgICAgIGdsLmZyYW1lYnVmZmVyUmVuZGVyYnVmZmVyKGdsLkZSQU1FQlVGRkVSLCBnbC5TVEVOQ0lMX0FUVEFDSE1FTlQsXG4gICAgICAgICAgZ2wuUkVOREVSQlVGRkVSLCBzZWxmLnN0ZW5jaWxCdWZmZXIpO1xuICAgIH1cblxuICAgIGlmICghZ2wuY2hlY2tGcmFtZWJ1ZmZlclN0YXR1cyhnbC5GUkFNRUJVRkZFUikgPT09IGdsLkZSQU1FQlVGRkVSX0NPTVBMRVRFKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdGcmFtZWJ1ZmZlciBpbmNvbXBsZXRlIScpO1xuICAgIH1cblxuICAgIHNlbGYucmVhbEJpbmRGcmFtZWJ1ZmZlci5jYWxsKGdsLCBnbC5GUkFNRUJVRkZFUiwgc2VsZi5sYXN0Qm91bmRGcmFtZWJ1ZmZlcik7XG5cbiAgICBpZiAoc2VsZi5zY2lzc29yVGVzdCkgeyBzZWxmLnJlYWxFbmFibGUuY2FsbChnbCwgZ2wuU0NJU1NPUl9URVNUKTsgfVxuXG4gICAgc2VsZi5yZWFsQ29sb3JNYXNrLmFwcGx5KGdsLCBzZWxmLmNvbG9yTWFzayk7XG4gICAgc2VsZi5yZWFsVmlld3BvcnQuYXBwbHkoZ2wsIHNlbGYudmlld3BvcnQpO1xuICAgIHNlbGYucmVhbENsZWFyQ29sb3IuYXBwbHkoZ2wsIHNlbGYuY2xlYXJDb2xvcik7XG4gIH0pO1xuXG4gIGlmICh0aGlzLmNhcmRib2FyZFVJKSB7XG4gICAgdGhpcy5jYXJkYm9hcmRVSS5vblJlc2l6ZSgpO1xuICB9XG59O1xuXG5DYXJkYm9hcmREaXN0b3J0ZXIucHJvdG90eXBlLnBhdGNoID0gZnVuY3Rpb24oKSB7XG4gIGlmICh0aGlzLmlzUGF0Y2hlZCkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHZhciBzZWxmID0gdGhpcztcbiAgdmFyIGNhbnZhcyA9IHRoaXMuZ2wuY2FudmFzO1xuICB2YXIgZ2wgPSB0aGlzLmdsO1xuXG4gIGlmICghVXRpbC5pc0lPUygpKSB7XG4gICAgY2FudmFzLndpZHRoID0gVXRpbC5nZXRTY3JlZW5XaWR0aCgpICogdGhpcy5idWZmZXJTY2FsZTtcbiAgICBjYW52YXMuaGVpZ2h0ID0gVXRpbC5nZXRTY3JlZW5IZWlnaHQoKSAqIHRoaXMuYnVmZmVyU2NhbGU7XG5cbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkoY2FudmFzLCAnd2lkdGgnLCB7XG4gICAgICBjb25maWd1cmFibGU6IHRydWUsXG4gICAgICBlbnVtZXJhYmxlOiB0cnVlLFxuICAgICAgZ2V0OiBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHNlbGYuYnVmZmVyV2lkdGg7XG4gICAgICB9LFxuICAgICAgc2V0OiBmdW5jdGlvbih2YWx1ZSkge1xuICAgICAgICBzZWxmLmJ1ZmZlcldpZHRoID0gdmFsdWU7XG4gICAgICAgIHNlbGYucmVhbENhbnZhc1dpZHRoLnNldC5jYWxsKGNhbnZhcywgdmFsdWUpO1xuICAgICAgICBzZWxmLm9uUmVzaXplKCk7XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkoY2FudmFzLCAnaGVpZ2h0Jywge1xuICAgICAgY29uZmlndXJhYmxlOiB0cnVlLFxuICAgICAgZW51bWVyYWJsZTogdHJ1ZSxcbiAgICAgIGdldDogZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiBzZWxmLmJ1ZmZlckhlaWdodDtcbiAgICAgIH0sXG4gICAgICBzZXQ6IGZ1bmN0aW9uKHZhbHVlKSB7XG4gICAgICAgIHNlbGYuYnVmZmVySGVpZ2h0ID0gdmFsdWU7XG4gICAgICAgIHNlbGYucmVhbENhbnZhc0hlaWdodC5zZXQuY2FsbChjYW52YXMsIHZhbHVlKTtcbiAgICAgICAgc2VsZi5vblJlc2l6ZSgpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgdGhpcy5sYXN0Qm91bmRGcmFtZWJ1ZmZlciA9IGdsLmdldFBhcmFtZXRlcihnbC5GUkFNRUJVRkZFUl9CSU5ESU5HKTtcblxuICBpZiAodGhpcy5sYXN0Qm91bmRGcmFtZWJ1ZmZlciA9PSBudWxsKSB7XG4gICAgdGhpcy5sYXN0Qm91bmRGcmFtZWJ1ZmZlciA9IHRoaXMuZnJhbWVidWZmZXI7XG4gICAgdGhpcy5nbC5iaW5kRnJhbWVidWZmZXIoZ2wuRlJBTUVCVUZGRVIsIHRoaXMuZnJhbWVidWZmZXIpO1xuICB9XG5cbiAgdGhpcy5nbC5iaW5kRnJhbWVidWZmZXIgPSBmdW5jdGlvbih0YXJnZXQsIGZyYW1lYnVmZmVyKSB7XG4gICAgc2VsZi5sYXN0Qm91bmRGcmFtZWJ1ZmZlciA9IGZyYW1lYnVmZmVyID8gZnJhbWVidWZmZXIgOiBzZWxmLmZyYW1lYnVmZmVyO1xuICAgIC8vIFNpbGVudGx5IG1ha2UgY2FsbHMgdG8gYmluZCB0aGUgZGVmYXVsdCBmcmFtZWJ1ZmZlciBiaW5kIG91cnMgaW5zdGVhZC5cbiAgICBzZWxmLnJlYWxCaW5kRnJhbWVidWZmZXIuY2FsbChnbCwgdGFyZ2V0LCBzZWxmLmxhc3RCb3VuZEZyYW1lYnVmZmVyKTtcbiAgfTtcblxuICB0aGlzLmN1bGxGYWNlID0gZ2wuZ2V0UGFyYW1ldGVyKGdsLkNVTExfRkFDRSk7XG4gIHRoaXMuZGVwdGhUZXN0ID0gZ2wuZ2V0UGFyYW1ldGVyKGdsLkRFUFRIX1RFU1QpO1xuICB0aGlzLmJsZW5kID0gZ2wuZ2V0UGFyYW1ldGVyKGdsLkJMRU5EKTtcbiAgdGhpcy5zY2lzc29yVGVzdCA9IGdsLmdldFBhcmFtZXRlcihnbC5TQ0lTU09SX1RFU1QpO1xuICB0aGlzLnN0ZW5jaWxUZXN0ID0gZ2wuZ2V0UGFyYW1ldGVyKGdsLlNURU5DSUxfVEVTVCk7XG5cbiAgZ2wuZW5hYmxlID0gZnVuY3Rpb24ocG5hbWUpIHtcbiAgICBzd2l0Y2ggKHBuYW1lKSB7XG4gICAgICBjYXNlIGdsLkNVTExfRkFDRTogc2VsZi5jdWxsRmFjZSA9IHRydWU7IGJyZWFrO1xuICAgICAgY2FzZSBnbC5ERVBUSF9URVNUOiBzZWxmLmRlcHRoVGVzdCA9IHRydWU7IGJyZWFrO1xuICAgICAgY2FzZSBnbC5CTEVORDogc2VsZi5ibGVuZCA9IHRydWU7IGJyZWFrO1xuICAgICAgY2FzZSBnbC5TQ0lTU09SX1RFU1Q6IHNlbGYuc2Npc3NvclRlc3QgPSB0cnVlOyBicmVhaztcbiAgICAgIGNhc2UgZ2wuU1RFTkNJTF9URVNUOiBzZWxmLnN0ZW5jaWxUZXN0ID0gdHJ1ZTsgYnJlYWs7XG4gICAgfVxuICAgIHNlbGYucmVhbEVuYWJsZS5jYWxsKGdsLCBwbmFtZSk7XG4gIH07XG5cbiAgZ2wuZGlzYWJsZSA9IGZ1bmN0aW9uKHBuYW1lKSB7XG4gICAgc3dpdGNoIChwbmFtZSkge1xuICAgICAgY2FzZSBnbC5DVUxMX0ZBQ0U6IHNlbGYuY3VsbEZhY2UgPSBmYWxzZTsgYnJlYWs7XG4gICAgICBjYXNlIGdsLkRFUFRIX1RFU1Q6IHNlbGYuZGVwdGhUZXN0ID0gZmFsc2U7IGJyZWFrO1xuICAgICAgY2FzZSBnbC5CTEVORDogc2VsZi5ibGVuZCA9IGZhbHNlOyBicmVhaztcbiAgICAgIGNhc2UgZ2wuU0NJU1NPUl9URVNUOiBzZWxmLnNjaXNzb3JUZXN0ID0gZmFsc2U7IGJyZWFrO1xuICAgICAgY2FzZSBnbC5TVEVOQ0lMX1RFU1Q6IHNlbGYuc3RlbmNpbFRlc3QgPSBmYWxzZTsgYnJlYWs7XG4gICAgfVxuICAgIHNlbGYucmVhbERpc2FibGUuY2FsbChnbCwgcG5hbWUpO1xuICB9O1xuXG4gIHRoaXMuY29sb3JNYXNrID0gZ2wuZ2V0UGFyYW1ldGVyKGdsLkNPTE9SX1dSSVRFTUFTSyk7XG4gIGdsLmNvbG9yTWFzayA9IGZ1bmN0aW9uKHIsIGcsIGIsIGEpIHtcbiAgICBzZWxmLmNvbG9yTWFza1swXSA9IHI7XG4gICAgc2VsZi5jb2xvck1hc2tbMV0gPSBnO1xuICAgIHNlbGYuY29sb3JNYXNrWzJdID0gYjtcbiAgICBzZWxmLmNvbG9yTWFza1szXSA9IGE7XG4gICAgc2VsZi5yZWFsQ29sb3JNYXNrLmNhbGwoZ2wsIHIsIGcsIGIsIGEpO1xuICB9O1xuXG4gIHRoaXMuY2xlYXJDb2xvciA9IGdsLmdldFBhcmFtZXRlcihnbC5DT0xPUl9DTEVBUl9WQUxVRSk7XG4gIGdsLmNsZWFyQ29sb3IgPSBmdW5jdGlvbihyLCBnLCBiLCBhKSB7XG4gICAgc2VsZi5jbGVhckNvbG9yWzBdID0gcjtcbiAgICBzZWxmLmNsZWFyQ29sb3JbMV0gPSBnO1xuICAgIHNlbGYuY2xlYXJDb2xvclsyXSA9IGI7XG4gICAgc2VsZi5jbGVhckNvbG9yWzNdID0gYTtcbiAgICBzZWxmLnJlYWxDbGVhckNvbG9yLmNhbGwoZ2wsIHIsIGcsIGIsIGEpO1xuICB9O1xuXG4gIHRoaXMudmlld3BvcnQgPSBnbC5nZXRQYXJhbWV0ZXIoZ2wuVklFV1BPUlQpO1xuICBnbC52aWV3cG9ydCA9IGZ1bmN0aW9uKHgsIHksIHcsIGgpIHtcbiAgICBzZWxmLnZpZXdwb3J0WzBdID0geDtcbiAgICBzZWxmLnZpZXdwb3J0WzFdID0geTtcbiAgICBzZWxmLnZpZXdwb3J0WzJdID0gdztcbiAgICBzZWxmLnZpZXdwb3J0WzNdID0gaDtcbiAgICBzZWxmLnJlYWxWaWV3cG9ydC5jYWxsKGdsLCB4LCB5LCB3LCBoKTtcbiAgfTtcblxuICB0aGlzLmlzUGF0Y2hlZCA9IHRydWU7XG4gIFV0aWwuc2FmYXJpQ3NzU2l6ZVdvcmthcm91bmQoY2FudmFzKTtcbn07XG5cbkNhcmRib2FyZERpc3RvcnRlci5wcm90b3R5cGUudW5wYXRjaCA9IGZ1bmN0aW9uKCkge1xuICBpZiAoIXRoaXMuaXNQYXRjaGVkKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdmFyIGdsID0gdGhpcy5nbDtcbiAgdmFyIGNhbnZhcyA9IHRoaXMuZ2wuY2FudmFzO1xuXG4gIGlmICghVXRpbC5pc0lPUygpKSB7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KGNhbnZhcywgJ3dpZHRoJywgdGhpcy5yZWFsQ2FudmFzV2lkdGgpO1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShjYW52YXMsICdoZWlnaHQnLCB0aGlzLnJlYWxDYW52YXNIZWlnaHQpO1xuICB9XG4gIGNhbnZhcy53aWR0aCA9IHRoaXMuYnVmZmVyV2lkdGg7XG4gIGNhbnZhcy5oZWlnaHQgPSB0aGlzLmJ1ZmZlckhlaWdodDtcblxuICBnbC5iaW5kRnJhbWVidWZmZXIgPSB0aGlzLnJlYWxCaW5kRnJhbWVidWZmZXI7XG4gIGdsLmVuYWJsZSA9IHRoaXMucmVhbEVuYWJsZTtcbiAgZ2wuZGlzYWJsZSA9IHRoaXMucmVhbERpc2FibGU7XG4gIGdsLmNvbG9yTWFzayA9IHRoaXMucmVhbENvbG9yTWFzaztcbiAgZ2wuY2xlYXJDb2xvciA9IHRoaXMucmVhbENsZWFyQ29sb3I7XG4gIGdsLnZpZXdwb3J0ID0gdGhpcy5yZWFsVmlld3BvcnQ7XG5cbiAgLy8gQ2hlY2sgdG8gc2VlIGlmIG91ciBmYWtlIGJhY2tidWZmZXIgaXMgYm91bmQgYW5kIGJpbmQgdGhlIHJlYWwgYmFja2J1ZmZlclxuICAvLyBpZiB0aGF0J3MgdGhlIGNhc2UuXG4gIGlmICh0aGlzLmxhc3RCb3VuZEZyYW1lYnVmZmVyID09IHRoaXMuZnJhbWVidWZmZXIpIHtcbiAgICBnbC5iaW5kRnJhbWVidWZmZXIoZ2wuRlJBTUVCVUZGRVIsIG51bGwpO1xuICB9XG5cbiAgdGhpcy5pc1BhdGNoZWQgPSBmYWxzZTtcblxuICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgIFV0aWwuc2FmYXJpQ3NzU2l6ZVdvcmthcm91bmQoY2FudmFzKTtcbiAgfSwgMSk7XG59O1xuXG5DYXJkYm9hcmREaXN0b3J0ZXIucHJvdG90eXBlLnNldFRleHR1cmVCb3VuZHMgPSBmdW5jdGlvbihsZWZ0Qm91bmRzLCByaWdodEJvdW5kcykge1xuICBpZiAoIWxlZnRCb3VuZHMpIHtcbiAgICBsZWZ0Qm91bmRzID0gWzAsIDAsIDAuNSwgMV07XG4gIH1cblxuICBpZiAoIXJpZ2h0Qm91bmRzKSB7XG4gICAgcmlnaHRCb3VuZHMgPSBbMC41LCAwLCAwLjUsIDFdO1xuICB9XG5cbiAgLy8gTGVmdCBleWVcbiAgdGhpcy52aWV3cG9ydE9mZnNldFNjYWxlWzBdID0gbGVmdEJvdW5kc1swXTsgLy8gWFxuICB0aGlzLnZpZXdwb3J0T2Zmc2V0U2NhbGVbMV0gPSBsZWZ0Qm91bmRzWzFdOyAvLyBZXG4gIHRoaXMudmlld3BvcnRPZmZzZXRTY2FsZVsyXSA9IGxlZnRCb3VuZHNbMl07IC8vIFdpZHRoXG4gIHRoaXMudmlld3BvcnRPZmZzZXRTY2FsZVszXSA9IGxlZnRCb3VuZHNbM107IC8vIEhlaWdodFxuXG4gIC8vIFJpZ2h0IGV5ZVxuICB0aGlzLnZpZXdwb3J0T2Zmc2V0U2NhbGVbNF0gPSByaWdodEJvdW5kc1swXTsgLy8gWFxuICB0aGlzLnZpZXdwb3J0T2Zmc2V0U2NhbGVbNV0gPSByaWdodEJvdW5kc1sxXTsgLy8gWVxuICB0aGlzLnZpZXdwb3J0T2Zmc2V0U2NhbGVbNl0gPSByaWdodEJvdW5kc1syXTsgLy8gV2lkdGhcbiAgdGhpcy52aWV3cG9ydE9mZnNldFNjYWxlWzddID0gcmlnaHRCb3VuZHNbM107IC8vIEhlaWdodFxufTtcblxuLyoqXG4gKiBQZXJmb3JtcyBkaXN0b3J0aW9uIHBhc3Mgb24gdGhlIGluamVjdGVkIGJhY2tidWZmZXIsIHJlbmRlcmluZyBpdCB0byB0aGUgcmVhbFxuICogYmFja2J1ZmZlci5cbiAqL1xuQ2FyZGJvYXJkRGlzdG9ydGVyLnByb3RvdHlwZS5zdWJtaXRGcmFtZSA9IGZ1bmN0aW9uKCkge1xuICB2YXIgZ2wgPSB0aGlzLmdsO1xuICB2YXIgc2VsZiA9IHRoaXM7XG5cbiAgdmFyIGdsU3RhdGUgPSBbXTtcblxuICBpZiAoIXdpbmRvdy5XZWJWUkNvbmZpZy5ESVJUWV9TVUJNSVRfRlJBTUVfQklORElOR1MpIHtcbiAgICBnbFN0YXRlLnB1c2goXG4gICAgICBnbC5DVVJSRU5UX1BST0dSQU0sXG4gICAgICBnbC5BUlJBWV9CVUZGRVJfQklORElORyxcbiAgICAgIGdsLkVMRU1FTlRfQVJSQVlfQlVGRkVSX0JJTkRJTkcsXG4gICAgICBnbC5URVhUVVJFX0JJTkRJTkdfMkQsIGdsLlRFWFRVUkUwXG4gICAgKTtcbiAgfVxuXG4gIFdHTFVQcmVzZXJ2ZUdMU3RhdGUoZ2wsIGdsU3RhdGUsIGZ1bmN0aW9uKGdsKSB7XG4gICAgLy8gQmluZCB0aGUgcmVhbCBkZWZhdWx0IGZyYW1lYnVmZmVyXG4gICAgc2VsZi5yZWFsQmluZEZyYW1lYnVmZmVyLmNhbGwoZ2wsIGdsLkZSQU1FQlVGRkVSLCBudWxsKTtcblxuICAgIC8vIE1ha2Ugc3VyZSB0aGUgR0wgc3RhdGUgaXMgaW4gYSBnb29kIHBsYWNlXG4gICAgaWYgKHNlbGYuY3VsbEZhY2UpIHsgc2VsZi5yZWFsRGlzYWJsZS5jYWxsKGdsLCBnbC5DVUxMX0ZBQ0UpOyB9XG4gICAgaWYgKHNlbGYuZGVwdGhUZXN0KSB7IHNlbGYucmVhbERpc2FibGUuY2FsbChnbCwgZ2wuREVQVEhfVEVTVCk7IH1cbiAgICBpZiAoc2VsZi5ibGVuZCkgeyBzZWxmLnJlYWxEaXNhYmxlLmNhbGwoZ2wsIGdsLkJMRU5EKTsgfVxuICAgIGlmIChzZWxmLnNjaXNzb3JUZXN0KSB7IHNlbGYucmVhbERpc2FibGUuY2FsbChnbCwgZ2wuU0NJU1NPUl9URVNUKTsgfVxuICAgIGlmIChzZWxmLnN0ZW5jaWxUZXN0KSB7IHNlbGYucmVhbERpc2FibGUuY2FsbChnbCwgZ2wuU1RFTkNJTF9URVNUKTsgfVxuICAgIHNlbGYucmVhbENvbG9yTWFzay5jYWxsKGdsLCB0cnVlLCB0cnVlLCB0cnVlLCB0cnVlKTtcbiAgICBzZWxmLnJlYWxWaWV3cG9ydC5jYWxsKGdsLCAwLCAwLCBnbC5kcmF3aW5nQnVmZmVyV2lkdGgsIGdsLmRyYXdpbmdCdWZmZXJIZWlnaHQpO1xuXG4gICAgLy8gSWYgdGhlIGJhY2tidWZmZXIgaGFzIGFuIGFscGhhIGNoYW5uZWwgY2xlYXIgZXZlcnkgZnJhbWUgc28gdGhlIHBhZ2VcbiAgICAvLyBkb2Vzbid0IHNob3cgdGhyb3VnaC5cbiAgICBpZiAoc2VsZi5jdHhBdHRyaWJzLmFscGhhIHx8IFV0aWwuaXNJT1MoKSkge1xuICAgICAgc2VsZi5yZWFsQ2xlYXJDb2xvci5jYWxsKGdsLCAwLCAwLCAwLCAxKTtcbiAgICAgIGdsLmNsZWFyKGdsLkNPTE9SX0JVRkZFUl9CSVQpO1xuICAgIH1cblxuICAgIC8vIEJpbmQgZGlzdG9ydGlvbiBwcm9ncmFtIGFuZCBtZXNoXG4gICAgZ2wudXNlUHJvZ3JhbShzZWxmLnByb2dyYW0pO1xuXG4gICAgZ2wuYmluZEJ1ZmZlcihnbC5FTEVNRU5UX0FSUkFZX0JVRkZFUiwgc2VsZi5pbmRleEJ1ZmZlcik7XG5cbiAgICBnbC5iaW5kQnVmZmVyKGdsLkFSUkFZX0JVRkZFUiwgc2VsZi52ZXJ0ZXhCdWZmZXIpO1xuICAgIGdsLmVuYWJsZVZlcnRleEF0dHJpYkFycmF5KHNlbGYuYXR0cmlicy5wb3NpdGlvbik7XG4gICAgZ2wuZW5hYmxlVmVydGV4QXR0cmliQXJyYXkoc2VsZi5hdHRyaWJzLnRleENvb3JkKTtcbiAgICBnbC52ZXJ0ZXhBdHRyaWJQb2ludGVyKHNlbGYuYXR0cmlicy5wb3NpdGlvbiwgMiwgZ2wuRkxPQVQsIGZhbHNlLCAyMCwgMCk7XG4gICAgZ2wudmVydGV4QXR0cmliUG9pbnRlcihzZWxmLmF0dHJpYnMudGV4Q29vcmQsIDMsIGdsLkZMT0FULCBmYWxzZSwgMjAsIDgpO1xuXG4gICAgZ2wuYWN0aXZlVGV4dHVyZShnbC5URVhUVVJFMCk7XG4gICAgZ2wudW5pZm9ybTFpKHNlbGYudW5pZm9ybXMuZGlmZnVzZSwgMCk7XG4gICAgZ2wuYmluZFRleHR1cmUoZ2wuVEVYVFVSRV8yRCwgc2VsZi5yZW5kZXJUYXJnZXQpO1xuXG4gICAgZ2wudW5pZm9ybTRmdihzZWxmLnVuaWZvcm1zLnZpZXdwb3J0T2Zmc2V0U2NhbGUsIHNlbGYudmlld3BvcnRPZmZzZXRTY2FsZSk7XG5cbiAgICAvLyBEcmF3cyBib3RoIGV5ZXNcbiAgICBnbC5kcmF3RWxlbWVudHMoZ2wuVFJJQU5HTEVTLCBzZWxmLmluZGV4Q291bnQsIGdsLlVOU0lHTkVEX1NIT1JULCAwKTtcblxuICAgIGlmIChzZWxmLmNhcmRib2FyZFVJKSB7XG4gICAgICBzZWxmLmNhcmRib2FyZFVJLnJlbmRlck5vU3RhdGUoKTtcbiAgICB9XG5cbiAgICAvLyBCaW5kIHRoZSBmYWtlIGRlZmF1bHQgZnJhbWVidWZmZXIgYWdhaW5cbiAgICBzZWxmLnJlYWxCaW5kRnJhbWVidWZmZXIuY2FsbChzZWxmLmdsLCBnbC5GUkFNRUJVRkZFUiwgc2VsZi5mcmFtZWJ1ZmZlcik7XG5cbiAgICAvLyBJZiBwcmVzZXJ2ZURyYXdpbmdCdWZmZXIgPT0gZmFsc2UgY2xlYXIgdGhlIGZyYW1lYnVmZmVyXG4gICAgaWYgKCFzZWxmLmN0eEF0dHJpYnMucHJlc2VydmVEcmF3aW5nQnVmZmVyKSB7XG4gICAgICBzZWxmLnJlYWxDbGVhckNvbG9yLmNhbGwoZ2wsIDAsIDAsIDAsIDApO1xuICAgICAgZ2wuY2xlYXIoZ2wuQ09MT1JfQlVGRkVSX0JJVCk7XG4gICAgfVxuXG4gICAgaWYgKCF3aW5kb3cuV2ViVlJDb25maWcuRElSVFlfU1VCTUlUX0ZSQU1FX0JJTkRJTkdTKSB7XG4gICAgICBzZWxmLnJlYWxCaW5kRnJhbWVidWZmZXIuY2FsbChnbCwgZ2wuRlJBTUVCVUZGRVIsIHNlbGYubGFzdEJvdW5kRnJhbWVidWZmZXIpO1xuICAgIH1cblxuICAgIC8vIFJlc3RvcmUgc3RhdGVcbiAgICBpZiAoc2VsZi5jdWxsRmFjZSkgeyBzZWxmLnJlYWxFbmFibGUuY2FsbChnbCwgZ2wuQ1VMTF9GQUNFKTsgfVxuICAgIGlmIChzZWxmLmRlcHRoVGVzdCkgeyBzZWxmLnJlYWxFbmFibGUuY2FsbChnbCwgZ2wuREVQVEhfVEVTVCk7IH1cbiAgICBpZiAoc2VsZi5ibGVuZCkgeyBzZWxmLnJlYWxFbmFibGUuY2FsbChnbCwgZ2wuQkxFTkQpOyB9XG4gICAgaWYgKHNlbGYuc2Npc3NvclRlc3QpIHsgc2VsZi5yZWFsRW5hYmxlLmNhbGwoZ2wsIGdsLlNDSVNTT1JfVEVTVCk7IH1cbiAgICBpZiAoc2VsZi5zdGVuY2lsVGVzdCkgeyBzZWxmLnJlYWxFbmFibGUuY2FsbChnbCwgZ2wuU1RFTkNJTF9URVNUKTsgfVxuXG4gICAgc2VsZi5yZWFsQ29sb3JNYXNrLmFwcGx5KGdsLCBzZWxmLmNvbG9yTWFzayk7XG4gICAgc2VsZi5yZWFsVmlld3BvcnQuYXBwbHkoZ2wsIHNlbGYudmlld3BvcnQpO1xuICAgIGlmIChzZWxmLmN0eEF0dHJpYnMuYWxwaGEgfHwgIXNlbGYuY3R4QXR0cmlicy5wcmVzZXJ2ZURyYXdpbmdCdWZmZXIpIHtcbiAgICAgIHNlbGYucmVhbENsZWFyQ29sb3IuYXBwbHkoZ2wsIHNlbGYuY2xlYXJDb2xvcik7XG4gICAgfVxuICB9KTtcblxuICAvLyBXb3JrYXJvdW5kIGZvciB0aGUgZmFjdCB0aGF0IFNhZmFyaSBkb2Vzbid0IGFsbG93IHVzIHRvIHBhdGNoIHRoZSBjYW52YXNcbiAgLy8gd2lkdGggYW5kIGhlaWdodCBjb3JyZWN0bHkuIEFmdGVyIGVhY2ggc3VibWl0IGZyYW1lIGNoZWNrIHRvIHNlZSB3aGF0IHRoZVxuICAvLyByZWFsIGJhY2tidWZmZXIgc2l6ZSBoYXMgYmVlbiBzZXQgdG8gYW5kIHJlc2l6ZSB0aGUgZmFrZSBiYWNrYnVmZmVyIHNpemVcbiAgLy8gdG8gbWF0Y2guXG4gIGlmIChVdGlsLmlzSU9TKCkpIHtcbiAgICB2YXIgY2FudmFzID0gZ2wuY2FudmFzO1xuICAgIGlmIChjYW52YXMud2lkdGggIT0gc2VsZi5idWZmZXJXaWR0aCB8fCBjYW52YXMuaGVpZ2h0ICE9IHNlbGYuYnVmZmVySGVpZ2h0KSB7XG4gICAgICBzZWxmLmJ1ZmZlcldpZHRoID0gY2FudmFzLndpZHRoO1xuICAgICAgc2VsZi5idWZmZXJIZWlnaHQgPSBjYW52YXMuaGVpZ2h0O1xuICAgICAgc2VsZi5vblJlc2l6ZSgpO1xuICAgIH1cbiAgfVxufTtcblxuLyoqXG4gKiBDYWxsIHdoZW4gdGhlIGRldmljZUluZm8gaGFzIGNoYW5nZWQuIEF0IHRoaXMgcG9pbnQgd2UgbmVlZFxuICogdG8gcmUtY2FsY3VsYXRlIHRoZSBkaXN0b3J0aW9uIG1lc2guXG4gKi9cbkNhcmRib2FyZERpc3RvcnRlci5wcm90b3R5cGUudXBkYXRlRGV2aWNlSW5mbyA9IGZ1bmN0aW9uKGRldmljZUluZm8pIHtcbiAgdmFyIGdsID0gdGhpcy5nbDtcbiAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gIHZhciBnbFN0YXRlID0gW2dsLkFSUkFZX0JVRkZFUl9CSU5ESU5HLCBnbC5FTEVNRU5UX0FSUkFZX0JVRkZFUl9CSU5ESU5HXTtcbiAgV0dMVVByZXNlcnZlR0xTdGF0ZShnbCwgZ2xTdGF0ZSwgZnVuY3Rpb24oZ2wpIHtcbiAgICB2YXIgdmVydGljZXMgPSBzZWxmLmNvbXB1dGVNZXNoVmVydGljZXNfKHNlbGYubWVzaFdpZHRoLCBzZWxmLm1lc2hIZWlnaHQsIGRldmljZUluZm8pO1xuICAgIGdsLmJpbmRCdWZmZXIoZ2wuQVJSQVlfQlVGRkVSLCBzZWxmLnZlcnRleEJ1ZmZlcik7XG4gICAgZ2wuYnVmZmVyRGF0YShnbC5BUlJBWV9CVUZGRVIsIHZlcnRpY2VzLCBnbC5TVEFUSUNfRFJBVyk7XG5cbiAgICAvLyBJbmRpY2VzIGRvbid0IGNoYW5nZSBiYXNlZCBvbiBkZXZpY2UgcGFyYW1ldGVycywgc28gb25seSBjb21wdXRlIG9uY2UuXG4gICAgaWYgKCFzZWxmLmluZGV4Q291bnQpIHtcbiAgICAgIHZhciBpbmRpY2VzID0gc2VsZi5jb21wdXRlTWVzaEluZGljZXNfKHNlbGYubWVzaFdpZHRoLCBzZWxmLm1lc2hIZWlnaHQpO1xuICAgICAgZ2wuYmluZEJ1ZmZlcihnbC5FTEVNRU5UX0FSUkFZX0JVRkZFUiwgc2VsZi5pbmRleEJ1ZmZlcik7XG4gICAgICBnbC5idWZmZXJEYXRhKGdsLkVMRU1FTlRfQVJSQVlfQlVGRkVSLCBpbmRpY2VzLCBnbC5TVEFUSUNfRFJBVyk7XG4gICAgICBzZWxmLmluZGV4Q291bnQgPSBpbmRpY2VzLmxlbmd0aDtcbiAgICB9XG4gIH0pO1xufTtcblxuLyoqXG4gKiBCdWlsZCB0aGUgZGlzdG9ydGlvbiBtZXNoIHZlcnRpY2VzLlxuICogQmFzZWQgb24gY29kZSBmcm9tIHRoZSBVbml0eSBjYXJkYm9hcmQgcGx1Z2luLlxuICovXG5DYXJkYm9hcmREaXN0b3J0ZXIucHJvdG90eXBlLmNvbXB1dGVNZXNoVmVydGljZXNfID0gZnVuY3Rpb24od2lkdGgsIGhlaWdodCwgZGV2aWNlSW5mbykge1xuICB2YXIgdmVydGljZXMgPSBuZXcgRmxvYXQzMkFycmF5KDIgKiB3aWR0aCAqIGhlaWdodCAqIDUpO1xuXG4gIHZhciBsZW5zRnJ1c3R1bSA9IGRldmljZUluZm8uZ2V0TGVmdEV5ZVZpc2libGVUYW5BbmdsZXMoKTtcbiAgdmFyIG5vTGVuc0ZydXN0dW0gPSBkZXZpY2VJbmZvLmdldExlZnRFeWVOb0xlbnNUYW5BbmdsZXMoKTtcbiAgdmFyIHZpZXdwb3J0ID0gZGV2aWNlSW5mby5nZXRMZWZ0RXllVmlzaWJsZVNjcmVlblJlY3Qobm9MZW5zRnJ1c3R1bSk7XG4gIHZhciB2aWR4ID0gMDtcbiAgdmFyIGlpZHggPSAwO1xuICBmb3IgKHZhciBlID0gMDsgZSA8IDI7IGUrKykge1xuICAgIGZvciAodmFyIGogPSAwOyBqIDwgaGVpZ2h0OyBqKyspIHtcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgd2lkdGg7IGkrKywgdmlkeCsrKSB7XG4gICAgICAgIHZhciB1ID0gaSAvICh3aWR0aCAtIDEpO1xuICAgICAgICB2YXIgdiA9IGogLyAoaGVpZ2h0IC0gMSk7XG5cbiAgICAgICAgLy8gR3JpZCBwb2ludHMgcmVndWxhcmx5IHNwYWNlZCBpbiBTdHJlb1NjcmVlbiwgYW5kIGJhcnJlbCBkaXN0b3J0ZWQgaW5cbiAgICAgICAgLy8gdGhlIG1lc2guXG4gICAgICAgIHZhciBzID0gdTtcbiAgICAgICAgdmFyIHQgPSB2O1xuICAgICAgICB2YXIgeCA9IFV0aWwubGVycChsZW5zRnJ1c3R1bVswXSwgbGVuc0ZydXN0dW1bMl0sIHUpO1xuICAgICAgICB2YXIgeSA9IFV0aWwubGVycChsZW5zRnJ1c3R1bVszXSwgbGVuc0ZydXN0dW1bMV0sIHYpO1xuICAgICAgICB2YXIgZCA9IE1hdGguc3FydCh4ICogeCArIHkgKiB5KTtcbiAgICAgICAgdmFyIHIgPSBkZXZpY2VJbmZvLmRpc3RvcnRpb24uZGlzdG9ydEludmVyc2UoZCk7XG4gICAgICAgIHZhciBwID0geCAqIHIgLyBkO1xuICAgICAgICB2YXIgcSA9IHkgKiByIC8gZDtcbiAgICAgICAgdSA9IChwIC0gbm9MZW5zRnJ1c3R1bVswXSkgLyAobm9MZW5zRnJ1c3R1bVsyXSAtIG5vTGVuc0ZydXN0dW1bMF0pO1xuICAgICAgICB2ID0gKHEgLSBub0xlbnNGcnVzdHVtWzNdKSAvIChub0xlbnNGcnVzdHVtWzFdIC0gbm9MZW5zRnJ1c3R1bVszXSk7XG5cbiAgICAgICAgLy8gQ29udmVydCB1LHYgdG8gbWVzaCBzY3JlZW4gY29vcmRpbmF0ZXMuXG4gICAgICAgIHZhciBhc3BlY3QgPSBkZXZpY2VJbmZvLmRldmljZS53aWR0aE1ldGVycyAvIGRldmljZUluZm8uZGV2aWNlLmhlaWdodE1ldGVycztcblxuICAgICAgICAvLyBGSVhNRTogVGhlIG9yaWdpbmFsIFVuaXR5IHBsdWdpbiBtdWx0aXBsaWVkIFUgYnkgdGhlIGFzcGVjdCByYXRpb1xuICAgICAgICAvLyBhbmQgZGlkbid0IG11bHRpcGx5IGVpdGhlciB2YWx1ZSBieSAyLCBidXQgdGhhdCBzZWVtcyB0byBnZXQgaXRcbiAgICAgICAgLy8gcmVhbGx5IGNsb3NlIHRvIGNvcnJlY3QgbG9va2luZyBmb3IgbWUuIEkgaGF0ZSB0aGlzIGtpbmQgb2YgXCJEb24ndFxuICAgICAgICAvLyBrbm93IHdoeSBpdCB3b3Jrc1wiIGNvZGUgdGhvdWdoLCBhbmQgd29sZCBsb3ZlIGEgbW9yZSBsb2dpY2FsXG4gICAgICAgIC8vIGV4cGxhbmF0aW9uIG9mIHdoYXQgbmVlZHMgdG8gaGFwcGVuIGhlcmUuXG4gICAgICAgIHUgPSAodmlld3BvcnQueCArIHUgKiB2aWV3cG9ydC53aWR0aCAtIDAuNSkgKiAyLjA7IC8vKiBhc3BlY3Q7XG4gICAgICAgIHYgPSAodmlld3BvcnQueSArIHYgKiB2aWV3cG9ydC5oZWlnaHQgLSAwLjUpICogMi4wO1xuXG4gICAgICAgIHZlcnRpY2VzWyh2aWR4ICogNSkgKyAwXSA9IHU7IC8vIHBvc2l0aW9uLnhcbiAgICAgICAgdmVydGljZXNbKHZpZHggKiA1KSArIDFdID0gdjsgLy8gcG9zaXRpb24ueVxuICAgICAgICB2ZXJ0aWNlc1sodmlkeCAqIDUpICsgMl0gPSBzOyAvLyB0ZXhDb29yZC54XG4gICAgICAgIHZlcnRpY2VzWyh2aWR4ICogNSkgKyAzXSA9IHQ7IC8vIHRleENvb3JkLnlcbiAgICAgICAgdmVydGljZXNbKHZpZHggKiA1KSArIDRdID0gZTsgLy8gdGV4Q29vcmQueiAodmlld3BvcnQgaW5kZXgpXG4gICAgICB9XG4gICAgfVxuICAgIHZhciB3ID0gbGVuc0ZydXN0dW1bMl0gLSBsZW5zRnJ1c3R1bVswXTtcbiAgICBsZW5zRnJ1c3R1bVswXSA9IC0odyArIGxlbnNGcnVzdHVtWzBdKTtcbiAgICBsZW5zRnJ1c3R1bVsyXSA9IHcgLSBsZW5zRnJ1c3R1bVsyXTtcbiAgICB3ID0gbm9MZW5zRnJ1c3R1bVsyXSAtIG5vTGVuc0ZydXN0dW1bMF07XG4gICAgbm9MZW5zRnJ1c3R1bVswXSA9IC0odyArIG5vTGVuc0ZydXN0dW1bMF0pO1xuICAgIG5vTGVuc0ZydXN0dW1bMl0gPSB3IC0gbm9MZW5zRnJ1c3R1bVsyXTtcbiAgICB2aWV3cG9ydC54ID0gMSAtICh2aWV3cG9ydC54ICsgdmlld3BvcnQud2lkdGgpO1xuICB9XG4gIHJldHVybiB2ZXJ0aWNlcztcbn1cblxuLyoqXG4gKiBCdWlsZCB0aGUgZGlzdG9ydGlvbiBtZXNoIGluZGljZXMuXG4gKiBCYXNlZCBvbiBjb2RlIGZyb20gdGhlIFVuaXR5IGNhcmRib2FyZCBwbHVnaW4uXG4gKi9cbkNhcmRib2FyZERpc3RvcnRlci5wcm90b3R5cGUuY29tcHV0ZU1lc2hJbmRpY2VzXyA9IGZ1bmN0aW9uKHdpZHRoLCBoZWlnaHQpIHtcbiAgdmFyIGluZGljZXMgPSBuZXcgVWludDE2QXJyYXkoMiAqICh3aWR0aCAtIDEpICogKGhlaWdodCAtIDEpICogNik7XG4gIHZhciBoYWxmd2lkdGggPSB3aWR0aCAvIDI7XG4gIHZhciBoYWxmaGVpZ2h0ID0gaGVpZ2h0IC8gMjtcbiAgdmFyIHZpZHggPSAwO1xuICB2YXIgaWlkeCA9IDA7XG4gIGZvciAodmFyIGUgPSAwOyBlIDwgMjsgZSsrKSB7XG4gICAgZm9yICh2YXIgaiA9IDA7IGogPCBoZWlnaHQ7IGorKykge1xuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB3aWR0aDsgaSsrLCB2aWR4KyspIHtcbiAgICAgICAgaWYgKGkgPT0gMCB8fCBqID09IDApXG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIC8vIEJ1aWxkIGEgcXVhZC4gIExvd2VyIHJpZ2h0IGFuZCB1cHBlciBsZWZ0IHF1YWRyYW50cyBoYXZlIHF1YWRzIHdpdGhcbiAgICAgICAgLy8gdGhlIHRyaWFuZ2xlIGRpYWdvbmFsIGZsaXBwZWQgdG8gZ2V0IHRoZSB2aWduZXR0ZSB0byBpbnRlcnBvbGF0ZVxuICAgICAgICAvLyBjb3JyZWN0bHkuXG4gICAgICAgIGlmICgoaSA8PSBoYWxmd2lkdGgpID09IChqIDw9IGhhbGZoZWlnaHQpKSB7XG4gICAgICAgICAgLy8gUXVhZCBkaWFnb25hbCBsb3dlciBsZWZ0IHRvIHVwcGVyIHJpZ2h0LlxuICAgICAgICAgIGluZGljZXNbaWlkeCsrXSA9IHZpZHg7XG4gICAgICAgICAgaW5kaWNlc1tpaWR4KytdID0gdmlkeCAtIHdpZHRoIC0gMTtcbiAgICAgICAgICBpbmRpY2VzW2lpZHgrK10gPSB2aWR4IC0gd2lkdGg7XG4gICAgICAgICAgaW5kaWNlc1tpaWR4KytdID0gdmlkeCAtIHdpZHRoIC0gMTtcbiAgICAgICAgICBpbmRpY2VzW2lpZHgrK10gPSB2aWR4O1xuICAgICAgICAgIGluZGljZXNbaWlkeCsrXSA9IHZpZHggLSAxO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIFF1YWQgZGlhZ29uYWwgdXBwZXIgbGVmdCB0byBsb3dlciByaWdodC5cbiAgICAgICAgICBpbmRpY2VzW2lpZHgrK10gPSB2aWR4IC0gMTtcbiAgICAgICAgICBpbmRpY2VzW2lpZHgrK10gPSB2aWR4IC0gd2lkdGg7XG4gICAgICAgICAgaW5kaWNlc1tpaWR4KytdID0gdmlkeDtcbiAgICAgICAgICBpbmRpY2VzW2lpZHgrK10gPSB2aWR4IC0gd2lkdGg7XG4gICAgICAgICAgaW5kaWNlc1tpaWR4KytdID0gdmlkeCAtIDE7XG4gICAgICAgICAgaW5kaWNlc1tpaWR4KytdID0gdmlkeCAtIHdpZHRoIC0gMTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuICByZXR1cm4gaW5kaWNlcztcbn07XG5cbkNhcmRib2FyZERpc3RvcnRlci5wcm90b3R5cGUuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yXyA9IGZ1bmN0aW9uKHByb3RvLCBhdHRyTmFtZSkge1xuICB2YXIgZGVzY3JpcHRvciA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IocHJvdG8sIGF0dHJOYW1lKTtcbiAgLy8gSW4gc29tZSBjYXNlcyAoYWhlbS4uLiBTYWZhcmkpLCB0aGUgZGVzY3JpcHRvciByZXR1cm5zIHVuZGVmaW5lZCBnZXQgYW5kXG4gIC8vIHNldCBmaWVsZHMuIEluIHRoaXMgY2FzZSwgd2UgbmVlZCB0byBjcmVhdGUgYSBzeW50aGV0aWMgcHJvcGVydHlcbiAgLy8gZGVzY3JpcHRvci4gVGhpcyB3b3JrcyBhcm91bmQgc29tZSBvZiB0aGUgaXNzdWVzIGluXG4gIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9ib3Jpc211cy93ZWJ2ci1wb2x5ZmlsbC9pc3N1ZXMvNDZcbiAgaWYgKGRlc2NyaXB0b3IuZ2V0ID09PSB1bmRlZmluZWQgfHwgZGVzY3JpcHRvci5zZXQgPT09IHVuZGVmaW5lZCkge1xuICAgIGRlc2NyaXB0b3IuY29uZmlndXJhYmxlID0gdHJ1ZTtcbiAgICBkZXNjcmlwdG9yLmVudW1lcmFibGUgPSB0cnVlO1xuICAgIGRlc2NyaXB0b3IuZ2V0ID0gZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gdGhpcy5nZXRBdHRyaWJ1dGUoYXR0ck5hbWUpO1xuICAgIH07XG4gICAgZGVzY3JpcHRvci5zZXQgPSBmdW5jdGlvbih2YWwpIHtcbiAgICAgIHRoaXMuc2V0QXR0cmlidXRlKGF0dHJOYW1lLCB2YWwpO1xuICAgIH07XG4gIH1cbiAgcmV0dXJuIGRlc2NyaXB0b3I7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IENhcmRib2FyZERpc3RvcnRlcjtcbiIsIi8qXG4gKiBDb3B5cmlnaHQgMjAxNiBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICovXG5cbnZhciBVdGlsID0gcmVxdWlyZSgnLi91dGlsLmpzJyk7XG52YXIgV0dMVVByZXNlcnZlR0xTdGF0ZSA9IHJlcXVpcmUoJy4vZGVwcy93Z2x1LXByZXNlcnZlLXN0YXRlLmpzJyk7XG5cbnZhciB1aVZTID0gW1xuICAnYXR0cmlidXRlIHZlYzIgcG9zaXRpb247JyxcblxuICAndW5pZm9ybSBtYXQ0IHByb2plY3Rpb25NYXQ7JyxcblxuICAndm9pZCBtYWluKCkgeycsXG4gICcgIGdsX1Bvc2l0aW9uID0gcHJvamVjdGlvbk1hdCAqIHZlYzQoIHBvc2l0aW9uLCAtMS4wLCAxLjAgKTsnLFxuICAnfScsXG5dLmpvaW4oJ1xcbicpO1xuXG52YXIgdWlGUyA9IFtcbiAgJ3ByZWNpc2lvbiBtZWRpdW1wIGZsb2F0OycsXG5cbiAgJ3VuaWZvcm0gdmVjNCBjb2xvcjsnLFxuXG4gICd2b2lkIG1haW4oKSB7JyxcbiAgJyAgZ2xfRnJhZ0NvbG9yID0gY29sb3I7JyxcbiAgJ30nLFxuXS5qb2luKCdcXG4nKTtcblxudmFyIERFRzJSQUQgPSBNYXRoLlBJLzE4MC4wO1xuXG4vLyBUaGUgZ2VhciBoYXMgNiBpZGVudGljYWwgc2VjdGlvbnMsIGVhY2ggc3Bhbm5pbmcgNjAgZGVncmVlcy5cbnZhciBrQW5nbGVQZXJHZWFyU2VjdGlvbiA9IDYwO1xuXG4vLyBIYWxmLWFuZ2xlIG9mIHRoZSBzcGFuIG9mIHRoZSBvdXRlciByaW0uXG52YXIga091dGVyUmltRW5kQW5nbGUgPSAxMjtcblxuLy8gQW5nbGUgYmV0d2VlbiB0aGUgbWlkZGxlIG9mIHRoZSBvdXRlciByaW0gYW5kIHRoZSBzdGFydCBvZiB0aGUgaW5uZXIgcmltLlxudmFyIGtJbm5lclJpbUJlZ2luQW5nbGUgPSAyMDtcblxuLy8gRGlzdGFuY2UgZnJvbSBjZW50ZXIgdG8gb3V0ZXIgcmltLCBub3JtYWxpemVkIHNvIHRoYXQgdGhlIGVudGlyZSBtb2RlbFxuLy8gZml0cyBpbiBhIFstMSwgMV0geCBbLTEsIDFdIHNxdWFyZS5cbnZhciBrT3V0ZXJSYWRpdXMgPSAxO1xuXG4vLyBEaXN0YW5jZSBmcm9tIGNlbnRlciB0byBkZXByZXNzZWQgcmltLCBpbiBtb2RlbCB1bml0cy5cbnZhciBrTWlkZGxlUmFkaXVzID0gMC43NTtcblxuLy8gUmFkaXVzIG9mIHRoZSBpbm5lciBob2xsb3cgY2lyY2xlLCBpbiBtb2RlbCB1bml0cy5cbnZhciBrSW5uZXJSYWRpdXMgPSAwLjMxMjU7XG5cbi8vIENlbnRlciBsaW5lIHRoaWNrbmVzcyBpbiBEUC5cbnZhciBrQ2VudGVyTGluZVRoaWNrbmVzc0RwID0gNDtcblxuLy8gQnV0dG9uIHdpZHRoIGluIERQLlxudmFyIGtCdXR0b25XaWR0aERwID0gMjg7XG5cbi8vIEZhY3RvciB0byBzY2FsZSB0aGUgdG91Y2ggYXJlYSB0aGF0IHJlc3BvbmRzIHRvIHRoZSB0b3VjaC5cbnZhciBrVG91Y2hTbG9wRmFjdG9yID0gMS41O1xuXG52YXIgQW5nbGVzID0gW1xuICAwLCBrT3V0ZXJSaW1FbmRBbmdsZSwga0lubmVyUmltQmVnaW5BbmdsZSxcbiAga0FuZ2xlUGVyR2VhclNlY3Rpb24gLSBrSW5uZXJSaW1CZWdpbkFuZ2xlLFxuICBrQW5nbGVQZXJHZWFyU2VjdGlvbiAtIGtPdXRlclJpbUVuZEFuZ2xlXG5dO1xuXG4vKipcbiAqIFJlbmRlcnMgdGhlIGFsaWdubWVudCBsaW5lIGFuZCBcIm9wdGlvbnNcIiBnZWFyLiBJdCBpcyBhc3N1bWVkIHRoYXQgdGhlIGNhbnZhc1xuICogdGhpcyBpcyByZW5kZXJlZCBpbnRvIGNvdmVycyB0aGUgZW50aXJlIHNjcmVlbiAob3IgY2xvc2UgdG8gaXQuKVxuICovXG5mdW5jdGlvbiBDYXJkYm9hcmRVSShnbCkge1xuICB0aGlzLmdsID0gZ2w7XG5cbiAgdGhpcy5hdHRyaWJzID0ge1xuICAgIHBvc2l0aW9uOiAwXG4gIH07XG4gIHRoaXMucHJvZ3JhbSA9IFV0aWwubGlua1Byb2dyYW0oZ2wsIHVpVlMsIHVpRlMsIHRoaXMuYXR0cmlicyk7XG4gIHRoaXMudW5pZm9ybXMgPSBVdGlsLmdldFByb2dyYW1Vbmlmb3JtcyhnbCwgdGhpcy5wcm9ncmFtKTtcblxuICB0aGlzLnZlcnRleEJ1ZmZlciA9IGdsLmNyZWF0ZUJ1ZmZlcigpO1xuICB0aGlzLmdlYXJPZmZzZXQgPSAwO1xuICB0aGlzLmdlYXJWZXJ0ZXhDb3VudCA9IDA7XG4gIHRoaXMuYXJyb3dPZmZzZXQgPSAwO1xuICB0aGlzLmFycm93VmVydGV4Q291bnQgPSAwO1xuXG4gIHRoaXMucHJvak1hdCA9IG5ldyBGbG9hdDMyQXJyYXkoMTYpO1xuXG4gIHRoaXMubGlzdGVuZXIgPSBudWxsO1xuXG4gIHRoaXMub25SZXNpemUoKTtcbn07XG5cbi8qKlxuICogVGVhcnMgZG93biBhbGwgdGhlIHJlc291cmNlcyBjcmVhdGVkIGJ5IHRoZSBVSSByZW5kZXJlci5cbiAqL1xuQ2FyZGJvYXJkVUkucHJvdG90eXBlLmRlc3Ryb3kgPSBmdW5jdGlvbigpIHtcbiAgdmFyIGdsID0gdGhpcy5nbDtcblxuICBpZiAodGhpcy5saXN0ZW5lcikge1xuICAgIGdsLmNhbnZhcy5yZW1vdmVFdmVudExpc3RlbmVyKCdjbGljaycsIHRoaXMubGlzdGVuZXIsIGZhbHNlKTtcbiAgfVxuXG4gIGdsLmRlbGV0ZVByb2dyYW0odGhpcy5wcm9ncmFtKTtcbiAgZ2wuZGVsZXRlQnVmZmVyKHRoaXMudmVydGV4QnVmZmVyKTtcbn07XG5cbi8qKlxuICogQWRkcyBhIGxpc3RlbmVyIHRvIGNsaWNrcyBvbiB0aGUgZ2VhciBhbmQgYmFjayBpY29uc1xuICovXG5DYXJkYm9hcmRVSS5wcm90b3R5cGUubGlzdGVuID0gZnVuY3Rpb24ob3B0aW9uc0NhbGxiYWNrLCBiYWNrQ2FsbGJhY2spIHtcbiAgdmFyIGNhbnZhcyA9IHRoaXMuZ2wuY2FudmFzO1xuICB0aGlzLmxpc3RlbmVyID0gZnVuY3Rpb24oZXZlbnQpIHtcbiAgICB2YXIgbWlkbGluZSA9IGNhbnZhcy5jbGllbnRXaWR0aCAvIDI7XG4gICAgdmFyIGJ1dHRvblNpemUgPSBrQnV0dG9uV2lkdGhEcCAqIGtUb3VjaFNsb3BGYWN0b3I7XG4gICAgLy8gQ2hlY2sgdG8gc2VlIGlmIHRoZSB1c2VyIGNsaWNrZWQgb24gKG9yIGFyb3VuZCkgdGhlIGdlYXIgaWNvblxuICAgIGlmIChldmVudC5jbGllbnRYID4gbWlkbGluZSAtIGJ1dHRvblNpemUgJiZcbiAgICAgICAgZXZlbnQuY2xpZW50WCA8IG1pZGxpbmUgKyBidXR0b25TaXplICYmXG4gICAgICAgIGV2ZW50LmNsaWVudFkgPiBjYW52YXMuY2xpZW50SGVpZ2h0IC0gYnV0dG9uU2l6ZSkge1xuICAgICAgb3B0aW9uc0NhbGxiYWNrKGV2ZW50KTtcbiAgICB9XG4gICAgLy8gQ2hlY2sgdG8gc2VlIGlmIHRoZSB1c2VyIGNsaWNrZWQgb24gKG9yIGFyb3VuZCkgdGhlIGJhY2sgaWNvblxuICAgIGVsc2UgaWYgKGV2ZW50LmNsaWVudFggPCBidXR0b25TaXplICYmIGV2ZW50LmNsaWVudFkgPCBidXR0b25TaXplKSB7XG4gICAgICBiYWNrQ2FsbGJhY2soZXZlbnQpO1xuICAgIH1cbiAgfTtcbiAgY2FudmFzLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgdGhpcy5saXN0ZW5lciwgZmFsc2UpO1xufTtcblxuLyoqXG4gKiBCdWlsZHMgdGhlIFVJIG1lc2guXG4gKi9cbkNhcmRib2FyZFVJLnByb3RvdHlwZS5vblJlc2l6ZSA9IGZ1bmN0aW9uKCkge1xuICB2YXIgZ2wgPSB0aGlzLmdsO1xuICB2YXIgc2VsZiA9IHRoaXM7XG5cbiAgdmFyIGdsU3RhdGUgPSBbXG4gICAgZ2wuQVJSQVlfQlVGRkVSX0JJTkRJTkdcbiAgXTtcblxuICBXR0xVUHJlc2VydmVHTFN0YXRlKGdsLCBnbFN0YXRlLCBmdW5jdGlvbihnbCkge1xuICAgIHZhciB2ZXJ0aWNlcyA9IFtdO1xuXG4gICAgdmFyIG1pZGxpbmUgPSBnbC5kcmF3aW5nQnVmZmVyV2lkdGggLyAyO1xuXG4gICAgLy8gVGhlIGdsIGJ1ZmZlciBzaXplIHdpbGwgbGlrZWx5IGJlIHNtYWxsZXIgdGhhbiB0aGUgcGh5c2ljYWwgcGl4ZWwgY291bnQuXG4gICAgLy8gU28gd2UgbmVlZCB0byBzY2FsZSB0aGUgZHBzIGRvd24gYmFzZWQgb24gdGhlIGFjdHVhbCBidWZmZXIgc2l6ZSB2cyBwaHlzaWNhbCBwaXhlbCBjb3VudC5cbiAgICAvLyBUaGlzIHdpbGwgcHJvcGVybHkgc2l6ZSB0aGUgdWkgZWxlbWVudHMgbm8gbWF0dGVyIHdoYXQgdGhlIGdsIGJ1ZmZlciByZXNvbHV0aW9uIGlzXG4gICAgdmFyIHBoeXNpY2FsUGl4ZWxzID0gTWF0aC5tYXgoc2NyZWVuLndpZHRoLCBzY3JlZW4uaGVpZ2h0KSAqIHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvO1xuICAgIHZhciBzY2FsaW5nUmF0aW8gPSBnbC5kcmF3aW5nQnVmZmVyV2lkdGggLyBwaHlzaWNhbFBpeGVscztcbiAgICB2YXIgZHBzID0gc2NhbGluZ1JhdGlvICogIHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvO1xuXG4gICAgdmFyIGxpbmVXaWR0aCA9IGtDZW50ZXJMaW5lVGhpY2tuZXNzRHAgKiBkcHMgLyAyO1xuICAgIHZhciBidXR0b25TaXplID0ga0J1dHRvbldpZHRoRHAgKiBrVG91Y2hTbG9wRmFjdG9yICogZHBzO1xuICAgIHZhciBidXR0b25TY2FsZSA9IGtCdXR0b25XaWR0aERwICogZHBzIC8gMjtcbiAgICB2YXIgYnV0dG9uQm9yZGVyID0gKChrQnV0dG9uV2lkdGhEcCAqIGtUb3VjaFNsb3BGYWN0b3IpIC0ga0J1dHRvbldpZHRoRHApICogZHBzO1xuXG4gICAgLy8gQnVpbGQgY2VudGVybGluZVxuICAgIHZlcnRpY2VzLnB1c2gobWlkbGluZSAtIGxpbmVXaWR0aCwgYnV0dG9uU2l6ZSk7XG4gICAgdmVydGljZXMucHVzaChtaWRsaW5lIC0gbGluZVdpZHRoLCBnbC5kcmF3aW5nQnVmZmVySGVpZ2h0KTtcbiAgICB2ZXJ0aWNlcy5wdXNoKG1pZGxpbmUgKyBsaW5lV2lkdGgsIGJ1dHRvblNpemUpO1xuICAgIHZlcnRpY2VzLnB1c2gobWlkbGluZSArIGxpbmVXaWR0aCwgZ2wuZHJhd2luZ0J1ZmZlckhlaWdodCk7XG5cbiAgICAvLyBCdWlsZCBnZWFyXG4gICAgc2VsZi5nZWFyT2Zmc2V0ID0gKHZlcnRpY2VzLmxlbmd0aCAvIDIpO1xuXG4gICAgZnVuY3Rpb24gYWRkR2VhclNlZ21lbnQodGhldGEsIHIpIHtcbiAgICAgIHZhciBhbmdsZSA9ICg5MCAtIHRoZXRhKSAqIERFRzJSQUQ7XG4gICAgICB2YXIgeCA9IE1hdGguY29zKGFuZ2xlKTtcbiAgICAgIHZhciB5ID0gTWF0aC5zaW4oYW5nbGUpO1xuICAgICAgdmVydGljZXMucHVzaChrSW5uZXJSYWRpdXMgKiB4ICogYnV0dG9uU2NhbGUgKyBtaWRsaW5lLCBrSW5uZXJSYWRpdXMgKiB5ICogYnV0dG9uU2NhbGUgKyBidXR0b25TY2FsZSk7XG4gICAgICB2ZXJ0aWNlcy5wdXNoKHIgKiB4ICogYnV0dG9uU2NhbGUgKyBtaWRsaW5lLCByICogeSAqIGJ1dHRvblNjYWxlICsgYnV0dG9uU2NhbGUpO1xuICAgIH1cblxuICAgIGZvciAodmFyIGkgPSAwOyBpIDw9IDY7IGkrKykge1xuICAgICAgdmFyIHNlZ21lbnRUaGV0YSA9IGkgKiBrQW5nbGVQZXJHZWFyU2VjdGlvbjtcblxuICAgICAgYWRkR2VhclNlZ21lbnQoc2VnbWVudFRoZXRhLCBrT3V0ZXJSYWRpdXMpO1xuICAgICAgYWRkR2VhclNlZ21lbnQoc2VnbWVudFRoZXRhICsga091dGVyUmltRW5kQW5nbGUsIGtPdXRlclJhZGl1cyk7XG4gICAgICBhZGRHZWFyU2VnbWVudChzZWdtZW50VGhldGEgKyBrSW5uZXJSaW1CZWdpbkFuZ2xlLCBrTWlkZGxlUmFkaXVzKTtcbiAgICAgIGFkZEdlYXJTZWdtZW50KHNlZ21lbnRUaGV0YSArIChrQW5nbGVQZXJHZWFyU2VjdGlvbiAtIGtJbm5lclJpbUJlZ2luQW5nbGUpLCBrTWlkZGxlUmFkaXVzKTtcbiAgICAgIGFkZEdlYXJTZWdtZW50KHNlZ21lbnRUaGV0YSArIChrQW5nbGVQZXJHZWFyU2VjdGlvbiAtIGtPdXRlclJpbUVuZEFuZ2xlKSwga091dGVyUmFkaXVzKTtcbiAgICB9XG5cbiAgICBzZWxmLmdlYXJWZXJ0ZXhDb3VudCA9ICh2ZXJ0aWNlcy5sZW5ndGggLyAyKSAtIHNlbGYuZ2Vhck9mZnNldDtcblxuICAgIC8vIEJ1aWxkIGJhY2sgYXJyb3dcbiAgICBzZWxmLmFycm93T2Zmc2V0ID0gKHZlcnRpY2VzLmxlbmd0aCAvIDIpO1xuXG4gICAgZnVuY3Rpb24gYWRkQXJyb3dWZXJ0ZXgoeCwgeSkge1xuICAgICAgdmVydGljZXMucHVzaChidXR0b25Cb3JkZXIgKyB4LCBnbC5kcmF3aW5nQnVmZmVySGVpZ2h0IC0gYnV0dG9uQm9yZGVyIC0geSk7XG4gICAgfVxuXG4gICAgdmFyIGFuZ2xlZExpbmVXaWR0aCA9IGxpbmVXaWR0aCAvIE1hdGguc2luKDQ1ICogREVHMlJBRCk7XG5cbiAgICBhZGRBcnJvd1ZlcnRleCgwLCBidXR0b25TY2FsZSk7XG4gICAgYWRkQXJyb3dWZXJ0ZXgoYnV0dG9uU2NhbGUsIDApO1xuICAgIGFkZEFycm93VmVydGV4KGJ1dHRvblNjYWxlICsgYW5nbGVkTGluZVdpZHRoLCBhbmdsZWRMaW5lV2lkdGgpO1xuICAgIGFkZEFycm93VmVydGV4KGFuZ2xlZExpbmVXaWR0aCwgYnV0dG9uU2NhbGUgKyBhbmdsZWRMaW5lV2lkdGgpO1xuXG4gICAgYWRkQXJyb3dWZXJ0ZXgoYW5nbGVkTGluZVdpZHRoLCBidXR0b25TY2FsZSAtIGFuZ2xlZExpbmVXaWR0aCk7XG4gICAgYWRkQXJyb3dWZXJ0ZXgoMCwgYnV0dG9uU2NhbGUpO1xuICAgIGFkZEFycm93VmVydGV4KGJ1dHRvblNjYWxlLCBidXR0b25TY2FsZSAqIDIpO1xuICAgIGFkZEFycm93VmVydGV4KGJ1dHRvblNjYWxlICsgYW5nbGVkTGluZVdpZHRoLCAoYnV0dG9uU2NhbGUgKiAyKSAtIGFuZ2xlZExpbmVXaWR0aCk7XG5cbiAgICBhZGRBcnJvd1ZlcnRleChhbmdsZWRMaW5lV2lkdGgsIGJ1dHRvblNjYWxlIC0gYW5nbGVkTGluZVdpZHRoKTtcbiAgICBhZGRBcnJvd1ZlcnRleCgwLCBidXR0b25TY2FsZSk7XG5cbiAgICBhZGRBcnJvd1ZlcnRleChhbmdsZWRMaW5lV2lkdGgsIGJ1dHRvblNjYWxlIC0gbGluZVdpZHRoKTtcbiAgICBhZGRBcnJvd1ZlcnRleChrQnV0dG9uV2lkdGhEcCAqIGRwcywgYnV0dG9uU2NhbGUgLSBsaW5lV2lkdGgpO1xuICAgIGFkZEFycm93VmVydGV4KGFuZ2xlZExpbmVXaWR0aCwgYnV0dG9uU2NhbGUgKyBsaW5lV2lkdGgpO1xuICAgIGFkZEFycm93VmVydGV4KGtCdXR0b25XaWR0aERwICogZHBzLCBidXR0b25TY2FsZSArIGxpbmVXaWR0aCk7XG5cbiAgICBzZWxmLmFycm93VmVydGV4Q291bnQgPSAodmVydGljZXMubGVuZ3RoIC8gMikgLSBzZWxmLmFycm93T2Zmc2V0O1xuXG4gICAgLy8gQnVmZmVyIGRhdGFcbiAgICBnbC5iaW5kQnVmZmVyKGdsLkFSUkFZX0JVRkZFUiwgc2VsZi52ZXJ0ZXhCdWZmZXIpO1xuICAgIGdsLmJ1ZmZlckRhdGEoZ2wuQVJSQVlfQlVGRkVSLCBuZXcgRmxvYXQzMkFycmF5KHZlcnRpY2VzKSwgZ2wuU1RBVElDX0RSQVcpO1xuICB9KTtcbn07XG5cbi8qKlxuICogUGVyZm9ybXMgZGlzdG9ydGlvbiBwYXNzIG9uIHRoZSBpbmplY3RlZCBiYWNrYnVmZmVyLCByZW5kZXJpbmcgaXQgdG8gdGhlIHJlYWxcbiAqIGJhY2tidWZmZXIuXG4gKi9cbkNhcmRib2FyZFVJLnByb3RvdHlwZS5yZW5kZXIgPSBmdW5jdGlvbigpIHtcbiAgdmFyIGdsID0gdGhpcy5nbDtcbiAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gIHZhciBnbFN0YXRlID0gW1xuICAgIGdsLkNVTExfRkFDRSxcbiAgICBnbC5ERVBUSF9URVNULFxuICAgIGdsLkJMRU5ELFxuICAgIGdsLlNDSVNTT1JfVEVTVCxcbiAgICBnbC5TVEVOQ0lMX1RFU1QsXG4gICAgZ2wuQ09MT1JfV1JJVEVNQVNLLFxuICAgIGdsLlZJRVdQT1JULFxuXG4gICAgZ2wuQ1VSUkVOVF9QUk9HUkFNLFxuICAgIGdsLkFSUkFZX0JVRkZFUl9CSU5ESU5HXG4gIF07XG5cbiAgV0dMVVByZXNlcnZlR0xTdGF0ZShnbCwgZ2xTdGF0ZSwgZnVuY3Rpb24oZ2wpIHtcbiAgICAvLyBNYWtlIHN1cmUgdGhlIEdMIHN0YXRlIGlzIGluIGEgZ29vZCBwbGFjZVxuICAgIGdsLmRpc2FibGUoZ2wuQ1VMTF9GQUNFKTtcbiAgICBnbC5kaXNhYmxlKGdsLkRFUFRIX1RFU1QpO1xuICAgIGdsLmRpc2FibGUoZ2wuQkxFTkQpO1xuICAgIGdsLmRpc2FibGUoZ2wuU0NJU1NPUl9URVNUKTtcbiAgICBnbC5kaXNhYmxlKGdsLlNURU5DSUxfVEVTVCk7XG4gICAgZ2wuY29sb3JNYXNrKHRydWUsIHRydWUsIHRydWUsIHRydWUpO1xuICAgIGdsLnZpZXdwb3J0KDAsIDAsIGdsLmRyYXdpbmdCdWZmZXJXaWR0aCwgZ2wuZHJhd2luZ0J1ZmZlckhlaWdodCk7XG5cbiAgICBzZWxmLnJlbmRlck5vU3RhdGUoKTtcbiAgfSk7XG59O1xuXG5DYXJkYm9hcmRVSS5wcm90b3R5cGUucmVuZGVyTm9TdGF0ZSA9IGZ1bmN0aW9uKCkge1xuICB2YXIgZ2wgPSB0aGlzLmdsO1xuXG4gIC8vIEJpbmQgZGlzdG9ydGlvbiBwcm9ncmFtIGFuZCBtZXNoXG4gIGdsLnVzZVByb2dyYW0odGhpcy5wcm9ncmFtKTtcblxuICBnbC5iaW5kQnVmZmVyKGdsLkFSUkFZX0JVRkZFUiwgdGhpcy52ZXJ0ZXhCdWZmZXIpO1xuICBnbC5lbmFibGVWZXJ0ZXhBdHRyaWJBcnJheSh0aGlzLmF0dHJpYnMucG9zaXRpb24pO1xuICBnbC52ZXJ0ZXhBdHRyaWJQb2ludGVyKHRoaXMuYXR0cmlicy5wb3NpdGlvbiwgMiwgZ2wuRkxPQVQsIGZhbHNlLCA4LCAwKTtcblxuICBnbC51bmlmb3JtNGYodGhpcy51bmlmb3Jtcy5jb2xvciwgMS4wLCAxLjAsIDEuMCwgMS4wKTtcblxuICBVdGlsLm9ydGhvTWF0cml4KHRoaXMucHJvak1hdCwgMCwgZ2wuZHJhd2luZ0J1ZmZlcldpZHRoLCAwLCBnbC5kcmF3aW5nQnVmZmVySGVpZ2h0LCAwLjEsIDEwMjQuMCk7XG4gIGdsLnVuaWZvcm1NYXRyaXg0ZnYodGhpcy51bmlmb3Jtcy5wcm9qZWN0aW9uTWF0LCBmYWxzZSwgdGhpcy5wcm9qTWF0KTtcblxuICAvLyBEcmF3cyBVSSBlbGVtZW50XG4gIGdsLmRyYXdBcnJheXMoZ2wuVFJJQU5HTEVfU1RSSVAsIDAsIDQpO1xuICBnbC5kcmF3QXJyYXlzKGdsLlRSSUFOR0xFX1NUUklQLCB0aGlzLmdlYXJPZmZzZXQsIHRoaXMuZ2VhclZlcnRleENvdW50KTtcbiAgZ2wuZHJhd0FycmF5cyhnbC5UUklBTkdMRV9TVFJJUCwgdGhpcy5hcnJvd09mZnNldCwgdGhpcy5hcnJvd1ZlcnRleENvdW50KTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gQ2FyZGJvYXJkVUk7XG4iLCIvKlxuICogQ29weXJpZ2h0IDIwMTYgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqL1xuXG52YXIgQ2FyZGJvYXJkRGlzdG9ydGVyID0gcmVxdWlyZSgnLi9jYXJkYm9hcmQtZGlzdG9ydGVyLmpzJyk7XG52YXIgQ2FyZGJvYXJkVUkgPSByZXF1aXJlKCcuL2NhcmRib2FyZC11aS5qcycpO1xudmFyIERldmljZUluZm8gPSByZXF1aXJlKCcuL2RldmljZS1pbmZvLmpzJyk7XG52YXIgRHBkYiA9IHJlcXVpcmUoJy4vZHBkYi9kcGRiLmpzJyk7XG52YXIgRnVzaW9uUG9zZVNlbnNvciA9IHJlcXVpcmUoJy4vc2Vuc29yLWZ1c2lvbi9mdXNpb24tcG9zZS1zZW5zb3IuanMnKTtcbnZhciBSb3RhdGVJbnN0cnVjdGlvbnMgPSByZXF1aXJlKCcuL3JvdGF0ZS1pbnN0cnVjdGlvbnMuanMnKTtcbnZhciBWaWV3ZXJTZWxlY3RvciA9IHJlcXVpcmUoJy4vdmlld2VyLXNlbGVjdG9yLmpzJyk7XG52YXIgVlJEaXNwbGF5ID0gcmVxdWlyZSgnLi9iYXNlLmpzJykuVlJEaXNwbGF5O1xudmFyIFV0aWwgPSByZXF1aXJlKCcuL3V0aWwuanMnKTtcblxudmFyIEV5ZSA9IHtcbiAgTEVGVDogJ2xlZnQnLFxuICBSSUdIVDogJ3JpZ2h0J1xufTtcblxuLyoqXG4gKiBWUkRpc3BsYXkgYmFzZWQgb24gbW9iaWxlIGRldmljZSBwYXJhbWV0ZXJzIGFuZCBEZXZpY2VNb3Rpb24gQVBJcy5cbiAqL1xuZnVuY3Rpb24gQ2FyZGJvYXJkVlJEaXNwbGF5KCkge1xuICB0aGlzLmRpc3BsYXlOYW1lID0gJ0NhcmRib2FyZCBWUkRpc3BsYXkgKHdlYnZyLXBvbHlmaWxsKSc7XG5cbiAgdGhpcy5jYXBhYmlsaXRpZXMuaGFzT3JpZW50YXRpb24gPSB0cnVlO1xuICB0aGlzLmNhcGFiaWxpdGllcy5jYW5QcmVzZW50ID0gdHJ1ZTtcblxuICAvLyBcIlByaXZhdGVcIiBtZW1iZXJzLlxuICB0aGlzLmJ1ZmZlclNjYWxlXyA9IHdpbmRvdy5XZWJWUkNvbmZpZy5CVUZGRVJfU0NBTEU7XG4gIHRoaXMucG9zZVNlbnNvcl8gPSBuZXcgRnVzaW9uUG9zZVNlbnNvcigpO1xuICB0aGlzLmRpc3RvcnRlcl8gPSBudWxsO1xuICB0aGlzLmNhcmRib2FyZFVJXyA9IG51bGw7XG5cbiAgdGhpcy5kcGRiXyA9IG5ldyBEcGRiKHRydWUsIHRoaXMub25EZXZpY2VQYXJhbXNVcGRhdGVkXy5iaW5kKHRoaXMpKTtcbiAgdGhpcy5kZXZpY2VJbmZvXyA9IG5ldyBEZXZpY2VJbmZvKHRoaXMuZHBkYl8uZ2V0RGV2aWNlUGFyYW1zKCkpO1xuXG4gIHRoaXMudmlld2VyU2VsZWN0b3JfID0gbmV3IFZpZXdlclNlbGVjdG9yKCk7XG4gIHRoaXMudmlld2VyU2VsZWN0b3JfLm9uQ2hhbmdlKHRoaXMub25WaWV3ZXJDaGFuZ2VkXy5iaW5kKHRoaXMpKTtcblxuICAvLyBTZXQgdGhlIGNvcnJlY3QgaW5pdGlhbCB2aWV3ZXIuXG4gIHRoaXMuZGV2aWNlSW5mb18uc2V0Vmlld2VyKHRoaXMudmlld2VyU2VsZWN0b3JfLmdldEN1cnJlbnRWaWV3ZXIoKSk7XG5cbiAgaWYgKCF3aW5kb3cuV2ViVlJDb25maWcuUk9UQVRFX0lOU1RSVUNUSU9OU19ESVNBQkxFRCkge1xuICAgIHRoaXMucm90YXRlSW5zdHJ1Y3Rpb25zXyA9IG5ldyBSb3RhdGVJbnN0cnVjdGlvbnMoKTtcbiAgfVxuXG4gIGlmIChVdGlsLmlzSU9TKCkpIHtcbiAgICAvLyBMaXN0ZW4gZm9yIHJlc2l6ZSBldmVudHMgdG8gd29ya2Fyb3VuZCB0aGlzIGF3ZnVsIFNhZmFyaSBidWcuXG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsIHRoaXMub25SZXNpemVfLmJpbmQodGhpcykpO1xuICB9XG59XG5DYXJkYm9hcmRWUkRpc3BsYXkucHJvdG90eXBlID0gbmV3IFZSRGlzcGxheSgpO1xuXG5DYXJkYm9hcmRWUkRpc3BsYXkucHJvdG90eXBlLmdldEltbWVkaWF0ZVBvc2UgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHtcbiAgICBwb3NpdGlvbjogdGhpcy5wb3NlU2Vuc29yXy5nZXRQb3NpdGlvbigpLFxuICAgIG9yaWVudGF0aW9uOiB0aGlzLnBvc2VTZW5zb3JfLmdldE9yaWVudGF0aW9uKCksXG4gICAgbGluZWFyVmVsb2NpdHk6IG51bGwsXG4gICAgbGluZWFyQWNjZWxlcmF0aW9uOiBudWxsLFxuICAgIGFuZ3VsYXJWZWxvY2l0eTogbnVsbCxcbiAgICBhbmd1bGFyQWNjZWxlcmF0aW9uOiBudWxsXG4gIH07XG59O1xuXG5DYXJkYm9hcmRWUkRpc3BsYXkucHJvdG90eXBlLnJlc2V0UG9zZSA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLnBvc2VTZW5zb3JfLnJlc2V0UG9zZSgpO1xufTtcblxuQ2FyZGJvYXJkVlJEaXNwbGF5LnByb3RvdHlwZS5nZXRFeWVQYXJhbWV0ZXJzID0gZnVuY3Rpb24od2hpY2hFeWUpIHtcbiAgdmFyIG9mZnNldCA9IFt0aGlzLmRldmljZUluZm9fLnZpZXdlci5pbnRlckxlbnNEaXN0YW5jZSAqIDAuNSwgMC4wLCAwLjBdO1xuICB2YXIgZmllbGRPZlZpZXc7XG5cbiAgLy8gVE9ETzogRm9WIGNhbiBiZSBhIGxpdHRsZSBleHBlbnNpdmUgdG8gY29tcHV0ZS4gQ2FjaGUgd2hlbiBkZXZpY2UgcGFyYW1zIGNoYW5nZS5cbiAgaWYgKHdoaWNoRXllID09IEV5ZS5MRUZUKSB7XG4gICAgb2Zmc2V0WzBdICo9IC0xLjA7XG4gICAgZmllbGRPZlZpZXcgPSB0aGlzLmRldmljZUluZm9fLmdldEZpZWxkT2ZWaWV3TGVmdEV5ZSgpO1xuICB9IGVsc2UgaWYgKHdoaWNoRXllID09IEV5ZS5SSUdIVCkge1xuICAgIGZpZWxkT2ZWaWV3ID0gdGhpcy5kZXZpY2VJbmZvXy5nZXRGaWVsZE9mVmlld1JpZ2h0RXllKCk7XG4gIH0gZWxzZSB7XG4gICAgY29uc29sZS5lcnJvcignSW52YWxpZCBleWUgcHJvdmlkZWQ6ICVzJywgd2hpY2hFeWUpO1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBmaWVsZE9mVmlldzogZmllbGRPZlZpZXcsXG4gICAgb2Zmc2V0OiBvZmZzZXQsXG4gICAgLy8gVE9ETzogU2hvdWxkIGJlIGFibGUgdG8gcHJvdmlkZSBiZXR0ZXIgdmFsdWVzIHRoYW4gdGhlc2UuXG4gICAgcmVuZGVyV2lkdGg6IHRoaXMuZGV2aWNlSW5mb18uZGV2aWNlLndpZHRoICogMC41ICogdGhpcy5idWZmZXJTY2FsZV8sXG4gICAgcmVuZGVySGVpZ2h0OiB0aGlzLmRldmljZUluZm9fLmRldmljZS5oZWlnaHQgKiB0aGlzLmJ1ZmZlclNjYWxlXyxcbiAgfTtcbn07XG5cbkNhcmRib2FyZFZSRGlzcGxheS5wcm90b3R5cGUub25EZXZpY2VQYXJhbXNVcGRhdGVkXyA9IGZ1bmN0aW9uKG5ld1BhcmFtcykge1xuICBpZiAoVXRpbC5pc0RlYnVnKCkpIHtcbiAgICBjb25zb2xlLmxvZygnRFBEQiByZXBvcnRlZCB0aGF0IGRldmljZSBwYXJhbXMgd2VyZSB1cGRhdGVkLicpO1xuICB9XG4gIHRoaXMuZGV2aWNlSW5mb18udXBkYXRlRGV2aWNlUGFyYW1zKG5ld1BhcmFtcyk7XG5cbiAgaWYgKHRoaXMuZGlzdG9ydGVyXykge1xuICAgIHRoaXMuZGlzdG9ydGVyXy51cGRhdGVEZXZpY2VJbmZvKHRoaXMuZGV2aWNlSW5mb18pO1xuICB9XG59O1xuXG5DYXJkYm9hcmRWUkRpc3BsYXkucHJvdG90eXBlLnVwZGF0ZUJvdW5kc18gPSBmdW5jdGlvbiAoKSB7XG4gIGlmICh0aGlzLmxheWVyXyAmJiB0aGlzLmRpc3RvcnRlcl8gJiYgKHRoaXMubGF5ZXJfLmxlZnRCb3VuZHMgfHwgdGhpcy5sYXllcl8ucmlnaHRCb3VuZHMpKSB7XG4gICAgdGhpcy5kaXN0b3J0ZXJfLnNldFRleHR1cmVCb3VuZHModGhpcy5sYXllcl8ubGVmdEJvdW5kcywgdGhpcy5sYXllcl8ucmlnaHRCb3VuZHMpO1xuICB9XG59O1xuXG5DYXJkYm9hcmRWUkRpc3BsYXkucHJvdG90eXBlLmJlZ2luUHJlc2VudF8gPSBmdW5jdGlvbigpIHtcbiAgdmFyIGdsID0gdGhpcy5sYXllcl8uc291cmNlLmdldENvbnRleHQoJ3dlYmdsJyk7XG4gIGlmICghZ2wpXG4gICAgZ2wgPSB0aGlzLmxheWVyXy5zb3VyY2UuZ2V0Q29udGV4dCgnZXhwZXJpbWVudGFsLXdlYmdsJyk7XG4gIGlmICghZ2wpXG4gICAgZ2wgPSB0aGlzLmxheWVyXy5zb3VyY2UuZ2V0Q29udGV4dCgnd2ViZ2wyJyk7XG5cbiAgaWYgKCFnbClcbiAgICByZXR1cm47IC8vIENhbid0IGRvIGRpc3RvcnRpb24gd2l0aG91dCBhIFdlYkdMIGNvbnRleHQuXG5cbiAgLy8gUHJvdmlkZXMgYSB3YXkgdG8gb3B0IG91dCBvZiBkaXN0b3J0aW9uXG4gIGlmICh0aGlzLmxheWVyXy5wcmVkaXN0b3J0ZWQpIHtcbiAgICBpZiAoIXdpbmRvdy5XZWJWUkNvbmZpZy5DQVJEQk9BUkRfVUlfRElTQUJMRUQpIHtcbiAgICAgIGdsLmNhbnZhcy53aWR0aCA9IFV0aWwuZ2V0U2NyZWVuV2lkdGgoKSAqIHRoaXMuYnVmZmVyU2NhbGVfO1xuICAgICAgZ2wuY2FudmFzLmhlaWdodCA9IFV0aWwuZ2V0U2NyZWVuSGVpZ2h0KCkgKiB0aGlzLmJ1ZmZlclNjYWxlXztcbiAgICAgIHRoaXMuY2FyZGJvYXJkVUlfID0gbmV3IENhcmRib2FyZFVJKGdsKTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgLy8gQ3JlYXRlIGEgbmV3IGRpc3RvcnRlciBmb3IgdGhlIHRhcmdldCBjb250ZXh0XG4gICAgdGhpcy5kaXN0b3J0ZXJfID0gbmV3IENhcmRib2FyZERpc3RvcnRlcihnbCk7XG4gICAgdGhpcy5kaXN0b3J0ZXJfLnVwZGF0ZURldmljZUluZm8odGhpcy5kZXZpY2VJbmZvXyk7XG4gICAgdGhpcy5jYXJkYm9hcmRVSV8gPSB0aGlzLmRpc3RvcnRlcl8uY2FyZGJvYXJkVUk7XG4gIH1cblxuICBpZiAodGhpcy5jYXJkYm9hcmRVSV8pIHtcbiAgICB0aGlzLmNhcmRib2FyZFVJXy5saXN0ZW4oZnVuY3Rpb24oZSkge1xuICAgICAgLy8gT3B0aW9ucyBjbGlja2VkLlxuICAgICAgdGhpcy52aWV3ZXJTZWxlY3Rvcl8uc2hvdyh0aGlzLmxheWVyXy5zb3VyY2UucGFyZW50RWxlbWVudCk7XG4gICAgICBlLnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIH0uYmluZCh0aGlzKSwgZnVuY3Rpb24oZSkge1xuICAgICAgLy8gQmFjayBjbGlja2VkLlxuICAgICAgdGhpcy5leGl0UHJlc2VudCgpO1xuICAgICAgZS5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICB9LmJpbmQodGhpcykpO1xuICB9XG5cbiAgaWYgKHRoaXMucm90YXRlSW5zdHJ1Y3Rpb25zXykge1xuICAgIGlmIChVdGlsLmlzTGFuZHNjYXBlTW9kZSgpICYmIFV0aWwuaXNNb2JpbGUoKSkge1xuICAgICAgLy8gSW4gbGFuZHNjYXBlIG1vZGUsIHRlbXBvcmFyaWx5IHNob3cgdGhlIFwicHV0IGludG8gQ2FyZGJvYXJkXCJcbiAgICAgIC8vIGludGVyc3RpdGlhbC4gT3RoZXJ3aXNlLCBkbyB0aGUgZGVmYXVsdCB0aGluZy5cbiAgICAgIHRoaXMucm90YXRlSW5zdHJ1Y3Rpb25zXy5zaG93VGVtcG9yYXJpbHkoMzAwMCwgdGhpcy5sYXllcl8uc291cmNlLnBhcmVudEVsZW1lbnQpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnJvdGF0ZUluc3RydWN0aW9uc18udXBkYXRlKCk7XG4gICAgfVxuICB9XG5cbiAgLy8gTGlzdGVuIGZvciBvcmllbnRhdGlvbiBjaGFuZ2UgZXZlbnRzIGluIG9yZGVyIHRvIHNob3cgaW50ZXJzdGl0aWFsLlxuICB0aGlzLm9yaWVudGF0aW9uSGFuZGxlciA9IHRoaXMub25PcmllbnRhdGlvbkNoYW5nZV8uYmluZCh0aGlzKTtcbiAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ29yaWVudGF0aW9uY2hhbmdlJywgdGhpcy5vcmllbnRhdGlvbkhhbmRsZXIpO1xuXG4gIC8vIExpc3RlbiBmb3IgcHJlc2VudCBkaXNwbGF5IGNoYW5nZSBldmVudHMgaW4gb3JkZXIgdG8gdXBkYXRlIGRpc3RvcnRlciBkaW1lbnNpb25zXG4gIHRoaXMudnJkaXNwbGF5cHJlc2VudGNoYW5nZUhhbmRsZXIgPSB0aGlzLnVwZGF0ZUJvdW5kc18uYmluZCh0aGlzKTtcbiAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3ZyZGlzcGxheXByZXNlbnRjaGFuZ2UnLCB0aGlzLnZyZGlzcGxheXByZXNlbnRjaGFuZ2VIYW5kbGVyKTtcblxuICAvLyBGaXJlIHRoaXMgZXZlbnQgaW5pdGlhbGx5LCB0byBnaXZlIGdlb21ldHJ5LWRpc3RvcnRpb24gY2xpZW50cyB0aGUgY2hhbmNlXG4gIC8vIHRvIGRvIHNvbWV0aGluZyBjdXN0b20uXG4gIHRoaXMuZmlyZVZSRGlzcGxheURldmljZVBhcmFtc0NoYW5nZV8oKTtcbn07XG5cbkNhcmRib2FyZFZSRGlzcGxheS5wcm90b3R5cGUuZW5kUHJlc2VudF8gPSBmdW5jdGlvbigpIHtcbiAgaWYgKHRoaXMuZGlzdG9ydGVyXykge1xuICAgIHRoaXMuZGlzdG9ydGVyXy5kZXN0cm95KCk7XG4gICAgdGhpcy5kaXN0b3J0ZXJfID0gbnVsbDtcbiAgfVxuICBpZiAodGhpcy5jYXJkYm9hcmRVSV8pIHtcbiAgICB0aGlzLmNhcmRib2FyZFVJXy5kZXN0cm95KCk7XG4gICAgdGhpcy5jYXJkYm9hcmRVSV8gPSBudWxsO1xuICB9XG5cbiAgaWYgKHRoaXMucm90YXRlSW5zdHJ1Y3Rpb25zXykge1xuICAgIHRoaXMucm90YXRlSW5zdHJ1Y3Rpb25zXy5oaWRlKCk7XG4gIH1cbiAgdGhpcy52aWV3ZXJTZWxlY3Rvcl8uaGlkZSgpO1xuXG4gIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCdvcmllbnRhdGlvbmNoYW5nZScsIHRoaXMub3JpZW50YXRpb25IYW5kbGVyKTtcbiAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3ZyZGlzcGxheXByZXNlbnRjaGFuZ2UnLCB0aGlzLnZyZGlzcGxheXByZXNlbnRjaGFuZ2VIYW5kbGVyKTtcbn07XG5cbkNhcmRib2FyZFZSRGlzcGxheS5wcm90b3R5cGUuc3VibWl0RnJhbWUgPSBmdW5jdGlvbihwb3NlKSB7XG4gIGlmICh0aGlzLmRpc3RvcnRlcl8pIHtcbiAgICB0aGlzLnVwZGF0ZUJvdW5kc18oKTtcbiAgICB0aGlzLmRpc3RvcnRlcl8uc3VibWl0RnJhbWUoKTtcbiAgfSBlbHNlIGlmICh0aGlzLmNhcmRib2FyZFVJXyAmJiB0aGlzLmxheWVyXykge1xuICAgIC8vIEhhY2sgZm9yIHByZWRpc3RvcnRlZDogdHJ1ZS5cbiAgICB2YXIgY2FudmFzID0gdGhpcy5sYXllcl8uc291cmNlLmdldENvbnRleHQoJ3dlYmdsJykuY2FudmFzO1xuICAgIGlmIChjYW52YXMud2lkdGggIT0gdGhpcy5sYXN0V2lkdGggfHwgY2FudmFzLmhlaWdodCAhPSB0aGlzLmxhc3RIZWlnaHQpIHtcbiAgICAgIHRoaXMuY2FyZGJvYXJkVUlfLm9uUmVzaXplKCk7XG4gICAgfVxuICAgIHRoaXMubGFzdFdpZHRoID0gY2FudmFzLndpZHRoO1xuICAgIHRoaXMubGFzdEhlaWdodCA9IGNhbnZhcy5oZWlnaHQ7XG5cbiAgICAvLyBSZW5kZXIgdGhlIENhcmRib2FyZCBVSS5cbiAgICB0aGlzLmNhcmRib2FyZFVJXy5yZW5kZXIoKTtcbiAgfVxufTtcblxuQ2FyZGJvYXJkVlJEaXNwbGF5LnByb3RvdHlwZS5vbk9yaWVudGF0aW9uQ2hhbmdlXyA9IGZ1bmN0aW9uKGUpIHtcbiAgLy8gSGlkZSB0aGUgdmlld2VyIHNlbGVjdG9yLlxuICB0aGlzLnZpZXdlclNlbGVjdG9yXy5oaWRlKCk7XG5cbiAgLy8gVXBkYXRlIHRoZSByb3RhdGUgaW5zdHJ1Y3Rpb25zLlxuICBpZiAodGhpcy5yb3RhdGVJbnN0cnVjdGlvbnNfKSB7XG4gICAgdGhpcy5yb3RhdGVJbnN0cnVjdGlvbnNfLnVwZGF0ZSgpO1xuICB9XG5cbiAgdGhpcy5vblJlc2l6ZV8oKTtcbn07XG5cbkNhcmRib2FyZFZSRGlzcGxheS5wcm90b3R5cGUub25SZXNpemVfID0gZnVuY3Rpb24oZSkge1xuICBpZiAodGhpcy5sYXllcl8pIHtcbiAgICB2YXIgZ2wgPSB0aGlzLmxheWVyXy5zb3VyY2UuZ2V0Q29udGV4dCgnd2ViZ2wnKTtcbiAgICAvLyBTaXplIHRoZSBDU1MgY2FudmFzLlxuICAgIC8vIEFkZGVkIHBhZGRpbmcgb24gcmlnaHQgYW5kIGJvdHRvbSBiZWNhdXNlIGlQaG9uZSA1IHdpbGwgbm90XG4gICAgLy8gaGlkZSB0aGUgVVJMIGJhciB1bmxlc3MgY29udGVudCBpcyBiaWdnZXIgdGhhbiB0aGUgc2NyZWVuLlxuICAgIC8vIFRoaXMgd2lsbCBub3QgYmUgdmlzaWJsZSBhcyBsb25nIGFzIHRoZSBjb250YWluZXIgZWxlbWVudCAoZS5nLiBib2R5KVxuICAgIC8vIGlzIHNldCB0byAnb3ZlcmZsb3c6IGhpZGRlbicuXG4gICAgLy8gQWRkaXRpb25hbGx5LCAnYm94LXNpemluZzogY29udGVudC1ib3gnIGVuc3VyZXMgcmVuZGVyV2lkdGggPSB3aWR0aCArIHBhZGRpbmcuXG4gICAgLy8gVGhpcyBpcyByZXF1aXJlZCB3aGVuICdib3gtc2l6aW5nOiBib3JkZXItYm94JyBpcyB1c2VkIGVsc2V3aGVyZSBpbiB0aGUgcGFnZS5cbiAgICB2YXIgY3NzUHJvcGVydGllcyA9IFtcbiAgICAgICdwb3NpdGlvbjogYWJzb2x1dGUnLFxuICAgICAgJ3RvcDogMCcsXG4gICAgICAnbGVmdDogMCcsXG4gICAgICAnd2lkdGg6ICcgKyBNYXRoLm1heChzY3JlZW4ud2lkdGgsIHNjcmVlbi5oZWlnaHQpICsgJ3B4JyxcbiAgICAgICdoZWlnaHQ6ICcgKyBNYXRoLm1pbihzY3JlZW4uaGVpZ2h0LCBzY3JlZW4ud2lkdGgpICsgJ3B4JyxcbiAgICAgICdib3JkZXI6IDAnLFxuICAgICAgJ21hcmdpbjogMCcsXG4gICAgICAncGFkZGluZzogMCAxMHB4IDEwcHggMCcsXG4gICAgICAnYm94LXNpemluZzogY29udGVudC1ib3gnLFxuICAgIF07XG4gICAgZ2wuY2FudmFzLnNldEF0dHJpYnV0ZSgnc3R5bGUnLCBjc3NQcm9wZXJ0aWVzLmpvaW4oJzsgJykgKyAnOycpO1xuXG4gICAgVXRpbC5zYWZhcmlDc3NTaXplV29ya2Fyb3VuZChnbC5jYW52YXMpO1xuICB9XG59O1xuXG5DYXJkYm9hcmRWUkRpc3BsYXkucHJvdG90eXBlLm9uVmlld2VyQ2hhbmdlZF8gPSBmdW5jdGlvbih2aWV3ZXIpIHtcbiAgdGhpcy5kZXZpY2VJbmZvXy5zZXRWaWV3ZXIodmlld2VyKTtcblxuICBpZiAodGhpcy5kaXN0b3J0ZXJfKSB7XG4gICAgLy8gVXBkYXRlIHRoZSBkaXN0b3J0aW9uIGFwcHJvcHJpYXRlbHkuXG4gICAgdGhpcy5kaXN0b3J0ZXJfLnVwZGF0ZURldmljZUluZm8odGhpcy5kZXZpY2VJbmZvXyk7XG4gIH1cblxuICAvLyBGaXJlIGEgbmV3IGV2ZW50IGNvbnRhaW5pbmcgdmlld2VyIGFuZCBkZXZpY2UgcGFyYW1ldGVycyBmb3IgY2xpZW50cyB0aGF0XG4gIC8vIHdhbnQgdG8gaW1wbGVtZW50IHRoZWlyIG93biBnZW9tZXRyeS1iYXNlZCBkaXN0b3J0aW9uLlxuICB0aGlzLmZpcmVWUkRpc3BsYXlEZXZpY2VQYXJhbXNDaGFuZ2VfKCk7XG59O1xuXG5DYXJkYm9hcmRWUkRpc3BsYXkucHJvdG90eXBlLmZpcmVWUkRpc3BsYXlEZXZpY2VQYXJhbXNDaGFuZ2VfID0gZnVuY3Rpb24oKSB7XG4gIHZhciBldmVudCA9IG5ldyBDdXN0b21FdmVudCgndnJkaXNwbGF5ZGV2aWNlcGFyYW1zY2hhbmdlJywge1xuICAgIGRldGFpbDoge1xuICAgICAgdnJkaXNwbGF5OiB0aGlzLFxuICAgICAgZGV2aWNlSW5mbzogdGhpcy5kZXZpY2VJbmZvXyxcbiAgICB9XG4gIH0pO1xuICB3aW5kb3cuZGlzcGF0Y2hFdmVudChldmVudCk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IENhcmRib2FyZFZSRGlzcGxheTtcbiIsIi8qKlxuICogQ29weXJpZ2h0IChjKSAyMDE2LCBCcmFuZG9uIEpvbmVzLlxuICogaHR0cHM6Ly9naXRodWIuY29tL3Rvamkvd2ViZ2wtdXRpbHMvYmxvYi9tYXN0ZXIvc3JjL3dnbHUtcHJlc2VydmUtc3RhdGUuanNcbiAqIExJQ0VOU0U6IGh0dHBzOi8vZ2l0aHViLmNvbS90b2ppL3dlYmdsLXV0aWxzL2Jsb2IvbWFzdGVyL0xJQ0VOU0UubWRcbiAqL1xuXG5mdW5jdGlvbiBXR0xVUHJlc2VydmVHTFN0YXRlKGdsLCBiaW5kaW5ncywgY2FsbGJhY2spIHtcbiAgaWYgKCFiaW5kaW5ncykge1xuICAgIGNhbGxiYWNrKGdsKTtcbiAgICByZXR1cm47XG4gIH1cblxuICB2YXIgYm91bmRWYWx1ZXMgPSBbXTtcblxuICB2YXIgYWN0aXZlVGV4dHVyZSA9IG51bGw7XG4gIGZvciAodmFyIGkgPSAwOyBpIDwgYmluZGluZ3MubGVuZ3RoOyArK2kpIHtcbiAgICB2YXIgYmluZGluZyA9IGJpbmRpbmdzW2ldO1xuICAgIHN3aXRjaCAoYmluZGluZykge1xuICAgICAgY2FzZSBnbC5URVhUVVJFX0JJTkRJTkdfMkQ6XG4gICAgICBjYXNlIGdsLlRFWFRVUkVfQklORElOR19DVUJFX01BUDpcbiAgICAgICAgdmFyIHRleHR1cmVVbml0ID0gYmluZGluZ3NbKytpXTtcbiAgICAgICAgaWYgKHRleHR1cmVVbml0IDwgZ2wuVEVYVFVSRTAgfHwgdGV4dHVyZVVuaXQgPiBnbC5URVhUVVJFMzEpIHtcbiAgICAgICAgICBjb25zb2xlLmVycm9yKFwiVEVYVFVSRV9CSU5ESU5HXzJEIG9yIFRFWFRVUkVfQklORElOR19DVUJFX01BUCBtdXN0IGJlIGZvbGxvd2VkIGJ5IGEgdmFsaWQgdGV4dHVyZSB1bml0XCIpO1xuICAgICAgICAgIGJvdW5kVmFsdWVzLnB1c2gobnVsbCwgbnVsbCk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFhY3RpdmVUZXh0dXJlKSB7XG4gICAgICAgICAgYWN0aXZlVGV4dHVyZSA9IGdsLmdldFBhcmFtZXRlcihnbC5BQ1RJVkVfVEVYVFVSRSk7XG4gICAgICAgIH1cbiAgICAgICAgZ2wuYWN0aXZlVGV4dHVyZSh0ZXh0dXJlVW5pdCk7XG4gICAgICAgIGJvdW5kVmFsdWVzLnB1c2goZ2wuZ2V0UGFyYW1ldGVyKGJpbmRpbmcpLCBudWxsKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIGdsLkFDVElWRV9URVhUVVJFOlxuICAgICAgICBhY3RpdmVUZXh0dXJlID0gZ2wuZ2V0UGFyYW1ldGVyKGdsLkFDVElWRV9URVhUVVJFKTtcbiAgICAgICAgYm91bmRWYWx1ZXMucHVzaChudWxsKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICBib3VuZFZhbHVlcy5wdXNoKGdsLmdldFBhcmFtZXRlcihiaW5kaW5nKSk7XG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxuXG4gIGNhbGxiYWNrKGdsKTtcblxuICBmb3IgKHZhciBpID0gMDsgaSA8IGJpbmRpbmdzLmxlbmd0aDsgKytpKSB7XG4gICAgdmFyIGJpbmRpbmcgPSBiaW5kaW5nc1tpXTtcbiAgICB2YXIgYm91bmRWYWx1ZSA9IGJvdW5kVmFsdWVzW2ldO1xuICAgIHN3aXRjaCAoYmluZGluZykge1xuICAgICAgY2FzZSBnbC5BQ1RJVkVfVEVYVFVSRTpcbiAgICAgICAgYnJlYWs7IC8vIElnbm9yZSB0aGlzIGJpbmRpbmcsIHNpbmNlIHdlIHNwZWNpYWwtY2FzZSBpdCB0byBoYXBwZW4gbGFzdC5cbiAgICAgIGNhc2UgZ2wuQVJSQVlfQlVGRkVSX0JJTkRJTkc6XG4gICAgICAgIGdsLmJpbmRCdWZmZXIoZ2wuQVJSQVlfQlVGRkVSLCBib3VuZFZhbHVlKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIGdsLkNPTE9SX0NMRUFSX1ZBTFVFOlxuICAgICAgICBnbC5jbGVhckNvbG9yKGJvdW5kVmFsdWVbMF0sIGJvdW5kVmFsdWVbMV0sIGJvdW5kVmFsdWVbMl0sIGJvdW5kVmFsdWVbM10pO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgZ2wuQ09MT1JfV1JJVEVNQVNLOlxuICAgICAgICBnbC5jb2xvck1hc2soYm91bmRWYWx1ZVswXSwgYm91bmRWYWx1ZVsxXSwgYm91bmRWYWx1ZVsyXSwgYm91bmRWYWx1ZVszXSk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSBnbC5DVVJSRU5UX1BST0dSQU06XG4gICAgICAgIGdsLnVzZVByb2dyYW0oYm91bmRWYWx1ZSk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSBnbC5FTEVNRU5UX0FSUkFZX0JVRkZFUl9CSU5ESU5HOlxuICAgICAgICBnbC5iaW5kQnVmZmVyKGdsLkVMRU1FTlRfQVJSQVlfQlVGRkVSLCBib3VuZFZhbHVlKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIGdsLkZSQU1FQlVGRkVSX0JJTkRJTkc6XG4gICAgICAgIGdsLmJpbmRGcmFtZWJ1ZmZlcihnbC5GUkFNRUJVRkZFUiwgYm91bmRWYWx1ZSk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSBnbC5SRU5ERVJCVUZGRVJfQklORElORzpcbiAgICAgICAgZ2wuYmluZFJlbmRlcmJ1ZmZlcihnbC5SRU5ERVJCVUZGRVIsIGJvdW5kVmFsdWUpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgZ2wuVEVYVFVSRV9CSU5ESU5HXzJEOlxuICAgICAgICB2YXIgdGV4dHVyZVVuaXQgPSBiaW5kaW5nc1srK2ldO1xuICAgICAgICBpZiAodGV4dHVyZVVuaXQgPCBnbC5URVhUVVJFMCB8fCB0ZXh0dXJlVW5pdCA+IGdsLlRFWFRVUkUzMSlcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgZ2wuYWN0aXZlVGV4dHVyZSh0ZXh0dXJlVW5pdCk7XG4gICAgICAgIGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIGJvdW5kVmFsdWUpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgZ2wuVEVYVFVSRV9CSU5ESU5HX0NVQkVfTUFQOlxuICAgICAgICB2YXIgdGV4dHVyZVVuaXQgPSBiaW5kaW5nc1srK2ldO1xuICAgICAgICBpZiAodGV4dHVyZVVuaXQgPCBnbC5URVhUVVJFMCB8fCB0ZXh0dXJlVW5pdCA+IGdsLlRFWFRVUkUzMSlcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgZ2wuYWN0aXZlVGV4dHVyZSh0ZXh0dXJlVW5pdCk7XG4gICAgICAgIGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfQ1VCRV9NQVAsIGJvdW5kVmFsdWUpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgZ2wuVklFV1BPUlQ6XG4gICAgICAgIGdsLnZpZXdwb3J0KGJvdW5kVmFsdWVbMF0sIGJvdW5kVmFsdWVbMV0sIGJvdW5kVmFsdWVbMl0sIGJvdW5kVmFsdWVbM10pO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgZ2wuQkxFTkQ6XG4gICAgICBjYXNlIGdsLkNVTExfRkFDRTpcbiAgICAgIGNhc2UgZ2wuREVQVEhfVEVTVDpcbiAgICAgIGNhc2UgZ2wuU0NJU1NPUl9URVNUOlxuICAgICAgY2FzZSBnbC5TVEVOQ0lMX1RFU1Q6XG4gICAgICAgIGlmIChib3VuZFZhbHVlKSB7XG4gICAgICAgICAgZ2wuZW5hYmxlKGJpbmRpbmcpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdsLmRpc2FibGUoYmluZGluZyk7XG4gICAgICAgIH1cbiAgICAgICAgYnJlYWs7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICBjb25zb2xlLmxvZyhcIk5vIEdMIHJlc3RvcmUgYmVoYXZpb3IgZm9yIDB4XCIgKyBiaW5kaW5nLnRvU3RyaW5nKDE2KSk7XG4gICAgICAgIGJyZWFrO1xuICAgIH1cblxuICAgIGlmIChhY3RpdmVUZXh0dXJlKSB7XG4gICAgICBnbC5hY3RpdmVUZXh0dXJlKGFjdGl2ZVRleHR1cmUpO1xuICAgIH1cbiAgfVxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IFdHTFVQcmVzZXJ2ZUdMU3RhdGU7XG4iLCIvKlxuICogQ29weXJpZ2h0IDIwMTUgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqL1xuXG52YXIgRGlzdG9ydGlvbiA9IHJlcXVpcmUoJy4vZGlzdG9ydGlvbi9kaXN0b3J0aW9uLmpzJyk7XG52YXIgTWF0aFV0aWwgPSByZXF1aXJlKCcuL21hdGgtdXRpbC5qcycpO1xudmFyIFV0aWwgPSByZXF1aXJlKCcuL3V0aWwuanMnKTtcblxuZnVuY3Rpb24gRGV2aWNlKHBhcmFtcykge1xuICB0aGlzLndpZHRoID0gcGFyYW1zLndpZHRoIHx8IFV0aWwuZ2V0U2NyZWVuV2lkdGgoKTtcbiAgdGhpcy5oZWlnaHQgPSBwYXJhbXMuaGVpZ2h0IHx8IFV0aWwuZ2V0U2NyZWVuSGVpZ2h0KCk7XG4gIHRoaXMud2lkdGhNZXRlcnMgPSBwYXJhbXMud2lkdGhNZXRlcnM7XG4gIHRoaXMuaGVpZ2h0TWV0ZXJzID0gcGFyYW1zLmhlaWdodE1ldGVycztcbiAgdGhpcy5iZXZlbE1ldGVycyA9IHBhcmFtcy5iZXZlbE1ldGVycztcbn1cblxuXG4vLyBGYWxsYmFjayBBbmRyb2lkIGRldmljZSAoYmFzZWQgb24gTmV4dXMgNSBtZWFzdXJlbWVudHMpIGZvciB1c2Ugd2hlblxuLy8gd2UgY2FuJ3QgcmVjb2duaXplIGFuIEFuZHJvaWQgZGV2aWNlLlxudmFyIERFRkFVTFRfQU5EUk9JRCA9IG5ldyBEZXZpY2Uoe1xuICB3aWR0aE1ldGVyczogMC4xMTAsXG4gIGhlaWdodE1ldGVyczogMC4wNjIsXG4gIGJldmVsTWV0ZXJzOiAwLjAwNFxufSk7XG5cbi8vIEZhbGxiYWNrIGlPUyBkZXZpY2UgKGJhc2VkIG9uIGlQaG9uZTYpIGZvciB1c2Ugd2hlblxuLy8gd2UgY2FuJ3QgcmVjb2duaXplIGFuIEFuZHJvaWQgZGV2aWNlLlxudmFyIERFRkFVTFRfSU9TID0gbmV3IERldmljZSh7XG4gIHdpZHRoTWV0ZXJzOiAwLjEwMzgsXG4gIGhlaWdodE1ldGVyczogMC4wNTg0LFxuICBiZXZlbE1ldGVyczogMC4wMDRcbn0pO1xuXG5cbnZhciBWaWV3ZXJzID0ge1xuICBDYXJkYm9hcmRWMTogbmV3IENhcmRib2FyZFZpZXdlcih7XG4gICAgaWQ6ICdDYXJkYm9hcmRWMScsXG4gICAgbGFiZWw6ICdDYXJkYm9hcmQgSS9PIDIwMTQnLFxuICAgIGZvdjogNDAsXG4gICAgaW50ZXJMZW5zRGlzdGFuY2U6IDAuMDYwLFxuICAgIGJhc2VsaW5lTGVuc0Rpc3RhbmNlOiAwLjAzNSxcbiAgICBzY3JlZW5MZW5zRGlzdGFuY2U6IDAuMDQyLFxuICAgIGRpc3RvcnRpb25Db2VmZmljaWVudHM6IFswLjQ0MSwgMC4xNTZdLFxuICAgIGludmVyc2VDb2VmZmljaWVudHM6IFstMC40NDEwMDM1LCAwLjQyNzU2MTU1LCAtMC40ODA0NDM5LCAwLjU0NjAxMzksXG4gICAgICAtMC41ODgyMTE4MywgMC41NzMzOTM4LCAtMC40ODMwMzIwMiwgMC4zMzI5OTA4MywgLTAuMTc1NzM4NDEsXG4gICAgICAwLjA2NTE3NzIsIC0wLjAxNDg4OTYzLCAwLjAwMTU1OTgzNF1cbiAgfSksXG4gIENhcmRib2FyZFYyOiBuZXcgQ2FyZGJvYXJkVmlld2VyKHtcbiAgICBpZDogJ0NhcmRib2FyZFYyJyxcbiAgICBsYWJlbDogJ0NhcmRib2FyZCBJL08gMjAxNScsXG4gICAgZm92OiA2MCxcbiAgICBpbnRlckxlbnNEaXN0YW5jZTogMC4wNjQsXG4gICAgYmFzZWxpbmVMZW5zRGlzdGFuY2U6IDAuMDM1LFxuICAgIHNjcmVlbkxlbnNEaXN0YW5jZTogMC4wMzksXG4gICAgZGlzdG9ydGlvbkNvZWZmaWNpZW50czogWzAuMzQsIDAuNTVdLFxuICAgIGludmVyc2VDb2VmZmljaWVudHM6IFstMC4zMzgzNjcwNCwgLTAuMTgxNjIxODUsIDAuODYyNjU1LCAtMS4yNDYyMDUxLFxuICAgICAgMS4wNTYwNjAyLCAtMC41ODIwODMxNywgMC4yMTYwOTA3OCwgLTAuMDU0NDQ4MjMsIDAuMDA5MTc3OTU2LFxuICAgICAgLTkuOTA0MTY5RS00LCA2LjE4MzUzNUUtNSwgLTEuNjk4MTgwM0UtNl1cbiAgfSlcbn07XG5cblxudmFyIERFRkFVTFRfTEVGVF9DRU5URVIgPSB7eDogMC41LCB5OiAwLjV9O1xudmFyIERFRkFVTFRfUklHSFRfQ0VOVEVSID0ge3g6IDAuNSwgeTogMC41fTtcblxuLyoqXG4gKiBNYW5hZ2VzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBkZXZpY2UgYW5kIHRoZSB2aWV3ZXIuXG4gKlxuICogZGV2aWNlUGFyYW1zIGluZGljYXRlcyB0aGUgcGFyYW1ldGVycyBvZiB0aGUgZGV2aWNlIHRvIHVzZSAoZ2VuZXJhbGx5XG4gKiBvYnRhaW5lZCBmcm9tIGRwZGIuZ2V0RGV2aWNlUGFyYW1zKCkpLiBDYW4gYmUgbnVsbCB0byBtZWFuIG5vIGRldmljZVxuICogcGFyYW1zIHdlcmUgZm91bmQuXG4gKi9cbmZ1bmN0aW9uIERldmljZUluZm8oZGV2aWNlUGFyYW1zKSB7XG4gIHRoaXMudmlld2VyID0gVmlld2Vycy5DYXJkYm9hcmRWMjtcbiAgdGhpcy51cGRhdGVEZXZpY2VQYXJhbXMoZGV2aWNlUGFyYW1zKTtcbiAgdGhpcy5kaXN0b3J0aW9uID0gbmV3IERpc3RvcnRpb24odGhpcy52aWV3ZXIuZGlzdG9ydGlvbkNvZWZmaWNpZW50cyk7XG59XG5cbkRldmljZUluZm8ucHJvdG90eXBlLnVwZGF0ZURldmljZVBhcmFtcyA9IGZ1bmN0aW9uKGRldmljZVBhcmFtcykge1xuICB0aGlzLmRldmljZSA9IHRoaXMuZGV0ZXJtaW5lRGV2aWNlXyhkZXZpY2VQYXJhbXMpIHx8IHRoaXMuZGV2aWNlO1xufTtcblxuRGV2aWNlSW5mby5wcm90b3R5cGUuZ2V0RGV2aWNlID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiB0aGlzLmRldmljZTtcbn07XG5cbkRldmljZUluZm8ucHJvdG90eXBlLnNldFZpZXdlciA9IGZ1bmN0aW9uKHZpZXdlcikge1xuICB0aGlzLnZpZXdlciA9IHZpZXdlcjtcbiAgdGhpcy5kaXN0b3J0aW9uID0gbmV3IERpc3RvcnRpb24odGhpcy52aWV3ZXIuZGlzdG9ydGlvbkNvZWZmaWNpZW50cyk7XG59O1xuXG5EZXZpY2VJbmZvLnByb3RvdHlwZS5kZXRlcm1pbmVEZXZpY2VfID0gZnVuY3Rpb24oZGV2aWNlUGFyYW1zKSB7XG4gIGlmICghZGV2aWNlUGFyYW1zKSB7XG4gICAgLy8gTm8gcGFyYW1ldGVycywgc28gdXNlIGEgZGVmYXVsdC5cbiAgICBpZiAoVXRpbC5pc0lPUygpKSB7XG4gICAgICBjb25zb2xlLndhcm4oJ1VzaW5nIGZhbGxiYWNrIGlPUyBkZXZpY2UgbWVhc3VyZW1lbnRzLicpO1xuICAgICAgcmV0dXJuIERFRkFVTFRfSU9TO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zb2xlLndhcm4oJ1VzaW5nIGZhbGxiYWNrIEFuZHJvaWQgZGV2aWNlIG1lYXN1cmVtZW50cy4nKTtcbiAgICAgIHJldHVybiBERUZBVUxUX0FORFJPSUQ7XG4gICAgfVxuICB9XG5cbiAgLy8gQ29tcHV0ZSBkZXZpY2Ugc2NyZWVuIGRpbWVuc2lvbnMgYmFzZWQgb24gZGV2aWNlUGFyYW1zLlxuICB2YXIgTUVURVJTX1BFUl9JTkNIID0gMC4wMjU0O1xuICB2YXIgbWV0ZXJzUGVyUGl4ZWxYID0gTUVURVJTX1BFUl9JTkNIIC8gZGV2aWNlUGFyYW1zLnhkcGk7XG4gIHZhciBtZXRlcnNQZXJQaXhlbFkgPSBNRVRFUlNfUEVSX0lOQ0ggLyBkZXZpY2VQYXJhbXMueWRwaTtcbiAgdmFyIHdpZHRoID0gVXRpbC5nZXRTY3JlZW5XaWR0aCgpO1xuICB2YXIgaGVpZ2h0ID0gVXRpbC5nZXRTY3JlZW5IZWlnaHQoKTtcbiAgcmV0dXJuIG5ldyBEZXZpY2Uoe1xuICAgIHdpZHRoTWV0ZXJzOiBtZXRlcnNQZXJQaXhlbFggKiB3aWR0aCxcbiAgICBoZWlnaHRNZXRlcnM6IG1ldGVyc1BlclBpeGVsWSAqIGhlaWdodCxcbiAgICBiZXZlbE1ldGVyczogZGV2aWNlUGFyYW1zLmJldmVsTW0gKiAwLjAwMSxcbiAgfSk7XG59O1xuXG4vKipcbiAqIENhbGN1bGF0ZXMgZmllbGQgb2YgdmlldyBmb3IgdGhlIGxlZnQgZXllLlxuICovXG5EZXZpY2VJbmZvLnByb3RvdHlwZS5nZXREaXN0b3J0ZWRGaWVsZE9mVmlld0xlZnRFeWUgPSBmdW5jdGlvbigpIHtcbiAgdmFyIHZpZXdlciA9IHRoaXMudmlld2VyO1xuICB2YXIgZGV2aWNlID0gdGhpcy5kZXZpY2U7XG4gIHZhciBkaXN0b3J0aW9uID0gdGhpcy5kaXN0b3J0aW9uO1xuXG4gIC8vIERldmljZS5oZWlnaHQgYW5kIGRldmljZS53aWR0aCBmb3IgZGV2aWNlIGluIHBvcnRyYWl0IG1vZGUsIHNvIHRyYW5zcG9zZS5cbiAgdmFyIGV5ZVRvU2NyZWVuRGlzdGFuY2UgPSB2aWV3ZXIuc2NyZWVuTGVuc0Rpc3RhbmNlO1xuXG4gIHZhciBvdXRlckRpc3QgPSAoZGV2aWNlLndpZHRoTWV0ZXJzIC0gdmlld2VyLmludGVyTGVuc0Rpc3RhbmNlKSAvIDI7XG4gIHZhciBpbm5lckRpc3QgPSB2aWV3ZXIuaW50ZXJMZW5zRGlzdGFuY2UgLyAyO1xuICB2YXIgYm90dG9tRGlzdCA9IHZpZXdlci5iYXNlbGluZUxlbnNEaXN0YW5jZSAtIGRldmljZS5iZXZlbE1ldGVycztcbiAgdmFyIHRvcERpc3QgPSBkZXZpY2UuaGVpZ2h0TWV0ZXJzIC0gYm90dG9tRGlzdDtcblxuICB2YXIgb3V0ZXJBbmdsZSA9IE1hdGhVdGlsLnJhZFRvRGVnICogTWF0aC5hdGFuKFxuICAgICAgZGlzdG9ydGlvbi5kaXN0b3J0KG91dGVyRGlzdCAvIGV5ZVRvU2NyZWVuRGlzdGFuY2UpKTtcbiAgdmFyIGlubmVyQW5nbGUgPSBNYXRoVXRpbC5yYWRUb0RlZyAqIE1hdGguYXRhbihcbiAgICAgIGRpc3RvcnRpb24uZGlzdG9ydChpbm5lckRpc3QgLyBleWVUb1NjcmVlbkRpc3RhbmNlKSk7XG4gIHZhciBib3R0b21BbmdsZSA9IE1hdGhVdGlsLnJhZFRvRGVnICogTWF0aC5hdGFuKFxuICAgICAgZGlzdG9ydGlvbi5kaXN0b3J0KGJvdHRvbURpc3QgLyBleWVUb1NjcmVlbkRpc3RhbmNlKSk7XG4gIHZhciB0b3BBbmdsZSA9IE1hdGhVdGlsLnJhZFRvRGVnICogTWF0aC5hdGFuKFxuICAgICAgZGlzdG9ydGlvbi5kaXN0b3J0KHRvcERpc3QgLyBleWVUb1NjcmVlbkRpc3RhbmNlKSk7XG5cbiAgcmV0dXJuIHtcbiAgICBsZWZ0RGVncmVlczogTWF0aC5taW4ob3V0ZXJBbmdsZSwgdmlld2VyLmZvdiksXG4gICAgcmlnaHREZWdyZWVzOiBNYXRoLm1pbihpbm5lckFuZ2xlLCB2aWV3ZXIuZm92KSxcbiAgICBkb3duRGVncmVlczogTWF0aC5taW4oYm90dG9tQW5nbGUsIHZpZXdlci5mb3YpLFxuICAgIHVwRGVncmVlczogTWF0aC5taW4odG9wQW5nbGUsIHZpZXdlci5mb3YpXG4gIH07XG59O1xuXG4vKipcbiAqIENhbGN1bGF0ZXMgdGhlIHRhbi1hbmdsZXMgZnJvbSB0aGUgbWF4aW11bSBGT1YgZm9yIHRoZSBsZWZ0IGV5ZSBmb3IgdGhlXG4gKiBjdXJyZW50IGRldmljZSBhbmQgc2NyZWVuIHBhcmFtZXRlcnMuXG4gKi9cbkRldmljZUluZm8ucHJvdG90eXBlLmdldExlZnRFeWVWaXNpYmxlVGFuQW5nbGVzID0gZnVuY3Rpb24oKSB7XG4gIHZhciB2aWV3ZXIgPSB0aGlzLnZpZXdlcjtcbiAgdmFyIGRldmljZSA9IHRoaXMuZGV2aWNlO1xuICB2YXIgZGlzdG9ydGlvbiA9IHRoaXMuZGlzdG9ydGlvbjtcblxuICAvLyBUYW4tYW5nbGVzIGZyb20gdGhlIG1heCBGT1YuXG4gIHZhciBmb3ZMZWZ0ID0gTWF0aC50YW4oLU1hdGhVdGlsLmRlZ1RvUmFkICogdmlld2VyLmZvdik7XG4gIHZhciBmb3ZUb3AgPSBNYXRoLnRhbihNYXRoVXRpbC5kZWdUb1JhZCAqIHZpZXdlci5mb3YpO1xuICB2YXIgZm92UmlnaHQgPSBNYXRoLnRhbihNYXRoVXRpbC5kZWdUb1JhZCAqIHZpZXdlci5mb3YpO1xuICB2YXIgZm92Qm90dG9tID0gTWF0aC50YW4oLU1hdGhVdGlsLmRlZ1RvUmFkICogdmlld2VyLmZvdik7XG4gIC8vIFZpZXdwb3J0IHNpemUuXG4gIHZhciBoYWxmV2lkdGggPSBkZXZpY2Uud2lkdGhNZXRlcnMgLyA0O1xuICB2YXIgaGFsZkhlaWdodCA9IGRldmljZS5oZWlnaHRNZXRlcnMgLyAyO1xuICAvLyBWaWV3cG9ydCBjZW50ZXIsIG1lYXN1cmVkIGZyb20gbGVmdCBsZW5zIHBvc2l0aW9uLlxuICB2YXIgdmVydGljYWxMZW5zT2Zmc2V0ID0gKHZpZXdlci5iYXNlbGluZUxlbnNEaXN0YW5jZSAtIGRldmljZS5iZXZlbE1ldGVycyAtIGhhbGZIZWlnaHQpO1xuICB2YXIgY2VudGVyWCA9IHZpZXdlci5pbnRlckxlbnNEaXN0YW5jZSAvIDIgLSBoYWxmV2lkdGg7XG4gIHZhciBjZW50ZXJZID0gLXZlcnRpY2FsTGVuc09mZnNldDtcbiAgdmFyIGNlbnRlclogPSB2aWV3ZXIuc2NyZWVuTGVuc0Rpc3RhbmNlO1xuICAvLyBUYW4tYW5nbGVzIG9mIHRoZSB2aWV3cG9ydCBlZGdlcywgYXMgc2VlbiB0aHJvdWdoIHRoZSBsZW5zLlxuICB2YXIgc2NyZWVuTGVmdCA9IGRpc3RvcnRpb24uZGlzdG9ydCgoY2VudGVyWCAtIGhhbGZXaWR0aCkgLyBjZW50ZXJaKTtcbiAgdmFyIHNjcmVlblRvcCA9IGRpc3RvcnRpb24uZGlzdG9ydCgoY2VudGVyWSArIGhhbGZIZWlnaHQpIC8gY2VudGVyWik7XG4gIHZhciBzY3JlZW5SaWdodCA9IGRpc3RvcnRpb24uZGlzdG9ydCgoY2VudGVyWCArIGhhbGZXaWR0aCkgLyBjZW50ZXJaKTtcbiAgdmFyIHNjcmVlbkJvdHRvbSA9IGRpc3RvcnRpb24uZGlzdG9ydCgoY2VudGVyWSAtIGhhbGZIZWlnaHQpIC8gY2VudGVyWik7XG4gIC8vIENvbXBhcmUgdGhlIHR3byBzZXRzIG9mIHRhbi1hbmdsZXMgYW5kIHRha2UgdGhlIHZhbHVlIGNsb3NlciB0byB6ZXJvIG9uIGVhY2ggc2lkZS5cbiAgdmFyIHJlc3VsdCA9IG5ldyBGbG9hdDMyQXJyYXkoNCk7XG4gIHJlc3VsdFswXSA9IE1hdGgubWF4KGZvdkxlZnQsIHNjcmVlbkxlZnQpO1xuICByZXN1bHRbMV0gPSBNYXRoLm1pbihmb3ZUb3AsIHNjcmVlblRvcCk7XG4gIHJlc3VsdFsyXSA9IE1hdGgubWluKGZvdlJpZ2h0LCBzY3JlZW5SaWdodCk7XG4gIHJlc3VsdFszXSA9IE1hdGgubWF4KGZvdkJvdHRvbSwgc2NyZWVuQm90dG9tKTtcbiAgcmV0dXJuIHJlc3VsdDtcbn07XG5cbi8qKlxuICogQ2FsY3VsYXRlcyB0aGUgdGFuLWFuZ2xlcyBmcm9tIHRoZSBtYXhpbXVtIEZPViBmb3IgdGhlIGxlZnQgZXllIGZvciB0aGVcbiAqIGN1cnJlbnQgZGV2aWNlIGFuZCBzY3JlZW4gcGFyYW1ldGVycywgYXNzdW1pbmcgbm8gbGVuc2VzLlxuICovXG5EZXZpY2VJbmZvLnByb3RvdHlwZS5nZXRMZWZ0RXllTm9MZW5zVGFuQW5nbGVzID0gZnVuY3Rpb24oKSB7XG4gIHZhciB2aWV3ZXIgPSB0aGlzLnZpZXdlcjtcbiAgdmFyIGRldmljZSA9IHRoaXMuZGV2aWNlO1xuICB2YXIgZGlzdG9ydGlvbiA9IHRoaXMuZGlzdG9ydGlvbjtcblxuICB2YXIgcmVzdWx0ID0gbmV3IEZsb2F0MzJBcnJheSg0KTtcbiAgLy8gVGFuLWFuZ2xlcyBmcm9tIHRoZSBtYXggRk9WLlxuICB2YXIgZm92TGVmdCA9IGRpc3RvcnRpb24uZGlzdG9ydEludmVyc2UoTWF0aC50YW4oLU1hdGhVdGlsLmRlZ1RvUmFkICogdmlld2VyLmZvdikpO1xuICB2YXIgZm92VG9wID0gZGlzdG9ydGlvbi5kaXN0b3J0SW52ZXJzZShNYXRoLnRhbihNYXRoVXRpbC5kZWdUb1JhZCAqIHZpZXdlci5mb3YpKTtcbiAgdmFyIGZvdlJpZ2h0ID0gZGlzdG9ydGlvbi5kaXN0b3J0SW52ZXJzZShNYXRoLnRhbihNYXRoVXRpbC5kZWdUb1JhZCAqIHZpZXdlci5mb3YpKTtcbiAgdmFyIGZvdkJvdHRvbSA9IGRpc3RvcnRpb24uZGlzdG9ydEludmVyc2UoTWF0aC50YW4oLU1hdGhVdGlsLmRlZ1RvUmFkICogdmlld2VyLmZvdikpO1xuICAvLyBWaWV3cG9ydCBzaXplLlxuICB2YXIgaGFsZldpZHRoID0gZGV2aWNlLndpZHRoTWV0ZXJzIC8gNDtcbiAgdmFyIGhhbGZIZWlnaHQgPSBkZXZpY2UuaGVpZ2h0TWV0ZXJzIC8gMjtcbiAgLy8gVmlld3BvcnQgY2VudGVyLCBtZWFzdXJlZCBmcm9tIGxlZnQgbGVucyBwb3NpdGlvbi5cbiAgdmFyIHZlcnRpY2FsTGVuc09mZnNldCA9ICh2aWV3ZXIuYmFzZWxpbmVMZW5zRGlzdGFuY2UgLSBkZXZpY2UuYmV2ZWxNZXRlcnMgLSBoYWxmSGVpZ2h0KTtcbiAgdmFyIGNlbnRlclggPSB2aWV3ZXIuaW50ZXJMZW5zRGlzdGFuY2UgLyAyIC0gaGFsZldpZHRoO1xuICB2YXIgY2VudGVyWSA9IC12ZXJ0aWNhbExlbnNPZmZzZXQ7XG4gIHZhciBjZW50ZXJaID0gdmlld2VyLnNjcmVlbkxlbnNEaXN0YW5jZTtcbiAgLy8gVGFuLWFuZ2xlcyBvZiB0aGUgdmlld3BvcnQgZWRnZXMsIGFzIHNlZW4gdGhyb3VnaCB0aGUgbGVucy5cbiAgdmFyIHNjcmVlbkxlZnQgPSAoY2VudGVyWCAtIGhhbGZXaWR0aCkgLyBjZW50ZXJaO1xuICB2YXIgc2NyZWVuVG9wID0gKGNlbnRlclkgKyBoYWxmSGVpZ2h0KSAvIGNlbnRlclo7XG4gIHZhciBzY3JlZW5SaWdodCA9IChjZW50ZXJYICsgaGFsZldpZHRoKSAvIGNlbnRlclo7XG4gIHZhciBzY3JlZW5Cb3R0b20gPSAoY2VudGVyWSAtIGhhbGZIZWlnaHQpIC8gY2VudGVyWjtcbiAgLy8gQ29tcGFyZSB0aGUgdHdvIHNldHMgb2YgdGFuLWFuZ2xlcyBhbmQgdGFrZSB0aGUgdmFsdWUgY2xvc2VyIHRvIHplcm8gb24gZWFjaCBzaWRlLlxuICByZXN1bHRbMF0gPSBNYXRoLm1heChmb3ZMZWZ0LCBzY3JlZW5MZWZ0KTtcbiAgcmVzdWx0WzFdID0gTWF0aC5taW4oZm92VG9wLCBzY3JlZW5Ub3ApO1xuICByZXN1bHRbMl0gPSBNYXRoLm1pbihmb3ZSaWdodCwgc2NyZWVuUmlnaHQpO1xuICByZXN1bHRbM10gPSBNYXRoLm1heChmb3ZCb3R0b20sIHNjcmVlbkJvdHRvbSk7XG4gIHJldHVybiByZXN1bHQ7XG59O1xuXG4vKipcbiAqIENhbGN1bGF0ZXMgdGhlIHNjcmVlbiByZWN0YW5nbGUgdmlzaWJsZSBmcm9tIHRoZSBsZWZ0IGV5ZSBmb3IgdGhlXG4gKiBjdXJyZW50IGRldmljZSBhbmQgc2NyZWVuIHBhcmFtZXRlcnMuXG4gKi9cbkRldmljZUluZm8ucHJvdG90eXBlLmdldExlZnRFeWVWaXNpYmxlU2NyZWVuUmVjdCA9IGZ1bmN0aW9uKHVuZGlzdG9ydGVkRnJ1c3R1bSkge1xuICB2YXIgdmlld2VyID0gdGhpcy52aWV3ZXI7XG4gIHZhciBkZXZpY2UgPSB0aGlzLmRldmljZTtcblxuICB2YXIgZGlzdCA9IHZpZXdlci5zY3JlZW5MZW5zRGlzdGFuY2U7XG4gIHZhciBleWVYID0gKGRldmljZS53aWR0aE1ldGVycyAtIHZpZXdlci5pbnRlckxlbnNEaXN0YW5jZSkgLyAyO1xuICB2YXIgZXllWSA9IHZpZXdlci5iYXNlbGluZUxlbnNEaXN0YW5jZSAtIGRldmljZS5iZXZlbE1ldGVycztcbiAgdmFyIGxlZnQgPSAodW5kaXN0b3J0ZWRGcnVzdHVtWzBdICogZGlzdCArIGV5ZVgpIC8gZGV2aWNlLndpZHRoTWV0ZXJzO1xuICB2YXIgdG9wID0gKHVuZGlzdG9ydGVkRnJ1c3R1bVsxXSAqIGRpc3QgKyBleWVZKSAvIGRldmljZS5oZWlnaHRNZXRlcnM7XG4gIHZhciByaWdodCA9ICh1bmRpc3RvcnRlZEZydXN0dW1bMl0gKiBkaXN0ICsgZXllWCkgLyBkZXZpY2Uud2lkdGhNZXRlcnM7XG4gIHZhciBib3R0b20gPSAodW5kaXN0b3J0ZWRGcnVzdHVtWzNdICogZGlzdCArIGV5ZVkpIC8gZGV2aWNlLmhlaWdodE1ldGVycztcbiAgcmV0dXJuIHtcbiAgICB4OiBsZWZ0LFxuICAgIHk6IGJvdHRvbSxcbiAgICB3aWR0aDogcmlnaHQgLSBsZWZ0LFxuICAgIGhlaWdodDogdG9wIC0gYm90dG9tXG4gIH07XG59O1xuXG5EZXZpY2VJbmZvLnByb3RvdHlwZS5nZXRGaWVsZE9mVmlld0xlZnRFeWUgPSBmdW5jdGlvbihvcHRfaXNVbmRpc3RvcnRlZCkge1xuICByZXR1cm4gb3B0X2lzVW5kaXN0b3J0ZWQgPyB0aGlzLmdldFVuZGlzdG9ydGVkRmllbGRPZlZpZXdMZWZ0RXllKCkgOlxuICAgICAgdGhpcy5nZXREaXN0b3J0ZWRGaWVsZE9mVmlld0xlZnRFeWUoKTtcbn07XG5cbkRldmljZUluZm8ucHJvdG90eXBlLmdldEZpZWxkT2ZWaWV3UmlnaHRFeWUgPSBmdW5jdGlvbihvcHRfaXNVbmRpc3RvcnRlZCkge1xuICB2YXIgZm92ID0gdGhpcy5nZXRGaWVsZE9mVmlld0xlZnRFeWUob3B0X2lzVW5kaXN0b3J0ZWQpO1xuICByZXR1cm4ge1xuICAgIGxlZnREZWdyZWVzOiBmb3YucmlnaHREZWdyZWVzLFxuICAgIHJpZ2h0RGVncmVlczogZm92LmxlZnREZWdyZWVzLFxuICAgIHVwRGVncmVlczogZm92LnVwRGVncmVlcyxcbiAgICBkb3duRGVncmVlczogZm92LmRvd25EZWdyZWVzXG4gIH07XG59O1xuXG4vKipcbiAqIENhbGN1bGF0ZXMgdW5kaXN0b3J0ZWQgZmllbGQgb2YgdmlldyBmb3IgdGhlIGxlZnQgZXllLlxuICovXG5EZXZpY2VJbmZvLnByb3RvdHlwZS5nZXRVbmRpc3RvcnRlZEZpZWxkT2ZWaWV3TGVmdEV5ZSA9IGZ1bmN0aW9uKCkge1xuICB2YXIgcCA9IHRoaXMuZ2V0VW5kaXN0b3J0ZWRQYXJhbXNfKCk7XG5cbiAgcmV0dXJuIHtcbiAgICBsZWZ0RGVncmVlczogTWF0aFV0aWwucmFkVG9EZWcgKiBNYXRoLmF0YW4ocC5vdXRlckRpc3QpLFxuICAgIHJpZ2h0RGVncmVlczogTWF0aFV0aWwucmFkVG9EZWcgKiBNYXRoLmF0YW4ocC5pbm5lckRpc3QpLFxuICAgIGRvd25EZWdyZWVzOiBNYXRoVXRpbC5yYWRUb0RlZyAqIE1hdGguYXRhbihwLmJvdHRvbURpc3QpLFxuICAgIHVwRGVncmVlczogTWF0aFV0aWwucmFkVG9EZWcgKiBNYXRoLmF0YW4ocC50b3BEaXN0KVxuICB9O1xufTtcblxuRGV2aWNlSW5mby5wcm90b3R5cGUuZ2V0VW5kaXN0b3J0ZWRWaWV3cG9ydExlZnRFeWUgPSBmdW5jdGlvbigpIHtcbiAgdmFyIHAgPSB0aGlzLmdldFVuZGlzdG9ydGVkUGFyYW1zXygpO1xuICB2YXIgdmlld2VyID0gdGhpcy52aWV3ZXI7XG4gIHZhciBkZXZpY2UgPSB0aGlzLmRldmljZTtcblxuICAvLyBEaXN0YW5jZXMgc3RvcmVkIGluIGxvY2FsIHZhcmlhYmxlcyBhcmUgaW4gdGFuLWFuZ2xlIHVuaXRzIHVubGVzcyBvdGhlcndpc2VcbiAgLy8gbm90ZWQuXG4gIHZhciBleWVUb1NjcmVlbkRpc3RhbmNlID0gdmlld2VyLnNjcmVlbkxlbnNEaXN0YW5jZTtcbiAgdmFyIHNjcmVlbldpZHRoID0gZGV2aWNlLndpZHRoTWV0ZXJzIC8gZXllVG9TY3JlZW5EaXN0YW5jZTtcbiAgdmFyIHNjcmVlbkhlaWdodCA9IGRldmljZS5oZWlnaHRNZXRlcnMgLyBleWVUb1NjcmVlbkRpc3RhbmNlO1xuICB2YXIgeFB4UGVyVGFuQW5nbGUgPSBkZXZpY2Uud2lkdGggLyBzY3JlZW5XaWR0aDtcbiAgdmFyIHlQeFBlclRhbkFuZ2xlID0gZGV2aWNlLmhlaWdodCAvIHNjcmVlbkhlaWdodDtcblxuICB2YXIgeCA9IE1hdGgucm91bmQoKHAuZXllUG9zWCAtIHAub3V0ZXJEaXN0KSAqIHhQeFBlclRhbkFuZ2xlKTtcbiAgdmFyIHkgPSBNYXRoLnJvdW5kKChwLmV5ZVBvc1kgLSBwLmJvdHRvbURpc3QpICogeVB4UGVyVGFuQW5nbGUpO1xuICByZXR1cm4ge1xuICAgIHg6IHgsXG4gICAgeTogeSxcbiAgICB3aWR0aDogTWF0aC5yb3VuZCgocC5leWVQb3NYICsgcC5pbm5lckRpc3QpICogeFB4UGVyVGFuQW5nbGUpIC0geCxcbiAgICBoZWlnaHQ6IE1hdGgucm91bmQoKHAuZXllUG9zWSArIHAudG9wRGlzdCkgKiB5UHhQZXJUYW5BbmdsZSkgLSB5XG4gIH07XG59O1xuXG5EZXZpY2VJbmZvLnByb3RvdHlwZS5nZXRVbmRpc3RvcnRlZFBhcmFtc18gPSBmdW5jdGlvbigpIHtcbiAgdmFyIHZpZXdlciA9IHRoaXMudmlld2VyO1xuICB2YXIgZGV2aWNlID0gdGhpcy5kZXZpY2U7XG4gIHZhciBkaXN0b3J0aW9uID0gdGhpcy5kaXN0b3J0aW9uO1xuXG4gIC8vIE1vc3Qgb2YgdGhlc2UgdmFyaWFibGVzIGluIHRhbi1hbmdsZSB1bml0cy5cbiAgdmFyIGV5ZVRvU2NyZWVuRGlzdGFuY2UgPSB2aWV3ZXIuc2NyZWVuTGVuc0Rpc3RhbmNlO1xuICB2YXIgaGFsZkxlbnNEaXN0YW5jZSA9IHZpZXdlci5pbnRlckxlbnNEaXN0YW5jZSAvIDIgLyBleWVUb1NjcmVlbkRpc3RhbmNlO1xuICB2YXIgc2NyZWVuV2lkdGggPSBkZXZpY2Uud2lkdGhNZXRlcnMgLyBleWVUb1NjcmVlbkRpc3RhbmNlO1xuICB2YXIgc2NyZWVuSGVpZ2h0ID0gZGV2aWNlLmhlaWdodE1ldGVycyAvIGV5ZVRvU2NyZWVuRGlzdGFuY2U7XG5cbiAgdmFyIGV5ZVBvc1ggPSBzY3JlZW5XaWR0aCAvIDIgLSBoYWxmTGVuc0Rpc3RhbmNlO1xuICB2YXIgZXllUG9zWSA9ICh2aWV3ZXIuYmFzZWxpbmVMZW5zRGlzdGFuY2UgLSBkZXZpY2UuYmV2ZWxNZXRlcnMpIC8gZXllVG9TY3JlZW5EaXN0YW5jZTtcblxuICB2YXIgbWF4Rm92ID0gdmlld2VyLmZvdjtcbiAgdmFyIHZpZXdlck1heCA9IGRpc3RvcnRpb24uZGlzdG9ydEludmVyc2UoTWF0aC50YW4oTWF0aFV0aWwuZGVnVG9SYWQgKiBtYXhGb3YpKTtcbiAgdmFyIG91dGVyRGlzdCA9IE1hdGgubWluKGV5ZVBvc1gsIHZpZXdlck1heCk7XG4gIHZhciBpbm5lckRpc3QgPSBNYXRoLm1pbihoYWxmTGVuc0Rpc3RhbmNlLCB2aWV3ZXJNYXgpO1xuICB2YXIgYm90dG9tRGlzdCA9IE1hdGgubWluKGV5ZVBvc1ksIHZpZXdlck1heCk7XG4gIHZhciB0b3BEaXN0ID0gTWF0aC5taW4oc2NyZWVuSGVpZ2h0IC0gZXllUG9zWSwgdmlld2VyTWF4KTtcblxuICByZXR1cm4ge1xuICAgIG91dGVyRGlzdDogb3V0ZXJEaXN0LFxuICAgIGlubmVyRGlzdDogaW5uZXJEaXN0LFxuICAgIHRvcERpc3Q6IHRvcERpc3QsXG4gICAgYm90dG9tRGlzdDogYm90dG9tRGlzdCxcbiAgICBleWVQb3NYOiBleWVQb3NYLFxuICAgIGV5ZVBvc1k6IGV5ZVBvc1lcbiAgfTtcbn07XG5cblxuZnVuY3Rpb24gQ2FyZGJvYXJkVmlld2VyKHBhcmFtcykge1xuICAvLyBBIG1hY2hpbmUgcmVhZGFibGUgSUQuXG4gIHRoaXMuaWQgPSBwYXJhbXMuaWQ7XG4gIC8vIEEgaHVtYW4gcmVhZGFibGUgbGFiZWwuXG4gIHRoaXMubGFiZWwgPSBwYXJhbXMubGFiZWw7XG5cbiAgLy8gRmllbGQgb2YgdmlldyBpbiBkZWdyZWVzIChwZXIgc2lkZSkuXG4gIHRoaXMuZm92ID0gcGFyYW1zLmZvdjtcblxuICAvLyBEaXN0YW5jZSBiZXR3ZWVuIGxlbnMgY2VudGVycyBpbiBtZXRlcnMuXG4gIHRoaXMuaW50ZXJMZW5zRGlzdGFuY2UgPSBwYXJhbXMuaW50ZXJMZW5zRGlzdGFuY2U7XG4gIC8vIERpc3RhbmNlIGJldHdlZW4gdmlld2VyIGJhc2VsaW5lIGFuZCBsZW5zIGNlbnRlciBpbiBtZXRlcnMuXG4gIHRoaXMuYmFzZWxpbmVMZW5zRGlzdGFuY2UgPSBwYXJhbXMuYmFzZWxpbmVMZW5zRGlzdGFuY2U7XG4gIC8vIFNjcmVlbi10by1sZW5zIGRpc3RhbmNlIGluIG1ldGVycy5cbiAgdGhpcy5zY3JlZW5MZW5zRGlzdGFuY2UgPSBwYXJhbXMuc2NyZWVuTGVuc0Rpc3RhbmNlO1xuXG4gIC8vIERpc3RvcnRpb24gY29lZmZpY2llbnRzLlxuICB0aGlzLmRpc3RvcnRpb25Db2VmZmljaWVudHMgPSBwYXJhbXMuZGlzdG9ydGlvbkNvZWZmaWNpZW50cztcbiAgLy8gSW52ZXJzZSBkaXN0b3J0aW9uIGNvZWZmaWNpZW50cy5cbiAgLy8gVE9ETzogQ2FsY3VsYXRlIHRoZXNlIGZyb20gZGlzdG9ydGlvbkNvZWZmaWNpZW50cyBpbiB0aGUgZnV0dXJlLlxuICB0aGlzLmludmVyc2VDb2VmZmljaWVudHMgPSBwYXJhbXMuaW52ZXJzZUNvZWZmaWNpZW50cztcbn1cblxuLy8gRXhwb3J0IHZpZXdlciBpbmZvcm1hdGlvbi5cbkRldmljZUluZm8uVmlld2VycyA9IFZpZXdlcnM7XG5tb2R1bGUuZXhwb3J0cyA9IERldmljZUluZm87XG4iLCIvKlxuICogQ29weXJpZ2h0IDIwMTYgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqL1xudmFyIFZSRGlzcGxheSA9IHJlcXVpcmUoJy4vYmFzZS5qcycpLlZSRGlzcGxheTtcbnZhciBITURWUkRldmljZSA9IHJlcXVpcmUoJy4vYmFzZS5qcycpLkhNRFZSRGV2aWNlO1xudmFyIFBvc2l0aW9uU2Vuc29yVlJEZXZpY2UgPSByZXF1aXJlKCcuL2Jhc2UuanMnKS5Qb3NpdGlvblNlbnNvclZSRGV2aWNlO1xuXG4vKipcbiAqIFdyYXBzIGEgVlJEaXNwbGF5IGFuZCBleHBvc2VzIGl0IGFzIGEgSE1EVlJEZXZpY2VcbiAqL1xuZnVuY3Rpb24gVlJEaXNwbGF5SE1ERGV2aWNlKGRpc3BsYXkpIHtcbiAgdGhpcy5kaXNwbGF5ID0gZGlzcGxheTtcblxuICB0aGlzLmhhcmR3YXJlVW5pdElkID0gZGlzcGxheS5kaXNwbGF5SWQ7XG4gIHRoaXMuZGV2aWNlSWQgPSAnd2VidnItcG9seWZpbGw6SE1EOicgKyBkaXNwbGF5LmRpc3BsYXlJZDtcbiAgdGhpcy5kZXZpY2VOYW1lID0gZGlzcGxheS5kaXNwbGF5TmFtZSArICcgKEhNRCknO1xufVxuVlJEaXNwbGF5SE1ERGV2aWNlLnByb3RvdHlwZSA9IG5ldyBITURWUkRldmljZSgpO1xuXG5WUkRpc3BsYXlITUREZXZpY2UucHJvdG90eXBlLmdldEV5ZVBhcmFtZXRlcnMgPSBmdW5jdGlvbih3aGljaEV5ZSkge1xuICB2YXIgZXllUGFyYW1ldGVycyA9IHRoaXMuZGlzcGxheS5nZXRFeWVQYXJhbWV0ZXJzKHdoaWNoRXllKTtcblxuICByZXR1cm4ge1xuICAgIGN1cnJlbnRGaWVsZE9mVmlldzogZXllUGFyYW1ldGVycy5maWVsZE9mVmlldyxcbiAgICBtYXhpbXVtRmllbGRPZlZpZXc6IGV5ZVBhcmFtZXRlcnMuZmllbGRPZlZpZXcsXG4gICAgbWluaW11bUZpZWxkT2ZWaWV3OiBleWVQYXJhbWV0ZXJzLmZpZWxkT2ZWaWV3LFxuICAgIHJlY29tbWVuZGVkRmllbGRPZlZpZXc6IGV5ZVBhcmFtZXRlcnMuZmllbGRPZlZpZXcsXG4gICAgZXllVHJhbnNsYXRpb246IHsgeDogZXllUGFyYW1ldGVycy5vZmZzZXRbMF0sIHk6IGV5ZVBhcmFtZXRlcnMub2Zmc2V0WzFdLCB6OiBleWVQYXJhbWV0ZXJzLm9mZnNldFsyXSB9LFxuICAgIHJlbmRlclJlY3Q6IHtcbiAgICAgIHg6ICh3aGljaEV5ZSA9PSAncmlnaHQnKSA/IGV5ZVBhcmFtZXRlcnMucmVuZGVyV2lkdGggOiAwLFxuICAgICAgeTogMCxcbiAgICAgIHdpZHRoOiBleWVQYXJhbWV0ZXJzLnJlbmRlcldpZHRoLFxuICAgICAgaGVpZ2h0OiBleWVQYXJhbWV0ZXJzLnJlbmRlckhlaWdodFxuICAgIH1cbiAgfTtcbn07XG5cblZSRGlzcGxheUhNRERldmljZS5wcm90b3R5cGUuc2V0RmllbGRPZlZpZXcgPVxuICAgIGZ1bmN0aW9uKG9wdF9mb3ZMZWZ0LCBvcHRfZm92UmlnaHQsIG9wdF96TmVhciwgb3B0X3pGYXIpIHtcbiAgLy8gTm90IHN1cHBvcnRlZC4gZ2V0RXllUGFyYW1ldGVycyByZXBvcnRzIHRoYXQgdGhlIG1pbiwgbWF4LCBhbmQgcmVjb21tZW5kZWRcbiAgLy8gRm9WIGlzIGFsbCB0aGUgc2FtZSwgc28gbm8gYWRqdXN0bWVudCBjYW4gYmUgbWFkZS5cbn07XG5cbi8vIFRPRE86IE5lZWQgdG8gaG9vayByZXF1ZXN0RnVsbHNjcmVlbiB0byBzZWUgaWYgYSB3cmFwcGVkIFZSRGlzcGxheSB3YXMgcGFzc2VkXG4vLyBpbiBhcyBhbiBvcHRpb24uIElmIHNvIHdlIHNob3VsZCBwcmV2ZW50IHRoZSBkZWZhdWx0IGZ1bGxzY3JlZW4gYmVoYXZpb3IgYW5kXG4vLyBjYWxsIFZSRGlzcGxheS5yZXF1ZXN0UHJlc2VudCBpbnN0ZWFkLlxuXG4vKipcbiAqIFdyYXBzIGEgVlJEaXNwbGF5IGFuZCBleHBvc2VzIGl0IGFzIGEgUG9zaXRpb25TZW5zb3JWUkRldmljZVxuICovXG5mdW5jdGlvbiBWUkRpc3BsYXlQb3NpdGlvblNlbnNvckRldmljZShkaXNwbGF5KSB7XG4gIHRoaXMuZGlzcGxheSA9IGRpc3BsYXk7XG5cbiAgdGhpcy5oYXJkd2FyZVVuaXRJZCA9IGRpc3BsYXkuZGlzcGxheUlkO1xuICB0aGlzLmRldmljZUlkID0gJ3dlYnZyLXBvbHlmaWxsOlBvc2l0aW9uU2Vuc29yOiAnICsgZGlzcGxheS5kaXNwbGF5SWQ7XG4gIHRoaXMuZGV2aWNlTmFtZSA9IGRpc3BsYXkuZGlzcGxheU5hbWUgKyAnIChQb3NpdGlvblNlbnNvciknO1xufVxuVlJEaXNwbGF5UG9zaXRpb25TZW5zb3JEZXZpY2UucHJvdG90eXBlID0gbmV3IFBvc2l0aW9uU2Vuc29yVlJEZXZpY2UoKTtcblxuVlJEaXNwbGF5UG9zaXRpb25TZW5zb3JEZXZpY2UucHJvdG90eXBlLmdldFN0YXRlID0gZnVuY3Rpb24oKSB7XG4gIHZhciBwb3NlID0gdGhpcy5kaXNwbGF5LmdldFBvc2UoKTtcbiAgcmV0dXJuIHtcbiAgICBwb3NpdGlvbjogcG9zZS5wb3NpdGlvbiA/IHsgeDogcG9zZS5wb3NpdGlvblswXSwgeTogcG9zZS5wb3NpdGlvblsxXSwgejogcG9zZS5wb3NpdGlvblsyXSB9IDogbnVsbCxcbiAgICBvcmllbnRhdGlvbjogcG9zZS5vcmllbnRhdGlvbiA/IHsgeDogcG9zZS5vcmllbnRhdGlvblswXSwgeTogcG9zZS5vcmllbnRhdGlvblsxXSwgejogcG9zZS5vcmllbnRhdGlvblsyXSwgdzogcG9zZS5vcmllbnRhdGlvblszXSB9IDogbnVsbCxcbiAgICBsaW5lYXJWZWxvY2l0eTogbnVsbCxcbiAgICBsaW5lYXJBY2NlbGVyYXRpb246IG51bGwsXG4gICAgYW5ndWxhclZlbG9jaXR5OiBudWxsLFxuICAgIGFuZ3VsYXJBY2NlbGVyYXRpb246IG51bGxcbiAgfTtcbn07XG5cblZSRGlzcGxheVBvc2l0aW9uU2Vuc29yRGV2aWNlLnByb3RvdHlwZS5yZXNldFN0YXRlID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiB0aGlzLnBvc2l0aW9uRGV2aWNlLnJlc2V0UG9zZSgpO1xufTtcblxuXG5tb2R1bGUuZXhwb3J0cy5WUkRpc3BsYXlITUREZXZpY2UgPSBWUkRpc3BsYXlITUREZXZpY2U7XG5tb2R1bGUuZXhwb3J0cy5WUkRpc3BsYXlQb3NpdGlvblNlbnNvckRldmljZSA9IFZSRGlzcGxheVBvc2l0aW9uU2Vuc29yRGV2aWNlO1xuXG4iLCIvKipcbiAqIFRPRE8oc211cyk6IEltcGxlbWVudCBjb2VmZmljaWVudCBpbnZlcnNpb24uXG4gKi9cbmZ1bmN0aW9uIERpc3RvcnRpb24oY29lZmZpY2llbnRzKSB7XG4gIHRoaXMuY29lZmZpY2llbnRzID0gY29lZmZpY2llbnRzO1xufVxuXG4vKipcbiAqIENhbGN1bGF0ZXMgdGhlIGludmVyc2UgZGlzdG9ydGlvbiBmb3IgYSByYWRpdXMuXG4gKiA8L3A+PHA+XG4gKiBBbGxvd3MgdG8gY29tcHV0ZSB0aGUgb3JpZ2luYWwgdW5kaXN0b3J0ZWQgcmFkaXVzIGZyb20gYSBkaXN0b3J0ZWQgb25lLlxuICogU2VlIGFsc28gZ2V0QXBwcm94aW1hdGVJbnZlcnNlRGlzdG9ydGlvbigpIGZvciBhIGZhc3RlciBidXQgcG90ZW50aWFsbHlcbiAqIGxlc3MgYWNjdXJhdGUgbWV0aG9kLlxuICpcbiAqIEBwYXJhbSB7TnVtYmVyfSByYWRpdXMgRGlzdG9ydGVkIHJhZGl1cyBmcm9tIHRoZSBsZW5zIGNlbnRlciBpbiB0YW4tYW5nbGUgdW5pdHMuXG4gKiBAcmV0dXJuIHtOdW1iZXJ9IFRoZSB1bmRpc3RvcnRlZCByYWRpdXMgaW4gdGFuLWFuZ2xlIHVuaXRzLlxuICovXG5EaXN0b3J0aW9uLnByb3RvdHlwZS5kaXN0b3J0SW52ZXJzZSA9IGZ1bmN0aW9uKHJhZGl1cykge1xuICAvLyBTZWNhbnQgbWV0aG9kLlxuICB2YXIgcjAgPSAwO1xuICB2YXIgcjEgPSAxO1xuICB2YXIgZHIwID0gcmFkaXVzIC0gdGhpcy5kaXN0b3J0KHIwKTtcbiAgd2hpbGUgKE1hdGguYWJzKHIxIC0gcjApID4gMC4wMDAxIC8qKiAwLjFtbSAqLykge1xuICAgIHZhciBkcjEgPSByYWRpdXMgLSB0aGlzLmRpc3RvcnQocjEpO1xuICAgIHZhciByMiA9IHIxIC0gZHIxICogKChyMSAtIHIwKSAvIChkcjEgLSBkcjApKTtcbiAgICByMCA9IHIxO1xuICAgIHIxID0gcjI7XG4gICAgZHIwID0gZHIxO1xuICB9XG4gIHJldHVybiByMTtcbn07XG5cbi8qKlxuICogRGlzdG9ydHMgYSByYWRpdXMgYnkgaXRzIGRpc3RvcnRpb24gZmFjdG9yIGZyb20gdGhlIGNlbnRlciBvZiB0aGUgbGVuc2VzLlxuICpcbiAqIEBwYXJhbSB7TnVtYmVyfSByYWRpdXMgUmFkaXVzIGZyb20gdGhlIGxlbnMgY2VudGVyIGluIHRhbi1hbmdsZSB1bml0cy5cbiAqIEByZXR1cm4ge051bWJlcn0gVGhlIGRpc3RvcnRlZCByYWRpdXMgaW4gdGFuLWFuZ2xlIHVuaXRzLlxuICovXG5EaXN0b3J0aW9uLnByb3RvdHlwZS5kaXN0b3J0ID0gZnVuY3Rpb24ocmFkaXVzKSB7XG4gIHZhciByMiA9IHJhZGl1cyAqIHJhZGl1cztcbiAgdmFyIHJldCA9IDA7XG4gIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5jb2VmZmljaWVudHMubGVuZ3RoOyBpKyspIHtcbiAgICByZXQgPSByMiAqIChyZXQgKyB0aGlzLmNvZWZmaWNpZW50c1tpXSk7XG4gIH1cbiAgcmV0dXJuIChyZXQgKyAxKSAqIHJhZGl1cztcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gRGlzdG9ydGlvbjtcbiIsIm1vZHVsZS5leHBvcnRzPXtcbiAgXCJmb3JtYXRcIjogMSxcbiAgXCJsYXN0X3VwZGF0ZWRcIjogXCIyMDE3LTA2LTAxVDIyOjMzOjQyWlwiLFxuICBcImRldmljZXNcIjogW1xuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiYXN1cy8qL05leHVzIDcvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiTmV4dXMgN1wiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDMyMC44LFxuICAgICAgICAzMjNcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDUwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJhc3VzLyovQVNVU19aMDBBRC8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJBU1VTX1owMEFEXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNDAzLFxuICAgICAgICA0MDQuNlxuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJHb29nbGUvKi9QaXhlbCBYTC8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJQaXhlbCBYTFwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDUzNy45LFxuICAgICAgICA1MzNcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiR29vZ2xlLyovUGl4ZWwvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiUGl4ZWxcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICA0MzIuNixcbiAgICAgICAgNDM2LjdcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiSFRDLyovSFRDNjQzNUxWVy8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJIVEM2NDM1TFZXXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNDQ5LjcsXG4gICAgICAgIDQ0My4zXG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcIkhUQy8qL0hUQyBPbmUgWEwvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiSFRDIE9uZSBYTFwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDMxNS4zLFxuICAgICAgICAzMTQuNlxuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJodGMvKi9OZXh1cyA5LypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIk5leHVzIDlcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogMjg5LFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiA1MDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiSFRDLyovSFRDIE9uZSBNOS8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJIVEMgT25lIE05XCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNDQyLjUsXG4gICAgICAgIDQ0My4zXG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiA1MDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiSFRDLyovSFRDIE9uZV9NOC8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJIVEMgT25lX004XCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNDQ5LjcsXG4gICAgICAgIDQ0Ny40XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiA1MDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiSFRDLyovSFRDIE9uZS8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJIVEMgT25lXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IDQ3Mi44LFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcIkh1YXdlaS8qL05leHVzIDZQLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIk5leHVzIDZQXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNTE1LjEsXG4gICAgICAgIDUxOFxuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJMR0UvKi9OZXh1cyA1WC8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJOZXh1cyA1WFwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDQyMixcbiAgICAgICAgNDE5LjlcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiTEdFLyovTEdNUzM0NS8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJMR01TMzQ1XCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgMjIxLjcsXG4gICAgICAgIDIxOS4xXG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiA1MDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiTEdFLyovTEctRDgwMC8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJMRy1EODAwXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNDIyLFxuICAgICAgICA0MjQuMVxuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogNTAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcIkxHRS8qL0xHLUQ4NTAvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiTEctRDg1MFwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDUzNy45LFxuICAgICAgICA1NDEuOVxuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogNTAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcIkxHRS8qL1ZTOTg1IDRHLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlZTOTg1IDRHXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNTM3LjksXG4gICAgICAgIDUzNS42XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcIkxHRS8qL05leHVzIDUvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiTmV4dXMgNSBCXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNDQyLjQsXG4gICAgICAgIDQ0NC44XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcIkxHRS8qL05leHVzIDQvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiTmV4dXMgNFwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDMxOS44LFxuICAgICAgICAzMTguNFxuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJMR0UvKi9MRy1QNzY5LypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIkxHLVA3NjlcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICAyNDAuNixcbiAgICAgICAgMjQ3LjVcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiTEdFLyovTEdNUzMyMy8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJMR01TMzIzXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgMjA2LjYsXG4gICAgICAgIDIwNC42XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcIkxHRS8qL0xHTFM5OTYvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiTEdMUzk5NlwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDQwMy40LFxuICAgICAgICA0MDEuNVxuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJNaWNyb21heC8qLzQ1NjBNTVgvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiNDU2ME1NWFwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDI0MCxcbiAgICAgICAgMjE5LjRcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiTWljcm9tYXgvKi9BMjUwLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIk1pY3JvbWF4IEEyNTBcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICA0ODAsXG4gICAgICAgIDQ0Ni40XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcIk1pY3JvbWF4LyovTWljcm9tYXggQVE0NTAxLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIk1pY3JvbWF4IEFRNDUwMVwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiAyNDAsXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDUwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJtb3Rvcm9sYS8qL0RST0lEIFJBWlIvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiRFJPSUQgUkFaUlwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDM2OC4xLFxuICAgICAgICAyNTYuN1xuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJtb3Rvcm9sYS8qL1hUODMwQy8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJYVDgzMENcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICAyNTQsXG4gICAgICAgIDI1NS45XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcIm1vdG9yb2xhLyovWFQxMDIxLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlhUMTAyMVwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDI1NCxcbiAgICAgICAgMjU2LjdcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDUwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJtb3Rvcm9sYS8qL1hUMTAyMy8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJYVDEwMjNcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICAyNTQsXG4gICAgICAgIDI1Ni43XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiA1MDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwibW90b3JvbGEvKi9YVDEwMjgvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiWFQxMDI4XCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgMzI2LjYsXG4gICAgICAgIDMyNy42XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcIm1vdG9yb2xhLyovWFQxMDM0LypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlhUMTAzNFwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDMyNi42LFxuICAgICAgICAzMjguNFxuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogNTAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcIm1vdG9yb2xhLyovWFQxMDUzLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlhUMTA1M1wiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDMxNS4zLFxuICAgICAgICAzMTYuMVxuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJtb3Rvcm9sYS8qL1hUMTU2Mi8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJYVDE1NjJcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICA0MDMuNCxcbiAgICAgICAgNDAyLjdcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwibW90b3JvbGEvKi9OZXh1cyA2LypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIk5leHVzIDYgQlwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDQ5NC4zLFxuICAgICAgICA0ODkuN1xuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJtb3Rvcm9sYS8qL1hUMTA2My8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJYVDEwNjNcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICAyOTUsXG4gICAgICAgIDI5Ni42XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcIm1vdG9yb2xhLyovWFQxMDY0LypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlhUMTA2NFwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDI5NSxcbiAgICAgICAgMjk1LjZcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDUwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJtb3Rvcm9sYS8qL1hUMTA5Mi8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJYVDEwOTJcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICA0MjIsXG4gICAgICAgIDQyNC4xXG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiA1MDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwibW90b3JvbGEvKi9YVDEwOTUvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiWFQxMDk1XCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNDIyLFxuICAgICAgICA0MjMuNFxuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJtb3Rvcm9sYS8qL0c0LypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIk1vdG8gRyAoNClcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogNDAxLFxuICAgICAgXCJid1wiOiA0LFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcIk9uZVBsdXMvKi9BMDAwMS8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJBMDAwMVwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDQwMy40LFxuICAgICAgICA0MDFcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiT25lUGx1cy8qL09ORSBFMTAwNS8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJPTkUgRTEwMDVcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICA0NDIuNCxcbiAgICAgICAgNDQxLjRcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiT25lUGx1cy8qL09ORSBBMjAwNS8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJPTkUgQTIwMDVcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICAzOTEuOSxcbiAgICAgICAgNDA1LjRcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiT1BQTy8qL1g5MDkvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiWDkwOVwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDQ0Mi40LFxuICAgICAgICA0NDQuMVxuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJzYW1zdW5nLyovR1QtSTkwODIvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiR1QtSTkwODJcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICAxODQuNyxcbiAgICAgICAgMTg1LjRcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwic2Ftc3VuZy8qL1NNLUczNjBQLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlNNLUczNjBQXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgMTk2LjcsXG4gICAgICAgIDIwNS40XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcInNhbXN1bmcvKi9OZXh1cyBTLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIk5leHVzIFNcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICAyMzQuNSxcbiAgICAgICAgMjI5LjhcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwic2Ftc3VuZy8qL0dULUk5MzAwLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIkdULUk5MzAwXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgMzA0LjgsXG4gICAgICAgIDMwMy45XG4gICAgICBdLFxuICAgICAgXCJid1wiOiA1LFxuICAgICAgXCJhY1wiOiA1MDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwic2Ftc3VuZy8qL1NNLVQyMzBOVS8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJTTS1UMjMwTlVcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogMjE2LFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiA1MDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwic2Ftc3VuZy8qL1NHSC1UMzk5LypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlNHSC1UMzk5XCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgMjE3LjcsXG4gICAgICAgIDIzMS40XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcInNhbXN1bmcvKi9TR0gtTTkxOS8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJTR0gtTTkxOVwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDQ0MC44LFxuICAgICAgICA0MzcuN1xuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJzYW1zdW5nLyovU00tTjkwMDUvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiU00tTjkwMDVcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICAzODYuNCxcbiAgICAgICAgMzg3XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiA1MDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwic2Ftc3VuZy8qL1NBTVNVTkctU00tTjkwMEEvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiU0FNU1VORy1TTS1OOTAwQVwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDM4Ni40LFxuICAgICAgICAzODcuN1xuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJzYW1zdW5nLyovR1QtSTk1MDAvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiR1QtSTk1MDBcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICA0NDIuNSxcbiAgICAgICAgNDQzLjNcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDUwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJzYW1zdW5nLyovR1QtSTk1MDUvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiR1QtSTk1MDVcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogNDM5LjQsXG4gICAgICBcImJ3XCI6IDQsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwic2Ftc3VuZy8qL1NNLUc5MDBGLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlNNLUc5MDBGXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNDE1LjYsXG4gICAgICAgIDQzMS42XG4gICAgICBdLFxuICAgICAgXCJid1wiOiA1LFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcInNhbXN1bmcvKi9TTS1HOTAwTS8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJTTS1HOTAwTVwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDQxNS42LFxuICAgICAgICA0MzEuNlxuICAgICAgXSxcbiAgICAgIFwiYndcIjogNSxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJzYW1zdW5nLyovU00tRzgwMEYvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiU00tRzgwMEZcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogMzI2LjgsXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwic2Ftc3VuZy8qL1NNLUc5MDZTLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlNNLUc5MDZTXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNTYyLjcsXG4gICAgICAgIDU3Mi40XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcInNhbXN1bmcvKi9HVC1JOTMwMC8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJHVC1JOTMwMFwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDMwNi43LFxuICAgICAgICAzMDQuOFxuICAgICAgXSxcbiAgICAgIFwiYndcIjogNSxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJzYW1zdW5nLyovU00tVDUzNS8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJTTS1UNTM1XCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgMTQyLjYsXG4gICAgICAgIDEzNi40XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiA1MDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwic2Ftc3VuZy8qL1NNLU45MjBDLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlNNLU45MjBDXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNTE1LjEsXG4gICAgICAgIDUxOC40XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcInNhbXN1bmcvKi9TTS1OOTIwVzgvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiU00tTjkyMFc4XCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNTE1LjEsXG4gICAgICAgIDUxOC40XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcInNhbXN1bmcvKi9HVC1JOTMwMEkvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiR1QtSTkzMDBJXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgMzA0LjgsXG4gICAgICAgIDMwNS44XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcInNhbXN1bmcvKi9HVC1JOTE5NS8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJHVC1JOTE5NVwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDI0OS40LFxuICAgICAgICAyNTYuN1xuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogNTAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcInNhbXN1bmcvKi9TUEgtTDUyMC8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJTUEgtTDUyMFwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDI0OS40LFxuICAgICAgICAyNTUuOVxuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJzYW1zdW5nLyovU0FNU1VORy1TR0gtSTcxNy8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJTQU1TVU5HLVNHSC1JNzE3XCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IDI4NS44LFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcInNhbXN1bmcvKi9TUEgtRDcxMC8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJTUEgtRDcxMFwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDIxNy43LFxuICAgICAgICAyMDQuMlxuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJzYW1zdW5nLyovR1QtTjcxMDAvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiR1QtTjcxMDBcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogMjY1LjEsXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwic2Ftc3VuZy8qL1NDSC1JNjA1LypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlNDSC1JNjA1XCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IDI2NS4xLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcInNhbXN1bmcvKi9HYWxheHkgTmV4dXMvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiR2FsYXh5IE5leHVzXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgMzE1LjMsXG4gICAgICAgIDMxNC4yXG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcInNhbXN1bmcvKi9TTS1OOTEwSC8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJTTS1OOTEwSFwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDUxNS4xLFxuICAgICAgICA1MThcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwic2Ftc3VuZy8qL1NNLU45MTBDLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlNNLU45MTBDXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNTE1LjIsXG4gICAgICAgIDUyMC4yXG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiA1MDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwic2Ftc3VuZy8qL1NNLUcxMzBNLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlNNLUcxMzBNXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgMTY1LjksXG4gICAgICAgIDE2NC44XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiA1MDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwic2Ftc3VuZy8qL1NNLUc5MjhJLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlNNLUc5MjhJXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNTE1LjEsXG4gICAgICAgIDUxOC40XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcInNhbXN1bmcvKi9TTS1HOTIwRi8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJTTS1HOTIwRlwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiA1ODAuNixcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogNTAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcInNhbXN1bmcvKi9TTS1HOTIwUC8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJTTS1HOTIwUFwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDUyMi41LFxuICAgICAgICA1NzdcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwic2Ftc3VuZy8qL1NNLUc5MjVGLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlNNLUc5MjVGXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IDU4MC42LFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiA1MDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwic2Ftc3VuZy8qL1NNLUc5MjVWLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlNNLUc5MjVWXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNTIyLjUsXG4gICAgICAgIDU3Ni42XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcInNhbXN1bmcvKi9TTS1HOTMwRi8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJTTS1HOTMwRlwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiA1NzYuNixcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJzYW1zdW5nLyovU00tRzkzNUYvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiU00tRzkzNUZcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogNTMzLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiA1MDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiU29ueS8qL0M2OTAzLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIkM2OTAzXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNDQyLjUsXG4gICAgICAgIDQ0My4zXG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiA1MDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiU29ueS8qL0Q2NjUzLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIkQ2NjUzXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgNDI4LjYsXG4gICAgICAgIDQyNy42XG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJhbmRyb2lkXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwibWRtaFwiOiBcIlNvbnkvKi9FNjY1My8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJFNjY1M1wiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDQyOC42LFxuICAgICAgICA0MjUuN1xuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJTb255LyovRTY4NTMvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiRTY4NTNcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICA0MDMuNCxcbiAgICAgICAgNDAxLjlcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiU29ueS8qL1NHUDMyMS8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJTR1AzMjFcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICAyMjQuNyxcbiAgICAgICAgMjI0LjFcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDUwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJUQ1QvKi9BTENBVEVMIE9ORSBUT1VDSCBGaWVyY2UvKlwiXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBcInVhXCI6IFwiQUxDQVRFTCBPTkUgVE9VQ0ggRmllcmNlXCJcbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgMjQwLFxuICAgICAgICAyNDcuNVxuICAgICAgXSxcbiAgICAgIFwiYndcIjogMyxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiYW5kcm9pZFwiLFxuICAgICAgXCJydWxlc1wiOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBcIm1kbWhcIjogXCJUSEwvKi90aGwgNTAwMC8qXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIFwidWFcIjogXCJ0aGwgNTAwMFwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDQ4MCxcbiAgICAgICAgNDQzLjNcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9LFxuICAgIHtcbiAgICAgIFwidHlwZVwiOiBcImFuZHJvaWRcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJtZG1oXCI6IFwiWlRFLyovWlRFIEJsYWRlIEwyLypcIlxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgXCJ1YVwiOiBcIlpURSBCbGFkZSBMMlwiXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiAyNDAsXG4gICAgICBcImJ3XCI6IDMsXG4gICAgICBcImFjXCI6IDUwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiaW9zXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwicmVzXCI6IFtcbiAgICAgICAgICAgIDY0MCxcbiAgICAgICAgICAgIDk2MFxuICAgICAgICAgIF1cbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgMzI1LjEsXG4gICAgICAgIDMyOC40XG4gICAgICBdLFxuICAgICAgXCJid1wiOiA0LFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJpb3NcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJyZXNcIjogW1xuICAgICAgICAgICAgNjQwLFxuICAgICAgICAgICAgMTEzNlxuICAgICAgICAgIF1cbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IFtcbiAgICAgICAgMzE3LjEsXG4gICAgICAgIDMyMC4yXG4gICAgICBdLFxuICAgICAgXCJid1wiOiAzLFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJpb3NcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJyZXNcIjogW1xuICAgICAgICAgICAgNzUwLFxuICAgICAgICAgICAgMTMzNFxuICAgICAgICAgIF1cbiAgICAgICAgfVxuICAgICAgXSxcbiAgICAgIFwiZHBpXCI6IDMyNi40LFxuICAgICAgXCJid1wiOiA0LFxuICAgICAgXCJhY1wiOiAxMDAwXG4gICAgfSxcbiAgICB7XG4gICAgICBcInR5cGVcIjogXCJpb3NcIixcbiAgICAgIFwicnVsZXNcIjogW1xuICAgICAgICB7XG4gICAgICAgICAgXCJyZXNcIjogW1xuICAgICAgICAgICAgMTI0MixcbiAgICAgICAgICAgIDIyMDhcbiAgICAgICAgICBdXG4gICAgICAgIH1cbiAgICAgIF0sXG4gICAgICBcImRwaVwiOiBbXG4gICAgICAgIDQ1My42LFxuICAgICAgICA0NTguNFxuICAgICAgXSxcbiAgICAgIFwiYndcIjogNCxcbiAgICAgIFwiYWNcIjogMTAwMFxuICAgIH0sXG4gICAge1xuICAgICAgXCJ0eXBlXCI6IFwiaW9zXCIsXG4gICAgICBcInJ1bGVzXCI6IFtcbiAgICAgICAge1xuICAgICAgICAgIFwicmVzXCI6IFtcbiAgICAgICAgICAgIDExMjUsXG4gICAgICAgICAgICAyMDAxXG4gICAgICAgICAgXVxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgXCJkcGlcIjogW1xuICAgICAgICA0MTAuOSxcbiAgICAgICAgNDE1LjRcbiAgICAgIF0sXG4gICAgICBcImJ3XCI6IDQsXG4gICAgICBcImFjXCI6IDEwMDBcbiAgICB9XG4gIF1cbn0iLCIvKlxuICogQ29weXJpZ2h0IDIwMTUgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqL1xuXG4vLyBPZmZsaW5lIGNhY2hlIG9mIHRoZSBEUERCLCB0byBiZSB1c2VkIHVudGlsIHdlIGxvYWQgdGhlIG9ubGluZSBvbmUgKGFuZFxuLy8gYXMgYSBmYWxsYmFjayBpbiBjYXNlIHdlIGNhbid0IGxvYWQgdGhlIG9ubGluZSBvbmUpLlxudmFyIERQREJfQ0FDSEUgPSByZXF1aXJlKCcuL2RwZGIuanNvbicpO1xudmFyIFV0aWwgPSByZXF1aXJlKCcuLi91dGlsLmpzJyk7XG5cbi8vIE9ubGluZSBEUERCIFVSTC5cbnZhciBPTkxJTkVfRFBEQl9VUkwgPVxuICAnaHR0cHM6Ly9kcGRiLndlYnZyLnJvY2tzL2RwZGIuanNvbic7XG5cbi8qKlxuICogQ2FsY3VsYXRlcyBkZXZpY2UgcGFyYW1ldGVycyBiYXNlZCBvbiB0aGUgRFBEQiAoRGV2aWNlIFBhcmFtZXRlciBEYXRhYmFzZSkuXG4gKiBJbml0aWFsbHksIHVzZXMgdGhlIGNhY2hlZCBEUERCIHZhbHVlcy5cbiAqXG4gKiBJZiBmZXRjaE9ubGluZSA9PSB0cnVlLCB0aGVuIHRoaXMgb2JqZWN0IHRyaWVzIHRvIGZldGNoIHRoZSBvbmxpbmUgdmVyc2lvblxuICogb2YgdGhlIERQREIgYW5kIHVwZGF0ZXMgdGhlIGRldmljZSBpbmZvIGlmIGEgYmV0dGVyIG1hdGNoIGlzIGZvdW5kLlxuICogQ2FsbHMgdGhlIG9uRGV2aWNlUGFyYW1zVXBkYXRlZCBjYWxsYmFjayB3aGVuIHRoZXJlIGlzIGFuIHVwZGF0ZSB0byB0aGVcbiAqIGRldmljZSBpbmZvcm1hdGlvbi5cbiAqL1xuZnVuY3Rpb24gRHBkYihmZXRjaE9ubGluZSwgb25EZXZpY2VQYXJhbXNVcGRhdGVkKSB7XG4gIC8vIFN0YXJ0IHdpdGggdGhlIG9mZmxpbmUgRFBEQiBjYWNoZSB3aGlsZSB3ZSBhcmUgbG9hZGluZyB0aGUgcmVhbCBvbmUuXG4gIHRoaXMuZHBkYiA9IERQREJfQ0FDSEU7XG5cbiAgLy8gQ2FsY3VsYXRlIGRldmljZSBwYXJhbXMgYmFzZWQgb24gdGhlIG9mZmxpbmUgdmVyc2lvbiBvZiB0aGUgRFBEQi5cbiAgdGhpcy5yZWNhbGN1bGF0ZURldmljZVBhcmFtc18oKTtcblxuICAvLyBYSFIgdG8gZmV0Y2ggb25saW5lIERQREIgZmlsZSwgaWYgcmVxdWVzdGVkLlxuICBpZiAoZmV0Y2hPbmxpbmUpIHtcbiAgICAvLyBTZXQgdGhlIGNhbGxiYWNrLlxuICAgIHRoaXMub25EZXZpY2VQYXJhbXNVcGRhdGVkID0gb25EZXZpY2VQYXJhbXNVcGRhdGVkO1xuXG4gICAgdmFyIHhociA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpO1xuICAgIHZhciBvYmogPSB0aGlzO1xuICAgIHhoci5vcGVuKCdHRVQnLCBPTkxJTkVfRFBEQl9VUkwsIHRydWUpO1xuICAgIHhoci5hZGRFdmVudExpc3RlbmVyKCdsb2FkJywgZnVuY3Rpb24oKSB7XG4gICAgICBvYmoubG9hZGluZyA9IGZhbHNlO1xuICAgICAgaWYgKHhoci5zdGF0dXMgPj0gMjAwICYmIHhoci5zdGF0dXMgPD0gMjk5KSB7XG4gICAgICAgIC8vIFN1Y2Nlc3MuXG4gICAgICAgIG9iai5kcGRiID0gSlNPTi5wYXJzZSh4aHIucmVzcG9uc2UpO1xuICAgICAgICBvYmoucmVjYWxjdWxhdGVEZXZpY2VQYXJhbXNfKCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBFcnJvciBsb2FkaW5nIHRoZSBEUERCLlxuICAgICAgICBjb25zb2xlLmVycm9yKCdFcnJvciBsb2FkaW5nIG9ubGluZSBEUERCIScpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHhoci5zZW5kKCk7XG4gIH1cbn1cblxuLy8gUmV0dXJucyB0aGUgY3VycmVudCBkZXZpY2UgcGFyYW1ldGVycy5cbkRwZGIucHJvdG90eXBlLmdldERldmljZVBhcmFtcyA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gdGhpcy5kZXZpY2VQYXJhbXM7XG59O1xuXG4vLyBSZWNhbGN1bGF0ZXMgdGhpcyBkZXZpY2UncyBwYXJhbWV0ZXJzIGJhc2VkIG9uIHRoZSBEUERCLlxuRHBkYi5wcm90b3R5cGUucmVjYWxjdWxhdGVEZXZpY2VQYXJhbXNfID0gZnVuY3Rpb24oKSB7XG4gIHZhciBuZXdEZXZpY2VQYXJhbXMgPSB0aGlzLmNhbGNEZXZpY2VQYXJhbXNfKCk7XG4gIGlmIChuZXdEZXZpY2VQYXJhbXMpIHtcbiAgICB0aGlzLmRldmljZVBhcmFtcyA9IG5ld0RldmljZVBhcmFtcztcbiAgICAvLyBJbnZva2UgY2FsbGJhY2ssIGlmIGl0IGlzIHNldC5cbiAgICBpZiAodGhpcy5vbkRldmljZVBhcmFtc1VwZGF0ZWQpIHtcbiAgICAgIHRoaXMub25EZXZpY2VQYXJhbXNVcGRhdGVkKHRoaXMuZGV2aWNlUGFyYW1zKTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgY29uc29sZS5lcnJvcignRmFpbGVkIHRvIHJlY2FsY3VsYXRlIGRldmljZSBwYXJhbWV0ZXJzLicpO1xuICB9XG59O1xuXG4vLyBSZXR1cm5zIGEgRGV2aWNlUGFyYW1zIG9iamVjdCB0aGF0IHJlcHJlc2VudHMgdGhlIGJlc3QgZ3Vlc3MgYXMgdG8gdGhpc1xuLy8gZGV2aWNlJ3MgcGFyYW1ldGVycy4gQ2FuIHJldHVybiBudWxsIGlmIHRoZSBkZXZpY2UgZG9lcyBub3QgbWF0Y2ggYW55XG4vLyBrbm93biBkZXZpY2VzLlxuRHBkYi5wcm90b3R5cGUuY2FsY0RldmljZVBhcmFtc18gPSBmdW5jdGlvbigpIHtcbiAgdmFyIGRiID0gdGhpcy5kcGRiOyAvLyBzaG9ydGhhbmRcbiAgaWYgKCFkYikge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0RQREIgbm90IGF2YWlsYWJsZS4nKTtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBpZiAoZGIuZm9ybWF0ICE9IDEpIHtcbiAgICBjb25zb2xlLmVycm9yKCdEUERCIGhhcyB1bmV4cGVjdGVkIGZvcm1hdCB2ZXJzaW9uLicpO1xuICAgIHJldHVybiBudWxsO1xuICB9XG4gIGlmICghZGIuZGV2aWNlcyB8fCAhZGIuZGV2aWNlcy5sZW5ndGgpIHtcbiAgICBjb25zb2xlLmVycm9yKCdEUERCIGRvZXMgbm90IGhhdmUgYSBkZXZpY2VzIHNlY3Rpb24uJyk7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvLyBHZXQgdGhlIGFjdHVhbCB1c2VyIGFnZW50IGFuZCBzY3JlZW4gZGltZW5zaW9ucyBpbiBwaXhlbHMuXG4gIHZhciB1c2VyQWdlbnQgPSBuYXZpZ2F0b3IudXNlckFnZW50IHx8IG5hdmlnYXRvci52ZW5kb3IgfHwgd2luZG93Lm9wZXJhO1xuICB2YXIgd2lkdGggPSBVdGlsLmdldFNjcmVlbldpZHRoKCk7XG4gIHZhciBoZWlnaHQgPSBVdGlsLmdldFNjcmVlbkhlaWdodCgpO1xuXG4gIGlmICghZGIuZGV2aWNlcykge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0RQREIgaGFzIG5vIGRldmljZXMgc2VjdGlvbi4nKTtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIGZvciAodmFyIGkgPSAwOyBpIDwgZGIuZGV2aWNlcy5sZW5ndGg7IGkrKykge1xuICAgIHZhciBkZXZpY2UgPSBkYi5kZXZpY2VzW2ldO1xuICAgIGlmICghZGV2aWNlLnJ1bGVzKSB7XG4gICAgICBjb25zb2xlLndhcm4oJ0RldmljZVsnICsgaSArICddIGhhcyBubyBydWxlcyBzZWN0aW9uLicpO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgaWYgKGRldmljZS50eXBlICE9ICdpb3MnICYmIGRldmljZS50eXBlICE9ICdhbmRyb2lkJykge1xuICAgICAgY29uc29sZS53YXJuKCdEZXZpY2VbJyArIGkgKyAnXSBoYXMgaW52YWxpZCB0eXBlLicpO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gU2VlIGlmIHRoaXMgZGV2aWNlIGlzIG9mIHRoZSBhcHByb3ByaWF0ZSB0eXBlLlxuICAgIGlmIChVdGlsLmlzSU9TKCkgIT0gKGRldmljZS50eXBlID09ICdpb3MnKSkgY29udGludWU7XG5cbiAgICAvLyBTZWUgaWYgdGhpcyBkZXZpY2UgbWF0Y2hlcyBhbnkgb2YgdGhlIHJ1bGVzOlxuICAgIHZhciBtYXRjaGVkID0gZmFsc2U7XG4gICAgZm9yICh2YXIgaiA9IDA7IGogPCBkZXZpY2UucnVsZXMubGVuZ3RoOyBqKyspIHtcbiAgICAgIHZhciBydWxlID0gZGV2aWNlLnJ1bGVzW2pdO1xuICAgICAgaWYgKHRoaXMubWF0Y2hSdWxlXyhydWxlLCB1c2VyQWdlbnQsIHdpZHRoLCBoZWlnaHQpKSB7XG4gICAgICAgIG1hdGNoZWQgPSB0cnVlO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKCFtYXRjaGVkKSBjb250aW51ZTtcblxuICAgIC8vIGRldmljZS5kcGkgbWlnaHQgYmUgYW4gYXJyYXkgb2YgWyB4ZHBpLCB5ZHBpXSBvciBqdXN0IGEgc2NhbGFyLlxuICAgIHZhciB4ZHBpID0gZGV2aWNlLmRwaVswXSB8fCBkZXZpY2UuZHBpO1xuICAgIHZhciB5ZHBpID0gZGV2aWNlLmRwaVsxXSB8fCBkZXZpY2UuZHBpO1xuXG4gICAgcmV0dXJuIG5ldyBEZXZpY2VQYXJhbXMoeyB4ZHBpOiB4ZHBpLCB5ZHBpOiB5ZHBpLCBiZXZlbE1tOiBkZXZpY2UuYncgfSk7XG4gIH1cblxuICBjb25zb2xlLndhcm4oJ05vIERQREIgZGV2aWNlIG1hdGNoLicpO1xuICByZXR1cm4gbnVsbDtcbn07XG5cbkRwZGIucHJvdG90eXBlLm1hdGNoUnVsZV8gPSBmdW5jdGlvbihydWxlLCB1YSwgc2NyZWVuV2lkdGgsIHNjcmVlbkhlaWdodCkge1xuICAvLyBXZSBjYW4gb25seSBtYXRjaCAndWEnIGFuZCAncmVzJyBydWxlcywgbm90IG90aGVyIHR5cGVzIGxpa2UgJ21kbWgnXG4gIC8vICh3aGljaCBhcmUgbWVhbnQgZm9yIG5hdGl2ZSBwbGF0Zm9ybXMpLlxuICBpZiAoIXJ1bGUudWEgJiYgIXJ1bGUucmVzKSByZXR1cm4gZmFsc2U7XG5cbiAgLy8gSWYgb3VyIHVzZXIgYWdlbnQgc3RyaW5nIGRvZXNuJ3QgY29udGFpbiB0aGUgaW5kaWNhdGVkIHVzZXIgYWdlbnQgc3RyaW5nLFxuICAvLyB0aGUgbWF0Y2ggZmFpbHMuXG4gIGlmIChydWxlLnVhICYmIHVhLmluZGV4T2YocnVsZS51YSkgPCAwKSByZXR1cm4gZmFsc2U7XG5cbiAgLy8gSWYgdGhlIHJ1bGUgc3BlY2lmaWVzIHNjcmVlbiBkaW1lbnNpb25zIHRoYXQgZG9uJ3QgY29ycmVzcG9uZCB0byBvdXJzLFxuICAvLyB0aGUgbWF0Y2ggZmFpbHMuXG4gIGlmIChydWxlLnJlcykge1xuICAgIGlmICghcnVsZS5yZXNbMF0gfHwgIXJ1bGUucmVzWzFdKSByZXR1cm4gZmFsc2U7XG4gICAgdmFyIHJlc1ggPSBydWxlLnJlc1swXTtcbiAgICB2YXIgcmVzWSA9IHJ1bGUucmVzWzFdO1xuICAgIC8vIENvbXBhcmUgbWluIGFuZCBtYXggc28gYXMgdG8gbWFrZSB0aGUgb3JkZXIgbm90IG1hdHRlciwgaS5lLiwgaXQgc2hvdWxkXG4gICAgLy8gYmUgdHJ1ZSB0aGF0IDY0MHg0ODAgPT0gNDgweDY0MC5cbiAgICBpZiAoTWF0aC5taW4oc2NyZWVuV2lkdGgsIHNjcmVlbkhlaWdodCkgIT0gTWF0aC5taW4ocmVzWCwgcmVzWSkgfHxcbiAgICAgICAgKE1hdGgubWF4KHNjcmVlbldpZHRoLCBzY3JlZW5IZWlnaHQpICE9IE1hdGgubWF4KHJlc1gsIHJlc1kpKSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiB0cnVlO1xufVxuXG5mdW5jdGlvbiBEZXZpY2VQYXJhbXMocGFyYW1zKSB7XG4gIHRoaXMueGRwaSA9IHBhcmFtcy54ZHBpO1xuICB0aGlzLnlkcGkgPSBwYXJhbXMueWRwaTtcbiAgdGhpcy5iZXZlbE1tID0gcGFyYW1zLmJldmVsTW07XG59XG5cbm1vZHVsZS5leHBvcnRzID0gRHBkYjtcbiIsIi8qXG4gKiBDb3B5cmlnaHQgMjAxNSBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICovXG52YXIgVXRpbCA9IHJlcXVpcmUoJy4vdXRpbC5qcycpO1xudmFyIFdlYlZSUG9seWZpbGwgPSByZXF1aXJlKCcuL3dlYnZyLXBvbHlmaWxsLmpzJykuV2ViVlJQb2x5ZmlsbDtcblxuLy8gSW5pdGlhbGl6ZSBhIFdlYlZSQ29uZmlnIGp1c3QgaW4gY2FzZS5cbndpbmRvdy5XZWJWUkNvbmZpZyA9IFV0aWwuZXh0ZW5kKHtcbiAgLy8gRm9yY2VzIGF2YWlsYWJpbGl0eSBvZiBWUiBtb2RlLCBldmVuIGZvciBub24tbW9iaWxlIGRldmljZXMuXG4gIEZPUkNFX0VOQUJMRV9WUjogZmFsc2UsXG5cbiAgLy8gQ29tcGxlbWVudGFyeSBmaWx0ZXIgY29lZmZpY2llbnQuIDAgZm9yIGFjY2VsZXJvbWV0ZXIsIDEgZm9yIGd5cm8uXG4gIEtfRklMVEVSOiAwLjk4LFxuXG4gIC8vIEhvdyBmYXIgaW50byB0aGUgZnV0dXJlIHRvIHByZWRpY3QgZHVyaW5nIGZhc3QgbW90aW9uIChpbiBzZWNvbmRzKS5cbiAgUFJFRElDVElPTl9USU1FX1M6IDAuMDQwLFxuXG4gIC8vIEZsYWcgdG8gZW5hYmxlIHRvdWNoIHBhbm5lci4gSW4gY2FzZSB5b3UgaGF2ZSB5b3VyIG93biB0b3VjaCBjb250cm9scy5cbiAgVE9VQ0hfUEFOTkVSX0RJU0FCTEVEOiB0cnVlLFxuXG4gIC8vIEZsYWcgdG8gZGlzYWJsZWQgdGhlIFVJIGluIFZSIE1vZGUuXG4gIENBUkRCT0FSRF9VSV9ESVNBQkxFRDogZmFsc2UsIC8vIERlZmF1bHQ6IGZhbHNlXG5cbiAgLy8gRmxhZyB0byBkaXNhYmxlIHRoZSBpbnN0cnVjdGlvbnMgdG8gcm90YXRlIHlvdXIgZGV2aWNlLlxuICBST1RBVEVfSU5TVFJVQ1RJT05TX0RJU0FCTEVEOiBmYWxzZSwgLy8gRGVmYXVsdDogZmFsc2UuXG5cbiAgLy8gRW5hYmxlIHlhdyBwYW5uaW5nIG9ubHksIGRpc2FibGluZyByb2xsIGFuZCBwaXRjaC4gVGhpcyBjYW4gYmUgdXNlZnVsXG4gIC8vIGZvciBwYW5vcmFtYXMgd2l0aCBub3RoaW5nIGludGVyZXN0aW5nIGFib3ZlIG9yIGJlbG93LlxuICBZQVdfT05MWTogZmFsc2UsXG5cbiAgLy8gVG8gZGlzYWJsZSBrZXlib2FyZCBhbmQgbW91c2UgY29udHJvbHMsIGlmIHlvdSB3YW50IHRvIHVzZSB5b3VyIG93blxuICAvLyBpbXBsZW1lbnRhdGlvbi5cbiAgTU9VU0VfS0VZQk9BUkRfQ09OVFJPTFNfRElTQUJMRUQ6IGZhbHNlLFxuXG4gIC8vIFByZXZlbnQgdGhlIHBvbHlmaWxsIGZyb20gaW5pdGlhbGl6aW5nIGltbWVkaWF0ZWx5LiBSZXF1aXJlcyB0aGUgYXBwXG4gIC8vIHRvIGNhbGwgSW5pdGlhbGl6ZVdlYlZSUG9seWZpbGwoKSBiZWZvcmUgaXQgY2FuIGJlIHVzZWQuXG4gIERFRkVSX0lOSVRJQUxJWkFUSU9OOiBmYWxzZSxcblxuICAvLyBFbmFibGUgdGhlIGRlcHJlY2F0ZWQgdmVyc2lvbiBvZiB0aGUgQVBJIChuYXZpZ2F0b3IuZ2V0VlJEZXZpY2VzKS5cbiAgRU5BQkxFX0RFUFJFQ0FURURfQVBJOiBmYWxzZSxcblxuICAvLyBTY2FsZXMgdGhlIHJlY29tbWVuZGVkIGJ1ZmZlciBzaXplIHJlcG9ydGVkIGJ5IFdlYlZSLCB3aGljaCBjYW4gaW1wcm92ZVxuICAvLyBwZXJmb3JtYW5jZS5cbiAgLy8gVVBEQVRFKDIwMTYtMDUtMDMpOiBTZXR0aW5nIHRoaXMgdG8gMC41IGJ5IGRlZmF1bHQgc2luY2UgMS4wIGRvZXMgbm90XG4gIC8vIHBlcmZvcm0gd2VsbCBvbiBtYW55IG1vYmlsZSBkZXZpY2VzLlxuICBCVUZGRVJfU0NBTEU6IDAuNSxcblxuICAvLyBBbGxvdyBWUkRpc3BsYXkuc3VibWl0RnJhbWUgdG8gY2hhbmdlIGdsIGJpbmRpbmdzLCB3aGljaCBpcyBtb3JlXG4gIC8vIGVmZmljaWVudCBpZiB0aGUgYXBwbGljYXRpb24gY29kZSB3aWxsIHJlLWJpbmQgaXRzIHJlc291cmNlcyBvbiB0aGVcbiAgLy8gbmV4dCBmcmFtZSBhbnl3YXkuIFRoaXMgaGFzIGJlZW4gc2VlbiB0byBjYXVzZSByZW5kZXJpbmcgZ2xpdGNoZXMgd2l0aFxuICAvLyBUSFJFRS5qcy5cbiAgLy8gRGlydHkgYmluZGluZ3MgaW5jbHVkZTogZ2wuRlJBTUVCVUZGRVJfQklORElORywgZ2wuQ1VSUkVOVF9QUk9HUkFNLFxuICAvLyBnbC5BUlJBWV9CVUZGRVJfQklORElORywgZ2wuRUxFTUVOVF9BUlJBWV9CVUZGRVJfQklORElORyxcbiAgLy8gYW5kIGdsLlRFWFRVUkVfQklORElOR18yRCBmb3IgdGV4dHVyZSB1bml0IDAuXG4gIERJUlRZX1NVQk1JVF9GUkFNRV9CSU5ESU5HUzogZmFsc2UsXG5cbiAgLy8gV2hlbiBzZXQgdG8gdHJ1ZSwgdGhpcyB3aWxsIGNhdXNlIGEgcG9seWZpbGxlZCBWUkRpc3BsYXkgdG8gYWx3YXlzIGJlXG4gIC8vIGFwcGVuZGVkIHRvIHRoZSBsaXN0IHJldHVybmVkIGJ5IG5hdmlnYXRvci5nZXRWUkRpc3BsYXlzKCksIGV2ZW4gaWYgdGhhdFxuICAvLyBsaXN0IGluY2x1ZGVzIGEgbmF0aXZlIFZSRGlzcGxheS5cbiAgQUxXQVlTX0FQUEVORF9QT0xZRklMTF9ESVNQTEFZOiBmYWxzZSxcblxuICAvLyBUaGVyZSBhcmUgdmVyc2lvbnMgb2YgQ2hyb21lIChNNTgtTTYwPykgd2hlcmUgdGhlIG5hdGl2ZSBXZWJWUiBBUEkgZXhpc3RzLFxuICAvLyBhbmQgaW5zdGVhZCBvZiByZXR1cm5pbmcgMCBWUiBkaXNwbGF5cyB3aGVuIG5vbmUgYXJlIGRldGVjdGVkLFxuICAvLyBgbmF2aWdhdG9yLmdldFZSRGlzcGxheXMoKWAncyBwcm9taXNlIG5ldmVyIHJlc29sdmVzLiBUaGlzIHJlc3VsdHNcbiAgLy8gaW4gdGhlIHBvbHlmaWxsIGhhbmdpbmcgYW5kIG5vdCBiZWluZyBhYmxlIHRvIHByb3ZpZGUgZmFsbGJhY2tcbiAgLy8gZGlzcGxheXMsIHNvIHNldCBhIHRpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIHRvIHN0b3Agd2FpdGluZyBmb3IgYSByZXNwb25zZVxuICAvLyBhbmQganVzdCB1c2UgcG9seWZpbGxlZCBkaXNwbGF5cy5cbiAgLy8gaHR0cHM6Ly9idWdzLmNocm9taXVtLm9yZy9wL2Nocm9taXVtL2lzc3Vlcy9kZXRhaWw/aWQ9NzI3OTY5XG4gIEdFVF9WUl9ESVNQTEFZU19USU1FT1VUOiAxMDAwLFxufSwgd2luZG93LldlYlZSQ29uZmlnKTtcblxuaWYgKCF3aW5kb3cuV2ViVlJDb25maWcuREVGRVJfSU5JVElBTElaQVRJT04pIHtcbiAgbmV3IFdlYlZSUG9seWZpbGwoKTtcbn0gZWxzZSB7XG4gIHdpbmRvdy5Jbml0aWFsaXplV2ViVlJQb2x5ZmlsbCA9IGZ1bmN0aW9uKCkge1xuICAgIG5ldyBXZWJWUlBvbHlmaWxsKCk7XG4gIH1cbn1cblxud2luZG93LldlYlZSUG9seWZpbGwgPSBXZWJWUlBvbHlmaWxsO1xuIiwiLypcbiAqIENvcHlyaWdodCAyMDE2IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKi9cblxudmFyIE1hdGhVdGlsID0gd2luZG93Lk1hdGhVdGlsIHx8IHt9O1xuXG5NYXRoVXRpbC5kZWdUb1JhZCA9IE1hdGguUEkgLyAxODA7XG5NYXRoVXRpbC5yYWRUb0RlZyA9IDE4MCAvIE1hdGguUEk7XG5cbi8vIFNvbWUgbWluaW1hbCBtYXRoIGZ1bmN0aW9uYWxpdHkgYm9ycm93ZWQgZnJvbSBUSFJFRS5NYXRoIGFuZCBzdHJpcHBlZCBkb3duXG4vLyBmb3IgdGhlIHB1cnBvc2VzIG9mIHRoaXMgbGlicmFyeS5cblxuXG5NYXRoVXRpbC5WZWN0b3IyID0gZnVuY3Rpb24gKCB4LCB5ICkge1xuICB0aGlzLnggPSB4IHx8IDA7XG4gIHRoaXMueSA9IHkgfHwgMDtcbn07XG5cbk1hdGhVdGlsLlZlY3RvcjIucHJvdG90eXBlID0ge1xuICBjb25zdHJ1Y3RvcjogTWF0aFV0aWwuVmVjdG9yMixcblxuICBzZXQ6IGZ1bmN0aW9uICggeCwgeSApIHtcbiAgICB0aGlzLnggPSB4O1xuICAgIHRoaXMueSA9IHk7XG5cbiAgICByZXR1cm4gdGhpcztcbiAgfSxcblxuICBjb3B5OiBmdW5jdGlvbiAoIHYgKSB7XG4gICAgdGhpcy54ID0gdi54O1xuICAgIHRoaXMueSA9IHYueTtcblxuICAgIHJldHVybiB0aGlzO1xuICB9LFxuXG4gIHN1YlZlY3RvcnM6IGZ1bmN0aW9uICggYSwgYiApIHtcbiAgICB0aGlzLnggPSBhLnggLSBiLng7XG4gICAgdGhpcy55ID0gYS55IC0gYi55O1xuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH0sXG59O1xuXG5NYXRoVXRpbC5WZWN0b3IzID0gZnVuY3Rpb24gKCB4LCB5LCB6ICkge1xuICB0aGlzLnggPSB4IHx8IDA7XG4gIHRoaXMueSA9IHkgfHwgMDtcbiAgdGhpcy56ID0geiB8fCAwO1xufTtcblxuTWF0aFV0aWwuVmVjdG9yMy5wcm90b3R5cGUgPSB7XG4gIGNvbnN0cnVjdG9yOiBNYXRoVXRpbC5WZWN0b3IzLFxuXG4gIHNldDogZnVuY3Rpb24gKCB4LCB5LCB6ICkge1xuICAgIHRoaXMueCA9IHg7XG4gICAgdGhpcy55ID0geTtcbiAgICB0aGlzLnogPSB6O1xuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH0sXG5cbiAgY29weTogZnVuY3Rpb24gKCB2ICkge1xuICAgIHRoaXMueCA9IHYueDtcbiAgICB0aGlzLnkgPSB2Lnk7XG4gICAgdGhpcy56ID0gdi56O1xuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH0sXG5cbiAgbGVuZ3RoOiBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIE1hdGguc3FydCggdGhpcy54ICogdGhpcy54ICsgdGhpcy55ICogdGhpcy55ICsgdGhpcy56ICogdGhpcy56ICk7XG4gIH0sXG5cbiAgbm9ybWFsaXplOiBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIHNjYWxhciA9IHRoaXMubGVuZ3RoKCk7XG5cbiAgICBpZiAoIHNjYWxhciAhPT0gMCApIHtcbiAgICAgIHZhciBpbnZTY2FsYXIgPSAxIC8gc2NhbGFyO1xuXG4gICAgICB0aGlzLm11bHRpcGx5U2NhbGFyKGludlNjYWxhcik7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMueCA9IDA7XG4gICAgICB0aGlzLnkgPSAwO1xuICAgICAgdGhpcy56ID0gMDtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcztcbiAgfSxcblxuICBtdWx0aXBseVNjYWxhcjogZnVuY3Rpb24gKCBzY2FsYXIgKSB7XG4gICAgdGhpcy54ICo9IHNjYWxhcjtcbiAgICB0aGlzLnkgKj0gc2NhbGFyO1xuICAgIHRoaXMueiAqPSBzY2FsYXI7XG4gIH0sXG5cbiAgYXBwbHlRdWF0ZXJuaW9uOiBmdW5jdGlvbiAoIHEgKSB7XG4gICAgdmFyIHggPSB0aGlzLng7XG4gICAgdmFyIHkgPSB0aGlzLnk7XG4gICAgdmFyIHogPSB0aGlzLno7XG5cbiAgICB2YXIgcXggPSBxLng7XG4gICAgdmFyIHF5ID0gcS55O1xuICAgIHZhciBxeiA9IHEuejtcbiAgICB2YXIgcXcgPSBxLnc7XG5cbiAgICAvLyBjYWxjdWxhdGUgcXVhdCAqIHZlY3RvclxuICAgIHZhciBpeCA9ICBxdyAqIHggKyBxeSAqIHogLSBxeiAqIHk7XG4gICAgdmFyIGl5ID0gIHF3ICogeSArIHF6ICogeCAtIHF4ICogejtcbiAgICB2YXIgaXogPSAgcXcgKiB6ICsgcXggKiB5IC0gcXkgKiB4O1xuICAgIHZhciBpdyA9IC0gcXggKiB4IC0gcXkgKiB5IC0gcXogKiB6O1xuXG4gICAgLy8gY2FsY3VsYXRlIHJlc3VsdCAqIGludmVyc2UgcXVhdFxuICAgIHRoaXMueCA9IGl4ICogcXcgKyBpdyAqIC0gcXggKyBpeSAqIC0gcXogLSBpeiAqIC0gcXk7XG4gICAgdGhpcy55ID0gaXkgKiBxdyArIGl3ICogLSBxeSArIGl6ICogLSBxeCAtIGl4ICogLSBxejtcbiAgICB0aGlzLnogPSBpeiAqIHF3ICsgaXcgKiAtIHF6ICsgaXggKiAtIHF5IC0gaXkgKiAtIHF4O1xuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH0sXG5cbiAgZG90OiBmdW5jdGlvbiAoIHYgKSB7XG4gICAgcmV0dXJuIHRoaXMueCAqIHYueCArIHRoaXMueSAqIHYueSArIHRoaXMueiAqIHYuejtcbiAgfSxcblxuICBjcm9zc1ZlY3RvcnM6IGZ1bmN0aW9uICggYSwgYiApIHtcbiAgICB2YXIgYXggPSBhLngsIGF5ID0gYS55LCBheiA9IGEuejtcbiAgICB2YXIgYnggPSBiLngsIGJ5ID0gYi55LCBieiA9IGIuejtcblxuICAgIHRoaXMueCA9IGF5ICogYnogLSBheiAqIGJ5O1xuICAgIHRoaXMueSA9IGF6ICogYnggLSBheCAqIGJ6O1xuICAgIHRoaXMueiA9IGF4ICogYnkgLSBheSAqIGJ4O1xuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH0sXG59O1xuXG5NYXRoVXRpbC5RdWF0ZXJuaW9uID0gZnVuY3Rpb24gKCB4LCB5LCB6LCB3ICkge1xuICB0aGlzLnggPSB4IHx8IDA7XG4gIHRoaXMueSA9IHkgfHwgMDtcbiAgdGhpcy56ID0geiB8fCAwO1xuICB0aGlzLncgPSAoIHcgIT09IHVuZGVmaW5lZCApID8gdyA6IDE7XG59O1xuXG5NYXRoVXRpbC5RdWF0ZXJuaW9uLnByb3RvdHlwZSA9IHtcbiAgY29uc3RydWN0b3I6IE1hdGhVdGlsLlF1YXRlcm5pb24sXG5cbiAgc2V0OiBmdW5jdGlvbiAoIHgsIHksIHosIHcgKSB7XG4gICAgdGhpcy54ID0geDtcbiAgICB0aGlzLnkgPSB5O1xuICAgIHRoaXMueiA9IHo7XG4gICAgdGhpcy53ID0gdztcblxuICAgIHJldHVybiB0aGlzO1xuICB9LFxuXG4gIGNvcHk6IGZ1bmN0aW9uICggcXVhdGVybmlvbiApIHtcbiAgICB0aGlzLnggPSBxdWF0ZXJuaW9uLng7XG4gICAgdGhpcy55ID0gcXVhdGVybmlvbi55O1xuICAgIHRoaXMueiA9IHF1YXRlcm5pb24uejtcbiAgICB0aGlzLncgPSBxdWF0ZXJuaW9uLnc7XG5cbiAgICByZXR1cm4gdGhpcztcbiAgfSxcblxuICBzZXRGcm9tRXVsZXJYWVo6IGZ1bmN0aW9uKCB4LCB5LCB6ICkge1xuICAgIHZhciBjMSA9IE1hdGguY29zKCB4IC8gMiApO1xuICAgIHZhciBjMiA9IE1hdGguY29zKCB5IC8gMiApO1xuICAgIHZhciBjMyA9IE1hdGguY29zKCB6IC8gMiApO1xuICAgIHZhciBzMSA9IE1hdGguc2luKCB4IC8gMiApO1xuICAgIHZhciBzMiA9IE1hdGguc2luKCB5IC8gMiApO1xuICAgIHZhciBzMyA9IE1hdGguc2luKCB6IC8gMiApO1xuXG4gICAgdGhpcy54ID0gczEgKiBjMiAqIGMzICsgYzEgKiBzMiAqIHMzO1xuICAgIHRoaXMueSA9IGMxICogczIgKiBjMyAtIHMxICogYzIgKiBzMztcbiAgICB0aGlzLnogPSBjMSAqIGMyICogczMgKyBzMSAqIHMyICogYzM7XG4gICAgdGhpcy53ID0gYzEgKiBjMiAqIGMzIC0gczEgKiBzMiAqIHMzO1xuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH0sXG5cbiAgc2V0RnJvbUV1bGVyWVhaOiBmdW5jdGlvbiggeCwgeSwgeiApIHtcbiAgICB2YXIgYzEgPSBNYXRoLmNvcyggeCAvIDIgKTtcbiAgICB2YXIgYzIgPSBNYXRoLmNvcyggeSAvIDIgKTtcbiAgICB2YXIgYzMgPSBNYXRoLmNvcyggeiAvIDIgKTtcbiAgICB2YXIgczEgPSBNYXRoLnNpbiggeCAvIDIgKTtcbiAgICB2YXIgczIgPSBNYXRoLnNpbiggeSAvIDIgKTtcbiAgICB2YXIgczMgPSBNYXRoLnNpbiggeiAvIDIgKTtcblxuICAgIHRoaXMueCA9IHMxICogYzIgKiBjMyArIGMxICogczIgKiBzMztcbiAgICB0aGlzLnkgPSBjMSAqIHMyICogYzMgLSBzMSAqIGMyICogczM7XG4gICAgdGhpcy56ID0gYzEgKiBjMiAqIHMzIC0gczEgKiBzMiAqIGMzO1xuICAgIHRoaXMudyA9IGMxICogYzIgKiBjMyArIHMxICogczIgKiBzMztcblxuICAgIHJldHVybiB0aGlzO1xuICB9LFxuXG4gIHNldEZyb21BeGlzQW5nbGU6IGZ1bmN0aW9uICggYXhpcywgYW5nbGUgKSB7XG4gICAgLy8gaHR0cDovL3d3dy5ldWNsaWRlYW5zcGFjZS5jb20vbWF0aHMvZ2VvbWV0cnkvcm90YXRpb25zL2NvbnZlcnNpb25zL2FuZ2xlVG9RdWF0ZXJuaW9uL2luZGV4Lmh0bVxuICAgIC8vIGFzc3VtZXMgYXhpcyBpcyBub3JtYWxpemVkXG5cbiAgICB2YXIgaGFsZkFuZ2xlID0gYW5nbGUgLyAyLCBzID0gTWF0aC5zaW4oIGhhbGZBbmdsZSApO1xuXG4gICAgdGhpcy54ID0gYXhpcy54ICogcztcbiAgICB0aGlzLnkgPSBheGlzLnkgKiBzO1xuICAgIHRoaXMueiA9IGF4aXMueiAqIHM7XG4gICAgdGhpcy53ID0gTWF0aC5jb3MoIGhhbGZBbmdsZSApO1xuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH0sXG5cbiAgbXVsdGlwbHk6IGZ1bmN0aW9uICggcSApIHtcbiAgICByZXR1cm4gdGhpcy5tdWx0aXBseVF1YXRlcm5pb25zKCB0aGlzLCBxICk7XG4gIH0sXG5cbiAgbXVsdGlwbHlRdWF0ZXJuaW9uczogZnVuY3Rpb24gKCBhLCBiICkge1xuICAgIC8vIGZyb20gaHR0cDovL3d3dy5ldWNsaWRlYW5zcGFjZS5jb20vbWF0aHMvYWxnZWJyYS9yZWFsTm9ybWVkQWxnZWJyYS9xdWF0ZXJuaW9ucy9jb2RlL2luZGV4Lmh0bVxuXG4gICAgdmFyIHFheCA9IGEueCwgcWF5ID0gYS55LCBxYXogPSBhLnosIHFhdyA9IGEudztcbiAgICB2YXIgcWJ4ID0gYi54LCBxYnkgPSBiLnksIHFieiA9IGIueiwgcWJ3ID0gYi53O1xuXG4gICAgdGhpcy54ID0gcWF4ICogcWJ3ICsgcWF3ICogcWJ4ICsgcWF5ICogcWJ6IC0gcWF6ICogcWJ5O1xuICAgIHRoaXMueSA9IHFheSAqIHFidyArIHFhdyAqIHFieSArIHFheiAqIHFieCAtIHFheCAqIHFiejtcbiAgICB0aGlzLnogPSBxYXogKiBxYncgKyBxYXcgKiBxYnogKyBxYXggKiBxYnkgLSBxYXkgKiBxYng7XG4gICAgdGhpcy53ID0gcWF3ICogcWJ3IC0gcWF4ICogcWJ4IC0gcWF5ICogcWJ5IC0gcWF6ICogcWJ6O1xuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH0sXG5cbiAgaW52ZXJzZTogZnVuY3Rpb24gKCkge1xuICAgIHRoaXMueCAqPSAtMTtcbiAgICB0aGlzLnkgKj0gLTE7XG4gICAgdGhpcy56ICo9IC0xO1xuXG4gICAgdGhpcy5ub3JtYWxpemUoKTtcblxuICAgIHJldHVybiB0aGlzO1xuICB9LFxuXG4gIG5vcm1hbGl6ZTogZnVuY3Rpb24gKCkge1xuICAgIHZhciBsID0gTWF0aC5zcXJ0KCB0aGlzLnggKiB0aGlzLnggKyB0aGlzLnkgKiB0aGlzLnkgKyB0aGlzLnogKiB0aGlzLnogKyB0aGlzLncgKiB0aGlzLncgKTtcblxuICAgIGlmICggbCA9PT0gMCApIHtcbiAgICAgIHRoaXMueCA9IDA7XG4gICAgICB0aGlzLnkgPSAwO1xuICAgICAgdGhpcy56ID0gMDtcbiAgICAgIHRoaXMudyA9IDE7XG4gICAgfSBlbHNlIHtcbiAgICAgIGwgPSAxIC8gbDtcblxuICAgICAgdGhpcy54ID0gdGhpcy54ICogbDtcbiAgICAgIHRoaXMueSA9IHRoaXMueSAqIGw7XG4gICAgICB0aGlzLnogPSB0aGlzLnogKiBsO1xuICAgICAgdGhpcy53ID0gdGhpcy53ICogbDtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcztcbiAgfSxcblxuICBzbGVycDogZnVuY3Rpb24gKCBxYiwgdCApIHtcbiAgICBpZiAoIHQgPT09IDAgKSByZXR1cm4gdGhpcztcbiAgICBpZiAoIHQgPT09IDEgKSByZXR1cm4gdGhpcy5jb3B5KCBxYiApO1xuXG4gICAgdmFyIHggPSB0aGlzLngsIHkgPSB0aGlzLnksIHogPSB0aGlzLnosIHcgPSB0aGlzLnc7XG5cbiAgICAvLyBodHRwOi8vd3d3LmV1Y2xpZGVhbnNwYWNlLmNvbS9tYXRocy9hbGdlYnJhL3JlYWxOb3JtZWRBbGdlYnJhL3F1YXRlcm5pb25zL3NsZXJwL1xuXG4gICAgdmFyIGNvc0hhbGZUaGV0YSA9IHcgKiBxYi53ICsgeCAqIHFiLnggKyB5ICogcWIueSArIHogKiBxYi56O1xuXG4gICAgaWYgKCBjb3NIYWxmVGhldGEgPCAwICkge1xuICAgICAgdGhpcy53ID0gLSBxYi53O1xuICAgICAgdGhpcy54ID0gLSBxYi54O1xuICAgICAgdGhpcy55ID0gLSBxYi55O1xuICAgICAgdGhpcy56ID0gLSBxYi56O1xuXG4gICAgICBjb3NIYWxmVGhldGEgPSAtIGNvc0hhbGZUaGV0YTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5jb3B5KCBxYiApO1xuICAgIH1cblxuICAgIGlmICggY29zSGFsZlRoZXRhID49IDEuMCApIHtcbiAgICAgIHRoaXMudyA9IHc7XG4gICAgICB0aGlzLnggPSB4O1xuICAgICAgdGhpcy55ID0geTtcbiAgICAgIHRoaXMueiA9IHo7XG5cbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIHZhciBoYWxmVGhldGEgPSBNYXRoLmFjb3MoIGNvc0hhbGZUaGV0YSApO1xuICAgIHZhciBzaW5IYWxmVGhldGEgPSBNYXRoLnNxcnQoIDEuMCAtIGNvc0hhbGZUaGV0YSAqIGNvc0hhbGZUaGV0YSApO1xuXG4gICAgaWYgKCBNYXRoLmFicyggc2luSGFsZlRoZXRhICkgPCAwLjAwMSApIHtcbiAgICAgIHRoaXMudyA9IDAuNSAqICggdyArIHRoaXMudyApO1xuICAgICAgdGhpcy54ID0gMC41ICogKCB4ICsgdGhpcy54ICk7XG4gICAgICB0aGlzLnkgPSAwLjUgKiAoIHkgKyB0aGlzLnkgKTtcbiAgICAgIHRoaXMueiA9IDAuNSAqICggeiArIHRoaXMueiApO1xuXG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICB2YXIgcmF0aW9BID0gTWF0aC5zaW4oICggMSAtIHQgKSAqIGhhbGZUaGV0YSApIC8gc2luSGFsZlRoZXRhLFxuICAgIHJhdGlvQiA9IE1hdGguc2luKCB0ICogaGFsZlRoZXRhICkgLyBzaW5IYWxmVGhldGE7XG5cbiAgICB0aGlzLncgPSAoIHcgKiByYXRpb0EgKyB0aGlzLncgKiByYXRpb0IgKTtcbiAgICB0aGlzLnggPSAoIHggKiByYXRpb0EgKyB0aGlzLnggKiByYXRpb0IgKTtcbiAgICB0aGlzLnkgPSAoIHkgKiByYXRpb0EgKyB0aGlzLnkgKiByYXRpb0IgKTtcbiAgICB0aGlzLnogPSAoIHogKiByYXRpb0EgKyB0aGlzLnogKiByYXRpb0IgKTtcblxuICAgIHJldHVybiB0aGlzO1xuICB9LFxuXG4gIHNldEZyb21Vbml0VmVjdG9yczogZnVuY3Rpb24gKCkge1xuICAgIC8vIGh0dHA6Ly9sb2xlbmdpbmUubmV0L2Jsb2cvMjAxNC8wMi8yNC9xdWF0ZXJuaW9uLWZyb20tdHdvLXZlY3RvcnMtZmluYWxcbiAgICAvLyBhc3N1bWVzIGRpcmVjdGlvbiB2ZWN0b3JzIHZGcm9tIGFuZCB2VG8gYXJlIG5vcm1hbGl6ZWRcblxuICAgIHZhciB2MSwgcjtcbiAgICB2YXIgRVBTID0gMC4wMDAwMDE7XG5cbiAgICByZXR1cm4gZnVuY3Rpb24gKCB2RnJvbSwgdlRvICkge1xuICAgICAgaWYgKCB2MSA9PT0gdW5kZWZpbmVkICkgdjEgPSBuZXcgTWF0aFV0aWwuVmVjdG9yMygpO1xuXG4gICAgICByID0gdkZyb20uZG90KCB2VG8gKSArIDE7XG5cbiAgICAgIGlmICggciA8IEVQUyApIHtcbiAgICAgICAgciA9IDA7XG5cbiAgICAgICAgaWYgKCBNYXRoLmFicyggdkZyb20ueCApID4gTWF0aC5hYnMoIHZGcm9tLnogKSApIHtcbiAgICAgICAgICB2MS5zZXQoIC0gdkZyb20ueSwgdkZyb20ueCwgMCApO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHYxLnNldCggMCwgLSB2RnJvbS56LCB2RnJvbS55ICk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHYxLmNyb3NzVmVjdG9ycyggdkZyb20sIHZUbyApO1xuICAgICAgfVxuXG4gICAgICB0aGlzLnggPSB2MS54O1xuICAgICAgdGhpcy55ID0gdjEueTtcbiAgICAgIHRoaXMueiA9IHYxLno7XG4gICAgICB0aGlzLncgPSByO1xuXG4gICAgICB0aGlzLm5vcm1hbGl6ZSgpO1xuXG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gIH0oKSxcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gTWF0aFV0aWw7XG4iLCIvKlxuICogQ29weXJpZ2h0IDIwMTYgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqL1xuXG52YXIgVlJEaXNwbGF5ID0gcmVxdWlyZSgnLi9iYXNlLmpzJykuVlJEaXNwbGF5O1xudmFyIE1hdGhVdGlsID0gcmVxdWlyZSgnLi9tYXRoLXV0aWwuanMnKTtcbnZhciBVdGlsID0gcmVxdWlyZSgnLi91dGlsLmpzJyk7XG5cbi8vIEhvdyBtdWNoIHRvIHJvdGF0ZSBwZXIga2V5IHN0cm9rZS5cbnZhciBLRVlfU1BFRUQgPSAwLjE1O1xudmFyIEtFWV9BTklNQVRJT05fRFVSQVRJT04gPSA4MDtcblxuLy8gSG93IG11Y2ggdG8gcm90YXRlIGZvciBtb3VzZSBldmVudHMuXG52YXIgTU9VU0VfU1BFRURfWCA9IDAuNTtcbnZhciBNT1VTRV9TUEVFRF9ZID0gMC4zO1xuXG4vKipcbiAqIFZSRGlzcGxheSBiYXNlZCBvbiBtb3VzZSBhbmQga2V5Ym9hcmQgaW5wdXQuIERlc2lnbmVkIGZvciBkZXNrdG9wcy9sYXB0b3BzXG4gKiB3aGVyZSBvcmllbnRhdGlvbiBldmVudHMgYXJlbid0IHN1cHBvcnRlZC4gQ2Fubm90IHByZXNlbnQuXG4gKi9cbmZ1bmN0aW9uIE1vdXNlS2V5Ym9hcmRWUkRpc3BsYXkoKSB7XG4gIHRoaXMuZGlzcGxheU5hbWUgPSAnTW91c2UgYW5kIEtleWJvYXJkIFZSRGlzcGxheSAod2VidnItcG9seWZpbGwpJztcblxuICB0aGlzLmNhcGFiaWxpdGllcy5oYXNPcmllbnRhdGlvbiA9IHRydWU7XG5cbiAgLy8gQXR0YWNoIHRvIG1vdXNlIGFuZCBrZXlib2FyZCBldmVudHMuXG4gIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdrZXlkb3duJywgdGhpcy5vbktleURvd25fLmJpbmQodGhpcykpO1xuICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbW91c2Vtb3ZlJywgdGhpcy5vbk1vdXNlTW92ZV8uYmluZCh0aGlzKSk7XG4gIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtb3VzZWRvd24nLCB0aGlzLm9uTW91c2VEb3duXy5iaW5kKHRoaXMpKTtcbiAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNldXAnLCB0aGlzLm9uTW91c2VVcF8uYmluZCh0aGlzKSk7XG5cbiAgLy8gXCJQcml2YXRlXCIgbWVtYmVycy5cbiAgdGhpcy5waGlfID0gMDtcbiAgdGhpcy50aGV0YV8gPSAwO1xuXG4gIC8vIFZhcmlhYmxlcyBmb3Iga2V5Ym9hcmQtYmFzZWQgcm90YXRpb24gYW5pbWF0aW9uLlxuICB0aGlzLnRhcmdldEFuZ2xlXyA9IG51bGw7XG4gIHRoaXMuYW5nbGVBbmltYXRpb25fID0gbnVsbDtcblxuICAvLyBTdGF0ZSB2YXJpYWJsZXMgZm9yIGNhbGN1bGF0aW9ucy5cbiAgdGhpcy5vcmllbnRhdGlvbl8gPSBuZXcgTWF0aFV0aWwuUXVhdGVybmlvbigpO1xuXG4gIC8vIFZhcmlhYmxlcyBmb3IgbW91c2UtYmFzZWQgcm90YXRpb24uXG4gIHRoaXMucm90YXRlU3RhcnRfID0gbmV3IE1hdGhVdGlsLlZlY3RvcjIoKTtcbiAgdGhpcy5yb3RhdGVFbmRfID0gbmV3IE1hdGhVdGlsLlZlY3RvcjIoKTtcbiAgdGhpcy5yb3RhdGVEZWx0YV8gPSBuZXcgTWF0aFV0aWwuVmVjdG9yMigpO1xuICB0aGlzLmlzRHJhZ2dpbmdfID0gZmFsc2U7XG5cbiAgdGhpcy5vcmllbnRhdGlvbk91dF8gPSBuZXcgRmxvYXQzMkFycmF5KDQpO1xufVxuTW91c2VLZXlib2FyZFZSRGlzcGxheS5wcm90b3R5cGUgPSBuZXcgVlJEaXNwbGF5KCk7XG5cbk1vdXNlS2V5Ym9hcmRWUkRpc3BsYXkucHJvdG90eXBlLmdldEltbWVkaWF0ZVBvc2UgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5vcmllbnRhdGlvbl8uc2V0RnJvbUV1bGVyWVhaKHRoaXMucGhpXywgdGhpcy50aGV0YV8sIDApO1xuXG4gIHRoaXMub3JpZW50YXRpb25PdXRfWzBdID0gdGhpcy5vcmllbnRhdGlvbl8ueDtcbiAgdGhpcy5vcmllbnRhdGlvbk91dF9bMV0gPSB0aGlzLm9yaWVudGF0aW9uXy55O1xuICB0aGlzLm9yaWVudGF0aW9uT3V0X1syXSA9IHRoaXMub3JpZW50YXRpb25fLno7XG4gIHRoaXMub3JpZW50YXRpb25PdXRfWzNdID0gdGhpcy5vcmllbnRhdGlvbl8udztcblxuICByZXR1cm4ge1xuICAgIHBvc2l0aW9uOiBudWxsLFxuICAgIG9yaWVudGF0aW9uOiB0aGlzLm9yaWVudGF0aW9uT3V0XyxcbiAgICBsaW5lYXJWZWxvY2l0eTogbnVsbCxcbiAgICBsaW5lYXJBY2NlbGVyYXRpb246IG51bGwsXG4gICAgYW5ndWxhclZlbG9jaXR5OiBudWxsLFxuICAgIGFuZ3VsYXJBY2NlbGVyYXRpb246IG51bGxcbiAgfTtcbn07XG5cbk1vdXNlS2V5Ym9hcmRWUkRpc3BsYXkucHJvdG90eXBlLm9uS2V5RG93bl8gPSBmdW5jdGlvbihlKSB7XG4gIC8vIFRyYWNrIFdBU0QgYW5kIGFycm93IGtleXMuXG4gIGlmIChlLmtleUNvZGUgPT0gMzgpIHsgLy8gVXAga2V5LlxuICAgIHRoaXMuYW5pbWF0ZVBoaV8odGhpcy5waGlfICsgS0VZX1NQRUVEKTtcbiAgfSBlbHNlIGlmIChlLmtleUNvZGUgPT0gMzkpIHsgLy8gUmlnaHQga2V5LlxuICAgIHRoaXMuYW5pbWF0ZVRoZXRhXyh0aGlzLnRoZXRhXyAtIEtFWV9TUEVFRCk7XG4gIH0gZWxzZSBpZiAoZS5rZXlDb2RlID09IDQwKSB7IC8vIERvd24ga2V5LlxuICAgIHRoaXMuYW5pbWF0ZVBoaV8odGhpcy5waGlfIC0gS0VZX1NQRUVEKTtcbiAgfSBlbHNlIGlmIChlLmtleUNvZGUgPT0gMzcpIHsgLy8gTGVmdCBrZXkuXG4gICAgdGhpcy5hbmltYXRlVGhldGFfKHRoaXMudGhldGFfICsgS0VZX1NQRUVEKTtcbiAgfVxufTtcblxuTW91c2VLZXlib2FyZFZSRGlzcGxheS5wcm90b3R5cGUuYW5pbWF0ZVRoZXRhXyA9IGZ1bmN0aW9uKHRhcmdldEFuZ2xlKSB7XG4gIHRoaXMuYW5pbWF0ZUtleVRyYW5zaXRpb25zXygndGhldGFfJywgdGFyZ2V0QW5nbGUpO1xufTtcblxuTW91c2VLZXlib2FyZFZSRGlzcGxheS5wcm90b3R5cGUuYW5pbWF0ZVBoaV8gPSBmdW5jdGlvbih0YXJnZXRBbmdsZSkge1xuICAvLyBQcmV2ZW50IGxvb2tpbmcgdG9vIGZhciB1cCBvciBkb3duLlxuICB0YXJnZXRBbmdsZSA9IFV0aWwuY2xhbXAodGFyZ2V0QW5nbGUsIC1NYXRoLlBJLzIsIE1hdGguUEkvMik7XG4gIHRoaXMuYW5pbWF0ZUtleVRyYW5zaXRpb25zXygncGhpXycsIHRhcmdldEFuZ2xlKTtcbn07XG5cbi8qKlxuICogU3RhcnQgYW4gYW5pbWF0aW9uIHRvIHRyYW5zaXRpb24gYW4gYW5nbGUgZnJvbSBvbmUgdmFsdWUgdG8gYW5vdGhlci5cbiAqL1xuTW91c2VLZXlib2FyZFZSRGlzcGxheS5wcm90b3R5cGUuYW5pbWF0ZUtleVRyYW5zaXRpb25zXyA9IGZ1bmN0aW9uKGFuZ2xlTmFtZSwgdGFyZ2V0QW5nbGUpIHtcbiAgLy8gSWYgYW4gYW5pbWF0aW9uIGlzIGN1cnJlbnRseSBydW5uaW5nLCBjYW5jZWwgaXQuXG4gIGlmICh0aGlzLmFuZ2xlQW5pbWF0aW9uXykge1xuICAgIGNhbmNlbEFuaW1hdGlvbkZyYW1lKHRoaXMuYW5nbGVBbmltYXRpb25fKTtcbiAgfVxuICB2YXIgc3RhcnRBbmdsZSA9IHRoaXNbYW5nbGVOYW1lXTtcbiAgdmFyIHN0YXJ0VGltZSA9IG5ldyBEYXRlKCk7XG4gIC8vIFNldCB1cCBhbiBpbnRlcnZhbCB0aW1lciB0byBwZXJmb3JtIHRoZSBhbmltYXRpb24uXG4gIHRoaXMuYW5nbGVBbmltYXRpb25fID0gcmVxdWVzdEFuaW1hdGlvbkZyYW1lKGZ1bmN0aW9uIGFuaW1hdGUoKSB7XG4gICAgLy8gT25jZSB3ZSdyZSBmaW5pc2hlZCB0aGUgYW5pbWF0aW9uLCB3ZSdyZSBkb25lLlxuICAgIHZhciBlbGFwc2VkID0gbmV3IERhdGUoKSAtIHN0YXJ0VGltZTtcbiAgICBpZiAoZWxhcHNlZCA+PSBLRVlfQU5JTUFUSU9OX0RVUkFUSU9OKSB7XG4gICAgICB0aGlzW2FuZ2xlTmFtZV0gPSB0YXJnZXRBbmdsZTtcbiAgICAgIGNhbmNlbEFuaW1hdGlvbkZyYW1lKHRoaXMuYW5nbGVBbmltYXRpb25fKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gbG9vcCB3aXRoIHJlcXVlc3RBbmltYXRpb25GcmFtZVxuICAgIHRoaXMuYW5nbGVBbmltYXRpb25fID0gcmVxdWVzdEFuaW1hdGlvbkZyYW1lKGFuaW1hdGUuYmluZCh0aGlzKSlcbiAgICAvLyBMaW5lYXJseSBpbnRlcnBvbGF0ZSB0aGUgYW5nbGUgc29tZSBhbW91bnQuXG4gICAgdmFyIHBlcmNlbnQgPSBlbGFwc2VkIC8gS0VZX0FOSU1BVElPTl9EVVJBVElPTjtcbiAgICB0aGlzW2FuZ2xlTmFtZV0gPSBzdGFydEFuZ2xlICsgKHRhcmdldEFuZ2xlIC0gc3RhcnRBbmdsZSkgKiBwZXJjZW50O1xuICB9LmJpbmQodGhpcykpO1xufTtcblxuTW91c2VLZXlib2FyZFZSRGlzcGxheS5wcm90b3R5cGUub25Nb3VzZURvd25fID0gZnVuY3Rpb24oZSkge1xuICB0aGlzLnJvdGF0ZVN0YXJ0Xy5zZXQoZS5jbGllbnRYLCBlLmNsaWVudFkpO1xuICB0aGlzLmlzRHJhZ2dpbmdfID0gdHJ1ZTtcbn07XG5cbi8vIFZlcnkgc2ltaWxhciB0byBodHRwczovL2dpc3QuZ2l0aHViLmNvbS9tcmZsaXgvODM1MTAyMFxuTW91c2VLZXlib2FyZFZSRGlzcGxheS5wcm90b3R5cGUub25Nb3VzZU1vdmVfID0gZnVuY3Rpb24oZSkge1xuICBpZiAoIXRoaXMuaXNEcmFnZ2luZ18gJiYgIXRoaXMuaXNQb2ludGVyTG9ja2VkXygpKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIC8vIFN1cHBvcnQgcG9pbnRlciBsb2NrIEFQSS5cbiAgaWYgKHRoaXMuaXNQb2ludGVyTG9ja2VkXygpKSB7XG4gICAgdmFyIG1vdmVtZW50WCA9IGUubW92ZW1lbnRYIHx8IGUubW96TW92ZW1lbnRYIHx8IDA7XG4gICAgdmFyIG1vdmVtZW50WSA9IGUubW92ZW1lbnRZIHx8IGUubW96TW92ZW1lbnRZIHx8IDA7XG4gICAgdGhpcy5yb3RhdGVFbmRfLnNldCh0aGlzLnJvdGF0ZVN0YXJ0Xy54IC0gbW92ZW1lbnRYLCB0aGlzLnJvdGF0ZVN0YXJ0Xy55IC0gbW92ZW1lbnRZKTtcbiAgfSBlbHNlIHtcbiAgICB0aGlzLnJvdGF0ZUVuZF8uc2V0KGUuY2xpZW50WCwgZS5jbGllbnRZKTtcbiAgfVxuICAvLyBDYWxjdWxhdGUgaG93IG11Y2ggd2UgbW92ZWQgaW4gbW91c2Ugc3BhY2UuXG4gIHRoaXMucm90YXRlRGVsdGFfLnN1YlZlY3RvcnModGhpcy5yb3RhdGVFbmRfLCB0aGlzLnJvdGF0ZVN0YXJ0Xyk7XG4gIHRoaXMucm90YXRlU3RhcnRfLmNvcHkodGhpcy5yb3RhdGVFbmRfKTtcblxuICAvLyBLZWVwIHRyYWNrIG9mIHRoZSBjdW11bGF0aXZlIGV1bGVyIGFuZ2xlcy5cbiAgdGhpcy5waGlfICs9IDIgKiBNYXRoLlBJICogdGhpcy5yb3RhdGVEZWx0YV8ueSAvIHNjcmVlbi5oZWlnaHQgKiBNT1VTRV9TUEVFRF9ZO1xuICB0aGlzLnRoZXRhXyArPSAyICogTWF0aC5QSSAqIHRoaXMucm90YXRlRGVsdGFfLnggLyBzY3JlZW4ud2lkdGggKiBNT1VTRV9TUEVFRF9YO1xuXG4gIC8vIFByZXZlbnQgbG9va2luZyB0b28gZmFyIHVwIG9yIGRvd24uXG4gIHRoaXMucGhpXyA9IFV0aWwuY2xhbXAodGhpcy5waGlfLCAtTWF0aC5QSS8yLCBNYXRoLlBJLzIpO1xufTtcblxuTW91c2VLZXlib2FyZFZSRGlzcGxheS5wcm90b3R5cGUub25Nb3VzZVVwXyA9IGZ1bmN0aW9uKGUpIHtcbiAgdGhpcy5pc0RyYWdnaW5nXyA9IGZhbHNlO1xufTtcblxuTW91c2VLZXlib2FyZFZSRGlzcGxheS5wcm90b3R5cGUuaXNQb2ludGVyTG9ja2VkXyA9IGZ1bmN0aW9uKCkge1xuICB2YXIgZWwgPSBkb2N1bWVudC5wb2ludGVyTG9ja0VsZW1lbnQgfHwgZG9jdW1lbnQubW96UG9pbnRlckxvY2tFbGVtZW50IHx8XG4gICAgICBkb2N1bWVudC53ZWJraXRQb2ludGVyTG9ja0VsZW1lbnQ7XG4gIHJldHVybiBlbCAhPT0gdW5kZWZpbmVkO1xufTtcblxuTW91c2VLZXlib2FyZFZSRGlzcGxheS5wcm90b3R5cGUucmVzZXRQb3NlID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMucGhpXyA9IDA7XG4gIHRoaXMudGhldGFfID0gMDtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gTW91c2VLZXlib2FyZFZSRGlzcGxheTtcbiIsIi8vIFRoaXMgaXMgdGhlIGVudHJ5IHBvaW50IGlmIHJlcXVpcmluZy9pbXBvcnRpbmcgdmlhIG5vZGUsIG9yXHJcbi8vIGEgYnVpbGQgdG9vbCB0aGF0IHVzZXMgcGFja2FnZS5qc29uIGVudHJ5IChsaWtlIGJyb3dzZXJpZnksIHdlYnBhY2spLlxyXG4vLyBJZiBydW5uaW5nIGluIG5vZGUgd2l0aCBhIHdpbmRvdyBtb2NrIGF2YWlsYWJsZSwgZ2xvYmFsaXplIGl0cyBtZW1iZXJzXHJcbi8vIGlmIG5lZWRlZC4gT3RoZXJ3aXNlLCBqdXN0IGNvbnRpbnVlIHRvIGAuL21haW5gXHJcbmlmICh0eXBlb2YgZ2xvYmFsICE9PSAndW5kZWZpbmVkJyAmJiBnbG9iYWwud2luZG93KSB7XHJcbiAgZ2xvYmFsLmRvY3VtZW50ID0gZ2xvYmFsLndpbmRvdy5kb2N1bWVudDtcclxuICBnbG9iYWwubmF2aWdhdG9yID0gZ2xvYmFsLndpbmRvdy5uYXZpZ2F0b3I7XHJcbn1cclxuXHJcbnJlcXVpcmUoJy4vbWFpbicpO1xyXG4iLCIvKlxuICogQ29weXJpZ2h0IDIwMTUgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqL1xuXG52YXIgVXRpbCA9IHJlcXVpcmUoJy4vdXRpbC5qcycpO1xuXG5mdW5jdGlvbiBSb3RhdGVJbnN0cnVjdGlvbnMoKSB7XG4gIHRoaXMubG9hZEljb25fKCk7XG5cbiAgdmFyIG92ZXJsYXkgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgdmFyIHMgPSBvdmVybGF5LnN0eWxlO1xuICBzLnBvc2l0aW9uID0gJ2ZpeGVkJztcbiAgcy50b3AgPSAwO1xuICBzLnJpZ2h0ID0gMDtcbiAgcy5ib3R0b20gPSAwO1xuICBzLmxlZnQgPSAwO1xuICBzLmJhY2tncm91bmRDb2xvciA9ICdncmF5JztcbiAgcy5mb250RmFtaWx5ID0gJ3NhbnMtc2VyaWYnO1xuICAvLyBGb3JjZSB0aGlzIHRvIGJlIGFib3ZlIHRoZSBmdWxsc2NyZWVuIGNhbnZhcywgd2hpY2ggaXMgYXQgekluZGV4OiA5OTk5OTkuXG4gIHMuekluZGV4ID0gMTAwMDAwMDtcblxuICB2YXIgaW1nID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaW1nJyk7XG4gIGltZy5zcmMgPSB0aGlzLmljb247XG4gIHZhciBzID0gaW1nLnN0eWxlO1xuICBzLm1hcmdpbkxlZnQgPSAnMjUlJztcbiAgcy5tYXJnaW5Ub3AgPSAnMjUlJztcbiAgcy53aWR0aCA9ICc1MCUnO1xuICBvdmVybGF5LmFwcGVuZENoaWxkKGltZyk7XG5cbiAgdmFyIHRleHQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgdmFyIHMgPSB0ZXh0LnN0eWxlO1xuICBzLnRleHRBbGlnbiA9ICdjZW50ZXInO1xuICBzLmZvbnRTaXplID0gJzE2cHgnO1xuICBzLmxpbmVIZWlnaHQgPSAnMjRweCc7XG4gIHMubWFyZ2luID0gJzI0cHggMjUlJztcbiAgcy53aWR0aCA9ICc1MCUnO1xuICB0ZXh0LmlubmVySFRNTCA9ICdQbGFjZSB5b3VyIHBob25lIGludG8geW91ciBDYXJkYm9hcmQgdmlld2VyLic7XG4gIG92ZXJsYXkuYXBwZW5kQ2hpbGQodGV4dCk7XG5cbiAgdmFyIHNuYWNrYmFyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gIHZhciBzID0gc25hY2tiYXIuc3R5bGU7XG4gIHMuYmFja2dyb3VuZENvbG9yID0gJyNDRkQ4REMnO1xuICBzLnBvc2l0aW9uID0gJ2ZpeGVkJztcbiAgcy5ib3R0b20gPSAwO1xuICBzLndpZHRoID0gJzEwMCUnO1xuICBzLmhlaWdodCA9ICc0OHB4JztcbiAgcy5wYWRkaW5nID0gJzE0cHggMjRweCc7XG4gIHMuYm94U2l6aW5nID0gJ2JvcmRlci1ib3gnO1xuICBzLmNvbG9yID0gJyM2NTZBNkInO1xuICBvdmVybGF5LmFwcGVuZENoaWxkKHNuYWNrYmFyKTtcblxuICB2YXIgc25hY2tiYXJUZXh0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gIHNuYWNrYmFyVGV4dC5zdHlsZS5mbG9hdCA9ICdsZWZ0JztcbiAgc25hY2tiYXJUZXh0LmlubmVySFRNTCA9ICdObyBDYXJkYm9hcmQgdmlld2VyPyc7XG5cbiAgdmFyIHNuYWNrYmFyQnV0dG9uID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYScpO1xuICBzbmFja2JhckJ1dHRvbi5ocmVmID0gJ2h0dHBzOi8vd3d3Lmdvb2dsZS5jb20vZ2V0L2NhcmRib2FyZC9nZXQtY2FyZGJvYXJkLyc7XG4gIHNuYWNrYmFyQnV0dG9uLmlubmVySFRNTCA9ICdnZXQgb25lJztcbiAgc25hY2tiYXJCdXR0b24udGFyZ2V0ID0gJ19ibGFuayc7XG4gIHZhciBzID0gc25hY2tiYXJCdXR0b24uc3R5bGU7XG4gIHMuZmxvYXQgPSAncmlnaHQnO1xuICBzLmZvbnRXZWlnaHQgPSA2MDA7XG4gIHMudGV4dFRyYW5zZm9ybSA9ICd1cHBlcmNhc2UnO1xuICBzLmJvcmRlckxlZnQgPSAnMXB4IHNvbGlkIGdyYXknO1xuICBzLnBhZGRpbmdMZWZ0ID0gJzI0cHgnO1xuICBzLnRleHREZWNvcmF0aW9uID0gJ25vbmUnO1xuICBzLmNvbG9yID0gJyM2NTZBNkInO1xuXG4gIHNuYWNrYmFyLmFwcGVuZENoaWxkKHNuYWNrYmFyVGV4dCk7XG4gIHNuYWNrYmFyLmFwcGVuZENoaWxkKHNuYWNrYmFyQnV0dG9uKTtcblxuICB0aGlzLm92ZXJsYXkgPSBvdmVybGF5O1xuICB0aGlzLnRleHQgPSB0ZXh0O1xuXG4gIHRoaXMuaGlkZSgpO1xufVxuXG5Sb3RhdGVJbnN0cnVjdGlvbnMucHJvdG90eXBlLnNob3cgPSBmdW5jdGlvbihwYXJlbnQpIHtcbiAgaWYgKCFwYXJlbnQgJiYgIXRoaXMub3ZlcmxheS5wYXJlbnRFbGVtZW50KSB7XG4gICAgZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZCh0aGlzLm92ZXJsYXkpO1xuICB9IGVsc2UgaWYgKHBhcmVudCkge1xuICAgIGlmICh0aGlzLm92ZXJsYXkucGFyZW50RWxlbWVudCAmJiB0aGlzLm92ZXJsYXkucGFyZW50RWxlbWVudCAhPSBwYXJlbnQpXG4gICAgICB0aGlzLm92ZXJsYXkucGFyZW50RWxlbWVudC5yZW1vdmVDaGlsZCh0aGlzLm92ZXJsYXkpO1xuXG4gICAgcGFyZW50LmFwcGVuZENoaWxkKHRoaXMub3ZlcmxheSk7XG4gIH1cblxuICB0aGlzLm92ZXJsYXkuc3R5bGUuZGlzcGxheSA9ICdibG9jayc7XG5cbiAgdmFyIGltZyA9IHRoaXMub3ZlcmxheS5xdWVyeVNlbGVjdG9yKCdpbWcnKTtcbiAgdmFyIHMgPSBpbWcuc3R5bGU7XG5cbiAgaWYgKFV0aWwuaXNMYW5kc2NhcGVNb2RlKCkpIHtcbiAgICBzLndpZHRoID0gJzIwJSc7XG4gICAgcy5tYXJnaW5MZWZ0ID0gJzQwJSc7XG4gICAgcy5tYXJnaW5Ub3AgPSAnMyUnO1xuICB9IGVsc2Uge1xuICAgIHMud2lkdGggPSAnNTAlJztcbiAgICBzLm1hcmdpbkxlZnQgPSAnMjUlJztcbiAgICBzLm1hcmdpblRvcCA9ICcyNSUnO1xuICB9XG59O1xuXG5Sb3RhdGVJbnN0cnVjdGlvbnMucHJvdG90eXBlLmhpZGUgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5vdmVybGF5LnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XG59O1xuXG5Sb3RhdGVJbnN0cnVjdGlvbnMucHJvdG90eXBlLnNob3dUZW1wb3JhcmlseSA9IGZ1bmN0aW9uKG1zLCBwYXJlbnQpIHtcbiAgdGhpcy5zaG93KHBhcmVudCk7XG4gIHRoaXMudGltZXIgPSBzZXRUaW1lb3V0KHRoaXMuaGlkZS5iaW5kKHRoaXMpLCBtcyk7XG59O1xuXG5Sb3RhdGVJbnN0cnVjdGlvbnMucHJvdG90eXBlLmRpc2FibGVTaG93VGVtcG9yYXJpbHkgPSBmdW5jdGlvbigpIHtcbiAgY2xlYXJUaW1lb3V0KHRoaXMudGltZXIpO1xufTtcblxuUm90YXRlSW5zdHJ1Y3Rpb25zLnByb3RvdHlwZS51cGRhdGUgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5kaXNhYmxlU2hvd1RlbXBvcmFyaWx5KCk7XG4gIC8vIEluIHBvcnRyYWl0IFZSIG1vZGUsIHRlbGwgdGhlIHVzZXIgdG8gcm90YXRlIHRvIGxhbmRzY2FwZS4gT3RoZXJ3aXNlLCBoaWRlXG4gIC8vIHRoZSBpbnN0cnVjdGlvbnMuXG4gIGlmICghVXRpbC5pc0xhbmRzY2FwZU1vZGUoKSAmJiBVdGlsLmlzTW9iaWxlKCkpIHtcbiAgICB0aGlzLnNob3coKTtcbiAgfSBlbHNlIHtcbiAgICB0aGlzLmhpZGUoKTtcbiAgfVxufTtcblxuUm90YXRlSW5zdHJ1Y3Rpb25zLnByb3RvdHlwZS5sb2FkSWNvbl8gPSBmdW5jdGlvbigpIHtcbiAgLy8gRW5jb2RlZCBhc3NldF9zcmMvcm90YXRlLWluc3RydWN0aW9ucy5zdmdcbiAgdGhpcy5pY29uID0gVXRpbC5iYXNlNjQoJ2ltYWdlL3N2Zyt4bWwnLCAnUEQ5NGJXd2dkbVZ5YzJsdmJqMGlNUzR3SWlCbGJtTnZaR2x1WnowaVZWUkdMVGdpSUhOMFlXNWtZV3h2Ym1VOUltNXZJajgrQ2p4emRtY2dkMmxrZEdnOUlqRTVPSEI0SWlCb1pXbG5hSFE5SWpJME1IQjRJaUIyYVdWM1FtOTRQU0l3SURBZ01UazRJREkwTUNJZ2RtVnljMmx2YmowaU1TNHhJaUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGh0Ykc1ek9uaHNhVzVyUFNKb2RIUndPaTh2ZDNkM0xuY3pMbTl5Wnk4eE9UazVMM2hzYVc1cklpQjRiV3h1Y3pwemEyVjBZMmc5SW1oMGRIQTZMeTkzZDNjdVltOW9aVzFwWVc1amIyUnBibWN1WTI5dEwzTnJaWFJqYUM5dWN5SStDaUFnSUNBOElTMHRJRWRsYm1WeVlYUnZjam9nVTJ0bGRHTm9JRE11TXk0eklDZ3hNakE0TVNrZ0xTQm9kSFJ3T2k4dmQzZDNMbUp2YUdWdGFXRnVZMjlrYVc1bkxtTnZiUzl6YTJWMFkyZ2dMUzArQ2lBZ0lDQThkR2wwYkdVK2RISmhibk5wZEdsdmJqd3ZkR2wwYkdVK0NpQWdJQ0E4WkdWell6NURjbVZoZEdWa0lIZHBkR2dnVTJ0bGRHTm9Mand2WkdWell6NEtJQ0FnSUR4a1pXWnpQand2WkdWbWN6NEtJQ0FnSUR4bklHbGtQU0pRWVdkbExURWlJSE4wY205clpUMGlibTl1WlNJZ2MzUnliMnRsTFhkcFpIUm9QU0l4SWlCbWFXeHNQU0p1YjI1bElpQm1hV3hzTFhKMWJHVTlJbVYyWlc1dlpHUWlJSE5yWlhSamFEcDBlWEJsUFNKTlUxQmhaMlVpUGdvZ0lDQWdJQ0FnSUR4bklHbGtQU0owY21GdWMybDBhVzl1SWlCemEyVjBZMmc2ZEhsd1pUMGlUVk5CY25SaWIyRnlaRWR5YjNWd0lqNEtJQ0FnSUNBZ0lDQWdJQ0FnUEdjZ2FXUTlJa2x0Y0c5eWRHVmtMVXhoZVdWeWN5MURiM0I1TFRRdEt5MUpiWEJ2Y25SbFpDMU1ZWGxsY25NdFEyOXdlUzByTFVsdGNHOXlkR1ZrTFV4aGVXVnljeTFEYjNCNUxUSXRRMjl3ZVNJZ2MydGxkR05vT25SNWNHVTlJazFUVEdGNVpYSkhjbTkxY0NJK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFp5QnBaRDBpU1cxd2IzSjBaV1F0VEdGNVpYSnpMVU52Y0hrdE5DSWdkSEpoYm5ObWIzSnRQU0owY21GdWMyeGhkR1VvTUM0d01EQXdNREFzSURFd055NHdNREF3TURBcElpQnphMlYwWTJnNmRIbHdaVDBpVFZOVGFHRndaVWR5YjNWd0lqNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0E4Y0dGMGFDQmtQU0pOTVRRNUxqWXlOU3d5TGpVeU55QkRNVFE1TGpZeU5Td3lMalV5TnlBeE5UVXVPREExTERZdU1EazJJREUxTmk0ek5qSXNOaTQwTVRnZ1RERTFOaTR6TmpJc055NHpNRFFnUXpFMU5pNHpOaklzTnk0ME9ERWdNVFUyTGpNM05TdzNMalkyTkNBeE5UWXVOQ3czTGpnMU15QkRNVFUyTGpReExEY3VPVE0wSURFMU5pNDBNaXc0TGpBeE5TQXhOVFl1TkRJM0xEZ3VNRGsxSUVNeE5UWXVOVFkzTERrdU5URWdNVFUzTGpRd01Td3hNUzR3T1RNZ01UVTRMalV6TWl3eE1pNHdPVFFnVERFMk5DNHlOVElzTVRjdU1UVTJJRXd4TmpRdU16TXpMREUzTGpBMk5pQkRNVFkwTGpNek15d3hOeTR3TmpZZ01UWTRMamN4TlN3eE5DNDFNellnTVRZNUxqVTJPQ3d4TkM0d05ESWdRekUzTVM0d01qVXNNVFF1T0RneklERTVOUzQxTXpnc01qa3VNRE0xSURFNU5TNDFNemdzTWprdU1ETTFJRXd4T1RVdU5UTTRMRGd6TGpBek5pQkRNVGsxTGpVek9DdzRNeTQ0TURjZ01UazFMakUxTWl3NE5DNHlOVE1nTVRrMExqVTVMRGcwTGpJMU15QkRNVGswTGpNMU55dzROQzR5TlRNZ01UazBMakE1TlN3NE5DNHhOemNnTVRrekxqZ3hPQ3c0TkM0d01UY2dUREUyT1M0NE5URXNOekF1TVRjNUlFd3hOamt1T0RNM0xEY3dMakl3TXlCTU1UUXlMalV4TlN3NE5TNDVOemdnVERFME1TNDJOalVzT0RRdU5qVTFJRU14TXpZdU9UTTBMRGd6TGpFeU5pQXhNekV1T1RFM0xEZ3hMamt4TlNBeE1qWXVOekUwTERneExqQTBOU0JETVRJMkxqY3dPU3c0TVM0d05pQXhNall1TnpBM0xEZ3hMakEyT1NBeE1qWXVOekEzTERneExqQTJPU0JNTVRJeExqWTBMRGs0TGpBeklFd3hNVE11TnpRNUxERXdNaTQxT0RZZ1RERXhNeTQzTVRJc01UQXlMalV5TXlCTU1URXpMamN4TWl3eE16QXVNVEV6SUVNeE1UTXVOekV5TERFek1DNDRPRFVnTVRFekxqTXlOaXd4TXpFdU16TWdNVEV5TGpjMk5Dd3hNekV1TXpNZ1F6RXhNaTQxTXpJc01UTXhMak16SURFeE1pNHlOamtzTVRNeExqSTFOQ0F4TVRFdU9Ua3lMREV6TVM0d09UUWdURFk1TGpVeE9Td3hNRFl1TlRjeUlFTTJPQzQxTmprc01UQTJMakF5TXlBMk55NDNPVGtzTVRBMExqWTVOU0EyTnk0M09Ua3NNVEF6TGpZd05TQk1OamN1TnprNUxERXdNaTQxTnlCTU5qY3VOemM0TERFd01pNDJNVGNnUXpZM0xqSTNMREV3TWk0ek9UTWdOall1TmpRNExERXdNaTR5TkRrZ05qVXVPVFl5TERFd01pNHlNVGdnUXpZMUxqZzNOU3d4TURJdU1qRTBJRFkxTGpjNE9Dd3hNREl1TWpFeUlEWTFMamN3TVN3eE1ESXVNakV5SUVNMk5TNDJNRFlzTVRBeUxqSXhNaUEyTlM0MU1URXNNVEF5TGpJeE5TQTJOUzQwTVRZc01UQXlMakl4T1NCRE5qVXVNVGsxTERFd01pNHlNamtnTmpRdU9UYzBMREV3TWk0eU16VWdOalF1TnpVMExERXdNaTR5TXpVZ1F6WTBMak16TVN3eE1ESXVNak0xSURZekxqa3hNU3d4TURJdU1qRTJJRFl6TGpRNU9Dd3hNREl1TVRjNElFTTJNUzQ0TkRNc01UQXlMakF5TlNBMk1DNHlPVGdzTVRBeExqVTNPQ0ExT1M0d09UUXNNVEF3TGpnNE1pQk1NVEl1TlRFNExEY3pMams1TWlCTU1USXVOVEl6TERjMExqQXdOQ0JNTWk0eU5EVXNOVFV1TWpVMElFTXhMakkwTkN3MU15NDBNamNnTWk0d01EUXNOVEV1TURNNElETXVPVFF6TERRNUxqa3hPQ0JNTlRrdU9UVTBMREUzTGpVM015QkROakF1TmpJMkxERTNMakU0TlNBMk1TNHpOU3d4Tnk0d01ERWdOakl1TURVekxERTNMakF3TVNCRE5qTXVNemM1TERFM0xqQXdNU0EyTkM0Mk1qVXNNVGN1TmpZZ05qVXVNamdzTVRndU9EVTBJRXcyTlM0eU9EVXNNVGd1T0RVeElFdzJOUzQxTVRJc01Ua3VNalkwSUV3Mk5TNDFNRFlzTVRrdU1qWTRJRU0yTlM0NU1Ea3NNakF1TURBeklEWTJMalF3TlN3eU1DNDJPQ0EyTmk0NU9ETXNNakV1TWpnMklFdzJOeTR5Tml3eU1TNDFOVFlnUXpZNUxqRTNOQ3d5TXk0ME1EWWdOekV1TnpJNExESTBMak0xTnlBM05DNHpOek1zTWpRdU16VTNJRU0zTmk0ek1qSXNNalF1TXpVM0lEYzRMak15TVN3eU15NDROQ0E0TUM0eE5EZ3NNakl1TnpnMUlFTTRNQzR4TmpFc01qSXVOemcxSURnM0xqUTJOeXd4T0M0MU5qWWdPRGN1TkRZM0xERTRMalUyTmlCRE9EZ3VNVE01TERFNExqRTNPQ0E0T0M0NE5qTXNNVGN1T1RrMElEZzVMalUyTml3eE55NDVPVFFnUXprd0xqZzVNaXd4Tnk0NU9UUWdPVEl1TVRNNExERTRMalkxTWlBNU1pNDNPVElzTVRrdU9EUTNJRXc1Tmk0d05ESXNNalV1TnpjMUlFdzVOaTR3TmpRc01qVXVOelUzSUV3eE1ESXVPRFE1TERJNUxqWTNOQ0JNTVRBeUxqYzBOQ3d5T1M0ME9USWdUREUwT1M0Mk1qVXNNaTQxTWpjZ1RURTBPUzQyTWpVc01DNDRPVElnUXpFME9TNHpORE1zTUM0NE9USWdNVFE1TGpBMk1pd3dMamsyTlNBeE5EZ3VPREVzTVM0eE1TQk1NVEF5TGpZME1Td3lOeTQyTmpZZ1REazNMakl6TVN3eU5DNDFORElnVERrMExqSXlOaXd4T1M0d05qRWdRemt6TGpNeE15d3hOeTR6T1RRZ09URXVOVEkzTERFMkxqTTFPU0E0T1M0MU5qWXNNVFl1TXpVNElFTTRPQzQxTlRVc01UWXVNelU0SURnM0xqVTBOaXd4Tmk0Mk16SWdPRFl1TmpRNUxERTNMakUxSUVNNE15NDROemdzTVRndU56VWdOemt1TmpnM0xESXhMakUyT1NBM09TNHpOelFzTWpFdU16UTFJRU0zT1M0ek5Ua3NNakV1TXpVeklEYzVMak0wTlN3eU1TNHpOakVnTnprdU16TXNNakV1TXpZNUlFTTNOeTQzT1Rnc01qSXVNalUwSURjMkxqQTROQ3d5TWk0M01qSWdOelF1TXpjekxESXlMamN5TWlCRE56SXVNRGd4TERJeUxqY3lNaUEyT1M0NU5Ua3NNakV1T0RrZ05qZ3VNemszTERJd0xqTTRJRXcyT0M0eE5EVXNNakF1TVRNMUlFTTJOeTQzTURZc01Ua3VOamN5SURZM0xqTXlNeXd4T1M0eE5UWWdOamN1TURBMkxERTRMall3TVNCRE5qWXVPVGc0TERFNExqVTFPU0EyTmk0NU5qZ3NNVGd1TlRFNUlEWTJMamswTml3eE9DNDBOemtnVERZMkxqY3hPU3d4T0M0d05qVWdRelkyTGpZNUxERTRMakF4TWlBMk5pNDJOVGdzTVRjdU9UWWdOall1TmpJMExERTNMamt4TVNCRE5qVXVOamcyTERFMkxqTXpOeUEyTXk0NU5URXNNVFV1TXpZMklEWXlMakExTXl3eE5TNHpOallnUXpZeExqQTBNaXd4TlM0ek5qWWdOakF1TURNekxERTFMalkwSURVNUxqRXpOaXd4Tmk0eE5UZ2dURE11TVRJMUxEUTRMalV3TWlCRE1DNDBNallzTlRBdU1EWXhJQzB3TGpZeE15dzFNeTQwTkRJZ01DNDRNVEVzTlRZdU1EUWdUREV4TGpBNE9TdzNOQzQzT1NCRE1URXVNalkyTERjMUxqRXhNeUF4TVM0MU16Y3NOelV1TXpVeklERXhMamcxTERjMUxqUTVOQ0JNTlRndU1qYzJMREV3TWk0eU9UZ2dRelU1TGpZM09Td3hNRE11TVRBNElEWXhMalF6TXl3eE1ETXVOak1nTmpNdU16UTRMREV3TXk0NE1EWWdRell6TGpneE1pd3hNRE11T0RRNElEWTBMakk0TlN3eE1ETXVPRGNnTmpRdU56VTBMREV3TXk0NE55QkROalVzTVRBekxqZzNJRFkxTGpJME9Td3hNRE11T0RZMElEWTFMalE1TkN3eE1ETXVPRFV5SUVNMk5TNDFOak1zTVRBekxqZzBPU0EyTlM0Mk16SXNNVEF6TGpnME55QTJOUzQzTURFc01UQXpMamcwTnlCRE5qVXVOelkwTERFd015NDRORGNnTmpVdU9ESTRMREV3TXk0NE5Ea2dOalV1T0Rrc01UQXpMamcxTWlCRE5qVXVPVGcyTERFd015NDROVFlnTmpZdU1EZ3NNVEF6TGpnMk15QTJOaTR4TnpNc01UQXpMamczTkNCRE5qWXVNamd5TERFd05TNDBOamNnTmpjdU16TXlMREV3Tnk0eE9UY2dOamd1TnpBeUxERXdOeTQ1T0RnZ1RERXhNUzR4TnpRc01UTXlMalV4SUVNeE1URXVOams0TERFek1pNDRNVElnTVRFeUxqSXpNaXd4TXpJdU9UWTFJREV4TWk0M05qUXNNVE15TGprMk5TQkRNVEUwTGpJMk1Td3hNekl1T1RZMUlERXhOUzR6TkRjc01UTXhMamMyTlNBeE1UVXVNelEzTERFek1DNHhNVE1nVERFeE5TNHpORGNzTVRBekxqVTFNU0JNTVRJeUxqUTFPQ3c1T1M0ME5EWWdRekV5TWk0NE1Ua3NPVGt1TWpNM0lERXlNeTR3T0Rjc09UZ3VPRGs0SURFeU15NHlNRGNzT1RndU5EazRJRXd4TWpjdU9EWTFMRGd5TGprd05TQkRNVE15TGpJM09TdzRNeTQzTURJZ01UTTJMalUxTnl3NE5DNDNOVE1nTVRRd0xqWXdOeXc0Tmk0d016TWdUREUwTVM0eE5DdzROaTQ0TmpJZ1F6RTBNUzQwTlRFc09EY3VNelEySURFME1TNDVOemNzT0RjdU5qRXpJREUwTWk0MU1UWXNPRGN1TmpFeklFTXhOREl1TnprMExEZzNMall4TXlBeE5ETXVNRGMyTERnM0xqVTBNaUF4TkRNdU16TXpMRGczTGpNNU15Qk1NVFk1TGpnMk5TdzNNaTR3TnpZZ1RERTVNeXc0TlM0ME16TWdRekU1TXk0MU1qTXNPRFV1TnpNMUlERTVOQzR3TlRnc09EVXVPRGc0SURFNU5DNDFPU3c0TlM0NE9EZ2dRekU1Tmk0d09EY3NPRFV1T0RnNElERTVOeTR4TnpNc09EUXVOamc1SURFNU55NHhOek1zT0RNdU1ETTJJRXd4T1RjdU1UY3pMREk1TGpBek5TQkRNVGszTGpFM015d3lPQzQwTlRFZ01UazJMamcyTVN3eU55NDVNVEVnTVRrMkxqTTFOU3d5Tnk0Mk1Ua2dRekU1Tmk0ek5UVXNNamN1TmpFNUlERTNNUzQ0TkRNc01UTXVORFkzSURFM01DNHpPRFVzTVRJdU5qSTJJRU14TnpBdU1UTXlMREV5TGpRNElERTJPUzQ0TlN3eE1pNDBNRGNnTVRZNUxqVTJPQ3d4TWk0ME1EY2dRekUyT1M0eU9EVXNNVEl1TkRBM0lERTJPUzR3TURJc01USXVORGd4SURFMk9DNDNORGtzTVRJdU5qSTNJRU14TmpndU1UUXpMREV5TGprM09DQXhOalV1TnpVMkxERTBMak0xTnlBeE5qUXVOREkwTERFMUxqRXlOU0JNTVRVNUxqWXhOU3d4TUM0NE55QkRNVFU0TGpjNU5pd3hNQzR4TkRVZ01UVTRMakUxTkN3NExqa3pOeUF4TlRndU1EVTBMRGN1T1RNMElFTXhOVGd1TURRMUxEY3VPRE0zSURFMU9DNHdNelFzTnk0M016a2dNVFU0TGpBeU1TdzNMalkwSUVNeE5UZ3VNREExTERjdU5USXpJREUxTnk0NU9UZ3NOeTQwTVNBeE5UY3VPVGs0TERjdU16QTBJRXd4TlRjdU9UazRMRFl1TkRFNElFTXhOVGN1T1RrNExEVXVPRE0wSURFMU55NDJPRFlzTlM0eU9UVWdNVFUzTGpFNE1TdzFMakF3TWlCRE1UVTJMall5TkN3MExqWTRJREUxTUM0ME5ESXNNUzR4TVRFZ01UVXdMalEwTWl3eExqRXhNU0JETVRVd0xqRTRPU3d3TGprMk5TQXhORGt1T1RBM0xEQXVPRGt5SURFME9TNDJNalVzTUM0NE9USWlJR2xrUFNKR2FXeHNMVEVpSUdacGJHdzlJaU0wTlRWQk5qUWlQand2Y0dGMGFENEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0E4Y0dGMGFDQmtQU0pOT1RZdU1ESTNMREkxTGpZek5pQk1NVFF5TGpZd015dzFNaTQxTWpjZ1F6RTBNeTQ0TURjc05UTXVNakl5SURFME5DNDFPRElzTlRRdU1URTBJREUwTkM0NE5EVXNOVFV1TURZNElFd3hORFF1T0RNMUxEVTFMakEzTlNCTU5qTXVORFl4TERFd01pNHdOVGNnVERZekxqUTJMREV3TWk0d05UY2dRell4TGpnd05pd3hNREV1T1RBMUlEWXdMakkyTVN3eE1ERXVORFUzSURVNUxqQTFOeXd4TURBdU56WXlJRXd4TWk0ME9ERXNOek11T0RjeElFdzVOaTR3TWpjc01qVXVOak0ySWlCcFpEMGlSbWxzYkMweUlpQm1hV3hzUFNJalJrRkdRVVpCSWo0OEwzQmhkR2crQ2lBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ1BIQmhkR2dnWkQwaVRUWXpMalEyTVN3eE1ESXVNVGMwSUVNMk15NDBOVE1zTVRBeUxqRTNOQ0EyTXk0ME5EWXNNVEF5TGpFM05DQTJNeTQwTXprc01UQXlMakUzTWlCRE5qRXVOelEyTERFd01pNHdNVFlnTmpBdU1qRXhMREV3TVM0MU5qTWdOVGd1T1RrNExERXdNQzQ0TmpNZ1RERXlMalF5TWl3M015NDVOek1nUXpFeUxqTTROaXczTXk0NU5USWdNVEl1TXpZMExEY3pMamt4TkNBeE1pNHpOalFzTnpNdU9EY3hJRU14TWk0ek5qUXNOek11T0RNZ01USXVNemcyTERjekxqYzVNU0F4TWk0ME1qSXNOek11TnpjZ1REazFMamsyT0N3eU5TNDFNelVnUXprMkxqQXdOQ3d5TlM0MU1UUWdPVFl1TURRNUxESTFMalV4TkNBNU5pNHdPRFVzTWpVdU5UTTFJRXd4TkRJdU5qWXhMRFV5TGpReU5pQkRNVFF6TGpnNE9DdzFNeTR4TXpRZ01UUTBMalk0TWl3MU5DNHdNemdnTVRRMExqazFOeXcxTlM0d016Y2dRekUwTkM0NU55dzFOUzR3T0RNZ01UUTBMamsxTXl3MU5TNHhNek1nTVRRMExqa3hOU3cxTlM0eE5qRWdRekUwTkM0NU1URXNOVFV1TVRZMUlERTBOQzQ0T1Rnc05UVXVNVGMwSURFME5DNDRPVFFzTlRVdU1UYzNJRXcyTXk0MU1Ua3NNVEF5TGpFMU9DQkROak11TlRBeExERXdNaTR4TmprZ05qTXVORGd4TERFd01pNHhOelFnTmpNdU5EWXhMREV3TWk0eE56UWdURFl6TGpRMk1Td3hNREl1TVRjMElGb2dUVEV5TGpjeE5DdzNNeTQ0TnpFZ1REVTVMakV4TlN3eE1EQXVOall4SUVNMk1DNHlPVE1zTVRBeExqTTBNU0EyTVM0M09EWXNNVEF4TGpjNE1pQTJNeTQwTXpVc01UQXhMamt6TnlCTU1UUTBMamN3Tnl3MU5TNHdNVFVnUXpFME5DNDBNamdzTlRRdU1UQTRJREUwTXk0Mk9ESXNOVE11TWpnMUlERTBNaTQxTkRRc05USXVOakk0SUV3NU5pNHdNamNzTWpVdU56Y3hJRXd4TWk0M01UUXNOek11T0RjeElFd3hNaTQzTVRRc056TXVPRGN4SUZvaUlHbGtQU0pHYVd4c0xUTWlJR1pwYkd3OUlpTTJNRGRFT0VJaVBqd3ZjR0YwYUQ0S0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQThjR0YwYUNCa1BTSk5NVFE0TGpNeU55dzFPQzQwTnpFZ1F6RTBPQzR4TkRVc05UZ3VORGdnTVRRM0xqazJNaXcxT0M0ME9DQXhORGN1TnpneExEVTRMalEzTWlCRE1UUTFMamc0Tnl3MU9DNHpPRGtnTVRRMExqUTNPU3cxTnk0ME16UWdNVFEwTGpZek5pdzFOaTR6TkNCRE1UUTBMalk0T1N3MU5TNDVOamNnTVRRMExqWTJOQ3cxTlM0MU9UY2dNVFEwTGpVMk5DdzFOUzR5TXpVZ1REWXpMalEyTVN3eE1ESXVNRFUzSUVNMk5DNHdPRGtzTVRBeUxqRXhOU0EyTkM0M016TXNNVEF5TGpFeklEWTFMak0zT1N3eE1ESXVNRGs1SUVNMk5TNDFOakVzTVRBeUxqQTVJRFkxTGpjME15d3hNREl1TURrZ05qVXVPVEkxTERFd01pNHdPVGdnUXpZM0xqZ3hPU3d4TURJdU1UZ3hJRFk1TGpJeU55d3hNRE11TVRNMklEWTVMakEzTERFd05DNHlNeUJNTVRRNExqTXlOeXcxT0M0ME56RWlJR2xrUFNKR2FXeHNMVFFpSUdacGJHdzlJaU5HUmtaR1JrWWlQand2Y0dGMGFENEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0E4Y0dGMGFDQmtQU0pOTmprdU1EY3NNVEEwTGpNME55QkROamt1TURRNExERXdOQzR6TkRjZ05qa3VNREkxTERFd05DNHpOQ0EyT1M0d01EVXNNVEEwTGpNeU55QkROamd1T1RZNExERXdOQzR6TURFZ05qZ3VPVFE0TERFd05DNHlOVGNnTmpndU9UVTFMREV3TkM0eU1UTWdRelk1TERFd015NDRPVFlnTmpndU9EazRMREV3TXk0MU56WWdOamd1TmpVNExERXdNeTR5T0RnZ1F6WTRMakUxTXl3eE1ESXVOamM0SURZM0xqRXdNeXd4TURJdU1qWTJJRFkxTGpreUxERXdNaTR5TVRRZ1F6WTFMamMwTWl3eE1ESXVNakEySURZMUxqVTJNeXd4TURJdU1qQTNJRFkxTGpNNE5Td3hNREl1TWpFMUlFTTJOQzQzTkRJc01UQXlMakkwTmlBMk5DNHdPRGNzTVRBeUxqSXpNaUEyTXk0ME5Td3hNREl1TVRjMElFTTJNeTR6T1Rrc01UQXlMakUyT1NBMk15NHpOVGdzTVRBeUxqRXpNaUEyTXk0ek5EY3NNVEF5TGpBNE1pQkROak11TXpNMkxERXdNaTR3TXpNZ05qTXVNelU0TERFd01TNDVPREVnTmpNdU5EQXlMREV3TVM0NU5UWWdUREUwTkM0MU1EWXNOVFV1TVRNMElFTXhORFF1TlRNM0xEVTFMakV4TmlBeE5EUXVOVGMxTERVMUxqRXhNeUF4TkRRdU5qQTVMRFUxTGpFeU55QkRNVFEwTGpZME1pdzFOUzR4TkRFZ01UUTBMalkyT0N3MU5TNHhOeUF4TkRRdU5qYzNMRFUxTGpJd05DQkRNVFEwTGpjNE1TdzFOUzQxT0RVZ01UUTBMamd3Tml3MU5TNDVOeklnTVRRMExqYzFNU3cxTmk0ek5UY2dRekUwTkM0M01EWXNOVFl1TmpjeklERTBOQzQ0TURnc05UWXVPVGswSURFME5TNHdORGNzTlRjdU1qZ3lJRU14TkRVdU5UVXpMRFUzTGpnNU1pQXhORFl1TmpBeUxEVTRMak13TXlBeE5EY3VOemcyTERVNExqTTFOU0JETVRRM0xqazJOQ3cxT0M0ek5qTWdNVFE0TGpFME15dzFPQzR6TmpNZ01UUTRMak15TVN3MU9DNHpOVFFnUXpFME9DNHpOemNzTlRndU16VXlJREUwT0M0ME1qUXNOVGd1TXpnM0lERTBPQzQwTXprc05UZ3VORE00SUVNeE5EZ3VORFUwTERVNExqUTVJREUwT0M0ME16SXNOVGd1TlRRMUlERTBPQzR6T0RVc05UZ3VOVGN5SUV3Mk9TNHhNamtzTVRBMExqTXpNU0JETmprdU1URXhMREV3TkM0ek5ESWdOamt1TURrc01UQTBMak0wTnlBMk9TNHdOeXd4TURRdU16UTNJRXcyT1M0d055d3hNRFF1TXpRM0lGb2dUVFkxTGpZMk5Td3hNREV1T1RjMUlFTTJOUzQzTlRRc01UQXhMamszTlNBMk5TNDRORElzTVRBeExqazNOeUEyTlM0NU15d3hNREV1T1RneElFTTJOeTR4T1RZc01UQXlMakF6TnlBMk9DNHlPRE1zTVRBeUxqUTJPU0EyT0M0NE16Z3NNVEF6TGpFek9TQkROamt1TURZMUxERXdNeTQwTVRNZ05qa3VNVGc0TERFd015NDNNVFFnTmprdU1UazRMREV3TkM0d01qRWdUREUwTnk0NE9ETXNOVGd1TlRreUlFTXhORGN1T0RRM0xEVTRMalU1TWlBeE5EY3VPREV4TERVNExqVTVNU0F4TkRjdU56YzJMRFU0TGpVNE9TQkRNVFEyTGpVd09TdzFPQzQxTXpNZ01UUTFMalF5TWl3MU9DNHhJREUwTkM0NE5qY3NOVGN1TkRNeElFTXhORFF1TlRnMUxEVTNMakE1TVNBeE5EUXVORFkxTERVMkxqY3dOeUF4TkRRdU5USXNOVFl1TXpJMElFTXhORFF1TlRZekxEVTJMakF5TVNBeE5EUXVOVFV5TERVMUxqY3hOaUF4TkRRdU5EZzRMRFUxTGpReE5DQk1Oak11T0RRMkxERXdNUzQ1TnlCRE5qUXVNelV6TERFd01pNHdNRElnTmpRdU9EWTNMREV3TWk0d01EWWdOalV1TXpjMExERXdNUzQ1T0RJZ1F6WTFMalEzTVN3eE1ERXVPVGMzSURZMUxqVTJPQ3d4TURFdU9UYzFJRFkxTGpZMk5Td3hNREV1T1RjMUlFdzJOUzQyTmpVc01UQXhMamszTlNCYUlpQnBaRDBpUm1sc2JDMDFJaUJtYVd4c1BTSWpOakEzUkRoQ0lqNDhMM0JoZEdnK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdQSEJoZEdnZ1pEMGlUVEl1TWpBNExEVTFMakV6TkNCRE1TNHlNRGNzTlRNdU16QTNJREV1T1RZM0xEVXdMamt4TnlBekxqa3dOaXcwT1M0M09UY2dURFU1TGpreE55d3hOeTQwTlRNZ1F6WXhMamcxTml3eE5pNHpNek1nTmpRdU1qUXhMREUyTGprd055QTJOUzR5TkRNc01UZ3VOek0wSUV3Mk5TNDBOelVzTVRrdU1UUTBJRU0yTlM0NE56SXNNVGt1T0RneUlEWTJMak0yT0N3eU1DNDFOaUEyTmk0NU5EVXNNakV1TVRZMUlFdzJOeTR5TWpNc01qRXVORE0xSUVNM01DNDFORGdzTWpRdU5qUTVJRGMxTGpnd05pd3lOUzR4TlRFZ09EQXVNVEV4TERJeUxqWTJOU0JNT0RjdU5ETXNNVGd1TkRRMUlFTTRPUzR6Tnl3eE55NHpNallnT1RFdU56VTBMREUzTGpnNU9TQTVNaTQzTlRVc01Ua3VOekkzSUV3NU5pNHdNRFVzTWpVdU5qVTFJRXd4TWk0ME9EWXNOek11T0RnMElFd3lMakl3T0N3MU5TNHhNelFnV2lJZ2FXUTlJa1pwYkd3dE5pSWdabWxzYkQwaUkwWkJSa0ZHUVNJK1BDOXdZWFJvUGdvZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lEeHdZWFJvSUdROUlrMHhNaTQwT0RZc056UXVNREF4SUVNeE1pNDBOellzTnpRdU1EQXhJREV5TGpRMk5TdzNNeTQ1T1RrZ01USXVORFUxTERjekxqazVOaUJETVRJdU5ESTBMRGN6TGprNE9DQXhNaTR6T1Rrc056TXVPVFkzSURFeUxqTTROQ3czTXk0NU5DQk1NaTR4TURZc05UVXVNVGtnUXpFdU1EYzFMRFV6TGpNeElERXVPRFUzTERVd0xqZzBOU0F6TGpnME9DdzBPUzQyT1RZZ1REVTVMamcxT0N3eE55NHpOVElnUXpZd0xqVXlOU3d4Tmk0NU5qY2dOakV1TWpjeExERTJMamMyTkNBMk1pNHdNVFlzTVRZdU56WTBJRU0yTXk0ME16RXNNVFl1TnpZMElEWTBMalkyTml3eE55NDBOallnTmpVdU16STNMREU0TGpZME5pQkROalV1TXpNM0xERTRMalkxTkNBMk5TNHpORFVzTVRndU5qWXpJRFkxTGpNMU1Td3hPQzQyTnpRZ1REWTFMalUzT0N3eE9TNHdPRGdnUXpZMUxqVTROQ3d4T1M0eElEWTFMalU0T1N3eE9TNHhNVElnTmpVdU5Ua3hMREU1TGpFeU5pQkROalV1T1RnMUxERTVMamd6T0NBMk5pNDBOamtzTWpBdU5EazNJRFkzTGpBekxESXhMakE0TlNCTU5qY3VNekExTERJeExqTTFNU0JETmprdU1UVXhMREl6TGpFek55QTNNUzQyTkRrc01qUXVNVElnTnpRdU16TTJMREkwTGpFeUlFTTNOaTR6TVRNc01qUXVNVElnTnpndU1qa3NNak11TlRneUlEZ3dMakExTXl3eU1pNDFOak1nUXpnd0xqQTJOQ3d5TWk0MU5UY2dPREF1TURjMkxESXlMalUxTXlBNE1DNHdPRGdzTWpJdU5UVWdURGczTGpNM01pd3hPQzR6TkRRZ1F6ZzRMakF6T0N3eE55NDVOVGtnT0RndU56ZzBMREUzTGpjMU5pQTRPUzQxTWprc01UY3VOelUySUVNNU1DNDVOVFlzTVRjdU56VTJJRGt5TGpJd01Td3hPQzQwTnpJZ09USXVPRFU0TERFNUxqWTNJRXc1Tmk0eE1EY3NNalV1TlRrNUlFTTVOaTR4TXpnc01qVXVOalUwSURrMkxqRXhPQ3d5TlM0M01qUWdPVFl1TURZekxESTFMamMxTmlCTU1USXVOVFExTERjekxqazROU0JETVRJdU5USTJMRGN6TGprNU5pQXhNaTQxTURZc056UXVNREF4SURFeUxqUTROaXczTkM0d01ERWdUREV5TGpRNE5pdzNOQzR3TURFZ1dpQk5Oakl1TURFMkxERTJMams1TnlCRE5qRXVNekV5TERFMkxqazVOeUEyTUM0Mk1EWXNNVGN1TVRrZ05Ua3VPVGMxTERFM0xqVTFOQ0JNTXk0NU5qVXNORGt1T0RrNUlFTXlMakE0TXl3MU1DNDVPRFVnTVM0ek5ERXNOVE11TXpBNElESXVNekVzTlRVdU1EYzRJRXd4TWk0MU16RXNOek11TnpJeklFdzVOUzQ0TkRnc01qVXVOakV4SUV3NU1pNDJOVE1zTVRrdU56Z3lJRU01TWk0d016Z3NNVGd1TmpZZ09UQXVPRGNzTVRjdU9Ua2dPRGt1TlRJNUxERTNMams1SUVNNE9DNDRNalVzTVRjdU9Ua2dPRGd1TVRFNUxERTRMakU0TWlBNE55NDBPRGtzTVRndU5UUTNJRXc0TUM0eE56SXNNakl1TnpjeUlFTTRNQzR4TmpFc01qSXVOemM0SURnd0xqRTBPU3d5TWk0M09ESWdPREF1TVRNM0xESXlMamM0TlNCRE56Z3VNelEyTERJekxqZ3hNU0EzTmk0ek5ERXNNalF1TXpVMElEYzBMak16Tml3eU5DNHpOVFFnUXpjeExqVTRPQ3d5TkM0ek5UUWdOamt1TURNekxESXpMak0wTnlBMk55NHhORElzTWpFdU5URTVJRXcyTmk0NE5qUXNNakV1TWpRNUlFTTJOaTR5Tnpjc01qQXVOak0wSURZMUxqYzNOQ3d4T1M0NU5EY2dOalV1TXpZM0xERTVMakl3TXlCRE5qVXVNellzTVRrdU1Ua3lJRFkxTGpNMU5pd3hPUzR4TnprZ05qVXVNelUwTERFNUxqRTJOaUJNTmpVdU1UWXpMREU0TGpneE9TQkROalV1TVRVMExERTRMamd4TVNBMk5TNHhORFlzTVRndU9EQXhJRFkxTGpFMExERTRMamM1SUVNMk5DNDFNalVzTVRjdU5qWTNJRFl6TGpNMU55d3hOaTQ1T1RjZ05qSXVNREUyTERFMkxqazVOeUJNTmpJdU1ERTJMREUyTGprNU55QmFJaUJwWkQwaVJtbHNiQzAzSWlCbWFXeHNQU0lqTmpBM1JEaENJajQ4TDNCaGRHZytDaUFnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnUEhCaGRHZ2daRDBpVFRReUxqUXpOQ3cwT0M0NE1EZ2dURFF5TGpRek5DdzBPQzQ0TURnZ1F6TTVMamt5TkN3ME9DNDRNRGNnTXpjdU56TTNMRFEzTGpVMUlETTJMalU0TWl3ME5TNDBORE1nUXpNMExqYzNNU3cwTWk0eE16a2dNell1TVRRMExETTNMamd3T1NBek9TNDJOREVzTXpVdU56ZzVJRXcxTVM0NU16SXNNamd1TmpreElFTTFNeTR4TURNc01qZ3VNREUxSURVMExqUXhNeXd5Tnk0Mk5UZ2dOVFV1TnpJeExESTNMalkxT0NCRE5UZ3VNak14TERJM0xqWTFPQ0EyTUM0ME1UZ3NNamd1T1RFMklEWXhMalUzTXl3ek1TNHdNak1nUXpZekxqTTROQ3d6TkM0ek1qY2dOakl1TURFeUxETTRMalkxTnlBMU9DNDFNVFFzTkRBdU5qYzNJRXcwTmk0eU1qTXNORGN1TnpjMUlFTTBOUzR3TlRNc05EZ3VORFVnTkRNdU56UXlMRFE0TGpnd09DQTBNaTQwTXpRc05EZ3VPREE0SUV3ME1pNDBNelFzTkRndU9EQTRJRm9nVFRVMUxqY3lNU3d5T0M0eE1qVWdRelUwTGpRNU5Td3lPQzR4TWpVZ05UTXVNalkxTERJNExqUTJNU0ExTWk0eE5qWXNNamt1TURrMklFd3pPUzQ0TnpVc016WXVNVGswSUVNek5pNDFPVFlzTXpndU1EZzNJRE0xTGpNd01pdzBNaTR4TXpZZ016WXVPVGt5TERRMUxqSXhPQ0JETXpndU1EWXpMRFEzTGpFM015QTBNQzR3T1Rnc05EZ3VNelFnTkRJdU5ETTBMRFE0TGpNMElFTTBNeTQyTmpFc05EZ3VNelFnTkRRdU9Ea3NORGd1TURBMUlEUTFMams1TERRM0xqTTNJRXcxT0M0eU9ERXNOREF1TWpjeUlFTTJNUzQxTml3ek9DNHpOemtnTmpJdU9EVXpMRE0wTGpNeklEWXhMakUyTkN3ek1TNHlORGdnUXpZd0xqQTVNaXd5T1M0eU9UTWdOVGd1TURVNExESTRMakV5TlNBMU5TNDNNakVzTWpndU1USTFJRXcxTlM0M01qRXNNamd1TVRJMUlGb2lJR2xrUFNKR2FXeHNMVGdpSUdacGJHdzlJaU0yTURkRU9FSWlQand2Y0dGMGFENEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0E4Y0dGMGFDQmtQU0pOTVRRNUxqVTRPQ3d5TGpRd055QkRNVFE1TGpVNE9Dd3lMalF3TnlBeE5UVXVOelk0TERVdU9UYzFJREUxTmk0ek1qVXNOaTR5T1RjZ1RERTFOaTR6TWpVc055NHhPRFFnUXpFMU5pNHpNalVzTnk0ek5pQXhOVFl1TXpNNExEY3VOVFEwSURFMU5pNHpOaklzTnk0M016TWdRekUxTmk0ek56TXNOeTQ0TVRRZ01UVTJMak00TWl3M0xqZzVOQ0F4TlRZdU16a3NOeTQ1TnpVZ1F6RTFOaTQxTXl3NUxqTTVJREUxTnk0ek5qTXNNVEF1T1RjeklERTFPQzQwT1RVc01URXVPVGMwSUV3eE5qVXVPRGt4TERFNExqVXhPU0JETVRZMkxqQTJPQ3d4T0M0Mk56VWdNVFkyTGpJME9Td3hPQzQ0TVRRZ01UWTJMalF6TWl3eE9DNDVNelFnUXpFMk9DNHdNVEVzTVRrdU9UYzBJREUyT1M0ek9ESXNNVGt1TkNBeE5qa3VORGswTERFM0xqWTFNaUJETVRZNUxqVTBNeXd4Tmk0NE5qZ2dNVFk1TGpVMU1Td3hOaTR3TlRjZ01UWTVMalV4Tnl3eE5TNHlNak1nVERFMk9TNDFNVFFzTVRVdU1EWXpJRXd4TmprdU5URTBMREV6TGpreE1pQkRNVGN3TGpjNExERTBMalkwTWlBeE9UVXVOVEF4TERJNExqa3hOU0F4T1RVdU5UQXhMREk0TGpreE5TQk1NVGsxTGpVd01TdzRNaTQ1TVRVZ1F6RTVOUzQxTURFc09EUXVNREExSURFNU5DNDNNekVzT0RRdU5EUTFJREU1TXk0M09ERXNPRE11T0RrM0lFd3hOVEV1TXpBNExEVTVMak0zTkNCRE1UVXdMak0xT0N3MU9DNDRNallnTVRRNUxqVTRPQ3cxTnk0ME9UY2dNVFE1TGpVNE9DdzFOaTQwTURnZ1RERTBPUzQxT0Rnc01qSXVNemMxSWlCcFpEMGlSbWxzYkMwNUlpQm1hV3hzUFNJalJrRkdRVVpCSWo0OEwzQmhkR2crQ2lBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ1BIQmhkR2dnWkQwaVRURTVOQzQxTlRNc09EUXVNalVnUXpFNU5DNHlPVFlzT0RRdU1qVWdNVGswTGpBeE15dzROQzR4TmpVZ01Ua3pMamN5TWl3NE15NDVPVGNnVERFMU1TNHlOU3cxT1M0ME56WWdRekUxTUM0eU5qa3NOVGd1T1RBNUlERTBPUzQwTnpFc05UY3VOVE16SURFME9TNDBOekVzTlRZdU5EQTRJRXd4TkRrdU5EY3hMREl5TGpNM05TQk1NVFE1TGpjd05Td3lNaTR6TnpVZ1RERTBPUzQzTURVc05UWXVOREE0SUVNeE5Ea3VOekExTERVM0xqUTFPU0F4TlRBdU5EVXNOVGd1TnpRMElERTFNUzR6TmpZc05Ua3VNamMwSUV3eE9UTXVPRE01TERnekxqYzVOU0JETVRrMExqSTJNeXc0TkM0d05DQXhPVFF1TmpVMUxEZzBMakE0TXlBeE9UUXVPVFF5TERnekxqa3hOeUJETVRrMUxqSXlOeXc0TXk0M05UTWdNVGsxTGpNNE5DdzRNeTR6T1RjZ01UazFMak00TkN3NE1pNDVNVFVnVERFNU5TNHpPRFFzTWpndU9UZ3lJRU14T1RRdU1UQXlMREk0TGpJME1pQXhOekl1TVRBMExERTFMalUwTWlBeE5qa3VOak14TERFMExqRXhOQ0JNTVRZNUxqWXpOQ3d4TlM0eU1pQkRNVFk1TGpZMk9Dd3hOaTR3TlRJZ01UWTVMalkyTERFMkxqZzNOQ0F4TmprdU5qRXNNVGN1TmpVNUlFTXhOamt1TlRVMkxERTRMalV3TXlBeE5qa3VNakUwTERFNUxqRXlNeUF4TmpndU5qUTNMREU1TGpRd05TQkRNVFk0TGpBeU9Dd3hPUzQzTVRRZ01UWTNMakU1Tnl3eE9TNDFOemdnTVRZMkxqTTJOeXd4T1M0d016SWdRekUyTmk0eE9ERXNNVGd1T1RBNUlERTJOUzQ1T1RVc01UZ3VOelkySURFMk5TNDRNVFFzTVRndU5qQTJJRXd4TlRndU5ERTNMREV5TGpBMk1pQkRNVFUzTGpJMU9Td3hNUzR3TXpZZ01UVTJMalF4T0N3NUxqUXpOeUF4TlRZdU1qYzBMRGN1T1RnMklFTXhOVFl1TWpZMkxEY3VPVEEzSURFMU5pNHlOVGNzTnk0NE1qY2dNVFUyTGpJME55dzNMamMwT0NCRE1UVTJMakl5TVN3M0xqVTFOU0F4TlRZdU1qQTVMRGN1TXpZMUlERTFOaTR5TURrc055NHhPRFFnVERFMU5pNHlNRGtzTmk0ek5qUWdRekUxTlM0ek56VXNOUzQ0T0RNZ01UUTVMalV5T1N3eUxqVXdPQ0F4TkRrdU5USTVMREl1TlRBNElFd3hORGt1TmpRMkxESXVNekEySUVNeE5Ea3VOalEyTERJdU16QTJJREUxTlM0NE1qY3NOUzQ0TnpRZ01UVTJMak00TkN3MkxqRTVOaUJNTVRVMkxqUTBNaXcyTGpJeklFd3hOVFl1TkRReUxEY3VNVGcwSUVNeE5UWXVORFF5TERjdU16VTFJREUxTmk0ME5UUXNOeTQxTXpVZ01UVTJMalEzT0N3M0xqY3hOeUJETVRVMkxqUTRPU3czTGpnZ01UVTJMalE1T1N3M0xqZzRNaUF4TlRZdU5UQTNMRGN1T1RZeklFTXhOVFl1TmpRMUxEa3VNelU0SURFMU55NDBOVFVzTVRBdU9EazRJREUxT0M0MU56SXNNVEV1T0RnMklFd3hOalV1T1RZNUxERTRMalF6TVNCRE1UWTJMakUwTWl3eE9DNDFPRFFnTVRZMkxqTXhPU3d4T0M0M01pQXhOall1TkRrMkxERTRMamd6TnlCRE1UWTNMakkxTkN3eE9TNHpNellnTVRZNExERTVMalEyTnlBeE5qZ3VOVFF6TERFNUxqRTVOaUJETVRZNUxqQXpNeXd4T0M0NU5UTWdNVFk1TGpNeU9Td3hPQzQwTURFZ01UWTVMak0zTnl3eE55NDJORFVnUXpFMk9TNDBNamNzTVRZdU9EWTNJREUyT1M0ME16UXNNVFl1TURVMElERTJPUzQwTURFc01UVXVNakk0SUV3eE5qa3VNemszTERFMUxqQTJOU0JNTVRZNUxqTTVOeXd4TXk0M01TQk1NVFk1TGpVM01pd3hNeTQ0TVNCRE1UY3dMamd6T1N3eE5DNDFOREVnTVRrMUxqVTFPU3d5T0M0NE1UUWdNVGsxTGpVMU9Td3lPQzQ0TVRRZ1RERTVOUzQyTVRnc01qZ3VPRFEzSUV3eE9UVXVOakU0TERneUxqa3hOU0JETVRrMUxqWXhPQ3c0TXk0ME9EUWdNVGsxTGpReUxEZ3pMamt4TVNBeE9UVXVNRFU1TERnMExqRXhPU0JETVRrMExqa3dPQ3c0TkM0eU1EWWdNVGswTGpjek55dzROQzR5TlNBeE9UUXVOVFV6TERnMExqSTFJaUJwWkQwaVJtbHNiQzB4TUNJZ1ptbHNiRDBpSXpZd04wUTRRaUkrUEM5d1lYUm9QZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUR4d1lYUm9JR1E5SWsweE5EVXVOamcxTERVMkxqRTJNU0JNTVRZNUxqZ3NOekF1TURneklFd3hORE11T0RJeUxEZzFMakE0TVNCTU1UUXlMak0yTERnMExqYzNOQ0JETVRNMUxqZ3lOaXc0TWk0Mk1EUWdNVEk0TGpjek1pdzRNUzR3TkRZZ01USXhMak0wTVN3NE1DNHhOVGdnUXpFeE5pNDVOellzTnprdU5qTTBJREV4TWk0Mk56Z3NPREV1TWpVMElERXhNUzQzTkRNc09ETXVOemM0SUVNeE1URXVOVEEyTERnMExqUXhOQ0F4TVRFdU5UQXpMRGcxTGpBM01TQXhNVEV1TnpNeUxEZzFMamN3TmlCRE1URXpMakkzTERnNUxqazNNeUF4TVRVdU9UWTRMRGswTGpBMk9TQXhNVGt1TnpJM0xEazNMamcwTVNCTU1USXdMakkxT1N3NU9DNDJPRFlnUXpFeU1DNHlOaXc1T0M0Mk9EVWdPVFF1TWpneUxERXhNeTQyT0RNZ09UUXVNamd5TERFeE15NDJPRE1nVERjd0xqRTJOeXc1T1M0M05qRWdUREUwTlM0Mk9EVXNOVFl1TVRZeElpQnBaRDBpUm1sc2JDMHhNU0lnWm1sc2JEMGlJMFpHUmtaR1JpSStQQzl3WVhSb1Bnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJRHh3WVhSb0lHUTlJazA1TkM0eU9ESXNNVEV6TGpneE9DQk1PVFF1TWpJekxERXhNeTQzT0RVZ1REWTVMamt6TXl3NU9TNDNOakVnVERjd0xqRXdPQ3c1T1M0Mk5pQk1NVFExTGpZNE5TdzFOaTR3TWpZZ1RERTBOUzQzTkRNc05UWXVNRFU1SUV3eE56QXVNRE16TERjd0xqQTRNeUJNTVRRekxqZzBNaXc0TlM0eU1EVWdUREUwTXk0M09UY3NPRFV1TVRrMUlFTXhORE11TnpjeUxEZzFMakU1SURFME1pNHpNellzT0RRdU9EZzRJREUwTWk0ek16WXNPRFF1T0RnNElFTXhNelV1TnpnM0xEZ3lMamN4TkNBeE1qZ3VOekl6TERneExqRTJNeUF4TWpFdU16STNMRGd3TGpJM05DQkRNVEl3TGpjNE9DdzRNQzR5TURrZ01USXdMakl6Tml3NE1DNHhOemNnTVRFNUxqWTRPU3c0TUM0eE56Y2dRekV4TlM0NU16RXNPREF1TVRjM0lERXhNaTQyTXpVc09ERXVOekE0SURFeE1TNDROVElzT0RNdU9ERTVJRU14TVRFdU5qSTBMRGcwTGpRek1pQXhNVEV1TmpJeExEZzFMakExTXlBeE1URXVPRFF5TERnMUxqWTJOeUJETVRFekxqTTNOeXc0T1M0NU1qVWdNVEUyTGpBMU9DdzVNeTQ1T1RNZ01URTVMamd4TERrM0xqYzFPQ0JNTVRFNUxqZ3lOaXc1Tnk0M056a2dUREV5TUM0ek5USXNPVGd1TmpFMElFTXhNakF1TXpVMExEazRMall4TnlBeE1qQXVNelUyTERrNExqWXlJREV5TUM0ek5UZ3NPVGd1TmpJMElFd3hNakF1TkRJeUxEazRMamN5TmlCTU1USXdMak14Tnl3NU9DNDNPRGNnUXpFeU1DNHlOalFzT1RndU9ERTRJRGswTGpVNU9Td3hNVE11TmpNMUlEazBMak0wTERFeE15NDNPRFVnVERrMExqSTRNaXd4TVRNdU9ERTRJRXc1TkM0eU9ESXNNVEV6TGpneE9DQmFJRTAzTUM0ME1ERXNPVGt1TnpZeElFdzVOQzR5T0RJc01URXpMalUwT1NCTU1URTVMakE0TkN3NU9TNHlNamtnUXpFeE9TNDJNeXc1T0M0NU1UUWdNVEU1TGprekxEazRMamMwSURFeU1DNHhNREVzT1RndU5qVTBJRXd4TVRrdU5qTTFMRGszTGpreE5DQkRNVEUxTGpnMk5DdzVOQzR4TWpjZ01URXpMakUyT0N3NU1DNHdNek1nTVRFeExqWXlNaXc0TlM0M05EWWdRekV4TVM0ek9ESXNPRFV1TURjNUlERXhNUzR6T0RZc09EUXVOREEwSURFeE1TNDJNek1zT0RNdU56TTRJRU14TVRJdU5EUTRMRGd4TGpVek9TQXhNVFV1T0RNMkxEYzVMamswTXlBeE1Ua3VOamc1TERjNUxqazBNeUJETVRJd0xqSTBOaXczT1M0NU5ETWdNVEl3TGpnd05pdzNPUzQ1TnpZZ01USXhMak0xTlN3NE1DNHdORElnUXpFeU9DNDNOamNzT0RBdU9UTXpJREV6TlM0NE5EWXNPREl1TkRnM0lERTBNaTR6T1RZc09EUXVOall6SUVNeE5ETXVNak15TERnMExqZ3pPQ0F4TkRNdU5qRXhMRGcwTGpreE55QXhORE11TnpnMkxEZzBMamsyTnlCTU1UWTVMalUyTml3M01DNHdPRE1nVERFME5TNDJPRFVzTlRZdU1qazFJRXczTUM0ME1ERXNPVGt1TnpZeElFdzNNQzQwTURFc09Ua3VOell4SUZvaUlHbGtQU0pHYVd4c0xURXlJaUJtYVd4c1BTSWpOakEzUkRoQ0lqNDhMM0JoZEdnK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdQSEJoZEdnZ1pEMGlUVEUyTnk0eU15d3hPQzQ1TnprZ1RERTJOeTR5TXl3Mk9TNDROU0JNTVRNNUxqa3dPU3c0TlM0Mk1qTWdUREV6TXk0ME5EZ3NOekV1TkRVMklFTXhNekl1TlRNNExEWTVMalEySURFek1DNHdNaXcyT1M0M01UZ2dNVEkzTGpneU5DdzNNaTR3TXlCRE1USTJMamMyT1N3M015NHhOQ0F4TWpVdU9UTXhMRGMwTGpVNE5TQXhNalV1TkRrMExEYzJMakEwT0NCTU1URTVMakF6TkN3NU55NDJOellnVERreExqY3hNaXd4TVRNdU5EVWdURGt4TGpjeE1pdzJNaTQxTnprZ1RERTJOeTR5TXl3eE9DNDVOemtpSUdsa1BTSkdhV3hzTFRFeklpQm1hV3hzUFNJalJrWkdSa1pHSWo0OEwzQmhkR2crQ2lBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ1BIQmhkR2dnWkQwaVRUa3hMamN4TWl3eE1UTXVOVFkzSUVNNU1TNDJPVElzTVRFekxqVTJOeUE1TVM0Mk56SXNNVEV6TGpVMk1TQTVNUzQyTlRNc01URXpMalUxTVNCRE9URXVOakU0TERFeE15NDFNeUE1TVM0MU9UVXNNVEV6TGpRNU1pQTVNUzQxT1RVc01URXpMalExSUV3NU1TNDFPVFVzTmpJdU5UYzVJRU01TVM0MU9UVXNOakl1TlRNM0lEa3hMall4T0N3Mk1pNDBPVGtnT1RFdU5qVXpMRFl5TGpRM09DQk1NVFkzTGpFM01pd3hPQzQ0TnpnZ1F6RTJOeTR5TURnc01UZ3VPRFUzSURFMk55NHlOVElzTVRndU9EVTNJREUyTnk0eU9EZ3NNVGd1T0RjNElFTXhOamN1TXpJMExERTRMamc1T1NBeE5qY3VNelEzTERFNExqa3pOeUF4TmpjdU16UTNMREU0TGprM09TQk1NVFkzTGpNME55dzJPUzQ0TlNCRE1UWTNMak0wTnl3Mk9TNDRPVEVnTVRZM0xqTXlOQ3cyT1M0NU15QXhOamN1TWpnNExEWTVMamsxSUV3eE16a3VPVFkzTERnMUxqY3lOU0JETVRNNUxqa3pPU3c0TlM0M05ERWdNVE01TGprd05TdzROUzQzTkRVZ01UTTVMamczTXl3NE5TNDNNelVnUXpFek9TNDRORElzT0RVdU56STFJREV6T1M0NE1UWXNPRFV1TnpBeUlERXpPUzQ0TURJc09EVXVOamN5SUV3eE16TXVNelF5TERjeExqVXdOQ0JETVRNeUxqazJOeXczTUM0Mk9ESWdNVE15TGpJNExEY3dMakl5T1NBeE16RXVOREE0TERjd0xqSXlPU0JETVRNd0xqTXhPU3czTUM0eU1qa2dNVEk1TGpBME5DdzNNQzQ1TVRVZ01USTNMamt3T0N3M01pNHhNU0JETVRJMkxqZzNOQ3czTXk0eUlERXlOaTR3TXpRc056UXVOalEzSURFeU5TNDJNRFlzTnpZdU1EZ3lJRXd4TVRrdU1UUTJMRGszTGpjd09TQkRNVEU1TGpFek55dzVOeTQzTXpnZ01URTVMakV4T0N3NU55NDNOaklnTVRFNUxqQTVNaXc1Tnk0M056Y2dURGt4TGpjM0xERXhNeTQxTlRFZ1F6a3hMamMxTWl3eE1UTXVOVFl4SURreExqY3pNaXd4TVRNdU5UWTNJRGt4TGpjeE1pd3hNVE11TlRZM0lFdzVNUzQzTVRJc01URXpMalUyTnlCYUlFMDVNUzQ0TWprc05qSXVOalEzSUV3NU1TNDRNamtzTVRFekxqSTBPQ0JNTVRFNExqa3pOU3c1Tnk0MU9UZ2dUREV5TlM0ek9ESXNOell1TURFMUlFTXhNalV1T0RJM0xEYzBMalV5TlNBeE1qWXVOalkwTERjekxqQTRNU0F4TWpjdU56TTVMRGN4TGprMUlFTXhNamd1T1RFNUxEY3dMamN3T0NBeE16QXVNalUyTERZNUxqazVOaUF4TXpFdU5EQTRMRFk1TGprNU5pQkRNVE15TGpNM055dzJPUzQ1T1RZZ01UTXpMakV6T1N3M01DNDBPVGNnTVRNekxqVTFOQ3czTVM0ME1EY2dUREV6T1M0NU5qRXNPRFV1TkRVNElFd3hOamN1TVRFekxEWTVMamM0TWlCTU1UWTNMakV4TXl3eE9TNHhPREVnVERreExqZ3lPU3cyTWk0Mk5EY2dURGt4TGpneU9TdzJNaTQyTkRjZ1dpSWdhV1E5SWtacGJHd3RNVFFpSUdacGJHdzlJaU0yTURkRU9FSWlQand2Y0dGMGFENEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0E4Y0dGMGFDQmtQU0pOTVRZNExqVTBNeXd4T1M0eU1UTWdUREUyT0M0MU5ETXNOekF1TURneklFd3hOREV1TWpJeExEZzFMamcxTnlCTU1UTTBMamMyTVN3M01TNDJPRGtnUXpFek15NDROVEVzTmprdU5qazBJREV6TVM0ek16TXNOamt1T1RVeElERXlPUzR4TXpjc056SXVNall6SUVNeE1qZ3VNRGd5TERjekxqTTNOQ0F4TWpjdU1qUTBMRGMwTGpneE9TQXhNall1T0RBM0xEYzJMakk0TWlCTU1USXdMak0wTml3NU55NDVNRGtnVERrekxqQXlOU3d4TVRNdU5qZ3pJRXc1TXk0d01qVXNOakl1T0RFeklFd3hOamd1TlRRekxERTVMakl4TXlJZ2FXUTlJa1pwYkd3dE1UVWlJR1pwYkd3OUlpTkdSa1pHUmtZaVBqd3ZjR0YwYUQ0S0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQThjR0YwYUNCa1BTSk5PVE11TURJMUxERXhNeTQ0SUVNNU15NHdNRFVzTVRFekxqZ2dPVEl1T1RnMExERXhNeTQzT1RVZ09USXVPVFkyTERFeE15NDNPRFVnUXpreUxqa3pNU3d4TVRNdU56WTBJRGt5TGprd09Dd3hNVE11TnpJMUlEa3lMamt3T0N3eE1UTXVOamcwSUV3NU1pNDVNRGdzTmpJdU9ERXpJRU01TWk0NU1EZ3NOakl1TnpjeElEa3lMamt6TVN3Mk1pNDNNek1nT1RJdU9UWTJMRFl5TGpjeE1pQk1NVFk0TGpRNE5Dd3hPUzR4TVRJZ1F6RTJPQzQxTWl3eE9TNHdPU0F4TmpndU5UWTFMREU1TGpBNUlERTJPQzQyTURFc01Ua3VNVEV5SUVNeE5qZ3VOak0zTERFNUxqRXpNaUF4TmpndU5qWXNNVGt1TVRjeElERTJPQzQyTml3eE9TNHlNVElnVERFMk9DNDJOaXczTUM0d09ETWdRekUyT0M0Mk5pdzNNQzR4TWpVZ01UWTRMall6Tnl3M01DNHhOalFnTVRZNExqWXdNU3czTUM0eE9EUWdUREUwTVM0eU9DdzROUzQ1TlRnZ1F6RTBNUzR5TlRFc09EVXVPVGMxSURFME1TNHlNVGNzT0RVdU9UYzVJREUwTVM0eE9EWXNPRFV1T1RZNElFTXhOREV1TVRVMExEZzFMamsxT0NBeE5ERXVNVEk1TERnMUxqa3pOaUF4TkRFdU1URTFMRGcxTGprd05pQk1NVE0wTGpZMU5TdzNNUzQzTXpnZ1F6RXpOQzR5T0N3M01DNDVNVFVnTVRNekxqVTVNeXczTUM0ME5qTWdNVE15TGpjeUxEY3dMalEyTXlCRE1UTXhMall6TWl3M01DNDBOak1nTVRNd0xqTTFOeXczTVM0eE5EZ2dNVEk1TGpJeU1TdzNNaTR6TkRRZ1F6RXlPQzR4T0RZc056TXVORE16SURFeU55NHpORGNzTnpRdU9EZ3hJREV5Tmk0NU1Ua3NOell1TXpFMUlFd3hNakF1TkRVNExEazNMamswTXlCRE1USXdMalExTERrM0xqazNNaUF4TWpBdU5ETXhMRGszTGprNU5pQXhNakF1TkRBMUxEazRMakF4SUV3NU15NHdPRE1zTVRFekxqYzROU0JET1RNdU1EWTFMREV4TXk0M09UVWdPVE11TURRMUxERXhNeTQ0SURrekxqQXlOU3d4TVRNdU9DQk1PVE11TURJMUxERXhNeTQ0SUZvZ1RUa3pMakUwTWl3Mk1pNDRPREVnVERrekxqRTBNaXd4TVRNdU5EZ3hJRXd4TWpBdU1qUTRMRGszTGpnek1pQk1NVEkyTGpZNU5TdzNOaTR5TkRnZ1F6RXlOeTR4TkN3M05DNDNOVGdnTVRJM0xqazNOeXczTXk0ek1UVWdNVEk1TGpBMU1pdzNNaTR4T0RNZ1F6RXpNQzR5TXpFc056QXVPVFF5SURFek1TNDFOamdzTnpBdU1qSTVJREV6TWk0M01pdzNNQzR5TWprZ1F6RXpNeTQyT0Rrc056QXVNakk1SURFek5DNDBOVElzTnpBdU56TXhJREV6TkM0NE5qY3NOekV1TmpReElFd3hOREV1TWpjMExEZzFMalk1TWlCTU1UWTRMalF5Tml3M01DNHdNVFlnVERFMk9DNDBNallzTVRrdU5ERTFJRXc1TXk0eE5ESXNOakl1T0RneElFdzVNeTR4TkRJc05qSXVPRGd4SUZvaUlHbGtQU0pHYVd4c0xURTJJaUJtYVd4c1BTSWpOakEzUkRoQ0lqNDhMM0JoZEdnK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdQSEJoZEdnZ1pEMGlUVEUyT1M0NExEY3dMakE0TXlCTU1UUXlMalEzT0N3NE5TNDROVGNnVERFek5pNHdNVGdzTnpFdU5qZzVJRU14TXpVdU1UQTRMRFk1TGpZNU5DQXhNekl1TlRrc05qa3VPVFV4SURFek1DNHpPVE1zTnpJdU1qWXpJRU14TWprdU16TTVMRGN6TGpNM05DQXhNamd1TlN3M05DNDRNVGtnTVRJNExqQTJOQ3czTmk0eU9ESWdUREV5TVM0Mk1ETXNPVGN1T1RBNUlFdzVOQzR5T0RJc01URXpMalk0TXlCTU9UUXVNamd5TERZeUxqZ3hNeUJNTVRZNUxqZ3NNVGt1TWpFeklFd3hOamt1T0N3M01DNHdPRE1nV2lJZ2FXUTlJa1pwYkd3dE1UY2lJR1pwYkd3OUlpTkdRVVpCUmtFaVBqd3ZjR0YwYUQ0S0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQThjR0YwYUNCa1BTSk5PVFF1TWpneUxERXhNeTQ1TVRjZ1F6azBMakkwTVN3eE1UTXVPVEUzSURrMExqSXdNU3d4TVRNdU9UQTNJRGswTGpFMk5Td3hNVE11T0RnMklFTTVOQzR3T1RNc01URXpMamcwTlNBNU5DNHdORGdzTVRFekxqYzJOeUE1TkM0d05EZ3NNVEV6TGpZNE5DQk1PVFF1TURRNExEWXlMamd4TXlCRE9UUXVNRFE0TERZeUxqY3pJRGswTGpBNU15dzJNaTQyTlRJZ09UUXVNVFkxTERZeUxqWXhNU0JNTVRZNUxqWTRNeXd4T1M0d01TQkRNVFk1TGpjMU5Td3hPQzQ1TmprZ01UWTVMamcwTkN3eE9DNDVOamtnTVRZNUxqa3hOeXd4T1M0d01TQkRNVFk1TGprNE9Td3hPUzR3TlRJZ01UY3dMakF6TXl3eE9TNHhNamtnTVRjd0xqQXpNeXd4T1M0eU1USWdUREUzTUM0d016TXNOekF1TURneklFTXhOekF1TURNekxEY3dMakUyTmlBeE5qa3VPVGc1TERjd0xqSTBOQ0F4TmprdU9URTNMRGN3TGpJNE5TQk1NVFF5TGpVNU5TdzROaTR3TmlCRE1UUXlMalV6T0N3NE5pNHdPVElnTVRReUxqUTJPU3c0Tmk0eElERTBNaTQwTURjc09EWXVNRGdnUXpFME1pNHpORFFzT0RZdU1EWWdNVFF5TGpJNU15dzROaTR3TVRRZ01UUXlMakkyTml3NE5TNDVOVFFnVERFek5TNDRNRFVzTnpFdU56ZzJJRU14TXpVdU5EUTFMRGN3TGprNU55QXhNelF1T0RFekxEY3dMalU0SURFek15NDVOemNzTnpBdU5UZ2dRekV6TWk0NU1qRXNOekF1TlRnZ01UTXhMalkzTml3M01TNHlOVElnTVRNd0xqVTJNaXczTWk0ME1qUWdRekV5T1M0MU5DdzNNeTQxTURFZ01USTRMamN4TVN3M05DNDVNekVnTVRJNExqSTROeXczTmk0ek5EZ2dUREV5TVM0NE1qY3NPVGN1T1RjMklFTXhNakV1T0RFc09UZ3VNRE0wSURFeU1TNDNOekVzT1RndU1EZ3lJREV5TVM0M01pdzVPQzR4TVRJZ1REazBMak01T0N3eE1UTXVPRGcySUVNNU5DNHpOaklzTVRFekxqa3dOeUE1TkM0ek1qSXNNVEV6TGpreE55QTVOQzR5T0RJc01URXpMamt4TnlCTU9UUXVNamd5TERFeE15NDVNVGNnV2lCTk9UUXVOVEUxTERZeUxqazBPQ0JNT1RRdU5URTFMREV4TXk0eU56a2dUREV5TVM0ME1EWXNPVGN1TnpVMElFd3hNamN1T0RRc056WXVNakUxSUVNeE1qZ3VNamtzTnpRdU56QTRJREV5T1M0eE16Y3NOek11TWpRM0lERXpNQzR5TWpRc056SXVNVEF6SUVNeE16RXVOREkxTERjd0xqZ3pPQ0F4TXpJdU56a3pMRGN3TGpFeE1pQXhNek11T1RjM0xEY3dMakV4TWlCRE1UTTBMams1TlN3M01DNHhNVElnTVRNMUxqYzVOU3czTUM0Mk16Z2dNVE0yTGpJekxEY3hMalU1TWlCTU1UUXlMalU0TkN3NE5TNDFNallnVERFMk9TNDFOallzTmprdU9UUTRJRXd4TmprdU5UWTJMREU1TGpZeE55Qk1PVFF1TlRFMUxEWXlMamswT0NCTU9UUXVOVEUxTERZeUxqazBPQ0JhSWlCcFpEMGlSbWxzYkMweE9DSWdabWxzYkQwaUl6WXdOMFE0UWlJK1BDOXdZWFJvUGdvZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lEeHdZWFJvSUdROUlrMHhNRGt1T0RrMExEa3lMamswTXlCTU1UQTVMamc1TkN3NU1pNDVORE1nUXpFd09DNHhNaXc1TWk0NU5ETWdNVEEyTGpZMU15dzVNaTR5TVRnZ01UQTFMalkxTERrd0xqZ3lNeUJETVRBMUxqVTRNeXc1TUM0M016RWdNVEExTGpVNU15dzVNQzQyTVNBeE1EVXVOamN6TERrd0xqVXlPU0JETVRBMUxqYzFNeXc1TUM0ME5EZ2dNVEExTGpnNExEa3dMalEwSURFd05TNDVOelFzT1RBdU5UQTJJRU14TURZdU56VTBMRGt4TGpBMU15QXhNRGN1TmpjNUxEa3hMak16TXlBeE1EZ3VOekkwTERreExqTXpNeUJETVRFd0xqQTBOeXc1TVM0ek16TWdNVEV4TGpRM09DdzVNQzQ0T1RRZ01URXlMams0TERrd0xqQXlOeUJETVRFNExqSTVNU3c0Tmk0NU5pQXhNakl1TmpFeExEYzVMalV3T1NBeE1qSXVOakV4TERjekxqUXhOaUJETVRJeUxqWXhNU3czTVM0ME9Ea2dNVEl5TGpFMk9TdzJPUzQ0TlRZZ01USXhMak16TXl3Mk9DNDJPVElnUXpFeU1TNHlOallzTmpndU5pQXhNakV1TWpjMkxEWTRMalEzTXlBeE1qRXVNelUyTERZNExqTTVNaUJETVRJeExqUXpOaXcyT0M0ek1URWdNVEl4TGpVMk15dzJPQzR5T1RrZ01USXhMalkxTml3Mk9DNHpOalVnUXpFeU15NHpNamNzTmprdU5UTTNJREV5TkM0eU5EY3NOekV1TnpRMklERXlOQzR5TkRjc056UXVOVGcwSUVNeE1qUXVNalEzTERnd0xqZ3lOaUF4TVRrdU9ESXhMRGc0TGpRME55QXhNVFF1TXpneUxEa3hMalU0TnlCRE1URXlMamd3T0N3NU1pNDBPVFVnTVRFeExqSTVPQ3c1TWk0NU5ETWdNVEE1TGpnNU5DdzVNaTQ1TkRNZ1RERXdPUzQ0T1RRc09USXVPVFF6SUZvZ1RURXdOaTQ1TWpVc09URXVOREF4SUVNeE1EY3VOek00TERreUxqQTFNaUF4TURndU56UTFMRGt5TGpJM09DQXhNRGt1T0RrekxEa3lMakkzT0NCTU1UQTVMamc1TkN3NU1pNHlOemdnUXpFeE1TNHlNVFVzT1RJdU1qYzRJREV4TWk0Mk5EY3NPVEV1T1RVeElERXhOQzR4TkRnc09URXVNRGcwSUVNeE1Ua3VORFU1TERnNExqQXhOeUF4TWpNdU56Z3NPREF1TmpJeElERXlNeTQzT0N3M05DNDFNamdnUXpFeU15NDNPQ3czTWk0MU5Ea2dNVEl6TGpNeE55dzNNQzQ1TWprZ01USXlMalExTkN3Mk9TNDNOamNnUXpFeU1pNDROalVzTnpBdU9EQXlJREV5TXk0d056a3NOekl1TURReUlERXlNeTR3Tnprc056TXVOREF5SUVNeE1qTXVNRGM1TERjNUxqWTBOU0F4TVRndU5qVXpMRGczTGpJNE5TQXhNVE11TWpFMExEa3dMalF5TlNCRE1URXhMalkwTERreExqTXpOQ0F4TVRBdU1UTXNPVEV1TnpReUlERXdPQzQzTWpRc09URXVOelF5SUVNeE1EZ3VNRGd6TERreExqYzBNaUF4TURjdU5EZ3hMRGt4TGpVNU15QXhNRFl1T1RJMUxEa3hMalF3TVNCTU1UQTJMamt5TlN3NU1TNDBNREVnV2lJZ2FXUTlJa1pwYkd3dE1Ua2lJR1pwYkd3OUlpTTJNRGRFT0VJaVBqd3ZjR0YwYUQ0S0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQThjR0YwYUNCa1BTSk5NVEV6TGpBNU55dzVNQzR5TXlCRE1URTRMalE0TVN3NE55NHhNaklnTVRJeUxqZzBOU3czT1M0MU9UUWdNVEl5TGpnME5TdzNNeTQwTVRZZ1F6RXlNaTQ0TkRVc056RXVNelkxSURFeU1pNHpOaklzTmprdU56STBJREV5TVM0MU1qSXNOamd1TlRVMklFTXhNVGt1TnpNNExEWTNMak13TkNBeE1UY3VNVFE0TERZM0xqTTJNaUF4TVRRdU1qWTFMRFk1TGpBeU5pQkRNVEE0TGpnNE1TdzNNaTR4TXpRZ01UQTBMalV4Tnl3M09TNDJOaklnTVRBMExqVXhOeXc0TlM0NE5DQkRNVEEwTGpVeE55dzROeTQ0T1RFZ01UQTFMRGc1TGpVek1pQXhNRFV1T0RRc09UQXVOeUJETVRBM0xqWXlOQ3c1TVM0NU5USWdNVEV3TGpJeE5DdzVNUzQ0T1RRZ01URXpMakE1Tnl3NU1DNHlNeUlnYVdROUlrWnBiR3d0TWpBaUlHWnBiR3c5SWlOR1FVWkJSa0VpUGp3dmNHRjBhRDRLSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBOGNHRjBhQ0JrUFNKTk1UQTRMamN5TkN3NU1TNDJNVFFnVERFd09DNDNNalFzT1RFdU5qRTBJRU14TURjdU5UZ3lMRGt4TGpZeE5DQXhNRFl1TlRZMkxEa3hMalF3TVNBeE1EVXVOekExTERrd0xqYzVOeUJETVRBMUxqWTROQ3c1TUM0M09ETWdNVEExTGpZMk5TdzVNQzQ0TVRFZ01UQTFMalkxTERrd0xqYzVJRU14TURRdU56VTJMRGc1TGpVME5pQXhNRFF1TWpnekxEZzNMamcwTWlBeE1EUXVNamd6TERnMUxqZ3hOeUJETVRBMExqSTRNeXczT1M0MU56VWdNVEE0TGpjd09TdzNNUzQ1TlRNZ01URTBMakUwT0N3Mk9DNDRNVElnUXpFeE5TNDNNaklzTmpjdU9UQTBJREV4Tnk0eU16SXNOamN1TkRRNUlERXhPQzQyTXpnc05qY3VORFE1SUVNeE1Ua3VOemdzTmpjdU5EUTVJREV5TUM0M09UWXNOamN1TnpVNElERXlNUzQyTlRZc05qZ3VNell5SUVNeE1qRXVOamM0TERZNExqTTNOeUF4TWpFdU5qazNMRFk0TGpNNU55QXhNakV1TnpFeUxEWTRMalF4T0NCRE1USXlMall3Tml3Mk9TNDJOaklnTVRJekxqQTNPU3czTVM0ek9TQXhNak11TURjNUxEY3pMalF4TlNCRE1USXpMakEzT1N3M09TNDJOVGdnTVRFNExqWTFNeXc0Tnk0eE9UZ2dNVEV6TGpJeE5DdzVNQzR6TXpnZ1F6RXhNUzQyTkN3NU1TNHlORGNnTVRFd0xqRXpMRGt4TGpZeE5DQXhNRGd1TnpJMExEa3hMall4TkNCTU1UQTRMamN5TkN3NU1TNDJNVFFnV2lCTk1UQTJMakF3Tml3NU1DNDFNRFVnUXpFd05pNDNPQ3c1TVM0d016Y2dNVEEzTGpZNU5DdzVNUzR5T0RFZ01UQTRMamN5TkN3NU1TNHlPREVnUXpFeE1DNHdORGNzT1RFdU1qZ3hJREV4TVM0ME56Z3NPVEF1T0RZNElERXhNaTQ1T0N3NU1DNHdNREVnUXpFeE9DNHlPVEVzT0RZdU9UTTFJREV5TWk0Mk1URXNOemt1TkRrMklERXlNaTQyTVRFc056TXVOREF6SUVNeE1qSXVOakV4TERjeExqUTVOQ0F4TWpJdU1UYzNMRFk1TGpnNElERXlNUzR6TlRZc05qZ3VOekU0SUVNeE1qQXVOVGd5TERZNExqRTROU0F4TVRrdU5qWTRMRFkzTGpreE9TQXhNVGd1TmpNNExEWTNMamt4T1NCRE1URTNMak14TlN3Mk55NDVNVGtnTVRFMUxqZzRNeXcyT0M0ek5pQXhNVFF1TXpneUxEWTVMakl5TnlCRE1UQTVMakEzTVN3M01pNHlPVE1nTVRBMExqYzFNU3czT1M0M016TWdNVEEwTGpjMU1TdzROUzQ0TWpZZ1F6RXdOQzQzTlRFc09EY3VOek0xSURFd05TNHhPRFVzT0RrdU16UXpJREV3Tmk0d01EWXNPVEF1TlRBMUlFd3hNRFl1TURBMkxEa3dMalV3TlNCYUlpQnBaRDBpUm1sc2JDMHlNU0lnWm1sc2JEMGlJell3TjBRNFFpSStQQzl3WVhSb1Bnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJRHh3WVhSb0lHUTlJazB4TkRrdU16RTRMRGN1TWpZeUlFd3hNemt1TXpNMExERTJMakUwSUV3eE5UVXVNakkzTERJM0xqRTNNU0JNTVRZd0xqZ3hOaXd5TVM0d05Ua2dUREUwT1M0ek1UZ3NOeTR5TmpJaUlHbGtQU0pHYVd4c0xUSXlJaUJtYVd4c1BTSWpSa0ZHUVVaQklqNDhMM0JoZEdnK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdQSEJoZEdnZ1pEMGlUVEUyT1M0Mk56WXNNVE11T0RRZ1RERTFPUzQ1TWpnc01Ua3VORFkzSUVNeE5UWXVNamcyTERJeExqVTNJREUxTUM0MExESXhMalU0SURFME5pNDNPREVzTVRrdU5Ea3hJRU14TkRNdU1UWXhMREUzTGpRd01pQXhORE11TVRnc01UUXVNREF6SURFME5pNDRNaklzTVRFdU9TQk1NVFUyTGpNeE55dzJMakk1TWlCTU1UUTVMalU0T0N3eUxqUXdOeUJNTmpjdU56VXlMRFE1TGpRM09DQk1NVEV6TGpZM05TdzNOUzQ1T1RJZ1RERXhOaTQzTlRZc056UXVNakV6SUVNeE1UY3VNemczTERjekxqZzBPQ0F4TVRjdU5qSTFMRGN6TGpNeE5TQXhNVGN1TXpjMExEY3lMamd5TXlCRE1URTFMakF4Tnl3Mk9DNHhPVEVnTVRFMExqYzRNU3cyTXk0eU56Y2dNVEUyTGpZNU1TdzFPQzQxTmpFZ1F6RXlNaTR6TWprc05EUXVOalF4SURFME1TNHlMRE16TGpjME5pQXhOalV1TXpBNUxETXdMalE1TVNCRE1UY3pMalEzT0N3eU9TNHpPRGdnTVRneExqazRPU3d5T1M0MU1qUWdNVGt3TGpBeE15d3pNQzQ0T0RVZ1F6RTVNQzQ0TmpVc016RXVNRE1nTVRreExqYzRPU3d6TUM0NE9UTWdNVGt5TGpReUxETXdMalV5T0NCTU1UazFMalV3TVN3eU9DNDNOU0JNTVRZNUxqWTNOaXd4TXk0NE5DSWdhV1E5SWtacGJHd3RNak1pSUdacGJHdzlJaU5HUVVaQlJrRWlQand2Y0dGMGFENEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0E4Y0dGMGFDQmtQU0pOTVRFekxqWTNOU3czTmk0ME5Ua2dRekV4TXk0MU9UUXNOell1TkRVNUlERXhNeTQxTVRRc056WXVORE00SURFeE15NDBORElzTnpZdU16azNJRXcyTnk0MU1UZ3NORGt1T0RneUlFTTJOeTR6TnpRc05Ea3VOems1SURZM0xqSTROQ3cwT1M0Mk5EVWdOamN1TWpnMUxEUTVMalEzT0NCRE5qY3VNamcxTERRNUxqTXhNU0EyTnk0ek56UXNORGt1TVRVM0lEWTNMalV4T1N3ME9TNHdOek1nVERFME9TNHpOVFVzTWk0d01ESWdRekUwT1M0ME9Ua3NNUzQ1TVRrZ01UUTVMalkzTnl3eExqa3hPU0F4TkRrdU9ESXhMREl1TURBeUlFd3hOVFl1TlRVc05TNDRPRGNnUXpFMU5pNDNOelFzTmk0d01UY2dNVFUyTGpnMUxEWXVNekF5SURFMU5pNDNNaklzTmk0MU1qWWdRekUxTmk0MU9USXNOaTQzTkRrZ01UVTJMak13Tnl3MkxqZ3lOaUF4TlRZdU1EZ3pMRFl1TmprMklFd3hORGt1TlRnM0xESXVPVFEySUV3Mk9DNDJPRGNzTkRrdU5EYzVJRXd4TVRNdU5qYzFMRGMxTGpRMU1pQk1NVEUyTGpVeU15dzNNeTQ0TURnZ1F6RXhOaTQzTVRVc056TXVOamszSURFeE55NHhORE1zTnpNdU16azVJREV4Tmk0NU5UZ3NOek11TURNMUlFTXhNVFF1TlRReUxEWTRMakk0TnlBeE1UUXVNeXcyTXk0eU1qRWdNVEUyTGpJMU9DdzFPQzR6T0RVZ1F6RXhPUzR3TmpRc05URXVORFU0SURFeU5TNHhORE1zTkRVdU1UUXpJREV6TXk0NE5DdzBNQzR4TWpJZ1F6RTBNaTQwT1Rjc016VXVNVEkwSURFMU15NHpOVGdzTXpFdU5qTXpJREUyTlM0eU5EY3NNekF1TURJNElFTXhOek11TkRRMUxESTRMamt5TVNBeE9ESXVNRE0zTERJNUxqQTFPQ0F4T1RBdU1Ea3hMRE13TGpReU5TQkRNVGt3TGpnekxETXdMalUxSURFNU1TNDJOVElzTXpBdU5ETXlJREU1TWk0eE9EWXNNekF1TVRJMElFd3hPVFF1TlRZM0xESTRMamMxSUV3eE5qa3VORFF5TERFMExqSTBOQ0JETVRZNUxqSXhPU3d4TkM0eE1UVWdNVFk1TGpFME1pd3hNeTQ0TWprZ01UWTVMakkzTVN3eE15NDJNRFlnUXpFMk9TNDBMREV6TGpNNE1pQXhOamt1TmpnMUxERXpMak13TmlBeE5qa3VPVEE1TERFekxqUXpOU0JNTVRrMUxqY3pOQ3d5T0M0ek5EVWdRekU1TlM0NE56a3NNamd1TkRJNElERTVOUzQ1Tmpnc01qZ3VOVGd6SURFNU5TNDVOamdzTWpndU56VWdRekU1TlM0NU5qZ3NNamd1T1RFMklERTVOUzQ0Tnprc01qa3VNRGN4SURFNU5TNDNNelFzTWprdU1UVTBJRXd4T1RJdU5qVXpMRE13TGprek15QkRNVGt4TGprek1pd3pNUzR6TlNBeE9UQXVPRGtzTXpFdU5UQTRJREU0T1M0NU16VXNNekV1TXpRMklFTXhPREV1T1RjeUxESTVMams1TlNBeE56TXVORGM0TERJNUxqZzJJREUyTlM0ek56SXNNekF1T1RVMElFTXhOVE11TmpBeUxETXlMalUwTXlBeE5ESXVPRFlzTXpVdU9Ua3pJREV6TkM0ek1EY3NOREF1T1RNeElFTXhNalV1TnprekxEUTFMamcwTnlBeE1Ua3VPRFV4TERVeUxqQXdOQ0F4TVRjdU1USTBMRFU0TGpjek5pQkRNVEUxTGpJM0xEWXpMak14TkNBeE1UVXVOVEF4TERZNExqRXhNaUF4TVRjdU56a3NOekl1TmpFeElFTXhNVGd1TVRZc056TXVNek0ySURFeE55NDRORFVzTnpRdU1USTBJREV4Tmk0NU9TdzNOQzQyTVRjZ1RERXhNeTQ1TURrc056WXVNemszSUVNeE1UTXVPRE0yTERjMkxqUXpPQ0F4TVRNdU56VTJMRGMyTGpRMU9TQXhNVE11TmpjMUxEYzJMalExT1NJZ2FXUTlJa1pwYkd3dE1qUWlJR1pwYkd3OUlpTTBOVFZCTmpRaVBqd3ZjR0YwYUQ0S0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQThjR0YwYUNCa1BTSk5NVFV6TGpNeE5pd3lNUzR5TnprZ1F6RTFNQzQ1TURNc01qRXVNamM1SURFME9DNDBPVFVzTWpBdU56VXhJREUwTmk0Mk5qUXNNVGt1TmpreklFTXhORFF1T0RRMkxERTRMalkwTkNBeE5ETXVPRFEwTERFM0xqSXpNaUF4TkRNdU9EUTBMREUxTGpjeE9DQkRNVFF6TGpnME5Dd3hOQzR4T1RFZ01UUTBMamcyTERFeUxqYzJNeUF4TkRZdU56QTFMREV4TGpZNU9DQk1NVFUyTGpFNU9DdzJMakE1TVNCRE1UVTJMak13T1N3MkxqQXlOU0F4TlRZdU5EVXlMRFl1TURZeUlERTFOaTQxTVRnc05pNHhOek1nUXpFMU5pNDFPRE1zTmk0eU9EUWdNVFUyTGpVME55dzJMalF5TnlBeE5UWXVORE0yTERZdU5Ea3pJRXd4TkRZdU9UUXNNVEl1TVRBeUlFTXhORFV1TWpRMExERXpMakE0TVNBeE5EUXVNekV5TERFMExqTTJOU0F4TkRRdU16RXlMREUxTGpjeE9DQkRNVFEwTGpNeE1pd3hOeTR3TlRnZ01UUTFMakl6TERFNExqTXlOaUF4TkRZdU9EazNMREU1TGpJNE9TQkRNVFV3TGpRME5pd3lNUzR6TXpnZ01UVTJMakkwTERJeExqTXlOeUF4TlRrdU9ERXhMREU1TGpJMk5TQk1NVFk1TGpVMU9Td3hNeTQyTXpjZ1F6RTJPUzQyTnl3eE15NDFOek1nTVRZNUxqZ3hNeXd4TXk0Mk1URWdNVFk1TGpnM09Dd3hNeTQzTWpNZ1F6RTJPUzQ1TkRNc01UTXVPRE0wSURFMk9TNDVNRFFzTVRNdU9UYzNJREUyT1M0M09UTXNNVFF1TURReUlFd3hOakF1TURRMUxERTVMalkzSUVNeE5UZ3VNVGczTERJd0xqYzBNaUF4TlRVdU56UTVMREl4TGpJM09TQXhOVE11TXpFMkxESXhMakkzT1NJZ2FXUTlJa1pwYkd3dE1qVWlJR1pwYkd3OUlpTTJNRGRFT0VJaVBqd3ZjR0YwYUQ0S0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQThjR0YwYUNCa1BTSk5NVEV6TGpZM05TdzNOUzQ1T1RJZ1REWTNMamMyTWl3ME9TNDBPRFFpSUdsa1BTSkdhV3hzTFRJMklpQm1hV3hzUFNJak5EVTFRVFkwSWo0OEwzQmhkR2crQ2lBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ1BIQmhkR2dnWkQwaVRURXhNeTQyTnpVc056WXVNelF5SUVNeE1UTXVOakUxTERjMkxqTTBNaUF4TVRNdU5UVTFMRGMyTGpNeU55QXhNVE11TlN3M05pNHlPVFVnVERZM0xqVTROeXcwT1M0M09EY2dRelkzTGpReE9TdzBPUzQyT1NBMk55NHpOaklzTkRrdU5EYzJJRFkzTGpRMU9TdzBPUzR6TURrZ1F6WTNMalUxTml3ME9TNHhOREVnTmpjdU56Y3NORGt1TURneklEWTNMamt6Tnl3ME9TNHhPQ0JNTVRFekxqZzFMRGMxTGpZNE9DQkRNVEUwTGpBeE9DdzNOUzQzT0RVZ01URTBMakEzTlN3M05pQXhNVE11T1RjNExEYzJMakUyTnlCRE1URXpMamt4TkN3M05pNHlOemtnTVRFekxqYzVOaXczTmk0ek5ESWdNVEV6TGpZM05TdzNOaTR6TkRJaUlHbGtQU0pHYVd4c0xUSTNJaUJtYVd4c1BTSWpORFUxUVRZMElqNDhMM0JoZEdnK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdQSEJoZEdnZ1pEMGlUVFkzTGpjMk1pdzBPUzQwT0RRZ1REWTNMamMyTWl3eE1ETXVORGcxSUVNMk55NDNOaklzTVRBMExqVTNOU0EyT0M0MU16SXNNVEExTGprd015QTJPUzQwT0RJc01UQTJMalExTWlCTU1URXhMamsxTlN3eE16QXVPVGN6SUVNeE1USXVPVEExTERFek1TNDFNaklnTVRFekxqWTNOU3d4TXpFdU1EZ3pJREV4TXk0Mk56VXNNVEk1TGprNU15Qk1NVEV6TGpZM05TdzNOUzQ1T1RJaUlHbGtQU0pHYVd4c0xUSTRJaUJtYVd4c1BTSWpSa0ZHUVVaQklqNDhMM0JoZEdnK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdQSEJoZEdnZ1pEMGlUVEV4TWk0M01qY3NNVE14TGpVMk1TQkRNVEV5TGpRekxERXpNUzQxTmpFZ01URXlMakV3Tnl3eE16RXVORFkySURFeE1TNDNPQ3d4TXpFdU1qYzJJRXcyT1M0ek1EY3NNVEEyTGpjMU5TQkROamd1TWpRMExERXdOaTR4TkRJZ05qY3VOREV5TERFd05DNDNNRFVnTmpjdU5ERXlMREV3TXk0ME9EVWdURFkzTGpReE1pdzBPUzQwT0RRZ1F6WTNMalF4TWl3ME9TNHlPU0EyTnk0MU5qa3NORGt1TVRNMElEWTNMamMyTWl3ME9TNHhNelFnUXpZM0xqazFOaXcwT1M0eE16UWdOamd1TVRFekxEUTVMakk1SURZNExqRXhNeXcwT1M0ME9EUWdURFk0TGpFeE15d3hNRE11TkRnMUlFTTJPQzR4TVRNc01UQTBMalEwTlNBMk9DNDRNaXd4TURVdU5qWTFJRFk1TGpZMU55d3hNRFl1TVRRNElFd3hNVEl1TVRNc01UTXdMalkzSUVNeE1USXVORGMwTERFek1DNDROamdnTVRFeUxqYzVNU3d4TXpBdU9URXpJREV4TXl3eE16QXVOemt5SUVNeE1UTXVNakEyTERFek1DNDJOek1nTVRFekxqTXlOU3d4TXpBdU16Z3hJREV4TXk0ek1qVXNNVEk1TGprNU15Qk1NVEV6TGpNeU5TdzNOUzQ1T1RJZ1F6RXhNeTR6TWpVc056VXVOems0SURFeE15NDBPRElzTnpVdU5qUXhJREV4TXk0Mk56VXNOelV1TmpReElFTXhNVE11T0RZNUxEYzFMalkwTVNBeE1UUXVNREkxTERjMUxqYzVPQ0F4TVRRdU1ESTFMRGMxTGprNU1pQk1NVEUwTGpBeU5Td3hNamt1T1RreklFTXhNVFF1TURJMUxERXpNQzQyTkRnZ01URXpMamM0Tml3eE16RXVNVFEzSURFeE15NHpOU3d4TXpFdU16azVJRU14TVRNdU1UWXlMREV6TVM0MU1EY2dNVEV5TGprMU1pd3hNekV1TlRZeElERXhNaTQzTWpjc01UTXhMalUyTVNJZ2FXUTlJa1pwYkd3dE1qa2lJR1pwYkd3OUlpTTBOVFZCTmpRaVBqd3ZjR0YwYUQ0S0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQThjR0YwYUNCa1BTSk5NVEV5TGpnMkxEUXdMalV4TWlCRE1URXlMamcyTERRd0xqVXhNaUF4TVRJdU9EWXNOREF1TlRFeUlERXhNaTQ0TlRrc05EQXVOVEV5SUVNeE1UQXVOVFF4TERRd0xqVXhNaUF4TURndU16WXNNemt1T1RrZ01UQTJMamN4Tnl3ek9TNHdOREVnUXpFd05TNHdNVElzTXpndU1EVTNJREV3TkM0d056UXNNell1TnpJMklERXdOQzR3TnpRc016VXVNamt5SUVNeE1EUXVNRGMwTERNekxqZzBOeUF4TURVdU1ESTJMRE15TGpVd01TQXhNRFl1TnpVMExETXhMalV3TkNCTU1URTRMamM1TlN3eU5DNDFOVEVnUXpFeU1DNDBOak1zTWpNdU5UZzVJREV5TWk0Mk5qa3NNak11TURVNElERXlOUzR3TURjc01qTXVNRFU0SUVNeE1qY3VNekkxTERJekxqQTFPQ0F4TWprdU5UQTJMREl6TGpVNE1TQXhNekV1TVRVc01qUXVOVE1nUXpFek1pNDROVFFzTWpVdU5URTBJREV6TXk0M09UTXNNall1T0RRMUlERXpNeTQzT1RNc01qZ3VNamM0SUVNeE16TXVOemt6TERJNUxqY3lOQ0F4TXpJdU9EUXhMRE14TGpBMk9TQXhNekV1TVRFekxETXlMakEyTnlCTU1URTVMakEzTVN3ek9TNHdNVGtnUXpFeE55NDBNRE1zTXprdU9UZ3lJREV4TlM0eE9UY3NOREF1TlRFeUlERXhNaTQ0Tml3ME1DNDFNVElnVERFeE1pNDROaXcwTUM0MU1USWdXaUJOTVRJMUxqQXdOeXd5TXk0M05Ua2dRekV5TWk0M09Td3lNeTQzTlRrZ01USXdMamN3T1N3eU5DNHlOVFlnTVRFNUxqRTBOaXd5TlM0eE5UZ2dUREV3Tnk0eE1EUXNNekl1TVRFZ1F6RXdOUzQyTURJc016SXVPVGM0SURFd05DNDNOelFzTXpRdU1UQTRJREV3TkM0M056UXNNelV1TWpreUlFTXhNRFF1TnpjMExETTJMalEyTlNBeE1EVXVOVGc1TERNM0xqVTRNU0F4TURjdU1EWTNMRE00TGpRek5DQkRNVEE0TGpZd05Td3pPUzR6TWpNZ01URXdMalkyTXl3ek9TNDRNVElnTVRFeUxqZzFPU3d6T1M0NE1USWdUREV4TWk0NE5pd3pPUzQ0TVRJZ1F6RXhOUzR3TnpZc016a3VPREV5SURFeE55NHhOVGdzTXprdU16RTFJREV4T0M0M01qRXNNemd1TkRFeklFd3hNekF1TnpZeUxETXhMalEySUVNeE16SXVNalkwTERNd0xqVTVNeUF4TXpNdU1Ea3lMREk1TGpRMk15QXhNek11TURreUxESTRMakkzT0NCRE1UTXpMakE1TWl3eU55NHhNRFlnTVRNeUxqSTNPQ3d5TlM0NU9TQXhNekF1T0N3eU5TNHhNellnUXpFeU9TNHlOakVzTWpRdU1qUTRJREV5Tnk0eU1EUXNNak11TnpVNUlERXlOUzR3TURjc01qTXVOelU1SUV3eE1qVXVNREEzTERJekxqYzFPU0JhSWlCcFpEMGlSbWxzYkMwek1DSWdabWxzYkQwaUl6WXdOMFE0UWlJK1BDOXdZWFJvUGdvZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lEeHdZWFJvSUdROUlrMHhOalV1TmpNc01UWXVNakU1SUV3eE5Ua3VPRGsyTERFNUxqVXpJRU14TlRZdU56STVMREl4TGpNMU9DQXhOVEV1TmpFc01qRXVNelkzSURFME9DNDBOak1zTVRrdU5UVWdRekUwTlM0ek1UWXNNVGN1TnpNeklERTBOUzR6TXpJc01UUXVOemM0SURFME9DNDBPVGtzTVRJdU9UUTVJRXd4TlRRdU1qTXpMRGt1TmpNNUlFd3hOalV1TmpNc01UWXVNakU1SWlCcFpEMGlSbWxzYkMwek1TSWdabWxzYkQwaUkwWkJSa0ZHUVNJK1BDOXdZWFJvUGdvZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lEeHdZWFJvSUdROUlrMHhOVFF1TWpNekxERXdMalEwT0NCTU1UWTBMakl5T0N3eE5pNHlNVGtnVERFMU9TNDFORFlzTVRndU9USXpJRU14TlRndU1URXlMREU1TGpjMUlERTFOaTR4T1RRc01qQXVNakEySURFMU5DNHhORGNzTWpBdU1qQTJJRU14TlRJdU1URTRMREl3TGpJd05pQXhOVEF1TWpJMExERTVMamMxTnlBeE5EZ3VPREUwTERFNExqazBNeUJETVRRM0xqVXlOQ3d4T0M0eE9Ua2dNVFEyTGpneE5Dd3hOeTR5TkRrZ01UUTJMamd4TkN3eE5pNHlOamtnUXpFME5pNDRNVFFzTVRVdU1qYzRJREUwTnk0MU16Y3NNVFF1TXpFMElERTBPQzQ0TlN3eE15NDFOVFlnVERFMU5DNHlNek1zTVRBdU5EUTRJRTB4TlRRdU1qTXpMRGt1TmpNNUlFd3hORGd1TkRrNUxERXlMamswT1NCRE1UUTFMak16TWl3eE5DNDNOemdnTVRRMUxqTXhOaXd4Tnk0M016TWdNVFE0TGpRMk15d3hPUzQxTlNCRE1UVXdMakF6TVN3eU1DNDBOVFVnTVRVeUxqQTROaXd5TUM0NU1EY2dNVFUwTGpFME55d3lNQzQ1TURjZ1F6RTFOaTR5TWpRc01qQXVPVEEzSURFMU9DNHpNRFlzTWpBdU5EUTNJREUxT1M0NE9UWXNNVGt1TlRNZ1RERTJOUzQyTXl3eE5pNHlNVGtnVERFMU5DNHlNek1zT1M0Mk16a2lJR2xrUFNKR2FXeHNMVE15SWlCbWFXeHNQU0lqTmpBM1JEaENJajQ4TDNCaGRHZytDaUFnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnUEhCaGRHZ2daRDBpVFRFME5TNDBORFVzTnpJdU5qWTNJRXd4TkRVdU5EUTFMRGN5TGpZMk55QkRNVFF6TGpZM01pdzNNaTQyTmpjZ01UUXlMakl3TkN3M01TNDRNVGNnTVRReExqSXdNaXczTUM0ME1qSWdRekUwTVM0eE16VXNOekF1TXpNZ01UUXhMakUwTlN3M01DNHhORGNnTVRReExqSXlOU3czTUM0d05qWWdRekUwTVM0ek1EVXNOamt1T1RnMUlERTBNUzQwTXpJc05qa3VPVFEySURFME1TNDFNalVzTnpBdU1ERXhJRU14TkRJdU16QTJMRGN3TGpVMU9TQXhORE11TWpNeExEY3dMamd5TXlBeE5EUXVNamMyTERjd0xqZ3lNaUJETVRRMUxqVTVPQ3czTUM0NE1qSWdNVFEzTGpBekxEY3dMak0zTmlBeE5EZ3VOVE15TERZNUxqVXdPU0JETVRVekxqZzBNaXcyTmk0ME5ETWdNVFU0TGpFMk15dzFPQzQ1T0RjZ01UVTRMakUyTXl3MU1pNDRPVFFnUXpFMU9DNHhOak1zTlRBdU9UWTNJREUxTnk0M01qRXNORGt1TXpNeUlERTFOaTQ0T0RRc05EZ3VNVFk0SUVNeE5UWXVPREU0TERRNExqQTNOaUF4TlRZdU9ESTRMRFEzTGprME9DQXhOVFl1T1RBNExEUTNMamcyTnlCRE1UVTJMams0T0N3ME55NDNPRFlnTVRVM0xqRXhOQ3cwTnk0M056UWdNVFUzTGpJd09DdzBOeTQ0TkNCRE1UVTRMamczT0N3ME9TNHdNVElnTVRVNUxqYzVPQ3cxTVM0eU1pQXhOVGt1TnprNExEVTBMakExT1NCRE1UVTVMamM1T0N3Mk1DNHpNREVnTVRVMUxqTTNNeXcyT0M0d05EWWdNVFE1TGprek15dzNNUzR4T0RZZ1F6RTBPQzR6Tml3M01pNHdPVFFnTVRRMkxqZzFMRGN5TGpZMk55QXhORFV1TkRRMUxEY3lMalkyTnlCTU1UUTFMalEwTlN3M01pNDJOamNnV2lCTk1UUXlMalEzTml3M01TQkRNVFF6TGpJNUxEY3hMalkxTVNBeE5EUXVNamsyTERjeUxqQXdNaUF4TkRVdU5EUTFMRGN5TGpBd01pQkRNVFEyTGpjMk55dzNNaTR3TURJZ01UUTRMakU1T0N3M01TNDFOU0F4TkRrdU55dzNNQzQyT0RJZ1F6RTFOUzR3TVN3Mk55NDJNVGNnTVRVNUxqTXpNU3cyTUM0eE5Ua2dNVFU1TGpNek1TdzFOQzR3TmpVZ1F6RTFPUzR6TXpFc05USXVNRGcxSURFMU9DNDROamdzTlRBdU5ETTFJREUxT0M0d01EWXNORGt1TWpjeUlFTXhOVGd1TkRFM0xEVXdMak13TnlBeE5UZ3VOak1zTlRFdU5UTXlJREUxT0M0Mk15dzFNaTQ0T1RJZ1F6RTFPQzQyTXl3MU9TNHhNelFnTVRVMExqSXdOU3cyTmk0M05qY2dNVFE0TGpjMk5TdzJPUzQ1TURjZ1F6RTBOeTR4T1RJc056QXVPREUySURFME5TNDJPREVzTnpFdU1qZ3pJREUwTkM0eU56WXNOekV1TWpneklFTXhORE11TmpNMExEY3hMakk0TXlBeE5ETXVNRE16TERjeExqRTVNaUF4TkRJdU5EYzJMRGN4SUV3eE5ESXVORGMyTERjeElGb2lJR2xrUFNKR2FXeHNMVE16SWlCbWFXeHNQU0lqTmpBM1JEaENJajQ4TDNCaGRHZytDaUFnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnUEhCaGRHZ2daRDBpVFRFME9DNDJORGdzTmprdU56QTBJRU14TlRRdU1ETXlMRFkyTGpVNU5pQXhOVGd1TXprMkxEVTVMakEyT0NBeE5UZ3VNemsyTERVeUxqZzVNU0JETVRVNExqTTVOaXcxTUM0NE16a2dNVFUzTGpreE15dzBPUzR4T1RnZ01UVTNMakEzTkN3ME9DNHdNeUJETVRVMUxqSTRPU3cwTmk0M056Z2dNVFV5TGpZNU9TdzBOaTQ0TXpZZ01UUTVMamd4Tml3ME9DNDFNREVnUXpFME5DNDBNek1zTlRFdU5qQTVJREUwTUM0d05qZ3NOVGt1TVRNM0lERTBNQzR3Tmpnc05qVXVNekUwSUVNeE5EQXVNRFk0TERZM0xqTTJOU0F4TkRBdU5UVXlMRFk1TGpBd05pQXhOREV1TXpreExEY3dMakUzTkNCRE1UUXpMakUzTml3M01TNDBNamNnTVRRMUxqYzJOU3czTVM0ek5qa2dNVFE0TGpZME9DdzJPUzQzTURRaUlHbGtQU0pHYVd4c0xUTTBJaUJtYVd4c1BTSWpSa0ZHUVVaQklqNDhMM0JoZEdnK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdQSEJoZEdnZ1pEMGlUVEUwTkM0eU56WXNOekV1TWpjMklFd3hORFF1TWpjMkxEY3hMakkzTmlCRE1UUXpMakV6TXl3M01TNHlOellnTVRReUxqRXhPQ3czTUM0NU5qa2dNVFF4TGpJMU55dzNNQzR6TmpVZ1F6RTBNUzR5TXpZc056QXVNelV4SURFME1TNHlNVGNzTnpBdU16TXlJREUwTVM0eU1ESXNOekF1TXpFeElFTXhOREF1TXpBM0xEWTVMakEyTnlBeE16a3VPRE0xTERZM0xqTXpPU0F4TXprdU9ETTFMRFkxTGpNeE5DQkRNVE01TGpnek5TdzFPUzR3TnpNZ01UUTBMakkyTERVeExqUXpPU0F4TkRrdU55dzBPQzR5T1RnZ1F6RTFNUzR5TnpNc05EY3VNemtnTVRVeUxqYzROQ3cwTmk0NU1qa2dNVFUwTGpFNE9TdzBOaTQ1TWprZ1F6RTFOUzR6TXpJc05EWXVPVEk1SURFMU5pNHpORGNzTkRjdU1qTTJJREUxTnk0eU1EZ3NORGN1T0RNNUlFTXhOVGN1TWpJNUxEUTNMamcxTkNBeE5UY3VNalE0TERRM0xqZzNNeUF4TlRjdU1qWXpMRFEzTGpnNU5DQkRNVFU0TGpFMU55dzBPUzR4TXpnZ01UVTRMall6TERVd0xqZzJOU0F4TlRndU5qTXNOVEl1T0RreElFTXhOVGd1TmpNc05Ua3VNVE15SURFMU5DNHlNRFVzTmpZdU56WTJJREUwT0M0M05qVXNOamt1T1RBM0lFTXhORGN1TVRreUxEY3dMamd4TlNBeE5EVXVOamd4TERjeExqSTNOaUF4TkRRdU1qYzJMRGN4TGpJM05pQk1NVFEwTGpJM05pdzNNUzR5TnpZZ1dpQk5NVFF4TGpVMU9DdzNNQzR4TURRZ1F6RTBNaTR6TXpFc056QXVOak0zSURFME15NHlORFVzTnpFdU1EQTFJREUwTkM0eU56WXNOekV1TURBMUlFTXhORFV1TlRrNExEY3hMakF3TlNBeE5EY3VNRE1zTnpBdU5EWTNJREUwT0M0MU16SXNOamt1TmlCRE1UVXpMamcwTWl3Mk5pNDFNelFnTVRVNExqRTJNeXcxT1M0d016TWdNVFU0TGpFMk15dzFNaTQ1TXprZ1F6RTFPQzR4TmpNc05URXVNRE14SURFMU55NDNNamtzTkRrdU16ZzFJREUxTmk0NU1EY3NORGd1TWpJeklFTXhOVFl1TVRNekxEUTNMalk1TVNBeE5UVXVNakU1TERRM0xqUXdPU0F4TlRRdU1UZzVMRFEzTGpRd09TQkRNVFV5TGpnMk55dzBOeTQwTURrZ01UVXhMalF6TlN3ME55NDRORElnTVRRNUxqa3pNeXcwT0M0M01Ea2dRekUwTkM0Mk1qTXNOVEV1TnpjMUlERTBNQzR6TURJc05Ua3VNamN6SURFME1DNHpNRElzTmpVdU16WTJJRU14TkRBdU16QXlMRFkzTGpJM05pQXhOREF1TnpNMkxEWTRMamswTWlBeE5ERXVOVFU0TERjd0xqRXdOQ0JNTVRReExqVTFPQ3czTUM0eE1EUWdXaUlnYVdROUlrWnBiR3d0TXpVaUlHWnBiR3c5SWlNMk1EZEVPRUlpUGp3dmNHRjBhRDRLSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBOGNHRjBhQ0JrUFNKTk1UVXdMamN5TERZMUxqTTJNU0JNTVRVd0xqTTFOeXcyTlM0d05qWWdRekUxTVM0eE5EY3NOalF1TURreUlERTFNUzQ0Tmprc05qTXVNRFFnTVRVeUxqVXdOU3cyTVM0NU16Z2dRekUxTXk0ek1UTXNOakF1TlRNNUlERTFNeTQ1Tnpnc05Ua3VNRFkzSURFMU5DNDBPRElzTlRjdU5UWXpJRXd4TlRRdU9USTFMRFUzTGpjeE1pQkRNVFUwTGpReE1pdzFPUzR5TkRVZ01UVXpMamN6TXl3Mk1DNDNORFVnTVRVeUxqa3hMRFl5TGpFM01pQkRNVFV5TGpJMk1pdzJNeTR5T1RVZ01UVXhMalV5TlN3Mk5DNHpOamdnTVRVd0xqY3lMRFkxTGpNMk1TSWdhV1E5SWtacGJHd3RNellpSUdacGJHdzlJaU0yTURkRU9FSWlQand2Y0dGMGFENEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0E4Y0dGMGFDQmtQU0pOTVRFMUxqa3hOeXc0TkM0MU1UUWdUREV4TlM0MU5UUXNPRFF1TWpJZ1F6RXhOaTR6TkRRc09ETXVNalExSURFeE55NHdOallzT0RJdU1UazBJREV4Tnk0M01ESXNPREV1TURreUlFTXhNVGd1TlRFc056a3VOamt5SURFeE9TNHhOelVzTnpndU1qSWdNVEU1TGpZM09DdzNOaTQzTVRjZ1RERXlNQzR4TWpFc056WXVPRFkxSUVNeE1Ua3VOakE0TERjNExqTTVPQ0F4TVRndU9UTXNOemt1T0RrNUlERXhPQzR4TURZc09ERXVNekkySUVNeE1UY3VORFU0TERneUxqUTBPQ0F4TVRZdU56SXlMRGd6TGpVeU1TQXhNVFV1T1RFM0xEZzBMalV4TkNJZ2FXUTlJa1pwYkd3dE16Y2lJR1pwYkd3OUlpTTJNRGRFT0VJaVBqd3ZjR0YwYUQ0S0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQThjR0YwYUNCa1BTSk5NVEUwTERFek1DNDBOellnVERFeE5Dd3hNekF1TURBNElFd3hNVFFzTnpZdU1EVXlJRXd4TVRRc056VXVOVGcwSUV3eE1UUXNOell1TURVeUlFd3hNVFFzTVRNd0xqQXdPQ0JNTVRFMExERXpNQzQwTnpZaUlHbGtQU0pHYVd4c0xUTTRJaUJtYVd4c1BTSWpOakEzUkRoQ0lqNDhMM0JoZEdnK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBOEwyYytDaUFnSUNBZ0lDQWdJQ0FnSUNBZ0lDQThaeUJwWkQwaVNXMXdiM0owWldRdFRHRjVaWEp6TFVOdmNIa2lJSFJ5WVc1elptOXliVDBpZEhKaGJuTnNZWFJsS0RZeUxqQXdNREF3TUN3Z01DNHdNREF3TURBcElpQnphMlYwWTJnNmRIbHdaVDBpVFZOVGFHRndaVWR5YjNWd0lqNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0E4Y0dGMGFDQmtQU0pOTVRrdU9ESXlMRE0zTGpRM05DQkRNVGt1T0RNNUxETTNMak16T1NBeE9TNDNORGNzTXpjdU1UazBJREU1TGpVMU5Td3pOeTR3T0RJZ1F6RTVMakl5T0N3ek5pNDRPVFFnTVRndU56STVMRE0yTGpnM01pQXhPQzQwTkRZc016Y3VNRE0zSUV3eE1pNDBNelFzTkRBdU5UQTRJRU14TWk0ek1ETXNOREF1TlRnMElERXlMakkwTERRd0xqWTROaUF4TWk0eU5ETXNOREF1TnpreklFTXhNaTR5TkRVc05EQXVPVEkxSURFeUxqSTBOU3cwTVM0eU5UUWdNVEl1TWpRMUxEUXhMak0zTVNCTU1USXVNalExTERReExqUXhOQ0JNTVRJdU1qTTRMRFF4TGpVME1pQkRPQzR4TkRnc05ETXVPRGczSURVdU5qUTNMRFExTGpNeU1TQTFMalkwTnl3ME5TNHpNakVnUXpVdU5qUTJMRFExTGpNeU1TQXpMalUzTERRMkxqTTJOeUF5TGpnMkxEVXdMalV4TXlCRE1pNDROaXcxTUM0MU1UTWdNUzQ1TkRnc05UY3VORGMwSURFdU9UWXlMRGN3TGpJMU9DQkRNUzQ1Tnpjc09ESXVPREk0SURJdU5UWTRMRGczTGpNeU9DQXpMakV5T1N3NU1TNDJNRGtnUXpNdU16UTVMRGt6TGpJNU15QTJMakV6TERrekxqY3pOQ0EyTGpFekxEa3pMamN6TkNCRE5pNDBOakVzT1RNdU56YzBJRFl1T0RJNExEa3pMamN3TnlBM0xqSXhMRGt6TGpRNE5pQk1PREl1TkRnekxEUTVMamt6TlNCRE9EUXVNamt4TERRNExqZzJOaUE0TlM0eE5TdzBOaTR5TVRZZ09EVXVOVE01TERRekxqWTFNU0JET0RZdU56VXlMRE0xTGpZMk1TQTROeTR5TVRRc01UQXVOamN6SURnMUxqSTJOQ3d6TGpjM015QkRPRFV1TURZNExETXVNRGdnT0RRdU56VTBMREl1TmprZ09EUXVNemsyTERJdU5Ea3hJRXc0TWk0ek1Td3hMamN3TVNCRE9ERXVOVGd6TERFdU56STVJRGd3TGpnNU5Dd3lMakUyT0NBNE1DNDNOellzTWk0eU16WWdRemd3TGpZek5pd3lMak14TnlBME1TNDRNRGNzTWpRdU5UZzFJREl3TGpBek1pd3pOeTR3TnpJZ1RERTVMamd5TWl3ek55NDBOelFpSUdsa1BTSkdhV3hzTFRFaUlHWnBiR3c5SWlOR1JrWkdSa1lpUGp3dmNHRjBhRDRLSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBOGNHRjBhQ0JrUFNKTk9ESXVNekV4TERFdU56QXhJRXc0TkM0ek9UWXNNaTQwT1RFZ1F6ZzBMamMxTkN3eUxqWTVJRGcxTGpBMk9Dd3pMakE0SURnMUxqSTJOQ3d6TGpjM015QkRPRGN1TWpFekxERXdMalkzTXlBNE5pNDNOVEVzTXpVdU5qWWdPRFV1TlRNNUxEUXpMalkxTVNCRE9EVXVNVFE1TERRMkxqSXhOaUE0TkM0eU9TdzBPQzQ0TmpZZ09ESXVORGd6TERRNUxqa3pOU0JNTnk0eU1TdzVNeTQwT0RZZ1F6WXVPRGszTERrekxqWTJOeUEyTGpVNU5TdzVNeTQzTkRRZ05pNHpNVFFzT1RNdU56UTBJRXcyTGpFek1TdzVNeTQzTXpNZ1F6WXVNVE14TERrekxqY3pOQ0F6TGpNME9TdzVNeTR5T1RNZ015NHhNamdzT1RFdU5qQTVJRU15TGpVMk9DdzROeTR6TWpjZ01TNDVOemNzT0RJdU9ESTRJREV1T1RZekxEY3dMakkxT0NCRE1TNDVORGdzTlRjdU5EYzBJREl1T0RZc05UQXVOVEV6SURJdU9EWXNOVEF1TlRFeklFTXpMalUzTERRMkxqTTJOeUExTGpZME55dzBOUzR6TWpFZ05TNDJORGNzTkRVdU16SXhJRU0xTGpZME55dzBOUzR6TWpFZ09DNHhORGdzTkRNdU9EZzNJREV5TGpJek9DdzBNUzQxTkRJZ1RERXlMakkwTlN3ME1TNDBNVFFnVERFeUxqSTBOU3cwTVM0ek56RWdRekV5TGpJME5TdzBNUzR5TlRRZ01USXVNalExTERRd0xqa3lOU0F4TWk0eU5ETXNOREF1TnpreklFTXhNaTR5TkN3ME1DNDJPRFlnTVRJdU16QXlMRFF3TGpVNE15QXhNaTQwTXpRc05EQXVOVEE0SUV3eE9DNDBORFlzTXpjdU1ETTJJRU14T0M0MU56UXNNell1T1RZeUlERTRMamMwTml3ek5pNDVNallnTVRndU9USTNMRE0yTGpreU5pQkRNVGt1TVRRMUxETTJMamt5TmlBeE9TNHpOellzTXpZdU9UYzVJREU1TGpVMU5Dd3pOeTR3T0RJZ1F6RTVMamMwTnl3ek55NHhPVFFnTVRrdU9ETTVMRE0zTGpNMElERTVMamd5TWl3ek55NDBOelFnVERJd0xqQXpNeXd6Tnk0d056SWdRelF4TGpnd05pd3lOQzQxT0RVZ09EQXVOak0yTERJdU16RTRJRGd3TGpjM055d3lMakl6TmlCRE9EQXVPRGswTERJdU1UWTRJRGd4TGpVNE15d3hMamN5T1NBNE1pNHpNVEVzTVM0M01ERWdUVGd5TGpNeE1Td3dMamN3TkNCTU9ESXVNamN5TERBdU56QTFJRU00TVM0Mk5UUXNNQzQzTWpnZ09EQXVPVGc1TERBdU9UUTVJRGd3TGpJNU9Dd3hMak0yTVNCTU9EQXVNamMzTERFdU16Y3pJRU00TUM0eE1qa3NNUzQwTlRnZ05Ua3VOelk0TERFekxqRXpOU0F4T1M0M05UZ3NNell1TURjNUlFTXhPUzQxTERNMUxqazRNU0F4T1M0eU1UUXNNelV1T1RJNUlERTRMamt5Tnl3ek5TNDVNamtnUXpFNExqVTJNaXd6TlM0NU1qa2dNVGd1TWpJekxETTJMakF4TXlBeE55NDVORGNzTXpZdU1UY3pJRXd4TVM0NU16VXNNemt1TmpRMElFTXhNUzQwT1RNc016a3VPRGs1SURFeExqSXpOaXcwTUM0ek16UWdNVEV1TWpRMkxEUXdMamd4SUV3eE1TNHlORGNzTkRBdU9UWWdURFV1TVRZM0xEUTBMalEwTnlCRE5DNDNPVFFzTkRRdU5qUTJJREl1TmpJMUxEUTFMamszT0NBeExqZzNOeXcxTUM0ek5EVWdUREV1T0RjeExEVXdMak00TkNCRE1TNDROaklzTlRBdU5EVTBJREF1T1RVeExEVTNMalUxTnlBd0xqazJOU3czTUM0eU5Ua2dRekF1T1RjNUxEZ3lMamczT1NBeExqVTJPQ3c0Tnk0ek56VWdNaTR4TXpjc09URXVOekkwSUV3eUxqRXpPU3c1TVM0M016a2dRekl1TkRRM0xEazBMakE1TkNBMUxqWXhOQ3c1TkM0Mk5qSWdOUzQ1TnpVc09UUXVOekU1SUV3MkxqQXdPU3c1TkM0M01qTWdRell1TVRFc09UUXVOek0ySURZdU1qRXpMRGswTGpjME1pQTJMak14TkN3NU5DNDNORElnUXpZdU56a3NPVFF1TnpReUlEY3VNallzT1RRdU5qRWdOeTQzTVN3NU5DNHpOU0JNT0RJdU9UZ3pMRFV3TGpjNU9DQkRPRFF1TnprMExEUTVMamN5TnlBNE5TNDVPRElzTkRjdU16YzFJRGcyTGpVeU5TdzBNeTQ0TURFZ1F6ZzNMamN4TVN3ek5TNDVPRGNnT0RndU1qVTVMREV3TGpjd05TQTROaTR5TWpRc015NDFNRElnUXpnMUxqazNNU3d5TGpZd09TQTROUzQxTWl3eExqazNOU0E0TkM0NE9ERXNNUzQyTWlCTU9EUXVOelE1TERFdU5UVTRJRXc0TWk0Mk5qUXNNQzQzTmprZ1F6Z3lMalUxTVN3d0xqY3lOU0E0TWk0ME16RXNNQzQzTURRZ09ESXVNekV4TERBdU56QTBJaUJwWkQwaVJtbHNiQzB5SWlCbWFXeHNQU0lqTkRVMVFUWTBJajQ4TDNCaGRHZytDaUFnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnUEhCaGRHZ2daRDBpVFRZMkxqSTJOeXd4TVM0MU5qVWdURFkzTGpjMk1pd3hNUzQ1T1RrZ1RERXhMalF5TXl3ME5DNHpNalVpSUdsa1BTSkdhV3hzTFRNaUlHWnBiR3c5SWlOR1JrWkdSa1lpUGp3dmNHRjBhRDRLSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBOGNHRjBhQ0JrUFNKTk1USXVNakF5TERrd0xqVTBOU0JETVRJdU1ESTVMRGt3TGpVME5TQXhNUzQ0TmpJc09UQXVORFUxSURFeExqYzJPU3c1TUM0eU9UVWdRekV4TGpZek1pdzVNQzR3TlRjZ01URXVOekV6TERnNUxqYzFNaUF4TVM0NU5USXNPRGt1TmpFMElFd3pNQzR6T0Rrc056Z3VPVFk1SUVNek1DNDJNamdzTnpndU9ETXhJRE13TGprek15dzNPQzQ1TVRNZ016RXVNRGN4TERjNUxqRTFNaUJETXpFdU1qQTRMRGM1TGpNNUlETXhMakV5Tnl3M09TNDJPVFlnTXpBdU9EZzRMRGM1TGpnek15Qk1NVEl1TkRVeExEa3dMalEzT0NCTU1USXVNakF5TERrd0xqVTBOU0lnYVdROUlrWnBiR3d0TkNJZ1ptbHNiRDBpSXpZd04wUTRRaUkrUEM5d1lYUm9QZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUR4d1lYUm9JR1E5SWsweE15NDNOalFzTkRJdU5qVTBJRXd4TXk0Mk5UWXNOREl1TlRreUlFd3hNeTQzTURJc05ESXVOREl4SUV3eE9DNDRNemNzTXprdU5EVTNJRXd4T1M0d01EY3NNemt1TlRBeUlFd3hPQzQ1TmpJc016a3VOamN6SUV3eE15NDRNamNzTkRJdU5qTTNJRXd4TXk0M05qUXNOREl1TmpVMElpQnBaRDBpUm1sc2JDMDFJaUJtYVd4c1BTSWpOakEzUkRoQ0lqNDhMM0JoZEdnK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdQSEJoZEdnZ1pEMGlUVGd1TlRJc09UQXVNemMxSUV3NExqVXlMRFEyTGpReU1TQk1PQzQxT0RNc05EWXVNemcxSUV3M05TNDROQ3czTGpVMU5DQk1OelV1T0RRc05URXVOVEE0SUV3M05TNDNOemdzTlRFdU5UUTBJRXc0TGpVeUxEa3dMak0zTlNCTU9DNDFNaXc1TUM0ek56VWdXaUJOT0M0M055dzBOaTQxTmpRZ1REZ3VOemNzT0RrdU9UUTBJRXczTlM0MU9URXNOVEV1TXpZMUlFdzNOUzQxT1RFc055NDVPRFVnVERndU56Y3NORFl1TlRZMElFdzRMamMzTERRMkxqVTJOQ0JhSWlCcFpEMGlSbWxzYkMwMklpQm1hV3hzUFNJak5qQTNSRGhDSWo0OEwzQmhkR2crQ2lBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ1BIQmhkR2dnWkQwaVRUSTBMams0Tml3NE15NHhPRElnUXpJMExqYzFOaXc0TXk0ek16RWdNalF1TXpjMExEZ3pMalUyTmlBeU5DNHhNemNzT0RNdU56QTFJRXd4TWk0Mk16SXNPVEF1TkRBMklFTXhNaTR6T1RVc09UQXVOVFExSURFeUxqUXlOaXc1TUM0Mk5UZ2dNVEl1Tnl3NU1DNDJOVGdnVERFekxqSTJOU3c1TUM0Mk5UZ2dRekV6TGpVMExEa3dMalkxT0NBeE15NDVOVGdzT1RBdU5UUTFJREUwTGpFNU5TdzVNQzQwTURZZ1RESTFMamNzT0RNdU56QTFJRU15TlM0NU16Y3NPRE11TlRZMklESTJMakV5T0N3NE15NDBOVElnTWpZdU1USTFMRGd6TGpRME9TQkRNall1TVRJeUxEZ3pMalEwTnlBeU5pNHhNVGtzT0RNdU1qSWdNall1TVRFNUxEZ3lMamswTmlCRE1qWXVNVEU1TERneUxqWTNNaUF5TlM0NU16RXNPREl1TlRZNUlESTFMamN3TVN3NE1pNDNNVGtnVERJMExqazROaXc0TXk0eE9ESWlJR2xrUFNKR2FXeHNMVGNpSUdacGJHdzlJaU0yTURkRU9FSWlQand2Y0dGMGFENEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0E4Y0dGMGFDQmtQU0pOTVRNdU1qWTJMRGt3TGpjNE1pQk1NVEl1Tnl3NU1DNDNPRElnUXpFeUxqVXNPVEF1TnpneUlERXlMak00TkN3NU1DNDNNallnTVRJdU16VTBMRGt3TGpZeE5pQkRNVEl1TXpJMExEa3dMalV3TmlBeE1pNHpPVGNzT1RBdU16azVJREV5TGpVMk9TdzVNQzR5T1RrZ1RESTBMakEzTkN3NE15NDFPVGNnUXpJMExqTXhMRGd6TGpRMU9TQXlOQzQyT0Rrc09ETXVNakkySURJMExqa3hPQ3c0TXk0d056Z2dUREkxTGpZek15dzRNaTQyTVRRZ1F6STFMamN5TXl3NE1pNDFOVFVnTWpVdU9ERXpMRGd5TGpVeU5TQXlOUzQ0T1Rrc09ESXVOVEkxSUVNeU5pNHdOekVzT0RJdU5USTFJREkyTGpJME5DdzRNaTQyTlRVZ01qWXVNalEwTERneUxqazBOaUJETWpZdU1qUTBMRGd6TGpFMklESTJMakkwTlN3NE15NHpNRGtnTWpZdU1qUTNMRGd6TGpNNE15Qk1Nall1TWpVekxEZ3pMak00TnlCTU1qWXVNalE1TERnekxqUTFOaUJETWpZdU1qUTJMRGd6TGpVek1TQXlOaTR5TkRZc09ETXVOVE14SURJMUxqYzJNeXc0TXk0NE1USWdUREUwTGpJMU9DdzVNQzQxTVRRZ1F6RTBMRGt3TGpZMk5TQXhNeTQxTmpRc09UQXVOemd5SURFekxqSTJOaXc1TUM0M09ESWdUREV6TGpJMk5pdzVNQzQzT0RJZ1dpQk5NVEl1TmpZMkxEa3dMalV6TWlCTU1USXVOeXc1TUM0MU16TWdUREV6TGpJMk5pdzVNQzQxTXpNZ1F6RXpMalV4T0N3NU1DNDFNek1nTVRNdU9URTFMRGt3TGpReU5TQXhOQzR4TXpJc09UQXVNams1SUV3eU5TNDJNemNzT0RNdU5UazNJRU15TlM0NE1EVXNPRE11TkRrNUlESTFMamt6TVN3NE15NDBNalFnTWpVdU9UazRMRGd6TGpNNE15QkRNalV1T1RrMExEZ3pMakk1T1NBeU5TNDVPVFFzT0RNdU1UWTFJREkxTGprNU5DdzRNaTQ1TkRZZ1RESTFMamc1T1N3NE1pNDNOelVnVERJMUxqYzJPQ3c0TWk0NE1qUWdUREkxTGpBMU5DdzRNeTR5T0RjZ1F6STBMamd5TWl3NE15NDBNemNnTWpRdU5ETTRMRGd6TGpZM015QXlOQzR5TERnekxqZ3hNaUJNTVRJdU5qazFMRGt3TGpVeE5DQk1NVEl1TmpZMkxEa3dMalV6TWlCTU1USXVOalkyTERrd0xqVXpNaUJhSWlCcFpEMGlSbWxzYkMwNElpQm1hV3hzUFNJak5qQTNSRGhDSWo0OEwzQmhkR2crQ2lBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ1BIQmhkR2dnWkQwaVRURXpMakkyTml3NE9TNDROekVnVERFeUxqY3NPRGt1T0RjeElFTXhNaTQxTERnNUxqZzNNU0F4TWk0ek9EUXNPRGt1T0RFMUlERXlMak0xTkN3NE9TNDNNRFVnUXpFeUxqTXlOQ3c0T1M0MU9UVWdNVEl1TXprM0xEZzVMalE0T0NBeE1pNDFOamtzT0RrdU16ZzRJRXd5TkM0d056UXNPREl1TmpnMklFTXlOQzR6TXpJc09ESXVOVE0xSURJMExqYzJPQ3c0TWk0ME1UZ2dNalV1TURZM0xEZ3lMalF4T0NCTU1qVXVOak15TERneUxqUXhPQ0JETWpVdU9ETXlMRGd5TGpReE9DQXlOUzQ1TkRnc09ESXVORGMwSURJMUxqazNPQ3c0TWk0MU9EUWdRekkyTGpBd09DdzRNaTQyT1RRZ01qVXVPVE0xTERneUxqZ3dNU0F5TlM0M05qTXNPREl1T1RBeElFd3hOQzR5TlRnc09Ea3VOakF6SUVNeE5DdzRPUzQzTlRRZ01UTXVOVFkwTERnNUxqZzNNU0F4TXk0eU5qWXNPRGt1T0RjeElFd3hNeTR5TmpZc09Ea3VPRGN4SUZvZ1RURXlMalkyTml3NE9TNDJNakVnVERFeUxqY3NPRGt1TmpJeUlFd3hNeTR5TmpZc09Ea3VOakl5SUVNeE15NDFNVGdzT0RrdU5qSXlJREV6TGpreE5TdzRPUzQxTVRVZ01UUXVNVE15TERnNUxqTTRPQ0JNTWpVdU5qTTNMRGd5TGpZNE5pQk1NalV1TmpZM0xEZ3lMalkyT0NCTU1qVXVOak15TERneUxqWTJOeUJNTWpVdU1EWTNMRGd5TGpZMk55QkRNalF1T0RFMUxEZ3lMalkyTnlBeU5DNDBNVGdzT0RJdU56YzFJREkwTGpJc09ESXVPVEF4SUV3eE1pNDJPVFVzT0RrdU5qQXpJRXd4TWk0Mk5qWXNPRGt1TmpJeElFd3hNaTQyTmpZc09Ea3VOakl4SUZvaUlHbGtQU0pHYVd4c0xUa2lJR1pwYkd3OUlpTTJNRGRFT0VJaVBqd3ZjR0YwYUQ0S0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQThjR0YwYUNCa1BTSk5NVEl1TXpjc09UQXVPREF4SUV3eE1pNHpOeXc0T1M0MU5UUWdUREV5TGpNM0xEa3dMamd3TVNJZ2FXUTlJa1pwYkd3dE1UQWlJR1pwYkd3OUlpTTJNRGRFT0VJaVBqd3ZjR0YwYUQ0S0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQThjR0YwYUNCa1BTSk5OaTR4TXl3NU15NDVNREVnUXpVdU16YzVMRGt6TGpnd09DQTBMamd4Tml3NU15NHhOalFnTkM0Mk9URXNPVEl1TlRJMUlFTXpMamcyTERnNExqSTROeUF6TGpVMExEZ3pMamMwTXlBekxqVXlOaXczTVM0eE56TWdRek11TlRFeExEVTRMak00T1NBMExqUXlNeXcxTVM0ME1qZ2dOQzQwTWpNc05URXVOREk0SUVNMUxqRXpOQ3cwTnk0eU9ESWdOeTR5TVN3ME5pNHlNellnTnk0eU1TdzBOaTR5TXpZZ1F6Y3VNakVzTkRZdU1qTTJJRGd4TGpZMk55d3pMakkxSURneUxqQTJPU3d6TGpBeE55QkRPREl1TWpreUxESXVPRGc0SURnMExqVTFOaXd4TGpRek15QTROUzR5TmpRc015NDVOQ0JET0RjdU1qRTBMREV3TGpnMElEZzJMamMxTWl3ek5TNDRNamNnT0RVdU5UTTVMRFF6TGpneE9DQkRPRFV1TVRVc05EWXVNemd6SURnMExqSTVNU3cwT1M0d016TWdPREl1TkRnekxEVXdMakV3TVNCTU55NHlNU3c1TXk0Mk5UTWdRell1T0RJNExEa3pMamczTkNBMkxqUTJNU3c1TXk0NU5ERWdOaTR4TXl3NU15NDVNREVnUXpZdU1UTXNPVE11T1RBeElETXVNelE1TERrekxqUTJJRE11TVRJNUxEa3hMamMzTmlCRE1pNDFOamdzT0RjdU5EazFJREV1T1RjM0xEZ3lMams1TlNBeExqazJNaXczTUM0ME1qVWdRekV1T1RRNExEVTNMalkwTVNBeUxqZzJMRFV3TGpZNElESXVPRFlzTlRBdU5qZ2dRek11TlRjc05EWXVOVE0wSURVdU5qUTNMRFExTGpRNE9TQTFMalkwTnl3ME5TNDBPRGtnUXpVdU5qUTJMRFExTGpRNE9TQTRMakEyTlN3ME5DNHdPVElnTVRJdU1qUTFMRFF4TGpZM09TQk1NVE11TVRFMkxEUXhMalUySUV3eE9TNDNNVFVzTXpjdU56TWdUREU1TGpjMk1Td3pOeTR5TmprZ1REWXVNVE1zT1RNdU9UQXhJaUJwWkQwaVJtbHNiQzB4TVNJZ1ptbHNiRDBpSTBaQlJrRkdRU0krUEM5d1lYUm9QZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUR4d1lYUm9JR1E5SWswMkxqTXhOeXc1TkM0eE5qRWdURFl1TVRBeUxEazBMakUwT0NCTU5pNHhNREVzT1RRdU1UUTRJRXcxTGpnMU55dzVOQzR4TURFZ1F6VXVNVE00TERrekxqazBOU0F6TGpBNE5TdzVNeTR6TmpVZ01pNDRPREVzT1RFdU9EQTVJRU15TGpNeE15dzROeTQwTmprZ01TNDNNamNzT0RJdU9UazJJREV1TnpFekxEY3dMalF5TlNCRE1TNDJPVGtzTlRjdU56Y3hJREl1TmpBMExEVXdMamN4T0NBeUxqWXhNeXcxTUM0Mk5EZ2dRek11TXpNNExEUTJMalF4TnlBMUxqUTBOU3cwTlM0ek1TQTFMalV6TlN3ME5TNHlOallnVERFeUxqRTJNeXcwTVM0ME16a2dUREV6TGpBek15dzBNUzR6TWlCTU1Ua3VORGM1TERNM0xqVTNPQ0JNTVRrdU5URXpMRE0zTGpJME5DQkRNVGt1TlRJMkxETTNMakV3TnlBeE9TNDJORGNzTXpjdU1EQTRJREU1TGpjNE5pd3pOeTR3TWpFZ1F6RTVMamt5TWl3ek55NHdNelFnTWpBdU1ESXpMRE0zTGpFMU5pQXlNQzR3TURrc016Y3VNamt6SUV3eE9TNDVOU3d6Tnk0NE9ESWdUREV6TGpFNU9DdzBNUzQ0TURFZ1RERXlMak15T0N3ME1TNDVNVGtnVERVdU56Y3lMRFExTGpjd05DQkROUzQzTkRFc05EVXVOeklnTXk0M09ESXNORFl1TnpjeUlETXVNVEEyTERVd0xqY3lNaUJETXk0d09Ua3NOVEF1TnpneUlESXVNVGs0TERVM0xqZ3dPQ0F5TGpJeE1pdzNNQzQwTWpRZ1F6SXVNakkyTERneUxqazJNeUF5TGpnd09TdzROeTQwTWlBekxqTTNNeXc1TVM0M01qa2dRek11TkRZMExEa3lMalF5SURRdU1EWXlMRGt5TGpnNE15QTBMalk0TWl3NU15NHhPREVnUXpRdU5UWTJMRGt5TGprNE5DQTBMalE0Tml3NU1pNDNOellnTkM0ME5EWXNPVEl1TlRjeUlFTXpMalkyTlN3NE9DNDFPRGdnTXk0eU9URXNPRFF1TXpjZ015NHlOellzTnpFdU1UY3pJRU16TGpJMk1pdzFPQzQxTWlBMExqRTJOeXcxTVM0ME5qWWdOQzR4TnpZc05URXVNemsySUVNMExqa3dNU3cwTnk0eE5qVWdOeTR3TURnc05EWXVNRFU1SURjdU1EazRMRFEyTGpBeE5DQkROeTR3T1RRc05EWXVNREUxSURneExqVTBNaXd6TGpBek5DQTRNUzQ1TkRRc01pNDRNRElnVERneExqazNNaXd5TGpjNE5TQkRPREl1T0RjMkxESXVNalEzSURnekxqWTVNaXd5TGpBNU55QTROQzR6TXpJc01pNHpOVElnUXpnMExqZzROeXd5TGpVM015QTROUzR5T0RFc015NHdPRFVnT0RVdU5UQTBMRE11T0RjeUlFTTROeTQxTVRnc01URWdPRFl1T1RZMExETTJMakE1TVNBNE5TNDNPRFVzTkRNdU9EVTFJRU00TlM0eU56Z3NORGN1TVRrMklEZzBMakl4TERRNUxqTTNJRGd5TGpZeExEVXdMak14TnlCTU55NHpNelVzT1RNdU9EWTVJRU0yTGprNU9TdzVOQzR3TmpNZ05pNDJOVGdzT1RRdU1UWXhJRFl1TXpFM0xEazBMakUyTVNCTU5pNHpNVGNzT1RRdU1UWXhJRm9nVFRZdU1UY3NPVE11TmpVMElFTTJMalEyTXl3NU15NDJPU0EyTGpjM05DdzVNeTQyTVRjZ055NHdPRFVzT1RNdU5ETTNJRXc0TWk0ek5UZ3NORGt1T0RnMklFTTROQzR4T0RFc05EZ3VPREE0SURnMExqazJMRFExTGprM01TQTROUzR5T1RJc05ETXVOemdnUXpnMkxqUTJOaXd6Tmk0d05Ea2dPRGN1TURJekxERXhMakE0TlNBNE5TNHdNalFzTkM0d01EZ2dRemcwTGpnME5pd3pMak0zTnlBNE5DNDFOVEVzTWk0NU56WWdPRFF1TVRRNExESXVPREUySUVNNE15NDJOalFzTWk0Mk1qTWdPREl1T1RneUxESXVOelkwSURneUxqSXlOeXd6TGpJeE15Qk1PREl1TVRrekxETXVNak0wSUVNNE1TNDNPVEVzTXk0ME5qWWdOeTR6TXpVc05EWXVORFV5SURjdU16TTFMRFEyTGpRMU1pQkROeTR6TURRc05EWXVORFk1SURVdU16UTJMRFEzTGpVeU1TQTBMalkyT1N3MU1TNDBOekVnUXpRdU5qWXlMRFV4TGpVeklETXVOell4TERVNExqVTFOaUF6TGpjM05TdzNNUzR4TnpNZ1F6TXVOemtzT0RRdU16STRJRFF1TVRZeExEZzRMalV5TkNBMExqa3pOaXc1TWk0ME56WWdRelV1TURJMkxEa3lMamt6TnlBMUxqUXhNaXc1TXk0ME5Ua2dOUzQ1TnpNc09UTXVOakUxSUVNMkxqQTROeXc1TXk0Mk5DQTJMakUxT0N3NU15NDJOVElnTmk0eE5qa3NPVE11TmpVMElFdzJMakUzTERrekxqWTFOQ0JNTmk0eE55dzVNeTQyTlRRZ1dpSWdhV1E5SWtacGJHd3RNVElpSUdacGJHdzlJaU0wTlRWQk5qUWlQand2Y0dGMGFENEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0E4Y0dGMGFDQmtQU0pOTnk0ek1UY3NOamd1T1RneUlFTTNMamd3Tml3Mk9DNDNNREVnT0M0eU1ESXNOamd1T1RJMklEZ3VNakF5TERZNUxqUTROeUJET0M0eU1ESXNOekF1TURRM0lEY3VPREEyTERjd0xqY3pJRGN1TXpFM0xEY3hMakF4TWlCRE5pNDRNamtzTnpFdU1qazBJRFl1TkRNekxEY3hMakEyT1NBMkxqUXpNeXczTUM0MU1EZ2dRell1TkRNekxEWTVMamswT0NBMkxqZ3lPU3cyT1M0eU5qVWdOeTR6TVRjc05qZ3VPVGd5SWlCcFpEMGlSbWxzYkMweE15SWdabWxzYkQwaUkwWkdSa1pHUmlJK1BDOXdZWFJvUGdvZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lEeHdZWFJvSUdROUlrMDJMamt5TERjeExqRXpNeUJETmk0Mk16RXNOekV1TVRNeklEWXVORE16TERjd0xqa3dOU0EyTGpRek15dzNNQzQxTURnZ1F6WXVORE16TERZNUxqazBPQ0EyTGpneU9TdzJPUzR5TmpVZ055NHpNVGNzTmpndU9UZ3lJRU0zTGpRMkxEWTRMamtnTnk0MU9UVXNOamd1T0RZeElEY3VOekUwTERZNExqZzJNU0JET0M0d01ETXNOamd1T0RZeElEZ3VNakF5TERZNUxqQTVJRGd1TWpBeUxEWTVMalE0TnlCRE9DNHlNRElzTnpBdU1EUTNJRGN1T0RBMkxEY3dMamN6SURjdU16RTNMRGN4TGpBeE1pQkROeTR4TnpRc056RXVNRGswSURjdU1ETTVMRGN4TGpFek15QTJMamt5TERjeExqRXpNeUJOTnk0M01UUXNOamd1TmpjMElFTTNMalUxTnl3Mk9DNDJOelFnTnk0ek9USXNOamd1TnpJeklEY3VNakkwTERZNExqZ3lNU0JETmk0Mk56WXNOamt1TVRNNElEWXVNalEyTERZNUxqZzNPU0EyTGpJME5pdzNNQzQxTURnZ1F6WXVNalEyTERjd0xqazVOQ0EyTGpVeE55dzNNUzR6TWlBMkxqa3lMRGN4TGpNeUlFTTNMakEzT0N3M01TNHpNaUEzTGpJME15dzNNUzR5TnpFZ055NDBNVEVzTnpFdU1UYzBJRU0zTGprMU9TdzNNQzQ0TlRjZ09DNHpPRGtzTnpBdU1URTNJRGd1TXpnNUxEWTVMalE0TnlCRE9DNHpPRGtzTmprdU1EQXhJRGd1TVRFM0xEWTRMalkzTkNBM0xqY3hOQ3cyT0M0Mk56UWlJR2xrUFNKR2FXeHNMVEUwSWlCbWFXeHNQU0lqT0RBNU4wRXlJajQ4TDNCaGRHZytDaUFnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnUEhCaGRHZ2daRDBpVFRZdU9USXNOekF1T1RRM0lFTTJMalkwT1N3M01DNDVORGNnTmk0Mk1qRXNOekF1TmpRZ05pNDJNakVzTnpBdU5UQTRJRU0yTGpZeU1TdzNNQzR3TVRjZ05pNDVPRElzTmprdU16a3lJRGN1TkRFeExEWTVMakUwTlNCRE55NDFNakVzTmprdU1EZ3lJRGN1TmpJMUxEWTVMakEwT1NBM0xqY3hOQ3cyT1M0d05Ea2dRemN1T1RnMkxEWTVMakEwT1NBNExqQXhOU3cyT1M0ek5UVWdPQzR3TVRVc05qa3VORGczSUVNNExqQXhOU3cyT1M0NU56Z2dOeTQyTlRJc056QXVOakF6SURjdU1qSTBMRGN3TGpnMU1TQkROeTR4TVRVc056QXVPVEUwSURjdU1ERXNOekF1T1RRM0lEWXVPVElzTnpBdU9UUTNJRTAzTGpjeE5DdzJPQzQ0TmpFZ1F6Y3VOVGsxTERZNExqZzJNU0EzTGpRMkxEWTRMamtnTnk0ek1UY3NOamd1T1RneUlFTTJMamd5T1N3Mk9TNHlOalVnTmk0ME16TXNOamt1T1RRNElEWXVORE16TERjd0xqVXdPQ0JETmk0ME16TXNOekF1T1RBMUlEWXVOak14TERjeExqRXpNeUEyTGpreUxEY3hMakV6TXlCRE55NHdNemtzTnpFdU1UTXpJRGN1TVRjMExEY3hMakE1TkNBM0xqTXhOeXczTVM0d01USWdRemN1T0RBMkxEY3dMamN6SURndU1qQXlMRGN3TGpBME55QTRMakl3TWl3Mk9TNDBPRGNnUXpndU1qQXlMRFk1TGpBNUlEZ3VNREF6TERZNExqZzJNU0EzTGpjeE5DdzJPQzQ0TmpFaUlHbGtQU0pHYVd4c0xURTFJaUJtYVd4c1BTSWpPREE1TjBFeUlqNDhMM0JoZEdnK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdQSEJoZEdnZ1pEMGlUVGN1TkRRMExEZzFMak0xSUVNM0xqY3dPQ3c0TlM0eE9UZ2dOeTQ1TWpFc09EVXVNekU1SURjdU9USXhMRGcxTGpZeU1pQkROeTQ1TWpFc09EVXVPVEkxSURjdU56QTRMRGcyTGpJNU1pQTNMalEwTkN3NE5pNDBORFFnUXpjdU1UZ3hMRGcyTGpVNU55QTJMamsyTnl3NE5pNDBOelVnTmk0NU5qY3NPRFl1TVRjeklFTTJMamsyTnl3NE5TNDROekVnTnk0eE9ERXNPRFV1TlRBeUlEY3VORFEwTERnMUxqTTFJaUJwWkQwaVJtbHNiQzB4TmlJZ1ptbHNiRDBpSTBaR1JrWkdSaUkrUEM5d1lYUm9QZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUR4d1lYUm9JR1E5SWswM0xqSXpMRGcyTGpVeElFTTNMakEzTkN3NE5pNDFNU0EyTGprMk55dzROaTR6T0RjZ05pNDVOamNzT0RZdU1UY3pJRU0yTGprMk55dzROUzQ0TnpFZ055NHhPREVzT0RVdU5UQXlJRGN1TkRRMExEZzFMak0xSUVNM0xqVXlNU3c0TlM0ek1EVWdOeTQxT1RRc09EVXVNamcwSURjdU5qVTRMRGcxTGpJNE5DQkROeTQ0TVRRc09EVXVNamcwSURjdU9USXhMRGcxTGpRd09DQTNMamt5TVN3NE5TNDJNaklnUXpjdU9USXhMRGcxTGpreU5TQTNMamN3T0N3NE5pNHlPVElnTnk0ME5EUXNPRFl1TkRRMElFTTNMak0yTnl3NE5pNDBPRGtnTnk0eU9UUXNPRFl1TlRFZ055NHlNeXc0Tmk0MU1TQk5OeTQyTlRnc09EVXVNRGs0SUVNM0xqVTFPQ3c0TlM0d09UZ2dOeTQwTlRVc09EVXVNVEkzSURjdU16VXhMRGcxTGpFNE9DQkROeTR3TXpFc09EVXVNemN6SURZdU56Z3hMRGcxTGpnd05pQTJMamM0TVN3NE5pNHhOek1nUXpZdU56Z3hMRGcyTGpRNE1pQTJMamsyTml3NE5pNDJPVGNnTnk0eU15dzROaTQyT1RjZ1F6Y3VNek1zT0RZdU5qazNJRGN1TkRNekxEZzJMalkyTmlBM0xqVXpPQ3c0Tmk0Mk1EY2dRemN1T0RVNExEZzJMalF5TWlBNExqRXdPQ3c0TlM0NU9Ea2dPQzR4TURnc09EVXVOakl5SUVNNExqRXdPQ3c0TlM0ek1UTWdOeTQ1TWpNc09EVXVNRGs0SURjdU5qVTRMRGcxTGpBNU9DSWdhV1E5SWtacGJHd3RNVGNpSUdacGJHdzlJaU00TURrM1FUSWlQand2Y0dGMGFENEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0E4Y0dGMGFDQmtQU0pOTnk0eU15dzROaTR6TWpJZ1REY3VNVFUwTERnMkxqRTNNeUJETnk0eE5UUXNPRFV1T1RNNElEY3VNek16TERnMUxqWXlPU0EzTGpVek9DdzROUzQxTVRJZ1REY3VOalU0TERnMUxqUTNNU0JNTnk0M016UXNPRFV1TmpJeUlFTTNMamN6TkN3NE5TNDROVFlnTnk0MU5UVXNPRFl1TVRZMElEY3VNelV4TERnMkxqSTRNaUJNTnk0eU15dzROaTR6TWpJZ1RUY3VOalU0TERnMUxqSTROQ0JETnk0MU9UUXNPRFV1TWpnMElEY3VOVEl4TERnMUxqTXdOU0EzTGpRME5DdzROUzR6TlNCRE55NHhPREVzT0RVdU5UQXlJRFl1T1RZM0xEZzFMamczTVNBMkxqazJOeXc0Tmk0eE56TWdRell1T1RZM0xEZzJMak00TnlBM0xqQTNOQ3c0Tmk0MU1TQTNMakl6TERnMkxqVXhJRU0zTGpJNU5DdzROaTQxTVNBM0xqTTJOeXc0Tmk0ME9Ea2dOeTQwTkRRc09EWXVORFEwSUVNM0xqY3dPQ3c0Tmk0eU9USWdOeTQ1TWpFc09EVXVPVEkxSURjdU9USXhMRGcxTGpZeU1pQkROeTQ1TWpFc09EVXVOREE0SURjdU9ERTBMRGcxTGpJNE5DQTNMalkxT0N3NE5TNHlPRFFpSUdsa1BTSkdhV3hzTFRFNElpQm1hV3hzUFNJak9EQTVOMEV5SWo0OEwzQmhkR2crQ2lBZ0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ1BIQmhkR2dnWkQwaVRUYzNMakkzT0N3M0xqYzJPU0JNTnpjdU1qYzRMRFV4TGpRek5pQk1NVEF1TWpBNExEa3dMakUySUV3eE1DNHlNRGdzTkRZdU5Ea3pJRXczTnk0eU56Z3NOeTQzTmpraUlHbGtQU0pHYVd4c0xURTVJaUJtYVd4c1BTSWpORFUxUVRZMElqNDhMM0JoZEdnK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lDQWdQSEJoZEdnZ1pEMGlUVEV3TGpBNE15dzVNQzR6TnpVZ1RERXdMakE0TXl3ME5pNDBNakVnVERFd0xqRTBOaXcwTmk0ek9EVWdURGMzTGpRd015dzNMalUxTkNCTU56Y3VOREF6TERVeExqVXdPQ0JNTnpjdU16UXhMRFV4TGpVME5DQk1NVEF1TURnekxEa3dMak0zTlNCTU1UQXVNRGd6TERrd0xqTTNOU0JhSUUweE1DNHpNek1zTkRZdU5UWTBJRXd4TUM0ek16TXNPRGt1T1RRMElFdzNOeTR4TlRRc05URXVNelkxSUV3M055NHhOVFFzTnk0NU9EVWdUREV3TGpNek15dzBOaTQxTmpRZ1RERXdMak16TXl3ME5pNDFOalFnV2lJZ2FXUTlJa1pwYkd3dE1qQWlJR1pwYkd3OUlpTTJNRGRFT0VJaVBqd3ZjR0YwYUQ0S0lDQWdJQ0FnSUNBZ0lDQWdJQ0FnSUR3dlp6NEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBZ0lEeHdZWFJvSUdROUlrMHhNalV1TnpNM0xEZzRMalkwTnlCTU1URTRMakE1T0N3NU1TNDVPREVnVERFeE9DNHdPVGdzT0RRZ1RERXdOaTQyTXprc09EZ3VOekV6SUV3eE1EWXVOak01TERrMkxqazRNaUJNT1Rrc01UQXdMak14TlNCTU1URXlMak0yT1N3eE1ETXVPVFl4SUV3eE1qVXVOek0zTERnNExqWTBOeUlnYVdROUlrbHRjRzl5ZEdWa0xVeGhlV1Z5Y3kxRGIzQjVMVElpSUdacGJHdzlJaU0wTlRWQk5qUWlJSE5yWlhSamFEcDBlWEJsUFNKTlUxTm9ZWEJsUjNKdmRYQWlQand2Y0dGMGFENEtJQ0FnSUNBZ0lDQWdJQ0FnUEM5blBnb2dJQ0FnSUNBZ0lEd3ZaejRLSUNBZ0lEd3ZaejRLUEM5emRtYysnKTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gUm90YXRlSW5zdHJ1Y3Rpb25zO1xuIiwiLypcbiAqIENvcHlyaWdodCAyMDE1IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKi9cblxudmFyIFNlbnNvclNhbXBsZSA9IHJlcXVpcmUoJy4vc2Vuc29yLXNhbXBsZS5qcycpO1xudmFyIE1hdGhVdGlsID0gcmVxdWlyZSgnLi4vbWF0aC11dGlsLmpzJyk7XG52YXIgVXRpbCA9IHJlcXVpcmUoJy4uL3V0aWwuanMnKTtcblxuLyoqXG4gKiBBbiBpbXBsZW1lbnRhdGlvbiBvZiBhIHNpbXBsZSBjb21wbGVtZW50YXJ5IGZpbHRlciwgd2hpY2ggZnVzZXMgZ3lyb3Njb3BlIGFuZFxuICogYWNjZWxlcm9tZXRlciBkYXRhIGZyb20gdGhlICdkZXZpY2Vtb3Rpb24nIGV2ZW50LlxuICpcbiAqIEFjY2VsZXJvbWV0ZXIgZGF0YSBpcyB2ZXJ5IG5vaXN5LCBidXQgc3RhYmxlIG92ZXIgdGhlIGxvbmcgdGVybS5cbiAqIEd5cm9zY29wZSBkYXRhIGlzIHNtb290aCwgYnV0IHRlbmRzIHRvIGRyaWZ0IG92ZXIgdGhlIGxvbmcgdGVybS5cbiAqXG4gKiBUaGlzIGZ1c2lvbiBpcyByZWxhdGl2ZWx5IHNpbXBsZTpcbiAqIDEuIEdldCBvcmllbnRhdGlvbiBlc3RpbWF0ZXMgZnJvbSBhY2NlbGVyb21ldGVyIGJ5IGFwcGx5aW5nIGEgbG93LXBhc3MgZmlsdGVyXG4gKiAgICBvbiB0aGF0IGRhdGEuXG4gKiAyLiBHZXQgb3JpZW50YXRpb24gZXN0aW1hdGVzIGZyb20gZ3lyb3Njb3BlIGJ5IGludGVncmF0aW5nIG92ZXIgdGltZS5cbiAqIDMuIENvbWJpbmUgdGhlIHR3byBlc3RpbWF0ZXMsIHdlaWdoaW5nICgxKSBpbiB0aGUgbG9uZyB0ZXJtLCBidXQgKDIpIGZvciB0aGVcbiAqICAgIHNob3J0IHRlcm0uXG4gKi9cbmZ1bmN0aW9uIENvbXBsZW1lbnRhcnlGaWx0ZXIoa0ZpbHRlcikge1xuICB0aGlzLmtGaWx0ZXIgPSBrRmlsdGVyO1xuXG4gIC8vIFJhdyBzZW5zb3IgbWVhc3VyZW1lbnRzLlxuICB0aGlzLmN1cnJlbnRBY2NlbE1lYXN1cmVtZW50ID0gbmV3IFNlbnNvclNhbXBsZSgpO1xuICB0aGlzLmN1cnJlbnRHeXJvTWVhc3VyZW1lbnQgPSBuZXcgU2Vuc29yU2FtcGxlKCk7XG4gIHRoaXMucHJldmlvdXNHeXJvTWVhc3VyZW1lbnQgPSBuZXcgU2Vuc29yU2FtcGxlKCk7XG5cbiAgLy8gU2V0IGRlZmF1bHQgbG9vayBkaXJlY3Rpb24gdG8gYmUgaW4gdGhlIGNvcnJlY3QgZGlyZWN0aW9uLlxuICBpZiAoVXRpbC5pc0lPUygpKSB7XG4gICAgdGhpcy5maWx0ZXJRID0gbmV3IE1hdGhVdGlsLlF1YXRlcm5pb24oLTEsIDAsIDAsIDEpO1xuICB9IGVsc2Uge1xuICAgIHRoaXMuZmlsdGVyUSA9IG5ldyBNYXRoVXRpbC5RdWF0ZXJuaW9uKDEsIDAsIDAsIDEpO1xuICB9XG4gIHRoaXMucHJldmlvdXNGaWx0ZXJRID0gbmV3IE1hdGhVdGlsLlF1YXRlcm5pb24oKTtcbiAgdGhpcy5wcmV2aW91c0ZpbHRlclEuY29weSh0aGlzLmZpbHRlclEpO1xuXG4gIC8vIE9yaWVudGF0aW9uIGJhc2VkIG9uIHRoZSBhY2NlbGVyb21ldGVyLlxuICB0aGlzLmFjY2VsUSA9IG5ldyBNYXRoVXRpbC5RdWF0ZXJuaW9uKCk7XG4gIC8vIFdoZXRoZXIgb3Igbm90IHRoZSBvcmllbnRhdGlvbiBoYXMgYmVlbiBpbml0aWFsaXplZC5cbiAgdGhpcy5pc09yaWVudGF0aW9uSW5pdGlhbGl6ZWQgPSBmYWxzZTtcbiAgLy8gUnVubmluZyBlc3RpbWF0ZSBvZiBncmF2aXR5IGJhc2VkIG9uIHRoZSBjdXJyZW50IG9yaWVudGF0aW9uLlxuICB0aGlzLmVzdGltYXRlZEdyYXZpdHkgPSBuZXcgTWF0aFV0aWwuVmVjdG9yMygpO1xuICAvLyBNZWFzdXJlZCBncmF2aXR5IGJhc2VkIG9uIGFjY2VsZXJvbWV0ZXIuXG4gIHRoaXMubWVhc3VyZWRHcmF2aXR5ID0gbmV3IE1hdGhVdGlsLlZlY3RvcjMoKTtcblxuICAvLyBEZWJ1ZyBvbmx5IHF1YXRlcm5pb24gb2YgZ3lyby1iYXNlZCBvcmllbnRhdGlvbi5cbiAgdGhpcy5neXJvSW50ZWdyYWxRID0gbmV3IE1hdGhVdGlsLlF1YXRlcm5pb24oKTtcbn1cblxuQ29tcGxlbWVudGFyeUZpbHRlci5wcm90b3R5cGUuYWRkQWNjZWxNZWFzdXJlbWVudCA9IGZ1bmN0aW9uKHZlY3RvciwgdGltZXN0YW1wUykge1xuICB0aGlzLmN1cnJlbnRBY2NlbE1lYXN1cmVtZW50LnNldCh2ZWN0b3IsIHRpbWVzdGFtcFMpO1xufTtcblxuQ29tcGxlbWVudGFyeUZpbHRlci5wcm90b3R5cGUuYWRkR3lyb01lYXN1cmVtZW50ID0gZnVuY3Rpb24odmVjdG9yLCB0aW1lc3RhbXBTKSB7XG4gIHRoaXMuY3VycmVudEd5cm9NZWFzdXJlbWVudC5zZXQodmVjdG9yLCB0aW1lc3RhbXBTKTtcblxuICB2YXIgZGVsdGFUID0gdGltZXN0YW1wUyAtIHRoaXMucHJldmlvdXNHeXJvTWVhc3VyZW1lbnQudGltZXN0YW1wUztcbiAgaWYgKFV0aWwuaXNUaW1lc3RhbXBEZWx0YVZhbGlkKGRlbHRhVCkpIHtcbiAgICB0aGlzLnJ1bl8oKTtcbiAgfVxuXG4gIHRoaXMucHJldmlvdXNHeXJvTWVhc3VyZW1lbnQuY29weSh0aGlzLmN1cnJlbnRHeXJvTWVhc3VyZW1lbnQpO1xufTtcblxuQ29tcGxlbWVudGFyeUZpbHRlci5wcm90b3R5cGUucnVuXyA9IGZ1bmN0aW9uKCkge1xuXG4gIGlmICghdGhpcy5pc09yaWVudGF0aW9uSW5pdGlhbGl6ZWQpIHtcbiAgICB0aGlzLmFjY2VsUSA9IHRoaXMuYWNjZWxUb1F1YXRlcm5pb25fKHRoaXMuY3VycmVudEFjY2VsTWVhc3VyZW1lbnQuc2FtcGxlKTtcbiAgICB0aGlzLnByZXZpb3VzRmlsdGVyUS5jb3B5KHRoaXMuYWNjZWxRKTtcbiAgICB0aGlzLmlzT3JpZW50YXRpb25Jbml0aWFsaXplZCA9IHRydWU7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdmFyIGRlbHRhVCA9IHRoaXMuY3VycmVudEd5cm9NZWFzdXJlbWVudC50aW1lc3RhbXBTIC1cbiAgICAgIHRoaXMucHJldmlvdXNHeXJvTWVhc3VyZW1lbnQudGltZXN0YW1wUztcblxuICAvLyBDb252ZXJ0IGd5cm8gcm90YXRpb24gdmVjdG9yIHRvIGEgcXVhdGVybmlvbiBkZWx0YS5cbiAgdmFyIGd5cm9EZWx0YVEgPSB0aGlzLmd5cm9Ub1F1YXRlcm5pb25EZWx0YV8odGhpcy5jdXJyZW50R3lyb01lYXN1cmVtZW50LnNhbXBsZSwgZGVsdGFUKTtcbiAgdGhpcy5neXJvSW50ZWdyYWxRLm11bHRpcGx5KGd5cm9EZWx0YVEpO1xuXG4gIC8vIGZpbHRlcl8xID0gSyAqIChmaWx0ZXJfMCArIGd5cm8gKiBkVCkgKyAoMSAtIEspICogYWNjZWwuXG4gIHRoaXMuZmlsdGVyUS5jb3B5KHRoaXMucHJldmlvdXNGaWx0ZXJRKTtcbiAgdGhpcy5maWx0ZXJRLm11bHRpcGx5KGd5cm9EZWx0YVEpO1xuXG4gIC8vIENhbGN1bGF0ZSB0aGUgZGVsdGEgYmV0d2VlbiB0aGUgY3VycmVudCBlc3RpbWF0ZWQgZ3Jhdml0eSBhbmQgdGhlIHJlYWxcbiAgLy8gZ3Jhdml0eSB2ZWN0b3IgZnJvbSBhY2NlbGVyb21ldGVyLlxuICB2YXIgaW52RmlsdGVyUSA9IG5ldyBNYXRoVXRpbC5RdWF0ZXJuaW9uKCk7XG4gIGludkZpbHRlclEuY29weSh0aGlzLmZpbHRlclEpO1xuICBpbnZGaWx0ZXJRLmludmVyc2UoKTtcblxuICB0aGlzLmVzdGltYXRlZEdyYXZpdHkuc2V0KDAsIDAsIC0xKTtcbiAgdGhpcy5lc3RpbWF0ZWRHcmF2aXR5LmFwcGx5UXVhdGVybmlvbihpbnZGaWx0ZXJRKTtcbiAgdGhpcy5lc3RpbWF0ZWRHcmF2aXR5Lm5vcm1hbGl6ZSgpO1xuXG4gIHRoaXMubWVhc3VyZWRHcmF2aXR5LmNvcHkodGhpcy5jdXJyZW50QWNjZWxNZWFzdXJlbWVudC5zYW1wbGUpO1xuICB0aGlzLm1lYXN1cmVkR3Jhdml0eS5ub3JtYWxpemUoKTtcblxuICAvLyBDb21wYXJlIGVzdGltYXRlZCBncmF2aXR5IHdpdGggbWVhc3VyZWQgZ3Jhdml0eSwgZ2V0IHRoZSBkZWx0YSBxdWF0ZXJuaW9uXG4gIC8vIGJldHdlZW4gdGhlIHR3by5cbiAgdmFyIGRlbHRhUSA9IG5ldyBNYXRoVXRpbC5RdWF0ZXJuaW9uKCk7XG4gIGRlbHRhUS5zZXRGcm9tVW5pdFZlY3RvcnModGhpcy5lc3RpbWF0ZWRHcmF2aXR5LCB0aGlzLm1lYXN1cmVkR3Jhdml0eSk7XG4gIGRlbHRhUS5pbnZlcnNlKCk7XG5cbiAgaWYgKFV0aWwuaXNEZWJ1ZygpKSB7XG4gICAgY29uc29sZS5sb2coJ0RlbHRhOiAlZCBkZWcsIEdfZXN0OiAoJXMsICVzLCAlcyksIEdfbWVhczogKCVzLCAlcywgJXMpJyxcbiAgICAgICAgICAgICAgICBNYXRoVXRpbC5yYWRUb0RlZyAqIFV0aWwuZ2V0UXVhdGVybmlvbkFuZ2xlKGRlbHRhUSksXG4gICAgICAgICAgICAgICAgKHRoaXMuZXN0aW1hdGVkR3Jhdml0eS54KS50b0ZpeGVkKDEpLFxuICAgICAgICAgICAgICAgICh0aGlzLmVzdGltYXRlZEdyYXZpdHkueSkudG9GaXhlZCgxKSxcbiAgICAgICAgICAgICAgICAodGhpcy5lc3RpbWF0ZWRHcmF2aXR5LnopLnRvRml4ZWQoMSksXG4gICAgICAgICAgICAgICAgKHRoaXMubWVhc3VyZWRHcmF2aXR5LngpLnRvRml4ZWQoMSksXG4gICAgICAgICAgICAgICAgKHRoaXMubWVhc3VyZWRHcmF2aXR5LnkpLnRvRml4ZWQoMSksXG4gICAgICAgICAgICAgICAgKHRoaXMubWVhc3VyZWRHcmF2aXR5LnopLnRvRml4ZWQoMSkpO1xuICB9XG5cbiAgLy8gQ2FsY3VsYXRlIHRoZSBTTEVSUCB0YXJnZXQ6IGN1cnJlbnQgb3JpZW50YXRpb24gcGx1cyB0aGUgbWVhc3VyZWQtZXN0aW1hdGVkXG4gIC8vIHF1YXRlcm5pb24gZGVsdGEuXG4gIHZhciB0YXJnZXRRID0gbmV3IE1hdGhVdGlsLlF1YXRlcm5pb24oKTtcbiAgdGFyZ2V0US5jb3B5KHRoaXMuZmlsdGVyUSk7XG4gIHRhcmdldFEubXVsdGlwbHkoZGVsdGFRKTtcblxuICAvLyBTTEVSUCBmYWN0b3I6IDAgaXMgcHVyZSBneXJvLCAxIGlzIHB1cmUgYWNjZWwuXG4gIHRoaXMuZmlsdGVyUS5zbGVycCh0YXJnZXRRLCAxIC0gdGhpcy5rRmlsdGVyKTtcblxuICB0aGlzLnByZXZpb3VzRmlsdGVyUS5jb3B5KHRoaXMuZmlsdGVyUSk7XG59O1xuXG5Db21wbGVtZW50YXJ5RmlsdGVyLnByb3RvdHlwZS5nZXRPcmllbnRhdGlvbiA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gdGhpcy5maWx0ZXJRO1xufTtcblxuQ29tcGxlbWVudGFyeUZpbHRlci5wcm90b3R5cGUuYWNjZWxUb1F1YXRlcm5pb25fID0gZnVuY3Rpb24oYWNjZWwpIHtcbiAgdmFyIG5vcm1BY2NlbCA9IG5ldyBNYXRoVXRpbC5WZWN0b3IzKCk7XG4gIG5vcm1BY2NlbC5jb3B5KGFjY2VsKTtcbiAgbm9ybUFjY2VsLm5vcm1hbGl6ZSgpO1xuICB2YXIgcXVhdCA9IG5ldyBNYXRoVXRpbC5RdWF0ZXJuaW9uKCk7XG4gIHF1YXQuc2V0RnJvbVVuaXRWZWN0b3JzKG5ldyBNYXRoVXRpbC5WZWN0b3IzKDAsIDAsIC0xKSwgbm9ybUFjY2VsKTtcbiAgcXVhdC5pbnZlcnNlKCk7XG4gIHJldHVybiBxdWF0O1xufTtcblxuQ29tcGxlbWVudGFyeUZpbHRlci5wcm90b3R5cGUuZ3lyb1RvUXVhdGVybmlvbkRlbHRhXyA9IGZ1bmN0aW9uKGd5cm8sIGR0KSB7XG4gIC8vIEV4dHJhY3QgYXhpcyBhbmQgYW5nbGUgZnJvbSB0aGUgZ3lyb3Njb3BlIGRhdGEuXG4gIHZhciBxdWF0ID0gbmV3IE1hdGhVdGlsLlF1YXRlcm5pb24oKTtcbiAgdmFyIGF4aXMgPSBuZXcgTWF0aFV0aWwuVmVjdG9yMygpO1xuICBheGlzLmNvcHkoZ3lybyk7XG4gIGF4aXMubm9ybWFsaXplKCk7XG4gIHF1YXQuc2V0RnJvbUF4aXNBbmdsZShheGlzLCBneXJvLmxlbmd0aCgpICogZHQpO1xuICByZXR1cm4gcXVhdDtcbn07XG5cblxubW9kdWxlLmV4cG9ydHMgPSBDb21wbGVtZW50YXJ5RmlsdGVyO1xuIiwiLypcbiAqIENvcHlyaWdodCAyMDE1IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKi9cbnZhciBDb21wbGVtZW50YXJ5RmlsdGVyID0gcmVxdWlyZSgnLi9jb21wbGVtZW50YXJ5LWZpbHRlci5qcycpO1xudmFyIFBvc2VQcmVkaWN0b3IgPSByZXF1aXJlKCcuL3Bvc2UtcHJlZGljdG9yLmpzJyk7XG52YXIgVG91Y2hQYW5uZXIgPSByZXF1aXJlKCcuLi90b3VjaC1wYW5uZXIuanMnKTtcbnZhciBNYXRoVXRpbCA9IHJlcXVpcmUoJy4uL21hdGgtdXRpbC5qcycpO1xudmFyIFV0aWwgPSByZXF1aXJlKCcuLi91dGlsLmpzJyk7XG5cbi8qKlxuICogVGhlIHBvc2Ugc2Vuc29yLCBpbXBsZW1lbnRlZCB1c2luZyBEZXZpY2VNb3Rpb24gQVBJcy5cbiAqL1xuZnVuY3Rpb24gRnVzaW9uUG9zZVNlbnNvcigpIHtcbiAgdGhpcy5kZXZpY2VJZCA9ICd3ZWJ2ci1wb2x5ZmlsbDpmdXNlZCc7XG4gIHRoaXMuZGV2aWNlTmFtZSA9ICdWUiBQb3NpdGlvbiBEZXZpY2UgKHdlYnZyLXBvbHlmaWxsOmZ1c2VkKSc7XG5cbiAgdGhpcy5hY2NlbGVyb21ldGVyID0gbmV3IE1hdGhVdGlsLlZlY3RvcjMoKTtcbiAgdGhpcy5neXJvc2NvcGUgPSBuZXcgTWF0aFV0aWwuVmVjdG9yMygpO1xuXG4gIHRoaXMuc3RhcnQoKTtcblxuICB0aGlzLmZpbHRlciA9IG5ldyBDb21wbGVtZW50YXJ5RmlsdGVyKHdpbmRvdy5XZWJWUkNvbmZpZy5LX0ZJTFRFUik7XG4gIHRoaXMucG9zZVByZWRpY3RvciA9IG5ldyBQb3NlUHJlZGljdG9yKHdpbmRvdy5XZWJWUkNvbmZpZy5QUkVESUNUSU9OX1RJTUVfUyk7XG4gIHRoaXMudG91Y2hQYW5uZXIgPSBuZXcgVG91Y2hQYW5uZXIoKTtcblxuICB0aGlzLmZpbHRlclRvV29ybGRRID0gbmV3IE1hdGhVdGlsLlF1YXRlcm5pb24oKTtcblxuICAvLyBTZXQgdGhlIGZpbHRlciB0byB3b3JsZCB0cmFuc2Zvcm0sIGRlcGVuZGluZyBvbiBPUy5cbiAgaWYgKFV0aWwuaXNJT1MoKSkge1xuICAgIHRoaXMuZmlsdGVyVG9Xb3JsZFEuc2V0RnJvbUF4aXNBbmdsZShuZXcgTWF0aFV0aWwuVmVjdG9yMygxLCAwLCAwKSwgTWF0aC5QSSAvIDIpO1xuICB9IGVsc2Uge1xuICAgIHRoaXMuZmlsdGVyVG9Xb3JsZFEuc2V0RnJvbUF4aXNBbmdsZShuZXcgTWF0aFV0aWwuVmVjdG9yMygxLCAwLCAwKSwgLU1hdGguUEkgLyAyKTtcbiAgfVxuXG4gIHRoaXMuaW52ZXJzZVdvcmxkVG9TY3JlZW5RID0gbmV3IE1hdGhVdGlsLlF1YXRlcm5pb24oKTtcbiAgdGhpcy53b3JsZFRvU2NyZWVuUSA9IG5ldyBNYXRoVXRpbC5RdWF0ZXJuaW9uKCk7XG4gIHRoaXMub3JpZ2luYWxQb3NlQWRqdXN0USA9IG5ldyBNYXRoVXRpbC5RdWF0ZXJuaW9uKCk7XG4gIHRoaXMub3JpZ2luYWxQb3NlQWRqdXN0US5zZXRGcm9tQXhpc0FuZ2xlKG5ldyBNYXRoVXRpbC5WZWN0b3IzKDAsIDAsIDEpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC13aW5kb3cub3JpZW50YXRpb24gKiBNYXRoLlBJIC8gMTgwKTtcblxuICB0aGlzLnNldFNjcmVlblRyYW5zZm9ybV8oKTtcbiAgLy8gQWRqdXN0IHRoaXMgZmlsdGVyIGZvciBiZWluZyBpbiBsYW5kc2NhcGUgbW9kZS5cbiAgaWYgKFV0aWwuaXNMYW5kc2NhcGVNb2RlKCkpIHtcbiAgICB0aGlzLmZpbHRlclRvV29ybGRRLm11bHRpcGx5KHRoaXMuaW52ZXJzZVdvcmxkVG9TY3JlZW5RKTtcbiAgfVxuXG4gIC8vIEtlZXAgdHJhY2sgb2YgYSByZXNldCB0cmFuc2Zvcm0gZm9yIHJlc2V0U2Vuc29yLlxuICB0aGlzLnJlc2V0USA9IG5ldyBNYXRoVXRpbC5RdWF0ZXJuaW9uKCk7XG5cbiAgdGhpcy5pc0ZpcmVmb3hBbmRyb2lkID0gVXRpbC5pc0ZpcmVmb3hBbmRyb2lkKCk7XG4gIHRoaXMuaXNJT1MgPSBVdGlsLmlzSU9TKCk7XG5cbiAgdGhpcy5vcmllbnRhdGlvbk91dF8gPSBuZXcgRmxvYXQzMkFycmF5KDQpO1xufVxuXG5GdXNpb25Qb3NlU2Vuc29yLnByb3RvdHlwZS5nZXRQb3NpdGlvbiA9IGZ1bmN0aW9uKCkge1xuICAvLyBUaGlzIFBvc2VTZW5zb3IgZG9lc24ndCBzdXBwb3J0IHBvc2l0aW9uXG4gIHJldHVybiBudWxsO1xufTtcblxuRnVzaW9uUG9zZVNlbnNvci5wcm90b3R5cGUuZ2V0T3JpZW50YXRpb24gPSBmdW5jdGlvbigpIHtcbiAgLy8gQ29udmVydCBmcm9tIGZpbHRlciBzcGFjZSB0byB0aGUgdGhlIHNhbWUgc3lzdGVtIHVzZWQgYnkgdGhlXG4gIC8vIGRldmljZW9yaWVudGF0aW9uIGV2ZW50LlxuICB2YXIgb3JpZW50YXRpb24gPSB0aGlzLmZpbHRlci5nZXRPcmllbnRhdGlvbigpO1xuXG4gIC8vIFByZWRpY3Qgb3JpZW50YXRpb24uXG4gIHRoaXMucHJlZGljdGVkUSA9IHRoaXMucG9zZVByZWRpY3Rvci5nZXRQcmVkaWN0aW9uKG9yaWVudGF0aW9uLCB0aGlzLmd5cm9zY29wZSwgdGhpcy5wcmV2aW91c1RpbWVzdGFtcFMpO1xuXG4gIC8vIENvbnZlcnQgdG8gVEhSRUUgY29vcmRpbmF0ZSBzeXN0ZW06IC1aIGZvcndhcmQsIFkgdXAsIFggcmlnaHQuXG4gIHZhciBvdXQgPSBuZXcgTWF0aFV0aWwuUXVhdGVybmlvbigpO1xuICBvdXQuY29weSh0aGlzLmZpbHRlclRvV29ybGRRKTtcbiAgb3V0Lm11bHRpcGx5KHRoaXMucmVzZXRRKTtcbiAgaWYgKCF3aW5kb3cuV2ViVlJDb25maWcuVE9VQ0hfUEFOTkVSX0RJU0FCTEVEKSB7XG4gICAgb3V0Lm11bHRpcGx5KHRoaXMudG91Y2hQYW5uZXIuZ2V0T3JpZW50YXRpb24oKSk7XG4gIH1cbiAgb3V0Lm11bHRpcGx5KHRoaXMucHJlZGljdGVkUSk7XG4gIG91dC5tdWx0aXBseSh0aGlzLndvcmxkVG9TY3JlZW5RKTtcblxuICAvLyBIYW5kbGUgdGhlIHlhdy1vbmx5IGNhc2UuXG4gIGlmICh3aW5kb3cuV2ViVlJDb25maWcuWUFXX09OTFkpIHtcbiAgICAvLyBNYWtlIGEgcXVhdGVybmlvbiB0aGF0IG9ubHkgdHVybnMgYXJvdW5kIHRoZSBZLWF4aXMuXG4gICAgb3V0LnggPSAwO1xuICAgIG91dC56ID0gMDtcbiAgICBvdXQubm9ybWFsaXplKCk7XG4gIH1cblxuICB0aGlzLm9yaWVudGF0aW9uT3V0X1swXSA9IG91dC54O1xuICB0aGlzLm9yaWVudGF0aW9uT3V0X1sxXSA9IG91dC55O1xuICB0aGlzLm9yaWVudGF0aW9uT3V0X1syXSA9IG91dC56O1xuICB0aGlzLm9yaWVudGF0aW9uT3V0X1szXSA9IG91dC53O1xuICByZXR1cm4gdGhpcy5vcmllbnRhdGlvbk91dF87XG59O1xuXG5GdXNpb25Qb3NlU2Vuc29yLnByb3RvdHlwZS5yZXNldFBvc2UgPSBmdW5jdGlvbigpIHtcbiAgLy8gUmVkdWNlIHRvIGludmVydGVkIHlhdy1vbmx5LlxuICB0aGlzLnJlc2V0US5jb3B5KHRoaXMuZmlsdGVyLmdldE9yaWVudGF0aW9uKCkpO1xuICB0aGlzLnJlc2V0US54ID0gMDtcbiAgdGhpcy5yZXNldFEueSA9IDA7XG4gIHRoaXMucmVzZXRRLnogKj0gLTE7XG4gIHRoaXMucmVzZXRRLm5vcm1hbGl6ZSgpO1xuXG4gIC8vIFRha2UgaW50byBhY2NvdW50IGV4dHJhIHRyYW5zZm9ybWF0aW9ucyBpbiBsYW5kc2NhcGUgbW9kZS5cbiAgaWYgKFV0aWwuaXNMYW5kc2NhcGVNb2RlKCkpIHtcbiAgICB0aGlzLnJlc2V0US5tdWx0aXBseSh0aGlzLmludmVyc2VXb3JsZFRvU2NyZWVuUSk7XG4gIH1cblxuICAvLyBUYWtlIGludG8gYWNjb3VudCBvcmlnaW5hbCBwb3NlLlxuICB0aGlzLnJlc2V0US5tdWx0aXBseSh0aGlzLm9yaWdpbmFsUG9zZUFkanVzdFEpO1xuXG4gIGlmICghd2luZG93LldlYlZSQ29uZmlnLlRPVUNIX1BBTk5FUl9ESVNBQkxFRCkge1xuICAgIHRoaXMudG91Y2hQYW5uZXIucmVzZXRTZW5zb3IoKTtcbiAgfVxufTtcblxuRnVzaW9uUG9zZVNlbnNvci5wcm90b3R5cGUub25EZXZpY2VNb3Rpb25fID0gZnVuY3Rpb24oZGV2aWNlTW90aW9uKSB7XG4gIHRoaXMudXBkYXRlRGV2aWNlTW90aW9uXyhkZXZpY2VNb3Rpb24pO1xufTtcblxuRnVzaW9uUG9zZVNlbnNvci5wcm90b3R5cGUudXBkYXRlRGV2aWNlTW90aW9uXyA9IGZ1bmN0aW9uKGRldmljZU1vdGlvbikge1xuICB2YXIgYWNjR3Jhdml0eSA9IGRldmljZU1vdGlvbi5hY2NlbGVyYXRpb25JbmNsdWRpbmdHcmF2aXR5O1xuICB2YXIgcm90UmF0ZSA9IGRldmljZU1vdGlvbi5yb3RhdGlvblJhdGU7XG4gIHZhciB0aW1lc3RhbXBTID0gZGV2aWNlTW90aW9uLnRpbWVTdGFtcCAvIDEwMDA7XG5cbiAgLy8gRmlyZWZveCBBbmRyb2lkIHRpbWVTdGFtcCByZXR1cm5zIG9uZSB0aG91c2FuZHRoIG9mIGEgbWlsbGlzZWNvbmQuXG4gIGlmICh0aGlzLmlzRmlyZWZveEFuZHJvaWQpIHtcbiAgICB0aW1lc3RhbXBTIC89IDEwMDA7XG4gIH1cblxuICB2YXIgZGVsdGFTID0gdGltZXN0YW1wUyAtIHRoaXMucHJldmlvdXNUaW1lc3RhbXBTO1xuICBpZiAoZGVsdGFTIDw9IFV0aWwuTUlOX1RJTUVTVEVQIHx8IGRlbHRhUyA+IFV0aWwuTUFYX1RJTUVTVEVQKSB7XG4gICAgY29uc29sZS53YXJuKCdJbnZhbGlkIHRpbWVzdGFtcHMgZGV0ZWN0ZWQuIFRpbWUgc3RlcCBiZXR3ZWVuIHN1Y2Nlc3NpdmUgJyArXG4gICAgICAgICAgICAgICAgICdneXJvc2NvcGUgc2Vuc29yIHNhbXBsZXMgaXMgdmVyeSBzbWFsbCBvciBub3QgbW9ub3RvbmljJyk7XG4gICAgdGhpcy5wcmV2aW91c1RpbWVzdGFtcFMgPSB0aW1lc3RhbXBTO1xuICAgIHJldHVybjtcbiAgfVxuICB0aGlzLmFjY2VsZXJvbWV0ZXIuc2V0KC1hY2NHcmF2aXR5LngsIC1hY2NHcmF2aXR5LnksIC1hY2NHcmF2aXR5LnopO1xuICB0aGlzLmd5cm9zY29wZS5zZXQocm90UmF0ZS5hbHBoYSwgcm90UmF0ZS5iZXRhLCByb3RSYXRlLmdhbW1hKTtcblxuICAvLyBXaXRoIGlPUyBhbmQgRmlyZWZveCBBbmRyb2lkLCByb3RhdGlvblJhdGUgaXMgcmVwb3J0ZWQgaW4gZGVncmVlcyxcbiAgLy8gc28gd2UgZmlyc3QgY29udmVydCB0byByYWRpYW5zLlxuICBpZiAodGhpcy5pc0lPUyB8fCB0aGlzLmlzRmlyZWZveEFuZHJvaWQpIHtcbiAgICB0aGlzLmd5cm9zY29wZS5tdWx0aXBseVNjYWxhcihNYXRoLlBJIC8gMTgwKTtcbiAgfVxuXG4gIHRoaXMuZmlsdGVyLmFkZEFjY2VsTWVhc3VyZW1lbnQodGhpcy5hY2NlbGVyb21ldGVyLCB0aW1lc3RhbXBTKTtcbiAgdGhpcy5maWx0ZXIuYWRkR3lyb01lYXN1cmVtZW50KHRoaXMuZ3lyb3Njb3BlLCB0aW1lc3RhbXBTKTtcblxuICB0aGlzLnByZXZpb3VzVGltZXN0YW1wUyA9IHRpbWVzdGFtcFM7XG59O1xuXG5GdXNpb25Qb3NlU2Vuc29yLnByb3RvdHlwZS5vbk9yaWVudGF0aW9uQ2hhbmdlXyA9IGZ1bmN0aW9uKHNjcmVlbk9yaWVudGF0aW9uKSB7XG4gIHRoaXMuc2V0U2NyZWVuVHJhbnNmb3JtXygpO1xufTtcblxuLyoqXG4gKiBUaGlzIGlzIG9ubHkgbmVlZGVkIGlmIHdlIGFyZSBpbiBhbiBjcm9zcyBvcmlnaW4gaWZyYW1lIG9uIGlPUyB0byB3b3JrIGFyb3VuZFxuICogdGhpcyBpc3N1ZTogaHR0cHM6Ly9idWdzLndlYmtpdC5vcmcvc2hvd19idWcuY2dpP2lkPTE1MjI5OS5cbiAqL1xuRnVzaW9uUG9zZVNlbnNvci5wcm90b3R5cGUub25NZXNzYWdlXyA9IGZ1bmN0aW9uKGV2ZW50KSB7XG4gIHZhciBtZXNzYWdlID0gZXZlbnQuZGF0YTtcblxuICAvLyBJZiB0aGVyZSdzIG5vIG1lc3NhZ2UgdHlwZSwgaWdub3JlIGl0LlxuICBpZiAoIW1lc3NhZ2UgfHwgIW1lc3NhZ2UudHlwZSkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIC8vIElnbm9yZSBhbGwgbWVzc2FnZXMgdGhhdCBhcmVuJ3QgZGV2aWNlbW90aW9uLlxuICB2YXIgdHlwZSA9IG1lc3NhZ2UudHlwZS50b0xvd2VyQ2FzZSgpO1xuICBpZiAodHlwZSAhPT0gJ2RldmljZW1vdGlvbicpIHtcbiAgICByZXR1cm47XG4gIH1cblxuICAvLyBVcGRhdGUgZGV2aWNlIG1vdGlvbi5cbiAgdGhpcy51cGRhdGVEZXZpY2VNb3Rpb25fKG1lc3NhZ2UuZGV2aWNlTW90aW9uRXZlbnQpO1xufTtcblxuRnVzaW9uUG9zZVNlbnNvci5wcm90b3R5cGUuc2V0U2NyZWVuVHJhbnNmb3JtXyA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLndvcmxkVG9TY3JlZW5RLnNldCgwLCAwLCAwLCAxKTtcbiAgc3dpdGNoICh3aW5kb3cub3JpZW50YXRpb24pIHtcbiAgICBjYXNlIDA6XG4gICAgICBicmVhaztcbiAgICBjYXNlIDkwOlxuICAgICAgdGhpcy53b3JsZFRvU2NyZWVuUS5zZXRGcm9tQXhpc0FuZ2xlKG5ldyBNYXRoVXRpbC5WZWN0b3IzKDAsIDAsIDEpLCAtTWF0aC5QSSAvIDIpO1xuICAgICAgYnJlYWs7XG4gICAgY2FzZSAtOTA6XG4gICAgICB0aGlzLndvcmxkVG9TY3JlZW5RLnNldEZyb21BeGlzQW5nbGUobmV3IE1hdGhVdGlsLlZlY3RvcjMoMCwgMCwgMSksIE1hdGguUEkgLyAyKTtcbiAgICAgIGJyZWFrO1xuICAgIGNhc2UgMTgwOlxuICAgICAgLy8gVE9ETy5cbiAgICAgIGJyZWFrO1xuICB9XG4gIHRoaXMuaW52ZXJzZVdvcmxkVG9TY3JlZW5RLmNvcHkodGhpcy53b3JsZFRvU2NyZWVuUSk7XG4gIHRoaXMuaW52ZXJzZVdvcmxkVG9TY3JlZW5RLmludmVyc2UoKTtcbn07XG5cbkZ1c2lvblBvc2VTZW5zb3IucHJvdG90eXBlLnN0YXJ0ID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMub25EZXZpY2VNb3Rpb25DYWxsYmFja18gPSB0aGlzLm9uRGV2aWNlTW90aW9uXy5iaW5kKHRoaXMpO1xuICB0aGlzLm9uT3JpZW50YXRpb25DaGFuZ2VDYWxsYmFja18gPSB0aGlzLm9uT3JpZW50YXRpb25DaGFuZ2VfLmJpbmQodGhpcyk7XG4gIHRoaXMub25NZXNzYWdlQ2FsbGJhY2tfID0gdGhpcy5vbk1lc3NhZ2VfLmJpbmQodGhpcyk7XG5cbiAgLy8gT25seSBsaXN0ZW4gZm9yIHBvc3RNZXNzYWdlcyBpZiB3ZSdyZSBpbiBhbiBpT1MgYW5kIGVtYmVkZGVkIGluc2lkZSBhIGNyb3NzXG4gIC8vIGRvbWFpbiBJRnJhbWUuIEluIHRoaXMgY2FzZSwgdGhlIHBvbHlmaWxsIGNhbiBzdGlsbCB3b3JrIGlmIHRoZSBjb250YWluaW5nXG4gIC8vIHBhZ2Ugc2VuZHMgc3ludGhldGljIGRldmljZW1vdGlvbiBldmVudHMuIEZvciBhbiBleGFtcGxlIG9mIHRoaXMsIHNlZVxuICAvLyBpZnJhbWUtbWVzc2FnZS1zZW5kZXIuanMgaW4gVlIgVmlldzogaHR0cHM6Ly9nb28uZ2wvWER0dkZaXG4gIGlmIChVdGlsLmlzSU9TKCkgJiYgVXRpbC5pc0luc2lkZUNyb3NzRG9tYWluSUZyYW1lKCkpIHtcbiAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIHRoaXMub25NZXNzYWdlQ2FsbGJhY2tfKTtcbiAgfVxuICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignb3JpZW50YXRpb25jaGFuZ2UnLCB0aGlzLm9uT3JpZW50YXRpb25DaGFuZ2VDYWxsYmFja18pO1xuICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignZGV2aWNlbW90aW9uJywgdGhpcy5vbkRldmljZU1vdGlvbkNhbGxiYWNrXyk7XG59O1xuXG5GdXNpb25Qb3NlU2Vuc29yLnByb3RvdHlwZS5zdG9wID0gZnVuY3Rpb24oKSB7XG4gIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCdkZXZpY2Vtb3Rpb24nLCB0aGlzLm9uRGV2aWNlTW90aW9uQ2FsbGJhY2tfKTtcbiAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ29yaWVudGF0aW9uY2hhbmdlJywgdGhpcy5vbk9yaWVudGF0aW9uQ2hhbmdlQ2FsbGJhY2tfKTtcbiAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ21lc3NhZ2UnLCB0aGlzLm9uTWVzc2FnZUNhbGxiYWNrXyk7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IEZ1c2lvblBvc2VTZW5zb3I7XG4iLCIvKlxuICogQ29weXJpZ2h0IDIwMTUgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqL1xudmFyIE1hdGhVdGlsID0gcmVxdWlyZSgnLi4vbWF0aC11dGlsJyk7XG52YXIgVXRpbCA9IHJlcXVpcmUoJy4uL3V0aWwnKTtcblxuLyoqXG4gKiBHaXZlbiBhbiBvcmllbnRhdGlvbiBhbmQgdGhlIGd5cm9zY29wZSBkYXRhLCBwcmVkaWN0cyB0aGUgZnV0dXJlIG9yaWVudGF0aW9uXG4gKiBvZiB0aGUgaGVhZC4gVGhpcyBtYWtlcyByZW5kZXJpbmcgYXBwZWFyIGZhc3Rlci5cbiAqXG4gKiBBbHNvIHNlZTogaHR0cDovL21zbC5jcy51aXVjLmVkdS9+bGF2YWxsZS9wYXBlcnMvTGF2WWVyS2F0QW50MTQucGRmXG4gKlxuICogQHBhcmFtIHtOdW1iZXJ9IHByZWRpY3Rpb25UaW1lUyB0aW1lIGZyb20gaGVhZCBtb3ZlbWVudCB0byB0aGUgYXBwZWFyYW5jZSBvZlxuICogdGhlIGNvcnJlc3BvbmRpbmcgaW1hZ2UuXG4gKi9cbmZ1bmN0aW9uIFBvc2VQcmVkaWN0b3IocHJlZGljdGlvblRpbWVTKSB7XG4gIHRoaXMucHJlZGljdGlvblRpbWVTID0gcHJlZGljdGlvblRpbWVTO1xuXG4gIC8vIFRoZSBxdWF0ZXJuaW9uIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHByZXZpb3VzIHN0YXRlLlxuICB0aGlzLnByZXZpb3VzUSA9IG5ldyBNYXRoVXRpbC5RdWF0ZXJuaW9uKCk7XG4gIC8vIFByZXZpb3VzIHRpbWUgYSBwcmVkaWN0aW9uIG9jY3VycmVkLlxuICB0aGlzLnByZXZpb3VzVGltZXN0YW1wUyA9IG51bGw7XG5cbiAgLy8gVGhlIGRlbHRhIHF1YXRlcm5pb24gdGhhdCBhZGp1c3RzIHRoZSBjdXJyZW50IHBvc2UuXG4gIHRoaXMuZGVsdGFRID0gbmV3IE1hdGhVdGlsLlF1YXRlcm5pb24oKTtcbiAgLy8gVGhlIG91dHB1dCBxdWF0ZXJuaW9uLlxuICB0aGlzLm91dFEgPSBuZXcgTWF0aFV0aWwuUXVhdGVybmlvbigpO1xufVxuXG5Qb3NlUHJlZGljdG9yLnByb3RvdHlwZS5nZXRQcmVkaWN0aW9uID0gZnVuY3Rpb24oY3VycmVudFEsIGd5cm8sIHRpbWVzdGFtcFMpIHtcbiAgaWYgKCF0aGlzLnByZXZpb3VzVGltZXN0YW1wUykge1xuICAgIHRoaXMucHJldmlvdXNRLmNvcHkoY3VycmVudFEpO1xuICAgIHRoaXMucHJldmlvdXNUaW1lc3RhbXBTID0gdGltZXN0YW1wUztcbiAgICByZXR1cm4gY3VycmVudFE7XG4gIH1cblxuICAvLyBDYWxjdWxhdGUgYXhpcyBhbmQgYW5nbGUgYmFzZWQgb24gZ3lyb3Njb3BlIHJvdGF0aW9uIHJhdGUgZGF0YS5cbiAgdmFyIGF4aXMgPSBuZXcgTWF0aFV0aWwuVmVjdG9yMygpO1xuICBheGlzLmNvcHkoZ3lybyk7XG4gIGF4aXMubm9ybWFsaXplKCk7XG5cbiAgdmFyIGFuZ3VsYXJTcGVlZCA9IGd5cm8ubGVuZ3RoKCk7XG5cbiAgLy8gSWYgd2UncmUgcm90YXRpbmcgc2xvd2x5LCBkb24ndCBkbyBwcmVkaWN0aW9uLlxuICBpZiAoYW5ndWxhclNwZWVkIDwgTWF0aFV0aWwuZGVnVG9SYWQgKiAyMCkge1xuICAgIGlmIChVdGlsLmlzRGVidWcoKSkge1xuICAgICAgY29uc29sZS5sb2coJ01vdmluZyBzbG93bHksIGF0ICVzIGRlZy9zOiBubyBwcmVkaWN0aW9uJyxcbiAgICAgICAgICAgICAgICAgIChNYXRoVXRpbC5yYWRUb0RlZyAqIGFuZ3VsYXJTcGVlZCkudG9GaXhlZCgxKSk7XG4gICAgfVxuICAgIHRoaXMub3V0US5jb3B5KGN1cnJlbnRRKTtcbiAgICB0aGlzLnByZXZpb3VzUS5jb3B5KGN1cnJlbnRRKTtcbiAgICByZXR1cm4gdGhpcy5vdXRRO1xuICB9XG5cbiAgLy8gR2V0IHRoZSBwcmVkaWN0ZWQgYW5nbGUgYmFzZWQgb24gdGhlIHRpbWUgZGVsdGEgYW5kIGxhdGVuY3kuXG4gIHZhciBkZWx0YVQgPSB0aW1lc3RhbXBTIC0gdGhpcy5wcmV2aW91c1RpbWVzdGFtcFM7XG4gIHZhciBwcmVkaWN0QW5nbGUgPSBhbmd1bGFyU3BlZWQgKiB0aGlzLnByZWRpY3Rpb25UaW1lUztcblxuICB0aGlzLmRlbHRhUS5zZXRGcm9tQXhpc0FuZ2xlKGF4aXMsIHByZWRpY3RBbmdsZSk7XG4gIHRoaXMub3V0US5jb3B5KHRoaXMucHJldmlvdXNRKTtcbiAgdGhpcy5vdXRRLm11bHRpcGx5KHRoaXMuZGVsdGFRKTtcblxuICB0aGlzLnByZXZpb3VzUS5jb3B5KGN1cnJlbnRRKTtcbiAgdGhpcy5wcmV2aW91c1RpbWVzdGFtcFMgPSB0aW1lc3RhbXBTO1xuXG4gIHJldHVybiB0aGlzLm91dFE7XG59O1xuXG5cbm1vZHVsZS5leHBvcnRzID0gUG9zZVByZWRpY3RvcjtcbiIsImZ1bmN0aW9uIFNlbnNvclNhbXBsZShzYW1wbGUsIHRpbWVzdGFtcFMpIHtcbiAgdGhpcy5zZXQoc2FtcGxlLCB0aW1lc3RhbXBTKTtcbn07XG5cblNlbnNvclNhbXBsZS5wcm90b3R5cGUuc2V0ID0gZnVuY3Rpb24oc2FtcGxlLCB0aW1lc3RhbXBTKSB7XG4gIHRoaXMuc2FtcGxlID0gc2FtcGxlO1xuICB0aGlzLnRpbWVzdGFtcFMgPSB0aW1lc3RhbXBTO1xufTtcblxuU2Vuc29yU2FtcGxlLnByb3RvdHlwZS5jb3B5ID0gZnVuY3Rpb24oc2Vuc29yU2FtcGxlKSB7XG4gIHRoaXMuc2V0KHNlbnNvclNhbXBsZS5zYW1wbGUsIHNlbnNvclNhbXBsZS50aW1lc3RhbXBTKTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gU2Vuc29yU2FtcGxlO1xuIiwiLypcbiAqIENvcHlyaWdodCAyMDE1IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKi9cbnZhciBNYXRoVXRpbCA9IHJlcXVpcmUoJy4vbWF0aC11dGlsLmpzJyk7XG52YXIgVXRpbCA9IHJlcXVpcmUoJy4vdXRpbC5qcycpO1xuXG52YXIgUk9UQVRFX1NQRUVEID0gMC41O1xuLyoqXG4gKiBQcm92aWRlcyBhIHF1YXRlcm5pb24gcmVzcG9uc2libGUgZm9yIHByZS1wYW5uaW5nIHRoZSBzY2VuZSBiZWZvcmUgZnVydGhlclxuICogdHJhbnNmb3JtYXRpb25zIGR1ZSB0byBkZXZpY2Ugc2Vuc29ycy5cbiAqL1xuZnVuY3Rpb24gVG91Y2hQYW5uZXIoKSB7XG4gIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCd0b3VjaHN0YXJ0JywgdGhpcy5vblRvdWNoU3RhcnRfLmJpbmQodGhpcykpO1xuICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigndG91Y2htb3ZlJywgdGhpcy5vblRvdWNoTW92ZV8uYmluZCh0aGlzKSk7XG4gIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCd0b3VjaGVuZCcsIHRoaXMub25Ub3VjaEVuZF8uYmluZCh0aGlzKSk7XG5cbiAgdGhpcy5pc1RvdWNoaW5nID0gZmFsc2U7XG4gIHRoaXMucm90YXRlU3RhcnQgPSBuZXcgTWF0aFV0aWwuVmVjdG9yMigpO1xuICB0aGlzLnJvdGF0ZUVuZCA9IG5ldyBNYXRoVXRpbC5WZWN0b3IyKCk7XG4gIHRoaXMucm90YXRlRGVsdGEgPSBuZXcgTWF0aFV0aWwuVmVjdG9yMigpO1xuXG4gIHRoaXMudGhldGEgPSAwO1xuICB0aGlzLm9yaWVudGF0aW9uID0gbmV3IE1hdGhVdGlsLlF1YXRlcm5pb24oKTtcbn1cblxuVG91Y2hQYW5uZXIucHJvdG90eXBlLmdldE9yaWVudGF0aW9uID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMub3JpZW50YXRpb24uc2V0RnJvbUV1bGVyWFlaKDAsIDAsIHRoaXMudGhldGEpO1xuICByZXR1cm4gdGhpcy5vcmllbnRhdGlvbjtcbn07XG5cblRvdWNoUGFubmVyLnByb3RvdHlwZS5yZXNldFNlbnNvciA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLnRoZXRhID0gMDtcbn07XG5cblRvdWNoUGFubmVyLnByb3RvdHlwZS5vblRvdWNoU3RhcnRfID0gZnVuY3Rpb24oZSkge1xuICAvLyBPbmx5IHJlc3BvbmQgaWYgdGhlcmUgaXMgZXhhY3RseSBvbmUgdG91Y2guXG4gIC8vIE5vdGUgdGhhdCB0aGUgRGF5ZHJlYW0gY29udHJvbGxlciBwYXNzZXMgaW4gYSBgdG91Y2hzdGFydGAgZXZlbnQgd2l0aFxuICAvLyBubyBgdG91Y2hlc2AgcHJvcGVydHksIHNvIHdlIG11c3QgY2hlY2sgZm9yIHRoYXQgY2FzZSB0b28uXG4gIGlmICghZS50b3VjaGVzIHx8IGUudG91Y2hlcy5sZW5ndGggIT0gMSkge1xuICAgIHJldHVybjtcbiAgfVxuICB0aGlzLnJvdGF0ZVN0YXJ0LnNldChlLnRvdWNoZXNbMF0ucGFnZVgsIGUudG91Y2hlc1swXS5wYWdlWSk7XG4gIHRoaXMuaXNUb3VjaGluZyA9IHRydWU7XG59O1xuXG5Ub3VjaFBhbm5lci5wcm90b3R5cGUub25Ub3VjaE1vdmVfID0gZnVuY3Rpb24oZSkge1xuICBpZiAoIXRoaXMuaXNUb3VjaGluZykge1xuICAgIHJldHVybjtcbiAgfVxuICB0aGlzLnJvdGF0ZUVuZC5zZXQoZS50b3VjaGVzWzBdLnBhZ2VYLCBlLnRvdWNoZXNbMF0ucGFnZVkpO1xuICB0aGlzLnJvdGF0ZURlbHRhLnN1YlZlY3RvcnModGhpcy5yb3RhdGVFbmQsIHRoaXMucm90YXRlU3RhcnQpO1xuICB0aGlzLnJvdGF0ZVN0YXJ0LmNvcHkodGhpcy5yb3RhdGVFbmQpO1xuXG4gIC8vIE9uIGlPUywgZGlyZWN0aW9uIGlzIGludmVydGVkLlxuICBpZiAoVXRpbC5pc0lPUygpKSB7XG4gICAgdGhpcy5yb3RhdGVEZWx0YS54ICo9IC0xO1xuICB9XG5cbiAgdmFyIGVsZW1lbnQgPSBkb2N1bWVudC5ib2R5O1xuICB0aGlzLnRoZXRhICs9IDIgKiBNYXRoLlBJICogdGhpcy5yb3RhdGVEZWx0YS54IC8gZWxlbWVudC5jbGllbnRXaWR0aCAqIFJPVEFURV9TUEVFRDtcbn07XG5cblRvdWNoUGFubmVyLnByb3RvdHlwZS5vblRvdWNoRW5kXyA9IGZ1bmN0aW9uKGUpIHtcbiAgdGhpcy5pc1RvdWNoaW5nID0gZmFsc2U7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFRvdWNoUGFubmVyO1xuIiwiLypcbiAqIENvcHlyaWdodCAyMDE1IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKi9cblxudmFyIFV0aWwgPSB3aW5kb3cuVXRpbCB8fCB7fTtcblxuVXRpbC5NSU5fVElNRVNURVAgPSAwLjAwMTtcblV0aWwuTUFYX1RJTUVTVEVQID0gMTtcblxuVXRpbC5iYXNlNjQgPSBmdW5jdGlvbihtaW1lVHlwZSwgYmFzZTY0KSB7XG4gIHJldHVybiAnZGF0YTonICsgbWltZVR5cGUgKyAnO2Jhc2U2NCwnICsgYmFzZTY0O1xufTtcblxuVXRpbC5jbGFtcCA9IGZ1bmN0aW9uKHZhbHVlLCBtaW4sIG1heCkge1xuICByZXR1cm4gTWF0aC5taW4oTWF0aC5tYXgobWluLCB2YWx1ZSksIG1heCk7XG59O1xuXG5VdGlsLmxlcnAgPSBmdW5jdGlvbihhLCBiLCB0KSB7XG4gIHJldHVybiBhICsgKChiIC0gYSkgKiB0KTtcbn07XG5cbi8qKlxuICogTGlnaHQgcG9seWZpbGwgZm9yIGBQcm9taXNlLnJhY2VgLiBSZXR1cm5zXG4gKiBhIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBmaXJzdCBwcm9taXNlXG4gKiBwcm92aWRlZCByZXNvbHZlcy5cbiAqXG4gKiBAcGFyYW0ge0FycmF5PFByb21pc2U+fSBwcm9taXNlc1xuICovXG5VdGlsLnJhY2UgPSBmdW5jdGlvbihwcm9taXNlcykge1xuICBpZiAoUHJvbWlzZS5yYWNlKSB7XG4gICAgcmV0dXJuIFByb21pc2UucmFjZShwcm9taXNlcyk7XG4gIH1cblxuICByZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24gKHJlc29sdmUsIHJlamVjdCkge1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgcHJvbWlzZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIHByb21pc2VzW2ldLnRoZW4ocmVzb2x2ZSwgcmVqZWN0KTtcbiAgICB9XG4gIH0pO1xufTtcblxuVXRpbC5pc0lPUyA9IChmdW5jdGlvbigpIHtcbiAgdmFyIGlzSU9TID0gL2lQYWR8aVBob25lfGlQb2QvLnRlc3QobmF2aWdhdG9yLnBsYXRmb3JtKTtcbiAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiBpc0lPUztcbiAgfTtcbn0pKCk7XG5cblV0aWwuaXNXZWJWaWV3QW5kcm9pZCA9IChmdW5jdGlvbigpIHtcbiAgdmFyIGlzV2ViVmlld0FuZHJvaWQgPSBuYXZpZ2F0b3IudXNlckFnZW50LmluZGV4T2YoJ1ZlcnNpb24nKSAhPT0gLTEgJiZcbiAgICAgIG5hdmlnYXRvci51c2VyQWdlbnQuaW5kZXhPZignQW5kcm9pZCcpICE9PSAtMSAmJlxuICAgICAgbmF2aWdhdG9yLnVzZXJBZ2VudC5pbmRleE9mKCdDaHJvbWUnKSAhPT0gLTE7XG4gIHJldHVybiBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gaXNXZWJWaWV3QW5kcm9pZDtcbiAgfTtcbn0pKCk7XG5cblV0aWwuaXNTYWZhcmkgPSAoZnVuY3Rpb24oKSB7XG4gIHZhciBpc1NhZmFyaSA9IC9eKCg/IWNocm9tZXxhbmRyb2lkKS4pKnNhZmFyaS9pLnRlc3QobmF2aWdhdG9yLnVzZXJBZ2VudCk7XG4gIHJldHVybiBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gaXNTYWZhcmk7XG4gIH07XG59KSgpO1xuXG5VdGlsLmlzRmlyZWZveEFuZHJvaWQgPSAoZnVuY3Rpb24oKSB7XG4gIHZhciBpc0ZpcmVmb3hBbmRyb2lkID0gbmF2aWdhdG9yLnVzZXJBZ2VudC5pbmRleE9mKCdGaXJlZm94JykgIT09IC0xICYmXG4gICAgICBuYXZpZ2F0b3IudXNlckFnZW50LmluZGV4T2YoJ0FuZHJvaWQnKSAhPT0gLTE7XG4gIHJldHVybiBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gaXNGaXJlZm94QW5kcm9pZDtcbiAgfTtcbn0pKCk7XG5cblV0aWwuaXNMYW5kc2NhcGVNb2RlID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiAod2luZG93Lm9yaWVudGF0aW9uID09IDkwIHx8IHdpbmRvdy5vcmllbnRhdGlvbiA9PSAtOTApO1xufTtcblxuLy8gSGVscGVyIG1ldGhvZCB0byB2YWxpZGF0ZSB0aGUgdGltZSBzdGVwcyBvZiBzZW5zb3IgdGltZXN0YW1wcy5cblV0aWwuaXNUaW1lc3RhbXBEZWx0YVZhbGlkID0gZnVuY3Rpb24odGltZXN0YW1wRGVsdGFTKSB7XG4gIGlmIChpc05hTih0aW1lc3RhbXBEZWx0YVMpKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIGlmICh0aW1lc3RhbXBEZWx0YVMgPD0gVXRpbC5NSU5fVElNRVNURVApIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgaWYgKHRpbWVzdGFtcERlbHRhUyA+IFV0aWwuTUFYX1RJTUVTVEVQKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIHJldHVybiB0cnVlO1xufTtcblxuVXRpbC5nZXRTY3JlZW5XaWR0aCA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gTWF0aC5tYXgod2luZG93LnNjcmVlbi53aWR0aCwgd2luZG93LnNjcmVlbi5oZWlnaHQpICpcbiAgICAgIHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvO1xufTtcblxuVXRpbC5nZXRTY3JlZW5IZWlnaHQgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIE1hdGgubWluKHdpbmRvdy5zY3JlZW4ud2lkdGgsIHdpbmRvdy5zY3JlZW4uaGVpZ2h0KSAqXG4gICAgICB3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbztcbn07XG5cblV0aWwucmVxdWVzdEZ1bGxzY3JlZW4gPSBmdW5jdGlvbihlbGVtZW50KSB7XG4gIGlmIChVdGlsLmlzV2ViVmlld0FuZHJvaWQoKSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIGlmIChlbGVtZW50LnJlcXVlc3RGdWxsc2NyZWVuKSB7XG4gICAgZWxlbWVudC5yZXF1ZXN0RnVsbHNjcmVlbigpO1xuICB9IGVsc2UgaWYgKGVsZW1lbnQud2Via2l0UmVxdWVzdEZ1bGxzY3JlZW4pIHtcbiAgICBlbGVtZW50LndlYmtpdFJlcXVlc3RGdWxsc2NyZWVuKCk7XG4gIH0gZWxzZSBpZiAoZWxlbWVudC5tb3pSZXF1ZXN0RnVsbFNjcmVlbikge1xuICAgIGVsZW1lbnQubW96UmVxdWVzdEZ1bGxTY3JlZW4oKTtcbiAgfSBlbHNlIGlmIChlbGVtZW50Lm1zUmVxdWVzdEZ1bGxzY3JlZW4pIHtcbiAgICBlbGVtZW50Lm1zUmVxdWVzdEZ1bGxzY3JlZW4oKTtcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICByZXR1cm4gdHJ1ZTtcbn07XG5cblV0aWwuZXhpdEZ1bGxzY3JlZW4gPSBmdW5jdGlvbigpIHtcbiAgaWYgKGRvY3VtZW50LmV4aXRGdWxsc2NyZWVuKSB7XG4gICAgZG9jdW1lbnQuZXhpdEZ1bGxzY3JlZW4oKTtcbiAgfSBlbHNlIGlmIChkb2N1bWVudC53ZWJraXRFeGl0RnVsbHNjcmVlbikge1xuICAgIGRvY3VtZW50LndlYmtpdEV4aXRGdWxsc2NyZWVuKCk7XG4gIH0gZWxzZSBpZiAoZG9jdW1lbnQubW96Q2FuY2VsRnVsbFNjcmVlbikge1xuICAgIGRvY3VtZW50Lm1vekNhbmNlbEZ1bGxTY3JlZW4oKTtcbiAgfSBlbHNlIGlmIChkb2N1bWVudC5tc0V4aXRGdWxsc2NyZWVuKSB7XG4gICAgZG9jdW1lbnQubXNFeGl0RnVsbHNjcmVlbigpO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIHJldHVybiB0cnVlO1xufTtcblxuVXRpbC5nZXRGdWxsc2NyZWVuRWxlbWVudCA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gZG9jdW1lbnQuZnVsbHNjcmVlbkVsZW1lbnQgfHxcbiAgICAgIGRvY3VtZW50LndlYmtpdEZ1bGxzY3JlZW5FbGVtZW50IHx8XG4gICAgICBkb2N1bWVudC5tb3pGdWxsU2NyZWVuRWxlbWVudCB8fFxuICAgICAgZG9jdW1lbnQubXNGdWxsc2NyZWVuRWxlbWVudDtcbn07XG5cblV0aWwubGlua1Byb2dyYW0gPSBmdW5jdGlvbihnbCwgdmVydGV4U291cmNlLCBmcmFnbWVudFNvdXJjZSwgYXR0cmliTG9jYXRpb25NYXApIHtcbiAgLy8gTm8gZXJyb3IgY2hlY2tpbmcgZm9yIGJyZXZpdHkuXG4gIHZhciB2ZXJ0ZXhTaGFkZXIgPSBnbC5jcmVhdGVTaGFkZXIoZ2wuVkVSVEVYX1NIQURFUik7XG4gIGdsLnNoYWRlclNvdXJjZSh2ZXJ0ZXhTaGFkZXIsIHZlcnRleFNvdXJjZSk7XG4gIGdsLmNvbXBpbGVTaGFkZXIodmVydGV4U2hhZGVyKTtcblxuICB2YXIgZnJhZ21lbnRTaGFkZXIgPSBnbC5jcmVhdGVTaGFkZXIoZ2wuRlJBR01FTlRfU0hBREVSKTtcbiAgZ2wuc2hhZGVyU291cmNlKGZyYWdtZW50U2hhZGVyLCBmcmFnbWVudFNvdXJjZSk7XG4gIGdsLmNvbXBpbGVTaGFkZXIoZnJhZ21lbnRTaGFkZXIpO1xuXG4gIHZhciBwcm9ncmFtID0gZ2wuY3JlYXRlUHJvZ3JhbSgpO1xuICBnbC5hdHRhY2hTaGFkZXIocHJvZ3JhbSwgdmVydGV4U2hhZGVyKTtcbiAgZ2wuYXR0YWNoU2hhZGVyKHByb2dyYW0sIGZyYWdtZW50U2hhZGVyKTtcblxuICBmb3IgKHZhciBhdHRyaWJOYW1lIGluIGF0dHJpYkxvY2F0aW9uTWFwKVxuICAgIGdsLmJpbmRBdHRyaWJMb2NhdGlvbihwcm9ncmFtLCBhdHRyaWJMb2NhdGlvbk1hcFthdHRyaWJOYW1lXSwgYXR0cmliTmFtZSk7XG5cbiAgZ2wubGlua1Byb2dyYW0ocHJvZ3JhbSk7XG5cbiAgZ2wuZGVsZXRlU2hhZGVyKHZlcnRleFNoYWRlcik7XG4gIGdsLmRlbGV0ZVNoYWRlcihmcmFnbWVudFNoYWRlcik7XG5cbiAgcmV0dXJuIHByb2dyYW07XG59O1xuXG5VdGlsLmdldFByb2dyYW1Vbmlmb3JtcyA9IGZ1bmN0aW9uKGdsLCBwcm9ncmFtKSB7XG4gIHZhciB1bmlmb3JtcyA9IHt9O1xuICB2YXIgdW5pZm9ybUNvdW50ID0gZ2wuZ2V0UHJvZ3JhbVBhcmFtZXRlcihwcm9ncmFtLCBnbC5BQ1RJVkVfVU5JRk9STVMpO1xuICB2YXIgdW5pZm9ybU5hbWUgPSAnJztcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCB1bmlmb3JtQ291bnQ7IGkrKykge1xuICAgIHZhciB1bmlmb3JtSW5mbyA9IGdsLmdldEFjdGl2ZVVuaWZvcm0ocHJvZ3JhbSwgaSk7XG4gICAgdW5pZm9ybU5hbWUgPSB1bmlmb3JtSW5mby5uYW1lLnJlcGxhY2UoJ1swXScsICcnKTtcbiAgICB1bmlmb3Jtc1t1bmlmb3JtTmFtZV0gPSBnbC5nZXRVbmlmb3JtTG9jYXRpb24ocHJvZ3JhbSwgdW5pZm9ybU5hbWUpO1xuICB9XG4gIHJldHVybiB1bmlmb3Jtcztcbn07XG5cblV0aWwub3J0aG9NYXRyaXggPSBmdW5jdGlvbiAob3V0LCBsZWZ0LCByaWdodCwgYm90dG9tLCB0b3AsIG5lYXIsIGZhcikge1xuICB2YXIgbHIgPSAxIC8gKGxlZnQgLSByaWdodCksXG4gICAgICBidCA9IDEgLyAoYm90dG9tIC0gdG9wKSxcbiAgICAgIG5mID0gMSAvIChuZWFyIC0gZmFyKTtcbiAgb3V0WzBdID0gLTIgKiBscjtcbiAgb3V0WzFdID0gMDtcbiAgb3V0WzJdID0gMDtcbiAgb3V0WzNdID0gMDtcbiAgb3V0WzRdID0gMDtcbiAgb3V0WzVdID0gLTIgKiBidDtcbiAgb3V0WzZdID0gMDtcbiAgb3V0WzddID0gMDtcbiAgb3V0WzhdID0gMDtcbiAgb3V0WzldID0gMDtcbiAgb3V0WzEwXSA9IDIgKiBuZjtcbiAgb3V0WzExXSA9IDA7XG4gIG91dFsxMl0gPSAobGVmdCArIHJpZ2h0KSAqIGxyO1xuICBvdXRbMTNdID0gKHRvcCArIGJvdHRvbSkgKiBidDtcbiAgb3V0WzE0XSA9IChmYXIgKyBuZWFyKSAqIG5mO1xuICBvdXRbMTVdID0gMTtcbiAgcmV0dXJuIG91dDtcbn07XG5cblV0aWwuY29weUFycmF5ID0gZnVuY3Rpb24gKHNvdXJjZSwgZGVzdCkge1xuICBmb3IgKHZhciBpID0gMCwgbiA9IHNvdXJjZS5sZW5ndGg7IGkgPCBuOyBpKyspIHtcbiAgICBkZXN0W2ldID0gc291cmNlW2ldO1xuICB9XG59O1xuXG5VdGlsLmlzTW9iaWxlID0gZnVuY3Rpb24oKSB7XG4gIHZhciBjaGVjayA9IGZhbHNlO1xuICAoZnVuY3Rpb24oYSl7aWYoLyhhbmRyb2lkfGJiXFxkK3xtZWVnbykuK21vYmlsZXxhdmFudGdvfGJhZGFcXC98YmxhY2tiZXJyeXxibGF6ZXJ8Y29tcGFsfGVsYWluZXxmZW5uZWN8aGlwdG9wfGllbW9iaWxlfGlwKGhvbmV8b2QpfGlyaXN8a2luZGxlfGxnZSB8bWFlbW98bWlkcHxtbXB8bW9iaWxlLitmaXJlZm94fG5ldGZyb250fG9wZXJhIG0ob2J8aW4paXxwYWxtKCBvcyk/fHBob25lfHAoaXhpfHJlKVxcL3xwbHVja2VyfHBvY2tldHxwc3B8c2VyaWVzKDR8NikwfHN5bWJpYW58dHJlb3x1cFxcLihicm93c2VyfGxpbmspfHZvZGFmb25lfHdhcHx3aW5kb3dzIGNlfHhkYXx4aWluby9pLnRlc3QoYSl8fC8xMjA3fDYzMTB8NjU5MHwzZ3NvfDR0aHB8NTBbMS02XWl8Nzcwc3w4MDJzfGEgd2F8YWJhY3xhYyhlcnxvb3xzXFwtKXxhaShrb3xybil8YWwoYXZ8Y2F8Y28pfGFtb2l8YW4oZXh8bnl8eXcpfGFwdHV8YXIoY2h8Z28pfGFzKHRlfHVzKXxhdHR3fGF1KGRpfFxcLW18ciB8cyApfGF2YW58YmUoY2t8bGx8bnEpfGJpKGxifHJkKXxibChhY3xheil8YnIoZXx2KXd8YnVtYnxid1xcLShufHUpfGM1NVxcL3xjYXBpfGNjd2F8Y2RtXFwtfGNlbGx8Y2h0bXxjbGRjfGNtZFxcLXxjbyhtcHxuZCl8Y3Jhd3xkYShpdHxsbHxuZyl8ZGJ0ZXxkY1xcLXN8ZGV2aXxkaWNhfGRtb2J8ZG8oY3xwKW98ZHMoMTJ8XFwtZCl8ZWwoNDl8YWkpfGVtKGwyfHVsKXxlcihpY3xrMCl8ZXNsOHxleihbNC03XTB8b3N8d2F8emUpfGZldGN8Zmx5KFxcLXxfKXxnMSB1fGc1NjB8Z2VuZXxnZlxcLTV8Z1xcLW1vfGdvKFxcLnd8b2QpfGdyKGFkfHVuKXxoYWllfGhjaXR8aGRcXC0obXxwfHQpfGhlaVxcLXxoaShwdHx0YSl8aHAoIGl8aXApfGhzXFwtY3xodChjKFxcLXwgfF98YXxnfHB8c3x0KXx0cCl8aHUoYXd8dGMpfGlcXC0oMjB8Z298bWEpfGkyMzB8aWFjKCB8XFwtfFxcLyl8aWJyb3xpZGVhfGlnMDF8aWtvbXxpbTFrfGlubm98aXBhcXxpcmlzfGphKHR8dilhfGpicm98amVtdXxqaWdzfGtkZGl8a2VqaXxrZ3QoIHxcXC8pfGtsb258a3B0IHxrd2NcXC18a3lvKGN8ayl8bGUobm98eGkpfGxnKCBnfFxcLyhrfGx8dSl8NTB8NTR8XFwtW2Etd10pfGxpYnd8bHlueHxtMVxcLXd8bTNnYXxtNTBcXC98bWEodGV8dWl8eG8pfG1jKDAxfDIxfGNhKXxtXFwtY3J8bWUocmN8cmkpfG1pKG84fG9hfHRzKXxtbWVmfG1vKDAxfDAyfGJpfGRlfGRvfHQoXFwtfCB8b3x2KXx6eil8bXQoNTB8cDF8diApfG13YnB8bXl3YXxuMTBbMC0yXXxuMjBbMi0zXXxuMzAoMHwyKXxuNTAoMHwyfDUpfG43KDAoMHwxKXwxMCl8bmUoKGN8bSlcXC18b258dGZ8d2Z8d2d8d3QpfG5vayg2fGkpfG56cGh8bzJpbXxvcCh0aXx3dil8b3Jhbnxvd2cxfHA4MDB8cGFuKGF8ZHx0KXxwZHhnfHBnKDEzfFxcLShbMS04XXxjKSl8cGhpbHxwaXJlfHBsKGF5fHVjKXxwblxcLTJ8cG8oY2t8cnR8c2UpfHByb3h8cHNpb3xwdFxcLWd8cWFcXC1hfHFjKDA3fDEyfDIxfDMyfDYwfFxcLVsyLTddfGlcXC0pfHF0ZWt8cjM4MHxyNjAwfHJha3N8cmltOXxybyh2ZXx6byl8czU1XFwvfHNhKGdlfG1hfG1tfG1zfG55fHZhKXxzYygwMXxoXFwtfG9vfHBcXC0pfHNka1xcL3xzZShjKFxcLXwwfDEpfDQ3fG1jfG5kfHJpKXxzZ2hcXC18c2hhcnxzaWUoXFwtfG0pfHNrXFwtMHxzbCg0NXxpZCl8c20oYWx8YXJ8YjN8aXR8dDUpfHNvKGZ0fG55KXxzcCgwMXxoXFwtfHZcXC18diApfHN5KDAxfG1iKXx0MigxOHw1MCl8dDYoMDB8MTB8MTgpfHRhKGd0fGxrKXx0Y2xcXC18dGRnXFwtfHRlbChpfG0pfHRpbVxcLXx0XFwtbW98dG8ocGx8c2gpfHRzKDcwfG1cXC18bTN8bTUpfHR4XFwtOXx1cChcXC5ifGcxfHNpKXx1dHN0fHY0MDB8djc1MHx2ZXJpfHZpKHJnfHRlKXx2ayg0MHw1WzAtM118XFwtdil8dm00MHx2b2RhfHZ1bGN8dngoNTJ8NTN8NjB8NjF8NzB8ODB8ODF8ODN8ODV8OTgpfHczYyhcXC18ICl8d2ViY3x3aGl0fHdpKGcgfG5jfG53KXx3bWxifHdvbnV8eDcwMHx5YXNcXC18eW91cnx6ZXRvfHp0ZVxcLS9pLnRlc3QoYS5zdWJzdHIoMCw0KSkpY2hlY2sgPSB0cnVlfSkobmF2aWdhdG9yLnVzZXJBZ2VudHx8bmF2aWdhdG9yLnZlbmRvcnx8d2luZG93Lm9wZXJhKTtcbiAgcmV0dXJuIGNoZWNrO1xufTtcblxuVXRpbC5leHRlbmQgPSBmdW5jdGlvbihkZXN0LCBzcmMpIHtcbiAgZm9yICh2YXIga2V5IGluIHNyYykge1xuICAgIGlmIChzcmMuaGFzT3duUHJvcGVydHkoa2V5KSkge1xuICAgICAgZGVzdFtrZXldID0gc3JjW2tleV07XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIGRlc3Q7XG59XG5cblV0aWwuc2FmYXJpQ3NzU2l6ZVdvcmthcm91bmQgPSBmdW5jdGlvbihjYW52YXMpIHtcbiAgLy8gVE9ETyhzbXVzKTogUmVtb3ZlIHRoaXMgd29ya2Fyb3VuZCB3aGVuIFNhZmFyaSBmb3IgaU9TIGlzIGZpeGVkLlxuICAvLyBpT1Mgb25seSB3b3JrYXJvdW5kIChmb3IgaHR0cHM6Ly9idWdzLndlYmtpdC5vcmcvc2hvd19idWcuY2dpP2lkPTE1MjU1NikuXG4gIC8vXG4gIC8vIFwiVG8gdGhlIGxhc3QgSSBncmFwcGxlIHdpdGggdGhlZTtcbiAgLy8gIGZyb20gaGVsbCdzIGhlYXJ0IEkgc3RhYiBhdCB0aGVlO1xuICAvLyAgZm9yIGhhdGUncyBzYWtlIEkgc3BpdCBteSBsYXN0IGJyZWF0aCBhdCB0aGVlLlwiXG4gIC8vIC0tIE1vYnkgRGljaywgYnkgSGVybWFuIE1lbHZpbGxlXG4gIGlmIChVdGlsLmlzSU9TKCkpIHtcbiAgICB2YXIgd2lkdGggPSBjYW52YXMuc3R5bGUud2lkdGg7XG4gICAgdmFyIGhlaWdodCA9IGNhbnZhcy5zdHlsZS5oZWlnaHQ7XG4gICAgY2FudmFzLnN0eWxlLndpZHRoID0gKHBhcnNlSW50KHdpZHRoKSArIDEpICsgJ3B4JztcbiAgICBjYW52YXMuc3R5bGUuaGVpZ2h0ID0gKHBhcnNlSW50KGhlaWdodCkpICsgJ3B4JztcbiAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgY2FudmFzLnN0eWxlLndpZHRoID0gd2lkdGg7XG4gICAgICBjYW52YXMuc3R5bGUuaGVpZ2h0ID0gaGVpZ2h0O1xuICAgIH0sIDEwMCk7XG4gIH1cblxuICAvLyBEZWJ1ZyBvbmx5LlxuICB3aW5kb3cuVXRpbCA9IFV0aWw7XG4gIHdpbmRvdy5jYW52YXMgPSBjYW52YXM7XG59O1xuXG5VdGlsLmlzRGVidWcgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIFV0aWwuZ2V0UXVlcnlQYXJhbWV0ZXIoJ2RlYnVnJyk7XG59O1xuXG5VdGlsLmdldFF1ZXJ5UGFyYW1ldGVyID0gZnVuY3Rpb24obmFtZSkge1xuICB2YXIgbmFtZSA9IG5hbWUucmVwbGFjZSgvW1xcW10vLCBcIlxcXFxbXCIpLnJlcGxhY2UoL1tcXF1dLywgXCJcXFxcXVwiKTtcbiAgdmFyIHJlZ2V4ID0gbmV3IFJlZ0V4cChcIltcXFxcPyZdXCIgKyBuYW1lICsgXCI9KFteJiNdKilcIiksXG4gICAgICByZXN1bHRzID0gcmVnZXguZXhlYyhsb2NhdGlvbi5zZWFyY2gpO1xuICByZXR1cm4gcmVzdWx0cyA9PT0gbnVsbCA/IFwiXCIgOiBkZWNvZGVVUklDb21wb25lbnQocmVzdWx0c1sxXS5yZXBsYWNlKC9cXCsvZywgXCIgXCIpKTtcbn07XG5cblV0aWwuZnJhbWVEYXRhRnJvbVBvc2UgPSAoZnVuY3Rpb24oKSB7XG4gIHZhciBwaU92ZXIxODAgPSBNYXRoLlBJIC8gMTgwLjA7XG4gIHZhciByYWQ0NSA9IE1hdGguUEkgKiAwLjI1O1xuXG4gIC8vIEJvcnJvd2VkIGZyb20gZ2xNYXRyaXguXG4gIGZ1bmN0aW9uIG1hdDRfcGVyc3BlY3RpdmVGcm9tRmllbGRPZlZpZXcob3V0LCBmb3YsIG5lYXIsIGZhcikge1xuICAgIHZhciB1cFRhbiA9IE1hdGgudGFuKGZvdiA/IChmb3YudXBEZWdyZWVzICogcGlPdmVyMTgwKSA6IHJhZDQ1KSxcbiAgICBkb3duVGFuID0gTWF0aC50YW4oZm92ID8gKGZvdi5kb3duRGVncmVlcyAqIHBpT3ZlcjE4MCkgOiByYWQ0NSksXG4gICAgbGVmdFRhbiA9IE1hdGgudGFuKGZvdiA/IChmb3YubGVmdERlZ3JlZXMgKiBwaU92ZXIxODApIDogcmFkNDUpLFxuICAgIHJpZ2h0VGFuID0gTWF0aC50YW4oZm92ID8gKGZvdi5yaWdodERlZ3JlZXMgKiBwaU92ZXIxODApIDogcmFkNDUpLFxuICAgIHhTY2FsZSA9IDIuMCAvIChsZWZ0VGFuICsgcmlnaHRUYW4pLFxuICAgIHlTY2FsZSA9IDIuMCAvICh1cFRhbiArIGRvd25UYW4pO1xuXG4gICAgb3V0WzBdID0geFNjYWxlO1xuICAgIG91dFsxXSA9IDAuMDtcbiAgICBvdXRbMl0gPSAwLjA7XG4gICAgb3V0WzNdID0gMC4wO1xuICAgIG91dFs0XSA9IDAuMDtcbiAgICBvdXRbNV0gPSB5U2NhbGU7XG4gICAgb3V0WzZdID0gMC4wO1xuICAgIG91dFs3XSA9IDAuMDtcbiAgICBvdXRbOF0gPSAtKChsZWZ0VGFuIC0gcmlnaHRUYW4pICogeFNjYWxlICogMC41KTtcbiAgICBvdXRbOV0gPSAoKHVwVGFuIC0gZG93blRhbikgKiB5U2NhbGUgKiAwLjUpO1xuICAgIG91dFsxMF0gPSBmYXIgLyAobmVhciAtIGZhcik7XG4gICAgb3V0WzExXSA9IC0xLjA7XG4gICAgb3V0WzEyXSA9IDAuMDtcbiAgICBvdXRbMTNdID0gMC4wO1xuICAgIG91dFsxNF0gPSAoZmFyICogbmVhcikgLyAobmVhciAtIGZhcik7XG4gICAgb3V0WzE1XSA9IDAuMDtcbiAgICByZXR1cm4gb3V0O1xuICB9XG5cbiAgZnVuY3Rpb24gbWF0NF9mcm9tUm90YXRpb25UcmFuc2xhdGlvbihvdXQsIHEsIHYpIHtcbiAgICAvLyBRdWF0ZXJuaW9uIG1hdGhcbiAgICB2YXIgeCA9IHFbMF0sIHkgPSBxWzFdLCB6ID0gcVsyXSwgdyA9IHFbM10sXG4gICAgICAgIHgyID0geCArIHgsXG4gICAgICAgIHkyID0geSArIHksXG4gICAgICAgIHoyID0geiArIHosXG5cbiAgICAgICAgeHggPSB4ICogeDIsXG4gICAgICAgIHh5ID0geCAqIHkyLFxuICAgICAgICB4eiA9IHggKiB6MixcbiAgICAgICAgeXkgPSB5ICogeTIsXG4gICAgICAgIHl6ID0geSAqIHoyLFxuICAgICAgICB6eiA9IHogKiB6MixcbiAgICAgICAgd3ggPSB3ICogeDIsXG4gICAgICAgIHd5ID0gdyAqIHkyLFxuICAgICAgICB3eiA9IHcgKiB6MjtcblxuICAgIG91dFswXSA9IDEgLSAoeXkgKyB6eik7XG4gICAgb3V0WzFdID0geHkgKyB3ejtcbiAgICBvdXRbMl0gPSB4eiAtIHd5O1xuICAgIG91dFszXSA9IDA7XG4gICAgb3V0WzRdID0geHkgLSB3ejtcbiAgICBvdXRbNV0gPSAxIC0gKHh4ICsgenopO1xuICAgIG91dFs2XSA9IHl6ICsgd3g7XG4gICAgb3V0WzddID0gMDtcbiAgICBvdXRbOF0gPSB4eiArIHd5O1xuICAgIG91dFs5XSA9IHl6IC0gd3g7XG4gICAgb3V0WzEwXSA9IDEgLSAoeHggKyB5eSk7XG4gICAgb3V0WzExXSA9IDA7XG4gICAgb3V0WzEyXSA9IHZbMF07XG4gICAgb3V0WzEzXSA9IHZbMV07XG4gICAgb3V0WzE0XSA9IHZbMl07XG4gICAgb3V0WzE1XSA9IDE7XG5cbiAgICByZXR1cm4gb3V0O1xuICB9O1xuXG4gIGZ1bmN0aW9uIG1hdDRfdHJhbnNsYXRlKG91dCwgYSwgdikge1xuICAgIHZhciB4ID0gdlswXSwgeSA9IHZbMV0sIHogPSB2WzJdLFxuICAgICAgICBhMDAsIGEwMSwgYTAyLCBhMDMsXG4gICAgICAgIGExMCwgYTExLCBhMTIsIGExMyxcbiAgICAgICAgYTIwLCBhMjEsIGEyMiwgYTIzO1xuXG4gICAgaWYgKGEgPT09IG91dCkge1xuICAgICAgb3V0WzEyXSA9IGFbMF0gKiB4ICsgYVs0XSAqIHkgKyBhWzhdICogeiArIGFbMTJdO1xuICAgICAgb3V0WzEzXSA9IGFbMV0gKiB4ICsgYVs1XSAqIHkgKyBhWzldICogeiArIGFbMTNdO1xuICAgICAgb3V0WzE0XSA9IGFbMl0gKiB4ICsgYVs2XSAqIHkgKyBhWzEwXSAqIHogKyBhWzE0XTtcbiAgICAgIG91dFsxNV0gPSBhWzNdICogeCArIGFbN10gKiB5ICsgYVsxMV0gKiB6ICsgYVsxNV07XG4gICAgfSBlbHNlIHtcbiAgICAgIGEwMCA9IGFbMF07IGEwMSA9IGFbMV07IGEwMiA9IGFbMl07IGEwMyA9IGFbM107XG4gICAgICBhMTAgPSBhWzRdOyBhMTEgPSBhWzVdOyBhMTIgPSBhWzZdOyBhMTMgPSBhWzddO1xuICAgICAgYTIwID0gYVs4XTsgYTIxID0gYVs5XTsgYTIyID0gYVsxMF07IGEyMyA9IGFbMTFdO1xuXG4gICAgICBvdXRbMF0gPSBhMDA7IG91dFsxXSA9IGEwMTsgb3V0WzJdID0gYTAyOyBvdXRbM10gPSBhMDM7XG4gICAgICBvdXRbNF0gPSBhMTA7IG91dFs1XSA9IGExMTsgb3V0WzZdID0gYTEyOyBvdXRbN10gPSBhMTM7XG4gICAgICBvdXRbOF0gPSBhMjA7IG91dFs5XSA9IGEyMTsgb3V0WzEwXSA9IGEyMjsgb3V0WzExXSA9IGEyMztcblxuICAgICAgb3V0WzEyXSA9IGEwMCAqIHggKyBhMTAgKiB5ICsgYTIwICogeiArIGFbMTJdO1xuICAgICAgb3V0WzEzXSA9IGEwMSAqIHggKyBhMTEgKiB5ICsgYTIxICogeiArIGFbMTNdO1xuICAgICAgb3V0WzE0XSA9IGEwMiAqIHggKyBhMTIgKiB5ICsgYTIyICogeiArIGFbMTRdO1xuICAgICAgb3V0WzE1XSA9IGEwMyAqIHggKyBhMTMgKiB5ICsgYTIzICogeiArIGFbMTVdO1xuICAgIH1cblxuICAgIHJldHVybiBvdXQ7XG4gIH07XG5cbiAgZnVuY3Rpb24gbWF0NF9pbnZlcnQob3V0LCBhKSB7XG4gICAgdmFyIGEwMCA9IGFbMF0sIGEwMSA9IGFbMV0sIGEwMiA9IGFbMl0sIGEwMyA9IGFbM10sXG4gICAgICAgIGExMCA9IGFbNF0sIGExMSA9IGFbNV0sIGExMiA9IGFbNl0sIGExMyA9IGFbN10sXG4gICAgICAgIGEyMCA9IGFbOF0sIGEyMSA9IGFbOV0sIGEyMiA9IGFbMTBdLCBhMjMgPSBhWzExXSxcbiAgICAgICAgYTMwID0gYVsxMl0sIGEzMSA9IGFbMTNdLCBhMzIgPSBhWzE0XSwgYTMzID0gYVsxNV0sXG5cbiAgICAgICAgYjAwID0gYTAwICogYTExIC0gYTAxICogYTEwLFxuICAgICAgICBiMDEgPSBhMDAgKiBhMTIgLSBhMDIgKiBhMTAsXG4gICAgICAgIGIwMiA9IGEwMCAqIGExMyAtIGEwMyAqIGExMCxcbiAgICAgICAgYjAzID0gYTAxICogYTEyIC0gYTAyICogYTExLFxuICAgICAgICBiMDQgPSBhMDEgKiBhMTMgLSBhMDMgKiBhMTEsXG4gICAgICAgIGIwNSA9IGEwMiAqIGExMyAtIGEwMyAqIGExMixcbiAgICAgICAgYjA2ID0gYTIwICogYTMxIC0gYTIxICogYTMwLFxuICAgICAgICBiMDcgPSBhMjAgKiBhMzIgLSBhMjIgKiBhMzAsXG4gICAgICAgIGIwOCA9IGEyMCAqIGEzMyAtIGEyMyAqIGEzMCxcbiAgICAgICAgYjA5ID0gYTIxICogYTMyIC0gYTIyICogYTMxLFxuICAgICAgICBiMTAgPSBhMjEgKiBhMzMgLSBhMjMgKiBhMzEsXG4gICAgICAgIGIxMSA9IGEyMiAqIGEzMyAtIGEyMyAqIGEzMixcblxuICAgICAgICAvLyBDYWxjdWxhdGUgdGhlIGRldGVybWluYW50XG4gICAgICAgIGRldCA9IGIwMCAqIGIxMSAtIGIwMSAqIGIxMCArIGIwMiAqIGIwOSArIGIwMyAqIGIwOCAtIGIwNCAqIGIwNyArIGIwNSAqIGIwNjtcblxuICAgIGlmICghZGV0KSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgZGV0ID0gMS4wIC8gZGV0O1xuXG4gICAgb3V0WzBdID0gKGExMSAqIGIxMSAtIGExMiAqIGIxMCArIGExMyAqIGIwOSkgKiBkZXQ7XG4gICAgb3V0WzFdID0gKGEwMiAqIGIxMCAtIGEwMSAqIGIxMSAtIGEwMyAqIGIwOSkgKiBkZXQ7XG4gICAgb3V0WzJdID0gKGEzMSAqIGIwNSAtIGEzMiAqIGIwNCArIGEzMyAqIGIwMykgKiBkZXQ7XG4gICAgb3V0WzNdID0gKGEyMiAqIGIwNCAtIGEyMSAqIGIwNSAtIGEyMyAqIGIwMykgKiBkZXQ7XG4gICAgb3V0WzRdID0gKGExMiAqIGIwOCAtIGExMCAqIGIxMSAtIGExMyAqIGIwNykgKiBkZXQ7XG4gICAgb3V0WzVdID0gKGEwMCAqIGIxMSAtIGEwMiAqIGIwOCArIGEwMyAqIGIwNykgKiBkZXQ7XG4gICAgb3V0WzZdID0gKGEzMiAqIGIwMiAtIGEzMCAqIGIwNSAtIGEzMyAqIGIwMSkgKiBkZXQ7XG4gICAgb3V0WzddID0gKGEyMCAqIGIwNSAtIGEyMiAqIGIwMiArIGEyMyAqIGIwMSkgKiBkZXQ7XG4gICAgb3V0WzhdID0gKGExMCAqIGIxMCAtIGExMSAqIGIwOCArIGExMyAqIGIwNikgKiBkZXQ7XG4gICAgb3V0WzldID0gKGEwMSAqIGIwOCAtIGEwMCAqIGIxMCAtIGEwMyAqIGIwNikgKiBkZXQ7XG4gICAgb3V0WzEwXSA9IChhMzAgKiBiMDQgLSBhMzEgKiBiMDIgKyBhMzMgKiBiMDApICogZGV0O1xuICAgIG91dFsxMV0gPSAoYTIxICogYjAyIC0gYTIwICogYjA0IC0gYTIzICogYjAwKSAqIGRldDtcbiAgICBvdXRbMTJdID0gKGExMSAqIGIwNyAtIGExMCAqIGIwOSAtIGExMiAqIGIwNikgKiBkZXQ7XG4gICAgb3V0WzEzXSA9IChhMDAgKiBiMDkgLSBhMDEgKiBiMDcgKyBhMDIgKiBiMDYpICogZGV0O1xuICAgIG91dFsxNF0gPSAoYTMxICogYjAxIC0gYTMwICogYjAzIC0gYTMyICogYjAwKSAqIGRldDtcbiAgICBvdXRbMTVdID0gKGEyMCAqIGIwMyAtIGEyMSAqIGIwMSArIGEyMiAqIGIwMCkgKiBkZXQ7XG5cbiAgICByZXR1cm4gb3V0O1xuICB9O1xuXG4gIHZhciBkZWZhdWx0T3JpZW50YXRpb24gPSBuZXcgRmxvYXQzMkFycmF5KFswLCAwLCAwLCAxXSk7XG4gIHZhciBkZWZhdWx0UG9zaXRpb24gPSBuZXcgRmxvYXQzMkFycmF5KFswLCAwLCAwXSk7XG5cbiAgZnVuY3Rpb24gdXBkYXRlRXllTWF0cmljZXMocHJvamVjdGlvbiwgdmlldywgcG9zZSwgcGFyYW1ldGVycywgdnJEaXNwbGF5KSB7XG4gICAgbWF0NF9wZXJzcGVjdGl2ZUZyb21GaWVsZE9mVmlldyhwcm9qZWN0aW9uLCBwYXJhbWV0ZXJzID8gcGFyYW1ldGVycy5maWVsZE9mVmlldyA6IG51bGwsIHZyRGlzcGxheS5kZXB0aE5lYXIsIHZyRGlzcGxheS5kZXB0aEZhcik7XG5cbiAgICB2YXIgb3JpZW50YXRpb24gPSBwb3NlLm9yaWVudGF0aW9uIHx8IGRlZmF1bHRPcmllbnRhdGlvbjtcbiAgICB2YXIgcG9zaXRpb24gPSBwb3NlLnBvc2l0aW9uIHx8IGRlZmF1bHRQb3NpdGlvbjtcblxuICAgIG1hdDRfZnJvbVJvdGF0aW9uVHJhbnNsYXRpb24odmlldywgb3JpZW50YXRpb24sIHBvc2l0aW9uKTtcbiAgICBpZiAocGFyYW1ldGVycylcbiAgICAgIG1hdDRfdHJhbnNsYXRlKHZpZXcsIHZpZXcsIHBhcmFtZXRlcnMub2Zmc2V0KTtcbiAgICBtYXQ0X2ludmVydCh2aWV3LCB2aWV3KTtcbiAgfVxuXG4gIHJldHVybiBmdW5jdGlvbihmcmFtZURhdGEsIHBvc2UsIHZyRGlzcGxheSkge1xuICAgIGlmICghZnJhbWVEYXRhIHx8ICFwb3NlKVxuICAgICAgcmV0dXJuIGZhbHNlO1xuXG4gICAgZnJhbWVEYXRhLnBvc2UgPSBwb3NlO1xuICAgIGZyYW1lRGF0YS50aW1lc3RhbXAgPSBwb3NlLnRpbWVzdGFtcDtcblxuICAgIHVwZGF0ZUV5ZU1hdHJpY2VzKFxuICAgICAgICBmcmFtZURhdGEubGVmdFByb2plY3Rpb25NYXRyaXgsIGZyYW1lRGF0YS5sZWZ0Vmlld01hdHJpeCxcbiAgICAgICAgcG9zZSwgdnJEaXNwbGF5LmdldEV5ZVBhcmFtZXRlcnMoXCJsZWZ0XCIpLCB2ckRpc3BsYXkpO1xuICAgIHVwZGF0ZUV5ZU1hdHJpY2VzKFxuICAgICAgICBmcmFtZURhdGEucmlnaHRQcm9qZWN0aW9uTWF0cml4LCBmcmFtZURhdGEucmlnaHRWaWV3TWF0cml4LFxuICAgICAgICBwb3NlLCB2ckRpc3BsYXkuZ2V0RXllUGFyYW1ldGVycyhcInJpZ2h0XCIpLCB2ckRpc3BsYXkpO1xuXG4gICAgcmV0dXJuIHRydWU7XG4gIH07XG59KSgpO1xuXG5VdGlsLmlzSW5zaWRlQ3Jvc3NEb21haW5JRnJhbWUgPSBmdW5jdGlvbigpIHtcbiAgdmFyIGlzRnJhbWVkID0gKHdpbmRvdy5zZWxmICE9PSB3aW5kb3cudG9wKTtcbiAgdmFyIHJlZkRvbWFpbiA9IFV0aWwuZ2V0RG9tYWluRnJvbVVybChkb2N1bWVudC5yZWZlcnJlcik7XG4gIHZhciB0aGlzRG9tYWluID0gVXRpbC5nZXREb21haW5Gcm9tVXJsKHdpbmRvdy5sb2NhdGlvbi5ocmVmKTtcblxuICByZXR1cm4gaXNGcmFtZWQgJiYgKHJlZkRvbWFpbiAhPT0gdGhpc0RvbWFpbik7XG59O1xuXG4vLyBGcm9tIGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9hLzIzOTQ1MDI3LlxuVXRpbC5nZXREb21haW5Gcm9tVXJsID0gZnVuY3Rpb24odXJsKSB7XG4gIHZhciBkb21haW47XG4gIC8vIEZpbmQgJiByZW1vdmUgcHJvdG9jb2wgKGh0dHAsIGZ0cCwgZXRjLikgYW5kIGdldCBkb21haW4uXG4gIGlmICh1cmwuaW5kZXhPZihcIjovL1wiKSA+IC0xKSB7XG4gICAgZG9tYWluID0gdXJsLnNwbGl0KCcvJylbMl07XG4gIH1cbiAgZWxzZSB7XG4gICAgZG9tYWluID0gdXJsLnNwbGl0KCcvJylbMF07XG4gIH1cblxuICAvL2ZpbmQgJiByZW1vdmUgcG9ydCBudW1iZXJcbiAgZG9tYWluID0gZG9tYWluLnNwbGl0KCc6JylbMF07XG5cbiAgcmV0dXJuIGRvbWFpbjtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBVdGlsO1xuIiwiLypcbiAqIENvcHlyaWdodCAyMDE1IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKi9cblxudmFyIERldmljZUluZm8gPSByZXF1aXJlKCcuL2RldmljZS1pbmZvLmpzJyk7XG52YXIgVXRpbCA9IHJlcXVpcmUoJy4vdXRpbC5qcycpO1xuXG52YXIgREVGQVVMVF9WSUVXRVIgPSAnQ2FyZGJvYXJkVjEnO1xudmFyIFZJRVdFUl9LRVkgPSAnV0VCVlJfQ0FSREJPQVJEX1ZJRVdFUic7XG52YXIgQ0xBU1NfTkFNRSA9ICd3ZWJ2ci1wb2x5ZmlsbC12aWV3ZXItc2VsZWN0b3InO1xuXG4vKipcbiAqIENyZWF0ZXMgYSB2aWV3ZXIgc2VsZWN0b3Igd2l0aCB0aGUgb3B0aW9ucyBzcGVjaWZpZWQuIFN1cHBvcnRzIGJlaW5nIHNob3duXG4gKiBhbmQgaGlkZGVuLiBHZW5lcmF0ZXMgZXZlbnRzIHdoZW4gdmlld2VyIHBhcmFtZXRlcnMgY2hhbmdlLiBBbHNvIHN1cHBvcnRzXG4gKiBzYXZpbmcgdGhlIGN1cnJlbnRseSBzZWxlY3RlZCBpbmRleCBpbiBsb2NhbFN0b3JhZ2UuXG4gKi9cbmZ1bmN0aW9uIFZpZXdlclNlbGVjdG9yKCkge1xuICAvLyBUcnkgdG8gbG9hZCB0aGUgc2VsZWN0ZWQga2V5IGZyb20gbG9jYWwgc3RvcmFnZS5cbiAgdHJ5IHtcbiAgICB0aGlzLnNlbGVjdGVkS2V5ID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0oVklFV0VSX0tFWSk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc29sZS5lcnJvcignRmFpbGVkIHRvIGxvYWQgdmlld2VyIHByb2ZpbGU6ICVzJywgZXJyb3IpO1xuICB9XG5cbiAgLy9JZiBub25lIGV4aXN0cywgb3IgaWYgbG9jYWxzdG9yYWdlIGlzIHVuYXZhaWxhYmxlLCB1c2UgdGhlIGRlZmF1bHQga2V5LlxuICBpZiAoIXRoaXMuc2VsZWN0ZWRLZXkpIHtcbiAgICB0aGlzLnNlbGVjdGVkS2V5ID0gREVGQVVMVF9WSUVXRVI7XG4gIH1cblxuICB0aGlzLmRpYWxvZyA9IHRoaXMuY3JlYXRlRGlhbG9nXyhEZXZpY2VJbmZvLlZpZXdlcnMpO1xuICB0aGlzLnJvb3QgPSBudWxsO1xuICB0aGlzLm9uQ2hhbmdlQ2FsbGJhY2tzXyA9IFtdO1xufVxuXG5WaWV3ZXJTZWxlY3Rvci5wcm90b3R5cGUuc2hvdyA9IGZ1bmN0aW9uKHJvb3QpIHtcbiAgdGhpcy5yb290ID0gcm9vdDtcblxuICByb290LmFwcGVuZENoaWxkKHRoaXMuZGlhbG9nKTtcblxuICAvLyBFbnN1cmUgdGhlIGN1cnJlbnRseSBzZWxlY3RlZCBpdGVtIGlzIGNoZWNrZWQuXG4gIHZhciBzZWxlY3RlZCA9IHRoaXMuZGlhbG9nLnF1ZXJ5U2VsZWN0b3IoJyMnICsgdGhpcy5zZWxlY3RlZEtleSk7XG4gIHNlbGVjdGVkLmNoZWNrZWQgPSB0cnVlO1xuXG4gIC8vIFNob3cgdGhlIFVJLlxuICB0aGlzLmRpYWxvZy5zdHlsZS5kaXNwbGF5ID0gJ2Jsb2NrJztcbn07XG5cblZpZXdlclNlbGVjdG9yLnByb3RvdHlwZS5oaWRlID0gZnVuY3Rpb24oKSB7XG4gIGlmICh0aGlzLnJvb3QgJiYgdGhpcy5yb290LmNvbnRhaW5zKHRoaXMuZGlhbG9nKSkge1xuICAgIHRoaXMucm9vdC5yZW1vdmVDaGlsZCh0aGlzLmRpYWxvZyk7XG4gIH1cbiAgdGhpcy5kaWFsb2cuc3R5bGUuZGlzcGxheSA9ICdub25lJztcbn07XG5cblZpZXdlclNlbGVjdG9yLnByb3RvdHlwZS5nZXRDdXJyZW50Vmlld2VyID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiBEZXZpY2VJbmZvLlZpZXdlcnNbdGhpcy5zZWxlY3RlZEtleV07XG59O1xuXG5WaWV3ZXJTZWxlY3Rvci5wcm90b3R5cGUuZ2V0U2VsZWN0ZWRLZXlfID0gZnVuY3Rpb24oKSB7XG4gIHZhciBpbnB1dCA9IHRoaXMuZGlhbG9nLnF1ZXJ5U2VsZWN0b3IoJ2lucHV0W25hbWU9ZmllbGRdOmNoZWNrZWQnKTtcbiAgaWYgKGlucHV0KSB7XG4gICAgcmV0dXJuIGlucHV0LmlkO1xuICB9XG4gIHJldHVybiBudWxsO1xufTtcblxuVmlld2VyU2VsZWN0b3IucHJvdG90eXBlLm9uQ2hhbmdlID0gZnVuY3Rpb24oY2IpIHtcbiAgdGhpcy5vbkNoYW5nZUNhbGxiYWNrc18ucHVzaChjYik7XG59O1xuXG5WaWV3ZXJTZWxlY3Rvci5wcm90b3R5cGUuZmlyZU9uQ2hhbmdlXyA9IGZ1bmN0aW9uKHZpZXdlcikge1xuICBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMub25DaGFuZ2VDYWxsYmFja3NfLmxlbmd0aDsgaSsrKSB7XG4gICAgdGhpcy5vbkNoYW5nZUNhbGxiYWNrc19baV0odmlld2VyKTtcbiAgfVxufTtcblxuVmlld2VyU2VsZWN0b3IucHJvdG90eXBlLm9uU2F2ZV8gPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5zZWxlY3RlZEtleSA9IHRoaXMuZ2V0U2VsZWN0ZWRLZXlfKCk7XG4gIGlmICghdGhpcy5zZWxlY3RlZEtleSB8fCAhRGV2aWNlSW5mby5WaWV3ZXJzW3RoaXMuc2VsZWN0ZWRLZXldKSB7XG4gICAgY29uc29sZS5lcnJvcignVmlld2VyU2VsZWN0b3Iub25TYXZlXzogdGhpcyBzaG91bGQgbmV2ZXIgaGFwcGVuIScpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHRoaXMuZmlyZU9uQ2hhbmdlXyhEZXZpY2VJbmZvLlZpZXdlcnNbdGhpcy5zZWxlY3RlZEtleV0pO1xuXG4gIC8vIEF0dGVtcHQgdG8gc2F2ZSB0aGUgdmlld2VyIHByb2ZpbGUsIGJ1dCBmYWlscyBpbiBwcml2YXRlIG1vZGUuXG4gIHRyeSB7XG4gICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oVklFV0VSX0tFWSwgdGhpcy5zZWxlY3RlZEtleSk7XG4gIH0gY2F0Y2goZXJyb3IpIHtcbiAgICBjb25zb2xlLmVycm9yKCdGYWlsZWQgdG8gc2F2ZSB2aWV3ZXIgcHJvZmlsZTogJXMnLCBlcnJvcik7XG4gIH1cbiAgdGhpcy5oaWRlKCk7XG59O1xuXG4vKipcbiAqIENyZWF0ZXMgdGhlIGRpYWxvZy5cbiAqL1xuVmlld2VyU2VsZWN0b3IucHJvdG90eXBlLmNyZWF0ZURpYWxvZ18gPSBmdW5jdGlvbihvcHRpb25zKSB7XG4gIHZhciBjb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgY29udGFpbmVyLmNsYXNzTGlzdC5hZGQoQ0xBU1NfTkFNRSk7XG4gIGNvbnRhaW5lci5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuICAvLyBDcmVhdGUgYW4gb3ZlcmxheSB0aGF0IGRpbXMgdGhlIGJhY2tncm91bmQsIGFuZCB3aGljaCBnb2VzIGF3YXkgd2hlbiB5b3VcbiAgLy8gdGFwIGl0LlxuICB2YXIgb3ZlcmxheSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuICB2YXIgcyA9IG92ZXJsYXkuc3R5bGU7XG4gIHMucG9zaXRpb24gPSAnZml4ZWQnO1xuICBzLmxlZnQgPSAwO1xuICBzLnRvcCA9IDA7XG4gIHMud2lkdGggPSAnMTAwJSc7XG4gIHMuaGVpZ2h0ID0gJzEwMCUnO1xuICBzLmJhY2tncm91bmQgPSAncmdiYSgwLCAwLCAwLCAwLjMpJztcbiAgb3ZlcmxheS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIHRoaXMuaGlkZS5iaW5kKHRoaXMpKTtcblxuICB2YXIgd2lkdGggPSAyODA7XG4gIHZhciBkaWFsb2cgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgdmFyIHMgPSBkaWFsb2cuc3R5bGU7XG4gIHMuYm94U2l6aW5nID0gJ2JvcmRlci1ib3gnO1xuICBzLnBvc2l0aW9uID0gJ2ZpeGVkJztcbiAgcy50b3AgPSAnMjRweCc7XG4gIHMubGVmdCA9ICc1MCUnO1xuICBzLm1hcmdpbkxlZnQgPSAoLXdpZHRoLzIpICsgJ3B4JztcbiAgcy53aWR0aCA9IHdpZHRoICsgJ3B4JztcbiAgcy5wYWRkaW5nID0gJzI0cHgnO1xuICBzLm92ZXJmbG93ID0gJ2hpZGRlbic7XG4gIHMuYmFja2dyb3VuZCA9ICcjZmFmYWZhJztcbiAgcy5mb250RmFtaWx5ID0gXCInUm9ib3RvJywgc2Fucy1zZXJpZlwiO1xuICBzLmJveFNoYWRvdyA9ICcwcHggNXB4IDIwcHggIzY2Nic7XG5cbiAgZGlhbG9nLmFwcGVuZENoaWxkKHRoaXMuY3JlYXRlSDFfKCdTZWxlY3QgeW91ciB2aWV3ZXInKSk7XG4gIGZvciAodmFyIGlkIGluIG9wdGlvbnMpIHtcbiAgICBkaWFsb2cuYXBwZW5kQ2hpbGQodGhpcy5jcmVhdGVDaG9pY2VfKGlkLCBvcHRpb25zW2lkXS5sYWJlbCkpO1xuICB9XG4gIGRpYWxvZy5hcHBlbmRDaGlsZCh0aGlzLmNyZWF0ZUJ1dHRvbl8oJ1NhdmUnLCB0aGlzLm9uU2F2ZV8uYmluZCh0aGlzKSkpO1xuXG4gIGNvbnRhaW5lci5hcHBlbmRDaGlsZChvdmVybGF5KTtcbiAgY29udGFpbmVyLmFwcGVuZENoaWxkKGRpYWxvZyk7XG5cbiAgcmV0dXJuIGNvbnRhaW5lcjtcbn07XG5cblZpZXdlclNlbGVjdG9yLnByb3RvdHlwZS5jcmVhdGVIMV8gPSBmdW5jdGlvbihuYW1lKSB7XG4gIHZhciBoMSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2gxJyk7XG4gIHZhciBzID0gaDEuc3R5bGU7XG4gIHMuY29sb3IgPSAnYmxhY2snO1xuICBzLmZvbnRTaXplID0gJzIwcHgnO1xuICBzLmZvbnRXZWlnaHQgPSAnYm9sZCc7XG4gIHMubWFyZ2luVG9wID0gMDtcbiAgcy5tYXJnaW5Cb3R0b20gPSAnMjRweCc7XG4gIGgxLmlubmVySFRNTCA9IG5hbWU7XG4gIHJldHVybiBoMTtcbn07XG5cblZpZXdlclNlbGVjdG9yLnByb3RvdHlwZS5jcmVhdGVDaG9pY2VfID0gZnVuY3Rpb24oaWQsIG5hbWUpIHtcbiAgLypcbiAgPGRpdiBjbGFzcz1cImNob2ljZVwiPlxuICA8aW5wdXQgaWQ9XCJ2MVwiIHR5cGU9XCJyYWRpb1wiIG5hbWU9XCJmaWVsZFwiIHZhbHVlPVwidjFcIj5cbiAgPGxhYmVsIGZvcj1cInYxXCI+Q2FyZGJvYXJkIFYxPC9sYWJlbD5cbiAgPC9kaXY+XG4gICovXG4gIHZhciBkaXYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgZGl2LnN0eWxlLm1hcmdpblRvcCA9ICc4cHgnO1xuICBkaXYuc3R5bGUuY29sb3IgPSAnYmxhY2snO1xuXG4gIHZhciBpbnB1dCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2lucHV0Jyk7XG4gIGlucHV0LnN0eWxlLmZvbnRTaXplID0gJzMwcHgnO1xuICBpbnB1dC5zZXRBdHRyaWJ1dGUoJ2lkJywgaWQpO1xuICBpbnB1dC5zZXRBdHRyaWJ1dGUoJ3R5cGUnLCAncmFkaW8nKTtcbiAgaW5wdXQuc2V0QXR0cmlidXRlKCd2YWx1ZScsIGlkKTtcbiAgaW5wdXQuc2V0QXR0cmlidXRlKCduYW1lJywgJ2ZpZWxkJyk7XG5cbiAgdmFyIGxhYmVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnbGFiZWwnKTtcbiAgbGFiZWwuc3R5bGUubWFyZ2luTGVmdCA9ICc0cHgnO1xuICBsYWJlbC5zZXRBdHRyaWJ1dGUoJ2ZvcicsIGlkKTtcbiAgbGFiZWwuaW5uZXJIVE1MID0gbmFtZTtcblxuICBkaXYuYXBwZW5kQ2hpbGQoaW5wdXQpO1xuICBkaXYuYXBwZW5kQ2hpbGQobGFiZWwpO1xuXG4gIHJldHVybiBkaXY7XG59O1xuXG5WaWV3ZXJTZWxlY3Rvci5wcm90b3R5cGUuY3JlYXRlQnV0dG9uXyA9IGZ1bmN0aW9uKGxhYmVsLCBvbmNsaWNrKSB7XG4gIHZhciBidXR0b24gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdidXR0b24nKTtcbiAgYnV0dG9uLmlubmVySFRNTCA9IGxhYmVsO1xuICB2YXIgcyA9IGJ1dHRvbi5zdHlsZTtcbiAgcy5mbG9hdCA9ICdyaWdodCc7XG4gIHMudGV4dFRyYW5zZm9ybSA9ICd1cHBlcmNhc2UnO1xuICBzLmNvbG9yID0gJyMxMDk0ZjcnO1xuICBzLmZvbnRTaXplID0gJzE0cHgnO1xuICBzLmxldHRlclNwYWNpbmcgPSAwO1xuICBzLmJvcmRlciA9IDA7XG4gIHMuYmFja2dyb3VuZCA9ICdub25lJztcbiAgcy5tYXJnaW5Ub3AgPSAnMTZweCc7XG5cbiAgYnV0dG9uLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgb25jbGljayk7XG5cbiAgcmV0dXJuIGJ1dHRvbjtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gVmlld2VyU2VsZWN0b3I7XG4iLCIvKlxuICogQ29weXJpZ2h0IDIwMTUgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqL1xuXG52YXIgVXRpbCA9IHJlcXVpcmUoJy4vdXRpbC5qcycpO1xuXG4vKipcbiAqIEFuZHJvaWQgYW5kIGlPUyBjb21wYXRpYmxlIHdha2Vsb2NrIGltcGxlbWVudGF0aW9uLlxuICpcbiAqIFJlZmFjdG9yZWQgdGhhbmtzIHRvIGRrb3ZhbGV2QC5cbiAqL1xuZnVuY3Rpb24gQW5kcm9pZFdha2VMb2NrKCkge1xuICB2YXIgdmlkZW8gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd2aWRlbycpO1xuICB2aWRlby5zZXRBdHRyaWJ1dGUoJ2xvb3AnLCAnJyk7XG5cbiAgZnVuY3Rpb24gYWRkU291cmNlVG9WaWRlbyhlbGVtZW50LCB0eXBlLCBkYXRhVVJJKSB7XG4gICAgdmFyIHNvdXJjZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NvdXJjZScpO1xuICAgIHNvdXJjZS5zcmMgPSBkYXRhVVJJO1xuICAgIHNvdXJjZS50eXBlID0gJ3ZpZGVvLycgKyB0eXBlO1xuICAgIGVsZW1lbnQuYXBwZW5kQ2hpbGQoc291cmNlKTtcbiAgfVxuXG4gIGFkZFNvdXJjZVRvVmlkZW8odmlkZW8sJ3dlYm0nLCBVdGlsLmJhc2U2NCgndmlkZW8vd2VibScsICdHa1hmbzBBZ1FvYUJBVUwzZ1FGQzhvRUVRdk9CQ0VLQ1FBUjNaV0p0UW9lQkFrS0ZnUUlZVTRCblFJMFZTYWxtUUNncTE3RkFBdzlDUUUyQVFBWjNhR0Z0YlhsWFFVQUdkMmhoYlcxNVJJbEFDRUNQUUFBQUFBQUFGbFN1YTBBeHJrQXUxNEVCWThXQkFaeUJBQ0sxbkVBRGRXNWtoa0FGVmw5V1VEZ2xob2hBQTFaUU9JT0JBZUJBQnJDQkNMcUJDQjlEdG5WQUl1ZUJBS05BSElFQUFJQXdBUUNkQVNvSUFBZ0FBVUFtSmFRQUEzQUEvdnowQUFBPScpKTtcbiAgYWRkU291cmNlVG9WaWRlbyh2aWRlbywgJ21wNCcsIFV0aWwuYmFzZTY0KCd2aWRlby9tcDQnLCAnQUFBQUhHWjBlWEJwYzI5dEFBQUNBR2x6YjIxcGMyOHliWEEwTVFBQUFBaG1jbVZsQUFBQUcyMWtZWFFBQUFHekFCQUhBQUFCdGhBREFvd2RiYjkvQUFBQzZXMXZiM1lBQUFCc2JYWm9aQUFBQUFCOEpiQ0FmQ1d3Z0FBQUErZ0FBQUFBQUFFQUFBRUFBQUFBQUFBQUFBQUFBQUFCQUFBQUFBQUFBQUFBQUFBQUFBQUFBUUFBQUFBQUFBQUFBQUFBQUFBQVFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUlBQUFJVmRISmhhd0FBQUZ4MGEyaGtBQUFBRDN3bHNJQjhKYkNBQUFBQUFRQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQkFBQUFBQUFBQUFBQUFBQUFBQUFBQVFBQUFBQUFBQUFBQUFBQUFBQUFRQUFBQUFBSUFBQUFDQUFBQUFBQnNXMWthV0VBQUFBZ2JXUm9aQUFBQUFCOEpiQ0FmQ1d3Z0FBQUErZ0FBQUFBVmNRQUFBQUFBQzFvWkd4eUFBQUFBQUFBQUFCMmFXUmxBQUFBQUFBQUFBQUFBQUFBVm1sa1pXOUlZVzVrYkdWeUFBQUFBVnh0YVc1bUFBQUFGSFp0YUdRQUFBQUJBQUFBQUFBQUFBQUFBQUFrWkdsdVpnQUFBQnhrY21WbUFBQUFBQUFBQUFFQUFBQU1kWEpzSUFBQUFBRUFBQUVjYzNSaWJBQUFBTGh6ZEhOa0FBQUFBQUFBQUFFQUFBQ29iWEEwZGdBQUFBQUFBQUFCQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFJQUFnQVNBQUFBRWdBQUFBQUFBQUFBUUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFCai8vd0FBQUZKbGMyUnpBQUFBQUFORUFBRUFCRHdnRVFBQUFBQUREVUFBQUFBQUJTMEFBQUd3QVFBQUFiV0pFd0FBQVFBQUFBRWdBTVNOaUI5RkFFUUJGR01BQUFHeVRHRjJZelV5TGpnM0xqUUdBUUlBQUFBWWMzUjBjd0FBQUFBQUFBQUJBQUFBQVFBQUFBQUFBQUFjYzNSell3QUFBQUFBQUFBQkFBQUFBUUFBQUFFQUFBQUJBQUFBRkhOMGMzb0FBQUFBQUFBQUV3QUFBQUVBQUFBVWMzUmpid0FBQUFBQUFBQUJBQUFBTEFBQUFHQjFaSFJoQUFBQVdHMWxkR0VBQUFBQUFBQUFJV2hrYkhJQUFBQUFBQUFBQUcxa2FYSmhjSEJzQUFBQUFBQUFBQUFBQUFBQUsybHNjM1FBQUFBanFYUnZid0FBQUJ0a1lYUmhBQUFBQVFBQUFBQk1ZWFptTlRJdU56Z3VNdz09JykpO1xuXG4gIHRoaXMucmVxdWVzdCA9IGZ1bmN0aW9uKCkge1xuICAgIGlmICh2aWRlby5wYXVzZWQpIHtcbiAgICAgIHZpZGVvLnBsYXkoKTtcbiAgICB9XG4gIH07XG5cbiAgdGhpcy5yZWxlYXNlID0gZnVuY3Rpb24oKSB7XG4gICAgdmlkZW8ucGF1c2UoKTtcbiAgfTtcbn1cblxuZnVuY3Rpb24gaU9TV2FrZUxvY2soKSB7XG4gIHZhciB0aW1lciA9IG51bGw7XG5cbiAgdGhpcy5yZXF1ZXN0ID0gZnVuY3Rpb24oKSB7XG4gICAgaWYgKCF0aW1lcikge1xuICAgICAgdGltZXIgPSBzZXRJbnRlcnZhbChmdW5jdGlvbigpIHtcbiAgICAgICAgd2luZG93LmxvY2F0aW9uID0gd2luZG93LmxvY2F0aW9uO1xuICAgICAgICBzZXRUaW1lb3V0KHdpbmRvdy5zdG9wLCAwKTtcbiAgICAgIH0sIDMwMDAwKTtcbiAgICB9XG4gIH1cblxuICB0aGlzLnJlbGVhc2UgPSBmdW5jdGlvbigpIHtcbiAgICBpZiAodGltZXIpIHtcbiAgICAgIGNsZWFySW50ZXJ2YWwodGltZXIpO1xuICAgICAgdGltZXIgPSBudWxsO1xuICAgIH1cbiAgfVxufVxuXG5cbmZ1bmN0aW9uIGdldFdha2VMb2NrKCkge1xuICB2YXIgdXNlckFnZW50ID0gbmF2aWdhdG9yLnVzZXJBZ2VudCB8fCBuYXZpZ2F0b3IudmVuZG9yIHx8IHdpbmRvdy5vcGVyYTtcbiAgaWYgKHVzZXJBZ2VudC5tYXRjaCgvaVBob25lL2kpIHx8IHVzZXJBZ2VudC5tYXRjaCgvaVBvZC9pKSkge1xuICAgIHJldHVybiBpT1NXYWtlTG9jaztcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gQW5kcm9pZFdha2VMb2NrO1xuICB9XG59XG5cbm1vZHVsZS5leHBvcnRzID0gZ2V0V2FrZUxvY2soKTsiLCIvKlxuICogQ29weXJpZ2h0IDIwMTUgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqL1xuXG52YXIgVXRpbCA9IHJlcXVpcmUoJy4vdXRpbC5qcycpO1xudmFyIENhcmRib2FyZFZSRGlzcGxheSA9IHJlcXVpcmUoJy4vY2FyZGJvYXJkLXZyLWRpc3BsYXkuanMnKTtcbnZhciBNb3VzZUtleWJvYXJkVlJEaXNwbGF5ID0gcmVxdWlyZSgnLi9tb3VzZS1rZXlib2FyZC12ci1kaXNwbGF5LmpzJyk7XG4vLyBVbmNvbW1lbnQgdG8gYWRkIHBvc2l0aW9uYWwgdHJhY2tpbmcgdmlhIHdlYmNhbS5cbi8vdmFyIFdlYmNhbVBvc2l0aW9uU2Vuc29yVlJEZXZpY2UgPSByZXF1aXJlKCcuL3dlYmNhbS1wb3NpdGlvbi1zZW5zb3ItdnItZGV2aWNlLmpzJyk7XG52YXIgVlJEaXNwbGF5ID0gcmVxdWlyZSgnLi9iYXNlLmpzJykuVlJEaXNwbGF5O1xudmFyIFZSRnJhbWVEYXRhID0gcmVxdWlyZSgnLi9iYXNlLmpzJykuVlJGcmFtZURhdGE7XG52YXIgSE1EVlJEZXZpY2UgPSByZXF1aXJlKCcuL2Jhc2UuanMnKS5ITURWUkRldmljZTtcbnZhciBQb3NpdGlvblNlbnNvclZSRGV2aWNlID0gcmVxdWlyZSgnLi9iYXNlLmpzJykuUG9zaXRpb25TZW5zb3JWUkRldmljZTtcbnZhciBWUkRpc3BsYXlITUREZXZpY2UgPSByZXF1aXJlKCcuL2Rpc3BsYXktd3JhcHBlcnMuanMnKS5WUkRpc3BsYXlITUREZXZpY2U7XG52YXIgVlJEaXNwbGF5UG9zaXRpb25TZW5zb3JEZXZpY2UgPSByZXF1aXJlKCcuL2Rpc3BsYXktd3JhcHBlcnMuanMnKS5WUkRpc3BsYXlQb3NpdGlvblNlbnNvckRldmljZTtcbnZhciB2ZXJzaW9uID0gcmVxdWlyZSgnLi4vcGFja2FnZS5qc29uJykudmVyc2lvbjtcblxuZnVuY3Rpb24gV2ViVlJQb2x5ZmlsbCgpIHtcbiAgdGhpcy5kaXNwbGF5cyA9IFtdO1xuICB0aGlzLmRldmljZXMgPSBbXTsgLy8gRm9yIGRlcHJlY2F0ZWQgb2JqZWN0c1xuICB0aGlzLmRldmljZXNQb3B1bGF0ZWQgPSBmYWxzZTtcbiAgdGhpcy5uYXRpdmVXZWJWUkF2YWlsYWJsZSA9IHRoaXMuaXNXZWJWUkF2YWlsYWJsZSgpO1xuICB0aGlzLm5hdGl2ZUxlZ2FjeVdlYlZSQXZhaWxhYmxlID0gdGhpcy5pc0RlcHJlY2F0ZWRXZWJWUkF2YWlsYWJsZSgpO1xuICB0aGlzLm5hdGl2ZUdldFZSRGlzcGxheXNGdW5jID0gdGhpcy5uYXRpdmVXZWJWUkF2YWlsYWJsZSA/XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYXZpZ2F0b3IuZ2V0VlJEaXNwbGF5cyA6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudWxsO1xuXG4gIGlmICghdGhpcy5uYXRpdmVMZWdhY3lXZWJWUkF2YWlsYWJsZSkge1xuICAgIHRoaXMuZW5hYmxlUG9seWZpbGwoKTtcbiAgICBpZiAod2luZG93LldlYlZSQ29uZmlnLkVOQUJMRV9ERVBSRUNBVEVEX0FQSSkge1xuICAgICAgdGhpcy5lbmFibGVEZXByZWNhdGVkUG9seWZpbGwoKTtcbiAgICB9XG4gIH1cblxuICAvLyBQdXQgYSBzaGltIGluIHBsYWNlIHRvIHVwZGF0ZSB0aGUgQVBJIHRvIDEuMSBpZiBuZWVkZWQuXG4gIEluc3RhbGxXZWJWUlNwZWNTaGltKCk7XG59XG5cbldlYlZSUG9seWZpbGwucHJvdG90eXBlLmlzV2ViVlJBdmFpbGFibGUgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuICgnZ2V0VlJEaXNwbGF5cycgaW4gbmF2aWdhdG9yKTtcbn07XG5cbldlYlZSUG9seWZpbGwucHJvdG90eXBlLmlzRGVwcmVjYXRlZFdlYlZSQXZhaWxhYmxlID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiAoJ2dldFZSRGV2aWNlcycgaW4gbmF2aWdhdG9yKSB8fCAoJ21vekdldFZSRGV2aWNlcycgaW4gbmF2aWdhdG9yKTtcbn07XG5cbldlYlZSUG9seWZpbGwucHJvdG90eXBlLmNvbm5lY3REaXNwbGF5ID0gZnVuY3Rpb24odnJEaXNwbGF5KSB7XG4gIHZyRGlzcGxheS5maXJlVlJEaXNwbGF5Q29ubmVjdF8oKTtcbiAgdGhpcy5kaXNwbGF5cy5wdXNoKHZyRGlzcGxheSk7XG59O1xuXG5XZWJWUlBvbHlmaWxsLnByb3RvdHlwZS5wb3B1bGF0ZURldmljZXMgPSBmdW5jdGlvbigpIHtcbiAgaWYgKHRoaXMuZGV2aWNlc1BvcHVsYXRlZCkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIC8vIEluaXRpYWxpemUgb3VyIHZpcnR1YWwgVlIgZGV2aWNlcy5cbiAgdmFyIHZyRGlzcGxheSA9IG51bGw7XG5cbiAgLy8gQWRkIGEgQ2FyZGJvYXJkIFZSRGlzcGxheSBvbiBjb21wYXRpYmxlIG1vYmlsZSBkZXZpY2VzXG4gIGlmICh0aGlzLmlzQ2FyZGJvYXJkQ29tcGF0aWJsZSgpKSB7XG4gICAgdnJEaXNwbGF5ID0gbmV3IENhcmRib2FyZFZSRGlzcGxheSgpO1xuXG4gICAgdGhpcy5jb25uZWN0RGlzcGxheSh2ckRpc3BsYXkpO1xuXG4gICAgLy8gRm9yIGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5XG4gICAgaWYgKHdpbmRvdy5XZWJWUkNvbmZpZy5FTkFCTEVfREVQUkVDQVRFRF9BUEkpIHtcbiAgICAgIHRoaXMuZGV2aWNlcy5wdXNoKG5ldyBWUkRpc3BsYXlITUREZXZpY2UodnJEaXNwbGF5KSk7XG4gICAgICB0aGlzLmRldmljZXMucHVzaChuZXcgVlJEaXNwbGF5UG9zaXRpb25TZW5zb3JEZXZpY2UodnJEaXNwbGF5KSk7XG4gICAgfVxuICB9XG5cbiAgLy8gQWRkIGEgTW91c2UgYW5kIEtleWJvYXJkIGRyaXZlbiBWUkRpc3BsYXkgZm9yIGRlc2t0b3BzL2xhcHRvcHNcbiAgaWYgKCF0aGlzLmlzTW9iaWxlKCkgJiYgIXdpbmRvdy5XZWJWUkNvbmZpZy5NT1VTRV9LRVlCT0FSRF9DT05UUk9MU19ESVNBQkxFRCkge1xuICAgIHZyRGlzcGxheSA9IG5ldyBNb3VzZUtleWJvYXJkVlJEaXNwbGF5KCk7XG4gICAgdGhpcy5jb25uZWN0RGlzcGxheSh2ckRpc3BsYXkpO1xuXG4gICAgLy8gRm9yIGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5XG4gICAgaWYgKHdpbmRvdy5XZWJWUkNvbmZpZy5FTkFCTEVfREVQUkVDQVRFRF9BUEkpIHtcbiAgICAgIHRoaXMuZGV2aWNlcy5wdXNoKG5ldyBWUkRpc3BsYXlITUREZXZpY2UodnJEaXNwbGF5KSk7XG4gICAgICB0aGlzLmRldmljZXMucHVzaChuZXcgVlJEaXNwbGF5UG9zaXRpb25TZW5zb3JEZXZpY2UodnJEaXNwbGF5KSk7XG4gICAgfVxuICB9XG5cbiAgLy8gVW5jb21tZW50IHRvIGFkZCBwb3NpdGlvbmFsIHRyYWNraW5nIHZpYSB3ZWJjYW0uXG4gIC8vaWYgKCF0aGlzLmlzTW9iaWxlKCkgJiYgd2luZG93LldlYlZSQ29uZmlnLkVOQUJMRV9ERVBSRUNBVEVEX0FQSSkge1xuICAvLyAgcG9zaXRpb25EZXZpY2UgPSBuZXcgV2ViY2FtUG9zaXRpb25TZW5zb3JWUkRldmljZSgpO1xuICAvLyAgdGhpcy5kZXZpY2VzLnB1c2gocG9zaXRpb25EZXZpY2UpO1xuICAvL31cblxuICB0aGlzLmRldmljZXNQb3B1bGF0ZWQgPSB0cnVlO1xufTtcblxuV2ViVlJQb2x5ZmlsbC5wcm90b3R5cGUuZW5hYmxlUG9seWZpbGwgPSBmdW5jdGlvbigpIHtcbiAgLy8gUHJvdmlkZSBuYXZpZ2F0b3IuZ2V0VlJEaXNwbGF5cy5cbiAgbmF2aWdhdG9yLmdldFZSRGlzcGxheXMgPSB0aGlzLmdldFZSRGlzcGxheXMuYmluZCh0aGlzKTtcblxuICAvLyBQb2x5ZmlsbCBuYXRpdmUgVlJEaXNwbGF5LmdldEZyYW1lRGF0YVxuICBpZiAodGhpcy5uYXRpdmVXZWJWUkF2YWlsYWJsZSAmJiB3aW5kb3cuVlJGcmFtZURhdGEpIHtcbiAgICB2YXIgTmF0aXZlVlJGcmFtZURhdGEgPSB3aW5kb3cuVlJGcmFtZURhdGE7XG4gICAgdmFyIG5hdGl2ZUZyYW1lRGF0YSA9IG5ldyB3aW5kb3cuVlJGcmFtZURhdGEoKTtcbiAgICB2YXIgbmF0aXZlR2V0RnJhbWVEYXRhID0gd2luZG93LlZSRGlzcGxheS5wcm90b3R5cGUuZ2V0RnJhbWVEYXRhO1xuICAgIHdpbmRvdy5WUkZyYW1lRGF0YSA9IFZSRnJhbWVEYXRhO1xuXG4gICAgd2luZG93LlZSRGlzcGxheS5wcm90b3R5cGUuZ2V0RnJhbWVEYXRhID0gZnVuY3Rpb24oZnJhbWVEYXRhKSB7XG4gICAgICBpZiAoZnJhbWVEYXRhIGluc3RhbmNlb2YgTmF0aXZlVlJGcmFtZURhdGEpIHtcbiAgICAgICAgbmF0aXZlR2V0RnJhbWVEYXRhLmNhbGwodGhpcywgZnJhbWVEYXRhKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICAvKlxuICAgICAgQ29weSBmcmFtZSBkYXRhIGZyb20gdGhlIG5hdGl2ZSBvYmplY3QgaW50byB0aGUgcG9seWZpbGxlZCBvYmplY3QuXG4gICAgICAqL1xuXG4gICAgICBuYXRpdmVHZXRGcmFtZURhdGEuY2FsbCh0aGlzLCBuYXRpdmVGcmFtZURhdGEpO1xuICAgICAgZnJhbWVEYXRhLnBvc2UgPSBuYXRpdmVGcmFtZURhdGEucG9zZTtcbiAgICAgIFV0aWwuY29weUFycmF5KG5hdGl2ZUZyYW1lRGF0YS5sZWZ0UHJvamVjdGlvbk1hdHJpeCwgZnJhbWVEYXRhLmxlZnRQcm9qZWN0aW9uTWF0cml4KTtcbiAgICAgIFV0aWwuY29weUFycmF5KG5hdGl2ZUZyYW1lRGF0YS5yaWdodFByb2plY3Rpb25NYXRyaXgsIGZyYW1lRGF0YS5yaWdodFByb2plY3Rpb25NYXRyaXgpO1xuICAgICAgVXRpbC5jb3B5QXJyYXkobmF0aXZlRnJhbWVEYXRhLmxlZnRWaWV3TWF0cml4LCBmcmFtZURhdGEubGVmdFZpZXdNYXRyaXgpO1xuICAgICAgVXRpbC5jb3B5QXJyYXkobmF0aXZlRnJhbWVEYXRhLnJpZ2h0Vmlld01hdHJpeCwgZnJhbWVEYXRhLnJpZ2h0Vmlld01hdHJpeCk7XG4gICAgICAvL3RvZG86IGNvcHlcbiAgICB9O1xuICB9XG5cbiAgLy8gUHJvdmlkZSB0aGUgYFZSRGlzcGxheWAgb2JqZWN0LlxuICB3aW5kb3cuVlJEaXNwbGF5ID0gVlJEaXNwbGF5O1xuXG4gIC8vIFByb3ZpZGUgdGhlIGBuYXZpZ2F0b3IudnJFbmFibGVkYCBwcm9wZXJ0eS5cbiAgaWYgKG5hdmlnYXRvciAmJiAhbmF2aWdhdG9yLnZyRW5hYmxlZCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkobmF2aWdhdG9yLCAndnJFbmFibGVkJywge1xuICAgICAgZ2V0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBzZWxmLmlzQ2FyZGJvYXJkQ29tcGF0aWJsZSgpICYmXG4gICAgICAgICAgICAoc2VsZi5pc0Z1bGxTY3JlZW5BdmFpbGFibGUoKSB8fCBVdGlsLmlzSU9TKCkpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgaWYgKCEoJ1ZSRnJhbWVEYXRhJyBpbiB3aW5kb3cpKSB7XG4gICAgLy8gUHJvdmlkZSB0aGUgVlJGcmFtZURhdGEgb2JqZWN0LlxuICAgIHdpbmRvdy5WUkZyYW1lRGF0YSA9IFZSRnJhbWVEYXRhO1xuICB9XG59O1xuXG5XZWJWUlBvbHlmaWxsLnByb3RvdHlwZS5lbmFibGVEZXByZWNhdGVkUG9seWZpbGwgPSBmdW5jdGlvbigpIHtcbiAgLy8gUHJvdmlkZSBuYXZpZ2F0b3IuZ2V0VlJEZXZpY2VzLlxuICBuYXZpZ2F0b3IuZ2V0VlJEZXZpY2VzID0gdGhpcy5nZXRWUkRldmljZXMuYmluZCh0aGlzKTtcblxuICAvLyBQcm92aWRlIHRoZSBDYXJkYm9hcmRITURWUkRldmljZSBhbmQgUG9zaXRpb25TZW5zb3JWUkRldmljZSBvYmplY3RzLlxuICB3aW5kb3cuSE1EVlJEZXZpY2UgPSBITURWUkRldmljZTtcbiAgd2luZG93LlBvc2l0aW9uU2Vuc29yVlJEZXZpY2UgPSBQb3NpdGlvblNlbnNvclZSRGV2aWNlO1xufTtcblxuV2ViVlJQb2x5ZmlsbC5wcm90b3R5cGUuZ2V0VlJEaXNwbGF5cyA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLnBvcHVsYXRlRGV2aWNlcygpO1xuICB2YXIgcG9seWZpbGxEaXNwbGF5cyA9IHRoaXMuZGlzcGxheXM7XG5cbiAgaWYgKCF0aGlzLm5hdGl2ZVdlYlZSQXZhaWxhYmxlKSB7XG4gICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShwb2x5ZmlsbERpc3BsYXlzKTtcbiAgfVxuXG4gIC8vIFNldCB1cCBhIHJhY2UgY29uZGl0aW9uIGlmIHRoaXMgYnJvd3NlciBoYXMgYSBidWcgd2hlcmVcbiAgLy8gYG5hdmlnYXRvci5nZXRWUkRpc3BsYXlzKClgIG5ldmVyIHJlc29sdmVzLlxuICB2YXIgdGltZW91dElkO1xuICB2YXIgdnJEaXNwbGF5c05hdGl2ZSA9IHRoaXMubmF0aXZlR2V0VlJEaXNwbGF5c0Z1bmMuY2FsbChuYXZpZ2F0b3IpO1xuICB2YXIgdGltZW91dFByb21pc2UgPSBuZXcgUHJvbWlzZShmdW5jdGlvbihyZXNvbHZlKSB7XG4gICAgdGltZW91dElkID0gc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgIGNvbnNvbGUud2FybignTmF0aXZlIFdlYlZSIGltcGxlbWVudGF0aW9uIGRldGVjdGVkLCBidXQgYGdldFZSRGlzcGxheXMoKWAgZmFpbGVkIHRvIHJlc29sdmUuIEZhbGxpbmcgYmFjayB0byBwb2x5ZmlsbC4nKTtcbiAgICAgIHJlc29sdmUoW10pO1xuICAgIH0sIHdpbmRvdy5XZWJWUkNvbmZpZy5HRVRfVlJfRElTUExBWVNfVElNRU9VVCk7XG4gIH0pO1xuXG4gIHJldHVybiBVdGlsLnJhY2UoW1xuICAgIHZyRGlzcGxheXNOYXRpdmUsXG4gICAgdGltZW91dFByb21pc2VcbiAgXSkudGhlbihmdW5jdGlvbihuYXRpdmVEaXNwbGF5cykge1xuICAgIGNsZWFyVGltZW91dCh0aW1lb3V0SWQpO1xuICAgIGlmICh3aW5kb3cuV2ViVlJDb25maWcuQUxXQVlTX0FQUEVORF9QT0xZRklMTF9ESVNQTEFZKSB7XG4gICAgICByZXR1cm4gbmF0aXZlRGlzcGxheXMuY29uY2F0KHBvbHlmaWxsRGlzcGxheXMpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gbmF0aXZlRGlzcGxheXMubGVuZ3RoID4gMCA/IG5hdGl2ZURpc3BsYXlzIDogcG9seWZpbGxEaXNwbGF5cztcbiAgICB9XG4gIH0pO1xufTtcblxuV2ViVlJQb2x5ZmlsbC5wcm90b3R5cGUuZ2V0VlJEZXZpY2VzID0gZnVuY3Rpb24oKSB7XG4gIGNvbnNvbGUud2FybignZ2V0VlJEZXZpY2VzIGlzIGRlcHJlY2F0ZWQuIFBsZWFzZSB1cGRhdGUgeW91ciBjb2RlIHRvIHVzZSBnZXRWUkRpc3BsYXlzIGluc3RlYWQuJyk7XG4gIHZhciBzZWxmID0gdGhpcztcbiAgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uKHJlc29sdmUsIHJlamVjdCkge1xuICAgIHRyeSB7XG4gICAgICBpZiAoIXNlbGYuZGV2aWNlc1BvcHVsYXRlZCkge1xuICAgICAgICBpZiAoc2VsZi5uYXRpdmVXZWJWUkF2YWlsYWJsZSkge1xuICAgICAgICAgIHJldHVybiBuYXZpZ2F0b3IuZ2V0VlJEaXNwbGF5cyhmdW5jdGlvbihkaXNwbGF5cykge1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBkaXNwbGF5cy5sZW5ndGg7ICsraSkge1xuICAgICAgICAgICAgICBzZWxmLmRldmljZXMucHVzaChuZXcgVlJEaXNwbGF5SE1ERGV2aWNlKGRpc3BsYXlzW2ldKSk7XG4gICAgICAgICAgICAgIHNlbGYuZGV2aWNlcy5wdXNoKG5ldyBWUkRpc3BsYXlQb3NpdGlvblNlbnNvckRldmljZShkaXNwbGF5c1tpXSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc2VsZi5kZXZpY2VzUG9wdWxhdGVkID0gdHJ1ZTtcbiAgICAgICAgICAgIHJlc29sdmUoc2VsZi5kZXZpY2VzKTtcbiAgICAgICAgICB9LCByZWplY3QpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHNlbGYubmF0aXZlTGVnYWN5V2ViVlJBdmFpbGFibGUpIHtcbiAgICAgICAgICByZXR1cm4gKG5hdmlnYXRvci5nZXRWUkREZXZpY2VzIHx8IG5hdmlnYXRvci5tb3pHZXRWUkRldmljZXMpKGZ1bmN0aW9uKGRldmljZXMpIHtcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgZGV2aWNlcy5sZW5ndGg7ICsraSkge1xuICAgICAgICAgICAgICBpZiAoZGV2aWNlc1tpXSBpbnN0YW5jZW9mIEhNRFZSRGV2aWNlKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5kZXZpY2VzLnB1c2goZGV2aWNlc1tpXSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgaWYgKGRldmljZXNbaV0gaW5zdGFuY2VvZiBQb3NpdGlvblNlbnNvclZSRGV2aWNlKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5kZXZpY2VzLnB1c2goZGV2aWNlc1tpXSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHNlbGYuZGV2aWNlc1BvcHVsYXRlZCA9IHRydWU7XG4gICAgICAgICAgICByZXNvbHZlKHNlbGYuZGV2aWNlcyk7XG4gICAgICAgICAgfSwgcmVqZWN0KTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBzZWxmLnBvcHVsYXRlRGV2aWNlcygpO1xuICAgICAgcmVzb2x2ZShzZWxmLmRldmljZXMpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHJlamVjdChlKTtcbiAgICB9XG4gIH0pO1xufTtcblxuV2ViVlJQb2x5ZmlsbC5wcm90b3R5cGUuTmF0aXZlVlJGcmFtZURhdGEgPSB3aW5kb3cuVlJGcmFtZURhdGE7XG5cbi8qKlxuICogRGV0ZXJtaW5lIGlmIGEgZGV2aWNlIGlzIG1vYmlsZS5cbiAqL1xuV2ViVlJQb2x5ZmlsbC5wcm90b3R5cGUuaXNNb2JpbGUgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIC9BbmRyb2lkL2kudGVzdChuYXZpZ2F0b3IudXNlckFnZW50KSB8fFxuICAgICAgL2lQaG9uZXxpUGFkfGlQb2QvaS50ZXN0KG5hdmlnYXRvci51c2VyQWdlbnQpO1xufTtcblxuV2ViVlJQb2x5ZmlsbC5wcm90b3R5cGUuaXNDYXJkYm9hcmRDb21wYXRpYmxlID0gZnVuY3Rpb24oKSB7XG4gIC8vIEZvciBub3csIHN1cHBvcnQgYWxsIGlPUyBhbmQgQW5kcm9pZCBkZXZpY2VzLlxuICAvLyBBbHNvIGVuYWJsZSB0aGUgV2ViVlJDb25maWcuRk9SQ0VfVlIgZmxhZyBmb3IgZGVidWdnaW5nLlxuICByZXR1cm4gdGhpcy5pc01vYmlsZSgpIHx8IHdpbmRvdy5XZWJWUkNvbmZpZy5GT1JDRV9FTkFCTEVfVlI7XG59O1xuXG5XZWJWUlBvbHlmaWxsLnByb3RvdHlwZS5pc0Z1bGxTY3JlZW5BdmFpbGFibGUgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIChkb2N1bWVudC5mdWxsc2NyZWVuRW5hYmxlZCB8fFxuICAgICAgICAgIGRvY3VtZW50Lm1vekZ1bGxTY3JlZW5FbmFibGVkIHx8XG4gICAgICAgICAgZG9jdW1lbnQud2Via2l0RnVsbHNjcmVlbkVuYWJsZWQgfHxcbiAgICAgICAgICBmYWxzZSk7XG59O1xuXG4vLyBJbnN0YWxscyBhIHNoaW0gdGhhdCB1cGRhdGVzIGEgV2ViVlIgMS4wIHNwZWMgaW1wbGVtZW50YXRpb24gdG8gV2ViVlIgMS4xXG5mdW5jdGlvbiBJbnN0YWxsV2ViVlJTcGVjU2hpbSgpIHtcbiAgaWYgKCdWUkRpc3BsYXknIGluIHdpbmRvdyAmJiAhKCdWUkZyYW1lRGF0YScgaW4gd2luZG93KSkge1xuICAgIC8vIFByb3ZpZGUgdGhlIFZSRnJhbWVEYXRhIG9iamVjdC5cbiAgICB3aW5kb3cuVlJGcmFtZURhdGEgPSBWUkZyYW1lRGF0YTtcblxuICAgIC8vIEEgbG90IG9mIENocm9tZSBidWlsZHMgZG9uJ3QgaGF2ZSBkZXB0aE5lYXIgYW5kIGRlcHRoRmFyLCBldmVuXG4gICAgLy8gdGhvdWdoIHRoZXkncmUgaW4gdGhlIFdlYlZSIDEuMCBzcGVjLiBQYXRjaCB0aGVtIGluIGlmIHRoZXkncmUgbm90IHByZXNlbnQuXG4gICAgaWYoISgnZGVwdGhOZWFyJyBpbiB3aW5kb3cuVlJEaXNwbGF5LnByb3RvdHlwZSkpIHtcbiAgICAgIHdpbmRvdy5WUkRpc3BsYXkucHJvdG90eXBlLmRlcHRoTmVhciA9IDAuMDE7XG4gICAgfVxuXG4gICAgaWYoISgnZGVwdGhGYXInIGluIHdpbmRvdy5WUkRpc3BsYXkucHJvdG90eXBlKSkge1xuICAgICAgd2luZG93LlZSRGlzcGxheS5wcm90b3R5cGUuZGVwdGhGYXIgPSAxMDAwMC4wO1xuICAgIH1cblxuICAgIHdpbmRvdy5WUkRpc3BsYXkucHJvdG90eXBlLmdldEZyYW1lRGF0YSA9IGZ1bmN0aW9uKGZyYW1lRGF0YSkge1xuICAgICAgcmV0dXJuIFV0aWwuZnJhbWVEYXRhRnJvbVBvc2UoZnJhbWVEYXRhLCB0aGlzLmdldFBvc2UoKSwgdGhpcyk7XG4gICAgfVxuICB9XG59O1xuXG5XZWJWUlBvbHlmaWxsLkluc3RhbGxXZWJWUlNwZWNTaGltID0gSW5zdGFsbFdlYlZSU3BlY1NoaW07XG5XZWJWUlBvbHlmaWxsLnZlcnNpb24gPSB2ZXJzaW9uO1xuXG5tb2R1bGUuZXhwb3J0cy5XZWJWUlBvbHlmaWxsID0gV2ViVlJQb2x5ZmlsbDtcbiIsImdsb2JhbC5USFJFRSA9IHJlcXVpcmUoJ3RocmVlJyk7XHJcbnJlcXVpcmUoJ3RocmVlL2V4YW1wbGVzL2pzL2NvbnRyb2xzL1ZSQ29udHJvbHMuanMnKTtcclxucmVxdWlyZSgndGhyZWUvZXhhbXBsZXMvanMvZWZmZWN0cy9WUkVmZmVjdC5qcycpO1xyXG5nbG9iYWwuV2ViVlJDb25maWcgPSByZXF1aXJlKCcuL3dlYnZyLmNvbmZpZy5qcycpO1xyXG52YXIgV2ViVnJQb2x5ZmlsbCA9IHJlcXVpcmUoJ3dlYnZyLXBvbHlmaWxsJyk7XHJcbnZhciBXZWJWUk1hbmFnZXIgPSByZXF1aXJlKCd3ZWJ2ci1ib2lsZXJwbGF0ZScpO1xyXG53aW5kb3cuV2ViVlJNYW5hZ2VyID0gV2ViVlJNYW5hZ2VyO1xyXG5yZXF1aXJlKCcuL3ZpZGVvanMudnIuanMnKTtcclxuIiwiLyohIHZpZGVvanMtdnIgLSB2MC4yLjAgLSAyMDE2LTA5LTEwICovXHJcbi8qXHJcbiAqIHZyXHJcbiAqIGh0dHBzOi8vZ2l0aHViLmNvbS9NZXRhQ0ROL3ZpZGVvanMtdnJcclxuICpcclxuICogQ29weXJpZ2h0IChjKSAyMDE0IFNlYW4gTGF3cmVuY2VcclxuICogQ29weXJpZ2h0IChjKSAyMDE1IEphbWVzIEJyb2JlcmdcclxuICogQ29weXJpZ2h0IChjKSAyMDE2IEJyaWdodGNvdmUgSW5jLiwgSGFwWWFrIEluYy4sIE1ldGFDRE4gUHR5LiBMdGQuXHJcbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cclxuICovXHJcbihmdW5jdGlvbih2anMpIHtcclxuICAgIHZhciB1dGlscyA9IHtcclxuICAgICAgICBicm93c2VyOiB7XHJcbiAgICAgICAgICAgIGlzTW9iaWxlOiBmdW5jdGlvbigpIHtcclxuICAgICAgICAgICAgICAgIHZhciBjaGVjayA9IGZhbHNlO1xyXG4gICAgICAgICAgICAgICAgKGZ1bmN0aW9uKGEpe2lmKC8oYW5kcm9pZHxiYlxcZCt8bWVlZ28pLittb2JpbGV8YXZhbnRnb3xiYWRhXFwvfGJsYWNrYmVycnl8YmxhemVyfGNvbXBhbHxlbGFpbmV8ZmVubmVjfGhpcHRvcHxpZW1vYmlsZXxpcChob25lfG9kKXxpcmlzfGtpbmRsZXxsZ2UgfG1hZW1vfG1pZHB8bW1wfG1vYmlsZS4rZmlyZWZveHxuZXRmcm9udHxvcGVyYSBtKG9ifGluKWl8cGFsbSggb3MpP3xwaG9uZXxwKGl4aXxyZSlcXC98cGx1Y2tlcnxwb2NrZXR8cHNwfHNlcmllcyg0fDYpMHxzeW1iaWFufHRyZW98dXBcXC4oYnJvd3NlcnxsaW5rKXx2b2RhZm9uZXx3YXB8d2luZG93cyBjZXx4ZGF8eGlpbm8vaS50ZXN0KGEpfHwvMTIwN3w2MzEwfDY1OTB8M2dzb3w0dGhwfDUwWzEtNl1pfDc3MHN8ODAyc3xhIHdhfGFiYWN8YWMoZXJ8b298c1xcLSl8YWkoa298cm4pfGFsKGF2fGNhfGNvKXxhbW9pfGFuKGV4fG55fHl3KXxhcHR1fGFyKGNofGdvKXxhcyh0ZXx1cyl8YXR0d3xhdShkaXxcXC1tfHIgfHMgKXxhdmFufGJlKGNrfGxsfG5xKXxiaShsYnxyZCl8YmwoYWN8YXopfGJyKGV8dil3fGJ1bWJ8YndcXC0obnx1KXxjNTVcXC98Y2FwaXxjY3dhfGNkbVxcLXxjZWxsfGNodG18Y2xkY3xjbWRcXC18Y28obXB8bmQpfGNyYXd8ZGEoaXR8bGx8bmcpfGRidGV8ZGNcXC1zfGRldml8ZGljYXxkbW9ifGRvKGN8cClvfGRzKDEyfFxcLWQpfGVsKDQ5fGFpKXxlbShsMnx1bCl8ZXIoaWN8azApfGVzbDh8ZXooWzQtN10wfG9zfHdhfHplKXxmZXRjfGZseShcXC18Xyl8ZzEgdXxnNTYwfGdlbmV8Z2ZcXC01fGdcXC1tb3xnbyhcXC53fG9kKXxncihhZHx1bil8aGFpZXxoY2l0fGhkXFwtKG18cHx0KXxoZWlcXC18aGkocHR8dGEpfGhwKCBpfGlwKXxoc1xcLWN8aHQoYyhcXC18IHxffGF8Z3xwfHN8dCl8dHApfGh1KGF3fHRjKXxpXFwtKDIwfGdvfG1hKXxpMjMwfGlhYyggfFxcLXxcXC8pfGlicm98aWRlYXxpZzAxfGlrb218aW0xa3xpbm5vfGlwYXF8aXJpc3xqYSh0fHYpYXxqYnJvfGplbXV8amlnc3xrZGRpfGtlaml8a2d0KCB8XFwvKXxrbG9ufGtwdCB8a3djXFwtfGt5byhjfGspfGxlKG5vfHhpKXxsZyggZ3xcXC8oa3xsfHUpfDUwfDU0fFxcLVthLXddKXxsaWJ3fGx5bnh8bTFcXC13fG0zZ2F8bTUwXFwvfG1hKHRlfHVpfHhvKXxtYygwMXwyMXxjYSl8bVxcLWNyfG1lKHJjfHJpKXxtaShvOHxvYXx0cyl8bW1lZnxtbygwMXwwMnxiaXxkZXxkb3x0KFxcLXwgfG98dil8enopfG10KDUwfHAxfHYgKXxtd2JwfG15d2F8bjEwWzAtMl18bjIwWzItM118bjMwKDB8Mil8bjUwKDB8Mnw1KXxuNygwKDB8MSl8MTApfG5lKChjfG0pXFwtfG9ufHRmfHdmfHdnfHd0KXxub2soNnxpKXxuenBofG8yaW18b3AodGl8d3YpfG9yYW58b3dnMXxwODAwfHBhbihhfGR8dCl8cGR4Z3xwZygxM3xcXC0oWzEtOF18YykpfHBoaWx8cGlyZXxwbChheXx1Yyl8cG5cXC0yfHBvKGNrfHJ0fHNlKXxwcm94fHBzaW98cHRcXC1nfHFhXFwtYXxxYygwN3wxMnwyMXwzMnw2MHxcXC1bMi03XXxpXFwtKXxxdGVrfHIzODB8cjYwMHxyYWtzfHJpbTl8cm8odmV8em8pfHM1NVxcL3xzYShnZXxtYXxtbXxtc3xueXx2YSl8c2MoMDF8aFxcLXxvb3xwXFwtKXxzZGtcXC98c2UoYyhcXC18MHwxKXw0N3xtY3xuZHxyaSl8c2doXFwtfHNoYXJ8c2llKFxcLXxtKXxza1xcLTB8c2woNDV8aWQpfHNtKGFsfGFyfGIzfGl0fHQ1KXxzbyhmdHxueSl8c3AoMDF8aFxcLXx2XFwtfHYgKXxzeSgwMXxtYil8dDIoMTh8NTApfHQ2KDAwfDEwfDE4KXx0YShndHxsayl8dGNsXFwtfHRkZ1xcLXx0ZWwoaXxtKXx0aW1cXC18dFxcLW1vfHRvKHBsfHNoKXx0cyg3MHxtXFwtfG0zfG01KXx0eFxcLTl8dXAoXFwuYnxnMXxzaSl8dXRzdHx2NDAwfHY3NTB8dmVyaXx2aShyZ3x0ZSl8dmsoNDB8NVswLTNdfFxcLXYpfHZtNDB8dm9kYXx2dWxjfHZ4KDUyfDUzfDYwfDYxfDcwfDgwfDgxfDgzfDg1fDk4KXx3M2MoXFwtfCApfHdlYmN8d2hpdHx3aShnIHxuY3xudyl8d21sYnx3b251fHg3MDB8eWFzXFwtfHlvdXJ8emV0b3x6dGVcXC0vaS50ZXN0KGEuc3Vic3RyKDAsNCkpKWNoZWNrID0gdHJ1ZX0pKG5hdmlnYXRvci51c2VyQWdlbnR8fG5hdmlnYXRvci52ZW5kb3J8fHdpbmRvdy5vcGVyYSk7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gY2hlY2s7XHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIGlzSU9TOiBmdW5jdGlvbiAoKSB7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gLyhpUGFkfGlQaG9uZXxpUG9kKS9nLnRlc3QobmF2aWdhdG9yLnVzZXJBZ2VudCk7XHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIGlzU2FmYXJpOiBmdW5jdGlvbigpe1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIC9eKCg/IWNocm9tZXxhbmRyb2lkKS4pKnNhZmFyaS9pLnRlc3QobmF2aWdhdG9yLnVzZXJBZ2VudCk7XHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIC8vd2UgbmVlZCB0aGlzIGFzIElFIDExIHJlcG9ydHMgdGhhdCBpdCBoYXMgYSBWUiBkaXNwbGF5LCBidXQgaXNudCBjb21wYXRpYmxlIHdpdGggVmlkZW8gYXMgYSBUZXh0dXJlLiBmb3IgZXhhbXBsZVxyXG4gICAgICAgICAgICBpc0Jyb3dzZXJDb21wYXRpYmxlOiBmdW5jdGlvbiAoKSB7XHJcbiAgICAgICAgICAgICAgICB2YXIgdWEgPSB3aW5kb3cubmF2aWdhdG9yLnVzZXJBZ2VudDtcclxuXHJcbiAgICAgICAgICAgICAgICB2YXIgbXNpZSA9IHVhLmluZGV4T2YoJ01TSUUgJyk7XHJcbiAgICAgICAgICAgICAgICBpZiAobXNpZSA+IDApIHtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgdmFyIHRyaWRlbnQgPSB1YS5pbmRleE9mKCdUcmlkZW50LycpO1xyXG4gICAgICAgICAgICAgICAgaWYgKHRyaWRlbnQgPiAwKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgIC8vIE1TIEVkZ2UgYW5kIG90aGVyIGJyb3dzZXJzXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBDb3BpZXMgcHJvcGVydGllcyBmcm9tIG9uZSBvciBtb3JlIG9iamVjdHMgb250byBhbiBvcmlnaW5hbC5cclxuICAgICAqL1xyXG4gICAgdmFyIGV4dGVuZCA9IGZ1bmN0aW9uKG9iaiAvKiwgYXJnMSwgYXJnMiwgLi4uICovKSB7XHJcbiAgICAgICAgdmFyIGFyZywgaSwgaztcclxuICAgICAgICBmb3IgKGkgPSAxOyBpIDwgYXJndW1lbnRzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgICAgIGFyZyA9IGFyZ3VtZW50c1tpXTtcclxuICAgICAgICAgICAgZm9yIChrIGluIGFyZykge1xyXG4gICAgICAgICAgICAgICAgaWYgKGFyZy5oYXNPd25Qcm9wZXJ0eShrKSkge1xyXG4gICAgICAgICAgICAgICAgICAgIG9ialtrXSA9IGFyZ1trXTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gb2JqO1xyXG4gICAgfSxcclxuXHJcbiAgICBwcm9qZWN0aW9ucyA9IFtcIjM2MFwiLCBcIjM2MF9MUlwiLCBcIjM2MF9UQlwiLCBcIjM2MF9DVUJFXCIsIFwiTk9ORVwiLCBcIkFVVE9cIl0sXHJcblxyXG4gICAgZGVmYXVsdHMgPSB7XHJcbiAgICAgICAgcHJvamVjdGlvbjogXCJBVVRPXCIsXHJcbiAgICAgICAgZGVidWc6IGZhbHNlXHJcbiAgICB9LFxyXG5cclxuICAgIC8qKlxyXG4gICAgICogSW5pdGlhbGl6ZXMgdGhlIHBsdWdpblxyXG4gICAgICovXHJcbiAgICBwbHVnaW4gPSBmdW5jdGlvbihvcHRpb25zKSB7XHJcbiAgICAgICAgLy92YXJzIGdsb2JhbCAodmlhIGNsb3N1cmUpIHRvIHRoZSBwbHVnaW5cclxuICAgICAgICB2YXIgcGxheWVyID0gdGhpcyxcclxuICAgICAgICAgICAgc2V0dGluZ3MgPSBleHRlbmQoe30sIGRlZmF1bHRzLCBvcHRpb25zIHx8IHt9KSxcclxuICAgICAgICAgICAgdmlkZW9FbCA9IHRoaXMuZWwoKS5nZXRFbGVtZW50c0J5VGFnTmFtZSgndmlkZW8nKVswXSxcclxuICAgICAgICAgICAgY29udGFpbmVyID0gdGhpcy5lbCgpLFxyXG4gICAgICAgICAgICBjdXJyZW50X3Byb2ogPSBzZXR0aW5ncy5wcm9qZWN0aW9uLFxyXG4gICAgICAgICAgICBtb3ZpZU1hdGVyaWFsLFxyXG4gICAgICAgICAgICBtb3ZpZUdlb21ldHJ5LFxyXG4gICAgICAgICAgICBtb3ZpZVNjcmVlbixcclxuICAgICAgICAgICAgY29udHJvbHMzZCxcclxuICAgICAgICAgICAgc2NlbmUsXHJcbiAgICAgICAgICAgIGNhbWVyYVZlY3RvcixcclxuICAgICAgICAgICAgeCxcclxuICAgICAgICAgICAgeSxcclxuICAgICAgICAgICAgejtcclxuXHJcbiAgICAgICAgdmFyIGxvZyA9IGZ1bmN0aW9uKG1zZyl7XHJcbiAgICAgICAgICAgIGlmKHNldHRpbmdzLmRlYnVnKSB7XHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhtc2cpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgZnVuY3Rpb24gcmVnaXN0ZXJFcnJvck1lc3NhZ2VzKCkge1xyXG4gICAgICAgICAgcGxheWVyLmVycm9ycyh7XHJcbiAgICAgICAgICAgICAgXCJlcnJvcnNcIjoge1xyXG4gICAgICAgICAgICAgICAgICBcIndlYi12ci1uby1kZXZpY2VzLWZvdW5kXCI6IHtcclxuICAgICAgICAgICAgICAgICAgICAgIFwiaGVhZGxpbmVcIjogXCJObyAzNjAgZGV2aWNlcyBmb3VuZFwiLFxyXG4gICAgICAgICAgICAgICAgICAgICAgXCJ0eXBlXCI6IFwiMzYwX05PX0RFVklDRVNfRk9VTkRcIixcclxuICAgICAgICAgICAgICAgICAgICAgIFwibWVzc2FnZVwiOiBcIllvdXIgYnJvd3NlciBzdXBwb3J0cyAzNjAsIGJ1dCBubyAzNjAgZGlzcGxheXMgZm91bmQuXCJcclxuICAgICAgICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgICAgICAgXCJ3ZWItdnItb3V0LW9mLWRhdGVcIjoge1xyXG4gICAgICAgICAgICAgICAgICAgICAgXCJoZWFkbGluZVwiOiBcIjM2MCBpcyBvdXQgb2YgZGF0ZVwiLFxyXG4gICAgICAgICAgICAgICAgICAgICAgXCJ0eXBlXCI6IFwiMzYwX09VVF9PRl9EQVRFXCIsXHJcbiAgICAgICAgICAgICAgICAgICAgICBcIm1lc3NhZ2VcIjogXCJZb3VyIGJyb3dzZXIgc3VwcG9ydHMgMzYwIGJ1dCBub3QgdGhlIGxhdGVzdCB2ZXJzaW9uLiBTZWUgPGEgaHJlZj0naHR0cDovL3dlYnZyLmluZm8nPndlYnZyLmluZm88L2E+IGZvciBtb3JlIGluZm8uXCJcclxuICAgICAgICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgICAgICAgXCJ3ZWItdnItbm90LXN1cHBvcnRlZFwiOiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICBcImhlYWRsaW5lXCI6IFwiMzYwIG5vdCBzdXBwb3J0ZWQgb24gdGhpcyBkZXZpY2VcIixcclxuICAgICAgICAgICAgICAgICAgICAgIFwidHlwZVwiOiBcIjM2MF9OT1RfU1VQUE9SVEVEXCIsXHJcbiAgICAgICAgICAgICAgICAgICAgICBcIm1lc3NhZ2VcIjogXCJZb3VyIGJyb3dzZXIgZG9lcyBub3Qgc3VwcG9ydCAzNjAuIFNlZSA8YSBocmVmPSdodHRwOi8vd2VidnIuaW5mbyc+d2VidnIuaW5mbzwvYT4gZm9yIGFzc2lzdGFuY2UuXCJcclxuICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgIH0pO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmVnaXN0ZXJFcnJvck1lc3NhZ2VzKCk7XHJcblxyXG4gICAgICAgIGlmICh2aWRlb0VsID09IHVuZGVmaW5lZCB8fCB2aWRlb0VsID09IG51bGwpIHtcclxuICAgICAgICAgICAgLy8gUGxheWVyIGlzIG5vdCB1c2luZyBIVE1MNSB0ZWNoLCBzbyBkb24ndCBpbml0IGl0LlxyXG4gICAgICAgICAgICByZXR1cm4gO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYgKGN1cnJlbnRfcHJvaiA9PT0gXCJOT05FXCIpIHtcclxuICAgICAgICAgICAvLyBTaG93IHJhdyAzNjAgdmlkZW8uXHJcbiAgICAgICAgICAgcmV0dXJuIDtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGZ1bmN0aW9uIGNoYW5nZVByb2plY3Rpb24ocHJvamVjdGlvbikge1xyXG4gICAgICAgICAgICB2YXIgcG9zaXRpb24gPSB7eDowLCB5OjAsIHo6MCB9O1xyXG4gICAgICAgICAgICBpZiAoc2NlbmUpIHtcclxuICAgICAgICAgICAgICAgIHNjZW5lLnJlbW92ZShtb3ZpZVNjcmVlbik7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgaWYgKHByb2plY3Rpb24gPT09IFwiQVVUT1wiKXtcclxuICAgICAgICAgICAgICAgIGlmKHBsYXllci5tZWRpYWluZm8gJiYgcGxheWVyLm1lZGlhaW5mby5wcm9qZWN0aW9uICYmIHBsYXllci5tZWRpYWluZm8ucHJvamVjdGlvbiA9PT0gXCJlcXVpcmVjdGFuZ3VsYXJcIil7XHJcbiAgICAgICAgICAgICAgICAgICAgY3VycmVudF9wcm9qID0gXCIzNjBcIjtcclxuICAgICAgICAgICAgICAgICAgICBjaGFuZ2VQcm9qZWN0aW9uKFwiMzYwXCIpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgZWxzZXtcclxuICAgICAgICAgICAgICAgICAgICBjdXJyZW50X3Byb2ogPSBcIk5PTkVcIjtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBlbHNlIGlmIChwcm9qZWN0aW9uID09PSBcIjM2MFwiKSB7XHJcbiAgICAgICAgICAgICAgICBtb3ZpZUdlb21ldHJ5ID0gbmV3IFRIUkVFLlNwaGVyZUJ1ZmZlckdlb21ldHJ5KCAyNTYsIDMyLCAzMiApO1xyXG5cclxuICAgICAgICAgICAgICAgIG1vdmllU2NyZWVuID0gbmV3IFRIUkVFLk1lc2goIG1vdmllR2VvbWV0cnksIG1vdmllTWF0ZXJpYWwgKTtcclxuICAgICAgICAgICAgICAgIG1vdmllU2NyZWVuLnBvc2l0aW9uLnNldChwb3NpdGlvbi54LCBwb3NpdGlvbi55LCBwb3NpdGlvbi56KTtcclxuXHJcbiAgICAgICAgICAgICAgICBtb3ZpZVNjcmVlbi5zY2FsZS54ID0gLTE7XHJcbiAgICAgICAgICAgICAgICBtb3ZpZVNjcmVlbi5xdWF0ZXJuaW9uLnNldEZyb21BeGlzQW5nbGUoe3g6IDAsIHk6IDEsIHo6IDB9LCAtTWF0aC5QSSAvIDIpO1xyXG4gICAgICAgICAgICAgICAgc2NlbmUuYWRkKG1vdmllU2NyZWVuKTtcclxuXHJcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoKHByb2plY3Rpb24gPT09IFwiMzYwX0xSXCIpIHx8IChwcm9qZWN0aW9uID09PSBcIjM2MF9UQlwiKSkge1xyXG4gICAgICAgICAgICAgICAgdmFyIGdlb21ldHJ5ID0gbmV3IFRIUkVFLlNwaGVyZUdlb21ldHJ5KCAyNTYsIDMyLCAzMiApO1xyXG5cclxuICAgICAgICAgICAgICAgIC8vIExlZnQgZXllIHZpZXdcclxuICAgICAgICAgICAgICAgIGdlb21ldHJ5LnNjYWxlKCAtIDEsIDEsIDEgKTtcclxuICAgICAgICAgICAgICAgIHZhciB1dnMgPSBnZW9tZXRyeS5mYWNlVmVydGV4VXZzWyAwIF07XHJcbiAgICAgICAgICAgICAgICBmb3IgKCB2YXIgaSA9IDA7IGkgPCB1dnMubGVuZ3RoOyBpICsrICkge1xyXG4gICAgICAgICAgICAgICAgICAgIGZvciAoIHZhciBqID0gMDsgaiA8IDM7IGogKysgKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChwcm9qZWN0aW9uID09PSBcIjM2MF9MUlwiKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1dnNbIGkgXVsgaiBdLnggKj0gMC41O1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdXZzWyBpIF1bIGogXS55ICo9IDAuNTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICBtb3ZpZUdlb21ldHJ5ID0gbmV3IFRIUkVFLkJ1ZmZlckdlb21ldHJ5KCkuZnJvbUdlb21ldHJ5KCBnZW9tZXRyeSApO1xyXG4gICAgICAgICAgICAgICAgbW92aWVTY3JlZW4gPSBuZXcgVEhSRUUuTWVzaCggbW92aWVHZW9tZXRyeSwgbW92aWVNYXRlcmlhbCApO1xyXG4gICAgICAgICAgICAgICAgbW92aWVTY3JlZW4ucm90YXRpb24ueSA9IC0gTWF0aC5QSSAvIDI7XHJcbiAgICAgICAgICAgICAgICBtb3ZpZVNjcmVlbi5sYXllcnMuc2V0KCAxICk7IC8vIGRpc3BsYXkgaW4gbGVmdCBleWUgb25seVxyXG4gICAgICAgICAgICAgICAgc2NlbmUuYWRkKCBtb3ZpZVNjcmVlbiApO1xyXG5cclxuICAgICAgICAgICAgICAgIC8vIFJpZ2h0IGV5ZSB2aWV3XHJcbiAgICAgICAgICAgICAgICB2YXIgZ2VvbWV0cnkgPSBuZXcgVEhSRUUuU3BoZXJlR2VvbWV0cnkoIDI1NiwgMzIsIDMyICk7XHJcbiAgICAgICAgICAgICAgICBnZW9tZXRyeS5zY2FsZSggLSAxLCAxLCAxICk7XHJcbiAgICAgICAgICAgICAgICB2YXIgdXZzID0gZ2VvbWV0cnkuZmFjZVZlcnRleFV2c1sgMCBdO1xyXG5cclxuICAgICAgICAgICAgICAgIGZvciAoIHZhciBpID0gMDsgaSA8IHV2cy5sZW5ndGg7IGkgKysgKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgZm9yICggdmFyIGogPSAwOyBqIDwgMzsgaiArKyApIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHByb2plY3Rpb24gPT09IFwiMzYwX0xSXCIpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHV2c1sgaSBdWyBqIF0ueCAqPSAwLjU7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1dnNbIGkgXVsgaiBdLnggKz0gMC41O1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdXZzWyBpIF1bIGogXS55ICo9IDAuNTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHV2c1sgaSBdWyBqIF0ueSArPSAwLjU7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgbW92aWVHZW9tZXRyeSA9IG5ldyBUSFJFRS5CdWZmZXJHZW9tZXRyeSgpLmZyb21HZW9tZXRyeSggZ2VvbWV0cnkgKTtcclxuICAgICAgICAgICAgICAgIG1vdmllU2NyZWVuID0gbmV3IFRIUkVFLk1lc2goIG1vdmllR2VvbWV0cnksIG1vdmllTWF0ZXJpYWwgKTtcclxuICAgICAgICAgICAgICAgIG1vdmllU2NyZWVuLnJvdGF0aW9uLnkgPSAtIE1hdGguUEkgLyAyO1xyXG4gICAgICAgICAgICAgICAgbW92aWVTY3JlZW4ubGF5ZXJzLnNldCggMiApOyAvLyBkaXNwbGF5IGluIHJpZ2h0IGV5ZSBvbmx5XHJcbiAgICAgICAgICAgICAgICBzY2VuZS5hZGQoIG1vdmllU2NyZWVuICk7XHJcblxyXG4gICAgICAgICAgICB9IGVsc2UgaWYgKHByb2plY3Rpb24gPT09IFwiMzYwX0NVQkVcIikge1xyXG4gICAgICAgICAgICAgICAgLy8gQ3VycmVudGx5IGRvZXNuJ3Qgd29yayAtIG5lZWQgdG8gZmlndXJlIG91dCBvcmRlciBvZiBjdWJlIGZhY2VzXHJcbiAgICAgICAgICAgICAgICBtb3ZpZUdlb21ldHJ5ID0gbmV3IFRIUkVFLkN1YmVHZW9tZXRyeSggMjU2LCAyNTYsIDI1NiApO1xyXG4gICAgICAgICAgICAgICAgdmFyIGZhY2UxID0gW25ldyBUSFJFRS5WZWN0b3IyKDAsIC41KSwgbmV3IFRIUkVFLlZlY3RvcjIoLjMzMywgLjUpLCBuZXcgVEhSRUUuVmVjdG9yMiguMzMzLCAxKSwgbmV3IFRIUkVFLlZlY3RvcjIoMCwgMSldO1xyXG4gICAgICAgICAgICAgICAgdmFyIGZhY2UyID0gW25ldyBUSFJFRS5WZWN0b3IyKC4zMzMsIC41KSwgbmV3IFRIUkVFLlZlY3RvcjIoLjY2NiwgLjUpLCBuZXcgVEhSRUUuVmVjdG9yMiguNjY2LCAxKSwgbmV3IFRIUkVFLlZlY3RvcjIoLjMzMywgMSldO1xyXG4gICAgICAgICAgICAgICAgdmFyIGZhY2UzID0gW25ldyBUSFJFRS5WZWN0b3IyKC42NjYsIC41KSwgbmV3IFRIUkVFLlZlY3RvcjIoMSwgLjUpLCBuZXcgVEhSRUUuVmVjdG9yMigxLCAxKSwgbmV3IFRIUkVFLlZlY3RvcjIoMC42NjYsIDEpXTtcclxuICAgICAgICAgICAgICAgIHZhciBmYWNlNCA9IFtuZXcgVEhSRUUuVmVjdG9yMigwLCAwKSwgbmV3IFRIUkVFLlZlY3RvcjIoLjMzMywgMSksIG5ldyBUSFJFRS5WZWN0b3IyKC4zMzMsIC41KSwgbmV3IFRIUkVFLlZlY3RvcjIoMCwgLjUpXTtcclxuICAgICAgICAgICAgICAgIHZhciBmYWNlNSA9IFtuZXcgVEhSRUUuVmVjdG9yMiguMzMzLCAxKSwgbmV3IFRIUkVFLlZlY3RvcjIoLjY2NiwgMSksIG5ldyBUSFJFRS5WZWN0b3IyKC42NjYsIC41KSwgbmV3IFRIUkVFLlZlY3RvcjIoLjMzMywgLjUpXTtcclxuICAgICAgICAgICAgICAgIHZhciBmYWNlNiA9IFtuZXcgVEhSRUUuVmVjdG9yMiguNjY2LCAxKSwgbmV3IFRIUkVFLlZlY3RvcjIoMSwgMCksIG5ldyBUSFJFRS5WZWN0b3IyKDEsIC41KSwgbmV3IFRIUkVFLlZlY3RvcjIoLjY2NiwgLjUpXTtcclxuXHJcbiAgICAgICAgICAgICAgICBtb3ZpZUdlb21ldHJ5LmZhY2VWZXJ0ZXhVdnNbMF0gPSBbXTtcclxuXHJcbiAgICAgICAgICAgICAgICBtb3ZpZUdlb21ldHJ5LmZhY2VWZXJ0ZXhVdnNbMF1bMF0gPSBbIGZhY2UxWzBdLCBmYWNlMVsxXSwgZmFjZTFbM10gXTtcclxuICAgICAgICAgICAgICAgIG1vdmllR2VvbWV0cnkuZmFjZVZlcnRleFV2c1swXVsxXSA9IFsgZmFjZTFbMV0sIGZhY2UxWzJdLCBmYWNlMVszXSBdO1xyXG5cclxuICAgICAgICAgICAgICAgIG1vdmllR2VvbWV0cnkuZmFjZVZlcnRleFV2c1swXVsyXSA9IFsgZmFjZTJbMF0sIGZhY2UyWzFdLCBmYWNlMlszXSBdO1xyXG4gICAgICAgICAgICAgICAgbW92aWVHZW9tZXRyeS5mYWNlVmVydGV4VXZzWzBdWzNdID0gWyBmYWNlMlsxXSwgZmFjZTJbMl0sIGZhY2UyWzNdIF07XHJcblxyXG4gICAgICAgICAgICAgICAgbW92aWVHZW9tZXRyeS5mYWNlVmVydGV4VXZzWzBdWzRdID0gWyBmYWNlM1swXSwgZmFjZTNbMV0sIGZhY2UzWzNdIF07XHJcbiAgICAgICAgICAgICAgICBtb3ZpZUdlb21ldHJ5LmZhY2VWZXJ0ZXhVdnNbMF1bNV0gPSBbIGZhY2UzWzFdLCBmYWNlM1syXSwgZmFjZTNbM10gXTtcclxuXHJcbiAgICAgICAgICAgICAgICBtb3ZpZUdlb21ldHJ5LmZhY2VWZXJ0ZXhVdnNbMF1bNl0gPSBbIGZhY2U0WzBdLCBmYWNlNFsxXSwgZmFjZTRbM10gXTtcclxuICAgICAgICAgICAgICAgIG1vdmllR2VvbWV0cnkuZmFjZVZlcnRleFV2c1swXVs3XSA9IFsgZmFjZTRbMV0sIGZhY2U0WzJdLCBmYWNlNFszXSBdO1xyXG5cclxuICAgICAgICAgICAgICAgIG1vdmllR2VvbWV0cnkuZmFjZVZlcnRleFV2c1swXVs4XSA9IFsgZmFjZTVbMF0sIGZhY2U1WzFdLCBmYWNlNVszXSBdO1xyXG4gICAgICAgICAgICAgICAgbW92aWVHZW9tZXRyeS5mYWNlVmVydGV4VXZzWzBdWzldID0gWyBmYWNlNVsxXSwgZmFjZTVbMl0sIGZhY2U1WzNdIF07XHJcblxyXG4gICAgICAgICAgICAgICAgbW92aWVHZW9tZXRyeS5mYWNlVmVydGV4VXZzWzBdWzEwXSA9IFsgZmFjZTZbMF0sIGZhY2U2WzFdLCBmYWNlNlszXSBdO1xyXG4gICAgICAgICAgICAgICAgbW92aWVHZW9tZXRyeS5mYWNlVmVydGV4VXZzWzBdWzExXSA9IFsgZmFjZTZbMV0sIGZhY2U2WzJdLCBmYWNlNlszXSBdO1xyXG5cclxuICAgICAgICAgICAgICAgIG1vdmllU2NyZWVuID0gbmV3IFRIUkVFLk1lc2goIG1vdmllR2VvbWV0cnksIG1vdmllTWF0ZXJpYWwgKTtcclxuICAgICAgICAgICAgICAgIG1vdmllU2NyZWVuLnBvc2l0aW9uLnNldChwb3NpdGlvbi54LCBwb3NpdGlvbi55LCBwb3NpdGlvbi56KTtcclxuXHJcbiAgICAgICAgICAgICAgICBzY2VuZS5hZGQobW92aWVTY3JlZW4pO1xyXG5cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZnVuY3Rpb24gaW5pdFNjZW5lKCkge1xyXG5cclxuICAgICAgICAgICAgaWYoIXV0aWxzLmJyb3dzZXIuaXNCcm93c2VyQ29tcGF0aWJsZSgpKVxyXG4gICAgICAgICAgICB7XHJcbiAgICAgICAgICAgICAgICBwbGF5ZXIuZXJyb3Ioe2NvZGU6ICd3ZWItdnItbm90LXN1cHBvcnRlZCcsIGRpc21pc3M6IGZhbHNlfSk7XHJcbiAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHZhciB0aW1lID0gRGF0ZS5ub3coKSxcclxuICAgICAgICAgICAgICAgIGVmZmVjdCxcclxuICAgICAgICAgICAgICAgIHZpZGVvVGV4dHVyZSxcclxuICAgICAgICAgICAgICAgIHJlcXVlc3RJZCxcclxuICAgICAgICAgICAgICAgIHJlbmRlcmVyLFxyXG4gICAgICAgICAgICAgICAgY2FtZXJhLFxyXG4gICAgICAgICAgICAgICAgcmVuZGVyZWRDYW52YXM7XHJcblxyXG4gICAgICAgICAgICBjYW1lcmEgPSBuZXcgVEhSRUUuUGVyc3BlY3RpdmVDYW1lcmEoIDc1LCB3aW5kb3cuaW5uZXJXaWR0aCAvIHdpbmRvdy5pbm5lckhlaWdodCwgMSwgMTAwMCApO1xyXG5cclxuICAgICAgICAgICAgY2FtZXJhVmVjdG9yID0gbmV3IFRIUkVFLlZlY3RvcjMoKTsgLy8gU3RvcmUgdmVjdG9yIHJlcHJlc2VudGluZyB0aGUgZGlyZWN0aW9uIGluIHdoaWNoIHRoZSBjYW1lcmEgaXMgbG9va2luZywgaW4gd29ybGQgc3BhY2UuXHJcblxyXG4gICAgICAgICAgICBpZiAoKGN1cnJlbnRfcHJvaiA9PT0gXCIzNjBfTFJcIikgfHwgKGN1cnJlbnRfcHJvaiA9PT0gXCIzNjBfVEJcIikpIHtcclxuICAgICAgICAgICAgICAgIGNhbWVyYS5sYXllcnMuZW5hYmxlKCAxICk7IC8vIFJlbmRlciBsZWZ0IGV5ZSB3aGVuIG5vdCBpbiBWUiBtb2RlXHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHNjZW5lID0gbmV3IFRIUkVFLlNjZW5lKCk7XHJcblxyXG4gICAgICAgICAgICBjb250cm9sczNkID0gbmV3IFRIUkVFLlZSQ29udHJvbHMoY2FtZXJhKTtcclxuXHJcbiAgICAgICAgICAgIHZpZGVvVGV4dHVyZSA9IG5ldyBUSFJFRS5WaWRlb1RleHR1cmUoIHZpZGVvRWwgKTtcclxuICAgICAgICAgICAgdmlkZW9UZXh0dXJlLmdlbmVyYXRlTWlwbWFwcyA9IGZhbHNlO1xyXG4gICAgICAgICAgICB2aWRlb1RleHR1cmUubWluRmlsdGVyID0gVEhSRUUuTGluZWFyRmlsdGVyO1xyXG4gICAgICAgICAgICB2aWRlb1RleHR1cmUubWFnRmlsdGVyID0gVEhSRUUuTGluZWFyRmlsdGVyO1xyXG5cclxuICAgICAgICAgICAgLy8gaU9TIGFuZCBtYWNPUyBITFMgZml4L2hhY2tzXHJcbiAgICAgICAgICAgIC8vIGh0dHBzOi8vYnVncy53ZWJraXQub3JnL3Nob3dfYnVnLmNnaT9pZD0xNjM4NjYjYzNcclxuICAgICAgICAgICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL21yZG9vYi90aHJlZS5qcy9pc3N1ZXMvOTc1NFxyXG4gICAgICAgICAgICAvLyBPbiBpT1Mgd2l0aCBITFMsIGNvbG9yIHNwYWNlIGlzIHdyb25nIGFuZCB0ZXh0dXJlIGlzIGZsaXBwZWQgb24gWSBheGlzXHJcbiAgICAgICAgICAgIC8vIE9uIG1hY09TLCBqdXN0IG5lZWQgdG8gZmxpcCB0ZXh0dXJlIFkgYXhpc1xyXG5cclxuICAgICAgICAgICAgaWYgKGlzSExTKCkgJiYgdXRpbHMuYnJvd3Nlci5pc1NhZmFyaSgpICYmIHV0aWxzLmJyb3dzZXIuaXNJT1MoKSkge1xyXG4gICAgICAgICAgICAgICAgbG9nKFwiU2FmYXJpICsgaU9TICsgSExTID0gZmxpcFkgYW5kIGNvbG9yc3BhY2UgaGFja1wiKTtcclxuICAgICAgICAgICAgICAgIHZpZGVvVGV4dHVyZS5mb3JtYXQgPSBUSFJFRS5SR0JBRm9ybWF0O1xyXG4gICAgICAgICAgICAgICAgdmlkZW9UZXh0dXJlLmZsaXBZID0gZmFsc2U7XHJcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoaXNITFMoKSAmJiB1dGlscy5icm93c2VyLmlzU2FmYXJpKCkpIHtcclxuICAgICAgICAgICAgICAgIGxvZyhcIlNhZmFyaSArIEhMUyA9IGZsaXBZIGhhY2tcIik7XHJcbiAgICAgICAgICAgICAgICB2aWRlb1RleHR1cmUuZm9ybWF0ID0gVEhSRUUuUkdCRm9ybWF0O1xyXG4gICAgICAgICAgICAgICAgdmlkZW9UZXh0dXJlLmZsaXBZID0gZmFsc2U7XHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICB2aWRlb1RleHR1cmUuZm9ybWF0ID0gVEhSRUUuUkdCRm9ybWF0O1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBpZiAodmlkZW9UZXh0dXJlLmZvcm1hdCA9PT0gVEhSRUUuUkdCQUZvcm1hdCAmJiB2aWRlb1RleHR1cmUuZmxpcFkgPT09IGZhbHNlKSB7XHJcbiAgICAgICAgICAgICAgICBtb3ZpZU1hdGVyaWFsID0gbmV3IFRIUkVFLlNoYWRlck1hdGVyaWFsKHtcclxuICAgICAgICAgICAgICAgICAgICB1bmlmb3Jtczoge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0ZXh0dXJlOiB7IHZhbHVlOiB2aWRlb1RleHR1cmUgfVxyXG4gICAgICAgICAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgICAgICAgICAgdmVydGV4U2hhZGVyOiBbXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIFwidmFyeWluZyB2ZWMyIHZVVjtcIixcclxuICAgICAgICAgICAgICAgICAgICAgICAgXCJ2b2lkIG1haW4oKSB7XCIsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiXHR2VVYgPSB2ZWMyKCB1di54LCAxLjAgLSB1di55ICk7XCIsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiXHRnbF9Qb3NpdGlvbiA9IHByb2plY3Rpb25NYXRyaXggKiBtb2RlbFZpZXdNYXRyaXggKiB2ZWM0KCBwb3NpdGlvbiwgMS4wICk7XCIsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIFwifVwiXHJcbiAgICAgICAgICAgICAgICAgICAgXS5qb2luKFwiXFxuXCIpLFxyXG4gICAgICAgICAgICAgICAgICAgIGZyYWdtZW50U2hhZGVyOiBbXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIFwidW5pZm9ybSBzYW1wbGVyMkQgdGV4dHVyZTtcIixcclxuICAgICAgICAgICAgICAgICAgICAgICAgXCJ2YXJ5aW5nIHZlYzIgdlVWO1wiLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICBcInZvaWQgbWFpbigpIHtcIixcclxuICAgICAgICAgICAgICAgICAgICAgICAgXCIgZ2xfRnJhZ0NvbG9yID0gdGV4dHVyZTJEKCB0ZXh0dXJlLCB2VVYgICkuYmdyYTtcIixcclxuICAgICAgICAgICAgICAgICAgICAgICAgXCJ9XCJcclxuICAgICAgICAgICAgICAgICAgICBdLmpvaW4oXCJcXG5cIilcclxuICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICB9IGVsc2UgaWYgKHZpZGVvVGV4dHVyZS5mb3JtYXQgPT09IFRIUkVFLlJHQkZvcm1hdCAmJiB2aWRlb1RleHR1cmUuZmxpcFkgPT09IGZhbHNlKSB7XHJcbiAgICAgICAgICAgICAgICAgbW92aWVNYXRlcmlhbCA9IG5ldyBUSFJFRS5TaGFkZXJNYXRlcmlhbCh7XHJcbiAgICAgICAgICAgICAgICAgICAgdW5pZm9ybXM6IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGV4dHVyZTogeyB2YWx1ZTogdmlkZW9UZXh0dXJlIH1cclxuICAgICAgICAgICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAgICAgICAgIHZlcnRleFNoYWRlcjogW1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBcInZhcnlpbmcgdmVjMiB2VVY7XCIsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIFwidm9pZCBtYWluKCkge1wiLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICBcIlx0dlVWID0gdmVjMiggdXYueCwgMS4wIC0gdXYueSApO1wiLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICBcIlx0Z2xfUG9zaXRpb24gPSBwcm9qZWN0aW9uTWF0cml4ICogbW9kZWxWaWV3TWF0cml4ICogdmVjNCggcG9zaXRpb24sIDEuMCApO1wiLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICBcIn1cIlxyXG4gICAgICAgICAgICAgICAgICAgIF0uam9pbihcIlxcblwiKSxcclxuICAgICAgICAgICAgICAgICAgICBmcmFnbWVudFNoYWRlcjogW1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBcInVuaWZvcm0gc2FtcGxlcjJEIHRleHR1cmU7XCIsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIFwidmFyeWluZyB2ZWMyIHZVVjtcIixcclxuICAgICAgICAgICAgICAgICAgICAgICAgXCJ2b2lkIG1haW4oKSB7XCIsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiIGdsX0ZyYWdDb2xvciA9IHRleHR1cmUyRCggdGV4dHVyZSwgdlVWICApO1wiLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICBcIn1cIlxyXG4gICAgICAgICAgICAgICAgICAgIF0uam9pbihcIlxcblwiKVxyXG4gICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICBtb3ZpZU1hdGVyaWFsID0gbmV3IFRIUkVFLk1lc2hCYXNpY01hdGVyaWFsKCB7IG1hcDogdmlkZW9UZXh0dXJlLCBvdmVyZHJhdzogdHJ1ZSwgc2lkZTpUSFJFRS5Eb3VibGVTaWRlIH0gKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgY2hhbmdlUHJvamVjdGlvbihjdXJyZW50X3Byb2opO1xyXG5cclxuICAgICAgICAgICAgaWYoY3VycmVudF9wcm9qID09PSBcIk5PTkVcIikge1xyXG4gICAgICAgICAgICAgICAgbG9nKFwiUHJvamVjdGlvbiBpcyBOT05FLCBkb250IGluaXRcIik7XHJcbiAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGNhbWVyYS5wb3NpdGlvbi5zZXQoMCwwLDApO1xyXG5cclxuICAgICAgICAgICAgcmVuZGVyZXIgPSBuZXcgVEhSRUUuV2ViR0xSZW5kZXJlcih7XHJcbiAgICAgICAgICAgICAgICBkZXZpY2VQaXhlbFJhdGlvOiB3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbyxcclxuICAgICAgICAgICAgICAgIGFscGhhOiBmYWxzZSxcclxuICAgICAgICAgICAgICAgIGNsZWFyQ29sb3I6IDB4ZmZmZmZmLFxyXG4gICAgICAgICAgICAgICAgYW50aWFsaWFzOiB0cnVlXHJcbiAgICAgICAgICAgIH0pO1xyXG5cclxuICAgICAgICAgICAgcmVuZGVyZXIuc2V0U2l6ZSh3aW5kb3cuaW5uZXJXaWR0aCwgd2luZG93LmlubmVySGVpZ2h0KTtcclxuICAgICAgICAgICAgZWZmZWN0ID0gbmV3IFRIUkVFLlZSRWZmZWN0KHJlbmRlcmVyKTtcclxuICAgICAgICAgICAgZWZmZWN0LnNldFNpemUod2luZG93LmlubmVyV2lkdGgsIHdpbmRvdy5pbm5lckhlaWdodCk7XHJcblxyXG4gICAgICAgICAgICB2YXIgdnJEaXNwbGF5ID0gbnVsbDtcclxuICAgICAgICAgICAgdmFyIGZyYW1lRGF0YSA9IG51bGw7XHJcblxyXG4gICAgICAgICAgICAvLyBQcmV2aW91cyB0aW1lc3RhbXBzIGZvciBnYW1lcGFkIHVwZGF0ZXNcclxuICAgICAgICAgICAgdmFyIHByZXZUaW1lc3RhbXBzID0gW107XHJcblxyXG4gICAgICAgICAgICB2YXIgbWFuYWdlciA9IG5ldyBXZWJWUk1hbmFnZXIocmVuZGVyZXIsIGVmZmVjdCwge2hpZGVCdXR0b246IHRydWV9KTtcclxuXHJcbiAgICAgICAgICAgIHJlbmRlcmVkQ2FudmFzID0gcmVuZGVyZXIuZG9tRWxlbWVudDtcclxuICAgICAgICAgICAgcmVuZGVyZWRDYW52YXMuc3R5bGUud2lkdGggPSBcImluaGVyaXRcIjtcclxuICAgICAgICAgICAgcmVuZGVyZWRDYW52YXMuc3R5bGUuaGVpZ2h0ID0gXCJpbmhlcml0XCI7XHJcblxyXG4gICAgICAgICAgICBjb250YWluZXIuaW5zZXJ0QmVmb3JlKHJlbmRlcmVkQ2FudmFzLCBjb250YWluZXIuZmlyc3RDaGlsZCk7XHJcbiAgICAgICAgICAgIHZpZGVvRWwuc3R5bGUuZGlzcGxheSA9IFwibm9uZVwiO1xyXG5cclxuICAgICAgICAgICAgLy8gSGFuZGxlIHdpbmRvdyByZXNpemVzXHJcbiAgICAgICAgICAgIGZ1bmN0aW9uIG9uV2luZG93UmVzaXplKCkge1xyXG4gICAgICAgICAgICAgICAgLy9pZiAod2luZG93Lm9yaWVudGF0aW9uID09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgICAgICAgICAgICAgIGVmZmVjdC5zZXRTaXplKHdpbmRvdy5pbm5lcldpZHRoLCB3aW5kb3cuaW5uZXJIZWlnaHQpO1xyXG4gICAgICAgICAgICAgICAgICAgIGNhbWVyYS5hc3BlY3QgPSB3aW5kb3cuaW5uZXJXaWR0aCAvIHdpbmRvdy5pbm5lckhlaWdodDtcclxuICAgICAgICAgICAgICAgICAgICBjYW1lcmEudXBkYXRlUHJvamVjdGlvbk1hdHJpeCgpO1xyXG4gICAgICAgICAgICAgICAgLy99XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCBvbldpbmRvd1Jlc2l6ZSwgZmFsc2UpO1xyXG4gICAgICAgICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigndnJkaXNwbGF5cHJlc2VudGNoYW5nZScsIG9uV2luZG93UmVzaXplLCB0cnVlKTtcclxuXHJcbiAgICAgICAgICAgIGZ1bmN0aW9uIG9uVlJSZXF1ZXN0UHJlc2VudCgpIHtcclxuICAgICAgICAgICAgICAgbWFuYWdlci5lbnRlclZSTW9kZV8oKTtcclxuICAgICAgICAgICAgICAgbWFuYWdlci5zZXRNb2RlXygzKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgZnVuY3Rpb24gb25WUkV4aXRQcmVzZW50KCkge1xyXG4gICAgICAgICAgICAgICAgaWYgKCF2ckRpc3BsYXkuaXNQcmVzZW50aW5nKVxyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIHZyRGlzcGxheS5leGl0UHJlc2VudCgpO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigndnJkaXNwbGF5YWN0aXZhdGUnLCBvblZSUmVxdWVzdFByZXNlbnQsIHRydWUpO1xyXG4gICAgICAgICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigndnJkaXNwbGF5ZGVhY3RpdmF0ZScsIG9uVlJFeGl0UHJlc2VudCwgdHJ1ZSk7XHJcblxyXG4gICAgICAgICAgICBpZiAobmF2aWdhdG9yLmdldFZSRGlzcGxheXMpIHtcclxuICAgICAgICAgICAgICAgIC8vZnJhbWVEYXRhID0gbmV3IFZSRnJhbWVEYXRhKCk7XHJcbiAgICAgICAgICAgICAgICBuYXZpZ2F0b3IuZ2V0VlJEaXNwbGF5cygpLnRoZW4oZnVuY3Rpb24gKGRpc3BsYXlzKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKGRpc3BsYXlzLmxlbmd0aCA+IDApIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgbG9nKFwiV2ViVlIgc3VwcG9ydGVkLCBWUkRpc3BsYXlzIGZvdW5kLlwiKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgdnJEaXNwbGF5ID0gZGlzcGxheXNbMF07XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvZyh2ckRpc3BsYXkpO1xyXG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHBsYXllci5lcnJvcih7Y29kZTogJ3dlYi12ci1uby1kZXZpY2VzLWZvdW5kJywgZGlzbWlzczogZmFsc2V9KTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfSBlbHNlIGlmIChuYXZpZ2F0b3IuZ2V0VlJEZXZpY2VzKSB7XHJcbiAgICAgICAgICAgICAgICBwbGF5ZXIuZXJyb3Ioe2NvZGU6ICd3ZWItdnItb3V0LW9mLWRhdGUnLCBkaXNtaXNzOiBmYWxzZX0pO1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgcGxheWVyLmVycm9yKHtjb2RlOiAnd2ViLXZyLW5vdC1zdXBwb3J0ZWQnLCBkaXNtaXNzOiBmYWxzZX0pO1xyXG4gICAgICAgICAgICB9XHJcblxyXG5cclxuICAgICAgICAgICAgZnVuY3Rpb24gaXNITFModmlkZW9FbGVtZW50KSB7XHJcbiAgICAgICAgICAgICAgICB2YXIgcmVzdWx0ID0gZmFsc2U7XHJcbiAgICAgICAgICAgICAgICB2YXIgY3VycmVudFR5cGUgPSBwbGF5ZXIuY3VycmVudFR5cGUoKTtcclxuXHJcbiAgICAgICAgICAgICAgICBpZiAoY3VycmVudFR5cGUgPT0gXCJhcHBsaWNhdGlvbi94LW1wZWdVUkxcIikge1xyXG4gICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IHRydWU7XHJcbiAgICAgICAgICAgICAgICAgICAgbG9nKFwiRGV0ZWN0ZWQgSExTIFN0cmVhbVwiKTtcclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gcmVzdWx0O1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAvLyBIYW5kbGUgd2luZG93IHJvdGF0ZVxyXG4gICAgICAgICAgICBmdW5jdGlvbiBvbldpbmRvd1JvdGF0ZSgpIHtcclxuICAgICAgICAgICAgICAgIGlmICh3aW5kb3cub3JpZW50YXRpb24gPT0gLTkwIHx8IHdpbmRvdy5vcmllbnRhdGlvbiA9PSA5MCkge1xyXG4gICAgICAgICAgICAgICAgICAgIC8vIGluIGlPUywgd2lkdGggYW5kIGhlaWdodCBuZXZlciBjaGFuZ2VzIHJlZ2FyZGxlc3Mgb3JpZW50YXRpb25cclxuICAgICAgICAgICAgICAgICAgICAvLyBzbyB3aGVuIGluIGEgaG9yaXpvbnRhbCBtb2RlLCBoZWlnaHQgc3RpbGwgZ3JlYXRlciB0aGFuIHdpZHRoXHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHNjcmVlbi5oZWlnaHQgPiBzY3JlZW4ud2lkdGgpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY2FtZXJhLmFzcGVjdCA9IHNjcmVlbi5oZWlnaHQgLyBzY3JlZW4ud2lkdGg7XHJcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICAvLyBpbiBBbmRyb2lkLCB3aWR0aCBhbmQgaGVpZ2h0IHdpbGwgc3dhcCB2YWx1ZSBkZXBlbmRpbmcgb24gb3JpZW50YXRpb25cclxuICAgICAgICAgICAgICAgICAgICAgICAgY2FtZXJhLmFzcGVjdCA9IHNjcmVlbi53aWR0aCAvIHNjcmVlbi5oZWlnaHQ7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICBjYW1lcmEuYXNwZWN0ID0gc2NyZWVuLndpZHRoIC8gc2NyZWVuLmhlaWdodDtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIGNhbWVyYS51cGRhdGVQcm9qZWN0aW9uTWF0cml4KCk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ29yaWVudGF0aW9uY2hhbmdlJywgb25XaW5kb3dSb3RhdGUsIGZhbHNlKTtcclxuXHJcbiAgICAgICAgICAgIGZ1bmN0aW9uIHRvZ2dsZVBsYXkoKSB7XHJcbiAgICAgICAgICAgICAgICAvLyBEb2Vzbid0IGN1cnJlbnRseSBjYXRlciBmb3IgY2FzZSB3aGVyZSBwYXVzZWQgZHVlIHRvIGJ1ZmZlcmluZ1xyXG4gICAgICAgICAgICAgICAgLy8gYW5kL29yIGxhY2sgb2YgZGF0YVxyXG4gICAgICAgICAgICAgICAgaWYgKHBsYXllci5wYXVzZWQoKSkge1xyXG4gICAgICAgICAgICAgICAgICAgIHBsYXllci5wbGF5KCk7XHJcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgIHBsYXllci5wYXVzZSgpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAoZnVuY3Rpb24gYW5pbWF0ZSgpIHtcclxuICAgICAgICAgICAgICAgIGlmICggdmlkZW9FbC5yZWFkeVN0YXRlID09PSB2aWRlb0VsLkhBVkVfRU5PVUdIX0RBVEEgKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHZpZGVvVGV4dHVyZSkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB2aWRlb1RleHR1cmUubmVlZHNVcGRhdGUgPSB0cnVlO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICBjb250cm9sczNkLnVwZGF0ZSgpO1xyXG4gICAgICAgICAgICAgICAgbWFuYWdlci5yZW5kZXIoIHNjZW5lLCBjYW1lcmEgKTtcclxuXHJcbiAgICAgICAgICAgICAgICBpZiAodnJEaXNwbGF5KSB7XHJcbiAgICAgICAgICAgICAgICAgICAgdnJEaXNwbGF5LnJlcXVlc3RBbmltYXRpb25GcmFtZShhbmltYXRlKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgLy8gR3JhYiBhbGwgZ2FtZXBhZHNcclxuICAgICAgICAgICAgICAgICAgICBpZihuYXZpZ2F0b3IuZ2V0R2FtZXBhZHMpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGdhbWVwYWRzID0gbmF2aWdhdG9yLmdldEdhbWVwYWRzKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgZ2FtZXBhZHMubGVuZ3RoOyArK2kpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhciBnYW1lcGFkID0gZ2FtZXBhZHNbaV07XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBNYWtlIHN1cmUgZ2FtZXBhZCBpcyBkZWZpbmVkXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZ2FtZXBhZCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE9ubHkgdGFrZSBpbnB1dCBpZiBzdGF0ZSBoYXMgY2hhbmdlZCBzaW5jZSB3ZSBjaGVja2VkIGxhc3RcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZ2FtZXBhZC50aW1lc3RhbXAgJiYgIShnYW1lcGFkLnRpbWVzdGFtcCA9PT0gcHJldlRpbWVzdGFtcHNbaV0pKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgZ2FtZXBhZC5idXR0b25zLmxlbmd0aDsgKytqKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZ2FtZXBhZC5idXR0b25zW2pdLnByZXNzZWQpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b2dnbGVQbGF5KCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJldlRpbWVzdGFtcHNbaV0gPSBnYW1lcGFkLnRpbWVzdGFtcDtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICAvL3ZyRGlzcGxheS5nZXRGcmFtZURhdGEoZnJhbWVEYXRhKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgLy9pZiAodnJEaXNwbGF5LmlzUHJlc2VudGluZykge1xyXG4gICAgICAgICAgICAgICAgICAgIC8vICAgdnJEaXNwbGF5LnN1Ym1pdEZyYW1lKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgLy99XHJcbiAgICAgICAgICAgICAgICB9ICBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICB3aW5kb3cucmVxdWVzdEFuaW1hdGlvbkZyYW1lKGFuaW1hdGUpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgIGNhbWVyYS5nZXRXb3JsZERpcmVjdGlvbiggY2FtZXJhVmVjdG9yICk7XHJcbiAgICAgICAgICAgICAgICB4ID0gZ2V0Q2FtZXJhQW5nbGUoIGNhbWVyYVZlY3RvciwgXCJ4XCIpO1xyXG4gICAgICAgICAgICAgICAgeSA9IGdldENhbWVyYUFuZ2xlKCBjYW1lcmFWZWN0b3IsIFwieVwiKTtcclxuXHJcbiAgICAgICAgICAgICAgICAvLyBsb2coJ1ggY2FtZXJhIGFuZ2xlOicgKyBnZXRDYW1lcmFBbmdsZSggY2FtZXJhVmVjdG9yLCBcInhcIikgKTtcclxuICAgICAgICAgICAgICAgIC8vIGxvZygnWSBjYW1lcmEgYW5nbGU6JyArIGdldENhbWVyYUFuZ2xlKCBjYW1lcmFWZWN0b3IsIFwieVwiKSApO1xyXG4gICAgICAgICAgICAgICAgLy8gbG9nKGNhbWVyYVZlY3Rvci54LCBjYW1lcmFWZWN0b3IueSwgY2FtZXJhVmVjdG9yLnopO1xyXG4gICAgICAgICAgICB9KCkpO1xyXG5cclxuICAgICAgICAgICAgZnVuY3Rpb24gZ2V0Q2FtZXJhQW5nbGUoY2FtZXJhVmVjdG9yLCBheGlzKSB7XHJcbiAgICAgICAgICAgICAgIGlmIChheGlzID09IFwieFwiKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIDE4MC1yYWRpYW5zVG9EZWdyZWVzKE1hdGguYXRhbjIoY2FtZXJhVmVjdG9yLngsIGNhbWVyYVZlY3Rvci56KSk7XHJcbiAgICAgICAgICAgICAgICAgICAgLy9yZXR1cm4gY2FtZXJhVmVjdG9yLnggKiAxMTQuNTkxNTU5MDI2MTY0NjU7XHJcbiAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoYXhpcyA9PSBcInlcIikge1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiAxODAtcmFkaWFuc1RvRGVncmVlcyhNYXRoLmF0YW4yKGNhbWVyYVZlY3Rvci55LCBjYW1lcmFWZWN0b3IueikpO1xyXG4gICAgICAgICAgICAgICAgICAgIC8vcmV0dXJuIGNhbWVyYVZlY3Rvci55ICogMTE0LjU5MTU1OTAyNjE2NDY1O1xyXG4gICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGZ1bmN0aW9uIHJhZGlhbnNUb0RlZ3JlZXMocmFkaWFucykge1xyXG4gICAgICAgICAgICAgICByZXR1cm4gcmFkaWFucyAqIDE4MCAvIE1hdGguUEk7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIC8vIEV4cG9zZSB0aGUgVGhyZWUuanMgcGVyc3BlY3RpdmUgY2FtZXJhIGFuZCByb3RhdGlvbiB2YWx1ZXMgb24gdGhlIHBsYXllciB1bmRlciB0aGUgJ3ZyJyBuYW1lc3BhY2U6XHJcbiAgICAgICAgICAgIHBsYXllci52ciA9IHtcclxuICAgICAgICAgICAgICBjYW1lcmFWZWN0b3I6IGNhbWVyYVZlY3RvcixcclxuICAgICAgICAgICAgICB0aHJlZUpzOiB7XHJcbiAgICAgICAgICAgICAgICAgIHNjZW5lOiBzY2VuZSxcclxuICAgICAgICAgICAgICAgICAgY2FtZXJhOiBjYW1lcmEsXHJcbiAgICAgICAgICAgICAgICAgIHJlbmRlcmVyOiByZW5kZXJlclxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfTtcclxuXHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBwbGF5ZXIub24oJ2xvYWRzdGFydCcsIGZ1bmN0aW9uICgpIHtcclxuICAgICAgICAgICAgaW5pdFNjZW5lKCk7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIHBsYXllci5yZWFkeShmdW5jdGlvbiAoKSB7XHJcblxyXG4gICAgICAgICAgICBmdW5jdGlvbiB1cGRhdGVWckNvbnRyb2xzKGNiKSB7XHJcblxyXG4gICAgICAgICAgICAgICAgLy9BZGQgQ2FyYm9hcmQgYnV0dG9uXHJcbiAgICAgICAgICAgICAgICB2YXIgQnV0dG9uID0gdmlkZW9qcy5nZXRDb21wb25lbnQoJ0J1dHRvbicpO1xyXG4gICAgICAgICAgICAgICAgdmFyIE15QnV0dG9uID0gdmlkZW9qcy5leHRlbmQoQnV0dG9uLCB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3RydWN0b3I6IGZ1bmN0aW9uKCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBCdXR0b24uYXBwbHkodGhpcywgYXJndW1lbnRzKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgbG9nKHRoaXMpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAvKiBpbml0aWFsaXplIHlvdXIgYnV0dG9uICovXHJcbiAgICAgICAgICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgICAgICAgICBoYW5kbGVDbGljazogZnVuY3Rpb24oKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBldmVudCA9IG5ldyBFdmVudCgndnJkaXNwbGF5YWN0aXZhdGUnKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgd2luZG93LmRpc3BhdGNoRXZlbnQoZXZlbnQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAvKiBkbyBzb21ldGhpbmcgb24gY2xpY2sgKi9cclxuICAgICAgICAgICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAgICAgICAgIGJ1aWxkQ1NTQ2xhc3M6IGZ1bmN0aW9uKCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gQnV0dG9uLnByb3RvdHlwZS5idWlsZENTU0NsYXNzLmNhbGwodGhpcykgKyAndmpzLWJ1dHRvbi12cic7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICB2aWRlb2pzLnJlZ2lzdGVyQ29tcG9uZW50KCdNeUJ1dHRvbicsIE15QnV0dG9uKTtcclxuICAgICAgICAgICAgICAgIGNiLmFkZENoaWxkKCdteUJ1dHRvbicsIHt9KTtcclxuICAgICAgICAgICAgICAgIFxyXG4gICAgICAgICAgICAgICAgLy9pZiBpb3MgcmVtb3ZlIGZ1bGwgc2NyZWVuIHRvZ2dsZVxyXG4gICAgICAgICAgICAgICAgaWYodXRpbHMuYnJvd3Nlci5pc0lPUygpKVxyXG4gICAgICAgICAgICAgICAge1xyXG4gICAgICAgICAgICAgICAgICAgIHBsYXllci5jb250cm9sQmFyLmZ1bGxzY3JlZW5Ub2dnbGUuaGlkZSgpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBpZih1dGlscy5icm93c2VyLmlzTW9iaWxlKCkpe1xyXG4gICAgICAgICAgICAgICAgdXBkYXRlVnJDb250cm9scyhwbGF5ZXIuY29udHJvbEJhcik7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuXHJcblxyXG4gICAgIHJldHVybiB7XHJcbiAgICAgICAgIGNoYW5nZVByb2plY3Rpb246IGNoYW5nZVByb2plY3Rpb25cclxuICAgICB9O1xyXG4gIH07XHJcblxyXG4gIC8vIHJlZ2lzdGVyIHRoZSBwbHVnaW4gd2l0aCB2aWRlby5qc1xyXG4gIHZpZGVvanMucmVnaXN0ZXJQbHVnaW4oJ3ZyJywgcGx1Z2luKTtcclxuXHJcbn0od2luZG93LnZpZGVvanMpKTtcclxuIiwibW9kdWxlLmV4cG9ydHMgPSB7XHJcbiAgLy8gRm9yY2VzIGF2YWlsYWJpbGl0eSBvZiBWUiBtb2RlLCBldmVuIGZvciBub24tbW9iaWxlIGRldmljZXMuXHJcbiAgRk9SQ0VfRU5BQkxFX1ZSOiBmYWxzZSwgLy8gRGVmYXVsdDogZmFsc2UuXHJcblxyXG4gIC8vIENvbXBsZW1lbnRhcnkgZmlsdGVyIGNvZWZmaWNpZW50LiAwIGZvciBhY2NlbGVyb21ldGVyLCAxIGZvciBneXJvLlxyXG4gIEtfRklMVEVSOiAwLjk4LCAvLyBEZWZhdWx0OiAwLjk4LlxyXG5cclxuICAvLyBIb3cgZmFyIGludG8gdGhlIGZ1dHVyZSB0byBwcmVkaWN0IGR1cmluZyBmYXN0IG1vdGlvbiAoaW4gc2Vjb25kcykuXHJcbiAgUFJFRElDVElPTl9USU1FX1M6IDAuMDQwLCAvLyBEZWZhdWx0OiAwLjA0MC5cclxuXHJcbiAgLy8gRmxhZyB0byBkaXNhYmxlIHRvdWNoIHBhbm5lci4gSW4gY2FzZSB5b3UgaGF2ZSB5b3VyIG93biB0b3VjaCBjb250cm9scy5cclxuICBUT1VDSF9QQU5ORVJfRElTQUJMRUQ6IGZhbHNlLCAvLyBEZWZhdWx0OiBmYWxzZS5cclxuXHJcbiAgLy8gRW5hYmxlIHlhdyBwYW5uaW5nIG9ubHksIGRpc2FibGluZyByb2xsIGFuZCBwaXRjaC4gVGhpcyBjYW4gYmUgdXNlZnVsXHJcbiAgLy8gZm9yIHBhbm9yYW1hcyB3aXRoIG5vdGhpbmcgaW50ZXJlc3RpbmcgYWJvdmUgb3IgYmVsb3cuXHJcbiAgWUFXX09OTFk6IGZhbHNlLCAvLyBEZWZhdWx0OiBmYWxzZS5cclxuXHJcbiAgLy8gVG8gZGlzYWJsZSBrZXlib2FyZCBhbmQgbW91c2UgY29udHJvbHMsIGlmIHlvdSB3YW50IHRvIHVzZSB5b3VyIG93blxyXG4gIC8vIGltcGxlbWVudGF0aW9uLlxyXG4gIE1PVVNFX0tFWUJPQVJEX0NPTlRST0xTX0RJU0FCTEVEOiBmYWxzZSwgLy8gRGVmYXVsdDogZmFsc2UuXHJcblxyXG4gIC8vIFByZXZlbnQgdGhlIHBvbHlmaWxsIGZyb20gaW5pdGlhbGl6aW5nIGltbWVkaWF0ZWx5LiBSZXF1aXJlcyB0aGUgYXBwXHJcbiAgLy8gdG8gY2FsbCBJbml0aWFsaXplV2ViVlJQb2x5ZmlsbCgpIGJlZm9yZSBpdCBjYW4gYmUgdXNlZC5cclxuICBERUZFUl9JTklUSUFMSVpBVElPTjogZmFsc2UsIC8vIERlZmF1bHQ6IGZhbHNlLlxyXG5cclxuICAvLyBFbmFibGUgdGhlIGRlcHJlY2F0ZWQgdmVyc2lvbiBvZiB0aGUgQVBJIChuYXZpZ2F0b3IuZ2V0VlJEZXZpY2VzKS5cclxuICBFTkFCTEVfREVQUkVDQVRFRF9BUEk6IGZhbHNlLCAvLyBEZWZhdWx0OiBmYWxzZS5cclxuXHJcbiAgLy8gU2NhbGVzIHRoZSByZWNvbW1lbmRlZCBidWZmZXIgc2l6ZSByZXBvcnRlZCBieSBXZWJWUiwgd2hpY2ggY2FuIGltcHJvdmVcclxuICAvLyBwZXJmb3JtYW5jZS5cclxuICBCVUZGRVJfU0NBTEU6IDAuNSwgLy8gRGVmYXVsdDogMS4wLlxyXG5cclxuICAvLyBBbGxvdyBWUkRpc3BsYXkuc3VibWl0RnJhbWUgdG8gY2hhbmdlIGdsIGJpbmRpbmdzLCB3aGljaCBpcyBtb3JlXHJcbiAgLy8gZWZmaWNpZW50IGlmIHRoZSBhcHBsaWNhdGlvbiBjb2RlIHdpbGwgcmUtYmluZCBpdHMgcmVzb3VyY2VzIG9uIHRoZVxyXG4gIC8vIG5leHQgZnJhbWUgYW55d2F5LiBUaGlzIGhhcyBiZWVuIHNlZW4gdG8gY2F1c2UgcmVuZGVyaW5nIGdsaXRjaGVzIHdpdGhcclxuICAvLyBUSFJFRS5qcy5cclxuICAvLyBEaXJ0eSBiaW5kaW5ncyBpbmNsdWRlOiBnbC5GUkFNRUJVRkZFUl9CSU5ESU5HLCBnbC5DVVJSRU5UX1BST0dSQU0sXHJcbiAgLy8gZ2wuQVJSQVlfQlVGRkVSX0JJTkRJTkcsIGdsLkVMRU1FTlRfQVJSQVlfQlVGRkVSX0JJTkRJTkcsXHJcbiAgLy8gYW5kIGdsLlRFWFRVUkVfQklORElOR18yRCBmb3IgdGV4dHVyZSB1bml0IDAuXHJcbiAgRElSVFlfU1VCTUlUX0ZSQU1FX0JJTkRJTkdTOiBmYWxzZSAvLyBEZWZhdWx0OiBmYWxzZS5cclxufVxyXG4iXX0= diff --git a/dist/videocloud.vr.min.js b/dist/videocloud.vr.min.js deleted file mode 100644 index a1ab1cf7..00000000 --- a/dist/videocloud.vr.min.js +++ /dev/null @@ -1 +0,0 @@ -!function e(t,i,n){function r(s,o){if(!i[s]){if(!t[s]){var u="function"==typeof require&&require;if(!o&&u)return u(s,!0);if(a)return a(s,!0);var c=new Error("Cannot find module '"+s+"'");throw c.code="MODULE_NOT_FOUND",c}var l=i[s]={exports:{}};t[s][0].call(l.exports,function(e){var i=t[s][1][e];return r(i||e)},l,l.exports,e,t,i,n)}return i[s].exports}for(var a="function"==typeof require&&require,s=0;s0&&console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.")}function l(e,t,i,r,a,s,o,u,c,l){e=e!==undefined?e:[],t=t!==undefined?t:ra,n.call(this,e,t,i,r,a,s,o,u,c,l),this.flipY=!1}function h(){this.seq=[],this.map={}}function d(e,t,i){var n=e[0];if(n<=0||n>0)return e;var r=t*i,a=ms[r];if(a===undefined&&(a=new Float32Array(r),ms[r]=a),0!==t){n.toArray(a,0);for(var s=1,o=0;s!==t;++s)o+=i,e[s].toArray(a,o)}return a}function p(e,t){var i=Ms[t];i===undefined&&(i=new Int32Array(t),Ms[t]=i);for(var n=0;n!==t;++n)i[n]=e.allocTextureUnit();return i}function f(e,t){e.uniform1f(this.addr,t)}function g(e,t){e.uniform1i(this.addr,t)}function m(e,t){t.x===undefined?e.uniform2fv(this.addr,t):e.uniform2f(this.addr,t.x,t.y)}function M(e,t){t.x!==undefined?e.uniform3f(this.addr,t.x,t.y,t.z):t.r!==undefined?e.uniform3f(this.addr,t.r,t.g,t.b):e.uniform3fv(this.addr,t)}function y(e,t){t.x===undefined?e.uniform4fv(this.addr,t):e.uniform4f(this.addr,t.x,t.y,t.z,t.w)}function v(e,t){e.uniformMatrix2fv(this.addr,!1,t.elements||t)}function w(e,t){e.uniformMatrix3fv(this.addr,!1,t.elements||t)}function x(e,t){e.uniformMatrix4fv(this.addr,!1,t.elements||t)}function T(e,t,i){var n=i.allocTextureUnit();e.uniform1i(this.addr,n),i.setTexture2D(t||fs,n)}function E(e,t,i){var n=i.allocTextureUnit();e.uniform1i(this.addr,n),i.setTextureCube(t||gs,n)}function N(e,t){e.uniform2iv(this.addr,t)}function L(e,t){e.uniform3iv(this.addr,t)}function D(e,t){e.uniform4iv(this.addr,t)}function A(e){switch(e){case 5126:return f;case 35664:return m;case 35665:return M;case 35666:return y;case 35674:return v;case 35675:return w;case 35676:return x;case 35678:return T;case 35680:return E;case 5124:case 35670:return g;case 35667:case 35671:return N;case 35668:case 35672:return L;case 35669:case 35673:return D}}function I(e,t){e.uniform1fv(this.addr,t)}function b(e,t){e.uniform1iv(this.addr,t)}function S(e,t){e.uniform2fv(this.addr,d(t,this.size,2))}function _(e,t){e.uniform3fv(this.addr,d(t,this.size,3))}function j(e,t){e.uniform4fv(this.addr,d(t,this.size,4))}function C(e,t){e.uniformMatrix2fv(this.addr,!1,d(t,this.size,4))}function z(e,t){e.uniformMatrix3fv(this.addr,!1,d(t,this.size,9))}function O(e,t){e.uniformMatrix4fv(this.addr,!1,d(t,this.size,16))}function R(e,t,i){var n=t.length,r=p(i,n);e.uniform1iv(this.addr,r);for(var a=0;a!==n;++a)i.setTexture2D(t[a]||fs,r[a])}function U(e,t,i){var n=t.length,r=p(i,n);e.uniform1iv(this.addr,r);for(var a=0;a!==n;++a)i.setTextureCube(t[a]||gs,r[a])}function P(e){switch(e){case 5126:return I;case 35664:return S;case 35665:return _;case 35666:return j;case 35674:return C;case 35675:return z;case 35676:return O;case 35678:return R;case 35680:return U;case 5124:case 35670:return b;case 35667:case 35671:return N;case 35668:case 35672:return L;case 35669:case 35673:return D}}function k(e,t,i){this.id=e,this.addr=i,this.setValue=A(t.type)}function B(e,t,i){this.id=e,this.addr=i,this.size=t.size,this.setValue=P(t.type)}function F(e){this.id=e,h.call(this)}function G(e,t){e.seq.push(t),e.map[t.id]=t}function Q(e,t,i){var n=e.name,r=n.length;for(ys.lastIndex=0;;){var a=ys.exec(n),s=ys.lastIndex,o=a[1],u="]"===a[2],c=a[3];if(u&&(o|=0),c===undefined||"["===c&&s+2===r){G(i,c===undefined?new k(o,e,t):new B(o,e,t));break}var l=i.map[o];l===undefined&&G(i,l=new F(o)),i=l}}function Y(e,t,i){h.call(this),this.renderer=i;for(var n=e.getProgramParameter(t,e.ACTIVE_UNIFORMS),r=0;r!==n;++r){var a=e.getActiveUniform(t,r),s=a.name;Q(a,e.getUniformLocation(t,s),this)}}function V(e,t,i){return t===undefined&&i===undefined?this.set(e):this.setRGB(e,t,i)}function H(e,t,i,r,a,s,o,u,c,l,h,d){n.call(this,null,s,o,u,c,l,r,a,h,d),this.image={data:e,width:t,height:i},this.magFilter=c!==undefined?c:ma,this.minFilter=l!==undefined?l:ma,this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1}function W(e,t){this.min=e!==undefined?e:new i(+Infinity,+Infinity),this.max=t!==undefined?t:new i(-Infinity,-Infinity)}function X(e,t){function n(){var e=new Float32Array([-1,-1,0,0,1,-1,1,0,1,1,1,1,-1,1,0,1]),t=new Uint16Array([0,1,2,0,2,3]);a=f.createBuffer(),s=f.createBuffer(),f.bindBuffer(f.ARRAY_BUFFER,a),f.bufferData(f.ARRAY_BUFFER,e,f.STATIC_DRAW),f.bindBuffer(f.ELEMENT_ARRAY_BUFFER,s),f.bufferData(f.ELEMENT_ARRAY_BUFFER,t,f.STATIC_DRAW),d=f.createTexture(),p=f.createTexture(),g.bindTexture(f.TEXTURE_2D,d),f.texImage2D(f.TEXTURE_2D,0,f.RGB,16,16,0,f.RGB,f.UNSIGNED_BYTE,null),f.texParameteri(f.TEXTURE_2D,f.TEXTURE_WRAP_S,f.CLAMP_TO_EDGE),f.texParameteri(f.TEXTURE_2D,f.TEXTURE_WRAP_T,f.CLAMP_TO_EDGE),f.texParameteri(f.TEXTURE_2D,f.TEXTURE_MAG_FILTER,f.NEAREST),f.texParameteri(f.TEXTURE_2D,f.TEXTURE_MIN_FILTER,f.NEAREST),g.bindTexture(f.TEXTURE_2D,p),f.texImage2D(f.TEXTURE_2D,0,f.RGBA,16,16,0,f.RGBA,f.UNSIGNED_BYTE,null),f.texParameteri(f.TEXTURE_2D,f.TEXTURE_WRAP_S,f.CLAMP_TO_EDGE),f.texParameteri(f.TEXTURE_2D,f.TEXTURE_WRAP_T,f.CLAMP_TO_EDGE),f.texParameteri(f.TEXTURE_2D,f.TEXTURE_MAG_FILTER,f.NEAREST),f.texParameteri(f.TEXTURE_2D,f.TEXTURE_MIN_FILTER,f.NEAREST),o={vertexShader:["uniform lowp int renderType;","uniform vec3 screenPosition;","uniform vec2 scale;","uniform float rotation;","uniform sampler2D occlusionMap;","attribute vec2 position;","attribute vec2 uv;","varying vec2 vUV;","varying float vVisibility;","void main() {","vUV = uv;","vec2 pos = position;","if ( renderType == 2 ) {","vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );","visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );","visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );","visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );","visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );","visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );","visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );","visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );","visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );","vVisibility = visibility.r / 9.0;","vVisibility *= 1.0 - visibility.g / 9.0;","vVisibility *= visibility.b / 9.0;","vVisibility *= 1.0 - visibility.a / 9.0;","pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;","pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;","}","gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );","}"].join("\n"),fragmentShader:["uniform lowp int renderType;","uniform sampler2D map;","uniform float opacity;","uniform vec3 color;","varying vec2 vUV;","varying float vVisibility;","void main() {","if ( renderType == 0 ) {","gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );","} else if ( renderType == 1 ) {","gl_FragColor = texture2D( map, vUV );","} else {","vec4 texture = texture2D( map, vUV );","texture.a *= opacity * vVisibility;","gl_FragColor = texture;","gl_FragColor.rgb *= color;","}","}"].join("\n")},c=r(o),l={vertex:f.getAttribLocation(c,"position"),uv:f.getAttribLocation(c,"uv")},h={renderType:f.getUniformLocation(c,"renderType"),map:f.getUniformLocation(c,"map"),occlusionMap:f.getUniformLocation(c,"occlusionMap"),opacity:f.getUniformLocation(c,"opacity"),color:f.getUniformLocation(c,"color"),scale:f.getUniformLocation(c,"scale"),rotation:f.getUniformLocation(c,"rotation"),screenPosition:f.getUniformLocation(c,"screenPosition")}}function r(t){var i=f.createProgram(),n=f.createShader(f.FRAGMENT_SHADER),r=f.createShader(f.VERTEX_SHADER),a="precision "+e.getPrecision()+" float;\n";return f.shaderSource(n,a+t.fragmentShader),f.shaderSource(r,a+t.vertexShader),f.compileShader(n),f.compileShader(r),f.attachShader(i,n),f.attachShader(i,r),f.linkProgram(i),i}var a,s,o,c,l,h,d,p,f=e.context,g=e.state;this.render=function(r,o,m){if(0!==t.length){var M=new u,y=m.w/m.z,v=.5*m.z,w=.5*m.w,x=16/m.w,T=new i(x*y,x),E=new u(1,1,0),N=new i(1,1),L=new W;L.min.set(m.x,m.y),L.max.set(m.x+(m.z-16),m.y+(m.w-16)),c===undefined&&n(),f.useProgram(c),g.initAttributes(),g.enableAttribute(l.vertex),g.enableAttribute(l.uv),g.disableUnusedAttributes(),f.uniform1i(h.occlusionMap,0),f.uniform1i(h.map,1),f.bindBuffer(f.ARRAY_BUFFER,a),f.vertexAttribPointer(l.vertex,2,f.FLOAT,!1,16,0),f.vertexAttribPointer(l.uv,2,f.FLOAT,!1,16,8),f.bindBuffer(f.ELEMENT_ARRAY_BUFFER,s),g.disable(f.CULL_FACE),g.setDepthWrite(!1);for(var D=0,A=t.length;D.001&&_.scale>.001&&(E.x=_.x,E.y=_.y,E.z=_.z,x=_.size*_.scale/m.w,T.x=x*y,T.y=x,f.uniform3f(h.screenPosition,E.x,E.y,E.z),f.uniform2f(h.scale,T.x,T.y),f.uniform1f(h.rotation,_.rotation),f.uniform1f(h.opacity,_.opacity),f.uniform3f(h.color,_.color.r,_.color.g,_.color.b),g.setBlending(_.blending,_.blendEquation,_.blendSrc,_.blendDst),e.setTexture2D(_.texture,1),f.drawElements(f.TRIANGLES,6,f.UNSIGNED_SHORT,0))}}}g.enable(f.CULL_FACE),g.enable(f.DEPTH_TEST),g.setDepthWrite(!0),e.resetGLState()}}}function Z(e,t){function i(){var e=new Float32Array([-.5,-.5,0,0,.5,-.5,1,0,.5,.5,1,1,-.5,.5,0,1]),t=new Uint16Array([0,1,2,0,2,3]);s=f.createBuffer(),c=f.createBuffer(),f.bindBuffer(f.ARRAY_BUFFER,s),f.bufferData(f.ARRAY_BUFFER,e,f.STATIC_DRAW),f.bindBuffer(f.ELEMENT_ARRAY_BUFFER,c),f.bufferData(f.ELEMENT_ARRAY_BUFFER,t,f.STATIC_DRAW),l=r(),h={position:f.getAttribLocation(l,"position"),uv:f.getAttribLocation(l,"uv")},d={uvOffset:f.getUniformLocation(l,"uvOffset"),uvScale:f.getUniformLocation(l,"uvScale"),rotation:f.getUniformLocation(l,"rotation"),scale:f.getUniformLocation(l,"scale"),color:f.getUniformLocation(l,"color"),map:f.getUniformLocation(l,"map"),opacity:f.getUniformLocation(l,"opacity"),modelViewMatrix:f.getUniformLocation(l,"modelViewMatrix"),projectionMatrix:f.getUniformLocation(l,"projectionMatrix"),fogType:f.getUniformLocation(l,"fogType"),fogDensity:f.getUniformLocation(l,"fogDensity"),fogNear:f.getUniformLocation(l,"fogNear"),fogFar:f.getUniformLocation(l,"fogFar"),fogColor:f.getUniformLocation(l,"fogColor"),alphaTest:f.getUniformLocation(l,"alphaTest")};var i=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");i.width=8,i.height=8;var a=i.getContext("2d");a.fillStyle="white",a.fillRect(0,0,8,8),(p=new n(i)).needsUpdate=!0}function r(){var t=f.createProgram(),i=f.createShader(f.VERTEX_SHADER),n=f.createShader(f.FRAGMENT_SHADER);return f.shaderSource(i,["precision "+e.getPrecision()+" float;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform float rotation;","uniform vec2 scale;","uniform vec2 uvOffset;","uniform vec2 uvScale;","attribute vec2 position;","attribute vec2 uv;","varying vec2 vUV;","void main() {","vUV = uvOffset + uv * uvScale;","vec2 alignedPosition = position * scale;","vec2 rotatedPosition;","rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;","rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;","vec4 finalPosition;","finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );","finalPosition.xy += rotatedPosition;","finalPosition = projectionMatrix * finalPosition;","gl_Position = finalPosition;","}"].join("\n")),f.shaderSource(n,["precision "+e.getPrecision()+" float;","uniform vec3 color;","uniform sampler2D map;","uniform float opacity;","uniform int fogType;","uniform vec3 fogColor;","uniform float fogDensity;","uniform float fogNear;","uniform float fogFar;","uniform float alphaTest;","varying vec2 vUV;","void main() {","vec4 texture = texture2D( map, vUV );","if ( texture.a < alphaTest ) discard;","gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );","if ( fogType > 0 ) {","float depth = gl_FragCoord.z / gl_FragCoord.w;","float fogFactor = 0.0;","if ( fogType == 1 ) {","fogFactor = smoothstep( fogNear, fogFar, depth );","} else {","const float LOG2 = 1.442695;","fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );","fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );","}","gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );","}","}"].join("\n")),f.compileShader(i),f.compileShader(n),f.attachShader(t,i),f.attachShader(t,n),f.linkProgram(t),t}function a(e,t){return e.renderOrder!==t.renderOrder?e.renderOrder-t.renderOrder:e.z!==t.z?t.z-e.z:t.id-e.id}var s,c,l,h,d,p,f=e.context,g=e.state,m=new u,M=new o,y=new u;this.render=function(n,r){if(0!==t.length){l===undefined&&i(),f.useProgram(l),g.initAttributes(),g.enableAttribute(h.position),g.enableAttribute(h.uv),g.disableUnusedAttributes(),g.disable(f.CULL_FACE),g.enable(f.BLEND),f.bindBuffer(f.ARRAY_BUFFER,s),f.vertexAttribPointer(h.position,2,f.FLOAT,!1,16,0),f.vertexAttribPointer(h.uv,2,f.FLOAT,!1,16,8),f.bindBuffer(f.ELEMENT_ARRAY_BUFFER,c),f.uniformMatrix4fv(d.projectionMatrix,!1,r.projectionMatrix.elements),g.activeTexture(f.TEXTURE0),f.uniform1i(d.map,0);var o=0,u=0,v=n.fog;v?(f.uniform3f(d.fogColor,v.color.r,v.color.g,v.color.b),v.isFog?(f.uniform1f(d.fogNear,v.near),f.uniform1f(d.fogFar,v.far),f.uniform1i(d.fogType,1),o=1,u=1):v.isFogExp2&&(f.uniform1f(d.fogDensity,v.density),f.uniform1i(d.fogType,2),o=2,u=2)):(f.uniform1i(d.fogType,0),o=0,u=0);for(var w=0,x=t.length;w0&&console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.")}function ie(e,t){this.normal=e!==undefined?e:new u(1,0,0),this.constant=t!==undefined?t:0}function ne(e,t,i,n,r,a){this.planes=[e!==undefined?e:new ie,t!==undefined?t:new ie,i!==undefined?i:new ie,n!==undefined?n:new ie,r!==undefined?r:new ie,a!==undefined?a:new ie]}function re(e,t,n,s){function o(t,i,n,r){var a=t.geometry,s=null,o=N,u=t.customDepthMaterial;if(n&&(o=L,u=t.customDistanceMaterial),u)s=u;else{var c=!1;i.morphTargets&&(a&&a.isBufferGeometry?c=a.morphAttributes&&a.morphAttributes.position&&a.morphAttributes.position.length>0:a&&a.isGeometry&&(c=a.morphTargets&&a.morphTargets.length>0));var l=t.isSkinnedMesh&&i.skinning,h=0;c&&(h|=x),l&&(h|=T),s=o[h]}if(e.localClippingEnabled&&!0===i.clipShadows&&0!==i.clippingPlanes.length){var d=s.uuid,p=i.uuid,f=D[d];f===undefined&&(f={},D[d]=f);var g=f[p];g===undefined&&(g=s.clone(),f[p]=g),s=g}s.visible=i.visible,s.wireframe=i.wireframe;var m=i.side;return P.renderSingleSided&&m==pr&&(m=hr),P.renderReverseSided&&(m===hr?m=dr:m===dr&&(m=hr)),s.side=m,s.clipShadows=i.clipShadows,s.clippingPlanes=i.clippingPlanes,s.wireframeLinewidth=i.wireframeLinewidth,s.linewidth=i.linewidth,n&&s.uniforms.lightPos!==undefined&&s.uniforms.lightPos.value.copy(r),s}function l(e,t,i){if(!1!==e.visible){0!=(e.layers.mask&t.layers.mask)&&(e.isMesh||e.isLine||e.isPoints)&&(!e.castShadow||!1!==e.frustumCulled&&!0!==p.intersectsObject(e)||!0===e.material.visible&&(e.modelViewMatrix.multiplyMatrices(i.matrixWorldInverse,e.matrixWorld),w.push(e)));for(var n=e.children,r=0,a=n.length;r0?1:-1,p[g]=_.x,p[g+1]=_.y,p[g+2]=_.z,f[m]=z/l,f[m+1]=1-j/w,g+=3,m+=2,b+=1}for(j=0;j65535?Uint32Array:Uint16Array)(l),d=new Float32Array(3*c),p=new Float32Array(3*c),f=new Float32Array(2*c),g=0,m=0,M=0,y=0,v=0;s("z","y","x",-1,-1,i,t,e,a,r,0),s("z","y","x",1,-1,i,t,-e,a,r,1),s("x","z","y",1,1,e,i,t,n,a,2),s("x","z","y",1,-1,e,i,-t,n,a,3),s("x","y","z",1,-1,e,t,i,n,r,4),s("x","y","z",-1,-1,e,t,-i,n,r,5),this.setIndex(new pe(h,1)),this.addAttribute("position",new pe(d,3)),this.addAttribute("normal",new pe(p,3)),this.addAttribute("uv",new pe(f,2))}function be(e,t,i,n){De.call(this),this.type="PlaneBufferGeometry",this.parameters={width:e,height:t,widthSegments:i,heightSegments:n};for(var r=e/2,a=t/2,s=Math.floor(i)||1,o=Math.floor(n)||1,u=s+1,c=o+1,l=e/s,h=t/o,d=new Float32Array(u*c*3),p=new Float32Array(u*c*3),f=new Float32Array(u*c*2),g=0,m=0,M=0;M65535?Uint32Array:Uint16Array)(s*o*6),M=0;M/g;return e.replace(t,function(e,t){var i=ws[t];if(i===undefined)throw new Error("Can not resolve #include <"+t+">");return We(i)})}function Xe(e){var t=/for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g;return e.replace(t,function(e,t,i,n){for(var r="",a=parseInt(t);a0?e.gammaFactor:1,m=Ge(a,n,e.extensions),M=Qe(s),y=r.createProgram();i.isRawShaderMaterial?(p=[M,"\n"].filter(Ve).join("\n"),f=[m,M,"\n"].filter(Ve).join("\n")):(p=["precision "+n.precision+" float;","precision "+n.precision+" int;","#define SHADER_NAME "+i.__webglShader.name,M,n.supportsVertexTextures?"#define VERTEX_TEXTURES":"","#define GAMMA_FACTOR "+g,"#define MAX_BONES "+n.maxBones,n.map?"#define USE_MAP":"",n.envMap?"#define USE_ENVMAP":"",n.envMap?"#define "+h:"",n.lightMap?"#define USE_LIGHTMAP":"",n.aoMap?"#define USE_AOMAP":"",n.emissiveMap?"#define USE_EMISSIVEMAP":"",n.bumpMap?"#define USE_BUMPMAP":"",n.normalMap?"#define USE_NORMALMAP":"",n.displacementMap&&n.supportsVertexTextures?"#define USE_DISPLACEMENTMAP":"",n.specularMap?"#define USE_SPECULARMAP":"",n.roughnessMap?"#define USE_ROUGHNESSMAP":"",n.metalnessMap?"#define USE_METALNESSMAP":"",n.alphaMap?"#define USE_ALPHAMAP":"",n.vertexColors?"#define USE_COLOR":"",n.flatShading?"#define FLAT_SHADED":"",n.skinning?"#define USE_SKINNING":"",n.useVertexTexture?"#define BONE_TEXTURE":"",n.morphTargets?"#define USE_MORPHTARGETS":"",n.morphNormals&&!1===n.flatShading?"#define USE_MORPHNORMALS":"",n.doubleSided?"#define DOUBLE_SIDED":"",n.flipSided?"#define FLIP_SIDED":"","#define NUM_CLIPPING_PLANES "+n.numClippingPlanes,n.shadowMapEnabled?"#define USE_SHADOWMAP":"",n.shadowMapEnabled?"#define "+c:"",n.sizeAttenuation?"#define USE_SIZEATTENUATION":"",n.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",n.logarithmicDepthBuffer&&e.extensions.get("EXT_frag_depth")?"#define USE_LOGDEPTHBUF_EXT":"","uniform mat4 modelMatrix;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform mat4 viewMatrix;","uniform mat3 normalMatrix;","uniform vec3 cameraPosition;","attribute vec3 position;","attribute vec3 normal;","attribute vec2 uv;","#ifdef USE_COLOR","\tattribute vec3 color;","#endif","#ifdef USE_MORPHTARGETS","\tattribute vec3 morphTarget0;","\tattribute vec3 morphTarget1;","\tattribute vec3 morphTarget2;","\tattribute vec3 morphTarget3;","\t#ifdef USE_MORPHNORMALS","\t\tattribute vec3 morphNormal0;","\t\tattribute vec3 morphNormal1;","\t\tattribute vec3 morphNormal2;","\t\tattribute vec3 morphNormal3;","\t#else","\t\tattribute vec3 morphTarget4;","\t\tattribute vec3 morphTarget5;","\t\tattribute vec3 morphTarget6;","\t\tattribute vec3 morphTarget7;","\t#endif","#endif","#ifdef USE_SKINNING","\tattribute vec4 skinIndex;","\tattribute vec4 skinWeight;","#endif","\n"].filter(Ve).join("\n"),f=[m,"precision "+n.precision+" float;","precision "+n.precision+" int;","#define SHADER_NAME "+i.__webglShader.name,M,n.alphaTest?"#define ALPHATEST "+n.alphaTest:"","#define GAMMA_FACTOR "+g,n.useFog&&n.fog?"#define USE_FOG":"",n.useFog&&n.fogExp?"#define FOG_EXP2":"",n.map?"#define USE_MAP":"",n.envMap?"#define USE_ENVMAP":"",n.envMap?"#define "+l:"",n.envMap?"#define "+h:"",n.envMap?"#define "+d:"",n.lightMap?"#define USE_LIGHTMAP":"",n.aoMap?"#define USE_AOMAP":"",n.emissiveMap?"#define USE_EMISSIVEMAP":"",n.bumpMap?"#define USE_BUMPMAP":"",n.normalMap?"#define USE_NORMALMAP":"",n.specularMap?"#define USE_SPECULARMAP":"",n.roughnessMap?"#define USE_ROUGHNESSMAP":"",n.metalnessMap?"#define USE_METALNESSMAP":"",n.alphaMap?"#define USE_ALPHAMAP":"",n.vertexColors?"#define USE_COLOR":"",n.gradientMap?"#define USE_GRADIENTMAP":"",n.flatShading?"#define FLAT_SHADED":"",n.doubleSided?"#define DOUBLE_SIDED":"",n.flipSided?"#define FLIP_SIDED":"","#define NUM_CLIPPING_PLANES "+n.numClippingPlanes,"#define UNION_CLIPPING_PLANES "+(n.numClippingPlanes-n.numClipIntersection),n.shadowMapEnabled?"#define USE_SHADOWMAP":"",n.shadowMapEnabled?"#define "+c:"",n.premultipliedAlpha?"#define PREMULTIPLIED_ALPHA":"",n.physicallyCorrectLights?"#define PHYSICALLY_CORRECT_LIGHTS":"",n.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",n.logarithmicDepthBuffer&&e.extensions.get("EXT_frag_depth")?"#define USE_LOGDEPTHBUF_EXT":"",n.envMap&&e.extensions.get("EXT_shader_texture_lod")?"#define TEXTURE_LOD_EXT":"","uniform mat4 viewMatrix;","uniform vec3 cameraPosition;",n.toneMapping!==$r?"#define TONE_MAPPING":"",n.toneMapping!==$r?ws.tonemapping_pars_fragment:"",n.toneMapping!==$r?Fe("toneMapping",n.toneMapping):"",n.outputEncoding||n.mapEncoding||n.envMapEncoding||n.emissiveMapEncoding?ws.encodings_pars_fragment:"",n.mapEncoding?ke("mapTexelToLinear",n.mapEncoding):"",n.envMapEncoding?ke("envMapTexelToLinear",n.envMapEncoding):"",n.emissiveMapEncoding?ke("emissiveMapTexelToLinear",n.emissiveMapEncoding):"",n.outputEncoding?Be("linearToOutputTexel",n.outputEncoding):"",n.depthPacking?"#define DEPTH_PACKING "+i.depthPacking:"","\n"].filter(Ve).join("\n")),o=He(o=We(o,n),n),u=He(u=We(u,n),n),i.isShaderMaterial||(o=Xe(o),u=Xe(u));var v=p+o,w=f+u,x=Ue(r,r.VERTEX_SHADER,v),T=Ue(r,r.FRAGMENT_SHADER,w);r.attachShader(y,x),r.attachShader(y,T),i.index0AttributeName!==undefined?r.bindAttribLocation(y,0,i.index0AttributeName):!0===n.morphTargets&&r.bindAttribLocation(y,0,"position"),r.linkProgram(y);var E=r.getProgramInfoLog(y),N=r.getShaderInfoLog(x),L=r.getShaderInfoLog(T),D=!0,A=!0;!1===r.getProgramParameter(y,r.LINK_STATUS)?(D=!1,console.error("THREE.WebGLProgram: shader error: ",r.getError(),"gl.VALIDATE_STATUS",r.getProgramParameter(y,r.VALIDATE_STATUS),"gl.getProgramInfoLog",E,N,L)):""!==E?console.warn("THREE.WebGLProgram: gl.getProgramInfoLog()",E):""!==N&&""!==L||(A=!1),A&&(this.diagnostics={runnable:D,material:i,programLog:E,vertexShader:{log:N,prefix:p},fragmentShader:{log:L,prefix:f}}),r.deleteShader(x),r.deleteShader(T);var I;this.getUniforms=function(){return I===undefined&&(I=new Y(r,y,e)),I};var b;return this.getAttributes=function(){return b===undefined&&(b=Ye(r,y)),b},this.destroy=function(){r.deleteProgram(y),this.program=undefined},Object.defineProperties(this,{uniforms:{get:function(){return console.warn("THREE.WebGLProgram: .uniforms is now .getUniforms()."),this.getUniforms()}},attributes:{get:function(){return console.warn("THREE.WebGLProgram: .attributes is now .getAttributes()."),this.getAttributes()}}}),this.id=As++,this.code=t,this.usedTimes=1,this.program=y,this.vertexShader=x,this.fragmentShader=T,this}function qe(e,t){function i(e){if(t.floatVertexTextures&&e&&e.skeleton&&e.skeleton.useVertexTexture)return 1024;var i=t.maxVertexUniforms,n=Math.floor((i-20)/4);return e!==undefined&&e&&e.isSkinnedMesh&&(n=Math.min(e.skeleton.bones.length,n))0,shadowMapType:e.shadowMap.type,toneMapping:e.toneMapping,physicallyCorrectLights:e.physicallyCorrectLights,premultipliedAlpha:r.premultipliedAlpha,alphaTest:r.alphaTest,doubleSided:r.side===pr,flipSided:r.side===dr,depthPacking:r.depthPacking!==undefined&&r.depthPacking}},this.getProgramCode=function(e,t){var i=[];if(t.shaderID?i.push(t.shaderID):(i.push(e.fragmentShader),i.push(e.vertexShader)),e.defines!==undefined)for(var n in e.defines)i.push(n),i.push(e.defines[n]);for(var r=0;r65535?Uint32Array:Uint16Array)(a),1);return n(g,e.ELEMENT_ARRAY_BUFFER),r.wireframe=g,g},update:function(t){var i=s.get(t);t.geometry.isGeometry&&i.updateFromObject(t);var r=i.index,a=i.attributes;null!==r&&n(r,e.ELEMENT_ARRAY_BUFFER);for(var o in a)n(a[o],e.ARRAY_BUFFER);var u=i.morphAttributes;for(var o in u)for(var c=u[o],l=0,h=c.length;lt||e.height>t){var i=t/Math.max(e.width,e.height),n=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");return n.width=Math.floor(e.width*i),n.height=Math.floor(e.height*i),n.getContext("2d").drawImage(e,0,0,e.width,e.height,0,0,n.width,n.height),console.warn("THREE.WebGLRenderer: image is too big ("+e.width+"x"+e.height+"). Resized to "+n.width+"x"+n.height,e),n}return e}function u(e){return ds.isPowerOfTwo(e.width)&&ds.isPowerOfTwo(e.height)}function c(e){if(e instanceof HTMLImageElement||e instanceof HTMLCanvasElement){var t=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");return t.width=ds.nearestPowerOfTwo(e.width),t.height=ds.nearestPowerOfTwo(e.height),t.getContext("2d").drawImage(e,0,0,t.width,t.height),console.warn("THREE.WebGLRenderer: image is not power of two ("+e.width+"x"+e.height+"). Resized to "+t.width+"x"+t.height,e),t}return e}function l(e){return e.wrapS!==pa||e.wrapT!==pa||e.minFilter!==ma&&e.minFilter!==va}function h(t){return t===ma||t===Ma||t===ya?e.NEAREST:e.LINEAR}function d(e){var t=e.target;t.removeEventListener("dispose",d),f(t),E.textures--}function p(e){var t=e.target;t.removeEventListener("dispose",p),g(t),E.textures--}function f(t){var i=n.get(t);if(t.image&&i.__image__webglTextureCube)e.deleteTexture(i.__image__webglTextureCube);else{if(i.__webglInit===undefined)return;e.deleteTexture(i.__webglTexture)}n["delete"](t)}function g(t){var i=n.get(t),r=n.get(t.texture);if(t){if(r.__webglTexture!==undefined&&e.deleteTexture(r.__webglTexture),t.depthTexture&&t.depthTexture.dispose(),t.isWebGLRenderTargetCube)for(var a=0;a<6;a++)e.deleteFramebuffer(i.__webglFramebuffer[a]),i.__webglDepthbuffer&&e.deleteRenderbuffer(i.__webglDepthbuffer[a]);else e.deleteFramebuffer(i.__webglFramebuffer),i.__webglDepthbuffer&&e.deleteRenderbuffer(i.__webglDepthbuffer);n["delete"](t.texture),n["delete"](t)}}function m(t,r){var a=n.get(t);if(t.version>0&&a.__version!==t.version){var s=t.image;if(s===undefined)console.warn("THREE.WebGLRenderer: Texture marked for update but image is undefined",t);else{if(!1!==s.complete)return void y(a,t,r);console.warn("THREE.WebGLRenderer: Texture marked for update but image is incomplete",t)}}i.activeTexture(e.TEXTURE0+r),i.bindTexture(e.TEXTURE_2D,a.__webglTexture)}function M(i,s,o){var u;if(o?(e.texParameteri(i,e.TEXTURE_WRAP_S,a(s.wrapS)),e.texParameteri(i,e.TEXTURE_WRAP_T,a(s.wrapT)),e.texParameteri(i,e.TEXTURE_MAG_FILTER,a(s.magFilter)),e.texParameteri(i,e.TEXTURE_MIN_FILTER,a(s.minFilter))):(e.texParameteri(i,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(i,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),s.wrapS===pa&&s.wrapT===pa||console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.",s),e.texParameteri(i,e.TEXTURE_MAG_FILTER,h(s.magFilter)),e.texParameteri(i,e.TEXTURE_MIN_FILTER,h(s.minFilter)),s.minFilter!==ma&&s.minFilter!==va&&console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.",s)),u=t.get("EXT_texture_filter_anisotropic")){if(s.type===ba&&null===t.get("OES_texture_float_linear"))return;if(s.type===Sa&&null===t.get("OES_texture_half_float_linear"))return;(s.anisotropy>1||n.get(s).__currentAnisotropy)&&(e.texParameterf(i,u.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(s.anisotropy,r.getMaxAnisotropy())),n.get(s).__currentAnisotropy=s.anisotropy)}}function y(t,n,s){t.__webglInit===undefined&&(t.__webglInit=!0,n.addEventListener("dispose",d),t.__webglTexture=e.createTexture(),E.textures++),i.activeTexture(e.TEXTURE0+s),i.bindTexture(e.TEXTURE_2D,t.__webglTexture),e.pixelStorei(e.UNPACK_FLIP_Y_WEBGL,n.flipY),e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL,n.premultiplyAlpha),e.pixelStorei(e.UNPACK_ALIGNMENT,n.unpackAlignment);var h=o(n.image,r.maxTextureSize);l(n)&&!1===u(h)&&(h=c(h));var p=u(h),f=a(n.format),g=a(n.type);M(e.TEXTURE_2D,n,p);var m,y=n.mipmaps;if(n.isDepthTexture){var v=e.DEPTH_COMPONENT;if(n.type===ba){if(!N)throw new Error("Float Depth Texture only supported in WebGL2.0");v=e.DEPTH_COMPONENT32F}else N&&(v=e.DEPTH_COMPONENT16);n.format===Fa&&v===e.DEPTH_COMPONENT&&n.type!==Da&&n.type!==Ia&&(console.warn("THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture."),n.type=Da,g=a(n.type)),n.format===Ga&&(v=e.DEPTH_STENCIL,n.type!==za&&(console.warn("THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture."),n.type=za,g=a(n.type))),i.texImage2D(e.TEXTURE_2D,0,v,h.width,h.height,0,f,g,null)}else if(n.isDataTexture)if(y.length>0&&p){for(var w=0,x=y.length;w-1?i.compressedTexImage2D(e.TEXTURE_2D,w,f,m.width,m.height,0,m.data):console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()"):i.texImage2D(e.TEXTURE_2D,w,f,m.width,m.height,0,f,g,m.data);else if(y.length>0&&p){for(var w=0,x=y.length;w0&&c.__version!==t.version){c.__image__webglTextureCube||(t.addEventListener("dispose",d),c.__image__webglTextureCube=e.createTexture(),E.textures++),i.activeTexture(e.TEXTURE0+s),i.bindTexture(e.TEXTURE_CUBE_MAP,c.__image__webglTextureCube),e.pixelStorei(e.UNPACK_FLIP_Y_WEBGL,t.flipY);for(var l=t&&t.isCompressedTexture,h=t.image[0]&&t.image[0].isDataTexture,p=[],f=0;f<6;f++)p[f]=l||h?h?t.image[f].image:t.image[f]:o(t.image[f],r.maxCubemapSize);var g=u(p[0]),m=a(t.format),y=a(t.type);for(M(e.TEXTURE_CUBE_MAP,t,g),f=0;f<6;f++)if(l)for(var v,w=p[f].mipmaps,x=0,T=w.length;x-1?i.compressedTexImage2D(e.TEXTURE_CUBE_MAP_POSITIVE_X+f,x,m,v.width,v.height,0,v.data):console.warn("THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()"):i.texImage2D(e.TEXTURE_CUBE_MAP_POSITIVE_X+f,x,m,v.width,v.height,0,m,y,v.data);else h?i.texImage2D(e.TEXTURE_CUBE_MAP_POSITIVE_X+f,0,m,p[f].width,p[f].height,0,m,y,p[f].data):i.texImage2D(e.TEXTURE_CUBE_MAP_POSITIVE_X+f,0,m,m,y,p[f]);t.generateMipmaps&&g&&e.generateMipmap(e.TEXTURE_CUBE_MAP),c.__version=t.version,t.onUpdate&&t.onUpdate(t)}else i.activeTexture(e.TEXTURE0+s),i.bindTexture(e.TEXTURE_CUBE_MAP,c.__image__webglTextureCube)},this.setTextureCubeDynamic=function(t,r){i.activeTexture(e.TEXTURE0+r),i.bindTexture(e.TEXTURE_CUBE_MAP,n.get(t).__webglTexture)},this.setupRenderTarget=function(t){var r=n.get(t),a=n.get(t.texture);t.addEventListener("dispose",p),a.__webglTexture=e.createTexture(),E.textures++;var s=!0===t.isWebGLRenderTargetCube,o=u(t);if(s)for(r.__webglFramebuffer=[],c=0;c<6;c++)r.__webglFramebuffer[c]=e.createFramebuffer();else r.__webglFramebuffer=e.createFramebuffer();if(s){i.bindTexture(e.TEXTURE_CUBE_MAP,a.__webglTexture),M(e.TEXTURE_CUBE_MAP,t.texture,o);for(var c=0;c<6;c++)v(r.__webglFramebuffer[c],t,e.COLOR_ATTACHMENT0,e.TEXTURE_CUBE_MAP_POSITIVE_X+c);t.texture.generateMipmaps&&o&&e.generateMipmap(e.TEXTURE_CUBE_MAP),i.bindTexture(e.TEXTURE_CUBE_MAP,null)}else i.bindTexture(e.TEXTURE_2D,a.__webglTexture),M(e.TEXTURE_2D,t.texture,o),v(r.__webglFramebuffer,t,e.COLOR_ATTACHMENT0,e.TEXTURE_2D),t.texture.generateMipmaps&&o&&e.generateMipmap(e.TEXTURE_2D),i.bindTexture(e.TEXTURE_2D,null);t.depthBuffer&&T(t)},this.updateRenderTargetMipmap=function(t){var r=t.texture;if(r.generateMipmaps&&u(t)&&r.minFilter!==ma&&r.minFilter!==va){var a=t&&t.isWebGLRenderTargetCube?e.TEXTURE_CUBE_MAP:e.TEXTURE_2D,s=n.get(r).__webglTexture;i.bindTexture(a,s),e.generateMipmap(a),i.bindTexture(a,null)}}}function et(){var e={};return{get:function(t){var i=t.uuid,n=e[i];return n===undefined&&(n={},e[i]=n),n},"delete":function(t){delete e[t.uuid]},clear:function(){e={}}}}function tt(e,t,i){function n(t,i,n){var r=new Uint8Array(4),a=e.createTexture();e.bindTexture(t,a),e.texParameteri(t,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(t,e.TEXTURE_MAG_FILTER,e.NEAREST);for(var s=0;s=1,P=null,k={},B=new r,F=new r,G={};return G[e.TEXTURE_2D]=n(e.TEXTURE_2D,e.TEXTURE_2D,1),G[e.TEXTURE_CUBE_MAP]=n(e.TEXTURE_CUBE_MAP,e.TEXTURE_CUBE_MAP_POSITIVE_X,6),{buffers:{color:d,depth:p,stencil:f},init:function(){d.setClear(0,0,0,1),p.setClear(1),f.setClear(0),a(e.DEPTH_TEST),u(Vr),c(!1),l(sr),a(e.CULL_FACE),a(e.BLEND),o(wr)},initAttributes:function(){for(var e=0,t=m.length;e0&&e.getShaderPrecisionFormat(e.FRAGMENT_SHADER,e.HIGH_FLOAT).precision>0)return"highp";t="mediump"}return"mediump"===t&&e.getShaderPrecisionFormat(e.VERTEX_SHADER,e.MEDIUM_FLOAT).precision>0&&e.getShaderPrecisionFormat(e.FRAGMENT_SHADER,e.MEDIUM_FLOAT).precision>0?"mediump":"lowp"}var r,a=i.precision!==undefined?i.precision:"highp",s=n(a);s!==a&&(console.warn("THREE.WebGLRenderer:",a,"not supported, using",s,"instead."),a=s);var o=!0===i.logarithmicDepthBuffer&&!!t.get("EXT_frag_depth"),u=e.getParameter(e.MAX_TEXTURE_IMAGE_UNITS),c=e.getParameter(e.MAX_VERTEX_TEXTURE_IMAGE_UNITS),l=e.getParameter(e.MAX_TEXTURE_SIZE),h=e.getParameter(e.MAX_CUBE_MAP_TEXTURE_SIZE),d=e.getParameter(e.MAX_VERTEX_ATTRIBS),p=e.getParameter(e.MAX_VERTEX_UNIFORM_VECTORS),f=e.getParameter(e.MAX_VARYING_VECTORS),g=e.getParameter(e.MAX_FRAGMENT_UNIFORM_VECTORS),m=c>0,M=!!t.get("OES_texture_float");return{getMaxAnisotropy:function(){if(r!==undefined)return r;var i=t.get("EXT_texture_filter_anisotropic");return r=null!==i?e.getParameter(i.MAX_TEXTURE_MAX_ANISOTROPY_EXT):0},getMaxPrecision:n,precision:a,logarithmicDepthBuffer:o,maxTextures:u,maxVertexTextures:c,maxTextureSize:l,maxCubemapSize:h,maxAttributes:d,maxVertexUniforms:p,maxVaryings:f,maxFragmentUniforms:g,vertexTextures:m,floatFragmentTextures:M,floatVertexTextures:m&&M}}function nt(e){var t={};return{get:function(i){if(t[i]!==undefined)return t[i];var n;switch(i){case"WEBGL_depth_texture":n=e.getExtension("WEBGL_depth_texture")||e.getExtension("MOZ_WEBGL_depth_texture")||e.getExtension("WEBKIT_WEBGL_depth_texture");break;case"EXT_texture_filter_anisotropic":n=e.getExtension("EXT_texture_filter_anisotropic")||e.getExtension("MOZ_EXT_texture_filter_anisotropic")||e.getExtension("WEBKIT_EXT_texture_filter_anisotropic");break;case"WEBGL_compressed_texture_s3tc":n=e.getExtension("WEBGL_compressed_texture_s3tc")||e.getExtension("MOZ_WEBGL_compressed_texture_s3tc")||e.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc");break;case"WEBGL_compressed_texture_pvrtc":n=e.getExtension("WEBGL_compressed_texture_pvrtc")||e.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc");break;case"WEBGL_compressed_texture_etc1":n=e.getExtension("WEBGL_compressed_texture_etc1");break;default:n=e.getExtension(i)}return null===n&&console.warn("THREE.WebGLRenderer: "+i+" extension not supported."),t[i]=n,n}}}function rt(){function e(){c.value!==n&&(c.value=n,c.needsUpdate=r>0),i.numPlanes=r,i.numIntersection=0}function t(e,t,n,r){var a=null!==e?e.length:0,s=null;if(0!==a){if(s=c.value,!0!==r||null===s){var l=n+4*a,h=t.matrixWorldInverse;u.getNormalMatrix(h),(null===s||s.length=0){var l=a[u];if(l!==undefined){var h=l.normalized,d=l.itemSize,p=lt.getAttributeProperties(l),f=p.__webglBuffer,g=p.type,m=p.bytesPerElement;if(l.isInterleavedBufferAttribute){var M=l.data,y=M.stride,v=l.offset;M&&M.isInstancedInterleavedBuffer?(ot.enableAttributeAndDivisor(c,M.meshPerAttribute,r),i.maxInstancedCount===undefined&&(i.maxInstancedCount=M.meshPerAttribute*M.count)):ot.enableAttribute(c),Ze.bindBuffer(Ze.ARRAY_BUFFER,f),Ze.vertexAttribPointer(c,d,g,h,y*m,(n*y+v)*m)}else l.isInstancedBufferAttribute?(ot.enableAttributeAndDivisor(c,l.meshPerAttribute,r),i.maxInstancedCount===undefined&&(i.maxInstancedCount=l.meshPerAttribute*l.count)):ot.enableAttribute(c),Ze.bindBuffer(Ze.ARRAY_BUFFER,f),Ze.vertexAttribPointer(c,d,g,h,0,n*d*m)}else if(o!==undefined){var w=o[u];if(w!==undefined)switch(w.length){case 2:Ze.vertexAttrib2fv(c,w);break;case 3:Ze.vertexAttrib3fv(c,w);break;case 4:Ze.vertexAttrib4fv(c,w);break;default:Ze.vertexAttrib1fv(c,w)}}}}ot.disableUnusedAttributes()}}function d(e,t){return Math.abs(t[0])-Math.abs(e[0])}function p(e,t){return e.object.renderOrder!==t.object.renderOrder?e.object.renderOrder-t.object.renderOrder:e.material.program&&t.material.program&&e.material.program!==t.material.program?e.material.program.id-t.material.program.id:e.material.id!==t.material.id?e.material.id-t.material.id:e.z!==t.z?e.z-t.z:e.id-t.id}function f(e,t){return e.object.renderOrder!==t.object.renderOrder?e.object.renderOrder-t.object.renderOrder:e.z!==t.z?t.z-e.z:e.id-t.id}function g(e,t,i,n,r){var a,s;i.transparent?(a=ie,s=++ae):(a=$,s=++te);var o=a[s];o!==undefined?(o.id=e.id,o.object=e,o.geometry=t,o.material=i,o.z=Ye.z,o.group=r):(o={id:e.id,object:e,geometry:t,material:i,z:Ye.z,group:r},a.push(o))}function m(e){var t=e.geometry;return null===t.boundingSphere&&t.computeBoundingSphere(),Ge.copy(t.boundingSphere).applyMatrix4(e.matrixWorld),y(Ge)}function M(e){return Ge.center.set(0,0,0),Ge.radius=.7071067811865476,Ge.applyMatrix4(e.matrixWorld),y(Ge)}function y(e){if(!Pe.intersectsSphere(e))return!1;var t=ke.numPlanes;if(0===t)return!0;var i=ce.clippingPlanes,n=e.center,r=-e.radius,a=0;do{if(i[a].distanceToPoint(n)=0&&e.numSupportedMorphTargets++}if(e.morphNormals){e.numSupportedMorphNormals=0;for(var d=0;d=0&&e.numSupportedMorphNormals++}var p=n.__webglShader.uniforms;(e.isShaderMaterial||e.isRawShaderMaterial)&&!0!==e.clipping||(n.numClippingPlanes=ke.numPlanes,n.numIntersection=ke.numIntersection,p.clippingPlanes=ke.uniform),n.fog=t,n.lightsHash=We.hash,e.lights&&(p.ambientLightColor.value=We.ambient,p.directionalLights.value=We.directional,p.spotLights.value=We.spot,p.rectAreaLights.value=We.rectArea,p.pointLights.value=We.point,p.hemisphereLights.value=We.hemi,p.directionalShadowMap.value=We.directionalShadowMap,p.directionalShadowMatrix.value=We.directionalShadowMatrix,p.spotShadowMap.value=We.spotShadowMap,p.spotShadowMatrix.value=We.spotShadowMatrix,p.pointShadowMap.value=We.pointShadowMap,p.pointShadowMatrix.value=We.pointShadowMatrix);var f=n.program.getUniforms(),g=Y.seqWithValue(f.seq,p);n.uniformsList=g}function T(e){e.side===pr?ot.disable(Ze.CULL_FACE):ot.enable(Ze.CULL_FACE),ot.setFlipSided(e.side===dr),!0===e.transparent?ot.setBlending(e.blending,e.blendEquation,e.blendSrc,e.blendDst,e.blendEquationAlpha,e.blendSrcAlpha,e.blendDstAlpha,e.premultipliedAlpha):ot.setBlending(vr),ot.setDepthFunc(e.depthFunc),ot.setDepthTest(e.depthTest),ot.setDepthWrite(e.depthWrite),ot.setColorWrite(e.colorWrite),ot.setPolygonOffset(e.polygonOffset,e.polygonOffsetFactor,e.polygonOffsetUnits)}function E(e,t,i,n){we=0;var r=ut.get(i);if(Be&&(Fe||e!==me)){var a=e===me&&i.id===fe;ke.setState(i.clippingPlanes,i.clipIntersection,i.clipShadows,e,r,a)}!1===i.needsUpdate&&(r.program===undefined?i.needsUpdate=!0:i.fog&&r.fog!==t?i.needsUpdate=!0:i.lights&&r.lightsHash!==We.hash?i.needsUpdate=!0:r.numClippingPlanes===undefined||r.numClippingPlanes===ke.numPlanes&&r.numIntersection===ke.numIntersection||(i.needsUpdate=!0)),i.needsUpdate&&(x(i,t,n),i.needsUpdate=!1);var s=!1,o=!1,u=!1,c=r.program,l=c.getUniforms(),h=r.__webglShader.uniforms;if(c.id!==le&&(Ze.useProgram(c.program),le=c.id,s=!0,o=!0,u=!0),i.id!==fe&&(fe=i.id,o=!0),s||e!==me){if(l.set(Ze,e,"projectionMatrix"),st.logarithmicDepthBuffer&&l.setValue(Ze,"logDepthBufFC",2/(Math.log(e.far+1)/Math.LN2)),e!==me&&(me=e,o=!0,u=!0),i.isShaderMaterial||i.isMeshPhongMaterial||i.isMeshStandardMaterial||i.envMap){var d=l.map.cameraPosition;d!==undefined&&d.setValue(Ze,Ye.setFromMatrixPosition(e.matrixWorld))}(i.isMeshPhongMaterial||i.isMeshLambertMaterial||i.isMeshBasicMaterial||i.isMeshStandardMaterial||i.isShaderMaterial||i.skinning)&&l.setValue(Ze,"viewMatrix",e.matrixWorldInverse),l.set(Ze,ce,"toneMappingExposure"),l.set(Ze,ce,"toneMappingWhitePoint")}if(i.skinning){l.setOptional(Ze,n,"bindMatrix"),l.setOptional(Ze,n,"bindMatrixInverse");var p=n.skeleton;p&&(st.floatVertexTextures&&p.useVertexTexture?(l.set(Ze,p,"boneTexture"),l.set(Ze,p,"boneTextureWidth"),l.set(Ze,p,"boneTextureHeight")):l.setOptional(Ze,p,"boneMatrices"))}return o&&(i.lights&&O(h,u),t&&i.fog&&I(h,t),(i.isMeshBasicMaterial||i.isMeshLambertMaterial||i.isMeshPhongMaterial||i.isMeshStandardMaterial||i.isMeshNormalMaterial||i.isMeshDepthMaterial)&&N(h,i),i.isLineBasicMaterial?L(h,i):i.isLineDashedMaterial?(L(h,i),D(h,i)):i.isPointsMaterial?A(h,i):i.isMeshLambertMaterial?b(h,i):i.isMeshToonMaterial?_(h,i):i.isMeshPhongMaterial?S(h,i):i.isMeshPhysicalMaterial?C(h,i):i.isMeshStandardMaterial?j(h,i):i.isMeshDepthMaterial?i.displacementMap&&(h.displacementMap.value=i.displacementMap,h.displacementScale.value=i.displacementScale,h.displacementBias.value=i.displacementBias):i.isMeshNormalMaterial&&z(h,i),h.ltcMat!==undefined&&(h.ltcMat.value=THREE.UniformsLib.LTC_MAT_TEXTURE),h.ltcMag!==undefined&&(h.ltcMag.value=THREE.UniformsLib.LTC_MAG_TEXTURE),Y.upload(Ze,r.uniformsList,h,ce)),l.set(Ze,n,"modelViewMatrix"),l.set(Ze,n,"normalMatrix"),l.setValue(Ze,"modelMatrix",n.matrixWorld),c}function N(e,t){e.opacity.value=t.opacity,e.diffuse.value=t.color,t.emissive&&e.emissive.value.copy(t.emissive).multiplyScalar(t.emissiveIntensity),e.map.value=t.map,e.specularMap.value=t.specularMap,e.alphaMap.value=t.alphaMap,t.lightMap&&(e.lightMap.value=t.lightMap,e.lightMapIntensity.value=t.lightMapIntensity),t.aoMap&&(e.aoMap.value=t.aoMap,e.aoMapIntensity.value=t.aoMapIntensity);var i;if(t.map?i=t.map:t.specularMap?i=t.specularMap:t.displacementMap?i=t.displacementMap:t.normalMap?i=t.normalMap:t.bumpMap?i=t.bumpMap:t.roughnessMap?i=t.roughnessMap:t.metalnessMap?i=t.metalnessMap:t.alphaMap?i=t.alphaMap:t.emissiveMap&&(i=t.emissiveMap),i!==undefined){i.isWebGLRenderTarget&&(i=i.texture);var n=i.offset,r=i.repeat;e.offsetRepeat.value.set(n.x,n.y,r.x,r.y)}e.envMap.value=t.envMap,e.flipEnvMap.value=t.envMap&&t.envMap.isCubeTexture?-1:1,e.reflectivity.value=t.reflectivity,e.refractionRatio.value=t.refractionRatio}function L(e,t){e.diffuse.value=t.color,e.opacity.value=t.opacity}function D(e,t){e.dashSize.value=t.dashSize,e.totalSize.value=t.dashSize+t.gapSize,e.scale.value=t.scale}function A(e,t){if(e.diffuse.value=t.color,e.opacity.value=t.opacity,e.size.value=t.size*Le,e.scale.value=.5*Ne,e.map.value=t.map,null!==t.map){var i=t.map.offset,n=t.map.repeat;e.offsetRepeat.value.set(i.x,i.y,n.x,n.y)}}function I(e,t){e.fogColor.value=t.color,t.isFog?(e.fogNear.value=t.near,e.fogFar.value=t.far):t.isFogExp2&&(e.fogDensity.value=t.density)}function b(e,t){t.emissiveMap&&(e.emissiveMap.value=t.emissiveMap)}function S(e,t){e.specular.value=t.specular,e.shininess.value=Math.max(t.shininess,1e-4),t.emissiveMap&&(e.emissiveMap.value=t.emissiveMap),t.bumpMap&&(e.bumpMap.value=t.bumpMap,e.bumpScale.value=t.bumpScale),t.normalMap&&(e.normalMap.value=t.normalMap,e.normalScale.value.copy(t.normalScale)),t.displacementMap&&(e.displacementMap.value=t.displacementMap,e.displacementScale.value=t.displacementScale,e.displacementBias.value=t.displacementBias)}function _(e,t){S(e,t),t.gradientMap&&(e.gradientMap.value=t.gradientMap)}function j(e,t){e.roughness.value=t.roughness,e.metalness.value=t.metalness,t.roughnessMap&&(e.roughnessMap.value=t.roughnessMap),t.metalnessMap&&(e.metalnessMap.value=t.metalnessMap),t.emissiveMap&&(e.emissiveMap.value=t.emissiveMap),t.bumpMap&&(e.bumpMap.value=t.bumpMap,e.bumpScale.value=t.bumpScale),t.normalMap&&(e.normalMap.value=t.normalMap,e.normalScale.value.copy(t.normalScale)),t.displacementMap&&(e.displacementMap.value=t.displacementMap,e.displacementScale.value=t.displacementScale,e.displacementBias.value=t.displacementBias),t.envMap&&(e.envMapIntensity.value=t.envMapIntensity)}function C(e,t){e.clearCoat.value=t.clearCoat,e.clearCoatRoughness.value=t.clearCoatRoughness,j(e,t)}function z(e,t){t.bumpMap&&(e.bumpMap.value=t.bumpMap,e.bumpScale.value=t.bumpScale),t.normalMap&&(e.normalMap.value=t.normalMap,e.normalScale.value.copy(t.normalScale)),t.displacementMap&&(e.displacementMap.value=t.displacementMap,e.displacementScale.value=t.displacementScale,e.displacementBias.value=t.displacementBias)}function O(e,t){e.ambientLightColor.needsUpdate=t,e.directionalLights.needsUpdate=t,e.pointLights.needsUpdate=t,e.spotLights.needsUpdate=t,e.rectAreaLights.needsUpdate=t,e.hemisphereLights.needsUpdate=t}function R(e){for(var t=0,i=0,n=e.length;i8&&(p.length=8);for(var m=n.morphAttributes,f=0,g=p.length;f0&&N.renderInstances(n,S,j):N.render(S,j)}},this.render=function(e,t,i,n){if(t===undefined||!0===t.isCamera){ge="",fe=-1,me=null,!0===e.autoUpdate&&e.updateMatrixWorld(),null===t.parent&&t.updateMatrixWorld(),t.matrixWorldInverse.getInverse(t.matrixWorld),Qe.multiplyMatrices(t.projectionMatrix,t.matrixWorldInverse),Pe.setFromMatrix(Qe),K.length=0,te=-1,ae=-1,oe.length=0,ue.length=0,Fe=this.localClippingEnabled,Be=ke.init(this.clippingPlanes,Fe,t),v(e,t),$.length=te+1,ie.length=ae+1,!0===ce.sortObjects&&($.sort(p),ie.sort(f)),Be&&ke.beginShadows(),R(K),wt.render(e,t),U(K,t),Be&&ke.endShadows(),Xe.calls=0,Xe.vertices=0,Xe.faces=0,Xe.points=0,i===undefined&&(i=null),this.setRenderTarget(i);var r=e.background;if(null===r?ot.buffers.color.setClear(xe.r,xe.g,xe.b,Te,W):r&&r.isColor&&(ot.buffers.color.setClear(r.r,r.g,r.b,1,W),n=!0),(this.autoClear||n)&&this.clear(this.autoClearColor,this.autoClearDepth,this.autoClearStencil),r&&r.isCubeTexture?(mt.projectionMatrix.copy(t.projectionMatrix),mt.matrixWorld.extractRotation(t.matrixWorld),mt.matrixWorldInverse.getInverse(mt.matrixWorld),vt.material.uniforms.tCube.value=r,vt.modelViewMatrix.multiplyMatrices(mt.matrixWorldInverse,vt.matrixWorld),lt.update(vt),ce.renderBufferDirect(mt,null,vt.geometry,vt.material,vt,null)):r&&r.isTexture&&(Mt.material.map=r,lt.update(Mt),ce.renderBufferDirect(gt,null,Mt.geometry,Mt.material,Mt,null)),e.overrideMaterial){var a=e.overrideMaterial;w($,e,t,a),w(ie,e,t,a)}else ot.setBlending(vr),w($,e,t),w(ie,e,t);xt.render(e,t),Tt.render(e,t,ve),i&&ct.updateRenderTargetMipmap(i),ot.setDepthTest(!0),ot.setDepthWrite(!0),ot.setColorWrite(!0)}else console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.")},this.setFaceCulling=function(e,t){ot.setCullFace(e),ot.setFlipSided(t===ur)},this.allocTextureUnit=function(){var e=we;return e>=st.maxTextures&&console.warn("WebGLRenderer: trying to use "+e+" texture units while this GPU supports only "+st.maxTextures),we+=1,e},this.setTexture2D=function(){var e=!1;return function(t,i){t&&t.isWebGLRenderTarget&&(e||(console.warn("THREE.WebGLRenderer.setTexture2D: don't use render targets as textures. Use their .texture property instead."),e=!0),t=t.texture),ct.setTexture2D(t,i)}}(),this.setTexture=function(){var e=!1;return function(t,i){e||(console.warn("THREE.WebGLRenderer: .setTexture is deprecated, use setTexture2D instead."),e=!0),ct.setTexture2D(t,i)}}(),this.setTextureCube=function(){var e=!1;return function(t,i){t&&t.isWebGLRenderTargetCube&&(e||(console.warn("THREE.WebGLRenderer.setTextureCube: don't use cube render targets as textures. Use their .texture property instead."),e=!0),t=t.texture),t&&t.isCubeTexture||Array.isArray(t.image)&&6===t.image.length?ct.setTextureCube(t,i):ct.setTextureCubeDynamic(t,i)}}(),this.getCurrentRenderTarget=function(){return he},this.setRenderTarget=function(e){he=e,e&&ut.get(e).__webglFramebuffer===undefined&&ct.setupRenderTarget(e);var t,i=e&&e.isWebGLRenderTargetCube;if(e){var n=ut.get(e);t=i?n.__webglFramebuffer[e.activeCubeFace]:n.__webglFramebuffer,Me.copy(e.scissor),ye=e.scissorTest,ve.copy(e.viewport)}else t=null,Me.copy(Se).multiplyScalar(Le),ye=Re,ve.copy(Ue).multiplyScalar(Le);if(pe!==t&&(Ze.bindFramebuffer(Ze.FRAMEBUFFER,t),pe=t),ot.scissor(Me),ot.setScissorTest(ye),ot.viewport(ve),i){var r=ut.get(e.texture);Ze.framebufferTexture2D(Ze.FRAMEBUFFER,Ze.COLOR_ATTACHMENT0,Ze.TEXTURE_CUBE_MAP_POSITIVE_X+e.activeCubeFace,r.__webglTexture,e.activeMipMapLevel)}},this.readRenderTargetPixels=function(e,t,i,n,r,a){if(!1!==(e&&e.isWebGLRenderTarget)){var s=ut.get(e).__webglFramebuffer;if(s){var o=!1;s!==pe&&(Ze.bindFramebuffer(Ze.FRAMEBUFFER,s),o=!0);try{var u=e.texture,c=u.format,l=u.type;if(c!==Ua&&P(c)!==Ze.getParameter(Ze.IMPLEMENTATION_COLOR_READ_FORMAT))return void console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.");if(!(l===Ea||P(l)===Ze.getParameter(Ze.IMPLEMENTATION_COLOR_READ_TYPE)||l===ba&&(at.get("OES_texture_float")||at.get("WEBGL_color_buffer_float"))||l===Sa&&at.get("EXT_color_buffer_half_float")))return void console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.");Ze.checkFramebufferStatus(Ze.FRAMEBUFFER)===Ze.FRAMEBUFFER_COMPLETE?t>=0&&t<=e.width-n&&i>=0&&i<=e.height-r&&Ze.readPixels(t,i,n,r,P(c),P(l),a):console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.")}finally{o&&Ze.bindFramebuffer(Ze.FRAMEBUFFER,pe)}}}else console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.")}}function st(e,t){this.name="",this.color=new V(e),this.density=t!==undefined?t:25e-5}function ot(e,t,i){this.name="",this.color=new V(e),this.near=t!==undefined?t:1,this.far=i!==undefined?i:1e3}function ut(){ue.call(this),this.type="Scene",this.background=null,this.fog=null,this.overrideMaterial=null,this.autoUpdate=!0}function ct(e,t,i,n,r){ue.call(this),this.lensFlares=[],this.positionScreen=new u,this.customUpdateCallback=undefined,e!==undefined&&this.add(e,t,i,n,r)}function lt(e){q.call(this),this.type="SpriteMaterial",this.color=new V(16777215),this.map=null,this.rotation=0,this.fog=!1,this.lights=!1,this.setValues(e)}function ht(e){ue.call(this),this.type="Sprite",this.material=e!==undefined?e:new lt}function dt(){ue.call(this),this.type="LOD",Object.defineProperties(this,{levels:{enumerable:!0,value:[]}})}function pt(e,t,i){if(this.useVertexTexture=i===undefined||i,this.identityMatrix=new c,e=e||[],this.bones=e.slice(0),this.useVertexTexture){var n=Math.sqrt(4*this.bones.length);n=ds.nextPowerOfTwo(Math.ceil(n)),n=Math.max(n,4),this.boneTextureWidth=n,this.boneTextureHeight=n,this.boneMatrices=new Float32Array(this.boneTextureWidth*this.boneTextureHeight*4),this.boneTexture=new H(this.boneMatrices,this.boneTextureWidth,this.boneTextureHeight,Ua,ba)}else this.boneMatrices=new Float32Array(16*this.bones.length);if(t===undefined)this.calculateInverses();else if(this.bones.length===t.length)this.boneInverses=t.slice(0);else{console.warn("THREE.Skeleton bonInverses is the wrong length."),this.boneInverses=[];for(var r=0,a=this.bones.length;r=e.HAVE_CURRENT_DATA&&(h.needsUpdate=!0)}n.call(this,e,t,i,r,a,s,o,u,c),this.generateMipmaps=!1;var h=this;l()}function Et(e,t,i,r,a,s,o,u,c,l,h,d){n.call(this,null,s,o,u,c,l,r,a,h,d),this.image={width:t,height:i},this.mipmaps=e,this.flipY=!1,this.generateMipmaps=!1}function Nt(e,t,i,r,a,s,o,u,c){n.call(this,e,t,i,r,a,s,o,u,c),this.needsUpdate=!0}function Lt(e,t,i,r,a,s,o,u,c,l){if((l=l!==undefined?l:Fa)!==Fa&&l!==Ga)throw new Error("DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat");i===undefined&&l===Fa&&(i=Da),i===undefined&&l===Ga&&(i=za),n.call(this,null,r,a,s,o,u,l,i,c),this.image={width:e,height:t},this.magFilter=o!==undefined?o:ma,this.minFilter=u!==undefined?u:ma,this.flipY=!1,this.generateMipmaps=!1}function Dt(e){function t(e,t){return e-t}De.call(this);var i=[0,0],n={},r=["a","b","c"];if(e&&e.isGeometry){for(var a=e.vertices,s=e.faces,o=0,u=new Uint32Array(6*s.length),c=0,l=s.length;c65535?we:ye)(g,1)),this.addAttribute("position",new xe(u,3)),this.addAttribute("uv",new xe(c,2)),this.computeVertexNormals()}function It(e,t,i){Ne.call(this),this.type="ParametricGeometry",this.parameters={func:e,slices:t,stacks:i},this.fromBufferGeometry(new At(e,t,i)),this.mergeVertices()}function bt(e,t,n,r){function a(e,t,i,n){var r,a,s=Math.pow(2,n),u=[];for(r=0;r<=s;r++){u[r]=[];var c=e.clone().lerp(i,r/s),l=t.clone().lerp(i,r/s),h=s-r;for(a=0;a<=h;a++)u[r][a]=0===a&&r===s?c:c.clone().lerp(l,a/h)}for(r=0;r.9&&a<.1&&(t<.2&&(g[e+0]+=1),i<.2&&(g[e+2]+=1),n<.2&&(g[e+4]+=1))}}function o(e){f.push(e.x,e.y,e.z)}function c(t,i){var n=3*t;i.x=e[n+0],i.y=e[n+1],i.z=e[n+2]}function l(){for(var e=new u,t=new u,n=new u,r=new u,a=new i,s=new i,o=new i,c=0,l=0;c65535?we:ye)(v,1)),this.addAttribute("position",new xe(m,3)),this.addAttribute("normal",new xe(M,3)),this.addAttribute("uv",new xe(y,2))}function Bt(e,t,i,n,r,a){Ne.call(this),this.type="TubeGeometry",this.parameters={path:e,tubularSegments:t,radius:i,radialSegments:n,closed:r},a!==undefined&&console.warn("THREE.TubeGeometry: taper has been removed.");var s=new kt(e,t,i,n,r);this.tangents=s.tangents,this.normals=s.normals,this.binormals=s.binormals,this.fromBufferGeometry(s),this.mergeVertices()}function Ft(e,t,n,r,a,s){function o(e,t,i,n,r){var a=Math.cos(e),s=Math.sin(e),o=i/t*e,u=Math.cos(o);r.x=n*(2+u)*.5*a,r.y=n*(2+u)*s*.5,r.z=n*Math.sin(o)*.5}De.call(this),this.type="TorusKnotBufferGeometry",this.parameters={radius:e,tube:t,tubularSegments:n,radialSegments:r,p:a,q:s},e=e||100,t=t||40,n=Math.floor(n)||64,r=Math.floor(r)||8,a=a||2,s=s||3;var c,l,h=(r+1)*(n+1),d=r*n*2*3,p=new pe(new(d>65535?Uint32Array:Uint16Array)(d),1),f=new pe(new Float32Array(3*h),3),g=new pe(new Float32Array(3*h),3),m=new pe(new Float32Array(2*h),2),M=0,y=0,v=new u,w=new u,x=new i,T=new u,E=new u,N=new u,L=new u,D=new u;for(c=0;c<=n;++c){var A=c/n*a*Math.PI*2;for(o(A,a,s,e,T),o(A+.01,a,s,e,E),L.subVectors(E,T),D.addVectors(E,T),N.crossVectors(L,D),D.crossVectors(N,L),N.normalize(),D.normalize(),l=0;l<=r;++l){var I=l/r*Math.PI*2,b=-t*Math.cos(I),S=t*Math.sin(I);v.x=T.x+(b*D.x+S*N.x),v.y=T.y+(b*D.y+S*N.y),v.z=T.z+(b*D.z+S*N.z),f.setXYZ(M,v.x,v.y,v.z),w.subVectors(v,T).normalize(),g.setXYZ(M,w.x,w.y,w.z),x.x=c/n,x.y=l/r,m.setXY(M,x.x,x.y),M++}}for(l=1;l<=n;l++)for(c=1;c<=r;c++){var _=(r+1)*(l-1)+(c-1),j=(r+1)*l+(c-1),C=(r+1)*l+c,z=(r+1)*(l-1)+c;p.setX(y,_),y++,p.setX(y,j),y++,p.setX(y,z),y++,p.setX(y,j),y++,p.setX(y,C),y++,p.setX(y,z),y++}this.setIndex(p),this.addAttribute("position",f),this.addAttribute("normal",g),this.addAttribute("uv",m)}function Gt(e,t,i,n,r,a,s){Ne.call(this),this.type="TorusKnotGeometry",this.parameters={radius:e,tube:t,tubularSegments:i,radialSegments:n,p:r,q:a},s!==undefined&&console.warn("THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead."),this.fromBufferGeometry(new Ft(e,t,i,n,r,a)),this.mergeVertices()}function Qt(e,t,i,n,r){De.call(this),this.type="TorusBufferGeometry",this.parameters={radius:e,tube:t,radialSegments:i,tubularSegments:n,arc:r},e=e||100,t=t||40,i=Math.floor(i)||8,n=Math.floor(n)||6,r=r||2*Math.PI;var a,s,o=(i+1)*(n+1),c=i*n*2*3,l=new(c>65535?Uint32Array:Uint16Array)(c),h=new Float32Array(3*o),d=new Float32Array(3*o),p=new Float32Array(2*o),f=0,g=0,m=0,M=new u,y=new u,v=new u;for(a=0;a<=i;a++)for(s=0;s<=n;s++){var w=s/n*r,x=a/i*Math.PI*2;y.x=(e+t*Math.cos(x))*Math.cos(w),y.y=(e+t*Math.cos(x))*Math.sin(w),y.z=t*Math.sin(x),h[f]=y.x,h[f+1]=y.y,h[f+2]=y.z,M.x=e*Math.cos(w),M.y=e*Math.sin(w),v.subVectors(y,M).normalize(),d[f]=v.x,d[f+1]=v.y,d[f+2]=v.z,p[g]=s/n,p[g+1]=a/i,f+=3,g+=2}for(a=1;a<=i;a++)for(s=1;s<=n;s++){var T=(n+1)*a+s-1,E=(n+1)*(a-1)+s-1,N=(n+1)*(a-1)+s,L=(n+1)*a+s;l[m]=T,l[m+1]=E,l[m+2]=L,l[m+3]=E,l[m+4]=N,l[m+5]=L,m+=6}this.setIndex(new pe(l,1)),this.addAttribute("position",new pe(h,3)),this.addAttribute("normal",new pe(d,3)),this.addAttribute("uv",new pe(p,2))}function Yt(e,t,i,n,r){Ne.call(this),this.type="TorusGeometry",this.parameters={radius:e,tube:t,radialSegments:i,tubularSegments:n,arc:r},this.fromBufferGeometry(new Qt(e,t,i,n,r))}function Vt(e,t){void 0!==e?(Ne.call(this),this.type="ExtrudeGeometry",e=Array.isArray(e)?e:[e],this.addShapeList(e,t),this.computeFaceNormals()):e=[]}function Ht(e,t){var i=(t=t||{}).font;if(!1===(i&&i.isFont))return console.error("THREE.TextGeometry: font parameter is not an instance of THREE.Font."),new Ne;var n=i.generateShapes(e,t.size,t.curveSegments);t.amount=t.height!==undefined?t.height:50,t.bevelThickness===undefined&&(t.bevelThickness=10),t.bevelSize===undefined&&(t.bevelSize=8),t.bevelEnabled===undefined&&(t.bevelEnabled=!1),Vt.call(this,n,t),this.type="TextGeometry"}function Wt(e,t,i,n,r,a,s){De.call(this),this.type="SphereBufferGeometry",this.parameters={radius:e,widthSegments:t,heightSegments:i,phiStart:n,phiLength:r,thetaStart:a,thetaLength:s},e=e||50,t=Math.max(3,Math.floor(t)||8),i=Math.max(2,Math.floor(i)||6),n=n!==undefined?n:0,r=r!==undefined?r:2*Math.PI;for(var o=(a=a!==undefined?a:0)+(s=s!==undefined?s:Math.PI),c=(t+1)*(i+1),l=new pe(new Float32Array(3*c),3),h=new pe(new Float32Array(3*c),3),d=new pe(new Float32Array(2*c),2),p=0,f=[],g=new u,m=0;m<=i;m++){for(var M=[],y=m/i,v=0;v<=t;v++){var w=v/t,x=-e*Math.cos(n+w*r)*Math.sin(a+y*s),T=e*Math.cos(a+y*s),E=e*Math.sin(n+w*r)*Math.sin(a+y*s);g.set(x,T,E).normalize(),l.setXYZ(p,x,T,E),h.setXYZ(p,g.x,g.y,g.z),d.setXY(p,w,1-y),M.push(p),p++}f.push(M)}for(var N=[],m=0;m0)&&N.push(L,D,I),(m!==i-1||o65535?we:ye)(N,1)),this.addAttribute("position",l),this.addAttribute("normal",h),this.addAttribute("uv",d),this.boundingSphere=new ee(new u,e)}function Xt(e,t,i,n,r,a,s){Ne.call(this),this.type="SphereGeometry",this.parameters={radius:e,widthSegments:t,heightSegments:i,phiStart:n,phiLength:r,thetaStart:a,thetaLength:s},this.fromBufferGeometry(new Wt(e,t,i,n,r,a,s))}function Zt(e,t,n,r,a,s){De.call(this),this.type="RingBufferGeometry",this.parameters={innerRadius:e,outerRadius:t,thetaSegments:n,phiSegments:r,thetaStart:a,thetaLength:s},e=e||20,t=t||50,a=a!==undefined?a:0,s=s!==undefined?s:2*Math.PI;var o,c,l,h=((n=n!==undefined?Math.max(3,n):8)+1)*((r=r!==undefined?Math.max(1,r):1)+1),d=n*r*2*3,p=new pe(new(d>65535?Uint32Array:Uint16Array)(d),1),f=new pe(new Float32Array(3*h),3),g=new pe(new Float32Array(3*h),3),m=new pe(new Float32Array(2*h),2),M=0,y=0,v=e,w=(t-e)/r,x=new u,T=new i;for(c=0;c<=r;c++){for(l=0;l<=n;l++)o=a+l/n*s,x.x=v*Math.cos(o),x.y=v*Math.sin(o),f.setXYZ(M,x.x,x.y,x.z),g.setXYZ(M,0,0,1),T.x=(x.x/t+1)/2,T.y=(x.y/t+1)/2,m.setXY(M,T.x,T.y),M++;v+=w}for(c=0;c65535?Uint32Array:Uint16Array)(l),1),d=new pe(new Float32Array(3*c),3),p=new pe(new Float32Array(2*c),2),f=0,g=0,m=1/t,M=new u,y=new i;for(s=0;s<=t;s++){var v=n+s*m*r,w=Math.sin(v),x=Math.cos(v);for(o=0;o<=e.length-1;o++)M.x=e[o].x*w,M.y=e[o].y,M.z=e[o].x*x,d.setXYZ(f,M.x,M.y,M.z),y.x=s/t,y.y=o/(e.length-1),p.setXY(f,y.x,y.y),f++}for(s=0;s65535?we:ye)(s,1)),this.addAttribute("position",new xe(n,3)),this.addAttribute("normal",new xe(r,3)),this.addAttribute("uv",new xe(a,2))}function ti(e,t){Ne.call(this),this.type="ShapeGeometry","object"==typeof t&&(console.warn("THREE.ShapeGeometry: Options parameter has been removed."),t=t.curveSegments),this.parameters={shapes:e,curveSegments:t},this.fromBufferGeometry(new ei(e,t)),this.mergeVertices()}function ii(e,t){De.call(this),t=t!==undefined?t:1;var i,n=Math.cos(ds.DEG2RAD*t),r=[0,0],a={},s=["a","b","c"];e.isBufferGeometry?(i=new Ne).fromBufferGeometry(e):i=e.clone(),i.mergeVertices(),i.computeFaceNormals();for(var o=i.vertices,u=i.faces,c=0,l=u.length;c0&&d++,t>0&&d++);var p=function(){var e=(r+1)*(a+1);return!1===s&&(e+=(r+1)*d+r*d),e}(),f=function(){var e=r*a*2*3;return!1===s&&(e+=r*d*3),e}(),g=new pe(new(f>65535?Uint32Array:Uint16Array)(f),1),m=new pe(new Float32Array(3*p),3),M=new pe(new Float32Array(3*p),3),y=new pe(new Float32Array(2*p),2),v=0,w=0,x=[],T=n/2,E=0;!function(){var i,s,l=new u,d=new u,p=0,f=(t-e)/n;for(s=0;s<=a;s++){var N=[],L=s/a,D=L*(t-e)+e;for(i=0;i<=r;i++){var A=i/r,I=A*c+o,b=Math.sin(I),S=Math.cos(I);d.x=D*b,d.y=-L*n+T,d.z=D*S,m.setXYZ(v,d.x,d.y,d.z),l.set(b,f,S).normalize(),M.setXYZ(v,l.x,l.y,l.z),y.setXY(v,A,1-L),N.push(v),v++}x.push(N)}for(i=0;i0&&l(!0),t>0&&l(!1)),this.setIndex(g),this.addAttribute("position",m),this.addAttribute("normal",M),this.addAttribute("uv",y)}function ri(e,t,i,n,r,a,s,o){Ne.call(this),this.type="CylinderGeometry",this.parameters={radiusTop:e,radiusBottom:t,height:i,radialSegments:n,heightSegments:r,openEnded:a,thetaStart:s,thetaLength:o},this.fromBufferGeometry(new ni(e,t,i,n,r,a,s,o)),this.mergeVertices()}function ai(e,t,i,n,r,a,s){ri.call(this,0,e,t,i,n,r,a,s),this.type="ConeGeometry",this.parameters={radius:e,height:t,radialSegments:i,heightSegments:n,openEnded:r,thetaStart:a,thetaLength:s}}function si(e,t,i,n,r,a,s){ni.call(this,0,e,t,i,n,r,a,s),this.type="ConeBufferGeometry",this.parameters={radius:e,height:t,radialSegments:i,heightSegments:n,openEnded:r,thetaStart:a,thetaLength:s}}function oi(e,t,i,n){De.call(this),this.type="CircleBufferGeometry",this.parameters={radius:e,segments:t,thetaStart:i,thetaLength:n},e=e||50,t=t!==undefined?Math.max(3,t):8,i=i!==undefined?i:0,n=n!==undefined?n:2*Math.PI;var r=t+2,a=new Float32Array(3*r),s=new Float32Array(3*r),o=new Float32Array(2*r);s[2]=1,o[0]=.5,o[1]=.5;for(var c=0,l=3,h=2;c<=t;c++,l+=3,h+=2){var d=i+c/t*n;a[l]=e*Math.cos(d),a[l+1]=e*Math.sin(d),s[l+2]=1,o[h]=(a[l]/e+1)/2,o[h+1]=(a[l+1]/e+1)/2}for(var p=[],l=1;l<=t;l++)p.push(l,l+1,0);this.setIndex(new pe(new Uint16Array(p),1)),this.addAttribute("position",new pe(a,3)),this.addAttribute("normal",new pe(s,3)),this.addAttribute("uv",new pe(o,2)),this.boundingSphere=new ee(new u,e)}function ui(e,t,i,n){Ne.call(this),this.type="CircleGeometry",this.parameters={radius:e,segments:t,thetaStart:i,thetaLength:n},this.fromBufferGeometry(new oi(e,t,i,n))}function ci(e,t,i,n,r,a){Ne.call(this),this.type="BoxGeometry",this.parameters={width:e,height:t,depth:i,widthSegments:n,heightSegments:r,depthSegments:a},this.fromBufferGeometry(new Ie(e,t,i,n,r,a)),this.mergeVertices()}function li(){J.call(this,{uniforms:vs.merge([Ts.lights,{opacity:{value:1}}]),vertexShader:ws.shadow_vert,fragmentShader:ws.shadow_frag}),this.lights=!0,this.transparent=!0,Object.defineProperties(this,{opacity:{enumerable:!0,get:function(){return this.uniforms.opacity.value},set:function(e){this.uniforms.opacity.value=e}}})}function hi(e){J.call(this,e),this.type="RawShaderMaterial"}function di(e){this.uuid=ds.generateUUID(),this.type="MultiMaterial",this.materials=Array.isArray(e)?e:[],this.visible=!0}function pi(e){q.call(this),this.defines={STANDARD:""},this.type="MeshStandardMaterial",this.color=new V(16777215),this.roughness=.5,this.metalness=.5,this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissive=new V(0),this.emissiveIntensity=1,this.emissiveMap=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalScale=new i(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.roughnessMap=null,this.metalnessMap=null,this.alphaMap=null,this.envMap=null,this.envMapIntensity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.skinning=!1,this.morphTargets=!1,this.morphNormals=!1,this.setValues(e)}function fi(e){pi.call(this),this.defines={PHYSICAL:""},this.type="MeshPhysicalMaterial",this.reflectivity=.5,this.clearCoat=0,this.clearCoatRoughness=0,this.setValues(e)}function gi(e){q.call(this),this.type="MeshPhongMaterial",this.color=new V(16777215),this.specular=new V(1118481),this.shininess=30,this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissive=new V(0),this.emissiveIntensity=1,this.emissiveMap=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalScale=new i(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.combine=qr,this.reflectivity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.skinning=!1,this.morphTargets=!1,this.morphNormals=!1,this.setValues(e)}function mi(e){gi.call(this),this.defines={TOON:""},this.type="MeshToonMaterial",this.gradientMap=null,this.setValues(e)}function Mi(e){q.call(this,e),this.type="MeshNormalMaterial",this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalScale=new i(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.wireframe=!1,this.wireframeLinewidth=1,this.fog=!1,this.lights=!1,this.skinning=!1,this.morphTargets=!1,this.morphNormals=!1,this.setValues(e)}function yi(e){q.call(this),this.type="MeshLambertMaterial",this.color=new V(16777215),this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissive=new V(0),this.emissiveIntensity=1,this.emissiveMap=null,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.combine=qr,this.reflectivity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.skinning=!1,this.morphTargets=!1,this.morphNormals=!1,this.setValues(e)}function vi(e){q.call(this),this.type="LineDashedMaterial",this.color=new V(16777215),this.linewidth=1,this.scale=1,this.dashSize=3,this.gapSize=1,this.lights=!1,this.setValues(e)}function wi(e,t,i){var n=this,r=!1,a=0,s=0;this.onStart=undefined,this.onLoad=e,this.onProgress=t,this.onError=i,this.itemStart=function(e){s++,!1===r&&n.onStart!==undefined&&n.onStart(e,a,s),r=!0},this.itemEnd=function(e){a++,n.onProgress!==undefined&&n.onProgress(e,a,s),a===s&&(r=!1,n.onLoad!==undefined&&n.onLoad())},this.itemError=function(e){n.onError!==undefined&&n.onError(e)}}function xi(e){this.manager=e!==undefined?e:js}function Ti(e){this.manager=e!==undefined?e:js,this._parser=null}function Ei(e){this.manager=e!==undefined?e:js,this._parser=null}function Ni(e){this.manager=e!==undefined?e:js}function Li(e){this.manager=e!==undefined?e:js}function Di(e){this.manager=e!==undefined?e:js}function Ai(e,t){ue.call(this),this.type="Light",this.color=new V(e),this.intensity=t!==undefined?t:1,this.receiveShadow=undefined}function Ii(e,t,i){Ai.call(this,e,i),this.type="HemisphereLight",this.castShadow=undefined,this.position.copy(ue.DefaultUp),this.updateMatrix(),this.groundColor=new V(t)}function bi(e){this.camera=e,this.bias=0,this.radius=1,this.mapSize=new i(512,512),this.map=null,this.matrix=new c}function Si(){bi.call(this,new _e(50,1,.5,500))}function _i(e,t,i,n,r,a){Ai.call(this,e,t),this.type="SpotLight",this.position.copy(ue.DefaultUp),this.updateMatrix(),this.target=new ue,Object.defineProperty(this,"power",{get:function(){return this.intensity*Math.PI},set:function(e){this.intensity=e/Math.PI}}),this.distance=i!==undefined?i:0,this.angle=n!==undefined?n:Math.PI/3,this.penumbra=r!==undefined?r:0,this.decay=a!==undefined?a:1,this.shadow=new Si}function ji(e,t,i,n){Ai.call(this,e,t),this.type="PointLight",Object.defineProperty(this,"power",{get:function(){return 4*this.intensity*Math.PI},set:function(e){this.intensity=e/(4*Math.PI)}}),this.distance=i!==undefined?i:0,this.decay=n!==undefined?n:1,this.shadow=new bi(new _e(90,1,.5,500))}function Ci(e){bi.call(this,new je(-5,5,5,-5,.5,500))}function zi(e,t){Ai.call(this,e,t),this.type="DirectionalLight",this.position.copy(ue.DefaultUp),this.updateMatrix(),this.target=new ue,this.shadow=new Ci}function Oi(e,t){Ai.call(this,e,t),this.type="AmbientLight",this.castShadow=undefined}function Ri(e,t,i,n){this.parameterPositions=e,this._cachedIndex=0,this.resultBuffer=n!==undefined?n:new t.constructor(i),this.sampleValues=t,this.valueSize=i}function Ui(e,t,i,n){Ri.call(this,e,t,i,n),this._weightPrev=-0,this._offsetPrev=-0,this._weightNext=-0,this._offsetNext=-0}function Pi(e,t,i,n){Ri.call(this,e,t,i,n)}function ki(e,t,i,n){Ri.call(this,e,t,i,n)}function Bi(e,t,i,n){if(e===undefined)throw new Error("track name is undefined");if(t===undefined||0===t.length)throw new Error("no keyframes in track named "+e);this.name=e,this.times=zs.convertArray(t,this.TimeBufferType),this.values=zs.convertArray(i,this.ValueBufferType),this.setInterpolation(n||this.DefaultInterpolation),this.validate(),this.optimize()}function Fi(e,t,i,n){Bi.call(this,e,t,i,n)}function Gi(e,t,i,n){Ri.call(this,e,t,i,n)}function Qi(e,t,i,n){Bi.call(this,e,t,i,n)}function Yi(e,t,i,n){Bi.call(this,e,t,i,n)}function Vi(e,t,i,n){Bi.call(this,e,t,i,n)}function Hi(e,t,i){Bi.call(this,e,t,i)}function Wi(e,t,i,n){Bi.call(this,e,t,i,n)}function Xi(e,t,i,n){Bi.apply(this,arguments)}function Zi(e,t,i){this.name=e,this.tracks=i,this.duration=t!==undefined?t:-1,this.uuid=ds.generateUUID(),this.duration<0&&this.resetDuration(),this.optimize()}function qi(e){this.manager=e!==undefined?e:js,this.textures={}}function Ji(e){this.manager=e!==undefined?e:js}function Ki(){this.onLoadStart=function(){},this.onLoadProgress=function(){},this.onLoadComplete=function(){}}function $i(e){"boolean"==typeof e&&(console.warn("THREE.JSONLoader: showStatus parameter has been removed from constructor."),e=undefined),this.manager=e!==undefined?e:js,this.withCredentials=!1}function en(e){this.manager=e!==undefined?e:js,this.texturePath=""}function tn(){}function nn(e,t){this.v1=e,this.v2=t}function rn(){this.curves=[],this.autoClose=!1}function an(e,t,i,n,r,a,s,o){this.aX=e,this.aY=t,this.xRadius=i,this.yRadius=n,this.aStartAngle=r,this.aEndAngle=a,this.aClockwise=s,this.aRotation=o||0}function sn(e){this.points=e===undefined?[]:e}function on(e,t,i,n){this.v0=e,this.v1=t,this.v2=i,this.v3=n}function un(e,t,i){this.v0=e,this.v1=t,this.v2=i}function cn(){ln.apply(this,arguments),this.holes=[]}function ln(e){rn.call(this),this.currentPoint=new i,e&&this.fromPoints(e)}function hn(){this.subPaths=[],this.currentPath=null}function dn(e){this.data=e}function pn(e){this.manager=e!==undefined?e:js}function fn(e){this.manager=e!==undefined?e:js}function gn(e,t,i,n){Ai.call(this,e,t),this.type="RectAreaLight",this.position.set(0,1,0),this.updateMatrix(),this.width=i!==undefined?i:10,this.height=n!==undefined?n:10}function mn(){this.type="StereoCamera",this.aspect=1,this.eyeSep=.064,this.cameraL=new _e,this.cameraL.layers.enable(1),this.cameraL.matrixAutoUpdate=!1,this.cameraR=new _e,this.cameraR.layers.enable(2),this.cameraR.matrixAutoUpdate=!1}function Mn(e,t,i){ue.call(this),this.type="CubeCamera";var n=new _e(90,1,e,t);n.up.set(0,-1,0),n.lookAt(new u(1,0,0)),this.add(n);var r=new _e(90,1,e,t);r.up.set(0,-1,0),r.lookAt(new u(-1,0,0)),this.add(r);var a=new _e(90,1,e,t);a.up.set(0,0,1),a.lookAt(new u(0,1,0)),this.add(a);var o=new _e(90,1,e,t);o.up.set(0,0,-1),o.lookAt(new u(0,-1,0)),this.add(o);var c=new _e(90,1,e,t);c.up.set(0,-1,0),c.lookAt(new u(0,0,1)),this.add(c);var l=new _e(90,1,e,t);l.up.set(0,-1,0),l.lookAt(new u(0,0,-1)),this.add(l);var h={format:Ra,magFilter:va,minFilter:va};this.renderTarget=new s(i,i,h),this.updateCubeMap=function(e,t){null===this.parent&&this.updateMatrixWorld();var i=this.renderTarget,s=i.texture.generateMipmaps;i.texture.generateMipmaps=!1,i.activeCubeFace=0,e.render(t,n,i),i.activeCubeFace=1,e.render(t,r,i),i.activeCubeFace=2,e.render(t,a,i),i.activeCubeFace=3,e.render(t,o,i),i.activeCubeFace=4,e.render(t,c,i),i.texture.generateMipmaps=s,i.activeCubeFace=5,e.render(t,l,i),e.setRenderTarget(null)}}function yn(){ue.call(this),this.type="AudioListener",this.context=ks.getContext(),this.gain=this.context.createGain(),this.gain.connect(this.context.destination),this.filter=null}function vn(e){ue.call(this),this.type="Audio",this.context=e.context,this.gain=this.context.createGain(),this.gain.connect(e.getInput()),this.autoplay=!1,this.buffer=null,this.loop=!1,this.startTime=0,this.playbackRate=1,this.isPlaying=!1,this.hasPlaybackControl=!0,this.sourceType="empty",this.filters=[]}function wn(e){vn.call(this,e),this.panner=this.context.createPanner(),this.panner.connect(this.gain)}function xn(e,t){this.analyser=e.context.createAnalyser(),this.analyser.fftSize=t!==undefined?t:2048,this.data=new Uint8Array(this.analyser.frequencyBinCount),e.getOutput().connect(this.analyser)}function Tn(e,t,i){this.binding=e,this.valueSize=i;var n,r=Float64Array;switch(t){case"quaternion":n=this._slerp;break;case"string":case"bool":r=Array,n=this._select;break;default:n=this._lerp}this.buffer=new r(4*i),this._mixBufferRegion=n,this.cumulativeWeight=0,this.useCount=0,this.referenceCount=0}function En(e,t,i){this.path=t,this.parsedPath=i||En.parseTrackName(t),this.node=En.findNode(e,this.parsedPath.nodeName)||e,this.rootNode=e}function Nn(e){this.uuid=ds.generateUUID(),this._objects=Array.prototype.slice.call(arguments),this.nCachedObjects_=0;var t={};this._indicesByUUID=t;for(var i=0,n=arguments.length;i!==n;++i)t[arguments[i].uuid]=i;this._paths=[],this._parsedPaths=[],this._bindings=[],this._bindingsIndicesByPath={};var r=this;this.stats={objects:{get total(){return r._objects.length},get inUse(){return this.total-r.nCachedObjects_}},get bindingsPerObject(){return r._bindings.length}}}function Ln(e,t,i){this._mixer=e,this._clip=t,this._localRoot=i||null;for(var n=t.tracks,r=n.length,a=new Array(r),s={endingStart:$a,endingEnd:$a},o=0;o!==r;++o){var u=n[o].createInterpolant(null);a[o]=u,u.settings=s}this._interpolantSettings=s,this._interpolants=a,this._propertyBindings=new Array(r),this._cacheIndex=null,this._byClipCacheIndex=null,this._timeScaleInterpolant=null,this._weightInterpolant=null,this.loop=Ka,this._loopCount=-1,this._startTime=null,this.time=0,this.timeScale=1,this._effectiveTimeScale=1,this.weight=1,this._effectiveWeight=1,this.repetitions=Infinity,this.paused=!1,this.enabled=!0,this.clampWhenFinished=!1,this.zeroSlopeAtStart=!0,this.zeroSlopeAtEnd=!0}function Dn(e){this._root=e,this._initMemoryManager(),this._accuIndex=0,this.time=0,this.timeScale=1}function An(e){"string"==typeof e&&(console.warn("THREE.Uniform: Type parameter is no longer needed."),e=arguments[1]),this.value=e}function In(){De.call(this),this.type="InstancedBufferGeometry",this.maxInstancedCount=undefined}function bn(e,t,i,n){this.uuid=ds.generateUUID(),this.data=e,this.itemSize=t,this.offset=i,this.normalized=!0===n}function Sn(e,t){this.uuid=ds.generateUUID(),this.array=e,this.stride=t,this.count=e!==undefined?e.length/t:0,this.dynamic=!1,this.updateRange={offset:0,count:-1},this.onUploadCallback=function(){},this.version=0}function _n(e,t,i){Sn.call(this,e,t),this.meshPerAttribute=i||1}function jn(e,t,i){pe.call(this,e,t),this.meshPerAttribute=i||1}function Cn(e,t,i,n){this.ray=new ae(e,t),this.near=i||0,this.far=n||Infinity,this.params={Mesh:{},Line:{},LOD:{},Points:{threshold:1},Sprite:{}},Object.defineProperties(this.params,{PointCloud:{get:function(){return console.warn("THREE.Raycaster: params.PointCloud has been renamed to params.Points."),this.Points}}})}function zn(e,t){return e.distance-t.distance}function On(e,t,i,n){if(!1!==e.visible&&(e.raycast(t,i),!0===n))for(var r=e.children,a=0,s=r.length;a0?1:+e}),Function.prototype.name===undefined&&Object.defineProperty(Function.prototype,"name",{get:function(){return this.toString().match(/^\s*function\s*([^\(\s]*)/)[1]}}),Object.assign===undefined&&(Object.assign=function(e){if(e===undefined||null===e)throw new TypeError("Cannot convert undefined or null to object");for(var t=Object(e),i=1;i>=4,i[r]=t[19===r?3&e|8:e]);return i.join("")}}(),clamp:function(e,t,i){return Math.max(t,Math.min(i,e))},euclideanModulo:function(e,t){return(e%t+t)%t},mapLinear:function(e,t,i,n,r){return n+(e-t)*(r-n)/(i-t)},lerp:function(e,t,i){return(1-i)*e+i*t},smoothstep:function(e,t,i){return e<=t?0:e>=i?1:(e=(e-t)/(i-t))*e*(3-2*e)},smootherstep:function(e,t,i){return e<=t?0:e>=i?1:(e=(e-t)/(i-t))*e*e*(e*(6*e-15)+10)},randInt:function(e,t){return e+Math.floor(Math.random()*(t-e+1))},randFloat:function(e,t){return e+Math.random()*(t-e)},randFloatSpread:function(e){return e*(.5-Math.random())},degToRad:function(e){return e*ds.DEG2RAD},radToDeg:function(e){return e*ds.RAD2DEG},isPowerOfTwo:function(e){return 0==(e&e-1)&&0!==e},nearestPowerOfTwo:function(e){return Math.pow(2,Math.round(Math.log(e)/Math.LN2))},nextPowerOfTwo:function(e){return e--,e|=e>>1,e|=e>>2,e|=e>>4,e|=e>>8,e|=e>>16,++e}};i.prototype={constructor:i,isVector2:!0,get width(){return this.x},set width(e){this.x=e},get height(){return this.y},set height(e){this.y=e},set:function(e,t){return this.x=e,this.y=t,this},setScalar:function(e){return this.x=e,this.y=e,this},setX:function(e){return this.x=e,this},setY:function(e){return this.y=e,this},setComponent:function(e,t){switch(e){case 0:this.x=t;break;case 1:this.y=t;break;default:throw new Error("index is out of range: "+e)}return this},getComponent:function(e){switch(e){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+e)}},clone:function(){return new this.constructor(this.x,this.y)},copy:function(e){return this.x=e.x,this.y=e.y,this},add:function(e,t){return t!==undefined?(console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(e,t)):(this.x+=e.x,this.y+=e.y,this)},addScalar:function(e){return this.x+=e,this.y+=e,this},addVectors:function(e,t){return this.x=e.x+t.x,this.y=e.y+t.y,this},addScaledVector:function(e,t){return this.x+=e.x*t,this.y+=e.y*t,this},sub:function(e,t){return t!==undefined?(console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(e,t)):(this.x-=e.x,this.y-=e.y,this)},subScalar:function(e){return this.x-=e,this.y-=e,this},subVectors:function(e,t){return this.x=e.x-t.x,this.y=e.y-t.y,this},multiply:function(e){return this.x*=e.x,this.y*=e.y,this},multiplyScalar:function(e){return isFinite(e)?(this.x*=e,this.y*=e):(this.x=0,this.y=0),this},divide:function(e){return this.x/=e.x,this.y/=e.y,this},divideScalar:function(e){return this.multiplyScalar(1/e)},min:function(e){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this},max:function(e){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this},clamp:function(e,t){return this.x=Math.max(e.x,Math.min(t.x,this.x)),this.y=Math.max(e.y,Math.min(t.y,this.y)),this},clampScalar:function(){var e,t;return function(n,r){return e===undefined&&(e=new i,t=new i),e.set(n,n),t.set(r,r),this.clamp(e,t)}}(),clampLength:function(e,t){var i=this.length();return this.multiplyScalar(Math.max(e,Math.min(t,i))/i)},floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this},negate:function(){return this.x=-this.x,this.y=-this.y,this},dot:function(e){return this.x*e.x+this.y*e.y},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)},normalize:function(){return this.divideScalar(this.length())},angle:function(){var e=Math.atan2(this.y,this.x);return e<0&&(e+=2*Math.PI),e},distanceTo:function(e){return Math.sqrt(this.distanceToSquared(e))},distanceToSquared:function(e){var t=this.x-e.x,i=this.y-e.y;return t*t+i*i},distanceToManhattan:function(e){return Math.abs(this.x-e.x)+Math.abs(this.y-e.y)},setLength:function(e){return this.multiplyScalar(e/this.length())},lerp:function(e,t){return this.x+=(e.x-this.x)*t,this.y+=(e.y-this.y)*t,this},lerpVectors:function(e,t,i){return this.subVectors(t,e).multiplyScalar(i).add(e)},equals:function(e){return e.x===this.x&&e.y===this.y},fromArray:function(e,t){return t===undefined&&(t=0),this.x=e[t],this.y=e[t+1],this},toArray:function(e,t){return e===undefined&&(e=[]),t===undefined&&(t=0),e[t]=this.x,e[t+1]=this.y,e},fromAttribute:function(e,t,i){return i!==undefined&&console.warn("THREE.Vector2: offset has been removed from .fromAttribute()."),this.x=e.getX(t),this.y=e.getY(t),this},rotateAround:function(e,t){var i=Math.cos(t),n=Math.sin(t),r=this.x-e.x,a=this.y-e.y;return this.x=r*i-a*n+e.x,this.y=r*n+a*i+e.y,this}};var ps=0;n.DEFAULT_IMAGE=undefined,n.DEFAULT_MAPPING=300,n.prototype={constructor:n,isTexture:!0,set needsUpdate(e){!0===e&&this.version++},clone:function(){return(new this.constructor).copy(this)},copy:function(e){return this.image=e.image,this.mipmaps=e.mipmaps.slice(0),this.mapping=e.mapping,this.wrapS=e.wrapS,this.wrapT=e.wrapT,this.magFilter=e.magFilter,this.minFilter=e.minFilter,this.anisotropy=e.anisotropy,this.format=e.format,this.type=e.type,this.offset.copy(e.offset),this.repeat.copy(e.repeat),this.generateMipmaps=e.generateMipmaps,this.premultiplyAlpha=e.premultiplyAlpha,this.flipY=e.flipY,this.unpackAlignment=e.unpackAlignment,this.encoding=e.encoding,this},toJSON:function(e){if(e.textures[this.uuid]!==undefined)return e.textures[this.uuid];var t={metadata:{version:4.4,type:"Texture",generator:"Texture.toJSON"},uuid:this.uuid,name:this.name,mapping:this.mapping,repeat:[this.repeat.x,this.repeat.y],offset:[this.offset.x,this.offset.y],wrap:[this.wrapS,this.wrapT],minFilter:this.minFilter,magFilter:this.magFilter,anisotropy:this.anisotropy,flipY:this.flipY};if(this.image!==undefined){var i=this.image;i.uuid===undefined&&(i.uuid=ds.generateUUID()),e.images[i.uuid]===undefined&&(e.images[i.uuid]={uuid:i.uuid,url:function(e){var t;return e.toDataURL!==undefined?t=e:((t=document.createElementNS("http://www.w3.org/1999/xhtml","canvas")).width=e.width,t.height=e.height,t.getContext("2d").drawImage(e,0,0,e.width,e.height)),t.width>2048||t.height>2048?t.toDataURL("image/jpeg",.6):t.toDataURL("image/png")}(i)}),t.image=i.uuid}return e.textures[this.uuid]=t,t},dispose:function(){this.dispatchEvent({type:"dispose"})},transformUv:function(e){if(300===this.mapping){if(e.multiply(this.repeat),e.add(this.offset),e.x<0||e.x>1)switch(this.wrapS){case da:e.x=e.x-Math.floor(e.x);break;case pa:e.x=e.x<0?0:1;break;case fa:1===Math.abs(Math.floor(e.x)%2)?e.x=Math.ceil(e.x)-e.x:e.x=e.x-Math.floor(e.x)}if(e.y<0||e.y>1)switch(this.wrapT){case da:e.y=e.y-Math.floor(e.y);break;case pa:e.y=e.y<0?0:1;break;case fa:1===Math.abs(Math.floor(e.y)%2)?e.y=Math.ceil(e.y)-e.y:e.y=e.y-Math.floor(e.y)}this.flipY&&(e.y=1-e.y)}}},Object.assign(n.prototype,t.prototype),r.prototype={constructor:r,isVector4:!0,set:function(e,t,i,n){return this.x=e,this.y=t,this.z=i,this.w=n,this},setScalar:function(e){return this.x=e,this.y=e,this.z=e,this.w=e,this},setX:function(e){return this.x=e,this},setY:function(e){return this.y=e,this},setZ:function(e){return this.z=e,this},setW:function(e){return this.w=e,this},setComponent:function(e,t){switch(e){case 0:this.x=t;break;case 1:this.y=t;break;case 2:this.z=t;break;case 3:this.w=t;break;default:throw new Error("index is out of range: "+e)}return this},getComponent:function(e){switch(e){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("index is out of range: "+e)}},clone:function(){return new this.constructor(this.x,this.y,this.z,this.w)},copy:function(e){return this.x=e.x,this.y=e.y,this.z=e.z,this.w=e.w!==undefined?e.w:1,this},add:function(e,t){return t!==undefined?(console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(e,t)):(this.x+=e.x,this.y+=e.y,this.z+=e.z,this.w+=e.w,this)},addScalar:function(e){return this.x+=e,this.y+=e,this.z+=e,this.w+=e,this},addVectors:function(e,t){return this.x=e.x+t.x,this.y=e.y+t.y,this.z=e.z+t.z,this.w=e.w+t.w,this},addScaledVector:function(e,t){return this.x+=e.x*t,this.y+=e.y*t,this.z+=e.z*t,this.w+=e.w*t,this},sub:function(e,t){return t!==undefined?(console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(e,t)):(this.x-=e.x,this.y-=e.y,this.z-=e.z,this.w-=e.w,this)},subScalar:function(e){return this.x-=e,this.y-=e,this.z-=e,this.w-=e,this},subVectors:function(e,t){return this.x=e.x-t.x,this.y=e.y-t.y,this.z=e.z-t.z,this.w=e.w-t.w,this},multiplyScalar:function(e){return isFinite(e)?(this.x*=e,this.y*=e,this.z*=e,this.w*=e):(this.x=0,this.y=0,this.z=0,this.w=0),this},applyMatrix4:function(e){var t=this.x,i=this.y,n=this.z,r=this.w,a=e.elements;return this.x=a[0]*t+a[4]*i+a[8]*n+a[12]*r,this.y=a[1]*t+a[5]*i+a[9]*n+a[13]*r,this.z=a[2]*t+a[6]*i+a[10]*n+a[14]*r,this.w=a[3]*t+a[7]*i+a[11]*n+a[15]*r,this},divideScalar:function(e){return this.multiplyScalar(1/e)},setAxisAngleFromQuaternion:function(e){this.w=2*Math.acos(e.w);var t=Math.sqrt(1-e.w*e.w);return t<1e-4?(this.x=1,this.y=0,this.z=0):(this.x=e.x/t,this.y=e.y/t,this.z=e.z/t),this},setAxisAngleFromRotationMatrix:function(e){var t,i,n,r,a=e.elements,s=a[0],o=a[4],u=a[8],c=a[1],l=a[5],h=a[9],d=a[2],p=a[6],f=a[10];if(Math.abs(o-c)<.01&&Math.abs(u-d)<.01&&Math.abs(h-p)<.01){if(Math.abs(o+c)<.1&&Math.abs(u+d)<.1&&Math.abs(h+p)<.1&&Math.abs(s+l+f-3)<.1)return this.set(1,0,0,0),this;t=Math.PI;var g=(s+1)/2,m=(l+1)/2,M=(f+1)/2,y=(o+c)/4,v=(u+d)/4,w=(h+p)/4;return g>m&&g>M?g<.01?(i=0,n=.707106781,r=.707106781):(n=y/(i=Math.sqrt(g)),r=v/i):m>M?m<.01?(i=.707106781,n=0,r=.707106781):(i=y/(n=Math.sqrt(m)),r=w/n):M<.01?(i=.707106781,n=.707106781,r=0):(i=v/(r=Math.sqrt(M)),n=w/r),this.set(i,n,r,t),this}var x=Math.sqrt((p-h)*(p-h)+(u-d)*(u-d)+(c-o)*(c-o));return Math.abs(x)<.001&&(x=1),this.x=(p-h)/x,this.y=(u-d)/x,this.z=(c-o)/x,this.w=Math.acos((s+l+f-1)/2),this},min:function(e){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this.z=Math.min(this.z,e.z),this.w=Math.min(this.w,e.w),this},max:function(e){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this.z=Math.max(this.z,e.z),this.w=Math.max(this.w,e.w),this},clamp:function(e,t){return this.x=Math.max(e.x,Math.min(t.x,this.x)),this.y=Math.max(e.y,Math.min(t.y,this.y)),this.z=Math.max(e.z,Math.min(t.z,this.z)),this.w=Math.max(e.w,Math.min(t.w,this.w)),this},clampScalar:function(){var e,t;return function(i,n){return e===undefined&&(e=new r,t=new r),e.set(i,i,i,i),t.set(n,n,n,n),this.clamp(e,t)}}(),floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this.w=Math.floor(this.w),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this.w=Math.ceil(this.w),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this.w=Math.round(this.w),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this.w=this.w<0?Math.ceil(this.w):Math.floor(this.w),this},negate:function(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this.w=-this.w,this},dot:function(e){return this.x*e.x+this.y*e.y+this.z*e.z+this.w*e.w},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)},normalize:function(){return this.divideScalar(this.length())},setLength:function(e){return this.multiplyScalar(e/this.length())},lerp:function(e,t){return this.x+=(e.x-this.x)*t,this.y+=(e.y-this.y)*t,this.z+=(e.z-this.z)*t,this.w+=(e.w-this.w)*t,this},lerpVectors:function(e,t,i){return this.subVectors(t,e).multiplyScalar(i).add(e)},equals:function(e){return e.x===this.x&&e.y===this.y&&e.z===this.z&&e.w===this.w},fromArray:function(e,t){return t===undefined&&(t=0),this.x=e[t],this.y=e[t+1],this.z=e[t+2],this.w=e[t+3],this},toArray:function(e,t){return e===undefined&&(e=[]),t===undefined&&(t=0),e[t]=this.x,e[t+1]=this.y,e[t+2]=this.z,e[t+3]=this.w,e},fromAttribute:function(e,t,i){return i!==undefined&&console.warn("THREE.Vector4: offset has been removed from .fromAttribute()."),this.x=e.getX(t),this.y=e.getY(t),this.z=e.getZ(t),this.w=e.getW(t),this}},Object.assign(a.prototype,t.prototype,{isWebGLRenderTarget:!0,setSize:function(e,t){this.width===e&&this.height===t||(this.width=e,this.height=t,this.dispose()),this.viewport.set(0,0,e,t),this.scissor.set(0,0,e,t)},clone:function(){return(new this.constructor).copy(this)},copy:function(e){return this.width=e.width,this.height=e.height,this.viewport.copy(e.viewport),this.texture=e.texture.clone(),this.depthBuffer=e.depthBuffer,this.stencilBuffer=e.stencilBuffer,this.depthTexture=e.depthTexture,this},dispose:function(){this.dispatchEvent({type:"dispose"})}}),s.prototype=Object.create(a.prototype),s.prototype.constructor=s,s.prototype.isWebGLRenderTargetCube=!0,o.prototype={constructor:o,get x(){return this._x},set x(e){this._x=e,this.onChangeCallback()},get y(){return this._y},set y(e){this._y=e,this.onChangeCallback()},get z(){return this._z},set z(e){this._z=e,this.onChangeCallback()},get w(){return this._w},set w(e){this._w=e,this.onChangeCallback()},set:function(e,t,i,n){return this._x=e,this._y=t,this._z=i,this._w=n,this.onChangeCallback(),this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._w)},copy:function(e){return this._x=e.x,this._y=e.y,this._z=e.z,this._w=e.w,this.onChangeCallback(),this},setFromEuler:function(e,t){if(!1===(e&&e.isEuler))throw new Error("THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.");var i=Math.cos(e._x/2),n=Math.cos(e._y/2),r=Math.cos(e._z/2),a=Math.sin(e._x/2),s=Math.sin(e._y/2),o=Math.sin(e._z/2),u=e.order;return"XYZ"===u?(this._x=a*n*r+i*s*o,this._y=i*s*r-a*n*o,this._z=i*n*o+a*s*r,this._w=i*n*r-a*s*o):"YXZ"===u?(this._x=a*n*r+i*s*o,this._y=i*s*r-a*n*o,this._z=i*n*o-a*s*r,this._w=i*n*r+a*s*o):"ZXY"===u?(this._x=a*n*r-i*s*o,this._y=i*s*r+a*n*o,this._z=i*n*o+a*s*r,this._w=i*n*r-a*s*o):"ZYX"===u?(this._x=a*n*r-i*s*o,this._y=i*s*r+a*n*o,this._z=i*n*o-a*s*r,this._w=i*n*r+a*s*o):"YZX"===u?(this._x=a*n*r+i*s*o,this._y=i*s*r+a*n*o,this._z=i*n*o-a*s*r,this._w=i*n*r-a*s*o):"XZY"===u&&(this._x=a*n*r-i*s*o,this._y=i*s*r-a*n*o,this._z=i*n*o+a*s*r,this._w=i*n*r+a*s*o),!1!==t&&this.onChangeCallback(),this},setFromAxisAngle:function(e,t){var i=t/2,n=Math.sin(i);return this._x=e.x*n,this._y=e.y*n,this._z=e.z*n,this._w=Math.cos(i),this.onChangeCallback(),this},setFromRotationMatrix:function(e){var t,i=e.elements,n=i[0],r=i[4],a=i[8],s=i[1],o=i[5],u=i[9],c=i[2],l=i[6],h=i[10],d=n+o+h;return d>0?(t=.5/Math.sqrt(d+1),this._w=.25/t,this._x=(l-u)*t,this._y=(a-c)*t,this._z=(s-r)*t):n>o&&n>h?(t=2*Math.sqrt(1+n-o-h),this._w=(l-u)/t,this._x=.25*t,this._y=(r+s)/t,this._z=(a+c)/t):o>h?(t=2*Math.sqrt(1+o-n-h),this._w=(a-c)/t,this._x=(r+s)/t,this._y=.25*t,this._z=(u+l)/t):(t=2*Math.sqrt(1+h-n-o),this._w=(s-r)/t,this._x=(a+c)/t,this._y=(u+l)/t,this._z=.25*t),this.onChangeCallback(),this},setFromUnitVectors:function(){var e,t;return function(i,n){return e===undefined&&(e=new u),(t=i.dot(n)+1)<1e-6?(t=0,Math.abs(i.x)>Math.abs(i.z)?e.set(-i.y,i.x,0):e.set(0,-i.z,i.y)):e.crossVectors(i,n),this._x=e.x,this._y=e.y,this._z=e.z,this._w=t,this.normalize()}}(),inverse:function(){return this.conjugate().normalize()},conjugate:function(){return this._x*=-1,this._y*=-1,this._z*=-1,this.onChangeCallback(),this},dot:function(e){return this._x*e._x+this._y*e._y+this._z*e._z+this._w*e._w},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var e=this.length();return 0===e?(this._x=0,this._y=0,this._z=0,this._w=1):(e=1/e,this._x=this._x*e,this._y=this._y*e,this._z=this._z*e,this._w=this._w*e),this.onChangeCallback(),this},multiply:function(e,t){return t!==undefined?(console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(e,t)):this.multiplyQuaternions(this,e)},premultiply:function(e){return this.multiplyQuaternions(e,this)},multiplyQuaternions:function(e,t){var i=e._x,n=e._y,r=e._z,a=e._w,s=t._x,o=t._y,u=t._z,c=t._w;return this._x=i*c+a*s+n*u-r*o,this._y=n*c+a*o+r*s-i*u,this._z=r*c+a*u+i*o-n*s,this._w=a*c-i*s-n*o-r*u,this.onChangeCallback(),this},slerp:function(e,t){if(0===t)return this;if(1===t)return this.copy(e);var i=this._x,n=this._y,r=this._z,a=this._w,s=a*e._w+i*e._x+n*e._y+r*e._z;if(s<0?(this._w=-e._w,this._x=-e._x,this._y=-e._y,this._z=-e._z,s=-s):this.copy(e),s>=1)return this._w=a,this._x=i,this._y=n,this._z=r,this;var o=Math.sqrt(1-s*s);if(Math.abs(o)<.001)return this._w=.5*(a+this._w),this._x=.5*(i+this._x),this._y=.5*(n+this._y),this._z=.5*(r+this._z),this;var u=Math.atan2(o,s),c=Math.sin((1-t)*u)/o,l=Math.sin(t*u)/o;return this._w=a*c+this._w*l,this._x=i*c+this._x*l,this._y=n*c+this._y*l,this._z=r*c+this._z*l,this.onChangeCallback(),this},equals:function(e){return e._x===this._x&&e._y===this._y&&e._z===this._z&&e._w===this._w},fromArray:function(e,t){return t===undefined&&(t=0),this._x=e[t],this._y=e[t+1],this._z=e[t+2],this._w=e[t+3],this.onChangeCallback(),this},toArray:function(e,t){return e===undefined&&(e=[]),t===undefined&&(t=0),e[t]=this._x,e[t+1]=this._y,e[t+2]=this._z,e[t+3]=this._w,e},onChange:function(e){return this.onChangeCallback=e,this},onChangeCallback:function(){}},Object.assign(o,{slerp:function(e,t,i,n){return i.copy(e).slerp(t,n)},slerpFlat:function(e,t,i,n,r,a,s){var o=i[n+0],u=i[n+1],c=i[n+2],l=i[n+3],h=r[a+0],d=r[a+1],p=r[a+2],f=r[a+3];if(l!==f||o!==h||u!==d||c!==p){var g=1-s,m=o*h+u*d+c*p+l*f,M=m>=0?1:-1,y=1-m*m;if(y>Number.EPSILON){var v=Math.sqrt(y),w=Math.atan2(v,m*M);g=Math.sin(g*w)/v,s=Math.sin(s*w)/v}var x=s*M;if(o=o*g+h*x,u=u*g+d*x,c=c*g+p*x,l=l*g+f*x,g===1-s){var T=1/Math.sqrt(o*o+u*u+c*c+l*l);o*=T,u*=T,c*=T,l*=T}}e[t]=o,e[t+1]=u,e[t+2]=c,e[t+3]=l}}),u.prototype={constructor:u,isVector3:!0,set:function(e,t,i){return this.x=e,this.y=t,this.z=i,this},setScalar:function(e){return this.x=e,this.y=e,this.z=e,this},setX:function(e){return this.x=e,this},setY:function(e){return this.y=e,this},setZ:function(e){return this.z=e,this},setComponent:function(e,t){switch(e){case 0:this.x=t;break;case 1:this.y=t;break;case 2:this.z=t;break;default:throw new Error("index is out of range: "+e)}return this},getComponent:function(e){switch(e){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("index is out of range: "+e)}},clone:function(){return new this.constructor(this.x,this.y,this.z)},copy:function(e){return this.x=e.x,this.y=e.y,this.z=e.z,this},add:function(e,t){return t!==undefined?(console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(e,t)):(this.x+=e.x,this.y+=e.y,this.z+=e.z,this)},addScalar:function(e){return this.x+=e,this.y+=e,this.z+=e,this},addVectors:function(e,t){return this.x=e.x+t.x,this.y=e.y+t.y,this.z=e.z+t.z,this},addScaledVector:function(e,t){return this.x+=e.x*t,this.y+=e.y*t,this.z+=e.z*t,this},sub:function(e,t){return t!==undefined?(console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(e,t)):(this.x-=e.x,this.y-=e.y,this.z-=e.z,this)},subScalar:function(e){return this.x-=e,this.y-=e,this.z-=e,this},subVectors:function(e,t){return this.x=e.x-t.x,this.y=e.y-t.y,this.z=e.z-t.z,this},multiply:function(e,t){return t!==undefined?(console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(e,t)):(this.x*=e.x,this.y*=e.y,this.z*=e.z,this)},multiplyScalar:function(e){return isFinite(e)?(this.x*=e,this.y*=e,this.z*=e):(this.x=0,this.y=0,this.z=0),this},multiplyVectors:function(e,t){return this.x=e.x*t.x,this.y=e.y*t.y,this.z=e.z*t.z,this},applyEuler:function(){var e;return function(t){return!1===(t&&t.isEuler)&&console.error("THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order."),e===undefined&&(e=new o),this.applyQuaternion(e.setFromEuler(t))}}(),applyAxisAngle:function(){var e;return function(t,i){return e===undefined&&(e=new o),this.applyQuaternion(e.setFromAxisAngle(t,i))}}(),applyMatrix3:function(e){var t=this.x,i=this.y,n=this.z,r=e.elements;return this.x=r[0]*t+r[3]*i+r[6]*n,this.y=r[1]*t+r[4]*i+r[7]*n,this.z=r[2]*t+r[5]*i+r[8]*n,this},applyMatrix4:function(e){var t=this.x,i=this.y,n=this.z,r=e.elements;return this.x=r[0]*t+r[4]*i+r[8]*n+r[12],this.y=r[1]*t+r[5]*i+r[9]*n+r[13],this.z=r[2]*t+r[6]*i+r[10]*n+r[14],this},applyProjection:function(e){var t=this.x,i=this.y,n=this.z,r=e.elements,a=1/(r[3]*t+r[7]*i+r[11]*n+r[15]);return this.x=(r[0]*t+r[4]*i+r[8]*n+r[12])*a,this.y=(r[1]*t+r[5]*i+r[9]*n+r[13])*a,this.z=(r[2]*t+r[6]*i+r[10]*n+r[14])*a,this},applyQuaternion:function(e){var t=this.x,i=this.y,n=this.z,r=e.x,a=e.y,s=e.z,o=e.w,u=o*t+a*n-s*i,c=o*i+s*t-r*n,l=o*n+r*i-a*t,h=-r*t-a*i-s*n;return this.x=u*o+h*-r+c*-s-l*-a,this.y=c*o+h*-a+l*-r-u*-s,this.z=l*o+h*-s+u*-a-c*-r,this},project:function(){var e;return function(t){return e===undefined&&(e=new c),e.multiplyMatrices(t.projectionMatrix,e.getInverse(t.matrixWorld)),this.applyProjection(e)}}(),unproject:function(){var e;return function(t){return e===undefined&&(e=new c),e.multiplyMatrices(t.matrixWorld,e.getInverse(t.projectionMatrix)),this.applyProjection(e)}}(),transformDirection:function(e){var t=this.x,i=this.y,n=this.z,r=e.elements;return this.x=r[0]*t+r[4]*i+r[8]*n,this.y=r[1]*t+r[5]*i+r[9]*n,this.z=r[2]*t+r[6]*i+r[10]*n,this.normalize()},divide:function(e){return this.x/=e.x,this.y/=e.y,this.z/=e.z,this},divideScalar:function(e){return this.multiplyScalar(1/e)},min:function(e){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this.z=Math.min(this.z,e.z),this},max:function(e){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this.z=Math.max(this.z,e.z),this},clamp:function(e,t){return this.x=Math.max(e.x,Math.min(t.x,this.x)),this.y=Math.max(e.y,Math.min(t.y,this.y)),this.z=Math.max(e.z,Math.min(t.z,this.z)),this},clampScalar:function(){var e,t;return function(i,n){return e===undefined&&(e=new u,t=new u),e.set(i,i,i),t.set(n,n,n),this.clamp(e,t)}}(),clampLength:function(e,t){var i=this.length();return this.multiplyScalar(Math.max(e,Math.min(t,i))/i)},floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this},ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this},round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this},roundToZero:function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this},negate:function(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this},dot:function(e){return this.x*e.x+this.y*e.y+this.z*e.z},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length())},setLength:function(e){return this.multiplyScalar(e/this.length())},lerp:function(e,t){return this.x+=(e.x-this.x)*t,this.y+=(e.y-this.y)*t,this.z+=(e.z-this.z)*t,this},lerpVectors:function(e,t,i){return this.subVectors(t,e).multiplyScalar(i).add(e)},cross:function(e,t){if(t!==undefined)return console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(e,t);var i=this.x,n=this.y,r=this.z;return this.x=n*e.z-r*e.y,this.y=r*e.x-i*e.z,this.z=i*e.y-n*e.x,this},crossVectors:function(e,t){var i=e.x,n=e.y,r=e.z,a=t.x,s=t.y,o=t.z;return this.x=n*o-r*s,this.y=r*a-i*o,this.z=i*s-n*a,this},projectOnVector:function(e){var t=e.dot(this)/e.lengthSq();return this.copy(e).multiplyScalar(t)},projectOnPlane:function(){var e;return function(t){return e===undefined&&(e=new u),e.copy(this).projectOnVector(t),this.sub(e)}}(),reflect:function(){var e;return function(t){return e===undefined&&(e=new u),this.sub(e.copy(t).multiplyScalar(2*this.dot(t)))}}(),angleTo:function(e){var t=this.dot(e)/Math.sqrt(this.lengthSq()*e.lengthSq());return Math.acos(ds.clamp(t,-1,1))},distanceTo:function(e){return Math.sqrt(this.distanceToSquared(e))},distanceToSquared:function(e){var t=this.x-e.x,i=this.y-e.y,n=this.z-e.z;return t*t+i*i+n*n},distanceToManhattan:function(e){return Math.abs(this.x-e.x)+Math.abs(this.y-e.y)+Math.abs(this.z-e.z)},setFromSpherical:function(e){var t=Math.sin(e.phi)*e.radius;return this.x=t*Math.sin(e.theta),this.y=Math.cos(e.phi)*e.radius,this.z=t*Math.cos(e.theta),this},setFromCylindrical:function(e){return this.x=e.radius*Math.sin(e.theta),this.y=e.y,this.z=e.radius*Math.cos(e.theta),this},setFromMatrixPosition:function(e){return this.setFromMatrixColumn(e,3)},setFromMatrixScale:function(e){var t=this.setFromMatrixColumn(e,0).length(),i=this.setFromMatrixColumn(e,1).length(),n=this.setFromMatrixColumn(e,2).length();return this.x=t,this.y=i,this.z=n,this},setFromMatrixColumn:function(e,t){if("number"==typeof e){console.warn("THREE.Vector3: setFromMatrixColumn now expects ( matrix, index ).");var i=e;e=t,t=i}return this.fromArray(e.elements,4*t)},equals:function(e){return e.x===this.x&&e.y===this.y&&e.z===this.z},fromArray:function(e,t){return t===undefined&&(t=0),this.x=e[t],this.y=e[t+1],this.z=e[t+2],this},toArray:function(e,t){return e===undefined&&(e=[]),t===undefined&&(t=0),e[t]=this.x,e[t+1]=this.y,e[t+2]=this.z,e},fromAttribute:function(e,t,i){return i!==undefined&&console.warn("THREE.Vector3: offset has been removed from .fromAttribute()."),this.x=e.getX(t),this.y=e.getY(t),this.z=e.getZ(t),this}},c.prototype={constructor:c,isMatrix4:!0,set:function(e,t,i,n,r,a,s,o,u,c,l,h,d,p,f,g){var m=this.elements;return m[0]=e,m[4]=t,m[8]=i,m[12]=n,m[1]=r,m[5]=a,m[9]=s,m[13]=o,m[2]=u,m[6]=c,m[10]=l,m[14]=h,m[3]=d,m[7]=p,m[11]=f,m[15]=g,this},identity:function(){return this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),this},clone:function(){return(new c).fromArray(this.elements)},copy:function(e){return this.elements.set(e.elements),this},copyPosition:function(e){var t=this.elements,i=e.elements;return t[12]=i[12],t[13]=i[13],t[14]=i[14],this},extractBasis:function(e,t,i){return e.setFromMatrixColumn(this,0),t.setFromMatrixColumn(this,1),i.setFromMatrixColumn(this,2),this},makeBasis:function(e,t,i){return this.set(e.x,t.x,i.x,0,e.y,t.y,i.y,0,e.z,t.z,i.z,0,0,0,0,1),this},extractRotation:function(){var e;return function(t){e===undefined&&(e=new u);var i=this.elements,n=t.elements,r=1/e.setFromMatrixColumn(t,0).length(),a=1/e.setFromMatrixColumn(t,1).length(),s=1/e.setFromMatrixColumn(t,2).length();return i[0]=n[0]*r,i[1]=n[1]*r,i[2]=n[2]*r,i[4]=n[4]*a,i[5]=n[5]*a,i[6]=n[6]*a,i[8]=n[8]*s,i[9]=n[9]*s,i[10]=n[10]*s,this}}(),makeRotationFromEuler:function(e){!1===(e&&e.isEuler)&&console.error("THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var t=this.elements,i=e.x,n=e.y,r=e.z,a=Math.cos(i),s=Math.sin(i),o=Math.cos(n),u=Math.sin(n),c=Math.cos(r),l=Math.sin(r);if("XYZ"===e.order){var h=a*c,d=a*l,p=s*c,f=s*l;t[0]=o*c,t[4]=-o*l,t[8]=u,t[1]=d+p*u,t[5]=h-f*u,t[9]=-s*o,t[2]=f-h*u,t[6]=p+d*u,t[10]=a*o}else if("YXZ"===e.order){var g=o*c,m=o*l,M=u*c,y=u*l;t[0]=g+y*s,t[4]=M*s-m,t[8]=a*u,t[1]=a*l,t[5]=a*c,t[9]=-s,t[2]=m*s-M,t[6]=y+g*s,t[10]=a*o}else if("ZXY"===e.order){var g=o*c,m=o*l,M=u*c,y=u*l;t[0]=g-y*s,t[4]=-a*l,t[8]=M+m*s,t[1]=m+M*s,t[5]=a*c,t[9]=y-g*s,t[2]=-a*u,t[6]=s,t[10]=a*o}else if("ZYX"===e.order){var h=a*c,d=a*l,p=s*c,f=s*l;t[0]=o*c,t[4]=p*u-d,t[8]=h*u+f,t[1]=o*l,t[5]=f*u+h,t[9]=d*u-p,t[2]=-u,t[6]=s*o,t[10]=a*o}else if("YZX"===e.order){var v=a*o,w=a*u,x=s*o,T=s*u;t[0]=o*c,t[4]=T-v*l,t[8]=x*l+w,t[1]=l,t[5]=a*c,t[9]=-s*c,t[2]=-u*c,t[6]=w*l+x,t[10]=v-T*l}else if("XZY"===e.order){var v=a*o,w=a*u,x=s*o,T=s*u;t[0]=o*c,t[4]=-l,t[8]=u*c,t[1]=v*l+T,t[5]=a*c,t[9]=w*l-x,t[2]=x*l-w,t[6]=s*c,t[10]=T*l+v}return t[3]=0,t[7]=0,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,this},makeRotationFromQuaternion:function(e){var t=this.elements,i=e.x,n=e.y,r=e.z,a=e.w,s=i+i,o=n+n,u=r+r,c=i*s,l=i*o,h=i*u,d=n*o,p=n*u,f=r*u,g=a*s,m=a*o,M=a*u;return t[0]=1-(d+f),t[4]=l-M,t[8]=h+m,t[1]=l+M,t[5]=1-(c+f),t[9]=p-g,t[2]=h-m,t[6]=p+g,t[10]=1-(c+d),t[3]=0,t[7]=0,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,this},lookAt:function(){var e,t,i;return function(n,r,a){e===undefined&&(e=new u,t=new u,i=new u);var s=this.elements;return i.subVectors(n,r).normalize(),0===i.lengthSq()&&(i.z=1),e.crossVectors(a,i).normalize(),0===e.lengthSq()&&(i.z+=1e-4,e.crossVectors(a,i).normalize()),t.crossVectors(i,e),s[0]=e.x,s[4]=t.x,s[8]=i.x,s[1]=e.y,s[5]=t.y,s[9]=i.y,s[2]=e.z,s[6]=t.z,s[10]=i.z,this}}(),multiply:function(e,t){return t!==undefined?(console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."),this.multiplyMatrices(e,t)):this.multiplyMatrices(this,e)},premultiply:function(e){return this.multiplyMatrices(e,this)},multiplyMatrices:function(e,t){var i=e.elements,n=t.elements,r=this.elements,a=i[0],s=i[4],o=i[8],u=i[12],c=i[1],l=i[5],h=i[9],d=i[13],p=i[2],f=i[6],g=i[10],m=i[14],M=i[3],y=i[7],v=i[11],w=i[15],x=n[0],T=n[4],E=n[8],N=n[12],L=n[1],D=n[5],A=n[9],I=n[13],b=n[2],S=n[6],_=n[10],j=n[14],C=n[3],z=n[7],O=n[11],R=n[15];return r[0]=a*x+s*L+o*b+u*C,r[4]=a*T+s*D+o*S+u*z,r[8]=a*E+s*A+o*_+u*O,r[12]=a*N+s*I+o*j+u*R,r[1]=c*x+l*L+h*b+d*C,r[5]=c*T+l*D+h*S+d*z,r[9]=c*E+l*A+h*_+d*O,r[13]=c*N+l*I+h*j+d*R,r[2]=p*x+f*L+g*b+m*C,r[6]=p*T+f*D+g*S+m*z,r[10]=p*E+f*A+g*_+m*O,r[14]=p*N+f*I+g*j+m*R,r[3]=M*x+y*L+v*b+w*C,r[7]=M*T+y*D+v*S+w*z,r[11]=M*E+y*A+v*_+w*O,r[15]=M*N+y*I+v*j+w*R,this},multiplyToArray:function(e,t,i){var n=this.elements;return this.multiplyMatrices(e,t),i[0]=n[0],i[1]=n[1],i[2]=n[2],i[3]=n[3],i[4]=n[4],i[5]=n[5],i[6]=n[6],i[7]=n[7],i[8]=n[8],i[9]=n[9],i[10]=n[10],i[11]=n[11],i[12]=n[12],i[13]=n[13],i[14]=n[14],i[15]=n[15],this},multiplyScalar:function(e){var t=this.elements;return t[0]*=e,t[4]*=e,t[8]*=e,t[12]*=e,t[1]*=e,t[5]*=e,t[9]*=e,t[13]*=e,t[2]*=e,t[6]*=e,t[10]*=e,t[14]*=e,t[3]*=e,t[7]*=e,t[11]*=e,t[15]*=e,this},applyToVector3Array:function(){var e;return function(t,i,n){e===undefined&&(e=new u),i===undefined&&(i=0),n===undefined&&(n=t.length);for(var r=0,a=i;r 0.0 ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\t\t\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\t\t\tfloat maxDistanceCutoffFactor = pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t\t\treturn distanceFalloff * maxDistanceCutoffFactor;\n#else\n\t\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n#endif\n\t\t}\n\t\treturn 1.0;\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 ltcTextureCoords( const in GeometricContext geometry, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = (LUT_SIZE - 1.0)/LUT_SIZE;\n\tconst float LUT_BIAS = 0.5/LUT_SIZE;\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 P = geometry.position;\n\tfloat theta = acos( dot( N, V ) );\n\tvec2 uv = vec2(\n\t\tsqrt( saturate( roughness ) ),\n\t\tsaturate( theta / ( 0.5 * PI ) ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nvoid clipQuadToHorizon( inout vec3 L[5], out int n ) {\n\tint config = 0;\n\tif ( L[0].z > 0.0 ) config += 1;\n\tif ( L[1].z > 0.0 ) config += 2;\n\tif ( L[2].z > 0.0 ) config += 4;\n\tif ( L[3].z > 0.0 ) config += 8;\n\tn = 0;\n\tif ( config == 0 ) {\n\t} else if ( config == 1 ) {\n\t\tn = 3;\n\t\tL[1] = -L[1].z * L[0] + L[0].z * L[1];\n\t\tL[2] = -L[3].z * L[0] + L[0].z * L[3];\n\t} else if ( config == 2 ) {\n\t\tn = 3;\n\t\tL[0] = -L[0].z * L[1] + L[1].z * L[0];\n\t\tL[2] = -L[2].z * L[1] + L[1].z * L[2];\n\t} else if ( config == 3 ) {\n\t\tn = 4;\n\t\tL[2] = -L[2].z * L[1] + L[1].z * L[2];\n\t\tL[3] = -L[3].z * L[0] + L[0].z * L[3];\n\t} else if ( config == 4 ) {\n\t\tn = 3;\n\t\tL[0] = -L[3].z * L[2] + L[2].z * L[3];\n\t\tL[1] = -L[1].z * L[2] + L[2].z * L[1];\n\t} else if ( config == 5 ) {\n\t\tn = 0;\n\t} else if ( config == 6 ) {\n\t\tn = 4;\n\t\tL[0] = -L[0].z * L[1] + L[1].z * L[0];\n\t\tL[3] = -L[3].z * L[2] + L[2].z * L[3];\n\t} else if ( config == 7 ) {\n\t\tn = 5;\n\t\tL[4] = -L[3].z * L[0] + L[0].z * L[3];\n\t\tL[3] = -L[3].z * L[2] + L[2].z * L[3];\n\t} else if ( config == 8 ) {\n\t\tn = 3;\n\t\tL[0] = -L[0].z * L[3] + L[3].z * L[0];\n\t\tL[1] = -L[2].z * L[3] + L[3].z * L[2];\n\t\tL[2] = L[3];\n\t} else if ( config == 9 ) {\n\t\tn = 4;\n\t\tL[1] = -L[1].z * L[0] + L[0].z * L[1];\n\t\tL[2] = -L[2].z * L[3] + L[3].z * L[2];\n\t} else if ( config == 10 ) {\n\t\tn = 0;\n\t} else if ( config == 11 ) {\n\t\tn = 5;\n\t\tL[4] = L[3];\n\t\tL[3] = -L[2].z * L[3] + L[3].z * L[2];\n\t\tL[2] = -L[2].z * L[1] + L[1].z * L[2];\n\t} else if ( config == 12 ) {\n\t\tn = 4;\n\t\tL[1] = -L[1].z * L[2] + L[2].z * L[1];\n\t\tL[0] = -L[0].z * L[3] + L[3].z * L[0];\n\t} else if ( config == 13 ) {\n\t\tn = 5;\n\t\tL[4] = L[3];\n\t\tL[3] = L[2];\n\t\tL[2] = -L[1].z * L[2] + L[2].z * L[1];\n\t\tL[1] = -L[1].z * L[0] + L[0].z * L[1];\n\t} else if ( config == 14 ) {\n\t\tn = 5;\n\t\tL[4] = -L[0].z * L[3] + L[3].z * L[0];\n\t\tL[0] = -L[0].z * L[1] + L[1].z * L[0];\n\t} else if ( config == 15 ) {\n\t\tn = 4;\n\t}\n\tif ( n == 3 )\n\t\tL[3] = L[0];\n\tif ( n == 4 )\n\t\tL[4] = L[0];\n}\nfloat integrateLtcBrdfOverRectEdge( vec3 v1, vec3 v2 ) {\n\tfloat cosTheta = dot( v1, v2 );\n\tfloat theta = acos( cosTheta );\n\tfloat res = cross( v1, v2 ).z * ( ( theta > 0.001 ) ? theta / sin( theta ) : 1.0 );\n\treturn res;\n}\nvoid initRectPoints( const in vec3 pos, const in vec3 halfWidth, const in vec3 halfHeight, out vec3 rectPoints[4] ) {\n\trectPoints[0] = pos - halfWidth - halfHeight;\n\trectPoints[1] = pos + halfWidth - halfHeight;\n\trectPoints[2] = pos + halfWidth + halfHeight;\n\trectPoints[3] = pos - halfWidth + halfHeight;\n}\nvec3 integrateLtcBrdfOverRect( const in GeometricContext geometry, const in mat3 brdfMat, const in vec3 rectPoints[4] ) {\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 P = geometry.position;\n\tvec3 T1, T2;\n\tT1 = normalize(V - N * dot( V, N ));\n\tT2 = - cross( N, T1 );\n\tmat3 brdfWrtSurface = brdfMat * transpose( mat3( T1, T2, N ) );\n\tvec3 clippedRect[5];\n\tclippedRect[0] = brdfWrtSurface * ( rectPoints[0] - P );\n\tclippedRect[1] = brdfWrtSurface * ( rectPoints[1] - P );\n\tclippedRect[2] = brdfWrtSurface * ( rectPoints[2] - P );\n\tclippedRect[3] = brdfWrtSurface * ( rectPoints[3] - P );\n\tint n;\n\tclipQuadToHorizon(clippedRect, n);\n\tif ( n == 0 )\n\t\treturn vec3( 0, 0, 0 );\n\tclippedRect[0] = normalize( clippedRect[0] );\n\tclippedRect[1] = normalize( clippedRect[1] );\n\tclippedRect[2] = normalize( clippedRect[2] );\n\tclippedRect[3] = normalize( clippedRect[3] );\n\tclippedRect[4] = normalize( clippedRect[4] );\n\tfloat sum = 0.0;\n\tsum += integrateLtcBrdfOverRectEdge( clippedRect[0], clippedRect[1] );\n\tsum += integrateLtcBrdfOverRectEdge( clippedRect[1], clippedRect[2] );\n\tsum += integrateLtcBrdfOverRectEdge( clippedRect[2], clippedRect[3] );\n\tif (n >= 4)\n\t\tsum += integrateLtcBrdfOverRectEdge( clippedRect[3], clippedRect[4] );\n\tif (n == 5)\n\t\tsum += integrateLtcBrdfOverRectEdge( clippedRect[4], clippedRect[0] );\n\tsum = max( 0.0, sum );\n\tvec3 Lo_i = vec3( sum, sum, sum );\n\treturn Lo_i;\n}\nvec3 Rect_Area_Light_Specular_Reflectance(\n\t\tconst in GeometricContext geometry,\n\t\tconst in vec3 lightPos, const in vec3 lightHalfWidth, const in vec3 lightHalfHeight,\n\t\tconst in float roughness,\n\t\tconst in sampler2D ltcMat, const in sampler2D ltcMag ) {\n\tvec3 rectPoints[4];\n\tinitRectPoints( lightPos, lightHalfWidth, lightHalfHeight, rectPoints );\n\tvec2 uv = ltcTextureCoords( geometry, roughness );\n\tvec4 brdfLtcApproxParams, t;\n\tbrdfLtcApproxParams = texture2D( ltcMat, uv );\n\tt = texture2D( ltcMat, uv );\n\tfloat brdfLtcScalar = texture2D( ltcMag, uv ).a;\n\tmat3 brdfLtcApproxMat = mat3(\n\t\tvec3( 1, 0, t.y ),\n\t\tvec3( 0, t.z, 0 ),\n\t\tvec3( t.w, 0, t.x )\n\t);\n\tvec3 specularReflectance = integrateLtcBrdfOverRect( geometry, brdfLtcApproxMat, rectPoints );\n\tspecularReflectance *= brdfLtcScalar;\n\treturn specularReflectance;\n}\nvec3 Rect_Area_Light_Diffuse_Reflectance(\n\t\tconst in GeometricContext geometry,\n\t\tconst in vec3 lightPos, const in vec3 lightHalfWidth, const in vec3 lightHalfHeight ) {\n\tvec3 rectPoints[4];\n\tinitRectPoints( lightPos, lightHalfWidth, lightHalfHeight, rectPoints );\n\tmat3 diffuseBrdfMat = mat3(1);\n\tvec3 diffuseReflectance = integrateLtcBrdfOverRect( geometry, diffuseBrdfMat, rectPoints );\n\treturn diffuseReflectance;\n}\nvec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\tvec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;\n\treturn specularColor * AB.x + AB.y;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n",bumpmap_pars_fragment:"#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = dFdx( surf_pos );\n\t\tvec3 vSigmaY = dFdy( surf_pos );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif\n",clipping_planes_fragment:"#if NUM_CLIPPING_PLANES > 0\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; ++ i ) {\n\t\tvec4 plane = clippingPlanes[ i ];\n\t\tif ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t\t\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; ++ i ) {\n\t\t\tvec4 plane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\tif ( clipped ) discard;\n\t\n\t#endif\n#endif\n",clipping_planes_pars_fragment:"#if NUM_CLIPPING_PLANES > 0\n\t#if ! defined( PHYSICAL ) && ! defined( PHONG )\n\t\tvarying vec3 vViewPosition;\n\t#endif\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif\n",clipping_planes_pars_vertex:"#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvarying vec3 vViewPosition;\n#endif\n",clipping_planes_vertex:"#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n",color_fragment:"#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif",color_pars_fragment:"#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif\n",color_pars_vertex:"#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif",color_vertex:"#ifdef USE_COLOR\n\tvColor.xyz = color.xyz;\n#endif",common:"#define PI 3.14159265359\n#define PI2 6.28318530718\n#define PI_HALF 1.5707963267949\n#define RECIPROCAL_PI 0.31830988618\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transpose( const in mat3 v ) {\n\tmat3 tmp;\n\ttmp[0] = vec3(v[0].x, v[1].x, v[2].x);\n\ttmp[1] = vec3(v[0].y, v[1].y, v[2].y);\n\ttmp[2] = vec3(v[0].z, v[1].z, v[2].z);\n\treturn tmp;\n}\n",cube_uv_reflection_fragment:"#ifdef ENVMAP_TYPE_CUBE_UV\n#define cubeUV_textureSize (1024.0)\nint getFaceFromDirection(vec3 direction) {\n\tvec3 absDirection = abs(direction);\n\tint face = -1;\n\tif( absDirection.x > absDirection.z ) {\n\t\tif(absDirection.x > absDirection.y )\n\t\t\tface = direction.x > 0.0 ? 0 : 3;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\telse {\n\t\tif(absDirection.z > absDirection.y )\n\t\t\tface = direction.z > 0.0 ? 2 : 5;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\treturn face;\n}\n#define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0)\n#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))\nvec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {\n\tfloat scale = exp2(cubeUV_maxLods1 - roughnessLevel);\n\tfloat dxRoughness = dFdx(roughness);\n\tfloat dyRoughness = dFdy(roughness);\n\tvec3 dx = dFdx( vec * scale * dxRoughness );\n\tvec3 dy = dFdy( vec * scale * dyRoughness );\n\tfloat d = max( dot( dx, dx ), dot( dy, dy ) );\n\td = clamp(d, 1.0, cubeUV_rangeClamp);\n\tfloat mipLevel = 0.5 * log2(d);\n\treturn vec2(floor(mipLevel), fract(mipLevel));\n}\n#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)\n#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)\nvec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {\n\tmipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;\n\tfloat a = 16.0 * cubeUV_rcpTextureSize;\n\tvec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );\n\tvec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;\n\tfloat powScale = exp2_packed.x * exp2_packed.y;\n\tfloat scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;\n\tfloat mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;\n\tbool bRes = mipLevel == 0.0;\n\tscale = bRes && (scale < a) ? a : scale;\n\tvec3 r;\n\tvec2 offset;\n\tint face = getFaceFromDirection(direction);\n\tfloat rcpPowScale = 1.0 / powScale;\n\tif( face == 0) {\n\t\tr = vec3(direction.x, -direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 1) {\n\t\tr = vec3(direction.y, direction.x, direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 2) {\n\t\tr = vec3(direction.z, direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 3) {\n\t\tr = vec3(direction.x, direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse if( face == 4) {\n\t\tr = vec3(direction.y, direction.x, -direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse {\n\t\tr = vec3(direction.z, -direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\tr = normalize(r);\n\tfloat texelOffset = 0.5 * cubeUV_rcpTextureSize;\n\tvec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;\n\tvec2 base = offset + vec2( texelOffset );\n\treturn base + s * ( scale - 2.0 * texelOffset );\n}\n#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)\nvec4 textureCubeUV(vec3 reflectedDirection, float roughness ) {\n\tfloat roughnessVal = roughness* cubeUV_maxLods3;\n\tfloat r1 = floor(roughnessVal);\n\tfloat r2 = r1 + 1.0;\n\tfloat t = fract(roughnessVal);\n\tvec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);\n\tfloat s = mipInfo.y;\n\tfloat level0 = mipInfo.x;\n\tfloat level1 = level0 + 1.0;\n\tlevel1 = level1 > 5.0 ? 5.0 : level1;\n\tlevel0 += min( floor( s + 0.5 ), 5.0 );\n\tvec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);\n\tvec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));\n\tvec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);\n\tvec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));\n\tvec4 result = mix(color10, color20, t);\n\treturn vec4(result.rgb, 1.0);\n}\n#endif\n",defaultnormal_vertex:"#ifdef FLIP_SIDED\n\tobjectNormal = -objectNormal;\n#endif\nvec3 transformedNormal = normalMatrix * objectNormal;\n",displacementmap_pars_vertex:"#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif\n",displacementmap_vertex:"#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n#endif\n",emissivemap_fragment:"#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif\n",emissivemap_pars_fragment:"#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif\n",encodings_fragment:" gl_FragColor = linearToOutputTexel( gl_FragColor );\n",encodings_pars_fragment:"\nvec4 LinearToLinear( in vec4 value ) {\n return value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n return vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.w );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.w );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n return vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n float maxComponent = max( max( value.r, value.g ), value.b );\n float fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n return vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n return vec4( value.xyz * value.w * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n float maxRGB = max( value.x, max( value.g, value.b ) );\n float M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n M = ceil( M * 255.0 ) / 255.0;\n return vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n return vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n float maxRGB = max( value.x, max( value.g, value.b ) );\n float D = max( maxRange / maxRGB, 1.0 );\n D = min( floor( D ) / 255.0, 1.0 );\n return vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n vec3 Xp_Y_XYZp = value.rgb * cLogLuvM;\n Xp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));\n vec4 vResult;\n vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n float Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n vResult.w = fract(Le);\n vResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0;\n return vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n float Le = value.z * 255.0 + value.w;\n vec3 Xp_Y_XYZp;\n Xp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);\n Xp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n Xp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n vec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM;\n return vec4( max(vRGB, 0.0), 1.0 );\n}\n",envmap_fragment:"#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\tvec2 sampleUV;\n\t\tsampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n\t\tsampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\tvec4 envColor = texture2D( envMap, sampleUV );\n\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\tvec3 reflectView = flipNormal * normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) );\n\t\tvec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\tenvColor = envMapTexelToLinear( envColor );\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif\n",envmap_pars_fragment:"#if defined( USE_ENVMAP ) || defined( PHYSICAL )\n\tuniform float reflectivity;\n\tuniform float envMapIntensity;\n#endif\n#ifdef USE_ENVMAP\n\t#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) )\n\t\tvarying vec3 vWorldPosition;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\tuniform float flipEnvMap;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif\n",envmap_pars_vertex:"#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif\n",envmap_vertex:"#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif\n",fog_fragment:"#ifdef USE_FOG\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tfloat depth = gl_FragDepthEXT / gl_FragCoord.w;\n\t#else\n\t\tfloat depth = gl_FragCoord.z / gl_FragCoord.w;\n\t#endif\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, depth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif\n",fog_pars_fragment:"#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif",gradientmap_pars_fragment:"#ifdef TOON\n\tuniform sampler2D gradientMap;\n\tvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\t\tfloat dotNL = dot( normal, lightDirection );\n\t\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t\t#ifdef USE_GRADIENTMAP\n\t\t\treturn texture2D( gradientMap, coord ).rgb;\n\t\t#else\n\t\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t\t#endif\n\t}\n#endif\n",lightmap_fragment:"#ifdef USE_LIGHTMAP\n\treflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n#endif\n",lightmap_pars_fragment:"#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif",lights_lambert_vertex:"vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\n#if NUM_POINT_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t}\n#endif\n#if NUM_DIR_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n#endif\n",lights_pars:"uniform vec3 ambientLightColor;\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltcMat;\tuniform sampler2D ltcMag;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif\n#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\tvec4 envMapColor = textureCubeUV( queryVec, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar - 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\tvec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent));\n\t\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\t\tvec2 sampleUV;\n\t\t\tsampleUV.y = saturate( reflectVec.y * 0.5 + 0.5 );\n\t\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif\n",lights_phong_fragment:"BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;\n",lights_phong_pars_fragment:"varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3\tdiffuseColor;\n\tvec3\tspecularColor;\n\tfloat\tspecularShininess;\n\tfloat\tspecularStrength;\n};\n#if NUM_RECT_AREA_LIGHTS > 0\n void RE_Direct_RectArea_BlinnPhong( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n vec3 matDiffColor = material.diffuseColor;\n vec3 matSpecColor = material.specularColor;\n vec3 lightColor = rectAreaLight.color;\n float roughness = BlinnExponentToGGXRoughness( material.specularShininess );\n vec3 spec = Rect_Area_Light_Specular_Reflectance(\n geometry,\n rectAreaLight.position, rectAreaLight.halfWidth, rectAreaLight.halfHeight,\n roughness,\n ltcMat, ltcMag );\n vec3 diff = Rect_Area_Light_Diffuse_Reflectance(\n geometry,\n rectAreaLight.position, rectAreaLight.halfWidth, rectAreaLight.halfHeight );\n reflectedLight.directSpecular += lightColor * matSpecColor * spec / PI2;\n reflectedLight.directDiffuse += lightColor * matDiffColor * diff / PI2;\n }\n#endif\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifdef TOON\n\t\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#else\n\t\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\t\tvec3 irradiance = dotNL * directLight.color;\n\t#endif\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)\n",lights_physical_fragment:"PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\n#ifdef STANDARD\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.clearCoat = saturate( clearCoat );\tmaterial.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 );\n#endif\n",lights_physical_pars_fragment:"struct PhysicalMaterial {\n\tvec3\tdiffuseColor;\n\tfloat\tspecularRoughness;\n\tvec3\tspecularColor;\n\t#ifndef STANDARD\n\t\tfloat clearCoat;\n\t\tfloat clearCoatRoughness;\n\t#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearCoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n vec3 matDiffColor = material.diffuseColor;\n vec3 matSpecColor = material.specularColor;\n vec3 lightColor = rectAreaLight.color;\n float roughness = material.specularRoughness;\n vec3 spec = Rect_Area_Light_Specular_Reflectance(\n geometry,\n rectAreaLight.position, rectAreaLight.halfWidth, rectAreaLight.halfHeight,\n roughness,\n ltcMat, ltcMag );\n vec3 diff = Rect_Area_Light_Diffuse_Reflectance(\n geometry,\n rectAreaLight.position, rectAreaLight.halfWidth, rectAreaLight.halfHeight );\n reflectedLight.directSpecular += lightColor * matSpecColor * spec;\n reflectedLight.directDiffuse += lightColor * matDiffColor * diff;\n }\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifndef STANDARD\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );\n\treflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\t#ifndef STANDARD\n\t\treflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifndef STANDARD\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\tfloat dotNL = dotNV;\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.indirectSpecular += ( 1.0 - clearCoatDHR ) * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );\n\t#ifndef STANDARD\n\t\treflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\n#define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness )\n#define Material_ClearCoat_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.clearCoatRoughness )\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}\n",lights_template:"\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = normalize( vViewPosition );\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\t#ifdef USE_LIGHTMAP\n\t\tvec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t \tirradiance += getLightProbeIndirectIrradiance( geometry, 8 );\n\t#endif\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tvec3 radiance = getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), 8 );\n\t#ifndef STANDARD\n\t\tvec3 clearCoatRadiance = getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), 8 );\n\t#else\n\t\tvec3 clearCoatRadiance = vec3( 0.0 );\n\t#endif\n\t\t\n\tRE_IndirectSpecular( radiance, clearCoatRadiance, geometry, material, reflectedLight );\n#endif\n",logdepthbuf_fragment:"#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\tgl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n#endif",logdepthbuf_pars_fragment:"#ifdef USE_LOGDEPTHBUF\n\tuniform float logDepthBufFC;\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#endif\n#endif\n",logdepthbuf_pars_vertex:"#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#endif\n\tuniform float logDepthBufFC;\n#endif",logdepthbuf_vertex:"#ifdef USE_LOGDEPTHBUF\n\tgl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t#else\n\t\tgl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\t#endif\n#endif\n",map_fragment:"#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif\n",map_pars_fragment:"#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n",map_particle_fragment:"#ifdef USE_MAP\n\tvec4 mapTexel = texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n",map_particle_pars_fragment:"#ifdef USE_MAP\n\tuniform vec4 offsetRepeat;\n\tuniform sampler2D map;\n#endif\n",metalnessmap_fragment:"float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.r;\n#endif\n",metalnessmap_pars_fragment:"#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif",morphnormal_vertex:"#ifdef USE_MORPHNORMALS\n\tobjectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n\tobjectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n\tobjectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n\tobjectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n#endif\n",morphtarget_pars_vertex:"#ifdef USE_MORPHTARGETS\n\t#ifndef USE_MORPHNORMALS\n\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif",morphtarget_vertex:"#ifdef USE_MORPHTARGETS\n\ttransformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n\ttransformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n\ttransformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n\ttransformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\ttransformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n\ttransformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n\ttransformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n\ttransformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\t#endif\n#endif\n",normal_flip:"#ifdef DOUBLE_SIDED\n\tfloat flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n#else\n\tfloat flipNormal = 1.0;\n#endif\n",normal_fragment:"#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal ) * flipNormal;\n#endif\n#ifdef USE_NORMALMAP\n\tnormal = perturbNormal2Arb( -vViewPosition, normal );\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif\n",normalmap_pars_fragment:"#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\t\tvec3 q0 = dFdx( eye_pos.xyz );\n\t\tvec3 q1 = dFdy( eye_pos.xyz );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tvec3 S = normalize( q0 * st1.t - q1 * st0.t );\n\t\tvec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n\t\tvec3 N = normalize( surf_norm );\n\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\tmapN.xy = normalScale * mapN.xy;\n\t\tmat3 tsn = mat3( S, T, N );\n\t\treturn normalize( tsn * mapN );\n\t}\n#endif\n",packing:"vec3 packNormalToRGB( const in vec3 normal ) {\n return normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n return 1.0 - 2.0 * rgb.xyz;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n return ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n return linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n return (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n return ( near * far ) / ( ( far - near ) * invClipZ - far );\n}\n",premultiplied_alpha_fragment:"#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif\n",project_vertex:"#ifdef USE_SKINNING\n\tvec4 mvPosition = modelViewMatrix * skinned;\n#else\n\tvec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n#endif\ngl_Position = projectionMatrix * mvPosition;\n",roughnessmap_fragment:"float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.r;\n#endif\n",roughnessmap_pars_fragment:"#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif",shadowmap_pars_fragment:"#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n #if NUM_RECT_AREA_LIGHTS > 0\n #endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tfloat texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\n\t\tconst vec2 offset = vec2( 0.0, 1.0 );\n\t\tvec2 texelSize = vec2( 1.0 ) / size;\n\t\tvec2 centroidUV = floor( uv * size + 0.5 ) / size;\n\t\tfloat lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\n\t\tfloat lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\n\t\tfloat rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\n\t\tfloat rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\n\t\tvec2 f = fract( uv * size + 0.5 );\n\t\tfloat a = mix( lb, lt, f.y );\n\t\tfloat b = mix( rb, rt, f.y );\n\t\tfloat c = mix( a, b, f.x );\n\t\treturn c;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\treturn (\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn 1.0;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\tfloat dp = ( length( lightToPosition ) - shadowBias ) / 1000.0;\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif\n",shadowmap_pars_vertex:"#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n #if NUM_RECT_AREA_LIGHTS > 0\n #endif\n#endif\n",shadowmap_vertex:"#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n #if NUM_RECT_AREA_LIGHTS > 0\n #endif\n#endif\n",shadowmask_pars_fragment:"float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\tDirectionalLight directionalLight;\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tshadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\tSpotLight spotLight;\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tshadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\tPointLight pointLight;\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tshadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_RECT_AREA_LIGHTS > 0\n\t#endif\n\t#endif\n\treturn shadow;\n}\n",skinbase_vertex:"#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif",skinning_pars_vertex:"#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform sampler2D boneTexture;\n\t\tuniform int boneTextureWidth;\n\t\tuniform int boneTextureHeight;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureWidth ) );\n\t\t\tfloat y = floor( j / float( boneTextureWidth ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureWidth );\n\t\t\tfloat dy = 1.0 / float( boneTextureHeight );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif\n",skinning_vertex:"#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\tskinned = bindMatrixInverse * skinned;\n#endif\n",skinnormal_vertex:"#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n#endif\n",specularmap_fragment:"float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif",specularmap_pars_fragment:"#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif",tonemapping_fragment:"#if defined( TONE_MAPPING )\n gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif\n",tonemapping_pars_fragment:"#define saturate(a) clamp( a, 0.0, 1.0 )\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n return toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n color *= toneMappingExposure;\n return saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n color *= toneMappingExposure;\n return saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n color *= toneMappingExposure;\n color = max( vec3( 0.0 ), color - 0.004 );\n return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\n",uv_pars_fragment:"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n#endif",uv_pars_vertex:"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n\tuniform vec4 offsetRepeat;\n#endif\n",uv_vertex:"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n#endif",uv2_pars_fragment:"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif",uv2_pars_vertex:"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n#endif",uv2_vertex:"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = uv2;\n#endif",worldpos_vertex:"#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( PHYSICAL ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\t#ifdef USE_SKINNING\n\t\tvec4 worldPosition = modelMatrix * skinned;\n\t#else\n\t\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n\t#endif\n#endif\n",cube_frag:"uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldPosition;\n#include \nvoid main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n\tgl_FragColor.a *= opacity;\n}\n",cube_vert:"varying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}\n",depth_frag:"#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( gl_FragCoord.z ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( gl_FragCoord.z );\n\t#endif\n}\n",depth_vert:"#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\n",distanceRGBA_frag:"uniform vec3 lightPos;\nvarying vec4 vWorldPosition;\n#include \n#include \n#include \nvoid main () {\n\t#include \n\tgl_FragColor = packDepthToRGBA( length( vWorldPosition.xyz - lightPos.xyz ) / 1000.0 );\n}\n",distanceRGBA_vert:"varying vec4 vWorldPosition;\n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition;\n}\n",equirect_frag:"uniform sampler2D tEquirect;\nuniform float tFlip;\nvarying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldPosition );\n\tvec2 sampleUV;\n\tsampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );\n\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n}\n",equirect_vert:"varying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}\n",linedashed_frag:"uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n",linedashed_vert:"uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvLineDistance = scale * lineDistance;\n\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n}\n",meshbasic_frag:"uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\treflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n",meshbasic_vert:"#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include \n\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\n",meshlambert_frag:"uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\treflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n",meshlambert_vert:"#define LAMBERT\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\n",meshphong_frag:"#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n",meshphong_vert:"#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}\n",meshphysical_frag:"#define PHYSICAL\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifndef STANDARD\n\tuniform float clearCoat;\n\tuniform float clearCoatRoughness;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n",meshphysical_vert:"#define PHYSICAL\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n}\n",normal_frag:"#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n\t#include \n\t#include \n}\n",normal_vert:"#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}\n",points_frag:"uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n",points_vert:"uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#ifdef USE_SIZEATTENUATION\n\t\tgl_PointSize = size * ( scale / - mvPosition.z );\n\t#else\n\t\tgl_PointSize = size;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}\n",shadow_frag:"uniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( 0.0, 0.0, 0.0, opacity * ( 1.0 - getShadowMask() ) );\n}\n",shadow_vert:"#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"};V.prototype={constructor:V,isColor:!0,r:1,g:1,b:1,set:function(e){return e&&e.isColor?this.copy(e):"number"==typeof e?this.setHex(e):"string"==typeof e&&this.setStyle(e),this},setScalar:function(e){return this.r=e,this.g=e,this.b=e,this},setHex:function(e){return e=Math.floor(e),this.r=(e>>16&255)/255,this.g=(e>>8&255)/255,this.b=(255&e)/255,this},setRGB:function(e,t,i){return this.r=e,this.g=t,this.b=i,this},setHSL:function(){function e(e,t,i){return i<0&&(i+=1),i>1&&(i-=1),i<1/6?e+6*(t-e)*i:i<.5?t:i<2/3?e+6*(t-e)*(2/3-i):e}return function(t,i,n){if(t=ds.euclideanModulo(t,1),i=ds.clamp(i,0,1),n=ds.clamp(n,0,1),0===i)this.r=this.g=this.b=n;else{var r=n<=.5?n*(1+i):n+i-n*i,a=2*n-r;this.r=e(a,r,t+1/3),this.g=e(a,r,t),this.b=e(a,r,t-1/3)}return this}}(),setStyle:function(e){function t(t){t!==undefined&&parseFloat(t)<1&&console.warn("THREE.Color: Alpha component of "+e+" will be ignored.")}var i;if(i=/^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec(e)){var n,r=i[1],a=i[2];switch(r){case"rgb":case"rgba":if(n=/^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(a))return this.r=Math.min(255,parseInt(n[1],10))/255,this.g=Math.min(255,parseInt(n[2],10))/255,this.b=Math.min(255,parseInt(n[3],10))/255,t(n[5]),this;if(n=/^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(a))return this.r=Math.min(100,parseInt(n[1],10))/100,this.g=Math.min(100,parseInt(n[2],10))/100,this.b=Math.min(100,parseInt(n[3],10))/100,t(n[5]),this;break;case"hsl":case"hsla":if(n=/^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(a)){var s=parseFloat(n[1])/360,o=parseInt(n[2],10)/100,u=parseInt(n[3],10)/100;return t(n[5]),this.setHSL(s,o,u)}}}else if(i=/^\#([A-Fa-f0-9]+)$/.exec(e)){var c=(l=i[1]).length;if(3===c)return this.r=parseInt(l.charAt(0)+l.charAt(0),16)/255,this.g=parseInt(l.charAt(1)+l.charAt(1),16)/255,this.b=parseInt(l.charAt(2)+l.charAt(2),16)/255,this;if(6===c)return this.r=parseInt(l.charAt(0)+l.charAt(1),16)/255,this.g=parseInt(l.charAt(2)+l.charAt(3),16)/255,this.b=parseInt(l.charAt(4)+l.charAt(5),16)/255,this}if(e&&e.length>0){var l=xs[e];l!==undefined?this.setHex(l):console.warn("THREE.Color: Unknown color "+e)}return this},clone:function(){return new this.constructor(this.r,this.g,this.b)},copy:function(e){return this.r=e.r,this.g=e.g,this.b=e.b,this},copyGammaToLinear:function(e,t){return t===undefined&&(t=2),this.r=Math.pow(e.r,t),this.g=Math.pow(e.g,t),this.b=Math.pow(e.b,t),this},copyLinearToGamma:function(e,t){t===undefined&&(t=2);var i=t>0?1/t:1;return this.r=Math.pow(e.r,i),this.g=Math.pow(e.g,i),this.b=Math.pow(e.b,i),this},convertGammaToLinear:function(){var e=this.r,t=this.g,i=this.b;return this.r=e*e,this.g=t*t,this.b=i*i,this},convertLinearToGamma:function(){return this.r=Math.sqrt(this.r),this.g=Math.sqrt(this.g),this.b=Math.sqrt(this.b),this},getHex:function(){return 255*this.r<<16^255*this.g<<8^255*this.b<<0},getHexString:function(){return("000000"+this.getHex().toString(16)).slice(-6)},getHSL:function(e){var t,i,n=e||{h:0,s:0,l:0},r=this.r,a=this.g,s=this.b,o=Math.max(r,a,s),u=Math.min(r,a,s),c=(u+o)/2;if(u===o)t=0,i=0;else{var l=o-u;switch(i=c<=.5?l/(o+u):l/(2-o-u),o){case r:t=(a-s)/l+(athis.max.x||e.ythis.max.y)},containsBox:function(e){return this.min.x<=e.min.x&&e.max.x<=this.max.x&&this.min.y<=e.min.y&&e.max.y<=this.max.y},getParameter:function(e,t){return(t||new i).set((e.x-this.min.x)/(this.max.x-this.min.x),(e.y-this.min.y)/(this.max.y-this.min.y))},intersectsBox:function(e){return!(e.max.xthis.max.x||e.max.ythis.max.y)},clampPoint:function(e,t){return(t||new i).copy(e).clamp(this.min,this.max)},distanceToPoint:function(){var e=new i;return function(t){return e.copy(t).clamp(this.min,this.max).sub(t).length()}}(),intersect:function(e){return this.min.max(e.min),this.max.min(e.max),this},union:function(e){return this.min.min(e.min),this.max.max(e.max),this},translate:function(e){return this.min.add(e),this.max.add(e),this},equals:function(e){return e.min.equals(this.min)&&e.max.equals(this.max)}};var Ns=0;q.prototype={constructor:q,isMaterial:!0,get needsUpdate(){return this._needsUpdate},set needsUpdate(e){!0===e&&this.update(),this._needsUpdate=e},setValues:function(e){if(e!==undefined)for(var t in e){var i=e[t];if(i!==undefined){var n=this[t];n!==undefined?n&&n.isColor?n.set(i):n&&n.isVector3&&i&&i.isVector3?n.copy(i):this[t]="overdraw"===t?Number(i):i:console.warn("THREE."+this.type+": '"+t+"' is not a property of this material.")}else console.warn("THREE.Material: '"+t+"' parameter is undefined.")}},toJSON:function(e){function t(e){var t=[];for(var i in e){var n=e[i];delete n.metadata,t.push(n)}return t}var i=e===undefined;i&&(e={textures:{},images:{}});var n={metadata:{version:4.4,type:"Material",generator:"Material.toJSON"}};if(n.uuid=this.uuid,n.type=this.type,""!==this.name&&(n.name=this.name),this.color&&this.color.isColor&&(n.color=this.color.getHex()),this.roughness!==undefined&&(n.roughness=this.roughness),this.metalness!==undefined&&(n.metalness=this.metalness),this.emissive&&this.emissive.isColor&&(n.emissive=this.emissive.getHex()),this.specular&&this.specular.isColor&&(n.specular=this.specular.getHex()),this.shininess!==undefined&&(n.shininess=this.shininess),this.clearCoat!==undefined&&(n.clearCoat=this.clearCoat),this.clearCoatRoughness!==undefined&&(n.clearCoatRoughness=this.clearCoatRoughness),this.map&&this.map.isTexture&&(n.map=this.map.toJSON(e).uuid),this.alphaMap&&this.alphaMap.isTexture&&(n.alphaMap=this.alphaMap.toJSON(e).uuid),this.lightMap&&this.lightMap.isTexture&&(n.lightMap=this.lightMap.toJSON(e).uuid),this.bumpMap&&this.bumpMap.isTexture&&(n.bumpMap=this.bumpMap.toJSON(e).uuid,n.bumpScale=this.bumpScale),this.normalMap&&this.normalMap.isTexture&&(n.normalMap=this.normalMap.toJSON(e).uuid,n.normalScale=this.normalScale.toArray()),this.displacementMap&&this.displacementMap.isTexture&&(n.displacementMap=this.displacementMap.toJSON(e).uuid,n.displacementScale=this.displacementScale,n.displacementBias=this.displacementBias),this.roughnessMap&&this.roughnessMap.isTexture&&(n.roughnessMap=this.roughnessMap.toJSON(e).uuid),this.metalnessMap&&this.metalnessMap.isTexture&&(n.metalnessMap=this.metalnessMap.toJSON(e).uuid),this.emissiveMap&&this.emissiveMap.isTexture&&(n.emissiveMap=this.emissiveMap.toJSON(e).uuid),this.specularMap&&this.specularMap.isTexture&&(n.specularMap=this.specularMap.toJSON(e).uuid),this.envMap&&this.envMap.isTexture&&(n.envMap=this.envMap.toJSON(e).uuid,n.reflectivity=this.reflectivity),this.gradientMap&&this.gradientMap.isTexture&&(n.gradientMap=this.gradientMap.toJSON(e).uuid),this.size!==undefined&&(n.size=this.size),this.sizeAttenuation!==undefined&&(n.sizeAttenuation=this.sizeAttenuation),this.blending!==wr&&(n.blending=this.blending),this.shading!==gr&&(n.shading=this.shading),this.side!==hr&&(n.side=this.side),this.vertexColors!==mr&&(n.vertexColors=this.vertexColors),this.opacity<1&&(n.opacity=this.opacity),!0===this.transparent&&(n.transparent=this.transparent),n.depthFunc=this.depthFunc,n.depthTest=this.depthTest,n.depthWrite=this.depthWrite,this.alphaTest>0&&(n.alphaTest=this.alphaTest),!0===this.premultipliedAlpha&&(n.premultipliedAlpha=this.premultipliedAlpha),!0===this.wireframe&&(n.wireframe=this.wireframe),this.wireframeLinewidth>1&&(n.wireframeLinewidth=this.wireframeLinewidth),"round"!==this.wireframeLinecap&&(n.wireframeLinecap=this.wireframeLinecap),"round"!==this.wireframeLinejoin&&(n.wireframeLinejoin=this.wireframeLinejoin),n.skinning=this.skinning,n.morphTargets=this.morphTargets,i){var r=t(e.textures),a=t(e.images);r.length>0&&(n.textures=r),a.length>0&&(n.images=a)}return n},clone:function(){return(new this.constructor).copy(this)},copy:function(e){this.name=e.name,this.fog=e.fog,this.lights=e.lights,this.blending=e.blending,this.side=e.side,this.shading=e.shading,this.vertexColors=e.vertexColors,this.opacity=e.opacity,this.transparent=e.transparent,this.blendSrc=e.blendSrc,this.blendDst=e.blendDst,this.blendEquation=e.blendEquation,this.blendSrcAlpha=e.blendSrcAlpha,this.blendDstAlpha=e.blendDstAlpha,this.blendEquationAlpha=e.blendEquationAlpha,this.depthFunc=e.depthFunc,this.depthTest=e.depthTest,this.depthWrite=e.depthWrite,this.colorWrite=e.colorWrite,this.precision=e.precision,this.polygonOffset=e.polygonOffset,this.polygonOffsetFactor=e.polygonOffsetFactor,this.polygonOffsetUnits=e.polygonOffsetUnits,this.alphaTest=e.alphaTest,this.premultipliedAlpha=e.premultipliedAlpha,this.overdraw=e.overdraw,this.visible=e.visible,this.clipShadows=e.clipShadows,this.clipIntersection=e.clipIntersection;var t=e.clippingPlanes,i=null;if(null!==t){var n=t.length;i=new Array(n);for(var r=0;r!==n;++r)i[r]=t[r].clone()}return this.clippingPlanes=i,this},update:function(){this.dispatchEvent({type:"update"})},dispose:function(){this.dispatchEvent({type:"dispose"})}},Object.assign(q.prototype,t.prototype),J.prototype=Object.create(q.prototype),J.prototype.constructor=J,J.prototype.isShaderMaterial=!0,J.prototype.copy=function(e){return q.prototype.copy.call(this,e),this.fragmentShader=e.fragmentShader,this.vertexShader=e.vertexShader,this.uniforms=vs.clone(e.uniforms),this.defines=e.defines,this.wireframe=e.wireframe,this.wireframeLinewidth=e.wireframeLinewidth,this.lights=e.lights,this.clipping=e.clipping,this.skinning=e.skinning,this.morphTargets=e.morphTargets,this.morphNormals=e.morphNormals,this.extensions=e.extensions,this},J.prototype.toJSON=function(e){var t=q.prototype.toJSON.call(this,e);return t.uniforms=this.uniforms,t.vertexShader=this.vertexShader,t.fragmentShader=this.fragmentShader,t},K.prototype=Object.create(q.prototype),K.prototype.constructor=K,K.prototype.isMeshDepthMaterial=!0,K.prototype.copy=function(e){return q.prototype.copy.call(this,e),this.depthPacking=e.depthPacking,this.skinning=e.skinning,this.morphTargets=e.morphTargets,this.map=e.map,this.alphaMap=e.alphaMap,this.displacementMap=e.displacementMap,this.displacementScale=e.displacementScale,this.displacementBias=e.displacementBias,this.wireframe=e.wireframe,this.wireframeLinewidth=e.wireframeLinewidth,this},$.prototype={constructor:$,isBox3:!0,set:function(e,t){return this.min.copy(e),this.max.copy(t),this},setFromArray:function(e){for(var t=+Infinity,i=+Infinity,n=+Infinity,r=-Infinity,a=-Infinity,s=-Infinity,o=0,u=e.length;or&&(r=c),l>a&&(a=l),h>s&&(s=h)}this.min.set(t,i,n),this.max.set(r,a,s)},setFromBufferAttribute:function(e){for(var t=+Infinity,i=+Infinity,n=+Infinity,r=-Infinity,a=-Infinity,s=-Infinity,o=0,u=e.count;or&&(r=c),l>a&&(a=l),h>s&&(s=h)}this.min.set(t,i,n),this.max.set(r,a,s)},setFromPoints:function(e){this.makeEmpty();for(var t=0,i=e.length;tthis.max.x||e.ythis.max.y||e.zthis.max.z)},containsBox:function(e){return this.min.x<=e.min.x&&e.max.x<=this.max.x&&this.min.y<=e.min.y&&e.max.y<=this.max.y&&this.min.z<=e.min.z&&e.max.z<=this.max.z},getParameter:function(e,t){return(t||new u).set((e.x-this.min.x)/(this.max.x-this.min.x),(e.y-this.min.y)/(this.max.y-this.min.y),(e.z-this.min.z)/(this.max.z-this.min.z))},intersectsBox:function(e){return!(e.max.xthis.max.x||e.max.ythis.max.y||e.max.zthis.max.z)},intersectsSphere:function(){var e;return function(t){return e===undefined&&(e=new u),this.clampPoint(t.center,e),e.distanceToSquared(t.center)<=t.radius*t.radius}}(),intersectsPlane:function(e){var t,i;return e.normal.x>0?(t=e.normal.x*this.min.x,i=e.normal.x*this.max.x):(t=e.normal.x*this.max.x,i=e.normal.x*this.min.x),e.normal.y>0?(t+=e.normal.y*this.min.y,i+=e.normal.y*this.max.y):(t+=e.normal.y*this.max.y,i+=e.normal.y*this.min.y),e.normal.z>0?(t+=e.normal.z*this.min.z,i+=e.normal.z*this.max.z):(t+=e.normal.z*this.max.z,i+=e.normal.z*this.min.z),t<=e.constant&&i>=e.constant},clampPoint:function(e,t){return(t||new u).copy(e).clamp(this.min,this.max)},distanceToPoint:function(){var e=new u;return function(t){return e.copy(t).clamp(this.min,this.max).sub(t).length()}}(),getBoundingSphere:function(){var e=new u;return function(t){var i=t||new ee;return this.getCenter(i.center),i.radius=.5*this.getSize(e).length(),i}}(),intersect:function(e){return this.min.max(e.min),this.max.min(e.max),this.isEmpty()&&this.makeEmpty(),this},union:function(e){return this.min.min(e.min),this.max.max(e.max),this},applyMatrix4:function(){var e=[new u,new u,new u,new u,new u,new u,new u,new u];return function(t){return this.isEmpty()?this:(e[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(t),e[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(t),e[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(t),e[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(t),e[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(t),e[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(t),e[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(t),e[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(t),this.setFromPoints(e),this)}}(),translate:function(e){return this.min.add(e),this.max.add(e),this},equals:function(e){return e.min.equals(this.min)&&e.max.equals(this.max)}},ee.prototype={constructor:ee,set:function(e,t){return this.center.copy(e),this.radius=t,this},setFromPoints:function(){var e=new $;return function(t,i){var n=this.center;i!==undefined?n.copy(i):e.setFromPoints(t).getCenter(n);for(var r=0,a=0,s=t.length;athis.radius*this.radius&&(n.sub(this.center).normalize(),n.multiplyScalar(this.radius).add(this.center)),n},getBoundingBox:function(e){var t=e||new $;return t.set(this.center,this.center),t.expandByScalar(this.radius),t},applyMatrix4:function(e){return this.center.applyMatrix4(e),this.radius=this.radius*e.getMaxScaleOnAxis(),this},translate:function(e){return this.center.add(e),this},equals:function(e){return e.center.equals(this.center)&&e.radius===this.radius}},te.prototype={constructor:te,isMatrix3:!0,set:function(e,t,i,n,r,a,s,o,u){var c=this.elements;return c[0]=e,c[1]=n,c[2]=s,c[3]=t,c[4]=r,c[5]=o,c[6]=i,c[7]=a,c[8]=u,this},identity:function(){return this.set(1,0,0,0,1,0,0,0,1),this},clone:function(){return(new this.constructor).fromArray(this.elements)},copy:function(e){var t=e.elements;return this.set(t[0],t[3],t[6],t[1],t[4],t[7],t[2],t[5],t[8]),this},setFromMatrix4:function(e){var t=e.elements;return this.set(t[0],t[4],t[8],t[1],t[5],t[9],t[2],t[6],t[10]),this},applyToVector3Array:function(){var e;return function(t,i,n){e===undefined&&(e=new u),i===undefined&&(i=0),n===undefined&&(n=t.length);for(var r=0,a=i;r1?undefined:n.copy(r).multiplyScalar(s).add(t.start)}}(),intersectsLine:function(e){var t=this.distanceToPoint(e.start),i=this.distanceToPoint(e.end);return t<0&&i>0||i<0&&t>0},intersectsBox:function(e){return e.intersectsPlane(this)},intersectsSphere:function(e){return e.intersectsPlane(this)},coplanarPoint:function(e){return(e||new u).copy(this.normal).multiplyScalar(-this.constant)},applyMatrix4:function(){var e=new u,t=new te;return function(i,n){var r=this.coplanarPoint(e).applyMatrix4(i),a=n||t.getNormalMatrix(i),s=this.normal.applyMatrix3(a).normalize();return this.constant=-r.dot(s),this}}(),translate:function(e){return this.constant=this.constant-e.dot(this.normal),this},equals:function(e){return e.normal.equals(this.normal)&&e.constant===this.constant}},ne.prototype={constructor:ne,set:function(e,t,i,n,r,a){var s=this.planes;return s[0].copy(e),s[1].copy(t),s[2].copy(i),s[3].copy(n),s[4].copy(r),s[5].copy(a),this},clone:function(){return(new this.constructor).copy(this)},copy:function(e){for(var t=this.planes,i=0;i<6;i++)t[i].copy(e.planes[i]);return this},setFromMatrix:function(e){var t=this.planes,i=e.elements,n=i[0],r=i[1],a=i[2],s=i[3],o=i[4],u=i[5],c=i[6],l=i[7],h=i[8],d=i[9],p=i[10],f=i[11],g=i[12],m=i[13],M=i[14],y=i[15];return t[0].setComponents(s-n,l-o,f-h,y-g).normalize(),t[1].setComponents(s+n,l+o,f+h,y+g).normalize(),t[2].setComponents(s+r,l+u,f+d,y+m).normalize(),t[3].setComponents(s-r,l-u,f-d,y-m).normalize(),t[4].setComponents(s-a,l-c,f-p,y-M).normalize(),t[5].setComponents(s+a,l+c,f+p,y+M).normalize(),this},intersectsObject:function(){var e=new ee;return function(t){var i=t.geometry;return null===i.boundingSphere&&i.computeBoundingSphere(),e.copy(i.boundingSphere).applyMatrix4(t.matrixWorld),this.intersectsSphere(e)}}(),intersectsSprite:function(){var e=new ee;return function(t){return e.center.set(0,0,0),e.radius=.7071067811865476,e.applyMatrix4(t.matrixWorld),this.intersectsSphere(e)}}(),intersectsSphere:function(e){for(var t=this.planes,i=e.center,n=-e.radius,r=0;r<6;r++)if(t[r].distanceToPoint(i)0?i.min.x:i.max.x,t.x=a.normal.x>0?i.max.x:i.min.x,e.y=a.normal.y>0?i.min.y:i.max.y,t.y=a.normal.y>0?i.max.y:i.min.y,e.z=a.normal.z>0?i.min.z:i.max.z,t.z=a.normal.z>0?i.max.z:i.min.z;var s=a.distanceToPoint(e),o=a.distanceToPoint(t);if(s<0&&o<0)return!1}return!0}}(),containsPoint:function(e){for(var t=this.planes,i=0;i<6;i++)if(t[i].distanceToPoint(e)<0)return!1;return!0}},ae.prototype={constructor:ae,set:function(e,t){return this.origin.copy(e),this.direction.copy(t),this},clone:function(){return(new this.constructor).copy(this)},copy:function(e){return this.origin.copy(e.origin),this.direction.copy(e.direction),this},at:function(e,t){return(t||new u).copy(this.direction).multiplyScalar(e).add(this.origin)},lookAt:function(e){return this.direction.copy(e).sub(this.origin).normalize(),this},recast:function(){var e=new u;return function(t){return this.origin.copy(this.at(t,e)),this}}(),closestPointToPoint:function(e,t){var i=t||new u;i.subVectors(e,this.origin);var n=i.dot(this.direction);return n<0?i.copy(this.origin):i.copy(this.direction).multiplyScalar(n).add(this.origin)},distanceToPoint:function(e){return Math.sqrt(this.distanceSqToPoint(e))},distanceSqToPoint:function(){var e=new u;return function(t){var i=e.subVectors(t,this.origin).dot(this.direction);return i<0?this.origin.distanceToSquared(t):(e.copy(this.direction).multiplyScalar(i).add(this.origin),e.distanceToSquared(t))}}(),distanceSqToSegment:function(){var e=new u,t=new u,i=new u;return function(n,r,a,s){e.copy(n).add(r).multiplyScalar(.5),t.copy(r).sub(n).normalize(),i.copy(this.origin).sub(e);var o,u,c,l,h=.5*n.distanceTo(r),d=-this.direction.dot(t),p=i.dot(this.direction),f=-i.dot(t),g=i.lengthSq(),m=Math.abs(1-d*d);if(m>0)if(o=d*f-p,u=d*p-f,l=h*m,o>=0)if(u>=-l)if(u<=l){var M=1/m;c=(o*=M)*(o+d*(u*=M)+2*p)+u*(d*o+u+2*f)+g}else u=h,c=-(o=Math.max(0,-(d*u+p)))*o+u*(u+2*f)+g;else u=-h,c=-(o=Math.max(0,-(d*u+p)))*o+u*(u+2*f)+g;else u<=-l?c=-(o=Math.max(0,-(-d*h+p)))*o+(u=o>0?-h:Math.min(Math.max(-h,-f),h))*(u+2*f)+g:u<=l?(o=0,c=(u=Math.min(Math.max(-h,-f),h))*(u+2*f)+g):c=-(o=Math.max(0,-(d*h+p)))*o+(u=o>0?h:Math.min(Math.max(-h,-f),h))*(u+2*f)+g;else u=d>0?-h:h,c=-(o=Math.max(0,-(d*u+p)))*o+u*(u+2*f)+g;return a&&a.copy(this.direction).multiplyScalar(o).add(this.origin),s&&s.copy(t).multiplyScalar(u).add(e),c}}(),intersectSphere:function(){var e=new u;return function(t,i){e.subVectors(t.center,this.origin);var n=e.dot(this.direction),r=e.dot(e)-n*n,a=t.radius*t.radius;if(r>a)return null;var s=Math.sqrt(a-r),o=n-s,u=n+s;return o<0&&u<0?null:o<0?this.at(u,i):this.at(o,i)}}(),intersectsSphere:function(e){return this.distanceToPoint(e.center)<=e.radius},distanceToPlane:function(e){var t=e.normal.dot(this.direction);if(0===t)return 0===e.distanceToPoint(this.origin)?0:null;var i=-(this.origin.dot(e.normal)+e.constant)/t;return i>=0?i:null},intersectPlane:function(e,t){var i=this.distanceToPlane(e);return null===i?null:this.at(i,t)},intersectsPlane:function(e){var t=e.distanceToPoint(this.origin);return 0===t||e.normal.dot(this.direction)*t<0},intersectBox:function(e,t){var i,n,r,a,s,o,u=1/this.direction.x,c=1/this.direction.y,l=1/this.direction.z,h=this.origin;return u>=0?(i=(e.min.x-h.x)*u,n=(e.max.x-h.x)*u):(i=(e.max.x-h.x)*u,n=(e.min.x-h.x)*u),c>=0?(r=(e.min.y-h.y)*c,a=(e.max.y-h.y)*c):(r=(e.max.y-h.y)*c,a=(e.min.y-h.y)*c),i>a||r>n?null:((r>i||i!==i)&&(i=r),(a=0?(s=(e.min.z-h.z)*l,o=(e.max.z-h.z)*l):(s=(e.max.z-h.z)*l,o=(e.min.z-h.z)*l),i>o||s>n?null:((s>i||i!==i)&&(i=s),(o=0?i:n,t)))},intersectsBox:function(){var e=new u;return function(t){return null!==this.intersectBox(t,e)}}(),intersectTriangle:function(){var e=new u,t=new u,i=new u,n=new u;return function(r,a,s,o,u){t.subVectors(a,r),i.subVectors(s,r),n.crossVectors(t,i);var c,l=this.direction.dot(n);if(l>0){if(o)return null;c=1}else{if(!(l<0))return null;c=-1,l=-l}e.subVectors(this.origin,r);var h=c*this.direction.dot(i.crossVectors(e,i));if(h<0)return null;var d=c*this.direction.dot(t.cross(e));if(d<0)return null;if(h+d>l)return null;var p=-c*e.dot(n);return p<0?null:this.at(p/l,u)}}(),applyMatrix4:function(e){return this.direction.add(this.origin).applyMatrix4(e),this.origin.applyMatrix4(e),this.direction.sub(this.origin),this.direction.normalize(),this},equals:function(e){return e.origin.equals(this.origin)&&e.direction.equals(this.direction)}},se.RotationOrders=["XYZ","YZX","ZXY","XZY","YXZ","ZYX"],se.DefaultOrder="XYZ",se.prototype={constructor:se,isEuler:!0,get x(){return this._x},set x(e){this._x=e,this.onChangeCallback()},get y(){return this._y},set y(e){this._y=e,this.onChangeCallback()},get z(){return this._z},set z(e){this._z=e,this.onChangeCallback()},get order(){return this._order},set order(e){this._order=e,this.onChangeCallback()},set:function(e,t,i,n){return this._x=e,this._y=t,this._z=i,this._order=n||this._order,this.onChangeCallback(),this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._order)},copy:function(e){return this._x=e._x,this._y=e._y,this._z=e._z,this._order=e._order,this.onChangeCallback(),this},setFromRotationMatrix:function(e,t,i){var n=ds.clamp,r=e.elements,a=r[0],s=r[4],o=r[8],u=r[1],c=r[5],l=r[9],h=r[2],d=r[6],p=r[10];return"XYZ"===(t=t||this._order)?(this._y=Math.asin(n(o,-1,1)),Math.abs(o)<.99999?(this._x=Math.atan2(-l,p),this._z=Math.atan2(-s,a)):(this._x=Math.atan2(d,c),this._z=0)):"YXZ"===t?(this._x=Math.asin(-n(l,-1,1)),Math.abs(l)<.99999?(this._y=Math.atan2(o,p),this._z=Math.atan2(u,c)):(this._y=Math.atan2(-h,a),this._z=0)):"ZXY"===t?(this._x=Math.asin(n(d,-1,1)),Math.abs(d)<.99999?(this._y=Math.atan2(-h,p),this._z=Math.atan2(-s,c)):(this._y=0,this._z=Math.atan2(u,a))):"ZYX"===t?(this._y=Math.asin(-n(h,-1,1)),Math.abs(h)<.99999?(this._x=Math.atan2(d,p),this._z=Math.atan2(u,a)):(this._x=0,this._z=Math.atan2(-s,c))):"YZX"===t?(this._z=Math.asin(n(u,-1,1)),Math.abs(u)<.99999?(this._x=Math.atan2(-l,c),this._y=Math.atan2(-h,a)):(this._x=0,this._y=Math.atan2(o,p))):"XZY"===t?(this._z=Math.asin(-n(s,-1,1)),Math.abs(s)<.99999?(this._x=Math.atan2(d,c),this._y=Math.atan2(o,a)):(this._x=Math.atan2(-l,p),this._y=0)):console.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+t),this._order=t,!1!==i&&this.onChangeCallback(),this},setFromQuaternion:function(){var e;return function(t,i,n){return e===undefined&&(e=new c),e.makeRotationFromQuaternion(t),this.setFromRotationMatrix(e,i,n)}}(),setFromVector3:function(e,t){return this.set(e.x,e.y,e.z,t||this._order)},reorder:function(){var e=new o;return function(t){return e.setFromEuler(this),this.setFromQuaternion(e,t)}}(),equals:function(e){return e._x===this._x&&e._y===this._y&&e._z===this._z&&e._order===this._order},fromArray:function(e){return this._x=e[0],this._y=e[1],this._z=e[2],e[3]!==undefined&&(this._order=e[3]),this.onChangeCallback(),this},toArray:function(e,t){return e===undefined&&(e=[]),t===undefined&&(t=0),e[t]=this._x,e[t+1]=this._y,e[t+2]=this._z,e[t+3]=this._order,e},toVector3:function(e){return e?e.set(this._x,this._y,this._z):new u(this._x,this._y,this._z)},onChange:function(e){return this.onChangeCallback=e,this},onChangeCallback:function(){}},oe.prototype={constructor:oe,set:function(e){this.mask=1<1){for(var t=0;t1)for(var t=0;t0){r.children=[];for(var a=0;a0&&(n.geometries=s),o.length>0&&(n.materials=o),u.length>0&&(n.textures=u),c.length>0&&(n.images=c)}return n.object=r,n},clone:function(e){return(new this.constructor).copy(this,e)},copy:function(e,t){if(t===undefined&&(t=!0),this.name=e.name,this.up.copy(e.up),this.position.copy(e.position),this.quaternion.copy(e.quaternion),this.scale.copy(e.scale),this.matrix.copy(e.matrix),this.matrixWorld.copy(e.matrixWorld),this.matrixAutoUpdate=e.matrixAutoUpdate,this.matrixWorldNeedsUpdate=e.matrixWorldNeedsUpdate,this.layers.mask=e.layers.mask,this.visible=e.visible,this.castShadow=e.castShadow,this.receiveShadow=e.receiveShadow,this.frustumCulled=e.frustumCulled,this.renderOrder=e.renderOrder,this.userData=JSON.parse(JSON.stringify(e.userData)),!0===t)for(var i=0;i0?a.multiplyScalar(1/Math.sqrt(s)):a.set(0,0,0)}}(),le.barycoordFromPoint=function(){var e=new u,t=new u,i=new u;return function(n,r,a,s,o){e.subVectors(s,r),t.subVectors(a,r),i.subVectors(n,r);var c=e.dot(e),l=e.dot(t),h=e.dot(i),d=t.dot(t),p=t.dot(i),f=c*d-l*l,g=o||new u;if(0===f)return g.set(-2,-1,-1);var m=1/f,M=(d*h-l*p)*m,y=(c*p-l*h)*m;return g.set(1-M-y,y,M)}}(),le.containsPoint=function(){var e=new u;return function(t,i,n,r){var a=le.barycoordFromPoint(t,i,n,r,e);return a.x>=0&&a.y>=0&&a.x+a.y<=1}}(),le.prototype={constructor:le,set:function(e,t,i){return this.a.copy(e),this.b.copy(t),this.c.copy(i),this},setFromPointsAndIndices:function(e,t,i,n){return this.a.copy(e[t]),this.b.copy(e[i]),this.c.copy(e[n]),this},clone:function(){return(new this.constructor).copy(this)},copy:function(e){return this.a.copy(e.a),this.b.copy(e.b),this.c.copy(e.c),this},area:function(){var e=new u,t=new u;return function(){return e.subVectors(this.c,this.b),t.subVectors(this.a,this.b),.5*e.cross(t).length()}}(),midpoint:function(e){return(e||new u).addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},normal:function(e){return le.normal(this.a,this.b,this.c,e)},plane:function(e){return(e||new ie).setFromCoplanarPoints(this.a,this.b,this.c)},barycoordFromPoint:function(e,t){return le.barycoordFromPoint(e,this.a,this.b,this.c,t)},containsPoint:function(e){return le.containsPoint(e,this.a,this.b,this.c)},closestPointToPoint:function(){var e,t,i,n;return function(r,a){e===undefined&&(e=new ie,t=[new ce,new ce,new ce],i=new u,n=new u);var s=a||new u,o=Infinity;if(e.setFromCoplanarPoints(this.a,this.b,this.c),e.projectPoint(r,i),!0===this.containsPoint(i))s.copy(i);else{t[0].set(this.a,this.b),t[1].set(this.b,this.c),t[2].set(this.c,this.a);for(var c=0;c0,o=a[1]&&a[1].length>0,u=e.morphTargets,c=u.length;if(c>0){t=[];for(M=0;M0){l=[];for(M=0;M0)for(g=0;g0&&(this.normalsNeedUpdate=!0)},computeFlatVertexNormals:function(){var e,t,i;for(this.computeFaceNormals(),e=0,t=this.faces.length;e0&&(this.normalsNeedUpdate=!0)},computeMorphNormals:function(){var e,t,i,n,r;for(i=0,n=this.faces.length;i0&&(e+=t[i].distanceTo(t[i-1])),this.lineDistances[i]=e},computeBoundingBox:function(){null===this.boundingBox&&(this.boundingBox=new $),this.boundingBox.setFromPoints(this.vertices)},computeBoundingSphere:function(){null===this.boundingSphere&&(this.boundingSphere=new ee),this.boundingSphere.setFromPoints(this.vertices)},merge:function(e,t,i){if(!1!==(e&&e.isGeometry)){var n,r=this.vertices.length,a=this.vertices,s=e.vertices,o=this.faces,u=e.faces,c=this.faceVertexUvs[0],l=e.faceVertexUvs[0],h=this.colors,d=e.colors;i===undefined&&(i=0),t!==undefined&&(n=(new te).getNormalMatrix(t));for(var p=0,f=s.length;p=0;i--){var f=d[i];for(this.faces.splice(f,1),s=0,o=this.faceVertexUvs.length;s0,w=M.vertexNormals.length>0,x=1!==M.color.r||1!==M.color.g||1!==M.color.b,T=M.vertexColors.length>0,E=0;if(E=e(E,0,0),E=e(E,1,!0),E=e(E,2,!1),E=e(E,3,y),E=e(E,4,v),E=e(E,5,w),E=e(E,6,x),E=e(E,7,T),l.push(E),l.push(M.a,M.b,M.c),l.push(M.materialIndex),y){var N=this.faceVertexUvs[0][u];l.push(n(N[0]),n(N[1]),n(N[2]))}if(v&&l.push(t(M.normal)),w){var L=M.vertexNormals;l.push(t(L[0]),t(L[1]),t(L[2]))}if(x&&l.push(i(M.color)),T){var D=M.vertexColors;l.push(i(D[0]),i(D[1]),i(D[2]))}}return r.data={},r.data.vertices=o,r.data.normals=h,p.length>0&&(r.data.colors=p),g.length>0&&(r.data.uvs=[g]),r.data.faces=l,r},clone:function(){return(new Ne).copy(this)},copy:function(e){this.vertices=[],this.faces=[],this.faceVertexUvs=[[]],this.colors=[];for(var t=e.vertices,i=0,n=t.length;i0){var i=new Float32Array(3*e.normals.length);this.addAttribute("normal",new pe(i,3).copyVector3sArray(e.normals))}if(e.colors.length>0){var n=new Float32Array(3*e.colors.length);this.addAttribute("color",new pe(n,3).copyColorsArray(e.colors))}if(e.uvs.length>0){var r=new Float32Array(2*e.uvs.length);this.addAttribute("uv",new pe(r,2).copyVector2sArray(e.uvs))}if(e.uvs2.length>0){var a=new Float32Array(2*e.uvs2.length);this.addAttribute("uv2",new pe(a,2).copyVector2sArray(e.uvs2))}if(e.indices.length>0){var s=new(e.vertices.length>65535?Uint32Array:Uint16Array)(3*e.indices.length);this.setIndex(new pe(s,1).copyIndicesArray(e.indices))}this.groups=e.groups;for(var o in e.morphTargets){for(var u=[],c=e.morphTargets[o],l=0,h=c.length;l0){var f=new xe(4*e.skinIndices.length,4);this.addAttribute("skinIndex",f.copyVector4sArray(e.skinIndices))}if(e.skinWeights.length>0){var g=new xe(4*e.skinWeights.length,4);this.addAttribute("skinWeight",g.copyVector4sArray(e.skinWeights))}return null!==e.boundingSphere&&(this.boundingSphere=e.boundingSphere.clone()),null!==e.boundingBox&&(this.boundingBox=e.boundingBox.clone()),this},computeBoundingBox:function(){null===this.boundingBox&&(this.boundingBox=new $);var e=this.attributes.position;e!==undefined?this.boundingBox.setFromBufferAttribute(e):this.boundingBox.makeEmpty(),(isNaN(this.boundingBox.min.x)||isNaN(this.boundingBox.min.y)||isNaN(this.boundingBox.min.z))&&console.error('THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.',this)},computeBoundingSphere:function(){var e=new $,t=new u;return function(){null===this.boundingSphere&&(this.boundingSphere=new ee);var i=this.attributes.position;if(i){var n=this.boundingSphere.center;e.setFromBufferAttribute(i),e.getCenter(n);for(var r=0,a=0,s=i.count;a0&&(e.data.groups=JSON.parse(JSON.stringify(o)));var u=this.boundingSphere;return null!==u&&(e.data.boundingSphere={center:u.center.toArray(),radius:u.radius}),e},clone:function(){return(new De).copy(this)},copy:function(e){var t=e.index;null!==t&&this.setIndex(t.clone());var i=e.attributes;for(var n in i){var r=i[n];this.addAttribute(n,r.clone())}for(var a=e.groups,s=0,o=a.length;s0){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(var t=0,i=e.length;tt.far?null:{distance:u,point:w.clone(),object:e}}function n(i,n,r,a,s,u,c,d){o.fromArray(a,3*u),l.fromArray(a,3*c),h.fromArray(a,3*d);var p=t(i,n,r,o,l,h,v);return p&&(s&&(g.fromArray(s,2*u),m.fromArray(s,2*c),M.fromArray(s,2*d),p.uv=e(v,o,l,h,g,m,M)),p.face=new he(u,c,d,le.normal(o,l,h)),p.faceIndex=u),p}var r=new c,a=new ae,s=new ee,o=new u,l=new u,h=new u,d=new u,p=new u,f=new u,g=new i,m=new i,M=new i,y=new u,v=new u,w=new u;return function(i,u){var c=this.geometry,y=this.material,w=this.matrixWorld;if(y!==undefined&&(null===c.boundingSphere&&c.computeBoundingSphere(),s.copy(c.boundingSphere),s.applyMatrix4(w),!1!==i.ray.intersectsSphere(s)&&(r.getInverse(w),a.copy(i.ray).applyMatrix4(r),null===c.boundingBox||!1!==a.intersectsBox(c.boundingBox)))){var x,T;if(c.isBufferGeometry){var E,N,L,D=c.index,A=c.attributes,I=A.position.array;if(A.uv!==undefined&&(x=A.uv.array),null!==D)for(var b=D.array,S=0,_=b.length;S<_;S+=3)E=b[S],N=b[S+1],L=b[S+2],(T=n(this,i,a,I,x,E,N,L))&&(T.faceIndex=Math.floor(S/3),u.push(T));else for(var S=0,_=I.length;S<_;S+=9)(T=n(this,i,a,I,x,E=S/3,N=E+1,L=E+2))&&(T.index=E,u.push(T))}else if(c.isGeometry){var j,C,z,O=y&&y.isMultiMaterial,R=!0===O?y.materials:null,U=c.vertices,P=c.faces,k=c.faceVertexUvs[0];k.length>0&&(x=k);for(var B=0,F=P.length;Bthis.scale.x*this.scale.y/4||i.push({distance:Math.sqrt(n),point:this.position,face:null,object:this})}}(),clone:function(){return new this.constructor(this.material).copy(this)}}),dt.prototype=Object.assign(Object.create(ue.prototype),{constructor:dt,copy:function(e){ue.prototype.copy.call(this,e,!1);for(var t=e.levels,i=0,n=t.length;i1){e.setFromMatrixPosition(i.matrixWorld),t.setFromMatrixPosition(this.matrixWorld);var r=e.distanceTo(t);n[0].object.visible=!0;for(var a=1,s=n.length;a=n[a].distance;a++)n[a-1].object.visible=!1,n[a].object.visible=!0;for(;as||(p.applyMatrix4(this.matrixWorld),(L=n.ray.origin.distanceTo(p))n.far||r.push({distance:L,point:d.clone().applyMatrix4(this.matrixWorld),index:y,face:null,faceIndex:null,object:this}))}else for(var y=0,v=m.length/3-1;ys||(p.applyMatrix4(this.matrixWorld),(L=n.ray.origin.distanceTo(p))n.far||r.push({distance:L,point:d.clone().applyMatrix4(this.matrixWorld),index:y,face:null,faceIndex:null,object:this}))}else if(o.isGeometry)for(var T=o.vertices,E=T.length,y=0;ys)){p.applyMatrix4(this.matrixWorld);var L=n.ray.origin.distanceTo(p);Ln.far||r.push({distance:L,point:d.clone().applyMatrix4(this.matrixWorld),index:y,face:null,faceIndex:null,object:this})}}}}}(),clone:function(){return new this.constructor(this.geometry,this.material).copy(this)}}),yt.prototype=Object.assign(Object.create(Mt.prototype),{constructor:yt,isLineSegments:!0}),vt.prototype=Object.create(q.prototype),vt.prototype.constructor=vt,vt.prototype.isPointsMaterial=!0,vt.prototype.copy=function(e){return q.prototype.copy.call(this,e),this.color.copy(e.color),this.map=e.map,this.size=e.size,this.sizeAttenuation=e.sizeAttenuation,this},wt.prototype=Object.assign(Object.create(ue.prototype),{constructor:wt,isPoints:!0,raycast:function(){var e=new c,t=new ae,i=new ee;return function(n,r){function a(e,i){var a=t.distanceSqToPoint(e);if(an.far)return;r.push({distance:u,distanceToRay:Math.sqrt(a),point:o.clone(),index:i,face:null,object:s})}}var s=this,o=this.geometry,c=this.matrixWorld,l=n.params.Points.threshold;if(null===o.boundingSphere&&o.computeBoundingSphere(),i.copy(o.boundingSphere),i.applyMatrix4(c),!1!==n.ray.intersectsSphere(i)){e.getInverse(c),t.copy(n.ray).applyMatrix4(e);var h=l/((this.scale.x+this.scale.y+this.scale.z)/3),d=h*h,p=new u;if(o.isBufferGeometry){var f=o.index,g=o.attributes.position.array;if(null!==f)for(var m=f.array,M=0,y=m.length;M=-Number.EPSILON&&I>=-Number.EPSILON&&A>=-Number.EPSILON))return!1;return!0}return function(t,i){var n=t.length;if(n<3)return null;var r,a,s,o=[],u=[],c=[];if(Is.area(t)>0)for(a=0;a2;){if(h--<=0)return console.warn("THREE.ShapeUtils: Unable to triangulate polygon! in triangulate()"),i?c:o;if(r=a,l<=r&&(r=0),a=r+1,l<=a&&(a=0),s=a+1,l<=s&&(s=0),e(t,r,a,s,l,u)){var d,p,f,g,m;for(d=u[r],p=u[a],f=u[s],o.push([t[d],t[p],t[f]]),c.push([u[r],u[a],u[s]]),g=a,m=a+1;m2&&e[t-1].equals(e[0])&&e.pop()}function n(e,t,i){return e.x!==t.x?e.xNumber.EPSILON){var f;if(d>0){if(p<0||p>d)return[];if((f=c*l-u*h)<0||f>d)return[]}else{if(p>0||p0||fL?[]:w===L?a?[]:[y]:x<=L?[y,v]:[y,E]}function a(e,t,i,n){var r=t.x-e.x,a=t.y-e.y,s=i.x-e.x,o=i.y-e.y,u=n.x-e.x,c=n.y-e.y,l=r*o-a*s,h=r*c-a*u;if(Math.abs(l)>Number.EPSILON){var d=u*o-c*s;return l>0?h>=0&&d>=0:h>=0||d>=0}return h>0}i(e),t.forEach(i);for(var s,o,u,c,l,h,d={},p=e.concat(),f=0,g=t.length;f0;){if(--x<0){console.log("Infinite Loop! Holes left:"+m.length+", Probably Hole outside Shape!");break}for(s=w;sn&&(s=0);var o=a(g[e],g[r],g[s],i[t]);if(!o)return!1;var u=i.length-1,c=t-1;c<0&&(c=u);var l=t+1;return l>u&&(l=0),!!(o=a(i[t],i[c],i[l],g[e]))}(s,T)&&!function(e,t){var i,n;for(i=0;i0)return!0;return!1}(o,u)&&!function(e,i){var n,a,s,o;for(n=0;n0)return!0;return!1}(o,u)){n=T,m.splice(y,1),h=g.slice(0,s+1),d=g.slice(s),p=i.slice(n),f=i.slice(0,n+1),g=h.concat(p).concat(f).concat(d),w=s;break}if(n>=0)break;M[l]=!0}if(n>=0)break}}return g}(e,t),M=Is.triangulate(m,!1);for(s=0,o=M.length;sNumber.EPSILON){var p=Math.sqrt(h),f=Math.sqrt(c*c+l*l),g=t.x-u/p,m=t.y+o/p,M=((n.x-l/f-g)*l-(n.y+c/f-m)*c)/(o*l-u*c),y=(r=g+o*M-e.x)*r+(a=m+u*M-e.y)*a;if(y<=2)return new i(r,a);s=Math.sqrt(y/2)}else{var v=!1;o>Number.EPSILON?c>Number.EPSILON&&(v=!0):o<-Number.EPSILON?c<-Number.EPSILON&&(v=!0):Math.sign(u)===Math.sign(l)&&(v=!0),v?(r=-u,a=o,s=Math.sqrt(h)):(r=o,a=u,s=Math.sqrt(h/2))}return new i(r/s,a/s)}function a(e,t){var i,n;for(V=e.length;--V>=0;){i=V,(n=V-1)<0&&(n=e.length-1);var r=0,a=x+2*y;for(r=0;r=0;R--){for(P=R/y,k=m*Math.cos(P*Math.PI/2),U=M*Math.sin(P*Math.PI/2),V=0,H=O.length;V0||0===e.search(/^data\:image\/jpeg/);a.format=n?Ra:Ua,a.image=i,a.needsUpdate=!0,t!==undefined&&t(a)},i,r),a},setCrossOrigin:function(e){return this.crossOrigin=e,this},setWithCredentials:function(e){return this.withCredentials=e,this},setPath:function(e){return this.path=e,this}}),Ai.prototype=Object.assign(Object.create(ue.prototype),{constructor:Ai,isLight:!0,copy:function(e){return ue.prototype.copy.call(this,e),this.color.copy(e.color),this.intensity=e.intensity,this},toJSON:function(e){var t=ue.prototype.toJSON.call(this,e);return t.object.color=this.color.getHex(),t.object.intensity=this.intensity,this.groundColor!==undefined&&(t.object.groundColor=this.groundColor.getHex()),this.distance!==undefined&&(t.object.distance=this.distance),this.angle!==undefined&&(t.object.angle=this.angle),this.decay!==undefined&&(t.object.decay=this.decay),this.penumbra!==undefined&&(t.object.penumbra=this.penumbra),this.shadow!==undefined&&(t.object.shadow=this.shadow.toJSON()),t}}),Ii.prototype=Object.assign(Object.create(Ai.prototype),{constructor:Ii,isHemisphereLight:!0,copy:function(e){return Ai.prototype.copy.call(this,e),this.groundColor.copy(e.groundColor),this}}),Object.assign(bi.prototype,{copy:function(e){return this.camera=e.camera.clone(),this.bias=e.bias,this.radius=e.radius,this.mapSize.copy(e.mapSize),this},clone:function(){return(new this.constructor).copy(this)},toJSON:function(){var e={};return 0!==this.bias&&(e.bias=this.bias),1!==this.radius&&(e.radius=this.radius),512===this.mapSize.x&&512===this.mapSize.y||(e.mapSize=this.mapSize.toArray()),e.camera=this.camera.toJSON(!1).object,delete e.camera.matrix,e}}),Si.prototype=Object.assign(Object.create(bi.prototype),{constructor:Si,isSpotLightShadow:!0,update:function(e){var t=2*ds.RAD2DEG*e.angle,i=this.mapSize.width/this.mapSize.height,n=e.distance||500,r=this.camera;t===r.fov&&i===r.aspect&&n===r.far||(r.fov=t,r.aspect=i,r.far=n,r.updateProjectionMatrix())}}),_i.prototype=Object.assign(Object.create(Ai.prototype),{constructor:_i,isSpotLight:!0,copy:function(e){return Ai.prototype.copy.call(this,e),this.distance=e.distance,this.angle=e.angle,this.penumbra=e.penumbra,this.decay=e.decay,this.target=e.target.clone(),this.shadow=e.shadow.clone(),this}}),ji.prototype=Object.assign(Object.create(Ai.prototype),{constructor:ji,isPointLight:!0,copy:function(e){return Ai.prototype.copy.call(this,e),this.distance=e.distance,this.decay=e.decay,this.shadow=e.shadow.clone(),this}}),Ci.prototype=Object.assign(Object.create(bi.prototype),{constructor:Ci}),zi.prototype=Object.assign(Object.create(Ai.prototype),{constructor:zi,isDirectionalLight:!0,copy:function(e){return Ai.prototype.copy.call(this,e),this.target=e.target.clone(),this.shadow=e.shadow.clone(),this}}),Oi.prototype=Object.assign(Object.create(Ai.prototype),{constructor:Oi,isAmbientLight:!0});var zs={arraySlice:function(e,t,i){return zs.isTypedArray(e)?new e.constructor(e.subarray(t,i)):e.slice(t,i)},convertArray:function(e,t,i){return!e||!i&&e.constructor===t?e:"number"==typeof t.BYTES_PER_ELEMENT?new t(e):Array.prototype.slice.call(e)},isTypedArray:function(e){return ArrayBuffer.isView(e)&&!(e instanceof DataView)},getKeyframeOrder:function(e){for(var t=e.length,i=new Array(t),n=0;n!==t;++n)i[n]=n;return i.sort(function(t,i){return e[t]-e[i]}),i},sortedArray:function(e,t,i){for(var n=e.length,r=new e.constructor(n),a=0,s=0;s!==n;++a)for(var o=i[a]*t,u=0;u!==t;++u)r[s++]=e[o+u];return r},flattenJSON:function(e,t,i,n){for(var r=1,a=e[0];a!==undefined&&a[n]===undefined;)a=e[r++];if(a!==undefined){var s=a[n];if(s!==undefined)if(Array.isArray(s)){do{(s=a[n])!==undefined&&(t.push(a.time),i.push.apply(i,s)),a=e[r++]}while(a!==undefined)}else if(s.toArray!==undefined){do{(s=a[n])!==undefined&&(t.push(a.time),s.toArray(i,i.length)),a=e[r++]}while(a!==undefined)}else do{(s=a[n])!==undefined&&(t.push(a.time),i.push(s)),a=e[r++]}while(a!==undefined)}}};Ri.prototype={constructor:Ri,evaluate:function(e){var t=this.parameterPositions,i=this._cachedIndex,n=t[i],r=t[i-1];e:{t:{var a;i:{n:if(!(e=r)break e;var s=t[1];e=r)break t}a=i,i=0}}for(;i>>1;et;)--a;if(++a,0!==r||a!==n){r>=a&&(a=Math.max(a,1),r=a-1);var s=this.getValueSize();this.times=zs.arraySlice(i,r,a),this.values=zs.arraySlice(this.values,r*s,a*s)}return this},validate:function(){var e=!0,t=this.getValueSize();t-Math.floor(t)!=0&&(console.error("invalid value size in track",this),e=!1);var i=this.times,n=this.values,r=i.length;0===r&&(console.error("track is empty",this),e=!1);for(var a=null,s=0;s!==r;s++){var o=i[s];if("number"==typeof o&&isNaN(o)){console.error("time is not a valid number",this,s,o),e=!1;break}if(null!==a&&a>o){console.error("out of order keys",this,s,o,a),e=!1;break}a=o}if(n!==undefined&&zs.isTypedArray(n))for(var s=0,u=n.length;s!==u;++s){var c=n[s];if(isNaN(c)){console.error("value is not a valid number",this,s,c),e=!1;break}}return e},optimize:function(){for(var e=this.times,t=this.values,i=this.getValueSize(),n=2302===this.getInterpolation(),r=1,a=e.length-1,s=1;s0){e[r]=e[a];for(var f=a*i,g=r*i,d=0;d!==i;++d)t[g+d]=t[f+d];++r}return r!==e.length&&(this.times=zs.arraySlice(e,0,r),this.values=zs.arraySlice(t,0,r*i)),this}},Fi.prototype=Object.assign(Object.create(Os),{constructor:Fi,ValueTypeName:"vector"}),Gi.prototype=Object.assign(Object.create(Ri.prototype),{constructor:Gi,interpolate_:function(e,t,i,n){for(var r=this.resultBuffer,a=this.sampleValues,s=this.valueSize,u=e*s,c=(i-t)/(n-t),l=u+s;u!==l;u+=4)o.slerpFlat(r,0,a,u-s,a,u,c);return r}}),Qi.prototype=Object.assign(Object.create(Os),{constructor:Qi,ValueTypeName:"quaternion",DefaultInterpolation:2301,InterpolantFactoryMethodLinear:function(e){return new Gi(this.times,this.values,this.getValueSize(),e)},InterpolantFactoryMethodSmooth:undefined}),Yi.prototype=Object.assign(Object.create(Os),{constructor:Yi,ValueTypeName:"number"}),Vi.prototype=Object.assign(Object.create(Os),{constructor:Vi,ValueTypeName:"string",ValueBufferType:Array,DefaultInterpolation:2300,InterpolantFactoryMethodLinear:undefined,InterpolantFactoryMethodSmooth:undefined}),Hi.prototype=Object.assign(Object.create(Os),{constructor:Hi,ValueTypeName:"bool",ValueBufferType:Array,DefaultInterpolation:2300,InterpolantFactoryMethodLinear:undefined,InterpolantFactoryMethodSmooth:undefined}),Wi.prototype=Object.assign(Object.create(Os),{constructor:Wi,ValueTypeName:"color"}),Xi.prototype=Os,Os.constructor=Xi,Object.assign(Xi,{parse:function(e){if(e.type===undefined)throw new Error("track type undefined, can not parse");var t=Xi._getTrackTypeForValueTypeName(e.type);if(e.times===undefined){var i=[],n=[];zs.flattenJSON(e.keys,i,n,"value"),e.times=i,e.values=n}return t.parse!==undefined?t.parse(e):new t(e.name,e.times,e.values,e.interpolation)},toJSON:function(e){var t,i=e.constructor;if(i.toJSON!==undefined)t=i.toJSON(e);else{t={name:e.name,times:zs.convertArray(e.times,Array),values:zs.convertArray(e.values,Array)};var n=e.getInterpolation();n!==e.DefaultInterpolation&&(t.interpolation=n)}return t.type=e.ValueTypeName,t},_getTrackTypeForValueTypeName:function(e){switch(e.toLowerCase()){case"scalar":case"double":case"float":case"number":case"integer":return Yi;case"vector":case"vector2":case"vector3":case"vector4":return Fi;case"color":return Wi;case"quaternion":return Qi;case"bool":case"boolean":return Hi;case"string":return Vi}throw new Error("Unsupported typeName: "+e)}}),Zi.prototype={constructor:Zi,resetDuration:function(){for(var e=0,t=0,i=this.tracks.length;t!==i;++t){var n=this.tracks[t];e=Math.max(e,n.times[n.times.length-1])}this.duration=e},trim:function(){for(var e=0;e1){var c=n[h=u[1]];c||(n[h]=c=[]),c.push(o)}}var l=[];for(var h in n)l.push(Zi.CreateFromMorphTargetSequence(h,n[h],t,i));return l},parseAnimation:function(e,t){if(!e)return console.error(" no animation in JSONLoader data"),null;for(var i=function(e,t,i,n,r){if(0!==i.length){var a=[],s=[];zs.flattenJSON(i,a,s,n),0!==a.length&&r.push(new e(t,a,s))}},n=[],r=e.name||"default",a=e.length||-1,s=e.fps||30,o=e.hierarchy||[],u=0;u1?e.skinWeights[i+1]:0,u=t>2?e.skinWeights[i+2]:0,c=t>3?e.skinWeights[i+3]:0;n.skinWeights.push(new r(s,o,u,c))}if(e.skinIndices)for(var i=0,a=e.skinIndices.length;i1?e.skinIndices[i+1]:0,d=t>2?e.skinIndices[i+2]:0,p=t>3?e.skinIndices[i+3]:0;n.skinIndices.push(new r(l,h,d,p))}n.bones=e.bones,n.bones&&n.bones.length>0&&(n.skinWeights.length!==n.skinIndices.length||n.skinIndices.length!==n.vertices.length)&&console.warn("When skinning, number of vertices ("+n.vertices.length+"), skinIndices ("+n.skinIndices.length+"), and skinWeights ("+n.skinWeights.length+") should match.")}(),function(t){if(e.morphTargets!==undefined)for(var i=0,r=e.morphTargets.length;i0){console.warn('THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.');for(var h=n.faces,d=e.morphColors[0].colors,i=0,r=h.length;i0&&(n.animations=t)}(),n.computeFaceNormals(),n.computeBoundingSphere(),e.materials===undefined||0===e.materials.length)return{geometry:n};var s=Ki.prototype.initMaterials(e.materials,t,this.crossOrigin);return{geometry:n,materials:s}}}),Object.assign(en.prototype,{load:function(e,t,i,n){""===this.texturePath&&(this.texturePath=e.substring(0,e.lastIndexOf("/")+1));var r=this;new xi(r.manager).load(e,function(i){var n=null;try{n=JSON.parse(i)}catch(s){return void console.error("THREE:ObjectLoader: Can't parse "+e+".",s.message)}var a=n.metadata;a!==undefined&&a.type!==undefined&&"geometry"!==a.type.toLowerCase()?r.parse(n,t):console.error("THREE.ObjectLoader: Can't load "+e+". Use THREE.JSONLoader instead.")},i,n)},setTexturePath:function(e){this.texturePath=e},setCrossOrigin:function(e){this.crossOrigin=e},parse:function(e,t){var i=this.parseGeometries(e.geometries),n=this.parseImages(e.images,function(){t!==undefined&&t(s)}),r=this.parseTextures(e.textures,n),a=this.parseMaterials(e.materials,r),s=this.parseObject(e.object,i,a);return e.animations&&(s.animations=this.parseAnimations(e.animations)),e.images!==undefined&&0!==e.images.length||t!==undefined&&t(s),s},parseGeometries:function(e){var t={};if(e!==undefined)for(var i=new $i,n=new Ji,r=0,a=e.length;r0){var r=new Ni(new wi(t));r.setCrossOrigin(this.crossOrigin);for(var a=0,s=e.length;a0?new gt(o,u):new Ae(o,u);break;case"LOD":s=new dt;break;case"Line":s=new Mt(r(t.geometry),a(t.material),t.mode);break;case"LineSegments":s=new yt(r(t.geometry),a(t.material));break;case"PointCloud":case"Points":s=new wt(r(t.geometry),a(t.material));break;case"Sprite":s=new ht(a(t.material));break;case"Group":s=new xt;break;case"SkinnedMesh":console.warn("THREE.ObjectLoader.parseObject() does not support SkinnedMesh type. Instantiates Object3D instead.");default:s=new ue}if(s.uuid=t.uuid,t.name!==undefined&&(s.name=t.name),t.matrix!==undefined?(e.fromArray(t.matrix),e.decompose(s.position,s.quaternion,s.scale)):(t.position!==undefined&&s.position.fromArray(t.position),t.rotation!==undefined&&s.rotation.fromArray(t.rotation),t.quaternion!==undefined&&s.quaternion.fromArray(t.quaternion),t.scale!==undefined&&s.scale.fromArray(t.scale)),t.castShadow!==undefined&&(s.castShadow=t.castShadow),t.receiveShadow!==undefined&&(s.receiveShadow=t.receiveShadow),t.shadow&&(t.shadow.bias!==undefined&&(s.shadow.bias=t.shadow.bias),t.shadow.radius!==undefined&&(s.shadow.radius=t.shadow.radius),t.shadow.mapSize!==undefined&&s.shadow.mapSize.fromArray(t.shadow.mapSize),t.shadow.camera!==undefined&&(s.shadow.camera=this.parseObject(t.shadow.camera))),t.visible!==undefined&&(s.visible=t.visible),t.userData!==undefined&&(s.userData=t.userData),t.children!==undefined)for(var c in t.children)s.add(this.parseObject(t.children[c],i,n));if("LOD"===t.type)for(var l=t.levels,h=0;h0)){u=r;break}u=r-1}if(r=u,n[r]===i)return l=r/(a-1);var c=n[r],l=(r+(i-c)/(n[r+1]-c))/(a-1);return l},getTangent:function(e){var t=e-1e-4,i=e+1e-4;t<0&&(t=0),i>1&&(i=1);var n=this.getPoint(t);return this.getPoint(i).clone().sub(n).normalize()},getTangentAt:function(e){var t=this.getUtoTmapping(e);return this.getTangent(t)},computeFrenetFrames:function(e,t){var i,n,r,a=new u,s=[],o=[],l=[],h=new u,d=new c;for(i=0;i<=e;i++)n=i/e,s[i]=this.getTangentAt(n),s[i].normalize();o[0]=new u,l[0]=new u;var p=Number.MAX_VALUE,f=Math.abs(s[0].x),g=Math.abs(s[0].y),m=Math.abs(s[0].z);for(f<=p&&(p=f,a.set(1,0,0)),g<=p&&(p=g,a.set(0,1,0)),m<=p&&a.set(0,0,1),h.crossVectors(s[0],a).normalize(),o[0].crossVectors(s[0],h),l[0].crossVectors(s[0],o[0]),i=1;i<=e;i++)o[i]=o[i-1].clone(),l[i]=l[i-1].clone(),h.crossVectors(s[i-1],s[i]),h.length()>Number.EPSILON&&(h.normalize(),r=Math.acos(ds.clamp(s[i-1].dot(s[i]),-1,1)),o[i].applyMatrix4(d.makeRotationAxis(h,r))),l[i].crossVectors(s[i],o[i]);if(!0===t)for(r=Math.acos(ds.clamp(o[0].dot(o[e]),-1,1)),r/=e,s[0].dot(h.crossVectors(o[0],o[e]))>0&&(r=-r),i=1;i<=e;i++)o[i].applyMatrix4(d.makeRotationAxis(s[i],r*i)),l[i].crossVectors(s[i],o[i]);return{tangents:s,normals:o,binormals:l}}},tn.create=function(e,t){return e.prototype=Object.create(tn.prototype),e.prototype.constructor=e,e.prototype.getPoint=t,e},(nn.prototype=Object.create(tn.prototype)).constructor=nn,nn.prototype.isLineCurve=!0,nn.prototype.getPoint=function(e){if(1===e)return this.v2.clone();var t=this.v2.clone().sub(this.v1);return t.multiplyScalar(e).add(this.v1),t},nn.prototype.getPointAt=function(e){return this.getPoint(e)},nn.prototype.getTangent=function(e){return this.v2.clone().sub(this.v1).normalize()},rn.prototype=Object.assign(Object.create(tn.prototype),{constructor:rn,add:function(e){this.curves.push(e)},closePath:function(){var e=this.curves[0].getPoint(0),t=this.curves[this.curves.length-1].getPoint(1);e.equals(t)||this.curves.push(new nn(t,e))},getPoint:function(e){for(var t=e*this.getLength(),i=this.getCurveLengths(),n=0;n=t){var r=i[n]-t,a=this.curves[n],s=a.getLength(),o=0===s?0:1-r/s;return a.getPointAt(o)}n++}return null},getLength:function(){var e=this.getCurveLengths();return e[e.length-1]},updateArcLengths:function(){this.needsUpdate=!0,this.cacheLengths=null,this.getLengths()},getCurveLengths:function(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length)return this.cacheLengths;for(var e=[],t=0,i=0,n=this.curves.length;i1&&!i[i.length-1].equals(i[0])&&i.push(i[0]),i},createPointsGeometry:function(e){var t=this.getPoints(e);return this.createGeometry(t)},createSpacedPointsGeometry:function(e){var t=this.getSpacedPoints(e);return this.createGeometry(t)},createGeometry:function(e){for(var t=new Ne,i=0,n=e.length;it;)n-=t;nt.length-2?t.length-1:r+1],c=t[r>t.length-3?t.length-1:r+2],l=Rs.interpolate;return new i(l(s.x,o.x,u.x,c.x,a),l(s.y,o.y,u.y,c.y,a))},(on.prototype=Object.create(tn.prototype)).constructor=on,on.prototype.getPoint=function(e){var t=Is.b3;return new i(t(e,this.v0.x,this.v1.x,this.v2.x,this.v3.x),t(e,this.v0.y,this.v1.y,this.v2.y,this.v3.y))},on.prototype.getTangent=function(e){var t=Rs.tangentCubicBezier;return new i(t(e,this.v0.x,this.v1.x,this.v2.x,this.v3.x),t(e,this.v0.y,this.v1.y,this.v2.y,this.v3.y)).normalize()},(un.prototype=Object.create(tn.prototype)).constructor=un,un.prototype.getPoint=function(e){var t=Is.b2;return new i(t(e,this.v0.x,this.v1.x,this.v2.x),t(e,this.v0.y,this.v1.y,this.v2.y))},un.prototype.getTangent=function(e){var t=Rs.tangentQuadraticBezier;return new i(t(e,this.v0.x,this.v1.x,this.v2.x),t(e,this.v0.y,this.v1.y,this.v2.y)).normalize()};var Us=Object.assign(Object.create(rn.prototype),{fromPoints:function(e){this.moveTo(e[0].x,e[0].y);for(var t=1,i=e.length;t0){var c=u.getPoint(0);c.equals(this.currentPoint)||this.lineTo(c.x,c.y)}this.curves.push(u);var l=u.getPoint(1);this.currentPoint.copy(l)}});cn.prototype=Object.assign(Object.create(Us),{constructor:cn,getPointsHoles:function(e){for(var t=[],i=0,n=this.holes.length;i1){for(var M=!1,y=[],v=0,w=d.length;vNumber.EPSILON){if(c<0&&(s=t[a],u=-u,o=t[r],c=-c),e.yo.y)continue;if(e.y===s.y){if(e.x===s.x)return!0}else{var l=c*(e.x-s.x)-u*(e.y-s.y);if(0===l)return!0;if(l<0)continue;n=!n}}else{if(e.y!==s.y)continue;if(o.x<=e.x&&e.x<=s.x||s.x<=e.x&&e.x<=o.x)return!0}}return n})(E.p,d[L].p)&&(v!==L&&y.push({froms:v,tos:L,hole:T}),N?(N=!1,h[L].push(E)):M=!0);N&&h[v].push(E)}y.length>0&&(M||(p=h))}for(var D,g=0,A=d.length;g0){this.source.connect(this.filters[0]);for(var e=1,t=this.filters.length;e0){this.source.disconnect(this.filters[0]);for(var e=1,t=this.filters.length;e=.5)for(var a=0;a!==r;++a)e[t+a]=e[i+a]},_slerp:function(e,t,i,n,r){o.slerpFlat(e,t,e,t,e,i,n)},_lerp:function(e,t,i,n,r){for(var a=1-n,s=0;s!==r;++s){var o=t+s;e[o]=e[o]*a+e[i+s]*n}}},En.prototype={constructor:En,getValue:function(e,t){this.bind(),this.getValue(e,t)},setValue:function(e,t){this.bind(),this.setValue(e,t)},bind:function(){var e=this.node,t=this.parsedPath,i=t.objectName,n=t.propertyName,r=t.propertyIndex;if(e||(e=En.findNode(this.rootNode,t.nodeName)||this.rootNode,this.node=e),this.getValue=this._getValue_unavailable,this.setValue=this._setValue_unavailable,e){if(i){var a=t.objectIndex;switch(i){case"materials":if(!e.material)return void console.error(" can not bind to material as node does not have a material",this);if(!e.material.materials)return void console.error(" can not bind to material.materials as node.material does not have a materials array",this);e=e.material.materials;break;case"bones":if(!e.skeleton)return void console.error(" can not bind to bones as node does not have a skeleton",this);e=e.skeleton.bones;for(c=0;c=i){var h=i++,d=t[h];n[d.uuid]=l,t[l]=d,n[c]=h,t[h]=u;for(var p=0,f=a;p!==f;++p){var g=r[p],m=g[h],M=g[l];g[l]=m,g[h]=M}}}this.nCachedObjects_=i},uncache:function(e){for(var t=this._objects,i=t.length,n=this.nCachedObjects_,r=this._indicesByUUID,a=this._bindings,s=a.length,o=0,u=arguments.length;o!==u;++o){var c=arguments[o].uuid,l=r[c];if(l!==undefined)if(delete r[c],l0)for(var u=this._interpolants,c=this._propertyBindings,l=0,h=u.length;l!==h;++l)u[l].evaluate(s),c[l].accumulate(n,o)},_updateWeight:function(e){var t=0;if(this.enabled){t=this.weight;var i=this._weightInterpolant;if(null!==i){var n=i.evaluate(e)[0];t*=n,e>i.parameterPositions[1]&&(this.stopFading(),0===n&&(this.enabled=!1))}}return this._effectiveWeight=t,t},_updateTimeScale:function(e){var t=0;if(!this.paused){t=this.timeScale;var i=this._timeScaleInterpolant;null!==i&&(t*=i.evaluate(e)[0],e>i.parameterPositions[1]&&(this.stopWarping(),0===t?this.paused=!0:this.timeScale=t))}return this._effectiveTimeScale=t,t},_updateTime:function(e){var t=this.time+e;if(0===e)return t;var i=this._clip.duration,n=this.loop,r=this._loopCount;if(2200===n){-1===r&&(this._loopCount=0,this._setEndings(!0,!0,!1));e:{if(t>=i)t=i;else{if(!(t<0))break e;t=0}this.clampWhenFinished?this.paused=!0:this.enabled=!1,this._mixer.dispatchEvent({type:"finished",action:this,direction:e<0?-1:1})}}else{var a=2202===n;if(-1===r&&(e>=0?(r=0,this._setEndings(!0,0===this.repetitions,a)):this._setEndings(0===this.repetitions,!0,a)),t>=i||t<0){var s=Math.floor(t/i);t-=i*s,r+=Math.abs(s);var o=this.repetitions-r;if(o<0)this.clampWhenFinished?this.paused=!0:this.enabled=!1,t=e>0?i:0,this._mixer.dispatchEvent({type:"finished",action:this,direction:e>0?1:-1});else{if(0===o){var u=e<0;this._setEndings(u,!u,a)}else this._setEndings(!1,!1,a);this._loopCount=r,this._mixer.dispatchEvent({type:"loop",action:this,loopDelta:s})}}if(a&&1==(1&r))return this.time=t,i-t}return this.time=t,t},_setEndings:function(e,t,i){var n=this._interpolantSettings;i?(n.endingStart=2401,n.endingEnd=2401):(n.endingStart=e?this.zeroSlopeAtStart?2401:$a:2402,n.endingEnd=t?this.zeroSlopeAtEnd?2401:$a:2402)},_scheduleFading:function(e,t,i){var n=this._mixer,r=n.time,a=this._weightInterpolant;null===a&&(a=n._lendControlInterpolant(),this._weightInterpolant=a);var s=a.parameterPositions,o=a.sampleValues;return s[0]=r,o[0]=t,s[1]=r+e,o[1]=i,this}},Object.assign(Dn.prototype,t.prototype,{clipAction:function(e,t){var i=t||this._root,n=i.uuid,r="string"==typeof e?Zi.findByName(i,e):e,a=null!==r?r.uuid:e,s=this._actionsByClip[a],o=null;if(s!==undefined){var u=s.actionByRoot[n];if(u!==undefined)return u;o=s.knownActions[0],null===r&&(r=o._clip)}if(null===r)return null;var c=new Ln(this,r,t);return this._bindAction(c,o),this._addInactiveAction(c,a,n),c},existingAction:function(e,t){var i=t||this._root,n=i.uuid,r="string"==typeof e?Zi.findByName(i,e):e,a=r?r.uuid:e,s=this._actionsByClip[a];return s!==undefined?s.actionByRoot[n]||null:null},stopAllAction:function(){var e=this._actions,t=this._nActiveActions,i=this._bindings,n=this._nActiveBindings;this._nActiveActions=0,this._nActiveBindings=0;for(r=0;r!==t;++r)e[r].reset();for(var r=0;r!==n;++r)i[r].useCount=0;return this},update:function(e){e*=this.timeScale;for(var t=this._actions,i=this._nActiveActions,n=this.time+=e,r=Math.sign(e),a=this._accuIndex^=1,s=0;s!==i;++s){var o=t[s];o.enabled&&o._update(n,e,r,a)}for(var u=this._bindings,c=this._nActiveBindings,s=0;s!==c;++s)u[s].apply(a);return this},getRoot:function(){return this._root},uncacheClip:function(e){var t=this._actions,i=e.uuid,n=this._actionsByClip,r=n[i];if(r!==undefined){for(var a=r.knownActions,s=0,o=a.length;s!==o;++s){var u=a[s];this._deactivateAction(u);var c=u._cacheIndex,l=t[t.length-1];u._cacheIndex=null,u._byClipCacheIndex=null,l._cacheIndex=c,t[c]=l,t.pop(),this._removeInactiveBindingsForAction(u)}delete n[i]}},uncacheRoot:function(e){var t=e.uuid,i=this._actionsByClip;for(var n in i){var r=i[n].actionByRoot[t];r!==undefined&&(this._deactivateAction(r),this._removeInactiveAction(r))}var a=this._bindingsByRootAndName[t];if(a!==undefined)for(var s in a){var o=a[s];o.restoreOriginalState(),this._removeInactiveBinding(o)}},uncacheAction:function(e,t){var i=this.existingAction(e,t);null!==i&&(this._deactivateAction(i),this._removeInactiveAction(i))}}),Object.assign(Dn.prototype,{_bindAction:function(e,t){var i=e._localRoot||this._root,n=e._clip.tracks,r=n.length,a=e._propertyBindings,s=e._interpolants,o=i.uuid,u=this._bindingsByRootAndName,c=u[o];c===undefined&&(c={},u[o]=c);for(var l=0;l!==r;++l){var h=n[l],d=h.name,p=c[d];if(p!==undefined)a[l]=p;else{if((p=a[l])!==undefined){null===p._cacheIndex&&(++p.referenceCount,this._addInactiveBinding(p,o,d));continue}var f=t&&t._propertyBindings[l].binding.parsedPath;++(p=new Tn(En.create(i,d,f),h.ValueTypeName,h.getValueSize())).referenceCount,this._addInactiveBinding(p,o,d),a[l]=p}s[l].resultBuffer=p.buffer}},_activateAction:function(e){if(!this._isActiveAction(e)){if(null===e._cacheIndex){var t=(e._localRoot||this._root).uuid,i=e._clip.uuid,n=this._actionsByClip[i];this._bindAction(e,n&&n.knownActions[0]),this._addInactiveAction(e,i,t)}for(var r=e._propertyBindings,a=0,s=r.length;a!==s;++a){var o=r[a];0==o.useCount++&&(this._lendBinding(o),o.saveOriginalState())}this._lendAction(e)}},_deactivateAction:function(e){if(this._isActiveAction(e)){for(var t=e._propertyBindings,i=0,n=t.length;i!==n;++i){var r=t[i];0==--r.useCount&&(r.restoreOriginalState(),this._takeBackBinding(r))}this._takeBackAction(e)}},_initMemoryManager:function(){this._actions=[],this._nActiveActions=0,this._actionsByClip={},this._bindings=[],this._nActiveBindings=0,this._bindingsByRootAndName={},this._controlInterpolants=[],this._nActiveControlInterpolants=0;var e=this;this.stats={actions:{get total(){return e._actions.length},get inUse(){return e._nActiveActions}},bindings:{get total(){return e._bindings.length},get inUse(){return e._nActiveBindings}},controlInterpolants:{get total(){return e._controlInterpolants.length},get inUse(){return e._nActiveControlInterpolants}}}},_isActiveAction:function(e){var t=e._cacheIndex;return null!==t&&t1&&(n[u=o[1]]||(n[u]={start:Infinity,end:-Infinity}),a<(c=n[u]).start&&(c.start=a),a>c.end&&(c.end=a),t||(t=u))}for(var u in n){var c=n[u];this.createAnimation(u,c.start,c.end,e)}this.firstAnimation=t},kn.prototype.setAnimationDirectionForward=function(e){var t=this.animationsMap[e];t&&(t.direction=1,t.directionBackwards=!1)},kn.prototype.setAnimationDirectionBackward=function(e){var t=this.animationsMap[e];t&&(t.direction=-1,t.directionBackwards=!0)},kn.prototype.setAnimationFPS=function(e,t){var i=this.animationsMap[e];i&&(i.fps=t,i.duration=(i.end-i.start)/i.fps)},kn.prototype.setAnimationDuration=function(e,t){var i=this.animationsMap[e];i&&(i.duration=t,i.fps=(i.end-i.start)/i.duration)},kn.prototype.setAnimationWeight=function(e,t){var i=this.animationsMap[e];i&&(i.weight=t)},kn.prototype.setAnimationTime=function(e,t){var i=this.animationsMap[e];i&&(i.time=t)},kn.prototype.getAnimationTime=function(e){var t=0,i=this.animationsMap[e];return i&&(t=i.time),t},kn.prototype.getAnimationDuration=function(e){var t=-1,i=this.animationsMap[e];return i&&(t=i.duration),t},kn.prototype.playAnimation=function(e){var t=this.animationsMap[e];t?(t.time=0,t.active=!0):console.warn("THREE.MorphBlendMesh: animation["+e+"] undefined in .playAnimation()")},kn.prototype.stopAnimation=function(e){var t=this.animationsMap[e];t&&(t.active=!1)},kn.prototype.update=function(e){for(var t=0,i=this.animationsList.length;tn.duration||n.time<0)&&(n.direction*=-1,n.time>n.duration&&(n.time=n.duration,n.directionBackwards=!0),n.time<0&&(n.time=0,n.directionBackwards=!1)):(n.time=n.time%n.duration,n.time<0&&(n.time+=n.duration));var a=n.start+ds.clamp(Math.floor(n.time/r),0,n.length-1),s=n.weight;a!==n.currentFrame&&(this.morphTargetInfluences[n.lastFrame]=0,this.morphTargetInfluences[n.currentFrame]=1*s,this.morphTargetInfluences[a]=0,n.lastFrame=n.currentFrame,n.currentFrame=a);var o=n.time%r/r;n.directionBackwards&&(o=1-o),n.currentFrame!==n.lastFrame?(this.morphTargetInfluences[n.currentFrame]=o*s,this.morphTargetInfluences[n.lastFrame]=(1-o)*s):this.morphTargetInfluences[n.currentFrame]=s}}},(Bn.prototype=Object.create(ue.prototype)).constructor=Bn,Bn.prototype.isImmediateRenderObject=!0,(Fn.prototype=Object.create(yt.prototype)).constructor=Fn,Fn.prototype.update=function(){var e=new u,t=new u,i=new te;return function(){var n=["a","b","c"];this.object.updateMatrixWorld(!0),i.getNormalMatrix(this.object.matrixWorld);var r=this.object.matrixWorld,a=this.geometry.attributes.position,s=this.object.geometry;if(s&&s.isGeometry)for(var o=s.vertices,u=s.faces,c=0,l=0,h=u.length;l.99999?this.quaternion.set(0,0,0,1):i.y<-.99999?this.quaternion.set(1,0,0,0):(t.set(i.z,0,-i.x).normalize(),e=Math.acos(i.y),this.quaternion.setFromAxisAngle(t,e))}}(),$n.prototype.setLength=function(e,t,i){t===undefined&&(t=.2*e),i===undefined&&(i=.2*t),this.line.scale.set(1,Math.max(0,e-t),1),this.line.updateMatrix(),this.cone.scale.set(i,t,i),this.cone.position.y=e,this.cone.updateMatrix()},$n.prototype.setColor=function(e){this.line.material.color.copy(e),this.cone.material.color.copy(e)},(er.prototype=Object.create(yt.prototype)).constructor=er;var Gs=function(){function e(){}var t=new u,i=new e,n=new e,r=new e;return e.prototype.init=function(e,t,i,n){this.c0=e,this.c1=i,this.c2=-3*e+3*t-2*i-n,this.c3=2*e-2*t+i+n},e.prototype.initNonuniformCatmullRom=function(e,t,i,n,r,a,s){var o=(t-e)/r-(i-e)/(r+a)+(i-t)/a,u=(i-t)/a-(n-t)/(a+s)+(n-i)/s;o*=a,u*=a,this.init(t,i,o,u)},e.prototype.initCatmullRom=function(e,t,i,n,r){this.init(t,i,r*(i-e),r*(n-t))},e.prototype.calc=function(e){var t=e*e,i=t*e;return this.c0+this.c1*e+this.c2*t+this.c3*i},tn.create(function(e){this.points=e||[],this.closed=!1},function(e){var a,s,o,c,l=this.points;(c=l.length)<2&&console.log("duh, you need at least 2 points"),o=(a=(c-(this.closed?0:1))*e)-(s=Math.floor(a)),this.closed?s+=s>0?0:(Math.floor(Math.abs(s)/l.length)+1)*l.length:0===o&&s===c-1&&(s=c-2,o=1);var h,d,p,f;if(this.closed||s>0?h=l[(s-1)%c]:(t.subVectors(l[0],l[1]).add(l[0]),h=t),d=l[s%c],p=l[(s+1)%c],this.closed||s+2t.length-2?t.length-1:n+1],c=t[n>t.length-3?t.length-1:n+2],l=Rs.interpolate;return new u(l(a.x,s.x,o.x,c.x,r),l(a.y,s.y,o.y,c.y,r),l(a.z,s.z,o.z,c.z,r))}),Ys=tn.create(function(e,t,i,n){this.v0=e,this.v1=t,this.v2=i,this.v3=n},function(e){var t=Is.b3;return new u(t(e,this.v0.x,this.v1.x,this.v2.x,this.v3.x),t(e,this.v0.y,this.v1.y,this.v2.y,this.v3.y),t(e,this.v0.z,this.v1.z,this.v2.z,this.v3.z))}),Vs=tn.create(function(e,t,i){this.v0=e,this.v1=t,this.v2=i},function(e){var t=Is.b2;return new u(t(e,this.v0.x,this.v1.x,this.v2.x),t(e,this.v0.y,this.v1.y,this.v2.y),t(e,this.v0.z,this.v1.z,this.v2.z))}),Hs=tn.create(function(e,t){this.v1=e,this.v2=t},function(e){if(1===e)return this.v2.clone();var t=new u;return t.subVectors(this.v2,this.v1),t.multiplyScalar(e),t.add(this.v1),t});(tr.prototype=Object.create(an.prototype)).constructor=tr;var Ws={createMultiMaterialObject:function(e,t){for(var i=new xt,n=0,r=t.length;nthis.points.length-2?this.points.length-1:n+1,d[3]=n>this.points.length-3?this.points.length-1:n+2,o=this.points[d[0]],c=this.points[d[1]],l=this.points[d[2]],h=this.points[d[3]],a=r*r,s=r*a,p.x=t(o.x,c.x,l.x,h.x,r,a,s),p.y=t(o.y,c.y,l.y,h.y,r,a,s),p.z=t(o.z,c.z,l.z,h.z,r,a,s),p},this.getControlPointsArray=function(){var e,t,i=this.points.length,n=[];for(e=0;e0?i=e[0]:t&&t("VR input not available.")})["catch"](function(){console.warn("THREE.VRControls: Unable to get VR Displays")}),this.scale=1,this.standing=!1,this.userHeight=1.6,this.getVRDisplay=function(){return i},this.setVRDisplay=function(e){i=e},this.getVRDisplays=function(){return console.warn("THREE.VRControls: getVRDisplays() is being deprecated."),n},this.getStandingMatrix=function(){return a},this.update=function(){if(i){var t;i.getFrameData?(i.getFrameData(s),t=s.pose):i.getPose&&(t=i.getPose()),null!==t.orientation&&e.quaternion.fromArray(t.orientation),null!==t.position?e.position.fromArray(t.position):e.position.set(0,0,0),this.standing&&(i.stageParameters?(e.updateMatrix(),a.fromArray(i.stageParameters.sittingToStandingTransform),e.applyMatrix(a)):e.position.setY(e.position.y+this.userHeight)),e.position.multiplyScalar(r.scale)}},this.resetPose=function(){i&&i.resetPose()},this.resetSensor=function(){console.warn("THREE.VRControls: .resetSensor() is now .resetPose()."),this.resetPose()},this.zeroSensor=function(){console.warn("THREE.VRControls: .zeroSensor() is now .resetPose()."),this.resetPose()},this.dispose=function(){i=null}}},{}],3:[function(e,t,i){THREE.VREffect=function(e,t){function i(){var t=p.isPresenting;if(p.isPresenting=s!==undefined&&s.isPresenting,p.isPresenting){var i=s.getEyeParameters("left"),n=i.renderWidth,r=i.renderHeight;t||(m=e.getPixelRatio(),f=e.getSize(),e.setPixelRatio(1),e.setSize(2*n,r,!1))}else t&&(e.setPixelRatio(m),e.setSize(f.width,f.height,g))}function n(e){var t=2/(e.leftTan+e.rightTan),i=(e.leftTan-e.rightTan)*t*.5,n=2/(e.upTan+e.downTan);return{scale:[t,n],offset:[i,(e.upTan-e.downTan)*n*.5]}}function r(e,t,i,r){t=t===undefined||t,i=i===undefined?.01:i,r=r===undefined?1e4:r;var a=t?-1:1,s=new THREE.Matrix4,o=s.elements,u=n(e);return o[0]=u.scale[0],o[1]=0,o[2]=u.offset[0]*a,o[3]=0,o[4]=0,o[5]=u.scale[1],o[6]=-u.offset[1]*a,o[7]=0,o[8]=0,o[9]=0,o[10]=r/(i-r)*-a,o[11]=r*i/(i-r),o[12]=0,o[13]=0,o[14]=a,o[15]=0,s.transpose(),s}function a(e,t,i,n){var a=Math.PI/180;return r({upTan:Math.tan(e.upDegrees*a),downTan:Math.tan(e.downDegrees*a),leftTan:Math.tan(e.leftDegrees*a),rightTan:Math.tan(e.rightDegrees*a)},t,i,n)}var s,o,u,c,l=new THREE.Vector3,h=new THREE.Vector3,d=null;"VRFrameData"in window&&(d=new window.VRFrameData),navigator.getVRDisplays&&navigator.getVRDisplays().then(function(e){o=e,e.length>0?s=e[0]:t&&t("HMD not available")})["catch"](function(){console.warn("THREE.VREffect: Unable to get VR Displays")}),this.isPresenting=!1,this.scale=1;var p=this,f=e.getSize(),g=!1,m=e.getPixelRatio();this.getVRDisplay=function(){return s},this.setVRDisplay=function(e){s=e},this.getVRDisplays=function(){return console.warn("THREE.VREffect: getVRDisplays() is being deprecated."),o},this.setSize=function(t,i,n){if(f={width:t,height:i},g=n,p.isPresenting){var r=s.getEyeParameters("left");e.setPixelRatio(1),e.setSize(2*r.renderWidth,r.renderHeight,!1)}else e.setPixelRatio(m),e.setSize(t,i,n)};var M=e.domElement,y=[0,0,.5,1],v=[.5,0,.5,1];window.addEventListener("vrdisplaypresentchange",i,!1),this.setFullScreen=function(e){return new Promise(function(t,i){s!==undefined?p.isPresenting!==e?t(e?s.requestPresent([{source:M}]):s.exitPresent()):t():i(new Error("No VR hardware found."))})},this.requestPresent=function(){return this.setFullScreen(!0)},this.exitPresent=function(){return this.setFullScreen(!1)},this.requestAnimationFrame=function(e){return s!==undefined?s.requestAnimationFrame(e):window.requestAnimationFrame(e)},this.cancelAnimationFrame=function(e){s!==undefined?s.cancelAnimationFrame(e):window.cancelAnimationFrame(e)},this.submitFrame=function(){s!==undefined&&p.isPresenting&&s.submitFrame()},this.autoSubmitFrame=!0;var w=new THREE.PerspectiveCamera;w.layers.enable(1);var x=new THREE.PerspectiveCamera;x.layers.enable(2),this.render=function(t,i,n,r){if(s&&p.isPresenting){var o=t.autoUpdate;o&&(t.updateMatrixWorld(),t.autoUpdate=!1);var f=s.getEyeParameters("left"),g=s.getEyeParameters("right");l.fromArray(f.offset),h.fromArray(g.offset),Array.isArray(t)&&(console.warn("THREE.VREffect.render() no longer supports arrays. Use object.layers instead."),t=t[0]);var m,M,T=e.getSize(),E=s.getLayers();if(E.length){var N=E[0];m=null!==N.leftBounds&&4===N.leftBounds.length?N.leftBounds:y,M=null!==N.rightBounds&&4===N.rightBounds.length?N.rightBounds:v}else m=y,M=v;u={x:Math.round(T.width*m[0]),y:Math.round(T.height*m[1]),width:Math.round(T.width*m[2]),height:Math.round(T.height*m[3])},c={x:Math.round(T.width*M[0]),y:Math.round(T.height*M[1]),width:Math.round(T.width*M[2]),height:Math.round(T.height*M[3])},n?(e.setRenderTarget(n),n.scissorTest=!0):(e.setRenderTarget(null),e.setScissorTest(!0)),(e.autoClear||r)&&e.clear(),null===i.parent&&i.updateMatrixWorld(),i.matrixWorld.decompose(w.position,w.quaternion,w.scale),i.matrixWorld.decompose(x.position,x.quaternion,x.scale);var L=this.scale;return w.translateOnAxis(l,L),x.translateOnAxis(h,L),s.getFrameData?(s.depthNear=i.near,s.depthFar=i.far,s.getFrameData(d),w.projectionMatrix.elements=d.leftProjectionMatrix,x.projectionMatrix.elements=d.rightProjectionMatrix):(w.projectionMatrix=a(f.fieldOfView,!0,i.near,i.far),x.projectionMatrix=a(g.fieldOfView,!0,i.near,i.far)),n?(n.viewport.set(u.x,u.y,u.width,u.height),n.scissor.set(u.x,u.y,u.width,u.height)):(e.setViewport(u.x,u.y,u.width,u.height),e.setScissor(u.x,u.y,u.width,u.height)),e.render(t,w,n,r),n?(n.viewport.set(c.x,c.y,c.width,c.height),n.scissor.set(c.x,c.y,c.width,c.height)):(e.setViewport(c.x,c.y,c.width,c.height),e.setScissor(c.x,c.y,c.width,c.height)),e.render(t,x,n,r),n?(n.viewport.set(0,0,T.width,T.height),n.scissor.set(0,0,T.width,T.height),n.scissorTest=!1,e.setRenderTarget(null)):(e.setViewport(0,0,T.width,T.height),e.setScissorTest(!1)),o&&(t.autoUpdate=!0),void(p.autoSubmitFrame&&p.submitFrame())}e.render(t,i,n,r)},this.dispose=function(){window.removeEventListener("vrdisplaypresentchange",i,!1)}}},{}],4:[function(e,t,i){(function(n){!function(e){if("object"==typeof i&&void 0!==t)t.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:void 0!==n?n:"undefined"!=typeof self?self:this).WebVRManager=e()}}(function(){return function t(i,n,r){function a(o,u){if(!n[o]){if(!i[o]){var c="function"==typeof e&&e;if(!u&&c)return c(o,!0);if(s)return s(o,!0);var l=new Error("Cannot find module '"+o+"'");throw l.code="MODULE_NOT_FOUND",l}var h=n[o]={exports:{}};i[o][0].call(h.exports,function(e){var t=i[o][1][e];return a(t||e)},h,h.exports,t,i,n,r)}return n[o].exports}for(var s="function"==typeof e&&e,o=0;o","Brandon Jones ","Jordan Santell "],description:"Use WebVR today, on mobile or desktop, without requiring a special browser build.",devDependencies:{chai:"^3.5.0",jsdom:"^9.12.0",mocha:"^3.2.0",semver:"^5.3.0",webpack:"^2.6.1","webpack-dev-server":"^2.4.5"},main:"src/node-entry",keywords:["vr","webvr"],license:"Apache-2.0",scripts:{start:"npm run watch",watch:"webpack-dev-server",build:"webpack",test:"mocha"},repository:"googlevr/webvr-polyfill",bugs:{url:"https://github.com/googlevr/webvr-polyfill/issues"}}},{}],6:[function(e,t,i){function n(){this.isPolyfilled=!0,this.displayId=c++,this.displayName="webvr-polyfill displayName",this.depthNear=.01,this.depthFar=1e4,this.isConnected=!0,this.isPresenting=!1,this.capabilities={hasPosition:!1,hasOrientation:!1,hasExternalDisplay:!1,canPresent:!1,maxLayers:1},this.stageParameters=null,this.waitingForPresent_=!1,this.layer_=null,this.fullscreenElement_=null,this.fullscreenWrapper_=null,this.fullscreenElementCachedStyle_=null,this.fullscreenEventTarget_=null,this.fullscreenChangeHandler_=null,this.fullscreenErrorHandler_=null,this.wakelock_=new u}function r(){this.isPolyfilled=!0,this.hardwareUnitId="webvr-polyfill hardwareUnitId",this.deviceId="webvr-polyfill deviceId",this.deviceName="webvr-polyfill deviceName"}function a(){}function s(){}var o=e("./util.js"),u=e("./wakelock.js"),c=1e3,l=!1,h=[0,0,.5,1],d=[.5,0,.5,1];n.prototype.getFrameData=function(e){return o.frameDataFromPose(e,this.getPose(),this)},n.prototype.getPose=function(){return this.getImmediatePose()},n.prototype.requestAnimationFrame=function(e){return window.requestAnimationFrame(e)},n.prototype.cancelAnimationFrame=function(e){return window.cancelAnimationFrame(e)},n.prototype.wrapForFullscreen=function(e){if(o.isIOS())return e;if(!this.fullscreenWrapper_){this.fullscreenWrapper_=document.createElement("div");var t=["height: "+Math.min(screen.height,screen.width)+"px !important","top: 0 !important","left: 0 !important","right: 0 !important","border: 0","margin: 0","padding: 0","z-index: 999999 !important","position: fixed"];this.fullscreenWrapper_.setAttribute("style",t.join("; ")+";"),this.fullscreenWrapper_.classList.add("webvr-polyfill-fullscreen-wrapper")}if(this.fullscreenElement_==e)return this.fullscreenWrapper_;this.removeFullscreenWrapper(),this.fullscreenElement_=e;var i=this.fullscreenElement_.parentElement;i.insertBefore(this.fullscreenWrapper_,this.fullscreenElement_),i.removeChild(this.fullscreenElement_),this.fullscreenWrapper_.insertBefore(this.fullscreenElement_,this.fullscreenWrapper_.firstChild),this.fullscreenElementCachedStyle_=this.fullscreenElement_.getAttribute("style");var n=this;return function(){if(n.fullscreenElement_){var e=["position: absolute","top: 0","left: 0","width: "+Math.max(screen.width,screen.height)+"px","height: "+Math.min(screen.height,screen.width)+"px","border: 0","margin: 0","padding: 0"];n.fullscreenElement_.setAttribute("style",e.join("; ")+";")}}(),this.fullscreenWrapper_},n.prototype.removeFullscreenWrapper=function(){if(this.fullscreenElement_){var e=this.fullscreenElement_;this.fullscreenElementCachedStyle_?e.setAttribute("style",this.fullscreenElementCachedStyle_):e.removeAttribute("style"),this.fullscreenElement_=null,this.fullscreenElementCachedStyle_=null;var t=this.fullscreenWrapper_.parentElement;return this.fullscreenWrapper_.removeChild(e),t.insertBefore(e,this.fullscreenWrapper_),t.removeChild(this.fullscreenWrapper_),e}},n.prototype.requestPresent=function(e){var t=this.isPresenting,i=this;return e instanceof Array||(l||(console.warn("Using a deprecated form of requestPresent. Should pass in an array of VRLayers."),l=!0),e=[e]),new Promise(function(n,r){if(i.capabilities.canPresent)if(0==e.length||e.length>i.capabilities.maxLayers)r(new Error("Invalid number of layers."));else{var a=e[0];if(a.source){var s=a.leftBounds||h,u=a.rightBounds||d;if(t){var c=i.layer_;c.source!==a.source&&(c.source=a.source);for(var l=0;l<4;l++)c.leftBounds[l]=s[l],c.rightBounds[l]=u[l];n()}else{if(i.layer_={predistorted:a.predistorted,source:a.source,leftBounds:s.slice(0),rightBounds:u.slice(0)},i.waitingForPresent_=!1,i.layer_&&i.layer_.source){var p=i.wrapForFullscreen(i.layer_.source);i.addFullscreenListeners_(p,function(){var e=o.getFullscreenElement();i.isPresenting=p===e,i.isPresenting?(screen.orientation&&screen.orientation.lock&&screen.orientation.lock("landscape-primary")["catch"](function(e){console.error("screen.orientation.lock() failed due to",e.message)}),i.waitingForPresent_=!1,i.beginPresent_(),n()):(screen.orientation&&screen.orientation.unlock&&screen.orientation.unlock(),i.removeFullscreenWrapper(),i.wakelock_.release(),i.endPresent_(),i.removeFullscreenListeners_()),i.fireVRDisplayPresentChange_()},function(){i.waitingForPresent_&&(i.removeFullscreenWrapper(),i.removeFullscreenListeners_(),i.wakelock_.release(),i.waitingForPresent_=!1,i.isPresenting=!1,r(new Error("Unable to present.")))}),o.requestFullscreen(p)?(i.wakelock_.request(),i.waitingForPresent_=!0):(o.isIOS()||o.isWebViewAndroid())&&(i.wakelock_.request(),i.isPresenting=!0,i.beginPresent_(),i.fireVRDisplayPresentChange_(),n())}i.waitingForPresent_||o.isIOS()||(o.exitFullscreen(),r(new Error("Unable to present.")))}}else n()}else r(new Error("VRDisplay is not capable of presenting."))})},n.prototype.exitPresent=function(){var e=this.isPresenting,t=this;return this.isPresenting=!1,this.layer_=null,this.wakelock_.release(),new Promise(function(i,n){e?(!o.exitFullscreen()&&o.isIOS()&&(t.endPresent_(),t.fireVRDisplayPresentChange_()),o.isWebViewAndroid()&&(t.removeFullscreenWrapper(),t.removeFullscreenListeners_(),t.endPresent_(),t.fireVRDisplayPresentChange_()),i()):n(new Error("Was not presenting to VRDisplay."))})},n.prototype.getLayers=function(){return this.layer_?[this.layer_]:[]},n.prototype.fireVRDisplayPresentChange_=function(){var e=new CustomEvent("vrdisplaypresentchange",{detail:{display:this}});window.dispatchEvent(e)},n.prototype.fireVRDisplayConnect_=function(){var e=new CustomEvent("vrdisplayconnect",{detail:{display:this}});window.dispatchEvent(e)},n.prototype.addFullscreenListeners_=function(e,t,i){this.removeFullscreenListeners_(),this.fullscreenEventTarget_=e,this.fullscreenChangeHandler_=t,this.fullscreenErrorHandler_=i,t&&(document.fullscreenEnabled?e.addEventListener("fullscreenchange",t,!1):document.webkitFullscreenEnabled?e.addEventListener("webkitfullscreenchange",t,!1):document.mozFullScreenEnabled?document.addEventListener("mozfullscreenchange",t,!1):document.msFullscreenEnabled&&e.addEventListener("msfullscreenchange",t,!1)),i&&(document.fullscreenEnabled?e.addEventListener("fullscreenerror",i,!1):document.webkitFullscreenEnabled?e.addEventListener("webkitfullscreenerror",i,!1):document.mozFullScreenEnabled?document.addEventListener("mozfullscreenerror",i,!1):document.msFullscreenEnabled&&e.addEventListener("msfullscreenerror",i,!1))},n.prototype.removeFullscreenListeners_=function(){if(this.fullscreenEventTarget_){var e=this.fullscreenEventTarget_;if(this.fullscreenChangeHandler_){var t=this.fullscreenChangeHandler_;e.removeEventListener("fullscreenchange",t,!1),e.removeEventListener("webkitfullscreenchange",t,!1),document.removeEventListener("mozfullscreenchange",t,!1),e.removeEventListener("msfullscreenchange",t,!1)}if(this.fullscreenErrorHandler_){var i=this.fullscreenErrorHandler_;e.removeEventListener("fullscreenerror",i,!1),e.removeEventListener("webkitfullscreenerror",i,!1),document.removeEventListener("mozfullscreenerror",i,!1),e.removeEventListener("msfullscreenerror",i,!1)}this.fullscreenEventTarget_=null,this.fullscreenChangeHandler_=null,this.fullscreenErrorHandler_=null}},n.prototype.beginPresent_=function(){},n.prototype.endPresent_=function(){},n.prototype.submitFrame=function(e){},n.prototype.getEyeParameters=function(e){return null},a.prototype=new r,s.prototype=new r,t.exports.VRFrameData=function(){this.leftProjectionMatrix=new Float32Array(16),this.leftViewMatrix=new Float32Array(16),this.rightProjectionMatrix=new Float32Array(16),this.rightViewMatrix=new Float32Array(16),this.pose=null},t.exports.VRDisplay=n,t.exports.VRDevice=r,t.exports.HMDVRDevice=a,t.exports.PositionSensorVRDevice=s},{"./util.js":26,"./wakelock.js":28}],7:[function(e,t,i){function n(e){this.gl=e,this.ctxAttribs=e.getContextAttributes(),this.meshWidth=20,this.meshHeight=20,this.bufferScale=window.WebVRConfig.BUFFER_SCALE,this.bufferWidth=e.drawingBufferWidth,this.bufferHeight=e.drawingBufferHeight,this.realBindFramebuffer=e.bindFramebuffer,this.realEnable=e.enable,this.realDisable=e.disable,this.realColorMask=e.colorMask,this.realClearColor=e.clearColor,this.realViewport=e.viewport,a.isIOS()||(this.realCanvasWidth=Object.getOwnPropertyDescriptor(e.canvas.__proto__,"width"),this.realCanvasHeight=Object.getOwnPropertyDescriptor(e.canvas.__proto__,"height")),this.isPatched=!1,this.lastBoundFramebuffer=null,this.cullFace=!1,this.depthTest=!1,this.blend=!1,this.scissorTest=!1,this.stencilTest=!1,this.viewport=[0,0,0,0],this.colorMask=[!0,!0,!0,!0],this.clearColor=[0,0,0,0],this.attribs={position:0,texCoord:1},this.program=a.linkProgram(e,o,u,this.attribs),this.uniforms=a.getProgramUniforms(e,this.program),this.viewportOffsetScale=new Float32Array(8),this.setTextureBounds(),this.vertexBuffer=e.createBuffer(),this.indexBuffer=e.createBuffer(),this.indexCount=0,this.renderTarget=e.createTexture(),this.framebuffer=e.createFramebuffer(),this.depthStencilBuffer=null,this.depthBuffer=null,this.stencilBuffer=null,this.ctxAttribs.depth&&this.ctxAttribs.stencil?this.depthStencilBuffer=e.createRenderbuffer():this.ctxAttribs.depth?this.depthBuffer=e.createRenderbuffer():this.ctxAttribs.stencil&&(this.stencilBuffer=e.createRenderbuffer()),this.patch(),this.onResize(),window.WebVRConfig.CARDBOARD_UI_DISABLED||(this.cardboardUI=new r(e))}var r=e("./cardboard-ui.js"),a=e("./util.js"),s=e("./deps/wglu-preserve-state.js"),o=["attribute vec2 position;","attribute vec3 texCoord;","varying vec2 vTexCoord;","uniform vec4 viewportOffsetScale[2];","void main() {"," vec4 viewport = viewportOffsetScale[int(texCoord.z)];"," vTexCoord = (texCoord.xy * viewport.zw) + viewport.xy;"," gl_Position = vec4( position, 1.0, 1.0 );","}"].join("\n"),u=["precision mediump float;","uniform sampler2D diffuse;","varying vec2 vTexCoord;","void main() {"," gl_FragColor = texture2D(diffuse, vTexCoord);","}"].join("\n");n.prototype.destroy=function(){var e=this.gl;this.unpatch(),e.deleteProgram(this.program),e.deleteBuffer(this.vertexBuffer),e.deleteBuffer(this.indexBuffer),e.deleteTexture(this.renderTarget),e.deleteFramebuffer(this.framebuffer),this.depthStencilBuffer&&e.deleteRenderbuffer(this.depthStencilBuffer),this.depthBuffer&&e.deleteRenderbuffer(this.depthBuffer),this.stencilBuffer&&e.deleteRenderbuffer(this.stencilBuffer),this.cardboardUI&&this.cardboardUI.destroy()},n.prototype.onResize=function(){var e=this.gl,t=this,i=[e.RENDERBUFFER_BINDING,e.TEXTURE_BINDING_2D,e.TEXTURE0];s(e,i,function(e){t.realBindFramebuffer.call(e,e.FRAMEBUFFER,null),t.scissorTest&&t.realDisable.call(e,e.SCISSOR_TEST),t.realColorMask.call(e,!0,!0,!0,!0),t.realViewport.call(e,0,0,e.drawingBufferWidth,e.drawingBufferHeight),t.realClearColor.call(e,0,0,0,1),e.clear(e.COLOR_BUFFER_BIT),t.realBindFramebuffer.call(e,e.FRAMEBUFFER,t.framebuffer),e.bindTexture(e.TEXTURE_2D,t.renderTarget),e.texImage2D(e.TEXTURE_2D,0,t.ctxAttribs.alpha?e.RGBA:e.RGB,t.bufferWidth,t.bufferHeight,0,t.ctxAttribs.alpha?e.RGBA:e.RGB,e.UNSIGNED_BYTE,null),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,t.renderTarget,0),t.ctxAttribs.depth&&t.ctxAttribs.stencil?(e.bindRenderbuffer(e.RENDERBUFFER,t.depthStencilBuffer),e.renderbufferStorage(e.RENDERBUFFER,e.DEPTH_STENCIL,t.bufferWidth,t.bufferHeight),e.framebufferRenderbuffer(e.FRAMEBUFFER,e.DEPTH_STENCIL_ATTACHMENT,e.RENDERBUFFER,t.depthStencilBuffer)):t.ctxAttribs.depth?(e.bindRenderbuffer(e.RENDERBUFFER,t.depthBuffer),e.renderbufferStorage(e.RENDERBUFFER,e.DEPTH_COMPONENT16,t.bufferWidth,t.bufferHeight),e.framebufferRenderbuffer(e.FRAMEBUFFER,e.DEPTH_ATTACHMENT,e.RENDERBUFFER,t.depthBuffer)):t.ctxAttribs.stencil&&(e.bindRenderbuffer(e.RENDERBUFFER,t.stencilBuffer),e.renderbufferStorage(e.RENDERBUFFER,e.STENCIL_INDEX8,t.bufferWidth,t.bufferHeight),e.framebufferRenderbuffer(e.FRAMEBUFFER,e.STENCIL_ATTACHMENT,e.RENDERBUFFER,t.stencilBuffer)),!e.checkFramebufferStatus(e.FRAMEBUFFER)===e.FRAMEBUFFER_COMPLETE&&console.error("Framebuffer incomplete!"),t.realBindFramebuffer.call(e,e.FRAMEBUFFER,t.lastBoundFramebuffer),t.scissorTest&&t.realEnable.call(e,e.SCISSOR_TEST),t.realColorMask.apply(e,t.colorMask),t.realViewport.apply(e,t.viewport),t.realClearColor.apply(e,t.clearColor)}),this.cardboardUI&&this.cardboardUI.onResize()},n.prototype.patch=function(){if(!this.isPatched){var e=this,t=this.gl.canvas,i=this.gl;a.isIOS()||(t.width=a.getScreenWidth()*this.bufferScale,t.height=a.getScreenHeight()*this.bufferScale,Object.defineProperty(t,"width",{configurable:!0,enumerable:!0,get:function(){return e.bufferWidth},set:function(i){e.bufferWidth=i,e.realCanvasWidth.set.call(t,i),e.onResize()}}),Object.defineProperty(t,"height",{configurable:!0,enumerable:!0,get:function(){return e.bufferHeight},set:function(i){e.bufferHeight=i,e.realCanvasHeight.set.call(t,i),e.onResize()}})),this.lastBoundFramebuffer=i.getParameter(i.FRAMEBUFFER_BINDING),null==this.lastBoundFramebuffer&&(this.lastBoundFramebuffer=this.framebuffer,this.gl.bindFramebuffer(i.FRAMEBUFFER,this.framebuffer)),this.gl.bindFramebuffer=function(t,n){e.lastBoundFramebuffer=n||e.framebuffer,e.realBindFramebuffer.call(i,t,e.lastBoundFramebuffer)},this.cullFace=i.getParameter(i.CULL_FACE),this.depthTest=i.getParameter(i.DEPTH_TEST),this.blend=i.getParameter(i.BLEND),this.scissorTest=i.getParameter(i.SCISSOR_TEST),this.stencilTest=i.getParameter(i.STENCIL_TEST),i.enable=function(t){switch(t){case i.CULL_FACE:e.cullFace=!0;break;case i.DEPTH_TEST:e.depthTest=!0;break;case i.BLEND:e.blend=!0;break;case i.SCISSOR_TEST:e.scissorTest=!0;break;case i.STENCIL_TEST:e.stencilTest=!0}e.realEnable.call(i,t)},i.disable=function(t){switch(t){case i.CULL_FACE:e.cullFace=!1;break;case i.DEPTH_TEST:e.depthTest=!1;break;case i.BLEND:e.blend=!1;break;case i.SCISSOR_TEST:e.scissorTest=!1;break;case i.STENCIL_TEST:e.stencilTest=!1}e.realDisable.call(i,t)},this.colorMask=i.getParameter(i.COLOR_WRITEMASK),i.colorMask=function(t,n,r,a){e.colorMask[0]=t,e.colorMask[1]=n,e.colorMask[2]=r,e.colorMask[3]=a,e.realColorMask.call(i,t,n,r,a)},this.clearColor=i.getParameter(i.COLOR_CLEAR_VALUE),i.clearColor=function(t,n,r,a){e.clearColor[0]=t,e.clearColor[1]=n,e.clearColor[2]=r,e.clearColor[3]=a,e.realClearColor.call(i,t,n,r,a)},this.viewport=i.getParameter(i.VIEWPORT),i.viewport=function(t,n,r,a){e.viewport[0]=t,e.viewport[1]=n,e.viewport[2]=r,e.viewport[3]=a,e.realViewport.call(i,t,n,r,a)},this.isPatched=!0,a.safariCssSizeWorkaround(t)}},n.prototype.unpatch=function(){if(this.isPatched){var e=this.gl,t=this.gl.canvas;a.isIOS()||(Object.defineProperty(t,"width",this.realCanvasWidth),Object.defineProperty(t,"height",this.realCanvasHeight)),t.width=this.bufferWidth,t.height=this.bufferHeight,e.bindFramebuffer=this.realBindFramebuffer,e.enable=this.realEnable,e.disable=this.realDisable,e.colorMask=this.realColorMask,e.clearColor=this.realClearColor,e.viewport=this.realViewport,this.lastBoundFramebuffer==this.framebuffer&&e.bindFramebuffer(e.FRAMEBUFFER,null),this.isPatched=!1,setTimeout(function(){a.safariCssSizeWorkaround(t)},1)}},n.prototype.setTextureBounds=function(e,t){e||(e=[0,0,.5,1]),t||(t=[.5,0,.5,1]),this.viewportOffsetScale[0]=e[0],this.viewportOffsetScale[1]=e[1],this.viewportOffsetScale[2]=e[2],this.viewportOffsetScale[3]=e[3],this.viewportOffsetScale[4]=t[0],this.viewportOffsetScale[5]=t[1],this.viewportOffsetScale[6]=t[2],this.viewportOffsetScale[7]=t[3]},n.prototype.submitFrame=function(){var e=this.gl,t=this,i=[];if(window.WebVRConfig.DIRTY_SUBMIT_FRAME_BINDINGS||i.push(e.CURRENT_PROGRAM,e.ARRAY_BUFFER_BINDING,e.ELEMENT_ARRAY_BUFFER_BINDING,e.TEXTURE_BINDING_2D,e.TEXTURE0),s(e,i,function(e){t.realBindFramebuffer.call(e,e.FRAMEBUFFER,null),t.cullFace&&t.realDisable.call(e,e.CULL_FACE),t.depthTest&&t.realDisable.call(e,e.DEPTH_TEST),t.blend&&t.realDisable.call(e,e.BLEND),t.scissorTest&&t.realDisable.call(e,e.SCISSOR_TEST),t.stencilTest&&t.realDisable.call(e,e.STENCIL_TEST),t.realColorMask.call(e,!0,!0,!0,!0),t.realViewport.call(e,0,0,e.drawingBufferWidth,e.drawingBufferHeight),(t.ctxAttribs.alpha||a.isIOS())&&(t.realClearColor.call(e,0,0,0,1),e.clear(e.COLOR_BUFFER_BIT)),e.useProgram(t.program),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,t.indexBuffer),e.bindBuffer(e.ARRAY_BUFFER,t.vertexBuffer),e.enableVertexAttribArray(t.attribs.position),e.enableVertexAttribArray(t.attribs.texCoord),e.vertexAttribPointer(t.attribs.position,2,e.FLOAT,!1,20,0),e.vertexAttribPointer(t.attribs.texCoord,3,e.FLOAT,!1,20,8),e.activeTexture(e.TEXTURE0),e.uniform1i(t.uniforms.diffuse,0),e.bindTexture(e.TEXTURE_2D,t.renderTarget),e.uniform4fv(t.uniforms.viewportOffsetScale,t.viewportOffsetScale),e.drawElements(e.TRIANGLES,t.indexCount,e.UNSIGNED_SHORT,0),t.cardboardUI&&t.cardboardUI.renderNoState(),t.realBindFramebuffer.call(t.gl,e.FRAMEBUFFER,t.framebuffer),t.ctxAttribs.preserveDrawingBuffer||(t.realClearColor.call(e,0,0,0,0),e.clear(e.COLOR_BUFFER_BIT)),window.WebVRConfig.DIRTY_SUBMIT_FRAME_BINDINGS||t.realBindFramebuffer.call(e,e.FRAMEBUFFER,t.lastBoundFramebuffer),t.cullFace&&t.realEnable.call(e,e.CULL_FACE),t.depthTest&&t.realEnable.call(e,e.DEPTH_TEST),t.blend&&t.realEnable.call(e,e.BLEND),t.scissorTest&&t.realEnable.call(e,e.SCISSOR_TEST),t.stencilTest&&t.realEnable.call(e,e.STENCIL_TEST),t.realColorMask.apply(e,t.colorMask),t.realViewport.apply(e,t.viewport),!t.ctxAttribs.alpha&&t.ctxAttribs.preserveDrawingBuffer||t.realClearColor.apply(e,t.clearColor)}),a.isIOS()){var n=e.canvas;n.width==t.bufferWidth&&n.height==t.bufferHeight||(t.bufferWidth=n.width,t.bufferHeight=n.height,t.onResize())}},n.prototype.updateDeviceInfo=function(e){var t=this.gl,i=this,n=[t.ARRAY_BUFFER_BINDING,t.ELEMENT_ARRAY_BUFFER_BINDING];s(t,n,function(t){var n=i.computeMeshVertices_(i.meshWidth,i.meshHeight,e);if(t.bindBuffer(t.ARRAY_BUFFER,i.vertexBuffer),t.bufferData(t.ARRAY_BUFFER,n,t.STATIC_DRAW),!i.indexCount){var r=i.computeMeshIndices_(i.meshWidth,i.meshHeight);t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,i.indexBuffer),t.bufferData(t.ELEMENT_ARRAY_BUFFER,r,t.STATIC_DRAW),i.indexCount=r.length}})},n.prototype.computeMeshVertices_=function(e,t,i){for(var n=new Float32Array(2*e*t*5),r=i.getLeftEyeVisibleTanAngles(),s=i.getLeftEyeNoLensTanAngles(),o=i.getLeftEyeVisibleScreenRect(s),u=0,c=0;c<2;c++){for(var l=0;lr-42&&n.clientXi.clientHeight-42?e(n):n.clientX<42&&n.clientY<42&&t(n)},i.addEventListener("click",this.listener,!1)},n.prototype.onResize=function(){var e=this.gl,t=this,i=[e.ARRAY_BUFFER_BINDING];a(e,i,function(e){function i(e,t){var i=(90-e)*u,n=Math.cos(i),s=Math.sin(i);r.push(c*n*d+a,c*s*d+d),r.push(t*n*d+a,t*s*d+d)}function n(t,i){r.push(p+t,e.drawingBufferHeight-p-i)}var r=[],a=e.drawingBufferWidth/2,s=Math.max(screen.width,screen.height)*window.devicePixelRatio,o=e.drawingBufferWidth/s*window.devicePixelRatio,l=4*o/2,h=42*o,d=28*o/2,p=14*o;r.push(a-l,h),r.push(a-l,e.drawingBufferHeight),r.push(a+l,h),r.push(a+l,e.drawingBufferHeight),t.gearOffset=r.length/2;for(var f=0;f<=6;f++){var g=60*f;i(g,1),i(g+12,1),i(g+20,.75),i(g+40,.75),i(g+48,1)}t.gearVertexCount=r.length/2-t.gearOffset,t.arrowOffset=r.length/2;var m=l/Math.sin(45*u);n(0,d),n(d,0),n(d+m,m),n(m,d+m),n(m,d-m),n(0,d),n(d,2*d),n(d+m,2*d-m),n(m,d-m),n(0,d),n(m,d-l),n(28*o,d-l),n(m,d+l),n(28*o,d+l),t.arrowVertexCount=r.length/2-t.arrowOffset,e.bindBuffer(e.ARRAY_BUFFER,t.vertexBuffer),e.bufferData(e.ARRAY_BUFFER,new Float32Array(r),e.STATIC_DRAW)})},n.prototype.render=function(){var e=this.gl,t=this,i=[e.CULL_FACE,e.DEPTH_TEST,e.BLEND,e.SCISSOR_TEST,e.STENCIL_TEST,e.COLOR_WRITEMASK,e.VIEWPORT,e.CURRENT_PROGRAM,e.ARRAY_BUFFER_BINDING];a(e,i,function(e){e.disable(e.CULL_FACE),e.disable(e.DEPTH_TEST),e.disable(e.BLEND),e.disable(e.SCISSOR_TEST),e.disable(e.STENCIL_TEST),e.colorMask(!0,!0,!0,!0),e.viewport(0,0,e.drawingBufferWidth,e.drawingBufferHeight),t.renderNoState()})},n.prototype.renderNoState=function(){var e=this.gl;e.useProgram(this.program),e.bindBuffer(e.ARRAY_BUFFER,this.vertexBuffer),e.enableVertexAttribArray(this.attribs.position),e.vertexAttribPointer(this.attribs.position,2,e.FLOAT,!1,8,0),e.uniform4f(this.uniforms.color,1,1,1,1),r.orthoMatrix(this.projMat,0,e.drawingBufferWidth,0,e.drawingBufferHeight,.1,1024),e.uniformMatrix4fv(this.uniforms.projectionMat,!1,this.projMat),e.drawArrays(e.TRIANGLE_STRIP,0,4),e.drawArrays(e.TRIANGLE_STRIP,this.gearOffset,this.gearVertexCount),e.drawArrays(e.TRIANGLE_STRIP,this.arrowOffset,this.arrowVertexCount)},t.exports=n},{"./deps/wglu-preserve-state.js":10,"./util.js":26}],9:[function(e,t,i){function n(){this.displayName="Cardboard VRDisplay (webvr-polyfill)",this.capabilities.hasOrientation=!0,this.capabilities.canPresent=!0,this.bufferScale_=window.WebVRConfig.BUFFER_SCALE,this.poseSensor_=new u,this.distorter_=null,this.cardboardUI_=null,this.dpdb_=new o(!0,this.onDeviceParamsUpdated_.bind(this)),this.deviceInfo_=new s(this.dpdb_.getDeviceParams()),this.viewerSelector_=new l,this.viewerSelector_.onChange(this.onViewerChanged_.bind(this)),this.deviceInfo_.setViewer(this.viewerSelector_.getCurrentViewer()),window.WebVRConfig.ROTATE_INSTRUCTIONS_DISABLED||(this.rotateInstructions_=new c),d.isIOS()&&window.addEventListener("resize",this.onResize_.bind(this))}var r=e("./cardboard-distorter.js"),a=e("./cardboard-ui.js"),s=e("./device-info.js"),o=e("./dpdb/dpdb.js"),u=e("./sensor-fusion/fusion-pose-sensor.js"),c=e("./rotate-instructions.js"),l=e("./viewer-selector.js"),h=e("./base.js").VRDisplay,d=e("./util.js"),p={LEFT:"left",RIGHT:"right"};(n.prototype=new h).getImmediatePose=function(){return{position:this.poseSensor_.getPosition(),orientation:this.poseSensor_.getOrientation(),linearVelocity:null,linearAcceleration:null,angularVelocity:null,angularAcceleration:null}},n.prototype.resetPose=function(){this.poseSensor_.resetPose()},n.prototype.getEyeParameters=function(e){var t,i=[.5*this.deviceInfo_.viewer.interLensDistance,0,0];if(e==p.LEFT)i[0]*=-1,t=this.deviceInfo_.getFieldOfViewLeftEye();else{if(e!=p.RIGHT)return console.error("Invalid eye provided: %s",e),null;t=this.deviceInfo_.getFieldOfViewRightEye()}return{fieldOfView:t,offset:i,renderWidth:.5*this.deviceInfo_.device.width*this.bufferScale_,renderHeight:this.deviceInfo_.device.height*this.bufferScale_}},n.prototype.onDeviceParamsUpdated_=function(e){d.isDebug()&&console.log("DPDB reported that device params were updated."),this.deviceInfo_.updateDeviceParams(e),this.distorter_&&this.distorter_.updateDeviceInfo(this.deviceInfo_)},n.prototype.updateBounds_=function(){this.layer_&&this.distorter_&&(this.layer_.leftBounds||this.layer_.rightBounds)&&this.distorter_.setTextureBounds(this.layer_.leftBounds,this.layer_.rightBounds)},n.prototype.beginPresent_=function(){var e=this.layer_.source.getContext("webgl");e||(e=this.layer_.source.getContext("experimental-webgl")),e||(e=this.layer_.source.getContext("webgl2")),e&&(this.layer_.predistorted?window.WebVRConfig.CARDBOARD_UI_DISABLED||(e.canvas.width=d.getScreenWidth()*this.bufferScale_,e.canvas.height=d.getScreenHeight()*this.bufferScale_,this.cardboardUI_=new a(e)):(this.distorter_=new r(e),this.distorter_.updateDeviceInfo(this.deviceInfo_),this.cardboardUI_=this.distorter_.cardboardUI),this.cardboardUI_&&this.cardboardUI_.listen(function(e){this.viewerSelector_.show(this.layer_.source.parentElement),e.stopPropagation(),e.preventDefault()}.bind(this),function(e){this.exitPresent(),e.stopPropagation(),e.preventDefault()}.bind(this)),this.rotateInstructions_&&(d.isLandscapeMode()&&d.isMobile()?this.rotateInstructions_.showTemporarily(3e3,this.layer_.source.parentElement):this.rotateInstructions_.update()),this.orientationHandler=this.onOrientationChange_.bind(this),window.addEventListener("orientationchange",this.orientationHandler),this.vrdisplaypresentchangeHandler=this.updateBounds_.bind(this),window.addEventListener("vrdisplaypresentchange",this.vrdisplaypresentchangeHandler),this.fireVRDisplayDeviceParamsChange_())},n.prototype.endPresent_=function(){this.distorter_&&(this.distorter_.destroy(),this.distorter_=null),this.cardboardUI_&&(this.cardboardUI_.destroy(),this.cardboardUI_=null),this.rotateInstructions_&&this.rotateInstructions_.hide(),this.viewerSelector_.hide(),window.removeEventListener("orientationchange",this.orientationHandler),window.removeEventListener("vrdisplaypresentchange",this.vrdisplaypresentchangeHandler)},n.prototype.submitFrame=function(e){if(this.distorter_)this.updateBounds_(),this.distorter_.submitFrame();else if(this.cardboardUI_&&this.layer_){var t=this.layer_.source.getContext("webgl").canvas;t.width==this.lastWidth&&t.height==this.lastHeight||this.cardboardUI_.onResize(),this.lastWidth=t.width,this.lastHeight=t.height,this.cardboardUI_.render()}},n.prototype.onOrientationChange_=function(e){this.viewerSelector_.hide(),this.rotateInstructions_&&this.rotateInstructions_.update(),this.onResize_()},n.prototype.onResize_=function(e){if(this.layer_){var t=this.layer_.source.getContext("webgl"),i=["position: absolute","top: 0","left: 0","width: "+Math.max(screen.width,screen.height)+"px","height: "+Math.min(screen.height,screen.width)+"px","border: 0","margin: 0","padding: 0 10px 10px 0","box-sizing: content-box"];t.canvas.setAttribute("style",i.join("; ")+";"),d.safariCssSizeWorkaround(t.canvas)}},n.prototype.onViewerChanged_=function(e){this.deviceInfo_.setViewer(e),this.distorter_&&this.distorter_.updateDeviceInfo(this.deviceInfo_),this.fireVRDisplayDeviceParamsChange_()},n.prototype.fireVRDisplayDeviceParamsChange_=function(){var e=new CustomEvent("vrdisplaydeviceparamschange",{detail:{vrdisplay:this,deviceInfo:this.deviceInfo_}});window.dispatchEvent(e)},t.exports=n},{"./base.js":6,"./cardboard-distorter.js":7,"./cardboard-ui.js":8,"./device-info.js":11,"./dpdb/dpdb.js":15,"./rotate-instructions.js":20,"./sensor-fusion/fusion-pose-sensor.js":22,"./util.js":26,"./viewer-selector.js":27}],10:[function(e,t,i){t.exports=function(e,t,i){if(t){for(var n=[],r=null,a=0;ae.TEXTURE31){console.error("TEXTURE_BINDING_2D or TEXTURE_BINDING_CUBE_MAP must be followed by a valid texture unit"),n.push(null,null);break}r||(r=e.getParameter(e.ACTIVE_TEXTURE)),e.activeTexture(u),n.push(e.getParameter(s),null);break;case e.ACTIVE_TEXTURE:r=e.getParameter(e.ACTIVE_TEXTURE),n.push(null);break;default:n.push(e.getParameter(s))}for(i(e),a=0;ae.TEXTURE31)break;e.activeTexture(u),e.bindTexture(e.TEXTURE_2D,o);break;case e.TEXTURE_BINDING_CUBE_MAP:var u=t[++a];if(ue.TEXTURE31)break;e.activeTexture(u),e.bindTexture(e.TEXTURE_CUBE_MAP,o);break;case e.VIEWPORT:e.viewport(o[0],o[1],o[2],o[3]);break;case e.BLEND:case e.CULL_FACE:case e.DEPTH_TEST:case e.SCISSOR_TEST:case e.STENCIL_TEST:o?e.enable(s):e.disable(s);break;default:console.log("No GL restore behavior for 0x"+s.toString(16))}r&&e.activeTexture(r)}}else i(e)}},{}],11:[function(e,t,i){function n(e){this.width=e.width||u.getScreenWidth(),this.height=e.height||u.getScreenHeight(),this.widthMeters=e.widthMeters,this.heightMeters=e.heightMeters,this.bevelMeters=e.bevelMeters}function r(e){this.viewer=h.CardboardV2,this.updateDeviceParams(e),this.distortion=new s(this.viewer.distortionCoefficients)}function a(e){this.id=e.id,this.label=e.label,this.fov=e.fov,this.interLensDistance=e.interLensDistance,this.baselineLensDistance=e.baselineLensDistance,this.screenLensDistance=e.screenLensDistance,this.distortionCoefficients=e.distortionCoefficients,this.inverseCoefficients=e.inverseCoefficients}var s=e("./distortion/distortion.js"),o=e("./math-util.js"),u=e("./util.js"),c=new n({widthMeters:.11,heightMeters:.062,bevelMeters:.004}),l=new n({widthMeters:.1038,heightMeters:.0584,bevelMeters:.004}),h={CardboardV1:new a({id:"CardboardV1",label:"Cardboard I/O 2014",fov:40,interLensDistance:.06,baselineLensDistance:.035,screenLensDistance:.042,distortionCoefficients:[.441,.156],inverseCoefficients:[-.4410035,.42756155,-.4804439,.5460139,-.58821183,.5733938,-.48303202,.33299083,-.17573841,.0651772,-.01488963,.001559834]}),CardboardV2:new a({id:"CardboardV2",label:"Cardboard I/O 2015",fov:60,interLensDistance:.064,baselineLensDistance:.035,screenLensDistance:.039,distortionCoefficients:[.34,.55],inverseCoefficients:[-.33836704,-.18162185,.862655,-1.2462051,1.0560602,-.58208317,.21609078,-.05444823,.009177956,-.0009904169,6183535e-11,-16981803e-13]})};r.prototype.updateDeviceParams=function(e){this.device=this.determineDevice_(e)||this.device},r.prototype.getDevice=function(){return this.device},r.prototype.setViewer=function(e){this.viewer=e,this.distortion=new s(this.viewer.distortionCoefficients)},r.prototype.determineDevice_=function(e){if(!e)return u.isIOS()?(console.warn("Using fallback iOS device measurements."),l):(console.warn("Using fallback Android device measurements."),c);var t=.0254/e.xdpi,i=.0254/e.ydpi;return new n({widthMeters:t*u.getScreenWidth(),heightMeters:i*u.getScreenHeight(),bevelMeters:.001*e.bevelMm})},r.prototype.getDistortedFieldOfViewLeftEye=function(){var e=this.viewer,t=this.device,i=this.distortion,n=e.screenLensDistance,r=(t.widthMeters-e.interLensDistance)/2,a=e.interLensDistance/2,s=e.baselineLensDistance-t.bevelMeters,u=t.heightMeters-s,c=o.radToDeg*Math.atan(i.distort(r/n)),l=o.radToDeg*Math.atan(i.distort(a/n)),h=o.radToDeg*Math.atan(i.distort(s/n)),d=o.radToDeg*Math.atan(i.distort(u/n));return{leftDegrees:Math.min(c,e.fov),rightDegrees:Math.min(l,e.fov),downDegrees:Math.min(h,e.fov),upDegrees:Math.min(d,e.fov)}},r.prototype.getLeftEyeVisibleTanAngles=function(){var e=this.viewer,t=this.device,i=this.distortion,n=Math.tan(-o.degToRad*e.fov),r=Math.tan(o.degToRad*e.fov),a=Math.tan(o.degToRad*e.fov),s=Math.tan(-o.degToRad*e.fov),u=t.widthMeters/4,c=t.heightMeters/2,l=e.baselineLensDistance-t.bevelMeters-c,h=e.interLensDistance/2-u,d=-l,p=e.screenLensDistance,f=i.distort((h-u)/p),g=i.distort((d+c)/p),m=i.distort((h+u)/p),M=i.distort((d-c)/p),y=new Float32Array(4);return y[0]=Math.max(n,f),y[1]=Math.min(r,g),y[2]=Math.min(a,m),y[3]=Math.max(s,M),y},r.prototype.getLeftEyeNoLensTanAngles=function(){var e=this.viewer,t=this.device,i=this.distortion,n=new Float32Array(4),r=i.distortInverse(Math.tan(-o.degToRad*e.fov)),a=i.distortInverse(Math.tan(o.degToRad*e.fov)),s=i.distortInverse(Math.tan(o.degToRad*e.fov)),u=i.distortInverse(Math.tan(-o.degToRad*e.fov)),c=t.widthMeters/4,l=t.heightMeters/2,h=e.baselineLensDistance-t.bevelMeters-l,d=e.interLensDistance/2-c,p=-h,f=e.screenLensDistance,g=(d-c)/f,m=(p+l)/f,M=(d+c)/f,y=(p-l)/f;return n[0]=Math.max(r,g),n[1]=Math.min(a,m),n[2]=Math.min(s,M),n[3]=Math.max(u,y),n},r.prototype.getLeftEyeVisibleScreenRect=function(e){var t=this.viewer,i=this.device,n=t.screenLensDistance,r=(i.widthMeters-t.interLensDistance)/2,a=t.baselineLensDistance-i.bevelMeters,s=(e[0]*n+r)/i.widthMeters,o=(e[1]*n+a)/i.heightMeters,u=(e[2]*n+r)/i.widthMeters,c=(e[3]*n+a)/i.heightMeters;return{x:s,y:c,width:u-s,height:o-c}},r.prototype.getFieldOfViewLeftEye=function(e){return e?this.getUndistortedFieldOfViewLeftEye():this.getDistortedFieldOfViewLeftEye()},r.prototype.getFieldOfViewRightEye=function(e){var t=this.getFieldOfViewLeftEye(e);return{leftDegrees:t.rightDegrees,rightDegrees:t.leftDegrees,upDegrees:t.upDegrees,downDegrees:t.downDegrees}},r.prototype.getUndistortedFieldOfViewLeftEye=function(){var e=this.getUndistortedParams_();return{leftDegrees:o.radToDeg*Math.atan(e.outerDist),rightDegrees:o.radToDeg*Math.atan(e.innerDist),downDegrees:o.radToDeg*Math.atan(e.bottomDist),upDegrees:o.radToDeg*Math.atan(e.topDist)}},r.prototype.getUndistortedViewportLeftEye=function(){var e=this.getUndistortedParams_(),t=this.viewer,i=this.device,n=t.screenLensDistance,r=i.widthMeters/n,a=i.heightMeters/n,s=i.width/r,o=i.height/a,u=Math.round((e.eyePosX-e.outerDist)*s),c=Math.round((e.eyePosY-e.bottomDist)*o);return{x:u,y:c,width:Math.round((e.eyePosX+e.innerDist)*s)-u,height:Math.round((e.eyePosY+e.topDist)*o)-c}},r.prototype.getUndistortedParams_=function(){var e=this.viewer,t=this.device,i=this.distortion,n=e.screenLensDistance,r=e.interLensDistance/2/n,a=t.widthMeters/n,s=t.heightMeters/n,u=a/2-r,c=(e.baselineLensDistance-t.bevelMeters)/n,l=e.fov,h=i.distortInverse(Math.tan(o.degToRad*l)),d=Math.min(u,h),p=Math.min(r,h),f=Math.min(c,h);return{outerDist:d,innerDist:p,topDist:Math.min(s-c,h),bottomDist:f,eyePosX:u,eyePosY:c}},r.Viewers=h,t.exports=r},{"./distortion/distortion.js":13,"./math-util.js":17,"./util.js":26}],12:[function(e,t,i){function n(e){this.display=e,this.hardwareUnitId=e.displayId,this.deviceId="webvr-polyfill:HMD:"+e.displayId,this.deviceName=e.displayName+" (HMD)"}function r(e){this.display=e,this.hardwareUnitId=e.displayId,this.deviceId="webvr-polyfill:PositionSensor: "+e.displayId,this.deviceName=e.displayName+" (PositionSensor)"}e("./base.js").VRDisplay;var a=e("./base.js").HMDVRDevice,s=e("./base.js").PositionSensorVRDevice;(n.prototype=new a).getEyeParameters=function(e){var t=this.display.getEyeParameters(e);return{currentFieldOfView:t.fieldOfView,maximumFieldOfView:t.fieldOfView,minimumFieldOfView:t.fieldOfView,recommendedFieldOfView:t.fieldOfView,eyeTranslation:{x:t.offset[0],y:t.offset[1],z:t.offset[2]},renderRect:{x:"right"==e?t.renderWidth:0,y:0,width:t.renderWidth,height:t.renderHeight}}},n.prototype.setFieldOfView=function(e,t,i,n){},(r.prototype=new s).getState=function(){var e=this.display.getPose();return{position:e.position?{x:e.position[0],y:e.position[1],z:e.position[2]}:null,orientation:e.orientation?{x:e.orientation[0],y:e.orientation[1],z:e.orientation[2],w:e.orientation[3]}:null,linearVelocity:null,linearAcceleration:null,angularVelocity:null,angularAcceleration:null}},r.prototype.resetState=function(){return this.positionDevice.resetPose()},t.exports.VRDisplayHMDDevice=n,t.exports.VRDisplayPositionSensorDevice=r},{"./base.js":6}],13:[function(e,t,i){function n(e){this.coefficients=e}n.prototype.distortInverse=function(e){for(var t=0,i=1,n=e-this.distort(t);Math.abs(i-t)>1e-4;){var r=e-this.distort(i),a=i-r*((i-t)/(r-n));t=i,i=a,n=r}return i},n.prototype.distort=function(e){for(var t=e*e,i=0,n=0;n=200&&i.status<=299?(n.dpdb=JSON.parse(i.response),n.recalculateDeviceParams_()):console.error("Error loading online DPDB!")}),i.send()}}function r(e){this.xdpi=e.xdpi,this.ydpi=e.ydpi,this.bevelMm=e.bevelMm}var a=e("./dpdb.json"),s=e("../util.js"),o="https://dpdb.webvr.rocks/dpdb.json";n.prototype.getDeviceParams=function(){return this.deviceParams},n.prototype.recalculateDeviceParams_=function(){var e=this.calcDeviceParams_();e?(this.deviceParams=e,this.onDeviceParamsUpdated&&this.onDeviceParamsUpdated(this.deviceParams)):console.error("Failed to recalculate device parameters.")},n.prototype.calcDeviceParams_=function(){var e=this.dpdb;if(!e)return console.error("DPDB not available."),null;if(1!=e.format)return console.error("DPDB has unexpected format version."),null;if(!e.devices||!e.devices.length)return console.error("DPDB does not have a devices section."),null;var t=navigator.userAgent||navigator.vendor||window.opera,i=s.getScreenWidth(),n=s.getScreenHeight();if(!e.devices)return console.error("DPDB has no devices section."),null;for(var a=0;a=1)return this.w=a,this.x=i,this.y=n,this.z=r,this;var o=Math.acos(s),u=Math.sqrt(1-s*s);if(Math.abs(u)<.001)return this.w=.5*(a+this.w),this.x=.5*(i+this.x),this.y=.5*(n+this.y),this.z=.5*(r+this.z),this;var c=Math.sin((1-t)*o)/u,l=Math.sin(t*o)/u;return this.w=a*c+this.w*l,this.x=i*c+this.x*l,this.y=n*c+this.y*l,this.z=r*c+this.z*l,this},setFromUnitVectors:function(){var e,t;return function(i,r){return e===undefined&&(e=new n.Vector3),(t=i.dot(r)+1)<1e-6?(t=0,Math.abs(i.x)>Math.abs(i.z)?e.set(-i.y,i.x,0):e.set(0,-i.z,i.y)):e.crossVectors(i,r),this.x=e.x,this.y=e.y,this.z=e.z,this.w=t,this.normalize(),this}}()},t.exports=n},{}],18:[function(e,t,i){function n(){this.displayName="Mouse and Keyboard VRDisplay (webvr-polyfill)",this.capabilities.hasOrientation=!0,window.addEventListener("keydown",this.onKeyDown_.bind(this)),window.addEventListener("mousemove",this.onMouseMove_.bind(this)),window.addEventListener("mousedown",this.onMouseDown_.bind(this)),window.addEventListener("mouseup",this.onMouseUp_.bind(this)),this.phi_=0,this.theta_=0,this.targetAngle_=null,this.angleAnimation_=null,this.orientation_=new a.Quaternion,this.rotateStart_=new a.Vector2,this.rotateEnd_=new a.Vector2,this.rotateDelta_=new a.Vector2,this.isDragging_=!1,this.orientationOut_=new Float32Array(4)}var r=e("./base.js").VRDisplay,a=e("./math-util.js"),s=e("./util.js");(n.prototype=new r).getImmediatePose=function(){return this.orientation_.setFromEulerYXZ(this.phi_,this.theta_,0),this.orientationOut_[0]=this.orientation_.x,this.orientationOut_[1]=this.orientation_.y,this.orientationOut_[2]=this.orientation_.z,this.orientationOut_[3]=this.orientation_.w,{position:null,orientation:this.orientationOut_,linearVelocity:null,linearAcceleration:null,angularVelocity:null,angularAcceleration:null}},n.prototype.onKeyDown_=function(e){38==e.keyCode?this.animatePhi_(this.phi_+.15):39==e.keyCode?this.animateTheta_(this.theta_-.15):40==e.keyCode?this.animatePhi_(this.phi_-.15):37==e.keyCode&&this.animateTheta_(this.theta_+.15)},n.prototype.animateTheta_=function(e){this.animateKeyTransitions_("theta_",e)},n.prototype.animatePhi_=function(e){e=s.clamp(e,-Math.PI/2,Math.PI/2),this.animateKeyTransitions_("phi_",e)},n.prototype.animateKeyTransitions_=function(e,t){this.angleAnimation_&&cancelAnimationFrame(this.angleAnimation_);var i=this[e],n=new Date;this.angleAnimation_=requestAnimationFrame(function r(){var a=new Date-n;if(a>=80)return this[e]=t,void cancelAnimationFrame(this.angleAnimation_);this.angleAnimation_=requestAnimationFrame(r.bind(this));var s=a/80;this[e]=i+(t-i)*s}.bind(this))},n.prototype.onMouseDown_=function(e){this.rotateStart_.set(e.clientX,e.clientY),this.isDragging_=!0},n.prototype.onMouseMove_=function(e){if(this.isDragging_||this.isPointerLocked_()){if(this.isPointerLocked_()){var t=e.movementX||e.mozMovementX||0,i=e.movementY||e.mozMovementY||0;this.rotateEnd_.set(this.rotateStart_.x-t,this.rotateStart_.y-i)}else this.rotateEnd_.set(e.clientX,e.clientY);this.rotateDelta_.subVectors(this.rotateEnd_,this.rotateStart_),this.rotateStart_.copy(this.rotateEnd_),this.phi_+=2*Math.PI*this.rotateDelta_.y/screen.height*.3,this.theta_+=2*Math.PI*this.rotateDelta_.x/screen.width*.5,this.phi_=s.clamp(this.phi_,-Math.PI/2,Math.PI/2)}},n.prototype.onMouseUp_=function(e){this.isDragging_=!1},n.prototype.isPointerLocked_=function(){return(document.pointerLockElement||document.mozPointerLockElement||document.webkitPointerLockElement)!==undefined},n.prototype.resetPose=function(){this.phi_=0,this.theta_=0},t.exports=n},{"./base.js":6,"./math-util.js":17,"./util.js":26}],19:[function(e,t,i){(function(t){void 0!==t&&t.window&&(t.document=t.window.document,t.navigator=t.window.navigator),e("./main")}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./main":16}],20:[function(e,t,i){function n(){this.loadIcon_();var e=document.createElement("div"),t=e.style;t.position="fixed",t.top=0,t.right=0,t.bottom=0,t.left=0,t.backgroundColor="gray",t.fontFamily="sans-serif",t.zIndex=1e6;var i=document.createElement("img");i.src=this.icon,(t=i.style).marginLeft="25%",t.marginTop="25%",t.width="50%",e.appendChild(i);var n=document.createElement("div");(t=n.style).textAlign="center",t.fontSize="16px",t.lineHeight="24px",t.margin="24px 25%",t.width="50%",n.innerHTML="Place your phone into your Cardboard viewer.",e.appendChild(n);var r=document.createElement("div");(t=r.style).backgroundColor="#CFD8DC",t.position="fixed",t.bottom=0,t.width="100%",t.height="48px",t.padding="14px 24px",t.boxSizing="border-box",t.color="#656A6B",e.appendChild(r);var a=document.createElement("div");a.style["float"]="left",a.innerHTML="No Cardboard viewer?";var s=document.createElement("a");s.href="https://www.google.com/get/cardboard/get-cardboard/",s.innerHTML="get one",s.target="_blank",(t=s.style)["float"]="right",t.fontWeight=600,t.textTransform="uppercase",t.borderLeft="1px solid gray",t.paddingLeft="24px",t.textDecoration="none",t.color="#656A6B",r.appendChild(a),r.appendChild(s),this.overlay=e,this.text=n,this.hide()}var r=e("./util.js");n.prototype.show=function(e){e||this.overlay.parentElement?e&&(this.overlay.parentElement&&this.overlay.parentElement!=e&&this.overlay.parentElement.removeChild(this.overlay),e.appendChild(this.overlay)):document.body.appendChild(this.overlay),this.overlay.style.display="block";var t=this.overlay.querySelector("img").style;r.isLandscapeMode()?(t.width="20%",t.marginLeft="40%",t.marginTop="3%"):(t.width="50%",t.marginLeft="25%",t.marginTop="25%")},n.prototype.hide=function(){this.overlay.style.display="none"},n.prototype.showTemporarily=function(e,t){this.show(t),this.timer=setTimeout(this.hide.bind(this),e)},n.prototype.disableShowTemporarily=function(){clearTimeout(this.timer)},n.prototype.update=function(){this.disableShowTemporarily(),!r.isLandscapeMode()&&r.isMobile()?this.show():this.hide()},n.prototype.loadIcon_=function(){this.icon=r.base64("image/svg+xml","PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjE5OHB4IiBoZWlnaHQ9IjI0MHB4IiB2aWV3Qm94PSIwIDAgMTk4IDI0MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxuczpza2V0Y2g9Imh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaC9ucyI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDMuMy4zICgxMjA4MSkgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+dHJhbnNpdGlvbjwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPjwvZGVmcz4KICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHNrZXRjaDp0eXBlPSJNU1BhZ2UiPgogICAgICAgIDxnIGlkPSJ0cmFuc2l0aW9uIiBza2V0Y2g6dHlwZT0iTVNBcnRib2FyZEdyb3VwIj4KICAgICAgICAgICAgPGcgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTQtKy1JbXBvcnRlZC1MYXllcnMtQ29weS0rLUltcG9ydGVkLUxheWVycy1Db3B5LTItQ29weSIgc2tldGNoOnR5cGU9Ik1TTGF5ZXJHcm91cCI+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHktNCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDEwNy4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjYyNSwyLjUyNyBDMTQ5LjYyNSwyLjUyNyAxNTUuODA1LDYuMDk2IDE1Ni4zNjIsNi40MTggTDE1Ni4zNjIsNy4zMDQgQzE1Ni4zNjIsNy40ODEgMTU2LjM3NSw3LjY2NCAxNTYuNCw3Ljg1MyBDMTU2LjQxLDcuOTM0IDE1Ni40Miw4LjAxNSAxNTYuNDI3LDguMDk1IEMxNTYuNTY3LDkuNTEgMTU3LjQwMSwxMS4wOTMgMTU4LjUzMiwxMi4wOTQgTDE2NC4yNTIsMTcuMTU2IEwxNjQuMzMzLDE3LjA2NiBDMTY0LjMzMywxNy4wNjYgMTY4LjcxNSwxNC41MzYgMTY5LjU2OCwxNC4wNDIgQzE3MS4wMjUsMTQuODgzIDE5NS41MzgsMjkuMDM1IDE5NS41MzgsMjkuMDM1IEwxOTUuNTM4LDgzLjAzNiBDMTk1LjUzOCw4My44MDcgMTk1LjE1Miw4NC4yNTMgMTk0LjU5LDg0LjI1MyBDMTk0LjM1Nyw4NC4yNTMgMTk0LjA5NSw4NC4xNzcgMTkzLjgxOCw4NC4wMTcgTDE2OS44NTEsNzAuMTc5IEwxNjkuODM3LDcwLjIwMyBMMTQyLjUxNSw4NS45NzggTDE0MS42NjUsODQuNjU1IEMxMzYuOTM0LDgzLjEyNiAxMzEuOTE3LDgxLjkxNSAxMjYuNzE0LDgxLjA0NSBDMTI2LjcwOSw4MS4wNiAxMjYuNzA3LDgxLjA2OSAxMjYuNzA3LDgxLjA2OSBMMTIxLjY0LDk4LjAzIEwxMTMuNzQ5LDEwMi41ODYgTDExMy43MTIsMTAyLjUyMyBMMTEzLjcxMiwxMzAuMTEzIEMxMTMuNzEyLDEzMC44ODUgMTEzLjMyNiwxMzEuMzMgMTEyLjc2NCwxMzEuMzMgQzExMi41MzIsMTMxLjMzIDExMi4yNjksMTMxLjI1NCAxMTEuOTkyLDEzMS4wOTQgTDY5LjUxOSwxMDYuNTcyIEM2OC41NjksMTA2LjAyMyA2Ny43OTksMTA0LjY5NSA2Ny43OTksMTAzLjYwNSBMNjcuNzk5LDEwMi41NyBMNjcuNzc4LDEwMi42MTcgQzY3LjI3LDEwMi4zOTMgNjYuNjQ4LDEwMi4yNDkgNjUuOTYyLDEwMi4yMTggQzY1Ljg3NSwxMDIuMjE0IDY1Ljc4OCwxMDIuMjEyIDY1LjcwMSwxMDIuMjEyIEM2NS42MDYsMTAyLjIxMiA2NS41MTEsMTAyLjIxNSA2NS40MTYsMTAyLjIxOSBDNjUuMTk1LDEwMi4yMjkgNjQuOTc0LDEwMi4yMzUgNjQuNzU0LDEwMi4yMzUgQzY0LjMzMSwxMDIuMjM1IDYzLjkxMSwxMDIuMjE2IDYzLjQ5OCwxMDIuMTc4IEM2MS44NDMsMTAyLjAyNSA2MC4yOTgsMTAxLjU3OCA1OS4wOTQsMTAwLjg4MiBMMTIuNTE4LDczLjk5MiBMMTIuNTIzLDc0LjAwNCBMMi4yNDUsNTUuMjU0IEMxLjI0NCw1My40MjcgMi4wMDQsNTEuMDM4IDMuOTQzLDQ5LjkxOCBMNTkuOTU0LDE3LjU3MyBDNjAuNjI2LDE3LjE4NSA2MS4zNSwxNy4wMDEgNjIuMDUzLDE3LjAwMSBDNjMuMzc5LDE3LjAwMSA2NC42MjUsMTcuNjYgNjUuMjgsMTguODU0IEw2NS4yODUsMTguODUxIEw2NS41MTIsMTkuMjY0IEw2NS41MDYsMTkuMjY4IEM2NS45MDksMjAuMDAzIDY2LjQwNSwyMC42OCA2Ni45ODMsMjEuMjg2IEw2Ny4yNiwyMS41NTYgQzY5LjE3NCwyMy40MDYgNzEuNzI4LDI0LjM1NyA3NC4zNzMsMjQuMzU3IEM3Ni4zMjIsMjQuMzU3IDc4LjMyMSwyMy44NCA4MC4xNDgsMjIuNzg1IEM4MC4xNjEsMjIuNzg1IDg3LjQ2NywxOC41NjYgODcuNDY3LDE4LjU2NiBDODguMTM5LDE4LjE3OCA4OC44NjMsMTcuOTk0IDg5LjU2NiwxNy45OTQgQzkwLjg5MiwxNy45OTQgOTIuMTM4LDE4LjY1MiA5Mi43OTIsMTkuODQ3IEw5Ni4wNDIsMjUuNzc1IEw5Ni4wNjQsMjUuNzU3IEwxMDIuODQ5LDI5LjY3NCBMMTAyLjc0NCwyOS40OTIgTDE0OS42MjUsMi41MjcgTTE0OS42MjUsMC44OTIgQzE0OS4zNDMsMC44OTIgMTQ5LjA2MiwwLjk2NSAxNDguODEsMS4xMSBMMTAyLjY0MSwyNy42NjYgTDk3LjIzMSwyNC41NDIgTDk0LjIyNiwxOS4wNjEgQzkzLjMxMywxNy4zOTQgOTEuNTI3LDE2LjM1OSA4OS41NjYsMTYuMzU4IEM4OC41NTUsMTYuMzU4IDg3LjU0NiwxNi42MzIgODYuNjQ5LDE3LjE1IEM4My44NzgsMTguNzUgNzkuNjg3LDIxLjE2OSA3OS4zNzQsMjEuMzQ1IEM3OS4zNTksMjEuMzUzIDc5LjM0NSwyMS4zNjEgNzkuMzMsMjEuMzY5IEM3Ny43OTgsMjIuMjU0IDc2LjA4NCwyMi43MjIgNzQuMzczLDIyLjcyMiBDNzIuMDgxLDIyLjcyMiA2OS45NTksMjEuODkgNjguMzk3LDIwLjM4IEw2OC4xNDUsMjAuMTM1IEM2Ny43MDYsMTkuNjcyIDY3LjMyMywxOS4xNTYgNjcuMDA2LDE4LjYwMSBDNjYuOTg4LDE4LjU1OSA2Ni45NjgsMTguNTE5IDY2Ljk0NiwxOC40NzkgTDY2LjcxOSwxOC4wNjUgQzY2LjY5LDE4LjAxMiA2Ni42NTgsMTcuOTYgNjYuNjI0LDE3LjkxMSBDNjUuNjg2LDE2LjMzNyA2My45NTEsMTUuMzY2IDYyLjA1MywxNS4zNjYgQzYxLjA0MiwxNS4zNjYgNjAuMDMzLDE1LjY0IDU5LjEzNiwxNi4xNTggTDMuMTI1LDQ4LjUwMiBDMC40MjYsNTAuMDYxIC0wLjYxMyw1My40NDIgMC44MTEsNTYuMDQgTDExLjA4OSw3NC43OSBDMTEuMjY2LDc1LjExMyAxMS41MzcsNzUuMzUzIDExLjg1LDc1LjQ5NCBMNTguMjc2LDEwMi4yOTggQzU5LjY3OSwxMDMuMTA4IDYxLjQzMywxMDMuNjMgNjMuMzQ4LDEwMy44MDYgQzYzLjgxMiwxMDMuODQ4IDY0LjI4NSwxMDMuODcgNjQuNzU0LDEwMy44NyBDNjUsMTAzLjg3IDY1LjI0OSwxMDMuODY0IDY1LjQ5NCwxMDMuODUyIEM2NS41NjMsMTAzLjg0OSA2NS42MzIsMTAzLjg0NyA2NS43MDEsMTAzLjg0NyBDNjUuNzY0LDEwMy44NDcgNjUuODI4LDEwMy44NDkgNjUuODksMTAzLjg1MiBDNjUuOTg2LDEwMy44NTYgNjYuMDgsMTAzLjg2MyA2Ni4xNzMsMTAzLjg3NCBDNjYuMjgyLDEwNS40NjcgNjcuMzMyLDEwNy4xOTcgNjguNzAyLDEwNy45ODggTDExMS4xNzQsMTMyLjUxIEMxMTEuNjk4LDEzMi44MTIgMTEyLjIzMiwxMzIuOTY1IDExMi43NjQsMTMyLjk2NSBDMTE0LjI2MSwxMzIuOTY1IDExNS4zNDcsMTMxLjc2NSAxMTUuMzQ3LDEzMC4xMTMgTDExNS4zNDcsMTAzLjU1MSBMMTIyLjQ1OCw5OS40NDYgQzEyMi44MTksOTkuMjM3IDEyMy4wODcsOTguODk4IDEyMy4yMDcsOTguNDk4IEwxMjcuODY1LDgyLjkwNSBDMTMyLjI3OSw4My43MDIgMTM2LjU1Nyw4NC43NTMgMTQwLjYwNyw4Ni4wMzMgTDE0MS4xNCw4Ni44NjIgQzE0MS40NTEsODcuMzQ2IDE0MS45NzcsODcuNjEzIDE0Mi41MTYsODcuNjEzIEMxNDIuNzk0LDg3LjYxMyAxNDMuMDc2LDg3LjU0MiAxNDMuMzMzLDg3LjM5MyBMMTY5Ljg2NSw3Mi4wNzYgTDE5Myw4NS40MzMgQzE5My41MjMsODUuNzM1IDE5NC4wNTgsODUuODg4IDE5NC41OSw4NS44ODggQzE5Ni4wODcsODUuODg4IDE5Ny4xNzMsODQuNjg5IDE5Ny4xNzMsODMuMDM2IEwxOTcuMTczLDI5LjAzNSBDMTk3LjE3MywyOC40NTEgMTk2Ljg2MSwyNy45MTEgMTk2LjM1NSwyNy42MTkgQzE5Ni4zNTUsMjcuNjE5IDE3MS44NDMsMTMuNDY3IDE3MC4zODUsMTIuNjI2IEMxNzAuMTMyLDEyLjQ4IDE2OS44NSwxMi40MDcgMTY5LjU2OCwxMi40MDcgQzE2OS4yODUsMTIuNDA3IDE2OS4wMDIsMTIuNDgxIDE2OC43NDksMTIuNjI3IEMxNjguMTQzLDEyLjk3OCAxNjUuNzU2LDE0LjM1NyAxNjQuNDI0LDE1LjEyNSBMMTU5LjYxNSwxMC44NyBDMTU4Ljc5NiwxMC4xNDUgMTU4LjE1NCw4LjkzNyAxNTguMDU0LDcuOTM0IEMxNTguMDQ1LDcuODM3IDE1OC4wMzQsNy43MzkgMTU4LjAyMSw3LjY0IEMxNTguMDA1LDcuNTIzIDE1Ny45OTgsNy40MSAxNTcuOTk4LDcuMzA0IEwxNTcuOTk4LDYuNDE4IEMxNTcuOTk4LDUuODM0IDE1Ny42ODYsNS4yOTUgMTU3LjE4MSw1LjAwMiBDMTU2LjYyNCw0LjY4IDE1MC40NDIsMS4xMTEgMTUwLjQ0MiwxLjExMSBDMTUwLjE4OSwwLjk2NSAxNDkuOTA3LDAuODkyIDE0OS42MjUsMC44OTIiIGlkPSJGaWxsLTEiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTYuMDI3LDI1LjYzNiBMMTQyLjYwMyw1Mi41MjcgQzE0My44MDcsNTMuMjIyIDE0NC41ODIsNTQuMTE0IDE0NC44NDUsNTUuMDY4IEwxNDQuODM1LDU1LjA3NSBMNjMuNDYxLDEwMi4wNTcgTDYzLjQ2LDEwMi4wNTcgQzYxLjgwNiwxMDEuOTA1IDYwLjI2MSwxMDEuNDU3IDU5LjA1NywxMDAuNzYyIEwxMi40ODEsNzMuODcxIEw5Ni4wMjcsMjUuNjM2IiBpZD0iRmlsbC0yIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYzLjQ2MSwxMDIuMTc0IEM2My40NTMsMTAyLjE3NCA2My40NDYsMTAyLjE3NCA2My40MzksMTAyLjE3MiBDNjEuNzQ2LDEwMi4wMTYgNjAuMjExLDEwMS41NjMgNTguOTk4LDEwMC44NjMgTDEyLjQyMiw3My45NzMgQzEyLjM4Niw3My45NTIgMTIuMzY0LDczLjkxNCAxMi4zNjQsNzMuODcxIEMxMi4zNjQsNzMuODMgMTIuMzg2LDczLjc5MSAxMi40MjIsNzMuNzcgTDk1Ljk2OCwyNS41MzUgQzk2LjAwNCwyNS41MTQgOTYuMDQ5LDI1LjUxNCA5Ni4wODUsMjUuNTM1IEwxNDIuNjYxLDUyLjQyNiBDMTQzLjg4OCw1My4xMzQgMTQ0LjY4Miw1NC4wMzggMTQ0Ljk1Nyw1NS4wMzcgQzE0NC45Nyw1NS4wODMgMTQ0Ljk1Myw1NS4xMzMgMTQ0LjkxNSw1NS4xNjEgQzE0NC45MTEsNTUuMTY1IDE0NC44OTgsNTUuMTc0IDE0NC44OTQsNTUuMTc3IEw2My41MTksMTAyLjE1OCBDNjMuNTAxLDEwMi4xNjkgNjMuNDgxLDEwMi4xNzQgNjMuNDYxLDEwMi4xNzQgTDYzLjQ2MSwxMDIuMTc0IFogTTEyLjcxNCw3My44NzEgTDU5LjExNSwxMDAuNjYxIEM2MC4yOTMsMTAxLjM0MSA2MS43ODYsMTAxLjc4MiA2My40MzUsMTAxLjkzNyBMMTQ0LjcwNyw1NS4wMTUgQzE0NC40MjgsNTQuMTA4IDE0My42ODIsNTMuMjg1IDE0Mi41NDQsNTIuNjI4IEw5Ni4wMjcsMjUuNzcxIEwxMi43MTQsNzMuODcxIEwxMi43MTQsNzMuODcxIFoiIGlkPSJGaWxsLTMiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ4LjMyNyw1OC40NzEgQzE0OC4xNDUsNTguNDggMTQ3Ljk2Miw1OC40OCAxNDcuNzgxLDU4LjQ3MiBDMTQ1Ljg4Nyw1OC4zODkgMTQ0LjQ3OSw1Ny40MzQgMTQ0LjYzNiw1Ni4zNCBDMTQ0LjY4OSw1NS45NjcgMTQ0LjY2NCw1NS41OTcgMTQ0LjU2NCw1NS4yMzUgTDYzLjQ2MSwxMDIuMDU3IEM2NC4wODksMTAyLjExNSA2NC43MzMsMTAyLjEzIDY1LjM3OSwxMDIuMDk5IEM2NS41NjEsMTAyLjA5IDY1Ljc0MywxMDIuMDkgNjUuOTI1LDEwMi4wOTggQzY3LjgxOSwxMDIuMTgxIDY5LjIyNywxMDMuMTM2IDY5LjA3LDEwNC4yMyBMMTQ4LjMyNyw1OC40NzEiIGlkPSJGaWxsLTQiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNjkuMDcsMTA0LjM0NyBDNjkuMDQ4LDEwNC4zNDcgNjkuMDI1LDEwNC4zNCA2OS4wMDUsMTA0LjMyNyBDNjguOTY4LDEwNC4zMDEgNjguOTQ4LDEwNC4yNTcgNjguOTU1LDEwNC4yMTMgQzY5LDEwMy44OTYgNjguODk4LDEwMy41NzYgNjguNjU4LDEwMy4yODggQzY4LjE1MywxMDIuNjc4IDY3LjEwMywxMDIuMjY2IDY1LjkyLDEwMi4yMTQgQzY1Ljc0MiwxMDIuMjA2IDY1LjU2MywxMDIuMjA3IDY1LjM4NSwxMDIuMjE1IEM2NC43NDIsMTAyLjI0NiA2NC4wODcsMTAyLjIzMiA2My40NSwxMDIuMTc0IEM2My4zOTksMTAyLjE2OSA2My4zNTgsMTAyLjEzMiA2My4zNDcsMTAyLjA4MiBDNjMuMzM2LDEwMi4wMzMgNjMuMzU4LDEwMS45ODEgNjMuNDAyLDEwMS45NTYgTDE0NC41MDYsNTUuMTM0IEMxNDQuNTM3LDU1LjExNiAxNDQuNTc1LDU1LjExMyAxNDQuNjA5LDU1LjEyNyBDMTQ0LjY0Miw1NS4xNDEgMTQ0LjY2OCw1NS4xNyAxNDQuNjc3LDU1LjIwNCBDMTQ0Ljc4MSw1NS41ODUgMTQ0LjgwNiw1NS45NzIgMTQ0Ljc1MSw1Ni4zNTcgQzE0NC43MDYsNTYuNjczIDE0NC44MDgsNTYuOTk0IDE0NS4wNDcsNTcuMjgyIEMxNDUuNTUzLDU3Ljg5MiAxNDYuNjAyLDU4LjMwMyAxNDcuNzg2LDU4LjM1NSBDMTQ3Ljk2NCw1OC4zNjMgMTQ4LjE0Myw1OC4zNjMgMTQ4LjMyMSw1OC4zNTQgQzE0OC4zNzcsNTguMzUyIDE0OC40MjQsNTguMzg3IDE0OC40MzksNTguNDM4IEMxNDguNDU0LDU4LjQ5IDE0OC40MzIsNTguNTQ1IDE0OC4zODUsNTguNTcyIEw2OS4xMjksMTA0LjMzMSBDNjkuMTExLDEwNC4zNDIgNjkuMDksMTA0LjM0NyA2OS4wNywxMDQuMzQ3IEw2OS4wNywxMDQuMzQ3IFogTTY1LjY2NSwxMDEuOTc1IEM2NS43NTQsMTAxLjk3NSA2NS44NDIsMTAxLjk3NyA2NS45MywxMDEuOTgxIEM2Ny4xOTYsMTAyLjAzNyA2OC4yODMsMTAyLjQ2OSA2OC44MzgsMTAzLjEzOSBDNjkuMDY1LDEwMy40MTMgNjkuMTg4LDEwMy43MTQgNjkuMTk4LDEwNC4wMjEgTDE0Ny44ODMsNTguNTkyIEMxNDcuODQ3LDU4LjU5MiAxNDcuODExLDU4LjU5MSAxNDcuNzc2LDU4LjU4OSBDMTQ2LjUwOSw1OC41MzMgMTQ1LjQyMiw1OC4xIDE0NC44NjcsNTcuNDMxIEMxNDQuNTg1LDU3LjA5MSAxNDQuNDY1LDU2LjcwNyAxNDQuNTIsNTYuMzI0IEMxNDQuNTYzLDU2LjAyMSAxNDQuNTUyLDU1LjcxNiAxNDQuNDg4LDU1LjQxNCBMNjMuODQ2LDEwMS45NyBDNjQuMzUzLDEwMi4wMDIgNjQuODY3LDEwMi4wMDYgNjUuMzc0LDEwMS45ODIgQzY1LjQ3MSwxMDEuOTc3IDY1LjU2OCwxMDEuOTc1IDY1LjY2NSwxMDEuOTc1IEw2NS42NjUsMTAxLjk3NSBaIiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTIuMjA4LDU1LjEzNCBDMS4yMDcsNTMuMzA3IDEuOTY3LDUwLjkxNyAzLjkwNiw0OS43OTcgTDU5LjkxNywxNy40NTMgQzYxLjg1NiwxNi4zMzMgNjQuMjQxLDE2LjkwNyA2NS4yNDMsMTguNzM0IEw2NS40NzUsMTkuMTQ0IEM2NS44NzIsMTkuODgyIDY2LjM2OCwyMC41NiA2Ni45NDUsMjEuMTY1IEw2Ny4yMjMsMjEuNDM1IEM3MC41NDgsMjQuNjQ5IDc1LjgwNiwyNS4xNTEgODAuMTExLDIyLjY2NSBMODcuNDMsMTguNDQ1IEM4OS4zNywxNy4zMjYgOTEuNzU0LDE3Ljg5OSA5Mi43NTUsMTkuNzI3IEw5Ni4wMDUsMjUuNjU1IEwxMi40ODYsNzMuODg0IEwyLjIwOCw1NS4xMzQgWiIgaWQ9IkZpbGwtNiIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMi40ODYsNzQuMDAxIEMxMi40NzYsNzQuMDAxIDEyLjQ2NSw3My45OTkgMTIuNDU1LDczLjk5NiBDMTIuNDI0LDczLjk4OCAxMi4zOTksNzMuOTY3IDEyLjM4NCw3My45NCBMMi4xMDYsNTUuMTkgQzEuMDc1LDUzLjMxIDEuODU3LDUwLjg0NSAzLjg0OCw0OS42OTYgTDU5Ljg1OCwxNy4zNTIgQzYwLjUyNSwxNi45NjcgNjEuMjcxLDE2Ljc2NCA2Mi4wMTYsMTYuNzY0IEM2My40MzEsMTYuNzY0IDY0LjY2NiwxNy40NjYgNjUuMzI3LDE4LjY0NiBDNjUuMzM3LDE4LjY1NCA2NS4zNDUsMTguNjYzIDY1LjM1MSwxOC42NzQgTDY1LjU3OCwxOS4wODggQzY1LjU4NCwxOS4xIDY1LjU4OSwxOS4xMTIgNjUuNTkxLDE5LjEyNiBDNjUuOTg1LDE5LjgzOCA2Ni40NjksMjAuNDk3IDY3LjAzLDIxLjA4NSBMNjcuMzA1LDIxLjM1MSBDNjkuMTUxLDIzLjEzNyA3MS42NDksMjQuMTIgNzQuMzM2LDI0LjEyIEM3Ni4zMTMsMjQuMTIgNzguMjksMjMuNTgyIDgwLjA1MywyMi41NjMgQzgwLjA2NCwyMi41NTcgODAuMDc2LDIyLjU1MyA4MC4wODgsMjIuNTUgTDg3LjM3MiwxOC4zNDQgQzg4LjAzOCwxNy45NTkgODguNzg0LDE3Ljc1NiA4OS41MjksMTcuNzU2IEM5MC45NTYsMTcuNzU2IDkyLjIwMSwxOC40NzIgOTIuODU4LDE5LjY3IEw5Ni4xMDcsMjUuNTk5IEM5Ni4xMzgsMjUuNjU0IDk2LjExOCwyNS43MjQgOTYuMDYzLDI1Ljc1NiBMMTIuNTQ1LDczLjk4NSBDMTIuNTI2LDczLjk5NiAxMi41MDYsNzQuMDAxIDEyLjQ4Niw3NC4wMDEgTDEyLjQ4Niw3NC4wMDEgWiBNNjIuMDE2LDE2Ljk5NyBDNjEuMzEyLDE2Ljk5NyA2MC42MDYsMTcuMTkgNTkuOTc1LDE3LjU1NCBMMy45NjUsNDkuODk5IEMyLjA4Myw1MC45ODUgMS4zNDEsNTMuMzA4IDIuMzEsNTUuMDc4IEwxMi41MzEsNzMuNzIzIEw5NS44NDgsMjUuNjExIEw5Mi42NTMsMTkuNzgyIEM5Mi4wMzgsMTguNjYgOTAuODcsMTcuOTkgODkuNTI5LDE3Ljk5IEM4OC44MjUsMTcuOTkgODguMTE5LDE4LjE4MiA4Ny40ODksMTguNTQ3IEw4MC4xNzIsMjIuNzcyIEM4MC4xNjEsMjIuNzc4IDgwLjE0OSwyMi43ODIgODAuMTM3LDIyLjc4NSBDNzguMzQ2LDIzLjgxMSA3Ni4zNDEsMjQuMzU0IDc0LjMzNiwyNC4zNTQgQzcxLjU4OCwyNC4zNTQgNjkuMDMzLDIzLjM0NyA2Ny4xNDIsMjEuNTE5IEw2Ni44NjQsMjEuMjQ5IEM2Ni4yNzcsMjAuNjM0IDY1Ljc3NCwxOS45NDcgNjUuMzY3LDE5LjIwMyBDNjUuMzYsMTkuMTkyIDY1LjM1NiwxOS4xNzkgNjUuMzU0LDE5LjE2NiBMNjUuMTYzLDE4LjgxOSBDNjUuMTU0LDE4LjgxMSA2NS4xNDYsMTguODAxIDY1LjE0LDE4Ljc5IEM2NC41MjUsMTcuNjY3IDYzLjM1NywxNi45OTcgNjIuMDE2LDE2Ljk5NyBMNjIuMDE2LDE2Ljk5NyBaIiBpZD0iRmlsbC03IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTQyLjQzNCw0OC44MDggTDQyLjQzNCw0OC44MDggQzM5LjkyNCw0OC44MDcgMzcuNzM3LDQ3LjU1IDM2LjU4Miw0NS40NDMgQzM0Ljc3MSw0Mi4xMzkgMzYuMTQ0LDM3LjgwOSAzOS42NDEsMzUuNzg5IEw1MS45MzIsMjguNjkxIEM1My4xMDMsMjguMDE1IDU0LjQxMywyNy42NTggNTUuNzIxLDI3LjY1OCBDNTguMjMxLDI3LjY1OCA2MC40MTgsMjguOTE2IDYxLjU3MywzMS4wMjMgQzYzLjM4NCwzNC4zMjcgNjIuMDEyLDM4LjY1NyA1OC41MTQsNDAuNjc3IEw0Ni4yMjMsNDcuNzc1IEM0NS4wNTMsNDguNDUgNDMuNzQyLDQ4LjgwOCA0Mi40MzQsNDguODA4IEw0Mi40MzQsNDguODA4IFogTTU1LjcyMSwyOC4xMjUgQzU0LjQ5NSwyOC4xMjUgNTMuMjY1LDI4LjQ2MSA1Mi4xNjYsMjkuMDk2IEwzOS44NzUsMzYuMTk0IEMzNi41OTYsMzguMDg3IDM1LjMwMiw0Mi4xMzYgMzYuOTkyLDQ1LjIxOCBDMzguMDYzLDQ3LjE3MyA0MC4wOTgsNDguMzQgNDIuNDM0LDQ4LjM0IEM0My42NjEsNDguMzQgNDQuODksNDguMDA1IDQ1Ljk5LDQ3LjM3IEw1OC4yODEsNDAuMjcyIEM2MS41NiwzOC4zNzkgNjIuODUzLDM0LjMzIDYxLjE2NCwzMS4yNDggQzYwLjA5MiwyOS4yOTMgNTguMDU4LDI4LjEyNSA1NS43MjEsMjguMTI1IEw1NS43MjEsMjguMTI1IFoiIGlkPSJGaWxsLTgiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjU4OCwyLjQwNyBDMTQ5LjU4OCwyLjQwNyAxNTUuNzY4LDUuOTc1IDE1Ni4zMjUsNi4yOTcgTDE1Ni4zMjUsNy4xODQgQzE1Ni4zMjUsNy4zNiAxNTYuMzM4LDcuNTQ0IDE1Ni4zNjIsNy43MzMgQzE1Ni4zNzMsNy44MTQgMTU2LjM4Miw3Ljg5NCAxNTYuMzksNy45NzUgQzE1Ni41Myw5LjM5IDE1Ny4zNjMsMTAuOTczIDE1OC40OTUsMTEuOTc0IEwxNjUuODkxLDE4LjUxOSBDMTY2LjA2OCwxOC42NzUgMTY2LjI0OSwxOC44MTQgMTY2LjQzMiwxOC45MzQgQzE2OC4wMTEsMTkuOTc0IDE2OS4zODIsMTkuNCAxNjkuNDk0LDE3LjY1MiBDMTY5LjU0MywxNi44NjggMTY5LjU1MSwxNi4wNTcgMTY5LjUxNywxNS4yMjMgTDE2OS41MTQsMTUuMDYzIEwxNjkuNTE0LDEzLjkxMiBDMTcwLjc4LDE0LjY0MiAxOTUuNTAxLDI4LjkxNSAxOTUuNTAxLDI4LjkxNSBMMTk1LjUwMSw4Mi45MTUgQzE5NS41MDEsODQuMDA1IDE5NC43MzEsODQuNDQ1IDE5My43ODEsODMuODk3IEwxNTEuMzA4LDU5LjM3NCBDMTUwLjM1OCw1OC44MjYgMTQ5LjU4OCw1Ny40OTcgMTQ5LjU4OCw1Ni40MDggTDE0OS41ODgsMjIuMzc1IiBpZD0iRmlsbC05IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE5NC41NTMsODQuMjUgQzE5NC4yOTYsODQuMjUgMTk0LjAxMyw4NC4xNjUgMTkzLjcyMiw4My45OTcgTDE1MS4yNSw1OS40NzYgQzE1MC4yNjksNTguOTA5IDE0OS40NzEsNTcuNTMzIDE0OS40NzEsNTYuNDA4IEwxNDkuNDcxLDIyLjM3NSBMMTQ5LjcwNSwyMi4zNzUgTDE0OS43MDUsNTYuNDA4IEMxNDkuNzA1LDU3LjQ1OSAxNTAuNDUsNTguNzQ0IDE1MS4zNjYsNTkuMjc0IEwxOTMuODM5LDgzLjc5NSBDMTk0LjI2Myw4NC4wNCAxOTQuNjU1LDg0LjA4MyAxOTQuOTQyLDgzLjkxNyBDMTk1LjIyNyw4My43NTMgMTk1LjM4NCw4My4zOTcgMTk1LjM4NCw4Mi45MTUgTDE5NS4zODQsMjguOTgyIEMxOTQuMTAyLDI4LjI0MiAxNzIuMTA0LDE1LjU0MiAxNjkuNjMxLDE0LjExNCBMMTY5LjYzNCwxNS4yMiBDMTY5LjY2OCwxNi4wNTIgMTY5LjY2LDE2Ljg3NCAxNjkuNjEsMTcuNjU5IEMxNjkuNTU2LDE4LjUwMyAxNjkuMjE0LDE5LjEyMyAxNjguNjQ3LDE5LjQwNSBDMTY4LjAyOCwxOS43MTQgMTY3LjE5NywxOS41NzggMTY2LjM2NywxOS4wMzIgQzE2Ni4xODEsMTguOTA5IDE2NS45OTUsMTguNzY2IDE2NS44MTQsMTguNjA2IEwxNTguNDE3LDEyLjA2MiBDMTU3LjI1OSwxMS4wMzYgMTU2LjQxOCw5LjQzNyAxNTYuMjc0LDcuOTg2IEMxNTYuMjY2LDcuOTA3IDE1Ni4yNTcsNy44MjcgMTU2LjI0Nyw3Ljc0OCBDMTU2LjIyMSw3LjU1NSAxNTYuMjA5LDcuMzY1IDE1Ni4yMDksNy4xODQgTDE1Ni4yMDksNi4zNjQgQzE1NS4zNzUsNS44ODMgMTQ5LjUyOSwyLjUwOCAxNDkuNTI5LDIuNTA4IEwxNDkuNjQ2LDIuMzA2IEMxNDkuNjQ2LDIuMzA2IDE1NS44MjcsNS44NzQgMTU2LjM4NCw2LjE5NiBMMTU2LjQ0Miw2LjIzIEwxNTYuNDQyLDcuMTg0IEMxNTYuNDQyLDcuMzU1IDE1Ni40NTQsNy41MzUgMTU2LjQ3OCw3LjcxNyBDMTU2LjQ4OSw3LjggMTU2LjQ5OSw3Ljg4MiAxNTYuNTA3LDcuOTYzIEMxNTYuNjQ1LDkuMzU4IDE1Ny40NTUsMTAuODk4IDE1OC41NzIsMTEuODg2IEwxNjUuOTY5LDE4LjQzMSBDMTY2LjE0MiwxOC41ODQgMTY2LjMxOSwxOC43MiAxNjYuNDk2LDE4LjgzNyBDMTY3LjI1NCwxOS4zMzYgMTY4LDE5LjQ2NyAxNjguNTQzLDE5LjE5NiBDMTY5LjAzMywxOC45NTMgMTY5LjMyOSwxOC40MDEgMTY5LjM3NywxNy42NDUgQzE2OS40MjcsMTYuODY3IDE2OS40MzQsMTYuMDU0IDE2OS40MDEsMTUuMjI4IEwxNjkuMzk3LDE1LjA2NSBMMTY5LjM5NywxMy43MSBMMTY5LjU3MiwxMy44MSBDMTcwLjgzOSwxNC41NDEgMTk1LjU1OSwyOC44MTQgMTk1LjU1OSwyOC44MTQgTDE5NS42MTgsMjguODQ3IEwxOTUuNjE4LDgyLjkxNSBDMTk1LjYxOCw4My40ODQgMTk1LjQyLDgzLjkxMSAxOTUuMDU5LDg0LjExOSBDMTk0LjkwOCw4NC4yMDYgMTk0LjczNyw4NC4yNSAxOTQuNTUzLDg0LjI1IiBpZD0iRmlsbC0xMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDUuNjg1LDU2LjE2MSBMMTY5LjgsNzAuMDgzIEwxNDMuODIyLDg1LjA4MSBMMTQyLjM2LDg0Ljc3NCBDMTM1LjgyNiw4Mi42MDQgMTI4LjczMiw4MS4wNDYgMTIxLjM0MSw4MC4xNTggQzExNi45NzYsNzkuNjM0IDExMi42NzgsODEuMjU0IDExMS43NDMsODMuNzc4IEMxMTEuNTA2LDg0LjQxNCAxMTEuNTAzLDg1LjA3MSAxMTEuNzMyLDg1LjcwNiBDMTEzLjI3LDg5Ljk3MyAxMTUuOTY4LDk0LjA2OSAxMTkuNzI3LDk3Ljg0MSBMMTIwLjI1OSw5OC42ODYgQzEyMC4yNiw5OC42ODUgOTQuMjgyLDExMy42ODMgOTQuMjgyLDExMy42ODMgTDcwLjE2Nyw5OS43NjEgTDE0NS42ODUsNTYuMTYxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik05NC4yODIsMTEzLjgxOCBMOTQuMjIzLDExMy43ODUgTDY5LjkzMyw5OS43NjEgTDcwLjEwOCw5OS42NiBMMTQ1LjY4NSw1Ni4wMjYgTDE0NS43NDMsNTYuMDU5IEwxNzAuMDMzLDcwLjA4MyBMMTQzLjg0Miw4NS4yMDUgTDE0My43OTcsODUuMTk1IEMxNDMuNzcyLDg1LjE5IDE0Mi4zMzYsODQuODg4IDE0Mi4zMzYsODQuODg4IEMxMzUuNzg3LDgyLjcxNCAxMjguNzIzLDgxLjE2MyAxMjEuMzI3LDgwLjI3NCBDMTIwLjc4OCw4MC4yMDkgMTIwLjIzNiw4MC4xNzcgMTE5LjY4OSw4MC4xNzcgQzExNS45MzEsODAuMTc3IDExMi42MzUsODEuNzA4IDExMS44NTIsODMuODE5IEMxMTEuNjI0LDg0LjQzMiAxMTEuNjIxLDg1LjA1MyAxMTEuODQyLDg1LjY2NyBDMTEzLjM3Nyw4OS45MjUgMTE2LjA1OCw5My45OTMgMTE5LjgxLDk3Ljc1OCBMMTE5LjgyNiw5Ny43NzkgTDEyMC4zNTIsOTguNjE0IEMxMjAuMzU0LDk4LjYxNyAxMjAuMzU2LDk4LjYyIDEyMC4zNTgsOTguNjI0IEwxMjAuNDIyLDk4LjcyNiBMMTIwLjMxNyw5OC43ODcgQzEyMC4yNjQsOTguODE4IDk0LjU5OSwxMTMuNjM1IDk0LjM0LDExMy43ODUgTDk0LjI4MiwxMTMuODE4IEw5NC4yODIsMTEzLjgxOCBaIE03MC40MDEsOTkuNzYxIEw5NC4yODIsMTEzLjU0OSBMMTE5LjA4NCw5OS4yMjkgQzExOS42Myw5OC45MTQgMTE5LjkzLDk4Ljc0IDEyMC4xMDEsOTguNjU0IEwxMTkuNjM1LDk3LjkxNCBDMTE1Ljg2NCw5NC4xMjcgMTEzLjE2OCw5MC4wMzMgMTExLjYyMiw4NS43NDYgQzExMS4zODIsODUuMDc5IDExMS4zODYsODQuNDA0IDExMS42MzMsODMuNzM4IEMxMTIuNDQ4LDgxLjUzOSAxMTUuODM2LDc5Ljk0MyAxMTkuNjg5LDc5Ljk0MyBDMTIwLjI0Niw3OS45NDMgMTIwLjgwNiw3OS45NzYgMTIxLjM1NSw4MC4wNDIgQzEyOC43NjcsODAuOTMzIDEzNS44NDYsODIuNDg3IDE0Mi4zOTYsODQuNjYzIEMxNDMuMjMyLDg0LjgzOCAxNDMuNjExLDg0LjkxNyAxNDMuNzg2LDg0Ljk2NyBMMTY5LjU2Niw3MC4wODMgTDE0NS42ODUsNTYuMjk1IEw3MC40MDEsOTkuNzYxIEw3MC40MDEsOTkuNzYxIFoiIGlkPSJGaWxsLTEyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2Ny4yMywxOC45NzkgTDE2Ny4yMyw2OS44NSBMMTM5LjkwOSw4NS42MjMgTDEzMy40NDgsNzEuNDU2IEMxMzIuNTM4LDY5LjQ2IDEzMC4wMiw2OS43MTggMTI3LjgyNCw3Mi4wMyBDMTI2Ljc2OSw3My4xNCAxMjUuOTMxLDc0LjU4NSAxMjUuNDk0LDc2LjA0OCBMMTE5LjAzNCw5Ny42NzYgTDkxLjcxMiwxMTMuNDUgTDkxLjcxMiw2Mi41NzkgTDE2Ny4yMywxOC45NzkiIGlkPSJGaWxsLTEzIiBmaWxsPSIjRkZGRkZGIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTkxLjcxMiwxMTMuNTY3IEM5MS42OTIsMTEzLjU2NyA5MS42NzIsMTEzLjU2MSA5MS42NTMsMTEzLjU1MSBDOTEuNjE4LDExMy41MyA5MS41OTUsMTEzLjQ5MiA5MS41OTUsMTEzLjQ1IEw5MS41OTUsNjIuNTc5IEM5MS41OTUsNjIuNTM3IDkxLjYxOCw2Mi40OTkgOTEuNjUzLDYyLjQ3OCBMMTY3LjE3MiwxOC44NzggQzE2Ny4yMDgsMTguODU3IDE2Ny4yNTIsMTguODU3IDE2Ny4yODgsMTguODc4IEMxNjcuMzI0LDE4Ljg5OSAxNjcuMzQ3LDE4LjkzNyAxNjcuMzQ3LDE4Ljk3OSBMMTY3LjM0Nyw2OS44NSBDMTY3LjM0Nyw2OS44OTEgMTY3LjMyNCw2OS45MyAxNjcuMjg4LDY5Ljk1IEwxMzkuOTY3LDg1LjcyNSBDMTM5LjkzOSw4NS43NDEgMTM5LjkwNSw4NS43NDUgMTM5Ljg3Myw4NS43MzUgQzEzOS44NDIsODUuNzI1IDEzOS44MTYsODUuNzAyIDEzOS44MDIsODUuNjcyIEwxMzMuMzQyLDcxLjUwNCBDMTMyLjk2Nyw3MC42ODIgMTMyLjI4LDcwLjIyOSAxMzEuNDA4LDcwLjIyOSBDMTMwLjMxOSw3MC4yMjkgMTI5LjA0NCw3MC45MTUgMTI3LjkwOCw3Mi4xMSBDMTI2Ljg3NCw3My4yIDEyNi4wMzQsNzQuNjQ3IDEyNS42MDYsNzYuMDgyIEwxMTkuMTQ2LDk3LjcwOSBDMTE5LjEzNyw5Ny43MzggMTE5LjExOCw5Ny43NjIgMTE5LjA5Miw5Ny43NzcgTDkxLjc3LDExMy41NTEgQzkxLjc1MiwxMTMuNTYxIDkxLjczMiwxMTMuNTY3IDkxLjcxMiwxMTMuNTY3IEw5MS43MTIsMTEzLjU2NyBaIE05MS44MjksNjIuNjQ3IEw5MS44MjksMTEzLjI0OCBMMTE4LjkzNSw5Ny41OTggTDEyNS4zODIsNzYuMDE1IEMxMjUuODI3LDc0LjUyNSAxMjYuNjY0LDczLjA4MSAxMjcuNzM5LDcxLjk1IEMxMjguOTE5LDcwLjcwOCAxMzAuMjU2LDY5Ljk5NiAxMzEuNDA4LDY5Ljk5NiBDMTMyLjM3Nyw2OS45OTYgMTMzLjEzOSw3MC40OTcgMTMzLjU1NCw3MS40MDcgTDEzOS45NjEsODUuNDU4IEwxNjcuMTEzLDY5Ljc4MiBMMTY3LjExMywxOS4xODEgTDkxLjgyOSw2Mi42NDcgTDkxLjgyOSw2Mi42NDcgWiIgaWQ9IkZpbGwtMTQiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTY4LjU0MywxOS4yMTMgTDE2OC41NDMsNzAuMDgzIEwxNDEuMjIxLDg1Ljg1NyBMMTM0Ljc2MSw3MS42ODkgQzEzMy44NTEsNjkuNjk0IDEzMS4zMzMsNjkuOTUxIDEyOS4xMzcsNzIuMjYzIEMxMjguMDgyLDczLjM3NCAxMjcuMjQ0LDc0LjgxOSAxMjYuODA3LDc2LjI4MiBMMTIwLjM0Niw5Ny45MDkgTDkzLjAyNSwxMTMuNjgzIEw5My4wMjUsNjIuODEzIEwxNjguNTQzLDE5LjIxMyIgaWQ9IkZpbGwtMTUiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTMuMDI1LDExMy44IEM5My4wMDUsMTEzLjggOTIuOTg0LDExMy43OTUgOTIuOTY2LDExMy43ODUgQzkyLjkzMSwxMTMuNzY0IDkyLjkwOCwxMTMuNzI1IDkyLjkwOCwxMTMuNjg0IEw5Mi45MDgsNjIuODEzIEM5Mi45MDgsNjIuNzcxIDkyLjkzMSw2Mi43MzMgOTIuOTY2LDYyLjcxMiBMMTY4LjQ4NCwxOS4xMTIgQzE2OC41MiwxOS4wOSAxNjguNTY1LDE5LjA5IDE2OC42MDEsMTkuMTEyIEMxNjguNjM3LDE5LjEzMiAxNjguNjYsMTkuMTcxIDE2OC42NiwxOS4yMTIgTDE2OC42Niw3MC4wODMgQzE2OC42Niw3MC4xMjUgMTY4LjYzNyw3MC4xNjQgMTY4LjYwMSw3MC4xODQgTDE0MS4yOCw4NS45NTggQzE0MS4yNTEsODUuOTc1IDE0MS4yMTcsODUuOTc5IDE0MS4xODYsODUuOTY4IEMxNDEuMTU0LDg1Ljk1OCAxNDEuMTI5LDg1LjkzNiAxNDEuMTE1LDg1LjkwNiBMMTM0LjY1NSw3MS43MzggQzEzNC4yOCw3MC45MTUgMTMzLjU5Myw3MC40NjMgMTMyLjcyLDcwLjQ2MyBDMTMxLjYzMiw3MC40NjMgMTMwLjM1Nyw3MS4xNDggMTI5LjIyMSw3Mi4zNDQgQzEyOC4xODYsNzMuNDMzIDEyNy4zNDcsNzQuODgxIDEyNi45MTksNzYuMzE1IEwxMjAuNDU4LDk3Ljk0MyBDMTIwLjQ1LDk3Ljk3MiAxMjAuNDMxLDk3Ljk5NiAxMjAuNDA1LDk4LjAxIEw5My4wODMsMTEzLjc4NSBDOTMuMDY1LDExMy43OTUgOTMuMDQ1LDExMy44IDkzLjAyNSwxMTMuOCBMOTMuMDI1LDExMy44IFogTTkzLjE0Miw2Mi44ODEgTDkzLjE0MiwxMTMuNDgxIEwxMjAuMjQ4LDk3LjgzMiBMMTI2LjY5NSw3Ni4yNDggQzEyNy4xNCw3NC43NTggMTI3Ljk3Nyw3My4zMTUgMTI5LjA1Miw3Mi4xODMgQzEzMC4yMzEsNzAuOTQyIDEzMS41NjgsNzAuMjI5IDEzMi43Miw3MC4yMjkgQzEzMy42ODksNzAuMjI5IDEzNC40NTIsNzAuNzMxIDEzNC44NjcsNzEuNjQxIEwxNDEuMjc0LDg1LjY5MiBMMTY4LjQyNiw3MC4wMTYgTDE2OC40MjYsMTkuNDE1IEw5My4xNDIsNjIuODgxIEw5My4xNDIsNjIuODgxIFoiIGlkPSJGaWxsLTE2IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS44LDcwLjA4MyBMMTQyLjQ3OCw4NS44NTcgTDEzNi4wMTgsNzEuNjg5IEMxMzUuMTA4LDY5LjY5NCAxMzIuNTksNjkuOTUxIDEzMC4zOTMsNzIuMjYzIEMxMjkuMzM5LDczLjM3NCAxMjguNSw3NC44MTkgMTI4LjA2NCw3Ni4yODIgTDEyMS42MDMsOTcuOTA5IEw5NC4yODIsMTEzLjY4MyBMOTQuMjgyLDYyLjgxMyBMMTY5LjgsMTkuMjEzIEwxNjkuOCw3MC4wODMgWiIgaWQ9IkZpbGwtMTciIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTQuMjgyLDExMy45MTcgQzk0LjI0MSwxMTMuOTE3IDk0LjIwMSwxMTMuOTA3IDk0LjE2NSwxMTMuODg2IEM5NC4wOTMsMTEzLjg0NSA5NC4wNDgsMTEzLjc2NyA5NC4wNDgsMTEzLjY4NCBMOTQuMDQ4LDYyLjgxMyBDOTQuMDQ4LDYyLjczIDk0LjA5Myw2Mi42NTIgOTQuMTY1LDYyLjYxMSBMMTY5LjY4MywxOS4wMSBDMTY5Ljc1NSwxOC45NjkgMTY5Ljg0NCwxOC45NjkgMTY5LjkxNywxOS4wMSBDMTY5Ljk4OSwxOS4wNTIgMTcwLjAzMywxOS4xMjkgMTcwLjAzMywxOS4yMTIgTDE3MC4wMzMsNzAuMDgzIEMxNzAuMDMzLDcwLjE2NiAxNjkuOTg5LDcwLjI0NCAxNjkuOTE3LDcwLjI4NSBMMTQyLjU5NSw4Ni4wNiBDMTQyLjUzOCw4Ni4wOTIgMTQyLjQ2OSw4Ni4xIDE0Mi40MDcsODYuMDggQzE0Mi4zNDQsODYuMDYgMTQyLjI5Myw4Ni4wMTQgMTQyLjI2Niw4NS45NTQgTDEzNS44MDUsNzEuNzg2IEMxMzUuNDQ1LDcwLjk5NyAxMzQuODEzLDcwLjU4IDEzMy45NzcsNzAuNTggQzEzMi45MjEsNzAuNTggMTMxLjY3Niw3MS4yNTIgMTMwLjU2Miw3Mi40MjQgQzEyOS41NCw3My41MDEgMTI4LjcxMSw3NC45MzEgMTI4LjI4Nyw3Ni4zNDggTDEyMS44MjcsOTcuOTc2IEMxMjEuODEsOTguMDM0IDEyMS43NzEsOTguMDgyIDEyMS43Miw5OC4xMTIgTDk0LjM5OCwxMTMuODg2IEM5NC4zNjIsMTEzLjkwNyA5NC4zMjIsMTEzLjkxNyA5NC4yODIsMTEzLjkxNyBMOTQuMjgyLDExMy45MTcgWiBNOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDExMy4yNzkgTDEyMS40MDYsOTcuNzU0IEwxMjcuODQsNzYuMjE1IEMxMjguMjksNzQuNzA4IDEyOS4xMzcsNzMuMjQ3IDEzMC4yMjQsNzIuMTAzIEMxMzEuNDI1LDcwLjgzOCAxMzIuNzkzLDcwLjExMiAxMzMuOTc3LDcwLjExMiBDMTM0Ljk5NSw3MC4xMTIgMTM1Ljc5NSw3MC42MzggMTM2LjIzLDcxLjU5MiBMMTQyLjU4NCw4NS41MjYgTDE2OS41NjYsNjkuOTQ4IEwxNjkuNTY2LDE5LjYxNyBMOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDYyLjk0OCBaIiBpZD0iRmlsbC0xOCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMDkuODk0LDkyLjk0MyBMMTA5Ljg5NCw5Mi45NDMgQzEwOC4xMiw5Mi45NDMgMTA2LjY1Myw5Mi4yMTggMTA1LjY1LDkwLjgyMyBDMTA1LjU4Myw5MC43MzEgMTA1LjU5Myw5MC42MSAxMDUuNjczLDkwLjUyOSBDMTA1Ljc1Myw5MC40NDggMTA1Ljg4LDkwLjQ0IDEwNS45NzQsOTAuNTA2IEMxMDYuNzU0LDkxLjA1MyAxMDcuNjc5LDkxLjMzMyAxMDguNzI0LDkxLjMzMyBDMTEwLjA0Nyw5MS4zMzMgMTExLjQ3OCw5MC44OTQgMTEyLjk4LDkwLjAyNyBDMTE4LjI5MSw4Ni45NiAxMjIuNjExLDc5LjUwOSAxMjIuNjExLDczLjQxNiBDMTIyLjYxMSw3MS40ODkgMTIyLjE2OSw2OS44NTYgMTIxLjMzMyw2OC42OTIgQzEyMS4yNjYsNjguNiAxMjEuMjc2LDY4LjQ3MyAxMjEuMzU2LDY4LjM5MiBDMTIxLjQzNiw2OC4zMTEgMTIxLjU2Myw2OC4yOTkgMTIxLjY1Niw2OC4zNjUgQzEyMy4zMjcsNjkuNTM3IDEyNC4yNDcsNzEuNzQ2IDEyNC4yNDcsNzQuNTg0IEMxMjQuMjQ3LDgwLjgyNiAxMTkuODIxLDg4LjQ0NyAxMTQuMzgyLDkxLjU4NyBDMTEyLjgwOCw5Mi40OTUgMTExLjI5OCw5Mi45NDMgMTA5Ljg5NCw5Mi45NDMgTDEwOS44OTQsOTIuOTQzIFogTTEwNi45MjUsOTEuNDAxIEMxMDcuNzM4LDkyLjA1MiAxMDguNzQ1LDkyLjI3OCAxMDkuODkzLDkyLjI3OCBMMTA5Ljg5NCw5Mi4yNzggQzExMS4yMTUsOTIuMjc4IDExMi42NDcsOTEuOTUxIDExNC4xNDgsOTEuMDg0IEMxMTkuNDU5LDg4LjAxNyAxMjMuNzgsODAuNjIxIDEyMy43OCw3NC41MjggQzEyMy43OCw3Mi41NDkgMTIzLjMxNyw3MC45MjkgMTIyLjQ1NCw2OS43NjcgQzEyMi44NjUsNzAuODAyIDEyMy4wNzksNzIuMDQyIDEyMy4wNzksNzMuNDAyIEMxMjMuMDc5LDc5LjY0NSAxMTguNjUzLDg3LjI4NSAxMTMuMjE0LDkwLjQyNSBDMTExLjY0LDkxLjMzNCAxMTAuMTMsOTEuNzQyIDEwOC43MjQsOTEuNzQyIEMxMDguMDgzLDkxLjc0MiAxMDcuNDgxLDkxLjU5MyAxMDYuOTI1LDkxLjQwMSBMMTA2LjkyNSw5MS40MDEgWiIgaWQ9IkZpbGwtMTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjA5Nyw5MC4yMyBDMTE4LjQ4MSw4Ny4xMjIgMTIyLjg0NSw3OS41OTQgMTIyLjg0NSw3My40MTYgQzEyMi44NDUsNzEuMzY1IDEyMi4zNjIsNjkuNzI0IDEyMS41MjIsNjguNTU2IEMxMTkuNzM4LDY3LjMwNCAxMTcuMTQ4LDY3LjM2MiAxMTQuMjY1LDY5LjAyNiBDMTA4Ljg4MSw3Mi4xMzQgMTA0LjUxNyw3OS42NjIgMTA0LjUxNyw4NS44NCBDMTA0LjUxNyw4Ny44OTEgMTA1LDg5LjUzMiAxMDUuODQsOTAuNyBDMTA3LjYyNCw5MS45NTIgMTEwLjIxNCw5MS44OTQgMTEzLjA5Nyw5MC4yMyIgaWQ9IkZpbGwtMjAiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTA4LjcyNCw5MS42MTQgTDEwOC43MjQsOTEuNjE0IEMxMDcuNTgyLDkxLjYxNCAxMDYuNTY2LDkxLjQwMSAxMDUuNzA1LDkwLjc5NyBDMTA1LjY4NCw5MC43ODMgMTA1LjY2NSw5MC44MTEgMTA1LjY1LDkwLjc5IEMxMDQuNzU2LDg5LjU0NiAxMDQuMjgzLDg3Ljg0MiAxMDQuMjgzLDg1LjgxNyBDMTA0LjI4Myw3OS41NzUgMTA4LjcwOSw3MS45NTMgMTE0LjE0OCw2OC44MTIgQzExNS43MjIsNjcuOTA0IDExNy4yMzIsNjcuNDQ5IDExOC42MzgsNjcuNDQ5IEMxMTkuNzgsNjcuNDQ5IDEyMC43OTYsNjcuNzU4IDEyMS42NTYsNjguMzYyIEMxMjEuNjc4LDY4LjM3NyAxMjEuNjk3LDY4LjM5NyAxMjEuNzEyLDY4LjQxOCBDMTIyLjYwNiw2OS42NjIgMTIzLjA3OSw3MS4zOSAxMjMuMDc5LDczLjQxNSBDMTIzLjA3OSw3OS42NTggMTE4LjY1Myw4Ny4xOTggMTEzLjIxNCw5MC4zMzggQzExMS42NCw5MS4yNDcgMTEwLjEzLDkxLjYxNCAxMDguNzI0LDkxLjYxNCBMMTA4LjcyNCw5MS42MTQgWiBNMTA2LjAwNiw5MC41MDUgQzEwNi43OCw5MS4wMzcgMTA3LjY5NCw5MS4yODEgMTA4LjcyNCw5MS4yODEgQzExMC4wNDcsOTEuMjgxIDExMS40NzgsOTAuODY4IDExMi45OCw5MC4wMDEgQzExOC4yOTEsODYuOTM1IDEyMi42MTEsNzkuNDk2IDEyMi42MTEsNzMuNDAzIEMxMjIuNjExLDcxLjQ5NCAxMjIuMTc3LDY5Ljg4IDEyMS4zNTYsNjguNzE4IEMxMjAuNTgyLDY4LjE4NSAxMTkuNjY4LDY3LjkxOSAxMTguNjM4LDY3LjkxOSBDMTE3LjMxNSw2Ny45MTkgMTE1Ljg4Myw2OC4zNiAxMTQuMzgyLDY5LjIyNyBDMTA5LjA3MSw3Mi4yOTMgMTA0Ljc1MSw3OS43MzMgMTA0Ljc1MSw4NS44MjYgQzEwNC43NTEsODcuNzM1IDEwNS4xODUsODkuMzQzIDEwNi4wMDYsOTAuNTA1IEwxMDYuMDA2LDkwLjUwNSBaIiBpZD0iRmlsbC0yMSIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDkuMzE4LDcuMjYyIEwxMzkuMzM0LDE2LjE0IEwxNTUuMjI3LDI3LjE3MSBMMTYwLjgxNiwyMS4wNTkgTDE0OS4zMTgsNy4yNjIiIGlkPSJGaWxsLTIyIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS42NzYsMTMuODQgTDE1OS45MjgsMTkuNDY3IEMxNTYuMjg2LDIxLjU3IDE1MC40LDIxLjU4IDE0Ni43ODEsMTkuNDkxIEMxNDMuMTYxLDE3LjQwMiAxNDMuMTgsMTQuMDAzIDE0Ni44MjIsMTEuOSBMMTU2LjMxNyw2LjI5MiBMMTQ5LjU4OCwyLjQwNyBMNjcuNzUyLDQ5LjQ3OCBMMTEzLjY3NSw3NS45OTIgTDExNi43NTYsNzQuMjEzIEMxMTcuMzg3LDczLjg0OCAxMTcuNjI1LDczLjMxNSAxMTcuMzc0LDcyLjgyMyBDMTE1LjAxNyw2OC4xOTEgMTE0Ljc4MSw2My4yNzcgMTE2LjY5MSw1OC41NjEgQzEyMi4zMjksNDQuNjQxIDE0MS4yLDMzLjc0NiAxNjUuMzA5LDMwLjQ5MSBDMTczLjQ3OCwyOS4zODggMTgxLjk4OSwyOS41MjQgMTkwLjAxMywzMC44ODUgQzE5MC44NjUsMzEuMDMgMTkxLjc4OSwzMC44OTMgMTkyLjQyLDMwLjUyOCBMMTk1LjUwMSwyOC43NSBMMTY5LjY3NiwxMy44NCIgaWQ9IkZpbGwtMjMiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3Ni40NTkgQzExMy41OTQsNzYuNDU5IDExMy41MTQsNzYuNDM4IDExMy40NDIsNzYuMzk3IEw2Ny41MTgsNDkuODgyIEM2Ny4zNzQsNDkuNzk5IDY3LjI4NCw0OS42NDUgNjcuMjg1LDQ5LjQ3OCBDNjcuMjg1LDQ5LjMxMSA2Ny4zNzQsNDkuMTU3IDY3LjUxOSw0OS4wNzMgTDE0OS4zNTUsMi4wMDIgQzE0OS40OTksMS45MTkgMTQ5LjY3NywxLjkxOSAxNDkuODIxLDIuMDAyIEwxNTYuNTUsNS44ODcgQzE1Ni43NzQsNi4wMTcgMTU2Ljg1LDYuMzAyIDE1Ni43MjIsNi41MjYgQzE1Ni41OTIsNi43NDkgMTU2LjMwNyw2LjgyNiAxNTYuMDgzLDYuNjk2IEwxNDkuNTg3LDIuOTQ2IEw2OC42ODcsNDkuNDc5IEwxMTMuNjc1LDc1LjQ1MiBMMTE2LjUyMyw3My44MDggQzExNi43MTUsNzMuNjk3IDExNy4xNDMsNzMuMzk5IDExNi45NTgsNzMuMDM1IEMxMTQuNTQyLDY4LjI4NyAxMTQuMyw2My4yMjEgMTE2LjI1OCw1OC4zODUgQzExOS4wNjQsNTEuNDU4IDEyNS4xNDMsNDUuMTQzIDEzMy44NCw0MC4xMjIgQzE0Mi40OTcsMzUuMTI0IDE1My4zNTgsMzEuNjMzIDE2NS4yNDcsMzAuMDI4IEMxNzMuNDQ1LDI4LjkyMSAxODIuMDM3LDI5LjA1OCAxOTAuMDkxLDMwLjQyNSBDMTkwLjgzLDMwLjU1IDE5MS42NTIsMzAuNDMyIDE5Mi4xODYsMzAuMTI0IEwxOTQuNTY3LDI4Ljc1IEwxNjkuNDQyLDE0LjI0NCBDMTY5LjIxOSwxNC4xMTUgMTY5LjE0MiwxMy44MjkgMTY5LjI3MSwxMy42MDYgQzE2OS40LDEzLjM4MiAxNjkuNjg1LDEzLjMwNiAxNjkuOTA5LDEzLjQzNSBMMTk1LjczNCwyOC4zNDUgQzE5NS44NzksMjguNDI4IDE5NS45NjgsMjguNTgzIDE5NS45NjgsMjguNzUgQzE5NS45NjgsMjguOTE2IDE5NS44NzksMjkuMDcxIDE5NS43MzQsMjkuMTU0IEwxOTIuNjUzLDMwLjkzMyBDMTkxLjkzMiwzMS4zNSAxOTAuODksMzEuNTA4IDE4OS45MzUsMzEuMzQ2IEMxODEuOTcyLDI5Ljk5NSAxNzMuNDc4LDI5Ljg2IDE2NS4zNzIsMzAuOTU0IEMxNTMuNjAyLDMyLjU0MyAxNDIuODYsMzUuOTkzIDEzNC4zMDcsNDAuOTMxIEMxMjUuNzkzLDQ1Ljg0NyAxMTkuODUxLDUyLjAwNCAxMTcuMTI0LDU4LjczNiBDMTE1LjI3LDYzLjMxNCAxMTUuNTAxLDY4LjExMiAxMTcuNzksNzIuNjExIEMxMTguMTYsNzMuMzM2IDExNy44NDUsNzQuMTI0IDExNi45OSw3NC42MTcgTDExMy45MDksNzYuMzk3IEMxMTMuODM2LDc2LjQzOCAxMTMuNzU2LDc2LjQ1OSAxMTMuNjc1LDc2LjQ1OSIgaWQ9IkZpbGwtMjQiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUzLjMxNiwyMS4yNzkgQzE1MC45MDMsMjEuMjc5IDE0OC40OTUsMjAuNzUxIDE0Ni42NjQsMTkuNjkzIEMxNDQuODQ2LDE4LjY0NCAxNDMuODQ0LDE3LjIzMiAxNDMuODQ0LDE1LjcxOCBDMTQzLjg0NCwxNC4xOTEgMTQ0Ljg2LDEyLjc2MyAxNDYuNzA1LDExLjY5OCBMMTU2LjE5OCw2LjA5MSBDMTU2LjMwOSw2LjAyNSAxNTYuNDUyLDYuMDYyIDE1Ni41MTgsNi4xNzMgQzE1Ni41ODMsNi4yODQgMTU2LjU0Nyw2LjQyNyAxNTYuNDM2LDYuNDkzIEwxNDYuOTQsMTIuMTAyIEMxNDUuMjQ0LDEzLjA4MSAxNDQuMzEyLDE0LjM2NSAxNDQuMzEyLDE1LjcxOCBDMTQ0LjMxMiwxNy4wNTggMTQ1LjIzLDE4LjMyNiAxNDYuODk3LDE5LjI4OSBDMTUwLjQ0NiwyMS4zMzggMTU2LjI0LDIxLjMyNyAxNTkuODExLDE5LjI2NSBMMTY5LjU1OSwxMy42MzcgQzE2OS42NywxMy41NzMgMTY5LjgxMywxMy42MTEgMTY5Ljg3OCwxMy43MjMgQzE2OS45NDMsMTMuODM0IDE2OS45MDQsMTMuOTc3IDE2OS43OTMsMTQuMDQyIEwxNjAuMDQ1LDE5LjY3IEMxNTguMTg3LDIwLjc0MiAxNTUuNzQ5LDIxLjI3OSAxNTMuMzE2LDIxLjI3OSIgaWQ9IkZpbGwtMjUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3NS45OTIgTDY3Ljc2Miw0OS40ODQiIGlkPSJGaWxsLTI2IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMy42NzUsNzYuMzQyIEMxMTMuNjE1LDc2LjM0MiAxMTMuNTU1LDc2LjMyNyAxMTMuNSw3Ni4yOTUgTDY3LjU4Nyw0OS43ODcgQzY3LjQxOSw0OS42OSA2Ny4zNjIsNDkuNDc2IDY3LjQ1OSw0OS4zMDkgQzY3LjU1Niw0OS4xNDEgNjcuNzcsNDkuMDgzIDY3LjkzNyw0OS4xOCBMMTEzLjg1LDc1LjY4OCBDMTE0LjAxOCw3NS43ODUgMTE0LjA3NSw3NiAxMTMuOTc4LDc2LjE2NyBDMTEzLjkxNCw3Ni4yNzkgMTEzLjc5Niw3Ni4zNDIgMTEzLjY3NSw3Ni4zNDIiIGlkPSJGaWxsLTI3IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY3Ljc2Miw0OS40ODQgTDY3Ljc2MiwxMDMuNDg1IEM2Ny43NjIsMTA0LjU3NSA2OC41MzIsMTA1LjkwMyA2OS40ODIsMTA2LjQ1MiBMMTExLjk1NSwxMzAuOTczIEMxMTIuOTA1LDEzMS41MjIgMTEzLjY3NSwxMzEuMDgzIDExMy42NzUsMTI5Ljk5MyBMMTEzLjY3NSw3NS45OTIiIGlkPSJGaWxsLTI4IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMi43MjcsMTMxLjU2MSBDMTEyLjQzLDEzMS41NjEgMTEyLjEwNywxMzEuNDY2IDExMS43OCwxMzEuMjc2IEw2OS4zMDcsMTA2Ljc1NSBDNjguMjQ0LDEwNi4xNDIgNjcuNDEyLDEwNC43MDUgNjcuNDEyLDEwMy40ODUgTDY3LjQxMiw0OS40ODQgQzY3LjQxMiw0OS4yOSA2Ny41NjksNDkuMTM0IDY3Ljc2Miw0OS4xMzQgQzY3Ljk1Niw0OS4xMzQgNjguMTEzLDQ5LjI5IDY4LjExMyw0OS40ODQgTDY4LjExMywxMDMuNDg1IEM2OC4xMTMsMTA0LjQ0NSA2OC44MiwxMDUuNjY1IDY5LjY1NywxMDYuMTQ4IEwxMTIuMTMsMTMwLjY3IEMxMTIuNDc0LDEzMC44NjggMTEyLjc5MSwxMzAuOTEzIDExMywxMzAuNzkyIEMxMTMuMjA2LDEzMC42NzMgMTEzLjMyNSwxMzAuMzgxIDExMy4zMjUsMTI5Ljk5MyBMMTEzLjMyNSw3NS45OTIgQzExMy4zMjUsNzUuNzk4IDExMy40ODIsNzUuNjQxIDExMy42NzUsNzUuNjQxIEMxMTMuODY5LDc1LjY0MSAxMTQuMDI1LDc1Ljc5OCAxMTQuMDI1LDc1Ljk5MiBMMTE0LjAyNSwxMjkuOTkzIEMxMTQuMDI1LDEzMC42NDggMTEzLjc4NiwxMzEuMTQ3IDExMy4zNSwxMzEuMzk5IEMxMTMuMTYyLDEzMS41MDcgMTEyLjk1MiwxMzEuNTYxIDExMi43MjcsMTMxLjU2MSIgaWQ9IkZpbGwtMjkiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEyLjg2LDQwLjUxMiBDMTEyLjg2LDQwLjUxMiAxMTIuODYsNDAuNTEyIDExMi44NTksNDAuNTEyIEMxMTAuNTQxLDQwLjUxMiAxMDguMzYsMzkuOTkgMTA2LjcxNywzOS4wNDEgQzEwNS4wMTIsMzguMDU3IDEwNC4wNzQsMzYuNzI2IDEwNC4wNzQsMzUuMjkyIEMxMDQuMDc0LDMzLjg0NyAxMDUuMDI2LDMyLjUwMSAxMDYuNzU0LDMxLjUwNCBMMTE4Ljc5NSwyNC41NTEgQzEyMC40NjMsMjMuNTg5IDEyMi42NjksMjMuMDU4IDEyNS4wMDcsMjMuMDU4IEMxMjcuMzI1LDIzLjA1OCAxMjkuNTA2LDIzLjU4MSAxMzEuMTUsMjQuNTMgQzEzMi44NTQsMjUuNTE0IDEzMy43OTMsMjYuODQ1IDEzMy43OTMsMjguMjc4IEMxMzMuNzkzLDI5LjcyNCAxMzIuODQxLDMxLjA2OSAxMzEuMTEzLDMyLjA2NyBMMTE5LjA3MSwzOS4wMTkgQzExNy40MDMsMzkuOTgyIDExNS4xOTcsNDAuNTEyIDExMi44Niw0MC41MTIgTDExMi44Niw0MC41MTIgWiBNMTI1LjAwNywyMy43NTkgQzEyMi43OSwyMy43NTkgMTIwLjcwOSwyNC4yNTYgMTE5LjE0NiwyNS4xNTggTDEwNy4xMDQsMzIuMTEgQzEwNS42MDIsMzIuOTc4IDEwNC43NzQsMzQuMTA4IDEwNC43NzQsMzUuMjkyIEMxMDQuNzc0LDM2LjQ2NSAxMDUuNTg5LDM3LjU4MSAxMDcuMDY3LDM4LjQzNCBDMTA4LjYwNSwzOS4zMjMgMTEwLjY2MywzOS44MTIgMTEyLjg1OSwzOS44MTIgTDExMi44NiwzOS44MTIgQzExNS4wNzYsMzkuODEyIDExNy4xNTgsMzkuMzE1IDExOC43MjEsMzguNDEzIEwxMzAuNzYyLDMxLjQ2IEMxMzIuMjY0LDMwLjU5MyAxMzMuMDkyLDI5LjQ2MyAxMzMuMDkyLDI4LjI3OCBDMTMzLjA5MiwyNy4xMDYgMTMyLjI3OCwyNS45OSAxMzAuOCwyNS4xMzYgQzEyOS4yNjEsMjQuMjQ4IDEyNy4yMDQsMjMuNzU5IDEyNS4wMDcsMjMuNzU5IEwxMjUuMDA3LDIzLjc1OSBaIiBpZD0iRmlsbC0zMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNjUuNjMsMTYuMjE5IEwxNTkuODk2LDE5LjUzIEMxNTYuNzI5LDIxLjM1OCAxNTEuNjEsMjEuMzY3IDE0OC40NjMsMTkuNTUgQzE0NS4zMTYsMTcuNzMzIDE0NS4zMzIsMTQuNzc4IDE0OC40OTksMTIuOTQ5IEwxNTQuMjMzLDkuNjM5IEwxNjUuNjMsMTYuMjE5IiBpZD0iRmlsbC0zMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNTQuMjMzLDEwLjQ0OCBMMTY0LjIyOCwxNi4yMTkgTDE1OS41NDYsMTguOTIzIEMxNTguMTEyLDE5Ljc1IDE1Ni4xOTQsMjAuMjA2IDE1NC4xNDcsMjAuMjA2IEMxNTIuMTE4LDIwLjIwNiAxNTAuMjI0LDE5Ljc1NyAxNDguODE0LDE4Ljk0MyBDMTQ3LjUyNCwxOC4xOTkgMTQ2LjgxNCwxNy4yNDkgMTQ2LjgxNCwxNi4yNjkgQzE0Ni44MTQsMTUuMjc4IDE0Ny41MzcsMTQuMzE0IDE0OC44NSwxMy41NTYgTDE1NC4yMzMsMTAuNDQ4IE0xNTQuMjMzLDkuNjM5IEwxNDguNDk5LDEyLjk0OSBDMTQ1LjMzMiwxNC43NzggMTQ1LjMxNiwxNy43MzMgMTQ4LjQ2MywxOS41NSBDMTUwLjAzMSwyMC40NTUgMTUyLjA4NiwyMC45MDcgMTU0LjE0NywyMC45MDcgQzE1Ni4yMjQsMjAuOTA3IDE1OC4zMDYsMjAuNDQ3IDE1OS44OTYsMTkuNTMgTDE2NS42MywxNi4yMTkgTDE1NC4yMzMsOS42MzkiIGlkPSJGaWxsLTMyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NS40NDUsNzIuNjY3IEwxNDUuNDQ1LDcyLjY2NyBDMTQzLjY3Miw3Mi42NjcgMTQyLjIwNCw3MS44MTcgMTQxLjIwMiw3MC40MjIgQzE0MS4xMzUsNzAuMzMgMTQxLjE0NSw3MC4xNDcgMTQxLjIyNSw3MC4wNjYgQzE0MS4zMDUsNjkuOTg1IDE0MS40MzIsNjkuOTQ2IDE0MS41MjUsNzAuMDExIEMxNDIuMzA2LDcwLjU1OSAxNDMuMjMxLDcwLjgyMyAxNDQuMjc2LDcwLjgyMiBDMTQ1LjU5OCw3MC44MjIgMTQ3LjAzLDcwLjM3NiAxNDguNTMyLDY5LjUwOSBDMTUzLjg0Miw2Ni40NDMgMTU4LjE2Myw1OC45ODcgMTU4LjE2Myw1Mi44OTQgQzE1OC4xNjMsNTAuOTY3IDE1Ny43MjEsNDkuMzMyIDE1Ni44ODQsNDguMTY4IEMxNTYuODE4LDQ4LjA3NiAxNTYuODI4LDQ3Ljk0OCAxNTYuOTA4LDQ3Ljg2NyBDMTU2Ljk4OCw0Ny43ODYgMTU3LjExNCw0Ny43NzQgMTU3LjIwOCw0Ny44NCBDMTU4Ljg3OCw0OS4wMTIgMTU5Ljc5OCw1MS4yMiAxNTkuNzk4LDU0LjA1OSBDMTU5Ljc5OCw2MC4zMDEgMTU1LjM3Myw2OC4wNDYgMTQ5LjkzMyw3MS4xODYgQzE0OC4zNiw3Mi4wOTQgMTQ2Ljg1LDcyLjY2NyAxNDUuNDQ1LDcyLjY2NyBMMTQ1LjQ0NSw3Mi42NjcgWiBNMTQyLjQ3Niw3MSBDMTQzLjI5LDcxLjY1MSAxNDQuMjk2LDcyLjAwMiAxNDUuNDQ1LDcyLjAwMiBDMTQ2Ljc2Nyw3Mi4wMDIgMTQ4LjE5OCw3MS41NSAxNDkuNyw3MC42ODIgQzE1NS4wMSw2Ny42MTcgMTU5LjMzMSw2MC4xNTkgMTU5LjMzMSw1NC4wNjUgQzE1OS4zMzEsNTIuMDg1IDE1OC44NjgsNTAuNDM1IDE1OC4wMDYsNDkuMjcyIEMxNTguNDE3LDUwLjMwNyAxNTguNjMsNTEuNTMyIDE1OC42Myw1Mi44OTIgQzE1OC42Myw1OS4xMzQgMTU0LjIwNSw2Ni43NjcgMTQ4Ljc2NSw2OS45MDcgQzE0Ny4xOTIsNzAuODE2IDE0NS42ODEsNzEuMjgzIDE0NC4yNzYsNzEuMjgzIEMxNDMuNjM0LDcxLjI4MyAxNDMuMDMzLDcxLjE5MiAxNDIuNDc2LDcxIEwxNDIuNDc2LDcxIFoiIGlkPSJGaWxsLTMzIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0OC42NDgsNjkuNzA0IEMxNTQuMDMyLDY2LjU5NiAxNTguMzk2LDU5LjA2OCAxNTguMzk2LDUyLjg5MSBDMTU4LjM5Niw1MC44MzkgMTU3LjkxMyw0OS4xOTggMTU3LjA3NCw0OC4wMyBDMTU1LjI4OSw0Ni43NzggMTUyLjY5OSw0Ni44MzYgMTQ5LjgxNiw0OC41MDEgQzE0NC40MzMsNTEuNjA5IDE0MC4wNjgsNTkuMTM3IDE0MC4wNjgsNjUuMzE0IEMxNDAuMDY4LDY3LjM2NSAxNDAuNTUyLDY5LjAwNiAxNDEuMzkxLDcwLjE3NCBDMTQzLjE3Niw3MS40MjcgMTQ1Ljc2NSw3MS4zNjkgMTQ4LjY0OCw2OS43MDQiIGlkPSJGaWxsLTM0IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NC4yNzYsNzEuMjc2IEwxNDQuMjc2LDcxLjI3NiBDMTQzLjEzMyw3MS4yNzYgMTQyLjExOCw3MC45NjkgMTQxLjI1Nyw3MC4zNjUgQzE0MS4yMzYsNzAuMzUxIDE0MS4yMTcsNzAuMzMyIDE0MS4yMDIsNzAuMzExIEMxNDAuMzA3LDY5LjA2NyAxMzkuODM1LDY3LjMzOSAxMzkuODM1LDY1LjMxNCBDMTM5LjgzNSw1OS4wNzMgMTQ0LjI2LDUxLjQzOSAxNDkuNyw0OC4yOTggQzE1MS4yNzMsNDcuMzkgMTUyLjc4NCw0Ni45MjkgMTU0LjE4OSw0Ni45MjkgQzE1NS4zMzIsNDYuOTI5IDE1Ni4zNDcsNDcuMjM2IDE1Ny4yMDgsNDcuODM5IEMxNTcuMjI5LDQ3Ljg1NCAxNTcuMjQ4LDQ3Ljg3MyAxNTcuMjYzLDQ3Ljg5NCBDMTU4LjE1Nyw0OS4xMzggMTU4LjYzLDUwLjg2NSAxNTguNjMsNTIuODkxIEMxNTguNjMsNTkuMTMyIDE1NC4yMDUsNjYuNzY2IDE0OC43NjUsNjkuOTA3IEMxNDcuMTkyLDcwLjgxNSAxNDUuNjgxLDcxLjI3NiAxNDQuMjc2LDcxLjI3NiBMMTQ0LjI3Niw3MS4yNzYgWiBNMTQxLjU1OCw3MC4xMDQgQzE0Mi4zMzEsNzAuNjM3IDE0My4yNDUsNzEuMDA1IDE0NC4yNzYsNzEuMDA1IEMxNDUuNTk4LDcxLjAwNSAxNDcuMDMsNzAuNDY3IDE0OC41MzIsNjkuNiBDMTUzLjg0Miw2Ni41MzQgMTU4LjE2Myw1OS4wMzMgMTU4LjE2Myw1Mi45MzkgQzE1OC4xNjMsNTEuMDMxIDE1Ny43MjksNDkuMzg1IDE1Ni45MDcsNDguMjIzIEMxNTYuMTMzLDQ3LjY5MSAxNTUuMjE5LDQ3LjQwOSAxNTQuMTg5LDQ3LjQwOSBDMTUyLjg2Nyw0Ny40MDkgMTUxLjQzNSw0Ny44NDIgMTQ5LjkzMyw0OC43MDkgQzE0NC42MjMsNTEuNzc1IDE0MC4zMDIsNTkuMjczIDE0MC4zMDIsNjUuMzY2IEMxNDAuMzAyLDY3LjI3NiAxNDAuNzM2LDY4Ljk0MiAxNDEuNTU4LDcwLjEwNCBMMTQxLjU1OCw3MC4xMDQgWiIgaWQ9IkZpbGwtMzUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUwLjcyLDY1LjM2MSBMMTUwLjM1Nyw2NS4wNjYgQzE1MS4xNDcsNjQuMDkyIDE1MS44NjksNjMuMDQgMTUyLjUwNSw2MS45MzggQzE1My4zMTMsNjAuNTM5IDE1My45NzgsNTkuMDY3IDE1NC40ODIsNTcuNTYzIEwxNTQuOTI1LDU3LjcxMiBDMTU0LjQxMiw1OS4yNDUgMTUzLjczMyw2MC43NDUgMTUyLjkxLDYyLjE3MiBDMTUyLjI2Miw2My4yOTUgMTUxLjUyNSw2NC4zNjggMTUwLjcyLDY1LjM2MSIgaWQ9IkZpbGwtMzYiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE1LjkxNyw4NC41MTQgTDExNS41NTQsODQuMjIgQzExNi4zNDQsODMuMjQ1IDExNy4wNjYsODIuMTk0IDExNy43MDIsODEuMDkyIEMxMTguNTEsNzkuNjkyIDExOS4xNzUsNzguMjIgMTE5LjY3OCw3Ni43MTcgTDEyMC4xMjEsNzYuODY1IEMxMTkuNjA4LDc4LjM5OCAxMTguOTMsNzkuODk5IDExOC4xMDYsODEuMzI2IEMxMTcuNDU4LDgyLjQ0OCAxMTYuNzIyLDgzLjUyMSAxMTUuOTE3LDg0LjUxNCIgaWQ9IkZpbGwtMzciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE0LDEzMC40NzYgTDExNCwxMzAuMDA4IEwxMTQsNzYuMDUyIEwxMTQsNzUuNTg0IEwxMTQsNzYuMDUyIEwxMTQsMTMwLjAwOCBMMTE0LDEzMC40NzYiIGlkPSJGaWxsLTM4IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYyLjAwMDAwMCwgMC4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTkuODIyLDM3LjQ3NCBDMTkuODM5LDM3LjMzOSAxOS43NDcsMzcuMTk0IDE5LjU1NSwzNy4wODIgQzE5LjIyOCwzNi44OTQgMTguNzI5LDM2Ljg3MiAxOC40NDYsMzcuMDM3IEwxMi40MzQsNDAuNTA4IEMxMi4zMDMsNDAuNTg0IDEyLjI0LDQwLjY4NiAxMi4yNDMsNDAuNzkzIEMxMi4yNDUsNDAuOTI1IDEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQxLjM3MSBMMTIuMjQ1LDQxLjQxNCBMMTIuMjM4LDQxLjU0MiBDOC4xNDgsNDMuODg3IDUuNjQ3LDQ1LjMyMSA1LjY0Nyw0NS4zMjEgQzUuNjQ2LDQ1LjMyMSAzLjU3LDQ2LjM2NyAyLjg2LDUwLjUxMyBDMi44Niw1MC41MTMgMS45NDgsNTcuNDc0IDEuOTYyLDcwLjI1OCBDMS45NzcsODIuODI4IDIuNTY4LDg3LjMyOCAzLjEyOSw5MS42MDkgQzMuMzQ5LDkzLjI5MyA2LjEzLDkzLjczNCA2LjEzLDkzLjczNCBDNi40NjEsOTMuNzc0IDYuODI4LDkzLjcwNyA3LjIxLDkzLjQ4NiBMODIuNDgzLDQ5LjkzNSBDODQuMjkxLDQ4Ljg2NiA4NS4xNSw0Ni4yMTYgODUuNTM5LDQzLjY1MSBDODYuNzUyLDM1LjY2MSA4Ny4yMTQsMTAuNjczIDg1LjI2NCwzLjc3MyBDODUuMDY4LDMuMDggODQuNzU0LDIuNjkgODQuMzk2LDIuNDkxIEw4Mi4zMSwxLjcwMSBDODEuNTgzLDEuNzI5IDgwLjg5NCwyLjE2OCA4MC43NzYsMi4yMzYgQzgwLjYzNiwyLjMxNyA0MS44MDcsMjQuNTg1IDIwLjAzMiwzNy4wNzIgTDE5LjgyMiwzNy40NzQiIGlkPSJGaWxsLTEiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNODIuMzExLDEuNzAxIEw4NC4zOTYsMi40OTEgQzg0Ljc1NCwyLjY5IDg1LjA2OCwzLjA4IDg1LjI2NCwzLjc3MyBDODcuMjEzLDEwLjY3MyA4Ni43NTEsMzUuNjYgODUuNTM5LDQzLjY1MSBDODUuMTQ5LDQ2LjIxNiA4NC4yOSw0OC44NjYgODIuNDgzLDQ5LjkzNSBMNy4yMSw5My40ODYgQzYuODk3LDkzLjY2NyA2LjU5NSw5My43NDQgNi4zMTQsOTMuNzQ0IEw2LjEzMSw5My43MzMgQzYuMTMxLDkzLjczNCAzLjM0OSw5My4yOTMgMy4xMjgsOTEuNjA5IEMyLjU2OCw4Ny4zMjcgMS45NzcsODIuODI4IDEuOTYzLDcwLjI1OCBDMS45NDgsNTcuNDc0IDIuODYsNTAuNTEzIDIuODYsNTAuNTEzIEMzLjU3LDQ2LjM2NyA1LjY0Nyw0NS4zMjEgNS42NDcsNDUuMzIxIEM1LjY0Nyw0NS4zMjEgOC4xNDgsNDMuODg3IDEyLjIzOCw0MS41NDIgTDEyLjI0NSw0MS40MTQgTDEyLjI0NSw0MS4zNzEgQzEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQwLjkyNSAxMi4yNDMsNDAuNzkzIEMxMi4yNCw0MC42ODYgMTIuMzAyLDQwLjU4MyAxMi40MzQsNDAuNTA4IEwxOC40NDYsMzcuMDM2IEMxOC41NzQsMzYuOTYyIDE4Ljc0NiwzNi45MjYgMTguOTI3LDM2LjkyNiBDMTkuMTQ1LDM2LjkyNiAxOS4zNzYsMzYuOTc5IDE5LjU1NCwzNy4wODIgQzE5Ljc0NywzNy4xOTQgMTkuODM5LDM3LjM0IDE5LjgyMiwzNy40NzQgTDIwLjAzMywzNy4wNzIgQzQxLjgwNiwyNC41ODUgODAuNjM2LDIuMzE4IDgwLjc3NywyLjIzNiBDODAuODk0LDIuMTY4IDgxLjU4MywxLjcyOSA4Mi4zMTEsMS43MDEgTTgyLjMxMSwwLjcwNCBMODIuMjcyLDAuNzA1IEM4MS42NTQsMC43MjggODAuOTg5LDAuOTQ5IDgwLjI5OCwxLjM2MSBMODAuMjc3LDEuMzczIEM4MC4xMjksMS40NTggNTkuNzY4LDEzLjEzNSAxOS43NTgsMzYuMDc5IEMxOS41LDM1Ljk4MSAxOS4yMTQsMzUuOTI5IDE4LjkyNywzNS45MjkgQzE4LjU2MiwzNS45MjkgMTguMjIzLDM2LjAxMyAxNy45NDcsMzYuMTczIEwxMS45MzUsMzkuNjQ0IEMxMS40OTMsMzkuODk5IDExLjIzNiw0MC4zMzQgMTEuMjQ2LDQwLjgxIEwxMS4yNDcsNDAuOTYgTDUuMTY3LDQ0LjQ0NyBDNC43OTQsNDQuNjQ2IDIuNjI1LDQ1Ljk3OCAxLjg3Nyw1MC4zNDUgTDEuODcxLDUwLjM4NCBDMS44NjIsNTAuNDU0IDAuOTUxLDU3LjU1NyAwLjk2NSw3MC4yNTkgQzAuOTc5LDgyLjg3OSAxLjU2OCw4Ny4zNzUgMi4xMzcsOTEuNzI0IEwyLjEzOSw5MS43MzkgQzIuNDQ3LDk0LjA5NCA1LjYxNCw5NC42NjIgNS45NzUsOTQuNzE5IEw2LjAwOSw5NC43MjMgQzYuMTEsOTQuNzM2IDYuMjEzLDk0Ljc0MiA2LjMxNCw5NC43NDIgQzYuNzksOTQuNzQyIDcuMjYsOTQuNjEgNy43MSw5NC4zNSBMODIuOTgzLDUwLjc5OCBDODQuNzk0LDQ5LjcyNyA4NS45ODIsNDcuMzc1IDg2LjUyNSw0My44MDEgQzg3LjcxMSwzNS45ODcgODguMjU5LDEwLjcwNSA4Ni4yMjQsMy41MDIgQzg1Ljk3MSwyLjYwOSA4NS41MiwxLjk3NSA4NC44ODEsMS42MiBMODQuNzQ5LDEuNTU4IEw4Mi42NjQsMC43NjkgQzgyLjU1MSwwLjcyNSA4Mi40MzEsMC43MDQgODIuMzExLDAuNzA0IiBpZD0iRmlsbC0yIiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY2LjI2NywxMS41NjUgTDY3Ljc2MiwxMS45OTkgTDExLjQyMyw0NC4zMjUiIGlkPSJGaWxsLTMiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMjAyLDkwLjU0NSBDMTIuMDI5LDkwLjU0NSAxMS44NjIsOTAuNDU1IDExLjc2OSw5MC4yOTUgQzExLjYzMiw5MC4wNTcgMTEuNzEzLDg5Ljc1MiAxMS45NTIsODkuNjE0IEwzMC4zODksNzguOTY5IEMzMC42MjgsNzguODMxIDMwLjkzMyw3OC45MTMgMzEuMDcxLDc5LjE1MiBDMzEuMjA4LDc5LjM5IDMxLjEyNyw3OS42OTYgMzAuODg4LDc5LjgzMyBMMTIuNDUxLDkwLjQ3OCBMMTIuMjAyLDkwLjU0NSIgaWQ9IkZpbGwtNCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMy43NjQsNDIuNjU0IEwxMy42NTYsNDIuNTkyIEwxMy43MDIsNDIuNDIxIEwxOC44MzcsMzkuNDU3IEwxOS4wMDcsMzkuNTAyIEwxOC45NjIsMzkuNjczIEwxMy44MjcsNDIuNjM3IEwxMy43NjQsNDIuNjU0IiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTguNTIsOTAuMzc1IEw4LjUyLDQ2LjQyMSBMOC41ODMsNDYuMzg1IEw3NS44NCw3LjU1NCBMNzUuODQsNTEuNTA4IEw3NS43NzgsNTEuNTQ0IEw4LjUyLDkwLjM3NSBMOC41Miw5MC4zNzUgWiBNOC43Nyw0Ni41NjQgTDguNzcsODkuOTQ0IEw3NS41OTEsNTEuMzY1IEw3NS41OTEsNy45ODUgTDguNzcsNDYuNTY0IEw4Ljc3LDQ2LjU2NCBaIiBpZD0iRmlsbC02IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTI0Ljk4Niw4My4xODIgQzI0Ljc1Niw4My4zMzEgMjQuMzc0LDgzLjU2NiAyNC4xMzcsODMuNzA1IEwxMi42MzIsOTAuNDA2IEMxMi4zOTUsOTAuNTQ1IDEyLjQyNiw5MC42NTggMTIuNyw5MC42NTggTDEzLjI2NSw5MC42NTggQzEzLjU0LDkwLjY1OCAxMy45NTgsOTAuNTQ1IDE0LjE5NSw5MC40MDYgTDI1LjcsODMuNzA1IEMyNS45MzcsODMuNTY2IDI2LjEyOCw4My40NTIgMjYuMTI1LDgzLjQ0OSBDMjYuMTIyLDgzLjQ0NyAyNi4xMTksODMuMjIgMjYuMTE5LDgyLjk0NiBDMjYuMTE5LDgyLjY3MiAyNS45MzEsODIuNTY5IDI1LjcwMSw4Mi43MTkgTDI0Ljk4Niw4My4xODIiIGlkPSJGaWxsLTciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTMuMjY2LDkwLjc4MiBMMTIuNyw5MC43ODIgQzEyLjUsOTAuNzgyIDEyLjM4NCw5MC43MjYgMTIuMzU0LDkwLjYxNiBDMTIuMzI0LDkwLjUwNiAxMi4zOTcsOTAuMzk5IDEyLjU2OSw5MC4yOTkgTDI0LjA3NCw4My41OTcgQzI0LjMxLDgzLjQ1OSAyNC42ODksODMuMjI2IDI0LjkxOCw4My4wNzggTDI1LjYzMyw4Mi42MTQgQzI1LjcyMyw4Mi41NTUgMjUuODEzLDgyLjUyNSAyNS44OTksODIuNTI1IEMyNi4wNzEsODIuNTI1IDI2LjI0NCw4Mi42NTUgMjYuMjQ0LDgyLjk0NiBDMjYuMjQ0LDgzLjE2IDI2LjI0NSw4My4zMDkgMjYuMjQ3LDgzLjM4MyBMMjYuMjUzLDgzLjM4NyBMMjYuMjQ5LDgzLjQ1NiBDMjYuMjQ2LDgzLjUzMSAyNi4yNDYsODMuNTMxIDI1Ljc2Myw4My44MTIgTDE0LjI1OCw5MC41MTQgQzE0LDkwLjY2NSAxMy41NjQsOTAuNzgyIDEzLjI2Niw5MC43ODIgTDEzLjI2Niw5MC43ODIgWiBNMTIuNjY2LDkwLjUzMiBMMTIuNyw5MC41MzMgTDEzLjI2Niw5MC41MzMgQzEzLjUxOCw5MC41MzMgMTMuOTE1LDkwLjQyNSAxNC4xMzIsOTAuMjk5IEwyNS42MzcsODMuNTk3IEMyNS44MDUsODMuNDk5IDI1LjkzMSw4My40MjQgMjUuOTk4LDgzLjM4MyBDMjUuOTk0LDgzLjI5OSAyNS45OTQsODMuMTY1IDI1Ljk5NCw4Mi45NDYgTDI1Ljg5OSw4Mi43NzUgTDI1Ljc2OCw4Mi44MjQgTDI1LjA1NCw4My4yODcgQzI0LjgyMiw4My40MzcgMjQuNDM4LDgzLjY3MyAyNC4yLDgzLjgxMiBMMTIuNjk1LDkwLjUxNCBMMTIuNjY2LDkwLjUzMiBMMTIuNjY2LDkwLjUzMiBaIiBpZD0iRmlsbC04IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEzLjI2Niw4OS44NzEgTDEyLjcsODkuODcxIEMxMi41LDg5Ljg3MSAxMi4zODQsODkuODE1IDEyLjM1NCw4OS43MDUgQzEyLjMyNCw4OS41OTUgMTIuMzk3LDg5LjQ4OCAxMi41NjksODkuMzg4IEwyNC4wNzQsODIuNjg2IEMyNC4zMzIsODIuNTM1IDI0Ljc2OCw4Mi40MTggMjUuMDY3LDgyLjQxOCBMMjUuNjMyLDgyLjQxOCBDMjUuODMyLDgyLjQxOCAyNS45NDgsODIuNDc0IDI1Ljk3OCw4Mi41ODQgQzI2LjAwOCw4Mi42OTQgMjUuOTM1LDgyLjgwMSAyNS43NjMsODIuOTAxIEwxNC4yNTgsODkuNjAzIEMxNCw4OS43NTQgMTMuNTY0LDg5Ljg3MSAxMy4yNjYsODkuODcxIEwxMy4yNjYsODkuODcxIFogTTEyLjY2Niw4OS42MjEgTDEyLjcsODkuNjIyIEwxMy4yNjYsODkuNjIyIEMxMy41MTgsODkuNjIyIDEzLjkxNSw4OS41MTUgMTQuMTMyLDg5LjM4OCBMMjUuNjM3LDgyLjY4NiBMMjUuNjY3LDgyLjY2OCBMMjUuNjMyLDgyLjY2NyBMMjUuMDY3LDgyLjY2NyBDMjQuODE1LDgyLjY2NyAyNC40MTgsODIuNzc1IDI0LjIsODIuOTAxIEwxMi42OTUsODkuNjAzIEwxMi42NjYsODkuNjIxIEwxMi42NjYsODkuNjIxIFoiIGlkPSJGaWxsLTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMzcsOTAuODAxIEwxMi4zNyw4OS41NTQgTDEyLjM3LDkwLjgwMSIgaWQ9IkZpbGwtMTAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNi4xMyw5My45MDEgQzUuMzc5LDkzLjgwOCA0LjgxNiw5My4xNjQgNC42OTEsOTIuNTI1IEMzLjg2LDg4LjI4NyAzLjU0LDgzLjc0MyAzLjUyNiw3MS4xNzMgQzMuNTExLDU4LjM4OSA0LjQyMyw1MS40MjggNC40MjMsNTEuNDI4IEM1LjEzNCw0Ny4yODIgNy4yMSw0Ni4yMzYgNy4yMSw0Ni4yMzYgQzcuMjEsNDYuMjM2IDgxLjY2NywzLjI1IDgyLjA2OSwzLjAxNyBDODIuMjkyLDIuODg4IDg0LjU1NiwxLjQzMyA4NS4yNjQsMy45NCBDODcuMjE0LDEwLjg0IDg2Ljc1MiwzNS44MjcgODUuNTM5LDQzLjgxOCBDODUuMTUsNDYuMzgzIDg0LjI5MSw0OS4wMzMgODIuNDgzLDUwLjEwMSBMNy4yMSw5My42NTMgQzYuODI4LDkzLjg3NCA2LjQ2MSw5My45NDEgNi4xMyw5My45MDEgQzYuMTMsOTMuOTAxIDMuMzQ5LDkzLjQ2IDMuMTI5LDkxLjc3NiBDMi41NjgsODcuNDk1IDEuOTc3LDgyLjk5NSAxLjk2Miw3MC40MjUgQzEuOTQ4LDU3LjY0MSAyLjg2LDUwLjY4IDIuODYsNTAuNjggQzMuNTcsNDYuNTM0IDUuNjQ3LDQ1LjQ4OSA1LjY0Nyw0NS40ODkgQzUuNjQ2LDQ1LjQ4OSA4LjA2NSw0NC4wOTIgMTIuMjQ1LDQxLjY3OSBMMTMuMTE2LDQxLjU2IEwxOS43MTUsMzcuNzMgTDE5Ljc2MSwzNy4yNjkgTDYuMTMsOTMuOTAxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjMxNyw5NC4xNjEgTDYuMTAyLDk0LjE0OCBMNi4xMDEsOTQuMTQ4IEw1Ljg1Nyw5NC4xMDEgQzUuMTM4LDkzLjk0NSAzLjA4NSw5My4zNjUgMi44ODEsOTEuODA5IEMyLjMxMyw4Ny40NjkgMS43MjcsODIuOTk2IDEuNzEzLDcwLjQyNSBDMS42OTksNTcuNzcxIDIuNjA0LDUwLjcxOCAyLjYxMyw1MC42NDggQzMuMzM4LDQ2LjQxNyA1LjQ0NSw0NS4zMSA1LjUzNSw0NS4yNjYgTDEyLjE2Myw0MS40MzkgTDEzLjAzMyw0MS4zMiBMMTkuNDc5LDM3LjU3OCBMMTkuNTEzLDM3LjI0NCBDMTkuNTI2LDM3LjEwNyAxOS42NDcsMzcuMDA4IDE5Ljc4NiwzNy4wMjEgQzE5LjkyMiwzNy4wMzQgMjAuMDIzLDM3LjE1NiAyMC4wMDksMzcuMjkzIEwxOS45NSwzNy44ODIgTDEzLjE5OCw0MS44MDEgTDEyLjMyOCw0MS45MTkgTDUuNzcyLDQ1LjcwNCBDNS43NDEsNDUuNzIgMy43ODIsNDYuNzcyIDMuMTA2LDUwLjcyMiBDMy4wOTksNTAuNzgyIDIuMTk4LDU3LjgwOCAyLjIxMiw3MC40MjQgQzIuMjI2LDgyLjk2MyAyLjgwOSw4Ny40MiAzLjM3Myw5MS43MjkgQzMuNDY0LDkyLjQyIDQuMDYyLDkyLjg4MyA0LjY4Miw5My4xODEgQzQuNTY2LDkyLjk4NCA0LjQ4Niw5Mi43NzYgNC40NDYsOTIuNTcyIEMzLjY2NSw4OC41ODggMy4yOTEsODQuMzcgMy4yNzYsNzEuMTczIEMzLjI2Miw1OC41MiA0LjE2Nyw1MS40NjYgNC4xNzYsNTEuMzk2IEM0LjkwMSw0Ny4xNjUgNy4wMDgsNDYuMDU5IDcuMDk4LDQ2LjAxNCBDNy4wOTQsNDYuMDE1IDgxLjU0MiwzLjAzNCA4MS45NDQsMi44MDIgTDgxLjk3MiwyLjc4NSBDODIuODc2LDIuMjQ3IDgzLjY5MiwyLjA5NyA4NC4zMzIsMi4zNTIgQzg0Ljg4NywyLjU3MyA4NS4yODEsMy4wODUgODUuNTA0LDMuODcyIEM4Ny41MTgsMTEgODYuOTY0LDM2LjA5MSA4NS43ODUsNDMuODU1IEM4NS4yNzgsNDcuMTk2IDg0LjIxLDQ5LjM3IDgyLjYxLDUwLjMxNyBMNy4zMzUsOTMuODY5IEM2Ljk5OSw5NC4wNjMgNi42NTgsOTQuMTYxIDYuMzE3LDk0LjE2MSBMNi4zMTcsOTQuMTYxIFogTTYuMTcsOTMuNjU0IEM2LjQ2Myw5My42OSA2Ljc3NCw5My42MTcgNy4wODUsOTMuNDM3IEw4Mi4zNTgsNDkuODg2IEM4NC4xODEsNDguODA4IDg0Ljk2LDQ1Ljk3MSA4NS4yOTIsNDMuNzggQzg2LjQ2NiwzNi4wNDkgODcuMDIzLDExLjA4NSA4NS4wMjQsNC4wMDggQzg0Ljg0NiwzLjM3NyA4NC41NTEsMi45NzYgODQuMTQ4LDIuODE2IEM4My42NjQsMi42MjMgODIuOTgyLDIuNzY0IDgyLjIyNywzLjIxMyBMODIuMTkzLDMuMjM0IEM4MS43OTEsMy40NjYgNy4zMzUsNDYuNDUyIDcuMzM1LDQ2LjQ1MiBDNy4zMDQsNDYuNDY5IDUuMzQ2LDQ3LjUyMSA0LjY2OSw1MS40NzEgQzQuNjYyLDUxLjUzIDMuNzYxLDU4LjU1NiAzLjc3NSw3MS4xNzMgQzMuNzksODQuMzI4IDQuMTYxLDg4LjUyNCA0LjkzNiw5Mi40NzYgQzUuMDI2LDkyLjkzNyA1LjQxMiw5My40NTkgNS45NzMsOTMuNjE1IEM2LjA4Nyw5My42NCA2LjE1OCw5My42NTIgNi4xNjksOTMuNjU0IEw2LjE3LDkzLjY1NCBMNi4xNyw5My42NTQgWiIgaWQ9IkZpbGwtMTIiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4zMTcsNjguOTgyIEM3LjgwNiw2OC43MDEgOC4yMDIsNjguOTI2IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNi44MjksNzEuMjk0IDYuNDMzLDcxLjA2OSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIiBpZD0iRmlsbC0xMyIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjkyLDcxLjEzMyBDNi42MzEsNzEuMTMzIDYuNDMzLDcwLjkwNSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIEM3LjQ2LDY4LjkgNy41OTUsNjguODYxIDcuNzE0LDY4Ljg2MSBDOC4wMDMsNjguODYxIDguMjAyLDY5LjA5IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNy4xNzQsNzEuMDk0IDcuMDM5LDcxLjEzMyA2LjkyLDcxLjEzMyBNNy43MTQsNjguNjc0IEM3LjU1Nyw2OC42NzQgNy4zOTIsNjguNzIzIDcuMjI0LDY4LjgyMSBDNi42NzYsNjkuMTM4IDYuMjQ2LDY5Ljg3OSA2LjI0Niw3MC41MDggQzYuMjQ2LDcwLjk5NCA2LjUxNyw3MS4zMiA2LjkyLDcxLjMyIEM3LjA3OCw3MS4zMiA3LjI0Myw3MS4yNzEgNy40MTEsNzEuMTc0IEM3Ljk1OSw3MC44NTcgOC4zODksNzAuMTE3IDguMzg5LDY5LjQ4NyBDOC4zODksNjkuMDAxIDguMTE3LDY4LjY3NCA3LjcxNCw2OC42NzQiIGlkPSJGaWxsLTE0IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYuOTIsNzAuOTQ3IEM2LjY0OSw3MC45NDcgNi42MjEsNzAuNjQgNi42MjEsNzAuNTA4IEM2LjYyMSw3MC4wMTcgNi45ODIsNjkuMzkyIDcuNDExLDY5LjE0NSBDNy41MjEsNjkuMDgyIDcuNjI1LDY5LjA0OSA3LjcxNCw2OS4wNDkgQzcuOTg2LDY5LjA0OSA4LjAxNSw2OS4zNTUgOC4wMTUsNjkuNDg3IEM4LjAxNSw2OS45NzggNy42NTIsNzAuNjAzIDcuMjI0LDcwLjg1MSBDNy4xMTUsNzAuOTE0IDcuMDEsNzAuOTQ3IDYuOTIsNzAuOTQ3IE03LjcxNCw2OC44NjEgQzcuNTk1LDY4Ljg2MSA3LjQ2LDY4LjkgNy4zMTcsNjguOTgyIEM2LjgyOSw2OS4yNjUgNi40MzMsNjkuOTQ4IDYuNDMzLDcwLjUwOCBDNi40MzMsNzAuOTA1IDYuNjMxLDcxLjEzMyA2LjkyLDcxLjEzMyBDNy4wMzksNzEuMTMzIDcuMTc0LDcxLjA5NCA3LjMxNyw3MS4wMTIgQzcuODA2LDcwLjczIDguMjAyLDcwLjA0NyA4LjIwMiw2OS40ODcgQzguMjAyLDY5LjA5IDguMDAzLDY4Ljg2MSA3LjcxNCw2OC44NjEiIGlkPSJGaWxsLTE1IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTcuNDQ0LDg1LjM1IEM3LjcwOCw4NS4xOTggNy45MjEsODUuMzE5IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuOTI1IDcuNzA4LDg2LjI5MiA3LjQ0NCw4Ni40NDQgQzcuMTgxLDg2LjU5NyA2Ljk2Nyw4Ni40NzUgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IiBpZD0iRmlsbC0xNiIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik03LjIzLDg2LjUxIEM3LjA3NCw4Ni41MSA2Ljk2Nyw4Ni4zODcgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IEM3LjUyMSw4NS4zMDUgNy41OTQsODUuMjg0IDcuNjU4LDg1LjI4NCBDNy44MTQsODUuMjg0IDcuOTIxLDg1LjQwOCA3LjkyMSw4NS42MjIgQzcuOTIxLDg1LjkyNSA3LjcwOCw4Ni4yOTIgNy40NDQsODYuNDQ0IEM3LjM2Nyw4Ni40ODkgNy4yOTQsODYuNTEgNy4yMyw4Ni41MSBNNy42NTgsODUuMDk4IEM3LjU1OCw4NS4wOTggNy40NTUsODUuMTI3IDcuMzUxLDg1LjE4OCBDNy4wMzEsODUuMzczIDYuNzgxLDg1LjgwNiA2Ljc4MSw4Ni4xNzMgQzYuNzgxLDg2LjQ4MiA2Ljk2Niw4Ni42OTcgNy4yMyw4Ni42OTcgQzcuMzMsODYuNjk3IDcuNDMzLDg2LjY2NiA3LjUzOCw4Ni42MDcgQzcuODU4LDg2LjQyMiA4LjEwOCw4NS45ODkgOC4xMDgsODUuNjIyIEM4LjEwOCw4NS4zMTMgNy45MjMsODUuMDk4IDcuNjU4LDg1LjA5OCIgaWQ9IkZpbGwtMTciIGZpbGw9IiM4MDk3QTIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4yMyw4Ni4zMjIgTDcuMTU0LDg2LjE3MyBDNy4xNTQsODUuOTM4IDcuMzMzLDg1LjYyOSA3LjUzOCw4NS41MTIgTDcuNjU4LDg1LjQ3MSBMNy43MzQsODUuNjIyIEM3LjczNCw4NS44NTYgNy41NTUsODYuMTY0IDcuMzUxLDg2LjI4MiBMNy4yMyw4Ni4zMjIgTTcuNjU4LDg1LjI4NCBDNy41OTQsODUuMjg0IDcuNTIxLDg1LjMwNSA3LjQ0NCw4NS4zNSBDNy4xODEsODUuNTAyIDYuOTY3LDg1Ljg3MSA2Ljk2Nyw4Ni4xNzMgQzYuOTY3LDg2LjM4NyA3LjA3NCw4Ni41MSA3LjIzLDg2LjUxIEM3LjI5NCw4Ni41MSA3LjM2Nyw4Ni40ODkgNy40NDQsODYuNDQ0IEM3LjcwOCw4Ni4yOTIgNy45MjEsODUuOTI1IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuNDA4IDcuODE0LDg1LjI4NCA3LjY1OCw4NS4yODQiIGlkPSJGaWxsLTE4IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTc3LjI3OCw3Ljc2OSBMNzcuMjc4LDUxLjQzNiBMMTAuMjA4LDkwLjE2IEwxMC4yMDgsNDYuNDkzIEw3Ny4yNzgsNy43NjkiIGlkPSJGaWxsLTE5IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEwLjA4Myw5MC4zNzUgTDEwLjA4Myw0Ni40MjEgTDEwLjE0Niw0Ni4zODUgTDc3LjQwMyw3LjU1NCBMNzcuNDAzLDUxLjUwOCBMNzcuMzQxLDUxLjU0NCBMMTAuMDgzLDkwLjM3NSBMMTAuMDgzLDkwLjM3NSBaIE0xMC4zMzMsNDYuNTY0IEwxMC4zMzMsODkuOTQ0IEw3Ny4xNTQsNTEuMzY1IEw3Ny4xNTQsNy45ODUgTDEwLjMzMyw0Ni41NjQgTDEwLjMzMyw0Ni41NjQgWiIgaWQ9IkZpbGwtMjAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMjUuNzM3LDg4LjY0NyBMMTE4LjA5OCw5MS45ODEgTDExOC4wOTgsODQgTDEwNi42MzksODguNzEzIEwxMDYuNjM5LDk2Ljk4MiBMOTksMTAwLjMxNSBMMTEyLjM2OSwxMDMuOTYxIEwxMjUuNzM3LDg4LjY0NyIgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTIiIGZpbGw9IiM0NTVBNjQiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+")},t.exports=n},{"./util.js":26}],21:[function(e,t,i){function n(e){this.kFilter=e,this.currentAccelMeasurement=new r,this.currentGyroMeasurement=new r,this.previousGyroMeasurement=new r,s.isIOS()?this.filterQ=new a.Quaternion(-1,0,0,1):this.filterQ=new a.Quaternion(1,0,0,1),this.previousFilterQ=new a.Quaternion,this.previousFilterQ.copy(this.filterQ),this.accelQ=new a.Quaternion,this.isOrientationInitialized=!1,this.estimatedGravity=new a.Vector3,this.measuredGravity=new a.Vector3,this.gyroIntegralQ=new a.Quaternion}var r=e("./sensor-sample.js"),a=e("../math-util.js"),s=e("../util.js");n.prototype.addAccelMeasurement=function(e,t){this.currentAccelMeasurement.set(e,t)},n.prototype.addGyroMeasurement=function(e,t){this.currentGyroMeasurement.set(e,t);var i=t-this.previousGyroMeasurement.timestampS;s.isTimestampDeltaValid(i)&&this.run_(),this.previousGyroMeasurement.copy(this.currentGyroMeasurement)},n.prototype.run_=function(){if(!this.isOrientationInitialized)return this.accelQ=this.accelToQuaternion_(this.currentAccelMeasurement.sample),this.previousFilterQ.copy(this.accelQ),void(this.isOrientationInitialized=!0);var e=this.currentGyroMeasurement.timestampS-this.previousGyroMeasurement.timestampS,t=this.gyroToQuaternionDelta_(this.currentGyroMeasurement.sample,e);this.gyroIntegralQ.multiply(t),this.filterQ.copy(this.previousFilterQ),this.filterQ.multiply(t);var i=new a.Quaternion;i.copy(this.filterQ),i.inverse(),this.estimatedGravity.set(0,0,-1),this.estimatedGravity.applyQuaternion(i),this.estimatedGravity.normalize(),this.measuredGravity.copy(this.currentAccelMeasurement.sample),this.measuredGravity.normalize();var n=new a.Quaternion;n.setFromUnitVectors(this.estimatedGravity,this.measuredGravity),n.inverse(),s.isDebug()&&console.log("Delta: %d deg, G_est: (%s, %s, %s), G_meas: (%s, %s, %s)",a.radToDeg*s.getQuaternionAngle(n),this.estimatedGravity.x.toFixed(1),this.estimatedGravity.y.toFixed(1),this.estimatedGravity.z.toFixed(1),this.measuredGravity.x.toFixed(1),this.measuredGravity.y.toFixed(1),this.measuredGravity.z.toFixed(1));var r=new a.Quaternion;r.copy(this.filterQ),r.multiply(n),this.filterQ.slerp(r,1-this.kFilter),this.previousFilterQ.copy(this.filterQ)},n.prototype.getOrientation=function(){return this.filterQ},n.prototype.accelToQuaternion_=function(e){var t=new a.Vector3;t.copy(e),t.normalize();var i=new a.Quaternion;return i.setFromUnitVectors(new a.Vector3(0,0,-1),t),i.inverse(),i},n.prototype.gyroToQuaternionDelta_=function(e,t){var i=new a.Quaternion,n=new a.Vector3;return n.copy(e),n.normalize(),i.setFromAxisAngle(n,e.length()*t),i},t.exports=n},{"../math-util.js":17,"../util.js":26,"./sensor-sample.js":24}],22:[function(e,t,i){function n(){this.deviceId="webvr-polyfill:fused",this.deviceName="VR Position Device (webvr-polyfill:fused)",this.accelerometer=new o.Vector3,this.gyroscope=new o.Vector3,this.start(),this.filter=new r(window.WebVRConfig.K_FILTER),this.posePredictor=new a(window.WebVRConfig.PREDICTION_TIME_S),this.touchPanner=new s,this.filterToWorldQ=new o.Quaternion,u.isIOS()?this.filterToWorldQ.setFromAxisAngle(new o.Vector3(1,0,0),Math.PI/2):this.filterToWorldQ.setFromAxisAngle(new o.Vector3(1,0,0),-Math.PI/2),this.inverseWorldToScreenQ=new o.Quaternion,this.worldToScreenQ=new o.Quaternion,this.originalPoseAdjustQ=new o.Quaternion,this.originalPoseAdjustQ.setFromAxisAngle(new o.Vector3(0,0,1),-window.orientation*Math.PI/180),this.setScreenTransform_(),u.isLandscapeMode()&&this.filterToWorldQ.multiply(this.inverseWorldToScreenQ),this.resetQ=new o.Quaternion,this.isFirefoxAndroid=u.isFirefoxAndroid(),this.isIOS=u.isIOS(),this.orientationOut_=new Float32Array(4)}var r=e("./complementary-filter.js"),a=e("./pose-predictor.js"),s=e("../touch-panner.js"),o=e("../math-util.js"),u=e("../util.js");n.prototype.getPosition=function(){return null},n.prototype.getOrientation=function(){var e=this.filter.getOrientation();this.predictedQ=this.posePredictor.getPrediction(e,this.gyroscope,this.previousTimestampS);var t=new o.Quaternion;return t.copy(this.filterToWorldQ),t.multiply(this.resetQ),window.WebVRConfig.TOUCH_PANNER_DISABLED||t.multiply(this.touchPanner.getOrientation()),t.multiply(this.predictedQ),t.multiply(this.worldToScreenQ),window.WebVRConfig.YAW_ONLY&&(t.x=0,t.z=0,t.normalize()),this.orientationOut_[0]=t.x,this.orientationOut_[1]=t.y,this.orientationOut_[2]=t.z,this.orientationOut_[3]=t.w,this.orientationOut_},n.prototype.resetPose=function(){this.resetQ.copy(this.filter.getOrientation()),this.resetQ.x=0,this.resetQ.y=0,this.resetQ.z*=-1,this.resetQ.normalize(),u.isLandscapeMode()&&this.resetQ.multiply(this.inverseWorldToScreenQ),this.resetQ.multiply(this.originalPoseAdjustQ),window.WebVRConfig.TOUCH_PANNER_DISABLED||this.touchPanner.resetSensor()},n.prototype.onDeviceMotion_=function(e){this.updateDeviceMotion_(e)},n.prototype.updateDeviceMotion_=function(e){var t=e.accelerationIncludingGravity,i=e.rotationRate,n=e.timeStamp/1e3;this.isFirefoxAndroid&&(n/=1e3);var r=n-this.previousTimestampS;if(r<=u.MIN_TIMESTEP||r>u.MAX_TIMESTEP)return console.warn("Invalid timestamps detected. Time step between successive gyroscope sensor samples is very small or not monotonic"),void(this.previousTimestampS=n);this.accelerometer.set(-t.x,-t.y,-t.z),this.gyroscope.set(i.alpha,i.beta,i.gamma),(this.isIOS||this.isFirefoxAndroid)&&this.gyroscope.multiplyScalar(Math.PI/180),this.filter.addAccelMeasurement(this.accelerometer,n),this.filter.addGyroMeasurement(this.gyroscope,n),this.previousTimestampS=n},n.prototype.onOrientationChange_=function(e){this.setScreenTransform_()},n.prototype.onMessage_=function(e){var t=e.data;t&&t.type&&"devicemotion"===t.type.toLowerCase()&&this.updateDeviceMotion_(t.deviceMotionEvent)},n.prototype.setScreenTransform_=function(){switch(this.worldToScreenQ.set(0,0,0,1),window.orientation){case 0:break;case 90:this.worldToScreenQ.setFromAxisAngle(new o.Vector3(0,0,1),-Math.PI/2);break;case-90:this.worldToScreenQ.setFromAxisAngle(new o.Vector3(0,0,1),Math.PI/2)}this.inverseWorldToScreenQ.copy(this.worldToScreenQ),this.inverseWorldToScreenQ.inverse()},n.prototype.start=function(){this.onDeviceMotionCallback_=this.onDeviceMotion_.bind(this),this.onOrientationChangeCallback_=this.onOrientationChange_.bind(this),this.onMessageCallback_=this.onMessage_.bind(this),u.isIOS()&&u.isInsideCrossDomainIFrame()&&window.addEventListener("message",this.onMessageCallback_),window.addEventListener("orientationchange",this.onOrientationChangeCallback_),window.addEventListener("devicemotion",this.onDeviceMotionCallback_)},n.prototype.stop=function(){window.removeEventListener("devicemotion",this.onDeviceMotionCallback_),window.removeEventListener("orientationchange",this.onOrientationChangeCallback_),window.removeEventListener("message",this.onMessageCallback_)},t.exports=n},{"../math-util.js":17,"../touch-panner.js":25,"../util.js":26,"./complementary-filter.js":21,"./pose-predictor.js":23}],23:[function(e,t,i){function n(e){this.predictionTimeS=e,this.previousQ=new r.Quaternion,this.previousTimestampS=null,this.deltaQ=new r.Quaternion,this.outQ=new r.Quaternion}var r=e("../math-util"),a=e("../util");n.prototype.getPrediction=function(e,t,i){if(!this.previousTimestampS)return this.previousQ.copy(e),this.previousTimestampS=i,e;var n=new r.Vector3;n.copy(t),n.normalize();var s=t.length();if(s<20*r.degToRad)return a.isDebug()&&console.log("Moving slowly, at %s deg/s: no prediction",(r.radToDeg*s).toFixed(1)),this.outQ.copy(e),this.previousQ.copy(e),this.outQ;this.previousTimestampS;var o=s*this.predictionTimeS;return this.deltaQ.setFromAxisAngle(n,o),this.outQ.copy(this.previousQ),this.outQ.multiply(this.deltaQ),this.previousQ.copy(e),this.previousTimestampS=i,this.outQ},t.exports=n},{"../math-util":17,"../util":26}],24:[function(e,t,i){function n(e,t){this.set(e,t)}n.prototype.set=function(e,t){this.sample=e,this.timestampS=t},n.prototype.copy=function(e){this.set(e.sample,e.timestampS)},t.exports=n},{}],25:[function(e,t,i){function n(){window.addEventListener("touchstart",this.onTouchStart_.bind(this)),window.addEventListener("touchmove",this.onTouchMove_.bind(this)),window.addEventListener("touchend",this.onTouchEnd_.bind(this)),this.isTouching=!1,this.rotateStart=new r.Vector2,this.rotateEnd=new r.Vector2,this.rotateDelta=new r.Vector2,this.theta=0,this.orientation=new r.Quaternion}var r=e("./math-util.js"),a=e("./util.js");n.prototype.getOrientation=function(){return this.orientation.setFromEulerXYZ(0,0,this.theta),this.orientation},n.prototype.resetSensor=function(){this.theta=0},n.prototype.onTouchStart_=function(e){e.touches&&1==e.touches.length&&(this.rotateStart.set(e.touches[0].pageX,e.touches[0].pageY),this.isTouching=!0)},n.prototype.onTouchMove_=function(e){if(this.isTouching){this.rotateEnd.set(e.touches[0].pageX,e.touches[0].pageY),this.rotateDelta.subVectors(this.rotateEnd,this.rotateStart),this.rotateStart.copy(this.rotateEnd),a.isIOS()&&(this.rotateDelta.x*=-1);var t=document.body;this.theta+=2*Math.PI*this.rotateDelta.x/t.clientWidth*.5}},n.prototype.onTouchEnd_=function(e){this.isTouching=!1},t.exports=n},{"./math-util.js":17,"./util.js":26}],26:[function(e,t,i){var n=window.Util||{};n.MIN_TIMESTEP=.001,n.MAX_TIMESTEP=1,n.base64=function(e,t){return"data:"+e+";base64,"+t},n.clamp=function(e,t,i){return Math.min(Math.max(t,e),i)},n.lerp=function(e,t,i){return e+(t-e)*i},n.race=function(e){return Promise.race?Promise.race(e):new Promise(function(t,i){for(var n=0;nn.MAX_TIMESTEP))},n.getScreenWidth=function(){return Math.max(window.screen.width,window.screen.height)*window.devicePixelRatio},n.getScreenHeight=function(){return Math.min(window.screen.width,window.screen.height)*window.devicePixelRatio},n.requestFullscreen=function(e){if(n.isWebViewAndroid())return!1;if(e.requestFullscreen)e.requestFullscreen();else if(e.webkitRequestFullscreen)e.webkitRequestFullscreen();else if(e.mozRequestFullScreen)e.mozRequestFullScreen();else{if(!e.msRequestFullscreen)return!1;e.msRequestFullscreen()}return!0},n.exitFullscreen=function(){if(document.exitFullscreen)document.exitFullscreen();else if(document.webkitExitFullscreen)document.webkitExitFullscreen();else if(document.mozCancelFullScreen)document.mozCancelFullScreen();else{if(!document.msExitFullscreen)return!1;document.msExitFullscreen()}return!0},n.getFullscreenElement=function(){return document.fullscreenElement||document.webkitFullscreenElement||document.mozFullScreenElement||document.msFullscreenElement},n.linkProgram=function(e,t,i,n){var r=e.createShader(e.VERTEX_SHADER);e.shaderSource(r,t),e.compileShader(r);var a=e.createShader(e.FRAGMENT_SHADER);e.shaderSource(a,i),e.compileShader(a);var s=e.createProgram();e.attachShader(s,r),e.attachShader(s,a);for(var o in n)e.bindAttribLocation(s,n[o],o);return e.linkProgram(s),e.deleteShader(r),e.deleteShader(a),s},n.getProgramUniforms=function(e,t){for(var i={},n=e.getProgramParameter(t,e.ACTIVE_UNIFORMS),r="",a=0;a-1?e.split("/")[2]:e.split("/")[0],t=t.split(":")[0]},t.exports=n},{}],27:[function(e,t,i){function n(){try{this.selectedKey=localStorage.getItem(s)}catch(e){console.error("Failed to load viewer profile: %s",e)}this.selectedKey||(this.selectedKey=a),this.dialog=this.createDialog_(r.Viewers),this.root=null,this.onChangeCallbacks_=[]}var r=e("./device-info.js"),a=(e("./util.js"),"CardboardV1"),s="WEBVR_CARDBOARD_VIEWER";n.prototype.show=function(e){this.root=e,e.appendChild(this.dialog),this.dialog.querySelector("#"+this.selectedKey).checked=!0,this.dialog.style.display="block"},n.prototype.hide=function(){this.root&&this.root.contains(this.dialog)&&this.root.removeChild(this.dialog),this.dialog.style.display="none"},n.prototype.getCurrentViewer=function(){return r.Viewers[this.selectedKey]},n.prototype.getSelectedKey_=function(){var e=this.dialog.querySelector("input[name=field]:checked");return e?e.id:null},n.prototype.onChange=function(e){this.onChangeCallbacks_.push(e)},n.prototype.fireOnChange_=function(e){for(var t=0;t0?i:e})},n.prototype.getVRDevices=function(){console.warn("getVRDevices is deprecated. Please update your code to use getVRDisplays instead.");var e=this;return new Promise(function(t,i){try{if(!e.devicesPopulated){if(e.nativeWebVRAvailable)return navigator.getVRDisplays(function(i){for(var n=0;n0)&&!(e.indexOf("Trident/")>0)}}},i=function(e){var t,i,n;for(i=1;i0?(v("WebVR supported, VRDisplays found."),E=e[0],v(E)):f.error({code:"web-vr-no-devices-found",dismiss:!1})}):navigator.getVRDevices?f.error({code:"web-vr-out-of-date",dismiss:!1}):f.error({code:"web-vr-not-supported",dismiss:!1}),window.addEventListener("orientationchange",function(){(-90==window.orientation||90==window.orientation)&&screen.height>screen.width?x.aspect=screen.height/screen.width:x.aspect=screen.width/screen.height,x.updateProjectionMatrix()},!1),function D(){if(m.readyState===m.HAVE_ENOUGH_DATA&&g&&(g.needsUpdate=!0),c.update(),L.render(l,x),E){if(E.requestAnimationFrame(D),navigator.getGamepads)for(var e=navigator.getGamepads(),t=0;twebvr.info for more info."},"web-vr-not-supported":{headline:"360 not supported on this device",type:"360_NOT_SUPPORTED",message:"Your browser does not support 360. See webvr.info for assistance."}}}),m!=undefined&&null!=m&&"NONE"!==y)return f.on("loadstart",function(){a()}),f.ready(function(){t.browser.isMobile()&&function(e){var i=videojs.getComponent("Button"),n=videojs.extend(i,{constructor:function(){i.apply(this,arguments),v(this)},handleClick:function(){var e=new Event("vrdisplayactivate");window.dispatchEvent(e)},buildCSSClass:function(){return i.prototype.buildCSSClass.call(this)+"vjs-button-vr"}});videojs.registerComponent("MyButton",n),e.addChild("myButton",{}),t.browser.isIOS()&&f.controlBar.fullscreenToggle.hide()}(f.controlBar)}),{changeProjection:r}})}(window.videojs)},{}],32:[function(e,t,i){t.exports={FORCE_ENABLE_VR:!1,K_FILTER:.98,PREDICTION_TIME_S:.04,TOUCH_PANNER_DISABLED:!1,YAW_ONLY:!1,MOUSE_KEYBOARD_CONTROLS_DISABLED:!1,DEFER_INITIALIZATION:!1,ENABLE_DEPRECATED_API:!1,BUFFER_SCALE:.5,DIRTY_SUBMIT_FRAME_BINDINGS:!1}},{}]},{},[30]); \ No newline at end of file diff --git a/example-external.html b/example-external.html deleted file mode 100644 index 31919423..00000000 --- a/example-external.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - videojs-vr example - - - - - - - - - - - - - - - - - - diff --git a/example-iframe.html b/example-iframe.html deleted file mode 100644 index 8204c472..00000000 --- a/example-iframe.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - Title - - - - - - - \ No newline at end of file diff --git a/example-lazyload-video.html b/example-lazyload-video.html deleted file mode 100644 index fd0116b8..00000000 --- a/example-lazyload-video.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - videojs-vr example - - - - - - - - - - - - - - - - - - diff --git a/example-videocloud.html b/example-videocloud.html deleted file mode 100644 index abfdfd2c..00000000 --- a/example-videocloud.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - videojs-vr example - - - - - - - - - - - - - - - - - diff --git a/index.html b/index.html new file mode 100644 index 00000000..245740ea --- /dev/null +++ b/index.html @@ -0,0 +1,25 @@ + + + + + videojs-vr Demo + + + + + + + + + + + diff --git a/jsdoc.json b/jsdoc.json new file mode 100644 index 00000000..df0756c6 --- /dev/null +++ b/jsdoc.json @@ -0,0 +1,3 @@ +{ + "plugins": ["plugins/markdown"] +} diff --git a/package.json b/package.json index e473833b..b7183b06 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { - "name": "videojs-bc-vr", + "name": "videojs-vr", "version": "0.3.4", + "description": "A plugin to add 360 and VR video support to video.js.", "author": { "name": "James Broberg", "email": "james.broberg@metacdn.com" @@ -13,36 +14,119 @@ { "name": "Mitchell Trent", "email": "mitch@tofi.com.au" + }, + { + "name": "Brandon Casey", + "email": "branonocasey@gmail.com" } ], - "engines": { - "node": ">= 5.0.0" - }, "license": "MIT", "repository": { "type": "git", - "url": "https://github.com/brightcove/videojs-bc-vr" + "url": "https://github.com/videojs/videojs-vr" + }, + "main": "dist/videojs-vr.cjs.js", + "module": "dist/videojs-vr.es.js", + "generator-videojs-plugin": { + "version": "5.0.0" }, - "main": "src/js/videocloud.vr.js", "scripts": { - "build-js": "browserify ./src/js/videocloud.vr.js -v -d > ./dist/videocloud.vr.js", - "build-uglify": "uglifyjs dist/videocloud.vr.js --comments --mangle --compress --ie8 -o dist/videocloud.vr.min.js", - "build-scss": "node-sass ./src/css/videocloud.vr.scss ./dist/videocloud.vr.css", - "build": "rm -rf dist && mkdir dist && npm run build-js && npm run build-uglify && npm run build-scss", - "serve": "npm run build && live-server --port=3000" + "prebuild": "npm run clean", + "build": "npm-run-all -p build:*", + "build:css": "npm-run-all build:css:sass build:css:bannerize", + "build:css:sass": "node-sass src/plugin.scss dist/videojs-vr.css --output-style=compressed --linefeed=lf", + "build:css:bannerize": "bannerize dist/videojs-vr.css --banner=scripts/banner.ejs", + "build:js": "npm-run-all build:js:rollup-modules build:js:rollup-umd build:js:bannerize build:js:uglify", + "build:js:bannerize": "bannerize dist/videojs-vr.js --banner=scripts/banner.ejs", + "build:js:rollup-modules": "rollup -c scripts/modules.rollup.config.js", + "build:js:rollup-umd": "rollup -c scripts/umd.rollup.config.js", + "build:js:uglify": "uglifyjs dist/videojs-vr.js --comments --mangle --compress --ie8 -o dist/videojs-vr.min.js", + "build:test": "rollup -c scripts/test.rollup.config.js", + "clean": "rimraf dist test/dist", + "postclean": "mkdirp dist test/dist", + "docs": "npm-run-all docs:*", + "docs:api": "jsdoc src -r -c jsdoc.json -d docs/api", + "docs:toc": "doctoc README.md", + "lint": "vjsstandard", + "prestart": "npm run build", + "start": "npm-run-all -p start:server watch", + "start:server": "static -a 0.0.0.0 -p 9999 -H '{\"Cache-Control\": \"no-cache, must-revalidate\"}' .", + "pretest": "npm-run-all lint build", + "test": "karma start test/karma.conf.js", + "preversion": "npm test", + "version": "node scripts/version.js", + "watch": "npm-run-all -p watch:*", + "watch:css": "node-sass src/plugin.scss dist/videojs-vr.css --output-style=compressed --linefeed=lf --watch src/**/*.scss", + "watch:js-modules": "rollup -c scripts/modules.rollup.config.js -w", + "watch:js-umd": "rollup -c scripts/umd.rollup.config.js -w", + "watch:test": "rollup -c scripts/test.rollup.config.js -w", + "prepublish": "npm run build", + "prepush": "npm run lint", + "precommit": "npm run docs && git add README.md" }, + "keywords": [ + "videojs", + "videojs-plugin" + ], + "vjsstandard": { + "ignore": [ + "dist", + "docs", + "test/dist", + "test/karma.conf.js", + "src/VRControls.js", + "src/VREffect.js" + ] + }, + "files": [ + "CONTRIBUTING.md", + "dist/", + "docs/", + "index.html", + "scripts/", + "src/", + "test/" + ], "dependencies": { - "three": "0.83.0", + "global": "^4.3.2", + "three": "^0.83.0", + "video.js": "^6.2.0", "webvr-boilerplate": "MetaCDN/webvr-boilerplate", - "webvr-polyfill": "^0.9.35" + "webvr-polyfill": "^0.9.23" }, "devDependencies": { - "browserify": "13.0.0", - "live-server": "^1.2.0", + "babel-plugin-external-helpers": "^6.22.0", + "babel-plugin-transform-object-assign": "^6.8.0", + "babel-preset-es2015": "^6.14.0", + "bannerize": "^1.0.2", + "conventional-changelog-cli": "^1.3.1", + "conventional-changelog-videojs": "^3.0.0", + "doctoc": "^1.3.0", + "husky": "^0.13.3", + "jsdoc": "^3.4.3", + "karma": "~1.3.0", + "karma-chrome-launcher": "^2.1.1", + "karma-detect-browsers": "^2.2.5", + "karma-firefox-launcher": "^1.0.1", + "karma-ie-launcher": "^1.0.0", + "karma-qunit": "^1.2.1", + "karma-safari-launcher": "^1.0.0", + "mkdirp": "^0.5.1", + "node-static": "^0.7.9", "node-sass": "^4.5.3", - "three": "0.83.0", - "uglify-js": "^3.0.20", - "webvr-boilerplate": "MetaCDN/webvr-boilerplate", - "webvr-polyfill": "^0.9.35" + "npm-run-all": "^4.0.2", + "qunitjs": "^1.21.0", + "rimraf": "^2.6.1", + "rollup": "^0.41.6", + "rollup-plugin-babel": "^2.7.1", + "rollup-plugin-commonjs": "^8.0.2", + "rollup-plugin-json": "^2.1.1", + "rollup-plugin-multi-entry": "^2.0.1", + "rollup-plugin-node-resolve": "^3.0.0", + "rollup-watch": "^3.2.2", + "semver": "^5.3.0", + "sinon": "^2.2.0", + "uglify-js": "^3.0.7", + "videojs-standard": "^6.0.0" } } diff --git a/samples/eagle-360.mp4 b/samples/eagle-360.mp4 new file mode 100644 index 00000000..f69a2243 Binary files /dev/null and b/samples/eagle-360.mp4 differ diff --git a/scripts/banner.ejs b/scripts/banner.ejs new file mode 100644 index 00000000..4966491b --- /dev/null +++ b/scripts/banner.ejs @@ -0,0 +1,6 @@ +/** + * <%- pkg.name %> + * @version <%- pkg.version %> + * @copyright <%- date.getFullYear() %> <%- pkg.author %> + * @license <%- pkg.license %> + */ diff --git a/scripts/modules.rollup.config.js b/scripts/modules.rollup.config.js new file mode 100644 index 00000000..bc6926f7 --- /dev/null +++ b/scripts/modules.rollup.config.js @@ -0,0 +1,39 @@ +/** + * Rollup configuration for packaging the plugin in a module that is consumable + * by either CommonJS (e.g. Node or Browserify) or ECMAScript (e.g. Rollup). + * + * These modules DO NOT include their dependencies as we expect those to be + * handled by the module system. + */ +import babel from 'rollup-plugin-babel'; +import json from 'rollup-plugin-json'; + +export default { + moduleName: 'videojsVr', + entry: 'src/plugin.js', + external: ['video.js'], + globals: { + 'video.js': 'videojs' + }, + plugins: [ + json(), + babel({ + babelrc: false, + exclude: 'node_modules/**', + presets: [ + ['es2015', { + loose: true, + modules: false + }] + ], + plugins: [ + 'external-helpers', + 'transform-object-assign' + ] + }) + ], + targets: [ + {dest: 'dist/videojs-vr.cjs.js', format: 'cjs'}, + {dest: 'dist/videojs-vr.es.js', format: 'es'} + ] +}; diff --git a/scripts/test.rollup.config.js b/scripts/test.rollup.config.js new file mode 100644 index 00000000..946e17cf --- /dev/null +++ b/scripts/test.rollup.config.js @@ -0,0 +1,57 @@ +/** + * Rollup configuration for packaging the plugin in a test bundle. + * + * This includes all dependencies for both the plugin and its tests. + */ +import babel from 'rollup-plugin-babel'; +import commonjs from 'rollup-plugin-commonjs'; +import json from 'rollup-plugin-json'; +import multiEntry from 'rollup-plugin-multi-entry'; +import resolve from 'rollup-plugin-node-resolve'; + +export default { + moduleName: 'videojsVrTests', + entry: 'test/**/*.test.js', + dest: 'test/dist/bundle.js', + format: 'iife', + external: [ + 'qunit', + 'qunitjs', + 'sinon', + 'video.js' + ], + globals: { + 'qunit': 'QUnit', + 'qunitjs': 'QUnit', + 'sinon': 'sinon', + 'video.js': 'videojs' + }, + plugins: [ + multiEntry({ + exports: false + }), + resolve({ + browser: true, + main: true, + jsnext: true + }), + json(), + commonjs({ + sourceMap: false + }), + babel({ + babelrc: false, + exclude: 'node_modules/**', + presets: [ + ['es2015', { + loose: true, + modules: false + }] + ], + plugins: [ + 'external-helpers', + 'transform-object-assign' + ] + }) + ] +}; diff --git a/scripts/umd.rollup.config.js b/scripts/umd.rollup.config.js new file mode 100644 index 00000000..4a8751a2 --- /dev/null +++ b/scripts/umd.rollup.config.js @@ -0,0 +1,46 @@ +/** + * Rollup configuration for packaging the plugin in a module that is consumable + * as the `src` of a `script` tag or via AMD or similar client-side loading. + * + * This module DOES include its dependencies. + */ +import babel from 'rollup-plugin-babel'; +import commonjs from 'rollup-plugin-commonjs'; +import json from 'rollup-plugin-json'; +import resolve from 'rollup-plugin-node-resolve'; + +export default { + moduleName: 'videojsVr', + entry: 'src/plugin.js', + dest: 'dist/videojs-vr.js', + format: 'umd', + external: ['video.js'], + globals: { + 'video.js': 'videojs' + }, + plugins: [ + resolve({ + browser: true, + main: true, + jsnext: true + }), + json(), + commonjs({ + sourceMap: false + }), + babel({ + babelrc: false, + exclude: 'node_modules/**', + presets: [ + ['es2015', { + loose: true, + modules: false + }] + ], + plugins: [ + 'external-helpers', + 'transform-object-assign' + ] + }) + ] +}; diff --git a/scripts/version.js b/scripts/version.js new file mode 100644 index 00000000..d2e93ecb --- /dev/null +++ b/scripts/version.js @@ -0,0 +1,10 @@ +const execSync = require('child_process').execSync; +const path = require('path'); +const semver = require('semver'); +const pkg = require('../package.json'); + +if (!semver.prerelease(pkg.version)) { + process.chdir(path.resolve(__dirname, '..')); + execSync('conventional-changelog -p videojs -i CHANGELOG.md -s'); + execSync('git add CHANGELOG.md'); +} diff --git a/src/VRControls.js b/src/VRControls.js new file mode 100644 index 00000000..b49c6662 --- /dev/null +++ b/src/VRControls.js @@ -0,0 +1,187 @@ +/** + * NOTE THAT THIS CODE WAS NOT WRITTEN BY US! + * + * This code is a slightly modified version of code from three.js to allow for an + * export of the VRControls variable from an example. The original code can be found here: + * * https://github.com/mrdoob/three.js/blob/dev/examples/js/controls/VRControls.js + * * node_modules/three/examples/js/controls/VRControls.js + * + * NOTE THAT THIS CODE WAS NOT WRITTEN BY US! + **/ +/** + * @author dmarcos / https://github.com/dmarcos + * @author mrdoob / http://mrdoob.com + */ + +import * as THREE from 'three'; + +var VRControls = function ( object, onError ) { + + var scope = this; + + var vrDisplay, vrDisplays; + + var standingMatrix = new THREE.Matrix4(); + + var frameData = null; + + if ( 'VRFrameData' in window ) { + + frameData = new VRFrameData(); + + } + + function gotVRDisplays( displays ) { + + vrDisplays = displays; + + if ( displays.length > 0 ) { + + vrDisplay = displays[ 0 ]; + + } else { + + if ( onError ) onError( 'VR input not available.' ); + + } + + } + + if ( navigator.getVRDisplays ) { + + navigator.getVRDisplays().then( gotVRDisplays ).catch ( function () { + + console.warn( 'THREE.VRControls: Unable to get VR Displays' ); + + } ); + + } + + // the Rift SDK returns the position in meters + // this scale factor allows the user to define how meters + // are converted to scene units. + + this.scale = 1; + + // If true will use "standing space" coordinate system where y=0 is the + // floor and x=0, z=0 is the center of the room. + this.standing = false; + + // Distance from the users eyes to the floor in meters. Used when + // standing=true but the VRDisplay doesn't provide stageParameters. + this.userHeight = 1.6; + + this.getVRDisplay = function () { + + return vrDisplay; + + }; + + this.setVRDisplay = function ( value ) { + + vrDisplay = value; + + }; + + this.getVRDisplays = function () { + + console.warn( 'THREE.VRControls: getVRDisplays() is being deprecated.' ); + return vrDisplays; + + }; + + this.getStandingMatrix = function () { + + return standingMatrix; + + }; + + this.update = function () { + + if ( vrDisplay ) { + + var pose; + + if ( vrDisplay.getFrameData ) { + + vrDisplay.getFrameData( frameData ); + pose = frameData.pose; + + } else if ( vrDisplay.getPose ) { + + pose = vrDisplay.getPose(); + + } + + if ( pose.orientation !== null ) { + + object.quaternion.fromArray( pose.orientation ); + + } + + if ( pose.position !== null ) { + + object.position.fromArray( pose.position ); + + } else { + + object.position.set( 0, 0, 0 ); + + } + + if ( this.standing ) { + + if ( vrDisplay.stageParameters ) { + + object.updateMatrix(); + + standingMatrix.fromArray( vrDisplay.stageParameters.sittingToStandingTransform ); + object.applyMatrix( standingMatrix ); + + } else { + + object.position.setY( object.position.y + this.userHeight ); + + } + + } + + object.position.multiplyScalar( scope.scale ); + + } + + }; + + this.resetPose = function () { + + if ( vrDisplay ) { + + vrDisplay.resetPose(); + + } + + }; + + this.resetSensor = function () { + + console.warn( 'THREE.VRControls: .resetSensor() is now .resetPose().' ); + this.resetPose(); + + }; + + this.zeroSensor = function () { + + console.warn( 'THREE.VRControls: .zeroSensor() is now .resetPose().' ); + this.resetPose(); + + }; + + this.dispose = function () { + + vrDisplay = null; + + }; + +}; + +export default VRControls; diff --git a/src/VREffect.js b/src/VREffect.js new file mode 100644 index 00000000..df622a25 --- /dev/null +++ b/src/VREffect.js @@ -0,0 +1,490 @@ + +/** + * NOTE THAT THIS CODE WAS NOT WRITTEN BY US! + * + * This code is a slightly modified version of code from three.js to allow for an + * export of the VREffect variable from an example. The original code can be found here: + * * https://github.com/mrdoob/three.js/blob/dev/examples/js/effects/VREffect.js + * * node_modules/three/examples/js/effects/VREffect.js + * + * NOTE THAT THIS CODE WAS NOT WRITTEN BY US! + **/ +/** + * @author dmarcos / https://github.com/dmarcos + * @author mrdoob / http://mrdoob.com + * + * WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html + * + * Firefox: http://mozvr.com/downloads/ + * Chromium: https://webvr.info/get-chrome + * + */ +import * as THREE from 'three'; + +var VREffect = function( renderer, onError ) { + + var vrDisplay, vrDisplays; + var eyeTranslationL = new THREE.Vector3(); + var eyeTranslationR = new THREE.Vector3(); + var renderRectL, renderRectR; + + var frameData = null; + + if ( 'VRFrameData' in window ) { + + frameData = new window.VRFrameData(); + + } + + function gotVRDisplays( displays ) { + + vrDisplays = displays; + + if ( displays.length > 0 ) { + + vrDisplay = displays[ 0 ]; + + } else { + + if ( onError ) onError( 'HMD not available' ); + + } + + } + + if ( navigator.getVRDisplays ) { + + navigator.getVRDisplays().then( gotVRDisplays ).catch( function() { + + console.warn( 'THREE.VREffect: Unable to get VR Displays' ); + + } ); + + } + + // + + this.isPresenting = false; + this.scale = 1; + + var scope = this; + + var rendererSize = renderer.getSize(); + var rendererUpdateStyle = false; + var rendererPixelRatio = renderer.getPixelRatio(); + + this.getVRDisplay = function() { + + return vrDisplay; + + }; + + this.setVRDisplay = function( value ) { + + vrDisplay = value; + + }; + + this.getVRDisplays = function() { + + console.warn( 'THREE.VREffect: getVRDisplays() is being deprecated.' ); + return vrDisplays; + + }; + + this.setSize = function( width, height, updateStyle ) { + + rendererSize = { width: width, height: height }; + rendererUpdateStyle = updateStyle; + + if ( scope.isPresenting ) { + + var eyeParamsL = vrDisplay.getEyeParameters( 'left' ); + renderer.setPixelRatio( 1 ); + renderer.setSize( eyeParamsL.renderWidth * 2, eyeParamsL.renderHeight, false ); + + } else { + + renderer.setPixelRatio( rendererPixelRatio ); + renderer.setSize( width, height, updateStyle ); + + } + + }; + + // VR presentation + + var canvas = renderer.domElement; + var defaultLeftBounds = [ 0.0, 0.0, 0.5, 1.0 ]; + var defaultRightBounds = [ 0.5, 0.0, 0.5, 1.0 ]; + + function onVRDisplayPresentChange() { + + var wasPresenting = scope.isPresenting; + scope.isPresenting = vrDisplay !== undefined && vrDisplay.isPresenting; + + if ( scope.isPresenting ) { + + var eyeParamsL = vrDisplay.getEyeParameters( 'left' ); + var eyeWidth = eyeParamsL.renderWidth; + var eyeHeight = eyeParamsL.renderHeight; + + if ( ! wasPresenting ) { + + rendererPixelRatio = renderer.getPixelRatio(); + rendererSize = renderer.getSize(); + + renderer.setPixelRatio( 1 ); + renderer.setSize( eyeWidth * 2, eyeHeight, false ); + + } + + } else if ( wasPresenting ) { + + renderer.setPixelRatio( rendererPixelRatio ); + renderer.setSize( rendererSize.width, rendererSize.height, rendererUpdateStyle ); + + } + + } + + window.addEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange, false ); + + this.setFullScreen = function( boolean ) { + + return new Promise( function( resolve, reject ) { + + if ( vrDisplay === undefined ) { + + reject( new Error( 'No VR hardware found.' ) ); + return; + + } + + if ( scope.isPresenting === boolean ) { + + resolve(); + return; + + } + + if ( boolean ) { + + resolve( vrDisplay.requestPresent( [ { source: canvas } ] ) ); + + } else { + + resolve( vrDisplay.exitPresent() ); + + } + + } ); + + }; + + this.requestPresent = function() { + + return this.setFullScreen( true ); + + }; + + this.exitPresent = function() { + + return this.setFullScreen( false ); + + }; + + this.requestAnimationFrame = function( f ) { + + if ( vrDisplay !== undefined ) { + + return vrDisplay.requestAnimationFrame( f ); + + } else { + + return window.requestAnimationFrame( f ); + + } + + }; + + this.cancelAnimationFrame = function( h ) { + + if ( vrDisplay !== undefined ) { + + vrDisplay.cancelAnimationFrame( h ); + + } else { + + window.cancelAnimationFrame( h ); + + } + + }; + + this.submitFrame = function() { + + if ( vrDisplay !== undefined && scope.isPresenting ) { + + vrDisplay.submitFrame(); + + } + + }; + + this.autoSubmitFrame = true; + + // render + + var cameraL = new THREE.PerspectiveCamera(); + cameraL.layers.enable( 1 ); + + var cameraR = new THREE.PerspectiveCamera(); + cameraR.layers.enable( 2 ); + + this.render = function( scene, camera, renderTarget, forceClear ) { + + if ( vrDisplay && scope.isPresenting ) { + + var autoUpdate = scene.autoUpdate; + + if ( autoUpdate ) { + + scene.updateMatrixWorld(); + scene.autoUpdate = false; + + } + + var eyeParamsL = vrDisplay.getEyeParameters( 'left' ); + var eyeParamsR = vrDisplay.getEyeParameters( 'right' ); + + eyeTranslationL.fromArray( eyeParamsL.offset ); + eyeTranslationR.fromArray( eyeParamsR.offset ); + + if ( Array.isArray( scene ) ) { + + console.warn( 'THREE.VREffect.render() no longer supports arrays. Use object.layers instead.' ); + scene = scene[ 0 ]; + + } + + // When rendering we don't care what the recommended size is, only what the actual size + // of the backbuffer is. + var size = renderer.getSize(); + var layers = vrDisplay.getLayers(); + var leftBounds; + var rightBounds; + + if ( layers.length ) { + + var layer = layers[ 0 ]; + + leftBounds = layer.leftBounds !== null && layer.leftBounds.length === 4 ? layer.leftBounds : defaultLeftBounds; + rightBounds = layer.rightBounds !== null && layer.rightBounds.length === 4 ? layer.rightBounds : defaultRightBounds; + + } else { + + leftBounds = defaultLeftBounds; + rightBounds = defaultRightBounds; + + } + + renderRectL = { + x: Math.round( size.width * leftBounds[ 0 ] ), + y: Math.round( size.height * leftBounds[ 1 ] ), + width: Math.round( size.width * leftBounds[ 2 ] ), + height: Math.round( size.height * leftBounds[ 3 ] ) + }; + renderRectR = { + x: Math.round( size.width * rightBounds[ 0 ] ), + y: Math.round( size.height * rightBounds[ 1 ] ), + width: Math.round( size.width * rightBounds[ 2 ] ), + height: Math.round( size.height * rightBounds[ 3 ] ) + }; + + if ( renderTarget ) { + + renderer.setRenderTarget( renderTarget ); + renderTarget.scissorTest = true; + + } else { + + renderer.setRenderTarget( null ); + renderer.setScissorTest( true ); + + } + + if ( renderer.autoClear || forceClear ) renderer.clear(); + + if ( camera.parent === null ) camera.updateMatrixWorld(); + + camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale ); + camera.matrixWorld.decompose( cameraR.position, cameraR.quaternion, cameraR.scale ); + + var scale = this.scale; + cameraL.translateOnAxis( eyeTranslationL, scale ); + cameraR.translateOnAxis( eyeTranslationR, scale ); + + if ( vrDisplay.getFrameData ) { + + vrDisplay.depthNear = camera.near; + vrDisplay.depthFar = camera.far; + + vrDisplay.getFrameData( frameData ); + + cameraL.projectionMatrix.elements = frameData.leftProjectionMatrix; + cameraR.projectionMatrix.elements = frameData.rightProjectionMatrix; + + } else { + + cameraL.projectionMatrix = fovToProjection( eyeParamsL.fieldOfView, true, camera.near, camera.far ); + cameraR.projectionMatrix = fovToProjection( eyeParamsR.fieldOfView, true, camera.near, camera.far ); + + } + + // render left eye + if ( renderTarget ) { + + renderTarget.viewport.set( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height ); + renderTarget.scissor.set( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height ); + + } else { + + renderer.setViewport( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height ); + renderer.setScissor( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height ); + + } + renderer.render( scene, cameraL, renderTarget, forceClear ); + + // render right eye + if ( renderTarget ) { + + renderTarget.viewport.set( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height ); + renderTarget.scissor.set( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height ); + + } else { + + renderer.setViewport( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height ); + renderer.setScissor( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height ); + + } + renderer.render( scene, cameraR, renderTarget, forceClear ); + + if ( renderTarget ) { + + renderTarget.viewport.set( 0, 0, size.width, size.height ); + renderTarget.scissor.set( 0, 0, size.width, size.height ); + renderTarget.scissorTest = false; + renderer.setRenderTarget( null ); + + } else { + + renderer.setViewport( 0, 0, size.width, size.height ); + renderer.setScissorTest( false ); + + } + + if ( autoUpdate ) { + + scene.autoUpdate = true; + + } + + if ( scope.autoSubmitFrame ) { + + scope.submitFrame(); + + } + + return; + + } + + // Regular render mode if not HMD + + renderer.render( scene, camera, renderTarget, forceClear ); + + }; + + this.dispose = function() { + + window.removeEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange, false ); + + }; + + // + + function fovToNDCScaleOffset( fov ) { + + var pxscale = 2.0 / ( fov.leftTan + fov.rightTan ); + var pxoffset = ( fov.leftTan - fov.rightTan ) * pxscale * 0.5; + var pyscale = 2.0 / ( fov.upTan + fov.downTan ); + var pyoffset = ( fov.upTan - fov.downTan ) * pyscale * 0.5; + return { scale: [ pxscale, pyscale ], offset: [ pxoffset, pyoffset ] }; + + } + + function fovPortToProjection( fov, rightHanded, zNear, zFar ) { + + rightHanded = rightHanded === undefined ? true : rightHanded; + zNear = zNear === undefined ? 0.01 : zNear; + zFar = zFar === undefined ? 10000.0 : zFar; + + var handednessScale = rightHanded ? - 1.0 : 1.0; + + // start with an identity matrix + var mobj = new THREE.Matrix4(); + var m = mobj.elements; + + // and with scale/offset info for normalized device coords + var scaleAndOffset = fovToNDCScaleOffset( fov ); + + // X result, map clip edges to [-w,+w] + m[ 0 * 4 + 0 ] = scaleAndOffset.scale[ 0 ]; + m[ 0 * 4 + 1 ] = 0.0; + m[ 0 * 4 + 2 ] = scaleAndOffset.offset[ 0 ] * handednessScale; + m[ 0 * 4 + 3 ] = 0.0; + + // Y result, map clip edges to [-w,+w] + // Y offset is negated because this proj matrix transforms from world coords with Y=up, + // but the NDC scaling has Y=down (thanks D3D?) + m[ 1 * 4 + 0 ] = 0.0; + m[ 1 * 4 + 1 ] = scaleAndOffset.scale[ 1 ]; + m[ 1 * 4 + 2 ] = - scaleAndOffset.offset[ 1 ] * handednessScale; + m[ 1 * 4 + 3 ] = 0.0; + + // Z result (up to the app) + m[ 2 * 4 + 0 ] = 0.0; + m[ 2 * 4 + 1 ] = 0.0; + m[ 2 * 4 + 2 ] = zFar / ( zNear - zFar ) * - handednessScale; + m[ 2 * 4 + 3 ] = ( zFar * zNear ) / ( zNear - zFar ); + + // W result (= Z in) + m[ 3 * 4 + 0 ] = 0.0; + m[ 3 * 4 + 1 ] = 0.0; + m[ 3 * 4 + 2 ] = handednessScale; + m[ 3 * 4 + 3 ] = 0.0; + + mobj.transpose(); + + return mobj; + + } + + function fovToProjection( fov, rightHanded, zNear, zFar ) { + + var DEG2RAD = Math.PI / 180.0; + + var fovPort = { + upTan: Math.tan( fov.upDegrees * DEG2RAD ), + downTan: Math.tan( fov.downDegrees * DEG2RAD ), + leftTan: Math.tan( fov.leftDegrees * DEG2RAD ), + rightTan: Math.tan( fov.rightDegrees * DEG2RAD ) + }; + + return fovPortToProjection( fovPort, rightHanded, zNear, zFar ); + + } + +}; +export default VREffect; diff --git a/src/js/videocloud.vr.js b/src/js/videocloud.vr.js deleted file mode 100644 index a5c39c73..00000000 --- a/src/js/videocloud.vr.js +++ /dev/null @@ -1,8 +0,0 @@ -global.THREE = require('three'); -require('three/examples/js/controls/VRControls.js'); -require('three/examples/js/effects/VREffect.js'); -global.WebVRConfig = require('./webvr.config.js'); -var WebVrPolyfill = require('webvr-polyfill'); -var WebVRManager = require('webvr-boilerplate'); -window.WebVRManager = WebVRManager; -require('./videojs.vr.js'); diff --git a/src/js/videojs.vr.js b/src/js/videojs.vr.js deleted file mode 100644 index dd60eb6c..00000000 --- a/src/js/videojs.vr.js +++ /dev/null @@ -1,575 +0,0 @@ -/*! videojs-vr - v0.2.0 - 2016-09-10 */ -/* - * vr - * https://github.com/MetaCDN/videojs-vr - * - * Copyright (c) 2014 Sean Lawrence - * Copyright (c) 2015 James Broberg - * Copyright (c) 2016 Brightcove Inc., HapYak Inc., MetaCDN Pty. Ltd. - * Licensed under the MIT license. - */ -(function(vjs) { - var utils = { - browser: { - isMobile: function() { - var check = false; - (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); - return check; - }, - isIOS: function () { - return /(iPad|iPhone|iPod)/g.test(navigator.userAgent); - }, - isSafari: function(){ - return /^((?!chrome|android).)*safari/i.test(navigator.userAgent); - }, - //we need this as IE 11 reports that it has a VR display, but isnt compatible with Video as a Texture. for example - isBrowserCompatible: function () { - var ua = window.navigator.userAgent; - - var msie = ua.indexOf('MSIE '); - if (msie > 0) { - return false; - } - - var trident = ua.indexOf('Trident/'); - if (trident > 0) { - return false; - } - - // MS Edge and other browsers - return true; - } - } - }; - - /** - * Copies properties from one or more objects onto an original. - */ - var extend = function(obj /*, arg1, arg2, ... */) { - var arg, i, k; - for (i = 1; i < arguments.length; i++) { - arg = arguments[i]; - for (k in arg) { - if (arg.hasOwnProperty(k)) { - obj[k] = arg[k]; - } - } - } - return obj; - }, - - projections = ["360", "360_LR", "360_TB", "360_CUBE", "NONE", "AUTO"], - - defaults = { - projection: "AUTO", - debug: false - }, - - /** - * Initializes the plugin - */ - plugin = function(options) { - //vars global (via closure) to the plugin - var player = this, - settings = extend({}, defaults, options || {}), - videoEl = this.el().getElementsByTagName('video')[0], - container = this.el(), - current_proj = settings.projection, - movieMaterial, - movieGeometry, - movieScreen, - controls3d, - scene, - cameraVector, - x, - y, - z; - - var log = function(msg){ - if(settings.debug) { - console.log(msg); - } - }; - - function registerErrorMessages() { - player.errors({ - "errors": { - "web-vr-no-devices-found": { - "headline": "No 360 devices found", - "type": "360_NO_DEVICES_FOUND", - "message": "Your browser supports 360, but no 360 displays found." - }, - "web-vr-out-of-date": { - "headline": "360 is out of date", - "type": "360_OUT_OF_DATE", - "message": "Your browser supports 360 but not the latest version. See webvr.info for more info." - }, - "web-vr-not-supported": { - "headline": "360 not supported on this device", - "type": "360_NOT_SUPPORTED", - "message": "Your browser does not support 360. See webvr.info for assistance." - } - } - }); - } - - registerErrorMessages(); - - if (videoEl == undefined || videoEl == null) { - // Player is not using HTML5 tech, so don't init it. - return ; - } - - if (current_proj === "NONE") { - // Show raw 360 video. - return ; - } - - function changeProjection(projection) { - var position = {x:0, y:0, z:0 }; - if (scene) { - scene.remove(movieScreen); - } - if (projection === "AUTO"){ - if(player.mediainfo && player.mediainfo.projection && player.mediainfo.projection === "equirectangular"){ - current_proj = "360"; - changeProjection("360"); - } - else{ - current_proj = "NONE"; - } - } - else if (projection === "360") { - movieGeometry = new THREE.SphereBufferGeometry( 256, 32, 32 ); - - movieScreen = new THREE.Mesh( movieGeometry, movieMaterial ); - movieScreen.position.set(position.x, position.y, position.z); - - movieScreen.scale.x = -1; - movieScreen.quaternion.setFromAxisAngle({x: 0, y: 1, z: 0}, -Math.PI / 2); - scene.add(movieScreen); - - } else if ((projection === "360_LR") || (projection === "360_TB")) { - var geometry = new THREE.SphereGeometry( 256, 32, 32 ); - - // Left eye view - geometry.scale( - 1, 1, 1 ); - var uvs = geometry.faceVertexUvs[ 0 ]; - for ( var i = 0; i < uvs.length; i ++ ) { - for ( var j = 0; j < 3; j ++ ) { - if (projection === "360_LR") { - uvs[ i ][ j ].x *= 0.5; - } else { - uvs[ i ][ j ].y *= 0.5; - } - } - } - - movieGeometry = new THREE.BufferGeometry().fromGeometry( geometry ); - movieScreen = new THREE.Mesh( movieGeometry, movieMaterial ); - movieScreen.rotation.y = - Math.PI / 2; - movieScreen.layers.set( 1 ); // display in left eye only - scene.add( movieScreen ); - - // Right eye view - var geometry = new THREE.SphereGeometry( 256, 32, 32 ); - geometry.scale( - 1, 1, 1 ); - var uvs = geometry.faceVertexUvs[ 0 ]; - - for ( var i = 0; i < uvs.length; i ++ ) { - for ( var j = 0; j < 3; j ++ ) { - if (projection === "360_LR") { - uvs[ i ][ j ].x *= 0.5; - uvs[ i ][ j ].x += 0.5; - } else { - uvs[ i ][ j ].y *= 0.5; - uvs[ i ][ j ].y += 0.5; - } - } - } - - movieGeometry = new THREE.BufferGeometry().fromGeometry( geometry ); - movieScreen = new THREE.Mesh( movieGeometry, movieMaterial ); - movieScreen.rotation.y = - Math.PI / 2; - movieScreen.layers.set( 2 ); // display in right eye only - scene.add( movieScreen ); - - } else if (projection === "360_CUBE") { - // Currently doesn't work - need to figure out order of cube faces - movieGeometry = new THREE.CubeGeometry( 256, 256, 256 ); - var face1 = [new THREE.Vector2(0, .5), new THREE.Vector2(.333, .5), new THREE.Vector2(.333, 1), new THREE.Vector2(0, 1)]; - var face2 = [new THREE.Vector2(.333, .5), new THREE.Vector2(.666, .5), new THREE.Vector2(.666, 1), new THREE.Vector2(.333, 1)]; - var face3 = [new THREE.Vector2(.666, .5), new THREE.Vector2(1, .5), new THREE.Vector2(1, 1), new THREE.Vector2(0.666, 1)]; - var face4 = [new THREE.Vector2(0, 0), new THREE.Vector2(.333, 1), new THREE.Vector2(.333, .5), new THREE.Vector2(0, .5)]; - var face5 = [new THREE.Vector2(.333, 1), new THREE.Vector2(.666, 1), new THREE.Vector2(.666, .5), new THREE.Vector2(.333, .5)]; - var face6 = [new THREE.Vector2(.666, 1), new THREE.Vector2(1, 0), new THREE.Vector2(1, .5), new THREE.Vector2(.666, .5)]; - - movieGeometry.faceVertexUvs[0] = []; - - movieGeometry.faceVertexUvs[0][0] = [ face1[0], face1[1], face1[3] ]; - movieGeometry.faceVertexUvs[0][1] = [ face1[1], face1[2], face1[3] ]; - - movieGeometry.faceVertexUvs[0][2] = [ face2[0], face2[1], face2[3] ]; - movieGeometry.faceVertexUvs[0][3] = [ face2[1], face2[2], face2[3] ]; - - movieGeometry.faceVertexUvs[0][4] = [ face3[0], face3[1], face3[3] ]; - movieGeometry.faceVertexUvs[0][5] = [ face3[1], face3[2], face3[3] ]; - - movieGeometry.faceVertexUvs[0][6] = [ face4[0], face4[1], face4[3] ]; - movieGeometry.faceVertexUvs[0][7] = [ face4[1], face4[2], face4[3] ]; - - movieGeometry.faceVertexUvs[0][8] = [ face5[0], face5[1], face5[3] ]; - movieGeometry.faceVertexUvs[0][9] = [ face5[1], face5[2], face5[3] ]; - - movieGeometry.faceVertexUvs[0][10] = [ face6[0], face6[1], face6[3] ]; - movieGeometry.faceVertexUvs[0][11] = [ face6[1], face6[2], face6[3] ]; - - movieScreen = new THREE.Mesh( movieGeometry, movieMaterial ); - movieScreen.position.set(position.x, position.y, position.z); - - scene.add(movieScreen); - - } - } - - function initScene() { - - if(!utils.browser.isBrowserCompatible()) - { - player.error({code: 'web-vr-not-supported', dismiss: false}); - return; - } - - var time = Date.now(), - effect, - videoTexture, - requestId, - renderer, - camera, - renderedCanvas; - - camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1000 ); - - cameraVector = new THREE.Vector3(); // Store vector representing the direction in which the camera is looking, in world space. - - if ((current_proj === "360_LR") || (current_proj === "360_TB")) { - camera.layers.enable( 1 ); // Render left eye when not in VR mode - } - - scene = new THREE.Scene(); - - controls3d = new THREE.VRControls(camera); - - videoTexture = new THREE.VideoTexture( videoEl ); - videoTexture.generateMipmaps = false; - videoTexture.minFilter = THREE.LinearFilter; - videoTexture.magFilter = THREE.LinearFilter; - - // iOS and macOS HLS fix/hacks - // https://bugs.webkit.org/show_bug.cgi?id=163866#c3 - // https://github.com/mrdoob/three.js/issues/9754 - // On iOS with HLS, color space is wrong and texture is flipped on Y axis - // On macOS, just need to flip texture Y axis - - if (isHLS() && utils.browser.isSafari() && utils.browser.isIOS()) { - log("Safari + iOS + HLS = flipY and colorspace hack"); - videoTexture.format = THREE.RGBAFormat; - videoTexture.flipY = false; - } else if (isHLS() && utils.browser.isSafari()) { - log("Safari + HLS = flipY hack"); - videoTexture.format = THREE.RGBFormat; - videoTexture.flipY = false; - } else { - videoTexture.format = THREE.RGBFormat; - } - - if (videoTexture.format === THREE.RGBAFormat && videoTexture.flipY === false) { - movieMaterial = new THREE.ShaderMaterial({ - uniforms: { - texture: { value: videoTexture } - }, - vertexShader: [ - "varying vec2 vUV;", - "void main() {", - " vUV = vec2( uv.x, 1.0 - uv.y );", - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - "}" - ].join("\n"), - fragmentShader: [ - "uniform sampler2D texture;", - "varying vec2 vUV;", - "void main() {", - " gl_FragColor = texture2D( texture, vUV ).bgra;", - "}" - ].join("\n") - }); - } else if (videoTexture.format === THREE.RGBFormat && videoTexture.flipY === false) { - movieMaterial = new THREE.ShaderMaterial({ - uniforms: { - texture: { value: videoTexture } - }, - vertexShader: [ - "varying vec2 vUV;", - "void main() {", - " vUV = vec2( uv.x, 1.0 - uv.y );", - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - "}" - ].join("\n"), - fragmentShader: [ - "uniform sampler2D texture;", - "varying vec2 vUV;", - "void main() {", - " gl_FragColor = texture2D( texture, vUV );", - "}" - ].join("\n") - }); - } else { - movieMaterial = new THREE.MeshBasicMaterial( { map: videoTexture, overdraw: true, side:THREE.DoubleSide } ); - } - - changeProjection(current_proj); - - if(current_proj === "NONE") { - log("Projection is NONE, dont init"); - return; - } - - camera.position.set(0,0,0); - - renderer = new THREE.WebGLRenderer({ - devicePixelRatio: window.devicePixelRatio, - alpha: false, - clearColor: 0xffffff, - antialias: true - }); - - renderer.setSize(window.innerWidth, window.innerHeight); - effect = new THREE.VREffect(renderer); - effect.setSize(window.innerWidth, window.innerHeight); - - var vrDisplay = null; - var frameData = null; - - // Previous timestamps for gamepad updates - var prevTimestamps = []; - - var manager = new WebVRManager(renderer, effect, {hideButton: true}); - - renderedCanvas = renderer.domElement; - renderedCanvas.style.width = "inherit"; - renderedCanvas.style.height = "inherit"; - - container.insertBefore(renderedCanvas, container.firstChild); - videoEl.style.display = "none"; - - // Handle window resizes - function onWindowResize() { - //if (window.orientation == undefined) { - effect.setSize(window.innerWidth, window.innerHeight); - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - //} - } - - window.addEventListener('resize', onWindowResize, false); - window.addEventListener('vrdisplaypresentchange', onWindowResize, true); - - function onVRRequestPresent() { - manager.enterVRMode_(); - manager.setMode_(3); - } - - function onVRExitPresent() { - if (!vrDisplay.isPresenting) - return; - vrDisplay.exitPresent(); - } - - window.addEventListener('vrdisplayactivate', onVRRequestPresent, true); - window.addEventListener('vrdisplaydeactivate', onVRExitPresent, true); - - if (navigator.getVRDisplays) { - //frameData = new VRFrameData(); - navigator.getVRDisplays().then(function (displays) { - if (displays.length > 0) { - log("WebVR supported, VRDisplays found."); - vrDisplay = displays[0]; - log(vrDisplay); - } else { - player.error({code: 'web-vr-no-devices-found', dismiss: false}); - } - }); - } else if (navigator.getVRDevices) { - player.error({code: 'web-vr-out-of-date', dismiss: false}); - } else { - player.error({code: 'web-vr-not-supported', dismiss: false}); - } - - - function isHLS(videoElement) { - var result = false; - var currentType = player.currentType(); - - if (currentType == "application/x-mpegURL") { - result = true; - log("Detected HLS Stream"); - } - - return result; - } - - // Handle window rotate - function onWindowRotate() { - if (window.orientation == -90 || window.orientation == 90) { - // in iOS, width and height never changes regardless orientation - // so when in a horizontal mode, height still greater than width - if (screen.height > screen.width) { - camera.aspect = screen.height / screen.width; - } else { - // in Android, width and height will swap value depending on orientation - camera.aspect = screen.width / screen.height; - } - } else { - camera.aspect = screen.width / screen.height; - } - camera.updateProjectionMatrix(); - } - window.addEventListener('orientationchange', onWindowRotate, false); - - function togglePlay() { - // Doesn't currently cater for case where paused due to buffering - // and/or lack of data - if (player.paused()) { - player.play(); - } else { - player.pause(); - } - } - - (function animate() { - if ( videoEl.readyState === videoEl.HAVE_ENOUGH_DATA ) { - if (videoTexture) { - videoTexture.needsUpdate = true; - } - } - - controls3d.update(); - manager.render( scene, camera ); - - if (vrDisplay) { - vrDisplay.requestAnimationFrame(animate); - - // Grab all gamepads - if(navigator.getGamepads) { - var gamepads = navigator.getGamepads(); - for (var i = 0; i < gamepads.length; ++i) { - var gamepad = gamepads[i]; - // Make sure gamepad is defined - if (gamepad) { - // Only take input if state has changed since we checked last - if (gamepad.timestamp && !(gamepad.timestamp === prevTimestamps[i])) { - for (var j = 0; j < gamepad.buttons.length; ++j) { - if (gamepad.buttons[j].pressed) { - togglePlay(); - prevTimestamps[i] = gamepad.timestamp; - break; - } - } - } - } - } - } - //vrDisplay.getFrameData(frameData); - - //if (vrDisplay.isPresenting) { - // vrDisplay.submitFrame(); - //} - } else { - window.requestAnimationFrame(animate); - } - - camera.getWorldDirection( cameraVector ); - x = getCameraAngle( cameraVector, "x"); - y = getCameraAngle( cameraVector, "y"); - - // log('X camera angle:' + getCameraAngle( cameraVector, "x") ); - // log('Y camera angle:' + getCameraAngle( cameraVector, "y") ); - // log(cameraVector.x, cameraVector.y, cameraVector.z); - }()); - - function getCameraAngle(cameraVector, axis) { - if (axis == "x") { - return 180-radiansToDegrees(Math.atan2(cameraVector.x, cameraVector.z)); - //return cameraVector.x * 114.59155902616465; - } else if (axis == "y") { - return 180-radiansToDegrees(Math.atan2(cameraVector.y, cameraVector.z)); - //return cameraVector.y * 114.59155902616465; - } - } - - function radiansToDegrees(radians) { - return radians * 180 / Math.PI; - } - - // Expose the Three.js perspective camera and rotation values on the player under the 'vr' namespace: - player.vr = { - cameraVector: cameraVector, - threeJs: { - scene: scene, - camera: camera, - renderer: renderer - } - }; - - } - - player.on('loadstart', function () { - initScene(); - }); - - player.ready(function () { - - function updateVrControls(cb) { - - //Add Carboard button - var Button = videojs.getComponent('Button'); - var MyButton = videojs.extend(Button, { - constructor: function() { - Button.apply(this, arguments); - log(this); - /* initialize your button */ - }, - handleClick: function() { - var event = new Event('vrdisplayactivate'); - window.dispatchEvent(event); - /* do something on click */ - }, - buildCSSClass: function() { - return Button.prototype.buildCSSClass.call(this) + 'vjs-button-vr'; - } - }); - videojs.registerComponent('MyButton', MyButton); - cb.addChild('myButton', {}); - - //if ios remove full screen toggle - if(utils.browser.isIOS()) - { - player.controlBar.fullscreenToggle.hide(); - } - } - - if(utils.browser.isMobile()){ - updateVrControls(player.controlBar); - } - }); - - - return { - changeProjection: changeProjection - }; - }; - - // register the plugin with video.js - videojs.registerPlugin('vr', plugin); - -}(window.videojs)); diff --git a/src/plugin.js b/src/plugin.js new file mode 100644 index 00000000..9c865883 --- /dev/null +++ b/src/plugin.js @@ -0,0 +1,536 @@ +import 'webvr-polyfill/build/webvr-polyfill'; + +import videojs from 'video.js'; +import {version as VERSION} from '../package.json'; +import window from 'global/window'; + +import * as THREE from 'three'; + +// previously we used +// * three/examples/js/controls/VRControls.js +// * three/examples/js/effects/VREffects.js +// but since we are using es6 now, there is no good way to make them export to us +// so the code has been copied locally to allow exporting +import VRControls from './VRControls.js'; +import VREffect from './VREffect.js'; + +import WebVrConfig from './webvr.config.js'; +import WebVRManager from 'webvr-boilerplate'; + +window.WebVrConfig = WebVrConfig; +window.WebVRManager = WebVRManager; + +const navigator = window.navigator; +const validProjections = ['360', '360_LR', '360_TB', '360_CUBE', 'NONE', 'AUTO']; +// Default options for the plugin. +const defaults = { + projection: 'AUTO', + debug: false +}; +const errors = { + 'web-vr-no-devices-found': { + headline: 'No 360 devices found', + type: '360_NO_DEVICES_FOUND', + message: 'Your browser supports 360, but no 360 displays found.' + }, + 'web-vr-out-of-date': { + headline: '360 is out of date', + type: '360_OUT_OF_DATE', + message: "Your browser supports 360 but not the latest version. See webvr.info for more info." + }, + 'web-vr-not-supported': { + headline: '360 not supported on this device', + type: '360_NOT_SUPPORTED', + message: "Your browser does not support 360. See webvr.info for assistance." + } +}; + +/** + * Initializes the plugin + */ +const vr = function(options) { + const player = this; + const settings = videojs.mergeOptions(defaults, options || {}); + const videoEl = this.el().getElementsByTagName('video')[0]; + const container = this.el(); + let currentProjection = settings.projection; + let movieMaterial; + let movieGeometry; + let movieScreen; + let controls3d; + let scene; + let cameraVector; + + const log = function(msg) { + if (settings.debug) { + videojs.log(msg); + } + }; + + // custom videojs-errors integration boolean + const videojsErrorsSupport = !!videojs.errors; + + if (videojsErrorsSupport) { + player.errors({errors}); + } + + const triggerError = function(errorObj) { + // if we have videojs-errors use it + if (videojsErrorsSupport) { + player.error(errorObj); + // if we don't have videojs-errors just use a normal player error + } else { + player.error({code: errorObj.code, message: errors[errorObj.code].message}); + } + }; + + if (videoEl === undefined || videoEl === null) { + // Player is not using HTML5 tech, so don't init it. + return; + } + + if (currentProjection === 'NONE' || !currentProjection) { + // Show raw 360 video. + return; + } + + if (validProjections.indexOf(currentProjection) === -1) { + videojs.log.error('videojs-vr: Please use a valid projection option: ' + validProjections.join(', ')); + return; + } + + function isHLS() { + const currentType = player.currentType(); + + // hls video types + const hlsTypes = [ + // Apple santioned + 'application/vnd.apple.mpegurl', + // Very common + 'application/x-mpegurl', + // Included for completeness + 'video/x-mpegurl', + 'video/mpegurl', + 'application/mpegurl' + ]; + + // if the current type has a case insensitivie match from the list above + // this is hls + return hlsTypes.some((type) => (new RegExp(type, 'i')).test(currentType)); + } + + function changeProjection(projection) { + // don't change to an invalid projection + if (validProjections.indexOf(projection) === -1) { + return; + } + + const position = {x: 0, y: 0, z: 0 }; + + if (scene) { + scene.remove(movieScreen); + } + if (projection === 'AUTO') { + if (player.mediainfo && player.mediainfo.projection && player.mediainfo.projection === 'equirectangular') { + changeProjection('360'); + } else { + changeProjection('NONE'); + } + } else if (projection === '360') { + movieGeometry = new THREE.SphereBufferGeometry(256, 32, 32); + + movieScreen = new THREE.Mesh(movieGeometry, movieMaterial); + movieScreen.position.set(position.x, position.y, position.z); + + movieScreen.scale.x = -1; + movieScreen.quaternion.setFromAxisAngle({x: 0, y: 1, z: 0}, -Math.PI / 2); + scene.add(movieScreen); + } else if (projection === '360_LR' || projection === '360_TB') { + let geometry = new THREE.SphereGeometry(256, 32, 32); + + // Left eye view + geometry.scale(-1, 1, 1); + let uvs = geometry.faceVertexUvs[ 0 ]; + + for (let i = 0; i < uvs.length; i++) { + for (let j = 0; j < 3; j++) { + if (projection === '360_LR') { + uvs[ i ][ j ].x *= 0.5; + } else { + uvs[ i ][ j ].y *= 0.5; + } + } + } + + movieGeometry = new THREE.BufferGeometry().fromGeometry(geometry); + movieScreen = new THREE.Mesh(movieGeometry, movieMaterial); + movieScreen.rotation.y = -Math.PI / 2; + // display in left eye only + movieScreen.layers.set(1); + scene.add(movieScreen); + + // Right eye view + geometry = new THREE.SphereGeometry(256, 32, 32); + geometry.scale(-1, 1, 1); + uvs = geometry.faceVertexUvs[ 0 ]; + + for (let i = 0; i < uvs.length; i++) { + for (let j = 0; j < 3; j++) { + if (projection === '360_LR') { + uvs[ i ][ j ].x *= 0.5; + uvs[ i ][ j ].x += 0.5; + } else { + uvs[ i ][ j ].y *= 0.5; + uvs[ i ][ j ].y += 0.5; + } + } + } + + movieGeometry = new THREE.BufferGeometry().fromGeometry(geometry); + movieScreen = new THREE.Mesh(movieGeometry, movieMaterial); + movieScreen.rotation.y = -Math.PI / 2; + // display in right eye only + movieScreen.layers.set(2); + scene.add(movieScreen); + + } else if (projection === '360_CUBE') { + // Currently doesn't work - need to figure out order of cube faces + movieGeometry = new THREE.CubeGeometry(256, 256, 256); + const face1 = [new THREE.Vector2(0, 0.5), new THREE.Vector2(0.333, 0.5), new THREE.Vector2(0.333, 1), new THREE.Vector2(0, 1)]; + const face2 = [new THREE.Vector2(0.333, 0.5), new THREE.Vector2(0.666, 0.5), new THREE.Vector2(0.666, 1), new THREE.Vector2(0.333, 1)]; + const face3 = [new THREE.Vector2(0.666, 0.5), new THREE.Vector2(1, 0.5), new THREE.Vector2(1, 1), new THREE.Vector2(0.666, 1)]; + const face4 = [new THREE.Vector2(0, 0), new THREE.Vector2(0.333, 1), new THREE.Vector2(0.333, 0.5), new THREE.Vector2(0, 0.5)]; + const face5 = [new THREE.Vector2(0.333, 1), new THREE.Vector2(0.666, 1), new THREE.Vector2(0.666, 0.5), new THREE.Vector2(0.333, 0.5)]; + const face6 = [new THREE.Vector2(0.666, 1), new THREE.Vector2(1, 0), new THREE.Vector2(1, 0.5), new THREE.Vector2(0.666, 0.5)]; + + movieGeometry.faceVertexUvs[0] = []; + + movieGeometry.faceVertexUvs[0][0] = [ face1[0], face1[1], face1[3] ]; + movieGeometry.faceVertexUvs[0][1] = [ face1[1], face1[2], face1[3] ]; + + movieGeometry.faceVertexUvs[0][2] = [ face2[0], face2[1], face2[3] ]; + movieGeometry.faceVertexUvs[0][3] = [ face2[1], face2[2], face2[3] ]; + + movieGeometry.faceVertexUvs[0][4] = [ face3[0], face3[1], face3[3] ]; + movieGeometry.faceVertexUvs[0][5] = [ face3[1], face3[2], face3[3] ]; + + movieGeometry.faceVertexUvs[0][6] = [ face4[0], face4[1], face4[3] ]; + movieGeometry.faceVertexUvs[0][7] = [ face4[1], face4[2], face4[3] ]; + + movieGeometry.faceVertexUvs[0][8] = [ face5[0], face5[1], face5[3] ]; + movieGeometry.faceVertexUvs[0][9] = [ face5[1], face5[2], face5[3] ]; + + movieGeometry.faceVertexUvs[0][10] = [ face6[0], face6[1], face6[3] ]; + movieGeometry.faceVertexUvs[0][11] = [ face6[1], face6[2], face6[3] ]; + + movieScreen = new THREE.Mesh(movieGeometry, movieMaterial); + movieScreen.position.set(position.x, position.y, position.z); + + scene.add(movieScreen); + } + + currentProjection = projection; + } + + function initScene() { + // we need this as IE 11 reports that it has a VR display, but isnt compatible with Video as a Texture. for example + if (videojs.browser.IE_VERSION) { + triggerError({code: 'web-vr-not-supported', dismiss: false}); + return; + } + + const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000); + + // Store vector representing the direction in which the camera is looking, in world space. + cameraVector = new THREE.Vector3(); + + if (currentProjection === '360_LR' || currentProjection === '360_TB') { + // Render left eye when not in VR mode + camera.layers.enable(1); + } + + scene = new THREE.Scene(); + controls3d = new VRControls(camera); + + const videoTexture = new THREE.VideoTexture(videoEl); + + videoTexture.generateMipmaps = false; + videoTexture.minFilter = THREE.LinearFilter; + videoTexture.magFilter = THREE.LinearFilter; + + // iOS and macOS HLS fix/hacks + // https://bugs.webkit.org/show_bug.cgi?id=163866#c3 + // https://github.com/mrdoob/three.js/issues/9754 + // On iOS with HLS, color space is wrong and texture is flipped on Y axis + // On macOS, just need to flip texture Y axis + + if (isHLS() && videojs.browser.IS_ANY_SAFARI) { + log('Safari + iOS + HLS = flipY and colorspace hack'); + videoTexture.format = THREE.RGBAFormat; + videoTexture.flipY = false; + } else if (isHLS() && videojs.browser.IS_SAFARI) { + log('Safari + HLS = flipY hack'); + videoTexture.format = THREE.RGBFormat; + videoTexture.flipY = false; + } else { + videoTexture.format = THREE.RGBFormat; + } + + if (videoTexture.format === THREE.RGBAFormat && videoTexture.flipY === false) { + movieMaterial = new THREE.ShaderMaterial({ + uniforms: { + texture: { value: videoTexture } + }, + vertexShader: [ + 'varying vec2 vUV;', + 'void main() {', + ' vUV = vec2( uv.x, 1.0 - uv.y );', + ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', + '}' + ].join('\n'), + fragmentShader: [ + 'uniform sampler2D texture;', + 'varying vec2 vUV;', + 'void main() {', + ' gl_FragColor = texture2D( texture, vUV ).bgra;', + '}' + ].join('\n') + }); + } else if (videoTexture.format === THREE.RGBFormat && videoTexture.flipY === false) { + movieMaterial = new THREE.ShaderMaterial({ + uniforms: { + texture: { value: videoTexture } + }, + vertexShader: [ + 'varying vec2 vUV;', + 'void main() {', + ' vUV = vec2( uv.x, 1.0 - uv.y );', + ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', + '}' + ].join('\n'), + fragmentShader: [ + 'uniform sampler2D texture;', + 'varying vec2 vUV;', + 'void main() {', + ' gl_FragColor = texture2D( texture, vUV );', + '}' + ].join('\n') + }); + } else { + movieMaterial = new THREE.MeshBasicMaterial({ map: videoTexture, overdraw: true, side: THREE.DoubleSide }); + } + + changeProjection(currentProjection); + + if (currentProjection === 'NONE') { + log('Projection is NONE, dont init'); + return; + } + + camera.position.set(0, 0, 0); + + const renderer = new THREE.WebGLRenderer({ + devicePixelRatio: window.devicePixelRatio, + alpha: false, + clearColor: 0xffffff, + antialias: true + }); + + renderer.setSize(window.innerWidth, window.innerHeight); + const effect = new VREffect(renderer); + + effect.setSize(window.innerWidth, window.innerHeight); + + let vrDisplay = null; + + // Previous timestamps for gamepad updates + const prevTimestamps = []; + const manager = new WebVRManager(renderer, effect, {hideButton: true}); + const renderedCanvas = renderer.domElement; + + renderedCanvas.style.width = 'inherit'; + renderedCanvas.style.height = 'inherit'; + + container.insertBefore(renderedCanvas, container.firstChild); + videoEl.style.display = 'none'; + + // Handle window resizes + function onWindowResize() { + const width = player.currentWidth(); + const height = player.currentHeight(); + + effect.setSize(width, height); + camera.aspect = width / height; + camera.updateProjectionMatrix(); + } + + window.addEventListener('resize', onWindowResize, false); + window.addEventListener('vrdisplaypresentchange', onWindowResize, true); + + function onVRRequestPresent() { + manager.enterVRMode_(); + manager.setMode_(3); + } + + function onVRExitPresent() { + if (!vrDisplay.isPresenting) { + return; + } + vrDisplay.exitPresent(); + } + + window.addEventListener('vrdisplayactivate', onVRRequestPresent, true); + window.addEventListener('vrdisplaydeactivate', onVRExitPresent, true); + + if (navigator.getVRDisplays) { + navigator.getVRDisplays().then(function(displays) { + if (displays.length > 0) { + log('WebVR supported, VRDisplays found.'); + vrDisplay = displays[0]; + log(vrDisplay); + } else { + triggerError({code: 'web-vr-no-devices-found', dismiss: false}); + } + }); + } else if (navigator.getVRDevices) { + triggerError({code: 'web-vr-out-of-date', dismiss: false}); + } else { + triggerError({code: 'web-vr-not-supported', dismiss: false}); + } + + // Handle window rotate + function onWindowRotate() { + const screen = window.screen; + + if (window.orientation === -90 || window.orientation === 90) { + // in iOS, width and height never changes regardless orientation + // so when in a horizontal mode, height still greater than width + if (screen.height > screen.width) { + camera.aspect = screen.height / screen.width; + } else { + // in Android, width and height will swap value depending on orientation + camera.aspect = screen.width / screen.height; + } + } else { + camera.aspect = screen.width / screen.height; + } + camera.updateProjectionMatrix(); + } + window.addEventListener('orientationchange', onWindowRotate, false); + + function togglePlay() { + // Doesn't currently cater for case where paused due to buffering + // and/or lack of data + if (player.paused()) { + player.play(); + } else { + player.pause(); + } + } + + (function animate() { + if (videoEl.readyState === videoEl.HAVE_ENOUGH_DATA) { + if (videoTexture) { + videoTexture.needsUpdate = true; + } + } + + controls3d.update(); + manager.render(scene, camera); + + if (vrDisplay) { + vrDisplay.requestAnimationFrame(animate); + + // Grab all gamepads + if (navigator.getGamepads) { + const gamepads = navigator.getGamepads(); + + for (let i = 0; i < gamepads.length; ++i) { + const gamepad = gamepads[i]; + + // Make sure gamepad is defined + if (gamepad) { + // Only take input if state has changed since we checked last + if (gamepad.timestamp && !(gamepad.timestamp === prevTimestamps[i])) { + for (let j = 0; j < gamepad.buttons.length; ++j) { + if (gamepad.buttons[j].pressed) { + togglePlay(); + prevTimestamps[i] = gamepad.timestamp; + break; + } + } + } + } + } + } + } else { + window.requestAnimationFrame(animate); + } + + camera.getWorldDirection(cameraVector); + }()); + + // Expose the Three.js perspective camera and rotation values on the player under the 'vr' namespace: + player.vr = { + cameraVector, + threeJs: { + scene, + camera, + renderer + } + }; + + } + + player.on('loadstart', function() { + initScene(); + }); + + player.ready(function() { + + function updateVrControls(cb) { + + // Add Carboard button + const Button = videojs.getComponent('Button'); + const VRButton = videojs.extend(Button, { + constructor() { + Button.apply(this, arguments); + log(this); + }, + handleClick() { + const event = new window.Event('vrdisplayactivate'); + + window.dispatchEvent(event); + }, + buildCSSClass() { + return Button.prototype.buildCSSClass.call(this) + 'vjs-button-vr'; + } + }); + + videojs.registerComponent('VRButton', VRButton); + cb.addChild('VRButton', {}); + + // if ios remove full screen toggle + if (videojs.browser.IS_IOS) { + player.controlBar.fullscreenToggle.hide(); + } + } + + // mobile devices + if (videojs.browser.IS_ANDROID || videojs.browser.IS_IOS || videojs.browser.IS_IPAD) { + updateVrControls(player.controlBar); + } + }); + + return { + changeProjection + }; +}; + +// register the plugin with video.js +videojs.registerPlugin('vr', vr); + +// Include the version number +vr.VERSION = VERSION; + +export default vr; diff --git a/src/css/videocloud.vr.scss b/src/plugin.scss similarity index 100% rename from src/css/videocloud.vr.scss rename to src/plugin.scss diff --git a/src/js/webvr.config.js b/src/webvr.config.js similarity index 70% rename from src/js/webvr.config.js rename to src/webvr.config.js index 3714e88e..89b648a0 100644 --- a/src/js/webvr.config.js +++ b/src/webvr.config.js @@ -1,34 +1,43 @@ -module.exports = { +export default { // Forces availability of VR mode, even for non-mobile devices. - FORCE_ENABLE_VR: false, // Default: false. + // Default: false. + FORCE_ENABLE_VR: false, // Complementary filter coefficient. 0 for accelerometer, 1 for gyro. - K_FILTER: 0.98, // Default: 0.98. + // Default: 0.98. + K_FILTER: 0.98, // How far into the future to predict during fast motion (in seconds). - PREDICTION_TIME_S: 0.040, // Default: 0.040. + // Default: 0.040. + PREDICTION_TIME_S: 0.040, // Flag to disable touch panner. In case you have your own touch controls. - TOUCH_PANNER_DISABLED: false, // Default: false. + // Default: false. + TOUCH_PANNER_DISABLED: false, // Enable yaw panning only, disabling roll and pitch. This can be useful // for panoramas with nothing interesting above or below. - YAW_ONLY: false, // Default: false. + // Default: false. + YAW_ONLY: false, // To disable keyboard and mouse controls, if you want to use your own // implementation. - MOUSE_KEYBOARD_CONTROLS_DISABLED: false, // Default: false. + // Default: false. + MOUSE_KEYBOARD_CONTROLS_DISABLED: false, // Prevent the polyfill from initializing immediately. Requires the app // to call InitializeWebVRPolyfill() before it can be used. - DEFER_INITIALIZATION: false, // Default: false. + // Default: false. + DEFER_INITIALIZATION: false, // Enable the deprecated version of the API (navigator.getVRDevices). - ENABLE_DEPRECATED_API: false, // Default: false. + // Default: false. + ENABLE_DEPRECATED_API: false, // Scales the recommended buffer size reported by WebVR, which can improve // performance. - BUFFER_SCALE: 0.5, // Default: 1.0. + // Default: 1.0. + BUFFER_SCALE: 0.5, // Allow VRDisplay.submitFrame to change gl bindings, which is more // efficient if the application code will re-bind its resources on the @@ -37,5 +46,6 @@ module.exports = { // Dirty bindings include: gl.FRAMEBUFFER_BINDING, gl.CURRENT_PROGRAM, // gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING, // and gl.TEXTURE_BINDING_2D for texture unit 0. - DIRTY_SUBMIT_FRAME_BINDINGS: false // Default: false. -} + // Default: false. + DIRTY_SUBMIT_FRAME_BINDINGS: false +}; diff --git a/test/index.html b/test/index.html new file mode 100644 index 00000000..8bf0f800 --- /dev/null +++ b/test/index.html @@ -0,0 +1,18 @@ + + + + + videojs-vr Unit Tests + + + + + +
+
+ + + + + + diff --git a/test/karma.conf.js b/test/karma.conf.js new file mode 100644 index 00000000..32b54908 --- /dev/null +++ b/test/karma.conf.js @@ -0,0 +1,42 @@ +module.exports = function(config) { + var detectBrowsers = { + enabled: false, + usePhantomJS: false + }; + + // On Travis CI, we can only run in Firefox and Chrome; so, enforce that. + if (process.env.TRAVIS) { + config.browsers = ['Firefox', 'travisChrome']; + } + + // If no browsers are specified, we enable `karma-detect-browsers` + // this will detect all browsers that are available for testing + if (!config.browsers.length) { + detectBrowsers.enabled = true; + } + + config.set({ + basePath: '..', + frameworks: ['qunit', 'detectBrowsers'], + files: [ + 'node_modules/video.js/dist/video-js.css', + 'dist/videojs-vr.css', + 'node_modules/sinon/pkg/sinon.js', + 'node_modules/video.js/dist/video.js', + 'test/dist/bundle.js' + ], + customLaunchers: { + travisChrome: { + base: 'Chrome', + flags: ['--no-sandbox'] + } + }, + detectBrowsers: detectBrowsers, + reporters: ['dots'], + port: 9876, + colors: true, + autoWatch: false, + singleRun: true, + concurrency: Infinity + }); +}; diff --git a/test/plugin.test.js b/test/plugin.test.js new file mode 100644 index 00000000..3c1a8c73 --- /dev/null +++ b/test/plugin.test.js @@ -0,0 +1,54 @@ +import document from 'global/document'; + +import QUnit from 'qunit'; +import sinon from 'sinon'; +import videojs from 'video.js'; + +import plugin from '../src/plugin'; + +const Player = videojs.getComponent('Player'); + +QUnit.test('the environment is sane', function(assert) { + assert.strictEqual(typeof Array.isArray, 'function', 'es5 exists'); + assert.strictEqual(typeof sinon, 'object', 'sinon exists'); + assert.strictEqual(typeof videojs, 'function', 'videojs exists'); + assert.strictEqual(typeof plugin, 'function', 'plugin is a function'); +}); + +QUnit.module('videojs-vr', { + + beforeEach() { + + // Mock the environment's timers because certain things - particularly + // player readiness - are asynchronous in video.js 5. This MUST come + // before any player is created; otherwise, timers could get created + // with the actual timer methods! + this.clock = sinon.useFakeTimers(); + + this.fixture = document.getElementById('qunit-fixture'); + this.video = document.createElement('video'); + this.fixture.appendChild(this.video); + this.player = videojs(this.video); + }, + + afterEach() { + this.player.dispose(); + this.clock.restore(); + } +}); + +QUnit.test('registers itself with video.js', function(assert) { + assert.expect(1); + + assert.strictEqual( + typeof Player.prototype.vr, + 'function', + 'videojs-vr plugin was registered' + ); + + this.player.vr(); + + // Tick the clock forward enough to trigger the player to be "ready". + this.clock.tick(1); + +});