Landsat 8 recopilada el 14 de junio de 2017. El subconjunto cubre el area entre Concord y Stockton, en California, EE. UU.

This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Seleccionar la carpeta de destino
#C:/Users/David/Desktop/PERCEPCION REMOTA
getwd()
[1] "C:/Users/David/Desktop/PERCEPCION REMOTA"

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

Se inserta un nuevo bloque de codigo ctrl+alt+i para descargar los datos con los que voy a trabajar
dir.create('data', showWarnings = FALSE)
if (!file.exists('data/rs/samples.rds')) {
    download.file('https://biogeo.ucdavis.edu/data/rspatial/rsdata.zip', dest = './data/rsdata.zip')
    unzip('./data/rsdata.zip', exdir='data')}
Crear objetos RasterLayer para capas individuales Landsat (bandas) y las llamamos enrutandolas como por ejemplo (‘./data/rs/LC08_044034_20170614_B2.tif’)
IMPORTANTE: instalar paquetes: raster, sp, rgdal
En este capitulo se describe la forma de acceder y explorar por satelite datos de teledeteccion con R . Tambien mostramos como usarlos para hacer mapas.
Utilizaremos principalmente un subconjunto espacial de una escena Landsat 8 recopilada el 14 de junio de 2017. El subconjunto cubre el area entre Concord y Stockton , en California, EE. UU.
Todas las escenas de imagenes de Landsat tienen una identificacion de producto y metadatos unicos. Puede encontrar la informacion sobre el sensor Landsat, el satelite, la ubicacion en la Tierra (ruta WRS, fila WRS) y la fecha de adquisicion a partir de la ID del producto. Por ejemplo, el identificador de producto de los datos que utilizaremos es ‘LC08_044034_20170614’. Con base en esta guia , puede ver que el Sensor-Satelite es OLI / TIRS combinados Landsat 8, WRS Path 44, WRS Row 34 y recopilados el 14 de junio de 2017. Las escenas de Landsat se entregan mas comunmente como un archivo comprimido, que contiene archivos separados para cada banda
Comenzaremos explorando y visualizando los datos (consulte las instrucciones en el Capitulo 1 para obtener instrucciones de descarga de datos si aun no lo ha hecho).

TITULO: PROPIEDADES DE IMAGEN

Crear objetos RasterLayer para capas individuales Landsat (bandas)
library(raster)
# azul
b2 = raster('./data/rs/LC08_044034_20170614_B2.tif')
# verde
b3 = raster('./data/rs/LC08_044034_20170614_B3.tif')
# rojo
b4 = raster('./data/rs/LC08_044034_20170614_B4.tif')
# Infrarojo cercano (NIR)
b5 = raster('./data/rs/LC08_044034_20170614_B5.tif')
se inserta un nuevo bloque de codigo ctrl+alt+i: imprimir las variables para verificar, por ejemplo:
b2
class      : RasterLayer 
dimensions : 1245, 1497, 1863765  (nrow, ncol, ncell)
resolution : 30, 30  (x, y)
extent     : 594090, 639000, 4190190, 4227540  (xmin, xmax, ymin, ymax)
crs        : +proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
source     : C:/Users/David/Desktop/PERCEPCION REMOTA/data/rs/LC08_044034_20170614_B2.tif 
names      : LC08_044034_20170614_B2 
values     : 0.0748399, 0.7177562  (min, max)
Puede ver 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 muestra como puede acceder a varias propiedades desde un objeto Raster * (esto es lo mismo para cualquier conjunto de datos raster).

# coordinate reference system (CRS)
crs(b2)
CRS arguments:
 +proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
## CRS arguments:
##  +proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84
## +towgs84=0,0,0
# Number of cells, rows, columns
ncell(b2)
[1] 1863765
## [1] 1863765
dim(b2)
[1] 1245 1497    1
## [1] 1245 1497    1
# spatial resolution
res(b2)
[1] 30 30
## [1] 30 30
# Number of bands
nlayers(b2)
[1] 1
## [1] 1
# Do the bands have the same extent, number of rows and columns, projection, resolution, and origin
compareRaster(b2,b3)
[1] TRUE
## [1] TRUE
Puede crear un RasterStack (un objeto con varias capas) a partir de los objetos RasterLayer (banda unica) existentes
Las bandas que llamos son las mismas que hemos llamadodo arriba como RasterLayer
s = stack(b5, b4, b3)
# Check the properties of the RasterStack
s
class      : RasterStack 
dimensions : 1245, 1497, 1863765, 3  (nrow, ncol, ncell, nlayers)
resolution : 30, 30  (x, y)
extent     : 594090, 639000, 4190190, 4227540  (xmin, xmax, ymin, ymax)
crs        : +proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
names      : LC08_044034_20170614_B5, LC08_044034_20170614_B4, LC08_044034_20170614_B3 
min values :            0.0008457669,            0.0208406653,            0.0425921641 
max values :               1.0124315,               0.7861769,               0.6924697 
## class      : RasterStack
## dimensions : 1245, 1497, 1863765, 3  (nrow, ncol, ncell, nlayers)
## resolution : 30, 30  (x, y)
## extent     : 594090, 639000, 4190190, 4227540  (xmin, xmax, ymin, ymax)
## crs        : +proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
## names      : LC08_044034_20170614_B5, LC08_044034_20170614_B4, LC08_044034_20170614_B3
## min values :            0.0008457669,            0.0208406653,            0.0425921641
## max values :               1.0124315,               0.7861769,    
Tambien puede crear el RasterStack usando los nombres de archivo
# first create a list of raster layers to use
filenames = paste0(' data/rs/LC08_044034_20170614_B', 1:11, ".tif")
filenames
 [1] " data/rs/LC08_044034_20170614_B1.tif"  " data/rs/LC08_044034_20170614_B2.tif" 
 [3] " data/rs/LC08_044034_20170614_B3.tif"  " data/rs/LC08_044034_20170614_B4.tif" 
 [5] " data/rs/LC08_044034_20170614_B5.tif"  " data/rs/LC08_044034_20170614_B6.tif" 
 [7] " data/rs/LC08_044034_20170614_B7.tif"  " data/rs/LC08_044034_20170614_B8.tif" 
 [9] " data/rs/LC08_044034_20170614_B9.tif"  " data/rs/LC08_044034_20170614_B10.tif"
[11] " data/rs/LC08_044034_20170614_B11.tif"
##  [1] "./data/rs/LC08_044034_20170614_B1.tif"
##  [2] "./data/rs/LC08_044034_20170614_B2.tif"
##  [3] "./data/rs/LC08_044034_20170614_B3.tif"
##  [4] "./data/rs/LC08_044034_20170614_B4.tif"
##  [5] "./data/rs/LC08_044034_20170614_B5.tif"
##  [6] "./data/rs/LC08_044034_20170614_B6.tif"
##  [7] "./data/rs/LC08_044034_20170614_B7.tif"
##  [8] "./data/rs/LC08_044034_20170614_B8.tif"
##  [9] "./data/rs/LC08_044034_20170614_B9.tif"
## [10] "./data/rs/LC08_044034_20170614_B10.tif"
## [11] "./data/rs/LC08_044034_20170614_B11.tif"
landsat = stack(filenames)
landsat
class      : RasterStack 
dimensions : 1245, 1497, 1863765, 11  (nrow, ncol, ncell, nlayers)
resolution : 30, 30  (x, y)
extent     : 594090, 639000, 4190190, 4227540  (xmin, xmax, ymin, ymax)
crs        : +proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
names      : LC08_044034_20170614_B1, LC08_044034_20170614_B2, LC08_044034_20170614_B3, LC08_044034_20170614_B4, LC08_044034_20170614_B5, LC08_044034_20170614_B6, LC08_044034_20170614_B7, LC08_044034_20170614_B8, LC08_044034_20170614_B9, LC08_044034_20170614_B10, LC08_044034_20170614_B11 
min values :            9.641791e-02,            7.483990e-02,            4.259216e-02,            2.084067e-02,            8.457669e-04,           -7.872183e-03,           -5.052945e-03,            3.931751e-02,           -4.337332e-04,             2.897978e+02,             2.885000e+02 
max values :              0.73462820,              0.71775615,              0.69246972,              0.78617686,              1.01243150,              1.04320455,              1.11793602,              0.82673049,              0.03547901,             322.43139648,             317.99530029 
## class      : RasterStack
## dimensions : 1245, 1497, 1863765, 11  (nrow, ncol, ncell, nlayers)
## resolution : 30, 30  (x, y)
## extent     : 594090, 639000, 4190190, 4227540  (xmin, xmax, ymin, ymax)
## crs        : +proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
## names      : LC08_044034_20170614_B1, LC08_044034_20170614_B2, LC08_044034_20170614_B3, LC08_044034_20170614_B4, LC08_044034_20170614_B5, LC08_044034_20170614_B6, LC08_044034_20170614_B7, LC08_044034_20170614_B8, LC08_044034_20170614_B9, LC08_044034_20170614_B10, LC08_044034_20170614_B11
## min values :            9.641791e-02,            7.483990e-02,            4.259216e-02,            2.084067e-02,            8.457669e-04,           -7.872183e-03,           -5.052945e-03,            3.931751e-02,           -4.337332e-04,             2.897978e+02,             2.885000e+02
## max values :              0.73462820,              0.71775615,              0.69246972,              0.78617686,              1.01243150,              1.04320455,              1.11793602,              0.82673049,              0.03547901,             322.43139648,             317.99530029
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.
par(mfrow = c(2,2))
There were 30 warnings (use warnings() to see them)
plot(b2, main = "Blue", col = gray(0:100 / 100))
plot(b3, main = "Green", col = gray(0:100 / 100))
plot(b4, main = "Red", col = gray(0:100 / 100))
plot(b5, main = "NIR", col = gray(0:100 / 100))

