Intentemos recuperar datos acerca de los miembros del parlamento del Uruguay. Para cada miembro de la Asamblea General, intentaremos recuperar su nombre, a qué partido pertenece, a qué comisiones pertenece, y datos acerca de su actividad parlamentaria. Que intentaremos recopilar aparecen en la página del plenario del Parlamento.

Empecemos por el final. Vayamos a la página de uno de los miembros y veamos cuáles son los proyectos que ha presentado:

library(httr)

URL <- "https://parlamento.gub.uy/camarasycomisiones/legisladores/759/iniciativas-legislador"
r <- GET(URL)

Veamos en primer lugar el contenido al que nos enfrentamos:

res <- content(r, "text")

El problema es por tanto ver cómo podemos navegar y extraer elementos de HTML de la página. Para eso, httr no nos sirve y tenemos que recurrir a otro paquete. En este caso, usaremos rvest

library(rvest)
library(stringr)

Empecemos por leer el contenido de la página

spage <- read_html(URL)
spage

El paquete nos ofrece funciones para navegar y extraer elementos del modelo de la página. Por ejemplo, nos ofrece una función html_table que nos permite recuperar el contenido de las tablas o la función html_nodes para extraer nodos del DOM. Pero para ver qué tenemos que extraer, lo más práctico es empezar por examinar la página en developer mode.

Ahí vemos que lo que queremos extraer son los datos de la única tabla en la página:

datos <- spage %>%
    html_table() %>%
    .[[1]]
datos

Démosle a esta pequeña pieza de código un nombre para que sea más fácil de usar en el futuro:

extraer_proyectos <- function(url) {
    res <- read_html(url) %>%
        html_table() %>%
        .[[1]]
    return(res)
}

Vayamos ahora un nivel más arriba. De esta página nos interesan varias cosas. Por una parte, la tabla de la lista de las comisiones en las que esta persona participa. De nuevo, esto es una tabla y podemos usar código similar al anterior:

URL <- "https://parlamento.gub.uy/camarasycomisiones/legisladores/759"
spage <- read_html(URL) %>%
    html_table() %>%
    .[[1]]

Y ponemos el código en otra función:

extraer_comisiones <- function(url) {
    res <- read_html(url) %>%
        html_table() %>%
        .[[1]]
    return(res)
}

También nos interesa obtener el partido al que pertenece. La información en este caso la podemos encontrar en un campo div al que podemos referirnos por su clase:

URL <- "https://parlamento.gub.uy/camarasycomisiones/legisladores/759"
spage <- read_html(URL) %>%
    html_nodes("div.field-item")
spage

Vemos que obtenemos demasiados resultados. Podríamos referirnos al que nos interesa a través del orden que ocupa en el vector de retorno, pero parece más seguro referirnos por la información del contexto, como, por ejemplo, a qué grupo sigue.

URL <- "https://parlamento.gub.uy/camarasycomisiones/legisladores/759"
spage <- read_html(URL) %>%
    html_nodes("div.field-name-field-persona-desc > div.field-items > div.field-item")
spage

De este resultado nos interesa únicamente el texto entre las etiquetas:

URL <- "https://parlamento.gub.uy/camarasycomisiones/legisladores/759"
spage <- read_html(URL) %>%
    html_nodes("div.field-name-field-persona-desc > div.field-items > div.field-item") %>%
    html_text()
spage

Pongamos este código en otra función:

extraer_descripcion <- function(url) {
    res <- read_html(url) %>%
        html_nodes("div.field-name-field-persona-desc > div.field-items > div.field-item") %>%
        html_text()
    return(res)
}

Pero también nos interesa saber cómo acceder a los datos de los proyectos presentados, para recuperar la información que extrajimos antes. Para eso queremos saber cómo se llega desde esta página a la anterior. Lo que vemos es que esos resultados están disponibles en un enlace fijo (/iniciativas-legislador) que sigue al identificador único de esta persona. Podemos hacer dos cosas. Por una parte, sabiendo que la página está en una carpeta más profunda, podemos simplemente adjuntar ese nombre de carpeta a la URL con la que estamos trabajando. La otra alternativa es recuperar el enlace que aparece en la página y seguirlo. La segunda alternativa es más “segura.”

Para ello, podemos extraer todos los enlaces de la ficha y quedarnos con el que se llame Proyectos presentados

URL <- "https://parlamento.gub.uy/camarasycomisiones/legisladores/759"
row <- read_html(URL) %>%
    html_nodes("div.pane-custom") %>%
    html_nodes("li") %>%
    html_text() %>%
    as.data.frame() %>%    
    mutate(matches=grepl("Proyectos presentados", .)) %>%
    summarize(row=which(matches)) %>%
    as.numeric
row

Ahora podemos seleccionar el elemento correspondiente en la tabla utilizando el código anterior

URL <- "https://parlamento.gub.uy/camarasycomisiones/legisladores/759"
spage <- read_html(URL) %>%
    html_nodes("div.pane-custom") %>%
    html_nodes("li") %>%    
    .[[row]] %>%
    html_nodes("a") %>%    
    html_attr("href")
spage

Pongamos ahora este código en una función para que sea más fácil de operar.

extraer_link_proyectos <- function(url) {
    lists <- read_html(url) %>%
        html_nodes("div.pane-custom") %>%
        html_nodes("li")

    row <- lists %>%
        html_text() %>%
        as.data.frame() %>%    
        mutate(matches=grepl("Proyectos presentados", .)) %>%
        summarize(row=which(matches)) %>%
        as.numeric

    res <- lists %>%
        .[[row]] %>%
        html_nodes("a") %>%    
        html_attr("href")
    return(res)
}

La función para extraer los proyectos es similar a la anterior

extraer_proyectos <- function(url) {
    res <- read_html(url) %>%
        html_table() %>%
        .[[1]]
    return(res)
}

Pero ahora que tenemos esta función, podemos pensar en usarla más generalmente. Por ejemplo, uno de los enlaces de la tabla del perfil del miembro nos permite acceder a información sobre su asistencia a comisiones.

URL <- "https://parlamento.gub.uy/camarasycomisiones/legisladores/759/asistencia-a-comisiones"
row <- read_html(URL) %>%
    html_table()
row

Digamos que nos interesa únicamente la información más desagregada. Por ejemplo, la segunda tabla. Extraer esta información es muy sencillo y podemos ponerlo en una función

extraer_asistencia <- function(url) {
    res <- read_html(url) %>%
        html_table() %>%
        .[[2]]
    return(res)
}

Pero, ahora que tenemos extraer_link_proyectos, podemos pensar en generalizer esta función para extraer no solo el link a los proyectos sino también el link a esta tabla:

extraer_link <- function(url, texto) {
    lists <- read_html(url) %>%
        html_nodes("div.pane-custom") %>%
        html_nodes("li")

    row <- lists %>%
        html_text() %>%
        as.data.frame() %>%    
        mutate(matches=grepl(texto, .)) %>%
        summarize(row=which(matches)) %>%
        as.numeric

    res <- lists %>%
        .[[row]] %>%
        html_nodes("a") %>%    
        html_attr("href")
    return(res)
}

