Landsat 8, La imagen corresponde al municipio de facatativa, departamento de cundinamarca, colombia, con frecha de 30 de diciembra del 2018
Seleccionar la carpeta de destino
# "C:\percepcionremota\PR\imagenfaca"
getwd()
[1] "C:/percepcionremota/PR/imagenfaca"

Crear objetos RasterLayer para capas individuales Landsat (bandas) y las llamamos enrutandolas como por ejemplo (‘./data/LC08_L1TP_008057_20180317_20180403_01_T1_B2.tif’)

INTRODUCCION

####Para realizar procesamiento de imagenes satelitales encontramos diversas alternativas en la actualidad, ya sea con software libre o comercial, se afrima que opciones son altamente competitivas y poseen gran capacidad de procesamiento. Para este estudio se utilizaron R, Rstudio, que permiten realizar una clasificación de imagenes no supervisada y supervisada, ArcGIS para la elaboracion de shape, como otros procesos que se desarrollaran durante este estudio, explorando aspectos aspectos técnicos.

####Dentro del proyecto podemos resaltar algunas fases u operaciones como el preprocesado implementado a la imagen satelital donde se han aplicado una serie de transformaciones, la aplicación de técnicas de clasificación supervisada mediante la realización de entrenamiento y testeo, usando como area de interes el municipio de Facatativa.

IMPORTANTE: instalar paquetes: raster, sp, rgdal

Se implementó principalmente un subconjunto espacial de una escena Landsat 8 del municipipo de Facatativa, Cundinamarca, Colombia

TITULO: CAPITULO 1

TITULO: PROPIEDADES DE IMAGEN

Se crearonr objetos RasterLayer para capas individuales Landsat (bandas)
library(raster)
# Costera
b1 = raster('./LC08_L1TP_008057_20181230_20190130_01_T1_B1.TIF')
# Azul
b2 = raster('./LC08_L1TP_008057_20181230_20190130_01_T1_B2.TIF')
# Verde
b3 = raster('./LC08_L1TP_008057_20181230_20190130_01_T1_B3.TIF')
# Rojo
b4 = raster('./LC08_L1TP_008057_20181230_20190130_01_T1_B4.TIF')
# Infrarojo cercano (NIR)
b5 = raster('./LC08_L1TP_008057_20181230_20190130_01_T1_B5.TIF')
# Infrarrojo de Onda Corta 1 (SWIR 1)
b6 = raster('./LC08_L1TP_008057_20181230_20190130_01_T1_B6.tif')
# Infrarrojo de Onda Corta 2 (SWIR 2)
b7 = raster('./LC08_L1TP_008057_20181230_20190130_01_T1_B7.tif')
# Pancromatica
b8 = raster('./LC08_L1TP_008057_20181230_20190130_01_T1_B8.tif')
# Cirros (Cirrus)
b9 = raster('./LC08_L1TP_008057_20181230_20190130_01_T1_B9.tif')
# Sensor Térmico Infrarrojo 1 (TIRS 1)
b10 = raster('./LC08_L1TP_008057_20181230_20190130_01_T1_B10.tif')
# Sensor Térmico Infrarrojo 2 (TIRS 2)
b11 = raster('./LC08_L1TP_008057_20181230_20190130_01_T1_B11.tif')
A continuación podemos imprimir las variables a verificar, por ejemplo con la banda dos (b2):
b2
class      : RasterLayer 
dimensions : 7741, 7581, 58684521  (nrow, ncol, ncell)
resolution : 30, 30  (x, y)
extent     : 446685, 674115, 362985, 595215  (xmin, xmax, ymin, ymax)
crs        : +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
source     : C:/percepcionremota/PR/imagenfaca/LC08_L1TP_008057_20181230_20190130_01_T1_B2.TIF 
names      : LC08_L1TP_008057_20181230_20190130_01_T1_B2 
values     : 0, 58849  (min, max)
Podemos observar la resolucion espacial, la extension, el numero de capas, el sistema de referencia de coordenadas y mas.

TITULO: INFORMACION DE IMAGEN Y ESTADISTICAS

A continuacion se presentan las diferentes propiedades desde un objeto Raster

# Sistema de coordenadas de referencia (CRS)
crs(b2)
CRS arguments:
 +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
# Numero de celdas, filas, columnas
ncell(b2)
[1] 58684521
# Dimension
dim(b2)
[1] 7741 7581    1
# Resolucion espacial
res(b2)
[1] 30 30
# Numero de badas 
nlayers(b2)
[1] 1
# ¿Las bandas tienen la misma extension, numero de filas y columnas, proyeccion, resolucion y origen?
compareRaster(b2,b3)
[1] TRUE
## [1] TRUE
Puede crear un RasterStack con la misma extensión y resolución espacial. Se puede crear un RasterStack a partir de objetos RasterLayer, o de archivos ráster, o ambos.
# Creamos el RasterStack
s2015 = stack(b5, b4, b3)

# Revisamos las propiedades del RasterStack
s2015
class      : RasterStack 
dimensions : 7741, 7581, 58684521, 3  (nrow, ncol, ncell, nlayers)
resolution : 30, 30  (x, y)
extent     : 446685, 674115, 362985, 595215  (xmin, xmax, ymin, ymax)
crs        : +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
names      : LC08_L1TP_008057_20181230_20190130_01_T1_B5, LC08_L1TP_008057_20181230_20190130_01_T1_B4, LC08_L1TP_008057_20181230_20190130_01_T1_B3 
min values :                                           0,                                           0,                                           0 
max values :                                       65535,                                       62063,                                       58803 
## class      : RasterStack
## dimensions : 7741, 7581, 58684521, 3  (nrow, ncol, ncell, nlayers)
## resolution : 30, 30  (x, y)
## extent     : 446085, 673515, 362985, 595215  (xmin, xmax, ymin, ymax)
## crs        : ++proj=utm +zone=18 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
## names      : LC08_L1TP_008057_20180317_20180403_01_T1_B5, LC08_L1TP_008057_20180317_20180403_01_T1_B4,     LC08_L1TP_008057_20180317_20180403_01_T1_B3
## min values :  0, 0, 0 
## max values :  65535, 65535, 65535
Tambien puede crear el RasterStack usando los nombres de archivo

# Primero creamos una lista de los raster layers usando:

filenames = paste0('./LC08_L1TP_008057_20181230_20190130_01_T1_B', 1:7, ".tif")
filenames
[1] "./LC08_L1TP_008057_20181230_20190130_01_T1_B1.tif" "./LC08_L1TP_008057_20181230_20190130_01_T1_B2.tif"
[3] "./LC08_L1TP_008057_20181230_20190130_01_T1_B3.tif" "./LC08_L1TP_008057_20181230_20190130_01_T1_B4.tif"
[5] "./LC08_L1TP_008057_20181230_20190130_01_T1_B5.tif" "./LC08_L1TP_008057_20181230_20190130_01_T1_B6.tif"
[7] "./LC08_L1TP_008057_20181230_20190130_01_T1_B7.tif"
## [1] "./data2015/LC08_L1TP_008057_20150104_20170415_01_T1_B1.tif" 
## [2] "./data2015/LC08_L1TP_008057_20150104_20170415_01_T1_B2.tif" 
## [3] "./data2015/LC08_L1TP_008057_20150104_20170415_01_T1_B3.tif" 
## [4] "./data2015/LC08_L1TP_008057_20150104_20170415_01_T1_B4.tif" 
## [5] "./data2015/LC08_L1TP_008057_20150104_20170415_01_T1_B5.tif" 
## [6] "./data2015/LC08_L1TP_008057_20150104_20170415_01_T1_B6.tif" 
## [7] "./data2015/LC08_L1TP_008057_20150104_20170415_01_T1_B7.tif" 

###### no lee la banda 8 porque tiene diferente extension, la banda 9 es cirro, y las bandas 10 y 11 son termales

## [8] "./data2015/LC08_L1TP_008057_20150104_20170415_01_T1_B8.tif" 
## [9] "./data2015/LC08_L1TP_008057_20150104_20170415_01_T1_B9.tif" 
## [10] "./data2015/LC08_L1TP_008057_20150104_20170415_01_T1_B10.tif"
## [11] "./data2015/LC08_L1TP_008057_20150104_20170415_01_T1_B11.tif"

# Creo el RasterStack de las bandas seleccionadas, es decir  de la 1 a la  7 (1:7)
landsatFaca = stack (filenames)
landsatFaca
class      : RasterStack 
dimensions : 7741, 7581, 58684521, 7  (nrow, ncol, ncell, nlayers)
resolution : 30, 30  (x, y)
extent     : 446685, 674115, 362985, 595215  (xmin, xmax, ymin, ymax)
crs        : +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
names      : LC08_L1TP//0_01_T1_B1, LC08_L1TP//0_01_T1_B2, LC08_L1TP//0_01_T1_B3, LC08_L1TP//0_01_T1_B4, LC08_L1TP//0_01_T1_B5, LC08_L1TP//0_01_T1_B6, LC08_L1TP//0_01_T1_B7 
min values :                     0,                     0,                     0,                     0,                     0,                     0,                     0 
max values :                 56188,                 58849,                 58803,                 62063,                 65535,                 45351,                 37289 
## class      : RasterStack
## dimensions : 7741, 7581, 58684521, 7  (nrow, ncol, ncell, nlayers)
## resolution : 30, 30  (x, y)
## extent     : 446085, 673515, 362985, 595215  (xmin, xmax, ymin, ymax)
## crs        : +proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
## names      : LC08_L1TP//5_01_T1_B1, LC08_L1TP//5_01_T1_B2, LC08_L1TP//5_01_T1_B3, LC08_L1TP//5_01_T1_B4, LC08_L1TP//5_01_T1_B5, LC08_L1TP//5_01_T1_B6, LC08_L1TP//5_01_T1_B7
## min values :                     0,                     0,                     0,                     0,                     0,                     0,                     0 
## max values :                 65535,                 65535,                 65535,                 65535,                 65535,                 65535,                 65535 
Arriba creamos un RasterStack con 11 capas. Las capas representan la intensidad de la reflexion en las siguientes longitudes de onda: Ultra azul, azul, verde, rojo, infrarrojo cercano (NIR), infrarrojo de onda corta (SWIR) 1, infrarrojo de onda corta (SWIR) 2, pancromatico, cirro, infrarrojo termico (TIRS) 1, Infrarrojo termico (TIRS) 2. No utilizaremos las ultimas cuatro capas y vera como eliminarlas en las siguientes secciones.

TITULO: BANDA UNICA Y MAPAS COMPUESTOS

Puede trazar capas individuales de un RasterStack de una imagen multiespectral.
# Trazando capas indivuduales (aZUL, VERDE, ROJO, INFRAROJO CERCANO) a partir del RasterStack
par(mfrow = c(2,2))
plot(b2, main = "Azul", col = gray(0:100 / 100), axes = FALSE)
plot(b3, main = "Verde", col = gray(0:100 / 100), axes = FALSE)
plot(b4, main = "Rojo", col = gray(0:100 / 100), axes = FALSE)
plot(b5, main = "NIR", col = gray(0:100 / 100), axes = FALSE)

En la imagen anterior podemos observar que ls leyendas Pueden variar entre 0 y 1. las diferentes caracteristicas de la superficie reflejan la radiacion solar incidente de manera diferente, esto se observa en la imagen en la diferencial del tono de grises, Cada capa representa la cantidad de radiacion solar incidente que se refleja para un rango de longitud de onda particular. Por ejemplo, la vegetacion refleja mas energia en NIR que otras longitudes de onda y, por lo tanto, parece mas brillante. Por el contrario, el agua absorbe la mayor parte de la energia en la longitud de onda NIR y parece oscura.

#####Cuando las bandas espectrales de un determinado sensor coinciden exactamente con las longitudes de onda de los colores Rojo, Verde y Azul, la composición resultante se conoce como “color verdadero”. Las composiciones RGB se utilizan para analizar y resaltar de forma visual determinada información de la imagen, como ,o presenta la siguiente imagen:

# Ploteamos la combinacion de bandas 4, 3 y 2 que para L8 es color verdadero
landsatRGBFaca <- stack(b4, b3, b2)
plotRGB(landsatRGBFaca, axes = TRUE, stretch = "lin", main = "Landsat True Color Composite")

Para hacer una imagen de “color verdadero (o natural)”, es decir, algo que se parece a una fotografia normal (vegetacion en verde, azul agua, etc.), necesitamos bandas en las regiones roja, verde y azul. Para esta imagen Landsat, se pueden usar las bandas 4 (rojo), 3 (verde) y 2 (azul). El metodo - plotRGB - se puede utilizar para combinarlos en un solo compuesto. Tambien puede proporcionar argumentos adicionales - plotRGB - para mejorar la visualizacion (por ejemplo, un estiramiento lineal de los valores, utilizando ).- strecth = “lin” -
Nota : Compruebe siempre la documentacion del paquete (- help(plotRGB) -) para ver otros argumentos que se pueden agregar (como la escala) para mejorar o modificar la imagen.
Pregunta 1 : Use la funcion plotRGB con RasterStack `` landsat ’’ para crear un compuesto de color verdadero y falso (recuerde la posicion de las bandas en la pila).

## solucion a la pregunta 1, ploteamos la combinacion de bandas 5, 6 y 4 que para L8 es color falso
landsatfaca_p1 = stack(b5, b6, b4)
plotRGB(landsatfaca_p1, axes=TRUE, stretch="lin", main="Composicion de color falsa - Agua / Tierra")

TITULO: SUBCONJUNTO Y RENOMBRAR BANDAS

Puede seleccionar capas (bandas) especificas mediante la funcion - subset - o mediante indexacion.

# select first 3 bands only
landsatFacasub1 <- subset(landsatFaca, 1:3)
# same
landsatFacasub2 <- landsatFaca[[1:3]]
# Number of bands in the original and new data
nlayers(landsatFaca)
[1] 7
nlayers(landsatFacasub1)
[1] 3
nlayers(landsatFacasub2)
[1] 3
No usaremos las ultimas cuatro bandas - landsat -. Puedes eliminar aquellos usando:
## Eliminando las ultimas cuatro bandas, es decir, seleccionaremos para nuestro ejercicio las bandas de 1 a la 7 (1:7)
landsatFaca <- subset(landsatFaca, 1:7)
Para mayor claridad, es util establecer los nombres de las bandas.
# Aca vemos que nombres tienen las bandas
names(landsatFaca) <- c('ultra-blue', 'blue', 'green', 'red', 'NIR', 'SWIR1', 'SWIR2')
names(landsatFaca)
[1] "ultra.blue" "blue"       "green"      "red"        "NIR"        "SWIR1"      "SWIR2"     
plot (landsatFaca)


## [1] "LC08_L1TP_008057_20180317_20180403_01_T1_B1" "LC08_L1TP_008057_20180317_20180403_01_T1_B2"
## [3] "LC08_L1TP_008057_20180317_20180403_01_T1_B3" "LC08_L1TP_008057_20180317_20180403_01_T1_B4"
## [5] "LC08_L1TP_008057_20180317_20180403_01_T1_B5" "LC08_L1TP_008057_20180317_20180403_01_T1_B6"
## [7] "LC08_L1TP_008057_20180317_20180403_01_T1_B7"

# Aca las renombramos y las ploteamos
## [1] "ultra.azuk" "azul"       "verde"      "rojo"       "NIR"        "SWIR1"      "SWIR2"     
## [1] "ultra.azul" "azul"       "verde"      "rojo"       "NIR"        "SWIR1"      "SWIR2"

TITULO: SUBCONJUNTO ESPACIAL O RECORTE

El subconjunto espacial se puede usar para limitar el analisis a un subconjunto geografico de la imagen. Los subconjuntos espaciales se pueden crear con la funcion - crop -, utilizando un objeto - extent - u otro objeto espacial del que se puede extraer una Extension.

# Usando Extension
extent(landsatFaca)
class      : Extent 
xmin       : 446685 
xmax       : 674115 
ymin       : 362985 
ymax       : 595215 
## class      : Extent
## xmin       : 446085 
## xmax       : 673515
## ymin       : 362985
## ymax       : 595215

# Cargamos un objeto espacial que se encuentra en un shapefile. Esta extension es el area de faca
Facashape = shapefile('C:/percepcionremota/ShpFaca/coberturatierrafaca18.shp')
e = extent(landsatFaca)



###crop landsat 

landsatFacacrop = crop (landsatFaca, e)

plotRGB(landsatFacacrop, axes = TRUE, stretch= "lin", main= "Landsat composicion a falso color faca")

NA
NA
Pregunta 2 : Tambien es posible la seleccion interactiva de la imagen. Use drawExtent y drawPoly para seleccionar un area de interes

se da respuesta a esta pregunta con el ejercicio anterior

Pregunta 3 : Use el `` Landsatcrop ’’ de RasterStack para crear un compuesto de color verdadero y falso

# Creacion de un compuesto de color verdadero, para landsat 8 la combinacion de color es 4,3,2
landsatcropfacasolucion = crop(stack(b4, b3, b2), e)

# Ploteamos la combinacion
plotRGB(landsatcropfacasolucion, axes = TRUE, stretch = "lin", main = "imagen faca - Color Verdadero")

TITULO: GUARDANDO RESULTADOS EN EL DISCO

En esta etapa, es posible que queramos guardar el raster en el disco usando la funcion - writeRaster -. Se admiten multiples tipos de archivos. Utilizaremos el formato GeoTiff de uso comun. Mientras se conserva el orden de las capas, los nombres de las capas se pierden desafortunadamente en el formato GeoTiff
# Aca guardanmos nuestro raster de faca y lo imprimimos para ver lo que se ha guardado
x = writeRaster(landsatFaca, filename="faca_banda.tif", overwrite=TRUE)
plot(x)

Alternativamente, puede utilizar el formato ‘raster-grd’.

# Lo guardamos tambien en formato .grd ya que este formato si guarda los nombres de las capas
writeRaster(landsatFaca, filename="faca_banda.grd", overwrite=TRUE)
Una ventaja de este formato es que guarda los nombres de las capas. La desventaja de este formato es que no muchos otros programas pueden leer los datos, en contraste con el formato GeoTiff.
Nota: Consulte la documentacion del paquete (- help(writeRaster) -) para ver argumentos utiles adicionales que se pueden agregar

TITULO: RELACION ENTRE BANDAS

Una matriz de diagrama de dispersion puede ser util para explorar las relaciones entre capas raster. Esto se puede hacer con la funcion pares () del paquete raster.
Trazado de reflejo en la longitud de onda ultra azul contra el reflejo en la longitud de onda azul.
# Comparacion del reflejo en la longitud de onda Ulta azul vs azul
pairs(landsatFacacrop [[1:2]], main = "ultra=blue vs blue" )

Trazado de reflejo en la longitud de onda roja contra reflejo en la longitud de onda NIR.
# Comparacion del reflejo en la longitud de onda Roja vs NIR
pairs(landsatFacacrop[[4:5]], main = "Red versus NIR")

La primera grafica revela altas correlaciones entre las regiones de longitud de onda azul. Debido a la alta correlacion, podemos usar una de las bandas azules sin perder mucha informacion.
Esta distribucion de puntos en la segunda grafica (entre NIR y rojo) es unica debido a su forma triangular. La vegetacion se refleja muy bien en el rango NIR que en el rojo y crea la esquina superior cerca del eje NIR (y). El agua absorbe energia de todas las bandas y ocupa el lugar cercano al origen. El rincon mas alejado se crea debido a las caracteristicas superficiales altamente reflectantes, como el suelo brillante o el hormigon.

TITULO: EXTRAER VALORES DE PIXELES

A menudo, queremos obtener los valores de las celdas raster para ubicaciones geograficas o areas especificas. La funcion - extract - se utiliza para obtener valores raster en las ubicaciones de otros datos espaciales. Puede usar puntos, lineas, poligonos o un objeto de extension (rectangulo). Tambien puede usar numeros de celda para extraer valores. Al usar puntos, - extract - devuelve los valores de un objeto - Raster* - para las celdas en las que se ubica un conjunto de puntos.
# Cargar los poligonos con informacion sobre el uso del suelo
facacobert = shapefile ('C:/percepcionremota/ShpFaca/coberturatierrafaca18.shp')
    
# Generar muestras de 300 puntos a partir de los poligonos
ptsfacacobert = spsample(facacobert,300, type='regular')

# Agregar la clase de cobertura del suelo a los puntos
ptsfacacobert$Name = over(ptsfacacobert, facacobert)$Name
    
# Extraer valores con puntos
df = extract(landsatFaca, ptsfacacobert)
   
# Para ver algunos de los valores de reflectancia
head(df)
     ultra.blue  blue green   red   NIR SWIR1 SWIR2
[1,]       8937  8344  8179  7794 13903 12615  9427
[2,]       9169  8481  8421  7857 15754 11541  8527
[3,]      10319 10032  9834  9328 14415 12669 10064
[4,]      10912 10810 10895 11307 15852 15358 12324
[5,]       8739  8084  7965  7107 18447 10927  7865
[6,]       8905  8264  8213  7922 14526 12878  9336
#     ultra.azul azul verde rojo  NIR  SWIR1 SWIR2
#[1,]       7928 7238  6623 6033 12696  7520  5997
#[2,]       7957 7278  6700 6101 13199  7933  6123
#[3,]       7973 7285  6741 6148 13482  8072  6219
#[4,]       7962 7262  6710 6114 13413  8116  6199
#[5,]       7931 7246  6671 6060 13541  7655  6014
#[6,]       7943 7242  6623 6024 12437  7529  6039

TITULO: PERFILES ESPECTRALES

Una grafica del espectro (todas las bandas) para los pixeles que representan ciertas caracteristicas de la superficie terrestre (p. Ej. Agua) se conoce como perfil espectral. Dichos perfiles demuestran las diferencias en las propiedades espectrales de varias caracteristicas de la superficie terrestre y constituyen la base para el analisis de imagenes. Los valores espectrales se pueden extraer de cualquier conjunto de datos multiespectrales utilizando la funcion - extract -. En el ejemplo anterior, extrajimos valores de datos de Landsat para las muestras. Estas muestras incluyen: tierras de cultivo, agua, barbecho, construido y abierto. Primero calculamos los valores medios de reflectancia para cada clase y cada banda.

numero= length(ptsfacacobert)
numero
[1] 306
df_samples = as (ptsfacacobert, "SpatialPointsDataFrame")
df_samples
class       : SpatialPointsDataFrame 
features    : 306 
extent      : 564553.6, 581280.3, 526517.1, 541062.1  (xmin, xmax, ymin, ymax)
crs         : +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
variables   : 1
names       :                Name 
min values  :          artificial 
max values  : plantacion_forestal 
df_samples@data=data.frame(ID=1:numero,size=1)
df_samples
class       : SpatialPointsDataFrame 
features    : 306 
extent      : 564553.6, 581280.3, 526517.1, 541062.1  (xmin, xmax, ymin, ymax)
crs         : +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
variables   : 2
names       :  ID, size 
min values  :   1,    1 
max values  : 306,    1 
plot(landsatFacacrop)
plot(facacobert, add= TRUE)
plot(df_samples,pch=1, cex=(df_samples$size)/4, add=TRUE)



df_samples$Name =over(df_samples,facacobert)$Name
df_samples
class       : SpatialPointsDataFrame 
features    : 306 
extent      : 564553.6, 581280.3, 526517.1, 541062.1  (xmin, xmax, ymin, ymax)
crs         : +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
variables   : 3
names       :  ID, size,                Name 
min values  :   1,    1,          artificial 
max values  : 306,    1, plantacion_forestal 
df1=raster::extract(landsatFacacrop, df_samples)


ms = aggregate(df1, list(ptsfacacobert$Name), mean)

# En lugar de la primera columna, usamos nombres de fila
rownames(ms) = ms[,1]
ms = ms[,-1]
ms

##              ultra.blue       blue      green       red        NIR      SWIR1    SWIR2
# Agua           8251.053     7501.298   6840.658     6066.404  5762.921    5081.289    5050.825
# Bosque           8026.761   7326.134   6773.410     6172.284  11207.410   7962.134    6412.746
# Cultivos       8417.864     7800.318   7603.682     7197.955  18156.818   14063.045   11198.864
# Fresa          8541.667   7870.667     7464.667     6902.000  13157.667   9832.333    7806.000
# Invernadero     11522.083  11035.833  11442.333    11179.083  18368.333   14785.000   11300.083
# pastos           8481.000   7926.727   8267.909     7331.636  21093.273   12449.000   8507.273
# zona desnuda  10975.500    11035.500  12884.750    14428.000  17930.750   20671.750   16907.500
Ahora trazamos el espectro medio de estas caracteristicas.
# Cree un vector de color para las clases de cobertura del suelo para usar en el trazado
mycolor = c('darkred', 'yellow', 'burlywood', 'cyan', 'blue', 'green','magenta', "darkgreen")

# transformar ms de un data.frame a una matriz
ms = as.matrix(ms)

# Primero crea una parcela vacia
plot(0, ylim=c(0,25000), xlim = c(1,7), type='n', xlab="Bandas", ylab = "Reflectancia")

# Agrega las diferentes clases
for (i in 1:nrow(ms)){
  lines(ms[i,], type = "l", lwd = 3, lty = 1, col = mycolor[i])
}

# Titulo
title(main="Perfil espectral de Landsat", font.main = 2)

# Leyenda
legend("topleft", rownames(ms),
      cex=0.8, col=mycolor, lty = 1, lwd =3, bty = "n")

El perfil espectral muestra (des) similitud en la reflectancia de diferentes caracteristicas en la superficie de la tierra (o por encima de ella). ‘Agua’ muestra una reflexion relativamente baja en todas las longitudes de onda, y ‘Pastos’, ‘Invernadero’ y ‘Zona desnuda’ tienen una reflectancia relativamente alta en las longitudes de onda mas largas.

TITULO: OPERACIONES MATEMATICAS BASICAS

El paquete - raster- admite muchas operaciones matematicas. Las operaciones matematicas generalmente se realizan por pixel (celda de cuadricula). Primero haremos algunas operaciones aritmeticas basicas para combinar bandas. En el primer ejemplo, escribimos una funcion matematica personalizada para calcular el indice de vegetacion de diferencia normalizada (NDVI).

### solo puedo cargar las bandas de la 1 a la 7 porque la 8 no tiene la misma extension de las demas
raslistp3 = paste0('./LC08_L1TP_008057_20181230_20190130_01_T1_B', 1:7, ".tif")
##raslistp3= practica 3

# Stack de las bandas cargadas
landsatp3 = stack(raslistp3)
plot(landsatp3)


# Combinacion de las bandas 4,3,2
landsatRGBp3 = landsatp3[[c(4,3,2)]]
## landsatRGBp3 = practica 3
plot(landsatRGBp3)


# Combinacion de las bandas en falso color 5,4,3
landsatFCCp3 = landsatp3[[c(5,4,3)]]
## landsatFCCp3 = practica 3
plot(landsatFCCp3)

NA
NA

TITULO: INDICES DE VEGETACION

Definamos una funciOn general para un Indice basado en razOn (vegetaciOn). En la funcion a continuacion, -img- es un objeto Raster * de multiples capas -i- y -k- son los indices de las capas (numeros de capa) utilizados para calcular el indice de vegetacion.
## vifaca= indice de vegetacion para la imagen 2015
vifaca = function(img, k, i) {
  
  bk = img [[k]]
  bi = img [[i]]
  vifaca=(bk-bi)/(bk+bi)
  return(vifaca)
}

## para landsat NIR = 5, red = 4
## ndvifaca= indice de vegetacion de direfencia normalizada (NDVI) para la imagen landsat 2015

ndvifaca = vifaca(landsatp3, 5, 4) 
plot(ndvifaca, col=rev(terrain.colors(9)), main= "Landsat faca - NVDI")


# Seleccionando el shape del area de faca, cargandolo y recortandolo con respecto del NDVI de la imagen Landsat
NDVI1_faca = shapefile('C:/percepcionremota/ShpFaca/coberturatierrafaca18')
eNDVI1_faca = extent(NDVI1_faca)
NDVI_recorte_1 = crop(NDVI1_faca, eNDVI1_faca)
Puedes ver la variacion en el verdor desde la trama.
Una forma alternativa de lograr esto es asi

vi2_faca = function(x, y) {
  (x-y)/(x+y)
}

ndvi2_faca = overlay(landsatp3[[5]], landsatp3[[4]], fun = vi2_faca)
plot(ndvi2_faca, col=rev(terrain.colors(10)), main = "Landsat faca5 - NDVI v2")


