Datos y
Limpieza
Carga del
archivo
El archivo trae 17 columnas. Para que ninguna celda con formato de
fecha corrompido se pierda al cargar, se fuerza el tipo
numeric en las columnas de coordenadas: así, cualquier
celda mal formateada como fecha llega a R como su número de serie
original en vez de como texto inservible.
# Busca el archivo en varias rutas probables en lugar de asumir una sola.
# Si ninguna existe, el error indica el directorio de trabajo actual y
# todas las rutas revisadas, para localizar el archivo sin adivinar.
ruta_documento_actual <- tryCatch({
if (requireNamespace("rstudioapi", quietly = TRUE) && rstudioapi::isAvailable()) {
dirname(rstudioapi::getSourceEditorContext()$path)
} else {
NA_character_
}
}, error = function(e) NA_character_)
buscar_archivo_datos <- function(nombre_archivo = "Trama_Waze.xlsx") {
candidatos <- unique(c(
nombre_archivo,
file.path(getwd(), nombre_archivo),
if (!is.na(ruta_documento_actual)) file.path(ruta_documento_actual, nombre_archivo),
"C:/Pontificia_Un_Javeriana/Analisis_Geoespacial/M02_U02_Patrones/Trama_Waze.xlsx",
file.path(Sys.getenv("USERPROFILE"), "Downloads", nombre_archivo),
file.path(Sys.getenv("USERPROFILE"), "Documents", nombre_archivo),
file.path(Sys.getenv("USERPROFILE"), "Desktop", nombre_archivo)
))
candidatos <- candidatos[nzchar(candidatos)]
encontrados <- candidatos[file.exists(candidatos)]
if (length(encontrados) == 0) {
stop(
"\nNo se encontro '", nombre_archivo, "' en ninguna ruta candidata.\n",
"Directorio de trabajo actual (getwd()): ", getwd(), "\n",
"Rutas revisadas:\n - ", paste(candidatos, collapse = "\n - "), "\n",
"Solucion: copia '", nombre_archivo, "' a la carpeta del .Rmd ",
"(la mas simple), o agrega su ruta real dentro del vector 'candidatos' ",
"en este chunk."
)
}
message("Archivo de datos encontrado en: ", encontrados[1])
encontrados[1]
}
ruta_excel <- buscar_archivo_datos("Trama_Waze.xlsx")
tipos_columnas <- c("numeric", "numeric", "text", "numeric", "logical",
"numeric", "numeric", "text", "text", "numeric",
"numeric", "text", "text", "numeric", "numeric",
"numeric", "text")
datos_waze <- read_excel(ruta_excel, sheet = "Hoja 1",
col_types = tipos_columnas)
kable(head(datos_waze, 5), caption = "Primeros registros de la trama Waze") %>%
kable_styling(bootstrap_options = c("striped", "hover"), font_size = 12) %>%
scroll_box(width = "100%")
Primeros registros de la trama Waze
|
id
|
waze_json_trama_id
|
country
|
reportRating
|
reportByMunicipalityUser
|
confidence
|
reliability
|
type
|
uuid
|
roadType
|
magvar
|
subtype
|
street
|
location_x
|
location_y
|
pubMillis
|
creation_Date
|
|
16
|
14
|
CO
|
2
|
FALSE
|
1
|
8
|
HAZARD
|
74a153fa-6ccd-4d6b-a94b-db01a88b002d
|
3
|
96
|
HAZARD_ON_SHOULDER_CAR_STOPPED
|
NA
|
-74016928
|
4938376
|
1.727315e+12
|
2024-09-26 01:53:49.600
|
|
17
|
14
|
CO
|
3
|
FALSE
|
4
|
10
|
HAZARD
|
b91961a4-e32c-4770-b2a0-551d7add5669
|
3
|
153
|
HAZARD_ON_SHOULDER_CAR_STOPPED
|
Variante Cajicá / RD45A Ramal A >(S)
|
-74016985
|
2733202
|
1.727313e+12
|
2024-09-26 01:53:49.600
|
|
18
|
14
|
CO
|
0
|
FALSE
|
4
|
10
|
HAZARD
|
b9262a77-b38b-46bd-b0b2-cda1b855a549
|
3
|
27
|
HAZARD_ON_SHOULDER_CAR_STOPPED
|
Bogotá-Tocancipá / RN55-01 >(N)
|
-73996251
|
4925537
|
1.727313e+12
|
2024-09-26 01:53:49.600
|
|
20
|
15
|
CO
|
2
|
FALSE
|
1
|
8
|
HAZARD
|
74a153fa-6ccd-4d6b-a94b-db01a88b002d
|
3
|
96
|
HAZARD_ON_SHOULDER_CAR_STOPPED
|
NA
|
-74016928
|
4938376
|
1.727315e+12
|
2024-09-26 01:54:02.100
|
|
21
|
15
|
CO
|
3
|
FALSE
|
4
|
10
|
HAZARD
|
b91961a4-e32c-4770-b2a0-551d7add5669
|
3
|
153
|
HAZARD_ON_SHOULDER_CAR_STOPPED
|
Variante Cajicá / RD45A Ramal A >(S)
|
-74016985
|
2733202
|
1.727313e+12
|
2024-09-26 01:54:02.100
|
Corrección de
coordenadas
location_x y location_y están multiplicadas
por 10^6, pero una parte de los registros perdió uno o dos dígitos en la
exportación, lo que desplaza la coordenada un orden de magnitud. La
función siguiente prueba potencias de diez hasta ubicar el valor en el
rango geográfico plausible del corredor (longitud entre 73.9° y 74.1°
oeste, latitud entre 4.0° y 5.2° norte), conservando el signo
original.
corregir_magnitud <- function(valor, minimo, maximo) {
base <- abs(valor) / 1e6
signo <- sign(valor)
resultado <- rep(NA_real_, length(valor))
for (potencia in 0:3) {
candidato <- base * 10^potencia
pendiente <- is.na(resultado) & candidato >= minimo & candidato <= maximo
resultado[pendiente] <- signo[pendiente] * candidato[pendiente]
}
resultado
}
datos_waze <- datos_waze %>%
mutate(
longitud = corregir_magnitud(location_x, 73.9, 74.1),
latitud = corregir_magnitud(location_y, 4.0, 5.2)
)
res_total_filas <- nrow(datos_waze)
res_filas_corruptas <- sum(is.na(datos_waze$longitud) | is.na(datos_waze$latitud))
res_pct_recuperado <- formatC(100 * (1 - res_filas_corruptas / res_total_filas),
format = "f", digits = 1)
De 5070 registros, 74 no tenían un valor numérico recuperable detrás
de la celda corrompida y quedaron fuera. El resto, 98.5%, se reubicó
dentro del rango esperado sin necesidad de descartarlo.
Interpretación: perder un dígito en la exportación
es un problema sistemático del archivo, no errores sueltos. Corregir por
magnitud en vez de eliminar la fila evita perder información sobre
tramos que solo aparecen en esos registros.
Ventana y eventos
únicos
La ventana de estudio (ventana) cubre el tramo entre las
coordenadas -74.04331° y -73.9929° de longitud y 4.885736° a 4.948562°
de latitud, la franja donde aparecen los nombres de calle de Cajicá y
Tocancipá en la trama. Cada evento se repite mientras está activo, así
que se conserva un solo punto por evento (uuid), tomando su
primer reporte de cada día.
ventana <- owin(xrange = c(-74.04331, -73.9929),
yrange = c(4.885736, 4.948562))
datos_waze <- datos_waze %>%
mutate(fecha_hora = ymd_hms(creation_Date, quiet = TRUE),
fecha = as.Date(fecha_hora))
datos_validos <- datos_waze %>%
filter(!is.na(longitud), !is.na(latitud),
longitud >= ventana$xrange[1], longitud <= ventana$xrange[2],
latitud >= ventana$yrange[1], latitud <= ventana$yrange[2])
eventos_unicos <- datos_validos %>%
filter(fecha %in% as.Date(c("2024-09-26", "2024-09-27"))) %>%
arrange(fecha_hora) %>%
distinct(uuid, fecha, .keep_all = TRUE)
tabla_resumen <- eventos_unicos %>%
count(type, fecha, name = "eventos_unicos") %>%
arrange(type, fecha)
kable(tabla_resumen, caption = "Eventos únicos por tipo y día") %>%
kable_styling(bootstrap_options = c("striped", "hover"), font_size = 12)
Eventos únicos por tipo y día
|
type
|
fecha
|
eventos_unicos
|
|
ACCIDENT
|
2024-09-26
|
4
|
|
ACCIDENT
|
2024-09-27
|
6
|
|
HAZARD
|
2024-09-26
|
15
|
|
HAZARD
|
2024-09-27
|
28
|
|
JAM
|
2024-09-26
|
76
|
|
JAM
|
2024-09-27
|
121
|
|
ROAD_CLOSED
|
2024-09-26
|
3
|
|
ROAD_CLOSED
|
2024-09-27
|
3
|
res_pct_jam_total <- formatC(100 * sum(eventos_unicos$type == "JAM") / nrow(eventos_unicos),
format = "f", digits = 0)
Interpretación: la congestión (JAM) es
el 77% de los 256 eventos únicos de los dos días, más que las otras tres
categorías juntas. Cierres de vía y accidentes quedan con tan pocos
puntos por día que cualquier prueba estadística sobre ellos debe leerse
como exploratoria.
Patrón de
Congestión
La congestión (JAM) recibe el análisis completo porque
es el 77% de los eventos únicos registrados, la única categoría con
suficientes puntos para sostener pruebas de aleatoriedad espacial con
algo de potencia estadística. Las otras tres se revisan más adelante sin
la misma profundidad.
Construcción del
patrón de puntos
jam_26 <- eventos_unicos %>% filter(type == "JAM", fecha == as.Date("2024-09-26"))
jam_27 <- eventos_unicos %>% filter(type == "JAM", fecha == as.Date("2024-09-27"))
jam_26_ppp <- unique(ppp(x = jam_26$longitud, y = jam_26$latitud, window = ventana))
jam_27_ppp <- unique(ppp(x = jam_27$longitud, y = jam_27$latitud, window = ventana))
jam_26_sf <- ppp_a_sf(jam_26_ppp)
jam_27_sf <- ppp_a_sf(jam_27_ppp)
res_n_jam_26 <- npoints(jam_26_ppp)
res_n_jam_27 <- npoints(jam_27_ppp)
res_incremento_jam <- formatC(100 * (res_n_jam_27 / res_n_jam_26 - 1), format = "f", digits = 0)
El jueves se registraron 76 eventos únicos de congestión y el viernes
121, un 59% más. La sección de perfil horario revisa si ese aumento se
concentra en alguna franja particular del día, lo que ayudaría a
descartar o sostener una explicación de tipo fin de semana.
tm_basemap(mapa_base) +
tm_shape(jam_26_sf) +
tm_dots(col = paleta[1], size = 0.05) +
tm_layout(title = "Congestión, jueves 26 de septiembre")
Figura 1. Patrón espacial de eventos de congestión
sobre el mapa de Bogotá, jueves 26 de septiembre de 2024.
tm_basemap(mapa_base) +
tm_shape(jam_27_sf) +
tm_dots(col = paleta[2], size = 0.05) +
tm_layout(title = "Congestión, viernes 27 de septiembre")
Figura 2. Patrón espacial de eventos de congestión
sobre el mapa de Bogotá, viernes 27 de septiembre de 2024.
Perfil horario
Antes de entrar a las pruebas espaciales formales conviene saber a
qué hora arrancó cada evento único de congestión, no cuántas veces se
reportó mientras seguía activo, que ya se filtró en la
deduplicación.
perfil_horario <- eventos_unicos %>%
filter(type == "JAM") %>%
mutate(hora = lubridate::hour(fecha_hora)) %>%
count(fecha, hora)
hora_pico_26 <- perfil_horario %>% filter(fecha == as.Date("2024-09-26")) %>%
filter(n == max(n)) %>% pull(hora) %>% min()
hora_pico_27 <- perfil_horario %>% filter(fecha == as.Date("2024-09-27")) %>%
filter(n == max(n)) %>% pull(hora) %>% min()
res_hora_pico_26 <- hora_pico_26
res_hora_pico_27 <- hora_pico_27
res_n_pico_26 <- perfil_horario %>% filter(fecha == as.Date("2024-09-26"), hora == hora_pico_26) %>% pull(n)
res_n_pico_27 <- perfil_horario %>% filter(fecha == as.Date("2024-09-27"), hora == hora_pico_27) %>% pull(n)
ggplot(perfil_horario, aes(x = hora, y = n, color = factor(fecha))) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
scale_x_continuous(breaks = seq(0, 23, 2)) +
scale_color_manual(values = paleta[1:2], labels = c("26 sept", "27 sept"),
name = "Día") +
labs(title = "Eventos únicos de congestión por hora de inicio",
x = "Hora del día", y = "Eventos únicos") +
theme_minimal()

Figura 3. Hora de inicio de los eventos únicos de
congestión, jueves y viernes.
Interpretación: el jueves la mayor cantidad de
eventos arrancó a las 11:00, con 17 inicios. El viernes el pico se
corrió a las 20:00, con 21 inicios. Ese corrimiento hacia la noche es
compatible con un patrón de salida de fin de semana, aunque esta trama
no incluye datos de movilidad inter-municipal para confirmarlo de forma
directa.
Prueba
CSR
Ji-cuadrado por
cuadrantes
Se divide el corredor en una malla de 4 por 3 cuadrantes,
dimensionada para que cada celda tenga en promedio más de cinco eventos
esperados bajo aleatoriedad, condición que necesita la aproximación
ji-cuadrado para ser válida.
prueba_26 <- quadrat.test(jam_26_ppp, nx = 4, ny = 3)
prueba_27 <- quadrat.test(jam_27_ppp, nx = 4, ny = 3)
res_chi_26 <- formatC(prueba_26$statistic, format = "f", digits = 2)
res_p_26 <- formatC(prueba_26$p.value, format = "f", digits = 4)
res_chi_27 <- formatC(prueba_27$statistic, format = "f", digits = 2)
res_p_27 <- formatC(prueba_27$p.value, format = "f", digits = 4)
# Disparidad real entre cuadrantes, solo contando los que tienen al menos
# un evento (los cuadrantes en cero suelen caer fuera del trazado vial y
# no aportan una comparación útil de intensidad).
conteo_26 <- as.vector(quadratcount(jam_26_ppp, nx = 4, ny = 3))
conteo_26_activos <- conteo_26[conteo_26 > 0]
res_cuadrante_max_26 <- max(conteo_26_activos)
res_cuadrante_min_26 <- min(conteo_26_activos)
res_cuadrante_razon_26 <- formatC(res_cuadrante_max_26 / res_cuadrante_min_26,
format = "f", digits = 1)
plot(quadratcount(jam_26_ppp, nx = 4, ny = 3),
main = "Conteo por cuadrantes, 26 de septiembre")
points(jam_26_ppp, col = paleta[1], pch = 16, cex = 0.6)

Figura 4. Conteo de eventos de congestión por
cuadrante, jueves 26 de septiembre.
El estadístico ji-cuadrado es 65.47 el jueves y 99.07 el viernes,
ambos con valor p de 0.0000 y 0.0000 respectivamente.
Interpretación: los dos valores p quedan muy por
debajo de 0.05, así que se rechaza la aleatoriedad espacial completa los
dos días. El cuadrante con más eventos el jueves tiene 18, frente a 2 en
el que menos tiene entre los que registran al menos un evento, una razón
de 9.0 a 1 que ya anticipa que la intensidad no es constante y que
conviene usar la función K inhomogénea más adelante.
Vecino más cercano
(función G)
La prueba de cuadrantes mira la intensidad por zonas, pero no dice
nada sobre qué tan cerca está cada evento de su vecino más próximo. La
función G y la distancia media al vecino más cercano completan ese
diagnóstico a una escala más fina.
nn_26 <- nndist(jam_26_ppp) * m_por_grado
nn_27 <- nndist(jam_27_ppp) * m_por_grado
res_nn_media_26 <- formatC(mean(nn_26), format = "f", digits = 0)
res_nn_media_27 <- formatC(mean(nn_27), format = "f", digits = 0)
res_nn_mediana_26 <- formatC(median(nn_26), format = "f", digits = 0)
res_nn_mediana_27 <- formatC(median(nn_27), format = "f", digits = 0)
area_m2 <- area.owin(ventana) * m_por_grado^2
lambda_26 <- res_n_jam_26 / area_m2
lambda_27 <- res_n_jam_27 / area_m2
nn_csr_26 <- 1 / (2 * sqrt(lambda_26))
nn_csr_27 <- 1 / (2 * sqrt(lambda_27))
res_nn_csr_26 <- formatC(nn_csr_26, format = "f", digits = 0)
res_nn_csr_27 <- formatC(nn_csr_27, format = "f", digits = 0)
res_nn_ratio_26 <- formatC(100 * mean(nn_26) / nn_csr_26, format = "f", digits = 0)
res_nn_ratio_27 <- formatC(100 * mean(nn_27) / nn_csr_27, format = "f", digits = 0)
g_26 <- Gest(jam_26_ppp, correction = "km")
g_27 <- Gest(jam_27_ppp, correction = "km")
par(mfrow = c(1, 2))
plot(g_26, main = "G, 26 sept", col = c(paleta[1], "grey50"))
plot(g_27, main = "G, 27 sept", col = c(paleta[2], "grey50"))

