En este artículo revisaremos como crear redes semánticas usando R, y
en el proceso veremos cómo hacer algunas de las tareas más comunes al
procesar texto.
Paquetes necesarios
Los paquetes usaremos son:
- tidytext: El paquete principal de esta ocasión.
Contiene herramientas para procesar texto de manera limpia e
intuitiva.
- tidyverse: Un meta paquete que llama a otros más
(dplyr, readr, purrr, etc.) que nos facilitan la lecuta, procesamiento y
visualización de datos.
- igraph y ggraph: Paquetes que
amplían las funciones del paquete ggplot2 del tidyverse. En particular,
graph hará el trabajo de crear las redes semánticas.
- tm: Herramientas de minería de texto.
Si no contamos con las paquetes, los instalamos con
install.packages(), como es usual.
library(tidytext)
library(tidyverse)
library(tm)
library(igraph)
library(ggraph)
Continuemos obteniendo el texto que analizaremos.
Nuestro texto: El amigo manso
Para este ejemplo usaremos el texto de la novela “El amigo manso” de
Benito Pérez Galdós, publicada originalmente en 1882 y disponible en
Project Gutenberg por formar parte del dominio público.
Descargaremos esta novela como un archivo .txt. He
creado una copia del archivo en un repositorio de Github, para facilitar
el proceso.
Usamos download.file() para descargar el archivo.
download.file(url = "https://raw.githubusercontent.com/jboscomendoza/rpubs/master/red_semantica/55563-0.txt", destfile = "55563-0.txt")
Exploramos nuestro documento con read_lines() de
readr, pidiendo después las primeras 15 líneas con
head().
read_lines("55563-0.txt") %>%
head(15)
## [1] "The Project Gutenberg EBook of El amigo Manso, by Benito Pérez Galdós"
## [2] ""
## [3] "This eBook is for the use of anyone anywhere at no cost and with"
## [4] "almost no restrictions whatsoever. You may copy it, give it away or"
## [5] "re-use it under the terms of the Project Gutenberg License included"
## [6] "with this eBook or online at www.gutenberg.org/license"
## [7] ""
## [8] ""
## [9] "Title: El amigo Manso"
## [10] ""
## [11] "Author: Benito Pérez Galdós"
## [12] ""
## [13] "Release Date: September 16, 2017 [EBook #55563]"
## [14] ""
## [15] "Language: Spanish"
Considerando que estamos trabajando con una novela completa, de un
par cientos de páginas de largo, será necesario que ver nuestro
documento fuera de R para explorar más fácilmente su estructura. Usamos
file.show() para esto.
La estructura de nuestro texto
Después de revisar el documento nos damos cuenta de un par de
cosas.
Primero, el texto está estructurado en renglones con un límite de
ancho, es decir cuando el texto llega a un ancho determinado (menos de
80 caracteres), ocurre un salto de línea. Por lo tanto tenemos muchos
enunciados interrumpido a la mitad de ellos y no contamos con párrafos.
Sin embargo, para indicar cada final de un párrafo hay una línea en
blanco.
Esto es importante porque una línea de 80 caracteres no contiene
suficiente texto como para armar redes semánticas que tengan
sentido.
Segundo, además del texto de la novela, tenemos una sección
introductoria al texto y al final la información legal de Project
Gutenberg. El texto que nos interesa esta entre estos dos.
Considerando lo anterior, necesitamos hacer dos cosas: importar sólo
el texto que nos interesa y estructurarlo por párrafos.
Creando párrafos
Revisando nuestro documento, sabemos que la novela empieza en la
línea 154 y termina en la 10612. Usamos esta infomación en
read_lines de *readr. skip = 153 para empezar
en la línea 154, y n_max = (10612 - 153) para leer hasta la
línea que originalmente sería la 10612, pero tomando en cuenta que
empezamos a leer desde la 153.
Usamos trimws() con map() de purrr
para quitar los espacios en blanco al inicio y final de cada línea de
texto. Esto hará más consistente nuestro texto.
Sabemos que los párrafos de nuestro texto están indicados por un
renglón en blanco, así que usaremos esta información para reestructurar
nuestro documento por párrafos, no renglones de ancho más o menos
fijo.
Cambiamos el contenido de estos renglones por
“salto” usando ifelse(), así
tendremos una palabra clave única para indicar saltos de página. Una vez
hecho esto, unimos todo el texto en una sola cadena de texto con
paste0() usando el argumento collapse = " ",
para asegurarnos que habrá un espacio entre cada renglón unido.
Ahora, separamos de vuelta el texto, usando strsplit()
con el argumento split = "_salto_". De este modo separamos
cada que ocurre un salto de párrafo, obteniendo entonces párrafos.
Finalmente, usamos otra vez trimws() con
map() y convertimos nuestro resultados a un data frame con
data.frame(), usando el argumento
stringsAsFactors = FALSE para que nuestro texto se conserve
como de tipo character. Pasamos los anterior a un tibble, con
tbl_df() de dplyr, y ponemos “texto” como nombre
de la columna resultante, usando names() <-.
manso <-
read_lines("55563-0.txt", skip = 153, n_max = (10612 - 153)) %>%
map(trimws) %>%
ifelse(. == "", "_salto_", .) %>%
paste0(., collapse = " ") %>%
strsplit(split = "_salto_") %>%
map(trimws) %>%
data.frame(stringsAsFactors = FALSE) %>%
tbl_df() %>%
{
names(.) <- "texto"
.
}
## Warning: `tbl_df()` was deprecated in dplyr 1.0.0.
## Please use `tibble::as_tibble()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
Como es posible que después procesemos datos con estructuras
similares, definimos un par de funciones.
Una para leer nuestro texto.
leer_texto <- function(archivo, inicio, final) {
read_lines(archivo, skip = inicio, n_max = (final - inicio)) %>%
map_chr(trimws)
}
Otra para crear párrafos.
crear_parrafos <- function(texto) {
texto %>%
map(trimws) %>%
ifelse(. == "", "_salto_", .) %>%
paste0(., collapse = " ") %>%
strsplit(split = "_salto_") %>%
map(trimws) %>%
data.frame(stringsAsFactors = FALSE) %>%
tbl_df() %>%
{
names(.) <- "texto"
.
}
}
Por cierto, separar y unir son tareas que realizaremos mucho en este
ejemplo.
Quitando renglones vacíos
Ahora quitamos los renglones vacíos y los espacios en blanco al
principio y final de cada renglón, usando filter() y
mutate_all() de dplyr, con ayuda de
trimws().
manso <-
manso %>%
filter(!texto %in% c(" ", "")) %>%
mutate_all(trimws)
Naturalmente, una función nos ahorrara trabajo en el futuro.
borrar_vacios <- function(libro_vacios) {
libro_vacios %>%
filter(!texto %in% c(" ", "")) %>%
mutate_all(trimws)
}
Obteniendo capítulos.
Otra que cosa que descubrimos al revisar nuestro documento, es que
los capítulos de la novela están indicados por números romanos, cada uno
en su propio renglón. Si queremos agrupar los párrafos que hemos creado
en capítulos, lo cual será conveniente para análisis posteriores, este
será nuestro punto de referencia
Lo primero que necesitamos es una manera de encontrar estos números
romanos. Para esto, usamos regex.
Creamos una expresión regular que capture todos los renglones de
manso en los que su único contenido sean números
romanos. Sabemos que los números romanos son letras mayúsculas así que
podemos usar [[:upper:]]. También sabemos que es lo único
que aparece en ese renglón, así que usamos ^ para indicar
que el texto que deseamos capturar inicia con una mayúscula, y
$ para indicar que termina con una mayúscula. Por último,
usamos el cuantificador + para que nuestra regex capture
cadenas de texto de largo 1 o más.
Nuestra regex luciría así: "^[[:upper:]]+$".
Usamos grepl() con filter() de
dplyr para verificar.
manso %>%
filter(grepl("^[[:upper:]]+$", texto))
## # A tibble: 50 × 1
## texto
## <chr>
## 1 I
## 2 II
## 3 III
## 4 IV
## 5 V
## 6 VI
## 7 VII
## 8 VIII
## 9 IX
## 10 X
## # … with 40 more rows
Luce bien, capturamos cincuenta renglones, que es el número de
capítulo de manso, del I al L.
Ahora, usamos mutate() de dplyr e
ifelse() para crear una nueva columna llamada
capitulo.
Buscamos en la columna texto los renglones que
captura nuestra expresión regular, y en los casos que esto es verdadero,
mandamos el texto encontrado a la columna capitulo.
Después, llenamos los renglones debajo de este con su contenido, usando
fill() de tidyr, etiquetando así a todos los párrafos con
el número de capítulo que les corresponde. Por último, usamos
filter() para quitar los renglones con el número de
capítulo.
manso <-
manso %>%
mutate(capitulo = ifelse(grepl("^[[:upper:]]+$", texto), texto, NA)) %>%
fill(capitulo) %>%
filter(texto != capitulo)
Creamos una función para encontrar capítulos, para simplificar la
vida a nuestro yo del futuro.
encontrar_capitulos <- function(libro) {
libro %>%
mutate(capitulo = ifelse(grepl("^[[:upper:]]+$", texto), texto, NA)) %>%
fill(capitulo) %>%
filter(texto != capitulo)
}
Ahora sí, estamos listos para continuar.
Creando tokens: bigramas.
Como crearemos una red semántica conectando palabras, necesitamos
segmentar nuestro texto por parejas de palabra, es decir, n-gramas en
los que n es igual dos. Estos casos de n-grama son conocidos como
bigramas. Este es el token o unidad de texto de
nuestro análisis.
Para esta tarea usaramos la función unnest_tokens() de
tidytext, con los argumentos token = "ngram" y
n = 2. Tomamos la columna “texto” como entrada y obtenemos
“bigrama” de salida.
manso_bigrama <-
manso %>%
unnest_tokens(input = "texto", output = "bigrama", token = "ngrams", n = 2)
Así, obtenemos un data frame con un bigrama por renglón. Nota que el
número de capítulo nos ha ayudado a identificar a agruparlos, sin esta
información, tendríamos problemas de duplicación.
## # A tibble: 88,635 × 2
## capitulo bigrama
## <chr> <chr>
## 1 I yo no
## 2 I no existo
## 3 I yo no
## 4 I no existo
## 5 I existo y
## 6 I y por
## 7 I por si
## 8 I si algún
## 9 I algún desconfiado
## 10 I desconfiado ó
## # … with 88,625 more rows
Podemos explorar cuáles son los bigramas más comunes.
manso_bigrama %>%
count(bigrama, sort = T)
## # A tibble: 55,014 × 2
## bigrama n
## <chr> <int>
## 1 de la 599
## 2 lo que 275
## 3 en la 250
## 4 á la 247
## 5 que no 242
## 6 en el 238
## 7 de los 211
## 8 que se 192
## 9 que me 182
## 10 de su 164
## # … with 55,004 more rows
Parece que nuestros bigramas más comunes son conjunciones,
preposiciones y artículos. Esto es un problema. Si dejamos estos
bigramas para formar nuestra red semántica, obtendremos una red muy
“enredada”, de la cual podremos extraer poca información. Hay que
solucionar esta situación
Quitando palabras huecas
Las palabras que aportan poca información semántica, como
conjunciones, preposiciones y artículos son conocidas como
palabras huecas.
Para quitarlas de nuestro texto, contamos con la ayuda de la función
stopwords() de tm. Si llamamos a esta función con
el argumento kind = "es", nos devolverá un vector con un
listado de palabras huecas en español.
stopwords(kind = "es") %>% head(15)
## [1] "de" "la" "que" "el" "en" "y" "a" "los" "del" "se"
## [11] "las" "por" "un" "para" "con"
Podríamos usar este vector con filter() si nuestros
datos fueran palabras, no bigramas. Así que separamos nuestros bigramas
en palabra uno y dos, con
separate() de tidyr, y entonces filtramos.
Además, necesitamos una palabra por columna para crear las redes
semánticas, de modo que es algo que tendríamos que hacer de todos modos.
Usamos count() de dplyr para obtener la frecuencia
de cada bigrama.
manso_bigrama <-
manso_bigrama %>%
separate(bigrama, into = c("uno", "dos"), sep = " ") %>%
filter(!uno %in% stopwords(kind = "es")) %>%
filter(!dos %in% stopwords(kind = "es")) %>%
count(uno, dos)
Definimos también una función para la generación de bigramas sin
palabras huecas.
generar_bigramas <- function(libro_parrafo) {
libro_parrafo %>%
unnest_tokens(input = "texto", output = "bigrama", token = "ngrams", n = 2) %>%
separate(bigrama, into = c("uno", "dos"), sep = " ") %>%
filter(!uno %in% stopwords("es")) %>%
filter(!dos %in% stopwords("es")) %>%
count(uno, dos)
}
Hemos hecho el conteo de palabras porque crearemos una red que
muestre la intensidad con la que se relacionan las
palabras, cuyo indicador será la frecuencia con la que parejas
de palabras aparecen en el texto. ¡Ahora sí, a crear nuestra red
semántica!
Creando una red semántica
Empezamos filtrando los bigramas con una frecuencia muy baja. Nos
quedamos con los que aparecen cinco veces o más.
Después, usamos las función graph_from_data_frame() de
igraph para convertir nuestros datos a un formato apropiado
para generar redes semánticas.
Hecho esto, usamos ggraph() del paquete con el mismo
nombre para crear nuestra red. Esta función funciona con el mismo
sistema que ggplot2, por lo tanto, tenemos que indicar los
geoms para armar un gráfico. Usamos
geom_edge_link() para indicar las conexiones,
geom_node_point() para nodos, y
geom_node_text() con el argumento
aes(label = name) para mostrar las palabras.
Notarás que dentro de geom_edge_link() hemos usado la
función arrow(). Esta creará las flechas marcando la
direccionalidad de las relaciones.
Veamos que obtenemos. Usamos set.seed() para obtener
siempre la misma versión de la red.
set.seed(175)
manso_bigrama %>%
filter(n >= 5) %>%
graph_from_data_frame() %>%
ggraph() +
geom_edge_link(arrow = arrow(type = "closed", length = unit(.075, "inches"))) +
geom_node_point() +
geom_node_text(aes(label = name), vjust = 1, hjust = 1) +
theme_void()
## Warning in graph_from_data_frame(.): In `d' `NA' elements were replaced with
## string "NA"
## Using `stress` as default layout