# Seleccionando el shape del area de faca, cargandolo y recortandolo con respecto del NDVI de la imagen Landsat
NDVI2_faca = shapefile('C:/percepcionremota/ShpFaca/coberturatierrafaca18.shp')
eNDVI2_faca = extent(NDVI2_faca)
NDVI_recorte_2 = crop(NDVI2_faca, eNDVI2_faca)
plot(NDVI_recorte_2, col=rev(terrain.colors(10)), main = "Landsat faca - NDVI")

Pregunta 1 : Se utiliza el codigo anterioemente relacionado y se ajusta a la necesidad del proyecto para de esta forma calcular los indices y identificar i) agua y ii) acumulados. Sugerencia: Use el diagrama del perfil espectral para encontrar las bandas que tienen reflectancia maxima y minima para estas dos clases.
## s1 es solucion 1

ndvifaca_s1 = vifaca(landsatp3, 3, 6) 
plot(ndvifaca_s1, col=rev(terrain.colors(10)), main= "Landsat faca - NDWI")


vi2_faca_s1 = function(x, y) {
  (x-y)/(x+y)
}

ndvi2_faca_s1 = overlay(landsatp3[[3]], landsatp3[[6]], fun = vi2_faca_s1)
plot(ndvi2_faca_s1, col=rev(topo.colors(10)), main = "Landsat faca - NDWI")


### PARA INDICE DE AGUA DE DIFERENCIA NORMALIZADA - NDWI
# Seleccionando el shape del area de faca, cargandolo y recortandolo con respecto del NDwI de la imagen Landsat

NDWI_faca = shapefile('C:/percepcionremota/ShpFaca/coberturatierrafaca18.shp')
eNDWI = extent(NDWI_faca)
NDWI_recorte = crop(ndvi2_faca_s1, eNDWI)
plot(NDWI_recorte, col=rev(topo.colors(10)), main = "Landsat faca - NDWI")


### PARA INDICE INCORPORADO DE DIFERENCIA NORMALIZADA - NDBI

ndbi1_faca_s1 = overlay(landsatp3[[6]], landsatp3[[5]], fun = vi2_faca_s1)
plot(ndbi1_faca_s1, col=rev(heat.colors(10)), main = "Landsat faca - NDBI")


# Seleccionando el shape del area de faca, cargandolo y recortandolo con respecto del NDBI de la imagen Landsat

NDBI_faca = shapefile('C:/percepcionremota/ShpFaca/coberturatierrafaca18.shp')
eNDBI = extent(NDBI_faca)
NDBI_recorte = crop(ndbi1_faca_s1, eNDBI)
plot(NDBI_recorte, col=rev(heat.colors(10)), main = "Landsat faca - NDBI")

TITULO: HISTOGRAMA

Podemos explorar la distribucion de valores contenidos en nuestro raster utilizando la funcion -hist ()- que produce un histograma. Los histogramas a menudo son utiles para identificar valores atipicos y valores de datos incorrectos en nuestros datos raster

## ver histograma de datos
hist(NDBI_recorte,
     main= "Dsitribucion de valores del NDVI",
     xlab= "NDVI",
     ylab= "Frecuencia",
     col= terrain.colors(50),
     xlim= c (-0.1,0.7),
     breaks = 30,
     xaxt= 'n')

axis(side = 1, at=seq(-0.5,1,0.05), labels = seq(-0.5,1,0.05))

Nos referiremos a este histograma para la siguiente subseccion sobre umbralizacion.
Pregunta 2 : Haga histogramas de los valores que los indices de vegetacion desarrollaron en la pregunta 1

## respuesta a la pregunta 2: ver histograma de datos
hist(NDWI_recorte,
     main= "Dsitribucion de valores del NDWI",
     xlab= "NDWI",
     ylab= "Frecuencia",
     col= topo.colors(50),
     xlim= c (-0.5,0.3),
     breaks = 30,
     xaxt= 'n')

axis(side = 1, at=seq(-0.5,1,0.05), labels = seq(-0.5,1,0.05))


## respuesta a la pregunta 2: ver histograma de datos
hist(NDBI_recorte,
     main= "Dsitribucion de valores del NDBI",
     xlab= "NDBI",
     ylab= "Frecuencia",
     col= heat.colors(50),
     xlim= c (-0.5,0.3),
     breaks = 30,
     xaxt= 'n')

axis(side = 1, at=seq(-0.5,1,0.05), labels = seq(-0.5,1,0.05))

TITULO: UMBRALIZACION

Podemos aplicar reglas basicas para obtener una estimacion de la extension espacial de diferentes caracteristicas de la superficie terrestre. Tenga en cuenta que los valores NDVI estan estandarizados y varian entre -1 y +1. Los valores mas altos indican mas cobertura verde.
Las celdas con valores de NDVI superiores a 0.4 son definitivamente vegetacion. La siguiente operacion enmascara todas las celdas que tal vez no sean vegetacion.

veg = reclassify(NDBI_recorte, cbind(-Inf, 0.4, NA))
ning昼㹡n argumento finito para min; retornando Infningun argumento finito para max; retornando -Inf
plot(veg, main = "Vegetacion")
ning昼㹡n argumento finito para min; retornando Infningun argumento finito para max; retornando -Inf

Vamos a mapear el area que corresponde al pico entre 0.25 y 0.3 en el histograma NDVI.

land = reclassify(NDBI_recorte, c(-Inf, 0.25, NA, 0.25, 0.3, 1, 0.3, Inf, NA))
plot(land, main = " Que es esto?")

Estas pueden ser las areas abiertas. Puede trazar -land- sobre el -landsatFCC- raster original para obtener mas informacion


plotRGB(landsatFaca, r=7, g=6, b=4, axes = TRUE, stretch="lin", main = "faca composicion en falso color")
plot(land, add = TRUE, legend= FALSE)

plotRGB(landsatFaca, r=1, g=2, b=3, axes=TRUE, stretch="lin", main="Landsat False Color Composite")
plot(land, add=TRUE, legend=FALSE)

Tambien puede crear clases para diferentes cantidades de presencia de vegetacion.
vegc = reclassify(NDBI_recorte, c(-Inf,0.25,1, 0.25,0.3,2, 0.3,0.4,3, 0.4,0.5,4, 0.5,Inf, 5))
plot(vegc, col = rev(terrain.colors(4)), main = 'Umbral basado en NDVI')

Pregunta 3 : Es posible encontrar agua usando el umbral de NDVI o cualquier otro indice
# Umbral, en este caso para agua
agua = reclassify(NDBI_recorte, cbind(-Inf, 0.1, NA))
plot(agua, main = "Agua", col= topo.colors(10))


#Reclasifico el Umbral, en este caso para agua
aguac = reclassify(NDBI_recorte, c(-Inf,0.025,1, 0.025,0.03,2, 0.03,0.1,3, 0.1,0.2,4, 0.2,Inf, 5))
plot(aguac, col = rev(topo.colors(10)), main = 'Umbral basado en NDWI')

ANALISIS DE COMPONENTES PRINCIPALES

Los datos multiespectrales a veces se transforman para ayudar a reducir la dimensionalidad y el ruido en los datos. La transformacion de componentes principales es un metodo generico de reduccion de datos que se puede utilizar para crear algunas bandas no correlacionadas a partir de un conjunto mas grande de bandas correlacionadas.
Puede calcular el mismo numero de componentes principales que el numero de bandas de entrada. El primer componente principal (PC) explica el mayor porcentaje de varianza y otros PC explican la varianza adicional en orden decreciente.

set.seed(1)

sr = sampleRandom(landsatp3, 100000)
plot(sr[,c(4,5)], main = "NIR - ROJO")

Esto se conoce como diagrama de vegetacion y linea de suelo (igual que el diagrama de dispersion en la seccion anterior).
pca = prcomp(sr, scale = TRUE)
pca
Standard deviations (1, .., p=7):
[1] 2.59003689 0.44921866 0.27644178 0.09269922 0.06591655 0.02057198 0.01140578

Rotation (n x k) = (7 x 7):
                                                   PC1        PC2         PC3         PC4         PC5
LC08_L1TP_008057_20181230_20190130_01_T1_B1 -0.3828966  0.2384366 -0.13610614 -0.60007235 -0.32922500
LC08_L1TP_008057_20181230_20190130_01_T1_B2 -0.3817989  0.3228502 -0.06913742 -0.24378800 -0.12020857
LC08_L1TP_008057_20181230_20190130_01_T1_B3 -0.3821098  0.3131713 -0.02221730  0.21450895  0.09840963
LC08_L1TP_008057_20181230_20190130_01_T1_B4 -0.3789077  0.4004274  0.13575699  0.58234331  0.17008071
LC08_L1TP_008057_20181230_20190130_01_T1_B5 -0.3695465 -0.4480919 -0.74943217  0.08558584  0.29581948
LC08_L1TP_008057_20181230_20190130_01_T1_B6 -0.3741646 -0.5129561  0.25823666  0.29187454 -0.66453292
LC08_L1TP_008057_20181230_20190130_01_T1_B7 -0.3761344 -0.3414484  0.57396587 -0.32066662  0.55626948
                                                     PC6         PC7
LC08_L1TP_008057_20181230_20190130_01_T1_B1  0.127061460 -0.54165739
LC08_L1TP_008057_20181230_20190130_01_T1_B2 -0.488236233  0.65799701
LC08_L1TP_008057_20181230_20190130_01_T1_B3  0.781106318  0.29932697
LC08_L1TP_008057_20181230_20190130_01_T1_B4 -0.361200728 -0.42324897
LC08_L1TP_008057_20181230_20190130_01_T1_B5 -0.068502312 -0.03838947
LC08_L1TP_008057_20181230_20190130_01_T1_B6  0.011655828  0.05709746
LC08_L1TP_008057_20181230_20190130_01_T1_B7 -0.007700369 -0.01330462
screeplot(pca)


pci = predict(landsatp3, pca, index = 1:2)
plot(pci[[1]])


## Recortado a faca
pci_faca = shapefile('C:/percepcionremota/ShpFaca/coberturatierrafaca18.shp')
epci = extent(pci_faca)
pci_rec = crop(pci, epci)
plot(pci_rec, col=rev(terrain.colors(20)), main = "pci faca")

El primer componente principal resalta los limites entre las clases de uso de la tierra o los detalles espaciales, que es la informacion mas comun entre todas las longitudes de onda. Es dificil entender lo que destaca el segundo componente principal. Vamos a intentar umbral de nuevo:

landsatfcfaca = stack(b5, b4, b3)

## recortado a faca
pc2 = shapefile('C:/percepcionremota/ShpFaca/coberturatierrafaca18.shp')
epc2 = extent(pc2)
pc2_rec= crop(landsatfcfaca, epc2)

pc2 = reclassify(pci_rec  [[2]], c(-Inf,0,1,0,Inf,NA))
par(mfrow = c(1,2))
plotRGB(pc2_rec, r = 1, g = 2, b = 3, axes = TRUE, stretch = "lin", main = "FFC")
plotRGB(pc2_rec, r = 1, g = 2, b = 3, axes = TRUE, stretch = "lin", main = "FFC")
plot(pc2, legend = FALSE, add = TRUE)

Para obtener mas informacion acerca de la informacion contenida en las parcelas de vegetacion y lineas de suelo, lea este documento de -Gitelson et al- . Una extension de PCA en la teledeteccion se conoce como Transformacion -Tasseled-cap- .

TITULO: CAPITULO 2

En este capitulo exploramos la clasificacion no supervisada. Existen varios algoritmos de clasificación no supervisados, y la eleccion del algoritmo puede afectar los resultados. Exploraremos un solo algoritmo (k-means) para ilustrar el principio general
faca, Imagen LandSat 8, utilizando 7 bandas
library(raster)
landsat8 = stack(b2,b3,b4,b5,b6,b7)
names(landsat8) = c ('blue', 'green', 'red', 'NIR', 'SWIR1','SWIR2')
plot(landsat8)

Pregunta 1 : Haga una trama compuesta de 3 bandas de falso color de `` landsat8 ’’.

landsat8faca_imagen = stack(b5, b6, b4)
plotRGB(landsat8faca_imagen, axes=TRUE, stretch="lin", main="Landsat composicion de color falsa - Agua / Tierra")


# recortando a faca

facaextend = shapefile('C:/percepcionremota/ShpFaca/coberturatierrafaca18.shp')
ext = extent(facaextend)
landsatfcc = crop(landsat8faca_imagen, ext)
plotRGB(landsatfcc, axes = TRUE, stretch = "lin", main = "Landsat True Color Composite")

En la clasificacion no supervisada, utilizamos los datos de reflectancia, pero no proporcionamos ningun dato de respuesta (es decir, no identificamos ningun pixel como perteneciente a una clase en particular). Esto puede parecer extraño, pero puede ser util cuando no tenemos mucho conocimiento previo de un area de estudio. O si tiene un amplio conocimiento de la distribucion de las clases de interes de la cobertura del suelo, pero no tiene datos especificos
El algoritmo agrupa pixeles con caracteristicas espectrales similares en grupos.
Obtenga mas informacion sobre K-means y otros algoritmos supervisados sin supervision .
Realizaremos una clasificacion no supervisada en un subconjunto espacial de la capa -ndvi-. Aqui hay otra forma de calcular -ndvi-. En este caso, no usamos una funcion separada, sino una notacion algebraica directa.

ndviLandSat8=(landsat8[['NIR']]-landsat8[['red']])/(landsat8[['NIR']]+landsat8[['red']])
plot(ndviLandSat8)

Haremos un agrupamiento -kmeans- de los datos -ndvi-. Primero usamos -crop- para hacer un subconjunto espacial de -ndvi-, para permitir un procesamiento mas rapido (puede seleccionar cualquier -extent- usando la función -drawExtent()).

TITULO: CLASIFICACION KMEANS

landsat8faca_imagen = stack(b5, b6, b4)
plotRGB(landsat8faca_imagen, axes=TRUE, stretch="lin", main="Landsat composicion de color falsa - Agua / Tierra")


# recortando a faca

facaextend = shapefile('C:/percepcionremota/ShpFaca/coberturatierrafaca18.shp')
ext = extent(facaextend)
landsatfcc = crop(landsat8faca_imagen, ext)
plotRGB(landsatfcc, axes = TRUE, stretch = "lin", main = "Landsat True Color Composite")

NA
NA
NA
Tenga en cuenta que -getValues- convirtio el RasterLayer -ndvi- en una matriz (matriz). Ahora realizaremos el agrupamiento -kmeans- en la matriz e inspeccionaremos la salida.

ndviLandSat8=(landsat8[['NIR']]-landsat8[['red']])/(landsat8[['NIR']]+landsat8[['red']])
plot(ndviLandSat8)

-kmeans-devuelve un objeto con 9 elementos. La longitud del elemento -cluster- dentro -kmncluster- es 216567 que es igual a la longitud de -nr- creado desde -ndvi-. Los valores de celda de rango -kmncluster\(cluster- entre 1 a 10 correspondientes al numero de entrada del cluster que proporcionamos en la funcion -kmeans-. -kmncluster\)cluster- indica la etiqueta del cluster para el pixel correspondiente. Necesitamos convertir los valores -kmncluster$cluster- nuevamente a RasterLayer de la misma dimension que -ndvi-.
# Es importante configurar el generador de semillas porque `kmeans` inicia los centros en ubicaciones aleatorias, para eso utilizaremos set.seed
set.seed(99)

# Queremos crear 10 grupos, permitir 500 iteraciones, comenzar con 5 conjuntos aleatorios utilizando el método "Lloyd"
kmncluster = kmeans(na.omit(nr), centers = 10, iter.max = 500, nstart = 5, algorithm="Lloyd")
Error in na.omit(nr) : objeto 'nr' no encontrado
Podemos ver que -knr- es un RasterLayer, pero no sabemos que grupo (1-10) pertenece a que clase de cobertura terrestre (y si pertenece a una clase que reconoceriamos). Puede averiguarlo trazandolos lado a lado con capas de referencia y usando un color unico para cada grupo.
# Use el objeto ndvi (en este caso se llama "landsat8cropfaca") para establecer los valores del cluster en un nuevo raster
knr = setValues(landsatFacacrop, kmncluster$cluster)

# Tambien puedes hacerlo asi
knr= raster(landsatFacacrop)
values(knr) = kmncluster$cluster
knr
Si bien para otros fines suele ser mejor definir mas clases (y posiblemente fusionar clases mas adelante), una clasificacion simple como esta podria ser util, por ejemplo, fusionar los grupos 4 y 5 para construir una mascara de agua para el año 2015.
Puedes cambiar los colores en mi -mycolor-. Obtenga mas informacion sobre la seleccion de colores en R -aqui- y -aqui- .
Pregunta 2 : Grafique el RGB de 3 bandas de “landsat8” para el subconjunto (extensión “ext”) y el resultado de la agrupacion “kmeans” lado a lado y haga una tabla de cobertura del suelo para el uso del suelo Etiquetas para los grupos. Por ejemplo, los grupos 4 y 5 son agua.

landsat8p2 = stack(b6, b3, b2)
#plotRGB(landsat8p2, axes=TRUE, stretch="lin", main="Landsat composicion de color falsa ")

#crop landsat by the extent
landsatfacap2 = crop(landsat8p2, ext)
plotRGB(landsatfacap2, axes = TRUE, stretch = "lin", main = "Landsat FCC - faca")


# convertir el raster a vector / matriz
nr_p2=getValues(landsatfacap2)
str(nr_p2)
 int [1:300979, 1:3] 8933 9002 10072 10961 11390 12368 12708 13320 13510 13658 ...
 - attr(*, "dimnames")=List of 2
  ..$ : NULL
  ..$ : chr [1:3] "LC08_L1TP_008057_20181230_20190130_01_T1_B6" "LC08_L1TP_008057_20181230_20190130_01_T1_B3" "LC08_L1TP_008057_20181230_20190130_01_T1_B2"
# Es importante configurar el generador de semillas porque `kmeans` inicia los centros en ubicaciones aleatorias, para eso utilizaremos set.seed
set.seed (99)

# Queremos crear 10 grupos, permitir 500 iteraciones, comenzar con 5 conjuntos aleatorios utilizando el método "Lloyd"
kmncluster_p2 = kmeans(na.omit(nr_p2), centers = 10, iter.max = 500, nstart = 5, algorithm = "Lloyd")

# kmeans devuelve un objeto de la clase "kmeans"
str(kmncluster_p2)
List of 9
 $ cluster     : int [1:300979] 9 9 9 6 6 8 8 8 8 8 ...
 $ centers     : num [1:10, 1:3] 14704 14166 26021 19680 33698 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:10] "1" "2" "3" "4" ...
  .. ..$ : chr [1:3] "LC08_L1TP_008057_20181230_20190130_01_T1_B6" "LC08_L1TP_008057_20181230_20190130_01_T1_B3" "LC08_L1TP_008057_20181230_20190130_01_T1_B2"
 $ totss       : num 1.18e+13
 $ withinss    : num [1:10] 3.76e+10 9.01e+10 9.30e+10 9.32e+10 1.04e+11 ...
 $ tot.withinss: num 6e+11
 $ betweenss   : num 1.12e+13
 $ size        : int [1:10] 40372 16656 5230 6859 3016 64330 14028 67531 49945 33012
 $ iter        : int 115
 $ ifault      : NULL
 - attr(*, "class")= chr "kmeans"
# Use el objeto ndvi (en este caso se llama "landsatfacap2") para establecer los valores del cluster en un nuevo raster
knr_p2 = setValues(landsatfacap2, kmncluster_p2$cluster)

# Tambien puedes hacerlo asi
knr_p2= raster(landsatfacap2)
values(knr_p2) = kmncluster_p2$cluster
knr_p2
class      : RasterLayer 
dimensions : 511, 589, 300979  (nrow, ncol, ncell)
resolution : 30, 30  (x, y)
extent     : 564225, 581895, 526425, 541755  (xmin, xmax, ymin, ymax)
crs        : +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
source     : memory
names      : layer 
values     : 1, 10  (min, max)
#Defina un vector de color para 10 grupos (mas informacion sobre como configurar el color mas adelante)
#mycolor_p2 = c (topo.colors(10))
plot(knr_p2, main = 'Unsupervised classification', col = rev(topo.colors(10))) 

TITULO: CAPITULO 3

TITULO: CLASIFICACION SUPERVISADA

Aqui exploramos la clasificacion supervisada. Existen varios algoritmos de clasificacion supervisados, y la eleccion del algoritmo puede afectar los resultados. Aqui exploramos dos algoritmos relacionados (CART y RandomForest).
En la clasificacion supervisada, tenemos conocimiento previo sobre algunos de los tipos de cobertura del suelo mediante, por ejemplo, trabajo de campo, datos espaciales de referencia o interpretación de imagenes de alta resolucion (como las disponibles en los mapas de Google). Se identifican sitios especificos en el area de estudio que representan ejemplos homogeneos de estos tipos de cobertura terrestre conocidos. Estas areas se conocen comunmente como sitios de entrenamiento porque las propiedades espectrales de estos sitios se usan para entrenar el algoritmo de clasificacion.
Los siguientes ejemplos utilizan un clasificador de arboles de clasificacion y regresion (CART) (Breiman et al. 1984) ( lecturas adicionales para predecir las clases de cobertura del suelo en el area de estudio).
Realizaremos los siguientes pasos:
Genere sitios de muestra basados en un raster de referencia
Extraer valores de celda de datos de Landsat para los sitios de muestra
Entrenar al clasificador usando muestras de entrenamiento
Clasifique los datos de Landsat usando el modelo entrenado
Evaluar la precision del modelo.

TITULO: DATOS DE REFERENCIA

