1 Introducción

El tramo de la Autopista Norte entre Bogotá, Cajicá y Tocancipá aparece repetido en los nombres de calle de la trama Waze (street) usada en este trabajo. Este documento revisa si los reportes de congestión en ese corredor se concentran en tramos fijos o se reparten al azar, comparando el jueves 26 y el viernes 27 de septiembre de 2024 con técnicas de análisis de patrones puntuales en R.

1.1 Objetivo

Establecer si la congestión vehicular del corredor se concentra en tramos fijos, si esa concentración cambia entre el jueves 26 y el viernes 27 de septiembre de 2024, y qué tramos deberían priorizarse en la gestión del tráfico.

1.2 Fuente de los datos

La trama corresponde a eventos Waze para Colombia, recortada al tramo de la Autopista Norte entre Bogotá, Cajicá y Tocancipá, durante el 26 y 27 de septiembre de 2024.

2 Datos y Limpieza

2.1 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

2.2 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.

2.3 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.

3 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.

3.1 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.

3.2 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.

4 Prueba CSR

4.1 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.

4.2 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.

5 Función K

5.1 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.

5.2 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.

5.3 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.

6 Densidad Kernel

6.1 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).

6.2 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.

7 Comparación 26-27

7.1 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.

7.2 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.

7.3 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.

8 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.

9 Conclusiones

9.1 Hallazgos principales

El ji-cuadrado, la distancia al vecino más cercano y la función K coinciden en lo mismo desde tres ángulos distintos: el corredor no reparte la congestión al azar, se concentra sobre la vía que cruza Cajicá y sigue hacia Calahorra, en tramos que se repiten el jueves y el viernes. El viernes el número de eventos sube 59% y el pico horario se corre de las 11:00 a las 20:00, un patrón compatible con la salida hacia Cajicá y Tocancipá antes del fin de semana.

9.2 Recomendaciones para planificación urbana

El aumento de 59% en eventos del viernes, concentrado en la franja de la noche, justifica medidas de tráfico acotadas a esa ventana horaria, como agentes o semaforización adaptativa entre las 20:00 y el cierre de la noche, sin necesidad de mantenerlas el resto de la semana. Los tramos que se repiten en ambas jornadas, visibles donde coinciden las Figuras 10 y 11 antes de calcular la diferencia, son los que justifican una intervención permanente de infraestructura, porque su congestión no depende del día.

9.3 Limitaciones

El análisis cubre dos días y un corredor específico, por lo que no debe extenderse a toda Bogotá sin más evidencia. Cierres de vía y accidentes necesitan una ventana de tiempo más larga antes de poder analizarse con el mismo rigor estadístico que la congestión.

