2. Practica 2: Exploración de imÔgenes
2.1. Cargando imƔgenes a R
Las imĆ”genes previamente descargadas y descomprimidas en la carpeta /data/rs/, se cargan empleando mĆŗltiples opciones, mediante las librerĆas raster y sp, serĆ”n subidas las primeras 7 bandas de forma individual, las cuales se encuentran identificadas a continuación.
| Azul |
b2 |
| Verde |
b3 |
| Roja |
b4 |
| NIR |
b5 |
| SWIR1 |
b6 |
| SWIR 2 |
b7 |
library(sp)
library (raster)
b2 <- raster('data/rs/LC08_044034_20170614_B2.tif')
b3 <- raster('data/rs/LC08_044034_20170614_B3.tif')
b4 <- raster('data/rs/LC08_044034_20170614_B4.tif')
b5 <- raster('data/rs/LC08_044034_20170614_B5.tif')
b6 <- raster('data/rs/LC08_044034_20170614_B6.tif')
b7 <- raster('data/rs/LC08_044034_20170614_B7.tif')
2.2. Información contenida en las variables
Debido a que las bandas han sido almacenadas como objetos (ya que R emplea un sistema de programación enfocado en objetos), se pueden visualizar la información contenida dentro de estas variables recientemente creadas.
Banda 2
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/Elkin/Documents/R/data/rs/LC08_044034_20170614_B2.tif
names : LC08_044034_20170614_B2
values : 0.0748399, 0.7177562 (min, max)
crs(b2)
CRS arguments:
+proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
ncell(b2)
[1] 1863765
dim(b2)
[1] 1245 1497 1
res(b2)
[1] 30 30
nlayers(b2)
[1] 1
Banda 3
b3
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/Elkin/Documents/R/data/rs/LC08_044034_20170614_B3.tif
names : LC08_044034_20170614_B3
values : 0.04259216, 0.6924697 (min, max)
crs(b3)
CRS arguments:
+proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
ncell(b3)
[1] 1863765
dim(b3)
[1] 1245 1497 1
res(b3)
[1] 30 30
nlayers(b3)
[1] 1
Banda 4
b4
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/Elkin/Documents/R/data/rs/LC08_044034_20170614_B4.tif
names : LC08_044034_20170614_B4
values : 0.02084067, 0.7861769 (min, max)
crs(b4)
CRS arguments:
+proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
ncell(b4)
[1] 1863765
dim(b4)
[1] 1245 1497 1
res(b4)
[1] 30 30
nlayers(b4)
[1] 1
Banda 5
b5
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/Elkin/Documents/R/data/rs/LC08_044034_20170614_B5.tif
names : LC08_044034_20170614_B5
values : 0.0008457669, 1.012432 (min, max)
crs(b5)
CRS arguments:
+proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
ncell(b5)
[1] 1863765
dim(b5)
[1] 1245 1497 1
res(b5)
[1] 30 30
nlayers(b5)
[1] 1
Comparación entre las diferentes bandas, un valor de TRUE permitirÔ la interacción correcta entre ellas posteriormente.
compareRaster(b2,b3,b4,b5)
[1] TRUE
2.3. Otra alternativa para cargar las imƔgenes
Al crear un objeto que contenga el listado de nombres que serƔn cargados, puede emplearse un (stack) donde estƩn almacenadas las bandas requeridas.
filenames <- paste0('data/rs/LC08_044034_20170614_B', 1:11, ".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
2.4. Banda Ćŗnica y mapas compuestos
Mediante una variación del código, donde la función (stretch) que da franjas mĆnimas y mĆ”ximas en forma de cuantiles y estira en este intervalo de salida los valores originales, se logra una mejor resolución de la imagen.
par(mfrow = c(1,2))
plot(b2, main = "Blue", col = gray(0:100 / 100))
plot(stretch(b2, minq=0.5, maxq=0.9), main = "Blue", col = gray(0:100 / 100))

plot(stretch(b3, minq=0.5, maxq=0.9), main = "Green", col = gray(0:100 / 100))

plot(stretch(b4, minq=0.5, maxq=0.9), main = "Red", col = gray(0:100 / 100))

plot(stretch(b5, minq=0.5, maxq=0.9), main = "NIR", col = gray(0:100 / 100))

2.5. Composición de imÔgenes en color verdadero Y falso
Debido a que las imÔgenes proyectadas en blanco y negro de las bandas independientes no arrojan mucha información acerca de la cobertura del suelo, es posible emplear una combinación de bandas que sea procesada por el programa, con el fin de generar una imagen RGB (Red-Green-Blue), lo que permitirÔ en principio emplear las bandas 4, 3 y 2 respectivamente para elaborar una imagen en color verdadero de la superficie del terreno.
landsatRGB <- landsat[[c(4,3,2)]]
plotRGB(landsatRGB, axes =TRUE, stretch = "lin", main = "Landsat True Color Composite")

Sin embargo, reemplazando las bandas anteriormente mencionadas, por otras diferentes, y generando combinaciones adecuadas, es posible identificar información que en color verdadero no son fÔcilmente clasificables, es allà donde las imÔgenes en color falso, donde las bandas no corresponden con el sistema RGB, presentan una gran utilidad.
Debido a que la banda NIR presenta una gran reflectancia de la vegetación, esta es representada por coloraciones rojas intensas.
landsatFCC_veg <- landsat[[c(5,4,3)]]
plotRGB(landsatFCC_veg, axes =TRUE, stretch = "lin", main = "Landsat False Color Composite-Vegetation")

Una opción adicional es clasificar las zonas que tienen reflectancias infrarrojas de onda corta mayores, que por lo general son Ôreas urbanas, con coberturas de concreto.
landsatFCC_urb <- landsat[[c(7,6,4)]]
plotRGB(landsatFCC_urb, axes =TRUE, stretch = "lin", main = "Landsat False Color Composite-Urban Zones")

De igual forma, la vegetación y las zonas en preparación y explotación agrĆcola pueden resultar identificadas usando las bandas SWIR1, NIR y azul.
landsatFCC_agr <- landsat[[c(6,5,2)]]
plotRGB(landsatFCC_agr, axes =TRUE, stretch = "lin", main = "Landsat False Color Composite-Agriculture")

landsatFCC_veghealt <- landsat[[c(5,6,2)]]
plotRGB(landsatFCC_veghealt, axes =TRUE, stretch = "lin", main = "Landsat False Color Composite-
Healthy Veg.")

2.6. Subconjuntos de archivos
La función (subset) y su variación landsat[[c(x1,x2,x3)]] o landsat[[x1:x3]] ya fue empleada anteriormente para graficar los mapas landsatRGB, landsatFCC y se verÔ posteriormente en los respectivos recortes (landsatcrop), cómoda y practica ya que el orden de enlace puede ser ascendente o descendente teniendo almacenado un solo bloque con todas las variables y no estas independientes y por aparte.
landsatsub1 <- subset(landsat, 1:3)
landsatsub2 <- landsat[[1:3]]
nlayers(landsat)
[1] 11
nlayers(landsatsub2)
[1] 3
Tambien puede ser empleada para sobreescribir un objeto y reemplazar las bandas almacenadas en un bloque, eliminando aquellas que no nos interesan.
landsat <- subset(landsat, 1:7)
names(landsat)
[1] "LC08_044034_20170614_B1" "LC08_044034_20170614_B2" "LC08_044034_20170614_B3" "LC08_044034_20170614_B4" "LC08_044034_20170614_B5"
[6] "LC08_044034_20170614_B6" "LC08_044034_20170614_B7"
Renombrandolas, permitirÔ una facil identificación de estas.
names(landsat) <- c('ultra-blue','blue','green','red','NIR','SWIR1','SWIR2')
names(landsat)
[1] "ultra.blue" "blue" "green" "red" "NIR" "SWIR1" "SWIR2"
2.7. Recortes espaciales de imƔgenes
El caso mĆ”s comĆŗn es obtener una imagen satelital de gran tamaƱo, que abarca el Ć”rea de interĆ©s y mucho mĆ”s, por lo que se requiere delimitar Ćŗnicamente la zona donde se desean conocer las caracterĆsticas, evitando asĆ ralentizar los procesos.
(extent) requiere dimensionar previamente el Ôrea de estudio, y empleando coordenadas cartesianas puede ser delimitado un rectÔngulo (Ymin, Ymax, Xmin y Xmax), posteriormente la función (crop) permitirÔ almacenar en un nuevo objeto el recorte realizado entre el raster y el recuadro recientemente creado.
extent(landsat)
class : Extent
xmin : 594090
xmax : 639000
ymin : 4190190
ymax : 4227540
e <-extent(624384, 635752, 4200047, 4210939)
landsatcrop <- crop(landsat,e)
extent(landsatcrop)
class : Extent
xmin : 624390
xmax : 635760
ymin : 4200060
ymax : 4210950
2.8. Composición de Colores Verdadero y Falso para las ImÔgenes Recortadas
A continuación se presentan procesos de ploteo con el detalle de las imÔgenes recortadas.
landsatRGBcrop <- landsatcrop[[c(4,3,2)]]
plotRGB(landsatRGBcrop, axes =TRUE, stretch = "lin", main = "Landsat True Color Composite-croped")

landsatFCCcrop_veg <- landsatcrop[[c(5,4,3)]]
plotRGB(landsatFCCcrop_veg, axes =TRUE, stretch = "lin", main = "Landsat False Color Composite-croped-vegetation")

2.9. Almacenamiento de Datos en el Disco
Los diferentes archivos en los que se puede almacenar la información procesada, tienen caracterĆsticas que deben ser claras.
| Los archivos GeoTiff son de uso comĆŗn, conservan el orden de almacenamiento de las capas del raster, pero sus nombres se pierden en este tipo de formato |
Los archivos de esta caracterĆstica, permiten almacenar el nombre de las capas, sin embargo, no muchos programas logran leerlos. |
x1 <- writeRaster(landsatcrop, filename="data/rs/cropped-landsat.tif", overwrite=TRUE)
x2 <- writeRaster(landsatcrop, filename="data/rs/cropped-landsat.grd", overwrite=TRUE)
2.10. Relación entre bandas
Encontrar la proporcionalidad de la relación entre algunas bandas permite identificar cuales pueden ser usadas sin perder información, como sucede con la banda 1 y 2 de Landsat 8, mediante la función (pairs), la comparación es llevada a cabo, asà mismo, puede darse el caso de las bandas 4 y 5, que generan una nube de puntos cercana al eje Y, donde la reflectancia vegetal es muy marcada, mostrando una baja correlación.
pairs(landsatcrop[[1:2]], main = "Ultra-Blue Vs Blue")

pairs(landsatcrop[[4:5]], main = "Red Vs NIR")

2.11. Valores de los Pixeles
Teniendo un archivo de polĆgonos que identifiquen fĆ”cilmente las diferentes Ć”reas de explotación agrĆcola, forestal, urbana entre otras, se pueden extraer coordenadas o puntos mediante la función (spsample), escogiendo la cantidad de puntos y la forma de extracción de estos.
#Esta primera linea, carga el archivo RDS que contiene los polĆgonos de clasificación de suelo.
lc_samples <- readRDS('data/rs/samples.rds')
#spsample, genera para este caso, 300 puntos de forma regular sobre los polĆgonos almacenados en el objeto lc_samples
ptsamp <- spsample(lc_samples, 300, type = 'regular')
#Se agreaga la clase de cobertura de suelo a los puntos extraidos.
ptsamp$class <- over(ptsamp, lc_samples)$class
#Extraer los valores de radiancia en el raster "landsat"de los puntos
df <- extract(landsat, ptsamp)
head(df)
ultra.blue blue green red NIR SWIR1 SWIR2
[1,] 0.1412655 0.1346294 0.1439546 0.1778288 0.3226725 0.3632045 0.2504567
[2,] 0.1400077 0.1235477 0.1126394 0.1163911 0.1883034 0.2332810 0.1958719
[3,] 0.1360607 0.1181911 0.1034443 0.1039431 0.1727325 0.2174066 0.1815155
[4,] 0.1382511 0.1212055 0.1052660 0.1049841 0.1787830 0.2135247 0.1745975
[5,] 0.1370583 0.1364945 0.1499184 0.2007297 0.3393711 0.3296990 0.1946141
[6,] 0.1383378 0.1363426 0.1537352 0.2059128 0.3326266 0.3665659 0.2251053
2.12. Perfiles espectrales
Una de las herramientas mas importantes es el perfil espectral de las bandas contenidas en un objeto, elaborada con los pixeles extraĆdos anteriormente, donde estĆ”n representadas algunas caracterĆsticas de la superficie.
ms <- aggregate(df, list(ptsamp$class), mean)
rownames(ms) <- ms[,1]
#La primera columna, tendrĆ” los nombres de las clases
ms <- ms[,-1]
ms
mycolor <- c('darkred','yellow','burlywood','cyan','blue')
ms <- as.matrix(ms)
plot(0, ylim=c(0,0.6), xlim=c(1,7),type='n',xlab="Bands",ylab="Reflectance")
for (i in 1:nrow(ms)){
lines(ms[i,],type="l", lwd=3,lty=1,col=mycolor[i])
}
title(main="Spectral Profile from Landsat", font.main=2)
legend("topleft",rownames(ms),
cex=0.8, col=mycolor,lty=1,lwd=3,bty="n")

Practica 3: Operaciones matemƔticas bƔsicas
Ćndices de vegetación
vi <- function(img,k,i){
bk <- img[[k]]
bi <- img[[i]]
vi <- (bk-bi)/(bk+bi)
return(vi)
}
ndvi <- vi(landsat, 5, 4)
plot(ndvi, col = rev(terrain.colors(10)), main="Landsat-NDVI")

Otra alternativa:
vi2 <- function(x,y){
(x-y)/(x+y)
}
ndvi2 <- overlay(landsat[[5]], landsat[[4]], fun=vi2)
plot(ndvi2, col=rev(terrain.colors(10)), main="Landsat-NDVI")

Histograma de NDVI
hist(ndvi,
main = "Distribution of NDVI values",
xlab = "NDVI",
ylab= "Frequency",
col = "green",
xlim = c(-0.5, 1),
breaks = 30,
xaxt = 'n')
axis(side=1, at = seq(-0.5,1, 0.05), labels = seq(-0.5,1, 0.05))

Ćndice de Agua de Diferencia Normalizada
wi <- function(img,k,i){
bk <- img[[k]]
bi <- img[[i]]
wi <- (bk-bi)/(bk+bi)
return(vi)
}
ndwi <- vi(landsat, 3, 5)
plot(ndwi, col = rev(topo.colors(10)), main="NDWI-Mc Feeters (2006)")

Histograma de NDWI - Mc Feeters (2006)
hist(ndwi,
main = "Distribution of NDWI values",
xlab = "NDWI",
ylab= "Frequency",
col = "wheat",
xlim = c(-0.5, 1),
breaks = 30,
xaxt = 'n')
axis(side=1, at = seq(-0.5,1, 0.05), labels = seq(-0.5,1, 0.05))

ndwifunc <- function(img, k, i){
bcrk <- img[[k]]
bcri <- img[[i]]
ndwifunc <- (bcrk - bcri) / (bcrk + bcri)
return(ndwifunc)
}
mndwi <- ndwifunc(landsat, 3, 6)
plot(mndwi, col = rev(topo.colors(10)), main = "M-NDWI - Xu (2006)")

Histograma de MNDWI - Xu (2006)
hist(mndwi,
main = "Distribution of M-NDWI values",
xlab = "MNDWI",
ylab= "Frequency",
col = "yellow",
xlim = c(-0.5, 1),
breaks = 30,
xaxt = 'n')
axis(side=1, at = seq(-0.5,1, 0.05), labels = seq(-0.5,1, 0.05))

Ćndice de Diferencias Normalizadas de Ćrea Construida
(Empleando la imagen recortada)
ndbifunc <- function(img, k, i){
bcrk <- img[[k]]
bcri <- img[[i]]
ndbifunc <- (bcrk - bcri) / (bcri + bcrk)
return(ndbifunc)
}
ndbi <- ndbifunc(landsatcrop, 6, 5)
plot(ndbi, col = rev(topo.colors(10)), main = "NDBI")

Histograma de NDBI
hist(ndbi,
main = "Distribution of NDBI values",
xlab = "NDBI",
ylab= "Frequency",
col = "blue",
xlim = c(-0.5, 1),
breaks = 30,
xaxt = 'n')
axis(side=1, at = seq(-0.5,1, 0.05), labels = seq(-0.5,1, 0.05))

Umbralización
veg <- reclassify(ndvi, cbind(-Inf,0.4,NA))
plot(veg, main='Vegetation')

land <- reclassify(ndvi, c(-Inf, 0.25, NA, 0.25, 0.3, 1, 0.3, Inf, NA))
plot(land, main = 'Open Areas')

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

vegc <- reclassify(ndvi, 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 = 'NDVI based thresholding')

La respuesta del perfil espectral, muestra que para las 7 bandas, la reflectancia del agua es la unica que se encuentra entre 0 y 0.2 como valores mÔximos aproximados, asà que acotamos el subconjunto de reclasificación en esta franja.
water <- reclassify(ndwi, cbind(-Inf, 0.1, NA))
#Solo es necesario acotar el limite inferior, ya que todo sobre este valor (0.1) se muestra con clasificación de interés
colors <- colorRampPalette(c("wheat","cyan","blue"))
plot(water,col = colors(10), main = 'water')

Y para el caso de Ɣreas construidas, el nivel de reflectancia muestra el mismo comportamiento sobre los valores de 0.1 o 0.15 hasta 0.2
urbanreclass <- reclassify(ndbi, cbind(-Inf, 0.0, NA))
colors <- colorRampPalette(c("wheat","grey","black"))
plot(urbanreclass,col = colors(50), main = 'Urban zones')

Bajando el lĆmite superior hasta 0, se logran evidenciar incluso, lo que parecen ser vias que rodean los cuerpos de agua.
AnƔlisis de componentes Principales
set.seed(1)
sr <- sampleRandom(landsat, 10000)
plot(sr[,c(4,5)], main = "NIR-Red plot")

pca <- prcomp(sr, scale = TRUE)
pca
Standard deviations (1, .., p=7):
[1] 2.20737373 1.17428246 0.73633573 0.42388639 0.12500714 0.09389890 0.04741454
Rotation (n x k) = (7 x 7):
PC1 PC2 PC3 PC4 PC5 PC6 PC7
ultra.blue 0.3662344 -0.4524940727 -0.15415991 0.52595682 -0.21276947 0.1337062 0.54550658
blue 0.4125282 -0.3336052918 -0.15782033 0.09509849 -0.21537031 0.0836529 -0.79447772
green 0.4372284 -0.1056685710 -0.26457625 -0.20443752 0.49716419 -0.6553920 0.09570391
red 0.4333279 -0.0003693985 -0.05074204 -0.67055674 0.01651199 0.5619375 0.20966468
NIR 0.1762472 0.6825385571 -0.58744574 0.32192859 0.09371265 0.2090330 -0.04325163
SWIR1 0.3815340 0.4119308383 0.29523433 -0.08811728 -0.65630314 -0.3878540 0.09301774
SWIR2 0.3743258 0.1929889704 0.66820379 0.33388919 0.47051465 0.1889099 -0.08709876
screeplot(pca)

pci <- predict(landsat, pca, index = 1:2)
plot(pci[[1]])

par(mfrow=c(1,2))
pc2 <- reclassify(pci[[2]], c(-Inf,0,1,0,Inf,NA))
plotRGB(landsatFCC_veg, r = 1, g = 2, b = 3, axes = TRUE, stretch = "lin", main = "Landsat False Color Composite")
plotRGB(landsatFCC_veg, r = 1, g = 2, b = 3, axes = TRUE, stretch = "lin", main = "Landsat False Color Composite")
plot(pc2, legend = FALSE, add = TRUE)

LS0tDQp0aXRsZTogJ1ByYWN0aWNhcyAxIGEgMyAtIFBlcmNlcGNpw7NuIFJlbW90YSAgJw0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBodG1sX2RvY3VtZW50Og0KICAgICAgZGZfcHJpbnQ6IHBhZ2VkDQotLS0NCg0KYGBge3J9DQpsaWJyYXJ5KGtuaXRyKQ0KYGBgDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCg0KIyAqKkNvbnRlbmlkbyoqDQoNCjEuIFByYWN0aWNhIDE6IERlc2NhcmdhIGRlIGRhdG9zIGLDoXNpY29zIHBhcmEgYW7DoWxpc2lzDQoyLiBQcmFjdGljYSAyOiBFeHBsb3JhY2nDs24gZGUgaW3DoWdlbmVzICANCjIuMS4gQ2FyZ2FuZG8gaW3DoWdlbmVzIGEgUiAgDQoyLjIuIEluZm9ybWFjacOzbiBjb250ZW5pZGEgZW4gbGFzIHZhcmlhYmxlcyAgDQoyLjMuIE90cmEgYWx0ZXJuYXRpdmEgcGFyYSBjYXJnYXIgaW3DoWdlbmVzICANCjIuNC4gQmFuZGEgw7puaWNhIHkgbWFwYXMgY29tcHVlc3RvcyAgDQoyLjUuIENvbXBvc2ljacOzbiBkZSBpbcOhZ2VuZXMgZW4gY29sb3IgdmVyZGFkZXJvIHkgZmFsc28gIA0KMi42LiBTdWJjb25qdW50b3MgZGUgYXJjaGl2b3MgIA0KMi43LiBSZWNvcnRlcyBlc3BhY2lhbGVzIGRlIGltw6FnZW5lcyAgICANCjIuOC4gQ29tcG9zaWNpw7NuIGRlIGNvbG9yIHZlcmRhZGVybyB5IGZhbHNvIHBhcmEgaW3DoWdlbmVzIHJlY29ydGFkYXMgICANCjIuOS4gQWxtYWNlbmFtaWVudG8gZGUgZGF0b3MgZW4gZGlzY28gICANCjIuMTAuIFJlbGFjacOzbiBlbnRyZSBiYW5kYXMgIA0KMi4xMS4gVmFsb3JlcyBkZSBwaXhlbGVzICANCjIuMTIuIFBlcmZpbGVzIGVzcGVjdHJhbGVzICANCjMuIFByYWN0aWNhIDM6IE9wZXJhY2lvbmVzIG1hdGVtw6F0aWNhcyBiw6FzaWNhcyAgICAgDQozLjEuIMONbmRpY2UgZGUgdmVnZXRhY2nDs24gKk5EVkkqICANCjMuMS4xLiBIaXN0b2dyYW1hIGRlICpORFZJKiAgDQozLjIuIMONbmRpY2UgZGUgYWd1YSBkZSBkaWZlcmVuY2lhIG5vcm1hbGl6YWRhICpORFdJKiAgDQozLjIuMS4gTWMgRmVldGVycyAoMjAwNikgIA0KMy4yLjIuIEhpc3RvZ3JhbWEgZGUgKk5EV0kqIOKAkyBNYyBGZWV0ZXJzICgyMDA2KSAgDQozLjIuMy4gKk0tTkRXSSogWHUgKDIwMDYpICANCjMuMi40LiBIaXN0b2dyYW1hIGRlICpNTkRXSSog4oCTIFh1ICgyMDA2KSAgDQozLjMuIMONbmRpY2UgZGUgZGlmZXJlbmNpYSBub3JtYWxpemFkYSBkZSDDoXJlYSBjb25zdHJ1aWRhICpOREJJKiAgDQozLjMuMS4gSGlzdG9ncmFtYSBkZSAqTkRCSSogIA0KMy40LiBVbWJyYWxpemFjacOzbiAgIA0KMy41LiBBbsOhbGlzaXMgZGUgY29tcG9uZW50ZXMgcHJpbmNpcGFsZXMgIA0KDQoNCiAgDQojICoqMS4gUHJhY3RpY2EgMTogRGVzY2FyZ2EgZGUgZGF0b3MgYsOhc2ljb3MgcGFyYSBhbsOhbGlzaXMqKiAgDQogIA0KTG9zIGRhdG9zIHB1ZWRlbiBzZXIgZGVzY2FyZ2Fkb3MsIHkgZ3VhcmRhZG9zIGRpcmVjdGFtZW50ZSBlbiBsYSBkaXJlY2Npw7NuIGRlIHByZWZlcmVuY2lhIHBhcmEgc3UgcG9zdGVyaW9yIGFuw6FsaXNpcywgbWVkaWFudGUgbGEgY3JlYWNpw7NuIGRlIHVuIGRpcmVjdG9yaW8gKihkaXIuY3JlYXRlKSogeSBwb3N0ZXJpb3JtZW50ZSBkZXNjb21wcmltaWRvcyAqKHVuemlwKSouICANCg0KYGBge3J9DQpkaXIuY3JlYXRlKCdkYXRhJywgc2hvd1dhcm5pbmdzID0gRkFMU0UpDQppZiAoIWZpbGUuZXhpc3RzKCdkYXRhL3JzL3NhbXBsZXMucmRzJykpIHsNCiAgICBkb3dubG9hZC5maWxlKCdodHRwczovL2Jpb2dlby51Y2RhdmlzLmVkdS9kYXRhL3JzcGF0aWFsL3JzZGF0YS56aXAnLCBkZXN0ID0gJ2RhdGEvcnNkYXRhLnppcCcpDQogICAgdW56aXAoJ2RhdGEvcnNkYXRhLnppcCcsIGV4ZGlyPSdkYXRhJykNCn0NCmBgYA0KDQojICoqMi4gUHJhY3RpY2EgMjogRXhwbG9yYWNpw7NuIGRlIGltw6FnZW5lcyoqDQoNCiMjICoqMi4xLiBDYXJnYW5kbyBpbcOhZ2VuZXMgYSBSKioNCg0KTGFzIGltw6FnZW5lcyBwcmV2aWFtZW50ZSBkZXNjYXJnYWRhcyB5IGRlc2NvbXByaW1pZGFzIGVuIGxhIGNhcnBldGEgKi9kYXRhL3JzLyosIHNlIGNhcmdhbiBlbXBsZWFuZG8gbcO6bHRpcGxlcyBvcGNpb25lcywgbWVkaWFudGUgbGFzIGxpYnJlcsOtYXMgKnJhc3RlciogeSAqc3AqLCBzZXLDoW4gc3ViaWRhcyBsYXMgcHJpbWVyYXMgNyBiYW5kYXMgZGUgZm9ybWEgaW5kaXZpZHVhbCwgbGFzIGN1YWxlcyBzZSBlbmN1ZW50cmFuIGlkZW50aWZpY2FkYXMgYSBjb250aW51YWNpw7NuLiAgDQogIA0KfEJhbmRhIExhbmRzYXQgOCB8IENvZGlmaWNhY2nDs24gfA0KfDotLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLTp8DQp8IEF6dWwgfCBiMiB8DQp8IFZlcmRlIHwgYjMgfA0KfCBSb2phIHwgYjQgfA0KfCBOSVIgfCBiNSB8DQp8IFNXSVIxIHwgYjYgfA0KfCBTV0lSIDIgfCBiNyB8DQoNCg0KYGBge3J9DQpsaWJyYXJ5KHNwKQ0KbGlicmFyeSAocmFzdGVyKQ0KDQpiMiA8LSByYXN0ZXIoJ2RhdGEvcnMvTEMwOF8wNDQwMzRfMjAxNzA2MTRfQjIudGlmJykNCmIzIDwtIHJhc3RlcignZGF0YS9ycy9MQzA4XzA0NDAzNF8yMDE3MDYxNF9CMy50aWYnKQ0KYjQgPC0gcmFzdGVyKCdkYXRhL3JzL0xDMDhfMDQ0MDM0XzIwMTcwNjE0X0I0LnRpZicpDQpiNSA8LSByYXN0ZXIoJ2RhdGEvcnMvTEMwOF8wNDQwMzRfMjAxNzA2MTRfQjUudGlmJykNCmI2IDwtIHJhc3RlcignZGF0YS9ycy9MQzA4XzA0NDAzNF8yMDE3MDYxNF9CNi50aWYnKQ0KYjcgPC0gcmFzdGVyKCdkYXRhL3JzL0xDMDhfMDQ0MDM0XzIwMTcwNjE0X0I3LnRpZicpDQpgYGANCg0KIyMgKioyLjIuIEluZm9ybWFjacOzbiBjb250ZW5pZGEgZW4gbGFzIHZhcmlhYmxlcyoqDQoNCkRlYmlkbyBhIHF1ZSBsYXMgYmFuZGFzIGhhbiBzaWRvIGFsbWFjZW5hZGFzIGNvbW8gb2JqZXRvcyAoeWEgcXVlIFIgZW1wbGVhIHVuIHNpc3RlbWEgZGUgcHJvZ3JhbWFjacOzbiBlbmZvY2FkbyBlbiBvYmpldG9zKSwgc2UgcHVlZGVuIHZpc3VhbGl6YXIgbGEgaW5mb3JtYWNpw7NuIGNvbnRlbmlkYSBkZW50cm8gZGUgZXN0YXMgdmFyaWFibGVzIHJlY2llbnRlbWVudGUgY3JlYWRhcy4gIA0KICANCiMjIyAqKkJhbmRhIDIqKg0KDQpgYGB7cn0NCmIyDQpjcnMoYjIpDQpuY2VsbChiMikNCmRpbShiMikNCnJlcyhiMikNCm5sYXllcnMoYjIpDQpgYGANCg0KIyMjICoqQmFuZGEgMyoqDQoNCmBgYHtyfQ0KYjMNCmNycyhiMykNCm5jZWxsKGIzKQ0KZGltKGIzKQ0KcmVzKGIzKQ0KbmxheWVycyhiMykNCmBgYA0KDQojIyMgKipCYW5kYSA0KioNCg0KYGBge3J9DQpiNA0KY3JzKGI0KQ0KbmNlbGwoYjQpDQpkaW0oYjQpDQpyZXMoYjQpDQpubGF5ZXJzKGI0KQ0KYGBgDQoNCg0KIyMjICoqQmFuZGEgNSoqDQoNCmBgYHtyfQ0KYjUNCmNycyhiNSkNCm5jZWxsKGI1KQ0KZGltKGI1KQ0KcmVzKGI1KQ0KbmxheWVycyhiNSkNCmBgYA0KDQpDb21wYXJhY2nDs24gZW50cmUgbGFzIGRpZmVyZW50ZXMgYmFuZGFzLCB1biB2YWxvciBkZSAqVFJVRSogcGVybWl0aXLDoSBsYSBpbnRlcmFjY2nDs24gY29ycmVjdGEgZW50cmUgZWxsYXMgcG9zdGVyaW9ybWVudGUuDQpgYGB7cn0NCmNvbXBhcmVSYXN0ZXIoYjIsYjMsYjQsYjUpDQpgYGANCg0KIyMgKioyLjMuIE90cmEgYWx0ZXJuYXRpdmEgcGFyYSBjYXJnYXIgbGFzIGltw6FnZW5lcyoqDQogIA0KQWwgY3JlYXIgdW4gb2JqZXRvIHF1ZSBjb250ZW5nYSBlbCBsaXN0YWRvIGRlIG5vbWJyZXMgcXVlIHNlcsOhbiBjYXJnYWRvcywgcHVlZGUgZW1wbGVhcnNlIHVuICooc3RhY2spKiBkb25kZSBlc3TDqW4gYWxtYWNlbmFkYXMgbGFzIGJhbmRhcyByZXF1ZXJpZGFzLiAgDQogIA0KDQpgYGB7cn0NCmZpbGVuYW1lcyA8LSBwYXN0ZTAoJ2RhdGEvcnMvTEMwOF8wNDQwMzRfMjAxNzA2MTRfQicsIDE6MTEsICIudGlmIikNCmxhbmRzYXQgPC0gc3RhY2soZmlsZW5hbWVzKQ0KbGFuZHNhdA0KYGBgDQoNCg0KIyMgKioyLjQuIEJhbmRhIMO6bmljYSB5IG1hcGFzIGNvbXB1ZXN0b3MqKg0KICANCk1lZGlhbnRlIHVuYSB2YXJpYWNpw7NuIGRlbCBjw7NkaWdvLCBkb25kZSBsYSBmdW5jacOzbiAqKHN0cmV0Y2gpKiBxdWUgZGEgZnJhbmphcyBtw61uaW1hcyB5IG3DoXhpbWFzIGVuIGZvcm1hIGRlIGN1YW50aWxlcyB5IGVzdGlyYSBlbiBlc3RlIGludGVydmFsbyBkZSBzYWxpZGEgbG9zIHZhbG9yZXMgb3JpZ2luYWxlcywgc2UgbG9ncmEgdW5hIG1lam9yIHJlc29sdWNpw7NuIGRlIGxhIGltYWdlbi4gIA0KDQpgYGB7cn0NCnBhcihtZnJvdyA9IGMoMSwyKSkNCnBsb3QoYjIsIG1haW4gPSAiQmx1ZSIsIGNvbCA9IGdyYXkoMDoxMDAgLyAxMDApKQ0KcGxvdChzdHJldGNoKGIyLCBtaW5xPTAuNSwgbWF4cT0wLjkpLCBtYWluID0gIkJsdWUiLCBjb2wgPSBncmF5KDA6MTAwIC8gMTAwKSkNCmBgYA0KDQpgYGB7cn0NCnBsb3Qoc3RyZXRjaChiMywgbWlucT0wLjUsIG1heHE9MC45KSwgbWFpbiA9ICJHcmVlbiIsIGNvbCA9IGdyYXkoMDoxMDAgLyAxMDApKQ0KcGxvdChzdHJldGNoKGI0LCBtaW5xPTAuNSwgbWF4cT0wLjkpLCBtYWluID0gIlJlZCIsIGNvbCA9IGdyYXkoMDoxMDAgLyAxMDApKQ0KcGxvdChzdHJldGNoKGI1LCBtaW5xPTAuNSwgbWF4cT0wLjkpLCBtYWluID0gIk5JUiIsIGNvbCA9IGdyYXkoMDoxMDAgLyAxMDApKQ0KYGBgDQoNCg0KIyMgKioyLjUuIENvbXBvc2ljacOzbiBkZSBpbcOhZ2VuZXMgZW4gY29sb3IgdmVyZGFkZXJvIFkgZmFsc28qKg0KICANCkRlYmlkbyBhIHF1ZSBsYXMgaW3DoWdlbmVzIHByb3llY3RhZGFzIGVuIGJsYW5jbyB5IG5lZ3JvIGRlIGxhcyBiYW5kYXMgaW5kZXBlbmRpZW50ZXMgbm8gYXJyb2phbiBtdWNoYSBpbmZvcm1hY2nDs24gYWNlcmNhIGRlIGxhIGNvYmVydHVyYSBkZWwgc3VlbG8sIGVzIHBvc2libGUgZW1wbGVhciB1bmEgY29tYmluYWNpw7NuIGRlIGJhbmRhcyBxdWUgc2VhIHByb2Nlc2FkYSBwb3IgZWwgcHJvZ3JhbWEsIGNvbiBlbCBmaW4gZGUgZ2VuZXJhciB1bmEgaW1hZ2VuIFJHQiAqKFJlZC1HcmVlbi1CbHVlKSosIGxvIHF1ZSBwZXJtaXRpcsOhIGVuIHByaW5jaXBpbyBlbXBsZWFyIGxhcyBiYW5kYXMgNCwgMyB5IDIgcmVzcGVjdGl2YW1lbnRlIHBhcmEgZWxhYm9yYXIgdW5hIGltYWdlbiBlbiBjb2xvciB2ZXJkYWRlcm8gZGUgbGEgc3VwZXJmaWNpZSBkZWwgdGVycmVuby4gIA0KDQpgYGB7cn0NCmxhbmRzYXRSR0IgPC0gbGFuZHNhdFtbYyg0LDMsMildXQ0KcGxvdFJHQihsYW5kc2F0UkdCLCBheGVzID1UUlVFLCBzdHJldGNoID0gImxpbiIsIG1haW4gPSAiTGFuZHNhdCBUcnVlIENvbG9yIENvbXBvc2l0ZSIpDQpgYGANCg0KICANClNpbiBlbWJhcmdvLCByZWVtcGxhemFuZG8gbGFzIGJhbmRhcyBhbnRlcmlvcm1lbnRlIG1lbmNpb25hZGFzLCBwb3Igb3RyYXMgZGlmZXJlbnRlcywgeSBnZW5lcmFuZG8gY29tYmluYWNpb25lcyBhZGVjdWFkYXMsIGVzIHBvc2libGUgaWRlbnRpZmljYXIgaW5mb3JtYWNpw7NuIHF1ZSBlbiBjb2xvciB2ZXJkYWRlcm8gbm8gc29uIGbDoWNpbG1lbnRlIGNsYXNpZmljYWJsZXMsIGVzIGFsbMOtIGRvbmRlIGxhcyBpbcOhZ2VuZXMgZW4gY29sb3IgZmFsc28sIGRvbmRlIGxhcyBiYW5kYXMgbm8gY29ycmVzcG9uZGVuIGNvbiBlbCBzaXN0ZW1hIFJHQiwgcHJlc2VudGFuIHVuYSBncmFuIHV0aWxpZGFkLiAgDQpEZWJpZG8gYSBxdWUgbGEgYmFuZGEgTklSIHByZXNlbnRhIHVuYSBncmFuIHJlZmxlY3RhbmNpYSBkZSBsYSB2ZWdldGFjacOzbiwgZXN0YSBlcyByZXByZXNlbnRhZGEgcG9yIGNvbG9yYWNpb25lcyByb2phcyBpbnRlbnNhcy4gIA0KYGBge3J9DQpsYW5kc2F0RkNDX3ZlZyA8LSBsYW5kc2F0W1tjKDUsNCwzKV1dDQpwbG90UkdCKGxhbmRzYXRGQ0NfdmVnLCBheGVzID1UUlVFLCBzdHJldGNoID0gImxpbiIsIG1haW4gPSAiTGFuZHNhdCBGYWxzZSBDb2xvciBDb21wb3NpdGUtVmVnZXRhdGlvbiIpDQpgYGANCiAgDQpVbmEgb3BjacOzbiBhZGljaW9uYWwgZXMgY2xhc2lmaWNhciBsYXMgem9uYXMgcXVlIHRpZW5lbiByZWZsZWN0YW5jaWFzIGluZnJhcnJvamFzIGRlIG9uZGEgY29ydGEgbWF5b3JlcywgcXVlIHBvciBsbyBnZW5lcmFsIHNvbiDDoXJlYXMgdXJiYW5hcywgY29uIGNvYmVydHVyYXMgZGUgY29uY3JldG8uDQoNCmBgYHtyfQ0KbGFuZHNhdEZDQ191cmIgPC0gbGFuZHNhdFtbYyg3LDYsNCldXQ0KcGxvdFJHQihsYW5kc2F0RkNDX3VyYiwgYXhlcyA9VFJVRSwgc3RyZXRjaCA9ICJsaW4iLCBtYWluID0gIkxhbmRzYXQgRmFsc2UgQ29sb3IgQ29tcG9zaXRlLVVyYmFuIFpvbmVzIikNCmBgYA0KICANCkRlIGlndWFsIGZvcm1hLCBsYSB2ZWdldGFjacOzbiB5IGxhcyB6b25hcyBlbiBwcmVwYXJhY2nDs24geSBleHBsb3RhY2nDs24gYWdyw61jb2xhIHB1ZWRlbiByZXN1bHRhciBpZGVudGlmaWNhZGFzIHVzYW5kbyBsYXMgYmFuZGFzIFNXSVIxLCBOSVIgeSBhenVsLg0KICANCmBgYHtyfQ0KbGFuZHNhdEZDQ19hZ3IgPC0gbGFuZHNhdFtbYyg2LDUsMildXQ0KcGxvdFJHQihsYW5kc2F0RkNDX2FnciwgYXhlcyA9VFJVRSwgc3RyZXRjaCA9ICJsaW4iLCBtYWluID0gIkxhbmRzYXQgRmFsc2UgQ29sb3IgQ29tcG9zaXRlLUFncmljdWx0dXJlIikNCmBgYA0KDQoNCmBgYHtyfQ0KbGFuZHNhdEZDQ192ZWdoZWFsdCA8LSBsYW5kc2F0W1tjKDUsNiwyKV1dDQpwbG90UkdCKGxhbmRzYXRGQ0NfdmVnaGVhbHQsIGF4ZXMgPVRSVUUsIHN0cmV0Y2ggPSAibGluIiwgbWFpbiA9ICJMYW5kc2F0IEZhbHNlIENvbG9yIENvbXBvc2l0ZS0NCkhlYWx0aHkgVmVnLiIpDQpgYGANCg0KDQojIyAqKjIuNi4gU3ViY29uanVudG9zIGRlIGFyY2hpdm9zKioNCiAgDQpMYSBmdW5jacOzbiAqKHN1YnNldCkqIHkgc3UgdmFyaWFjacOzbiAqbGFuZHNhdFtbYyh4MSx4Mix4MyldXSogbyAqbGFuZHNhdFtbeDE6eDNdXSogeWEgZnVlIGVtcGxlYWRhIGFudGVyaW9ybWVudGUgcGFyYSBncmFmaWNhciBsb3MgbWFwYXMgKmxhbmRzYXRSR0IqLCAqbGFuZHNhdEZDQyogeSBzZSB2ZXLDoSBwb3N0ZXJpb3JtZW50ZSBlbiBsb3MgcmVzcGVjdGl2b3MgcmVjb3J0ZXMgKihsYW5kc2F0Y3JvcCkqLCBjw7Ntb2RhIHkgcHJhY3RpY2EgeWEgcXVlIGVsIG9yZGVuIGRlIGVubGFjZSBwdWVkZSBzZXIgYXNjZW5kZW50ZSBvIGRlc2NlbmRlbnRlIHRlbmllbmRvIGFsbWFjZW5hZG8gdW4gc29sbyBibG9xdWUgY29uIHRvZGFzIGxhcyB2YXJpYWJsZXMgeSBubyBlc3RhcyBpbmRlcGVuZGllbnRlcyB5IHBvciBhcGFydGUuICAgDQoNCmBgYHtyfQ0KbGFuZHNhdHN1YjEgPC0gc3Vic2V0KGxhbmRzYXQsIDE6MykNCmBgYA0KDQoNCmBgYHtyfQ0KbGFuZHNhdHN1YjIgPC0gbGFuZHNhdFtbMTozXV0NCmBgYA0KDQpgYGB7cn0NCm5sYXllcnMobGFuZHNhdCkNCmBgYA0KDQpgYGB7cn0NCm5sYXllcnMobGFuZHNhdHN1YjIpDQpgYGANCg0KVGFtYmllbiBwdWVkZSBzZXIgZW1wbGVhZGEgcGFyYSBzb2JyZWVzY3JpYmlyIHVuIG9iamV0byB5IHJlZW1wbGF6YXIgbGFzIGJhbmRhcyBhbG1hY2VuYWRhcyBlbiB1biBibG9xdWUsIGVsaW1pbmFuZG8gYXF1ZWxsYXMgcXVlIG5vIG5vcyBpbnRlcmVzYW4uDQoNCmBgYHtyfQ0KbGFuZHNhdCA8LSBzdWJzZXQobGFuZHNhdCwgMTo3KQ0KYGBgDQoNCmBgYHtyfQ0KbmFtZXMobGFuZHNhdCkNCmBgYA0KDQpSZW5vbWJyYW5kb2xhcywgcGVybWl0aXLDoSB1bmEgZmFjaWwgaWRlbnRpZmljYWNpw7NuIGRlIGVzdGFzLg0KDQpgYGB7cn0NCm5hbWVzKGxhbmRzYXQpIDwtIGMoJ3VsdHJhLWJsdWUnLCdibHVlJywnZ3JlZW4nLCdyZWQnLCdOSVInLCdTV0lSMScsJ1NXSVIyJykNCm5hbWVzKGxhbmRzYXQpDQpgYGANCg0KIyMgKioyLjcuIFJlY29ydGVzIGVzcGFjaWFsZXMgZGUgaW3DoWdlbmVzKioNCg0KRWwgY2FzbyBtw6FzIGNvbcO6biBlcyBvYnRlbmVyIHVuYSBpbWFnZW4gc2F0ZWxpdGFsIGRlIGdyYW4gdGFtYcOxbywgcXVlIGFiYXJjYSBlbCDDoXJlYSBkZSBpbnRlcsOpcyB5IG11Y2hvIG3DoXMsIHBvciBsbyBxdWUgc2UgcmVxdWllcmUgZGVsaW1pdGFyIMO6bmljYW1lbnRlIGxhIHpvbmEgZG9uZGUgc2UgZGVzZWFuIGNvbm9jZXIgbGFzIGNhcmFjdGVyw61zdGljYXMsIGV2aXRhbmRvIGFzw60gcmFsZW50aXphciBsb3MgcHJvY2Vzb3MuICANCg0KKihleHRlbnQpKiByZXF1aWVyZSBkaW1lbnNpb25hciBwcmV2aWFtZW50ZSBlbCDDoXJlYSBkZSBlc3R1ZGlvLCB5IGVtcGxlYW5kbyBjb29yZGVuYWRhcyBjYXJ0ZXNpYW5hcyBwdWVkZSBzZXIgZGVsaW1pdGFkbyB1biByZWN0w6FuZ3VsbyAgKihZbWluLCBZbWF4LCBYbWluIHkgWG1heCkqLCBwb3N0ZXJpb3JtZW50ZSBsYSBmdW5jacOzbiAqKGNyb3ApKiBwZXJtaXRpcsOhIGFsbWFjZW5hciBlbiB1biBudWV2byBvYmpldG8gZWwgcmVjb3J0ZSByZWFsaXphZG8gZW50cmUgZWwgcmFzdGVyIHkgZWwgcmVjdWFkcm8gcmVjaWVudGVtZW50ZSBjcmVhZG8uDQoNCg0KYGBge3J9DQpleHRlbnQobGFuZHNhdCkNCmBgYA0KDQpgYGB7cn0NCmUgPC1leHRlbnQoNjI0Mzg0LCA2MzU3NTIsIDQyMDAwNDcsIDQyMTA5MzkpDQpsYW5kc2F0Y3JvcCA8LSBjcm9wKGxhbmRzYXQsZSkNCmV4dGVudChsYW5kc2F0Y3JvcCkNCmBgYA0KDQojIyAqKjIuOC4gQ29tcG9zaWNpw7NuIGRlIENvbG9yZXMgVmVyZGFkZXJvIHkgRmFsc28gcGFyYSBsYXMgSW3DoWdlbmVzIFJlY29ydGFkYXMqKg0KICANCkEgY29udGludWFjacOzbiBzZSBwcmVzZW50YW4gcHJvY2Vzb3MgZGUgcGxvdGVvIGNvbiBlbCBkZXRhbGxlIGRlIGxhcyBpbcOhZ2VuZXMgcmVjb3J0YWRhcy4gICANCg0KYGBge3J9DQpsYW5kc2F0UkdCY3JvcCA8LSBsYW5kc2F0Y3JvcFtbYyg0LDMsMildXQ0KcGxvdFJHQihsYW5kc2F0UkdCY3JvcCwgYXhlcyA9VFJVRSwgc3RyZXRjaCA9ICJsaW4iLCBtYWluID0gIkxhbmRzYXQgVHJ1ZSBDb2xvciBDb21wb3NpdGUtY3JvcGVkIikNCmBgYA0KDQpgYGB7cn0NCmxhbmRzYXRGQ0Njcm9wX3ZlZyA8LSBsYW5kc2F0Y3JvcFtbYyg1LDQsMyldXQ0KcGxvdFJHQihsYW5kc2F0RkNDY3JvcF92ZWcsIGF4ZXMgPVRSVUUsIHN0cmV0Y2ggPSAibGluIiwgbWFpbiA9ICJMYW5kc2F0IEZhbHNlIENvbG9yIENvbXBvc2l0ZS1jcm9wZWQtdmVnZXRhdGlvbiIpDQpgYGANCg0KIyMgKioyLjkuIEFsbWFjZW5hbWllbnRvIGRlIERhdG9zIGVuIGVsIERpc2NvKioNCg0KTG9zIGRpZmVyZW50ZXMgYXJjaGl2b3MgZW4gbG9zIHF1ZSBzZSBwdWVkZSBhbG1hY2VuYXIgbGEgaW5mb3JtYWNpw7NuIHByb2Nlc2FkYSwgdGllbmVuIGNhcmFjdGVyw61zdGljYXMgcXVlIGRlYmVuIHNlciBjbGFyYXMuDQoNCnxBcmNoaXZvcyAqIi50aWYiKiB8IGFyY2hpdm9zICoiLmdyZCIqIHwNCnw6LS0tLS0tLS0tLS0tLS06fDotLS0tLS0tLS0tLS06fA0KfCBMb3MgYXJjaGl2b3MgR2VvVGlmZiBzb24gZGUgdXNvIGNvbcO6biwgY29uc2VydmFuIGVsIG9yZGVuIGRlIGFsbWFjZW5hbWllbnRvIGRlIGxhcyBjYXBhcyBkZWwgcmFzdGVyLCBwZXJvIHN1cyBub21icmVzIHNlIHBpZXJkZW4gZW4gZXN0ZSB0aXBvIGRlIGZvcm1hdG98IExvcyBhcmNoaXZvcyBkZSBlc3RhIGNhcmFjdGVyw61zdGljYSwgcGVybWl0ZW4gYWxtYWNlbmFyIGVsIG5vbWJyZSBkZSBsYXMgY2FwYXMsIHNpbiBlbWJhcmdvLCBubyBtdWNob3MgcHJvZ3JhbWFzIGxvZ3JhbiBsZWVybG9zLiB8DQoNCg0KYGBge3J9DQp4MSA8LSB3cml0ZVJhc3RlcihsYW5kc2F0Y3JvcCwgZmlsZW5hbWU9ImRhdGEvcnMvY3JvcHBlZC1sYW5kc2F0LnRpZiIsIG92ZXJ3cml0ZT1UUlVFKQ0KDQoNCngyIDwtIHdyaXRlUmFzdGVyKGxhbmRzYXRjcm9wLCBmaWxlbmFtZT0iZGF0YS9ycy9jcm9wcGVkLWxhbmRzYXQuZ3JkIiwgb3ZlcndyaXRlPVRSVUUpDQoNCmBgYA0KDQojIyAqKjIuMTAuIFJlbGFjacOzbiBlbnRyZSBiYW5kYXMqKg0KICANCg0KRW5jb250cmFyIGxhIHByb3BvcmNpb25hbGlkYWQgZGUgbGEgcmVsYWNpw7NuIGVudHJlIGFsZ3VuYXMgYmFuZGFzIHBlcm1pdGUgaWRlbnRpZmljYXIgY3VhbGVzIHB1ZWRlbiBzZXIgdXNhZGFzIHNpbiBwZXJkZXIgaW5mb3JtYWNpw7NuLCBjb21vIHN1Y2VkZSBjb24gbGEgYmFuZGEgMSB5IDIgZGUgTGFuZHNhdCA4LCBtZWRpYW50ZSBsYSBmdW5jacOzbiAqKHBhaXJzKSosIGxhIGNvbXBhcmFjacOzbiBlcyBsbGV2YWRhIGEgY2FibywgYXPDrSBtaXNtbywgcHVlZGUgZGFyc2UgZWwgY2FzbyBkZSBsYXMgYmFuZGFzIDQgeSA1LCBxdWUgZ2VuZXJhbiB1bmEgbnViZSBkZSBwdW50b3MgY2VyY2FuYSBhbCBlamUgWSwgZG9uZGUgbGEgcmVmbGVjdGFuY2lhIHZlZ2V0YWwgZXMgbXV5IG1hcmNhZGEsIG1vc3RyYW5kbyB1bmEgYmFqYSBjb3JyZWxhY2nDs24uDQogIA0KICANCg0KYGBge3J9DQpwYWlycyhsYW5kc2F0Y3JvcFtbMToyXV0sIG1haW4gPSAiVWx0cmEtQmx1ZSBWcyBCbHVlIikNCmBgYA0KDQpgYGB7cn0NCnBhaXJzKGxhbmRzYXRjcm9wW1s0OjVdXSwgbWFpbiA9ICJSZWQgVnMgTklSIikNCmBgYA0KDQoNCiMjICoqMi4xMS4gVmFsb3JlcyBkZSBsb3MgUGl4ZWxlcyoqDQoNClRlbmllbmRvIHVuIGFyY2hpdm8gZGUgcG9sw61nb25vcyBxdWUgaWRlbnRpZmlxdWVuIGbDoWNpbG1lbnRlIGxhcyBkaWZlcmVudGVzIMOhcmVhcyBkZSBleHBsb3RhY2nDs24gYWdyw61jb2xhLCBmb3Jlc3RhbCwgdXJiYW5hIGVudHJlIG90cmFzLCBzZSBwdWVkZW4gZXh0cmFlciBjb29yZGVuYWRhcyBvIHB1bnRvcyBtZWRpYW50ZSBsYSBmdW5jacOzbiAqKHNwc2FtcGxlKSosIGVzY29naWVuZG8gbGEgY2FudGlkYWQgZGUgcHVudG9zIHkgbGEgZm9ybWEgZGUgZXh0cmFjY2nDs24gZGUgZXN0b3MuICAgDQoNCmBgYHtyfQ0KI0VzdGEgcHJpbWVyYSBsaW5lYSwgY2FyZ2EgZWwgYXJjaGl2byBSRFMgcXVlIGNvbnRpZW5lIGxvcyBwb2zDrWdvbm9zIGRlIGNsYXNpZmljYWNpw7NuIGRlIHN1ZWxvLg0KbGNfc2FtcGxlcyA8LSByZWFkUkRTKCdkYXRhL3JzL3NhbXBsZXMucmRzJykNCiNzcHNhbXBsZSwgZ2VuZXJhIHBhcmEgZXN0ZSBjYXNvLCAzMDAgcHVudG9zIGRlIGZvcm1hIHJlZ3VsYXIgc29icmUgbG9zIHBvbMOtZ29ub3MgYWxtYWNlbmFkb3MgZW4gZWwgb2JqZXRvIGxjX3NhbXBsZXMNCnB0c2FtcCA8LSBzcHNhbXBsZShsY19zYW1wbGVzLCAzMDAsIHR5cGUgPSAncmVndWxhcicpDQojU2UgYWdyZWFnYSBsYSBjbGFzZSBkZSBjb2JlcnR1cmEgZGUgc3VlbG8gYSBsb3MgcHVudG9zIGV4dHJhaWRvcy4gDQpwdHNhbXAkY2xhc3MgPC0gb3ZlcihwdHNhbXAsIGxjX3NhbXBsZXMpJGNsYXNzDQojRXh0cmFlciBsb3MgdmFsb3JlcyBkZSByYWRpYW5jaWEgZW4gZWwgcmFzdGVyICJsYW5kc2F0ImRlIGxvcyBwdW50b3MNCmRmIDwtIGV4dHJhY3QobGFuZHNhdCwgcHRzYW1wKQ0KaGVhZChkZikNCmBgYA0KDQojIyAqKjIuMTIuIFBlcmZpbGVzIGVzcGVjdHJhbGVzKioNCg0KVW5hIGRlIGxhcyBoZXJyYW1pZW50YXMgbWFzIGltcG9ydGFudGVzIGVzIGVsIHBlcmZpbCBlc3BlY3RyYWwgZGUgbGFzIGJhbmRhcyBjb250ZW5pZGFzIGVuIHVuIG9iamV0bywgZWxhYm9yYWRhIGNvbiBsb3MgcGl4ZWxlcyBleHRyYcOtZG9zIGFudGVyaW9ybWVudGUsIGRvbmRlIGVzdMOhbiByZXByZXNlbnRhZGFzIGFsZ3VuYXMgY2FyYWN0ZXLDrXN0aWNhcyBkZSBsYSBzdXBlcmZpY2llLg0KDQoNCmBgYHtyfQ0KbXMgPC0gYWdncmVnYXRlKGRmLCBsaXN0KHB0c2FtcCRjbGFzcyksIG1lYW4pDQpgYGANCg0KDQpgYGB7cn0NCnJvd25hbWVzKG1zKSA8LSBtc1ssMV0NCiNMYSBwcmltZXJhIGNvbHVtbmEsIHRlbmRyw6EgbG9zIG5vbWJyZXMgZGUgbGFzIGNsYXNlcw0KbXMgPC0gbXNbLC0xXQ0KbXMNCmBgYA0KDQoNCmBgYHtyfQ0KbXljb2xvciA8LSBjKCdkYXJrcmVkJywneWVsbG93JywnYnVybHl3b29kJywnY3lhbicsJ2JsdWUnKQ0KbXMgPC0gYXMubWF0cml4KG1zKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdCgwLCB5bGltPWMoMCwwLjYpLCB4bGltPWMoMSw3KSx0eXBlPSduJyx4bGFiPSJCYW5kcyIseWxhYj0iUmVmbGVjdGFuY2UiKQ0KZm9yIChpIGluIDE6bnJvdyhtcykpew0KICBsaW5lcyhtc1tpLF0sdHlwZT0ibCIsIGx3ZD0zLGx0eT0xLGNvbD1teWNvbG9yW2ldKQ0KfQ0KdGl0bGUobWFpbj0iU3BlY3RyYWwgUHJvZmlsZSBmcm9tIExhbmRzYXQiLCBmb250Lm1haW49MikNCmxlZ2VuZCgidG9wbGVmdCIscm93bmFtZXMobXMpLA0KICAgICAgIGNleD0wLjgsIGNvbD1teWNvbG9yLGx0eT0xLGx3ZD0zLGJ0eT0ibiIpDQpgYGANCg0KIyAqKlByYWN0aWNhIDM6IE9wZXJhY2lvbmVzIG1hdGVtw6F0aWNhcyBiw6FzaWNhcyoqDQoNCg0KIyMgKirDjW5kaWNlcyBkZSB2ZWdldGFjacOzbioqDQoNCg0KYGBge3J9DQp2aSA8LSBmdW5jdGlvbihpbWcsayxpKXsNCiAgYmsgPC0gaW1nW1trXV0NCiAgYmkgPC0gaW1nW1tpXV0NCiAgdmkgPC0gKGJrLWJpKS8oYmsrYmkpDQogIHJldHVybih2aSkNCn0NCm5kdmkgPC0gdmkobGFuZHNhdCwgNSwgNCkNCnBsb3QobmR2aSwgY29sID0gcmV2KHRlcnJhaW4uY29sb3JzKDEwKSksIG1haW49IkxhbmRzYXQtTkRWSSIpDQpgYGANCg0KDQpPdHJhIGFsdGVybmF0aXZhOg0KDQpgYGB7cn0NCnZpMiA8LSBmdW5jdGlvbih4LHkpew0KICAoeC15KS8oeCt5KQ0KfQ0KbmR2aTIgPC0gb3ZlcmxheShsYW5kc2F0W1s1XV0sIGxhbmRzYXRbWzRdXSwgZnVuPXZpMikNCnBsb3QobmR2aTIsIGNvbD1yZXYodGVycmFpbi5jb2xvcnMoMTApKSwgbWFpbj0iTGFuZHNhdC1ORFZJIikNCmBgYA0KDQoNCiMjIyAqKkhpc3RvZ3JhbWEgZGUgTkRWSSoqDQoNCg0KYGBge3J9DQpoaXN0KG5kdmksDQogICAgIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIE5EVkkgdmFsdWVzIiwNCiAgICAgeGxhYiA9ICJORFZJIiwNCiAgICAgeWxhYj0gIkZyZXF1ZW5jeSIsDQogICAgIGNvbCA9ICJncmVlbiIsDQogICAgIHhsaW0gPSBjKC0wLjUsIDEpLA0KICAgICBicmVha3MgPSAzMCwNCiAgICAgeGF4dCA9ICduJykNCmF4aXMoc2lkZT0xLCBhdCA9IHNlcSgtMC41LDEsIDAuMDUpLCBsYWJlbHMgPSBzZXEoLTAuNSwxLCAwLjA1KSkNCmBgYA0KDQojIyAqKsONbmRpY2UgZGUgQWd1YSBkZSBEaWZlcmVuY2lhIE5vcm1hbGl6YWRhKioNCg0KDQpgYGB7cn0NCndpIDwtIGZ1bmN0aW9uKGltZyxrLGkpew0KICBiayA8LSBpbWdbW2tdXQ0KICBiaSA8LSBpbWdbW2ldXQ0KICB3aSA8LSAoYmstYmkpLyhiaytiaSkNCiAgcmV0dXJuKHZpKQ0KfQ0KbmR3aSA8LSB2aShsYW5kc2F0LCAzLCA1KQ0KcGxvdChuZHdpLCBjb2wgPSByZXYodG9wby5jb2xvcnMoMTApKSwgbWFpbj0iTkRXSS1NYyBGZWV0ZXJzICgyMDA2KSIpDQpgYGANCg0KIyMjICoqSGlzdG9ncmFtYSBkZSBORFdJIC0gTWMgRmVldGVycyAoMjAwNikqKg0KDQoNCmBgYHtyfQ0KaGlzdChuZHdpLA0KICAgICBtYWluID0gIkRpc3RyaWJ1dGlvbiBvZiBORFdJIHZhbHVlcyIsDQogICAgIHhsYWIgPSAiTkRXSSIsDQogICAgIHlsYWI9ICJGcmVxdWVuY3kiLA0KICAgICBjb2wgPSAid2hlYXQiLA0KICAgICB4bGltID0gYygtMC41LCAxKSwNCiAgICAgYnJlYWtzID0gMzAsDQogICAgIHhheHQgPSAnbicpDQpheGlzKHNpZGU9MSwgYXQgPSBzZXEoLTAuNSwxLCAwLjA1KSwgbGFiZWxzID0gc2VxKC0wLjUsMSwgMC4wNSkpDQpgYGANCg0KYGBge3J9DQpuZHdpZnVuYyA8LSBmdW5jdGlvbihpbWcsIGssIGkpew0KICBiY3JrIDwtIGltZ1tba11dDQogIGJjcmkgPC0gaW1nW1tpXV0NCiAgbmR3aWZ1bmMgPC0gKGJjcmsgLSBiY3JpKSAvIChiY3JrICsgYmNyaSkNCiAgcmV0dXJuKG5kd2lmdW5jKQ0KfQ0KDQptbmR3aSA8LSBuZHdpZnVuYyhsYW5kc2F0LCAzLCA2KQ0KcGxvdChtbmR3aSwgY29sID0gcmV2KHRvcG8uY29sb3JzKDEwKSksIG1haW4gPSAiTS1ORFdJIC0gWHUgKDIwMDYpIikNCmBgYA0KDQoNCiMjIyAqKkhpc3RvZ3JhbWEgZGUgTU5EV0kgLSBYdSAoMjAwNikqKg0KDQpgYGB7cn0NCmhpc3QobW5kd2ksDQogICAgIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIE0tTkRXSSB2YWx1ZXMiLA0KICAgICB4bGFiID0gIk1ORFdJIiwNCiAgICAgeWxhYj0gIkZyZXF1ZW5jeSIsDQogICAgIGNvbCA9ICJ5ZWxsb3ciLA0KICAgICB4bGltID0gYygtMC41LCAxKSwNCiAgICAgYnJlYWtzID0gMzAsDQogICAgIHhheHQgPSAnbicpDQpheGlzKHNpZGU9MSwgYXQgPSBzZXEoLTAuNSwxLCAwLjA1KSwgbGFiZWxzID0gc2VxKC0wLjUsMSwgMC4wNSkpDQpgYGANCg0KIyMgKirDjW5kaWNlIGRlIERpZmVyZW5jaWFzIE5vcm1hbGl6YWRhcyBkZSDDgXJlYSBDb25zdHJ1aWRhKioNCg0KKihFbXBsZWFuZG8gbGEgaW1hZ2VuIHJlY29ydGFkYSkqDQpgYGB7cn0NCm5kYmlmdW5jIDwtIGZ1bmN0aW9uKGltZywgaywgaSl7DQogIGJjcmsgPC0gaW1nW1trXV0NCiAgYmNyaSA8LSBpbWdbW2ldXQ0KICBuZGJpZnVuYyA8LSAoYmNyayAtIGJjcmkpIC8gKGJjcmkgKyBiY3JrKQ0KICByZXR1cm4obmRiaWZ1bmMpDQp9DQoNCm5kYmkgPC0gbmRiaWZ1bmMobGFuZHNhdGNyb3AsIDYsIDUpDQpwbG90KG5kYmksIGNvbCA9IHJldih0b3BvLmNvbG9ycygxMCkpLCBtYWluID0gIk5EQkkiKQ0KYGBgDQoNCiMjIyAqKkhpc3RvZ3JhbWEgZGUgTkRCSSoqDQoNCmBgYHtyfQ0KaGlzdChuZGJpLA0KICAgICBtYWluID0gIkRpc3RyaWJ1dGlvbiBvZiBOREJJIHZhbHVlcyIsDQogICAgIHhsYWIgPSAiTkRCSSIsDQogICAgIHlsYWI9ICJGcmVxdWVuY3kiLA0KICAgICBjb2wgPSAiYmx1ZSIsDQogICAgIHhsaW0gPSBjKC0wLjUsIDEpLA0KICAgICBicmVha3MgPSAzMCwNCiAgICAgeGF4dCA9ICduJykNCmF4aXMoc2lkZT0xLCBhdCA9IHNlcSgtMC41LDEsIDAuMDUpLCBsYWJlbHMgPSBzZXEoLTAuNSwxLCAwLjA1KSkNCmBgYA0KDQoNCiMjICoqVW1icmFsaXphY2nDs24qKg0KDQpgYGB7cn0NCnZlZyA8LSByZWNsYXNzaWZ5KG5kdmksIGNiaW5kKC1JbmYsMC40LE5BKSkNCnBsb3QodmVnLCBtYWluPSdWZWdldGF0aW9uJykNCmBgYA0KDQpgYGB7cn0NCmxhbmQgPC0gcmVjbGFzc2lmeShuZHZpLCBjKC1JbmYsIDAuMjUsIE5BLCAgMC4yNSwgMC4zLCAxLCAgMC4zLCBJbmYsIE5BKSkNCnBsb3QobGFuZCwgbWFpbiA9ICdPcGVuIEFyZWFzJykNCmBgYA0KDQpgYGB7cn0NCnBsb3RSR0IobGFuZHNhdFJHQiwgcj0xLCBnPTIsIGI9MywgYXhlcz1UUlVFLCBzdHJldGNoPSJsaW4iLCBtYWluPSJMYW5kc2F0IEZhbHNlIENvbG9yIENvbXBvc2l0ZSIpDQpwbG90KGxhbmQsIGFkZD1UUlVFLCBsZWdlbmQ9RkFMU0UpDQpgYGANCg0KDQpgYGB7cn0NCnZlZ2MgPC0gcmVjbGFzc2lmeShuZHZpLCBjKC1JbmYsMC4yNSwxLCAwLjI1LDAuMywyLCAwLjMsMC40LDMsIDAuNCwwLjUsNCwgMC41LEluZiwgNSkpDQpwbG90KHZlZ2MsY29sID0gcmV2KHRlcnJhaW4uY29sb3JzKDQpKSwgbWFpbiA9ICdORFZJIGJhc2VkIHRocmVzaG9sZGluZycpDQpgYGANCg0KTGEgcmVzcHVlc3RhIGRlbCBwZXJmaWwgZXNwZWN0cmFsLCBtdWVzdHJhIHF1ZSBwYXJhIGxhcyA3IGJhbmRhcywgbGEgcmVmbGVjdGFuY2lhIGRlbCBhZ3VhIGVzIGxhIHVuaWNhIHF1ZSBzZSBlbmN1ZW50cmEgZW50cmUgMCB5IDAuMiBjb21vIHZhbG9yZXMgbcOheGltb3MgYXByb3hpbWFkb3MsIGFzw60gcXVlIGFjb3RhbW9zIGVsIHN1YmNvbmp1bnRvIGRlIHJlY2xhc2lmaWNhY2nDs24gZW4gZXN0YSBmcmFuamEuDQoNCmBgYHtyfQ0Kd2F0ZXIgPC0gcmVjbGFzc2lmeShuZHdpLCBjYmluZCgtSW5mLCAwLjEsIE5BKSkNCiNTb2xvIGVzIG5lY2VzYXJpbyBhY290YXIgZWwgbGltaXRlIGluZmVyaW9yLCB5YSBxdWUgdG9kbyBzb2JyZSBlc3RlIHZhbG9yICgwLjEpIHNlIG11ZXN0cmEgY29uIGNsYXNpZmljYWNpw7NuIGRlIGludGVyw6lzDQpjb2xvcnMgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJ3aGVhdCIsImN5YW4iLCJibHVlIikpDQpwbG90KHdhdGVyLGNvbCA9IGNvbG9ycygxMCksIG1haW4gPSAnd2F0ZXInKQ0KYGBgDQoNClkgcGFyYSBlbCBjYXNvIGRlIMOhcmVhcyBjb25zdHJ1aWRhcywgZWwgbml2ZWwgZGUgcmVmbGVjdGFuY2lhIG11ZXN0cmEgZWwgbWlzbW8gY29tcG9ydGFtaWVudG8gc29icmUgbG9zIHZhbG9yZXMgZGUgMC4xIG8gMC4xNSBoYXN0YSAwLjINCg0KYGBge3J9DQp1cmJhbnJlY2xhc3MgPC0gcmVjbGFzc2lmeShuZGJpLCBjYmluZCgtSW5mLCAwLjAsIE5BKSkNCmNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKGMoIndoZWF0IiwiZ3JleSIsImJsYWNrIikpDQpwbG90KHVyYmFucmVjbGFzcyxjb2wgPSBjb2xvcnMoNTApLCBtYWluID0gJ1VyYmFuIHpvbmVzJykNCmBgYA0KQmFqYW5kbyBlbCBsw61taXRlIHN1cGVyaW9yIGhhc3RhIDAsIHNlIGxvZ3JhbiBldmlkZW5jaWFyIGluY2x1c28sIGxvIHF1ZSBwYXJlY2VuIHNlciB2aWFzIHF1ZSByb2RlYW4gbG9zIGN1ZXJwb3MgZGUgYWd1YS4NCg0KIyMgKipBbsOhbGlzaXMgZGUgY29tcG9uZW50ZXMgUHJpbmNpcGFsZXMqKg0KDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMSkNCnNyIDwtIHNhbXBsZVJhbmRvbShsYW5kc2F0LCAxMDAwMCkNCnBsb3Qoc3JbLGMoNCw1KV0sIG1haW4gPSAiTklSLVJlZCBwbG90IikNCmBgYA0KDQoNCmBgYHtyfQ0KcGNhIDwtIHByY29tcChzciwgc2NhbGUgPSBUUlVFKQ0KcGNhDQpzY3JlZXBsb3QocGNhKQ0KYGBgDQoNCmBgYHtyfQ0KcGNpIDwtIHByZWRpY3QobGFuZHNhdCwgcGNhLCBpbmRleCA9IDE6MikNCnBsb3QocGNpW1sxXV0pDQpgYGANCg0KYGBge3J9DQpwYXIobWZyb3c9YygxLDIpKQ0KcGMyIDwtIHJlY2xhc3NpZnkocGNpW1syXV0sIGMoLUluZiwwLDEsMCxJbmYsTkEpKQ0KcGxvdFJHQihsYW5kc2F0RkNDX3ZlZywgciA9IDEsIGcgPSAyLCBiID0gMywgYXhlcyA9IFRSVUUsIHN0cmV0Y2ggPSAibGluIiwgbWFpbiA9ICJMYW5kc2F0IEZhbHNlIENvbG9yIENvbXBvc2l0ZSIpDQpwbG90UkdCKGxhbmRzYXRGQ0NfdmVnLCByID0gMSwgZyA9IDIsIGIgPSAzLCBheGVzID0gVFJVRSwgc3RyZXRjaCA9ICJsaW4iLCBtYWluID0gIkxhbmRzYXQgRmFsc2UgQ29sb3IgQ29tcG9zaXRlIikNCnBsb3QocGMyLCBsZWdlbmQgPSBGQUxTRSwgYWRkID0gVFJVRSkNCmBgYA0KDQoNCg0K