Así, tenemos una función más general que podemos reusar para obtener otras piezas de información de la página

URL <- "https://parlamento.gub.uy/camarasycomisiones/legisladores/759"
extraer_link(URL, "Proyectos presentados")
extraer_link(URL, "Asistencia a Comisiones")

Ya solo nos queda una cosa por hacer. Ahora necesitamos ir a la primera página para obtener el listado de miembros del parlamento para poder recuperar toda la información anterior. Esta información la tenemos disponible en la sección Plenario de la página web. Aquí necesitamos dos cosas. Por una parte el nombre. Por otra, en enlace que nos permita seguir navegando. Las dos cosas las podemos encontrar en la tabla:

URL <- "https://parlamento.gub.uy/camarasycomisiones/asambleageneral/plenario/integracion"

spage <- read_html(URL) %>%
    html_nodes("span.field-content")
spage

Igual que antes, de aquí solo necesitamos un único tipo de contenido, al que podemos referirnos a través de la estructura del DOM

URL <- "https://parlamento.gub.uy/camarasycomisiones/asambleageneral/plenario/integracion"

link_ <- read_html(URL) %>%
    html_nodes("div.views-field-Psn-Codigo-1 > span > a") %>%
    html_attr("href")

text_ <- read_html(URL) %>%
    html_nodes("div.views-field-Psn-Codigo-1 > span > a") %>%
    html_text()

Ahora ya podemos poner todas las piezas juntas. El proceso es el siguiente. Para cada uno de los nombres que acabamos de extraer, seguiremos el enlace asociado, obtendremos la información relativa al miembro, capturaremos los enlaces que tengamos que seguir en ese punto y por último recuperaremos los proyectos y su asistencia a comisiones. Es decir, pondremos todas las piezas anteriores en un loop que empezará con la lista de miembros que acabamos de recuperar:

listado <- data.frame("nombre"=text_, "enlace"=link_)
listado

Las funciones ahora nos resultarán muy útiles para que código sea más legible. Para cada una de las entradas en listado, esto es lo que haremos:

capturar_datos_legislador <- function(url) {
    descripcion <- extraer_descripcion(url)
    comisiones <- extraer_comisiones(url)
    link_proyectos <- extraer_link(url, "Proyectos presentados")
    proyectos <- extraer_proyectos(link_proyectos)
    link_asistencia <- extraer_link(url, "Asistencia a Comisiones")
    asistencia <- extraer_asistencia(link_asistencia)
    return(list("descripcion"=descripcion,
                "comisiones"=comisiones
                "proyectos"=proyectos,
                "asistencia"=asistencia))
}

Lo único que tenemos que tener en cuenta es que las carpetas están definidas por su posición relativa. En concreto, parecen estar definidas por su posición relativa al TLD. Así pues:

BASEURL <- "https://parlamento.gub.uy"
full_url <- function(folder) {
    return(paste0(BASEURL, folder))
}

Nuestra función es ahora:

capturar_datos_legislador <- function(url) {
    url <- full_url(url)
    descripcion <- extraer_descripcion(url)
    comisiones <- extraer_comisiones(url)
    
    link_proyectos <- extraer_link(url, "Proyectos presentados")    
    link_proyectos <- full_url(link_proyectos)
    proyectos <- extraer_proyectos(link_proyectos)
    
    link_asistencia <- extraer_link(url, "Asistencia a Comisiones")
    link_asistencia <- full_url(link_asistencia)
    asistencia <- extraer_asistencia(link_asistencia)
    return(list("descripcion"=descripcion,
                "comisiones"=comisiones,
                "proyectos"=proyectos,
                "asistencia"=asistencia))
}

Y lo único que nos queda es aplicarla a cada una de las filas de la lista que recuperamos más arriba:

for (i in 1:nrow(listado)) {
    print(i)
    datos <- capturar_datos_legislador(listado[i, "enlace"])    
}

¿Por qué falla en la primera linea? Vayamos a la página correspondiente

listado[1, ]

En el código hemos asumido que cada una de las piezas que queremos extraer existen. En este caso, vemos que esta persona no pertenece a ninguna comisión. O, mejor dicho, ninguna comisión aparece listada. Lo que podemos hacer en este caso es “capturar una excepción” para que el código no falle:

tryCatch(extraer_comisiones(full_url(listado[1, "enlace"])),
         error=function(e) {return("No hay comisiones")})

Lo que vemos es que tryCatch intentará ejecutar extraer_comisiones. En el caso de que obtenga un error, entonces ejecutará en su lugar la función que hemos definido y que, por ahora, se limita a devolver información que puede ser útil.

capturar_datos_legislador <- function(url) {
    url <- full_url(url)
    descripcion <- extraer_descripcion(url)
    comisiones <- tryCatch(extraer_comisiones(url),
                          error=function(e) return("Ninguna comisión listada"))
    
    link_proyectos <- extraer_link(url, "Proyectos presentados")    
    link_proyectos <- full_url(link_proyectos)
    proyectos <- extraer_proyectos(link_proyectos)
    
    link_asistencia <- extraer_link(url, "Asistencia a Comisiones")
    link_asistencia <- full_url(link_asistencia)
    asistencia <- extraer_asistencia(link_asistencia)
    return(list("descripcion"=descripcion,
                "comisiones"=comisiones,
                "proyectos"=proyectos,
                "asistencia"=asistencia))
}

Por supuesto, podríamos (y deberíamos) asegurar que cada una de nuestras funciones tengan un tryCatch de tal modo que la ejecución no muera durante el proceso de captura. Pero, por ahora, nos sirve lo que tenemos.

Solo nos quedan un par de cosas. Por una parte, asociar a cada entrada el nombre del legislador. Para ello, lo almacenaremos todo en una lista de la que ya conocemos las dimesiones. Por último, entre llamada y llamada, dejaremos un tiempo de espera para no sobrecargar el servidor.

