UNPKG

kepler.gl

Version:

kepler.gl is a webgl based application to visualize large scale location data in the browser

470 lines (455 loc) 60.6 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.COLORMAP_TEXTURE_PARAMETERS = void 0; exports.generateCategoricalColormapTexture = generateCategoricalColormapTexture; exports.getCombineBandsModule = getCombineBandsModule; exports.getImageMaskModule = getImageMaskModule; exports.getModules = getModules; exports.loadImage = loadImage; exports.loadNpyArray = loadNpyArray; var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _core = require("@loaders.gl/core"); var _images = require("@loaders.gl/images"); var _textures = require("@loaders.gl/textures"); var _constants = _interopRequireDefault(require("@luma.gl/constants")); var _constants2 = require("@kepler.gl/constants"); var _deckglLayers = require("@kepler.gl/deckgl-layers"); var _rasterTileUtils = require("./raster-tile-utils"); function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2["default"])(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } // SPDX-License-Identifier: MIT // Copyright contributors to the kepler.gl project /** * Functions and constants for handling webgl/luma.gl/deck.gl entities */ var combineBandsFloat = _deckglLayers.RasterWebGL.combineBandsFloat, combineBandsInt = _deckglLayers.RasterWebGL.combineBandsInt, combineBandsUint = _deckglLayers.RasterWebGL.combineBandsUint, maskFloat = _deckglLayers.RasterWebGL.maskFloat, maskInt = _deckglLayers.RasterWebGL.maskInt, maskUint = _deckglLayers.RasterWebGL.maskUint, linearRescale = _deckglLayers.RasterWebGL.linearRescale, gammaContrast = _deckglLayers.RasterWebGL.gammaContrast, sigmoidalContrast = _deckglLayers.RasterWebGL.sigmoidalContrast, normalizedDifference = _deckglLayers.RasterWebGL.normalizedDifference, enhancedVegetationIndex = _deckglLayers.RasterWebGL.enhancedVegetationIndex, soilAdjustedVegetationIndex = _deckglLayers.RasterWebGL.soilAdjustedVegetationIndex, modifiedSoilAdjustedVegetationIndex = _deckglLayers.RasterWebGL.modifiedSoilAdjustedVegetationIndex, colormapModule = _deckglLayers.RasterWebGL.colormap, filter = _deckglLayers.RasterWebGL.filter, saturation = _deckglLayers.RasterWebGL.saturation, reorderBands = _deckglLayers.RasterWebGL.reorderBands, rgbaImage = _deckglLayers.RasterWebGL.rgbaImage; /** * Describe WebGL2 Texture parameters to use for given input data type */ /** * Convert TypedArray to WebGL2 Texture Parameters */ function getWebGL2TextureParameters(data) { if (data instanceof Uint8Array || data instanceof Uint8ClampedArray) { return { // Note: texture data has no auto-rescaling; pixel values stay as 0-255 format: _constants["default"].R8UI, dataFormat: _constants["default"].RED_INTEGER, type: _constants["default"].UNSIGNED_BYTE }; } if (data instanceof Uint16Array) { return { format: _constants["default"].R16UI, dataFormat: _constants["default"].RED_INTEGER, type: _constants["default"].UNSIGNED_SHORT }; } if (data instanceof Uint32Array) { return { format: _constants["default"].R32UI, dataFormat: _constants["default"].RED_INTEGER, type: _constants["default"].UNSIGNED_INT }; } if (data instanceof Int8Array) { return { format: _constants["default"].R8I, dataFormat: _constants["default"].RED_INTEGER, type: _constants["default"].BYTE }; } if (data instanceof Int16Array) { return { format: _constants["default"].R16I, dataFormat: _constants["default"].RED_INTEGER, type: _constants["default"].SHORT }; } if (data instanceof Int32Array) { return { format: _constants["default"].R32I, dataFormat: _constants["default"].RED_INTEGER, type: _constants["default"].INT }; } if (data instanceof Float32Array) { return { format: _constants["default"].R32F, dataFormat: _constants["default"].RED, type: _constants["default"].FLOAT }; } if (data instanceof Float64Array) { return { format: _constants["default"].R32F, dataFormat: _constants["default"].RED, type: _constants["default"].FLOAT }; } // For exhaustive check above; following should never occur // https://stackoverflow.com/a/58009992 var unexpectedInput = data; throw new Error(unexpectedInput); } /** * Discrete-valued colormaps (e.g. from the output of * classification algorithms) in the raster layer. Previously, the values passed to * `TEXTURE_MIN_FILTER` and `TEXTURE_MAG_FILTER` were `GL.LINEAR`, which meant that the GPU would * linearly interpolate values between two neighboring colormap pixel values. Setting these values * to NEAREST means that the GPU will choose the nearest value on the texture2D lookup operation, * which fixes precision issues for discrete-valued colormaps. This should be ok for continuous * colormaps as long as the color difference between each pixel on the colormap is small. */ var COLORMAP_TEXTURE_PARAMETERS = exports.COLORMAP_TEXTURE_PARAMETERS = (0, _defineProperty2["default"])((0, _defineProperty2["default"])((0, _defineProperty2["default"])((0, _defineProperty2["default"])({}, _constants["default"].TEXTURE_MIN_FILTER, _constants["default"].NEAREST), _constants["default"].TEXTURE_MAG_FILTER, _constants["default"].NEAREST), _constants["default"].TEXTURE_WRAP_S, _constants["default"].CLAMP_TO_EDGE), _constants["default"].TEXTURE_WRAP_T, _constants["default"].CLAMP_TO_EDGE); var DEFAULT_8BIT_TEXTURE_PARAMETERS = (0, _defineProperty2["default"])((0, _defineProperty2["default"])((0, _defineProperty2["default"])((0, _defineProperty2["default"])({}, _constants["default"].TEXTURE_MIN_FILTER, _constants["default"].LINEAR_MIPMAP_LINEAR), _constants["default"].TEXTURE_MAG_FILTER, _constants["default"].LINEAR), _constants["default"].TEXTURE_WRAP_S, _constants["default"].CLAMP_TO_EDGE), _constants["default"].TEXTURE_WRAP_T, _constants["default"].CLAMP_TO_EDGE); var DEFAULT_HIGH_BIT_TEXTURE_PARAMETERS = (0, _defineProperty2["default"])((0, _defineProperty2["default"])((0, _defineProperty2["default"])((0, _defineProperty2["default"])({}, _constants["default"].TEXTURE_MIN_FILTER, _constants["default"].NEAREST), _constants["default"].TEXTURE_MAG_FILTER, _constants["default"].NEAREST), _constants["default"].TEXTURE_WRAP_S, _constants["default"].CLAMP_TO_EDGE), _constants["default"].TEXTURE_WRAP_T, _constants["default"].CLAMP_TO_EDGE); /** * Select correct module type for "combineBands" * * combineBands joins up to four 2D arrays (contained in imageBands) into a single "rgba" image * texture on the GPU. That shader code needs to have the same data type as the actual image data. * E.g. for float data the texture needs to be `sampler2D`, for uint data the texture needs to be * `usampler2D` and for int data the texture needs to be `isampler2D`. */ function getCombineBandsModule(imageBands) { // Each image array is expected/required to be of the same data type switch (imageBands[0].format) { case _constants["default"].R8UI: return combineBandsUint; case _constants["default"].R16UI: return combineBandsUint; case _constants["default"].R32UI: return combineBandsUint; case _constants["default"].R8I: return combineBandsInt; case _constants["default"].R16I: return combineBandsInt; case _constants["default"].R32I: return combineBandsInt; case _constants["default"].R32F: return combineBandsFloat; default: throw new Error('bad format'); } } /** Select correct image masking shader module for mask data type * The imageMask could (at least in the future, theoretically) be of a different data format than * the imageBands data itself. */ function getImageMaskModule(imageMask) { switch (imageMask.format) { case _constants["default"].R8UI: return maskUint; case _constants["default"].R16UI: return maskUint; case _constants["default"].R32UI: return maskUint; case _constants["default"].R8I: return maskInt; case _constants["default"].R16I: return maskInt; case _constants["default"].R32I: return maskInt; case _constants["default"].R32F: return maskFloat; default: throw new Error('bad format'); } } /** * Load image and wrap with default WebGL texture parameters * * @param url URL to load image * @param textureParams parameters to pass to Texture2D * * @return image object to pass to Texture2D constructor */ function loadImage(_x) { return _loadImage.apply(this, arguments); } function _loadImage() { _loadImage = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee(url) { var textureParams, requestOptions, response, image, _args = arguments; return _regenerator["default"].wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: textureParams = _args.length > 1 && _args[1] !== undefined ? _args[1] : {}; requestOptions = _args.length > 2 && _args[2] !== undefined ? _args[2] : {}; _context.next = 4; return (0, _core.fetchFile)(url, requestOptions); case 4: response = _context.sent; _context.next = 7; return (0, _core.parse)(response, _images.ImageLoader); case 7: image = _context.sent; return _context.abrupt("return", _objectSpread({ data: image, parameters: DEFAULT_8BIT_TEXTURE_PARAMETERS, format: _constants["default"].RGB }, textureParams)); case 9: case "end": return _context.stop(); } }, _callee); })); return _loadImage.apply(this, arguments); } /** * Load NPY Array * * The NPY format is described here: https://numpy.org/doc/stable/reference/generated/numpy.lib.format.html. * It's designed to be a very simple file format to hold an N-dimensional block of data. The header describes the data type, shape, and order (either C or Fortran) of the array. * * @param url URL to load NPY Array * @param split Whether to split single typed array representing an N-dimensional array into an Array with each dimension as its own typed array * * @return image object to pass to Texture2D constructor */ function loadNpyArray(_x2, _x3, _x4) { return _loadNpyArray.apply(this, arguments); } /** * Create texture data for categorical colormap scale * @param categoricalOptions - color map configuration and min-max values of categorical band * @returns texture data */ function _loadNpyArray() { _loadNpyArray = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2(request, split, options) { var _request$options$sign, _getLoaderOptions, npyOptions, response, data, header, shape, _getWebGL2TexturePara, format, dataFormat, type, _shape, z, height, width, mipmaps, parameters, channels, channelSize, i; return _regenerator["default"].wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: _context2.prev = 0; _getLoaderOptions = (0, _constants2.getLoaderOptions)(), npyOptions = _getLoaderOptions.npy; _context2.next = 4; return (0, _core.load)(request.url, _textures.NPYLoader, { npy: npyOptions, fetch: options === null || options === void 0 ? void 0 : options.fetch }); case 4: response = _context2.sent; if (!(!response || !response.data || (_request$options$sign = request.options.signal) !== null && _request$options$sign !== void 0 && _request$options$sign.aborted)) { _context2.next = 7; break; } return _context2.abrupt("return", null); case 7: // Float64 data needs to be coerced to Float32 for the GPU if (response.data instanceof Float64Array) { response.data = Float32Array.from(response.data); } data = response.data, header = response.header; shape = header.shape; _getWebGL2TexturePara = getWebGL2TextureParameters(data), format = _getWebGL2TexturePara.format, dataFormat = _getWebGL2TexturePara.dataFormat, type = _getWebGL2TexturePara.type; // TODO: check height-width or width-height // Regardless, images usually square // TODO: handle cases of 256x256x1 instead of 1x256x256 _shape = (0, _slicedToArray2["default"])(shape, 3), z = _shape[0], height = _shape[1], width = _shape[2]; // Since we now use WebGL2 data types for 8-bit textures, we set the following for all textures mipmaps = false; parameters = DEFAULT_HIGH_BIT_TEXTURE_PARAMETERS; if (split) { _context2.next = 16; break; } return _context2.abrupt("return", { data: data, width: width, height: height, format: format, dataFormat: dataFormat, type: type, parameters: parameters, mipmaps: mipmaps }); case 16: // Split into individual arrays channels = []; channelSize = height * width; for (i = 0; i < z; i++) { channels.push({ data: data.subarray(i * channelSize, (i + 1) * channelSize), width: width, height: height, format: format, dataFormat: dataFormat, type: type, parameters: parameters, mipmaps: mipmaps }); } return _context2.abrupt("return", channels); case 22: _context2.prev = 22; _context2.t0 = _context2["catch"](0); return _context2.abrupt("return", null); case 25: case "end": return _context2.stop(); } }, _callee2, null, [[0, 22]]); })); return _loadNpyArray.apply(this, arguments); } function generateCategoricalColormapTexture(categoricalOptions) { var data = (0, _rasterTileUtils.generateCategoricalBitmapArray)(categoricalOptions); return { data: data, width: _rasterTileUtils.CATEGORICAL_TEXTURE_WIDTH, height: 1, format: _constants["default"].RGBA, dataFormat: _constants["default"].RGBA, type: _constants["default"].UNSIGNED_BYTE, parameters: COLORMAP_TEXTURE_PARAMETERS, mipmaps: false }; } // TODO: would probably be simpler to only pass in the props actually used by this function. That // would mean a smaller object than RenderSubLayersProps // eslint-disable-next-line max-statements, complexity function getModules(_ref) { var images = _ref.images, props = _ref.props; var moduleProps = {}; // Array of luma.gl WebGL modules to pass to the RasterLayer var modules = []; // use rgba image directly. Used for raster .pmtiles rendering if (images.imageRgba) { modules.push(rgbaImage); // no support for other modules atm for direct rgba mode return { modules: modules, moduleProps: moduleProps }; } if (!props) { return { modules: modules, moduleProps: moduleProps }; } var renderBandIndexes = props.renderBandIndexes, nonLinearRescaling = props.nonLinearRescaling, linearRescalingFactor = props.linearRescalingFactor, minPixelValue = props.minPixelValue, maxPixelValue = props.maxPixelValue, gammaContrastFactor = props.gammaContrastFactor, sigmoidalContrastFactor = props.sigmoidalContrastFactor, sigmoidalBiasFactor = props.sigmoidalBiasFactor, saturationValue = props.saturationValue, bandCombination = props.bandCombination, filterEnabled = props.filterEnabled, filterRange = props.filterRange, dataType = props.dataType, minCategoricalBandValue = props.minCategoricalBandValue, maxCategoricalBandValue = props.maxCategoricalBandValue, hasCategoricalColorMap = props.hasCategoricalColorMap; if (Array.isArray(images.imageBands) && images.imageBands.length > 0) { modules.push(getCombineBandsModule(images.imageBands)); } if (images.imageMask) { modules.push(getImageMaskModule(images.imageMask)); // In general, data masks are 0 for nodata and the maximum value for valid data, e.g. 255 or // 65535 for uint8 or uint16 data, respectively moduleProps.maskKeepMin = 1; } if (Array.isArray(renderBandIndexes)) { modules.push(reorderBands); moduleProps.ordering = renderBandIndexes; } var globalRange = maxPixelValue - minPixelValue; // Fix rescaling if we are sure that dataset is categorical if (hasCategoricalColorMap) { modules.push(linearRescale); moduleProps.linearRescaleScaler = 1 / maxPixelValue; moduleProps.linearRescaleOffset = 0; } else if ((0, _rasterTileUtils.isRescalingAllowed)(bandCombination)) { if (!nonLinearRescaling) { var _linearRescalingFacto = (0, _slicedToArray2["default"])(linearRescalingFactor, 2), min = _linearRescalingFacto[0], max = _linearRescalingFacto[1]; var localRange = max - min; // Add linear rescaling module modules.push(linearRescale); // Divide by local range * global range moduleProps.linearRescaleScaler = 1 / (localRange * globalRange); // Subtract off the local min moduleProps.linearRescaleOffset = -min; // Clamp to [0, 1] done automatically? } else { modules.push(linearRescale); moduleProps.linearRescaleScaler = 1 / maxPixelValue; moduleProps.linearRescaleOffset = 0; modules.push(gammaContrast); moduleProps.gammaContrastValue = gammaContrastFactor; modules.push(sigmoidalContrast); moduleProps.sigmoidalContrast = sigmoidalContrastFactor; moduleProps.sigmoidalBias = sigmoidalBiasFactor; } if (Number.isFinite(saturationValue) && saturationValue !== 1) { modules.push(saturation); moduleProps.saturationValue = saturationValue; } } switch (bandCombination) { case 'normalizedDifference': modules.push(normalizedDifference); break; case 'enhancedVegetationIndex': modules.push(enhancedVegetationIndex); break; case 'soilAdjustedVegetationIndex': modules.push(soilAdjustedVegetationIndex); break; case 'modifiedSoilAdjustedVegetationIndex': modules.push(modifiedSoilAdjustedVegetationIndex); break; default: break; } if ((0, _rasterTileUtils.isFilterAllowed)(bandCombination) && filterEnabled) { modules.push(filter); moduleProps.filterMin1 = filterRange[0]; moduleProps.filterMax1 = filterRange[1]; } // Apply colormap if ((0, _rasterTileUtils.isColormapAllowed)(bandCombination) && images.imageColormap) { modules.push(colormapModule); moduleProps.minCategoricalBandValue = minCategoricalBandValue; moduleProps.maxCategoricalBandValue = maxCategoricalBandValue; moduleProps.dataTypeMaxValue = _rasterTileUtils.dtypeMaxValue[dataType]; moduleProps.maxPixelValue = maxPixelValue; } return { modules: modules, moduleProps: moduleProps }; } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfY29yZSIsInJlcXVpcmUiLCJfaW1hZ2VzIiwiX3RleHR1cmVzIiwiX2NvbnN0YW50cyIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJfY29uc3RhbnRzMiIsIl9kZWNrZ2xMYXllcnMiLCJfcmFzdGVyVGlsZVV0aWxzIiwib3duS2V5cyIsImUiLCJyIiwidCIsIk9iamVjdCIsImtleXMiLCJnZXRPd25Qcm9wZXJ0eVN5bWJvbHMiLCJvIiwiZmlsdGVyIiwiZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yIiwiZW51bWVyYWJsZSIsInB1c2giLCJhcHBseSIsIl9vYmplY3RTcHJlYWQiLCJhcmd1bWVudHMiLCJsZW5ndGgiLCJmb3JFYWNoIiwiX2RlZmluZVByb3BlcnR5MiIsImdldE93blByb3BlcnR5RGVzY3JpcHRvcnMiLCJkZWZpbmVQcm9wZXJ0aWVzIiwiZGVmaW5lUHJvcGVydHkiLCJjb21iaW5lQmFuZHNGbG9hdCIsIlJhc3RlcldlYkdMIiwiY29tYmluZUJhbmRzSW50IiwiY29tYmluZUJhbmRzVWludCIsIm1hc2tGbG9hdCIsIm1hc2tJbnQiLCJtYXNrVWludCIsImxpbmVhclJlc2NhbGUiLCJnYW1tYUNvbnRyYXN0Iiwic2lnbW9pZGFsQ29udHJhc3QiLCJub3JtYWxpemVkRGlmZmVyZW5jZSIsImVuaGFuY2VkVmVnZXRhdGlvbkluZGV4Iiwic29pbEFkanVzdGVkVmVnZXRhdGlvbkluZGV4IiwibW9kaWZpZWRTb2lsQWRqdXN0ZWRWZWdldGF0aW9uSW5kZXgiLCJjb2xvcm1hcE1vZHVsZSIsImNvbG9ybWFwIiwic2F0dXJhdGlvbiIsInJlb3JkZXJCYW5kcyIsInJnYmFJbWFnZSIsImdldFdlYkdMMlRleHR1cmVQYXJhbWV0ZXJzIiwiZGF0YSIsIlVpbnQ4QXJyYXkiLCJVaW50OENsYW1wZWRBcnJheSIsImZvcm1hdCIsIkdMIiwiUjhVSSIsImRhdGFGb3JtYXQiLCJSRURfSU5URUdFUiIsInR5cGUiLCJVTlNJR05FRF9CWVRFIiwiVWludDE2QXJyYXkiLCJSMTZVSSIsIlVOU0lHTkVEX1NIT1JUIiwiVWludDMyQXJyYXkiLCJSMzJVSSIsIlVOU0lHTkVEX0lOVCIsIkludDhBcnJheSIsIlI4SSIsIkJZVEUiLCJJbnQxNkFycmF5IiwiUjE2SSIsIlNIT1JUIiwiSW50MzJBcnJheSIsIlIzMkkiLCJJTlQiLCJGbG9hdDMyQXJyYXkiLCJSMzJGIiwiUkVEIiwiRkxPQVQiLCJGbG9hdDY0QXJyYXkiLCJ1bmV4cGVjdGVkSW5wdXQiLCJFcnJvciIsIkNPTE9STUFQX1RFWFRVUkVfUEFSQU1FVEVSUyIsImV4cG9ydHMiLCJURVhUVVJFX01JTl9GSUxURVIiLCJORUFSRVNUIiwiVEVYVFVSRV9NQUdfRklMVEVSIiwiVEVYVFVSRV9XUkFQX1MiLCJDTEFNUF9UT19FREdFIiwiVEVYVFVSRV9XUkFQX1QiLCJERUZBVUxUXzhCSVRfVEVYVFVSRV9QQVJBTUVURVJTIiwiTElORUFSX01JUE1BUF9MSU5FQVIiLCJMSU5FQVIiLCJERUZBVUxUX0hJR0hfQklUX1RFWFRVUkVfUEFSQU1FVEVSUyIsImdldENvbWJpbmVCYW5kc01vZHVsZSIsImltYWdlQmFuZHMiLCJnZXRJbWFnZU1hc2tNb2R1bGUiLCJpbWFnZU1hc2siLCJsb2FkSW1hZ2UiLCJfeCIsIl9sb2FkSW1hZ2UiLCJfYXN5bmNUb0dlbmVyYXRvcjIiLCJfcmVnZW5lcmF0b3IiLCJtYXJrIiwiX2NhbGxlZSIsInVybCIsInRleHR1cmVQYXJhbXMiLCJyZXF1ZXN0T3B0aW9ucyIsInJlc3BvbnNlIiwiaW1hZ2UiLCJfYXJncyIsIndyYXAiLCJfY2FsbGVlJCIsIl9jb250ZXh0IiwicHJldiIsIm5leHQiLCJ1bmRlZmluZWQiLCJmZXRjaEZpbGUiLCJzZW50IiwicGFyc2UiLCJJbWFnZUxvYWRlciIsImFicnVwdCIsInBhcmFtZXRlcnMiLCJSR0IiLCJzdG9wIiwibG9hZE5weUFycmF5IiwiX3gyIiwiX3gzIiwiX3g0IiwiX2xvYWROcHlBcnJheSIsIl9jYWxsZWUyIiwicmVxdWVzdCIsInNwbGl0Iiwib3B0aW9ucyIsIl9yZXF1ZXN0JG9wdGlvbnMkc2lnbiIsIl9nZXRMb2FkZXJPcHRpb25zIiwibnB5T3B0aW9ucyIsImhlYWRlciIsInNoYXBlIiwiX2dldFdlYkdMMlRleHR1cmVQYXJhIiwiX3NoYXBlIiwieiIsImhlaWdodCIsIndpZHRoIiwibWlwbWFwcyIsImNoYW5uZWxzIiwiY2hhbm5lbFNpemUiLCJpIiwiX2NhbGxlZTIkIiwiX2NvbnRleHQyIiwiZ2V0TG9hZGVyT3B0aW9ucyIsIm5weSIsImxvYWQiLCJOUFlMb2FkZXIiLCJmZXRjaCIsInNpZ25hbCIsImFib3J0ZWQiLCJmcm9tIiwiX3NsaWNlZFRvQXJyYXkyIiwic3ViYXJyYXkiLCJ0MCIsImdlbmVyYXRlQ2F0ZWdvcmljYWxDb2xvcm1hcFRleHR1cmUiLCJjYXRlZ29yaWNhbE9wdGlvbnMiLCJnZW5lcmF0ZUNhdGVnb3JpY2FsQml0bWFwQXJyYXkiLCJDQVRFR09SSUNBTF9URVhUVVJFX1dJRFRIIiwiUkdCQSIsImdldE1vZHVsZXMiLCJfcmVmIiwiaW1hZ2VzIiwicHJvcHMiLCJtb2R1bGVQcm9wcyIsIm1vZHVsZXMiLCJpbWFnZVJnYmEiLCJyZW5kZXJCYW5kSW5kZXhlcyIsIm5vbkxpbmVhclJlc2NhbGluZyIsImxpbmVhclJlc2NhbGluZ0ZhY3RvciIsIm1pblBpeGVsVmFsdWUiLCJtYXhQaXhlbFZhbHVlIiwiZ2FtbWFDb250cmFzdEZhY3RvciIsInNpZ21vaWRhbENvbnRyYXN0RmFjdG9yIiwic2lnbW9pZGFsQmlhc0ZhY3RvciIsInNhdHVyYXRpb25WYWx1ZSIsImJhbmRDb21iaW5hdGlvbiIsImZpbHRlckVuYWJsZWQiLCJmaWx0ZXJSYW5nZSIsImRhdGFUeXBlIiwibWluQ2F0ZWdvcmljYWxCYW5kVmFsdWUiLCJtYXhDYXRlZ29yaWNhbEJhbmRWYWx1ZSIsImhhc0NhdGVnb3JpY2FsQ29sb3JNYXAiLCJBcnJheSIsImlzQXJyYXkiLCJtYXNrS2VlcE1pbiIsIm9yZGVyaW5nIiwiZ2xvYmFsUmFuZ2UiLCJsaW5lYXJSZXNjYWxlU2NhbGVyIiwibGluZWFyUmVzY2FsZU9mZnNldCIsImlzUmVzY2FsaW5nQWxsb3dlZCIsIl9saW5lYXJSZXNjYWxpbmdGYWN0byIsIm1pbiIsIm1heCIsImxvY2FsUmFuZ2UiLCJnYW1tYUNvbnRyYXN0VmFsdWUiLCJzaWdtb2lkYWxCaWFzIiwiTnVtYmVyIiwiaXNGaW5pdGUiLCJpc0ZpbHRlckFsbG93ZWQiLCJmaWx0ZXJNaW4xIiwiZmlsdGVyTWF4MSIsImlzQ29sb3JtYXBBbGxvd2VkIiwiaW1hZ2VDb2xvcm1hcCIsImRhdGFUeXBlTWF4VmFsdWUiLCJkdHlwZU1heFZhbHVlIl0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Jhc3Rlci10aWxlL2dwdS11dGlscy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogTUlUXG4vLyBDb3B5cmlnaHQgY29udHJpYnV0b3JzIHRvIHRoZSBrZXBsZXIuZ2wgcHJvamVjdFxuXG4vKipcbiAqIEZ1bmN0aW9ucyBhbmQgY29uc3RhbnRzIGZvciBoYW5kbGluZyB3ZWJnbC9sdW1hLmdsL2RlY2suZ2wgZW50aXRpZXNcbiAqL1xuXG5pbXBvcnQge3BhcnNlLCBmZXRjaEZpbGUsIGxvYWR9IGZyb20gJ0Bsb2FkZXJzLmdsL2NvcmUnO1xuaW1wb3J0IHtJbWFnZUxvYWRlcn0gZnJvbSAnQGxvYWRlcnMuZ2wvaW1hZ2VzJztcbmltcG9ydCB7TlBZTG9hZGVyfSBmcm9tICdAbG9hZGVycy5nbC90ZXh0dXJlcyc7XG5pbXBvcnQgR0wgZnJvbSAnQGx1bWEuZ2wvY29uc3RhbnRzJztcbmltcG9ydCB7VGV4dHVyZTJEUHJvcHN9IGZyb20gJ0BsdW1hLmdsL3dlYmdsJztcblxuaW1wb3J0IHtnZXRMb2FkZXJPcHRpb25zfSBmcm9tICdAa2VwbGVyLmdsL2NvbnN0YW50cyc7XG5pbXBvcnQge1Jhc3RlcldlYkdMfSBmcm9tICdAa2VwbGVyLmdsL2RlY2tnbC1sYXllcnMnO1xuXG50eXBlIFNoYWRlck1vZHVsZSA9IFJhc3RlcldlYkdMLlNoYWRlck1vZHVsZTtcbmNvbnN0IHtcbiAgY29tYmluZUJhbmRzRmxvYXQsXG4gIGNvbWJpbmVCYW5kc0ludCxcbiAgY29tYmluZUJhbmRzVWludCxcbiAgbWFza0Zsb2F0LFxuICBtYXNrSW50LFxuICBtYXNrVWludCxcbiAgbGluZWFyUmVzY2FsZSxcbiAgZ2FtbWFDb250cmFzdCxcbiAgc2lnbW9pZGFsQ29udHJhc3QsXG4gIG5vcm1hbGl6ZWREaWZmZXJlbmNlLFxuICBlbmhhbmNlZFZlZ2V0YXRpb25JbmRleCxcbiAgc29pbEFkanVzdGVkVmVnZXRhdGlvbkluZGV4LFxuICBtb2RpZmllZFNvaWxBZGp1c3RlZFZlZ2V0YXRpb25JbmRleCxcbiAgY29sb3JtYXA6IGNvbG9ybWFwTW9kdWxlLFxuICBmaWx0ZXIsXG4gIHNhdHVyYXRpb24sXG4gIHJlb3JkZXJCYW5kcyxcbiAgcmdiYUltYWdlXG59ID0gUmFzdGVyV2ViR0w7XG5cbmltcG9ydCB7XG4gIENBVEVHT1JJQ0FMX1RFWFRVUkVfV0lEVEgsXG4gIGR0eXBlTWF4VmFsdWUsXG4gIGdlbmVyYXRlQ2F0ZWdvcmljYWxCaXRtYXBBcnJheSxcbiAgaXNDb2xvcm1hcEFsbG93ZWQsXG4gIGlzRmlsdGVyQWxsb3dlZCxcbiAgaXNSZXNjYWxpbmdBbGxvd2VkXG59IGZyb20gJy4vcmFzdGVyLXRpbGUtdXRpbHMnO1xuaW1wb3J0IHtcbiAgQ2F0ZWdvcmljYWxDb2xvcm1hcE9wdGlvbnMsXG4gIEltYWdlRGF0YSxcbiAgTlBZTG9hZGVyRGF0YVR5cGVzLFxuICBOUFlMb2FkZXJSZXNwb25zZSxcbiAgUmVuZGVyU3ViTGF5ZXJzUHJvcHNcbn0gZnJvbSAnLi90eXBlcyc7XG5cbi8qKlxuICogRGVzY3JpYmUgV2ViR0wyIFRleHR1cmUgcGFyYW1ldGVycyB0byB1c2UgZm9yIGdpdmVuIGlucHV0IGRhdGEgdHlwZVxuICovXG5pbnRlcmZhY2UgV2ViR0xUZXh0dXJlRm9ybWF0IHtcbiAgZm9ybWF0OiBudW1iZXI7XG4gIGRhdGFGb3JtYXQ6IG51bWJlcjtcbiAgdHlwZTogbnVtYmVyO1xufVxuXG4vKipcbiAqIENvbnZlcnQgVHlwZWRBcnJheSB0byBXZWJHTDIgVGV4dHVyZSBQYXJhbWV0ZXJzXG4gKi9cbmZ1bmN0aW9uIGdldFdlYkdMMlRleHR1cmVQYXJhbWV0ZXJzKGRhdGE6IE5QWUxvYWRlckRhdGFUeXBlcyk6IFdlYkdMVGV4dHVyZUZvcm1hdCB8IG5ldmVyIHtcbiAgaWYgKGRhdGEgaW5zdGFuY2VvZiBVaW50OEFycmF5IHx8IGRhdGEgaW5zdGFuY2VvZiBVaW50OENsYW1wZWRBcnJheSkge1xuICAgIHJldHVybiB7XG4gICAgICAvLyBOb3RlOiB0ZXh0dXJlIGRhdGEgaGFzIG5vIGF1dG8tcmVzY2FsaW5nOyBwaXhlbCB2YWx1ZXMgc3RheSBhcyAwLTI1NVxuICAgICAgZm9ybWF0OiBHTC5SOFVJLFxuICAgICAgZGF0YUZvcm1hdDogR0wuUkVEX0lOVEVHRVIsXG4gICAgICB0eXBlOiBHTC5VTlNJR05FRF9CWVRFXG4gICAgfTtcbiAgfVxuXG4gIGlmIChkYXRhIGluc3RhbmNlb2YgVWludDE2QXJyYXkpIHtcbiAgICByZXR1cm4ge1xuICAgICAgZm9ybWF0OiBHTC5SMTZVSSxcbiAgICAgIGRhdGFGb3JtYXQ6IEdMLlJFRF9JTlRFR0VSLFxuICAgICAgdHlwZTogR0wuVU5TSUdORURfU0hPUlRcbiAgICB9O1xuICB9XG5cbiAgaWYgKGRhdGEgaW5zdGFuY2VvZiBVaW50MzJBcnJheSkge1xuICAgIHJldHVybiB7XG4gICAgICBmb3JtYXQ6IEdMLlIzMlVJLFxuICAgICAgZGF0YUZvcm1hdDogR0wuUkVEX0lOVEVHRVIsXG4gICAgICB0eXBlOiBHTC5VTlNJR05FRF9JTlRcbiAgICB9O1xuICB9XG5cbiAgaWYgKGRhdGEgaW5zdGFuY2VvZiBJbnQ4QXJyYXkpIHtcbiAgICByZXR1cm4ge1xuICAgICAgZm9ybWF0OiBHTC5SOEksXG4gICAgICBkYXRhRm9ybWF0OiBHTC5SRURfSU5URUdFUixcbiAgICAgIHR5cGU6IEdMLkJZVEVcbiAgICB9O1xuICB9XG5cbiAgaWYgKGRhdGEgaW5zdGFuY2VvZiBJbnQxNkFycmF5KSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGZvcm1hdDogR0wuUjE2SSxcbiAgICAgIGRhdGFGb3JtYXQ6IEdMLlJFRF9JTlRFR0VSLFxuICAgICAgdHlwZTogR0wuU0hPUlRcbiAgICB9O1xuICB9XG4gIGlmIChkYXRhIGluc3RhbmNlb2YgSW50MzJBcnJheSkge1xuICAgIHJldHVybiB7XG4gICAgICBmb3JtYXQ6IEdMLlIzMkksXG4gICAgICBkYXRhRm9ybWF0OiBHTC5SRURfSU5URUdFUixcbiAgICAgIHR5cGU6IEdMLklOVFxuICAgIH07XG4gIH1cbiAgaWYgKGRhdGEgaW5zdGFuY2VvZiBGbG9hdDMyQXJyYXkpIHtcbiAgICByZXR1cm4ge1xuICAgICAgZm9ybWF0OiBHTC5SMzJGLFxuICAgICAgZGF0YUZvcm1hdDogR0wuUkVELFxuICAgICAgdHlwZTogR0wuRkxPQVRcbiAgICB9O1xuICB9XG5cbiAgaWYgKGRhdGEgaW5zdGFuY2VvZiBGbG9hdDY0QXJyYXkpIHtcbiAgICByZXR1cm4ge1xuICAgICAgZm9ybWF0OiBHTC5SMzJGLFxuICAgICAgZGF0YUZvcm1hdDogR0wuUkVELFxuICAgICAgdHlwZTogR0wuRkxPQVRcbiAgICB9O1xuICB9XG5cbiAgLy8gRm9yIGV4aGF1c3RpdmUgY2hlY2sgYWJvdmU7IGZvbGxvd2luZyBzaG91bGQgbmV2ZXIgb2NjdXJcbiAgLy8gaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9hLzU4MDA5OTkyXG4gIGNvbnN0IHVuZXhwZWN0ZWRJbnB1dDogbmV2ZXIgPSBkYXRhO1xuICB0aHJvdyBuZXcgRXJyb3IodW5leHBlY3RlZElucHV0KTtcbn1cblxuLyoqXG4gKiBEaXNjcmV0ZS12YWx1ZWQgY29sb3JtYXBzIChlLmcuIGZyb20gdGhlIG91dHB1dCBvZlxuICogY2xhc3NpZmljYXRpb24gYWxnb3JpdGhtcykgaW4gdGhlIHJhc3RlciBsYXllci4gUHJldmlvdXNseSwgdGhlIHZhbHVlcyBwYXNzZWQgdG9cbiAqIGBURVhUVVJFX01JTl9GSUxURVJgIGFuZCBgVEVYVFVSRV9NQUdfRklMVEVSYCB3ZXJlIGBHTC5MSU5FQVJgLCB3aGljaCBtZWFudCB0aGF0IHRoZSBHUFUgd291bGRcbiAqIGxpbmVhcmx5IGludGVycG9sYXRlIHZhbHVlcyBiZXR3ZWVuIHR3byBuZWlnaGJvcmluZyBjb2xvcm1hcCBwaXhlbCB2YWx1ZXMuIFNldHRpbmcgdGhlc2UgdmFsdWVzXG4gKiB0byBORUFSRVNUIG1lYW5zIHRoYXQgdGhlIEdQVSB3aWxsIGNob29zZSB0aGUgbmVhcmVzdCB2YWx1ZSBvbiB0aGUgdGV4dHVyZTJEIGxvb2t1cCBvcGVyYXRpb24sXG4gKiB3aGljaCBmaXhlcyBwcmVjaXNpb24gaXNzdWVzIGZvciBkaXNjcmV0ZS12YWx1ZWQgY29sb3JtYXBzLiBUaGlzIHNob3VsZCBiZSBvayBmb3IgY29udGludW91c1xuICogY29sb3JtYXBzIGFzIGxvbmcgYXMgdGhlIGNvbG9yIGRpZmZlcmVuY2UgYmV0d2VlbiBlYWNoIHBpeGVsIG9uIHRoZSBjb2xvcm1hcCBpcyBzbWFsbC5cbiAqL1xuZXhwb3J0IGNvbnN0IENPTE9STUFQX1RFWFRVUkVfUEFSQU1FVEVSUyA9IHtcbiAgW0dMLlRFWFRVUkVfTUlOX0ZJTFRFUl06IEdMLk5FQVJFU1QsXG4gIFtHTC5URVhUVVJFX01BR19GSUxURVJdOiBHTC5ORUFSRVNULFxuICBbR0wuVEVYVFVSRV9XUkFQX1NdOiBHTC5DTEFNUF9UT19FREdFLFxuICBbR0wuVEVYVFVSRV9XUkFQX1RdOiBHTC5DTEFNUF9UT19FREdFXG59O1xuXG5jb25zdCBERUZBVUxUXzhCSVRfVEVYVFVSRV9QQVJBTUVURVJTID0ge1xuICBbR0wuVEVYVFVSRV9NSU5fRklMVEVSXTogR0wuTElORUFSX01JUE1BUF9MSU5FQVIsXG4gIFtHTC5URVhUVVJFX01BR19GSUxURVJdOiBHTC5MSU5FQVIsXG4gIFtHTC5URVhUVVJFX1dSQVBfU106IEdMLkNMQU1QX1RPX0VER0UsXG4gIFtHTC5URVhUVVJFX1dSQVBfVF06IEdMLkNMQU1QX1RPX0VER0Vcbn07XG5cbmNvbnN0IERFRkFVTFRfSElHSF9CSVRfVEVYVFVSRV9QQVJBTUVURVJTID0ge1xuICBbR0wuVEVYVFVSRV9NSU5fRklMVEVSXTogR0wuTkVBUkVTVCxcbiAgW0dMLlRFWFRVUkVfTUFHX0ZJTFRFUl06IEdMLk5FQVJFU1QsXG4gIFtHTC5URVhUVVJFX1dSQVBfU106IEdMLkNMQU1QX1RPX0VER0UsXG4gIFtHTC5URVhUVVJFX1dSQVBfVF06IEdMLkNMQU1QX1RPX0VER0Vcbn07XG5cbi8qKlxuICogU2VsZWN0IGNvcnJlY3QgbW9kdWxlIHR5cGUgZm9yIFwiY29tYmluZUJhbmRzXCJcbiAqXG4gKiBjb21iaW5lQmFuZHMgam9pbnMgdXAgdG8gZm91ciAyRCBhcnJheXMgKGNvbnRhaW5lZCBpbiBpbWFnZUJhbmRzKSBpbnRvIGEgc2luZ2xlIFwicmdiYVwiIGltYWdlXG4gKiB0ZXh0dXJlIG9uIHRoZSBHUFUuIFRoYXQgc2hhZGVyIGNvZGUgbmVlZHMgdG8gaGF2ZSB0aGUgc2FtZSBkYXRhIHR5cGUgYXMgdGhlIGFjdHVhbCBpbWFnZSBkYXRhLlxuICogRS5nLiBmb3IgZmxvYXQgZGF0YSB0aGUgdGV4dHVyZSBuZWVkcyB0byBiZSBgc2FtcGxlcjJEYCwgZm9yIHVpbnQgZGF0YSB0aGUgdGV4dHVyZSBuZWVkcyB0byBiZVxuICogYHVzYW1wbGVyMkRgIGFuZCBmb3IgaW50IGRhdGEgdGhlIHRleHR1cmUgbmVlZHMgdG8gYmUgYGlzYW1wbGVyMkRgLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q29tYmluZUJhbmRzTW9kdWxlKGltYWdlQmFuZHM6IFRleHR1cmUyRFByb3BzW10pOiBTaGFkZXJNb2R1bGUge1xuICAvLyBFYWNoIGltYWdlIGFycmF5IGlzIGV4cGVjdGVkL3JlcXVpcmVkIHRvIGJlIG9mIHRoZSBzYW1lIGRhdGEgdHlwZVxuICBzd2l0Y2ggKGltYWdlQmFuZHNbMF0uZm9ybWF0KSB7XG4gICAgY2FzZSBHTC5SOFVJOlxuICAgICAgcmV0dXJuIGNvbWJpbmVCYW5kc1VpbnQ7XG4gICAgY2FzZSBHTC5SMTZVSTpcbiAgICAgIHJldHVybiBjb21iaW5lQmFuZHNVaW50O1xuICAgIGNhc2UgR0wuUjMyVUk6XG4gICAgICByZXR1cm4gY29tYmluZUJhbmRzVWludDtcbiAgICBjYXNlIEdMLlI4STpcbiAgICAgIHJldHVybiBjb21iaW5lQmFuZHNJbnQ7XG4gICAgY2FzZSBHTC5SMTZJOlxuICAgICAgcmV0dXJuIGNvbWJpbmVCYW5kc0ludDtcbiAgICBjYXNlIEdMLlIzMkk6XG4gICAgICByZXR1cm4gY29tYmluZUJhbmRzSW50O1xuICAgIGNhc2UgR0wuUjMyRjpcbiAgICAgIHJldHVybiBjb21iaW5lQmFuZHNGbG9hdDtcbiAgICBkZWZhdWx0OlxuICAgICAgdGhyb3cgbmV3IEVycm9yKCdiYWQgZm9ybWF0Jyk7XG4gIH1cbn1cblxuLyoqIFNlbGVjdCBjb3JyZWN0IGltYWdlIG1hc2tpbmcgc2hhZGVyIG1vZHVsZSBmb3IgbWFzayBkYXRhIHR5cGVcbiAqIFRoZSBpbWFnZU1hc2sgY291bGQgKGF0IGxlYXN0IGluIHRoZSBmdXR1cmUsIHRoZW9yZXRpY2FsbHkpIGJlIG9mIGEgZGlmZmVyZW50IGRhdGEgZm9ybWF0IHRoYW5cbiAqIHRoZSBpbWFnZUJhbmRzIGRhdGEgaXRzZWxmLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0SW1hZ2VNYXNrTW9kdWxlKGltYWdlTWFzazogVGV4dHVyZTJEUHJvcHMpOiBTaGFkZXJNb2R1bGUge1xuICBzd2l0Y2ggKGltYWdlTWFzay5mb3JtYXQpIHtcbiAgICBjYXNlIEdMLlI4VUk6XG4gICAgICByZXR1cm4gbWFza1VpbnQ7XG4gICAgY2FzZSBHTC5SMTZVSTpcbiAgICAgIHJldHVybiBtYXNrVWludDtcbiAgICBjYXNlIEdMLlIzMlVJOlxuICAgICAgcmV0dXJuIG1hc2tVaW50O1xuICAgIGNhc2UgR0wuUjhJOlxuICAgICAgcmV0dXJuIG1hc2tJbnQ7XG4gICAgY2FzZSBHTC5SMTZJOlxuICAgICAgcmV0dXJuIG1hc2tJbnQ7XG4gICAgY2FzZSBHTC5SMzJJOlxuICAgICAgcmV0dXJuIG1hc2tJbnQ7XG4gICAgY2FzZSBHTC5SMzJGOlxuICAgICAgcmV0dXJuIG1hc2tGbG9hdDtcbiAgICBkZWZhdWx0OlxuICAgICAgdGhyb3cgbmV3IEVycm9yKCdiYWQgZm9ybWF0Jyk7XG4gIH1cbn1cblxuLyoqXG4gKiBMb2FkIGltYWdlIGFuZCB3cmFwIHdpdGggZGVmYXVsdCBXZWJHTCB0ZXh0dXJlIHBhcmFtZXRlcnNcbiAqXG4gKiBAcGFyYW0gdXJsIFVSTCB0byBsb2FkIGltYWdlXG4gKiBAcGFyYW0gdGV4dHVyZVBhcmFtcyBwYXJhbWV0ZXJzIHRvIHBhc3MgdG8gVGV4dHVyZTJEXG4gKlxuICogQHJldHVybiBpbWFnZSBvYmplY3QgdG8gcGFzcyB0byBUZXh0dXJlMkQgY29uc3RydWN0b3JcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRJbWFnZShcbiAgdXJsOiBzdHJpbmcsXG4gIHRleHR1cmVQYXJhbXM6IFRleHR1cmUyRFByb3BzID0ge30sXG4gIHJlcXVlc3RPcHRpb25zOiBSZXF1ZXN0SW5pdCA9IHt9XG4pOiBQcm9taXNlPFRleHR1cmUyRFByb3BzPiB7XG4gIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2hGaWxlKHVybCwgcmVxdWVzdE9wdGlvbnMpO1xuICBjb25zdCBpbWFnZSA9IGF3YWl0IHBhcnNlKHJlc3BvbnNlLCBJbWFnZUxvYWRlcik7XG5cbiAgcmV0dXJuIHtcbiAgICBkYXRhOiBpbWFnZSxcbiAgICBwYXJhbWV0ZXJzOiBERUZBVUxUXzhCSVRfVEVYVFVSRV9QQVJBTUVURVJTLFxuICAgIGZvcm1hdDogR0wuUkdCLFxuICAgIC4uLnRleHR1cmVQYXJhbXNcbiAgfTtcbn1cblxudHlwZSBGZXRjaExpa2UgPSAodXJsOiBzdHJpbmcsIG9wdGlvbnM/OiBSZXF1ZXN0SW5pdCkgPT4gUHJvbWlzZTxSZXNwb25zZT47XG50eXBlIExvYWRpbmdPcHRpb25zID0ge1xuICBmZXRjaD86IHR5cGVvZiBmZXRjaCB8IEZldGNoTGlrZTtcbn07XG5cbi8qKlxuICogTG9hZCBOUFkgQXJyYXlcbiAqXG4gKiBUaGUgTlBZIGZvcm1hdCBpcyBkZXNjcmliZWQgaGVyZTogaHR0cHM6Ly9udW1weS5vcmcvZG9jL3N0YWJsZS9yZWZlcmVuY2UvZ2VuZXJhdGVkL251bXB5LmxpYi5mb3JtYXQuaHRtbC5cbiAqIEl0J3MgZGVzaWduZWQgdG8gYmUgYSB2ZXJ5IHNpbXBsZSBmaWxlIGZvcm1hdCB0byBob2xkIGFuIE4tZGltZW5zaW9uYWwgYmxvY2sgb2YgZGF0YS4gVGhlIGhlYWRlciBkZXNjcmliZXMgdGhlIGRhdGEgdHlwZSwgc2hhcGUsIGFuZCBvcmRlciAoZWl0aGVyIEMgb3IgRm9ydHJhbikgb2YgdGhlIGFycmF5LlxuICpcbiAqIEBwYXJhbSB1cmwgVVJMIHRvIGxvYWQgTlBZIEFycmF5XG4gKiBAcGFyYW0gc3BsaXQgV2hldGhlciB0byBzcGxpdCBzaW5nbGUgdHlwZWQgYXJyYXkgcmVwcmVzZW50aW5nIGFuIE4tZGltZW5zaW9uYWwgYXJyYXkgaW50byBhbiBBcnJheSB3aXRoIGVhY2ggZGltZW5zaW9uIGFzIGl0cyBvd24gdHlwZWQgYXJyYXlcbiAqXG4gKiBAcmV0dXJuIGltYWdlIG9iamVjdCB0byBwYXNzIHRvIFRleHR1cmUyRCBjb25zdHJ1Y3RvclxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbG9hZE5weUFycmF5KFxuICByZXF1ZXN0OiB7dXJsOiBzdHJpbmc7IG9wdGlvbnM6IFJlcXVlc3RJbml0fSxcbiAgc3BsaXQ6IHRydWUsXG4gIG9wdGlvbnM/OiBMb2FkaW5nT3B0aW9uc1xuKTogUHJvbWlzZTxUZXh0dXJlMkRQcm9wc1tdIHwgbnVsbD47XG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbG9hZE5weUFycmF5KFxuICByZXF1ZXN0OiB7dXJsOiBzdHJpbmc7IG9wdGlvbnM6IFJlcXVlc3RJbml0fSxcbiAgc3BsaXQ6IGZhbHNlLFxuICBvcHRpb25zPzogTG9hZGluZ09wdGlvbnNcbik6IFByb21pc2U8VGV4dHVyZTJEUHJvcHMgfCBudWxsPjtcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2FkTnB5QXJyYXkoXG4gIHJlcXVlc3Q6IHt1cmw6IHN0cmluZzsgb3B0aW9uczogUmVxdWVzdEluaXR9LFxuICBzcGxpdDogYm9vbGVhbixcbiAgb3B0aW9ucz86IExvYWRpbmdPcHRpb25zXG4pOiBQcm9taXNlPFRleHR1cmUyRFByb3BzIHwgVGV4dHVyZTJEUHJvcHNbXSB8IG51bGw+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCB7bnB5OiBucHlPcHRpb25zfSA9IGdldExvYWRlck9wdGlvbnMoKTtcbiAgICBjb25zdCByZXNwb25zZTogTlBZTG9hZGVyUmVzcG9uc2UgPSBhd2FpdCBsb2FkKHJlcXVlc3QudXJsLCBOUFlMb2FkZXIsIHtcbiAgICAgIG5weTogbnB5T3B0aW9ucyxcbiAgICAgIGZldGNoOiBvcHRpb25zPy5mZXRjaFxuICAgIH0pO1xuXG4gICAgaWYgKCFyZXNwb25zZSB8fCAhcmVzcG9uc2UuZGF0YSB8fCByZXF1ZXN0Lm9wdGlvbnMuc2lnbmFsPy5hYm9ydGVkKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICAvLyBGbG9hdDY0IGRhdGEgbmVlZHMgdG8gYmUgY29lcmNlZCB0byBGbG9hdDMyIGZvciB0aGUgR1BVXG4gICAgaWYgKHJlc3BvbnNlLmRhdGEgaW5zdGFuY2VvZiBGbG9hdDY0QXJyYXkpIHtcbiAgICAgIHJlc3BvbnNlLmRhdGEgPSBGbG9hdDMyQXJyYXkuZnJvbShyZXNwb25zZS5kYXRhKTtcbiAgICB9XG5cbiAgICBjb25zdCB7ZGF0YSwgaGVhZGVyfSA9IHJlc3BvbnNlO1xuICAgIGNvbnN0IHtzaGFwZX0gPSBoZWFkZXI7XG4gICAgY29uc3Qge2Zvcm1hdCwgZGF0YUZvcm1hdCwgdHlwZX0gPSBnZXRXZWJHTDJUZXh0dXJlUGFyYW1ldGVycyhkYXRhKTtcblxuICAgIC8vIFRPRE86IGNoZWNrIGhlaWdodC13aWR0aCBvciB3aWR0aC1oZWlnaHRcbiAgICAvLyBSZWdhcmRsZXNzLCBpbWFnZXMgdXN1YWxseSBzcXVhcmVcbiAgICAvLyBUT0RPOiBoYW5kbGUgY2FzZXMgb2YgMjU2eDI1NngxIGluc3RlYWQgb2YgMXgyNTZ4MjU2XG4gICAgY29uc3QgW3osIGhlaWdodCwgd2lkdGhdID0gc2hhcGU7XG5cbiAgICAvLyBTaW5jZSB3ZSBub3cgdXNlIFdlYkdMMiBkYXRhIHR5cGVzIGZvciA4LWJpdCB0ZXh0dXJlcywgd2Ugc2V0IHRoZSBmb2xsb3dpbmcgZm9yIGFsbCB0ZXh0dXJlc1xuICAgIGNvbnN0IG1pcG1hcHMgPSBmYWxzZTtcbiAgICBjb25zdCBwYXJhbWV0ZXJzID0gREVGQVVMVF9ISUdIX0JJVF9URVhUVVJFX1BBUkFNRVRFUlM7XG5cbiAgICBpZiAoIXNwbGl0KSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBkYXRhLFxuICAgICAgICB3aWR0aCxcbiAgICAgICAgaGVpZ2h0LFxuICAgICAgICBmb3JtYXQsXG4gICAgICAgIGRhdGFGb3JtYXQsXG4gICAgICAgIHR5cGUsXG4gICAgICAgIHBhcmFtZXRlcnMsXG4gICAgICAgIG1pcG1hcHNcbiAgICAgIH07XG4gICAgfVxuXG4gICAgLy8gU3BsaXQgaW50byBpbmRpdmlkdWFsIGFycmF5c1xuICAgIGNvbnN0IGNoYW5uZWxzOiBUZXh0dXJlMkRQcm9wc1tdID0gW107XG4gICAgY29uc3QgY2hhbm5lbFNpemUgPSBoZWlnaHQgKiB3aWR0aDtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHo7IGkrKykge1xuICAgICAgY2hhbm5lbHMucHVzaCh7XG4gICAgICAgIGRhdGE6IGRhdGEuc3ViYXJyYXkoaSAqIGNoYW5uZWxTaXplLCAoaSArIDEpICogY2hhbm5lbFNpemUpLFxuICAgICAgICB3aWR0aCxcbiAgICAgICAgaGVpZ2h0LFxuICAgICAgICBmb3JtYXQsXG4gICAgICAgIGRhdGFGb3JtYXQsXG4gICAgICAgIHR5cGUsXG4gICAgICAgIHBhcmFtZXRlcnMsXG4gICAgICAgIG1pcG1hcHNcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gY2hhbm5lbHM7XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiBudWxsO1xuICB9XG59XG5cbi8qKlxuICogQ3JlYXRlIHRleHR1cmUgZGF0YSBmb3IgY2F0ZWdvcmljYWwgY29sb3JtYXAgc2NhbGVcbiAqIEBwYXJhbSBjYXRlZ29yaWNhbE9wdGlvbnMgLSBjb2xvciBtYXAgY29uZmlndXJhdGlvbiBhbmQgbWluLW1heCB2YWx1ZXMgb2YgY2F0ZWdvcmljYWwgYmFuZFxuICogQHJldHVybnMgdGV4dHVyZSBkYXRhXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZW5lcmF0ZUNhdGVnb3JpY2FsQ29sb3JtYXBUZXh0dXJlKFxuICBjYXRlZ29yaWNhbE9wdGlvbnM6IENhdGVnb3JpY2FsQ29sb3JtYXBPcHRpb25zXG4pOiBUZXh0dXJlMkRQcm9wcyB7XG4gIGNvbnN0IGRhdGEgPSBnZW5lcmF0ZUNhdGVnb3JpY2FsQml0bWFwQXJyYXkoY2F0ZWdvcmljYWxPcHRpb25zKTtcbiAgcmV0dXJuIHtcbiAgICBkYXRhLFxuICAgIHdpZHRoOiBDQVRFR09SSUNBTF9URVhUVVJFX1dJRFRILFxuICAgIGhlaWdodDogMSxcbiAgICBmb3JtYXQ6IEdMLlJHQkEsXG4gICAgZGF0YUZvcm1hdDogR0wuUkdCQSxcbiAgICB0eXBlOiBHTC5VTlNJR05FRF9CWVRFLFxuICAgIHBhcmFtZXRlcnM6IENPTE9STUFQX1RFWFRVUkVfUEFSQU1FVEVSUyxcbiAgICBtaXBtYXBzOiBmYWxzZVxuICB9O1xufVxuXG4vLyBUT0RPOiB3b3VsZCBwcm9iYWJseSBiZSBzaW1wbGVyIHRvIG9ubHkgcGFzcyBpbiB0aGUgcHJvcHMgYWN0dWFsbHkgdXNlZCBieSB0aGlzIGZ1bmN0aW9uLiBUaGF0XG4vLyB3b3VsZCBtZWFuIGEgc21hbGxlciBvYmplY3QgdGhhbiBSZW5kZXJTdWJMYXllcnNQcm9wc1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG1heC1zdGF0ZW1lbnRzLCBjb21wbGV4aXR5XG5leHBvcnQgZnVuY3Rpb24gZ2V0TW9kdWxlcyh7XG4gIGltYWdlcyxcbiAgcHJvcHNcbn06IHtcbiAgaW1hZ2VzOiBQYXJ0aWFsPEltYWdlRGF0YT47XG4gIHByb3BzPzogUmVuZGVyU3ViTGF5ZXJzUHJvcHM7XG59KToge1xuICBtb2R1bGVzOiBTaGFkZXJNb2R1bGVbXTtcbiAgbW9kdWxlUHJvcHM6IFJlY29yZDxzdHJpbmcsIGFueT47XG59IHtcbiAgY29uc3QgbW9kdWxlUHJvcHM6IFJlY29yZDxzdHJpbmcsIGFueT4gPSB7fTtcbiAgLy8gQXJyYXkgb2YgbHVtYS5nbCBXZWJHTCBtb2R1bGVzIHRvIHBhc3MgdG8gdGhlIFJhc3RlckxheWVyXG4gIGNvbnN0IG1vZHVsZXM6IFNoYWRlck1vZHVsZVtdID0gW107XG5cbiAgLy8gdXNlIHJnYmEgaW1hZ2UgZGlyZWN0bHkuIFVzZWQgZm9yIHJhc3RlciAucG10aWxlcyByZW5kZXJpbmdcbiAgaWYgKGltYWdlcy5pbWFnZVJnYmEpIHtcbiAgICBtb2R1bGVzLnB1c2gocmdiYUltYWdlKTtcblxuICAgIC8vIG5vIHN1cHBvcnQgZm9yIG90aGVyIG1vZHVsZXMgYXRtIGZvciBkaXJlY3QgcmdiYSBtb2RlXG4gICAgcmV0dXJuIHttb2R1bGVzLCBtb2R1bGVQcm9wc307XG4gIH1cblxuICBpZiAoIXByb3BzKSB7XG4gICAgcmV0dXJuIHttb2R1bGVzLCBtb2R1bGVQcm9wc307XG4gIH1cblxuICBjb25zdCB7XG4gICAgcmVuZGVyQmFuZEluZGV4ZXMsXG4gICAgbm9uTGluZWFyUmVzY2FsaW5nLFxuICAgIGxpbmVhclJlc2NhbGluZ0ZhY3RvcixcbiAgICBtaW5QaXhlbFZhbHVlLFxuICAgIG1heFBpeGVsVmFsdWUsXG4gICAgZ2FtbWFDb250cmFzdEZhY3RvcixcbiAgICBzaWdtb2lkYWxDb250cmFzdEZhY3RvcixcbiAgICBzaWdtb2lkYWxCaWFzRmFjdG9yLFxuICAgIHNhdHVyYXRpb25WYWx1ZSxcbiAgICBiYW5kQ29tYmluYXRpb24sXG4gICAgZmlsdGVyRW5hYmxlZCxcbiAgICBmaWx0ZXJSYW5nZSxcbiAgICBkYXRhVHlwZSxcbiAgICBtaW5DYXRlZ29yaWNhbEJhbmRWYWx1ZSxcbiAgICBtYXhDYXRlZ29yaWNhbEJhbmRWYWx1ZSxcbiAgICBoYXNDYXRlZ29yaWNhbENvbG9yTWFwXG4gIH0gPSBwcm9wcztcblxuICBpZiAoQXJyYXkuaXNBcnJheShpbWFnZXMuaW1hZ2VCYW5kcykgJiYgaW1hZ2VzLmltYWdlQmFuZHMubGVuZ3RoID4gMCkge1xuICAgIG1vZHVsZXMucHVzaChnZXRDb21iaW5lQmFuZHNNb2R1bGUoaW1hZ2VzLmltYWdlQmFuZHMpKTtcbiAgfVxuXG4gIGlmIChpbWFnZXMuaW1hZ2VNYXNrKSB7XG4gICAgbW9kdWxlcy5wdXNoKGdldEltYWdlTWFza01vZHVsZShpbWFnZXMuaW1hZ2VNYXNrKSk7XG4gICAgLy8gSW4gZ2VuZXJhbCwgZGF0YSBtYXNrcyBhcmUgMCBmb3Igbm9kYXRhIGFuZCB0aGUgbWF4aW11bSB2YWx1ZSBmb3IgdmFsaWQgZGF0YSwgZS5nLiAyNTUgb3JcbiAgICAvLyA2NTUzNSBmb3IgdWludDggb3IgdWludDE2IGRhdGEsIHJlc3BlY3RpdmVseVxuICAgIG1vZHVsZVByb3BzLm1hc2tLZWVwTWluID0gMTtcbiAgfVxuXG4gIGlmIChBcnJheS5pc0FycmF5KHJlbmRlckJhbmRJbmRleGVzKSkge1xuICAgIG1vZHVsZXMucHVzaChyZW9yZGVyQmFuZHMpO1xuICAgIG1vZHVsZVByb3BzLm9yZGVyaW5nID0gcmVuZGVyQmFuZEluZGV4ZXM7XG4gIH1cblxuICBjb25zdCBnbG9iYWxSYW5nZSA9IG1heFBpeGVsVmFsdWUgLSBtaW5QaXhlbFZhbHVlO1xuICAvLyBGaXggcmVzY2FsaW5nIGlmIHdlIGFyZSBzdXJlIHRoYXQgZGF0YXNldCBpcyBjYXRlZ29yaWNhbFxuICBpZiAoaGFzQ2F0ZWdvcmljYWxDb2xvck1hcCkge1xuICAgIG1vZHVsZXMucHVzaChsaW5lYXJSZXNjYWxlKTtcbiAgICBtb2R1bGVQcm9wcy5saW5lYXJSZXNjYWxlU2NhbGVyID0gMSAvIG1heFBpeGVsVmFsdWU7XG4gICAgbW9kdWxlUHJvcHMubGluZWFyUmVzY2FsZU9mZnNldCA9IDA7XG4gIH0gZWxzZSBpZiAoaXNSZXNjYWxpbmdBbGxvd2VkKGJhbmRDb21iaW5hdGlvbikpIHtcbiAgICBpZiAoIW5vbkxpbmVhclJlc2NhbGluZykge1xuICAgICAgY29uc3QgW21pbiwgbWF4XSA9IGxpbmVhclJlc2NhbGluZ0ZhY3RvcjtcbiAgICAgIGNvbnN0IGxvY2FsUmFuZ2UgPSBtYXggLSBtaW47XG5cbiAgICAgIC8vIEFkZCBsaW5lYXIgcmVzY2FsaW5nIG1vZHVsZVxuICAgICAgbW9kdWxlcy5wdXNoKGxpbmVhclJlc2NhbGUpO1xuXG4gICAgICAvLyBEaXZpZGUgYnkgbG9jYWwgcmFuZ2UgKiBnbG9iYWwgcmFuZ2VcbiAgICAgIG1vZHVsZVByb3BzLmxpbmVhclJlc2NhbGVTY2FsZXIgPSAxIC8gKGxvY2FsUmFuZ2UgKiBnbG9iYWxSYW5nZSk7XG5cbiAgICAgIC8vIFN1YnRyYWN0IG9mZiB0aGUgbG9jYWwgbWluXG4gICAgICBtb2R1bGVQcm9wcy5saW5lYXJSZXNjYWxlT2Zmc2V0ID0gLW1pbjtcblxuICAgICAgLy8gQ2xhbXAgdG8gWzAsIDFdIGRvbmUgYXV0b21hdGljYWxseT9cbiAgICB9IGVsc2Uge1xuICAgICAgbW9kdWxlcy5wdXNoKGxpbmVhclJlc2NhbGUpO1xuICAgICAgbW9kdWxlUHJvcHMubGluZWFyUmVzY2FsZVNjYWxlciA9IDEgLyBtYXhQaXhlbFZhbHVlO1xuICAgICAgbW9kdWxlUHJvcHMubGluZWFyUmVzY2FsZU9mZnNldCA9IDA7XG5cbiAgICAgIG1vZHVsZXMucHVzaChnYW1tYUNvbnRyYXN0KTtcbiAgICAgIG1vZHVsZVByb3BzLmdhbW1hQ29udHJhc3RWYWx1ZSA9IGdhbW1hQ29udHJhc3RGYWN0b3I7XG5cbiAgICAgIG1vZHVsZXMucHVzaChzaWdtb2lkYWxDb250cmFzdCk7XG4gICAgICBtb2R1bGVQcm9wcy5zaWdtb2lkYWxDb250cmFzdCA9IHNpZ21vaWRhbENvbnRyYXN0RmFjdG9yO1xuICAgICAgbW9kdWxlUHJvcHMuc2lnbW9pZGFsQmlhcyA9IHNpZ21vaWRhbEJpYXNGYWN0b3I7XG4gICAgfVxuXG4gICAgaWYgKE51bWJlci5pc0Zpbml0ZShzYXR1cmF0aW9uVmFsdWUpICYmIHNhdHVyYXRpb25WYWx1ZSAhPT0gMSkge1xuICAgICAgbW9kdWxlcy5wdXNoKHNhdHVyYXRpb24pO1xuICAgICAgbW9kdWxlUHJvcHMuc2F0dXJhdGlvblZhbHVlID0gc2F0dXJhdGlvblZhbHVlO1xuICAgIH1cbiAgfVxuXG4gIHN3aXRjaCAoYmFuZENvbWJpbmF0aW9uKSB7XG4gICAgY2FzZSAnbm9ybWFsaXplZERpZmZlcmVuY2UnOlxuICAgICAgbW9kdWxlcy5wdXNoKG5vcm1hbGl6ZWREaWZmZXJlbmNlKTtcbiAgICAgIGJyZWFrO1xuICAgIGNhc2UgJ2VuaGFuY2VkVmVnZXRhdGlvbkluZGV4JzpcbiAgICAgIG1vZHVsZXMucHVzaChlbmhhbmNlZFZlZ2V0YXRpb25JbmRleCk7XG4gICAgICBicmVhaztcbiAgICBjYXNlICdzb2lsQWRqdXN0ZWRWZWdldGF0aW9uSW5kZXgnOlxuICAgICAgbW9kdWxlcy5wdXNoKHNvaWxBZGp1c3RlZFZlZ2V0YXRpb25JbmRleCk7XG4gICAgICBicmVhaztcbiAgICBjYXNlICdtb2RpZmllZFNvaWxBZGp1c3RlZFZlZ2V0YXRpb25JbmRleCc6XG4gICAgICBtb2R1bGVzLnB1c2gobW9kaWZpZWRTb2lsQWRqdXN0ZWRWZWdldGF0aW9uSW5kZXgpO1xuICAgICAgYnJlYWs7XG4gICAgZGVmYXVsdDpcbiAgICAgIGJyZWFrO1xuICB9XG5cbiAgaWYgKGlzRmlsdGVyQWxsb3dlZChiYW5kQ29tYmluYXRpb24pICYmIGZpbHRlckVuYWJsZWQpIHtcbiAgICBtb2R1bGVzLnB1c2goZmlsdGVyKTtcbiAgICBtb2R1bGVQcm9wcy5maWx0ZXJNaW4xID0gZmlsdGVyUmFuZ2VbMF07XG4gICAgbW9kdWxlUHJvcHMuZmlsdGVyTWF4MSA9IGZpbHRlclJhbmdlWzFdO1xuICB9XG5cbiAgLy8gQXBwbHkgY29sb3JtYXBcbiAgaWYgKGlzQ29sb3JtYXBBbGxvd2VkKGJhbmRDb21iaW5hdGlvbikgJiYgaW1hZ2VzLmltYWdlQ29sb3JtYXApIHtcbiAgICBtb2R1bGVzLnB1c2goY29sb3JtYXBNb2R1bGUpO1xuICAgIG1vZHVsZVByb3BzLm1pbkNhdGVnb3JpY2FsQmFuZFZhbHVlID0gbWluQ2F0ZWdvcmljYWxCYW5kVmFsdWU7XG4gICAgbW9kdWxlUHJvcHMubWF4Q2F0ZWdvcmljYWxCYW5kVmFsdWUgPSBtYXhDYXRlZ29yaWNhbEJhbmRWYWx1ZTtcbiAgICBtb2R1bGVQcm9wcy5kYXRhVHlwZU1heFZhbHVlID0gZHR5cGVNYXhWYWx1ZVtkYXRhVHlwZV07XG4gICAgbW9kdWxlUHJvcHMubWF4UGl4ZWxWYWx1ZSA9IG1heFBpeGVsVmFsdWU7XG4gIH1cblxuICByZXR1cm4ge21vZHVsZXMsIG1vZHVsZVByb3BzfTtcbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFPQSxJQUFBQSxLQUFBLEdBQUFDLE9BQUE7QUFDQSxJQUFBQyxPQUFBLEdBQUFELE9BQUE7QUFDQSxJQUFBRSxTQUFBLEdBQUFGLE9BQUE7QUFDQSxJQUFBRyxVQUFBLEdBQUFDLHNCQUFBLENBQUFKLE9BQUE7QUFHQSxJQUFBSyxXQUFBLEdBQUFMLE9BQUE7QUFDQSxJQUFBTSxhQUFBLEdBQUFOLE9BQUE7QUF3QkEsSUFBQU8sZ0JBQUEsR0FBQVAsT0FBQTtBQU82QixTQUFBUSxRQUFBQyxDQUFBLEVBQUFDLENBQUEsUUFBQUMsQ0FBQSxHQUFBQyxNQUFBLENBQUFDLElBQUEsQ0FBQUosQ0FBQSxPQUFBRyxNQUFBLENBQUFFLHFCQUFBLFFBQUFDLENBQUEsR0FBQUgsTUFBQSxDQUFBRSxxQkFBQSxDQUFBTCxDQUFBLEdBQUFDLENBQUEsS0FBQUssQ0FBQSxH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