4. Analisis poblacional y territorial de las upz
4.2 ¿Cómo varían las tasas de denuncias según el grupo de población
y qué UPZ son atípicas?
Teniendo en cuenta el hallazgo anterior y generada la segmentación
por medio de K-Means se evidencia lo siguiente.
Estadisticas descriptivas segmentación generada
# K-means # modelo de clusters
set.seed(123)
modelo <- kmeans(
scale(upz["poblacion"]),
centers = 3,
nstart = 25
)
# Identificar el orden de los grupos según su población promedio
orden_grupos <- order(modelo$centers[, 1])
# Asignar las categorías a cada UPZ (los nombres de los grupos se definen en el chunk "estilo")
upz <- upz %>%
mutate(
grupo_poblacion = factor(
grupos[match(modelo$cluster, orden_grupos)],
levels = grupos
)
)
# Variables derivadas que se usan en 4.3, 4.4 y el mapa (se calculan una sola vez)
cols_hurto <- c("casos_hurto_personas", "casos_hurto_residencias", "casos_hurto_automotor",
"casos_hurto_bicicletas", "casos_hurto_comercio", "casos_hurto_celulares",
"casos_hurto_motos")
upz <- upz %>%
mutate(
casos_hurto_total = rowSums(across(all_of(cols_hurto))),
tasa_hurto_total = casos_hurto_total / poblacion * 10000,
pct_30_59 = 100 - (pct_0_14 + pct_15_29 + pct_60_mas) # lo que falta para llegar a 100 %
)
k_atipico <- 1.5 # límite atípico: Q3 + 1,5 × IQR
k_extremo <- 3 # límite extremo: Q3 + 3 × IQR
# Agregar n a las etiquetas (grupo_poblacion ya es factor ordenado desde el K-means)
upz <- upz %>%
group_by(grupo_poblacion) %>%
mutate(
# todo se calcula con las tasas reales (sin log)
q1_g = quantile(tasa_total, 0.25),
q3_g = quantile(tasa_total, 0.75),
iqr_g = q3_g - q1_g,
lim_atipico = q3_g + k_atipico * iqr_g,
lim_extremo = q3_g + k_extremo * iqr_g,
es_atipico = tasa_total > lim_atipico | tasa_total < q1_g - k_atipico * iqr_g,
es_extremo = tasa_total > lim_extremo | tasa_total < q1_g - k_extremo * iqr_g,
etiqueta = paste0(grupo_poblacion, "<br>(n = ", n(), ")")
) %>%
ungroup() %>%
mutate(
etiqueta = factor(etiqueta, levels = unique(etiqueta[order(grupo_poblacion)])),
pos = as.numeric(etiqueta),
texto = paste0(
"<b>", upz_nombre, "</b>",
"<br>Localidad: ", localidad,
"<br>Población: ", scales::comma(poblacion),
"<br>Tasa total: ", round(tasa_total, 1),
ifelse(es_extremo, "<br><i>Atípico extremo</i>", "<br><i>Atípico</i>")
)
)
# límites por grupo (para que los revises)
upz %>% distinct(grupo_poblacion, q1_g, q3_g, iqr_g, lim_atipico, lim_extremo)%>%
tabla_bonita(resaltar = c())
|
grupo_poblacion
|
q1_g
|
q3_g
|
iqr_g
|
lim_atipico
|
lim_extremo
|
|
Población baja
|
163.1525
|
479.1225
|
315.97
|
953.0775
|
1427.033
|
|
Población media
|
106.4600
|
192.2100
|
85.75
|
320.8350
|
449.460
|
|
Población alta
|
113.9600
|
138.5400
|
24.58
|
175.4100
|
212.280
|
rgba <- function(col, a) { m <- col2rgb(col); sprintf("rgba(%d,%d,%d,%.2f)", m[1], m[2], m[3], a) }
g <- plot_ly()
# 1. cajas: los bigotes terminan en el límite superior/inferior (no en el máximo)
# los puntos propios de la caja se ocultan; los atípicos que se ven son los de abajo
for (gr in levels(upz$grupo_poblacion)) {
d <- upz %>% filter(grupo_poblacion == gr)
g <- g %>% add_trace(
data = d, type = "box",
x = ~pos, y = ~tasa_total, width = 0.6,
fillcolor = rgba(colores[[gr]], 0.5),
line = list(color = "grey20", width = 1.5),
boxpoints = "outliers",
marker = list(opacity = 0, size = 1), # puntos de la caja invisibles
quartilemethod = "linear",
hoveron = "boxes", # el cuadro del mouse solo sale sobre la caja
hoverinfo = "y", showlegend = FALSE
)
}
# 2. atípicos (negros)
set.seed(123)
negros <- upz %>% filter(es_atipico, !es_extremo) %>%
mutate(xj = pos + runif(n(), -0.07, 0.07))
g <- g %>% add_trace(
data = negros, type = "scatter", mode = "markers",
x = ~xj, y = ~tasa_total,
marker = list(color = "grey20", size = 7, opacity = 0.75),
hovertext = ~texto, hoverinfo = "text", showlegend = FALSE
)
# 3. atípicos extremos (rojos)
rojos <- upz %>% filter(es_extremo) %>%
mutate(xj = pos + runif(n(), -0.07, 0.07))
g <- g %>% add_trace(
data = rojos, type = "scatter", mode = "markers",
x = ~xj, y = ~tasa_total,
marker = list(color = "red", size = 10, opacity = 0.9),
hovertext = ~texto, hoverinfo = "text", showlegend = FALSE
)
# 4. escala log solo para la vista
marcas <- c(50, 100, 200, 500, 1000, 2000, 5000, 10000)
etiquetas_x <- levels(upz$etiqueta)
g <- g %>%
layout(
title = list(
text = paste0("<b>", partir_titulo("Poblacion baja tiene los atipicos extremos, aumentando la tasa de delitos, Poblacion media y alta son homogéneas"), "</b>",
"<br><sup>Método K-means · puntos negros = atípicos (> Q3 + 1,5 × IQR) · ",
"puntos rojos = atípicos extremos (> Q3 + 3 × IQR) · pasa el cursor para ver los valores</sup>"),
x = 0.01, xanchor = "left"
),
xaxis = list(title = "Grupo de población", showgrid = FALSE, zeroline = FALSE,
tickvals = seq_along(etiquetas_x), ticktext = etiquetas_x,
range = c(0.4, length(etiquetas_x) + 0.6)),
yaxis = list(title = "Tasa total de delitos (escala log)", type = "log",
tickvals = marcas, ticktext = format(marcas, big.mark = ".", scientific = FALSE),
hoverformat = ".1f"),
hoverlabel = list(align = "left"),
margin = list(t = 125)
) %>%
estilo_plotly()
g
En las poblaciones media y alta no se identifican valores atípicos
que puedan afectar significativamente el análisis de correlación entre
el tamaño de la población y las tasas de delito. Sin embargo, en la
población baja se identifican varios valores atipicos extremos, como se
muestran en la tabla a continuacion.
Estos valores merecen especial atención, ya que podrían influir en
los resultados del análisis de correlación y afectar la interpretación
de la relación entre el tamaño de la población y las tasas de
delito.
4.3 ¿Qué delitos predominan en cada grupo y cambia su peso entre
grupos?
Tabla de UPZ con valores atípicos en las tasas de denuncias
por delitos.
# 1. El hurto total y su tasa (por 10.000 hab.) se calculan en el chunk "preparar_datos"
# 2. Funciones de apoyo ----
orden_delitos <- c("Hurto", "Lesiones", "Homicidios", "Violencia intrafamiliar")
resumir <- function(datos) {
datos %>%
summarise(
poblacion = sum(poblacion),
Hurto = sum(casos_hurto_total),
Lesiones = sum(casos_lesiones),
Homicidios = sum(casos_homicidios),
`Violencia intrafamiliar` = sum(casos_violencia_intrafamiliar)
) %>%
pivot_longer(all_of(orden_delitos), names_to = "delito", values_to = "casos") %>%
mutate(tasa = casos / poblacion * 10000)
}
expandir <- function(d, rango, n_pts = 40) {
p <- d[rep(seq_len(nrow(d)), each = n_pts), ]
p$x <- rep(seq(0, rango, length.out = n_pts), times = nrow(d))
p
}
# 3. Vista 1: población baja con vs sin atípicos extremos ----
# es_extremo ya viene calculado en el boxplot (Q3 + 3 × IQR, con tasas reales y por segmentación)
baja_con <- upz %>% filter(grupo_poblacion == "Población baja")
atipicos <- baja_con %>%
filter(es_extremo) %>%
arrange(desc(tasa_total))
n_atip <- nrow(atipicos)
baja_sin <- baja_con %>% filter(!(upz_cod %in% atipicos$upz_cod))
comp <- resumir(baja_con) %>%
select(delito, casos_con = casos, tasa_con = tasa) %>%
left_join(resumir(baja_sin) %>% select(delito, casos_sin = casos, tasa_sin = tasa), by = "delito") %>%
mutate(
dif_casos = ifelse(casos_con == 0, 0, casos_sin / casos_con - 1),
es_max = casos_con == max(casos_con),
etiqueta = ifelse(es_max, paste0(delito, " ★"), delito),
texto = paste0(
"<b>", delito, "</b>",
"<br>Con atípicos extremos: ", num(casos_con), " casos · tasa ", num(tasa_con, accuracy = 0.1),
"<br>Sin atípicos extremos: ", num(casos_sin), " casos · tasa ", num(tasa_sin, accuracy = 0.1),
"<br>Cambio en casos: ", pc(dif_casos)
)
) %>%
arrange(desc(casos_con))
rango_baja <- max(comp$casos_con) * 1.1
puntos_baja <- expandir(comp, rango_baja)
prom_con <- mean(baja_con$tasa_total); prom_sin <- mean(baja_sin$tasa_total)
med_con <- median(baja_con$tasa_total); med_sin <- median(baja_sin$tasa_total)
pct_pob_ext <- sum(atipicos$poblacion) / sum(baja_con$poblacion)
#3.1 tabla de atipicos extremos
tabla_extremos <- atipicos %>% # ya viene ordenada de mayor a menor tasa
transmute(
`#` = row_number(),
UPZ = upz_nombre,
Localidad = localidad,
Población = num(poblacion),
`Casos totales` = num(casos_total),
`Tasa total (por 10.000 hab.)` = num(tasa_total, accuracy = 0.1),
`Veces la mediana del grupo` = paste0(num(tasa_total / median(baja_con$tasa_total),
accuracy = 0.1), "x")
)
tabla_bonita(tabla_extremos)
|
#
|
UPZ
|
Localidad
|
Población
|
Casos totales
|
Tasa total (por 10.000 hab.)
|
Veces la mediana del grupo
|
|
1
|
El Mochuelo
|
Ciudad Bolívar
|
30
|
34
|
11.333,3
|
43,5x
|
|
2
|
Aeropuerto El Dorado
|
Fontibón
|
883
|
545
|
6.172,1
|
23,7x
|
|
3
|
Parque Simon Bolivar - Can
|
Teusaquillo
|
3.323
|
958
|
2.882,9
|
11,1x
|
|
4
|
Zona Industrial
|
Puente Aranda
|
6.666
|
1.573
|
2.359,7
|
9,1x
|
|
5
|
Las Nieves
|
Santa Fe
|
17.203
|
4.033
|
2.344,4
|
9,0x
|
|
6
|
Parque Salitre
|
Barrios Unidos
|
1.442
|
233
|
1.615,8
|
6,2x
|
|
7
|
Sagrado Corazon
|
Santa Fe
|
11.247
|
1.707
|
1.517,7
|
5,8x
|
# 4. Vistas 2 y 3: población media y alta ----
preparar <- function(g) {
resumir(upz %>% filter(grupo_poblacion == g)) %>%
mutate(
pct = casos / sum(casos),
es_max = casos == max(casos),
etiqueta = ifelse(es_max, paste0(delito, " ★"), delito),
texto = paste0("<b>", delito, "</b>",
"<br>Casos: ", num(casos), " (", pc(pct), " del total)",
"<br>Tasa: ", num(tasa, accuracy = 0.1), " por 10.000 hab.")
) %>%
arrange(desc(casos))
}
d_media <- preparar("Población media")
d_alta <- preparar("Población alta")
stopifnot(
sum(baja_con$casos_hurto_total) == comp$casos_con[comp$delito == "Hurto"],
sum(baja_sin$casos_hurto_total) == comp$casos_sin[comp$delito == "Hurto"],
nrow(baja_con) - nrow(baja_sin) == n_atip,
sum(d_media$casos) == sum(upz$casos_total[upz$grupo_poblacion == "Población media"]),
sum(d_alta$casos) == sum(upz$casos_total[upz$grupo_poblacion == "Población alta"])
)
rango_media <- max(d_media$casos) * 1.1
rango_alta <- max(d_alta$casos) * 1.1
puntos_media <- expandir(d_media, rango_media)
puntos_alta <- expandir(d_alta, rango_alta)
# 5. COMENTARIOS ----
# >>> AQUÍ ESCRIBES TU COMENTARIO PARA CADA VISTA (puede ser tan largo como quieras) <<<
# Se muestra completo, de lado a lado, solo cuando eliges esa vista en el menú.
# Déjalo "" si no quieres agregar nada.
mis_comentarios <- c(
"Población baja: con vs sin atípicos" = "Si se quitan los 7 atípicos extremos no genera un cambio de ranking entre delitos en la población baja, pero lo que sí cambia son las tasas de delitos y los promedios, en donde los casos de hurto bajan de tasa 288.5 a 261.5 por cada 10.000 habitantes, este cambio en la tasa pasa debido que estas UPZ tienen muy pocas personas y muchas más denuncias, en los otros delitos, se presenta variación pero no es tan considerable como el resultado de Hurto",
"Población media" = "Frente a población baja, la tasa de hurto baja a menos de la mitad (288,5 > 122,4). Además, aquí no hay valores atípicos: las UPZ se parecen bastante entre sí, con tasas totales que van de 72 a 312.",
"Población alta" = "Lo que sí cambia es el peso de la violencia intrafamiliar: pasa de 6,6 % en población baja a 19,9 % aquí, y su tasa 23,9 es la más alta de los tres grupos, siendos solo 5 UPZ."
)
mostrar_automatico <- TRUE # FALSE = muestra solo tus comentarios
vistas <- names(mis_comentarios)
# vista 1
top_b <- comp %>% filter(es_max)
nombres_atip <- paste0(atipicos$upz_nombre, " (", num(atipicos$poblacion), " hab.)", collapse = ", ")
pct_top_con <- top_b$casos_con / sum(comp$casos_con)
pct_top_sin <- top_b$casos_sin / sum(comp$casos_sin)
comentario_baja <- c(
if (mostrar_automatico) c(
paste0("<b>Atípicos extremos retirados (", n_atip, "):</b> ", nombres_atip,
". Juntos tienen solo el ", pc(pct_pob_ext), " de la población del grupo."),
paste0("<b>Efecto en los totales:</b> el ", tolower(top_b$delito), " pasa de ",
num(top_b$casos_con), " a ", num(top_b$casos_sin), " casos (", pc(top_b$dif_casos),
") y su tasa de ", num(top_b$tasa_con, accuracy = 0.1), " a ", num(top_b$tasa_sin, accuracy = 0.1), "."),
paste0("<b>Lo que no cambia:</b> el ", tolower(top_b$delito), " sigue siendo el delito más frecuente (",
pc(pct_top_con), " del total con atípicos y ", pc(pct_top_sin), " sin ellos)."),
paste0("<b>Promedio vs mediana de la tasa por UPZ:</b> el promedio pasa de ", num(prom_con, accuracy = 0.1),
" a ", num(prom_sin, accuracy = 0.1), " (", pc(prom_sin / prom_con - 1),
"), mientras la mediana pasa de ", num(med_con, accuracy = 0.1), " a ",
num(med_sin, accuracy = 0.1), " (", pc(med_sin / med_con - 1), ").")
),
if (nzchar(mis_comentarios[[vistas[1]]])) paste0("<b>Comentario:</b> ", mis_comentarios[[vistas[1]]])
)
# vistas 2 y 3
comentario_grupo <- function(g, d) {
top <- d[1, ]; seg <- d[2, ]; ult <- d[nrow(d), ]
n_upz <- sum(upz$grupo_poblacion == g)
c(
if (mostrar_automatico) c(
paste0("<b>Contexto:</b> ", n_upz, " UPZ y ", num(top$poblacion), " habitantes en este grupo."),
paste0("<b>Más frecuente:</b> ", tolower(top$delito), ", con ", num(top$casos),
" casos (", pc(top$pct), " del total) y una tasa de ", num(top$tasa, accuracy = 0.1),
" por cada 10.000 habitantes."),
paste0("<b>Le sigue:</b> ", tolower(seg$delito), ", con ", num(seg$casos),
" casos (", pc(seg$pct), ")."),
paste0("<b>Menos frecuente:</b> ", tolower(ult$delito), ", con ", num(ult$casos),
" casos (", pc(ult$pct), ").")
),
if (nzchar(mis_comentarios[[g]])) paste0("<b>Comentario:</b> ", mis_comentarios[[g]])
)
}
comentarios <- list(comentario_baja,
comentario_grupo("Población media", d_media),
comentario_grupo("Población alta", d_alta))
# 6. Gráfico ----
op <- function(d) ifelse(d$es_max, 1, 0.45) # el delito más frecuente queda resaltado
invisible_marker <- list(color = "rgba(0,0,0,0)", size = 12)
# vistas 2 y 3: barras + puntos invisibles para el cuadro del mouse (misma estructura, cambian datos y color)
barras_vista <- function(fig, d, puntos, nombre, color) {
fig %>%
add_trace(data = d, type = "bar", orientation = "h", name = nombre,
x = ~casos, y = ~etiqueta,
marker = list(color = color, opacity = op(d)),
hoverinfo = "skip", visible = FALSE, showlegend = FALSE) %>%
add_trace(data = puntos, type = "scatter", mode = "markers",
x = ~x, y = ~etiqueta, marker = invisible_marker,
hovertext = ~texto, hoverinfo = "text", visible = FALSE, showlegend = FALSE)
}
fig <- plot_ly() %>%
# --- vista 1 (trazas 1-3)
add_trace(data = comp, type = "bar", orientation = "h", name = "Con atípicos extremos",
x = ~casos_con, y = ~etiqueta,
marker = list(color = magenta, opacity = op(comp)),
hoverinfo = "skip", visible = TRUE, showlegend = TRUE) %>%
add_trace(data = comp, type = "bar", orientation = "h", name = "Sin atípicos extremos",
x = ~casos_sin, y = ~etiqueta,
marker = list(color = morado, opacity = op(comp)),
hoverinfo = "skip", visible = TRUE, showlegend = TRUE) %>%
add_trace(data = puntos_baja, type = "scatter", mode = "markers",
x = ~x, y = ~etiqueta, marker = invisible_marker,
hovertext = ~texto, hoverinfo = "text", visible = TRUE, showlegend = FALSE) %>%
# --- vista 2: población media (trazas 4-5) y vista 3: población alta (trazas 6-7)
barras_vista(d_media, puntos_media, "Población media", turquesa) %>%
barras_vista(d_alta, puntos_alta, "Población alta", morado)
titulos <- c(paste0("Delitos Población baja: con y sin los ", n_atip, " valores atípicos extremos, Hurto: Delito que predomina con y sin los ", n_atip, " atípicos extremos."),
"Delitos en población media, Hurto contiene el 80% de los casos.",
"Delitos en población alta, Hurto predomina, pero violencia intrafamiliar presenta 20% de los casos siendo solo 5 UPZ.")
subtitulos <- c("Cantidad de casos · ★ y color fuerte = el delito que más se presenta · pasa el cursor para ver el detalle",
"Cantidad de casos · ★ y color fuerte = el delito que más se presenta · pasa el cursor para ver casos y tasa",
"Cantidad de casos · ★ y color fuerte = el delito que más se presenta · pasa el cursor para ver casos y tasa")
rangos <- c(rango_baja, rango_media, rango_alta)
ordenes <- list(comp$etiqueta, d_media$etiqueta, d_alta$etiqueta)
titulo <- function(i) paste0("<b>", partir_titulo(titulos[i]), "</b><br><sup>", subtitulos[i], "</sup>")
# 7 trazas: 1-3 vista 1, 4-5 vista 2, 6-7 vista 3
trazas_por_vista <- c(3, 2, 2)
visibilidad <- function(i) rep(seq_along(vistas) == i, times = trazas_por_vista)
botones <- lapply(seq_along(vistas), function(i) {
list(
method = "update",
label = vistas[i],
args = list(
list(visible = visibilidad(i)),
list("title.text" = titulo(i),
"xaxis.range" = list(0, rangos[i]),
"yaxis.categoryarray" = rev(ordenes[[i]]))
)
)
})
fig <- fig %>%
layout(
height = 520,
title = titulo_plotly(titulo(1)),
xaxis = list(title = "Cantidad de casos", range = c(0, rangos[1]),
gridcolor = "#EEEBF0", zeroline = FALSE),
yaxis = list(title = "", categoryorder = "array",
categoryarray = rev(ordenes[[1]]), tickfont = list(size = 13)),
barmode = "group",
bargap = 0.3,
hovermode = "closest",
hoverdistance = 40,
hoverlabel = hover_plotly,
updatemenus = list(menu_plotly(botones)),
legend = list(orientation = "h", x = 1, xanchor = "right", y = 1.04, yanchor = "bottom"),
margin = list(t = 140, b = 70, l = 10, r = 30),
showlegend = TRUE
) %>%
estilo_plotly()
# 7. Caja de comentarios de lado a lado (debajo del gráfico) ----
# Se cambia sola según la segmentación elegida en el menú (js_comentarios está en el chunk "estilo")
fig <- htmlwidgets::onRender(
fig, js_comentarios,
data = list(comentarios = comentarios,
inicios = c(0, cumsum(trazas_por_vista)[-length(trazas_por_vista)])) # traza inicial de cada vista
)
fig
4.4 ¿Cómo se relaciona la composición por edad con las tasas de
delito y esa relación cambia según el grupo poblacional?
# 1. Variables: grupos de edad y delitos (hurto total y pct_30_59 se crean en el chunk "preparar_datos") ----
edades <- c(pct_0_14 = "0–14 años",
pct_15_29 = "15–29 años",
pct_30_59 = "30–59 años",
pct_60_mas = "60 años o más")
delitos_corr <- c(tasa_hurto_total = "Hurto",
tasa_lesiones = "Lesiones",
tasa_homicidios = "Homicidios",
tasa_violencia_intrafamiliar = "Violencia intrafamiliar")
# 2. Correlación de Spearman por segmentación ----
cor_spearman <- function(x, y) {
r <- tryCatch(suppressWarnings(cor.test(x, y, method = "spearman", exact = FALSE)),
error = function(e) NULL)
if (is.null(r)) c(rho = NA_real_, p = NA_real_)
else c(rho = unname(r$estimate), p = r$p.value)
}
fuerza <- function(r) {
a <- abs(r)
ifelse(is.na(r), "sin dato",
ifelse(a < 0.2, "muy débil",
ifelse(a < 0.4, "débil",
ifelse(a < 0.6, "moderada",
ifelse(a < 0.8, "fuerte", "muy fuerte")))))
}
correlaciones <- expand_grid(grupo = grupos, vd = names(delitos_corr), ve = names(edades)) %>%
mutate(
r = pmap(list(grupo, vd, ve), function(g, vd, ve) {
s <- upz[upz$grupo_poblacion == g, ]
cor_spearman(s[[ve]], s[[vd]])
}),
rho = map_dbl(r, ~ .x[["rho"]]),
p = map_dbl(r, ~ .x[["p"]]),
n = map_int(grupo, ~ sum(upz$grupo_poblacion == .x)),
delito = unname(delitos_corr[vd]),
edad = unname(edades[ve])
) %>%
select(grupo, delito, edad, rho, p, n) %>%
mutate(
signif = !is.na(p) & p < 0.05,
etiqueta_celda = case_when(
is.na(rho) ~ "",
signif ~ paste0("<b>", num(rho, accuracy = 0.01), "*</b>"),
TRUE ~ num(rho, accuracy = 0.01)
),
texto = paste0(
"<b>", delito, " · ", edad, "</b>",
"<br>Correlación (Spearman): ", num(rho, accuracy = 0.01),
"<br>Relación ", fuerza(rho), ifelse(rho >= 0, " positiva", " negativa"),
"<br>p-valor: ", ifelse(p < 0.001, "< 0,001", num(p, accuracy = 0.001)),
"<br>", ifelse(signif, "Estadísticamente significativa", "No significativa"),
"<br>UPZ analizadas: ", n
)
)
# 3. COMENTARIOS ----
# >>> AQUÍ ESCRIBES TU COMENTARIO PARA CADA POBLACIÓN (puede ser tan largo como quieras) <<<
# Se muestra completo, de lado a lado, solo cuando eliges esa población en el menú.
# Déjalo "" si no quieres agregar nada.
mis_comentarios <- c(
"Población baja" = "El hurto es menor donde hay más niños y niñas, mayor donde predomina la población de 30–59 años. La violencia intrafamiliar y lesiones suben con más población de 15–29 años y baja con más población de 30–59., estas asociaciones son generales de las UPZ en este grupo poblacional, pero no asegura que la edad genere el aumento en las denuncias.",
"Población media" = "es donde se generan las relaciones mas fuertes, donde si aumentan las personas de 60 años, las denuncias en hurto aumentan, por el contrario los homicidios y la violencia intrafamiliar baja en los grupos de igual o mas de 30 años, en contraparte, aumenta en los jovenes estos dos delitos. importante aclarar que Relación no es causalidad",
"Población alta" = "Solo tiene 5 UPZ, y esto puede generar cambios drasticos es las correlaciones con solo aumentar en una UPZ, por tal motivo es importante indicar en los jovenes el hurto es bajo y la violencia intrafamiliar cumple el mismo patron en el grupo de 30 - 59 años, aunque se debe validar mas al detalle estos datos, debido que no se cuenta con datos estables que puedan generalizar."
)
mostrar_automatico <- FALSE #ALSE = muestra solo tus comentarios
comentario_corr <- function(g) {
d <- correlaciones %>% filter(grupo == g, !is.na(rho))
n <- d$n[1]
pos <- d %>% slice_max(rho, n = 1, with_ties = FALSE)
neg <- d %>% slice_min(rho, n = 1, with_ties = FALSE)
c(
if (mostrar_automatico) c(
paste0("<b>Cómo leerlo:</b> ", n, " UPZ en este grupo. Cada celda es la correlación de Spearman ",
"entre el % de población de ese grupo de edad y la tasa del delito (por 10.000 habitantes). ",
"Va de -1 a 1; las celdas en negrita con * son estadísticamente significativas (p < 0,05)."),
if (pos$rho > 0) paste0("<b>Relación positiva más fuerte:</b> ", tolower(pos$delito), " con ", pos$edad,
" (", num(pos$rho, accuracy = 0.01), "): las UPZ con más población de esa edad tienden a tener tasas más altas."),
if (neg$rho < 0) paste0("<b>Relación negativa más fuerte:</b> ", tolower(neg$delito), " con ", neg$edad,
" (", num(neg$rho, accuracy = 0.01), "): las UPZ con más población de esa edad tienden a tener tasas más bajas."),
paste0("<b>Relaciones significativas:</b> ", sum(d$signif), " de ", nrow(d), "."),
if (n < 10) paste0("<b>Ojo:</b> con solo ", n, " UPZ las correlaciones son poco confiables ",
"(basta con que una UPZ cambie para que el resultado cambie). Tómalas solo como referencia."),
paste0("<b>Recuerda:</b> correlación no es causalidad; muestra que dos cosas suben o bajan juntas, ",
"no que un grupo de edad cause el delito. Además los cuatro grupos de edad suman 100 %, ",
"por eso 30–59 años suele moverse al revés que los demás.")
),
if (nzchar(mis_comentarios[[g]])) paste0("<b>Comentarios:</b> ", mis_comentarios[[g]])
)
}
comentarios <- lapply(grupos, comentario_corr)
# 4. Gráfico: mapa de calor con menú ----
a_matriz <- function(d, col) {
d %>%
select(delito, edad, all_of(col)) %>%
pivot_wider(names_from = edad, values_from = all_of(col)) %>%
arrange(match(delito, delitos_corr)) %>%
select(all_of(unname(edades))) %>%
as.matrix()
}
fig <- plot_ly()
for (i in seq_along(grupos)) {
d <- correlaciones %>% filter(grupo == grupos[i])
fig <- fig %>% add_trace(
type = "heatmap",
x = unname(edades), y = unname(delitos_corr),
z = a_matriz(d, "rho"), text = a_matriz(d, "texto"),
hoverinfo = "text", xgap = 3, ygap = 3,
zmin = -1, zmax = 1,
colorscale = list(c(0, turquesa), c(0.5, "#FFFFFF"), c(1, magenta)),
colorbar = list(title = "Correlación<br>(Spearman)", len = 0.8, thickness = 14,
tickvals = c(-1, -0.5, 0, 0.5, 1)),
showscale = TRUE, visible = (i == 1)
)
}
# número escrito dentro de cada celda (en negrita si es significativa)
anotaciones_celdas <- function(g) {
d <- correlaciones %>% filter(grupo == g)
lapply(seq_len(nrow(d)), function(k) list(
x = d$edad[k], y = d$delito[k], xref = "x", yref = "y",
text = d$etiqueta_celda[k], showarrow = FALSE,
font = list(size = 15,
color = ifelse(!is.na(d$rho[k]) && abs(d$rho[k]) > 0.55, "white", "#333333"))
))
}
titulo <- function(i) {
paste0("<b>", partir_titulo("En poblaciones jóvenes la violencia intrafamiliar tiende a subir y el hurto a bajar; esto aplica a población baja y media"), "</b>",
"<br><sup>", grupos[i], " · Correlación de Spearman entre el % de población por edad y la tasa de delitos de cada UPZ",
" · * = significativa (p < 0,05)</sup>")
}
botones <- lapply(seq_along(grupos), function(i) {
list(
method = "update",
label = grupos[i],
args = list(
list(visible = seq_along(grupos) == i),
list("title.text" = titulo(i),
annotations = anotaciones_celdas(grupos[i]))
)
)
})
fig <- fig %>%
layout(
height = 520,
title = titulo_plotly(titulo(1)),
xaxis = list(title = "Grupo de edad (% de habitantes de la UPZ)", side = "bottom"),
yaxis = list(title = "", autorange = "reversed", tickfont = list(size = 13), automargin = TRUE),
annotations = anotaciones_celdas(grupos[1]),
hoverlabel = hover_plotly,
updatemenus = list(menu_plotly(botones)),
margin = list(t = 140, b = 80, l = 10, r = 30)
) %>%
estilo_plotly()
# 5. Caja de comentarios de lado a lado (debajo del gráfico) ----
# (js_comentarios está en el chunk "estilo")
fig <- htmlwidgets::onRender(
fig, js_comentarios,
data = list(comentarios = comentarios, inicios = seq_along(grupos) - 1) # una traza por grupo
)
fig
Conclusión de correlación
En esta seccion se mantienen los casos atipicos extremos en el
analisis, aunque son asociaciones entre UPZ, no explican por qué ocurren
y no significan que la edad cause el delito, para mayor analisis seria
importante tener la data por denuncias realizadas por edad, junto con el
delito a denunciar, de esta manera se podria comprobar positiva o
negativamente la correlación.
4.5 ¿Dónde se ubican las UPZ con mayores tasas y cómo se ven las
segmentaciones en el territorio?
# mapa bogota upz
ruta_shp <- file.path(ruta_input, "shp", "upz_bogota.shp")
upz_shp <- st_read(ruta_shp, quiet = TRUE)
loc_shp <- st_read(ruta_shp, quiet = TRUE) # recortadas al área urbana
# 1. Geometría + datos de cada UPZ ----
mapa <- upz_shp %>%
select(upz_cod) %>%
left_join(
upz %>% select(upz_cod, upz_nombre, localidad, grupo_poblacion, poblacion,
casos_total, tasa_total,
casos_hurto_total, tasa_hurto_total,
casos_lesiones, tasa_lesiones,
casos_homicidios, tasa_homicidios,
casos_violencia_intrafamiliar, tasa_violencia_intrafamiliar),
by = "upz_cod"
) %>%
mutate(id_upz = as.character(upz_cod))
stopifnot("Hay UPZ sin segmentación: corre primero el chunk de K-means" = !anyNA(mapa$grupo_poblacion))
# se aligera la geometría para que el HTML pese poco
mapa <- mapa %>%
st_make_valid() %>%
st_transform(3116) %>%
st_simplify(dTolerance = 30, preserveTopology = TRUE) %>%
st_transform(4326)
# límites exactos de Bogotá para que el mapa llene el espacio
bb <- st_bbox(mapa)
pad <- 0.005
rango_lon <- c(unname(bb["xmin"]) - pad, unname(bb["xmax"]) + pad)
rango_lat <- c(unname(bb["ymin"]) - pad, unname(bb["ymax"]) + pad)
a_geojson <- function(x) {
tmp <- tempfile(fileext = ".geojson")
st_write(x[, "id_upz"], tmp, driver = "GeoJSON", quiet = TRUE)
out <- read_json(tmp)
unlink(tmp)
out
}
# 2. Segmentación y delitos ----
n_grupo <- sapply(grupos, function(g) sum(mapa$grupo_poblacion == g))
delitos_mapa <- list(
"Total de delitos" = c("casos_total", "tasa_total"),
"Hurto" = c("casos_hurto_total", "tasa_hurto_total"),
"Lesiones" = c("casos_lesiones", "tasa_lesiones"),
"Homicidios" = c("casos_homicidios", "tasa_homicidios"),
"Violencia intrafamiliar" = c("casos_violencia_intrafamiliar", "tasa_violencia_intrafamiliar")
)
crear_texto <- function(d, delito) {
v <- delitos_mapa[[delito]]
paste0(
"<b>", d$upz_nombre, "</b>",
"<br>Localidad: ", d$localidad,
"<br>Segmentación: ", d$grupo_poblacion,
"<br>Población: ", num(d$poblacion),
"<br>Total de delitos: ", num(d$casos_total), " casos · tasa ", num(d$tasa_total, accuracy = 0.1),
if (delito != "Total de delitos")
paste0("<br><b>", delito, ":</b> ", num(d[[v[1]]]), " casos · tasa ", num(d[[v[2]]], accuracy = 0.1))
else ""
)
}
datos_g <- lapply(grupos, function(g) mapa %>% filter(grupo_poblacion == g))
textos <- lapply(names(delitos_mapa), function(dl) {
lapply(datos_g, function(dg) crear_texto(st_drop_geometry(dg), dl))
})
names(textos) <- names(delitos_mapa)
# 3. Mapa: base gris (todas las UPZ) + una capa por segmentación ----
alto_mapa <- 950
fig <- plot_ly(height = alto_mapa) %>%
add_trace(
type = "choropleth", name = "base", # <<< NUEVO: nombre de la traza
geojson = a_geojson(mapa), featureidkey = "properties.id_upz",
locations = mapa$id_upz, z = rep(1, nrow(mapa)), zmin = 0, zmax = 1,
colorscale = list(c(0, gris), c(1, gris)), showscale = FALSE,
marker = list(line = list(color = "white", width = 0.5)),
hoverinfo = "skip"
)
for (i in seq_along(grupos)) {
dg <- datos_g[[i]]
fig <- fig %>% add_trace(
type = "choropleth", name = grupos[i], # <<< NUEVO: la tabla lee este nombre
geojson = a_geojson(dg), featureidkey = "properties.id_upz",
locations = dg$id_upz, z = rep(1, nrow(dg)), zmin = 0, zmax = 1,
colorscale = list(c(0, colores[[grupos[i]]]), c(1, colores[[grupos[i]]])), showscale = FALSE,
marker = list(line = list(color = "white", width = 0.8)),
text = textos[["Total de delitos"]][[i]], hoverinfo = "text"
)
}
# 4. Selectores ----
botones_seg <- lapply(c("Todas", grupos), function(s) {
list(method = "restyle",
label = if (s == "Todas") paste0("Todas (", sum(n_grupo), " UPZ)") else paste0(s, " (", n_grupo[[s]], " UPZ)"),
args = list(list(visible = (grupos == s) | (s == "Todas")), c(1, 2, 3)))
})
botones_delito <- lapply(names(delitos_mapa), function(dl) {
list(method = "restyle", label = dl,
args = list(list(text = lapply(textos[[dl]], as.list)), c(1, 2, 3)))
})
leyenda <- paste0(
paste0("<span style='color:", colores, "'>■</span> ", grupos, " (", n_grupo, ")", collapse = " "),
" <span style='color:#BDBAC4'>■</span> UPZ no seleccionadas"
)
# 5. Diseño del mapa ----
fig <- fig %>%
layout(
height = alto_mapa,
title = titulo_plotly(
paste0("<b>Mapa de las UPZ de Bogotá según segmentación poblacional</b>",
"<br><sup>Elige la segmentación y el delito · pasa el cursor sobre cada UPZ · ",
"tasas por cada 10.000 habitantes</sup>"),
xref = "container"
),
geo = list(
projection = list(type = "mercator"),
lonaxis = list(range = rango_lon),
lataxis = list(range = rango_lat),
visible = FALSE, bgcolor = "rgba(0,0,0,0)",
domain = list(x = c(0, 1), y = c(0, 1))
),
hoverlabel = hover_plotly,
updatemenus = list(
menu_plotly(botones_seg, x = 0, y = 1.02),
menu_plotly(botones_delito, x = 0.42, y = 1.02)
),
annotations = list(
list(text = "Segmentación poblacional", x = 0, y = 1.10, xref = "paper", yref = "paper",
xanchor = "left", yanchor = "bottom", showarrow = FALSE, font = list(size = 12, color = "grey40")),
list(text = "Delito (en el cuadro del mouse)", x = 0.42, y = 1.10, xref = "paper", yref = "paper",
xanchor = "left", yanchor = "bottom", showarrow = FALSE, font = list(size = 12, color = "grey40")),
list(text = leyenda, x = 0, y = -0.02, xref = "paper", yref = "paper",
xanchor = "left", yanchor = "top", showarrow = FALSE, font = list(size = 13, color = "grey25"))
),
margin = list(t = 140, b = 50, l = 0, r = 0)
) %>%
estilo_plotly()
# 6. datos del ranking ----
top_n <- 15 # cuántas UPZ muestra la tabla
# es_extremo ya viene calculado en el boxplot (Q3 + 3 × IQR, tasas reales, por segmentación)
ranking_datos <- upz %>%
transmute(
upz_nombre,
grupo_poblacion = as.character(grupo_poblacion),
area_km2, poblacion,
tasa_hurto_total, tasa_lesiones, tasa_homicidios, tasa_violencia_intrafamiliar, tasa_total,
es_extremo
)
cfg <- list(
morado = morado, magenta = magenta, gris = gris,
top_n = top_n, grupos = grupos, colores = as.list(colores),
n_ext = sum(ranking_datos$es_extremo),
n_cero_hom = sum(ranking_datos$tasa_homicidios == 0)
)
# 7. tabla ranking conectada (en los dos sentidos) con el selector de segmentación del mapa ----
js_ranking <- r"---(
function(el, x, datos) {
var reg = JSON.parse(datos.registros);
var cfg = JSON.parse(datos.cfg);
function f(v, dec) {
return new Intl.NumberFormat('es-CO', {minimumFractionDigits: dec, maximumFractionDigits: dec}).format(v);
}
function esc(s) {
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
}
var cols = [
{k: 'upz_nombre', t: 'UPZ', num: false},
{k: 'grupo_poblacion', t: 'Segmentación', num: false},
{k: 'area_km2', t: 'Área (km²)', num: true, d: 2},
{k: 'poblacion', t: 'Población', num: true, d: 0},
{k: 'tasa_hurto_total', t: 'Tasa hurto', num: true, d: 1},
{k: 'tasa_lesiones', t: 'Tasa lesiones', num: true, d: 1},
{k: 'tasa_homicidios', t: 'Tasa homicidios', num: true, d: 2},
{k: 'tasa_violencia_intrafamiliar', t: 'Tasa violencia intrafamiliar', num: true, d: 1},
{k: 'tasa_total', t: 'Tasa total', num: true, d: 1}
];
var ordenables = cols.filter(function(c) { return c.num; });
var estado = {k: 'tasa_total', dir: 'desc', firma: null};
// estilo (mismo look que tabla_bonita: encabezado morado, fila resaltada rosada, hover)
var css = document.createElement('style');
css.textContent =
'.rk-cont{margin-top:14px;width:100%;box-sizing:border-box;padding:0 10px;font-family:inherit;color:#333}' +
'.rk-titulo{font-size:18px;color:' + cfg.morado + ';line-height:1.4;margin-bottom:8px}' +
'.rk-titulo span{font-size:13px;color:#555}' +
'.rk-ctrl{display:flex;gap:16px;flex-wrap:wrap;align-items:center;margin-bottom:10px;font-size:13px;color:#555}' +
'.rk-ctrl select{padding:5px 8px;border:1px solid ' + cfg.gris + ';border-radius:4px;color:' + cfg.morado + ';background:#fff;font-size:13px}' +
'.rk-wrap{overflow-x:auto}' +
'.rk-tabla{width:100%;border-collapse:collapse;font-size:14px}' +
'.rk-tabla th{background:' + cfg.morado + ';color:#fff;font-weight:bold;padding:7px 9px;text-align:left;white-space:nowrap}' +
'.rk-tabla th.rk-num{text-align:right}' +
'.rk-tabla th.rk-ord{cursor:pointer}' +
'.rk-tabla th.rk-ord:hover{filter:brightness(1.25)}' +
'.rk-tabla td{padding:6px 9px;border-top:1px solid #ddd}' +
'.rk-tabla td.rk-num{text-align:right;white-space:nowrap}' +
'.rk-tabla td.rk-pos{text-align:center;font-weight:bold;color:' + cfg.morado + ';width:42px}' +
'.rk-tabla tbody tr:hover{background:#f5f5f5}' +
'.rk-tabla tbody tr.rk-primero{background:#F6EEF2;font-weight:bold;color:' + cfg.morado + '}' +
'.rk-tabla tbody tr.rk-ext td{border-top:2px solid ' + cfg.magenta + ';border-bottom:2px solid ' + cfg.magenta + '}' +
'.rk-tabla tbody tr.rk-ext td:first-child{border-left:2px solid ' + cfg.magenta + '}' +
'.rk-tabla tbody tr.rk-ext td:last-child{border-right:2px solid ' + cfg.magenta + '}' +
'.rk-nota{margin-top:12px;font-size:13px;line-height:1.5;color:#444;border-top:2px solid ' + cfg.gris + ';padding-top:10px}' +
'.rk-nota ul{margin:6px 0 0 0;padding-left:20px}' +
'.rk-nota li{margin-bottom:4px}' +
'.rk-sw{display:inline-block;width:22px;height:11px;vertical-align:middle;margin-right:4px;box-sizing:border-box}' +
'.rk-sw-ext{border:2px solid ' + cfg.magenta + '}' +
'.rk-sw-prim{background:#F6EEF2;border:1px solid ' + cfg.gris + '}';
document.head.appendChild(css);
// estructura
var cont = document.createElement('div');
cont.className = 'rk-cont';
el.parentNode.insertBefore(cont, el.nextSibling);
var titulo = document.createElement('div');
titulo.className = 'rk-titulo';
cont.appendChild(titulo);
var ctrl = document.createElement('div');
ctrl.className = 'rk-ctrl';
// <<< NUEVO: cuántas UPZ tiene cada segmentación (para el selector)
var conteo = {};
reg.forEach(function(r) { conteo[r.grupo_poblacion] = (conteo[r.grupo_poblacion] || 0) + 1; });
ctrl.innerHTML =
// <<< NUEVO: selector de segmentación dentro de la tabla
'<label>Segmentación <select class="rk-sel-seg">' +
'<option value="Todas">Todas (' + reg.length + ' UPZ)</option>' +
cfg.grupos.map(function(g) { return '<option value="' + g + '">' + g + ' (' + (conteo[g] || 0) + ' UPZ)</option>'; }).join('') +
'</select></label>' +
'<label>Ordenar por <select class="rk-sel-k">' +
ordenables.map(function(c) { return '<option value="' + c.k + '">' + c.t + '</option>'; }).join('') +
'</select></label>' +
'<label>Orden <select class="rk-sel-dir">' +
'<option value="desc">De mayor a menor</option><option value="asc">De menor a mayor</option>' +
'</select></label>' +
'<span>También puedes hacer clic en el encabezado de una columna.</span>';
cont.appendChild(ctrl);
var selSeg = ctrl.querySelector('.rk-sel-seg'); // <<< NUEVO
var selK = ctrl.querySelector('.rk-sel-k');
var selDir = ctrl.querySelector('.rk-sel-dir');
selK.value = estado.k;
selDir.value = estado.dir;
var wrap = document.createElement('div');
wrap.className = 'rk-wrap';
cont.appendChild(wrap);
var nota = document.createElement('div');
nota.className = 'rk-nota';
nota.innerHTML =
'<b>Cómo leer esta tabla</b><ul>' +
'<li><span class="rk-sw rk-sw-ext"></span><b>Borde magenta:</b> atípico extremo, es decir, una UPZ cuya tasa total supera Q3 + 3 × IQR dentro de la segmentación poblacional generada (hoy son ' + cfg.n_ext + '). Se resalta siempre que aparezca en el ranking.</li>' +
'<li>Las tasas de delitos son por cada 10.000 habitantes.</li></ul>';
cont.appendChild(nota);
// segmentaciones visibles en el mapa
function seleccion() {
var s = [];
(el.data || []).forEach(function(t) {
if (cfg.grupos.indexOf(t.name) >= 0 && t.visible !== false && t.visible !== 'legendonly') s.push(t.name);
});
return s;
}
// <<< NUEVO: tabla -> mapa: muestra solo la segmentación elegida y actualiza el menú del mapa
function aplicarAlMapa(valor) {
if (typeof Plotly === 'undefined') return;
var vis = cfg.grupos.map(function(g) { return valor === 'Todas' || g === valor; });
var idx = cfg.grupos.map(function(g) {
for (var i = 0; i < el.data.length; i++) { if (el.data[i].name === g) return i; }
return -1;
});
if (idx.indexOf(-1) >= 0) return;
var pos = (valor === 'Todas') ? 0 : cfg.grupos.indexOf(valor) + 1;
Plotly.restyle(el, {visible: vis}, idx).then(function() {
return Plotly.relayout(el, {'updatemenus[0].active': pos});
});
}
function render(forzar) {
var sel = seleccion();
selSeg.value = (sel.length === 1) ? sel[0] : 'Todas'; // <<< NUEVO: mapa -> tabla (sincroniza el selector)
var firma = sel.join('|') + '#' + estado.k + '#' + estado.dir;
if (!forzar && firma === estado.firma) return;
estado.firma = firma;
var d = reg.filter(function(r) { return sel.indexOf(r.grupo_poblacion) >= 0; });
var sgn = estado.dir === 'desc' ? -1 : 1;
d.sort(function(a, b) {
var dif = (a[estado.k] - b[estado.k]) * sgn;
return dif !== 0 ? dif : a.upz_nombre.localeCompare(b.upz_nombre, 'es');
});
var top = d.slice(0, cfg.top_n);
var col = ordenables.filter(function(c) { return c.k === estado.k; })[0];
var txtSel = (sel.length === cfg.grupos.length) ? 'todas las segmentaciones'
: (sel.length ? sel.join(', ').toLowerCase() : 'ninguna segmentación');
titulo.innerHTML = '<b>Ranking de UPZ</b> · Top ' + top.length + ' de ' + d.length + ' UPZ · ' + txtSel +
'<br><span>Ordenado por <b>' + col.t.toLowerCase() + '</b> ' +
(estado.dir === 'desc' ? '(de mayor a menor)' : '(de menor a mayor)') + '</span>';
var h = '<table class="rk-tabla"><thead><tr><th class="rk-num" style="text-align:center">#</th>';
cols.forEach(function(c) {
var flecha = (c.k === estado.k) ? (estado.dir === 'desc' ? ' ▼' : ' ▲') : '';
h += '<th class="' + (c.num ? 'rk-num rk-ord' : '') + '"' + (c.num ? ' data-k="' + c.k + '"' : '') + '>' + c.t + flecha + '</th>';
});
h += '</tr></thead><tbody>';
top.forEach(function(r, i) {
var clases = (i === 0 ? 'rk-primero ' : '') + (r.es_extremo ? 'rk-ext' : '');
h += '<tr class="' + clases + '"><td class="rk-pos">' + (i + 1) + '</td>';
cols.forEach(function(c) {
if (c.k === 'grupo_poblacion') {
h += '<td><span style="color:' + (cfg.colores[r.grupo_poblacion] || '#999') + '">■</span> ' + esc(r.grupo_poblacion) + '</td>';
} else if (c.num) {
h += '<td class="rk-num">' + f(r[c.k], c.d) + '</td>';
} else {
h += '<td>' + esc(r[c.k]) + '</td>';
}
});
h += '</tr>';
});
h += '</tbody></table>';
wrap.innerHTML = h;
}
// <<< NUEVO: al cambiar la segmentación en la tabla, se mueve el mapa
selSeg.addEventListener('change', function() { aplicarAlMapa(selSeg.value); render(true); });
selK.addEventListener('change', function() { estado.k = selK.value; render(true); });
selDir.addEventListener('change', function() { estado.dir = selDir.value; render(true); });
wrap.addEventListener('click', function(e) {
var th = e.target.closest ? e.target.closest('th.rk-ord') : null;
if (!th) return;
var k = th.getAttribute('data-k');
if (estado.k === k) { estado.dir = (estado.dir === 'desc' ? 'asc' : 'desc'); }
else { estado.k = k; estado.dir = 'desc'; }
selK.value = estado.k;
selDir.value = estado.dir;
render(true);
});
render(true);
el.on('plotly_afterplot', function() { render(false); });
}
)---"
fig <- htmlwidgets::onRender(
fig, js_ranking,
data = list(
registros = as.character(toJSON(ranking_datos, dataframe = "rows", auto_unbox = TRUE, digits = NA)),
cfg = as.character(toJSON(cfg, auto_unbox = TRUE))
)
)
fig
# Cifras de la sección de Hallazgos: se calculan aquí y el texto las llama con `r ...`,
# así el informe se actualiza solo si cambian los datos, la semilla o los umbrales.
pct <- function(x) paste0(num(100 * x, accuracy = 0.1), " %")
# Correlación (Spearman) de la matriz del apartado 4.4, con el signo menos tipográfico
rho_txt <- function(g, d_, e_) {
r <- correlaciones$rho[correlaciones$grupo == g & correlaciones$delito == d_ & correlaciones$edad == e_]
sub("-", "−", num(r, accuracy = 0.01))
}
# Por grupo: cantidad de UPZ y peso de cada delito (sobre hurto + lesiones + homicidios + violencia intrafamiliar)
por_grupo <- upz %>%
group_by(grupo_poblacion) %>%
summarise(
n = n(),
delitos4 = sum(casos_hurto_total + casos_lesiones + casos_homicidios + casos_violencia_intrafamiliar),
pct_hurto = sum(casos_hurto_total) / delitos4,
pct_vif = sum(casos_violencia_intrafamiliar) / delitos4,
.groups = "drop"
) %>%
split(.$grupo_poblacion)
g_baja <- por_grupo[["Población baja"]]
g_media <- por_grupo[["Población media"]]
g_alta <- por_grupo[["Población alta"]]
# Ranking: composición del top de mayores y de menores tasas totales
top_may <- upz %>% slice_max(tasa_total, n = top_n, with_ties = FALSE)
top_men <- upz %>% slice_min(tasa_total, n = top_n, with_ties = FALSE)
n_may_baja <- sum(top_may$grupo_poblacion == "Población baja")
n_men_media <- sum(top_men$grupo_poblacion == "Población media")
# Alerta: si cambian los datos y alguna afirmación cualitativa del texto deja de ser cierta, el tejido avisa
stopifnot(
"Los atípicos extremos ya no están solo en población baja: revisa el texto de Hallazgos" =
all(upz$grupo_poblacion[upz$es_extremo] == "Población baja"),
"El top de mayores tasas ya no es todo de población baja: revisa el texto de Hallazgos" =
n_may_baja == top_n,
"En el top de menores tasas ya no predomina población media: revisa el texto de Hallazgos" =
n_men_media == max(table(top_men$grupo_poblacion))
)
LS0tDQp0aXRsZTogIkJvZ290w6EgYmFqbyBsYSBsdXBhOiBwb2JsYWNpw7NuLCB0ZXJyaXRvcmlvIHkgZGVudW5jaWFzIGVuIGxhcyBVUFoiDQpzdWJ0aXRsZTogIsK/Q8OzbW8gc2UgcmVsYWNpb25hbiBsYSBjb21wb3NpY2nDs24gcG9yIHJhbmdvcyBkZSBlZGFkIGRlIGxhIHBvYmxhY2nDs24geSBlbCDDoXJlYSB0ZXJyaXRvcmlhbCBkZSBsYXMgVVBaIGNvbiBsYSBjYW50aWRhZCB5IGxhIHRhc2EgZGUgZGVudW5jaWFzIHBvciBkZWxpdG8gZW4gQm9nb3TDoT8iDQphdXRob3I6ICJKZWlzb24gRGlheiAtIFJvYmluc29uIEdpcmFsZG8iDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCksICclZCBkZSAlQiBkZSAlWScpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0aGVtZTogZmxhdGx5DQogICAgZGZfcHJpbnQ6IGthYmxlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZSAgICMgYWdyZWdhIHVuIGJvdMOzbiBwYXJhIGRlc2NhcmdhciBlbCAuUm1kIG9yaWdpbmFsIC0tIHJlZnVlcnphIGxhIHJlcHJvZHVjaWJpbGlkYWQNCi0tLQ0KDQpgYGB7PWh0bWx9DQo8c3R5bGU+DQovKiBUaXBvZ3JhZsOtYSBlZGl0b3JpYWwgZGVsIGN1cnNvICAqLw0KQGltcG9ydCB1cmwoJ2h0dHBzOi8vZm9udHMuZ29vZ2xlYXBpcy5jb20vY3NzMj9mYW1pbHk9RmlyYStTYW5zOndnaHRANDAwOzYwMDs3MDAmZGlzcGxheT1zd2FwJyk7DQoNCmJvZHkgeyBmb250LWZhbWlseTogJ0ZpcmEgU2FucycsIC1hcHBsZS1zeXN0ZW0sIEJsaW5rTWFjU3lzdGVtRm9udCwgc2Fucy1zZXJpZjsgY29sb3I6ICMyQjI1MzA7IH0NCmgxLCBoMiwgaDMgeyBmb250LWZhbWlseTogJ0ZpcmEgU2FucycsIHNhbnMtc2VyaWY7IGNvbG9yOiAjMzUxQTRGOyB9DQpoMSB7IGJvcmRlci1ib3R0b206IDNweCBzb2xpZCAjRDkwMDZDOyBwYWRkaW5nLWJvdHRvbTogOHB4OyBtYXJnaW4tdG9wOiA0OHB4OyB9DQpoMiB7IG1hcmdpbi10b3A6IDM2cHg7IH0NCmEgeyBjb2xvcjogI0Q5MDA2QzsgfQ0KYTpob3ZlciB7IGNvbG9yOiAjMTY4Qzg2OyB9DQojVE9DIHsgYm9yZGVyLXJhZGl1czogMTBweDsgfQ0KLm5hdi1waWxscyA+IGxpLmFjdGl2ZSA+IGEsIC5uYXYtcGlsbHMgPiBsaS5hY3RpdmUgPiBhOmhvdmVyIHsgYmFja2dyb3VuZC1jb2xvcjogIzM1MUE0RiAhaW1wb3J0YW50OyB9DQpjYXB0aW9uIHsgY29sb3I6ICM1QTU1NjA7IGZvbnQtc3R5bGU6IGl0YWxpYzsgY2FwdGlvbi1zaWRlOiB0b3A7IH0NCmNvZGUgeyBjb2xvcjogIzhBMUE1QzsgYmFja2dyb3VuZDogI0Y2RUVGMjsgcGFkZGluZzogMXB4IDVweDsgYm9yZGVyLXJhZGl1czogNHB4OyB9DQp0YWJsZS5kYXRhVGFibGUgeyBmb250LXNpemU6IDAuOTJyZW07IH0NCi5sZWFmbGV0LWNvbnRhaW5lciB7IGJvcmRlci1yYWRpdXM6IDEwcHg7IGJhY2tncm91bmQ6ICNFQUYwRjQgIWltcG9ydGFudDsgfQ0KPC9zdHlsZT4NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgZWNobyA9IFRSVUUsDQogIHdhcm5pbmcgPSBGQUxTRSwNCiAgbWVzc2FnZSA9IEZBTFNFLA0KICBmaWcuYWxpZ24gPSAiY2VudGVyIiwNCiAgZmlnLndpZHRoID0gOCwNCiAgZmlnLmhlaWdodCA9IDUNCikNCg0KIyBDYXJwZXRhIGRlIGRhdG9zIChydXRhIHJlbGF0aXZhOiBsYSBjYXJwZXRhICJpbnB1dCIgZGViZSBlc3RhciBqdW50byBhbCAuUm1kKQ0KcnV0YV9pbnB1dCA8LSAiQzovVXNlcnMvVXNlci9EZXNrdG9wL2lucHV0LyINCmBgYA0KDQo8ZGl2IHN0eWxlPSJiYWNrZ3JvdW5kOiBsaW5lYXItZ3JhZGllbnQoMTM1ZGVnLCAjMzUxQTRGIDAlLCAjRDkwMDZDIDEwMCUpOyBwYWRkaW5nOiAyOHB4IDM0cHg7IGJvcmRlci1yYWRpdXM6IDE2cHg7IG1hcmdpbi1ib3R0b206IDMwcHg7IGRpc3BsYXk6IGZsZXg7IGFsaWduLWl0ZW1zOiBjZW50ZXI7IGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsgZmxleC13cmFwOiB3cmFwOyBnYXA6IDIycHg7IGJveC1zaGFkb3c6IDAgNnB4IDE4cHggcmdiYSg1MywgMjYsIDc5LCAwLjI1KTsiPg0KPGRpdiBzdHlsZT0iZmxleDogMSAxIDM4MHB4OyBtaW4td2lkdGg6IDA7IGNvbG9yOiB3aGl0ZTsiPg0KPGRpdiBzdHlsZT0iZm9udC1zaXplOiAxLjdyZW07IGZvbnQtd2VpZ2h0OiA3MDA7IGxpbmUtaGVpZ2h0OiAxLjI1OyI+VGVycml0b3JpbyB5IHNlZ3VyaWRhZCBlbiBCb2dvdMOhOiB1biBhbsOhbGlzaXMgZGUgbGFzIGRlbnVuY2lhcyBwb3IgVVBaPC9kaXY+DQo8ZGl2IHN0eWxlPSJjb2xvcjogI0Y0RENFOTsgZm9udC1zaXplOiAxLjAycmVtOyBsaW5lLWhlaWdodDogMS40NTsgbWFyZ2luLXRvcDogOHB4OyI+VW5hIGV4cGxvcmFjacOzbiBkZSBsYXMgY2FyYWN0ZXLDrXN0aWNhcyBkZW1vZ3LDoWZpY2FzIHkgdGVycml0b3JpYWxlcyB5IHN1IHJlbGFjacOzbiBjb24gbGFzIGRlbnVuY2lhcyBwb3IgZGVsaXRvIGVuIEJvZ290w6E8L2Rpdj4NCjwvZGl2Pg0KPGRpdiBzdHlsZT0iYmFja2dyb3VuZDogd2hpdGU7IHBhZGRpbmc6IDEycHggMThweDsgYm9yZGVyLXJhZGl1czogMTJweDsgZmxleDogMCAwIGF1dG87IGJveC1zaGFkb3c6IDAgMnB4IDhweCByZ2JhKDAsIDAsIDAsIDAuMTgpOyI+DQo8ZGl2IHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iTG9nbyBLb25yYWQgTG9yZW56IEZ1bmRhY2nDs24gVW5pdmVyc2l0YXJpYSIgc3R5bGU9IndpZHRoOiAxODBweDsgaGVpZ2h0OiA5MnB4OyBiYWNrZ3JvdW5kOiB1cmwoJ2ByIGtuaXRyOjppbWFnZV91cmkoZmlsZS5wYXRoKHJ1dGFfaW5wdXQsICdsb2dvIFVuaXZlcnNpZGFkLnBuZycpKWAnKSBjZW50ZXIgLyBjb3ZlciBuby1yZXBlYXQ7Ij48L2Rpdj4NCjwvZGl2Pg0KPC9kaXY+DQoNCmBgYHtyIGxpYnJlcmlhc30NCmxpYnJhcnkodGlkeXZlcnNlKSAgICAgICMgaW5jbHV5ZSBkcGx5ciwgdGlkeXIsIHJlYWRyLCBwdXJyciB5IGdncGxvdDINCmxpYnJhcnkoc2NhbGVzKQ0KbGlicmFyeShzaG93dGV4dCkNCmxpYnJhcnkoc3lzZm9udHMpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KGpzb25saXRlKQ0KDQpmdWVudGVfZWRpdG9yaWFsIDwtICJGaXJhIFNhbnMiDQpmb250X2FkZF9nb29nbGUobmFtZSA9IGZ1ZW50ZV9lZGl0b3JpYWwsIGZhbWlseSA9IGZ1ZW50ZV9lZGl0b3JpYWwpDQpzaG93dGV4dF9hdXRvKCkNCmBgYA0KDQpgYGB7ciBkYXRvcywgcmVzdWx0cz0naGlkZSd9DQojIDEuIGNhcmdhciBkYXRvcw0KdXB6IDwtIHJlYWRfY3N2KGZpbGUucGF0aChydXRhX2lucHV0LCAidXB6X3ZhcmlhYmxlcy5jc3YiKSwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkNCg0KIyAxLjEgLiBFeHBsb3JhciBEYXRhIHNldCBVUFoNCg0KIyBEaW1lbnNpb25lcyBkZSBsYSBiYXNlDQpkaW0odXB6KQ0KDQojIE5vbWJyZXMgZGUgbGFzIGNvbHVtbmFzDQpuYW1lcyh1cHopDQoNCiMgZ2xpbXBzZSgpOiB2ZXIgVE9EQVMgbGFzIGNvbHVtbmFzLCBzdSB0aXBvIHkgbG9zIHByaW1lcm9zIHZhbG9yZXMgZW4gdW5hIHBhbnRhbGxhLg0KZ2xpbXBzZSh1cHopDQoNCiMgUHJpbWVyYXMgb2JzZXJ2YWNpb25lcw0KaGVhZCh1cHopDQoNCiMgTGFzIGNvbHVtbmFzIGVzdMOhbiBhZ3J1cGFkYXMgcG9yIHByZWZpam8uIHN0YXJ0c193aXRoKCkgc2VsZWNjaW9uYSBwb3IgcHJlZmlqbzoNCnVweiAlPiUgc2VsZWN0KHVwel9ub21icmUsIHN0YXJ0c193aXRoKCJ0YXNhXyIpKSAlPiUgaGVhZCgpICAgIyB0YXNhcyBkZSBkZWxpdG9zIC8xMC4wMDAgaGFiDQp1cHogJT4lIHNlbGVjdCh1cHpfbm9tYnJlLCBzdGFydHNfd2l0aCgibWluXyIpKSAgJT4lIGhlYWQoKSAgICMgbWludXRvcyBjYW1pbmFuZG8gYSBzZXJ2aWNpb3MNCg0KIyAxLjIgdmFsaWRhciBmYWx0YW50ZXMgZW4gRGF0YXNldA0KDQpmYWx0YW50ZXMgPC0gdXB6IHw+DQogIHN1bW1hcmlzZShhY3Jvc3MoZXZlcnl0aGluZygpLA0KICAgICAgICAgICAgICAgICAgIH4gc3VtKGlzLm5hKC54KSkpKSB8Pg0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyA9IGV2ZXJ5dGhpbmcoKSwNCiAgICBuYW1lc190byA9ICJ2YXJpYWJsZSIsDQogICAgdmFsdWVzX3RvID0gImZhbHRhbnRlcyINCiAgKSB8Pg0KICBhcnJhbmdlKGRlc2MoZmFsdGFudGVzKSkNCg0KZmFsdGFudGVzDQpgYGANCg0KYGBge3IgZXN0aWxvfQ0KIyMgMi4gY2FyZ2FyIMK0cGFsZXRhIGRlIGNvbG9yZXMgDQojLS0tIFBhbGV0YSB5IHRlbWEgZGVsIGN1cnNvIChsb3MgbWlzbW9zIGRlIHRvZG9zIGxvcyBzY3JpcHRzKSAtLS0tLS0tLS0tLS0tLQ0KbW9yYWRvICAgPC0gIiMzNTFBNEYiDQptYWdlbnRhICA8LSAiI0Q5MDA2QyINCnR1cnF1ZXNhIDwtICIjMTY4Qzg2Ig0KZ3JpcyAgICAgPC0gIiNEOUQ2REQiDQoNCnRlbWFfY2xhc2UgPC0gdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMikgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE1LCBoanVzdCA9IDApLA0KICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLjUsIGNvbG9yID0gImdyZXkzNSIsIGhqdXN0ID0gMCksDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLCBjb2xvciA9ICJncmV5NDUiLCBzaXplID0gOC41KSwNCiAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigxMCwgMjAsIDEwLCAxMCkNCiAgKQ0KDQoNCmVzdGlsb19wbG90bHkgPC0gZnVuY3Rpb24ocCkgew0KICBwICU+JQ0KICAgIGNvbmZpZyhkaXNwbGF5TW9kZUJhciA9IEZBTFNFKSAlPiUgICAgICAgICAgDQogICAgbGF5b3V0KGZvbnQgPSBsaXN0KGZhbWlseSA9IGZ1ZW50ZV9lZGl0b3JpYWwpKSANCn0NCg0KdGFibGFfYm9uaXRhIDwtIGZ1bmN0aW9uKGRmLCByZXNhbHRhciA9IE5VTEwpIHsNCiAgayA8LSBkZiAlPiUNCiAgICBrYmwoKSAlPiUgICANCiAgICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygiaG92ZXIiLCAiY29uZGVuc2VkIiksIGZ1bGxfd2lkdGggPSBUUlVFLCBmb250X3NpemUgPSAxNCkgJT4lDQogICAgcm93X3NwZWMoMCwgYm9sZCA9IFRSVUUsIGNvbG9yID0gIndoaXRlIiwgYmFja2dyb3VuZCA9IG1vcmFkbykgICMgZmlsYSAwID0gZW5jYWJlemFkbw0KICBpZiAoIWlzLm51bGwocmVzYWx0YXIpKSB7DQogICAgayA8LSBrICU+JSByb3dfc3BlYyhyZXNhbHRhciwgYmFja2dyb3VuZCA9ICIjRjZFRUYyIiwgYm9sZCA9IFRSVUUsIGNvbG9yID0gbW9yYWRvKQ0KICB9DQogIGsNCn0NCg0KIyAtLS0gU2VnbWVudGFjacOzbiBwb2JsYWNpb25hbDogbm9tYnJlcyB5IGNvbG9yZXMgKHNlIHVzYW4gZW4gdG9kbyBlbCBpbmZvcm1lKSAtLS0NCmdydXBvcyAgPC0gYygiUG9ibGFjacOzbiBiYWphIiwgIlBvYmxhY2nDs24gbWVkaWEiLCAiUG9ibGFjacOzbiBhbHRhIikNCmNvbG9yZXMgPC0gYygiUG9ibGFjacOzbiBiYWphIiAgPSAiI0Y4NzY2RCIsDQogICAgICAgICAgICAgIlBvYmxhY2nDs24gbWVkaWEiID0gIiNGMkI4NEIiLA0KICAgICAgICAgICAgICJQb2JsYWNpw7NuIGFsdGEiICA9ICIjNEM5RjcwIikNCg0KIyAtLS0gRm9ybWF0byBkZSBuw7ptZXJvcyB5IHBvcmNlbnRhamVzIChlc3RpbG8gY29sb21iaWFubykgLS0tDQpudW0gPC0gZnVuY3Rpb24oeCwgLi4uKSBzY2FsZXM6Om51bWJlcih4LCBiaWcubWFyayA9ICIuIiwgZGVjaW1hbC5tYXJrID0gIiwiLCAuLi4pDQpwYyAgPC0gZnVuY3Rpb24oeCkgc2NhbGVzOjpwZXJjZW50KHgsIGFjY3VyYWN5ID0gMC4xLCBkZWNpbWFsLm1hcmsgPSAiLCIpDQoNCiMgLS0tIFBpZXphcyBkZSBlc3RpbG8gcGxvdGx5IHF1ZSBzZSByZXBldMOtYW4gZW4gdmFyaW9zIGdyw6FmaWNvcyAtLS0NCmhvdmVyX3Bsb3RseSA8LSBsaXN0KGJnY29sb3IgPSBtb3JhZG8sIGZvbnQgPSBsaXN0KGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDEzKSwgYWxpZ24gPSAibGVmdCIpDQoNCiMgUGFydGUgdW4gdMOtdHVsbyBsYXJnbyBlbiB2YXJpYXMgbMOtbmVhcyAocGxvdGx5IG5vIGJhamEgZGUgbMOtbmVhIHNvbG8geSBlbCB0w610dWxvIHNlIGNvcnRhKQ0KcGFydGlyX3RpdHVsbyA8LSBmdW5jdGlvbih0ZXh0bywgYW5jaG8gPSA3NSkgcGFzdGUoc3Ryd3JhcCh0ZXh0bywgd2lkdGggPSBhbmNobyksIGNvbGxhcHNlID0gIjxicj4iKQ0KDQp0aXR1bG9fcGxvdGx5IDwtIGZ1bmN0aW9uKHRleHRvLCAuLi4pIHsNCiAgYyhsaXN0KHRleHQgPSB0ZXh0bywgeCA9IDAuMDEsIHhhbmNob3IgPSAibGVmdCIsIHkgPSAwLjk3LCB5YW5jaG9yID0gInRvcCIsDQogICAgICAgICBmb250ID0gbGlzdChzaXplID0gMTgsIGNvbG9yID0gbW9yYWRvKSksIGxpc3QoLi4uKSkNCn0NCg0KbWVudV9wbG90bHkgPC0gZnVuY3Rpb24oYm90b25lcywgeCA9IDAsIHkgPSAxLjA0KSB7DQogIGxpc3QodHlwZSA9ICJkcm9wZG93biIsIGRpcmVjdGlvbiA9ICJkb3duIiwgYWN0aXZlID0gMCwgc2hvd2FjdGl2ZSA9IFRSVUUsDQogICAgICAgeCA9IHgsIHhhbmNob3IgPSAibGVmdCIsIHkgPSB5LCB5YW5jaG9yID0gImJvdHRvbSIsIGJ1dHRvbnMgPSBib3RvbmVzLA0KICAgICAgIGJnY29sb3IgPSAid2hpdGUiLCBib3JkZXJjb2xvciA9IGdyaXMsIGZvbnQgPSBsaXN0KGNvbG9yID0gbW9yYWRvLCBzaXplID0gMTMpKQ0KfQ0KDQojIC0tLSBDYWphIGRlIGNvbWVudGFyaW9zIGRlYmFqbyBkZWwgZ3LDoWZpY28gKGxhIHVzYW4gNC4zIHkgNC40KSAtLS0NCmpzX2NvbWVudGFyaW9zIDwtICINCmZ1bmN0aW9uKGVsLCB4LCBkYXRvcykgew0KICB2YXIgY2FqYSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOw0KICBjYWphLnN0eWxlLmNzc1RleHQgPSAnd2lkdGg6MTAwJTtib3gtc2l6aW5nOmJvcmRlci1ib3g7cGFkZGluZzoxNHB4IDE4cHg7bWFyZ2luLXRvcDo2cHg7Ym9yZGVyLXRvcDoycHggc29saWQgI0Q5RDZERDtmb250LXNpemU6MTRweDtsaW5lLWhlaWdodDoxLjU7Y29sb3I6IzMzMzsnOw0KICBlbC5wYXJlbnROb2RlLmluc2VydEJlZm9yZShjYWphLCBlbC5uZXh0U2libGluZyk7DQogIHZhciBhY3R1YWwgPSAtMTsNCg0KICBmdW5jdGlvbiBwaW50YXIoaSkgew0KICAgIGlmIChpID09PSBhY3R1YWwpIHJldHVybjsNCiAgICBhY3R1YWwgPSBpOw0KICAgIGNhamEuaW5uZXJIVE1MID0gJyc7DQogICAgdmFyIHVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndWwnKTsNCiAgICB1bC5zdHlsZS5jc3NUZXh0ID0gJ21hcmdpbjowO3BhZGRpbmctbGVmdDoyMHB4Oyc7DQogICAgW10uY29uY2F0KGRhdG9zLmNvbWVudGFyaW9zW2ldKS5mb3JFYWNoKGZ1bmN0aW9uKHQpIHsNCiAgICAgIHZhciBsaSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2xpJyk7DQogICAgICBsaS5zdHlsZS5tYXJnaW5Cb3R0b20gPSAnNHB4JzsNCiAgICAgIGxpLmlubmVySFRNTCA9IHQ7DQogICAgICB1bC5hcHBlbmRDaGlsZChsaSk7DQogICAgfSk7DQogICAgY2FqYS5hcHBlbmRDaGlsZCh1bCk7DQogIH0NCg0KICAvLyBkZXZ1ZWx2ZSBlbCBuw7ptZXJvIGRlIHZpc3RhIHNlZ8O6biBxdcOpIHRyYXphIGRlIGluaWNpbyBlc3TDoSB2aXNpYmxlIChkYXRvcy5pbmljaW9zID0gdHJhemEgaW5pY2lhbCBkZSBjYWRhIHZpc3RhKQ0KICBmdW5jdGlvbiB2aXN0YSgpIHsNCiAgICB2YXIgZCA9IGVsLmRhdGEsIGluaSA9IGRhdG9zLmluaWNpb3M7DQogICAgZm9yICh2YXIgaSA9IDA7IGkgPCBpbmkubGVuZ3RoOyBpKyspIHsNCiAgICAgIGlmIChkW2luaVtpXV0gJiYgZFtpbmlbaV1dLnZpc2libGUgPT09IHRydWUpIHJldHVybiBpOw0KICAgIH0NCiAgICByZXR1cm4gMDsNCiAgfQ0KDQogIHBpbnRhcigwKTsNCiAgZWwub24oJ3Bsb3RseV9hZnRlcnBsb3QnLCBmdW5jdGlvbigpIHsgcGludGFyKHZpc3RhKCkpOyB9KTsNCn0NCiINCmBgYA0KDQojIDEuIEludHJvZHVjY2nDs24NCg0KRW4gZXN0ZSBpbmZvcm1lIHNlIGFuYWxpemFuIGxhcyAxMTIgVVBaIGRlIEJvZ290w6EsIGNvbiBlbCBwcm9ww7NzaXRvIGRlIGV4cGxvcmFyIGxhcyBjYXJhY3RlcsOtc3RpY2FzIGRlbW9ncsOhZmljYXMgeSB0ZXJyaXRvcmlhbGVzIGRlIGNhZGEgdW5hLCBhc8OtIGNvbW8gc3UgcmVsYWNpw7NuIGNvbiBsYXMgZGVudW5jaWFzIHJlZ2lzdHJhZGFzIGVudHJlIGVuZXJvIHkganVsaW8gZGUgMjAyMy4gQWRlbcOhcywgc2UgYnVzY2EgaWRlbnRpZmljYXIgcG9zaWJsZXMgcmVsYWNpb25lcyBlbnRyZSBsYSBjb21wb3NpY2nDs24gZGUgbGEgcG9ibGFjacOzbiwgbGFzIGNhcmFjdGVyw61zdGljYXMgZGVsIHRlcnJpdG9yaW8geSBsYSBjYW50aWRhZCB5IGxhcyB0YXNhcyBkZSBkZW51bmNpYXMgYXNvY2lhZGFzIGEgZGlmZXJlbnRlcyB0aXBvcyBkZSBkZWxpdG8uIERlIGVzdGEgbWFuZXJhLCBzZSBwcmV0ZW5kZSByZWNvbm9jZXIgcGF0cm9uZXMgeSBkaWZlcmVuY2lhcyBlbnRyZSBsYXMgVVBaIHF1ZSBwZXJtaXRhbiBjb21wcmVuZGVyIG1lam9yIGVsIGNvbXBvcnRhbWllbnRvIHRlcnJpdG9yaWFsIGRlIGxhcyBkZW51bmNpYXMgZW4gbGEgY2l1ZGFkLg0KDQojIyMgUHJlZ3VudGFzIHF1ZSBndcOtYW4gZWwgYW7DoWxpc2lzDQoNCjEuICoqwr9TZWdtZW50YXIgbGFzIFVQWiBwb3IgdGFtYcOxbyBkZSBwb2JsYWNpw7NuIHBlcm1pdGUgY29tcGFyYWNpb25lcyBtw6FzIGp1c3Rhcz8qKg0KMi4gKirCv0PDs21vIHZhcsOtYW4gbGFzIHRhc2FzIGRlIGRlbnVuY2lhcyBzZWfDum4gZWwgZ3J1cG8gZGUgcG9ibGFjacOzbiB5IHF1w6kgVVBaIHNvbiBhdMOtcGljYXM/KioNCjMuICoqwr9RdcOpIGRlbGl0b3MgcHJlZG9taW5hbiBlbiBjYWRhIGdydXBvIHkgY2FtYmlhIHN1IHBlc28gZW50cmUgZ3J1cG9zPyoqDQo0LiAqKsK/Q8OzbW8gc2UgcmVsYWNpb25hIGxhIGNvbXBvc2ljacOzbiBwb3IgZWRhZCBjb24gbGFzIHRhc2FzIGRlIGRlbGl0byB5IGVzYSByZWxhY2nDs24gY2FtYmlhIHNlZ8O6biBlbCBncnVwbz8qKiANCjUuICoqwr9Ew7NuZGUgc2UgdWJpY2FuIGxhcyBVUFogY29uIG1heW9yZXMgdGFzYXMgeSBjw7NtbyBzZSB2ZW4gbGFzIHNlZ21lbnRhY2lvbmVzIGVuIGVsIHRlcnJpdG9yaW8/KioNCg0KIyMjIFZhcmlhYmxlcyBzZWxlY2Npb25hZGFzIHkgcG9yIHF1w6kNCg0KU2UgdXNhbiAxNCB2YXJpYWJsZXMgYWdydXBhZGFzIGVuIHRyZXMgYmxvcXVlcywgcG9ycXVlIGNhZGEgYmxvcXVlIHJlc3BvbmRlIGEgdW5hIHBhcnRlIGRlIGxhcyBwcmVndW50YXM6DQoNCnwgQmxvcXVlIHwgVmFyaWFibGVzIHwgUG9yIHF1w6kgc2UgZWxpZ2VuIHwNCnwtLS18LS0tfC0tLXwNCnwgKipUZXJyaXRvcmlhbCoqIHwgYGFyZWFfa20yYCwgYHBvYmxhY2lvbmAgfCBEZXNjcmliZW4gZWwgdGFtYcOxbyB5IGxhIHBvYmxhY2nDs24gZGUgY2FkYSBVUFouIGBwb2JsYWNpb25gIGVzIGxhIGJhc2UgZGUgbGEgc2VnbWVudGFjacOzbiAoSy1tZWFucykgeSBkZWwgY8OhbGN1bG8gZGUgdGFzYXMuIHwNCnwgKipEZW1vZ3LDoWZpY28qKiB8IGBwY3RfMF8xNGAsIGBwY3RfMTVfMjlgLCBgcGN0XzMwXzU5YCAoU2UgZ2VuZXJhIGVzdGEgdmFyaWFibGUpLCBgcGN0XzYwX21hc2AgfCBQZXJtaXRlbiB2ZXIgc2kgbGEgZXN0cnVjdHVyYSBwb3IgZWRhZGVzIHNlIGFzb2NpYSBjb24gbcOhcyBvIG1lbm9zIGRlbnVuY2lhcy4gfA0KfCAqKkRlbGl0b3MqKiB8IGBjYXNvc190b3RhbGAsIGB0YXNhX3RvdGFsYCwgYGh1cnRvX3RvdGFsYCAoc3VtYSBkZSA3IHRpcG9zIGRlIGh1cnRvKSwgYGxlc2lvbmVzYCwgYGhvbWljaWRpb3NgLCBgdmlvbGVuY2lhX2ludHJhZmFtaWxpYXJgIHkgc3VzIHRhc2FzIHwgU29uIGxvIHF1ZSBzZSBxdWllcmUgZXhwbGljYXIuIFNlIHVzYW4gKip0YXNhcyBwb3IgY2FkYSAxMC4wMDAgaGFiaXRhbnRlcyoqIHBhcmEgY29tcGFyYXIgVVBaIGRlIGRpc3RpbnRvIHRhbWHDsW8uIHwNCg0KPiAqKk5vdGE6KiogaW1wb3J0YW50ZSBpbmRpY2FyIHF1ZSBsYXMgY2lmcmFzIHNvbiBkZW51bmNpYXMgcmVnaXN0cmFkYXMsIG5vIGRlbGl0b3Mgb2N1cnJpZG9zLiBQb3IgZXNvIHNlIGludGVycHJldGFuIGNvbW8gYXNvY2lhY2lvbmVzIGVudHJlIFVQWiB5IG5vIGNvbW8gY2F1c2FzLg0KDQojIDIuIE9iamV0aXZvcw0KDQoqKk9iamV0aXZvIGdlbmVyYWw6KiogQW5hbGl6YXIgbGEgcmVsYWNpw7NuIGVudHJlIGxhcyBjYXJhY3RlcsOtc3RpY2FzIGRlbW9ncsOhZmljYXMgeSB0ZXJyaXRvcmlhbGVzIGRlIGxhcyAxMTIgVVBaIGRlIEJvZ290w6EgeSBsYSBjYW50aWRhZCB5IGxhcyB0YXNhcyBkZSBkZW51bmNpYXMgcmVnaXN0cmFkYXMgcG9yIGRpZmVyZW50ZXMgdGlwb3MgZGUgZGVsaXRvLCBjb24gZWwgZmluIGRlIGlkZW50aWZpY2FyIHBhdHJvbmVzIHkgZGlmZXJlbmNpYXMgdGVycml0b3JpYWxlcyBkdXJhbnRlIGVsIHBlcmlvZG8gZGUgZXN0dWRpby4NCg0KKipPYmpldGl2b3MgZXNwZWPDrWZpY29zOioqDQoNCjEuIFNlZ21lbnRhciBsYXMgVVBaIGRlIEJvZ290w6EgYSBwYXJ0aXIgZGUgc3VzIHZhcmlhYmxlcyBjb21vIGxhIHBvYmxhY2nDs24sIGxhIGNvbXBvc2ljacOzbiBwb3IgZ3J1cG9zIGRlIGVkYWQsIGVsIMOhcmVhIHkgbGEgZGVuc2lkYWQgcG9ibGFjaW9uYWwuDQoNCjIuIERlc2NyaWJpciB5IGNvbXBhcmFyIGxhIGNhbnRpZGFkIHkgbGFzIHRhc2FzIGRlIGRlbnVuY2lhcyBjb3JyZXNwb25kaWVudGVzIGEgZGlmZXJlbnRlcyB0aXBvcyBkZSBkZWxpdG8sIGlkZW50aWZpY2FuZG8gbGFzIFVQWiBxdWUgcHJlc2VudGFuIG1heW9yZXMgeSBtZW5vcmVzIHJlZ2lzdHJvcy4NCg0KMy4gRXhwbG9yYXIgbGFzIHBvc2libGVzIHJlbGFjaW9uZXMgZW50cmUgbGFzIGNhcmFjdGVyw61zdGljYXMgcG9ibGFjaW9uYWxlcyB5IHRlcnJpdG9yaWFsZXMgZGUgbGFzIFVQWiB5IGVsIGNvbXBvcnRhbWllbnRvIGRlIGxhcyBkZW51bmNpYXMsIG1lZGlhbnRlIGhlcnJhbWllbnRhcyBlc3RhZMOtc3RpY2FzIHkgdmlzdWFsaXphY2lvbmVzIHF1ZSBwZXJtaXRhbiBpZGVudGlmaWNhciBwYXRyb25lcyB5IGRpZmVyZW5jaWFzIGVzcGFjaWFsZXMuDQoNCiMgMy4gRnVlbnRlcyBkZSBpbmZvcm1hY2nDs24NCg0KIyMgMy4xIERhdGFzZXQgZGUgZXN0dWRpbyBVUFogQm9nb3TDoQ0KDQojIyMgMy4xLjEgRnVlbnRlcw0KDQoxLiBEZWxpdG9zOiBTZWNyZXRhcsOtYSBEaXN0cml0YWwgZGUgU2VndXJpZGFkLCBDb252aXZlbmNpYSB5IEp1c3RpY2lhLiBQb2JsYWNpw7NuIHkgdml2aWVuZGFzOiBwcm95ZWNjaW9uZXMNCkRBTkUgKENOUFYgMjAxOCkuIA0KDQoyLiBMdW1pbmFyaWFzOiBEYXRvcyBBYmllcnRvcyBCb2dvdMOhLiBFbmN1ZXN0YSBNdWx0aXByb3DDs3NpdG8gMjAyMTogREFORS4NCg0KIyMjIDMuMS4yIFZhcmlhYmxlcyBhbmFsaXNpcyBkZSBlc3R1ZGlvDQoNCmBgYHtyIHRhYmxhLXZhcmlhYmxlcy11cHp9DQojIExhcyA4IHZhcmlhYmxlcyBkZSBlc3R1ZGlvIGNvbiBzdSBkZXNjcmlwY2nDs24gKHVuYSBzb2xhIHZlejogdGFtYmnDqW4gc2UgdXNhIGVuIGxhIHRhYmxhIGRlc2NyaXB0aXZhKQ0KZGVzY3JpcGNpb25fdmFyaWFibGVzIDwtIGMoDQogIGFyZWFfa20yICAgICAgICAgPSAiw4FyZWEgdGVycml0b3JpYWwgZGUgbGEgVVBaIGVuIGtpbMOzbWV0cm9zIGN1YWRyYWRvcyIsDQogIHBvYmxhY2lvbiAgICAgICAgPSAiQ2FudGlkYWQgZGUgaGFiaXRhbnRlcyBkZSBsYSBVUFoiLA0KICBkZW5zaWRhZF9oYWJfa20yID0gIkRlbnNpZGFkIGRlIGhhYml0YW50ZXMgcG9yIGtpbMOzbWV0cm8gY3VhZHJhZG8iLA0KICBwY3RfMF8xNCAgICAgICAgID0gIlBvcmNlbnRhamUgZGUgcG9ibGFjacOzbiBlbnRyZSAwIHkgMTQgYcOxb3MiLA0KICBwY3RfMTVfMjkgICAgICAgID0gIlBvcmNlbnRhamUgZGUgcG9ibGFjacOzbiBlbnRyZSAxNSB5IDI5IGHDsW9zIiwNCiAgcGN0XzYwX21hcyAgICAgICA9ICJQb3JjZW50YWplIGRlIHBvYmxhY2nDs24gbWFzIGRlIDYwIGHDsW9zIiwNCiAgY2Fzb3NfdG90YWwgICAgICA9ICJDYW50aWRhZCB0b3RhbCBkZSBkZW51bmNpYXMgcmVnaXN0cmFkYXMiLA0KICB0YXNhX3RvdGFsICAgICAgID0gIlRhc2EgdG90YWwgZGUgZGVudW5jaWFzIHBvciBjYWRhIDEwLjAwMCBoYWJpdGFudGVzIg0KKQ0KDQp2YXJpYWJsZXMgPC0gbmFtZXMoZGVzY3JpcGNpb25fdmFyaWFibGVzKQ0KDQp0aWJibGUoDQogIFZhcmlhYmxlICAgID0gdmFyaWFibGVzLA0KICBEZXNjcmlwY2lvbiA9IHVubmFtZShkZXNjcmlwY2lvbl92YXJpYWJsZXMpDQopICU+JQ0KICB0YWJsYV9ib25pdGEocmVzYWx0YXIgPSBjKDEsMikpDQpgYGANCg0KKipEYXRvcyBpbXBvcnRhbnRlcyBkZWwgZGF0YXNldCBhIHRlbmVyIGVuIGN1ZW50YSoqDQoNCmBgYHtyIHJlc3VtZW4tZ2VuZXJhbH0NCm5fdXB6IDwtIG5fZGlzdGluY3QodXB6JHVwel9jb2QpDQoNCnBvYmxhY2lvbl90b3RhbCA8LSBzdW0odXB6JHBvYmxhY2lvbiwgbmEucm0gPSBUUlVFKQ0KDQphcmVhX3RvdGFsIDwtIHN1bSh1cHokYXJlYV9rbTIsIG5hLnJtID0gVFJVRSkNCg0KdG90YWxfZGVudW5jaWFzIDwtIHN1bSh1cHokY2Fzb3NfdG90YWwsIG5hLnJtID0gVFJVRSkNCg0KcGFzdGUwKA0KICBuX3VweiwgIiBVUFogYW5hbGl6YWRhcyDCtyAiLA0KICBjb21tYShwb2JsYWNpb25fdG90YWwsIGJpZy5tYXJrID0gIi4iKSwgIiBoYWJpdGFudGVzIMK3ICIsDQogIGNvbW1hKHJvdW5kKGFyZWFfdG90YWwsIDIpLCBiaWcubWFyayA9ICIuIiksICIga23CsiBkZSBleHRlbnNpw7NuIMK3ICIsDQogIGNvbW1hKHRvdGFsX2RlbnVuY2lhcywgYmlnLm1hcmsgPSAiLiIpLCAiIGRlbnVuY2lhcyByZWdpc3RyYWRhcyIsIiAgUGVyaW9kbzogZW5lcm8tanVsaW8gZGUgMjAyMyINCikNCmBgYA0KDQoqKkJvZ290w6EgZW4gY2lmcmFzOiB1bmEgbWlyYWRhIGVzdGFkw61zdGljYSBhIGxhcyBVUFo6KioNCg0KYGBge3IgdGFibGEtZGVzY3JpcHRpdmF9DQojICJ2YXJpYWJsZXMiIHZpZW5lIGRlIGxhIHRhYmxhIGRlIHZhcmlhYmxlcyAoc2VjY2nDs24gMy4xLjIpDQoNCnRhYmxhX2Rlc2NyaXB0aXZhIDwtIGRhdGEuZnJhbWUoDQogIE1lZGlhID0gc2FwcGx5KHVwelt2YXJpYWJsZXNdLCBtZWFuLCBuYS5ybSA9IFRSVUUpLA0KICBNZWRpYW5hID0gc2FwcGx5KHVwelt2YXJpYWJsZXNdLCBtZWRpYW4sIG5hLnJtID0gVFJVRSksDQogIERlc3ZpYWNpb24gPSBzYXBwbHkodXB6W3ZhcmlhYmxlc10sIHNkLCBuYS5ybSA9IFRSVUUpLA0KICBNaW5pbW8gPSBzYXBwbHkodXB6W3ZhcmlhYmxlc10sIG1pbiwgbmEucm0gPSBUUlVFKSwNCiAgTWF4aW1vID0gc2FwcGx5KHVwelt2YXJpYWJsZXNdLCBtYXgsIG5hLnJtID0gVFJVRSkNCikNCg0KIyBSZWRvbmRlYXIgbGFzIGVzdGFkw61zdGljYXMgYSAxIGRlY2ltYWwNCnRhYmxhX2Rlc2NyaXB0aXZhW10gPC0gbGFwcGx5KHRhYmxhX2Rlc2NyaXB0aXZhLCByb3VuZCwgZGlnaXRzID0gMSkNCg0KIyBNb3N0cmFyIHRhYmxhDQp0YWJsYV9kZXNjcmlwdGl2YSAlPiUNCiAgdGFibGFfYm9uaXRhKHJlc2FsdGFyID0gYygxLCAyKSkNCmBgYA0KDQojIDQuIEFuYWxpc2lzIHBvYmxhY2lvbmFsIHkgdGVycml0b3JpYWwgZGUgbGFzIHVweg0KDQojIyA0LjEgwr9MYSBzZWdtZW50YWNpw7NuIGRlIGxhcyBwb2JsYWNpb25lcyBwZXJtaXRlIHJlYWxpemFyIHVuIGFuw6FsaXNpcyBtw6FzIHByZWNpc28geSBwcm9mdW5kbyBkZSBsYSBpbmZvcm1hY2nDs24/DQoNCmBgYHtyIGhpc3RvLXBvYmxhY2lvbn0NCnBfcG9ibGFjaW9uX2FyZWEgPC0gZ2dwbG90KHVweiwgYWVzKHggPSBhcmVhX2ttMiwgeSA9IHBvYmxhY2lvbikpICsNCiAgZ2VvbV9wb2ludCgNCiAgICBjb2xvciA9ICIjNkI0QzhBIiwNCiAgICBzaXplID0gMi41LA0KICAgIGFscGhhID0gMC43LA0KICAgIGFlcyh0ZXh0ID0gcGFzdGUwKA0KICAgICAgIlVQWjogIiwgdXB6X25vbWJyZSwNCiAgICAgICI8YnI+w4FyZWE6ICIsIHJvdW5kKGFyZWFfa20yLCAyKSwgIiBrbcKyIiwNCiAgICAgICI8YnI+UG9ibGFjacOzbjogIiwNCiAgICAgIGZvcm1hdChyb3VuZChwb2JsYWNpb24pLCBiaWcubWFyayA9ICIuIiwgZGVjaW1hbC5tYXJrID0gIiwiLA0KICAgICAgICAgICAgIHNjaWVudGlmaWMgPSBGQUxTRSkNCiAgICApKQ0KICApICsNCiAgZ2VvbV9zbW9vdGgoDQogICAgbWV0aG9kID0gImxtIiwNCiAgICBzZSA9IEZBTFNFLA0KICAgIGNvbG9yID0gIiNEMTdBMjIiDQogICkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgbGFiZWxzID0gc2NhbGVzOjpsYWJlbF9jb21tYSgNCiAgICAgIGJpZy5tYXJrID0gIi4iLA0KICAgICAgZGVjaW1hbC5tYXJrID0gIiwiDQogICAgKQ0KICApICsNCiAgbGFicygNCiAgICB4ID0gIsOBcmVhIHRlcnJpdG9yaWFsIChrbcKyKSIsDQogICAgeSA9ICJQb2JsYWNpw7NuIChoYWJpdGFudGVzKSINCiAgKSArDQogIHRlbWFfY2xhc2UgKw0KICB0aGVtZSgNCiAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCkNCiAgKQ0KDQpnZ3Bsb3RseShwX3BvYmxhY2lvbl9hcmVhLCB0b29sdGlwID0gInRleHQiKSAlPiUNCiAgZXN0aWxvX3Bsb3RseSgpICU+JQ0KICBsYXlvdXQoDQogICAgdGl0bGUgPSB0aXR1bG9fcGxvdGx5KA0KICAgICAgcGFzdGUwKA0KICAgICAgICAiPGI+IiwgcGFydGlyX3RpdHVsbygiRWwgw6FyZWEgeSBsYSBwb2JsYWNpw7NuIHNlIHJlbGFjaW9uYW4gZGUgbWFuZXJhIG1vZGVyYWRhOiBhbGd1bmFzIFVQWiBwZXF1ZcOxYXMgdGllbmVuIG3DoXMgcG9ibGFjacOzbiBxdWUgb3RyYXMgbcOhcyBncmFuZGVzIiksICI8L2I+IiwNCiAgICAgICAgIjxicj48c3VwPkNhZGEgcHVudG8gcmVwcmVzZW50YSB1bmEgVVBaLiBQYXNhIGVsIGN1cnNvciBzb2JyZSBjYWRhIHB1bnRvIHBhcmEgY29uc3VsdGFyIHN1cyBjaWZyYXMuPC9zdXA+Ig0KICAgICAgKSwNCiAgICAgIHhyZWYgPSAiY29udGFpbmVyIg0KICAgICksDQogICAgbWFyZ2luID0gbGlzdCh0ID0gMTI1KQ0KICApDQpgYGANCg0KDQpBbCByZXZpc2FyIGxvcyBkYXRvcywgc2UgZXZpZGVuY2lhIHF1ZSBsYSBwb2JsYWNpw7NuIHByZXNlbnRhIHVuYSBkaWZlcmVuY2lhIGJhc3RhbnRlIGFtcGxpYSBlbnRyZSBlbCB2YWxvciBtw61uaW1vIHkgZWwgbcOheGltby4gUG9yIGVzdGEgcmF6w7NuLCBjb25zaWRlcm8gbmVjZXNhcmlvIGRpdmlkaXIgbGFzIFVQWiBlbiB0cmVzIGdydXBvczogcG9ibGFjacOzbiBiYWphLCBwb2JsYWNpw7NuIG1lZGlhIHkgcG9ibGFjacOzbiBhbHRhLiBFc3RvIHBlcm1pdGlyw6EgcmVhbGl6YXIgY29tcGFyYWNpb25lcyBlbnRyZSBVUFogcXVlIHRlbmdhbiBjYXJhY3RlcsOtc3RpY2FzIHBvYmxhY2lvbmFsZXMgc2ltaWxhcmVzIHkgYW5hbGl6YXIgZGUgdW5hIG1lam9yIG1hbmVyYSBsYXMgdGFzYXMgZGUgZGVudW5jaWFzLCB0ZW5pZW5kbyBlbiBjdWVudGEgcXVlIGVzdGFzIHB1ZWRlbiB2YXJpYXIgYmFzdGFudGUgZGVwZW5kaWVuZG8gZGUgbGEgY2FudGlkYWQgZGUgaGFiaXRhbnRlcyBkZSBjYWRhIHRlcnJpdG9yaW8uDQoNCiMjIDQuMiDCv0PDs21vIHZhcsOtYW4gbGFzIHRhc2FzIGRlIGRlbnVuY2lhcyBzZWfDum4gZWwgZ3J1cG8gZGUgcG9ibGFjacOzbiB5IHF1w6kgVVBaIHNvbiBhdMOtcGljYXM/DQoNClRlbmllbmRvIGVuIGN1ZW50YSBlbCBoYWxsYXpnbyBhbnRlcmlvciB5IGdlbmVyYWRhIGxhIHNlZ21lbnRhY2nDs24gcG9yIG1lZGlvIGRlIEstTWVhbnMgc2UgZXZpZGVuY2lhIGxvIHNpZ3VpZW50ZS4NCg0KKipFc3RhZGlzdGljYXMgZGVzY3JpcHRpdmFzIHNlZ21lbnRhY2nDs24gZ2VuZXJhZGEqKg0KDQpgYGB7ciBnZW5lcmFjaW9uX3NlZ21lbnRhY2lvbn0NCg0KIyBLLW1lYW5zICMgbW9kZWxvIGRlIGNsdXN0ZXJzDQpzZXQuc2VlZCgxMjMpDQoNCm1vZGVsbyA8LSBrbWVhbnMoDQogIHNjYWxlKHVwelsicG9ibGFjaW9uIl0pLA0KICBjZW50ZXJzID0gMywNCiAgbnN0YXJ0ID0gMjUNCikNCg0KIyBJZGVudGlmaWNhciBlbCBvcmRlbiBkZSBsb3MgZ3J1cG9zIHNlZ8O6biBzdSBwb2JsYWNpw7NuIHByb21lZGlvDQpvcmRlbl9ncnVwb3MgPC0gb3JkZXIobW9kZWxvJGNlbnRlcnNbLCAxXSkNCg0KIyBBc2lnbmFyIGxhcyBjYXRlZ29yw61hcyBhIGNhZGEgVVBaIChsb3Mgbm9tYnJlcyBkZSBsb3MgZ3J1cG9zIHNlIGRlZmluZW4gZW4gZWwgY2h1bmsgImVzdGlsbyIpDQp1cHogPC0gdXB6ICU+JQ0KICBtdXRhdGUoDQogICAgZ3J1cG9fcG9ibGFjaW9uID0gZmFjdG9yKA0KICAgICAgZ3J1cG9zW21hdGNoKG1vZGVsbyRjbHVzdGVyLCBvcmRlbl9ncnVwb3MpXSwNCiAgICAgIGxldmVscyA9IGdydXBvcw0KICAgICkNCiAgKQ0KYGBgDQoNCmBgYHtyIHByZXBhcmFyX2RhdG9zfQ0KIyBWYXJpYWJsZXMgZGVyaXZhZGFzIHF1ZSBzZSB1c2FuIGVuIDQuMywgNC40IHkgZWwgbWFwYSAoc2UgY2FsY3VsYW4gdW5hIHNvbGEgdmV6KQ0KY29sc19odXJ0byA8LSBjKCJjYXNvc19odXJ0b19wZXJzb25hcyIsICJjYXNvc19odXJ0b19yZXNpZGVuY2lhcyIsICJjYXNvc19odXJ0b19hdXRvbW90b3IiLA0KICAgICAgICAgICAgICAgICJjYXNvc19odXJ0b19iaWNpY2xldGFzIiwgImNhc29zX2h1cnRvX2NvbWVyY2lvIiwgImNhc29zX2h1cnRvX2NlbHVsYXJlcyIsDQogICAgICAgICAgICAgICAgImNhc29zX2h1cnRvX21vdG9zIikNCg0KdXB6IDwtIHVweiAlPiUNCiAgbXV0YXRlKA0KICAgIGNhc29zX2h1cnRvX3RvdGFsID0gcm93U3VtcyhhY3Jvc3MoYWxsX29mKGNvbHNfaHVydG8pKSksDQogICAgdGFzYV9odXJ0b190b3RhbCAgPSBjYXNvc19odXJ0b190b3RhbCAvIHBvYmxhY2lvbiAqIDEwMDAwLA0KICAgIHBjdF8zMF81OSAgICAgICAgID0gMTAwIC0gKHBjdF8wXzE0ICsgcGN0XzE1XzI5ICsgcGN0XzYwX21hcykgICAjIGxvIHF1ZSBmYWx0YSBwYXJhIGxsZWdhciBhIDEwMCAlDQogICkNCmBgYA0KDQpgYGB7ciBnZW5lcmFjaW9uX2dyYWZpY299DQprX2F0aXBpY28gPC0gMS41ICAgIyBsw61taXRlIGF0w61waWNvOiBRMyArIDEsNSDDlyBJUVINCmtfZXh0cmVtbyA8LSAzICAgICAjIGzDrW1pdGUgZXh0cmVtbzogUTMgKyAzIMOXIElRUg0KDQojIEFncmVnYXIgbiBhIGxhcyBldGlxdWV0YXMgKGdydXBvX3BvYmxhY2lvbiB5YSBlcyBmYWN0b3Igb3JkZW5hZG8gZGVzZGUgZWwgSy1tZWFucykNCnVweiA8LSB1cHogJT4lDQogIGdyb3VwX2J5KGdydXBvX3BvYmxhY2lvbikgJT4lDQogIG11dGF0ZSgNCiAgICAjIHRvZG8gc2UgY2FsY3VsYSBjb24gbGFzIHRhc2FzIHJlYWxlcyAoc2luIGxvZykNCiAgICBxMV9nID0gcXVhbnRpbGUodGFzYV90b3RhbCwgMC4yNSksDQogICAgcTNfZyA9IHF1YW50aWxlKHRhc2FfdG90YWwsIDAuNzUpLA0KICAgIGlxcl9nID0gcTNfZyAtIHExX2csDQogICAgbGltX2F0aXBpY28gPSBxM19nICsga19hdGlwaWNvICogaXFyX2csDQogICAgbGltX2V4dHJlbW8gPSBxM19nICsga19leHRyZW1vICogaXFyX2csDQogICAgZXNfYXRpcGljbyAgPSB0YXNhX3RvdGFsID4gbGltX2F0aXBpY28gfCB0YXNhX3RvdGFsIDwgcTFfZyAtIGtfYXRpcGljbyAqIGlxcl9nLA0KICAgIGVzX2V4dHJlbW8gID0gdGFzYV90b3RhbCA+IGxpbV9leHRyZW1vIHwgdGFzYV90b3RhbCA8IHExX2cgLSBrX2V4dHJlbW8gKiBpcXJfZywNCiAgICBldGlxdWV0YSA9IHBhc3RlMChncnVwb19wb2JsYWNpb24sICI8YnI+KG4gPSAiLCBuKCksICIpIikNCiAgKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBtdXRhdGUoDQogICAgZXRpcXVldGEgPSBmYWN0b3IoZXRpcXVldGEsIGxldmVscyA9IHVuaXF1ZShldGlxdWV0YVtvcmRlcihncnVwb19wb2JsYWNpb24pXSkpLA0KICAgIHBvcyA9IGFzLm51bWVyaWMoZXRpcXVldGEpLA0KICAgIHRleHRvID0gcGFzdGUwKA0KICAgICAgIjxiPiIsIHVwel9ub21icmUsICI8L2I+IiwNCiAgICAgICI8YnI+TG9jYWxpZGFkOiAiLCBsb2NhbGlkYWQsDQogICAgICAiPGJyPlBvYmxhY2nDs246ICIsIHNjYWxlczo6Y29tbWEocG9ibGFjaW9uKSwNCiAgICAgICI8YnI+VGFzYSB0b3RhbDogIiwgcm91bmQodGFzYV90b3RhbCwgMSksDQogICAgICBpZmVsc2UoZXNfZXh0cmVtbywgIjxicj48aT5BdMOtcGljbyBleHRyZW1vPC9pPiIsICI8YnI+PGk+QXTDrXBpY288L2k+IikNCiAgICApDQogICkNCg0KIyBsw61taXRlcyBwb3IgZ3J1cG8gKHBhcmEgcXVlIGxvcyByZXZpc2VzKQ0KdXB6ICU+JSBkaXN0aW5jdChncnVwb19wb2JsYWNpb24sIHExX2csIHEzX2csIGlxcl9nLCBsaW1fYXRpcGljbywgbGltX2V4dHJlbW8pJT4lDQogIHRhYmxhX2Jvbml0YShyZXNhbHRhciA9IGMoKSkNCg0KcmdiYSA8LSBmdW5jdGlvbihjb2wsIGEpIHsgbSA8LSBjb2wycmdiKGNvbCk7IHNwcmludGYoInJnYmEoJWQsJWQsJWQsJS4yZikiLCBtWzFdLCBtWzJdLCBtWzNdLCBhKSB9DQoNCmcgPC0gcGxvdF9seSgpDQoNCiMgMS4gY2FqYXM6IGxvcyBiaWdvdGVzIHRlcm1pbmFuIGVuIGVsIGzDrW1pdGUgc3VwZXJpb3IvaW5mZXJpb3IgKG5vIGVuIGVsIG3DoXhpbW8pDQojICAgIGxvcyBwdW50b3MgcHJvcGlvcyBkZSBsYSBjYWphIHNlIG9jdWx0YW47IGxvcyBhdMOtcGljb3MgcXVlIHNlIHZlbiBzb24gbG9zIGRlIGFiYWpvDQpmb3IgKGdyIGluIGxldmVscyh1cHokZ3J1cG9fcG9ibGFjaW9uKSkgew0KICBkIDwtIHVweiAlPiUgZmlsdGVyKGdydXBvX3BvYmxhY2lvbiA9PSBncikNCiAgZyA8LSBnICU+JSBhZGRfdHJhY2UoDQogICAgZGF0YSA9IGQsIHR5cGUgPSAiYm94IiwNCiAgICB4ID0gfnBvcywgeSA9IH50YXNhX3RvdGFsLCB3aWR0aCA9IDAuNiwNCiAgICBmaWxsY29sb3IgPSByZ2JhKGNvbG9yZXNbW2dyXV0sIDAuNSksDQogICAgbGluZSA9IGxpc3QoY29sb3IgPSAiZ3JleTIwIiwgd2lkdGggPSAxLjUpLA0KICAgIGJveHBvaW50cyA9ICJvdXRsaWVycyIsDQogICAgbWFya2VyID0gbGlzdChvcGFjaXR5ID0gMCwgc2l6ZSA9IDEpLCAgICMgcHVudG9zIGRlIGxhIGNhamEgaW52aXNpYmxlcw0KICAgIHF1YXJ0aWxlbWV0aG9kID0gImxpbmVhciIsDQogICAgaG92ZXJvbiA9ICJib3hlcyIsICAgICAgICAgICAgICAgICAgICAgICMgZWwgY3VhZHJvIGRlbCBtb3VzZSBzb2xvIHNhbGUgc29icmUgbGEgY2FqYQ0KICAgIGhvdmVyaW5mbyA9ICJ5Iiwgc2hvd2xlZ2VuZCA9IEZBTFNFDQogICkNCn0NCg0KIyAyLiBhdMOtcGljb3MgKG5lZ3JvcykNCnNldC5zZWVkKDEyMykNCm5lZ3JvcyA8LSB1cHogJT4lIGZpbHRlcihlc19hdGlwaWNvLCAhZXNfZXh0cmVtbykgJT4lDQogIG11dGF0ZSh4aiA9IHBvcyArIHJ1bmlmKG4oKSwgLTAuMDcsIDAuMDcpKQ0KDQpnIDwtIGcgJT4lIGFkZF90cmFjZSgNCiAgZGF0YSA9IG5lZ3JvcywgdHlwZSA9ICJzY2F0dGVyIiwgbW9kZSA9ICJtYXJrZXJzIiwNCiAgeCA9IH54aiwgeSA9IH50YXNhX3RvdGFsLA0KICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gImdyZXkyMCIsIHNpemUgPSA3LCBvcGFjaXR5ID0gMC43NSksDQogIGhvdmVydGV4dCA9IH50ZXh0bywgaG92ZXJpbmZvID0gInRleHQiLCBzaG93bGVnZW5kID0gRkFMU0UNCikNCg0KIyAzLiBhdMOtcGljb3MgZXh0cmVtb3MgKHJvam9zKQ0Kcm9qb3MgPC0gdXB6ICU+JSBmaWx0ZXIoZXNfZXh0cmVtbykgJT4lDQogIG11dGF0ZSh4aiA9IHBvcyArIHJ1bmlmKG4oKSwgLTAuMDcsIDAuMDcpKQ0KDQpnIDwtIGcgJT4lIGFkZF90cmFjZSgNCiAgZGF0YSA9IHJvam9zLCB0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gIm1hcmtlcnMiLA0KICB4ID0gfnhqLCB5ID0gfnRhc2FfdG90YWwsDQogIG1hcmtlciA9IGxpc3QoY29sb3IgPSAicmVkIiwgc2l6ZSA9IDEwLCBvcGFjaXR5ID0gMC45KSwNCiAgaG92ZXJ0ZXh0ID0gfnRleHRvLCBob3ZlcmluZm8gPSAidGV4dCIsIHNob3dsZWdlbmQgPSBGQUxTRQ0KKQ0KDQojIDQuIGVzY2FsYSBsb2cgc29sbyBwYXJhIGxhIHZpc3RhDQptYXJjYXMgPC0gYyg1MCwgMTAwLCAyMDAsIDUwMCwgMTAwMCwgMjAwMCwgNTAwMCwgMTAwMDApDQpldGlxdWV0YXNfeCA8LSBsZXZlbHModXB6JGV0aXF1ZXRhKQ0KDQpnIDwtIGcgJT4lDQogIGxheW91dCgNCiAgICB0aXRsZSA9IGxpc3QoDQogICAgICB0ZXh0ID0gcGFzdGUwKCI8Yj4iLCBwYXJ0aXJfdGl0dWxvKCJQb2JsYWNpb24gYmFqYSB0aWVuZSBsb3MgYXRpcGljb3MgZXh0cmVtb3MsIGF1bWVudGFuZG8gbGEgdGFzYSBkZSBkZWxpdG9zLCBQb2JsYWNpb24gbWVkaWEgeSBhbHRhIHNvbiBob21vZ8OpbmVhcyIpLCAiPC9iPiIsDQogICAgICAgICAgICAgICAgICAgICI8YnI+PHN1cD5Nw6l0b2RvIEstbWVhbnMgwrcgcHVudG9zIG5lZ3JvcyA9IGF0w61waWNvcyAoPiBRMyArIDEsNSDDlyBJUVIpIMK3ICIsDQogICAgICAgICAgICAgICAgICAgICJwdW50b3Mgcm9qb3MgPSBhdMOtcGljb3MgZXh0cmVtb3MgKD4gUTMgKyAzIMOXIElRUikgwrcgcGFzYSBlbCBjdXJzb3IgcGFyYSB2ZXIgbG9zIHZhbG9yZXM8L3N1cD4iKSwNCiAgICAgIHggPSAwLjAxLCB4YW5jaG9yID0gImxlZnQiDQogICAgKSwNCiAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiR3J1cG8gZGUgcG9ibGFjacOzbiIsIHNob3dncmlkID0gRkFMU0UsIHplcm9saW5lID0gRkFMU0UsDQogICAgICAgICAgICAgICAgIHRpY2t2YWxzID0gc2VxX2Fsb25nKGV0aXF1ZXRhc194KSwgdGlja3RleHQgPSBldGlxdWV0YXNfeCwNCiAgICAgICAgICAgICAgICAgcmFuZ2UgPSBjKDAuNCwgbGVuZ3RoKGV0aXF1ZXRhc194KSArIDAuNikpLA0KICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJUYXNhIHRvdGFsIGRlIGRlbGl0b3MgKGVzY2FsYSBsb2cpIiwgdHlwZSA9ICJsb2ciLA0KICAgICAgICAgICAgICAgICB0aWNrdmFscyA9IG1hcmNhcywgdGlja3RleHQgPSBmb3JtYXQobWFyY2FzLCBiaWcubWFyayA9ICIuIiwgc2NpZW50aWZpYyA9IEZBTFNFKSwNCiAgICAgICAgICAgICAgICAgaG92ZXJmb3JtYXQgPSAiLjFmIiksDQogICAgaG92ZXJsYWJlbCA9IGxpc3QoYWxpZ24gPSAibGVmdCIpLA0KICAgIG1hcmdpbiA9IGxpc3QodCA9IDEyNSkNCiAgKSAlPiUNCiAgZXN0aWxvX3Bsb3RseSgpDQoNCmcNCmBgYA0KDQpFbiBsYXMgcG9ibGFjaW9uZXMgbWVkaWEgeSBhbHRhIG5vIHNlIGlkZW50aWZpY2FuIHZhbG9yZXMgYXTDrXBpY29zIHF1ZSBwdWVkYW4gYWZlY3RhciBzaWduaWZpY2F0aXZhbWVudGUgZWwgYW7DoWxpc2lzIGRlIGNvcnJlbGFjacOzbiBlbnRyZSBlbCB0YW1hw7FvIGRlIGxhIHBvYmxhY2nDs24geSBsYXMgdGFzYXMgZGUgZGVsaXRvLiBTaW4gZW1iYXJnbywgZW4gbGEgcG9ibGFjacOzbiBiYWphIHNlIGlkZW50aWZpY2FuIHZhcmlvcyB2YWxvcmVzIGF0aXBpY29zIGV4dHJlbW9zLCBjb21vIHNlIG11ZXN0cmFuIGVuIGxhIHRhYmxhIGEgY29udGludWFjaW9uLg0KDQpFc3RvcyB2YWxvcmVzIG1lcmVjZW4gZXNwZWNpYWwgYXRlbmNpw7NuLCB5YSBxdWUgcG9kcsOtYW4gaW5mbHVpciBlbiBsb3MgcmVzdWx0YWRvcyBkZWwgYW7DoWxpc2lzIGRlIGNvcnJlbGFjacOzbiB5IGFmZWN0YXIgbGEgaW50ZXJwcmV0YWNpw7NuIGRlIGxhIHJlbGFjacOzbiBlbnRyZSBlbCB0YW1hw7FvIGRlIGxhIHBvYmxhY2nDs24geSBsYXMgdGFzYXMgZGUgZGVsaXRvLg0KDQojIyA0LjMgwr9RdcOpIGRlbGl0b3MgcHJlZG9taW5hbiBlbiBjYWRhIGdydXBvIHkgY2FtYmlhIHN1IHBlc28gZW50cmUgZ3J1cG9zPw0KDQoqKlRhYmxhIGRlIFVQWiBjb24gdmFsb3JlcyBhdMOtcGljb3MgZW4gbGFzIHRhc2FzIGRlIGRlbnVuY2lhcyBwb3IgZGVsaXRvcy4qKg0KDQpgYGB7ciBncmFmaWNvX2JhcnJhc19kZWxpdG9zX3hfc2VnbWVudGFjaW9ufQ0KIyAxLiBFbCBodXJ0byB0b3RhbCB5IHN1IHRhc2EgKHBvciAxMC4wMDAgaGFiLikgc2UgY2FsY3VsYW4gZW4gZWwgY2h1bmsgInByZXBhcmFyX2RhdG9zIg0KDQojIDIuIEZ1bmNpb25lcyBkZSBhcG95byAtLS0tDQpvcmRlbl9kZWxpdG9zIDwtIGMoIkh1cnRvIiwgIkxlc2lvbmVzIiwgIkhvbWljaWRpb3MiLCAiVmlvbGVuY2lhIGludHJhZmFtaWxpYXIiKQ0KDQpyZXN1bWlyIDwtIGZ1bmN0aW9uKGRhdG9zKSB7DQogIGRhdG9zICU+JQ0KICAgIHN1bW1hcmlzZSgNCiAgICAgIHBvYmxhY2lvbiAgPSBzdW0ocG9ibGFjaW9uKSwNCiAgICAgIEh1cnRvICAgICAgPSBzdW0oY2Fzb3NfaHVydG9fdG90YWwpLA0KICAgICAgTGVzaW9uZXMgICA9IHN1bShjYXNvc19sZXNpb25lcyksDQogICAgICBIb21pY2lkaW9zID0gc3VtKGNhc29zX2hvbWljaWRpb3MpLA0KICAgICAgYFZpb2xlbmNpYSBpbnRyYWZhbWlsaWFyYCA9IHN1bShjYXNvc192aW9sZW5jaWFfaW50cmFmYW1pbGlhcikNCiAgICApICU+JQ0KICAgIHBpdm90X2xvbmdlcihhbGxfb2Yob3JkZW5fZGVsaXRvcyksIG5hbWVzX3RvID0gImRlbGl0byIsIHZhbHVlc190byA9ICJjYXNvcyIpICU+JQ0KICAgIG11dGF0ZSh0YXNhID0gY2Fzb3MgLyBwb2JsYWNpb24gKiAxMDAwMCkNCn0NCg0KZXhwYW5kaXIgPC0gZnVuY3Rpb24oZCwgcmFuZ28sIG5fcHRzID0gNDApIHsNCiAgcCA8LSBkW3JlcChzZXFfbGVuKG5yb3coZCkpLCBlYWNoID0gbl9wdHMpLCBdDQogIHAkeCA8LSByZXAoc2VxKDAsIHJhbmdvLCBsZW5ndGgub3V0ID0gbl9wdHMpLCB0aW1lcyA9IG5yb3coZCkpDQogIHANCn0NCg0KIyAzLiBWaXN0YSAxOiBwb2JsYWNpw7NuIGJhamEgY29uIHZzIHNpbiBhdMOtcGljb3MgZXh0cmVtb3MgLS0tLQ0KIyBlc19leHRyZW1vIHlhIHZpZW5lIGNhbGN1bGFkbyBlbiBlbCBib3hwbG90IChRMyArIDMgw5cgSVFSLCBjb24gdGFzYXMgcmVhbGVzIHkgcG9yIHNlZ21lbnRhY2nDs24pDQpiYWphX2NvbiA8LSB1cHogJT4lIGZpbHRlcihncnVwb19wb2JsYWNpb24gPT0gIlBvYmxhY2nDs24gYmFqYSIpDQoNCmF0aXBpY29zIDwtIGJhamFfY29uICU+JQ0KICBmaWx0ZXIoZXNfZXh0cmVtbykgJT4lDQogIGFycmFuZ2UoZGVzYyh0YXNhX3RvdGFsKSkNCg0Kbl9hdGlwICAgPC0gbnJvdyhhdGlwaWNvcykNCmJhamFfc2luIDwtIGJhamFfY29uICU+JSBmaWx0ZXIoISh1cHpfY29kICVpbiUgYXRpcGljb3MkdXB6X2NvZCkpDQoNCmNvbXAgPC0gcmVzdW1pcihiYWphX2NvbikgJT4lDQogIHNlbGVjdChkZWxpdG8sIGNhc29zX2NvbiA9IGNhc29zLCB0YXNhX2NvbiA9IHRhc2EpICU+JQ0KICBsZWZ0X2pvaW4ocmVzdW1pcihiYWphX3NpbikgJT4lIHNlbGVjdChkZWxpdG8sIGNhc29zX3NpbiA9IGNhc29zLCB0YXNhX3NpbiA9IHRhc2EpLCBieSA9ICJkZWxpdG8iKSAlPiUNCiAgbXV0YXRlKA0KICAgIGRpZl9jYXNvcyA9IGlmZWxzZShjYXNvc19jb24gPT0gMCwgMCwgY2Fzb3Nfc2luIC8gY2Fzb3NfY29uIC0gMSksDQogICAgZXNfbWF4ICAgID0gY2Fzb3NfY29uID09IG1heChjYXNvc19jb24pLA0KICAgIGV0aXF1ZXRhICA9IGlmZWxzZShlc19tYXgsIHBhc3RlMChkZWxpdG8sICIgIOKYhSIpLCBkZWxpdG8pLA0KICAgIHRleHRvID0gcGFzdGUwKA0KICAgICAgIjxiPiIsIGRlbGl0bywgIjwvYj4iLA0KICAgICAgIjxicj5Db24gYXTDrXBpY29zIGV4dHJlbW9zOiAiLCBudW0oY2Fzb3NfY29uKSwgIiBjYXNvcyDCtyB0YXNhICIsIG51bSh0YXNhX2NvbiwgYWNjdXJhY3kgPSAwLjEpLA0KICAgICAgIjxicj5TaW4gYXTDrXBpY29zIGV4dHJlbW9zOiAiLCBudW0oY2Fzb3Nfc2luKSwgIiBjYXNvcyDCtyB0YXNhICIsIG51bSh0YXNhX3NpbiwgYWNjdXJhY3kgPSAwLjEpLA0KICAgICAgIjxicj5DYW1iaW8gZW4gY2Fzb3M6ICIsIHBjKGRpZl9jYXNvcykNCiAgICApDQogICkgJT4lDQogIGFycmFuZ2UoZGVzYyhjYXNvc19jb24pKQ0KDQpyYW5nb19iYWphICA8LSBtYXgoY29tcCRjYXNvc19jb24pICogMS4xDQpwdW50b3NfYmFqYSA8LSBleHBhbmRpcihjb21wLCByYW5nb19iYWphKQ0KDQpwcm9tX2NvbiA8LSBtZWFuKGJhamFfY29uJHRhc2FfdG90YWwpOyAgIHByb21fc2luIDwtIG1lYW4oYmFqYV9zaW4kdGFzYV90b3RhbCkNCm1lZF9jb24gIDwtIG1lZGlhbihiYWphX2NvbiR0YXNhX3RvdGFsKTsgbWVkX3NpbiAgPC0gbWVkaWFuKGJhamFfc2luJHRhc2FfdG90YWwpDQpwY3RfcG9iX2V4dCA8LSBzdW0oYXRpcGljb3MkcG9ibGFjaW9uKSAvIHN1bShiYWphX2NvbiRwb2JsYWNpb24pDQoNCiMzLjEgdGFibGEgZGUgYXRpcGljb3MgZXh0cmVtb3MNCg0KdGFibGFfZXh0cmVtb3MgPC0gYXRpcGljb3MgJT4lICAgICAgIyB5YSB2aWVuZSBvcmRlbmFkYSBkZSBtYXlvciBhIG1lbm9yIHRhc2ENCiAgdHJhbnNtdXRlKA0KICAgIGAjYCAgICAgICAgICA9IHJvd19udW1iZXIoKSwNCiAgICBVUFogICAgICAgICAgPSB1cHpfbm9tYnJlLA0KICAgIExvY2FsaWRhZCAgICA9IGxvY2FsaWRhZCwNCiAgICBQb2JsYWNpw7NuICAgID0gbnVtKHBvYmxhY2lvbiksDQogICAgYENhc29zIHRvdGFsZXNgID0gbnVtKGNhc29zX3RvdGFsKSwNCiAgICBgVGFzYSB0b3RhbCAocG9yIDEwLjAwMCBoYWIuKWAgPSBudW0odGFzYV90b3RhbCwgYWNjdXJhY3kgPSAwLjEpLA0KICAgIGBWZWNlcyBsYSBtZWRpYW5hIGRlbCBncnVwb2AgICA9IHBhc3RlMChudW0odGFzYV90b3RhbCAvIG1lZGlhbihiYWphX2NvbiR0YXNhX3RvdGFsKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjY3VyYWN5ID0gMC4xKSwgIngiKQ0KICApDQoNCnRhYmxhX2Jvbml0YSh0YWJsYV9leHRyZW1vcykNCg0KIyA0LiBWaXN0YXMgMiB5IDM6IHBvYmxhY2nDs24gbWVkaWEgeSBhbHRhIC0tLS0NCnByZXBhcmFyIDwtIGZ1bmN0aW9uKGcpIHsNCiAgcmVzdW1pcih1cHogJT4lIGZpbHRlcihncnVwb19wb2JsYWNpb24gPT0gZykpICU+JQ0KICAgIG11dGF0ZSgNCiAgICAgIHBjdCAgICAgID0gY2Fzb3MgLyBzdW0oY2Fzb3MpLA0KICAgICAgZXNfbWF4ICAgPSBjYXNvcyA9PSBtYXgoY2Fzb3MpLA0KICAgICAgZXRpcXVldGEgPSBpZmVsc2UoZXNfbWF4LCBwYXN0ZTAoZGVsaXRvLCAiICDimIUiKSwgZGVsaXRvKSwNCiAgICAgIHRleHRvID0gcGFzdGUwKCI8Yj4iLCBkZWxpdG8sICI8L2I+IiwNCiAgICAgICAgICAgICAgICAgICAgICI8YnI+Q2Fzb3M6ICIsIG51bShjYXNvcyksICIgKCIsIHBjKHBjdCksICIgZGVsIHRvdGFsKSIsDQogICAgICAgICAgICAgICAgICAgICAiPGJyPlRhc2E6ICIsIG51bSh0YXNhLCBhY2N1cmFjeSA9IDAuMSksICIgcG9yIDEwLjAwMCBoYWIuIikNCiAgICApICU+JQ0KICAgIGFycmFuZ2UoZGVzYyhjYXNvcykpDQp9DQoNCmRfbWVkaWEgPC0gcHJlcGFyYXIoIlBvYmxhY2nDs24gbWVkaWEiKQ0KZF9hbHRhICA8LSBwcmVwYXJhcigiUG9ibGFjacOzbiBhbHRhIikNCg0Kc3RvcGlmbm90KA0KICBzdW0oYmFqYV9jb24kY2Fzb3NfaHVydG9fdG90YWwpID09IGNvbXAkY2Fzb3NfY29uW2NvbXAkZGVsaXRvID09ICJIdXJ0byJdLA0KICBzdW0oYmFqYV9zaW4kY2Fzb3NfaHVydG9fdG90YWwpID09IGNvbXAkY2Fzb3Nfc2luW2NvbXAkZGVsaXRvID09ICJIdXJ0byJdLA0KICBucm93KGJhamFfY29uKSAtIG5yb3coYmFqYV9zaW4pID09IG5fYXRpcCwNCiAgc3VtKGRfbWVkaWEkY2Fzb3MpID09IHN1bSh1cHokY2Fzb3NfdG90YWxbdXB6JGdydXBvX3BvYmxhY2lvbiA9PSAiUG9ibGFjacOzbiBtZWRpYSJdKSwNCiAgc3VtKGRfYWx0YSRjYXNvcykgID09IHN1bSh1cHokY2Fzb3NfdG90YWxbdXB6JGdydXBvX3BvYmxhY2lvbiA9PSAiUG9ibGFjacOzbiBhbHRhIl0pDQopDQoNCnJhbmdvX21lZGlhIDwtIG1heChkX21lZGlhJGNhc29zKSAqIDEuMQ0KcmFuZ29fYWx0YSAgPC0gbWF4KGRfYWx0YSRjYXNvcykgKiAxLjENCg0KcHVudG9zX21lZGlhIDwtIGV4cGFuZGlyKGRfbWVkaWEsIHJhbmdvX21lZGlhKQ0KcHVudG9zX2FsdGEgIDwtIGV4cGFuZGlyKGRfYWx0YSwgcmFuZ29fYWx0YSkNCg0KIyA1LiBDT01FTlRBUklPUyAtLS0tDQojID4+PiBBUVXDjSBFU0NSSUJFUyBUVSBDT01FTlRBUklPIFBBUkEgQ0FEQSBWSVNUQSAocHVlZGUgc2VyIHRhbiBsYXJnbyBjb21vIHF1aWVyYXMpIDw8PA0KIyBTZSBtdWVzdHJhIGNvbXBsZXRvLCBkZSBsYWRvIGEgbGFkbywgc29sbyBjdWFuZG8gZWxpZ2VzIGVzYSB2aXN0YSBlbiBlbCBtZW7Dui4NCiMgRMOpamFsbyAiIiBzaSBubyBxdWllcmVzIGFncmVnYXIgbmFkYS4NCm1pc19jb21lbnRhcmlvcyA8LSBjKA0KICAiUG9ibGFjacOzbiBiYWphOiBjb24gdnMgc2luIGF0w61waWNvcyIgPSAiU2kgc2UgcXVpdGFuIGxvcyA3IGF0w61waWNvcyBleHRyZW1vcyBubyBnZW5lcmEgdW4gY2FtYmlvIGRlIHJhbmtpbmcgZW50cmUgZGVsaXRvcyBlbiBsYSBwb2JsYWNpw7NuIGJhamEsIHBlcm8gbG8gcXVlIHPDrSBjYW1iaWEgc29uIGxhcyB0YXNhcyBkZSBkZWxpdG9zIHkgbG9zIHByb21lZGlvcywgZW4gZG9uZGUgbG9zIGNhc29zIGRlIGh1cnRvIGJhamFuIGRlIHRhc2EgMjg4LjUgYSAyNjEuNSBwb3IgY2FkYSAxMC4wMDAgaGFiaXRhbnRlcywgZXN0ZSBjYW1iaW8gZW4gbGEgdGFzYSBwYXNhIGRlYmlkbyBxdWUgZXN0YXMgVVBaIHRpZW5lbiBtdXkgcG9jYXMgcGVyc29uYXMgeSBtdWNoYXMgbcOhcyBkZW51bmNpYXMsIGVuIGxvcyBvdHJvcyBkZWxpdG9zLCBzZSBwcmVzZW50YSB2YXJpYWNpw7NuIHBlcm8gbm8gZXMgdGFuIGNvbnNpZGVyYWJsZSBjb21vIGVsIHJlc3VsdGFkbyBkZSBIdXJ0byIsDQogICJQb2JsYWNpw7NuIG1lZGlhIiAgICAgICAgICAgICAgICAgICAgID0gIkZyZW50ZSBhIHBvYmxhY2nDs24gYmFqYSwgbGEgdGFzYSBkZSBodXJ0byBiYWphIGEgbWVub3MgZGUgbGEgbWl0YWQgKDI4OCw1ID4gMTIyLDQpLiBBZGVtw6FzLCBhcXXDrSBubyBoYXkgdmFsb3JlcyBhdMOtcGljb3M6IGxhcyBVUFogc2UgcGFyZWNlbiBiYXN0YW50ZSBlbnRyZSBzw60sIGNvbiB0YXNhcyB0b3RhbGVzIHF1ZSB2YW4gZGUgNzIgYSAzMTIuIiwNCiAgIlBvYmxhY2nDs24gYWx0YSIgICAgICAgICAgICAgICAgICAgICAgPSAiTG8gcXVlIHPDrSBjYW1iaWEgZXMgZWwgcGVzbyBkZSBsYSB2aW9sZW5jaWEgaW50cmFmYW1pbGlhcjogcGFzYSBkZSA2LDYgJSBlbiBwb2JsYWNpw7NuIGJhamEgYSAxOSw5ICUgYXF1w60sIHkgc3UgdGFzYSAyMyw5IGVzIGxhIG3DoXMgYWx0YSBkZSBsb3MgdHJlcyBncnVwb3MsIHNpZW5kb3Mgc29sbyA1IFVQWi4iDQopDQoNCm1vc3RyYXJfYXV0b21hdGljbyA8LSBUUlVFICAgIyBGQUxTRSA9IG11ZXN0cmEgc29sbyB0dXMgY29tZW50YXJpb3MNCg0KdmlzdGFzIDwtIG5hbWVzKG1pc19jb21lbnRhcmlvcykNCg0KIyB2aXN0YSAxDQp0b3BfYiA8LSBjb21wICU+JSBmaWx0ZXIoZXNfbWF4KQ0Kbm9tYnJlc19hdGlwIDwtIHBhc3RlMChhdGlwaWNvcyR1cHpfbm9tYnJlLCAiICgiLCBudW0oYXRpcGljb3MkcG9ibGFjaW9uKSwgIiBoYWIuKSIsIGNvbGxhcHNlID0gIiwgIikNCnBjdF90b3BfY29uICA8LSB0b3BfYiRjYXNvc19jb24gLyBzdW0oY29tcCRjYXNvc19jb24pDQpwY3RfdG9wX3NpbiAgPC0gdG9wX2IkY2Fzb3Nfc2luIC8gc3VtKGNvbXAkY2Fzb3Nfc2luKQ0KDQpjb21lbnRhcmlvX2JhamEgPC0gYygNCiAgaWYgKG1vc3RyYXJfYXV0b21hdGljbykgYygNCiAgICBwYXN0ZTAoIjxiPkF0w61waWNvcyBleHRyZW1vcyByZXRpcmFkb3MgKCIsIG5fYXRpcCwgIik6PC9iPiAiLCBub21icmVzX2F0aXAsDQogICAgICAgICAgICIuIEp1bnRvcyB0aWVuZW4gc29sbyBlbCAiLCBwYyhwY3RfcG9iX2V4dCksICIgZGUgbGEgcG9ibGFjacOzbiBkZWwgZ3J1cG8uIiksDQogICAgcGFzdGUwKCI8Yj5FZmVjdG8gZW4gbG9zIHRvdGFsZXM6PC9iPiBlbCAiLCB0b2xvd2VyKHRvcF9iJGRlbGl0byksICIgcGFzYSBkZSAiLA0KICAgICAgICAgICBudW0odG9wX2IkY2Fzb3NfY29uKSwgIiBhICIsIG51bSh0b3BfYiRjYXNvc19zaW4pLCAiIGNhc29zICgiLCBwYyh0b3BfYiRkaWZfY2Fzb3MpLA0KICAgICAgICAgICAiKSB5IHN1IHRhc2EgZGUgIiwgbnVtKHRvcF9iJHRhc2FfY29uLCBhY2N1cmFjeSA9IDAuMSksICIgYSAiLCBudW0odG9wX2IkdGFzYV9zaW4sIGFjY3VyYWN5ID0gMC4xKSwgIi4iKSwNCiAgICBwYXN0ZTAoIjxiPkxvIHF1ZSBubyBjYW1iaWE6PC9iPiBlbCAiLCB0b2xvd2VyKHRvcF9iJGRlbGl0byksICIgc2lndWUgc2llbmRvIGVsIGRlbGl0byBtw6FzIGZyZWN1ZW50ZSAoIiwNCiAgICAgICAgICAgcGMocGN0X3RvcF9jb24pLCAiIGRlbCB0b3RhbCBjb24gYXTDrXBpY29zIHkgIiwgcGMocGN0X3RvcF9zaW4pLCAiIHNpbiBlbGxvcykuIiksDQogICAgcGFzdGUwKCI8Yj5Qcm9tZWRpbyB2cyBtZWRpYW5hIGRlIGxhIHRhc2EgcG9yIFVQWjo8L2I+IGVsIHByb21lZGlvIHBhc2EgZGUgIiwgbnVtKHByb21fY29uLCBhY2N1cmFjeSA9IDAuMSksDQogICAgICAgICAgICIgYSAiLCBudW0ocHJvbV9zaW4sIGFjY3VyYWN5ID0gMC4xKSwgIiAoIiwgcGMocHJvbV9zaW4gLyBwcm9tX2NvbiAtIDEpLA0KICAgICAgICAgICAiKSwgbWllbnRyYXMgbGEgbWVkaWFuYSBwYXNhIGRlICIsIG51bShtZWRfY29uLCBhY2N1cmFjeSA9IDAuMSksICIgYSAiLA0KICAgICAgICAgICBudW0obWVkX3NpbiwgYWNjdXJhY3kgPSAwLjEpLCAiICgiLCBwYyhtZWRfc2luIC8gbWVkX2NvbiAtIDEpLCAiKS4iKQ0KICApLA0KICBpZiAobnpjaGFyKG1pc19jb21lbnRhcmlvc1tbdmlzdGFzWzFdXV0pKSBwYXN0ZTAoIjxiPkNvbWVudGFyaW86PC9iPiAiLCBtaXNfY29tZW50YXJpb3NbW3Zpc3Rhc1sxXV1dKQ0KKQ0KDQojIHZpc3RhcyAyIHkgMw0KY29tZW50YXJpb19ncnVwbyA8LSBmdW5jdGlvbihnLCBkKSB7DQogIHRvcCA8LSBkWzEsIF07IHNlZyA8LSBkWzIsIF07IHVsdCA8LSBkW25yb3coZCksIF0NCiAgbl91cHogPC0gc3VtKHVweiRncnVwb19wb2JsYWNpb24gPT0gZykNCiAgYygNCiAgICBpZiAobW9zdHJhcl9hdXRvbWF0aWNvKSBjKA0KICAgICAgcGFzdGUwKCI8Yj5Db250ZXh0bzo8L2I+ICIsIG5fdXB6LCAiIFVQWiB5ICIsIG51bSh0b3AkcG9ibGFjaW9uKSwgIiBoYWJpdGFudGVzIGVuIGVzdGUgZ3J1cG8uIiksDQogICAgICBwYXN0ZTAoIjxiPk3DoXMgZnJlY3VlbnRlOjwvYj4gIiwgdG9sb3dlcih0b3AkZGVsaXRvKSwgIiwgY29uICIsIG51bSh0b3AkY2Fzb3MpLA0KICAgICAgICAgICAgICIgY2Fzb3MgKCIsIHBjKHRvcCRwY3QpLCAiIGRlbCB0b3RhbCkgeSB1bmEgdGFzYSBkZSAiLCBudW0odG9wJHRhc2EsIGFjY3VyYWN5ID0gMC4xKSwNCiAgICAgICAgICAgICAiIHBvciBjYWRhIDEwLjAwMCBoYWJpdGFudGVzLiIpLA0KICAgICAgcGFzdGUwKCI8Yj5MZSBzaWd1ZTo8L2I+ICIsIHRvbG93ZXIoc2VnJGRlbGl0byksICIsIGNvbiAiLCBudW0oc2VnJGNhc29zKSwNCiAgICAgICAgICAgICAiIGNhc29zICgiLCBwYyhzZWckcGN0KSwgIikuIiksDQogICAgICBwYXN0ZTAoIjxiPk1lbm9zIGZyZWN1ZW50ZTo8L2I+ICIsIHRvbG93ZXIodWx0JGRlbGl0byksICIsIGNvbiAiLCBudW0odWx0JGNhc29zKSwNCiAgICAgICAgICAgICAiIGNhc29zICgiLCBwYyh1bHQkcGN0KSwgIikuIikNCiAgICApLA0KICAgIGlmIChuemNoYXIobWlzX2NvbWVudGFyaW9zW1tnXV0pKSBwYXN0ZTAoIjxiPkNvbWVudGFyaW86PC9iPiAiLCBtaXNfY29tZW50YXJpb3NbW2ddXSkNCiAgKQ0KfQ0KDQpjb21lbnRhcmlvcyA8LSBsaXN0KGNvbWVudGFyaW9fYmFqYSwNCiAgICAgICAgICAgICAgICAgICAgY29tZW50YXJpb19ncnVwbygiUG9ibGFjacOzbiBtZWRpYSIsIGRfbWVkaWEpLA0KICAgICAgICAgICAgICAgICAgICBjb21lbnRhcmlvX2dydXBvKCJQb2JsYWNpw7NuIGFsdGEiLCAgZF9hbHRhKSkNCg0KIyA2LiBHcsOhZmljbyAtLS0tDQpvcCA8LSBmdW5jdGlvbihkKSBpZmVsc2UoZCRlc19tYXgsIDEsIDAuNDUpICAgIyBlbCBkZWxpdG8gbcOhcyBmcmVjdWVudGUgcXVlZGEgcmVzYWx0YWRvDQppbnZpc2libGVfbWFya2VyIDwtIGxpc3QoY29sb3IgPSAicmdiYSgwLDAsMCwwKSIsIHNpemUgPSAxMikNCg0KIyB2aXN0YXMgMiB5IDM6IGJhcnJhcyArIHB1bnRvcyBpbnZpc2libGVzIHBhcmEgZWwgY3VhZHJvIGRlbCBtb3VzZSAobWlzbWEgZXN0cnVjdHVyYSwgY2FtYmlhbiBkYXRvcyB5IGNvbG9yKQ0KYmFycmFzX3Zpc3RhIDwtIGZ1bmN0aW9uKGZpZywgZCwgcHVudG9zLCBub21icmUsIGNvbG9yKSB7DQogIGZpZyAlPiUNCiAgICBhZGRfdHJhY2UoZGF0YSA9IGQsIHR5cGUgPSAiYmFyIiwgb3JpZW50YXRpb24gPSAiaCIsIG5hbWUgPSBub21icmUsDQogICAgICAgICAgICAgIHggPSB+Y2Fzb3MsIHkgPSB+ZXRpcXVldGEsDQogICAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSBjb2xvciwgb3BhY2l0eSA9IG9wKGQpKSwNCiAgICAgICAgICAgICAgaG92ZXJpbmZvID0gInNraXAiLCB2aXNpYmxlID0gRkFMU0UsIHNob3dsZWdlbmQgPSBGQUxTRSkgJT4lDQogICAgYWRkX3RyYWNlKGRhdGEgPSBwdW50b3MsIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIsDQogICAgICAgICAgICAgIHggPSB+eCwgeSA9IH5ldGlxdWV0YSwgbWFya2VyID0gaW52aXNpYmxlX21hcmtlciwNCiAgICAgICAgICAgICAgaG92ZXJ0ZXh0ID0gfnRleHRvLCBob3ZlcmluZm8gPSAidGV4dCIsIHZpc2libGUgPSBGQUxTRSwgc2hvd2xlZ2VuZCA9IEZBTFNFKQ0KfQ0KDQpmaWcgPC0gcGxvdF9seSgpICU+JQ0KICAjIC0tLSB2aXN0YSAxICh0cmF6YXMgMS0zKQ0KICBhZGRfdHJhY2UoZGF0YSA9IGNvbXAsIHR5cGUgPSAiYmFyIiwgb3JpZW50YXRpb24gPSAiaCIsIG5hbWUgPSAiQ29uIGF0w61waWNvcyBleHRyZW1vcyIsDQogICAgICAgICAgICB4ID0gfmNhc29zX2NvbiwgeSA9IH5ldGlxdWV0YSwNCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSBtYWdlbnRhLCBvcGFjaXR5ID0gb3AoY29tcCkpLA0KICAgICAgICAgICAgaG92ZXJpbmZvID0gInNraXAiLCB2aXNpYmxlID0gVFJVRSwgc2hvd2xlZ2VuZCA9IFRSVUUpICU+JQ0KICBhZGRfdHJhY2UoZGF0YSA9IGNvbXAsIHR5cGUgPSAiYmFyIiwgb3JpZW50YXRpb24gPSAiaCIsIG5hbWUgPSAiU2luIGF0w61waWNvcyBleHRyZW1vcyIsDQogICAgICAgICAgICB4ID0gfmNhc29zX3NpbiwgeSA9IH5ldGlxdWV0YSwNCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSBtb3JhZG8sIG9wYWNpdHkgPSBvcChjb21wKSksDQogICAgICAgICAgICBob3ZlcmluZm8gPSAic2tpcCIsIHZpc2libGUgPSBUUlVFLCBzaG93bGVnZW5kID0gVFJVRSkgJT4lDQogIGFkZF90cmFjZShkYXRhID0gcHVudG9zX2JhamEsIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIsDQogICAgICAgICAgICB4ID0gfngsIHkgPSB+ZXRpcXVldGEsIG1hcmtlciA9IGludmlzaWJsZV9tYXJrZXIsDQogICAgICAgICAgICBob3ZlcnRleHQgPSB+dGV4dG8sIGhvdmVyaW5mbyA9ICJ0ZXh0IiwgdmlzaWJsZSA9IFRSVUUsIHNob3dsZWdlbmQgPSBGQUxTRSkgJT4lDQogICMgLS0tIHZpc3RhIDI6IHBvYmxhY2nDs24gbWVkaWEgKHRyYXphcyA0LTUpIHkgdmlzdGEgMzogcG9ibGFjacOzbiBhbHRhICh0cmF6YXMgNi03KQ0KICBiYXJyYXNfdmlzdGEoZF9tZWRpYSwgcHVudG9zX21lZGlhLCAiUG9ibGFjacOzbiBtZWRpYSIsIHR1cnF1ZXNhKSAlPiUNCiAgYmFycmFzX3Zpc3RhKGRfYWx0YSwgIHB1bnRvc19hbHRhLCAgIlBvYmxhY2nDs24gYWx0YSIsICBtb3JhZG8pDQoNCnRpdHVsb3MgPC0gYyhwYXN0ZTAoIkRlbGl0b3MgUG9ibGFjacOzbiBiYWphOiBjb24geSBzaW4gbG9zICIsIG5fYXRpcCwgIiB2YWxvcmVzIGF0w61waWNvcyBleHRyZW1vcywgSHVydG86IERlbGl0byBxdWUgcHJlZG9taW5hIGNvbiB5IHNpbiBsb3MgIiwgbl9hdGlwLCAiIGF0w61waWNvcyBleHRyZW1vcy4iKSwNCiAgICAgICAgICAgICAiRGVsaXRvcyBlbiBwb2JsYWNpw7NuIG1lZGlhLCBIdXJ0byBjb250aWVuZSBlbCA4MCUgZGUgbG9zIGNhc29zLiIsDQogICAgICAgICAgICAgIkRlbGl0b3MgZW4gcG9ibGFjacOzbiBhbHRhLCBIdXJ0byBwcmVkb21pbmEsIHBlcm8gdmlvbGVuY2lhIGludHJhZmFtaWxpYXIgcHJlc2VudGEgMjAlIGRlIGxvcyBjYXNvcyBzaWVuZG8gc29sbyA1IFVQWi4iKQ0Kc3VidGl0dWxvcyA8LSBjKCJDYW50aWRhZCBkZSBjYXNvcyDCtyDimIUgeSBjb2xvciBmdWVydGUgPSBlbCBkZWxpdG8gcXVlIG3DoXMgc2UgcHJlc2VudGEgwrcgcGFzYSBlbCBjdXJzb3IgcGFyYSB2ZXIgZWwgZGV0YWxsZSIsDQogICAgICAgICAgICAgICAgIkNhbnRpZGFkIGRlIGNhc29zIMK3IOKYhSB5IGNvbG9yIGZ1ZXJ0ZSA9IGVsIGRlbGl0byBxdWUgbcOhcyBzZSBwcmVzZW50YSDCtyBwYXNhIGVsIGN1cnNvciBwYXJhIHZlciBjYXNvcyB5IHRhc2EiLA0KICAgICAgICAgICAgICAgICJDYW50aWRhZCBkZSBjYXNvcyDCtyDimIUgeSBjb2xvciBmdWVydGUgPSBlbCBkZWxpdG8gcXVlIG3DoXMgc2UgcHJlc2VudGEgwrcgcGFzYSBlbCBjdXJzb3IgcGFyYSB2ZXIgY2Fzb3MgeSB0YXNhIikNCnJhbmdvcyAgPC0gYyhyYW5nb19iYWphLCByYW5nb19tZWRpYSwgcmFuZ29fYWx0YSkNCm9yZGVuZXMgPC0gbGlzdChjb21wJGV0aXF1ZXRhLCBkX21lZGlhJGV0aXF1ZXRhLCBkX2FsdGEkZXRpcXVldGEpDQoNCnRpdHVsbyA8LSBmdW5jdGlvbihpKSBwYXN0ZTAoIjxiPiIsIHBhcnRpcl90aXR1bG8odGl0dWxvc1tpXSksICI8L2I+PGJyPjxzdXA+Iiwgc3VidGl0dWxvc1tpXSwgIjwvc3VwPiIpDQoNCiMgNyB0cmF6YXM6IDEtMyB2aXN0YSAxLCA0LTUgdmlzdGEgMiwgNi03IHZpc3RhIDMNCnRyYXphc19wb3JfdmlzdGEgPC0gYygzLCAyLCAyKQ0KdmlzaWJpbGlkYWQgPC0gZnVuY3Rpb24oaSkgcmVwKHNlcV9hbG9uZyh2aXN0YXMpID09IGksIHRpbWVzID0gdHJhemFzX3Bvcl92aXN0YSkNCg0KYm90b25lcyA8LSBsYXBwbHkoc2VxX2Fsb25nKHZpc3RhcyksIGZ1bmN0aW9uKGkpIHsNCiAgbGlzdCgNCiAgICBtZXRob2QgPSAidXBkYXRlIiwNCiAgICBsYWJlbCAgPSB2aXN0YXNbaV0sDQogICAgYXJncyAgID0gbGlzdCgNCiAgICAgIGxpc3QodmlzaWJsZSA9IHZpc2liaWxpZGFkKGkpKSwNCiAgICAgIGxpc3QoInRpdGxlLnRleHQiICAgICAgICAgID0gdGl0dWxvKGkpLA0KICAgICAgICAgICAieGF4aXMucmFuZ2UiICAgICAgICAgPSBsaXN0KDAsIHJhbmdvc1tpXSksDQogICAgICAgICAgICJ5YXhpcy5jYXRlZ29yeWFycmF5IiA9IHJldihvcmRlbmVzW1tpXV0pKQ0KICAgICkNCiAgKQ0KfSkNCg0KZmlnIDwtIGZpZyAlPiUNCiAgbGF5b3V0KA0KICAgIGhlaWdodCA9IDUyMCwNCiAgICB0aXRsZSAgPSB0aXR1bG9fcGxvdGx5KHRpdHVsbygxKSksDQogICAgeGF4aXMgID0gbGlzdCh0aXRsZSA9ICJDYW50aWRhZCBkZSBjYXNvcyIsIHJhbmdlID0gYygwLCByYW5nb3NbMV0pLA0KICAgICAgICAgICAgICAgICAgZ3JpZGNvbG9yID0gIiNFRUVCRjAiLCB6ZXJvbGluZSA9IEZBTFNFKSwNCiAgICB5YXhpcyAgPSBsaXN0KHRpdGxlID0gIiIsIGNhdGVnb3J5b3JkZXIgPSAiYXJyYXkiLA0KICAgICAgICAgICAgICAgICAgY2F0ZWdvcnlhcnJheSA9IHJldihvcmRlbmVzW1sxXV0pLCB0aWNrZm9udCA9IGxpc3Qoc2l6ZSA9IDEzKSksDQogICAgYmFybW9kZSA9ICJncm91cCIsDQogICAgYmFyZ2FwICA9IDAuMywNCiAgICBob3Zlcm1vZGUgICAgID0gImNsb3Nlc3QiLA0KICAgIGhvdmVyZGlzdGFuY2UgPSA0MCwNCiAgICBob3ZlcmxhYmVsID0gaG92ZXJfcGxvdGx5LA0KICAgIHVwZGF0ZW1lbnVzID0gbGlzdChtZW51X3Bsb3RseShib3RvbmVzKSksDQogICAgbGVnZW5kID0gbGlzdChvcmllbnRhdGlvbiA9ICJoIiwgeCA9IDEsIHhhbmNob3IgPSAicmlnaHQiLCB5ID0gMS4wNCwgeWFuY2hvciA9ICJib3R0b20iKSwNCiAgICBtYXJnaW4gPSBsaXN0KHQgPSAxNDAsIGIgPSA3MCwgbCA9IDEwLCByID0gMzApLA0KICAgIHNob3dsZWdlbmQgPSBUUlVFDQogICkgJT4lDQogIGVzdGlsb19wbG90bHkoKQ0KDQojIDcuIENhamEgZGUgY29tZW50YXJpb3MgZGUgbGFkbyBhIGxhZG8gKGRlYmFqbyBkZWwgZ3LDoWZpY28pIC0tLS0NCiMgU2UgY2FtYmlhIHNvbGEgc2Vnw7puIGxhIHNlZ21lbnRhY2nDs24gZWxlZ2lkYSBlbiBlbCBtZW7DuiAoanNfY29tZW50YXJpb3MgZXN0w6EgZW4gZWwgY2h1bmsgImVzdGlsbyIpDQpmaWcgPC0gaHRtbHdpZGdldHM6Om9uUmVuZGVyKA0KICBmaWcsIGpzX2NvbWVudGFyaW9zLA0KICBkYXRhID0gbGlzdChjb21lbnRhcmlvcyA9IGNvbWVudGFyaW9zLA0KICAgICAgICAgICAgICBpbmljaW9zID0gYygwLCBjdW1zdW0odHJhemFzX3Bvcl92aXN0YSlbLWxlbmd0aCh0cmF6YXNfcG9yX3Zpc3RhKV0pKSAgICMgdHJhemEgaW5pY2lhbCBkZSBjYWRhIHZpc3RhDQopDQoNCmZpZw0KYGBgDQoNCiMjIDQuNCDCv0PDs21vIHNlIHJlbGFjaW9uYSBsYSBjb21wb3NpY2nDs24gcG9yIGVkYWQgY29uIGxhcyB0YXNhcyBkZSBkZWxpdG8geSBlc2EgcmVsYWNpw7NuIGNhbWJpYSBzZWfDum4gZWwgZ3J1cG8gcG9ibGFjaW9uYWw/DQoNCg0KYGBge3IgZ3JhZmljb19jb3JyZWxhY2lvbl9ncnVwb3NfZWRhZGVzX3ZzX2RlbGl0b3NfeF9zZWdtZW50YWNpb25fcG9ibGFjaW9uYWx9DQojIDEuIFZhcmlhYmxlczogZ3J1cG9zIGRlIGVkYWQgeSBkZWxpdG9zIChodXJ0byB0b3RhbCB5IHBjdF8zMF81OSBzZSBjcmVhbiBlbiBlbCBjaHVuayAicHJlcGFyYXJfZGF0b3MiKSAtLS0tDQoNCmVkYWRlcyA8LSBjKHBjdF8wXzE0ICAgPSAiMOKAkzE0IGHDsW9zIiwNCiAgICAgICAgICAgIHBjdF8xNV8yOSAgPSAiMTXigJMyOSBhw7FvcyIsDQogICAgICAgICAgICBwY3RfMzBfNTkgID0gIjMw4oCTNTkgYcOxb3MiLA0KICAgICAgICAgICAgcGN0XzYwX21hcyA9ICI2MCBhw7FvcyBvIG3DoXMiKQ0KDQpkZWxpdG9zX2NvcnIgPC0gYyh0YXNhX2h1cnRvX3RvdGFsICAgICAgICAgICAgPSAiSHVydG8iLA0KICAgICAgICAgICAgICAgICAgdGFzYV9sZXNpb25lcyAgICAgICAgICAgICAgID0gIkxlc2lvbmVzIiwNCiAgICAgICAgICAgICAgICAgIHRhc2FfaG9taWNpZGlvcyAgICAgICAgICAgICA9ICJIb21pY2lkaW9zIiwNCiAgICAgICAgICAgICAgICAgIHRhc2FfdmlvbGVuY2lhX2ludHJhZmFtaWxpYXIgPSAiVmlvbGVuY2lhIGludHJhZmFtaWxpYXIiKQ0KDQojIDIuIENvcnJlbGFjacOzbiBkZSBTcGVhcm1hbiBwb3Igc2VnbWVudGFjacOzbiAtLS0tDQpjb3Jfc3BlYXJtYW4gPC0gZnVuY3Rpb24oeCwgeSkgew0KICByIDwtIHRyeUNhdGNoKHN1cHByZXNzV2FybmluZ3MoY29yLnRlc3QoeCwgeSwgbWV0aG9kID0gInNwZWFybWFuIiwgZXhhY3QgPSBGQUxTRSkpLA0KICAgICAgICAgICAgICAgIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTCkNCiAgaWYgKGlzLm51bGwocikpIGMocmhvID0gTkFfcmVhbF8sIHAgPSBOQV9yZWFsXykNCiAgZWxzZSBjKHJobyA9IHVubmFtZShyJGVzdGltYXRlKSwgcCA9IHIkcC52YWx1ZSkNCn0NCg0KZnVlcnphIDwtIGZ1bmN0aW9uKHIpIHsNCiAgYSA8LSBhYnMocikNCiAgaWZlbHNlKGlzLm5hKHIpLCAic2luIGRhdG8iLA0KICBpZmVsc2UoYSA8IDAuMiwgIm11eSBkw6liaWwiLA0KICBpZmVsc2UoYSA8IDAuNCwgImTDqWJpbCIsDQogIGlmZWxzZShhIDwgMC42LCAibW9kZXJhZGEiLA0KICBpZmVsc2UoYSA8IDAuOCwgImZ1ZXJ0ZSIsICJtdXkgZnVlcnRlIikpKSkpDQp9DQoNCmNvcnJlbGFjaW9uZXMgPC0gZXhwYW5kX2dyaWQoZ3J1cG8gPSBncnVwb3MsIHZkID0gbmFtZXMoZGVsaXRvc19jb3JyKSwgdmUgPSBuYW1lcyhlZGFkZXMpKSAlPiUNCiAgbXV0YXRlKA0KICAgIHIgICAgICA9IHBtYXAobGlzdChncnVwbywgdmQsIHZlKSwgZnVuY3Rpb24oZywgdmQsIHZlKSB7DQogICAgICAgICAgICAgICBzIDwtIHVwelt1cHokZ3J1cG9fcG9ibGFjaW9uID09IGcsIF0NCiAgICAgICAgICAgICAgIGNvcl9zcGVhcm1hbihzW1t2ZV1dLCBzW1t2ZF1dKQ0KICAgICAgICAgICAgIH0pLA0KICAgIHJobyAgICA9IG1hcF9kYmwociwgfiAueFtbInJobyJdXSksDQogICAgcCAgICAgID0gbWFwX2RibChyLCB+IC54W1sicCJdXSksDQogICAgbiAgICAgID0gbWFwX2ludChncnVwbywgfiBzdW0odXB6JGdydXBvX3BvYmxhY2lvbiA9PSAueCkpLA0KICAgIGRlbGl0byA9IHVubmFtZShkZWxpdG9zX2NvcnJbdmRdKSwNCiAgICBlZGFkICAgPSB1bm5hbWUoZWRhZGVzW3ZlXSkNCiAgKSAlPiUNCiAgc2VsZWN0KGdydXBvLCBkZWxpdG8sIGVkYWQsIHJobywgcCwgbikgJT4lDQogIG11dGF0ZSgNCiAgICBzaWduaWYgPSAhaXMubmEocCkgJiBwIDwgMC4wNSwNCiAgICBldGlxdWV0YV9jZWxkYSA9IGNhc2Vfd2hlbigNCiAgICAgIGlzLm5hKHJobykgfiAiIiwNCiAgICAgIHNpZ25pZiAgICAgfiBwYXN0ZTAoIjxiPiIsIG51bShyaG8sIGFjY3VyYWN5ID0gMC4wMSksICIqPC9iPiIpLA0KICAgICAgVFJVRSAgICAgICB+IG51bShyaG8sIGFjY3VyYWN5ID0gMC4wMSkNCiAgICApLA0KICAgIHRleHRvID0gcGFzdGUwKA0KICAgICAgIjxiPiIsIGRlbGl0bywgIiDCtyAiLCBlZGFkLCAiPC9iPiIsDQogICAgICAiPGJyPkNvcnJlbGFjacOzbiAoU3BlYXJtYW4pOiAiLCBudW0ocmhvLCBhY2N1cmFjeSA9IDAuMDEpLA0KICAgICAgIjxicj5SZWxhY2nDs24gIiwgZnVlcnphKHJobyksIGlmZWxzZShyaG8gPj0gMCwgIiBwb3NpdGl2YSIsICIgbmVnYXRpdmEiKSwNCiAgICAgICI8YnI+cC12YWxvcjogIiwgaWZlbHNlKHAgPCAwLjAwMSwgIjwgMCwwMDEiLCBudW0ocCwgYWNjdXJhY3kgPSAwLjAwMSkpLA0KICAgICAgIjxicj4iLCBpZmVsc2Uoc2lnbmlmLCAiRXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YSIsICJObyBzaWduaWZpY2F0aXZhIiksDQogICAgICAiPGJyPlVQWiBhbmFsaXphZGFzOiAiLCBuDQogICAgKQ0KICApDQoNCiMgMy4gQ09NRU5UQVJJT1MgLS0tLQ0KIyA+Pj4gQVFVw40gRVNDUklCRVMgVFUgQ09NRU5UQVJJTyBQQVJBIENBREEgUE9CTEFDScOTTiAocHVlZGUgc2VyIHRhbiBsYXJnbyBjb21vIHF1aWVyYXMpIDw8PA0KIyBTZSBtdWVzdHJhIGNvbXBsZXRvLCBkZSBsYWRvIGEgbGFkbywgc29sbyBjdWFuZG8gZWxpZ2VzIGVzYSBwb2JsYWNpw7NuIGVuIGVsIG1lbsO6Lg0KIyBEw6lqYWxvICIiIHNpIG5vIHF1aWVyZXMgYWdyZWdhciBuYWRhLg0KbWlzX2NvbWVudGFyaW9zIDwtIGMoDQogICJQb2JsYWNpw7NuIGJhamEiICA9ICJFbCBodXJ0byBlcyBtZW5vciBkb25kZSBoYXkgbcOhcyBuacOxb3MgeSBuacOxYXMsIG1heW9yIGRvbmRlIHByZWRvbWluYSBsYSBwb2JsYWNpw7NuIGRlIDMw4oCTNTkgYcOxb3MuIExhIHZpb2xlbmNpYSBpbnRyYWZhbWlsaWFyIHkgbGVzaW9uZXMgc3ViZW4gY29uIG3DoXMgcG9ibGFjacOzbiBkZSAxNeKAkzI5IGHDsW9zIHkgYmFqYSBjb24gbcOhcyBwb2JsYWNpw7NuIGRlIDMw4oCTNTkuLCBlc3RhcyBhc29jaWFjaW9uZXMgc29uIGdlbmVyYWxlcyBkZSBsYXMgVVBaIGVuIGVzdGUgZ3J1cG8gcG9ibGFjaW9uYWwsIHBlcm8gbm8gYXNlZ3VyYSBxdWUgbGEgZWRhZCBnZW5lcmUgZWwgYXVtZW50byBlbiBsYXMgZGVudW5jaWFzLiIsDQogICJQb2JsYWNpw7NuIG1lZGlhIiA9ICJlcyBkb25kZSBzZSBnZW5lcmFuIGxhcyByZWxhY2lvbmVzIG1hcyBmdWVydGVzLCBkb25kZSBzaSBhdW1lbnRhbiBsYXMgcGVyc29uYXMgZGUgNjAgYcOxb3MsIGxhcyBkZW51bmNpYXMgZW4gaHVydG8gYXVtZW50YW4sIHBvciBlbCBjb250cmFyaW8gbG9zIGhvbWljaWRpb3MgeSBsYSB2aW9sZW5jaWEgaW50cmFmYW1pbGlhciBiYWphIGVuIGxvcyBncnVwb3MgZGUgaWd1YWwgbyBtYXMgZGUgMzAgYcOxb3MsIGVuIGNvbnRyYXBhcnRlLCBhdW1lbnRhIGVuIGxvcyBqb3ZlbmVzIGVzdG9zIGRvcyBkZWxpdG9zLiBpbXBvcnRhbnRlIGFjbGFyYXIgcXVlIFJlbGFjacOzbiBubyBlcyBjYXVzYWxpZGFkIiwNCiAgIlBvYmxhY2nDs24gYWx0YSIgID0gIlNvbG8gdGllbmUgNSBVUFosIHkgZXN0byBwdWVkZSBnZW5lcmFyIGNhbWJpb3MgZHJhc3RpY29zIGVzIGxhcyBjb3JyZWxhY2lvbmVzIGNvbiBzb2xvIGF1bWVudGFyIGVuIHVuYSBVUFosIHBvciB0YWwgbW90aXZvIGVzIGltcG9ydGFudGUgaW5kaWNhciBlbiBsb3Mgam92ZW5lcyBlbCBodXJ0byBlcyBiYWpvIHkgbGEgdmlvbGVuY2lhIGludHJhZmFtaWxpYXIgY3VtcGxlIGVsIG1pc21vIHBhdHJvbiBlbiBlbCBncnVwbyBkZSAzMCAtIDU5IGHDsW9zLCBhdW5xdWUgc2UgZGViZSB2YWxpZGFyIG1hcyBhbCBkZXRhbGxlIGVzdG9zIGRhdG9zLCBkZWJpZG8gcXVlIG5vIHNlIGN1ZW50YSBjb24gZGF0b3MgZXN0YWJsZXMgcXVlIHB1ZWRhbiBnZW5lcmFsaXphci4iDQopDQoNCm1vc3RyYXJfYXV0b21hdGljbyA8LSBGQUxTRSAjQUxTRSA9IG11ZXN0cmEgc29sbyB0dXMgY29tZW50YXJpb3MNCg0KY29tZW50YXJpb19jb3JyIDwtIGZ1bmN0aW9uKGcpIHsNCiAgZCA8LSBjb3JyZWxhY2lvbmVzICU+JSBmaWx0ZXIoZ3J1cG8gPT0gZywgIWlzLm5hKHJobykpDQogIG4gPC0gZCRuWzFdDQogIHBvcyA8LSBkICU+JSBzbGljZV9tYXgocmhvLCBuID0gMSwgd2l0aF90aWVzID0gRkFMU0UpDQogIG5lZyA8LSBkICU+JSBzbGljZV9taW4ocmhvLCBuID0gMSwgd2l0aF90aWVzID0gRkFMU0UpDQoNCiAgYygNCiAgICBpZiAobW9zdHJhcl9hdXRvbWF0aWNvKSBjKA0KICAgICAgcGFzdGUwKCI8Yj5Dw7NtbyBsZWVybG86PC9iPiAiLCBuLCAiIFVQWiBlbiBlc3RlIGdydXBvLiBDYWRhIGNlbGRhIGVzIGxhIGNvcnJlbGFjacOzbiBkZSBTcGVhcm1hbiAiLA0KICAgICAgICAgICAgICJlbnRyZSBlbCAlIGRlIHBvYmxhY2nDs24gZGUgZXNlIGdydXBvIGRlIGVkYWQgeSBsYSB0YXNhIGRlbCBkZWxpdG8gKHBvciAxMC4wMDAgaGFiaXRhbnRlcykuICIsDQogICAgICAgICAgICAgIlZhIGRlIC0xIGEgMTsgbGFzIGNlbGRhcyBlbiBuZWdyaXRhIGNvbiAqIHNvbiBlc3RhZMOtc3RpY2FtZW50ZSBzaWduaWZpY2F0aXZhcyAocCA8IDAsMDUpLiIpLA0KICAgICAgaWYgKHBvcyRyaG8gPiAwKSBwYXN0ZTAoIjxiPlJlbGFjacOzbiBwb3NpdGl2YSBtw6FzIGZ1ZXJ0ZTo8L2I+ICIsIHRvbG93ZXIocG9zJGRlbGl0byksICIgY29uICIsIHBvcyRlZGFkLA0KICAgICAgICAgICAgICIgKCIsIG51bShwb3MkcmhvLCBhY2N1cmFjeSA9IDAuMDEpLCAiKTogbGFzIFVQWiBjb24gbcOhcyBwb2JsYWNpw7NuIGRlIGVzYSBlZGFkIHRpZW5kZW4gYSB0ZW5lciB0YXNhcyBtw6FzIGFsdGFzLiIpLA0KICAgICAgaWYgKG5lZyRyaG8gPCAwKSBwYXN0ZTAoIjxiPlJlbGFjacOzbiBuZWdhdGl2YSBtw6FzIGZ1ZXJ0ZTo8L2I+ICIsIHRvbG93ZXIobmVnJGRlbGl0byksICIgY29uICIsIG5lZyRlZGFkLA0KICAgICAgICAgICAgICIgKCIsIG51bShuZWckcmhvLCBhY2N1cmFjeSA9IDAuMDEpLCAiKTogbGFzIFVQWiBjb24gbcOhcyBwb2JsYWNpw7NuIGRlIGVzYSBlZGFkIHRpZW5kZW4gYSB0ZW5lciB0YXNhcyBtw6FzIGJhamFzLiIpLA0KICAgICAgcGFzdGUwKCI8Yj5SZWxhY2lvbmVzIHNpZ25pZmljYXRpdmFzOjwvYj4gIiwgc3VtKGQkc2lnbmlmKSwgIiBkZSAiLCBucm93KGQpLCAiLiIpLA0KICAgICAgaWYgKG4gPCAxMCkgcGFzdGUwKCI8Yj5Pam86PC9iPiBjb24gc29sbyAiLCBuLCAiIFVQWiBsYXMgY29ycmVsYWNpb25lcyBzb24gcG9jbyBjb25maWFibGVzICIsDQogICAgICAgICAgICAgIihiYXN0YSBjb24gcXVlIHVuYSBVUFogY2FtYmllIHBhcmEgcXVlIGVsIHJlc3VsdGFkbyBjYW1iaWUpLiBUw7NtYWxhcyBzb2xvIGNvbW8gcmVmZXJlbmNpYS4iKSwNCiAgICAgIHBhc3RlMCgiPGI+UmVjdWVyZGE6PC9iPiBjb3JyZWxhY2nDs24gbm8gZXMgY2F1c2FsaWRhZDsgbXVlc3RyYSBxdWUgZG9zIGNvc2FzIHN1YmVuIG8gYmFqYW4ganVudGFzLCAiLA0KICAgICAgICAgICAgICJubyBxdWUgdW4gZ3J1cG8gZGUgZWRhZCBjYXVzZSBlbCBkZWxpdG8uIEFkZW3DoXMgbG9zIGN1YXRybyBncnVwb3MgZGUgZWRhZCBzdW1hbiAxMDAgJSwgIiwNCiAgICAgICAgICAgICAicG9yIGVzbyAzMOKAkzU5IGHDsW9zIHN1ZWxlIG1vdmVyc2UgYWwgcmV2w6lzIHF1ZSBsb3MgZGVtw6FzLiIpDQogICAgKSwNCiAgICBpZiAobnpjaGFyKG1pc19jb21lbnRhcmlvc1tbZ11dKSkgcGFzdGUwKCI8Yj5Db21lbnRhcmlvczo8L2I+ICIsIG1pc19jb21lbnRhcmlvc1tbZ11dKQ0KICApDQp9DQoNCmNvbWVudGFyaW9zIDwtIGxhcHBseShncnVwb3MsIGNvbWVudGFyaW9fY29ycikNCg0KIyA0LiBHcsOhZmljbzogbWFwYSBkZSBjYWxvciBjb24gbWVuw7ogLS0tLQ0KYV9tYXRyaXogPC0gZnVuY3Rpb24oZCwgY29sKSB7DQogIGQgJT4lDQogICAgc2VsZWN0KGRlbGl0bywgZWRhZCwgYWxsX29mKGNvbCkpICU+JQ0KICAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBlZGFkLCB2YWx1ZXNfZnJvbSA9IGFsbF9vZihjb2wpKSAlPiUNCiAgICBhcnJhbmdlKG1hdGNoKGRlbGl0bywgZGVsaXRvc19jb3JyKSkgJT4lDQogICAgc2VsZWN0KGFsbF9vZih1bm5hbWUoZWRhZGVzKSkpICU+JQ0KICAgIGFzLm1hdHJpeCgpDQp9DQoNCmZpZyA8LSBwbG90X2x5KCkNCg0KZm9yIChpIGluIHNlcV9hbG9uZyhncnVwb3MpKSB7DQogIGQgPC0gY29ycmVsYWNpb25lcyAlPiUgZmlsdGVyKGdydXBvID09IGdydXBvc1tpXSkNCiAgZmlnIDwtIGZpZyAlPiUgYWRkX3RyYWNlKA0KICAgIHR5cGUgPSAiaGVhdG1hcCIsDQogICAgeCA9IHVubmFtZShlZGFkZXMpLCB5ID0gdW5uYW1lKGRlbGl0b3NfY29yciksDQogICAgeiA9IGFfbWF0cml6KGQsICJyaG8iKSwgdGV4dCA9IGFfbWF0cml6KGQsICJ0ZXh0byIpLA0KICAgIGhvdmVyaW5mbyA9ICJ0ZXh0IiwgeGdhcCA9IDMsIHlnYXAgPSAzLA0KICAgIHptaW4gPSAtMSwgem1heCA9IDEsDQogICAgY29sb3JzY2FsZSA9IGxpc3QoYygwLCB0dXJxdWVzYSksIGMoMC41LCAiI0ZGRkZGRiIpLCBjKDEsIG1hZ2VudGEpKSwNCiAgICBjb2xvcmJhciA9IGxpc3QodGl0bGUgPSAiQ29ycmVsYWNpw7NuPGJyPihTcGVhcm1hbikiLCBsZW4gPSAwLjgsIHRoaWNrbmVzcyA9IDE0LA0KICAgICAgICAgICAgICAgICAgICB0aWNrdmFscyA9IGMoLTEsIC0wLjUsIDAsIDAuNSwgMSkpLA0KICAgIHNob3dzY2FsZSA9IFRSVUUsIHZpc2libGUgPSAoaSA9PSAxKQ0KICApDQp9DQoNCiMgbsO6bWVybyBlc2NyaXRvIGRlbnRybyBkZSBjYWRhIGNlbGRhIChlbiBuZWdyaXRhIHNpIGVzIHNpZ25pZmljYXRpdmEpDQphbm90YWNpb25lc19jZWxkYXMgPC0gZnVuY3Rpb24oZykgew0KICBkIDwtIGNvcnJlbGFjaW9uZXMgJT4lIGZpbHRlcihncnVwbyA9PSBnKQ0KICBsYXBwbHkoc2VxX2xlbihucm93KGQpKSwgZnVuY3Rpb24oaykgbGlzdCgNCiAgICB4ID0gZCRlZGFkW2tdLCB5ID0gZCRkZWxpdG9ba10sIHhyZWYgPSAieCIsIHlyZWYgPSAieSIsDQogICAgdGV4dCA9IGQkZXRpcXVldGFfY2VsZGFba10sIHNob3dhcnJvdyA9IEZBTFNFLA0KICAgIGZvbnQgPSBsaXN0KHNpemUgPSAxNSwNCiAgICAgICAgICAgICAgICBjb2xvciA9IGlmZWxzZSghaXMubmEoZCRyaG9ba10pICYmIGFicyhkJHJob1trXSkgPiAwLjU1LCAid2hpdGUiLCAiIzMzMzMzMyIpKQ0KICApKQ0KfQ0KDQp0aXR1bG8gPC0gZnVuY3Rpb24oaSkgew0KICBwYXN0ZTAoIjxiPiIsIHBhcnRpcl90aXR1bG8oIkVuIHBvYmxhY2lvbmVzIGrDs3ZlbmVzIGxhIHZpb2xlbmNpYSBpbnRyYWZhbWlsaWFyIHRpZW5kZSBhIHN1YmlyIHkgZWwgaHVydG8gYSBiYWphcjsgZXN0byBhcGxpY2EgYSBwb2JsYWNpw7NuIGJhamEgeSBtZWRpYSIpLCAiPC9iPiIsDQogICAgICAgICAiPGJyPjxzdXA+IiwgZ3J1cG9zW2ldLCAiIMK3IENvcnJlbGFjacOzbiBkZSBTcGVhcm1hbiBlbnRyZSBlbCAlIGRlIHBvYmxhY2nDs24gcG9yIGVkYWQgeSBsYSB0YXNhIGRlIGRlbGl0b3MgZGUgY2FkYSBVUFoiLA0KICAgICAgICAgIiDCtyAqID0gc2lnbmlmaWNhdGl2YSAocCA8IDAsMDUpPC9zdXA+IikNCn0NCg0KYm90b25lcyA8LSBsYXBwbHkoc2VxX2Fsb25nKGdydXBvcyksIGZ1bmN0aW9uKGkpIHsNCiAgbGlzdCgNCiAgICBtZXRob2QgPSAidXBkYXRlIiwNCiAgICBsYWJlbCAgPSBncnVwb3NbaV0sDQogICAgYXJncyAgID0gbGlzdCgNCiAgICAgIGxpc3QodmlzaWJsZSA9IHNlcV9hbG9uZyhncnVwb3MpID09IGkpLA0KICAgICAgbGlzdCgidGl0bGUudGV4dCIgPSB0aXR1bG8oaSksDQogICAgICAgICAgIGFubm90YXRpb25zICA9IGFub3RhY2lvbmVzX2NlbGRhcyhncnVwb3NbaV0pKQ0KICAgICkNCiAgKQ0KfSkNCg0KZmlnIDwtIGZpZyAlPiUNCiAgbGF5b3V0KA0KICAgIGhlaWdodCA9IDUyMCwNCiAgICB0aXRsZSAgPSB0aXR1bG9fcGxvdGx5KHRpdHVsbygxKSksDQogICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIkdydXBvIGRlIGVkYWQgKCUgZGUgaGFiaXRhbnRlcyBkZSBsYSBVUFopIiwgc2lkZSA9ICJib3R0b20iKSwNCiAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiIiwgYXV0b3JhbmdlID0gInJldmVyc2VkIiwgdGlja2ZvbnQgPSBsaXN0KHNpemUgPSAxMyksIGF1dG9tYXJnaW4gPSBUUlVFKSwNCiAgICBhbm5vdGF0aW9ucyA9IGFub3RhY2lvbmVzX2NlbGRhcyhncnVwb3NbMV0pLA0KICAgIGhvdmVybGFiZWwgPSBob3Zlcl9wbG90bHksDQogICAgdXBkYXRlbWVudXMgPSBsaXN0KG1lbnVfcGxvdGx5KGJvdG9uZXMpKSwNCiAgICBtYXJnaW4gPSBsaXN0KHQgPSAxNDAsIGIgPSA4MCwgbCA9IDEwLCByID0gMzApDQogICkgJT4lDQogIGVzdGlsb19wbG90bHkoKQ0KDQojIDUuIENhamEgZGUgY29tZW50YXJpb3MgZGUgbGFkbyBhIGxhZG8gKGRlYmFqbyBkZWwgZ3LDoWZpY28pIC0tLS0NCiMgKGpzX2NvbWVudGFyaW9zIGVzdMOhIGVuIGVsIGNodW5rICJlc3RpbG8iKQ0KZmlnIDwtIGh0bWx3aWRnZXRzOjpvblJlbmRlcigNCiAgZmlnLCBqc19jb21lbnRhcmlvcywNCiAgZGF0YSA9IGxpc3QoY29tZW50YXJpb3MgPSBjb21lbnRhcmlvcywgaW5pY2lvcyA9IHNlcV9hbG9uZyhncnVwb3MpIC0gMSkgICAjIHVuYSB0cmF6YSBwb3IgZ3J1cG8NCikNCg0KZmlnDQpgYGANCg0KKipDb25jbHVzacOzbiBkZSBjb3JyZWxhY2nDs24qKg0KDQpFbiBlc3RhIHNlY2Npb24gc2UgbWFudGllbmVuIGxvcyBjYXNvcyBhdGlwaWNvcyBleHRyZW1vcyBlbiBlbCBhbmFsaXNpcywgYXVucXVlIHNvbiBhc29jaWFjaW9uZXMgZW50cmUgVVBaLCBubyBleHBsaWNhbiBwb3IgcXXDqSBvY3VycmVuIHkgbm8gc2lnbmlmaWNhbiBxdWUgbGEgZWRhZCBjYXVzZSBlbCBkZWxpdG8sIHBhcmEgbWF5b3IgYW5hbGlzaXMgc2VyaWEgaW1wb3J0YW50ZSB0ZW5lciBsYSBkYXRhIHBvciBkZW51bmNpYXMgcmVhbGl6YWRhcyBwb3IgZWRhZCwganVudG8gY29uIGVsIGRlbGl0byBhIGRlbnVuY2lhciwgZGUgZXN0YSBtYW5lcmEgc2UgcG9kcmlhIGNvbXByb2JhciBwb3NpdGl2YSBvIG5lZ2F0aXZhbWVudGUgbGEgY29ycmVsYWNpw7NuLg0KDQojIyA0LjUgwr9Ew7NuZGUgc2UgdWJpY2FuIGxhcyBVUFogY29uIG1heW9yZXMgdGFzYXMgeSBjw7NtbyBzZSB2ZW4gbGFzIHNlZ21lbnRhY2lvbmVzIGVuIGVsIHRlcnJpdG9yaW8/DQoNCmBgYHtyIE1hcGF9DQojIG1hcGEgYm9nb3RhIHVweg0KcnV0YV9zaHAgPC0gZmlsZS5wYXRoKHJ1dGFfaW5wdXQsICJzaHAiLCAidXB6X2JvZ290YS5zaHAiKQ0KdXB6X3NocCA8LSBzdF9yZWFkKHJ1dGFfc2hwLCBxdWlldCA9IFRSVUUpDQpsb2Nfc2hwIDwtIHN0X3JlYWQocnV0YV9zaHAsIHF1aWV0ID0gVFJVRSkgICMgcmVjb3J0YWRhcyBhbCDDoXJlYSB1cmJhbmENCg0KIyAxLiBHZW9tZXRyw61hICsgZGF0b3MgZGUgY2FkYSBVUFogLS0tLQ0KbWFwYSA8LSB1cHpfc2hwICU+JQ0KICBzZWxlY3QodXB6X2NvZCkgJT4lDQogIGxlZnRfam9pbigNCiAgICB1cHogJT4lIHNlbGVjdCh1cHpfY29kLCB1cHpfbm9tYnJlLCBsb2NhbGlkYWQsIGdydXBvX3BvYmxhY2lvbiwgcG9ibGFjaW9uLA0KICAgICAgICAgICAgICAgICAgIGNhc29zX3RvdGFsLCB0YXNhX3RvdGFsLA0KICAgICAgICAgICAgICAgICAgIGNhc29zX2h1cnRvX3RvdGFsLCB0YXNhX2h1cnRvX3RvdGFsLA0KICAgICAgICAgICAgICAgICAgIGNhc29zX2xlc2lvbmVzLCB0YXNhX2xlc2lvbmVzLA0KICAgICAgICAgICAgICAgICAgIGNhc29zX2hvbWljaWRpb3MsIHRhc2FfaG9taWNpZGlvcywNCiAgICAgICAgICAgICAgICAgICBjYXNvc192aW9sZW5jaWFfaW50cmFmYW1pbGlhciwgdGFzYV92aW9sZW5jaWFfaW50cmFmYW1pbGlhciksDQogICAgYnkgPSAidXB6X2NvZCINCiAgKSAlPiUNCiAgbXV0YXRlKGlkX3VweiA9IGFzLmNoYXJhY3Rlcih1cHpfY29kKSkNCg0Kc3RvcGlmbm90KCJIYXkgVVBaIHNpbiBzZWdtZW50YWNpw7NuOiBjb3JyZSBwcmltZXJvIGVsIGNodW5rIGRlIEstbWVhbnMiID0gIWFueU5BKG1hcGEkZ3J1cG9fcG9ibGFjaW9uKSkNCg0KIyBzZSBhbGlnZXJhIGxhIGdlb21ldHLDrWEgcGFyYSBxdWUgZWwgSFRNTCBwZXNlIHBvY28NCm1hcGEgPC0gbWFwYSAlPiUNCiAgc3RfbWFrZV92YWxpZCgpICU+JQ0KICBzdF90cmFuc2Zvcm0oMzExNikgJT4lDQogIHN0X3NpbXBsaWZ5KGRUb2xlcmFuY2UgPSAzMCwgcHJlc2VydmVUb3BvbG9neSA9IFRSVUUpICU+JQ0KICBzdF90cmFuc2Zvcm0oNDMyNikNCg0KIyBsw61taXRlcyBleGFjdG9zIGRlIEJvZ290w6EgcGFyYSBxdWUgZWwgbWFwYSBsbGVuZSBlbCBlc3BhY2lvDQpiYiAgPC0gc3RfYmJveChtYXBhKQ0KcGFkIDwtIDAuMDA1DQpyYW5nb19sb24gPC0gYyh1bm5hbWUoYmJbInhtaW4iXSkgLSBwYWQsIHVubmFtZShiYlsieG1heCJdKSArIHBhZCkNCnJhbmdvX2xhdCA8LSBjKHVubmFtZShiYlsieW1pbiJdKSAtIHBhZCwgdW5uYW1lKGJiWyJ5bWF4Il0pICsgcGFkKQ0KDQphX2dlb2pzb24gPC0gZnVuY3Rpb24oeCkgew0KICB0bXAgPC0gdGVtcGZpbGUoZmlsZWV4dCA9ICIuZ2VvanNvbiIpDQogIHN0X3dyaXRlKHhbLCAiaWRfdXB6Il0sIHRtcCwgZHJpdmVyID0gIkdlb0pTT04iLCBxdWlldCA9IFRSVUUpDQogIG91dCA8LSByZWFkX2pzb24odG1wKQ0KICB1bmxpbmsodG1wKQ0KICBvdXQNCn0NCg0KIyAyLiBTZWdtZW50YWNpw7NuIHkgZGVsaXRvcyAtLS0tDQpuX2dydXBvIDwtIHNhcHBseShncnVwb3MsIGZ1bmN0aW9uKGcpIHN1bShtYXBhJGdydXBvX3BvYmxhY2lvbiA9PSBnKSkNCg0KZGVsaXRvc19tYXBhIDwtIGxpc3QoDQogICJUb3RhbCBkZSBkZWxpdG9zIiAgICAgICAgPSBjKCJjYXNvc190b3RhbCIsICJ0YXNhX3RvdGFsIiksDQogICJIdXJ0byIgICAgICAgICAgICAgICAgICAgPSBjKCJjYXNvc19odXJ0b190b3RhbCIsICJ0YXNhX2h1cnRvX3RvdGFsIiksDQogICJMZXNpb25lcyIgICAgICAgICAgICAgICAgPSBjKCJjYXNvc19sZXNpb25lcyIsICJ0YXNhX2xlc2lvbmVzIiksDQogICJIb21pY2lkaW9zIiAgICAgICAgICAgICAgPSBjKCJjYXNvc19ob21pY2lkaW9zIiwgInRhc2FfaG9taWNpZGlvcyIpLA0KICAiVmlvbGVuY2lhIGludHJhZmFtaWxpYXIiID0gYygiY2Fzb3NfdmlvbGVuY2lhX2ludHJhZmFtaWxpYXIiLCAidGFzYV92aW9sZW5jaWFfaW50cmFmYW1pbGlhciIpDQopDQoNCmNyZWFyX3RleHRvIDwtIGZ1bmN0aW9uKGQsIGRlbGl0bykgew0KICB2IDwtIGRlbGl0b3NfbWFwYVtbZGVsaXRvXV0NCiAgcGFzdGUwKA0KICAgICI8Yj4iLCBkJHVwel9ub21icmUsICI8L2I+IiwNCiAgICAiPGJyPkxvY2FsaWRhZDogIiwgZCRsb2NhbGlkYWQsDQogICAgIjxicj5TZWdtZW50YWNpw7NuOiAiLCBkJGdydXBvX3BvYmxhY2lvbiwNCiAgICAiPGJyPlBvYmxhY2nDs246ICIsIG51bShkJHBvYmxhY2lvbiksDQogICAgIjxicj5Ub3RhbCBkZSBkZWxpdG9zOiAiLCBudW0oZCRjYXNvc190b3RhbCksICIgY2Fzb3MgwrcgdGFzYSAiLCBudW0oZCR0YXNhX3RvdGFsLCBhY2N1cmFjeSA9IDAuMSksDQogICAgaWYgKGRlbGl0byAhPSAiVG90YWwgZGUgZGVsaXRvcyIpDQogICAgICBwYXN0ZTAoIjxicj48Yj4iLCBkZWxpdG8sICI6PC9iPiAiLCBudW0oZFtbdlsxXV1dKSwgIiBjYXNvcyDCtyB0YXNhICIsIG51bShkW1t2WzJdXV0sIGFjY3VyYWN5ID0gMC4xKSkNCiAgICBlbHNlICIiDQogICkNCn0NCg0KZGF0b3NfZyA8LSBsYXBwbHkoZ3J1cG9zLCBmdW5jdGlvbihnKSBtYXBhICU+JSBmaWx0ZXIoZ3J1cG9fcG9ibGFjaW9uID09IGcpKQ0KdGV4dG9zICA8LSBsYXBwbHkobmFtZXMoZGVsaXRvc19tYXBhKSwgZnVuY3Rpb24oZGwpIHsNCiAgbGFwcGx5KGRhdG9zX2csIGZ1bmN0aW9uKGRnKSBjcmVhcl90ZXh0byhzdF9kcm9wX2dlb21ldHJ5KGRnKSwgZGwpKQ0KfSkNCm5hbWVzKHRleHRvcykgPC0gbmFtZXMoZGVsaXRvc19tYXBhKQ0KDQojIDMuIE1hcGE6IGJhc2UgZ3JpcyAodG9kYXMgbGFzIFVQWikgKyB1bmEgY2FwYSBwb3Igc2VnbWVudGFjacOzbiAtLS0tDQphbHRvX21hcGEgPC0gOTUwDQoNCmZpZyA8LSBwbG90X2x5KGhlaWdodCA9IGFsdG9fbWFwYSkgJT4lDQogIGFkZF90cmFjZSgNCiAgICB0eXBlID0gImNob3JvcGxldGgiLCBuYW1lID0gImJhc2UiLCAgICAgICAgICAgICAgICAgICAgICAgIyA8PDwgTlVFVk86IG5vbWJyZSBkZSBsYSB0cmF6YQ0KICAgIGdlb2pzb24gPSBhX2dlb2pzb24obWFwYSksIGZlYXR1cmVpZGtleSA9ICJwcm9wZXJ0aWVzLmlkX3VweiIsDQogICAgbG9jYXRpb25zID0gbWFwYSRpZF91cHosIHogPSByZXAoMSwgbnJvdyhtYXBhKSksIHptaW4gPSAwLCB6bWF4ID0gMSwNCiAgICBjb2xvcnNjYWxlID0gbGlzdChjKDAsIGdyaXMpLCBjKDEsIGdyaXMpKSwgc2hvd3NjYWxlID0gRkFMU0UsDQogICAgbWFya2VyID0gbGlzdChsaW5lID0gbGlzdChjb2xvciA9ICJ3aGl0ZSIsIHdpZHRoID0gMC41KSksDQogICAgaG92ZXJpbmZvID0gInNraXAiDQogICkNCg0KZm9yIChpIGluIHNlcV9hbG9uZyhncnVwb3MpKSB7DQogIGRnIDwtIGRhdG9zX2dbW2ldXQ0KICBmaWcgPC0gZmlnICU+JSBhZGRfdHJhY2UoDQogICAgdHlwZSA9ICJjaG9yb3BsZXRoIiwgbmFtZSA9IGdydXBvc1tpXSwgICAgICAgICAgICAgICAgICAgICMgPDw8IE5VRVZPOiBsYSB0YWJsYSBsZWUgZXN0ZSBub21icmUNCiAgICBnZW9qc29uID0gYV9nZW9qc29uKGRnKSwgZmVhdHVyZWlka2V5ID0gInByb3BlcnRpZXMuaWRfdXB6IiwNCiAgICBsb2NhdGlvbnMgPSBkZyRpZF91cHosIHogPSByZXAoMSwgbnJvdyhkZykpLCB6bWluID0gMCwgem1heCA9IDEsDQogICAgY29sb3JzY2FsZSA9IGxpc3QoYygwLCBjb2xvcmVzW1tncnVwb3NbaV1dXSksIGMoMSwgY29sb3Jlc1tbZ3J1cG9zW2ldXV0pKSwgc2hvd3NjYWxlID0gRkFMU0UsDQogICAgbWFya2VyID0gbGlzdChsaW5lID0gbGlzdChjb2xvciA9ICJ3aGl0ZSIsIHdpZHRoID0gMC44KSksDQogICAgdGV4dCA9IHRleHRvc1tbIlRvdGFsIGRlIGRlbGl0b3MiXV1bW2ldXSwgaG92ZXJpbmZvID0gInRleHQiDQogICkNCn0NCg0KIyA0LiBTZWxlY3RvcmVzIC0tLS0NCmJvdG9uZXNfc2VnIDwtIGxhcHBseShjKCJUb2RhcyIsIGdydXBvcyksIGZ1bmN0aW9uKHMpIHsNCiAgbGlzdChtZXRob2QgPSAicmVzdHlsZSIsDQogICAgICAgbGFiZWwgID0gaWYgKHMgPT0gIlRvZGFzIikgcGFzdGUwKCJUb2RhcyAoIiwgc3VtKG5fZ3J1cG8pLCAiIFVQWikiKSBlbHNlIHBhc3RlMChzLCAiICgiLCBuX2dydXBvW1tzXV0sICIgVVBaKSIpLA0KICAgICAgIGFyZ3MgICA9IGxpc3QobGlzdCh2aXNpYmxlID0gKGdydXBvcyA9PSBzKSB8IChzID09ICJUb2RhcyIpKSwgYygxLCAyLCAzKSkpDQp9KQ0KDQpib3RvbmVzX2RlbGl0byA8LSBsYXBwbHkobmFtZXMoZGVsaXRvc19tYXBhKSwgZnVuY3Rpb24oZGwpIHsNCiAgbGlzdChtZXRob2QgPSAicmVzdHlsZSIsIGxhYmVsID0gZGwsDQogICAgICAgYXJncyAgID0gbGlzdChsaXN0KHRleHQgPSBsYXBwbHkodGV4dG9zW1tkbF1dLCBhcy5saXN0KSksIGMoMSwgMiwgMykpKQ0KfSkNCg0KbGV5ZW5kYSA8LSBwYXN0ZTAoDQogIHBhc3RlMCgiPHNwYW4gc3R5bGU9J2NvbG9yOiIsIGNvbG9yZXMsICInPuKWoDwvc3Bhbj4gIiwgZ3J1cG9zLCAiICgiLCBuX2dydXBvLCAiKSIsIGNvbGxhcHNlID0gIiAgICAgICIpLA0KICAiICAgICAgPHNwYW4gc3R5bGU9J2NvbG9yOiNCREJBQzQnPuKWoDwvc3Bhbj4gVVBaIG5vIHNlbGVjY2lvbmFkYXMiDQopDQoNCiMgNS4gRGlzZcOxbyBkZWwgbWFwYSAtLS0tDQpmaWcgPC0gZmlnICU+JQ0KICBsYXlvdXQoDQogICAgaGVpZ2h0ID0gYWx0b19tYXBhLA0KICAgIHRpdGxlID0gdGl0dWxvX3Bsb3RseSgNCiAgICAgIHBhc3RlMCgiPGI+TWFwYSBkZSBsYXMgVVBaIGRlIEJvZ290w6Egc2Vnw7puIHNlZ21lbnRhY2nDs24gcG9ibGFjaW9uYWw8L2I+IiwNCiAgICAgICAgICAgICAiPGJyPjxzdXA+RWxpZ2UgbGEgc2VnbWVudGFjacOzbiB5IGVsIGRlbGl0byDCtyBwYXNhIGVsIGN1cnNvciBzb2JyZSBjYWRhIFVQWiDCtyAiLA0KICAgICAgICAgICAgICJ0YXNhcyBwb3IgY2FkYSAxMC4wMDAgaGFiaXRhbnRlczwvc3VwPiIpLA0KICAgICAgeHJlZiA9ICJjb250YWluZXIiDQogICAgKSwNCiAgICBnZW8gPSBsaXN0KA0KICAgICAgcHJvamVjdGlvbiA9IGxpc3QodHlwZSA9ICJtZXJjYXRvciIpLA0KICAgICAgbG9uYXhpcyA9IGxpc3QocmFuZ2UgPSByYW5nb19sb24pLA0KICAgICAgbGF0YXhpcyA9IGxpc3QocmFuZ2UgPSByYW5nb19sYXQpLA0KICAgICAgdmlzaWJsZSA9IEZBTFNFLCBiZ2NvbG9yID0gInJnYmEoMCwwLDAsMCkiLA0KICAgICAgZG9tYWluID0gbGlzdCh4ID0gYygwLCAxKSwgeSA9IGMoMCwgMSkpDQogICAgKSwNCiAgICBob3ZlcmxhYmVsID0gaG92ZXJfcGxvdGx5LA0KICAgIHVwZGF0ZW1lbnVzID0gbGlzdCgNCiAgICAgIG1lbnVfcGxvdGx5KGJvdG9uZXNfc2VnLCAgICB4ID0gMCwgICAgeSA9IDEuMDIpLA0KICAgICAgbWVudV9wbG90bHkoYm90b25lc19kZWxpdG8sIHggPSAwLjQyLCB5ID0gMS4wMikNCiAgICApLA0KICAgIGFubm90YXRpb25zID0gbGlzdCgNCiAgICAgIGxpc3QodGV4dCA9ICJTZWdtZW50YWNpw7NuIHBvYmxhY2lvbmFsIiwgeCA9IDAsIHkgPSAxLjEwLCB4cmVmID0gInBhcGVyIiwgeXJlZiA9ICJwYXBlciIsDQogICAgICAgICAgIHhhbmNob3IgPSAibGVmdCIsIHlhbmNob3IgPSAiYm90dG9tIiwgc2hvd2Fycm93ID0gRkFMU0UsIGZvbnQgPSBsaXN0KHNpemUgPSAxMiwgY29sb3IgPSAiZ3JleTQwIikpLA0KICAgICAgbGlzdCh0ZXh0ID0gIkRlbGl0byAoZW4gZWwgY3VhZHJvIGRlbCBtb3VzZSkiLCB4ID0gMC40MiwgeSA9IDEuMTAsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwNCiAgICAgICAgICAgeGFuY2hvciA9ICJsZWZ0IiwgeWFuY2hvciA9ICJib3R0b20iLCBzaG93YXJyb3cgPSBGQUxTRSwgZm9udCA9IGxpc3Qoc2l6ZSA9IDEyLCBjb2xvciA9ICJncmV5NDAiKSksDQogICAgICBsaXN0KHRleHQgPSBsZXllbmRhLCB4ID0gMCwgeSA9IC0wLjAyLCB4cmVmID0gInBhcGVyIiwgeXJlZiA9ICJwYXBlciIsDQogICAgICAgICAgIHhhbmNob3IgPSAibGVmdCIsIHlhbmNob3IgPSAidG9wIiwgc2hvd2Fycm93ID0gRkFMU0UsIGZvbnQgPSBsaXN0KHNpemUgPSAxMywgY29sb3IgPSAiZ3JleTI1IikpDQogICAgKSwNCiAgICBtYXJnaW4gPSBsaXN0KHQgPSAxNDAsIGIgPSA1MCwgbCA9IDAsIHIgPSAwKQ0KICApICU+JQ0KICBlc3RpbG9fcGxvdGx5KCkNCmBgYA0KDQpgYGB7ciBnZW5lcmFjaW9uX3Jhbmtpbmd9DQojIDYuIGRhdG9zIGRlbCByYW5raW5nIC0tLS0NCnRvcF9uIDwtIDE1ICAgIyBjdcOhbnRhcyBVUFogbXVlc3RyYSBsYSB0YWJsYQ0KDQojIGVzX2V4dHJlbW8geWEgdmllbmUgY2FsY3VsYWRvIGVuIGVsIGJveHBsb3QgKFEzICsgMyDDlyBJUVIsIHRhc2FzIHJlYWxlcywgcG9yIHNlZ21lbnRhY2nDs24pDQpyYW5raW5nX2RhdG9zIDwtIHVweiAlPiUNCiAgdHJhbnNtdXRlKA0KICAgIHVwel9ub21icmUsDQogICAgZ3J1cG9fcG9ibGFjaW9uID0gYXMuY2hhcmFjdGVyKGdydXBvX3BvYmxhY2lvbiksDQogICAgYXJlYV9rbTIsIHBvYmxhY2lvbiwNCiAgICB0YXNhX2h1cnRvX3RvdGFsLCB0YXNhX2xlc2lvbmVzLCB0YXNhX2hvbWljaWRpb3MsIHRhc2FfdmlvbGVuY2lhX2ludHJhZmFtaWxpYXIsIHRhc2FfdG90YWwsDQogICAgZXNfZXh0cmVtbw0KICApDQoNCmNmZyA8LSBsaXN0KA0KICBtb3JhZG8gPSBtb3JhZG8sIG1hZ2VudGEgPSBtYWdlbnRhLCBncmlzID0gZ3JpcywNCiAgdG9wX24gPSB0b3BfbiwgZ3J1cG9zID0gZ3J1cG9zLCBjb2xvcmVzID0gYXMubGlzdChjb2xvcmVzKSwNCiAgbl9leHQgPSBzdW0ocmFua2luZ19kYXRvcyRlc19leHRyZW1vKSwNCiAgbl9jZXJvX2hvbSA9IHN1bShyYW5raW5nX2RhdG9zJHRhc2FfaG9taWNpZGlvcyA9PSAwKQ0KKQ0KDQojIDcuIHRhYmxhIHJhbmtpbmcgY29uZWN0YWRhIChlbiBsb3MgZG9zIHNlbnRpZG9zKSBjb24gZWwgc2VsZWN0b3IgZGUgc2VnbWVudGFjacOzbiBkZWwgbWFwYSAtLS0tDQpqc19yYW5raW5nIDwtIHIiLS0tKA0KZnVuY3Rpb24oZWwsIHgsIGRhdG9zKSB7DQogIHZhciByZWcgPSBKU09OLnBhcnNlKGRhdG9zLnJlZ2lzdHJvcyk7DQogIHZhciBjZmcgPSBKU09OLnBhcnNlKGRhdG9zLmNmZyk7DQoNCiAgZnVuY3Rpb24gZih2LCBkZWMpIHsNCiAgICByZXR1cm4gbmV3IEludGwuTnVtYmVyRm9ybWF0KCdlcy1DTycsIHttaW5pbXVtRnJhY3Rpb25EaWdpdHM6IGRlYywgbWF4aW11bUZyYWN0aW9uRGlnaXRzOiBkZWN9KS5mb3JtYXQodik7DQogIH0NCiAgZnVuY3Rpb24gZXNjKHMpIHsNCiAgICByZXR1cm4gU3RyaW5nKHMpLnJlcGxhY2UoLyYvZywgJyZhbXA7JykucmVwbGFjZSgvPC9nLCAnJmx0OycpLnJlcGxhY2UoLz4vZywgJyZndDsnKTsNCiAgfQ0KDQogIHZhciBjb2xzID0gWw0KICAgIHtrOiAndXB6X25vbWJyZScsICAgICAgICAgICAgICAgICAgIHQ6ICdVUFonLCAgICAgICAgICAgICAgICAgICAgICAgICBudW06IGZhbHNlfSwNCiAgICB7azogJ2dydXBvX3BvYmxhY2lvbicsICAgICAgICAgICAgICB0OiAnU2VnbWVudGFjacOzbicsICAgICAgICAgICAgICAgIG51bTogZmFsc2V9LA0KICAgIHtrOiAnYXJlYV9rbTInLCAgICAgICAgICAgICAgICAgICAgIHQ6ICfDgXJlYSAoa23CsiknLCAgICAgICAgICAgICAgICAgIG51bTogdHJ1ZSwgZDogMn0sDQogICAge2s6ICdwb2JsYWNpb24nLCAgICAgICAgICAgICAgICAgICAgdDogJ1BvYmxhY2nDs24nLCAgICAgICAgICAgICAgICAgICBudW06IHRydWUsIGQ6IDB9LA0KICAgIHtrOiAndGFzYV9odXJ0b190b3RhbCcsICAgICAgICAgICAgIHQ6ICdUYXNhIGh1cnRvJywgICAgICAgICAgICAgICAgICBudW06IHRydWUsIGQ6IDF9LA0KICAgIHtrOiAndGFzYV9sZXNpb25lcycsICAgICAgICAgICAgICAgIHQ6ICdUYXNhIGxlc2lvbmVzJywgICAgICAgICAgICAgICBudW06IHRydWUsIGQ6IDF9LA0KICAgIHtrOiAndGFzYV9ob21pY2lkaW9zJywgICAgICAgICAgICAgIHQ6ICdUYXNhIGhvbWljaWRpb3MnLCAgICAgICAgICAgICBudW06IHRydWUsIGQ6IDJ9LA0KICAgIHtrOiAndGFzYV92aW9sZW5jaWFfaW50cmFmYW1pbGlhcicsIHQ6ICdUYXNhIHZpb2xlbmNpYSBpbnRyYWZhbWlsaWFyJywgbnVtOiB0cnVlLCBkOiAxfSwNCiAgICB7azogJ3Rhc2FfdG90YWwnLCAgICAgICAgICAgICAgICAgICB0OiAnVGFzYSB0b3RhbCcsICAgICAgICAgICAgICAgICAgbnVtOiB0cnVlLCBkOiAxfQ0KICBdOw0KICB2YXIgb3JkZW5hYmxlcyA9IGNvbHMuZmlsdGVyKGZ1bmN0aW9uKGMpIHsgcmV0dXJuIGMubnVtOyB9KTsNCiAgdmFyIGVzdGFkbyA9IHtrOiAndGFzYV90b3RhbCcsIGRpcjogJ2Rlc2MnLCBmaXJtYTogbnVsbH07DQoNCiAgLy8gZXN0aWxvIChtaXNtbyBsb29rIHF1ZSB0YWJsYV9ib25pdGE6IGVuY2FiZXphZG8gbW9yYWRvLCBmaWxhIHJlc2FsdGFkYSByb3NhZGEsIGhvdmVyKQ0KICB2YXIgY3NzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3R5bGUnKTsNCiAgY3NzLnRleHRDb250ZW50ID0NCiAgICAnLnJrLWNvbnR7bWFyZ2luLXRvcDoxNHB4O3dpZHRoOjEwMCU7Ym94LXNpemluZzpib3JkZXItYm94O3BhZGRpbmc6MCAxMHB4O2ZvbnQtZmFtaWx5OmluaGVyaXQ7Y29sb3I6IzMzM30nICsNCiAgICAnLnJrLXRpdHVsb3tmb250LXNpemU6MThweDtjb2xvcjonICsgY2ZnLm1vcmFkbyArICc7bGluZS1oZWlnaHQ6MS40O21hcmdpbi1ib3R0b206OHB4fScgKw0KICAgICcucmstdGl0dWxvIHNwYW57Zm9udC1zaXplOjEzcHg7Y29sb3I6IzU1NX0nICsNCiAgICAnLnJrLWN0cmx7ZGlzcGxheTpmbGV4O2dhcDoxNnB4O2ZsZXgtd3JhcDp3cmFwO2FsaWduLWl0ZW1zOmNlbnRlcjttYXJnaW4tYm90dG9tOjEwcHg7Zm9udC1zaXplOjEzcHg7Y29sb3I6IzU1NX0nICsNCiAgICAnLnJrLWN0cmwgc2VsZWN0e3BhZGRpbmc6NXB4IDhweDtib3JkZXI6MXB4IHNvbGlkICcgKyBjZmcuZ3JpcyArICc7Ym9yZGVyLXJhZGl1czo0cHg7Y29sb3I6JyArIGNmZy5tb3JhZG8gKyAnO2JhY2tncm91bmQ6I2ZmZjtmb250LXNpemU6MTNweH0nICsNCiAgICAnLnJrLXdyYXB7b3ZlcmZsb3cteDphdXRvfScgKw0KICAgICcucmstdGFibGF7d2lkdGg6MTAwJTtib3JkZXItY29sbGFwc2U6Y29sbGFwc2U7Zm9udC1zaXplOjE0cHh9JyArDQogICAgJy5yay10YWJsYSB0aHtiYWNrZ3JvdW5kOicgKyBjZmcubW9yYWRvICsgJztjb2xvcjojZmZmO2ZvbnQtd2VpZ2h0OmJvbGQ7cGFkZGluZzo3cHggOXB4O3RleHQtYWxpZ246bGVmdDt3aGl0ZS1zcGFjZTpub3dyYXB9JyArDQogICAgJy5yay10YWJsYSB0aC5yay1udW17dGV4dC1hbGlnbjpyaWdodH0nICsNCiAgICAnLnJrLXRhYmxhIHRoLnJrLW9yZHtjdXJzb3I6cG9pbnRlcn0nICsNCiAgICAnLnJrLXRhYmxhIHRoLnJrLW9yZDpob3ZlcntmaWx0ZXI6YnJpZ2h0bmVzcygxLjI1KX0nICsNCiAgICAnLnJrLXRhYmxhIHRke3BhZGRpbmc6NnB4IDlweDtib3JkZXItdG9wOjFweCBzb2xpZCAjZGRkfScgKw0KICAgICcucmstdGFibGEgdGQucmstbnVte3RleHQtYWxpZ246cmlnaHQ7d2hpdGUtc3BhY2U6bm93cmFwfScgKw0KICAgICcucmstdGFibGEgdGQucmstcG9ze3RleHQtYWxpZ246Y2VudGVyO2ZvbnQtd2VpZ2h0OmJvbGQ7Y29sb3I6JyArIGNmZy5tb3JhZG8gKyAnO3dpZHRoOjQycHh9JyArDQogICAgJy5yay10YWJsYSB0Ym9keSB0cjpob3ZlcntiYWNrZ3JvdW5kOiNmNWY1ZjV9JyArDQogICAgJy5yay10YWJsYSB0Ym9keSB0ci5yay1wcmltZXJve2JhY2tncm91bmQ6I0Y2RUVGMjtmb250LXdlaWdodDpib2xkO2NvbG9yOicgKyBjZmcubW9yYWRvICsgJ30nICsNCiAgICAnLnJrLXRhYmxhIHRib2R5IHRyLnJrLWV4dCB0ZHtib3JkZXItdG9wOjJweCBzb2xpZCAnICsgY2ZnLm1hZ2VudGEgKyAnO2JvcmRlci1ib3R0b206MnB4IHNvbGlkICcgKyBjZmcubWFnZW50YSArICd9JyArDQogICAgJy5yay10YWJsYSB0Ym9keSB0ci5yay1leHQgdGQ6Zmlyc3QtY2hpbGR7Ym9yZGVyLWxlZnQ6MnB4IHNvbGlkICcgKyBjZmcubWFnZW50YSArICd9JyArDQogICAgJy5yay10YWJsYSB0Ym9keSB0ci5yay1leHQgdGQ6bGFzdC1jaGlsZHtib3JkZXItcmlnaHQ6MnB4IHNvbGlkICcgKyBjZmcubWFnZW50YSArICd9JyArDQogICAgJy5yay1ub3Rhe21hcmdpbi10b3A6MTJweDtmb250LXNpemU6MTNweDtsaW5lLWhlaWdodDoxLjU7Y29sb3I6IzQ0NDtib3JkZXItdG9wOjJweCBzb2xpZCAnICsgY2ZnLmdyaXMgKyAnO3BhZGRpbmctdG9wOjEwcHh9JyArDQogICAgJy5yay1ub3RhIHVse21hcmdpbjo2cHggMCAwIDA7cGFkZGluZy1sZWZ0OjIwcHh9JyArDQogICAgJy5yay1ub3RhIGxpe21hcmdpbi1ib3R0b206NHB4fScgKw0KICAgICcucmstc3d7ZGlzcGxheTppbmxpbmUtYmxvY2s7d2lkdGg6MjJweDtoZWlnaHQ6MTFweDt2ZXJ0aWNhbC1hbGlnbjptaWRkbGU7bWFyZ2luLXJpZ2h0OjRweDtib3gtc2l6aW5nOmJvcmRlci1ib3h9JyArDQogICAgJy5yay1zdy1leHR7Ym9yZGVyOjJweCBzb2xpZCAnICsgY2ZnLm1hZ2VudGEgKyAnfScgKw0KICAgICcucmstc3ctcHJpbXtiYWNrZ3JvdW5kOiNGNkVFRjI7Ym9yZGVyOjFweCBzb2xpZCAnICsgY2ZnLmdyaXMgKyAnfSc7DQogIGRvY3VtZW50LmhlYWQuYXBwZW5kQ2hpbGQoY3NzKTsNCg0KICAvLyBlc3RydWN0dXJhDQogIHZhciBjb250ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7DQogIGNvbnQuY2xhc3NOYW1lID0gJ3JrLWNvbnQnOw0KICBlbC5wYXJlbnROb2RlLmluc2VydEJlZm9yZShjb250LCBlbC5uZXh0U2libGluZyk7DQoNCiAgdmFyIHRpdHVsbyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOw0KICB0aXR1bG8uY2xhc3NOYW1lID0gJ3JrLXRpdHVsbyc7DQogIGNvbnQuYXBwZW5kQ2hpbGQodGl0dWxvKTsNCg0KICB2YXIgY3RybCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOw0KICBjdHJsLmNsYXNzTmFtZSA9ICdyay1jdHJsJzsNCg0KICAvLyA8PDwgTlVFVk86IGN1w6FudGFzIFVQWiB0aWVuZSBjYWRhIHNlZ21lbnRhY2nDs24gKHBhcmEgZWwgc2VsZWN0b3IpDQogIHZhciBjb250ZW8gPSB7fTsNCiAgcmVnLmZvckVhY2goZnVuY3Rpb24ocikgeyBjb250ZW9bci5ncnVwb19wb2JsYWNpb25dID0gKGNvbnRlb1tyLmdydXBvX3BvYmxhY2lvbl0gfHwgMCkgKyAxOyB9KTsNCg0KICBjdHJsLmlubmVySFRNTCA9DQogICAgLy8gPDw8IE5VRVZPOiBzZWxlY3RvciBkZSBzZWdtZW50YWNpw7NuIGRlbnRybyBkZSBsYSB0YWJsYQ0KICAgICc8bGFiZWw+U2VnbWVudGFjacOzbiZuYnNwOzxzZWxlY3QgY2xhc3M9InJrLXNlbC1zZWciPicgKw0KICAgICc8b3B0aW9uIHZhbHVlPSJUb2RhcyI+VG9kYXMgKCcgKyByZWcubGVuZ3RoICsgJyBVUFopPC9vcHRpb24+JyArDQogICAgY2ZnLmdydXBvcy5tYXAoZnVuY3Rpb24oZykgeyByZXR1cm4gJzxvcHRpb24gdmFsdWU9IicgKyBnICsgJyI+JyArIGcgKyAnICgnICsgKGNvbnRlb1tnXSB8fCAwKSArICcgVVBaKTwvb3B0aW9uPic7IH0pLmpvaW4oJycpICsNCiAgICAnPC9zZWxlY3Q+PC9sYWJlbD4nICsNCiAgICAnPGxhYmVsPk9yZGVuYXIgcG9yJm5ic3A7PHNlbGVjdCBjbGFzcz0icmstc2VsLWsiPicgKw0KICAgIG9yZGVuYWJsZXMubWFwKGZ1bmN0aW9uKGMpIHsgcmV0dXJuICc8b3B0aW9uIHZhbHVlPSInICsgYy5rICsgJyI+JyArIGMudCArICc8L29wdGlvbj4nOyB9KS5qb2luKCcnKSArDQogICAgJzwvc2VsZWN0PjwvbGFiZWw+JyArDQogICAgJzxsYWJlbD5PcmRlbiZuYnNwOzxzZWxlY3QgY2xhc3M9InJrLXNlbC1kaXIiPicgKw0KICAgICc8b3B0aW9uIHZhbHVlPSJkZXNjIj5EZSBtYXlvciBhIG1lbm9yPC9vcHRpb24+PG9wdGlvbiB2YWx1ZT0iYXNjIj5EZSBtZW5vciBhIG1heW9yPC9vcHRpb24+JyArDQogICAgJzwvc2VsZWN0PjwvbGFiZWw+JyArDQogICAgJzxzcGFuPlRhbWJpw6luIHB1ZWRlcyBoYWNlciBjbGljIGVuIGVsIGVuY2FiZXphZG8gZGUgdW5hIGNvbHVtbmEuPC9zcGFuPic7DQogIGNvbnQuYXBwZW5kQ2hpbGQoY3RybCk7DQogIHZhciBzZWxTZWcgPSBjdHJsLnF1ZXJ5U2VsZWN0b3IoJy5yay1zZWwtc2VnJyk7ICAgLy8gPDw8IE5VRVZPDQogIHZhciBzZWxLID0gY3RybC5xdWVyeVNlbGVjdG9yKCcucmstc2VsLWsnKTsNCiAgdmFyIHNlbERpciA9IGN0cmwucXVlcnlTZWxlY3RvcignLnJrLXNlbC1kaXInKTsNCiAgc2VsSy52YWx1ZSA9IGVzdGFkby5rOw0KICBzZWxEaXIudmFsdWUgPSBlc3RhZG8uZGlyOw0KDQogIHZhciB3cmFwID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7DQogIHdyYXAuY2xhc3NOYW1lID0gJ3JrLXdyYXAnOw0KICBjb250LmFwcGVuZENoaWxkKHdyYXApOw0KDQogIHZhciBub3RhID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7DQogIG5vdGEuY2xhc3NOYW1lID0gJ3JrLW5vdGEnOw0KICBub3RhLmlubmVySFRNTCA9DQogICAgJzxiPkPDs21vIGxlZXIgZXN0YSB0YWJsYTwvYj48dWw+JyArDQogICAgJzxsaT48c3BhbiBjbGFzcz0icmstc3cgcmstc3ctZXh0Ij48L3NwYW4+PGI+Qm9yZGUgbWFnZW50YTo8L2I+IGF0w61waWNvIGV4dHJlbW8sIGVzIGRlY2lyLCB1bmEgVVBaIGN1eWEgdGFzYSB0b3RhbCBzdXBlcmEgUTMgKyAzIMOXIElRUiBkZW50cm8gZGUgbGEgc2VnbWVudGFjacOzbiBwb2JsYWNpb25hbCBnZW5lcmFkYSAoaG95IHNvbiAnICsgY2ZnLm5fZXh0ICsgJykuIFNlIHJlc2FsdGEgc2llbXByZSBxdWUgYXBhcmV6Y2EgZW4gZWwgcmFua2luZy48L2xpPicgKw0KICAgICc8bGk+TGFzIHRhc2FzIGRlIGRlbGl0b3Mgc29uIHBvciBjYWRhIDEwLjAwMCBoYWJpdGFudGVzLjwvbGk+PC91bD4nOw0KICBjb250LmFwcGVuZENoaWxkKG5vdGEpOw0KDQogIC8vIHNlZ21lbnRhY2lvbmVzIHZpc2libGVzIGVuIGVsIG1hcGENCiAgZnVuY3Rpb24gc2VsZWNjaW9uKCkgew0KICAgIHZhciBzID0gW107DQogICAgKGVsLmRhdGEgfHwgW10pLmZvckVhY2goZnVuY3Rpb24odCkgew0KICAgICAgaWYgKGNmZy5ncnVwb3MuaW5kZXhPZih0Lm5hbWUpID49IDAgJiYgdC52aXNpYmxlICE9PSBmYWxzZSAmJiB0LnZpc2libGUgIT09ICdsZWdlbmRvbmx5Jykgcy5wdXNoKHQubmFtZSk7DQogICAgfSk7DQogICAgcmV0dXJuIHM7DQogIH0NCg0KICAvLyA8PDwgTlVFVk86IHRhYmxhIC0+IG1hcGE6IG11ZXN0cmEgc29sbyBsYSBzZWdtZW50YWNpw7NuIGVsZWdpZGEgeSBhY3R1YWxpemEgZWwgbWVuw7ogZGVsIG1hcGENCiAgZnVuY3Rpb24gYXBsaWNhckFsTWFwYSh2YWxvcikgew0KICAgIGlmICh0eXBlb2YgUGxvdGx5ID09PSAndW5kZWZpbmVkJykgcmV0dXJuOw0KICAgIHZhciB2aXMgPSBjZmcuZ3J1cG9zLm1hcChmdW5jdGlvbihnKSB7IHJldHVybiB2YWxvciA9PT0gJ1RvZGFzJyB8fCBnID09PSB2YWxvcjsgfSk7DQogICAgdmFyIGlkeCA9IGNmZy5ncnVwb3MubWFwKGZ1bmN0aW9uKGcpIHsNCiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgZWwuZGF0YS5sZW5ndGg7IGkrKykgeyBpZiAoZWwuZGF0YVtpXS5uYW1lID09PSBnKSByZXR1cm4gaTsgfQ0KICAgICAgcmV0dXJuIC0xOw0KICAgIH0pOw0KICAgIGlmIChpZHguaW5kZXhPZigtMSkgPj0gMCkgcmV0dXJuOw0KICAgIHZhciBwb3MgPSAodmFsb3IgPT09ICdUb2RhcycpID8gMCA6IGNmZy5ncnVwb3MuaW5kZXhPZih2YWxvcikgKyAxOw0KICAgIFBsb3RseS5yZXN0eWxlKGVsLCB7dmlzaWJsZTogdmlzfSwgaWR4KS50aGVuKGZ1bmN0aW9uKCkgew0KICAgICAgcmV0dXJuIFBsb3RseS5yZWxheW91dChlbCwgeyd1cGRhdGVtZW51c1swXS5hY3RpdmUnOiBwb3N9KTsNCiAgICB9KTsNCiAgfQ0KDQogIGZ1bmN0aW9uIHJlbmRlcihmb3J6YXIpIHsNCiAgICB2YXIgc2VsID0gc2VsZWNjaW9uKCk7DQogICAgc2VsU2VnLnZhbHVlID0gKHNlbC5sZW5ndGggPT09IDEpID8gc2VsWzBdIDogJ1RvZGFzJzsgICAvLyA8PDwgTlVFVk86IG1hcGEgLT4gdGFibGEgKHNpbmNyb25pemEgZWwgc2VsZWN0b3IpDQogICAgdmFyIGZpcm1hID0gc2VsLmpvaW4oJ3wnKSArICcjJyArIGVzdGFkby5rICsgJyMnICsgZXN0YWRvLmRpcjsNCiAgICBpZiAoIWZvcnphciAmJiBmaXJtYSA9PT0gZXN0YWRvLmZpcm1hKSByZXR1cm47DQogICAgZXN0YWRvLmZpcm1hID0gZmlybWE7DQoNCiAgICB2YXIgZCA9IHJlZy5maWx0ZXIoZnVuY3Rpb24ocikgeyByZXR1cm4gc2VsLmluZGV4T2Yoci5ncnVwb19wb2JsYWNpb24pID49IDA7IH0pOw0KICAgIHZhciBzZ24gPSBlc3RhZG8uZGlyID09PSAnZGVzYycgPyAtMSA6IDE7DQogICAgZC5zb3J0KGZ1bmN0aW9uKGEsIGIpIHsNCiAgICAgIHZhciBkaWYgPSAoYVtlc3RhZG8ua10gLSBiW2VzdGFkby5rXSkgKiBzZ247DQogICAgICByZXR1cm4gZGlmICE9PSAwID8gZGlmIDogYS51cHpfbm9tYnJlLmxvY2FsZUNvbXBhcmUoYi51cHpfbm9tYnJlLCAnZXMnKTsNCiAgICB9KTsNCiAgICB2YXIgdG9wID0gZC5zbGljZSgwLCBjZmcudG9wX24pOw0KDQogICAgdmFyIGNvbCA9IG9yZGVuYWJsZXMuZmlsdGVyKGZ1bmN0aW9uKGMpIHsgcmV0dXJuIGMuayA9PT0gZXN0YWRvLms7IH0pWzBdOw0KICAgIHZhciB0eHRTZWwgPSAoc2VsLmxlbmd0aCA9PT0gY2ZnLmdydXBvcy5sZW5ndGgpID8gJ3RvZGFzIGxhcyBzZWdtZW50YWNpb25lcycNCiAgICAgICAgICAgICAgIDogKHNlbC5sZW5ndGggPyBzZWwuam9pbignLCAnKS50b0xvd2VyQ2FzZSgpIDogJ25pbmd1bmEgc2VnbWVudGFjacOzbicpOw0KICAgIHRpdHVsby5pbm5lckhUTUwgPSAnPGI+UmFua2luZyBkZSBVUFo8L2I+IMK3IFRvcCAnICsgdG9wLmxlbmd0aCArICcgZGUgJyArIGQubGVuZ3RoICsgJyBVUFogwrcgJyArIHR4dFNlbCArDQogICAgICAnPGJyPjxzcGFuPk9yZGVuYWRvIHBvciA8Yj4nICsgY29sLnQudG9Mb3dlckNhc2UoKSArICc8L2I+ICcgKw0KICAgICAgKGVzdGFkby5kaXIgPT09ICdkZXNjJyA/ICcoZGUgbWF5b3IgYSBtZW5vciknIDogJyhkZSBtZW5vciBhIG1heW9yKScpICsgJzwvc3Bhbj4nOw0KDQogICAgdmFyIGggPSAnPHRhYmxlIGNsYXNzPSJyay10YWJsYSI+PHRoZWFkPjx0cj48dGggY2xhc3M9InJrLW51bSIgc3R5bGU9InRleHQtYWxpZ246Y2VudGVyIj4jPC90aD4nOw0KICAgIGNvbHMuZm9yRWFjaChmdW5jdGlvbihjKSB7DQogICAgICB2YXIgZmxlY2hhID0gKGMuayA9PT0gZXN0YWRvLmspID8gKGVzdGFkby5kaXIgPT09ICdkZXNjJyA/ICcg4pa8JyA6ICcg4payJykgOiAnJzsNCiAgICAgIGggKz0gJzx0aCBjbGFzcz0iJyArIChjLm51bSA/ICdyay1udW0gcmstb3JkJyA6ICcnKSArICciJyArIChjLm51bSA/ICcgZGF0YS1rPSInICsgYy5rICsgJyInIDogJycpICsgJz4nICsgYy50ICsgZmxlY2hhICsgJzwvdGg+JzsNCiAgICB9KTsNCiAgICBoICs9ICc8L3RyPjwvdGhlYWQ+PHRib2R5Pic7DQogICAgdG9wLmZvckVhY2goZnVuY3Rpb24ociwgaSkgew0KICAgICAgdmFyIGNsYXNlcyA9IChpID09PSAwID8gJ3JrLXByaW1lcm8gJyA6ICcnKSArIChyLmVzX2V4dHJlbW8gPyAncmstZXh0JyA6ICcnKTsNCiAgICAgIGggKz0gJzx0ciBjbGFzcz0iJyArIGNsYXNlcyArICciPjx0ZCBjbGFzcz0icmstcG9zIj4nICsgKGkgKyAxKSArICc8L3RkPic7DQogICAgICBjb2xzLmZvckVhY2goZnVuY3Rpb24oYykgew0KICAgICAgICBpZiAoYy5rID09PSAnZ3J1cG9fcG9ibGFjaW9uJykgew0KICAgICAgICAgIGggKz0gJzx0ZD48c3BhbiBzdHlsZT0iY29sb3I6JyArIChjZmcuY29sb3Jlc1tyLmdydXBvX3BvYmxhY2lvbl0gfHwgJyM5OTknKSArICciPuKWoDwvc3Bhbj4gJyArIGVzYyhyLmdydXBvX3BvYmxhY2lvbikgKyAnPC90ZD4nOw0KICAgICAgICB9IGVsc2UgaWYgKGMubnVtKSB7DQogICAgICAgICAgaCArPSAnPHRkIGNsYXNzPSJyay1udW0iPicgKyBmKHJbYy5rXSwgYy5kKSArICc8L3RkPic7DQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgaCArPSAnPHRkPicgKyBlc2MocltjLmtdKSArICc8L3RkPic7DQogICAgICAgIH0NCiAgICAgIH0pOw0KICAgICAgaCArPSAnPC90cj4nOw0KICAgIH0pOw0KICAgIGggKz0gJzwvdGJvZHk+PC90YWJsZT4nOw0KICAgIHdyYXAuaW5uZXJIVE1MID0gaDsNCiAgfQ0KDQogIC8vIDw8PCBOVUVWTzogYWwgY2FtYmlhciBsYSBzZWdtZW50YWNpw7NuIGVuIGxhIHRhYmxhLCBzZSBtdWV2ZSBlbCBtYXBhDQogIHNlbFNlZy5hZGRFdmVudExpc3RlbmVyKCdjaGFuZ2UnLCBmdW5jdGlvbigpIHsgYXBsaWNhckFsTWFwYShzZWxTZWcudmFsdWUpOyByZW5kZXIodHJ1ZSk7IH0pOw0KICBzZWxLLmFkZEV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsIGZ1bmN0aW9uKCkgeyBlc3RhZG8uayA9IHNlbEsudmFsdWU7IHJlbmRlcih0cnVlKTsgfSk7DQogIHNlbERpci5hZGRFdmVudExpc3RlbmVyKCdjaGFuZ2UnLCBmdW5jdGlvbigpIHsgZXN0YWRvLmRpciA9IHNlbERpci52YWx1ZTsgcmVuZGVyKHRydWUpOyB9KTsNCiAgd3JhcC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGZ1bmN0aW9uKGUpIHsNCiAgICB2YXIgdGggPSBlLnRhcmdldC5jbG9zZXN0ID8gZS50YXJnZXQuY2xvc2VzdCgndGgucmstb3JkJykgOiBudWxsOw0KICAgIGlmICghdGgpIHJldHVybjsNCiAgICB2YXIgayA9IHRoLmdldEF0dHJpYnV0ZSgnZGF0YS1rJyk7DQogICAgaWYgKGVzdGFkby5rID09PSBrKSB7IGVzdGFkby5kaXIgPSAoZXN0YWRvLmRpciA9PT0gJ2Rlc2MnID8gJ2FzYycgOiAnZGVzYycpOyB9DQogICAgZWxzZSB7IGVzdGFkby5rID0gazsgZXN0YWRvLmRpciA9ICdkZXNjJzsgfQ0KICAgIHNlbEsudmFsdWUgPSBlc3RhZG8uazsNCiAgICBzZWxEaXIudmFsdWUgPSBlc3RhZG8uZGlyOw0KICAgIHJlbmRlcih0cnVlKTsNCiAgfSk7DQoNCiAgcmVuZGVyKHRydWUpOw0KICBlbC5vbigncGxvdGx5X2FmdGVycGxvdCcsIGZ1bmN0aW9uKCkgeyByZW5kZXIoZmFsc2UpOyB9KTsNCn0NCiktLS0iDQoNCmZpZyA8LSBodG1sd2lkZ2V0czo6b25SZW5kZXIoDQogIGZpZywganNfcmFua2luZywNCiAgZGF0YSA9IGxpc3QoDQogICAgcmVnaXN0cm9zID0gYXMuY2hhcmFjdGVyKHRvSlNPTihyYW5raW5nX2RhdG9zLCBkYXRhZnJhbWUgPSAicm93cyIsIGF1dG9fdW5ib3ggPSBUUlVFLCBkaWdpdHMgPSBOQSkpLA0KICAgIGNmZyAgICAgICA9IGFzLmNoYXJhY3Rlcih0b0pTT04oY2ZnLCBhdXRvX3VuYm94ID0gVFJVRSkpDQogICkNCikNCg0KZmlnDQpgYGANCg0KYGBge3IgZGF0b3NfaGFsbGF6Z29zfQ0KIyBDaWZyYXMgZGUgbGEgc2VjY2nDs24gZGUgSGFsbGF6Z29zOiBzZSBjYWxjdWxhbiBhcXXDrSB5IGVsIHRleHRvIGxhcyBsbGFtYSBjb24gYHIgLi4uYCwNCiMgYXPDrSBlbCBpbmZvcm1lIHNlIGFjdHVhbGl6YSBzb2xvIHNpIGNhbWJpYW4gbG9zIGRhdG9zLCBsYSBzZW1pbGxhIG8gbG9zIHVtYnJhbGVzLg0KcGN0IDwtIGZ1bmN0aW9uKHgpIHBhc3RlMChudW0oMTAwICogeCwgYWNjdXJhY3kgPSAwLjEpLCAiICUiKQ0KDQojIENvcnJlbGFjacOzbiAoU3BlYXJtYW4pIGRlIGxhIG1hdHJpeiBkZWwgYXBhcnRhZG8gNC40LCBjb24gZWwgc2lnbm8gbWVub3MgdGlwb2dyw6FmaWNvDQpyaG9fdHh0IDwtIGZ1bmN0aW9uKGcsIGRfLCBlXykgew0KICByIDwtIGNvcnJlbGFjaW9uZXMkcmhvW2NvcnJlbGFjaW9uZXMkZ3J1cG8gPT0gZyAmIGNvcnJlbGFjaW9uZXMkZGVsaXRvID09IGRfICYgY29ycmVsYWNpb25lcyRlZGFkID09IGVfXQ0KICBzdWIoIi0iLCAi4oiSIiwgbnVtKHIsIGFjY3VyYWN5ID0gMC4wMSkpDQp9DQoNCiMgUG9yIGdydXBvOiBjYW50aWRhZCBkZSBVUFogeSBwZXNvIGRlIGNhZGEgZGVsaXRvIChzb2JyZSBodXJ0byArIGxlc2lvbmVzICsgaG9taWNpZGlvcyArIHZpb2xlbmNpYSBpbnRyYWZhbWlsaWFyKQ0KcG9yX2dydXBvIDwtIHVweiAlPiUNCiAgZ3JvdXBfYnkoZ3J1cG9fcG9ibGFjaW9uKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIG4gICAgICAgICA9IG4oKSwNCiAgICBkZWxpdG9zNCAgPSBzdW0oY2Fzb3NfaHVydG9fdG90YWwgKyBjYXNvc19sZXNpb25lcyArIGNhc29zX2hvbWljaWRpb3MgKyBjYXNvc192aW9sZW5jaWFfaW50cmFmYW1pbGlhciksDQogICAgcGN0X2h1cnRvID0gc3VtKGNhc29zX2h1cnRvX3RvdGFsKSAvIGRlbGl0b3M0LA0KICAgIHBjdF92aWYgICA9IHN1bShjYXNvc192aW9sZW5jaWFfaW50cmFmYW1pbGlhcikgLyBkZWxpdG9zNCwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkgJT4lDQogIHNwbGl0KC4kZ3J1cG9fcG9ibGFjaW9uKQ0KDQpnX2JhamEgIDwtIHBvcl9ncnVwb1tbIlBvYmxhY2nDs24gYmFqYSJdXQ0KZ19tZWRpYSA8LSBwb3JfZ3J1cG9bWyJQb2JsYWNpw7NuIG1lZGlhIl1dDQpnX2FsdGEgIDwtIHBvcl9ncnVwb1tbIlBvYmxhY2nDs24gYWx0YSJdXQ0KDQojIFJhbmtpbmc6IGNvbXBvc2ljacOzbiBkZWwgdG9wIGRlIG1heW9yZXMgeSBkZSBtZW5vcmVzIHRhc2FzIHRvdGFsZXMNCnRvcF9tYXkgPC0gdXB6ICU+JSBzbGljZV9tYXgodGFzYV90b3RhbCwgbiA9IHRvcF9uLCB3aXRoX3RpZXMgPSBGQUxTRSkNCnRvcF9tZW4gPC0gdXB6ICU+JSBzbGljZV9taW4odGFzYV90b3RhbCwgbiA9IHRvcF9uLCB3aXRoX3RpZXMgPSBGQUxTRSkNCm5fbWF5X2JhamEgIDwtIHN1bSh0b3BfbWF5JGdydXBvX3BvYmxhY2lvbiA9PSAiUG9ibGFjacOzbiBiYWphIikNCm5fbWVuX21lZGlhIDwtIHN1bSh0b3BfbWVuJGdydXBvX3BvYmxhY2lvbiA9PSAiUG9ibGFjacOzbiBtZWRpYSIpDQoNCiMgQWxlcnRhOiBzaSBjYW1iaWFuIGxvcyBkYXRvcyB5IGFsZ3VuYSBhZmlybWFjacOzbiBjdWFsaXRhdGl2YSBkZWwgdGV4dG8gZGVqYSBkZSBzZXIgY2llcnRhLCBlbCB0ZWppZG8gYXZpc2ENCnN0b3BpZm5vdCgNCiAgIkxvcyBhdMOtcGljb3MgZXh0cmVtb3MgeWEgbm8gZXN0w6FuIHNvbG8gZW4gcG9ibGFjacOzbiBiYWphOiByZXZpc2EgZWwgdGV4dG8gZGUgSGFsbGF6Z29zIiA9DQogICAgYWxsKHVweiRncnVwb19wb2JsYWNpb25bdXB6JGVzX2V4dHJlbW9dID09ICJQb2JsYWNpw7NuIGJhamEiKSwNCiAgIkVsIHRvcCBkZSBtYXlvcmVzIHRhc2FzIHlhIG5vIGVzIHRvZG8gZGUgcG9ibGFjacOzbiBiYWphOiByZXZpc2EgZWwgdGV4dG8gZGUgSGFsbGF6Z29zIiA9DQogICAgbl9tYXlfYmFqYSA9PSB0b3BfbiwNCiAgIkVuIGVsIHRvcCBkZSBtZW5vcmVzIHRhc2FzIHlhIG5vIHByZWRvbWluYSBwb2JsYWNpw7NuIG1lZGlhOiByZXZpc2EgZWwgdGV4dG8gZGUgSGFsbGF6Z29zIiA9DQogICAgbl9tZW5fbWVkaWEgPT0gbWF4KHRhYmxlKHRvcF9tZW4kZ3J1cG9fcG9ibGFjaW9uKSkNCikNCmBgYA0KDQojIDUgSGFsbGF6Z29zDQoNCi0gU2UgZXZpZGVuY2lhIHF1ZSBsYSBzZWdtZW50YWNpw7NuIG1lZGlhbnRlIEstbWVhbnMgcGVybWl0ZSBkaXZpZGlyIGxhIHBvYmxhY2nDs24gZGUgQm9nb3TDoSBkZSB1bmEgbWFuZXJhIG3DoXMgYWNvcmRlIGNvbiBsYSBjYW50aWRhZCBkZSBoYWJpdGFudGVzLiBTaW4gZW1iYXJnbywgZWwgZ3J1cG8gZGUgcG9ibGFjacOzbiBhbHRhIHF1ZWRhIGNvbmZvcm1hZG8gcG9yIHNvbG8gYHIgZ19hbHRhJG5gIFVQWi4gRXN0byBwZXJtaXRlIHBsYW50ZWFyIHF1ZSwgZW4gZnV0dXJvcyBhbsOhbGlzaXMsIHNlIGVzdHVkaWUgZXN0YSBzZWdtZW50YWNpw7NuIGRlIG1hbmVyYSBpbmRpdmlkdWFsIHBvciBVUFouDQoNCi0gTG9zIGF0w61waWNvcyBleHRyZW1vcyBzZSBwcmVzZW50YW4gw7puaWNhbWVudGUgZW4gZWwgZ3J1cG8gZGUgcG9ibGFjacOzbiBiYWphLiBTZWfDum4gZWwgYW7DoWxpc2lzLCBlc3RvcyBnZW5lcmFuIHVuIGF1bWVudG8gY29uc2lkZXJhYmxlIGVuIGxhIHRhc2EgdG90YWwgZGUgZGVsaXRvcy4gQWwgYW5hbGl6YXIgY29uIG1heW9yIGRldGFsbGUgbG9zIHJlc3VsdGFkb3MsIHNlIG9ic2VydmEgcXVlIHN1IGVmZWN0byBzZSBjb25jZW50cmEgcHJpbmNpcGFsbWVudGUgZW4gZWwgZGVsaXRvIGRlIGh1cnRvLiBTaW4gZW1iYXJnbywgZXMgaW1wb3J0YW50ZSB0ZW5lciBwcmVzZW50ZSBlc3RhIGluZm9ybWFjacOzbiBwYXJhIGVzdHVkaWFyIGVzdGFzIGByIG5fYXRpcGAgVVBaIHkgY29ub2NlciBsb3MgbW90aXZvcyBkZXRyw6FzIGRlIGxhIGNhbnRpZGFkIGRlIGRlbnVuY2lhcyByZWdpc3RyYWRhcy4NCg0KLSBFbCBodXJ0byBwcmVkb21pbmEgZW4gbG9zIHRyZXMgZ3J1cG9zIHBvYmxhY2lvbmFsZXMsIHBlcm8gcGVzYSBtZW5vcyBhIG1lZGlkYSBxdWUgY3JlY2UgbGEgcG9ibGFjacOzbjogYHIgcGN0KGdfYmFqYSRwY3RfaHVydG8pYCBlbiBwb2JsYWNpw7NuIGJhamEsIGByIHBjdChnX21lZGlhJHBjdF9odXJ0bylgIGVuIG1lZGlhIHkgYHIgcGN0KGdfYWx0YSRwY3RfaHVydG8pYCBlbiBhbHRhLiBMbyBjb250cmFyaW8gcGFzYSBjb24gbGEgdmlvbGVuY2lhIGludHJhZmFtaWxpYXIsIHF1ZSBzdWJlIGRlIGByIHBjdChnX2JhamEkcGN0X3ZpZilgIGEgYHIgcGN0KGdfbWVkaWEkcGN0X3ZpZilgIHkgYHIgcGN0KGdfYWx0YSRwY3RfdmlmKWAuDQoNCi0gRW4gZWwgcmFua2luZyB0b3AgYHIgdG9wX25gIGRlIG1heW9yZXMgdGFzYXMgZGUgZGVudW5jaWFzIHRvdGFsZXMsIHNvbG8gc2UgZXZpZGVuY2lhbiBVUFogY2F0ZWdvcml6YWRhcyBjb21vICJwb2JsYWNpb24gYmFqYSIsIHBlcm8gY3VhbmRvIHNlIHZhbGlkYSBhbCB0b3AgYHIgdG9wX25gIGRlIG1lbm9yZXMgdGFzYXMgZGUgZGVudW5jaWFzIHRvdGFsZXMsIHByZWRvbWluYSBwb2JsYWNpb24gbWVkaWEgY29uIGByIG5fbWVuX21lZGlhYCBVUFogZGUgYHIgdG9wX25gLCBzaWVuZG8gZXN0YXMgZWwgYHIgcGN0KG5fbWVuX21lZGlhIC8gZ19tZWRpYSRuKWAgZGUgdG9kYSBsYXMgY2F0ZWdvcml6YWRhcyBwb3IgcG9ibGFjaW9uIG1lZGlhIChgciBnX21lZGlhJG5gIFVQWikuDQoNCi0gRW4gcG9ibGFjacOzbiBiYWphIHkgbWVkaWEsIG3DoXMgcG9ibGFjacOzbiBqb3ZlbiBzZSBhc29jaWEgY29uIG3DoXMgdmlvbGVuY2lhIGludHJhZmFtaWxpYXIgeSBtZW5vcyBodXJ0bywgcG9yIGVqZW1wbG8sIGVuIHBvYmxhY2lvbiBtZWRpYSwgZW4gZWwgZ3J1cG8gMOKAkzE0IGHDsW9zIGNvbiB2aW9sZW5jaWEgaW50cmFmYW1pbGlhciBsYSBjb3JyZWxhY2lvbiBlcyDPgSA9IGByIHJob190eHQoIlBvYmxhY2nDs24gbWVkaWEiLCAiVmlvbGVuY2lhIGludHJhZmFtaWxpYXIiLCAiMOKAkzE0IGHDsW9zIilgIHkgY29uIGh1cnRvIM+BID0gYHIgcmhvX3R4dCgiUG9ibGFjacOzbiBtZWRpYSIsICJIdXJ0byIsICIw4oCTMTQgYcOxb3MiKWAuIFNvbiBhc29jaWFjaW9uZXMgZW50cmUgVVBaLCBubyBjYXVzYXMuDQoNCiMgNiBMaW1pdGFjaW9uZXMNCg0KLSBTZSBpZGVudGlmaWNhIHVuYSBvcG9ydHVuaWRhZCBkZSBtZWpvcmEgZW4gbG9zIGRhdG9zIGRpc3BvbmlibGVzOiBjb250YXIgY29uIHVuIG1heW9yIGRlc2dsb3NlIGRlIGxhcyBkZW51bmNpYXMgcG9yIGVkYWQgeSB0aXBvIGRlIGRlbGl0byBwZXJtaXRpcsOtYSwgY29tbyBzZSBtZW5jaW9uw7MgYW50ZXJpb3JtZW50ZSwgcmVhbGl6YXIgdW4gYW7DoWxpc2lzIGRlIGNvcnJlbGFjacOzbiBtw6FzIGRldGFsbGFkbyBlbnRyZSBlc3RhcyB2YXJpYWJsZXMuDQoNCi0gRWwgcGVyaW9kbyBkZSBhbsOhbGlzaXMgZGUgbGFzIGRlbnVuY2lhcyB5IGxhcyB0YXNhcyBjb21wcmVuZGUgw7puaWNhbWVudGUgNyBtZXNlcywgZGVqYW5kbyBwb3IgZnVlcmEgbG9zIMO6bHRpbW9zIG1lc2VzIGRlbCBhw7FvLiBFc3RvcyBtZXNlcyBwb2Ryw61hbiBzZXIgcmVsZXZhbnRlcywgY29uc2lkZXJhbmRvIHF1ZSBjb3JyZXNwb25kZW4gYSB1bmEgw6lwb2NhIGVuIGxhIHF1ZSBzZSBnZW5lcmFuIG3DoXMgaW50ZXJhY2Npb25lcyBlbnRyZSBsb3MgaGFiaXRhbnRlcyBkZSBCb2dvdMOhLiBFc3RvIHBsYW50ZWEgdW5hIHByZWd1bnRhOiDCv3NlIG1hbnRlbmRyw61hbiBsYXMgdGVuZGVuY2lhcyBvYnNlcnZhZGFzIGR1cmFudGUgZXNvcyA1IG1lc2VzIG8gc2UgcHJlc2VudGFyw61hbiBhdW1lbnRvcyBvIGRpc21pbnVjaW9uZXMgY29uc2lkZXJhYmxlcyBlbiBsYXMgdGFzYXMgZGUgZGVudW5jaWFzIHBvciBkZWxpdG9zPw0KDQojIDcgQ29uY2x1c2lvbg0KDQpMYSBzZWd1cmlkYWQgZW4gbGFzIFVQWiwgc2Vnw7puIGxvIHZhbGlkYWRvIGVuIGVsIGFuw6FsaXNpcywgcHJlc2VudGEgdmFyaWFzIG9ic2VydmFjaW9uZXMgYSB0ZW5lciBlbiBjdWVudGEuIFVuYSBlcyBxdWUsIGVudHJlIG3DoXMgcG9ibGFjacOzbiwgbcOhcyBjYXNvcyBkZSBkZW51bmNpYXM7IHNpbiBlbWJhcmdvLCBlc3RvIG5vIGFwbGljYSBkZWwgdG9kbyBjdWFuZG8gc2UgZXZpZGVuY2lhbiBhdMOtcGljb3MgZXh0cmVtb3MsIGVuIGxvcyBjdWFsZXMgbGEgcG9ibGFjacOzbiBlcmEgbXVjaG8gbWVub3IuIEFkZW3DoXMsIHRlbmllbmRvIGVuIGN1ZW50YSBsYSBwb2JsYWNpw7NuLCB0YW1wb2NvIGVzIHZpYWJsZSBkZWNpciBxdWUgZWwgw6FyZWEgc2UgcmVsYWNpb25hIGRpcmVjdGFtZW50ZSBjb24gbGEgY2FudGlkYWQgZGUgcGVyc29uYXMgcXVlIGhhYml0YW4gbGEgVVBaLCBkZWJpZG8gYSBxdWUgaGF5IGNhc29zIGVuIGxvcyBxdWUgdW5hIFVQWiBlcyBwZXF1ZcOxYSBlbiBjb21wYXJhY2nDs24gY29uIG90cmFzIG3DoXMgZ3JhbmRlcy4gU8OtIHNlIHB1ZWRlIGluZGljYXIgcXVlIGxhcyBkZW51bmNpYXMgcG9yIGh1cnRvIHByZWRvbWluYW4gYSBuaXZlbCBnZW5lcmFsIHkgcG9yIHNlZ21lbnRvIHBvYmxhY2lvbmFsLg0KDQpDb24gcmVzcGVjdG8gYSBsYXMgY29ycmVsYWNpb25lcywgZXMgbmVjZXNhcmlvIGVudGVuZGVyIHF1ZSBlbiBlc3RvcyBjYXNvcyBzZSBidXNjw7MgZ2VuZXJhciB1bmEgYXNvY2lhY2nDs24sIHF1ZSBkaXJlY3RhbWVudGUgbm8gaW5kaWNhIGNhdXNhbGlkYWQuIFBhcmEgcG9kZXIgYWZpcm1hciBlc3RhcyByZWxhY2lvbmVzLCBlcyBuZWNlc2FyaW8gY29udGFyIGNvbiBlbCBkZXRhbGxlIGRlIGxhIGVkYWQgeSBkZSBsYXMgZGVudW5jaWFzIGdlbmVyYWRhcyBwb3IgY2FkYSBkZWxpdG8uIEVzIGRlY2lyLCBwYXJhIHVuIHByaW1lciBhbsOhbGlzaXMgZWwgaW5mb3JtZSBlcyBtdXkgw7p0aWwsIGF1bnF1ZSBhYnJlIGxhIHB1ZXJ0YSBhIHVuIG1heW9yIGRldGFsbGUgeSBlbnRlbmRpbWllbnRvLCBjb21vIHBvciBlamVtcGxvIGVuIGxhcyBVUFogYXTDrXBpY2FzIGV4dHJlbWFzIGVuIHRhc2EgdG90YWwgZGUgZGVsaXRvcy4=