getwd()
[1] "C:/percepcionremota/PR/Informe2_GEE"
Introducciòn
Google Earth Engine es una plataforma basada en la nube para el análisis geoespacial a escala planetaria que ofrece las capacidades computacionales masivas de Google para abordar una variedad de problemas sociales de alto impacto, incluyendo deforestación, sequía, desastres, enfermedades, seguridad alimentaria, gestión del agua, monitoreo del clima y protección del medio ambiente. Es único en el campo como una plataforma integrada diseñada para potenciar no solo a los científicos de teledetección tradicionales , sino también a un público mucho más amplio que carece de la capacidad técnica necesaria para utilizar supercomputadoras tradicionales o recursos de computación en la nube de productos básicos a gran escala (Gorelick et al., 2017).
Viera (2017) cita a (Venturino et al., 2014) y (Google, 2016) quienes hacen referencia de que Google Earth Engine (GEE) es una plataforma de escala planetaria para el análisis de datos medioambientales. Reúne más de 40 años de imágenes de satélite de todo el mundo actuales e históricas, y ofrece las herramientas y la potencia computacional necesarias para analizar y extraer información de este enorme almacén de datos. Entre una de sus aplicaciones es detección de cambios de la cubierta terrestre. GEE es una tecnología paralela masiva para el procesamiento de alto rendimiento de los datos geoespaciales, y alberga una copia de todo el catálogo de imágenes Landsat y otras imágenes. En este informe tutorial se utiliza GEE como una alternativa a los procesamientos digitales de imágenes (PDI) donde se abordara el área compuesta por el municipio de Sasaima Cundinamarca.
Este informe tutorial es herramienta para el análisis de imágenes satelitales que se pueda usar a partir de líneas de código con lenguaje de programación de JavaScript a través de la plataforma Google Earth Engine (GEE) Code Editor API. JavaScript es un lenguaje de script, creado por Brendan Eich para el navegador Netscape en 1995 con el fin de posibilitar páginas web más dinámicas. Hoy en día es uno de los lenguajes de programación más importantes en el mundo de la informática, ya que se utiliza en la gran mayoría de los sitios web modernos. Una ventaja de JavaScript es que se incrusta fácilmente dentro de HTML, el lenguaje de marcado empleado para codificar páginas web. Además, como afirma Subramanian, es uno de los lenguajes de programación más poderosos, extendidos y flexibles que existen actualmente (Krohn, 2019). El siguiente informe tutorial se hace con el objetivo de entender el funcionamiento de la plataforma GEE, algunas líneas de código básicas para el procesamiento de imágenes.
Palabras claves: GEE, Landsat, Sentinel, NDVI.
Objetivo general
Documentar algunos conceptos y principios básico en el manejo del lenguaje JavaScript para el uso de la plataforma de Google Earth Engine para los procesamientos en percepción remota
Objetivos específicoss
- Por medio de funciones básicas del lenguaje JavaScript realizar un tutorial que nos permita realizar algunos procesos de imágenes satelitales.
- Explicar por medio de ejemplos prácticos las diferentes líneas de códigos que conforman una función, un arreglo, condiciones y objetos para el análisis de imágenes satelitales.
Materiales y Métodos
El área de estudio incluyó el municipio de Sasaima, Cundinamarca, ubicado a una Latitud: Latitud: 4.967, Longitud: -74.433 4° 58′ 1″ Norte, 74° 25′ 59″ Oeste(Lucero & Becerra, 2018). Se utilizó archivo vectorial shapefile (.shp) descargados de los datos de cobertura de la tierra elaborados por IDEAM (http://www.ideam.gov.co/web/ecosistemas/coberturas-tierra) que usamos para acotar la información espacial del municipio de interés de un dataset ráster o un dataset de mosaico, este recorte (clip) permite extraer una porción de un dataset ráster en función de una extensión de plantilla. La salida de recorte incluye todos los píxeles que intersectan la extensión de plantilla (https://desktop.arcgis.com/es/arcmap/10.3/tools/data-management-toolbox/clip.htm).
Se utilizaron imágenes Sentinel-2 y Landsat-8. Sentinel-2 (S2) es una misión de imágenes multiespectrales de banda ancha, alta resolución y apoyo a los estudios (https://earth.esa.int/web/sentinel/user-guides/sentinel-2-msi/product-types/level-1c), S2 es un satélite de la ESA que cuenta con 13 bandas proporcionando imágenes de alta calidad radiométrica y excelente resolución espacial (10 y 20 m) ideal para trabajos de clasificación (Borràs et al., 2017). Por su parte, Landsat-8 lleva dos sensores, es decir, el Operational Land Imager (OLI) y el sensor térmico infrarrojo (TIRS) OLI recopila datos a una resolución espacial de 30 m con ocho bandas ubicadas en las regiones visible e infrarroja cercana y en las regiones infrarrojas de onda corta del espectro electromagnético, más un banda pancromática adicional a una resolución espacial de 15 m. mide la luminosidad TIR a una resolución espacial de 100 m utilizando dos bandas ubicadas en la ventana atmosférica entre 10 y 12 μm (Jiménez-muñoz et al., 2014). En la Tabla 1, se observan algunas propiedades de las imágenes Landsat-8 OLI y Sentinel-2.
Tabla 1. Tomado de Bar (2020), Información de bandas de Landsat-8 OLI y Sentinel-2 que se utilizan para realizar parches de incendios forestales (Bar, Parida, & Pandey, 2020)
Para el análisis de los datos y la preparación del presente tutorial se utilizó la plataforma Google Earth Engine (GEE) Code Editor API a partir de líneas de código con lenguaje de programación de JavaScript.
Matemáticas de las bandas, cálculo de la NDVI (Normalized Difference Vegetation Index)
Bran (2015) cita a diversos autores que afirman que el NDVI es uno de los más ampliamente utilizados dado que en diversos estudios se han determinado relaciones significativas entre este índice y aspectos claves de la estructura y funcionalidad de los ecosistemas, como el contenido de biomasa (Tucker et al., 1985), el índice de área foliar (Steltzer & Welker, 2006), la productividad primaria neta aérea (Paruelo et al.,1997) y la cobertura vegetal (Bran & Azcona, 2015).
Resultados
Cargamos lo feature collection de municipio de Colombia a la cual le aplicamos un filtro (filter), para poder visualizar nuestra área de interés que para este caso es el municipio de Sasaima, Cundinamarca. Posteriormente, conseguimos la unión de todas las geometrías para poder adjuntar la capa con las especificaciones y agregamos el nombre de la capa, declaramos geometría de rectángulo para abarcar el área de interés, adjuntamos la capa con las especificaciones de color y nombre de la capa, una vez allí, encontramos el polígono que cubre las extremidades del feature y agregamos la capa con las especificaciones de color y nombre, hallamos el área común de dos o más características, como se presenta en la imagen 1.

Imagen 1. Unión de geometrías a partir de la feature collection, donde se presenta: imagen 1A, área de visualización de GEE; 1B, shapefile de municipios de Colombia; 1C, área de interés del municipio de Sasaima; 1D, capa con las especificaciones del color; 1E, geometría de rectángulo para abarcar el área de interés; 1I, polígono que cubre las extremidades del feature.
La obtención de los resultados anteriormente mostrados se consiguió a partir de las siguientes líneas de código:
var municipios = ee.FeatureCollection("users/ohfrancom/municipios4326");
Map.addLayer(municipios);
Map.setCenter(-74.44, 4.96, 10);
var municsasaima = municipios.filter(ee.Filter.eq('MunNombre', 'SASAIMA'));
Map.addLayer(municsasaima);
var municsasaimadiss = municsasaima.union();
Map.addLayer(municsasaimadiss, {color: 'red'}, 'solo Sasaima');
var bound = municsasaimadiss.geometry().bounds();
Map.addLayer(bound, {color: 'yellow'}, 'Bounds');
var convex = municsasaimadiss.geometry().convexHull();
Map.addLayer(convex, {color: 'blue'}, 'Convex Hull');
var intersect = bound.intersection(convex, 100);
Map.addLayer(intersect, {color: 'green'}, 'Convex','Intersection');
var area = municsasaimadiss.geometry().area()
print ('El área del municipio de sasaima es =', ar_km2);
var ar_km2 = ar.divide(1000000);
Visualización de imágenes
Por medio de la plataforma GEE basada en la nube los usuarios podemos visualizar y analizar imágenes satelitales sin descargarlas a nuestra computadora con la capacidad de procesamiento en servidores propios de Google.
Para realizar el cargue de la imagen a la plataforma el usuario puede hacerlo por medio del código. El siguiente scrip nos ilustrar como se puede realizar:
var imagen = ee.Image('LANDSAT/LC08/C01/T1/LC08_008057_20180317');
print(imagen);
Map.addLayer(imagen,{min:5122, max:25102, bands:["B6","B5","B4"]});
Map.setCenter(-69.62, -15.919, 12);
Map.centerObject(imagen, 10);
Una vez realizados los pasos anteriormente corrido el código anteriormente expuesto podemos observar en la plataforma la información de la imagen en la consola de la plataforma gracias a la función “print”, igualmente podemos visualizar la imagen en el área de visualización de la plata forma por medio de la función “Map.addLayer” y centramos la visualización con la función “Map.setCenter” y “Map.centerObject”, como lo podemos observar en la imagen 2.

Imagen 2. ‘LANDSAT/LC08/C01/T1/LC08_008057_20180317’
Podemos realizar el cálculo del NDNI de tres formas diferentes como se muestra en los códigos a continuación:
Lo primero que tendremos que hacer, como siempre, será realizar una llamada a la imagen en la que estamos interesado y comenzar a declarar a nuestra imagen como una nueva variable. Podemos incorporar el ID de la imagen de manera directa si conoces su nomenclatura o identificándola en la plataforma.
#forma1
var ndvi1 = imagen.select('B5').subtract(imagen.select('B4'))
.divide(imagen.select('B5').add(imagen.select('B4')));
print(ndvi1);
Map.addLayer(ndvi1,{},'NDVI1');
#forma 2
var ndvi2 = imagen.normalizedDifference(['B5','B4']);
print(ndvi2);
ap.addLayer(ndvi2,{},'NDVI2');
#forma 3
var ndvi3 = imagen.expression('float(nir-red)/float(nir+red)', {
'nir':imagen.select('B5'),
'red':imagen.select('B4'),
});
print(ndvi3);
Con el cálculo de índice NDVI, índice que representa valores entre -1 y 1 de interpretación en la imagen 3, podemos clasificar la vegetación en función de su respuesta espectral y atenuar los detalles de otros elementos como el suelo. la imagen 4 presenta índices de NDVI que van entre 2 y >4, esto nos indica que los datos corresponden a áreas con vegetación escasa y vegetación moderada. De acuerdo con el mapa ecológico de Colombia, en el área de Sasaima se encuentran tres formaciones vegetales las cuales citan (Carlos & Flores, 2015) basados en la clasificación de Holdridge que a continuación se presentan:
Bosque húmedo premontaño (bh- PM) Localización. Aparece el bh- PM en parte de las tierra que constituyen la “zona cafetera” a lo largo de los repliegues cordilleranos de la franja occidental limitando con el bosque seco tropical del valle del Magdalena, y se ubica entre los 1.000 y 1.300 m.s.n.m.
Bosque muy húmedo premontano (bmh- PM) Localización: La mayoría de la “zona cafetera” del Municipio corresponde a esta formación que se extiende ampliamente por las laderas. Unas veces limita con el bosque húmedo tropical, en otras, es la prolongación muy húmeda del bh- PM, ya que es muy notorio el incremento de la lluvia hacia las cimas de las montañas.
Bosque húmedo montano bajo (Bh- Mb) Localización: Se encuentra rodeando el bs- MB de la Sabana de Bogotá, y en la parte baja el bosque muy húmedo premontano.

Imagen 3. Interpretación de NDVI.

Imagen 3A. NDVI en tono de grises; Imagen 3B. Interpretación de NDVI basado en la teoría sobre los códigos de colores HTML.
Colección de imágenes; Sentinel-2 y Landsat-8
Trabajo con colección de imágenes; Sentinel-2 e Landsat-8
realizar una llamada a la imagen en la que estamos interesado y comenzar a declarar a nuestra imagen como una nueva variable:
var IMGsentinel = ee.ImageCollection('COPERNICUS/S2').filterBounds(geometry);
Map.addLayer(IMGsentinel,{max:10000, min:0, bands:['B11','B8','B4']});
print(IMGsentinel)

Imagen 4. ImageCollection de Sentinel sobre el municipio de Sasaima

Imagen 5. Serie temporal de cada banda en todas las imágenes
Realizamos sorteo de acuerdo con el porcentaje de nubes, para lo cual aplicamos el siguiente bloque de Código:
var sorteo = IMGsentinel.sort('CLOUDY_PIXEL_PERCENTAJE');
var sentinel2 = ee.Image(sorteo.first());
Map.addLayer(sentinel2,{max:10000, min:0, bands:['B11','B8','B4']});
print(sentinel2);
Obtenemos 283 imágenes que están ordenadas e de forma ascendente donde el layer de la capa con menos porcentaje de nubes es el primero

Imagen 6. A la izquierda ImageCollection con nubes, a la derecha ImageCollection con filtro de nubes
Landsat: USGS Landsat 8 Collection 1 Tier 1 and Real-Time data TOA Reflectance
Traemos la imagen a partir del ID de la imagen (), va os a trabajar con colecciones de imágenes, hacemos una composición a verdadero color con las bandas B5, B4, B3 y posteriormente seleccionamos el área de interés con la función “geometry”.
Earth Engine proporciona una variedad de métodos convenientes para filtrar colecciones de imágenes. Específicamente, muchos casos de uso comunes son manejados por imageCollection.filterDate(), y imageCollection.filterBounds(). Para el filtrado de uso general, use imageCollection.filter()con un ee.Filtercomo argumento. El siguiente ejemplo muestra ambos métodos de conveniencia y filter() para identificar y eliminar imágenes con un registro incorrecto de un ImageCollection.
Una vez realizado esto observamos imágenes con nubes por lo cual aplicamos algunos filtros “filter” como Sorteo de acuerdo con el porcentaje de nubes, filtros por Geometría, filtro por tiempo, filtro por Metadata. Lo anterior lo aplicamos con el siguiente bloque de Código:
var IMGlandsat = ee. ImageCollection('LANDSAT/LC08/C01/T1_RT_TOA').filterBounds(geometry);
print(IMGlandsat);
Map.addLayer(IMGlandsat,{max:1, min:0, bands:['B6','B5','B4']});
var sorteo2 = IMGlandsat.sort('CLOUDY_PIXEL_PERCENTAJE');
var IMGlandsat2 = ee.Image(sorteo2.first());
Map.addLayer(IMGlandsat2,{max:1, min:0, bands:['B11','B8','B4']});

Imagen 7. Landsat composición B5, B4, B3; Imagen 7. Landsat composición B11, B8, B4
A continuación, generamos otras clases de filtros con las siguientes líneas de Código:
//filtro por tiempo
var IMGlandsat3 = ee. ImageCollection('LANDSAT/LC08/C01/T1_RT_TOA').filterBounds(geometry)
.filterDate('2017-12-01','2017-12-31');
//filtro por Metadata
var landsat = ee. ImageCollection('LANDSAT/LC08/C01/T1_RT_TOA').filterBounds(geometry)
.filterMetadata('CLOUD_COVER','equals','10');
Reducción de ImageCollection
Para reducir una ImageCollection, debemos usar imageCollection.reduce(). Esto reducirá todas las imágenes de la colección en una sola imagen que representa, por ejemplo, la desviación mínima, máxima, media o estándar de las imágenes. Para obtener más información sobre los reductores Imagen 8.

Con las siguientes líneas de código obtendremos las imágenes reducidas:
Aplicamos reducciones de mediana, media, mínima, máxima y una variante de mediana donde le aplicamos la mediana a cada banda Imagen 8
//Reducción
var landsatmediana = landsat.median();
var landsatmedia = landsat.mean();
var landsatmax = landsat.max();
var landsatmin = landsat.max();
var landsatmediana2 = landsat.reduce(ee.Reducer.median());
Llamamos las imagenes reducidas:
//reducir una colección de imágenes
print(landsat);
Map.centerObject(landsat, 10);
Map.addLayer(landsat,{max:1, min:0, bands:['B6','B5','B4']});
Map.addLayer(landsatmediana,{max:1, min:0, bands:['B6','B5','B4']}, 'L8 mediana');
Map.addLayer(landsatmedia,{max:1, min:0, bands:['B6','B5','B4']}, 'L8 media');
Map.addLayer(landsatmax,{max:1, min:0, bands:['B6','B5','B4']}, 'L8 max');
Map.addLayer(landsatmin,{max:1, min:0, bands:['B6','B5','B4']}, 'L8 min');
Map.addLayer(landsatmediana2,{max:1, min:0, bands:['B6_median','B5_median','B4_median']}, 'L8 mediana2');

Imagen 9. mediana, media, mínima, máxima y una variante de mediana
Discución y Conclusiones
- Se logró abordar de manera general el uso práctico del lenguaje JavaScript dentro de la plataforma GEE para procesamientos en percepción remota.
- Usando el lenguaje JavaScript se pudieron generar algunas funciones importantes en el procesamiento de imágenes que posteriormente podemos usar para investigaciones que apunten a la resolución de problemas de nuestra realidad. Se generaron funciones y arreglos importantes para el análisis de imágenes mediante diferentes líneas de código.
- La plataforma GEE no permite correr líneas de código de manera individual, esto dificulta la realización de algunos procesos.
- En general, GEE proporcionó un rendimiento muy bueno al permitir el acceso a productos de detección remota a través de la plataforma en la nube
Bibliografía
Bar, S., Parida, B. R., & Pandey, A. C. (2020). Remote Sensing Applications : Society and Environment Landsat-8 and Sentinel-2 based Forest fire burn area mapping using machine learning algorithms on GEE cloud platform over Uttarakhand , Western Himalaya. Remote Sensing Applications: Society and Environment, 18(March), 100324. https://doi.org/10.1016/j.rsase.2020.100324
Borràs, J., , Delegido, J., , Pezzola, A., , Pereira, M., , Morassi, G., & , Camps-Valls, G. (2017). Clasificación de usos del suelo a partir de imágenes Sentinel-2. Revista de Teledetección, 55–66.
Bran, D. E. D., & Azcona, C. (2015). Tendencia del NDVI en el período 2000-2014 como indicador de la degradación de tierras en Argentina : ventajas y limitaciones, 32(2), 83–93. Carlos, J., & Flores, R. (2015). MUNICIPIO DE SASAIMA ( Cundinamarca ) PLAN MUNICIPAL DE GESTIÓN DEL RIESGO DE Oficina de Medio Ambiente.
Gorelick, N., Hancher, M., Dixon, M., Ilyushchenko, S., Thau, D., & Moore, R. (2017). Google Earth Engine: Planetary-scale geospatial analysis for everyone. Remote Sensing of Environment, 202, 18–27. https://doi.org/10.1016/j.rse.2017.06.031
Jiménez-muñoz, J. C., Sobrino, J. A., Skokovi, D., Mattar, C., Cristóbal, J., & Bands, A. L.-T. (2014). Land Surface Temperature Retrieval Methods From Landsat-8 Thermal Infrared Sensor Data, 11(10), 1840–1843.
Krohn, H. S. (2019). Programación de buscadores en JavaScript para diccionarios digitales ∗, (fase 1), 109–130.
Lucero, S., & Becerra, C. (2018). Implementación de modelos espacio temporales para predecir la distribución de las enfermedades transmitidas por vectores en zonas críticas de Sasaima- Cundinamarca.
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCmdldHdkKCkNCmBgYA0KDQoNCiMjIyAqKkludHJvZHVjY2nDsm4qKg0KX19fX19fXw0KDQo8cCBzdHlsZT0ndGV4dC1hbGlnbjoganVzdGlmeTsnPiAgR29vZ2xlIEVhcnRoIEVuZ2luZSBlcyB1bmEgcGxhdGFmb3JtYSBiYXNhZGEgZW4gbGEgbnViZSBwYXJhIGVsIGFuw6FsaXNpcyBnZW9lc3BhY2lhbCBhIGVzY2FsYSBwbGFuZXRhcmlhIHF1ZSBvZnJlY2UgbGFzIGNhcGFjaWRhZGVzIGNvbXB1dGFjaW9uYWxlcyBtYXNpdmFzIGRlIEdvb2dsZSBwYXJhIGFib3JkYXIgdW5hIHZhcmllZGFkIGRlIHByb2JsZW1hcyBzb2NpYWxlcyBkZSBhbHRvIGltcGFjdG8sIGluY2x1eWVuZG8gZGVmb3Jlc3RhY2nDs24sIHNlcXXDrWEsIGRlc2FzdHJlcywgZW5mZXJtZWRhZGVzLCBzZWd1cmlkYWQgYWxpbWVudGFyaWEsIGdlc3Rpw7NuIGRlbCBhZ3VhLCBtb25pdG9yZW8gZGVsIGNsaW1hIHkgcHJvdGVjY2nDs24gZGVsIG1lZGlvIGFtYmllbnRlLiBFcyDDum5pY28gZW4gZWwgY2FtcG8gY29tbyB1bmEgcGxhdGFmb3JtYSBpbnRlZ3JhZGEgZGlzZcOxYWRhIHBhcmEgcG90ZW5jaWFyIG5vIHNvbG8gYSBsb3MgY2llbnTDrWZpY29zIGRlIHRlbGVkZXRlY2Npw7NuIHRyYWRpY2lvbmFsZXMgLCBzaW5vIHRhbWJpw6luIGEgdW4gcMO6YmxpY28gbXVjaG8gbcOhcyBhbXBsaW8gcXVlIGNhcmVjZSBkZSBsYSBjYXBhY2lkYWQgdMOpY25pY2EgbmVjZXNhcmlhIHBhcmEgdXRpbGl6YXIgc3VwZXJjb21wdXRhZG9yYXMgdHJhZGljaW9uYWxlcyBvIHJlY3Vyc29zIGRlIGNvbXB1dGFjacOzbiBlbiBsYSBudWJlIGRlIHByb2R1Y3RvcyBiw6FzaWNvcyBhIGdyYW4gZXNjYWxhIChHb3JlbGljayBldCBhbC4sIDIwMTcpLiA8L3A+DQoNCjxwIHN0eWxlPSd0ZXh0LWFsaWduOiBqdXN0aWZ5Oyc+IFZpZXJhICgyMDE3KSBjaXRhIGEgKFZlbnR1cmlubyBldCBhbC4sIDIwMTQpIHkgKEdvb2dsZSwgMjAxNikgcXVpZW5lcyBoYWNlbiByZWZlcmVuY2lhIGRlIHF1ZSBHb29nbGUgRWFydGggRW5naW5lIChHRUUpIGVzIHVuYSBwbGF0YWZvcm1hIGRlIGVzY2FsYSBwbGFuZXRhcmlhIHBhcmEgZWwgYW7DoWxpc2lzIGRlIGRhdG9zIG1lZGlvYW1iaWVudGFsZXMuIFJlw7puZSBtw6FzIGRlIDQwIGHDsW9zIGRlIGltw6FnZW5lcyBkZSBzYXTDqWxpdGUgZGUgdG9kbyBlbCBtdW5kbyBhY3R1YWxlcyBlIGhpc3TDs3JpY2FzLCB5IG9mcmVjZSBsYXMgaGVycmFtaWVudGFzIHkgbGEgcG90ZW5jaWEgY29tcHV0YWNpb25hbCBuZWNlc2FyaWFzIHBhcmEgYW5hbGl6YXIgeSBleHRyYWVyIGluZm9ybWFjacOzbiBkZSBlc3RlIGVub3JtZSBhbG1hY8OpbiBkZSBkYXRvcy4gRW50cmUgdW5hIGRlIHN1cyBhcGxpY2FjaW9uZXMgZXMgZGV0ZWNjacOzbiBkZSBjYW1iaW9zIGRlIGxhIGN1YmllcnRhIHRlcnJlc3RyZS4gR0VFIGVzIHVuYSB0ZWNub2xvZ8OtYSBwYXJhbGVsYSBtYXNpdmEgcGFyYSBlbCBwcm9jZXNhbWllbnRvIGRlIGFsdG8gcmVuZGltaWVudG8gZGUgbG9zIGRhdG9zIGdlb2VzcGFjaWFsZXMsIHkgYWxiZXJnYSB1bmEgY29waWEgZGUgdG9kbyBlbCBjYXTDoWxvZ28gZGUgaW3DoWdlbmVzIExhbmRzYXQgeSBvdHJhcyBpbcOhZ2VuZXMuIEVuIGVzdGUgaW5mb3JtZSB0dXRvcmlhbCBzZSB1dGlsaXphIEdFRSBjb21vIHVuYSBhbHRlcm5hdGl2YSBhIGxvcyBwcm9jZXNhbWllbnRvcyBkaWdpdGFsZXMgZGUgaW3DoWdlbmVzIChQREkpIGRvbmRlIHNlIGFib3JkYXJhIGVsIMOhcmVhIGNvbXB1ZXN0YSBwb3IgZWwgbXVuaWNpcGlvIGRlIFNhc2FpbWEgQ3VuZGluYW1hcmNhLiA8L3A+DQoNCjxwIHN0eWxlPSd0ZXh0LWFsaWduOiBqdXN0aWZ5Oyc+IEVzdGUgaW5mb3JtZSB0dXRvcmlhbCBlcyBoZXJyYW1pZW50YSBwYXJhIGVsIGFuw6FsaXNpcyBkZSBpbcOhZ2VuZXMgc2F0ZWxpdGFsZXMgcXVlIHNlIHB1ZWRhIHVzYXIgYSBwYXJ0aXIgZGUgbMOtbmVhcyBkZSBjw7NkaWdvIGNvbiBsZW5ndWFqZSBkZSBwcm9ncmFtYWNpw7NuIGRlIEphdmFTY3JpcHQgYSB0cmF2w6lzIGRlIGxhIHBsYXRhZm9ybWEgR29vZ2xlIEVhcnRoIEVuZ2luZSAoR0VFKSBDb2RlIEVkaXRvciBBUEkuIEphdmFTY3JpcHQgZXMgdW4gbGVuZ3VhamUgZGUgc2NyaXB0LCBjcmVhZG8gcG9yIEJyZW5kYW4gRWljaCBwYXJhIGVsIG5hdmVnYWRvciBOZXRzY2FwZSBlbiAxOTk1IGNvbiBlbCBmaW4gZGUgcG9zaWJpbGl0YXIgcMOhZ2luYXMgd2ViIG3DoXMgZGluw6FtaWNhcy4gSG95IGVuIGTDrWEgZXMgdW5vIGRlIGxvcyBsZW5ndWFqZXMgZGUgcHJvZ3JhbWFjacOzbiBtw6FzIGltcG9ydGFudGVzIGVuIGVsIG11bmRvIGRlIGxhIGluZm9ybcOhdGljYSwgeWEgcXVlIHNlIHV0aWxpemEgZW4gbGEgZ3JhbiBtYXlvcsOtYSBkZSBsb3Mgc2l0aW9zIHdlYiBtb2Rlcm5vcy4gVW5hIHZlbnRhamEgZGUgSmF2YVNjcmlwdCBlcyBxdWUgc2UgaW5jcnVzdGEgZsOhY2lsbWVudGUgZGVudHJvIGRlIEhUTUwsIGVsIGxlbmd1YWplIGRlIG1hcmNhZG8gZW1wbGVhZG8gcGFyYSBjb2RpZmljYXIgcMOhZ2luYXMgd2ViLiBBZGVtw6FzLCBjb21vIGFmaXJtYSBTdWJyYW1hbmlhbiwgZXMgdW5vIGRlIGxvcyBsZW5ndWFqZXMgZGUgcHJvZ3JhbWFjacOzbiBtw6FzIHBvZGVyb3NvcywgZXh0ZW5kaWRvcyB5IGZsZXhpYmxlcyBxdWUgZXhpc3RlbiBhY3R1YWxtZW50ZSAoS3JvaG4sIDIwMTkpLiBFbCBzaWd1aWVudGUgaW5mb3JtZSB0dXRvcmlhbCBzZSBoYWNlIGNvbiBlbCBvYmpldGl2byBkZSBlbnRlbmRlciBlbCBmdW5jaW9uYW1pZW50byBkZSBsYSBwbGF0YWZvcm1hIEdFRSwgYWxndW5hcyBsw61uZWFzIGRlIGPDs2RpZ28gYsOhc2ljYXMgcGFyYSBlbCBwcm9jZXNhbWllbnRvIGRlIGltw6FnZW5lcy48L3A+DQoNCl9fXw0KIyMjICoqUGFsYWJyYXMgY2xhdmVzOioqIEdFRSwgTGFuZHNhdCwgU2VudGluZWwsIE5EVkkuDQoNCl9fXw0KDQojIyMgKipPYmpldGl2byBnZW5lcmFsKioNCg0KPHAgc3R5bGU9J3RleHQtYWxpZ246IGp1c3RpZnk7Jz4gRG9jdW1lbnRhciBhbGd1bm9zIGNvbmNlcHRvcyB5IHByaW5jaXBpb3MgYsOhc2ljbyBlbiBlbCBtYW5lam8gZGVsIGxlbmd1YWplIEphdmFTY3JpcHQgcGFyYSBlbCB1c28gZGUgbGEgcGxhdGFmb3JtYSBkZSBHb29nbGUgRWFydGggRW5naW5lIHBhcmEgbG9zIHByb2Nlc2FtaWVudG9zIGVuIHBlcmNlcGNpw7NuIHJlbW90YSA8L3A+DQoNCiMjIyAqKk9iamV0aXZvcyBlc3BlY8OtZmljb3NzKioNCg0KPHAgc3R5bGU9J3RleHQtYWxpZ246IGp1c3RpZnk7Jz4gLSBQb3IgbWVkaW8gZGUgZnVuY2lvbmVzIGLDoXNpY2FzIGRlbCBsZW5ndWFqZSBKYXZhU2NyaXB0IHJlYWxpemFyIHVuIHR1dG9yaWFsIHF1ZSBub3MgcGVybWl0YSByZWFsaXphciBhbGd1bm9zIHByb2Nlc29zIGRlIGltw6FnZW5lcyBzYXRlbGl0YWxlcy4gDQotIEV4cGxpY2FyIHBvciBtZWRpbyBkZSBlamVtcGxvcyBwcsOhY3RpY29zIGxhcyBkaWZlcmVudGVzIGzDrW5lYXMgZGUgY8OzZGlnb3MgcXVlIGNvbmZvcm1hbiB1bmEgZnVuY2nDs24sIHVuIGFycmVnbG8sIGNvbmRpY2lvbmVzIHkgb2JqZXRvcyBwYXJhIGVsIGFuw6FsaXNpcyBkZSBpbcOhZ2VuZXMgc2F0ZWxpdGFsZXMuIDwvcD4NCg0KX19fX19fXw0KDQojIyMgKipNYXRlcmlhbGVzIHkgTcOpdG9kb3MqKg0KDQoNCjxwIHN0eWxlPSd0ZXh0LWFsaWduOiBqdXN0aWZ5Oyc+IEVsIMOhcmVhIGRlIGVzdHVkaW8gaW5jbHV5w7MgZWwgbXVuaWNpcGlvIGRlIFNhc2FpbWEsIEN1bmRpbmFtYXJjYSwgdWJpY2FkbyBhIHVuYSBMYXRpdHVkOiBMYXRpdHVkOiA0Ljk2NywgTG9uZ2l0dWQ6IC03NC40MzMgNMKwIDU44oCyIDHigLMgTm9ydGUsIDc0wrAgMjXigLIgNTnigLMgT2VzdGUoTHVjZXJvICYgQmVjZXJyYSwgMjAxOCkuIFNlIHV0aWxpesOzIGFyY2hpdm8gdmVjdG9yaWFsIHNoYXBlZmlsZSAoLnNocCkgZGVzY2FyZ2Fkb3MgZGUgbG9zIGRhdG9zIGRlIGNvYmVydHVyYSBkZSBsYSB0aWVycmEgZWxhYm9yYWRvcyBwb3IgSURFQU0gKGh0dHA6Ly93d3cuaWRlYW0uZ292LmNvL3dlYi9lY29zaXN0ZW1hcy9jb2JlcnR1cmFzLXRpZXJyYSkgcXVlIHVzYW1vcyBwYXJhIGFjb3RhciBsYSBpbmZvcm1hY2nDs24gZXNwYWNpYWwgZGVsIG11bmljaXBpbyBkZSBpbnRlcsOpcyAgZGUgdW4gZGF0YXNldCByw6FzdGVyIG8gdW4gZGF0YXNldCBkZSBtb3NhaWNvLCBlc3RlIHJlY29ydGUgKGNsaXApIHBlcm1pdGUgZXh0cmFlciB1bmEgcG9yY2nDs24gZGUgdW4gZGF0YXNldCByw6FzdGVyIGVuIGZ1bmNpw7NuIGRlIHVuYSBleHRlbnNpw7NuIGRlIHBsYW50aWxsYS4gTGEgc2FsaWRhIGRlIHJlY29ydGUgaW5jbHV5ZSB0b2RvcyBsb3MgcMOteGVsZXMgcXVlIGludGVyc2VjdGFuIGxhIGV4dGVuc2nDs24gZGUgcGxhbnRpbGxhIChodHRwczovL2Rlc2t0b3AuYXJjZ2lzLmNvbS9lcy9hcmNtYXAvMTAuMy90b29scy9kYXRhLW1hbmFnZW1lbnQtdG9vbGJveC9jbGlwLmh0bSkuPC9wPg0KDQo8cCBzdHlsZT0ndGV4dC1hbGlnbjoganVzdGlmeTsnPiBTZSB1dGlsaXphcm9uIGltw6FnZW5lcyBTZW50aW5lbC0yIHkgTGFuZHNhdC04LiBTZW50aW5lbC0yIChTMikgZXMgdW5hIG1pc2nDs24gZGUgaW3DoWdlbmVzIG11bHRpZXNwZWN0cmFsZXMgZGUgYmFuZGEgYW5jaGEsIGFsdGEgcmVzb2x1Y2nDs24geSBhcG95byBhIGxvcyBlc3R1ZGlvcyAoaHR0cHM6Ly9lYXJ0aC5lc2EuaW50L3dlYi9zZW50aW5lbC91c2VyLWd1aWRlcy9zZW50aW5lbC0yLW1zaS9wcm9kdWN0LXR5cGVzL2xldmVsLTFjKSwgUzIgZXMgdW4gc2F0w6lsaXRlIGRlIGxhIEVTQSBxdWUgY3VlbnRhIGNvbiAxMyBiYW5kYXMgcHJvcG9yY2lvbmFuZG8gaW3DoWdlbmVzIGRlIGFsdGEgY2FsaWRhZCByYWRpb23DqXRyaWNhIHkgZXhjZWxlbnRlIHJlc29sdWNpw7NuIGVzcGFjaWFsICgxMCB5IDIwIG0pIGlkZWFsIHBhcmEgdHJhYmFqb3MgZGUgY2xhc2lmaWNhY2nDs24gKEJvcnLDoHMgZXQgYWwuLCAyMDE3KS4gUG9yIHN1IHBhcnRlLCBMYW5kc2F0LTggbGxldmEgZG9zIHNlbnNvcmVzLCBlcyBkZWNpciwgZWwgT3BlcmF0aW9uYWwgTGFuZCBJbWFnZXIgKE9MSSkgeSBlbCBzZW5zb3IgdMOpcm1pY28gaW5mcmFycm9qbyAoVElSUykgT0xJIHJlY29waWxhIGRhdG9zIGEgdW5hIHJlc29sdWNpw7NuIGVzcGFjaWFsIGRlIDMwIG0gY29uIG9jaG8gYmFuZGFzIHViaWNhZGFzIGVuIGxhcyByZWdpb25lcyB2aXNpYmxlIGUgaW5mcmFycm9qYSBjZXJjYW5hIHkgZW4gbGFzIHJlZ2lvbmVzIGluZnJhcnJvamFzIGRlIG9uZGEgY29ydGEgZGVsIGVzcGVjdHJvIGVsZWN0cm9tYWduw6l0aWNvLCBtw6FzIHVuIGJhbmRhIHBhbmNyb23DoXRpY2EgYWRpY2lvbmFsIGEgdW5hIHJlc29sdWNpw7NuIGVzcGFjaWFsIGRlIDE1IG0uIG1pZGUgbGEgbHVtaW5vc2lkYWQgVElSIGEgdW5hIHJlc29sdWNpw7NuIGVzcGFjaWFsIGRlIDEwMCBtIHV0aWxpemFuZG8gZG9zIGJhbmRhcyB1YmljYWRhcyBlbiBsYSB2ZW50YW5hIGF0bW9zZsOpcmljYSBlbnRyZSAxMCB5IDEyIM68bSAoSmltw6luZXotbXXDsW96IGV0IGFsLiwgMjAxNCkuIEVuIGxhICoqVGFibGEgMSoqLCBzZSBvYnNlcnZhbiBhbGd1bmFzIHByb3BpZWRhZGVzIGRlIGxhcyBpbcOhZ2VuZXMgTGFuZHNhdC04IE9MSSB5IFNlbnRpbmVsLTIuPC9wPg0KDQohW1Byb3BpZWRhZGVzXShDOlxwZXJjZXBjaW9ucmVtb3RhXFBSXEluZm9ybWUyX0dFRS9zZW50bGFuZC5KUEcpDQoNCjxwIHN0eWxlPSd0ZXh0LWFsaWduOiBqdXN0aWZ5Oyc+ICoqVGFibGEgMSoqLiBUb21hZG8gZGUgQmFyICgyMDIwKSwgSW5mb3JtYWNpw7NuIGRlIGJhbmRhcyBkZSBMYW5kc2F0LTggT0xJIHkgU2VudGluZWwtMiBxdWUgc2UgdXRpbGl6YW4gcGFyYSByZWFsaXphciBwYXJjaGVzIGRlIGluY2VuZGlvcyBmb3Jlc3RhbGVzIChCYXIsIFBhcmlkYSwgJiBQYW5kZXksIDIwMjApIDxwIHN0eWxlPSd0ZXh0LWFsaWduOiBqdXN0aWZ5Oyc+DQoNCjxwIHN0eWxlPSd0ZXh0LWFsaWduOiBqdXN0aWZ5Oyc+IFBhcmEgZWwgYW7DoWxpc2lzIGRlIGxvcyBkYXRvcyB5IGxhIHByZXBhcmFjacOzbiBkZWwgcHJlc2VudGUgdHV0b3JpYWwgc2UgdXRpbGl6w7MgbGEgcGxhdGFmb3JtYSBHb29nbGUgRWFydGggRW5naW5lIChHRUUpIENvZGUgRWRpdG9yIEFQSSBhIHBhcnRpciBkZSBsw61uZWFzIGRlIGPDs2RpZ28gY29uIGxlbmd1YWplIGRlIHByb2dyYW1hY2nDs24gZGUgSmF2YVNjcmlwdC4gPC9wPg0KDQoNCiMjIyMgKipNYXRlbcOhdGljYXMgZGUgbGFzIGJhbmRhcywgY8OhbGN1bG8gZGUgbGEgTkRWSSAoTm9ybWFsaXplZCBEaWZmZXJlbmNlIFZlZ2V0YXRpb24gSW5kZXgpKioNCg0KPHAgc3R5bGU9J3RleHQtYWxpZ246IGp1c3RpZnk7Jz4gQnJhbiAoMjAxNSkgY2l0YSBhIGRpdmVyc29zIGF1dG9yZXMgcXVlIGFmaXJtYW4gcXVlIGVsIE5EVkkgZXMgdW5vIGRlIGxvcyBtw6FzIGFtcGxpYW1lbnRlIHV0aWxpemFkb3MgZGFkbyBxdWUgZW4gZGl2ZXJzb3MgZXN0dWRpb3Mgc2UgaGFuIGRldGVybWluYWRvIHJlbGFjaW9uZXMgc2lnbmlmaWNhdGl2YXMgZW50cmUgZXN0ZSDDrW5kaWNlIHkgYXNwZWN0b3MgY2xhdmVzIGRlIGxhIGVzdHJ1Y3R1cmEgeSBmdW5jaW9uYWxpZGFkIGRlIGxvcyBlY29zaXN0ZW1hcywgY29tbyBlbCBjb250ZW5pZG8gZGUgYmlvbWFzYSAoVHVja2VyIGV0IGFsLiwgMTk4NSksIGVsIMOtbmRpY2UgZGUgw6FyZWEgZm9saWFyIChTdGVsdHplciAmIFdlbGtlciwgMjAwNiksIGxhIHByb2R1Y3RpdmlkYWQgcHJpbWFyaWEgbmV0YSBhw6lyZWEgKFBhcnVlbG8gZXQgYWwuLDE5OTcpIHkgbGEgY29iZXJ0dXJhIHZlZ2V0YWwgKEJyYW4gJiBBemNvbmEsIDIwMTUpLiA8L3A+DQoNCiMjIyMgRW4gZXN0ZSB0dXRvcmlhbCBzZSBwcmVzZW50YW4gdHJlcyBmb3JtYXMgcGFyYSByZWFsaXphciBlbCBjw6FsY3VsbyBkZWwgTkROSSB1c2FuZG8gbGEgc2lndWllbnRlIGZvcm11bGE6DQoNCiFbTkRWSV0oQzpccGVyY2VwY2lvbnJlbW90YVxQUlxJbmZvcm1lMl9HRUUvTkRWSS5KUEcpDQoNCg0KPHAgc3R5bGU9J3RleHQtYWxpZ246IGp1c3RpZnk7Jz4gRXMgZGVjaXIsIG1lZGlhbnRlIGxhIGRpZmVyZW5jaWEgZW50cmUgbGEgcmVmbGVjdGFuY2lhIGRlIGxhcyBiYW5kYXMgNCAoaW5mcmFycm9qbyBjZXJjYW5vKSB5IDMgKHZpc2libGUg4oCTIHJvam8pIGRpdmlkaWRvIHBvciBsYSBzdW1hIGRlIGVzdGFzIGRvcyBiYW5kYXMgZGUgcmVmbGVjdGFuY2lhLiBFbiBsYXMgaW3DoWdlbmVzIExhbmRzYXQgOCwgbGEgYmFuZGEgNCAoMC42MzAg4oCTIDAuNjgwIMK1bSkgY29ycmVzcG9uZGUgYWwgcm9qbyAoUikgeSBsYSBiYW5kYSA1ICgwLjg0NSDigJMgMC44ODUgwrVtKSBhbCBpbmZyYXJyb2pvIChJUkMpLCBwb3IgbG8gdGFudG8gDQpwYXJhIGVsIGPDoWxjdWxvIE5EVkkgc2UgcmVxdWllcmUgY29udGFyIGNvbiBhbWJhcyBiYW5kYXMuIDwvcD4NCg0KKioqDQoNCiMjIyMgKipBcGxpY2FjacOzbiBkZSBmaWx0cm8gbWVkaWFudGUgbGEgZnVuY2nDs24gRklMVEVSKioNCl9fX19fX18NCg0KLSBTb3J0ZW8gZGUgYWN1ZXJkbyBjb24gZWwgcG9yY2VudGFqZSBkZSBudWJlcyA8L3A+DQotIEZpbHRyb3MgcG9yIEdlb21ldHLDrWEgPC9wPg0KLSBGaWx0cm8gcG9yIHRpZW1wbyA8L3A+DQotIEZpbHRybyBwb3IgTWV0YWRhdGEgPC9wPg0KLSBSZWR1Y2Npw7NuIDwvcD4NCg0KX19fX19fXw0KDQojIyMgKipSZXN1bHRhZG9zKioNCl9fX19fX18NCg0KPHAgc3R5bGU9J3RleHQtYWxpZ246IGp1c3RpZnk7Jz4gQ2FyZ2Ftb3MgbG8gZmVhdHVyZSBjb2xsZWN0aW9uIGRlIG11bmljaXBpbyBkZSBDb2xvbWJpYSBhIGxhIGN1YWwgbGUgYXBsaWNhbW9zIHVuIGZpbHRybyAoZmlsdGVyKSwgcGFyYSBwb2RlciB2aXN1YWxpemFyIG51ZXN0cmEgw6FyZWEgZGUgaW50ZXLDqXMgcXVlIHBhcmEgZXN0ZSBjYXNvIGVzIGVsIG11bmljaXBpbyBkZSBTYXNhaW1hLCBDdW5kaW5hbWFyY2EuIFBvc3Rlcmlvcm1lbnRlLCBjb25zZWd1aW1vcyBsYSB1bmnDs24gZGUgdG9kYXMgbGFzIGdlb21ldHLDrWFzIHBhcmEgcG9kZXIgYWRqdW50YXIgbGEgY2FwYSBjb24gbGFzIGVzcGVjaWZpY2FjaW9uZXMgeSBhZ3JlZ2Ftb3MgZWwgbm9tYnJlIGRlIGxhIGNhcGEsIGRlY2xhcmFtb3MgZ2VvbWV0csOtYSBkZSByZWN0w6FuZ3VsbyBwYXJhIGFiYXJjYXIgZWwgw6FyZWEgZGUgaW50ZXLDqXMsIGFkanVudGFtb3MgbGEgY2FwYSBjb24gbGFzIGVzcGVjaWZpY2FjaW9uZXMgZGUgY29sb3IgeSBub21icmUgZGUgbGEgY2FwYSwgdW5hIHZleiBhbGzDrSwgZW5jb250cmFtb3MgZWwgcG9sw61nb25vIHF1ZSBjdWJyZSBsYXMgZXh0cmVtaWRhZGVzIGRlbCBmZWF0dXJlIHkgYWdyZWdhbW9zIGxhIGNhcGEgY29uIGxhcyBlc3BlY2lmaWNhY2lvbmVzIGRlIGNvbG9yIHkgbm9tYnJlLCBoYWxsYW1vcyBlbCDDoXJlYSBjb23Dum4gZGUgZG9zIG8gbcOhcyBjYXJhY3RlcsOtc3RpY2FzLCBjb21vIHNlIHByZXNlbnRhIGVuIGxhICoqaW1hZ2VuIDEqKi4gICA8L3A+DQoNCiFbXShDOlxwZXJjZXBjaW9ucmVtb3RhXFBSXEluZm9ybWUyX0dFRS9GZWF0dXJlY2xhc3MuSlBHKQ0KDQojIyMjIyMgKipJbWFnZW4gMS4qKiBVbmnDs24gZGUgZ2VvbWV0csOtYXMgYSBwYXJ0aXIgZGUgbGEgZmVhdHVyZSBjb2xsZWN0aW9uLCBkb25kZSBzZSBwcmVzZW50YTogKippbWFnZW4gMUEqKiwgw6FyZWEgZGUgdmlzdWFsaXphY2nDs24gZGUgR0VFOyAqKjFCKiosIHNoYXBlZmlsZSBkZSBtdW5pY2lwaW9zIGRlIENvbG9tYmlhOyAqKjFDKiosIMOhcmVhIGRlIGludGVyw6lzIGRlbCBtdW5pY2lwaW8gZGUgU2FzYWltYTsgKioxRCoqLCBjYXBhIGNvbiBsYXMgZXNwZWNpZmljYWNpb25lcyBkZWwgY29sb3I7IDFFLCBnZW9tZXRyw61hIGRlIHJlY3TDoW5ndWxvIHBhcmEgYWJhcmNhciBlbCDDoXJlYSBkZSBpbnRlcsOpczsgKioxSSoqLCBwb2zDrWdvbm8gcXVlIGN1YnJlIGxhcyBleHRyZW1pZGFkZXMgZGVsIGZlYXR1cmUuIA0KDQo8cCBzdHlsZT0ndGV4dC1hbGlnbjoganVzdGlmeTsnPiBMYSBvYnRlbmNpw7NuIGRlIGxvcyByZXN1bHRhZG9zIGFudGVyaW9ybWVudGUgbW9zdHJhZG9zIHNlIGNvbnNpZ3Vpw7MgYSBwYXJ0aXIgZGUgbGFzIHNpZ3VpZW50ZXMgbMOtbmVhcyBkZSBjw7NkaWdvOiA8L3A+DQoNCmBgYHtyfQ0KdmFyIG11bmljaXBpb3MgPSBlZS5GZWF0dXJlQ29sbGVjdGlvbigidXNlcnMvb2hmcmFuY29tL211bmljaXBpb3M0MzI2Iik7DQpNYXAuYWRkTGF5ZXIobXVuaWNpcGlvcyk7DQpNYXAuc2V0Q2VudGVyKC03NC40NCwgNC45NiwgMTApOw0KdmFyIG11bmljc2FzYWltYSA9IG11bmljaXBpb3MuZmlsdGVyKGVlLkZpbHRlci5lcSgnTXVuTm9tYnJlJywgJ1NBU0FJTUEnKSk7DQpNYXAuYWRkTGF5ZXIobXVuaWNzYXNhaW1hKTsNCnZhciBtdW5pY3Nhc2FpbWFkaXNzID0gbXVuaWNzYXNhaW1hLnVuaW9uKCk7DQpNYXAuYWRkTGF5ZXIobXVuaWNzYXNhaW1hZGlzcywge2NvbG9yOiAncmVkJ30sICdzb2xvIFNhc2FpbWEnKTsNCnZhciBib3VuZCA9IG11bmljc2FzYWltYWRpc3MuZ2VvbWV0cnkoKS5ib3VuZHMoKTsNCk1hcC5hZGRMYXllcihib3VuZCwge2NvbG9yOiAneWVsbG93J30sICdCb3VuZHMnKTsNCnZhciBjb252ZXggPSBtdW5pY3Nhc2FpbWFkaXNzLmdlb21ldHJ5KCkuY29udmV4SHVsbCgpOw0KTWFwLmFkZExheWVyKGNvbnZleCwge2NvbG9yOiAnYmx1ZSd9LCAnQ29udmV4IEh1bGwnKTsNCnZhciBpbnRlcnNlY3QgPSBib3VuZC5pbnRlcnNlY3Rpb24oY29udmV4LCAxMDApOw0KTWFwLmFkZExheWVyKGludGVyc2VjdCwge2NvbG9yOiAnZ3JlZW4nfSwgJ0NvbnZleCcsJ0ludGVyc2VjdGlvbicpOw0KdmFyIGFyZWEgPSBtdW5pY3Nhc2FpbWFkaXNzLmdlb21ldHJ5KCkuYXJlYSgpDQpwcmludCAoJ0VsIMOhcmVhIGRlbCBtdW5pY2lwaW8gZGUgc2FzYWltYSBlcyA9JywgYXJfa20yKTsNCnZhciBhcl9rbTIgPSBhci5kaXZpZGUoMTAwMDAwMCk7DQoNCmBgYA0KIyMjICoqVmlzdWFsaXphY2nDs24gZGUgaW3DoWdlbmVzKioNCl9fX19fX18NCg0KPHAgc3R5bGU9J3RleHQtYWxpZ246IGp1c3RpZnk7Jz4gUG9yIG1lZGlvIGRlIGxhIHBsYXRhZm9ybWEgR0VFIGJhc2FkYSBlbiBsYSBudWJlIGxvcyB1c3VhcmlvcyBwb2RlbW9zIHZpc3VhbGl6YXIgeSBhbmFsaXphciBpbcOhZ2VuZXMgc2F0ZWxpdGFsZXMgc2luIGRlc2NhcmdhcmxhcyBhIG51ZXN0cmEgY29tcHV0YWRvcmEgY29uIGxhIGNhcGFjaWRhZCBkZSBwcm9jZXNhbWllbnRvIGVuIHNlcnZpZG9yZXMgcHJvcGlvcyBkZSBHb29nbGUuIDwvcD4NCg0KPHAgc3R5bGU9J3RleHQtYWxpZ246IGp1c3RpZnk7Jz4gUGFyYSByZWFsaXphciBlbCBjYXJndWUgZGUgbGEgaW1hZ2VuIGEgbGEgcGxhdGFmb3JtYSBlbCB1c3VhcmlvIHB1ZWRlIGhhY2VybG8gcG9yIG1lZGlvIGRlbCBjw7NkaWdvLiBFbCBzaWd1aWVudGUgc2NyaXAgbm9zIGlsdXN0cmFyIGNvbW8gc2UgcHVlZGUgcmVhbGl6YXI6IDwvcD4NCg0KYGBge3J9DQp2YXIgaW1hZ2VuID0gZWUuSW1hZ2UoJ0xBTkRTQVQvTEMwOC9DMDEvVDEvTEMwOF8wMDgwNTdfMjAxODAzMTcnKTsNCnByaW50KGltYWdlbik7DQpNYXAuYWRkTGF5ZXIoaW1hZ2VuLHttaW46NTEyMiwgbWF4OjI1MTAyLCBiYW5kczpbIkI2IiwiQjUiLCJCNCJdfSk7DQpNYXAuc2V0Q2VudGVyKC02OS42MiwgLTE1LjkxOSwgMTIpOw0KTWFwLmNlbnRlck9iamVjdChpbWFnZW4sIDEwKTsNCg0KYGBgDQo8cCBzdHlsZT0ndGV4dC1hbGlnbjoganVzdGlmeTsnPiBVbmEgdmV6IHJlYWxpemFkb3MgbG9zIHBhc29zIGFudGVyaW9ybWVudGUgY29ycmlkbyBlbCBjw7NkaWdvIGFudGVyaW9ybWVudGUgZXhwdWVzdG8gcG9kZW1vcyBvYnNlcnZhciBlbiBsYSBwbGF0YWZvcm1hIGxhIGluZm9ybWFjacOzbiBkZSBsYSBpbWFnZW4gZW4gbGEgY29uc29sYSBkZSBsYSBwbGF0YWZvcm1hIGdyYWNpYXMgYSBsYSBmdW5jacOzbiBf4oCccHJpbnTigJ1fLCBpZ3VhbG1lbnRlIHBvZGVtb3MgdmlzdWFsaXphciBsYSBpbWFnZW4gZW4gZWwgw6FyZWEgZGUgdmlzdWFsaXphY2nDs24gZGUgbGEgcGxhdGEgZm9ybWEgcG9yIG1lZGlvIGRlIGxhIGZ1bmNpw7NuIF/igJxNYXAuYWRkTGF5ZXLigJ1fIHkgY2VudHJhbW9zIGxhIHZpc3VhbGl6YWNpw7NuIGNvbiBsYSBmdW5jacOzbiBfIk1hcC5zZXRDZW50ZXIiXyB5IF8iTWFwLmNlbnRlck9iamVjdCJfLCBjb21vIGxvIHBvZGVtb3Mgb2JzZXJ2YXIgZW4gbGEgKippbWFnZW4gMioqLiAgPC9wPg0KDQohW10oQzpccGVyY2VwY2lvbnJlbW90YVxQUlxJbmZvcm1lMl9HRUUvbGFuZHNhdC5KUEcpDQoNCiMjIyMjIyAqKkltYWdlbiAyKiouICdMQU5EU0FUL0xDMDgvQzAxL1QxL0xDMDhfMDA4MDU3XzIwMTgwMzE3Jw0KDQo8cCBzdHlsZT0ndGV4dC1hbGlnbjoganVzdGlmeTsnPiBQb2RlbW9zIHJlYWxpemFyIGVsIGPDoWxjdWxvIGRlbCBORE5JIGRlIHRyZXMgZm9ybWFzIGRpZmVyZW50ZXMgY29tbyBzZSBtdWVzdHJhIGVuIGxvcyBjw7NkaWdvcyBhIGNvbnRpbnVhY2nDs246IDwvcD4NCg0KPHAgc3R5bGU9J3RleHQtYWxpZ246IGp1c3RpZnk7Jz4gTG8gcHJpbWVybyBxdWUgdGVuZHJlbW9zIHF1ZSBoYWNlciwgY29tbyBzaWVtcHJlLCBzZXLDoSByZWFsaXphciB1bmEgbGxhbWFkYSBhIGxhIGltYWdlbiBlbiBsYSBxdWUgZXN0YW1vcyBpbnRlcmVzYWRvIHkgY29tZW56YXIgYSBkZWNsYXJhciBhIG51ZXN0cmEgaW1hZ2VuIGNvbW8gdW5hIG51ZXZhIHZhcmlhYmxlLiBQb2RlbW9zIGluY29ycG9yYXIgZWwgSUQgZGUgbGEgaW1hZ2VuIGRlIG1hbmVyYSBkaXJlY3RhIHNpIGNvbm9jZXMgc3Ugbm9tZW5jbGF0dXJhIG8gaWRlbnRpZmljw6FuZG9sYSBlbiBsYSBwbGF0YWZvcm1hLiA8L3A+DQoNCmBgYHtyfQ0KI2Zvcm1hMQ0KdmFyIG5kdmkxID0gaW1hZ2VuLnNlbGVjdCgnQjUnKS5zdWJ0cmFjdChpbWFnZW4uc2VsZWN0KCdCNCcpKQ0KICAgICAgICAgICAgLmRpdmlkZShpbWFnZW4uc2VsZWN0KCdCNScpLmFkZChpbWFnZW4uc2VsZWN0KCdCNCcpKSk7DQogICAgICAgICAgICANCnByaW50KG5kdmkxKTsNCk1hcC5hZGRMYXllcihuZHZpMSx7fSwnTkRWSTEnKTsNCiAgICAgIA0KI2Zvcm1hIDINCnZhciBuZHZpMiA9IGltYWdlbi5ub3JtYWxpemVkRGlmZmVyZW5jZShbJ0I1JywnQjQnXSk7DQpwcmludChuZHZpMik7DQphcC5hZGRMYXllcihuZHZpMix7fSwnTkRWSTInKTsNCiAgICAgICAgICAgIA0KI2Zvcm1hIDMNCnZhciBuZHZpMyA9IGltYWdlbi5leHByZXNzaW9uKCdmbG9hdChuaXItcmVkKS9mbG9hdChuaXIrcmVkKScsIHsNCiAgICAgICAgICAgICduaXInOmltYWdlbi5zZWxlY3QoJ0I1JyksDQogICAgICAgICAgICAncmVkJzppbWFnZW4uc2VsZWN0KCdCNCcpLA0KfSk7DQoNCnByaW50KG5kdmkzKTsNCmBgYA0KDQo8cCBzdHlsZT0ndGV4dC1hbGlnbjoganVzdGlmeTsnPiBDb24gZWwgY8OhbGN1bG8gZGUgw61uZGljZSBORFZJLCDDrW5kaWNlIHF1ZSByZXByZXNlbnRhIHZhbG9yZXMgZW50cmUgLTEgeSAxIGRlIGludGVycHJldGFjacOzbiBlbiBsYSAqKmltYWdlbiAzKiosIHBvZGVtb3MgY2xhc2lmaWNhciBsYSB2ZWdldGFjacOzbiBlbiBmdW5jacOzbiBkZSBzdSByZXNwdWVzdGEgZXNwZWN0cmFsIHkgYXRlbnVhciBsb3MgZGV0YWxsZXMgZGUgb3Ryb3MgZWxlbWVudG9zIGNvbW8gZWwgc3VlbG8uIGxhICoqaW1hZ2VuIDQqKiBwcmVzZW50YSDDrW5kaWNlcyBkZSBORFZJIHF1ZSB2YW4gZW50cmUgMiB5ID40LCBlc3RvIG5vcyBpbmRpY2EgcXVlIGxvcyBkYXRvcyBjb3JyZXNwb25kZW4gYSDDoXJlYXMgY29uIHZlZ2V0YWNpw7NuIGVzY2FzYSB5IHZlZ2V0YWNpw7NuIG1vZGVyYWRhLiBEZSBhY3VlcmRvIGNvbiBlbCBtYXBhIGVjb2zDs2dpY28gZGUgQ29sb21iaWEsIGVuIGVsIMOhcmVhIGRlIFNhc2FpbWEgc2UgZW5jdWVudHJhbiB0cmVzIGZvcm1hY2lvbmVzIHZlZ2V0YWxlcyBsYXMgY3VhbGVzIGNpdGFuIChDYXJsb3MgJiBGbG9yZXMsIDIwMTUpIGJhc2Fkb3MgZW4gbGEgY2xhc2lmaWNhY2nDs24gZGUgSG9sZHJpZGdlIHF1ZSBhIGNvbnRpbnVhY2nDs24gc2UgcHJlc2VudGFuOjwvcD4NCg0KPHAgc3R5bGU9J3RleHQtYWxpZ246IGp1c3RpZnk7Jz4gQm9zcXVlIGjDum1lZG8gcHJlbW9udGHDsW8gKGJoLSBQTSkgTG9jYWxpemFjacOzbi4gQXBhcmVjZSBlbCBiaC0gUE0gZW4gcGFydGUgZGUgbGFzIHRpZXJyYSBxdWUgY29uc3RpdHV5ZW4gbGEg4oCcem9uYSBjYWZldGVyYeKAnSBhIGxvIGxhcmdvIGRlIGxvcyByZXBsaWVndWVzIGNvcmRpbGxlcmFub3MgZGUgbGEgZnJhbmphIG9jY2lkZW50YWwgbGltaXRhbmRvIGNvbiBlbCBib3NxdWUgc2VjbyB0cm9waWNhbCBkZWwgdmFsbGUgZGVsIE1hZ2RhbGVuYSwgeSBzZSB1YmljYSBlbnRyZSBsb3MgMS4wMDAgeSAxLjMwMCBtLnMubi5tLiA8L3A+DQoNCjxwIHN0eWxlPSd0ZXh0LWFsaWduOiBqdXN0aWZ5Oyc+IEJvc3F1ZSBtdXkgaMO6bWVkbyBwcmVtb250YW5vIChibWgtIFBNKSBMb2NhbGl6YWNpw7NuOiBMYSBtYXlvcsOtYSBkZSBsYSDigJx6b25hIGNhZmV0ZXJh4oCdIGRlbCBNdW5pY2lwaW8gY29ycmVzcG9uZGUgYSBlc3RhIGZvcm1hY2nDs24gcXVlIHNlIGV4dGllbmRlIGFtcGxpYW1lbnRlIHBvciBsYXMgbGFkZXJhcy4gVW5hcyB2ZWNlcyBsaW1pdGEgY29uIGVsIGJvc3F1ZSBow7ptZWRvIHRyb3BpY2FsLCBlbiBvdHJhcywgZXMgbGEgcHJvbG9uZ2FjacOzbiBtdXkgaMO6bWVkYSBkZWwgYmgtIFBNLCB5YSBxdWUgZXMgbXV5IG5vdG9yaW8gZWwgaW5jcmVtZW50byBkZSBsYSBsbHV2aWEgaGFjaWEgbGFzIGNpbWFzIGRlIGxhcyBtb250YcOxYXMuPC9wPg0KDQo8cCBzdHlsZT0ndGV4dC1hbGlnbjoganVzdGlmeTsnPiBCb3NxdWUgaMO6bWVkbyBtb250YW5vIGJham8gKEJoLSBNYikgTG9jYWxpemFjacOzbjogU2UgZW5jdWVudHJhIHJvZGVhbmRvIGVsIGJzLSBNQiBkZSBsYSBTYWJhbmEgZGUgQm9nb3TDoSwgeSBlbiBsYSBwYXJ0ZSBiYWphIGVsIGJvc3F1ZSBtdXkgaMO6bWVkbyBwcmVtb250YW5vLiA8L3A+DQoNCg0KIVtdKEM6XHBlcmNlcGNpb25yZW1vdGFcUFJcSW5mb3JtZTJfR0VFL2ludGVybmR2aS5KUEcpDQoNCiMjIyMjIyAqKkltYWdlbiAzLioqIEludGVycHJldGFjacOzbiBkZSBORFZJLiANCg0KDQohW10oQzpccGVyY2VwY2lvbnJlbW90YVxQUlxJbmZvcm1lMl9HRUUvTkRWSTMuSlBHKQ0KDQojIyMjIyMgKipJbWFnZW4gM0EqKi4gTkRWSSBlbiB0b25vIGRlIGdyaXNlczsgKipJbWFnZW4gM0IuKiogSW50ZXJwcmV0YWNpw7NuIGRlIE5EVkkgYmFzYWRvIGVuIGxhIHRlb3LDrWEgc29icmUgbG9zIGPDs2RpZ29zIGRlIGNvbG9yZXMgSFRNTC4NCg0KIyMjICoqQ29sZWNjacOzbiBkZSBpbcOhZ2VuZXM7IFNlbnRpbmVsLTIgeSBMYW5kc2F0LTgqKg0KX19fX19fXw0KDQo8cCBzdHlsZT0ndGV4dC1hbGlnbjoganVzdGlmeTsnPiBUcmFiYWpvIGNvbiBjb2xlY2Npw7NuIGRlIGltw6FnZW5lczsgU2VudGluZWwtMiBlIExhbmRzYXQtOCA8L3A+DQo8cCBzdHlsZT0ndGV4dC1hbGlnbjoganVzdGlmeTsnPnJlYWxpemFyIHVuYSBsbGFtYWRhIGEgbGEgaW1hZ2VuIGVuIGxhIHF1ZSBlc3RhbW9zIGludGVyZXNhZG8geSBjb21lbnphciBhIGRlY2xhcmFyIGEgbnVlc3RyYSBpbWFnZW4gY29tbyB1bmEgbnVldmEgdmFyaWFibGU6IDwvcD4NCg0KYGBge3J9DQp2YXIgSU1Hc2VudGluZWwgPSBlZS5JbWFnZUNvbGxlY3Rpb24oJ0NPUEVSTklDVVMvUzInKS5maWx0ZXJCb3VuZHMoZ2VvbWV0cnkpOw0KTWFwLmFkZExheWVyKElNR3NlbnRpbmVsLHttYXg6MTAwMDAsIG1pbjowLCBiYW5kczpbJ0IxMScsJ0I4JywnQjQnXX0pOw0KcHJpbnQoSU1Hc2VudGluZWwpIA0KYGBgDQoNCg0KIVtdKEM6XHBlcmNlcGNpb25yZW1vdGFcUFJcSW5mb3JtZTJfR0VFL2NvbGV0c2VudGkuSlBHKQ0KDQojIyMjIyMgKipJbWFnZW4gNC4qKiBJbmZvcm1hY2nDs24gcXVlIHNlIHByZXNlbnRhIGVuIGxhIGNvbnNvbGEgY29uIGVsIG7Dum1lcm8gZGUgaW3DoWdlbmVzIHF1ZSBzZSBoYSBsbGFtYWRvLCBsYXMgY29vcmRlbmFkYXMgZG9uZGUgc2UgaGEgY2VudHJhZG8sIHkgZWwgbsO6bWVybyBkZSBiYW5kYXMuDQoNCiFbXShDOlxwZXJjZXBjaW9ucmVtb3RhXFBSXEluZm9ybWUyX0dFRS9zZW50aW5lbC5KUEcpDQoNCiMjIyMjIyAqKkltYWdlbiA0LioqIEltYWdlQ29sbGVjdGlvbiBkZSBTZW50aW5lbCBzb2JyZSBlbCBtdW5pY2lwaW8gZGUgU2FzYWltYQ0KDQohW10oQzpccGVyY2VwY2lvbnJlbW90YVxQUlxJbmZvcm1lMl9HRUUvdGVtcC5KUEcpDQoNCiMjIyMjIyAqKkltYWdlbiA1LioqIFNlcmllIHRlbXBvcmFsIGRlIGNhZGEgYmFuZGEgZW4gdG9kYXMgbGFzIGltw6FnZW5lcw0KDQo8cCBzdHlsZT0ndGV4dC1hbGlnbjoganVzdGlmeTsnPiBSZWFsaXphbW9zIHNvcnRlbyBkZSBhY3VlcmRvIGNvbiBlbCBwb3JjZW50YWplIGRlIG51YmVzLCBwYXJhIGxvIGN1YWwgYXBsaWNhbW9zIGVsIHNpZ3VpZW50ZSBibG9xdWUgZGUgQ8OzZGlnbzogPC9wPg0KDQpgYGB7cn0NCnZhciBzb3J0ZW8gPSBJTUdzZW50aW5lbC5zb3J0KCdDTE9VRFlfUElYRUxfUEVSQ0VOVEFKRScpOw0KdmFyIHNlbnRpbmVsMiA9IGVlLkltYWdlKHNvcnRlby5maXJzdCgpKTsNCk1hcC5hZGRMYXllcihzZW50aW5lbDIse21heDoxMDAwMCwgbWluOjAsIGJhbmRzOlsnQjExJywnQjgnLCdCNCddfSk7DQpwcmludChzZW50aW5lbDIpOyANCmBgYA0KDQo8cCBzdHlsZT0ndGV4dC1hbGlnbjoganVzdGlmeTsnPiBPYnRlbmVtb3MgMjgzIGltw6FnZW5lcyBxdWUgZXN0w6FuIG9yZGVuYWRhcyBlIGRlIGZvcm1hIGFzY2VuZGVudGUgZG9uZGUgZWwgbGF5ZXIgZGUgbGEgY2FwYSBjb24gbWVub3MgcG9yY2VudGFqZSBkZSBudWJlcyBlcyBlbCBwcmltZXJvIDwvcD4NCg0KIVtdKEM6XHBlcmNlcGNpb25yZW1vdGFcUFJcSW5mb3JtZTJfR0VFL2NvbnNpbi5KUEcpDQoNCiMjIyMjIyAqKkltYWdlbiA2LioqIEEgbGEgaXpxdWllcmRhIEltYWdlQ29sbGVjdGlvbiBjb24gbnViZXMsIGEgbGEgZGVyZWNoYSBJbWFnZUNvbGxlY3Rpb24gY29uIGZpbHRybyBkZSBudWJlcw0KDQoqKkxhbmRzYXQ6IFVTR1MgTGFuZHNhdCA4IENvbGxlY3Rpb24gMSBUaWVyIDEgYW5kIFJlYWwtVGltZSBkYXRhIFRPQSBSZWZsZWN0YW5jZSoqDQoNCjxwIHN0eWxlPSd0ZXh0LWFsaWduOiBqdXN0aWZ5Oyc+IFRyYWVtb3MgbGEgaW1hZ2VuIGEgcGFydGlyIGRlbCBJRCBkZSBsYSBpbWFnZW4gKCksIHZhIG9zIGEgdHJhYmFqYXIgY29uIGNvbGVjY2lvbmVzIGRlIGltw6FnZW5lcywgaGFjZW1vcyB1bmEgY29tcG9zaWNpw7NuIGEgdmVyZGFkZXJvIGNvbG9yIGNvbiBsYXMgYmFuZGFzIEI1LCBCNCwgQjMgeSBwb3N0ZXJpb3JtZW50ZSBzZWxlY2Npb25hbW9zIGVsIMOhcmVhIGRlIGludGVyw6lzIGNvbiBsYSBmdW5jacOzbiDigJxnZW9tZXRyeeKAnS4gPC9wPg0KDQo8cCBzdHlsZT0ndGV4dC1hbGlnbjoganVzdGlmeTsnPiBFYXJ0aCBFbmdpbmUgcHJvcG9yY2lvbmEgdW5hIHZhcmllZGFkIGRlIG3DqXRvZG9zIGNvbnZlbmllbnRlcyBwYXJhIGZpbHRyYXIgY29sZWNjaW9uZXMgZGUgaW3DoWdlbmVzLiBFc3BlY8OtZmljYW1lbnRlLCBtdWNob3MgY2Fzb3MgZGUgdXNvIGNvbXVuZXMgc29uIG1hbmVqYWRvcyBwb3IgaW1hZ2VDb2xsZWN0aW9uLmZpbHRlckRhdGUoKSwgeSBpbWFnZUNvbGxlY3Rpb24uZmlsdGVyQm91bmRzKCkuIFBhcmEgZWwgZmlsdHJhZG8gZGUgdXNvIGdlbmVyYWwsIHVzZSBpbWFnZUNvbGxlY3Rpb24uZmlsdGVyKCljb24gdW4gZWUuRmlsdGVyY29tbyBhcmd1bWVudG8uIEVsIHNpZ3VpZW50ZSBlamVtcGxvIG11ZXN0cmEgYW1ib3MgbcOpdG9kb3MgZGUgY29udmVuaWVuY2lhIHkgZmlsdGVyKCkgcGFyYSBpZGVudGlmaWNhciB5IGVsaW1pbmFyIGltw6FnZW5lcyBjb24gdW4gcmVnaXN0cm8gaW5jb3JyZWN0byBkZSB1biBJbWFnZUNvbGxlY3Rpb24uIDwvcD4NCg0KPHAgc3R5bGU9J3RleHQtYWxpZ246IGp1c3RpZnk7Jz4gVW5hIHZleiByZWFsaXphZG8gZXN0byBvYnNlcnZhbW9zIGltw6FnZW5lcyBjb24gbnViZXMgcG9yIGxvIGN1YWwgYXBsaWNhbW9zIGFsZ3Vub3MgZmlsdHJvcyDigJxmaWx0ZXLigJ0gY29tbyBTb3J0ZW8gZGUgYWN1ZXJkbyBjb24gZWwgcG9yY2VudGFqZSBkZSBudWJlcywgZmlsdHJvcyBwb3IgR2VvbWV0csOtYSwgZmlsdHJvIHBvciB0aWVtcG8sIGZpbHRybyBwb3IgTWV0YWRhdGEuIExvIGFudGVyaW9yIGxvIGFwbGljYW1vcyBjb24gZWwgc2lndWllbnRlIGJsb3F1ZSBkZSBDw7NkaWdvOiA8L3A+DQoNCmBgYHtyfQ0KdmFyIElNR2xhbmRzYXQgPSBlZS4gSW1hZ2VDb2xsZWN0aW9uKCdMQU5EU0FUL0xDMDgvQzAxL1QxX1JUX1RPQScpLmZpbHRlckJvdW5kcyhnZW9tZXRyeSk7DQpwcmludChJTUdsYW5kc2F0KTsNCk1hcC5hZGRMYXllcihJTUdsYW5kc2F0LHttYXg6MSwgbWluOjAsIGJhbmRzOlsnQjYnLCdCNScsJ0I0J119KTsNCg0KdmFyIHNvcnRlbzIgPSBJTUdsYW5kc2F0LnNvcnQoJ0NMT1VEWV9QSVhFTF9QRVJDRU5UQUpFJyk7DQp2YXIgSU1HbGFuZHNhdDIgPSBlZS5JbWFnZShzb3J0ZW8yLmZpcnN0KCkpOw0KTWFwLmFkZExheWVyKElNR2xhbmRzYXQyLHttYXg6MSwgbWluOjAsIGJhbmRzOlsnQjExJywnQjgnLCdCNCddfSk7DQpgYGANCiFbXShDOlxwZXJjZXBjaW9ucmVtb3RhXFBSXEluZm9ybWUyX0dFRS92ZXJjb2wuSlBHKQ0KDQojIyMjIyMgKipJbWFnZW4gNy4qKiBMYW5kc2F0IGNvbXBvc2ljacOzbiBCNSwgQjQsIEIzOyAqKkltYWdlbiA3LioqIExhbmRzYXQgY29tcG9zaWNpw7NuIEIxMSwgQjgsIEI0DQoNCjxwIHN0eWxlPSd0ZXh0LWFsaWduOiBqdXN0aWZ5Oyc+IEEgY29udGludWFjacOzbiwgZ2VuZXJhbW9zIG90cmFzIGNsYXNlcyBkZSBmaWx0cm9zIGNvbiBsYXMgc2lndWllbnRlcyBsw61uZWFzIGRlIEPDs2RpZ286IDwvcD4NCg0KYGBge3J9DQovL2ZpbHRybyBwb3IgdGllbXBvDQogDQp2YXIgSU1HbGFuZHNhdDMgPSBlZS4gSW1hZ2VDb2xsZWN0aW9uKCdMQU5EU0FUL0xDMDgvQzAxL1QxX1JUX1RPQScpLmZpbHRlckJvdW5kcyhnZW9tZXRyeSkgDQogICAgICAgICAgICAgICAgICAuZmlsdGVyRGF0ZSgnMjAxNy0xMi0wMScsJzIwMTctMTItMzEnKTsNCiANCiAvL2ZpbHRybyBwb3IgTWV0YWRhdGENCiANCnZhciBsYW5kc2F0ID0gZWUuIEltYWdlQ29sbGVjdGlvbignTEFORFNBVC9MQzA4L0MwMS9UMV9SVF9UT0EnKS5maWx0ZXJCb3VuZHMoZ2VvbWV0cnkpDQogICAgICAgICAgICAgICAgLmZpbHRlck1ldGFkYXRhKCdDTE9VRF9DT1ZFUicsJ2VxdWFscycsJzEwJyk7DQoNCmBgYA0KDQojIyMgKipSZWR1Y2Npw7NuIGRlIEltYWdlQ29sbGVjdGlvbioqDQoNCjxwIHN0eWxlPSd0ZXh0LWFsaWduOiBqdXN0aWZ5Oyc+ICBQYXJhIHJlZHVjaXIgdW5hIEltYWdlQ29sbGVjdGlvbiwgZGViZW1vcyB1c2FyIGltYWdlQ29sbGVjdGlvbi5yZWR1Y2UoKS4gRXN0byByZWR1Y2lyw6EgdG9kYXMgbGFzIGltw6FnZW5lcyBkZSBsYSBjb2xlY2Npw7NuIGVuIHVuYSBzb2xhIGltYWdlbiBxdWUgcmVwcmVzZW50YSwgcG9yIGVqZW1wbG8sIGxhIGRlc3ZpYWNpw7NuIG3DrW5pbWEsIG3DoXhpbWEsIG1lZGlhIG8gZXN0w6FuZGFyIGRlIGxhcyBpbcOhZ2VuZXMuIFBhcmEgb2J0ZW5lciBtw6FzIGluZm9ybWFjacOzbiBzb2JyZSBsb3MgcmVkdWN0b3JlcyAqKkltYWdlbiA4KiouPC9wPg0KDQoNCiFbXShDOlxwZXJjZXBjaW9ucmVtb3RhXFBSXEluZm9ybWUyX0dFRS9SZWR1Y2Npb24uSlBHKSAgIVtdKEM6XHBlcmNlcGNpb25yZW1vdGFcUFJcSW5mb3JtZTJfR0VFL1JlZHVjZXIuSlBHKQ0KDQojIyMjIyMgKipJbWFnZW4gOC4qKiBFc3F1ZW1hIGRlIHJlZHVjY2nDs24gZGUgdW5hIEltYWdlQ29sbGVjdGlvbi50b21hZGEgZGUgaHR0cHM6Ly9kZXZlbG9wZXJzLmdvb2dsZS5jb20vZWFydGgtZW5naW5lL3JlZHVjZXJzX2ltYWdlX2NvbGxlY3Rpb24NCg0KPHAgc3R5bGU9J3RleHQtYWxpZ246IGp1c3RpZnk7Jz4gIENvbiBsYXMgc2lndWllbnRlcyBsw61uZWFzIGRlIGPDs2RpZ28gb2J0ZW5kcmVtb3MgbGFzIGltw6FnZW5lcyByZWR1Y2lkYXM6IDwvcD4NCjxwIHN0eWxlPSd0ZXh0LWFsaWduOiBqdXN0aWZ5Oyc+IEFwbGljYW1vcyByZWR1Y2Npb25lcyBkZSBtZWRpYW5hLCBtZWRpYSwgbcOtbmltYSwgbcOheGltYSB5IHVuYSB2YXJpYW50ZSBkZSBtZWRpYW5hIGRvbmRlIGxlIGFwbGljYW1vcyBsYSBtZWRpYW5hIGEgY2FkYSBiYW5kYSAqKkltYWdlbiA4KiogPC9wPg0KDQpgYGB7cn0NCi8vUmVkdWNjacOzbg0KdmFyIGxhbmRzYXRtZWRpYW5hID0gbGFuZHNhdC5tZWRpYW4oKTsNCnZhciBsYW5kc2F0bWVkaWEgPSBsYW5kc2F0Lm1lYW4oKTsNCnZhciBsYW5kc2F0bWF4ID0gbGFuZHNhdC5tYXgoKTsNCnZhciBsYW5kc2F0bWluID0gbGFuZHNhdC5tYXgoKTsNCnZhciBsYW5kc2F0bWVkaWFuYTIgPSBsYW5kc2F0LnJlZHVjZShlZS5SZWR1Y2VyLm1lZGlhbigpKTsNCg0KDQpgYGANCg0KPHAgc3R5bGU9J3RleHQtYWxpZ246IGp1c3RpZnk7Jz4gTGxhbWFtb3MgbGFzIGltYWdlbmVzIHJlZHVjaWRhczogPC9wPg0KDQpgYGB7cn0NCi8vcmVkdWNpciB1bmEgY29sZWNjacOzbiBkZSBpbcOhZ2VuZXMNCiANCiBwcmludChsYW5kc2F0KTsNCiBNYXAuY2VudGVyT2JqZWN0KGxhbmRzYXQsIDEwKTsNCiBNYXAuYWRkTGF5ZXIobGFuZHNhdCx7bWF4OjEsIG1pbjowLCBiYW5kczpbJ0I2JywnQjUnLCdCNCddfSk7DQogTWFwLmFkZExheWVyKGxhbmRzYXRtZWRpYW5hLHttYXg6MSwgbWluOjAsIGJhbmRzOlsnQjYnLCdCNScsJ0I0J119LCAnTDggbWVkaWFuYScpOw0KIE1hcC5hZGRMYXllcihsYW5kc2F0bWVkaWEse21heDoxLCBtaW46MCwgYmFuZHM6WydCNicsJ0I1JywnQjQnXX0sICdMOCBtZWRpYScpOw0KIE1hcC5hZGRMYXllcihsYW5kc2F0bWF4LHttYXg6MSwgbWluOjAsIGJhbmRzOlsnQjYnLCdCNScsJ0I0J119LCAnTDggbWF4Jyk7DQogTWFwLmFkZExheWVyKGxhbmRzYXRtaW4se21heDoxLCBtaW46MCwgYmFuZHM6WydCNicsJ0I1JywnQjQnXX0sICdMOCBtaW4nKTsNCiBNYXAuYWRkTGF5ZXIobGFuZHNhdG1lZGlhbmEyLHttYXg6MSwgbWluOjAsIGJhbmRzOlsnQjZfbWVkaWFuJywnQjVfbWVkaWFuJywnQjRfbWVkaWFuJ119LCAnTDggbWVkaWFuYTInKTsNCmBgYA0KDQohW10oQzpccGVyY2VwY2lvbnJlbW90YVxQUlxJbmZvcm1lMl9HRUUvTGF5ZXIxLkpQRykgIVtdKEM6XHBlcmNlcGNpb25yZW1vdGFcUFJcSW5mb3JtZTJfR0VFL21lZGlhbmExLkpQRykNCiFbXShDOlxwZXJjZXBjaW9ucmVtb3RhXFBSXEluZm9ybWUyX0dFRS9tZWRpYS5KUEcpICAhW10oQzpccGVyY2VwY2lvbnJlbW90YVxQUlxJbmZvcm1lMl9HRUUvbWF4LkpQRykNCiFbXShDOlxwZXJjZXBjaW9ucmVtb3RhXFBSXEluZm9ybWUyX0dFRS9taW4uSlBHKSAgICAhW10oQzpccGVyY2VwY2lvbnJlbW90YVxQUlxJbmZvcm1lMl9HRUUvbWVkaWFuYTIuSlBHKQ0KDQojIyMjIyMgICoqSW1hZ2VuIDkuKiogIG1lZGlhbmEsIG1lZGlhLCBtw61uaW1hLCBtw6F4aW1hIHkgdW5hIHZhcmlhbnRlIGRlIG1lZGlhbmENCl9fX19fX18NCg0KDQojIyMgKipEaXNjdWNpw7NuIHkgQ29uY2x1c2lvbmVzKioNCjxwIHN0eWxlPSd0ZXh0LWFsaWduOiBqdXN0aWZ5Oyc+IC0gU2UgbG9ncsOzIGFib3JkYXIgZGUgbWFuZXJhIGdlbmVyYWwgZWwgdXNvIHByw6FjdGljbyBkZWwgbGVuZ3VhamUgSmF2YVNjcmlwdCBkZW50cm8gZGUgbGEgcGxhdGFmb3JtYSBHRUUgcGFyYSBwcm9jZXNhbWllbnRvcyBlbiBwZXJjZXBjacOzbiByZW1vdGEuDQotIFVzYW5kbyBlbCBsZW5ndWFqZSBKYXZhU2NyaXB0IHNlIHB1ZGllcm9uIGdlbmVyYXIgYWxndW5hcyBmdW5jaW9uZXMgaW1wb3J0YW50ZXMgZW4gZWwgcHJvY2VzYW1pZW50byBkZSBpbcOhZ2VuZXMgcXVlIHBvc3Rlcmlvcm1lbnRlIHBvZGVtb3MgdXNhciBwYXJhIGludmVzdGlnYWNpb25lcyBxdWUgYXB1bnRlbiBhIGxhIHJlc29sdWNpw7NuIGRlIHByb2JsZW1hcyBkZSBudWVzdHJhIHJlYWxpZGFkLiANClNlIGdlbmVyYXJvbiBmdW5jaW9uZXMgeSBhcnJlZ2xvcyBpbXBvcnRhbnRlcyBwYXJhIGVsIGFuw6FsaXNpcyBkZSBpbcOhZ2VuZXMgbWVkaWFudGUgZGlmZXJlbnRlcyBsw61uZWFzIGRlIGPDs2RpZ28uDQotIExhIHBsYXRhZm9ybWEgR0VFIG5vIHBlcm1pdGUgY29ycmVyIGzDrW5lYXMgZGUgY8OzZGlnbyBkZSBtYW5lcmEgaW5kaXZpZHVhbCwgZXN0byBkaWZpY3VsdGEgbGEgcmVhbGl6YWNpw7NuIGRlIGFsZ3Vub3MgcHJvY2Vzb3MuIA0KLSBFbiBnZW5lcmFsLCBHRUUgcHJvcG9yY2lvbsOzIHVuIHJlbmRpbWllbnRvIG11eSBidWVubyBhbCBwZXJtaXRpciBlbCBhY2Nlc28gYSBwcm9kdWN0b3MgZGUgZGV0ZWNjacOzbiByZW1vdGEgYSB0cmF2w6lzIGRlIGxhIHBsYXRhZm9ybWEgZW4gbGEgbnViZSA8L3A+DQoNCl9fX19fX18NCg0KIyMjICoqQmlibGlvZ3JhZsOtYSoqDQpfX19fX19fDQoNCjxwIHN0eWxlPSd0ZXh0LWFsaWduOiBqdXN0aWZ5Oyc+IEJhciwgUy4sIFBhcmlkYSwgQi4gUi4sICYgUGFuZGV5LCBBLiBDLiAoMjAyMCkuIFJlbW90ZSBTZW5zaW5nIEFwcGxpY2F0aW9uc+KArzogU29jaWV0eSBhbmQgRW52aXJvbm1lbnQgTGFuZHNhdC04IGFuZCBTZW50aW5lbC0yIGJhc2VkIEZvcmVzdCBmaXJlIGJ1cm4gYXJlYSBtYXBwaW5nIHVzaW5nIG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtcyBvbiBHRUUgY2xvdWQgcGxhdGZvcm0gb3ZlciBVdHRhcmFraGFuZCAsIFdlc3Rlcm4gSGltYWxheWEuIFJlbW90ZSBTZW5zaW5nIEFwcGxpY2F0aW9uczogU29jaWV0eSBhbmQgRW52aXJvbm1lbnQsIDE4KE1hcmNoKSwgMTAwMzI0LiBodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLnJzYXNlLjIwMjAuMTAwMzI0IDwvcD4NCjxwIHN0eWxlPSd0ZXh0LWFsaWduOiBqdXN0aWZ5Oyc+IEJvcnLDoHMsIEouLCAsIERlbGVnaWRvLCBKLiwgLCBQZXp6b2xhLCBBLiwgLCBQZXJlaXJhLCBNLiwgLCBNb3Jhc3NpLCBHLiwgJiAsIENhbXBzLVZhbGxzLCBHLiAoMjAxNykuIENsYXNpZmljYWNpw7NuIGRlIHVzb3MgZGVsIHN1ZWxvIGEgcGFydGlyIGRlIGltw6FnZW5lcyBTZW50aW5lbC0yLiBSZXZpc3RhIGRlIFRlbGVkZXRlY2Npw7NuLCA1NeKAkzY2LiA8L3A+DQo8cCBzdHlsZT0ndGV4dC1hbGlnbjoganVzdGlmeTsnPiBCcmFuLCBELiBFLiBELiwgJiBBemNvbmEsIEMuICgyMDE1KS4gVGVuZGVuY2lhIGRlbCBORFZJIGVuIGVsIHBlcsOtb2RvIDIwMDAtMjAxNCBjb21vIGluZGljYWRvciBkZSBsYSBkZWdyYWRhY2nDs24gZGUgdGllcnJhcyBlbiBBcmdlbnRpbmHigK86IHZlbnRhamFzIHkgbGltaXRhY2lvbmVzLCAzMigyKSwgODPigJM5My4NCkNhcmxvcywgSi4sICYgRmxvcmVzLCBSLiAoMjAxNSkuIE1VTklDSVBJTyBERSBTQVNBSU1BICggQ3VuZGluYW1hcmNhICkgUExBTiBNVU5JQ0lQQUwgREUgR0VTVEnDk04gREVMIFJJRVNHTyBERSBPZmljaW5hIGRlIE1lZGlvIEFtYmllbnRlLiA8L3A+DQo8cCBzdHlsZT0ndGV4dC1hbGlnbjoganVzdGlmeTsnPiBHb3JlbGljaywgTi4sIEhhbmNoZXIsIE0uLCBEaXhvbiwgTS4sIElseXVzaGNoZW5rbywgUy4sIFRoYXUsIEQuLCAmIE1vb3JlLCBSLiAoMjAxNykuIEdvb2dsZSBFYXJ0aCBFbmdpbmU6IFBsYW5ldGFyeS1zY2FsZSBnZW9zcGF0aWFsIGFuYWx5c2lzIGZvciBldmVyeW9uZS4gUmVtb3RlIFNlbnNpbmcgb2YgRW52aXJvbm1lbnQsIDIwMiwgMTjigJMyNy4gaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5yc2UuMjAxNy4wNi4wMzEgPC9wPg0KPHAgc3R5bGU9J3RleHQtYWxpZ246IGp1c3RpZnk7Jz4gSmltw6luZXotbXXDsW96LCBKLiBDLiwgU29icmlubywgSi4gQS4sIFNrb2tvdmksIEQuLCBNYXR0YXIsIEMuLCBDcmlzdMOzYmFsLCBKLiwgJiBCYW5kcywgQS4gTC4tVC4gKDIwMTQpLiBMYW5kIFN1cmZhY2UgVGVtcGVyYXR1cmUgUmV0cmlldmFsIE1ldGhvZHMgRnJvbSBMYW5kc2F0LTggVGhlcm1hbCBJbmZyYXJlZCBTZW5zb3IgRGF0YSwgMTEoMTApLCAxODQw4oCTMTg0My4gPC9wPg0KPHAgc3R5bGU9J3RleHQtYWxpZ246IGp1c3RpZnk7Jz4gS3JvaG4sIEguIFMuICgyMDE5KS4gUHJvZ3JhbWFjacOzbiBkZSBidXNjYWRvcmVzIGVuIEphdmFTY3JpcHQgcGFyYSBkaWNjaW9uYXJpb3MgZGlnaXRhbGVzIOKIlywgKGZhc2UgMSksIDEwOeKAkzEzMC48L3A+DQo8cCBzdHlsZT0ndGV4dC1hbGlnbjoganVzdGlmeTsnPiBMdWNlcm8sIFMuLCAmIEJlY2VycmEsIEMuICgyMDE4KS4gSW1wbGVtZW50YWNpw7NuIGRlIG1vZGVsb3MgZXNwYWNpbyB0ZW1wb3JhbGVzIHBhcmEgcHJlZGVjaXIgbGEgZGlzdHJpYnVjacOzbiBkZSBsYXMgZW5mZXJtZWRhZGVzIHRyYW5zbWl0aWRhcyBwb3IgdmVjdG9yZXMgZW4gem9uYXMgY3LDrXRpY2FzIGRlIFNhc2FpbWEtIEN1bmRpbmFtYXJjYS4gPC9wPg0KDQoNCg0KIA0KIA0KDQo=