res <- vector('list', nrow(listado))
for (i in 1:nrow(listado)) {
    print(i)
    nombre <- listado[i, "nombre"]
    datos <- capturar_datos_legislador(listado[i, "enlace"])
    res[[i]] <- list("nombre"=nombre, "datos"=datos)
    Sys.sleep(5)
}
LS0tIAp0aXRsZTogIkNhcHR1cmFyIGRhdG9zIGRlIHVuYSBww6FnaW5hIHdlYiIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLCBjYWNoZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGV2YWwgPSBGQUxTRSkKYGBgCgpJbnRlbnRlbW9zIHJlY3VwZXJhciBkYXRvcyBhY2VyY2EgZGUgbG9zIG1pZW1icm9zIGRlbCBwYXJsYW1lbnRvIGRlbApVcnVndWF5LiBQYXJhIGNhZGEgbWllbWJybyBkZSBsYSBBc2FtYmxlYSBHZW5lcmFsLCBpbnRlbnRhcmVtb3MKcmVjdXBlcmFyIHN1IG5vbWJyZSwgYSBxdcOpIHBhcnRpZG8gcGVydGVuZWNlLCBhIHF1w6kgY29taXNpb25lcwpwZXJ0ZW5lY2UsIHkgZGF0b3MgYWNlcmNhIGRlIHN1IGFjdGl2aWRhZCBwYXJsYW1lbnRhcmlhLiBRdWUKaW50ZW50YXJlbW9zIHJlY29waWxhciBhcGFyZWNlbiBlbiBsYSBww6FnaW5hIGRlbCBbcGxlbmFyaW8gZGVsClBhcmxhbWVudG9dKGh0dHBzOi8vcGFybGFtZW50by5ndWIudXkvY2FtYXJhc3ljb21pc2lvbmVzL2FzYW1ibGVhZ2VuZXJhbC9wbGVuYXJpby9pbnRlZ3JhY2lvbi9hY3R1YW50ZXMpLgoKPHAgc3R5bGU9InRleHQtYWxpZ246Y2VudGVyIj4KPGltZyBzcmM9Ii4vYXNzZXRzL3Zpc3RhLnBuZyIgd2lkdGg9IjQ2MCIvPgo8L3A+CgpFbXBlY2Vtb3MgcG9yIGVsIGZpbmFsLiBWYXlhbW9zIGEgbGEgcMOhZ2luYSBkZSB1bm8gZGUgbG9zIG1pZW1icm9zIHkKdmVhbW9zIGN1w6FsZXMgc29uIGxvcyBwcm95ZWN0b3MgcXVlIGhhIHByZXNlbnRhZG86CgpgYGB7cn0KbGlicmFyeShodHRyKQoKVVJMIDwtICJodHRwczovL3BhcmxhbWVudG8uZ3ViLnV5L2NhbWFyYXN5Y29taXNpb25lcy9sZWdpc2xhZG9yZXMvNzU5L2luaWNpYXRpdmFzLWxlZ2lzbGFkb3IiCnIgPC0gR0VUKFVSTCkKYGBgCgpWZWFtb3MgZW4gcHJpbWVyIGx1Z2FyIGVsIGNvbnRlbmlkbyBhbCBxdWUgbm9zIGVuZnJlbnRhbW9zOgoKYGBge3J9CnJlcyA8LSBjb250ZW50KHIsICJ0ZXh0IikKYGBgCgpFbCBwcm9ibGVtYSBlcyBwb3IgdGFudG8gdmVyIGPDs21vIHBvZGVtb3MgbmF2ZWdhciB5IGV4dHJhZXIgZWxlbWVudG9zIGRlIEhUTUwKZGUgbGEgcMOhZ2luYS4gUGFyYSBlc28sIGBodHRyYCBubyBub3Mgc2lydmUgeSB0ZW5lbW9zIHF1ZSByZWN1cnJpciBhIG90cm8KcGFxdWV0ZS4gRW4gZXN0ZSBjYXNvLCB1c2FyZW1vcyBgcnZlc3RgCgpgYGB7cn0KbGlicmFyeShydmVzdCkKbGlicmFyeShzdHJpbmdyKQpgYGAKCkVtcGVjZW1vcyBwb3IgbGVlciBlbCBjb250ZW5pZG8gZGUgbGEgcMOhZ2luYQpgYGB7cn0Kc3BhZ2UgPC0gcmVhZF9odG1sKFVSTCkKc3BhZ2UKYGBgCgpFbCBwYXF1ZXRlIG5vcyBvZnJlY2UgZnVuY2lvbmVzIHBhcmEgbmF2ZWdhciB5IGV4dHJhZXIgZWxlbWVudG9zIGRlbAptb2RlbG8gZGUgbGEgcMOhZ2luYS4gUG9yIGVqZW1wbG8sIG5vcyBvZnJlY2UgdW5hIGZ1bmNpw7NuIGBodG1sX3RhYmxlYApxdWUgbm9zIHBlcm1pdGUgcmVjdXBlcmFyIGVsIGNvbnRlbmlkbyBkZSBsYXMgdGFibGFzIG8gbGEgZnVuY2nDs24KYGh0bWxfbm9kZXNgIHBhcmEgZXh0cmFlciBub2RvcyBkZWwgRE9NLiBQZXJvIHBhcmEgdmVyIHF1w6kgdGVuZW1vcyBxdWUKZXh0cmFlciwgbG8gbcOhcyBwcsOhY3RpY28gZXMgZW1wZXphciBwb3IgZXhhbWluYXIgbGEgcMOhZ2luYSBlbgpfZGV2ZWxvcGVyIG1vZGVfLgoKCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOmNlbnRlciI+CjxpbWcgc3JjPSIuL2Fzc2V0cy92aXN0YS1kZXZlbG9wZXIucG5nIiB3aWR0aD0iNDYwIi8+CjwvcD4KCgpBaMOtIHZlbW9zIHF1ZSBsbyBxdWUgcXVlcmVtb3MgZXh0cmFlciBzb24gbG9zIGRhdG9zIGRlIGxhIMO6bmljYSB0YWJsYQplbiBsYSBww6FnaW5hOgoKYGBge3J9CmRhdG9zIDwtIHNwYWdlICU+JQogICAgaHRtbF90YWJsZSgpICU+JQogICAgLltbMV1dCmRhdG9zCmBgYAoKRMOpbW9zbGUgYSBlc3RhIHBlcXVlw7FhIHBpZXphIGRlIGPDs2RpZ28gdW4gbm9tYnJlIHBhcmEgcXVlIHNlYSBtw6FzCmbDoWNpbCBkZSB1c2FyIGVuIGVsIGZ1dHVybzoKYGBge3J9CmV4dHJhZXJfcHJveWVjdG9zIDwtIGZ1bmN0aW9uKHVybCkgewogICAgcmVzIDwtIHJlYWRfaHRtbCh1cmwpICU+JQogICAgICAgIGh0bWxfdGFibGUoKSAlPiUKICAgICAgICAuW1sxXV0KICAgIHJldHVybihyZXMpCn0KYGBgCgpWYXlhbW9zIGFob3JhIHVuIG5pdmVsIG3DoXMgYXJyaWJhLiBEZSBlc3RhIHDDoWdpbmEgbm9zIGludGVyZXNhbiB2YXJpYXMKY29zYXMuIFBvciB1bmEgcGFydGUsIGxhIHRhYmxhIGRlIGxhIGxpc3RhIGRlIGxhcyBjb21pc2lvbmVzIGVuIGxhcwpxdWUgZXN0YSBwZXJzb25hIHBhcnRpY2lwYS4gRGUgbnVldm8sIGVzdG8gZXMgdW5hIHRhYmxhIHkgcG9kZW1vcyB1c2FyCmPDs2RpZ28gc2ltaWxhciBhbCBhbnRlcmlvcjoKCmBgYHtyfQpVUkwgPC0gImh0dHBzOi8vcGFybGFtZW50by5ndWIudXkvY2FtYXJhc3ljb21pc2lvbmVzL2xlZ2lzbGFkb3Jlcy83NTkiCnNwYWdlIDwtIHJlYWRfaHRtbChVUkwpICU+JQogICAgaHRtbF90YWJsZSgpICU+JQogICAgLltbMV1dCmBgYAoKWSBwb25lbW9zIGVsIGPDs2RpZ28gZW4gb3RyYSBmdW5jacOzbjoKCmBgYHtyfQpleHRyYWVyX2NvbWlzaW9uZXMgPC0gZnVuY3Rpb24odXJsKSB7CiAgICByZXMgPC0gcmVhZF9odG1sKHVybCkgJT4lCiAgICAgICAgaHRtbF90YWJsZSgpICU+JQogICAgICAgIC5bWzFdXQogICAgcmV0dXJuKHJlcykKfQpgYGAKCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOmNlbnRlciI+CjxpbWcgc3JjPSIuL2Fzc2V0cy92aXN0YS1kZXZlbG9wZXItbWllbWJyby5wbmciIHdpZHRoPSI0NjAiLz4KPC9wPgoKClRhbWJpw6luIG5vcyBpbnRlcmVzYSBvYnRlbmVyIGVsIHBhcnRpZG8gYWwgcXVlIHBlcnRlbmVjZS4gTGEKaW5mb3JtYWNpw7NuIGVuIGVzdGUgY2FzbyBsYSBwb2RlbW9zIGVuY29udHJhciBlbiB1biBjYW1wbyBgZGl2YCBhbCBxdWUKcG9kZW1vcyByZWZlcmlybm9zIHBvciBzdSBjbGFzZToKCmBgYHtyfSAKVVJMIDwtICJodHRwczovL3BhcmxhbWVudG8uZ3ViLnV5L2NhbWFyYXN5Y29taXNpb25lcy9sZWdpc2xhZG9yZXMvNzU5IgpzcGFnZSA8LSByZWFkX2h0bWwoVVJMKSAlPiUKICAgIGh0bWxfbm9kZXMoImRpdi5maWVsZC1pdGVtIikKc3BhZ2UKYGBgCgpWZW1vcyBxdWUgb2J0ZW5lbW9zIGRlbWFzaWFkb3MgcmVzdWx0YWRvcy4gUG9kcsOtYW1vcyByZWZlcmlybm9zIGFsIHF1ZQpub3MgaW50ZXJlc2EgYSB0cmF2w6lzIGRlbCBvcmRlbiBxdWUgb2N1cGEgZW4gZWwgdmVjdG9yIGRlIHJldG9ybm8sCnBlcm8gcGFyZWNlIG3DoXMgc2VndXJvIHJlZmVyaXJub3MgcG9yIGxhIGluZm9ybWFjacOzbiBkZWwgY29udGV4dG8sCmNvbW8sIHBvciBlamVtcGxvLCBhIHF1w6kgZ3J1cG8gc2lndWUuIAoKYGBge3J9ClVSTCA8LSAiaHR0cHM6Ly9wYXJsYW1lbnRvLmd1Yi51eS9jYW1hcmFzeWNvbWlzaW9uZXMvbGVnaXNsYWRvcmVzLzc1OSIKc3BhZ2UgPC0gcmVhZF9odG1sKFVSTCkgJT4lCiAgICBodG1sX25vZGVzKCJkaXYuZmllbGQtbmFtZS1maWVsZC1wZXJzb25hLWRlc2MgPiBkaXYuZmllbGQtaXRlbXMgPiBkaXYuZmllbGQtaXRlbSIpCnNwYWdlCmBgYAoKRGUgZXN0ZSByZXN1bHRhZG8gbm9zIGludGVyZXNhIMO6bmljYW1lbnRlIGVsIHRleHRvIGVudHJlIGxhcwpldGlxdWV0YXM6CgpgYGB7cn0KVVJMIDwtICJodHRwczovL3BhcmxhbWVudG8uZ3ViLnV5L2NhbWFyYXN5Y29taXNpb25lcy9sZWdpc2xhZG9yZXMvNzU5IgpzcGFnZSA8LSByZWFkX2h0bWwoVVJMKSAlPiUKICAgIGh0bWxfbm9kZXMoImRpdi5maWVsZC1uYW1lLWZpZWxkLXBlcnNvbmEtZGVzYyA+IGRpdi5maWVsZC1pdGVtcyA+IGRpdi5maWVsZC1pdGVtIikgJT4lCiAgICBodG1sX3RleHQoKQpzcGFnZQpgYGAKClBvbmdhbW9zIGVzdGUgY8OzZGlnbyBlbiBvdHJhIGZ1bmNpw7NuOgoKYGBge3J9CmV4dHJhZXJfZGVzY3JpcGNpb24gPC0gZnVuY3Rpb24odXJsKSB7CiAgICByZXMgPC0gcmVhZF9odG1sKHVybCkgJT4lCiAgICAgICAgaHRtbF9ub2RlcygiZGl2LmZpZWxkLW5hbWUtZmllbGQtcGVyc29uYS1kZXNjID4gZGl2LmZpZWxkLWl0ZW1zID4gZGl2LmZpZWxkLWl0ZW0iKSAlPiUKICAgICAgICBodG1sX3RleHQoKQogICAgcmV0dXJuKHJlcykKfQpgYGAKClBlcm8gdGFtYmnDqW4gbm9zIGludGVyZXNhIHNhYmVyIGPDs21vIGFjY2VkZXIgYSBsb3MgZGF0b3MgZGUgbG9zCnByb3llY3RvcyBwcmVzZW50YWRvcywgcGFyYSByZWN1cGVyYXIgbGEgaW5mb3JtYWNpw7NuIHF1ZSBleHRyYWppbW9zCmFudGVzLiBQYXJhIGVzbyBxdWVyZW1vcyBzYWJlciBjw7NtbyBzZSBsbGVnYSBkZXNkZSBlc3RhIHDDoWdpbmEgYSBsYQphbnRlcmlvci4gTG8gcXVlIHZlbW9zIGVzIHF1ZSBlc29zIHJlc3VsdGFkb3MgZXN0w6FuIGRpc3BvbmlibGVzIGVuIHVuCmVubGFjZSBmaWpvIChgL2luaWNpYXRpdmFzLWxlZ2lzbGFkb3JgKSBxdWUgc2lndWUgYWwgaWRlbnRpZmljYWRvcgrDum5pY28gZGUgZXN0YSBwZXJzb25hLiBQb2RlbW9zIGhhY2VyIGRvcyBjb3Nhcy4gUG9yIHVuYSBwYXJ0ZSwKc2FiaWVuZG8gcXVlIGxhIHDDoWdpbmEgZXN0w6EgZW4gdW5hIGNhcnBldGEgbcOhcyBwcm9mdW5kYSwgcG9kZW1vcwpzaW1wbGVtZW50ZSBhZGp1bnRhciBlc2Ugbm9tYnJlIGRlIGNhcnBldGEgYSBsYSBVUkwgY29uIGxhIHF1ZSBlc3RhbW9zCnRyYWJhamFuZG8uIExhIG90cmEgYWx0ZXJuYXRpdmEgZXMgcmVjdXBlcmFyIGVsIGVubGFjZSBxdWUgYXBhcmVjZSBlbgpsYSBww6FnaW5hIHkgc2VndWlybG8uIExhIHNlZ3VuZGEgYWx0ZXJuYXRpdmEgZXMgbcOhcyAic2VndXJhLiIKClBhcmEgZWxsbywgcG9kZW1vcyBleHRyYWVyIHRvZG9zIGxvcyBlbmxhY2VzIGRlIGxhIGZpY2hhIHkgcXVlZGFybm9zCmNvbiBlbCBxdWUgc2UgbGxhbWUgX1Byb3llY3RvcyBwcmVzZW50YWRvc18KCmBgYHtyfQpVUkwgPC0gImh0dHBzOi8vcGFybGFtZW50by5ndWIudXkvY2FtYXJhc3ljb21pc2lvbmVzL2xlZ2lzbGFkb3Jlcy83NTkiCnJvdyA8LSByZWFkX2h0bWwoVVJMKSAlPiUKICAgIGh0bWxfbm9kZXMoImRpdi5wYW5lLWN1c3RvbSIpICU+JQogICAgaHRtbF9ub2RlcygibGkiKSAlPiUKICAgIGh0bWxfdGV4dCgpICU+JQogICAgYXMuZGF0YS5mcmFtZSgpICU+JSAgICAKICAgIG11dGF0ZShtYXRjaGVzPWdyZXBsKCJQcm95ZWN0b3MgcHJlc2VudGFkb3MiLCAuKSkgJT4lCiAgICBzdW1tYXJpemUocm93PXdoaWNoKG1hdGNoZXMpKSAlPiUKICAgIGFzLm51bWVyaWMKcm93CmBgYAoKQWhvcmEgcG9kZW1vcyBzZWxlY2Npb25hciBlbCBlbGVtZW50byBjb3JyZXNwb25kaWVudGUgZW4gbGEgdGFibGEKdXRpbGl6YW5kbyBlbCBjw7NkaWdvIGFudGVyaW9yCgpgYGB7cn0KVVJMIDwtICJodHRwczovL3BhcmxhbWVudG8uZ3ViLnV5L2NhbWFyYXN5Y29taXNpb25lcy9sZWdpc2xhZG9yZXMvNzU5IgpzcGFnZSA8LSByZWFkX2h0bWwoVVJMKSAlPiUKICAgIGh0bWxfbm9kZXMoImRpdi5wYW5lLWN1c3RvbSIpICU+JQogICAgaHRtbF9ub2RlcygibGkiKSAlPiUgICAgCiAgICAuW1tyb3ddXSAlPiUKICAgIGh0bWxfbm9kZXMoImEiKSAlPiUgICAgCiAgICBodG1sX2F0dHIoImhyZWYiKQpzcGFnZQpgYGAKClBvbmdhbW9zIGFob3JhIGVzdGUgY8OzZGlnbyBlbiB1bmEgZnVuY2nDs24gcGFyYSBxdWUgc2VhIG3DoXMgZsOhY2lsIGRlCm9wZXJhci4KCmBgYHtyfQpleHRyYWVyX2xpbmtfcHJveWVjdG9zIDwtIGZ1bmN0aW9uKHVybCkgewogICAgbGlzdHMgPC0gcmVhZF9odG1sKHVybCkgJT4lCiAgICAgICAgaHRtbF9ub2RlcygiZGl2LnBhbmUtY3VzdG9tIikgJT4lCiAgICAgICAgaHRtbF9ub2RlcygibGkiKQoKICAgIHJvdyA8LSBsaXN0cyAlPiUKICAgICAgICBodG1sX3RleHQoKSAlPiUKICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lICAgIAogICAgICAgIG11dGF0ZShtYXRjaGVzPWdyZXBsKCJQcm95ZWN0b3MgcHJlc2VudGFkb3MiLCAuKSkgJT4lCiAgICAgICAgc3VtbWFyaXplKHJvdz13aGljaChtYXRjaGVzKSkgJT4lCiAgICAgICAgYXMubnVtZXJpYwoKICAgIHJlcyA8LSBsaXN0cyAlPiUKICAgICAgICAuW1tyb3ddXSAlPiUKICAgICAgICBodG1sX25vZGVzKCJhIikgJT4lICAgIAogICAgICAgIGh0bWxfYXR0cigiaHJlZiIpCiAgICByZXR1cm4ocmVzKQp9CmBgYAoKTGEgZnVuY2nDs24gcGFyYSBleHRyYWVyIGxvcyBwcm95ZWN0b3MgZXMgc2ltaWxhciBhIGxhIGFudGVyaW9yCgpgYGB7cn0KZXh0cmFlcl9wcm95ZWN0b3MgPC0gZnVuY3Rpb24odXJsKSB7CiAgICByZXMgPC0gcmVhZF9odG1sKHVybCkgJT4lCiAgICAgICAgaHRtbF90YWJsZSgpICU+JQogICAgICAgIC5bWzFdXQogICAgcmV0dXJuKHJlcykKfQpgYGAKUGVybyBhaG9yYSBxdWUgdGVuZW1vcyBlc3RhIGZ1bmNpw7NuLCBwb2RlbW9zIHBlbnNhciBlbiB1c2FybGEgbcOhcwpnZW5lcmFsbWVudGUuIFBvciBlamVtcGxvLCB1bm8gZGUgbG9zIGVubGFjZXMgZGUgbGEgdGFibGEgZGVsIHBlcmZpbApkZWwgbWllbWJybyBub3MgcGVybWl0ZSBhY2NlZGVyIGEgaW5mb3JtYWNpw7NuIHNvYnJlIHN1IGFzaXN0ZW5jaWEgYQpjb21pc2lvbmVzLiAKCmBgYHtyfQpVUkwgPC0gImh0dHBzOi8vcGFybGFtZW50by5ndWIudXkvY2FtYXJhc3ljb21pc2lvbmVzL2xlZ2lzbGFkb3Jlcy83NTkvYXNpc3RlbmNpYS1hLWNvbWlzaW9uZXMiCnJvdyA8LSByZWFkX2h0bWwoVVJMKSAlPiUKICAgIGh0bWxfdGFibGUoKQpyb3cKYGBgCgpEaWdhbW9zIHF1ZSBub3MgaW50ZXJlc2Egw7puaWNhbWVudGUgbGEgaW5mb3JtYWNpw7NuIG3DoXMgZGVzYWdyZWdhZGEuClBvciBlamVtcGxvLCBsYSBzZWd1bmRhIHRhYmxhLiBFeHRyYWVyIGVzdGEgaW5mb3JtYWNpw7NuIGVzIG11eQpzZW5jaWxsbyB5IHBvZGVtb3MgcG9uZXJsbyBlbiB1bmEgZnVuY2nDs24KCmBgYHtyfQpleHRyYWVyX2FzaXN0ZW5jaWEgPC0gZnVuY3Rpb24odXJsKSB7CiAgICByZXMgPC0gcmVhZF9odG1sKHVybCkgJT4lCiAgICAgICAgaHRtbF90YWJsZSgpICU+JQogICAgICAgIC5bWzJdXQogICAgcmV0dXJuKHJlcykKfQpgYGAKClBlcm8sIGFob3JhIHF1ZSB0ZW5lbW9zIGBleHRyYWVyX2xpbmtfcHJveWVjdG9zYCwgcG9kZW1vcyBwZW5zYXIgZW4KZ2VuZXJhbGl6ZXIgZXN0YSBmdW5jacOzbiBwYXJhIGV4dHJhZXIgbm8gc29sbyBlbCBsaW5rIGEgbG9zIHByb3llY3RvcwpzaW5vIHRhbWJpw6luIGVsIGxpbmsgYSBlc3RhIHRhYmxhOgoKYGBge3J9CmV4dHJhZXJfbGluayA8LSBmdW5jdGlvbih1cmwsIHRleHRvKSB7CiAgICBsaXN0cyA8LSByZWFkX2h0bWwodXJsKSAlPiUKICAgICAgICBodG1sX25vZGVzKCJkaXYucGFuZS1jdXN0b20iKSAlPiUKICAgICAgICBodG1sX25vZGVzKCJsaSIpCgogICAgcm93IDwtIGxpc3RzICU+JQogICAgICAgIGh0bWxfdGV4dCgpICU+JQogICAgICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUgICAgCiAgICAgICAgbXV0YXRlKG1hdGNoZXM9Z3JlcGwodGV4dG8sIC4pKSAlPiUKICAgICAgICBzdW1tYXJpemUocm93PXdoaWNoKG1hdGNoZXMpKSAlPiUKICAgICAgICBhcy5udW1lcmljCgogICAgcmVzIDwtIGxpc3RzICU+JQogICAgICAgIC5bW3Jvd11dICU+JQogICAgICAgIGh0bWxfbm9kZXMoImEiKSAlPiUgICAgCiAgICAgICAgaHRtbF9hdHRyKCJocmVmIikKICAgIHJldHVybihyZXMpCn0KYGBgCgpBc8OtLCB0ZW5lbW9zIHVuYSBmdW5jacOzbiBtw6FzIGdlbmVyYWwgcXVlIHBvZGVtb3MgcmV1c2FyIHBhcmEgb2J0ZW5lcgpvdHJhcyBwaWV6YXMgZGUgaW5mb3JtYWNpw7NuIGRlIGxhIHDDoWdpbmEKCmBgYHtyfQpVUkwgPC0gImh0dHBzOi8vcGFybGFtZW50by5ndWIudXkvY2FtYXJhc3ljb21pc2lvbmVzL2xlZ2lzbGFkb3Jlcy83NTkiCmV4dHJhZXJfbGluayhVUkwsICJQcm95ZWN0b3MgcHJlc2VudGFkb3MiKQpleHRyYWVyX2xpbmsoVVJMLCAiQXNpc3RlbmNpYSBhIENvbWlzaW9uZXMiKQpgYGAKCllhIHNvbG8gbm9zIHF1ZWRhIHVuYSBjb3NhIHBvciBoYWNlci4gQWhvcmEgbmVjZXNpdGFtb3MgaXIgYSBsYQpwcmltZXJhIHDDoWdpbmEgcGFyYSBvYnRlbmVyIGVsIGxpc3RhZG8gZGUgbWllbWJyb3MgZGVsIHBhcmxhbWVudG8gcGFyYQpwb2RlciByZWN1cGVyYXIgdG9kYSBsYSBpbmZvcm1hY2nDs24gYW50ZXJpb3IuIEVzdGEgaW5mb3JtYWNpw7NuIGxhCnRlbmVtb3MgZGlzcG9uaWJsZSBlbiBsYSBzZWNjacOzbiBfUGxlbmFyaW9fIGRlIGxhIHDDoWdpbmEgd2ViLiBBcXXDrQpuZWNlc2l0YW1vcyBkb3MgY29zYXMuIFBvciB1bmEgcGFydGUgZWwgbm9tYnJlLiBQb3Igb3RyYSwgZW4gZW5sYWNlCnF1ZSBub3MgcGVybWl0YSBzZWd1aXIgbmF2ZWdhbmRvLiBMYXMgZG9zIGNvc2FzIGxhcyBwb2RlbW9zIGVuY29udHJhcgplbiBsYSB0YWJsYToKCmBgYHtyfQpVUkwgPC0gImh0dHBzOi8vcGFybGFtZW50by5ndWIudXkvY2FtYXJhc3ljb21pc2lvbmVzL2FzYW1ibGVhZ2VuZXJhbC9wbGVuYXJpby9pbnRlZ3JhY2lvbiIKCnNwYWdlIDwtIHJlYWRfaHRtbChVUkwpICU+JQogICAgaHRtbF9ub2Rlcygic3Bhbi5maWVsZC1jb250ZW50IikKc3BhZ2UKYGBgCgpJZ3VhbCBxdWUgYW50ZXMsIGRlIGFxdcOtIHNvbG8gbmVjZXNpdGFtb3MgdW4gw7puaWNvIHRpcG8gZGUgY29udGVuaWRvLAphbCBxdWUgcG9kZW1vcyByZWZlcmlybm9zIGEgdHJhdsOpcyBkZSBsYSBlc3RydWN0dXJhIGRlbCBET00KCmBgYHtyfQpVUkwgPC0gImh0dHBzOi8vcGFybGFtZW50by5ndWIudXkvY2FtYXJhc3ljb21pc2lvbmVzL2FzYW1ibGVhZ2VuZXJhbC9wbGVuYXJpby9pbnRlZ3JhY2lvbiIKCmxpbmtfIDwtIHJlYWRfaHRtbChVUkwpICU+JQogICAgaHRtbF9ub2RlcygiZGl2LnZpZXdzLWZpZWxkLVBzbi1Db2RpZ28tMSA+IHNwYW4gPiBhIikgJT4lCiAgICBodG1sX2F0dHIoImhyZWYiKQoKdGV4dF8gPC0gcmVhZF9odG1sKFVSTCkgJT4lCiAgICBodG1sX25vZGVzKCJkaXYudmlld3MtZmllbGQtUHNuLUNvZGlnby0xID4gc3BhbiA+IGEiKSAlPiUKICAgIGh0bWxfdGV4dCgpCmBgYAoKQWhvcmEgeWEgcG9kZW1vcyBwb25lciB0b2RhcyBsYXMgcGllemFzIGp1bnRhcy4gRWwgcHJvY2VzbyBlcyBlbApzaWd1aWVudGUuIFBhcmEgY2FkYSB1bm8gZGUgbG9zIG5vbWJyZXMgcXVlIGFjYWJhbW9zIGRlIGV4dHJhZXIsCnNlZ3VpcmVtb3MgZWwgZW5sYWNlIGFzb2NpYWRvLCBvYnRlbmRyZW1vcyBsYSBpbmZvcm1hY2nDs24gcmVsYXRpdmEgYWwKbWllbWJybywgY2FwdHVyYXJlbW9zIGxvcyBlbmxhY2VzIHF1ZSB0ZW5nYW1vcyBxdWUgc2VndWlyIGVuIGVzZSBwdW50bwp5IHBvciDDumx0aW1vIHJlY3VwZXJhcmVtb3MgbG9zIHByb3llY3RvcyB5IHN1IGFzaXN0ZW5jaWEgYSBjb21pc2lvbmVzLgpFcyBkZWNpciwgcG9uZHJlbW9zIHRvZGFzIGxhcyBwaWV6YXMgYW50ZXJpb3JlcyBlbiB1biBsb29wIHF1ZQplbXBlemFyw6EgY29uIGxhIGxpc3RhIGRlIG1pZW1icm9zIHF1ZSBhY2FiYW1vcyBkZSByZWN1cGVyYXI6CgpgYGB7cn0KbGlzdGFkbyA8LSBkYXRhLmZyYW1lKCJub21icmUiPXRleHRfLCAiZW5sYWNlIj1saW5rXykKbGlzdGFkbwpgYGAKCkxhcyBmdW5jaW9uZXMgYWhvcmEgbm9zIHJlc3VsdGFyw6FuIG11eSDDunRpbGVzIHBhcmEgcXVlIGPDs2RpZ28gc2VhIG3DoXMKbGVnaWJsZS4gUGFyYSBjYWRhIHVuYSBkZSBsYXMgZW50cmFkYXMgZW4gYGxpc3RhZG9gLCBlc3RvIGVzIGxvIHF1ZSBoYXJlbW9zOgoKYGBge3J9CmNhcHR1cmFyX2RhdG9zX2xlZ2lzbGFkb3IgPC0gZnVuY3Rpb24odXJsKSB7CiAgICBkZXNjcmlwY2lvbiA8LSBleHRyYWVyX2Rlc2NyaXBjaW9uKHVybCkKICAgIGNvbWlzaW9uZXMgPC0gZXh0cmFlcl9jb21pc2lvbmVzKHVybCkKICAgIGxpbmtfcHJveWVjdG9zIDwtIGV4dHJhZXJfbGluayh1cmwsICJQcm95ZWN0b3MgcHJlc2VudGFkb3MiKQogICAgcHJveWVjdG9zIDwtIGV4dHJhZXJfcHJveWVjdG9zKGxpbmtfcHJveWVjdG9zKQogICAgbGlua19hc2lzdGVuY2lhIDwtIGV4dHJhZXJfbGluayh1cmwsICJBc2lzdGVuY2lhIGEgQ29taXNpb25lcyIpCiAgICBhc2lzdGVuY2lhIDwtIGV4dHJhZXJfYXNpc3RlbmNpYShsaW5rX2FzaXN0ZW5jaWEpCiAgICByZXR1cm4obGlzdCgiZGVzY3JpcGNpb24iPWRlc2NyaXBjaW9uLAogICAgICAgICAgICAgICAgImNvbWlzaW9uZXMiPWNvbWlzaW9uZXMKICAgICAgICAgICAgICAgICJwcm95ZWN0b3MiPXByb3llY3RvcywKICAgICAgICAgICAgICAgICJhc2lzdGVuY2lhIj1hc2lzdGVuY2lhKSkKfQpgYGAKCkxvIMO6bmljbyBxdWUgdGVuZW1vcyBxdWUgdGVuZXIgZW4gY3VlbnRhIGVzIHF1ZSBsYXMgY2FycGV0YXMgZXN0w6FuCmRlZmluaWRhcyBwb3Igc3UgcG9zaWNpw7NuIHJlbGF0aXZhLiBFbiBjb25jcmV0bywgcGFyZWNlbiBlc3RhcgpkZWZpbmlkYXMgcG9yIHN1IHBvc2ljacOzbiByZWxhdGl2YSBhbCBUTEQuIEFzw60gcHVlczoKCmBgYHtyfQpCQVNFVVJMIDwtICJodHRwczovL3BhcmxhbWVudG8uZ3ViLnV5IgpmdWxsX3VybCA8LSBmdW5jdGlvbihmb2xkZXIpIHsKICAgIHJldHVybihwYXN0ZTAoQkFTRVVSTCwgZm9sZGVyKSkKfQpgYGAKCk51ZXN0cmEgZnVuY2nDs24gZXMgYWhvcmE6CgpgYGB7cn0KY2FwdHVyYXJfZGF0b3NfbGVnaXNsYWRvciA8LSBmdW5jdGlvbih1cmwpIHsKICAgIHVybCA8LSBmdWxsX3VybCh1cmwpCiAgICBkZXNjcmlwY2lvbiA8LSBleHRyYWVyX2Rlc2NyaXBjaW9uKHVybCkKICAgIGNvbWlzaW9uZXMgPC0gZXh0cmFlcl9jb21pc2lvbmVzKHVybCkKICAgIAogICAgbGlua19wcm95ZWN0b3MgPC0gZXh0cmFlcl9saW5rKHVybCwgIlByb3llY3RvcyBwcmVzZW50YWRvcyIpICAgIAogICAgbGlua19wcm95ZWN0b3MgPC0gZnVsbF91cmwobGlua19wcm95ZWN0b3MpCiAgICBwcm95ZWN0b3MgPC0gZXh0cmFlcl9wcm95ZWN0b3MobGlua19wcm95ZWN0b3MpCiAgICAKICAgIGxpbmtfYXNpc3RlbmNpYSA8LSBleHRyYWVyX2xpbmsodXJsLCAiQXNpc3RlbmNpYSBhIENvbWlzaW9uZXMiKQogICAgbGlua19hc2lzdGVuY2lhIDwtIGZ1bGxfdXJsKGxpbmtfYXNpc3RlbmNpYSkKICAgIGFzaXN0ZW5jaWEgPC0gZXh0cmFlcl9hc2lzdGVuY2lhKGxpbmtfYXNpc3RlbmNpYSkKICAgIHJldHVybihsaXN0KCJkZXNjcmlwY2lvbiI9ZGVzY3JpcGNpb24sCiAgICAgICAgICAgICAgICAiY29taXNpb25lcyI9Y29taXNpb25lcywKICAgICAgICAgICAgICAgICJwcm95ZWN0b3MiPXByb3llY3RvcywKICAgICAgICAgICAgICAgICJhc2lzdGVuY2lhIj1hc2lzdGVuY2lhKSkKfQpgYGAKClkgbG8gw7puaWNvIHF1ZSBub3MgcXVlZGEgZXMgYXBsaWNhcmxhIGEgY2FkYSB1bmEgZGUgbGFzIGZpbGFzIGRlIGxhCmxpc3RhIHF1ZSByZWN1cGVyYW1vcyBtw6FzIGFycmliYToKCmBgYHtyfQpmb3IgKGkgaW4gMTpucm93KGxpc3RhZG8pKSB7CiAgICBwcmludChpKQogICAgZGF0b3MgPC0gY2FwdHVyYXJfZGF0b3NfbGVnaXNsYWRvcihsaXN0YWRvW2ksICJlbmxhY2UiXSkgICAgCn0KYGBgCgrCv1BvciBxdcOpIGZhbGxhIGVuIGxhIHByaW1lcmEgbGluZWE/IFZheWFtb3MgYSBsYSBww6FnaW5hCmNvcnJlc3BvbmRpZW50ZQoKYGBge3J9Cmxpc3RhZG9bMSwgXQpgYGAKCkVuIGVsIGPDs2RpZ28gaGVtb3MgYXN1bWlkbyBxdWUgY2FkYSB1bmEgZGUgbGFzIHBpZXphcyBxdWUgcXVlcmVtb3MKZXh0cmFlciBleGlzdGVuLiBFbiBlc3RlIGNhc28sIHZlbW9zIHF1ZSBlc3RhIHBlcnNvbmEgbm8gcGVydGVuZWNlIGEKbmluZ3VuYSBjb21pc2nDs24uIE8sIG1lam9yIGRpY2hvLCBuaW5ndW5hIGNvbWlzacOzbiBhcGFyZWNlIGxpc3RhZGEuCkxvIHF1ZSBwb2RlbW9zIGhhY2VyIGVuIGVzdGUgY2FzbyBlcyAiY2FwdHVyYXIgdW5hIGV4Y2VwY2nDs24iIHBhcmEgcXVlCmVsIGPDs2RpZ28gbm8gZmFsbGU6CgpgYGB7cn0KdHJ5Q2F0Y2goZXh0cmFlcl9jb21pc2lvbmVzKGZ1bGxfdXJsKGxpc3RhZG9bMSwgImVubGFjZSJdKSksCiAgICAgICAgIGVycm9yPWZ1bmN0aW9uKGUpIHtyZXR1cm4oIk5vIGhheSBjb21pc2lvbmVzIil9KQpgYGAKCkxvIHF1ZSB2ZW1vcyBlcyBxdWUgYHRyeUNhdGNoYCBpbnRlbnRhcsOhIGVqZWN1dGFyCmBleHRyYWVyX2NvbWlzaW9uZXMuYCBFbiBlbCBjYXNvIGRlIHF1ZSBvYnRlbmdhIHVuIGVycm9yLCBlbnRvbmNlcwplamVjdXRhcsOhIGVuIHN1IGx1Z2FyIGxhIGZ1bmNpw7NuIHF1ZSBoZW1vcyBkZWZpbmlkbyB5IHF1ZSwgcG9yIGFob3JhLApzZSBsaW1pdGEgYSBkZXZvbHZlciBpbmZvcm1hY2nDs24gcXVlIHB1ZWRlIHNlciDDunRpbC4KCgpgYGB7cn0KY2FwdHVyYXJfZGF0b3NfbGVnaXNsYWRvciA8LSBmdW5jdGlvbih1cmwpIHsKICAgIHVybCA8LSBmdWxsX3VybCh1cmwpCiAgICBkZXNjcmlwY2lvbiA8LSBleHRyYWVyX2Rlc2NyaXBjaW9uKHVybCkKICAgIGNvbWlzaW9uZXMgPC0gdHJ5Q2F0Y2goZXh0cmFlcl9jb21pc2lvbmVzKHVybCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3I9ZnVuY3Rpb24oZSkgcmV0dXJuKCJOaW5ndW5hIGNvbWlzacOzbiBsaXN0YWRhIikpCiAgICAKICAgIGxpbmtfcHJveWVjdG9zIDwtIGV4dHJhZXJfbGluayh1cmwsICJQcm95ZWN0b3MgcHJlc2VudGFkb3MiKSAgICAKICAgIGxpbmtfcHJveWVjdG9zIDwtIGZ1bGxfdXJsKGxpbmtfcHJveWVjdG9zKQogICAgcHJveWVjdG9zIDwtIGV4dHJhZXJfcHJveWVjdG9zKGxpbmtfcHJveWVjdG9zKQogICAgCiAgICBsaW5rX2FzaXN0ZW5jaWEgPC0gZXh0cmFlcl9saW5rKHVybCwgIkFzaXN0ZW5jaWEgYSBDb21pc2lvbmVzIikKICAgIGxpbmtfYXNpc3RlbmNpYSA8LSBmdWxsX3VybChsaW5rX2FzaXN0ZW5jaWEpCiAgICBhc2lzdGVuY2lhIDwtIGV4dHJhZXJfYXNpc3RlbmNpYShsaW5rX2FzaXN0ZW5jaWEpCiAgICByZXR1cm4obGlzdCgiZGVzY3JpcGNpb24iPWRlc2NyaXBjaW9uLAogICAgICAgICAgICAgICAgImNvbWlzaW9uZXMiPWNvbWlzaW9uZXMsCiAgICAgICAgICAgICAgICAicHJveWVjdG9zIj1wcm95ZWN0b3MsCiAgICAgICAgICAgICAgICAiYXNpc3RlbmNpYSI9YXNpc3RlbmNpYSkpCn0KYGBgCgpQb3Igc3VwdWVzdG8sIHBvZHLDrWFtb3MgKHkgZGViZXLDrWFtb3MpIGFzZWd1cmFyIHF1ZSBjYWRhIHVuYSBkZQpudWVzdHJhcyBmdW5jaW9uZXMgdGVuZ2FuIHVuIGB0cnlDYXRjaGAgZGUgdGFsIG1vZG8gcXVlIGxhIGVqZWN1Y2nDs24Kbm8gbXVlcmEgZHVyYW50ZSBlbCBwcm9jZXNvIGRlIGNhcHR1cmEuIFBlcm8sIHBvciBhaG9yYSwgbm9zIHNpcnZlIGxvCnF1ZSB0ZW5lbW9zLiAKClNvbG8gbm9zIHF1ZWRhbiB1biBwYXIgZGUgY29zYXMuIFBvciB1bmEgcGFydGUsIGFzb2NpYXIgYSBjYWRhIGVudHJhZGEKZWwgbm9tYnJlIGRlbCBsZWdpc2xhZG9yLiBQYXJhIGVsbG8sIGxvIGFsbWFjZW5hcmVtb3MgdG9kbyBlbiB1bmEKbGlzdGEgZGUgbGEgcXVlIHlhIGNvbm9jZW1vcyBsYXMgZGltZXNpb25lcy4gUG9yIMO6bHRpbW8sIGVudHJlIGxsYW1hZGEKeSBsbGFtYWRhLCBkZWphcmVtb3MgdW4gdGllbXBvIGRlIGVzcGVyYSBwYXJhIG5vIHNvYnJlY2FyZ2FyIGVsIHNlcnZpZG9yLgoKYGBge3J9CnJlcyA8LSB2ZWN0b3IoJ2xpc3QnLCBucm93KGxpc3RhZG8pKQpmb3IgKGkgaW4gMTpucm93KGxpc3RhZG8pKSB7CiAgICBwcmludChpKQogICAgbm9tYnJlIDwtIGxpc3RhZG9baSwgIm5vbWJyZSJdCiAgICBkYXRvcyA8LSBjYXB0dXJhcl9kYXRvc19sZWdpc2xhZG9yKGxpc3RhZG9baSwgImVubGFjZSJdKQogICAgcmVzW1tpXV0gPC0gbGlzdCgibm9tYnJlIj1ub21icmUsICJkYXRvcyI9ZGF0b3MpCiAgICBTeXMuc2xlZXAoNSkKfQpgYGAKCg==