La National Land Cover Database 2011 (NLCD 2011) es un producto de cobertura terrestre para los EE. UU. NLCD es una base de datos de cobertura del suelo basada en Landsat de 30 m que abarca 4 épocas (1992, 2001, 2006 y 2011). NLCD 2011 se basa principalmente en una clasificación de árbol de decisión de datos Landsat de alrededor de 2011.
Puede encontrar los nombres de clase en NCLD 2011 (aquí) -[ https://www.mrlc.gov/nlcd11_leg.php ]-. Tiene dos pares de valores de clase y nombres que corresponden a los niveles de uso del suelo y el sistema de clasificacion de la cobertura del suelo. Estos niveles generalmente representan el nivel de complejidad, siendo el nivel I el mas simple con amplias categorias de cobertura del suelo. Lea este informe de Anderson et al. Para obtener mas informacion sobre este sistema de clasificacion de uso y cobertura de la tierra.

CAPITULO 3

CLASIFICACION SUPERVISADA

Aqui exploramos la clasificacion supervisada. Existen varios algoritmos de clasificacion supervisados, y la eleccion del algoritmo puede afectar los resultados. Aqui exploramos dos algoritmos relacionados (RandomForest y arbol de decision).

En la clasificacion supervisada, tenemos conocimiento previo sobre algunos de los tipos de cobertura del suelo mediante, por ejemplo, trabajo de campo, datos espaciales de referencia o interpretación de imagenes de alta resolucion (como las disponibles en los mapas de Google). Se identifican sitios especificos en el area de estudio que representan ejemplos homogeneos de estos tipos de cobertura terrestre conocidos. Estas areas se conocen comunmente como sitios de entrenamiento porque las propiedades espectrales de estos sitios se usan para entrenar el algoritmo de clasificacion.

# Directorio de trabajo

setwd("C:/percepcionremota/PR/imagenfaca/")

# Importar las imagenes
B2 = raster("LC08_L1TP_008057_20181230_20190130_01_T1_B2.TIF")
B3 = raster("LC08_L1TP_008057_20181230_20190130_01_T1_B3.TIF")
B4 = raster("LC08_L1TP_008057_20181230_20190130_01_T1_B4.TIF")
B5 = raster("LC08_L1TP_008057_20181230_20190130_01_T1_B5.TIF")
B6 = raster("LC08_L1TP_008057_20181230_20190130_01_T1_B6.TIF")
B7 = raster("LC08_L1TP_008057_20181230_20190130_01_T1_B7.TIF")

# Datos imagen MTL
dia <- 004
SUN_ELEVATION = 51.89134539

RADIANCE_MULT_BAND_2 = 1.3298E-02
RADIANCE_MULT_BAND_3 = 1.2254E-02
RADIANCE_MULT_BAND_4 = 1.0333E-02
RADIANCE_MULT_BAND_5 = 6.3236E-03
RADIANCE_MULT_BAND_6 = 1.5726E-03
RADIANCE_MULT_BAND_7 = 5.3006E-04

RADIANCE_ADD_BAND_2 = -66.49141
RADIANCE_ADD_BAND_3 = -61.27126
RADIANCE_ADD_BAND_4 = -51.66738
RADIANCE_ADD_BAND_5 = -31.61786
RADIANCE_ADD_BAND_6 = -7.86307
RADIANCE_ADD_BAND_7 = -2.65028

REFLECTANCE_MAXIMUM_BAND_2 = 1.210700
REFLECTANCE_MAXIMUM_BAND_3 = 1.210700
REFLECTANCE_MAXIMUM_BAND_4 = 1.210700
REFLECTANCE_MAXIMUM_BAND_5 = 1.210700
REFLECTANCE_MAXIMUM_BAND_6 = 1.210700
REFLECTANCE_MAXIMUM_BAND_7 = 1.210700

RADIANCE_MAXIMUM_BAND_2 = 805.01141
RADIANCE_MAXIMUM_BAND_3 = 741.81116
RADIANCE_MAXIMUM_BAND_4 = 625.53699
RADIANCE_MAXIMUM_BAND_5 = 382.79742
RADIANCE_MAXIMUM_BAND_6 = 95.19823
RADIANCE_MAXIMUM_BAND_7 = 32.08690

# Conversion
SUN_ELEVATION_R <- SUN_ELEVATION*pi/180  # radianes
d <- 1+0.0167*(sin((2*pi*(dia-93.5))/365)) # distancia del sol a la tierra

# Determinacion ESUN
ESUN2 <- pi*(d^2)*RADIANCE_MAXIMUM_BAND_2/REFLECTANCE_MAXIMUM_BAND_2
ESUN3 <- pi*(d^2)*RADIANCE_MAXIMUM_BAND_3/REFLECTANCE_MAXIMUM_BAND_3
ESUN4 <- pi*(d^2)*RADIANCE_MAXIMUM_BAND_4/REFLECTANCE_MAXIMUM_BAND_4
ESUN5 <- pi*(d^2)*RADIANCE_MAXIMUM_BAND_5/REFLECTANCE_MAXIMUM_BAND_5
ESUN6 <- pi*(d^2)*RADIANCE_MAXIMUM_BAND_6/REFLECTANCE_MAXIMUM_BAND_6
ESUN7 <- pi*(d^2)*RADIANCE_MAXIMUM_BAND_7/REFLECTANCE_MAXIMUM_BAND_7

# Elimar valores nulos
B2[B2==0] <- NA
B3[B3==0] <- NA
B4[B4==0] <- NA
B5[B5==0] <- NA
B6[B6==0] <- NA
B7[B7==0] <- NA

# Determinacion de radiancia sensor
L2 <- RADIANCE_MULT_BAND_2*B2 + RADIANCE_ADD_BAND_2
L3 <- RADIANCE_MULT_BAND_3*B3 + RADIANCE_ADD_BAND_3
L4 <- RADIANCE_MULT_BAND_4*B4 + RADIANCE_ADD_BAND_4
L5 <- RADIANCE_MULT_BAND_5*B5 + RADIANCE_ADD_BAND_5
L6 <- RADIANCE_MULT_BAND_6*B6 + RADIANCE_ADD_BAND_6
L7 <- RADIANCE_MULT_BAND_7*B7 + RADIANCE_ADD_BAND_7

# Determinacion de la radiancia minima
Lmin2 <- RADIANCE_MULT_BAND_2*min(getValues(B2), na.rm = TRUE)+ RADIANCE_ADD_BAND_2
Lmin3 <- RADIANCE_MULT_BAND_3*min(getValues(B3), na.rm = TRUE)+ RADIANCE_ADD_BAND_3
Lmin4 <- RADIANCE_MULT_BAND_4*min(getValues(B4), na.rm = TRUE)+ RADIANCE_ADD_BAND_4
Lmin5 <- RADIANCE_MULT_BAND_5*min(getValues(B5), na.rm = TRUE)+ RADIANCE_ADD_BAND_5
Lmin6 <- RADIANCE_MULT_BAND_6*min(getValues(B6), na.rm = TRUE)+ RADIANCE_ADD_BAND_6
Lmin7 <- RADIANCE_MULT_BAND_7*min(getValues(B7), na.rm = TRUE)+ RADIANCE_ADD_BAND_7

# Determinacion radiancia del objeto oscuro
LDOS2 <- 0.01*ESUN2*sin(SUN_ELEVATION_R)/pi*(d^2)
LDOS3 <- 0.01*ESUN3*sin(SUN_ELEVATION_R)/pi*(d^2)
LDOS4 <- 0.01*ESUN4*sin(SUN_ELEVATION_R)/pi*(d^2)
LDOS5 <- 0.01*ESUN5*sin(SUN_ELEVATION_R)/pi*(d^2)
LDOS6 <- 0.01*ESUN6*sin(SUN_ELEVATION_R)/pi*(d^2)
LDOS7 <- 0.01*ESUN7*sin(SUN_ELEVATION_R)/pi*(d^2)

# Determinar el efecto bruma
LP2 <- Lmin2-LDOS2
LP3 <- Lmin3-LDOS3
LP4 <- Lmin4-LDOS4
LP5 <- Lmin5-LDOS5
LP6 <- Lmin6-LDOS6
LP7 <- Lmin7-LDOS7

# Banda Reflectancia Superficie Dark Object Substrction (DOS)
RS_dos_B2 <- (pi*(L2-LP2)*(d)^2)/(ESUN2*sin(SUN_ELEVATION_R))
RS_dos_B3 <- (pi*(L3-LP3)*(d)^2)/(ESUN3*sin(SUN_ELEVATION_R))
RS_dos_B4 <- (pi*(L4-LP4)*(d)^2)/(ESUN4*sin(SUN_ELEVATION_R))
RS_dos_B5 <- (pi*(L5-LP5)*(d)^2)/(ESUN5*sin(SUN_ELEVATION_R))
RS_dos_B6 <- (pi*(L6-LP6)*(d)^2)/(ESUN6*sin(SUN_ELEVATION_R))
RS_dos_B7 <- (pi*(L7-LP7)*(d)^2)/(ESUN7*sin(SUN_ELEVATION_R))

# Combinacion de bandas
L8_B234567_RS_DOS <- stack(RS_dos_B2,RS_dos_B3,RS_dos_B4,RS_dos_B5,RS_dos_B6,RS_dos_B7)

# Exportacion imagen reflectancia Superfie DOS

writeRaster(L8_B234567_RS_DOS,"L8_B234567_RS_DOS1.tif", drivername="Gtiff", overwrite=TRUE)


## Tema: Temperatura de brillo de Landsat 8 TIRS


# Importar las imagenes
B10 = raster("LC08_L1TP_008057_20181230_20190130_01_T1_B10.TIF")
B11 = raster("LC08_L1TP_008057_20181230_20190130_01_T1_B11.TIF")

# K imagen Landsat 8 TIRS
K1_CONSTANT_BAND_10 = 774.8853
K2_CONSTANT_BAND_10 = 1321.0789
K1_CONSTANT_BAND_11 = 480.8883
K2_CONSTANT_BAND_11 = 1201.1442

# Datos de radiancia del sensor imagen Landsat
RADIANCE_MULT_BAND_10 = 3.3420E-04
RADIANCE_MULT_BAND_11 = 3.3420E-04

RADIANCE_ADD_BAND_10 = 0.10000
RADIANCE_ADD_BAND_11 = 0.10000

# Elimar valores nulos
B10[B10==0] <- NA
B11[B11==0] <- NA

# Determinancion de la Radianca
L10 <- RADIANCE_MULT_BAND_10*(na.omit(B10)) + RADIANCE_ADD_BAND_10
L11 <- RADIANCE_MULT_BAND_11*(na.omit(B11)) + RADIANCE_ADD_BAND_11


# Determinacion Temperatura de brillo Celsius
TB_B10 <- K2_CONSTANT_BAND_10/log(K1_CONSTANT_BAND_10/L10+1) - 273.15
TB_B11 <- K2_CONSTANT_BAND_11/log(K1_CONSTANT_BAND_11/L11+1) - 273.15

# Exportacion imagen Temperatura de brillo Celsius
writeRaster(TB_B10,"L8_TB_B10.tif", drivername="Gtiff",overwrite=TRUE)
writeRaster(TB_B11,"L8_TB_B11.tif", drivername="Gtiff",overwrite=TRUE)

L8_B234567_RS_DOS_py = projectRaster(L8_B234567_RS_DOS, crs = "+init=epsg:32618")

##Clasificacion supervisada con Random Forest - Landsat 8 OLI

# Agregar raster multibandas de landsat 8
L8_faca <- stack("clip_L8_B234567_RS_DOS_py.tif")
Error in .local(.Object, ...) : 

Error in .rasterObjectFromFile(x, objecttype = "RasterBrick", ...) : 
  Cannot create a RasterLayer object from this file. (file does not exist)

## Clasificacion supervisada con Decision_Tree - Landsat 8 OLI

# Agregar raster multibandas de landsat 8
L8_2015 <- stack("clip_L8_B234567_RS_DOS_py.tif")
Error in .local(.Object, ...) : 

Error in .rasterObjectFromFile(x, objecttype = "RasterBrick", ...) : 
  Cannot create a RasterLayer object from this file. (file does not exist)
LS0tDQp0aXRsZTogIioqSW1hZ2VuIGZhY2EgTGFuZFNhdCA4KioiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyMjIyAqKkxhbmRzYXQgOCwgTGEgaW1hZ2VuIGNvcnJlc3BvbmRlIGFsIG11bmljaXBpbyBkZSBmYWNhdGF0aXZhLCBkZXBhcnRhbWVudG8gZGUgY3VuZGluYW1hcmNhLCBjb2xvbWJpYSwgIGNvbiBmcmVjaGEgZGUgMzAgZGUgZGljaWVtYnJhIGRlbCAyMDE4KioNCg0KDQojIyMjIyBTZWxlY2Npb25hciBsYSBjYXJwZXRhIGRlIGRlc3Rpbm8gDQoNCg0KYGBge3J9DQojICJDOlxwZXJjZXBjaW9ucmVtb3RhXFBSXGltYWdlbmZhY2EiDQpnZXR3ZCgpDQpgYGANCg0KDQojIyMjIENyZWFyIG9iamV0b3MgUmFzdGVyTGF5ZXIgcGFyYSBjYXBhcyBpbmRpdmlkdWFsZXMgTGFuZHNhdCAoYmFuZGFzKSB5IGxhcyBsbGFtYW1vcyBlbnJ1dGFuZG9sYXMgY29tbyBwb3IgZWplbXBsbyAoJy4vZGF0YS9MQzA4X0wxVFBfMDA4MDU3XzIwMTgwMzE3XzIwMTgwNDAzXzAxX1QxX0IyLnRpZicpDQoNCg0KDQojIyMjIyBJTlRST0RVQ0NJT04NCg0KIyMjI1BhcmEgcmVhbGl6YXIgcHJvY2VzYW1pZW50byBkZSBpbWFnZW5lcyBzYXRlbGl0YWxlcyBlbmNvbnRyYW1vcyBkaXZlcnNhcyBhbHRlcm5hdGl2YXMgZW4gbGEgYWN0dWFsaWRhZCwgeWEgc2VhIGNvbiBzb2Z0d2FyZSBsaWJyZSBvIGNvbWVyY2lhbCwgc2UgYWZyaW1hIHF1ZSAgb3BjaW9uZXMgc29uIGFsdGFtZW50ZSBjb21wZXRpdGl2YXMgeSBwb3NlZW4gZ3JhbiBjYXBhY2lkYWQgZGUgcHJvY2VzYW1pZW50by4gUGFyYSBlc3RlIGVzdHVkaW8gc2UgdXRpbGl6YXJvbiBSLCBSc3R1ZGlvLCBxdWUgcGVybWl0ZW4gcmVhbGl6YXIgdW5hIGNsYXNpZmljYWNpw7NuIGRlIGltYWdlbmVzIG5vIHN1cGVydmlzYWRhIHkgc3VwZXJ2aXNhZGEsIEFyY0dJUyBwYXJhIGxhIGVsYWJvcmFjaW9uIGRlIHNoYXBlLCBjb21vIG90cm9zIHByb2Nlc29zIHF1ZSBzZSBkZXNhcnJvbGxhcmFuIGR1cmFudGUgZXN0ZSBlc3R1ZGlvLCAgZXhwbG9yYW5kbyBhc3BlY3RvcyAgYXNwZWN0b3MgdMOpY25pY29zLg0KDQojIyMjRGVudHJvIGRlbCBwcm95ZWN0byBwb2RlbW9zIHJlc2FsdGFyIGFsZ3VuYXMgZmFzZXMgdSBvcGVyYWNpb25lcyBjb21vIGVsIHByZXByb2Nlc2FkbyBpbXBsZW1lbnRhZG8gYSBsYSBpbWFnZW4gc2F0ZWxpdGFsIGRvbmRlIHNlIGhhbiBhcGxpY2FkbyB1bmEgc2VyaWUgZGUgdHJhbnNmb3JtYWNpb25lcywgbGEgYXBsaWNhY2nDs24gZGUgdMOpY25pY2FzIGRlIGNsYXNpZmljYWNpw7NuIHN1cGVydmlzYWRhIG1lZGlhbnRlIGxhIHJlYWxpemFjacOzbiBkZSBlbnRyZW5hbWllbnRvIHkgdGVzdGVvLCB1c2FuZG8gY29tbyBhcmVhIGRlIGludGVyZXMgZWwgbXVuaWNpcGlvIGRlIEZhY2F0YXRpdmEuDQoNCiMjIyMjICoqSU1QT1JUQU5URTogaW5zdGFsYXIgcGFxdWV0ZXM6IHJhc3Rlciwgc3AsIHJnZGFsKioNCg0KIyMjIyBTZSBpbXBsZW1lbnTDsyAgcHJpbmNpcGFsbWVudGUgdW4gc3ViY29uanVudG8gZXNwYWNpYWwgZGUgdW5hIGVzY2VuYSBMYW5kc2F0IDggZGVsIG11bmljaXBpcG8gZGUgRmFjYXRhdGl2YSwgQ3VuZGluYW1hcmNhLCBDb2xvbWJpYQ0KDQoNCg0KIyMgKipUSVRVTE86IENBUElUVUxPIDEqKg0KDQoNCg0KIyMjICoqVElUVUxPOiBQUk9QSUVEQURFUyBERSBJTUFHRU4qKg0KDQoNCiMjIyMjIFNlIGNyZWFyb25yIG9iamV0b3MgUmFzdGVyTGF5ZXIgcGFyYSBjYXBhcyBpbmRpdmlkdWFsZXMgTGFuZHNhdCAoYmFuZGFzKQ0KDQoNCmBgYHtyfQ0KbGlicmFyeShyYXN0ZXIpDQojIENvc3RlcmENCmIxID0gcmFzdGVyKCcuL0xDMDhfTDFUUF8wMDgwNTdfMjAxODEyMzBfMjAxOTAxMzBfMDFfVDFfQjEuVElGJykNCiMgQXp1bA0KYjIgPSByYXN0ZXIoJy4vTEMwOF9MMVRQXzAwODA1N18yMDE4MTIzMF8yMDE5MDEzMF8wMV9UMV9CMi5USUYnKQ0KIyBWZXJkZQ0KYjMgPSByYXN0ZXIoJy4vTEMwOF9MMVRQXzAwODA1N18yMDE4MTIzMF8yMDE5MDEzMF8wMV9UMV9CMy5USUYnKQ0KIyBSb2pvDQpiNCA9IHJhc3RlcignLi9MQzA4X0wxVFBfMDA4MDU3XzIwMTgxMjMwXzIwMTkwMTMwXzAxX1QxX0I0LlRJRicpDQojIEluZnJhcm9qbyBjZXJjYW5vIChOSVIpDQpiNSA9IHJhc3RlcignLi9MQzA4X0wxVFBfMDA4MDU3XzIwMTgxMjMwXzIwMTkwMTMwXzAxX1QxX0I1LlRJRicpDQojIEluZnJhcnJvam8gZGUgT25kYSBDb3J0YSAxIChTV0lSIDEpDQpiNiA9IHJhc3RlcignLi9MQzA4X0wxVFBfMDA4MDU3XzIwMTgxMjMwXzIwMTkwMTMwXzAxX1QxX0I2LnRpZicpDQojIEluZnJhcnJvam8gZGUgT25kYSBDb3J0YSAyIChTV0lSIDIpDQpiNyA9IHJhc3RlcignLi9MQzA4X0wxVFBfMDA4MDU3XzIwMTgxMjMwXzIwMTkwMTMwXzAxX1QxX0I3LnRpZicpDQojIFBhbmNyb21hdGljYQ0KYjggPSByYXN0ZXIoJy4vTEMwOF9MMVRQXzAwODA1N18yMDE4MTIzMF8yMDE5MDEzMF8wMV9UMV9COC50aWYnKQ0KIyBDaXJyb3MgKENpcnJ1cykNCmI5ID0gcmFzdGVyKCcuL0xDMDhfTDFUUF8wMDgwNTdfMjAxODEyMzBfMjAxOTAxMzBfMDFfVDFfQjkudGlmJykNCiMgU2Vuc29yIFTDqXJtaWNvIEluZnJhcnJvam8gMSAoVElSUyAxKQ0KYjEwID0gcmFzdGVyKCcuL0xDMDhfTDFUUF8wMDgwNTdfMjAxODEyMzBfMjAxOTAxMzBfMDFfVDFfQjEwLnRpZicpDQojIFNlbnNvciBUw6lybWljbyBJbmZyYXJyb2pvIDIgKFRJUlMgMikNCmIxMSA9IHJhc3RlcignLi9MQzA4X0wxVFBfMDA4MDU3XzIwMTgxMjMwXzIwMTkwMTMwXzAxX1QxX0IxMS50aWYnKQ0KDQpgYGANCg0KDQojIyMjIyBBIGNvbnRpbnVhY2nDs24gcG9kZW1vcyBpbXByaW1pciBsYXMgdmFyaWFibGVzIGEgdmVyaWZpY2FyLCBwb3IgZWplbXBsbyBjb24gbGEgYmFuZGEgZG9zIChiMik6DQoNCg0KYGBge3J9DQpiMg0KYGBgDQoNCg0KIyMjIyMgUG9kZW1vcyBvYnNlcnZhciBsYSByZXNvbHVjaW9uIGVzcGFjaWFsLCBsYSBleHRlbnNpb24sIGVsIG51bWVybyBkZSBjYXBhcywgZWwgc2lzdGVtYSBkZSByZWZlcmVuY2lhIGRlIGNvb3JkZW5hZGFzIHkgbWFzLg0KDQoNCiMjIyAqKlRJVFVMTzogSU5GT1JNQUNJT04gREUgSU1BR0VOIFkgRVNUQURJU1RJQ0FTKioNCg0KDQojIyMgQSBjb250aW51YWNpb24gc2UgcHJlc2VudGFuIGxhcyBkaWZlcmVudGVzIHByb3BpZWRhZGVzIGRlc2RlIHVuIG9iamV0byBSYXN0ZXINCg0KDQpgYGB7cn0NCiMgU2lzdGVtYSBkZSBjb29yZGVuYWRhcyBkZSByZWZlcmVuY2lhIChDUlMpDQpjcnMoYjIpDQojIE51bWVybyBkZSBjZWxkYXMsIGZpbGFzLCBjb2x1bW5hcw0KbmNlbGwoYjIpDQojIERpbWVuc2lvbg0KZGltKGIyKQ0KIyBSZXNvbHVjaW9uIGVzcGFjaWFsDQpyZXMoYjIpDQojIE51bWVybyBkZSBiYWRhcyANCm5sYXllcnMoYjIpDQojIMK/TGFzIGJhbmRhcyB0aWVuZW4gbGEgbWlzbWEgZXh0ZW5zaW9uLCBudW1lcm8gZGUgZmlsYXMgeSBjb2x1bW5hcywgcHJveWVjY2lvbiwgcmVzb2x1Y2lvbiB5IG9yaWdlbj8NCmNvbXBhcmVSYXN0ZXIoYjIsYjMpDQojIyBbMV0gVFJVRQ0KYGBgDQoNCg0KIyMjIyMgUHVlZGUgY3JlYXIgdW4gUmFzdGVyU3RhY2sgY29uIGxhIG1pc21hIGV4dGVuc2nDs24geSByZXNvbHVjacOzbiBlc3BhY2lhbC4gU2UgcHVlZGUgY3JlYXIgdW4gUmFzdGVyU3RhY2sgYSBwYXJ0aXIgZGUgb2JqZXRvcyBSYXN0ZXJMYXllciwgbyBkZSBhcmNoaXZvcyByw6FzdGVyLCBvIGFtYm9zLg0KDQoNCg0KYGBge3J9DQojIENyZWFtb3MgZWwgUmFzdGVyU3RhY2sNCnMyMDE1ID0gc3RhY2soYjUsIGI0LCBiMykNCg0KIyBSZXZpc2Ftb3MgbGFzIHByb3BpZWRhZGVzIGRlbCBSYXN0ZXJTdGFjaw0KczIwMTUNCiMjIGNsYXNzICAgICAgOiBSYXN0ZXJTdGFjaw0KIyMgZGltZW5zaW9ucyA6IDc3NDEsIDc1ODEsIDU4Njg0NTIxLCAzICAobnJvdywgbmNvbCwgbmNlbGwsIG5sYXllcnMpDQojIyByZXNvbHV0aW9uIDogMzAsIDMwICAoeCwgeSkNCiMjIGV4dGVudCAgICAgOiA0NDYwODUsIDY3MzUxNSwgMzYyOTg1LCA1OTUyMTUgICh4bWluLCB4bWF4LCB5bWluLCB5bWF4KQ0KIyMgY3JzICAgICAgICA6ICsrcHJvaj11dG0gK3pvbmU9MTggK2RhdHVtPVdHUzg0ICt1bml0cz1tICtub19kZWZzICtlbGxwcz1XR1M4NCArdG93Z3M4ND0wLDAsMA0KIyMgbmFtZXMgICAgICA6IExDMDhfTDFUUF8wMDgwNTdfMjAxODAzMTdfMjAxODA0MDNfMDFfVDFfQjUsIExDMDhfTDFUUF8wMDgwNTdfMjAxODAzMTdfMjAxODA0MDNfMDFfVDFfQjQsICAgICBMQzA4X0wxVFBfMDA4MDU3XzIwMTgwMzE3XzIwMTgwNDAzXzAxX1QxX0IzDQojIyBtaW4gdmFsdWVzIDogIDAsIDAsIDAgDQojIyBtYXggdmFsdWVzIDogIDY1NTM1LCA2NTUzNSwgNjU1MzUNCmBgYA0KDQoNCiMjIyMjIFRhbWJpZW4gcHVlZGUgY3JlYXIgZWwgUmFzdGVyU3RhY2sgdXNhbmRvIGxvcyBub21icmVzIGRlIGFyY2hpdm8gDQoNCg0KYGBge3J9DQoNCiMgUHJpbWVybyBjcmVhbW9zIHVuYSBsaXN0YSBkZSBsb3MgcmFzdGVyIGxheWVycyB1c2FuZG86DQoNCmZpbGVuYW1lcyA9IHBhc3RlMCgnLi9MQzA4X0wxVFBfMDA4MDU3XzIwMTgxMjMwXzIwMTkwMTMwXzAxX1QxX0InLCAxOjcsICIudGlmIikNCmZpbGVuYW1lcw0KDQojIyBbMV0gIi4vZGF0YTIwMTUvTEMwOF9MMVRQXzAwODA1N18yMDE1MDEwNF8yMDE3MDQxNV8wMV9UMV9CMS50aWYiIA0KIyMgWzJdICIuL2RhdGEyMDE1L0xDMDhfTDFUUF8wMDgwNTdfMjAxNTAxMDRfMjAxNzA0MTVfMDFfVDFfQjIudGlmIiANCiMjIFszXSAiLi9kYXRhMjAxNS9MQzA4X0wxVFBfMDA4MDU3XzIwMTUwMTA0XzIwMTcwNDE1XzAxX1QxX0IzLnRpZiIgDQojIyBbNF0gIi4vZGF0YTIwMTUvTEMwOF9MMVRQXzAwODA1N18yMDE1MDEwNF8yMDE3MDQxNV8wMV9UMV9CNC50aWYiIA0KIyMgWzVdICIuL2RhdGEyMDE1L0xDMDhfTDFUUF8wMDgwNTdfMjAxNTAxMDRfMjAxNzA0MTVfMDFfVDFfQjUudGlmIiANCiMjIFs2XSAiLi9kYXRhMjAxNS9MQzA4X0wxVFBfMDA4MDU3XzIwMTUwMTA0XzIwMTcwNDE1XzAxX1QxX0I2LnRpZiIgDQojIyBbN10gIi4vZGF0YTIwMTUvTEMwOF9MMVRQXzAwODA1N18yMDE1MDEwNF8yMDE3MDQxNV8wMV9UMV9CNy50aWYiIA0KDQojIyMjIyMgbm8gbGVlIGxhIGJhbmRhIDggcG9ycXVlIHRpZW5lIGRpZmVyZW50ZSBleHRlbnNpb24sIGxhIGJhbmRhIDkgZXMgY2lycm8sIHkgbGFzIGJhbmRhcyAxMCB5IDExIHNvbiB0ZXJtYWxlcw0KDQojIyBbOF0gIi4vZGF0YTIwMTUvTEMwOF9MMVRQXzAwODA1N18yMDE1MDEwNF8yMDE3MDQxNV8wMV9UMV9COC50aWYiIA0KIyMgWzldICIuL2RhdGEyMDE1L0xDMDhfTDFUUF8wMDgwNTdfMjAxNTAxMDRfMjAxNzA0MTVfMDFfVDFfQjkudGlmIiANCiMjIFsxMF0gIi4vZGF0YTIwMTUvTEMwOF9MMVRQXzAwODA1N18yMDE1MDEwNF8yMDE3MDQxNV8wMV9UMV9CMTAudGlmIg0KIyMgWzExXSAiLi9kYXRhMjAxNS9MQzA4X0wxVFBfMDA4MDU3XzIwMTUwMTA0XzIwMTcwNDE1XzAxX1QxX0IxMS50aWYiDQoNCiMgQ3JlbyBlbCBSYXN0ZXJTdGFjayBkZSBsYXMgYmFuZGFzIHNlbGVjY2lvbmFkYXMsIGVzIGRlY2lyICBkZSBsYSAxIGEgbGEgIDcgKDE6NykNCmxhbmRzYXRGYWNhID0gc3RhY2sgKGZpbGVuYW1lcykNCmxhbmRzYXRGYWNhDQoNCiMjIGNsYXNzICAgICAgOiBSYXN0ZXJTdGFjaw0KIyMgZGltZW5zaW9ucyA6IDc3NDEsIDc1ODEsIDU4Njg0NTIxLCA3ICAobnJvdywgbmNvbCwgbmNlbGwsIG5sYXllcnMpDQojIyByZXNvbHV0aW9uIDogMzAsIDMwICAoeCwgeSkNCiMjIGV4dGVudCAgICAgOiA0NDYwODUsIDY3MzUxNSwgMzYyOTg1LCA1OTUyMTUgICh4bWluLCB4bWF4LCB5bWluLCB5bWF4KQ0KIyMgY3JzICAgICAgICA6ICtwcm9qPXV0bSArem9uZT0xMCArZGF0dW09V0dTODQgK3VuaXRzPW0gK25vX2RlZnMgK2VsbHBzPVdHUzg0ICt0b3dnczg0PTAsMCwwDQojIyBuYW1lcyAgICAgIDogTEMwOF9MMVRQLy81XzAxX1QxX0IxLCBMQzA4X0wxVFAvLzVfMDFfVDFfQjIsIExDMDhfTDFUUC8vNV8wMV9UMV9CMywgTEMwOF9MMVRQLy81XzAxX1QxX0I0LCBMQzA4X0wxVFAvLzVfMDFfVDFfQjUsIExDMDhfTDFUUC8vNV8wMV9UMV9CNiwgTEMwOF9MMVRQLy81XzAxX1QxX0I3DQojIyBtaW4gdmFsdWVzIDogICAgICAgICAgICAgICAgICAgICAwLCAgICAgICAgICAgICAgICAgICAgIDAsICAgICAgICAgICAgICAgICAgICAgMCwgICAgICAgICAgICAgICAgICAgICAwLCAgICAgICAgICAgICAgICAgICAgIDAsICAgICAgICAgICAgICAgICAgICAgMCwgICAgICAgICAgICAgICAgICAgICAwIA0KIyMgbWF4IHZhbHVlcyA6ICAgICAgICAgICAgICAgICA2NTUzNSwgICAgICAgICAgICAgICAgIDY1NTM1LCAgICAgICAgICAgICAgICAgNjU1MzUsICAgICAgICAgICAgICAgICA2NTUzNSwgICAgICAgICAgICAgICAgIDY1NTM1LCAgICAgICAgICAgICAgICAgNjU1MzUsICAgICAgICAgICAgICAgICA2NTUzNSANCg0KDQpgYGANCg0KDQojIyMjIyBBcnJpYmEgY3JlYW1vcyB1biBSYXN0ZXJTdGFjayBjb24gMTEgY2FwYXMuIExhcyBjYXBhcyByZXByZXNlbnRhbiBsYSBpbnRlbnNpZGFkIGRlIGxhIHJlZmxleGlvbiBlbiBsYXMgc2lndWllbnRlcyBsb25naXR1ZGVzIGRlIG9uZGE6IFVsdHJhIGF6dWwsIGF6dWwsIHZlcmRlLCByb2pvLCBpbmZyYXJyb2pvIGNlcmNhbm8gKE5JUiksIGluZnJhcnJvam8gZGUgb25kYSBjb3J0YSAoU1dJUikgMSwgaW5mcmFycm9qbyBkZSBvbmRhIGNvcnRhIChTV0lSKSAyLCBwYW5jcm9tYXRpY28sIGNpcnJvLCBpbmZyYXJyb2pvIHRlcm1pY28gKFRJUlMpIDEsIEluZnJhcnJvam8gdGVybWljbyAoVElSUykgMi4gTm8gdXRpbGl6YXJlbW9zIGxhcyB1bHRpbWFzIGN1YXRybyBjYXBhcyB5IHZlcmEgY29tbyBlbGltaW5hcmxhcyBlbiBsYXMgc2lndWllbnRlcyBzZWNjaW9uZXMuDQoNCg0KIyMjICoqVElUVUxPOiBCQU5EQSBVTklDQSBZIE1BUEFTIENPTVBVRVNUT1MqKg0KDQoNCiMjIyMjIFB1ZWRlIHRyYXphciBjYXBhcyBpbmRpdmlkdWFsZXMgZGUgdW4gUmFzdGVyU3RhY2sgZGUgdW5hIGltYWdlbiBtdWx0aWVzcGVjdHJhbC4NCg0KDQpgYGB7cn0NCiMgVHJhemFuZG8gY2FwYXMgaW5kaXZ1ZHVhbGVzIChhWlVMLCBWRVJERSwgUk9KTywgSU5GUkFST0pPIENFUkNBTk8pIGEgcGFydGlyIGRlbCBSYXN0ZXJTdGFjaw0KcGFyKG1mcm93ID0gYygyLDIpKQ0KcGxvdChiMiwgbWFpbiA9ICJBenVsIiwgY29sID0gZ3JheSgwOjEwMCAvIDEwMCksIGF4ZXMgPSBGQUxTRSkNCnBsb3QoYjMsIG1haW4gPSAiVmVyZGUiLCBjb2wgPSBncmF5KDA6MTAwIC8gMTAwKSwgYXhlcyA9IEZBTFNFKQ0KcGxvdChiNCwgbWFpbiA9ICJSb2pvIiwgY29sID0gZ3JheSgwOjEwMCAvIDEwMCksIGF4ZXMgPSBGQUxTRSkNCnBsb3QoYjUsIG1haW4gPSAiTklSIiwgY29sID0gZ3JheSgwOjEwMCAvIDEwMCksIGF4ZXMgPSBGQUxTRSkNCg0KYGBgDQoNCg0KIyMjIyMgRW4gbGEgaW1hZ2VuIGFudGVyaW9yIHBvZGVtb3Mgb2JzZXJ2YXIgcXVlIGxzIGxleWVuZGFzIFB1ZWRlbiB2YXJpYXIgZW50cmUgMCB5IDEuICBsYXMgZGlmZXJlbnRlcyBjYXJhY3RlcmlzdGljYXMgZGUgbGEgc3VwZXJmaWNpZSByZWZsZWphbiBsYSByYWRpYWNpb24gc29sYXIgaW5jaWRlbnRlIGRlIG1hbmVyYSBkaWZlcmVudGUsIGVzdG8gc2Ugb2JzZXJ2YSBlbiBsYSBpbWFnZW4gZW4gbGEgZGlmZXJlbmNpYWwgZGVsIHRvbm8gZGUgZ3Jpc2VzLCBDYWRhIGNhcGEgcmVwcmVzZW50YSBsYSBjYW50aWRhZCBkZSByYWRpYWNpb24gc29sYXIgaW5jaWRlbnRlIHF1ZSBzZSByZWZsZWphIHBhcmEgdW4gcmFuZ28gZGUgbG9uZ2l0dWQgZGUgb25kYSBwYXJ0aWN1bGFyLiBQb3IgZWplbXBsbywgbGEgdmVnZXRhY2lvbiByZWZsZWphIG1hcyBlbmVyZ2lhIGVuIE5JUiBxdWUgb3RyYXMgbG9uZ2l0dWRlcyBkZSBvbmRhIHksIHBvciBsbyB0YW50bywgcGFyZWNlIG1hcyBicmlsbGFudGUuIFBvciBlbCBjb250cmFyaW8sIGVsIGFndWEgYWJzb3JiZSBsYSBtYXlvciBwYXJ0ZSBkZSBsYSBlbmVyZ2lhIGVuIGxhIGxvbmdpdHVkIGRlIG9uZGEgTklSIHkgcGFyZWNlIG9zY3VyYS4NCg0KDQojIyMjI0N1YW5kbyBsYXMgYmFuZGFzIGVzcGVjdHJhbGVzIGRlIHVuIGRldGVybWluYWRvIHNlbnNvciBjb2luY2lkZW4gZXhhY3RhbWVudGUgY29uIGxhcyBsb25naXR1ZGVzIGRlIG9uZGEgZGUgbG9zIGNvbG9yZXMgUm9qbywgVmVyZGUgeSBBenVsLCBsYSBjb21wb3NpY2nDs24gcmVzdWx0YW50ZSBzZSBjb25vY2UgY29tbyDigJxjb2xvciB2ZXJkYWRlcm/igJ0uIExhcyBjb21wb3NpY2lvbmVzIFJHQiBzZSB1dGlsaXphbiBwYXJhIGFuYWxpemFyIHkgcmVzYWx0YXIgZGUgZm9ybWEgdmlzdWFsIGRldGVybWluYWRhIGluZm9ybWFjacOzbiBkZSBsYSBpbWFnZW4sIGNvbW8gLG8gcHJlc2VudGEgbGEgc2lndWllbnRlIGltYWdlbjoNCg0KDQoNCg0KYGBge3J9DQojIFBsb3RlYW1vcyBsYSBjb21iaW5hY2lvbiBkZSBiYW5kYXMgNCwgMyB5IDIgcXVlIHBhcmEgTDggZXMgY29sb3IgdmVyZGFkZXJvDQpsYW5kc2F0UkdCRmFjYSA8LSBzdGFjayhiNCwgYjMsIGIyKQ0KcGxvdFJHQihsYW5kc2F0UkdCRmFjYSwgYXhlcyA9IFRSVUUsIHN0cmV0Y2ggPSAibGluIiwgbWFpbiA9ICJMYW5kc2F0IFRydWUgQ29sb3IgQ29tcG9zaXRlIikNCmBgYA0KIyMjIyMgUGFyYSBoYWNlciB1bmEgaW1hZ2VuIGRlICJjb2xvciB2ZXJkYWRlcm8gKG8gbmF0dXJhbCkiLCBlcyBkZWNpciwgYWxnbyBxdWUgc2UgcGFyZWNlIGEgdW5hIGZvdG9ncmFmaWEgbm9ybWFsICh2ZWdldGFjaW9uIGVuIHZlcmRlLCBhenVsIGFndWEsIGV0Yy4pLCBuZWNlc2l0YW1vcyBiYW5kYXMgZW4gbGFzIHJlZ2lvbmVzIHJvamEsIHZlcmRlIHkgYXp1bC4gUGFyYSBlc3RhIGltYWdlbiBMYW5kc2F0LCBzZSBwdWVkZW4gdXNhciBsYXMgYmFuZGFzIDQgKHJvam8pLCAzICh2ZXJkZSkgeSAyIChhenVsKS4gRWwgbWV0b2RvIC0gcGxvdFJHQiAtICBzZSBwdWVkZSB1dGlsaXphciBwYXJhIGNvbWJpbmFybG9zIGVuIHVuIHNvbG8gY29tcHVlc3RvLiBUYW1iaWVuIHB1ZWRlIHByb3BvcmNpb25hciBhcmd1bWVudG9zIGFkaWNpb25hbGVzIC0gcGxvdFJHQiAtIHBhcmEgbWVqb3JhciBsYSB2aXN1YWxpemFjaW9uIChwb3IgZWplbXBsbywgdW4gZXN0aXJhbWllbnRvIGxpbmVhbCBkZSBsb3MgdmFsb3JlcywgdXRpbGl6YW5kbyApLi0gc3RyZWN0aCA9ICJsaW4iIC0NCg0KIyMjIyMgRWwgY29tcHVlc3RvIGRlIGNvbG9yIHZlcmRhZGVybyByZXZlbGEgbXVjaG8gbWFzIHNvYnJlIGVsIHBhaXNhamUgcXVlIGxhcyBpbWFnZW5lcyBncmlzZXMgYW50ZXJpb3Jlcy4gT3RybyBtZXRvZG8gcG9wdWxhciBkZSB2aXN1YWxpemFjaW9uIGRlIGltYWdlbmVzIGVuIGxhIHRlbGVkZXRlY2Npb24gZXMgbGEgaW1hZ2VuIGNvbm9jaWRhIGNvbW8gImNvbG9yIGZhbHNvIiBlbiBsYSBxdWUgc2UgY29tYmluYW4gbGFzIGJhbmRhcyBOSVIsIHJvam8geSB2ZXJkZS4gRXN0YSByZXByZXNlbnRhY2lvbiBlcyBwb3B1bGFyIHlhIHF1ZSBoYWNlIHF1ZSBzZWEgZmFjaWwgdmVyIGxhIHZlZ2V0YWNpb24gKGVuIHJvam8pLg0KDQoNCmBgYHtyfQ0KIyBQbG90ZWFtb3MgbGEgY29tYmluYWNpb24gZGUgYmFuZGFzIDUsIDQgeSAzIHF1ZSBwYXJhIEw4IGVzIGNvbG9yIGZhbHNvDQpwYXIobWZyb3cgPSBjKDEsMikpDQpwbG90UkdCKGxhbmRzYXRSR0JGYWNhLCBheGVzPVRSVUUsIHN0cmV0Y2g9ImxpbiIsIG1haW49IkxhbmRzYXQgVHJ1ZSBDb2xvciBDb21wb3NpdGUiKQ0KbGFuZHNhdEZDQyA8LSBzdGFjayhiNSwgYjQsIGIzKQ0KcGxvdFJHQihsYW5kc2F0RkNDLCBheGVzPVRSVUUsIHN0cmV0Y2g9ImxpbiIsIG1haW49IkxhbmRzYXQgRmFsc2UgQ29sb3IgQ29tcG9zaXRlIikNCg0KYGBgDQoNCiMjIyMjICpOb3RhIDogQ29tcHJ1ZWJlIHNpZW1wcmUgbGEgZG9jdW1lbnRhY2lvbiBkZWwgcGFxdWV0ZSAoLSBoZWxwKHBsb3RSR0IpIC0pIHBhcmEgdmVyIG90cm9zIGFyZ3VtZW50b3MgcXVlIHNlIHB1ZWRlbiBhZ3JlZ2FyIChjb21vIGxhIGVzY2FsYSkgcGFyYSBtZWpvcmFyIG8gbW9kaWZpY2FyIGxhIGltYWdlbi4qDQoNCiMjIyMjICpQcmVndW50YSAxIDogVXNlIGxhIGZ1bmNpb24gcGxvdFJHQiBjb24gUmFzdGVyU3RhY2sgYGAgbGFuZHNhdCAnJyBwYXJhIGNyZWFyIHVuIGNvbXB1ZXN0byBkZSBjb2xvciB2ZXJkYWRlcm8geSBmYWxzbyAocmVjdWVyZGUgbGEgcG9zaWNpb24gZGUgbGFzIGJhbmRhcyBlbiBsYSBwaWxhKS4qDQoNCmBgYHtyfQ0KDQojIyBzb2x1Y2lvbiBhIGxhIHByZWd1bnRhIDEsIHBsb3RlYW1vcyBsYSBjb21iaW5hY2lvbiBkZSBiYW5kYXMgNSwgNiB5IDQgcXVlIHBhcmEgTDggZXMgY29sb3IgZmFsc28NCmxhbmRzYXRmYWNhX3AxID0gc3RhY2soYjUsIGI2LCBiNCkNCnBsb3RSR0IobGFuZHNhdGZhY2FfcDEsIGF4ZXM9VFJVRSwgc3RyZXRjaD0ibGluIiwgbWFpbj0iQ29tcG9zaWNpb24gZGUgY29sb3IgZmFsc2EgLSBBZ3VhIC8gVGllcnJhIikNCg0KYGBgDQoNCg0KIyMjICoqVElUVUxPOiBTVUJDT05KVU5UTyBZIFJFTk9NQlJBUiBCQU5EQVMqKg0KDQoNCiMjIyMjIFB1ZWRlIHNlbGVjY2lvbmFyIGNhcGFzIChiYW5kYXMpIGVzcGVjaWZpY2FzIG1lZGlhbnRlIGxhIGZ1bmNpb24gLSBzdWJzZXQgLSBvIG1lZGlhbnRlIGluZGV4YWNpb24uDQoNCg0KYGBge3J9DQoNCiMgc2VsZWN0IGZpcnN0IDMgYmFuZHMgb25seQ0KbGFuZHNhdEZhY2FzdWIxIDwtIHN1YnNldChsYW5kc2F0RmFjYSwgMTozKQ0KIyBzYW1lDQpsYW5kc2F0RmFjYXN1YjIgPC0gbGFuZHNhdEZhY2FbWzE6M11dDQojIE51bWJlciBvZiBiYW5kcyBpbiB0aGUgb3JpZ2luYWwgYW5kIG5ldyBkYXRhDQpubGF5ZXJzKGxhbmRzYXRGYWNhKQ0KbmxheWVycyhsYW5kc2F0RmFjYXN1YjEpDQpubGF5ZXJzKGxhbmRzYXRGYWNhc3ViMikNCg0KYGBgDQoNCg0KIyMjIyMgTm8gdXNhcmVtb3MgbGFzIHVsdGltYXMgY3VhdHJvIGJhbmRhcyAtIGxhbmRzYXQgLS4gUHVlZGVzIGVsaW1pbmFyIGFxdWVsbG9zIHVzYW5kbzoNCg0KDQpgYGB7cn0NCiMjIEVsaW1pbmFuZG8gbGFzIHVsdGltYXMgY3VhdHJvIGJhbmRhcywgZXMgZGVjaXIsIHNlbGVjY2lvbmFyZW1vcyBwYXJhIG51ZXN0cm8gZWplcmNpY2lvIGxhcyBiYW5kYXMgZGUgMSBhIGxhIDcgKDE6NykNCmxhbmRzYXRGYWNhIDwtIHN1YnNldChsYW5kc2F0RmFjYSwgMTo3KQ0KDQpgYGANCg0KDQojIyMjIyBQYXJhIG1heW9yIGNsYXJpZGFkLCBlcyB1dGlsIGVzdGFibGVjZXIgbG9zIG5vbWJyZXMgZGUgbGFzIGJhbmRhcy4NCg0KDQpgYGB7cn0NCiMgQWNhIHZlbW9zIHF1ZSBub21icmVzIHRpZW5lbiBsYXMgYmFuZGFzDQpuYW1lcyhsYW5kc2F0RmFjYSkgPC0gYygndWx0cmEtYmx1ZScsICdibHVlJywgJ2dyZWVuJywgJ3JlZCcsICdOSVInLCAnU1dJUjEnLCAnU1dJUjInKQ0KbmFtZXMobGFuZHNhdEZhY2EpDQpwbG90IChsYW5kc2F0RmFjYSkNCg0KIyMgWzFdICJMQzA4X0wxVFBfMDA4MDU3XzIwMTgwMzE3XzIwMTgwNDAzXzAxX1QxX0IxIiAiTEMwOF9MMVRQXzAwODA1N18yMDE4MDMxN18yMDE4MDQwM18wMV9UMV9CMiINCiMjIFszXSAiTEMwOF9MMVRQXzAwODA1N18yMDE4MDMxN18yMDE4MDQwM18wMV9UMV9CMyIgIkxDMDhfTDFUUF8wMDgwNTdfMjAxODAzMTdfMjAxODA0MDNfMDFfVDFfQjQiDQojIyBbNV0gIkxDMDhfTDFUUF8wMDgwNTdfMjAxODAzMTdfMjAxODA0MDNfMDFfVDFfQjUiICJMQzA4X0wxVFBfMDA4MDU3XzIwMTgwMzE3XzIwMTgwNDAzXzAxX1QxX0I2Ig0KIyMgWzddICJMQzA4X0wxVFBfMDA4MDU3XzIwMTgwMzE3XzIwMTgwNDAzXzAxX1QxX0I3Ig0KDQojIEFjYSBsYXMgcmVub21icmFtb3MgeSBsYXMgcGxvdGVhbW9zDQojIyBbMV0gInVsdHJhLmF6dWsiICJhenVsIiAgICAgICAidmVyZGUiICAgICAgInJvam8iICAgICAgICJOSVIiICAgICAgICAiU1dJUjEiICAgICAgIlNXSVIyIiAgICAgDQojIyBbMV0gInVsdHJhLmF6dWwiICJhenVsIiAgICAgICAidmVyZGUiICAgICAgInJvam8iICAgICAgICJOSVIiICAgICAgICAiU1dJUjEiICAgICAgIlNXSVIyIg0KYGBgDQogDQoNCiMjIyAqKlRJVFVMTzogU1VCQ09OSlVOVE8gRVNQQUNJQUwgTyBSRUNPUlRFKioNCg0KDQojIyMjIyBFbCBzdWJjb25qdW50byBlc3BhY2lhbCBzZSBwdWVkZSB1c2FyIHBhcmEgbGltaXRhciBlbCBhbmFsaXNpcyBhIHVuIHN1YmNvbmp1bnRvIGdlb2dyYWZpY28gZGUgbGEgaW1hZ2VuLiBMb3Mgc3ViY29uanVudG9zIGVzcGFjaWFsZXMgc2UgcHVlZGVuIGNyZWFyIGNvbiBsYSBmdW5jaW9uIC0gY3JvcCAtLCB1dGlsaXphbmRvIHVuIG9iamV0byAtIGV4dGVudCAtICB1IG90cm8gb2JqZXRvIGVzcGFjaWFsIGRlbCBxdWUgc2UgcHVlZGUgZXh0cmFlciB1bmEgRXh0ZW5zaW9uLg0KDQoNCmBgYHtyfQ0KDQojIFVzYW5kbyBFeHRlbnNpb24NCmV4dGVudChsYW5kc2F0RmFjYSkNCg0KIyMgY2xhc3MgICAgICA6IEV4dGVudA0KIyMgeG1pbiAgICAgICA6IDQ0NjA4NSANCiMjIHhtYXggICAgICAgOiA2NzM1MTUNCiMjIHltaW4gICAgICAgOiAzNjI5ODUNCiMjIHltYXggICAgICAgOiA1OTUyMTUNCg0KIyBDYXJnYW1vcyB1biBvYmpldG8gZXNwYWNpYWwgcXVlIHNlIGVuY3VlbnRyYSBlbiB1biBzaGFwZWZpbGUuIEVzdGEgZXh0ZW5zaW9uIGVzIGVsIGFyZWEgZGUgZmFjYQ0KRmFjYXNoYXBlID0gc2hhcGVmaWxlKCdDOi9wZXJjZXBjaW9ucmVtb3RhL1NocEZhY2EvY29iZXJ0dXJhdGllcnJhZmFjYTE4LnNocCcpDQplID0gZXh0ZW50KGxhbmRzYXRGYWNhKQ0KDQoNCg0KIyMjY3JvcCBsYW5kc2F0IA0KDQpsYW5kc2F0RmFjYWNyb3AgPSBjcm9wIChsYW5kc2F0RmFjYSwgZSkNCg0KcGxvdFJHQihsYW5kc2F0RmFjYWNyb3AsIGF4ZXMgPSBUUlVFLCBzdHJldGNoPSAibGluIiwgbWFpbj0gIkxhbmRzYXQgY29tcG9zaWNpb24gYSBmYWxzbyBjb2xvciBmYWNhIikNCg0KDQpgYGANCg0KDQojIyMjIyAqUHJlZ3VudGEgMiA6IFRhbWJpZW4gZXMgcG9zaWJsZSBsYSBzZWxlY2Npb24gaW50ZXJhY3RpdmEgZGUgbGEgaW1hZ2VuLiBVc2UgYGAgZHJhd0V4dGVudGBgIHkgYGAgZHJhd1BvbHlgYCBwYXJhIHNlbGVjY2lvbmFyIHVuIGFyZWEgZGUgaW50ZXJlcyoNCg0KDQojIyMjIHNlIGRhIHJlc3B1ZXN0YSBhIGVzdGEgcHJlZ3VudGEgY29uIGVsIGVqZXJjaWNpbyBhbnRlcmlvcg0KDQoNCiMjIyMjICpQcmVndW50YSAzIDogVXNlIGVsIGBgIExhbmRzYXRjcm9wICcnIGRlIFJhc3RlclN0YWNrIHBhcmEgY3JlYXIgdW4gY29tcHVlc3RvIGRlIGNvbG9yIHZlcmRhZGVybyB5IGZhbHNvKg0KDQoNCmBgYHtyfQ0KDQojIENyZWFjaW9uIGRlIHVuIGNvbXB1ZXN0byBkZSBjb2xvciB2ZXJkYWRlcm8sIHBhcmEgbGFuZHNhdCA4IGxhIGNvbWJpbmFjaW9uIGRlIGNvbG9yIGVzIDQsMywyDQpsYW5kc2F0Y3JvcGZhY2Fzb2x1Y2lvbiA9IGNyb3Aoc3RhY2soYjQsIGIzLCBiMiksIGUpDQoNCiMgUGxvdGVhbW9zIGxhIGNvbWJpbmFjaW9uDQpwbG90UkdCKGxhbmRzYXRjcm9wZmFjYXNvbHVjaW9uLCBheGVzID0gVFJVRSwgc3RyZXRjaCA9ICJsaW4iLCBtYWluID0gImltYWdlbiBmYWNhIC0gQ29sb3IgVmVyZGFkZXJvIikNCg0KYGBgDQoNCg0KIyMjICoqVElUVUxPOiBHVUFSREFORE8gUkVTVUxUQURPUyBFTiBFTCBESVNDTyoqDQoNCg0KIyMjIyMgRW4gZXN0YSBldGFwYSwgZXMgcG9zaWJsZSBxdWUgcXVlcmFtb3MgZ3VhcmRhciBlbCByYXN0ZXIgZW4gZWwgZGlzY28gdXNhbmRvIGxhIGZ1bmNpb24gLSB3cml0ZVJhc3RlciAtLiBTZSBhZG1pdGVuIG11bHRpcGxlcyB0aXBvcyBkZSBhcmNoaXZvcy4gVXRpbGl6YXJlbW9zIGVsIGZvcm1hdG8gR2VvVGlmZiBkZSB1c28gY29tdW4uIE1pZW50cmFzIHNlIGNvbnNlcnZhIGVsIG9yZGVuIGRlIGxhcyBjYXBhcywgbG9zIG5vbWJyZXMgZGUgbGFzIGNhcGFzIHNlIHBpZXJkZW4gZGVzYWZvcnR1bmFkYW1lbnRlIGVuIGVsIGZvcm1hdG8gR2VvVGlmZg0KDQoNCmBgYHtyfQ0KIyBBY2EgZ3VhcmRhbm1vcyBudWVzdHJvIHJhc3RlciBkZSBmYWNhIHkgbG8gaW1wcmltaW1vcyBwYXJhIHZlciBsbyBxdWUgc2UgaGEgZ3VhcmRhZG8NCnggPSB3cml0ZVJhc3RlcihsYW5kc2F0RmFjYSwgZmlsZW5hbWU9ImZhY2FfYmFuZGEudGlmIiwgb3ZlcndyaXRlPVRSVUUpDQpwbG90KHgpDQoNCmBgYA0KDQoNCiMjIyMjIEFsdGVybmF0aXZhbWVudGUsIHB1ZWRlIHV0aWxpemFyIGVsIGZvcm1hdG8gJ3Jhc3Rlci1ncmQnLg0KDQoNCmBgYHtyfQ0KDQojIExvIGd1YXJkYW1vcyB0YW1iaWVuIGVuIGZvcm1hdG8gLmdyZCB5YSBxdWUgZXN0ZSBmb3JtYXRvIHNpIGd1YXJkYSBsb3Mgbm9tYnJlcyBkZSBsYXMgY2FwYXMNCndyaXRlUmFzdGVyKGxhbmRzYXRGYWNhLCBmaWxlbmFtZT0iZmFjYV9iYW5kYS5ncmQiLCBvdmVyd3JpdGU9VFJVRSkNCg0KYGBgDQoNCg0KIyMjIyMgVW5hIHZlbnRhamEgZGUgZXN0ZSBmb3JtYXRvIGVzIHF1ZSBndWFyZGEgbG9zIG5vbWJyZXMgZGUgbGFzIGNhcGFzLiBMYSBkZXN2ZW50YWphIGRlIGVzdGUgZm9ybWF0byBlcyBxdWUgbm8gbXVjaG9zIG90cm9zIHByb2dyYW1hcyBwdWVkZW4gbGVlciBsb3MgZGF0b3MsIGVuIGNvbnRyYXN0ZSBjb24gZWwgZm9ybWF0byBHZW9UaWZmLg0KDQoNCiMjIyMjICpOb3RhOiBDb25zdWx0ZSBsYSBkb2N1bWVudGFjaW9uIGRlbCBwYXF1ZXRlICgtIGhlbHAod3JpdGVSYXN0ZXIpIC0pIHBhcmEgdmVyIGFyZ3VtZW50b3MgdXRpbGVzIGFkaWNpb25hbGVzIHF1ZSBzZSBwdWVkZW4gYWdyZWdhcioNCg0KDQojIyMgKipUSVRVTE86IFJFTEFDSU9OIEVOVFJFIEJBTkRBUyoqDQoNCg0KIyMjIyMgVW5hIG1hdHJpeiBkZSBkaWFncmFtYSBkZSBkaXNwZXJzaW9uIHB1ZWRlIHNlciB1dGlsIHBhcmEgZXhwbG9yYXIgbGFzIHJlbGFjaW9uZXMgZW50cmUgY2FwYXMgcmFzdGVyLiBFc3RvIHNlIHB1ZWRlIGhhY2VyIGNvbiBsYSBmdW5jaW9uIHBhcmVzICgpIGRlbCBwYXF1ZXRlIHJhc3Rlci4NCg0KDQojIyMjIyBUcmF6YWRvIGRlIHJlZmxlam8gZW4gbGEgbG9uZ2l0dWQgZGUgb25kYSB1bHRyYSBhenVsIGNvbnRyYSBlbCByZWZsZWpvIGVuIGxhIGxvbmdpdHVkIGRlIG9uZGEgYXp1bC4NCg0KDQpgYGB7cn0NCiMgQ29tcGFyYWNpb24gZGVsIHJlZmxlam8gZW4gbGEgbG9uZ2l0dWQgZGUgb25kYSBVbHRhIGF6dWwgdnMgYXp1bA0KcGFpcnMobGFuZHNhdEZhY2Fjcm9wIFtbMToyXV0sIG1haW4gPSAidWx0cmE9Ymx1ZSB2cyBibHVlIiApDQpgYGANCg0KDQojIyMjIyBUcmF6YWRvIGRlIHJlZmxlam8gZW4gbGEgbG9uZ2l0dWQgZGUgb25kYSByb2phIGNvbnRyYSByZWZsZWpvIGVuIGxhIGxvbmdpdHVkIGRlIG9uZGEgTklSLg0KDQoNCmBgYHtyfQ0KIyBDb21wYXJhY2lvbiBkZWwgcmVmbGVqbyBlbiBsYSBsb25naXR1ZCBkZSBvbmRhIFJvamEgdnMgTklSDQpwYWlycyhsYW5kc2F0RmFjYWNyb3BbWzQ6NV1dLCBtYWluID0gIlJlZCB2ZXJzdXMgTklSIikNCmBgYA0KDQoNCiMjIyMjIExhIHByaW1lcmEgZ3JhZmljYSByZXZlbGEgYWx0YXMgY29ycmVsYWNpb25lcyBlbnRyZSBsYXMgcmVnaW9uZXMgZGUgbG9uZ2l0dWQgZGUgb25kYSBhenVsLiBEZWJpZG8gYSBsYSBhbHRhIGNvcnJlbGFjaW9uLCBwb2RlbW9zIHVzYXIgdW5hIGRlIGxhcyBiYW5kYXMgYXp1bGVzIHNpbiBwZXJkZXIgbXVjaGEgaW5mb3JtYWNpb24uDQoNCg0KIyMjIyMgRXN0YSBkaXN0cmlidWNpb24gZGUgcHVudG9zIGVuIGxhIHNlZ3VuZGEgZ3JhZmljYSAoZW50cmUgTklSIHkgcm9qbykgZXMgdW5pY2EgZGViaWRvIGEgc3UgZm9ybWEgdHJpYW5ndWxhci4gTGEgdmVnZXRhY2lvbiBzZSByZWZsZWphIG11eSBiaWVuIGVuIGVsIHJhbmdvIE5JUiBxdWUgZW4gZWwgcm9qbyB5IGNyZWEgbGEgZXNxdWluYSBzdXBlcmlvciBjZXJjYSBkZWwgZWplIE5JUiAoeSkuIEVsIGFndWEgYWJzb3JiZSBlbmVyZ2lhIGRlIHRvZGFzIGxhcyBiYW5kYXMgeSBvY3VwYSBlbCBsdWdhciBjZXJjYW5vIGFsIG9yaWdlbi4gRWwgcmluY29uIG1hcyBhbGVqYWRvIHNlIGNyZWEgZGViaWRvIGEgbGFzIGNhcmFjdGVyaXN0aWNhcyBzdXBlcmZpY2lhbGVzIGFsdGFtZW50ZSByZWZsZWN0YW50ZXMsIGNvbW8gZWwgc3VlbG8gYnJpbGxhbnRlIG8gZWwgaG9ybWlnb24uDQoNCg0KIyMjICoqVElUVUxPOiBFWFRSQUVSIFZBTE9SRVMgREUgUElYRUxFUyAqKg0KDQoNCiMjIyMjIEEgbWVudWRvLCBxdWVyZW1vcyBvYnRlbmVyIGxvcyB2YWxvcmVzIGRlIGxhcyBjZWxkYXMgcmFzdGVyIHBhcmEgdWJpY2FjaW9uZXMgZ2VvZ3JhZmljYXMgbyBhcmVhcyBlc3BlY2lmaWNhcy4gTGEgZnVuY2lvbiAtIGV4dHJhY3QgLSBzZSB1dGlsaXphIHBhcmEgb2J0ZW5lciB2YWxvcmVzIHJhc3RlciBlbiBsYXMgdWJpY2FjaW9uZXMgZGUgb3Ryb3MgZGF0b3MgZXNwYWNpYWxlcy4gUHVlZGUgdXNhciBwdW50b3MsIGxpbmVhcywgcG9saWdvbm9zIG8gdW4gb2JqZXRvIGRlIGV4dGVuc2lvbiAocmVjdGFuZ3VsbykuIFRhbWJpZW4gcHVlZGUgdXNhciBudW1lcm9zIGRlIGNlbGRhIHBhcmEgZXh0cmFlciB2YWxvcmVzLiBBbCB1c2FyIHB1bnRvcywgLSBleHRyYWN0IC0gZGV2dWVsdmUgbG9zIHZhbG9yZXMgZGUgdW4gb2JqZXRvIC0gUmFzdGVyKiAtICBwYXJhIGxhcyBjZWxkYXMgZW4gbGFzIHF1ZSBzZSB1YmljYSB1biBjb25qdW50byBkZSBwdW50b3MuDQoNCg0KYGBge3J9DQojIENhcmdhciBsb3MgcG9saWdvbm9zIGNvbiBpbmZvcm1hY2lvbiBzb2JyZSBlbCB1c28gZGVsIHN1ZWxvDQpmYWNhY29iZXJ0ID0gc2hhcGVmaWxlICgnQzovcGVyY2VwY2lvbnJlbW90YS9TaHBGYWNhL2NvYmVydHVyYXRpZXJyYWZhY2ExOC5zaHAnKQ0KICAgIA0KIyBHZW5lcmFyIG11ZXN0cmFzIGRlIDMwMCBwdW50b3MgYSBwYXJ0aXIgZGUgbG9zIHBvbGlnb25vcw0KcHRzZmFjYWNvYmVydCA9IHNwc2FtcGxlKGZhY2Fjb2JlcnQsMzAwLCB0eXBlPSdyZWd1bGFyJykNCg0KIyBBZ3JlZ2FyIGxhIGNsYXNlIGRlIGNvYmVydHVyYSBkZWwgc3VlbG8gYSBsb3MgcHVudG9zDQpwdHNmYWNhY29iZXJ0JE5hbWUgPSBvdmVyKHB0c2ZhY2Fjb2JlcnQsIGZhY2Fjb2JlcnQpJE5hbWUNCiAgICANCiMgRXh0cmFlciB2YWxvcmVzIGNvbiBwdW50b3MNCmRmID0gZXh0cmFjdChsYW5kc2F0RmFjYSwgcHRzZmFjYWNvYmVydCkNCiAgIA0KIyBQYXJhIHZlciBhbGd1bm9zIGRlIGxvcyB2YWxvcmVzIGRlIHJlZmxlY3RhbmNpYQ0KaGVhZChkZikNCg0KIyAgICAgdWx0cmEuYXp1bCBhenVsIHZlcmRlIHJvam8gIE5JUiAgU1dJUjEgU1dJUjINCiNbMSxdICAgICAgIDc5MjggNzIzOCAgNjYyMyA2MDMzIDEyNjk2ICA3NTIwICA1OTk3DQojWzIsXSAgICAgICA3OTU3IDcyNzggIDY3MDAgNjEwMSAxMzE5OSAgNzkzMyAgNjEyMw0KI1szLF0gICAgICAgNzk3MyA3Mjg1ICA2NzQxIDYxNDggMTM0ODIgIDgwNzIgIDYyMTkNCiNbNCxdICAgICAgIDc5NjIgNzI2MiAgNjcxMCA2MTE0IDEzNDEzICA4MTE2ICA2MTk5DQojWzUsXSAgICAgICA3OTMxIDcyNDYgIDY2NzEgNjA2MCAxMzU0MSAgNzY1NSAgNjAxNA0KI1s2LF0gICAgICAgNzk0MyA3MjQyICA2NjIzIDYwMjQgMTI0MzcgIDc1MjkgIDYwMzkNCmBgYA0KDQoNCiMjIyAqKlRJVFVMTzogUEVSRklMRVMgRVNQRUNUUkFMRVMqKg0KDQoNCiMjIyMjIFVuYSBncmFmaWNhIGRlbCBlc3BlY3RybyAodG9kYXMgbGFzIGJhbmRhcykgcGFyYSBsb3MgcGl4ZWxlcyBxdWUgcmVwcmVzZW50YW4gY2llcnRhcyBjYXJhY3RlcmlzdGljYXMgZGUgbGEgc3VwZXJmaWNpZSB0ZXJyZXN0cmUgKHAuIEVqLiBBZ3VhKSBzZSBjb25vY2UgY29tbyBwZXJmaWwgZXNwZWN0cmFsLiBEaWNob3MgcGVyZmlsZXMgZGVtdWVzdHJhbiBsYXMgZGlmZXJlbmNpYXMgZW4gbGFzIHByb3BpZWRhZGVzIGVzcGVjdHJhbGVzIGRlIHZhcmlhcyBjYXJhY3RlcmlzdGljYXMgZGUgbGEgc3VwZXJmaWNpZSB0ZXJyZXN0cmUgeSBjb25zdGl0dXllbiBsYSBiYXNlIHBhcmEgZWwgYW5hbGlzaXMgZGUgaW1hZ2VuZXMuIExvcyB2YWxvcmVzIGVzcGVjdHJhbGVzIHNlIHB1ZWRlbiBleHRyYWVyIGRlIGN1YWxxdWllciBjb25qdW50byBkZSBkYXRvcyBtdWx0aWVzcGVjdHJhbGVzIHV0aWxpemFuZG8gbGEgZnVuY2lvbiAtIGV4dHJhY3QgLS4gIEVuIGVsIGVqZW1wbG8gYW50ZXJpb3IsIGV4dHJhamltb3MgdmFsb3JlcyBkZSBkYXRvcyBkZSBMYW5kc2F0IHBhcmEgbGFzIG11ZXN0cmFzLiBFc3RhcyBtdWVzdHJhcyBpbmNsdXllbjogdGllcnJhcyBkZSBjdWx0aXZvLCBhZ3VhLCBiYXJiZWNobywgY29uc3RydWlkbyB5IGFiaWVydG8uIFByaW1lcm8gY2FsY3VsYW1vcyBsb3MgdmFsb3JlcyBtZWRpb3MgZGUgcmVmbGVjdGFuY2lhIHBhcmEgY2FkYSBjbGFzZSB5IGNhZGEgYmFuZGEuDQoNCg0KYGBge3J9DQoNCm51bWVybz0gbGVuZ3RoKHB0c2ZhY2Fjb2JlcnQpDQpudW1lcm8NCg0KZGZfc2FtcGxlcyA9IGFzIChwdHNmYWNhY29iZXJ0LCAiU3BhdGlhbFBvaW50c0RhdGFGcmFtZSIpDQpkZl9zYW1wbGVzDQpkZl9zYW1wbGVzQGRhdGE9ZGF0YS5mcmFtZShJRD0xOm51bWVybyxzaXplPTEpDQpkZl9zYW1wbGVzDQoNCg0KcGxvdChsYW5kc2F0RmFjYWNyb3ApDQpwbG90KGZhY2Fjb2JlcnQsIGFkZD0gVFJVRSkNCnBsb3QoZGZfc2FtcGxlcyxwY2g9MSwgY2V4PShkZl9zYW1wbGVzJHNpemUpLzQsIGFkZD1UUlVFKQ0KDQoNCmRmX3NhbXBsZXMkTmFtZSA9b3ZlcihkZl9zYW1wbGVzLGZhY2Fjb2JlcnQpJE5hbWUNCmRmX3NhbXBsZXMNCmRmMT1yYXN0ZXI6OmV4dHJhY3QobGFuZHNhdEZhY2Fjcm9wLCBkZl9zYW1wbGVzKQ0KDQoNCm1zID0gYWdncmVnYXRlKGRmMSwgbGlzdChwdHNmYWNhY29iZXJ0JE5hbWUpLCBtZWFuKQ0KDQojIEVuIGx1Z2FyIGRlIGxhIHByaW1lcmEgY29sdW1uYSwgdXNhbW9zIG5vbWJyZXMgZGUgZmlsYQ0Kcm93bmFtZXMobXMpID0gbXNbLDFdDQptcyA9IG1zWywtMV0NCm1zDQoNCiMjICAgICAgICAgICAgICB1bHRyYS5ibHVlICAgICAgIGJsdWUgICAgICBncmVlbiAgICAgICByZWQgICAgICAgIE5JUiAgICAgIFNXSVIxICAgIFNXSVIyDQojIEFndWEJICAgICAgICAgODI1MS4wNTMJICA3NTAxLjI5OCAJIDY4NDAuNjU4CSAgNjA2Ni40MDQJNTc2Mi45MjEJNTA4MS4yODkJNTA1MC44MjUNCiMgQm9zcXVlCSAgICAgICA4MDI2Ljc2MQkgIDczMjYuMTM0CSA2NzczLjQxMAkgIDYxNzIuMjg0CTExMjA3LjQxMAk3OTYyLjEzNAk2NDEyLjc0Ng0KIyBDdWx0aXZvcwkgICAgIDg0MTcuODY0CSAgNzgwMC4zMTgJIDc2MDMuNjgyCSAgNzE5Ny45NTUJMTgxNTYuODE4CTE0MDYzLjA0NQkxMTE5OC44NjQNCiMgRnJlc2EJICAgICAgICAgODU0MS42NjcgIAk3ODcwLjY2NwkgNzQ2NC42NjcJICA2OTAyLjAwMAkxMzE1Ny42NjcJOTgzMi4zMzMJNzgwNi4wMDANCiMgSW52ZXJuYWRlcm8JICAxMTUyMi4wODMJIDExMDM1LjgzMyAgMTE0NDIuMzMzCSAxMTE3OS4wODMJMTgzNjguMzMzCTE0Nzg1LjAwMAkxMTMwMC4wODMNCiMgcGFzdG9zCSAgICAgICA4NDgxLjAwMAkgIDc5MjYuNzI3CSA4MjY3LjkwOQkgIDczMzEuNjM2CTIxMDkzLjI3MwkxMjQ0OS4wMDAJODUwNy4yNzMNCiMgem9uYSBkZXNudWRhCTEwOTc1LjUwMAkgMTEwMzUuNTAwCTEyODg0Ljc1MAkgMTQ0MjguMDAwCTE3OTMwLjc1MAkyMDY3MS43NTAJMTY5MDcuNTAwDQoNCg0KDQpgYGANCg0KDQojIyMjIyBBaG9yYSB0cmF6YW1vcyBlbCBlc3BlY3RybyBtZWRpbyBkZSBlc3RhcyBjYXJhY3RlcmlzdGljYXMuDQoNCg0KYGBge3J9DQojIENyZWUgdW4gdmVjdG9yIGRlIGNvbG9yIHBhcmEgbGFzIGNsYXNlcyBkZSBjb2JlcnR1cmEgZGVsIHN1ZWxvIHBhcmEgdXNhciBlbiBlbCB0cmF6YWRvDQpteWNvbG9yID0gYygnZGFya3JlZCcsICd5ZWxsb3cnLCAnYnVybHl3b29kJywgJ2N5YW4nLCAnYmx1ZScsICdncmVlbicsJ21hZ2VudGEnLCAiZGFya2dyZWVuIikNCg0KIyB0cmFuc2Zvcm1hciBtcyBkZSB1biBkYXRhLmZyYW1lIGEgdW5hIG1hdHJpeg0KbXMgPSBhcy5tYXRyaXgobXMpDQoNCiMgUHJpbWVybyBjcmVhIHVuYSBwYXJjZWxhIHZhY2lhDQpwbG90KDAsIHlsaW09YygwLDI1MDAwKSwgeGxpbSA9IGMoMSw3KSwgdHlwZT0nbicsIHhsYWI9IkJhbmRhcyIsIHlsYWIgPSAiUmVmbGVjdGFuY2lhIikNCg0KIyBBZ3JlZ2EgbGFzIGRpZmVyZW50ZXMgY2xhc2VzDQpmb3IgKGkgaW4gMTpucm93KG1zKSl7DQogIGxpbmVzKG1zW2ksXSwgdHlwZSA9ICJsIiwgbHdkID0gMywgbHR5ID0gMSwgY29sID0gbXljb2xvcltpXSkNCn0NCg0KIyBUaXR1bG8NCnRpdGxlKG1haW49IlBlcmZpbCBlc3BlY3RyYWwgZGUgTGFuZHNhdCIsIGZvbnQubWFpbiA9IDIpDQoNCiMgTGV5ZW5kYQ0KbGVnZW5kKCJ0b3BsZWZ0Iiwgcm93bmFtZXMobXMpLA0KICAgICAgY2V4PTAuOCwgY29sPW15Y29sb3IsIGx0eSA9IDEsIGx3ZCA9MywgYnR5ID0gIm4iKQ0KYGBgDQoNCg0KIyMjIyMgRWwgcGVyZmlsIGVzcGVjdHJhbCBtdWVzdHJhIChkZXMpIHNpbWlsaXR1ZCBlbiBsYSByZWZsZWN0YW5jaWEgZGUgZGlmZXJlbnRlcyBjYXJhY3RlcmlzdGljYXMgZW4gbGEgc3VwZXJmaWNpZSBkZSBsYSB0aWVycmEgKG8gcG9yIGVuY2ltYSBkZSBlbGxhKS4gJ0FndWEnIG11ZXN0cmEgdW5hIHJlZmxleGlvbiByZWxhdGl2YW1lbnRlIGJhamEgZW4gdG9kYXMgbGFzIGxvbmdpdHVkZXMgZGUgb25kYSwgeSAnUGFzdG9zJywgJ0ludmVybmFkZXJvJyB5ICdab25hIGRlc251ZGEnIHRpZW5lbiB1bmEgcmVmbGVjdGFuY2lhIHJlbGF0aXZhbWVudGUgYWx0YSBlbiBsYXMgbG9uZ2l0dWRlcyBkZSBvbmRhIG1hcyBsYXJnYXMuDQoNCg0KIyMjICoqVElUVUxPOiBPUEVSQUNJT05FUyBNQVRFTUFUSUNBUyBCQVNJQ0FTKioNCg0KDQojIyMjIyBFbCBwYXF1ZXRlIC0gcmFzdGVyLSBhZG1pdGUgbXVjaGFzIG9wZXJhY2lvbmVzIG1hdGVtYXRpY2FzLiBMYXMgb3BlcmFjaW9uZXMgbWF0ZW1hdGljYXMgZ2VuZXJhbG1lbnRlIHNlIHJlYWxpemFuIHBvciBwaXhlbCAoY2VsZGEgZGUgY3VhZHJpY3VsYSkuIFByaW1lcm8gaGFyZW1vcyBhbGd1bmFzIG9wZXJhY2lvbmVzIGFyaXRtZXRpY2FzIGJhc2ljYXMgcGFyYSBjb21iaW5hciBiYW5kYXMuIEVuIGVsIHByaW1lciBlamVtcGxvLCBlc2NyaWJpbW9zIHVuYSBmdW5jaW9uIG1hdGVtYXRpY2EgcGVyc29uYWxpemFkYSBwYXJhIGNhbGN1bGFyIGVsIGluZGljZSBkZSB2ZWdldGFjaW9uIGRlIGRpZmVyZW5jaWEgbm9ybWFsaXphZGEgKE5EVkkpLg0KDQoNCmBgYHtyfQ0KDQojIyMgc29sbyBwdWVkbyBjYXJnYXIgbGFzIGJhbmRhcyBkZSBsYSAxIGEgbGEgNyBwb3JxdWUgbGEgOCBubyB0aWVuZSBsYSBtaXNtYSBleHRlbnNpb24gZGUgbGFzIGRlbWFzDQpyYXNsaXN0cDMgPSBwYXN0ZTAoJy4vTEMwOF9MMVRQXzAwODA1N18yMDE4MTIzMF8yMDE5MDEzMF8wMV9UMV9CJywgMTo3LCAiLnRpZiIpDQojI3Jhc2xpc3RwMz0gcHJhY3RpY2EgMw0KDQojIFN0YWNrIGRlIGxhcyBiYW5kYXMgY2FyZ2FkYXMNCmxhbmRzYXRwMyA9IHN0YWNrKHJhc2xpc3RwMykNCnBsb3QobGFuZHNhdHAzKQ0KDQojIENvbWJpbmFjaW9uIGRlIGxhcyBiYW5kYXMgNCwzLDINCmxhbmRzYXRSR0JwMyA9IGxhbmRzYXRwM1tbYyg0LDMsMildXQ0KIyMgbGFuZHNhdFJHQnAzID0gcHJhY3RpY2EgMw0KcGxvdChsYW5kc2F0UkdCcDMpDQoNCiMgQ29tYmluYWNpb24gZGUgbGFzIGJhbmRhcyBlbiBmYWxzbyBjb2xvciA1LDQsMw0KbGFuZHNhdEZDQ3AzID0gbGFuZHNhdHAzW1tjKDUsNCwzKV1dDQojIyBsYW5kc2F0RkNDcDMgPSBwcmFjdGljYSAzDQpwbG90KGxhbmRzYXRGQ0NwMykNCg0KDQpgYGANCg0KDQojIyMgKipUSVRVTE86IElORElDRVMgREUgVkVHRVRBQ0lPTioqDQoNCg0KIyMjIyMgRGVmaW5hbW9zIHVuYSBmdW5jaU9uIGdlbmVyYWwgcGFyYSB1biBJbmRpY2UgYmFzYWRvIGVuIHJhek9uICh2ZWdldGFjaU9uKS4gRW4gbGEgZnVuY2lvbiBhIGNvbnRpbnVhY2lvbiwgLWltZy0gZXMgdW4gb2JqZXRvIFJhc3RlciAqIGRlIG11bHRpcGxlcyBjYXBhcyAtaS0geSAtay0gc29uIGxvcyBpbmRpY2VzIGRlIGxhcyBjYXBhcyAobnVtZXJvcyBkZSBjYXBhKSB1dGlsaXphZG9zIHBhcmEgY2FsY3VsYXIgZWwgaW5kaWNlIGRlIHZlZ2V0YWNpb24uDQoNCg0KYGBge3J9DQojIyB2aWZhY2E9IGluZGljZSBkZSB2ZWdldGFjaW9uIHBhcmEgbGEgaW1hZ2VuIDIwMTUNCnZpZmFjYSA9IGZ1bmN0aW9uKGltZywgaywgaSkgew0KICANCiAgYmsgPSBpbWcgW1trXV0NCiAgYmkgPSBpbWcgW1tpXV0NCiAgdmlmYWNhPShiay1iaSkvKGJrK2JpKQ0KICByZXR1cm4odmlmYWNhKQ0KfQ0KDQpgYGANCg0KYGBge3J9DQoNCiMjIHBhcmEgbGFuZHNhdCBOSVIgPSA1LCByZWQgPSA0DQojIyBuZHZpZmFjYT0gaW5kaWNlIGRlIHZlZ2V0YWNpb24gZGUgZGlyZWZlbmNpYSBub3JtYWxpemFkYSAoTkRWSSkgcGFyYSBsYSBpbWFnZW4gbGFuZHNhdCAyMDE1DQoNCm5kdmlmYWNhID0gdmlmYWNhKGxhbmRzYXRwMywgNSwgNCkgDQpwbG90KG5kdmlmYWNhLCBjb2w9cmV2KHRlcnJhaW4uY29sb3JzKDkpKSwgbWFpbj0gIkxhbmRzYXQgZmFjYSAtIE5WREkiKQ0KDQojIFNlbGVjY2lvbmFuZG8gZWwgc2hhcGUgZGVsIGFyZWEgZGUgZmFjYSwgY2FyZ2FuZG9sbyB5IHJlY29ydGFuZG9sbyBjb24gcmVzcGVjdG8gZGVsIE5EVkkgZGUgbGEgaW1hZ2VuIExhbmRzYXQNCk5EVkkxX2ZhY2EgPSBzaGFwZWZpbGUoJ0M6L3BlcmNlcGNpb25yZW1vdGEvU2hwRmFjYS9jb2JlcnR1cmF0aWVycmFmYWNhMTgnKQ0KZU5EVkkxX2ZhY2EgPSBleHRlbnQoTkRWSTFfZmFjYSkNCk5EVklfcmVjb3J0ZV8xID0gY3JvcChORFZJMV9mYWNhLCBlTkRWSTFfZmFjYSkNCg0KDQoNCmBgYA0KDQoNCiMjIyMjIFB1ZWRlcyB2ZXIgbGEgdmFyaWFjaW9uIGVuIGVsIHZlcmRvciBkZXNkZSBsYSB0cmFtYS4NCg0KDQojIyMjIyBVbmEgZm9ybWEgYWx0ZXJuYXRpdmEgZGUgbG9ncmFyIGVzdG8gZXMgYXNpDQoNCg0KYGBge3J9DQoNCnZpMl9mYWNhID0gZnVuY3Rpb24oeCwgeSkgew0KICAoeC15KS8oeCt5KQ0KfQ0KDQpuZHZpMl9mYWNhID0gb3ZlcmxheShsYW5kc2F0cDNbWzVdXSwgbGFuZHNhdHAzW1s0XV0sIGZ1biA9IHZpMl9mYWNhKQ0KcGxvdChuZHZpMl9mYWNhLCBjb2w9cmV2KHRlcnJhaW4uY29sb3JzKDEwKSksIG1haW4gPSAiTGFuZHNhdCBmYWNhNSAtIE5EVkkgdjIiKQ0KDQojIFNlbGVjY2lvbmFuZG8gZWwgc2hhcGUgZGVsIGFyZWEgZGUgZmFjYSwgY2FyZ2FuZG9sbyB5IHJlY29ydGFuZG9sbyBjb24gcmVzcGVjdG8gZGVsIE5EVkkgZGUgbGEgaW1hZ2VuIExhbmRzYXQNCk5EVkkyX2ZhY2EgPSBzaGFwZWZpbGUoJ0M6L3BlcmNlcGNpb25yZW1vdGEvU2hwRmFjYS9jb2JlcnR1cmF0aWVycmFmYWNhMTguc2hwJykNCmVORFZJMl9mYWNhID0gZXh0ZW50KE5EVkkyX2ZhY2EpDQpORFZJX3JlY29ydGVfMiA9IGNyb3AoTkRWSTJfZmFjYSwgZU5EVkkyX2ZhY2EpDQpwbG90KE5EVklfcmVjb3J0ZV8yLCBjb2w9cmV2KHRlcnJhaW4uY29sb3JzKDEwKSksIG1haW4gPSAiTGFuZHNhdCBmYWNhIC0gTkRWSSIpDQoNCmBgYA0KDQoNCiMjIyMjIFByZWd1bnRhIDEgOiBTZSB1dGlsaXphIGVsIGNvZGlnbyBhbnRlcmlvZW1lbnRlIHJlbGFjaW9uYWRvIHkgc2UgYWp1c3RhIGEgbGEgbmVjZXNpZGFkIGRlbCBwcm95ZWN0byBwYXJhIGRlIGVzdGEgZm9ybWEgY2FsY3VsYXIgbG9zIGluZGljZXMgeSBpZGVudGlmaWNhciBpKSBhZ3VhIHkgaWkpIGFjdW11bGFkb3MuIFN1Z2VyZW5jaWE6IFVzZSBlbCBkaWFncmFtYSBkZWwgcGVyZmlsIGVzcGVjdHJhbCBwYXJhIGVuY29udHJhciBsYXMgYmFuZGFzIHF1ZSB0aWVuZW4gcmVmbGVjdGFuY2lhIG1heGltYSB5IG1pbmltYSBwYXJhIGVzdGFzIGRvcyBjbGFzZXMuDQoNCg0KYGBge3J9DQojIyBzMSBlcyBzb2x1Y2lvbiAxDQoNCm5kdmlmYWNhX3MxID0gdmlmYWNhKGxhbmRzYXRwMywgMywgNikgDQpwbG90KG5kdmlmYWNhX3MxLCBjb2w9cmV2KHRlcnJhaW4uY29sb3JzKDEwKSksIG1haW49ICJMYW5kc2F0IGZhY2EgLSBORFdJIikNCg0KdmkyX2ZhY2FfczEgPSBmdW5jdGlvbih4LCB5KSB7DQogICh4LXkpLyh4K3kpDQp9DQoNCm5kdmkyX2ZhY2FfczEgPSBvdmVybGF5KGxhbmRzYXRwM1tbM11dLCBsYW5kc2F0cDNbWzZdXSwgZnVuID0gdmkyX2ZhY2FfczEpDQpwbG90KG5kdmkyX2ZhY2FfczEsIGNvbD1yZXYodG9wby5jb2xvcnMoMTApKSwgbWFpbiA9ICJMYW5kc2F0IGZhY2EgLSBORFdJIikNCg0KIyMjIFBBUkEgSU5ESUNFIERFIEFHVUEgREUgRElGRVJFTkNJQSBOT1JNQUxJWkFEQSAtIE5EV0kNCiMgU2VsZWNjaW9uYW5kbyBlbCBzaGFwZSBkZWwgYXJlYSBkZSBmYWNhLCBjYXJnYW5kb2xvIHkgcmVjb3J0YW5kb2xvIGNvbiByZXNwZWN0byBkZWwgTkR3SSBkZSBsYSBpbWFnZW4gTGFuZHNhdA0KDQpORFdJX2ZhY2EgPSBzaGFwZWZpbGUoJ0M6L3BlcmNlcGNpb25yZW1vdGEvU2hwRmFjYS9jb2JlcnR1cmF0aWVycmFmYWNhMTguc2hwJykNCmVORFdJID0gZXh0ZW50KE5EV0lfZmFjYSkNCk5EV0lfcmVjb3J0ZSA9IGNyb3AobmR2aTJfZmFjYV9zMSwgZU5EV0kpDQpwbG90KE5EV0lfcmVjb3J0ZSwgY29sPXJldih0b3BvLmNvbG9ycygxMCkpLCBtYWluID0gIkxhbmRzYXQgZmFjYSAtIE5EV0kiKQ0KDQpgYGANCg0KDQpgYGB7cn0NCg0KIyMjIFBBUkEgSU5ESUNFIElOQ09SUE9SQURPIERFIERJRkVSRU5DSUEgTk9STUFMSVpBREEgLSBOREJJDQoNCm5kYmkxX2ZhY2FfczEgPSBvdmVybGF5KGxhbmRzYXRwM1tbNl1dLCBsYW5kc2F0cDNbWzVdXSwgZnVuID0gdmkyX2ZhY2FfczEpDQpwbG90KG5kYmkxX2ZhY2FfczEsIGNvbD1yZXYoaGVhdC5jb2xvcnMoMTApKSwgbWFpbiA9ICJMYW5kc2F0IGZhY2EgLSBOREJJIikNCg0KIyBTZWxlY2Npb25hbmRvIGVsIHNoYXBlIGRlbCBhcmVhIGRlIGZhY2EsIGNhcmdhbmRvbG8geSByZWNvcnRhbmRvbG8gY29uIHJlc3BlY3RvIGRlbCBOREJJIGRlIGxhIGltYWdlbiBMYW5kc2F0DQoNCk5EQklfZmFjYSA9IHNoYXBlZmlsZSgnQzovcGVyY2VwY2lvbnJlbW90YS9TaHBGYWNhL2NvYmVydHVyYXRpZXJyYWZhY2ExOC5zaHAnKQ0KZU5EQkkgPSBleHRlbnQoTkRCSV9mYWNhKQ0KTkRCSV9yZWNvcnRlID0gY3JvcChuZGJpMV9mYWNhX3MxLCBlTkRCSSkNCnBsb3QoTkRCSV9yZWNvcnRlLCBjb2w9cmV2KGhlYXQuY29sb3JzKDEwKSksIG1haW4gPSAiTGFuZHNhdCBmYWNhIC0gTkRCSSIpDQoNCmBgYA0KDQoNCiMjIyAqKlRJVFVMTzogSElTVE9HUkFNQSoqDQoNCg0KIyMjIyMgUG9kZW1vcyBleHBsb3JhciBsYSBkaXN0cmlidWNpb24gZGUgdmFsb3JlcyBjb250ZW5pZG9zIGVuIG51ZXN0cm8gcmFzdGVyIHV0aWxpemFuZG8gbGEgZnVuY2lvbiAtaGlzdCAoKS0gcXVlIHByb2R1Y2UgdW4gaGlzdG9ncmFtYS4gTG9zIGhpc3RvZ3JhbWFzIGEgbWVudWRvIHNvbiB1dGlsZXMgcGFyYSBpZGVudGlmaWNhciB2YWxvcmVzIGF0aXBpY29zIHkgdmFsb3JlcyBkZSBkYXRvcyBpbmNvcnJlY3RvcyBlbiBudWVzdHJvcyBkYXRvcyByYXN0ZXINCg0KDQpgYGB7cn0NCg0KIyMgdmVyIGhpc3RvZ3JhbWEgZGUgZGF0b3MNCmhpc3QoTkRCSV9yZWNvcnRlLA0KICAgICBtYWluPSAiRHNpdHJpYnVjaW9uIGRlIHZhbG9yZXMgZGVsIE5EVkkiLA0KICAgICB4bGFiPSAiTkRWSSIsDQogICAgIHlsYWI9ICJGcmVjdWVuY2lhIiwNCiAgICAgY29sPSB0ZXJyYWluLmNvbG9ycyg1MCksDQogICAgIHhsaW09IGMgKC0wLjEsMC43KSwNCiAgICAgYnJlYWtzID0gMzAsDQogICAgIHhheHQ9ICduJykNCg0KYXhpcyhzaWRlID0gMSwgYXQ9c2VxKC0wLjUsMSwwLjA1KSwgbGFiZWxzID0gc2VxKC0wLjUsMSwwLjA1KSkNCg0KYGBgDQoNCg0KIyMjIyMgTm9zIHJlZmVyaXJlbW9zIGEgZXN0ZSBoaXN0b2dyYW1hIHBhcmEgbGEgc2lndWllbnRlIHN1YnNlY2Npb24gc29icmUgdW1icmFsaXphY2lvbi4NCg0KDQojIyMjIyBQcmVndW50YSAyIDogSGFnYSBoaXN0b2dyYW1hcyBkZSBsb3MgdmFsb3JlcyBxdWUgbG9zIGluZGljZXMgZGUgdmVnZXRhY2lvbiBkZXNhcnJvbGxhcm9uIGVuIGxhIHByZWd1bnRhIDENCg0KDQpgYGB7cn0NCg0KIyMgcmVzcHVlc3RhIGEgbGEgcHJlZ3VudGEgMjogdmVyIGhpc3RvZ3JhbWEgZGUgZGF0b3MNCmhpc3QoTkRXSV9yZWNvcnRlLA0KICAgICBtYWluPSAiRHNpdHJpYnVjaW9uIGRlIHZhbG9yZXMgZGVsIE5EV0kiLA0KICAgICB4bGFiPSAiTkRXSSIsDQogICAgIHlsYWI9ICJGcmVjdWVuY2lhIiwNCiAgICAgY29sPSB0b3BvLmNvbG9ycyg1MCksDQogICAgIHhsaW09IGMgKC0wLjUsMC4zKSwNCiAgICAgYnJlYWtzID0gMzAsDQogICAgIHhheHQ9ICduJykNCg0KYXhpcyhzaWRlID0gMSwgYXQ9c2VxKC0wLjUsMSwwLjA1KSwgbGFiZWxzID0gc2VxKC0wLjUsMSwwLjA1KSkNCg0KYGBgDQoNCg0KYGBge3J9DQoNCiMjIHJlc3B1ZXN0YSBhIGxhIHByZWd1bnRhIDI6IHZlciBoaXN0b2dyYW1hIGRlIGRhdG9zDQpoaXN0KE5EQklfcmVjb3J0ZSwNCiAgICAgbWFpbj0gIkRzaXRyaWJ1Y2lvbiBkZSB2YWxvcmVzIGRlbCBOREJJIiwNCiAgICAgeGxhYj0gIk5EQkkiLA0KICAgICB5bGFiPSAiRnJlY3VlbmNpYSIsDQogICAgIGNvbD0gaGVhdC5jb2xvcnMoNTApLA0KICAgICB4bGltPSBjICgtMC41LDAuMyksDQogICAgIGJyZWFrcyA9IDMwLA0KICAgICB4YXh0PSAnbicpDQoNCmF4aXMoc2lkZSA9IDEsIGF0PXNlcSgtMC41LDEsMC4wNSksIGxhYmVscyA9IHNlcSgtMC41LDEsMC4wNSkpDQoNCmBgYA0KDQoNCiMjIyAqKlRJVFVMTzogVU1CUkFMSVpBQ0lPTioqDQoNCg0KIyMjIyMgUG9kZW1vcyBhcGxpY2FyIHJlZ2xhcyBiYXNpY2FzIHBhcmEgb2J0ZW5lciB1bmEgZXN0aW1hY2lvbiBkZSBsYSBleHRlbnNpb24gZXNwYWNpYWwgZGUgZGlmZXJlbnRlcyBjYXJhY3RlcmlzdGljYXMgZGUgbGEgc3VwZXJmaWNpZSB0ZXJyZXN0cmUuIFRlbmdhIGVuIGN1ZW50YSBxdWUgbG9zIHZhbG9yZXMgTkRWSSBlc3RhbiBlc3RhbmRhcml6YWRvcyB5IHZhcmlhbiBlbnRyZSAtMSB5ICsxLiBMb3MgdmFsb3JlcyBtYXMgYWx0b3MgaW5kaWNhbiBtYXMgY29iZXJ0dXJhIHZlcmRlLg0KDQoNCiMjIyMjIExhcyBjZWxkYXMgY29uIHZhbG9yZXMgZGUgTkRWSSBzdXBlcmlvcmVzIGEgMC40IHNvbiBkZWZpbml0aXZhbWVudGUgdmVnZXRhY2lvbi4gTGEgc2lndWllbnRlIG9wZXJhY2lvbiBlbm1hc2NhcmEgdG9kYXMgbGFzIGNlbGRhcyBxdWUgdGFsIHZleiBubyBzZWFuIHZlZ2V0YWNpb24uDQoNCg0KYGBge3J9DQoNCnZlZyA9IHJlY2xhc3NpZnkoTkRCSV9yZWNvcnRlLCBjYmluZCgtSW5mLCAwLjQsIE5BKSkNCnBsb3QodmVnLCBtYWluID0gIlZlZ2V0YWNpb24iKQ0KDQpgYGANCg0KDQojIyMjIyBWYW1vcyBhIG1hcGVhciBlbCBhcmVhIHF1ZSBjb3JyZXNwb25kZSBhbCBwaWNvIGVudHJlIDAuMjUgeSAwLjMgZW4gZWwgaGlzdG9ncmFtYSBORFZJLg0KDQoNCmBgYHtyfQ0KDQpsYW5kID0gcmVjbGFzc2lmeShOREJJX3JlY29ydGUsIGMoLUluZiwgMC4yNSwgTkEsIDAuMjUsIDAuMywgMSwgMC4zLCBJbmYsIE5BKSkNCnBsb3QobGFuZCwgbWFpbiA9ICIgUXVlIGVzIGVzdG8/IikNCg0KYGBgDQoNCg0KIyMgRXN0YXMgcHVlZGVuIHNlciBsYXMgYXJlYXMgYWJpZXJ0YXMuIFB1ZWRlIHRyYXphciAtbGFuZC0gc29icmUgZWwgLWxhbmRzYXRGQ0MtIHJhc3RlciBvcmlnaW5hbCBwYXJhIG9idGVuZXIgbWFzIGluZm9ybWFjaW9uDQoNCg0KYGBge3J9DQoNCnBsb3RSR0IobGFuZHNhdEZhY2EsIHI9NywgZz02LCBiPTQsIGF4ZXMgPSBUUlVFLCBzdHJldGNoPSJsaW4iLCBtYWluID0gImZhY2EgY29tcG9zaWNpb24gZW4gZmFsc28gY29sb3IiKQ0KcGxvdChsYW5kLCBhZGQgPSBUUlVFLCBsZWdlbmQ9IEZBTFNFKQ0KDQpgYGANCmBgYHtyfQ0KcGxvdFJHQihsYW5kc2F0RmFjYSwgcj0xLCBnPTIsIGI9MywgYXhlcz1UUlVFLCBzdHJldGNoPSJsaW4iLCBtYWluPSJMYW5kc2F0IEZhbHNlIENvbG9yIENvbXBvc2l0ZSIpDQpwbG90KGxhbmQsIGFkZD1UUlVFLCBsZWdlbmQ9RkFMU0UpDQpgYGANCg0KDQojIyMjIyBUYW1iaWVuIHB1ZWRlIGNyZWFyIGNsYXNlcyBwYXJhIGRpZmVyZW50ZXMgY2FudGlkYWRlcyBkZSBwcmVzZW5jaWEgZGUgdmVnZXRhY2lvbi4NCg0KDQpgYGB7cn0NCnZlZ2MgPSByZWNsYXNzaWZ5KE5EQklfcmVjb3J0ZSwgYygtSW5mLDAuMjUsMSwgMC4yNSwwLjMsMiwgMC4zLDAuNCwzLCAwLjQsMC41LDQsIDAuNSxJbmYsIDUpKQ0KcGxvdCh2ZWdjLCBjb2wgPSByZXYodGVycmFpbi5jb2xvcnMoNCkpLCBtYWluID0gJ1VtYnJhbCBiYXNhZG8gZW4gTkRWSScpDQoNCmBgYA0KDQoNCiMjIyMjIFByZWd1bnRhIDMgOiBFcyBwb3NpYmxlIGVuY29udHJhciBhZ3VhIHVzYW5kbyBlbCB1bWJyYWwgZGUgTkRWSSBvIGN1YWxxdWllciBvdHJvIGluZGljZQ0KDQoNCmBgYHtyfQ0KIyBVbWJyYWwsIGVuIGVzdGUgY2FzbyBwYXJhIGFndWENCmFndWEgPSByZWNsYXNzaWZ5KE5EQklfcmVjb3J0ZSwgY2JpbmQoLUluZiwgMC4xLCBOQSkpDQpwbG90KGFndWEsIG1haW4gPSAiQWd1YSIsIGNvbD0gdG9wby5jb2xvcnMoMTApKQ0KDQojUmVjbGFzaWZpY28gZWwgVW1icmFsLCBlbiBlc3RlIGNhc28gcGFyYSBhZ3VhDQphZ3VhYyA9IHJlY2xhc3NpZnkoTkRCSV9yZWNvcnRlLCBjKC1JbmYsMC4wMjUsMSwgMC4wMjUsMC4wMywyLCAwLjAzLDAuMSwzLCAwLjEsMC4yLDQsIDAuMixJbmYsIDUpKQ0KcGxvdChhZ3VhYywgY29sID0gcmV2KHRvcG8uY29sb3JzKDEwKSksIG1haW4gPSAnVW1icmFsIGJhc2FkbyBlbiBORFdJJykNCg0KYGBgDQoNCg0KPHNwYW4gc3R5bGU9ImNvbG9yOmJsYWNrIj4gQU5BTElTSVMgREUgQ09NUE9ORU5URVMgUFJJTkNJUEFMRVMgIDxzcGFuLz4NCg0KDQojIyMjIyBMb3MgZGF0b3MgbXVsdGllc3BlY3RyYWxlcyBhIHZlY2VzIHNlIHRyYW5zZm9ybWFuIHBhcmEgYXl1ZGFyIGEgcmVkdWNpciBsYSBkaW1lbnNpb25hbGlkYWQgeSBlbCBydWlkbyBlbiBsb3MgZGF0b3MuIExhIHRyYW5zZm9ybWFjaW9uIGRlIGNvbXBvbmVudGVzIHByaW5jaXBhbGVzIGVzIHVuIG1ldG9kbyBnZW5lcmljbyBkZSByZWR1Y2Npb24gZGUgZGF0b3MgcXVlIHNlIHB1ZWRlIHV0aWxpemFyIHBhcmEgY3JlYXIgYWxndW5hcyBiYW5kYXMgbm8gY29ycmVsYWNpb25hZGFzIGEgcGFydGlyIGRlIHVuIGNvbmp1bnRvIG1hcyBncmFuZGUgZGUgYmFuZGFzIGNvcnJlbGFjaW9uYWRhcy4NCg0KDQojIyMjIyBQdWVkZSBjYWxjdWxhciBlbCBtaXNtbyBudW1lcm8gZGUgY29tcG9uZW50ZXMgcHJpbmNpcGFsZXMgcXVlIGVsIG51bWVybyBkZSBiYW5kYXMgZGUgZW50cmFkYS4gRWwgcHJpbWVyIGNvbXBvbmVudGUgcHJpbmNpcGFsIChQQykgZXhwbGljYSBlbCBtYXlvciBwb3JjZW50YWplIGRlIHZhcmlhbnphIHkgb3Ryb3MgUEMgZXhwbGljYW4gbGEgdmFyaWFuemEgYWRpY2lvbmFsIGVuIG9yZGVuIGRlY3JlY2llbnRlLg0KDQoNCmBgYHtyfQ0KDQpzZXQuc2VlZCgxKQ0KDQpzciA9IHNhbXBsZVJhbmRvbShsYW5kc2F0cDMsIDEwMDAwMCkNCnBsb3Qoc3JbLGMoNCw1KV0sIG1haW4gPSAiTklSIC0gUk9KTyIpDQoNCmBgYA0KDQoNCiMjIyMjIEVzdG8gc2UgY29ub2NlIGNvbW8gZGlhZ3JhbWEgZGUgdmVnZXRhY2lvbiB5IGxpbmVhIGRlIHN1ZWxvIChpZ3VhbCBxdWUgZWwgZGlhZ3JhbWEgZGUgZGlzcGVyc2lvbiBlbiBsYSBzZWNjaW9uIGFudGVyaW9yKS4NCg0KDQpgYGB7cn0NCnBjYSA9IHByY29tcChzciwgc2NhbGUgPSBUUlVFKQ0KcGNhDQoNCnNjcmVlcGxvdChwY2EpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQpwY2kgPSBwcmVkaWN0KGxhbmRzYXRwMywgcGNhLCBpbmRleCA9IDE6MikNCnBsb3QocGNpW1sxXV0pDQoNCiMjIFJlY29ydGFkbyBhIGZhY2ENCnBjaV9mYWNhID0gc2hhcGVmaWxlKCdDOi9wZXJjZXBjaW9ucmVtb3RhL1NocEZhY2EvY29iZXJ0dXJhdGllcnJhZmFjYTE4LnNocCcpDQplcGNpID0gZXh0ZW50KHBjaV9mYWNhKQ0KcGNpX3JlYyA9IGNyb3AocGNpLCBlcGNpKQ0KcGxvdChwY2lfcmVjLCBjb2w9cmV2KHRlcnJhaW4uY29sb3JzKDIwKSksIG1haW4gPSAicGNpIGZhY2EiKQ0KDQpgYGANCg0KDQojIyMjIyBFbCBwcmltZXIgY29tcG9uZW50ZSBwcmluY2lwYWwgcmVzYWx0YSBsb3MgbGltaXRlcyBlbnRyZSBsYXMgY2xhc2VzIGRlIHVzbyBkZSBsYSB0aWVycmEgbyBsb3MgZGV0YWxsZXMgZXNwYWNpYWxlcywgcXVlIGVzIGxhIGluZm9ybWFjaW9uIG1hcyBjb211biBlbnRyZSB0b2RhcyBsYXMgbG9uZ2l0dWRlcyBkZSBvbmRhLiBFcyBkaWZpY2lsIGVudGVuZGVyIGxvIHF1ZSBkZXN0YWNhIGVsIHNlZ3VuZG8gY29tcG9uZW50ZSBwcmluY2lwYWwuIFZhbW9zIGEgaW50ZW50YXIgdW1icmFsIGRlIG51ZXZvOg0KDQoNCmBgYHtyfQ0KDQpsYW5kc2F0ZmNmYWNhID0gc3RhY2soYjUsIGI0LCBiMykNCg0KIyMgcmVjb3J0YWRvIGEgZmFjYQ0KcGMyID0gc2hhcGVmaWxlKCdDOi9wZXJjZXBjaW9ucmVtb3RhL1NocEZhY2EvY29iZXJ0dXJhdGllcnJhZmFjYTE4LnNocCcpDQplcGMyID0gZXh0ZW50KHBjMikNCnBjMl9yZWM9IGNyb3AobGFuZHNhdGZjZmFjYSwgZXBjMikNCg0KcGMyID0gcmVjbGFzc2lmeShwY2lfcmVjICBbWzJdXSwgYygtSW5mLDAsMSwwLEluZixOQSkpDQpwYXIobWZyb3cgPSBjKDEsMikpDQpwbG90UkdCKHBjMl9yZWMsIHIgPSAxLCBnID0gMiwgYiA9IDMsIGF4ZXMgPSBUUlVFLCBzdHJldGNoID0gImxpbiIsIG1haW4gPSAiRkZDIikNCnBsb3RSR0IocGMyX3JlYywgciA9IDEsIGcgPSAyLCBiID0gMywgYXhlcyA9IFRSVUUsIHN0cmV0Y2ggPSAibGluIiwgbWFpbiA9ICJGRkMiKQ0KcGxvdChwYzIsIGxlZ2VuZCA9IEZBTFNFLCBhZGQgPSBUUlVFKQ0KDQpgYGANCg0KDQojIyMjIyBQYXJhIG9idGVuZXIgbWFzIGluZm9ybWFjaW9uIGFjZXJjYSBkZSBsYSBpbmZvcm1hY2lvbiBjb250ZW5pZGEgZW4gbGFzIHBhcmNlbGFzIGRlIHZlZ2V0YWNpb24geSBsaW5lYXMgZGUgc3VlbG8sIGxlYSBlc3RlIGRvY3VtZW50byBkZSAtR2l0ZWxzb24gZXQgYWwtIC4gVW5hIGV4dGVuc2lvbiBkZSBQQ0EgZW4gbGEgdGVsZWRldGVjY2lvbiBzZSBjb25vY2UgY29tbyBUcmFuc2Zvcm1hY2lvbiAtVGFzc2VsZWQtY2FwLSAuDQoNCg0KDQojIyAqKlRJVFVMTzogQ0FQSVRVTE8gMioqDQoNCg0KDQojIyMjIyBFbiBlc3RlIGNhcGl0dWxvIGV4cGxvcmFtb3MgbGEgY2xhc2lmaWNhY2lvbiBubyBzdXBlcnZpc2FkYS4gRXhpc3RlbiB2YXJpb3MgYWxnb3JpdG1vcyBkZSBjbGFzaWZpY2FjacOzbiBubyBzdXBlcnZpc2Fkb3MsIHkgbGEgZWxlY2Npb24gZGVsIGFsZ29yaXRtbyBwdWVkZSBhZmVjdGFyIGxvcyByZXN1bHRhZG9zLiBFeHBsb3JhcmVtb3MgdW4gc29sbyBhbGdvcml0bW8gKGstbWVhbnMpIHBhcmEgaWx1c3RyYXIgZWwgcHJpbmNpcGlvIGdlbmVyYWwNCg0KDQoNCg0KIyMjIyMgZmFjYSwgSW1hZ2VuIExhbmRTYXQgOCwgdXRpbGl6YW5kbyA3IGJhbmRhcw0KDQpgYGB7cn0NCmxpYnJhcnkocmFzdGVyKQ0KbGFuZHNhdDggPSBzdGFjayhiMixiMyxiNCxiNSxiNixiNykNCm5hbWVzKGxhbmRzYXQ4KSA9IGMgKCdibHVlJywgJ2dyZWVuJywgJ3JlZCcsICdOSVInLCAnU1dJUjEnLCdTV0lSMicpDQpwbG90KGxhbmRzYXQ4KQ0KYGBgDQoNCg0KDQoNCiMjIyMjIFByZWd1bnRhIDEgOiBIYWdhIHVuYSB0cmFtYSBjb21wdWVzdGEgZGUgMyBiYW5kYXMgZGUgZmFsc28gY29sb3IgZGUgYGAgbGFuZHNhdDggJycuDQoNCg0KYGBge3J9DQoNCmxhbmRzYXQ4ZmFjYV9pbWFnZW4gPSBzdGFjayhiNSwgYjYsIGI0KQ0KcGxvdFJHQihsYW5kc2F0OGZhY2FfaW1hZ2VuLCBheGVzPVRSVUUsIHN0cmV0Y2g9ImxpbiIsIG1haW49IkxhbmRzYXQgY29tcG9zaWNpb24gZGUgY29sb3IgZmFsc2EgLSBBZ3VhIC8gVGllcnJhIikNCg0KIyByZWNvcnRhbmRvIGEgZmFjYQ0KDQpmYWNhZXh0ZW5kID0gc2hhcGVmaWxlKCdDOi9wZXJjZXBjaW9ucmVtb3RhL1NocEZhY2EvY29iZXJ0dXJhdGllcnJhZmFjYTE4LnNocCcpDQpleHQgPSBleHRlbnQoZmFjYWV4dGVuZCkNCmxhbmRzYXRmY2MgPSBjcm9wKGxhbmRzYXQ4ZmFjYV9pbWFnZW4sIGV4dCkNCnBsb3RSR0IobGFuZHNhdGZjYywgYXhlcyA9IFRSVUUsIHN0cmV0Y2ggPSAibGluIiwgbWFpbiA9ICJMYW5kc2F0IFRydWUgQ29sb3IgQ29tcG9zaXRlIikNCg0KYGBgDQoNCg0KIyMjIyMgRW4gbGEgY2xhc2lmaWNhY2lvbiBubyBzdXBlcnZpc2FkYSwgdXRpbGl6YW1vcyBsb3MgZGF0b3MgZGUgcmVmbGVjdGFuY2lhLCBwZXJvIG5vIHByb3BvcmNpb25hbW9zIG5pbmd1biBkYXRvIGRlIHJlc3B1ZXN0YSAoZXMgZGVjaXIsIG5vIGlkZW50aWZpY2Ftb3MgbmluZ3VuIHBpeGVsIGNvbW8gcGVydGVuZWNpZW50ZSBhIHVuYSBjbGFzZSBlbiBwYXJ0aWN1bGFyKS4gRXN0byBwdWVkZSBwYXJlY2VyIGV4dHJhw7FvLCBwZXJvIHB1ZWRlIHNlciB1dGlsIGN1YW5kbyBubyB0ZW5lbW9zIG11Y2hvIGNvbm9jaW1pZW50byBwcmV2aW8gZGUgdW4gYXJlYSBkZSBlc3R1ZGlvLiBPIHNpIHRpZW5lIHVuIGFtcGxpbyBjb25vY2ltaWVudG8gZGUgbGEgZGlzdHJpYnVjaW9uIGRlIGxhcyBjbGFzZXMgZGUgaW50ZXJlcyBkZSBsYSBjb2JlcnR1cmEgZGVsIHN1ZWxvLCBwZXJvIG5vIHRpZW5lIGRhdG9zIGVzcGVjaWZpY29zIA0KDQoNCiMjIyMjIEVsIGFsZ29yaXRtbyBhZ3J1cGEgcGl4ZWxlcyBjb24gY2FyYWN0ZXJpc3RpY2FzIGVzcGVjdHJhbGVzIHNpbWlsYXJlcyBlbiBncnVwb3MuDQoNCg0KIyMjIyMgT2J0ZW5nYSBtYXMgaW5mb3JtYWNpb24gc29icmUgSy1tZWFucyB5IG90cm9zIGFsZ29yaXRtb3Mgc3VwZXJ2aXNhZG9zIHNpbiBzdXBlcnZpc2lvbiAuDQoNCg0KIyMjIyMgUmVhbGl6YXJlbW9zIHVuYSBjbGFzaWZpY2FjaW9uIG5vIHN1cGVydmlzYWRhIGVuIHVuIHN1YmNvbmp1bnRvIGVzcGFjaWFsIGRlIGxhIGNhcGEgLW5kdmktLiBBcXVpIGhheSBvdHJhIGZvcm1hIGRlIGNhbGN1bGFyIC1uZHZpLS4gRW4gZXN0ZSBjYXNvLCBubyB1c2Ftb3MgdW5hIGZ1bmNpb24gc2VwYXJhZGEsIHNpbm8gdW5hIG5vdGFjaW9uIGFsZ2VicmFpY2EgZGlyZWN0YS4NCg0KDQpgYGB7cn0NCg0KbmR2aUxhbmRTYXQ4PShsYW5kc2F0OFtbJ05JUiddXS1sYW5kc2F0OFtbJ3JlZCddXSkvKGxhbmRzYXQ4W1snTklSJ11dK2xhbmRzYXQ4W1sncmVkJ11dKQ0KcGxvdChuZHZpTGFuZFNhdDgpDQoNCmBgYA0KDQoNCiMjIyMjIEhhcmVtb3MgdW4gYWdydXBhbWllbnRvIC1rbWVhbnMtIGRlIGxvcyBkYXRvcyAtbmR2aS0uICBQcmltZXJvIHVzYW1vcyAtY3JvcC0gcGFyYSBoYWNlciB1biBzdWJjb25qdW50byBlc3BhY2lhbCBkZSAtbmR2aS0sIHBhcmEgcGVybWl0aXIgdW4gcHJvY2VzYW1pZW50byBtYXMgcmFwaWRvIChwdWVkZSBzZWxlY2Npb25hciBjdWFscXVpZXIgLWV4dGVudC0gdXNhbmRvIGxhIGZ1bmNpw7NuIC1kcmF3RXh0ZW50KCkpLg0KDQoNCiMjIyAqKlRJVFVMTzogQ0xBU0lGSUNBQ0lPTiBLTUVBTlMqKg0KDQoNCmBgYHtyfQ0KbGFuZHNhdDhmYWNhX2ltYWdlbiA9IHN0YWNrKGI1LCBiNiwgYjQpDQpwbG90UkdCKGxhbmRzYXQ4ZmFjYV9pbWFnZW4sIGF4ZXM9VFJVRSwgc3RyZXRjaD0ibGluIiwgbWFpbj0iTGFuZHNhdCBjb21wb3NpY2lvbiBkZSBjb2xvciBmYWxzYSAtIEFndWEgLyBUaWVycmEiKQ0KDQojIHJlY29ydGFuZG8gYSBmYWNhDQoNCmZhY2FleHRlbmQgPSBzaGFwZWZpbGUoJ0M6L3BlcmNlcGNpb25yZW1vdGEvU2hwRmFjYS9jb2JlcnR1cmF0aWVycmFmYWNhMTguc2hwJykNCmV4dCA9IGV4dGVudChmYWNhZXh0ZW5kKQ0KbGFuZHNhdGZjYyA9IGNyb3AobGFuZHNhdDhmYWNhX2ltYWdlbiwgZXh0KQ0KcGxvdFJHQihsYW5kc2F0ZmNjLCBheGVzID0gVFJVRSwgc3RyZXRjaCA9ICJsaW4iLCBtYWluID0gIkxhbmRzYXQgVHJ1ZSBDb2xvciBDb21wb3NpdGUiKQ0KDQoNCg0KYGBgDQoNCg0KIyMjIyMgVGVuZ2EgZW4gY3VlbnRhIHF1ZSAtZ2V0VmFsdWVzLSBjb252aXJ0aW8gZWwgUmFzdGVyTGF5ZXIgLW5kdmktIGVuIHVuYSBtYXRyaXogKG1hdHJpeikuIEFob3JhIHJlYWxpemFyZW1vcyBlbCBhZ3J1cGFtaWVudG8gLWttZWFucy0gZW4gbGEgbWF0cml6IGUgaW5zcGVjY2lvbmFyZW1vcyBsYSBzYWxpZGEuDQoNCg0KYGBge3J9DQoNCm5kdmlMYW5kU2F0OD0obGFuZHNhdDhbWydOSVInXV0tbGFuZHNhdDhbWydyZWQnXV0pLyhsYW5kc2F0OFtbJ05JUiddXStsYW5kc2F0OFtbJ3JlZCddXSkNCnBsb3QobmR2aUxhbmRTYXQ4KQ0KDQpgYGANCg0KDQojIyMjIyAta21lYW5zLWRldnVlbHZlIHVuIG9iamV0byBjb24gOSBlbGVtZW50b3MuIExhIGxvbmdpdHVkIGRlbCBlbGVtZW50byAtY2x1c3Rlci0gZGVudHJvIC1rbW5jbHVzdGVyLSBlcyAyMTY1NjcgcXVlIGVzIGlndWFsIGEgbGEgbG9uZ2l0dWQgZGUgLW5yLSBjcmVhZG8gZGVzZGUgLW5kdmktLiBMb3MgdmFsb3JlcyBkZSBjZWxkYSBkZSByYW5nbyAta21uY2x1c3RlciRjbHVzdGVyLSBlbnRyZSAxIGEgMTAgY29ycmVzcG9uZGllbnRlcyBhbCBudW1lcm8gZGUgZW50cmFkYSBkZWwgY2x1c3RlciBxdWUgcHJvcG9yY2lvbmFtb3MgZW4gbGEgZnVuY2lvbiAta21lYW5zLS4gIC1rbW5jbHVzdGVyJGNsdXN0ZXItIGluZGljYSBsYSBldGlxdWV0YSBkZWwgY2x1c3RlciBwYXJhIGVsIHBpeGVsIGNvcnJlc3BvbmRpZW50ZS4gTmVjZXNpdGFtb3MgY29udmVydGlyIGxvcyB2YWxvcmVzIC1rbW5jbHVzdGVyJGNsdXN0ZXItIG51ZXZhbWVudGUgYSBSYXN0ZXJMYXllciBkZSBsYSBtaXNtYSBkaW1lbnNpb24gcXVlIC1uZHZpLS4NCg0KDQpgYGB7cn0NCiMgRXMgaW1wb3J0YW50ZSBjb25maWd1cmFyIGVsIGdlbmVyYWRvciBkZSBzZW1pbGxhcyBwb3JxdWUgYGttZWFuc2AgaW5pY2lhIGxvcyBjZW50cm9zIGVuIHViaWNhY2lvbmVzIGFsZWF0b3JpYXMsIHBhcmEgZXNvIHV0aWxpemFyZW1vcyBzZXQuc2VlZA0Kc2V0LnNlZWQoOTkpDQoNCiMgUXVlcmVtb3MgY3JlYXIgMTAgZ3J1cG9zLCBwZXJtaXRpciA1MDAgaXRlcmFjaW9uZXMsIGNvbWVuemFyIGNvbiA1IGNvbmp1bnRvcyBhbGVhdG9yaW9zIHV0aWxpemFuZG8gZWwgbcOpdG9kbyAiTGxveWQiDQprbW5jbHVzdGVyID0ga21lYW5zKG5hLm9taXQobnIpLCBjZW50ZXJzID0gMTAsIGl0ZXIubWF4ID0gNTAwLCBuc3RhcnQgPSA1LCBhbGdvcml0aG09Ikxsb3lkIikNCg0KIyBrbWVhbnMgZGV2dWVsdmUgdW4gb2JqZXRvIGRlIGxhIGNsYXNlICJrbWVhbnMiDQpzdHIoa21uY2x1c3RlcikNCg0KYGBgDQoNCg0KIyMjIyMgUG9kZW1vcyB2ZXIgcXVlIC1rbnItIGVzIHVuIFJhc3RlckxheWVyLCBwZXJvIG5vIHNhYmVtb3MgcXVlIGdydXBvICgxLTEwKSBwZXJ0ZW5lY2UgYSBxdWUgY2xhc2UgZGUgY29iZXJ0dXJhIHRlcnJlc3RyZSAoeSBzaSBwZXJ0ZW5lY2UgYSB1bmEgY2xhc2UgcXVlIHJlY29ub2NlcmlhbW9zKS4gUHVlZGUgYXZlcmlndWFybG8gdHJhemFuZG9sb3MgbGFkbyBhIGxhZG8gY29uIGNhcGFzIGRlIHJlZmVyZW5jaWEgeSB1c2FuZG8gdW4gY29sb3IgdW5pY28gcGFyYSBjYWRhIGdydXBvLg0KDQoNCmBgYHtyfQ0KIyBVc2UgZWwgb2JqZXRvIG5kdmkgKGVuIGVzdGUgY2FzbyBzZSBsbGFtYSAibGFuZHNhdDhjcm9wZmFjYSIpIHBhcmEgZXN0YWJsZWNlciBsb3MgdmFsb3JlcyBkZWwgY2x1c3RlciBlbiB1biBudWV2byByYXN0ZXINCmtuciA9IHNldFZhbHVlcyhsYW5kc2F0RmFjYWNyb3AsIGttbmNsdXN0ZXIkY2x1c3RlcikNCg0KIyBUYW1iaWVuIHB1ZWRlcyBoYWNlcmxvIGFzaQ0Ka25yPSByYXN0ZXIobGFuZHNhdEZhY2Fjcm9wKQ0KdmFsdWVzKGtucikgPSBrbW5jbHVzdGVyJGNsdXN0ZXINCmtucg0KYGBgDQoNCg0KIyMjIyMgU2kgYmllbiBwYXJhIG90cm9zIGZpbmVzIHN1ZWxlIHNlciBtZWpvciBkZWZpbmlyIG1hcyBjbGFzZXMgKHkgcG9zaWJsZW1lbnRlIGZ1c2lvbmFyIGNsYXNlcyBtYXMgYWRlbGFudGUpLCB1bmEgY2xhc2lmaWNhY2lvbiBzaW1wbGUgY29tbyBlc3RhIHBvZHJpYSBzZXIgdXRpbCwgcG9yIGVqZW1wbG8sIGZ1c2lvbmFyIGxvcyBncnVwb3MgNCB5IDUgcGFyYSBjb25zdHJ1aXIgdW5hIG1hc2NhcmEgZGUgYWd1YSBwYXJhIGVsIGHDsW8gMjAxNS4NCg0KDQojIyMjIyBQdWVkZXMgY2FtYmlhciBsb3MgY29sb3JlcyBlbiBtaSAtbXljb2xvci0uIE9idGVuZ2EgbWFzIGluZm9ybWFjaW9uIHNvYnJlIGxhIHNlbGVjY2lvbiBkZSBjb2xvcmVzIGVuIFIgLWFxdWktIHkgLWFxdWktIC4NCg0KDQojIyMjIyBQcmVndW50YSAyIDogR3JhZmlxdWUgZWwgUkdCIGRlIDMgYmFuZGFzIGRlICAibGFuZHNhdDgiIHBhcmEgZWwgc3ViY29uanVudG8gKGV4dGVuc2nDs24gImV4dCIpIHkgZWwgcmVzdWx0YWRvIGRlIGxhIGFncnVwYWNpb24gImttZWFucyIgbGFkbyBhIGxhZG8geSBoYWdhIHVuYSB0YWJsYSBkZSBjb2JlcnR1cmEgZGVsIHN1ZWxvIHBhcmEgZWwgdXNvIGRlbCBzdWVsbyBFdGlxdWV0YXMgcGFyYSBsb3MgZ3J1cG9zLiBQb3IgZWplbXBsbywgbG9zIGdydXBvcyA0IHkgNSBzb24gYWd1YS4NCg0KDQpgYGB7cn0NCg0KbGFuZHNhdDhwMiA9IHN0YWNrKGI2LCBiMywgYjIpDQojcGxvdFJHQihsYW5kc2F0OHAyLCBheGVzPVRSVUUsIHN0cmV0Y2g9ImxpbiIsIG1haW49IkxhbmRzYXQgY29tcG9zaWNpb24gZGUgY29sb3IgZmFsc2EgIikNCg0KI2Nyb3AgbGFuZHNhdCBieSB0aGUgZXh0ZW50DQpsYW5kc2F0ZmFjYXAyID0gY3JvcChsYW5kc2F0OHAyLCBleHQpDQpwbG90UkdCKGxhbmRzYXRmYWNhcDIsIGF4ZXMgPSBUUlVFLCBzdHJldGNoID0gImxpbiIsIG1haW4gPSAiTGFuZHNhdCBGQ0MgLSBmYWNhIikNCg0KIyBjb252ZXJ0aXIgZWwgcmFzdGVyIGEgdmVjdG9yIC8gbWF0cml6DQpucl9wMj1nZXRWYWx1ZXMobGFuZHNhdGZhY2FwMikNCnN0cihucl9wMikNCg0KDQojIEVzIGltcG9ydGFudGUgY29uZmlndXJhciBlbCBnZW5lcmFkb3IgZGUgc2VtaWxsYXMgcG9ycXVlIGBrbWVhbnNgIGluaWNpYSBsb3MgY2VudHJvcyBlbiB1YmljYWNpb25lcyBhbGVhdG9yaWFzLCBwYXJhIGVzbyB1dGlsaXphcmVtb3Mgc2V0LnNlZWQNCnNldC5zZWVkICg5OSkNCg0KIyBRdWVyZW1vcyBjcmVhciAxMCBncnVwb3MsIHBlcm1pdGlyIDUwMCBpdGVyYWNpb25lcywgY29tZW56YXIgY29uIDUgY29uanVudG9zIGFsZWF0b3Jpb3MgdXRpbGl6YW5kbyBlbCBtw6l0b2RvICJMbG95ZCINCmttbmNsdXN0ZXJfcDIgPSBrbWVhbnMobmEub21pdChucl9wMiksIGNlbnRlcnMgPSAxMCwgaXRlci5tYXggPSA1MDAsIG5zdGFydCA9IDUsIGFsZ29yaXRobSA9ICJMbG95ZCIpDQoNCiMga21lYW5zIGRldnVlbHZlIHVuIG9iamV0byBkZSBsYSBjbGFzZSAia21lYW5zIg0Kc3RyKGttbmNsdXN0ZXJfcDIpDQoNCg0KIyBVc2UgZWwgb2JqZXRvIG5kdmkgKGVuIGVzdGUgY2FzbyBzZSBsbGFtYSAibGFuZHNhdGZhY2FwMiIpIHBhcmEgZXN0YWJsZWNlciBsb3MgdmFsb3JlcyBkZWwgY2x1c3RlciBlbiB1biBudWV2byByYXN0ZXINCmtucl9wMiA9IHNldFZhbHVlcyhsYW5kc2F0ZmFjYXAyLCBrbW5jbHVzdGVyX3AyJGNsdXN0ZXIpDQoNCiMgVGFtYmllbiBwdWVkZXMgaGFjZXJsbyBhc2kNCmtucl9wMj0gcmFzdGVyKGxhbmRzYXRmYWNhcDIpDQp2YWx1ZXMoa25yX3AyKSA9IGttbmNsdXN0ZXJfcDIkY2x1c3Rlcg0Ka25yX3AyDQoNCiNEZWZpbmEgdW4gdmVjdG9yIGRlIGNvbG9yIHBhcmEgMTAgZ3J1cG9zIChtYXMgaW5mb3JtYWNpb24gc29icmUgY29tbyBjb25maWd1cmFyIGVsIGNvbG9yIG1hcyBhZGVsYW50ZSkNCiNteWNvbG9yX3AyID0gYyAodG9wby5jb2xvcnMoMTApKQ0KcGxvdChrbnJfcDIsIG1haW4gPSAnVW5zdXBlcnZpc2VkIGNsYXNzaWZpY2F0aW9uJywgY29sID0gcmV2KHRvcG8uY29sb3JzKDEwKSkpIA0KDQpgYGANCg0KDQoNCiMjICoqVElUVUxPOiBDQVBJVFVMTyAzKioNCg0KDQoNCg0KIyMjICoqVElUVUxPOiBDTEFTSUZJQ0FDSU9OIFNVUEVSVklTQURBKioNCg0KDQojIyMjIyBBcXVpIGV4cGxvcmFtb3MgbGEgY2xhc2lmaWNhY2lvbiBzdXBlcnZpc2FkYS4gRXhpc3RlbiB2YXJpb3MgYWxnb3JpdG1vcyBkZSBjbGFzaWZpY2FjaW9uIHN1cGVydmlzYWRvcywgeSBsYSBlbGVjY2lvbiBkZWwgYWxnb3JpdG1vIHB1ZWRlIGFmZWN0YXIgbG9zIHJlc3VsdGFkb3MuIEFxdWkgZXhwbG9yYW1vcyBkb3MgYWxnb3JpdG1vcyByZWxhY2lvbmFkb3MgKENBUlQgeSBSYW5kb21Gb3Jlc3QpLg0KDQojIyMjIyBFbiBsYSBjbGFzaWZpY2FjaW9uIHN1cGVydmlzYWRhLCB0ZW5lbW9zIGNvbm9jaW1pZW50byBwcmV2aW8gc29icmUgYWxndW5vcyBkZSBsb3MgdGlwb3MgZGUgY29iZXJ0dXJhIGRlbCBzdWVsbyBtZWRpYW50ZSwgcG9yIGVqZW1wbG8sIHRyYWJham8gZGUgY2FtcG8sIGRhdG9zIGVzcGFjaWFsZXMgZGUgcmVmZXJlbmNpYSBvIGludGVycHJldGFjacOzbiBkZSBpbWFnZW5lcyBkZSBhbHRhIHJlc29sdWNpb24gKGNvbW8gbGFzIGRpc3BvbmlibGVzIGVuIGxvcyBtYXBhcyBkZSBHb29nbGUpLiBTZSBpZGVudGlmaWNhbiBzaXRpb3MgZXNwZWNpZmljb3MgZW4gZWwgYXJlYSBkZSBlc3R1ZGlvIHF1ZSByZXByZXNlbnRhbiBlamVtcGxvcyBob21vZ2VuZW9zIGRlIGVzdG9zIHRpcG9zIGRlIGNvYmVydHVyYSB0ZXJyZXN0cmUgY29ub2NpZG9zLiBFc3RhcyBhcmVhcyBzZSBjb25vY2VuIGNvbXVubWVudGUgY29tbyBzaXRpb3MgZGUgZW50cmVuYW1pZW50byBwb3JxdWUgbGFzIHByb3BpZWRhZGVzIGVzcGVjdHJhbGVzIGRlIGVzdG9zIHNpdGlvcyBzZSB1c2FuIHBhcmEgZW50cmVuYXIgZWwgYWxnb3JpdG1vIGRlIGNsYXNpZmljYWNpb24uDQoNCiMjIyMjIExvcyBzaWd1aWVudGVzIGVqZW1wbG9zIHV0aWxpemFuIHVuIGNsYXNpZmljYWRvciBkZSBhcmJvbGVzIGRlIGNsYXNpZmljYWNpb24geSByZWdyZXNpb24gKENBUlQpIChCcmVpbWFuIGV0IGFsLiAxOTg0KSAoIGxlY3R1cmFzIGFkaWNpb25hbGVzIHBhcmEgcHJlZGVjaXIgbGFzIGNsYXNlcyBkZSBjb2JlcnR1cmEgZGVsIHN1ZWxvIGVuIGVsIGFyZWEgZGUgZXN0dWRpbykuDQoNCiMjIyMjIFJlYWxpemFyZW1vcyBsb3Mgc2lndWllbnRlcyBwYXNvczoNCg0KIyMjIyMgR2VuZXJlIHNpdGlvcyBkZSBtdWVzdHJhIGJhc2Fkb3MgZW4gdW4gcmFzdGVyIGRlIHJlZmVyZW5jaWENCiMjIyMjIEV4dHJhZXIgdmFsb3JlcyBkZSBjZWxkYSBkZSBkYXRvcyBkZSBMYW5kc2F0IHBhcmEgbG9zIHNpdGlvcyBkZSBtdWVzdHJhDQojIyMjIyBFbnRyZW5hciBhbCBjbGFzaWZpY2Fkb3IgdXNhbmRvIG11ZXN0cmFzIGRlIGVudHJlbmFtaWVudG8NCiMjIyMjIENsYXNpZmlxdWUgbG9zIGRhdG9zIGRlIExhbmRzYXQgdXNhbmRvIGVsIG1vZGVsbyBlbnRyZW5hZG8NCiMjIyMjIEV2YWx1YXIgbGEgcHJlY2lzaW9uIGRlbCBtb2RlbG8uDQoNCg0KIyMjICoqVElUVUxPOiBEQVRPUyBERSBSRUZFUkVOQ0lBKioNCg0KDQojIyMjIyBMYSBOYXRpb25hbCBMYW5kIENvdmVyIERhdGFiYXNlIDIwMTEgKE5MQ0QgMjAxMSkgZXMgdW4gcHJvZHVjdG8gZGUgY29iZXJ0dXJhIHRlcnJlc3RyZSBwYXJhIGxvcyBFRS4gVVUuIE5MQ0QgZXMgdW5hIGJhc2UgZGUgZGF0b3MgZGUgY29iZXJ0dXJhIGRlbCBzdWVsbyBiYXNhZGEgZW4gTGFuZHNhdCBkZSAzMCBtIHF1ZSBhYmFyY2EgNCDDqXBvY2FzICgxOTkyLCAyMDAxLCAyMDA2IHkgMjAxMSkuIE5MQ0QgMjAxMSBzZSBiYXNhIHByaW5jaXBhbG1lbnRlIGVuIHVuYSBjbGFzaWZpY2FjacOzbiBkZSDDoXJib2wgZGUgZGVjaXNpw7NuIGRlIGRhdG9zIExhbmRzYXQgZGUgYWxyZWRlZG9yIGRlIDIwMTEuDQoNCg0KIyMjIyMgUHVlZGUgZW5jb250cmFyIGxvcyBub21icmVzIGRlIGNsYXNlIGVuIE5DTEQgMjAxMSAoYXF1w60pIC1bIGh0dHBzOi8vd3d3Lm1ybGMuZ292L25sY2QxMV9sZWcucGhwIF0tLiBUaWVuZSBkb3MgcGFyZXMgZGUgdmFsb3JlcyBkZSBjbGFzZSB5IG5vbWJyZXMgcXVlIGNvcnJlc3BvbmRlbiBhIGxvcyBuaXZlbGVzIGRlIHVzbyBkZWwgc3VlbG8geSBlbCBzaXN0ZW1hIGRlIGNsYXNpZmljYWNpb24gZGUgbGEgY29iZXJ0dXJhIGRlbCBzdWVsby4gRXN0b3Mgbml2ZWxlcyBnZW5lcmFsbWVudGUgcmVwcmVzZW50YW4gZWwgbml2ZWwgZGUgY29tcGxlamlkYWQsIHNpZW5kbyBlbCBuaXZlbCBJIGVsIG1hcyBzaW1wbGUgY29uIGFtcGxpYXMgY2F0ZWdvcmlhcyBkZSBjb2JlcnR1cmEgZGVsIHN1ZWxvLiBMZWEgZXN0ZSBpbmZvcm1lIGRlIEFuZGVyc29uIGV0IGFsLiBQYXJhIG9idGVuZXIgbWFzIGluZm9ybWFjaW9uIHNvYnJlIGVzdGUgc2lzdGVtYSBkZSBjbGFzaWZpY2FjaW9uIGRlIHVzbyB5IGNvYmVydHVyYSBkZSBsYSB0aWVycmEuDQoNCiMNCiMNCiMNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlIj4gQ0FQSVRVTE8gMyA8c3Bhbi8+IA0KIw0KIw0KIw0KIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlIj4gQ0xBU0lGSUNBQ0lPTiBTVVBFUlZJU0FEQSA8c3Bhbi8+IA0KIw0KIw0KIyMjIyMgQXF1aSBleHBsb3JhbW9zIGxhIGNsYXNpZmljYWNpb24gc3VwZXJ2aXNhZGEuIEV4aXN0ZW4gdmFyaW9zIGFsZ29yaXRtb3MgZGUgY2xhc2lmaWNhY2lvbiBzdXBlcnZpc2Fkb3MsIHkgbGEgZWxlY2Npb24gZGVsIGFsZ29yaXRtbyBwdWVkZSBhZmVjdGFyIGxvcyByZXN1bHRhZG9zLiBBcXVpIGV4cGxvcmFtb3MgZG9zIGFsZ29yaXRtb3MgcmVsYWNpb25hZG9zIChSYW5kb21Gb3Jlc3QgeSBhcmJvbCBkZSBkZWNpc2lvbikuDQojDQojDQojIyMjIyBFbiBsYSBjbGFzaWZpY2FjaW9uIHN1cGVydmlzYWRhLCB0ZW5lbW9zIGNvbm9jaW1pZW50byBwcmV2aW8gc29icmUgYWxndW5vcyBkZSBsb3MgdGlwb3MgZGUgY29iZXJ0dXJhIGRlbCBzdWVsbyBtZWRpYW50ZSwgcG9yIGVqZW1wbG8sIHRyYWJham8gZGUgY2FtcG8sIGRhdG9zIGVzcGFjaWFsZXMgZGUgcmVmZXJlbmNpYSBvIGludGVycHJldGFjacOzbiBkZSBpbWFnZW5lcyBkZSBhbHRhIHJlc29sdWNpb24gKGNvbW8gbGFzIGRpc3BvbmlibGVzIGVuIGxvcyBtYXBhcyBkZSBHb29nbGUpLiBTZSBpZGVudGlmaWNhbiBzaXRpb3MgZXNwZWNpZmljb3MgZW4gZWwgYXJlYSBkZSBlc3R1ZGlvIHF1ZSByZXByZXNlbnRhbiBlamVtcGxvcyBob21vZ2VuZW9zIGRlIGVzdG9zIHRpcG9zIGRlIGNvYmVydHVyYSB0ZXJyZXN0cmUgY29ub2NpZG9zLiBFc3RhcyBhcmVhcyBzZSBjb25vY2VuIGNvbXVubWVudGUgY29tbyBzaXRpb3MgZGUgZW50cmVuYW1pZW50byBwb3JxdWUgbGFzIHByb3BpZWRhZGVzIGVzcGVjdHJhbGVzIGRlIGVzdG9zIHNpdGlvcyBzZSB1c2FuIHBhcmEgZW50cmVuYXIgZWwgYWxnb3JpdG1vIGRlIGNsYXNpZmljYWNpb24uDQojDQojDQpgYGB7cn0NCiMgRGlyZWN0b3JpbyBkZSB0cmFiYWpvDQoNCnNldHdkKCJDOi9wZXJjZXBjaW9ucmVtb3RhL1BSL2ltYWdlbmZhY2EvIikNCg0KIyBJbXBvcnRhciBsYXMgaW1hZ2VuZXMNCkIyID0gcmFzdGVyKCJMQzA4X0wxVFBfMDA4MDU3XzIwMTgxMjMwXzIwMTkwMTMwXzAxX1QxX0IyLlRJRiIpDQpCMyA9IHJhc3RlcigiTEMwOF9MMVRQXzAwODA1N18yMDE4MTIzMF8yMDE5MDEzMF8wMV9UMV9CMy5USUYiKQ0KQjQgPSByYXN0ZXIoIkxDMDhfTDFUUF8wMDgwNTdfMjAxODEyMzBfMjAxOTAxMzBfMDFfVDFfQjQuVElGIikNCkI1ID0gcmFzdGVyKCJMQzA4X0wxVFBfMDA4MDU3XzIwMTgxMjMwXzIwMTkwMTMwXzAxX1QxX0I1LlRJRiIpDQpCNiA9IHJhc3RlcigiTEMwOF9MMVRQXzAwODA1N18yMDE4MTIzMF8yMDE5MDEzMF8wMV9UMV9CNi5USUYiKQ0KQjcgPSByYXN0ZXIoIkxDMDhfTDFUUF8wMDgwNTdfMjAxODEyMzBfMjAxOTAxMzBfMDFfVDFfQjcuVElGIikNCg0KIyBEYXRvcyBpbWFnZW4gTVRMDQpkaWEgPC0gMDA0DQpTVU5fRUxFVkFUSU9OID0gNTEuODkxMzQ1MzkNCg0KUkFESUFOQ0VfTVVMVF9CQU5EXzIgPSAxLjMyOThFLTAyDQpSQURJQU5DRV9NVUxUX0JBTkRfMyA9IDEuMjI1NEUtMDINClJBRElBTkNFX01VTFRfQkFORF80ID0gMS4wMzMzRS0wMg0KUkFESUFOQ0VfTVVMVF9CQU5EXzUgPSA2LjMyMzZFLTAzDQpSQURJQU5DRV9NVUxUX0JBTkRfNiA9IDEuNTcyNkUtMDMNClJBRElBTkNFX01VTFRfQkFORF83ID0gNS4zMDA2RS0wNA0KDQpSQURJQU5DRV9BRERfQkFORF8yID0gLTY2LjQ5MTQxDQpSQURJQU5DRV9BRERfQkFORF8zID0gLTYxLjI3MTI2DQpSQURJQU5DRV9BRERfQkFORF80ID0gLTUxLjY2NzM4DQpSQURJQU5DRV9BRERfQkFORF81ID0gLTMxLjYxNzg2DQpSQURJQU5DRV9BRERfQkFORF82ID0gLTcuODYzMDcNClJBRElBTkNFX0FERF9CQU5EXzcgPSAtMi42NTAyOA0KDQpSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfMiA9IDEuMjEwNzAwDQpSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfMyA9IDEuMjEwNzAwDQpSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfNCA9IDEuMjEwNzAwDQpSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfNSA9IDEuMjEwNzAwDQpSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfNiA9IDEuMjEwNzAwDQpSRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfNyA9IDEuMjEwNzAwDQoNClJBRElBTkNFX01BWElNVU1fQkFORF8yID0gODA1LjAxMTQxDQpSQURJQU5DRV9NQVhJTVVNX0JBTkRfMyA9IDc0MS44MTExNg0KUkFESUFOQ0VfTUFYSU1VTV9CQU5EXzQgPSA2MjUuNTM2OTkNClJBRElBTkNFX01BWElNVU1fQkFORF81ID0gMzgyLjc5NzQyDQpSQURJQU5DRV9NQVhJTVVNX0JBTkRfNiA9IDk1LjE5ODIzDQpSQURJQU5DRV9NQVhJTVVNX0JBTkRfNyA9IDMyLjA4NjkwDQoNCiMgQ29udmVyc2lvbg0KU1VOX0VMRVZBVElPTl9SIDwtIFNVTl9FTEVWQVRJT04qcGkvMTgwICAjIHJhZGlhbmVzDQpkIDwtIDErMC4wMTY3KihzaW4oKDIqcGkqKGRpYS05My41KSkvMzY1KSkgIyBkaXN0YW5jaWEgZGVsIHNvbCBhIGxhIHRpZXJyYQ0KDQojIERldGVybWluYWNpb24gRVNVTg0KRVNVTjIgPC0gcGkqKGReMikqUkFESUFOQ0VfTUFYSU1VTV9CQU5EXzIvUkVGTEVDVEFOQ0VfTUFYSU1VTV9CQU5EXzINCkVTVU4zIDwtIHBpKihkXjIpKlJBRElBTkNFX01BWElNVU1fQkFORF8zL1JFRkxFQ1RBTkNFX01BWElNVU1fQkFORF8zDQpFU1VONCA8LSBwaSooZF4yKSpSQURJQU5DRV9NQVhJTVVNX0JBTkRfNC9SRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfNA0KRVNVTjUgPC0gcGkqKGReMikqUkFESUFOQ0VfTUFYSU1VTV9CQU5EXzUvUkVGTEVDVEFOQ0VfTUFYSU1VTV9CQU5EXzUNCkVTVU42IDwtIHBpKihkXjIpKlJBRElBTkNFX01BWElNVU1fQkFORF82L1JFRkxFQ1RBTkNFX01BWElNVU1fQkFORF82DQpFU1VONyA8LSBwaSooZF4yKSpSQURJQU5DRV9NQVhJTVVNX0JBTkRfNy9SRUZMRUNUQU5DRV9NQVhJTVVNX0JBTkRfNw0KDQojIEVsaW1hciB2YWxvcmVzIG51bG9zDQpCMltCMj09MF0gPC0gTkENCkIzW0IzPT0wXSA8LSBOQQ0KQjRbQjQ9PTBdIDwtIE5BDQpCNVtCNT09MF0gPC0gTkENCkI2W0I2PT0wXSA8LSBOQQ0KQjdbQjc9PTBdIDwtIE5BDQoNCiMgRGV0ZXJtaW5hY2lvbiBkZSByYWRpYW5jaWEgc2Vuc29yDQpMMiA8LSBSQURJQU5DRV9NVUxUX0JBTkRfMipCMiArIFJBRElBTkNFX0FERF9CQU5EXzINCkwzIDwtIFJBRElBTkNFX01VTFRfQkFORF8zKkIzICsgUkFESUFOQ0VfQUREX0JBTkRfMw0KTDQgPC0gUkFESUFOQ0VfTVVMVF9CQU5EXzQqQjQgKyBSQURJQU5DRV9BRERfQkFORF80DQpMNSA8LSBSQURJQU5DRV9NVUxUX0JBTkRfNSpCNSArIFJBRElBTkNFX0FERF9CQU5EXzUNCkw2IDwtIFJBRElBTkNFX01VTFRfQkFORF82KkI2ICsgUkFESUFOQ0VfQUREX0JBTkRfNg0KTDcgPC0gUkFESUFOQ0VfTVVMVF9CQU5EXzcqQjcgKyBSQURJQU5DRV9BRERfQkFORF83DQoNCiMgRGV0ZXJtaW5hY2lvbiBkZSBsYSByYWRpYW5jaWEgbWluaW1hDQpMbWluMiA8LSBSQURJQU5DRV9NVUxUX0JBTkRfMiptaW4oZ2V0VmFsdWVzKEIyKSwgbmEucm0gPSBUUlVFKSsgUkFESUFOQ0VfQUREX0JBTkRfMg0KTG1pbjMgPC0gUkFESUFOQ0VfTVVMVF9CQU5EXzMqbWluKGdldFZhbHVlcyhCMyksIG5hLnJtID0gVFJVRSkrIFJBRElBTkNFX0FERF9CQU5EXzMNCkxtaW40IDwtIFJBRElBTkNFX01VTFRfQkFORF80Km1pbihnZXRWYWx1ZXMoQjQpLCBuYS5ybSA9IFRSVUUpKyBSQURJQU5DRV9BRERfQkFORF80DQpMbWluNSA8LSBSQURJQU5DRV9NVUxUX0JBTkRfNSptaW4oZ2V0VmFsdWVzKEI1KSwgbmEucm0gPSBUUlVFKSsgUkFESUFOQ0VfQUREX0JBTkRfNQ0KTG1pbjYgPC0gUkFESUFOQ0VfTVVMVF9CQU5EXzYqbWluKGdldFZhbHVlcyhCNiksIG5hLnJtID0gVFJVRSkrIFJBRElBTkNFX0FERF9CQU5EXzYNCkxtaW43IDwtIFJBRElBTkNFX01VTFRfQkFORF83Km1pbihnZXRWYWx1ZXMoQjcpLCBuYS5ybSA9IFRSVUUpKyBSQURJQU5DRV9BRERfQkFORF83DQoNCiMgRGV0ZXJtaW5hY2lvbiByYWRpYW5jaWEgZGVsIG9iamV0byBvc2N1cm8NCkxET1MyIDwtIDAuMDEqRVNVTjIqc2luKFNVTl9FTEVWQVRJT05fUikvcGkqKGReMikNCkxET1MzIDwtIDAuMDEqRVNVTjMqc2luKFNVTl9FTEVWQVRJT05fUikvcGkqKGReMikNCkxET1M0IDwtIDAuMDEqRVNVTjQqc2luKFNVTl9FTEVWQVRJT05fUikvcGkqKGReMikNCkxET1M1IDwtIDAuMDEqRVNVTjUqc2luKFNVTl9FTEVWQVRJT05fUikvcGkqKGReMikNCkxET1M2IDwtIDAuMDEqRVNVTjYqc2luKFNVTl9FTEVWQVRJT05fUikvcGkqKGReMikNCkxET1M3IDwtIDAuMDEqRVNVTjcqc2luKFNVTl9FTEVWQVRJT05fUikvcGkqKGReMikNCg0KIyBEZXRlcm1pbmFyIGVsIGVmZWN0byBicnVtYQ0KTFAyIDwtIExtaW4yLUxET1MyDQpMUDMgPC0gTG1pbjMtTERPUzMNCkxQNCA8LSBMbWluNC1MRE9TNA0KTFA1IDwtIExtaW41LUxET1M1DQpMUDYgPC0gTG1pbjYtTERPUzYNCkxQNyA8LSBMbWluNy1MRE9TNw0KDQojIEJhbmRhIFJlZmxlY3RhbmNpYSBTdXBlcmZpY2llIERhcmsgT2JqZWN0IFN1YnN0cmN0aW9uIChET1MpDQpSU19kb3NfQjIgPC0gKHBpKihMMi1MUDIpKihkKV4yKS8oRVNVTjIqc2luKFNVTl9FTEVWQVRJT05fUikpDQpSU19kb3NfQjMgPC0gKHBpKihMMy1MUDMpKihkKV4yKS8oRVNVTjMqc2luKFNVTl9FTEVWQVRJT05fUikpDQpSU19kb3NfQjQgPC0gKHBpKihMNC1MUDQpKihkKV4yKS8oRVNVTjQqc2luKFNVTl9FTEVWQVRJT05fUikpDQpSU19kb3NfQjUgPC0gKHBpKihMNS1MUDUpKihkKV4yKS8oRVNVTjUqc2luKFNVTl9FTEVWQVRJT05fUikpDQpSU19kb3NfQjYgPC0gKHBpKihMNi1MUDYpKihkKV4yKS8oRVNVTjYqc2luKFNVTl9FTEVWQVRJT05fUikpDQpSU19kb3NfQjcgPC0gKHBpKihMNy1MUDcpKihkKV4yKS8oRVNVTjcqc2luKFNVTl9FTEVWQVRJT05fUikpDQoNCiMgQ29tYmluYWNpb24gZGUgYmFuZGFzDQpMOF9CMjM0NTY3X1JTX0RPUyA8LSBzdGFjayhSU19kb3NfQjIsUlNfZG9zX0IzLFJTX2Rvc19CNCxSU19kb3NfQjUsUlNfZG9zX0I2LFJTX2Rvc19CNykNCg0KIyBFeHBvcnRhY2lvbiBpbWFnZW4gcmVmbGVjdGFuY2lhIFN1cGVyZmllIERPUw0KDQp3cml0ZVJhc3RlcihMOF9CMjM0NTY3X1JTX0RPUywiTDhfQjIzNDU2N19SU19ET1MxLnRpZiIsIGRyaXZlcm5hbWU9Ikd0aWZmIiwgb3ZlcndyaXRlPVRSVUUpDQpgYGANCiMNCiMNCmBgYHtyfQ0KDQojIyBUZW1hOiBUZW1wZXJhdHVyYSBkZSBicmlsbG8gZGUgTGFuZHNhdCA4IFRJUlMNCg0KDQojIEltcG9ydGFyIGxhcyBpbWFnZW5lcw0KQjEwID0gcmFzdGVyKCJMQzA4X0wxVFBfMDA4MDU3XzIwMTgxMjMwXzIwMTkwMTMwXzAxX1QxX0IxMC5USUYiKQ0KQjExID0gcmFzdGVyKCJMQzA4X0wxVFBfMDA4MDU3XzIwMTgxMjMwXzIwMTkwMTMwXzAxX1QxX0IxMS5USUYiKQ0KDQojIEsgaW1hZ2VuIExhbmRzYXQgOCBUSVJTDQpLMV9DT05TVEFOVF9CQU5EXzEwID0gNzc0Ljg4NTMNCksyX0NPTlNUQU5UX0JBTkRfMTAgPSAxMzIxLjA3ODkNCksxX0NPTlNUQU5UX0JBTkRfMTEgPSA0ODAuODg4Mw0KSzJfQ09OU1RBTlRfQkFORF8xMSA9IDEyMDEuMTQ0Mg0KDQojIERhdG9zIGRlIHJhZGlhbmNpYSBkZWwgc2Vuc29yIGltYWdlbiBMYW5kc2F0DQpSQURJQU5DRV9NVUxUX0JBTkRfMTAgPSAzLjM0MjBFLTA0DQpSQURJQU5DRV9NVUxUX0JBTkRfMTEgPSAzLjM0MjBFLTA0DQoNClJBRElBTkNFX0FERF9CQU5EXzEwID0gMC4xMDAwMA0KUkFESUFOQ0VfQUREX0JBTkRfMTEgPSAwLjEwMDAwDQoNCiMgRWxpbWFyIHZhbG9yZXMgbnVsb3MNCkIxMFtCMTA9PTBdIDwtIE5BDQpCMTFbQjExPT0wXSA8LSBOQQ0KDQojIERldGVybWluYW5jaW9uIGRlIGxhIFJhZGlhbmNhDQpMMTAgPC0gUkFESUFOQ0VfTVVMVF9CQU5EXzEwKihuYS5vbWl0KEIxMCkpICsgUkFESUFOQ0VfQUREX0JBTkRfMTANCkwxMSA8LSBSQURJQU5DRV9NVUxUX0JBTkRfMTEqKG5hLm9taXQoQjExKSkgKyBSQURJQU5DRV9BRERfQkFORF8xMQ0KDQoNCiMgRGV0ZXJtaW5hY2lvbiBUZW1wZXJhdHVyYSBkZSBicmlsbG8gQ2Vsc2l1cw0KVEJfQjEwIDwtIEsyX0NPTlNUQU5UX0JBTkRfMTAvbG9nKEsxX0NPTlNUQU5UX0JBTkRfMTAvTDEwKzEpIC0gMjczLjE1DQpUQl9CMTEgPC0gSzJfQ09OU1RBTlRfQkFORF8xMS9sb2coSzFfQ09OU1RBTlRfQkFORF8xMS9MMTErMSkgLSAyNzMuMTUNCg0KIyBFeHBvcnRhY2lvbiBpbWFnZW4gVGVtcGVyYXR1cmEgZGUgYnJpbGxvIENlbHNpdXMNCndyaXRlUmFzdGVyKFRCX0IxMCwiTDhfVEJfQjEwLnRpZiIsIGRyaXZlcm5hbWU9Ikd0aWZmIixvdmVyd3JpdGU9VFJVRSkNCndyaXRlUmFzdGVyKFRCX0IxMSwiTDhfVEJfQjExLnRpZiIsIGRyaXZlcm5hbWU9Ikd0aWZmIixvdmVyd3JpdGU9VFJVRSkNCg0KYGBgDQojDQojDQpgYGB7cn0NCiMjUmVwcm95ZWN0YXIgeSByZWNvcnRlIGRlIGltYWdlcyBjb24gbGEgem9uYSBkZSBlc3R1ZGlvIExhbmRzYXQgOCBPTEkNCg0KIyBJbXBvcnRhciBsYXMgaW1hZ2VuZXMNCkw4X0IyMzQ1NjdfUlNfRE9TID0gc3RhY2soIkw4X0IyMzQ1NjdfUlNfRE9TMS50aWYiKQ0KDQojIFZlcmlmaWNhciBzaXN0ZW1hcyBkZSBjb29yZGVuYWRhDQpzdF9jcnMoTDhfQjIzNDU2N19SU19ET1MpDQoNCiMgUmVwcm95ZWN0YXIgaW1hZ2VuDQpMOF9CMjM0NTY3X1JTX0RPU19weSA9IHByb2plY3RSYXN0ZXIoTDhfQjIzNDU2N19SU19ET1MsIGNycyA9ICIraW5pdD1lcHNnOjMyNjE4IikNCg0KIyMgaW1wb3J0YXIgc2hhcGVmaWxlDQpab25hX1VUTV8xOG4gPC0gc2hhcGVmaWxlKCJmYWNhc2hhcGUiKQ0Kc3RfY3JzKFpvbmFfVVRNXzE4bikNCg0KIyBSZWNvcnRhciBpbWFnZW4gc2VndW4gc2hhcGVmaWxlIExhbmRzYXQgOCBleHRlbnNpb24NCmNsaXBfTDhfQjIzNDU2N19SU19ET1NfcHkgPC0gY3JvcChMOF9CMjM0NTY3X1JTX0RPU19weSxab25hX1VUTV8xOG4pDQoNCiMgVmlzdWFsaXphY2lvbiBkZWwgcmVjb3J0ZSBkZSBsYSB6b25hIGRlIGVzdHVkaW8NCnBsb3RSR0IoY2xpcF9MOF9CMjM0NTY3X1JTX0RPU19weSwgNSw0LDMsIHN0cmV0Y2ggPSAibGluIikNCg0KIyBHdWFyZGFyIGFyY2hpdm8gcG9yIGJhbmRhIHJlY29ydGFkYSB5IHByb3llY3RhZGENCkw4X1NSX0IyIDwtIGNsaXBfTDhfQjIzNDU2N19SU19ET1NfcHkkTDhfQjIzNDU2N19SU19ET1MxLjENCkw4X1NSX0IzIDwtIGNsaXBfTDhfQjIzNDU2N19SU19ET1NfcHkkTDhfQjIzNDU2N19SU19ET1MxLjINCkw4X1NSX0I0IDwtIGNsaXBfTDhfQjIzNDU2N19SU19ET1NfcHkkTDhfQjIzNDU2N19SU19ET1MxLjMNCkw4X1NSX0I1IDwtIGNsaXBfTDhfQjIzNDU2N19SU19ET1NfcHkkTDhfQjIzNDU2N19SU19ET1MxLjQNCkw4X1NSX0I2IDwtIGNsaXBfTDhfQjIzNDU2N19SU19ET1NfcHkkTDhfQjIzNDU2N19SU19ET1MxLjUNCkw4X1NSX0I3IDwtIGNsaXBfTDhfQjIzNDU2N19SU19ET1NfcHkkTDhfQjIzNDU2N19SU19ET1MxLjYNCg0KIyBFeHBvcnRhY2lvbiBpbWFnZW4gcmVmbGVjdGFuY2lhIFN1cGVyZmllIERPUzEgUHJveWVjdGFkbyB5IHJlY29ydGFkbw0Kd3JpdGVSYXN0ZXIoY2xpcF9MOF9CMjM0NTY3X1JTX0RPU19weSwiY2xpcF9MOF9CMjM0NTY3X1JTX0RPU19weS50aWYiLCBkcml2ZXJuYW1lPSJHdGlmZiIsb3ZlcndyaXRlPVRSVUUpDQp3cml0ZVJhc3RlcihMOF9TUl9CMiwiTDhfU1JfQjIudGlmIiwgZHJpdmVybmFtZT0iR3RpZmYiLG92ZXJ3cml0ZT1UUlVFKQ0Kd3JpdGVSYXN0ZXIoTDhfU1JfQjMsIkw4X1NSX0IzLnRpZiIsIGRyaXZlcm5hbWU9Ikd0aWZmIixvdmVyd3JpdGU9VFJVRSkNCndyaXRlUmFzdGVyKEw4X1NSX0I0LCJMOF9TUl9CNC50aWYiLCBkcml2ZXJuYW1lPSJHdGlmZiIsb3ZlcndyaXRlPVRSVUUpDQp3cml0ZVJhc3RlcihMOF9TUl9CNSwiTDhfU1JfQjUudGlmIiwgZHJpdmVybmFtZT0iR3RpZmYiLG92ZXJ3cml0ZT1UUlVFKQ0Kd3JpdGVSYXN0ZXIoTDhfU1JfQjYsIkw4X1NSX0I2LnRpZiIsIGRyaXZlcm5hbWU9Ikd0aWZmIixvdmVyd3JpdGU9VFJVRSkNCndyaXRlUmFzdGVyKEw4X1NSX0I3LCJMOF9TUl9CNy50aWYiLCBkcml2ZXJuYW1lPSJHdGlmZiIsb3ZlcndyaXRlPVRSVUUpDQpgYGANCiMNCiMNCmBgYHtyfQ0KIyNDbGFzaWZpY2FjaW9uIHN1cGVydmlzYWRhIGNvbiBSYW5kb20gRm9yZXN0IC0gTGFuZHNhdCA4IE9MSQ0KDQojIEFncmVnYXIgcmFzdGVyIG11bHRpYmFuZGFzIGRlIGxhbmRzYXQgOA0KTDhfZmFjYSA8LSBzdGFjaygiY2xpcF9MOF9CMjM0NTY3X1JTX0RPU19weS50aWYiKQ0KDQojIERpbWVuc2lvbiBkZSBsYSBiYW5kYQ0KZGltKEw4X2ZhY2EpDQoNCiMgTm9tYnJlIGRlIGxhcyBiYW5kYXMNCm5hbWVzKEw4X2ZhY2EpDQoNCiMgY2FtYmlhciBub21icmUgZGUgbGFzIGJhbmRhcw0KbmFtZXMoTDhfZmFjYSkgPC0gYygiQkxVRSIsICJHUkVFTiIsICJSRUQiLCAiTklSIiwgIlNXSVIxIiwgIlNXSVIyIikNCg0KIyBBZ3JlZ2FyIGxvcyBzaGFwZWZpbGUgZGVsIFJPSSAyMDE5DQpyb2kgPC0gc2hhcGVmaWxlKCJjbGFzaWZpY2FjaW9uLnNocCIpDQoNCiMgSW5mb3JtYWNpb24gZGUgbGEgdGFibGEgZGUgYXRyaWJ1IGRlbCBzaHANCnByaW50KHJvaSkNCmhlYWQocm9pQGRhdGEsMTApDQoNCiMgUGxvdGVhbW9zIGxhIGltYWdlbiB5IGxhcyBwdW50b3MgZGUgbXVlc3RyZW8NCnBsb3RSR0IoTDhfMjAxNSw1LDQsMywgc3RyZXRjaD0ibGluIikNCnBsb3Qocm9pLGFkZD1UUlVFLGNvbD0icmVkIixwY2ggPSAxNSwgbHdkPTMpDQoNCiMgRXh0cmFlciBsb3MgdmFsb3JlcyBkZWwgcmFzdGVyIGEgdGFibGENCkV4dHJhZXJfUl9TaHAgPC0gZXh0cmFjdChMOF8yMDE1LHJvaSkgIyB0ZW5lbW9zIGxvcyB2YWxvcmVzIFJlZmxlY3RhbmNpYSBTdXBlcmZpY2llDQpwcmludChFeHRyYWVyX1JfU2hwKQ0KDQojIFNlbGVjY2lvbmFyIGxhIGNvbHVtbmEgZGUgbGFzIGNsYXNlcw0KQ2xhc3MgPC0gcm9pQGRhdGEkQ2xhc3MNCnByaW50KENsYXNzKQ0KDQojIENyZWFyIHVuIGRhdGEgZnJhbWUgY29uIGxvcyBkYXRvcyByZWZsZWN0YW5jaWEgc3VwZXJmaWNpZSB5IGxhcyBjbGFzZXMNCnRhYmxhIDwtIGRhdGEuZnJhbWUoRXh0cmFlcl9SX1NocCwgQ2xhc3MpDQpzdW1tYXJ5KHRhYmxhJENsYXNzKQ0KDQojIFNlcGVyYW5kbyBsb3MgZGF0b3MgZW4gRW50cmVuYW1pZW50byB5IHBydWViYQ0KTXVlc3RyYSA8LSBzYW1wbGUoMToyMjM1LDEwMDApICMgc2FtcGxlIHNlcGFyYSBhbGVhdG9yaWFtZW50ZSBsb3MgZGF0b3MNClBydWViYXMgPC0gdGFibGFbTXVlc3RyYSxdDQpFbnRyZW5hbWllbnRvIDwtIHRhYmxhWy1NdWVzdHJhLF0NCg0KIyBBcGxpY2FuZG8gZWwgYWxnb3JpdG1vIFJhbmRvbSBGb3Jlc3QNCm1vZGVsbyA8LSByYW5kb21Gb3Jlc3QoQ2xhc3Mgfi4sZGF0YT1FbnRyZW5hbWllbnRvLCBpbXBvcnRhbmNlPVRSVUUpDQpwcmVkaWNjaW9uPC0gcHJlZGljdChtb2RlbG8sIFBydWViYXNbLC03XSkNCg0KIyBEZWZpbmltb3MgbGEgcGFsZXRhIGRlIGNvbG9yZXMgc2VndW4gbGFzIDcgY2xhc2VzDQpteWNvbG9yIDwtIGMoIiMwMEZGMDAiLCIjMDAwMEZGIiwiIzIyOEIyMiIsIiNGRjE0OTMiLCAiIzFDMUNFMiIsICIjMUMxQ0UyIiAsIiM1NDU0QUEiKQ0KDQojIyBWaXN1YWxpemFyIHByZWRpY2Npb24gYSBvYnRlbmVyDQojIyBDYW50aWRhZCBkZSBwdW50byBxdWUgc2UgZW5jdWVudHJhIGVuIGxhIGNsYXNlDQpwbG90KHByZWRpY2Npb24sIG1haW4gPSAiTnVtZXJvIGRlIHBpeGVsZXMgaWRlbnRpZmljYWRvcyIsDQogICAgIGF4ZXMgPSBUUlVFLCB4bGFiID0gIkNsYXNlcyIsIHlsYWIgPSAiTnVtZXJvIGRlIHBpeGVsIiwgY29sPSBteWNvbG9yKQ0KDQojIE1hdHJpeiBkZSBjb25mdXNpb24NCk1DPC10YWJsZShQcnVlYmFzWyw3XSxwcmVkaWNjaW9uKQ0KTUMNCg0KIyBJbmRpY2UgZGUgS2FwcGENCmthcHBhPC1zdW0oZGlhZyhNQykpL3N1bShNQykNCmthcHBhDQoNCiMgRWplY3V0YW1vcyBlbCBjbGFzaWZpY2Fkb3IgUmFuZG9tIEZvcmVzdA0KYmVnaW5DbHVzdGVyKCkNCg0KIyMgOCBjb3JlcyBkZXRlY3RlZCwgdXNpbmcgNw0KcmZfY2xhc3M8LSBjbHVzdGVyUihMOF8yMDE1LCByYXN0ZXI6OnByZWRpY3QsYXJncyA9IGxpc3QobW9kZWwgPW1vZGVsbykpDQplbmRDbHVzdGVyKCkNCg0KIyBQbG90ZWFtb3MgbGEgY2xhc2lmaWNhY2nDs24gY29uIFN1cHBvcnQgdmVjdG9yIG1hY2hpbmUNCnBsb3QocmZfY2xhc3MsbWFpbj1wYXN0ZSgia2FwcGEgPSAiLGthcHBhLHNlcCA9IiIpLGNvbCA9bXljb2xvcixjZXgubGFiPTAuOCwNCiAgICAgY2V4LmF4aXM9MC44LGNleC5tYWluPTAuOSkNCg0KIyBFeHBvcnRhY2lvbiBkZSBiYW5kYXMNCndyaXRlUmFzdGVyKHJmX2NsYXNzLCJDbGFzc19MOF9SYW5kb21fRm9yZXN0LnRpZiIsIGRyaXZlcm5hbWU9Ikd0aWZmIixvdmVyd3JpdGU9VFJVRSkNCmBgYA0KIw0KIw0KYGBge3J9DQojIyBDbGFzaWZpY2FjaW9uIHN1cGVydmlzYWRhIGNvbiBEZWNpc2lvbl9UcmVlIC0gTGFuZHNhdCA4IE9MSQ0KDQojIEFncmVnYXIgcmFzdGVyIG11bHRpYmFuZGFzIGRlIGxhbmRzYXQgOA0KTDhfZmFjYSA8LSBzdGFjaygiY2xpcF9MOF9CMjM0NTY3X1JTX0RPU19weS50aWYiKQ0KDQojIERpbWVuc2lvbiBkZSBsYSBiYW5kYQ0KZGltKEw4X2ZhY2EpDQoNCiMgTm9tYnJlIGRlIGxhcyBiYW5kYXMNCm5hbWVzKEw4X2ZhY2EpDQoNCiMgY2FtYmlhciBub21icmUgZGUgbGFzIGJhbmRhcw0KbmFtZXMoTDhfZmFjYSkgPC0gYygiQkxVRSIsICJHUkVFTiIsICJSRUQiLCAiTklSIiwgIlNXSVIxIiwgIlNXSVIyIikNCg0KIyBBZ3JlZ2FyIGxvcyBzaGFwZWZpbGUgZGVsIFJPSSAyMDE5DQoNCnJvaSA8LSBzaGFwZWZpbGUoImNsYXNpZmljYWNpb24uc2hwIikNCg0KIyBJbmZvcm1hY2lvbiBkZSBsYSB0YWJsYSBkZSBhdHJpYnUgZGVsIHNocA0KcHJpbnQocm9pKQ0KaGVhZChyb2lAZGF0YSwxMCkNCg0KIyBQbG90ZWFtb3MgbGEgaW1hZ2VuIHkgbGFzIHB1bnRvcyBkZSBtdWVzdHJlbw0KcGxvdFJHQihMOF8yMDE1LDUsNCwzLCBzdHJldGNoPSJsaW4iKQ0KcGxvdChyb2ksYWRkPVRSVUUsY29sPSJyZWQiLHBjaCA9IDE1LCBsd2Q9MykNCg0KIyBFeHRyYWVyIGxvcyB2YWxvcmVzIGRlbCByYXN0ZXIgYSB0YWJsYQ0KRXh0cmFlcl9SX1NocCA8LSBleHRyYWN0KEw4XzIwMTUscm9pKSAjIHRlbmVtb3MgbG9zIHZhbG9yZXMgUmVmbGVjdGFuY2lhIFN1cGVyZmljaWUNCnByaW50KEV4dHJhZXJfUl9TaHApDQoNCiMgU2VsZWNjaW9uYXIgbGEgY29sdW1uYSBkZSBsYXMgY2xhc2VzDQpDbGFzcyA8LSByb2lAZGF0YSRDbGFzcw0KcHJpbnQoQ2xhc3MpDQoNCiMgQ3JlYXIgdW4gZGF0YSBmcmFtZSBjb24gbG9zIGRhdG9zIHJlZmxlY3RhbmNpYSBzdXBlcmZpY2llIHkgbGFzIGNsYXNlcw0KdGFibGEgPC0gZGF0YS5mcmFtZShFeHRyYWVyX1JfU2hwLCBDbGFzcykNCnN1bW1hcnkodGFibGEkQ2xhc3MpDQoNCiMgU2VwZXJhbmRvIGxvcyBkYXRvcyBlbiBFbnRyZW5hbWllbnRvIHkgcHJ1ZWJhDQpNdWVzdHJhIDwtIHNhbXBsZSgxOjIyMzUsMTAwMCkgIyBzYW1wbGUgc2VwYXJhIGFsZWF0b3JpYW1lbnRlIGxvcyBkYXRvcw0KUHJ1ZWJhcyA8LSB0YWJsYVtNdWVzdHJhLF0NCkVudHJlbmFtaWVudG8gPC0gdGFibGFbLU11ZXN0cmEsXQ0KDQojIEFwbGljYW5kbyBlbCBhbGdvcml0bW8gRFQNCm1vZGVsbyA8LSBycGFydChDbGFzcyB+LixkYXRhPUVudHJlbmFtaWVudG8pDQoNCiMgUHJlZGljY2lvbiBkZWwgbW9kZWxvIGEgb2J0ZW5lcg0KcHJlZGljY2lvbiA8LSBwcmVkaWN0KG1vZGVsbywgUHJ1ZWJhc1ssLTddLHR5cGU9ImNsYXNzIikNCg0KIyBEZWZpbmltb3MgbGEgcGFsZXRhIGRlIGNvbG9yZXMgc2VndW4gbGFzIDcgY2xhc2VzDQpteWNvbG9yIDwtIGMoIiMwMEZGMDAiLCIjMDAwMEZGIiwiIzIyOEIyMiIsIiNGRjE0OTMiLCAiIzFDMUNFMiIsICIjMUMxQ0UyIiAsIiM1NDU0QUEiKQ0KDQojIyBWaXN1YWxpemFyIHByZWRpY2Npb24gYSBvYnRlbmVyDQojIyBDYW50aWRhZCBkZSBwdW50byBxdWUgc2UgZW5jdWVudHJhIGVuIGxhIGNsYXNlDQpwbG90KHByZWRpY2Npb24sIG1haW4gPSAiTnVtZXJvIGRlIHBpeGVsZXMgaWRlbnRpZmljYWRvcyIsDQogICAgIGF4ZXMgPSBUUlVFLCB4bGFiID0gIkNsYXNlcyIsIHlsYWIgPSAiTnVtZXJvIGRlIHBpeGVsIiwgY29sPSBteWNvbG9yKQ0KDQojIERldGVybWluYWNpb24gZGVsIGluZGljZSBkZSBrYXBwYSBkZSBsYSBjbGFzaWZpY2FjaW9uDQpNQyA8LSB0YWJsZShQcnVlYmFzWyw3XSxwcmVkaWNjaW9uKQ0Ka2FwcGE8LXN1bShkaWFnKE1DKSkvc3VtKE1DKQ0KcHJpbnQoa2FwcGEpDQoNCiMgR3JhZmljYW5kbyBlbCBhcmJvbCBkZSBkZWNpc2lvbg0KcHJwKG1vZGVsbywgZXh0cmE9MTA0LCBicmFuY2gudHlwZSA9MSwgYm94LmNvbCA9IGMoInBpbmsiLCJwYWxlZ3JlZW4zIilbbW9kZWxvJGZyYW1lJHl2YWxdKQ0KDQojIEVqZWN1dGFtb3MgZWwgY2xhc2lmaWNhZG9yIERUDQpiZWdpbkNsdXN0ZXIoKQ0KDQojIyA4IGNvcmVzIGRldGVjdGVkLCB1c2luZyA3DQp0Y19jbGFzczwtIGNsdXN0ZXJSKEw4XzIwMTUsIHJhc3Rlcjo6cHJlZGljdCxhcmdzID0gbGlzdChtb2RlbCA9bW9kZWxvLHR5cGU9ImNsYXNzIikpDQplbmRDbHVzdGVyKCkNCg0KIyBQbG90ZWFtb3MgbGEgY2xhc2lmaWNhY2nDs24gY29uIFN1cHBvcnQgdmVjdG9yIG1hY2hpbmUNCnBsb3QodGNfY2xhc3MsbWFpbj1wYXN0ZSgia2FwcGEgPSAiLGthcHBhLHNlcCA9IiIpLGNvbCA9bXljb2xvcixjZXgubGFiPTAuOCwNCiAgICAgY2V4LmF4aXM9MC44LGNleC5tYWluPTAuOSkNCg0KIyBFeHBvcnRhY2lvbiBkZSBiYW5kYXMNCndyaXRlUmFzdGVyKHRjX2NsYXNzLCJDbGFzc19MOF9EZWNpc2lvbl9UcmVlLnRpZiIsIGRyaXZlcm5hbWU9Ikd0aWZmIixvdmVyd3JpdGU9VFJVRSkNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=