Presentación
En el presente proyecto se analizarán algunos comentarios hechos a través de la red social Twitter por los usuarios de la banca en México, específicamente, los clientes de Citibanamex, en el periodo comprendido del 21 al 30 de diciembre de 2021. Lo anteior, debido a que dicha red social solo permite que se extraigan datos de los últimos 6-9 días. Se extrajeron tweets de la cuenta @ContactoCitibmx, que es uno de los canales de atención a clientes que tiene dicha entidad financiera para interactuar con sus clientes.
Este análisis es de minería de texto, por lo que, la mayoría del trabajo mostrará técnicas estadísticas descriptivas y exploratorias.
Los datos se pueden extraer directamente desde twitter, sin embargo, por comodidad y para seguir el presete trabajo, también, se encuentran en mi repositorio de github junto con el código de esta publicación.
Contenido
- Presentación
- Contenido
- Set up
- Extracción de la información
- Tokenizacion y limpieza de texto
- Análisis exploratorio
- Distribución temporal de los tweets
- Frecuencia de palabras
- Nube de palabras
- Relación entre palabras
- Análisis de sentimientos
- Conclusiones
Set up
Iniciamos configurando las opciones generales que vamos a requerir para el desarrollo de este proyecto.
knitr::opts_chunk$set(echo = TRUE,
warning = FALSE,
message = FALSE,
fig.align = "center"
)
paquetes <- c("rtweet", "tidyverse", "lubridate", "wordcloud", "RColorBrewer", "tidytext", "igraph", "ggraph", "knitr", "DT", "kableExtra", "tm", "reshape2")
instalados <- paquetes %in% installed.packages()
if(sum(instalados == FALSE) > 0) {
install.packages(paquetes[!instalados])
}
lapply(paquetes, require, character.only = TRUE)
Tokenizacion y limpieza de texto
En este proyecto, el proceso de limpieza consiste en eliminar del texto todas aquellas palabras, signos, números, abreviaturas, etc., que no aportan información importante.
Además, para poder seguir adelante es necesario tener la información de los comentarios (columna text), en palabras individuales para poder obtener, entre otras cosas sus frecuencias para saber cuáles son las más mencionadas por los usuarios.
source("limpieza.R")
tweets.tkns <- tweets %>%
mutate(texto_tokenizado = map(.x = text,
.f = limpiar_tokenizar))
tweets.tkns %>%
slice(1) %>%
select(texto_tokenizado) %>%
pull()
## [[1]]
## [1] "citibanamex" "que" "les" "pasa" "estoy"
## [6] "muy" "molesta" "tienen" "acaso" "ejecutivos"
## [11] "para" "atender" "audio" "matico" "llevo"
## [16] "semana" "tratando" "que" "ser" "humano"
## [21] "atienda" "voy" "sucursal" "dice" "ejecutivo"
## [26] "que" "duda" "solo" "puede" "ser"
## [31] "resuelta" "audiomatico" "dice" "que" "hay"
## [36] "agentes"
tweets.tkns <- tweets.tkns %>%
select(-text) %>%
unnest()
tweets.tkns <- tweets.tkns %>%
rename(token = texto_tokenizado)
tweets.tkns %>%
head(10) %>%
kbl(
align = "c",
caption = "Tokenización",
) %>%
kable_paper("hover", full_width = F)
Tokenización
|
created_at
|
token
|
|
2021-12-30 23:41:04
|
citibanamex
|
|
2021-12-30 23:41:04
|
que
|
|
2021-12-30 23:41:04
|
les
|
|
2021-12-30 23:41:04
|
pasa
|
|
2021-12-30 23:41:04
|
estoy
|
|
2021-12-30 23:41:04
|
muy
|
|
2021-12-30 23:41:04
|
molesta
|
|
2021-12-30 23:41:04
|
tienen
|
|
2021-12-30 23:41:04
|
acaso
|
|
2021-12-30 23:41:04
|
ejecutivos
|
Como los comentarios están en el idioma Español, se utilizará el paquete tm para trabajar con las llamadas stopwords, que son palabras que no aportan valor al análisis como los artículos, conectores, etc.
custom_stop_words <- bind_rows(
tibble(word = tm::stopwords("spanish"),
lexicon = "custom"))
tweets.tkns <- tweets.tkns %>%
rename(word = token,
fecha = created_at)
tweets.tkns <- anti_join(x = tweets.tkns,
y = custom_stop_words,
by = "word")
tweets.tkns %>%
head(10) %>%
kbl(
align = "c",
caption = "Tokenización",
) %>%
kable_paper("hover", full_width = F)
Tokenización
|
fecha
|
word
|
|
2021-12-30 23:41:04
|
citibanamex
|
|
2021-12-30 23:41:04
|
pasa
|
|
2021-12-30 23:41:04
|
molesta
|
|
2021-12-30 23:41:04
|
acaso
|
|
2021-12-30 23:41:04
|
ejecutivos
|
|
2021-12-30 23:41:04
|
atender
|
|
2021-12-30 23:41:04
|
audio
|
|
2021-12-30 23:41:04
|
matico
|
|
2021-12-30 23:41:04
|
llevo
|
|
2021-12-30 23:41:04
|
semana
|
El resultado de la limpieza y tokenización es un marco de datos de 32,211 filas y 2 columnas:
dim(tweets.tkns)
## [1] 32211 2
Análisis exploratorio
A continuación, se crearán algunas gráficas que faciliten el análisis exploratorio tanto del comportamiento de los usuarios como del contenido de sus comentarios. Lo anterior, nos dará una primer idea de lo que transmitieron en sus tweets.
Frecuencia de palabras
Veamos cuáles son las palabras más utilizadas por los usuarios en sus tweets.
count(tweets.tkns,
word,
sort = TRUE) %>%
head(10) %>%
kbl(
align = "c",
caption = "Tokenización",
) %>%
kable_styling("hover", full_width = F)
Tokenización
|
word
|
n
|
|
citibanamex
|
2149
|
|
banamex
|
629
|
|
tarjeta
|
390
|
|
banco
|
245
|
|
cuenta
|
201
|
|
hacer
|
178
|
|
dinero
|
171
|
|
solo
|
150
|
|
sucursal
|
150
|
|
bbva
|
149
|
tweets.tkns %>%
count(word) %>%
top_n(10, n) %>%
arrange(desc(n)) %>%
ggplot(aes(x = reorder(word, n), y = n)) +
geom_col(fill = "steelblue", color = "firebrick") +
theme_bw() +
labs(y = "", x = "") +
theme(legend.position = "none") +
scale_y_continuous(labels = scales::comma) +
coord_flip() +
labs(title = "Top 10: Palabras más frecuentes",
y = "Frecuencia")

No es de extrañar que las palabras más frecuentes tengan que ver con el nombre de la institución bancaria a la que pertenecen los usuarios cuyos tweets descargamos. También, por las demás palabras parece ser que los comentarios de los usuarios tienen que ver con señalamientos referentes a tarjetas, cuentas, banco, sucursal, etc.
Nube de palabras
Otra forma de ver las palabras más frecuentes y más utilizada en temas de análisis de texto es la Word Cloud o nube de palabras, la cual es una representación gráfica de la frecuencia y cuyas palabras más importantes (repetidas) tienen mayor tamaño.
count(tweets.tkns, word) %>%
with(wordcloud(words = word,
freq = n,
max.words = 400,
scale = c(3,1),
rot.per = 0.3,
random.order = FALSE,
colors = brewer.pal(6, "Dark2")))