LS0tCnRpdGxlOiAiKipNMlUyIC0gQ2FzbzogQW7DoWxpc2lzIGRlIFBhdHJvbmVzIFB1bnR1YWxlcyBkZSBFdmVudG9zIGRlIE1vdmlsaWRhZCBXYXplKioiCmF1dGhvcjogIlJhZmFlbCBKb3NlIERlbCBDYXN0aWxsbyBQYXZhamVhdSIKZGF0ZTogIjIwMjYtMDYtMDgiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBmYWxzZQogICAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRoZW1lOiBjb3NtbwogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKcGFyYW1zOgogIHNlZWQ6IDg5NzI3OTEKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUKLS0tCgo8c3R5bGU+CmJvZHksIGgxLCBoMiwgaDMsIGg0LCBwLCBsaSwgdGQsIHRoLCAudG9jLWNvbnRlbnQgewogIGZvbnQtZmFtaWx5OiAnQXB0b3MgRGlzcGxheScsICdDYWxpYnJpJywgJ1NlZ29lIFVJJywgc2Fucy1zZXJpZjsKfQpoMSBzdHJvbmcsIGgxIGIgewogIGZvbnQtd2VpZ2h0OiA3MDA7Cn0KPC9zdHlsZT4KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICJjZW50ZXIiKQoKIyBJbnN0YWxhY2nDs24geSBjYXJnYSBkZSBsaWJyZXLDrWFzLiBTZSB1c2EgcmVwb3MgZXhwbMOtY2l0byBwYXJhIGV2aXRhcgojIGVsIGVycm9yIGRlIHNlbGVjY2nDs24gZGUgZXNwZWpvIGVuIGVudG9ybm9zIG5vIGludGVyYWN0aXZvcy4KcGFxdWV0ZXMgPC0gYygicmVhZHhsIiwgImRwbHlyIiwgImx1YnJpZGF0ZSIsICJzcGF0c3RhdCIsICJzZiIsCiAgICAgICAgICAgICAgInJhc3RlciIsICJ0bWFwIiwgImxlYWZsZXQiLCAiZ2dwbG90MiIsICJrbml0ciIsICJrYWJsZUV4dHJhIikKCmZvciAocGtnIGluIHBhcXVldGVzKSB7CiAgaWYgKCFyZXF1aXJlKHBrZywgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcyhwa2csIHJlcG9zID0gImh0dHBzOi8vY2xvdWQuci1wcm9qZWN0Lm9yZyIpCiAgICBsaWJyYXJ5KHBrZywgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQogIH0KfQoKc2V0LnNlZWQocGFyYW1zJHNlZWQpCgojIE1vZG8gInZpZXciIGRlIHRtYXA6IGxhcyBncsOhZmljYXMgZ2VvZXNwYWNpYWxlcyBzZSByZW5kZXJpemFuIGNvbW8KIyBtYXBhcyBkZSBMZWFmbGV0IGludGVyYWN0aXZvcyBjb24gdW4gbWFwYSBiYXNlIHJlYWwgZGUgZm9uZG8uIExhcwojIHRlc2VsYXMgZGVsIG1hcGEgc2UgZGVzY2FyZ2FuIGFsIGFicmlyIGVsIEhUTUwgZW4gZWwgbmF2ZWdhZG9yLCBubwojIGFsIHRlamVyIGVsIGRvY3VtZW50bywgcG9yIGxvIHF1ZSBubyBzZSBuZWNlc2l0YSBpbnRlcm5ldCBlbiBSU3R1ZGlvCiMgZW4gZWwgbW9tZW50byBkZSBoYWNlciBrbml0Lgp0bWFwX21vZGUoInZpZXciKQptYXBhX2Jhc2UgPC0gIk9wZW5TdHJlZXRNYXAiCgojIENvbnZpZXJ0ZSB1biBvYmpldG8gcHBwIGRlIHNwYXRzdGF0IGEgcHVudG9zIHNmIGVuIFdHUzg0LCBsaXN0b3MKIyBwYXJhIGRpYnVqYXJzZSBzb2JyZSB1biBtYXBhIGJhc2UgY29uIHRtYXAuCnBwcF9hX3NmIDwtIGZ1bmN0aW9uKG9iamV0b19wcHApIHsKICBzZjo6c3RfYXNfc2YoZGF0YS5mcmFtZShsb24gPSBvYmpldG9fcHBwJHgsIGxhdCA9IG9iamV0b19wcHAkeSksCiAgICAgICAgICAgICAgIGNvb3JkcyA9IGMoImxvbiIsICJsYXQiKSwgY3JzID0gNDMyNikKfQoKIyBMYSB2ZW50YW5hIGRlIGVzdHVkaW8gZXMgdW4gcmVjdMOhbmd1bG8sIHBlcm8gbG9zIGV2ZW50b3Mgc2UgY29uY2VudHJhbgojIGVuIHVuYSBmcmFuamEgYW5nb3N0YSBzb2JyZSBsYSB2w61hLiBTaW4gZW5tYXNjYXJhciwgZWwgcmFzdGVyIGRlCiMgZGVuc2lkYWQgcGludGEgY2FzaSB0b2RvIGVsIHJlY3TDoW5ndWxvIGRlbCBtaXNtbyBjb2xvciB5IGVsIG1hcGEgZGUKIyBjYWxvciByZWFsIHF1ZWRhIGludmlzaWJsZS4gRXN0YSBmdW5jacOzbiB2dWVsdmUgTkEgKHRyYW5zcGFyZW50ZSkgbGFzCiMgY2VsZGFzIHBvciBkZWJham8gZGVsIHVtYnJhbCwgcGFyYSBxdWUgc29sbyBzZSB2ZWEgY29sb3IgZG9uZGUgaGF5CiMgc2XDsWFsIHJlYWwgeSBlbCBtYXBhIGJhc2Ugc2Ugbm90ZSBkZWJham8gZW4gZWwgcmVzdG8uCmVubWFzY2FyYXJfYmFqb191bWJyYWwgPC0gZnVuY3Rpb24oY2FwYSwgdW1icmFsID0gMC4wNSkgewogIGNhcGFbY2FwYSA8IHVtYnJhbF0gPC0gTkEKICBjYXBhCn0KCiMgUGFsZXRhIGhvbW9nw6luZWEgcGFyYSB0b2RvIGVsIGRvY3VtZW50bwpwYWxldGEgPC0gYygiIzJDN0JCNiIsICIjRDcxOTFDIiwgIiMxQTk2NDEiLCAiI0ZEQUU2MSIsICIjQUJEOUU5IikKCiMgRmFjdG9yIGRlIGNvbnZlcnNpw7NuIGdyYWRvLW1ldHJvIHVzYWRvIGVuIHRvZG8gZWwgZG9jdW1lbnRvLiBFcyB1bmEKIyBhcHJveGltYWNpw7NuIChpZ25vcmEgbGEgY29tcHJlc2nDs24gZGUgbGEgbG9uZ2l0dWQgcG9yIGxhIGxhdGl0dWQpLAojIHN1ZmljaWVudGUgcGFyYSBoYWJsYXIgZGUgZXNjYWxhcyBkZSBjaWVudG9zIGRlIG1ldHJvcywgbm8gcGFyYQojIG1lZGljaW9uZXMgZGUgcHJlY2lzacOzbiBjYXRhc3RyYWwuCm1fcG9yX2dyYWRvIDwtIDExMTAwMApgYGAKCiMgKipJbnRyb2R1Y2Npw7NuKioKCkVsIHRyYW1vIGRlIGxhIEF1dG9waXN0YSBOb3J0ZSBlbnRyZSBCb2dvdMOhLCBDYWppY8OhIHkgVG9jYW5jaXDDoSBhcGFyZWNlIHJlcGV0aWRvIGVuIGxvcyBub21icmVzIGRlIGNhbGxlIGRlIGxhIHRyYW1hIFdhemUgKGBzdHJlZXRgKSB1c2FkYSBlbiBlc3RlIHRyYWJham8uIEVzdGUgZG9jdW1lbnRvIHJldmlzYSBzaSBsb3MgcmVwb3J0ZXMgZGUgY29uZ2VzdGnDs24gZW4gZXNlIGNvcnJlZG9yIHNlIGNvbmNlbnRyYW4gZW4gdHJhbW9zIGZpam9zIG8gc2UgcmVwYXJ0ZW4gYWwgYXphciwgY29tcGFyYW5kbyBlbCBqdWV2ZXMgMjYgeSBlbCB2aWVybmVzIDI3IGRlIHNlcHRpZW1icmUgZGUgMjAyNCBjb24gdMOpY25pY2FzIGRlIGFuw6FsaXNpcyBkZSBwYXRyb25lcyBwdW50dWFsZXMgZW4gUi4KCiMjIE9iamV0aXZvCgpFc3RhYmxlY2VyIHNpIGxhIGNvbmdlc3Rpw7NuIHZlaGljdWxhciBkZWwgY29ycmVkb3Igc2UgY29uY2VudHJhIGVuIHRyYW1vcyBmaWpvcywgc2kgZXNhIGNvbmNlbnRyYWNpw7NuIGNhbWJpYSBlbnRyZSBlbCBqdWV2ZXMgMjYgeSBlbCB2aWVybmVzIDI3IGRlIHNlcHRpZW1icmUgZGUgMjAyNCwgeSBxdcOpIHRyYW1vcyBkZWJlcsOtYW4gcHJpb3JpemFyc2UgZW4gbGEgZ2VzdGnDs24gZGVsIHRyw6FmaWNvLgoKIyMgRnVlbnRlIGRlIGxvcyBkYXRvcwoKTGEgdHJhbWEgY29ycmVzcG9uZGUgYSBldmVudG9zIFdhemUgcGFyYSBDb2xvbWJpYSwgcmVjb3J0YWRhIGFsIHRyYW1vIGRlIGxhIEF1dG9waXN0YSBOb3J0ZSBlbnRyZSBCb2dvdMOhLCBDYWppY8OhIHkgVG9jYW5jaXDDoSwgZHVyYW50ZSBlbCAyNiB5IDI3IGRlIHNlcHRpZW1icmUgZGUgMjAyNC4KCiMgKipEYXRvcyB5IExpbXBpZXphKioKCiMjIENhcmdhIGRlbCBhcmNoaXZvCgpFbCBhcmNoaXZvIHRyYWUgMTcgY29sdW1uYXMuIFBhcmEgcXVlIG5pbmd1bmEgY2VsZGEgY29uIGZvcm1hdG8gZGUgZmVjaGEgY29ycm9tcGlkbyBzZSBwaWVyZGEgYWwgY2FyZ2FyLCBzZSBmdWVyemEgZWwgdGlwbyBgbnVtZXJpY2AgZW4gbGFzIGNvbHVtbmFzIGRlIGNvb3JkZW5hZGFzOiBhc8OtLCBjdWFscXVpZXIgY2VsZGEgbWFsIGZvcm1hdGVhZGEgY29tbyBmZWNoYSBsbGVnYSBhIFIgY29tbyBzdSBuw7ptZXJvIGRlIHNlcmllIG9yaWdpbmFsIGVuIHZleiBkZSBjb21vIHRleHRvIGluc2VydmlibGUuCgpgYGB7ciBjYXJnYXItZGF0b3N9CiMgQnVzY2EgZWwgYXJjaGl2byBlbiB2YXJpYXMgcnV0YXMgcHJvYmFibGVzIGVuIGx1Z2FyIGRlIGFzdW1pciB1bmEgc29sYS4KIyBTaSBuaW5ndW5hIGV4aXN0ZSwgZWwgZXJyb3IgaW5kaWNhIGVsIGRpcmVjdG9yaW8gZGUgdHJhYmFqbyBhY3R1YWwgeQojIHRvZGFzIGxhcyBydXRhcyByZXZpc2FkYXMsIHBhcmEgbG9jYWxpemFyIGVsIGFyY2hpdm8gc2luIGFkaXZpbmFyLgpydXRhX2RvY3VtZW50b19hY3R1YWwgPC0gdHJ5Q2F0Y2goewogIGlmIChyZXF1aXJlTmFtZXNwYWNlKCJyc3R1ZGlvYXBpIiwgcXVpZXRseSA9IFRSVUUpICYmIHJzdHVkaW9hcGk6OmlzQXZhaWxhYmxlKCkpIHsKICAgIGRpcm5hbWUocnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGgpCiAgfSBlbHNlIHsKICAgIE5BX2NoYXJhY3Rlcl8KICB9Cn0sIGVycm9yID0gZnVuY3Rpb24oZSkgTkFfY2hhcmFjdGVyXykKCmJ1c2Nhcl9hcmNoaXZvX2RhdG9zIDwtIGZ1bmN0aW9uKG5vbWJyZV9hcmNoaXZvID0gIlRyYW1hX1dhemUueGxzeCIpIHsKICBjYW5kaWRhdG9zIDwtIHVuaXF1ZShjKAogICAgbm9tYnJlX2FyY2hpdm8sCiAgICBmaWxlLnBhdGgoZ2V0d2QoKSwgbm9tYnJlX2FyY2hpdm8pLAogICAgaWYgKCFpcy5uYShydXRhX2RvY3VtZW50b19hY3R1YWwpKSBmaWxlLnBhdGgocnV0YV9kb2N1bWVudG9fYWN0dWFsLCBub21icmVfYXJjaGl2byksCiAgICAiQzovUG9udGlmaWNpYV9Vbl9KYXZlcmlhbmEvQW5hbGlzaXNfR2VvZXNwYWNpYWwvTTAyX1UwMl9QYXRyb25lcy9UcmFtYV9XYXplLnhsc3giLAogICAgZmlsZS5wYXRoKFN5cy5nZXRlbnYoIlVTRVJQUk9GSUxFIiksICJEb3dubG9hZHMiLCBub21icmVfYXJjaGl2byksCiAgICBmaWxlLnBhdGgoU3lzLmdldGVudigiVVNFUlBST0ZJTEUiKSwgIkRvY3VtZW50cyIsIG5vbWJyZV9hcmNoaXZvKSwKICAgIGZpbGUucGF0aChTeXMuZ2V0ZW52KCJVU0VSUFJPRklMRSIpLCAiRGVza3RvcCIsIG5vbWJyZV9hcmNoaXZvKQogICkpCiAgY2FuZGlkYXRvcyA8LSBjYW5kaWRhdG9zW256Y2hhcihjYW5kaWRhdG9zKV0KICBlbmNvbnRyYWRvcyA8LSBjYW5kaWRhdG9zW2ZpbGUuZXhpc3RzKGNhbmRpZGF0b3MpXQoKICBpZiAobGVuZ3RoKGVuY29udHJhZG9zKSA9PSAwKSB7CiAgICBzdG9wKAogICAgICAiXG5ObyBzZSBlbmNvbnRybyAnIiwgbm9tYnJlX2FyY2hpdm8sICInIGVuIG5pbmd1bmEgcnV0YSBjYW5kaWRhdGEuXG4iLAogICAgICAiRGlyZWN0b3JpbyBkZSB0cmFiYWpvIGFjdHVhbCAoZ2V0d2QoKSk6ICIsIGdldHdkKCksICJcbiIsCiAgICAgICJSdXRhcyByZXZpc2FkYXM6XG4gIC0gIiwgcGFzdGUoY2FuZGlkYXRvcywgY29sbGFwc2UgPSAiXG4gIC0gIiksICJcbiIsCiAgICAgICJTb2x1Y2lvbjogY29waWEgJyIsIG5vbWJyZV9hcmNoaXZvLCAiJyBhIGxhIGNhcnBldGEgZGVsIC5SbWQgIiwKICAgICAgIihsYSBtYXMgc2ltcGxlKSwgbyBhZ3JlZ2Egc3UgcnV0YSByZWFsIGRlbnRybyBkZWwgdmVjdG9yICdjYW5kaWRhdG9zJyAiLAogICAgICAiZW4gZXN0ZSBjaHVuay4iCiAgICApCiAgfQoKICBtZXNzYWdlKCJBcmNoaXZvIGRlIGRhdG9zIGVuY29udHJhZG8gZW46ICIsIGVuY29udHJhZG9zWzFdKQogIGVuY29udHJhZG9zWzFdCn0KCnJ1dGFfZXhjZWwgPC0gYnVzY2FyX2FyY2hpdm9fZGF0b3MoIlRyYW1hX1dhemUueGxzeCIpCgp0aXBvc19jb2x1bW5hcyA8LSBjKCJudW1lcmljIiwgIm51bWVyaWMiLCAidGV4dCIsICJudW1lcmljIiwgImxvZ2ljYWwiLAogICAgICAgICAgICAgICAgICAgICAibnVtZXJpYyIsICJudW1lcmljIiwgInRleHQiLCAidGV4dCIsICJudW1lcmljIiwKICAgICAgICAgICAgICAgICAgICAgIm51bWVyaWMiLCAidGV4dCIsICJ0ZXh0IiwgIm51bWVyaWMiLCAibnVtZXJpYyIsCiAgICAgICAgICAgICAgICAgICAgICJudW1lcmljIiwgInRleHQiKQoKZGF0b3Nfd2F6ZSA8LSByZWFkX2V4Y2VsKHJ1dGFfZXhjZWwsIHNoZWV0ID0gIkhvamEgMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sX3R5cGVzID0gdGlwb3NfY29sdW1uYXMpCgprYWJsZShoZWFkKGRhdG9zX3dhemUsIDUpLCBjYXB0aW9uID0gIlByaW1lcm9zIHJlZ2lzdHJvcyBkZSBsYSB0cmFtYSBXYXplIikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZm9udF9zaXplID0gMTIpICU+JQogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpCmBgYAoKIyMgQ29ycmVjY2nDs24gZGUgY29vcmRlbmFkYXMKCmBsb2NhdGlvbl94YCB5IGBsb2NhdGlvbl95YCBlc3TDoW4gbXVsdGlwbGljYWRhcyBwb3IgMTBeNiwgcGVybyB1bmEgcGFydGUgZGUgbG9zIHJlZ2lzdHJvcyBwZXJkacOzIHVubyBvIGRvcyBkw61naXRvcyBlbiBsYSBleHBvcnRhY2nDs24sIGxvIHF1ZSBkZXNwbGF6YSBsYSBjb29yZGVuYWRhIHVuIG9yZGVuIGRlIG1hZ25pdHVkLiBMYSBmdW5jacOzbiBzaWd1aWVudGUgcHJ1ZWJhIHBvdGVuY2lhcyBkZSBkaWV6IGhhc3RhIHViaWNhciBlbCB2YWxvciBlbiBlbCByYW5nbyBnZW9ncsOhZmljbyBwbGF1c2libGUgZGVsIGNvcnJlZG9yIChsb25naXR1ZCBlbnRyZSA3My45wrAgeSA3NC4xwrAgb2VzdGUsIGxhdGl0dWQgZW50cmUgNC4wwrAgeSA1LjLCsCBub3J0ZSksIGNvbnNlcnZhbmRvIGVsIHNpZ25vIG9yaWdpbmFsLgoKYGBge3IgY29ycmVnaXItY29vcmRlbmFkYXN9CmNvcnJlZ2lyX21hZ25pdHVkIDwtIGZ1bmN0aW9uKHZhbG9yLCBtaW5pbW8sIG1heGltbykgewogIGJhc2UgPC0gYWJzKHZhbG9yKSAvIDFlNgogIHNpZ25vIDwtIHNpZ24odmFsb3IpCiAgcmVzdWx0YWRvIDwtIHJlcChOQV9yZWFsXywgbGVuZ3RoKHZhbG9yKSkKICBmb3IgKHBvdGVuY2lhIGluIDA6MykgewogICAgY2FuZGlkYXRvIDwtIGJhc2UgKiAxMF5wb3RlbmNpYQogICAgcGVuZGllbnRlIDwtIGlzLm5hKHJlc3VsdGFkbykgJiBjYW5kaWRhdG8gPj0gbWluaW1vICYgY2FuZGlkYXRvIDw9IG1heGltbwogICAgcmVzdWx0YWRvW3BlbmRpZW50ZV0gPC0gc2lnbm9bcGVuZGllbnRlXSAqIGNhbmRpZGF0b1twZW5kaWVudGVdCiAgfQogIHJlc3VsdGFkbwp9CgpkYXRvc193YXplIDwtIGRhdG9zX3dhemUgJT4lCiAgbXV0YXRlKAogICAgbG9uZ2l0dWQgPSBjb3JyZWdpcl9tYWduaXR1ZChsb2NhdGlvbl94LCA3My45LCA3NC4xKSwKICAgIGxhdGl0dWQgID0gY29ycmVnaXJfbWFnbml0dWQobG9jYXRpb25feSwgNC4wLCA1LjIpCiAgKQoKcmVzX3RvdGFsX2ZpbGFzICAgICA8LSBucm93KGRhdG9zX3dhemUpCnJlc19maWxhc19jb3JydXB0YXMgPC0gc3VtKGlzLm5hKGRhdG9zX3dhemUkbG9uZ2l0dWQpIHwgaXMubmEoZGF0b3Nfd2F6ZSRsYXRpdHVkKSkKcmVzX3BjdF9yZWN1cGVyYWRvICA8LSBmb3JtYXRDKDEwMCAqICgxIC0gcmVzX2ZpbGFzX2NvcnJ1cHRhcyAvIHJlc190b3RhbF9maWxhcyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAxKQpgYGAKCkRlIGByIHJlc190b3RhbF9maWxhc2AgcmVnaXN0cm9zLCBgciByZXNfZmlsYXNfY29ycnVwdGFzYCBubyB0ZW7DrWFuIHVuIHZhbG9yIG51bcOpcmljbyByZWN1cGVyYWJsZSBkZXRyw6FzIGRlIGxhIGNlbGRhIGNvcnJvbXBpZGEgeSBxdWVkYXJvbiBmdWVyYS4gRWwgcmVzdG8sIGByIHJlc19wY3RfcmVjdXBlcmFkb2AlLCBzZSByZXViaWPDsyBkZW50cm8gZGVsIHJhbmdvIGVzcGVyYWRvIHNpbiBuZWNlc2lkYWQgZGUgZGVzY2FydGFybG8uCgoqKkludGVycHJldGFjacOzbjoqKiBwZXJkZXIgdW4gZMOtZ2l0byBlbiBsYSBleHBvcnRhY2nDs24gZXMgdW4gcHJvYmxlbWEgc2lzdGVtw6F0aWNvIGRlbCBhcmNoaXZvLCBubyBlcnJvcmVzIHN1ZWx0b3MuIENvcnJlZ2lyIHBvciBtYWduaXR1ZCBlbiB2ZXogZGUgZWxpbWluYXIgbGEgZmlsYSBldml0YSBwZXJkZXIgaW5mb3JtYWNpw7NuIHNvYnJlIHRyYW1vcyBxdWUgc29sbyBhcGFyZWNlbiBlbiBlc29zIHJlZ2lzdHJvcy4KCiMjIFZlbnRhbmEgeSBldmVudG9zIMO6bmljb3MKCkxhIHZlbnRhbmEgZGUgZXN0dWRpbyAoYHZlbnRhbmFgKSBjdWJyZSBlbCB0cmFtbyBlbnRyZSBsYXMgY29vcmRlbmFkYXMgLTc0LjA0MzMxwrAgeSAtNzMuOTkyOcKwIGRlIGxvbmdpdHVkIHkgNC44ODU3MzbCsCBhIDQuOTQ4NTYywrAgZGUgbGF0aXR1ZCwgbGEgZnJhbmphIGRvbmRlIGFwYXJlY2VuIGxvcyBub21icmVzIGRlIGNhbGxlIGRlIENhamljw6EgeSBUb2NhbmNpcMOhIGVuIGxhIHRyYW1hLiBDYWRhIGV2ZW50byBzZSByZXBpdGUgbWllbnRyYXMgZXN0w6EgYWN0aXZvLCBhc8OtIHF1ZSBzZSBjb25zZXJ2YSB1biBzb2xvIHB1bnRvIHBvciBldmVudG8gKGB1dWlkYCksIHRvbWFuZG8gc3UgcHJpbWVyIHJlcG9ydGUgZGUgY2FkYSBkw61hLgoKYGBge3IgZmVjaGFzLWRlZHVwfQp2ZW50YW5hIDwtIG93aW4oeHJhbmdlID0gYygtNzQuMDQzMzEsIC03My45OTI5KSwKICAgICAgICAgICAgICAgICB5cmFuZ2UgPSBjKDQuODg1NzM2LCA0Ljk0ODU2MikpCgpkYXRvc193YXplIDwtIGRhdG9zX3dhemUgJT4lCiAgbXV0YXRlKGZlY2hhX2hvcmEgPSB5bWRfaG1zKGNyZWF0aW9uX0RhdGUsIHF1aWV0ID0gVFJVRSksCiAgICAgICAgIGZlY2hhID0gYXMuRGF0ZShmZWNoYV9ob3JhKSkKCmRhdG9zX3ZhbGlkb3MgPC0gZGF0b3Nfd2F6ZSAlPiUKICBmaWx0ZXIoIWlzLm5hKGxvbmdpdHVkKSwgIWlzLm5hKGxhdGl0dWQpLAogICAgICAgICBsb25naXR1ZCA+PSB2ZW50YW5hJHhyYW5nZVsxXSwgbG9uZ2l0dWQgPD0gdmVudGFuYSR4cmFuZ2VbMl0sCiAgICAgICAgIGxhdGl0dWQgID49IHZlbnRhbmEkeXJhbmdlWzFdLCBsYXRpdHVkICA8PSB2ZW50YW5hJHlyYW5nZVsyXSkKCmV2ZW50b3NfdW5pY29zIDwtIGRhdG9zX3ZhbGlkb3MgJT4lCiAgZmlsdGVyKGZlY2hhICVpbiUgYXMuRGF0ZShjKCIyMDI0LTA5LTI2IiwgIjIwMjQtMDktMjciKSkpICU+JQogIGFycmFuZ2UoZmVjaGFfaG9yYSkgJT4lCiAgZGlzdGluY3QodXVpZCwgZmVjaGEsIC5rZWVwX2FsbCA9IFRSVUUpCgp0YWJsYV9yZXN1bWVuIDwtIGV2ZW50b3NfdW5pY29zICU+JQogIGNvdW50KHR5cGUsIGZlY2hhLCBuYW1lID0gImV2ZW50b3NfdW5pY29zIikgJT4lCiAgYXJyYW5nZSh0eXBlLCBmZWNoYSkKCmthYmxlKHRhYmxhX3Jlc3VtZW4sIGNhcHRpb24gPSAiRXZlbnRvcyDDum5pY29zIHBvciB0aXBvIHkgZMOtYSIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZvbnRfc2l6ZSA9IDEyKQoKcmVzX3BjdF9qYW1fdG90YWwgPC0gZm9ybWF0QygxMDAgKiBzdW0oZXZlbnRvc191bmljb3MkdHlwZSA9PSAiSkFNIikgLyBucm93KGV2ZW50b3NfdW5pY29zKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAwKQpgYGAKCioqSW50ZXJwcmV0YWNpw7NuOioqIGxhIGNvbmdlc3Rpw7NuIChgSkFNYCkgZXMgZWwgYHIgcmVzX3BjdF9qYW1fdG90YWxgJSBkZSBsb3MgYHIgbnJvdyhldmVudG9zX3VuaWNvcylgIGV2ZW50b3Mgw7puaWNvcyBkZSBsb3MgZG9zIGTDrWFzLCBtw6FzIHF1ZSBsYXMgb3RyYXMgdHJlcyBjYXRlZ29yw61hcyBqdW50YXMuIENpZXJyZXMgZGUgdsOtYSB5IGFjY2lkZW50ZXMgcXVlZGFuIGNvbiB0YW4gcG9jb3MgcHVudG9zIHBvciBkw61hIHF1ZSBjdWFscXVpZXIgcHJ1ZWJhIGVzdGFkw61zdGljYSBzb2JyZSBlbGxvcyBkZWJlIGxlZXJzZSBjb21vIGV4cGxvcmF0b3JpYS4KCiMgKipQYXRyw7NuIGRlIENvbmdlc3Rpw7NuKioKCkxhIGNvbmdlc3Rpw7NuIChgSkFNYCkgcmVjaWJlIGVsIGFuw6FsaXNpcyBjb21wbGV0byBwb3JxdWUgZXMgZWwgYHIgcmVzX3BjdF9qYW1fdG90YWxgJSBkZSBsb3MgZXZlbnRvcyDDum5pY29zIHJlZ2lzdHJhZG9zLCBsYSDDum5pY2EgY2F0ZWdvcsOtYSBjb24gc3VmaWNpZW50ZXMgcHVudG9zIHBhcmEgc29zdGVuZXIgcHJ1ZWJhcyBkZSBhbGVhdG9yaWVkYWQgZXNwYWNpYWwgY29uIGFsZ28gZGUgcG90ZW5jaWEgZXN0YWTDrXN0aWNhLiBMYXMgb3RyYXMgdHJlcyBzZSByZXZpc2FuIG3DoXMgYWRlbGFudGUgc2luIGxhIG1pc21hIHByb2Z1bmRpZGFkLgoKIyMgQ29uc3RydWNjacOzbiBkZWwgcGF0csOzbiBkZSBwdW50b3MKCmBgYHtyIGNvbnN0cnVpci1wcHB9CmphbV8yNiA8LSBldmVudG9zX3VuaWNvcyAlPiUgZmlsdGVyKHR5cGUgPT0gIkpBTSIsIGZlY2hhID09IGFzLkRhdGUoIjIwMjQtMDktMjYiKSkKamFtXzI3IDwtIGV2ZW50b3NfdW5pY29zICU+JSBmaWx0ZXIodHlwZSA9PSAiSkFNIiwgZmVjaGEgPT0gYXMuRGF0ZSgiMjAyNC0wOS0yNyIpKQoKamFtXzI2X3BwcCA8LSB1bmlxdWUocHBwKHggPSBqYW1fMjYkbG9uZ2l0dWQsIHkgPSBqYW1fMjYkbGF0aXR1ZCwgd2luZG93ID0gdmVudGFuYSkpCmphbV8yN19wcHAgPC0gdW5pcXVlKHBwcCh4ID0gamFtXzI3JGxvbmdpdHVkLCB5ID0gamFtXzI3JGxhdGl0dWQsIHdpbmRvdyA9IHZlbnRhbmEpKQoKamFtXzI2X3NmIDwtIHBwcF9hX3NmKGphbV8yNl9wcHApCmphbV8yN19zZiA8LSBwcHBfYV9zZihqYW1fMjdfcHBwKQoKcmVzX25famFtXzI2IDwtIG5wb2ludHMoamFtXzI2X3BwcCkKcmVzX25famFtXzI3IDwtIG5wb2ludHMoamFtXzI3X3BwcCkKcmVzX2luY3JlbWVudG9famFtIDwtIGZvcm1hdEMoMTAwICogKHJlc19uX2phbV8yNyAvIHJlc19uX2phbV8yNiAtIDEpLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDApCmBgYAoKRWwganVldmVzIHNlIHJlZ2lzdHJhcm9uIGByIHJlc19uX2phbV8yNmAgZXZlbnRvcyDDum5pY29zIGRlIGNvbmdlc3Rpw7NuIHkgZWwgdmllcm5lcyBgciByZXNfbl9qYW1fMjdgLCB1biBgciByZXNfaW5jcmVtZW50b19qYW1gJSBtw6FzLiBMYSBzZWNjacOzbiBkZSBwZXJmaWwgaG9yYXJpbyByZXZpc2Egc2kgZXNlIGF1bWVudG8gc2UgY29uY2VudHJhIGVuIGFsZ3VuYSBmcmFuamEgcGFydGljdWxhciBkZWwgZMOtYSwgbG8gcXVlIGF5dWRhcsOtYSBhIGRlc2NhcnRhciBvIHNvc3RlbmVyIHVuYSBleHBsaWNhY2nDs24gZGUgdGlwbyBmaW4gZGUgc2VtYW5hLgoKYGBge3IgZmlnLXBwcC0yNn0KdG1fYmFzZW1hcChtYXBhX2Jhc2UpICsKICB0bV9zaGFwZShqYW1fMjZfc2YpICsKICB0bV9kb3RzKGNvbCA9IHBhbGV0YVsxXSwgc2l6ZSA9IDAuMDUpICsKICB0bV9sYXlvdXQodGl0bGUgPSAiQ29uZ2VzdGnDs24sIGp1ZXZlcyAyNiBkZSBzZXB0aWVtYnJlIikKYGBgCgoqKkZpZ3VyYSAxLioqIFBhdHLDs24gZXNwYWNpYWwgZGUgZXZlbnRvcyBkZSBjb25nZXN0acOzbiBzb2JyZSBlbCBtYXBhIGRlIEJvZ290w6EsIGp1ZXZlcyAyNiBkZSBzZXB0aWVtYnJlIGRlIDIwMjQuCgpgYGB7ciBmaWctcHBwLTI3fQp0bV9iYXNlbWFwKG1hcGFfYmFzZSkgKwogIHRtX3NoYXBlKGphbV8yN19zZikgKwogIHRtX2RvdHMoY29sID0gcGFsZXRhWzJdLCBzaXplID0gMC4wNSkgKwogIHRtX2xheW91dCh0aXRsZSA9ICJDb25nZXN0acOzbiwgdmllcm5lcyAyNyBkZSBzZXB0aWVtYnJlIikKYGBgCgoqKkZpZ3VyYSAyLioqIFBhdHLDs24gZXNwYWNpYWwgZGUgZXZlbnRvcyBkZSBjb25nZXN0acOzbiBzb2JyZSBlbCBtYXBhIGRlIEJvZ290w6EsIHZpZXJuZXMgMjcgZGUgc2VwdGllbWJyZSBkZSAyMDI0LgoKIyMgUGVyZmlsIGhvcmFyaW8KCkFudGVzIGRlIGVudHJhciBhIGxhcyBwcnVlYmFzIGVzcGFjaWFsZXMgZm9ybWFsZXMgY29udmllbmUgc2FiZXIgYSBxdcOpIGhvcmEgYXJyYW5jw7MgY2FkYSBldmVudG8gw7puaWNvIGRlIGNvbmdlc3Rpw7NuLCBubyBjdcOhbnRhcyB2ZWNlcyBzZSByZXBvcnTDsyBtaWVudHJhcyBzZWd1w61hIGFjdGl2bywgcXVlIHlhIHNlIGZpbHRyw7MgZW4gbGEgZGVkdXBsaWNhY2nDs24uCgpgYGB7ciBwZXJmaWwtaG9yYXJpb30KcGVyZmlsX2hvcmFyaW8gPC0gZXZlbnRvc191bmljb3MgJT4lCiAgZmlsdGVyKHR5cGUgPT0gIkpBTSIpICU+JQogIG11dGF0ZShob3JhID0gbHVicmlkYXRlOjpob3VyKGZlY2hhX2hvcmEpKSAlPiUKICBjb3VudChmZWNoYSwgaG9yYSkKCmhvcmFfcGljb18yNiA8LSBwZXJmaWxfaG9yYXJpbyAlPiUgZmlsdGVyKGZlY2hhID09IGFzLkRhdGUoIjIwMjQtMDktMjYiKSkgJT4lCiAgZmlsdGVyKG4gPT0gbWF4KG4pKSAlPiUgcHVsbChob3JhKSAlPiUgbWluKCkKaG9yYV9waWNvXzI3IDwtIHBlcmZpbF9ob3JhcmlvICU+JSBmaWx0ZXIoZmVjaGEgPT0gYXMuRGF0ZSgiMjAyNC0wOS0yNyIpKSAlPiUKICBmaWx0ZXIobiA9PSBtYXgobikpICU+JSBwdWxsKGhvcmEpICU+JSBtaW4oKQoKcmVzX2hvcmFfcGljb18yNiA8LSBob3JhX3BpY29fMjYKcmVzX2hvcmFfcGljb18yNyA8LSBob3JhX3BpY29fMjcKcmVzX25fcGljb18yNiA8LSBwZXJmaWxfaG9yYXJpbyAlPiUgZmlsdGVyKGZlY2hhID09IGFzLkRhdGUoIjIwMjQtMDktMjYiKSwgaG9yYSA9PSBob3JhX3BpY29fMjYpICU+JSBwdWxsKG4pCnJlc19uX3BpY29fMjcgPC0gcGVyZmlsX2hvcmFyaW8gJT4lIGZpbHRlcihmZWNoYSA9PSBhcy5EYXRlKCIyMDI0LTA5LTI3IiksIGhvcmEgPT0gaG9yYV9waWNvXzI3KSAlPiUgcHVsbChuKQpgYGAKCmBgYHtyIGZpZy1wZXJmaWwtaG9yYXJpb30KZ2dwbG90KHBlcmZpbF9ob3JhcmlvLCBhZXMoeCA9IGhvcmEsIHkgPSBuLCBjb2xvciA9IGZhY3RvcihmZWNoYSkpKSArCiAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDEpICsKICBnZW9tX3BvaW50KHNpemUgPSAyKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAyMywgMikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsZXRhWzE6Ml0sIGxhYmVscyA9IGMoIjI2IHNlcHQiLCAiMjcgc2VwdCIpLAogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJEw61hIikgKwogIGxhYnModGl0bGUgPSAiRXZlbnRvcyDDum5pY29zIGRlIGNvbmdlc3Rpw7NuIHBvciBob3JhIGRlIGluaWNpbyIsCiAgICAgICB4ID0gIkhvcmEgZGVsIGTDrWEiLCB5ID0gIkV2ZW50b3Mgw7puaWNvcyIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgoqKkZpZ3VyYSAzLioqIEhvcmEgZGUgaW5pY2lvIGRlIGxvcyBldmVudG9zIMO6bmljb3MgZGUgY29uZ2VzdGnDs24sIGp1ZXZlcyB5IHZpZXJuZXMuCgoqKkludGVycHJldGFjacOzbjoqKiBlbCBqdWV2ZXMgbGEgbWF5b3IgY2FudGlkYWQgZGUgZXZlbnRvcyBhcnJhbmPDsyBhIGxhcyBgciByZXNfaG9yYV9waWNvXzI2YDowMCwgY29uIGByIHJlc19uX3BpY29fMjZgIGluaWNpb3MuIEVsIHZpZXJuZXMgZWwgcGljbyBzZSBjb3JyacOzIGEgbGFzIGByIHJlc19ob3JhX3BpY29fMjdgOjAwLCBjb24gYHIgcmVzX25fcGljb18yN2AgaW5pY2lvcy4gRXNlIGNvcnJpbWllbnRvIGhhY2lhIGxhIG5vY2hlIGVzIGNvbXBhdGlibGUgY29uIHVuIHBhdHLDs24gZGUgc2FsaWRhIGRlIGZpbiBkZSBzZW1hbmEsIGF1bnF1ZSBlc3RhIHRyYW1hIG5vIGluY2x1eWUgZGF0b3MgZGUgbW92aWxpZGFkIGludGVyLW11bmljaXBhbCBwYXJhIGNvbmZpcm1hcmxvIGRlIGZvcm1hIGRpcmVjdGEuCgojICoqUHJ1ZWJhIENTUioqCgojIyBKaS1jdWFkcmFkbyBwb3IgY3VhZHJhbnRlcwoKU2UgZGl2aWRlIGVsIGNvcnJlZG9yIGVuIHVuYSBtYWxsYSBkZSA0IHBvciAzIGN1YWRyYW50ZXMsIGRpbWVuc2lvbmFkYSBwYXJhIHF1ZSBjYWRhIGNlbGRhIHRlbmdhIGVuIHByb21lZGlvIG3DoXMgZGUgY2luY28gZXZlbnRvcyBlc3BlcmFkb3MgYmFqbyBhbGVhdG9yaWVkYWQsIGNvbmRpY2nDs24gcXVlIG5lY2VzaXRhIGxhIGFwcm94aW1hY2nDs24gamktY3VhZHJhZG8gcGFyYSBzZXIgdsOhbGlkYS4KCmBgYHtyIGNoaS1jdWFkcmFkb30KcHJ1ZWJhXzI2IDwtIHF1YWRyYXQudGVzdChqYW1fMjZfcHBwLCBueCA9IDQsIG55ID0gMykKcHJ1ZWJhXzI3IDwtIHF1YWRyYXQudGVzdChqYW1fMjdfcHBwLCBueCA9IDQsIG55ID0gMykKCnJlc19jaGlfMjYgPC0gZm9ybWF0QyhwcnVlYmFfMjYkc3RhdGlzdGljLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDIpCnJlc19wXzI2ICAgPC0gZm9ybWF0QyhwcnVlYmFfMjYkcC52YWx1ZSwgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSA0KQpyZXNfY2hpXzI3IDwtIGZvcm1hdEMocHJ1ZWJhXzI3JHN0YXRpc3RpYywgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAyKQpyZXNfcF8yNyAgIDwtIGZvcm1hdEMocHJ1ZWJhXzI3JHAudmFsdWUsIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gNCkKCiMgRGlzcGFyaWRhZCByZWFsIGVudHJlIGN1YWRyYW50ZXMsIHNvbG8gY29udGFuZG8gbG9zIHF1ZSB0aWVuZW4gYWwgbWVub3MKIyB1biBldmVudG8gKGxvcyBjdWFkcmFudGVzIGVuIGNlcm8gc3VlbGVuIGNhZXIgZnVlcmEgZGVsIHRyYXphZG8gdmlhbCB5CiMgbm8gYXBvcnRhbiB1bmEgY29tcGFyYWNpw7NuIMO6dGlsIGRlIGludGVuc2lkYWQpLgpjb250ZW9fMjYgPC0gYXMudmVjdG9yKHF1YWRyYXRjb3VudChqYW1fMjZfcHBwLCBueCA9IDQsIG55ID0gMykpCmNvbnRlb18yNl9hY3Rpdm9zIDwtIGNvbnRlb18yNltjb250ZW9fMjYgPiAwXQpyZXNfY3VhZHJhbnRlX21heF8yNiA8LSBtYXgoY29udGVvXzI2X2FjdGl2b3MpCnJlc19jdWFkcmFudGVfbWluXzI2IDwtIG1pbihjb250ZW9fMjZfYWN0aXZvcykKcmVzX2N1YWRyYW50ZV9yYXpvbl8yNiA8LSBmb3JtYXRDKHJlc19jdWFkcmFudGVfbWF4XzI2IC8gcmVzX2N1YWRyYW50ZV9taW5fMjYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAxKQpgYGAKCmBgYHtyIGZpZy1jdWFkcmFudGVzfQpwbG90KHF1YWRyYXRjb3VudChqYW1fMjZfcHBwLCBueCA9IDQsIG55ID0gMyksCiAgICAgbWFpbiA9ICJDb250ZW8gcG9yIGN1YWRyYW50ZXMsIDI2IGRlIHNlcHRpZW1icmUiKQpwb2ludHMoamFtXzI2X3BwcCwgY29sID0gcGFsZXRhWzFdLCBwY2ggPSAxNiwgY2V4ID0gMC42KQpgYGAKCioqRmlndXJhIDQuKiogQ29udGVvIGRlIGV2ZW50b3MgZGUgY29uZ2VzdGnDs24gcG9yIGN1YWRyYW50ZSwganVldmVzIDI2IGRlIHNlcHRpZW1icmUuCgpFbCBlc3RhZMOtc3RpY28gamktY3VhZHJhZG8gZXMgYHIgcmVzX2NoaV8yNmAgZWwganVldmVzIHkgYHIgcmVzX2NoaV8yN2AgZWwgdmllcm5lcywgYW1ib3MgY29uIHZhbG9yIHAgZGUgYHIgcmVzX3BfMjZgIHkgYHIgcmVzX3BfMjdgIHJlc3BlY3RpdmFtZW50ZS4KCioqSW50ZXJwcmV0YWNpw7NuOioqIGxvcyBkb3MgdmFsb3JlcyBwIHF1ZWRhbiBtdXkgcG9yIGRlYmFqbyBkZSAwLjA1LCBhc8OtIHF1ZSBzZSByZWNoYXphIGxhIGFsZWF0b3JpZWRhZCBlc3BhY2lhbCBjb21wbGV0YSBsb3MgZG9zIGTDrWFzLiBFbCBjdWFkcmFudGUgY29uIG3DoXMgZXZlbnRvcyBlbCBqdWV2ZXMgdGllbmUgYHIgcmVzX2N1YWRyYW50ZV9tYXhfMjZgLCBmcmVudGUgYSBgciByZXNfY3VhZHJhbnRlX21pbl8yNmAgZW4gZWwgcXVlIG1lbm9zIHRpZW5lIGVudHJlIGxvcyBxdWUgcmVnaXN0cmFuIGFsIG1lbm9zIHVuIGV2ZW50bywgdW5hIHJhesOzbiBkZSBgciByZXNfY3VhZHJhbnRlX3Jhem9uXzI2YCBhIDEgcXVlIHlhIGFudGljaXBhIHF1ZSBsYSBpbnRlbnNpZGFkIG5vIGVzIGNvbnN0YW50ZSB5IHF1ZSBjb252aWVuZSB1c2FyIGxhIGZ1bmNpw7NuIEsgaW5ob21vZ8OpbmVhIG3DoXMgYWRlbGFudGUuCgojIyBWZWNpbm8gbcOhcyBjZXJjYW5vIChmdW5jacOzbiBHKQoKTGEgcHJ1ZWJhIGRlIGN1YWRyYW50ZXMgbWlyYSBsYSBpbnRlbnNpZGFkIHBvciB6b25hcywgcGVybyBubyBkaWNlIG5hZGEgc29icmUgcXXDqSB0YW4gY2VyY2EgZXN0w6EgY2FkYSBldmVudG8gZGUgc3UgdmVjaW5vIG3DoXMgcHLDs3hpbW8uIExhIGZ1bmNpw7NuIEcgeSBsYSBkaXN0YW5jaWEgbWVkaWEgYWwgdmVjaW5vIG3DoXMgY2VyY2FubyBjb21wbGV0YW4gZXNlIGRpYWduw7NzdGljbyBhIHVuYSBlc2NhbGEgbcOhcyBmaW5hLgoKYGBge3IgZnVuY2lvbi1nfQpubl8yNiA8LSBubmRpc3QoamFtXzI2X3BwcCkgKiBtX3Bvcl9ncmFkbwpubl8yNyA8LSBubmRpc3QoamFtXzI3X3BwcCkgKiBtX3Bvcl9ncmFkbwoKcmVzX25uX21lZGlhXzI2IDwtIGZvcm1hdEMobWVhbihubl8yNiksIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMCkKcmVzX25uX21lZGlhXzI3IDwtIGZvcm1hdEMobWVhbihubl8yNyksIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMCkKcmVzX25uX21lZGlhbmFfMjYgPC0gZm9ybWF0QyhtZWRpYW4obm5fMjYpLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDApCnJlc19ubl9tZWRpYW5hXzI3IDwtIGZvcm1hdEMobWVkaWFuKG5uXzI3KSwgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAwKQoKYXJlYV9tMiA8LSBhcmVhLm93aW4odmVudGFuYSkgKiBtX3Bvcl9ncmFkb14yCmxhbWJkYV8yNiA8LSByZXNfbl9qYW1fMjYgLyBhcmVhX20yCmxhbWJkYV8yNyA8LSByZXNfbl9qYW1fMjcgLyBhcmVhX20yCm5uX2Nzcl8yNiA8LSAxIC8gKDIgKiBzcXJ0KGxhbWJkYV8yNikpCm5uX2Nzcl8yNyA8LSAxIC8gKDIgKiBzcXJ0KGxhbWJkYV8yNykpCgpyZXNfbm5fY3NyXzI2IDwtIGZvcm1hdEMobm5fY3NyXzI2LCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDApCnJlc19ubl9jc3JfMjcgPC0gZm9ybWF0Qyhubl9jc3JfMjcsIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMCkKcmVzX25uX3JhdGlvXzI2IDwtIGZvcm1hdEMoMTAwICogbWVhbihubl8yNikgLyBubl9jc3JfMjYsIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMCkKcmVzX25uX3JhdGlvXzI3IDwtIGZvcm1hdEMoMTAwICogbWVhbihubl8yNykgLyBubl9jc3JfMjcsIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMCkKCmdfMjYgPC0gR2VzdChqYW1fMjZfcHBwLCBjb3JyZWN0aW9uID0gImttIikKZ18yNyA8LSBHZXN0KGphbV8yN19wcHAsIGNvcnJlY3Rpb24gPSAia20iKQpgYGAKCmBgYHtyIGZpZy1mdW5jaW9uLWd9CnBhcihtZnJvdyA9IGMoMSwgMikpCnBsb3QoZ18yNiwgbWFpbiA9ICJHLCAyNiBzZXB0IiwgY29sID0gYyhwYWxldGFbMV0sICJncmV5NTAiKSkKcGxvdChnXzI3LCBtYWluID0gIkcsIDI3IHNlcHQiLCBjb2wgPSBjKHBhbGV0YVsyXSwgImdyZXk1MCIpKQpwYXIobWZyb3cgPSBjKDEsIDEpKQpgYGAKCioqRmlndXJhIDUuKiogRnVuY2nDs24gRyBvYnNlcnZhZGEgY29udHJhIGxhIHRlw7NyaWNhIGRlIFBvaXNzb24sIGp1ZXZlcyB5IHZpZXJuZXMuCgpMYSBkaXN0YW5jaWEgbWVkaWEgYWwgdmVjaW5vIG3DoXMgY2VyY2FubyBlcyBkZSBgciByZXNfbm5fbWVkaWFfMjZgIG1ldHJvcyBlbCBqdWV2ZXMgeSBgciByZXNfbm5fbWVkaWFuYV8yNmAgbWV0cm9zIGRlIG1lZGlhbmE7IGJham8gYWxlYXRvcmllZGFkIGNvbXBsZXRhLCBjb24gYHIgcmVzX25famFtXzI2YCBldmVudG9zIGVuIGVzdGEgdmVudGFuYSwgZXNhIGRpc3RhbmNpYSBlc3BlcmFkYSBzZXLDrWEgZGUgYHIgcmVzX25uX2Nzcl8yNmAgbWV0cm9zLiBFbCB2aWVybmVzIGxhIG1lZGlhIG9ic2VydmFkYSBlcyBkZSBgciByZXNfbm5fbWVkaWFfMjdgIG1ldHJvcyBmcmVudGUgYSBgciByZXNfbm5fY3NyXzI3YCBtZXRyb3MgZXNwZXJhZG9zLgoKKipJbnRlcnByZXRhY2nDs246KiogbGEgZGlzdGFuY2lhIG9ic2VydmFkYSBlcyBhcGVuYXMgZWwgYHIgcmVzX25uX3JhdGlvXzI2YCUgZGUgbGEgZXNwZXJhZGEgZWwganVldmVzIHkgZWwgYHIgcmVzX25uX3JhdGlvXzI3YCUgZWwgdmllcm5lcy4gTG9zIGV2ZW50b3MgZXN0w6FuIG11Y2hvIG3DoXMgY2VyY2EgZW50cmUgc8OtIGRlIGxvIHF1ZSBwcmVkaWNlIGVsIGF6YXIsIHkgZXNhIHByb3BvcmNpw7NuIHNlIG1hbnRpZW5lIHNpbWlsYXIgZW50cmUgbG9zIGRvcyBkw61hcyBhdW5xdWUgZWwgdmllcm5lcyB0ZW5nYSBtw6FzIGV2ZW50b3MsIGxvIHF1ZSBzdWdpZXJlIHF1ZSBlbCBncmFkbyBkZSBhZ3J1cGFtaWVudG8gcmVsYXRpdm8gbm8gY2FtYmlhLCBzb2xvIHN1IHZvbHVtZW4uCgojICoqRnVuY2nDs24gSyoqCgojIyBEaXN0YW5jaWEgZGUgcmVmZXJlbmNpYQoKRWwgcmFkaW8gbcOheGltbyBkZSBhbsOhbGlzaXMgc2UgY2FsY3VsYSBkZXNkZSBsYXMgZGltZW5zaW9uZXMgZGUgbGEgdmVudGFuYSwgbm8gZGVzZGUgbGFzIGRpc3RhbmNpYXMgZW50cmUgcHVudG9zLCBwYXJhIG5vIGNhbGN1bGFyIHBhcmVzIGRlIGRpc3RhbmNpYXMgZGUgZm9ybWEgY3VhZHLDoXRpY2Egc29icmUgY2llbnRvcyBkZSBldmVudG9zLgoKYGBge3IgZGlzdGFuY2lhLXJlZmVyZW5jaWF9CmFuY2hvX3ZlbnRhbmEgPC0gZGlmZih2ZW50YW5hJHhyYW5nZSkKYWx0b192ZW50YW5hICA8LSBkaWZmKHZlbnRhbmEkeXJhbmdlKQpyX21heCA8LSAwLjI1ICogc3FydChhbmNob192ZW50YW5hXjIgKyBhbHRvX3ZlbnRhbmFeMikKcl9zZXEgPC0gc2VxKDAsIHJfbWF4LCBsZW5ndGgub3V0ID0gMTI4KQoKcmVzX3JfbWF4X20gPC0gZm9ybWF0QyhyX21heCAqIG1fcG9yX2dyYWRvLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDApCmBgYAoKRWwgcmFkaW8gbcOheGltbyBlcXVpdmFsZSBhIGByIHJlc19yX21heF9tYCBtZXRyb3MsIHVuYSBjdWFydGEgcGFydGUgZGUgbGEgZGlhZ29uYWwgZGVsIGNvcnJlZG9yLgoKIyMgSyBob21vZ8OpbmVhCgpgYGB7ciBrLWhvbW9nZW5lYX0Ka19ob21vZ18yNiA8LSBLZXN0KGphbV8yNl9wcHAsIHIgPSByX3NlcSwgY29ycmVjdGlvbiA9ICJSaXBsZXkiKQprX2hvbW9nXzI3IDwtIEtlc3QoamFtXzI3X3BwcCwgciA9IHJfc2VxLCBjb3JyZWN0aW9uID0gIlJpcGxleSIpCmBgYAoKYGBge3IgZmlnLWstaG9tb2dlbmVhfQpwYXIobWZyb3cgPSBjKDEsIDIpKQpwbG90KGtfaG9tb2dfMjYsIG1haW4gPSAiSyBob21vZ8OpbmVhLCAyNiBzZXB0IiwgY29sID0gYyhwYWxldGFbMV0sICJncmV5NTAiKSkKcGxvdChrX2hvbW9nXzI3LCBtYWluID0gIksgaG9tb2fDqW5lYSwgMjcgc2VwdCIsIGNvbCA9IGMocGFsZXRhWzJdLCAiZ3JleTUwIikpCnBhcihtZnJvdyA9IGMoMSwgMSkpCmBgYAoKKipGaWd1cmEgNi4qKiBGdW5jacOzbiBLIGhvbW9nw6luZWEgZGUgUmlwbGV5LCBqdWV2ZXMgeSB2aWVybmVzLgoKKipJbnRlcnByZXRhY2nDs246KiogbGEgY3VydmEgb2JzZXJ2YWRhIHF1ZWRhIHBvciBlbmNpbWEgZGUgbGEgZGUgUG9pc3NvbiBlbiBsb3MgZG9zIGTDrWFzLCBsbyBxdWUgZGUgbnVldm8gYXB1bnRhIGEgYWdydXBhbWllbnRvLiBQZXJvIGNvbW8gbGEgcHJ1ZWJhIGRlIGN1YWRyYW50ZXMgeWEgbW9zdHLDsyBxdWUgbGEgaW50ZW5zaWRhZCBjYW1iaWEgcG9yIHpvbmEsIHBhcnRlIGRlIGVzdGEgc2VwYXJhY2nDs24gcHVlZGUgc2VyIHNvbG8gZWwgZWZlY3RvIGRlIHRlbmVyIG3DoXMgZXZlbnRvcyBlbiB1bmFzIHpvbmFzIHF1ZSBlbiBvdHJhcywgbm8gYWdydXBhbWllbnRvIGFkaWNpb25hbCBlbnRyZSBldmVudG9zIGNlcmNhbm9zLgoKIyMgSyBpbmhvbW9nw6luZWEKCkNvbW8gbGEgaW50ZW5zaWRhZCBubyBlcyBjb25zdGFudGUsIHNlIHVzYSBsYSBmdW5jacOzbiBLIGluaG9tb2fDqW5lYSwgcXVlIGNvbXBhcmEgY2FkYSBldmVudG8gY29udHJhIHVuYSBpbnRlbnNpZGFkIGxvY2FsIGVuIGx1Z2FyIGRlIHVuIHByb21lZGlvIMO6bmljbyBwYXJhIHRvZG8gZWwgY29ycmVkb3IuCgpgYGB7ciBrLWluaG9tb2dlbmVhfQprX2luaG9tXzI2IDwtIEtpbmhvbShqYW1fMjZfcHBwLCByID0gcl9zZXEsIGNvcnJlY3Rpb24gPSAiUmlwbGV5IikKa19pbmhvbV8yNyA8LSBLaW5ob20oamFtXzI3X3BwcCwgciA9IHJfc2VxLCBjb3JyZWN0aW9uID0gIlJpcGxleSIpCmBgYAoKYGBge3IgZmlnLWstaW5ob21vZ2VuZWF9CnBhcihtZnJvdyA9IGMoMSwgMikpCnBsb3Qoa19pbmhvbV8yNiwgbWFpbiA9ICJLIGluaG9tb2fDqW5lYSwgMjYgc2VwdCIsIGNvbCA9IGMocGFsZXRhWzFdLCAiZ3JleTUwIikpCnBsb3Qoa19pbmhvbV8yNywgbWFpbiA9ICJLIGluaG9tb2fDqW5lYSwgMjcgc2VwdCIsIGNvbCA9IGMocGFsZXRhWzJdLCAiZ3JleTUwIikpCnBhcihtZnJvdyA9IGMoMSwgMSkpCmBgYAoKKipGaWd1cmEgNy4qKiBGdW5jacOzbiBLIGluaG9tb2fDqW5lYSBkZSBSaXBsZXksIGp1ZXZlcyB5IHZpZXJuZXMuCgoqKkludGVycHJldGFjacOzbjoqKiBhbCBjb250cm9sYXIgcG9yIGxhIGludGVuc2lkYWQgbG9jYWwsIGxhIGN1cnZhIHNlIGFjZXJjYSBtw6FzIGEgbGEgZGUgUG9pc3NvbiBxdWUgZW4gbGEgdmVyc2nDs24gaG9tb2fDqW5lYS4gQnVlbmEgcGFydGUgZGUgbG8gcXVlIHBhcmVjw61hIGFncnVwYW1pZW50byBlcyB2YXJpYWNpw7NuIGRlIGxhIGRlbnNpZGFkIGRlIGZvbmRvIGRlbCBjb3JyZWRvciwgbG8gcXVlIHlhIGhhYsOtYW4gYW50aWNpcGFkbyB0YW50byBlbCBqaS1jdWFkcmFkbyBjb21vIGxhIGZ1bmNpw7NuIEcuCgojICoqRGVuc2lkYWQgS2VybmVsKioKCiMjIFNlbGVjY2nDs24gZGVsIGFuY2hvIGRlIGJhbmRhCgpgYGB7ciBzZWxlY2Npb24tYmFuZHdpZHRofQpid19kaWdnbGVfMjYgPC0gYncuZGlnZ2xlKGphbV8yNl9wcHApCmJ3X2RpZ2dsZV8yNyA8LSBidy5kaWdnbGUoamFtXzI3X3BwcCkKYndfc2NvdHRfMjYgIDwtIGJ3LnNjb3R0KGphbV8yNl9wcHApCmJ3X3Njb3R0XzI3ICA8LSBidy5zY290dChqYW1fMjdfcHBwKQoKdGFibGFfYmFuZHdpZHRoIDwtIGRhdGEuZnJhbWUoCiAgZGlhID0gYygiMjYgc2VwdCIsICIyNyBzZXB0IiksCiAgYndfZGlnZ2xlX20gPSBjKGZvcm1hdEMoYndfZGlnZ2xlXzI2ICogbV9wb3JfZ3JhZG8sIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMCksCiAgICAgICAgICAgICAgICAgICBmb3JtYXRDKGJ3X2RpZ2dsZV8yNyAqIG1fcG9yX2dyYWRvLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDApKSwKICBid19zY290dF94X20gPSBjKGZvcm1hdEMoYndfc2NvdHRfMjZbMV0gKiBtX3Bvcl9ncmFkbywgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAwKSwKICAgICAgICAgICAgICAgICAgICBmb3JtYXRDKGJ3X3Njb3R0XzI3WzFdICogbV9wb3JfZ3JhZG8sIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMCkpLAogIGJ3X3Njb3R0X3lfbSA9IGMoZm9ybWF0Qyhid19zY290dF8yNlsyXSAqIG1fcG9yX2dyYWRvLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDApLAogICAgICAgICAgICAgICAgICAgIGZvcm1hdEMoYndfc2NvdHRfMjdbMl0gKiBtX3Bvcl9ncmFkbywgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAwKSkKKQoKa2FibGUodGFibGFfYmFuZHdpZHRoLAogICAgICBjb2wubmFtZXMgPSBjKCJEw61hIiwgImJ3LmRpZ2dsZSAobSkiLCAiYncuc2NvdHQgZWplIFggKG0pIiwgImJ3LnNjb3R0IGVqZSBZIChtKSIpLAogICAgICBjYXB0aW9uID0gIkNvbXBhcmFjacOzbiBkZSBhbmNob3MgZGUgYmFuZGEiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmb250X3NpemUgPSAxMikKCnJlc19id18yNl9tIDwtIGZvcm1hdEMoYndfZGlnZ2xlXzI2ICogbV9wb3JfZ3JhZG8sIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMCkKcmVzX2J3XzI3X20gPC0gZm9ybWF0Qyhid19kaWdnbGVfMjcgKiBtX3Bvcl9ncmFkbywgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAwKQpgYGAKCmBgYHtyIGJhbmR3aWR0aC1jb21wYXJhY2lvbn0KcmVzX2J3X3Njb3R0XzI2X20gPC0gZm9ybWF0Qyhid19zY290dF8yNlsxXSAqIG1fcG9yX2dyYWRvLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDApCnJlc19id19kaWZlcmVuY2lhX3BjdF8yNiA8LSBmb3JtYXRDKDEwMCAqIChid19zY290dF8yNlsxXSAtIGJ3X2RpZ2dsZV8yNikgLyBid19kaWdnbGVfMjYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDApCmBgYAoKKipJbnRlcnByZXRhY2nDs246KiogYGJ3LnNjb3R0YCBkYSBgciByZXNfYndfc2NvdHRfMjZfbWAgbSBlbCBqdWV2ZXMsIHVuIGByIHJlc19id19kaWZlcmVuY2lhX3BjdF8yNmAlIG3DoXMgYW5jaG8gcXVlIGByIHJlc19id18yNl9tYCBtIGRlIGBidy5kaWdnbGVgLiBFc2EgZGlmZXJlbmNpYSBlcyBjb25zaXN0ZW50ZSBjb24gcXVlIGBidy5zY290dGAgYXN1bWUgdW5hIGRpc3BlcnNpw7NuIGNlcmNhbmEgYSBsYSBub3JtYWwgYml2YXJpYWRhLCBtaWVudHJhcyBgYncuZGlnZ2xlYCBhanVzdGEgcG9yIHZhbGlkYWNpw7NuIGNydXphZGEgeSByZXNwb25kZSBtZWpvciBhIHVuIHBhdHLDs24gYWdydXBhZG8gc29icmUgdW5hIHbDrWEgYW5nb3N0YSBjb21vIGVzdGUgY29ycmVkb3IsIHBvciBsbyBxdWUgc2UgdXNhIHBhcmEgbG9zIG1hcGFzIGRlIGludGVuc2lkYWQgKGByIHJlc19id18yNl9tYCBtIGVsIGp1ZXZlcywgYHIgcmVzX2J3XzI3X21gIG0gZWwgdmllcm5lcykuCgojIyBNYXBhcyBkZSBpbnRlbnNpZGFkCgpgYGB7ciBjb252ZXJ0aXItcmFzdGVyfQojIERlZmluaWRhcyBhcXXDrSBwb3JxdWUgYSBwYXJ0aXIgZGUgZXN0ZSBwdW50byBjYWRhIG1hcGEgZGUgaW50ZW5zaWRhZAojIHNlIG5lY2VzaXRhIHRhbnRvIGVuIHN1IHByb3BpYSBmaWd1cmEgY29tbywgbcOhcyBhZGVsYW50ZSwgZW4gbGEKIyBkaWZlcmVuY2lhIGVudHJlIGxvcyBkb3MgZMOtYXMuCmNvbnZlcnRpcl9hX3Jhc3RlciA8LSBmdW5jdGlvbihpbWFnZW4pIHsKICBtYXRyaXogPC0gYXMubWF0cml4KGltYWdlbikKICBjYXBhIDwtIHJhc3Rlcjo6cmFzdGVyKG1hdHJpeikKICByYXN0ZXI6OmV4dGVudChjYXBhKSA8LSByYXN0ZXI6OmV4dGVudChpbWFnZW4keHJhbmdlWzFdLCBpbWFnZW4keHJhbmdlWzJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbWFnZW4keXJhbmdlWzFdLCBpbWFnZW4keXJhbmdlWzJdKQogIGNhcGEgPC0gcmFzdGVyOjpmbGlwKGNhcGEsIGRpcmVjdGlvbiA9ICJ5IikKICByYXN0ZXI6OmNycyhjYXBhKSA8LSAiK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQiCiAgY2FwYQp9Cgpub3JtYWxpemFyIDwtIGZ1bmN0aW9uKGNhcGEpIHsKICAoY2FwYSAtIHJhc3Rlcjo6Y2VsbFN0YXRzKGNhcGEsICJtaW4iKSkgLwogICAgKHJhc3Rlcjo6Y2VsbFN0YXRzKGNhcGEsICJtYXgiKSAtIHJhc3Rlcjo6Y2VsbFN0YXRzKGNhcGEsICJtaW4iKSkKfQpgYGAKCmBgYHtyIGRlbnNpZGFkLTI2fQppbnRlbnNpZGFkXzI2IDwtIGRlbnNpdHkucHBwKGphbV8yNl9wcHAsIHNpZ21hID0gYndfZGlnZ2xlXzI2LCBlZGdlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlteXggPSBjKDEyOCwgMTI4KSkKcmFzdGVyXzI2IDwtIG5vcm1hbGl6YXIoY29udmVydGlyX2FfcmFzdGVyKGludGVuc2lkYWRfMjYpKQpyYXN0ZXJfMjZfdmlzIDwtIGVubWFzY2FyYXJfYmFqb191bWJyYWwocmFzdGVyXzI2KQpgYGAKCmBgYHtyIGZpZy1kZW5zaWRhZC0yNn0KdG1fYmFzZW1hcChtYXBhX2Jhc2UpICsKICB0bV9zaGFwZShyYXN0ZXJfMjZfdmlzKSArCiAgdG1fcmFzdGVyKHBhbGV0dGUgPSBjKHBhbGV0YVs1XSwgcGFsZXRhWzFdKSwgc3R5bGUgPSAiY29udCIsIGFscGhhID0gMC44NSwKICAgICAgICAgICAgdGl0bGUgPSAiSW50ZW5zaWRhZCBub3JtYWxpemFkYSIpICsKICB0bV9zaGFwZShqYW1fMjZfc2YpICsKICB0bV9kb3RzKGNvbCA9ICJibGFjayIsIHNpemUgPSAwLjAzKSArCiAgdG1fbGF5b3V0KHRpdGxlID0gIkRlbnNpZGFkIGRlIGNvbmdlc3Rpw7NuLCAyNiBkZSBzZXB0aWVtYnJlIikKYGBgCgoqKkZpZ3VyYSA4LioqIERlbnNpZGFkIGtlcm5lbCBkZSBldmVudG9zIGRlIGNvbmdlc3Rpw7NuIHNvYnJlIGVsIG1hcGEgZGUgQm9nb3TDoSwganVldmVzIDI2IGRlIHNlcHRpZW1icmUuCgpgYGB7ciBkZW5zaWRhZC0yN30KaW50ZW5zaWRhZF8yNyA8LSBkZW5zaXR5LnBwcChqYW1fMjdfcHBwLCBzaWdtYSA9IGJ3X2RpZ2dsZV8yNywgZWRnZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpbXl4ID0gYygxMjgsIDEyOCkpCnJhc3Rlcl8yNyA8LSBub3JtYWxpemFyKGNvbnZlcnRpcl9hX3Jhc3RlcihpbnRlbnNpZGFkXzI3KSkKcmFzdGVyXzI3X3ZpcyA8LSBlbm1hc2NhcmFyX2Jham9fdW1icmFsKHJhc3Rlcl8yNykKYGBgCgpgYGB7ciBmaWctZGVuc2lkYWQtMjd9CnRtX2Jhc2VtYXAobWFwYV9iYXNlKSArCiAgdG1fc2hhcGUocmFzdGVyXzI3X3ZpcykgKwogIHRtX3Jhc3RlcihwYWxldHRlID0gYyhwYWxldGFbNV0sIHBhbGV0YVsyXSksIHN0eWxlID0gImNvbnQiLCBhbHBoYSA9IDAuODUsCiAgICAgICAgICAgIHRpdGxlID0gIkludGVuc2lkYWQgbm9ybWFsaXphZGEiKSArCiAgdG1fc2hhcGUoamFtXzI3X3NmKSArCiAgdG1fZG90cyhjb2wgPSAiYmxhY2siLCBzaXplID0gMC4wMykgKwogIHRtX2xheW91dCh0aXRsZSA9ICJEZW5zaWRhZCBkZSBjb25nZXN0acOzbiwgMjcgZGUgc2VwdGllbWJyZSIpCmBgYAoKKipGaWd1cmEgOS4qKiBEZW5zaWRhZCBrZXJuZWwgZGUgZXZlbnRvcyBkZSBjb25nZXN0acOzbiBzb2JyZSBlbCBtYXBhIGRlIEJvZ290w6EsIHZpZXJuZXMgMjcgZGUgc2VwdGllbWJyZS4KCioqSW50ZXJwcmV0YWNpw7NuOioqIGVuIGxhcyBkb3MgZmlndXJhcyBsYSBmcmFuamEgZGUgbWF5b3IgaW50ZW5zaWRhZCBjb3JyZSBzb2JyZSBsYSB2w61hIHF1ZSBjcnV6YSBlbCBjZW50cm8gZGUgQ2FqaWPDoSB5IHNpZ3VlIGhhY2lhIENhbGFob3JyYSwgbm8gc2UgcmVwYXJ0ZSBwb3IgZWwgcmVzdG8gZGVsIHJlY3TDoW5ndWxvIGRlIGxhIHZlbnRhbmEuIEVzbyBlcyBqdXN0YW1lbnRlIGxvIHF1ZSBlbCBqaS1jdWFkcmFkbyB5IGxhIGZ1bmNpw7NuIEsgeWEgaGFiw61hbiBzZcOxYWxhZG8gY29uIG7Dum1lcm9zOiBlbCBwcm9ibGVtYSBubyBlcyBsYSB6b25hIGVuIGdlbmVyYWwsIGVzIGVzZSB0cmFtbyB2aWFsIHB1bnR1YWwsIHkgcG9yIGVzbyBhbCBlbm1hc2NhcmFyIGxhcyBjZWxkYXMgZGUgYmFqYSBkZW5zaWRhZCBlbCBtYXBhIGJhc2UgcXVlZGEgdmlzaWJsZSBlbiBjYXNpIHRvZGEgbGEgdmVudGFuYSBzYWx2byBhaMOtLgoKPiBOb3RhIGRlIHZlcmlmaWNhY2nDs246IGxhIG9yaWVudGFjacOzbiBkZWwgcmFzdGVyIGRlcGVuZGUgZGUgY8OzbW8gYHNwYXRzdGF0YCBhbG1hY2VuYSBsYSBtYXRyaXogZGUgaW50ZW5zaWRhZC4gU2kgYWwgdmVyIGxhIEZpZ3VyYSA4IGVsIMOhcmVhIGRlIG1heW9yIGludGVuc2lkYWQgbm8gY29pbmNpZGUgY29uIGxhIG51YmUgZGUgcHVudG9zIG5lZ3JvcywgcmV0aXJhciBsYSBsw61uZWEgYGZsaXAoKWAgZGVudHJvIGRlIGBjb252ZXJ0aXJfYV9yYXN0ZXIoKWAgZW4gbHVnYXIgZGUgYXBsaWNhcmxhLgoKIyAqKkNvbXBhcmFjacOzbiAyNi0yNyoqCgojIyBOb3JtYWxpemFjacOzbiB5IGRpZmVyZW5jaWEKCkNhZGEgbWFwYSBkZSBpbnRlbnNpZGFkIHlhIHNlIG5vcm1hbGl6w7MgZW50cmUgMCB5IDEgZGUgZm9ybWEgaW5kZXBlbmRpZW50ZSBhbCBjb25zdHJ1aXIgbGFzIEZpZ3VyYXMgOCB5IDksIHBhcmEgcXVlIGxhIGNvbXBhcmFjacOzbiBubyBzZSB2ZWEgYWZlY3RhZGEgcG9yIHRlbmVyIGByIHJlc19uX2phbV8yNmAgZXZlbnRvcyB1biBkw61hIHkgYHIgcmVzX25famFtXzI3YCBlbCBvdHJvLgoKIyMgRGlmZXJlbmNpYSBjZWxkYSBhIGNlbGRhCgpgYGB7ciBkaWZlcmVuY2lhLXJhc3Rlcn0KZGlmZXJlbmNpYV9yYXN0ZXIgPC0gcmFzdGVyXzI3IC0gcmFzdGVyXzI2CgpyZXNfbWF4X2V4cGFuc2lvbiAgIDwtIGZvcm1hdEMocmFzdGVyOjpjZWxsU3RhdHMoZGlmZXJlbmNpYV9yYXN0ZXIsICJtYXgiKSwgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAyKQpyZXNfbWF4X2NvbnRyYWNjaW9uIDwtIGZvcm1hdEMocmFzdGVyOjpjZWxsU3RhdHMoZGlmZXJlbmNpYV9yYXN0ZXIsICJtaW4iKSwgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAyKQoKIyBQYXJhIGVsIG1hcGEsIHNlIG9jdWx0YSBlbCBjYW1iaW8gY2FzaSBudWxvICh8ZGlmZXJlbmNpYXwgPCAwLjA1KSB5IHNlCiMgZGVqYSB2ZXIgc29sbyBkw7NuZGUgbGEgY29uZ2VzdGnDs24gcmVsYXRpdmEgc3ViacOzIG8gYmFqw7MgZGUgZm9ybWEKIyBhcHJlY2lhYmxlIGVudHJlIGxvcyBkb3MgZMOtYXMuCmRpZmVyZW5jaWFfcmFzdGVyX3ZpcyA8LSBkaWZlcmVuY2lhX3Jhc3RlcgpkaWZlcmVuY2lhX3Jhc3Rlcl92aXNbYWJzKGRpZmVyZW5jaWFfcmFzdGVyKSA8IDAuMDVdIDwtIE5BCmBgYAoKRWwgY2FtYmlvIGNlbGRhIGEgY2VsZGEgdmEgZGUgYHIgcmVzX21heF9jb250cmFjY2lvbmAgKG1heW9yIGNvbnRyYWNjacOzbiByZWxhdGl2YSkgYSBgciByZXNfbWF4X2V4cGFuc2lvbmAgKG1heW9yIGV4cGFuc2nDs24gcmVsYXRpdmEpIGVuIGxhIGVzY2FsYSBub3JtYWxpemFkYS4KCioqSW50ZXJwcmV0YWNpw7NuOioqIGxvcyB2YWxvcmVzIHBvc2l0aXZvcyBtYXJjYW4gdHJhbW9zIGRvbmRlIGxhIGNvbmdlc3Rpw7NuIHJlbGF0aXZhIGNyZWNpw7MgZWwgdmllcm5lcyBmcmVudGUgYWwganVldmVzLCB5IGxvcyBuZWdhdGl2b3MgZG9uZGUgYmFqw7MuIExvcyBwdW50b3MgcXVlIGFwYXJlY2VuIGVuIGFtYm9zIGTDrWFzIGJham8gdW5hIHkgb3RyYSBpbnRlbnNpZGFkLCBzaW4gZGVwZW5kZXIgZGVsIGNhbWJpbyBkZSBjYWxlbmRhcmlvLCBzb24gbG9zIHF1ZSBtw6FzIHN1Z2llcmVuIHVuIGN1ZWxsbyBkZSBib3RlbGxhIGVzdHJ1Y3R1cmFsOyBsb3MgcXVlIHNvbG8gYXBhcmVjZW4gZWwgdmllcm5lcyBlc3TDoW4gbcOhcyBsaWdhZG9zIGFsIGVmZWN0byBkZSBmaW4gZGUgc2VtYW5hIHF1ZSBhbCBkaXNlw7FvIGRlIGxhIHbDrWEuCgojIyBNYXBhcyBmaW5hbGVzCgpgYGB7ciBtYXBhLTI2fQp0bV9iYXNlbWFwKG1hcGFfYmFzZSkgKwogIHRtX3NoYXBlKHJhc3Rlcl8yNl92aXMpICsKICB0bV9yYXN0ZXIocGFsZXR0ZSA9IGMocGFsZXRhWzVdLCBwYWxldGFbMV0pLCBzdHlsZSA9ICJjb250IiwgYWxwaGEgPSAwLjg1LAogICAgICAgICAgICB0aXRsZSA9ICJJbnRlbnNpZGFkIG5vcm1hbGl6YWRhIikgKwogIHRtX2xheW91dCh0aXRsZSA9ICJDb25nZXN0acOzbiwganVldmVzIDI2IikKYGBgCgoqKkZpZ3VyYSAxMC4qKiBNYXBhIDEsIGludGVuc2lkYWQgbm9ybWFsaXphZGEgZGUgY29uZ2VzdGnDs24gc29icmUgZWwgbWFwYSBkZSBCb2dvdMOhLCBqdWV2ZXMgMjYgZGUgc2VwdGllbWJyZS4KCmBgYHtyIG1hcGEtMjd9CnRtX2Jhc2VtYXAobWFwYV9iYXNlKSArCiAgdG1fc2hhcGUocmFzdGVyXzI3X3ZpcykgKwogIHRtX3Jhc3RlcihwYWxldHRlID0gYyhwYWxldGFbNV0sIHBhbGV0YVsyXSksIHN0eWxlID0gImNvbnQiLCBhbHBoYSA9IDAuODUsCiAgICAgICAgICAgIHRpdGxlID0gIkludGVuc2lkYWQgbm9ybWFsaXphZGEiKSArCiAgdG1fbGF5b3V0KHRpdGxlID0gIkNvbmdlc3Rpw7NuLCB2aWVybmVzIDI3IikKYGBgCgoqKkZpZ3VyYSAxMS4qKiBNYXBhIDIsIGludGVuc2lkYWQgbm9ybWFsaXphZGEgZGUgY29uZ2VzdGnDs24gc29icmUgZWwgbWFwYSBkZSBCb2dvdMOhLCB2aWVybmVzIDI3IGRlIHNlcHRpZW1icmUuCgpgYGB7ciBtYXBhLWRpZmVyZW5jaWF9CnRtX2Jhc2VtYXAobWFwYV9iYXNlKSArCiAgdG1fc2hhcGUoZGlmZXJlbmNpYV9yYXN0ZXJfdmlzKSArCiAgdG1fcmFzdGVyKHBhbGV0dGUgPSBjKHBhbGV0YVsxXSwgIndoaXRlIiwgcGFsZXRhWzJdKSwgc3R5bGUgPSAiY29udCIsCiAgICAgICAgICAgIG1pZHBvaW50ID0gMCwgYWxwaGEgPSAwLjg1LCB0aXRsZSA9ICJDYW1iaW8gcmVsYXRpdm8iKSArCiAgdG1fbGF5b3V0KHRpdGxlID0gIkRpZmVyZW5jaWEgMjcgbWVub3MgMjYgZGUgc2VwdGllbWJyZSIpCmBgYAoKKipGaWd1cmEgMTIuKiogTWFwYSAzLCBkaWZlcmVuY2lhIGNlbGRhIGEgY2VsZGEgc29icmUgZWwgbWFwYSBkZSBCb2dvdMOhLCBlbnRyZSBlbCB2aWVybmVzIHkgZWwganVldmVzLgoKIyAqKk90cm9zIEV2ZW50b3MqKgoKQ2llcnJlcyBkZSB2w61hLCBwZWxpZ3JvcyB5IGFjY2lkZW50ZXMgc2UgcmV2aXNhbiBkZSBmb3JtYSBleHBsb3JhdG9yaWEuIENvbiBjaWVycmVzIGRlIHbDrWEgeSBhY2NpZGVudGVzIGVsIG7Dum1lcm8gZGUgZXZlbnRvcyDDum5pY29zIHBvciBkw61hIGVzIHRhbiBiYWpvIHF1ZSBzb2xvIHNlIHViaWNhbiBlbiBlbCBtYXBhLCBzaW4gcHJ1ZWJhIGVzdGFkw61zdGljYS4KCmBgYHtyIG90cm9zLWV2ZW50b3MtY29uc3RydWlyfQpjb25zdHJ1aXJfcHBwX3RpcG8gPC0gZnVuY3Rpb24odGlwbywgZGlhKSB7CiAgc3ViIDwtIGV2ZW50b3NfdW5pY29zICU+JSBmaWx0ZXIodHlwZSA9PSB0aXBvLCBmZWNoYSA9PSBhcy5EYXRlKGRpYSkpCiAgaWYgKG5yb3coc3ViKSA9PSAwKSByZXR1cm4oTlVMTCkKICB1bmlxdWUocHBwKHggPSBzdWIkbG9uZ2l0dWQsIHkgPSBzdWIkbGF0aXR1ZCwgd2luZG93ID0gdmVudGFuYSkpCn0KCmhhemFyZF8yNiA8LSBjb25zdHJ1aXJfcHBwX3RpcG8oIkhBWkFSRCIsICIyMDI0LTA5LTI2IikKaGF6YXJkXzI3IDwtIGNvbnN0cnVpcl9wcHBfdGlwbygiSEFaQVJEIiwgIjIwMjQtMDktMjciKQpjZXJyYWRvXzI2IDwtIGNvbnN0cnVpcl9wcHBfdGlwbygiUk9BRF9DTE9TRUQiLCAiMjAyNC0wOS0yNiIpCmNlcnJhZG9fMjcgPC0gY29uc3RydWlyX3BwcF90aXBvKCJST0FEX0NMT1NFRCIsICIyMDI0LTA5LTI3IikKYWNjaWRlbnRlXzI2IDwtIGNvbnN0cnVpcl9wcHBfdGlwbygiQUNDSURFTlQiLCAiMjAyNC0wOS0yNiIpCmFjY2lkZW50ZV8yNyA8LSBjb25zdHJ1aXJfcHBwX3RpcG8oIkFDQ0lERU5UIiwgIjIwMjQtMDktMjciKQoKcmVzX25faGF6YXJkXzI2IDwtIG5wb2ludHMoaGF6YXJkXzI2KQpyZXNfbl9oYXphcmRfMjcgPC0gbnBvaW50cyhoYXphcmRfMjcpCnJlc19uX2NlcnJhZG9fMjYgPC0gbnBvaW50cyhjZXJyYWRvXzI2KQpyZXNfbl9jZXJyYWRvXzI3IDwtIG5wb2ludHMoY2VycmFkb18yNykKcmVzX25fYWNjaWRlbnRlXzI2IDwtIG5wb2ludHMoYWNjaWRlbnRlXzI2KQpyZXNfbl9hY2NpZGVudGVfMjcgPC0gbnBvaW50cyhhY2NpZGVudGVfMjcpCmBgYAoKUGVsaWdyb3MgZW4gdsOtYSBwYXNhcm9uIGRlIGByIHJlc19uX2hhemFyZF8yNmAgZXZlbnRvcyBlbCBqdWV2ZXMgYSBgciByZXNfbl9oYXphcmRfMjdgIGVsIHZpZXJuZXMsIHVuYSBtdWVzdHJhIHN1ZmljaWVudGUgcGFyYSB1bmEgcHJ1ZWJhIENTUiBvcmllbnRhdGl2YS4gQ2llcnJlcyBkZSB2w61hIChgciByZXNfbl9jZXJyYWRvXzI2YCB5IGByIHJlc19uX2NlcnJhZG9fMjdgKSB5IGFjY2lkZW50ZXMgKGByIHJlc19uX2FjY2lkZW50ZV8yNmAgeSBgciByZXNfbl9hY2NpZGVudGVfMjdgKSBzb24gZGVtYXNpYWRvIGVzY2Fzb3MgcGFyYSBjdWFscXVpZXIgcHJ1ZWJhIGZvcm1hbC4KCmBgYHtyIG90cm9zLWV2ZW50b3MtcHJ1ZWJhfQpwcnVlYmFfaGF6YXJkXzI2IDwtIHF1YWRyYXQudGVzdChoYXphcmRfMjYsIG54ID0gMiwgbnkgPSAyKQpwcnVlYmFfaGF6YXJkXzI3IDwtIHF1YWRyYXQudGVzdChoYXphcmRfMjcsIG54ID0gMiwgbnkgPSAyKQoKcmVzX3BfaGF6YXJkXzI2IDwtIGZvcm1hdEMocHJ1ZWJhX2hhemFyZF8yNiRwLnZhbHVlLCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDQpCnJlc19wX2hhemFyZF8yNyA8LSBmb3JtYXRDKHBydWViYV9oYXphcmRfMjckcC52YWx1ZSwgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSA0KQpgYGAKCkVsIHZhbG9yIHAgZGUgbGEgcHJ1ZWJhIGppLWN1YWRyYWRvIHBhcmEgcGVsaWdyb3MgZW4gdsOtYSBlcyBgciByZXNfcF9oYXphcmRfMjZgIGVsIGp1ZXZlcyB5IGByIHJlc19wX2hhemFyZF8yN2AgZWwgdmllcm5lcy4KCioqSW50ZXJwcmV0YWNpw7NuOioqIGNvbiBtdWVzdHJhcyBkZSBgciByZXNfbl9oYXphcmRfMjZgIHkgYHIgcmVzX25faGF6YXJkXzI3YCBldmVudG9zLCBlc3RhIHBydWViYSBlcyBvcmllbnRhdGl2YS4gU2lydmUgcGFyYSBubyBkZXNjYXJ0YXIgbGEgY2F0ZWdvcsOtYSBkZSBlbnRyYWRhLCBubyBwYXJhIHNvc3RlbmVyIHVuYSBjb25jbHVzacOzbiBlc3RhZMOtc3RpY2EgZmlybWUgc29icmUgcGVsaWdyb3MgZW4gdsOtYS4KCmBgYHtyIG90cm9zLWV2ZW50b3Mtc2Z9CmNvbnN0cnVpcl9zZl9ldGlxdWV0YWRvIDwtIGZ1bmN0aW9uKG9iamV0b19wcHAsIGV0aXF1ZXRhKSB7CiAgaWYgKGlzLm51bGwob2JqZXRvX3BwcCkgfHwgbnBvaW50cyhvYmpldG9fcHBwKSA9PSAwKSByZXR1cm4oTlVMTCkKICBzZl9vYmogPC0gcHBwX2Ffc2Yob2JqZXRvX3BwcCkKICBzZl9vYmokZXZlbnRvIDwtIGV0aXF1ZXRhCiAgc2Zfb2JqCn0KCm90cm9zX2V2ZW50b3NfbGlzdGEgPC0gbGlzdCgKICBjb25zdHJ1aXJfc2ZfZXRpcXVldGFkbyhjZXJyYWRvXzI2LCAiQ2llcnJlIGRlIHbDrWEsIDI2IHNlcHQiKSwKICBjb25zdHJ1aXJfc2ZfZXRpcXVldGFkbyhjZXJyYWRvXzI3LCAiQ2llcnJlIGRlIHbDrWEsIDI3IHNlcHQiKSwKICBjb25zdHJ1aXJfc2ZfZXRpcXVldGFkbyhhY2NpZGVudGVfMjYsICJBY2NpZGVudGUsIDI2IHNlcHQiKSwKICBjb25zdHJ1aXJfc2ZfZXRpcXVldGFkbyhhY2NpZGVudGVfMjcsICJBY2NpZGVudGUsIDI3IHNlcHQiKQopCm90cm9zX2V2ZW50b3NfbGlzdGEgPC0gRmlsdGVyKE5lZ2F0ZShpcy5udWxsKSwgb3Ryb3NfZXZlbnRvc19saXN0YSkKb3Ryb3NfZXZlbnRvc19zZiA8LSBkby5jYWxsKHJiaW5kLCBvdHJvc19ldmVudG9zX2xpc3RhKQpgYGAKCmBgYHtyIGZpZy1vdHJvcy1ldmVudG9zfQojIEVsIG1vZG8gInZpZXciIGRlIHRtYXAgZGlidWphIHNpZW1wcmUgbWFyY2Fkb3JlcyBjaXJjdWxhcmVzIHkgbm8KIyBzb3BvcnRhIGNvbWJpbmFyIGNvbG9yIHkgZm9ybWEgZW4gdW5hIHNvbGEgY2FwYSBkZSBwdW50b3M7IHBvciBlc28KIyB0aXBvIHkgZMOtYSBzZSBjb21iaW5hbiBlbiB1bmEgc29sYSBjYXRlZ29yw61hIGRlIGNvbG9yIGVuIHZleiBkZQojIHVzYXIgZG9zIGVzdMOpdGljYXMgKGNvbG9yIHkgZm9ybWEpIGFsIG1pc21vIHRpZW1wby4KdG1fYmFzZW1hcChtYXBhX2Jhc2UpICsKICB0bV9zaGFwZShvdHJvc19ldmVudG9zX3NmKSArCiAgdG1fZG90cyhjb2wgPSAiZXZlbnRvIiwKICAgICAgICAgIHBhbGV0dGUgPSBjKCJDaWVycmUgZGUgdsOtYSwgMjYgc2VwdCIgPSBwYWxldGFbM10sCiAgICAgICAgICAgICAgICAgICAgICAiQ2llcnJlIGRlIHbDrWEsIDI3IHNlcHQiID0gcGFsZXRhWzRdLAogICAgICAgICAgICAgICAgICAgICAgIkFjY2lkZW50ZSwgMjYgc2VwdCIgPSBwYWxldGFbMV0sCiAgICAgICAgICAgICAgICAgICAgICAiQWNjaWRlbnRlLCAyNyBzZXB0IiA9IHBhbGV0YVsyXSksCiAgICAgICAgICBzaXplID0gMC4wOCwgdGl0bGUgPSAiRXZlbnRvIHkgZMOtYSIpICsKICB0bV9sYXlvdXQodGl0bGUgPSAiQ2llcnJlcyBkZSB2w61hIHkgYWNjaWRlbnRlcywgMjYtMjcgZGUgc2VwdGllbWJyZSIpCmBgYAoKKipGaWd1cmEgMTMuKiogQ2llcnJlcyBkZSB2w61hIHkgYWNjaWRlbnRlcyBzb2JyZSBlbCBtYXBhIGRlIEJvZ290w6EsIGp1ZXZlcyB5IHZpZXJuZXMsIHNpbiBwcnVlYmEgZXN0YWTDrXN0aWNhIHBvciB0YW1hw7FvIGRlIG11ZXN0cmEuCgojICoqQ29uY2x1c2lvbmVzKioKCiMjIEhhbGxhemdvcyBwcmluY2lwYWxlcwoKRWwgamktY3VhZHJhZG8sIGxhIGRpc3RhbmNpYSBhbCB2ZWNpbm8gbcOhcyBjZXJjYW5vIHkgbGEgZnVuY2nDs24gSyBjb2luY2lkZW4gZW4gbG8gbWlzbW8gZGVzZGUgdHJlcyDDoW5ndWxvcyBkaXN0aW50b3M6IGVsIGNvcnJlZG9yIG5vIHJlcGFydGUgbGEgY29uZ2VzdGnDs24gYWwgYXphciwgc2UgY29uY2VudHJhIHNvYnJlIGxhIHbDrWEgcXVlIGNydXphIENhamljw6EgeSBzaWd1ZSBoYWNpYSBDYWxhaG9ycmEsIGVuIHRyYW1vcyBxdWUgc2UgcmVwaXRlbiBlbCBqdWV2ZXMgeSBlbCB2aWVybmVzLiBFbCB2aWVybmVzIGVsIG7Dum1lcm8gZGUgZXZlbnRvcyBzdWJlIGByIHJlc19pbmNyZW1lbnRvX2phbWAlIHkgZWwgcGljbyBob3JhcmlvIHNlIGNvcnJlIGRlIGxhcyBgciByZXNfaG9yYV9waWNvXzI2YDowMCBhIGxhcyBgciByZXNfaG9yYV9waWNvXzI3YDowMCwgdW4gcGF0csOzbiBjb21wYXRpYmxlIGNvbiBsYSBzYWxpZGEgaGFjaWEgQ2FqaWPDoSB5IFRvY2FuY2lww6EgYW50ZXMgZGVsIGZpbiBkZSBzZW1hbmEuCgojIyBSZWNvbWVuZGFjaW9uZXMgcGFyYSBwbGFuaWZpY2FjacOzbiB1cmJhbmEKCkVsIGF1bWVudG8gZGUgYHIgcmVzX2luY3JlbWVudG9famFtYCUgZW4gZXZlbnRvcyBkZWwgdmllcm5lcywgY29uY2VudHJhZG8gZW4gbGEgZnJhbmphIGRlIGxhIG5vY2hlLCBqdXN0aWZpY2EgbWVkaWRhcyBkZSB0csOhZmljbyBhY290YWRhcyBhIGVzYSB2ZW50YW5hIGhvcmFyaWEsIGNvbW8gYWdlbnRlcyBvIHNlbWFmb3JpemFjacOzbiBhZGFwdGF0aXZhIGVudHJlIGxhcyBgciByZXNfaG9yYV9waWNvXzI3YDowMCB5IGVsIGNpZXJyZSBkZSBsYSBub2NoZSwgc2luIG5lY2VzaWRhZCBkZSBtYW50ZW5lcmxhcyBlbCByZXN0byBkZSBsYSBzZW1hbmEuIExvcyB0cmFtb3MgcXVlIHNlIHJlcGl0ZW4gZW4gYW1iYXMgam9ybmFkYXMsIHZpc2libGVzIGRvbmRlIGNvaW5jaWRlbiBsYXMgRmlndXJhcyAxMCB5IDExIGFudGVzIGRlIGNhbGN1bGFyIGxhIGRpZmVyZW5jaWEsIHNvbiBsb3MgcXVlIGp1c3RpZmljYW4gdW5hIGludGVydmVuY2nDs24gcGVybWFuZW50ZSBkZSBpbmZyYWVzdHJ1Y3R1cmEsIHBvcnF1ZSBzdSBjb25nZXN0acOzbiBubyBkZXBlbmRlIGRlbCBkw61hLgoKIyMgTGltaXRhY2lvbmVzCgpFbCBhbsOhbGlzaXMgY3VicmUgZG9zIGTDrWFzIHkgdW4gY29ycmVkb3IgZXNwZWPDrWZpY28sIHBvciBsbyBxdWUgbm8gZGViZSBleHRlbmRlcnNlIGEgdG9kYSBCb2dvdMOhIHNpbiBtw6FzIGV2aWRlbmNpYS4gQ2llcnJlcyBkZSB2w61hIHkgYWNjaWRlbnRlcyBuZWNlc2l0YW4gdW5hIHZlbnRhbmEgZGUgdGllbXBvIG3DoXMgbGFyZ2EgYW50ZXMgZGUgcG9kZXIgYW5hbGl6YXJzZSBjb24gZWwgbWlzbW8gcmlnb3IgZXN0YWTDrXN0aWNvIHF1ZSBsYSBjb25nZXN0acOzbi4K