Echa un vistazo a las leyendas de los mapas creados anteriormente. Pueden variar entre 0 y 1. Observe la diferencia en el sombreado y el rango de leyendas entre las diferentes bandas. Esto se debe a que las diferentes caracteristicas de la superficie reflejan la radiacion solar incidente de manera diferente. 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.
No obtenemos tanta informacion de estas parcelas en escala de grises; a menudo se combinan en un “compuesto” para crear tramas mas interesantes. Puede obtener mas informacion sobre los compuestos de color en la teledeteccion aqui y tambien en la seccion a continuacion.
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 - plotRGB - metodo 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” -
landsatRGB = stack(b4, b3, b2)
plotRGB(landsatRGB, axes = TRUE, stretch = "lin", main = "Landsat True Color Composite")

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).

TITULO: SUBCONJUNTO Y RENOMBRAR BANDAS

Puede seleccionar capas (bandas) especificas mediante la - subset - funcion o mediante indexacion.
# select first 3 bands only
landsatsub1 = subset(landsat, 1:3)
# same
landsatsub2 = landsat[[1:3]]
# Number of bands in the original and new data
nlayers(landsat)
[1] 11
## [1] 11
nlayers(landsatsub1)
[1] 3
## [1] 3
nlayers(landsatsub2)
[1] 3
## [1] 3
No usaremos las ultimas cuatro bandas - landsat -. Puedes eliminar aquellos usando:
landsat = subset(landsat, 1:7)
Para mayor claridad, es util establecer los nombres de las bandas.
names(landsat)
[1] "LC08_044034_20170614_B1" "LC08_044034_20170614_B2" "LC08_044034_20170614_B3"
[4] "LC08_044034_20170614_B4" "LC08_044034_20170614_B5" "LC08_044034_20170614_B6"
[7] "LC08_044034_20170614_B7"
## [1] "LC08_044034_20170614_B1" "LC08_044034_20170614_B2"
## [3] "LC08_044034_20170614_B3" "LC08_044034_20170614_B4"
## [5] "LC08_044034_20170614_B5" "LC08_044034_20170614_B6"
## [7] "LC08_044034_20170614_B7"
names(landsat) = c('ultra-blue', 'blue', 'green', 'red', 'NIR', 'SWIR1', 'SWIR2')
names(landsat)
[1] "ultra.blue" "blue"       "green"      "red"        "NIR"        "SWIR1"      "SWIR2"     
## [1] "ultra.blue" "blue"       "green"      "red"        "NIR"
## [6] "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 - crop - funcion, utilizando un - extent - objeto u otro objeto espacial del que se puede extraer una Extension.
# Using extent
extent(landsat)
class      : Extent 
xmin       : 594090 
xmax       : 639000 
ymin       : 4190190 
ymax       : 4227540 
## class      : Extent
## xmin       : 594090
## xmax       : 639000
## ymin       : 4190190
## ymax       : 4227540
e = extent(624387, 635752, 4200047, 4210939)
# crop landsat by the extent
landsatcrop = crop(landsat, e)
Pregunta 2 : Tambien es posible la seleccion interactiva de la imagen. Use drawExtent y drawPoly para seleccionar un area de interes
Pregunta 3 : Use el `` Landsatcrop ’’ de RasterStack para crear un compuesto de color verdadero y falso

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
x = writeRaster(landsatcrop, filename="cropped-landsat.tif", overwrite=TRUE)
Alternativamente, puede utilizar el formato ‘raster-grd’.
writeRaster(landsatcrop, filename="cropped-landsat.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.
pairs(landsatcrop[[1:2]], main = "Ultra-blue versus Blue")

Trazado de reflejo en la longitud de onda roja contra reflejo en la longitud de onda NIR.
pairs(landsatcrop[[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 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 - extract - funcion 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 - Raster* - objeto para las celdas en las que se ubica un conjunto de puntos.
# load the polygons with land use land cover information
samp = readRDS('data/rs/samples.rds')
# generate 300 point samples from the polygons
ptsamp = spsample(samp, 300, type='regular')
# add the land cover class to the points
ptsamp$class = over(ptsamp, samp)$class
# extract values with points
df = extract(landsat, ptsamp)
# To see some of the reflectance values
head(df)
     ultra.blue      blue      green
[1,]  0.1351065 0.1173020 0.10157929
[2,]  0.1326343 0.1151767 0.09889016
[3,]  0.1356270 0.1332415 0.14410640
[4,]  0.1454509 0.1424365 0.16008930
[5,]  0.1598074 0.1680049 0.19600205
[6,]  0.1400944 0.1241549 0.11179360
            red       NIR     SWIR1
[1,] 0.10047328 0.1631471 0.2042646
[2,] 0.09769741 0.1678530 0.2016188
[3,] 0.19365992 0.3442072 0.3302411
[4,] 0.20805971 0.3220870 0.3971004
[5,] 0.26268786 0.4421865 0.4378276
[6,] 0.11508994 0.1862865 0.2442327
         SWIR2
[1,] 0.1674193
[2,] 0.1650121
[3,] 0.1929660
[4,] 0.2689119
[5,] 0.2693673
[6,] 0.2109006
##      ultra.blue      blue      green        red       NIR     SWIR1
## [1,]  0.1367547 0.1197091 0.10429009 0.10507080 0.1670290 0.2161921
## [2,]  0.1343041 0.1163694 0.09889016 0.09752392 0.1686988 0.2066501
## [3,]  0.1383812 0.1375354 0.15377855 0.20988137 0.3602552 0.3594528
## [4,]  0.1293813 0.1254127 0.13582218 0.18546245 0.3094872 0.2950440
## [5,]  0.1481184 0.1531496 0.17986734 0.24896033 0.3882957 0.4010257
## [6,]  0.1342608 0.1158490 0.10029978 0.09932390 0.1649471 0.2108356
##          SWIR2
## [1,] 0.1817324
## [2,] 0.1710843
## [3,] 0.2157801
## [4,] 0.1653591
## [5,] 0.2454254
## [6,] 0.1800408

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 - extract - funcion. 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.
ms = aggregate(df, list(ptsamp$class), mean)
# instead of the first column, we use row names
rownames(ms) = ms[,1]
ms = ms[,-1]
ms
##          ultra.blue      blue      green       red        NIR      SWIR1
## built     0.1864925 0.1795371 0.17953317 0.1958414 0.25448447 0.24850197
## cropland  0.1129813 0.0909645 0.08596722 0.0550344 0.48335462 0.16142085
## fallow    0.1319198 0.1164869 0.10453764 0.1151243 0.18012962 0.23139228
## open      0.1388014 0.1375235 0.15273163 0.2066425 0.34476670 0.35820877
## water     0.1336242 0.1165728 0.09922726 0.0785947 0.04909201 0.03360047
##               SWIR2
## built    0.20001306
## cropland 0.07314186
## fallow   0.19143030
## open     0.21346343
## water    0.02723398
Ahora trazamos el espectro medio de estas caracteristicas.
# Create a vector of color for the land cover classes for use in plotting
mycolor = c('darkred', 'yellow', 'burlywood', 'cyan', 'blue')
#transform ms from a data.frame to a matrix
ms = as.matrix(ms)
# First create an empty plot
plot(0, ylim=c(0,0.6), xlim = c(1,7), type='n', xlab="Bands", ylab = "Reflectance")
# add the different classes
for (i in 1:nrow(ms)){
  lines(ms[i,], type = "l", lwd = 3, lty = 1, col = mycolor[i])
}
# Title
title(main="Spectral Profile from Landsat", font.main = 2)
# Legend
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 ‘construido’, ‘en barbecho’ y ‘abierto’ tienen una reflectancia relativamente alta en las longitudes de onda mas largas.

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

LS0tDQp0aXRsZTogIioqSW1hZ2VuIExhbmRTYXQgOCwgQ2FsaWZvcm5pYSBFRS5VVSBfIERBR1oqKiINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQoNCiMjIyMjICoqTGFuZHNhdCA4IHJlY29waWxhZGEgZWwgMTQgZGUganVuaW8gZGUgMjAxNy4gRWwgc3ViY29uanVudG8gY3VicmUgZWwgYXJlYSBlbnRyZSBDb25jb3JkIHkgU3RvY2t0b24sIGVuIENhbGlmb3JuaWEsIEVFLiBVVS4qKg0KDQoNClRoaXMgaXMgYW4gW1IgTWFya2Rvd25dKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pIE5vdGVib29rLiBXaGVuIHlvdSBleGVjdXRlIGNvZGUgd2l0aGluIHRoZSBub3RlYm9vaywgdGhlIHJlc3VsdHMgYXBwZWFyIGJlbmVhdGggdGhlIGNvZGUuIA0KDQoNCiMjIyMjIFNlbGVjY2lvbmFyIGxhIGNhcnBldGEgZGUgZGVzdGlubyANCg0KDQpgYGB7cn0NCiNDOi9Vc2Vycy9EYXZpZC9EZXNrdG9wL1BFUkNFUENJT04gUkVNT1RBDQpnZXR3ZCgpDQpgYGANCg0KDQpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouIA0KDQoNCiMjIyMjIFNlIGluc2VydGEgdW4gbnVldm8gYmxvcXVlIGRlIGNvZGlnbyBjdHJsK2FsdCtpIHBhcmEgZGVzY2FyZ2FyIGxvcyBkYXRvcyBjb24gbG9zIHF1ZSB2b3kgYSB0cmFiYWphciANCg0KDQpgYGB7cn0NCmRpci5jcmVhdGUoJ2RhdGEnLCBzaG93V2FybmluZ3MgPSBGQUxTRSkNCmlmICghZmlsZS5leGlzdHMoJ2RhdGEvcnMvc2FtcGxlcy5yZHMnKSkgew0KICAgIGRvd25sb2FkLmZpbGUoJ2h0dHBzOi8vYmlvZ2VvLnVjZGF2aXMuZWR1L2RhdGEvcnNwYXRpYWwvcnNkYXRhLnppcCcsIGRlc3QgPSAnLi9kYXRhL3JzZGF0YS56aXAnKQ0KICAgIHVuemlwKCcuL2RhdGEvcnNkYXRhLnppcCcsIGV4ZGlyPSdkYXRhJyl9DQpgYGANCg0KDQojIyMjIyBDcmVhciBvYmpldG9zIFJhc3RlckxheWVyIHBhcmEgY2FwYXMgaW5kaXZpZHVhbGVzIExhbmRzYXQgKGJhbmRhcykgeSBsYXMgbGxhbWFtb3MgZW5ydXRhbmRvbGFzIGNvbW8gcG9yIGVqZW1wbG8gKCcuL2RhdGEvcnMvTEMwOF8wNDQwMzRfMjAxNzA2MTRfQjIudGlmJykNCg0KIyMjIyMqSU1QT1JUQU5URTogaW5zdGFsYXIgcGFxdWV0ZXM6IHJhc3Rlciwgc3AsIHJnZGFsKg0KDQojIyMjIyBFbiBlc3RlIGNhcGl0dWxvIHNlIGRlc2NyaWJlIGxhIGZvcm1hIGRlIGFjY2VkZXIgeSBleHBsb3JhciBwb3Igc2F0ZWxpdGUgZGF0b3MgZGUgdGVsZWRldGVjY2lvbiBjb24gUiAuIFRhbWJpZW4gbW9zdHJhbW9zIGNvbW8gdXNhcmxvcyBwYXJhIGhhY2VyIG1hcGFzLg0KDQojIyMjIyBVdGlsaXphcmVtb3MgcHJpbmNpcGFsbWVudGUgdW4gc3ViY29uanVudG8gZXNwYWNpYWwgZGUgdW5hIGVzY2VuYSBMYW5kc2F0IDggcmVjb3BpbGFkYSBlbCAxNCBkZSBqdW5pbyBkZSAyMDE3LiBFbCBzdWJjb25qdW50byBjdWJyZSBlbCBhcmVhIGVudHJlIENvbmNvcmQgeSBTdG9ja3RvbiAsIGVuIENhbGlmb3JuaWEsIEVFLiBVVS4NCg0KIyMjIyMgVG9kYXMgbGFzIGVzY2VuYXMgZGUgaW1hZ2VuZXMgZGUgTGFuZHNhdCB0aWVuZW4gdW5hIGlkZW50aWZpY2FjaW9uIGRlIHByb2R1Y3RvIHkgbWV0YWRhdG9zIHVuaWNvcy4gUHVlZGUgZW5jb250cmFyIGxhIGluZm9ybWFjaW9uIHNvYnJlIGVsIHNlbnNvciBMYW5kc2F0LCBlbCBzYXRlbGl0ZSwgbGEgdWJpY2FjaW9uIGVuIGxhIFRpZXJyYSAocnV0YSBXUlMsIGZpbGEgV1JTKSB5IGxhIGZlY2hhIGRlIGFkcXVpc2ljaW9uIGEgcGFydGlyIGRlIGxhIElEIGRlbCBwcm9kdWN0by4gUG9yIGVqZW1wbG8sIGVsIGlkZW50aWZpY2Fkb3IgZGUgcHJvZHVjdG8gZGUgbG9zIGRhdG9zIHF1ZSB1dGlsaXphcmVtb3MgZXMgJ0xDMDhfMDQ0MDM0XzIwMTcwNjE0Jy4gQ29uIGJhc2UgZW4gZXN0YSBndWlhICwgcHVlZGUgdmVyIHF1ZSBlbCBTZW5zb3ItU2F0ZWxpdGUgZXMgT0xJIC8gVElSUyBjb21iaW5hZG9zIExhbmRzYXQgOCwgV1JTIFBhdGggNDQsIFdSUyBSb3cgMzQgeSByZWNvcGlsYWRvcyBlbCAxNCBkZSBqdW5pbyBkZSAyMDE3LiBMYXMgZXNjZW5hcyBkZSBMYW5kc2F0IHNlIGVudHJlZ2FuIG1hcyBjb211bm1lbnRlIGNvbW8gdW4gYXJjaGl2byBjb21wcmltaWRvLCBxdWUgY29udGllbmUgYXJjaGl2b3Mgc2VwYXJhZG9zIHBhcmEgY2FkYSBiYW5kYQ0KDQojIyMjIyBDb21lbnphcmVtb3MgZXhwbG9yYW5kbyB5IHZpc3VhbGl6YW5kbyBsb3MgZGF0b3MgKGNvbnN1bHRlIGxhcyBpbnN0cnVjY2lvbmVzIGVuIGVsIENhcGl0dWxvIDEgcGFyYSBvYnRlbmVyIGluc3RydWNjaW9uZXMgZGUgZGVzY2FyZ2EgZGUgZGF0b3Mgc2kgYXVuIG5vIGxvIGhhIGhlY2hvKS4NCg0KDQoNCiMjIyAqKlRJVFVMTzogUFJPUElFREFERVMgREUgSU1BR0VOKioNCg0KIyMjIyMgQ3JlYXIgb2JqZXRvcyBSYXN0ZXJMYXllciBwYXJhIGNhcGFzIGluZGl2aWR1YWxlcyBMYW5kc2F0IChiYW5kYXMpDQoNCmBgYHtyfQ0KbGlicmFyeShyYXN0ZXIpDQoNCiMgYXp1bA0KYjIgPSByYXN0ZXIoJy4vZGF0YS9ycy9MQzA4XzA0NDAzNF8yMDE3MDYxNF9CMi50aWYnKQ0KIyB2ZXJkZQ0KYjMgPSByYXN0ZXIoJy4vZGF0YS9ycy9MQzA4XzA0NDAzNF8yMDE3MDYxNF9CMy50aWYnKQ0KIyByb2pvDQpiNCA9IHJhc3RlcignLi9kYXRhL3JzL0xDMDhfMDQ0MDM0XzIwMTcwNjE0X0I0LnRpZicpDQojIEluZnJhcm9qbyBjZXJjYW5vIChOSVIpDQpiNSA9IHJhc3RlcignLi9kYXRhL3JzL0xDMDhfMDQ0MDM0XzIwMTcwNjE0X0I1LnRpZicpDQoNCmBgYA0KDQoNCiMjIyMjIHNlIGluc2VydGEgdW4gbnVldm8gYmxvcXVlIGRlIGNvZGlnbyBjdHJsK2FsdCtpOiBpbXByaW1pciBsYXMgdmFyaWFibGVzIHBhcmEgdmVyaWZpY2FyLCBwb3IgZWplbXBsbzoNCg0KDQpgYGB7cn0NCmIyDQpgYGANCg0KDQojIyMjIyBQdWVkZSB2ZXIgbGEgcmVzb2x1Y2lvbiBlc3BhY2lhbCwgbGEgZXh0ZW5zaW9uLCBlbCBudW1lcm8gZGUgY2FwYXMsIGVsIHNpc3RlbWEgZGUgcmVmZXJlbmNpYSBkZSBjb29yZGVuYWRhcyB5IG1hcy4NCg0KDQoNCiMjIyAqKlRJVFVMTzogSU5GT1JNQUNJT04gREUgSU1BR0VOIFkgRVNUQURJU1RJQ0FTKioNCg0KDQojIyMgQSBjb250aW51YWNpb24gc2UgbXVlc3RyYSBjb21vIHB1ZWRlIGFjY2VkZXIgYSB2YXJpYXMgcHJvcGllZGFkZXMgZGVzZGUgdW4gb2JqZXRvIFJhc3RlciAqIChlc3RvIGVzIGxvIG1pc21vIHBhcmEgY3VhbHF1aWVyIGNvbmp1bnRvIGRlIGRhdG9zIHJhc3RlcikuDQoNCg0KYGBge3J9DQojIGNvb3JkaW5hdGUgcmVmZXJlbmNlIHN5c3RlbSAoQ1JTKQ0KY3JzKGIyKQ0KIyMgQ1JTIGFyZ3VtZW50czoNCiMjICArcHJvaj11dG0gK3pvbmU9MTAgK2RhdHVtPVdHUzg0ICt1bml0cz1tICtub19kZWZzICtlbGxwcz1XR1M4NA0KIyMgK3Rvd2dzODQ9MCwwLDANCiMgTnVtYmVyIG9mIGNlbGxzLCByb3dzLCBjb2x1bW5zDQpuY2VsbChiMikNCiMjIFsxXSAxODYzNzY1DQpkaW0oYjIpDQojIyBbMV0gMTI0NSAxNDk3ICAgIDENCiMgc3BhdGlhbCByZXNvbHV0aW9uDQpyZXMoYjIpDQojIyBbMV0gMzAgMzANCiMgTnVtYmVyIG9mIGJhbmRzDQpubGF5ZXJzKGIyKQ0KIyMgWzFdIDENCiMgRG8gdGhlIGJhbmRzIGhhdmUgdGhlIHNhbWUgZXh0ZW50LCBudW1iZXIgb2Ygcm93cyBhbmQgY29sdW1ucywgcHJvamVjdGlvbiwgcmVzb2x1dGlvbiwgYW5kIG9yaWdpbg0KY29tcGFyZVJhc3RlcihiMixiMykNCiMjIFsxXSBUUlVFDQpgYGANCg0KDQojIyMjIyBQdWVkZSBjcmVhciB1biBSYXN0ZXJTdGFjayAodW4gb2JqZXRvIGNvbiB2YXJpYXMgY2FwYXMpIGEgcGFydGlyIGRlIGxvcyBvYmpldG9zIFJhc3RlckxheWVyIChiYW5kYSB1bmljYSkgZXhpc3RlbnRlcyANCg0KDQojIyMjIyBMYXMgYmFuZGFzIHF1ZSBsbGFtb3Mgc29uIGxhcyBtaXNtYXMgcXVlIGhlbW9zIGxsYW1hZG9kbyBhcnJpYmEgY29tbyBSYXN0ZXJMYXllcg0KDQpgYGB7cn0NCnMgPSBzdGFjayhiNSwgYjQsIGIzKQ0KDQojIENoZWNrIHRoZSBwcm9wZXJ0aWVzIG9mIHRoZSBSYXN0ZXJTdGFjaw0Kcw0KIyMgY2xhc3MgICAgICA6IFJhc3RlclN0YWNrDQojIyBkaW1lbnNpb25zIDogMTI0NSwgMTQ5NywgMTg2Mzc2NSwgMyAgKG5yb3csIG5jb2wsIG5jZWxsLCBubGF5ZXJzKQ0KIyMgcmVzb2x1dGlvbiA6IDMwLCAzMCAgKHgsIHkpDQojIyBleHRlbnQgICAgIDogNTk0MDkwLCA2MzkwMDAsIDQxOTAxOTAsIDQyMjc1NDAgICh4bWluLCB4bWF4LCB5bWluLCB5bWF4KQ0KIyMgY3JzICAgICAgICA6ICtwcm9qPXV0bSArem9uZT0xMCArZGF0dW09V0dTODQgK3VuaXRzPW0gK25vX2RlZnMgK2VsbHBzPVdHUzg0ICt0b3dnczg0PTAsMCwwDQojIyBuYW1lcyAgICAgIDogTEMwOF8wNDQwMzRfMjAxNzA2MTRfQjUsIExDMDhfMDQ0MDM0XzIwMTcwNjE0X0I0LCBMQzA4XzA0NDAzNF8yMDE3MDYxNF9CMw0KIyMgbWluIHZhbHVlcyA6ICAgICAgICAgICAgMC4wMDA4NDU3NjY5LCAgICAgICAgICAgIDAuMDIwODQwNjY1MywgICAgICAgICAgICAwLjA0MjU5MjE2NDENCiMjIG1heCB2YWx1ZXMgOiAgICAgICAgICAgICAgIDEuMDEyNDMxNSwgICAgICAgICAgICAgICAwLjc4NjE3NjksICAgIA0KDQpgYGANCg0KDQojIyMjIyBUYW1iaWVuIHB1ZWRlIGNyZWFyIGVsIFJhc3RlclN0YWNrIHVzYW5kbyBsb3Mgbm9tYnJlcyBkZSBhcmNoaXZvIA0KDQoNCmBgYHtyfQ0KDQojIGZpcnN0IGNyZWF0ZSBhIGxpc3Qgb2YgcmFzdGVyIGxheWVycyB0byB1c2UNCmZpbGVuYW1lcyA9IHBhc3RlMCgnIGRhdGEvcnMvTEMwOF8wNDQwMzRfMjAxNzA2MTRfQicsIDE6MTEsICIudGlmIikNCmZpbGVuYW1lcw0KIyMgIFsxXSAiLi9kYXRhL3JzL0xDMDhfMDQ0MDM0XzIwMTcwNjE0X0IxLnRpZiINCiMjICBbMl0gIi4vZGF0YS9ycy9MQzA4XzA0NDAzNF8yMDE3MDYxNF9CMi50aWYiDQojIyAgWzNdICIuL2RhdGEvcnMvTEMwOF8wNDQwMzRfMjAxNzA2MTRfQjMudGlmIg0KIyMgIFs0XSAiLi9kYXRhL3JzL0xDMDhfMDQ0MDM0XzIwMTcwNjE0X0I0LnRpZiINCiMjICBbNV0gIi4vZGF0YS9ycy9MQzA4XzA0NDAzNF8yMDE3MDYxNF9CNS50aWYiDQojIyAgWzZdICIuL2RhdGEvcnMvTEMwOF8wNDQwMzRfMjAxNzA2MTRfQjYudGlmIg0KIyMgIFs3XSAiLi9kYXRhL3JzL0xDMDhfMDQ0MDM0XzIwMTcwNjE0X0I3LnRpZiINCiMjICBbOF0gIi4vZGF0YS9ycy9MQzA4XzA0NDAzNF8yMDE3MDYxNF9COC50aWYiDQojIyAgWzldICIuL2RhdGEvcnMvTEMwOF8wNDQwMzRfMjAxNzA2MTRfQjkudGlmIg0KIyMgWzEwXSAiLi9kYXRhL3JzL0xDMDhfMDQ0MDM0XzIwMTcwNjE0X0IxMC50aWYiDQojIyBbMTFdICIuL2RhdGEvcnMvTEMwOF8wNDQwMzRfMjAxNzA2MTRfQjExLnRpZiINCmxhbmRzYXQgPSBzdGFjayhmaWxlbmFtZXMpDQpsYW5kc2F0DQojIyBjbGFzcyAgICAgIDogUmFzdGVyU3RhY2sNCiMjIGRpbWVuc2lvbnMgOiAxMjQ1LCAxNDk3LCAxODYzNzY1LCAxMSAgKG5yb3csIG5jb2wsIG5jZWxsLCBubGF5ZXJzKQ0KIyMgcmVzb2x1dGlvbiA6IDMwLCAzMCAgKHgsIHkpDQojIyBleHRlbnQgICAgIDogNTk0MDkwLCA2MzkwMDAsIDQxOTAxOTAsIDQyMjc1NDAgICh4bWluLCB4bWF4LCB5bWluLCB5bWF4KQ0KIyMgY3JzICAgICAgICA6ICtwcm9qPXV0bSArem9uZT0xMCArZGF0dW09V0dTODQgK3VuaXRzPW0gK25vX2RlZnMgK2VsbHBzPVdHUzg0ICt0b3dnczg0PTAsMCwwDQojIyBuYW1lcyAgICAgIDogTEMwOF8wNDQwMzRfMjAxNzA2MTRfQjEsIExDMDhfMDQ0MDM0XzIwMTcwNjE0X0IyLCBMQzA4XzA0NDAzNF8yMDE3MDYxNF9CMywgTEMwOF8wNDQwMzRfMjAxNzA2MTRfQjQsIExDMDhfMDQ0MDM0XzIwMTcwNjE0X0I1LCBMQzA4XzA0NDAzNF8yMDE3MDYxNF9CNiwgTEMwOF8wNDQwMzRfMjAxNzA2MTRfQjcsIExDMDhfMDQ0MDM0XzIwMTcwNjE0X0I4LCBMQzA4XzA0NDAzNF8yMDE3MDYxNF9COSwgTEMwOF8wNDQwMzRfMjAxNzA2MTRfQjEwLCBMQzA4XzA0NDAzNF8yMDE3MDYxNF9CMTENCiMjIG1pbiB2YWx1ZXMgOiAgICAgICAgICAgIDkuNjQxNzkxZS0wMiwgICAgICAgICAgICA3LjQ4Mzk5MGUtMDIsICAgICAgICAgICAgNC4yNTkyMTZlLTAyLCAgICAgICAgICAgIDIuMDg0MDY3ZS0wMiwgICAgICAgICAgICA4LjQ1NzY2OWUtMDQsICAgICAgICAgICAtNy44NzIxODNlLTAzLCAgICAgICAgICAgLTUuMDUyOTQ1ZS0wMywgICAgICAgICAgICAzLjkzMTc1MWUtMDIsICAgICAgICAgICAtNC4zMzczMzJlLTA0LCAgICAgICAgICAgICAyLjg5Nzk3OGUrMDIsICAgICAgICAgICAgIDIuODg1MDAwZSswMg0KIyMgbWF4IHZhbHVlcyA6ICAgICAgICAgICAgICAwLjczNDYyODIwLCAgICAgICAgICAgICAgMC43MTc3NTYxNSwgICAgICAgICAgICAgIDAuNjkyNDY5NzIsICAgICAgICAgICAgICAwLjc4NjE3Njg2LCAgICAgICAgICAgICAgMS4wMTI0MzE1MCwgICAgICAgICAgICAgIDEuMDQzMjA0NTUsICAgICAgICAgICAgICAxLjExNzkzNjAyLCAgICAgICAgICAgICAgMC44MjY3MzA0OSwgICAgICAgICAgICAgIDAuMDM1NDc5MDEsICAgICAgICAgICAgIDMyMi40MzEzOTY0OCwgICAgICAgICAgICAgMzE3Ljk5NTMwMDI5DQoNCg0KYGBgDQoNCg0KIyMjIyMgQXJyaWJhIGNyZWFtb3MgdW4gUmFzdGVyU3RhY2sgY29uIDExIGNhcGFzLiBMYXMgY2FwYXMgcmVwcmVzZW50YW4gbGEgaW50ZW5zaWRhZCBkZSBsYSByZWZsZXhpb24gZW4gbGFzIHNpZ3VpZW50ZXMgbG9uZ2l0dWRlcyBkZSBvbmRhOiBVbHRyYSBhenVsLCBhenVsLCB2ZXJkZSwgcm9qbywgaW5mcmFycm9qbyBjZXJjYW5vIChOSVIpLCBpbmZyYXJyb2pvIGRlIG9uZGEgY29ydGEgKFNXSVIpIDEsIGluZnJhcnJvam8gZGUgb25kYSBjb3J0YSAoU1dJUikgMiwgcGFuY3JvbWF0aWNvLCBjaXJybywgaW5mcmFycm9qbyB0ZXJtaWNvIChUSVJTKSAxLCBJbmZyYXJyb2pvIHRlcm1pY28gKFRJUlMpIDIuIE5vIHV0aWxpemFyZW1vcyBsYXMgdWx0aW1hcyBjdWF0cm8gY2FwYXMgeSB2ZXJhIGNvbW8gZWxpbWluYXJsYXMgZW4gbGFzIHNpZ3VpZW50ZXMgc2VjY2lvbmVzLg0KDQoNCg0KIyMjICoqVElUVUxPOiBCQU5EQSBVTklDQSBZIE1BUEFTIENPTVBVRVNUT1MqKg0KDQoNCiMjIyMjIFB1ZWRlIHRyYXphciBjYXBhcyBpbmRpdmlkdWFsZXMgZGUgdW4gUmFzdGVyU3RhY2sgZGUgdW5hIGltYWdlbiBtdWx0aWVzcGVjdHJhbC4NCg0KDQpgYGB7cn0NCg0KcGFyKG1mcm93ID0gYygyLDIpKQ0KcGxvdChiMiwgbWFpbiA9ICJCbHVlIiwgY29sID0gZ3JheSgwOjEwMCAvIDEwMCkpDQpwbG90KGIzLCBtYWluID0gIkdyZWVuIiwgY29sID0gZ3JheSgwOjEwMCAvIDEwMCkpDQpwbG90KGI0LCBtYWluID0gIlJlZCIsIGNvbCA9IGdyYXkoMDoxMDAgLyAxMDApKQ0KcGxvdChiNSwgbWFpbiA9ICJOSVIiLCBjb2wgPSBncmF5KDA6MTAwIC8gMTAwKSkNCg0KYGBgDQoNCiMjIyMjIEVjaGEgdW4gdmlzdGF6byBhIGxhcyBsZXllbmRhcyBkZSBsb3MgbWFwYXMgY3JlYWRvcyBhbnRlcmlvcm1lbnRlLiBQdWVkZW4gdmFyaWFyIGVudHJlIDAgeSAxLiBPYnNlcnZlIGxhIGRpZmVyZW5jaWEgZW4gZWwgc29tYnJlYWRvIHkgZWwgcmFuZ28gZGUgbGV5ZW5kYXMgZW50cmUgbGFzIGRpZmVyZW50ZXMgYmFuZGFzLiBFc3RvIHNlIGRlYmUgYSBxdWUgbGFzIGRpZmVyZW50ZXMgY2FyYWN0ZXJpc3RpY2FzIGRlIGxhIHN1cGVyZmljaWUgcmVmbGVqYW4gbGEgcmFkaWFjaW9uIHNvbGFyIGluY2lkZW50ZSBkZSBtYW5lcmEgZGlmZXJlbnRlLiBDYWRhIGNhcGEgcmVwcmVzZW50YSBsYSBjYW50aWRhZCBkZSByYWRpYWNpb24gc29sYXIgaW5jaWRlbnRlIHF1ZSBzZSByZWZsZWphIHBhcmEgdW4gcmFuZ28gZGUgbG9uZ2l0dWQgZGUgb25kYSBwYXJ0aWN1bGFyLiBQb3IgZWplbXBsbywgbGEgdmVnZXRhY2lvbiByZWZsZWphIG1hcyBlbmVyZ2lhIGVuIE5JUiBxdWUgb3RyYXMgbG9uZ2l0dWRlcyBkZSBvbmRhIHksIHBvciBsbyB0YW50bywgcGFyZWNlIG1hcyBicmlsbGFudGUuIFBvciBlbCBjb250cmFyaW8sIGVsIGFndWEgYWJzb3JiZSBsYSBtYXlvciBwYXJ0ZSBkZSBsYSBlbmVyZ2lhIGVuIGxhIGxvbmdpdHVkIGRlIG9uZGEgTklSIHkgcGFyZWNlIG9zY3VyYS4NCg0KDQojIyMjIyBObyBvYnRlbmVtb3MgdGFudGEgaW5mb3JtYWNpb24gZGUgZXN0YXMgcGFyY2VsYXMgZW4gZXNjYWxhIGRlIGdyaXNlczsgYSBtZW51ZG8gc2UgY29tYmluYW4gZW4gdW4gImNvbXB1ZXN0byIgcGFyYSBjcmVhciB0cmFtYXMgbWFzIGludGVyZXNhbnRlcy4gUHVlZGUgb2J0ZW5lciBtYXMgaW5mb3JtYWNpb24gc29icmUgbG9zIGNvbXB1ZXN0b3MgZGUgY29sb3IgZW4gbGEgdGVsZWRldGVjY2lvbiBhcXVpIHkgdGFtYmllbiBlbiBsYSBzZWNjaW9uIGEgY29udGludWFjaW9uLg0KDQoNCiMjIyMjIFBhcmEgaGFjZXIgdW5hIGltYWdlbiBkZSAiY29sb3IgdmVyZGFkZXJvIChvIG5hdHVyYWwpIiwgZXMgZGVjaXIsIGFsZ28gcXVlIHNlIHBhcmVjZSBhIHVuYSBmb3RvZ3JhZmlhIG5vcm1hbCAodmVnZXRhY2lvbiBlbiB2ZXJkZSwgYXp1bCBhZ3VhLCBldGMuKSwgbmVjZXNpdGFtb3MgYmFuZGFzIGVuIGxhcyByZWdpb25lcyByb2phLCB2ZXJkZSB5IGF6dWwuIFBhcmEgZXN0YSBpbWFnZW4gTGFuZHNhdCwgc2UgcHVlZGVuIHVzYXIgbGFzIGJhbmRhcyA0IChyb2pvKSwgMyAodmVyZGUpIHkgMiAoYXp1bCkuIEVsIC0gcGxvdFJHQiAtIG1ldG9kbyBzZSBwdWVkZSB1dGlsaXphciBwYXJhIGNvbWJpbmFybG9zIGVuIHVuIHNvbG8gY29tcHVlc3RvLiBUYW1iaWVuIHB1ZWRlIHByb3BvcmNpb25hciBhcmd1bWVudG9zIGFkaWNpb25hbGVzIC0gcGxvdFJHQiAtIHBhcmEgbWVqb3JhciBsYSB2aXN1YWxpemFjaW9uIChwb3IgZWplbXBsbywgdW4gZXN0aXJhbWllbnRvIGxpbmVhbCBkZSBsb3MgdmFsb3JlcywgdXRpbGl6YW5kbyApLi0gc3RyZWN0aCA9ICJsaW4iIC0NCg0KDQpgYGB7cn0NCmxhbmRzYXRSR0IgPSBzdGFjayhiNCwgYjMsIGIyKQ0KcGxvdFJHQihsYW5kc2F0UkdCLCBheGVzID0gVFJVRSwgc3RyZXRjaCA9ICJsaW4iLCBtYWluID0gIkxhbmRzYXQgVHJ1ZSBDb2xvciBDb21wb3NpdGUiKQ0KDQpgYGANCg0KDQojIyMjIyBFbCBjb21wdWVzdG8gZGUgY29sb3IgdmVyZGFkZXJvIHJldmVsYSBtdWNobyBtYXMgc29icmUgZWwgcGFpc2FqZSBxdWUgbGFzIGltYWdlbmVzIGdyaXNlcyBhbnRlcmlvcmVzLiBPdHJvIG1ldG9kbyBwb3B1bGFyIGRlIHZpc3VhbGl6YWNpb24gZGUgaW1hZ2VuZXMgZW4gbGEgdGVsZWRldGVjY2lvbiBlcyBsYSBpbWFnZW4gY29ub2NpZGEgZGUgImNvbG9yIGZhbHNvIiBlbiBsYSBxdWUgc2UgY29tYmluYW4gbGFzIGJhbmRhcyBOSVIsIHJvam8geSB2ZXJkZS4gRXN0YSByZXByZXNlbnRhY2lvbiBlcyBwb3B1bGFyIHlhIHF1ZSBoYWNlIHF1ZSBzZWEgZmFjaWwgdmVyIGxhIHZlZ2V0YWNpb24gKGVuIHJvam8pLg0KDQoNCmBgYHtyfQ0KDQpwYXIobWZyb3cgPSBjKDEsMikpDQpwbG90UkdCKGxhbmRzYXRSR0IsIGF4ZXM9VFJVRSwgc3RyZXRjaD0ibGluIiwgbWFpbj0iTGFuZHNhdCBUcnVlIENvbG9yIENvbXBvc2l0ZSIpDQpsYW5kc2F0RkNDID0gc3RhY2soYjUsIGI0LCBiMykNCnBsb3RSR0IobGFuZHNhdEZDQywgYXhlcz1UUlVFLCBzdHJldGNoPSJsaW4iLCBtYWluPSJMYW5kc2F0IEZhbHNlIENvbG9yIENvbXBvc2l0ZSIpDQoNCmBgYA0KDQojIyMjIyAqTm90YSA6IENvbXBydWViZSBzaWVtcHJlIGxhIGRvY3VtZW50YWNpb24gZGVsIHBhcXVldGUgKC0gaGVscChwbG90UkdCKSAtKSBwYXJhIHZlciBvdHJvcyBhcmd1bWVudG9zIHF1ZSBzZSBwdWVkZW4gYWdyZWdhciAoY29tbyBsYSBlc2NhbGEpIHBhcmEgbWVqb3JhciBvIG1vZGlmaWNhciBsYSBpbWFnZW4uKg0KDQojIyMjIyAqUHJlZ3VudGEgMSA6IFVzZSBsYSBmdW5jaW9uIHBsb3RSR0IgY29uIFJhc3RlclN0YWNrIGBgIGxhbmRzYXQgJycgcGFyYSBjcmVhciB1biBjb21wdWVzdG8gZGUgY29sb3IgdmVyZGFkZXJvIHkgZmFsc28gKHJlY3VlcmRlIGxhIHBvc2ljaW9uIGRlIGxhcyBiYW5kYXMgZW4gbGEgcGlsYSkuKg0KDQoNCg0KIyMjICoqVElUVUxPOiBTVUJDT05KVU5UTyBZIFJFTk9NQlJBUiBCQU5EQVMqKg0KDQoNCiMjIyMjIFB1ZWRlIHNlbGVjY2lvbmFyIGNhcGFzIChiYW5kYXMpIGVzcGVjaWZpY2FzIG1lZGlhbnRlIGxhIC0gc3Vic2V0IC0gZnVuY2lvbiBvIG1lZGlhbnRlIGluZGV4YWNpb24uDQoNCg0KYGBge3J9DQoNCiMgc2VsZWN0IGZpcnN0IDMgYmFuZHMgb25seQ0KbGFuZHNhdHN1YjEgPSBzdWJzZXQobGFuZHNhdCwgMTozKQ0KIyBzYW1lDQpsYW5kc2F0c3ViMiA9IGxhbmRzYXRbWzE6M11dDQojIE51bWJlciBvZiBiYW5kcyBpbiB0aGUgb3JpZ2luYWwgYW5kIG5ldyBkYXRhDQpubGF5ZXJzKGxhbmRzYXQpDQojIyBbMV0gMTENCm5sYXllcnMobGFuZHNhdHN1YjEpDQojIyBbMV0gMw0KbmxheWVycyhsYW5kc2F0c3ViMikNCiMjIFsxXSAzDQoNCmBgYA0KDQoNCiMjIyMjIE5vIHVzYXJlbW9zIGxhcyB1bHRpbWFzIGN1YXRybyBiYW5kYXMgLSBsYW5kc2F0IC0uIFB1ZWRlcyBlbGltaW5hciBhcXVlbGxvcyB1c2FuZG86DQoNCg0KYGBge3J9DQoNCmxhbmRzYXQgPSBzdWJzZXQobGFuZHNhdCwgMTo3KQ0KDQpgYGANCg0KDQojIyMjIyBQYXJhIG1heW9yIGNsYXJpZGFkLCBlcyB1dGlsIGVzdGFibGVjZXIgbG9zIG5vbWJyZXMgZGUgbGFzIGJhbmRhcy4NCg0KDQpgYGB7cn0NCg0KbmFtZXMobGFuZHNhdCkNCiMjIFsxXSAiTEMwOF8wNDQwMzRfMjAxNzA2MTRfQjEiICJMQzA4XzA0NDAzNF8yMDE3MDYxNF9CMiINCiMjIFszXSAiTEMwOF8wNDQwMzRfMjAxNzA2MTRfQjMiICJMQzA4XzA0NDAzNF8yMDE3MDYxNF9CNCINCiMjIFs1XSAiTEMwOF8wNDQwMzRfMjAxNzA2MTRfQjUiICJMQzA4XzA0NDAzNF8yMDE3MDYxNF9CNiINCiMjIFs3XSAiTEMwOF8wNDQwMzRfMjAxNzA2MTRfQjciDQpuYW1lcyhsYW5kc2F0KSA9IGMoJ3VsdHJhLWJsdWUnLCAnYmx1ZScsICdncmVlbicsICdyZWQnLCAnTklSJywgJ1NXSVIxJywgJ1NXSVIyJykNCm5hbWVzKGxhbmRzYXQpDQojIyBbMV0gInVsdHJhLmJsdWUiICJibHVlIiAgICAgICAiZ3JlZW4iICAgICAgInJlZCIgICAgICAgICJOSVIiDQojIyBbNl0gIlNXSVIxIiAgICAgICJTV0lSMiINCg0KYGBgDQoNCg0KDQojIyMgKipUSVRVTE86IFNVQkNPTkpVTlRPIEVTUEFDSUFMIE8gUkVDT1JURSoqDQoNCg0KIyMjIyMgRWwgc3ViY29uanVudG8gZXNwYWNpYWwgc2UgcHVlZGUgdXNhciBwYXJhIGxpbWl0YXIgZWwgYW5hbGlzaXMgYSB1biBzdWJjb25qdW50byBnZW9ncmFmaWNvIGRlIGxhIGltYWdlbi4gTG9zIHN1YmNvbmp1bnRvcyBlc3BhY2lhbGVzIHNlIHB1ZWRlbiBjcmVhciBjb24gbGEgLSBjcm9wIC0gZnVuY2lvbiwgdXRpbGl6YW5kbyB1biAtIGV4dGVudCAtIG9iamV0byB1IG90cm8gb2JqZXRvIGVzcGFjaWFsIGRlbCBxdWUgc2UgcHVlZGUgZXh0cmFlciB1bmEgRXh0ZW5zaW9uLg0KDQpgYGB7cn0NCg0KIyBVc2luZyBleHRlbnQNCmV4dGVudChsYW5kc2F0KQ0KIyMgY2xhc3MgICAgICA6IEV4dGVudA0KIyMgeG1pbiAgICAgICA6IDU5NDA5MA0KIyMgeG1heCAgICAgICA6IDYzOTAwMA0KIyMgeW1pbiAgICAgICA6IDQxOTAxOTANCiMjIHltYXggICAgICAgOiA0MjI3NTQwDQplID0gZXh0ZW50KDYyNDM4NywgNjM1NzUyLCA0MjAwMDQ3LCA0MjEwOTM5KQ0KIyBjcm9wIGxhbmRzYXQgYnkgdGhlIGV4dGVudA0KbGFuZHNhdGNyb3AgPSBjcm9wKGxhbmRzYXQsIGUpDQoNCmBgYA0KDQoNCiMjIyMjICpQcmVndW50YSAyIDogVGFtYmllbiBlcyBwb3NpYmxlIGxhIHNlbGVjY2lvbiBpbnRlcmFjdGl2YSBkZSBsYSBpbWFnZW4uIFVzZSBgYCBkcmF3RXh0ZW50YGAgeSBgYCBkcmF3UG9seWBgIHBhcmEgc2VsZWNjaW9uYXIgdW4gYXJlYSBkZSBpbnRlcmVzKg0KDQojIyMjIyAqUHJlZ3VudGEgMyA6IFVzZSBlbCBgYCBMYW5kc2F0Y3JvcCAnJyBkZSBSYXN0ZXJTdGFjayBwYXJhIGNyZWFyIHVuIGNvbXB1ZXN0byBkZSBjb2xvciB2ZXJkYWRlcm8geSBmYWxzbyoNCg0KDQoNCiMjIyAqKlRJVFVMTzogR1VBUkRBTkRPIFJFU1VMVEFET1MgRU4gRUwgRElTQ08qKg0KDQoNCiMjIyMjIEVuIGVzdGEgZXRhcGEsIGVzIHBvc2libGUgcXVlIHF1ZXJhbW9zIGd1YXJkYXIgZWwgcmFzdGVyIGVuIGVsIGRpc2NvIHVzYW5kbyBsYSBmdW5jaW9uIC0gd3JpdGVSYXN0ZXIgLS4gU2UgYWRtaXRlbiBtdWx0aXBsZXMgdGlwb3MgZGUgYXJjaGl2b3MuIFV0aWxpemFyZW1vcyBlbCBmb3JtYXRvIEdlb1RpZmYgZGUgdXNvIGNvbXVuLiBNaWVudHJhcyBzZSBjb25zZXJ2YSBlbCBvcmRlbiBkZSBsYXMgY2FwYXMsIGxvcyBub21icmVzIGRlIGxhcyBjYXBhcyBzZSBwaWVyZGVuIGRlc2Fmb3J0dW5hZGFtZW50ZSBlbiBlbCBmb3JtYXRvIEdlb1RpZmYNCg0KYGBge3J9DQp4ID0gd3JpdGVSYXN0ZXIobGFuZHNhdGNyb3AsIGZpbGVuYW1lPSJjcm9wcGVkLWxhbmRzYXQudGlmIiwgb3ZlcndyaXRlPVRSVUUpDQpgYGANCg0KDQojIyMjIyBBbHRlcm5hdGl2YW1lbnRlLCBwdWVkZSB1dGlsaXphciBlbCBmb3JtYXRvICdyYXN0ZXItZ3JkJy4NCg0KDQpgYGB7cn0NCndyaXRlUmFzdGVyKGxhbmRzYXRjcm9wLCBmaWxlbmFtZT0iY3JvcHBlZC1sYW5kc2F0LmdyZCIsIG92ZXJ3cml0ZT1UUlVFKQ0KYGBgDQoNCg0KIyMjIyMgVW5hIHZlbnRhamEgZGUgZXN0ZSBmb3JtYXRvIGVzIHF1ZSBndWFyZGEgbG9zIG5vbWJyZXMgZGUgbGFzIGNhcGFzLiBMYSBkZXN2ZW50YWphIGRlIGVzdGUgZm9ybWF0byBlcyBxdWUgbm8gbXVjaG9zIG90cm9zIHByb2dyYW1hcyBwdWVkZW4gbGVlciBsb3MgZGF0b3MsIGVuIGNvbnRyYXN0ZSBjb24gZWwgZm9ybWF0byBHZW9UaWZmLg0KDQojIyMjIyAqTm90YTogQ29uc3VsdGUgbGEgZG9jdW1lbnRhY2lvbiBkZWwgcGFxdWV0ZSAoLSBoZWxwKHdyaXRlUmFzdGVyKSAtKSBwYXJhIHZlciBhcmd1bWVudG9zIHV0aWxlcyBhZGljaW9uYWxlcyBxdWUgc2UgcHVlZGVuIGFncmVnYXIqDQoNCg0KDQojIyMgKipUSVRVTE86IFJFTEFDSU9OIEVOVFJFIEJBTkRBUyoqDQoNCg0KIyMjIyMgVW5hIG1hdHJpeiBkZSBkaWFncmFtYSBkZSBkaXNwZXJzaW9uIHB1ZWRlIHNlciB1dGlsIHBhcmEgZXhwbG9yYXIgbGFzIHJlbGFjaW9uZXMgZW50cmUgY2FwYXMgcmFzdGVyLiBFc3RvIHNlIHB1ZWRlIGhhY2VyIGNvbiBsYSBmdW5jaW9uIHBhcmVzICgpIGRlbCBwYXF1ZXRlIHJhc3Rlci4NCg0KDQojIyMjIyBUcmF6YWRvIGRlIHJlZmxlam8gZW4gbGEgbG9uZ2l0dWQgZGUgb25kYSB1bHRyYSBhenVsIGNvbnRyYSBlbCByZWZsZWpvIGVuIGxhIGxvbmdpdHVkIGRlIG9uZGEgYXp1bC4NCg0KDQpgYGB7cn0NCnBhaXJzKGxhbmRzYXRjcm9wW1sxOjJdXSwgbWFpbiA9ICJVbHRyYS1ibHVlIHZlcnN1cyBCbHVlIikNCmBgYA0KDQoNCiMjIyMjIFRyYXphZG8gZGUgcmVmbGVqbyBlbiBsYSBsb25naXR1ZCBkZSBvbmRhIHJvamEgY29udHJhIHJlZmxlam8gZW4gbGEgbG9uZ2l0dWQgZGUgb25kYSBOSVIuDQoNCg0KYGBge3J9DQpwYWlycyhsYW5kc2F0Y3JvcFtbNDo1XV0sIG1haW4gPSAiUmVkIHZlcnN1cyBOSVIiKQ0KYGBgDQoNCg0KIyMjIyMgTGEgcHJpbWVyYSBncmFmaWNhIHJldmVsYSBhbHRhcyBjb3JyZWxhY2lvbmVzIGVudHJlIGxhcyByZWdpb25lcyBkZSBsb25naXR1ZCBkZSBvbmRhIGF6dWwuIERlYmlkbyBhIGxhIGFsdGEgY29ycmVsYWNpb24sIHBvZGVtb3MgdXNhciB1bmEgZGUgbGFzIGJhbmRhcyBhenVsZXMgc2luIHBlcmRlciBtdWNoYSBpbmZvcm1hY2lvbi4NCg0KIyMjIyMgRXN0YSBkaXN0cmlidWNpb24gZGUgcHVudG9zIGVuIGxhIHNlZ3VuZGEgZ3JhZmljYSAoZW50cmUgTklSIHkgcm9qbykgZXMgdW5pY2EgZGViaWRvIGEgc3UgZm9ybWEgdHJpYW5ndWxhci4gTGEgdmVnZXRhY2lvbiBzZSByZWZsZWphIG11eSBiaWVuIGVuIGVsIHJhbmdvIE5JUiBxdWUgZWwgcm9qbyB5IGNyZWEgbGEgZXNxdWluYSBzdXBlcmlvciBjZXJjYSBkZWwgZWplIE5JUiAoeSkuIEVsIGFndWEgYWJzb3JiZSBlbmVyZ2lhIGRlIHRvZGFzIGxhcyBiYW5kYXMgeSBvY3VwYSBlbCBsdWdhciBjZXJjYW5vIGFsIG9yaWdlbi4gRWwgcmluY29uIG1hcyBhbGVqYWRvIHNlIGNyZWEgZGViaWRvIGEgbGFzIGNhcmFjdGVyaXN0aWNhcyBzdXBlcmZpY2lhbGVzIGFsdGFtZW50ZSByZWZsZWN0YW50ZXMsIGNvbW8gZWwgc3VlbG8gYnJpbGxhbnRlIG8gZWwgaG9ybWlnb24uDQoNCg0KDQojIyMgKipUSVRVTE86IEVYVFJBRVIgVkFMT1JFUyBERSBQSVhFTEVTICoqDQoNCg0KIyMjIyMgQSBtZW51ZG8sIHF1ZXJlbW9zIG9idGVuZXIgbG9zIHZhbG9yZXMgZGUgbGFzIGNlbGRhcyByYXN0ZXIgcGFyYSB1YmljYWNpb25lcyBnZW9ncmFmaWNhcyBvIGFyZWFzIGVzcGVjaWZpY2FzLiBMYSAtIGV4dHJhY3QgLSBmdW5jaW9uIHNlIHV0aWxpemEgcGFyYSBvYnRlbmVyIHZhbG9yZXMgcmFzdGVyIGVuIGxhcyB1YmljYWNpb25lcyBkZSBvdHJvcyBkYXRvcyBlc3BhY2lhbGVzLiBQdWVkZSB1c2FyIHB1bnRvcywgbGluZWFzLCBwb2xpZ29ub3MgbyB1biBvYmpldG8gZGUgZXh0ZW5zaW9uIChyZWN0YW5ndWxvKS4gVGFtYmllbiBwdWVkZSB1c2FyIG51bWVyb3MgZGUgY2VsZGEgcGFyYSBleHRyYWVyIHZhbG9yZXMuIEFsIHVzYXIgcHVudG9zLCAtIGV4dHJhY3QgLSBkZXZ1ZWx2ZSBsb3MgdmFsb3JlcyBkZSB1biAtIFJhc3RlciogLSBvYmpldG8gcGFyYSBsYXMgY2VsZGFzIGVuIGxhcyBxdWUgc2UgdWJpY2EgdW4gY29uanVudG8gZGUgcHVudG9zLg0KDQoNCmBgYHtyfQ0KIyBsb2FkIHRoZSBwb2x5Z29ucyB3aXRoIGxhbmQgdXNlIGxhbmQgY292ZXIgaW5mb3JtYXRpb24NCnNhbXAgPSByZWFkUkRTKCdkYXRhL3JzL3NhbXBsZXMucmRzJykNCiMgZ2VuZXJhdGUgMzAwIHBvaW50IHNhbXBsZXMgZnJvbSB0aGUgcG9seWdvbnMNCnB0c2FtcCA9IHNwc2FtcGxlKHNhbXAsIDMwMCwgdHlwZT0ncmVndWxhcicpDQojIGFkZCB0aGUgbGFuZCBjb3ZlciBjbGFzcyB0byB0aGUgcG9pbnRzDQpwdHNhbXAkY2xhc3MgPSBvdmVyKHB0c2FtcCwgc2FtcCkkY2xhc3MNCiMgZXh0cmFjdCB2YWx1ZXMgd2l0aCBwb2ludHMNCmRmID0gZXh0cmFjdChsYW5kc2F0LCBwdHNhbXApDQojIFRvIHNlZSBzb21lIG9mIHRoZSByZWZsZWN0YW5jZSB2YWx1ZXMNCmhlYWQoZGYpDQojIyAgICAgIHVsdHJhLmJsdWUgICAgICBibHVlICAgICAgZ3JlZW4gICAgICAgIHJlZCAgICAgICBOSVIgICAgIFNXSVIxDQojIyBbMSxdICAwLjEzNjc1NDcgMC4xMTk3MDkxIDAuMTA0MjkwMDkgMC4xMDUwNzA4MCAwLjE2NzAyOTAgMC4yMTYxOTIxDQojIyBbMixdICAwLjEzNDMwNDEgMC4xMTYzNjk0IDAuMDk4ODkwMTYgMC4wOTc1MjM5MiAwLjE2ODY5ODggMC4yMDY2NTAxDQojIyBbMyxdICAwLjEzODM4MTIgMC4xMzc1MzU0IDAuMTUzNzc4NTUgMC4yMDk4ODEzNyAwLjM2MDI1NTIgMC4zNTk0NTI4DQojIyBbNCxdICAwLjEyOTM4MTMgMC4xMjU0MTI3IDAuMTM1ODIyMTggMC4xODU0NjI0NSAwLjMwOTQ4NzIgMC4yOTUwNDQwDQojIyBbNSxdICAwLjE0ODExODQgMC4xNTMxNDk2IDAuMTc5ODY3MzQgMC4yNDg5NjAzMyAwLjM4ODI5NTcgMC40MDEwMjU3DQojIyBbNixdICAwLjEzNDI2MDggMC4xMTU4NDkwIDAuMTAwMjk5NzggMC4wOTkzMjM5MCAwLjE2NDk0NzEgMC4yMTA4MzU2DQojIyAgICAgICAgICBTV0lSMg0KIyMgWzEsXSAwLjE4MTczMjQNCiMjIFsyLF0gMC4xNzEwODQzDQojIyBbMyxdIDAuMjE1NzgwMQ0KIyMgWzQsXSAwLjE2NTM1OTENCiMjIFs1LF0gMC4yNDU0MjU0DQojIyBbNixdIDAuMTgwMDQwOA0KYGBgDQoNCg0KDQojIyMgKipUSVRVTE86IFBFUkZJTEVTIEVTUEVDVFJBTEVTKioNCg0KDQojIyMjIyBVbmEgZ3JhZmljYSBkZWwgZXNwZWN0cm8gKHRvZGFzIGxhcyBiYW5kYXMpIHBhcmEgbG9zIHBpeGVsZXMgcXVlIHJlcHJlc2VudGFuIGNpZXJ0YXMgY2FyYWN0ZXJpc3RpY2FzIGRlIGxhIHN1cGVyZmljaWUgdGVycmVzdHJlIChwLiBFai4gQWd1YSkgc2UgY29ub2NlIGNvbW8gcGVyZmlsIGVzcGVjdHJhbC4gRGljaG9zIHBlcmZpbGVzIGRlbXVlc3RyYW4gbGFzIGRpZmVyZW5jaWFzIGVuIGxhcyBwcm9waWVkYWRlcyBlc3BlY3RyYWxlcyBkZSB2YXJpYXMgY2FyYWN0ZXJpc3RpY2FzIGRlIGxhIHN1cGVyZmljaWUgdGVycmVzdHJlIHkgY29uc3RpdHV5ZW4gbGEgYmFzZSBwYXJhIGVsIGFuYWxpc2lzIGRlIGltYWdlbmVzLiBMb3MgdmFsb3JlcyBlc3BlY3RyYWxlcyBzZSBwdWVkZW4gZXh0cmFlciBkZSBjdWFscXVpZXIgY29uanVudG8gZGUgZGF0b3MgbXVsdGllc3BlY3RyYWxlcyB1dGlsaXphbmRvIGxhIC0gZXh0cmFjdCAtIGZ1bmNpb24uIEVuIGVsIGVqZW1wbG8gYW50ZXJpb3IsIGV4dHJhamltb3MgdmFsb3JlcyBkZSBkYXRvcyBkZSBMYW5kc2F0IHBhcmEgbGFzIG11ZXN0cmFzLiBFc3RhcyBtdWVzdHJhcyBpbmNsdXllbjogdGllcnJhcyBkZSBjdWx0aXZvLCBhZ3VhLCBiYXJiZWNobywgY29uc3RydWlkbyB5IGFiaWVydG8uIFByaW1lcm8gY2FsY3VsYW1vcyBsb3MgdmFsb3JlcyBtZWRpb3MgZGUgcmVmbGVjdGFuY2lhIHBhcmEgY2FkYSBjbGFzZSB5IGNhZGEgYmFuZGEuDQoNCg0KYGBge3J9DQptcyA9IGFnZ3JlZ2F0ZShkZiwgbGlzdChwdHNhbXAkY2xhc3MpLCBtZWFuKQ0KIyBpbnN0ZWFkIG9mIHRoZSBmaXJzdCBjb2x1bW4sIHdlIHVzZSByb3cgbmFtZXMNCnJvd25hbWVzKG1zKSA9IG1zWywxXQ0KbXMgPSBtc1ssLTFdDQptcw0KIyMgICAgICAgICAgdWx0cmEuYmx1ZSAgICAgIGJsdWUgICAgICBncmVlbiAgICAgICByZWQgICAgICAgIE5JUiAgICAgIFNXSVIxDQojIyBidWlsdCAgICAgMC4xODY0OTI1IDAuMTc5NTM3MSAwLjE3OTUzMzE3IDAuMTk1ODQxNCAwLjI1NDQ4NDQ3IDAuMjQ4NTAxOTcNCiMjIGNyb3BsYW5kICAwLjExMjk4MTMgMC4wOTA5NjQ1IDAuMDg1OTY3MjIgMC4wNTUwMzQ0IDAuNDgzMzU0NjIgMC4xNjE0MjA4NQ0KIyMgZmFsbG93ICAgIDAuMTMxOTE5OCAwLjExNjQ4NjkgMC4xMDQ1Mzc2NCAwLjExNTEyNDMgMC4xODAxMjk2MiAwLjIzMTM5MjI4DQojIyBvcGVuICAgICAgMC4xMzg4MDE0IDAuMTM3NTIzNSAwLjE1MjczMTYzIDAuMjA2NjQyNSAwLjM0NDc2NjcwIDAuMzU4MjA4NzcNCiMjIHdhdGVyICAgICAwLjEzMzYyNDIgMC4xMTY1NzI4IDAuMDk5MjI3MjYgMC4wNzg1OTQ3IDAuMDQ5MDkyMDEgMC4wMzM2MDA0Nw0KIyMgICAgICAgICAgICAgICBTV0lSMg0KIyMgYnVpbHQgICAgMC4yMDAwMTMwNg0KIyMgY3JvcGxhbmQgMC4wNzMxNDE4Ng0KIyMgZmFsbG93ICAgMC4xOTE0MzAzMA0KIyMgb3BlbiAgICAgMC4yMTM0NjM0Mw0KIyMgd2F0ZXIgICAgMC4wMjcyMzM5OA0KYGBgDQoNCg0KIyMjIyMgQWhvcmEgdHJhemFtb3MgZWwgZXNwZWN0cm8gbWVkaW8gZGUgZXN0YXMgY2FyYWN0ZXJpc3RpY2FzLg0KDQoNCmBgYHtyfQ0KIyBDcmVhdGUgYSB2ZWN0b3Igb2YgY29sb3IgZm9yIHRoZSBsYW5kIGNvdmVyIGNsYXNzZXMgZm9yIHVzZSBpbiBwbG90dGluZw0KbXljb2xvciA9IGMoJ2RhcmtyZWQnLCAneWVsbG93JywgJ2J1cmx5d29vZCcsICdjeWFuJywgJ2JsdWUnKQ0KI3RyYW5zZm9ybSBtcyBmcm9tIGEgZGF0YS5mcmFtZSB0byBhIG1hdHJpeA0KbXMgPSBhcy5tYXRyaXgobXMpDQojIEZpcnN0IGNyZWF0ZSBhbiBlbXB0eSBwbG90DQpwbG90KDAsIHlsaW09YygwLDAuNiksIHhsaW0gPSBjKDEsNyksIHR5cGU9J24nLCB4bGFiPSJCYW5kcyIsIHlsYWIgPSAiUmVmbGVjdGFuY2UiKQ0KIyBhZGQgdGhlIGRpZmZlcmVudCBjbGFzc2VzDQpmb3IgKGkgaW4gMTpucm93KG1zKSl7DQogIGxpbmVzKG1zW2ksXSwgdHlwZSA9ICJsIiwgbHdkID0gMywgbHR5ID0gMSwgY29sID0gbXljb2xvcltpXSkNCn0NCiMgVGl0bGUNCnRpdGxlKG1haW49IlNwZWN0cmFsIFByb2ZpbGUgZnJvbSBMYW5kc2F0IiwgZm9udC5tYWluID0gMikNCiMgTGVnZW5kDQpsZWdlbmQoInRvcGxlZnQiLCByb3duYW1lcyhtcyksDQogICAgICAgY2V4PTAuOCwgY29sPW15Y29sb3IsIGx0eSA9IDEsIGx3ZCA9MywgYnR5ID0gIm4iKQ0KYGBgDQoNCg0KIyMjIyMgRWwgcGVyZmlsIGVzcGVjdHJhbCBtdWVzdHJhIChkZXMpIHNpbWlsaXR1ZCBlbiBsYSByZWZsZWN0YW5jaWEgZGUgZGlmZXJlbnRlcyBjYXJhY3RlcmlzdGljYXMgZW4gbGEgc3VwZXJmaWNpZSBkZSBsYSB0aWVycmEgKG8gcG9yIGVuY2ltYSBkZSBlbGxhKS4gJ0FndWEnIG11ZXN0cmEgdW5hIHJlZmxleGlvbiByZWxhdGl2YW1lbnRlIGJhamEgZW4gdG9kYXMgbGFzIGxvbmdpdHVkZXMgZGUgb25kYSwgeSAnY29uc3RydWlkbycsICdlbiBiYXJiZWNobycgeSAnYWJpZXJ0bycgdGllbmVuIHVuYSByZWZsZWN0YW5jaWEgcmVsYXRpdmFtZW50ZSBhbHRhIGVuIGxhcyBsb25naXR1ZGVzIGRlIG9uZGEgbWFzIGxhcmdhcy4NCg0KDQpBZGQgYSBuZXcgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpJbnNlcnQgQ2h1bmsqIGJ1dHRvbiBvbiB0aGUgdG9vbGJhciBvciBieSBwcmVzc2luZyAqQ3RybCtBbHQrSSouDQoNCldoZW4geW91IHNhdmUgdGhlIG5vdGVib29rLCBhbiBIVE1MIGZpbGUgY29udGFpbmluZyB0aGUgY29kZSBhbmQgb3V0cHV0IHdpbGwgYmUgc2F2ZWQgYWxvbmdzaWRlIGl0IChjbGljayB0aGUgKlByZXZpZXcqIGJ1dHRvbiBvciBwcmVzcyAqQ3RybCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLg0KDQpUaGUgcHJldmlldyBzaG93cyB5b3UgYSByZW5kZXJlZCBIVE1MIGNvcHkgb2YgdGhlIGNvbnRlbnRzIG9mIHRoZSBlZGl0b3IuIENvbnNlcXVlbnRseSwgdW5saWtlICpLbml0KiwgKlByZXZpZXcqIGRvZXMgbm90IHJ1biBhbnkgUiBjb2RlIGNodW5rcy4gSW5zdGVhZCwgdGhlIG91dHB1dCBvZiB0aGUgY2h1bmsgd2hlbiBpdCB3YXMgbGFzdCBydW4gaW4gdGhlIGVkaXRvciBpcyBkaXNwbGF5ZWQuDQo=