Relación entre palabras
Se han hecho descripciones del texto considerando palabras de forma individual que nos han mostrado las más utilizadas por los usuarios, sin embargo, para hacer una descripción un poco más completa y realista es conveniente analizar las palabras en forma conjunta. Lo siguiente que se hará es calcular y visualizar las relaciones entre dos palabras (bigramas).
bigramas <- tweets %>%
mutate(texto = limpiar(text)) %>%
select(text) %>%
unnest_tokens(input = text,
output = "bigrama",
token = "ngrams",
n = 2,
drop = TRUE)
bigramas %>%
count(bigrama, sort = TRUE) %>%
head(10) %>%
kbl(
align = "c",
caption = "Tokenización",
) %>%
kable_styling("hover", full_width = F)
Tokenización
|
bigrama
|
n
|
|
https t.co
|
693
|
|
que no
|
196
|
|
no me
|
165
|
|
y no
|
153
|
|
de citibanamex
|
152
|
|
en el
|
151
|
|
de la
|
146
|
|
en la
|
136
|
|
que me
|
128
|
|
de banamex
|
126
|
Se quitan manualmente algunas palabras que no aportan valor al análisis:
bigramas <- filter(bigramas,
bigrama!="https t.co")
En el resumen anterior, se aprecia que los bigramas más frecuentes están formados por stopwords, por lo que, se eliminarán por no aportar valor al análisis.
bigramas_separados <- bigramas %>%
separate(bigrama, c("palabra1", "palabra2"), sep = " ")
head(bigramas_separados)
# Filtrado de los bigramas que contienen alguna stopword
bigramas_separados <- bigramas_separados %>%
filter(!palabra1 %in% custom_stop_words$word) %>%
filter(!palabra2 %in% custom_stop_words$word)
# Unión de las palabras para formar de nuevo los bigramas
bigramas <- bigramas_separados %>%
unite(bigrama, palabra1, palabra2, sep = " ")
bigramas %>%
count(bigrama, sort = TRUE) %>%
head(20) %>%
kbl(
align = "c",
caption = "Tokenización",
) %>%
kable_styling("hover", full_width = F)
Tokenización
|
bigrama
|
n
|
|
almendrayek citibanamex
|
77
|
|
tarjeta citibanamex
|
66
|
|
citibanamex https
|
56
|
|
citibanamex contactocitibmx
|
44
|
|
citibanamex si
|
40
|
|
banco azteca
|
38
|
|
tarjeta banamex
|
38
|
|
citibanamex hola
|
36
|
|
pésimo servicio
|
32
|
|
contactocitibmx citibanamex
|
30
|
|
citibanamex gracias
|
25
|
|
puedo hacer
|
23
|
|
msi https
|
22
|
|
citibanamex condusefmx
|
21
|
|
pesos 10
|
21
|
|
2021 https
|
19
|
|
pesos https
|
19
|
|
preventa citibanamex
|
19
|
|
centro citibanamex
|
18
|
|
mejor fin
|
18
|
Una forma más visual e informativa de analizar las relaciones entre las palabras es mediante el uso de redes.
graph <- bigramas %>%
separate(bigrama, c("palabra1", "palabra2"), sep = " ") %>%
count(palabra1, palabra2, sort = TRUE) %>%
filter(n > 18) %>%
graph_from_data_frame(directed = FALSE)
set.seed(123)
plot(graph, vertex.label.font = 3,
vertex.label.color = "black",
vertex.label.cex = 0.9, edge.color = "gray85")

Podemos apreciar que dentro del top 10 de los bigramas están:
- tarjeta citibanamex
- banco azteca
- pésimo servicio
Probablemente, las quejas tienen que ver con alguna tarjeta de crédito, con la atención al público tanto en sucursal, vía telefónica y online.
Análisis de sentimientos
En esta última parte del trabajo se hará el análisis de sentimientos, es decir, se clasificarán las palabras contenidas en los tweets en una de las siguientes dos categorías: positiva o negativa, además, utilizando el lexicon nrc se podrán también clasificar las palabras en los siguientes sentimientos: confianza, ira, disgusto, asombro, alegría, miedo, tristeza y premonición.
El archivo de sentimientos se puede descargar del repositorio github del proyecto 7PartidasDigital elaborado por la Universidad de Valladolid.
source("get_sentiments.R")
sentimientos <- readRDS(file = "sentimientos.rds")
tweets.tkns <- tweets.tkns %>%
rename(palabra = word)
Veamos la cantidad de palabras clasificadas como positivas y negativas de todos los tweets
tweets.tkns %>%
right_join(get_sentiments("nrc")) %>%
filter(!is.na(sentimiento) & sentimiento == "positivo" | sentimiento == "negativo") %>%
count(sentimiento, sort = TRUE) %>%
kable(
format = "html",
digits = 0,
caption = "Sentimientos Positivos - Negativos",
format.args = list(big.mark = ","),
table.attr = "style='width:50%;'") %>%
kable_styling(bootstrap_options = c("striped", "hover"))
Sentimientos Positivos - Negativos
|
sentimiento
|
n
|
|
positivo
|
3,960
|
|
negativo
|
3,824
|
tweets.tkns %>%
right_join(get_sentiments("nrc")) %>%
filter(!is.na(sentimiento)& sentimiento == "positivo" | sentimiento == "negativo") %>%
count(palabra, sentimiento, sort = TRUE) %>%
acast(palabra ~ sentimiento, value.var = "n", fill = 0) %>%
comparison.cloud(colors = c("red","blue"),
max.words = 300,
title.size = 2)

En el cuadro anterior, se aprecia que se detectó casi la misma cantidad de palabras negativas que positivas. Ahora, se muestra el detalle (top 15) de las palabras en cada categoría.
tweets.tkns %>%
right_join(get_sentiments("nrc")) %>%
filter(!is.na(sentimiento)& sentimiento == "positivo" | sentimiento == "negativo") %>%
count(palabra, sentimiento, short = TRUE) %>%
group_by(sentimiento) %>%
top_n(15) %>%
ungroup() %>%
mutate(palabra = reorder(palabra, n)) %>%
ggplot(aes(palabra, n, fill = sentimiento)) +
geom_col(show.legend = FALSE) +
scale_fill_manual(values = c("darkred", "steelblue")) +
geom_text(aes(label = n), hjust = 1.2, color = "white") +
facet_wrap(~sentimiento, scales = "free_y") +
coord_flip() +
xlab(NULL) +
labs(title = "Detalle de Sentimientos")

En las gráficas anteriores, se aprecian las palabras que los usuarios capturaron en sus tweets y que el algoritmo los clasificó como negativos o positivos. Las palabras negativas parece que tienen más que ver con reclamos y quejas de los usuarios como robo, mal, peor, cancelar, problema, entre otras.
Ahora, veamos el conteo de los demás sentimientos para darnos una idea del ánimo de los usuarios cuando escribieron sus tweets.
tweets.tkns %>%
right_join(get_sentiments("nrc")) %>%
filter(!is.na(sentimiento)) %>%
count(sentimiento, sort = TRUE) %>%
kable(
format = "html",
digits = 0,
caption = "Resumen de Sentimientos",
format.args = list(big.mark = ","),
table.attr = "style='width:50%;'") %>%
kable_styling(bootstrap_options = c("striped", "hover"))
Resumen de Sentimientos
|
sentimiento
|
n
|
|
positivo
|
3,960
|
|
negativo
|
3,824
|
|
confianza
|
2,857
|
|
miedo
|
1,945
|
|
tristeza
|
1,678
|
|
premonición
|
1,674
|
|
ira
|
1,610
|
|
disgusto
|
1,271
|
|
alegría
|
1,234
|
|
asombro
|
941
|
Gráficamente:
tweets.tkns %>%
right_join(get_sentiments("nrc")) %>%
filter(!is.na(sentimiento)) %>%
group_by(sentimiento) %>%
count(sentimiento, sort = TRUE) %>%
mutate(sentimiento = reorder(factor(sentimiento), n)) %>%
ggplot(aes(reorder(sentimiento,n), n, fill = sentimiento)) +
geom_col(show.legend = FALSE) +
geom_text(aes(label = format(n, big.mark = ",")), hjust = 1.2, color = "black") +
coord_flip() +
xlab(NULL) +
labs(title = "Detalle de Emociones")

Bajando más el análisis para conocer las palabras contenidas en cada emoción:
tweets.tkns %>%
right_join(get_sentiments("nrc")) %>%
filter(!is.na(sentimiento)) %>%
count(palabra, sentimiento, short = TRUE) %>%
group_by(sentimiento) %>%
top_n(15) %>%
ungroup() %>%
mutate(palabra = reorder(palabra, n)) %>%
ggplot(aes(palabra, n, fill = sentimiento)) +
geom_col(show.legend = FALSE) +
geom_text(aes(label = n), hjust = 1.2, color = "white") +
facet_wrap(~sentimiento, scales = "free_y") +
coord_flip() +
xlab(NULL) +
labs(title = "Detalle de Emociones")

Hay algunas palabras que se clasifican en más de una emoción como, por ejemplo, dinero que se encuentra en las categorías de alegría, asombro e ira. Lo anterior, porque depende mucho del contexto de la palabra.
tweets.tkns %>%
right_join(get_sentiments("nrc")) %>%
count(palabra, sentimiento, sort = TRUE) %>%
filter(!is.na(sentimiento), sentimiento != "positivo", sentimiento != "negativo") %>%
acast(palabra ~ sentimiento, value.var = "n", fill = 0) %>%
comparison.cloud(title.size = 1.5)

