UNPKG

@nebulae/angular-ble

Version:

A Web Bluetooth (Bluetooth Low Energy) module for angular (v2+)

1,734 lines 180 kB
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('aes-js'), require('rxjs/operators'), require('rxjs'), require('@angular/common')) :
    typeof define === 'function' && define.amd ? define('@nebulae/angular-ble', ['exports', '@angular/core', 'aes-js', 'rxjs/operators', 'rxjs', '@angular/common'], factory) :
    (factory((global.nebulae = global.nebulae || {}, global.nebulae['angular-ble'] = {}),global.ng.core,null,global.rxjs.operators,global.rxjs,global.ng.common));
}(this, (function (exports,i0,aes,operators,rxjs,common) { 'use strict';

    /**
     * @fileoverview added by tsickle
     * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
     */
    var BrowserWebBluetooth = (function () {
        function BrowserWebBluetooth() {
            this._ble = navigator.bluetooth;
            if (!this._ble) {
                console.log('error cargando bluetooth');
                // bluetoothService.setBluetoothAvailable(false);
                // throw new Error('Your browser does not support Smart Bluetooth. See http://caniuse.com/#search=Bluetooth for more details.');
            }
        }
        /**
         * @param {?} options
         * @return {?}
         */
        BrowserWebBluetooth.prototype.requestDevice = /**
         * @param {?} options
         * @return {?}
         */
            function (options) {
                return this._ble.requestDevice(options);
            };
        BrowserWebBluetooth.decorators = [
            { type: i0.Injectable },
        ];
        /** @nocollapse */
        BrowserWebBluetooth.ctorParameters = function () { return []; };
        return BrowserWebBluetooth;
    }());

    /*! *****************************************************************************
    Copyright (c) Microsoft Corporation. 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

    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
    MERCHANTABLITY OR NON-INFRINGEMENT.

    See the Apache Version 2.0 License for specific language governing permissions
    and limitations under the License.
    ***************************************************************************** */
    /* global Reflect, Promise */
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b)
                if (b.hasOwnProperty(p))
                    d[p] = b[p]; };
        return extendStatics(d, b);
    };
    function __extends(d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    }
    function __values(o) {
        var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0;
        if (m)
            return m.call(o);
        return {
            next: function () {
                if (o && i >= o.length)
                    o = void 0;
                return { value: o && o[i++], done: !o };
            }
        };
    }
    function __read(o, n) {
        var m = typeof Symbol === "function" && o[Symbol.iterator];
        if (!m)
            return o;
        var i = m.call(o), r, ar = [], e;
        try {
            while ((n === void 0 || n-- > 0) && !(r = i.next()).done)
                ar.push(r.value);
        }
        catch (error) {
            e = { error: error };
        }
        finally {
            try {
                if (r && !r.done && (m = i["return"]))
                    m.call(i);
            }
            finally {
                if (e)
                    throw e.error;
            }
        }
        return ar;
    }

    /**
     * @fileoverview added by tsickle
     * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
     */
    /** @type {?} */
    var GattServices = {
        GENERIC_ACCESS: {
            SERVICE: 'generic_access',
            DEVICE_NAME: 'device_name',
            APPEARANCE: 'appearance',
            PRIVACY_FLAG: 'privacy_flag',
            RECONNECTION_ADDRESS: 'reconnection_address',
            PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS: 'peripheral_preferred_connection_parameters'
        },
        BATTERY: {
            SERVICE: 'battery_service',
            BATTERY_LEVEL: 'battery_level'
        },
        DEVICE_INFORMATION: {
            SERVICE: 'device_information',
            MANUFACTURER_NAME: 'manufacturer_name_string',
            MODEL_NUMBER: 'model_number_string',
            SERIAL_NUMBER: 'serial_number_string',
            HARDWARE_REVISION: 'hardware_revision_string',
            FIRMWARE_REVISION: 'firmware_revision_string',
            SOFTWARE_REVISION: 'software_revision_string',
            SYSTEM_ID: 'system_id',
            PNP_ID: 'pnp_id'
        }
    };
    /** @type {?} */
    var GATT_SERVICES = Object.keys(GattServices).map(function (key) { return GattServices[key].SERVICE; });

    /**
     * @fileoverview added by tsickle
     * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
     */
    var CypherAesService = (function () {
        function CypherAesService() {
            this.masterKey = [];
            this.initialVector = [];
            this.encryptMethod = 'CBC';
            this.isStaticInitialVector = true;
            this.isConfigExecuted = false;
        }
        /**
         * Initial config used to initalice all required params
         * @param masterKey key used to encrypt and decrypt
         * @param initialVector vector used to encrypt abd decrypt except when ECB encrypt method is used
         * @param encryptMethod type of encrypt method is used, the possible options are: CBC, CTR, CFB, OFB, ECB
         * @param additionalEncryptMethodParams configuration params used by the selected encrypt method.
         * Note: if the method CTR or CFB is used this param is required otherwise is an optinal param.
         * By CTR require the param counter and by CFB require the param segmentSize
         * @param isStaticInitialVector defines if the initial vector is changed or not when the data are encrypted or not
         */
        /**
         * Initial config used to initalice all required params
         * @param {?} masterKey key used to encrypt and decrypt
         * @param {?=} initialVector vector used to encrypt abd decrypt except when ECB encrypt method is used
         * @param {?=} encryptMethod type of encrypt method is used, the possible options are: CBC, CTR, CFB, OFB, ECB
         * @param {?=} additionalEncryptMethodParams configuration params used by the selected encrypt method.
         * Note: if the method CTR or CFB is used this param is required otherwise is an optinal param.
         * By CTR require the param counter and by CFB require the param segmentSize
         * @param {?=} isStaticInitialVector defines if the initial vector is changed or not when the data are encrypted or not
         * @return {?}
         */
        CypherAesService.prototype.config = /**
         * Initial config used to initalice all required params
         * @param {?} masterKey key used to encrypt and decrypt
         * @param {?=} initialVector vector used to encrypt abd decrypt except when ECB encrypt method is used
         * @param {?=} encryptMethod type of encrypt method is used, the possible options are: CBC, CTR, CFB, OFB, ECB
         * @param {?=} additionalEncryptMethodParams configuration params used by the selected encrypt method.
         * Note: if the method CTR or CFB is used this param is required otherwise is an optinal param.
         * By CTR require the param counter and by CFB require the param segmentSize
         * @param {?=} isStaticInitialVector defines if the initial vector is changed or not when the data are encrypted or not
         * @return {?}
         */
            function (masterKey, initialVector, encryptMethod, additionalEncryptMethodParams, isStaticInitialVector) {
                if (initialVector === void 0) {
                    initialVector = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
                }
                if (encryptMethod === void 0) {
                    encryptMethod = 'CBC';
                }
                if (additionalEncryptMethodParams === void 0) {
                    additionalEncryptMethodParams = {};
                }
                if (isStaticInitialVector === void 0) {
                    isStaticInitialVector = true;
                }
                this.isConfigExecuted = true;
                this.masterKey = masterKey;
                this.initialVector = initialVector;
                this.encryptMethod = encryptMethod;
                this.isStaticInitialVector = isStaticInitialVector;
                this.additionalEncryptMethodParams = additionalEncryptMethodParams;
                if (!isStaticInitialVector) {
                    this.enctrypMethodInstance = this.generateEncryptMethodInstance();
                }
            };
        /**
         * Encrypt the data using the encrypt method previously configured
         * @param dataArrayBuffer data to encrypt
         */
        /**
         * Encrypt the data using the encrypt method previously configured
         * @param {?} dataArrayBuffer data to encrypt
         * @return {?}
         */
        CypherAesService.prototype.encrypt = /**
         * Encrypt the data using the encrypt method previously configured
         * @param {?} dataArrayBuffer data to encrypt
         * @return {?}
         */
            function (dataArrayBuffer) {
                if (!this.isConfigExecuted) {
                    throw new Error('Must configurate cypher-aes before call this method, use the method config()');
                }
                if (this.encryptMethod === 'CBC' || this.encryptMethod === 'ECB') {
                    dataArrayBuffer = this.addPadding(dataArrayBuffer);
                }
                return this.isStaticInitialVector
                    ? this.generateEncryptMethodInstance().encrypt(dataArrayBuffer)
                    : this.enctrypMethodInstance.encrypt(dataArrayBuffer);
            };
        /**
         * Decrypt the data using the encrypt method previously configured
         * @param dataArrayBuffer data to decrypt
         */
        /**
         * Decrypt the data using the encrypt method previously configured
         * @param {?} dataArrayBuffer data to decrypt
         * @return {?}
         */
        CypherAesService.prototype.decrypt = /**
         * Decrypt the data using the encrypt method previously configured
         * @param {?} dataArrayBuffer data to decrypt
         * @return {?}
         */
            function (dataArrayBuffer) {
                if (!this.isConfigExecuted) {
                    throw new Error('Must configurate cypher-aes before call this method, use the method config()');
                }
                return this.isStaticInitialVector
                    ? this.generateEncryptMethodInstance().decrypt(dataArrayBuffer)
                    : this.enctrypMethodInstance.decrypt(dataArrayBuffer);
            };
        /**
         * Change the current initalVector
         * @param initialVector new initalVector
         */
        /**
         * Change the current initalVector
         * @param {?} initialVector new initalVector
         * @return {?}
         */
        CypherAesService.prototype.changeInitialVector = /**
         * Change the current initalVector
         * @param {?} initialVector new initalVector
         * @return {?}
         */
            function (initialVector) {
                if (!this.isStaticInitialVector) {
                    this.enctrypMethodInstance = this.generateEncryptMethodInstance();
                }
                this.initialVector = initialVector;
            };
        /**
         * Change the current encyptMethod
         * @param encryptMethod new encryptMethod
         */
        /**
         * Change the current encyptMethod
         * @param {?} encryptMethod new encryptMethod
         * @return {?}
         */
        CypherAesService.prototype.changeEncryptMethod = /**
         * Change the current encyptMethod
         * @param {?} encryptMethod new encryptMethod
         * @return {?}
         */
            function (encryptMethod) {
                if (!this.isStaticInitialVector) {
                    this.enctrypMethodInstance = this.generateEncryptMethodInstance();
                }
                this.encryptMethod = encryptMethod;
            };
        /**
         * Change the current isStaticInitialVector
         * @param isStaticInitialVector new isStaticInitalVector
         */
        /**
         * Change the current isStaticInitialVector
         * @param {?} isStaticInitialVector new isStaticInitalVector
         * @return {?}
         */
        CypherAesService.prototype.changeStaticInitialVector = /**
         * Change the current isStaticInitialVector
         * @param {?} isStaticInitialVector new isStaticInitalVector
         * @return {?}
         */
            function (isStaticInitialVector) {
                if (!isStaticInitialVector) {
                    this.enctrypMethodInstance = this.generateEncryptMethodInstance();
                }
                this.isStaticInitialVector = isStaticInitialVector;
            };
        /**
         * Change the current masterKey
         * @param masterKey new masterKey
         */
        /**
         * Change the current masterKey
         * @param {?} masterKey new masterKey
         * @return {?}
         */
        CypherAesService.prototype.changeMasterKey = /**
         * Change the current masterKey
         * @param {?} masterKey new masterKey
         * @return {?}
         */
            function (masterKey) {
                if (!this.isStaticInitialVector) {
                    this.enctrypMethodInstance = this.generateEncryptMethodInstance();
                }
                this.masterKey = masterKey;
            };
        /**
         * Add padding to the list
         * @param {?} arrayBuffer
         * @return {?}
         */
        CypherAesService.prototype.addPadding = /**
         * Add padding to the list
         * @param {?} arrayBuffer
         * @return {?}
         */
            function (arrayBuffer) {
                /** @type {?} */
                var paddingLength = Math.ceil(Array.from(arrayBuffer).length / 16) * 16;
                /** @type {?} */
                var paddingList = new Array(paddingLength - Array.from(arrayBuffer).length).fill(0);
                return new Uint8Array(Array.from(arrayBuffer).concat(paddingList));
            };
        /**
         * generate a instance of the encrypt method using the current method configured
         * @return {?}
         */
        CypherAesService.prototype.generateEncryptMethodInstance = /**
         * generate a instance of the encrypt method using the current method configured
         * @return {?}
         */
            function () {
                /** @type {?} */
                var enctrypMethodInstance;
                switch (this.encryptMethod) {
                    case 'CBC':
                        enctrypMethodInstance = new aes.ModeOfOperation.cbc(this.masterKey, this.initialVector);
                        break;
                    case 'CTR':
                        if (!this.additionalEncryptMethodParams.counter) {
                            throw new Error('additionalEncryptMethodParams.counter is required to use encrypt method CTR');
                        }
                        enctrypMethodInstance = new aes.ModeOfOperation.ctr(this.masterKey, this.initialVector, new aes.Counter(this.additionalEncryptMethodParams.counter));
                        break;
                    case 'CFB':
                        if (!this.additionalEncryptMethodParams.segmentSize) {
                            throw new Error('additionalEncryptMethodParams.segmentSize is required to use encrypt method CFB');
                        }
                        enctrypMethodInstance = new aes.ModeOfOperation.cfb(this.masterKey, this.initialVector, this.additionalEncryptMethodParams.segmentSize);
                        break;
                    case 'OFB':
                        enctrypMethodInstance = new aes.ModeOfOperation.ofb(this.masterKey, this.initialVector);
                        break;
                    case 'ECB':
                        enctrypMethodInstance = new aes.ModeOfOperation.ecb(this.masterKey);
                        break;
                }
                return enctrypMethodInstance;
            };
        // #region UTILS
        /**
         * Convert the text to bytes
         * @param text Text to convert
         */
        /**
         * Convert the text to bytes
         * @param {?} text Text to convert
         * @return {?}
         */
        CypherAesService.prototype.textToBytes = /**
         * Convert the text to bytes
         * @param {?} text Text to convert
         * @return {?}
         */
            function (text) {
                return aes.utils.utf8.toBytes(text);
            };
        /**
         * Convert the bytes to text
         * @param bytes Bytes to convert
         */
        /**
         * Convert the bytes to text
         * @param {?} bytes Bytes to convert
         * @return {?}
         */
        CypherAesService.prototype.bytesToText = /**
         * Convert the bytes to text
         * @param {?} bytes Bytes to convert
         * @return {?}
         */
            function (bytes) {
                return aes.utils.utf8.fromBytes(bytes);
            };
        /**
         * Convert the bytes to hex
         * @param bytes bytes to convert
         */
        /**
         * Convert the bytes to hex
         * @param {?} bytes bytes to convert
         * @return {?}
         */
        CypherAesService.prototype.bytesTohex = /**
         * Convert the bytes to hex
         * @param {?} bytes bytes to convert
         * @return {?}
         */
            function (bytes) {
                return aes.utils.hex.fromBytes(bytes);
            };
        /**
         * Convert the hex to bytes
         * @param hex Hex to convert
         */
        /**
         * Convert the hex to bytes
         * @param {?} hex Hex to convert
         * @return {?}
         */
        CypherAesService.prototype.hexToBytes = /**
         * Convert the hex to bytes
         * @param {?} hex Hex to convert
         * @return {?}
         */
            function (hex) {
                return aes.utils.hex.toBytes(hex);
            };
        // #endregion
        /**
         * @param {?} key
         * @return {?}
         */
        CypherAesService.prototype.generateSubkeys = /**
         * @param {?} key
         * @return {?}
         */
            function (key) {
                /** @type {?} */
                var const_Zero = new Uint8Array(16);
                /** @type {?} */
                var const_Rb = new Buffer('00000000000000000000000000000087', 'hex');
                /** @type {?} */
                var enctrypMethodInstance = new aes.ModeOfOperation.cbc(key, new Uint8Array(16));
                /** @type {?} */
                var lEncrypted = enctrypMethodInstance.encrypt(const_Zero);
                /** @type {?} */
                var l = new Buffer(this.bytesTohex(lEncrypted), 'hex');
                /** @type {?} */
                var subkey1 = this.bitShiftLeft(l);
                // tslint:disable-next-line:no-bitwise
                if (l[0] & 0x80) {
                    subkey1 = this.xor(subkey1, const_Rb);
                }
                /** @type {?} */
                var subkey2 = this.bitShiftLeft(subkey1);
                // tslint:disable-next-line:no-bitwise
                if (subkey1[0] & 0x80) {
                    subkey2 = this.xor(subkey2, const_Rb);
                }
                return { subkey1: subkey1, subkey2: subkey2 };
            };
        /**
         * @param {?} key
         * @param {?} message
         * @return {?}
         */
        CypherAesService.prototype.aesCmac = /**
         * @param {?} key
         * @param {?} message
         * @return {?}
         */
            function (key, message) {
                console.log('INICIA CIFRADO!!!!!!!!!!!!!!!!');
                /** @type {?} */
                var subkeys = this.generateSubkeys(key);
                /** @type {?} */
                var blockCount = Math.ceil(message.length / 16);
                /** @type {?} */
                var lastBlockCompleteFlag;
                /** @type {?} */
                var lastBlock;
                /** @type {?} */
                var lastBlockIndex;
                if (blockCount === 0) {
                    blockCount = 1;
                    lastBlockCompleteFlag = false;
                }
                else {
                    lastBlockCompleteFlag = message.length % 16 === 0;
                }
                lastBlockIndex = blockCount - 1;
                if (lastBlockCompleteFlag) {
                    lastBlock = this.xor(this.getMessageBlock(message, lastBlockIndex), subkeys.subkey1);
                }
                else {
                    lastBlock = this.xor(this.getPaddedMessageBlock(message, lastBlockIndex), subkeys.subkey2);
                }
                /** @type {?} */
                var x = new Buffer('00000000000000000000000000000000', 'hex');
                /** @type {?} */
                var y;
                /** @type {?} */
                var enctrypMethodInstance;
                for (var index = 0; index < lastBlockIndex; index++) {
                    enctrypMethodInstance = new aes.ModeOfOperation.cbc(key, new Uint8Array(16));
                    y = this.xor(x, this.getMessageBlock(message, index));
                    /** @type {?} */
                    var xEncrypted = enctrypMethodInstance.encrypt(y);
                    console.log('X normal ===============> ', this.bytesTohex(y));
                    console.log('X encrypted ==============> ', this.bytesTohex(xEncrypted));
                    x = new Buffer(this.bytesTohex(xEncrypted), 'hex');
                }
                y = this.xor(lastBlock, x);
                enctrypMethodInstance = new aes.ModeOfOperation.cbc(key, new Uint8Array(16));
                /** @type {?} */
                var yEncrypted = enctrypMethodInstance.encrypt(y);
                console.log('Y normal ==============> ', this.bytesTohex(y));
                console.log('Y encrypted ==============> ', this.bytesTohex(yEncrypted));
                return yEncrypted;
            };
        /**
         * @param {?} message
         * @param {?} blockIndex
         * @return {?}
         */
        CypherAesService.prototype.getMessageBlock = /**
         * @param {?} message
         * @param {?} blockIndex
         * @return {?}
         */
            function (message, blockIndex) {
                /** @type {?} */
                var block = new Buffer(16);
                /** @type {?} */
                var start = blockIndex * 16;
                /** @type {?} */
                var end = start + 16;
                /** @type {?} */
                var blockI = 0;
                for (var i = start; i < end; i++) {
                    block[blockI] = message[i];
                    blockI++;
                }
                return block;
            };
        /**
         * @param {?} message
         * @param {?} blockIndex
         * @return {?}
         */
        CypherAesService.prototype.getPaddedMessageBlock = /**
         * @param {?} message
         * @param {?} blockIndex
         * @return {?}
         */
            function (message, blockIndex) {
                /** @type {?} */
                var block = new Buffer(16);
                /** @type {?} */
                var start = blockIndex * 16;
                /** @type {?} */
                var end = message.length;
                block.fill(0);
                /** @type {?} */
                var blockI = 0;
                for (var i = start; i < end; i++) {
                    block[blockI] = message[i];
                    blockI++;
                }
                block[end - start] = 0x80;
                return block;
            };
        /**
         * @param {?} buffer
         * @return {?}
         */
        CypherAesService.prototype.bitShiftLeft = /**
         * @param {?} buffer
         * @return {?}
         */
            function (buffer) {
                /** @type {?} */
                var shifted = new Buffer(buffer.length);
                /** @type {?} */
                var last = buffer.length - 1;
                for (var index = 0; index < last; index++) {
                    // tslint:disable-next-line:no-bitwise
                    shifted[index] = buffer[index] << 1;
                    // tslint:disable-next-line:no-bitwise
                    if (buffer[index + 1] & 0x80) {
                        shifted[index] += 0x01;
                    }
                }
                // tslint:disable-next-line:no-bitwise
                shifted[last] = buffer[last] << 1;
                return shifted;
            };
        /**
         * @param {?} bufferA
         * @param {?} bufferB
         * @return {?}
         */
        CypherAesService.prototype.xor = /**
         * @param {?} bufferA
         * @param {?} bufferB
         * @return {?}
         */
            function (bufferA, bufferB) {
                /** @type {?} */
                var length = Math.min(bufferA.length, bufferB.length);
                /** @type {?} */
                var output = new Buffer(length);
                for (var index = 0; index < length; index++) {
                    // tslint:disable-next-line:no-bitwise
                    output[index] = bufferA[index] ^ bufferB[index];
                }
                return output;
            };
        CypherAesService.decorators = [
            { type: i0.Injectable, args: [{
                        providedIn: 'root'
                    },] },
        ];
        /** @nocollapse */
        CypherAesService.ctorParameters = function () { return []; };
        /** @nocollapse */ CypherAesService.ngInjectableDef = i0.defineInjectable({ factory: function CypherAesService_Factory() { return new CypherAesService(); }, token: CypherAesService, providedIn: "root" });
        return CypherAesService;
    }());

    /**
     * @fileoverview added by tsickle
     * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
     */
    var ConsoleLoggerService = (function () {
        function ConsoleLoggerService() {
        }
        /**
         * @param {...?} args
         * @return {?}
         */
        ConsoleLoggerService.prototype.log = /**
         * @param {...?} args
         * @return {?}
         */
            function () {
                var args = [];
                for (var _i = 0; _i < arguments.length; _i++) {
                    args[_i] = arguments[_i];
                }
                console.log.apply(console, args);
            };
        /**
         * @param {...?} args
         * @return {?}
         */
        ConsoleLoggerService.prototype.error = /**
         * @param {...?} args
         * @return {?}
         */
            function () {
                var args = [];
                for (var _i = 0; _i < arguments.length; _i++) {
                    args[_i] = arguments[_i];
                }
                console.error.apply(console, args);
            };
        /**
         * @param {...?} args
         * @return {?}
         */
        ConsoleLoggerService.prototype.warn = /**
         * @param {...?} args
         * @return {?}
         */
            function () {
                var args = [];
                for (var _i = 0; _i < arguments.length; _i++) {
                    args[_i] = arguments[_i];
                }
                console.warn.apply(console, args);
            };
        ConsoleLoggerService.decorators = [
            { type: i0.Injectable, args: [{
                        providedIn: 'root'
                    },] },
        ];
        /** @nocollapse */ ConsoleLoggerService.ngInjectableDef = i0.defineInjectable({ factory: function ConsoleLoggerService_Factory() { return new ConsoleLoggerService(); }, token: ConsoleLoggerService, providedIn: "root" });
        return ConsoleLoggerService;
    }());
    var NoLoggerService = (function () {
        function NoLoggerService() {
        }
        /**
         * @param {...?} args
         * @return {?}
         */
        NoLoggerService.prototype.log = /**
         * @param {...?} args
         * @return {?}
         */
            function () {
                var args = [];
                for (var _i = 0; _i < arguments.length; _i++) {
                    args[_i] = arguments[_i];
                }
            };
        /**
         * @param {...?} args
         * @return {?}
         */
        NoLoggerService.prototype.error = /**
         * @param {...?} args
         * @return {?}
         */
            function () {
                var args = [];
                for (var _i = 0; _i < arguments.length; _i++) {
                    args[_i] = arguments[_i];
                }
            };
        /**
         * @param {...?} args
         * @return {?}
         */
        NoLoggerService.prototype.warn = /**
         * @param {...?} args
         * @return {?}
         */
            function () {
                var args = [];
                for (var _i = 0; _i < arguments.length; _i++) {
                    args[_i] = arguments[_i];
                }
            };
        NoLoggerService.decorators = [
            { type: i0.Injectable, args: [{
                        providedIn: 'root'
                    },] },
        ];
        /** @nocollapse */ NoLoggerService.ngInjectableDef = i0.defineInjectable({ factory: function NoLoggerService_Factory() { return new NoLoggerService(); }, token: NoLoggerService, providedIn: "root" });
        return NoLoggerService;
    }());

    /**
     * @fileoverview added by tsickle
     * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
     */
    var BluetoothService = (function (_super) {
        __extends(BluetoothService, _super);
        function BluetoothService(_webBle, cypherAesService, _console) {
            var _this = _super.call(this) || this;
            _this._webBle = _webBle;
            _this.cypherAesService = cypherAesService;
            _this._console = _console;
            _this.serviceCharacteristicVsSubscriptionList = {};
            _this.notifierSubject = new rxjs.Subject();
            _this.notifierStartedSubject = new rxjs.Subject();
            _this.bluetoothAvailable = false;
            _this._device$ = new i0.EventEmitter();
            if (_webBle._ble) {
                _this.bluetoothAvailable = true;
            }
            return _this;
        }
        /**
         * @return {?}
         */
        BluetoothService.prototype.isBluetoothAvailable = /**
         * @return {?}
         */
            function () {
                return this.bluetoothAvailable;
            };
        /**
         * get the current device, if the device return null is because the connection has lost
         * @returns the current connceted device
         */
        /**
         * get the current device, if the device return null is because the connection has lost
         * @return {?} the current connceted device
         */
        BluetoothService.prototype.getDevice$ = /**
         * get the current device, if the device return null is because the connection has lost
         * @return {?} the current connceted device
         */
            function () {
                return this._device$;
            };
        /**
         * @return {?}
         */
        BluetoothService.prototype.getNotifierStartedSubject$ = /**
         * @return {?}
         */
            function () {
                return this.notifierStartedSubject;
            };
        /**
         * start a stream by notifiers characteristics
         * @param service The service to which the characteristic belongs
         * @param characteristic The characteristic whose value you want to listen
         * @param options object that contains the
         * startByte:number (required), stopByte:number (required),
         * lengthPosition: { start: number, end: number, lengthPadding: number } (required)
         * @returns A DataView than contains the characteristic value
         */
        /**
         * start a stream by notifiers characteristics
         * @param {?} service The service to which the characteristic belongs
         * @param {?} characteristic The characteristic whose value you want to listen
         * @param {?} options object that contains the
         * startByte:number (required), stopByte:number (required),
         * lengthPosition: { start: number, end: number, lengthPadding: number } (required)
         * @return {?} A DataView than contains the characteristic value
         */
        BluetoothService.prototype.startNotifierListener$ = /**
         * start a stream by notifiers characteristics
         * @param {?} service The service to which the characteristic belongs
         * @param {?} characteristic The characteristic whose value you want to listen
         * @param {?} options object that contains the
         * startByte:number (required), stopByte:number (required),
         * lengthPosition: { start: number, end: number, lengthPadding: number } (required)
         * @return {?} A DataView than contains the characteristic value
         */
            function (service, characteristic, options) {
                var _this = this;
                return rxjs.defer(function () {
                    rxjs.of(service)
                        .pipe(operators.tap(function () { return _this._console.log('Inicia el notifier con la instancia: ', _this.serviceCharacteristicVsSubscriptionList); }), operators.filter(function (_) {
                        return Object.keys(_this.serviceCharacteristicVsSubscriptionList).indexOf(service + "-" + characteristic) === -1;
                    }))
                        .subscribe(function () {
                        _this.serviceCharacteristicVsSubscriptionList[service + "-" + characteristic] =
                            _this.buildNotifierListener$(service, characteristic, options).subscribe(function (message) {
                                _this.notifierSubject.next(message);
                            });
                    }, function (err) {
                        _this._console.log('[BLE::Info] Error in notifier: ', err);
                    }, function () { });
                    return rxjs.of("notifier as subscribed: service= " + service + ", characteristic= " + characteristic);
                });
            };
        /**
         * @param {?} service
         * @param {?} characteristic
         * @return {?}
         */
        BluetoothService.prototype.stopNotifierListener$ = /**
         * @param {?} service
         * @param {?} characteristic
         * @return {?}
         */
            function (service, characteristic) {
                var _this = this;
                return rxjs.defer(function () {
                    // this.serviceCharacteristicVsSubscriptionList[`${service}-${characteristic}`].unsubscribe();
                    delete _this.serviceCharacteristicVsSubscriptionList[service + "-" + characteristic];
                    return rxjs.of("the notifier of the characteristic " + characteristic + " as been stopped");
                });
            };
        /**
         * @param {?} service
         * @param {?} characteristic
         * @param {?} options
         * @return {?}
         */
        BluetoothService.prototype.buildNotifierListener$ = /**
         * @param {?} service
         * @param {?} characteristic
         * @param {?} options
         * @return {?}
         */
            function (service, characteristic, options) {
                var _this = this;
                return this.getPrimaryService$(service).pipe(operators.tap(function (serviceInstance) { return _this._console.log("toma exitosamente el servicio ================> " + serviceInstance); }), operators.mergeMap(function (primaryService) {
                    return _this.getCharacteristic$(primaryService, characteristic)
                        .pipe(operators.tap(function (char) { return _this._console.log("toma exitosamente la caracteristica ================> " + char); }));
                }), operators.mergeMap(function (char) {
                    return rxjs.defer(function () {
                        // enable the characteristic notifier
                        return char.startNotifications();
                    }).pipe(operators.retryWhen(function (error) {
                        return error.pipe(operators.tap(function () { return _this._console.log('ERROR EN EL startNotifications ================> '); }), operators.delay(1000), operators.take(5));
                    }), operators.tap(function () {
                        _this.notifierStartedSubject.next(true);
                        _this._console.log("incia las notifiaciones de la caracteristica 2 ================> " + characteristic);
                    }), operators.mergeMap(function (_) {
                        // start the lister from the even characteristicvaluechanged to get all changes on the specific
                        // characteristic
                        return rxjs.fromEvent(char, 'characteristicvaluechanged').pipe(operators.takeUntil(rxjs.fromEvent(char, 'gattserverdisconnected')), operators.map(function (event) {
                            // get a event from the characteristic and map that
                            return {
                                startByteMatches: false,
                                stopByteMatches: false,
                                lengthMatches: false,
                                messageLength: 0,
                                timestamp: Date.now(),
                                data: Array.from(new Uint8Array(((((event.target))
                                    .value)).buffer))
                            };
                        }), operators.scan(function (acc, value) {
                            acc.timestamp = acc.timestamp === 0 ? value.timestamp : acc.timestamp;
                            // if the current accumulator value is a valid message, then is restarted to get the next
                            // message
                            if ((acc.lengthMatches &&
                                acc.startByteMatches &&
                                acc.stopByteMatches)
                                || acc.timestamp + 1000 < Date.now()) {
                                acc = {
                                    startByteMatches: false,
                                    stopByteMatches: false,
                                    lengthMatches: false,
                                    messageLength: 0,
                                    timestamp: 0,
                                    data: []
                                };
                            }
                            // validate the start byte
                            if (!acc.startByteMatches &&
                                value.data[0] === options.startByte) {
                                // get the message length using the start and end position
                                acc.messageLength =
                                    new DataView(new Uint8Array(value.data.slice(options.lengthPosition.start, options.lengthPosition.end)).buffer).getInt16(0, false) +
                                        (options.lengthPosition.lengthPadding
                                            ? options.lengthPosition.lengthPadding
                                            : 0);
                                // valid that the initial byte was found
                                acc.startByteMatches = true;
                            }
                            if (!acc.stopByteMatches &&
                                value.data[value.data.length - 1] === options.stopByte) {
                                // valid that the end byte was found
                                acc.stopByteMatches = true;
                            }
                            if (acc.startByteMatches) {
                                // merge the new data bytes to the old bytes
                                acc.data = acc.data.concat(value.data);
                            }
                            acc.lengthMatches =
                                acc.startByteMatches &&
                                    acc.stopByteMatches &&
                                    acc.messageLength === acc.data.length;
                            return acc;
                        }, {
                            startByteMatches: false,
                            stopByteMatches: false,
                            lengthMatches: false,
                            messageLength: 0,
                            timestamp: 0,
                            data: []
                        }), 
                        // only publish the complete and valid message
                        operators.filter(function (data) {
                            return data.lengthMatches &&
                                data.startByteMatches &&
                                data.stopByteMatches;
                        }), 
                        // remove all custom data and publish the message data
                        operators.map(function (result) { return result.data; }));
                    }));
                }));
            };
        /**
         * Send a request to the device and wait a unique response
         * @param message Message to send
         * @param service The service to which the characteristic belongs
         * @param characteristic The characteristic whose value you want to send the message
         * @param responseType filter to use to identify the response, Sample: [{position: 3, byteToMatch: 0x83},
         *  {position: 13, byteToMatch: 0x45}]
         * @param cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
         */
        /**
         * Send a request to the device and wait a unique response
         * @param {?} message Message to send
         * @param {?} service The service to which the characteristic belongs
         * @param {?} characteristic The characteristic whose value you want to send the message
         * @param {?} responseType filter to use to identify the response, Sample: [{position: 3, byteToMatch: 0x83},
         *  {position: 13, byteToMatch: 0x45}]
         * @param {?=} cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
         * @return {?}
         */
        BluetoothService.prototype.sendAndWaitResponse$ = /**
         * Send a request to the device and wait a unique response
         * @param {?} message Message to send
         * @param {?} service The service to which the characteristic belongs
         * @param {?} characteristic The characteristic whose value you want to send the message
         * @param {?} responseType filter to use to identify the response, Sample: [{position: 3, byteToMatch: 0x83},
         *  {position: 13, byteToMatch: 0x45}]
         * @param {?=} cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
         * @return {?}
         */
            function (message, service, characteristic, responseType, cypherMasterKey) {
                this._console.log('[BLE::Info] Send message to device: ', this.cypherAesService.bytesTohex(message));
                return rxjs.forkJoin(this.subscribeToNotifierListener(responseType, cypherMasterKey).pipe(operators.take(1)), this.sendToNotifier$(message, service, characteristic)).pipe(operators.map(function (_a) {
                    var _b = __read(_a, 2), messageResp = _b[0], _ = _b[1];
                    return messageResp;
                }), operators.timeout(3000));
            };
        /**
         * Subscribe to the notifiers filtering by byte checking
         * @param filterOptions must specific the position and the byte to match Sample:
         * [{position: 3, byteToMatch: 0x83}, {position: 13, byteToMatch: 0x45}]
         * @param cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
         */
        /**
         * Subscribe to the notifiers filtering by byte checking
         * @param {?} filterOptions must specific the position and the byte to match Sample:
         * [{position: 3, byteToMatch: 0x83}, {position: 13, byteToMatch: 0x45}]
         * @param {?=} cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
         * @return {?}
         */
        BluetoothService.prototype.subscribeToNotifierListener = /**
         * Subscribe to the notifiers filtering by byte checking
         * @param {?} filterOptions must specific the position and the byte to match Sample:
         * [{position: 3, byteToMatch: 0x83}, {position: 13, byteToMatch: 0x45}]
         * @param {?=} cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
         * @return {?}
         */
            function (filterOptions, cypherMasterKey) {
                var _this = this;
                return this.notifierSubject.pipe(operators.map(function (messageUnformated) {
                    /** @type {?} */
                    var messageFormmated = (messageUnformated);
                    // validate if the message is cyphered
                    if (cypherMasterKey) {
                        /** @type {?} */
                        var datablockLength = new DataView(new Uint8Array(messageFormmated.slice(1, 3)).buffer).getInt16(0, false);
                        _this.cypherAesService.config(cypherMasterKey);
                        /** @type {?} */
                        var datablock = Array.from(_this.cypherAesService.decrypt(((messageUnformated)).slice(3, datablockLength + 3)));
                        // merge the datablock and the message
                        messageFormmated = ((messageUnformated))
                            .slice(0, 3)
                            .concat(datablock)
                            .concat(((messageUnformated)).slice(-2));
                    }
                    _this._console.log('[BLE::Info] Notification reived from device: ', _this.cypherAesService.bytesTohex(messageFormmated));
                    return messageFormmated;
                }), 
                // filter the message using the filter options
                operators.filter(function (message) {
                    /** @type {?} */
                    var availableMessage = false;
                    try {
                        for (var filterOptions_1 = __values(filterOptions), filterOptions_1_1 = filterOptions_1.next(); !filterOptions_1_1.done; filterOptions_1_1 = filterOptions_1.next()) {
                            var option = filterOptions_1_1.value;
                            if (message[option.position] === option.byteToMatch) {
                                availableMessage = true;
                            }
                            else {
                                availableMessage = false;
                                break;
                            }
                        }
                    }
                    catch (e_1_1) {
                        e_1 = { error: e_1_1 };
                    }
                    finally {
                        try {
                            if (filterOptions_1_1 && !filterOptions_1_1.done && (_a = filterOptions_1.return))
                                _a.call(filterOptions_1);
                        }
                        finally {
                            if (e_1)
                                throw e_1.error;
                        }
                    }
                    return availableMessage;
                    var e_1, _a;
                }));
            };
        /**
         * Start a request to the browser to list all available bluetooth devices
         * @param {?=} options Options to request the devices the structure is:
         * acceptAllDevices: true|false
         * filters: BluetoothDataFilterInit (see https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothlescanfilterinit for more info)
         * optionalServices: [] (services that are going to be used in
         * communication with the device, must use the UIID or GATT identfier to list ther services)
         * @return {?}
         */
        BluetoothService.prototype.discoverDevice$ = /**
         * Start a request to the browser to list all available bluetooth devices
         * @param {?=} options Options to request the devices the structure is:
         * acceptAllDevices: true|false
         * filters: BluetoothDataFilterInit (see https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothlescanfilterinit for more info)
         * optionalServices: [] (services that are going to be used in
         * communication with the device, must use the UIID or GATT identfier to list ther services)
         * @return {?}
         */
            function (options) {
                var _this = this;
                if (options === void 0) {
                    options = /** @type {?} */ ({});
                }
                return rxjs.defer(function () { return _this._webBle.requestDevice(options); }).pipe(operators.mergeMap(function (device) {
                    _this.device = device;
                    _this._device$.emit(device);
                    return _this.configureDeviceDisconnection$(device).pipe(operators.mapTo(device));
                }));
            };
        /**
         * @param {?} device
         * @return {?}
         */
        BluetoothService.prototype.configureDeviceDisconnection$ = /**
         * @param {?} device
         * @return {?}
         */
            function (device) {
                var _this = this;
                return rxjs.Observable.create(function (observer) {
                    rxjs.of(device)
                        .pipe(operators.mergeMap(function (dev) { return rxjs.fromEvent(device, 'gattserverdisconnected'); }), operators.take(1))
                        .subscribe(function () {
                        _this._console.log('Se desconecta disp en OnDevice disconnected!!!!!!!');
                        _this.device = null;
                        _this._device$.emit(null);
                    }, function (err) {
                        _this._console.log('[BLE::Info] Error in notifier: ', err);
                        observer.error(err);
                    }, function () {
                    });
                    observer.next("DisconnectionEvent as been register");
                });
            };
        /**
         * Discover all available devices and connect to a selected device
         * @param options Options to request the devices the structure is:
         * acceptAllDevices: true|false
         * filters: BluetoothDataFilterInit (see https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothlescanfilterinit for more info)
         * optionalServices: [] (services that are going to be used in
         * communication with the device, must use the UIID or GATT identfier to list ther services)
         * @returns the connected device
         */
        /**
         * Discover all available devices and connect to a selected device
         * @param {?=} options Options to request the devices the structure is:
         * acceptAllDevices: true|false
         * filters: BluetoothDataFilterInit (see https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothlescanfilterinit for more info)
         * optionalServices: [] (services that are going to be used in
         * communication with the device, must use the UIID or GATT identfier to list ther services)
         * @return {?} the connected device
         */
        BluetoothService.prototype.connectDevice$ = /**
         * Discover all available devices and connect to a selected device
         * @param {?=} options Options to request the devices the structure is:
         * acceptAllDevices: true|false
         * filters: BluetoothDataFilterInit (see https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothlescanfilterinit for more info)
         * optionalServices: [] (services that are going to be used in
         * communication with the device, must use the UIID or GATT identfier to list ther services)
         * @return {?} the connected device
         */
            function (options) {
                if (!options) {
                    options = {
                        acceptAllDevices: true,
                        optionalServices: []
                    };
                }
                else if (!options.optionalServices) {
                    options.optionalServices = [];
                }
                options.optionalServices.push(GattServices.GENERIC_ACCESS.SERVICE);
                options.optionalServices.push(GattServices.BATTERY.SERVICE);
                options.optionalServices.push(GattServices.DEVICE_INFORMATION.SERVICE);
                return this.discoverDevice$(options).pipe(operators.mergeMap(function (device) {
                    return rxjs.defer(function () { return ((device)).gatt.connect(); });
                }));
            };
        /**
         * Disconnect the current device
         */
        /**
         * Disconnect the current device
         * @return {?}
         */
        BluetoothService.prototype.disconnectDevice = /**
         * Disconnect the current device
         * @return {?}
         */
            function () {
                if (this.device) {
                    this._console.log('se deconecta dispositivo');
                    this.device.gatt.disconnect();
                }
            };
        /**
         * get a data from the device using the characteristic
         * @param service UUID or GATT identifier service
         * @param characteristic UUID or GATT identifier characteristic
         * @return The characteristic data in a DataView object
         */
        /**
         * get a data from the device using the characteristic
         * @param {?} service UUID or GATT identifier service
         * @param {?} characteristic UUID or GATT identifier characteristic
         * @return {?} The characteristic data in a DataView object
         */
        BluetoothService.prototype.readDeviceValue$ = /**
         * get a data from the device using the characteristic
         * @param {?} service UUID or GATT identifier service
         * @param {?} characteristic UUID or GATT identifier characteristic
         * @return {?} The characteristic data in a DataView object
         */
            function (service, characteristic) {
                var _this = this;
                if (!this.device) {
                    throw new Error('Must start a connection to a device before read the device value');
                }
                return this.getPrimaryService$(service).pipe(operators.mergeMap(function (primaryService) {
                    return _this.getCharacteristic$(primaryService, characteristic);
                }), operators.mergeMap(function (characteristicValue) {
                    return _this.readValue$(characteristicValue);
                }));
            };
        /**
         * write a value in the selected characteristic
         * @param characteristic the characterisitc where you want write the value
         * @param value value the value to write
         */
        /**
         * write a value in the selected characteristic
         * @param {?} service
         * @param {?} characteristic the characterisitc where you want write the value
         * @param {?} value value the value to write
         * @return {?}
         */
        BluetoothService.prototype.writeDeviceValue$ = /**
         * write a value in the selected characteristic
         * @param {?} service
         * @param {?} characteristic the characterisitc where you want write the value
         * @param {?} value value the value to write
         * @return {?}
         */
            function (service, characteristic, value) {
                var _this = this;
                if (!this.device) {
                    throw new Error('Must start a connection to a device before read the device value');
                }
                return this.getPrimaryService$(service).pipe(operators.mergeMap(function (primaryService) {
                    return _this.getCharacteristic$(primaryService, characteristic);
                }), operators.mergeMap(function (characteristicValue) {
                    return _this.writeValue$(characteristicValue, value);
                }));
            };
        /**
         * get a primary service instance using the service UIID or GATT identifier
         * @param service service identifier
         * @return service instance
         */
        /**
         * get a primary service instance using the service UIID or GATT identifier
         * @param {?} service service identifier
         * @return {?} service instance
         */
        BluetoothService.prototype.getPrimaryService$ = /**
         * get a primary service instance using the service UIID or GATT identifier
         * @param {?} service service identifier
         * @return {?} service instance
         */
            function (service) {
                return rxjs.of(this.device).pipe(operators.mergeMap(function (device) {
                    return device.gatt.getPrimaryService(service);
                }));
            };
        /**
         * Get a characterisitic instance using the service instance and a characteristic UUID
         * @param primaryService service instance
         * @param characteristic characterisitic identifier
         * @return characteristic instance
         */
        /**
         * Get a characterisitic instance using the service instance and a characteristic UUID
         * @param {?} primaryService service instance
         * @param {?} characteristic characterisitic identifier
         * @return {?} characteristic instance
         */
        BluetoothService.prototype.getCharacteristic$ = /**
         * Get a characterisitic instance using the service instance and a characteristic UUID
         * @param {?} primaryService service instance
         * @param {?} characteristic characterisitic identifier
         * @return {?} characteristic instance
         */
            function (primaryService, characteristic) {
                return rxjs.defer(function () { return primaryService.getCharacteristic(characteristic); });
            };
        /**
         * read the characteristic value
         * @param {?} characteristic characteristic instance
         * @return {?} The characteristic data in a DataView object
         */
        BluetoothService.prototype.readValue$ = /**
         * read the characteristic value
         * @param {?} characteristic characteristic instance
         * @return {?} The characteristic data in a DataView object
         */
            function (characteristic) {
                return rxjs.from(characteristic
                    .readValue()
                    .then(function (data) { return Promise.resolve(data); }, function (error) { return Promise.reject("" + error.message); }));
            };
        /**
         * write a value in the selected characteristic
         * @param {?} characteristic the characterisitc where you want write the value
         * @param {?} value the value to write
         * @return {?}
         */
        BluetoothService.prototype.writeValue$ = /**
         * write a value in the selected characteristic
         * @param {?} characteristic the characterisitc where you want write the value
         * @param {?} value the value to write
         * @return {?}
         */
            function (characteristic, value) {
                return rxjs.defer(function () {
                    return characteristic
                        .writeValue(value);
                });
            };
        /**
         * change the state of the characteristic to enable it
         * @param service  parent service of the characteristic
         * @param characteristic characteristic to change the state
         * @param state new state
         */
        /**
         * change the state of the characteristic to enable it
         * @param {?} service  parent service of the characteristic
         * @param {?} characteristic characteristic to change the state
         * @param {?=} state new state
         * @return {?}
         */
        BluetoothService.prototype.enableCharacteristic$ = /**
         * change the state of the characteristic to enable it
         * @param {?} service  parent service of the characteristic
         * @param {?} characteristic characteristic to change the state
         * @param {?=} state new state
         * @return {?}
         */
            function (service, characteristic, state) {
                state = state || new Uint8Array([1]);
                return this.setCharacteristicState$(service, characteristic, state);
            };
        /**
         * change the state of the characteristic to disable it
         * @param service  parent service of the characteristic
         * @param characteristic characteristic to change the state
         * @param state new state
         */
        /**
         * change the state of the characteristic to disable it
         * @param {?} service  parent service of the characteristic
         * @param {?} characteristic characteristic to change the state
         * @param {?=} state new state
         * @return {?}
         */
        BluetoothService.prototype.disbaleCharacteristic$ = /**
         * change the state of the characteristic to disable it
         * @param {?} service  parent service of the characteristic
         * @param {?} characteristic characteristic to change the state
         * @param {?=} state new state
         * @return {?}
         */
            function (service, characteristic, state) {
                state = state || new Uint8Array([0]);
                return this.setCharacteristicState$(service, characteristic, state);
            };
        /**
         * set a state to an specific characteristic
         * @param service  parent service of the characteristic
         * @param characteristic characteristic to change the state
         * @param state new state
         * @return
         */
        /**
         * set a state to an specific characteristic
         * @param {?} service  parent service of the characteristic
         * @param {?} characteristic characteristic to change the state
         * @param {?} state new state
         * @return {?}
         */
        BluetoothService.prototype.setCharacteristicState$ = /**
         * set a state to an specific characteristic
         * @param {?} service  parent service of the characteristic
         * @param {?} characteristic characteristic to change the state
         * @param {?} state new state
         * @return {?}
         */
            function (service, characteristic, state) {
                var _this = this;
                /** @type {?} */
                var primaryService = this.getPrimaryService$(service);
                return primaryService.pipe(operators.mergeMap(function (_primaryService) {
                    return _this.getCharacteristic$(_primaryService, characteristic);
                }), operators.map(function (_characteristic) {
                    return _this.writeValue$(_characteristic, state);
                }));
            };
        /**
         * Send a message using a notifier characteristic
         * @param message message to send
         * @param service service to which the characteristic belongs
         * @param characteristic feature in which you want to send the notification
         */
        /**
         * Send a message using a notifier characteristic
         * @param {?} message message to send
         * @param {?} service service to which the characteristic belongs
         * @param {?} characteristic feature in which you want to send the notification
         * @return {?}
         */
        BluetoothService.prototype.sendToNotifier$ = /**
         * Send a message using a notifier characteristic
         * @param {?} message message to send
         * @param {?} service service to which the characteristic belongs
         * @param {?} characteristic feature in which you want to send the notification
         * @return {?}
         */
            function (message, service, characteristic) {
                var _this = this;
                return this.getPrimaryService$(service).pipe(operators.mergeMap(function (primaryService) {
                    return _this.getCharacteristic$(primaryService, characteristic);
                }), operators.mergeMap(function (char) {
                    if (message.length > 16) {
                        /** @type {?} */
                        var obsTest = rxjs.of(undefined);
                        while (message.length > 16) {
                            obsTest = obsTest.pipe(operators.concat(_this.writeValue$(char, message.slice(0, 16))));
                            message = message.slice(16, message.length);
                        }
                        if (message.length > 0) {
                            obsTest = obsTest.pipe(operators.concat(_this.writeValue$(char, message)));
                        }
                        return obsTest;
                    }
                    else {
                        return _this.writeValue$(char, message);
                    }
                }));
            };
        /**
         * The Battery Level characteristic is read using the GATT Read Characteristic
         *  Value sub-procedure and returns the current battery level as a percentage
         *  from 0% to 100%; 0% represents a battery that is fully discharged, 100%
         * represents a battery that is fully charged
         */
        /**
         * The Battery Level characteristic is read using the GATT Read Characteristic
         *  Value sub-procedure and returns the current battery level as a percentage
         *  from 0% to 100%; 0% represents a battery that is fully discharged, 100%
         * represents a battery that is fully charged
         * @return {?}
         */
        BluetoothService.prototype.getBatteryLevel$ = /**
         * The Battery Level characteristic is read using the GATT Read Characteristic
         *  Value sub-procedure and returns the current battery level as a percentage
         *  from 0% to 100%; 0% represents a battery that is fully discharged, 100%
         * represents a battery that is fully charged
         * @return {?}
         */
            function () {
                return this.readDeviceValue$(GattServices.BATTERY.SERVICE, GattServices.BATTERY.BATTERY_LEVEL).pipe(operators.map(function (value) { return value.getUint8(0); }));
            };
        /**
         * This characteristic represents the name of the manufacturer of the device.
         */
        /**
         * This characteristic represents the name of the manufacturer of the device.
         * @return {?}
         */
        BluetoothService.prototype.getManufacturerName$ = /**
         * This characteristic represents the name of the manufacturer of the device.
         * @return {?}
         */
            function () {
                var _this = this;
                return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.MANUFACTURER_NAME).pipe(operators.map(function (dataView) {
                    return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
                }));
            };
        /**
         * This characteristic represents the model number that is assigned by the device vendor.
         */
        /**
         * This characteristic represents the model number that is assigned by the device vendor.
         * @return {?}
         */
        BluetoothService.prototype.getModelNumber$ = /**
         * This characteristic represents the model number that is assigned by the device vendor.
         * @return {?}
         */
            function () {
                var _this = this;
                return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.MODEL_NUMBER).pipe(operators.map(function (dataView) {
                    return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
                }));
            };
        /**
         * This characteristic represents the serial number for a particular instance of the device.
         */
        /**
         * This characteristic represents the serial number for a particular instance of the device.
         * @return {?}
         */
        BluetoothService.prototype.getSerialNumber$ = /**
         * This characteristic represents the serial number for a particular instance of the device.
         * @return {?}
         */
            function () {
                var _this = this;
                return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.SERIAL_NUMBER).pipe(operators.map(function (dataView) {
                    return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
                }));
            };
        /**
         * This characteristic represents the hardware revision for the hardware within the device.
         */
        /**
         * This characteristic represents the hardware revision for the hardware within the device.
         * @return {?}
         */
        BluetoothService.prototype.getHardwareRevision$ = /**
         * This characteristic represents the hardware revision for the hardware within the device.
         * @return {?}
         */
            function () {
                var _this = this;
                return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.HARDWARE_REVISION).pipe(operators.map(function (dataView) {
                    return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
                }));
            };
        /**
         * This characteristic represents the firmware revision for the firmware within the device.
         */
        /**
         * This characteristic represents the firmware revision for the firmware within the device.
         * @return {?}
         */
        BluetoothService.prototype.getFirmwareRevision$ = /**
         * This characteristic represents the firmware revision for the firmware within the device.
         * @return {?}
         */
            function () {
                var _this = this;
                return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.FIRMWARE_REVISION).pipe(operators.map(function (dataView) {
                    return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
                }));
            };
        /**
         * This characteristic represents the software revision for the software within the device.
         */
        /**
         * This characteristic represents the software revision for the software within the device.
         * @return {?}
         */
        BluetoothService.prototype.getSoftwareRevision$ = /**
         * This characteristic represents the software revision for the software within the device.
         * @return {?}
         */
            function () {
                var _this = this;
                return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.SOFTWARE_REVISION).pipe(operators.map(function (dataView) {
                    return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
                }));
            };
        /**
         * This characteristic represents a structure containing an Organizationally Unique Identifier
         *  (OUI) followed by a manufacturer-defined identifier and is unique for each individual instance of the product.
         */
        /**
         * This characteristic represents a structure containing an Organizationally Unique Identifier
         *  (OUI) followed by a manufacturer-defined identifier and is unique for each individual instance of the product.
         * @return {?}
         */
        BluetoothService.prototype.getSystemId$ = /**
         * This characteristic represents a structure containing an Organizationally Unique Identifier
         *  (OUI) followed by a manufacturer-defined identifier and is unique for each individual instance of the product.
         * @return {?}
         */
            function () {
                return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.SYSTEM_ID);
            };
        /**
         * The PnP_ID characteristic is a set of values used to create a device ID value that is unique for this device.
         */
        /**
         * The PnP_ID characteristic is a set of values used to create a device ID value that is unique for this device.
         * @return {?}
         */
        BluetoothService.prototype.getPnpId$ = /**
         * The PnP_ID characteristic is a set of values used to create a device ID value that is unique for this device.
         * @return {?}
         */
            function () {
                return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.PNP_ID);
            };
        BluetoothService.decorators = [
            { type: i0.Injectable, args: [{
                        providedIn: 'root'
                    },] },
        ];
        /** @nocollapse */
        BluetoothService.ctorParameters = function () {
            return [
                { type: BrowserWebBluetooth },
                { type: CypherAesService },
                { type: ConsoleLoggerService }
            ];
        };
        /** @nocollapse */ BluetoothService.ngInjectableDef = i0.defineInjectable({ factory: function BluetoothService_Factory() { return new BluetoothService(i0.inject(BrowserWebBluetooth), i0.inject(CypherAesService), i0.inject(ConsoleLoggerService)); }, token: BluetoothService, providedIn: "root" });
        return BluetoothService;
    }(rxjs.Subject));

    /**
     * @fileoverview added by tsickle
     * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
     */
    /**
     * @return {?}
     */
    function browserWebBluetooth() {
        return new BrowserWebBluetooth();
    }
    /**
     * @param {?} options
     * @return {?}
     */
    function consoleLoggerServiceConfig(options) {
        if (options && options.enableTracing) {
            return new ConsoleLoggerService();
        }
        else {
            return new NoLoggerService();
        }
    }
    /**
     * @return {?}
     */
    function makeMeTokenInjector() {
        return new i0.InjectionToken('AWBOptions');
    }
    var AngularBleModule = (function () {
        function AngularBleModule() {
        }
        /**
         * @param {?=} options
         * @return {?}
         */
        AngularBleModule.forRoot = /**
         * @param {?=} options
         * @return {?}
         */
            function (options) {
                if (options === void 0) {
                    options = {};
                }
                return {
                    ngModule: AngularBleModule,
                    providers: [
                        BluetoothService,
                        {
                            provide: BrowserWebBluetooth,
                            useFactory: browserWebBluetooth
                        },
                        {
                            provide: makeMeTokenInjector,
                            useValue: options
                        },
                        {
                            provide: ConsoleLoggerService,
                            useFactory: consoleLoggerServiceConfig,
                            deps: [makeMeTokenInjector]
                        }
                    ]
                };
            };
        AngularBleModule.decorators = [
            { type: i0.NgModule, args: [{
                        imports: [common.CommonModule]
                    },] },
        ];
        return AngularBleModule;
    }());

    /**
     * @fileoverview added by tsickle
     * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
     */

    /**
     * @fileoverview added by tsickle
     * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
     */

    exports.BrowserWebBluetooth = BrowserWebBluetooth;
    exports.browserWebBluetooth = browserWebBluetooth;
    exports.consoleLoggerServiceConfig = consoleLoggerServiceConfig;
    exports.makeMeTokenInjector = makeMeTokenInjector;
    exports.AngularBleModule = AngularBleModule;
    exports.CypherAesService = CypherAesService;
    exports.BluetoothService = BluetoothService;
    exports.ɵa = ConsoleLoggerService;

    Object.defineProperty(exports, '__esModule', { value: true });

})));