¡Nada mal! Sin embargo, tenemos un pequeño problema. Hay una gran
cantidad de conexiones a un par de palabras que no fueron identificadas
como huecas, pues aparecen con tilde, lo cual no es convencional en el
español moderno filtramos estas palabras.
manso_bigrama <-
manso_bigrama %>%
filter(!uno %in% c("á", "ó")) %>%
filter(!dos %in% c("á", "ó"))
Creamos de nuevo la red.
set.seed(175)
manso_bigrama %>%
filter(n >= 5) %>%
graph_from_data_frame() %>%
ggraph() +
geom_edge_link(arrow = arrow(type = "closed", length = unit(.075, "inches"))) +
geom_node_point() +
geom_node_text(aes(label = name), vjust = 1, hjust = 1) +
theme_void()
## Warning in graph_from_data_frame(.): In `d' `NA' elements were replaced with
## string "NA"
## Using `stress` as default layout

Mucho mejor. Ahora podemos ver con más claridad algunas relaciones de
palabras importantes. Por ejemplo, seguramente hay un personaje llamado
“manuel peña” y se habla de la “pobre niña chucha”. En realidad, con
esto nos damos cuenta que El amigo manso es una novela
que se centra en las relaciones que tienen algunos pocos personajes, los
cuales parecen tener la tendencia a hablar de manera formal y haciendo
referencias al pasado. Corresponde con lo que recuerdo de haber leído
este libro hace un par de años.
Creamos una función para generar redes, con algunos ajustes para
mejorar la presentación de la red semántica, entre otras, que los
vínculos tengan un color que corresponda la frecuencia con la que
ocurren.
crear_red <- function(libro_bigrama, umbral = 5) {
libro_bigrama %>%
filter(n > umbral) %>%
graph_from_data_frame() %>%
ggraph() +
geom_edge_link(aes(edge_alpha = n),
arrow = arrow(type = "closed", length = unit(.1, "inches"))) +
geom_node_point(size = 2, color = "#9966dd") +
geom_node_text(aes(label = name), vjust = 1, hjust = 1) +
theme_void()
}
Hacemos ajustes a una función que ya habíamos creado para generar
bigramas.
generar_bigramas <- function(libro_parrafo) {
libro_parrafo %>%
unnest_tokens(input = "texto", output = "bigrama", token = "ngrams", n = 2) %>%
separate(bigrama, into = c("uno", "dos"), sep = " ") %>%
filter(!uno %in% c(stopwords("es"), "á", "ó")) %>%
filter(!dos %in% c(stopwords("es"), "á", "ó")) %>%
count(uno, dos)
}
Y por supuesto, podemos crear una función que haga todo el proceso de
creación de redes.
red_texto <- function(archivo, inicio, final, umbral = 5) {
leer_texto(archivo, inicio = inicio, final = final) %>%
crear_parrafos() %>%
encontrar_capitulos() %>%
borrar_vacios() %>%
generar_bigramas() %>%
crear_red(umbral = umbral)
}
Pongamos a prueba nuestra función red_texto().
set.seed(175)
red_texto(archivo = "55563-0.txt", inicio = 153, final = 10612, umbral = 5)
## Warning in graph_from_data_frame(.): In `d' `NA' elements were replaced with
## string "NA"
## Using `stress` as default layout

