1. Análisis de palabras y documentos
Una de las principales preguntas que nos hacemos en tareas de mineo de texto y procesamiento del lenguaje natural es ¿cómo cuantificar de qué se trata un documento?. Para llevar a cabo esta tarea, cuando ya tenemos los datos pre-procesados, podemos calcular medidas sobre cada uno de los tokens, es decir, podemos empezar a extraer características. Tres medidas muy usadas son:
Frecuencias (term frequency): Esta mide qué tan frecuentemente una palabra aparece en un documento.
Frecuencia inversa en documentos (inverse document frequency): A veces existen palabras que aparecen muchas veces pero que no son importantes. Para separar este tipo de palabras podemos utilizar el enfoque de la frecuencia inversa en documentos. Esta medida decrece conforme la palabra aparezca en más documentos. \[idf(palabra) = ln\left(\frac{n_{\text{documentos}}}{n_{\text{documentos con el término}}}\right)\]
Tf-Idf (Term frequency - inverse document frequency): Esta medida combina las dos anteriores. Es decir, mide la importancia de una palabra por su frecuencia general, pero la penaliza según más frecuente se haga entre varios documentos.
1.1. Análisis por palabras
Como primera aproximación, veremos cuáles son las palabras más frecuentes en cada uno de los datasets de noticias que hemos venido trabajando. Para ello, primero cargaremos los datos:
library(tidyverse)
library(tidytext)
tidy_Velasco_annotated = readRDS("Caso1_Noticias/tidy_Velasco_annotated.RDS")
tidy_Velasco = tidy_Velasco_annotated %>%
filter(!is.na(lemma))
tidy_Larrea_annotated = readRDS("Caso1_Noticias/tidy_Larrea_annotated.RDS")
tidy_Larrea = tidy_Larrea_annotated %>%
filter(!is.na(lemma))
tidy_Freile_annotated = readRDS("Caso1_Noticias/tidy_Freile_annotated.RDS")
tidy_Freile = tidy_Freile_annotated %>%
filter(!is.na(lemma))
ejemplo_Velasco = unique(tidy_Velasco$doc_id)[1]
ejemplo_Larrea = unique(tidy_Larrea$doc_id)[1]
ejemplo_Freile = unique(tidy_Freile$doc_id)[2]
Con los datos cargados, exploraremos la frecuencia de palabras como se muestra a continuación:
token1_Velasco = tidy_Velasco %>%
count(doc_id, upos, lemma, sort = TRUE) %>%
group_by(doc_id) %>%
mutate(total = sum(n))
token1_Velasco %>%
filter(doc_id==ejemplo_Velasco) %>%
select(doc_id, lemma, upos, n, total) %>%
head()
token1_Larrea = tidy_Larrea %>%
count(doc_id, upos, lemma, sort = TRUE) %>%
group_by(doc_id) %>%
mutate(total = sum(n))
token1_Larrea %>%
filter(doc_id==ejemplo_Larrea) %>%
select(doc_id, lemma, upos, n, total) %>%
head()
token1_Freile = tidy_Freile %>%
count(doc_id, upos, lemma, sort = TRUE) %>%
group_by(doc_id) %>%
mutate(total = sum(n))
token1_Freile %>%
filter(doc_id==ejemplo_Freile) %>%
select(doc_id, lemma, upos, n, total) %>%
head()
Estos datos los podemos graficar para obtener mejores primeras conclusiones:
token1_Velasco %>%
filter(doc_id %in% unique(token1_Velasco$doc_id)[1:4]) %>%
filter(upos %in% c('NOUN','PROPN','VERB','ADJ')) %>%
group_by(doc_id, upos) %>%
slice_max(order_by = n/total, n=3) %>%
ggplot(aes(x=reorder_within(lemma, n/total, list(doc_id,upos)), y=n/total, fill = upos)) +
geom_col() +
scale_x_reordered()+
coord_flip()+
facet_wrap(vars(doc_id), scales = "free") +
labs(x="Frecuencia relativa de cada palabra en cada documento", y="Frecuencia",
title = "Frecuencias relativas: Cuatro noticias de Juan Fernando Velasco")+
theme(legend.position = "bottom")

Aquí podemos observar que la frecuencia relativa de las palabras va en general, máximo hasta un poco más arriba del 2% con respecto al total de palabras.
1.2. Tf-idf
La medida Tf-idf lo que hace es buscar las palabras importantes en el contenido de un documento midiendo su frecuencia general y penalizándola según más frecuente se haga entre varios documentos. Es decir, buscamos las palabras frecuentes pero no comunes.
En R esto se logra como:
library(tidytext)
token1_Velasco = token1_Velasco %>%
filter(upos %in% c('NOUN','PROPN','VERB','ADJ')) %>%
bind_tf_idf(lemma, doc_id, n)
A value for tf_idf is negative:
Input should have exactly one row per document-term combination.
token1_Velasco %>%
select(-total) %>%
arrange(desc(tf_idf))
La medida de tf_idf nos muestra qué palabras son más importantes dentro de cada documento, con respecto a su colección (conjunto de noticias).
Esto lo podemos observar gráficamente:
token1_Velasco %>%
filter(doc_id %in% unique(token1_Velasco$doc_id)[1:4]) %>%
arrange(desc(tf_idf)) %>%
mutate(lemma = factor(lemma, levels = rev(unique(lemma)))) %>%
group_by(doc_id) %>%
top_n(15) %>%
ungroup() %>%
ggplot(aes(reorder_within(lemma, tf_idf, doc_id), tf_idf, fill = doc_id)) +
geom_col(show.legend = FALSE) +
scale_x_reordered()+
labs(x = NULL, y = "tf-idf",
title = "tf-idf: Cuatro noticias de Juan Fernando Velasco") +
facet_wrap(~doc_id, ncol = 2, scales = "free") +
coord_flip()
Selecting by tf_idf

