1. Introducción
Vamos a utilizar R como una aplicación GIS (Sistemas de información
geográfica); es decir, lo vamos a utilizar para almacenar, manipular,
analizar y presentar datos geográficos o geoespaciales. GIS es un área
enorme, por ejemplo, aquí tienes
un listado de herramientas y paquetes para trabajar con datos espaciales
en R. Hay muchísimas!!!, es un área de conocimiento en sí misma, así que
sólo voy a intentar hacer una pequeña introducción a GIS con R.
Durante este tutorial usaremos distintos data.frames con geometrías
de:
- países del mundo (world)
- CC.AA españolas (CCAA)
- provincias españolas (Provincias)
- municipios españoles en 2020 (municipios_2020)
- ríos españoles (rios_espanya)
- 4 ríos relacionados con el río Ebro (rio_ebro_4)
- localización (longitud, latitud) de los municipios españoles
(IGN_nomencla_muni)
Venga, vamos a hacer mapas con ellos!!! Sí, enseguida!! A todos nos
gustan los mapas y hacer mapas con R es easy … bueno, no tan
fácil, pero vamos a ello!!
Más que mapas lo que vamos a hacer es visualizar datos
geoespaciales. Unos datos no se pueden considerar geoespaciales
si no vienen acompañados de un CRS (Coordinate
Reference System) de forma que las aplicaciones GIS puedan manipular y
mostrar correctamente esos datos. Los CRS utilizan un modelo matemático
para conectar los datos, las coordenadas de un objeto, con su posición
física en la superficie terrestre. Un sistema de referencia de
coordenadas geográficas permite que todos los puntos de la tierra puedan
ser descritos como un conjuntos de coordenadas (como latitud, longitud y
altitud). Diversos sistemas se utilizan para representar la tierra
tridimensional en un plano o mapa bidimensional.
Los datos geoespaciales representan lugares geográficos de la Tierra
y para poder representar la posición de un objeto en la Tierra
necesitamos dos cosas:
Un CRS consiste en modelo geométrico elipsoide de la superficie
terrestre y un datum que identifica el origen y orientación de
los ejes y también las unidades de medida.
Como sabéis la Tierra no es esférica y esto complica la utilización
de un único CRS, de forma que se han creado unos cuantos para tratar de
representar la superficie terrestre de forma adecuada.
Además, hay dos tipos de CRS: CRS geográficos y
proyectados. En los sistema geográficos, el marco de referencia
es el globo terráqueo y cuantos grados norte o este, está el objeto;
mientras que en los sistemas proyectados, el marco de referencia es una
representación plana de la Tierra (o de parte de ella). Si te interesa
el tema, aquí
y aquí
tienes dos posts divulgativos.
Proyecciones
Como la Tierra es esférica, para hacer un mapa bidimensional (o
plano) de ella, se tiene que usar algún tipo de proyección, por lo que
las áreas se mostrarán más o menos distorsionadas. Por ejemplo:
p1 <- ggplot(CCAA, aes(fill = ine_ccaa) ) +
geom_sf() +
scale_fill_viridis_d(guide = "none") +
labs(title = "Coord. geográficas")
p2 <- ggplot(CCAA, aes(fill = ine_ccaa) ) +
geom_sf() + coord_sf(crs = 2154, datum = sf::st_crs(2154)) +
scale_fill_viridis_d(guide = "none") +
labs(title = "Lambert 93")
p1 + p2
Datos espaciales
Para poder representar adecuadamente datos espaciales, estos deben
contener la información de su CRS y algunas veces hay que convertir
entre CRS’s; de forma que la estructura tradicional de datos en R (los
data.frames) no es adecuada para almacenar datos espaciales. Piensa
además que los datos espaciales más simples (los puntos) serían fáciles
de almacenar en dos columnas de un data.frame, pero piensa en objetos
espaciales más complejos como lineas, polígonos, polígonos con huecos
etc… hace que para almacenar datos espaciales en R se utilicen
estructuras de datos más complejas que un data.frame.
Además, para complicar un poco más las cosas, hay dos tipos
fundamentales de datos geográficos: raster y
vectoriales. En el curso solo vamos a ver (un poquito) los
datos espaciales vectoriales.
Los datos ráster se almacenan como una cuadrícula de valores que
se representan en un mapa como píxeles. Cada valor de píxel representa
un área en la superficie de la Tierra. Los datos ráster son datos
pixelados (o cuadriculados) en los que cada píxel está asociado a una
ubicación geográfica específica. El valor de un píxel puede ser continuo
(por ejemplo, elevación) o categórico (por ejemplo, uso del suelo). Un
ráster geoespacial solo es diferente de una foto digital en que está
acompañado de información espacial que conecta los datos a una ubicación
en particular. Esto incluye la extensión y el tamaño de celda del
ráster, el número de filas y columnas y su sistema de referencia de
coordenadas (o CRS)
Las estructuras de datos vectoriales representan posiciones
específicas en la superficie de la Tierra.
sp
versus sf
En R hay 2 formas alternativas de almacenar (y de manipular) datos
geográficos vectoriales: el paquete sp
y el paquete
sf
. Las principales diferencias entre los paquetes sp y sf
se derivan de la manera en que las clases sp y sf almacenan información
sobre CRS, distinguen entre puntos, líneas y polígonos, y cómo conectan
estos datos espaciales con datos no espaciales.
Cuando se usan estos paquetes, tanto sp
como
sf
, proporcionan enlaces a potentes bibliotecas SIG de
código abierto, como GDAL (Geospatial Data
Abstraction Library: es una biblioteca de software para la lectura y
escritura de formatos de datos geoespaciales, publicada bajo la MIT
License por la fundación geoespacial de código abierto (Open Source
Geospatial Foundation), GEOS
(Geometry Engine Open Source: para operaciones de topología en
geometrías) y PROJ (PROJ es una
biblioteca que proporciona métodos para transformar entre sistemas de
referencia de coordenadas diferentes y operaciones de proyección). Por
eso cuando cargamos el paquete sf
nos aparece el siguiente
mensaje en la consola:
Linking to GEOS 3.9.1, GDAL 3.3.2, PROJ 7.2.1
Muchos paquetes R para GIS utilizan la metodología/arquitectura
proporcionada por sp
, pero por varias razones la
metodología implementada por el paquete el paquete sf
ya es
prácticamente el estándar.
Las principales razones son 4:
El paquete sf
implementa el estándar abierto “simple
feautures” para la representación de datos geográficos vectoriales en R.
Simple features es un estándar, un conjunto común de reglas, para
representar las geometrías espaciales de objetos del mundo real en un
ordenador, en las que se define desde cómo se almacenan los datos en el
ordenador hasta qué operaciones geométricas se pueden aplicar.
sf
utiliza para contener/almacenar los datos
espaciales una estructura muy similar a un dataframe: los spatial
dataframes o sf
, que son muy parecidos a un data frame
salvo que incluyen una columna adicional, generalmente llamada
geometry
que almacena la información geográfica, los datos
vectoriales espaciales, ya sean estos puntos, lineas, polígonos
etc…
Muchas de las funciones del paquete sf
comienzan con
st_
; por ejemplo st_nearest_points()
, lo que
facilita su uso.
las funciones de sf
se integran en el
tidyverse workflow. De hecho la estructura de datos que
utiliza el paquete sf
para almacenar datos geográficos son
los objetos de tipo `sf
una extensión de los data.frames,
de forma que pueden ser manipulados con dplyr
y graficados
con ggplot2
. Los objetos sf
pueden almacenar
datos normales (cuantitativos, cualitativos, texto) pero almacenan datos
espaciales en una columna especial llamada habitualmente
geometry
con clase especial (sfc
), y allí se
almacena la información geográfica: el CRS, coordenadas y el tipo de
objeto geométrico. La clase sfc tiene siete subclases para denotar los
distintos tipos de objetos geométricos que se derivan del estándar
sf
.
- Por ejemplo, para seleccionar geometrías usamos la función
filter()
:
#- elegimos 4 CC.AA
zz_prov_chosen <- c("Andalucía", "Galicia", "Aragón", "Canarias", "Illes Balears")
zz_prov_1 <- Provincias %>% filter(ine_ccaa.n %in% zz_prov_chosen)
zz_prov_2 <- Provincias %>% filter(!(ine_ccaa.n %in% zz_prov_chosen))
p1 <- ggplot(zz_prov_1, aes(fill = ine_prov) ) +
geom_sf() +
scale_fill_viridis_d(guide = "none") +
labs(title = "Coord. geográficas")
p2 <- ggplot(zz_prov_2, aes(fill = ine_prov) ) +
geom_sf() +
scale_fill_viridis_d(guide = "none") +
labs(title = "Coord. geográficas")
p1 + p2
- Por ejemplo, para agrupar geometrías, en este caso las provincias
españolas, usamos la función
summarize()
#- creamos las geometrías de las CC.AA por agrupación de las geometrías provinciales
CCAA_2 <- Provincias %>% group_by(ine_ccaa, ine_ccaa.n) %>% summarize()
p1 <- ggplot(Provincias, aes(fill = ine_prov) ) +
geom_sf() +
scale_fill_viridis_d(guide = "none") +
labs(title = "Coord. geográficas")
p2 <- ggplot(CCAA_2, aes(fill = ine_ccaa) ) +
geom_sf() +
scale_fill_discrete(guide = "none") +
labs(title = "Coord. geográficas")
p1 + p2
Bueno, pues este es toda la teoría del tutorial. Vamos a hacer
gráficos!!
2. Haciendo gráficos
Primero vamos cargar un conjunto de datos espaciales del paquete rnaturalearth
.
Bueno, en realidad ya lo hemos cargado antes.
#- antes usaba las worlg geometies del paquete rnaturalearth, pero ...
# world <- rnaturalearth::ne_countries(scale = "medium", returnclass = "sf")
#- pero ahora prefiero utilizar las geometrias del paquete tmap,
#- las guarde en noviembre de 2021. Se cargaban así:
library(tmap)
data(World) #- hacemos accesibles 1 df
world <- World ; rm(World)
Veamos que hay en world
class(world) #- es un sf, pero tb es un data.frame
#> [1] "sf" "data.frame"
names(world) #- fijate que la ultima columna se llama geometry y almacena los datos espaciales
#> [1] "iso_a3" "name" "sovereignt" "continent" "area"
#> [6] "pop_est" "pop_est_dens" "economy" "income_grp" "gdp_cap_est"
#> [11] "life_exp" "well_being" "footprint" "inequality" "HPI"
#> [16] "geometry"
Los gráficos los vamos a hacer, de momento, con ggplot2
.
Es tan fácil como usar geom_sf()
. Las siguientes 3
expresiones producen el mismo gráfico:
ggplot(data = world) + geom_sf()
ggplot(data = world, aes(geometry = geometry)) + geom_sf()
ggplot() + geom_sf(data = world, aes(geometry = geometry))
Como el mapa que hemos hecho, en realidad es un gráfico ggplot, pues
ya sabemos cambiar/tunear algunas de las características del gráfico.
Por ejemplo:
ggplot(data = world) + geom_sf(color = "black", fill = "lightgreen", lwd = 0.2)
Podemos poner títulos y leyendas:
p <- ggplot(data = world) + geom_sf() +
labs(title = "Gráfico 1: Mapa del mundo",
caption = "Datos provenientes de tmap")
p
Podemos hacer mapas de COROPLETAS: las distintas
regiones se colorean de forma que muestren los valores de una variable
estadística. Por ejemplo, una coropleta con la variable
población(pob_est)
p + geom_sf(aes(fill = pop_est))
p + geom_sf(aes(fill = pop_est)) + scale_fill_distiller()
p + geom_sf(aes(fill = pop_est)) + scale_fill_viridis_c(option = "plasma", trans = "sqrt")
Proyecciones
Para cambiar la proyección de un objeto “sf” a otro CRS usamos la
función st_transform()
, o alternativamente podemos
especificar directamente el CRS en el gráfico con
coord_sf()
:
#- world2 <- st_transform(world, "+proj=laea +y_0=0 +lon_0=155 +lat_0=-90 +ellps=WGS84 +no_defs")
p + coord_sf(crs = "+proj=laea +y_0=0 +lon_0=155 +lat_0=-90 +ellps=WGS84 +no_defs") +
labs(subtitle = "Lambert Azimuthal Equal Area projection")
Vamos a jugar un poco con el CRS con la función
coord_sf()
. Hay que saber que ggplot usará el CRS de la
primera capa que contenga uno. Si no hubiese ningún CRS se usa
WGS84 (latitude/longitude, the reference system used in
GPS).
Se puede cambiar el CRS de varias maneras:
usando un
**string PROJ** válido. Por ejemplo este string
“+proj=laea
+lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000 +ellps=GRS80 +units=m
+no_defs”` es el European-centric ETRS89 Lambert Azimuthal Equal-Area
projection)
usando un código SRID (Spatial Reference System
Identifier)
Usando un código EPGS (European Petroleum Survey
Group)
Los siguientes tres gráficos hacen lo mismo. Representan los datos
del spatial.data.frame world
usando the ETRS89 Lambert
Azimuthal Equal-Area projection, cuyo código EPSG es 3035. Aquí
tienes algunos códigos de proyecciones:
p + coord_sf(crs = "+proj=laea +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000 +ellps=GRS80 +units=m +no_defs ") #- usamos PROJ string
p + coord_sf(crs = st_crs(3035)) #- usamos SRID identifier
p + coord_sf(crs = "+init=epsg:3035") #- usamos EPSG code (mejor no usarlo xq GDAL ha deprecated esta sintaxis)
p + coord_sf(crs = st_crs(3035)) + #- usamos SRID identifier
labs(subtitle = "(European-centric ETRS89 \nLambert Azimuthal Equal-Area projection)")
Zoom
Volvemos a la proyección nativa del conjunto de datos
world
y hacemos un zoom en nuestro mapa con
coord_sf()
. El zoom lo hacemos pare centrarnos en España y
alrededores. Para hacer el zoom en realidad ajustamos los límites de los
ejes del gráfico
p_esp <- p + coord_sf(xlim = c(-15.00, 15.00), ylim = c(21, 47.44), expand = TRUE)
p_esp
Con el paquete ggspatial
podemos poner elementos de
mapas como una Scale bar con annotation_scale()
y
una North arrow con annotation_north_arrow()
:
p_esp +
ggspatial::annotation_scale(location = "bl", width_hint = 0.5) +
ggspatial::annotation_north_arrow(location = "bl", which_north = "true",
pad_x = unit(0.75, "in"), pad_y = unit(0.5, "in"),
style = north_arrow_fancy_orienteering)
texto en el mapa
El mapa se ha hecho con ggplot2
, así que podemos añadir,
por ejemplo, texto con geom_text()
; pero antes de ello,
vamos a crear/calcular centroides de cada país para ver donde situamos
el texto en el mapa.
world_points <- st_centroid(world, of_largest_polygon = TRUE) #- sustituye la geometry por el centroide
De esta forma, con st_centroid()
podemos calcular los
centroides de cada geometría, de cada país, pero Los datos del centroide
(longitud, latitud) se almacenan en la columna geometry
; es
decir sustituyen o eliminan las geometrías anteriores que eran los
límites o fronteras de los países.
Si queremos que las geometrías originales y los centroides estén en
un mismo data.frame, hemos de hacer:
world_points <- cbind(world, st_coordinates(st_centroid(world$geometry, of_largest_polygon = TRUE))) #- ahora el centroide pasa a estar en 2 columnas, llamadas X e Y, donde figuran la longitud y latitud del centroide
Lo que hacemos es fusionar los los dos data.frame’s, y el
centroide pasará a estar en dos nuevas columnas llamadas X e Y, sin
sustituir las geometrías de los países.
Para ver esas dos nuevas columnas (X e Y), vamos a hacer un “pequeño
truco”, vamos a quitar la columna de geometrías. Esto facilita el
visionado de los datos, pero lógicamente cuando vayamos a hacer el
gráfico, realmente no podremos eliminar la geometry porque si no, los
datos dejarían de ser datos espaciales.
#- truco para ver mejor los datos espaciales: quitar la geometría
names(world_points)
#> [1] "iso_a3" "name" "sovereignt" "continent" "area"
#> [6] "pop_est" "pop_est_dens" "economy" "income_grp" "gdp_cap_est"
#> [11] "life_exp" "well_being" "footprint" "inequality" "HPI"
#> [16] "X" "Y" "geometry"
zz <- world_points %>% select(name, iso_a3, X, Y) %>%
st_set_geometry(NULL) #- por si quiero ver mejor los datos del df
Hacemos el mapa con los nombres de los países con
geom_text()
y el nombre del Mar Mediterráneo con
annotate()
p_esp +
geom_text(data = world_points,
aes(x = X, y = Y, label = name),
color = "darkblue", fontface = "bold", check_overlap = TRUE, size = 2.5) +
annotate(geom = "text", x = 5.5, y = 38,
label = "Mar Mediterráneo", fontface = "italic", color = "grey22", size = 4)
Tuneando del mapa
Vamos a tunear el mapa. Por ejemplo, vamos a poner el mar (en
realidad al background
del theme
) del color
aliceblue
, y a los continentes les pondremos un color
terroso con fill = "antiquewhite"
. Arreglamos un poco las
grid-lines dentro del theme
, y le ponemos la estrellita. No
nos va a salir bonito-bonito porque los nombres de los países se van a
superponer. Podríamos quitar nombres, pero otro día:
p <- p +
geom_sf(fill = "antiquewhite") +
geom_text(data = world_points,
aes(x = X, y = Y, label = name),
color = "darkblue", fontface = "bold",
check_overlap = TRUE, size = 2) +
annotation_north_arrow(location = "bl", which_north = "true",
pad_x = unit(0.75, "in"),
pad_y = unit(0.5, "in"),
style = north_arrow_fancy_orienteering) +
theme(panel.grid.major = element_line(color = gray(.5), linetype = "dashed", size = 0.5),
panel.background = element_rect(fill = "aliceblue"))
p
Hacemos otra vez un zoom con sf::coord_sf()
p + coord_sf(xlim = c(-15.00, 15.00), ylim = c(21, 47.44), expand = TRUE) +
ggspatial::annotation_scale(location = "bl", width_hint = 0.5)
Como nos ha quedado medio chulo, si quisiéramos guardar el mapa
haríamos:
ggsave("map.pdf")
ggsave("map_web.png", width = 6, height = 6, dpi = "screen")
3. Mas tuneado
Vamos a ir modificando el mapa, no necesariamente para hacerlo más
bonito, sino para ver como se pueden hacer mapas con
ggplot2
:
añadiendo puntos
Primero quiero añadir al mapa donde está mi pueblo y donde está
Valencia, vamos que voy a añadir puntos al mapa.
Para ello, primero necesito tener la localización de estos puntos en
el mapa, así que me fui a OSM,
OpenStreetMap y geolocalicé los dos municipios y con esta
información, voy a crear un data.frame con la long/lat de mi pueblo y de
Valencia:
pueblos <- data.frame(
nombre = c("Pancrudo", "Valencia"),
longitude = c(-1.028781, -0.3756572),
latitude = c(40.76175, 39.47534) )
pueblos
Pancrudo |
-1.0287810 |
40.76175 |
Valencia |
-0.3756572 |
39.47534 |
- lo mas fácil para añadir puntos es usar
geom_point()
p_esp +
geom_point(data = pueblos,
aes(x = longitude, y = latitude),
size = 2, color = "darkred") +
geom_text(data = pueblos,
aes(x = longitude, y = latitude, label = nombre),
color = "darkblue", fontface = "bold", check_overlap = TRUE, size = 2.5)
Utilizar geom_point()
es fácil y funciona, pero es más
flexible convertir el data.frame a un objeto sf
. Esto lo
podemos hacer con la función st_as_sf()
, definiendo la
proyección (aquí WGS84, que es el código CRS # 4326):
pueblos_sf <- st_as_sf(pueblos,
coords = c("longitude", "latitude"),
crs = 4326, #- EPSG:4326 Coordenadas Geográficas WGS84
agr = "constant")
Para después añadirlo como una capa más con geom_sf()
a
nuestro gráfico:
p_esp + geom_sf(data = pueblos_sf, size = 2, color = "darkred")
Hagamos zoom con coord_sf
p_esp + geom_sf(data = pueblos_sf, size = 2, color = "darkred") +
coord_sf(xlim = c(-15.00, 15.00), ylim = c(21, 47.44), expand = TRUE)
Añadiendo polígonos
La verdad es que se hace igual que con los puntos, hay que usar la
función geom_sf()
y a correr.
Vamos a añadir los contornos/polígonos de las CC.AA:
zz_CCAA <- CCAA %>% st_set_geometry(NULL)
p + geom_sf(data = CCAA, fill = NA) +
coord_sf(xlim = c(-15.00, 15.00), ylim = c(21, 47.44), expand = TRUE)
Ahora añadimos las provincias de Andalucía, PERO coloreadas según su
población media por municipio. Para ello:
- primero cargamos datos de población
INE_padron_muni_96_20 <- pjpv.datos.01::pob_muni_1996_2020
#INE_padron_muni_96_20 <- pjpv2020.01::pjp_data_pob_mun_1996_2019
pob_andalucia <- INE_padron_muni_96_20 %>%
filter(year == 2020) %>%
filter(ine_ccaa.n == "Andalucía") %>%
group_by(ine_prov.n) %>% mutate(n_pueblos = n_distinct(ine_muni.n)) %>%
mutate(pob_prov = sum(pob_total, na.rm = TRUE)) %>% ungroup() %>%
mutate(pob_media_pueblos = pob_prov/n_pueblos) %>%
select(ine_prov.n, ine_prov, pob_media_pueblos, pob_prov, n_pueblos) %>%
distinct() %>% ungroup()
- después cargamos lindes provinciales (en realidad ya los
tenemos)
# Provincias <- Provincias
zz_Provincias <- Provincias %>% st_set_geometry(NULL)
- fusionamos los datos de población y los lines provinciales:
geo_pob_andalucia <- left_join(pob_andalucia, Provincias, by = c("ine_prov" = "ine_prov"))
geo_pob_andalucia <- left_join(pob_andalucia, Provincias)
- finalmente añadimos las provincias coloreadas por población al
mapa:
p + geom_sf(data = CCAA, fill = NA) +
geom_sf(data = geo_pob_andalucia, aes(geometry = geometry, fill = n_pueblos)) +
scale_fill_viridis_c(alpha = .4) +
geom_sf(data = pueblos_sf, size = 2, color = "darkred") +
coord_sf(xlim = c(-15.00, 25.00), ylim = c(21, 57.44), expand = TRUE)
- vamos añadir también 3 municipios españoles seleccionados al
azar:
pueblos_3 <- IGN_nomencla_muni %>%
sample_n(3) %>%
select(NOMBRE_ACTUAL, PROVINCIA, LONGITUD_ETRS89, LATITUD_ETRS89) %>%
st_as_sf(coords = c("LONGITUD_ETRS89", "LATITUD_ETRS89"), crs = 4326, agr = "constant", remove = FALSE)
- Bueno, en realidad no los voy a elegir tan tan al azar:
pueblos_3 <- IGN_nomencla_muni %>%
filter(NOMBRE_ACTUAL %in% c("Pancrudo", "Alburquerque", "Polentinos")) %>%
select(NOMBRE_ACTUAL, PROVINCIA, LONGITUD_ETRS89, LATITUD_ETRS89) %>%
st_as_sf(coords = c("LONGITUD_ETRS89", "LATITUD_ETRS89"), crs = 4326, agr = "constant", remove = FALSE)
p_esp <- p + geom_sf(data = CCAA, fill = NA) +
geom_sf(data = geo_pob_andalucia, aes(geometry = geometry, fill = n_pueblos)) +
scale_fill_viridis_c(alpha = .4) +
geom_sf(data = pueblos_3, size = 2, color = "darkred") +
ggrepel::geom_label_repel(data = pueblos_3, aes(x = LONGITUD_ETRS89, y = LATITUD_ETRS89, label = NOMBRE_ACTUAL)) +
coord_sf(xlim = c(-10.00, 7.00), ylim = c(35, 45), expand = TRUE)
p_esp
Con los mapas y gráficos nunca se acaba!!!
Canarias no se ve. ¿La situamos en el mapa? Usaremos
sf::st_bbox()
canarias <- Provincias %>% filter(ine_prov %in% c(35,38))
peninsula <- Provincias %>% filter( !(ine_prov %in% c(35, 38)) )
my_shift <- st_bbox(peninsula)[c(1,2)]- (st_bbox(canarias)[c(1,2)]) + c(-2.4, -1.1)
canarias$geometry <- canarias$geometry + my_shift
st_crs(canarias) <- st_crs(peninsula)
peninsula_y_canarias <- rbind(peninsula, canarias)
p1 <- ggplot() + geom_sf(data = peninsula_y_canarias)
p2 <- ggplot() + geom_sf(data = peninsula) + geom_sf(data = canarias, fill = "purple")
#coord_sf(xlim = c(-12.00, 4.00), ylim = c(33, 44), expand = TRUE)
p1 + p2
ggplot() + geom_sf(data = peninsula) +
geom_sf(data = canarias) +
coord_sf(xlim = c(-12.00, 4.00), ylim = c(33, 44), expand = TRUE)
- pongamos Canarias, pintada en rojo, en nuestro mapa
p_esp + geom_sf(data = canarias, fill = "red") +
coord_sf(xlim = c(-12.00, 5.00), ylim = c(30, 44), expand = TRUE)
el río Ebro
Pues que quiero poner el Río Ebro en el mapa. Logicamente necesio los
datos geográficos del rio Ebro.
#rios <- rio::import("https://github.com/perezp44/archivos_download/blob/master/rio_ebro.rds?raw=true")
zz_rios <- rios_espanya %>% st_set_geometry(NULL)
names(rios_espanya)
#> [1] "Cod_Mar" "PfafCuen" "PfafRio" "Cod_Uni" "Nom_Rio"
#> [6] "LngTramo_m" "Long_Rio_m" "color" "geometry"
- Voy a situar en el mapa 4 ríos:
#- no hace falta pero
cod_rios_guenos <- c(913757, 913687, 913685, 913619)
nombre_rios_guenos <- c("Pancrudo", "Jiloca", "Jalón", "Ebro")
rios_guenos <- rios_espanya %>% filter(Cod_Uni %in% cod_rios_guenos)
ggplot(data = peninsula) + geom_sf() +
geom_sf(data = rios_guenos, color = "lightblue", lwd = 1.3) +
labs(subtitle = "Mapa de España con 4 ríos Güenos", color = "")
# col = sf::sf.colors(2, alpha = 0.5)
- ¿Cómo se llamará el río pequeñín?
ggplot(data = peninsula) + geom_sf() +
geom_sf(data = rios_guenos, color = "lightblue", lwd = 1.3) +
geom_point(data = pueblos, aes(x = longitude, y = latitude), size = 2, color = "darkred") +
geom_text(data = pueblos, aes(x = longitude, y = latitude, label = nombre), color = "darkblue", fontface = "bold", check_overlap = TRUE, size = 2.5) +
labs(subtitle = "Mapa de España con 4 ríos Güenos", color = "")
4. Layouts
Con los mapas se pueden hacer composiciones para destacar el mensaje
que se quiere mostrar. Se pueden hacer estas composiciones de varias
maneras:
Using “grobs”, i.e. graphic objects from ggplot2, which can be
inserted in the plot region using plot coordinates;
Usando el paquete patchwork
o el paquete
cowplot
names(world)
#> [1] "iso_a3" "name" "sovereignt" "continent" "area"
#> [6] "pop_est" "pop_est_dens" "economy" "income_grp" "gdp_cap_est"
#> [11] "life_exp" "well_being" "footprint" "inequality" "HPI"
#> [16] "geometry"
gworld <- ggplot(data = world) +
geom_sf(aes(fill = income_grp)) +
# geom_sf(aes(fill = region_wb)) +
geom_rect(xmin = -15, xmax = 25.00, ymin = 21,
ymax = 57.44, fill = NA, colour = "black", size = 1.5) +
theme(panel.background = element_rect(fill = "azure")) +
labs(subtitle = "Mapa del mundo") +
scale_fill_discrete(guide = "none")
gworld
g_europe <- ggplot(data = world) +
geom_sf() +
geom_sf(data = CCAA, fill = NA) +
geom_sf(data = geo_pob_andalucia, aes(geometry = geometry, fill = n_pueblos)) +
geom_sf(data = pueblos_3, size = 2, color = "darkred") +
geom_label_repel(data = pueblos_3, aes(x = LONGITUD_ETRS89, y = LATITUD_ETRS89, label = NOMBRE_ACTUAL)) +
coord_sf(xlim = c(-15.00, 25.00), ylim = c(21, 57.44), expand = TRUE) +
theme(panel.background = element_rect(fill = "azure")) +
labs(subtitle = "Mapa de Europa Occidental") +
scale_fill_continuous(guide = "none")
g_europe
library(patchwork)
gworld + g_europe + plot_layout(widths = c(1, 2))
5. Paquete Mapview
Vamos a ver lo fácil que es hacer un mapa con el paquete mapview
.
Por ejemplo, teníamos geolocalizados a Pancrudo y Valencia. Veámoslos
en un gráfico con mapview()
pueblos <- data.frame(nombre = c("Pancrudo", "Valencia"), longitude = c(-1.028781, -0.3756572), latitude = c(40.76175, 39.47534)) #- ETRS89
pueblos_sf <- st_as_sf(pueblos, coords = c("longitude", "latitude"), crs = 4326, agr = "constant") #- EPSG:4326 Coordenadas Geográficas WGS84
mapview::mapview(pueblos_sf)
Otro ejemplo con las geometrías de las CC.AA, aunque es una afrenta
para Pancrudo
mapview::mapview(rios)
Para ver que se puede hacer con mapview
vamos a añadir
alguna información a CCAA, por ejemplo el número de provincias, el
número de municipios y la población total en el año 2017:
INE_padron_muni_96_20 <- pjpv.datos.01::pob_muni_1996_2020
#INE_padron_muni_96_20 <- pjpv2020.01::pjp_data_pob_mun_1996_2019
info_CCAAs <- INE_padron_muni_96_20 %>%
filter(year == 2017) %>%
#filter(poblacion == "Total") %>%
group_by(ine_ccaa.n) %>%
mutate(n_prov = n_distinct(ine_prov)) %>%
mutate(n_pueblos = n_distinct(ine_muni)) %>%
mutate(pob_CCAA = sum(pob_total, na.rm = TRUE)) %>% ungroup() %>%
select(ine_ccaa, ine_ccaa.n, n_prov, n_pueblos, pob_CCAA) %>%
distinct() %>% ungroup()
Juntamos el data.frame info_CCAAs
con el dataframe con
las geometrías `CCAA
:
CCAA_con_info <- left_join(CCAA, info_CCAAs)
Y vamos a graficarlo ya con mapview
:
mapview::mapview(CCAA_con_info,
zcol = c("ine_ccaa.n", "n_prov", "n_pueblos", "pob_CCAA"),
color = "tomato", col.regions = "purple", lwd = 3,
cex = "n_pueblos")
mapview::mapview(CCAA_con_info,
zcol = c("ine_ccaa.n", "n_prov", "n_pueblos", "pob_CCAA"),
cex = "n_pueblos")
Se pueden poner varias capas:
mapview(list(CCAA_con_info, pueblos_sf), layer.name = c("CC.AA", "2 municipios chachis"))
O mejor así:
mapview::mapview(CCAA_con_info, zcol = c("ine_ccaa.n", "n_prov", "n_pueblos", "pob_CCAA")) +
mapview::mapview(pueblos_sf)
Y muchas mas cosas, por ejemplo Map
pop-ups o Image
pop-ups
- se puede usar el operador
%>%
con las funciones de
sf
CCAA_con_info %>% st_union %>% mapview
Integración con el paquete mapedit
Integracion con mapedit
#install.packages("mapedit")
library(mapedit)
my_map <- mapview(pueblos_sf)
drawFeatures(my_map, editor = "leafpm") #- good
Para recuperar el mapa es mejor así:
library(mapedit)
mys_dibujitos <- editMap(mapview(pueblos_sf))
mapview(mys_dibujitos$finished)
6. Paquete leaflet
El paquete leaflet
permite utilizar en R una de la librerías de JavaScript más conocidas:
Leaflet.
Para abrir un mapa leaflet:
library(leaflet)
leaflet() %>% addTiles() %>% leafem::addMouseCoordinates()
Podemos centrar el mapa, decidir el nivel de zoom y ponerle
marcadores y popups:
m <- leaflet() %>%
addTiles() %>%
setView(lng = -1.0287810, lat = 40.76175, zoom = 6) %>%
addMarkers(lng = -1.0287810, lat = 40.76175, popup = "Pancrudo") %>%
addPopups(lng = -0.3756572, lat = 39.47534, popup = "Valencia")
m
Vamos a añadirle elementos, por ejemplo puntos, por ejemplo mis 2
municipios:
leaflet() %>%
addTiles() %>% # Add default OpenStreetMap map tiles
addCircleMarkers(data = pueblos_sf)
#leafem::addHomeButton(ext = extent(breweries91), layer.name = "pueblos")
O, por ejemplo polígonos como las CC.AA:
leaflet(data = CCAA) %>% addTiles() %>%
addPolygons(fillColor = topo.colors(18, alpha = NULL), stroke = FALSE)
Si te ha gustado tu mapa leaflet y quieres guardarlo, puedes
guardarlo como un archivo html así:
htmltools::save_html(lplot, "leaflet.html")
Se pueden hacer bastantes más cosas, por ejemplo, para hacer
coropletas con leaflet puedes ir aquí. Otro
ejemplo de análisis con leaflet aquí.
7. Operaciones geométricas
Las operaciones geométricas que podemos hacer con sf
con
datos espaciales vectoriales se pueden dividir en tres grupos:
funciones que devuelven un valor que resume alguna propiedad de
la geometría:
st_area()
: calcula el area
st_lenght()
: calcula el perímetro
st_distance()
: calcula la distancia (entre dos
geometrías)
funciones que chequean si se cumple una condición:
En 1 geometría: st_is_valid()
: chequea si la
geometria es válida
En 2 geometrías: por ejemplo, st_intersects(A, B)
chequea si dos geometrías se intersectan. Otras funciones:
st_disjoint()
, st_touches()
,
st_crosses()
, st_within()
,
st_contains()
, st_overlaps()
,
st_covers()
, st_equals()
,
st_is_within_distance()
, y alguna más
funciones que crean una nueva geometría:
En 1 geométria: por ejemplo, st_centroid()
: calcula
el centroide. Otras funciones: st_buffer()
,
st_boundary()
, st_convex_hull()
,
st_voronoi()
En 2 geométrias: por ejemplo, st_intersection()
calcula la interseccion. Otras funciones: st_combine()
,
st_union()
, st_difference()
7.1 Operaciones geométricas (Ejemplos)
Las 5 provincias más grandes
Vamos a colorear las 5 provincias españolas con más territorio, para
ello tenemos que calcular el área con sf::st_area()
:
Provicias_5 <- Provincias %>%
mutate(area = st_area(.)) %>%
mutate(areakm2 = units::set_units(area, km^2)) %>%
arrange(desc(areakm2)) %>%
slice(1:5)
ggplot() + geom_sf(data = Provincias) + geom_sf(data = Provicias_5, fill = "purple")
Bien, pero mejor si ponemos el nombre de las provincias
Prov_5_centroides <- st_centroid(Provicias_5)
Prov_5_centroides <- cbind(Prov_5_centroides, st_coordinates(st_centroid(Prov_5_centroides$geometry)))
ggplot() + geom_sf(data = Provincias) +
geom_sf(data = Provicias_5, fill = "purple") +
geom_text(data = Prov_5_centroides,
aes(x = X, y = Y, label = ine_prov.n),
color = "darkblue", fontface = "bold", check_overlap = TRUE, size = 2.5) +
labs(title = "Las 5 provincias españolas más extensas", x = "", y = "")
Distancias (a mi pueblo)
Calculemos las distancias entre puntos, por ejemplo entre los
centroides de las 52 provincias españolas:
Prov_centroides <- st_centroid(Provincias)
Prov_centroides_XY <- cbind(Prov_centroides, st_coordinates(st_centroid(Prov_centroides$geometry)))
distancias <- st_distance(Prov_centroides_XY, by_element = FALSE) #- devuelve una matriz
Quiero ver que municipio está mas lejos de mi pueblo. Cargo datos de
los municipios con latitud-longitud en ETRS89
:
municipios <- IGN_nomencla_muni
municipios <- municipios %>% select(COD_INE, PROVINCIA, NOMBRE_ACTUAL, LONGITUD_ETRS89, LATITUD_ETRS89)
Tengo que convertir el df a un spatial-df, y tengo que tener cuidado
con el CRS que le pongo. Aquí vi que
ETRS89 es equivalente a EPSG Projection 4258
, así que,
espero haberlo hecho bien, ya vorem:
municipios_sf <- st_as_sf(municipios, coords = c("LONGITUD_ETRS89", "LATITUD_ETRS89"), crs = st_crs(4258))
Vamos a ver que fila ocupa mi pueblo. Ocupa la fila nº 6694, así que
voy a crear un df sólo con mi pueblo y luego calcular las distancias con
fs::st_distance()
:
pancrudo_sf <- municipios_sf %>% slice(6694)
g1 <- st_geometry(municipios_sf)
g1_pancrudo <- st_geometry(pancrudo_sf) #- dejo solo la geometria
#- distancias_a_Pancrudo <- mapply(st_distance, g1, g1_pancrudo) %>% as.vector) #- con R-base
distancias_a_Pancrudo <- map2(g1, g1_pancrudo, st_distance) %>% as_vector() #- con purrr
distancias_a_Pancrudo <- as.data.frame(distancias_a_Pancrudo)
df_con_distancias_a_Pancrudo <- bind_cols(municipios_sf, distancias_a_Pancrudo) %>%
mutate(Kms = distancias_a_Pancrudo * 100) %>%
arrange(Kms)
El pueblo más cercano a Pancrudo es Pozuel del Campo que está a
3.3507456, eso si, en linea recta. Por la carretera hay un poco más,
según Google Maps 7,1 kilómetros. A ver si podemos calcularlo luego con
R usando OSM.
El pueblo más lejano a Pancrudo, si consideramos sólo la península es
Muxía en la provincia de A Coruña, que está a 851,65 kilómetros (en
linea recta). En coche, según Google Maps, 924 Kms.
Municipios por los que pasa el río Ebro
Pues eso, que me apetece saber por donde pasa el Ebro y el
Pancrudo.
- Primero cargo geometrías de los municipios españoles. Las lindes
municipales provienen del paquete
LAU2boundaries4spain
muni_xx <- municipios_2020 %>% st_set_geometry(NULL)
muni <- municipios_2020
Vamos a quedarnos solo con la España peninsular:
#zz <- muni_xx %>% group_by(ine_ccaa, ine_ccaa.n) %>% count()
muni_borrar <- c("Illes Balears", "Canarias")
muni <- muni %>% filter(!(ine_ccaa.n %in% muni_borrar))
- Ahora los datos de ríos Pancrudo, Jiloca, Jalón y Ebro
rio_ebro_4 <- read_rds(here::here("datos", "mapas", "Rios", "rio_ebro.rds"))
Vamos a poner todos los tramos de los 4 ríos juntos. Con
summarise()
rio_ebro_4_xx <- rio_ebro_4 %>% st_set_geometry(NULL)
rio_ebro_4_x <- rio_ebro_4 %>% select(Nom_Rio, Long_Rio_m, geometry) %>% group_by(Nom_Rio) %>% summarize()
Hagamos ya un mapa de los lindes municipales, los ríos y voy a situar
Pancrudo y Valencia:
theme_set(cowplot::theme_map())
p <- ggplot(muni) +
geom_sf(color = "grey", fill = "white", lwd = 0.01) +
geom_sf(data = rio_ebro_4_x, color = "lightblue", lwd = 1.6) +
geom_point(data = pueblos, aes(x = longitude, y = latitude), size = 3, color = "darkred") +
geom_text(data = pueblos, aes(x = longitude, y = latitude, label = nombre), color = "darkred",
fontface = "bold", check_overlap = TRUE, size = 2.5) +
labs(title = "R-Mapa con 4 ríos güenos",
caption = "Datos provenientes del paquete LAU2boundaries4spain") +
theme(panel.background = element_rect(fill = "aliceblue"))
p
Venga, calculamos las intersecciones con
sf::st_intersects()
pero antes tenemos que poner los 2 dfs
en el mismo CRS:
muni_a <- muni %>% st_transform(crs = st_crs(rio_ebro_4_x))
zz_a <- st_intersects(muni_a, rio_ebro_4_x, sparse = FALSE, prepared = TRUE)
Ahora ya si seleccionamos aquellos municipios por los que pasa el
Ebro. Lo hago con R-base:
muni_ebro <- muni_a[zz_a,]
Parece que hay 190 municipios por loq eu pasa el Ebro. A ver si nos
sale el mapa
p + geom_sf(data = muni_ebro) +
geom_sf(data = rio_ebro_4_x, color = "lightblue", lwd = 0.25)
No, no voy a obtener los municipios por los que pasa el rio Pancrudo.
Eso está MAL!!!!
LS0tCnRpdGxlOiAiSW50cm9kdWNjacOzbiBhIEdJUyBjb24gUiIKYXV0aG9yOiAiUGVkcm8gSi4gUMOpcmV6IChwZWRyby5qLnBlcmV6QHV2LmVzKS4gVW5pdmVyc2l0YXQgZGUgVmFsw6huY2lhIDxicj4gPGJyPiBXZWIgZGVsIGN1cnNvOiA8aHR0cHM6Ly9wZXJlenA0NC5naXRodWIuaW8vaW50cm8tZHMtMjItMjMtd2ViLz4iCmRhdGU6ICJPY3R1YnJlIGRlIDIwMTkgKGFjdHVhbGl6YWRvIGVsIGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQtJW0tJVknKWApIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNzczogIWV4cHIgaGVyZTo6aGVyZSgiYXNzZXRzIiwgInN0eWxlc19wanAuY3NzIikKICAgIHRoZW1lOiBwYXBlcgogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB0cnVlCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICBpbmNsdWRlczoKICAgICAgYWZ0ZXJfYm9keTogIWV4cHIgaGVyZTo6aGVyZSgiYXNzZXRzIiwgImZvb3Rlci5odG1sIikgCiAgICAgIGluX2hlYWRlcjogCiAgICAgICAgLSAhZXhwciBoZXJlOjpoZXJlKCJhc3NldHMiLCAiZ29vZ2xlLWFuYWx5dGljcy5odG1sIikgCiAgICAgICAgLSAhZXhwciBoZXJlOjpoZXJlKCJhc3NldHMiLCAiZmF2aWNvbi1zb2wuaHRtbCIpCiAgICBkZl9wcmludDoga2FibGUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUKLS0tCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpgYGB7ciBjaHVuay1zZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGV2YWwgPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICNyZXN1bHRzID0gImhvbGQiLAogICAgICAgICAgICAgICAgICAgICAgY2FjaGUgPSBGQUxTRSwgY2FjaGUucGF0aCA9ICIvY2FjaGVzLyIsIGNvbW1lbnQgPSAiIz4iLAogICAgICAgICAgICAgICAgICAgICAgI2ZpZy53aWR0aCA9IDcsICNmaWcuaGVpZ2h0PSA3LAogICAgICAgICAgICAgICAgICAgICAgI291dC53aWR0aCA9IDcsIG91dC5oZWlnaHQgPSA3LAogICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSBUUlVFLCAgZmlnLnNob3cgPSAiaG9sZCIsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYXNwID0gMC42MjgsIG91dC53aWR0aCA9ICI3NSUiLCBmaWcuYWxpZ24gPSAiY2VudGVyIikKCiMtIHBhcmEgbWVqb3JhciBsb3MgZ3LDoWZpY29zLCBidWVubyBlbiByZWFsaWRhZCBwYXJhIHF1ZSBzZSB2ZWFuIGlndWFsIGVuIGRpc3RpbnRvcyBTTwojLSBodHRwczovL3d3dy5qdW1waW5ncml2ZXJzLmNvbS9ibG9nL3Ita25pdHItbWFya2Rvd24tcG5nLXBkZi1ncmFwaGljcy8Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGRldiA9ICJwbmciLCBkZXYuYXJncyA9IGxpc3QodHlwZSA9ICJjYWlyby1wbmciKSkKYGBgCgpgYGB7ciBvcHRpb25zLXNldHVwLCBpbmNsdWRlID0gRkFMU0V9Cm9wdGlvbnMoc2NpcGVuID0gOTk5KSAjLSBwYXJhIHF1aXRhciBsYSBub3RhY2nDs24gY2llbnTDrWZpY2EKb3B0aW9ucygieWFtbC5ldmFsLmV4cHIiID0gVFJVRSkgIy0gaHR0cHM6Ly9naXRodWIuY29tL3Zpa2luZy9yLXlhbWwvaXNzdWVzLzQ3ICAobG8gcHVzZSB4IGVsIHBiIGNvbiBlbCB3YXJuaW5nKSBFbiByZWFsaWRhZCBjcmVvIHF1ZSBtZWpvciBzZXLDrWEgcG9uZXJsbyBlbiBSUHJvZmlsZQpgYGAKCgpgYGB7ciBrbGlwcHksIGVjaG8gPSBGQUxTRX0Ka2xpcHB5OjprbGlwcHkocG9zaXRpb24gPSBjKCJ0b3AiLCAicmlnaHQiKSkgIy0gcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoInJsZXN1ci9rbGlwcHkiKQpgYGAKCmBgYHtyIHNldHVwLCBlY2hvID0gRkFMU0V9CmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoaGVyZSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoc2YpCmxpYnJhcnkocm5hdHVyYWxlYXJ0aCkKbGlicmFyeShybmF0dXJhbGVhcnRoZGF0YSkKbGlicmFyeShnZ3NwYXRpYWwpCiNsaWJyYXJ5KHNwYW5pc2hScG9ibGFjaW9uKSAgICAgIy0gcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoInBlcmV6cDQ0L3NwYW5pc2hScG9ibGFjaW9uIikKI2xpYnJhcnkoc3BhbmlzaFJlbnRpZGFkZXNJR04pICAgIy0gZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJwZXJlenA0NC9zcGFuaXNoUmVudGlkYWRlc0lHTiIpCmxpYnJhcnkobWFwdmlldykKbGlicmFyeShsZWFmZW0pCmxpYnJhcnkobGVhZmxldCkKYGBgCgotLS0tLS0tLS0tLS0tCgo8YnI+CgoKYGBge3IgcGFyYV9jYXNhXzIwMjEsIGV2YWwgPSBGQUxTRSwgZWNobyA9IEZBTFNFfQojLSBBUVVJIGNyZW8gZWwgZmljaGVybyBjb24gUkRhdGEgY29uIGxvcyBkYXRvcyBxdWUgbHVlZ28gdXNvCiMgQ0NBQSA8LSByaW86OmltcG9ydCgiL2hvbWUvcGpwdi9Fc2NyaXRvcmlvL215X1JSXzIwMjEvbXlfZGF0b3NfMjAyMS9kYXRvcy9nZW9fZGF0b3NfbWlvcy9nZW9fY2NhYV8yMDIwX0xBVTIucmRzIikKIyBQcm92aW5jaWFzIDwtIHJpbzo6aW1wb3J0KCIvaG9tZS9wanB2L0VzY3JpdG9yaW8vbXlfUlJfMjAyMS9teV9kYXRvc18yMDIxL2RhdG9zL2dlb19kYXRvc19taW9zL2dlb19wcm92XzIwMjBfTEFVMi5yZHMiKQojIG11bmljaXBpb3NfMjAyMCA8LSByaW86OmltcG9ydCgiL2hvbWUvcGpwdi9Fc2NyaXRvcmlvL215X1JSXzIwMjEvbXlfZGF0b3NfMjAyMS9kYXRvcy9nZW9fZGF0b3NfbWlvcy9nZW9fbXVuaV8yMDIwX0xBVTIucmRzIikKIyBJR05fbm9tZW5jbGFfbXVuaSA8LSByaW86OmltcG9ydCgiL2hvbWUvcGpwdi9Fc2NyaXRvcmlvL215X1JSXzIwMjEvbXlfZGF0b3NfMjAyMS9kYXRvcy9jb2RpZ29zL0lHTl9tdW5pY2lwaW9zLnhsc3giKQojIHJpb3NfZXNwYW55YSA8LSByaW86OmltcG9ydCgiL2hvbWUvcGpwdi9Fc2NyaXRvcmlvL215X1JSXzIwMjEvbXlfZGF0b3NfMjAyMS9kYXRvcy9nZW9fZGF0b3NfbWlvcy9yaW9zX2VzcGFueWEucmRzIikKIyByaW9fZWJyb180IDwtIHJpbzo6aW1wb3J0KCIvaG9tZS9wanB2L0VzY3JpdG9yaW8vbXlfUlJfMjAyMS9teV9kYXRvc18yMDIxL2RhdG9zL2dlb19kYXRvc19taW9zL3Jpb19lYnJvXzQucmRzIikKCiMtIHVzYWJhIGVsIGRlIE5hdHVyYWwgSGVhcnQgcGVybyBkYSBwYnMsIGFzaSBxIHVzbyBlbCBkZSB0bWFwCiN3b3JsZCA8LSBybmF0dXJhbGVhcnRoOjpuZV9jb3VudHJpZXMoc2NhbGUgPSAibWVkaXVtIiwgcmV0dXJuY2xhc3MgPSAic2YiKQpsaWJyYXJ5KHRtYXApIApkYXRhKFdvcmxkKSAjLSBoYWNlbW9zIGFjY2VzaWJsZXMgMSBkZgp3b3JsZCA8LSBXb3JsZApybShXb3JsZCkKIy0gYXF1w60gbG9zIGd1YXJkbyBwYXJhIGx1ZWdvIGltcG9ydGFybG9zIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KI3NhdmUoQ0NBQSwgUHJvdmluY2lhcywgbXVuaWNpcGlvc18yMDIwLCB3b3JsZCwgSUdOX25vbWVuY2xhX211bmksIHJpb3NfZXNwYW55YSwgcmlvX2Vicm9fNCwgZmlsZSA9ICIuL2RhdG9zL21hcGFzL2dlb21ldHJpYXNfQmlnRGF0YV8yMDIxLlJEYXRhIikKCgojLSBPTEQgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KI0NDQUEgPC0gcmlvOjppbXBvcnQoImh0dHBzOi8vZ2l0aHViLmNvbS9wZXJlenA0NC9MQVUyYm91bmRhcmllczRzcGFpbi9ibG9iL21hc3Rlci9kYXRhL0NDQUEucmRhP3Jhdz10cnVlIikKI1Byb3ZpbmNpYXMgPC0gcmlvOjppbXBvcnQoImh0dHBzOi8vZ2l0aHViLmNvbS9wZXJlenA0NC9MQVUyYm91bmRhcmllczRzcGFpbi9ibG9iL21hc3Rlci9kYXRhL1Byb3ZpbmNpYXMucmRhP3Jhdz10cnVlIikKI211bmljaXBpb3NfMjAxNyA8LSByaW86OmltcG9ydCgiaHR0cHM6Ly9naXRodWIuY29tL3BlcmV6cDQ0L0xBVTJib3VuZGFyaWVzNHNwYWluL2Jsb2IvbWFzdGVyL2RhdGEvbXVuaWNpcGlvc18yMDE3LnJkYT9yYXc9dHJ1ZSIpCiMgd29ybGQgPC0gcm5hdHVyYWxlYXJ0aDo6bmVfY291bnRyaWVzKHNjYWxlID0gIm1lZGl1bSIsIHJldHVybmNsYXNzID0gInNmIikKIyByaW9zIDwtIHJpbzo6aW1wb3J0KCJodHRwczovL2dpdGh1Yi5jb20vcGVyZXpwNDQvYXJjaGl2b3NfZG93bmxvYWQvYmxvYi9tYXN0ZXIvcmlvX2Vicm8ucmRzP3Jhdz10cnVlIikKIyBJR05fbm9tZW5jbGFfbXVuaSA8LSByaW86OmltcG9ydCgiaHR0cHM6Ly9naXRodWIuY29tL3BlcmV6cDQ0L2FyY2hpdm9zX2Rvd25sb2FkL2Jsb2IvbWFzdGVyL0lHTl9ub21lbmNsYV9tdW5pLnJkcz9yYXc9dHJ1ZSIpCmBgYAoKCgoKYGBge3IgcGFyYV9jYXJnYXJfZGF0b3NfMjAyMSwgZXZhbCA9IFRSVUUsIGVjaG8gPSBGQUxTRX0KI3NhdmUoQ0NBQSwgUHJvdmluY2lhcywgbXVuaWNpcGlvc18yMDIwLCB3b3JsZCwgSUdOX25vbWVuY2xhX211bmksIHJpb3NfZXNwYW55YSwgcmlvX2Vicm9fNCwgZmlsZSA9ICIuL2RhdG9zL21hcGFzL2dlb21ldHJpYXNfQmlnRGF0YV8yMDIxLlJEYXRhIikKbG9hZChoZXJlOjpoZXJlKCJkYXRvcyIsICJtYXBhcyIsICJnZW9tZXRyaWFzX0JpZ0RhdGFfMjAyMS5SRGF0YSIpKQojLSBPTEQgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojLSBsb3MgZ3VhcmTDqSBlbiAiLi9kYXRvcy9nZW9tZXRyaWFzX2NsYXNlXzEwLlJEYXRhIgojc2F2ZShDQ0FBLCBQcm92aW5jaWFzLCBtdW5pY2lwaW9zXzIwMTcsIHdvcmxkLCByaW9zLCBJR05fbm9tZW5jbGFfbXVuaSwgZmlsZSA9ICIuL2RhdG9zL21hcGFzL2dlb21ldHJpYXNfY2xhc2VfMTAuUkRhdGEiKQojLSBjYXJnbyBsb3MgZGF0b3MgcHJldmlhbWVudGUgZ3VhcmRhZG9zCiMtIGxvYWQoaGVyZTo6aGVyZSgiZGF0b3MiLCAibWFwYXMiLCAiZ2VvbWV0cmlhc19jbGFzZV8xMC5SRGF0YSIpKQpgYGAKCgojIDEuIEludHJvZHVjY2nDs24KClZhbW9zIGEgdXRpbGl6YXIgUiBjb21vIHVuYSBhcGxpY2FjacOzbiBHSVMgKFNpc3RlbWFzIGRlIGluZm9ybWFjacOzbiBnZW9ncsOhZmljYSk7IGVzIGRlY2lyLCBsbyB2YW1vcyBhIHV0aWxpemFyIHBhcmEgYWxtYWNlbmFyLCBtYW5pcHVsYXIsIGFuYWxpemFyIHkgcHJlc2VudGFyIGRhdG9zIGdlb2dyw6FmaWNvcyBvIGdlb2VzcGFjaWFsZXMuIEdJUyBlcyB1biDDoXJlYSBlbm9ybWUsIHBvciBlamVtcGxvLCBbYXF1w61dKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi92aWV3cy9TcGF0aWFsLmh0bWwpIHRpZW5lcyB1biBsaXN0YWRvIGRlIGhlcnJhbWllbnRhcyB5IHBhcXVldGVzIHBhcmEgdHJhYmFqYXIgY29uIGRhdG9zIGVzcGFjaWFsZXMgZW4gUi4gSGF5IG11Y2jDrXNpbWFzISEhLCBlcyB1biDDoXJlYSBkZSBjb25vY2ltaWVudG8gZW4gc8OtIG1pc21hLCBhc8OtIHF1ZSBzw7NsbyB2b3kgYSBpbnRlbnRhciBoYWNlciB1bmEgcGVxdWXDsWEgaW50cm9kdWNjacOzbiBhIEdJUyBjb24gUi4KCkR1cmFudGUgZXN0ZSB0dXRvcmlhbCB1c2FyZW1vcyBkaXN0aW50b3MgZGF0YS5mcmFtZXMgY29uIGdlb21ldHLDrWFzIGRlOgoKICAtIHBhw61zZXMgZGVsIG11bmRvICh3b3JsZCkgIAogIC0gQ0MuQUEgZXNwYcOxb2xhcyAoQ0NBQSkgIAogIC0gcHJvdmluY2lhcyBlc3Bhw7FvbGFzIChQcm92aW5jaWFzKSAgCiAgLSBtdW5pY2lwaW9zIGVzcGHDsW9sZXMgZW4gMjAyMCAobXVuaWNpcGlvc18yMDIwKSAgIAogIC0gcsOtb3MgZXNwYcOxb2xlcyAocmlvc19lc3BhbnlhKSAgIAogIC0gNCByw61vcyByZWxhY2lvbmFkb3MgY29uIGVsIHLDrW8gRWJybyAocmlvX2Vicm9fNCkgIAogIC0gbG9jYWxpemFjacOzbiAobG9uZ2l0dWQsIGxhdGl0dWQpIGRlIGxvcyBtdW5pY2lwaW9zIGVzcGHDsW9sZXMgKElHTl9ub21lbmNsYV9tdW5pKSAgCgoKClZlbmdhLCB2YW1vcyBhIGhhY2VyIG1hcGFzIGNvbiBlbGxvcyEhISBTw60sIGVuc2VndWlkYSEhIEEgdG9kb3Mgbm9zIGd1c3RhbiBsb3MgbWFwYXMgeSBoYWNlciBtYXBhcyBjb24gUiBlcyAqZWFzeSogLi4uIGJ1ZW5vLCBubyB0YW4gZsOhY2lsLCBwZXJvIHZhbW9zIGEgZWxsbyEhCgoKTcOhcyBxdWUgbWFwYXMgbG8gcXVlIHZhbW9zIGEgaGFjZXIgZXMgdmlzdWFsaXphciAqKmRhdG9zIGdlb2VzcGFjaWFsZXMqKi4gVW5vcyBkYXRvcyBubyBzZSBwdWVkZW4gY29uc2lkZXJhciBnZW9lc3BhY2lhbGVzIHNpIG5vIHZpZW5lbiBhY29tcGHDsWFkb3MgZGUgdW4gKipDUlMqKiAoQ29vcmRpbmF0ZSBSZWZlcmVuY2UgU3lzdGVtKSBkZSBmb3JtYSBxdWUgbGFzIGFwbGljYWNpb25lcyBHSVMgcHVlZGFuIG1hbmlwdWxhciB5IG1vc3RyYXIgY29ycmVjdGFtZW50ZSBlc29zIGRhdG9zLiBMb3MgQ1JTIHV0aWxpemFuIHVuIG1vZGVsbyBtYXRlbcOhdGljbyBwYXJhIGNvbmVjdGFyIGxvcyBkYXRvcywgbGFzIGNvb3JkZW5hZGFzIGRlIHVuIG9iamV0bywgY29uIHN1IHBvc2ljacOzbiBmw61zaWNhIGVuIGxhIHN1cGVyZmljaWUgdGVycmVzdHJlLiBVbiBzaXN0ZW1hIGRlIHJlZmVyZW5jaWEgZGUgY29vcmRlbmFkYXMgZ2VvZ3LDoWZpY2FzIHBlcm1pdGUgcXVlIHRvZG9zIGxvcyBwdW50b3MgZGUgbGEgdGllcnJhIHB1ZWRhbiBzZXIgZGVzY3JpdG9zIGNvbW8gdW4gY29uanVudG9zIGRlIGNvb3JkZW5hZGFzIChjb21vIGxhdGl0dWQsIGxvbmdpdHVkIHkgYWx0aXR1ZCkuIERpdmVyc29zIHNpc3RlbWFzIHNlIHV0aWxpemFuIHBhcmEgcmVwcmVzZW50YXIgbGEgdGllcnJhIHRyaWRpbWVuc2lvbmFsIGVuIHVuIHBsYW5vIG8gbWFwYSBiaWRpbWVuc2lvbmFsLgoKCkxvcyBkYXRvcyBnZW9lc3BhY2lhbGVzIHJlcHJlc2VudGFuIGx1Z2FyZXMgZ2VvZ3LDoWZpY29zIGRlIGxhIFRpZXJyYSB5IHBhcmEgcG9kZXIgcmVwcmVzZW50YXIgbGEgcG9zaWNpw7NuIGRlIHVuIG9iamV0byBlbiBsYSBUaWVycmEgbmVjZXNpdGFtb3MgZG9zIGNvc2FzOgoKLSBsYXMgY29vcmRlbmFkYXMgZGVsIG9iamV0byAKCi0gdW4gc2lzdGVtYSBkZSByZWZlcmVuY2lhIChDUlMpIHBhcmEgY29uZWN0YXIgZXNvcyBkYXRvcyBjb24gbGEgc3VwZXJmaWNpZSB0ZXJyZXN0cmUuCgpVbiBDUlMgY29uc2lzdGUgZW4gbW9kZWxvIGdlb23DqXRyaWNvIGVsaXBzb2lkZSBkZSBsYSBzdXBlcmZpY2llIHRlcnJlc3RyZSB5IHVuICpkYXR1bSogcXVlIGlkZW50aWZpY2EgZWwgb3JpZ2VuIHkgb3JpZW50YWNpw7NuIGRlIGxvcyBlamVzIHkgdGFtYmnDqW4gbGFzIHVuaWRhZGVzIGRlIG1lZGlkYS4KCkNvbW8gc2Fiw6lpcyBsYSBUaWVycmEgbm8gZXMgZXNmw6lyaWNhIHkgZXN0byBjb21wbGljYSBsYSB1dGlsaXphY2nDs24gZGUgdW4gw7puaWNvIENSUywgZGUgZm9ybWEgcXVlIHNlIGhhbiBjcmVhZG8gdW5vcyBjdWFudG9zIHBhcmEgdHJhdGFyIGRlIHJlcHJlc2VudGFyIGxhIHN1cGVyZmljaWUgdGVycmVzdHJlIGRlIGZvcm1hIGFkZWN1YWRhLgoKQWRlbcOhcywgaGF5IGRvcyB0aXBvcyBkZSBDUlM6ICoqQ1JTIGdlb2dyw6FmaWNvcyB5IHByb3llY3RhZG9zKiouIEVuIGxvcyBzaXN0ZW1hIGdlb2dyw6FmaWNvcywgZWwgbWFyY28gZGUgcmVmZXJlbmNpYSBlcyBlbCBnbG9ibyB0ZXJyw6FxdWVvIHkgY3VhbnRvcyBncmFkb3Mgbm9ydGUgbyBlc3RlLCBlc3TDoSBlbCBvYmpldG87IG1pZW50cmFzIHF1ZSBlbiBsb3Mgc2lzdGVtYXMgcHJveWVjdGFkb3MsIGVsIG1hcmNvIGRlIHJlZmVyZW5jaWEgZXMgdW5hIHJlcHJlc2VudGFjacOzbiBwbGFuYSBkZSBsYSBUaWVycmEgKG8gZGUgcGFydGUgZGUgZWxsYSkuIFNpIHRlIGludGVyZXNhIGVsIHRlbWEsIFthcXXDrV0oaHR0cHM6Ly9nZW9jb21wci5naXRodWIuaW8vcG9zdC8yMDE5L2Nycy1wcm9qZWN0aW9ucy10cmFuc2Zvcm1hdGlvbnMvKSB5ICBbYXF1w61dKGh0dHBzOi8vcGF1bGJsZ2lzLndvcmRwcmVzcy5jb20vMjAxNi8xMi8wOS9zaXN0ZW1hcy1kZS1jb29yZGVuYWRhcy1wcm95ZWN0YWRhcy15LWdlb2dyYWZpY2FzLWN1YWwtZXMtbGEtZGlmZXJlbmNpYS8pIHRpZW5lcyBkb3MgcG9zdHMgZGl2dWxnYXRpdm9zLgoKCgojIyBQcm95ZWNjaW9uZXMKCkNvbW8gbGEgVGllcnJhIGVzIGVzZsOpcmljYSwgcGFyYSBoYWNlciB1biBtYXBhIGJpZGltZW5zaW9uYWwgKG8gcGxhbm8pIGRlIGVsbGEsIHNlIHRpZW5lIHF1ZSB1c2FyIGFsZ8O6biB0aXBvIGRlIHByb3llY2Npw7NuLCBwb3IgbG8gcXVlIGxhcyDDoXJlYXMgc2UgbW9zdHJhcsOhbiBtw6FzIG8gbWVub3MgZGlzdG9yc2lvbmFkYXMuIFBvciBlamVtcGxvOgoKCgpgYGB7cn0KcDEgPC0gZ2dwbG90KENDQUEsICBhZXMoZmlsbCA9IGluZV9jY2FhKSApICsgCiAgICAgICAgICBnZW9tX3NmKCkgKwogICAgICAgICAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZ3VpZGUgPSAibm9uZSIpICsKICAgICAgICAgIGxhYnModGl0bGUgPSAiQ29vcmQuIGdlb2dyw6FmaWNhcyIpCgpwMiA8LSBnZ3Bsb3QoQ0NBQSwgIGFlcyhmaWxsID0gaW5lX2NjYWEpICkgKyAKICAgICAgICAgICBnZW9tX3NmKCkgKyBjb29yZF9zZihjcnMgPSAyMTU0LCBkYXR1bSA9IHNmOjpzdF9jcnMoMjE1NCkpICsKICAgICAgICAgICBzY2FsZV9maWxsX3ZpcmlkaXNfZChndWlkZSA9ICJub25lIikgKwogICAgICAgICAgIGxhYnModGl0bGUgPSAiTGFtYmVydCA5MyIpCgpwMSArIHAyCmBgYAoKCgoKIyMgRGF0b3MgZXNwYWNpYWxlcwoKUGFyYSBwb2RlciByZXByZXNlbnRhciBhZGVjdWFkYW1lbnRlIGRhdG9zIGVzcGFjaWFsZXMsIGVzdG9zIGRlYmVuIGNvbnRlbmVyIGxhIGluZm9ybWFjacOzbiBkZSBzdSBDUlMgeSBhbGd1bmFzIHZlY2VzIGhheSBxdWUgY29udmVydGlyIGVudHJlIENSUydzOyBkZSBmb3JtYSBxdWUgbGEgZXN0cnVjdHVyYSB0cmFkaWNpb25hbCBkZSBkYXRvcyBlbiBSIChsb3MgZGF0YS5mcmFtZXMpIG5vIGVzIGFkZWN1YWRhIHBhcmEgYWxtYWNlbmFyIGRhdG9zIGVzcGFjaWFsZXNeW01vc3QgZ2VvY29kZWQgZGF0YSBjcmVhdGVkIHRocm91Z2ggR29vZ2xlIE1hcHMgb3Igb3RoZXIgZ2VvY29kaW5nIHNpdGVzICBwcm92aWRlcyBjb29yZGluYXRlcyBpbiBsb25naXR1ZGUgYW5kIGxhdGl0dWRlIHZhbHVlcyB1c2luZyB0aGUgV0dTODQgZWxsaXBzb2lkIGFuZCB0aHVzIGNvbnRhaW5zIGEgQ1JTIGVxdWl2YWxlbnQgdG8gdGhlIFBST0ogYXJndW1lbnQgK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQgb3IgRVBTRyA0MzI2LiBIb3dldmVyLCB0aGlzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBDUlMgaXMgaW1wbGljaXQgYW5kIG5vdCBjb250YWluZWQgYW55d2hlcmUgd2l0aGluIHRoZSBkYXRhIGZyYW1lIGl0c2VsZl0uIFBpZW5zYSBhZGVtw6FzIHF1ZSBsb3MgZGF0b3MgZXNwYWNpYWxlcyBtw6FzIHNpbXBsZXMgKGxvcyBwdW50b3MpIHNlcsOtYW4gZsOhY2lsZXMgZGUgYWxtYWNlbmFyIGVuIGRvcyBjb2x1bW5hcyBkZSB1biBkYXRhLmZyYW1lLCBwZXJvIHBpZW5zYSBlbiBvYmpldG9zIGVzcGFjaWFsZXMgbcOhcyBjb21wbGVqb3MgY29tbyBsaW5lYXMsIHBvbMOtZ29ub3MsIHBvbMOtZ29ub3MgY29uIGh1ZWNvcyBldGMuLi4gaGFjZSBxdWUgcGFyYSBhbG1hY2VuYXIgZGF0b3MgZXNwYWNpYWxlcyBlbiBSIHNlIHV0aWxpY2VuIGVzdHJ1Y3R1cmFzIGRlIGRhdG9zIG3DoXMgY29tcGxlamFzIHF1ZSB1biBkYXRhLmZyYW1lLgoKCkFkZW3DoXMsIHBhcmEgY29tcGxpY2FyIHVuIHBvY28gbcOhcyBsYXMgY29zYXMsIGhheSBkb3MgdGlwb3MgZnVuZGFtZW50YWxlcyBkZSBkYXRvcyBnZW9ncsOhZmljb3M6ICoqcmFzdGVyIHkgdmVjdG9yaWFsZXMqKi4gRW4gZWwgY3Vyc28gc29sbyB2YW1vcyBhIHZlciAodW4gcG9xdWl0bykgbG9zIGRhdG9zIGVzcGFjaWFsZXMgdmVjdG9yaWFsZXMuCgotIExvcyBkYXRvcyByw6FzdGVyIHNlIGFsbWFjZW5hbiBjb21vIHVuYSBjdWFkcsOtY3VsYSBkZSB2YWxvcmVzIHF1ZSBzZSByZXByZXNlbnRhbiBlbiB1biBtYXBhIGNvbW8gcMOteGVsZXMuIENhZGEgdmFsb3IgZGUgcMOteGVsIHJlcHJlc2VudGEgdW4gw6FyZWEgZW4gbGEgc3VwZXJmaWNpZSBkZSBsYSBUaWVycmEuIExvcyBkYXRvcyByw6FzdGVyIHNvbiBkYXRvcyBwaXhlbGFkb3MgKG8gY3VhZHJpY3VsYWRvcykgZW4gbG9zIHF1ZSBjYWRhIHDDrXhlbCBlc3TDoSBhc29jaWFkbyBhIHVuYSB1YmljYWNpw7NuIGdlb2dyw6FmaWNhIGVzcGVjw61maWNhLiBFbCB2YWxvciBkZSB1biBww614ZWwgcHVlZGUgc2VyIGNvbnRpbnVvIChwb3IgZWplbXBsbywgZWxldmFjacOzbikgbyBjYXRlZ8OzcmljbyAocG9yIGVqZW1wbG8sIHVzbyBkZWwgc3VlbG8pLiBVbiByw6FzdGVyIGdlb2VzcGFjaWFsIHNvbG8gZXMgZGlmZXJlbnRlIGRlIHVuYSBmb3RvIGRpZ2l0YWwgZW4gcXVlIGVzdMOhIGFjb21wYcOxYWRvIGRlIGluZm9ybWFjacOzbiBlc3BhY2lhbCBxdWUgY29uZWN0YSBsb3MgZGF0b3MgYSB1bmEgdWJpY2FjacOzbiBlbiBwYXJ0aWN1bGFyLiBFc3RvIGluY2x1eWUgbGEgZXh0ZW5zacOzbiB5IGVsIHRhbWHDsW8gZGUgY2VsZGEgZGVsIHLDoXN0ZXIsIGVsIG7Dum1lcm8gZGUgZmlsYXMgeSBjb2x1bW5hcyB5IHN1IHNpc3RlbWEgZGUgcmVmZXJlbmNpYSBkZSBjb29yZGVuYWRhcyAobyBDUlMpCgoKLSBMYXMgZXN0cnVjdHVyYXMgZGUgZGF0b3MgdmVjdG9yaWFsZXMgcmVwcmVzZW50YW4gcG9zaWNpb25lcyBlc3BlY8OtZmljYXMgZW4gbGEgc3VwZXJmaWNpZSBkZSBsYSBUaWVycmEuCgoKCiMjIGBzcGAgdmVyc3VzIGBzZmAKCkVuIFIgaGF5IDIgZm9ybWFzIGFsdGVybmF0aXZhcyBkZSBhbG1hY2VuYXIgKHkgZGUgbWFuaXB1bGFyKSBkYXRvcyBnZW9ncsOhZmljb3MgdmVjdG9yaWFsZXM6IGVsIHBhcXVldGUgYHNwYCB5IGVsIHBhcXVldGUgYHNmYC4gTGFzIHByaW5jaXBhbGVzIGRpZmVyZW5jaWFzIGVudHJlIGxvcyBwYXF1ZXRlcyBzcCB5IHNmIHNlIGRlcml2YW4gZGUgbGEgbWFuZXJhIGVuIHF1ZSBsYXMgY2xhc2VzIHNwIHkgc2YgYWxtYWNlbmFuIGluZm9ybWFjacOzbiBzb2JyZSBDUlMsIGRpc3Rpbmd1ZW4gZW50cmUgcHVudG9zLCBsw61uZWFzIHkgcG9sw61nb25vcywgeSBjw7NtbyBjb25lY3RhbiBlc3RvcyBkYXRvcyBlc3BhY2lhbGVzIGNvbiBkYXRvcyBubyBlc3BhY2lhbGVzLgoKQ3VhbmRvIHNlIHVzYW4gZXN0b3MgcGFxdWV0ZXMsIHRhbnRvIGBzcGAgY29tbyBgc2ZgLCBwcm9wb3JjaW9uYW4gZW5sYWNlcyBhIHBvdGVudGVzIGJpYmxpb3RlY2FzIFNJRyBkZSBjw7NkaWdvIGFiaWVydG8sIGNvbW8gWyoqR0RBTCoqXShodHRwczovL2dkYWwub3JnLykgKEdlb3NwYXRpYWwgRGF0YSBBYnN0cmFjdGlvbiBMaWJyYXJ5OiBlcyB1bmEgYmlibGlvdGVjYSBkZSBzb2Z0d2FyZSBwYXJhIGxhIGxlY3R1cmEgeSBlc2NyaXR1cmEgZGUgZm9ybWF0b3MgZGUgZGF0b3MgZ2VvZXNwYWNpYWxlcywgcHVibGljYWRhIGJham8gbGEgTUlUIExpY2Vuc2UgcG9yIGxhIGZ1bmRhY2nDs24gZ2VvZXNwYWNpYWwgZGUgY8OzZGlnbyBhYmllcnRvIChPcGVuIFNvdXJjZSBHZW9zcGF0aWFsIEZvdW5kYXRpb24pLCBbKipHRU9TKipdKGh0dHBzOi8vZ2Vvcy5vc2dlby5vcmcvZG94eWdlbi9pbmRleC5odG1sKSAoR2VvbWV0cnkgRW5naW5lIE9wZW4gU291cmNlOiBwYXJhIG9wZXJhY2lvbmVzIGRlIHRvcG9sb2fDrWEgZW4gZ2VvbWV0csOtYXMpIHkgWyoqUFJPSioqXShodHRwczovL3Byb2oub3JnL25ld3MuaHRtbCkgKFBST0ogZXMgdW5hIGJpYmxpb3RlY2EgcXVlIHByb3BvcmNpb25hIG3DqXRvZG9zIHBhcmEgdHJhbnNmb3JtYXIgZW50cmUgc2lzdGVtYXMgZGUgcmVmZXJlbmNpYSBkZSBjb29yZGVuYWRhcyBkaWZlcmVudGVzIHkgb3BlcmFjaW9uZXMgZGUgcHJveWVjY2nDs24pLiBQb3IgZXNvIGN1YW5kbyBjYXJnYW1vcyBlbCBwYXF1ZXRlIGBzZmAgbm9zIGFwYXJlY2UgZWwgc2lndWllbnRlIG1lbnNhamUgZW4gbGEgY29uc29sYTogYExpbmtpbmcgdG8gR0VPUyAzLjkuMSwgR0RBTCAzLjMuMiwgUFJPSiA3LjIuMWAKCgpNdWNob3MgcGFxdWV0ZXMgUiBwYXJhIEdJUyB1dGlsaXphbiBsYSBtZXRvZG9sb2fDrWEvYXJxdWl0ZWN0dXJhIHByb3BvcmNpb25hZGEgcG9yIGBzcGAsIHBlcm8gcG9yIHZhcmlhcyByYXpvbmVzIGxhIG1ldG9kb2xvZ8OtYSBpbXBsZW1lbnRhZGEgcG9yIGVsIHBhcXVldGUgIGVsIHBhcXVldGUgYHNmYCB5YSBlcyBwcsOhY3RpY2FtZW50ZSBlbCBlc3TDoW5kYXIuCgpMYXMgcHJpbmNpcGFsZXMgcmF6b25lcyBzb24gNDogCgoxKSBFbCBwYXF1ZXRlIGBzZmAgaW1wbGVtZW50YSBlbCBlc3TDoW5kYXIgYWJpZXJ0byAic2ltcGxlIGZlYXV0dXJlcyIgcGFyYSBsYSByZXByZXNlbnRhY2nDs24gZGUgZGF0b3MgZ2VvZ3LDoWZpY29zIHZlY3RvcmlhbGVzIGVuIFIuIFNpbXBsZSBmZWF0dXJlcyBlcyB1biBlc3TDoW5kYXIsIHVuIGNvbmp1bnRvIGNvbcO6biBkZSByZWdsYXMsIHBhcmEgcmVwcmVzZW50YXIgbGFzIGdlb21ldHLDrWFzIGVzcGFjaWFsZXMgZGUgb2JqZXRvcyBkZWwgbXVuZG8gcmVhbCBlbiB1biBvcmRlbmFkb3IsIGVuIGxhcyBxdWUgc2UgZGVmaW5lIGRlc2RlIGPDs21vIHNlIGFsbWFjZW5hbiBsb3MgZGF0b3MgZW4gZWwgb3JkZW5hZG9yIGhhc3RhIHF1w6kgb3BlcmFjaW9uZXMgZ2VvbcOpdHJpY2FzIHNlIHB1ZWRlbiBhcGxpY2FyLiAKCjIpIGBzZmAgdXRpbGl6YSBwYXJhIGNvbnRlbmVyL2FsbWFjZW5hciBsb3MgZGF0b3MgZXNwYWNpYWxlcyB1bmEgZXN0cnVjdHVyYSBtdXkgc2ltaWxhciBhIHVuIGRhdGFmcmFtZTogbG9zIHNwYXRpYWwgZGF0YWZyYW1lcyBvIGBzZmBeW1PDrSwgdG9kbyBzZSBsbGFtYSBgc2ZgISEhIFRhbnRvIGVsIHBhcXVldGUgY29tbyBlbCBlc3TDoW5kYXIgcXVlIGluY29ycG9yYSBjb21vIGxhIGVzdHJ1Y3R1cmEgZGUgZGF0b3Mgc2UgbGxhbWFuIHRvZG9zIGBzZmAuXSAsIHF1ZSBzb24gbXV5IHBhcmVjaWRvcyBhIHVuIGRhdGEgZnJhbWUgc2Fsdm8gcXVlIGluY2x1eWVuIHVuYSBjb2x1bW5hIGFkaWNpb25hbCwgZ2VuZXJhbG1lbnRlIGxsYW1hZGEgYGdlb21ldHJ5YCBxdWUgYWxtYWNlbmEgbGEgaW5mb3JtYWNpw7NuIGdlb2dyw6FmaWNhLCBsb3MgZGF0b3MgdmVjdG9yaWFsZXMgZXNwYWNpYWxlcywgeWEgc2VhbiBlc3RvcyBwdW50b3MsIGxpbmVhcywgcG9sw61nb25vcyBldGMuLi4KCjMpIE11Y2hhcyBkZSBsYXMgZnVuY2lvbmVzIGRlbCBwYXF1ZXRlIGBzZmAgY29taWVuemFuIGNvbiBgc3RfYDsgcG9yIGVqZW1wbG8gIGBzdF9uZWFyZXN0X3BvaW50cygpYCwgbG8gcXVlIGZhY2lsaXRhIHN1IHVzby4KCgo0KSBsYXMgZnVuY2lvbmVzIGRlIGBzZmAgc2UgaW50ZWdyYW4gZW4gZWwgKip0aWR5dmVyc2Ugd29ya2Zsb3cqKi4gRGUgaGVjaG8gbGEgZXN0cnVjdHVyYSBkZSBkYXRvcyBxdWUgdXRpbGl6YSBlbCBwYXF1ZXRlIGBzZmAgcGFyYSBhbG1hY2VuYXIgZGF0b3MgZ2VvZ3LDoWZpY29zIHNvbiBsb3Mgb2JqZXRvcyBkZSB0aXBvIGBgc2ZgIHVuYSBleHRlbnNpw7NuIGRlIGxvcyBkYXRhLmZyYW1lcywgZGUgZm9ybWEgcXVlIHB1ZWRlbiBzZXIgbWFuaXB1bGFkb3MgY29uIGBkcGx5cmAgeSBncmFmaWNhZG9zIGNvbiBgZ2dwbG90MmAuIExvcyBvYmpldG9zIGBzZmAgcHVlZGVuIGFsbWFjZW5hciBkYXRvcyBub3JtYWxlcyAoY3VhbnRpdGF0aXZvcywgY3VhbGl0YXRpdm9zLCB0ZXh0bykgcGVybyBhbG1hY2VuYW4gZGF0b3MgZXNwYWNpYWxlcyBlbiB1bmEgY29sdW1uYSBlc3BlY2lhbCBsbGFtYWRhIGhhYml0dWFsbWVudGUgYGdlb21ldHJ5YCBjb24gY2xhc2UgZXNwZWNpYWwgKGBzZmNgKSwgeSBhbGzDrSBzZSBhbG1hY2VuYSBsYSBpbmZvcm1hY2nDs24gZ2VvZ3LDoWZpY2E6IGVsIENSUywgY29vcmRlbmFkYXMgeSBlbCB0aXBvIGRlIG9iamV0byBnZW9tw6l0cmljby4gTGEgY2xhc2Ugc2ZjIHRpZW5lIHNpZXRlIHN1YmNsYXNlcyBwYXJhIGRlbm90YXIgbG9zIGRpc3RpbnRvcyB0aXBvcyBkZSBvYmpldG9zIGdlb23DqXRyaWNvcyBxdWUgc2UgZGVyaXZhbiBkZWwgZXN0w6FuZGFyIGBzZmAuIAoKCi0gUG9yIGVqZW1wbG8sIHBhcmEgc2VsZWNjaW9uYXIgZ2VvbWV0csOtYXMgdXNhbW9zIGxhIGZ1bmNpw7NuIGBmaWx0ZXIoKWA6CgoKYGBge3J9CiMtIGVsZWdpbW9zIDQgQ0MuQUEKenpfcHJvdl9jaG9zZW4gPC0gYygiQW5kYWx1Y8OtYSIsICJHYWxpY2lhIiwgIkFyYWfDs24iLCAiQ2FuYXJpYXMiLCAiSWxsZXMgQmFsZWFycyIpCgp6el9wcm92XzEgPC0gUHJvdmluY2lhcyAlPiUgZmlsdGVyKGluZV9jY2FhLm4gJWluJSB6el9wcm92X2Nob3NlbikKenpfcHJvdl8yIDwtIFByb3ZpbmNpYXMgJT4lIGZpbHRlcighKGluZV9jY2FhLm4gJWluJSB6el9wcm92X2Nob3NlbikpCgpwMSA8LSBnZ3Bsb3QoenpfcHJvdl8xLCBhZXMoZmlsbCA9IGluZV9wcm92KSApICsgCiAgICAgICAgIGdlb21fc2YoKSAgKwogICAgICAgICBzY2FsZV9maWxsX3ZpcmlkaXNfZChndWlkZSA9ICJub25lIikgKwogICAgICAgICBsYWJzKHRpdGxlID0gIkNvb3JkLiBnZW9ncsOhZmljYXMiKQoKcDIgPC0gZ2dwbG90KHp6X3Byb3ZfMiwgYWVzKGZpbGwgPSBpbmVfcHJvdikgKSArIAogICAgICAgICBnZW9tX3NmKCkgICsKICAgICAgICAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZ3VpZGUgPSAibm9uZSIpICsKICAgICAgICAgbGFicyh0aXRsZSA9ICJDb29yZC4gZ2VvZ3LDoWZpY2FzIikKCnAxICsgcDIgCmBgYAoKLSBQb3IgZWplbXBsbywgcGFyYSBhZ3J1cGFyIGdlb21ldHLDrWFzLCBlbiBlc3RlIGNhc28gbGFzIHByb3ZpbmNpYXMgZXNwYcOxb2xhcywgdXNhbW9zIGxhIGZ1bmNpw7NuIGBzdW1tYXJpemUoKWAKCgpgYGB7cn0KIy0gY3JlYW1vcyBsYXMgZ2VvbWV0csOtYXMgZGUgbGFzIENDLkFBIHBvciBhZ3J1cGFjacOzbiBkZSBsYXMgZ2VvbWV0csOtYXMgcHJvdmluY2lhbGVzCkNDQUFfMiA8LSBQcm92aW5jaWFzICU+JSBncm91cF9ieShpbmVfY2NhYSwgaW5lX2NjYWEubikgJT4lIHN1bW1hcml6ZSgpCgpwMSA8LSBnZ3Bsb3QoUHJvdmluY2lhcywgYWVzKGZpbGwgPSBpbmVfcHJvdikgKSArIAogICAgICAgICBnZW9tX3NmKCkgICsKICAgICAgICAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZ3VpZGUgPSAibm9uZSIpICsKICAgICAgICAgbGFicyh0aXRsZSA9ICJDb29yZC4gZ2VvZ3LDoWZpY2FzIikKCgpwMiA8LSBnZ3Bsb3QoQ0NBQV8yLCBhZXMoZmlsbCA9IGluZV9jY2FhKSApICsgCiAgICAgICAgIGdlb21fc2YoKSAgKwogICAgICAgICBzY2FsZV9maWxsX2Rpc2NyZXRlKGd1aWRlID0gIm5vbmUiKSArCiAgICAgICAgIGxhYnModGl0bGUgPSAiQ29vcmQuIGdlb2dyw6FmaWNhcyIpCgpwMSArIHAyIApgYGAKCgpgYGB7ciwgZWNobyA9IEZBTFNFfQojLSBib3JyYW1vcyBsbyBxIG5vIG5vcyBoYWNlIGZhbHRhCnJtKHAxLCBwMiwgenpfcHJvdl9jaG9zZW4sIHp6X3Byb3ZfMSwgenpfcHJvdl8yLCBDQ0FBXzIpCmBgYAoKQnVlbm8sIHB1ZXMgZXN0ZSBlcyB0b2RhIGxhIHRlb3LDrWEgZGVsIHR1dG9yaWFsLiBWYW1vcyBhIGhhY2VyIGdyw6FmaWNvcyEhCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0KCjxicj4KCgojIDIuIEhhY2llbmRvIGdyw6FmaWNvcwoKYGBge3IsIGV2YWwgPSBGQUxTRSwgZWNobyA9IEZBTFNFfQojLSBwb25lbW9zIHVuIHRoZW1lIHVuIHBvY28gbcOhcyBhZGVjdWFkbyBwYXJhIG1hcGFzCnRoZW1lX3NldCh0aGVtZV9idygpKSAgIy0gVGhlIGNsYXNzaWMgZGFyay1vbi1saWdodCBnZ3Bsb3QyIHRoZW1lIHdvcmsgYmV0dGVyIGZvciBwcmVzZW50YXRpb25zIAp0aGVtZV9zZXQoY293cGxvdDo6dGhlbWVfbWFwKCkpCmBgYAoKClByaW1lcm8gdmFtb3MgY2FyZ2FyIHVuIGNvbmp1bnRvIGRlIGRhdG9zIGVzcGFjaWFsZXMgZGVsIHBhcXVldGUgW2BybmF0dXJhbGVhcnRoYF0oaHR0cHM6Ly9kb2NzLnJvcGVuc2NpLm9yZy9ybmF0dXJhbGVhcnRoLykuIEJ1ZW5vLCBlbiByZWFsaWRhZCB5YSBsbyBoZW1vcyBjYXJnYWRvIGFudGVzLgoKCmBgYHtyLCBldmFsID0gRkFMU0V9CiMtIGFudGVzIHVzYWJhIGxhcyB3b3JsZyBnZW9tZXRpZXMgZGVsIHBhcXVldGUgcm5hdHVyYWxlYXJ0aCwgcGVybyAuLi4KIyB3b3JsZCA8LSBybmF0dXJhbGVhcnRoOjpuZV9jb3VudHJpZXMoc2NhbGUgPSAibWVkaXVtIiwgcmV0dXJuY2xhc3MgPSAic2YiKQojLSBwZXJvIGFob3JhIHByZWZpZXJvIHV0aWxpemFyIGxhcyBnZW9tZXRyaWFzIGRlbCBwYXF1ZXRlIHRtYXAsCiMtIGxhcyBndWFyZGUgZW4gbm92aWVtYnJlIGRlIDIwMjEuIFNlIGNhcmdhYmFuIGFzw606CmxpYnJhcnkodG1hcCkgCmRhdGEoV29ybGQpICMtIGhhY2Vtb3MgYWNjZXNpYmxlcyAxIGRmCndvcmxkIDwtIFdvcmxkIDsgcm0oV29ybGQpCmBgYAoKVmVhbW9zIHF1ZSBoYXkgZW4gYHdvcmxkYAoKYGBge3J9CmNsYXNzKHdvcmxkKSAjLSBlcyB1biBzZiwgcGVybyB0YiBlcyB1biBkYXRhLmZyYW1lCm5hbWVzKHdvcmxkKSAjLSBmaWphdGUgcXVlIGxhIHVsdGltYSBjb2x1bW5hIHNlIGxsYW1hIGdlb21ldHJ5IHkgYWxtYWNlbmEgbG9zIGRhdG9zIGVzcGFjaWFsZXMKYGBgCgoKTG9zIGdyw6FmaWNvcyBsb3MgdmFtb3MgYSBoYWNlciwgZGUgbW9tZW50bywgY29uIGBnZ3Bsb3QyYC4gRXMgdGFuIGbDoWNpbCBjb21vIHVzYXIgYGdlb21fc2YoKWAuIExhcyBzaWd1aWVudGVzIDMgZXhwcmVzaW9uZXMgcHJvZHVjZW4gZWwgbWlzbW8gZ3LDoWZpY286IAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2dwbG90KGRhdGEgPSB3b3JsZCkgKyBnZW9tX3NmKCkKCmdncGxvdChkYXRhID0gd29ybGQsIGFlcyhnZW9tZXRyeSA9IGdlb21ldHJ5KSkgKyBnZW9tX3NmKCkKCmdncGxvdCgpICsgZ2VvbV9zZihkYXRhID0gd29ybGQsIGFlcyhnZW9tZXRyeSA9IGdlb21ldHJ5KSkKYGBgCgoKYGBge3IsIGVjaG8gPSBGQUxTRX0KZ2dwbG90KGRhdGEgPSB3b3JsZCkgKyBnZW9tX3NmKCkKYGBgCgpDb21vIGVsIG1hcGEgcXVlIGhlbW9zIGhlY2hvLCBlbiByZWFsaWRhZCBlcyB1biBncsOhZmljbyBnZ3Bsb3QsIHB1ZXMgeWEgc2FiZW1vcyBjYW1iaWFyL3R1bmVhciBhbGd1bmFzIGRlIGxhcyBjYXJhY3RlcsOtc3RpY2FzIGRlbCBncsOhZmljby4gUG9yIGVqZW1wbG86IAoKYGBge3J9CmdncGxvdChkYXRhID0gd29ybGQpICsgZ2VvbV9zZihjb2xvciA9ICJibGFjayIsIGZpbGwgPSAibGlnaHRncmVlbiIsIGx3ZCA9IDAuMikKYGBgCgoKUG9kZW1vcyBwb25lciB0w610dWxvcyB5IGxleWVuZGFzOgoKYGBge3J9CnAgPC0gZ2dwbG90KGRhdGEgPSB3b3JsZCkgKyBnZW9tX3NmKCkgKwogICAgICAgbGFicyh0aXRsZSA9ICJHcsOhZmljbyAxOiBNYXBhIGRlbCBtdW5kbyIsCiAgICAgICAgICAgIGNhcHRpb24gPSAiRGF0b3MgcHJvdmVuaWVudGVzIGRlIHRtYXAiKQpwCmBgYAoKClBvZGVtb3MgaGFjZXIgbWFwYXMgZGUgKipDT1JPUExFVEFTKio6IGxhcyBkaXN0aW50YXMgcmVnaW9uZXMgc2UgY29sb3JlYW4gZGUgZm9ybWEgcXVlIG11ZXN0cmVuIGxvcyB2YWxvcmVzIGRlIHVuYSB2YXJpYWJsZSBlc3RhZMOtc3RpY2EuIFBvciBlamVtcGxvLCB1bmEgY29yb3BsZXRhIGNvbiBsYSB2YXJpYWJsZSBwb2JsYWNpw7NuKHBvYl9lc3QpCgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KcCArIGdlb21fc2YoYWVzKGZpbGwgPSBwb3BfZXN0KSkKcCArIGdlb21fc2YoYWVzKGZpbGwgPSBwb3BfZXN0KSkgKyBzY2FsZV9maWxsX2Rpc3RpbGxlcigpCnAgKyBnZW9tX3NmKGFlcyhmaWxsID0gcG9wX2VzdCkpICsgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gInBsYXNtYSIsIHRyYW5zID0gInNxcnQiKQpgYGAKCgpgYGB7ciwgZWNobyA9IEZBTFNFfQpwICsgZ2VvbV9zZihhZXMoZmlsbCA9IHBvcF9lc3QpKSArIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJwbGFzbWEiLCB0cmFucyA9ICJzcXJ0IikKYGBgCjxicj4KCiMjIyBQcm95ZWNjaW9uZXMKClBhcmEgY2FtYmlhciBsYSBwcm95ZWNjacOzbiBkZSB1biBvYmpldG8gInNmIiBhIG90cm8gQ1JTIHVzYW1vcyBsYSBmdW5jacOzbiAgYHN0X3RyYW5zZm9ybSgpYCwgbyBhbHRlcm5hdGl2YW1lbnRlIHBvZGVtb3MgZXNwZWNpZmljYXIgZGlyZWN0YW1lbnRlIGVsIENSUyBlbiBlbCBncsOhZmljbyBjb24gYGNvb3JkX3NmKClgOgoKYGBge3J9CiMtIHdvcmxkMiA8LSBzdF90cmFuc2Zvcm0od29ybGQsICIrcHJvaj1sYWVhICt5XzA9MCArbG9uXzA9MTU1ICtsYXRfMD0tOTAgK2VsbHBzPVdHUzg0ICtub19kZWZzIikKcCArIGNvb3JkX3NmKGNycyA9ICIrcHJvaj1sYWVhICt5XzA9MCArbG9uXzA9MTU1ICtsYXRfMD0tOTAgK2VsbHBzPVdHUzg0ICtub19kZWZzIikgKyAKICAgICBsYWJzKHN1YnRpdGxlID0gIkxhbWJlcnQgQXppbXV0aGFsIEVxdWFsIEFyZWEgcHJvamVjdGlvbiIpCmBgYAoKPGJyPgoKVmFtb3MgYSBqdWdhciB1biBwb2NvIGNvbiBlbCBDUlMgY29uIGxhIGZ1bmNpw7NuIGBjb29yZF9zZigpYC4gSGF5IHF1ZSBzYWJlciBxdWUgZ2dwbG90IHVzYXLDoSBlbCBDUlMgZGUgbGEgcHJpbWVyYSBjYXBhIHF1ZSBjb250ZW5nYSB1bm8uIFNpIG5vIGh1Ymllc2UgbmluZ8O6biBDUlMgc2UgdXNhICoqV0dTODQqKiAobGF0aXR1ZGUvbG9uZ2l0dWRlLCB0aGUgcmVmZXJlbmNlIHN5c3RlbSB1c2VkIGluIEdQUykuCgpTZSBwdWVkZSBjYW1iaWFyIGVsIENSUyBkZSB2YXJpYXMgbWFuZXJhczoKCjEuIHVzYW5kbyB1biBgKipzdHJpbmcgUFJPSioqIHbDoWxpZG8uIFBvciBlamVtcGxvIGVzdGUgc3RyaW5nIGAiK3Byb2o9bGFlYSArbGF0XzA9NTIgK2xvbl8wPTEwICt4XzA9NDMyMTAwMCAreV8wPTMyMTAwMDAgK2VsbHBzPUdSUzgwICt1bml0cz1tICtub19kZWZzICJgIGVzIGVsIEV1cm9wZWFuLWNlbnRyaWMgRVRSUzg5IExhbWJlcnQgQXppbXV0aGFsIEVxdWFsLUFyZWEgcHJvamVjdGlvbikKCjIuIHVzYW5kbyB1biAqKmPDs2RpZ28gU1JJRCoqIChTcGF0aWFsIFJlZmVyZW5jZSBTeXN0ZW0gSWRlbnRpZmllcikgIAoKMy4gVXNhbmRvIHVuICoqY8OzZGlnbyBFUEdTKiogKEV1cm9wZWFuIFBldHJvbGV1bSBTdXJ2ZXkgR3JvdXApXltQYXJlY2UgcXVlIGNvbiBQUk9KNyBlcyBtZWpvciBubyB1c2FybG9dCgpMb3Mgc2lndWllbnRlcyB0cmVzIGdyw6FmaWNvcyBoYWNlbiBsbyBtaXNtby4gUmVwcmVzZW50YW4gbG9zIGRhdG9zIGRlbCBzcGF0aWFsLmRhdGEuZnJhbWUgYHdvcmxkYCB1c2FuZG8gdGhlIEVUUlM4OSBMYW1iZXJ0IEF6aW11dGhhbCBFcXVhbC1BcmVhIHByb2plY3Rpb24sIGN1eW8gY8OzZGlnbyBFUFNHIGVzIDMwMzUuIFtBcXXDrV0oaHR0cDovL3d3dy5qdW50YWRlYW5kYWx1Y2lhLmVzL21lZGlvYW1iaWVudGUvc2l0ZS9yZWRpYW0vbWVudWl0ZW0uMDRkYzQ0MjgxZTVkNTNjZjhjYTc4Y2E3MzE1MjVlYTAvP3ZnbmV4dG9pZD0yYTQxMmFiY2I4NmEyMjEwVmduVkNNMTAwMDAwMTMyNWU1MGFSQ1JEJmxyPWxhbmdfZXMpIHRpZW5lcyBhbGd1bm9zIGPDs2RpZ29zIGRlIHByb3llY2Npb25lczoKIAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KcCArIGNvb3JkX3NmKGNycyA9ICIrcHJvaj1sYWVhICtsYXRfMD01MiArbG9uXzA9MTAgK3hfMD00MzIxMDAwICt5XzA9MzIxMDAwMCArZWxscHM9R1JTODAgK3VuaXRzPW0gK25vX2RlZnMgIikgICMtIHVzYW1vcyBQUk9KIHN0cmluZwoKcCArIGNvb3JkX3NmKGNycyA9IHN0X2NycygzMDM1KSkgICAjLSB1c2Ftb3MgU1JJRCBpZGVudGlmaWVyCgpwICsgY29vcmRfc2YoY3JzID0gIitpbml0PWVwc2c6MzAzNSIpICAjLSB1c2Ftb3MgRVBTRyBjb2RlIChtZWpvciBubyB1c2FybG8geHEgR0RBTCBoYSBkZXByZWNhdGVkIGVzdGEgc2ludGF4aXMpCmBgYAoKCmBgYHtyLCBlY2hvID0gVFJVRX0KcCArIGNvb3JkX3NmKGNycyA9IHN0X2NycygzMDM1KSkgKyAjLSB1c2Ftb3MgU1JJRCBpZGVudGlmaWVyCiAgICBsYWJzKHN1YnRpdGxlID0gIihFdXJvcGVhbi1jZW50cmljIEVUUlM4OSBcbkxhbWJlcnQgQXppbXV0aGFsIEVxdWFsLUFyZWEgcHJvamVjdGlvbikiKQpgYGAKCgo8YnI+CgojIyMgWm9vbQoKVm9sdmVtb3MgYSBsYSBwcm95ZWNjacOzbiBuYXRpdmEgZGVsIGNvbmp1bnRvIGRlIGRhdG9zIGB3b3JsZGAgeSBoYWNlbW9zIHVuIHpvb20gZW4gbnVlc3RybyBtYXBhIGNvbiBgY29vcmRfc2YoKWAuIEVsIHpvb20gbG8gaGFjZW1vcyBwYXJlIGNlbnRyYXJub3MgZW4gRXNwYcOxYSB5IGFscmVkZWRvcmVzLiBQYXJhIGhhY2VyIGVsIHpvb20gZW4gcmVhbGlkYWQgYWp1c3RhbW9zIGxvcyBsw61taXRlcyBkZSBsb3MgZWplcyBkZWwgZ3LDoWZpY28KCgpgYGB7cn0KcF9lc3AgPC0gcCArIGNvb3JkX3NmKHhsaW0gPSBjKC0xNS4wMCwgMTUuMDApLCB5bGltID0gYygyMSwgNDcuNDQpLCBleHBhbmQgPSBUUlVFKQpwX2VzcApgYGAKCkNvbiBlbCBwYXF1ZXRlIGBnZ3NwYXRpYWxgIHBvZGVtb3MgcG9uZXIgZWxlbWVudG9zIGRlIG1hcGFzIGNvbW8gdW5hICpTY2FsZSBiYXIqIGNvbiBgYW5ub3RhdGlvbl9zY2FsZSgpYCB5IHVuYSAqTm9ydGggYXJyb3cqIGNvbiBgYW5ub3RhdGlvbl9ub3J0aF9hcnJvdygpYDoKCgpgYGB7cn0KcF9lc3AgKyAKIGdnc3BhdGlhbDo6YW5ub3RhdGlvbl9zY2FsZShsb2NhdGlvbiA9ICJibCIsIHdpZHRoX2hpbnQgPSAwLjUpICsKIGdnc3BhdGlhbDo6YW5ub3RhdGlvbl9ub3J0aF9hcnJvdyhsb2NhdGlvbiA9ICJibCIsIHdoaWNoX25vcnRoID0gInRydWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYWRfeCA9IHVuaXQoMC43NSwgImluIiksIHBhZF95ID0gdW5pdCgwLjUsICJpbiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0eWxlID0gbm9ydGhfYXJyb3dfZmFuY3lfb3JpZW50ZWVyaW5nKQpgYGAKCjxicj4KCiMjIyB0ZXh0byBlbiBlbCBtYXBhCgpFbCBtYXBhIHNlIGhhIGhlY2hvIGNvbiBgZ2dwbG90MmAsIGFzw60gcXVlIHBvZGVtb3MgYcOxYWRpciwgcG9yIGVqZW1wbG8sIHRleHRvIGNvbiBgZ2VvbV90ZXh0KClgOyBwZXJvIGFudGVzIGRlIGVsbG8sIHZhbW9zIGEgY3JlYXIvY2FsY3VsYXIgY2VudHJvaWRlcyBkZSBjYWRhIHBhw61zIHBhcmEgdmVyIGRvbmRlIHNpdHVhbW9zIGVsIHRleHRvIGVuIGVsIG1hcGEuIAoKCmBgYHtyfQp3b3JsZF9wb2ludHMgPC0gc3RfY2VudHJvaWQod29ybGQsIG9mX2xhcmdlc3RfcG9seWdvbiA9IFRSVUUpICAgICMtIHN1c3RpdHV5ZSBsYSBnZW9tZXRyeSBwb3IgZWwgY2VudHJvaWRlCmBgYAoKRGUgZXN0YSBmb3JtYSwgY29uIGBzdF9jZW50cm9pZCgpYCBwb2RlbW9zIGNhbGN1bGFyIGxvcyBjZW50cm9pZGVzIGRlIGNhZGEgZ2VvbWV0csOtYSwgZGUgY2FkYSBwYcOtcywgcGVybyBMb3MgZGF0b3MgZGVsIGNlbnRyb2lkZSAobG9uZ2l0dWQsIGxhdGl0dWQpIHNlIGFsbWFjZW5hbiBlbiBsYSBjb2x1bW5hIGBnZW9tZXRyeWA7IGVzIGRlY2lyIHN1c3RpdHV5ZW4gbyBlbGltaW5hbiBsYXMgZ2VvbWV0csOtYXMgYW50ZXJpb3JlcyBxdWUgZXJhbiBsb3MgbMOtbWl0ZXMgbyBmcm9udGVyYXMgZGUgbG9zIHBhw61zZXMuCgpTaSBxdWVyZW1vcyBxdWUgbGFzIGdlb21ldHLDrWFzIG9yaWdpbmFsZXMgeSBsb3MgY2VudHJvaWRlcyBlc3TDqW4gZW4gdW4gbWlzbW8gZGF0YS5mcmFtZSwgaGVtb3MgZGUgaGFjZXI6CgpgYGB7cn0Kd29ybGRfcG9pbnRzIDwtIGNiaW5kKHdvcmxkLCBzdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZCh3b3JsZCRnZW9tZXRyeSwgb2ZfbGFyZ2VzdF9wb2x5Z29uID0gVFJVRSkpKSAjLSBhaG9yYSBlbCBjZW50cm9pZGUgcGFzYSBhIGVzdGFyIGVuIDIgY29sdW1uYXMsIGxsYW1hZGFzIFggZSBZLCBkb25kZSBmaWd1cmFuIGxhIGxvbmdpdHVkIHkgbGF0aXR1ZCBkZWwgY2VudHJvaWRlCmBgYAoKTG8gcXVlIGhhY2Vtb3MgZXMgZnVzaW9uYXIgbG9zIGxvcyBkb3MgKmRhdGEuZnJhbWUncyosIHkgZWwgY2VudHJvaWRlIHBhc2Fyw6EgYSBlc3RhciBlbiBkb3MgbnVldmFzIGNvbHVtbmFzIGxsYW1hZGFzIFggZSBZLCBzaW4gc3VzdGl0dWlyIGxhcyBnZW9tZXRyw61hcyBkZSBsb3MgcGHDrXNlcy4KClBhcmEgdmVyIGVzYXMgZG9zIG51ZXZhcyBjb2x1bW5hcyAoWCBlIFkpLCB2YW1vcyBhIGhhY2VyIHVuICJwZXF1ZcOxbyB0cnVjbyIsIHZhbW9zIGEgcXVpdGFyIGxhIGNvbHVtbmEgZGUgZ2VvbWV0csOtYXMuIEVzdG8gZmFjaWxpdGEgZWwgdmlzaW9uYWRvIGRlIGxvcyBkYXRvcywgcGVybyBsw7NnaWNhbWVudGUgY3VhbmRvIHZheWFtb3MgYSBoYWNlciBlbCBncsOhZmljbywgcmVhbG1lbnRlIG5vIHBvZHJlbW9zIGVsaW1pbmFyIGxhIGdlb21ldHJ5IHBvcnF1ZSBzaSBubywgbG9zIGRhdG9zIGRlamFyw61hbiBkZSBzZXIgZGF0b3MgZXNwYWNpYWxlcy4KCmBgYHtyfQojLSB0cnVjbyBwYXJhIHZlciBtZWpvciBsb3MgZGF0b3MgZXNwYWNpYWxlczogcXVpdGFyIGxhIGdlb21ldHLDrWEKbmFtZXMod29ybGRfcG9pbnRzKQp6eiA8LSB3b3JsZF9wb2ludHMgJT4lIHNlbGVjdChuYW1lLCBpc29fYTMsICBYLCBZKSAlPiUgIAogICAgICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgICMtIHBvciBzaSBxdWllcm8gdmVyIG1lam9yIGxvcyBkYXRvcyBkZWwgZGYKYGBgCgoKSGFjZW1vcyBlbCBtYXBhIGNvbiBsb3Mgbm9tYnJlcyBkZSBsb3MgcGHDrXNlcyBjb24gYGdlb21fdGV4dCgpYCB5IGVsIG5vbWJyZSBkZWwgTWFyIE1lZGl0ZXJyw6FuZW8gY29uIGBhbm5vdGF0ZSgpYAoKYGBge3J9CnBfZXNwICsgCiAgZ2VvbV90ZXh0KGRhdGEgPSB3b3JsZF9wb2ludHMsIAogICAgICAgICAgICBhZXMoeCA9IFgsIHkgPSBZLCBsYWJlbCA9IG5hbWUpLCAKICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBmb250ZmFjZSA9ICJib2xkIiwgY2hlY2tfb3ZlcmxhcCA9IFRSVUUsIHNpemUgPSAyLjUpICsKICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCB4ID0gNS41LCB5ID0gMzgsIAogICAgICAgICAgIGxhYmVsID0gIk1hciBNZWRpdGVycsOhbmVvIiwgZm9udGZhY2UgPSAiaXRhbGljIiwgY29sb3IgPSAiZ3JleTIyIiwgc2l6ZSA9IDQpCmBgYAoKCjxicj4KCiMjIyBUdW5lYW5kbyBkZWwgbWFwYQoKClZhbW9zIGEgdHVuZWFyIGVsIG1hcGEuIFBvciBlamVtcGxvLCB2YW1vcyBhIHBvbmVyIGVsIG1hciAoZW4gcmVhbGlkYWQgYWwgYGJhY2tncm91bmRgIGRlbCBgdGhlbWVgKSBkZWwgY29sb3IgYGFsaWNlYmx1ZWAsIHkgYSBsb3MgY29udGluZW50ZXMgbGVzIHBvbmRyZW1vcyB1biBjb2xvciB0ZXJyb3NvIGNvbiBgZmlsbCA9ICJhbnRpcXVld2hpdGUiYC4gQXJyZWdsYW1vcyB1biBwb2NvIGxhcyBncmlkLWxpbmVzIGRlbnRybyBkZWwgYHRoZW1lYCwgeSBsZSBwb25lbW9zIGxhIGVzdHJlbGxpdGEuIE5vIG5vcyB2YSBhIHNhbGlyIGJvbml0by1ib25pdG8gcG9ycXVlIGxvcyBub21icmVzIGRlIGxvcyBwYcOtc2VzIHNlIHZhbiBhIHN1cGVycG9uZXIuIFBvZHLDrWFtb3MgcXVpdGFyIG5vbWJyZXMsIHBlcm8gb3RybyBkw61hOgoKCmBgYHtyLCBvdXQud2lkdGggPSAiOTAlIn0KcCA8LSBwICsgCiAgZ2VvbV9zZihmaWxsID0gImFudGlxdWV3aGl0ZSIpICsKICBnZW9tX3RleHQoZGF0YSA9IHdvcmxkX3BvaW50cywgCiAgICAgICAgICAgIGFlcyh4ID0gWCwgeSA9IFksIGxhYmVsID0gbmFtZSksIAogICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBmb250ZmFjZSA9ICJib2xkIiwgCiAgICAgICAgICAgICAgICBjaGVja19vdmVybGFwID0gVFJVRSwgc2l6ZSA9IDIpICsKICBhbm5vdGF0aW9uX25vcnRoX2Fycm93KGxvY2F0aW9uID0gImJsIiwgd2hpY2hfbm9ydGggPSAidHJ1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBwYWRfeCA9IHVuaXQoMC43NSwgImluIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgcGFkX3kgPSB1bml0KDAuNSwgImluIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBzdHlsZSA9IG5vcnRoX2Fycm93X2ZhbmN5X29yaWVudGVlcmluZykgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSBncmF5KC41KSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDAuNSksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJhbGljZWJsdWUiKSkKcApgYGAKCkhhY2Vtb3Mgb3RyYSB2ZXogdW4gem9vbSBjb24gYHNmOjpjb29yZF9zZigpYAoKCmBgYHtyLCBvdXQud2lkdGggPSAiNzUlIn0KcCArIGNvb3JkX3NmKHhsaW0gPSBjKC0xNS4wMCwgMTUuMDApLCB5bGltID0gYygyMSwgNDcuNDQpLCBleHBhbmQgPSBUUlVFKSArCiAgICBnZ3NwYXRpYWw6OmFubm90YXRpb25fc2NhbGUobG9jYXRpb24gPSAiYmwiLCB3aWR0aF9oaW50ID0gMC41KQpgYGAKCgpDb21vIG5vcyBoYSBxdWVkYWRvIG1lZGlvIGNodWxvLCBzaSBxdWlzacOpcmFtb3MgZ3VhcmRhciBlbCBtYXBhIGhhcsOtYW1vczoKCmBgYHtyLCBldmFsID0gRkFMU0V9Cmdnc2F2ZSgibWFwLnBkZiIpCmdnc2F2ZSgibWFwX3dlYi5wbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDYsIGRwaSA9ICJzY3JlZW4iKQpgYGAKCjxicj4KCi0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKCiMgMy4gTWFzIHR1bmVhZG8KClZhbW9zIGEgaXIgbW9kaWZpY2FuZG8gZWwgbWFwYSwgbm8gbmVjZXNhcmlhbWVudGUgcGFyYSBoYWNlcmxvIG3DoXMgYm9uaXRvLCBzaW5vIHBhcmEgdmVyIGNvbW8gc2UgcHVlZGVuIGhhY2VyIG1hcGFzIGNvbiBgZ2dwbG90MmA6CgojIyMgYcOxYWRpZW5kbyBwdW50b3MKClByaW1lcm8gcXVpZXJvIGHDsWFkaXIgYWwgbWFwYSBkb25kZSBlc3TDoSBtaSBwdWVibG8geSBkb25kZSBlc3TDoSBWYWxlbmNpYSwgdmFtb3MgcXVlIHZveSBhIGHDsWFkaXIgcHVudG9zIGFsIG1hcGEuIAoKUGFyYSBlbGxvLCBwcmltZXJvIG5lY2VzaXRvIHRlbmVyIGxhIGxvY2FsaXphY2nDs24gZGUgZXN0b3MgcHVudG9zIGVuIGVsIG1hcGEsIGFzw60gcXVlIG1lIGZ1aSBhIFtPU00sIE9wZW5TdHJlZXRNYXBdKGh0dHBzOi8vd3d3Lm9wZW5zdHJlZXRtYXAub3JnLyNtYXA9MTQvLTEuOTQyMi8zMS41ODY2JmxheWVycz1DKSB5IGdlb2xvY2FsaWPDqSBsb3MgZG9zIG11bmljaXBpb3MgeSBjb24gZXN0YSBpbmZvcm1hY2nDs24sIHZveSAgYSBjcmVhciB1biBkYXRhLmZyYW1lIGNvbiBsYSBsb25nL2xhdCBkZSBtaSBwdWVibG8geSBkZSBWYWxlbmNpYTogCgoKYGBge3J9CnB1ZWJsb3MgPC0gZGF0YS5mcmFtZSgKICAgICAgICAgICAgICAgbm9tYnJlID0gYygiUGFuY3J1ZG8iLCAiVmFsZW5jaWEiKSwgCiAgICAgICAgICAgICAgIGxvbmdpdHVkZSA9IGMoLTEuMDI4NzgxLCAtMC4zNzU2NTcyKSwKICAgICAgICAgICAgICAgbGF0aXR1ZGUgPSBjKDQwLjc2MTc1LCAzOS40NzUzNCkgKSAgICAgCnB1ZWJsb3MKYGBgCgoKLSBsbyBtYXMgZsOhY2lsIHBhcmEgYcOxYWRpciBwdW50b3MgZXMgdXNhciBgZ2VvbV9wb2ludCgpYAoKYGBge3J9CnBfZXNwICsgCiAgZ2VvbV9wb2ludChkYXRhID0gcHVlYmxvcywgCiAgICAgICAgICAgICBhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlKSwgCiAgICAgICAgICAgICBzaXplID0gMiwgY29sb3IgPSAiZGFya3JlZCIpICsKICBnZW9tX3RleHQoZGF0YSA9IHB1ZWJsb3MsIAogICAgICAgICAgICBhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlLCBsYWJlbCA9IG5vbWJyZSksIAogICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGZvbnRmYWNlID0gImJvbGQiLCBjaGVja19vdmVybGFwID0gVFJVRSwgc2l6ZSA9IDIuNSkKYGBgCgpVdGlsaXphciBgZ2VvbV9wb2ludCgpYCBlcyBmw6FjaWwgeSBmdW5jaW9uYSwgcGVybyBlcyBtw6FzIGZsZXhpYmxlIGNvbnZlcnRpciBlbCBkYXRhLmZyYW1lIGEgdW4gb2JqZXRvIGBzZmAuIEVzdG8gbG8gcG9kZW1vcyBoYWNlciBjb24gbGEgZnVuY2nDs24gYHN0X2FzX3NmKClgLCBkZWZpbmllbmRvIGxhIHByb3llY2Npw7NuIChhcXXDrSBXR1M4NCwgcXVlIGVzIGVsIGPDs2RpZ28gQ1JTICMgNDMyNik6CgoKYGBge3J9CnB1ZWJsb3Nfc2YgPC0gc3RfYXNfc2YocHVlYmxvcywgCiAgICAgICAgICAgICAgICAgICAgICAgY29vcmRzID0gYygibG9uZ2l0dWRlIiwgImxhdGl0dWRlIiksIAogICAgICAgICAgICAgICAgICAgICAgIGNycyA9IDQzMjYsICAjLSBFUFNHOjQzMjYgQ29vcmRlbmFkYXMgR2VvZ3LDoWZpY2FzIFdHUzg0CiAgICAgICAgICAgICAgICAgICAgICAgYWdyID0gImNvbnN0YW50IikKYGBgCgpQYXJhIGRlc3B1w6lzIGHDsWFkaXJsbyBjb21vIHVuYSBjYXBhIG3DoXMgY29uIGBnZW9tX3NmKClgIGEgbnVlc3RybyBncsOhZmljbzogCgpgYGB7cn0KcF9lc3AgKyBnZW9tX3NmKGRhdGEgPSBwdWVibG9zX3NmLCBzaXplID0gMiwgY29sb3IgPSAiZGFya3JlZCIpCmBgYAoKSGFnYW1vcyB6b29tIGNvbiBgY29vcmRfc2ZgCgpgYGB7cn0KcF9lc3AgKyBnZW9tX3NmKGRhdGEgPSBwdWVibG9zX3NmLCBzaXplID0gMiwgY29sb3IgPSAiZGFya3JlZCIpICsgCiAgICAgICAgY29vcmRfc2YoeGxpbSA9IGMoLTE1LjAwLCAxNS4wMCksIHlsaW0gPSBjKDIxLCA0Ny40NCksIGV4cGFuZCA9IFRSVUUpCmBgYAoKCiMjIyBBw7FhZGllbmRvIHBvbMOtZ29ub3MKCkxhIHZlcmRhZCBlcyBxdWUgc2UgaGFjZSBpZ3VhbCBxdWUgY29uIGxvcyBwdW50b3MsIGhheSBxdWUgdXNhciBsYSBmdW5jacOzbiBgZ2VvbV9zZigpYCB5IGEgY29ycmVyLgoKVmFtb3MgYSBhw7FhZGlyIGxvcyBjb250b3Jub3MvcG9sw61nb25vcyBkZSBsYXMgQ0MuQUE6CgoKYGBge3J9Cnp6X0NDQUEgPC0gQ0NBQSAlPiUgc3Rfc2V0X2dlb21ldHJ5KE5VTEwpCnAgKyBnZW9tX3NmKGRhdGEgPSBDQ0FBLCBmaWxsID0gTkEpICsgCiAgICAgIGNvb3JkX3NmKHhsaW0gPSBjKC0xNS4wMCwgMTUuMDApLCB5bGltID0gYygyMSwgNDcuNDQpLCBleHBhbmQgPSBUUlVFKQpgYGAKCkFob3JhIGHDsWFkaW1vcyBsYXMgcHJvdmluY2lhcyBkZSBBbmRhbHVjw61hLCBQRVJPIGNvbG9yZWFkYXMgc2Vnw7puIHN1IHBvYmxhY2nDs24gbWVkaWEgcG9yIG11bmljaXBpby4gUGFyYSBlbGxvOiAKCi0gMS4gcHJpbWVybyBjYXJnYW1vcyBkYXRvcyBkZSBwb2JsYWNpw7NuCgpgYGB7cn0KSU5FX3BhZHJvbl9tdW5pXzk2XzIwIDwtIHBqcHYuZGF0b3MuMDE6OnBvYl9tdW5pXzE5OTZfMjAyMAojSU5FX3BhZHJvbl9tdW5pXzk2XzIwIDwtIHBqcHYyMDIwLjAxOjpwanBfZGF0YV9wb2JfbXVuXzE5OTZfMjAxOQpwb2JfYW5kYWx1Y2lhIDwtIElORV9wYWRyb25fbXVuaV85Nl8yMCAlPiUgCiAgICAgICAgICAgICAgICAgZmlsdGVyKHllYXIgPT0gMjAyMCkgJT4lIAogICAgICAgICAgICAgICAgIGZpbHRlcihpbmVfY2NhYS5uID09ICJBbmRhbHVjw61hIikgJT4lCiAgICAgICAgICAgICAgICAgZ3JvdXBfYnkoaW5lX3Byb3YubikgJT4lIG11dGF0ZShuX3B1ZWJsb3MgPSBuX2Rpc3RpbmN0KGluZV9tdW5pLm4pKSAlPiUKICAgICAgICAgICAgICAgICBtdXRhdGUocG9iX3Byb3YgPSBzdW0ocG9iX3RvdGFsLCBuYS5ybSA9IFRSVUUpKSAlPiUgdW5ncm91cCgpICU+JQogICAgICAgICAgICAgICAgIG11dGF0ZShwb2JfbWVkaWFfcHVlYmxvcyA9IHBvYl9wcm92L25fcHVlYmxvcykgJT4lIAogICAgICAgICAgICAgICAgIHNlbGVjdChpbmVfcHJvdi5uLCBpbmVfcHJvdiwgcG9iX21lZGlhX3B1ZWJsb3MsIHBvYl9wcm92LCBuX3B1ZWJsb3MpICU+JQogICAgICAgICAgICAgICAgIGRpc3RpbmN0KCkgJT4lIHVuZ3JvdXAoKQpgYGAKCgoKLSAyLiBkZXNwdcOpcyBjYXJnYW1vcyBsaW5kZXMgcHJvdmluY2lhbGVzIChlbiByZWFsaWRhZCB5YSBsb3MgdGVuZW1vcykKCmBgYHtyfQojIFByb3ZpbmNpYXMgPC0gUHJvdmluY2lhcwp6el9Qcm92aW5jaWFzIDwtIFByb3ZpbmNpYXMgJT4lIHN0X3NldF9nZW9tZXRyeShOVUxMKQpgYGAKCgotIDMuIGZ1c2lvbmFtb3MgbG9zIGRhdG9zIGRlIHBvYmxhY2nDs24geSBsb3MgbGluZXMgcHJvdmluY2lhbGVzOgoKYGBge3J9Cmdlb19wb2JfYW5kYWx1Y2lhIDwtIGxlZnRfam9pbihwb2JfYW5kYWx1Y2lhLCBQcm92aW5jaWFzLCBieSA9IGMoImluZV9wcm92IiA9ICJpbmVfcHJvdiIpKQpnZW9fcG9iX2FuZGFsdWNpYSA8LSBsZWZ0X2pvaW4ocG9iX2FuZGFsdWNpYSwgUHJvdmluY2lhcykKYGBgCgoKLSA0LiBmaW5hbG1lbnRlIGHDsWFkaW1vcyBsYXMgcHJvdmluY2lhcyBjb2xvcmVhZGFzIHBvciBwb2JsYWNpw7NuIGFsIG1hcGE6CgpgYGB7cn0KcCArIGdlb21fc2YoZGF0YSA9IENDQUEsIGZpbGwgPSBOQSkgKwogICAgZ2VvbV9zZihkYXRhID0gZ2VvX3BvYl9hbmRhbHVjaWEsIGFlcyhnZW9tZXRyeSA9IGdlb21ldHJ5LCBmaWxsID0gbl9wdWVibG9zKSkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzX2MoYWxwaGEgPSAuNCkgKwogICAgZ2VvbV9zZihkYXRhID0gcHVlYmxvc19zZiwgc2l6ZSA9IDIsIGNvbG9yID0gImRhcmtyZWQiKSArCiAgICBjb29yZF9zZih4bGltID0gYygtMTUuMDAsIDI1LjAwKSwgeWxpbSA9IGMoMjEsIDU3LjQ0KSwgZXhwYW5kID0gVFJVRSkKYGBgCgotIHZhbW9zIGHDsWFkaXIgdGFtYmnDqW4gMyBtdW5pY2lwaW9zIGVzcGHDsW9sZXMgc2VsZWNjaW9uYWRvcyBhbCBhemFyOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KcHVlYmxvc18zIDwtIElHTl9ub21lbmNsYV9tdW5pICU+JSAKICAgICAgICAgICAgIHNhbXBsZV9uKDMpICU+JSAKICAgICAgICAgICAgIHNlbGVjdChOT01CUkVfQUNUVUFMLCBQUk9WSU5DSUEsIExPTkdJVFVEX0VUUlM4OSwgTEFUSVRVRF9FVFJTODkpICU+JQogICAgICAgICAgICAgc3RfYXNfc2YoY29vcmRzID0gYygiTE9OR0lUVURfRVRSUzg5IiwgIkxBVElUVURfRVRSUzg5IiksIGNycyA9IDQzMjYsIGFnciA9ICJjb25zdGFudCIsIHJlbW92ZSA9IEZBTFNFKQpgYGAKCi0gQnVlbm8sIGVuIHJlYWxpZGFkIG5vIGxvcyB2b3kgYSBlbGVnaXIgdGFuIHRhbiBhbCBhemFyOgoKYGBge3IsIGV2YWwgPSBUUlVFfQpwdWVibG9zXzMgPC0gSUdOX25vbWVuY2xhX211bmkgJT4lIAogICAgICAgICAgICAgZmlsdGVyKE5PTUJSRV9BQ1RVQUwgJWluJSBjKCJQYW5jcnVkbyIsICJBbGJ1cnF1ZXJxdWUiLCAiUG9sZW50aW5vcyIpKSAlPiUgCiAgICAgICAgICAgICBzZWxlY3QoTk9NQlJFX0FDVFVBTCwgUFJPVklOQ0lBLCBMT05HSVRVRF9FVFJTODksIExBVElUVURfRVRSUzg5KSAlPiUKICAgICAgICAgICAgIHN0X2FzX3NmKGNvb3JkcyA9IGMoIkxPTkdJVFVEX0VUUlM4OSIsICJMQVRJVFVEX0VUUlM4OSIpLCBjcnMgPSA0MzI2LCBhZ3IgPSAiY29uc3RhbnQiLCByZW1vdmUgPSBGQUxTRSkKYGBgCgoKYGBge3J9CnBfZXNwIDwtIHAgKyBnZW9tX3NmKGRhdGEgPSBDQ0FBLCBmaWxsID0gTkEpICsKICAgICAgICAgZ2VvbV9zZihkYXRhID0gZ2VvX3BvYl9hbmRhbHVjaWEsIGFlcyhnZW9tZXRyeSA9IGdlb21ldHJ5LCBmaWxsID0gbl9wdWVibG9zKSkgKwogICAgICAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhhbHBoYSA9IC40KSArCiAgICAgICAgIGdlb21fc2YoZGF0YSA9IHB1ZWJsb3NfMywgc2l6ZSA9IDIsIGNvbG9yID0gImRhcmtyZWQiKSArCiAgICAgICAgIGdncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IHB1ZWJsb3NfMywgYWVzKHggPSBMT05HSVRVRF9FVFJTODksIHkgPSBMQVRJVFVEX0VUUlM4OSwgbGFiZWwgPSBOT01CUkVfQUNUVUFMKSkgKwogICAgICAgICBjb29yZF9zZih4bGltID0gYygtMTAuMDAsIDcuMDApLCB5bGltID0gYygzNSwgNDUpLCBleHBhbmQgPSBUUlVFKQpwX2VzcApgYGAKCgojIyMgQ29uIGxvcyBtYXBhcyB5IGdyw6FmaWNvcyBudW5jYSBzZSBhY2FiYSEhIQoKQ2FuYXJpYXMgbm8gc2UgdmUuIMK/TGEgc2l0dWFtb3MgZW4gZWwgbWFwYT8gVXNhcmVtb3MgYHNmOjpzdF9iYm94KClgCgoKYGBge3J9CmNhbmFyaWFzIDwtIFByb3ZpbmNpYXMgJT4lIGZpbHRlcihpbmVfcHJvdiAlaW4lIGMoMzUsMzgpKQpwZW5pbnN1bGEgPC0gUHJvdmluY2lhcyAlPiUgZmlsdGVyKCAhKGluZV9wcm92ICVpbiUgYygzNSwgMzgpKSApCm15X3NoaWZ0IDwtIHN0X2Jib3gocGVuaW5zdWxhKVtjKDEsMildLSAoc3RfYmJveChjYW5hcmlhcylbYygxLDIpXSkgKyBjKC0yLjQsIC0xLjEpCmNhbmFyaWFzJGdlb21ldHJ5IDwtIGNhbmFyaWFzJGdlb21ldHJ5ICsgbXlfc2hpZnQKc3RfY3JzKGNhbmFyaWFzKSAgPC0gc3RfY3JzKHBlbmluc3VsYSkKcGVuaW5zdWxhX3lfY2FuYXJpYXMgPC0gcmJpbmQocGVuaW5zdWxhLCBjYW5hcmlhcykKCnAxIDwtIGdncGxvdCgpICsgZ2VvbV9zZihkYXRhID0gcGVuaW5zdWxhX3lfY2FuYXJpYXMpCnAyIDwtIGdncGxvdCgpICsgZ2VvbV9zZihkYXRhID0gcGVuaW5zdWxhKSArIGdlb21fc2YoZGF0YSA9IGNhbmFyaWFzLCBmaWxsID0gInB1cnBsZSIpIAojY29vcmRfc2YoeGxpbSA9IGMoLTEyLjAwLCA0LjAwKSwgeWxpbSA9IGMoMzMsIDQ0KSwgZXhwYW5kID0gVFJVRSkKCnAxICsgcDIgCmBgYAoKCi0gaGFnYW1vcyB6b29tCgpgYGB7cn0KZ2dwbG90KCkgKyBnZW9tX3NmKGRhdGEgPSBwZW5pbnN1bGEpICsKICAgICAgICAgICBnZW9tX3NmKGRhdGEgPSBjYW5hcmlhcykgKwogICAgICAgICAgIGNvb3JkX3NmKHhsaW0gPSBjKC0xMi4wMCwgNC4wMCksIHlsaW0gPSBjKDMzLCA0NCksIGV4cGFuZCA9IFRSVUUpCmBgYAoKCi0gcG9uZ2Ftb3MgQ2FuYXJpYXMsIHBpbnRhZGEgZW4gcm9qbywgZW4gbnVlc3RybyBtYXBhIAoKCmBgYHtyfQpwX2VzcCArIGdlb21fc2YoZGF0YSA9IGNhbmFyaWFzLCBmaWxsID0gInJlZCIpICsKICAgICAgICAgY29vcmRfc2YoeGxpbSA9IGMoLTEyLjAwLCA1LjAwKSwgeWxpbSA9IGMoMzAsIDQ0KSwgZXhwYW5kID0gVFJVRSkKYGBgCgoKIyMjIGVsIHLDrW8gRWJybwoKUHVlcyBxdWUgcXVpZXJvIHBvbmVyIGVsIFLDrW8gRWJybyBlbiBlbCBtYXBhLiBMb2dpY2FtZW50ZSBuZWNlc2lvIGxvcyBkYXRvcyBnZW9ncsOhZmljb3MgZGVsIHJpbyBFYnJvLgoKYGBge3J9CiNyaW9zIDwtIHJpbzo6aW1wb3J0KCJodHRwczovL2dpdGh1Yi5jb20vcGVyZXpwNDQvYXJjaGl2b3NfZG93bmxvYWQvYmxvYi9tYXN0ZXIvcmlvX2Vicm8ucmRzP3Jhdz10cnVlIikKenpfcmlvcyA8LSByaW9zX2VzcGFueWEgJT4lIHN0X3NldF9nZW9tZXRyeShOVUxMKQpuYW1lcyhyaW9zX2VzcGFueWEpCmBgYAoKCi0gVm95IGEgc2l0dWFyIGVuIGVsIG1hcGEgNCByw61vczoKCmBgYHtyLCByZXN1bHRzID0gImhpZGUifQojLSBubyBoYWNlIGZhbHRhIHBlcm8KY29kX3Jpb3NfZ3Vlbm9zIDwtIGMoOTEzNzU3LCA5MTM2ODcsIDkxMzY4NSwgOTEzNjE5KQpub21icmVfcmlvc19ndWVub3MgPC0gYygiUGFuY3J1ZG8iLCAiSmlsb2NhIiwgIkphbMOzbiIsICJFYnJvIikKCnJpb3NfZ3Vlbm9zIDwtIHJpb3NfZXNwYW55YSAlPiUgZmlsdGVyKENvZF9VbmkgJWluJSBjb2Rfcmlvc19ndWVub3MpCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IHBlbmluc3VsYSkgKyBnZW9tX3NmKCkgKwogICAgZ2VvbV9zZihkYXRhID0gcmlvc19ndWVub3MsIGNvbG9yID0gImxpZ2h0Ymx1ZSIsIGx3ZCA9IDEuMykgKwogICAgbGFicyhzdWJ0aXRsZSA9ICJNYXBhIGRlIEVzcGHDsWEgY29uIDQgcsOtb3MgR8O8ZW5vcyIsIGNvbG9yID0gIiIpCiMgY29sID0gc2Y6OnNmLmNvbG9ycygyLCBhbHBoYSA9IDAuNSkKYGBgCgotIMK/Q8OzbW8gc2UgbGxhbWFyw6EgZWwgcsOtbyBwZXF1ZcOxw61uPwoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IHBlbmluc3VsYSkgKyBnZW9tX3NmKCkgKwogICAgZ2VvbV9zZihkYXRhID0gcmlvc19ndWVub3MsIGNvbG9yID0gImxpZ2h0Ymx1ZSIsIGx3ZCA9IDEuMykgKwogICAgZ2VvbV9wb2ludChkYXRhID0gcHVlYmxvcywgYWVzKHggPSBsb25naXR1ZGUsIHkgPSBsYXRpdHVkZSksIHNpemUgPSAyLCBjb2xvciA9ICJkYXJrcmVkIikgKwogICAgZ2VvbV90ZXh0KGRhdGEgPSBwdWVibG9zLCBhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlLCBsYWJlbCA9IG5vbWJyZSksIGNvbG9yID0gImRhcmtibHVlIiwgZm9udGZhY2UgPSAiYm9sZCIsIGNoZWNrX292ZXJsYXAgPSBUUlVFLCBzaXplID0gMi41KSArIAogICAgbGFicyhzdWJ0aXRsZSA9ICJNYXBhIGRlIEVzcGHDsWEgY29uIDQgcsOtb3MgR8O8ZW5vcyIsIGNvbG9yID0gIiIpCmBgYAoKCjxicj4KCi0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKCgojIDQuICBMYXlvdXRzCgpDb24gbG9zIG1hcGFzIHNlIHB1ZWRlbiBoYWNlciBjb21wb3NpY2lvbmVzIHBhcmEgZGVzdGFjYXIgZWwgbWVuc2FqZSBxdWUgc2UgcXVpZXJlIG1vc3RyYXIuIFNlIHB1ZWRlbiBoYWNlciBlc3RhcyBjb21wb3NpY2lvbmVzIGRlIHZhcmlhcyBtYW5lcmFzOgoKCi0gVXNpbmcg4oCcZ3JvYnPigJ0sIGkuZS4gZ3JhcGhpYyBvYmplY3RzIGZyb20gZ2dwbG90Miwgd2hpY2ggY2FuIGJlIGluc2VydGVkIGluIHRoZSBwbG90IHJlZ2lvbiB1c2luZyBwbG90IGNvb3JkaW5hdGVzOwoKLSBVc2FuZG8gZWwgcGFxdWV0ZSBgcGF0Y2h3b3JrYCBvIGVsIHBhcXVldGUgYGNvd3Bsb3RgCgpgYGB7ciwgZXZhbCA9IFRSVUV9Cm5hbWVzKHdvcmxkKQpnd29ybGQgPC0gZ2dwbG90KGRhdGEgPSB3b3JsZCkgKwogIGdlb21fc2YoYWVzKGZpbGwgPSBpbmNvbWVfZ3JwKSkgKwogICMgZ2VvbV9zZihhZXMoZmlsbCA9IHJlZ2lvbl93YikpICsKICBnZW9tX3JlY3QoeG1pbiA9IC0xNSwgeG1heCA9IDI1LjAwLCB5bWluID0gMjEsIAogICAgICAgICAgICB5bWF4ID0gNTcuNDQsIGZpbGwgPSBOQSwgY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDEuNSkgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJhenVyZSIpKSArCiAgbGFicyhzdWJ0aXRsZSA9ICJNYXBhIGRlbCBtdW5kbyIpICsKICBzY2FsZV9maWxsX2Rpc2NyZXRlKGd1aWRlID0gIm5vbmUiKQoKZ3dvcmxkCmBgYAoKCmBgYHtyLCBldmFsID0gVFJVRX0KZ19ldXJvcGUgPC0gZ2dwbG90KGRhdGEgPSB3b3JsZCkgKwogICAgZ2VvbV9zZigpICsKICAgIGdlb21fc2YoZGF0YSA9IENDQUEsIGZpbGwgPSBOQSkgKwogICAgZ2VvbV9zZihkYXRhID0gZ2VvX3BvYl9hbmRhbHVjaWEsIGFlcyhnZW9tZXRyeSA9IGdlb21ldHJ5LCBmaWxsID0gbl9wdWVibG9zKSkgKwogICAgZ2VvbV9zZihkYXRhID0gcHVlYmxvc18zLCBzaXplID0gMiwgY29sb3IgPSAiZGFya3JlZCIpICsKICAgIGdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IHB1ZWJsb3NfMywgYWVzKHggPSBMT05HSVRVRF9FVFJTODksIHkgPSBMQVRJVFVEX0VUUlM4OSwgbGFiZWwgPSBOT01CUkVfQUNUVUFMKSkgKwogICAgY29vcmRfc2YoeGxpbSA9IGMoLTE1LjAwLCAyNS4wMCksIHlsaW0gPSBjKDIxLCA1Ny40NCksIGV4cGFuZCA9IFRSVUUpICsKICAgIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJhenVyZSIpKSArCiAgICBsYWJzKHN1YnRpdGxlID0gIk1hcGEgZGUgRXVyb3BhIE9jY2lkZW50YWwiKSArCiAgICBzY2FsZV9maWxsX2NvbnRpbnVvdXMoZ3VpZGUgPSAibm9uZSIpCmdfZXVyb3BlCmBgYAoKCgpgYGB7ciwgZXZhbCA9IFRSVUV9CmxpYnJhcnkocGF0Y2h3b3JrKQpnd29ybGQgKyBnX2V1cm9wZSArIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMSwgMikpCmBgYAoKCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0KCjxicj4KCiMgNS4gUGFxdWV0ZSBgTWFwdmlld2AKClZhbW9zIGEgdmVyIGxvIGbDoWNpbCBxdWUgZXMgaGFjZXIgdW4gbWFwYSBjb24gZWwgcGFxdWV0ZSBbYG1hcHZpZXdgXShodHRwczovL3Itc3BhdGlhbC5naXRodWIuaW8vbWFwdmlldy8pLgoKUG9yIGVqZW1wbG8sIHRlbsOtYW1vcyBnZW9sb2NhbGl6YWRvcyBhIFBhbmNydWRvIHkgVmFsZW5jaWEuIFZlw6Ftb3Nsb3MgZW4gdW4gZ3LDoWZpY28gY29uIGBtYXB2aWV3KClgCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwdWVibG9zIDwtIGRhdGEuZnJhbWUobm9tYnJlID0gYygiUGFuY3J1ZG8iLCAiVmFsZW5jaWEiKSwgbG9uZ2l0dWRlID0gYygtMS4wMjg3ODEsIC0wLjM3NTY1NzIpLCBsYXRpdHVkZSA9IGMoNDAuNzYxNzUsIDM5LjQ3NTM0KSkgIy0gRVRSUzg5CnB1ZWJsb3Nfc2YgPC0gc3RfYXNfc2YocHVlYmxvcywgY29vcmRzID0gYygibG9uZ2l0dWRlIiwgImxhdGl0dWRlIiksIGNycyA9IDQzMjYsIGFnciA9ICJjb25zdGFudCIpICMtIEVQU0c6NDMyNiBDb29yZGVuYWRhcyBHZW9ncsOhZmljYXMgV0dTODQKbWFwdmlldzo6bWFwdmlldyhwdWVibG9zX3NmKQpgYGAKCgpPdHJvIGVqZW1wbG8gY29uIGxhcyBnZW9tZXRyw61hcyBkZSBsYXMgQ0MuQUEsIGF1bnF1ZSBlcyB1bmEgYWZyZW50YSBwYXJhIFBhbmNydWRvCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQptYXB2aWV3OjptYXB2aWV3KHJpb3MpCmBgYAoKUGFyYSB2ZXIgcXVlIHNlIHB1ZWRlIGhhY2VyIGNvbiBgbWFwdmlld2AgdmFtb3MgYSBhw7FhZGlyIGFsZ3VuYSBpbmZvcm1hY2nDs24gYSBDQ0FBLCBwb3IgZWplbXBsbyBlbCBuw7ptZXJvIGRlIHByb3ZpbmNpYXMsIGVsIG7Dum1lcm8gZGUgbXVuaWNpcGlvcyB5IGxhIHBvYmxhY2nDs24gdG90YWwgZW4gZWwgYcOxbyAyMDE3OgoKCmBgYHtyLCBldmFsID0gRkFMU0V9CklORV9wYWRyb25fbXVuaV85Nl8yMCA8LSBwanB2LmRhdG9zLjAxOjpwb2JfbXVuaV8xOTk2XzIwMjAKI0lORV9wYWRyb25fbXVuaV85Nl8yMCA8LSBwanB2MjAyMC4wMTo6cGpwX2RhdGFfcG9iX211bl8xOTk2XzIwMTkKaW5mb19DQ0FBcyA8LSBJTkVfcGFkcm9uX211bmlfOTZfMjAgJT4lIAogICAgICAgICAgICAgIGZpbHRlcih5ZWFyID09IDIwMTcpICU+JSAKICAgICAgICAgICAgICAjZmlsdGVyKHBvYmxhY2lvbiA9PSAiVG90YWwiKSAlPiUgCiAgICAgICAgICAgICAgZ3JvdXBfYnkoaW5lX2NjYWEubikgJT4lIAogICAgICAgICAgICAgIG11dGF0ZShuX3Byb3YgPSBuX2Rpc3RpbmN0KGluZV9wcm92KSkgJT4lIAogICAgICAgICAgICAgIG11dGF0ZShuX3B1ZWJsb3MgPSBuX2Rpc3RpbmN0KGluZV9tdW5pKSkgJT4lIAogICAgICAgICAgICAgIG11dGF0ZShwb2JfQ0NBQSA9IHN1bShwb2JfdG90YWwsIG5hLnJtID0gVFJVRSkpICU+JSB1bmdyb3VwKCkgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KGluZV9jY2FhLCBpbmVfY2NhYS5uLCBuX3Byb3YsIG5fcHVlYmxvcywgcG9iX0NDQUEpICU+JQogICAgICAgICAgICAgIGRpc3RpbmN0KCkgJT4lIHVuZ3JvdXAoKQpgYGAKCgpKdW50YW1vcyBlbCBkYXRhLmZyYW1lIGBpbmZvX0NDQUFzYCBjb24gZWwgZGF0YWZyYW1lIGNvbiBsYXMgZ2VvbWV0csOtYXMgYGBDQ0FBYDoKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpDQ0FBX2Nvbl9pbmZvIDwtIGxlZnRfam9pbihDQ0FBLCBpbmZvX0NDQUFzKQpgYGAKCgpZIHZhbW9zIGEgZ3JhZmljYXJsbyB5YSBjb24gYG1hcHZpZXdgOgoKCmBgYHtyLCBldmFsID0gRkFMU0V9Cm1hcHZpZXc6Om1hcHZpZXcoQ0NBQV9jb25faW5mbywgCiAgICAgICAgICAgICAgICAgemNvbCA9IGMoImluZV9jY2FhLm4iLCAibl9wcm92IiwgIm5fcHVlYmxvcyIsICJwb2JfQ0NBQSIpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gInRvbWF0byIsIGNvbC5yZWdpb25zID0gInB1cnBsZSIsIGx3ZCA9IDMsCiAgICAgICAgICAgICAgICAgY2V4ID0gIm5fcHVlYmxvcyIpCmBgYAoKCmBgYHtyLCBldmFsID0gRkFMU0V9Cm1hcHZpZXc6Om1hcHZpZXcoQ0NBQV9jb25faW5mbywgCiAgICAgICAgICAgICAgICAgemNvbCA9IGMoImluZV9jY2FhLm4iLCAibl9wcm92IiwgIm5fcHVlYmxvcyIsICJwb2JfQ0NBQSIpLAogICAgICAgICAgICAgICAgY2V4ID0gIm5fcHVlYmxvcyIpCmBgYAoKU2UgcHVlZGVuIHBvbmVyIHZhcmlhcyBjYXBhczoKCmBgYHtyLCBldmFsID0gRkFMU0V9Cm1hcHZpZXcobGlzdChDQ0FBX2Nvbl9pbmZvLCBwdWVibG9zX3NmKSwgbGF5ZXIubmFtZSA9IGMoIkNDLkFBIiwgIjIgbXVuaWNpcGlvcyBjaGFjaGlzIikpCmBgYAoKTyBtZWpvciBhc8OtOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbWFwdmlldzo6bWFwdmlldyhDQ0FBX2Nvbl9pbmZvLCB6Y29sID0gYygiaW5lX2NjYWEubiIsICJuX3Byb3YiLCAibl9wdWVibG9zIiwgInBvYl9DQ0FBIikpICsKbWFwdmlldzo6bWFwdmlldyhwdWVibG9zX3NmKSAKYGBgCgoKWSBtdWNoYXMgbWFzIGNvc2FzLCBwb3IgZWplbXBsbyBbTWFwIHBvcC11cHNdKGh0dHBzOi8vci1zcGF0aWFsLmdpdGh1Yi5pby9tYXB2aWV3L2FydGljbGVzL2FydGljbGVzL21hcHZpZXdfMDQtcG9wdXBzLmh0bWwpICBvIFtJbWFnZSBwb3AtdXBzXShodHRwczovL3Itc3BhdGlhbC5naXRodWIuaW8vbWFwdmlldy9hcnRpY2xlcy9hcnRpY2xlcy9tYXB2aWV3XzA0LXBvcHVwcy5odG1sKQoKCi0gc2UgcHVlZGUgdXNhciBlbCBvcGVyYWRvciBgICU+JWAgY29uIGxhcyBmdW5jaW9uZXMgZGUgYHNmYAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KQ0NBQV9jb25faW5mbyAlPiUgc3RfdW5pb24gJT4lIG1hcHZpZXcKYGBgCgoKCjxicj4KCgojIyMgSW50ZWdyYWNpw7NuIGNvbiBlbCBwYXF1ZXRlIGBtYXBlZGl0YAoKSW50ZWdyYWNpb24gY29uIFtgbWFwZWRpdGBdKGh0dHBzOi8vd3d3LnItc3BhdGlhbC5vcmcvci8yMDE3LzA2LzA5L21hcGVkaXRfMC0yLTAuaHRtbCkKCgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KI2luc3RhbGwucGFja2FnZXMoIm1hcGVkaXQiKQpsaWJyYXJ5KG1hcGVkaXQpCm15X21hcCA8LSBtYXB2aWV3KHB1ZWJsb3Nfc2YpCmRyYXdGZWF0dXJlcyhteV9tYXAsIGVkaXRvciA9ICJsZWFmcG0iKSAgICMtIGdvb2QKYGBgCgpQYXJhIHJlY3VwZXJhciBlbCBtYXBhIGVzIG1lam9yIGFzw606CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KG1hcGVkaXQpCm15c19kaWJ1aml0b3MgPC0gZWRpdE1hcChtYXB2aWV3KHB1ZWJsb3Nfc2YpKQptYXB2aWV3KG15c19kaWJ1aml0b3MkZmluaXNoZWQpCmBgYAoKCgoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tCgo8YnI+CgoKIyA2LiBQYXF1ZXRlIGBsZWFmbGV0YAoKCkVsIHBhcXVldGUgW2BsZWFmbGV0YF0oaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9sZWFmbGV0LykgcGVybWl0ZSB1dGlsaXphciBlbiBSIHVuYSBkZSBsYSBsaWJyZXLDrWFzIGRlIEphdmFTY3JpcHQgbcOhcyBjb25vY2lkYXM6IExlYWZsZXQuCgoKUGFyYSBhYnJpciB1biBtYXBhIGxlYWZsZXQ6CgpgYGB7ciwgZXZhbCA9IEZBTFNFIH0KbGlicmFyeShsZWFmbGV0KQpsZWFmbGV0KCkgJT4lIGFkZFRpbGVzKCkgJT4lIGxlYWZlbTo6YWRkTW91c2VDb29yZGluYXRlcygpCmBgYAoKCgpQb2RlbW9zIGNlbnRyYXIgZWwgbWFwYSwgZGVjaWRpciBlbCBuaXZlbCBkZSB6b29tIHkgcG9uZXJsZSBtYXJjYWRvcmVzIHkgcG9wdXBzOgoKCmBgYHtyfQptIDwtIGxlYWZsZXQoKSAlPiUKICBhZGRUaWxlcygpICU+JSAKICBzZXRWaWV3KGxuZyA9IC0xLjAyODc4MTAsIGxhdCA9IDQwLjc2MTc1LCB6b29tID0gNikgJT4lIAogIGFkZE1hcmtlcnMobG5nID0gLTEuMDI4NzgxMCwgbGF0ID0gNDAuNzYxNzUsIHBvcHVwID0gIlBhbmNydWRvIikgJT4lIAogIGFkZFBvcHVwcyhsbmcgPSAtMC4zNzU2NTcyLCBsYXQgPSAzOS40NzUzNCwgcG9wdXAgPSAiVmFsZW5jaWEiKQptCmBgYAoKClZhbW9zIGEgYcOxYWRpcmxlIGVsZW1lbnRvcywgcG9yIGVqZW1wbG8gcHVudG9zLCBwb3IgZWplbXBsbyBtaXMgMiBtdW5pY2lwaW9zOgoKCmBgYHtyLCBldmFsID0gRkFMU0V9CmxlYWZsZXQoKSAlPiUgCiAgYWRkVGlsZXMoKSAlPiUgIyBBZGQgZGVmYXVsdCBPcGVuU3RyZWV0TWFwIG1hcCB0aWxlcwogIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IHB1ZWJsb3Nfc2YpCiAgI2xlYWZlbTo6YWRkSG9tZUJ1dHRvbihleHQgPSBleHRlbnQoYnJld2VyaWVzOTEpLCBsYXllci5uYW1lID0gInB1ZWJsb3MiKQpgYGAKCk8sIHBvciBlamVtcGxvIHBvbMOtZ29ub3MgY29tbyBsYXMgQ0MuQUE6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpsZWFmbGV0KGRhdGEgPSBDQ0FBKSAlPiUgYWRkVGlsZXMoKSAlPiUKICBhZGRQb2x5Z29ucyhmaWxsQ29sb3IgPSB0b3BvLmNvbG9ycygxOCwgYWxwaGEgPSBOVUxMKSwgc3Ryb2tlID0gRkFMU0UpCmBgYAoKCgpTaSB0ZSBoYSBndXN0YWRvIHR1IG1hcGEgbGVhZmxldCB5IHF1aWVyZXMgZ3VhcmRhcmxvLCBwdWVkZXMgZ3VhcmRhcmxvIGNvbW8gdW4gYXJjaGl2byBodG1sIGFzw606CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpodG1sdG9vbHM6OnNhdmVfaHRtbChscGxvdCwgImxlYWZsZXQuaHRtbCIpCmBgYAoKCgpTZSBwdWVkZW4gaGFjZXIgYmFzdGFudGVzIG3DoXMgY29zYXMsIHBvciBlamVtcGxvLCBwYXJhIGhhY2VyIGNvcm9wbGV0YXMgY29uIGxlYWZsZXQgcHVlZGVzIGlyIFthcXXDrV0oaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9sZWFmbGV0L2Nob3JvcGxldGhzLmh0bWwpLiBPdHJvIGVqZW1wbG8gZGUgYW7DoWxpc2lzIGNvbiBsZWFmbGV0IFthcXXDrV0oaHR0cHM6Ly93d3cuamxhLWRhdGEubmV0L2VuZy9sZWFmbGV0LWluLXItdGlwcy1hbmQtdHJpY2tzL2luZGV4Lmh0bWwpLgoKCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0KCjxicj4KCgojIDcuIE9wZXJhY2lvbmVzIGdlb23DqXRyaWNhcwoKCkxhcyBvcGVyYWNpb25lcyBnZW9tw6l0cmljYXMgcXVlIHBvZGVtb3MgaGFjZXIgY29uIGBzZmAgY29uIGRhdG9zIGVzcGFjaWFsZXMgdmVjdG9yaWFsZXMgc2UgcHVlZGVuIGRpdmlkaXIgZW4gdHJlcyBncnVwb3M6CgotIDEuIGZ1bmNpb25lcyBxdWUgZGV2dWVsdmVuIHVuIHZhbG9yIHF1ZSByZXN1bWUgYWxndW5hIHByb3BpZWRhZCBkZSBsYSBnZW9tZXRyw61hOiAgCgogICAgICAtICBgc3RfYXJlYSgpYCAgICA6IGNhbGN1bGEgZWwgYXJlYSAKICAgICAgLSAgYHN0X2xlbmdodCgpYCAgOiBjYWxjdWxhIGVsIHBlcsOtbWV0cm8gICAKICAgICAgLSAgYHN0X2Rpc3RhbmNlKClgOiBjYWxjdWxhIGxhIGRpc3RhbmNpYSAoZW50cmUgZG9zIGdlb21ldHLDrWFzKSAgIAogICAgCi0gMi4gZnVuY2lvbmVzIHF1ZSBjaGVxdWVhbiBzaSBzZSBjdW1wbGUgdW5hIGNvbmRpY2nDs246ICAKCiAgICAgIC0gIEVuIDEgZ2VvbWV0csOtYTogYHN0X2lzX3ZhbGlkKClgICAgIDogY2hlcXVlYSBzaSBsYSBnZW9tZXRyaWEgZXMgdsOhbGlkYSAgIAogICAgICAKICAgICAKICAgICAgLSBFbiAyIGdlb21ldHLDrWFzOiBwb3IgZWplbXBsbywgYHN0X2ludGVyc2VjdHMoQSwgQilgIGNoZXF1ZWEgc2kgZG9zIGdlb21ldHLDrWFzIHNlIGludGVyc2VjdGFuLiBPdHJhcyBmdW5jaW9uZXM6IGBzdF9kaXNqb2ludCgpYCwgYHN0X3RvdWNoZXMoKWAsIGBzdF9jcm9zc2VzKClgLCBgc3Rfd2l0aGluKClgLCBgc3RfY29udGFpbnMoKWAsIGBzdF9vdmVybGFwcygpYCwgYHN0X2NvdmVycygpYCwgYHN0X2VxdWFscygpYCwgYHN0X2lzX3dpdGhpbl9kaXN0YW5jZSgpYCwgeSBhbGd1bmEgbcOhcwogICAgCiAgICAKLSAzLiBmdW5jaW9uZXMgcXVlIGNyZWFuIHVuYSAgbnVldmEgZ2VvbWV0csOtYTogIAoKICAgICAgLSBFbiAxIGdlb23DqXRyaWE6IHBvciBlamVtcGxvLCBgc3RfY2VudHJvaWQoKWA6IGNhbGN1bGEgZWwgY2VudHJvaWRlLiBPdHJhcyBmdW5jaW9uZXM6IGBzdF9idWZmZXIoKWAsIGBzdF9ib3VuZGFyeSgpYCAsIGBzdF9jb252ZXhfaHVsbCgpYCwgYHN0X3Zvcm9ub2koKWAKICAgICAgCiAgICAgIAogICAgICAtIEVuIDIgZ2VvbcOpdHJpYXM6IHBvciBlamVtcGxvLCBgc3RfaW50ZXJzZWN0aW9uKClgIGNhbGN1bGEgbGEgaW50ZXJzZWNjaW9uLiBPdHJhcyBmdW5jaW9uZXM6IGBzdF9jb21iaW5lKClgLCBgc3RfdW5pb24oKWAsIGBzdF9kaWZmZXJlbmNlKClgCgo8YnI+CgojIyAgNy4xICBPcGVyYWNpb25lcyBnZW9tw6l0cmljYXMgKEVqZW1wbG9zKQoKCiMjIyBMYXMgNSBwcm92aW5jaWFzIG3DoXMgZ3JhbmRlcwoKVmFtb3MgYSBjb2xvcmVhciBsYXMgNSBwcm92aW5jaWFzIGVzcGHDsW9sYXMgY29uIG3DoXMgdGVycml0b3JpbywgcGFyYSBlbGxvIHRlbmVtb3MgcXVlIGNhbGN1bGFyIGVsIMOhcmVhIGNvbiBgc2Y6OnN0X2FyZWEoKWA6CgpgYGB7cn0KUHJvdmljaWFzXzUgPC0gUHJvdmluY2lhcyAlPiUgCiAgICAgICAgICAgICAgIG11dGF0ZShhcmVhID0gc3RfYXJlYSguKSkgJT4lIAogICAgICAgICAgICAgICBtdXRhdGUoYXJlYWttMiA9IHVuaXRzOjpzZXRfdW5pdHMoYXJlYSwga21eMikpICU+JSAKICAgICAgICAgICAgICAgYXJyYW5nZShkZXNjKGFyZWFrbTIpKSAlPiUgCiAgICAgICAgICAgICAgIHNsaWNlKDE6NSkKZ2dwbG90KCkgKyBnZW9tX3NmKGRhdGEgPSBQcm92aW5jaWFzKSArIGdlb21fc2YoZGF0YSA9IFByb3ZpY2lhc181LCBmaWxsID0gInB1cnBsZSIpCmBgYAoKQmllbiwgcGVybyBtZWpvciBzaSBwb25lbW9zIGVsIG5vbWJyZSBkZSBsYXMgcHJvdmluY2lhcwoKYGBge3J9ClByb3ZfNV9jZW50cm9pZGVzIDwtIHN0X2NlbnRyb2lkKFByb3ZpY2lhc181KSAKUHJvdl81X2NlbnRyb2lkZXMgPC0gIGNiaW5kKFByb3ZfNV9jZW50cm9pZGVzLCBzdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZChQcm92XzVfY2VudHJvaWRlcyRnZW9tZXRyeSkpKQoKZ2dwbG90KCkgKyBnZW9tX3NmKGRhdGEgPSBQcm92aW5jaWFzKSArIAogICAgICAgICAgIGdlb21fc2YoZGF0YSA9IFByb3ZpY2lhc181LCBmaWxsID0gInB1cnBsZSIpICsKICAgICAgICAgICBnZW9tX3RleHQoZGF0YSA9IFByb3ZfNV9jZW50cm9pZGVzLCAKICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBYLCB5ID0gWSwgbGFiZWwgPSBpbmVfcHJvdi5uKSwKICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBmb250ZmFjZSA9ICJib2xkIiwgY2hlY2tfb3ZlcmxhcCA9IFRSVUUsIHNpemUgPSAyLjUpICsKICAgICAgICAgIGxhYnModGl0bGUgPSAiTGFzIDUgcHJvdmluY2lhcyBlc3Bhw7FvbGFzIG3DoXMgZXh0ZW5zYXMiLCB4ID0gIiIsIHkgPSAiIikgCmBgYAoKCgojIyMgRGlzdGFuY2lhcyAoYSBtaSBwdWVibG8pCgpDYWxjdWxlbW9zIGxhcyBkaXN0YW5jaWFzIGVudHJlIHB1bnRvcywgcG9yIGVqZW1wbG8gZW50cmUgbG9zIGNlbnRyb2lkZXMgZGUgbGFzIDUyIHByb3ZpbmNpYXMgZXNwYcOxb2xhczoKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpQcm92X2NlbnRyb2lkZXMgPC0gc3RfY2VudHJvaWQoUHJvdmluY2lhcykgClByb3ZfY2VudHJvaWRlc19YWSA8LSBjYmluZChQcm92X2NlbnRyb2lkZXMsIHN0X2Nvb3JkaW5hdGVzKHN0X2NlbnRyb2lkKFByb3ZfY2VudHJvaWRlcyRnZW9tZXRyeSkpKQpkaXN0YW5jaWFzIDwtIHN0X2Rpc3RhbmNlKFByb3ZfY2VudHJvaWRlc19YWSwgYnlfZWxlbWVudCA9IEZBTFNFKSAjLSBkZXZ1ZWx2ZSB1bmEgbWF0cml6CmBgYAoKClF1aWVybyB2ZXIgcXVlIG11bmljaXBpbyBlc3TDoSBtYXMgbGVqb3MgZGUgbWkgcHVlYmxvLiBDYXJnbyBkYXRvcyBkZSBsb3MgbXVuaWNpcGlvcyBjb24gbGF0aXR1ZC1sb25naXR1ZCBlbiBgRVRSUzg5YDoKCgpgYGB7cn0KbXVuaWNpcGlvcyA8LSBJR05fbm9tZW5jbGFfbXVuaQptdW5pY2lwaW9zIDwtIG11bmljaXBpb3MgJT4lIHNlbGVjdChDT0RfSU5FLCBQUk9WSU5DSUEsIE5PTUJSRV9BQ1RVQUwsIExPTkdJVFVEX0VUUlM4OSwgTEFUSVRVRF9FVFJTODkpCmBgYAoKVGVuZ28gcXVlIGNvbnZlcnRpciBlbCBkZiBhIHVuIHNwYXRpYWwtZGYsIHkgdGVuZ28gcXVlIHRlbmVyIGN1aWRhZG8gY29uIGVsIENSUyBxdWUgbGUgcG9uZ28uIFtBcXXDrV0oaHR0cHM6Ly9zcGF0aWFscmVmZXJlbmNlLm9yZy9yZWYvZXBzZy80MjU4LykgdmkgcXVlIEVUUlM4OSBlcyBlcXVpdmFsZW50ZSBhIGBFUFNHIFByb2plY3Rpb24gNDI1OGAsIGFzw60gcXVlLCBlc3Blcm8gaGFiZXJsbyBoZWNobyBiaWVuLCB5YSB2b3JlbToKCgpgYGB7cn0KbXVuaWNpcGlvc19zZiA8LSBzdF9hc19zZihtdW5pY2lwaW9zLCBjb29yZHMgPSBjKCJMT05HSVRVRF9FVFJTODkiLCAiTEFUSVRVRF9FVFJTODkiKSwgY3JzID0gc3RfY3JzKDQyNTgpKQpgYGAKClZhbW9zIGEgdmVyIHF1ZSBmaWxhIG9jdXBhIG1pIHB1ZWJsby4gT2N1cGEgbGEgZmlsYSBuwrogNjY5NCwgYXPDrSBxdWUgdm95IGEgY3JlYXIgdW4gZGYgc8OzbG8gY29uIG1pIHB1ZWJsbyB5IGx1ZWdvIGNhbGN1bGFyIGxhcyBkaXN0YW5jaWFzIGNvbiBgZnM6OnN0X2Rpc3RhbmNlKClgOgoKCmBgYHtyfQpwYW5jcnVkb19zZiA8LSBtdW5pY2lwaW9zX3NmICU+JSBzbGljZSg2Njk0KQpnMSA8LSBzdF9nZW9tZXRyeShtdW5pY2lwaW9zX3NmKQpnMV9wYW5jcnVkbyA8LSBzdF9nZW9tZXRyeShwYW5jcnVkb19zZikgIy0gZGVqbyBzb2xvIGxhIGdlb21ldHJpYQoKIy0gZGlzdGFuY2lhc19hX1BhbmNydWRvIDwtIG1hcHBseShzdF9kaXN0YW5jZSwgZzEsIGcxX3BhbmNydWRvKSAlPiUgYXMudmVjdG9yKSAjLSBjb24gUi1iYXNlCmRpc3RhbmNpYXNfYV9QYW5jcnVkbyA8LSBtYXAyKGcxLCBnMV9wYW5jcnVkbywgc3RfZGlzdGFuY2UpICU+JSBhc192ZWN0b3IoKSAgICAgICAgICMtIGNvbiBwdXJycgpkaXN0YW5jaWFzX2FfUGFuY3J1ZG8gPC0gYXMuZGF0YS5mcmFtZShkaXN0YW5jaWFzX2FfUGFuY3J1ZG8pCmRmX2Nvbl9kaXN0YW5jaWFzX2FfUGFuY3J1ZG8gPC0gYmluZF9jb2xzKG11bmljaXBpb3Nfc2YsIGRpc3RhbmNpYXNfYV9QYW5jcnVkbykgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShLbXMgPSBkaXN0YW5jaWFzX2FfUGFuY3J1ZG8gKiAxMDApICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcnJhbmdlKEttcykKYGBgIAoKRWwgcHVlYmxvIG3DoXMgY2VyY2FubyBhIFBhbmNydWRvIGVzIGByIGRmX2Nvbl9kaXN0YW5jaWFzX2FfUGFuY3J1ZG8kTk9NQlJFX0FDVFVBTFsyXWAgcXVlIGVzdMOhIGEgYHIgZGZfY29uX2Rpc3RhbmNpYXNfYV9QYW5jcnVkbyRLbXNbMl1gLCBlc28gc2ksIGVuIGxpbmVhIHJlY3RhLiBQb3IgbGEgY2FycmV0ZXJhIGhheSB1biBwb2NvIG3DoXMsIHNlZ8O6biBHb29nbGUgTWFwcyA3LDEga2lsw7NtZXRyb3MuIEEgdmVyIHNpIHBvZGVtb3MgY2FsY3VsYXJsbyBsdWVnbyBjb24gUiB1c2FuZG8gT1NNLgoKRWwgcHVlYmxvIG3DoXMgbGVqYW5vIGEgUGFuY3J1ZG8sIHNpIGNvbnNpZGVyYW1vcyBzw7NsbyBsYSBwZW7DrW5zdWxhIGVzIE11eMOtYSBlbiBsYSBwcm92aW5jaWEgZGUgQSBDb3J1w7FhLCBxdWUgZXN0w6EgYSA4NTEsNjUga2lsw7NtZXRyb3MgKGVuIGxpbmVhIHJlY3RhKS4gRW4gY29jaGUsIHNlZ8O6biBHb29nbGUgTWFwcywgOTI0IEttcy4KCjxicj4KCgojIyMgTXVuaWNpcGlvcyBwb3IgbG9zIHF1ZSBwYXNhIGVsIHLDrW8gRWJybwoKClB1ZXMgZXNvLCBxdWUgbWUgYXBldGVjZSBzYWJlciBwb3IgZG9uZGUgcGFzYSBlbCBFYnJvIHkgZWwgUGFuY3J1ZG8uCgoxLiBQcmltZXJvIGNhcmdvIGdlb21ldHLDrWFzIGRlIGxvcyBtdW5pY2lwaW9zIGVzcGHDsW9sZXMuIExhcyBsaW5kZXMgbXVuaWNpcGFsZXMgcHJvdmllbmVuIGRlbCBwYXF1ZXRlIFtgTEFVMmJvdW5kYXJpZXM0c3BhaW5gXShodHRwczovL2dpdGh1Yi5jb20vcGVyZXpwNDQvTEFVMmJvdW5kYXJpZXM0c3BhaW4pCgpgYGB7cn0KbXVuaV94eCA8LSBtdW5pY2lwaW9zXzIwMjAgJT4lIHN0X3NldF9nZW9tZXRyeShOVUxMKQptdW5pICAgIDwtIG11bmljaXBpb3NfMjAyMApgYGAKCgpWYW1vcyBhIHF1ZWRhcm5vcyBzb2xvIGNvbiBsYSBFc3Bhw7FhIHBlbmluc3VsYXI6CgpgYGB7cn0KI3p6IDwtIG11bmlfeHggJT4lIGdyb3VwX2J5KGluZV9jY2FhLCBpbmVfY2NhYS5uKSAlPiUgY291bnQoKQptdW5pX2JvcnJhciA8LSBjKCJJbGxlcyBCYWxlYXJzIiwgIkNhbmFyaWFzIikKbXVuaSA8LSBtdW5pICU+JSBmaWx0ZXIoIShpbmVfY2NhYS5uICVpbiUgbXVuaV9ib3JyYXIpKQpgYGAKCgoyLiBBaG9yYSBsb3MgZGF0b3MgZGUgcsOtb3MgUGFuY3J1ZG8sIEppbG9jYSwgSmFsw7NuIHkgRWJybwoKCmBgYHtyLCBldmFsID0gRkFMU0UsIGVjaG8gPSBGQUxTRX0KIy0gYXNpIGNyZWUgcmlvc19lc3BhbnlhIHkgcmlvX2Vicm9fNAojcmlvcyA8LSBzdF9yZWFkKGhlcmU6OmhlcmUoImRhdG9zIiwgIm1hcGFzIiwgIlJpb3MiLCAiTV9SaW9zX3YyLnNocCIpKQojc2F2ZVJEUyhyaW9zLCAicmlvc19lc3BhbnlhLnJkcyIpICMtIGxvIGd1YXJkZQpjb2Rfcmlvc19ndWVub3MgPC0gYyg5MTM3NTcsIDkxMzY4NywgOTEzNjg1LCA5MTM2MTkpCm5vbWJyZV9yaW9zX2d1ZW5vcyA8LSBjKCJQYW5jcnVkbyIsICJKaWxvY2EiLCAiSmFsw7NuIiwgIkVicm8iKQpyaW9fZWJyb180IDwtIHJpb3MgJT4lIGZpbHRlcihDb2RfVW5pICVpbiUgY29kX3Jpb3NfZ3Vlbm9zKQpjbGFzcyhyaW9fZWJyb180KQojc2F2ZVJEUyhyaW9fZWJyb180LCAicmlvX2Vicm9fNC5yZHMiKQpgYGAKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpyaW9fZWJyb180IDwtIHJlYWRfcmRzKGhlcmU6OmhlcmUoImRhdG9zIiwgICJtYXBhcyIsICJSaW9zIiwgInJpb19lYnJvLnJkcyIpKQpgYGAKClZhbW9zIGEgcG9uZXIgdG9kb3MgbG9zIHRyYW1vcyBkZSBsb3MgNCByw61vcyBqdW50b3MuIENvbiBgc3VtbWFyaXNlKClgCgpgYGB7cn0KcmlvX2Vicm9fNF94eCA8LSByaW9fZWJyb180ICU+JSBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkKcmlvX2Vicm9fNF94IDwtIHJpb19lYnJvXzQgJT4lIHNlbGVjdChOb21fUmlvLCBMb25nX1Jpb19tLCBnZW9tZXRyeSkgJT4lIGdyb3VwX2J5KE5vbV9SaW8pICU+JSBzdW1tYXJpemUoKQpgYGAKCgpIYWdhbW9zIHlhIHVuIG1hcGEgZGUgbG9zIGxpbmRlcyBtdW5pY2lwYWxlcywgbG9zIHLDrW9zIHkgdm95IGEgc2l0dWFyIFBhbmNydWRvIHkgVmFsZW5jaWE6CgpgYGB7ciwgZWNobyA9IEZBTFNFfQpwdWVibG9zICA8LSBkYXRhLmZyYW1lKG5vbWJyZSA9IGMoIlBhbmNydWRvIiwgIlZhbGVuY2lhIiksIGxvbmdpdHVkZSA9IGMoLTEuMDI4NzgxLCAtMC4zNzU2NTcyKSwgbGF0aXR1ZGUgPSBjKDQwLjc2MTc1LCAzOS40NzUzNCkgKSAjLSBFVFJTODkKYGBgCgoKCmBgYHtyfQp0aGVtZV9zZXQoY293cGxvdDo6dGhlbWVfbWFwKCkpCnAgPC0gZ2dwbG90KG11bmkpICsgCiAgICAgZ2VvbV9zZihjb2xvciA9ICJncmV5IiwgZmlsbCA9ICJ3aGl0ZSIsIGx3ZCA9IDAuMDEpICsKICAgICBnZW9tX3NmKGRhdGEgPSByaW9fZWJyb180X3gsIGNvbG9yID0gImxpZ2h0Ymx1ZSIsIGx3ZCA9IDEuNikgKwogICAgIGdlb21fcG9pbnQoZGF0YSA9IHB1ZWJsb3MsIGFlcyh4ID0gbG9uZ2l0dWRlLCB5ID0gbGF0aXR1ZGUpLCBzaXplID0gMywgY29sb3IgPSAiZGFya3JlZCIpICsKICAgICBnZW9tX3RleHQoZGF0YSAgPSBwdWVibG9zLCBhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlLCBsYWJlbCA9IG5vbWJyZSksIGNvbG9yID0gImRhcmtyZWQiLAogICAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiLCBjaGVja19vdmVybGFwID0gVFJVRSwgc2l6ZSA9IDIuNSkgKwogICAgIGxhYnModGl0bGUgPSAiUi1NYXBhIGNvbiA0IHLDrW9zIGfDvGVub3MiLAogICAgICAgICAgY2FwdGlvbiA9ICJEYXRvcyBwcm92ZW5pZW50ZXMgZGVsIHBhcXVldGUgTEFVMmJvdW5kYXJpZXM0c3BhaW4iKSArICAKICAgICAgICAgIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJhbGljZWJsdWUiKSkKcApgYGAKClZlbmdhLCBjYWxjdWxhbW9zIGxhcyBpbnRlcnNlY2Npb25lcyBjb24gYHNmOjpzdF9pbnRlcnNlY3RzKClgIHBlcm8gYW50ZXMgdGVuZW1vcyBxdWUgcG9uZXIgbG9zIDIgZGZzIGVuIGVsIG1pc21vIENSUzoKCmBgYHtyLCBldmFsID0gVFJVRX0KbXVuaV9hIDwtIG11bmkgJT4lIHN0X3RyYW5zZm9ybShjcnMgPSBzdF9jcnMocmlvX2Vicm9fNF94KSkKenpfYSA8LSBzdF9pbnRlcnNlY3RzKG11bmlfYSwgcmlvX2Vicm9fNF94LCBzcGFyc2UgPSBGQUxTRSwgcHJlcGFyZWQgPSBUUlVFKQpgYGAKCkFob3JhIHlhIHNpIHNlbGVjY2lvbmFtb3MgYXF1ZWxsb3MgbXVuaWNpcGlvcyBwb3IgbG9zIHF1ZSBwYXNhIGVsIEVicm8uIExvIGhhZ28gY29uIFItYmFzZToKCmBgYHtyLCBldmFsID0gVFJVRX0KbXVuaV9lYnJvIDwtIG11bmlfYVt6el9hLF0KYGBgCgoKUGFyZWNlIHF1ZSBoYXkgYHIgbnJvdyhtdW5pX2Vicm8pYCBtdW5pY2lwaW9zIHBvciBsb3EgZXUgcGFzYSBlbCBFYnJvLiBBIHZlciBzaSBub3Mgc2FsZSBlbCBtYXBhCgpgYGB7cn0KcCArIGdlb21fc2YoZGF0YSA9IG11bmlfZWJybykgKwogICAgZ2VvbV9zZihkYXRhID0gcmlvX2Vicm9fNF94LCBjb2xvciA9ICJsaWdodGJsdWUiLCBsd2QgPSAwLjI1KQpgYGAKCk5vLCBubyB2b3kgYSBvYnRlbmVyIGxvcyBtdW5pY2lwaW9zIHBvciBsb3MgcXVlIHBhc2EgZWwgcmlvIFBhbmNydWRvLiBFc28gZXN0w6EgTUFMISEhIQoKPGJyPgogIAotLS0tLS0tLS0tLS0tCiAgCjxicj4KCgojIEJpYmxpbwoKLSBbR2VvY29tcHV0YXRpb24gd2l0aCBSXShodHRwczovL2dlb2NvbXByLnJvYmlubG92ZWxhY2UubmV0L2luZGV4Lmh0bWwpLiBkZSBSLiBMb3ZlbGFjZSB5IG90cm9zLiBBIGJvb2tkb3duIG9uIGdlb2dyYXBoaWMgZGF0YSBhbmFseXNpcywgdmlzdWFsaXphdGlvbiBhbmQgbW9kZWxpbmcgd2l0aCBSLiAKCi0gW0ludHJvIHRvIEdJUyBhbmQgU3BhdGlhbCBBbmFseXNpc10oaHR0cHM6Ly9tZ2ltb25kLmdpdGh1Yi5pby9TcGF0aWFsL2luZGV4Lmh0bWwpIGRlIE0uIEdpbW9uZC4gQnVlbiBsaWJybwoKLSBbSW50cm9kdWN0aW9uIHRvIEdJUyB3aXRoIFJdKGh0dHBzOi8vd3d3Lmplc3Nlc2FkbGVyLmNvbS9wb3N0L2dpcy13aXRoLXItaW50cm8vP3V0bV9jb250ZW50PWJ1ZmZlcjA3MmFlJnV0bV9tZWRpdW09c29jaWFsJnV0bV9zb3VyY2U9dHdpdHRlci5jb20mdXRtX2NhbXBhaWduPWJ1ZmZlcikgZGUgSmVzc2UgU2FkbGVyLCBzZWfDum4gW01pbGVzIE1jQmFpbl0oaHR0cHM6Ly90d2l0dGVyLmNvbS9NaWxlc01jQmFpbikgdW5hIGRlIGxhcyBtZWpvcmVzIGludHJvZHVjY2lvbmVzIGEgR0lTIGVuIFIuCgoKLSBbVmnDsWV0YXMgZGVsIHBhcXVldGUgYHNmYF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3NmLykKCgotIFtDb29yZGluYXRlIFJlZmVyZW5jZSBTeXN0ZW1zXShodHRwczovL2RhdGFjYXJwZW50cnkub3JnL29yZ2FuaXphdGlvbi1nZW9zcGF0aWFsLzAzLWNycy9pbmRleC5odG1sKS4gVHV0b3JpYWwgZGUgRGF0YSBDYXJwZW50cnkgc29icmUgQ1JTLgoKCi0gW0ludHJvZHVjdGlvbiB0byBHZW9zcGF0aWFsIENvbmNlcHRzXShodHRwczovL2RhdGFjYXJwZW50cnkub3JnL29yZ2FuaXphdGlvbi1nZW9zcGF0aWFsLykgZGUgRGF0YSBDYXJwZW50cnkuCgoKLSBVbmEgc2VyaWUgZGUgcG9zdHMgc29icmU6RHJhd2luZyBiZWF1dGlmdWwgbWFwcyBwcm9ncmFtbWF0aWNhbGx5IHdpdGggUiwgc2YgYW5kIGdncGxvdDIuIFtCYXNpY3NdKGh0dHBzOi8vd3d3LnItc3BhdGlhbC5vcmcvL3IvMjAxOC8xMC8yNS9nZ3Bsb3QyLXNmLmh0bWwpLFtMYXllcnNdKGh0dHBzOi8vd3d3LnItc3BhdGlhbC5vcmcvL3IvMjAxOC8xMC8yNS9nZ3Bsb3QyLXNmLTIuaHRtbCkgeSBbTGF5b3V0c10oaHR0cHM6Ly93d3cuci1zcGF0aWFsLm9yZy8vci8yMDE4LzEwLzI1L2dncGxvdDItc2YtMy5odG1sKQoKLSBVbiBlamVtcGxvIGVuIGVsIHF1ZSBzZSB2YSBjb25zdHJ1eWVuZG8gbGEgW1dpbmtlbCB0cmlwZWwgcHJvamVjdGlvbl0oaHR0cHM6Ly93aWxrZWxhYi5vcmcvcHJhY3RpY2FsZ2cvYXJ0aWNsZXMvV2lua2VsX3RyaXBlbC5odG1sKQoKLSBVbiBbcG9zdF0oaHR0cHM6Ly93d3cuamxhLWRhdGEubmV0L2VuZy9sZWFmbGV0LWluLXItdGlwcy1hbmQtdHJpY2tzL2luZGV4Lmh0bWwpIHNvYnJlIGxhIHV0aWxpemFjacOzbiBkZSBsZWFmbGV0CgotIFBhcXVldGUgW2BvcGVucm91dGVzZXJ2aWNlYF0oaHR0cHM6Ly9naXNjaWVuY2UuZ2l0aHViLmlvL29wZW5yb3V0ZXNlcnZpY2Utci9hcnRpY2xlcy9vcGVucm91dGVzZXJ2aWNlLmh0bWwpIHBhcmEgY2FsY3VsYXIgcnV0YXMgY29uIFtvcGVucm91dGVzZXJ2aWNlXShodHRwczovL29wZW5yb3V0ZXNlcnZpY2Uub3JnLykuCgotIFtTaXN0ZW1hcyBkZSBJbmZvcm1hY2nDs24gR2VvZ3LDoWZpY2FdKGh0dHBzOi8vdm9sYXlhLmdpdGh1Yi5pby9saWJyby1zaWcvaW5kZXguaHRtbCkuIFVuIGxpYnJvIGxpYnJlIGRlIFbDrWN0b3IgT2xheWEKCjxicj48YnI+Cgo=