model/baseMap/mapbox-gl-online-tiled-map-service-dev.js

(function (f) {

    if (typeof exports === 'object' && typeof module !== 'undefined') {

        module.exports = f();

    } else if (typeof define === 'function' && define.amd) {

        define([], f);

    } else {

        var g;
        if (typeof window !== 'undefined') {

            g = window;

        } else if (typeof global !== 'undefined') {

            g = global;

        } else if (typeof self !== 'undefined') {

            g = self;

        } else {

            g = this;

        }
        g.ArcGISTiledMapServiceSource = f();

    }

})(function () {

    var define, module, exports;
    return (function () {

        function e(t, n, r) {

            function s(o, u) {

                if (!n[o]) {

                    if (!t[o]) {

                        var a = typeof require == 'function' && require;
                        if (!u && a) return a(o, !0);
                        if (i) return i(o, !0);
                        var f = new Error('Cannot find module \'' + o + '\'');
                        throw f.code = 'MODULE_NOT_FOUND', f;

                    }
                    var l = n[o] = {exports: {}};
                    t[o][0].call(l.exports, function (e) {

                        var n = t[o][1][e];
                        return s(n ? n : e);

                    }, l, l.exports, e, t, n, r);

                }
                return n[o].exports;

            }

            var i = typeof require == 'function' && require;
            for (var o = 0; o < r.length; o++) s(r[o]);
            return s;

        }

        return e;

    })()({
        1: [function (require, module, exports) {

            'use strict';

            module.exports = Point;

            /**
             * A standalone point geometry with useful accessor, comparison, and
             * modification methods.
             *
             * @class Point
             * @param {Number} x the x-coordinate. this could be longitude or screen
             * pixels, or any other sort of unit.
             * @param {Number} y the y-coordinate. this could be latitude or screen
             * pixels, or any other sort of unit.
             * @example
             * var point = new Point(-77, 38);
             */
            function Point(x, y) {

                this.x = x;
                this.y = y;

            }

            Point.prototype = {

                /**
                 * Clone this point, returning a new point that can be modified
                 * without affecting the old one.
                 * @return {Point} the clone
                 */
                clone: function () {

                    return new Point(this.x, this.y);

                },

                /**
                 * Add this point's x & y coordinates to another point,
                 * yielding a new point.
                 * @param {Point} p the other point
                 * @return {Point} output point
                 */
                add: function (p) {

                    return this.clone()._add(p);

                },

                /**
                 * Subtract this point's x & y coordinates to from point,
                 * yielding a new point.
                 * @param {Point} p the other point
                 * @return {Point} output point
                 */
                sub: function (p) {

                    return this.clone()._sub(p);

                },

                /**
                 * Multiply this point's x & y coordinates by point,
                 * yielding a new point.
                 * @param {Point} p the other point
                 * @return {Point} output point
                 */
                multByPoint: function (p) {

                    return this.clone()._multByPoint(p);

                },

                /**
                 * Divide this point's x & y coordinates by point,
                 * yielding a new point.
                 * @param {Point} p the other point
                 * @return {Point} output point
                 */
                divByPoint: function (p) {

                    return this.clone()._divByPoint(p);

                },

                /**
                 * Multiply this point's x & y coordinates by a factor,
                 * yielding a new point.
                 * @param {Point} k factor
                 * @return {Point} output point
                 */
                mult: function (k) {

                    return this.clone()._mult(k);

                },

                /**
                 * Divide this point's x & y coordinates by a factor,
                 * yielding a new point.
                 * @param {Point} k factor
                 * @return {Point} output point
                 */
                div: function (k) {

                    return this.clone()._div(k);

                },

                /**
                 * Rotate this point around the 0, 0 origin by an angle a,
                 * given in radians
                 * @param {Number} a angle to rotate around, in radians
                 * @return {Point} output point
                 */
                rotate: function (a) {

                    return this.clone()._rotate(a);

                },

                /**
                 * Rotate this point around p point by an angle a,
                 * given in radians
                 * @param {Number} a angle to rotate around, in radians
                 * @param {Point} p Point to rotate around
                 * @return {Point} output point
                 */
                rotateAround: function (a, p) {

                    return this.clone()._rotateAround(a, p);

                },

                /**
                 * Multiply this point by a 4x1 transformation matrix
                 * @param {Array<Number>} m transformation matrix
                 * @return {Point} output point
                 */
                matMult: function (m) {

                    return this.clone()._matMult(m);

                },

                /**
                 * Calculate this point but as a unit vector from 0, 0, meaning
                 * that the distance from the resulting point to the 0, 0
                 * coordinate will be equal to 1 and the angle from the resulting
                 * point to the 0, 0 coordinate will be the same as before.
                 * @return {Point} unit vector point
                 */
                unit: function () {

                    return this.clone()._unit();

                },

                /**
                 * Compute a perpendicular point, where the new y coordinate
                 * is the old x coordinate and the new x coordinate is the old y
                 * coordinate multiplied by -1
                 * @return {Point} perpendicular point
                 */
                perp: function () {

                    return this.clone()._perp();

                },

                /**
                 * Return a version of this point with the x & y coordinates
                 * rounded to integers.
                 * @return {Point} rounded point
                 */
                round: function () {

                    return this.clone()._round();

                },

                /**
                 * Return the magitude of this point: this is the Euclidean
                 * distance from the 0, 0 coordinate to this point's x and y
                 * coordinates.
                 * @return {Number} magnitude
                 */
                mag: function () {

                    return Math.sqrt(this.x * this.x + this.y * this.y);

                },

                /**
                 * Judge whether this point is equal to another point, returning
                 * true or false.
                 * @param {Point} other the other point
                 * @return {boolean} whether the points are equal
                 */
                equals: function (other) {

                    return this.x === other.x &&
                        this.y === other.y;

                },

                /**
                 * Calculate the distance from this point to another point
                 * @param {Point} p the other point
                 * @return {Number} distance
                 */
                dist: function (p) {

                    return Math.sqrt(this.distSqr(p));

                },

                /**
                 * Calculate the distance from this point to another point,
                 * without the square root step. Useful if you're comparing
                 * relative distances.
                 * @param {Point} p the other point
                 * @return {Number} distance
                 */
                distSqr: function (p) {

                    var dx = p.x - this.x,
                        dy = p.y - this.y;
                    return dx * dx + dy * dy;

                },

                /**
                 * Get the angle from the 0, 0 coordinate to this point, in radians
                 * coordinates.
                 * @return {Number} angle
                 */
                angle: function () {

                    return Math.atan2(this.y, this.x);

                },

                /**
                 * Get the angle from this point to another point, in radians
                 * @param {Point} b the other point
                 * @return {Number} angle
                 */
                angleTo: function (b) {

                    return Math.atan2(this.y - b.y, this.x - b.x);

                },

                /**
                 * Get the angle between this point and another point, in radians
                 * @param {Point} b the other point
                 * @return {Number} angle
                 */
                angleWith: function (b) {

                    return this.angleWithSep(b.x, b.y);

                },

                /*
                 * Find the angle of the two vectors, solving the formula for
                 * the cross product a x b = |a||b|sin(θ) for θ.
                 * @param {Number} x the x-coordinate
                 * @param {Number} y the y-coordinate
                 * @return {Number} the angle in radians
                 */
                angleWithSep: function (x, y) {

                    return Math.atan2(
                        this.x * y - this.y * x,
                        this.x * x + this.y * y);

                },

                _matMult: function (m) {

                    var x = m[0] * this.x + m[1] * this.y,
                        y = m[2] * this.x + m[3] * this.y;
                    this.x = x;
                    this.y = y;
                    return this;

                },

                _add: function (p) {

                    this.x += p.x;
                    this.y += p.y;
                    return this;

                },

                _sub: function (p) {

                    this.x -= p.x;
                    this.y -= p.y;
                    return this;

                },

                _mult: function (k) {

                    this.x *= k;
                    this.y *= k;
                    return this;

                },

                _div: function (k) {

                    this.x /= k;
                    this.y /= k;
                    return this;

                },

                _multByPoint: function (p) {

                    this.x *= p.x;
                    this.y *= p.y;
                    return this;

                },

                _divByPoint: function (p) {

                    this.x /= p.x;
                    this.y /= p.y;
                    return this;

                },

                _unit: function () {

                    this._div(this.mag());
                    return this;

                },

                _perp: function () {

                    var y = this.y;
                    this.y = this.x;
                    this.x = -y;
                    return this;

                },

                _rotate: function (angle) {

                    var cos = Math.cos(angle),
                        sin = Math.sin(angle),
                        x = cos * this.x - sin * this.y,
                        y = sin * this.x + cos * this.y;
                    this.x = x;
                    this.y = y;
                    return this;

                },

                _rotateAround: function (angle, p) {

                    var cos = Math.cos(angle),
                        sin = Math.sin(angle),
                        x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y),
                        y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y);
                    this.x = x;
                    this.y = y;
                    return this;

                },

                _round: function () {

                    this.x = Math.round(this.x);
                    this.y = Math.round(this.y);
                    return this;

                }
            };

            /**
             * Construct a point from an array if necessary, otherwise if the input
             * is already a Point, or an unknown type, return it unchanged
             * @param {Array<Number>|Point|*} a any kind of input value
             * @return {Point} constructed point, or passed-through value.
             * @example
             * // this
             * var point = Point.convert([0, 1]);
             * // is equivalent to
             * var point = new Point(0, 1);
             */
            Point.convert = function (a) {

                if (a instanceof Point) {

                    return a;

                }
                if (Array.isArray(a)) {

                    return new Point(a[0], a[1]);

                }
                return a;

            };

        }, {}],
        2: [function (require, module, exports) {

            /*
             * Copyright (C) 2008 Apple Inc. All Rights Reserved.
             *
             * Redistribution and use in source and binary forms, with or without
             * modification, are permitted provided that the following conditions
             * are met:
             * 1. Redistributions of source code must retain the above copyright
             *    notice, this list of conditions and the following disclaimer.
             * 2. Redistributions in binary form must reproduce the above copyright
             *    notice, this list of conditions and the following disclaimer in the
             *    documentation and/or other materials provided with the distribution.
             *
             * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
             * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
             * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
             * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
             * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
             * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
             * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
             * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
             * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
             * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
             * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
             *
             * Ported from Webkit
             * http://svn.webkit.org/repository/webkit/trunk/Source/WebCore/platform/graphics/UnitBezier.h
             */

            module.exports = UnitBezier;

            function UnitBezier(p1x, p1y, p2x, p2y) {

                // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
                this.cx = 3.0 * p1x;
                this.bx = 3.0 * (p2x - p1x) - this.cx;
                this.ax = 1.0 - this.cx - this.bx;

                this.cy = 3.0 * p1y;
                this.by = 3.0 * (p2y - p1y) - this.cy;
                this.ay = 1.0 - this.cy - this.by;

                this.p1x = p1x;
                this.p1y = p2y;
                this.p2x = p2x;
                this.p2y = p2y;

            }

            UnitBezier.prototype.sampleCurveX = function (t) {

                // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
                return ((this.ax * t + this.bx) * t + this.cx) * t;

            };

            UnitBezier.prototype.sampleCurveY = function (t) {

                return ((this.ay * t + this.by) * t + this.cy) * t;

            };

            UnitBezier.prototype.sampleCurveDerivativeX = function (t) {

                return (3.0 * this.ax * t + 2.0 * this.bx) * t + this.cx;

            };

            UnitBezier.prototype.solveCurveX = function (x, epsilon) {

                if (typeof epsilon === 'undefined') epsilon = 1e-6;

                var t0, t1, t2, x2, i;

                // First try a few iterations of Newton's method -- normally very fast.
                for (t2 = x, i = 0; i < 8; i++) {

                    x2 = this.sampleCurveX(t2) - x;
                    if (Math.abs(x2) < epsilon) return t2;

                    var d2 = this.sampleCurveDerivativeX(t2);
                    if (Math.abs(d2) < 1e-6) break;

                    t2 = t2 - x2 / d2;

                }

                // Fall back to the bisection method for reliability.
                t0 = 0.0;
                t1 = 1.0;
                t2 = x;

                if (t2 < t0) return t0;
                if (t2 > t1) return t1;

                while (t0 < t1) {

                    x2 = this.sampleCurveX(t2);
                    if (Math.abs(x2 - x) < epsilon) return t2;

                    if (x > x2) {

                        t0 = t2;

                    } else {

                        t1 = t2;

                    }

                    t2 = (t1 - t0) * 0.5 + t0;

                }

                // Failure.
                return t2;

            };

            UnitBezier.prototype.solve = function (x, epsilon) {

                return this.sampleCurveY(this.solveCurveX(x, epsilon));

            };

        }, {}],
        3: [function (require, module, exports) {

            'use strict';//

            /**
             * A coordinate is a column, row, zoom combination, often used
             * as the data component of a tile.
             *
             * @param {number} column
             * @param {number} row
             * @param {number} zoom
             * @private
             */
            var Coordinate = function Coordinate(column, row, zoom) {

                this.column = column;
                this.row = row;
                this.zoom = zoom;

            };

            /**
             * Create a clone of this coordinate that can be mutated without
             * changing the original coordinate
             *
             * @returns {Coordinate} clone
             * @private
             * var coord = new Coordinate(0, 0, 0);
             * var c2 = coord.clone();
             * // since coord is cloned, modifying a property of c2 does
             * // not modify it.
             * c2.zoom = 2;
             */
            Coordinate.prototype.clone = function clone() {

                return new Coordinate(this.column, this.row, this.zoom);

            };

            /**
             * Zoom this coordinate to a given zoom level. This returns a new
             * coordinate object, not mutating the old one.
             *
             * @param {number} zoom
             * @returns {Coordinate} zoomed coordinate
             * @private
             * @example
             * var coord = new Coordinate(0, 0, 0);
             * var c2 = coord.zoomTo(1);
             * c2 // equals new Coordinate(0, 0, 1);
             */
            Coordinate.prototype.zoomTo = function zoomTo(zoom) {

                return this.clone()._zoomTo(zoom);

            };

            /**
             * Subtract the column and row values of this coordinate from those
             * of another coordinate. The other coordinat will be zoomed to the
             * same level as `this` before the subtraction occurs
             *
             * @param {Coordinate} c other coordinate
             * @returns {Coordinate} result
             * @private
             */
            Coordinate.prototype.sub = function sub(c) {

                return this.clone()._sub(c);

            };

            Coordinate.prototype._zoomTo = function _zoomTo(zoom) {

                var scale = Math.pow(2, zoom - this.zoom);
                this.column *= scale;
                this.row *= scale;
                this.zoom = zoom;
                return this;

            };

            Coordinate.prototype._sub = function _sub(c) {

                c = c.zoomTo(this.zoom);
                this.column -= c.column;
                this.row -= c.row;
                return this;

            };

            module.exports = Coordinate;


        }, {}],
        4: [function (require, module, exports) {

            'use strict';//

            var wrap = require('../util/util').wrap;

            /**
             * A `LngLat` object represents a given longitude and latitude coordinate, measured in degrees.
             *
             * Mapbox GL uses longitude, latitude coordinate order (as opposed to latitude, longitude) to match GeoJSON.
             *
             * Note that any Mapbox GL method that accepts a `LngLat` object as an argument or option
             * can also accept an `Array` of two numbers and will perform an implicit conversion.
             * This flexible type is documented as {@link LngLatLike}.
             *
             * @param {number} lng Longitude, measured in degrees.
             * @param {number} lat Latitude, measured in degrees.
             * @example
             * var ll = new mapboxgl.LngLat(-73.9749, 40.7736);
             * @see [Get coordinates of the mouse pointer](https://www.mapbox.com/mapbox-gl-js/example/mouse-position/)
             * @see [Display a popup](https://www.mapbox.com/mapbox-gl-js/example/popup/)
             * @see [Highlight features within a bounding box](https://www.mapbox.com/mapbox-gl-js/example/using-box-queryrenderedfeatures/)
             * @see [Create a timeline animation](https://www.mapbox.com/mapbox-gl-js/example/timeline-animation/)
             */
            var LngLat = function LngLat(lng, lat) {

                if (isNaN(lng) || isNaN(lat)) {

                    throw new Error(('Invalid LngLat object: (' + lng + ', ' + lat + ')'));

                }
                this.lng = +lng;
                this.lat = +lat;
                if (this.lat > 90 || this.lat < -90) {

                    throw new Error('Invalid LngLat latitude value: must be between -90 and 90');

                }

            };

            /**
             * Returns a new `LngLat` object whose longitude is wrapped to the range (-180, 180).
             *
             * @returns {LngLat} The wrapped `LngLat` object.
             * @example
             * var ll = new mapboxgl.LngLat(286.0251, 40.7736);
             * var wrapped = ll.wrap();
             * wrapped.lng; // = -73.9749
             */
            LngLat.prototype.wrap = function wrap$1() {

                return new LngLat(wrap(this.lng, -180, 180), this.lat);

            };

            /**
             * Returns the coordinates represented as an array of two numbers.
             *
             * @returns {Array<number>} The coordinates represeted as an array of longitude and latitude.
             * @example
             * var ll = new mapboxgl.LngLat(-73.9749, 40.7736);
             * ll.toArray(); // = [-73.9749, 40.7736]
             */
            LngLat.prototype.toArray = function toArray() {

                return [this.lng, this.lat];

            };

            /**
             * Returns the coordinates represent as a string.
             *
             * @returns {string} The coordinates represented as a string of the format `'LngLat(lng, lat)'`.
             * @example
             * var ll = new mapboxgl.LngLat(-73.9749, 40.7736);
             * ll.toString(); // = "LngLat(-73.9749, 40.7736)"
             */
            LngLat.prototype.toString = function toString() {

                return ('LngLat(' + (this.lng) + ', ' + (this.lat) + ')');

            };

            /**
             * Returns a `LngLatBounds` from the coordinates extended by a given `radius`.
             *
             * @param {number} radius Distance in meters from the coordinates to extend the bounds.
             * @returns {LngLatBounds} A new `LngLatBounds` object representing the coordinates extended by the `radius`.
             * @example
             * var ll = new mapboxgl.LngLat(-73.9749, 40.7736);
             * ll.toBounds(100).toArray(); // = [[-73.97501862141328, 40.77351016847229], [-73.97478137858673, 40.77368983152771]]
             */
            LngLat.prototype.toBounds = function toBounds(radius) {

                var earthCircumferenceInMetersAtEquator = 40075017;
                var latAccuracy = 360 * radius / earthCircumferenceInMetersAtEquator,
                    lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);

                var LngLatBounds = require('./lng_lat_bounds');
                return new LngLatBounds(new LngLat(this.lng - lngAccuracy, this.lat - latAccuracy),
                    new LngLat(this.lng + lngAccuracy, this.lat + latAccuracy));

            };

            /**
             * Converts an array of two numbers to a `LngLat` object.
             *
             * If a `LngLat` object is passed in, the function returns it unchanged.
             *
             * @param {LngLatLike} input An array of two numbers to convert, or a `LngLat` object to return.
             * @returns {LngLat} A new `LngLat` object, if a conversion occurred, or the original `LngLat` object.
             * @example
             * var arr = [-73.9749, 40.7736];
             * var ll = mapboxgl.LngLat.convert(arr);
             * ll;   // = LngLat {lng: -73.9749, lat: 40.7736}
             */
            LngLat.convert = function convert(input) {

                if (input instanceof LngLat) {

                    return input;

                }
                if (Array.isArray(input) && (input.length === 2 || input.length === 3)) {

                    return new LngLat(Number(input[0]), Number(input[1]));

                }
                if (!Array.isArray(input) && typeof input === 'object' && input !== null) {

                    return new LngLat(Number(input.lng), Number(input.lat));

                }
                throw new Error('`LngLatLike` argument must be specified as a LngLat instance, an object {lng: <lng>, lat: <lat>}, or an array of [<lng>, <lat>]');

            };

            /**
             * A {@link LngLat} object, an array of two numbers representing longitude and latitude,
             * or an object with `lng` and `lat` properties.
             *
             * @typedef {LngLat | {lng: number, lat: number} | [number, number]} LngLatLike
             * @example
             * var v1 = new mapboxgl.LngLat(-122.420679, 37.772537);
             * var v2 = [-122.420679, 37.772537];
             */


            module.exports = LngLat;


        }, {'../util/util': 11, './lng_lat_bounds': 5}],
        5: [function (require, module, exports) {

            'use strict';//

            var LngLat = require('./lng_lat');


            /**
             * A `LngLatBounds` object represents a geographical bounding box,
             * defined by its southwest and northeast points in longitude and latitude.
             *
             * If no arguments are provided to the constructor, a `null` bounding box is created.
             *
             * Note that any Mapbox GL method that accepts a `LngLatBounds` object as an argument or option
             * can also accept an `Array` of two {@link LngLatLike} constructs and will perform an implicit conversion.
             * This flexible type is documented as {@link LngLatBoundsLike}.
             *
             * @param {LngLatLike} [sw] The southwest corner of the bounding box.
             * @param {LngLatLike} [ne] The northeast corner of the bounding box.
             * @example
             * var sw = new mapboxgl.LngLat(-73.9876, 40.7661);
             * var ne = new mapboxgl.LngLat(-73.9397, 40.8002);
             * var llb = new mapboxgl.LngLatBounds(sw, ne);
             */
            var LngLatBounds = function LngLatBounds(sw, ne) {

                if (!sw) {

                    return;

                } else if (ne) {

                    this.setSouthWest(sw).setNorthEast(ne);

                } else if (sw.length === 4) {

                    this.setSouthWest([sw[0], sw[1]]).setNorthEast([sw[2], sw[3]]);

                } else {

                    this.setSouthWest(sw[0]).setNorthEast(sw[1]);

                }

            };

            /**
             * Set the northeast corner of the bounding box
             *
             * @param {LngLatLike} ne
             * @returns {LngLatBounds} `this`
             */
            LngLatBounds.prototype.setNorthEast = function setNorthEast(ne) {

                this._ne = ne instanceof LngLat ? new LngLat(ne.lng, ne.lat) : LngLat.convert(ne);
                return this;

            };

            /**
             * Set the southwest corner of the bounding box
             *
             * @param {LngLatLike} sw
             * @returns {LngLatBounds} `this`
             */
            LngLatBounds.prototype.setSouthWest = function setSouthWest(sw) {

                this._sw = sw instanceof LngLat ? new LngLat(sw.lng, sw.lat) : LngLat.convert(sw);
                return this;

            };

            /**
             * Extend the bounds to include a given LngLat or LngLatBounds.
             *
             * @param {LngLat|LngLatBounds} obj object to extend to
             * @returns {LngLatBounds} `this`
             */
            LngLatBounds.prototype.extend = function extend(obj) {

                var sw = this._sw,
                    ne = this._ne;
                var sw2, ne2;

                if (obj instanceof LngLat) {

                    sw2 = obj;
                    ne2 = obj;

                } else if (obj instanceof LngLatBounds) {

                    sw2 = obj._sw;
                    ne2 = obj._ne;

                    if (!sw2 || !ne2) {

                        return this;

                    }

                } else {

                    if (Array.isArray(obj)) {

                        if (obj.every(Array.isArray)) {

                            return this.extend(LngLatBounds.convert(obj));

                        } else {

                            return this.extend(LngLat.convert(obj));

                        }

                    }
                    return this;

                }

                if (!sw && !ne) {

                    this._sw = new LngLat(sw2.lng, sw2.lat);
                    this._ne = new LngLat(ne2.lng, ne2.lat);

                } else {

                    sw.lng = Math.min(sw2.lng, sw.lng);
                    sw.lat = Math.min(sw2.lat, sw.lat);
                    ne.lng = Math.max(ne2.lng, ne.lng);
                    ne.lat = Math.max(ne2.lat, ne.lat);

                }

                return this;

            };

            /**
             * Returns the geographical coordinate equidistant from the bounding box's corners.
             *
             * @returns {LngLat} The bounding box's center.
             * @example
             * var llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]);
             * llb.getCenter(); // = LngLat {lng: -73.96365, lat: 40.78315}
             */
            LngLatBounds.prototype.getCenter = function getCenter() {

                return new LngLat((this._sw.lng + this._ne.lng) / 2, (this._sw.lat + this._ne.lat) / 2);

            };

            /**
             * Returns the southwest corner of the bounding box.
             *
             * @returns {LngLat} The southwest corner of the bounding box.
             */
            LngLatBounds.prototype.getSouthWest = function getSouthWest() {

                return this._sw;

            };

            /**
             * Returns the northeast corner of the bounding box.
             *
             * @returns {LngLat} The northeast corner of the bounding box.
             */
            LngLatBounds.prototype.getNorthEast = function getNorthEast() {

                return this._ne;

            };

            /**
             * Returns the northwest corner of the bounding box.
             *
             * @returns {LngLat} The northwest corner of the bounding box.
             */
            LngLatBounds.prototype.getNorthWest = function getNorthWest() {

                return new LngLat(this.getWest(), this.getNorth());

            };

            /**
             * Returns the southeast corner of the bounding box.
             *
             * @returns {LngLat} The southeast corner of the bounding box.
             */
            LngLatBounds.prototype.getSouthEast = function getSouthEast() {

                return new LngLat(this.getEast(), this.getSouth());

            };

            /**
             * Returns the west edge of the bounding box.
             *
             * @returns {number} The west edge of the bounding box.
             */
            LngLatBounds.prototype.getWest = function getWest() {

                return this._sw.lng;

            };

            /**
             * Returns the south edge of the bounding box.
             *
             * @returns {number} The south edge of the bounding box.
             */
            LngLatBounds.prototype.getSouth = function getSouth() {

                return this._sw.lat;

            };

            /**
             * Returns the east edge of the bounding box.
             *
             * @returns {number} The east edge of the bounding box.
             */
            LngLatBounds.prototype.getEast = function getEast() {

                return this._ne.lng;

            };

            /**
             * Returns the north edge of the bounding box.
             *
             * @returns {number} The north edge of the bounding box.
             */
            LngLatBounds.prototype.getNorth = function getNorth() {

                return this._ne.lat;

            };

            /**
             * Returns the bounding box represented as an array.
             *
             * @returns {Array<Array<number>>} The bounding box represented as an array, consisting of the
             *   southwest and northeast coordinates of the bounding represented as arrays of numbers.
             * @example
             * var llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]);
             * llb.toArray(); // = [[-73.9876, 40.7661], [-73.9397, 40.8002]]
             */
            LngLatBounds.prototype.toArray = function toArray() {

                return [this._sw.toArray(), this._ne.toArray()];

            };

            /**
             * Return the bounding box represented as a string.
             *
             * @returns {string} The bounding box represents as a string of the format
             *   `'LngLatBounds(LngLat(lng, lat), LngLat(lng, lat))'`.
             * @example
             * var llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]);
             * llb.toString(); // = "LngLatBounds(LngLat(-73.9876, 40.7661), LngLat(-73.9397, 40.8002))"
             */
            LngLatBounds.prototype.toString = function toString() {

                return ('LngLatBounds(' + (this._sw.toString()) + ', ' + (this._ne.toString()) + ')');

            };

            /**
             * Converts an array to a `LngLatBounds` object.
             *
             * If a `LngLatBounds` object is passed in, the function returns it unchanged.
             *
             * Internally, the function calls `LngLat#convert` to convert arrays to `LngLat` values.
             *
             * @param {LngLatBoundsLike} input An array of two coordinates to convert, or a `LngLatBounds` object to return.
             * @returns {LngLatBounds} A new `LngLatBounds` object, if a conversion occurred, or the original `LngLatBounds` object.
             * @example
             * var arr = [[-73.9876, 40.7661], [-73.9397, 40.8002]];
             * var llb = mapboxgl.LngLatBounds.convert(arr);
             * llb;   // = LngLatBounds {_sw: LngLat {lng: -73.9876, lat: 40.7661}, _ne: LngLat {lng: -73.9397, lat: 40.8002}}
             */
            LngLatBounds.convert = function convert(input) {

                if (!input || input instanceof LngLatBounds) {

                    return input;

                }
                return new LngLatBounds(input);

            };

            /**
             * A {@link LngLatBounds} object, an array of {@link LngLatLike} objects in [sw, ne] order,
             * or an array of numbers in [west, south, east, north] order.
             *
             * @typedef {LngLatBounds | [LngLatLike, LngLatLike] | [number, number, number, number]} LngLatBoundsLike
             * @example
             * var v1 = new mapboxgl.LngLatBounds(
             *   new mapboxgl.LngLat(-73.9876, 40.7661),
             *   new mapboxgl.LngLat(-73.9397, 40.8002)
             * );
             * var v2 = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002])
             * var v3 = [[-73.9876, 40.7661], [-73.9397, 40.8002]];
             */


            module.exports = LngLatBounds;


        }, {'./lng_lat': 4}],
        6: [function (require, module, exports) {

            'use strict';//

            var ref = require('../util/window');
            var HTMLImageElement = ref.HTMLImageElement;
            var HTMLCanvasElement = ref.HTMLCanvasElement;
            var HTMLVideoElement = ref.HTMLVideoElement;
            var ImageData = ref.ImageData;


            var Texture = function Texture(context, image, format, premultiply) {

                this.context = context;

                var width = image.width;
                var height = image.height;
                this.size = [width, height];
                this.format = format;

                this.texture = context.gl.createTexture();
                this.update(image, premultiply);

            };

            Texture.prototype.update = function update(image, premultiply) {

                var width = image.width;
                var height = image.height;
                this.size = [width, height];

                var ref = this;
                var context = ref.context;
                var gl = context.gl;
                gl.bindTexture(gl.TEXTURE_2D, this.texture);
                context.pixelStoreUnpack.set(1);

                if (this.format === gl.RGBA && premultiply !== false) {

                    context.pixelStoreUnpackPremultiplyAlpha.set(true);

                }

                if (image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof HTMLVideoElement || image instanceof ImageData) {

                    gl.texImage2D(gl.TEXTURE_2D, 0, this.format, this.format, gl.UNSIGNED_BYTE, image);

                } else {

                    gl.texImage2D(gl.TEXTURE_2D, 0, this.format, width, height, 0, this.format, gl.UNSIGNED_BYTE, image.data);

                }

            };

            Texture.prototype.bind = function bind(filter, wrap, minFilter) {

                var ref = this;
                var context = ref.context;
                var gl = context.gl;
                gl.bindTexture(gl.TEXTURE_2D, this.texture);

                if (filter !== this.filter) {

                    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
                    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter || filter);
                    this.filter = filter;

                }

                if (wrap !== this.wrap) {

                    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrap);
                    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrap);
                    this.wrap = wrap;

                }

            };

            Texture.prototype.destroy = function destroy() {

                var ref = this.context;
                var gl = ref.gl;
                gl.deleteTexture(this.texture);
                this.texture = (null );

            };

            module.exports = Texture;


        }, {'../util/window': 9}],
        7: [function (require, module, exports) {

            'use strict';//

            var LngLatBounds = require('../geo/lng_lat_bounds');
            var clamp = require('../util/util').clamp;


            var TileBounds = function TileBounds(bounds, minzoom, maxzoom) {

                this.bounds = LngLatBounds.convert(this.validateBounds(bounds));
                this.minzoom = minzoom || 0;
                this.maxzoom = maxzoom || 24;

            };

            TileBounds.prototype.validateBounds = function validateBounds(bounds) {

                // make sure the bounds property contains valid longitude and latitudes
                if (!Array.isArray(bounds) || bounds.length !== 4) {

                    return [-180, -90, 180, 90];

                }
                return [Math.max(-180, bounds[0]), Math.max(-90, bounds[1]), Math.min(180, bounds[2]), Math.min(90, bounds[3])];

            };

            TileBounds.prototype.contains = function contains(tileID) {

                var level = {
                    minX: Math.floor(this.lngX(this.bounds.getWest(), tileID.z)),
                    minY: Math.floor(this.latY(this.bounds.getNorth(), tileID.z)),
                    maxX: Math.ceil(this.lngX(this.bounds.getEast(), tileID.z)),
                    maxY: Math.ceil(this.latY(this.bounds.getSouth(), tileID.z))
                };
                var hit = tileID.x >= level.minX && tileID.x < level.maxX && tileID.y >= level.minY && tileID.y < level.maxY;
                return hit;

            };

            TileBounds.prototype.lngX = function lngX(lng, zoom) {

                return (lng + 180) * (Math.pow(2, zoom) / 360);

            };

            TileBounds.prototype.latY = function latY(lat, zoom) {

                var f = clamp(Math.sin(Math.PI / 180 * lat), -0.9999, 0.9999);
                var scale = Math.pow(2, zoom) / (2 * Math.PI);
                return Math.pow(2, zoom - 1) + 0.5 * Math.log((1 + f) / (1 - f)) * -scale;

            };

            module.exports = TileBounds;


        }, {'../geo/lng_lat_bounds': 5, '../util/util': 11}],
        8: [function (require, module, exports) {

            'use strict';//

            var window = require('./window');


            /**
             * The type of a resource.
             * @private
             * @readonly
             * @enum {string}
             */
            var ResourceType = {
                Unknown: 'Unknown',
                Style: 'Style',
                Source: 'Source',
                Tile: 'Tile',
                Glyphs: 'Glyphs',
                SpriteImage: 'SpriteImage',
                SpriteJSON: 'SpriteJSON',
                Image: 'Image'
            };
            exports.ResourceType = ResourceType;

            if (typeof Object.freeze == 'function') {

                Object.freeze(ResourceType);

            }

            /**
             * A `RequestParameters` object to be returned from Map.options.transformRequest callbacks.
             * @typedef {Object} RequestParameters
             * @property {string} url The URL to be requested.
             * @property {Object} headers The headers to be sent with the request.
             * @property {string} credentials `'same-origin'|'include'` Use 'include' to send cookies with cross-origin requests.
             */






            var AJAXError = (function (Error) {

                function AJAXError(message, status) {

                    Error.call(this, message);
                    this.status = status;

                }

                if (Error) AJAXError.__proto__ = Error;
                AJAXError.prototype = Object.create(Error && Error.prototype);
                AJAXError.prototype.constructor = AJAXError;

                return AJAXError;

            }(Error));

            function makeRequest(requestParameters) {

                var xhr = new window.XMLHttpRequest();

                xhr.open('GET', requestParameters.url, true);
                for (var k in requestParameters.headers) {

                    xhr.setRequestHeader(k, requestParameters.headers[k]);

                }
                xhr.withCredentials = requestParameters.credentials === 'include';
                return xhr;

            }

            exports.getJSON = function (requestParameters, callback) {

                var xhr = makeRequest(requestParameters);
                xhr.setRequestHeader('Accept', 'application/json');
                xhr.onerror = function () {

                    callback(new Error(xhr.statusText));

                };
                xhr.onload = function () {

                    if (xhr.status >= 200 && xhr.status < 300 && xhr.response) {

                        var data;
                        try {

                            data = JSON.parse(xhr.response);

                        } catch (err) {

                            return callback(err);

                        }
                        callback(null, data);

                    } else {

                        callback(new AJAXError(xhr.statusText, xhr.status));

                    }

                };
                xhr.send();
                return xhr;

            };

            exports.getArrayBuffer = function (requestParameters, callback) {

                var xhr = makeRequest(requestParameters);
                xhr.responseType = 'arraybuffer';
                xhr.onerror = function () {

                    callback(new Error(xhr.statusText));

                };
                xhr.onload = function () {

                    var response = xhr.response;
                    if (response.byteLength === 0 && xhr.status === 200) {

                        return callback(new Error('http status 200 returned without content.'));

                    }
                    if (xhr.status >= 200 && xhr.status < 300 && xhr.response) {

                        callback(null, {
                            data: response,
                            cacheControl: xhr.getResponseHeader('Cache-Control'),
                            expires: xhr.getResponseHeader('Expires')
                        });

                    } else {

                        callback(new AJAXError(xhr.statusText, xhr.status));

                    }

                };
                xhr.send();
                return xhr;

            };

            function sameOrigin(url) {

                var a = window.document.createElement('a');
                a.href = url;
                return a.protocol === window.document.location.protocol && a.host === window.document.location.host;

            }

            var transparentPngUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII=';

            exports.getImage = function (requestParameters, callback) {

                // request the image with XHR to work around caching issues
                // see https://github.com/mapbox/mapbox-gl-js/issues/1470
                return exports.getArrayBuffer(requestParameters, function (err, imgData) {

                    if (err) {

                        callback(err);

                    } else if (imgData) {

                        var img = new window.Image();
                        var URL = window.URL || window.webkitURL;
                        img.onload = function () {

                            callback(null, img);
                            URL.revokeObjectURL(img.src);

                        };
                        var blob = new window.Blob([new Uint8Array(imgData.data)], {type: 'image/png'});
                        (img     ).cacheControl = imgData.cacheControl;
                        (img     ).expires = imgData.expires;
                        img.src = imgData.data.byteLength ? URL.createObjectURL(blob) : transparentPngUrl;

                    }

                });

            };

            exports.getVideo = function (urls, callback) {

                var video = window.document.createElement('video');
                video.onloadstart = function () {

                    callback(null, video);

                };
                for (var i = 0; i < urls.length; i++) {

                    var s = window.document.createElement('source');
                    if (!sameOrigin(urls[i])) {

                        video.crossOrigin = 'Anonymous';

                    }
                    s.src = urls[i];
                    video.appendChild(s);

                }
                return video;

            };


        }, {'./window': 9}],
        9: [function (require, module, exports) {

            'use strict';//

            /* eslint-env browser */
            module.exports = (self        );


        }, {}],
        10: [function (require, module, exports) {

            'use strict';//

            var util = require('./util');


            function _addEventListener(type, listener, listenerList) {

                listenerList[type] = listenerList[type] || [];
                listenerList[type].push(listener);

            }

            function _removeEventListener(type, listener, listenerList) {

                if (listenerList && listenerList[type]) {

                    var index = listenerList[type].indexOf(listener);
                    if (index !== -1) {

                        listenerList[type].splice(index, 1);

                    }

                }

            }

            /**
             * Methods mixed in to other classes for event capabilities.
             *
             * @mixin Evented
             */
            var Evented = function Evented() {
            };

            Evented.prototype.on = function on(type, listener) {

                this._listeners = this._listeners || {};
                _addEventListener(type, listener, this._listeners);

                return this;

            };

            /**
             * Removes a previously registered event listener.
             *
             * @param {string} type The event type to remove listeners for.
             * @param {Function} listener The listener function to remove.
             * @returns {Object} `this`
             */
            Evented.prototype.off = function off(type, listener) {

                _removeEventListener(type, listener, this._listeners);
                _removeEventListener(type, listener, this._oneTimeListeners);

                return this;

            };

            /**
             * Adds a listener that will be called only once to a specified event type.
             *
             * The listener will be called first time the event fires after the listener is registered.
             *
             * @param {string} type The event type to listen for.
             * @param {Function} listener The function to be called when the event is fired the first time.
             * @returns {Object} `this`
             */
            Evented.prototype.once = function once(type, listener) {

                this._oneTimeListeners = this._oneTimeListeners || {};
                _addEventListener(type, listener, this._oneTimeListeners);

                return this;

            };

            /**
             * Fires an event of the specified type.
             *
             * @param {string} type The type of event to fire.
             * @param {Object} [data] Data to be passed to any listeners.
             * @returns {Object} `this`
             */
            Evented.prototype.fire = function fire(type, data) {

                var this$1 = this;

                if (this.listens(type)) {

                    data = util.extend({}, data, {type: type, target: this});

                    // make sure adding or removing listeners inside other listeners won't cause an infinite loop
                    var listeners = this._listeners && this._listeners[type] ? this._listeners[type].slice() : [];
                    for (var i = 0, list = listeners; i < list.length; i += 1) {

                        var listener = list[i];

                        listener.call(this$1, data);

                    }

                    var oneTimeListeners = this._oneTimeListeners && this._oneTimeListeners[type] ? this._oneTimeListeners[type].slice() : [];
                    for (var i$1 = 0, list$1 = oneTimeListeners; i$1 < list$1.length; i$1 += 1) {

                        var listener$1 = list$1[i$1];

                        _removeEventListener(type, listener$1, this$1._oneTimeListeners);
                        listener$1.call(this$1, data);

                    }

                    if (this._eventedParent) {

                        this._eventedParent.fire(type, util.extend({}, data, typeof this._eventedParentData === 'function' ? this._eventedParentData() : this._eventedParentData));

                    }

                    // To ensure that no error events are dropped, print them to the
                    // console if they have no listeners.

                } else if (util.endsWith(type, 'error')) {

                    console.error((data && data.error) || data || 'Empty error event');

                }

                return this;

            };

            /**
             * Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type.
             *
             * @param {string} type The event type
             * @returns {boolean} `true` if there is at least one registered listener for specified event type, `false` otherwise
             */
            Evented.prototype.listens = function listens(type) {

                return (
                    (this._listeners && this._listeners[type] && this._listeners[type].length > 0) ||
                    (this._oneTimeListeners && this._oneTimeListeners[type] && this._oneTimeListeners[type].length > 0) ||
                    (this._eventedParent && this._eventedParent.listens(type))
                );

            };

            /**
             * Bubble all events fired by this instance of Evented to this parent instance of Evented.
             *
             * @private
             * @returns {Object} `this`
             */
            Evented.prototype.setEventedParent = function setEventedParent(parent, data) {

                this._eventedParent = parent;
                this._eventedParentData = data;

                return this;

            };

            module.exports = Evented;


        }, {'./util': 11}],
        11: [function (require, module, exports) {

            'use strict';//

            var UnitBezier = require('@mapbox/unitbezier');
            var Coordinate = require('../geo/coordinate');
            var Point = require('@mapbox/point-geometry');


            /**
             * @module util
             * @private
             */

            /**
             * Given a value `t` that varies between 0 and 1, return
             * an interpolation function that eases between 0 and 1 in a pleasing
             * cubic in-out fashion.
             *
             * @private
             */
            exports.easeCubicInOut = function (t) {

                if (t <= 0) {

                    return 0;

                }
                if (t >= 1) {

                    return 1;

                }
                var t2 = t * t,
                    t3 = t2 * t;
                return 4 * (t < 0.5 ? t3 : 3 * (t - t2) + t3 - 0.75);

            };

            /**
             * Given given (x, y), (x1, y1) control points for a bezier curve,
             * return a function that interpolates along that curve.
             *
             * @param p1x control point 1 x coordinate
             * @param p1y control point 1 y coordinate
             * @param p2x control point 2 x coordinate
             * @param p2y control point 2 y coordinate
             * @private
             */
            exports.bezier = function (p1x, p1y, p2x, p2y) {

                var bezier = new UnitBezier(p1x, p1y, p2x, p2y);
                return function (t) {

                    return bezier.solve(t);

                };

            };

            /**
             * A default bezier-curve powered easing function with
             * control points (0.25, 0.1) and (0.25, 1)
             *
             * @private
             */
            exports.ease = exports.bezier(0.25, 0.1, 0.25, 1);

            /**
             * constrain n to the given range via min + max
             *
             * @param n value
             * @param min the minimum value to be returned
             * @param max the maximum value to be returned
             * @returns the clamped value
             * @private
             */
            exports.clamp = function (n, min, max) {

                return Math.min(max, Math.max(min, n));

            };

            /**
             * constrain n to the given range, excluding the minimum, via modular arithmetic
             *
             * @param n value
             * @param min the minimum value to be returned, exclusive
             * @param max the maximum value to be returned, inclusive
             * @returns constrained number
             * @private
             */
            exports.wrap = function (n, min, max) {

                var d = max - min;
                var w = ((n - min) % d + d) % d + min;
                return (w === min) ? max : w;

            };

            /*
             * Call an asynchronous function on an array of arguments,
             * calling `callback` with the completed results of all calls.
             *
             * @param array input to each call of the async function.
             * @param fn an async function with signature (data, callback)
             * @param callback a callback run after all async work is done.
             * called with an array, containing the results of each async call.
             * @private
             */
            exports.asyncAll = function (array,
                                         fn,
                                         callback) {

                if (!array.length) {

                    return callback(null, []);

                }
                var remaining = array.length;
                var results = new Array(array.length);
                var error = null;
                array.forEach(function (item, i) {

                    fn(item, function (err, result) {

                        if (err) {

                            error = err;

                        }
                        results[i] = ((result     )        ); // https://github.com/facebook/flow/issues/2123
                        if (--remaining === 0) {

                            callback(error, results);

                        }

                    });

                });

            };

            /*
             * Polyfill for Object.values. Not fully spec compliant, but we don't
             * need it to be.
             *
             * @private
             */
            exports.values = function (obj) {

                var result = [];
                for (var k in obj) {

                    result.push(obj[k]);

                }
                return result;

            };

            /*
             * Compute the difference between the keys in one object and the keys
             * in another object.
             *
             * @returns keys difference
             * @private
             */
            exports.keysDifference = function (obj, other) {

                var difference = [];
                for (var i in obj) {

                    if (!(i in other)) {

                        difference.push(i);

                    }

                }
                return difference;

            };

            /**
             * Given a destination object and optionally many source objects,
             * copy all properties from the source objects into the destination.
             * The last source object given overrides properties from previous
             * source objects.
             *
             * @param dest destination object
             * @param sources sources from which properties are pulled
             * @private
             */
            exports.extend = function (dest) {

                var sources = [], len = arguments.length - 1;
                while (len-- > 0) sources[len] = arguments[len + 1];

                for (var i = 0, list = sources; i < list.length; i += 1) {

                    var src = list[i];

                    for (var k in src) {

                        dest[k] = src[k];

                    }

                }
                return dest;

            };

            /**
             * Given an object and a number of properties as strings, return version
             * of that object with only those properties.
             *
             * @param src the object
             * @param properties an array of property names chosen
             * to appear on the resulting object.
             * @returns object with limited properties.
             * @example
             * var foo = { name: 'Charlie', age: 10 };
             * var justName = pick(foo, ['name']);
             * // justName = { name: 'Charlie' }
             * @private
             */
            exports.pick = function (src, properties) {

                var result = {};
                for (var i = 0; i < properties.length; i++) {

                    var k = properties[i];
                    if (k in src) {

                        result[k] = src[k];

                    }

                }
                return result;

            };

            var id = 1;

            /**
             * Return a unique numeric id, starting at 1 and incrementing with
             * each call.
             *
             * @returns unique numeric id.
             * @private
             */
            exports.uniqueId = function () {

                return id++;

            };

            /**
             * Given an array of member function names as strings, replace all of them
             * with bound versions that will always refer to `context` as `this`. This
             * is useful for classes where otherwise event bindings would reassign
             * `this` to the evented object or some other value: this lets you ensure
             * the `this` value always.
             *
             * @param fns list of member function names
             * @param context the context value
             * @example
             * function MyClass() {
 *   bindAll(['ontimer'], this);
 *   this.name = 'Tom';
 * }
             * MyClass.prototype.ontimer = function() {
 *   alert(this.name);
 * };
             * var myClass = new MyClass();
             * setTimeout(myClass.ontimer, 100);
             * @private
             */
            exports.bindAll = function (fns, context) {

                fns.forEach(function (fn) {

                    if (!context[fn]) {

                        return;

                    }
                    context[fn] = context[fn].bind(context);

                });

            };

            /**
             * Given a list of coordinates, get their center as a coordinate.
             *
             * @returns centerpoint
             * @private
             */
            exports.getCoordinatesCenter = function (coords) {

                var minX = Infinity;
                var minY = Infinity;
                var maxX = -Infinity;
                var maxY = -Infinity;

                for (var i = 0; i < coords.length; i++) {

                    minX = Math.min(minX, coords[i].column);
                    minY = Math.min(minY, coords[i].row);
                    maxX = Math.max(maxX, coords[i].column);
                    maxY = Math.max(maxY, coords[i].row);

                }

                var dx = maxX - minX;
                var dy = maxY - minY;
                var dMax = Math.max(dx, dy);
                var zoom = Math.max(0, Math.floor(-Math.log(dMax) / Math.LN2));
                return new Coordinate((minX + maxX) / 2, (minY + maxY) / 2, 0)
                    .zoomTo(zoom);

            };

            /**
             * Determine if a string ends with a particular substring
             *
             * @private
             */
            exports.endsWith = function (string, suffix) {

                return string.indexOf(suffix, string.length - suffix.length) !== -1;

            };

            /**
             * Create an object by mapping all the values of an existing object while
             * preserving their keys.
             *
             * @private
             */
            exports.mapObject = function (input, iterator, context) {

                var this$1 = this;

                var output = {};
                for (var key in input) {

                    output[key] = iterator.call(context || this$1, input[key], key, input);

                }
                return output;

            };

            /**
             * Create an object by filtering out values of an existing object.
             *
             * @private
             */
            exports.filterObject = function (input, iterator, context) {

                var this$1 = this;

                var output = {};
                for (var key in input) {

                    if (iterator.call(context || this$1, input[key], key, input)) {

                        output[key] = input[key];

                    }

                }
                return output;

            };

            /**
             * Deeply compares two object literals.
             *
             * @private
             */
            exports.deepEqual = function (a, b) {

                if (Array.isArray(a)) {

                    if (!Array.isArray(b) || a.length !== b.length) {

                        return false;

                    }
                    for (var i = 0; i < a.length; i++) {

                        if (!exports.deepEqual(a[i], b[i])) {

                            return false;

                        }

                    }
                    return true;

                }
                if (typeof a === 'object' && a !== null && b !== null) {

                    if (!(typeof b === 'object')) {

                        return false;

                    }
                    var keys = Object.keys(a);
                    if (keys.length !== Object.keys(b).length) {

                        return false;

                    }
                    for (var key in a) {

                        if (!exports.deepEqual(a[key], b[key])) {

                            return false;

                        }

                    }
                    return true;

                }
                return a === b;

            };

            /**
             * Deeply clones two objects.
             *
             * @private
             */
            exports.clone = function (input) {

                if (Array.isArray(input)) {

                    return input.map(exports.clone);

                } else if (typeof input === 'object' && input) {

                    return ((exports.mapObject(input, exports.clone)     )   );

                } else {

                    return input;

                }

            };

            /**
             * Check if two arrays have at least one common element.
             *
             * @private
             */
            exports.arraysIntersect = function (a, b) {

                for (var l = 0; l < a.length; l++) {

                    if (b.indexOf(a[l]) >= 0) {

                        return true;

                    }

                }
                return false;

            };

            /**
             * Print a warning message to the console and ensure duplicate warning messages
             * are not printed.
             *
             * @private
             */
            var warnOnceHistory = {};
            exports.warnOnce = function (message) {

                if (!warnOnceHistory[message]) {

                    // console isn't defined in some WebWorkers, see #2558
                    if (typeof console !== 'undefined') {

                        console.warn(message);

                    }
                    warnOnceHistory[message] = true;

                }

            };

            /**
             * Indicates if the provided Points are in a counter clockwise (true) or clockwise (false) order
             *
             * @returns true for a counter clockwise set of points
             */
// http://bryceboe.com/2006/10/23/line-segment-intersection-algorithm/
            exports.isCounterClockwise = function (a, b, c) {

                return (c.y - a.y) * (b.x - a.x) > (b.y - a.y) * (c.x - a.x);

            };

            /**
             * Returns the signed area for the polygon ring.  Postive areas are exterior rings and
             * have a clockwise winding.  Negative areas are interior rings and have a counter clockwise
             * ordering.
             *
             * @param ring Exterior or interior ring
             */
            exports.calculateSignedArea = function (ring) {

                var sum = 0;
                for (var i = 0, len = ring.length, j = len - 1, p1 = (void 0), p2 = (void 0); i < len; j = i++) {

                    p1 = ring[i];
                    p2 = ring[j];
                    sum += (p2.x - p1.x) * (p1.y + p2.y);

                }
                return sum;

            };

            /**
             * Detects closed polygons, first + last point are equal
             *
             * @param points array of points
             * @return true if the points are a closed polygon
             */
            exports.isClosedPolygon = function (points) {

                // If it is 2 points that are the same then it is a point
                // If it is 3 points with start and end the same then it is a line
                if (points.length < 4) {

                    return false;

                }

                var p1 = points[0];
                var p2 = points[points.length - 1];

                if (Math.abs(p1.x - p2.x) > 0 ||
                    Math.abs(p1.y - p2.y) > 0) {

                    return false;

                }

                // polygon simplification can produce polygons with zero area and more than 3 points
                return (Math.abs(exports.calculateSignedArea(points)) > 0.01);

            };

            /**
             * Converts spherical coordinates to cartesian coordinates.
             *
             * @param spherical Spherical coordinates, in [radial, azimuthal, polar]
             * @return cartesian coordinates in [x, y, z]
             */

            exports.sphericalToCartesian = function (ref) {

                var r = ref[0];
                var azimuthal = ref[1];
                var polar = ref[2];

                // We abstract "north"/"up" (compass-wise) to be 0° when really this is 90° (π/2):
                // correct for that here
                azimuthal += 90;

                // Convert azimuthal and polar angles to radians
                azimuthal *= Math.PI / 180;
                polar *= Math.PI / 180;

                return {
                    x: r * Math.cos(azimuthal) * Math.sin(polar),
                    y: r * Math.sin(azimuthal) * Math.sin(polar),
                    z: r * Math.cos(polar)
                };

            };

            /**
             * Parses data from 'Cache-Control' headers.
             *
             * @param cacheControl Value of 'Cache-Control' header
             * @return object containing parsed header info.
             */

            exports.parseCacheControl = function (cacheControl) {

                // Taken from [Wreck](https://github.com/hapijs/wreck)
                var re = /(?:^|(?:\s*\,\s*))([^\x00-\x20\(\)<>@\,;\:\\"\/\[\]\?\=\{\}\x7F]+)(?:\=(?:([^\x00-\x20\(\)<>@\,;\:\\"\/\[\]\?\=\{\}\x7F]+)|(?:\"((?:[^"\\]|\\.)*)\")))?/g;

                var header = {};
                cacheControl.replace(re, function ($0, $1, $2, $3) {

                    var value = $2 || $3;
                    header[$1] = value ? value.toLowerCase() : true;
                    return '';

                });

                if (header['max-age']) {

                    var maxAge = parseInt(header['max-age'], 10);
                    if (isNaN(maxAge)) {

                        delete header['max-age'];

                    }
                    else {

                        header['max-age'] = maxAge;

                    }

                }

                return header;

            };


        }, {'../geo/coordinate': 3, '@mapbox/point-geometry': 1, '@mapbox/unitbezier': 2}],
        12: [function (require, module, exports) {

//      

            var util = require('mapbox-gl/src/util/util');
            var ajax = require('mapbox-gl/src/util/ajax');
            var Evented = require('mapbox-gl/src/util/evented');
            var loadArcGISMapServer = require('./load_arcgis_mapserver');
            var TileBounds = require('mapbox-gl/src/source/tile_bounds');
            var Texture = require('mapbox-gl/src/render/texture');
            var helpers = require('./helpers');


            var ArcGISTiledMapServiceSource = (function (Evented) {

                function ArcGISTiledMapServiceSource(id, options, dispatcher, eventedParent) {

                    Evented.call(this);
                    this.id = id;
                    this.dispatcher = dispatcher;
                    this.setEventedParent(eventedParent);

                    this.type = 'arcgisraster';
                    this.minzoom = 0;
                    this.maxzoom = 22;
                    this.roundZoom = true;
                    this.tileSize = 512;
                    this._loaded = false;

                    this._options = util.extend({}, options);
                    util.extend(this, util.pick(options, ['url', 'scheme', 'tileSize']));

                }

                if (Evented) ArcGISTiledMapServiceSource.__proto__ = Evented;
                ArcGISTiledMapServiceSource.prototype = Object.create(Evented && Evented.prototype);
                ArcGISTiledMapServiceSource.prototype.constructor = ArcGISTiledMapServiceSource;

                ArcGISTiledMapServiceSource.prototype.load = function load() {

                    this.fire('dataloading', {dataType: 'source'});
                    this.fire('data', {dataType: 'source', sourceDataType: 'metadata'});
                    this.fire('data', {dataType: 'source', sourceDataType: 'content'});
                    // loadArcGISMapServer(this._options, (err, metadata) => {
                    //     if (err) {
                    //         this.fire('error', err);
                    //     } else if (metadata) {
                    //         util.extend(this, metadata);
                    //
                    //         if (metadata.bounds) {
                    //             this.tileBounds = new TileBounds(metadata.bounds, this.minzoom, this.maxzoom);
                    //         }
                    //
                    //     // `content` is included here to prevent a race condition where `Style#_updateSources` is called
                    //     // before the TileJSON arrives. this makes sure the tiles needed are loaded once TileJSON arrives
                    //     // ref: https://github.com/mapbox/mapbox-gl-js/pull/4347#discussion_r104418088
                    //     this.fire('data', {dataType: 'source', sourceDataType: 'metadata'});
                    //     this.fire('data', {dataType: 'source', sourceDataType: 'content'});
                    //     }
                    // });

                };

                ArcGISTiledMapServiceSource.prototype.template = function template(str, data) {

                    return str.replace(/\{ *([\w_]+) *\}/g, function (str, key) {

                        let value = data[key];
                        if (!value) {

                            if (key === 'L') {

                                value = ('00' + data.z.toString(16)).slice(-2);

                            } else if (key === 'R') {

                                value = ('00000000' + data.y.toString(16)).slice(-8);

                            } else if (key === 'C') {

                                value = ('00000000' + data.x.toString(16)).slice(-8);

                            }
                            if (value === undefined) {

                                throw new Error('No value provided for variable ' + str);

                            }
                        
                        } else if (typeof value === 'function') {

                            value = value(data);

                        }
                        return value;

                    });

                };

                ArcGISTiledMapServiceSource.prototype.onAdd = function onAdd(map) {

                    // // set the urls
                    // const baseUrl = this.url.split('?')[0];
                    // this.tileUrl = `${baseUrl}/tile/{z}/{y}/{x}`;
                    //
                    // const arcgisonline = new RegExp(/tiles.arcgis(online)?\.com/g);
                    // if (arcgisonline.test(this.url)) {
                    //     this.tileUrl = this.tileUrl.replace('://tiles', '://tiles{s}');
                    //     this.subdomains = ['1', '2', '3', '4'];
                    // }
                    //
                    // if (this.token) {
                    //     this.tileUrl += (`?token=${this.token}`);
                    // }

                    this.map = map;
                    this.load();

                };

                ArcGISTiledMapServiceSource.prototype.serialize = function serialize() {

                    return util.extend({}, this._options);

                };

                ArcGISTiledMapServiceSource.prototype.hasTile = function hasTile(tileID) {

                    return !this.tileBounds || this.tileBounds.contains(tileID.canonical);

                };

                ArcGISTiledMapServiceSource.prototype.loadTile = function loadTile(tile, callback) {

                    var this$1 = this;

                    //convert to ags coords
                    var tilePoint = {
                        z: tile.tileID.overscaledZ,
                        x: tile.tileID.canonical.x,
                        y: tile.tileID.canonical.y
                    };

                    var url = this.template(this.url, {x: tilePoint.x, y: tilePoint.y, z: tilePoint.z});
                    tile.request = ajax.getImage({url: url}, function (err, img) {

                        delete tile.request;

                        if (tile.aborted) {

                            tile.state = 'unloaded';
                            callback(null);

                        } else if (err) {

                            tile.state = 'errored';
                            callback(err);

                        } else if (img) {

                            if (this$1.map._refreshExpiredTiles) {

                                tile.setExpiryData(img);

                            }
                            delete (img


                            ).cacheControl;
                            delete (img


                            ).expires;

                            var context = this$1.map.painter.context;
                            var gl = context.gl;
                            tile.texture = this$1.map.painter.getTileTexture(img.width);
                            if (tile.texture) {

                                tile.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
                                gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, img);

                            } else {

                                tile.texture = new Texture(context, img, gl.RGBA);
                                tile.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);

                                if (context.extTextureFilterAnisotropic) {

                                    gl.texParameterf(gl.TEXTURE_2D, context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, context.extTextureFilterAnisotropicMax);

                                }

                            }
                            gl.generateMipmap(gl.TEXTURE_2D);

                            tile.state = 'loaded';

                            callback(null);

                        }

                    });

                };

                ArcGISTiledMapServiceSource.prototype.abortTile = function abortTile(tile, callback) {

                    if (tile.request) {

                        tile.request.abort();
                        delete tile.request;

                    }
                    callback();

                };

                ArcGISTiledMapServiceSource.prototype.unloadTile = function unloadTile(tile, callback) {

                    if (tile.texture) {

                        this.map.painter.saveTileTexture(tile.texture);

                    }
                    callback();

                };

                ArcGISTiledMapServiceSource.prototype.hasTransition = function hasTransition() {

                    return false;

                };

                return ArcGISTiledMapServiceSource;

            }(Evented));

            module.exports = ArcGISTiledMapServiceSource;

        }, {
            './helpers': 13,
            './load_arcgis_mapserver': 15,
            'mapbox-gl/src/render/texture': 6,
            'mapbox-gl/src/source/tile_bounds': 7,
            'mapbox-gl/src/util/ajax': 8,
            'mapbox-gl/src/util/evented': 10,
            'mapbox-gl/src/util/util': 11
        }],
        13: [function (require, module, exports) {

//From https://github.com/Leaflet/Leaflet/blob/master/src/core/Util.js
            var _templateRe = /\{ *([\w_]+) *\}/g;
            var _template = function (str, data) {

                return str.replace(_templateRe, function (str, key) {

                    var value = data[key];

                    if (value === undefined) {

                        throw new Error(('No value provided for variable ' + str));

                    } else if (typeof value === 'function') {

                        value = value(data);

                    }
                    return value;

                });

            };

//From https://github.com/Leaflet/Leaflet/blob/master/src/layer/tile/TileLayer.js
            var _getSubdomain = function (tilePoint, subdomains) {

                if (subdomains) {

                    var index = Math.abs(tilePoint.x + tilePoint.y) % subdomains.length;
                    return subdomains[index];

                }
                return null;

            };

            module.exports = {
                _template: _template,
                _getSubdomain: _getSubdomain
            };


        }, {}],
        14: [function (require, module, exports) {

            var ArcGISTiledMapServiceSource = require('./arcgis_tiled_map_service_source');
            module.exports = ArcGISTiledMapServiceSource;

        }, {'./arcgis_tiled_map_service_source': 12}],
        15: [function (require, module, exports) {

            'use strict';
            module.exports = function (options, callback) {

                callback(null, {});

            };


        }, {}]
    }, {}, [14])(14);

});