2. Análisis exploratorio de n-gramas
Usualmente, para ver qué tan seguido una palabra x es seguida de una palabra y podemos construir un modelo relacional entre ellas, y ese será nuestro objetivo al final de este análisis.
2.1. Construcción de bigramas
Para construir los bigramas (o n-gramas de dimensión dos) recordemos el código que utilizamos en la última clase, sobre los datasets de noticias originales. Trabajaremos sobre dichos datasets en pos de ver las relaciones de las palabras incluso con artículos y preposiciones.
noticiasVelascoDF = readRDS("Caso1_Noticias/noticiasVelascoDF.RDS")
noticiasLarreaDF = readRDS("Caso1_Noticias/noticiasLarreaDF.RDS")
noticiasFreileDF = readRDS("Caso1_Noticias/noticiasFreileDF.RDS")
bigramas_Velasco = noticiasVelascoDF %>%
unnest_tokens(bigrama, Noticia, token = "ngrams", n = 2)
bigramas_Velasco %>%
filter(Titular==ejemplo_Velasco) %>%
select(Titular, bigrama)
bigramas_Larrea = noticiasLarreaDF %>%
unnest_tokens(bigrama, Noticia, token = "ngrams", n = 2)
bigramas_Larrea %>%
filter(Titular==ejemplo_Larrea) %>%
select(Titular, bigrama)
bigramas_Freile = noticiasFreileDF %>%
unnest_tokens(bigrama, Noticia, token = "ngrams", n = 2)
bigramas_Freile %>%
filter(Titular==ejemplo_Freile) %>%
select(Titular, bigrama)
2.2. Conteo y tf-idf de bigramas
Sobre los bigramas construidos en el literal anterior realicemos el conteo de tokens, quitando primero las palabras vacías, como aprendimos en la clase anterior.
library(readxl)
stopwords_es_1 = read_excel("Diccionarios/Stopwords/CustomStopWords.xlsx")
names(stopwords_es_1) = c("Token","Fuente")
stopwords_es_2 = tibble(Token=tm::stopwords(kind = "es"), Fuente="tm")
stopwords_es = rbind(stopwords_es_1, stopwords_es_2)
stopwords_es = stopwords_es[!duplicated(stopwords_es$Token),]
remove(stopwords_es_1, stopwords_es_2)
bigramas_Velasco = bigramas_Velasco %>%
separate(bigrama, c("palabra1", "palabra2"), sep = " ") %>%
filter(!palabra1 %in% c(stopwords_es$Token)) %>%
filter(!palabra2 %in% c(stopwords_es$Token))
bigramas_frec_Velasco = bigramas_Velasco %>%
count(Titular, palabra1, palabra2, sort = TRUE) %>%
unite(bigrama, palabra1, palabra2, sep = " ")
bigramas_frec_Velasco %>% select(bigrama, n) %>% head()
bigramas_Larrea = bigramas_Larrea %>%
separate(bigrama, c("palabra1", "palabra2"), sep = " ") %>%
filter(!palabra1 %in% c(stopwords_es$Token)) %>%
filter(!palabra2 %in% c(stopwords_es$Token))
bigramas_frec_Larrea = bigramas_Larrea %>%
count(Titular, palabra1, palabra2, sort = TRUE) %>%
unite(bigrama, palabra1, palabra2, sep = " ")
bigramas_frec_Larrea %>% select(bigrama, n) %>% head()
bigramas_Freile = bigramas_Freile %>%
separate(bigrama, c("palabra1", "palabra2"), sep = " ") %>%
filter(!palabra1 %in% c(stopwords_es$Token)) %>%
filter(!palabra2 %in% c(stopwords_es$Token))
bigramas_frec_Freile = bigramas_Freile %>%
count(Titular, palabra1, palabra2, sort = TRUE) %>%
unite(bigrama, palabra1, palabra2, sep = " ")
bigramas_frec_Freile %>% select(bigrama, n) %>% head()
Ahora, realicemos el tf-idf en estos datasets:
bigramas_tfidf_Velasco = bigramas_frec_Velasco %>%
bind_tf_idf(bigrama, Titular, n)
bigramas_tfidf_Velasco %>% arrange(desc(tf_idf))
bigramas_tfidf_Larrea = bigramas_frec_Larrea %>%
bind_tf_idf(bigrama, Titular, n)
bigramas_tfidf_Larrea %>% arrange(desc(tf_idf))
bigramas_tfidf_Freile = bigramas_frec_Freile %>%
bind_tf_idf(bigrama, Titular, n)
bigramas_tfidf_Freile %>% arrange(desc(tf_idf))
La utilidad adicional de utilizar bigramas es que podemos darle contexto al análisis de sentimientos.
2.3. Red de bigramas
Cuando tenemos bigramas o n-gramas de dimensión mayor a 2, es interesante visualizar las relaciones de manera simultánea a través de un grafo. Un grafo es sencillamente la combinación de nodos interconectados. Tales grafos se pueden construir a partir de un objeto tidy ya que solo requiere de 3 variables:
El paquete igraph se vale de la librería ggraph para realizar grafos a partir de datos en formato tidy. Esto lo haremos como se muestra a continuación.
library(igraph)
bigrama_grafo_Velasco = bigramas_Velasco %>%
count(palabra1, palabra2, sort = TRUE) %>%
filter(n >= 6) %>%
graph_from_data_frame()
bigrama_grafo_Velasco
IGRAPH 49e5247 DN-- 108 68 --
+ attr: name (v/c), n (e/n)
+ edges from 49e5247 (vertex names):
[1] juan ->fernando fernando ->velasco correo ->electrónico electrónico->requerido
[5] requerido ->asunto maría ->paula paula ->romo sector ->cultural
[9] lenín ->moreno rafael ->correa ana ->maría andrés ->arauz
[13] covid ->19 redes ->sociales biblioteca ->nacional cultura ->juan
[17] gestores ->culturales lucio ->gutiérrez consejo ->nacional electoral ->cne
[21] nacional ->electoral presidente ->lenín artes ->vivas gustavo ->larrea
[25] movimiento ->construye césar ->montúfar fuerza ->ecuador guillermo ->celi
[29] guillermo ->lasso asamblea ->política carlos ->sagnay cultura ->ecuatoriana
+ ... omitted several edges
bigrama_grafo_Larrea = bigramas_Larrea %>%
count(palabra1, palabra2, sort = TRUE) %>%
filter(n >= 6) %>%
graph_from_data_frame()
bigrama_grafo_Larrea
IGRAPH 4a68ae3 DN-- 210 150 --
+ attr: name (v/c), n (e/n)
+ edges from 4a68ae3 (vertex names):
[1] gustavo ->larrea rafael ->correa consejo ->nacional
[4] nacional ->electoral maría ->paula paula ->romo
[7] electoral ->cne candidato ->presidencial guillermo ->lasso
[10] lucio ->gutiérrez organizaciones->políticas andrés ->arauz
[13] isidro ->romero correo ->electrónico electrónico ->requerido
[16] requerido ->asunto ecuatoriano ->unido gerson ->almeida
[19] asamblea ->política alianza ->país sociedad ->patriótica
[22] alexandra ->peralta 08 ->2020 centro ->democrático
+ ... omitted several edges
bigrama_grafo_Freile = bigramas_Freile %>%
count(palabra1, palabra2, sort = TRUE) %>%
filter(n >= 6) %>%
graph_from_data_frame()
bigrama_grafo_Freile
IGRAPH 4af695d DN-- 190 131 --
+ attr: name (v/c), n (e/n)
+ edges from 4af695d (vertex names):
[1] consejo ->nacional nacional ->electoral electoral ->cne
[4] rafael ->correa candidato ->presidencial organizaciones->políticas
[7] josé ->freile maría ->paula paula ->romo
[10] pedro ->josé lucio ->gutiérrez yaku ->pérez
[13] 08 ->2020 gustavo ->larrea isidro ->romero
[16] guillermo ->lasso ximena ->peña andrés ->arauz
[19] correo ->electrónico electrónico ->requerido fernando ->velasco
[22] gerson ->almeida juan ->fernando requerido ->asunto
+ ... omitted several edges
library(ggraph)
set.seed(123)
ggraph(bigrama_grafo_Velasco, layout = "fr") +
geom_edge_link() +
geom_node_point() +
geom_node_label(aes(label = name), vjust = 1, hjust = 1)

ggraph(bigrama_grafo_Larrea, layout = "fr") +
geom_edge_link() +
geom_node_point() +
geom_node_label(aes(label = name), vjust = 1, hjust = 1)

ggraph(bigrama_grafo_Freile, layout = "fr") +
geom_edge_link() +
geom_node_point() +
geom_node_label(aes(label = name), vjust = 1, hjust = 1)