//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmVidWxhZS1hbmd1bGFyLWJsZS51bWQuanMubWFwIiwic291cmNlcyI6WyJuZzovL0BuZWJ1bGFlL2FuZ3VsYXItYmxlL2xpYi9wbGF0Zm9ybS9icm93c2VyLnRzIixudWxsLCJuZzovL0BuZWJ1bGFlL2FuZ3VsYXItYmxlL2xpYi9ibHVldG9vdGgvZ2F0dC1zZXJ2aWNlcy50cyIsIm5nOi8vQG5lYnVsYWUvYW5ndWxhci1ibGUvbGliL2N5cGhlci9jeXBoZXItYWVzLnNlcnZpY2UudHMiLCJuZzovL0BuZWJ1bGFlL2FuZ3VsYXItYmxlL2xpYi9sb2dnZXIuc2VydmljZS50cyIsIm5nOi8vQG5lYnVsYWUvYW5ndWxhci1ibGUvbGliL2JsdWV0b290aC9ibHVldG9vdGguc2VydmljZS50cyIsIm5nOi8vQG5lYnVsYWUvYW5ndWxhci1ibGUvbGliL2FuZ3VsYXItYmxlLm1vZHVsZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBCbHVldG9vdGhTZXJ2aWNlIH0gZnJvbSAnLi4vYmx1ZXRvb3RoL2JsdWV0b290aC5zZXJ2aWNlJztcblxuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIEJyb3dzZXJXZWJCbHVldG9vdGgge1xuICBwdWJsaWMgX2JsZTtcblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLl9ibGUgPSBuYXZpZ2F0b3IuYmx1ZXRvb3RoO1xuICAgIGlmICghdGhpcy5fYmxlKSB7XG4gICAgICBjb25zb2xlLmxvZygnZXJyb3IgY2FyZ2FuZG8gYmx1ZXRvb3RoJyk7XG4gICAgICAvLyBibHVldG9vdGhTZXJ2aWNlLnNldEJsdWV0b290aEF2YWlsYWJsZShmYWxzZSk7XG4gICAgICAvLyB0aHJvdyBuZXcgRXJyb3IoJ1lvdXIgYnJvd3NlciBkb2VzIG5vdCBzdXBwb3J0IFNtYXJ0IEJsdWV0b290aC4gU2VlIGh0dHA6Ly9jYW5pdXNlLmNvbS8jc2VhcmNoPUJsdWV0b290aCBmb3IgbW9yZSBkZXRhaWxzLicpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBibHVldG9vdGhTZXJ2aWNlLnNldEJsdWV0b290aEF2YWlsYWJsZSh0cnVlKTtcbiAgICB9XG4gIH1cblxuICByZXF1ZXN0RGV2aWNlKG9wdGlvbnM6IFJlcXVlc3REZXZpY2VPcHRpb25zKTogUHJvbWlzZTxCbHVldG9vdGhEZXZpY2U+IHtcbiAgICByZXR1cm4gdGhpcy5fYmxlLnJlcXVlc3REZXZpY2Uob3B0aW9ucyk7XG4gIH1cbn1cbiIsIi8qISAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5Db3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTsgeW91IG1heSBub3QgdXNlXHJcbnRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlXHJcbkxpY2Vuc2UgYXQgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXHJcblxyXG5USElTIENPREUgSVMgUFJPVklERUQgT04gQU4gKkFTIElTKiBCQVNJUywgV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZXHJcbktJTkQsIEVJVEhFUiBFWFBSRVNTIE9SIElNUExJRUQsIElOQ0xVRElORyBXSVRIT1VUIExJTUlUQVRJT04gQU5ZIElNUExJRURcclxuV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIFRJVExFLCBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSxcclxuTUVSQ0hBTlRBQkxJVFkgT1IgTk9OLUlORlJJTkdFTUVOVC5cclxuXHJcblNlZSB0aGUgQXBhY2hlIFZlcnNpb24gMi4wIExpY2Vuc2UgZm9yIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9uc1xyXG5hbmQgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqICovXHJcbi8qIGdsb2JhbCBSZWZsZWN0LCBQcm9taXNlICovXHJcblxyXG52YXIgZXh0ZW5kU3RhdGljcyA9IGZ1bmN0aW9uKGQsIGIpIHtcclxuICAgIGV4dGVuZFN0YXRpY3MgPSBPYmplY3Quc2V0UHJvdG90eXBlT2YgfHxcclxuICAgICAgICAoeyBfX3Byb3RvX186IFtdIH0gaW5zdGFuY2VvZiBBcnJheSAmJiBmdW5jdGlvbiAoZCwgYikgeyBkLl9fcHJvdG9fXyA9IGI7IH0pIHx8XHJcbiAgICAgICAgZnVuY3Rpb24gKGQsIGIpIHsgZm9yICh2YXIgcCBpbiBiKSBpZiAoYi5oYXNPd25Qcm9wZXJ0eShwKSkgZFtwXSA9IGJbcF07IH07XHJcbiAgICByZXR1cm4gZXh0ZW5kU3RhdGljcyhkLCBiKTtcclxufTtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2V4dGVuZHMoZCwgYikge1xyXG4gICAgZXh0ZW5kU3RhdGljcyhkLCBiKTtcclxuICAgIGZ1bmN0aW9uIF9fKCkgeyB0aGlzLmNvbnN0cnVjdG9yID0gZDsgfVxyXG4gICAgZC5wcm90b3R5cGUgPSBiID09PSBudWxsID8gT2JqZWN0LmNyZWF0ZShiKSA6IChfXy5wcm90b3R5cGUgPSBiLnByb3RvdHlwZSwgbmV3IF9fKCkpO1xyXG59XHJcblxyXG5leHBvcnQgdmFyIF9fYXNzaWduID0gZnVuY3Rpb24oKSB7XHJcbiAgICBfX2Fzc2lnbiA9IE9iamVjdC5hc3NpZ24gfHwgZnVuY3Rpb24gX19hc3NpZ24odCkge1xyXG4gICAgICAgIGZvciAodmFyIHMsIGkgPSAxLCBuID0gYXJndW1lbnRzLmxlbmd0aDsgaSA8IG47IGkrKykge1xyXG4gICAgICAgICAgICBzID0gYXJndW1lbnRzW2ldO1xyXG4gICAgICAgICAgICBmb3IgKHZhciBwIGluIHMpIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwocywgcCkpIHRbcF0gPSBzW3BdO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gdDtcclxuICAgIH1cclxuICAgIHJldHVybiBfX2Fzc2lnbi5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19yZXN0KHMsIGUpIHtcclxuICAgIHZhciB0ID0ge307XHJcbiAgICBmb3IgKHZhciBwIGluIHMpIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwocywgcCkgJiYgZS5pbmRleE9mKHApIDwgMClcclxuICAgICAgICB0W3BdID0gc1twXTtcclxuICAgIGlmIChzICE9IG51bGwgJiYgdHlwZW9mIE9iamVjdC5nZXRPd25Qcm9wZXJ0eVN5bWJvbHMgPT09IFwiZnVuY3Rpb25cIilcclxuICAgICAgICBmb3IgKHZhciBpID0gMCwgcCA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eVN5bWJvbHMocyk7IGkgPCBwLmxlbmd0aDsgaSsrKSBpZiAoZS5pbmRleE9mKHBbaV0pIDwgMClcclxuICAgICAgICAgICAgdFtwW2ldXSA9IHNbcFtpXV07XHJcbiAgICByZXR1cm4gdDtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fZGVjb3JhdGUoZGVjb3JhdG9ycywgdGFyZ2V0LCBrZXksIGRlc2MpIHtcclxuICAgIHZhciBjID0gYXJndW1lbnRzLmxlbmd0aCwgciA9IGMgPCAzID8gdGFyZ2V0IDogZGVzYyA9PT0gbnVsbCA/IGRlc2MgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHRhcmdldCwga2V5KSA6IGRlc2MsIGQ7XHJcbiAgICBpZiAodHlwZW9mIFJlZmxlY3QgPT09IFwib2JqZWN0XCIgJiYgdHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUgPT09IFwiZnVuY3Rpb25cIikgciA9IFJlZmxlY3QuZGVjb3JhdGUoZGVjb3JhdG9ycywgdGFyZ2V0LCBrZXksIGRlc2MpO1xyXG4gICAgZWxzZSBmb3IgKHZhciBpID0gZGVjb3JhdG9ycy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkgaWYgKGQgPSBkZWNvcmF0b3JzW2ldKSByID0gKGMgPCAzID8gZChyKSA6IGMgPiAzID8gZCh0YXJnZXQsIGtleSwgcikgOiBkKHRhcmdldCwga2V5KSkgfHwgcjtcclxuICAgIHJldHVybiBjID4gMyAmJiByICYmIE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0YXJnZXQsIGtleSwgciksIHI7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX3BhcmFtKHBhcmFtSW5kZXgsIGRlY29yYXRvcikge1xyXG4gICAgcmV0dXJuIGZ1bmN0aW9uICh0YXJnZXQsIGtleSkgeyBkZWNvcmF0b3IodGFyZ2V0LCBrZXksIHBhcmFtSW5kZXgpOyB9XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX21ldGFkYXRhKG1ldGFkYXRhS2V5LCBtZXRhZGF0YVZhbHVlKSB7XHJcbiAgICBpZiAodHlwZW9mIFJlZmxlY3QgPT09IFwib2JqZWN0XCIgJiYgdHlwZW9mIFJlZmxlY3QubWV0YWRhdGEgPT09IFwiZnVuY3Rpb25cIikgcmV0dXJuIFJlZmxlY3QubWV0YWRhdGEobWV0YWRhdGFLZXksIG1ldGFkYXRhVmFsdWUpO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19hd2FpdGVyKHRoaXNBcmcsIF9hcmd1bWVudHMsIFAsIGdlbmVyYXRvcikge1xyXG4gICAgcmV0dXJuIG5ldyAoUCB8fCAoUCA9IFByb21pc2UpKShmdW5jdGlvbiAocmVzb2x2ZSwgcmVqZWN0KSB7XHJcbiAgICAgICAgZnVuY3Rpb24gZnVsZmlsbGVkKHZhbHVlKSB7IHRyeSB7IHN0ZXAoZ2VuZXJhdG9yLm5leHQodmFsdWUpKTsgfSBjYXRjaCAoZSkgeyByZWplY3QoZSk7IH0gfVxyXG4gICAgICAgIGZ1bmN0aW9uIHJlamVjdGVkKHZhbHVlKSB7IHRyeSB7IHN0ZXAoZ2VuZXJhdG9yW1widGhyb3dcIl0odmFsdWUpKTsgfSBjYXRjaCAoZSkgeyByZWplY3QoZSk7IH0gfVxyXG4gICAgICAgIGZ1bmN0aW9uIHN0ZXAocmVzdWx0KSB7IHJlc3VsdC5kb25lID8gcmVzb2x2ZShyZXN1bHQudmFsdWUpIDogbmV3IFAoZnVuY3Rpb24gKHJlc29sdmUpIHsgcmVzb2x2ZShyZXN1bHQudmFsdWUpOyB9KS50aGVuKGZ1bGZpbGxlZCwgcmVqZWN0ZWQpOyB9XHJcbiAgICAgICAgc3RlcCgoZ2VuZXJhdG9yID0gZ2VuZXJhdG9yLmFwcGx5KHRoaXNBcmcsIF9hcmd1bWVudHMgfHwgW10pKS5uZXh0KCkpO1xyXG4gICAgfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2dlbmVyYXRvcih0aGlzQXJnLCBib2R5KSB7XHJcbiAgICB2YXIgXyA9IHsgbGFiZWw6IDAsIHNlbnQ6IGZ1bmN0aW9uKCkgeyBpZiAodFswXSAmIDEpIHRocm93IHRbMV07IHJldHVybiB0WzFdOyB9LCB0cnlzOiBbXSwgb3BzOiBbXSB9LCBmLCB5LCB0LCBnO1xyXG4gICAgcmV0dXJuIGcgPSB7IG5leHQ6IHZlcmIoMCksIFwidGhyb3dcIjogdmVyYigxKSwgXCJyZXR1cm5cIjogdmVyYigyKSB9LCB0eXBlb2YgU3ltYm9sID09PSBcImZ1bmN0aW9uXCIgJiYgKGdbU3ltYm9sLml0ZXJhdG9yXSA9IGZ1bmN0aW9uKCkgeyByZXR1cm4gdGhpczsgfSksIGc7XHJcbiAgICBmdW5jdGlvbiB2ZXJiKG4pIHsgcmV0dXJuIGZ1bmN0aW9uICh2KSB7IHJldHVybiBzdGVwKFtuLCB2XSk7IH07IH1cclxuICAgIGZ1bmN0aW9uIHN0ZXAob3ApIHtcclxuICAgICAgICBpZiAoZikgdGhyb3cgbmV3IFR5cGVFcnJvcihcIkdlbmVyYXRvciBpcyBhbHJlYWR5IGV4ZWN1dGluZy5cIik7XHJcbiAgICAgICAgd2hpbGUgKF8pIHRyeSB7XHJcbiAgICAgICAgICAgIGlmIChmID0gMSwgeSAmJiAodCA9IG9wWzBdICYgMiA/IHlbXCJyZXR1cm5cIl0gOiBvcFswXSA/IHlbXCJ0aHJvd1wiXSB8fCAoKHQgPSB5W1wicmV0dXJuXCJdKSAmJiB0LmNhbGwoeSksIDApIDogeS5uZXh0KSAmJiAhKHQgPSB0LmNhbGwoeSwgb3BbMV0pKS5kb25lKSByZXR1cm4gdDtcclxuICAgICAgICAgICAgaWYgKHkgPSAwLCB0KSBvcCA9IFtvcFswXSAmIDIsIHQudmFsdWVdO1xyXG4gICAgICAgICAgICBzd2l0Y2ggKG9wWzBdKSB7XHJcbiAgICAgICAgICAgICAgICBjYXNlIDA6IGNhc2UgMTogdCA9IG9wOyBicmVhaztcclxuICAgICAgICAgICAgICAgIGNhc2UgNDogXy5sYWJlbCsrOyByZXR1cm4geyB2YWx1ZTogb3BbMV0sIGRvbmU6IGZhbHNlIH07XHJcbiAgICAgICAgICAgICAgICBjYXNlIDU6IF8ubGFiZWwrKzsgeSA9IG9wWzFdOyBvcCA9IFswXTsgY29udGludWU7XHJcbiAgICAgICAgICAgICAgICBjYXNlIDc6IG9wID0gXy5vcHMucG9wKCk7IF8udHJ5cy5wb3AoKTsgY29udGludWU7XHJcbiAgICAgICAgICAgICAgICBkZWZhdWx0OlxyXG4gICAgICAgICAgICAgICAgICAgIGlmICghKHQgPSBfLnRyeXMsIHQgPSB0Lmxlbmd0aCA+IDAgJiYgdFt0Lmxlbmd0aCAtIDFdKSAmJiAob3BbMF0gPT09IDYgfHwgb3BbMF0gPT09IDIpKSB7IF8gPSAwOyBjb250aW51ZTsgfVxyXG4gICAgICAgICAgICAgICAgICAgIGlmIChvcFswXSA9PT0gMyAmJiAoIXQgfHwgKG9wWzFdID4gdFswXSAmJiBvcFsxXSA8IHRbM10pKSkgeyBfLmxhYmVsID0gb3BbMV07IGJyZWFrOyB9XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKG9wWzBdID09PSA2ICYmIF8ubGFiZWwgPCB0WzFdKSB7IF8ubGFiZWwgPSB0WzFdOyB0ID0gb3A7IGJyZWFrOyB9XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHQgJiYgXy5sYWJlbCA8IHRbMl0pIHsgXy5sYWJlbCA9IHRbMl07IF8ub3BzLnB1c2gob3ApOyBicmVhazsgfVxyXG4gICAgICAgICAgICAgICAgICAgIGlmICh0WzJdKSBfLm9wcy5wb3AoKTtcclxuICAgICAgICAgICAgICAgICAgICBfLnRyeXMucG9wKCk7IGNvbnRpbnVlO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIG9wID0gYm9keS5jYWxsKHRoaXNBcmcsIF8pO1xyXG4gICAgICAgIH0gY2F0Y2ggKGUpIHsgb3AgPSBbNiwgZV07IHkgPSAwOyB9IGZpbmFsbHkgeyBmID0gdCA9IDA7IH1cclxuICAgICAgICBpZiAob3BbMF0gJiA1KSB0aHJvdyBvcFsxXTsgcmV0dXJuIHsgdmFsdWU6IG9wWzBdID8gb3BbMV0gOiB2b2lkIDAsIGRvbmU6IHRydWUgfTtcclxuICAgIH1cclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fZXhwb3J0U3RhcihtLCBleHBvcnRzKSB7XHJcbiAgICBmb3IgKHZhciBwIGluIG0pIGlmICghZXhwb3J0cy5oYXNPd25Qcm9wZXJ0eShwKSkgZXhwb3J0c1twXSA9IG1bcF07XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX3ZhbHVlcyhvKSB7XHJcbiAgICB2YXIgbSA9IHR5cGVvZiBTeW1ib2wgPT09IFwiZnVuY3Rpb25cIiAmJiBvW1N5bWJvbC5pdGVyYXRvcl0sIGkgPSAwO1xyXG4gICAgaWYgKG0pIHJldHVybiBtLmNhbGwobyk7XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICAgIG5leHQ6IGZ1bmN0aW9uICgpIHtcclxuICAgICAgICAgICAgaWYgKG8gJiYgaSA+PSBvLmxlbmd0aCkgbyA9IHZvaWQgMDtcclxuICAgICAgICAgICAgcmV0dXJuIHsgdmFsdWU6IG8gJiYgb1tpKytdLCBkb25lOiAhbyB9O1xyXG4gICAgICAgIH1cclxuICAgIH07XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX3JlYWQobywgbikge1xyXG4gICAgdmFyIG0gPSB0eXBlb2YgU3ltYm9sID09PSBcImZ1bmN0aW9uXCIgJiYgb1tTeW1ib2wuaXRlcmF0b3JdO1xyXG4gICAgaWYgKCFtKSByZXR1cm4gbztcclxuICAgIHZhciBpID0gbS5jYWxsKG8pLCByLCBhciA9IFtdLCBlO1xyXG4gICAgdHJ5IHtcclxuICAgICAgICB3aGlsZSAoKG4gPT09IHZvaWQgMCB8fCBuLS0gPiAwKSAmJiAhKHIgPSBpLm5leHQoKSkuZG9uZSkgYXIucHVzaChyLnZhbHVlKTtcclxuICAgIH1cclxuICAgIGNhdGNoIChlcnJvcikgeyBlID0geyBlcnJvcjogZXJyb3IgfTsgfVxyXG4gICAgZmluYWxseSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgaWYgKHIgJiYgIXIuZG9uZSAmJiAobSA9IGlbXCJyZXR1cm5cIl0pKSBtLmNhbGwoaSk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGZpbmFsbHkgeyBpZiAoZSkgdGhyb3cgZS5lcnJvcjsgfVxyXG4gICAgfVxyXG4gICAgcmV0dXJuIGFyO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19zcHJlYWQoKSB7XHJcbiAgICBmb3IgKHZhciBhciA9IFtdLCBpID0gMDsgaSA8IGFyZ3VtZW50cy5sZW5ndGg7IGkrKylcclxuICAgICAgICBhciA9IGFyLmNvbmNhdChfX3JlYWQoYXJndW1lbnRzW2ldKSk7XHJcbiAgICByZXR1cm4gYXI7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2F3YWl0KHYpIHtcclxuICAgIHJldHVybiB0aGlzIGluc3RhbmNlb2YgX19hd2FpdCA/ICh0aGlzLnYgPSB2LCB0aGlzKSA6IG5ldyBfX2F3YWl0KHYpO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19hc3luY0dlbmVyYXRvcih0aGlzQXJnLCBfYXJndW1lbnRzLCBnZW5lcmF0b3IpIHtcclxuICAgIGlmICghU3ltYm9sLmFzeW5jSXRlcmF0b3IpIHRocm93IG5ldyBUeXBlRXJyb3IoXCJTeW1ib2wuYXN5bmNJdGVyYXRvciBpcyBub3QgZGVmaW5lZC5cIik7XHJcbiAgICB2YXIgZyA9IGdlbmVyYXRvci5hcHBseSh0aGlzQXJnLCBfYXJndW1lbnRzIHx8IFtdKSwgaSwgcSA9IFtdO1xyXG4gICAgcmV0dXJuIGkgPSB7fSwgdmVyYihcIm5leHRcIiksIHZlcmIoXCJ0aHJvd1wiKSwgdmVyYihcInJldHVyblwiKSwgaVtTeW1ib2wuYXN5bmNJdGVyYXRvcl0gPSBmdW5jdGlvbiAoKSB7IHJldHVybiB0aGlzOyB9LCBpO1xyXG4gICAgZnVuY3Rpb24gdmVyYihuKSB7IGlmIChnW25dKSBpW25dID0gZnVuY3Rpb24gKHYpIHsgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uIChhLCBiKSB7IHEucHVzaChbbiwgdiwgYSwgYl0pID4gMSB8fCByZXN1bWUobiwgdik7IH0pOyB9OyB9XHJcbiAgICBmdW5jdGlvbiByZXN1bWUobiwgdikgeyB0cnkgeyBzdGVwKGdbbl0odikpOyB9IGNhdGNoIChlKSB7IHNldHRsZShxWzBdWzNdLCBlKTsgfSB9XHJcbiAgICBmdW5jdGlvbiBzdGVwKHIpIHsgci52YWx1ZSBpbnN0YW5jZW9mIF9fYXdhaXQgPyBQcm9taXNlLnJlc29sdmUoci52YWx1ZS52KS50aGVuKGZ1bGZpbGwsIHJlamVjdCkgOiBzZXR0bGUocVswXVsyXSwgcik7IH1cclxuICAgIGZ1bmN0aW9uIGZ1bGZpbGwodmFsdWUpIHsgcmVzdW1lKFwibmV4dFwiLCB2YWx1ZSk7IH1cclxuICAgIGZ1bmN0aW9uIHJlamVjdCh2YWx1ZSkgeyByZXN1bWUoXCJ0aHJvd1wiLCB2YWx1ZSk7IH1cclxuICAgIGZ1bmN0aW9uIHNldHRsZShmLCB2KSB7IGlmIChmKHYpLCBxLnNoaWZ0KCksIHEubGVuZ3RoKSByZXN1bWUocVswXVswXSwgcVswXVsxXSk7IH1cclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fYXN5bmNEZWxlZ2F0b3Iobykge1xyXG4gICAgdmFyIGksIHA7XHJcbiAgICByZXR1cm4gaSA9IHt9LCB2ZXJiKFwibmV4dFwiKSwgdmVyYihcInRocm93XCIsIGZ1bmN0aW9uIChlKSB7IHRocm93IGU7IH0pLCB2ZXJiKFwicmV0dXJuXCIpLCBpW1N5bWJvbC5pdGVyYXRvcl0gPSBmdW5jdGlvbiAoKSB7IHJldHVybiB0aGlzOyB9LCBpO1xyXG4gICAgZnVuY3Rpb24gdmVyYihuLCBmKSB7IGlbbl0gPSBvW25dID8gZnVuY3Rpb24gKHYpIHsgcmV0dXJuIChwID0gIXApID8geyB2YWx1ZTogX19hd2FpdChvW25dKHYpKSwgZG9uZTogbiA9PT0gXCJyZXR1cm5cIiB9IDogZiA/IGYodikgOiB2OyB9IDogZjsgfVxyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gX19hc3luY1ZhbHVlcyhvKSB7XHJcbiAgICBpZiAoIVN5bWJvbC5hc3luY0l0ZXJhdG9yKSB0aHJvdyBuZXcgVHlwZUVycm9yKFwiU3ltYm9sLmFzeW5jSXRlcmF0b3IgaXMgbm90IGRlZmluZWQuXCIpO1xyXG4gICAgdmFyIG0gPSBvW1N5bWJvbC5hc3luY0l0ZXJhdG9yXSwgaTtcclxuICAgIHJldHVybiBtID8gbS5jYWxsKG8pIDogKG8gPSB0eXBlb2YgX192YWx1ZXMgPT09IFwiZnVuY3Rpb25cIiA/IF9fdmFsdWVzKG8pIDogb1tTeW1ib2wuaXRlcmF0b3JdKCksIGkgPSB7fSwgdmVyYihcIm5leHRcIiksIHZlcmIoXCJ0aHJvd1wiKSwgdmVyYihcInJldHVyblwiKSwgaVtTeW1ib2wuYXN5bmNJdGVyYXRvcl0gPSBmdW5jdGlvbiAoKSB7IHJldHVybiB0aGlzOyB9LCBpKTtcclxuICAgIGZ1bmN0aW9uIHZlcmIobikgeyBpW25dID0gb1tuXSAmJiBmdW5jdGlvbiAodikgeyByZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24gKHJlc29sdmUsIHJlamVjdCkgeyB2ID0gb1tuXSh2KSwgc2V0dGxlKHJlc29sdmUsIHJlamVjdCwgdi5kb25lLCB2LnZhbHVlKTsgfSk7IH07IH1cclxuICAgIGZ1bmN0aW9uIHNldHRsZShyZXNvbHZlLCByZWplY3QsIGQsIHYpIHsgUHJvbWlzZS5yZXNvbHZlKHYpLnRoZW4oZnVuY3Rpb24odikgeyByZXNvbHZlKHsgdmFsdWU6IHYsIGRvbmU6IGQgfSk7IH0sIHJlamVjdCk7IH1cclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fbWFrZVRlbXBsYXRlT2JqZWN0KGNvb2tlZCwgcmF3KSB7XHJcbiAgICBpZiAoT2JqZWN0LmRlZmluZVByb3BlcnR5KSB7IE9iamVjdC5kZWZpbmVQcm9wZXJ0eShjb29rZWQsIFwicmF3XCIsIHsgdmFsdWU6IHJhdyB9KTsgfSBlbHNlIHsgY29va2VkLnJhdyA9IHJhdzsgfVxyXG4gICAgcmV0dXJuIGNvb2tlZDtcclxufTtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2ltcG9ydFN0YXIobW9kKSB7XHJcbiAgICBpZiAobW9kICYmIG1vZC5fX2VzTW9kdWxlKSByZXR1cm4gbW9kO1xyXG4gICAgdmFyIHJlc3VsdCA9IHt9O1xyXG4gICAgaWYgKG1vZCAhPSBudWxsKSBmb3IgKHZhciBrIGluIG1vZCkgaWYgKE9iamVjdC5oYXNPd25Qcm9wZXJ0eS5jYWxsKG1vZCwgaykpIHJlc3VsdFtrXSA9IG1vZFtrXTtcclxuICAgIHJlc3VsdC5kZWZhdWx0ID0gbW9kO1xyXG4gICAgcmV0dXJuIHJlc3VsdDtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9faW1wb3J0RGVmYXVsdChtb2QpIHtcclxuICAgIHJldHVybiAobW9kICYmIG1vZC5fX2VzTW9kdWxlKSA/IG1vZCA6IHsgZGVmYXVsdDogbW9kIH07XHJcbn1cclxuIiwiZXhwb3J0IGNvbnN0IEdhdHRTZXJ2aWNlcyA9IHtcbiAgR0VORVJJQ19BQ0NFU1M6IHtcbiAgICBTRVJWSUNFOiAnZ2VuZXJpY19hY2Nlc3MnLFxuICAgIERFVklDRV9OQU1FOiAnZGV2aWNlX25hbWUnLFxuICAgIEFQUEVBUkFOQ0U6ICdhcHBlYXJhbmNlJyxcbiAgICBQUklWQUNZX0ZMQUc6ICdwcml2YWN5X2ZsYWcnLFxuICAgIFJFQ09OTkVDVElPTl9BRERSRVNTOiAncmVjb25uZWN0aW9uX2FkZHJlc3MnLFxuICAgIFBFUklQSEVSQUxfUFJFRkVSUkVEX0NPTk5FQ1RJT05fUEFSQU1FVEVSUzogJ3BlcmlwaGVyYWxfcHJlZmVycmVkX2Nvbm5lY3Rpb25fcGFyYW1ldGVycydcbiAgfSxcbiAgQkFUVEVSWToge1xuICAgIFNFUlZJQ0U6ICdiYXR0ZXJ5X3NlcnZpY2UnLFxuICAgIEJBVFRFUllfTEVWRUw6ICdiYXR0ZXJ5X2xldmVsJ1xuICB9LFxuICBERVZJQ0VfSU5GT1JNQVRJT046IHtcbiAgICBTRVJWSUNFOiAnZGV2aWNlX2luZm9ybWF0aW9uJyxcbiAgICBNQU5VRkFDVFVSRVJfTkFNRTogJ21hbnVmYWN0dXJlcl9uYW1lX3N0cmluZycsXG4gICAgTU9ERUxfTlVNQkVSOiAnbW9kZWxfbnVtYmVyX3N0cmluZycsXG4gICAgU0VSSUFMX05VTUJFUjogJ3NlcmlhbF9udW1iZXJfc3RyaW5nJyxcbiAgICBIQVJEV0FSRV9SRVZJU0lPTjogJ2hhcmR3YXJlX3JldmlzaW9uX3N0cmluZycsXG4gICAgRklSTVdBUkVfUkVWSVNJT046ICdmaXJtd2FyZV9yZXZpc2lvbl9zdHJpbmcnLFxuICAgIFNPRlRXQVJFX1JFVklTSU9OOiAnc29mdHdhcmVfcmV2aXNpb25fc3RyaW5nJyxcbiAgICBTWVNURU1fSUQ6ICdzeXN0ZW1faWQnLFxuICAgIFBOUF9JRDogJ3BucF9pZCdcbiAgfVxufTtcblxuZXhwb3J0IGNvbnN0IEdBVFRfU0VSVklDRVMgPSBPYmplY3Qua2V5cyhHYXR0U2VydmljZXMpLm1hcChcbiAga2V5ID0+IEdhdHRTZXJ2aWNlc1trZXldLlNFUlZJQ0Vcbik7XG4iLCJkZWNsYXJlIGNvbnN0IEJ1ZmZlcjtcbmltcG9ydCAqIGFzIGFlcyBmcm9tICdhZXMtanMnO1xuaW1wb3J0IHsgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBDeXBoZXJBZXNTZXJ2aWNlIHtcbiAgcHJpdmF0ZSBtYXN0ZXJLZXkgPSBbXTtcbiAgcHJpdmF0ZSBpbml0aWFsVmVjdG9yID0gW107XG4gIHByaXZhdGUgZW5jcnlwdE1ldGhvZCA9ICdDQkMnO1xuICBwcml2YXRlIGlzU3RhdGljSW5pdGlhbFZlY3RvciA9IHRydWU7XG4gIHByaXZhdGUgZW5jdHJ5cE1ldGhvZEluc3RhbmNlO1xuICBwcml2YXRlIGlzQ29uZmlnRXhlY3V0ZWQgPSBmYWxzZTtcbiAgcHJpdmF0ZSBhZGRpdGlvbmFsRW5jcnlwdE1ldGhvZFBhcmFtcztcbiAgY29uc3RydWN0b3IoKSB7fVxuICAvKipcbiAgICogSW5pdGlhbCBjb25maWcgdXNlZCB0byBpbml0YWxpY2UgYWxsIHJlcXVpcmVkIHBhcmFtc1xuICAgKiBAcGFyYW0gbWFzdGVyS2V5IGtleSB1c2VkIHRvIGVuY3J5cHQgYW5kIGRlY3J5cHRcbiAgICogQHBhcmFtIGluaXRpYWxWZWN0b3IgdmVjdG9yIHVzZWQgdG8gZW5jcnlwdCBhYmQgZGVjcnlwdCBleGNlcHQgd2hlbiBFQ0IgZW5jcnlwdCBtZXRob2QgaXMgdXNlZFxuICAgKiBAcGFyYW0gZW5jcnlwdE1ldGhvZCB0eXBlIG9mIGVuY3J5cHQgbWV0aG9kIGlzIHVzZWQsIHRoZSBwb3NzaWJsZSBvcHRpb25zIGFyZTogQ0JDLCBDVFIsIENGQiwgT0ZCLCBFQ0JcbiAgICogQHBhcmFtIGFkZGl0aW9uYWxFbmNyeXB0TWV0aG9kUGFyYW1zIGNvbmZpZ3VyYXRpb24gcGFyYW1zIHVzZWQgYnkgdGhlIHNlbGVjdGVkIGVuY3J5cHQgbWV0aG9kLlxuICAgKiBOb3RlOiBpZiB0aGUgbWV0aG9kIENUUiBvciBDRkIgaXMgdXNlZCB0aGlzIHBhcmFtIGlzIHJlcXVpcmVkIG90aGVyd2lzZSBpcyBhbiBvcHRpbmFsIHBhcmFtLlxuICAgKiBCeSBDVFIgcmVxdWlyZSB0aGUgcGFyYW0gY291bnRlciBhbmQgYnkgQ0ZCIHJlcXVpcmUgdGhlIHBhcmFtIHNlZ21lbnRTaXplXG4gICAqIEBwYXJhbSBpc1N0YXRpY0luaXRpYWxWZWN0b3IgZGVmaW5lcyBpZiB0aGUgaW5pdGlhbCB2ZWN0b3IgaXMgY2hhbmdlZCBvciBub3Qgd2hlbiB0aGUgZGF0YSBhcmUgZW5jcnlwdGVkIG9yIG5vdFxuICAgKi9cbiAgY29uZmlnKFxuICAgIG1hc3RlcktleSxcbiAgICBpbml0aWFsVmVjdG9yID0gWzAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDBdLFxuICAgIGVuY3J5cHRNZXRob2QgPSAnQ0JDJyxcbiAgICBhZGRpdGlvbmFsRW5jcnlwdE1ldGhvZFBhcmFtcyA9IHt9LFxuICAgIGlzU3RhdGljSW5pdGlhbFZlY3RvciA9IHRydWVcbiAgKSB7XG4gICAgdGhpcy5pc0NvbmZpZ0V4ZWN1dGVkID0gdHJ1ZTtcbiAgICB0aGlzLm1hc3RlcktleSA9IG1hc3RlcktleTtcbiAgICB0aGlzLmluaXRpYWxWZWN0b3IgPSBpbml0aWFsVmVjdG9yO1xuICAgIHRoaXMuZW5jcnlwdE1ldGhvZCA9IGVuY3J5cHRNZXRob2Q7XG4gICAgdGhpcy5pc1N0YXRpY0luaXRpYWxWZWN0b3IgPSBpc1N0YXRpY0luaXRpYWxWZWN0b3I7XG4gICAgdGhpcy5hZGRpdGlvbmFsRW5jcnlwdE1ldGhvZFBhcmFtcyA9IGFkZGl0aW9uYWxFbmNyeXB0TWV0aG9kUGFyYW1zO1xuICAgIGlmICghaXNTdGF0aWNJbml0aWFsVmVjdG9yKSB7XG4gICAgICB0aGlzLmVuY3RyeXBNZXRob2RJbnN0YW5jZSA9IHRoaXMuZ2VuZXJhdGVFbmNyeXB0TWV0aG9kSW5zdGFuY2UoKTtcbiAgICB9XG4gIH1cbiAgLyoqXG4gICAqIEVuY3J5cHQgdGhlIGRhdGEgdXNpbmcgdGhlIGVuY3J5cHQgbWV0aG9kIHByZXZpb3VzbHkgY29uZmlndXJlZFxuICAgKiBAcGFyYW0gZGF0YUFycmF5QnVmZmVyIGRhdGEgdG8gZW5jcnlwdFxuICAgKi9cbiAgZW5jcnlwdChkYXRhQXJyYXlCdWZmZXI6IFVpbnQ4QXJyYXkgfCBVaW50MTZBcnJheSB8IFVpbnQzMkFycmF5KSB7XG4gICAgaWYgKCF0aGlzLmlzQ29uZmlnRXhlY3V0ZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ011c3QgY29uZmlndXJhdGUgY3lwaGVyLWFlcyBiZWZvcmUgY2FsbCB0aGlzIG1ldGhvZCwgdXNlIHRoZSBtZXRob2QgY29uZmlnKCknXG4gICAgICApO1xuICAgIH1cbiAgICBpZiAodGhpcy5lbmNyeXB0TWV0aG9kID09PSAnQ0JDJyB8fCB0aGlzLmVuY3J5cHRNZXRob2QgPT09ICdFQ0InKSB7XG4gICAgICBkYXRhQXJyYXlCdWZmZXIgPSB0aGlzLmFkZFBhZGRpbmcoZGF0YUFycmF5QnVmZmVyKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuaXNTdGF0aWNJbml0aWFsVmVjdG9yXG4gICAgICA/IHRoaXMuZ2VuZXJhdGVFbmNyeXB0TWV0aG9kSW5zdGFuY2UoKS5lbmNyeXB0KGRhdGFBcnJheUJ1ZmZlcilcbiAgICAgIDogdGhpcy5lbmN0cnlwTWV0aG9kSW5zdGFuY2UuZW5jcnlwdChkYXRhQXJyYXlCdWZmZXIpO1xuICB9XG4gIC8qKlxuICAgKiBEZWNyeXB0IHRoZSBkYXRhIHVzaW5nIHRoZSBlbmNyeXB0IG1ldGhvZCBwcmV2aW91c2x5IGNvbmZpZ3VyZWRcbiAgICogQHBhcmFtIGRhdGFBcnJheUJ1ZmZlciBkYXRhIHRvIGRlY3J5cHRcbiAgICovXG4gIGRlY3J5cHQoZGF0YUFycmF5QnVmZmVyOiBVaW50OEFycmF5IHwgVWludDE2QXJyYXkgfCBVaW50MzJBcnJheSkge1xuICAgIGlmICghdGhpcy5pc0NvbmZpZ0V4ZWN1dGVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdNdXN0IGNvbmZpZ3VyYXRlIGN5cGhlci1hZXMgYmVmb3JlIGNhbGwgdGhpcyBtZXRob2QsIHVzZSB0aGUgbWV0aG9kIGNvbmZpZygpJ1xuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuaXNTdGF0aWNJbml0aWFsVmVjdG9yXG4gICAgICA/IHRoaXMuZ2VuZXJhdGVFbmNyeXB0TWV0aG9kSW5zdGFuY2UoKS5kZWNyeXB0KGRhdGFBcnJheUJ1ZmZlcilcbiAgICAgIDogdGhpcy5lbmN0cnlwTWV0aG9kSW5zdGFuY2UuZGVjcnlwdChkYXRhQXJyYXlCdWZmZXIpO1xuICB9XG4gIC8qKlxuICAgKiBDaGFuZ2UgdGhlIGN1cnJlbnQgaW5pdGFsVmVjdG9yXG4gICAqIEBwYXJhbSBpbml0aWFsVmVjdG9yIG5ldyBpbml0YWxWZWN0b3JcbiAgICovXG4gIGNoYW5nZUluaXRpYWxWZWN0b3IoaW5pdGlhbFZlY3Rvcikge1xuICAgIGlmICghdGhpcy5pc1N0YXRpY0luaXRpYWxWZWN0b3IpIHtcbiAgICAgIHRoaXMuZW5jdHJ5cE1ldGhvZEluc3RhbmNlID0gdGhpcy5nZW5lcmF0ZUVuY3J5cHRNZXRob2RJbnN0YW5jZSgpO1xuICAgIH1cbiAgICB0aGlzLmluaXRpYWxWZWN0b3IgPSBpbml0aWFsVmVjdG9yO1xuICB9XG5cbiAgLyoqXG4gICAqIENoYW5nZSB0aGUgY3VycmVudCBlbmN5cHRNZXRob2RcbiAgICogQHBhcmFtIGVuY3J5cHRNZXRob2QgbmV3IGVuY3J5cHRNZXRob2RcbiAgICovXG4gIGNoYW5nZUVuY3J5cHRNZXRob2QoZW5jcnlwdE1ldGhvZCkge1xuICAgIGlmICghdGhpcy5pc1N0YXRpY0luaXRpYWxWZWN0b3IpIHtcbiAgICAgIHRoaXMuZW5jdHJ5cE1ldGhvZEluc3RhbmNlID0gdGhpcy5nZW5lcmF0ZUVuY3J5cHRNZXRob2RJbnN0YW5jZSgpO1xuICAgIH1cbiAgICB0aGlzLmVuY3J5cHRNZXRob2QgPSBlbmNyeXB0TWV0aG9kO1xuICB9XG5cbiAgLyoqXG4gICAqIENoYW5nZSB0aGUgY3VycmVudCBpc1N0YXRpY0luaXRpYWxWZWN0b3JcbiAgICogQHBhcmFtIGlzU3RhdGljSW5pdGlhbFZlY3RvciBuZXcgaXNTdGF0aWNJbml0YWxWZWN0b3JcbiAgICovXG4gIGNoYW5nZVN0YXRpY0luaXRpYWxWZWN0b3IoaXNTdGF0aWNJbml0aWFsVmVjdG9yKSB7XG4gICAgaWYgKCFpc1N0YXRpY0luaXRpYWxWZWN0b3IpIHtcbiAgICAgIHRoaXMuZW5jdHJ5cE1ldGhvZEluc3RhbmNlID0gdGhpcy5nZW5lcmF0ZUVuY3J5cHRNZXRob2RJbnN0YW5jZSgpO1xuICAgIH1cbiAgICB0aGlzLmlzU3RhdGljSW5pdGlhbFZlY3RvciA9IGlzU3RhdGljSW5pdGlhbFZlY3RvcjtcbiAgfVxuICAvKipcbiAgICogQ2hhbmdlIHRoZSBjdXJyZW50IG1hc3RlcktleVxuICAgKiBAcGFyYW0gbWFzdGVyS2V5IG5ldyBtYXN0ZXJLZXlcbiAgICovXG4gIGNoYW5nZU1hc3RlcktleShtYXN0ZXJLZXkpIHtcbiAgICBpZiAoIXRoaXMuaXNTdGF0aWNJbml0aWFsVmVjdG9yKSB7XG4gICAgICB0aGlzLmVuY3RyeXBNZXRob2RJbnN0YW5jZSA9IHRoaXMuZ2VuZXJhdGVFbmNyeXB0TWV0aG9kSW5zdGFuY2UoKTtcbiAgICB9XG4gICAgdGhpcy5tYXN0ZXJLZXkgPSBtYXN0ZXJLZXk7XG4gIH1cblxuICAvKipcbiAgICogQWRkIHBhZGRpbmcgdG8gdGhlIGxpc3RcbiAgICovXG4gIHByaXZhdGUgYWRkUGFkZGluZyhhcnJheUJ1ZmZlcjogVWludDhBcnJheSB8IFVpbnQxNkFycmF5IHwgVWludDMyQXJyYXkpIHtcbiAgICBjb25zdCBwYWRkaW5nTGVuZ3RoID0gTWF0aC5jZWlsKEFycmF5LmZyb20oYXJyYXlCdWZmZXIpLmxlbmd0aCAvIDE2KSAqIDE2O1xuICAgIGNvbnN0IHBhZGRpbmdMaXN0ID0gbmV3IEFycmF5KFxuICAgICAgcGFkZGluZ0xlbmd0aCAtIEFycmF5LmZyb20oYXJyYXlCdWZmZXIpLmxlbmd0aFxuICAgICkuZmlsbCgwKTtcbiAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoQXJyYXkuZnJvbShhcnJheUJ1ZmZlcikuY29uY2F0KHBhZGRpbmdMaXN0KSk7XG4gIH1cbiAgLyoqXG4gICAqIGdlbmVyYXRlIGEgaW5zdGFuY2Ugb2YgdGhlIGVuY3J5cHQgbWV0aG9kIHVzaW5nIHRoZSBjdXJyZW50IG1ldGhvZCBjb25maWd1cmVkXG4gICAqL1xuICBwcml2YXRlIGdlbmVyYXRlRW5jcnlwdE1ldGhvZEluc3RhbmNlKCkge1xuICAgIGxldCBlbmN0cnlwTWV0aG9kSW5zdGFuY2U7XG4gICAgc3dpdGNoICh0aGlzLmVuY3J5cHRNZXRob2QpIHtcbiAgICAgIGNhc2UgJ0NCQyc6XG4gICAgICAgIGVuY3RyeXBNZXRob2RJbnN0YW5jZSA9IG5ldyBhZXMuTW9kZU9mT3BlcmF0aW9uLmNiYyhcbiAgICAgICAgICB0aGlzLm1hc3RlcktleSxcbiAgICAgICAgICB0aGlzLmluaXRpYWxWZWN0b3JcbiAgICAgICAgKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdDVFInOlxuICAgICAgICBpZiAoIXRoaXMuYWRkaXRpb25hbEVuY3J5cHRNZXRob2RQYXJhbXMuY291bnRlcikge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICdhZGRpdGlvbmFsRW5jcnlwdE1ldGhvZFBhcmFtcy5jb3VudGVyIGlzIHJlcXVpcmVkIHRvIHVzZSBlbmNyeXB0IG1ldGhvZCBDVFInXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICBlbmN0cnlwTWV0aG9kSW5zdGFuY2UgPSBuZXcgYWVzLk1vZGVPZk9wZXJhdGlvbi5jdHIoXG4gICAgICAgICAgdGhpcy5tYXN0ZXJLZXksXG4gICAgICAgICAgdGhpcy5pbml0aWFsVmVjdG9yLFxuICAgICAgICAgIG5ldyBhZXMuQ291bnRlcih0aGlzLmFkZGl0aW9uYWxFbmNyeXB0TWV0aG9kUGFyYW1zLmNvdW50ZXIpXG4gICAgICAgICk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnQ0ZCJzpcbiAgICAgICAgaWYgKCF0aGlzLmFkZGl0aW9uYWxFbmNyeXB0TWV0aG9kUGFyYW1zLnNlZ21lbnRTaXplKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgJ2FkZGl0aW9uYWxFbmNyeXB0TWV0aG9kUGFyYW1zLnNlZ21lbnRTaXplIGlzIHJlcXVpcmVkIHRvIHVzZSBlbmNyeXB0IG1ldGhvZCBDRkInXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICBlbmN0cnlwTWV0aG9kSW5zdGFuY2UgPSBuZXcgYWVzLk1vZGVPZk9wZXJhdGlvbi5jZmIoXG4gICAgICAgICAgdGhpcy5tYXN0ZXJLZXksXG4gICAgICAgICAgdGhpcy5pbml0aWFsVmVjdG9yLFxuICAgICAgICAgIHRoaXMuYWRkaXRpb25hbEVuY3J5cHRNZXRob2RQYXJhbXMuc2VnbWVudFNpemVcbiAgICAgICAgKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdPRkInOlxuICAgICAgICBlbmN0cnlwTWV0aG9kSW5zdGFuY2UgPSBuZXcgYWVzLk1vZGVPZk9wZXJhdGlvbi5vZmIoXG4gICAgICAgICAgdGhpcy5tYXN0ZXJLZXksXG4gICAgICAgICAgdGhpcy5pbml0aWFsVmVjdG9yXG4gICAgICAgICk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnRUNCJzpcbiAgICAgICAgZW5jdHJ5cE1ldGhvZEluc3RhbmNlID0gbmV3IGFlcy5Nb2RlT2ZPcGVyYXRpb24uZWNiKHRoaXMubWFzdGVyS2V5KTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICAgIHJldHVybiBlbmN0cnlwTWV0aG9kSW5zdGFuY2U7XG4gIH1cbiAgLy8gI3JlZ2lvbiBVVElMU1xuICAvKipcbiAgICogQ29udmVydCB0aGUgdGV4dCB0byBieXRlc1xuICAgKiBAcGFyYW0gdGV4dCBUZXh0IHRvIGNvbnZlcnRcbiAgICovXG4gIHRleHRUb0J5dGVzKHRleHQpIHtcbiAgICByZXR1cm4gYWVzLnV0aWxzLnV0ZjgudG9CeXRlcyh0ZXh0KTtcbiAgfVxuICAvKipcbiAgICogQ29udmVydCB0aGUgYnl0ZXMgdG8gdGV4dFxuICAgKiBAcGFyYW0gYnl0ZXMgQnl0ZXMgdG8gY29udmVydFxuICAgKi9cbiAgYnl0ZXNUb1RleHQoYnl0ZXM6IFVpbnQ4QXJyYXkgfCBVaW50MTZBcnJheSB8IFVpbnQzMkFycmF5KSB7XG4gICAgcmV0dXJuIGFlcy51dGlscy51dGY4LmZyb21CeXRlcyhieXRlcyk7XG4gIH1cblxuICAvKipcbiAgICogQ29udmVydCB0aGUgYnl0ZXMgdG8gaGV4XG4gICAqIEBwYXJhbSBieXRlcyBieXRlcyB0byBjb252ZXJ0XG4gICAqL1xuICBieXRlc1RvaGV4KGJ5dGVzKSB7XG4gICAgcmV0dXJuIGFlcy51dGlscy5oZXguZnJvbUJ5dGVzKGJ5dGVzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb252ZXJ0IHRoZSBoZXggdG8gYnl0ZXNcbiAgICogQHBhcmFtIGhleCBIZXggdG8gY29udmVydFxuICAgKi9cbiAgaGV4VG9CeXRlcyhoZXgpIHtcbiAgICByZXR1cm4gYWVzLnV0aWxzLmhleC50b0J5dGVzKGhleCk7XG4gIH1cbiAgLy8gI2VuZHJlZ2lvblxuXG4gIGdlbmVyYXRlU3Via2V5cyhrZXkpIHtcbiAgICBjb25zdCBjb25zdF9aZXJvID0gbmV3IFVpbnQ4QXJyYXkoMTYpO1xuICAgIGNvbnN0IGNvbnN0X1JiID0gbmV3IEJ1ZmZlcignMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwODcnLCAnaGV4Jyk7XG4gICAgY29uc3QgZW5jdHJ5cE1ldGhvZEluc3RhbmNlID0gbmV3IGFlcy5Nb2RlT2ZPcGVyYXRpb24uY2JjKGtleSwgbmV3IFVpbnQ4QXJyYXkoMTYpKTtcblxuICAgIGNvbnN0IGxFbmNyeXB0ZWQgPSBlbmN0cnlwTWV0aG9kSW5zdGFuY2UuZW5jcnlwdChjb25zdF9aZXJvKTtcbiAgICBjb25zdCBsID0gbmV3IEJ1ZmZlcih0aGlzLmJ5dGVzVG9oZXgobEVuY3J5cHRlZCksICdoZXgnKTtcbiAgICBsZXQgc3Via2V5MSA9IHRoaXMuYml0U2hpZnRMZWZ0KGwpO1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1iaXR3aXNlXG4gICAgaWYgKGxbMF0gJiAweDgwKSB7XG4gICAgICBzdWJrZXkxID0gdGhpcy54b3Ioc3Via2V5MSwgY29uc3RfUmIpO1xuICAgIH1cblxuICAgIGxldCBzdWJrZXkyID0gdGhpcy5iaXRTaGlmdExlZnQoc3Via2V5MSk7XG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWJpdHdpc2VcbiAgICBpZiAoc3Via2V5MVswXSAmIDB4ODApIHtcbiAgICAgIHN1YmtleTIgPSB0aGlzLnhvcihzdWJrZXkyLCBjb25zdF9SYik7XG4gICAgfVxuXG4gICAgcmV0dXJuIHsgc3Via2V5MTogc3Via2V5MSwgc3Via2V5Mjogc3Via2V5MiB9O1xuICB9XG5cbiAgYWVzQ21hYyhrZXksIG1lc3NhZ2UpIHtcbiAgICBjb25zb2xlLmxvZygnSU5JQ0lBIENJRlJBRE8hISEhISEhISEhISEhISEhJyk7XG4gICAgY29uc3Qgc3Via2V5cyA9IHRoaXMuZ2VuZXJhdGVTdWJrZXlzKGtleSk7XG4gICAgbGV0IGJsb2NrQ291bnQgPSBNYXRoLmNlaWwobWVzc2FnZS5sZW5ndGggLyAxNik7XG4gICAgbGV0IGxhc3RCbG9ja0NvbXBsZXRlRmxhZywgbGFzdEJsb2NrLCBsYXN0QmxvY2tJbmRleDtcblxuICAgIGlmIChibG9ja0NvdW50ID09PSAwKSB7XG4gICAgICBibG9ja0NvdW50ID0gMTtcbiAgICAgIGxhc3RCbG9ja0NvbXBsZXRlRmxhZyA9IGZhbHNlO1xuICAgIH0gZWxzZSB7XG4gICAgICBsYXN0QmxvY2tDb21wbGV0ZUZsYWcgPSBtZXNzYWdlLmxlbmd0aCAlIDE2ID09PSAwO1xuICAgIH1cbiAgICBsYXN0QmxvY2tJbmRleCA9IGJsb2NrQ291bnQgLSAxO1xuXG4gICAgaWYgKGxhc3RCbG9ja0NvbXBsZXRlRmxhZykge1xuICAgICAgbGFzdEJsb2NrID0gdGhpcy54b3IoXG4gICAgICAgIHRoaXMuZ2V0TWVzc2FnZUJsb2NrKG1lc3NhZ2UsIGxhc3RCbG9ja0luZGV4KSxcbiAgICAgICAgc3Via2V5cy5zdWJrZXkxXG4gICAgICApO1xuICAgIH0gZWxzZSB7XG4gICAgICBsYXN0QmxvY2sgPSB0aGlzLnhvcihcbiAgICAgICAgdGhpcy5nZXRQYWRkZWRNZXNzYWdlQmxvY2sobWVzc2FnZSwgbGFzdEJsb2NrSW5kZXgpLFxuICAgICAgICBzdWJrZXlzLnN1YmtleTJcbiAgICAgICk7XG4gICAgfVxuXG4gICAgbGV0IHggPSBuZXcgQnVmZmVyKCcwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCcsICdoZXgnKTtcbiAgICBsZXQgeTtcblxuICAgIGxldCBlbmN0cnlwTWV0aG9kSW5zdGFuY2U7XG4gICAgZm9yIChsZXQgaW5kZXggPSAwOyBpbmRleCA8IGxhc3RCbG9ja0luZGV4OyBpbmRleCsrKSB7XG4gICAgICBlbmN0cnlwTWV0aG9kSW5zdGFuY2UgPSBuZXcgYWVzLk1vZGVPZk9wZXJhdGlvbi5jYmMoa2V5LCBuZXcgVWludDhBcnJheSgxNikpO1xuICAgICAgeSA9IHRoaXMueG9yKHgsIHRoaXMuZ2V0TWVzc2FnZUJsb2NrKG1lc3NhZ2UsIGluZGV4KSk7XG4gICAgICBjb25zdCB4RW5jcnlwdGVkID0gZW5jdHJ5cE1ldGhvZEluc3RhbmNlLmVuY3J5cHQoeSk7XG4gICAgICBjb25zb2xlLmxvZygnWCBub3JtYWwgPT09PT09PT09PT09PT09PiAnLCB0aGlzLmJ5dGVzVG9oZXgoeSkpO1xuICAgICAgY29uc29sZS5sb2coJ1ggZW5jcnlwdGVkID09PT09PT09PT09PT09PiAnLCB0aGlzLmJ5dGVzVG9oZXgoeEVuY3J5cHRlZCkpO1xuICAgICAgeCA9IG5ldyBCdWZmZXIodGhpcy5ieXRlc1RvaGV4KHhFbmNyeXB0ZWQpLCAnaGV4Jyk7XG4gICAgfVxuICAgIHkgPSB0aGlzLnhvcihsYXN0QmxvY2ssIHgpO1xuICAgIGVuY3RyeXBNZXRob2RJbnN0YW5jZSA9IG5ldyBhZXMuTW9kZU9mT3BlcmF0aW9uLmNiYyhrZXksIG5ldyBVaW50OEFycmF5KDE2KSk7XG4gICAgY29uc3QgeUVuY3J5cHRlZCA9IGVuY3RyeXBNZXRob2RJbnN0YW5jZS5lbmNyeXB0KHkpO1xuICAgIGNvbnNvbGUubG9nKCdZIG5vcm1hbCA9PT09PT09PT09PT09PT4gJywgdGhpcy5ieXRlc1RvaGV4KHkpKTtcbiAgICBjb25zb2xlLmxvZygnWSBlbmNyeXB0ZWQgPT09PT09PT09PT09PT0+ICcsIHRoaXMuYnl0ZXNUb2hleCh5RW5jcnlwdGVkKSk7XG4gICAgcmV0dXJuIHlFbmNyeXB0ZWQ7XG4gIH1cblxuICBnZXRNZXNzYWdlQmxvY2sobWVzc2FnZSwgYmxvY2tJbmRleCkge1xuICAgIGNvbnN0IGJsb2NrID0gbmV3IEJ1ZmZlcigxNik7XG4gICAgY29uc3Qgc3RhcnQgPSBibG9ja0luZGV4ICogMTY7XG4gICAgY29uc3QgZW5kID0gc3RhcnQgKyAxNjtcbiAgICBsZXQgYmxvY2tJID0gMDtcbiAgICBmb3IgKGxldCBpID0gc3RhcnQ7IGkgPCBlbmQ7IGkrKykge1xuICAgICAgYmxvY2tbYmxvY2tJXSA9IG1lc3NhZ2VbaV07XG4gICAgICBibG9ja0krKztcbiAgICB9XG4gICAgcmV0dXJuIGJsb2NrO1xuICB9XG5cbiAgZ2V0UGFkZGVkTWVzc2FnZUJsb2NrKG1lc3NhZ2UsIGJsb2NrSW5kZXgpIHtcbiAgICBjb25zdCBibG9jayA9IG5ldyBCdWZmZXIoMTYpO1xuICAgIGNvbnN0IHN0YXJ0ID0gYmxvY2tJbmRleCAqIDE2O1xuICAgIGNvbnN0IGVuZCA9IG1lc3NhZ2UubGVuZ3RoO1xuXG4gICAgYmxvY2suZmlsbCgwKTtcbiAgICBsZXQgYmxvY2tJID0gMDtcbiAgICBmb3IgKGxldCBpID0gc3RhcnQ7IGkgPCBlbmQ7IGkrKykge1xuICAgICAgYmxvY2tbYmxvY2tJXSA9IG1lc3NhZ2VbaV07XG4gICAgICBibG9ja0krKztcbiAgICB9XG4gICAgYmxvY2tbZW5kIC0gc3RhcnRdID0gMHg4MDtcblxuICAgIHJldHVybiBibG9jaztcbiAgfVxuXG4gIGJpdFNoaWZ0TGVmdChidWZmZXIpIHtcbiAgICBjb25zdCBzaGlmdGVkID0gbmV3IEJ1ZmZlcihidWZmZXIubGVuZ3RoKTtcbiAgICBjb25zdCBsYXN0ID0gYnVmZmVyLmxlbmd0aCAtIDE7XG4gICAgZm9yIChsZXQgaW5kZXggPSAwOyBpbmRleCA8IGxhc3Q7IGluZGV4KyspIHtcbiAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1iaXR3aXNlXG4gICAgICBzaGlmdGVkW2luZGV4XSA9IGJ1ZmZlcltpbmRleF0gPDwgMTtcbiAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1iaXR3aXNlXG4gICAgICBpZiAoYnVmZmVyW2luZGV4ICsgMV0gJiAweDgwKSB7XG4gICAgICAgIHNoaWZ0ZWRbaW5kZXhdICs9IDB4MDE7XG4gICAgICB9XG4gICAgfVxuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1iaXR3aXNlXG4gICAgc2hpZnRlZFtsYXN0XSA9IGJ1ZmZlcltsYXN0XSA8PCAxO1xuICAgIHJldHVybiBzaGlmdGVkO1xuICB9XG5cbiAgeG9yKGJ1ZmZlckEsIGJ1ZmZlckIpIHtcbiAgICBjb25zdCBsZW5ndGggPSBNYXRoLm1pbihidWZmZXJBLmxlbmd0aCwgYnVmZmVyQi5sZW5ndGgpO1xuICAgIGNvbnN0IG91dHB1dCA9IG5ldyBCdWZmZXIobGVuZ3RoKTtcbiAgICBmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgbGVuZ3RoOyBpbmRleCsrKSB7XG4gICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYml0d2lzZVxuICAgICAgb3V0cHV0W2luZGV4XSA9IGJ1ZmZlckFbaW5kZXhdIF4gYnVmZmVyQltpbmRleF07XG4gICAgfVxuICAgIHJldHVybiBvdXRwdXQ7XG4gIH1cbn1cbiIsImltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuZXhwb3J0IGludGVyZmFjZSBMb2dnZXIge1xuICBsb2coYXJnczogc3RyaW5nW10pOiB2b2lkO1xuICBlcnJvcihhcmdzOiBzdHJpbmdbXSk6IHZvaWQ7XG4gIHdhcm4oYXJnczogc3RyaW5nW10pOiB2b2lkO1xufVxuXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBDb25zb2xlTG9nZ2VyU2VydmljZSBpbXBsZW1lbnRzIExvZ2dlciB7XG4gIGxvZyguLi5hcmdzKSB7XG4gICAgY29uc29sZS5sb2cuYXBwbHkoY29uc29sZSwgYXJncyk7XG4gIH1cbiAgZXJyb3IoLi4uYXJncykge1xuICAgIGNvbnNvbGUuZXJyb3IuYXBwbHkoY29uc29sZSwgYXJncyk7XG4gIH1cbiAgd2FybiguLi5hcmdzKSB7XG4gICAgY29uc29sZS53YXJuLmFwcGx5KGNvbnNvbGUsIGFyZ3MpO1xuICB9XG59XG5cbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnXG59KVxuZXhwb3J0IGNsYXNzIE5vTG9nZ2VyU2VydmljZSBpbXBsZW1lbnRzIExvZ2dlciB7XG4gIGxvZyguLi5hcmdzKSB7fVxuICBlcnJvciguLi5hcmdzKSB7fVxuICB3YXJuKC4uLmFyZ3MpIHt9XG59XG4iLCJpbXBvcnQgeyBJbmplY3RhYmxlLCBFdmVudEVtaXR0ZXIgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7XG4gIG1hcCxcbiAgbWVyZ2VNYXAsXG4gIGNvbmNhdCxcbiAgbWFwVG8sXG4gIGZpbHRlcixcbiAgdGFrZVVudGlsLFxuICBidWZmZXJUb2dnbGUsXG4gIHNjYW4sXG4gIHRhcCxcbiAgdGFrZSxcbiAgdGltZW91dCxcbiAgcmV0cnlXaGVuLFxuICBkZWxheVxufSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQge1xuICBPYnNlcnZhYmxlLFxuICBmb3JrSm9pbixcbiAgU3ViamVjdCxcbiAgZGVmZXIsXG4gIGZyb21FdmVudCxcbiAgb2YsXG4gIGZyb20sXG4gIGludGVydmFsXG59IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgR2F0dFNlcnZpY2VzIH0gZnJvbSAnLi9nYXR0LXNlcnZpY2VzJztcbmltcG9ydCB7IEJyb3dzZXJXZWJCbHVldG9vdGggfSBmcm9tICcuLi9wbGF0Zm9ybS9icm93c2VyJztcbmltcG9ydCB7IEN5cGhlckFlc1NlcnZpY2UgfSBmcm9tICcuLi9jeXBoZXIvY3lwaGVyLWFlcy5zZXJ2aWNlJztcbmltcG9ydCB7IENvbnNvbGVMb2dnZXJTZXJ2aWNlIH0gZnJvbSAnLi4vbG9nZ2VyLnNlcnZpY2UnO1xuXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBCbHVldG9vdGhTZXJ2aWNlIGV4dGVuZHMgU3ViamVjdDxCbHVldG9vdGhTZXJ2aWNlPiB7XG4gIHB1YmxpYyBfZGV2aWNlJDogRXZlbnRFbWl0dGVyPEJsdWV0b290aERldmljZT47XG4gIHByaXZhdGUgZGV2aWNlOiBCbHVldG9vdGhEZXZpY2U7XG4gIHByaXZhdGUgc2VydmljZUNoYXJhY3RlcmlzdGljVnNTdWJzY3JpcHRpb25MaXN0ID0ge307XG4gIHByaXZhdGUgbm90aWZpZXJTdWJqZWN0ID0gbmV3IFN1YmplY3QoKTtcbiAgcHJpdmF0ZSBub3RpZmllclN0YXJ0ZWRTdWJqZWN0ID0gbmV3IFN1YmplY3QoKTtcbiAgcHJpdmF0ZSBibHVldG9vdGhBdmFpbGFibGUgPSBmYWxzZTtcbiAgY29uc3RydWN0b3IoXG4gICAgcHVibGljIF93ZWJCbGU6IEJyb3dzZXJXZWJCbHVldG9vdGgsXG4gICAgcHJpdmF0ZSBjeXBoZXJBZXNTZXJ2aWNlOiBDeXBoZXJBZXNTZXJ2aWNlLFxuICAgIHB1YmxpYyBfY29uc29sZTogQ29uc29sZUxvZ2dlclNlcnZpY2VcbiAgKSB7XG4gICAgc3VwZXIoKTtcbiAgICB0aGlzLl9kZXZpY2UkID0gbmV3IEV2ZW50RW1pdHRlcjxCbHVldG9vdGhEZXZpY2U+KCk7XG4gICAgaWYgKF93ZWJCbGUuX2JsZSkge1xuICAgICAgdGhpcy5ibHVldG9vdGhBdmFpbGFibGUgPSB0cnVlO1xuICAgIH1cbiAgfVxuXG4gIGlzQmx1ZXRvb3RoQXZhaWxhYmxlKCkge1xuICAgIHJldHVybiB0aGlzLmJsdWV0b290aEF2YWlsYWJsZTtcbiAgfVxuICAvKipcbiAgICogZ2V0IHRoZSBjdXJyZW50IGRldmljZSwgaWYgdGhlIGRldmljZSByZXR1cm4gbnVsbCBpcyBiZWNhdXNlIHRoZSBjb25uZWN0aW9uIGhhcyBsb3N0XG4gICAqIEByZXR1cm5zIHRoZSBjdXJyZW50IGNvbm5jZXRlZCBkZXZpY2VcbiAgICovXG4gIGdldERldmljZSQoKTogT2JzZXJ2YWJsZTxCbHVldG9vdGhEZXZpY2U+IHtcbiAgICByZXR1cm4gdGhpcy5fZGV2aWNlJDtcbiAgfVxuXG4gIGdldE5vdGlmaWVyU3RhcnRlZFN1YmplY3QkKCkge1xuICAgIHJldHVybiB0aGlzLm5vdGlmaWVyU3RhcnRlZFN1YmplY3Q7XG4gIH1cbiAgLyoqXG4gICAqIHN0YXJ0IGEgc3RyZWFtIGJ5IG5vdGlmaWVycyBjaGFyYWN0ZXJpc3RpY3NcbiAgICogQHBhcmFtIHNlcnZpY2UgVGhlIHNlcnZpY2UgdG8gd2hpY2ggdGhlIGNoYXJhY3RlcmlzdGljIGJlbG9uZ3NcbiAgICogQHBhcmFtIGNoYXJhY3RlcmlzdGljIFRoZSBjaGFyYWN0ZXJpc3RpYyB3aG9zZSB2YWx1ZSB5b3Ugd2FudCB0byBsaXN0ZW5cbiAgICogQHBhcmFtIG9wdGlvbnMgb2JqZWN0IHRoYXQgY29udGFpbnMgdGhlXG4gICAqIHN0YXJ0Qnl0ZTpudW1iZXIgKHJlcXVpcmVkKSwgc3RvcEJ5dGU6bnVtYmVyIChyZXF1aXJlZCksXG4gICAqIGxlbmd0aFBvc2l0aW9uOiB7IHN0YXJ0OiBudW1iZXIsIGVuZDogbnVtYmVyLCBsZW5ndGhQYWRkaW5nOiBudW1iZXIgfSAocmVxdWlyZWQpXG4gICAqIEByZXR1cm5zIEEgRGF0YVZpZXcgdGhhbiBjb250YWlucyB0aGUgY2hhcmFjdGVyaXN0aWMgdmFsdWVcbiAgICovXG4gIHN0YXJ0Tm90aWZpZXJMaXN0ZW5lciQoc2VydmljZSwgY2hhcmFjdGVyaXN0aWMsIG9wdGlvbnMpIHtcbiAgICByZXR1cm4gZGVmZXIoKCkgPT4ge1xuICAgICAgb2Yoc2VydmljZSlcbiAgICAgICAgLnBpcGUoXG4gICAgICAgICAgdGFwKCgpID0+IHRoaXMuX2NvbnNvbGUubG9nKCdJbmljaWEgZWwgbm90aWZpZXIgY29uIGxhIGluc3RhbmNpYTogJywgdGhpcy5zZXJ2aWNlQ2hhcmFjdGVyaXN0aWNWc1N1YnNjcmlwdGlvbkxpc3QpKSxcbiAgICAgICAgICBmaWx0ZXIoXG4gICAgICAgICAgICBfID0+XG4gICAgICAgICAgICAgIE9iamVjdC5rZXlzKHRoaXMuc2VydmljZUNoYXJhY3RlcmlzdGljVnNTdWJzY3JpcHRpb25MaXN0KS5pbmRleE9mKFxuICAgICAgICAgICAgICAgIGAke3NlcnZpY2V9LSR7Y2hhcmFjdGVyaXN0aWN9YFxuICAgICAgICAgICAgICApID09PSAtMVxuICAgICAgICAgIClcbiAgICAgICAgKVxuICAgICAgICAuc3Vic2NyaWJlKFxuICAgICAgICAgICgpID0+IHtcbiAgICAgICAgICAgIHRoaXMuc2VydmljZUNoYXJhY3RlcmlzdGljVnNTdWJzY3JpcHRpb25MaXN0W2Ake3NlcnZpY2V9LSR7Y2hhcmFjdGVyaXN0aWN9YF0gPVxuICAgICAgICAgICAgICB0aGlzLmJ1aWxkTm90aWZpZXJMaXN0ZW5lciQoXG4gICAgICAgICAgICAgICAgc2VydmljZSxcbiAgICAgICAgICAgICAgICBjaGFyYWN0ZXJpc3RpYyxcbiAgICAgICAgICAgICAgICBvcHRpb25zXG4gICAgICAgICAgICAgICkuc3Vic2NyaWJlKG1lc3NhZ2UgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMubm90aWZpZXJTdWJqZWN0Lm5leHQobWVzc2FnZSk7XG4gICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgfSxcbiAgICAgICAgICBlcnIgPT4ge1xuICAgICAgICAgICAgdGhpcy5fY29uc29sZS5sb2coJ1tCTEU6OkluZm9dIEVycm9yIGluIG5vdGlmaWVyOiAnLCBlcnIpO1xuICAgICAgICAgIH0sXG4gICAgICAgICAgKCkgPT4geyB9XG4gICAgICApO1xuICAgICAgcmV0dXJuIG9mKGBub3RpZmllciBhcyBzdWJzY3JpYmVkOiBzZXJ2aWNlPSAke3NlcnZpY2V9LCBjaGFyYWN0ZXJpc3RpYz0gJHtjaGFyYWN0ZXJpc3RpY31gKTtcbiAgICB9KTtcblxuICB9XG5cbiAgc3RvcE5vdGlmaWVyTGlzdGVuZXIkKHNlcnZpY2UsIGNoYXJhY3RlcmlzdGljKSB7XG4gICAgcmV0dXJuIGRlZmVyKCgpID0+IHtcbiAgICAgIC8vIHRoaXMuc2VydmljZUNoYXJhY3RlcmlzdGljVnNTdWJzY3JpcHRpb25MaXN0W2Ake3NlcnZpY2V9LSR7Y2hhcmFjdGVyaXN0aWN9YF0udW5zdWJzY3JpYmUoKTtcbiAgICAgIGRlbGV0ZSB0aGlzLnNlcnZpY2VDaGFyYWN0ZXJpc3RpY1ZzU3Vic2NyaXB0aW9uTGlzdFtgJHtzZXJ2aWNlfS0ke2NoYXJhY3RlcmlzdGljfWBdO1xuICAgICAgcmV0dXJuIG9mKGB0aGUgbm90aWZpZXIgb2YgdGhlIGNoYXJhY3RlcmlzdGljICR7Y2hhcmFjdGVyaXN0aWN9IGFzIGJlZW4gc3RvcHBlZGApO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBidWlsZE5vdGlmaWVyTGlzdGVuZXIkKHNlcnZpY2UsIGNoYXJhY3RlcmlzdGljLCBvcHRpb25zKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0UHJpbWFyeVNlcnZpY2UkKHNlcnZpY2UpLnBpcGUoXG4gICAgICB0YXAoc2VydmljZUluc3RhbmNlID0+IHRoaXMuX2NvbnNvbGUubG9nKGB0b21hIGV4aXRvc2FtZW50ZSBlbCBzZXJ2aWNpbyA9PT09PT09PT09PT09PT09PiAke3NlcnZpY2VJbnN0YW5jZX1gKSksXG4gICAgICBtZXJnZU1hcChwcmltYXJ5U2VydmljZSA9PlxuICAgICAgICB0aGlzLmdldENoYXJhY3RlcmlzdGljJChwcmltYXJ5U2VydmljZSwgY2hhcmFjdGVyaXN0aWMpXG4gICAgICAgIC5waXBlKHRhcChjaGFyID0+IHRoaXMuX2NvbnNvbGUubG9nKGB0b21hIGV4aXRvc2FtZW50ZSBsYSBjYXJhY3RlcmlzdGljYSA9PT09PT09PT09PT09PT09PiAke2NoYXJ9YCkpKVxuICAgICAgKSxcbiAgICAgIG1lcmdlTWFwKChjaGFyOiBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWMpID0+IHtcbiAgICAgICAgcmV0dXJuIGRlZmVyKCgpID0+IHtcbiAgICAgICAgICAvLyBlbmFibGUgdGhlIGNoYXJhY3RlcmlzdGljIG5vdGlmaWVyXG4gICAgICAgICAgcmV0dXJuIGNoYXIuc3RhcnROb3RpZmljYXRpb25zKCk7XG4gICAgICAgIH0pLnBpcGUoXG4gICAgICAgICAgcmV0cnlXaGVuKGVycm9yID0+XG4gICAgICAgICAgICBlcnJvci5waXBlKFxuICAgICAgICAgICAgICB0YXAoKCkgPT4gdGhpcy5fY29uc29sZS5sb2coJ0VSUk9SIEVOIEVMIHN0YXJ0Tm90aWZpY2F0aW9ucyA9PT09PT09PT09PT09PT09PiAnKSksXG4gICAgICAgICAgICAgIGRlbGF5KDEwMDApLFxuICAgICAgICAgICAgICB0YWtlKDUpXG4gICAgICAgICAgICApXG4gICAgICAgICAgKSxcbiAgICAgICAgICB0YXAoKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5ub3RpZmllclN0YXJ0ZWRTdWJqZWN0Lm5leHQodHJ1ZSk7XG4gICAgICAgICAgICB0aGlzLl9jb25zb2xlLmxvZyhgaW5jaWEgbGFzIG5vdGlmaWFjaW9uZXMgZGUgbGEgY2FyYWN0ZXJpc3RpY2EgMiA9PT09PT09PT09PT09PT09PiAke2NoYXJhY3RlcmlzdGljfWApO1xuICAgICAgICAgIH0pLFxuICAgICAgICAgIG1lcmdlTWFwKF8gPT4ge1xuICAgICAgICAgICAgLy8gc3RhcnQgdGhlIGxpc3RlciBmcm9tIHRoZSBldmVuIGNoYXJhY3RlcmlzdGljdmFsdWVjaGFuZ2VkIHRvIGdldCBhbGwgY2hhbmdlcyBvbiB0aGUgc3BlY2lmaWNcbiAgICAgICAgICAgIC8vIGNoYXJhY3RlcmlzdGljXG4gICAgICAgICAgICByZXR1cm4gZnJvbUV2ZW50KGNoYXIsICdjaGFyYWN0ZXJpc3RpY3ZhbHVlY2hhbmdlZCcpLnBpcGUoXG4gICAgICAgICAgICAgIHRha2VVbnRpbChmcm9tRXZlbnQoY2hhciwgJ2dhdHRzZXJ2ZXJkaXNjb25uZWN0ZWQnKSksXG4gICAgICAgICAgICAgIG1hcCgoZXZlbnQ6IEV2ZW50KSA9PiB7XG4gICAgICAgICAgICAgICAgLy8gZ2V0IGEgZXZlbnQgZnJvbSB0aGUgY2hhcmFjdGVyaXN0aWMgYW5kIG1hcCB0aGF0XG4gICAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICAgIHN0YXJ0Qnl0ZU1hdGNoZXM6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgc3RvcEJ5dGVNYXRjaGVzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgIGxlbmd0aE1hdGNoZXM6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgbWVzc2FnZUxlbmd0aDogMCxcbiAgICAgICAgICAgICAgICAgIHRpbWVzdGFtcDogRGF0ZS5ub3coKSxcbiAgICAgICAgICAgICAgICAgIGRhdGE6IEFycmF5LmZyb20oXG4gICAgICAgICAgICAgICAgICAgIG5ldyBVaW50OEFycmF5KFxuICAgICAgICAgICAgICAgICAgICAgICgoZXZlbnQudGFyZ2V0IGFzIEJsdWV0b290aFJlbW90ZUdBVFRDaGFyYWN0ZXJpc3RpYylcbiAgICAgICAgICAgICAgICAgICAgICAgIC52YWx1ZSBhcyBEYXRhVmlldykuYnVmZmVyXG4gICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICB9KSxcbiAgICAgICAgICAgICAgc2NhbihcbiAgICAgICAgICAgICAgICAoYWNjLCB2YWx1ZSkgPT4ge1xuICAgICAgICAgICAgICAgICAgYWNjLnRpbWVzdGFtcCA9IGFjYy50aW1lc3RhbXAgPT09IDAgPyB2YWx1ZS50aW1lc3RhbXAgOiBhY2MudGltZXN0YW1wO1xuICAgICAgICAgICAgICAgICAgLy8gaWYgdGhlIGN1cnJlbnQgYWNjdW11bGF0b3IgdmFsdWUgaXMgYSB2YWxpZCBtZXNzYWdlLCB0aGVuIGlzIHJlc3RhcnRlZCB0byBnZXQgdGhlIG5leHRcbiAgICAgICAgICAgICAgICAgIC8vIG1lc3NhZ2VcbiAgICAgICAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICAgICAgKGFjYy5sZW5ndGhNYXRjaGVzICYmXG4gICAgICAgICAgICAgICAgICAgICAgYWNjLnN0YXJ0Qnl0ZU1hdGNoZXMgJiZcbiAgICAgICAgICAgICAgICAgICAgICBhY2Muc3RvcEJ5dGVNYXRjaGVzKVxuICAgICAgICAgICAgICAgICAgICB8fCBhY2MudGltZXN0YW1wICsgMTAwMCA8IERhdGUubm93KClcbiAgICAgICAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgICAgICBhY2MgPSB7XG4gICAgICAgICAgICAgICAgICAgICAgc3RhcnRCeXRlTWF0Y2hlczogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgICAgc3RvcEJ5dGVNYXRjaGVzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgICBsZW5ndGhNYXRjaGVzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlTGVuZ3RoOiAwLFxuICAgICAgICAgICAgICAgICAgICAgIHRpbWVzdGFtcDogMCxcbiAgICAgICAgICAgICAgICAgICAgICBkYXRhOiBbXVxuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgLy8gdmFsaWRhdGUgdGhlIHN0YXJ0IGJ5dGVcbiAgICAgICAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICAgICAgIWFjYy5zdGFydEJ5dGVNYXRjaGVzICYmXG4gICAgICAgICAgICAgICAgICAgIHZhbHVlLmRhdGFbMF0gPT09IG9wdGlvbnMuc3RhcnRCeXRlXG4gICAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gZ2V0IHRoZSBtZXNzYWdlIGxlbmd0aCB1c2luZyB0aGUgc3RhcnQgYW5kIGVuZCBwb3NpdGlvblxuICAgICAgICAgICAgICAgICAgICBhY2MubWVzc2FnZUxlbmd0aCA9XG4gICAgICAgICAgICAgICAgICAgICAgbmV3IERhdGFWaWV3KFxuICAgICAgICAgICAgICAgICAgICAgICAgbmV3IFVpbnQ4QXJyYXkoXG4gICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlLmRhdGEuc2xpY2UoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgb3B0aW9ucy5sZW5ndGhQb3NpdGlvbi5zdGFydCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcHRpb25zLmxlbmd0aFBvc2l0aW9uLmVuZFxuICAgICAgICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICAgICAgICApLmJ1ZmZlclxuICAgICAgICAgICAgICAgICAgICAgICkuZ2V0SW50MTYoMCwgZmFsc2UpICtcbiAgICAgICAgICAgICAgICAgICAgICAob3B0aW9ucy5sZW5ndGhQb3NpdGlvbi5sZW5ndGhQYWRkaW5nXG4gICAgICAgICAgICAgICAgICAgICAgICA/IG9wdGlvbnMubGVuZ3RoUG9zaXRpb24ubGVuZ3RoUGFkZGluZ1xuICAgICAgICAgICAgICAgICAgICAgICAgOiAwKTtcbiAgICAgICAgICAgICAgICAgICAgLy8gdmFsaWQgdGhhdCB0aGUgaW5pdGlhbCBieXRlIHdhcyBmb3VuZFxuICAgICAgICAgICAgICAgICAgICBhY2Muc3RhcnRCeXRlTWF0Y2hlcyA9IHRydWU7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICAgICAgICFhY2Muc3RvcEJ5dGVNYXRjaGVzICYmXG4gICAgICAgICAgICAgICAgICAgIHZhbHVlLmRhdGFbdmFsdWUuZGF0YS5sZW5ndGggLSAxXSA9PT0gb3B0aW9ucy5zdG9wQnl0ZVxuICAgICAgICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIHZhbGlkIHRoYXQgdGhlIGVuZCBieXRlIHdhcyBmb3VuZFxuICAgICAgICAgICAgICAgICAgICBhY2Muc3RvcEJ5dGVNYXRjaGVzID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIGlmIChhY2Muc3RhcnRCeXRlTWF0Y2hlcykge1xuICAgICAgICAgICAgICAgICAgICAvLyBtZXJnZSB0aGUgbmV3IGRhdGEgYnl0ZXMgdG8gdGhlIG9sZCBieXRlc1xuICAgICAgICAgICAgICAgICAgICBhY2MuZGF0YSA9IGFjYy5kYXRhLmNvbmNhdCh2YWx1ZS5kYXRhKTtcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIGFjYy5sZW5ndGhNYXRjaGVzID1cbiAgICAgICAgICAgICAgICAgICAgYWNjLnN0YXJ0Qnl0ZU1hdGNoZXMgJiZcbiAgICAgICAgICAgICAgICAgICAgYWNjLnN0b3BCeXRlTWF0Y2hlcyAmJlxuICAgICAgICAgICAgICAgICAgICBhY2MubWVzc2FnZUxlbmd0aCA9PT0gYWNjLmRhdGEubGVuZ3RoO1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIGFjYztcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgIHN0YXJ0Qnl0ZU1hdGNoZXM6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgc3RvcEJ5dGVNYXRjaGVzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgIGxlbmd0aE1hdGNoZXM6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgbWVzc2FnZUxlbmd0aDogMCxcbiAgICAgICAgICAgICAgICAgIHRpbWVzdGFtcDogMCxcbiAgICAgICAgICAgICAgICAgIGRhdGE6IFtdXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAvLyBvbmx5IHB1Ymxpc2ggdGhlIGNvbXBsZXRlIGFuZCB2YWxpZCBtZXNzYWdlXG4gICAgICAgICAgICAgIGZpbHRlcihcbiAgICAgICAgICAgICAgICBkYXRhID0+XG4gICAgICAgICAgICAgICAgICBkYXRhLmxlbmd0aE1hdGNoZXMgJiZcbiAgICAgICAgICAgICAgICAgIGRhdGEuc3RhcnRCeXRlTWF0Y2hlcyAmJlxuICAgICAgICAgICAgICAgICAgZGF0YS5zdG9wQnl0ZU1hdGNoZXNcbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgLy8gcmVtb3ZlIGFsbCBjdXN0b20gZGF0YSBhbmQgcHVibGlzaCB0aGUgbWVzc2FnZSBkYXRhXG4gICAgICAgICAgICAgIG1hcChyZXN1bHQgPT4gcmVzdWx0LmRhdGEpXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH0pXG4gICAgICAgICk7XG4gICAgICB9KVxuICAgICk7XG4gIH1cbiAgLyoqXG4gICAqIFNlbmQgYSByZXF1ZXN0IHRvIHRoZSBkZXZpY2UgYW5kIHdhaXQgYSB1bmlxdWUgcmVzcG9uc2VcbiAgICogQHBhcmFtIG1lc3NhZ2UgTWVzc2FnZSB0byBzZW5kXG4gICAqIEBwYXJhbSBzZXJ2aWNlIFRoZSBzZXJ2aWNlIHRvIHdoaWNoIHRoZSBjaGFyYWN0ZXJpc3RpYyBiZWxvbmdzXG4gICAqIEBwYXJhbSBjaGFyYWN0ZXJpc3RpYyBUaGUgY2hhcmFjdGVyaXN0aWMgd2hvc2UgdmFsdWUgeW91IHdhbnQgdG8gc2VuZCB0aGUgbWVzc2FnZVxuICAgKiBAcGFyYW0gcmVzcG9uc2VUeXBlIGZpbHRlciB0byB1c2UgdG8gaWRlbnRpZnkgdGhlIHJlc3BvbnNlLCBTYW1wbGU6IFt7cG9zaXRpb246IDMsIGJ5dGVUb01hdGNoOiAweDgzfSxcbiAgICogIHtwb3NpdGlvbjogMTMsIGJ5dGVUb01hdGNoOiAweDQ1fV1cbiAgICogQHBhcmFtIGN5cGhlck1hc3RlcktleSBtYXN0ZXIga2V5IHRvIGRlY3J5cHQgdGhlIG1lc3NhZ2UsIG9ubHkgdXNlIHRoaXMgcGFyYSBpZiB0aGUgbWVzc2FnZSB0byByZWNlaXZlIGlzIGVuY3J5cHRlZFxuICAgKi9cbiAgc2VuZEFuZFdhaXRSZXNwb25zZSQoXG4gICAgbWVzc2FnZSxcbiAgICBzZXJ2aWNlLFxuICAgIGNoYXJhY3RlcmlzdGljLFxuICAgIHJlc3BvbnNlVHlwZSxcbiAgICBjeXBoZXJNYXN0ZXJLZXk/XG4gICkge1xuICAgIHRoaXMuX2NvbnNvbGUubG9nKFxuICAgICAgJ1tCTEU6OkluZm9dIFNlbmQgbWVzc2FnZSB0byBkZXZpY2U6ICcsXG4gICAgICB0aGlzLmN5cGhlckFlc1NlcnZpY2UuYnl0ZXNUb2hleChtZXNzYWdlKVxuICAgICk7XG4gICAgcmV0dXJuIGZvcmtKb2luKFxuICAgICAgdGhpcy5zdWJzY3JpYmVUb05vdGlmaWVyTGlzdGVuZXIocmVzcG9uc2VUeXBlLCBjeXBoZXJNYXN0ZXJLZXkpLnBpcGUoXG4gICAgICAgIHRha2UoMSlcbiAgICAgICksXG4gICAgICB0aGlzLnNlbmRUb05vdGlmaWVyJChtZXNzYWdlLCBzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYylcbiAgICApLnBpcGUoXG4gICAgICBtYXAoKFttZXNzYWdlUmVzcCwgX10pID0+IG1lc3NhZ2VSZXNwKSxcbiAgICAgIHRpbWVvdXQoMzAwMClcbiAgICApO1xuICB9XG4gIC8qKlxuICAgKiBTdWJzY3JpYmUgdG8gdGhlIG5vdGlmaWVycyBmaWx0ZXJpbmcgYnkgYnl0ZSBjaGVja2luZ1xuICAgKiBAcGFyYW0gZmlsdGVyT3B0aW9ucyBtdXN0IHNwZWNpZmljIHRoZSBwb3NpdGlvbiBhbmQgdGhlIGJ5dGUgdG8gbWF0Y2ggU2FtcGxlOlxuICAgKiBbe3Bvc2l0aW9uOiAzLCBieXRlVG9NYXRjaDogMHg4M30sIHtwb3NpdGlvbjogMTMsIGJ5dGVUb01hdGNoOiAweDQ1fV1cbiAgICogQHBhcmFtIGN5cGhlck1hc3RlcktleSBtYXN0ZXIga2V5IHRvIGRlY3J5cHQgdGhlIG1lc3NhZ2UsIG9ubHkgdXNlIHRoaXMgcGFyYSBpZiB0aGUgbWVzc2FnZSB0byByZWNlaXZlIGlzIGVuY3J5cHRlZFxuICAgKi9cbiAgc3Vic2NyaWJlVG9Ob3RpZmllckxpc3RlbmVyKGZpbHRlck9wdGlvbnMsIGN5cGhlck1hc3RlcktleT8pIHtcbiAgICByZXR1cm4gdGhpcy5ub3RpZmllclN1YmplY3QucGlwZShcbiAgICAgIG1hcChtZXNzYWdlVW5mb3JtYXRlZCA9PiB7XG4gICAgICAgIC8vIHJlY2VpdmUgdGhlIHB1cmUgbWVzc2FnZVxuICAgICAgICBsZXQgbWVzc2FnZUZvcm1tYXRlZCA9IG1lc3NhZ2VVbmZvcm1hdGVkIGFzIGFueTtcbiAgICAgICAgLy8gdmFsaWRhdGUgaWYgdGhlIG1lc3NhZ2UgaXMgY3lwaGVyZWRcbiAgICAgICAgaWYgKGN5cGhlck1hc3RlcktleSkge1xuICAgICAgICAgIC8vIGdldCB0aGUgbWVzc2FnZUxlbmd0aFxuICAgICAgICAgIGNvbnN0IGRhdGFibG9ja0xlbmd0aCA9IG5ldyBEYXRhVmlldyhcbiAgICAgICAgICAgIG5ldyBVaW50OEFycmF5KG1lc3NhZ2VGb3JtbWF0ZWQuc2xpY2UoMSwgMykpLmJ1ZmZlclxuICAgICAgICAgICkuZ2V0SW50MTYoMCwgZmFsc2UpO1xuICAgICAgICAgIHRoaXMuY3lwaGVyQWVzU2VydmljZS5jb25maWcoY3lwaGVyTWFzdGVyS2V5KTtcbiAgICAgICAgICAvLyBnZXQgdGhlIGRhdGFibG9jayBvZiB0aGUgbWVzc2FnZSBhbmQgZGVjcnlwdCB0aGlzIHNlY3Rpb25cbiAgICAgICAgICBjb25zdCBkYXRhYmxvY2sgPSBBcnJheS5mcm9tKFxuICAgICAgICAgICAgdGhpcy5jeXBoZXJBZXNTZXJ2aWNlLmRlY3J5cHQoXG4gICAgICAgICAgICAgIChtZXNzYWdlVW5mb3JtYXRlZCBhcyBhbnkpLnNsaWNlKDMsIGRhdGFibG9ja0xlbmd0aCArIDMpXG4gICAgICAgICAgICApXG4gICAgICAgICAgKTtcbiAgICAgICAgICAvLyBtZXJnZSB0aGUgZGF0YWJsb2NrIGFuZCB0aGUgbWVzc2FnZVxuICAgICAgICAgIG1lc3NhZ2VGb3JtbWF0ZWQgPSAobWVzc2FnZVVuZm9ybWF0ZWQgYXMgYW55KVxuICAgICAgICAgICAgLnNsaWNlKDAsIDMpXG4gICAgICAgICAgICAuY29uY2F0KGRhdGFibG9jaylcbiAgICAgICAgICAgIC5jb25jYXQoKG1lc3NhZ2VVbmZvcm1hdGVkIGFzIGFueSkuc2xpY2UoLTIpKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLl9jb25zb2xlLmxvZyhcbiAgICAgICAgICAnW0JMRTo6SW5mb10gTm90aWZpY2F0aW9uIHJlaXZlZCBmcm9tIGRldmljZTogJyxcbiAgICAgICAgICB0aGlzLmN5cGhlckFlc1NlcnZpY2UuYnl0ZXNUb2hleChtZXNzYWdlRm9ybW1hdGVkKVxuICAgICAgICApO1xuICAgICAgICByZXR1cm4gbWVzc2FnZUZvcm1tYXRlZDtcbiAgICAgIH0pLFxuICAgICAgLy8gZmlsdGVyIHRoZSBtZXNzYWdlIHVzaW5nIHRoZSBmaWx0ZXIgb3B0aW9uc1xuICAgICAgZmlsdGVyKG1lc3NhZ2UgPT4ge1xuICAgICAgICBsZXQgYXZhaWxhYmxlTWVzc2FnZSA9IGZhbHNlO1xuICAgICAgICBmb3IgKGNvbnN0IG9wdGlvbiBvZiBmaWx0ZXJPcHRpb25zKSB7XG4gICAgICAgICAgaWYgKG1lc3NhZ2Vbb3B0aW9uLnBvc2l0aW9uXSA9PT0gb3B0aW9uLmJ5dGVUb01hdGNoKSB7XG4gICAgICAgICAgICBhdmFpbGFibGVNZXNzYWdlID0gdHJ1ZTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgYXZhaWxhYmxlTWVzc2FnZSA9IGZhbHNlO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBhdmFpbGFibGVNZXNzYWdlO1xuICAgICAgfSlcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIFN0YXJ0IGEgcmVxdWVzdCB0byB0aGUgYnJvd3NlciB0byBsaXN0IGFsbCBhdmFpbGFibGUgYmx1ZXRvb3RoIGRldmljZXNcbiAgICogQHBhcmFtIG9wdGlvbnMgT3B0aW9ucyB0byByZXF1ZXN0IHRoZSBkZXZpY2VzIHRoZSBzdHJ1Y3R1cmUgaXM6XG4gICAqIGFjY2VwdEFsbERldmljZXM6IHRydWV8ZmFsc2VcbiAgICogZmlsdGVyczogQmx1ZXRvb3RoRGF0YUZpbHRlckluaXQgKHNlZSBodHRwczovL3dlYmJsdWV0b290aGNnLmdpdGh1Yi5pby93ZWItYmx1ZXRvb3RoLyNkaWN0ZGVmLWJsdWV0b290aGxlc2NhbmZpbHRlcmluaXQgZm9yIG1vcmUgaW5mbylcbiAgICogb3B0aW9uYWxTZXJ2aWNlczogW10gKHNlcnZpY2VzIHRoYXQgYXJlIGdvaW5nIHRvIGJlIHVzZWQgaW5cbiAgICogY29tbXVuaWNhdGlvbiB3aXRoIHRoZSBkZXZpY2UsIG11c3QgdXNlIHRoZSBVSUlEIG9yIEdBVFQgaWRlbnRmaWVyIHRvIGxpc3QgdGhlciBzZXJ2aWNlcylcbiAgICovXG4gIHByaXZhdGUgZGlzY292ZXJEZXZpY2UkKFxuICAgIG9wdGlvbnM6IFJlcXVlc3REZXZpY2VPcHRpb25zID0ge30gYXMgUmVxdWVzdERldmljZU9wdGlvbnNcbiAgKSB7XG4gICAgcmV0dXJuIGRlZmVyKCgpID0+IHRoaXMuX3dlYkJsZS5yZXF1ZXN0RGV2aWNlKG9wdGlvbnMpKS5waXBlKFxuICAgICAgbWVyZ2VNYXAoZGV2aWNlID0+IHtcbiAgICAgICAgdGhpcy5kZXZpY2UgPSBkZXZpY2U7XG4gICAgICAgIHRoaXMuX2RldmljZSQuZW1pdChkZXZpY2UpO1xuICAgICAgICByZXR1cm4gdGhpcy5jb25maWd1cmVEZXZpY2VEaXNjb25uZWN0aW9uJChkZXZpY2UpLnBpcGUobWFwVG8oZGV2aWNlKSk7XG4gICAgICB9KVxuICAgICk7XG4gIH1cblxuICBwcml2YXRlIGNvbmZpZ3VyZURldmljZURpc2Nvbm5lY3Rpb24kKGRldmljZSkge1xuICAgIHJldHVybiBPYnNlcnZhYmxlLmNyZWF0ZShvYnNlcnZlciA9PiB7XG4gICAgICBvZihkZXZpY2UpXG4gICAgICAgIC5waXBlKFxuICAgICAgICAgIG1lcmdlTWFwKGRldiA9PiBmcm9tRXZlbnQoZGV2aWNlLCAnZ2F0dHNlcnZlcmRpc2Nvbm5lY3RlZCcpKSxcbiAgICAgICAgICB0YWtlKDEpXG4gICAgICAgIClcbiAgICAgICAgLnN1YnNjcmliZShcbiAgICAgICAgICAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLl9jb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgJ1NlIGRlc2NvbmVjdGEgZGlzcCBlbiBPbkRldmljZSBkaXNjb25uZWN0ZWQhISEhISEhJ1xuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHRoaXMuZGV2aWNlID0gbnVsbDtcbiAgICAgICAgICAgIHRoaXMuX2RldmljZSQuZW1pdChudWxsKTtcbiAgICAgICAgICB9LFxuICAgICAgICAgIGVyciA9PiB7XG4gICAgICAgICAgICB0aGlzLl9jb25zb2xlLmxvZygnW0JMRTo6SW5mb10gRXJyb3IgaW4gbm90aWZpZXI6ICcsIGVycik7XG4gICAgICAgICAgICBvYnNlcnZlci5lcnJvcihlcnIpO1xuICAgICAgICAgIH0sXG4gICAgICAgICgpID0+IHtcbiAgICAgICAgICB9XG4gICAgICAgICk7XG4gICAgICBvYnNlcnZlci5uZXh0KGBEaXNjb25uZWN0aW9uRXZlbnQgYXMgYmVlbiByZWdpc3RlcmApO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIERpc2NvdmVyIGFsbCBhdmFpbGFibGUgZGV2aWNlcyBhbmQgY29ubmVjdCB0byBhIHNlbGVjdGVkIGRldmljZVxuICAgKiBAcGFyYW0gb3B0aW9ucyBPcHRpb25zIHRvIHJlcXVlc3QgdGhlIGRldmljZXMgdGhlIHN0cnVjdHVyZSBpczpcbiAgICogYWNjZXB0QWxsRGV2aWNlczogdHJ1ZXxmYWxzZVxuICAgKiBmaWx0ZXJzOiBCbHVldG9vdGhEYXRhRmlsdGVySW5pdCAoc2VlIGh0dHBzOi8vd2ViYmx1ZXRvb3RoY2cuZ2l0aHViLmlvL3dlYi1ibHVldG9vdGgvI2RpY3RkZWYtYmx1ZXRvb3RobGVzY2FuZmlsdGVyaW5pdCBmb3IgbW9yZSBpbmZvKVxuICAgKiBvcHRpb25hbFNlcnZpY2VzOiBbXSAoc2VydmljZXMgdGhhdCBhcmUgZ29pbmcgdG8gYmUgdXNlZCBpblxuICAgKiBjb21tdW5pY2F0aW9uIHdpdGggdGhlIGRldmljZSwgbXVzdCB1c2UgdGhlIFVJSUQgb3IgR0FUVCBpZGVudGZpZXIgdG8gbGlzdCB0aGVyIHNlcnZpY2VzKVxuICAgKiBAcmV0dXJucyB0aGUgY29ubmVjdGVkIGRldmljZVxuICAgKi9cbiAgY29ubmVjdERldmljZSQob3B0aW9ucz86IFJlcXVlc3REZXZpY2VPcHRpb25zKSB7XG4gICAgaWYgKCFvcHRpb25zKSB7XG4gICAgICBvcHRpb25zID0ge1xuICAgICAgICBhY2NlcHRBbGxEZXZpY2VzOiB0cnVlLFxuICAgICAgICBvcHRpb25hbFNlcnZpY2VzOiBbXVxuICAgICAgfTtcbiAgICB9IGVsc2UgaWYgKCFvcHRpb25zLm9wdGlvbmFsU2VydmljZXMpIHtcbiAgICAgIG9wdGlvbnMub3B0aW9uYWxTZXJ2aWNlcyA9IFtdO1xuICAgIH1cbiAgICBvcHRpb25zLm9wdGlvbmFsU2VydmljZXMucHVzaChHYXR0U2VydmljZXMuR0VORVJJQ19BQ0NFU1MuU0VSVklDRSk7XG4gICAgb3B0aW9ucy5vcHRpb25hbFNlcnZpY2VzLnB1c2goR2F0dFNlcnZpY2VzLkJBVFRFUlkuU0VSVklDRSk7XG4gICAgb3B0aW9ucy5vcHRpb25hbFNlcnZpY2VzLnB1c2goR2F0dFNlcnZpY2VzLkRFVklDRV9JTkZPUk1BVElPTi5TRVJWSUNFKTtcbiAgICByZXR1cm4gdGhpcy5kaXNjb3ZlckRldmljZSQob3B0aW9ucykucGlwZShcbiAgICAgIG1lcmdlTWFwKGRldmljZSA9PiB7XG4gICAgICAgIHJldHVybiBkZWZlcigoKSA9PiAoZGV2aWNlIGFzIGFueSkuZ2F0dC5jb25uZWN0KCkpO1xuICAgICAgfSlcbiAgICApO1xuICB9XG4gIC8qKlxuICAgKiBEaXNjb25uZWN0IHRoZSBjdXJyZW50IGRldmljZVxuICAgKi9cbiAgZGlzY29ubmVjdERldmljZSgpIHtcbiAgICBpZiAodGhpcy5kZXZpY2UpIHtcbiAgICAgIHRoaXMuX2NvbnNvbGUubG9nKCdzZSBkZWNvbmVjdGEgZGlzcG9zaXRpdm8nKTtcbiAgICAgIHRoaXMuZGV2aWNlLmdhdHQuZGlzY29ubmVjdCgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBnZXQgYSBkYXRhIGZyb20gdGhlIGRldmljZSB1c2luZyB0aGUgY2hhcmFjdGVyaXN0aWNcbiAgICogQHBhcmFtIHNlcnZpY2UgVVVJRCBvciBHQVRUIGlkZW50aWZpZXIgc2VydmljZVxuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgVVVJRCBvciBHQVRUIGlkZW50aWZpZXIgY2hhcmFjdGVyaXN0aWNcbiAgICogQHJldHVybiBUaGUgY2hhcmFjdGVyaXN0aWMgZGF0YSBpbiBhIERhdGFWaWV3IG9iamVjdFxuICAgKi9cbiAgcmVhZERldmljZVZhbHVlJChzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYykge1xuICAgIGlmICghdGhpcy5kZXZpY2UpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ011c3Qgc3RhcnQgYSBjb25uZWN0aW9uIHRvIGEgZGV2aWNlIGJlZm9yZSByZWFkIHRoZSBkZXZpY2UgdmFsdWUnXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5nZXRQcmltYXJ5U2VydmljZSQoc2VydmljZSkucGlwZShcbiAgICAgIG1lcmdlTWFwKHByaW1hcnlTZXJ2aWNlID0+XG4gICAgICAgIHRoaXMuZ2V0Q2hhcmFjdGVyaXN0aWMkKHByaW1hcnlTZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYylcbiAgICAgICksXG4gICAgICBtZXJnZU1hcCgoY2hhcmFjdGVyaXN0aWNWYWx1ZTogQmx1ZXRvb3RoUmVtb3RlR0FUVENoYXJhY3RlcmlzdGljKSA9PlxuICAgICAgICB0aGlzLnJlYWRWYWx1ZSQoY2hhcmFjdGVyaXN0aWNWYWx1ZSlcbiAgICAgIClcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIHdyaXRlIGEgdmFsdWUgaW4gdGhlIHNlbGVjdGVkIGNoYXJhY3RlcmlzdGljXG4gICAqIEBwYXJhbSBjaGFyYWN0ZXJpc3RpYyB0aGUgY2hhcmFjdGVyaXNpdGMgd2hlcmUgeW91IHdhbnQgd3JpdGUgdGhlIHZhbHVlXG4gICAqIEBwYXJhbSB2YWx1ZSB2YWx1ZSB0aGUgdmFsdWUgdG8gd3JpdGVcbiAgICovXG4gIHdyaXRlRGV2aWNlVmFsdWUkKHNlcnZpY2UsIGNoYXJhY3RlcmlzdGljLCB2YWx1ZSkge1xuICAgIGlmICghdGhpcy5kZXZpY2UpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ011c3Qgc3RhcnQgYSBjb25uZWN0aW9uIHRvIGEgZGV2aWNlIGJlZm9yZSByZWFkIHRoZSBkZXZpY2UgdmFsdWUnXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5nZXRQcmltYXJ5U2VydmljZSQoc2VydmljZSkucGlwZShcbiAgICAgIG1lcmdlTWFwKHByaW1hcnlTZXJ2aWNlID0+XG4gICAgICAgIHRoaXMuZ2V0Q2hhcmFjdGVyaXN0aWMkKHByaW1hcnlTZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYylcbiAgICAgICksXG4gICAgICBtZXJnZU1hcCgoY2hhcmFjdGVyaXN0aWNWYWx1ZTogQmx1ZXRvb3RoUmVtb3RlR0FUVENoYXJhY3RlcmlzdGljKSA9PlxuICAgICAgICB0aGlzLndyaXRlVmFsdWUkKGNoYXJhY3RlcmlzdGljVmFsdWUsIHZhbHVlKVxuICAgICAgKVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogZ2V0IGEgcHJpbWFyeSBzZXJ2aWNlIGluc3RhbmNlIHVzaW5nIHRoZSBzZXJ2aWNlIFVJSUQgb3IgR0FUVCBpZGVudGlmaWVyXG4gICAqIEBwYXJhbSBzZXJ2aWNlIHNlcnZpY2UgaWRlbnRpZmllclxuICAgKiBAcmV0dXJuIHNlcnZpY2UgaW5zdGFuY2VcbiAgICovXG4gIGdldFByaW1hcnlTZXJ2aWNlJChzZXJ2aWNlOiBCbHVldG9vdGhTZXJ2aWNlVVVJRCkge1xuICAgIHJldHVybiBvZih0aGlzLmRldmljZSkucGlwZShcbiAgICAgIG1lcmdlTWFwKGRldmljZSA9PiB7XG4gICAgICAgIHJldHVybiBkZXZpY2UuZ2F0dC5nZXRQcmltYXJ5U2VydmljZShzZXJ2aWNlKTtcbiAgICAgIH0pXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgYSBjaGFyYWN0ZXJpc2l0aWMgaW5zdGFuY2UgdXNpbmcgdGhlIHNlcnZpY2UgaW5zdGFuY2UgYW5kIGEgY2hhcmFjdGVyaXN0aWMgVVVJRFxuICAgKiBAcGFyYW0gcHJpbWFyeVNlcnZpY2Ugc2VydmljZSBpbnN0YW5jZVxuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgY2hhcmFjdGVyaXNpdGljIGlkZW50aWZpZXJcbiAgICogQHJldHVybiBjaGFyYWN0ZXJpc3RpYyBpbnN0YW5jZVxuICAgKi9cbiAgZ2V0Q2hhcmFjdGVyaXN0aWMkKFxuICAgIHByaW1hcnlTZXJ2aWNlOiBCbHVldG9vdGhSZW1vdGVHQVRUU2VydmljZSxcbiAgICBjaGFyYWN0ZXJpc3RpYzogQmx1ZXRvb3RoQ2hhcmFjdGVyaXN0aWNVVUlEXG4gICk6IE9ic2VydmFibGU8dm9pZCB8IEJsdWV0b290aFJlbW90ZUdBVFRDaGFyYWN0ZXJpc3RpYz4ge1xuICAgIHJldHVybiBkZWZlcigoKSA9PiBwcmltYXJ5U2VydmljZS5nZXRDaGFyYWN0ZXJpc3RpYyhjaGFyYWN0ZXJpc3RpYykpO1xuICB9XG5cbiAgLyoqXG4gICAqIHJlYWQgdGhlIGNoYXJhY3RlcmlzdGljIHZhbHVlXG4gICAqIEBwYXJhbSBjaGFyYWN0ZXJpc3RpYyBjaGFyYWN0ZXJpc3RpYyBpbnN0YW5jZVxuICAgKiBAcmV0dXJuIFRoZSBjaGFyYWN0ZXJpc3RpYyBkYXRhIGluIGEgRGF0YVZpZXcgb2JqZWN0XG4gICAqL1xuICBwcml2YXRlIHJlYWRWYWx1ZSQoXG4gICAgY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aFJlbW90ZUdBVFRDaGFyYWN0ZXJpc3RpY1xuICApOiBPYnNlcnZhYmxlPERhdGFWaWV3PiB7XG4gICAgcmV0dXJuIGZyb20oXG4gICAgICBjaGFyYWN0ZXJpc3RpY1xuICAgICAgICAucmVhZFZhbHVlKClcbiAgICAgICAgLnRoZW4oXG4gICAgICAgICAgKGRhdGE6IERhdGFWaWV3KSA9PiBQcm9taXNlLnJlc29sdmUoZGF0YSksXG4gICAgICAgICAgKGVycm9yOiBET01FeGNlcHRpb24pID0+IFByb21pc2UucmVqZWN0KGAke2Vycm9yLm1lc3NhZ2V9YClcbiAgICAgICAgKVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogd3JpdGUgYSB2YWx1ZSBpbiB0aGUgc2VsZWN0ZWQgY2hhcmFjdGVyaXN0aWNcbiAgICogQHBhcmFtIGNoYXJhY3RlcmlzdGljIHRoZSBjaGFyYWN0ZXJpc2l0YyB3aGVyZSB5b3Ugd2FudCB3cml0ZSB0aGUgdmFsdWVcbiAgICogQHBhcmFtIHZhbHVlIHRoZSB2YWx1ZSB0byB3cml0ZVxuICAgKi9cbiAgcHJpdmF0ZSB3cml0ZVZhbHVlJChcbiAgICBjaGFyYWN0ZXJpc3RpYzogQmx1ZXRvb3RoUmVtb3RlR0FUVENoYXJhY3RlcmlzdGljLFxuICAgIHZhbHVlOiBBcnJheUJ1ZmZlciB8IFVpbnQ4QXJyYXlcbiAgKSB7XG4gICAgcmV0dXJuIGRlZmVyKCgpID0+XG4gICAgICBjaGFyYWN0ZXJpc3RpY1xuICAgICAgICAud3JpdGVWYWx1ZSh2YWx1ZSlcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIGNoYW5nZSB0aGUgc3RhdGUgb2YgdGhlIGNoYXJhY3RlcmlzdGljIHRvIGVuYWJsZSBpdFxuICAgKiBAcGFyYW0gc2VydmljZSAgcGFyZW50IHNlcnZpY2Ugb2YgdGhlIGNoYXJhY3RlcmlzdGljXG4gICAqIEBwYXJhbSBjaGFyYWN0ZXJpc3RpYyBjaGFyYWN0ZXJpc3RpYyB0byBjaGFuZ2UgdGhlIHN0YXRlXG4gICAqIEBwYXJhbSBzdGF0ZSBuZXcgc3RhdGVcbiAgICovXG4gIGVuYWJsZUNoYXJhY3RlcmlzdGljJChcbiAgICBzZXJ2aWNlOiBCbHVldG9vdGhTZXJ2aWNlVVVJRCxcbiAgICBjaGFyYWN0ZXJpc3RpYzogQmx1ZXRvb3RoQ2hhcmFjdGVyaXN0aWNVVUlELFxuICAgIHN0YXRlPzogYW55XG4gICkge1xuICAgIHN0YXRlID0gc3RhdGUgfHwgbmV3IFVpbnQ4QXJyYXkoWzFdKTtcbiAgICByZXR1cm4gdGhpcy5zZXRDaGFyYWN0ZXJpc3RpY1N0YXRlJChzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYywgc3RhdGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIGNoYW5nZSB0aGUgc3RhdGUgb2YgdGhlIGNoYXJhY3RlcmlzdGljIHRvIGRpc2FibGUgaXRcbiAgICogQHBhcmFtIHNlcnZpY2UgIHBhcmVudCBzZXJ2aWNlIG9mIHRoZSBjaGFyYWN0ZXJpc3RpY1xuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgY2hhcmFjdGVyaXN0aWMgdG8gY2hhbmdlIHRoZSBzdGF0ZVxuICAgKiBAcGFyYW0gc3RhdGUgbmV3IHN0YXRlXG4gICAqL1xuICBkaXNiYWxlQ2hhcmFjdGVyaXN0aWMkKFxuICAgIHNlcnZpY2U6IEJsdWV0b290aFNlcnZpY2VVVUlELFxuICAgIGNoYXJhY3RlcmlzdGljOiBCbHVldG9vdGhDaGFyYWN0ZXJpc3RpY1VVSUQsXG4gICAgc3RhdGU/OiBhbnlcbiAgKSB7XG4gICAgc3RhdGUgPSBzdGF0ZSB8fCBuZXcgVWludDhBcnJheShbMF0pO1xuICAgIHJldHVybiB0aGlzLnNldENoYXJhY3RlcmlzdGljU3RhdGUkKHNlcnZpY2UsIGNoYXJhY3RlcmlzdGljLCBzdGF0ZSk7XG4gIH1cblxuICAvKipcbiAgICogc2V0IGEgc3RhdGUgdG8gYW4gc3BlY2lmaWMgY2hhcmFjdGVyaXN0aWNcbiAgICogQHBhcmFtIHNlcnZpY2UgIHBhcmVudCBzZXJ2aWNlIG9mIHRoZSBjaGFyYWN0ZXJpc3RpY1xuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgY2hhcmFjdGVyaXN0aWMgdG8gY2hhbmdlIHRoZSBzdGF0ZVxuICAgKiBAcGFyYW0gc3RhdGUgbmV3IHN0YXRlXG4gICAqIEByZXR1cm5cbiAgICovXG4gIHNldENoYXJhY3RlcmlzdGljU3RhdGUkKFxuICAgIHNlcnZpY2U6IEJsdWV0b290aFNlcnZpY2VVVUlELFxuICAgIGNoYXJhY3RlcmlzdGljOiBCbHVldG9vdGhDaGFyYWN0ZXJpc3RpY1VVSUQsXG4gICAgc3RhdGU6IEFycmF5QnVmZmVyXG4gICkge1xuICAgIGNvbnN0IHByaW1hcnlTZXJ2aWNlID0gdGhpcy5nZXRQcmltYXJ5U2VydmljZSQoc2VydmljZSk7XG4gICAgcmV0dXJuIHByaW1hcnlTZXJ2aWNlLnBpcGUoXG4gICAgICBtZXJnZU1hcChfcHJpbWFyeVNlcnZpY2UgPT5cbiAgICAgICAgdGhpcy5nZXRDaGFyYWN0ZXJpc3RpYyQoX3ByaW1hcnlTZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYylcbiAgICAgICksXG4gICAgICBtYXAoKF9jaGFyYWN0ZXJpc3RpYzogQmx1ZXRvb3RoUmVtb3RlR0FUVENoYXJhY3RlcmlzdGljKSA9PlxuICAgICAgICB0aGlzLndyaXRlVmFsdWUkKF9jaGFyYWN0ZXJpc3RpYywgc3RhdGUpXG4gICAgICApXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZW5kIGEgbWVzc2FnZSB1c2luZyBhIG5vdGlmaWVyIGNoYXJhY3RlcmlzdGljXG4gICAqIEBwYXJhbSBtZXNzYWdlIG1lc3NhZ2UgdG8gc2VuZFxuICAgKiBAcGFyYW0gc2VydmljZSBzZXJ2aWNlIHRvIHdoaWNoIHRoZSBjaGFyYWN0ZXJpc3RpYyBiZWxvbmdzXG4gICAqIEBwYXJhbSBjaGFyYWN0ZXJpc3RpYyBmZWF0dXJlIGluIHdoaWNoIHlvdSB3YW50IHRvIHNlbmQgdGhlIG5vdGlmaWNhdGlvblxuICAgKi9cbiAgc2VuZFRvTm90aWZpZXIkKG1lc3NhZ2UsIHNlcnZpY2UsIGNoYXJhY3RlcmlzdGljKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0UHJpbWFyeVNlcnZpY2UkKHNlcnZpY2UpLnBpcGUoXG4gICAgICBtZXJnZU1hcCgocHJpbWFyeVNlcnZpY2U6IEJsdWV0b290aFJlbW90ZUdBVFRTZXJ2aWNlKSA9PlxuICAgICAgICB0aGlzLmdldENoYXJhY3RlcmlzdGljJChwcmltYXJ5U2VydmljZSwgY2hhcmFjdGVyaXN0aWMpXG4gICAgICApLFxuICAgICAgbWVyZ2VNYXAoKGNoYXI6IEJsdWV0b290aFJlbW90ZUdBVFRDaGFyYWN0ZXJpc3RpYykgPT4ge1xuICAgICAgICBpZiAobWVzc2FnZS5sZW5ndGggPiAxNikge1xuICAgICAgICAgIGxldCBvYnNUZXN0ID0gb2YodW5kZWZpbmVkKTtcbiAgICAgICAgICB3aGlsZSAobWVzc2FnZS5sZW5ndGggPiAxNikge1xuICAgICAgICAgICAgb2JzVGVzdCA9IG9ic1Rlc3QucGlwZShcbiAgICAgICAgICAgICAgY29uY2F0KHRoaXMud3JpdGVWYWx1ZSQoY2hhciwgbWVzc2FnZS5zbGljZSgwLCAxNikpKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIG1lc3NhZ2UgPSBtZXNzYWdlLnNsaWNlKDE2LCBtZXNzYWdlLmxlbmd0aCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChtZXNzYWdlLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIG9ic1Rlc3QgPSBvYnNUZXN0LnBpcGUoY29uY2F0KHRoaXMud3JpdGVWYWx1ZSQoY2hhciwgbWVzc2FnZSkpKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIG9ic1Rlc3Q7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIHRoaXMud3JpdGVWYWx1ZSQoY2hhciwgbWVzc2FnZSk7XG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgKTtcbiAgfVxuICAvKipcbiAgICogVGhlIEJhdHRlcnkgTGV2ZWwgY2hhcmFjdGVyaXN0aWMgaXMgcmVhZCB1c2luZyB0aGUgR0FUVCBSZWFkIENoYXJhY3RlcmlzdGljXG4gICAqICBWYWx1ZSBzdWItcHJvY2VkdXJlIGFuZCByZXR1cm5zIHRoZSBjdXJyZW50IGJhdHRlcnkgbGV2ZWwgYXMgYSBwZXJjZW50YWdlXG4gICAqICBmcm9tIDAlIHRvIDEwMCU7IDAlIHJlcHJlc2VudHMgYSBiYXR0ZXJ5IHRoYXQgaXMgZnVsbHkgZGlzY2hhcmdlZCwgMTAwJVxuICAgKiByZXByZXNlbnRzIGEgYmF0dGVyeSB0aGF0IGlzIGZ1bGx5IGNoYXJnZWRcbiAgICovXG4gIGdldEJhdHRlcnlMZXZlbCQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhZERldmljZVZhbHVlJChcbiAgICAgIEdhdHRTZXJ2aWNlcy5CQVRURVJZLlNFUlZJQ0UsXG4gICAgICBHYXR0U2VydmljZXMuQkFUVEVSWS5CQVRURVJZX0xFVkVMXG4gICAgKS5waXBlKG1hcCh2YWx1ZSA9PiB2YWx1ZS5nZXRVaW50OCgwKSkpO1xuICB9XG4gIC8qKlxuICAgKiBUaGlzIGNoYXJhY3RlcmlzdGljIHJlcHJlc2VudHMgdGhlIG5hbWUgb2YgdGhlIG1hbnVmYWN0dXJlciBvZiB0aGUgZGV2aWNlLlxuICAgKi9cbiAgZ2V0TWFudWZhY3R1cmVyTmFtZSQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhZERldmljZVZhbHVlJChcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU0VSVklDRSxcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uTUFOVUZBQ1RVUkVSX05BTUVcbiAgICApLnBpcGUoXG4gICAgICBtYXAoZGF0YVZpZXcgPT5cbiAgICAgICAgdGhpcy5jeXBoZXJBZXNTZXJ2aWNlLmJ5dGVzVG9UZXh0KG5ldyBVaW50OEFycmF5KGRhdGFWaWV3LmJ1ZmZlcikpXG4gICAgICApXG4gICAgKTtcbiAgfVxuICAvKipcbiAgICogVGhpcyBjaGFyYWN0ZXJpc3RpYyByZXByZXNlbnRzIHRoZSBtb2RlbCBudW1iZXIgdGhhdCBpcyBhc3NpZ25lZCBieSB0aGUgZGV2aWNlIHZlbmRvci5cbiAgICovXG4gIGdldE1vZGVsTnVtYmVyJCgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWFkRGV2aWNlVmFsdWUkKFxuICAgICAgR2F0dFNlcnZpY2VzLkRFVklDRV9JTkZPUk1BVElPTi5TRVJWSUNFLFxuICAgICAgR2F0dFNlcnZpY2VzLkRFVklDRV9JTkZPUk1BVElPTi5NT0RFTF9OVU1CRVJcbiAgICApLnBpcGUoXG4gICAgICBtYXAoZGF0YVZpZXcgPT5cbiAgICAgICAgdGhpcy5jeXBoZXJBZXNTZXJ2aWNlLmJ5dGVzVG9UZXh0KG5ldyBVaW50OEFycmF5KGRhdGFWaWV3LmJ1ZmZlcikpXG4gICAgICApXG4gICAgKTtcbiAgfVxuICAvKipcbiAgICogVGhpcyBjaGFyYWN0ZXJpc3RpYyByZXByZXNlbnRzIHRoZSBzZXJpYWwgbnVtYmVyIGZvciBhIHBhcnRpY3VsYXIgaW5zdGFuY2Ugb2YgdGhlIGRldmljZS5cbiAgICovXG4gIGdldFNlcmlhbE51bWJlciQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhZERldmljZVZhbHVlJChcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU0VSVklDRSxcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU0VSSUFMX05VTUJFUlxuICAgICkucGlwZShcbiAgICAgIG1hcChkYXRhVmlldyA9PlxuICAgICAgICB0aGlzLmN5cGhlckFlc1NlcnZpY2UuYnl0ZXNUb1RleHQobmV3IFVpbnQ4QXJyYXkoZGF0YVZpZXcuYnVmZmVyKSlcbiAgICAgIClcbiAgICApO1xuICB9XG4gIC8qKlxuICAgKiBUaGlzIGNoYXJhY3RlcmlzdGljIHJlcHJlc2VudHMgdGhlIGhhcmR3YXJlIHJldmlzaW9uIGZvciB0aGUgaGFyZHdhcmUgd2l0aGluIHRoZSBkZXZpY2UuXG4gICAqL1xuICBnZXRIYXJkd2FyZVJldmlzaW9uJCgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWFkRGV2aWNlVmFsdWUkKFxuICAgICAgR2F0dFNlcnZpY2VzLkRFVklDRV9JTkZPUk1BVElPTi5TRVJWSUNFLFxuICAgICAgR2F0dFNlcnZpY2VzLkRFVklDRV9JTkZPUk1BVElPTi5IQVJEV0FSRV9SRVZJU0lPTlxuICAgICkucGlwZShcbiAgICAgIG1hcChkYXRhVmlldyA9PlxuICAgICAgICB0aGlzLmN5cGhlckFlc1NlcnZpY2UuYnl0ZXNUb1RleHQobmV3IFVpbnQ4QXJyYXkoZGF0YVZpZXcuYnVmZmVyKSlcbiAgICAgIClcbiAgICApO1xuICB9XG4gIC8qKlxuICAgKiBUaGlzIGNoYXJhY3RlcmlzdGljIHJlcHJlc2VudHMgdGhlIGZpcm13YXJlIHJldmlzaW9uIGZvciB0aGUgZmlybXdhcmUgd2l0aGluIHRoZSBkZXZpY2UuXG4gICAqL1xuICBnZXRGaXJtd2FyZVJldmlzaW9uJCgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWFkRGV2aWNlVmFsdWUkKFxuICAgICAgR2F0dFNlcnZpY2VzLkRFVklDRV9JTkZPUk1BVElPTi5TRVJWSUNFLFxuICAgICAgR2F0dFNlcnZpY2VzLkRFVklDRV9JTkZPUk1BVElPTi5GSVJNV0FSRV9SRVZJU0lPTlxuICAgICkucGlwZShcbiAgICAgIG1hcChkYXRhVmlldyA9PlxuICAgICAgICB0aGlzLmN5cGhlckFlc1NlcnZpY2UuYnl0ZXNUb1RleHQobmV3IFVpbnQ4QXJyYXkoZGF0YVZpZXcuYnVmZmVyKSlcbiAgICAgIClcbiAgICApO1xuICB9XG4gIC8qKlxuICAgKiBUaGlzIGNoYXJhY3RlcmlzdGljIHJlcHJlc2VudHMgdGhlIHNvZnR3YXJlIHJldmlzaW9uIGZvciB0aGUgc29mdHdhcmUgd2l0aGluIHRoZSBkZXZpY2UuXG4gICAqL1xuICBnZXRTb2Z0d2FyZVJldmlzaW9uJCgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWFkRGV2aWNlVmFsdWUkKFxuICAgICAgR2F0dFNlcnZpY2VzLkRFVklDRV9JTkZPUk1BVElPTi5TRVJWSUNFLFxuICAgICAgR2F0dFNlcnZpY2VzLkRFVklDRV9JTkZPUk1BVElPTi5TT0ZUV0FSRV9SRVZJU0lPTlxuICAgICkucGlwZShcbiAgICAgIG1hcChkYXRhVmlldyA9PlxuICAgICAgICB0aGlzLmN5cGhlckFlc1NlcnZpY2UuYnl0ZXNUb1RleHQobmV3IFVpbnQ4QXJyYXkoZGF0YVZpZXcuYnVmZmVyKSlcbiAgICAgIClcbiAgICApO1xuICB9XG4gIC8qKlxuICAgKiBUaGlzIGNoYXJhY3RlcmlzdGljIHJlcHJlc2VudHMgYSBzdHJ1Y3R1cmUgY29udGFpbmluZyBhbiBPcmdhbml6YXRpb25hbGx5IFVuaXF1ZSBJZGVudGlmaWVyXG4gICAqICAoT1VJKSBmb2xsb3dlZCBieSBhIG1hbnVmYWN0dXJlci1kZWZpbmVkIGlkZW50aWZpZXIgYW5kIGlzIHVuaXF1ZSBmb3IgZWFjaCBpbmRpdmlkdWFsIGluc3RhbmNlIG9mIHRoZSBwcm9kdWN0LlxuICAgKi9cbiAgZ2V0U3lzdGVtSWQkKCkge1xuICAgIHJldHVybiB0aGlzLnJlYWREZXZpY2VWYWx1ZSQoXG4gICAgICBHYXR0U2VydmljZXMuREVWSUNFX0lORk9STUFUSU9OLlNFUlZJQ0UsXG4gICAgICBHYXR0U2VydmljZXMuREVWSUNFX0lORk9STUFUSU9OLlNZU1RFTV9JRFxuICAgICk7XG4gIH1cbiAgLyoqXG4gICAqIFRoZSBQblBfSUQgY2hhcmFjdGVyaXN0aWMgaXMgYSBzZXQgb2YgdmFsdWVzIHVzZWQgdG8gY3JlYXRlIGEgZGV2aWNlIElEIHZhbHVlIHRoYXQgaXMgdW5pcXVlIGZvciB0aGlzIGRldmljZS5cbiAgICovXG4gIGdldFBucElkJCgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWFkRGV2aWNlVmFsdWUkKFxuICAgICAgR2F0dFNlcnZpY2VzLkRFVklDRV9JTkZPUk1BVElPTi5TRVJWSUNFLFxuICAgICAgR2F0dFNlcnZpY2VzLkRFVklDRV9JTkZPUk1BVElPTi5QTlBfSURcbiAgICApO1xuICB9XG59XG4iLCJpbXBvcnQgeyBOZ01vZHVsZSwgTW9kdWxlV2l0aFByb3ZpZGVycywgSW5qZWN0aW9uVG9rZW4gIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBCcm93c2VyV2ViQmx1ZXRvb3RoIH0gZnJvbSAnLi9wbGF0Zm9ybS9icm93c2VyJztcbmltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQgeyBCbHVldG9vdGhTZXJ2aWNlIH0gZnJvbSAnLi9ibHVldG9vdGgvYmx1ZXRvb3RoLnNlcnZpY2UnO1xuaW1wb3J0IHsgQ29uc29sZUxvZ2dlclNlcnZpY2UsIE5vTG9nZ2VyU2VydmljZSB9IGZyb20gJy4vbG9nZ2VyLnNlcnZpY2UnO1xuXG5leHBvcnQgaW50ZXJmYWNlIEFXQk9wdGlvbnMge1xuICBlbmFibGVUcmFjaW5nPzogYm9vbGVhbjtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGJyb3dzZXJXZWJCbHVldG9vdGgoKSB7XG4gIHJldHVybiBuZXcgQnJvd3NlcldlYkJsdWV0b290aCgpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29uc29sZUxvZ2dlclNlcnZpY2VDb25maWcob3B0aW9uczogQVdCT3B0aW9ucykge1xuICBpZiAob3B0aW9ucyAmJiBvcHRpb25zLmVuYWJsZVRyYWNpbmcpIHtcbiAgICByZXR1cm4gbmV3IENvbnNvbGVMb2dnZXJTZXJ2aWNlKCk7XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIG5ldyBOb0xvZ2dlclNlcnZpY2UoKTtcbiAgfVxufVxuZXhwb3J0IGZ1bmN0aW9uIG1ha2VNZVRva2VuSW5qZWN0b3IoKSB7XG4gIHJldHVybiBuZXcgSW5qZWN0aW9uVG9rZW4oJ0FXQk9wdGlvbnMnKTtcbn1cblxuXG5ATmdNb2R1bGUoe1xuICBpbXBvcnRzOiBbQ29tbW9uTW9kdWxlXVxufSlcbmV4cG9ydCBjbGFzcyBBbmd1bGFyQmxlTW9kdWxlIHtcbiAgc3RhdGljIGZvclJvb3Qob3B0aW9uczogQVdCT3B0aW9ucyA9IHt9KTogTW9kdWxlV2l0aFByb3ZpZGVycyB7XG4gICAgcmV0dXJuIHtcbiAgICAgIG5nTW9kdWxlOiBBbmd1bGFyQmxlTW9kdWxlLFxuICAgICAgcHJvdmlkZXJzOiBbXG4gICAgICAgIEJsdWV0b290aFNlcnZpY2UsXG4gICAgICAgIHtcbiAgICAgICAgICBwcm92aWRlOiBCcm93c2VyV2ViQmx1ZXRvb3RoLFxuICAgICAgICAgIHVzZUZhY3Rvcnk6IGJyb3dzZXJXZWJCbHVldG9vdGhcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIHByb3ZpZGU6IG1ha2VNZVRva2VuSW5qZWN0b3IsXG4gICAgICAgICAgdXNlVmFsdWU6IG9wdGlvbnNcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIHByb3ZpZGU6IENvbnNvbGVMb2dnZXJTZXJ2aWNlLFxuICAgICAgICAgIHVzZUZhY3Rvcnk6IGNvbnNvbGVMb2dnZXJTZXJ2aWNlQ29uZmlnLFxuICAgICAgICAgIGRlcHM6IFttYWtlTWVUb2tlbkluamVjdG9yXVxuICAgICAgICB9XG4gICAgICBdXG4gICAgfTtcbiAgfVxufVxuIl0sIm5hbWVzIjpbIkluamVjdGFibGUiLCJhZXMuTW9kZU9mT3BlcmF0aW9uIiwiYWVzLkNvdW50ZXIiLCJhZXMudXRpbHMiLCJ0c2xpYl8xLl9fZXh0ZW5kcyIsIlN1YmplY3QiLCJFdmVudEVtaXR0ZXIiLCJkZWZlciIsIm9mIiwidGFwIiwiZmlsdGVyIiwibWVyZ2VNYXAiLCJyZXRyeVdoZW4iLCJkZWxheSIsInRha2UiLCJmcm9tRXZlbnQiLCJ0YWtlVW50aWwiLCJtYXAiLCJzY2FuIiwiZm9ya0pvaW4iLCJ0aW1lb3V0IiwidHNsaWJfMS5fX3ZhbHVlcyIsIm1hcFRvIiwiT2JzZXJ2YWJsZSIsImZyb20iLCJjb25jYXQiLCJJbmplY3Rpb25Ub2tlbiIsIk5nTW9kdWxlIiwiQ29tbW9uTW9kdWxlIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7O0FBQUE7UUFPRTtZQUNFLElBQUksQ0FBQyxJQUFJLEdBQUcsU0FBUyxDQUFDLFNBQVMsQ0FBQztZQUNoQyxJQUFJLENBQUMsSUFBSSxDQUFD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