Con esto estaremos listos para crear redes de textos con un formato
similar al de la novela que hemos analizado.
Para concluir
En este artículo revisamos como crear una red semántica usando R, en
particular las funciones de los paquetes tidytext,
igraph y ggraph. En el proceso también nos dimos
cuenta que separar y unir texto de distintas maneras son tareas de
procesamiento más importantes en minería de texto. En varias ocasiones,
unimos nuestro texto sólo para separarlo una vez más, para así poder
unirlo de una manera distinta.
Las redes semánticas son una herramienta muy útil al realizar minería
de texto. Como vimos, son relativamente simples de implementar y nos
permiten darnos una idea de los temas más importantes de nuestros
textos. Además, son lo suficientemente flexibles como para adaptarse a
distintas necesidades de análisis.
En este ejemplo generamos nuestras redes por frecuencia, pero es
posible utilizar otros indicadores, pero eso lo revisaremos en otra
ocasión.
LS0tDQp0aXRsZTogIlJlZGVzIHNlbcOhbnRpY2FzIGNvbiBSIg0KYXV0aG9yOiAiQ2FybG9zIEFsZnJlZG8gU2FuZG92YWwgU3VsbG9uIg0KZGF0ZTogIjAyIGRlIG1heW8gZGUgMjAyMiINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGRmX3ByaW50OiB0aWJibGUNCiAgICBmaWdfaGVpZ2h0OiA2LjUNCiAgICBmaWdfd2lkdGg6IDgNCiAgICBoaWdobGlnaHQ6IGthdGUNCiAgICB0aGVtZTogeWV0aQ0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19mbG9hdDogVFJVRQ0KICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRSwgb3V0LndpZHRoID0gNzB9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KRW4gZXN0ZSBhcnTDrWN1bG8gcmV2aXNhcmVtb3MgY29tbyBjcmVhciByZWRlcyBzZW3DoW50aWNhcyB1c2FuZG8gUiwgeSBlbiBlbCBwcm9jZXNvIHZlcmVtb3MgY8OzbW8gaGFjZXIgYWxndW5hcyBkZSBsYXMgdGFyZWFzIG3DoXMgY29tdW5lcyBhbCBwcm9jZXNhciB0ZXh0by4NCg0KIyBVbmEgaW50cm9kdWNjacOzbiBpbmZvcm1hbCBhIGxhcyByZWRlcyBzZW3DoW50aWNhcw0KTGFzIHJlZGVzIHNlbcOhbnRpY2FzIHNvbiB1bmEgdMOpY25pY2EgZGUgcmVwcmVzZW50YWNpw7NuIHVzYWRhIGVuIGRpc3RpbnRhcyBkaXNjaXBsaW5hcywgZW50cmUgZWxsYXMsIGxhIG1pbmVyw61hIGRlIHRleHRvLiBFc3RhcyByZWRlcyBzb24gdW5hIGZvcm1hIGRlIG9idGVuZXIgeSB2aXN1YWxpemFyIGxhIHJlbGFjacOzbiBlbnRyZSBlbGVtZW50b3MgZGUgdW4gdGV4dG8sIHF1ZSBwdWVkZW4gc2VyIHBhbGFicmFzLCBuLWdyYW1hcywgZnJhc2VzIHUgb3RyYXMgdW5pZGFkZXMgZGUgdGV4dG8uIEVsIHJlc3VsdGFkbyBlcyBzaW1pbGFyIGEgdW5hIHRlbGFyYcOxYSwgZW4gbGEgcXVlIGNhZGEgbm9kbyBvIHB1bnRvIGRlIHVuacOzbiBlcyB1bmEgdW5pZGFkLCB5IGRlIGVsbGFzIHNhbGVuIGzDrW5lYXMgcXVlIGxhcyB1bmVuIGEgb3RyYXMgdW5pZGFkZXMuIERlIGVzdGEgbWFuZXJhIHBvZGVtb3MgZXh0cmFlciBsYSBpbmZvcm1hY2nDs24gcmVsZXZhbnRlIGRlIGN1ZXJwb3MgZGUgY29tcGxlam9zLg0KDQpFeGlzdGVuIGRpZmVyZW50ZXMgZm9ybWFzIGVuIGxhcyBxdWUgcG9kZW1vcyBlc3RhYmxlY2VyIHJlbGFjaW9uZXMgZW50cmUgdW5pZGFkZXMsIGNvbiBhbGd1bmFzIGRlIGVsbGEsIGNvbW8gbGEgcXVlIHJldmlzYXJlbW9zIGVuIGVzdGUgZG9jdW1lbnRvLCBlcyBpbmNsdXNvIHBvc2libGUgZXN0YWJsZWNlciBsYSBkaXJlY2Npw7NuIGRlIGxhIHJlbGFjacOzbi4gUG9yIGVqZW1wbG8sIGRlY2lyIHF1ZSBkZSBsYSBwYWxhYnJhICJjYXNhIiBoYXkgdW5hIGNvbmV4acOzbiBhICJncmFuZGUiLCBkZSBsYSAgbWFuZXJhICJjYXNhIC0+IGdyYW5kZSIuIA0KDQpDb21vIHBvZHLDoXMgdmVyLCBlc3RhIGVzIHVuYSB0w6ljbmljYSBjb25jZXB0dWFsbWVudGUgc2VuY2lsbGEgeSBlc3RhIGVzIHN1IHByaW5jaXBhbCBmb3J0YWxlemEsIGVzIGbDoWNpbCBkZSBpbnRlcnByZXRhciB5IGVsIHByb2Nlc28gcGFyYSBnZW5lcmFyIHJlZGVzIGVzIHBvY28gZGVtYW5kYW50ZSBlbiBjdWFudG8gYSBjw7NtcHV0by4NCg0KUHVlZGVzIGxlZXIgbcOhcyBzb2JyZSByZWRlcyBzZW3DoW50aWNvcyBlbiBlbCBzaWd1aWVudGUgZW5sYWNlOg0KDQoqIGh0dHA6Ly9lbGllcy5yZWRpcmlzLmVzL2VsaWVzOS80LTMtMi5odG0NCg0KQ29tZW5jZW1vcyBwcmVwYXJhbmRvIG51ZXN0cm8gZXNwYWNpbyBkZSB0cmFiYWpvLg0KDQojIFBhcXVldGVzIG5lY2VzYXJpb3MNCkxvcyBwYXF1ZXRlcyB1c2FyZW1vcyBzb246DQoNCiogKip0aWR5dGV4dCoqOiBFbCBwYXF1ZXRlIHByaW5jaXBhbCBkZSBlc3RhIG9jYXNpw7NuLiBDb250aWVuZSBoZXJyYW1pZW50YXMgcGFyYSBwcm9jZXNhciB0ZXh0byBkZSBtYW5lcmEgbGltcGlhIGUgaW50dWl0aXZhLg0KKiAqKnRpZHl2ZXJzZSoqOiBVbiBtZXRhIHBhcXVldGUgcXVlIGxsYW1hIGEgb3Ryb3MgbcOhcyAoZHBseXIsIHJlYWRyLCBwdXJyciwgZXRjLikgcXVlIG5vcyBmYWNpbGl0YW4gbGEgbGVjdXRhLCBwcm9jZXNhbWllbnRvIHkgdmlzdWFsaXphY2nDs24gZGUgZGF0b3MuDQoqICoqaWdyYXBoKiogeSAqKmdncmFwaCoqOiBQYXF1ZXRlcyBxdWUgYW1wbMOtYW4gbGFzIGZ1bmNpb25lcyBkZWwgcGFxdWV0ZSBnZ3Bsb3QyIGRlbCB0aWR5dmVyc2UuIEVuIHBhcnRpY3VsYXIsIGdyYXBoIGhhcsOhIGVsIHRyYWJham8gZGUgY3JlYXIgbGFzIHJlZGVzIHNlbcOhbnRpY2FzLg0KKiAqKnRtKio6IEhlcnJhbWllbnRhcyBkZSBtaW5lcsOtYSBkZSB0ZXh0by4gDQoNClNpIG5vIGNvbnRhbW9zIGNvbiBsYXMgcGFxdWV0ZXMsIGxvcyBpbnN0YWxhbW9zIGNvbiBgaW5zdGFsbC5wYWNrYWdlcygpYCwgY29tbyBlcyB1c3VhbC4NCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeSh0aWR5dGV4dCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeSh0bSkNCmxpYnJhcnkoaWdyYXBoKQ0KbGlicmFyeShnZ3JhcGgpDQpgYGANCg0KQ29udGludWVtb3Mgb2J0ZW5pZW5kbyBlbCB0ZXh0byBxdWUgYW5hbGl6YXJlbW9zLg0KDQojIE51ZXN0cm8gdGV4dG86IEVsIGFtaWdvIG1hbnNvIA0KUGFyYSBlc3RlIGVqZW1wbG8gdXNhcmVtb3MgZWwgdGV4dG8gZGUgbGEgbm92ZWxhICJFbCBhbWlnbyBtYW5zbyIgZGUgQmVuaXRvIFDDqXJleiBHYWxkw7NzLCBwdWJsaWNhZGEgb3JpZ2luYWxtZW50ZSBlbiAxODgyIHkgZGlzcG9uaWJsZSBlbiBQcm9qZWN0IEd1dGVuYmVyZyBwb3IgZm9ybWFyIHBhcnRlIGRlbCBkb21pbmlvIHDDumJsaWNvLg0KDQpEZXNjYXJnYXJlbW9zIGVzdGEgbm92ZWxhIGNvbW8gdW4gYXJjaGl2byAqKi50eHQqKi4gSGUgY3JlYWRvIHVuYSBjb3BpYSBkZWwgYXJjaGl2byBlbiB1biByZXBvc2l0b3JpbyBkZSBHaXRodWIsIHBhcmEgZmFjaWxpdGFyIGVsIHByb2Nlc28uDQoNClVzYW1vcyBgZG93bmxvYWQuZmlsZSgpYCBwYXJhIGRlc2NhcmdhciBlbCBhcmNoaXZvLg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmRvd25sb2FkLmZpbGUodXJsID0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9qYm9zY29tZW5kb3phL3JwdWJzL21hc3Rlci9yZWRfc2VtYW50aWNhLzU1NTYzLTAudHh0IiwgZGVzdGZpbGUgPSAiNTU1NjMtMC50eHQiKQ0KYGBgDQoNCkV4cGxvcmFtb3MgbnVlc3RybyBkb2N1bWVudG8gY29uIGByZWFkX2xpbmVzKClgIGRlICpyZWFkciosIHBpZGllbmRvIGRlc3B1w6lzIGxhcyBwcmltZXJhcyAxNSBsw61uZWFzIGNvbiBgaGVhZCgpYC4NCmBgYHtyfQ0KcmVhZF9saW5lcygiNTU1NjMtMC50eHQiKSAlPiUgDQogIGhlYWQoMTUpDQpgYGANCg0KQ29uc2lkZXJhbmRvIHF1ZSBlc3RhbW9zIHRyYWJhamFuZG8gY29uIHVuYSBub3ZlbGEgY29tcGxldGEsIGRlIHVuIHBhciBjaWVudG9zIGRlIHDDoWdpbmFzIGRlIGxhcmdvLCBzZXLDoSBuZWNlc2FyaW8gcXVlIHZlciBudWVzdHJvIGRvY3VtZW50byBmdWVyYSBkZSBSIHBhcmEgZXhwbG9yYXIgbcOhcyBmw6FjaWxtZW50ZSBzdSBlc3RydWN0dXJhLiBVc2Ftb3MgYGZpbGUuc2hvdygpYCBwYXJhIGVzdG8uDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmZpbGUuc2hvdygiNTU1NjMtMC50eHQiKQ0KYGBgDQoNCiMgTGEgZXN0cnVjdHVyYSBkZSBudWVzdHJvIHRleHRvDQpEZXNwdcOpcyBkZSByZXZpc2FyIGVsIGRvY3VtZW50byBub3MgZGFtb3MgY3VlbnRhIGRlIHVuIHBhciBkZSBjb3Nhcy4NCg0KUHJpbWVybywgZWwgdGV4dG8gZXN0w6EgZXN0cnVjdHVyYWRvIGVuIHJlbmdsb25lcyBjb24gdW4gbMOtbWl0ZSBkZSBhbmNobywgZXMgZGVjaXIgY3VhbmRvIGVsIHRleHRvIGxsZWdhIGEgdW4gYW5jaG8gZGV0ZXJtaW5hZG8gKG1lbm9zIGRlIDgwIGNhcmFjdGVyZXMpLCBvY3VycmUgdW4gc2FsdG8gZGUgbMOtbmVhLiBQb3IgbG8gdGFudG8gdGVuZW1vcyBtdWNob3MgZW51bmNpYWRvcyBpbnRlcnJ1bXBpZG8gYSBsYSBtaXRhZCBkZSBlbGxvcyB5IG5vIGNvbnRhbW9zIGNvbiBww6FycmFmb3MuIFNpbiBlbWJhcmdvLCBwYXJhIGluZGljYXIgY2FkYSBmaW5hbCBkZSB1biBww6FycmFmbyBoYXkgdW5hIGzDrW5lYSBlbiBibGFuY28uDQoNCkVzdG8gZXMgaW1wb3J0YW50ZSBwb3JxdWUgdW5hIGzDrW5lYSBkZSA4MCBjYXJhY3RlcmVzIG5vIGNvbnRpZW5lIHN1ZmljaWVudGUgdGV4dG8gY29tbyBwYXJhIGFybWFyIHJlZGVzIHNlbcOhbnRpY2FzIHF1ZSB0ZW5nYW4gc2VudGlkby4NCg0KU2VndW5kbywgYWRlbcOhcyBkZWwgdGV4dG8gZGUgbGEgbm92ZWxhLCB0ZW5lbW9zIHVuYSBzZWNjacOzbiBpbnRyb2R1Y3RvcmlhIGFsIHRleHRvIHkgYWwgZmluYWwgbGEgaW5mb3JtYWNpw7NuIGxlZ2FsIGRlIFByb2plY3QgR3V0ZW5iZXJnLiBFbCB0ZXh0byBxdWUgbm9zIGludGVyZXNhIGVzdGEgZW50cmUgZXN0b3MgZG9zLg0KDQpDb25zaWRlcmFuZG8gbG8gYW50ZXJpb3IsIG5lY2VzaXRhbW9zIGhhY2VyIGRvcyBjb3NhczogaW1wb3J0YXIgc8OzbG8gZWwgdGV4dG8gcXVlIG5vcyBpbnRlcmVzYSB5IGVzdHJ1Y3R1cmFybG8gcG9yIHDDoXJyYWZvcy4NCg0KIyBDcmVhbmRvIHDDoXJyYWZvcw0KUmV2aXNhbmRvIG51ZXN0cm8gZG9jdW1lbnRvLCBzYWJlbW9zIHF1ZSBsYSBub3ZlbGEgZW1waWV6YSBlbiBsYSBsw61uZWEgMTU0IHkgdGVybWluYSBlbiBsYSAxMDYxMi4gVXNhbW9zIGVzdGEgaW5mb21hY2nDs24gZW4gYHJlYWRfbGluZXNgIGRlICpyZWFkci4gYHNraXAgPSAxNTNgIHBhcmEgZW1wZXphciBlbiBsYSBsw61uZWEgMTU0LCB5IGBuX21heCA9ICgxMDYxMiAtIDE1MylgIHBhcmEgbGVlciBoYXN0YSBsYSBsw61uZWEgcXVlIG9yaWdpbmFsbWVudGUgc2Vyw61hIGxhIDEwNjEyLCBwZXJvIHRvbWFuZG8gZW4gY3VlbnRhIHF1ZSBlbXBlemFtb3MgYSBsZWVyIGRlc2RlIGxhIDE1My4NCg0KVXNhbW9zIGB0cmltd3MoKWAgY29uIGBtYXAoKWAgZGUgKnB1cnJyKiBwYXJhIHF1aXRhciBsb3MgZXNwYWNpb3MgZW4gYmxhbmNvIGFsIGluaWNpbyB5IGZpbmFsIGRlIGNhZGEgbMOtbmVhIGRlIHRleHRvLiBFc3RvIGhhcsOhIG3DoXMgY29uc2lzdGVudGUgbnVlc3RybyB0ZXh0by4NCg0KU2FiZW1vcyBxdWUgbG9zIHDDoXJyYWZvcyBkZSBudWVzdHJvIHRleHRvIGVzdMOhbiBpbmRpY2Fkb3MgcG9yIHVuIHJlbmdsw7NuIGVuIGJsYW5jbywgYXPDrSBxdWUgdXNhcmVtb3MgZXN0YSBpbmZvcm1hY2nDs24gcGFyYSByZWVzdHJ1Y3R1cmFyIG51ZXN0cm8gZG9jdW1lbnRvIHBvciBww6FycmFmb3MsIG5vIHJlbmdsb25lcyBkZSBhbmNobyBtw6FzIG8gbWVub3MgZmlqby4NCg0KQ2FtYmlhbW9zIGVsIGNvbnRlbmlkbyBkZSBlc3RvcyByZW5nbG9uZXMgcG9yICoqIl9zYWx0b18iKiogdXNhbmRvIGBpZmVsc2UoKWAsIGFzw60gdGVuZHJlbW9zIHVuYSBwYWxhYnJhIGNsYXZlIMO6bmljYSBwYXJhIGluZGljYXIgc2FsdG9zIGRlIHDDoWdpbmEuIFVuYSB2ZXogaGVjaG8gZXN0bywgdW5pbW9zIHRvZG8gZWwgdGV4dG8gZW4gdW5hIHNvbGEgY2FkZW5hIGRlIHRleHRvIGNvbiBgcGFzdGUwKClgIHVzYW5kbyBlbCBhcmd1bWVudG8gYGNvbGxhcHNlID0gIiAiYCwgcGFyYSBhc2VndXJhcm5vcyBxdWUgaGFicsOhIHVuIGVzcGFjaW8gZW50cmUgY2FkYSByZW5nbMOzbiB1bmlkby4NCg0KQWhvcmEsIHNlcGFyYW1vcyBkZSB2dWVsdGEgZWwgdGV4dG8sIHVzYW5kbyBgc3Ryc3BsaXQoKWAgY29uIGVsIGFyZ3VtZW50byBgc3BsaXQgPSAiX3NhbHRvXyJgLiBEZSBlc3RlIG1vZG8gc2VwYXJhbW9zIGNhZGEgcXVlIG9jdXJyZSB1biBzYWx0byBkZSBww6FycmFmbywgb2J0ZW5pZW5kbyBlbnRvbmNlcyBww6FycmFmb3MuDQoNCkZpbmFsbWVudGUsIHVzYW1vcyBvdHJhIHZleiBgdHJpbXdzKClgIGNvbiBgbWFwKClgIHkgY29udmVydGltb3MgbnVlc3RybyByZXN1bHRhZG9zIGEgdW4gZGF0YSBmcmFtZSBjb24gYGRhdGEuZnJhbWUoKWAsIHVzYW5kbyBlbCBhcmd1bWVudG8gYHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRWAgcGFyYSBxdWUgbnVlc3RybyB0ZXh0byBzZSBjb25zZXJ2ZSBjb21vIGRlIHRpcG8gKmNoYXJhY3RlciouIFBhc2Ftb3MgbG9zIGFudGVyaW9yIGEgdW4gdGliYmxlLCBjb24gYHRibF9kZigpYCBkZSAqZHBseXIqLCB5IHBvbmVtb3MgInRleHRvIiBjb21vIG5vbWJyZSBkZSBsYSBjb2x1bW5hIHJlc3VsdGFudGUsIHVzYW5kbyBgbmFtZXMoKSA8LWAuDQpgYGB7cn0NCm1hbnNvIDwtIA0KICByZWFkX2xpbmVzKCI1NTU2My0wLnR4dCIsIHNraXAgPSAxNTMsIG5fbWF4ID0gKDEwNjEyIC0gMTUzKSkgJT4lIA0KICBtYXAodHJpbXdzKSAlPiUgDQogIGlmZWxzZSguID09ICIiLCAiX3NhbHRvXyIsIC4pICU+JSANCiAgcGFzdGUwKC4sIGNvbGxhcHNlID0gIiAiKSAlPiUgDQogIHN0cnNwbGl0KHNwbGl0ID0gIl9zYWx0b18iKSAlPiUgDQogIG1hcCh0cmltd3MpICU+JSANCiAgZGF0YS5mcmFtZShzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JSANCiAgdGJsX2RmKCkgJT4lIA0KICB7DQogICAgbmFtZXMoLikgPC0gInRleHRvIg0KICAgIC4NCiAgfQ0KYGBgDQoNCkNvbW8gZXMgcG9zaWJsZSBxdWUgZGVzcHXDqXMgcHJvY2VzZW1vcyBkYXRvcyBjb24gZXN0cnVjdHVyYXMgc2ltaWxhcmVzLCBkZWZpbmltb3MgdW4gcGFyIGRlIGZ1bmNpb25lcy4NCg0KVW5hIHBhcmEgbGVlciBudWVzdHJvIHRleHRvLg0KDQpgYGB7cn0NCmxlZXJfdGV4dG8gPC0gZnVuY3Rpb24oYXJjaGl2bywgaW5pY2lvLCBmaW5hbCkgew0KICAgcmVhZF9saW5lcyhhcmNoaXZvLCBza2lwID0gaW5pY2lvLCBuX21heCA9IChmaW5hbCAtIGluaWNpbykpICU+JSANCiAgbWFwX2Nocih0cmltd3MpDQogIH0NCmBgYA0KDQpPdHJhIHBhcmEgY3JlYXIgcMOhcnJhZm9zLg0KYGBge3J9DQpjcmVhcl9wYXJyYWZvcyA8LSBmdW5jdGlvbih0ZXh0bykgew0KICB0ZXh0byAlPiUgDQogICAgbWFwKHRyaW13cykgJT4lIA0KICAgIGlmZWxzZSguID09ICIiLCAiX3NhbHRvXyIsIC4pICU+JSANCiAgICBwYXN0ZTAoLiwgY29sbGFwc2UgPSAiICIpICU+JSANCiAgICBzdHJzcGxpdChzcGxpdCA9ICJfc2FsdG9fIikgJT4lIA0KICAgIG1hcCh0cmltd3MpICU+JSANCiAgICBkYXRhLmZyYW1lKHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lIA0KICAgIHRibF9kZigpICU+JSANCiAgICB7DQogICAgICBuYW1lcyguKSA8LSAidGV4dG8iDQogICAgICAuDQogICAgfQ0KfQ0KYGBgDQoNClBvciBjaWVydG8sIHNlcGFyYXIgeSB1bmlyIHNvbiB0YXJlYXMgcXVlIHJlYWxpemFyZW1vcyBtdWNobyBlbiBlc3RlIGVqZW1wbG8uDQoNCiMgUXVpdGFuZG8gcmVuZ2xvbmVzIHZhY8Otb3MNCkFob3JhIHF1aXRhbW9zIGxvcyByZW5nbG9uZXMgdmFjw61vcyB5IGxvcyBlc3BhY2lvcyBlbiBibGFuY28gYWwgcHJpbmNpcGlvIHkgZmluYWwgZGUgY2FkYSByZW5nbMOzbiwgdXNhbmRvIGBmaWx0ZXIoKWAgeSBgbXV0YXRlX2FsbCgpYCBkZSAqZHBseXIqLCBjb24gYXl1ZGEgZGUgYHRyaW13cygpYC4NCmBgYHtyfQ0KbWFuc28gPC0gDQogIG1hbnNvICU+JSANCiAgZmlsdGVyKCF0ZXh0byAlaW4lIGMoIiAiLCAiIikpICU+JSANCiAgbXV0YXRlX2FsbCh0cmltd3MpDQpgYGANCg0KTmF0dXJhbG1lbnRlLCB1bmEgZnVuY2nDs24gbm9zIGFob3JyYXJhIHRyYWJham8gZW4gZWwgZnV0dXJvLg0KYGBge3J9DQpib3JyYXJfdmFjaW9zIDwtIGZ1bmN0aW9uKGxpYnJvX3ZhY2lvcykgew0KICBsaWJyb192YWNpb3MgJT4lIA0KICBmaWx0ZXIoIXRleHRvICVpbiUgYygiICIsICIiKSkgJT4lIA0KICBtdXRhdGVfYWxsKHRyaW13cykNCn0NCmBgYA0KDQojIE9idGVuaWVuZG8gY2Fww610dWxvcy4NCk90cmEgcXVlIGNvc2EgcXVlIGRlc2N1YnJpbW9zIGFsIHJldmlzYXIgbnVlc3RybyBkb2N1bWVudG8sIGVzICBxdWUgbG9zIGNhcMOtdHVsb3MgZGUgbGEgbm92ZWxhIGVzdMOhbiBpbmRpY2Fkb3MgcG9yIG7Dum1lcm9zIHJvbWFub3MsIGNhZGEgdW5vIGVuIHN1IHByb3BpbyByZW5nbMOzbi4gU2kgcXVlcmVtb3MgYWdydXBhciBsb3MgcMOhcnJhZm9zIHF1ZSBoZW1vcyBjcmVhZG8gZW4gY2Fww610dWxvcywgbG8gY3VhbCBzZXLDoSBjb252ZW5pZW50ZSBwYXJhIGFuw6FsaXNpcyBwb3N0ZXJpb3JlcywgZXN0ZSBzZXLDoSBudWVzdHJvIHB1bnRvIGRlIHJlZmVyZW5jaWENCg0KTG8gcHJpbWVybyBxdWUgbmVjZXNpdGFtb3MgZXMgdW5hIG1hbmVyYSBkZSBlbmNvbnRyYXIgZXN0b3MgbsO6bWVyb3Mgcm9tYW5vcy4gUGFyYSBlc3RvLCB1c2Ftb3MgKipyZWdleCoqLg0KDQpDcmVhbW9zIHVuYSBleHByZXNpw7NuIHJlZ3VsYXIgcXVlIGNhcHR1cmUgdG9kb3MgbG9zIHJlbmdsb25lcyBkZSAqKm1hbnNvKiogZW4gbG9zIHF1ZSBzdSDDum5pY28gY29udGVuaWRvIHNlYW4gbsO6bWVyb3Mgcm9tYW5vcy4gU2FiZW1vcyBxdWUgbG9zIG7Dum1lcm9zIHJvbWFub3Mgc29uIGxldHJhcyBtYXnDunNjdWxhcyBhc8OtIHF1ZSBwb2RlbW9zIHVzYXIgYFtbOnVwcGVyOl1dYC4gVGFtYmnDqW4gc2FiZW1vcyBxdWUgZXMgbG8gw7puaWNvIHF1ZSBhcGFyZWNlIGVuIGVzZSByZW5nbMOzbiwgYXPDrSBxdWUgdXNhbW9zIGBeYCBwYXJhIGluZGljYXIgcXVlIGVsIHRleHRvIHF1ZSBkZXNlYW1vcyBjYXB0dXJhciBpbmljaWEgY29uIHVuYSBtYXnDunNjdWxhLCB5IGAkYCBwYXJhIGluZGljYXIgcXVlIHRlcm1pbmEgY29uIHVuYSBtYXnDunNjdWxhLiBQb3Igw7psdGltbywgdXNhbW9zIGVsIGN1YW50aWZpY2Fkb3IgYCtgIHBhcmEgcXVlIG51ZXN0cmEgcmVnZXggY2FwdHVyZSBjYWRlbmFzIGRlIHRleHRvIGRlIGxhcmdvIDEgbyBtw6FzLg0KDQpOdWVzdHJhIHJlZ2V4IGx1Y2lyw61hIGFzw606IGAiXltbOnVwcGVyOl1dKyQiYC4gIA0KDQpVc2Ftb3MgYGdyZXBsKClgIGNvbiBgZmlsdGVyKClgIGRlICpkcGx5ciogcGFyYSB2ZXJpZmljYXIuDQoNCmBgYHtyfQ0KbWFuc28gJT4lIA0KICBmaWx0ZXIoZ3JlcGwoIl5bWzp1cHBlcjpdXSskIiwgdGV4dG8pKQ0KYGBgDQpMdWNlIGJpZW4sIGNhcHR1cmFtb3MgY2luY3VlbnRhIHJlbmdsb25lcywgcXVlIGVzIGVsIG7Dum1lcm8gZGUgY2Fww610dWxvIGRlICoqbWFuc28qKiwgZGVsIEkgYWwgTC4NCg0KQWhvcmEsIHVzYW1vcyBgbXV0YXRlKClgIGRlICpkcGx5ciogZSBgaWZlbHNlKClgIHBhcmEgY3JlYXIgdW5hIG51ZXZhIGNvbHVtbmEgbGxhbWFkYSAqKmNhcGl0dWxvKiouIA0KDQpCdXNjYW1vcyBlbiBsYSBjb2x1bW5hICoqdGV4dG8qKiBsb3MgcmVuZ2xvbmVzIHF1ZSBjYXB0dXJhIG51ZXN0cmEgZXhwcmVzacOzbiByZWd1bGFyLCB5IGVuIGxvcyBjYXNvcyBxdWUgZXN0byBlcyB2ZXJkYWRlcm8sIG1hbmRhbW9zIGVsIHRleHRvIGVuY29udHJhZG8gYSBsYSBjb2x1bW5hICoqY2FwaXR1bG8qKi4gRGVzcHXDqXMsIGxsZW5hbW9zIGxvcyByZW5nbG9uZXMgZGViYWpvIGRlIGVzdGUgY29uIHN1IGNvbnRlbmlkbywgdXNhbmRvIGBmaWxsKClgIGRlIHRpZHlyLCBldGlxdWV0YW5kbyBhc8OtIGEgdG9kb3MgbG9zIHDDoXJyYWZvcyBjb24gZWwgbsO6bWVybyBkZSBjYXDDrXR1bG8gcXVlIGxlcyBjb3JyZXNwb25kZS4gUG9yIMO6bHRpbW8sIHVzYW1vcyBgZmlsdGVyKClgIHBhcmEgcXVpdGFyIGxvcyByZW5nbG9uZXMgY29uIGVsIG7Dum1lcm8gZGUgY2Fww610dWxvLg0KYGBge3J9DQptYW5zbyA8LSANCiAgbWFuc28gJT4lIA0KICBtdXRhdGUoY2FwaXR1bG8gPSBpZmVsc2UoZ3JlcGwoIl5bWzp1cHBlcjpdXSskIiwgdGV4dG8pLCB0ZXh0bywgTkEpKSAlPiUgDQogIGZpbGwoY2FwaXR1bG8pICU+JSANCiAgZmlsdGVyKHRleHRvICE9IGNhcGl0dWxvKQ0KDQpgYGANCg0KQ3JlYW1vcyB1bmEgZnVuY2nDs24gcGFyYSBlbmNvbnRyYXIgY2Fww610dWxvcywgcGFyYSBzaW1wbGlmaWNhciBsYSB2aWRhIGEgbnVlc3RybyB5byBkZWwgZnV0dXJvLg0KYGBge3J9DQplbmNvbnRyYXJfY2FwaXR1bG9zIDwtIGZ1bmN0aW9uKGxpYnJvKSB7DQogIGxpYnJvICU+JSANCiAgbXV0YXRlKGNhcGl0dWxvID0gaWZlbHNlKGdyZXBsKCJeW1s6dXBwZXI6XV0rJCIsIHRleHRvKSwgdGV4dG8sIE5BKSkgJT4lIA0KICBmaWxsKGNhcGl0dWxvKSAlPiUgDQogIGZpbHRlcih0ZXh0byAhPSBjYXBpdHVsbykNCn0NCmBgYA0KDQpBaG9yYSBzw60sIGVzdGFtb3MgbGlzdG9zIHBhcmEgY29udGludWFyLg0KDQojIENyZWFuZG8gdG9rZW5zOiBiaWdyYW1hcy4NCkNvbW8gY3JlYXJlbW9zIHVuYSByZWQgc2Vtw6FudGljYSBjb25lY3RhbmRvIHBhbGFicmFzLCBuZWNlc2l0YW1vcyBzZWdtZW50YXIgbnVlc3RybyB0ZXh0byBwb3IgcGFyZWphcyBkZSBwYWxhYnJhLCBlcyBkZWNpciwgbi1ncmFtYXMgZW4gbG9zIHF1ZSBuIGVzIGlndWFsIGRvcy4gRXN0b3MgY2Fzb3MgZGUgbi1ncmFtYSBzb24gY29ub2NpZG9zIGNvbW8gYmlncmFtYXMuIEVzdGUgZXMgZWwgKnRva2VuKiBvICp1bmlkYWQqIGRlIHRleHRvIGRlIG51ZXN0cm8gYW7DoWxpc2lzLg0KDQpQYXJhIGVzdGEgdGFyZWEgdXNhcmFtb3MgbGEgZnVuY2nDs24gYHVubmVzdF90b2tlbnMoKWAgZGUgKnRpZHl0ZXh0KiwgY29uIGxvcyBhcmd1bWVudG9zIGB0b2tlbiA9ICJuZ3JhbSJgIHkgYG4gPSAyYC4gVG9tYW1vcyBsYSBjb2x1bW5hICJ0ZXh0byIgY29tbyBlbnRyYWRhIHkgb2J0ZW5lbW9zICJiaWdyYW1hIiBkZSBzYWxpZGEuDQpgYGB7cn0NCm1hbnNvX2JpZ3JhbWEgPC0gDQogIG1hbnNvICU+JSANCiAgdW5uZXN0X3Rva2VucyhpbnB1dCA9ICJ0ZXh0byIsIG91dHB1dCA9ICJiaWdyYW1hIiwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpDQpgYGANCg0KQXPDrSwgb2J0ZW5lbW9zIHVuIGRhdGEgZnJhbWUgY29uIHVuIGJpZ3JhbWEgcG9yIHJlbmdsw7NuLiBOb3RhIHF1ZSBlbCBuw7ptZXJvIGRlIGNhcMOtdHVsbyBub3MgaGEgYXl1ZGFkbyBhIGlkZW50aWZpY2FyIGEgYWdydXBhcmxvcywgc2luIGVzdGEgaW5mb3JtYWNpw7NuLCB0ZW5kcsOtYW1vcyBwcm9ibGVtYXMgZGUgZHVwbGljYWNpw7NuLg0KYGBge3J9DQptYW5zb19iaWdyYW1hDQpgYGANCg0KUG9kZW1vcyBleHBsb3JhciBjdcOhbGVzIHNvbiBsb3MgYmlncmFtYXMgbcOhcyBjb211bmVzLg0KYGBge3J9DQptYW5zb19iaWdyYW1hICU+JSANCiAgY291bnQoYmlncmFtYSwgc29ydCA9IFQpDQpgYGANCg0KUGFyZWNlIHF1ZSBudWVzdHJvcyBiaWdyYW1hcyBtw6FzIGNvbXVuZXMgc29uIGNvbmp1bmNpb25lcywgcHJlcG9zaWNpb25lcyB5IGFydMOtY3Vsb3MuIEVzdG8gZXMgdW4gcHJvYmxlbWEuIFNpIGRlamFtb3MgZXN0b3MgYmlncmFtYXMgcGFyYSBmb3JtYXIgbnVlc3RyYSByZWQgc2Vtw6FudGljYSwgb2J0ZW5kcmVtb3MgdW5hIHJlZCBtdXkgImVucmVkYWRhIiwgZGUgbGEgY3VhbCBwb2RyZW1vcyBleHRyYWVyIHBvY2EgaW5mb3JtYWNpw7NuLiBIYXkgcXVlIHNvbHVjaW9uYXIgZXN0YSBzaXR1YWNpw7NuDQoNCiMgUXVpdGFuZG8gcGFsYWJyYXMgaHVlY2FzDQpMYXMgcGFsYWJyYXMgcXVlIGFwb3J0YW4gcG9jYSBpbmZvcm1hY2nDs24gc2Vtw6FudGljYSwgY29tbyBjb25qdW5jaW9uZXMsIHByZXBvc2ljaW9uZXMgeSBhcnTDrWN1bG9zIHNvbiBjb25vY2lkYXMgY29tbyAqKnBhbGFicmFzIGh1ZWNhcyoqLg0KDQpQYXJhIHF1aXRhcmxhcyBkZSBudWVzdHJvIHRleHRvLCBjb250YW1vcyBjb24gbGEgYXl1ZGEgZGUgbGEgZnVuY2nDs24gYHN0b3B3b3JkcygpYCBkZSAqdG0qLiBTaSBsbGFtYW1vcyBhIGVzdGEgZnVuY2nDs24gY29uIGVsIGFyZ3VtZW50byBga2luZCA9ICJlcyJgLCBub3MgZGV2b2x2ZXLDoSB1biB2ZWN0b3IgY29uIHVuIGxpc3RhZG8gZGUgcGFsYWJyYXMgaHVlY2FzIGVuIGVzcGHDsW9sLg0KYGBge3J9DQpzdG9wd29yZHMoa2luZCA9ICJlcyIpICU+JSBoZWFkKDE1KQ0KYGBgDQoNClBvZHLDrWFtb3MgdXNhciBlc3RlIHZlY3RvciBjb24gYGZpbHRlcigpYCBzaSBudWVzdHJvcyBkYXRvcyBmdWVyYW4gcGFsYWJyYXMsIG5vIGJpZ3JhbWFzLiBBc8OtIHF1ZSBzZXBhcmFtb3MgbnVlc3Ryb3MgYmlncmFtYXMgZW4gcGFsYWJyYSAqKnVubyoqIHkgKipkb3MqKiwgY29uIGBzZXBhcmF0ZSgpYCBkZSAqdGlkeXIqLCB5IGVudG9uY2VzIGZpbHRyYW1vcy4NCg0KQWRlbcOhcywgbmVjZXNpdGFtb3MgdW5hIHBhbGFicmEgcG9yIGNvbHVtbmEgcGFyYSBjcmVhciBsYXMgcmVkZXMgc2Vtw6FudGljYXMsIGRlIG1vZG8gcXVlIGVzIGFsZ28gcXVlIHRlbmRyw61hbW9zIHF1ZSBoYWNlciBkZSB0b2RvcyBtb2Rvcy4gVXNhbW9zIGBjb3VudCgpYCBkZSAqZHBseXIqIHBhcmEgb2J0ZW5lciBsYSBmcmVjdWVuY2lhIGRlIGNhZGEgYmlncmFtYS4NCmBgYHtyfQ0KbWFuc29fYmlncmFtYSA8LSANCiAgbWFuc29fYmlncmFtYSAlPiUgDQogIHNlcGFyYXRlKGJpZ3JhbWEsIGludG8gPSBjKCJ1bm8iLCAiZG9zIiksIHNlcCA9ICIgIikgJT4lIA0KICBmaWx0ZXIoIXVubyAlaW4lIHN0b3B3b3JkcyhraW5kID0gImVzIikpICU+JSANCiAgZmlsdGVyKCFkb3MgJWluJSBzdG9wd29yZHMoa2luZCA9ICJlcyIpKSAlPiUgDQogIGNvdW50KHVubywgZG9zKQ0KYGBgDQoNCkRlZmluaW1vcyB0YW1iacOpbiB1bmEgZnVuY2nDs24gcGFyYSBsYSBnZW5lcmFjacOzbiBkZSBiaWdyYW1hcyBzaW4gcGFsYWJyYXMgaHVlY2FzLg0KYGBge3J9DQpnZW5lcmFyX2JpZ3JhbWFzIDwtIGZ1bmN0aW9uKGxpYnJvX3BhcnJhZm8pIHsNCiAgbGlicm9fcGFycmFmbyAlPiUgDQogICAgdW5uZXN0X3Rva2VucyhpbnB1dCA9ICJ0ZXh0byIsIG91dHB1dCA9ICJiaWdyYW1hIiwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpICU+JSANCiAgICBzZXBhcmF0ZShiaWdyYW1hLCBpbnRvID0gYygidW5vIiwgImRvcyIpLCBzZXAgPSAiICIpICU+JSANCiAgICBmaWx0ZXIoIXVubyAlaW4lIHN0b3B3b3JkcygiZXMiKSkgJT4lIA0KICAgIGZpbHRlcighZG9zICVpbiUgc3RvcHdvcmRzKCJlcyIpKSAlPiUgDQogICAgY291bnQodW5vLCBkb3MpDQp9DQpgYGANCg0KSGVtb3MgaGVjaG8gZWwgY29udGVvIGRlIHBhbGFicmFzIHBvcnF1ZSBjcmVhcmVtb3MgdW5hIHJlZCBxdWUgbXVlc3RyZSAqKmxhIGludGVuc2lkYWQgY29uIGxhIHF1ZSBzZSByZWxhY2lvbmFuIGxhcyBwYWxhYnJhcyoqLCBjdXlvIGluZGljYWRvciBzZXLDoSBsYSBmcmVjdWVuY2lhIGNvbiBsYSBxdWUgcGFyZWphcyBkZSBwYWxhYnJhcyBhcGFyZWNlbiBlbiBlbCB0ZXh0by4gwqFBaG9yYSBzw60sIGEgY3JlYXIgbnVlc3RyYSByZWQgc2Vtw6FudGljYSENCg0KIyBDcmVhbmRvIHVuYSByZWQgc2Vtw6FudGljYQ0KRW1wZXphbW9zIGZpbHRyYW5kbyBsb3MgYmlncmFtYXMgY29uIHVuYSBmcmVjdWVuY2lhIG11eSBiYWphLiBOb3MgcXVlZGFtb3MgY29uIGxvcyBxdWUgYXBhcmVjZW4gY2luY28gdmVjZXMgbyBtw6FzLg0KDQpEZXNwdcOpcywgdXNhbW9zIGxhcyBmdW5jacOzbiBgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKClgIGRlICppZ3JhcGgqIHBhcmEgY29udmVydGlyIG51ZXN0cm9zIGRhdG9zIGEgdW4gZm9ybWF0byBhcHJvcGlhZG8gcGFyYSBnZW5lcmFyIHJlZGVzIHNlbcOhbnRpY2FzLg0KDQpIZWNobyBlc3RvLCB1c2Ftb3MgYGdncmFwaCgpYCBkZWwgcGFxdWV0ZSBjb24gZWwgbWlzbW8gbm9tYnJlIHBhcmEgY3JlYXIgbnVlc3RyYSByZWQuIEVzdGEgZnVuY2nDs24gZnVuY2lvbmEgY29uIGVsIG1pc21vIHNpc3RlbWEgcXVlIGBnZ3Bsb3QyYCwgcG9yIGxvIHRhbnRvLCB0ZW5lbW9zIHF1ZSBpbmRpY2FyIGxvcyAqKmdlb21zKiogcGFyYSBhcm1hciB1biBncsOhZmljby4gVXNhbW9zIGBnZW9tX2VkZ2VfbGluaygpYCBwYXJhIGluZGljYXIgbGFzIGNvbmV4aW9uZXMsIGBnZW9tX25vZGVfcG9pbnQoKWAgcGFyYSBub2RvcywgeSBgZ2VvbV9ub2RlX3RleHQoKWAgY29uIGVsIGFyZ3VtZW50byBgYWVzKGxhYmVsID0gbmFtZSlgIHBhcmEgbW9zdHJhciBsYXMgcGFsYWJyYXMuDQoNCk5vdGFyw6FzIHF1ZSBkZW50cm8gZGUgYGdlb21fZWRnZV9saW5rKClgIGhlbW9zIHVzYWRvIGxhIGZ1bmNpw7NuIGBhcnJvdygpYC4gRXN0YSBjcmVhcsOhIGxhcyBmbGVjaGFzIG1hcmNhbmRvIGxhIGRpcmVjY2lvbmFsaWRhZCBkZSBsYXMgcmVsYWNpb25lcy4NCg0KVmVhbW9zIHF1ZSBvYnRlbmVtb3MuIFVzYW1vcyBgc2V0LnNlZWQoKWAgcGFyYSBvYnRlbmVyIHNpZW1wcmUgbGEgbWlzbWEgdmVyc2nDs24gZGUgbGEgcmVkLg0KYGBge3J9DQpzZXQuc2VlZCgxNzUpDQptYW5zb19iaWdyYW1hICU+JSANCiAgZmlsdGVyKG4gPj0gNSkgJT4lIA0KICBncmFwaF9mcm9tX2RhdGFfZnJhbWUoKSAlPiUgDQogIGdncmFwaCgpICsNCiAgZ2VvbV9lZGdlX2xpbmsoYXJyb3cgPSBhcnJvdyh0eXBlID0gImNsb3NlZCIsIGxlbmd0aCA9IHVuaXQoLjA3NSwgImluY2hlcyIpKSkgKw0KICBnZW9tX25vZGVfcG9pbnQoKSArDQogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCB2anVzdCA9IDEsIGhqdXN0ID0gMSkgKyANCiAgdGhlbWVfdm9pZCgpDQpgYGANCg0KwqFOYWRhIG1hbCEgU2luIGVtYmFyZ28sIHRlbmVtb3MgdW4gcGVxdWXDsW8gcHJvYmxlbWEuIEhheSB1bmEgZ3JhbiBjYW50aWRhZCBkZSBjb25leGlvbmVzIGEgdW4gcGFyIGRlIHBhbGFicmFzIHF1ZSBubyBmdWVyb24gaWRlbnRpZmljYWRhcyBjb21vIGh1ZWNhcywgcHVlcyBhcGFyZWNlbiBjb24gdGlsZGUsIGxvIGN1YWwgbm8gZXMgY29udmVuY2lvbmFsIGVuIGVsIGVzcGHDsW9sIG1vZGVybm8gZmlsdHJhbW9zIGVzdGFzIHBhbGFicmFzLg0KDQpgYGB7cn0NCm1hbnNvX2JpZ3JhbWEgPC0gDQogIG1hbnNvX2JpZ3JhbWEgJT4lIA0KICBmaWx0ZXIoIXVubyAlaW4lIGMoIsOhIiwgIsOzIikpICU+JSANCiAgZmlsdGVyKCFkb3MgJWluJSBjKCLDoSIsICLDsyIpKQ0KYGBgDQoNCkNyZWFtb3MgZGUgbnVldm8gbGEgcmVkLg0KYGBge3J9DQpzZXQuc2VlZCgxNzUpDQptYW5zb19iaWdyYW1hICU+JSANCiAgZmlsdGVyKG4gPj0gNSkgJT4lIA0KICBncmFwaF9mcm9tX2RhdGFfZnJhbWUoKSAlPiUgDQogIGdncmFwaCgpICsNCiAgZ2VvbV9lZGdlX2xpbmsoYXJyb3cgPSBhcnJvdyh0eXBlID0gImNsb3NlZCIsIGxlbmd0aCA9IHVuaXQoLjA3NSwgImluY2hlcyIpKSkgKw0KICBnZW9tX25vZGVfcG9pbnQoKSArDQogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCB2anVzdCA9IDEsIGhqdXN0ID0gMSkgKyANCiAgdGhlbWVfdm9pZCgpDQpgYGANCg0KTXVjaG8gbWVqb3IuIEFob3JhIHBvZGVtb3MgdmVyIGNvbiBtw6FzIGNsYXJpZGFkIGFsZ3VuYXMgcmVsYWNpb25lcyBkZSBwYWxhYnJhcyBpbXBvcnRhbnRlcy4gUG9yIGVqZW1wbG8sIHNlZ3VyYW1lbnRlIGhheSB1biBwZXJzb25hamUgbGxhbWFkbyAibWFudWVsIHBlw7FhIiB5IHNlIGhhYmxhIGRlIGxhICJwb2JyZSBuacOxYSBjaHVjaGEiLiBFbiByZWFsaWRhZCwgY29uIGVzdG8gbm9zIGRhbW9zIGN1ZW50YSBxdWUgKipFbCBhbWlnbyBtYW5zbyoqIGVzIHVuYSBub3ZlbGEgcXVlIHNlIGNlbnRyYSBlbiBsYXMgcmVsYWNpb25lcyBxdWUgdGllbmVuIGFsZ3Vub3MgcG9jb3MgcGVyc29uYWplcywgbG9zIGN1YWxlcyBwYXJlY2VuIHRlbmVyIGxhIHRlbmRlbmNpYSBhIGhhYmxhciBkZSBtYW5lcmEgZm9ybWFsIHkgaGFjaWVuZG8gcmVmZXJlbmNpYXMgYWwgcGFzYWRvLiBDb3JyZXNwb25kZSBjb24gbG8gcXVlIHJlY3VlcmRvIGRlIGhhYmVyIGxlw61kbyBlc3RlIGxpYnJvIGhhY2UgdW4gcGFyIGRlIGHDsW9zLg0KDQpDcmVhbW9zIHVuYSBmdW5jacOzbiBwYXJhIGdlbmVyYXIgcmVkZXMsIGNvbiBhbGd1bm9zIGFqdXN0ZXMgcGFyYSBtZWpvcmFyIGxhIHByZXNlbnRhY2nDs24gZGUgbGEgcmVkIHNlbcOhbnRpY2EsIGVudHJlIG90cmFzLCBxdWUgbG9zIHbDrW5jdWxvcyB0ZW5nYW4gdW4gY29sb3IgcXVlIGNvcnJlc3BvbmRhIGxhIGZyZWN1ZW5jaWEgY29uIGxhIHF1ZSBvY3VycmVuLg0KYGBge3J9DQpjcmVhcl9yZWQgPC0gZnVuY3Rpb24obGlicm9fYmlncmFtYSwgdW1icmFsID0gNSkgew0KICBsaWJyb19iaWdyYW1hICU+JSANCiAgICBmaWx0ZXIobiA+IHVtYnJhbCkgJT4lIA0KICAgIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpICU+JSANCiAgICBnZ3JhcGgoKSArDQogICAgZ2VvbV9lZGdlX2xpbmsoYWVzKGVkZ2VfYWxwaGEgPSBuKSwNCiAgICAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KHR5cGUgPSAiY2xvc2VkIiwgbGVuZ3RoID0gdW5pdCguMSwgImluY2hlcyIpKSkgKw0KICAgIGdlb21fbm9kZV9wb2ludChzaXplID0gMiwgY29sb3IgPSAiIzk5NjZkZCIpICsNCiAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgdmp1c3QgPSAxLCBoanVzdCA9IDEpICsNCiAgICB0aGVtZV92b2lkKCkNCn0NCmBgYA0KDQpIYWNlbW9zIGFqdXN0ZXMgYSB1bmEgZnVuY2nDs24gcXVlIHlhIGhhYsOtYW1vcyBjcmVhZG8gcGFyYSBnZW5lcmFyIGJpZ3JhbWFzLg0KDQpgYGB7cn0NCmdlbmVyYXJfYmlncmFtYXMgPC0gZnVuY3Rpb24obGlicm9fcGFycmFmbykgew0KICBsaWJyb19wYXJyYWZvICU+JSANCiAgICB1bm5lc3RfdG9rZW5zKGlucHV0ID0gInRleHRvIiwgb3V0cHV0ID0gImJpZ3JhbWEiLCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMikgJT4lIA0KICAgIHNlcGFyYXRlKGJpZ3JhbWEsIGludG8gPSBjKCJ1bm8iLCAiZG9zIiksIHNlcCA9ICIgIikgJT4lIA0KICAgIGZpbHRlcighdW5vICVpbiUgYyhzdG9wd29yZHMoImVzIiksICLDoSIsICLDsyIpKSAlPiUgDQogICAgZmlsdGVyKCFkb3MgJWluJSBjKHN0b3B3b3JkcygiZXMiKSwgIsOhIiwgIsOzIikpICU+JSANCiAgICBjb3VudCh1bm8sIGRvcykNCn0NCmBgYA0KDQpZIHBvciBzdXB1ZXN0bywgcG9kZW1vcyBjcmVhciB1bmEgZnVuY2nDs24gcXVlIGhhZ2EgdG9kbyBlbCBwcm9jZXNvIGRlIGNyZWFjacOzbiBkZSByZWRlcy4NCmBgYHtyfQ0KcmVkX3RleHRvIDwtIGZ1bmN0aW9uKGFyY2hpdm8sIGluaWNpbywgZmluYWwsIHVtYnJhbCA9IDUpIHsNCiAgICBsZWVyX3RleHRvKGFyY2hpdm8sIGluaWNpbyA9IGluaWNpbywgZmluYWwgPSBmaW5hbCkgICU+JSANCiAgICBjcmVhcl9wYXJyYWZvcygpICU+JSANCiAgICBlbmNvbnRyYXJfY2FwaXR1bG9zKCkgJT4lIA0KICAgIGJvcnJhcl92YWNpb3MoKSAlPiUgDQogICAgZ2VuZXJhcl9iaWdyYW1hcygpICU+JSANCiAgICBjcmVhcl9yZWQodW1icmFsID0gdW1icmFsKQ0KfQ0KYGBgDQoNClBvbmdhbW9zIGEgcHJ1ZWJhIG51ZXN0cmEgZnVuY2nDs24gYHJlZF90ZXh0bygpYC4NCmBgYHtyfQ0Kc2V0LnNlZWQoMTc1KQ0KcmVkX3RleHRvKGFyY2hpdm8gPSAiNTU1NjMtMC50eHQiLCBpbmljaW8gPSAxNTMsIGZpbmFsID0gMTA2MTIsIHVtYnJhbCA9IDUpDQpgYGANCg0KQ29uIGVzdG8gZXN0YXJlbW9zIGxpc3RvcyBwYXJhIGNyZWFyIHJlZGVzIGRlIHRleHRvcyBjb24gdW4gZm9ybWF0byBzaW1pbGFyIGFsIGRlIGxhIG5vdmVsYSBxdWUgaGVtb3MgYW5hbGl6YWRvLg0KDQojIFBhcmEgY29uY2x1aXINCkVuIGVzdGUgYXJ0w61jdWxvIHJldmlzYW1vcyBjb21vIGNyZWFyIHVuYSByZWQgc2Vtw6FudGljYSB1c2FuZG8gUiwgZW4gcGFydGljdWxhciBsYXMgZnVuY2lvbmVzIGRlIGxvcyBwYXF1ZXRlcyAqdGlkeXRleHQqLCAqaWdyYXBoKiB5ICpnZ3JhcGgqLiBFbiBlbCBwcm9jZXNvIHRhbWJpw6luIG5vcyBkaW1vcyBjdWVudGEgcXVlIHNlcGFyYXIgeSB1bmlyIHRleHRvIGRlIGRpc3RpbnRhcyBtYW5lcmFzIHNvbiB0YXJlYXMgZGUgcHJvY2VzYW1pZW50byBtw6FzIGltcG9ydGFudGVzIGVuIG1pbmVyw61hIGRlIHRleHRvLiBFbiB2YXJpYXMgb2Nhc2lvbmVzLCB1bmltb3MgbnVlc3RybyB0ZXh0byBzw7NsbyBwYXJhIHNlcGFyYXJsbyB1bmEgdmV6IG3DoXMsIHBhcmEgYXPDrSBwb2RlciB1bmlybG8gZGUgdW5hIG1hbmVyYSBkaXN0aW50YS4NCg0KTGFzIHJlZGVzIHNlbcOhbnRpY2FzIHNvbiB1bmEgaGVycmFtaWVudGEgbXV5IMO6dGlsIGFsIHJlYWxpemFyIG1pbmVyw61hIGRlIHRleHRvLiBDb21vIHZpbW9zLCBzb24gcmVsYXRpdmFtZW50ZSBzaW1wbGVzIGRlIGltcGxlbWVudGFyIHkgbm9zIHBlcm1pdGVuIGRhcm5vcyB1bmEgaWRlYSBkZSBsb3MgdGVtYXMgbcOhcyBpbXBvcnRhbnRlcyBkZSBudWVzdHJvcyB0ZXh0b3MuIEFkZW3DoXMsIHNvbiBsbyBzdWZpY2llbnRlbWVudGUgZmxleGlibGVzIGNvbW8gcGFyYSBhZGFwdGFyc2UgYSBkaXN0aW50YXMgbmVjZXNpZGFkZXMgZGUgYW7DoWxpc2lzLg0KDQpFbiBlc3RlIGVqZW1wbG8gZ2VuZXJhbW9zIG51ZXN0cmFzIHJlZGVzIHBvciBmcmVjdWVuY2lhLCBwZXJvIGVzIHBvc2libGUgdXRpbGl6YXIgb3Ryb3MgaW5kaWNhZG9yZXMsIHBlcm8gZXNvIGxvIHJldmlzYXJlbW9zIGVuIG90cmEgb2Nhc2nDs24uDQoNCg0K