3. Nubes de palabras
Para realizar nubes de palabras, que coloquen el tamaño de las palabras acorde a su frecuencia, utilizaremos la librería wordcloud2.
library(echarts4r)
# library(stringi)
wc_Velasco = tidy_Velasco_annotated %>%
filter(upos %in% c("NOUN","PROPN", "ADJ", "VERB")) %>%
count(lemma, sort=T) %>%
filter(n > 20) %>%
e_color_range(n, color) %>%
e_charts() %>%
e_cloud(lemma, n, color) %>%
e_tooltip()
wc_Velasco
LS0tDQp0aXRsZTogIkFuw6FsaXNpcyBkZSBjb21wb3J0YW1pZW50byBlbiByZWRlcyBzb2NpYWxlcyB1c2FuZG8gUHJvY2VzYW1pZW50byBkZWwgTGVuZ3VhamUgTmF0dXJhbCINCnN1YnRpdGxlOiAnQ2Fww610dWxvIDQ6IFTDqWNuaWNhcyBkZSBhbsOhbGlzaXMgZXhwbG9yYXRvcmlvIGRlIGRhdG9zIGRlIHRleHRvJw0KYXV0aG9yOiBIdWdvIFBvcnJhcw0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjc3M6IEVzdGlsb3MuY3NzDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiAyDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiB0cnVlDQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQ0KYmlibGlvZ3JhcGh5OiBCaWJsaW9ncmFmaWEuYmliDQpjc2w6IGNlcGFsLnhtbA0Kbm9jaXRlOiB8IA0KICBAU2lsZ2UyMDE3LCBAVGh1c2hhcmEyMDE5DQotLS0NCg0KIyAqKjEuIEFuw6FsaXNpcyBkZSBwYWxhYnJhcyB5IGRvY3VtZW50b3MqKg0KDQo8YnI+PC9icj4NCjxjZW50ZXI+PGE+PGltZyB3aWR0aD0iNTAlIiBzcmM9ImZpZ3MvMDNfRURBLnBuZyI+PC9hPjwvY2VudGVyPg0KPGJyPjwvYnI+DQoNClVuYSBkZSBsYXMgcHJpbmNpcGFsZXMgcHJlZ3VudGFzIHF1ZSBub3MgaGFjZW1vcyBlbiB0YXJlYXMgZGUgbWluZW8gZGUgdGV4dG8geSBwcm9jZXNhbWllbnRvIGRlbCBsZW5ndWFqZSBuYXR1cmFsIGVzICoqwr9jw7NtbyBjdWFudGlmaWNhciBkZSBxdcOpIHNlIHRyYXRhIHVuIGRvY3VtZW50bz8qKi4gUGFyYSBsbGV2YXIgYSBjYWJvIGVzdGEgdGFyZWEsIGN1YW5kbyB5YSB0ZW5lbW9zIGxvcyBkYXRvcyBwcmUtcHJvY2VzYWRvcywgcG9kZW1vcyBjYWxjdWxhciBtZWRpZGFzIHNvYnJlIGNhZGEgdW5vIGRlIGxvcyB0b2tlbnMsIGVzIGRlY2lyLCBwb2RlbW9zIGVtcGV6YXIgYSBleHRyYWVyIGNhcmFjdGVyw61zdGljYXMuIFRyZXMgbWVkaWRhcyBtdXkgdXNhZGFzIHNvbjoNCg0KKyAqKkZyZWN1ZW5jaWFzICh0ZXJtIGZyZXF1ZW5jeSkqKjogRXN0YSBtaWRlIHF1w6kgdGFuIGZyZWN1ZW50ZW1lbnRlIHVuYSBwYWxhYnJhIGFwYXJlY2UgZW4gdW4gZG9jdW1lbnRvLg0KKyAqKkZyZWN1ZW5jaWEgaW52ZXJzYSBlbiBkb2N1bWVudG9zIChpbnZlcnNlIGRvY3VtZW50IGZyZXF1ZW5jeSkqKjogQSB2ZWNlcyBleGlzdGVuIHBhbGFicmFzIHF1ZSBhcGFyZWNlbiBtdWNoYXMgdmVjZXMgcGVybyBxdWUgbm8gc29uIGltcG9ydGFudGVzLiBQYXJhIHNlcGFyYXIgZXN0ZSB0aXBvIGRlIHBhbGFicmFzIHBvZGVtb3MgdXRpbGl6YXIgZWwgZW5mb3F1ZSBkZSBsYSBmcmVjdWVuY2lhIGludmVyc2EgZW4gZG9jdW1lbnRvcy4gRXN0YSBtZWRpZGEgZGVjcmVjZSBjb25mb3JtZSBsYSBwYWxhYnJhIGFwYXJlemNhIGVuIG3DoXMgZG9jdW1lbnRvcy4NClxbaWRmKHBhbGFicmEpID0gbG5cbGVmdChcZnJhY3tuX3tcdGV4dHtkb2N1bWVudG9zfX19e25fe1x0ZXh0e2RvY3VtZW50b3MgY29uIGVsIHTDqXJtaW5vfX19XHJpZ2h0KVxdDQoNCisgKipUZi1JZGYgKFRlcm0gZnJlcXVlbmN5IC0gaW52ZXJzZSBkb2N1bWVudCBmcmVxdWVuY3kpKio6IEVzdGEgbWVkaWRhIGNvbWJpbmEgbGFzIGRvcyBhbnRlcmlvcmVzLiBFcyBkZWNpciwgbWlkZSBsYSBpbXBvcnRhbmNpYSBkZSB1bmEgcGFsYWJyYSBwb3Igc3UgZnJlY3VlbmNpYSBnZW5lcmFsLCBwZXJvIGxhIHBlbmFsaXphIHNlZ8O6biBtw6FzIGZyZWN1ZW50ZSBzZSBoYWdhIGVudHJlIHZhcmlvcyBkb2N1bWVudG9zLg0KDQojIyAqKjEuMS4gQW7DoWxpc2lzIHBvciBwYWxhYnJhcyoqDQoNCkNvbW8gcHJpbWVyYSBhcHJveGltYWNpw7NuLCB2ZXJlbW9zIGN1w6FsZXMgc29uIGxhcyBwYWxhYnJhcyBtw6FzIGZyZWN1ZW50ZXMgZW4gY2FkYSB1bm8gZGUgbG9zIGRhdGFzZXRzIGRlIG5vdGljaWFzIHF1ZSBoZW1vcyB2ZW5pZG8gdHJhYmFqYW5kby4gUGFyYSBlbGxvLCBwcmltZXJvIGNhcmdhcmVtb3MgbG9zIGRhdG9zOg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeSh0aWR5dGV4dCkNCg0KdGlkeV9WZWxhc2NvX2Fubm90YXRlZCA9IHJlYWRSRFMoIkNhc28xX05vdGljaWFzL3RpZHlfVmVsYXNjb19hbm5vdGF0ZWQuUkRTIikNCnRpZHlfVmVsYXNjbyA9IHRpZHlfVmVsYXNjb19hbm5vdGF0ZWQgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGxlbW1hKSkgDQoNCnRpZHlfTGFycmVhX2Fubm90YXRlZCA9IHJlYWRSRFMoIkNhc28xX05vdGljaWFzL3RpZHlfTGFycmVhX2Fubm90YXRlZC5SRFMiKQ0KdGlkeV9MYXJyZWEgPSB0aWR5X0xhcnJlYV9hbm5vdGF0ZWQgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGxlbW1hKSkNCg0KdGlkeV9GcmVpbGVfYW5ub3RhdGVkID0gcmVhZFJEUygiQ2FzbzFfTm90aWNpYXMvdGlkeV9GcmVpbGVfYW5ub3RhdGVkLlJEUyIpDQp0aWR5X0ZyZWlsZSA9IHRpZHlfRnJlaWxlX2Fubm90YXRlZCAlPiUgDQogIGZpbHRlcighaXMubmEobGVtbWEpKQ0KDQplamVtcGxvX1ZlbGFzY28gPSB1bmlxdWUodGlkeV9WZWxhc2NvJGRvY19pZClbMV0NCmVqZW1wbG9fTGFycmVhID0gdW5pcXVlKHRpZHlfTGFycmVhJGRvY19pZClbMV0NCmVqZW1wbG9fRnJlaWxlID0gdW5pcXVlKHRpZHlfRnJlaWxlJGRvY19pZClbMl0NCmBgYA0KDQpDb24gbG9zIGRhdG9zIGNhcmdhZG9zLCBleHBsb3JhcmVtb3MgbGEgZnJlY3VlbmNpYSBkZSBwYWxhYnJhcyBjb21vIHNlIG11ZXN0cmEgYSBjb250aW51YWNpw7NuOg0KDQpgYGB7cn0NCnRva2VuMV9WZWxhc2NvID0gdGlkeV9WZWxhc2NvICU+JSANCiAgY291bnQoZG9jX2lkLCB1cG9zLCBsZW1tYSwgc29ydCA9IFRSVUUpICU+JSANCiAgZ3JvdXBfYnkoZG9jX2lkKSAlPiUNCiAgbXV0YXRlKHRvdGFsID0gc3VtKG4pKQ0KDQp0b2tlbjFfVmVsYXNjbyAlPiUgDQogIGZpbHRlcihkb2NfaWQ9PWVqZW1wbG9fVmVsYXNjbykgJT4lIA0KICBzZWxlY3QoZG9jX2lkLCBsZW1tYSwgdXBvcywgbiwgdG90YWwpICU+JSANCiAgaGVhZCgpDQpgYGANCg0KYGBge3J9DQp0b2tlbjFfTGFycmVhID0gdGlkeV9MYXJyZWEgJT4lIA0KICBjb3VudChkb2NfaWQsIHVwb3MsIGxlbW1hLCBzb3J0ID0gVFJVRSkgJT4lIA0KICBncm91cF9ieShkb2NfaWQpICU+JQ0KICBtdXRhdGUodG90YWwgPSBzdW0obikpDQoNCnRva2VuMV9MYXJyZWEgJT4lIA0KICBmaWx0ZXIoZG9jX2lkPT1lamVtcGxvX0xhcnJlYSkgJT4lIA0KICBzZWxlY3QoZG9jX2lkLCBsZW1tYSwgdXBvcywgbiwgdG90YWwpICU+JSANCiAgaGVhZCgpDQpgYGANCg0KYGBge3J9DQp0b2tlbjFfRnJlaWxlID0gdGlkeV9GcmVpbGUgJT4lIA0KICBjb3VudChkb2NfaWQsIHVwb3MsIGxlbW1hLCBzb3J0ID0gVFJVRSkgJT4lIA0KICBncm91cF9ieShkb2NfaWQpICU+JQ0KICBtdXRhdGUodG90YWwgPSBzdW0obikpDQoNCnRva2VuMV9GcmVpbGUgJT4lIA0KICBmaWx0ZXIoZG9jX2lkPT1lamVtcGxvX0ZyZWlsZSkgJT4lIA0KICBzZWxlY3QoZG9jX2lkLCBsZW1tYSwgdXBvcywgbiwgdG90YWwpICU+JSANCiAgaGVhZCgpDQpgYGANCg0KDQpFc3RvcyBkYXRvcyBsb3MgcG9kZW1vcyBncmFmaWNhciBwYXJhIG9idGVuZXIgbWVqb3JlcyBwcmltZXJhcyBjb25jbHVzaW9uZXM6DQoNCmBgYHtyIGZpZy5oZWlnaHQgPSA4fQ0KdG9rZW4xX1ZlbGFzY28gJT4lIA0KICBmaWx0ZXIoZG9jX2lkICVpbiUgdW5pcXVlKHRva2VuMV9WZWxhc2NvJGRvY19pZClbMTo0XSkgJT4lIA0KICBmaWx0ZXIodXBvcyAlaW4lIGMoJ05PVU4nLCdQUk9QTicsJ1ZFUkInLCdBREonKSkgJT4lIA0KICBncm91cF9ieShkb2NfaWQsIHVwb3MpICU+JSANCiAgc2xpY2VfbWF4KG9yZGVyX2J5ID0gbi90b3RhbCwgbj0zKSAlPiUgDQogIGdncGxvdChhZXMoeD1yZW9yZGVyX3dpdGhpbihsZW1tYSwgbi90b3RhbCwgbGlzdChkb2NfaWQsdXBvcykpLCB5PW4vdG90YWwsIGZpbGwgPSB1cG9zKSkgKw0KICBnZW9tX2NvbCgpICsNCiAgc2NhbGVfeF9yZW9yZGVyZWQoKSsNCiAgY29vcmRfZmxpcCgpKw0KICBmYWNldF93cmFwKHZhcnMoZG9jX2lkKSwgc2NhbGVzID0gImZyZWUiKSArIA0KICBsYWJzKHg9IkZyZWN1ZW5jaWEgcmVsYXRpdmEgZGUgY2FkYSBwYWxhYnJhIGVuIGNhZGEgZG9jdW1lbnRvIiwgeT0iRnJlY3VlbmNpYSIsDQogICAgICAgdGl0bGUgPSAiRnJlY3VlbmNpYXMgcmVsYXRpdmFzOiBDdWF0cm8gbm90aWNpYXMgZGUgSnVhbiBGZXJuYW5kbyBWZWxhc2NvIikrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KYGBgDQoNCkFxdcOtIHBvZGVtb3Mgb2JzZXJ2YXIgcXVlIGxhIGZyZWN1ZW5jaWEgcmVsYXRpdmEgZGUgbGFzIHBhbGFicmFzIHZhIGVuIGdlbmVyYWwsIG3DoXhpbW8gaGFzdGEgdW4gcG9jbyBtw6FzIGFycmliYSBkZWwgMiUgY29uIHJlc3BlY3RvIGFsIHRvdGFsIGRlIHBhbGFicmFzLg0KDQo8IS0tICMjICoqMS4yLiBMZXkgZGUgWmlwZioqIC0tPg0KDQo8IS0tIEVuIGRhdG9zIGRlIHRleHRvLCBsYXMgZGlzdHJpYnVjaW9uZXMgY29uIGNvbGFzIGxhcmdhcyBzb24gdXN1YWxlcy4gRW4gdW5hIG1hdHJpeiB0w6lybWlubyBkb2N1bWVudG8gdmVyZW1vcyBlc3RvIHJlZmxlamFkbyBlbiBxdWUgbGEgbWF0cml6IGVzIGRpc3BlcnNhICh0aWVuZSBtdWNoYXMgZW50cmFkYXMgaWd1YWwgYSBjZXJvKS4gR2VvcmdlIFppcGYsIGxpbmfDvGlzdGEgZXN0YWRvdW5pZGVuc2UgZXN0YWJsZWNlIHJlc3BlY3RvIGEgZXN0YXMgZGlzdHJpYnVjaW9uZXMgcXVlICoqJydsYSBmcmVjdWVuY2lhIHJlbGF0aXZhIGRlIGFwYXJlY2ltaWVudG8gZGUgdW5hIHBhbGFicmEgZXN0w6EgaW52ZXJzYW1lbnRlIHJlbGFjaW9uYWRhIGNvbiBzdSByYW5nbycnKiouIEVuIGVmZWN0bywgYWwgb3JkZW5hciBsYXMgcGFsYWJyYXMgcG9yIGZyZWN1ZW5jaWEgZGUgbWF5b3IgYSBtZW5vciwgY29sb2NhcmVtb3Mgc3UgcmFuZ28gY29tbyB1biBuw7ptZXJvIGVudGVybywgZW1wZXphbmRvIGVuIHVuby4gLS0+DQoNCjwhLS0gRW4gUiwgcG9kZW1vcyB2ZXIgbGEgbGV5IGRlIFppcGYgY29tbzogLS0+DQoNCjwhLS0gYGBge3J9IC0tPg0KPCEtLSByYW5nb19mcmVjX1l1bmRhID0gdG9rZW4xX1l1bmRhICU+JSAgLS0+DQo8IS0tICAgZ3JvdXBfYnkoZG9jX2lkKSAlPiUgIC0tPg0KPCEtLSAgIG11dGF0ZShyYW5nbyA9IHJvd19udW1iZXIoKSwgIC0tPg0KPCEtLSAgICAgICAgICBgdGVybSBmcmVxdWVuY3lgID0gbi90b3RhbCkgLS0+DQoNCjwhLS0gcmFuZ29fZnJlY19ZdW5kYSAlPiUgIC0tPg0KPCEtLSAgIGZpbHRlcihkb2NfaWQ9PWVqZW1wbG9fWXVuZGEpICU+JSAgLS0+DQo8IS0tICAgaGVhZCgpIC0tPg0KPCEtLSBgYGAgLS0+DQoNCjwhLS0gYGBge3J9IC0tPg0KPCEtLSByYW5nb19mcmVjX1ZpdGVyaSA9IHRva2VuMV9WaXRlcmkgJT4lICAtLT4NCjwhLS0gICBncm91cF9ieShkb2NfaWQpICU+JSAgLS0+DQo8IS0tICAgbXV0YXRlKHJhbmdvID0gcm93X251bWJlcigpLCAgLS0+DQo8IS0tICAgICAgICAgIGB0ZXJtIGZyZXF1ZW5jeWAgPSBuL3RvdGFsKSAtLT4NCg0KPCEtLSByYW5nb19mcmVjX1ZpdGVyaSAlPiUgIC0tPg0KPCEtLSAgIGZpbHRlcihkb2NfaWQ9PWVqZW1wbG9fVml0ZXJpKSAlPiUgIC0tPg0KPCEtLSAgIGhlYWQoKSAtLT4NCjwhLS0gYGBgIC0tPg0KDQo8IS0tIEdyw6FmaWNhbWVudGU6IC0tPg0KDQo8IS0tIGBgYHtyfSAtLT4NCjwhLS0gcmFuZ29fZnJlY19ZdW5kYSAlPiUgIC0tPg0KPCEtLSAgIGdncGxvdChhZXMocmFuZ28sIGB0ZXJtIGZyZXF1ZW5jeWAsIGNvbG9yID0gZG9jX2lkKSkgKyAgLS0+DQo8IS0tICAgZ2VvbV9saW5lKHNpemUgPSAxLjEsIGFscGhhID0gMC44LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArICAtLT4NCjwhLS0gICBzY2FsZV94X2xvZzEwKCkgKyAtLT4NCjwhLS0gICBzY2FsZV95X2xvZzEwKCkgKyAtLT4NCjwhLS0gICBsYWJzKHRpdGxlID0gIkxleSBkZSBaaXBmOiBKb3JnZSBZdW5kYSwgUXVpdG8iKSAtLT4NCjwhLS0gYGBgIC0tPg0KDQo8IS0tIGBgYHtyfSAtLT4NCjwhLS0gcmFuZ29fZnJlY19WaXRlcmkgJT4lICAtLT4NCjwhLS0gICBnZ3Bsb3QoYWVzKHJhbmdvLCBgdGVybSBmcmVxdWVuY3lgLCBjb2xvciA9IGRvY19pZCkpICsgIC0tPg0KPCEtLSAgIGdlb21fbGluZShzaXplID0gMS4xLCBhbHBoYSA9IDAuOCwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAgLS0+DQo8IS0tICAgc2NhbGVfeF9sb2cxMCgpICsgLS0+DQo8IS0tICAgc2NhbGVfeV9sb2cxMCgpICsgLS0+DQo8IS0tICAgbGFicyh0aXRsZSA9ICJMZXkgZGUgWmlwZjogQ3ludGhpYSBWaXRlcmksIFF1aXRvIikgLS0+DQo8IS0tIGBgYCAtLT4NCg0KPCEtLSBDb21vIHNlIHB1ZWRlIG9ic2VydmFyLCBlbiBlZmVjdG8gbGEgcmVsYWNpw7NuIGVuIGVsIHJhbmdvIHkgbGEgZnJlY3VlbmNpYSByZWxhdGl2YSBkZSBsYXMgcGFsYWJyYXMgcGFyYSBsb3Mgbm90aWNpYXMgYW5hbGl6YWRhcyBlcyBpbnZlcnNhLiDCv1F1w6kgdGFsIHNpIGVzdGltYW1vcyBlc3RhIHJlbGFjacOzbiBhIHRyYXbDqXMgZGUgdW5hIHJlZ3Jlc2nDs24gbGluZWFsPyAtLT4NCg0KPCEtLSBgYGB7cn0gLS0+DQo8IS0tIHN1bW1hcnkobG0obG9nMTAoYHRlcm0gZnJlcXVlbmN5YCkgfiBsb2cxMChyYW5nbyksIGRhdGEgPSByYW5nb19mcmVjX1l1bmRhKSkgLS0+DQo8IS0tIGBgYCAtLT4NCg0KPCEtLSBgYGB7cn0gLS0+DQo8IS0tIHN1bW1hcnkobG0obG9nMTAoYHRlcm0gZnJlcXVlbmN5YCkgfiBsb2cxMChyYW5nbyksIGRhdGEgPSByYW5nb19mcmVjX1ZpdGVyaSkpIC0tPg0KPCEtLSBgYGAgLS0+DQoNCjwhLS0gTGEgcmVncmVzacOzbiBjb25maXJtYSBsYSBkaXJlY2Npw7NuIGRlIGxhIGxleSBkZSBaaXBmLCBxdWUgZ3LDoWZpY2FtZW50ZSBzZSB2ZSBjb21vOiAtLT4NCg0KPCEtLSBgYGB7cn0gLS0+DQo8IS0tIHJhbmdvX2ZyZWNfWXVuZGEgJT4lICAtLT4NCjwhLS0gICBnZ3Bsb3QoYWVzKHJhbmdvLCBgdGVybSBmcmVxdWVuY3lgLCBjb2xvciA9IGRvY19pZCkpICsgIC0tPg0KPCEtLSAgIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IC0xLjQsIHNsb3BlID0gLTAuNSwgY29sb3IgPSAiZ3JheTUwIiwgbGluZXR5cGUgPSAyKSArIC0tPg0KPCEtLSAgIGdlb21fbGluZShzaXplID0gMS4xLCBhbHBoYSA9IDAuOCwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAgLS0+DQo8IS0tICAgc2NhbGVfeF9sb2cxMCgpICsgLS0+DQo8IS0tICAgc2NhbGVfeV9sb2cxMCgpICsgLS0+DQo8IS0tICAgbGFicyhUaXRsZT0iTGV5IGRlIFppcGYgYWp1c3RhZGEgcG9yIHJlZ3Jlc2nDs246IEpvcmdlIFl1bmRhLCBRdWl0byIpIC0tPg0KPCEtLSBgYGAgLS0+DQoNCjwhLS0gYGBge3J9IC0tPg0KPCEtLSByYW5nb19mcmVjX1ZpdGVyaSAlPiUgIC0tPg0KPCEtLSAgIGdncGxvdChhZXMocmFuZ28sIGB0ZXJtIGZyZXF1ZW5jeWAsIGNvbG9yID0gZG9jX2lkKSkgKyAgLS0+DQo8IS0tICAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gLTEuNSwgc2xvcGUgPSAtMC40LCBjb2xvciA9ICJncmF5NTAiLCBsaW5ldHlwZSA9IDIpICsgLS0+DQo8IS0tICAgZ2VvbV9saW5lKHNpemUgPSAxLjEsIGFscGhhID0gMC44LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArICAtLT4NCjwhLS0gICBzY2FsZV94X2xvZzEwKCkgKyAtLT4NCjwhLS0gICBzY2FsZV95X2xvZzEwKCkgKyAtLT4NCjwhLS0gICBsYWJzKFRpdGxlPSJMZXkgZGUgWmlwZiBhanVzdGFkYSBwb3IgcmVncmVzacOzbjogQ3ludGhpYSBWaXRlcmksIEd1YXlhcXVpbCIpIC0tPg0KPCEtLSBgYGAgLS0+DQoNCiMjICoqMS4yLiBUZi1pZGYqKg0KDQpMYSBtZWRpZGEgVGYtaWRmIGxvIHF1ZSBoYWNlIGVzIGJ1c2NhciBsYXMgcGFsYWJyYXMgaW1wb3J0YW50ZXMgZW4gZWwgY29udGVuaWRvIGRlIHVuIGRvY3VtZW50byBtaWRpZW5kbyBzdSBmcmVjdWVuY2lhIGdlbmVyYWwgeSBwZW5hbGl6w6FuZG9sYSBzZWfDum4gbcOhcyBmcmVjdWVudGUgc2UgaGFnYSBlbnRyZSB2YXJpb3MgZG9jdW1lbnRvcy4gRXMgZGVjaXIsIGJ1c2NhbW9zIGxhcyBwYWxhYnJhcyBmcmVjdWVudGVzIHBlcm8gbm8gY29tdW5lcy4gDQoNCkVuIFIgZXN0byBzZSBsb2dyYSBjb21vOg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXRleHQpDQp0b2tlbjFfVmVsYXNjbyA9IHRva2VuMV9WZWxhc2NvICU+JQ0KICBmaWx0ZXIodXBvcyAlaW4lIGMoJ05PVU4nLCdQUk9QTicsJ1ZFUkInLCdBREonKSkgJT4lIA0KICBiaW5kX3RmX2lkZihsZW1tYSwgZG9jX2lkLCBuKQ0KDQp0b2tlbjFfVmVsYXNjbyAlPiUNCiAgc2VsZWN0KC10b3RhbCkgJT4lDQogIGFycmFuZ2UoZGVzYyh0Zl9pZGYpKQ0KYGBgDQoNCkxhIG1lZGlkYSBkZSB0Zl9pZGYgbm9zIG11ZXN0cmEgcXXDqSBwYWxhYnJhcyBzb24gbcOhcyBpbXBvcnRhbnRlcyBkZW50cm8gZGUgY2FkYSBkb2N1bWVudG8sIGNvbiByZXNwZWN0byBhIHN1IGNvbGVjY2nDs24gKGNvbmp1bnRvIGRlIG5vdGljaWFzKS4NCg0KRXN0byBsbyBwb2RlbW9zIG9ic2VydmFyIGdyw6FmaWNhbWVudGU6DQoNCmBgYHtyIGZpZy5oZWlnaHQgPSA3fQ0KdG9rZW4xX1ZlbGFzY28gJT4lDQogIGZpbHRlcihkb2NfaWQgJWluJSB1bmlxdWUodG9rZW4xX1ZlbGFzY28kZG9jX2lkKVsxOjRdKSAlPiUgDQogIGFycmFuZ2UoZGVzYyh0Zl9pZGYpKSAlPiUNCiAgbXV0YXRlKGxlbW1hID0gZmFjdG9yKGxlbW1hLCBsZXZlbHMgPSByZXYodW5pcXVlKGxlbW1hKSkpKSAlPiUgDQogIGdyb3VwX2J5KGRvY19pZCkgJT4lIA0KICB0b3BfbigxNSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lDQogIGdncGxvdChhZXMocmVvcmRlcl93aXRoaW4obGVtbWEsIHRmX2lkZiwgZG9jX2lkKSwgdGZfaWRmLCBmaWxsID0gZG9jX2lkKSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIHNjYWxlX3hfcmVvcmRlcmVkKCkrDQogIGxhYnMoeCA9IE5VTEwsIHkgPSAidGYtaWRmIiwNCiAgICAgICB0aXRsZSA9ICJ0Zi1pZGY6IEN1YXRybyBub3RpY2lhcyBkZSBKdWFuIEZlcm5hbmRvIFZlbGFzY28iKSArDQogIGZhY2V0X3dyYXAofmRvY19pZCwgbmNvbCA9IDIsIHNjYWxlcyA9ICJmcmVlIikgKw0KICBjb29yZF9mbGlwKCkNCmBgYA0KDQojICoqMi4gQW7DoWxpc2lzIGV4cGxvcmF0b3JpbyBkZSBuLWdyYW1hcyoqDQoNClVzdWFsbWVudGUsIHBhcmEgdmVyIHF1w6kgdGFuIHNlZ3VpZG8gdW5hIHBhbGFicmEgKngqIGVzIHNlZ3VpZGEgZGUgdW5hIHBhbGFicmEgKnkqIHBvZGVtb3MgY29uc3RydWlyIHVuIG1vZGVsbyByZWxhY2lvbmFsIGVudHJlIGVsbGFzLCB5IGVzZSBzZXLDoSBudWVzdHJvIG9iamV0aXZvIGFsIGZpbmFsIGRlIGVzdGUgYW7DoWxpc2lzLg0KDQojIyAqKjIuMS4gQ29uc3RydWNjacOzbiBkZSBiaWdyYW1hcyoqDQoNClBhcmEgY29uc3RydWlyIGxvcyBiaWdyYW1hcyAobyBuLWdyYW1hcyBkZSBkaW1lbnNpw7NuIGRvcykgcmVjb3JkZW1vcyBlbCBjw7NkaWdvIHF1ZSB1dGlsaXphbW9zIGVuIGxhIMO6bHRpbWEgY2xhc2UsIHNvYnJlIGxvcyBkYXRhc2V0cyBkZSBub3RpY2lhcyBvcmlnaW5hbGVzLiBUcmFiYWphcmVtb3Mgc29icmUgZGljaG9zIGRhdGFzZXRzIGVuIHBvcyBkZSB2ZXIgbGFzIHJlbGFjaW9uZXMgZGUgbGFzIHBhbGFicmFzIGluY2x1c28gY29uIGFydMOtY3Vsb3MgeSBwcmVwb3NpY2lvbmVzLg0KDQpgYGB7cn0NCm5vdGljaWFzVmVsYXNjb0RGID0gcmVhZFJEUygiQ2FzbzFfTm90aWNpYXMvbm90aWNpYXNWZWxhc2NvREYuUkRTIikNCm5vdGljaWFzTGFycmVhREYgPSByZWFkUkRTKCJDYXNvMV9Ob3RpY2lhcy9ub3RpY2lhc0xhcnJlYURGLlJEUyIpDQpub3RpY2lhc0ZyZWlsZURGID0gcmVhZFJEUygiQ2FzbzFfTm90aWNpYXMvbm90aWNpYXNGcmVpbGVERi5SRFMiKQ0KYGBgDQoNCmBgYHtyfQ0KYmlncmFtYXNfVmVsYXNjbyA9IG5vdGljaWFzVmVsYXNjb0RGICU+JQ0KICB1bm5lc3RfdG9rZW5zKGJpZ3JhbWEsIE5vdGljaWEsIHRva2VuID0gIm5ncmFtcyIsIG4gPSAyKQ0KDQpiaWdyYW1hc19WZWxhc2NvICU+JSANCiAgZmlsdGVyKFRpdHVsYXI9PWVqZW1wbG9fVmVsYXNjbykgJT4lIA0KICBzZWxlY3QoVGl0dWxhciwgYmlncmFtYSkNCmBgYA0KDQpgYGB7cn0NCmJpZ3JhbWFzX0xhcnJlYSA9IG5vdGljaWFzTGFycmVhREYgJT4lDQogIHVubmVzdF90b2tlbnMoYmlncmFtYSwgTm90aWNpYSwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpDQoNCmJpZ3JhbWFzX0xhcnJlYSAlPiUgDQogIGZpbHRlcihUaXR1bGFyPT1lamVtcGxvX0xhcnJlYSkgJT4lIA0KICBzZWxlY3QoVGl0dWxhciwgYmlncmFtYSkNCmBgYA0KDQpgYGB7cn0NCmJpZ3JhbWFzX0ZyZWlsZSA9IG5vdGljaWFzRnJlaWxlREYgJT4lDQogIHVubmVzdF90b2tlbnMoYmlncmFtYSwgTm90aWNpYSwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpDQoNCmJpZ3JhbWFzX0ZyZWlsZSAlPiUgDQogIGZpbHRlcihUaXR1bGFyPT1lamVtcGxvX0ZyZWlsZSkgJT4lIA0KICBzZWxlY3QoVGl0dWxhciwgYmlncmFtYSkNCmBgYA0KDQojIyAqKjIuMi4gQ29udGVvIHkgdGYtaWRmIGRlIGJpZ3JhbWFzKioNCg0KU29icmUgbG9zIGJpZ3JhbWFzIGNvbnN0cnVpZG9zIGVuIGVsIGxpdGVyYWwgYW50ZXJpb3IgcmVhbGljZW1vcyBlbCBjb250ZW8gZGUgdG9rZW5zLCBxdWl0YW5kbyBwcmltZXJvIGxhcyBwYWxhYnJhcyB2YWPDrWFzLCBjb21vIGFwcmVuZGltb3MgZW4gbGEgY2xhc2UgYW50ZXJpb3IuDQoNCmBgYHtyfQ0KbGlicmFyeShyZWFkeGwpDQpzdG9wd29yZHNfZXNfMSA9IHJlYWRfZXhjZWwoIkRpY2Npb25hcmlvcy9TdG9wd29yZHMvQ3VzdG9tU3RvcFdvcmRzLnhsc3giKQ0KbmFtZXMoc3RvcHdvcmRzX2VzXzEpID0gYygiVG9rZW4iLCJGdWVudGUiKQ0Kc3RvcHdvcmRzX2VzXzIgPSB0aWJibGUoVG9rZW49dG06OnN0b3B3b3JkcyhraW5kID0gImVzIiksIEZ1ZW50ZT0idG0iKQ0Kc3RvcHdvcmRzX2VzID0gcmJpbmQoc3RvcHdvcmRzX2VzXzEsIHN0b3B3b3Jkc19lc18yKQ0Kc3RvcHdvcmRzX2VzID0gc3RvcHdvcmRzX2VzWyFkdXBsaWNhdGVkKHN0b3B3b3Jkc19lcyRUb2tlbiksXQ0KcmVtb3ZlKHN0b3B3b3Jkc19lc18xLCBzdG9wd29yZHNfZXNfMikNCmBgYA0KDQoNCmBgYHtyfQ0KYmlncmFtYXNfVmVsYXNjbyA9IGJpZ3JhbWFzX1ZlbGFzY28gJT4lDQogIHNlcGFyYXRlKGJpZ3JhbWEsIGMoInBhbGFicmExIiwgInBhbGFicmEyIiksIHNlcCA9ICIgIikgJT4lDQogIGZpbHRlcighcGFsYWJyYTEgJWluJSBjKHN0b3B3b3Jkc19lcyRUb2tlbikpICU+JQ0KICBmaWx0ZXIoIXBhbGFicmEyICVpbiUgYyhzdG9wd29yZHNfZXMkVG9rZW4pKQ0KDQpiaWdyYW1hc19mcmVjX1ZlbGFzY28gPSBiaWdyYW1hc19WZWxhc2NvICU+JSANCiAgY291bnQoVGl0dWxhciwgcGFsYWJyYTEsIHBhbGFicmEyLCBzb3J0ID0gVFJVRSkgJT4lIA0KICB1bml0ZShiaWdyYW1hLCBwYWxhYnJhMSwgcGFsYWJyYTIsIHNlcCA9ICIgIikNCg0KYmlncmFtYXNfZnJlY19WZWxhc2NvICU+JSBzZWxlY3QoYmlncmFtYSwgbikgJT4lIGhlYWQoKQ0KYGBgDQoNCmBgYHtyfQ0KYmlncmFtYXNfTGFycmVhID0gYmlncmFtYXNfTGFycmVhICU+JQ0KICBzZXBhcmF0ZShiaWdyYW1hLCBjKCJwYWxhYnJhMSIsICJwYWxhYnJhMiIpLCBzZXAgPSAiICIpICU+JQ0KICBmaWx0ZXIoIXBhbGFicmExICVpbiUgYyhzdG9wd29yZHNfZXMkVG9rZW4pKSAlPiUNCiAgZmlsdGVyKCFwYWxhYnJhMiAlaW4lIGMoc3RvcHdvcmRzX2VzJFRva2VuKSkNCg0KYmlncmFtYXNfZnJlY19MYXJyZWEgPSBiaWdyYW1hc19MYXJyZWEgJT4lIA0KICBjb3VudChUaXR1bGFyLCBwYWxhYnJhMSwgcGFsYWJyYTIsIHNvcnQgPSBUUlVFKSAlPiUgDQogIHVuaXRlKGJpZ3JhbWEsIHBhbGFicmExLCBwYWxhYnJhMiwgc2VwID0gIiAiKQ0KDQpiaWdyYW1hc19mcmVjX0xhcnJlYSAlPiUgc2VsZWN0KGJpZ3JhbWEsIG4pICU+JSBoZWFkKCkNCmBgYA0KDQpgYGB7cn0NCmJpZ3JhbWFzX0ZyZWlsZSA9IGJpZ3JhbWFzX0ZyZWlsZSAlPiUNCiAgc2VwYXJhdGUoYmlncmFtYSwgYygicGFsYWJyYTEiLCAicGFsYWJyYTIiKSwgc2VwID0gIiAiKSAlPiUNCiAgZmlsdGVyKCFwYWxhYnJhMSAlaW4lIGMoc3RvcHdvcmRzX2VzJFRva2VuKSkgJT4lDQogIGZpbHRlcighcGFsYWJyYTIgJWluJSBjKHN0b3B3b3Jkc19lcyRUb2tlbikpDQoNCmJpZ3JhbWFzX2ZyZWNfRnJlaWxlID0gYmlncmFtYXNfRnJlaWxlICU+JSANCiAgY291bnQoVGl0dWxhciwgcGFsYWJyYTEsIHBhbGFicmEyLCBzb3J0ID0gVFJVRSkgJT4lIA0KICB1bml0ZShiaWdyYW1hLCBwYWxhYnJhMSwgcGFsYWJyYTIsIHNlcCA9ICIgIikNCg0KYmlncmFtYXNfZnJlY19GcmVpbGUgJT4lIHNlbGVjdChiaWdyYW1hLCBuKSAlPiUgaGVhZCgpDQpgYGANCg0KDQpBaG9yYSwgcmVhbGljZW1vcyBlbCB0Zi1pZGYgZW4gZXN0b3MgZGF0YXNldHM6DQoNCmBgYHtyfQ0KYmlncmFtYXNfdGZpZGZfVmVsYXNjbyA9IGJpZ3JhbWFzX2ZyZWNfVmVsYXNjbyAlPiUNCiAgYmluZF90Zl9pZGYoYmlncmFtYSwgVGl0dWxhciwgbikNCg0KYmlncmFtYXNfdGZpZGZfVmVsYXNjbyAlPiUgYXJyYW5nZShkZXNjKHRmX2lkZikpDQpgYGANCg0KYGBge3J9DQpiaWdyYW1hc190ZmlkZl9MYXJyZWEgPSBiaWdyYW1hc19mcmVjX0xhcnJlYSAlPiUNCiAgYmluZF90Zl9pZGYoYmlncmFtYSwgVGl0dWxhciwgbikNCg0KYmlncmFtYXNfdGZpZGZfTGFycmVhICU+JSBhcnJhbmdlKGRlc2ModGZfaWRmKSkNCmBgYA0KDQpgYGB7cn0NCmJpZ3JhbWFzX3RmaWRmX0ZyZWlsZSA9IGJpZ3JhbWFzX2ZyZWNfRnJlaWxlICU+JQ0KICBiaW5kX3RmX2lkZihiaWdyYW1hLCBUaXR1bGFyLCBuKQ0KDQpiaWdyYW1hc190ZmlkZl9GcmVpbGUgJT4lIGFycmFuZ2UoZGVzYyh0Zl9pZGYpKQ0KYGBgDQoNCg0KTGEgdXRpbGlkYWQgYWRpY2lvbmFsIGRlIHV0aWxpemFyIGJpZ3JhbWFzIGVzIHF1ZSBwb2RlbW9zIGRhcmxlIGNvbnRleHRvIGFsIGFuw6FsaXNpcyBkZSBzZW50aW1pZW50b3MuDQoNCiMjICoqMi4zLiBSZWQgZGUgYmlncmFtYXMqKg0KDQpDdWFuZG8gdGVuZW1vcyBiaWdyYW1hcyBvIG4tZ3JhbWFzIGRlIGRpbWVuc2nDs24gbWF5b3IgYSAyLCBlcyBpbnRlcmVzYW50ZSB2aXN1YWxpemFyIGxhcyByZWxhY2lvbmVzIGRlIG1hbmVyYSBzaW11bHTDoW5lYSBhIHRyYXbDqXMgZGUgdW4gZ3JhZm8uIFVuIGdyYWZvIGVzIHNlbmNpbGxhbWVudGUgbGEgY29tYmluYWNpw7NuIGRlIG5vZG9zIGludGVyY29uZWN0YWRvcy4gVGFsZXMgZ3JhZm9zIHNlIHB1ZWRlbiBjb25zdHJ1aXIgYSBwYXJ0aXIgZGUgdW4gb2JqZXRvIHRpZHkgeWEgcXVlIHNvbG8gcmVxdWllcmUgZGUgMyB2YXJpYWJsZXM6DQoNCisgT3JpZ2VuDQorIERlc3Rpbm8NCisgUGVzbw0KDQpFbCBwYXF1ZXRlICppZ3JhcGgqIHNlIHZhbGUgZGUgbGEgbGlicmVyw61hICpnZ3JhcGgqIHBhcmEgcmVhbGl6YXIgZ3JhZm9zIGEgcGFydGlyIGRlIGRhdG9zIGVuIGZvcm1hdG8gdGlkeS4gRXN0byBsbyBoYXJlbW9zIGNvbW8gc2UgbXVlc3RyYSBhIGNvbnRpbnVhY2nDs24uDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZ9DQpsaWJyYXJ5KGlncmFwaCkNCg0KYmlncmFtYV9ncmFmb19WZWxhc2NvID0gYmlncmFtYXNfVmVsYXNjbyAlPiUNCiAgY291bnQocGFsYWJyYTEsIHBhbGFicmEyLCBzb3J0ID0gVFJVRSkgJT4lIA0KICBmaWx0ZXIobiA+PSA2KSAlPiUNCiAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkNCg0KYmlncmFtYV9ncmFmb19WZWxhc2NvDQpgYGANCmBgYHtyfQ0KYmlncmFtYV9ncmFmb19MYXJyZWEgPSBiaWdyYW1hc19MYXJyZWEgJT4lDQogIGNvdW50KHBhbGFicmExLCBwYWxhYnJhMiwgc29ydCA9IFRSVUUpICU+JSANCiAgZmlsdGVyKG4gPj0gNikgJT4lDQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpDQoNCmJpZ3JhbWFfZ3JhZm9fTGFycmVhDQpgYGANCmBgYHtyfQ0KYmlncmFtYV9ncmFmb19GcmVpbGUgPSBiaWdyYW1hc19GcmVpbGUgJT4lDQogIGNvdW50KHBhbGFicmExLCBwYWxhYnJhMiwgc29ydCA9IFRSVUUpICU+JSANCiAgZmlsdGVyKG4gPj0gNikgJT4lDQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpDQoNCmJpZ3JhbWFfZ3JhZm9fRnJlaWxlDQpgYGANCg0KDQpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTAsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KGdncmFwaCkNCnNldC5zZWVkKDEyMykNCg0KZ2dyYXBoKGJpZ3JhbWFfZ3JhZm9fVmVsYXNjbywgbGF5b3V0ID0gImZyIikgKw0KICBnZW9tX2VkZ2VfbGluaygpICsNCiAgZ2VvbV9ub2RlX3BvaW50KCkgKw0KICBnZW9tX25vZGVfbGFiZWwoYWVzKGxhYmVsID0gbmFtZSksIHZqdXN0ID0gMSwgaGp1c3QgPSAxKQ0KYGBgDQoNCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0NCmdncmFwaChiaWdyYW1hX2dyYWZvX0xhcnJlYSwgbGF5b3V0ID0gImZyIikgKw0KICBnZW9tX2VkZ2VfbGluaygpICsNCiAgZ2VvbV9ub2RlX3BvaW50KCkgKw0KICBnZW9tX25vZGVfbGFiZWwoYWVzKGxhYmVsID0gbmFtZSksIHZqdXN0ID0gMSwgaGp1c3QgPSAxKQ0KYGBgDQoNCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0NCmdncmFwaChiaWdyYW1hX2dyYWZvX0ZyZWlsZSwgbGF5b3V0ID0gImZyIikgKw0KICBnZW9tX2VkZ2VfbGluaygpICsNCiAgZ2VvbV9ub2RlX3BvaW50KCkgKw0KICBnZW9tX25vZGVfbGFiZWwoYWVzKGxhYmVsID0gbmFtZSksIHZqdXN0ID0gMSwgaGp1c3QgPSAxKQ0KYGBgDQoNCg0KIyAqKjMuIE51YmVzIGRlIHBhbGFicmFzKiogDQoNClBhcmEgcmVhbGl6YXIgbnViZXMgZGUgcGFsYWJyYXMsIHF1ZSBjb2xvcXVlbiBlbCB0YW1hw7FvIGRlIGxhcyBwYWxhYnJhcyBhY29yZGUgYSBzdSBmcmVjdWVuY2lhLCB1dGlsaXphcmVtb3MgbGEgbGlicmVyw61hICp3b3JkY2xvdWQyKi4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGVjaGFydHM0cikNCiMgbGlicmFyeShzdHJpbmdpKQ0Kd2NfVmVsYXNjbyA9IHRpZHlfVmVsYXNjb19hbm5vdGF0ZWQgJT4lIA0KICBmaWx0ZXIodXBvcyAlaW4lIGMoIk5PVU4iLCJQUk9QTiIsICJBREoiLCAiVkVSQiIpKSAlPiUNCiAgY291bnQobGVtbWEsIHNvcnQ9VCkgJT4lIA0KICBmaWx0ZXIobiA+IDIwKSAlPiUgDQogIGVfY29sb3JfcmFuZ2UobiwgY29sb3IpICU+JQ0KICBlX2NoYXJ0cygpICU+JQ0KICBlX2Nsb3VkKGxlbW1hLCBuLCBjb2xvcikgJT4lIA0KICBlX3Rvb2x0aXAoKQ0Kd2NfVmVsYXNjbw0KYGBgDQoNCiMgKio0LiBFeHRyYWNjacOzbiBkZSBwYWxhYnJhcyBjbGF2ZSoqDQoNCkVsIGdlbmVyYXIgY29uY2x1c2lvbmVzIGUgaW5zaWdodCBkZSB1biB0ZXh0byBjb24gbWlsZXMgZGUgbMOtbmVhcyB5IHBhbGFicmFzIHB1ZWRlIHNlciBhYm9yZGFkbyBkZXNkZSB2YXJpYXMgcGVyc3BlY3RpdmFzLiBVbmEgZGUgZWxsYXMgZXMgbGEgZXh0cmFjY2nDs24gZGUgcGFsYWJyYXMgY2xhdmVzLCBxdWUgcHVlZGUgc2VyIGNvbnNlZ3VpZGEgYSB0cmF2w6lzIGRlIGFsZ29yaXRtb3MgZGUgTkxQLiBFc3RhIHNlcsOhIGVudG9uY2VzIG51ZXN0cmEgw7psdGltYSBtaXNpw7NuIGVuIGVzdGUgY2Fww610dWxvLg0KDQpBY29yZGUgYSBUaHVzaGFyYSAqZXQgYWwuKiAoMjAxOSksIGFsZ3Vub3MgZGUgbG9zIGFsZ29yaXRtb3MgZGUgZXh0cmFjY2nDs24gZGUgcGFsYWJyYXMgY2xhdmUgc29uOg0KDQorIFJBS0UgKFJhcGlkIEF1dG9tYXRpYyBLZXl3b3JkIEV4dHJhY3Rpb24pOiAgRXMgdW4gYWxnb3JpdG1vIG5vIHN1cGVydmlzYWRvIGluZGVwZW5kaWVudGUgZGVsIGlkaW9tYSBxdWUgb2J0aWVuZSBwYWxhYnJhcyBjbGF2ZSBhIHRyYXbDqXMgZGVsIGFuw6FsaXNpcyBkZSBsYSBmcmVjdWVuY2lhIHkgZ3JhZG8gKGNvLW9jdXJyZW5jaWEpIGRlIGxhcyBwYWxhYnJhcy4NCisgVGV4dFJhbms6IEVzIHVuYSB0w6ljbmljYSBubyBzdXBlcnZpc2FkYSBiYXNhZGEgZW4gZ3JhZm9zIHF1ZSBvYnRpZW5lIGVsIHJlc3VtZW4gZGUgdW4gdGV4dG8uIEVzdMOhIGJhc2FkYSBzb2JyZXRvZG8gZW4gZXRpcXVldGFkbyBQT1MuDQorIFBvc2l0aW9uUmFuazogRXMgdW4gYWxnb3JpdG1vIGJhc2FkbyBlbiBncmFmb3MgcXVlIGJ1c2NhIGZyYXNlcyBhbCBpbmljaW8gZGUgbG9zIGRvY3VtZW50b3MgeSBsb3MgY2FuZGlkYXRpemEgYSBzZXIgY2xhdmUuDQoNCiMjICoqNC4xLiBSQUtFKioNCg0KTGEgbGlicmVyw61hICp1ZHBpcGUqIGluY2x1eWUgZWwgYWxnb3JpdG1vIFJBS0UgcGFyYSBleHRyYWNjacOzbiBkZSBwYWxhYnJhcyBjbGF2ZS4gRXN0ZSBlcyB1biBhbGdvcml0bW8gbm8gc3VwZXJ2aXNhZG8gaW5kZXBlbmRpZW50ZSBkZWwgaWRpb21hIHF1ZSBvYnRpZW5lIHBhbGFicmFzIGNsYXZlIGEgdHJhdsOpcyBkZWwgYW7DoWxpc2lzIGRlIGxhIGZyZWN1ZW5jaWEgeSBncmFkbyAoY28tb2N1cnJlbmNpYSkgZGUgbGFzIHBhbGFicmFzLiBFc3BlY8OtZmljYW1pZW50ZSwgc2lndWUgZXN0b3MgcGFzb3M6DQoNCisgU2UgZXh0cmFlbiBsYXMgcGFsYWJyYXMgY2FuZGlkYXRhcyBkZWwgY29uanVudG8gZGUgcGFsYWJyYXMsIGRlc3B1w6lzIGRlIGhhYmVyIHF1aXRhZG8gcGFsYWJyYXMgdmFjw61hcyBlIGlycmVsZXZhbnRlcy4NCisgU2UgY2FsY3VsYSB1biBzY29yZSBwYXJhIGNhZGEgcGFsYWJyYSBhIHRyYXbDqXMgZGU6DQogICsgU2UgYnVzY2EgY3XDoW50YXMgdmVjZXMgb2N1cnJlIHVuYSBwYWxhYnJhIHkgY3XDoW50YXMgdmVjZXMgY28tb2N1cnJlIGNvbiBvdHJhLg0KICArIFNlIGNvbnN0cnV5ZSBlbCBzY29yZSBjb21vIGVsIHJhdGlvIGRlIGNvLW9jdXJyZW5jaWFzIHZlcnN1cyBmcmVjdWVuY2lhLg0KKyBTZSBvcmRlbmEgZWwgc2NvcmUgZGUgbWF5b3IgYSBtZW5vciB5IGxhcyBwcmltZXJhcyBzZXLDoW4gbGFzIHBhbGFicmFzIGNsYXZlLg0KDQpBIGNvbnRpbnVhY2nDs24gdmVyZW1vcyBlamVtcGxvcyBkZSBsYSBhcGxpY2FjacOzbiBkZSBlc3RhIHTDqWNuaWNhIHBhcmEgcGFsYWJyYXMgeSBuLWdyYW1hcy4NCg0KYGBge3J9DQpsaWJyYXJ5KHVkcGlwZSkNClZlbGFzY29fa2V5d29yZHMgPSBrZXl3b3Jkc19yYWtlKHggPSB0aWR5X1ZlbGFzY28sIHRlcm0gPSAibGVtbWEiLCBncm91cCA9ICJkb2NfaWQiLCByZWxldmFudCA9IHRpZHlfVmVsYXNjbyR1cG9zICVpbiUgYygiTk9VTiIsICJBREoiLCJQUk9QTiIsIlZFUkIiKSwgbmdyYW1fbWF4ID0gMykNCmhlYWQoVmVsYXNjb19rZXl3b3JkcykNCmBgYA0KDQpgYGB7cn0NCkxhcnJlYV9rZXl3b3JkcyA9IGtleXdvcmRzX3Jha2UoeCA9IHRpZHlfTGFycmVhLCB0ZXJtID0gImxlbW1hIiwgZ3JvdXAgPSAiZG9jX2lkIiwgcmVsZXZhbnQgPSB0aWR5X0xhcnJlYSR1cG9zICVpbiUgYygiTk9VTiIsICJBREoiLCJQUk9QTiIsIlZFUkIiKSwgbmdyYW1fbWF4ID0gMykNCmhlYWQoTGFycmVhX2tleXdvcmRzKQ0KYGBgDQoNCmBgYHtyfQ0KRnJlaWxlX2tleXdvcmRzID0ga2V5d29yZHNfcmFrZSh4ID0gdGlkeV9GcmVpbGUsIHRlcm0gPSAibGVtbWEiLCBncm91cCA9ICJkb2NfaWQiLCByZWxldmFudCA9IHRpZHlfRnJlaWxlJHVwb3MgJWluJSBjKCJOT1VOIiwgIkFESiIsIlBST1BOIiwiVkVSQiIpLCBuZ3JhbV9tYXggPSAzKQ0KaGVhZChGcmVpbGVfa2V5d29yZHMpDQpgYGANCg0KPCEtLSAjICoqNS4gQ2FzbyBwcsOhY3RpY28qKiAtLT4NCg0KPCEtLSBQYXJhIGFwcm9iYXIgZXN0ZSBjdXJzbyBlbCBlc3R1ZGlhbnRlIGRlYmVyw6EgaGFjZXIgdW4gdHJhYmFqbyBwcsOhY3RpY28gY29uIHRvZG8gbG8gcXVlIGFwcmVuZGltb3MuIC0tPg0KDQo8IS0tIEVuIGJhc2UgYSBsYXMgdMOpY25pY2FzIGRlIG9idGVuY2nDs24sIGVzdHJ1Y3R1cmFjacOzbiwgcHJlLXByb2Nlc2FtaWVudG8geSBhbsOhbGlzaXMgZXhwbG9yYXRvcmlvIGRlIGRhdG9zIGRlIHRleHRvIHJldmlzYWRhcyBlbiBjbGFzZSwgZGVmaW5pciB1biBvYmpldGl2byBkZSBhbsOhbGlzaXMgcGFyYSB1biBjYXNvIGRlIGVzdHVkaW8gZGUgc2VsZWNjacOzbiBwZXJzb25hbC4gU29icmUgZXN0ZSBvYmpldGl2byByZWFsaXphciBjYWRhIHBhc28gZGVmaW5pZG8gZW4gbGEgc2VjY2nDs24g4oCcRmx1am8gZGUgdHJhYmFqbyBzb2JyZSBkYXRvcyBkZSB0ZXh0b+KAnSBkZWwgbnVtZXJhbCAxLjEuIGRlbCBjYXDDrXR1bG8gMy4gRXMgZGVjaXIsIGVsIGVzdHVkaWFudGUgZGViZXLDoTogLS0+DQoNCjwhLS0gKyBEZWZpbmlyIHVuIG9iamV0aXZvIGRlIGFuw6FsaXNpcy4gLS0+DQo8IS0tICsgT2J0ZW5lciBsb3MgZGF0b3MgYSB0cmF2w6lzIGRlIHdlYiBzY3JhcHBpbmcsIGRvY3VtZW50b3MgcGRmIG8gdXNhciB1biBkYXRhc2V0IGFwcm9waWFkby4gLS0+DQo8IS0tICsgUmVhbGl6YXIgbGEgZXN0cnVjdHVyYWNpw7NuIHkgcHJlLXByb2Nlc2VhbWllbnRvIGRlIGxvcyBkYXRvcywgaW5jbHV5ZW5kbyB5IG5vIGxpbWl0w6FuZG9zZSBhOiBkYXIgZm9ybWF0byB0aWR5IGEgbG9zIGRhdG9zLCB0b2tlbml6YXIsIGxlbWF0aXphciwgcmVhbGl6YXIgc3RlbW1pbmcsIHJlbW92ZXIgc3RvcHdvcmRzLCBldGMuIENhYmUgbm90YXIgcXVlLCBzZWfDum4gc3Ugb2JqZXRpdm8gZGUgZXN0dWRpbywgbGEgbGVtYXRpemFjacOzbiB5IHN0ZW1taW5nIHBvZHLDrWEgbm8gc2VyIG5lY2VzYXJpYXMuIC0tPg0KPCEtLSArIFJlYWxpemFyIGVsIGFuw6FsaXNpcyBleHBsb3JhdG9yaW8gc29icmUgcGFsYWJyYXMsIG4tZ3JhbWFzIG8gYWxnw7puIHRva2VuIGRlc2VhZG8uIFNlIHB1ZWRlbiB1c2FyIG51YmVzIGRlIHBhbGFicmFzLCBhbsOhbGlzaXMgZGUgZnJlY3VlbmNpYXMsIGdyYWZvcywgeSBvdHJhcyB0w6ljbmljYXMgcXVlIGVsIGVzdHVkaWFudGUgY29uc2lkZXJlIGFwcm9waWFkYS4gLS0+DQo8IS0tIENvbW8gw7puaWNhIHJlc3RyaWNjacOzbiBhIGVzdGEgdGFyZWEgc2UgZXN0YWJsZWNlIHF1ZSBubyBzZSBwb2Ryw6FuIHV0aWxpemFyIGxvcyBkYXRhc2V0IGRlIG5vdGljaWFzIG5pIGxpdGVyYXR1cmEgaW5jbHVpZGFzIGVuIGxvcyBub3RlYm9va3MgZGUgbGFzIGNsYXNlcy4gLS0+DQoNCjwhLS0gRWwgdGllbXBvIGzDrW1pdGUgcGFyYSBsYSBlbnRyZWdhIGRlIGxhIGFjdGl2aWRhZCBzZSBtZW5jaW9uYXLDoSBlbiBlbCDDumx0aW1vIGTDrWEgZGUgY2xhc2UsIHBlcm8gbm8gc2Vyw6EgaW5mZXJpb3IgYSBsb3MgMTUgZMOtYXMuIEVsIGFuw6FsaXNpcyBzZSBlbnRyZWdhcsOhIGNvbW8gdW4gc2NyaXB0IGNvbWVudGFkbyBvIGNvbiB1biBjdWFkZXJubyBkZSB0cmFiYWpvIHJlYWxpemFkbyBlbiBsZW5ndWFqZSBSIHNvYnJlIHVuIElERSBhcHJvcGlhZG8gKFJTdHVkaW8sIEp1cHl0ZXJMYWIsIEp1cHl0ZXJOb3RlYm9vaywgZXRjKS4gLS0+DQoNCjwhLS0gQXF1ZWxsb3MgdHJhYmFqb3Mgc2luIGNvbmNsdXNpb25lcyBhY2VyY2EgZGUgbG9zIHJlc3VsdGFkb3Mgc2Vyw6FuIGNhbGlmaWNhZG9zIGNvbiB1bmEgbm90YSBkZSBjZXJvLiAtLT4NCg0KDQojICoqNS4gQmlibGlvZ3JhZsOtYSoqDQo=