par(mfrow = c(1, 1))
Figura 5. Función G observada contra la teórica de
Poisson, jueves y viernes.
La distancia media al vecino más cercano es de 121 metros el jueves y
54 metros de mediana; bajo aleatoriedad completa, con 76 eventos en esta
ventana, esa distancia esperada sería de 358 metros. El viernes la media
observada es de 84 metros frente a 284 metros esperados.
Interpretación: la distancia observada es apenas el
34% de la esperada el jueves y el 30% el viernes. Los eventos están
mucho más cerca entre sí de lo que predice el azar, y esa proporción se
mantiene similar entre los dos días aunque el viernes tenga más eventos,
lo que sugiere que el grado de agrupamiento relativo no cambia, solo su
volumen.
Función
K
Distancia de
referencia
El radio máximo de análisis se calcula desde las dimensiones de la
ventana, no desde las distancias entre puntos, para no calcular pares de
distancias de forma cuadrática sobre cientos de eventos.
ancho_ventana <- diff(ventana$xrange)
alto_ventana <- diff(ventana$yrange)
r_max <- 0.25 * sqrt(ancho_ventana^2 + alto_ventana^2)
r_seq <- seq(0, r_max, length.out = 128)
res_r_max_m <- formatC(r_max * m_por_grado, format = "f", digits = 0)
El radio máximo equivale a 2235 metros, una cuarta parte de la
diagonal del corredor.
K homogénea
k_homog_26 <- Kest(jam_26_ppp, r = r_seq, correction = "Ripley")
k_homog_27 <- Kest(jam_27_ppp, r = r_seq, correction = "Ripley")
par(mfrow = c(1, 2))
plot(k_homog_26, main = "K homogénea, 26 sept", col = c(paleta[1], "grey50"))
plot(k_homog_27, main = "K homogénea, 27 sept", col = c(paleta[2], "grey50"))

par(mfrow = c(1, 1))
Figura 6. Función K homogénea de Ripley, jueves y
viernes.
Interpretación: la curva observada queda por encima
de la de Poisson en los dos días, lo que de nuevo apunta a agrupamiento.
Pero como la prueba de cuadrantes ya mostró que la intensidad cambia por
zona, parte de esta separación puede ser solo el efecto de tener más
eventos en unas zonas que en otras, no agrupamiento adicional entre
eventos cercanos.
K inhomogénea
Como la intensidad no es constante, se usa la función K inhomogénea,
que compara cada evento contra una intensidad local en lugar de un
promedio único para todo el corredor.
k_inhom_26 <- Kinhom(jam_26_ppp, r = r_seq, correction = "Ripley")
k_inhom_27 <- Kinhom(jam_27_ppp, r = r_seq, correction = "Ripley")
par(mfrow = c(1, 2))
plot(k_inhom_26, main = "K inhomogénea, 26 sept", col = c(paleta[1], "grey50"))
plot(k_inhom_27, main = "K inhomogénea, 27 sept", col = c(paleta[2], "grey50"))