Conclusiones
Después de haber realizado este pequeño análisis podemos concluir que los usuarios de la institución bancaria en cuestión se suelen quejar por temas relacionados con errores en algunas aplicaciones, se sinten robados, pésimo o mal servicio, cancelaciones y con algunos otros problemas que han tenido como clientes.
El resultado de hacer un análisis de emociones nos indica que la mayoría de los usuarios que comentaron en su cuenta de twitter tenían emociones negativas como miedo, tristeza, ira y disgusto. Lo anterior, porque fueron las emociones con mayor frecuencia, sin embargo, al clasificar todas las palabras en positivas y negativas la proporción es casi la misma.
Hay que aclarar que depende del lexicon o diccionario utilizado los resultados serán distintos.
Con este corto trabajo podemos darnos una idea de lo que el equipo de atención a clientes del banco podría hacer para disminuir los reclamos o quejas como, por ejemplo, realizar encuestas a los clientes que van directamente a sucursal y enviar encuestas vía correo electrónico después de usar algún cajero automático o directamente en su página web. Actualmente, hay empresas que se dedican a proporcionar ese servicio y ya solo le entregan los resultados al cliente, el cual con su equipo o área de analítica se encarga de procesar dicha información para la toma de decisiones.
LS0tDQp0aXRsZTogIkFuYWxpc2lzIGRlIFR3ZWV0cyBVc3VhcmlvcyBDaXRpYmFuYW1leCINCnN1YnRpdGxlOiAiUHJveWVjdG86IFByb2Nlc2FtaWVudG8gZGVsIExlbmd1YWplIE5hdHVyYWwiDQphdXRob3I6ICJBbGVqYW5kcm8gUm9qYXMgTW9yZW5vIg0KZGF0ZTogIjQvMS8yMDIyIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICB0aGVtZTogc3BhY2VsYWINCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCmVkaXRvcl9vcHRpb25zOg0KICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQ0KLS0tDQoNCjxicj4NCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6cmdiKDAsIDAsIDIwNSkiPlByZXNlbnRhY2nDs248L3NwYW4+DQoNCjxkaXYgY2xhc3M9dGV4dC1qdXN0aWZ5Pg0KRW4gZWwgcHJlc2VudGUgcHJveWVjdG8gc2UgYW5hbGl6YXLDoW4gYWxndW5vcyBjb21lbnRhcmlvcyBoZWNob3MgYSB0cmF2w6lzIGRlIGxhIHJlZCBzb2NpYWwgKipUd2l0dGVyKiogcG9yIGxvcyB1c3VhcmlvcyBkZSBsYSBiYW5jYSBlbiBNw6l4aWNvLCBlc3BlY8OtZmljYW1lbnRlLCBsb3MgY2xpZW50ZXMgZGUgKipDaXRpYmFuYW1leCoqLCBlbiBlbCBwZXJpb2RvIGNvbXByZW5kaWRvIGRlbCAyMSBhbCAzMCBkZSBkaWNpZW1icmUgZGUgMjAyMS4gTG8gYW50ZWlvciwgZGViaWRvIGEgcXVlIGRpY2hhIHJlZCBzb2NpYWwgc29sbyBwZXJtaXRlIHF1ZSBzZSBleHRyYWlnYW4gZGF0b3MgZGUgbG9zIMO6bHRpbW9zIDYtOSBkw61hcy4gU2UgZXh0cmFqZXJvbiB0d2VldHMgZGUgbGEgY3VlbnRhIEBDb250YWN0b0NpdGlibXgsIHF1ZSBlcyB1bm8gZGUgbG9zIGNhbmFsZXMgZGUgYXRlbmNpw7NuIGEgY2xpZW50ZXMgcXVlIHRpZW5lIGRpY2hhIGVudGlkYWQgZmluYW5jaWVyYSBwYXJhIGludGVyYWN0dWFyIGNvbiBzdXMgY2xpZW50ZXMuDQoNCkVzdGUgYW7DoWxpc2lzIGVzIGRlIG1pbmVyw61hIGRlIHRleHRvLCBwb3IgbG8gcXVlLCBsYSBtYXlvcsOtYSBkZWwgdHJhYmFqbyBtb3N0cmFyw6EgdMOpY25pY2FzIGVzdGFkw61zdGljYXMgZGVzY3JpcHRpdmFzIHkgZXhwbG9yYXRvcmlhcy4NCg0KTG9zIGRhdG9zIHNlIHB1ZWRlbiBleHRyYWVyIGRpcmVjdGFtZW50ZSBkZXNkZSB0d2l0dGVyLCBzaW4gZW1iYXJnbywgcG9yIGNvbW9kaWRhZCB5IHBhcmEgc2VndWlyIGVsIHByZXNldGUgdHJhYmFqbywgdGFtYmnDqW4sIHNlIGVuY3VlbnRyYW4gZW4gbWkgcmVwb3NpdG9yaW8gZGUgW2dpdGh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL2Fyb2phc21vci9RdWVqYXNibm14KSBqdW50byBjb24gZWwgY8OzZGlnbyBkZSBlc3RhIHB1YmxpY2FjacOzbi4NCjwvZGl2Pg0KDQo8YnI+DQoNCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJnYigwLCAwLCAyMDUpIj5Db250ZW5pZG88L3NwYW4+DQoNCiogUHJlc2VudGFjacOzbg0KKiBDb250ZW5pZG8NCiogU2V0IHVwDQoqIEV4dHJhY2Npw7NuIGRlIGxhIGluZm9ybWFjacOzbg0KKiBUb2tlbml6YWNpb24geSBsaW1waWV6YSBkZSB0ZXh0bw0KKiBBbsOhbGlzaXMgZXhwbG9yYXRvcmlvDQogICArIERpc3RyaWJ1Y2nDs24gdGVtcG9yYWwgZGUgbG9zIHR3ZWV0cw0KICAgKyBGcmVjdWVuY2lhIGRlIHBhbGFicmFzDQogICArIE51YmUgZGUgcGFsYWJyYXMNCiogUmVsYWNpw7NuIGVudHJlIHBhbGFicmFzDQoqIEFuw6FsaXNpcyBkZSBzZW50aW1pZW50b3MNCiogQ29uY2x1c2lvbmVzDQoNCjxicj4NCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6cmdiKDAsIDAsIDIwNSkiPlNldCB1cDwvc3Bhbj4NCg0KSW5pY2lhbW9zIGNvbmZpZ3VyYW5kbyBsYXMgb3BjaW9uZXMgZ2VuZXJhbGVzIHF1ZSB2YW1vcyBhIHJlcXVlcmlyIHBhcmEgZWwgZGVzYXJyb2xsbyBkZSBlc3RlIHByb3llY3RvLg0KDQpgYGB7ciBzZXR1cCwgbWVzc2FnZT1GQUxTRSwgY29tbWVudD0iIiwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ24gPSAiY2VudGVyIg0KICAgICAgICAgICAgICAgICAgICAgICkNCg0KcGFxdWV0ZXMgPC0gYygicnR3ZWV0IiwgInRpZHl2ZXJzZSIsICJsdWJyaWRhdGUiLCAid29yZGNsb3VkIiwgIlJDb2xvckJyZXdlciIsICJ0aWR5dGV4dCIsICJpZ3JhcGgiLCAiZ2dyYXBoIiwgImtuaXRyIiwgIkRUIiwgImthYmxlRXh0cmEiLCAidG0iLCAicmVzaGFwZTIiKQ0KDQppbnN0YWxhZG9zIDwtIHBhcXVldGVzICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkNCg0KaWYoc3VtKGluc3RhbGFkb3MgPT0gRkFMU0UpID4gMCkgew0KICBpbnN0YWxsLnBhY2thZ2VzKHBhcXVldGVzWyFpbnN0YWxhZG9zXSkNCn0NCg0KbGFwcGx5KHBhcXVldGVzLCByZXF1aXJlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQpgYGANCg0KJm5ic3A7DQoNCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJnYigwLCAwLCAyMDUpIj5FeHRyYWNjacOzbiBkZSBkZSBpbmZvcm1hY2nDs248L3NwYW4+DQoNCkxhIGluZm9ybWFjacOzbiBkZSBsb3MgdHdlZXRzIHNlIHB1ZWRlbiBkZXNjYXJnYXIgZGlyZWN0YW1lbnRlIGNvbiBhcG95byBkZSBsYSBmdW5jacOzbiAqKnNlYXJjaF90d2VldHMqKiBjb250ZW5pZGEgZW4gZWwgcGFxdWV0ZSAqKnJ0d2VldCoqOg0KDQpgYGB7ciBldmFsPUZBTFNFfQ0KIyBzZSBleHRyYWVuIGxvcyB0d2VldHMNCmRzIDwtIE1hcCgNCiAgInNlYXJjaF90d2VldHMiLA0KICBxID0gIkBDaXRpYmFuYW1leCBPUiBCYW5hbWV4IE9SIENpdGliYW5hbWV4IE9SIGJhbmFtZXggT1IgQGJhbmFtZXgiLA0KICBuID0gNTAwMCwNCiAgcGFyc2UgPSBUUlVFLA0KICBpbmNsdWRlX3J0cyA9IEZBTFNFLA0KICBsYW5nID0gImVzIg0KKQ0KDQojIHVuaXIgbG9zIGRhdG9zDQpkcyA8LSBkb19jYWxsX3JiaW5kKGRzKQ0KDQojIHNlIHNlbGVjY2lvbmFuIGNvbHVtbmFzDQpkcyA8LSBkcyAlPiUgDQogIHNlbGVjdCgic2NyZWVuX25hbWUiLCANCiAgICAgICAgICJjcmVhdGVkX2F0IiwgDQogICAgICAgICAic3RhdHVzX2lkIiwgDQogICAgICAgICAidGV4dCIsDQogICAgICAgICAidXNlcl9pZCIsDQogICAgICAgICAidmVyaWZpZWQiLA0KICAgICAgICAgImxvY2F0aW9uIikNCg0KIyBzZSBndWFyZGEgZWwgYXJjaGl2bw0Kd3JpdGVfY3N2KGRzLCAidHdlZXRzLmNzdiIpDQpgYGANCg0KYGBge3J9DQojIHNlIGNhcmdhbiBsb3MgZGF0b3MgcHJldmlhbWVudGUgZ3VhcmRhZG9zDQp0d2VldHMgPC0gcmVhZF9jc3YoInR3ZWV0cy5jc3YiKQ0KDQojIHNlIHF1aXRhbiBsYXMgcmVzcHVlc3RhcyBkZSBsYSBpbnN0aXR1Y2nDs24sIHlhIHF1ZSBub3MgaW50ZXJlc2FuIHNvbG8gbG9zIGNvbWVudGFyaW9zDQojIGRlIGxvcyBjbGllbnRlcw0KdHdlZXRzIDwtIHR3ZWV0cyAlPiUgDQogIGZpbHRlcihzY3JlZW5fbmFtZSAhPSJDaXRpYmFuYW1leCIgJiBzY3JlZW5fbmFtZSAhPSJDb250YWN0b0NpdGlibXgiKSAlPiUgDQogIHNlbGVjdChjKGNyZWF0ZWRfYXQsIHRleHQpKQ0KDQpgYGANCg0KU2UgbXVlc3RyYSB1biBmcmFnbWVudG8gZGUgbG9zIHR3ZWV0cyBleHRyYWlkb3M6DQoNCmBgYHtyfQ0KRFQ6OmRhdGF0YWJsZShoZWFkKHR3ZWV0cywgMjApLA0KICAgICAgICAgICAgICByb3duYW1lcyA9IEZBTFNFLA0KICAgICAgICAgICAgICBvcHRpb25zID0gbGlzdCgNCiAgICAgICAgICAgICAgICBwYWdlTGVuZ3RoID0gNSwNCiAgICAgICAgICAgICAgICBzY3JvbGxYID0gVFJVRSkpDQoNCmBgYA0KDQombmJzcDsNCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6cmdiKDAsIDAsIDIwNSkiPlRva2VuaXphY2lvbiB5IGxpbXBpZXphIGRlIHRleHRvPC9zcGFuPg0KDQpFbiBlc3RlIHByb3llY3RvLCBlbCBwcm9jZXNvIGRlIGxpbXBpZXphIGNvbnNpc3RlIGVuIGVsaW1pbmFyIGRlbCB0ZXh0byB0b2RhcyBhcXVlbGxhcyBwYWxhYnJhcywgc2lnbm9zLCBuw7ptZXJvcywgYWJyZXZpYXR1cmFzLCBldGMuLCBxdWUgbm8gYXBvcnRhbiBpbmZvcm1hY2nDs24gaW1wb3J0YW50ZS4NCg0KQWRlbcOhcywgcGFyYSBwb2RlciBzZWd1aXIgYWRlbGFudGUgZXMgbmVjZXNhcmlvIHRlbmVyIGxhIGluZm9ybWFjacOzbiBkZSBsb3MgY29tZW50YXJpb3MgKGNvbHVtbmEgdGV4dCksIGVuIHBhbGFicmFzIGluZGl2aWR1YWxlcyBwYXJhIHBvZGVyIG9idGVuZXIsIGVudHJlIG90cmFzIGNvc2FzIHN1cyBmcmVjdWVuY2lhcyBwYXJhIHNhYmVyIGN1w6FsZXMgc29uIGxhcyBtw6FzIG1lbmNpb25hZGFzIHBvciBsb3MgdXN1YXJpb3MuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQpzb3VyY2UoImxpbXBpZXphLlIiKQ0KDQp0d2VldHMudGtucyA8LSB0d2VldHMgJT4lIA0KICBtdXRhdGUodGV4dG9fdG9rZW5pemFkbyA9IG1hcCgueCA9IHRleHQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5mID0gbGltcGlhcl90b2tlbml6YXIpKQ0KDQp0d2VldHMudGtucyAlPiUgDQogIHNsaWNlKDEpICU+JSANCiAgc2VsZWN0KHRleHRvX3Rva2VuaXphZG8pICU+JQ0KICBwdWxsKCkNCg0KdHdlZXRzLnRrbnMgPC0gdHdlZXRzLnRrbnMgJT4lIA0KICBzZWxlY3QoLXRleHQpICU+JQ0KICB1bm5lc3QoKQ0KDQp0d2VldHMudGtucyA8LSB0d2VldHMudGtucyAlPiUgDQogIHJlbmFtZSh0b2tlbiA9IHRleHRvX3Rva2VuaXphZG8pDQoNCnR3ZWV0cy50a25zICU+JSANCiAgaGVhZCgxMCkgJT4lIA0KICBrYmwoIA0KICAgICAgYWxpZ24gPSAiYyIsDQogICAgICBjYXB0aW9uID0gIlRva2VuaXphY2nDs24iLA0KICAgICAgKSAlPiUNCiAga2FibGVfcGFwZXIoImhvdmVyIiwgZnVsbF93aWR0aCA9IEYpDQoNCmBgYA0KDQpDb21vIGxvcyBjb21lbnRhcmlvcyBlc3TDoW4gZW4gZWwgaWRpb21hIEVzcGHDsW9sLCBzZSB1dGlsaXphcsOhIGVsIHBhcXVldGUgKip0bSoqIHBhcmEgdHJhYmFqYXIgY29uIGxhcyBsbGFtYWRhcyBzdG9wd29yZHMsIHF1ZSBzb24gcGFsYWJyYXMgcXVlIG5vIGFwb3J0YW4gdmFsb3IgYWwgYW7DoWxpc2lzIGNvbW8gbG9zIGFydMOtY3Vsb3MsIGNvbmVjdG9yZXMsIGV0Yy4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCmN1c3RvbV9zdG9wX3dvcmRzIDwtIGJpbmRfcm93cygNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aWJibGUod29yZCA9IHRtOjpzdG9wd29yZHMoInNwYW5pc2giKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxleGljb24gPSAiY3VzdG9tIikpDQp0d2VldHMudGtucyA8LSB0d2VldHMudGtucyAlPiUgDQogIHJlbmFtZSh3b3JkID0gdG9rZW4sDQogICAgICAgICBmZWNoYSA9IGNyZWF0ZWRfYXQpDQoNCnR3ZWV0cy50a25zIDwtIGFudGlfam9pbih4ID0gdHdlZXRzLnRrbnMsDQogICAgICAgICAgICAgICAgICAgIHkgPSBjdXN0b21fc3RvcF93b3JkcywNCiAgICAgICAgICAgICAgICAgICAgYnkgPSAid29yZCIpDQoNCnR3ZWV0cy50a25zICU+JSANCiAgaGVhZCgxMCkgJT4lIA0KICBrYmwoIA0KICAgICAgYWxpZ24gPSAiYyIsDQogICAgICBjYXB0aW9uID0gIlRva2VuaXphY2nDs24iLA0KICAgICAgKSAlPiUNCiAga2FibGVfcGFwZXIoImhvdmVyIiwgZnVsbF93aWR0aCA9IEYpDQoNCmBgYA0KDQpFbCByZXN1bHRhZG8gZGUgbGEgbGltcGllemEgeSB0b2tlbml6YWNpw7NuIGVzIHVuIG1hcmNvIGRlIGRhdG9zIGRlIDMyLDIxMSBmaWxhcyB5IDIgY29sdW1uYXM6DQoNCmBgYHtyfQ0KZGltKHR3ZWV0cy50a25zKQ0KYGBgDQoNCiZuYnNwOw0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZ2IoMCwgMCwgMjA1KSI+QW7DoWxpc2lzIGV4cGxvcmF0b3Jpbzwvc3Bhbj4NCg0KQSBjb250aW51YWNpw7NuLCBzZSBjcmVhcsOhbiBhbGd1bmFzIGdyw6FmaWNhcyBxdWUgZmFjaWxpdGVuIGVsIGFuw6FsaXNpcyBleHBsb3JhdG9yaW8gdGFudG8gZGVsIGNvbXBvcnRhbWllbnRvIGRlIGxvcyB1c3VhcmlvcyBjb21vIGRlbCBjb250ZW5pZG8gZGUgc3VzIGNvbWVudGFyaW9zLiBMbyBhbnRlcmlvciwgbm9zIGRhcsOhIHVuYSBwcmltZXIgaWRlYSBkZSBsbyBxdWUgdHJhbnNtaXRpZXJvbiBlbiBzdXMgdHdlZXRzLg0KDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6cmdiKDAsIDAsIDIwNSkiPkRpc3RyaWJ1Y2nDs24gdGVtcG9yYWwgZGUgbG9zIHR3ZWV0czwvc3Bhbj4NCg0KVmVhbW9zIGxhIGRpc3RyaWJ1Y2nDs24gZGUgbG9zIHR3ZWV0cyBwYXJhIHZlciBzdSBldm9sdWNpw7NuIGVuIGVsIHRpZW1wbyBkdXJhbnRlIGxvcyA5IGTDrWFzIGRlIGFjdGl2aWRhZCBjb24gbGEgcXVlIGNvbnRhbW9zDQoNCmBgYHtyfQ0KdHdlZXRzLnRrbnMgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBhcy5EYXRlKGZlY2hhKSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0ocG9zaXRpb24gPSAiaWRlbnRpdHkiLCANCiAgICAgICAgICAgICAgICAgYmlucyA9IDIwLCANCiAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgIGZpbGwgPSAic3RlZWxibHVlIiwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZmlyZWJyaWNrIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpjb21tYSkgKw0KICBzY2FsZV94X2RhdGUoZGF0ZV9sYWJlbHMgPSAiJWQtJW0tJVkiLCBkYXRlX2JyZWFrcyA9ICIxIGRheSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnVjacOzbiBUd2VldHMgcG9yIETDrWEiLCB4ID0gImZlY2hhIGRlIHB1YmxpY2FjacOzbiIsIHkgPSAibsO6bWVybyBkZSB0d2VldHMiKSANCg0KYGBgDQoNCkVuIGVzdGEgcHJpbWVyIGdyw6FmaWNhIHNlIGFwcmVjaWEgcXVlIGxvcyDDumx0aW1vcyBjdWF0cm8gZMOtYXMgZGVsIGHDsW8gMjAyMSBodWJvIHVuYSBtYXlvciBhY3RpdmlkYWQgZGUgbG9zIHVzdWFyaW9zLCBzaWVuZG8gZWwgZMOtYSAyOCBlbCBkZSBtYXlvciBjYW50aWRhZCBkZSBtZW5zYWplcy4NCg0KYGBge3J9DQp0d2VldHMudGtucyAlPiUgDQogIGdncGxvdChhZXMoeCA9IGFzLlBPU0lYY3QoZmVjaGEpKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShwb3NpdGlvbiA9ICJpZGVudGl0eSIsIA0KICAgICAgICAgICAgICAgICBiaW5zID0gMjAsIA0KICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgZmlsbCA9ICJzdGVlbGJsdWUiLA0KICAgICAgICAgICAgICAgICBjb2xvciA9ICJmaXJlYnJpY2siKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSArDQogIHNjYWxlX3hfZGF0ZXRpbWUoZGF0ZV9sYWJlbHMgPSAiJWQtJW0tJVkgJUgiLCBkYXRlX2JyZWFrcyA9ICI2IGhvdXJzIikgKw0KICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnVjacOzbiBUd2VldHM6IGNhZGEgc2VpcyBob3JhcyIsDQogICAgICAgeCA9ICJmZWNoYSBkZSBwdWJsaWNhY2nDs24iLA0KICAgICAgIHkgPSAibsO6bWVybyBkZSB0d2VldHMiKSANCg0KYGBgDQoNCjxkaXYgY2xhc3M9dGV4dC1qdXN0aWZ5Pg0KRW4gZXN0YSBzZWd1bmRhIGdyw6FmaWNhIHNlIG11ZXN0cmEgbGEgYWN0aXZpZGFkIGRlIGxvcyB1c3VhcmlvcyBjYWRhIHNlaXMgaG9yYXMuIEVuIGVsbGEgc2Ugb2JzZXJ2YSBxdWUgZW4gbGEgc2VndW5kYSBwYXJ0ZSBkZWwgcmFuZ28gZGUgZMOtYXMgYW5hbGl6YWRvIGxhIGFjdGl2aWRhZCBlcyBtZW5vciBzb2xvIHF1ZSBlbiBsb3Mgw7psdGltb3MgNCBkw61hcyBkZXNwdcOpcyBkZSBsYXMgMTg6MDAgaG9yYXMgbGEgYWN0aXZpZGFkIGNyZWNlIGJhc3RhbnRlLg0KPC9kaXY+DQoNCiZuYnNwOw0KDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6cmdiKDAsIDAsIDIwNSkiPkZyZWN1ZW5jaWEgZGUgcGFsYWJyYXM8L3NwYW4+DQoNClZlYW1vcyBjdcOhbGVzIHNvbiBsYXMgcGFsYWJyYXMgbcOhcyB1dGlsaXphZGFzIHBvciBsb3MgdXN1YXJpb3MgZW4gc3VzIHR3ZWV0cy4NCg0KYGBge3J9DQpjb3VudCh0d2VldHMudGtucywNCiAgICAgIHdvcmQsDQogICAgICBzb3J0ID0gVFJVRSkgJT4lIA0KICBoZWFkKDEwKSAlPiUgDQogIGtibCggDQogICAgICBhbGlnbiA9ICJjIiwNCiAgICAgIGNhcHRpb24gPSAiVG9rZW5pemFjacOzbiIsDQogICAgICApICU+JQ0KICBrYWJsZV9zdHlsaW5nKCJob3ZlciIsIGZ1bGxfd2lkdGggPSBGKQ0KDQpgYGANCg0KYGBge3J9DQp0d2VldHMudGtucyAlPiUgDQogIGNvdW50KHdvcmQpICU+JSANCiAgdG9wX24oMTAsIG4pICU+JQ0KICBhcnJhbmdlKGRlc2MobikpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcih3b3JkLCBuKSwgeSA9IG4pKSArIA0KICBnZW9tX2NvbChmaWxsID0gInN0ZWVsYmx1ZSIsIGNvbG9yID0gImZpcmVicmljayIpICsgDQogIHRoZW1lX2J3KCkgKyANCiAgbGFicyh5ID0gIiIsIHggPSAiIikgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGxhYnModGl0bGUgPSAiVG9wIDEwOiBQYWxhYnJhcyBtw6FzIGZyZWN1ZW50ZXMiLCANCiAgICAgICB5ID0gIkZyZWN1ZW5jaWEiKQ0KDQpgYGANCg0KPGRpdiBjbGFzcz10ZXh0LWp1c3RpZnk+DQpObyBlcyBkZSBleHRyYcOxYXIgcXVlIGxhcyBwYWxhYnJhcyBtw6FzIGZyZWN1ZW50ZXMgdGVuZ2FuIHF1ZSB2ZXIgY29uIGVsIG5vbWJyZSBkZSBsYSBpbnN0aXR1Y2nDs24gYmFuY2FyaWEgYSBsYSBxdWUgcGVydGVuZWNlbiBsb3MgdXN1YXJpb3MgY3V5b3MgdHdlZXRzIGRlc2NhcmdhbW9zLiBUYW1iacOpbiwgcG9yIGxhcyBkZW3DoXMgcGFsYWJyYXMgcGFyZWNlIHNlciBxdWUgbG9zIGNvbWVudGFyaW9zIGRlIGxvcyB1c3VhcmlvcyB0aWVuZW4gcXVlIHZlciBjb24gc2XDsWFsYW1pZW50b3MgcmVmZXJlbnRlcyBhIHRhcmpldGFzLCBjdWVudGFzLCBiYW5jbywgc3VjdXJzYWwsIGV0Yy4NCjwvZGl2Pg0KDQombmJzcDsNCg0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOnJnYigwLCAwLCAyMDUpIj5OdWJlIGRlIHBhbGFicmFzPC9zcGFuPg0KDQpPdHJhIGZvcm1hIGRlIHZlciBsYXMgcGFsYWJyYXMgbcOhcyBmcmVjdWVudGVzIHkgbcOhcyB1dGlsaXphZGEgZW4gdGVtYXMgZGUgYW7DoWxpc2lzIGRlIHRleHRvIGVzIGxhIFdvcmQgQ2xvdWQgbyBudWJlIGRlIHBhbGFicmFzLCBsYSBjdWFsIGVzIHVuYSByZXByZXNlbnRhY2nDs24gZ3LDoWZpY2EgZGUgbGEgZnJlY3VlbmNpYSB5IGN1eWFzIHBhbGFicmFzIG3DoXMgaW1wb3J0YW50ZXMgKHJlcGV0aWRhcykgdGllbmVuIG1heW9yIHRhbWHDsW8uDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQpjb3VudCh0d2VldHMudGtucywgd29yZCkgJT4lDQogIHdpdGgod29yZGNsb3VkKHdvcmRzID0gd29yZCwNCiAgICAgICAgICAgICAgICAgZnJlcSA9IG4sDQogICAgICAgICAgICAgICAgIG1heC53b3JkcyA9IDQwMCwNCiAgICAgICAgICAgICAgICAgc2NhbGUgPSBjKDMsMSksDQogICAgICAgICAgICAgICAgIHJvdC5wZXIgPSAwLjMsDQogICAgICAgICAgICAgICAgIHJhbmRvbS5vcmRlciA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICBjb2xvcnMgPSBicmV3ZXIucGFsKDYsICJEYXJrMiIpKSkNCg0KYGBgDQoNCiZuYnNwOw0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZ2IoMCwgMCwgMjA1KSI+UmVsYWNpw7NuIGVudHJlIHBhbGFicmFzPC9zcGFuPg0KDQpTZSBoYW4gaGVjaG8gZGVzY3JpcGNpb25lcyBkZWwgdGV4dG8gY29uc2lkZXJhbmRvIHBhbGFicmFzIGRlIGZvcm1hIGluZGl2aWR1YWwgcXVlIG5vcyBoYW4gbW9zdHJhZG8gbGFzIG3DoXMgdXRpbGl6YWRhcyBwb3IgbG9zIHVzdWFyaW9zLCBzaW4gZW1iYXJnbywgcGFyYSBoYWNlciB1bmEgZGVzY3JpcGNpw7NuIHVuIHBvY28gbcOhcyBjb21wbGV0YSB5IHJlYWxpc3RhIGVzIGNvbnZlbmllbnRlIGFuYWxpemFyIGxhcyBwYWxhYnJhcyBlbiBmb3JtYSBjb25qdW50YS4gTG8gc2lndWllbnRlIHF1ZSBzZSBoYXLDoSBlcyBjYWxjdWxhciB5IHZpc3VhbGl6YXIgbGFzIHJlbGFjaW9uZXMgZW50cmUgZG9zIHBhbGFicmFzIChiaWdyYW1hcykuDQoNCmBgYHtyfQ0KYmlncmFtYXMgPC0gdHdlZXRzICU+JSANCiAgbXV0YXRlKHRleHRvID0gbGltcGlhcih0ZXh0KSkgJT4lDQogICAgICAgICAgIHNlbGVjdCh0ZXh0KSAlPiUNCiAgICAgICAgICAgdW5uZXN0X3Rva2VucyhpbnB1dCA9IHRleHQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dCA9ICJiaWdyYW1hIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICB0b2tlbiA9ICJuZ3JhbXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIG4gPSAyLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBkcm9wID0gVFJVRSkNCg0KYmlncmFtYXMgICU+JSANCiAgY291bnQoYmlncmFtYSwgc29ydCA9IFRSVUUpICU+JSANCiAgaGVhZCgxMCkgJT4lIA0KICBrYmwoIA0KICAgICAgYWxpZ24gPSAiYyIsDQogICAgICBjYXB0aW9uID0gIlRva2VuaXphY2nDs24iLA0KICAgICAgKSAlPiUNCiAga2FibGVfc3R5bGluZygiaG92ZXIiLCBmdWxsX3dpZHRoID0gRikNCg0KYGBgDQoNClNlIHF1aXRhbiBtYW51YWxtZW50ZSBhbGd1bmFzIHBhbGFicmFzIHF1ZSBubyBhcG9ydGFuIHZhbG9yIGFsIGFuw6FsaXNpczoNCg0KYGBge3J9DQpiaWdyYW1hcyA8LSBmaWx0ZXIoYmlncmFtYXMsIA0KICAgICAgICAgICAgICAgICAgIGJpZ3JhbWEhPSJodHRwcyB0LmNvIikNCg0KYGBgDQoNCkVuIGVsIHJlc3VtZW4gYW50ZXJpb3IsIHNlIGFwcmVjaWEgcXVlIGxvcyBiaWdyYW1hcyBtw6FzIGZyZWN1ZW50ZXMgZXN0w6FuIGZvcm1hZG9zIHBvciBzdG9wd29yZHMsIHBvciBsbyBxdWUsIHNlIGVsaW1pbmFyw6FuIHBvciBubyBhcG9ydGFyIHZhbG9yIGFsIGFuw6FsaXNpcy4NCg0KYGBge3J9DQpiaWdyYW1hc19zZXBhcmFkb3MgPC0gYmlncmFtYXMgJT4lIA0KICBzZXBhcmF0ZShiaWdyYW1hLCBjKCJwYWxhYnJhMSIsICJwYWxhYnJhMiIpLCBzZXAgPSAiICIpDQoNCmhlYWQoYmlncmFtYXNfc2VwYXJhZG9zKQ0KDQojIEZpbHRyYWRvIGRlIGxvcyBiaWdyYW1hcyBxdWUgY29udGllbmVuIGFsZ3VuYSBzdG9wd29yZA0KYmlncmFtYXNfc2VwYXJhZG9zIDwtIGJpZ3JhbWFzX3NlcGFyYWRvcyAgJT4lDQogIGZpbHRlcighcGFsYWJyYTEgJWluJSBjdXN0b21fc3RvcF93b3JkcyR3b3JkKSAlPiUNCiAgZmlsdGVyKCFwYWxhYnJhMiAlaW4lIGN1c3RvbV9zdG9wX3dvcmRzJHdvcmQpDQoNCiMgVW5pw7NuIGRlIGxhcyBwYWxhYnJhcyBwYXJhIGZvcm1hciBkZSBudWV2byBsb3MgYmlncmFtYXMNCmJpZ3JhbWFzIDwtIGJpZ3JhbWFzX3NlcGFyYWRvcyAlPiUNCiAgICAgICAgICAgIHVuaXRlKGJpZ3JhbWEsIHBhbGFicmExLCBwYWxhYnJhMiwgc2VwID0gIiAiKQ0KDQpiaWdyYW1hcyAlPiUgDQogIGNvdW50KGJpZ3JhbWEsIHNvcnQgPSBUUlVFKSAlPiUNCiAgaGVhZCgyMCkgJT4lIA0KICBrYmwoIA0KICAgICAgYWxpZ24gPSAiYyIsDQogICAgICBjYXB0aW9uID0gIlRva2VuaXphY2nDs24iLA0KICAgICAgKSAlPiUNCiAga2FibGVfc3R5bGluZygiaG92ZXIiLCBmdWxsX3dpZHRoID0gRikNCg0KYGBgDQoNClVuYSBmb3JtYSBtw6FzIHZpc3VhbCBlIGluZm9ybWF0aXZhIGRlIGFuYWxpemFyIGxhcyByZWxhY2lvbmVzIGVudHJlIGxhcyBwYWxhYnJhcyBlcyBtZWRpYW50ZSBlbCB1c28gZGUgcmVkZXMuDQoNCmBgYHtyfQ0KZ3JhcGggPC0gYmlncmFtYXMgJT4lDQogICAgICAgICBzZXBhcmF0ZShiaWdyYW1hLCBjKCJwYWxhYnJhMSIsICJwYWxhYnJhMiIpLCBzZXAgPSAiICIpICU+JSANCiAgICAgICAgIGNvdW50KHBhbGFicmExLCBwYWxhYnJhMiwgc29ydCA9IFRSVUUpICU+JQ0KICAgICAgICAgZmlsdGVyKG4gPiAxOCkgJT4lDQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShkaXJlY3RlZCA9IEZBTFNFKQ0KDQpzZXQuc2VlZCgxMjMpDQoNCnBsb3QoZ3JhcGgsIHZlcnRleC5sYWJlbC5mb250ID0gMywNCiAgICAgdmVydGV4LmxhYmVsLmNvbG9yID0gImJsYWNrIiwNCiAgICAgdmVydGV4LmxhYmVsLmNleCA9IDAuOSwgZWRnZS5jb2xvciA9ICJncmF5ODUiKQ0KDQpgYGANCg0KUG9kZW1vcyBhcHJlY2lhciBxdWUgZGVudHJvIGRlbCB0b3AgMTAgZGUgbG9zIGJpZ3JhbWFzIGVzdMOhbjoNCg0KKiAqKnRhcmpldGEgY2l0aWJhbmFtZXgqKg0KKiAqKmJhbmNvIGF6dGVjYSoqDQoqICoqcMOpc2ltbyBzZXJ2aWNpbyoqDQoNClByb2JhYmxlbWVudGUsIGxhcyBxdWVqYXMgdGllbmVuIHF1ZSB2ZXIgY29uIGFsZ3VuYSB0YXJqZXRhIGRlIGNyw6lkaXRvLCBjb24gbGEgYXRlbmNpw7NuIGFsIHDDumJsaWNvIHRhbnRvIGVuIHN1Y3Vyc2FsLCB2w61hIHRlbGVmw7NuaWNhIHkgb25saW5lLg0KDQombmJzcDsNCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6cmdiKDAsIDAsIDIwNSkiPkFuw6FsaXNpcyBkZSBzZW50aW1pZW50b3M8L3NwYW4+DQoNCjxkaXYgY2xhc3M9dGV4dC1qdXN0aWZ5Pg0KRW4gZXN0YSDDumx0aW1hIHBhcnRlIGRlbCB0cmFiYWpvIHNlIGhhcsOhIGVsIGFuw6FsaXNpcyBkZSBzZW50aW1pZW50b3MsIGVzIGRlY2lyLCBzZSBjbGFzaWZpY2Fyw6FuIGxhcyBwYWxhYnJhcyBjb250ZW5pZGFzIGVuIGxvcyB0d2VldHMgZW4gdW5hIGRlIGxhcyBzaWd1aWVudGVzIGRvcyBjYXRlZ29yw61hczogcG9zaXRpdmEgbyBuZWdhdGl2YSwgYWRlbcOhcywgdXRpbGl6YW5kbyBlbCBsZXhpY29uICoqbnJjKiogc2UgcG9kcsOhbiB0YW1iacOpbiBjbGFzaWZpY2FyIGxhcyBwYWxhYnJhcyBlbiBsb3Mgc2lndWllbnRlcyBzZW50aW1pZW50b3M6IGNvbmZpYW56YSwgaXJhLCBkaXNndXN0bywgYXNvbWJybywgYWxlZ3LDrWEsIG1pZWRvLCB0cmlzdGV6YSB5IHByZW1vbmljacOzbi4NCg0KRWwgYXJjaGl2byBkZSBzZW50aW1pZW50b3Mgc2UgcHVlZGUgZGVzY2FyZ2FyIGRlbCByZXBvc2l0b3JpbyBbZ2l0aHViXShodHRwczovL2dpdGh1Yi5jb20vN1BhcnRpZGFzRGlnaXRhbC9BbmFUZXh0L3RyZWUvbWFzdGVyL2RhdG9zL2RpY2Npb25hcmlvcykgZGVsIHByb3llY3RvICoqN1BhcnRpZGFzRGlnaXRhbCoqIGVsYWJvcmFkbyBwb3IgbGEgVW5pdmVyc2lkYWQgZGUgVmFsbGFkb2xpZC4NCjwvZGl2Pg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFfQ0Kc291cmNlKCJnZXRfc2VudGltZW50cy5SIikNCnNlbnRpbWllbnRvcyA8LSByZWFkUkRTKGZpbGUgPSAic2VudGltaWVudG9zLnJkcyIpDQoNCnR3ZWV0cy50a25zIDwtIHR3ZWV0cy50a25zICU+JSANCiAgcmVuYW1lKHBhbGFicmEgPSB3b3JkKQ0KDQpgYGANCg0KVmVhbW9zIGxhIGNhbnRpZGFkIGRlIHBhbGFicmFzIGNsYXNpZmljYWRhcyBjb21vIHBvc2l0aXZhcyB5IG5lZ2F0aXZhcyBkZSB0b2RvcyBsb3MgdHdlZXRzDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQp0d2VldHMudGtucyAlPiUNCiAgcmlnaHRfam9pbihnZXRfc2VudGltZW50cygibnJjIikpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKHNlbnRpbWllbnRvKSAmIHNlbnRpbWllbnRvID09ICJwb3NpdGl2byIgfCBzZW50aW1pZW50byA9PSAibmVnYXRpdm8iKSAlPiUNCiAgY291bnQoc2VudGltaWVudG8sIHNvcnQgPSBUUlVFKSAlPiUgDQogIGthYmxlKCANCiAgICAgIGZvcm1hdCA9ICJodG1sIiwNCiAgICAgIGRpZ2l0cyA9IDAsDQogICAgICBjYXB0aW9uID0gIlNlbnRpbWllbnRvcyBQb3NpdGl2b3MgLSBOZWdhdGl2b3MiLA0KICAgICAgZm9ybWF0LmFyZ3MgPSBsaXN0KGJpZy5tYXJrID0gIiwiKSwNCiAgICAgIHRhYmxlLmF0dHIgPSAic3R5bGU9J3dpZHRoOjUwJTsnIikgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpDQoNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFfQ0KdHdlZXRzLnRrbnMgJT4lDQogIHJpZ2h0X2pvaW4oZ2V0X3NlbnRpbWVudHMoIm5yYyIpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShzZW50aW1pZW50bykmIHNlbnRpbWllbnRvID09ICJwb3NpdGl2byIgfCBzZW50aW1pZW50byA9PSAibmVnYXRpdm8iKSAlPiUgDQogIGNvdW50KHBhbGFicmEsIHNlbnRpbWllbnRvLCBzb3J0ID0gVFJVRSkgJT4lDQogIGFjYXN0KHBhbGFicmEgfiBzZW50aW1pZW50bywgdmFsdWUudmFyID0gIm4iLCBmaWxsID0gMCkgJT4lDQogIGNvbXBhcmlzb24uY2xvdWQoY29sb3JzID0gYygicmVkIiwiYmx1ZSIpLCANCiAgICAgICAgICAgICAgICAgICBtYXgud29yZHMgPSAzMDAsDQogICAgICAgICAgICAgICAgICAgdGl0bGUuc2l6ZSA9IDIpDQoNCmBgYA0KDQpFbiBlbCBjdWFkcm8gYW50ZXJpb3IsIHNlIGFwcmVjaWEgcXVlIHNlIGRldGVjdMOzIGNhc2kgbGEgbWlzbWEgY2FudGlkYWQgZGUgcGFsYWJyYXMgbmVnYXRpdmFzIHF1ZSBwb3NpdGl2YXMuIEFob3JhLCBzZSBtdWVzdHJhIGVsIGRldGFsbGUgKHRvcCAxNSkgZGUgbGFzIHBhbGFicmFzIGVuIGNhZGEgY2F0ZWdvcsOtYS4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCnR3ZWV0cy50a25zICU+JQ0KICByaWdodF9qb2luKGdldF9zZW50aW1lbnRzKCJucmMiKSkgJT4lDQogIGZpbHRlcighaXMubmEoc2VudGltaWVudG8pJiBzZW50aW1pZW50byA9PSAicG9zaXRpdm8iIHwgc2VudGltaWVudG8gPT0gIm5lZ2F0aXZvIikgJT4lIA0KICBjb3VudChwYWxhYnJhLCBzZW50aW1pZW50bywgc2hvcnQgPSBUUlVFKSAlPiUgDQogIGdyb3VwX2J5KHNlbnRpbWllbnRvKSAlPiUgDQogIHRvcF9uKDE1KSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShwYWxhYnJhID0gcmVvcmRlcihwYWxhYnJhLCBuKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHBhbGFicmEsIG4sIGZpbGwgPSBzZW50aW1pZW50bykpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJkYXJrcmVkIiwgInN0ZWVsYmx1ZSIpKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBuKSwgaGp1c3QgPSAxLjIsIGNvbG9yID0gIndoaXRlIikgKw0KICBmYWNldF93cmFwKH5zZW50aW1pZW50bywgc2NhbGVzID0gImZyZWVfeSIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgeGxhYihOVUxMKSArDQogIGxhYnModGl0bGUgPSAiRGV0YWxsZSBkZSBTZW50aW1pZW50b3MiKQ0KDQpgYGANCg0KRW4gbGFzIGdyw6FmaWNhcyBhbnRlcmlvcmVzLCBzZSBhcHJlY2lhbiBsYXMgcGFsYWJyYXMgcXVlIGxvcyB1c3VhcmlvcyBjYXB0dXJhcm9uIGVuIHN1cyB0d2VldHMgeSBxdWUgZWwgYWxnb3JpdG1vIGxvcyBjbGFzaWZpY8OzIGNvbW8gbmVnYXRpdm9zIG8gcG9zaXRpdm9zLiBMYXMgcGFsYWJyYXMgbmVnYXRpdmFzIHBhcmVjZSBxdWUgdGllbmVuIG3DoXMgcXVlIHZlciBjb24gcmVjbGFtb3MgeSBxdWVqYXMgZGUgbG9zIHVzdWFyaW9zIGNvbW8gcm9ibywgbWFsLCBwZW9yLCBjYW5jZWxhciwgcHJvYmxlbWEsIGVudHJlIG90cmFzLg0KDQpBaG9yYSwgdmVhbW9zIGVsIGNvbnRlbyBkZSBsb3MgZGVtw6FzIHNlbnRpbWllbnRvcyBwYXJhIGRhcm5vcyB1bmEgaWRlYSBkZWwgw6FuaW1vIGRlIGxvcyB1c3VhcmlvcyBjdWFuZG8gZXNjcmliaWVyb24gc3VzIHR3ZWV0cy4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCnR3ZWV0cy50a25zICU+JQ0KICByaWdodF9qb2luKGdldF9zZW50aW1lbnRzKCJucmMiKSkgJT4lDQogIGZpbHRlcighaXMubmEoc2VudGltaWVudG8pKSAlPiUNCiAgY291bnQoc2VudGltaWVudG8sIHNvcnQgPSBUUlVFKSAlPiUgDQogIGthYmxlKCANCiAgICAgIGZvcm1hdCA9ICJodG1sIiwNCiAgICAgIGRpZ2l0cyA9IDAsDQogICAgICBjYXB0aW9uID0gIlJlc3VtZW4gZGUgU2VudGltaWVudG9zIiwNCiAgICAgIGZvcm1hdC5hcmdzID0gbGlzdChiaWcubWFyayA9ICIsIiksDQogICAgICB0YWJsZS5hdHRyID0gInN0eWxlPSd3aWR0aDo1MCU7JyIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQ0KDQpgYGANCg0KR3LDoWZpY2FtZW50ZToNCg0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCnR3ZWV0cy50a25zICU+JQ0KICByaWdodF9qb2luKGdldF9zZW50aW1lbnRzKCJucmMiKSkgJT4lDQogIGZpbHRlcighaXMubmEoc2VudGltaWVudG8pKSAlPiUNCiAgZ3JvdXBfYnkoc2VudGltaWVudG8pICU+JQ0KICBjb3VudChzZW50aW1pZW50bywgc29ydCA9IFRSVUUpICU+JSANCiAgbXV0YXRlKHNlbnRpbWllbnRvID0gcmVvcmRlcihmYWN0b3Ioc2VudGltaWVudG8pLCBuKSkgJT4lDQogIGdncGxvdChhZXMocmVvcmRlcihzZW50aW1pZW50byxuKSwgbiwgZmlsbCA9IHNlbnRpbWllbnRvKSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBmb3JtYXQobiwgYmlnLm1hcmsgPSAiLCIpKSwgaGp1c3QgPSAxLjIsIGNvbG9yID0gImJsYWNrIikgKw0KICBjb29yZF9mbGlwKCkgKw0KICB4bGFiKE5VTEwpICsNCiAgbGFicyh0aXRsZSA9ICJEZXRhbGxlIGRlIEVtb2Npb25lcyIpDQoNCmBgYA0KDQpCYWphbmRvIG3DoXMgZWwgYW7DoWxpc2lzIHBhcmEgY29ub2NlciBsYXMgcGFsYWJyYXMgY29udGVuaWRhcyBlbiBjYWRhIGVtb2Npw7NuOg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFfQ0KdHdlZXRzLnRrbnMgJT4lDQogIHJpZ2h0X2pvaW4oZ2V0X3NlbnRpbWVudHMoIm5yYyIpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShzZW50aW1pZW50bykpICU+JSANCiAgY291bnQocGFsYWJyYSwgc2VudGltaWVudG8sIHNob3J0ID0gVFJVRSkgJT4lIA0KICBncm91cF9ieShzZW50aW1pZW50bykgJT4lIA0KICB0b3BfbigxNSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGUocGFsYWJyYSA9IHJlb3JkZXIocGFsYWJyYSwgbikpICU+JSANCiAgZ2dwbG90KGFlcyhwYWxhYnJhLCBuLCBmaWxsID0gc2VudGltaWVudG8pKSArDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG4pLCBoanVzdCA9IDEuMiwgY29sb3IgPSAid2hpdGUiKSArDQogIGZhY2V0X3dyYXAofnNlbnRpbWllbnRvLCBzY2FsZXMgPSAiZnJlZV95IikgKw0KICBjb29yZF9mbGlwKCkgKw0KICB4bGFiKE5VTEwpICsNCiAgbGFicyh0aXRsZSA9ICJEZXRhbGxlIGRlIEVtb2Npb25lcyIpDQpgYGANCg0KSGF5IGFsZ3VuYXMgcGFsYWJyYXMgcXVlIHNlIGNsYXNpZmljYW4gZW4gbcOhcyBkZSB1bmEgZW1vY2nDs24gY29tbywgcG9yIGVqZW1wbG8sIGRpbmVybyBxdWUgc2UgZW5jdWVudHJhIGVuIGxhcyBjYXRlZ29yw61hcyBkZSBhbGVncsOtYSwgYXNvbWJybyBlIGlyYS4gTG8gYW50ZXJpb3IsIHBvcnF1ZSBkZXBlbmRlIG11Y2hvIGRlbCBjb250ZXh0byBkZSBsYSBwYWxhYnJhLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFfQ0KdHdlZXRzLnRrbnMgJT4lDQogIHJpZ2h0X2pvaW4oZ2V0X3NlbnRpbWVudHMoIm5yYyIpKSAlPiUNCiAgY291bnQocGFsYWJyYSwgc2VudGltaWVudG8sIHNvcnQgPSBUUlVFKSAlPiUgDQogIGZpbHRlcighaXMubmEoc2VudGltaWVudG8pLCBzZW50aW1pZW50byAhPSAicG9zaXRpdm8iLCBzZW50aW1pZW50byAhPSAibmVnYXRpdm8iKSAlPiUNCiAgYWNhc3QocGFsYWJyYSB+IHNlbnRpbWllbnRvLCB2YWx1ZS52YXIgPSAibiIsIGZpbGwgPSAwKSAlPiUNCiAgY29tcGFyaXNvbi5jbG91ZCh0aXRsZS5zaXplID0gMS41KQ0KDQpgYGANCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6cmdiKDAsIDAsIDIwNSkiPkNvbmNsdXNpb25lczwvc3Bhbj4NCg0KPGRpdiBjbGFzcz10ZXh0LWp1c3RpZnk+DQpEZXNwdcOpcyBkZSBoYWJlciByZWFsaXphZG8gZXN0ZSBwZXF1ZcOxbyBhbsOhbGlzaXMgcG9kZW1vcyBjb25jbHVpciBxdWUgbG9zIHVzdWFyaW9zIGRlIGxhIGluc3RpdHVjacOzbiBiYW5jYXJpYSBlbiBjdWVzdGnDs24gc2Ugc3VlbGVuIHF1ZWphciBwb3IgdGVtYXMgcmVsYWNpb25hZG9zIGNvbiBlcnJvcmVzIGVuIGFsZ3VuYXMgYXBsaWNhY2lvbmVzLCBzZSBzaW50ZW4gcm9iYWRvcywgcMOpc2ltbyBvIG1hbCBzZXJ2aWNpbywgY2FuY2VsYWNpb25lcyB5IGNvbiBhbGd1bm9zIG90cm9zIHByb2JsZW1hcyBxdWUgaGFuIHRlbmlkbyBjb21vIGNsaWVudGVzLg0KDQpFbCByZXN1bHRhZG8gZGUgaGFjZXIgdW4gYW7DoWxpc2lzIGRlIGVtb2Npb25lcyBub3MgaW5kaWNhIHF1ZSBsYSBtYXlvcsOtYSBkZSBsb3MgdXN1YXJpb3MgcXVlIGNvbWVudGFyb24gZW4gc3UgY3VlbnRhIGRlIHR3aXR0ZXIgdGVuw61hbiBlbW9jaW9uZXMgbmVnYXRpdmFzIGNvbW8gbWllZG8sIHRyaXN0ZXphLCBpcmEgeSBkaXNndXN0by4gTG8gYW50ZXJpb3IsIHBvcnF1ZSBmdWVyb24gbGFzIGVtb2Npb25lcyBjb24gbWF5b3IgZnJlY3VlbmNpYSwgc2luIGVtYmFyZ28sIGFsIGNsYXNpZmljYXIgdG9kYXMgbGFzIHBhbGFicmFzIGVuIHBvc2l0aXZhcyB5IG5lZ2F0aXZhcyBsYSBwcm9wb3JjacOzbiBlcyBjYXNpIGxhIG1pc21hLg0KDQpIYXkgcXVlIGFjbGFyYXIgcXVlIGRlcGVuZGUgZGVsIGxleGljb24gbyBkaWNjaW9uYXJpbyB1dGlsaXphZG8gbG9zIHJlc3VsdGFkb3Mgc2Vyw6FuIGRpc3RpbnRvcy4NCg0KQ29uIGVzdGUgY29ydG8gdHJhYmFqbyBwb2RlbW9zIGRhcm5vcyB1bmEgaWRlYSBkZSBsbyBxdWUgZWwgZXF1aXBvIGRlIGF0ZW5jacOzbiBhIGNsaWVudGVzIGRlbCBiYW5jbyBwb2Ryw61hIGhhY2VyIHBhcmEgZGlzbWludWlyIGxvcyByZWNsYW1vcyBvIHF1ZWphcyBjb21vLCBwb3IgZWplbXBsbywgcmVhbGl6YXIgZW5jdWVzdGFzIGEgbG9zIGNsaWVudGVzIHF1ZSB2YW4gZGlyZWN0YW1lbnRlIGEgc3VjdXJzYWwgeSBlbnZpYXIgZW5jdWVzdGFzIHbDrWEgY29ycmVvIGVsZWN0csOzbmljbyBkZXNwdcOpcyBkZSB1c2FyIGFsZ8O6biBjYWplcm8gYXV0b23DoXRpY28gbyBkaXJlY3RhbWVudGUgZW4gc3UgcMOhZ2luYSB3ZWIuIEFjdHVhbG1lbnRlLCBoYXkgZW1wcmVzYXMgcXVlIHNlIGRlZGljYW4gYSBwcm9wb3JjaW9uYXIgZXNlIHNlcnZpY2lvIHkgeWEgc29sbyBsZSBlbnRyZWdhbiBsb3MgcmVzdWx0YWRvcyBhbCBjbGllbnRlLCBlbCBjdWFsIGNvbiBzdSBlcXVpcG8gbyDDoXJlYSBkZSBhbmFsw610aWNhIHNlIGVuY2FyZ2EgZGUgcHJvY2VzYXIgZGljaGEgaW5mb3JtYWNpw7NuIHBhcmEgbGEgdG9tYSBkZSBkZWNpc2lvbmVzLg0KDQo8L2Rpdj4NCg0KDQoNCg0KDQo=