//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJub2RlX21vZHVsZXMvQG1hcGJveC9wb2ludC1nZW9tZXRyeS9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9AbWFwYm94L3VuaXRiZXppZXIvaW5kZXguanMiLCJFOi9tYXBib3gtZ2wtYXJjZ2lzLXRpbGVkLW1hcC1zZXJ2aWNlLW1hc3Rlci9ub2RlX21vZHVsZXMvbWFwYm94LWdsL3NyYy9nZW8vY29vcmRpbmF0ZS5qcyIsIkU6L21hcGJveC1nbC1hcmNnaXMtdGlsZWQtbWFwLXNlcnZpY2UtbWFzdGVyL25vZGVfbW9kdWxlcy9tYXBib3gtZ2wvc3JjL2dlby9sbmdfbGF0LmpzIiwiRTovbWFwYm94LWdsLWFyY2dpcy10aWxlZC1tYXAtc2VydmljZS1tYXN0ZXIvbm9kZV9tb2R1bGVzL21hcGJveC1nbC9zcmMvZ2VvL2xuZ19sYXRfYm91bmRzLmpzIiwiRTovbWFwYm94LWdsLWFyY2dpcy10aWxlZC1tYXAtc2VydmljZS1tYXN0ZXIvbm9kZV9tb2R1bGVzL21hcGJveC1nbC9zcmMvcmVuZGVyL3RleHR1cmUuanMiLCJFOi9tYXBib3gtZ2wtYXJjZ2lzLXRpbGVkLW1hcC1zZXJ2aWNlLW1hc3Rlci9ub2RlX21vZHVsZXMvbWFwYm94LWdsL3NyYy9zb3VyY2UvdGlsZV9ib3VuZHMuanMiLCJFOi9tYXBib3gtZ2wtYXJjZ2lzLXRpbGVkLW1hcC1zZXJ2aWNlLW1hc3Rlci9ub2RlX21vZHVsZXMvbWFwYm94LWdsL3NyYy91dGlsL2FqYXguanMiLCJFOi9tYXBib3gtZ2wtYXJjZ2lzLXRpbGVkLW1hcC1zZXJ2aWNlLW1hc3Rlci9ub2RlX21vZHVsZXMvbWFwYm94LWdsL3NyYy91dGlsL2Jyb3dzZXIvd2luZG93LmpzIiwiRTovbWFwYm94LWdsLWFyY2dpcy10aWxlZC1tYXAtc2VydmljZS1tYXN0ZXIvbm9kZV9tb2R1bGVzL21hcGJveC1nbC9zcmMvdXRpbC9ldmVudGVkLmpzIiwiRTovbWFwYm94LWdsLWFyY2dpcy10aWxlZC1tYXAtc2VydmljZS1tYXN0ZXIvbm9kZV9tb2R1bGVzL21hcGJveC1nbC9zcmMvdXRpbC91dGlsLmpzIiwiRTovbWFwYm94LWdsLWFyY2dpcy10aWxlZC1tYXAtc2VydmljZS1tYXN0ZXIvc3JjL2FyY2dpc190aWxlZF9tYXBfc2VydmljZV9zb3VyY2UuanMiLCJFOi9tYXBib3gtZ2wtYXJjZ2lzLXRpbGVkLW1hcC1zZXJ2aWNlLW1hc3Rlci9zcmMvaGVscGVycy5qcyIsIkU6L21hcGJveC1nbC1hcmNnaXMtdGlsZWQtbWFwLXNlcnZpY2UtbWFzdGVyL3NyYy9pbmRleC5qcyIsIkU6L21hcGJveC1nbC1hcmNnaXMtdGlsZWQtbWFwLXNlcnZpY2UtbWFzdGVyL3NyYy9sb2FkX2FyY2dpc19tYXBzZXJ2ZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4VEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDekdBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQ0FBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUNBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQ0FBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUNBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUNBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQ0FBOzs7Ozs7O0FDQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQ0FBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQ0FBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQ0FBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUNBQTs7OztBQ0FBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24oKXtmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc31yZXR1cm4gZX0pKCkiLCIndXNlIHN0cmljdCc7XG5cbm1vZHVsZS5leHBvcnRzID0gUG9pbnQ7XG5cbi8qKlxuICogQSBzdGFuZGFsb25lIHBvaW50IGdlb21ldHJ5IHdpdGggdXNlZnVsIGFjY2Vzc29yLCBjb21wYXJpc29uLCBhbmRcbiAqIG1vZGlmaWNhdGlvbiBtZXRob2RzLlxuICpcbiAqIEBjbGFzcyBQb2ludFxuICogQHBhcmFtIHtOdW1iZXJ9IHggdGhlIHgtY29vcmRpbmF0ZS4gdGhpcyBjb3VsZCBiZSBsb25naXR1ZGUgb3Igc2NyZWVuXG4gKiBwaXhlbHMsIG9yIGFueSBvdGhlciBzb3J0IG9mIHVuaXQuXG4gKiBAcGFyYW0ge051bWJlcn0geSB0aGUgeS1jb29yZGluYXRlLiB0aGlzIGNvdWxkIGJlIGxhdGl0dWRlIG9yIHNjcmVlblxuICogcGl4ZWxzLCBvciBhbnkgb3RoZXIgc29ydCBvZiB1bml0LlxuICogQGV4YW1wbGVcbiAqIHZhciBwb2ludCA9IG5ldyBQb2ludCgtNzcsIDM4KTtcbiAqL1xuZnVuY3Rpb24gUG9pbnQoeCwgeSkge1xuICAgIHRoaXMueCA9IHg7XG4gICAgdGhpcy55ID0geTtcbn1cblxuUG9pbnQucHJvdG90eXBlID0ge1xuXG4gICAgLyoqXG4gICAgICogQ2xvbmUgdGhpcyBwb2ludCwgcmV0dXJuaW5nIGEgbmV3IHBvaW50IHRoYXQgY2FuIGJlIG1vZGlmaWVkXG4gICAgICogd2l0aG91dCBhZmZlY3RpbmcgdGhlIG9sZCBvbmUuXG4gICAgICogQHJldHVybiB7UG9pbnR9IHRoZSBjbG9uZVxuICAgICAqL1xuICAgIGNsb25lOiBmdW5jdGlvbigpIHsgcmV0dXJuIG5ldyBQb2ludCh0aGlzLngsIHRoaXMueSk7IH0sXG5cbiAgICAvKipcbiAgICAgKiBBZGQgdGhpcyBwb2ludCdzIHggJiB5IGNvb3JkaW5hdGVzIHRvIGFub3RoZXIgcG9pbnQsXG4gICAgICogeWllbGRpbmcgYSBuZXcgcG9pbnQuXG4gICAgICogQHBhcmFtIHtQb2ludH0gcCB0aGUgb3RoZXIgcG9pbnRcbiAgICAgKiBAcmV0dXJuIHtQb2ludH0gb3V0cHV0IHBvaW50XG4gICAgICovXG4gICAgYWRkOiAgICAgZnVuY3Rpb24ocCkgeyByZXR1cm4gdGhpcy5jbG9uZSgpLl9hZGQocCk7IH0sXG5cbiAgICAvKipcbiAgICAgKiBTdWJ0cmFjdCB0aGlzIHBvaW50J3MgeCAmIHkgY29vcmRpbmF0ZXMgdG8gZnJvbSBwb2ludCxcbiAgICAgKiB5aWVsZGluZyBhIG5ldyBwb2ludC5cbiAgICAgKiBAcGFyYW0ge1BvaW50fSBwIHRoZSBvdGhlciBwb2ludFxuICAgICAqIEByZXR1cm4ge1BvaW50fSBvdXRwdXQgcG9pbnRcbiAgICAgKi9cbiAgICBzdWI6ICAgICBmdW5jdGlvbihwKSB7IHJldHVybiB0aGlzLmNsb25lKCkuX3N1YihwKTsgfSxcblxuICAgIC8qKlxuICAgICAqIE11bHRpcGx5IHRoaXMgcG9pbnQncyB4ICYgeSBjb29yZGluYXRlcyBieSBwb2ludCxcbiAgICAgKiB5aWVsZGluZyBhIG5ldyBwb2ludC5cbiAgICAgKiBAcGFyYW0ge1BvaW50fSBwIHRoZSBvdGhlciBwb2ludFxuICAgICAqIEByZXR1cm4ge1BvaW50fSBvdXRwdXQgcG9pbnRcbiAgICAgKi9cbiAgICBtdWx0QnlQb2ludDogICAgZnVuY3Rpb24ocCkgeyByZXR1cm4gdGhpcy5jbG9uZSgpLl9tdWx0QnlQb2ludChwKTsgfSxcblxuICAgIC8qKlxuICAgICAqIERpdmlkZSB0aGlzIHBvaW50J3MgeCAmIHkgY29vcmRpbmF0ZXMgYnkgcG9pbnQsXG4gICAgICogeWllbGRpbmcgYSBuZXcgcG9pbnQuXG4gICAgICogQHBhcmFtIHtQb2ludH0gcCB0aGUgb3RoZXIgcG9pbnRcbiAgICAgKiBAcmV0dXJuIHtQb2ludH0gb3V0cHV0IHBvaW50XG4gICAgICovXG4gICAgZGl2QnlQb2ludDogICAgIGZ1bmN0aW9uKHApIHsgcmV0dXJuIHRoaXMuY2xvbmUoKS5fZGl2QnlQb2ludChwKTsgfSxcblxuICAgIC8qKlxuICAgICAqIE11bHRpcGx5IHRoaXMgcG9pbnQncyB4ICYgeSBjb29yZGluYXRlcyBieSBhIGZhY3RvcixcbiAgICAgKiB5aWVsZGluZyBhIG5ldyBwb2ludC5cbiAgICAgKiBAcGFyYW0ge1BvaW50fSBrIGZhY3RvclxuICAgICAqIEByZXR1cm4ge1BvaW50fSBvdXRwdXQgcG9pbnRcbiAgICAgKi9cbiAgICBtdWx0OiAgICBmdW5jdGlvbihrKSB7IHJldHVybiB0aGlzLmNsb25lKCkuX211bHQoayk7IH0sXG5cbiAgICAvKipcbiAgICAgKiBEaXZpZGUgdGhpcyBwb2ludCdzIHggJiB5IGNvb3JkaW5hdGVzIGJ5IGEgZmFjdG9yLFxuICAgICAqIHlpZWxkaW5nIGEgbmV3IHBvaW50LlxuICAgICAqIEBwYXJhbSB7UG9pbnR9IGsgZmFjdG9yXG4gICAgICogQHJldHVybiB7UG9pbnR9IG91dHB1dCBwb2ludFxuICAgICAqL1xuICAgIGRpdjogICAgIGZ1bmN0aW9uKGspIHsgcmV0dXJuIHRoaXMuY2xvbmUoKS5fZGl2KGspOyB9LFxuXG4gICAgLyoqXG4gICAgICogUm90YXRlIHRoaXMgcG9pbnQgYXJvdW5kIHRoZSAwLCAwIG9yaWdpbiBieSBhbiBhbmdsZSBhLFxuICAgICAqIGdpdmVuIGluIHJhZGlhbnNcbiAgICAgKiBAcGFyYW0ge051bWJlcn0gYSBhbmdsZSB0byByb3RhdGUgYXJvdW5kLCBpbiByYWRpYW5zXG4gICAgICogQHJldHVybiB7UG9pbnR9IG91dHB1dCBwb2ludFxuICAgICAqL1xuICAgIHJvdGF0ZTogIGZ1bmN0aW9uKGEpIHsgcmV0dXJuIHRoaXMuY2xvbmUoKS5fcm90YXRlKGEpOyB9LFxuXG4gICAgLyoqXG4gICAgICogUm90YXRlIHRoaXMgcG9pbnQgYXJvdW5kIHAgcG9pbnQgYnkgYW4gYW5nbGUgYSxcbiAgICAgKiBnaXZlbiBpbiByYWRpYW5zXG4gICAgICogQHBhcmFtIHtOdW1iZXJ9IGEgYW5nbGUgdG8gcm90YXRlIGFyb3VuZCwgaW4gcmFkaWFuc1xuICAgICAqIEBwYXJhbSB7UG9pbnR9IHAgUG9pbnQgdG8gcm90YXRlIGFyb3VuZFxuICAgICAqIEByZXR1cm4ge1BvaW50fSBvdXRwdXQgcG9pbnRcbiAgICAgKi9cbiAgICByb3RhdGVBcm91bmQ6ICBmdW5jdGlvbihhLHApIHsgcmV0dXJuIHRoaXMuY2xvbmUoKS5fcm90YXRlQXJvdW5kKGEscCk7IH0sXG5cbiAgICAvKipcbiAgICAgKiBNdWx0aXBseSB0aGlzIHBvaW50IGJ5IGEgNHgxIHRyYW5zZm9ybWF0aW9uIG1hdHJpeFxuICAgICAqIEBwYXJhbSB7QXJyYXk8TnVtYmVyPn0gbSB0cmFuc2Zvcm1hdGlvbiBtYXRyaXhcbiAgICAgKiBAcmV0dXJuIHtQb2ludH0gb3V0cHV0IHBvaW50XG4gICAgICovXG4gICAgbWF0TXVsdDogZnVuY3Rpb24obSkgeyByZXR1cm4gdGhpcy5jbG9uZSgpLl9tYXRNdWx0KG0pOyB9LFxuXG4gICAgLyoqXG4gICAgICogQ2FsY3VsYXRlIHRoaXMgcG9pbnQgYnV0IGFzIGEgdW5pdCB2ZWN0b3IgZnJvbSAwLCAwLCBtZWFuaW5nXG4gICAgICogdGhhdCB0aGUgZGlzdGFuY2UgZnJvbSB0aGUgcmVzdWx0aW5nIHBvaW50IHRvIHRoZSAwLCAwXG4gICAgICogY29vcmRpbmF0ZSB3aWxsIGJlIGVxdWFsIHRvIDEgYW5kIHRoZSBhbmdsZSBmcm9tIHRoZSByZXN1bHRpbmdcbiAgICAgKiBwb2ludCB0byB0aGUgMCwgMCBjb29yZGluYXRlIHdpbGwgYmUgdGhlIHNhbWUgYXMgYmVmb3JlLlxuICAgICAqIEByZXR1cm4ge1BvaW50fSB1bml0IHZlY3RvciBwb2ludFxuICAgICAqL1xuICAgIHVuaXQ6ICAgIGZ1bmN0aW9uKCkgeyByZXR1cm4gdGhpcy5jbG9uZSgpLl91bml0KCk7IH0sXG5cbiAgICAvKipcbiAgICAgKiBDb21wdXRlIGEgcGVycGVuZGljdWxhciBwb2ludCwgd2hlcmUgdGhlIG5ldyB5IGNvb3JkaW5hdGVcbiAgICAgKiBpcyB0aGUgb2xkIHggY29vcmRpbmF0ZSBhbmQgdGhlIG5ldyB4IGNvb3JkaW5hdGUgaXMgdGhlIG9sZCB5XG4gICAgICogY29vcmRpbmF0ZSBtdWx0aXBsaWVkIGJ5IC0xXG4gICAgICogQHJldHVybiB7UG9pbnR9IHBlcnBlbmRpY3VsYXIgcG9pbnRcbiAgICAgKi9cbiAgICBwZXJwOiAgICBmdW5jdGlvbigpIHsgcmV0dXJuIHRoaXMuY2xvbmUoKS5fcGVycCgpOyB9LFxuXG4gICAgLyoqXG4gICAgICogUmV0dXJuIGEgdmVyc2lvbiBvZiB0aGlzIHBvaW50IHdpdGggdGhlIHggJiB5IGNvb3JkaW5hdGVzXG4gICAgICogcm91bmRlZCB0byBpbnRlZ2Vycy5cbiAgICAgKiBAcmV0dXJuIHtQb2ludH0gcm91bmRlZCBwb2ludFxuICAgICAqL1xuICAgIHJvdW5kOiAgIGZ1bmN0aW9uKCkgeyByZXR1cm4gdGhpcy5jbG9uZSgpLl9yb3VuZCgpOyB9LFxuXG4gICAgLyoqXG4gICAgICogUmV0dXJuIHRoZSBtYWdpdHVkZSBvZiB0aGlzIHBvaW50OiB0aGlzIGlzIHRoZSBFdWNsaWRlYW5cbiAgICAgKiBkaXN0YW5jZSBmcm9tIHRoZSAwLCAwIGNvb3JkaW5hdGUgdG8gdGhpcyBwb2ludCdzIHggYW5kIHlcbiAgICAgKiBjb29yZGluYXRlcy5cbiAgICAgKiBAcmV0dXJuIHtOdW1iZXJ9IG1hZ25pdHVkZVxuICAgICAqL1xuICAgIG1hZzogZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiBNYXRoLnNxcnQodGhpcy54ICogdGhpcy54ICsgdGhpcy55ICogdGhpcy55KTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogSnVkZ2Ugd2hldGhlciB0aGlzIHBvaW50IGlzIGVxdWFsIHRvIGFub3RoZXIgcG9pbnQsIHJldHVybmluZ1xuICAgICAqIHRydWUgb3IgZmFsc2UuXG4gICAgICogQHBhcmFtIHtQb2ludH0gb3RoZXIgdGhlIG90aGVyIHBvaW50XG4gICAgICogQHJldHVybiB7Ym9vbGVhbn0gd2hldGhlciB0aGUgcG9pbnRzIGFyZSBlcXVhbFxuICAgICAqL1xuICAgIGVxdWFsczogZnVuY3Rpb24ob3RoZXIpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMueCA9PT0gb3RoZXIueCAmJlxuICAgICAgICAgICAgICAgdGhpcy55ID09PSBvdGhlci55O1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBDYWxjdWxhdGUgdGhlIGRpc3RhbmNlIGZyb20gdGhpcyBwb2ludCB0byBhbm90aGVyIHBvaW50XG4gICAgICogQHBhcmFtIHtQb2ludH0gcCB0aGUgb3RoZXIgcG9pbnRcbiAgICAgKiBAcmV0dXJuIHtOdW1iZXJ9IGRpc3RhbmNlXG4gICAgICovXG4gICAgZGlzdDogZnVuY3Rpb24ocCkge1xuICAgICAgICByZXR1cm4gTWF0aC5zcXJ0KHRoaXMuZGlzdFNxcihwKSk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIENhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgZnJvbSB0aGlzIHBvaW50IHRvIGFub3RoZXIgcG9pbnQsXG4gICAgICogd2l0aG91dCB0aGUgc3F1YXJlIHJvb3Qgc3RlcC4gVXNlZnVsIGlmIHlvdSdyZSBjb21wYXJpbmdcbiAgICAgKiByZWxhdGl2ZSBkaXN0YW5jZXMuXG4gICAgICogQHBhcmFtIHtQb2ludH0gcCB0aGUgb3RoZXIgcG9pbnRcbiAgICAgKiBAcmV0dXJuIHtOdW1iZXJ9IGRpc3RhbmNlXG4gICAgICovXG4gICAgZGlzdFNxcjogZnVuY3Rpb24ocCkge1xuICAgICAgICB2YXIgZHggPSBwLnggLSB0aGlzLngsXG4gICAgICAgICAgICBkeSA9IHAueSAtIHRoaXMueTtcbiAgICAgICAgcmV0dXJuIGR4ICogZHggKyBkeSAqIGR5O1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBHZXQgdGhlIGFuZ2xlIGZyb20gdGhlIDAsIDAgY29vcmRpbmF0ZSB0byB0aGlzIHBvaW50LCBpbiByYWRpYW5zXG4gICAgICogY29vcmRpbmF0ZXMuXG4gICAgICogQHJldHVybiB7TnVtYmVyfSBhbmdsZVxuICAgICAqL1xuICAgIGFuZ2xlOiBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIE1hdGguYXRhbjIodGhpcy55LCB0aGlzLngpO1xuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBHZXQgdGhlIGFuZ2xlIGZyb20gdGhpcyBwb2ludCB0byBhbm90aGVyIHBvaW50LCBpbiByYWRpYW5zXG4gICAgICogQHBhcmFtIHtQb2ludH0gYiB0aGUgb3RoZXIgcG9pbnRcbiAgICAgKiBAcmV0dXJuIHtOdW1iZXJ9IGFuZ2xlXG4gICAgICovXG4gICAgYW5nbGVUbzogZnVuY3Rpb24oYikge1xuICAgICAgICByZXR1cm4gTWF0aC5hdGFuMih0aGlzLnkgLSBiLnksIHRoaXMueCAtIGIueCk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEdldCB0aGUgYW5nbGUgYmV0d2VlbiB0aGlzIHBvaW50IGFuZCBhbm90aGVyIHBvaW50LCBpbiByYWRpYW5zXG4gICAgICogQHBhcmFtIHtQb2ludH0gYiB0aGUgb3RoZXIgcG9pbnRcbiAgICAgKiBAcmV0dXJuIHtOdW1iZXJ9IGFuZ2xlXG4gICAgICovXG4gICAgYW5nbGVXaXRoOiBmdW5jdGlvbihiKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmFuZ2xlV2l0aFNlcChiLngsIGIueSk7XG4gICAgfSxcblxuICAgIC8qXG4gICAgICogRmluZCB0aGUgYW5nbGUgb2YgdGhlIHR3byB2ZWN0b3JzLCBzb2x2aW5nIHRoZSBmb3JtdWxhIGZvclxuICAgICAqIHRoZSBjcm9zcyBwcm9kdWN0IGEgeCBiID0gfGF8fGJ8c2luKM64KSBmb3IgzrguXG4gICAgICogQHBhcmFtIHtOdW1iZXJ9IHggdGhlIHgtY29vcmRpbmF0ZVxuICAgICAqIEBwYXJhbSB7TnVtYmVyfSB5IHRoZSB5LWNvb3JkaW5hdGVcbiAgICAgKiBAcmV0dXJuIHtOdW1iZXJ9IHRoZSBhbmdsZSBpbiByYWRpYW5zXG4gICAgICovXG4gICAgYW5nbGVXaXRoU2VwOiBmdW5jdGlvbih4LCB5KSB7XG4gICAgICAgIHJldHVybiBNYXRoLmF0YW4yKFxuICAgICAgICAgICAgdGhpcy54ICogeSAtIHRoaXMueSAqIHgsXG4gICAgICAgICAgICB0aGlzLnggKiB4ICsgdGhpcy55ICogeSk7XG4gICAgfSxcblxuICAgIF9tYXRNdWx0OiBmdW5jdGlvbihtKSB7XG4gICAgICAgIHZhciB4ID0gbVswXSAqIHRoaXMueCArIG1bMV0gKiB0aGlzLnksXG4gICAgICAgICAgICB5ID0gbVsyXSAqIHRoaXMueCArIG1bM10gKiB0aGlzLnk7XG4gICAgICAgIHRoaXMueCA9IHg7XG4gICAgICAgIHRoaXMueSA9IHk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG5cbiAgICBfYWRkOiBmdW5jdGlvbihwKSB7XG4gICAgICAgIHRoaXMueCArPSBwLng7XG4gICAgICAgIHRoaXMueSArPSBwLnk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG5cbiAgICBfc3ViOiBmdW5jdGlvbihwKSB7XG4gICAgICAgIHRoaXMueCAtPSBwLng7XG4gICAgICAgIHRoaXMueSAtPSBwLnk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG5cbiAgICBfbXVsdDogZnVuY3Rpb24oaykge1xuICAgICAgICB0aGlzLnggKj0gaztcbiAgICAgICAgdGhpcy55ICo9IGs7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG5cbiAgICBfZGl2OiBmdW5jdGlvbihrKSB7XG4gICAgICAgIHRoaXMueCAvPSBrO1xuICAgICAgICB0aGlzLnkgLz0gaztcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIF9tdWx0QnlQb2ludDogZnVuY3Rpb24ocCkge1xuICAgICAgICB0aGlzLnggKj0gcC54O1xuICAgICAgICB0aGlzLnkgKj0gcC55O1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgX2RpdkJ5UG9pbnQ6IGZ1bmN0aW9uKHApIHtcbiAgICAgICAgdGhpcy54IC89IHAueDtcbiAgICAgICAgdGhpcy55IC89IHAueTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIF91bml0OiBmdW5jdGlvbigpIHtcbiAgICAgICAgdGhpcy5fZGl2KHRoaXMubWFnKCkpO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgX3BlcnA6IGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgeSA9IHRoaXMueTtcbiAgICAgICAgdGhpcy55ID0gdGhpcy54O1xuICAgICAgICB0aGlzLnggPSAteTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIF9yb3RhdGU6IGZ1bmN0aW9uKGFuZ2xlKSB7XG4gICAgICAgIHZhciBjb3MgPSBNYXRoLmNvcyhhbmdsZSksXG4gICAgICAgICAgICBzaW4gPSBNYXRoLnNpbihhbmdsZSksXG4gICAgICAgICAgICB4ID0gY29zICogdGhpcy54IC0gc2luICogdGhpcy55LFxuICAgICAgICAgICAgeSA9IHNpbiAqIHRoaXMueCArIGNvcyAqIHRoaXMueTtcbiAgICAgICAgdGhpcy54ID0geDtcbiAgICAgICAgdGhpcy55ID0geTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcblxuICAgIF9yb3RhdGVBcm91bmQ6IGZ1bmN0aW9uKGFuZ2xlLCBwKSB7XG4gICAgICAgIHZhciBjb3MgPSBNYXRoLmNvcyhhbmdsZSksXG4gICAgICAgICAgICBzaW4gPSBNYXRoLnNpbihhbmdsZSksXG4gICAgICAgICAgICB4ID0gcC54ICsgY29zICogKHRoaXMueCAtIHAueCkgLSBzaW4gKiAodGhpcy55IC0gcC55KSxcbiAgICAgICAgICAgIHkgPSBwLnkgKyBzaW4gKiAodGhpcy54IC0gcC54KSArIGNvcyAqICh0aGlzLnkgLSBwLnkpO1xuICAgICAgICB0aGlzLnggPSB4O1xuICAgICAgICB0aGlzLnkgPSB5O1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuXG4gICAgX3JvdW5kOiBmdW5jdGlvbigpIHtcbiAgICAgICAgdGhpcy54ID0gTWF0aC5yb3VuZCh0aGlzLngpO1xuICAgICAgICB0aGlzLnkgPSBNYXRoLnJvdW5kKHRoaXMueSk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cbn07XG5cbi8qKlxuICogQ29uc3RydWN0IGEgcG9pbnQgZnJvbSBhbiBhcnJheSBpZiBuZWNlc3NhcnksIG90aGVyd2lzZSBpZiB0aGUgaW5wdXRcbiAqIGlzIGFscmVhZHkgYSBQb2ludCwgb3IgYW4gdW5rbm93biB0eXBlLCByZXR1cm4gaXQgdW5jaGFuZ2VkXG4gKiBAcGFyYW0ge0FycmF5PE51bWJlcj58UG9pbnR8Kn0gYSBhbnkga2luZCBvZiBpbnB1dCB2YWx1ZVxuICogQHJldHVybiB7UG9pbnR9IGNvbnN0cnVjdGVkIHBvaW50LCBvciBwYXNzZWQtdGhyb3VnaCB2YWx1ZS5cbiAqIEBleGFtcGxlXG4gKiAvLyB0aGlzXG4gKiB2YXIgcG9pbnQgPSBQb2ludC5jb252ZXJ0KFswLCAxXSk7XG4gKiAvLyBpcyBlcXVpdmFsZW50IHRvXG4gKiB2YXIgcG9pbnQgPSBuZXcgUG9pbnQoMCwgMSk7XG4gKi9cblBvaW50LmNvbnZlcnQgPSBmdW5jdGlvbiAoYSkge1xuICAgIGlmIChhIGluc3RhbmNlb2YgUG9pbnQpIHtcbiAgICAgICAgcmV0dXJuIGE7XG4gICAgfVxuICAgIGlmIChBcnJheS5pc0FycmF5KGEpKSB7XG4gICAgICAgIHJldHVybiBuZXcgUG9pbnQoYVswXSwgYVsxXSk7XG4gICAgfVxuICAgIHJldHVybiBhO1xufTtcbiIsIi8qXG4gKiBDb3B5cmlnaHQgKEMpIDIwMDggQXBwbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICpcbiAqIFJlZGlzdHJpYnV0aW9uIGFuZCB1c2UgaW4gc291cmNlIGFuZCBiaW5hcnkgZm9ybXMsIHdpdGggb3Igd2l0aG91dFxuICogbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zXG4gKiBhcmUgbWV0OlxuICogMS4gUmVkaXN0cmlidXRpb25zIG9mIHNvdXJjZSBjb2RlIG11c3QgcmV0YWluIHRoZSBhYm92ZSBjb3B5cmlnaHRcbiAqICAgIG5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lci5cbiAqIDIuIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0XG4gKiAgICBub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIgaW4gdGhlXG4gKiAgICBkb2N1bWVudGF0aW9uIGFuZC9vciBvdGhlciBtYXRlcmlhbHMgcHJvdmlkZWQgd2l0aCB0aGUgZGlzdHJpYnV0aW9uLlxuICpcbiAqIFRISVMgU09GVFdBUkUgSVMgUFJPVklERUQgQlkgQVBQTEUgSU5DLiBgYEFTIElTJycgQU5EIEFOWVxuICogRVhQUkVTUyBPUiBJTVBMSUVEIFdBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVCBMSU1JVEVEIFRPLCBUSEVcbiAqIElNUExJRUQgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFkgQU5EIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUlxuICogUFVSUE9TRSBBUkUgRElTQ0xBSU1FRC4gIElOIE5PIEVWRU5UIFNIQUxMIEFQUExFIElOQy4gT1JcbiAqIENPTlRSSUJVVE9SUyBCRSBMSUFCTEUgRk9SIEFOWSBESVJFQ1QsIElORElSRUNULCBJTkNJREVOVEFMLCBTUEVDSUFMLFxuICogRVhFTVBMQVJZLCBPUiBDT05TRVFVRU5USUFMIERBTUFHRVMgKElOQ0xVRElORywgQlVUIE5PVCBMSU1JVEVEIFRPLFxuICogUFJPQ1VSRU1FTlQgT0YgU1VCU1RJVFVURSBHT09EUyBPUiBTRVJWSUNFUzsgTE9TUyBPRiBVU0UsIERBVEEsIE9SXG4gKiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIgQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZXG4gKiBPRiBMSUFCSUxJVFksIFdIRVRIRVIgSU4gQ09OVFJBQ1QsIFNUUklDVCBMSUFCSUxJVFksIE9SIFRPUlRcbiAqIChJTkNMVURJTkcgTkVHTElHRU5DRSBPUiBPVEhFUldJU0UpIEFSSVNJTkcgSU4gQU5ZIFdBWSBPVVQgT0YgVEhFIFVTRVxuICogT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRSBQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS5cbiAqXG4gKiBQb3J0ZWQgZnJvbSBXZWJraXRcbiAqIGh0dHA6Ly9zdm4ud2Via2l0Lm9yZy9yZXBvc2l0b3J5L3dlYmtpdC90cnVuay9Tb3VyY2UvV2ViQ29yZS9wbGF0Zm9ybS9ncmFwaGljcy9Vbml0QmV6aWVyLmhcbiAqL1xuXG5tb2R1bGUuZXhwb3J0cyA9IFVuaXRCZXppZXI7XG5cbmZ1bmN0aW9uIFVuaXRCZXppZXIocDF4LCBwMXksIHAyeCwgcDJ5KSB7XG4gICAgLy8gQ2FsY3VsYXRlIHRoZSBwb2x5bm9taWFsIGNvZWZmaWNpZW50cywgaW1wbGljaXQgZmlyc3QgYW5kIGxhc3QgY29udHJvbCBwb2ludHMgYXJlICgwLDApIGFuZCAoMSwxKS5cbiAgICB0aGlzLmN4ID0gMy4wICogcDF4O1xuICAgIHRoaXMuYnggPSAzLjAgKiAocDJ4IC0gcDF4KSAtIHRoaXMuY3g7XG4gICAgdGhpcy5heCA9IDEuMCAtIHRoaXMuY3ggLSB0aGlzLmJ4O1xuXG4gICAgdGhpcy5jeSA9IDMuMCAqIHAxeTtcbiAgICB0aGlzLmJ5ID0gMy4wICogKHAyeSAtIHAxeSkgLSB0aGlzLmN5O1xuICAgIHRoaXMuYXkgPSAxLjAgLSB0aGlzLmN5IC0gdGhpcy5ieTtcblxuICAgIHRoaXMucDF4ID0gcDF4O1xuICAgIHRoaXMucDF5ID0gcDJ5O1xuICAgIHRoaXMucDJ4ID0gcDJ4O1xuICAgIHRoaXMucDJ5ID0gcDJ5O1xufVxuXG5Vbml0QmV6aWVyLnByb3RvdHlwZS5zYW1wbGVDdXJ2ZVggPSBmdW5jdGlvbih0KSB7XG4gICAgLy8gYGF4IHReMyArIGJ4IHReMiArIGN4IHQnIGV4cGFuZGVkIHVzaW5nIEhvcm5lcidzIHJ1bGUuXG4gICAgcmV0dXJuICgodGhpcy5heCAqIHQgKyB0aGlzLmJ4KSAqIHQgKyB0aGlzLmN4KSAqIHQ7XG59O1xuXG5Vbml0QmV6aWVyLnByb3RvdHlwZS5zYW1wbGVDdXJ2ZVkgPSBmdW5jdGlvbih0KSB7XG4gICAgcmV0dXJuICgodGhpcy5heSAqIHQgKyB0aGlzLmJ5KSAqIHQgKyB0aGlzLmN5KSAqIHQ7XG59O1xuXG5Vbml0QmV6aWVyLnByb3RvdHlwZS5zYW1wbGVDdXJ2ZURlcml2YXRpdmVYID0gZnVuY3Rpb24odCkge1xuICAgIHJldHVybiAoMy4wICogdGhpcy5heCAqIHQgKyAyLjAgKiB0aGlzLmJ4KSAqIHQgKyB0aGlzLmN4O1xufTtcblxuVW5pdEJlemllci5wcm90b3R5cGUuc29sdmVDdXJ2ZVggPSBmdW5jdGlvbih4LCBlcHNpbG9uKSB7XG4gICAgaWYgKHR5cGVvZiBlcHNpbG9uID09PSAndW5kZWZpbmVkJykgZXBzaWxvbiA9IDFlLTY7XG5cbiAgICB2YXIgdDAsIHQxLCB0MiwgeDIsIGk7XG5cbiAgICAvLyBGaXJzdCB0cnkgYSBmZXcgaXRlcmF0aW9ucyBvZiBOZXd0b24ncyBtZXRob2QgLS0gbm9ybWFsbHkgdmVyeSBmYXN0LlxuICAgIGZvciAodDIgPSB4LCBpID0gMDsgaSA8IDg7IGkrKykge1xuXG4gICAgICAgIHgyID0gdGhpcy5zYW1wbGVDdXJ2ZVgodDIpIC0geDtcbiAgICAgICAgaWYgKE1hdGguYWJzKHgyKSA8IGVwc2lsb24pIHJldHVybiB0MjtcblxuICAgICAgICB2YXIgZDIgPSB0aGlzLnNhbXBsZUN1cnZlRGVyaXZhdGl2ZVgodDIpO1xuICAgICAgICBpZiAoTWF0aC5hYnMoZDIpIDwgMWUtNikgYnJlYWs7XG5cbiAgICAgICAgdDIgPSB0MiAtIHgyIC8gZDI7XG4gICAgfVxuXG4gICAgLy8gRmFsbCBiYWNrIHRvIHRoZSBiaXNlY3Rpb24gbWV0aG9kIGZvciByZWxpYWJpbGl0eS5cbiAgICB0MCA9IDAuMDtcbiAgICB0MSA9IDEuMDtcbiAgICB0MiA9IHg7XG5cbiAgICBpZiAodDIgPCB0MCkgcmV0dXJuIHQwO1xuICAgIGlmICh0MiA+IHQxKSByZXR1cm4gdDE7XG5cbiAgICB3aGlsZSAodDAgPCB0MSkge1xuXG4gICAgICAgIHgyID0gdGhpcy5zYW1wbGVDdXJ2ZVgodDIpO1xuICAgICAgICBpZiAoTWF0aC5hYnMoeDIgLSB4KSA8IGVwc2lsb24pIHJldHVybiB0MjtcblxuICAgICAgICBpZiAoeCA+IHgyKSB7XG4gICAgICAgICAgICB0MCA9IHQyO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdDEgPSB0MjtcbiAgICAgICAgfVxuXG4gICAgICAgIHQyID0gKHQxIC0gdDApICogMC41ICsgdDA7XG4gICAgfVxuXG4gICAgLy8gRmFpbHVyZS5cbiAgICByZXR1cm4gdDI7XG59O1xuXG5Vbml0QmV6aWVyLnByb3RvdHlwZS5zb2x2ZSA9IGZ1bmN0aW9uKHgsIGVwc2lsb24pIHtcbiAgICByZXR1cm4gdGhpcy5zYW1wbGVDdXJ2ZVkodGhpcy5zb2x2ZUN1cnZlWCh4LCBlcHNpbG9uKSk7XG59O1xuIiwiLy8gICAgICBcblxuLyoqXG4gKiBBIGNvb3JkaW5hdGUgaXMgYSBjb2x1bW4sIHJvdywgem9vbSBjb21iaW5hdGlvbiwgb2Z0ZW4gdXNlZFxuICogYXMgdGhlIGRhdGEgY29tcG9uZW50IG9mIGEgdGlsZS5cbiAqXG4gKiBAcGFyYW0ge251bWJlcn0gY29sdW1uXG4gKiBAcGFyYW0ge251bWJlcn0gcm93XG4gKiBAcGFyYW0ge251bWJlcn0gem9vbVxuICogQHByaXZhdGVcbiAqL1xuY2xhc3MgQ29vcmRpbmF0ZSB7XG4gICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgIFxuICAgIGNvbnN0cnVjdG9yKGNvbHVtbiAgICAgICAgLCByb3cgICAgICAgICwgem9vbSAgICAgICAgKSB7XG4gICAgICAgIHRoaXMuY29sdW1uID0gY29sdW1uO1xuICAgICAgICB0aGlzLnJvdyA9IHJvdztcbiAgICAgICAgdGhpcy56b29tID0gem9vbTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgYSBjbG9uZSBvZiB0aGlzIGNvb3JkaW5hdGUgdGhhdCBjYW4gYmUgbXV0YXRlZCB3aXRob3V0XG4gICAgICogY2hhbmdpbmcgdGhlIG9yaWdpbmFsIGNvb3JkaW5hdGVcbiAgICAgKlxuICAgICAqIEByZXR1cm5zIHtDb29yZGluYXRlfSBjbG9uZVxuICAgICAqIEBwcml2YXRlXG4gICAgICogdmFyIGNvb3JkID0gbmV3IENvb3JkaW5hdGUoMCwgMCwgMCk7XG4gICAgICogdmFyIGMyID0gY29vcmQuY2xvbmUoKTtcbiAgICAgKiAvLyBzaW5jZSBjb29yZCBpcyBjbG9uZWQsIG1vZGlmeWluZyBhIHByb3BlcnR5IG9mIGMyIGRvZXNcbiAgICAgKiAvLyBub3QgbW9kaWZ5IGl0LlxuICAgICAqIGMyLnpvb20gPSAyO1xuICAgICAqL1xuICAgIGNsb25lKCkge1xuICAgICAgICByZXR1cm4gbmV3IENvb3JkaW5hdGUodGhpcy5jb2x1bW4sIHRoaXMucm93LCB0aGlzLnpvb20pO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFpvb20gdGhpcyBjb29yZGluYXRlIHRvIGEgZ2l2ZW4gem9vbSBsZXZlbC4gVGhpcyByZXR1cm5zIGEgbmV3XG4gICAgICogY29vcmRpbmF0ZSBvYmplY3QsIG5vdCBtdXRhdGluZyB0aGUgb2xkIG9uZS5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB6b29tXG4gICAgICogQHJldHVybnMge0Nvb3JkaW5hdGV9IHpvb21lZCBjb29yZGluYXRlXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAZXhhbXBsZVxuICAgICAqIHZhciBjb29yZCA9IG5ldyBDb29yZGluYXRlKDAsIDAsIDApO1xuICAgICAqIHZhciBjMiA9IGNvb3JkLnpvb21UbygxKTtcbiAgICAgKiBjMiAvLyBlcXVhbHMgbmV3IENvb3JkaW5hdGUoMCwgMCwgMSk7XG4gICAgICovXG4gICAgem9vbVRvKHpvb20gICAgICAgICkgeyByZXR1cm4gdGhpcy5jbG9uZSgpLl96b29tVG8oem9vbSk7IH1cblxuICAgIC8qKlxuICAgICAqIFN1YnRyYWN0IHRoZSBjb2x1bW4gYW5kIHJvdyB2YWx1ZXMgb2YgdGhpcyBjb29yZGluYXRlIGZyb20gdGhvc2VcbiAgICAgKiBvZiBhbm90aGVyIGNvb3JkaW5hdGUuIFRoZSBvdGhlciBjb29yZGluYXQgd2lsbCBiZSB6b29tZWQgdG8gdGhlXG4gICAgICogc2FtZSBsZXZlbCBhcyBgdGhpc2AgYmVmb3JlIHRoZSBzdWJ0cmFjdGlvbiBvY2N1cnNcbiAgICAgKlxuICAgICAqIEBwYXJhbSB7Q29vcmRpbmF0ZX0gYyBvdGhlciBjb29yZGluYXRlXG4gICAgICogQHJldHVybnMge0Nvb3JkaW5hdGV9IHJlc3VsdFxuICAgICAqIEBwcml2YXRlXG4gICAgICovXG4gICAgc3ViKGMgICAgICAgICAgICApIHsgcmV0dXJuIHRoaXMuY2xvbmUoKS5fc3ViKGMpOyB9XG5cbiAgICBfem9vbVRvKHpvb20gICAgICAgICkge1xuICAgICAgICBjb25zdCBzY2FsZSA9IE1hdGgucG93KDIsIHpvb20gLSB0aGlzLnpvb20pO1xuICAgICAgICB0aGlzLmNvbHVtbiAqPSBzY2FsZTtcbiAgICAgICAgdGhpcy5yb3cgKj0gc2NhbGU7XG4gICAgICAgIHRoaXMuem9vbSA9IHpvb207XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIF9zdWIoYyAgICAgICAgICAgICkge1xuICAgICAgICBjID0gYy56b29tVG8odGhpcy56b29tKTtcbiAgICAgICAgdGhpcy5jb2x1bW4gLT0gYy5jb2x1bW47XG4gICAgICAgIHRoaXMucm93IC09IGMucm93O1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG59XG5cbm1vZHVsZS5leHBvcnRzID0gQ29vcmRpbmF0ZTtcbiIsIi8vICAgICAgXG5cbmNvbnN0IHdyYXAgPSByZXF1aXJlKCcuLi91dGlsL3V0aWwnKS53cmFwO1xuXG4vKipcbiAqIEEgYExuZ0xhdGAgb2JqZWN0IHJlcHJlc2VudHMgYSBnaXZlbiBsb25naXR1ZGUgYW5kIGxhdGl0dWRlIGNvb3JkaW5hdGUsIG1lYXN1cmVkIGluIGRlZ3JlZXMuXG4gKlxuICogTWFwYm94IEdMIHVzZXMgbG9uZ2l0dWRlLCBsYXRpdHVkZSBjb29yZGluYXRlIG9yZGVyIChhcyBvcHBvc2VkIHRvIGxhdGl0dWRlLCBsb25naXR1ZGUpIHRvIG1hdGNoIEdlb0pTT04uXG4gKlxuICogTm90ZSB0aGF0IGFueSBNYXBib3ggR0wgbWV0aG9kIHRoYXQgYWNjZXB0cyBhIGBMbmdMYXRgIG9iamVjdCBhcyBhbiBhcmd1bWVudCBvciBvcHRpb25cbiAqIGNhbiBhbHNvIGFjY2VwdCBhbiBgQXJyYXlgIG9mIHR3byBudW1iZXJzIGFuZCB3aWxsIHBlcmZvcm0gYW4gaW1wbGljaXQgY29udmVyc2lvbi5cbiAqIFRoaXMgZmxleGlibGUgdHlwZSBpcyBkb2N1bWVudGVkIGFzIHtAbGluayBMbmdMYXRMaWtlfS5cbiAqXG4gKiBAcGFyYW0ge251bWJlcn0gbG5nIExvbmdpdHVkZSwgbWVhc3VyZWQgaW4gZGVncmVlcy5cbiAqIEBwYXJhbSB7bnVtYmVyfSBsYXQgTGF0aXR1ZGUsIG1lYXN1cmVkIGluIGRlZ3JlZXMuXG4gKiBAZXhhbXBsZVxuICogdmFyIGxsID0gbmV3IG1hcGJveGdsLkxuZ0xhdCgtNzMuOTc0OSwgNDAuNzczNik7XG4gKiBAc2VlIFtHZXQgY29vcmRpbmF0ZXMgb2YgdGhlIG1vdXNlIHBvaW50ZXJdKGh0dHBzOi8vd3d3Lm1hcGJveC5jb20vbWFwYm94LWdsLWpzL2V4YW1wbGUvbW91c2UtcG9zaXRpb24vKVxuICogQHNlZSBbRGlzcGxheSBhIHBvcHVwXShodHRwczovL3d3dy5tYXBib3guY29tL21hcGJveC1nbC1qcy9leGFtcGxlL3BvcHVwLylcbiAqIEBzZWUgW0hpZ2hsaWdodCBmZWF0dXJlcyB3aXRoaW4gYSBib3VuZGluZyBib3hdKGh0dHBzOi8vd3d3Lm1hcGJveC5jb20vbWFwYm94LWdsLWpzL2V4YW1wbGUvdXNpbmctYm94LXF1ZXJ5cmVuZGVyZWRmZWF0dXJlcy8pXG4gKiBAc2VlIFtDcmVhdGUgYSB0aW1lbGluZSBhbmltYXRpb25dKGh0dHBzOi8vd3d3Lm1hcGJveC5jb20vbWFwYm94LWdsLWpzL2V4YW1wbGUvdGltZWxpbmUtYW5pbWF0aW9uLylcbiAqL1xuY2xhc3MgTG5nTGF0IHtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICBcblxuICAgIGNvbnN0cnVjdG9yKGxuZyAgICAgICAgLCBsYXQgICAgICAgICkge1xuICAgICAgICBpZiAoaXNOYU4obG5nKSB8fCBpc05hTihsYXQpKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgTG5nTGF0IG9iamVjdDogKCR7bG5nfSwgJHtsYXR9KWApO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMubG5nID0gK2xuZztcbiAgICAgICAgdGhpcy5sYXQgPSArbGF0O1xuICAgICAgICBpZiAodGhpcy5sYXQgPiA5MCB8fCB0aGlzLmxhdCA8IC05MCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIExuZ0xhdCBsYXRpdHVkZSB2YWx1ZTogbXVzdCBiZSBiZXR3ZWVuIC05MCBhbmQgOTAnKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgYSBuZXcgYExuZ0xhdGAgb2JqZWN0IHdob3NlIGxvbmdpdHVkZSBpcyB3cmFwcGVkIHRvIHRoZSByYW5nZSAoLTE4MCwgMTgwKS5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIHtMbmdMYXR9IFRoZSB3cmFwcGVkIGBMbmdMYXRgIG9iamVjdC5cbiAgICAgKiBAZXhhbXBsZVxuICAgICAqIHZhciBsbCA9IG5ldyBtYXBib3hnbC5MbmdMYXQoMjg2LjAyNTEsIDQwLjc3MzYpO1xuICAgICAqIHZhciB3cmFwcGVkID0gbGwud3JhcCgpO1xuICAgICAqIHdyYXBwZWQubG5nOyAvLyA9IC03My45NzQ5XG4gICAgICovXG4gICAgd3JhcCgpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBMbmdMYXQod3JhcCh0aGlzLmxuZywgLTE4MCwgMTgwKSwgdGhpcy5sYXQpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNvb3JkaW5hdGVzIHJlcHJlc2VudGVkIGFzIGFuIGFycmF5IG9mIHR3byBudW1iZXJzLlxuICAgICAqXG4gICAgICogQHJldHVybnMge0FycmF5PG51bWJlcj59IFRoZSBjb29yZGluYXRlcyByZXByZXNldGVkIGFzIGFuIGFycmF5IG9mIGxvbmdpdHVkZSBhbmQgbGF0aXR1ZGUuXG4gICAgICogQGV4YW1wbGVcbiAgICAgKiB2YXIgbGwgPSBuZXcgbWFwYm94Z2wuTG5nTGF0KC03My45NzQ5LCA0MC43NzM2KTtcbiAgICAgKiBsbC50b0FycmF5KCk7IC8vID0gWy03My45NzQ5LCA0MC43NzM2XVxuICAgICAqL1xuICAgIHRvQXJyYXkoKSB7XG4gICAgICAgIHJldHVybiBbdGhpcy5sbmcsIHRoaXMubGF0XTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBjb29yZGluYXRlcyByZXByZXNlbnQgYXMgYSBzdHJpbmcuXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29vcmRpbmF0ZXMgcmVwcmVzZW50ZWQgYXMgYSBzdHJpbmcgb2YgdGhlIGZvcm1hdCBgJ0xuZ0xhdChsbmcsIGxhdCknYC5cbiAgICAgKiBAZXhhbXBsZVxuICAgICAqIHZhciBsbCA9IG5ldyBtYXBib3hnbC5MbmdMYXQoLTczLjk3NDksIDQwLjc3MzYpO1xuICAgICAqIGxsLnRvU3RyaW5nKCk7IC8vID0gXCJMbmdMYXQoLTczLjk3NDksIDQwLjc3MzYpXCJcbiAgICAgKi9cbiAgICB0b1N0cmluZygpIHtcbiAgICAgICAgcmV0dXJuIGBMbmdMYXQoJHt0aGlzLmxuZ30sICR7dGhpcy5sYXR9KWA7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyBhIGBMbmdMYXRCb3VuZHNgIGZyb20gdGhlIGNvb3JkaW5hdGVzIGV4dGVuZGVkIGJ5IGEgZ2l2ZW4gYHJhZGl1c2AuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gcmFkaXVzIERpc3RhbmNlIGluIG1ldGVycyBmcm9tIHRoZSBjb29yZGluYXRlcyB0byBleHRlbmQgdGhlIGJvdW5kcy5cbiAgICAgKiBAcmV0dXJucyB7TG5nTGF0Qm91bmRzfSBBIG5ldyBgTG5nTGF0Qm91bmRzYCBvYmplY3QgcmVwcmVzZW50aW5nIHRoZSBjb29yZGluYXRlcyBleHRlbmRlZCBieSB0aGUgYHJhZGl1c2AuXG4gICAgICogQGV4YW1wbGVcbiAgICAgKiB2YXIgbGwgPSBuZXcgbWFwYm94Z2wuTG5nTGF0KC03My45NzQ5LCA0MC43NzM2KTtcbiAgICAgKiBsbC50b0JvdW5kcygxMDApLnRvQXJyYXkoKTsgLy8gPSBbWy03My45NzUwMTg2MjE0MTMyOCwgNDAuNzczNTEwMTY4NDcyMjldLCBbLTczLjk3NDc4MTM3ODU4NjczLCA0MC43NzM2ODk4MzE1Mjc3MV1dXG4gICAgICovXG4gICAgdG9Cb3VuZHMocmFkaXVzICAgICAgICApIHtcbiAgICAgICAgY29uc3QgZWFydGhDaXJjdW1mZXJlbmNlSW5NZXRlcnNBdEVxdWF0b3IgPSA0MDA3NTAxNztcbiAgICAgICAgY29uc3QgbGF0QWNjdXJhY3kgPSAzNjAgKiByYWRpdXMgLyBlYXJ0aENpcmN1bWZlcmVuY2VJbk1ldGVyc0F0RXF1YXRvcixcbiAgICAgICAgICAgIGxuZ0FjY3VyYWN5ID0gbGF0QWNjdXJhY3kgLyBNYXRoLmNvcygoTWF0aC5QSSAvIDE4MCkgKiB0aGlzLmxhdCk7XG5cbiAgICAgICAgY29uc3QgTG5nTGF0Qm91bmRzID0gcmVxdWlyZSgnLi9sbmdfbGF0X2JvdW5kcycpO1xuICAgICAgICByZXR1cm4gbmV3IExuZ0xhdEJvdW5kcyhuZXcgTG5nTGF0KHRoaXMubG5nIC0gbG5nQWNjdXJhY3ksIHRoaXMubGF0IC0gbGF0QWNjdXJhY3kpLFxuICAgICAgICAgICAgbmV3IExuZ0xhdCh0aGlzLmxuZyArIGxuZ0FjY3VyYWN5LCB0aGlzLmxhdCArIGxhdEFjY3VyYWN5KSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ29udmVydHMgYW4gYXJyYXkgb2YgdHdvIG51bWJlcnMgdG8gYSBgTG5nTGF0YCBvYmplY3QuXG4gICAgICpcbiAgICAgKiBJZiBhIGBMbmdMYXRgIG9iamVjdCBpcyBwYXNzZWQgaW4sIHRoZSBmdW5jdGlvbiByZXR1cm5zIGl0IHVuY2hhbmdlZC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7TG5nTGF0TGlrZX0gaW5wdXQgQW4gYXJyYXkgb2YgdHdvIG51bWJlcnMgdG8gY29udmVydCwgb3IgYSBgTG5nTGF0YCBvYmplY3QgdG8gcmV0dXJuLlxuICAgICAqIEByZXR1cm5zIHtMbmdMYXR9IEEgbmV3IGBMbmdMYXRgIG9iamVjdCwgaWYgYSBjb252ZXJzaW9uIG9jY3VycmVkLCBvciB0aGUgb3JpZ2luYWwgYExuZ0xhdGAgb2JqZWN0LlxuICAgICAqIEBleGFtcGxlXG4gICAgICogdmFyIGFyciA9IFstNzMuOTc0OSwgNDAuNzczNl07XG4gICAgICogdmFyIGxsID0gbWFwYm94Z2wuTG5nTGF0LmNvbnZlcnQoYXJyKTtcbiAgICAgKiBsbDsgICAvLyA9IExuZ0xhdCB7bG5nOiAtNzMuOTc0OSwgbGF0OiA0MC43NzM2fVxuICAgICAqL1xuICAgIHN0YXRpYyBjb252ZXJ0KGlucHV0ICAgICAgICAgICAgKSAgICAgICAgIHtcbiAgICAgICAgaWYgKGlucHV0IGluc3RhbmNlb2YgTG5nTGF0KSB7XG4gICAgICAgICAgICByZXR1cm4gaW5wdXQ7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkoaW5wdXQpICYmIChpbnB1dC5sZW5ndGggPT09IDIgfHwgaW5wdXQubGVuZ3RoID09PSAzKSkge1xuICAgICAgICAgICAgcmV0dXJuIG5ldyBMbmdMYXQoTnVtYmVyKGlucHV0WzBdKSwgTnVtYmVyKGlucHV0WzFdKSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFBcnJheS5pc0FycmF5KGlucHV0KSAmJiB0eXBlb2YgaW5wdXQgPT09ICdvYmplY3QnICYmIGlucHV0ICE9PSBudWxsKSB7XG4gICAgICAgICAgICByZXR1cm4gbmV3IExuZ0xhdChOdW1iZXIoaW5wdXQubG5nKSwgTnVtYmVyKGlucHV0LmxhdCkpO1xuICAgICAgICB9XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcImBMbmdMYXRMaWtlYCBhcmd1bWVudCBtdXN0IGJlIHNwZWNpZmllZCBhcyBhIExuZ0xhdCBpbnN0YW5jZSwgYW4gb2JqZWN0IHtsbmc6IDxsbmc+LCBsYXQ6IDxsYXQ+fSwgb3IgYW4gYXJyYXkgb2YgWzxsbmc+LCA8bGF0Pl1cIik7XG4gICAgfVxufVxuXG4vKipcbiAqIEEge0BsaW5rIExuZ0xhdH0gb2JqZWN0LCBhbiBhcnJheSBvZiB0d28gbnVtYmVycyByZXByZXNlbnRpbmcgbG9uZ2l0dWRlIGFuZCBsYXRpdHVkZSxcbiAqIG9yIGFuIG9iamVjdCB3aXRoIGBsbmdgIGFuZCBgbGF0YCBwcm9wZXJ0aWVzLlxuICpcbiAqIEB0eXBlZGVmIHtMbmdMYXQgfCB7bG5nOiBudW1iZXIsIGxhdDogbnVtYmVyfSB8IFtudW1iZXIsIG51bWJlcl19IExuZ0xhdExpa2VcbiAqIEBleGFtcGxlXG4gKiB2YXIgdjEgPSBuZXcgbWFwYm94Z2wuTG5nTGF0KC0xMjIuNDIwNjc5LCAzNy43NzI1MzcpO1xuICogdmFyIHYyID0gWy0xMjIuNDIwNjc5LCAzNy43NzI1MzddO1xuICovXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFxuXG5tb2R1bGUuZXhwb3J0cyA9IExuZ0xhdDtcbiIsIi8vICAgICAgXG5cbmNvbnN0IExuZ0xhdCA9IHJlcXVpcmUoJy4vbG5nX2xhdCcpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcblxuLyoqXG4gKiBBIGBMbmdMYXRCb3VuZHNgIG9iamVjdCByZXByZXNlbnRzIGEgZ2VvZ3JhcGhpY2FsIGJvdW5kaW5nIGJveCxcbiAqIGRlZmluZWQgYnkgaXRzIHNvdXRod2VzdCBhbmQgbm9ydGhlYXN0IHBvaW50cyBpbiBsb25naXR1ZGUgYW5kIGxhdGl0dWRlLlxuICpcbiAqIElmIG5vIGFyZ3VtZW50cyBhcmUgcHJvdmlkZWQgdG8gdGhlIGNvbnN0cnVjdG9yLCBhIGBudWxsYCBib3VuZGluZyBib3ggaXMgY3JlYXRlZC5cbiAqXG4gKiBOb3RlIHRoYXQgYW55IE1hcGJveCBHTCBtZXRob2QgdGhhdCBhY2NlcHRzIGEgYExuZ0xhdEJvdW5kc2Agb2JqZWN0IGFzIGFuIGFyZ3VtZW50IG9yIG9wdGlvblxuICogY2FuIGFsc28gYWNjZXB0IGFuIGBBcnJheWAgb2YgdHdvIHtAbGluayBMbmdMYXRMaWtlfSBjb25zdHJ1Y3RzIGFuZCB3aWxsIHBlcmZvcm0gYW4gaW1wbGljaXQgY29udmVyc2lvbi5cbiAqIFRoaXMgZmxleGlibGUgdHlwZSBpcyBkb2N1bWVudGVkIGFzIHtAbGluayBMbmdMYXRCb3VuZHNMaWtlfS5cbiAqXG4gKiBAcGFyYW0ge0xuZ0xhdExpa2V9IFtzd10gVGhlIHNvdXRod2VzdCBjb3JuZXIgb2YgdGhlIGJvdW5kaW5nIGJveC5cbiAqIEBwYXJhbSB7TG5nTGF0TGlrZX0gW25lXSBUaGUgbm9ydGhlYXN0IGNvcm5lciBvZiB0aGUgYm91bmRpbmcgYm94LlxuICogQGV4YW1wbGVcbiAqIHZhciBzdyA9IG5ldyBtYXBib3hnbC5MbmdMYXQoLTczLjk4NzYsIDQwLjc2NjEpO1xuICogdmFyIG5lID0gbmV3IG1hcGJveGdsLkxuZ0xhdCgtNzMuOTM5NywgNDAuODAwMik7XG4gKiB2YXIgbGxiID0gbmV3IG1hcGJveGdsLkxuZ0xhdEJvdW5kcyhzdywgbmUpO1xuICovXG5jbGFzcyBMbmdMYXRCb3VuZHMge1xuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgIFxuXG4gICAgLy8gVGhpcyBjb25zdHJ1Y3RvciBpcyB0b28gZmxleGlibGUgdG8gdHlwZS4gSXQgc2hvdWxkIG5vdCBiZSBzbyBmbGV4aWJsZS5cbiAgICBjb25zdHJ1Y3RvcihzdyAgICAgLCBuZSAgICAgKSB7XG4gICAgICAgIGlmICghc3cpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfSBlbHNlIGlmIChuZSkge1xuICAgICAgICAgICAgdGhpcy5zZXRTb3V0aFdlc3Qoc3cpLnNldE5vcnRoRWFzdChuZSk7XG4gICAgICAgIH0gZWxzZSBpZiAoc3cubGVuZ3RoID09PSA0KSB7XG4gICAgICAgICAgICB0aGlzLnNldFNvdXRoV2VzdChbc3dbMF0sIHN3WzFdXSkuc2V0Tm9ydGhFYXN0KFtzd1syXSwgc3dbM11dKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuc2V0U291dGhXZXN0KHN3WzBdKS5zZXROb3J0aEVhc3Qoc3dbMV0pO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2V0IHRoZSBub3J0aGVhc3QgY29ybmVyIG9mIHRoZSBib3VuZGluZyBib3hcbiAgICAgKlxuICAgICAqIEBwYXJhbSB7TG5nTGF0TGlrZX0gbmVcbiAgICAgKiBAcmV0dXJucyB7TG5nTGF0Qm91bmRzfSBgdGhpc2BcbiAgICAgKi9cbiAgICBzZXROb3J0aEVhc3QobmUgICAgICAgICAgICApIHtcbiAgICAgICAgdGhpcy5fbmUgPSBuZSBpbnN0YW5jZW9mIExuZ0xhdCA/IG5ldyBMbmdMYXQobmUubG5nLCBuZS5sYXQpIDogTG5nTGF0LmNvbnZlcnQobmUpO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZXQgdGhlIHNvdXRod2VzdCBjb3JuZXIgb2YgdGhlIGJvdW5kaW5nIGJveFxuICAgICAqXG4gICAgICogQHBhcmFtIHtMbmdMYXRMaWtlfSBzd1xuICAgICAqIEByZXR1cm5zIHtMbmdMYXRCb3VuZHN9IGB0aGlzYFxuICAgICAqL1xuICAgIHNldFNvdXRoV2VzdChzdyAgICAgICAgICAgICkge1xuICAgICAgICB0aGlzLl9zdyA9IHN3IGluc3RhbmNlb2YgTG5nTGF0ID8gbmV3IExuZ0xhdChzdy5sbmcsIHN3LmxhdCkgOiBMbmdMYXQuY29udmVydChzdyk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEV4dGVuZCB0aGUgYm91bmRzIHRvIGluY2x1ZGUgYSBnaXZlbiBMbmdMYXQgb3IgTG5nTGF0Qm91bmRzLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtMbmdMYXR8TG5nTGF0Qm91bmRzfSBvYmogb2JqZWN0IHRvIGV4dGVuZCB0b1xuICAgICAqIEByZXR1cm5zIHtMbmdMYXRCb3VuZHN9IGB0aGlzYFxuICAgICAqL1xuICAgIGV4dGVuZChvYmopIHtcbiAgICAgICAgY29uc3Qgc3cgPSB0aGlzLl9zdyxcbiAgICAgICAgICAgIG5lID0gdGhpcy5fbmU7XG4gICAgICAgIGxldCBzdzIsIG5lMjtcblxuICAgICAgICBpZiAob2JqIGluc3RhbmNlb2YgTG5nTGF0KSB7XG4gICAgICAgICAgICBzdzIgPSBvYmo7XG4gICAgICAgICAgICBuZTIgPSBvYmo7XG5cbiAgICAgICAgfSBlbHNlIGlmIChvYmogaW5zdGFuY2VvZiBMbmdMYXRCb3VuZHMpIHtcbiAgICAgICAgICAgIHN3MiA9IG9iai5fc3c7XG4gICAgICAgICAgICBuZTIgPSBvYmouX25lO1xuXG4gICAgICAgICAgICBpZiAoIXN3MiB8fCAhbmUyKSByZXR1cm4gdGhpcztcblxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkob2JqKSkge1xuICAgICAgICAgICAgICAgIGlmIChvYmouZXZlcnkoQXJyYXkuaXNBcnJheSkpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuZXh0ZW5kKExuZ0xhdEJvdW5kcy5jb252ZXJ0KG9iaikpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmV4dGVuZChMbmdMYXQuY29udmVydChvYmopKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghc3cgJiYgIW5lKSB7XG4gICAgICAgICAgICB0aGlzLl9zdyA9IG5ldyBMbmdMYXQoc3cyLmxuZywgc3cyLmxhdCk7XG4gICAgICAgICAgICB0aGlzLl9uZSA9IG5ldyBMbmdMYXQobmUyLmxuZywgbmUyLmxhdCk7XG5cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHN3LmxuZyA9IE1hdGgubWluKHN3Mi5sbmcsIHN3LmxuZyk7XG4gICAgICAgICAgICBzdy5sYXQgPSBNYXRoLm1pbihzdzIubGF0LCBzdy5sYXQpO1xuICAgICAgICAgICAgbmUubG5nID0gTWF0aC5tYXgobmUyLmxuZywgbmUubG5nKTtcbiAgICAgICAgICAgIG5lLmxhdCA9IE1hdGgubWF4KG5lMi5sYXQsIG5lLmxhdCk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBnZW9ncmFwaGljYWwgY29vcmRpbmF0ZSBlcXVpZGlzdGFudCBmcm9tIHRoZSBib3VuZGluZyBib3gncyBjb3JuZXJzLlxuICAgICAqXG4gICAgICogQHJldHVybnMge0xuZ0xhdH0gVGhlIGJvdW5kaW5nIGJveCdzIGNlbnRlci5cbiAgICAgKiBAZXhhbXBsZVxuICAgICAqIHZhciBsbGIgPSBuZXcgbWFwYm94Z2wuTG5nTGF0Qm91bmRzKFstNzMuOTg3NiwgNDAuNzY2MV0sIFstNzMuOTM5NywgNDAuODAwMl0pO1xuICAgICAqIGxsYi5nZXRDZW50ZXIoKTsgLy8gPSBMbmdMYXQge2xuZzogLTczLjk2MzY1LCBsYXQ6IDQwLjc4MzE1fVxuICAgICAqL1xuICAgIGdldENlbnRlcigpICAgICAgICAge1xuICAgICAgICByZXR1cm4gbmV3IExuZ0xhdCgodGhpcy5fc3cubG5nICsgdGhpcy5fbmUubG5nKSAvIDIsICh0aGlzLl9zdy5sYXQgKyB0aGlzLl9uZS5sYXQpIC8gMik7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgc291dGh3ZXN0IGNvcm5lciBvZiB0aGUgYm91bmRpbmcgYm94LlxuICAgICAqXG4gICAgICogQHJldHVybnMge0xuZ0xhdH0gVGhlIHNvdXRod2VzdCBjb3JuZXIgb2YgdGhlIGJvdW5kaW5nIGJveC5cbiAgICAgKi9cbiAgICBnZXRTb3V0aFdlc3QoKSAgICAgICAgIHsgcmV0dXJuIHRoaXMuX3N3OyB9XG5cbiAgICAvKipcbiAgICAqIFJldHVybnMgdGhlIG5vcnRoZWFzdCBjb3JuZXIgb2YgdGhlIGJvdW5kaW5nIGJveC5cbiAgICAqXG4gICAgKiBAcmV0dXJucyB7TG5nTGF0fSBUaGUgbm9ydGhlYXN0IGNvcm5lciBvZiB0aGUgYm91bmRpbmcgYm94LlxuICAgICAqL1xuICAgIGdldE5vcnRoRWFzdCgpICAgICAgICAgeyByZXR1cm4gdGhpcy5fbmU7IH1cblxuICAgIC8qKlxuICAgICogUmV0dXJucyB0aGUgbm9ydGh3ZXN0IGNvcm5lciBvZiB0aGUgYm91bmRpbmcgYm94LlxuICAgICpcbiAgICAqIEByZXR1cm5zIHtMbmdMYXR9IFRoZSBub3J0aHdlc3QgY29ybmVyIG9mIHRoZSBib3VuZGluZyBib3guXG4gICAgICovXG4gICAgZ2V0Tm9ydGhXZXN0KCkgICAgICAgICB7IHJldHVybiBuZXcgTG5nTGF0KHRoaXMuZ2V0V2VzdCgpLCB0aGlzLmdldE5vcnRoKCkpOyB9XG5cbiAgICAvKipcbiAgICAqIFJldHVybnMgdGhlIHNvdXRoZWFzdCBjb3JuZXIgb2YgdGhlIGJvdW5kaW5nIGJveC5cbiAgICAqXG4gICAgKiBAcmV0dXJucyB7TG5nTGF0fSBUaGUgc291dGhlYXN0IGNvcm5lciBvZiB0aGUgYm91bmRpbmcgYm94LlxuICAgICAqL1xuICAgIGdldFNvdXRoRWFzdCgpICAgICAgICAgeyByZXR1cm4gbmV3IExuZ0xhdCh0aGlzLmdldEVhc3QoKSwgdGhpcy5nZXRTb3V0aCgpKTsgfVxuXG4gICAgLyoqXG4gICAgKiBSZXR1cm5zIHRoZSB3ZXN0IGVkZ2Ugb2YgdGhlIGJvdW5kaW5nIGJveC5cbiAgICAqXG4gICAgKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgd2VzdCBlZGdlIG9mIHRoZSBib3VuZGluZyBib3guXG4gICAgICovXG4gICAgZ2V0V2VzdCgpICAgICAgICAgeyByZXR1cm4gdGhpcy5fc3cubG5nOyB9XG5cbiAgICAvKipcbiAgICAqIFJldHVybnMgdGhlIHNvdXRoIGVkZ2Ugb2YgdGhlIGJvdW5kaW5nIGJveC5cbiAgICAqXG4gICAgKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgc291dGggZWRnZSBvZiB0aGUgYm91bmRpbmcgYm94LlxuICAgICAqL1xuICAgIGdldFNvdXRoKCkgICAgICAgICB7IHJldHVybiB0aGlzLl9zdy5sYXQ7IH1cblxuICAgIC8qKlxuICAgICogUmV0dXJucyB0aGUgZWFzdCBlZGdlIG9mIHRoZSBib3VuZGluZyBib3guXG4gICAgKlxuICAgICogQHJldHVybnMge251bWJlcn0gVGhlIGVhc3QgZWRnZSBvZiB0aGUgYm91bmRpbmcgYm94LlxuICAgICAqL1xuICAgIGdldEVhc3QoKSAgICAgICAgIHsgcmV0dXJuIHRoaXMuX25lLmxuZzsgfVxuXG4gICAgLyoqXG4gICAgKiBSZXR1cm5zIHRoZSBub3J0aCBlZGdlIG9mIHRoZSBib3VuZGluZyBib3guXG4gICAgKlxuICAgICogQHJldHVybnMge251bWJlcn0gVGhlIG5vcnRoIGVkZ2Ugb2YgdGhlIGJvdW5kaW5nIGJveC5cbiAgICAgKi9cbiAgICBnZXROb3J0aCgpICAgICAgICAgeyByZXR1cm4gdGhpcy5fbmUubGF0OyB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBib3VuZGluZyBib3ggcmVwcmVzZW50ZWQgYXMgYW4gYXJyYXkuXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyB7QXJyYXk8QXJyYXk8bnVtYmVyPj59IFRoZSBib3VuZGluZyBib3ggcmVwcmVzZW50ZWQgYXMgYW4gYXJyYXksIGNvbnNpc3Rpbmcgb2YgdGhlXG4gICAgICogICBzb3V0aHdlc3QgYW5kIG5vcnRoZWFzdCBjb29yZGluYXRlcyBvZiB0aGUgYm91bmRpbmcgcmVwcmVzZW50ZWQgYXMgYXJyYXlzIG9mIG51bWJlcnMuXG4gICAgICogQGV4YW1wbGVcbiAgICAgKiB2YXIgbGxiID0gbmV3IG1hcGJveGdsLkxuZ0xhdEJvdW5kcyhbLTczLjk4NzYsIDQwLjc2NjFdLCBbLTczLjkzOTcsIDQwLjgwMDJdKTtcbiAgICAgKiBsbGIudG9BcnJheSgpOyAvLyA9IFtbLTczLjk4NzYsIDQwLjc2NjFdLCBbLTczLjkzOTcsIDQwLjgwMDJdXVxuICAgICAqL1xuICAgIHRvQXJyYXkoKSB7XG4gICAgICAgIHJldHVybiBbdGhpcy5fc3cudG9BcnJheSgpLCB0aGlzLl9uZS50b0FycmF5KCldO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybiB0aGUgYm91bmRpbmcgYm94IHJlcHJlc2VudGVkIGFzIGEgc3RyaW5nLlxuICAgICAqXG4gICAgICogQHJldHVybnMge3N0cmluZ30gVGhlIGJvdW5kaW5nIGJveCByZXByZXNlbnRzIGFzIGEgc3RyaW5nIG9mIHRoZSBmb3JtYXRcbiAgICAgKiAgIGAnTG5nTGF0Qm91bmRzKExuZ0xhdChsbmcsIGxhdCksIExuZ0xhdChsbmcsIGxhdCkpJ2AuXG4gICAgICogQGV4YW1wbGVcbiAgICAgKiB2YXIgbGxiID0gbmV3IG1hcGJveGdsLkxuZ0xhdEJvdW5kcyhbLTczLjk4NzYsIDQwLjc2NjFdLCBbLTczLjkzOTcsIDQwLjgwMDJdKTtcbiAgICAgKiBsbGIudG9TdHJpbmcoKTsgLy8gPSBcIkxuZ0xhdEJvdW5kcyhMbmdMYXQoLTczLjk4NzYsIDQwLjc2NjEpLCBMbmdMYXQoLTczLjkzOTcsIDQwLjgwMDIpKVwiXG4gICAgICovXG4gICAgdG9TdHJpbmcoKSB7XG4gICAgICAgIHJldHVybiBgTG5nTGF0Qm91bmRzKCR7dGhpcy5fc3cudG9TdHJpbmcoKX0sICR7dGhpcy5fbmUudG9TdHJpbmcoKX0pYDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDb252ZXJ0cyBhbiBhcnJheSB0byBhIGBMbmdMYXRCb3VuZHNgIG9iamVjdC5cbiAgICAgKlxuICAgICAqIElmIGEgYExuZ0xhdEJvdW5kc2Agb2JqZWN0IGlzIHBhc3NlZCBpbiwgdGhlIGZ1bmN0aW9uIHJldHVybnMgaXQgdW5jaGFuZ2VkLlxuICAgICAqXG4gICAgICogSW50ZXJuYWxseSwgdGhlIGZ1bmN0aW9uIGNhbGxzIGBMbmdMYXQjY29udmVydGAgdG8gY29udmVydCBhcnJheXMgdG8gYExuZ0xhdGAgdmFsdWVzLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtMbmdMYXRCb3VuZHNMaWtlfSBpbnB1dCBBbiBhcnJheSBvZiB0d28gY29vcmRpbmF0ZXMgdG8gY29udmVydCwgb3IgYSBgTG5nTGF0Qm91bmRzYCBvYmplY3QgdG8gcmV0dXJuLlxuICAgICAqIEByZXR1cm5zIHtMbmdMYXRCb3VuZHN9IEEgbmV3IGBMbmdMYXRCb3VuZHNgIG9iamVjdCwgaWYgYSBjb252ZXJzaW9uIG9jY3VycmVkLCBvciB0aGUgb3JpZ2luYWwgYExuZ0xhdEJvdW5kc2Agb2JqZWN0LlxuICAgICAqIEBleGFtcGxlXG4gICAgICogdmFyIGFyciA9IFtbLTczLjk4NzYsIDQwLjc2NjFdLCBbLTczLjkzOTcsIDQwLjgwMDJdXTtcbiAgICAgKiB2YXIgbGxiID0gbWFwYm94Z2wuTG5nTGF0Qm91bmRzLmNvbnZlcnQoYXJyKTtcbiAgICAgKiBsbGI7ICAgLy8gPSBMbmdMYXRCb3VuZHMge19zdzogTG5nTGF0IHtsbmc6IC03My45ODc2LCBsYXQ6IDQwLjc2NjF9LCBfbmU6IExuZ0xhdCB7bG5nOiAtNzMuOTM5NywgbGF0OiA0MC44MDAyfX1cbiAgICAgKi9cbiAgICBzdGF0aWMgY29udmVydChpbnB1dCAgICAgICAgICAgICAgICAgICkgICAgICAgICAgICAgICB7XG4gICAgICAgIGlmICghaW5wdXQgfHwgaW5wdXQgaW5zdGFuY2VvZiBMbmdMYXRCb3VuZHMpIHJldHVybiBpbnB1dDtcbiAgICAgICAgcmV0dXJuIG5ldyBMbmdMYXRCb3VuZHMoaW5wdXQpO1xuICAgIH1cbn1cblxuLyoqXG4gKiBBIHtAbGluayBMbmdMYXRCb3VuZHN9IG9iamVjdCwgYW4gYXJyYXkgb2Yge0BsaW5rIExuZ0xhdExpa2V9IG9iamVjdHMgaW4gW3N3LCBuZV0gb3JkZXIsXG4gKiBvciBhbiBhcnJheSBvZiBudW1iZXJzIGluIFt3ZXN0LCBzb3V0aCwgZWFzdCwgbm9ydGhdIG9yZGVyLlxuICpcbiAqIEB0eXBlZGVmIHtMbmdMYXRCb3VuZHMgfCBbTG5nTGF0TGlrZSwgTG5nTGF0TGlrZV0gfCBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXX0gTG5nTGF0Qm91bmRzTGlrZVxuICogQGV4YW1wbGVcbiAqIHZhciB2MSA9IG5ldyBtYXBib3hnbC5MbmdMYXRCb3VuZHMoXG4gKiAgIG5ldyBtYXBib3hnbC5MbmdMYXQoLTczLjk4NzYsIDQwLjc2NjEpLFxuICogICBuZXcgbWFwYm94Z2wuTG5nTGF0KC03My45Mzk3LCA0MC44MDAyKVxuICogKTtcbiAqIHZhciB2MiA9IG5ldyBtYXBib3hnbC5MbmdMYXRCb3VuZHMoWy03My45ODc2LCA0MC43NjYxXSwgWy03My45Mzk3LCA0MC44MDAyXSlcbiAqIHZhciB2MyA9IFtbLTczLjk4NzYsIDQwLjc2NjFdLCBbLTczLjkzOTcsIDQwLjgwMDJdXTtcbiAqL1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFxuXG5tb2R1bGUuZXhwb3J0cyA9IExuZ0xhdEJvdW5kcztcbiIsIi8vICAgICAgXG5cbmNvbnN0IHtIVE1MSW1hZ2VFbGVtZW50LCBIVE1MQ2FudmFzRWxlbWVudCwgSFRNTFZpZGVvRWxlbWVudCwgSW1hZ2VEYXRhfSA9IHJlcXVpcmUoJy4uL3V0aWwvd2luZG93Jyk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFxuXG4gICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgXG4gXG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgXG5cbmNsYXNzIFRleHR1cmUge1xuICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgIFxuXG4gICAgY29uc3RydWN0b3IoY29udGV4dCAgICAgICAgICwgaW1hZ2UgICAgICAgICAgICAgICwgZm9ybWF0ICAgICAgICAgICAgICAgLCBwcmVtdWx0aXBseSAgICAgICAgICApIHtcbiAgICAgICAgdGhpcy5jb250ZXh0ID0gY29udGV4dDtcblxuICAgICAgICBjb25zdCB7d2lkdGgsIGhlaWdodH0gPSBpbWFnZTtcbiAgICAgICAgdGhpcy5zaXplID0gW3dpZHRoLCBoZWlnaHRdO1xuICAgICAgICB0aGlzLmZvcm1hdCA9IGZvcm1hdDtcblxuICAgICAgICB0aGlzLnRleHR1cmUgPSBjb250ZXh0LmdsLmNyZWF0ZVRleHR1cmUoKTtcbiAgICAgICAgdGhpcy51cGRhdGUoaW1hZ2UsIHByZW11bHRpcGx5KTtcbiAgICB9XG5cbiAgICB1cGRhdGUoaW1hZ2UgICAgICAgICAgICAgICwgcHJlbXVsdGlwbHkgICAgICAgICAgKSB7XG4gICAgICAgIGNvbnN0IHt3aWR0aCwgaGVpZ2h0fSA9IGltYWdlO1xuICAgICAgICB0aGlzLnNpemUgPSBbd2lkdGgsIGhlaWdodF07XG5cbiAgICAgICAgY29uc3Qge2NvbnRleHR9ID0gdGhpcztcbiAgICAgICAgY29uc3Qge2dsfSA9IGNvbnRleHQ7XG4gICAgICAgIGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIHRoaXMudGV4dHVyZSk7XG4gICAgICAgIGNvbnRleHQucGl4ZWxTdG9yZVVucGFjay5zZXQoMSk7XG5cbiAgICAgICAgaWYgKHRoaXMuZm9ybWF0ID09PSBnbC5SR0JBICYmIHByZW11bHRpcGx5ICE9PSBmYWxzZSkge1xuICAgICAgICAgICAgY29udGV4dC5waXhlbFN0b3JlVW5wYWNrUHJlbXVsdGlwbHlBbHBoYS5zZXQodHJ1ZSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoaW1hZ2UgaW5zdGFuY2VvZiBIVE1MSW1hZ2VFbGVtZW50IHx8IGltYWdlIGluc3RhbmNlb2YgSFRNTENhbnZhc0VsZW1lbnQgfHwgaW1hZ2UgaW5zdGFuY2VvZiBIVE1MVmlkZW9FbGVtZW50IHx8IGltYWdlIGluc3RhbmNlb2YgSW1hZ2VEYXRhKSB7XG4gICAgICAgICAgICBnbC50ZXhJbWFnZTJEKGdsLlRFWFRVUkVfMkQsIDAsIHRoaXMuZm9ybWF0LCB0aGlzLmZvcm1hdCwgZ2wuVU5TSUdORURfQllURSwgaW1hZ2UpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZ2wudGV4SW1hZ2UyRChnbC5URVhUVVJFXzJELCAwLCB0aGlzLmZvcm1hdCwgd2lkdGgsIGhlaWdodCwgMCwgdGhpcy5mb3JtYXQsIGdsLlVOU0lHTkVEX0JZVEUsIGltYWdlLmRhdGEpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgYmluZChmaWx0ZXIgICAgICAgICAgICAgICAsIHdyYXAgICAgICAgICAgICAgLCBtaW5GaWx0ZXIgICAgICAgICAgICAgICAgKSB7XG4gICAgICAgIGNvbnN0IHtjb250ZXh0fSA9IHRoaXM7XG4gICAgICAgIGNvbnN0IHtnbH0gPSBjb250ZXh0O1xuICAgICAgICBnbC5iaW5kVGV4dHVyZShnbC5URVhUVVJFXzJELCB0aGlzLnRleHR1cmUpO1xuXG4gICAgICAgIGlmIChmaWx0ZXIgIT09IHRoaXMuZmlsdGVyKSB7XG4gICAgICAgICAgICBnbC50ZXhQYXJhbWV0ZXJpKGdsLlRFWFRVUkVfMkQsIGdsLlRFWFRVUkVfTUFHX0ZJTFRFUiwgZmlsdGVyKTtcbiAgICAgICAgICAgIGdsLnRleFBhcmFtZXRlcmkoZ2wuVEVYVFVSRV8yRCwgZ2wuVEVYVFVSRV9NSU5fRklMVEVSLCBtaW5GaWx0ZXIgfHwgZmlsdGVyKTtcbiAgICAgICAgICAgIHRoaXMuZmlsdGVyID0gZmlsdGVyO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHdyYXAgIT09IHRoaXMud3JhcCkge1xuICAgICAgICAgICAgZ2wudGV4UGFyYW1ldGVyaShnbC5URVhUVVJFXzJELCBnbC5URVhUVVJFX1dSQVBfUywgd3JhcCk7XG4gICAgICAgICAgICBnbC50ZXhQYXJhbWV0ZXJpKGdsLlRFWFRVUkVfMkQsIGdsLlRFWFRVUkVfV1JBUF9ULCB3cmFwKTtcbiAgICAgICAgICAgIHRoaXMud3JhcCA9IHdyYXA7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBkZXN0cm95KCkge1xuICAgICAgICBjb25zdCB7Z2x9ID0gdGhpcy5jb250ZXh0O1xuICAgICAgICBnbC5kZWxldGVUZXh0dXJlKHRoaXMudGV4dHVyZSk7XG4gICAgICAgIHRoaXMudGV4dHVyZSA9IChudWxsICAgICApO1xuICAgIH1cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBUZXh0dXJlO1xuIiwiLy8gICAgICBcblxuY29uc3QgTG5nTGF0Qm91bmRzID0gcmVxdWlyZSgnLi4vZ2VvL2xuZ19sYXRfYm91bmRzJyk7XG5jb25zdCBjbGFtcCA9IHJlcXVpcmUoJy4uL3V0aWwvdXRpbCcpLmNsYW1wO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFxuXG5jbGFzcyBUaWxlQm91bmRzIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgIFxuXG4gICAgY29uc3RydWN0b3IoYm91bmRzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwgbWluem9vbSAgICAgICAgICwgbWF4em9vbSAgICAgICAgICkge1xuICAgICAgICB0aGlzLmJvdW5kcyA9IExuZ0xhdEJvdW5kcy5jb252ZXJ0KHRoaXMudmFsaWRhdGVCb3VuZHMoYm91bmRzKSk7XG4gICAgICAgIHRoaXMubWluem9vbSA9IG1pbnpvb20gfHwgMDtcbiAgICAgICAgdGhpcy5tYXh6b29tID0gbWF4em9vbSB8fCAyNDtcbiAgICB9XG5cbiAgICB2YWxpZGF0ZUJvdW5kcyhib3VuZHMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSB7XG4gICAgICAgIC8vIG1ha2Ugc3VyZSB0aGUgYm91bmRzIHByb3BlcnR5IGNvbnRhaW5zIHZhbGlkIGxvbmdpdHVkZSBhbmQgbGF0aXR1ZGVzXG4gICAgICAgIGlmICghQXJyYXkuaXNBcnJheShib3VuZHMpIHx8IGJvdW5kcy5sZW5ndGggIT09IDQpIHJldHVybiBbLTE4MCwgLTkwLCAxODAsIDkwXTtcbiAgICAgICAgcmV0dXJuIFtNYXRoLm1heCgtMTgwLCBib3VuZHNbMF0pLCBNYXRoLm1heCgtOTAsIGJvdW5kc1sxXSksIE1hdGgubWluKDE4MCwgYm91bmRzWzJdKSwgTWF0aC5taW4oOTAsIGJvdW5kc1szXSldO1xuICAgIH1cblxuICAgIGNvbnRhaW5zKHRpbGVJRCAgICAgICAgICAgICAgICAgKSB7XG4gICAgICAgIGNvbnN0IGxldmVsID0ge1xuICAgICAgICAgICAgbWluWDogTWF0aC5mbG9vcih0aGlzLmxuZ1godGhpcy5ib3VuZHMuZ2V0V2VzdCgpLCB0aWxlSUQueikpLFxuICAgICAgICAgICAgbWluWTogTWF0aC5mbG9vcih0aGlzLmxhdFkodGhpcy5ib3VuZHMuZ2V0Tm9ydGgoKSwgdGlsZUlELnopKSxcbiAgICAgICAgICAgIG1heFg6IE1hdGguY2VpbCh0aGlzLmxuZ1godGhpcy5ib3VuZHMuZ2V0RWFzdCgpLCB0aWxlSUQueikpLFxuICAgICAgICAgICAgbWF4WTogTWF0aC5jZWlsKHRoaXMubGF0WSh0aGlzLmJvdW5kcy5nZXRTb3V0aCgpLCB0aWxlSUQueikpXG4gICAgICAgIH07XG4gICAgICAgIGNvbnN0IGhpdCA9IHRpbGVJRC54ID49IGxldmVsLm1pblggJiYgdGlsZUlELnggPCBsZXZlbC5tYXhYICYmIHRpbGVJRC55ID49IGxldmVsLm1pblkgJiYgdGlsZUlELnkgPCBsZXZlbC5tYXhZO1xuICAgICAgICByZXR1cm4gaGl0O1xuICAgIH1cblxuICAgIGxuZ1gobG5nICAgICAgICAsIHpvb20gICAgICAgICkge1xuICAgICAgICByZXR1cm4gKGxuZyArIDE4MCkgKiAoTWF0aC5wb3coMiwgem9vbSkgLyAzNjApO1xuICAgIH1cblxuICAgIGxhdFkobGF0ICAgICAgICAsIHpvb20gICAgICAgICkge1xuICAgICAgICBjb25zdCBmID0gY2xhbXAoTWF0aC5zaW4oTWF0aC5QSSAvIDE4MCAqIGxhdCksIC0wLjk5OTksIDAuOTk5OSk7XG4gICAgICAgIGNvbnN0IHNjYWxlID0gTWF0aC5wb3coMiwgem9vbSkgLyAoMiAqIE1hdGguUEkpO1xuICAgICAgICByZXR1cm4gTWF0aC5wb3coMiwgem9vbSAtIDEpICsgMC41ICogTWF0aC5sb2coKDEgKyBmKSAvICgxIC0gZikpICogLXNjYWxlO1xuICAgIH1cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBUaWxlQm91bmRzO1xuIiwiLy8gICAgICBcblxuY29uc3Qgd2luZG93ID0gcmVxdWlyZSgnLi93aW5kb3cnKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcblxuLyoqXG4gKiBUaGUgdHlwZSBvZiBhIHJlc291cmNlLlxuICogQHByaXZhdGVcbiAqIEByZWFkb25seVxuICogQGVudW0ge3N0cmluZ31cbiAqL1xuY29uc3QgUmVzb3VyY2VUeXBlID0ge1xuICAgIFVua25vd246ICdVbmtub3duJyxcbiAgICBTdHlsZTogJ1N0eWxlJyxcbiAgICBTb3VyY2U6ICdTb3VyY2UnLFxuICAgIFRpbGU6ICdUaWxlJyxcbiAgICBHbHlwaHM6ICdHbHlwaHMnLFxuICAgIFNwcml0ZUltYWdlOiAnU3ByaXRlSW1hZ2UnLFxuICAgIFNwcml0ZUpTT046ICdTcHJpdGVKU09OJyxcbiAgICBJbWFnZTogJ0ltYWdlJ1xufTtcbmV4cG9ydHMuUmVzb3VyY2VUeXBlID0gUmVzb3VyY2VUeXBlO1xuXG5pZiAodHlwZW9mIE9iamVjdC5mcmVlemUgPT0gJ2Z1bmN0aW9uJykge1xuICAgIE9iamVjdC5mcmVlemUoUmVzb3VyY2VUeXBlKTtcbn1cblxuLyoqXG4gKiBBIGBSZXF1ZXN0UGFyYW1ldGVyc2Agb2JqZWN0IHRvIGJlIHJldHVybmVkIGZyb20gTWFwLm9wdGlvbnMudHJhbnNmb3JtUmVxdWVzdCBjYWxsYmFja3MuXG4gKiBAdHlwZWRlZiB7T2JqZWN0fSBSZXF1ZXN0UGFyYW1ldGVyc1xuICogQHByb3BlcnR5IHtzdHJpbmd9IHVybCBUaGUgVVJMIHRvIGJlIHJlcXVlc3RlZC5cbiAqIEBwcm9wZXJ0eSB7T2JqZWN0fSBoZWFkZXJzIFRoZSBoZWFkZXJzIHRvIGJlIHNlbnQgd2l0aCB0aGUgcmVxdWVzdC5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBjcmVkZW50aWFscyBgJ3NhbWUtb3JpZ2luJ3wnaW5jbHVkZSdgIFVzZSAnaW5jbHVkZScgdG8gc2VuZCBjb29raWVzIHdpdGggY3Jvc3Mtb3JpZ2luIHJlcXVlc3RzLlxuICovXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFxuICBcblxuY2xhc3MgQUpBWEVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICAgICAgICAgICAgICAgICAgIFxuICAgIGNvbnN0cnVjdG9yKG1lc3NhZ2UgICAgICAgICwgc3RhdHVzICAgICAgICApIHtcbiAgICAgICAgc3VwZXIobWVzc2FnZSk7XG4gICAgICAgIHRoaXMuc3RhdHVzID0gc3RhdHVzO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gbWFrZVJlcXVlc3QocmVxdWVzdFBhcmFtZXRlcnMgICAgICAgICAgICAgICAgICAgKSAgICAgICAgICAgICAgICAge1xuICAgIGNvbnN0IHhociAgICAgICAgICAgICAgICAgPSBuZXcgd2luZG93LlhNTEh0dHBSZXF1ZXN0KCk7XG5cbiAgICB4aHIub3BlbignR0VUJywgcmVxdWVzdFBhcmFtZXRlcnMudXJsLCB0cnVlKTtcbiAgICBmb3IgKGNvbnN0IGsgaW4gcmVxdWVzdFBhcmFtZXRlcnMuaGVhZGVycykge1xuICAgICAgICB4aHIuc2V0UmVxdWVzdEhlYWRlcihrLCByZXF1ZXN0UGFyYW1ldGVycy5oZWFkZXJzW2tdKTtcbiAgICB9XG4gICAgeGhyLndpdGhDcmVkZW50aWFscyA9IHJlcXVlc3RQYXJhbWV0ZXJzLmNyZWRlbnRpYWxzID09PSAnaW5jbHVkZSc7XG4gICAgcmV0dXJuIHhocjtcbn1cblxuZXhwb3J0cy5nZXRKU09OID0gZnVuY3Rpb24ocmVxdWVzdFBhcmFtZXRlcnMgICAgICAgICAgICAgICAgICAgLCBjYWxsYmFjayAgICAgICAgICAgICAgICAgKSB7XG4gICAgY29uc3QgeGhyID0gbWFrZVJlcXVlc3QocmVxdWVzdFBhcmFtZXRlcnMpO1xuICAgIHhoci5zZXRSZXF1ZXN0SGVhZGVyKCdBY2NlcHQnLCAnYXBwbGljYXRpb24vanNvbicpO1xuICAgIHhoci5vbmVycm9yID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIGNhbGxiYWNrKG5ldyBFcnJvcih4aHIuc3RhdHVzVGV4dCkpO1xuICAgIH07XG4gICAgeGhyLm9ubG9hZCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICBpZiAoeGhyLnN0YXR1cyA+PSAyMDAgJiYgeGhyLnN0YXR1cyA8IDMwMCAmJiB4aHIucmVzcG9uc2UpIHtcbiAgICAgICAgICAgIGxldCBkYXRhO1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBkYXRhID0gSlNPTi5wYXJzZSh4aHIucmVzcG9uc2UpO1xuICAgICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrKGVycik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjYWxsYmFjayhudWxsLCBkYXRhKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNhbGxiYWNrKG5ldyBBSkFYRXJyb3IoeGhyLnN0YXR1c1RleHQsIHhoci5zdGF0dXMpKTtcbiAgICAgICAgfVxuICAgIH07XG4gICAgeGhyLnNlbmQoKTtcbiAgICByZXR1cm4geGhyO1xufTtcblxuZXhwb3J0cy5nZXRBcnJheUJ1ZmZlciA9IGZ1bmN0aW9uKHJlcXVlc3RQYXJhbWV0ZXJzICAgICAgICAgICAgICAgICAgICwgY2FsbGJhY2sgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApIHtcbiAgICBjb25zdCB4aHIgPSBtYWtlUmVxdWVzdChyZXF1ZXN0UGFyYW1ldGVycyk7XG4gICAgeGhyLnJlc3BvbnNlVHlwZSA9ICdhcnJheWJ1ZmZlcic7XG4gICAgeGhyLm9uZXJyb3IgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgY2FsbGJhY2sobmV3IEVycm9yKHhoci5zdGF0dXNUZXh0KSk7XG4gICAgfTtcbiAgICB4aHIub25sb2FkID0gZnVuY3Rpb24oKSB7XG4gICAgICAgIGNvbnN0IHJlc3BvbnNlICAgICAgICAgICAgICA9IHhoci5yZXNwb25zZTtcbiAgICAgICAgaWYgKHJlc3BvbnNlLmJ5dGVMZW5ndGggPT09IDAgJiYgeGhyLnN0YXR1cyA9PT0gMjAwKSB7XG4gICAgICAgICAgICByZXR1cm4gY2FsbGJhY2sobmV3IEVycm9yKCdodHRwIHN0YXR1cyAyMDAgcmV0dXJuZWQgd2l0aG91dCBjb250ZW50LicpKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoeGhyLnN0YXR1cyA+PSAyMDAgJiYgeGhyLnN0YXR1cyA8IDMwMCAmJiB4aHIucmVzcG9uc2UpIHtcbiAgICAgICAgICAgIGNhbGxiYWNrKG51bGwsIHtcbiAgICAgICAgICAgICAgICBkYXRhOiByZXNwb25zZSxcbiAgICAgICAgICAgICAgICBjYWNoZUNvbnRyb2w6IHhoci5nZXRSZXNwb25zZUhlYWRlcignQ2FjaGUtQ29udHJvbCcpLFxuICAgICAgICAgICAgICAgIGV4cGlyZXM6IHhoci5nZXRSZXNwb25zZUhlYWRlcignRXhwaXJlcycpXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNhbGxiYWNrKG5ldyBBSkFYRXJyb3IoeGhyLnN0YXR1c1RleHQsIHhoci5zdGF0dXMpKTtcbiAgICAgICAgfVxuICAgIH07XG4gICAgeGhyLnNlbmQoKTtcbiAgICByZXR1cm4geGhyO1xufTtcblxuZnVuY3Rpb24gc2FtZU9yaWdpbih1cmwpIHtcbiAgICBjb25zdCBhICAgICAgICAgICAgICAgICAgICA9IHdpbmRvdy5kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG4gICAgYS5ocmVmID0gdXJsO1xuICAgIHJldHVybiBhLnByb3RvY29sID09PSB3aW5kb3cuZG9jdW1lbnQubG9jYXRpb24ucHJvdG9jb2wgJiYgYS5ob3N0ID09PSB3aW5kb3cuZG9jdW1lbnQubG9jYXRpb24uaG9zdDtcbn1cblxuY29uc3QgdHJhbnNwYXJlbnRQbmdVcmwgPSAnZGF0YTppbWFnZS9wbmc7YmFzZTY0LGlWQk9SdzBLR2dvQUFBQU5TVWhFVWdBQUFBRUFBQUFCQ0FZQUFBQWZGY1NKQUFBQUMwbEVRVlFZVjJOZ0FBSUFBQVVBQWFyVnlGRUFBQUFBU1VWT1JLNUNZSUk9JztcblxuZXhwb3J0cy5nZXRJbWFnZSA9IGZ1bmN0aW9uKHJlcXVlc3RQYXJhbWV0ZXJzICAgICAgICAgICAgICAgICAgICwgY2FsbGJhY2sgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSB7XG4gICAgLy8gcmVxdWVzdCB0aGUgaW1hZ2Ugd2l0aCBYSFIgdG8gd29yayBhcm91bmQgY2FjaGluZyBpc3N1ZXNcbiAgICAvLyBzZWUgaHR0cHM6Ly9naXRodWIuY29tL21hcGJveC9tYXBib3gtZ2wtanMvaXNzdWVzLzE0NzBcbiAgICByZXR1cm4gZXhwb3J0cy5nZXRBcnJheUJ1ZmZlcihyZXF1ZXN0UGFyYW1ldGVycywgKGVyciwgaW1nRGF0YSkgPT4ge1xuICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgICBjYWxsYmFjayhlcnIpO1xuICAgICAgICB9IGVsc2UgaWYgKGltZ0RhdGEpIHtcbiAgICAgICAgICAgIGNvbnN0IGltZyAgICAgICAgICAgICAgICAgICA9IG5ldyB3aW5kb3cuSW1hZ2UoKTtcbiAgICAgICAgICAgIGNvbnN0IFVSTCA9IHdpbmRvdy5VUkwgfHwgd2luZG93LndlYmtpdFVSTDtcbiAgICAgICAgICAgIGltZy5vbmxvYWQgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgY2FsbGJhY2sobnVsbCwgaW1nKTtcbiAgICAgICAgICAgICAgICBVUkwucmV2b2tlT2JqZWN0VVJMKGltZy5zcmMpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGNvbnN0IGJsb2IgICAgICAgPSBuZXcgd2luZG93LkJsb2IoW25ldyBVaW50OEFycmF5KGltZ0RhdGEuZGF0YSldLCB7IHR5cGU6ICdpbWFnZS9wbmcnIH0pO1xuICAgICAgICAgICAgKGltZyAgICAgKS5jYWNoZUNvbnRyb2wgPSBpbWdEYXRhLmNhY2hlQ29udHJvbDtcbiAgICAgICAgICAgIChpbWcgICAgICkuZXhwaXJlcyA9IGltZ0RhdGEuZXhwaXJlcztcbiAgICAgICAgICAgIGltZy5zcmMgPSBpbWdEYXRhLmRhdGEuYnl0ZUxlbmd0aCA/IFVSTC5jcmVhdGVPYmplY3RVUkwoYmxvYikgOiB0cmFuc3BhcmVudFBuZ1VybDtcbiAgICAgICAgfVxuICAgIH0pO1xufTtcblxuZXhwb3J0cy5nZXRWaWRlbyA9IGZ1bmN0aW9uKHVybHMgICAgICAgICAgICAgICAsIGNhbGxiYWNrICAgICAgICAgICAgICAgICAgICAgICAgICAgICkge1xuICAgIGNvbnN0IHZpZGVvICAgICAgICAgICAgICAgICAgID0gd2luZG93LmRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3ZpZGVvJyk7XG4gICAgdmlkZW8ub25sb2Fkc3RhcnQgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgY2FsbGJhY2sobnVsbCwgdmlkZW8pO1xuICAgIH07XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB1cmxzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGNvbnN0IHMgICAgICAgICAgICAgICAgICAgID0gd2luZG93LmRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NvdXJjZScpO1xuICAgICAgICBpZiAoIXNhbWVPcmlnaW4odXJsc1tpXSkpIHtcbiAgICAgICAgICAgIHZpZGVvLmNyb3NzT3JpZ2luID0gJ0Fub255bW91cyc7XG4gICAgICAgIH1cbiAgICAgICAgcy5zcmMgPSB1cmxzW2ldO1xuICAgICAgICB2aWRlby5hcHBlbmRDaGlsZChzKTtcbiAgICB9XG4gICAgcmV0dXJuIHZpZGVvO1xufTtcbiIsIi8vICAgICAgXG5cbi8qIGVzbGludC1lbnYgYnJvd3NlciAqL1xubW9kdWxlLmV4cG9ydHMgPSAoc2VsZiAgICAgICAgKTtcbiIsIi8vICAgICAgXG5cbmNvbnN0IHV0aWwgPSByZXF1aXJlKCcuL3V0aWwnKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXG5cbmZ1bmN0aW9uIF9hZGRFdmVudExpc3RlbmVyKHR5cGUgICAgICAgICwgbGlzdGVuZXIgICAgICAgICAgLCBsaXN0ZW5lckxpc3QgICAgICAgICAgICkge1xuICAgIGxpc3RlbmVyTGlzdFt0eXBlXSA9IGxpc3RlbmVyTGlzdFt0eXBlXSB8fCBbXTtcbiAgICBsaXN0ZW5lckxpc3RbdHlwZV0ucHVzaChsaXN0ZW5lcik7XG59XG5cbmZ1bmN0aW9uIF9yZW1vdmVFdmVudExpc3RlbmVyKHR5cGUgICAgICAgICwgbGlzdGVuZXIgICAgICAgICAgLCBsaXN0ZW5lckxpc3QgICAgICAgICAgICkge1xuICAgIGlmIChsaXN0ZW5lckxpc3QgJiYgbGlzdGVuZXJMaXN0W3R5cGVdKSB7XG4gICAgICAgIGNvbnN0IGluZGV4ID0gbGlzdGVuZXJMaXN0W3R5cGVdLmluZGV4T2YobGlzdGVuZXIpO1xuICAgICAgICBpZiAoaW5kZXggIT09IC0xKSB7XG4gICAgICAgICAgICBsaXN0ZW5lckxpc3RbdHlwZV0uc3BsaWNlKGluZGV4LCAxKTtcbiAgICAgICAgfVxuICAgIH1cbn1cblxuLyoqXG4gKiBNZXRob2RzIG1peGVkIGluIHRvIG90aGVyIGNsYXNzZXMgZm9yIGV2ZW50IGNhcGFiaWxpdGllcy5cbiAqXG4gKiBAbWl4aW4gRXZlbnRlZFxuICovXG5jbGFzcyBFdmVudGVkIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXG5cbiAgICAvKipcbiAgICAgKiBBZGRzIGEgbGlzdGVuZXIgdG8gYSBzcGVjaWZpZWQgZXZlbnQgdHlwZS5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIFRoZSBldmVudCB0eXBlIHRvIGFkZCBhIGxpc3RlbiBmb3IuXG4gICAgICogQHBhcmFtIHtGdW5jdGlvbn0gbGlzdGVuZXIgVGhlIGZ1bmN0aW9uIHRvIGJlIGNhbGxlZCB3aGVuIHRoZSBldmVudCBpcyBmaXJlZC5cbiAgICAgKiAgIFRoZSBsaXN0ZW5lciBmdW5jdGlvbiBpcyBjYWxsZWQgd2l0aCB0aGUgZGF0YSBvYmplY3QgcGFzc2VkIHRvIGBmaXJlYCxcbiAgICAgKiAgIGV4dGVuZGVkIHdpdGggYHRhcmdldGAgYW5kIGB0eXBlYCBwcm9wZXJ0aWVzLlxuICAgICAqIEByZXR1cm5zIHtPYmplY3R9IGB0aGlzYFxuICAgICAqL1xuICAgIG9uKHR5cGUgICAsIGxpc3RlbmVyICAgICAgICAgICkgICAgICAge1xuICAgICAgICB0aGlzLl9saXN0ZW5lcnMgPSB0aGlzLl9saXN0ZW5lcnMgfHwge307XG4gICAgICAgIF9hZGRFdmVudExpc3RlbmVyKHR5cGUsIGxpc3RlbmVyLCB0aGlzLl9saXN0ZW5lcnMpO1xuXG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlbW92ZXMgYSBwcmV2aW91c2x5IHJlZ2lzdGVyZWQgZXZlbnQgbGlzdGVuZXIuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gdHlwZSBUaGUgZXZlbnQgdHlwZSB0byByZW1vdmUgbGlzdGVuZXJzIGZvci5cbiAgICAgKiBAcGFyYW0ge0Z1bmN0aW9ufSBsaXN0ZW5lciBUaGUgbGlzdGVuZXIgZnVuY3Rpb24gdG8gcmVtb3ZlLlxuICAgICAqIEByZXR1cm5zIHtPYmplY3R9IGB0aGlzYFxuICAgICAqL1xuICAgIG9mZih0eXBlICAgLCBsaXN0ZW5lciAgICAgICAgICApIHtcbiAgICAgICAgX3JlbW92ZUV2ZW50TGlzdGVuZXIodHlwZSwgbGlzdGVuZXIsIHRoaXMuX2xpc3RlbmVycyk7XG4gICAgICAgIF9yZW1vdmVFdmVudExpc3RlbmVyKHR5cGUsIGxpc3RlbmVyLCB0aGlzLl9vbmVUaW1lTGlzdGVuZXJzKTtcblxuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBBZGRzIGEgbGlzdGVuZXIgdGhhdCB3aWxsIGJlIGNhbGxlZCBvbmx5IG9uY2UgdG8gYSBzcGVjaWZpZWQgZXZlbnQgdHlwZS5cbiAgICAgKlxuICAgICAqIFRoZSBsaXN0ZW5lciB3aWxsIGJlIGNhbGxlZCBmaXJzdCB0aW1lIHRoZSBldmVudCBmaXJlcyBhZnRlciB0aGUgbGlzdGVuZXIgaXMgcmVnaXN0ZXJlZC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIFRoZSBldmVudCB0eXBlIHRvIGxpc3RlbiBmb3IuXG4gICAgICogQHBhcmFtIHtGdW5jdGlvbn0gbGlzdGVuZXIgVGhlIGZ1bmN0aW9uIHRvIGJlIGNhbGxlZCB3aGVuIHRoZSBldmVudCBpcyBmaXJlZCB0aGUgZmlyc3QgdGltZS5cbiAgICAgKiBAcmV0dXJucyB7T2JqZWN0fSBgdGhpc2BcbiAgICAgKi9cbiAgICBvbmNlKHR5cGUgICAgICAgICwgbGlzdGVuZXIgICAgICAgICAgKSB7XG4gICAgICAgIHRoaXMuX29uZVRpbWVMaXN0ZW5lcnMgPSB0aGlzLl9vbmVUaW1lTGlzdGVuZXJzIHx8IHt9O1xuICAgICAgICBfYWRkRXZlbnRMaXN0ZW5lcih0eXBlLCBsaXN0ZW5lciwgdGhpcy5fb25lVGltZUxpc3RlbmVycyk7XG5cbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRmlyZXMgYW4gZXZlbnQgb2YgdGhlIHNwZWNpZmllZCB0eXBlLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgVGhlIHR5cGUgb2YgZXZlbnQgdG8gZmlyZS5cbiAgICAgKiBAcGFyYW0ge09iamVjdH0gW2RhdGFdIERhdGEgdG8gYmUgcGFzc2VkIHRvIGFueSBsaXN0ZW5lcnMuXG4gICAgICogQHJldHVybnMge09iamVjdH0gYHRoaXNgXG4gICAgICovXG4gICAgZmlyZSh0eXBlICAgICAgICAsIGRhdGEgICAgICAgICApIHtcbiAgICAgICAgaWYgKHRoaXMubGlzdGVucyh0eXBlKSkge1xuICAgICAgICAgICAgZGF0YSA9IHV0aWwuZXh0ZW5kKHt9LCBkYXRhLCB7dHlwZTogdHlwZSwgdGFyZ2V0OiB0aGlzfSk7XG5cbiAgICAgICAgICAgIC8vIG1ha2Ugc3VyZSBhZGRpbmcgb3IgcmVtb3ZpbmcgbGlzdGVuZXJzIGluc2lkZSBvdGhlciBsaXN0ZW5lcnMgd29uJ3QgY2F1c2UgYW4gaW5maW5pdGUgbG9vcFxuICAgICAgICAgICAgY29uc3QgbGlzdGVuZXJzID0gdGhpcy5fbGlzdGVuZXJzICYmIHRoaXMuX2xpc3RlbmVyc1t0eXBlXSA/IHRoaXMuX2xpc3RlbmVyc1t0eXBlXS5zbGljZSgpIDogW107XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGxpc3RlbmVyIG9mIGxpc3RlbmVycykge1xuICAgICAgICAgICAgICAgIGxpc3RlbmVyLmNhbGwodGhpcywgZGF0YSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IG9uZVRpbWVMaXN0ZW5lcnMgPSB0aGlzLl9vbmVUaW1lTGlzdGVuZXJzICYmIHRoaXMuX29uZVRpbWVMaXN0ZW5lcnNbdHlwZV0gPyB0aGlzLl9vbmVUaW1lTGlzdGVuZXJzW3R5cGVdLnNsaWNlKCkgOiBbXTtcbiAgICAgICAgICAgIGZvciAoY29uc3QgbGlzdGVuZXIgb2Ygb25lVGltZUxpc3RlbmVycykge1xuICAgICAgICAgICAgICAgIF9yZW1vdmVFdmVudExpc3RlbmVyKHR5cGUsIGxpc3RlbmVyLCB0aGlzLl9vbmVUaW1lTGlzdGVuZXJzKTtcbiAgICAgICAgICAgICAgICBsaXN0ZW5lci5jYWxsKHRoaXMsIGRhdGEpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodGhpcy5fZXZlbnRlZFBhcmVudCkge1xuICAgICAgICAgICAgICAgIHRoaXMuX2V2ZW50ZWRQYXJlbnQuZmlyZSh0eXBlLCB1dGlsLmV4dGVuZCh7fSwgZGF0YSwgdHlwZW9mIHRoaXMuX2V2ZW50ZWRQYXJlbnREYXRhID09PSAnZnVuY3Rpb24nID8gdGhpcy5fZXZlbnRlZFBhcmVudERhdGEoKSA6IHRoaXMuX2V2ZW50ZWRQYXJlbnREYXRhKSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgLy8gVG8gZW5zdXJlIHRoYXQgbm8gZXJyb3IgZXZlbnRzIGFyZSBkcm9wcGVkLCBwcmludCB0aGVtIHRvIHRoZVxuICAgICAgICAvLyBjb25zb2xlIGlmIHRoZXkgaGF2ZSBubyBsaXN0ZW5lcnMuXG4gICAgICAgIH0gZWxzZSBpZiAodXRpbC5lbmRzV2l0aCh0eXBlLCAnZXJyb3InKSkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcigoZGF0YSAmJiBkYXRhLmVycm9yKSB8fCBkYXRhIHx8ICdFbXB0eSBlcnJvciBldmVudCcpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyBhIHRydWUgaWYgdGhpcyBpbnN0YW5jZSBvZiBFdmVudGVkIG9yIGFueSBmb3J3YXJkZWVkIGluc3RhbmNlcyBvZiBFdmVudGVkIGhhdmUgYSBsaXN0ZW5lciBmb3IgdGhlIHNwZWNpZmllZCB0eXBlLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgVGhlIGV2ZW50IHR5cGVcbiAgICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gYHRydWVgIGlmIHRoZXJlIGlzIGF0IGxlYXN0IG9uZSByZWdpc3RlcmVkIGxpc3RlbmVyIGZvciBzcGVjaWZpZWQgZXZlbnQgdHlwZSwgYGZhbHNlYCBvdGhlcndpc2VcbiAgICAgKi9cbiAgICBsaXN0ZW5zKHR5cGUgICAgICAgICkge1xuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgKHRoaXMuX2xpc3RlbmVycyAmJiB0aGlzLl9saXN0ZW5lcnNbdHlwZV0gJiYgdGhpcy5fbGlzdGVuZXJzW3R5cGVdLmxlbmd0aCA+IDApIHx8XG4gICAgICAgICAgICAodGhpcy5fb25lVGltZUxpc3RlbmVycyAmJiB0aGlzLl9vbmVUaW1lTGlzdGVuZXJzW3R5cGVdICYmIHRoaXMuX29uZVRpbWVMaXN0ZW5lcnNbdHlwZV0ubGVuZ3RoID4gMCkgfHxcbiAgICAgICAgICAgICh0aGlzLl9ldmVudGVkUGFyZW50ICYmIHRoaXMuX2V2ZW50ZWRQYXJlbnQubGlzdGVucyh0eXBlKSlcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBCdWJibGUgYWxsIGV2ZW50cyBmaXJlZCBieSB0aGlzIGluc3RhbmNlIG9mIEV2ZW50ZWQgdG8gdGhpcyBwYXJlbnQgaW5zdGFuY2Ugb2YgRXZlbnRlZC5cbiAgICAgKlxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHJldHVybnMge09iamVjdH0gYHRoaXNgXG4gICAgICovXG4gICAgc2V0RXZlbnRlZFBhcmVudChwYXJlbnQgICAgICAgICAgLCBkYXRhICAgICAgICAgICAgICAgICAgICAgICAgKSB7XG4gICAgICAgIHRoaXMuX2V2ZW50ZWRQYXJlbnQgPSBwYXJlbnQ7XG4gICAgICAgIHRoaXMuX2V2ZW50ZWRQYXJlbnREYXRhID0gZGF0YTtcblxuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG59XG5cbm1vZHVsZS5leHBvcnRzID0gRXZlbnRlZDtcbiIsIi8vICAgICAgXG5cbmNvbnN0IFVuaXRCZXppZXIgPSByZXF1aXJlKCdAbWFwYm94L3VuaXRiZXppZXInKTtcbmNvbnN0IENvb3JkaW5hdGUgPSByZXF1aXJlKCcuLi9nZW8vY29vcmRpbmF0ZScpO1xuY29uc3QgUG9pbnQgPSByZXF1aXJlKCdAbWFwYm94L3BvaW50LWdlb21ldHJ5Jyk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFxuXG4vKipcbiAqIEBtb2R1bGUgdXRpbFxuICogQHByaXZhdGVcbiAqL1xuXG4vKipcbiAqIEdpdmVuIGEgdmFsdWUgYHRgIHRoYXQgdmFyaWVzIGJldHdlZW4gMCBhbmQgMSwgcmV0dXJuXG4gKiBhbiBpbnRlcnBvbGF0aW9uIGZ1bmN0aW9uIHRoYXQgZWFzZXMgYmV0d2VlbiAwIGFuZCAxIGluIGEgcGxlYXNpbmdcbiAqIGN1YmljIGluLW91dCBmYXNoaW9uLlxuICpcbiAqIEBwcml2YXRlXG4gKi9cbmV4cG9ydHMuZWFzZUN1YmljSW5PdXQgPSBmdW5jdGlvbih0ICAgICAgICApICAgICAgICAge1xuICAgIGlmICh0IDw9IDApIHJldHVybiAwO1xuICAgIGlmICh0ID49IDEpIHJldHVybiAxO1xuICAgIGNvbnN0IHQyID0gdCAqIHQsXG4gICAgICAgIHQzID0gdDIgKiB0O1xuICAgIHJldHVybiA0ICogKHQgPCAwLjUgPyB0MyA6IDMgKiAodCAtIHQyKSArIHQzIC0gMC43NSk7XG59O1xuXG4vKipcbiAqIEdpdmVuIGdpdmVuICh4LCB5KSwgKHgxLCB5MSkgY29udHJvbCBwb2ludHMgZm9yIGEgYmV6aWVyIGN1cnZlLFxuICogcmV0dXJuIGEgZnVuY3Rpb24gdGhhdCBpbnRlcnBvbGF0ZXMgYWxvbmcgdGhhdCBjdXJ2ZS5cbiAqXG4gKiBAcGFyYW0gcDF4IGNvbnRyb2wgcG9pbnQgMSB4IGNvb3JkaW5hdGVcbiAqIEBwYXJhbSBwMXkgY29udHJvbCBwb2ludCAxIHkgY29vcmRpbmF0ZVxuICogQHBhcmFtIHAyeCBjb250cm9sIHBvaW50IDIgeCBjb29yZGluYXRlXG4gKiBAcGFyYW0gcDJ5IGNvbnRyb2wgcG9pbnQgMiB5IGNvb3JkaW5hdGVcbiAqIEBwcml2YXRlXG4gKi9cbmV4cG9ydHMuYmV6aWVyID0gZnVuY3Rpb24ocDF4ICAgICAgICAsIHAxeSAgICAgICAgLCBwMnggICAgICAgICwgcDJ5ICAgICAgICApICAgICAgICAgICAgICAgICAgICAgICAge1xuICAgIGNvbnN0IGJlemllciA9IG5ldyBVbml0QmV6aWVyKHAxeCwgcDF5LCBwMngsIHAyeSk7XG4gICAgcmV0dXJuIGZ1bmN0aW9uKHQgICAgICAgICkge1xuICAgICAgICByZXR1cm4gYmV6aWVyLnNvbHZlKHQpO1xuICAgIH07XG59O1xuXG4vKipcbiAqIEEgZGVmYXVsdCBiZXppZXItY3VydmUgcG93ZXJlZCBlYXNpbmcgZnVuY3Rpb24gd2l0aFxuICogY29udHJvbCBwb2ludHMgKDAuMjUsIDAuMSkgYW5kICgwLjI1LCAxKVxuICpcbiAqIEBwcml2YXRlXG4gKi9cbmV4cG9ydHMuZWFzZSA9IGV4cG9ydHMuYmV6aWVyKDAuMjUsIDAuMSwgMC4yNSwgMSk7XG5cbi8qKlxuICogY29uc3RyYWluIG4gdG8gdGhlIGdpdmVuIHJhbmdlIHZpYSBtaW4gKyBtYXhcbiAqXG4gKiBAcGFyYW0gbiB2YWx1ZVxuICogQHBhcmFtIG1pbiB0aGUgbWluaW11bSB2YWx1ZSB0byBiZSByZXR1cm5lZFxuICogQHBhcmFtIG1heCB0aGUgbWF4aW11bSB2YWx1ZSB0byBiZSByZXR1cm5lZFxuICogQHJldHVybnMgdGhlIGNsYW1wZWQgdmFsdWVcbiAqIEBwcml2YXRlXG4gKi9cbmV4cG9ydHMuY2xhbXAgPSBmdW5jdGlvbiAobiAgICAgICAgLCBtaW4gICAgICAgICwgbWF4ICAgICAgICApICAgICAgICAge1xuICAgIHJldHVybiBNYXRoLm1pbihtYXgsIE1hdGgubWF4KG1pbiwgbikpO1xufTtcblxuLyoqXG4gKiBjb25zdHJhaW4gbiB0byB0aGUgZ2l2ZW4gcmFuZ2UsIGV4Y2x1ZGluZyB0aGUgbWluaW11bSwgdmlhIG1vZHVsYXIgYXJpdGhtZXRpY1xuICpcbiAqIEBwYXJhbSBuIHZhbHVlXG4gKiBAcGFyYW0gbWluIHRoZSBtaW5pbXVtIHZhbHVlIHRvIGJlIHJldHVybmVkLCBleGNsdXNpdmVcbiAqIEBwYXJhbSBtYXggdGhlIG1heGltdW0gdmFsdWUgdG8gYmUgcmV0dXJuZWQsIGluY2x1c2l2ZVxuICogQHJldHVybnMgY29uc3RyYWluZWQgbnVtYmVyXG4gKiBAcHJpdmF0ZVxuICovXG5leHBvcnRzLndyYXAgPSBmdW5jdGlvbiAobiAgICAgICAgLCBtaW4gICAgICAgICwgbWF4ICAgICAgICApICAgICAgICAge1xuICAgIGNvbnN0IGQgPSBtYXggLSBtaW47XG4gICAgY29uc3QgdyA9ICgobiAtIG1pbikgJSBkICsgZCkgJSBkICsgbWluO1xuICAgIHJldHVybiAodyA9PT0gbWluKSA/IG1heCA6IHc7XG59O1xuXG4vKlxuICogQ2FsbCBhbiBhc3luY2hyb25vdXMgZnVuY3Rpb24gb24gYW4gYXJyYXkgb2YgYXJndW1lbnRzLFxuICogY2FsbGluZyBgY2FsbGJhY2tgIHdpdGggdGhlIGNvbXBsZXRlZCByZXN1bHRzIG9mIGFsbCBjYWxscy5cbiAqXG4gKiBAcGFyYW0gYXJyYXkgaW5wdXQgdG8gZWFjaCBjYWxsIG9mIHRoZSBhc3luYyBmdW5jdGlvbi5cbiAqIEBwYXJhbSBmbiBhbiBhc3luYyBmdW5jdGlvbiB3aXRoIHNpZ25hdHVyZSAoZGF0YSwgY2FsbGJhY2spXG4gKiBAcGFyYW0gY2FsbGJhY2sgYSBjYWxsYmFjayBydW4gYWZ0ZXIgYWxsIGFzeW5jIHdvcmsgaXMgZG9uZS5cbiAqIGNhbGxlZCB3aXRoIGFuIGFycmF5LCBjb250YWluaW5nIHRoZSByZXN1bHRzIG9mIGVhY2ggYXN5bmMgY2FsbC5cbiAqIEBwcml2YXRlXG4gKi9cbmV4cG9ydHMuYXN5bmNBbGwgPSBmdW5jdGlvbiAgICAgICAgICAgICAgIChcbiAgICBhcnJheSAgICAgICAgICAgICAsXG4gICAgZm4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFxuICAgIGNhbGxiYWNrICAgICAgICAgICAgICAgICAgICAgICAgIFxuKSB7XG4gICAgaWYgKCFhcnJheS5sZW5ndGgpIHsgcmV0dXJuIGNhbGxiYWNrKG51bGwsIFtdKTsgfVxuICAgIGxldCByZW1haW5pbmcgPSBhcnJheS5sZW5ndGg7XG4gICAgY29uc3QgcmVzdWx0cyA9IG5ldyBBcnJheShhcnJheS5sZW5ndGgpO1xuICAgIGxldCBlcnJvciA9IG51bGw7XG4gICAgYXJyYXkuZm9yRWFjaCgoaXRlbSwgaSkgPT4ge1xuICAgICAgICBmbihpdGVtLCAoZXJyLCByZXN1bHQpID0+IHtcbiAgICAgICAgICAgIGlmIChlcnIpIGVycm9yID0gZXJyO1xuICAgICAgICAgICAgcmVzdWx0c1tpXSA9ICgocmVzdWx0ICAgICApICAgICAgICApOyAvLyBodHRwczovL2dpdGh1Yi5jb20vZmFjZWJvb2svZmxvdy9pc3N1ZXMvMjEyM1xuICAgICAgICAgICAgaWYgKC0tcmVtYWluaW5nID09PSAwKSBjYWxsYmFjayhlcnJvciwgcmVzdWx0cyk7XG4gICAgICAgIH0pO1xuICAgIH0pO1xufTtcblxuLypcbiAqIFBvbHlmaWxsIGZvciBPYmplY3QudmFsdWVzLiBOb3QgZnVsbHkgc3BlYyBjb21wbGlhbnQsIGJ1dCB3ZSBkb24ndFxuICogbmVlZCBpdCB0byBiZS5cbiAqXG4gKiBAcHJpdmF0ZVxuICovXG5leHBvcnRzLnZhbHVlcyA9IGZ1bmN0aW9uICAgKG9iaiAgICAgICAgICAgICAgICAgICAgKSAgICAgICAgICAge1xuICAgIGNvbnN0IHJlc3VsdCA9IFtdO1xuICAgIGZvciAoY29uc3QgayBpbiBvYmopIHtcbiAgICAgICAgcmVzdWx0LnB1c2gob2JqW2tdKTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbn07XG5cbi8qXG4gKiBDb21wdXRlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGtleXMgaW4gb25lIG9iamVjdCBhbmQgdGhlIGtleXNcbiAqIGluIGFub3RoZXIgb2JqZWN0LlxuICpcbiAqIEByZXR1cm5zIGtleXMgZGlmZmVyZW5jZVxuICogQHByaXZhdGVcbiAqL1xuZXhwb3J0cy5rZXlzRGlmZmVyZW5jZSA9IGZ1bmN0aW9uICAgICAgKG9iaiAgICAgICAgICAgICAgICAgICAgLCBvdGhlciAgICAgICAgICAgICAgICAgICAgKSAgICAgICAgICAgICAgICB7XG4gICAgY29uc3QgZGlmZmVyZW5jZSA9IFtdO1xuICAgIGZvciAoY29uc3QgaSBpbiBvYmopIHtcbiAgICAgICAgaWYgKCEoaSBpbiBvdGhlcikpIHtcbiAgICAgICAgICAgIGRpZmZlcmVuY2UucHVzaChpKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZGlmZmVyZW5jZTtcbn07XG5cbi8qKlxuICogR2l2ZW4gYSBkZXN0aW5hdGlvbiBvYmplY3QgYW5kIG9wdGlvbmFsbHkgbWFueSBzb3VyY2Ugb2JqZWN0cyxcbiAqIGNvcHkgYWxsIHByb3BlcnRpZXMgZnJvbSB0aGUgc291cmNlIG9iamVjdHMgaW50byB0aGUgZGVzdGluYXRpb24uXG4gKiBUaGUgbGFzdCBzb3VyY2Ugb2JqZWN0IGdpdmVuIG92ZXJyaWRlcyBwcm9wZXJ0aWVzIGZyb20gcHJldmlvdXNcbiAqIHNvdXJjZSBvYmplY3RzLlxuICpcbiAqIEBwYXJhbSBkZXN0IGRlc3RpbmF0aW9uIG9iamVjdFxuICogQHBhcmFtIHNvdXJjZXMgc291cmNlcyBmcm9tIHdoaWNoIHByb3BlcnRpZXMgYXJlIHB1bGxlZFxuICogQHByaXZhdGVcbiAqL1xuZXhwb3J0cy5leHRlbmQgPSBmdW5jdGlvbiAoZGVzdCAgICAgICAgLCAuLi5zb3VyY2VzICAgICAgICAgICAgICAgICkgICAgICAgICB7XG4gICAgZm9yIChjb25zdCBzcmMgb2Ygc291cmNlcykge1xuICAgICAgICBmb3IgKGNvbnN0IGsgaW4gc3JjKSB7XG4gICAgICAgICAgICBkZXN0W2tdID0gc3JjW2tdO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiBkZXN0O1xufTtcblxuLyoqXG4gKiBHaXZlbiBhbiBvYmplY3QgYW5kIGEgbnVtYmVyIG9mIHByb3BlcnRpZXMgYXMgc3RyaW5ncywgcmV0dXJuIHZlcnNpb25cbiAqIG9mIHRoYXQgb2JqZWN0IHdpdGggb25seSB0aG9zZSBwcm9wZXJ0aWVzLlxuICpcbiAqIEBwYXJhbSBzcmMgdGhlIG9iamVjdFxuICogQHBhcmFtIHByb3BlcnRpZXMgYW4gYXJyYXkgb2YgcHJvcGVydHkgbmFtZXMgY2hvc2VuXG4gKiB0byBhcHBlYXIgb24gdGhlIHJlc3VsdGluZyBvYmplY3QuXG4gKiBAcmV0dXJucyBvYmplY3Qgd2l0aCBsaW1pdGVkIHByb3BlcnRpZXMuXG4gKiBAZXhhbXBsZVxuICogdmFyIGZvbyA9IHsgbmFtZTogJ0NoYXJsaWUnLCBhZ2U6IDEwIH07XG4gKiB2YXIganVzdE5hbWUgPSBwaWNrKGZvbywgWyduYW1lJ10pO1xuICogLy8ganVzdE5hbWUgPSB7IG5hbWU6ICdDaGFybGllJyB9XG4gKiBAcHJpdmF0ZVxuICovXG5leHBvcnRzLnBpY2sgPSBmdW5jdGlvbiAoc3JjICAgICAgICAsIHByb3BlcnRpZXMgICAgICAgICAgICAgICApICAgICAgICAge1xuICAgIGNvbnN0IHJlc3VsdCA9IHt9O1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcHJvcGVydGllcy5sZW5ndGg7IGkrKykge1xuICAgICAgICBjb25zdCBrID0gcHJvcGVydGllc1tpXTtcbiAgICAgICAgaWYgKGsgaW4gc3JjKSB7XG4gICAgICAgICAgICByZXN1bHRba10gPSBzcmNba107XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbn07XG5cbmxldCBpZCA9IDE7XG5cbi8qKlxuICogUmV0dXJuIGEgdW5pcXVlIG51bWVyaWMgaWQsIHN0YXJ0aW5nIGF0IDEgYW5kIGluY3JlbWVudGluZyB3aXRoXG4gKiBlYWNoIGNhbGwuXG4gKlxuICogQHJldHVybnMgdW5pcXVlIG51bWVyaWMgaWQuXG4gKiBAcHJpdmF0ZVxuICovXG5leHBvcnRzLnVuaXF1ZUlkID0gZnVuY3Rpb24gKCkgICAgICAgICB7XG4gICAgcmV0dXJuIGlkKys7XG59O1xuXG4vKipcbiAqIEdpdmVuIGFuIGFycmF5IG9mIG1lbWJlciBmdW5jdGlvbiBuYW1lcyBhcyBzdHJpbmdzLCByZXBsYWNlIGFsbCBvZiB0aGVtXG4gKiB3aXRoIGJvdW5kIHZlcnNpb25zIHRoYXQgd2lsbCBhbHdheXMgcmVmZXIgdG8gYGNvbnRleHRgIGFzIGB0aGlzYC4gVGhpc1xuICogaXMgdXNlZnVsIGZvciBjbGFzc2VzIHdoZXJlIG90aGVyd2lzZSBldmVudCBiaW5kaW5ncyB3b3VsZCByZWFzc2lnblxuICogYHRoaXNgIHRvIHRoZSBldmVudGVkIG9iamVjdCBvciBzb21lIG90aGVyIHZhbHVlOiB0aGlzIGxldHMgeW91IGVuc3VyZVxuICogdGhlIGB0aGlzYCB2YWx1ZSBhbHdheXMuXG4gKlxuICogQHBhcmFtIGZucyBsaXN0IG9mIG1lbWJlciBmdW5jdGlvbiBuYW1lc1xuICogQHBhcmFtIGNvbnRleHQgdGhlIGNvbnRleHQgdmFsdWVcbiAqIEBleGFtcGxlXG4gKiBmdW5jdGlvbiBNeUNsYXNzKCkge1xuICogICBiaW5kQWxsKFsnb250aW1lciddLCB0aGlzKTtcbiAqICAgdGhpcy5uYW1lID0gJ1RvbSc7XG4gKiB9XG4gKiBNeUNsYXNzLnByb3RvdHlwZS5vbnRpbWVyID0gZnVuY3Rpb24oKSB7XG4gKiAgIGFsZXJ0KHRoaXMubmFtZSk7XG4gKiB9O1xuICogdmFyIG15Q2xhc3MgPSBuZXcgTXlDbGFzcygpO1xuICogc2V0VGltZW91dChteUNsYXNzLm9udGltZXIsIDEwMCk7XG4gKiBAcHJpdmF0ZVxuICovXG5leHBvcnRzLmJpbmRBbGwgPSBmdW5jdGlvbihmbnMgICAgICAgICAgICAgICAsIGNvbnRleHQgICAgICAgICkgICAgICAge1xuICAgIGZucy5mb3JFYWNoKChmbikgPT4ge1xuICAgICAgICBpZiAoIWNvbnRleHRbZm5dKSB7IHJldHVybjsgfVxuICAgICAgICBjb250ZXh0W2ZuXSA9IGNvbnRleHRbZm5dLmJpbmQoY29udGV4dCk7XG4gICAgfSk7XG59O1xuXG4vKipcbiAqIEdpdmVuIGEgbGlzdCBvZiBjb29yZGluYXRlcywgZ2V0IHRoZWlyIGNlbnRlciBhcyBhIGNvb3JkaW5hdGUuXG4gKlxuICogQHJldHVybnMgY2VudGVycG9pbnRcbiAqIEBwcml2YXRlXG4gKi9cbmV4cG9ydHMuZ2V0Q29vcmRpbmF0ZXNDZW50ZXIgPSBmdW5jdGlvbihjb29yZHMgICAgICAgICAgICAgICAgICAgKSAgICAgICAgICAgICB7XG4gICAgbGV0IG1pblggPSBJbmZpbml0eTtcbiAgICBsZXQgbWluWSA9IEluZmluaXR5O1xuICAgIGxldCBtYXhYID0gLUluZmluaXR5O1xuICAgIGxldCBtYXhZID0gLUluZmluaXR5O1xuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBjb29yZHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgbWluWCA9IE1hdGgubWluKG1pblgsIGNvb3Jkc1tpXS5jb2x1bW4pO1xuICAgICAgICBtaW5ZID0gTWF0aC5taW4obWluWSwgY29vcmRzW2ldLnJvdyk7XG4gICAgICAgIG1heFggPSBNYXRoLm1heChtYXhYLCBjb29yZHNbaV0uY29sdW1uKTtcbiAgICAgICAgbWF4WSA9IE1hdGgubWF4KG1heFksIGNvb3Jkc1tpXS5yb3cpO1xuICAgIH1cblxuICAgIGNvbnN0IGR4ID0gbWF4WCAtIG1pblg7XG4gICAgY29uc3QgZHkgPSBtYXhZIC0gbWluWTtcbiAgICBjb25zdCBkTWF4ID0gTWF0aC5tYXgoZHgsIGR5KTtcbiAgICBjb25zdCB6b29tID0gTWF0aC5tYXgoMCwgTWF0aC5mbG9vcigtTWF0aC5sb2coZE1heCkgLyBNYXRoLkxOMikpO1xuICAgIHJldHVybiBuZXcgQ29vcmRpbmF0ZSgobWluWCArIG1heFgpIC8gMiwgKG1pblkgKyBtYXhZKSAvIDIsIDApXG4gICAgICAgIC56b29tVG8oem9vbSk7XG59O1xuXG4vKipcbiAqIERldGVybWluZSBpZiBhIHN0cmluZyBlbmRzIHdpdGggYSBwYXJ0aWN1bGFyIHN1YnN0cmluZ1xuICpcbiAqIEBwcml2YXRlXG4gKi9cbmV4cG9ydHMuZW5kc1dpdGggPSBmdW5jdGlvbihzdHJpbmcgICAgICAgICwgc3VmZml4ICAgICAgICApICAgICAgICAgIHtcbiAgICByZXR1cm4gc3RyaW5nLmluZGV4T2Yoc3VmZml4LCBzdHJpbmcubGVuZ3RoIC0gc3VmZml4Lmxlbmd0aCkgIT09IC0xO1xufTtcblxuLyoqXG4gKiBDcmVhdGUgYW4gb2JqZWN0IGJ5IG1hcHBpbmcgYWxsIHRoZSB2YWx1ZXMgb2YgYW4gZXhpc3Rpbmcgb2JqZWN0IHdoaWxlXG4gKiBwcmVzZXJ2aW5nIHRoZWlyIGtleXMuXG4gKlxuICogQHByaXZhdGVcbiAqL1xuZXhwb3J0cy5tYXBPYmplY3QgPSBmdW5jdGlvbihpbnB1dCAgICAgICAgLCBpdGVyYXRvciAgICAgICAgICAsIGNvbnRleHQgICAgICAgICApICAgICAgICAge1xuICAgIGNvbnN0IG91dHB1dCA9IHt9O1xuICAgIGZvciAoY29uc3Qga2V5IGluIGlucHV0KSB7XG4gICAgICAgIG91dHB1dFtrZXldID0gaXRlcmF0b3IuY2FsbChjb250ZXh0IHx8IHRoaXMsIGlucHV0W2tleV0sIGtleSwgaW5wdXQpO1xuICAgIH1cbiAgICByZXR1cm4gb3V0cHV0O1xufTtcblxuLyoqXG4gKiBDcmVhdGUgYW4gb2JqZWN0IGJ5IGZpbHRlcmluZyBvdXQgdmFsdWVzIG9mIGFuIGV4aXN0aW5nIG9iamVjdC5cbiAqXG4gKiBAcHJpdmF0ZVxuICovXG5leHBvcnRzLmZpbHRlck9iamVjdCA9IGZ1bmN0aW9uKGlucHV0ICAgICAgICAsIGl0ZXJhdG9yICAgICAgICAgICwgY29udGV4dCAgICAgICAgICkgICAgICAgICB7XG4gICAgY29uc3Qgb3V0cHV0ID0ge307XG4gICAgZm9yIChjb25zdCBrZXkgaW4gaW5wdXQpIHtcbiAgICAgICAgaWYgKGl0ZXJhdG9yLmNhbGwoY29udGV4dCB8fCB0aGlzLCBpbnB1dFtrZXldLCBrZXksIGlucHV0KSkge1xuICAgICAgICAgICAgb3V0cHV0W2tleV0gPSBpbnB1dFtrZXldO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiBvdXRwdXQ7XG59O1xuXG4vKipcbiAqIERlZXBseSBjb21wYXJlcyB0d28gb2JqZWN0IGxpdGVyYWxzLlxuICpcbiAqIEBwcml2YXRlXG4gKi9cbmV4cG9ydHMuZGVlcEVxdWFsID0gZnVuY3Rpb24oYSAgICAgICAgLCBiICAgICAgICApICAgICAgICAgIHtcbiAgICBpZiAoQXJyYXkuaXNBcnJheShhKSkge1xuICAgICAgICBpZiAoIUFycmF5LmlzQXJyYXkoYikgfHwgYS5sZW5ndGggIT09IGIubGVuZ3RoKSByZXR1cm4gZmFsc2U7XG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYS5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgaWYgKCFleHBvcnRzLmRlZXBFcXVhbChhW2ldLCBiW2ldKSkgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgICBpZiAodHlwZW9mIGEgPT09ICdvYmplY3QnICYmIGEgIT09IG51bGwgJiYgYiAhPT0gbnVsbCkge1xuICAgICAgICBpZiAoISh0eXBlb2YgYiA9PT0gJ29iamVjdCcpKSByZXR1cm4gZmFsc2U7XG4gICAgICAgIGNvbnN0IGtleXMgPSBPYmplY3Qua2V5cyhhKTtcbiAgICAgICAgaWYgKGtleXMubGVuZ3RoICE9PSBPYmplY3Qua2V5cyhiKS5sZW5ndGgpIHJldHVybiBmYWxzZTtcbiAgICAgICAgZm9yIChjb25zdCBrZXkgaW4gYSkge1xuICAgICAgICAgICAgaWYgKCFleHBvcnRzLmRlZXBFcXVhbChhW2tleV0sIGJba2V5XSkpIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgcmV0dXJuIGEgPT09IGI7XG59O1xuXG4vKipcbiAqIERlZXBseSBjbG9uZXMgdHdvIG9iamVjdHMuXG4gKlxuICogQHByaXZhdGVcbiAqL1xuZXhwb3J0cy5jbG9uZSA9IGZ1bmN0aW9uICAgKGlucHV0ICAgKSAgICB7XG4gICAgaWYgKEFycmF5LmlzQXJyYXkoaW5wdXQpKSB7XG4gICAgICAgIHJldHVybiBpbnB1dC5tYXAoZXhwb3J0cy5jbG9uZSk7XG4gICAgfSBlbHNlIGlmICh0eXBlb2YgaW5wdXQgPT09ICdvYmplY3QnICYmIGlucHV0KSB7XG4gICAgICAgIHJldHVybiAoKGV4cG9ydHMubWFwT2JqZWN0KGlucHV0LCBleHBvcnRzLmNsb25lKSAgICAgKSAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIGlucHV0O1xuICAgIH1cbn07XG5cbi8qKlxuICogQ2hlY2sgaWYgdHdvIGFycmF5cyBoYXZlIGF0IGxlYXN0IG9uZSBjb21tb24gZWxlbWVudC5cbiAqXG4gKiBAcHJpdmF0ZVxuICovXG5leHBvcnRzLmFycmF5c0ludGVyc2VjdCA9IGZ1bmN0aW9uICAgKGEgICAgICAgICAgLCBiICAgICAgICAgICkgICAgICAgICAge1xuICAgIGZvciAobGV0IGwgPSAwOyBsIDwgYS5sZW5ndGg7IGwrKykge1xuICAgICAgICBpZiAoYi5pbmRleE9mKGFbbF0pID49IDApIHJldHVybiB0cnVlO1xuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG59O1xuXG4vKipcbiAqIFByaW50IGEgd2FybmluZyBtZXNzYWdlIHRvIHRoZSBjb25zb2xlIGFuZCBlbnN1cmUgZHVwbGljYXRlIHdhcm5pbmcgbWVzc2FnZXNcbiAqIGFyZSBub3QgcHJpbnRlZC5cbiAqXG4gKiBAcHJpdmF0ZVxuICovXG5jb25zdCB3YXJuT25jZUhpc3RvcnkgICAgICAgICAgICAgICAgICAgICAgICAgICA9IHt9O1xuZXhwb3J0cy53YXJuT25jZSA9IGZ1bmN0aW9uKG1lc3NhZ2UgICAgICAgICkgICAgICAge1xuICAgIGlmICghd2Fybk9uY2VIaXN0b3J5W21lc3NhZ2VdKSB7XG4gICAgICAgIC8vIGNvbnNvbGUgaXNuJ3QgZGVmaW5lZCBpbiBzb21lIFdlYldvcmtlcnMsIHNlZSAjMjU1OFxuICAgICAgICBpZiAodHlwZW9mIGNvbnNvbGUgIT09IFwidW5kZWZpbmVkXCIpIGNvbnNvbGUud2FybihtZXNzYWdlKTtcbiAgICAgICAgd2Fybk9uY2VIaXN0b3J5W21lc3NhZ2VdID0gdHJ1ZTtcbiAgICB9XG59O1xuXG4vKipcbiAqIEluZGljYXRlcyBpZiB0aGUgcHJvdmlkZWQgUG9pbnRzIGFyZSBpbiBhIGNvdW50ZXIgY2xvY2t3aXNlICh0cnVlKSBvciBjbG9ja3dpc2UgKGZhbHNlKSBvcmRlclxuICpcbiAqIEByZXR1cm5zIHRydWUgZm9yIGEgY291bnRlciBjbG9ja3dpc2Ugc2V0IG9mIHBvaW50c1xuICovXG4vLyBodHRwOi8vYnJ5Y2Vib2UuY29tLzIwMDYvMTAvMjMvbGluZS1zZWdtZW50LWludGVyc2VjdGlvbi1hbGdvcml0aG0vXG5leHBvcnRzLmlzQ291bnRlckNsb2Nrd2lzZSA9IGZ1bmN0aW9uKGEgICAgICAgLCBiICAgICAgICwgYyAgICAgICApICAgICAgICAgIHtcbiAgICByZXR1cm4gKGMueSAtIGEueSkgKiAoYi54IC0gYS54KSA+IChiLnkgLSBhLnkpICogKGMueCAtIGEueCk7XG59O1xuXG4vKipcbiAqIFJldHVybnMgdGhlIHNpZ25lZCBhcmVhIGZvciB0aGUgcG9seWdvbiByaW5nLiAgUG9zdGl2ZSBhcmVhcyBhcmUgZXh0ZXJpb3IgcmluZ3MgYW5kXG4gKiBoYXZlIGEgY2xvY2t3aXNlIHdpbmRpbmcuICBOZWdhdGl2ZSBhcmVhcyBhcmUgaW50ZXJpb3IgcmluZ3MgYW5kIGhhdmUgYSBjb3VudGVyIGNsb2Nrd2lzZVxuICogb3JkZXJpbmcuXG4gKlxuICogQHBhcmFtIHJpbmcgRXh0ZXJpb3Igb3IgaW50ZXJpb3IgcmluZ1xuICovXG5leHBvcnRzLmNhbGN1bGF0ZVNpZ25lZEFyZWEgPSBmdW5jdGlvbihyaW5nICAgICAgICAgICAgICApICAgICAgICAge1xuICAgIGxldCBzdW0gPSAwO1xuICAgIGZvciAobGV0IGkgPSAwLCBsZW4gPSByaW5nLmxlbmd0aCwgaiA9IGxlbiAtIDEsIHAxLCBwMjsgaSA8IGxlbjsgaiA9IGkrKykge1xuICAgICAgICBwMSA9IHJpbmdbaV07XG4gICAgICAgIHAyID0gcmluZ1tqXTtcbiAgICAgICAgc3VtICs9IChwMi54IC0gcDEueCkgKiAocDEueSArIHAyLnkpO1xuICAgIH1cbiAgICByZXR1cm4gc3VtO1xufTtcblxuLyoqXG4gKiBEZXRlY3RzIGNsb3NlZCBwb2x5Z29ucywgZmlyc3QgKyBsYXN0IHBvaW50IGFyZSBlcXVhbFxuICpcbiAqIEBwYXJhbSBwb2ludHMgYXJyYXkgb2YgcG9pbnRzXG4gKiBAcmV0dXJuIHRydWUgaWYgdGhlIHBvaW50cyBhcmUgYSBjbG9zZWQgcG9seWdvblxuICovXG5leHBvcnRzLmlzQ2xvc2VkUG9seWdvbiA9IGZ1bmN0aW9uKHBvaW50cyAgICAgICAgICAgICAgKSAgICAgICAgICB7XG4gICAgLy8gSWYgaXQgaXMgMiBwb2ludHMgdGhhdCBhcmUgdGhlIHNhbWUgdGhlbiBpdCBpcyBhIHBvaW50XG4gICAgLy8gSWYgaXQgaXMgMyBwb2ludHMgd2l0aCBzdGFydCBhbmQgZW5kIHRoZSBzYW1lIHRoZW4gaXQgaXMgYSBsaW5lXG4gICAgaWYgKHBvaW50cy5sZW5ndGggPCA0KVxuICAgICAgICByZXR1cm4gZmFsc2U7XG5cbiAgICBjb25zdCBwMSA9IHBvaW50c1swXTtcbiAgICBjb25zdCBwMiA9IHBvaW50c1twb2ludHMubGVuZ3RoIC0gMV07XG5cbiAgICBpZiAoTWF0aC5hYnMocDEueCAtIHAyLngpID4gMCB8fFxuICAgICAgICBNYXRoLmFicyhwMS55IC0gcDIueSkgPiAwKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICAvLyBwb2x5Z29uIHNpbXBsaWZpY2F0aW9uIGNhbiBwcm9kdWNlIHBvbHlnb25zIHdpdGggemVybyBhcmVhIGFuZCBtb3JlIHRoYW4gMyBwb2ludHNcbiAgICByZXR1cm4gKE1hdGguYWJzKGV4cG9ydHMuY2FsY3VsYXRlU2lnbmVkQXJlYShwb2ludHMpKSA+IDAuMDEpO1xufTtcblxuLyoqXG4gKiBDb252ZXJ0cyBzcGhlcmljYWwgY29vcmRpbmF0ZXMgdG8gY2FydGVzaWFuIGNvb3JkaW5hdGVzLlxuICpcbiAqIEBwYXJhbSBzcGhlcmljYWwgU3BoZXJpY2FsIGNvb3JkaW5hdGVzLCBpbiBbcmFkaWFsLCBhemltdXRoYWwsIHBvbGFyXVxuICogQHJldHVybiBjYXJ0ZXNpYW4gY29vcmRpbmF0ZXMgaW4gW3gsIHksIHpdXG4gKi9cblxuZXhwb3J0cy5zcGhlcmljYWxUb0NhcnRlc2lhbiA9IGZ1bmN0aW9uKFtyLCBhemltdXRoYWwsIHBvbGFyXSAgICAgICAgICAgICAgICAgICAgICAgICAgKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtcbiAgICAvLyBXZSBhYnN0cmFjdCBcIm5vcnRoXCIvXCJ1cFwiIChjb21wYXNzLXdpc2UpIHRvIGJlIDDCsCB3aGVuIHJlYWxseSB0aGlzIGlzIDkwwrAgKM+ALzIpOlxuICAgIC8vIGNvcnJlY3QgZm9yIHRoYXQgaGVyZVxuICAgIGF6aW11dGhhbCArPSA5MDtcblxuICAgIC8vIENvbnZlcnQgYXppbXV0aGFsIGFuZCBwb2xhciBhbmdsZXMgdG8gcmFkaWFuc1xuICAgIGF6aW11dGhhbCAqPSBNYXRoLlBJIC8gMTgwO1xuICAgIHBvbGFyICo9IE1hdGguUEkgLyAxODA7XG5cbiAgICByZXR1cm4ge1xuICAgICAgICB4OiByICogTWF0aC5jb3MoYXppbXV0aGFsKSAqIE1hdGguc2luKHBvbGFyKSxcbiAgICAgICAgeTogciAqIE1hdGguc2luKGF6aW11dGhhbCkgKiBNYXRoLnNpbihwb2xhciksXG4gICAgICAgIHo6IHIgKiBNYXRoLmNvcyhwb2xhcilcbiAgICB9O1xufTtcblxuLyoqXG4gKiBQYXJzZXMgZGF0YSBmcm9tICdDYWNoZS1Db250cm9sJyBoZWFkZXJzLlxuICpcbiAqIEBwYXJhbSBjYWNoZUNvbnRyb2wgVmFsdWUgb2YgJ0NhY2hlLUNvbnRyb2wnIGhlYWRlclxuICogQHJldHVybiBvYmplY3QgY29udGFpbmluZyBwYXJzZWQgaGVhZGVyIGluZm8uXG4gKi9cblxuZXhwb3J0cy5wYXJzZUNhY2hlQ29udHJvbCA9IGZ1bmN0aW9uKGNhY2hlQ29udHJvbCAgICAgICAgKSAgICAgICAgIHtcbiAgICAvLyBUYWtlbiBmcm9tIFtXcmVja10oaHR0cHM6Ly9naXRodWIuY29tL2hhcGlqcy93cmVjaylcbiAgICBjb25zdCByZSA9IC8oPzpefCg/OlxccypcXCxcXHMqKSkoW15cXHgwMC1cXHgyMFxcKFxcKTw+QFxcLDtcXDpcXFxcXCJcXC9cXFtcXF1cXD9cXD1cXHtcXH1cXHg3Rl0rKSg/OlxcPSg/OihbXlxceDAwLVxceDIwXFwoXFwpPD5AXFwsO1xcOlxcXFxcIlxcL1xcW1xcXVxcP1xcPVxce1xcfVxceDdGXSspfCg/OlxcXCIoKD86W15cIlxcXFxdfFxcXFwuKSopXFxcIikpKT8vZztcblxuICAgIGNvbnN0IGhlYWRlciA9IHt9O1xuICAgIGNhY2hlQ29udHJvbC5yZXBsYWNlKHJlLCAoJDAsICQxLCAkMiwgJDMpID0+IHtcbiAgICAgICAgY29uc3QgdmFsdWUgPSAkMiB8fCAkMztcbiAgICAgICAgaGVhZGVyWyQxXSA9IHZhbHVlID8gdmFsdWUudG9Mb3dlckNhc2UoKSA6IHRydWU7XG4gICAgICAgIHJldHVybiAnJztcbiAgICB9KTtcblxuICAgIGlmIChoZWFkZXJbJ21heC1hZ2UnXSkge1xuICAgICAgICBjb25zdCBtYXhBZ2UgPSBwYXJzZUludChoZWFkZXJbJ21heC1hZ2UnXSwgMTApO1xuICAgICAgICBpZiAoaXNOYU4obWF4QWdlKSkgZGVsZXRlIGhlYWRlclsnbWF4LWFnZSddO1xuICAgICAgICBlbHNlIGhlYWRlclsnbWF4LWFnZSddID0gbWF4QWdlO1xuICAgIH1cblxuICAgIHJldHVybiBoZWFkZXI7XG59O1xuIiwiLy8gICAgICBcblxuY29uc3QgdXRpbCA9IHJlcXVpcmUoJ21hcGJveC1nbC9zcmMvdXRpbC91dGlsJyk7XG5jb25zdCBhamF4ID0gcmVxdWlyZSgnbWFwYm94LWdsL3NyYy91dGlsL2FqYXgnKTtcbmNvbnN0IEV2ZW50ZWQgPSByZXF1aXJlKCdtYXBib3gtZ2wvc3JjL3V0aWwvZXZlbnRlZCcpO1xuY29uc3QgbG9hZEFyY0dJU01hcFNlcnZlciA9IHJlcXVpcmUoJy4vbG9hZF9hcmNnaXNfbWFwc2VydmVyJyk7XG5jb25zdCBUaWxlQm91bmRzID0gcmVxdWlyZSgnbWFwYm94LWdsL3NyYy9zb3VyY2UvdGlsZV9ib3VuZHMnKTtcbmNvbnN0IFRleHR1cmUgPSByZXF1aXJlKCdtYXBib3gtZ2wvc3JjL3JlbmRlci90ZXh0dXJlJyk7XG5jb25zdCBoZWxwZXJzID0gcmVxdWlyZSgnLi9oZWxwZXJzJyk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgIFxuXG4gICBcbiAgICBcbiAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgIFxuXG4gICAgICAgICAgXG4gICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICBcblxuICAgIFxuICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcblxuY2xhc3MgQXJjR0lTVGlsZWRNYXBTZXJ2aWNlU291cmNlIGV4dGVuZHMgRXZlbnRlZCAgICAgICAgICAgICAgICAgICB7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICBcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgXG5cbiAgICAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcblxuICAgIGNvbnN0cnVjdG9yKGlkICAgICAgICAsIG9wdGlvbnMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLCBkaXNwYXRjaGVyICAgICAgICAgICAgLCBldmVudGVkUGFyZW50ICAgICAgICAgKSB7XG4gICAgICAgIHN1cGVyKCk7XG4gICAgICAgIHRoaXMuaWQgPSBpZDtcbiAgICAgICAgdGhpcy5kaXNwYXRjaGVyID0gZGlzcGF0Y2hlcjtcbiAgICAgICAgdGhpcy5zZXRFdmVudGVkUGFyZW50KGV2ZW50ZWRQYXJlbnQpO1xuXG4gICAgICAgIHRoaXMudHlwZSA9ICdhcmNnaXNyYXN0ZXInO1xuICAgICAgICB0aGlzLm1pbnpvb20gPSAwO1xuICAgICAgICB0aGlzLm1heHpvb20gPSAyMjtcbiAgICAgICAgdGhpcy5yb3VuZFpvb20gPSB0cnVlO1xuICAgICAgICB0aGlzLnRpbGVTaXplID0gNTEyO1xuICAgICAgICB0aGlzLl9sb2FkZWQgPSBmYWxzZTtcblxuICAgICAgICB0aGlzLl9vcHRpb25zID0gdXRpbC5leHRlbmQoe30sIG9wdGlvbnMpO1xuICAgICAgICB1dGlsLmV4dGVuZCh0aGlzLCB1dGlsLnBpY2sob3B0aW9ucywgWyd1cmwnLCAnc2NoZW1lJywgJ3RpbGVTaXplJ10pKTtcbiAgICB9XG5cbiAgICBsb2FkKCkge1xuICAgICAgICB0aGlzLmZpcmUoJ2RhdGFsb2FkaW5nJywge2RhdGFUeXBlOiAnc291cmNlJ30pO1xuICAgICAgICB0aGlzLmZpcmUoJ2RhdGEnLCB7ZGF0YVR5cGU6ICdzb3VyY2UnLCBzb3VyY2VEYXRhVHlwZTogJ21ldGFkYXRhJ30pO1xuICAgICAgICB0aGlzLmZpcmUoJ2RhdGEnLCB7ZGF0YVR5cGU6ICdzb3VyY2UnLCBzb3VyY2VEYXRhVHlwZTogJ2NvbnRlbnQnfSk7XG4gICAgICAgIC8vIGxvYWRBcmNHSVNNYXBTZXJ2ZXIodGhpcy5fb3B0aW9ucywgKGVyciwgbWV0YWRhdGEpID0+IHtcbiAgICAgICAgLy8gICAgIGlmIChlcnIpIHtcbiAgICAgICAgLy8gICAgICAgICB0aGlzLmZpcmUoJ2Vycm9yJywgZXJyKTtcbiAgICAgICAgLy8gICAgIH0gZWxzZSBpZiAobWV0YWRhdGEpIHtcbiAgICAgICAgLy8gICAgICAgICB1dGlsLmV4dGVuZCh0aGlzLCBtZXRhZGF0YSk7XG4gICAgICAgIC8vXG4gICAgICAgIC8vICAgICAgICAgaWYgKG1ldGFkYXRhLmJvdW5kcykge1xuICAgICAgICAvLyAgICAgICAgICAgICB0aGlzLnRpbGVCb3VuZHMgPSBuZXcgVGlsZUJvdW5kcyhtZXRhZGF0YS5ib3VuZHMsIHRoaXMubWluem9vbSwgdGhpcy5tYXh6b29tKTtcbiAgICAgICAgLy8gICAgICAgICB9XG4gICAgICAgIC8vXG4gICAgICAgIC8vICAgICAvLyBgY29udGVudGAgaXMgaW5jbHVkZWQgaGVyZSB0byBwcmV2ZW50IGEgcmFjZSBjb25kaXRpb24gd2hlcmUgYFN0eWxlI191cGRhdGVTb3VyY2VzYCBpcyBjYWxsZWRcbiAgICAgICAgLy8gICAgIC8vIGJlZm9yZSB0aGUgVGlsZUpTT04gYXJyaXZlcy4gdGhpcyBtYWtlcyBzdXJlIHRoZSB0aWxlcyBuZWVkZWQgYXJlIGxvYWRlZCBvbmNlIFRpbGVKU09OIGFycml2ZXNcbiAgICAgICAgLy8gICAgIC8vIHJlZjogaHR0cHM6Ly9naXRodWIuY29tL21hcGJveC9tYXBib3gtZ2wtanMvcHVsbC80MzQ3I2Rpc2N1c3Npb25fcjEwNDQxODA4OFxuICAgICAgICAvLyAgICAgdGhpcy5maXJlKCdkYXRhJywge2RhdGFUeXBlOiAnc291cmNlJywgc291cmNlRGF0YVR5cGU6ICdtZXRhZGF0YSd9KTtcbiAgICAgICAgLy8gICAgIHRoaXMuZmlyZSgnZGF0YScsIHtkYXRhVHlwZTogJ3NvdXJjZScsIHNvdXJjZURhdGFUeXBlOiAnY29udGVudCd9KTtcbiAgICAgICAgLy8gICAgIH1cbiAgICAgICAgLy8gfSk7XG4gICAgfVxuXG4gICAgdGVtcGxhdGUoc3RyLCBkYXRhKSB7XG4gICAgICAgIHJldHVybiBzdHIucmVwbGFjZSgvXFx7ICooW1xcd19dKykgKlxcfS9nLCBmdW5jdGlvbiAoc3RyLCBrZXkpIHtcbiAgICAgICAgICAgIHZhciB2YWx1ZSA9IGRhdGFba2V5XTtcbiAgICAgICAgICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdObyB2YWx1ZSBwcm92aWRlZCBmb3IgdmFyaWFibGUgJyArIHN0cik7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgICAgIHZhbHVlID0gdmFsdWUoZGF0YSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdmFsdWU7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIG9uQWRkKG1hcCAgICAgKSB7XG4gICAgICAgIC8vIC8vIHNldCB0aGUgdXJsc1xuICAgICAgICAvLyBjb25zdCBiYXNlVXJsID0gdGhpcy51cmwuc3BsaXQoJz8nKVswXTtcbiAgICAgICAgLy8gdGhpcy50aWxlVXJsID0gYCR7YmFzZVVybH0vdGlsZS97en0ve3l9L3t4fWA7XG4gICAgICAgIC8vXG4gICAgICAgIC8vIGNvbnN0IGFyY2dpc29ubGluZSA9IG5ldyBSZWdFeHAoL3RpbGVzLmFyY2dpcyhvbmxpbmUpP1xcLmNvbS9nKTtcbiAgICAgICAgLy8gaWYgKGFyY2dpc29ubGluZS50ZXN0KHRoaXMudXJsKSkge1xuICAgICAgICAvLyAgICAgdGhpcy50aWxlVXJsID0gdGhpcy50aWxlVXJsLnJlcGxhY2UoJzovL3RpbGVzJywgJzovL3RpbGVze3N9Jyk7XG4gICAgICAgIC8vICAgICB0aGlzLnN1YmRvbWFpbnMgPSBbJzEnLCAnMicsICczJywgJzQnXTtcbiAgICAgICAgLy8gfVxuICAgICAgICAvL1xuICAgICAgICAvLyBpZiAodGhpcy50b2tlbikge1xuICAgICAgICAvLyAgICAgdGhpcy50aWxlVXJsICs9IChgP3Rva2VuPSR7dGhpcy50b2tlbn1gKTtcbiAgICAgICAgLy8gfVxuXG4gICAgICAgIHRoaXMubWFwID0gbWFwO1xuICAgICAgICB0aGlzLmxvYWQoKTtcbiAgICB9XG5cbiAgICBzZXJpYWxpemUoKSB7XG4gICAgICAgIHJldHVybiB1dGlsLmV4dGVuZCh7fSwgdGhpcy5fb3B0aW9ucyk7XG4gICAgfVxuXG4gICAgaGFzVGlsZSh0aWxlSUQgICAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgcmV0dXJuICF0aGlzLnRpbGVCb3VuZHMgfHwgdGhpcy50aWxlQm91bmRzLmNvbnRhaW5zKHRpbGVJRC5jYW5vbmljYWwpO1xuICAgIH1cblxuICAgIGxvYWRUaWxlKHRpbGUgICAgICAsIGNhbGxiYWNrICAgICAgICAgICAgICAgICkge1xuICAgICAgICAvL2NvbnZlcnQgdG8gYWdzIGNvb3Jkc1xuICAgICAgICBjb25zdCB0aWxlUG9pbnQgPSB7ejogdGlsZS50aWxlSUQub3ZlcnNjYWxlZFosIHg6IHRpbGUudGlsZUlELmNhbm9uaWNhbC54LCB5OiB0aWxlLnRpbGVJRC5jYW5vbmljYWwueX07XG5cbiAgICAgICAgY29uc3QgdXJsID0gdGhpcy50ZW1wbGF0ZSh0aGlzLnVybCwge3g6IHRpbGVQb2ludC54LCB5OiB0aWxlUG9pbnQueSwgejogdGlsZVBvaW50Lnp9KTtcbiAgICAgICAgdGlsZS5yZXF1ZXN0ID0gYWpheC5nZXRJbWFnZSh7dXJsfSwgKGVyciwgaW1nKSA9PiB7XG4gICAgICAgICAgICBkZWxldGUgdGlsZS5yZXF1ZXN0O1xuXG4gICAgICAgICAgICBpZiAodGlsZS5hYm9ydGVkKSB7XG4gICAgICAgICAgICAgICAgdGlsZS5zdGF0ZSA9ICd1bmxvYWRlZCc7XG4gICAgICAgICAgICAgICAgY2FsbGJhY2sobnVsbCk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGVycikge1xuICAgICAgICAgICAgICAgIHRpbGUuc3RhdGUgPSAnZXJyb3JlZCc7XG4gICAgICAgICAgICAgICAgY2FsbGJhY2soZXJyKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoaW1nKSB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMubWFwLl9yZWZyZXNoRXhwaXJlZFRpbGVzKSB0aWxlLnNldEV4cGlyeURhdGEoaW1nKTtcbiAgICAgICAgICAgICAgICBkZWxldGUgKGltZ1xuICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgKS5cbiAgICAgICAgICAgICAgICBjYWNoZUNvbnRyb2w7XG4gICAgICAgICAgICAgICAgZGVsZXRlIChpbWdcbiAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICkuXG4gICAgICAgICAgICAgICAgZXhwaXJlcztcblxuICAgICAgICAgICAgICAgIGNvbnN0IGNvbnRleHQgPSB0aGlzLm1hcC5wYWludGVyLmNvbnRleHQ7XG4gICAgICAgICAgICAgICAgY29uc3QgZ2wgPSBjb250ZXh0LmdsO1xuICAgICAgICAgICAgICAgIHRpbGUudGV4dHVyZSA9IHRoaXMubWFwLnBhaW50ZXIuZ2V0VGlsZVRleHR1cmUoaW1nLndpZHRoKTtcbiAgICAgICAgICAgICAgICBpZiAodGlsZS50ZXh0dXJlKSB7XG4gICAgICAgICAgICAgICAgICAgIHRpbGUudGV4dHVyZS5iaW5kKGdsLkxJTkVBUiwgZ2wuQ0xBTVBfVE9fRURHRSwgZ2wuTElORUFSX01JUE1BUF9ORUFSRVNUKTtcbiAgICAgICAgICAgICAgICAgICAgZ2wudGV4U3ViSW1hZ2UyRChnbC5URVhUVVJFXzJELCAwLCAwLCAwLCBnbC5SR0JBLCBnbC5VTlNJR05FRF9CWVRFLCBpbWcpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHRpbGUudGV4dHVyZSA9IG5ldyBUZXh0dXJlKGNvbnRleHQsIGltZywgZ2wuUkdCQSk7XG4gICAgICAgICAgICAgICAgICAgIHRpbGUudGV4dHVyZS5iaW5kKGdsLkxJTkVBUiwgZ2wuQ0xBTVBfVE9fRURHRSwgZ2wuTElORUFSX01JUE1BUF9ORUFSRVNUKTtcblxuICAgICAgICAgICAgICAgICAgICBpZiAoY29udGV4dC5leHRUZXh0dXJlRmlsdGVyQW5pc290cm9waWMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGdsLnRleFBhcmFtZXRlcmYoZ2wuVEVYVFVSRV8yRCwgY29udGV4dC5leHRUZXh0dXJlRmlsdGVyQW5pc290cm9waWMuVEVYVFVSRV9NQVhfQU5JU09UUk9QWV9FWFQsIGNvbnRleHQuZXh0VGV4dHVyZUZpbHRlckFuaXNvdHJvcGljTWF4KTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBnbC5nZW5lcmF0ZU1pcG1hcChnbC5URVhUVVJFXzJEKTtcblxuICAgICAgICAgICAgICAgIHRpbGUuc3RhdGUgPSAnbG9hZGVkJztcblxuICAgICAgICAgICAgICAgIGNhbGxiYWNrKG51bGwpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBhYm9ydFRpbGUodGlsZSAgICAgICwgY2FsbGJhY2sgICAgICAgICAgICAgICAgKSB7XG4gICAgICAgIGlmICh0aWxlLnJlcXVlc3QpIHtcbiAgICAgICAgICAgIHRpbGUucmVxdWVzdC5hYm9ydCgpO1xuICAgICAgICAgICAgZGVsZXRlIHRpbGUucmVxdWVzdDtcbiAgICAgICAgfVxuICAgICAgICBjYWxsYmFjaygpO1xuICAgIH1cblxuICAgIHVubG9hZFRpbGUodGlsZSAgICAgICwgY2FsbGJhY2sgICAgICAgICAgICAgICAgKSB7XG4gICAgICAgIGlmICh0aWxlLnRleHR1cmUpIHRoaXMubWFwLnBhaW50ZXIuc2F2ZVRpbGVUZXh0dXJlKHRpbGUudGV4dHVyZSk7XG4gICAgICAgIGNhbGxiYWNrKCk7XG4gICAgfVxuXG4gICAgaGFzVHJhbnNpdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBBcmNHSVNUaWxlZE1hcFNlcnZpY2VTb3VyY2U7IiwiXG4vL0Zyb20gaHR0cHM6Ly9naXRodWIuY29tL0xlYWZsZXQvTGVhZmxldC9ibG9iL21hc3Rlci9zcmMvY29yZS9VdGlsLmpzXG5jb25zdCBfdGVtcGxhdGVSZSA9IC9cXHsgKihbXFx3X10rKSAqXFx9L2c7XG5jb25zdCBfdGVtcGxhdGUgPSBmdW5jdGlvbiAoc3RyLCBkYXRhKSB7XG4gICAgcmV0dXJuIHN0ci5yZXBsYWNlKF90ZW1wbGF0ZVJlLCAoc3RyLCBrZXkpID0+IHtcbiAgICAgICAgbGV0IHZhbHVlID0gZGF0YVtrZXldO1xuXG4gICAgICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYE5vIHZhbHVlIHByb3ZpZGVkIGZvciB2YXJpYWJsZSAke3N0cn1gKTtcblxuICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgdmFsdWUgPSB2YWx1ZShkYXRhKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdmFsdWU7XG4gICAgfSk7XG59O1xuXG4vL0Zyb20gaHR0cHM6Ly9naXRodWIuY29tL0xlYWZsZXQvTGVhZmxldC9ibG9iL21hc3Rlci9zcmMvbGF5ZXIvdGlsZS9UaWxlTGF5ZXIuanNcbmNvbnN0IF9nZXRTdWJkb21haW4gPSBmdW5jdGlvbiAodGlsZVBvaW50LCBzdWJkb21haW5zKSB7XG4gICAgaWYgKHN1YmRvbWFpbnMpIHtcbiAgICAgICAgY29uc3QgaW5kZXggPSBNYXRoLmFicyh0aWxlUG9pbnQueCArIHRpbGVQb2ludC55KSAlIHN1YmRvbWFpbnMubGVuZ3RoO1xuICAgICAgICByZXR1cm4gc3ViZG9tYWluc1tpbmRleF07XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gICAgX3RlbXBsYXRlLFxuICAgIF9nZXRTdWJkb21haW5cbn07XG4iLCJjb25zdCBBcmNHSVNUaWxlZE1hcFNlcnZpY2VTb3VyY2UgPSByZXF1aXJlKCcuL2FyY2dpc190aWxlZF9tYXBfc2VydmljZV9zb3VyY2UnKTtcbm1vZHVsZS5leHBvcnRzID0gQXJjR0lTVGlsZWRNYXBTZXJ2aWNlU291cmNlOyIsIid1c2Ugc3RyaWN0JztcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAob3B0aW9ucywgY2FsbGJhY2spIHtcblxuICAgIGNhbGxiYWNrKG51bGwsIHt9KTtcblxufTtcbiJdfQ==