par(mfrow = c(1, 1))
Figura 7. Función K inhomogénea de Ripley, jueves y
viernes.
Interpretación: al controlar por la intensidad
local, la curva se acerca más a la de Poisson que en la versión
homogénea. Buena parte de lo que parecía agrupamiento es variación de la
densidad de fondo del corredor, lo que ya habían anticipado tanto el
ji-cuadrado como la función G.
Densidad
Kernel
Selección del ancho
de banda
bw_diggle_26 <- bw.diggle(jam_26_ppp)
bw_diggle_27 <- bw.diggle(jam_27_ppp)
bw_scott_26 <- bw.scott(jam_26_ppp)
bw_scott_27 <- bw.scott(jam_27_ppp)
tabla_bandwidth <- data.frame(
dia = c("26 sept", "27 sept"),
bw_diggle_m = c(formatC(bw_diggle_26 * m_por_grado, format = "f", digits = 0),
formatC(bw_diggle_27 * m_por_grado, format = "f", digits = 0)),
bw_scott_x_m = c(formatC(bw_scott_26[1] * m_por_grado, format = "f", digits = 0),
formatC(bw_scott_27[1] * m_por_grado, format = "f", digits = 0)),
bw_scott_y_m = c(formatC(bw_scott_26[2] * m_por_grado, format = "f", digits = 0),
formatC(bw_scott_27[2] * m_por_grado, format = "f", digits = 0))
)
kable(tabla_bandwidth,
col.names = c("Día", "bw.diggle (m)", "bw.scott eje X (m)", "bw.scott eje Y (m)"),
caption = "Comparación de anchos de banda") %>%
kable_styling(bootstrap_options = c("striped", "hover"), font_size = 12)
Comparación de anchos de banda
|
Día
|
bw.diggle (m)
|
bw.scott eje X (m)
|
bw.scott eje Y (m)
|
|
26 sept
|
67
|
575
|
805
|
|
27 sept
|
37
|
555
|
751
|
res_bw_26_m <- formatC(bw_diggle_26 * m_por_grado, format = "f", digits = 0)
res_bw_27_m <- formatC(bw_diggle_27 * m_por_grado, format = "f", digits = 0)
res_bw_scott_26_m <- formatC(bw_scott_26[1] * m_por_grado, format = "f", digits = 0)
res_bw_diferencia_pct_26 <- formatC(100 * (bw_scott_26[1] - bw_diggle_26) / bw_diggle_26,
format = "f", digits = 0)
Interpretación: bw.scott da 575 m el
jueves, un 757% más ancho que 67 m de bw.diggle. Esa
diferencia es consistente con que bw.scott asume una
dispersión cercana a la normal bivariada, mientras
bw.diggle ajusta por validación cruzada y responde mejor a
un patrón agrupado sobre una vía angosta como este corredor, por lo que
se usa para los mapas de intensidad (67 m el jueves, 37 m el
viernes).
Mapas de
intensidad
# Definidas aquí porque a partir de este punto cada mapa de intensidad
# se necesita tanto en su propia figura como, más adelante, en la
# diferencia entre los dos días.
convertir_a_raster <- function(imagen) {
matriz <- as.matrix(imagen)
capa <- raster::raster(matriz)
raster::extent(capa) <- raster::extent(imagen$xrange[1], imagen$xrange[2],
imagen$yrange[1], imagen$yrange[2])
capa <- raster::flip(capa, direction = "y")
raster::crs(capa) <- "+proj=longlat +datum=WGS84"
capa
}
normalizar <- function(capa) {
(capa - raster::cellStats(capa, "min")) /
(raster::cellStats(capa, "max") - raster::cellStats(capa, "min"))
}
intensidad_26 <- density.ppp(jam_26_ppp, sigma = bw_diggle_26, edge = TRUE,
dimyx = c(128, 128))
raster_26 <- normalizar(convertir_a_raster(intensidad_26))
raster_26_vis <- enmascarar_bajo_umbral(raster_26)
tm_basemap(mapa_base) +
tm_shape(raster_26_vis) +
tm_raster(palette = c(paleta[5], paleta[1]), style = "cont", alpha = 0.85,
title = "Intensidad normalizada") +
tm_shape(jam_26_sf) +
tm_dots(col = "black", size = 0.03) +
tm_layout(title = "Densidad de congestión, 26 de septiembre")
Figura 8. Densidad kernel de eventos de congestión
sobre el mapa de Bogotá, jueves 26 de septiembre.
intensidad_27 <- density.ppp(jam_27_ppp, sigma = bw_diggle_27, edge = TRUE,
dimyx = c(128, 128))
raster_27 <- normalizar(convertir_a_raster(intensidad_27))
raster_27_vis <- enmascarar_bajo_umbral(raster_27)
tm_basemap(mapa_base) +
tm_shape(raster_27_vis) +
tm_raster(palette = c(paleta[5], paleta[2]), style = "cont", alpha = 0.85,
title = "Intensidad normalizada") +
tm_shape(jam_27_sf) +
tm_dots(col = "black", size = 0.03) +
tm_layout(title = "Densidad de congestión, 27 de septiembre")
Figura 9. Densidad kernel de eventos de congestión
sobre el mapa de Bogotá, viernes 27 de septiembre.
Interpretación: en las dos figuras la franja de
mayor intensidad corre sobre la vía que cruza el centro de Cajicá y
sigue hacia Calahorra, no se reparte por el resto del rectángulo de la
ventana. Eso es justamente lo que el ji-cuadrado y la función K ya
habían señalado con números: el problema no es la zona en general, es
ese tramo vial puntual, y por eso al enmascarar las celdas de baja
densidad el mapa base queda visible en casi toda la ventana salvo
ahí.
Nota de verificación: la orientación del raster depende de cómo
spatstat almacena la matriz de intensidad. Si al ver la
Figura 8 el área de mayor intensidad no coincide con la nube de puntos
negros, retirar la línea flip() dentro de
convertir_a_raster() en lugar de aplicarla.
Comparación
26-27
Normalización y
diferencia
Cada mapa de intensidad ya se normalizó entre 0 y 1 de forma
independiente al construir las Figuras 8 y 9, para que la comparación no
se vea afectada por tener 76 eventos un día y 121 el otro.
Diferencia celda a
celda
diferencia_raster <- raster_27 - raster_26
res_max_expansion <- formatC(raster::cellStats(diferencia_raster, "max"), format = "f", digits = 2)
res_max_contraccion <- formatC(raster::cellStats(diferencia_raster, "min"), format = "f", digits = 2)
# Para el mapa, se oculta el cambio casi nulo (|diferencia| < 0.05) y se
# deja ver solo dónde la congestión relativa subió o bajó de forma
# apreciable entre los dos días.
diferencia_raster_vis <- diferencia_raster
diferencia_raster_vis[abs(diferencia_raster) < 0.05] <- NA
El cambio celda a celda va de -0.92 (mayor contracción relativa) a
0.66 (mayor expansión relativa) en la escala normalizada.
Interpretación: los valores positivos marcan tramos
donde la congestión relativa creció el viernes frente al jueves, y los
negativos donde bajó. Los puntos que aparecen en ambos días bajo una y
otra intensidad, sin depender del cambio de calendario, son los que más
sugieren un cuello de botella estructural; los que solo aparecen el
viernes están más ligados al efecto de fin de semana que al diseño de la
vía.
Mapas finales
tm_basemap(mapa_base) +
tm_shape(raster_26_vis) +
tm_raster(palette = c(paleta[5], paleta[1]), style = "cont", alpha = 0.85,
title = "Intensidad normalizada") +
tm_layout(title = "Congestión, jueves 26")
Figura 10. Mapa 1, intensidad normalizada de
congestión sobre el mapa de Bogotá, jueves 26 de septiembre.
tm_basemap(mapa_base) +
tm_shape(raster_27_vis) +
tm_raster(palette = c(paleta[5], paleta[2]), style = "cont", alpha = 0.85,
title = "Intensidad normalizada") +
tm_layout(title = "Congestión, viernes 27")
Figura 11. Mapa 2, intensidad normalizada de
congestión sobre el mapa de Bogotá, viernes 27 de septiembre.
tm_basemap(mapa_base) +
tm_shape(diferencia_raster_vis) +
tm_raster(palette = c(paleta[1], "white", paleta[2]), style = "cont",
midpoint = 0, alpha = 0.85, title = "Cambio relativo") +
tm_layout(title = "Diferencia 27 menos 26 de septiembre")
Figura 12. Mapa 3, diferencia celda a celda sobre el
mapa de Bogotá, entre el viernes y el jueves.
Otros
Eventos
Cierres de vía, peligros y accidentes se revisan de forma
exploratoria. Con cierres de vía y accidentes el número de eventos
únicos por día es tan bajo que solo se ubican en el mapa, sin prueba
estadística.
construir_ppp_tipo <- function(tipo, dia) {
sub <- eventos_unicos %>% filter(type == tipo, fecha == as.Date(dia))
if (nrow(sub) == 0) return(NULL)
unique(ppp(x = sub$longitud, y = sub$latitud, window = ventana))
}
hazard_26 <- construir_ppp_tipo("HAZARD", "2024-09-26")
hazard_27 <- construir_ppp_tipo("HAZARD", "2024-09-27")
cerrado_26 <- construir_ppp_tipo("ROAD_CLOSED", "2024-09-26")
cerrado_27 <- construir_ppp_tipo("ROAD_CLOSED", "2024-09-27")
accidente_26 <- construir_ppp_tipo("ACCIDENT", "2024-09-26")
accidente_27 <- construir_ppp_tipo("ACCIDENT", "2024-09-27")
res_n_hazard_26 <- npoints(hazard_26)
res_n_hazard_27 <- npoints(hazard_27)
res_n_cerrado_26 <- npoints(cerrado_26)
res_n_cerrado_27 <- npoints(cerrado_27)
res_n_accidente_26 <- npoints(accidente_26)
res_n_accidente_27 <- npoints(accidente_27)
Peligros en vía pasaron de 15 eventos el jueves a 28 el viernes, una
muestra suficiente para una prueba CSR orientativa. Cierres de vía (3 y
3) y accidentes (4 y 6) son demasiado escasos para cualquier prueba
formal.
prueba_hazard_26 <- quadrat.test(hazard_26, nx = 2, ny = 2)
prueba_hazard_27 <- quadrat.test(hazard_27, nx = 2, ny = 2)
res_p_hazard_26 <- formatC(prueba_hazard_26$p.value, format = "f", digits = 4)
res_p_hazard_27 <- formatC(prueba_hazard_27$p.value, format = "f", digits = 4)
El valor p de la prueba ji-cuadrado para peligros en vía es 0.0034 el
jueves y 0.0000 el viernes.
Interpretación: con muestras de 15 y 28 eventos,
esta prueba es orientativa. Sirve para no descartar la categoría de
entrada, no para sostener una conclusión estadística firme sobre
peligros en vía.
construir_sf_etiquetado <- function(objeto_ppp, etiqueta) {
if (is.null(objeto_ppp) || npoints(objeto_ppp) == 0) return(NULL)
sf_obj <- ppp_a_sf(objeto_ppp)
sf_obj$evento <- etiqueta
sf_obj
}
otros_eventos_lista <- list(
construir_sf_etiquetado(cerrado_26, "Cierre de vía, 26 sept"),
construir_sf_etiquetado(cerrado_27, "Cierre de vía, 27 sept"),
construir_sf_etiquetado(accidente_26, "Accidente, 26 sept"),
construir_sf_etiquetado(accidente_27, "Accidente, 27 sept")
)
otros_eventos_lista <- Filter(Negate(is.null), otros_eventos_lista)
otros_eventos_sf <- do.call(rbind, otros_eventos_lista)
# El modo "view" de tmap dibuja siempre marcadores circulares y no
# soporta combinar color y forma en una sola capa de puntos; por eso
# tipo y día se combinan en una sola categoría de color en vez de
# usar dos estéticas (color y forma) al mismo tiempo.
tm_basemap(mapa_base) +
tm_shape(otros_eventos_sf) +
tm_dots(col = "evento",
palette = c("Cierre de vía, 26 sept" = paleta[3],
"Cierre de vía, 27 sept" = paleta[4],
"Accidente, 26 sept" = paleta[1],
"Accidente, 27 sept" = paleta[2]),
size = 0.08, title = "Evento y día") +
tm_layout(title = "Cierres de vía y accidentes, 26-27 de septiembre")
Figura 13. Cierres de vía y accidentes sobre el mapa
de Bogotá, jueves y viernes, sin prueba estadística por tamaño de
muestra.
LS0tCnRpdGxlOiAiKipNMlUyIC0gQ2FzbzogQW7DoWxpc2lzIGRlIFBhdHJvbmVzIFB1bnR1YWxlcyBkZSBFdmVudG9zIGRlIE1vdmlsaWRhZCBXYXplKioiCmF1dGhvcjogIlJhZmFlbCBKb3NlIERlbCBDYXN0aWxsbyBQYXZhamVhdSIKZGF0ZTogIjIwMjYtMDYtMDgiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBmYWxzZQogICAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRoZW1lOiBjb3NtbwogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKcGFyYW1zOgogIHNlZWQ6IDg5NzI3OTEKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUKLS0tCgo8c3R5bGU+CmJvZHksIGgxLCBoMiwgaDMsIGg0LCBwLCBsaSwgdGQsIHRoLCAudG9jLWNvbnRlbnQgewogIGZvbnQtZmFtaWx5OiAnQXB0b3MgRGlzcGxheScsICdDYWxpYnJpJywgJ1NlZ29lIFVJJywgc2Fucy1zZXJpZjsKfQpoMSBzdHJvbmcsIGgxIGIgewogIGZvbnQtd2VpZ2h0OiA3MDA7Cn0KPC9zdHlsZT4KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICJjZW50ZXIiKQoKIyBJbnN0YWxhY2nDs24geSBjYXJnYSBkZSBsaWJyZXLDrWFzLiBTZSB1c2EgcmVwb3MgZXhwbMOtY2l0byBwYXJhIGV2aXRhcgojIGVsIGVycm9yIGRlIHNlbGVjY2nDs24gZGUgZXNwZWpvIGVuIGVudG9ybm9zIG5vIGludGVyYWN0aXZvcy4KcGFxdWV0ZXMgPC0gYygicmVhZHhsIiwgImRwbHlyIiwgImx1YnJpZGF0ZSIsICJzcGF0c3RhdCIsICJzZiIsCiAgICAgICAgICAgICAgInJhc3RlciIsICJ0bWFwIiwgImxlYWZsZXQiLCAiZ2dwbG90MiIsICJrbml0ciIsICJrYWJsZUV4dHJhIikKCmZvciAocGtnIGluIHBhcXVldGVzKSB7CiAgaWYgKCFyZXF1aXJlKHBrZywgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcyhwa2csIHJlcG9zID0gImh0dHBzOi8vY2xvdWQuci1wcm9qZWN0Lm9yZyIpCiAgICBsaWJyYXJ5KHBrZywgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQogIH0KfQoKc2V0LnNlZWQocGFyYW1zJHNlZWQpCgojIE1vZG8gInZpZXciIGRlIHRtYXA6IGxhcyBncsOhZmljYXMgZ2VvZXNwYWNpYWxlcyBzZSByZW5kZXJpemFuIGNvbW8KIyBtYXBhcyBkZSBMZWFmbGV0IGludGVyYWN0aXZvcyBjb24gdW4gbWFwYSBiYXNlIHJlYWwgZGUgZm9uZG8uIExhcwojIHRlc2VsYXMgZGVsIG1hcGEgc2UgZGVzY2FyZ2FuIGFsIGFicmlyIGVsIEhUTUwgZW4gZWwgbmF2ZWdhZG9yLCBubwojIGFsIHRlamVyIGVsIGRvY3VtZW50bywgcG9yIGxvIHF1ZSBubyBzZSBuZWNlc2l0YSBpbnRlcm5ldCBlbiBSU3R1ZGlvCiMgZW4gZWwgbW9tZW50byBkZSBoYWNlciBrbml0Lgp0bWFwX21vZGUoInZpZXciKQptYXBhX2Jhc2UgPC0gIk9wZW5TdHJlZXRNYXAiCgojIENvbnZpZXJ0ZSB1biBvYmpldG8gcHBwIGRlIHNwYXRzdGF0IGEgcHVudG9zIHNmIGVuIFdHUzg0LCBsaXN0b3MKIyBwYXJhIGRpYnVqYXJzZSBzb2JyZSB1biBtYXBhIGJhc2UgY29uIHRtYXAuCnBwcF9hX3NmIDwtIGZ1bmN0aW9uKG9iamV0b19wcHApIHsKICBzZjo6c3RfYXNfc2YoZGF0YS5mcmFtZShsb24gPSBvYmpldG9fcHBwJHgsIGxhdCA9IG9iamV0b19wcHAkeSksCiAgICAgICAgICAgICAgIGNvb3JkcyA9IGMoImxvbiIsICJsYXQiKSwgY3JzID0gNDMyNikKfQoKIyBMYSB2ZW50YW5hIGRlIGVzdHVkaW8gZXMgdW4gcmVjdMOhbmd1bG8sIHBlcm8gbG9zIGV2ZW50b3Mgc2UgY29uY2VudHJhbgojIGVuIHVuYSBmcmFuamEgYW5nb3N0YSBzb2JyZSBsYSB2w61hLiBTaW4gZW5tYXNjYXJhciwgZWwgcmFzdGVyIGRlCiMgZGVuc2lkYWQgcGludGEgY2FzaSB0b2RvIGVsIHJlY3TDoW5ndWxvIGRlbCBtaXNtbyBjb2xvciB5IGVsIG1hcGEgZGUKIyBjYWxvciByZWFsIHF1ZWRhIGludmlzaWJsZS4gRXN0YSBmdW5jacOzbiB2dWVsdmUgTkEgKHRyYW5zcGFyZW50ZSkgbGFzCiMgY2VsZGFzIHBvciBkZWJham8gZGVsIHVtYnJhbCwgcGFyYSBxdWUgc29sbyBzZSB2ZWEgY29sb3IgZG9uZGUgaGF5CiMgc2XDsWFsIHJlYWwgeSBlbCBtYXBhIGJhc2Ugc2Ugbm90ZSBkZWJham8gZW4gZWwgcmVzdG8uCmVubWFzY2FyYXJfYmFqb191bWJyYWwgPC0gZnVuY3Rpb24oY2FwYSwgdW1icmFsID0gMC4wNSkgewogIGNhcGFbY2FwYSA8IHVtYnJhbF0gPC0gTkEKICBjYXBhCn0KCiMgUGFsZXRhIGhvbW9nw6luZWEgcGFyYSB0b2RvIGVsIGRvY3VtZW50bwpwYWxldGEgPC0gYygiIzJDN0JCNiIsICIjRDcxOTFDIiwgIiMxQTk2NDEiLCAiI0ZEQUU2MSIsICIjQUJEOUU5IikKCiMgRmFjdG9yIGRlIGNvbnZlcnNpw7NuIGdyYWRvLW1ldHJvIHVzYWRvIGVuIHRvZG8gZWwgZG9jdW1lbnRvLiBFcyB1bmEKIyBhcHJveGltYWNpw7NuIChpZ25vcmEgbGEgY29tcHJlc2nDs24gZGUgbGEgbG9uZ2l0dWQgcG9yIGxhIGxhdGl0dWQpLAojIHN1ZmljaWVudGUgcGFyYSBoYWJsYXIgZGUgZXNjYWxhcyBkZSBjaWVudG9zIGRlIG1ldHJvcywgbm8gcGFyYQojIG1lZGljaW9uZXMgZGUgcHJlY2lzacOzbiBjYXRhc3RyYWwuCm1fcG9yX2dyYWRvIDwtIDExMTAwMApgYGAKCiMgKipJbnRyb2R1Y2Npw7NuKioKCkVsIHRyYW1vIGRlIGxhIEF1dG9waXN0YSBOb3J0ZSBlbnRyZSBCb2dvdMOhLCBDYWppY8OhIHkgVG9jYW5jaXDDoSBhcGFyZWNlIHJlcGV0aWRvIGVuIGxvcyBub21icmVzIGRlIGNhbGxlIGRlIGxhIHRyYW1hIFdhemUgKGBzdHJlZXRgKSB1c2FkYSBlbiBlc3RlIHRyYWJham8uIEVzdGUgZG9jdW1lbnRvIHJldmlzYSBzaSBsb3MgcmVwb3J0ZXMgZGUgY29uZ2VzdGnDs24gZW4gZXNlIGNvcnJlZG9yIHNlIGNvbmNlbnRyYW4gZW4gdHJhbW9zIGZpam9zIG8gc2UgcmVwYXJ0ZW4gYWwgYXphciwgY29tcGFyYW5kbyBlbCBqdWV2ZXMgMjYgeSBlbCB2aWVybmVzIDI3IGRlIHNlcHRpZW1icmUgZGUgMjAyNCBjb24gdMOpY25pY2FzIGRlIGFuw6FsaXNpcyBkZSBwYXRyb25lcyBwdW50dWFsZXMgZW4gUi4KCiMjIE9iamV0aXZvCgpFc3RhYmxlY2VyIHNpIGxhIGNvbmdlc3Rpw7NuIHZlaGljdWxhciBkZWwgY29ycmVkb3Igc2UgY29uY2VudHJhIGVuIHRyYW1vcyBmaWpvcywgc2kgZXNhIGNvbmNlbnRyYWNpw7NuIGNhbWJpYSBlbnRyZSBlbCBqdWV2ZXMgMjYgeSBlbCB2aWVybmVzIDI3IGRlIHNlcHRpZW1icmUgZGUgMjAyNCwgeSBxdcOpIHRyYW1vcyBkZWJlcsOtYW4gcHJpb3JpemFyc2UgZW4gbGEgZ2VzdGnDs24gZGVsIHRyw6FmaWNvLgoKIyMgRnVlbnRlIGRlIGxvcyBkYXRvcwoKTGEgdHJhbWEgY29ycmVzcG9uZGUgYSBldmVudG9zIFdhemUgcGFyYSBDb2xvbWJpYSwgcmVjb3J0YWRhIGFsIHRyYW1vIGRlIGxhIEF1dG9waXN0YSBOb3J0ZSBlbnRyZSBCb2dvdMOhLCBDYWppY8OhIHkgVG9jYW5jaXDDoSwgZHVyYW50ZSBlbCAyNiB5IDI3IGRlIHNlcHRpZW1icmUgZGUgMjAyNC4KCiMgKipEYXRvcyB5IExpbXBpZXphKioKCiMjIENhcmdhIGRlbCBhcmNoaXZvCgpFbCBhcmNoaXZvIHRyYWUgMTcgY29sdW1uYXMuIFBhcmEgcXVlIG5pbmd1bmEgY2VsZGEgY29uIGZvcm1hdG8gZGUgZmVjaGEgY29ycm9tcGlkbyBzZSBwaWVyZGEgYWwgY2FyZ2FyLCBzZSBmdWVyemEgZWwgdGlwbyBgbnVtZXJpY2AgZW4gbGFzIGNvbHVtbmFzIGRlIGNvb3JkZW5hZGFzOiBhc8OtLCBjdWFscXVpZXIgY2VsZGEgbWFsIGZvcm1hdGVhZGEgY29tbyBmZWNoYSBsbGVnYSBhIFIgY29tbyBzdSBuw7ptZXJvIGRlIHNlcmllIG9yaWdpbmFsIGVuIHZleiBkZSBjb21vIHRleHRvIGluc2VydmlibGUuCgpgYGB7ciBjYXJnYXItZGF0b3N9CiMgQnVzY2EgZWwgYXJjaGl2byBlbiB2YXJpYXMgcnV0YXMgcHJvYmFibGVzIGVuIGx1Z2FyIGRlIGFzdW1pciB1bmEgc29sYS4KIyBTaSBuaW5ndW5hIGV4aXN0ZSwgZWwgZXJyb3IgaW5kaWNhIGVsIGRpcmVjdG9yaW8gZGUgdHJhYmFqbyBhY3R1YWwgeQojIHRvZGFzIGxhcyBydXRhcyByZXZpc2FkYXMsIHBhcmEgbG9jYWxpemFyIGVsIGFyY2hpdm8gc2luIGFkaXZpbmFyLgpydXRhX2RvY3VtZW50b19hY3R1YWwgPC0gdHJ5Q2F0Y2goewogIGlmIChyZXF1aXJlTmFtZXNwYWNlKCJyc3R1ZGlvYXBpIiwgcXVpZXRseSA9IFRSVUUpICYmIHJzdHVkaW9hcGk6OmlzQXZhaWxhYmxlKCkpIHsKICAgIGRpcm5hbWUocnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGgpCiAgfSBlbHNlIHsKICAgIE5BX2NoYXJhY3Rlcl8KICB9Cn0sIGVycm9yID0gZnVuY3Rpb24oZSkgTkFfY2hhcmFjdGVyXykKCmJ1c2Nhcl9hcmNoaXZvX2RhdG9zIDwtIGZ1bmN0aW9uKG5vbWJyZV9hcmNoaXZvID0gIlRyYW1hX1dhemUueGxzeCIpIHsKICBjYW5kaWRhdG9zIDwtIHVuaXF1ZShjKAogICAgbm9tYnJlX2FyY2hpdm8sCiAgICBmaWxlLnBhdGgoZ2V0d2QoKSwgbm9tYnJlX2FyY2hpdm8pLAogICAgaWYgKCFpcy5uYShydXRhX2RvY3VtZW50b19hY3R1YWwpKSBmaWxlLnBhdGgocnV0YV9kb2N1bWVudG9fYWN0dWFsLCBub21icmVfYXJjaGl2byksCiAgICAiQzovUG9udGlmaWNpYV9Vbl9KYXZlcmlhbmEvQW5hbGlzaXNfR2VvZXNwYWNpYWwvTTAyX1UwMl9QYXRyb25lcy9UcmFtYV9XYXplLnhsc3giLAogICAgZmlsZS5wYXRoKFN5cy5nZXRlbnYoIlVTRVJQUk9GSUxFIiksICJEb3dubG9hZHMiLCBub21icmVfYXJjaGl2byksCiAgICBmaWxlLnBhdGgoU3lzLmdldGVudigiVVNFUlBST0ZJTEUiKSwgIkRvY3VtZW50cyIsIG5vbWJyZV9hcmNoaXZvKSwKICAgIGZpbGUucGF0aChTeXMuZ2V0ZW52KCJVU0VSUFJPRklMRSIpLCAiRGVza3RvcCIsIG5vbWJyZV9hcmNoaXZvKQogICkpCiAgY2FuZGlkYXRvcyA8LSBjYW5kaWRhdG9zW256Y2hhcihjYW5kaWRhdG9zKV0KICBlbmNvbnRyYWRvcyA8LSBjYW5kaWRhdG9zW2ZpbGUuZXhpc3RzKGNhbmRpZGF0b3MpXQoKICBpZiAobGVuZ3RoKGVuY29udHJhZG9zKSA9PSAwKSB7CiAgICBzdG9wKAogICAgICAiXG5ObyBzZSBlbmNvbnRybyAnIiwgbm9tYnJlX2FyY2hpdm8sICInIGVuIG5pbmd1bmEgcnV0YSBjYW5kaWRhdGEuXG4iLAogICAgICAiRGlyZWN0b3JpbyBkZSB0cmFiYWpvIGFjdHVhbCAoZ2V0d2QoKSk6ICIsIGdldHdkKCksICJcbiIsCiAgICAgICJSdXRhcyByZXZpc2FkYXM6XG4gIC0gIiwgcGFzdGUoY2FuZGlkYXRvcywgY29sbGFwc2UgPSAiXG4gIC0gIiksICJcbiIsCiAgICAgICJTb2x1Y2lvbjogY29waWEgJyIsIG5vbWJyZV9hcmNoaXZvLCAiJyBhIGxhIGNhcnBldGEgZGVsIC5SbWQgIiwKICAgICAgIihsYSBtYXMgc2ltcGxlKSwgbyBhZ3JlZ2Egc3UgcnV0YSByZWFsIGRlbnRybyBkZWwgdmVjdG9yICdjYW5kaWRhdG9zJyAiLAogICAgICAiZW4gZXN0ZSBjaHVuay4iCiAgICApCiAgfQoKICBtZXNzYWdlKCJBcmNoaXZvIGRlIGRhdG9zIGVuY29udHJhZG8gZW46ICIsIGVuY29udHJhZG9zWzFdKQogIGVuY29udHJhZG9zWzFdCn0KCnJ1dGFfZXhjZWwgPC0gYnVzY2FyX2FyY2hpdm9fZGF0b3MoIlRyYW1hX1dhemUueGxzeCIpCgp0aXBvc19jb2x1bW5hcyA8LSBjKCJudW1lcmljIiwgIm51bWVyaWMiLCAidGV4dCIsICJudW1lcmljIiwgImxvZ2ljYWwiLAogICAgICAgICAgICAgICAgICAgICAibnVtZXJpYyIsICJudW1lcmljIiwgInRleHQiLCAidGV4dCIsICJudW1lcmljIiwKICAgICAgICAgICAgICAgICAgICAgIm51bWVyaWMiLCAidGV4dCIsICJ0ZXh0IiwgIm51bWVyaWMiLCAibnVtZXJpYyIsCiAgICAgICAgICAgICAgICAgICAgICJudW1lcmljIiwgInRleHQiKQoKZGF0b3Nfd2F6ZSA8LSByZWFkX2V4Y2VsKHJ1dGFfZXhjZWwsIHNoZWV0ID0gIkhvamEgMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sX3R5cGVzID0gdGlwb3NfY29sdW1uYXMpCgprYWJsZShoZWFkKGRhdG9zX3dhemUsIDUpLCBjYXB0aW9uID0gIlByaW1lcm9zIHJlZ2lzdHJvcyBkZSBsYSB0cmFtYSBXYXplIikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZm9udF9zaXplID0gMTIpICU+JQogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpCmBgYAoKIyMgQ29ycmVjY2nDs24gZGUgY29vcmRlbmFkYXMKCmBsb2NhdGlvbl94YCB5IGBsb2NhdGlvbl95YCBlc3TDoW4gbXVsdGlwbGljYWRhcyBwb3IgMTBeNiwgcGVybyB1bmEgcGFydGUgZGUgbG9zIHJlZ2lzdHJvcyBwZXJkacOzIHVubyBvIGRvcyBkw61naXRvcyBlbiBsYSBleHBvcnRhY2nDs24sIGxvIHF1ZSBkZXNwbGF6YSBsYSBjb29yZGVuYWRhIHVuIG9yZGVuIGRlIG1hZ25pdHVkLiBMYSBmdW5jacOzbiBzaWd1aWVudGUgcHJ1ZWJhIHBvdGVuY2lhcyBkZSBkaWV6IGhhc3RhIHViaWNhciBlbCB2YWxvciBlbiBlbCByYW5nbyBnZW9ncsOhZmljbyBwbGF1c2libGUgZGVsIGNvcnJlZG9yIChsb25naXR1ZCBlbnRyZSA3My45wrAgeSA3NC4xwrAgb2VzdGUsIGxhdGl0dWQgZW50cmUgNC4wwrAgeSA1LjLCsCBub3J0ZSksIGNvbnNlcnZhbmRvIGVsIHNpZ25vIG9yaWdpbmFsLgoKYGBge3IgY29ycmVnaXItY29vcmRlbmFkYXN9CmNvcnJlZ2lyX21hZ25pdHVkIDwtIGZ1bmN0aW9uKHZhbG9yLCBtaW5pbW8sIG1heGltbykgewogIGJhc2UgPC0gYWJzKHZhbG9yKSAvIDFlNgogIHNpZ25vIDwtIHNpZ24odmFsb3IpCiAgcmVzdWx0YWRvIDwtIHJlcChOQV9yZWFsXywgbGVuZ3RoKHZhbG9yKSkKICBmb3IgKHBvdGVuY2lhIGluIDA6MykgewogICAgY2FuZGlkYXRvIDwtIGJhc2UgKiAxMF5wb3RlbmNpYQogICAgcGVuZGllbnRlIDwtIGlzLm5hKHJlc3VsdGFkbykgJiBjYW5kaWRhdG8gPj0gbWluaW1vICYgY2FuZGlkYXRvIDw9IG1heGltbwogICAgcmVzdWx0YWRvW3BlbmRpZW50ZV0gPC0gc2lnbm9bcGVuZGllbnRlXSAqIGNhbmRpZGF0b1twZW5kaWVudGVdCiAgfQogIHJlc3VsdGFkbwp9CgpkYXRvc193YXplIDwtIGRhdG9zX3dhemUgJT4lCiAgbXV0YXRlKAogICAgbG9uZ2l0dWQgPSBjb3JyZWdpcl9tYWduaXR1ZChsb2NhdGlvbl94LCA3My45LCA3NC4xKSwKICAgIGxhdGl0dWQgID0gY29ycmVnaXJfbWFnbml0dWQobG9jYXRpb25feSwgNC4wLCA1LjIpCiAgKQoKcmVzX3RvdGFsX2ZpbGFzICAgICA8LSBucm93KGRhdG9zX3dhemUpCnJlc19maWxhc19jb3JydXB0YXMgPC0gc3VtKGlzLm5hKGRhdG9zX3dhemUkbG9uZ2l0dWQpIHwgaXMubmEoZGF0b3Nfd2F6ZSRsYXRpdHVkKSkKcmVzX3BjdF9yZWN1cGVyYWRvICA8LSBmb3JtYXRDKDEwMCAqICgxIC0gcmVzX2ZpbGFzX2NvcnJ1cHRhcyAvIHJlc190b3RhbF9maWxhcyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAxKQpgYGAKCkRlIGByIHJlc190b3RhbF9maWxhc2AgcmVnaXN0cm9zLCBgciByZXNfZmlsYXNfY29ycnVwdGFzYCBubyB0ZW7DrWFuIHVuIHZhbG9yIG51bcOpcmljbyByZWN1cGVyYWJsZSBkZXRyw6FzIGRlIGxhIGNlbGRhIGNvcnJvbXBpZGEgeSBxdWVkYXJvbiBmdWVyYS4gRWwgcmVzdG8sIGByIHJlc19wY3RfcmVjdXBlcmFkb2AlLCBzZSByZXViaWPDsyBkZW50cm8gZGVsIHJhbmdvIGVzcGVyYWRvIHNpbiBuZWNlc2lkYWQgZGUgZGVzY2FydGFybG8uCgoqKkludGVycHJldGFjacOzbjoqKiBwZXJkZXIgdW4gZMOtZ2l0byBlbiBsYSBleHBvcnRhY2nDs24gZXMgdW4gcHJvYmxlbWEgc2lzdGVtw6F0aWNvIGRlbCBhcmNoaXZvLCBubyBlcnJvcmVzIHN1ZWx0b3MuIENvcnJlZ2lyIHBvciBtYWduaXR1ZCBlbiB2ZXogZGUgZWxpbWluYXIgbGEgZmlsYSBldml0YSBwZXJkZXIgaW5mb3JtYWNpw7NuIHNvYnJlIHRyYW1vcyBxdWUgc29sbyBhcGFyZWNlbiBlbiBlc29zIHJlZ2lzdHJvcy4KCiMjIFZlbnRhbmEgeSBldmVudG9zIMO6bmljb3MKCkxhIHZlbnRhbmEgZGUgZXN0dWRpbyAoYHZlbnRhbmFgKSBjdWJyZSBlbCB0cmFtbyBlbnRyZSBsYXMgY29vcmRlbmFkYXMgLTc0LjA0MzMxwrAgeSAtNzMuOTkyOcKwIGRlIGxvbmdpdHVkIHkgNC44ODU3MzbCsCBhIDQuOTQ4NTYywrAgZGUgbGF0aXR1ZCwgbGEgZnJhbmphIGRvbmRlIGFwYXJlY2VuIGxvcyBub21icmVzIGRlIGNhbGxlIGRlIENhamljw6EgeSBUb2NhbmNpcMOhIGVuIGxhIHRyYW1hLiBDYWRhIGV2ZW50byBzZSByZXBpdGUgbWllbnRyYXMgZXN0w6EgYWN0aXZvLCBhc8OtIHF1ZSBzZSBjb25zZXJ2YSB1biBzb2xvIHB1bnRvIHBvciBldmVudG8gKGB1dWlkYCksIHRvbWFuZG8gc3UgcHJpbWVyIHJlcG9ydGUgZGUgY2FkYSBkw61hLgoKYGBge3IgZmVjaGFzLWRlZHVwfQp2ZW50YW5hIDwtIG93aW4oeHJhbmdlID0gYygtNzQuMDQzMzEsIC03My45OTI5KSwKICAgICAgICAgICAgICAgICB5cmFuZ2UgPSBjKDQuODg1NzM2LCA0Ljk0ODU2MikpCgpkYXRvc193YXplIDwtIGRhdG9zX3dhemUgJT4lCiAgbXV0YXRlKGZlY2hhX2hvcmEgPSB5bWRfaG1zKGNyZWF0aW9uX0RhdGUsIHF1aWV0ID0gVFJVRSksCiAgICAgICAgIGZlY2hhID0gYXMuRGF0ZShmZWNoYV9ob3JhKSkKCmRhdG9zX3ZhbGlkb3MgPC0gZGF0b3Nfd2F6ZSAlPiUKICBmaWx0ZXIoIWlzLm5hKGxvbmdpdHVkKSwgIWlzLm5hKGxhdGl0dWQpLAogICAgICAgICBsb25naXR1ZCA+PSB2ZW50YW5hJHhyYW5nZVsxXSwgbG9uZ2l0dWQgPD0gdmVudGFuYSR4cmFuZ2VbMl0sCiAgICAgICAgIGxhdGl0dWQgID49IHZlbnRhbmEkeXJhbmdlWzFdLCBsYXRpdHVkICA8PSB2ZW50YW5hJHlyYW5nZVsyXSkKCmV2ZW50b3NfdW5pY29zIDwtIGRhdG9zX3ZhbGlkb3MgJT4lCiAgZmlsdGVyKGZlY2hhICVpbiUgYXMuRGF0ZShjKCIyMDI0LTA5LTI2IiwgIjIwMjQtMDktMjciKSkpICU+JQogIGFycmFuZ2UoZmVjaGFfaG9yYSkgJT4lCiAgZGlzdGluY3QodXVpZCwgZmVjaGEsIC5rZWVwX2FsbCA9IFRSVUUpCgp0YWJsYV9yZXN1bWVuIDwtIGV2ZW50b3NfdW5pY29zICU+JQogIGNvdW50KHR5cGUsIGZlY2hhLCBuYW1lID0gImV2ZW50b3NfdW5pY29zIikgJT4lCiAgYXJyYW5nZSh0eXBlLCBmZWNoYSkKCmthYmxlKHRhYmxhX3Jlc3VtZW4sIGNhcHRpb24gPSAiRXZlbnRvcyDDum5pY29zIHBvciB0aXBvIHkgZMOtYSIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZvbnRfc2l6ZSA9IDEyKQoKcmVzX3BjdF9qYW1fdG90YWwgPC0gZm9ybWF0QygxMDAgKiBzdW0oZXZlbnRvc191bmljb3MkdHlwZSA9PSAiSkFNIikgLyBucm93KGV2ZW50b3NfdW5pY29zKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAwKQpgYGAKCioqSW50ZXJwcmV0YWNpw7NuOioqIGxhIGNvbmdlc3Rpw7NuIChgSkFNYCkgZXMgZWwgYHIgcmVzX3BjdF9qYW1fdG90YWxgJSBkZSBsb3MgYHIgbnJvdyhldmVudG9zX3VuaWNvcylgIGV2ZW50b3Mgw7puaWNvcyBkZSBsb3MgZG9zIGTDrWFzLCBtw6FzIHF1ZSBsYXMgb3RyYXMgdHJlcyBjYXRlZ29yw61hcyBqdW50YXMuIENpZXJyZXMgZGUgdsOtYSB5IGFjY2lkZW50ZXMgcXVlZGFuIGNvbiB0YW4gcG9jb3MgcHVudG9zIHBvciBkw61hIHF1ZSBjdWFscXVpZXIgcHJ1ZWJhIGVzdGFkw61zdGljYSBzb2JyZSBlbGxvcyBkZWJlIGxlZXJzZSBjb21vIGV4cGxvcmF0b3JpYS4KCiMgKipQYXRyw7NuIGRlIENvbmdlc3Rpw7NuKioKCkxhIGNvbmdlc3Rpw7NuIChgSkFNYCkgcmVjaWJlIGVsIGFuw6FsaXNpcyBjb21wbGV0byBwb3JxdWUgZXMgZWwgYHIgcmVzX3BjdF9qYW1fdG90YWxgJSBkZSBsb3MgZXZlbnRvcyDDum5pY29zIHJlZ2lzdHJhZG9zLCBsYSDDum5pY2EgY2F0ZWdvcsOtYSBjb24gc3VmaWNpZW50ZXMgcHVudG9zIHBhcmEgc29zdGVuZXIgcHJ1ZWJhcyBkZSBhbGVhdG9yaWVkYWQgZXNwYWNpYWwgY29uIGFsZ28gZGUgcG90ZW5jaWEgZXN0YWTDrXN0aWNhLiBMYXMgb3RyYXMgdHJlcyBzZSByZXZpc2FuIG3DoXMgYWRlbGFudGUgc2luIGxhIG1pc21hIHByb2Z1bmRpZGFkLgoKIyMgQ29uc3RydWNjacOzbiBkZWwgcGF0csOzbiBkZSBwdW50b3MKCmBgYHtyIGNvbnN0cnVpci1wcHB9CmphbV8yNiA8LSBldmVudG9zX3VuaWNvcyAlPiUgZmlsdGVyKHR5cGUgPT0gIkpBTSIsIGZlY2hhID09IGFzLkRhdGUoIjIwMjQtMDktMjYiKSkKamFtXzI3IDwtIGV2ZW50b3NfdW5pY29zICU+JSBmaWx0ZXIodHlwZSA9PSAiSkFNIiwgZmVjaGEgPT0gYXMuRGF0ZSgiMjAyNC0wOS0yNyIpKQoKamFtXzI2X3BwcCA8LSB1bmlxdWUocHBwKHggPSBqYW1fMjYkbG9uZ2l0dWQsIHkgPSBqYW1fMjYkbGF0aXR1ZCwgd2luZG93ID0gdmVudGFuYSkpCmphbV8yN19wcHAgPC0gdW5pcXVlKHBwcCh4ID0gamFtXzI3JGxvbmdpdHVkLCB5ID0gamFtXzI3JGxhdGl0dWQsIHdpbmRvdyA9IHZlbnRhbmEpKQoKamFtXzI2X3NmIDwtIHBwcF9hX3NmKGphbV8yNl9wcHApCmphbV8yN19zZiA8LSBwcHBfYV9zZihqYW1fMjdfcHBwKQoKcmVzX25famFtXzI2IDwtIG5wb2ludHMoamFtXzI2X3BwcCkKcmVzX25famFtXzI3IDwtIG5wb2ludHMoamFtXzI3X3BwcCkKcmVzX2luY3JlbWVudG9famFtIDwtIGZvcm1hdEMoMTAwICogKHJlc19uX2phbV8yNyAvIHJlc19uX2phbV8yNiAtIDEpLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDApCmBgYAoKRWwganVldmVzIHNlIHJlZ2lzdHJhcm9uIGByIHJlc19uX2phbV8yNmAgZXZlbnRvcyDDum5pY29zIGRlIGNvbmdlc3Rpw7NuIHkgZWwgdmllcm5lcyBgciByZXNfbl9qYW1fMjdgLCB1biBgciByZXNfaW5jcmVtZW50b19qYW1gJSBtw6FzLiBMYSBzZWNjacOzbiBkZSBwZXJmaWwgaG9yYXJpbyByZXZpc2Egc2kgZXNlIGF1bWVudG8gc2UgY29uY2VudHJhIGVuIGFsZ3VuYSBmcmFuamEgcGFydGljdWxhciBkZWwgZMOtYSwgbG8gcXVlIGF5dWRhcsOtYSBhIGRlc2NhcnRhciBvIHNvc3RlbmVyIHVuYSBleHBsaWNhY2nDs24gZGUgdGlwbyBmaW4gZGUgc2VtYW5hLgoKYGBge3IgZmlnLXBwcC0yNn0KdG1fYmFzZW1hcChtYXBhX2Jhc2UpICsKICB0bV9zaGFwZShqYW1fMjZfc2YpICsKICB0bV9kb3RzKGNvbCA9IHBhbGV0YVsxXSwgc2l6ZSA9IDAuMDUpICsKICB0bV9sYXlvdXQodGl0bGUgPSAiQ29uZ2VzdGnDs24sIGp1ZXZlcyAyNiBkZSBzZXB0aWVtYnJlIikKYGBgCgoqKkZpZ3VyYSAxLioqIFBhdHLDs24gZXNwYWNpYWwgZGUgZXZlbnRvcyBkZSBjb25nZXN0acOzbiBzb2JyZSBlbCBtYXBhIGRlIEJvZ290w6EsIGp1ZXZlcyAyNiBkZSBzZXB0aWVtYnJlIGRlIDIwMjQuCgpgYGB7ciBmaWctcHBwLTI3fQp0bV9iYXNlbWFwKG1hcGFfYmFzZSkgKwogIHRtX3NoYXBlKGphbV8yN19zZikgKwogIHRtX2RvdHMoY29sID0gcGFsZXRhWzJdLCBzaXplID0gMC4wNSkgKwogIHRtX2xheW91dCh0aXRsZSA9ICJDb25nZXN0acOzbiwgdmllcm5lcyAyNyBkZSBzZXB0aWVtYnJlIikKYGBgCgoqKkZpZ3VyYSAyLioqIFBhdHLDs24gZXNwYWNpYWwgZGUgZXZlbnRvcyBkZSBjb25nZXN0acOzbiBzb2JyZSBlbCBtYXBhIGRlIEJvZ290w6EsIHZpZXJuZXMgMjcgZGUgc2VwdGllbWJyZSBkZSAyMDI0LgoKIyMgUGVyZmlsIGhvcmFyaW8KCkFudGVzIGRlIGVudHJhciBhIGxhcyBwcnVlYmFzIGVzcGFjaWFsZXMgZm9ybWFsZXMgY29udmllbmUgc2FiZXIgYSBxdcOpIGhvcmEgYXJyYW5jw7MgY2FkYSBldmVudG8gw7puaWNvIGRlIGNvbmdlc3Rpw7NuLCBubyBjdcOhbnRhcyB2ZWNlcyBzZSByZXBvcnTDsyBtaWVudHJhcyBzZWd1w61hIGFjdGl2bywgcXVlIHlhIHNlIGZpbHRyw7MgZW4gbGEgZGVkdXBsaWNhY2nDs24uCgpgYGB7ciBwZXJmaWwtaG9yYXJpb30KcGVyZmlsX2hvcmFyaW8gPC0gZXZlbnRvc191bmljb3MgJT4lCiAgZmlsdGVyKHR5cGUgPT0gIkpBTSIpICU+JQogIG11dGF0ZShob3JhID0gbHVicmlkYXRlOjpob3VyKGZlY2hhX2hvcmEpKSAlPiUKICBjb3VudChmZWNoYSwgaG9yYSkKCmhvcmFfcGljb18yNiA8LSBwZXJmaWxfaG9yYXJpbyAlPiUgZmlsdGVyKGZlY2hhID09IGFzLkRhdGUoIjIwMjQtMDktMjYiKSkgJT4lCiAgZmlsdGVyKG4gPT0gbWF4KG4pKSAlPiUgcHVsbChob3JhKSAlPiUgbWluKCkKaG9yYV9waWNvXzI3IDwtIHBlcmZpbF9ob3JhcmlvICU+JSBmaWx0ZXIoZmVjaGEgPT0gYXMuRGF0ZSgiMjAyNC0wOS0yNyIpKSAlPiUKICBmaWx0ZXIobiA9PSBtYXgobikpICU+JSBwdWxsKGhvcmEpICU+JSBtaW4oKQoKcmVzX2hvcmFfcGljb18yNiA8LSBob3JhX3BpY29fMjYKcmVzX2hvcmFfcGljb18yNyA8LSBob3JhX3BpY29fMjcKcmVzX25fcGljb18yNiA8LSBwZXJmaWxfaG9yYXJpbyAlPiUgZmlsdGVyKGZlY2hhID09IGFzLkRhdGUoIjIwMjQtMDktMjYiKSwgaG9yYSA9PSBob3JhX3BpY29fMjYpICU+JSBwdWxsKG4pCnJlc19uX3BpY29fMjcgPC0gcGVyZmlsX2hvcmFyaW8gJT4lIGZpbHRlcihmZWNoYSA9PSBhcy5EYXRlKCIyMDI0LTA5LTI3IiksIGhvcmEgPT0gaG9yYV9waWNvXzI3KSAlPiUgcHVsbChuKQpgYGAKCmBgYHtyIGZpZy1wZXJmaWwtaG9yYXJpb30KZ2dwbG90KHBlcmZpbF9ob3JhcmlvLCBhZXMoeCA9IGhvcmEsIHkgPSBuLCBjb2xvciA9IGZhY3RvcihmZWNoYSkpKSArCiAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDEpICsKICBnZW9tX3BvaW50KHNpemUgPSAyKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAyMywgMikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsZXRhWzE6Ml0sIGxhYmVscyA9IGMoIjI2IHNlcHQiLCAiMjcgc2VwdCIpLAogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJEw61hIikgKwogIGxhYnModGl0bGUgPSAiRXZlbnRvcyDDum5pY29zIGRlIGNvbmdlc3Rpw7NuIHBvciBob3JhIGRlIGluaWNpbyIsCiAgICAgICB4ID0gIkhvcmEgZGVsIGTDrWEiLCB5ID0gIkV2ZW50b3Mgw7puaWNvcyIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgoqKkZpZ3VyYSAzLioqIEhvcmEgZGUgaW5pY2lvIGRlIGxvcyBldmVudG9zIMO6bmljb3MgZGUgY29uZ2VzdGnDs24sIGp1ZXZlcyB5IHZpZXJuZXMuCgoqKkludGVycHJldGFjacOzbjoqKiBlbCBqdWV2ZXMgbGEgbWF5b3IgY2FudGlkYWQgZGUgZXZlbnRvcyBhcnJhbmPDsyBhIGxhcyBgciByZXNfaG9yYV9waWNvXzI2YDowMCwgY29uIGByIHJlc19uX3BpY29fMjZgIGluaWNpb3MuIEVsIHZpZXJuZXMgZWwgcGljbyBzZSBjb3JyacOzIGEgbGFzIGByIHJlc19ob3JhX3BpY29fMjdgOjAwLCBjb24gYHIgcmVzX25fcGljb18yN2AgaW5pY2lvcy4gRXNlIGNvcnJpbWllbnRvIGhhY2lhIGxhIG5vY2hlIGVzIGNvbXBhdGlibGUgY29uIHVuIHBhdHLDs24gZGUgc2FsaWRhIGRlIGZpbiBkZSBzZW1hbmEsIGF1bnF1ZSBlc3RhIHRyYW1hIG5vIGluY2x1eWUgZGF0b3MgZGUgbW92aWxpZGFkIGludGVyLW11bmljaXBhbCBwYXJhIGNvbmZpcm1hcmxvIGRlIGZvcm1hIGRpcmVjdGEuCgojICoqUHJ1ZWJhIENTUioqCgojIyBKaS1jdWFkcmFkbyBwb3IgY3VhZHJhbnRlcwoKU2UgZGl2aWRlIGVsIGNvcnJlZG9yIGVuIHVuYSBtYWxsYSBkZSA0IHBvciAzIGN1YWRyYW50ZXMsIGRpbWVuc2lvbmFkYSBwYXJhIHF1ZSBjYWRhIGNlbGRhIHRlbmdhIGVuIHByb21lZGlvIG3DoXMgZGUgY2luY28gZXZlbnRvcyBlc3BlcmFkb3MgYmFqbyBhbGVhdG9yaWVkYWQsIGNvbmRpY2nDs24gcXVlIG5lY2VzaXRhIGxhIGFwcm94aW1hY2nDs24gamktY3VhZHJhZG8gcGFyYSBzZXIgdsOhbGlkYS4KCmBgYHtyIGNoaS1jdWFkcmFkb30KcHJ1ZWJhXzI2IDwtIHF1YWRyYXQudGVzdChqYW1fMjZfcHBwLCBueCA9IDQsIG55ID0gMykKcHJ1ZWJhXzI3IDwtIHF1YWRyYXQudGVzdChqYW1fMjdfcHBwLCBueCA9IDQsIG55ID0gMykKCnJlc19jaGlfMjYgPC0gZm9ybWF0QyhwcnVlYmFfMjYkc3RhdGlzdGljLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDIpCnJlc19wXzI2ICAgPC0gZm9ybWF0QyhwcnVlYmFfMjYkcC52YWx1ZSwgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSA0KQpyZXNfY2hpXzI3IDwtIGZvcm1hdEMocHJ1ZWJhXzI3JHN0YXRpc3RpYywgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAyKQpyZXNfcF8yNyAgIDwtIGZvcm1hdEMocHJ1ZWJhXzI3JHAudmFsdWUsIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gNCkKCiMgRGlzcGFyaWRhZCByZWFsIGVudHJlIGN1YWRyYW50ZXMsIHNvbG8gY29udGFuZG8gbG9zIHF1ZSB0aWVuZW4gYWwgbWVub3MKIyB1biBldmVudG8gKGxvcyBjdWFkcmFudGVzIGVuIGNlcm8gc3VlbGVuIGNhZXIgZnVlcmEgZGVsIHRyYXphZG8gdmlhbCB5CiMgbm8gYXBvcnRhbiB1bmEgY29tcGFyYWNpw7NuIMO6dGlsIGRlIGludGVuc2lkYWQpLgpjb250ZW9fMjYgPC0gYXMudmVjdG9yKHF1YWRyYXRjb3VudChqYW1fMjZfcHBwLCBueCA9IDQsIG55ID0gMykpCmNvbnRlb18yNl9hY3Rpdm9zIDwtIGNvbnRlb18yNltjb250ZW9fMjYgPiAwXQpyZXNfY3VhZHJhbnRlX21heF8yNiA8LSBtYXgoY29udGVvXzI2X2FjdGl2b3MpCnJlc19jdWFkcmFudGVfbWluXzI2IDwtIG1pbihjb250ZW9fMjZfYWN0aXZvcykKcmVzX2N1YWRyYW50ZV9yYXpvbl8yNiA8LSBmb3JtYXRDKHJlc19jdWFkcmFudGVfbWF4XzI2IC8gcmVzX2N1YWRyYW50ZV9taW5fMjYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAxKQpgYGAKCmBgYHtyIGZpZy1jdWFkcmFudGVzfQpwbG90KHF1YWRyYXRjb3VudChqYW1fMjZfcHBwLCBueCA9IDQsIG55ID0gMyksCiAgICAgbWFpbiA9ICJDb250ZW8gcG9yIGN1YWRyYW50ZXMsIDI2IGRlIHNlcHRpZW1icmUiKQpwb2ludHMoamFtXzI2X3BwcCwgY29sID0gcGFsZXRhWzFdLCBwY2ggPSAxNiwgY2V4ID0gMC42KQpgYGAKCioqRmlndXJhIDQuKiogQ29udGVvIGRlIGV2ZW50b3MgZGUgY29uZ2VzdGnDs24gcG9yIGN1YWRyYW50ZSwganVldmVzIDI2IGRlIHNlcHRpZW1icmUuCgpFbCBlc3RhZMOtc3RpY28gamktY3VhZHJhZG8gZXMgYHIgcmVzX2NoaV8yNmAgZWwganVldmVzIHkgYHIgcmVzX2NoaV8yN2AgZWwgdmllcm5lcywgYW1ib3MgY29uIHZhbG9yIHAgZGUgYHIgcmVzX3BfMjZgIHkgYHIgcmVzX3BfMjdgIHJlc3BlY3RpdmFtZW50ZS4KCioqSW50ZXJwcmV0YWNpw7NuOioqIGxvcyBkb3MgdmFsb3JlcyBwIHF1ZWRhbiBtdXkgcG9yIGRlYmFqbyBkZSAwLjA1LCBhc8OtIHF1ZSBzZSByZWNoYXphIGxhIGFsZWF0b3JpZWRhZCBlc3BhY2lhbCBjb21wbGV0YSBsb3MgZG9zIGTDrWFzLiBFbCBjdWFkcmFudGUgY29uIG3DoXMgZXZlbnRvcyBlbCBqdWV2ZXMgdGllbmUgYHIgcmVzX2N1YWRyYW50ZV9tYXhfMjZgLCBmcmVudGUgYSBgciByZXNfY3VhZHJhbnRlX21pbl8yNmAgZW4gZWwgcXVlIG1lbm9zIHRpZW5lIGVudHJlIGxvcyBxdWUgcmVnaXN0cmFuIGFsIG1lbm9zIHVuIGV2ZW50bywgdW5hIHJhesOzbiBkZSBgciByZXNfY3VhZHJhbnRlX3Jhem9uXzI2YCBhIDEgcXVlIHlhIGFudGljaXBhIHF1ZSBsYSBpbnRlbnNpZGFkIG5vIGVzIGNvbnN0YW50ZSB5IHF1ZSBjb252aWVuZSB1c2FyIGxhIGZ1bmNpw7NuIEsgaW5ob21vZ8OpbmVhIG3DoXMgYWRlbGFudGUuCgojIyBWZWNpbm8gbcOhcyBjZXJjYW5vIChmdW5jacOzbiBHKQoKTGEgcHJ1ZWJhIGRlIGN1YWRyYW50ZXMgbWlyYSBsYSBpbnRlbnNpZGFkIHBvciB6b25hcywgcGVybyBubyBkaWNlIG5hZGEgc29icmUgcXXDqSB0YW4gY2VyY2EgZXN0w6EgY2FkYSBldmVudG8gZGUgc3UgdmVjaW5vIG3DoXMgcHLDs3hpbW8uIExhIGZ1bmNpw7NuIEcgeSBsYSBkaXN0YW5jaWEgbWVkaWEgYWwgdmVjaW5vIG3DoXMgY2VyY2FubyBjb21wbGV0YW4gZXNlIGRpYWduw7NzdGljbyBhIHVuYSBlc2NhbGEgbcOhcyBmaW5hLgoKYGBge3IgZnVuY2lvbi1nfQpubl8yNiA8LSBubmRpc3QoamFtXzI2X3BwcCkgKiBtX3Bvcl9ncmFkbwpubl8yNyA8LSBubmRpc3QoamFtXzI3X3BwcCkgKiBtX3Bvcl9ncmFkbwoKcmVzX25uX21lZGlhXzI2IDwtIGZvcm1hdEMobWVhbihubl8yNiksIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMCkKcmVzX25uX21lZGlhXzI3IDwtIGZvcm1hdEMobWVhbihubl8yNyksIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMCkKcmVzX25uX21lZGlhbmFfMjYgPC0gZm9ybWF0QyhtZWRpYW4obm5fMjYpLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDApCnJlc19ubl9tZWRpYW5hXzI3IDwtIGZvcm1hdEMobWVkaWFuKG5uXzI3KSwgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAwKQoKYXJlYV9tMiA8LSBhcmVhLm93aW4odmVudGFuYSkgKiBtX3Bvcl9ncmFkb14yCmxhbWJkYV8yNiA8LSByZXNfbl9qYW1fMjYgLyBhcmVhX20yCmxhbWJkYV8yNyA8LSByZXNfbl9qYW1fMjcgLyBhcmVhX20yCm5uX2Nzcl8yNiA8LSAxIC8gKDIgKiBzcXJ0KGxhbWJkYV8yNikpCm5uX2Nzcl8yNyA8LSAxIC8gKDIgKiBzcXJ0KGxhbWJkYV8yNykpCgpyZXNfbm5fY3NyXzI2IDwtIGZvcm1hdEMobm5fY3NyXzI2LCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDApCnJlc19ubl9jc3JfMjcgPC0gZm9ybWF0Qyhubl9jc3JfMjcsIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMCkKcmVzX25uX3JhdGlvXzI2IDwtIGZvcm1hdEMoMTAwICogbWVhbihubl8yNikgLyBubl9jc3JfMjYsIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMCkKcmVzX25uX3JhdGlvXzI3IDwtIGZvcm1hdEMoMTAwICogbWVhbihubl8yNykgLyBubl9jc3JfMjcsIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMCkKCmdfMjYgPC0gR2VzdChqYW1fMjZfcHBwLCBjb3JyZWN0aW9uID0gImttIikKZ18yNyA8LSBHZXN0KGphbV8yN19wcHAsIGNvcnJlY3Rpb24gPSAia20iKQpgYGAKCmBgYHtyIGZpZy1mdW5jaW9uLWd9CnBhcihtZnJvdyA9IGMoMSwgMikpCnBsb3QoZ18yNiwgbWFpbiA9ICJHLCAyNiBzZXB0IiwgY29sID0gYyhwYWxldGFbMV0sICJncmV5NTAiKSkKcGxvdChnXzI3LCBtYWluID0gIkcsIDI3IHNlcHQiLCBjb2wgPSBjKHBhbGV0YVsyXSwgImdyZXk1MCIpKQpwYXIobWZyb3cgPSBjKDEsIDEpKQpgYGAKCioqRmlndXJhIDUuKiogRnVuY2nDs24gRyBvYnNlcnZhZGEgY29udHJhIGxhIHRlw7NyaWNhIGRlIFBvaXNzb24sIGp1ZXZlcyB5IHZpZXJuZXMuCgpMYSBkaXN0YW5jaWEgbWVkaWEgYWwgdmVjaW5vIG3DoXMgY2VyY2FubyBlcyBkZSBgciByZXNfbm5fbWVkaWFfMjZgIG1ldHJvcyBlbCBqdWV2ZXMgeSBgciByZXNfbm5fbWVkaWFuYV8yNmAgbWV0cm9zIGRlIG1lZGlhbmE7IGJham8gYWxlYXRvcmllZGFkIGNvbXBsZXRhLCBjb24gYHIgcmVzX25famFtXzI2YCBldmVudG9zIGVuIGVzdGEgdmVudGFuYSwgZXNhIGRpc3RhbmNpYSBlc3BlcmFkYSBzZXLDrWEgZGUgYHIgcmVzX25uX2Nzcl8yNmAgbWV0cm9zLiBFbCB2aWVybmVzIGxhIG1lZGlhIG9ic2VydmFkYSBlcyBkZSBgciByZXNfbm5fbWVkaWFfMjdgIG1ldHJvcyBmcmVudGUgYSBgciByZXNfbm5fY3NyXzI3YCBtZXRyb3MgZXNwZXJhZG9zLgoKKipJbnRlcnByZXRhY2nDs246KiogbGEgZGlzdGFuY2lhIG9ic2VydmFkYSBlcyBhcGVuYXMgZWwgYHIgcmVzX25uX3JhdGlvXzI2YCUgZGUgbGEgZXNwZXJhZGEgZWwganVldmVzIHkgZWwgYHIgcmVzX25uX3JhdGlvXzI3YCUgZWwgdmllcm5lcy4gTG9zIGV2ZW50b3MgZXN0w6FuIG11Y2hvIG3DoXMgY2VyY2EgZW50cmUgc8OtIGRlIGxvIHF1ZSBwcmVkaWNlIGVsIGF6YXIsIHkgZXNhIHByb3BvcmNpw7NuIHNlIG1hbnRpZW5lIHNpbWlsYXIgZW50cmUgbG9zIGRvcyBkw61hcyBhdW5xdWUgZWwgdmllcm5lcyB0ZW5nYSBtw6FzIGV2ZW50b3MsIGxvIHF1ZSBzdWdpZXJlIHF1ZSBlbCBncmFkbyBkZSBhZ3J1cGFtaWVudG8gcmVsYXRpdm8gbm8gY2FtYmlhLCBzb2xvIHN1IHZvbHVtZW4uCgojICoqRnVuY2nDs24gSyoqCgojIyBEaXN0YW5jaWEgZGUgcmVmZXJlbmNpYQoKRWwgcmFkaW8gbcOheGltbyBkZSBhbsOhbGlzaXMgc2UgY2FsY3VsYSBkZXNkZSBsYXMgZGltZW5zaW9uZXMgZGUgbGEgdmVudGFuYSwgbm8gZGVzZGUgbGFzIGRpc3RhbmNpYXMgZW50cmUgcHVudG9zLCBwYXJhIG5vIGNhbGN1bGFyIHBhcmVzIGRlIGRpc3RhbmNpYXMgZGUgZm9ybWEgY3VhZHLDoXRpY2Egc29icmUgY2llbnRvcyBkZSBldmVudG9zLgoKYGBge3IgZGlzdGFuY2lhLXJlZmVyZW5jaWF9CmFuY2hvX3ZlbnRhbmEgPC0gZGlmZih2ZW50YW5hJHhyYW5nZSkKYWx0b192ZW50YW5hICA8LSBkaWZmKHZlbnRhbmEkeXJhbmdlKQpyX21heCA8LSAwLjI1ICogc3FydChhbmNob192ZW50YW5hXjIgKyBhbHRvX3ZlbnRhbmFeMikKcl9zZXEgPC0gc2VxKDAsIHJfbWF4LCBsZW5ndGgub3V0ID0gMTI4KQoKcmVzX3JfbWF4X20gPC0gZm9ybWF0QyhyX21heCAqIG1fcG9yX2dyYWRvLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDApCmBgYAoKRWwgcmFkaW8gbcOheGltbyBlcXVpdmFsZSBhIGByIHJlc19yX21heF9tYCBtZXRyb3MsIHVuYSBjdWFydGEgcGFydGUgZGUgbGEgZGlhZ29uYWwgZGVsIGNvcnJlZG9yLgoKIyMgSyBob21vZ8OpbmVhCgpgYGB7ciBrLWhvbW9nZW5lYX0Ka19ob21vZ18yNiA8LSBLZXN0KGphbV8yNl9wcHAsIHIgPSByX3NlcSwgY29ycmVjdGlvbiA9ICJSaXBsZXkiKQprX2hvbW9nXzI3IDwtIEtlc3QoamFtXzI3X3BwcCwgciA9IHJfc2VxLCBjb3JyZWN0aW9uID0gIlJpcGxleSIpCmBgYAoKYGBge3IgZmlnLWstaG9tb2dlbmVhfQpwYXIobWZyb3cgPSBjKDEsIDIpKQpwbG90KGtfaG9tb2dfMjYsIG1haW4gPSAiSyBob21vZ8OpbmVhLCAyNiBzZXB0IiwgY29sID0gYyhwYWxldGFbMV0sICJncmV5NTAiKSkKcGxvdChrX2hvbW9nXzI3LCBtYWluID0gIksgaG9tb2fDqW5lYSwgMjcgc2VwdCIsIGNvbCA9IGMocGFsZXRhWzJdLCAiZ3JleTUwIikpCnBhcihtZnJvdyA9IGMoMSwgMSkpCmBgYAoKKipGaWd1cmEgNi4qKiBGdW5jacOzbiBLIGhvbW9nw6luZWEgZGUgUmlwbGV5LCBqdWV2ZXMgeSB2aWVybmVzLgoKKipJbnRlcnByZXRhY2nDs246KiogbGEgY3VydmEgb2JzZXJ2YWRhIHF1ZWRhIHBvciBlbmNpbWEgZGUgbGEgZGUgUG9pc3NvbiBlbiBsb3MgZG9zIGTDrWFzLCBsbyBxdWUgZGUgbnVldm8gYXB1bnRhIGEgYWdydXBhbWllbnRvLiBQZXJvIGNvbW8gbGEgcHJ1ZWJhIGRlIGN1YWRyYW50ZXMgeWEgbW9zdHLDsyBxdWUgbGEgaW50ZW5zaWRhZCBjYW1iaWEgcG9yIHpvbmEsIHBhcnRlIGRlIGVzdGEgc2VwYXJhY2nDs24gcHVlZGUgc2VyIHNvbG8gZWwgZWZlY3RvIGRlIHRlbmVyIG3DoXMgZXZlbnRvcyBlbiB1bmFzIHpvbmFzIHF1ZSBlbiBvdHJhcywgbm8gYWdydXBhbWllbnRvIGFkaWNpb25hbCBlbnRyZSBldmVudG9zIGNlcmNhbm9zLgoKIyMgSyBpbmhvbW9nw6luZWEKCkNvbW8gbGEgaW50ZW5zaWRhZCBubyBlcyBjb25zdGFudGUsIHNlIHVzYSBsYSBmdW5jacOzbiBLIGluaG9tb2fDqW5lYSwgcXVlIGNvbXBhcmEgY2FkYSBldmVudG8gY29udHJhIHVuYSBpbnRlbnNpZGFkIGxvY2FsIGVuIGx1Z2FyIGRlIHVuIHByb21lZGlvIMO6bmljbyBwYXJhIHRvZG8gZWwgY29ycmVkb3IuCgpgYGB7ciBrLWluaG9tb2dlbmVhfQprX2luaG9tXzI2IDwtIEtpbmhvbShqYW1fMjZfcHBwLCByID0gcl9zZXEsIGNvcnJlY3Rpb24gPSAiUmlwbGV5IikKa19pbmhvbV8yNyA8LSBLaW5ob20oamFtXzI3X3BwcCwgciA9IHJfc2VxLCBjb3JyZWN0aW9uID0gIlJpcGxleSIpCmBgYAoKYGBge3IgZmlnLWstaW5ob21vZ2VuZWF9CnBhcihtZnJvdyA9IGMoMSwgMikpCnBsb3Qoa19pbmhvbV8yNiwgbWFpbiA9ICJLIGluaG9tb2fDqW5lYSwgMjYgc2VwdCIsIGNvbCA9IGMocGFsZXRhWzFdLCAiZ3JleTUwIikpCnBsb3Qoa19pbmhvbV8yNywgbWFpbiA9ICJLIGluaG9tb2fDqW5lYSwgMjcgc2VwdCIsIGNvbCA9IGMocGFsZXRhWzJdLCAiZ3JleTUwIikpCnBhcihtZnJvdyA9IGMoMSwgMSkpCmBgYAoKKipGaWd1cmEgNy4qKiBGdW5jacOzbiBLIGluaG9tb2fDqW5lYSBkZSBSaXBsZXksIGp1ZXZlcyB5IHZpZXJuZXMuCgoqKkludGVycHJldGFjacOzbjoqKiBhbCBjb250cm9sYXIgcG9yIGxhIGludGVuc2lkYWQgbG9jYWwsIGxhIGN1cnZhIHNlIGFjZXJjYSBtw6FzIGEgbGEgZGUgUG9pc3NvbiBxdWUgZW4gbGEgdmVyc2nDs24gaG9tb2fDqW5lYS4gQnVlbmEgcGFydGUgZGUgbG8gcXVlIHBhcmVjw61hIGFncnVwYW1pZW50byBlcyB2YXJpYWNpw7NuIGRlIGxhIGRlbnNpZGFkIGRlIGZvbmRvIGRlbCBjb3JyZWRvciwgbG8gcXVlIHlhIGhhYsOtYW4gYW50aWNpcGFkbyB0YW50byBlbCBqaS1jdWFkcmFkbyBjb21vIGxhIGZ1bmNpw7NuIEcuCgojICoqRGVuc2lkYWQgS2VybmVsKioKCiMjIFNlbGVjY2nDs24gZGVsIGFuY2hvIGRlIGJhbmRhCgpgYGB7ciBzZWxlY2Npb24tYmFuZHdpZHRofQpid19kaWdnbGVfMjYgPC0gYncuZGlnZ2xlKGphbV8yNl9wcHApCmJ3X2RpZ2dsZV8yNyA8LSBidy5kaWdnbGUoamFtXzI3X3BwcCkKYndfc2NvdHRfMjYgIDwtIGJ3LnNjb3R0KGphbV8yNl9wcHApCmJ3X3Njb3R0XzI3ICA8LSBidy5zY290dChqYW1fMjdfcHBwKQoKdGFibGFfYmFuZHdpZHRoIDwtIGRhdGEuZnJhbWUoCiAgZGlhID0gYygiMjYgc2VwdCIsICIyNyBzZXB0IiksCiAgYndfZGlnZ2xlX20gPSBjKGZvcm1hdEMoYndfZGlnZ2xlXzI2ICogbV9wb3JfZ3JhZG8sIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMCksCiAgICAgICAgICAgICAgICAgICBmb3JtYXRDKGJ3X2RpZ2dsZV8yNyAqIG1fcG9yX2dyYWRvLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDApKSwKICBid19zY290dF94X20gPSBjKGZvcm1hdEMoYndfc2NvdHRfMjZbMV0gKiBtX3Bvcl9ncmFkbywgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAwKSwKICAgICAgICAgICAgICAgICAgICBmb3JtYXRDKGJ3X3Njb3R0XzI3WzFdICogbV9wb3JfZ3JhZG8sIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMCkpLAogIGJ3X3Njb3R0X3lfbSA9IGMoZm9ybWF0Qyhid19zY290dF8yNlsyXSAqIG1fcG9yX2dyYWRvLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDApLAogICAgICAgICAgICAgICAgICAgIGZvcm1hdEMoYndfc2NvdHRfMjdbMl0gKiBtX3Bvcl9ncmFkbywgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAwKSkKKQoKa2FibGUodGFibGFfYmFuZHdpZHRoLAogICAgICBjb2wubmFtZXMgPSBjKCJEw61hIiwgImJ3LmRpZ2dsZSAobSkiLCAiYncuc2NvdHQgZWplIFggKG0pIiwgImJ3LnNjb3R0IGVqZSBZIChtKSIpLAogICAgICBjYXB0aW9uID0gIkNvbXBhcmFjacOzbiBkZSBhbmNob3MgZGUgYmFuZGEiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmb250X3NpemUgPSAxMikKCnJlc19id18yNl9tIDwtIGZvcm1hdEMoYndfZGlnZ2xlXzI2ICogbV9wb3JfZ3JhZG8sIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMCkKcmVzX2J3XzI3X20gPC0gZm9ybWF0Qyhid19kaWdnbGVfMjcgKiBtX3Bvcl9ncmFkbywgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAwKQpgYGAKCmBgYHtyIGJhbmR3aWR0aC1jb21wYXJhY2lvbn0KcmVzX2J3X3Njb3R0XzI2X20gPC0gZm9ybWF0Qyhid19zY290dF8yNlsxXSAqIG1fcG9yX2dyYWRvLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDApCnJlc19id19kaWZlcmVuY2lhX3BjdF8yNiA8LSBmb3JtYXRDKDEwMCAqIChid19zY290dF8yNlsxXSAtIGJ3X2RpZ2dsZV8yNikgLyBid19kaWdnbGVfMjYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDApCmBgYAoKKipJbnRlcnByZXRhY2nDs246KiogYGJ3LnNjb3R0YCBkYSBgciByZXNfYndfc2NvdHRfMjZfbWAgbSBlbCBqdWV2ZXMsIHVuIGByIHJlc19id19kaWZlcmVuY2lhX3BjdF8yNmAlIG3DoXMgYW5jaG8gcXVlIGByIHJlc19id18yNl9tYCBtIGRlIGBidy5kaWdnbGVgLiBFc2EgZGlmZXJlbmNpYSBlcyBjb25zaXN0ZW50ZSBjb24gcXVlIGBidy5zY290dGAgYXN1bWUgdW5hIGRpc3BlcnNpw7NuIGNlcmNhbmEgYSBsYSBub3JtYWwgYml2YXJpYWRhLCBtaWVudHJhcyBgYncuZGlnZ2xlYCBhanVzdGEgcG9yIHZhbGlkYWNpw7NuIGNydXphZGEgeSByZXNwb25kZSBtZWpvciBhIHVuIHBhdHLDs24gYWdydXBhZG8gc29icmUgdW5hIHbDrWEgYW5nb3N0YSBjb21vIGVzdGUgY29ycmVkb3IsIHBvciBsbyBxdWUgc2UgdXNhIHBhcmEgbG9zIG1hcGFzIGRlIGludGVuc2lkYWQgKGByIHJlc19id18yNl9tYCBtIGVsIGp1ZXZlcywgYHIgcmVzX2J3XzI3X21gIG0gZWwgdmllcm5lcykuCgojIyBNYXBhcyBkZSBpbnRlbnNpZGFkCgpgYGB7ciBjb252ZXJ0aXItcmFzdGVyfQojIERlZmluaWRhcyBhcXXDrSBwb3JxdWUgYSBwYXJ0aXIgZGUgZXN0ZSBwdW50byBjYWRhIG1hcGEgZGUgaW50ZW5zaWRhZAojIHNlIG5lY2VzaXRhIHRhbnRvIGVuIHN1IHByb3BpYSBmaWd1cmEgY29tbywgbcOhcyBhZGVsYW50ZSwgZW4gbGEKIyBkaWZlcmVuY2lhIGVudHJlIGxvcyBkb3MgZMOtYXMuCmNvbnZlcnRpcl9hX3Jhc3RlciA8LSBmdW5jdGlvbihpbWFnZW4pIHsKICBtYXRyaXogPC0gYXMubWF0cml4KGltYWdlbikKICBjYXBhIDwtIHJhc3Rlcjo6cmFzdGVyKG1hdHJpeikKICByYXN0ZXI6OmV4dGVudChjYXBhKSA8LSByYXN0ZXI6OmV4dGVudChpbWFnZW4keHJhbmdlWzFdLCBpbWFnZW4keHJhbmdlWzJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbWFnZW4keXJhbmdlWzFdLCBpbWFnZW4keXJhbmdlWzJdKQogIGNhcGEgPC0gcmFzdGVyOjpmbGlwKGNhcGEsIGRpcmVjdGlvbiA9ICJ5IikKICByYXN0ZXI6OmNycyhjYXBhKSA8LSAiK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQiCiAgY2FwYQp9Cgpub3JtYWxpemFyIDwtIGZ1bmN0aW9uKGNhcGEpIHsKICAoY2FwYSAtIHJhc3Rlcjo6Y2VsbFN0YXRzKGNhcGEsICJtaW4iKSkgLwogICAgKHJhc3Rlcjo6Y2VsbFN0YXRzKGNhcGEsICJtYXgiKSAtIHJhc3Rlcjo6Y2VsbFN0YXRzKGNhcGEsICJtaW4iKSkKfQpgYGAKCmBgYHtyIGRlbnNpZGFkLTI2fQppbnRlbnNpZGFkXzI2IDwtIGRlbnNpdHkucHBwKGphbV8yNl9wcHAsIHNpZ21hID0gYndfZGlnZ2xlXzI2LCBlZGdlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlteXggPSBjKDEyOCwgMTI4KSkKcmFzdGVyXzI2IDwtIG5vcm1hbGl6YXIoY29udmVydGlyX2FfcmFzdGVyKGludGVuc2lkYWRfMjYpKQpyYXN0ZXJfMjZfdmlzIDwtIGVubWFzY2FyYXJfYmFqb191bWJyYWwocmFzdGVyXzI2KQpgYGAKCmBgYHtyIGZpZy1kZW5zaWRhZC0yNn0KdG1fYmFzZW1hcChtYXBhX2Jhc2UpICsKICB0bV9zaGFwZShyYXN0ZXJfMjZfdmlzKSArCiAgdG1fcmFzdGVyKHBhbGV0dGUgPSBjKHBhbGV0YVs1XSwgcGFsZXRhWzFdKSwgc3R5bGUgPSAiY29udCIsIGFscGhhID0gMC44NSwKICAgICAgICAgICAgdGl0bGUgPSAiSW50ZW5zaWRhZCBub3JtYWxpemFkYSIpICsKICB0bV9zaGFwZShqYW1fMjZfc2YpICsKICB0bV9kb3RzKGNvbCA9ICJibGFjayIsIHNpemUgPSAwLjAzKSArCiAgdG1fbGF5b3V0KHRpdGxlID0gIkRlbnNpZGFkIGRlIGNvbmdlc3Rpw7NuLCAyNiBkZSBzZXB0aWVtYnJlIikKYGBgCgoqKkZpZ3VyYSA4LioqIERlbnNpZGFkIGtlcm5lbCBkZSBldmVudG9zIGRlIGNvbmdlc3Rpw7NuIHNvYnJlIGVsIG1hcGEgZGUgQm9nb3TDoSwganVldmVzIDI2IGRlIHNlcHRpZW1icmUuCgpgYGB7ciBkZW5zaWRhZC0yN30KaW50ZW5zaWRhZF8yNyA8LSBkZW5zaXR5LnBwcChqYW1fMjdfcHBwLCBzaWdtYSA9IGJ3X2RpZ2dsZV8yNywgZWRnZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpbXl4ID0gYygxMjgsIDEyOCkpCnJhc3Rlcl8yNyA8LSBub3JtYWxpemFyKGNvbnZlcnRpcl9hX3Jhc3RlcihpbnRlbnNpZGFkXzI3KSkKcmFzdGVyXzI3X3ZpcyA8LSBlbm1hc2NhcmFyX2Jham9fdW1icmFsKHJhc3Rlcl8yNykKYGBgCgpgYGB7ciBmaWctZGVuc2lkYWQtMjd9CnRtX2Jhc2VtYXAobWFwYV9iYXNlKSArCiAgdG1fc2hhcGUocmFzdGVyXzI3X3ZpcykgKwogIHRtX3Jhc3RlcihwYWxldHRlID0gYyhwYWxldGFbNV0sIHBhbGV0YVsyXSksIHN0eWxlID0gImNvbnQiLCBhbHBoYSA9IDAuODUsCiAgICAgICAgICAgIHRpdGxlID0gIkludGVuc2lkYWQgbm9ybWFsaXphZGEiKSArCiAgdG1fc2hhcGUoamFtXzI3X3NmKSArCiAgdG1fZG90cyhjb2wgPSAiYmxhY2siLCBzaXplID0gMC4wMykgKwogIHRtX2xheW91dCh0aXRsZSA9ICJEZW5zaWRhZCBkZSBjb25nZXN0acOzbiwgMjcgZGUgc2VwdGllbWJyZSIpCmBgYAoKKipGaWd1cmEgOS4qKiBEZW5zaWRhZCBrZXJuZWwgZGUgZXZlbnRvcyBkZSBjb25nZXN0acOzbiBzb2JyZSBlbCBtYXBhIGRlIEJvZ290w6EsIHZpZXJuZXMgMjcgZGUgc2VwdGllbWJyZS4KCioqSW50ZXJwcmV0YWNpw7NuOioqIGVuIGxhcyBkb3MgZmlndXJhcyBsYSBmcmFuamEgZGUgbWF5b3IgaW50ZW5zaWRhZCBjb3JyZSBzb2JyZSBsYSB2w61hIHF1ZSBjcnV6YSBlbCBjZW50cm8gZGUgQ2FqaWPDoSB5IHNpZ3VlIGhhY2lhIENhbGFob3JyYSwgbm8gc2UgcmVwYXJ0ZSBwb3IgZWwgcmVzdG8gZGVsIHJlY3TDoW5ndWxvIGRlIGxhIHZlbnRhbmEuIEVzbyBlcyBqdXN0YW1lbnRlIGxvIHF1ZSBlbCBqaS1jdWFkcmFkbyB5IGxhIGZ1bmNpw7NuIEsgeWEgaGFiw61hbiBzZcOxYWxhZG8gY29uIG7Dum1lcm9zOiBlbCBwcm9ibGVtYSBubyBlcyBsYSB6b25hIGVuIGdlbmVyYWwsIGVzIGVzZSB0cmFtbyB2aWFsIHB1bnR1YWwsIHkgcG9yIGVzbyBhbCBlbm1hc2NhcmFyIGxhcyBjZWxkYXMgZGUgYmFqYSBkZW5zaWRhZCBlbCBtYXBhIGJhc2UgcXVlZGEgdmlzaWJsZSBlbiBjYXNpIHRvZGEgbGEgdmVudGFuYSBzYWx2byBhaMOtLgoKPiBOb3RhIGRlIHZlcmlmaWNhY2nDs246IGxhIG9yaWVudGFjacOzbiBkZWwgcmFzdGVyIGRlcGVuZGUgZGUgY8OzbW8gYHNwYXRzdGF0YCBhbG1hY2VuYSBsYSBtYXRyaXogZGUgaW50ZW5zaWRhZC4gU2kgYWwgdmVyIGxhIEZpZ3VyYSA4IGVsIMOhcmVhIGRlIG1heW9yIGludGVuc2lkYWQgbm8gY29pbmNpZGUgY29uIGxhIG51YmUgZGUgcHVudG9zIG5lZ3JvcywgcmV0aXJhciBsYSBsw61uZWEgYGZsaXAoKWAgZGVudHJvIGRlIGBjb252ZXJ0aXJfYV9yYXN0ZXIoKWAgZW4gbHVnYXIgZGUgYXBsaWNhcmxhLgoKIyAqKkNvbXBhcmFjacOzbiAyNi0yNyoqCgojIyBOb3JtYWxpemFjacOzbiB5IGRpZmVyZW5jaWEKCkNhZGEgbWFwYSBkZSBpbnRlbnNpZGFkIHlhIHNlIG5vcm1hbGl6w7MgZW50cmUgMCB5IDEgZGUgZm9ybWEgaW5kZXBlbmRpZW50ZSBhbCBjb25zdHJ1aXIgbGFzIEZpZ3VyYXMgOCB5IDksIHBhcmEgcXVlIGxhIGNvbXBhcmFjacOzbiBubyBzZSB2ZWEgYWZlY3RhZGEgcG9yIHRlbmVyIGByIHJlc19uX2phbV8yNmAgZXZlbnRvcyB1biBkw61hIHkgYHIgcmVzX25famFtXzI3YCBlbCBvdHJvLgoKIyMgRGlmZXJlbmNpYSBjZWxkYSBhIGNlbGRhCgpgYGB7ciBkaWZlcmVuY2lhLXJhc3Rlcn0KZGlmZXJlbmNpYV9yYXN0ZXIgPC0gcmFzdGVyXzI3IC0gcmFzdGVyXzI2CgpyZXNfbWF4X2V4cGFuc2lvbiAgIDwtIGZvcm1hdEMocmFzdGVyOjpjZWxsU3RhdHMoZGlmZXJlbmNpYV9yYXN0ZXIsICJtYXgiKSwgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAyKQpyZXNfbWF4X2NvbnRyYWNjaW9uIDwtIGZvcm1hdEMocmFzdGVyOjpjZWxsU3RhdHMoZGlmZXJlbmNpYV9yYXN0ZXIsICJtaW4iKSwgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAyKQoKIyBQYXJhIGVsIG1hcGEsIHNlIG9jdWx0YSBlbCBjYW1iaW8gY2FzaSBudWxvICh8ZGlmZXJlbmNpYXwgPCAwLjA1KSB5IHNlCiMgZGVqYSB2ZXIgc29sbyBkw7NuZGUgbGEgY29uZ2VzdGnDs24gcmVsYXRpdmEgc3ViacOzIG8gYmFqw7MgZGUgZm9ybWEKIyBhcHJlY2lhYmxlIGVudHJlIGxvcyBkb3MgZMOtYXMuCmRpZmVyZW5jaWFfcmFzdGVyX3ZpcyA8LSBkaWZlcmVuY2lhX3Jhc3RlcgpkaWZlcmVuY2lhX3Jhc3Rlcl92aXNbYWJzKGRpZmVyZW5jaWFfcmFzdGVyKSA8IDAuMDVdIDwtIE5BCmBgYAoKRWwgY2FtYmlvIGNlbGRhIGEgY2VsZGEgdmEgZGUgYHIgcmVzX21heF9jb250cmFjY2lvbmAgKG1heW9yIGNvbnRyYWNjacOzbiByZWxhdGl2YSkgYSBgciByZXNfbWF4X2V4cGFuc2lvbmAgKG1heW9yIGV4cGFuc2nDs24gcmVsYXRpdmEpIGVuIGxhIGVzY2FsYSBub3JtYWxpemFkYS4KCioqSW50ZXJwcmV0YWNpw7NuOioqIGxvcyB2YWxvcmVzIHBvc2l0aXZvcyBtYXJjYW4gdHJhbW9zIGRvbmRlIGxhIGNvbmdlc3Rpw7NuIHJlbGF0aXZhIGNyZWNpw7MgZWwgdmllcm5lcyBmcmVudGUgYWwganVldmVzLCB5IGxvcyBuZWdhdGl2b3MgZG9uZGUgYmFqw7MuIExvcyBwdW50b3MgcXVlIGFwYXJlY2VuIGVuIGFtYm9zIGTDrWFzIGJham8gdW5hIHkgb3RyYSBpbnRlbnNpZGFkLCBzaW4gZGVwZW5kZXIgZGVsIGNhbWJpbyBkZSBjYWxlbmRhcmlvLCBzb24gbG9zIHF1ZSBtw6FzIHN1Z2llcmVuIHVuIGN1ZWxsbyBkZSBib3RlbGxhIGVzdHJ1Y3R1cmFsOyBsb3MgcXVlIHNvbG8gYXBhcmVjZW4gZWwgdmllcm5lcyBlc3TDoW4gbcOhcyBsaWdhZG9zIGFsIGVmZWN0byBkZSBmaW4gZGUgc2VtYW5hIHF1ZSBhbCBkaXNlw7FvIGRlIGxhIHbDrWEuCgojIyBNYXBhcyBmaW5hbGVzCgpgYGB7ciBtYXBhLTI2fQp0bV9iYXNlbWFwKG1hcGFfYmFzZSkgKwogIHRtX3NoYXBlKHJhc3Rlcl8yNl92aXMpICsKICB0bV9yYXN0ZXIocGFsZXR0ZSA9IGMocGFsZXRhWzVdLCBwYWxldGFbMV0pLCBzdHlsZSA9ICJjb250IiwgYWxwaGEgPSAwLjg1LAogICAgICAgICAgICB0aXRsZSA9ICJJbnRlbnNpZGFkIG5vcm1hbGl6YWRhIikgKwogIHRtX2xheW91dCh0aXRsZSA9ICJDb25nZXN0acOzbiwganVldmVzIDI2IikKYGBgCgoqKkZpZ3VyYSAxMC4qKiBNYXBhIDEsIGludGVuc2lkYWQgbm9ybWFsaXphZGEgZGUgY29uZ2VzdGnDs24gc29icmUgZWwgbWFwYSBkZSBCb2dvdMOhLCBqdWV2ZXMgMjYgZGUgc2VwdGllbWJyZS4KCmBgYHtyIG1hcGEtMjd9CnRtX2Jhc2VtYXAobWFwYV9iYXNlKSArCiAgdG1fc2hhcGUocmFzdGVyXzI3X3ZpcykgKwogIHRtX3Jhc3RlcihwYWxldHRlID0gYyhwYWxldGFbNV0sIHBhbGV0YVsyXSksIHN0eWxlID0gImNvbnQiLCBhbHBoYSA9IDAuODUsCiAgICAgICAgICAgIHRpdGxlID0gIkludGVuc2lkYWQgbm9ybWFsaXphZGEiKSArCiAgdG1fbGF5b3V0KHRpdGxlID0gIkNvbmdlc3Rpw7NuLCB2aWVybmVzIDI3IikKYGBgCgoqKkZpZ3VyYSAxMS4qKiBNYXBhIDIsIGludGVuc2lkYWQgbm9ybWFsaXphZGEgZGUgY29uZ2VzdGnDs24gc29icmUgZWwgbWFwYSBkZSBCb2dvdMOhLCB2aWVybmVzIDI3IGRlIHNlcHRpZW1icmUuCgpgYGB7ciBtYXBhLWRpZmVyZW5jaWF9CnRtX2Jhc2VtYXAobWFwYV9iYXNlKSArCiAgdG1fc2hhcGUoZGlmZXJlbmNpYV9yYXN0ZXJfdmlzKSArCiAgdG1fcmFzdGVyKHBhbGV0dGUgPSBjKHBhbGV0YVsxXSwgIndoaXRlIiwgcGFsZXRhWzJdKSwgc3R5bGUgPSAiY29udCIsCiAgICAgICAgICAgIG1pZHBvaW50ID0gMCwgYWxwaGEgPSAwLjg1LCB0aXRsZSA9ICJDYW1iaW8gcmVsYXRpdm8iKSArCiAgdG1fbGF5b3V0KHRpdGxlID0gIkRpZmVyZW5jaWEgMjcgbWVub3MgMjYgZGUgc2VwdGllbWJyZSIpCmBgYAoKKipGaWd1cmEgMTIuKiogTWFwYSAzLCBkaWZlcmVuY2lhIGNlbGRhIGEgY2VsZGEgc29icmUgZWwgbWFwYSBkZSBCb2dvdMOhLCBlbnRyZSBlbCB2aWVybmVzIHkgZWwganVldmVzLgoKIyAqKk90cm9zIEV2ZW50b3MqKgoKQ2llcnJlcyBkZSB2w61hLCBwZWxpZ3JvcyB5IGFjY2lkZW50ZXMgc2UgcmV2aXNhbiBkZSBmb3JtYSBleHBsb3JhdG9yaWEuIENvbiBjaWVycmVzIGRlIHbDrWEgeSBhY2NpZGVudGVzIGVsIG7Dum1lcm8gZGUgZXZlbnRvcyDDum5pY29zIHBvciBkw61hIGVzIHRhbiBiYWpvIHF1ZSBzb2xvIHNlIHViaWNhbiBlbiBlbCBtYXBhLCBzaW4gcHJ1ZWJhIGVzdGFkw61zdGljYS4KCmBgYHtyIG90cm9zLWV2ZW50b3MtY29uc3RydWlyfQpjb25zdHJ1aXJfcHBwX3RpcG8gPC0gZnVuY3Rpb24odGlwbywgZGlhKSB7CiAgc3ViIDwtIGV2ZW50b3NfdW5pY29zICU+JSBmaWx0ZXIodHlwZSA9PSB0aXBvLCBmZWNoYSA9PSBhcy5EYXRlKGRpYSkpCiAgaWYgKG5yb3coc3ViKSA9PSAwKSByZXR1cm4oTlVMTCkKICB1bmlxdWUocHBwKHggPSBzdWIkbG9uZ2l0dWQsIHkgPSBzdWIkbGF0aXR1ZCwgd2luZG93ID0gdmVudGFuYSkpCn0KCmhhemFyZF8yNiA8LSBjb25zdHJ1aXJfcHBwX3RpcG8oIkhBWkFSRCIsICIyMDI0LTA5LTI2IikKaGF6YXJkXzI3IDwtIGNvbnN0cnVpcl9wcHBfdGlwbygiSEFaQVJEIiwgIjIwMjQtMDktMjciKQpjZXJyYWRvXzI2IDwtIGNvbnN0cnVpcl9wcHBfdGlwbygiUk9BRF9DTE9TRUQiLCAiMjAyNC0wOS0yNiIpCmNlcnJhZG9fMjcgPC0gY29uc3RydWlyX3BwcF90aXBvKCJST0FEX0NMT1NFRCIsICIyMDI0LTA5LTI3IikKYWNjaWRlbnRlXzI2IDwtIGNvbnN0cnVpcl9wcHBfdGlwbygiQUNDSURFTlQiLCAiMjAyNC0wOS0yNiIpCmFjY2lkZW50ZV8yNyA8LSBjb25zdHJ1aXJfcHBwX3RpcG8oIkFDQ0lERU5UIiwgIjIwMjQtMDktMjciKQoKcmVzX25faGF6YXJkXzI2IDwtIG5wb2ludHMoaGF6YXJkXzI2KQpyZXNfbl9oYXphcmRfMjcgPC0gbnBvaW50cyhoYXphcmRfMjcpCnJlc19uX2NlcnJhZG9fMjYgPC0gbnBvaW50cyhjZXJyYWRvXzI2KQpyZXNfbl9jZXJyYWRvXzI3IDwtIG5wb2ludHMoY2VycmFkb18yNykKcmVzX25fYWNjaWRlbnRlXzI2IDwtIG5wb2ludHMoYWNjaWRlbnRlXzI2KQpyZXNfbl9hY2NpZGVudGVfMjcgPC0gbnBvaW50cyhhY2NpZGVudGVfMjcpCmBgYAoKUGVsaWdyb3MgZW4gdsOtYSBwYXNhcm9uIGRlIGByIHJlc19uX2hhemFyZF8yNmAgZXZlbnRvcyBlbCBqdWV2ZXMgYSBgciByZXNfbl9oYXphcmRfMjdgIGVsIHZpZXJuZXMsIHVuYSBtdWVzdHJhIHN1ZmljaWVudGUgcGFyYSB1bmEgcHJ1ZWJhIENTUiBvcmllbnRhdGl2YS4gQ2llcnJlcyBkZSB2w61hIChgciByZXNfbl9jZXJyYWRvXzI2YCB5IGByIHJlc19uX2NlcnJhZG9fMjdgKSB5IGFjY2lkZW50ZXMgKGByIHJlc19uX2FjY2lkZW50ZV8yNmAgeSBgciByZXNfbl9hY2NpZGVudGVfMjdgKSBzb24gZGVtYXNpYWRvIGVzY2Fzb3MgcGFyYSBjdWFscXVpZXIgcHJ1ZWJhIGZvcm1hbC4KCmBgYHtyIG90cm9zLWV2ZW50b3MtcHJ1ZWJhfQpwcnVlYmFfaGF6YXJkXzI2IDwtIHF1YWRyYXQudGVzdChoYXphcmRfMjYsIG54ID0gMiwgbnkgPSAyKQpwcnVlYmFfaGF6YXJkXzI3IDwtIHF1YWRyYXQudGVzdChoYXphcmRfMjcsIG54ID0gMiwgbnkgPSAyKQoKcmVzX3BfaGF6YXJkXzI2IDwtIGZvcm1hdEMocHJ1ZWJhX2hhemFyZF8yNiRwLnZhbHVlLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDQpCnJlc19wX2hhemFyZF8yNyA8LSBmb3JtYXRDKHBydWViYV9oYXphcmRfMjckcC52YWx1ZSwgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSA0KQpgYGAKCkVsIHZhbG9yIHAgZGUgbGEgcHJ1ZWJhIGppLWN1YWRyYWRvIHBhcmEgcGVsaWdyb3MgZW4gdsOtYSBlcyBgciByZXNfcF9oYXphcmRfMjZgIGVsIGp1ZXZlcyB5IGByIHJlc19wX2hhemFyZF8yN2AgZWwgdmllcm5lcy4KCioqSW50ZXJwcmV0YWNpw7NuOioqIGNvbiBtdWVzdHJhcyBkZSBgciByZXNfbl9oYXphcmRfMjZgIHkgYHIgcmVzX25faGF6YXJkXzI3YCBldmVudG9zLCBlc3RhIHBydWViYSBlcyBvcmllbnRhdGl2YS4gU2lydmUgcGFyYSBubyBkZXNjYXJ0YXIgbGEgY2F0ZWdvcsOtYSBkZSBlbnRyYWRhLCBubyBwYXJhIHNvc3RlbmVyIHVuYSBjb25jbHVzacOzbiBlc3RhZMOtc3RpY2EgZmlybWUgc29icmUgcGVsaWdyb3MgZW4gdsOtYS4KCmBgYHtyIG90cm9zLWV2ZW50b3Mtc2Z9CmNvbnN0cnVpcl9zZl9ldGlxdWV0YWRvIDwtIGZ1bmN0aW9uKG9iamV0b19wcHAsIGV0aXF1ZXRhKSB7CiAgaWYgKGlzLm51bGwob2JqZXRvX3BwcCkgfHwgbnBvaW50cyhvYmpldG9fcHBwKSA9PSAwKSByZXR1cm4oTlVMTCkKICBzZl9vYmogPC0gcHBwX2Ffc2Yob2JqZXRvX3BwcCkKICBzZl9vYmokZXZlbnRvIDwtIGV0aXF1ZXRhCiAgc2Zfb2JqCn0KCm90cm9zX2V2ZW50b3NfbGlzdGEgPC0gbGlzdCgKICBjb25zdHJ1aXJfc2ZfZXRpcXVldGFkbyhjZXJyYWRvXzI2LCAiQ2llcnJlIGRlIHbDrWEsIDI2IHNlcHQiKSwKICBjb25zdHJ1aXJfc2ZfZXRpcXVldGFkbyhjZXJyYWRvXzI3LCAiQ2llcnJlIGRlIHbDrWEsIDI3IHNlcHQiKSwKICBjb25zdHJ1aXJfc2ZfZXRpcXVldGFkbyhhY2NpZGVudGVfMjYsICJBY2NpZGVudGUsIDI2IHNlcHQiKSwKICBjb25zdHJ1aXJfc2ZfZXRpcXVldGFkbyhhY2NpZGVudGVfMjcsICJBY2NpZGVudGUsIDI3IHNlcHQiKQopCm90cm9zX2V2ZW50b3NfbGlzdGEgPC0gRmlsdGVyKE5lZ2F0ZShpcy5udWxsKSwgb3Ryb3NfZXZlbnRvc19saXN0YSkKb3Ryb3NfZXZlbnRvc19zZiA8LSBkby5jYWxsKHJiaW5kLCBvdHJvc19ldmVudG9zX2xpc3RhKQpgYGAKCmBgYHtyIGZpZy1vdHJvcy1ldmVudG9zfQojIEVsIG1vZG8gInZpZXciIGRlIHRtYXAgZGlidWphIHNpZW1wcmUgbWFyY2Fkb3JlcyBjaXJjdWxhcmVzIHkgbm8KIyBzb3BvcnRhIGNvbWJpbmFyIGNvbG9yIHkgZm9ybWEgZW4gdW5hIHNvbGEgY2FwYSBkZSBwdW50b3M7IHBvciBlc28KIyB0aXBvIHkgZMOtYSBzZSBjb21iaW5hbiBlbiB1bmEgc29sYSBjYXRlZ29yw61hIGRlIGNvbG9yIGVuIHZleiBkZQojIHVzYXIgZG9zIGVzdMOpdGljYXMgKGNvbG9yIHkgZm9ybWEpIGFsIG1pc21vIHRpZW1wby4KdG1fYmFzZW1hcChtYXBhX2Jhc2UpICsKICB0bV9zaGFwZShvdHJvc19ldmVudG9zX3NmKSArCiAgdG1fZG90cyhjb2wgPSAiZXZlbnRvIiwKICAgICAgICAgIHBhbGV0dGUgPSBjKCJDaWVycmUgZGUgdsOtYSwgMjYgc2VwdCIgPSBwYWxldGFbM10sCiAgICAgICAgICAgICAgICAgICAgICAiQ2llcnJlIGRlIHbDrWEsIDI3IHNlcHQiID0gcGFsZXRhWzRdLAogICAgICAgICAgICAgICAgICAgICAgIkFjY2lkZW50ZSwgMjYgc2VwdCIgPSBwYWxldGFbMV0sCiAgICAgICAgICAgICAgICAgICAgICAiQWNjaWRlbnRlLCAyNyBzZXB0IiA9IHBhbGV0YVsyXSksCiAgICAgICAgICBzaXplID0gMC4wOCwgdGl0bGUgPSAiRXZlbnRvIHkgZMOtYSIpICsKICB0bV9sYXlvdXQodGl0bGUgPSAiQ2llcnJlcyBkZSB2w61hIHkgYWNjaWRlbnRlcywgMjYtMjcgZGUgc2VwdGllbWJyZSIpCmBgYAoKKipGaWd1cmEgMTMuKiogQ2llcnJlcyBkZSB2w61hIHkgYWNjaWRlbnRlcyBzb2JyZSBlbCBtYXBhIGRlIEJvZ290w6EsIGp1ZXZlcyB5IHZpZXJuZXMsIHNpbiBwcnVlYmEgZXN0YWTDrXN0aWNhIHBvciB0YW1hw7FvIGRlIG11ZXN0cmEuCgojICoqQ29uY2x1c2lvbmVzKioKCiMjIEhhbGxhemdvcyBwcmluY2lwYWxlcwoKRWwgamktY3VhZHJhZG8sIGxhIGRpc3RhbmNpYSBhbCB2ZWNpbm8gbcOhcyBjZXJjYW5vIHkgbGEgZnVuY2nDs24gSyBjb2luY2lkZW4gZW4gbG8gbWlzbW8gZGVzZGUgdHJlcyDDoW5ndWxvcyBkaXN0aW50b3M6IGVsIGNvcnJlZG9yIG5vIHJlcGFydGUgbGEgY29uZ2VzdGnDs24gYWwgYXphciwgc2UgY29uY2VudHJhIHNvYnJlIGxhIHbDrWEgcXVlIGNydXphIENhamljw6EgeSBzaWd1ZSBoYWNpYSBDYWxhaG9ycmEsIGVuIHRyYW1vcyBxdWUgc2UgcmVwaXRlbiBlbCBqdWV2ZXMgeSBlbCB2aWVybmVzLiBFbCB2aWVybmVzIGVsIG7Dum1lcm8gZGUgZXZlbnRvcyBzdWJlIGByIHJlc19pbmNyZW1lbnRvX2phbWAlIHkgZWwgcGljbyBob3JhcmlvIHNlIGNvcnJlIGRlIGxhcyBgciByZXNfaG9yYV9waWNvXzI2YDowMCBhIGxhcyBgciByZXNfaG9yYV9waWNvXzI3YDowMCwgdW4gcGF0csOzbiBjb21wYXRpYmxlIGNvbiBsYSBzYWxpZGEgaGFjaWEgQ2FqaWPDoSB5IFRvY2FuY2lww6EgYW50ZXMgZGVsIGZpbiBkZSBzZW1hbmEuCgojIyBSZWNvbWVuZGFjaW9uZXMgcGFyYSBwbGFuaWZpY2FjacOzbiB1cmJhbmEKCkVsIGF1bWVudG8gZGUgYHIgcmVzX2luY3JlbWVudG9famFtYCUgZW4gZXZlbnRvcyBkZWwgdmllcm5lcywgY29uY2VudHJhZG8gZW4gbGEgZnJhbmphIGRlIGxhIG5vY2hlLCBqdXN0aWZpY2EgbWVkaWRhcyBkZSB0csOhZmljbyBhY290YWRhcyBhIGVzYSB2ZW50YW5hIGhvcmFyaWEsIGNvbW8gYWdlbnRlcyBvIHNlbWFmb3JpemFjacOzbiBhZGFwdGF0aXZhIGVudHJlIGxhcyBgciByZXNfaG9yYV9waWNvXzI3YDowMCB5IGVsIGNpZXJyZSBkZSBsYSBub2NoZSwgc2luIG5lY2VzaWRhZCBkZSBtYW50ZW5lcmxhcyBlbCByZXN0byBkZSBsYSBzZW1hbmEuIExvcyB0cmFtb3MgcXVlIHNlIHJlcGl0ZW4gZW4gYW1iYXMgam9ybmFkYXMsIHZpc2libGVzIGRvbmRlIGNvaW5jaWRlbiBsYXMgRmlndXJhcyAxMCB5IDExIGFudGVzIGRlIGNhbGN1bGFyIGxhIGRpZmVyZW5jaWEsIHNvbiBsb3MgcXVlIGp1c3RpZmljYW4gdW5hIGludGVydmVuY2nDs24gcGVybWFuZW50ZSBkZSBpbmZyYWVzdHJ1Y3R1cmEsIHBvcnF1ZSBzdSBjb25nZXN0acOzbiBubyBkZXBlbmRlIGRlbCBkw61hLgoKIyMgTGltaXRhY2lvbmVzCgpFbCBhbsOhbGlzaXMgY3VicmUgZG9zIGTDrWFzIHkgdW4gY29ycmVkb3IgZXNwZWPDrWZpY28sIHBvciBsbyBxdWUgbm8gZGViZSBleHRlbmRlcnNlIGEgdG9kYSBCb2dvdMOhIHNpbiBtw6FzIGV2aWRlbmNpYS4gQ2llcnJlcyBkZSB2w61hIHkgYWNjaWRlbnRlcyBuZWNlc2l0YW4gdW5hIHZlbnRhbmEgZGUgdGllbXBvIG3DoXMgbGFyZ2EgYW50ZXMgZGUgcG9kZXIgYW5hbGl6YXJzZSBjb24gZWwgbWlzbW8gcmlnb3IgZXN0YWTDrXN0aWNvIHF1ZSBsYSBjb25nZXN0acOzbi4K