1 Introducción y objetivo

¿Dónde empiezan y terminan los viajes registrados dentro de Cali y cómo cambia su distribución según el medio de transporte?

Este trabajo analiza la distribución de los orígenes y destinos de los desplazamientos registrados dentro de Cali, tanto de forma general como por medio de transporte. El análisis integra la preparación de la encuesta, la verificación de calidad de los datos, la construcción de mapas interactivos, una matriz origen–destino y un balance territorial por comuna.

Los datos de los desplazamientos provienen del archivo EncuestaOrigenDestino.xlsx, leído en la hoja Hoja1. Para ubicar los resultados se utiliza la cartografía Comunas.shp, que contiene los límites de las comunas de Cali. Son dos insumos diferentes: el Excel describe los registros de movilidad y la capa geográfica permite representarlos en el territorio.

Los registros con fecha válida abarcan del 04/05/2015 al 09/06/2015, según la columna fechas del Excel. Este intervalo corresponde a las fechas observadas en la base.

El objetivo es reconocer dónde se concentran las salidas y llegadas, comparar los modos de transporte y cuantificar las diferencias entre las comunas, sus conexiones y su balance de salidas y llegadas. Antes de interpretar los patrones, revisamos qué registros cuentan con información suficiente para ubicarlos.

La unidad de análisis es un registro de desplazamiento. Los resultados no representan personas únicas ni todos los viajes de Cali: para hacer esa estimación se necesitan factores de expansión y conocer el diseño muestral.

2 Preparación e importación

2.1 ¿Con qué vamos a trabajar?

Usaremos R para organizar los datos y hacer los cálculos, y RStudio como entorno de trabajo para abrir y ejecutar el archivo. El documento está escrito en R Markdown: en él conviven el texto, el código y los resultados, de modo que las tablas, los gráficos y los mapas se actualizan al generar el informe en HTML.

Cada paquete tiene una tarea concreta. readxl abre el Excel; dplyr y tidyr ayudan a ordenar y resumir la información; sf lee y prepara las comunas; ggplot2 dibuja los gráficos, y leaflet construye los mapas que podemos explorar con el cursor.

2.2 Paquetes y opciones

# Instalo los paquetes una sola vez, desde la consola de RStudio:
# install.packages(c("readxl", "dplyr", "tidyr", "sf", "leaflet", "ggplot2"))

library(readxl)   # Lee archivos de Excel.
library(dplyr)    # Filtra, transforma, agrupa y une tablas.
library(tidyr)    # Completa combinaciones y reemplaza valores ausentes.
library(sf)       # Lee y trabaja con datos geográficos vectoriales.
library(leaflet)  # Construye mapas interactivos con polígonos y ventanas de consulta.
library(ggplot2)  # Construye los gráficos estadísticos.

# echo = TRUE incluye el código; code_folding permite desplegarlo en el HTML.
# Las advertencias no se imprimen en el informe; los controles de datos sí se muestran.
# Para depuración se puede cambiar warning = TRUE y volver a ejecutar.
knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE,
                      fig.width = 10, fig.height = 6, fig.align = "center")
theme_set(theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold", colour = "#174b60"),
        plot.subtitle = element_text(colour = "#526875"),
        panel.grid.minor = element_blank(),
        panel.grid.major.y = element_blank(),
        plot.caption = element_text(hjust = 0, colour = "#526875")))
fmt_n <- function(x) format(x, big.mark = ".", scientific = FALSE, trim = TRUE)
fmt_p <- function(x) format(round(x, 1), decimal.mark = ",", nsmall = 1, trim = TRUE)

# library() carga un paquete instalado; no lo instala. %>% pasa el resultado de una
# operación a la siguiente y se puede leer como «después». paquete::funcion() permite usar
# una función indicando su paquete, como knitr::kable().

2.3 Importación de los datos

Primero indicamos la carpeta donde están los insumos. Con file.path() armamos la ruta del Excel y la del mapa. Luego consultamos las hojas disponibles con excel_sheets() y leemos Hoja1 con read_excel(): cada fila queda como un registro y cada encabezado como una columna.

La cartografía se importa con st_read(). Un shapefile no es un único archivo: .shp guarda las formas, .dbf sus atributos, .shx su índice y .prj el sistema de coordenadas. Deben estar juntos y compartir el nombre Comunas. No son columnas .sh: son archivos complementarios de una misma capa geográfica. Sus columnas de atributos se pueden consultar con names(comunas); la columna de geometría contiene los polígonos.

# Cambio únicamente estas rutas si traslado los archivos a otro equipo.
carpeta <- "C:/Users/nicolas.cobo/OneDrive - PUJ Cali/Escritorio/Casos"
hoja <- "Hoja1"  # Verifico este nombre con excel_sheets(archivo_excel).
archivo_excel <- file.path(carpeta, "EncuestaOrigenDestino.xlsx")
archivo_mapa <- file.path(carpeta, "cali", "Comunas.shp")

# file.path() construye una ruta sin concatenar manualmente las barras.
excel_sheets(archivo_excel)  # Muestra los nombres de las hojas disponibles.
encuesta <- read_excel(archivo_excel, sheet = hoja)
comunas <- st_read(archivo_mapa, quiet = TRUE)

names(comunas)   # Reviso los atributos de la cartografía.
names(encuesta)  # Identifico cómo se llaman realmente las columnas.
knitr::kable(head(encuesta, 5), caption = "Primeros cinco registros: revisión local")

# <- asigna un resultado a un objeto. head(x, 5) permite mirar cinco filas y names(x)
# muestra los nombres de las columnas. Las comillas invertidas permiten referirse a nombres
# que contienen espacios o tildes, como  TIPO DE VEHÍCULO .
names(comunas)
## [1] "OBJECTID" "gid"      "comuna"   "nombre"   "geometry"
names(encuesta)
##  [1] "FECHA"                                                                                   
##  [2] "ID ESTACIÓN"                                                                             
##  [3] "ESTACIÓN"                                                                                
##  [4] "ACCESO"                                                                                  
##  [5] "MOVIMIENTO"                                                                              
##  [6] "Hora de Encuesta"                                                                        
##  [7] "MUNICIPIO...7"                                                                           
##  [8] "DEPARTAMENTO / LOCALIDAD  / COMUNA / DISTRITO / BARRIO / VEREDA  / HITO / DIRECCIÓN...8" 
##  [9] "Codigo Origen_SDG"                                                                       
## [10] "¿QUE ESTABA HACIENDO EN ESE LUGAR?"                                                      
## [11] "MUNICIPIO...11"                                                                          
## [12] "DEPARTAMENTO / LOCALIDAD  / COMUNA / DISTRITO / BARRIO / VEREDA  / HITO / DIRECCIÓN...12"
## [13] "Codigo Destino_SDG"                                                                      
## [14] "¿QUE VA HACER A ESE LUGAR?"                                                              
## [15] "ESTRATO EN SU VIVIENDA"                                                                  
## [16] "¿DISPONIA DE UN VEHÍCULO PARA REALIZAR ESTE DESPLAZAMIENTO?"                             
## [17] "OTRO ¿CUÁL?...17"                                                                        
## [18] "ANTES"                                                                                   
## [19] "DESPUES"                                                                                 
## [20] "EDAD"                                                                                    
## [21] "SEXO"                                                                                    
## [22] "PERSONAS EN EL VEHÍCULO"                                                                 
## [23] "TIPO DE VEHÍCULO"                                                                        
## [24] "OTRO ¿CUÁL?...24"                                                                        
## [25] "TIPO DE VIAJERO"                                                                         
## [26] "comuna origen"                                                                           
## [27] "comuna destino"                                                                          
## [28] "Intracomuna"
knitr::kable(head(encuesta, 5), caption = "Primeros cinco registros: revisión local")
Primeros cinco registros: revisión local
FECHA ID ESTACIÓN ESTACIÓN ACCESO MOVIMIENTO Hora de Encuesta MUNICIPIO…7 DEPARTAMENTO / LOCALIDAD / COMUNA / DISTRITO / BARRIO / VEREDA / HITO / DIRECCIÓN…8 Codigo Origen_SDG ¿QUE ESTABA HACIENDO EN ESE LUGAR? MUNICIPIO…11 DEPARTAMENTO / LOCALIDAD / COMUNA / DISTRITO / BARRIO / VEREDA / HITO / DIRECCIÓN…12 Codigo Destino_SDG ¿QUE VA HACER A ESE LUGAR? ESTRATO EN SU VIVIENDA ¿DISPONIA DE UN VEHÍCULO PARA REALIZAR ESTE DESPLAZAMIENTO? OTRO ¿CUÁL?…17 ANTES DESPUES EDAD SEXO PERSONAS EN EL VEHÍCULO TIPO DE VEHÍCULO OTRO ¿CUÁL?…24 TIPO DE VIAJERO comuna origen comuna destino Intracomuna
2015-06-01 1 Avenida 6N X Calle 70N NORTE MOV 1 1899-12-31 06:10:00 CALI B CHIPICHAPE 218 5 CALI B PANCE 2216 1 3 0 NA NA NA 35 2 1 2 NA 1 02 22 0
2015-06-01 1 Avenida 6N X Calle 70N NORTE MOV 1 1899-12-31 06:11:00 CALIMA DARIEN 4102 6 CALI TEQUENDAMA 1904 4 2 NA NA NA NA 40 2 4 3 NA 1 Fuera de Cali 19 0
2015-06-01 1 Avenida 6N X Calle 70N NORTE MOV 1 1899-12-31 06:12:00 YUMBO VALLE DEL CAUCA 3102 6 CALI B EL PEÑON 302 1 3 NA NA NA NA 33 2 1 2 NA 1 Fuera de Cali 03 0
2015-06-01 1 Avenida 6N X Calle 70N NORTE MOV 1 1899-12-31 06:13:00 YUMBO VALLE DEL CAUCA 3102 6 CALI B OBRERO 910 1 2 NA NA NA NA 28 2 1 2 NA 1 Fuera de Cali 09 0
2015-06-01 1 Avenida 6N X Calle 70N NORTE MOV 1 1899-12-31 06:14:00 PALMIRA VALLE DEL CAUCA 3202 8 CALI B CENTENARIO 223 1 3 NA NA NA NA 25 2 2 2 NA 1 Fuera de Cali 02 0

3 Preparación y calidad de los datos

3.1 Identificación territorial y preparación de variables

En los mapas se usa el mismo conjunto de viajes para origen y destino: ambos municipios deben ser Cali y ambas comunas deben ser enteros entre 1 y 22. Los viajes intermunicipales se excluyen de este análisis, aunque no necesariamente son errores.

# Reviso los encabezados reales antes de transformar la encuesta.

# Creo claves de comparación sin cambiar los nombres originales del Excel.
# iconv() quita tildes; tolower() pasa a minúsculas.
# gsub() elimina espacios, saltos de línea, guiones y otros signos.
claves <- iconv(names(encuesta), from = "", to = "ASCII//TRANSLIT")
claves <- gsub("[^a-z0-9]", "", tolower(claves))
knitr::kable(data.frame(columna_original = names(encuesta), clave = claves),
             caption = "Encabezados disponibles en la hoja seleccionada")
Encabezados disponibles en la hoja seleccionada
columna_original clave
FECHA fecha
ID ESTACIÓN idestacion
ESTACIÓN estacion
ACCESO acceso
MOVIMIENTO movimiento
Hora de Encuesta horadeencuesta
MUNICIPIO…7 municipio7
DEPARTAMENTO / LOCALIDAD / COMUNA / DISTRITO / BARRIO / VEREDA / HITO / DIRECCIÓN…8 departamentolocalidadcomunadistritobarrioveredahitodireccion8
Codigo Origen_SDG codigoorigensdg
¿QUE ESTABA HACIENDO EN ESE LUGAR? queestabahaciendoeneselugar
MUNICIPIO…11 municipio11
DEPARTAMENTO / LOCALIDAD / COMUNA / DISTRITO / BARRIO / VEREDA / HITO / DIRECCIÓN…12 departamentolocalidadcomunadistritobarrioveredahitodireccion12
Codigo Destino_SDG codigodestinosdg
¿QUE VA HACER A ESE LUGAR? quevahaceraeselugar
ESTRATO EN SU VIVIENDA estratoensuvivienda
¿DISPONIA DE UN VEHÍCULO PARA REALIZAR ESTE DESPLAZAMIENTO? disponiadeunvehiculopararealizarestedesplazamiento
OTRO ¿CUÁL?…17 otrocual17
ANTES antes
DESPUES despues
EDAD edad
SEXO sexo
PERSONAS EN EL VEHÍCULO personasenelvehiculo
TIPO DE VEHÍCULO tipodevehiculo
OTRO ¿CUÁL?…24 otrocual24
TIPO DE VIAJERO tipodeviajero
comuna origen comunaorigen
comuna destino comunadestino
Intracomuna intracomuna
# Busco coincidencias exactas entre claves y recupero el nombre original.
# No elijo una columna por suposición si falta o hay varias coincidencias.
buscar_columna <- function(candidatos) {
  coincidencias <- names(encuesta)[which(claves %in% candidatos)]
  if (length(coincidencias) != 1) {
    stop("Columna ausente o ambigua: ", paste(candidatos, collapse = " / "),
         ". Revise la hoja seleccionada y estos encabezados: ",
         paste(names(encuesta), collapse = " | "))
  }
  coincidencias
}
columna_origen <- buscar_columna(c("comunaorigen", "comunadeorigen"))
columna_destino <- buscar_columna(c("comunadestino", "comunadedestino"))
columna_vehiculo <- buscar_columna(c("tipodevehiculo", "tipovehiculo"))
# La clave comunaorigen reconoce encabezados como COMUNA ORIGEN, Comuna_Origen o comuna
# origen ; también se admite comuna de origen. Esta revisión no cambia el contenido de las
# columnas.
# Identifico los municipios por sus nombres descriptivos normalizados.
# Si ambos se llaman MUNICIPIO, read_excel() añade sufijos como ...5 y ...9.
if (sum(claves %in% c("municipioorigen", "municipiodeorigen")) == 1 &&
    sum(claves %in% c("municipiodestino", "municipiodedestino")) == 1) {
  columnas_municipio <- c(
    buscar_columna(c("municipioorigen", "municipiodeorigen")),
    buscar_columna(c("municipiodestino", "municipiodedestino"))
  )
} else {
  columnas_municipio <- names(encuesta)[which(grepl("^municipio[0-9]*$", claves))]
}
if (length(columnas_municipio) != 2) {
  stop("Revise los encabezados: se necesitan dos columnas de municipio (origen y destino).")
}
columnas_municipio  # En encabezados repetidos, confirmo el orden origen–destino.
## [1] "MUNICIPIO...7"  "MUNICIPIO...11"
# Unifico tildes, mayúsculas y espacios; conservo la respuesta original en encuesta.
normalizar_texto <- function(x) {
  x <- toupper(trimws(as.character(x)))
  x <- iconv(x, from = "", to = "ASCII//TRANSLIT")
  gsub("[[:space:]]+", " ", x)
}
marcadores_ausencia <- c("", "NA", "N/A", "NULL", "SIN DATO", "SIN INFORMACION")
# Conservo encuesta intacta y creo una tabla para el análisis.
# sub() quita el prefijo COMUNA; toupper() y trimws() unifican el texto.
# as.numeric() convierte "Comuna 02", "COMUNA 2" y "02" al número 2.
# suppressWarnings() se limita a esta conversión: reviso los NA más abajo.
datos <- encuesta %>%
  mutate(
    municipio_origen = normalizar_texto(.data[[columnas_municipio[1]]]),
    municipio_destino = normalizar_texto(.data[[columnas_municipio[2]]]),
    origen = suppressWarnings(as.numeric(sub("^COMUNA[[:space:]]*", "",
      toupper(trimws(as.character(.data[[columna_origen]])))))),
    destino = suppressWarnings(as.numeric(sub("^COMUNA[[:space:]]*", "",
      toupper(trimws(as.character(.data[[columna_destino]])))))),
    vehiculo = normalizar_texto(.data[[columna_vehiculo]]),
    modo = case_when(
      vehiculo %in% c("1", "1.0", "BICICLETA", "BICI") ~ "Bicicleta",
      vehiculo %in% c("2", "2.0", "MOTO", "MOTOCICLETA") ~ "Motocicleta",
      vehiculo %in% c("3", "3.0", "AUTOMOVIL", "AUTOMÓVIL", "AUTO", "CARRO") ~ "Automóvil",
      is.na(vehiculo) | vehiculo %in% marcadores_ausencia ~ "Sin información",
      TRUE ~ "Otro modo o código"
    ),
    duplicado = duplicated(encuesta),
    control = case_when(
      is.na(municipio_origen) | is.na(municipio_destino) |
        municipio_origen %in% marcadores_ausencia |
        municipio_destino %in% marcadores_ausencia ~ "Municipio sin información",
      !municipio_origen %in% c("CALI", "SANTIAGO DE CALI") |
        !municipio_destino %in% c("CALI", "SANTIAGO DE CALI") ~ "Viaje externo o municipio por revisar",
      is.na(origen) | is.na(destino) |
        !origen %in% 1:22 | !destino %in% 1:22 ~ "Comuna ausente o inválida",
      TRUE ~ "Incluido"
    )
  )
viajes <- datos %>% filter(control == "Incluido")
if (nrow(viajes) == 0) stop("No hay viajes incluidos: revise municipios y comunas.")

# mutate() crea columnas y filter() selecciona filas. toupper() unifica mayúsculas y
# trimws() quita espacios al comienzo y al final. case_when() evalúa condiciones en orden y
# asigna la primera categoría que corresponda. %in% verifica pertenencia; ! significa
# negación y | significa «o». is.na() identifica valores ausentes. .data[[nombre]]
# selecciona una columna cuyo nombre está guardado en una variable.

Los códigos de comuna se normalizan antes del análisis: "Comuna 02", "COMUNA 2" y "02" se convierten en 2. Se conservan únicamente códigos enteros entre 1 y 22; los valores ausentes o no interpretables se identifican en el control de calidad. En este análisis se utiliza la equivalencia 1 = bicicleta, 2 = motocicleta y 3 = automóvil.

3.2 Biblioteca de variables

Esta tabla conecta las columnas de entrada con los nombres que usaremos en el análisis. Conservamos el Excel original y creamos una tabla de trabajo llamada datos; después, viajes contiene únicamente los registros incluidos.

biblioteca <- data.frame(
  Entrada = c(columnas_municipio, columna_origen, columna_destino,
              columna_vehiculo, columna_fecha, "Fila completa del Excel", "Variables territoriales"),
  Variable = c("municipio_origen", "municipio_destino", "origen", "destino",
               "vehiculo / modo", "fechas_registro", "duplicado", "control"),
  Tipo = c("Texto", "Texto", "Numérico", "Numérico", "Texto / categoría",
           "Fecha", "Lógico", "Categoría"),
  Uso = c("Verificar que el viaje comienza en Cali", "Verificar que termina en Cali",
          "Comuna de salida: entero entre 1 y 22", "Comuna de llegada: entero entre 1 y 22",
          "Conservar el código y agrupar los modos principales", "Establecer el periodo observado",
          "Marcar filas idénticas adicionales, sin eliminarlas", "Explicar inclusiones y exclusiones"))
knitr::kable(biblioteca, caption = "Variables de entrada y variables de trabajo")
Variables de entrada y variables de trabajo
Entrada Variable Tipo Uso
MUNICIPIO…7 municipio_origen Texto Verificar que el viaje comienza en Cali
MUNICIPIO…11 municipio_destino Texto Verificar que termina en Cali
comuna origen origen Numérico Comuna de salida: entero entre 1 y 22
comuna destino destino Numérico Comuna de llegada: entero entre 1 y 22
TIPO DE VEHÍCULO vehiculo / modo Texto / categoría Conservar el código y agrupar los modos principales
FECHA fechas_registro Fecha Establecer el periodo observado
Fila completa del Excel duplicado Lógico Marcar filas idénticas adicionales, sin eliminarlas
Variables territoriales control Categoría Explicar inclusiones y exclusiones

La columna FECHA contiene las fechas de los registros y se usa para calcular el intervalo presentado en la introducción.

control_fechas <- data.frame(
  Estado = c("Fecha válida", "Fecha ausente o no interpretable"),
  Registros = c(sum(!is.na(fechas_registro)), sum(is.na(fechas_registro))))
knitr::kable(control_fechas, caption = "Fechas utilizadas para establecer el periodo observado")
Fechas utilizadas para establecer el periodo observado
Estado Registros
Fecha válida 35054
Fecha ausente o no interpretable 0

El periodo se obtiene del archivo completo antes del filtro territorial. Los registros sin fecha válida no intervienen en ese intervalo, pero no se excluyen del análisis espacial por ese motivo.

En la cartografía, comuna será la llave territorial y la geometría conservará los límites. Más adelante agregaremos total, bicicletas, motos y automoviles: son conteos calculados, no columnas originales de la encuesta.

3.3 Inclusiones, valores ausentes y modos de transporte

Antes de mirar dónde se concentran los viajes, conviene responder tres preguntas: ¿cuántos registros podemos ubicar?, ¿qué información falta? y ¿cómo se distribuyen los modos? Los gráficos siguientes responden una pregunta a la vez.

La revisión diferencia dos bases de cálculo: el archivo completo, utilizado para evaluar la calidad y la selección territorial, y los viajes incluidos, utilizados para describir los modos de transporte. En las tablas y el gráfico, N identifica el total de registros de referencia y n el número de registros de cada categoría.

N_inicial <- nrow(datos)
N_incluido <- nrow(viajes)

# Cada registro se cuenta una sola vez, según la primera condición que cumple.
calidad <- datos %>%
  count(control, name = "registros") %>%
  mutate(porcentaje = round(100 * registros / N_inicial, 1))
knitr::kable(calidad,
  col.names = c("Categoría de control", "Registros (n)", "% del archivo completo"),
  caption = paste0("Trazabilidad territorial. N = ",
                   format(N_inicial, big.mark = ".", trim = TRUE), " registros"),
  format.args = list(big.mark = ".", decimal.mark = ","))
Trazabilidad territorial. N = 35.054 registros
Categoría de control Registros (n) % del archivo completo
Comuna ausente o inválida 2.389 6,8
Incluido 22.139 63,2
Municipio sin información 79 0,2
Viaje externo o municipio por revisar 10.447 29,8
ggplot(calidad, aes(reorder(control, registros), registros, fill = control == "Incluido")) +
  geom_col(width = 0.65, show.legend = FALSE) +
  geom_text(aes(label = paste0(fmt_n(registros), " (", fmt_p(porcentaje), " %)")),
            hjust = -0.08, size = 3.6) + coord_flip() +
  scale_fill_manual(values = c("FALSE" = "#95a9b5", "TRUE" = "#16847b")) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.4))) +
  labs(title = "Del archivo original al conjunto de análisis",
       subtitle = paste("Base de cálculo:", fmt_n(N_inicial), "registros iniciales"),
       x = NULL, y = "Registros", caption = "Cada registro aparece en una sola categoría.")

La barra verde muestra la información que podremos ubicar en ambos extremos del viaje. Las demás barras explican qué queda fuera y por qué: no deben leerse automáticamente como errores de captura.

De los 35.054 registros iniciales, se incluyen 22.139 (63,2 %). Los porcentajes de esta tabla se calculan como n de cada categoría / N del archivo completo × 100.

  • Incluido: ambos municipios corresponden a Cali y las dos comunas tienen códigos enteros entre 1 y 22.
  • Municipio sin información: falta al menos uno de los municipios o contiene un marcador de ausencia, como una celda vacía, NA, N/A o «sin información».
  • Viaje externo o municipio por revisar: los municipios tienen información, pero al menos uno no coincide con Cali o Santiago de Cali. Puede corresponder a un viaje intermunicipal válido o a una denominación pendiente de normalizar; no implica automáticamente un error.
  • Comuna ausente o inválida: los dos municipios corresponden a Cali, pero falta al menos una comuna, su texto no pudo convertirse a número o su código no es un entero entre 1 y 22.

Las categorías son excluyentes. El orden de evaluación es el siguiente: primero se revisa la ausencia municipal, después la condición Cali–Cali y finalmente las comunas. Por eso los problemas de comuna de viajes ya clasificados como externos no vuelven a contarse en «Comuna ausente o inválida».

# Un ausente original no es lo mismo que un texto que falló al convertirse.
# Se consultan las columnas originales para distinguir ambas situaciones.
marcadores_ausencia <- c("", "NA", "N/A", "NULL", "SIN DATO",
                         "SIN INFORMACION", "SIN INFORMACIÓN")
texto_origen <- toupper(trimws(as.character(encuesta[[columna_origen]])))
texto_destino <- toupper(trimws(as.character(encuesta[[columna_destino]])))
ausente_origen <- is.na(texto_origen) | texto_origen %in% marcadores_ausencia
ausente_destino <- is.na(texto_destino) | texto_destino %in% marcadores_ausencia

# Estos controles se calculan sobre TODO el archivo y pueden superponerse.
revision <- data.frame(
  Indicador = c("Registros iniciales", "Registros incluidos",
    "Comuna de origen: ausente en el archivo",
    "Comuna de destino: ausente en el archivo",
    "Comuna de origen: texto no convertible",
    "Comuna de destino: texto no convertible",
    "Comuna de origen: número fuera de 1–22 o no entero",
    "Comuna de destino: número fuera de 1–22 o no entero",
    "Filas idénticas adicionales"),
  Registros = c(N_inicial, N_incluido,
    sum(ausente_origen), sum(ausente_destino),
    sum(!ausente_origen & is.na(datos$origen)),
    sum(!ausente_destino & is.na(datos$destino)),
    sum(!is.na(datos$origen) & !datos$origen %in% 1:22),
    sum(!is.na(datos$destino) & !datos$destino %in% 1:22),
    sum(datos$duplicado))
) %>% mutate(Porcentaje = round(100 * Registros / N_inicial, 1))
knitr::kable(revision,
  col.names = c("Indicador", "Registros (n)", "% del archivo completo"),
  caption = paste0("Controles básicos y valores ausentes. N = ",
                   format(N_inicial, big.mark = ".", trim = TRUE), " registros"),
  format.args = list(big.mark = ".", decimal.mark = ","))
Controles básicos y valores ausentes. N = 35.054 registros
Indicador Registros (n) % del archivo completo
Registros iniciales 35.054 100,0
Registros incluidos 22.139 63,2
Comuna de origen: ausente en el archivo 0 0,0
Comuna de destino: ausente en el archivo 0 0,0
Comuna de origen: texto no convertible 6.306 18,0
Comuna de destino: texto no convertible 6.400 18,3
Comuna de origen: número fuera de 1–22 o no entero 672 1,9
Comuna de destino: número fuera de 1–22 o no entero 788 2,2
Filas idénticas adicionales 1 0,0

Ausente significa que la comuna no está informada en el archivo original o contiene uno de los marcadores de ausencia definidos en el código. Texto no convertible significa que sí existe una respuesta, pero no se obtiene un número después de normalizarla. Por ejemplo, «Comuna 02» se convierte correctamente a 2, mientras que «desconocida» no es convertible. Un valor como 25 o 2,5 puede ser numérico, pero es inválido para este análisis.

La tabla separa la ausencia original de las dificultades de conversión para que podamos distinguir qué información falta y cuál necesita revisión. Todos estos controles usan como denominador N = 35.054, incluso si el registro quedó fuera del análisis territorial. Sus filas no se suman: un mismo registro puede tener problemas en origen y destino, o ser una fila idéntica adicional. Por esta razón, tampoco deben sumarse los problemas de origen y destino para compararlos con la categoría territorial de comunas inválidas.

revision_grafico <- revision %>% filter(grepl("^Comuna", Indicador)) %>%
  mutate(Extremo = if_else(grepl("origen", Indicador), "Origen", "Destino"),
         Problema = sub("^Comuna de (origen|destino): ", "", Indicador))
ggplot(revision_grafico, aes(Problema, Registros, fill = Extremo)) +
  geom_col(position = position_dodge(width = 0.75), width = 0.65) +
  geom_text(aes(label = fmt_n(Registros)), position = position_dodge(width = 0.75),
            hjust = -0.15, size = 3.5) + coord_flip() +
  scale_fill_manual(values = c("Origen" = "#16847b", "Destino" = "#375b99")) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.2))) +
  labs(title = "¿Qué necesitamos revisar en las comunas?",
       subtitle = paste("Revisión del archivo completo: N =", fmt_n(N_inicial)),
       x = NULL, y = "Registros", fill = NULL,
       caption = "Los controles pueden coincidir en un mismo registro; las barras no se suman.")

Comparamos origen y destino para reconocer dónde se pierde más información. Una barra alta de ausencias sugiere revisar el diligenciamiento; una de textos no convertibles sugiere revisar cómo se escribieron las comunas. Los códigos fuera de rango requieren consultar el dato original antes de corregirlos.

# El denominador cambia: ahora son únicamente los viajes incluidos.
frecuencia_modo <- viajes %>%
  count(modo, name = "registros", sort = TRUE) %>%
  mutate(porcentaje = round(100 * registros / N_incluido, 1),
         etiqueta = paste0(format(registros, big.mark = ".", trim = TRUE),
                           " (", format(porcentaje, decimal.mark = ",",
                                        nsmall = 1, trim = TRUE), " %)"))
knitr::kable(frecuencia_modo %>% select(-etiqueta),
  col.names = c("Modo de transporte", "Registros (n)", "% de los incluidos"),
  caption = paste0("Modos de transporte. N = ",
                   format(N_incluido, big.mark = ".", trim = TRUE), " viajes incluidos"),
  format.args = list(big.mark = ".", decimal.mark = ","))
Modos de transporte. N = 22.139 viajes incluidos
Modo de transporte Registros (n) % de los incluidos
Motocicleta 10.302 46,5
Automóvil 8.598 38,8
Otro modo o código 1.966 8,9
Bicicleta 1.223 5,5
Sin información 50 0,2

En esta tabla, cada porcentaje corresponde a n del modo / N de viajes incluidos × 100; no se calcula sobre el archivo completo. «Sin información» agrupa respuestas de vehículo vacías, NA, N/A o «sin información», mientras que «Otro modo o código» contiene respuestas no reconocidas como bicicleta, motocicleta o automóvil. Esta última categoría no se interpreta como un modo específico sin consultar las convenciones de la encuesta. Las diferencias de una décima en la suma de porcentajes se deben al redondeo.

Las filas idénticas se conservan porque, sin un identificador de entrevista, no se puede asegurar que sean errores de captura. También se conservan los viajes dentro de una misma comuna. Los mapas generales incluyen otros modos y respuestas sin modo; los específicos muestran únicamente bicicleta, motocicleta o automóvil.

# Las etiquetas muestran n y porcentaje; el subtítulo identifica el denominador.
# Todos los valores se recalculan al ejecutar el documento con la base de datos.
ggplot(frecuencia_modo, aes(x = reorder(modo, registros), y = registros)) +
  geom_col(fill = "#16847b", width = 0.65) +
  geom_text(aes(label = etiqueta), hjust = -0.08, size = 3.8) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.30))) +
  coord_flip() +
  labs(title = "Composición modal de los registros incluidos",
       subtitle = paste0("N = ", format(N_incluido, big.mark = ".", trim = TRUE),
                         " viajes incluidos de ",
                         format(N_inicial, big.mark = ".", trim = TRUE),
                         " registros iniciales (",
                         format(round(100 * N_incluido / N_inicial, 1),
                                decimal.mark = ",", nsmall = 1, trim = TRUE), " %)"),
       x = NULL, y = "Registros de desplazamiento (n)",
       caption = "Cada etiqueta muestra n (% de los viajes incluidos). Porcentajes sin expansión muestral.")

El modo con más registros predomina en esta muestra. Esto no demuestra que sea el más utilizado por toda la población.

4 Resúmenes por comuna y unión geográfica

Ahora pasamos de los registros individuales a una lectura territorial. Contamos cuántos viajes comienzan y terminan en cada comuna y desagregamos esos conteos por modo. Después unimos los resultados con los polígonos para dibujar los mapas. Esta unión usa el código de comuna, no el orden de las filas.

Una misma comuna puede ser un origen frecuente y también un destino frecuente. Por eso analizamos ambos extremos antes de hablar de predominio de llegadas o salidas.

4.1 Conteos por origen y destino

# group_by() organiza los cálculos por comuna.
# sum(condición) cuenta los TRUE; na.rm = TRUE omite valores NA.
origen_resumen <- viajes %>%
  group_by(comuna = origen) %>%
  summarise(total = n(),
            bicicletas = sum(modo == "Bicicleta", na.rm = TRUE),
            motos = sum(modo == "Motocicleta", na.rm = TRUE),
            automoviles = sum(modo == "Automóvil", na.rm = TRUE),
            .groups = "drop")

destino_resumen <- viajes %>%
  group_by(comuna = destino) %>%
  summarise(total = n(),
            bicicletas = sum(modo == "Bicicleta", na.rm = TRUE),
            motos = sum(modo == "Motocicleta", na.rm = TRUE),
            automoviles = sum(modo == "Automóvil", na.rm = TRUE),
            .groups = "drop")

knitr::kable(origen_resumen, caption = "Registros por comuna de origen")
Registros por comuna de origen
comuna total bicicletas motos automoviles
1 626 32 296 234
2 2249 122 1062 860
3 1608 84 701 678
4 1193 58 571 443
5 521 28 260 185
6 796 29 377 313
7 583 30 272 229
8 862 46 421 319
9 737 36 317 295
10 1094 71 535 404
11 717 42 339 268
12 257 10 133 88
13 1031 62 453 412
14 574 32 258 235
15 974 55 455 377
16 836 65 383 320
17 1762 103 853 662
18 1208 85 593 433
19 2240 113 995 922
20 599 33 278 228
21 693 35 323 272
22 979 52 427 421
knitr::kable(destino_resumen, caption = "Registros por comuna de destino")
Registros por comuna de destino
comuna total bicicletas motos automoviles
1 187 9 90 73
2 3772 194 1734 1517
3 2881 169 1330 1095
4 1496 80 678 595
5 440 38 183 181
6 570 31 255 208
7 598 36 279 224
8 830 46 382 325
9 1055 56 512 382
10 741 34 353 291
11 483 26 217 204
12 207 13 106 71
13 560 28 285 189
14 316 20 150 120
15 481 17 232 199
16 645 31 290 275
17 1615 95 743 650
18 526 28 240 216
19 2438 131 1151 924
20 303 22 124 131
21 434 24 202 164
22 1561 95 766 564
# .groups = "drop" deja el resumen sin agrupación. El total puede superar la suma de los
# tres modos, porque incluye otras categorías y respuestas sin información de modo.

Los totales de origen y destino deben coincidir: cada viaje aporta una salida y una llegada. Las columnas de los tres modos principales pueden sumar menos que el total, porque todavía puede haber códigos sin identificar o respuestas ausentes. Una comuna que no aparezca en estos resúmenes recibirá cero al unirla con la cartografía.

4.2 ¿Dónde se concentran los registros?

Un mapa permite reconocer concentraciones; los porcentajes nos dicen cuánto pesan. Aquí comparamos las cinco comunas con más registros en cada extremo y medimos cuántas comunas acumulan al menos la mitad de las salidas o llegadas. Los empates se ordenan por código de comuna para que las selecciones sean reproducibles.

territorio <- data.frame(comuna = 1:22) %>%
  left_join(origen_resumen %>% select(comuna, salidas = total), by = "comuna") %>%
  left_join(destino_resumen %>% select(comuna, llegadas = total), by = "comuna") %>%
  mutate(salidas = replace_na(salidas, 0), llegadas = replace_na(llegadas, 0),
         pct_salidas = 100 * salidas / N_incluido,
         pct_llegadas = 100 * llegadas / N_incluido)
ranking_o <- territorio %>% arrange(desc(salidas), comuna) %>%
  mutate(acumulado = 100 * cumsum(salidas) / N_incluido)
ranking_d <- territorio %>% arrange(desc(llegadas), comuna) %>%
  mutate(acumulado = 100 * cumsum(llegadas) / N_incluido)
c3_o <- sum(head(ranking_o$salidas, 3)) / N_incluido * 100
c3_d <- sum(head(ranking_d$llegadas, 3)) / N_incluido * 100
mitad_o <- which(ranking_o$acumulado >= 50)[1]
mitad_d <- which(ranking_d$acumulado >= 50)[1]
resumen_concentracion <- data.frame(
  Indicador = c("Total de registros", "Media por comuna", "Mediana por comuna",
                "Mínimo", "Máximo", "Desviación estándar",
                "Participación de las 3 primeras (%)", "Participación de las 5 primeras (%)",
                "Comunas necesarias para reunir al menos el 50 %"),
  Salidas = c(N_incluido, mean(territorio$salidas), median(territorio$salidas),
              min(territorio$salidas), max(territorio$salidas), sd(territorio$salidas),
              c3_o, sum(head(ranking_o$salidas, 5)) / N_incluido * 100, mitad_o),
  Llegadas = c(N_incluido, mean(territorio$llegadas), median(territorio$llegadas),
               min(territorio$llegadas), max(territorio$llegadas), sd(territorio$llegadas),
               c3_d, sum(head(ranking_d$llegadas, 5)) / N_incluido * 100, mitad_d))
knitr::kable(resumen_concentracion, digits = 1,
             caption = "Distribución de los conteos entre las 22 comunas")
Distribución de los conteos entre las 22 comunas
Indicador Salidas Llegadas
Total de registros 22139.0 22139.0
Media por comuna 1006.3 1006.3
Mediana por comuna 849.0 584.0
Mínimo 257.0 187.0
Máximo 2249.0 3772.0
Desviación estándar 530.4 941.9
Participación de las 3 primeras (%) 28.2 41.1
Participación de las 5 primeras (%) 41.0 55.4
Comunas necesarias para reunir al menos el 50 % 7.0 5.0
rankings <- bind_rows(
  ranking_o %>% slice_head(n = 5) %>% transmute(Extremo = "Origen", comuna, registros = salidas, porcentaje = pct_salidas),
  ranking_d %>% slice_head(n = 5) %>% transmute(Extremo = "Destino", comuna, registros = llegadas, porcentaje = pct_llegadas))
knitr::kable(rankings, digits = 1, caption = "Cinco comunas con más registros en cada extremo")
Cinco comunas con más registros en cada extremo
Extremo comuna registros porcentaje
Origen 2 2249 10.2
Origen 19 2240 10.1
Origen 17 1762 8.0
Origen 3 1608 7.3
Origen 18 1208 5.5
Destino 2 3772 17.0
Destino 3 2881 13.0
Destino 19 2438 11.0
Destino 17 1615 7.3
Destino 22 1561 7.1
ggplot(rankings, aes(reorder(factor(comuna), registros), registros, fill = Extremo)) +
  geom_col(width = 0.65, show.legend = FALSE) +
  geom_text(aes(label = paste0(fmt_n(registros), " (", fmt_p(porcentaje), " %)")),
            hjust = -0.08, size = 3.2) +
  facet_wrap(~Extremo, ncol = 1, scales = "free_x") + coord_flip() +
  scale_fill_manual(values = c("Origen" = "#16847b", "Destino" = "#375b99")) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.35))) +
  labs(title = "Las comunas que más pesan en los registros",
       subtitle = paste("Cada porcentaje usa N =", fmt_n(N_incluido), "viajes incluidos"),
       x = "Comuna", y = "Registros")

cat("Las comunas **", paste(head(ranking_o$comuna, 3), collapse = ", "),
    "** reúnen el **", fmt_p(c3_o), " %** de las salidas; las comunas **",
    paste(head(ranking_d$comuna, 3), collapse = ", "), "** reúnen el **", fmt_p(c3_d),
    " %** de las llegadas. La diferencia de concentración entre destinos y orígenes es de **",
    fmt_p(c3_d - c3_o), " puntos porcentuales**.\n\n", sep = "")

Las comunas 2, 19, 17 reúnen el 28,2 % de las salidas; las comunas 2, 3, 19 reúnen el 41,1 % de las llegadas. La diferencia de concentración entre destinos y orígenes es de 12,8 puntos porcentuales.

cat("Para reunir al menos la mitad de los registros se necesitan **", mitad_o,
    " comunas de origen** y **", mitad_d, " de destino**.\n\n", sep = "")

Para reunir al menos la mitad de los registros se necesitan 7 comunas de origen y 5 de destino.

cat("La media de salidas y llegadas es idéntica porque sus totales son iguales. Las medianas son **",
    fmt_p(median(territorio$salidas)), "** y **", fmt_p(median(territorio$llegadas)),
    "**, respectivamente. Compararlas con la media permite apreciar cuánto influyen las comunas de mayor conteo.\n", sep = "")

La media de salidas y llegadas es idéntica porque sus totales son iguales. Las medianas son 849,0 y 584,0, respectivamente. Compararlas con la media permite apreciar cuánto influyen las comunas de mayor conteo.

Las llegadas están más concentradas en las tres primeras comunas que las salidas.

La concentración describe la distribución de los registros. Para comparar intensidad de movilidad por habitante se necesitarían denominadores de población; para medir densidad espacial, el área de cada comuna.

4.3 Preparación cartográfica y unión de tablas

# Identifico la llave del mapa sin depender de mayúsculas o tildes.
claves_mapa <- gsub("[^a-z0-9]", "", tolower(iconv(names(comunas), to = "ASCII//TRANSLIT")))
llave_mapa <- names(comunas)[claves_mapa %in% c("comuna", "codcomuna", "codigocomuna")]
if (length(llave_mapa) != 1) stop("Identifique la columna de comuna en la cartografía: ", paste(names(comunas), collapse = ", "))
# No asigno un CRS por suposición: debe venir definido en el archivo .prj.
if (is.na(st_crs(comunas))) stop("Falta el sistema de referencia: revise el archivo .prj.")
comunas <- comunas %>%
  mutate(comuna = suppressWarnings(as.numeric(sub("^COMUNA[[:space:]]*", "", normalizar_texto(.data[[llave_mapa]]))))) %>%
  select(comuna) %>%
  st_make_valid() %>%
  group_by(comuna) %>%
  summarise(.groups = "drop") %>%
  st_transform(4326)

# setequal() comprueba que estén las 22 comunas, sin códigos adicionales.
stopifnot(setequal(comunas$comuna, 1:22), !anyNA(comunas$comuna),
          !any(st_is_empty(comunas)), all(st_is_valid(comunas)))

# left_join() conserva todas las comunas y agrega sus conteos por la clave comuna.
# Uso objetos sf para mantener geometría y atributos unidos en cada operación.
mapa_origen <- comunas %>% left_join(origen_resumen, by = "comuna")
mapa_destino <- comunas %>% left_join(destino_resumen, by = "comuna")
conteos <- c("total", "bicicletas", "motos", "automoviles")
mapa_origen <- mapa_origen %>% mutate(across(all_of(conteos), ~replace_na(.x, 0)))
mapa_destino <- mapa_destino %>% mutate(across(all_of(conteos), ~replace_na(.x, 0)))

# Cada viaje debe sumar una vez en origen y una vez en destino.
stopifnot(sum(mapa_origen$total) == nrow(viajes),
          sum(mapa_destino$total) == nrow(viajes))
knitr::kable(st_drop_geometry(mapa_origen), caption = "Verificación de la unión: orígenes")
Verificación de la unión: orígenes
comuna total bicicletas motos automoviles
1 626 32 296 234
2 2249 122 1062 860
3 1608 84 701 678
4 1193 58 571 443
5 521 28 260 185
6 796 29 377 313
7 583 30 272 229
8 862 46 421 319
9 737 36 317 295
10 1094 71 535 404
11 717 42 339 268
12 257 10 133 88
13 1031 62 453 412
14 574 32 258 235
15 974 55 455 377
16 836 65 383 320
17 1762 103 853 662
18 1208 85 593 433
19 2240 113 995 922
20 599 33 278 228
21 693 35 323 272
22 979 52 427 421
# st_make_valid() repara geometrías y st_transform(4326) transforma al sistema WGS 84 para
# visualizar. Agrupar y resumir une las partes de una misma comuna. st_drop_geometry() deja
# únicamente sus atributos para mostrar una tabla. across() aplica una operación a varias
# columnas; .x representa cada columna y replace_na() reemplaza sus ausentes. Aquí el cero
# significa que una comuna válida no tiene registros en el resumen; no se aplica a la
# información faltante de la encuesta. stopifnot() detiene la ejecución cuando una
# comprobación no se cumple.

5 Mapas interactivos

Para leerlos, empezamos por el mapa general y luego miramos cada modo. Un tono más oscuro indica un conteo mayor de registros, no necesariamente mayor uso por habitante. Al comparar una misma comuna en origen y destino podemos reconocer si concentra más salidas o más llegadas.

Cada mapa muestra conteos de registros. Al pulsar una comuna se consulta su valor exacto. Para comparar origen y destino de una misma categoría se usan los mismos intervalos de color. Los intervalos pueden cambiar entre modos; por tanto, la comparación entre medios de transporte debe considerar los valores y no únicamente los colores.

# Se dibujan las comunas directamente y se ajusta la vista a su extensión.
# El fondo OpenStreetMap no requiere una clave API para esta visualización.
mapa <- function(capa, variable, titulo) {
  valores <- capa[[variable]]
  total <- sum(valores)
  maximo <- max(1, mapa_origen[[variable]], mapa_destino[[variable]])
  paleta <- colorBin("Blues", domain = c(0, maximo),
                     bins = seq(0, maximo, length.out = 6))
  limites <- st_bbox(capa)
  porcentaje <- round(100 * valores / max(1, total), 1)
  detalle <- paste0("<b>", titulo, "</b><br>Comuna ", capa$comuna,
                    "<br>Registros: ", format(valores, big.mark = ".", trim = TRUE),
                    "<br>Participación: ", porcentaje, " %",
                    "<br>Total de esta categoría (N): ", total)

  leaflet(capa, width = "100%", height = 480) %>%
  addProviderTiles(providers$Esri.WorldTopoMap) %>%
  addPolygons(
    fillColor = paleta(valores),
    fillOpacity = 0.75,
    color = "#FFFFFF",
    weight = 1.2,
    label = paste("Comuna", capa$comuna, "—", valores, "registros"),
    popup = detalle,
    highlightOptions = highlightOptions(
      weight = 3,
      color = "#163247"
    )
  ) %>%
    addLegend(pal = paleta, values = valores,
              title = paste0(titulo, "<br>N = ", total), position = "bottomright") %>%
    fitBounds(as.numeric(limites["xmin"]), as.numeric(limites["ymin"]),
              as.numeric(limites["xmax"]), as.numeric(limites["ymax"]))
}

# colorBin() asigna colores por intervalos comunes de origen y destino.
# addTiles() agrega el fondo; addPolygons() representa las comunas con sus conteos.
# popup muestra los datos al pulsar; label los muestra al pasar el cursor.
# fitBounds() centra el mapa en la extensión de las comunas.

5.1 Orígenes

5.1.1 General

mapa(mapa_origen, "total", "1. Origen: todos los modos")

Lectura del mapa. En todos los modos se registran 22.139 desplazamientos. El mayor conteo corresponde a la(s) comuna(s) 2, con 2.249 registros cada una (10,2 % de la categoría). En las comunas 2, 19, 17 salen 6.251 registros (28,2 %).

Hay 22 comunas con registros y 0 sin registros de esta categoría en la base incluida. El tono del mapa representa volumen registrado; no mide densidad de población ni congestión.

5.1.2 Bicicleta

mapa(mapa_origen, "bicicletas", "2. Origen: bicicleta")

Lectura del mapa. En bicicleta se registran 1.223 desplazamientos. El mayor conteo corresponde a la(s) comuna(s) 2, con 122 registros cada una (10,0 % de la categoría). En las comunas 2, 19, 17 salen 338 registros (27,6 %).

Hay 22 comunas con registros y 0 sin registros de esta categoría en la base incluida. En la primera comuna del ranking, este modo representa el 5,4 % del total de origen de esa comuna. Esta proporción local responde una pregunta distinta al porcentaje de la categoría en toda la ciudad analizada.

5.1.3 Motocicleta

mapa(mapa_origen, "motos", "3. Origen: motocicleta")

Lectura del mapa. En motocicleta se registran 10.302 desplazamientos. El mayor conteo corresponde a la(s) comuna(s) 2, con 1.062 registros cada una (10,3 % de la categoría). En las comunas 2, 19, 17 salen 2.910 registros (28,2 %).

Hay 22 comunas con registros y 0 sin registros de esta categoría en la base incluida. En la primera comuna del ranking, este modo representa el 47,2 % del total de origen de esa comuna. Esta proporción local responde una pregunta distinta al porcentaje de la categoría en toda la ciudad analizada.

5.1.4 Automóvil

mapa(mapa_origen, "automoviles", "4. Origen: automóvil")

Lectura del mapa. En automóvil se registran 8.598 desplazamientos. El mayor conteo corresponde a la(s) comuna(s) 19, con 922 registros cada una (10,7 % de la categoría). En las comunas 19, 2, 3 salen 2.460 registros (28,6 %).

Hay 22 comunas con registros y 0 sin registros de esta categoría en la base incluida. En la primera comuna del ranking, este modo representa el 41,2 % del total de origen de esa comuna. Esta proporción local responde una pregunta distinta al porcentaje de la categoría en toda la ciudad analizada.

5.2 Destinos

5.2.1 General

mapa(mapa_destino, "total", "5. Destino: todos los modos")

Lectura del mapa. En todos los modos se registran 22.139 desplazamientos. El mayor conteo corresponde a la(s) comuna(s) 2, con 3.772 registros cada una (17,0 % de la categoría). En las comunas 2, 3, 19 llegan 9.091 registros (41,1 %).

Hay 22 comunas con registros y 0 sin registros de esta categoría en la base incluida. El tono del mapa representa volumen registrado; no mide densidad de población ni congestión.

5.2.2 Bicicleta

mapa(mapa_destino, "bicicletas", "6. Destino: bicicleta")

Lectura del mapa. En bicicleta se registran 1.223 desplazamientos. El mayor conteo corresponde a la(s) comuna(s) 2, con 194 registros cada una (15,9 % de la categoría). En las comunas 2, 3, 19 llegan 494 registros (40,4 %).

Hay 22 comunas con registros y 0 sin registros de esta categoría en la base incluida. En la primera comuna del ranking, este modo representa el 5,1 % del total de destino de esa comuna. Esta proporción local responde una pregunta distinta al porcentaje de la categoría en toda la ciudad analizada.

5.2.3 Motocicleta

mapa(mapa_destino, "motos", "7. Destino: motocicleta")

Lectura del mapa. En motocicleta se registran 10.302 desplazamientos. El mayor conteo corresponde a la(s) comuna(s) 2, con 1.734 registros cada una (16,8 % de la categoría). En las comunas 2, 3, 19 llegan 4.215 registros (40,9 %).

Hay 22 comunas con registros y 0 sin registros de esta categoría en la base incluida. En la primera comuna del ranking, este modo representa el 46,0 % del total de destino de esa comuna. Esta proporción local responde una pregunta distinta al porcentaje de la categoría en toda la ciudad analizada.

5.2.4 Automóvil

mapa(mapa_destino, "automoviles", "8. Destino: automóvil")

Lectura del mapa. En automóvil se registran 8.598 desplazamientos. El mayor conteo corresponde a la(s) comuna(s) 2, con 1.517 registros cada una (17,6 % de la categoría). En las comunas 2, 3, 19 llegan 3.536 registros (41,1 %).

Hay 22 comunas con registros y 0 sin registros de esta categoría en la base incluida. En la primera comuna del ranking, este modo representa el 40,2 % del total de destino de esa comuna. Esta proporción local responde una pregunta distinta al porcentaje de la categoría en toda la ciudad analizada.

5.3 ¿Qué nos dicen los mapas?

lectura <- lapply(conteos, function(v) {
  o <- mapa_origen %>% st_drop_geometry() %>% filter(.data[[v]] == max(.data[[v]]))
  d <- mapa_destino %>% st_drop_geometry() %>% filter(.data[[v]] == max(.data[[v]]))
  N <- sum(mapa_origen[[v]])
  data.frame(Categoria = v, Viajes = N,
    Origenes_principales = if (N > 0) paste(o$comuna, collapse = ", ") else "Sin registros",
    Maximo_origen = max(mapa_origen[[v]]),
    Destinos_principales = if (N > 0) paste(d$comuna, collapse = ", ") else "Sin registros",
    Maximo_destino = max(mapa_destino[[v]]))
})
print(knitr::kable(bind_rows(lectura), col.names = c("Categoría", "N", "Comunas con más salidas", "n", "Comunas con más llegadas", "n"),
  caption = "Lectura conjunta de los mapas; se conservan todos los empates"))
Lectura conjunta de los mapas; se conservan todos los empates
Categoría N Comunas con más salidas n Comunas con más llegadas n
total 22139 2 2249 2 3772
bicicletas 1223 2 122 2 194
motos 10302 2 1062 2 1734
automoviles 8598 19 922 2 1517
Composición modal: comuna 2 frente al conjunto de viajes incluidos
Modo Participación general (%) Participación en llegadas a comuna 2 (%) Diferencia (pp)
Bicicleta 5,52 5,14 -0,38
Motocicleta 46,53 45,97 -0,56
Automóvil 38,84 40,22 1,38

Una diferencia positiva indica que el modo tiene mayor participación entre las llegadas a la comuna 2 que en el conjunto analizado; una negativa indica menor participación. Los denominadores incluyen todos los modos, aunque la tabla presenta únicamente bicicleta, motocicleta y automóvil.

  • La comuna 2 concentra la mayor atracción de viajes: recibe 3.772 llegadas (17,04 % del total) y genera 2.249 salidas (10,16 %). Su saldo de +1.523 registros equivale a 168 llegadas por cada 100 salidas. Esta comuna incluye barrios como Granada, Centenario, Versalles, San Vicente, Santa Mónica, Chipichape, La Flora, Prados del Norte, Vipasa, Juanambú y Normandía, entre otros.

  • Su configuración territorial permite plantear una explicación del patrón: la presencia de oferta gastronómica en Granada, comercial en Chipichape y Centenario, y de alojamiento en el entorno de la avenida Sexta–Chipichape hace plausible que parte de las llegadas esté relacionada con el acceso a bienes y servicios. Esta interpretación es consistente con su papel receptor, pero no demuestra el motivo de los viajes ni permite atribuir las llegadas a un barrio o establecimiento particular.

  • La atracción se mantiene entre medios de transporte: la comuna 2 recibe el 15,86 % de los destinos en bicicleta, el 16,83 % en motocicleta y el 17,64 % en automóvil. La coincidencia sugiere que su importancia como destino trasciende un vehículo específico. Además, las motocicletas representan el 45,97 % de sus llegadas, proporción cercana al 46,53 % del conjunto analizado: su elevado conteo refleja principalmente el volumen general de viajes y no una especialización marcada en este modo.

  • La comuna 19 presenta un patrón distinto en los orígenes: lidera las salidas en automóvil, con 922 registros (10,72 % de ese modo). Incluye barrios como San Fernando Viejo, San Fernando Nuevo, Urbanización Tequendama, Nueva Tequendama, El Lido, El Refugio, Miraflores, Santa Isabel, Champagnat, Los Cámbulos, Pampalinda y Cuarto de Legua–Guadalupe, entre otros. La presencia de vivienda y equipamientos de salud, educación y deporte permite plantear que las salidas pueden combinar desplazamientos desde hogares y desde lugares de actividad. Por ello, el origen de un viaje no debe interpretarse automáticamente como la residencia del viajero.

  • Los resultados sugieren funciones territoriales diferenciadas, pero no una conexión directa demostrada: la comuna 2 destaca como receptora de varios modos y la comuna 19 como principal origen de automóviles. Esto justifica examinar el intercambio entre ambas en la matriz origen–destino. Sin embargo, sus posiciones en los rankings no prueban que los automóviles que salen de la comuna 19 lleguen principalmente a la comuna 2.

  • La escala del análisis limita la interpretación por barrio: una comuna reúne sectores con características y actividades distintas. Los resultados permiten identificar territorios prioritarios para profundizar el estudio, pero no establecer cuáles de sus barrios explican la concentración. Para ello se necesitan destinos y orígenes más detallados, motivos de viaje y horarios.

5.4 ¿Dónde tiene más peso cada modo?

El mapa de conteos identifica dónde hay más registros de un vehículo. La composición local identifica dónde ese vehículo representa una mayor parte de los viajes registrados. Una comuna puede destacar en una medida y no en la otra.

modos_principales <- c("Bicicleta", "Motocicleta", "Automóvil")
modal_o <- viajes %>% count(comuna = origen, modo, name = "registros") %>%
  complete(comuna = 1:22, modo = modos_principales, fill = list(registros = 0)) %>%
  left_join(territorio %>% select(comuna, total_local = salidas), by = "comuna") %>%
  mutate(Extremo = "Origen")
modal_d <- viajes %>% count(comuna = destino, modo, name = "registros") %>%
  complete(comuna = 1:22, modo = modos_principales, fill = list(registros = 0)) %>%
  left_join(territorio %>% select(comuna, total_local = llegadas), by = "comuna") %>%
  mutate(Extremo = "Destino")
perfiles <- bind_rows(modal_o, modal_d) %>%
  filter(modo %in% modos_principales) %>%
  left_join(frecuencia_modo %>% transmute(modo, cuota_global = 100 * registros / N_incluido), by = "modo") %>%
  mutate(cuota_global = replace_na(cuota_global, 0),
         cuota_local = if_else(total_local > 0, 100 * registros / total_local, NA_real_),
         diferencia_pp = cuota_local - cuota_global)
ggplot(perfiles, aes(modo, factor(comuna, levels = 22:1), fill = cuota_local)) +
  geom_tile(colour = "white") + facet_wrap(~Extremo) +
  scale_fill_gradient(low = "#edf5f6", high = "#174b60", limits = c(0, 100),
                      na.value = "grey85", name = "% local") +
  labs(title = "Composición modal dentro de cada comuna", x = NULL, y = "Comuna",
       caption = "Denominador: todos los registros de la comuna en ese extremo. Gris: sin registros totales.")

# Selecciono por volumen para que el mayor porcentaje no oculte un conteo pequeño.
perfil_destacado <- perfiles %>% group_by(Extremo, modo) %>%
  arrange(desc(registros), comuna, .by_group = TRUE) %>% slice_head(n = 1) %>% ungroup() %>%
  select(Extremo, modo, comuna, registros, total_local, cuota_local, cuota_global, diferencia_pp)
knitr::kable(perfil_destacado, digits = 1,
  caption = "Comuna con más registros por modo: peso local y diferencia frente al conjunto incluido (pp)")
Comuna con más registros por modo: peso local y diferencia frente al conjunto incluido (pp)
Extremo modo comuna registros total_local cuota_local cuota_global diferencia_pp
Destino Automóvil 2 1517 3772 40.2 38.8 1.4
Destino Bicicleta 2 194 3772 5.1 5.5 -0.4
Destino Motocicleta 2 1734 3772 46.0 46.5 -0.6
Origen Automóvil 19 922 2240 41.2 38.8 2.3
Origen Bicicleta 2 122 2249 5.4 5.5 -0.1
Origen Motocicleta 2 1062 2249 47.2 46.5 0.7
  • Bicicleta: liderazgo en volumen, sin mayor participación local. La comuna 2 encabeza tanto las salidas como las llegadas en bicicleta, pero este modo representa el 5,4 % de sus orígenes y el 5,1 % de sus destinos, proporciones cercanas al 5,5 % del conjunto analizado. Su liderazgo responde principalmente al volumen de desplazamientos de la comuna; no evidencia una participación ciclista especialmente elevada.

  • Motocicleta: peso similar al patrón general, con predominio de llegadas. En la comuna 2, las motocicletas representan el 47,2 % de las salidas y el 46,0 % de las llegadas, frente al 46,5 % general. Aunque su participación local cambia poco, recibe 1.734 viajes y genera 1.062, con un saldo de +672 registros. La diferencia territorial se manifiesta principalmente en el volumen recibido, más que en una especialización en motocicleta.

  • Automóvil: mayor participación relativa en las dos comunas destacadas. En la comuna 19, el automóvil representa el 41,2 % de las salidas, 2,3 puntos porcentuales por encima del conjunto analizado. En la comuna 2 representa el 40,2 % de las llegadas, 1,4 puntos por encima. Estos resultados muestran una participación relativamente mayor del automóvil en ambos extremos, aunque las diferencias son moderadas. No permiten afirmar que los viajes que salen de la comuna 19 se dirijan a la comuna 2.

  • Lectura conjunta: concentración territorial con composiciones modales cercanas al promedio. Las diferencias observadas oscilan entre −0,6 y +2,3 puntos porcentuales. Por tanto, los mayores conteos de estas comunas no se acompañan de desviaciones marcadas en la composición modal de las filas presentadas. La comuna 2 destaca principalmente por concentrar llegadas de distintos vehículos, mientras la comuna 19 combina el mayor conteo de salidas en automóvil con una participación de este modo algo superior a la general.

Nota: las diferencias se calculan antes de redondear; por ello, restar los porcentajes mostrados puede producir una discrepancia de una décima. La tabla selecciona las comunas líderes por conteo en cada modo, no necesariamente aquellas con la mayor participación porcentual local.

6 Conexiones y balance territorial

6.1 Matriz origen–destino

# count(origen, destino) cuenta cada par dirigido.
# complete() incluye las 484 combinaciones y asigna cero a las no observadas.
od <- viajes %>%
  count(origen, destino, name = "registros") %>%
  complete(origen = 1:22, destino = 1:22, fill = list(registros = 0))
stopifnot(sum(od$registros) == nrow(viajes))

ggplot(od, aes(x = factor(destino), y = factor(origen, levels = 22:1), fill = registros)) +
  geom_tile(color = "white") +
  scale_fill_viridis_c(trans = "sqrt", name = "Registros") +
  coord_equal() +
  labs(title = "Conexiones entre comunas en la muestra",
       x = "Comuna de destino", y = "Comuna de origen")
Cada celda corresponde a un origen y un destino; la diagonal conserva los viajes intracomuna.

Cada celda corresponde a un origen y un destino; la diagonal conserva los viajes intracomuna.

pares_principales <- od %>%
  filter(origen != destino, registros > 0) %>%
  arrange(desc(registros), origen, destino) %>% slice_head(n = 10)
pares_principales <- pares_principales %>%
  mutate(pct_total = 100 * registros / N_incluido,
         pct_intercomuna = 100 * registros / max(1, sum(viajes$origen != viajes$destino)))
knitr::kable(pares_principales, digits = 2,
             caption = "Diez conexiones intercomuna: peso sobre todos los viajes y sobre los intercomuna")
Diez conexiones intercomuna: peso sobre todos los viajes y sobre los intercomuna
origen destino registros pct_total pct_intercomuna
19 2 492 2.22 2.38
17 2 340 1.54 1.65
2 19 306 1.38 1.48
17 22 294 1.33 1.42
18 2 256 1.16 1.24
2 3 252 1.14 1.22
3 2 244 1.10 1.18
17 3 236 1.07 1.14
3 19 216 0.98 1.05
18 3 210 0.95 1.02
# factor() trata los códigos como categorías; levels controla su orden. geom_tile() dibuja
# las celdas y coord_equal() las hace cuadradas. La transformación raíz cuadrada mejora la
# visibilidad de frecuencias pequeñas; la leyenda conserva los conteos originales.
# arrange(desc()) ordena de mayor a menor y slice_head() toma las primeras filas. El par 2 →
# 19 es distinto de 19 → 2; ninguno indica por qué calles se viajó.

La matriz y la tabla muestran una concentración de llegadas hacia la comuna 2 desde distintos orígenes, especialmente desde las comunas 19, 17 y 18. La conexión 19 → 2 presenta el mayor conteo, con 492 viajes (2,38 % de los intercomuna), frente a 306 en sentido contrario. Sin embargo, las diez conexiones principales reúnen apenas el 13,8 % de los viajes intercomuna: existen destinos destacados, pero los desplazamientos se distribuyen entre múltiples conexiones.

6.2 Viajes dentro y entre comunas

resumen_intra <- viajes %>% group_by(modo) %>%
  summarise(total = n(), intracomuna = sum(origen == destino),
            intercomuna = sum(origen != destino),
            pct_intra = 100 * intracomuna / total, .groups = "drop")
knitr::kable(resumen_intra, digits = 1, caption = "Viajes intracomuna por modo (% de cada modo)")
Viajes intracomuna por modo (% de cada modo)
modo total intracomuna intercomuna pct_intra
Automóvil 8598 588 8010 6.8
Bicicleta 1223 72 1151 5.9
Motocicleta 10302 676 9626 6.6
Otro modo o código 1966 141 1825 7.2
Sin información 50 4 46 8.0
intra_grafico <- resumen_intra %>%
  pivot_longer(c(intracomuna, intercomuna), names_to = "Tipo", values_to = "registros") %>%
  mutate(porcentaje = 100 * registros / total)
ggplot(intra_grafico, aes(modo, porcentaje, fill = Tipo)) +
  geom_col(width = 0.65) + coord_flip() +
  scale_fill_manual(values = c("intracomuna" = "#16847b", "intercomuna" = "#95a9b5")) +
  labs(title = "¿Qué parte de cada modo permanece en la misma comuna?",
       x = NULL, y = "% de registros de cada modo", fill = NULL)

Distribución de los viajes y participación de las conexiones
Indicador Registros (n) Base de cálculo (N) %
Viajes dentro de la misma comuna 1.481 22.139 6,7
Viajes entre comunas diferentes 20.658 22.139 93,3
Conexión 19 → 2 492 20.658 2,4
Conexión 2 → 19 306 20.658 1,5
Diez conexiones intercomuna principales 2.846 20.658 13,8
Demás conexiones intercomuna 17.812 20.658 86,2
  • Distribución territorial de los viajes: de los 22.139 registros incluidos, 20.658 (93,3 %) conectan comunas diferentes y 1.481 (6,7 %) permanecen en la misma comuna. Esta comparación permite dimensionar el papel de los desplazamientos entre territorios. Cruzar una frontera administrativa no implica necesariamente recorrer una distancia mayor.

  • Peso de las conexiones principales: las diez conexiones intercomuna seleccionadas reúnen 2.846 viajes (13,8 %). Las demás acumulan 17.812 (86,2 %). Esta distribución permite evaluar cuánto del movimiento registrado queda explicado por un grupo reducido de pares origen–destino. Los empates se resuelven por código de origen y destino.

Volumen y diferencia entre ambos sentidos
Indicador Resultado
Viajes 19 → 2 492
Viajes 2 → 19 306
Diferencia: 19 → 2 menos 2 → 19 186
Variación relativa respecto al sentido 2 → 19 60,8 %
  • Asimetría direccional: se registran 492 viajes de la comuna 19 a la 2, frente a 306 en sentido contrario. El primer sentido supera al segundo en 186 registros (60,8 %). Este resultado es consistente con el papel receptor de la comuna 2 observado en el balance general.

  • Predominan los desplazamientos entre comunas: 20.658 viajes (93,3 %) conectan comunas diferentes, mientras que 1.481 (6,7 %) permanecen dentro de la misma. Esto muestra que la movilidad registrada rebasa ampliamente los límites de cada comuna y respalda una lectura de conectividad entre territorios. Sin embargo, cruzar un límite administrativo no implica necesariamente recorrer una distancia mayor.

  • La conexión 19 → 2 presenta una asimetría direccional: registra 492 viajes, frente a 306 en sentido contrario. El flujo hacia la comuna 2 es 60,8 % mayor, lo que resulta consistente con su papel receptor en el análisis general.

  • La conexión principal no domina el conjunto: aunque 19 → 2 ocupa el primer lugar, representa aproximadamente el 2,4 % de los viajes intercomuna. Su liderazgo debe entenderse como el mayor conteo de una conexión particular, no como un eje que concentre la mayoría de los desplazamientos.

  • Los viajes se distribuyen entre numerosas conexiones: las diez principales reúnen el 13,8 % de los registros intercomuna; el 86,2 % restante corresponde a otros pares. Esto indica que la concentración de llegadas en ciertas comunas puede coexistir con una distribución amplia de conexiones: un destino importante puede recibir viajes desde múltiples orígenes.

  • La planificación requiere combinar destinos y conexiones: los resultados justifican profundizar en el intercambio entre las comunas 19 y 2, junto con las demás conexiones que alimentan los principales destinos. Para traducir estos enlaces en corredores de intervención se necesitan recorridos, modos de transporte y horarios, porque la matriz identifica relaciones entre comunas, pero no las vías utilizadas.

6.3 Generación, atracción y balance

# Parto de las 22 comunas para incluir también aquellas sin registros.
balance <- data.frame(comuna = 1:22) %>%
  left_join(origen_resumen %>% select(comuna, origenes = total), by = "comuna") %>%
  left_join(destino_resumen %>% select(comuna, destinos = total), by = "comuna") %>%
  mutate(origenes = replace_na(origenes, 0), destinos = replace_na(destinos, 0),
         balance = destinos - origenes,
         indice = if_else(destinos + origenes > 0,
                          balance / (destinos + origenes), NA_real_))
stopifnot(sum(balance$balance) == 0)
knitr::kable(balance, digits = 3, caption = "Conteos e indicadores por comuna")
Conteos e indicadores por comuna
comuna origenes destinos balance indice
1 626 187 -439 -0.540
2 2249 3772 1523 0.253
3 1608 2881 1273 0.284
4 1193 1496 303 0.113
5 521 440 -81 -0.084
6 796 570 -226 -0.165
7 583 598 15 0.013
8 862 830 -32 -0.019
9 737 1055 318 0.177
10 1094 741 -353 -0.192
11 717 483 -234 -0.195
12 257 207 -50 -0.108
13 1031 560 -471 -0.296
14 574 316 -258 -0.290
15 974 481 -493 -0.339
16 836 645 -191 -0.129
17 1762 1615 -147 -0.044
18 1208 526 -682 -0.393
19 2240 2438 198 0.042
20 599 303 -296 -0.328
21 693 434 -259 -0.230
22 979 1561 582 0.229
ggplot(balance, aes(x = reorder(factor(comuna), balance), y = balance)) +
  geom_col(fill = "#375b99") + geom_hline(yintercept = 0) + coord_flip() +
  labs(title = "Balance de llegadas y salidas observadas",
       x = "Comuna", y = "Destinos menos orígenes")

El índice de balance territorial permite identificar si en una comuna predominan las llegadas o las salidas y dimensionar ese desequilibrio respecto al movimiento registrado. Se calcula así:

\[ I = \frac{\text{Llegadas} - \text{Salidas}} {\text{Llegadas} + \text{Salidas}} \]

Su valor varía entre −1 y 1:

  • Positivo: se registran más llegadas que salidas.
  • Negativo: se registran más salidas que llegadas.
  • Cercano a cero: ambos conteos son similares, aunque pueden ser altos o bajos.
  • Cercano a los extremos: existe un predominio marcado de uno de los dos sentidos. Un valor de 1 significa que solo se registran llegadas y uno de −1, únicamente salidas. Si ambos conteos son cero, el índice queda sin definir.

A diferencia del balance absoluto, que expresa cuántas llegadas adicionales o salidas adicionales existen, el índice mide el desequilibrio relativo. Por eso una comuna con pocos registros puede presentar un índice elevado sin tener un gran volumen de desplazamientos. Ambas medidas deben interpretarse juntas.

6.3.1 Interpretación de los resultados

  • La comuna 2 combina volumen y predominio de llegadas: registra 3.772 llegadas y 2.249 salidas, con un balance de +1.523 y un índice aproximado de 0,253. Las llegadas representan el 62,6 % de la suma de ambos extremos y las salidas el 37,4 %. Esto confirma su papel receptor dentro del conjunto analizado.

  • Las comunas 2, 3 y 22 presentan los mayores excedentes absolutos de llegadas: destacan como territorios receptores de desplazamientos. Sin embargo, el orden del gráfico corresponde al balance absoluto; no permite establecer cuál presenta el mayor desequilibrio relativo sin comparar sus índices.

  • La comuna 18 presenta el mayor excedente absoluto de salidas: en este territorio se originan más desplazamientos de los que terminan. Para determinar qué tan marcado es ese predominio respecto a su movimiento total se debe consultar su índice, además del saldo negativo.

  • Un balance cercano a cero no implica poca actividad: las comunas con salidas y llegadas similares pueden mantener un intercambio elevado con otros territorios. El índice describe el equilibrio entre ambos conteos, pero no la intensidad total de movilidad.

6.3.2 ¿Qué comunas reciben más de lo que generan?

detalle_balance <- balance %>%
  mutate(pct_salida = 100 * origenes / N_incluido,
         pct_llegada = 100 * destinos / N_incluido,
         diferencia_pp = pct_llegada - pct_salida,
         llegadas_por_100_salidas = if_else(origenes > 0, 100 * destinos / origenes, NA_real_))
receptoras <- detalle_balance %>% filter(balance > 0) %>% arrange(desc(balance), comuna)
generadoras <- detalle_balance %>% filter(balance < 0) %>% arrange(balance, comuna)
knitr::kable(bind_rows(
  receptoras %>% slice_head(n = 3) %>% mutate(Perfil = "Más llegadas"),
  generadoras %>% slice_head(n = 3) %>% mutate(Perfil = "Más salidas")) %>%
    select(Perfil, comuna, origenes, destinos, balance, diferencia_pp, llegadas_por_100_salidas),
  digits = 1, caption = "Mayores diferencias absolutas y llegadas por cada 100 salidas")
Mayores diferencias absolutas y llegadas por cada 100 salidas
Perfil comuna origenes destinos balance diferencia_pp llegadas_por_100_salidas
Más llegadas 2 2249 3772 1523 6.9 167.7
Más llegadas 3 1608 2881 1273 5.8 179.2
Más llegadas 22 979 1561 582 2.6 159.4
Más salidas 18 1208 526 -682 -3.1 43.5
Más salidas 15 974 481 -493 -2.2 49.4
Más salidas 13 1031 560 -471 -2.1 54.3

Se observan 7 comunas con más llegadas, 15 con más salidas y 0 con conteos iguales.

  • Comuna 2: 2.249 salidas y 3.772 llegadas; el saldo es +1.523. Por cada 100 salidas se registran 167,7 llegadas.

  • Comuna 3: 1.608 salidas y 2.881 llegadas; el saldo es +1.273. Por cada 100 salidas se registran 179,2 llegadas.

  • Comuna 22: 979 salidas y 1.561 llegadas; el saldo es +582. Por cada 100 salidas se registran 159,4 llegadas.

  • Comuna 18: 1.208 salidas y 526 llegadas; el saldo es -682. Por cada 100 salidas se registran 43,5 llegadas.

  • Comuna 15: 974 salidas y 481 llegadas; el saldo es -493. Por cada 100 salidas se registran 49,4 llegadas.

  • Comuna 13: 1.031 salidas y 560 llegadas; el saldo es -471. Por cada 100 salidas se registran 54,3 llegadas.

El saldo identifica dónde predominan llegadas o salidas dentro del conjunto observado. La razón por cada 100 salidas ayuda a dimensionar la diferencia, pero puede ser muy alta cuando el número de salidas es pequeño. Ninguna de estas medidas convierte automáticamente una comuna en zona residencial, comercial o laboral; esa explicación requeriría datos del propósito del viaje o del uso del suelo.

6.4 Relación entre salidas y llegadas por comuna

Además del balance, podemos preguntarnos si las comunas con más salidas registradas también tienen más llegadas. Para ello comparamos los dos conteos de las 22 comunas en un diagrama de dispersión y calculamos la correlación de Pearson. No se correlacionan los códigos numéricos de las comunas: esos números son identificadores, no cantidades.

r_od <- if (sd(balance$origenes) > 0 && sd(balance$destinos) > 0) {
  cor(balance$origenes, balance$destinos, method = "pearson")
} else NA_real_
ggplot(balance, aes(origenes, destinos)) +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed", colour = "#98a9b3") +
  geom_point(size = 3.5, colour = "#16847b", alpha = 0.85) +
  geom_text(aes(label = comuna), vjust = -0.7, size = 3.2, check_overlap = TRUE) +
  scale_x_continuous(expand = expansion(mult = c(0.08, 0.12))) +
  scale_y_continuous(expand = expansion(mult = c(0.08, 0.12))) +
  coord_equal() +
  labs(title = "¿Las comunas con más salidas también reciben más llegadas?",
       subtitle = paste0("22 comunas · Pearson r = ", ifelse(is.na(r_od), "no definido", format(round(r_od, 3), decimal.mark = ","))),
       x = "Salidas registradas", y = "Llegadas registradas",
       caption = "Cada punto es una comuna. La diagonal indica igual número de salidas y llegadas.")

La correlación de Pearson (r = 0,876) muestra una asociación lineal positiva fuerte: las comunas con más salidas suelen registrar también más llegadas. Esto indica que los territorios con mayor volumen de movilidad participan de manera importante en ambos extremos del viaje.

Esta asociación no implica equilibrio territorial. Las comunas 2 y 3 combinan conteos elevados con un claro predominio de llegadas; la 22 también destaca por encima de la diagonal. En contraste, comunas como la 18 y la 13 presentan más salidas que llegadas. Así, una relación general fuerte convive con funciones receptoras y generadoras diferenciadas.

El coeficiente resume los conteos de las 22 comunas y puede ser sensible a aquellas con mayores volúmenes, especialmente la 2 y la 3. Su interpretación es descriptiva: no demuestra causalidad, conexiones directas entre comunas ni dependencia espacial.

7 Hallazgos y aplicación

El análisis incluye 22.139 de 35.054 registros (63,2 %). Las tres comunas con más salidas —2, 19, 17— concentran el 28,2 % de los orígenes; las tres con más llegadas —2, 3, 19— reúnen el 41,1 % de los destinos. Esta concentración territorial coexiste con una distribución amplia de conexiones: las diez principales representan el 13,8 % de los viajes intercomuna.

La lectura conjunta de mapas, composición modal y matriz origen–destino permite distinguir dónde se concentra el movimiento, qué vehículos participan y cómo se relacionan los territorios. Estas tres dimensiones orientan las siguientes líneas de análisis:

Hallazgo Aplicación que merece estudiarse Información necesaria para sustentar decisiones
La comuna 2 concentra llegadas de distintos modos Evaluar conjuntamente las condiciones de acceso y llegada a sus actividades Propósitos, horarios, destinos específicos y oferta de transporte
Las comunas presentan balances de distinto signo y magnitud Comparar territorios receptores y generadores mediante saldo, índice y volumen total Cobertura del levantamiento, usos del suelo y distribución horaria
La comuna 19 destaca como origen de automóviles Examinar sus principales destinos y las alternativas disponibles al automóvil Matriz por modo, tiempos, ocupación vehicular y transporte público
El intercambio 19 ↔︎ 2 presenta diferencias entre sentidos Profundizar en la composición y temporalidad de esta conexión Modo, motivo, horario y recorridos efectivos
Los viajes intercomuna predominan y se distribuyen entre múltiples pares Estudiar la conectividad de la red, además de sus enlaces de mayor conteo Transbordos, continuidad de rutas, frecuencias y accesibilidad
Existen comunas con pocos registros Verificar su representación antes de asignarles menor prioridad Diseño muestral, puntos de encuesta, horarios y factores de expansión

Estas aplicaciones constituyen líneas de diagnóstico, no intervenciones ya justificadas. Los conteos no miden directamente congestión, riesgo vial ni déficit de infraestructura; tampoco un cero implica ausencia real de desplazamientos.

8 Conclusiones

  1. La comuna 2 destaca como un territorio de atracción con actividades diversas. Incluye barrios como Granada, Centenario, Chipichape, Versalles, San Vicente y La Flora, entre otros. La oferta gastronómica de Granada, comercial de Chipichape y Centenario, y de alojamiento del entorno avenida Sexta–Chipichape aporta contexto a sus 3.772 llegadas, frente a 2.249 salidas, y a su índice de balance de 0,253. El resultado es compatible con desplazamientos hacia actividades y servicios, aunque identificar sus motivos exige consultar esa variable en la encuesta.

  2. La atracción de la comuna 2 se mantiene entre vehículos, sin una especialización modal marcada. Su liderazgo como destino en bicicleta, motocicleta y automóvil sugiere que distintos modos participan en el acceso a este territorio. Las motocicletas representan el 46,0 % de sus llegadas, proporción cercana al 46,5 % general. Por tanto, su principal rasgo es la concentración de destinos de varios modos, lo que justifica estudiar conjuntamente las condiciones de acceso, circulación y llegada.

  3. La comuna 19 combina funciones residenciales y de servicios que requieren una lectura diferenciada de sus orígenes. Comprende sectores como San Fernando Viejo y Nuevo, Tequendama, Nueva Tequendama, El Lido, El Refugio y Pampalinda, y reúne equipamientos de salud, educación y deporte. El automóvil representa el 41,2 % de sus salidas, frente al 38,8 % general. Su liderazgo en salidas de este modo puede involucrar viajes desde viviendas y desde lugares de actividad; no permite atribuir todos los desplazamientos a residentes ni inferir sus ingresos o tenencia de vehículo.

  4. El intercambio entre las comunas 19 y 2 vincula territorios con actividades diversas, pero su explicación funcional sigue abierta. Se observan 492 viajes en sentido 19 → 2 y 306 en sentido contrario. Esta asimetría es consistente con el papel receptor de la comuna 2, pero no demuestra una relación exclusiva entre vivienda y empleo ni un patrón de ida y regreso. Para establecerla se necesitan motivos, horarios y modos de los viajes de esta conexión.

  5. La estructura observada exige combinar una mirada de red con análisis dentro de cada comuna. El 93,3 % de los viajes conecta comunas diferentes, mientras las diez conexiones principales reúnen el 13,8 % de esos registros. Existen destinos destacados alimentados por múltiples relaciones territoriales. Sin embargo, cada comuna reúne barrios y actividades heterogéneos: sus conteos agregados no permiten identificar qué sectores explican la concentración ni localizar directamente corredores de intervención.

En conjunto, los resultados orientan un diagnóstico de accesibilidad entre territorios y hacia sus actividades. Su aplicación requiere contrastar los patrones con usos del suelo, oferta de transporte, recorridos y cobertura de la encuesta. Las conclusiones describen los registros incluidos, sin expansión muestral, y no estiman por sí solas la demanda total de movilidad de Cali.

Fuentes del contexto territorial: barrios de la comuna 2, barrios de la comuna 19, oferta turística de Cali y cartografía de la comuna 19 — IDESC.

Fuentes de datos. Archivo EncuestaOrigenDestino.xlsx y cartografía Comunas.shp. El periodo observado se obtiene de la columna fechas del Excel. Los empates en rankings y conexiones se resuelven por código ascendente.

## R version 4.5.1 (2025-06-13 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26100)
## 
## Matrix products: default
##   LAPACK version 3.12.1
## 
## locale:
## [1] LC_COLLATE=Spanish_Colombia.utf8  LC_CTYPE=Spanish_Colombia.utf8   
## [3] LC_MONETARY=Spanish_Colombia.utf8 LC_NUMERIC=C                     
## [5] LC_TIME=Spanish_Colombia.utf8    
## 
## time zone: America/Bogota
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] ggplot2_4.0.1 leaflet_2.2.3 sf_1.1-2      tidyr_1.3.2   dplyr_1.1.4  
## [6] readxl_1.4.5 
## 
## loaded via a namespace (and not attached):
##  [1] s2_1.1.11               sass_0.4.10             generics_0.1.4         
##  [4] class_7.3-23            KernSmooth_2.23-26      digest_0.6.39          
##  [7] magrittr_2.0.4          evaluate_1.0.5          grid_4.5.1             
## [10] RColorBrewer_1.1-3      fastmap_1.2.0           cellranger_1.1.0       
## [13] jsonlite_2.0.0          e1071_1.7-17            DBI_1.2.3              
## [16] purrr_1.2.1             viridisLite_0.4.3       crosstalk_1.2.2        
## [19] scales_1.4.0            jquerylib_0.1.4         cli_3.6.5              
## [22] rlang_1.1.7             units_1.0-1             withr_3.0.3            
## [25] cachem_1.1.0            yaml_2.3.12             otel_0.2.0             
## [28] tools_4.5.1             vctrs_0.6.5             R6_2.6.1               
## [31] proxy_0.4-29            lifecycle_1.0.5         classInt_0.4-11        
## [34] leaflet.providers_3.0.0 htmlwidgets_1.6.4       pkgconfig_2.0.3        
## [37] pillar_1.11.1           bslib_0.12.0            gtable_0.3.6           
## [40] glue_1.8.0              Rcpp_1.1.1              xfun_0.55              
## [43] tibble_3.3.1            tidyselect_1.2.1        rstudioapi_0.18.0      
## [46] knitr_1.51              farver_2.1.2            htmltools_0.5.9        
## [49] labeling_0.4.3          rmarkdown_2.31          wk_0.9.5               
## [52] compiler_4.5.1          S7_0.2.1
LS0tDQp0aXRsZTogIkNhbGkgZW4gbW92aW1pZW50byINCnN1YnRpdGxlOiAiRW5jdWVzdGEgb3JpZ2Vu4oCTZGVzdGlubzogY2FsaWRhZCBkZSBkYXRvcyB5IHBhdHJvbmVzIHBvciBjb211bmEiDQphdXRob3I6ICJOaWNvbMOhcyBDb2JvIg0KZGF0ZTogImByIGZvcm1hdChTeXMuRGF0ZSgpLCAnJWQvJW0vJVknKWAiDQpsYW5nOiBlcw0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdG9jX2RlcHRoOiAyDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIHNlbGZfY29udGFpbmVkOiB0cnVlDQogICAgZmlnX2NhcHRpb246IHRydWUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCiMgSW5zdGFsbyBsb3MgcGFxdWV0ZXMgdW5hIHNvbGEgdmV6LCBkZXNkZSBsYSBjb25zb2xhIGRlIFJTdHVkaW86DQojIGluc3RhbGwucGFja2FnZXMoYygicmVhZHhsIiwgImRwbHlyIiwgInRpZHlyIiwgInNmIiwgImxlYWZsZXQiLCAiZ2dwbG90MiIpKQ0KDQpsaWJyYXJ5KHJlYWR4bCkgICAjIExlZSBhcmNoaXZvcyBkZSBFeGNlbC4NCmxpYnJhcnkoZHBseXIpICAgICMgRmlsdHJhLCB0cmFuc2Zvcm1hLCBhZ3J1cGEgeSB1bmUgdGFibGFzLg0KbGlicmFyeSh0aWR5cikgICAgIyBDb21wbGV0YSBjb21iaW5hY2lvbmVzIHkgcmVlbXBsYXphIHZhbG9yZXMgYXVzZW50ZXMuDQpsaWJyYXJ5KHNmKSAgICAgICAjIExlZSB5IHRyYWJhamEgY29uIGRhdG9zIGdlb2dyw6FmaWNvcyB2ZWN0b3JpYWxlcy4NCmxpYnJhcnkobGVhZmxldCkgICMgQ29uc3RydXllIG1hcGFzIGludGVyYWN0aXZvcyBjb24gcG9sw61nb25vcyB5IHZlbnRhbmFzIGRlIGNvbnN1bHRhLg0KbGlicmFyeShnZ3Bsb3QyKSAgIyBDb25zdHJ1eWUgbG9zIGdyw6FmaWNvcyBlc3RhZMOtc3RpY29zLg0KDQojIGVjaG8gPSBUUlVFIGluY2x1eWUgZWwgY8OzZGlnbzsgY29kZV9mb2xkaW5nIHBlcm1pdGUgZGVzcGxlZ2FybG8gZW4gZWwgSFRNTC4NCiMgTGFzIGFkdmVydGVuY2lhcyBubyBzZSBpbXByaW1lbiBlbiBlbCBpbmZvcm1lOyBsb3MgY29udHJvbGVzIGRlIGRhdG9zIHPDrSBzZSBtdWVzdHJhbi4NCiMgUGFyYSBkZXB1cmFjacOzbiBzZSBwdWVkZSBjYW1iaWFyIHdhcm5pbmcgPSBUUlVFIHkgdm9sdmVyIGEgZWplY3V0YXIuDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLmFsaWduID0gImNlbnRlciIpDQp0aGVtZV9zZXQodGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGNvbG91ciA9ICIjMTc0YjYwIiksDQogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gIiM1MjY4NzUiKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLCBjb2xvdXIgPSAiIzUyNjg3NSIpKSkNCmZtdF9uIDwtIGZ1bmN0aW9uKHgpIGZvcm1hdCh4LCBiaWcubWFyayA9ICIuIiwgc2NpZW50aWZpYyA9IEZBTFNFLCB0cmltID0gVFJVRSkNCmZtdF9wIDwtIGZ1bmN0aW9uKHgpIGZvcm1hdChyb3VuZCh4LCAxKSwgZGVjaW1hbC5tYXJrID0gIiwiLCBuc21hbGwgPSAxLCB0cmltID0gVFJVRSkNCg0KIyBsaWJyYXJ5KCkgY2FyZ2EgdW4gcGFxdWV0ZSBpbnN0YWxhZG87IG5vIGxvIGluc3RhbGEuICU+JSBwYXNhIGVsIHJlc3VsdGFkbyBkZSB1bmENCiMgb3BlcmFjacOzbiBhIGxhIHNpZ3VpZW50ZSB5IHNlIHB1ZWRlIGxlZXIgY29tbyDCq2Rlc3B1w6lzwrsuIHBhcXVldGU6OmZ1bmNpb24oKSBwZXJtaXRlIHVzYXINCiMgdW5hIGZ1bmNpw7NuIGluZGljYW5kbyBzdSBwYXF1ZXRlLCBjb21vIGtuaXRyOjprYWJsZSgpLg0KYGBgDQoNCmBgYHtyIGltcG9ydGFyLCBpbmNsdWRlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KIyBDYW1iaW8gw7puaWNhbWVudGUgZXN0YXMgcnV0YXMgc2kgdHJhc2xhZG8gbG9zIGFyY2hpdm9zIGEgb3RybyBlcXVpcG8uDQpjYXJwZXRhIDwtICJDOi9Vc2Vycy9uaWNvbGFzLmNvYm8vT25lRHJpdmUgLSBQVUogQ2FsaS9Fc2NyaXRvcmlvL0Nhc29zIg0KaG9qYSA8LSAiSG9qYTEiICAjIFZlcmlmaWNvIGVzdGUgbm9tYnJlIGNvbiBleGNlbF9zaGVldHMoYXJjaGl2b19leGNlbCkuDQphcmNoaXZvX2V4Y2VsIDwtIGZpbGUucGF0aChjYXJwZXRhLCAiRW5jdWVzdGFPcmlnZW5EZXN0aW5vLnhsc3giKQ0KYXJjaGl2b19tYXBhIDwtIGZpbGUucGF0aChjYXJwZXRhLCAiY2FsaSIsICJDb211bmFzLnNocCIpDQoNCiMgZmlsZS5wYXRoKCkgY29uc3RydXllIHVuYSBydXRhIHNpbiBjb25jYXRlbmFyIG1hbnVhbG1lbnRlIGxhcyBiYXJyYXMuDQpleGNlbF9zaGVldHMoYXJjaGl2b19leGNlbCkgICMgTXVlc3RyYSBsb3Mgbm9tYnJlcyBkZSBsYXMgaG9qYXMgZGlzcG9uaWJsZXMuDQplbmN1ZXN0YSA8LSByZWFkX2V4Y2VsKGFyY2hpdm9fZXhjZWwsIHNoZWV0ID0gaG9qYSkNCmNvbXVuYXMgPC0gc3RfcmVhZChhcmNoaXZvX21hcGEsIHF1aWV0ID0gVFJVRSkNCg0KbmFtZXMoY29tdW5hcykgICAjIFJldmlzbyBsb3MgYXRyaWJ1dG9zIGRlIGxhIGNhcnRvZ3JhZsOtYS4NCm5hbWVzKGVuY3Vlc3RhKSAgIyBJZGVudGlmaWNvIGPDs21vIHNlIGxsYW1hbiByZWFsbWVudGUgbGFzIGNvbHVtbmFzLg0Ka25pdHI6OmthYmxlKGhlYWQoZW5jdWVzdGEsIDUpLCBjYXB0aW9uID0gIlByaW1lcm9zIGNpbmNvIHJlZ2lzdHJvczogcmV2aXNpw7NuIGxvY2FsIikNCg0KIyA8LSBhc2lnbmEgdW4gcmVzdWx0YWRvIGEgdW4gb2JqZXRvLiBoZWFkKHgsIDUpIHBlcm1pdGUgbWlyYXIgY2luY28gZmlsYXMgeSBuYW1lcyh4KQ0KIyBtdWVzdHJhIGxvcyBub21icmVzIGRlIGxhcyBjb2x1bW5hcy4gTGFzIGNvbWlsbGFzIGludmVydGlkYXMgcGVybWl0ZW4gcmVmZXJpcnNlIGEgbm9tYnJlcw0KIyBxdWUgY29udGllbmVuIGVzcGFjaW9zIG8gdGlsZGVzLCBjb21vICBUSVBPIERFIFZFSMONQ1VMTyAuDQpgYGANCg0KYGBge3IgcHJlcGFyYXItZmVjaGFzLCBpbmNsdWRlPUZBTFNFfQ0KIyBMb2NhbGl6byBsYSBjb2x1bW5hIHNpbiBkZXBlbmRlciBkZSBtYXnDunNjdWxhcywgdGlsZGVzIG8gZXNwYWNpb3MuDQpjbGF2ZXNfZmVjaGEgPC0gZ3N1YigiW15hLXowLTldIiwgIiIsIHRvbG93ZXIoaWNvbnYobmFtZXMoZW5jdWVzdGEpLCB0byA9ICJBU0NJSS8vVFJBTlNMSVQiKSkpDQpjb2x1bW5hc19mZWNoYSA8LSBuYW1lcyhlbmN1ZXN0YSlbY2xhdmVzX2ZlY2hhICVpbiUgYygiZmVjaGFzIiwgImZlY2hhIildDQppZiAobGVuZ3RoKGNvbHVtbmFzX2ZlY2hhKSAhPSAxKSB7DQogIHN0b3AoIlNlIHJlcXVpZXJlIHVuYSDDum5pY2EgY29sdW1uYSBmZWNoYXMgcGFyYSBjYWxjdWxhciBlbCBwZXJpb2RvIGRlbCByZXBvcnRlLiIpDQp9DQpjb2x1bW5hX2ZlY2hhIDwtIGNvbHVtbmFzX2ZlY2hhWzFdDQpjb252ZXJ0aXJfZmVjaGFzIDwtIGZ1bmN0aW9uKHgpIHsNCiAgaWYgKGluaGVyaXRzKHgsICJEYXRlIikpIHJldHVybih4KQ0KICBpZiAoaW5oZXJpdHMoeCwgIlBPU0lYdCIpKSByZXR1cm4oYXMuRGF0ZSh4KSkNCiAgIyBFbCBvcmlnZW4gY29ycmVzcG9uZGUgYWwgc2lzdGVtYSBkZSBmZWNoYXMgaGFiaXR1YWwgZGUgRXhjZWwgcGFyYSBXaW5kb3dzLg0KICBpZiAoaXMubnVtZXJpYyh4KSkgcmV0dXJuKGFzLkRhdGUoeCwgb3JpZ2luID0gIjE4OTktMTItMzAiKSkNCiAgdGV4dG8gPC0gdHJpbXdzKGFzLmNoYXJhY3Rlcih4KSkNCiAgc2FsaWRhIDwtIGFzLkRhdGUocmVwKE5BX2NoYXJhY3Rlcl8sIGxlbmd0aCh0ZXh0bykpKQ0KICAjIEFjZXB0byBmZWNoYXMgSVNPIG8gZMOtYS9tZXMvYcOxbywgY29uIG8gc2luIHVuYSBob3JhIGEgY29udGludWFjacOzbi4NCiAgaXNvIDwtICFpcy5uYSh0ZXh0bykgJiBncmVwbCgiXlswLTldezR9LVswLTldezJ9LVswLTldezJ9KCR8WyBUXSkiLCB0ZXh0bykNCiAgc2FsaWRhW2lzb10gPC0gc3VwcHJlc3NXYXJuaW5ncyhhcy5EYXRlKHN1YnN0cih0ZXh0b1tpc29dLCAxLCAxMCksIGZvcm1hdCA9ICIlWS0lbS0lZCIpKQ0KICBmb3IgKGZvcm1hdG8gaW4gYygiJWQvJW0vJVkiLCAiJWQtJW0tJVkiLCAiJVkvJW0vJWQiKSkgew0KICAgIHBhdHJvbiA8LSBzd2l0Y2goZm9ybWF0bywNCiAgICAgICIlZC8lbS8lWSIgPSAiXlswLTldezEsMn0vWzAtOV17MSwyfS9bMC05XXs0fSgkfFsgVF0pIiwNCiAgICAgICIlZC0lbS0lWSIgPSAiXlswLTldezEsMn0tWzAtOV17MSwyfS1bMC05XXs0fSgkfFsgVF0pIiwNCiAgICAgICIlWS8lbS8lZCIgPSAiXlswLTldezR9L1swLTldezEsMn0vWzAtOV17MSwyfSgkfFsgVF0pIikNCiAgICBwZW5kaWVudGVzIDwtIGlzLm5hKHNhbGlkYSkgJiAhaXMubmEodGV4dG8pICYgZ3JlcGwocGF0cm9uLCB0ZXh0bykNCiAgICBzYWxpZGFbcGVuZGllbnRlc10gPC0gc3VwcHJlc3NXYXJuaW5ncyhhcy5EYXRlKHN1YigiWyBUXS4qJCIsICIiLCB0ZXh0b1twZW5kaWVudGVzXSksIGZvcm1hdCA9IGZvcm1hdG8pKQ0KICB9DQogIHNhbGlkYQ0KfQ0KZmVjaGFzX3JlZ2lzdHJvIDwtIGNvbnZlcnRpcl9mZWNoYXMoZW5jdWVzdGFbW2NvbHVtbmFfZmVjaGFdXSkNCmZlY2hhc192YWxpZGFzIDwtIGZlY2hhc19yZWdpc3Ryb1shaXMubmEoZmVjaGFzX3JlZ2lzdHJvKV0NCmBgYA0KDQoNCjxzdHlsZT4NCmJvZHkge2NvbG9yOiMyNjM3NDY7IGxpbmUtaGVpZ2h0OjEuNzU7IGZvbnQtc2l6ZToxNnB4OyBiYWNrZ3JvdW5kOiNmNWY4ZmI7fQ0KLm1haW4tY29udGFpbmVyIHttYXgtd2lkdGg6MTE4MHB4OyBiYWNrZ3JvdW5kOndoaXRlOyBwYWRkaW5nOjM1cHggNDVweDt9DQpoMSxoMixoMyB7Y29sb3I6IzE3NGI2MDsgZm9udC13ZWlnaHQ6NjUwO30NCmgxIHttYXJnaW4tdG9wOjM4cHg7IHBhZGRpbmctYm90dG9tOjEycHg7IGJvcmRlci1ib3R0b206MnB4IHNvbGlkICNkY2ViZWU7fQ0KLnRpdGxlIHtmb250LXNpemU6NDJweDsgYm9yZGVyLWJvdHRvbTowO30NCi5zdWJ0aXRsZSB7Y29sb3I6IzQ0NzY4MTt9DQpwIHttYXJnaW4tYm90dG9tOjE2cHg7fQ0KLnRhYmxlIHtmb250LXNpemU6MTRweDsgYmFja2dyb3VuZDp3aGl0ZTt9DQoudGFibGUgdGhlYWQge2JhY2tncm91bmQ6IzE3NGI2MDsgY29sb3I6d2hpdGU7fQ0KLnRhYmxlIHRib2R5IHRyOm50aC1jaGlsZChldmVuKSB7YmFja2dyb3VuZDojZjBmNmY4O30NCnByZSB7Ym9yZGVyOjA7IGJvcmRlci1sZWZ0OjRweCBzb2xpZCAjMmE5ZDhmOyBiYWNrZ3JvdW5kOiNmMWY2Zjg7fQ0KYmxvY2txdW90ZSB7Ym9yZGVyLWxlZnQ6NHB4IHNvbGlkICNlOWI0NGM7IGJhY2tncm91bmQ6I2ZmZjhlOTsgcGFkZGluZzoxNXB4IDIwcHg7fQ0KLmxlYWZsZXQtY29udGFpbmVyIHtib3JkZXItcmFkaXVzOjEycHg7IGJvcmRlcjoxcHggc29saWQgI2RjZWJlZTsgbWFyZ2luOjE4cHggMCAyNXB4O30NCiNUT0Mge2ZvbnQtc2l6ZToxM3B4O30NCkBtZWRpYShtYXgtd2lkdGg6NzY3cHgpIHsubWFpbi1jb250YWluZXIge3BhZGRpbmc6MjBweDt9IC50aXRsZSB7Zm9udC1zaXplOjMycHg7fX0NCjwvc3R5bGU+DQoNCiMgSW50cm9kdWNjacOzbiB5IG9iamV0aXZvDQoNCioqwr9Ew7NuZGUgZW1waWV6YW4geSB0ZXJtaW5hbiBsb3MgdmlhamVzIHJlZ2lzdHJhZG9zIGRlbnRybyBkZSBDYWxpIHkgY8OzbW8gY2FtYmlhIHN1IGRpc3RyaWJ1Y2nDs24gc2Vnw7puIGVsIG1lZGlvIGRlIHRyYW5zcG9ydGU/KioNCg0KRXN0ZSB0cmFiYWpvIGFuYWxpemEgbGEgZGlzdHJpYnVjacOzbiBkZSBsb3Mgb3LDrWdlbmVzIHkgZGVzdGlub3MgZGUgbG9zIGRlc3BsYXphbWllbnRvcyByZWdpc3RyYWRvcyBkZW50cm8gZGUgQ2FsaSwgdGFudG8gZGUgZm9ybWEgZ2VuZXJhbCBjb21vIHBvciBtZWRpbyBkZSB0cmFuc3BvcnRlLiBFbCBhbsOhbGlzaXMgaW50ZWdyYSBsYSBwcmVwYXJhY2nDs24gZGUgbGEgZW5jdWVzdGEsIGxhIHZlcmlmaWNhY2nDs24gZGUgY2FsaWRhZCBkZSBsb3MgZGF0b3MsIGxhIGNvbnN0cnVjY2nDs24gZGUgbWFwYXMgaW50ZXJhY3Rpdm9zLCB1bmEgbWF0cml6IG9yaWdlbuKAk2Rlc3Rpbm8geSB1biBiYWxhbmNlIHRlcnJpdG9yaWFsIHBvciBjb211bmEuDQoNCkxvcyBkYXRvcyBkZSBsb3MgZGVzcGxhemFtaWVudG9zIHByb3ZpZW5lbiBkZWwgYXJjaGl2byAqKmBFbmN1ZXN0YU9yaWdlbkRlc3Rpbm8ueGxzeGAqKiwgbGXDrWRvIGVuIGxhIGhvamEgKipgSG9qYTFgKiouIFBhcmEgdWJpY2FyIGxvcyByZXN1bHRhZG9zIHNlIHV0aWxpemEgbGEgY2FydG9ncmFmw61hICoqYENvbXVuYXMuc2hwYCoqLCBxdWUgY29udGllbmUgbG9zIGzDrW1pdGVzIGRlIGxhcyBjb211bmFzIGRlIENhbGkuIFNvbiBkb3MgaW5zdW1vcyBkaWZlcmVudGVzOiBlbCBFeGNlbCBkZXNjcmliZSBsb3MgcmVnaXN0cm9zIGRlIG1vdmlsaWRhZCB5IGxhIGNhcGEgZ2VvZ3LDoWZpY2EgcGVybWl0ZSByZXByZXNlbnRhcmxvcyBlbiBlbCB0ZXJyaXRvcmlvLg0KDQpgYGB7ciBwZXJpb2RvLWludHJvZHVjY2lvbiwgZWNobz1GQUxTRSwgcmVzdWx0cz0nYXNpcyd9DQppZiAobGVuZ3RoKGZlY2hhc192YWxpZGFzKSA+IDApIHsNCiAgY2F0KCJMb3MgcmVnaXN0cm9zIGNvbiBmZWNoYSB2w6FsaWRhIGFiYXJjYW4gZGVsICoqIiwNCiAgICAgIGZvcm1hdChtaW4oZmVjaGFzX3ZhbGlkYXMpLCAiJWQvJW0vJVkiKSwgIioqIGFsICoqIiwNCiAgICAgIGZvcm1hdChtYXgoZmVjaGFzX3ZhbGlkYXMpLCAiJWQvJW0vJVkiKSwNCiAgICAgICIqKiwgc2Vnw7puIGxhIGNvbHVtbmEgYGZlY2hhc2AgZGVsIEV4Y2VsLiBFc3RlIGludGVydmFsbyBjb3JyZXNwb25kZSBhIGxhcyBmZWNoYXMgb2JzZXJ2YWRhcyBlbiBsYSBiYXNlLlxuXG4iLCBzZXAgPSAiIikNCn0NCmBgYA0KDQpFbCBvYmpldGl2byBlcyByZWNvbm9jZXIgZMOzbmRlIHNlIGNvbmNlbnRyYW4gbGFzIHNhbGlkYXMgeSBsbGVnYWRhcywgY29tcGFyYXIgbG9zIG1vZG9zIGRlIHRyYW5zcG9ydGUgeSBjdWFudGlmaWNhciBsYXMgZGlmZXJlbmNpYXMgZW50cmUgbGFzIGNvbXVuYXMsIHN1cyBjb25leGlvbmVzIHkgc3UgYmFsYW5jZSBkZSBzYWxpZGFzIHkgbGxlZ2FkYXMuIEFudGVzIGRlIGludGVycHJldGFyIGxvcyBwYXRyb25lcywgcmV2aXNhbW9zIHF1w6kgcmVnaXN0cm9zIGN1ZW50YW4gY29uIGluZm9ybWFjacOzbiBzdWZpY2llbnRlIHBhcmEgdWJpY2FybG9zLg0KDQpMYSB1bmlkYWQgZGUgYW7DoWxpc2lzIGVzICoqdW4gcmVnaXN0cm8gZGUgZGVzcGxhemFtaWVudG8qKi4gTG9zIHJlc3VsdGFkb3Mgbm8gcmVwcmVzZW50YW4gcGVyc29uYXMgw7puaWNhcyBuaSB0b2RvcyBsb3MgdmlhamVzIGRlIENhbGk6IHBhcmEgaGFjZXIgZXNhIGVzdGltYWNpw7NuIHNlIG5lY2VzaXRhbiBmYWN0b3JlcyBkZSBleHBhbnNpw7NuIHkgY29ub2NlciBlbCBkaXNlw7FvIG11ZXN0cmFsLg0KDQojIFByZXBhcmFjacOzbiBlIGltcG9ydGFjacOzbg0KDQojIyDCv0NvbiBxdcOpIHZhbW9zIGEgdHJhYmFqYXI/DQoNClVzYXJlbW9zICoqUioqIHBhcmEgb3JnYW5pemFyIGxvcyBkYXRvcyB5IGhhY2VyIGxvcyBjw6FsY3Vsb3MsIHkgKipSU3R1ZGlvKiogY29tbyBlbnRvcm5vIGRlIHRyYWJham8gcGFyYSBhYnJpciB5IGVqZWN1dGFyIGVsIGFyY2hpdm8uIEVsIGRvY3VtZW50byBlc3TDoSBlc2NyaXRvIGVuICoqUiBNYXJrZG93bioqOiBlbiDDqWwgY29udml2ZW4gZWwgdGV4dG8sIGVsIGPDs2RpZ28geSBsb3MgcmVzdWx0YWRvcywgZGUgbW9kbyBxdWUgbGFzIHRhYmxhcywgbG9zIGdyw6FmaWNvcyB5IGxvcyBtYXBhcyBzZSBhY3R1YWxpemFuIGFsIGdlbmVyYXIgZWwgaW5mb3JtZSBlbiBIVE1MLg0KDQpDYWRhIHBhcXVldGUgdGllbmUgdW5hIHRhcmVhIGNvbmNyZXRhLiBgcmVhZHhsYCBhYnJlIGVsIEV4Y2VsOyBgZHBseXJgIHkgYHRpZHlyYCBheXVkYW4gYSBvcmRlbmFyIHkgcmVzdW1pciBsYSBpbmZvcm1hY2nDs247IGBzZmAgbGVlIHkgcHJlcGFyYSBsYXMgY29tdW5hczsgYGdncGxvdDJgIGRpYnVqYSBsb3MgZ3LDoWZpY29zLCB5IGBsZWFmbGV0YCBjb25zdHJ1eWUgbG9zIG1hcGFzIHF1ZSBwb2RlbW9zIGV4cGxvcmFyIGNvbiBlbCBjdXJzb3IuDQoNCiMjIFBhcXVldGVzIHkgb3BjaW9uZXMNCg0KYGBge3IgbW9zdHJhci1zZXR1cCwgcmVmLmxhYmVsPSdzZXR1cCcsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0NCmBgYA0KDQoNCiMjIEltcG9ydGFjacOzbiBkZSBsb3MgZGF0b3MNCg0KUHJpbWVybyBpbmRpY2Ftb3MgbGEgY2FycGV0YSBkb25kZSBlc3TDoW4gbG9zIGluc3Vtb3MuIENvbiBgZmlsZS5wYXRoKClgIGFybWFtb3MgbGEgcnV0YSBkZWwgRXhjZWwgeSBsYSBkZWwgbWFwYS4gTHVlZ28gY29uc3VsdGFtb3MgbGFzIGhvamFzIGRpc3BvbmlibGVzIGNvbiBgZXhjZWxfc2hlZXRzKClgIHkgbGVlbW9zIGBIb2phMWAgY29uIGByZWFkX2V4Y2VsKClgOiBjYWRhIGZpbGEgcXVlZGEgY29tbyB1biByZWdpc3RybyB5IGNhZGEgZW5jYWJlemFkbyBjb21vIHVuYSBjb2x1bW5hLg0KDQpMYSBjYXJ0b2dyYWbDrWEgc2UgaW1wb3J0YSBjb24gYHN0X3JlYWQoKWAuIFVuICpzaGFwZWZpbGUqIG5vIGVzIHVuIMO6bmljbyBhcmNoaXZvOiAqKmAuc2hwYCBndWFyZGEgbGFzIGZvcm1hcywgYC5kYmZgIHN1cyBhdHJpYnV0b3MsIGAuc2h4YCBzdSDDrW5kaWNlIHkgYC5wcmpgIGVsIHNpc3RlbWEgZGUgY29vcmRlbmFkYXMqKi4gRGViZW4gZXN0YXIganVudG9zIHkgY29tcGFydGlyIGVsIG5vbWJyZSBgQ29tdW5hc2AuIE5vIHNvbiBjb2x1bW5hcyBgLnNoYDogc29uIGFyY2hpdm9zIGNvbXBsZW1lbnRhcmlvcyBkZSB1bmEgbWlzbWEgY2FwYSBnZW9ncsOhZmljYS4gU3VzIGNvbHVtbmFzIGRlIGF0cmlidXRvcyBzZSBwdWVkZW4gY29uc3VsdGFyIGNvbiBgbmFtZXMoY29tdW5hcylgOyBsYSBjb2x1bW5hIGRlIGdlb21ldHLDrWEgY29udGllbmUgbG9zIHBvbMOtZ29ub3MuDQoNCg0KYGBge3IgbW9zdHJhci1pbXBvcnRhciwgcmVmLmxhYmVsPSdpbXBvcnRhcicsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0NCmBgYA0KDQpgYGB7ciB2aXN0YS1pbXBvcnRhY2lvbn0NCm5hbWVzKGNvbXVuYXMpDQpuYW1lcyhlbmN1ZXN0YSkNCmtuaXRyOjprYWJsZShoZWFkKGVuY3Vlc3RhLCA1KSwgY2FwdGlvbiA9ICJQcmltZXJvcyBjaW5jbyByZWdpc3Ryb3M6IHJldmlzacOzbiBsb2NhbCIpDQpgYGANCg0KDQoNCiMgUHJlcGFyYWNpw7NuIHkgY2FsaWRhZCBkZSBsb3MgZGF0b3MNCg0KIyMgSWRlbnRpZmljYWNpw7NuIHRlcnJpdG9yaWFsIHkgcHJlcGFyYWNpw7NuIGRlIHZhcmlhYmxlcw0KDQpFbiBsb3MgbWFwYXMgc2UgdXNhICoqZWwgbWlzbW8gY29uanVudG8gZGUgdmlhamVzIHBhcmEgb3JpZ2VuIHkgZGVzdGlubyoqOiBhbWJvcyBtdW5pY2lwaW9zIGRlYmVuIHNlciBDYWxpIHkgYW1iYXMgY29tdW5hcyBkZWJlbiBzZXIgZW50ZXJvcyBlbnRyZSAxIHkgMjIuIExvcyB2aWFqZXMgaW50ZXJtdW5pY2lwYWxlcyBzZSBleGNsdXllbiBkZSBlc3RlIGFuw6FsaXNpcywgYXVucXVlIG5vIG5lY2VzYXJpYW1lbnRlIHNvbiBlcnJvcmVzLg0KDQpgYGB7ciByZXZpc2FyLWNvbHVtbmFzfQ0KIyBSZXZpc28gbG9zIGVuY2FiZXphZG9zIHJlYWxlcyBhbnRlcyBkZSB0cmFuc2Zvcm1hciBsYSBlbmN1ZXN0YS4NCg0KIyBDcmVvIGNsYXZlcyBkZSBjb21wYXJhY2nDs24gc2luIGNhbWJpYXIgbG9zIG5vbWJyZXMgb3JpZ2luYWxlcyBkZWwgRXhjZWwuDQojIGljb252KCkgcXVpdGEgdGlsZGVzOyB0b2xvd2VyKCkgcGFzYSBhIG1pbsO6c2N1bGFzLg0KIyBnc3ViKCkgZWxpbWluYSBlc3BhY2lvcywgc2FsdG9zIGRlIGzDrW5lYSwgZ3Vpb25lcyB5IG90cm9zIHNpZ25vcy4NCmNsYXZlcyA8LSBpY29udihuYW1lcyhlbmN1ZXN0YSksIGZyb20gPSAiIiwgdG8gPSAiQVNDSUkvL1RSQU5TTElUIikNCmNsYXZlcyA8LSBnc3ViKCJbXmEtejAtOV0iLCAiIiwgdG9sb3dlcihjbGF2ZXMpKQ0Ka25pdHI6OmthYmxlKGRhdGEuZnJhbWUoY29sdW1uYV9vcmlnaW5hbCA9IG5hbWVzKGVuY3Vlc3RhKSwgY2xhdmUgPSBjbGF2ZXMpLA0KICAgICAgICAgICAgIGNhcHRpb24gPSAiRW5jYWJlemFkb3MgZGlzcG9uaWJsZXMgZW4gbGEgaG9qYSBzZWxlY2Npb25hZGEiKQ0KDQojIEJ1c2NvIGNvaW5jaWRlbmNpYXMgZXhhY3RhcyBlbnRyZSBjbGF2ZXMgeSByZWN1cGVybyBlbCBub21icmUgb3JpZ2luYWwuDQojIE5vIGVsaWpvIHVuYSBjb2x1bW5hIHBvciBzdXBvc2ljacOzbiBzaSBmYWx0YSBvIGhheSB2YXJpYXMgY29pbmNpZGVuY2lhcy4NCmJ1c2Nhcl9jb2x1bW5hIDwtIGZ1bmN0aW9uKGNhbmRpZGF0b3MpIHsNCiAgY29pbmNpZGVuY2lhcyA8LSBuYW1lcyhlbmN1ZXN0YSlbd2hpY2goY2xhdmVzICVpbiUgY2FuZGlkYXRvcyldDQogIGlmIChsZW5ndGgoY29pbmNpZGVuY2lhcykgIT0gMSkgew0KICAgIHN0b3AoIkNvbHVtbmEgYXVzZW50ZSBvIGFtYmlndWE6ICIsIHBhc3RlKGNhbmRpZGF0b3MsIGNvbGxhcHNlID0gIiAvICIpLA0KICAgICAgICAgIi4gUmV2aXNlIGxhIGhvamEgc2VsZWNjaW9uYWRhIHkgZXN0b3MgZW5jYWJlemFkb3M6ICIsDQogICAgICAgICBwYXN0ZShuYW1lcyhlbmN1ZXN0YSksIGNvbGxhcHNlID0gIiB8ICIpKQ0KICB9DQogIGNvaW5jaWRlbmNpYXMNCn0NCmNvbHVtbmFfb3JpZ2VuIDwtIGJ1c2Nhcl9jb2x1bW5hKGMoImNvbXVuYW9yaWdlbiIsICJjb211bmFkZW9yaWdlbiIpKQ0KY29sdW1uYV9kZXN0aW5vIDwtIGJ1c2Nhcl9jb2x1bW5hKGMoImNvbXVuYWRlc3Rpbm8iLCAiY29tdW5hZGVkZXN0aW5vIikpDQpjb2x1bW5hX3ZlaGljdWxvIDwtIGJ1c2Nhcl9jb2x1bW5hKGMoInRpcG9kZXZlaGljdWxvIiwgInRpcG92ZWhpY3VsbyIpKQ0KIyBMYSBjbGF2ZSBjb211bmFvcmlnZW4gcmVjb25vY2UgZW5jYWJlemFkb3MgY29tbyBDT01VTkEgT1JJR0VOLCBDb211bmFfT3JpZ2VuIG8gY29tdW5hDQojIG9yaWdlbiA7IHRhbWJpw6luIHNlIGFkbWl0ZSBjb211bmEgZGUgb3JpZ2VuLiBFc3RhIHJldmlzacOzbiBubyBjYW1iaWEgZWwgY29udGVuaWRvIGRlIGxhcw0KIyBjb2x1bW5hcy4NCmBgYA0KDQoNCmBgYHtyIHByZXBhcmFyfQ0KIyBJZGVudGlmaWNvIGxvcyBtdW5pY2lwaW9zIHBvciBzdXMgbm9tYnJlcyBkZXNjcmlwdGl2b3Mgbm9ybWFsaXphZG9zLg0KIyBTaSBhbWJvcyBzZSBsbGFtYW4gTVVOSUNJUElPLCByZWFkX2V4Y2VsKCkgYcOxYWRlIHN1Zmlqb3MgY29tbyAuLi41IHkgLi4uOS4NCmlmIChzdW0oY2xhdmVzICVpbiUgYygibXVuaWNpcGlvb3JpZ2VuIiwgIm11bmljaXBpb2Rlb3JpZ2VuIikpID09IDEgJiYNCiAgICBzdW0oY2xhdmVzICVpbiUgYygibXVuaWNpcGlvZGVzdGlubyIsICJtdW5pY2lwaW9kZWRlc3Rpbm8iKSkgPT0gMSkgew0KICBjb2x1bW5hc19tdW5pY2lwaW8gPC0gYygNCiAgICBidXNjYXJfY29sdW1uYShjKCJtdW5pY2lwaW9vcmlnZW4iLCAibXVuaWNpcGlvZGVvcmlnZW4iKSksDQogICAgYnVzY2FyX2NvbHVtbmEoYygibXVuaWNpcGlvZGVzdGlubyIsICJtdW5pY2lwaW9kZWRlc3Rpbm8iKSkNCiAgKQ0KfSBlbHNlIHsNCiAgY29sdW1uYXNfbXVuaWNpcGlvIDwtIG5hbWVzKGVuY3Vlc3RhKVt3aGljaChncmVwbCgiXm11bmljaXBpb1swLTldKiQiLCBjbGF2ZXMpKV0NCn0NCmlmIChsZW5ndGgoY29sdW1uYXNfbXVuaWNpcGlvKSAhPSAyKSB7DQogIHN0b3AoIlJldmlzZSBsb3MgZW5jYWJlemFkb3M6IHNlIG5lY2VzaXRhbiBkb3MgY29sdW1uYXMgZGUgbXVuaWNpcGlvIChvcmlnZW4geSBkZXN0aW5vKS4iKQ0KfQ0KY29sdW1uYXNfbXVuaWNpcGlvICAjIEVuIGVuY2FiZXphZG9zIHJlcGV0aWRvcywgY29uZmlybW8gZWwgb3JkZW4gb3JpZ2Vu4oCTZGVzdGluby4NCg0KIyBVbmlmaWNvIHRpbGRlcywgbWF5w7pzY3VsYXMgeSBlc3BhY2lvczsgY29uc2Vydm8gbGEgcmVzcHVlc3RhIG9yaWdpbmFsIGVuIGVuY3Vlc3RhLg0Kbm9ybWFsaXphcl90ZXh0byA8LSBmdW5jdGlvbih4KSB7DQogIHggPC0gdG91cHBlcih0cmltd3MoYXMuY2hhcmFjdGVyKHgpKSkNCiAgeCA8LSBpY29udih4LCBmcm9tID0gIiIsIHRvID0gIkFTQ0lJLy9UUkFOU0xJVCIpDQogIGdzdWIoIltbOnNwYWNlOl1dKyIsICIgIiwgeCkNCn0NCm1hcmNhZG9yZXNfYXVzZW5jaWEgPC0gYygiIiwgIk5BIiwgIk4vQSIsICJOVUxMIiwgIlNJTiBEQVRPIiwgIlNJTiBJTkZPUk1BQ0lPTiIpDQojIENvbnNlcnZvIGVuY3Vlc3RhIGludGFjdGEgeSBjcmVvIHVuYSB0YWJsYSBwYXJhIGVsIGFuw6FsaXNpcy4NCiMgc3ViKCkgcXVpdGEgZWwgcHJlZmlqbyBDT01VTkE7IHRvdXBwZXIoKSB5IHRyaW13cygpIHVuaWZpY2FuIGVsIHRleHRvLg0KIyBhcy5udW1lcmljKCkgY29udmllcnRlICJDb211bmEgMDIiLCAiQ09NVU5BIDIiIHkgIjAyIiBhbCBuw7ptZXJvIDIuDQojIHN1cHByZXNzV2FybmluZ3MoKSBzZSBsaW1pdGEgYSBlc3RhIGNvbnZlcnNpw7NuOiByZXZpc28gbG9zIE5BIG3DoXMgYWJham8uDQpkYXRvcyA8LSBlbmN1ZXN0YSAlPiUNCiAgbXV0YXRlKA0KICAgIG11bmljaXBpb19vcmlnZW4gPSBub3JtYWxpemFyX3RleHRvKC5kYXRhW1tjb2x1bW5hc19tdW5pY2lwaW9bMV1dXSksDQogICAgbXVuaWNpcGlvX2Rlc3Rpbm8gPSBub3JtYWxpemFyX3RleHRvKC5kYXRhW1tjb2x1bW5hc19tdW5pY2lwaW9bMl1dXSksDQogICAgb3JpZ2VuID0gc3VwcHJlc3NXYXJuaW5ncyhhcy5udW1lcmljKHN1YigiXkNPTVVOQVtbOnNwYWNlOl1dKiIsICIiLA0KICAgICAgdG91cHBlcih0cmltd3MoYXMuY2hhcmFjdGVyKC5kYXRhW1tjb2x1bW5hX29yaWdlbl1dKSkpKSkpLA0KICAgIGRlc3Rpbm8gPSBzdXBwcmVzc1dhcm5pbmdzKGFzLm51bWVyaWMoc3ViKCJeQ09NVU5BW1s6c3BhY2U6XV0qIiwgIiIsDQogICAgICB0b3VwcGVyKHRyaW13cyhhcy5jaGFyYWN0ZXIoLmRhdGFbW2NvbHVtbmFfZGVzdGlub11dKSkpKSkpLA0KICAgIHZlaGljdWxvID0gbm9ybWFsaXphcl90ZXh0byguZGF0YVtbY29sdW1uYV92ZWhpY3Vsb11dKSwNCiAgICBtb2RvID0gY2FzZV93aGVuKA0KICAgICAgdmVoaWN1bG8gJWluJSBjKCIxIiwgIjEuMCIsICJCSUNJQ0xFVEEiLCAiQklDSSIpIH4gIkJpY2ljbGV0YSIsDQogICAgICB2ZWhpY3VsbyAlaW4lIGMoIjIiLCAiMi4wIiwgIk1PVE8iLCAiTU9UT0NJQ0xFVEEiKSB+ICJNb3RvY2ljbGV0YSIsDQogICAgICB2ZWhpY3VsbyAlaW4lIGMoIjMiLCAiMy4wIiwgIkFVVE9NT1ZJTCIsICJBVVRPTcOTVklMIiwgIkFVVE8iLCAiQ0FSUk8iKSB+ICJBdXRvbcOzdmlsIiwNCiAgICAgIGlzLm5hKHZlaGljdWxvKSB8IHZlaGljdWxvICVpbiUgbWFyY2Fkb3Jlc19hdXNlbmNpYSB+ICJTaW4gaW5mb3JtYWNpw7NuIiwNCiAgICAgIFRSVUUgfiAiT3RybyBtb2RvIG8gY8OzZGlnbyINCiAgICApLA0KICAgIGR1cGxpY2FkbyA9IGR1cGxpY2F0ZWQoZW5jdWVzdGEpLA0KICAgIGNvbnRyb2wgPSBjYXNlX3doZW4oDQogICAgICBpcy5uYShtdW5pY2lwaW9fb3JpZ2VuKSB8IGlzLm5hKG11bmljaXBpb19kZXN0aW5vKSB8DQogICAgICAgIG11bmljaXBpb19vcmlnZW4gJWluJSBtYXJjYWRvcmVzX2F1c2VuY2lhIHwNCiAgICAgICAgbXVuaWNpcGlvX2Rlc3Rpbm8gJWluJSBtYXJjYWRvcmVzX2F1c2VuY2lhIH4gIk11bmljaXBpbyBzaW4gaW5mb3JtYWNpw7NuIiwNCiAgICAgICFtdW5pY2lwaW9fb3JpZ2VuICVpbiUgYygiQ0FMSSIsICJTQU5USUFHTyBERSBDQUxJIikgfA0KICAgICAgICAhbXVuaWNpcGlvX2Rlc3Rpbm8gJWluJSBjKCJDQUxJIiwgIlNBTlRJQUdPIERFIENBTEkiKSB+ICJWaWFqZSBleHRlcm5vIG8gbXVuaWNpcGlvIHBvciByZXZpc2FyIiwNCiAgICAgIGlzLm5hKG9yaWdlbikgfCBpcy5uYShkZXN0aW5vKSB8DQogICAgICAgICFvcmlnZW4gJWluJSAxOjIyIHwgIWRlc3Rpbm8gJWluJSAxOjIyIH4gIkNvbXVuYSBhdXNlbnRlIG8gaW52w6FsaWRhIiwNCiAgICAgIFRSVUUgfiAiSW5jbHVpZG8iDQogICAgKQ0KICApDQp2aWFqZXMgPC0gZGF0b3MgJT4lIGZpbHRlcihjb250cm9sID09ICJJbmNsdWlkbyIpDQppZiAobnJvdyh2aWFqZXMpID09IDApIHN0b3AoIk5vIGhheSB2aWFqZXMgaW5jbHVpZG9zOiByZXZpc2UgbXVuaWNpcGlvcyB5IGNvbXVuYXMuIikNCg0KIyBtdXRhdGUoKSBjcmVhIGNvbHVtbmFzIHkgZmlsdGVyKCkgc2VsZWNjaW9uYSBmaWxhcy4gdG91cHBlcigpIHVuaWZpY2EgbWF5w7pzY3VsYXMgeQ0KIyB0cmltd3MoKSBxdWl0YSBlc3BhY2lvcyBhbCBjb21pZW56byB5IGFsIGZpbmFsLiBjYXNlX3doZW4oKSBldmFsw7phIGNvbmRpY2lvbmVzIGVuIG9yZGVuIHkNCiMgYXNpZ25hIGxhIHByaW1lcmEgY2F0ZWdvcsOtYSBxdWUgY29ycmVzcG9uZGEuICVpbiUgdmVyaWZpY2EgcGVydGVuZW5jaWE7ICEgc2lnbmlmaWNhDQojIG5lZ2FjacOzbiB5IHwgc2lnbmlmaWNhIMKrb8K7LiBpcy5uYSgpIGlkZW50aWZpY2EgdmFsb3JlcyBhdXNlbnRlcy4gLmRhdGFbW25vbWJyZV1dDQojIHNlbGVjY2lvbmEgdW5hIGNvbHVtbmEgY3V5byBub21icmUgZXN0w6EgZ3VhcmRhZG8gZW4gdW5hIHZhcmlhYmxlLg0KYGBgDQoNCg0KTG9zIGPDs2RpZ29zIGRlIGNvbXVuYSBzZSBub3JtYWxpemFuIGFudGVzIGRlbCBhbsOhbGlzaXM6IGAiQ29tdW5hIDAyImAsIGAiQ09NVU5BIDIiYCB5IGAiMDIiYCBzZSBjb252aWVydGVuIGVuIGAyYC4gU2UgY29uc2VydmFuIMO6bmljYW1lbnRlIGPDs2RpZ29zIGVudGVyb3MgZW50cmUgMSB5IDIyOyBsb3MgdmFsb3JlcyBhdXNlbnRlcyBvIG5vIGludGVycHJldGFibGVzIHNlIGlkZW50aWZpY2FuIGVuIGVsIGNvbnRyb2wgZGUgY2FsaWRhZC4gRW4gZXN0ZSBhbsOhbGlzaXMgc2UgdXRpbGl6YSBsYSBlcXVpdmFsZW5jaWEgKioxID0gYmljaWNsZXRhLCAyID0gbW90b2NpY2xldGEgeSAzID0gYXV0b23Ds3ZpbCoqLg0KDQojIyBCaWJsaW90ZWNhIGRlIHZhcmlhYmxlcw0KDQpFc3RhIHRhYmxhIGNvbmVjdGEgbGFzIGNvbHVtbmFzIGRlIGVudHJhZGEgY29uIGxvcyBub21icmVzIHF1ZSB1c2FyZW1vcyBlbiBlbCBhbsOhbGlzaXMuIENvbnNlcnZhbW9zIGVsIEV4Y2VsIG9yaWdpbmFsIHkgY3JlYW1vcyB1bmEgdGFibGEgZGUgdHJhYmFqbyBsbGFtYWRhIGBkYXRvc2A7IGRlc3B1w6lzLCBgdmlhamVzYCBjb250aWVuZSDDum5pY2FtZW50ZSBsb3MgcmVnaXN0cm9zIGluY2x1aWRvcy4NCg0KYGBge3IgYmlibGlvdGVjYS12YXJpYWJsZXN9DQpiaWJsaW90ZWNhIDwtIGRhdGEuZnJhbWUoDQogIEVudHJhZGEgPSBjKGNvbHVtbmFzX211bmljaXBpbywgY29sdW1uYV9vcmlnZW4sIGNvbHVtbmFfZGVzdGlubywNCiAgICAgICAgICAgICAgY29sdW1uYV92ZWhpY3VsbywgY29sdW1uYV9mZWNoYSwgIkZpbGEgY29tcGxldGEgZGVsIEV4Y2VsIiwgIlZhcmlhYmxlcyB0ZXJyaXRvcmlhbGVzIiksDQogIFZhcmlhYmxlID0gYygibXVuaWNpcGlvX29yaWdlbiIsICJtdW5pY2lwaW9fZGVzdGlubyIsICJvcmlnZW4iLCAiZGVzdGlubyIsDQogICAgICAgICAgICAgICAidmVoaWN1bG8gLyBtb2RvIiwgImZlY2hhc19yZWdpc3RybyIsICJkdXBsaWNhZG8iLCAiY29udHJvbCIpLA0KICBUaXBvID0gYygiVGV4dG8iLCAiVGV4dG8iLCAiTnVtw6lyaWNvIiwgIk51bcOpcmljbyIsICJUZXh0byAvIGNhdGVnb3LDrWEiLA0KICAgICAgICAgICAiRmVjaGEiLCAiTMOzZ2ljbyIsICJDYXRlZ29yw61hIiksDQogIFVzbyA9IGMoIlZlcmlmaWNhciBxdWUgZWwgdmlhamUgY29taWVuemEgZW4gQ2FsaSIsICJWZXJpZmljYXIgcXVlIHRlcm1pbmEgZW4gQ2FsaSIsDQogICAgICAgICAgIkNvbXVuYSBkZSBzYWxpZGE6IGVudGVybyBlbnRyZSAxIHkgMjIiLCAiQ29tdW5hIGRlIGxsZWdhZGE6IGVudGVybyBlbnRyZSAxIHkgMjIiLA0KICAgICAgICAgICJDb25zZXJ2YXIgZWwgY8OzZGlnbyB5IGFncnVwYXIgbG9zIG1vZG9zIHByaW5jaXBhbGVzIiwgIkVzdGFibGVjZXIgZWwgcGVyaW9kbyBvYnNlcnZhZG8iLA0KICAgICAgICAgICJNYXJjYXIgZmlsYXMgaWTDqW50aWNhcyBhZGljaW9uYWxlcywgc2luIGVsaW1pbmFybGFzIiwgIkV4cGxpY2FyIGluY2x1c2lvbmVzIHkgZXhjbHVzaW9uZXMiKSkNCmtuaXRyOjprYWJsZShiaWJsaW90ZWNhLCBjYXB0aW9uID0gIlZhcmlhYmxlcyBkZSBlbnRyYWRhIHkgdmFyaWFibGVzIGRlIHRyYWJham8iKQ0KYGBgDQoNCkxhIGNvbHVtbmEgKipgciBjb2x1bW5hX2ZlY2hhYCoqIGNvbnRpZW5lIGxhcyBmZWNoYXMgZGUgbG9zIHJlZ2lzdHJvcyB5IHNlIHVzYSBwYXJhIGNhbGN1bGFyIGVsIGludGVydmFsbyBwcmVzZW50YWRvIGVuIGxhIGludHJvZHVjY2nDs24uDQoNCmBgYHtyIGNhbGlkYWQtZmVjaGFzfQ0KY29udHJvbF9mZWNoYXMgPC0gZGF0YS5mcmFtZSgNCiAgRXN0YWRvID0gYygiRmVjaGEgdsOhbGlkYSIsICJGZWNoYSBhdXNlbnRlIG8gbm8gaW50ZXJwcmV0YWJsZSIpLA0KICBSZWdpc3Ryb3MgPSBjKHN1bSghaXMubmEoZmVjaGFzX3JlZ2lzdHJvKSksIHN1bShpcy5uYShmZWNoYXNfcmVnaXN0cm8pKSkpDQprbml0cjo6a2FibGUoY29udHJvbF9mZWNoYXMsIGNhcHRpb24gPSAiRmVjaGFzIHV0aWxpemFkYXMgcGFyYSBlc3RhYmxlY2VyIGVsIHBlcmlvZG8gb2JzZXJ2YWRvIikNCmBgYA0KDQpFbCBwZXJpb2RvIHNlIG9idGllbmUgZGVsIGFyY2hpdm8gY29tcGxldG8gYW50ZXMgZGVsIGZpbHRybyB0ZXJyaXRvcmlhbC4gTG9zIHJlZ2lzdHJvcyBzaW4gZmVjaGEgdsOhbGlkYSBubyBpbnRlcnZpZW5lbiBlbiBlc2UgaW50ZXJ2YWxvLCBwZXJvIG5vIHNlIGV4Y2x1eWVuIGRlbCBhbsOhbGlzaXMgZXNwYWNpYWwgcG9yIGVzZSBtb3Rpdm8uDQoNCkVuIGxhIGNhcnRvZ3JhZsOtYSwgYGNvbXVuYWAgc2Vyw6EgbGEgbGxhdmUgdGVycml0b3JpYWwgeSBsYSBnZW9tZXRyw61hIGNvbnNlcnZhcsOhIGxvcyBsw61taXRlcy4gTcOhcyBhZGVsYW50ZSBhZ3JlZ2FyZW1vcyBgdG90YWxgLCBgYmljaWNsZXRhc2AsIGBtb3Rvc2AgeSBgYXV0b21vdmlsZXNgOiBzb24gY29udGVvcyBjYWxjdWxhZG9zLCBubyBjb2x1bW5hcyBvcmlnaW5hbGVzIGRlIGxhIGVuY3Vlc3RhLg0KDQojIyBJbmNsdXNpb25lcywgdmFsb3JlcyBhdXNlbnRlcyB5IG1vZG9zIGRlIHRyYW5zcG9ydGUNCg0KQW50ZXMgZGUgbWlyYXIgZMOzbmRlIHNlIGNvbmNlbnRyYW4gbG9zIHZpYWplcywgY29udmllbmUgcmVzcG9uZGVyIHRyZXMgcHJlZ3VudGFzOiAqKsK/Y3XDoW50b3MgcmVnaXN0cm9zIHBvZGVtb3MgdWJpY2FyPywgwr9xdcOpIGluZm9ybWFjacOzbiBmYWx0YT8geSDCv2PDs21vIHNlIGRpc3RyaWJ1eWVuIGxvcyBtb2Rvcz8qKiBMb3MgZ3LDoWZpY29zIHNpZ3VpZW50ZXMgcmVzcG9uZGVuIHVuYSBwcmVndW50YSBhIGxhIHZlei4NCg0KTGEgcmV2aXNpw7NuIGRpZmVyZW5jaWEgZG9zIGJhc2VzIGRlIGPDoWxjdWxvOiAqKmVsIGFyY2hpdm8gY29tcGxldG8qKiwgdXRpbGl6YWRvIHBhcmEgZXZhbHVhciBsYSBjYWxpZGFkIHkgbGEgc2VsZWNjacOzbiB0ZXJyaXRvcmlhbCwgeSAqKmxvcyB2aWFqZXMgaW5jbHVpZG9zKiosIHV0aWxpemFkb3MgcGFyYSBkZXNjcmliaXIgbG9zIG1vZG9zIGRlIHRyYW5zcG9ydGUuIEVuIGxhcyB0YWJsYXMgeSBlbCBncsOhZmljbywgKipOKiogaWRlbnRpZmljYSBlbCB0b3RhbCBkZSByZWdpc3Ryb3MgZGUgcmVmZXJlbmNpYSB5ICoqbioqIGVsIG7Dum1lcm8gZGUgcmVnaXN0cm9zIGRlIGNhZGEgY2F0ZWdvcsOtYS4NCg0KYGBge3IgY2FsaWRhZH0NCk5faW5pY2lhbCA8LSBucm93KGRhdG9zKQ0KTl9pbmNsdWlkbyA8LSBucm93KHZpYWplcykNCg0KIyBDYWRhIHJlZ2lzdHJvIHNlIGN1ZW50YSB1bmEgc29sYSB2ZXosIHNlZ8O6biBsYSBwcmltZXJhIGNvbmRpY2nDs24gcXVlIGN1bXBsZS4NCmNhbGlkYWQgPC0gZGF0b3MgJT4lDQogIGNvdW50KGNvbnRyb2wsIG5hbWUgPSAicmVnaXN0cm9zIikgJT4lDQogIG11dGF0ZShwb3JjZW50YWplID0gcm91bmQoMTAwICogcmVnaXN0cm9zIC8gTl9pbmljaWFsLCAxKSkNCmtuaXRyOjprYWJsZShjYWxpZGFkLA0KICBjb2wubmFtZXMgPSBjKCJDYXRlZ29yw61hIGRlIGNvbnRyb2wiLCAiUmVnaXN0cm9zIChuKSIsICIlIGRlbCBhcmNoaXZvIGNvbXBsZXRvIiksDQogIGNhcHRpb24gPSBwYXN0ZTAoIlRyYXphYmlsaWRhZCB0ZXJyaXRvcmlhbC4gTiA9ICIsDQogICAgICAgICAgICAgICAgICAgZm9ybWF0KE5faW5pY2lhbCwgYmlnLm1hcmsgPSAiLiIsIHRyaW0gPSBUUlVFKSwgIiByZWdpc3Ryb3MiKSwNCiAgZm9ybWF0LmFyZ3MgPSBsaXN0KGJpZy5tYXJrID0gIi4iLCBkZWNpbWFsLm1hcmsgPSAiLCIpKQ0KYGBgDQoNCmBgYHtyIGdyYWZpY28taW5jbHVzaW9uZXN9DQpnZ3Bsb3QoY2FsaWRhZCwgYWVzKHJlb3JkZXIoY29udHJvbCwgcmVnaXN0cm9zKSwgcmVnaXN0cm9zLCBmaWxsID0gY29udHJvbCA9PSAiSW5jbHVpZG8iKSkgKw0KICBnZW9tX2NvbCh3aWR0aCA9IDAuNjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChmbXRfbihyZWdpc3Ryb3MpLCAiICgiLCBmbXRfcChwb3JjZW50YWplKSwgIiAlKSIpKSwNCiAgICAgICAgICAgIGhqdXN0ID0gLTAuMDgsIHNpemUgPSAzLjYpICsgY29vcmRfZmxpcCgpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiRkFMU0UiID0gIiM5NWE5YjUiLCAiVFJVRSIgPSAiIzE2ODQ3YiIpKSArDQogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMC40KSkpICsNCiAgbGFicyh0aXRsZSA9ICJEZWwgYXJjaGl2byBvcmlnaW5hbCBhbCBjb25qdW50byBkZSBhbsOhbGlzaXMiLA0KICAgICAgIHN1YnRpdGxlID0gcGFzdGUoIkJhc2UgZGUgY8OhbGN1bG86IiwgZm10X24oTl9pbmljaWFsKSwgInJlZ2lzdHJvcyBpbmljaWFsZXMiKSwNCiAgICAgICB4ID0gTlVMTCwgeSA9ICJSZWdpc3Ryb3MiLCBjYXB0aW9uID0gIkNhZGEgcmVnaXN0cm8gYXBhcmVjZSBlbiB1bmEgc29sYSBjYXRlZ29yw61hLiIpDQpgYGANCg0KTGEgYmFycmEgdmVyZGUgbXVlc3RyYSBsYSBpbmZvcm1hY2nDs24gcXVlIHBvZHJlbW9zIHViaWNhciBlbiBhbWJvcyBleHRyZW1vcyBkZWwgdmlhamUuIExhcyBkZW3DoXMgYmFycmFzIGV4cGxpY2FuIHF1w6kgcXVlZGEgZnVlcmEgeSBwb3IgcXXDqTogbm8gZGViZW4gbGVlcnNlIGF1dG9tw6F0aWNhbWVudGUgY29tbyBlcnJvcmVzIGRlIGNhcHR1cmEuDQoNCkRlIGxvcyAqKmByIGZvcm1hdChOX2luaWNpYWwsIGJpZy5tYXJrID0gJy4nLCB0cmltID0gVFJVRSlgIHJlZ2lzdHJvcyBpbmljaWFsZXMqKiwgc2UgaW5jbHV5ZW4gKipgciBmb3JtYXQoTl9pbmNsdWlkbywgYmlnLm1hcmsgPSAnLicsIHRyaW0gPSBUUlVFKWAgKGByIGZvcm1hdChyb3VuZCgxMDAgKiBOX2luY2x1aWRvIC8gTl9pbmljaWFsLCAxKSwgZGVjaW1hbC5tYXJrID0gJywnLCBuc21hbGwgPSAxLCB0cmltID0gVFJVRSlgICUpKiouIExvcyBwb3JjZW50YWplcyBkZSBlc3RhIHRhYmxhIHNlIGNhbGN1bGFuIGNvbW8gKipuIGRlIGNhZGEgY2F0ZWdvcsOtYSAvIE4gZGVsIGFyY2hpdm8gY29tcGxldG8gw5cgMTAwKiouDQoNCi0gKipJbmNsdWlkbzoqKiBhbWJvcyBtdW5pY2lwaW9zIGNvcnJlc3BvbmRlbiBhIENhbGkgeSBsYXMgZG9zIGNvbXVuYXMgdGllbmVuIGPDs2RpZ29zIGVudGVyb3MgZW50cmUgMSB5IDIyLg0KLSAqKk11bmljaXBpbyBzaW4gaW5mb3JtYWNpw7NuOioqIGZhbHRhIGFsIG1lbm9zIHVubyBkZSBsb3MgbXVuaWNpcGlvcyBvIGNvbnRpZW5lIHVuIG1hcmNhZG9yIGRlIGF1c2VuY2lhLCBjb21vIHVuYSBjZWxkYSB2YWPDrWEsIGBOQWAsIGBOL0FgIG8gwqtzaW4gaW5mb3JtYWNpw7NuwrsuDQotICoqVmlhamUgZXh0ZXJubyBvIG11bmljaXBpbyBwb3IgcmV2aXNhcjoqKiBsb3MgbXVuaWNpcGlvcyB0aWVuZW4gaW5mb3JtYWNpw7NuLCBwZXJvIGFsIG1lbm9zIHVubyBubyBjb2luY2lkZSBjb24gQ2FsaSBvIFNhbnRpYWdvIGRlIENhbGkuIFB1ZWRlIGNvcnJlc3BvbmRlciBhIHVuIHZpYWplIGludGVybXVuaWNpcGFsIHbDoWxpZG8gbyBhIHVuYSBkZW5vbWluYWNpw7NuIHBlbmRpZW50ZSBkZSBub3JtYWxpemFyOyBubyBpbXBsaWNhIGF1dG9tw6F0aWNhbWVudGUgdW4gZXJyb3IuDQotICoqQ29tdW5hIGF1c2VudGUgbyBpbnbDoWxpZGE6KiogbG9zIGRvcyBtdW5pY2lwaW9zIGNvcnJlc3BvbmRlbiBhIENhbGksIHBlcm8gZmFsdGEgYWwgbWVub3MgdW5hIGNvbXVuYSwgc3UgdGV4dG8gbm8gcHVkbyBjb252ZXJ0aXJzZSBhIG7Dum1lcm8gbyBzdSBjw7NkaWdvIG5vIGVzIHVuIGVudGVybyBlbnRyZSAxIHkgMjIuDQoNCkxhcyBjYXRlZ29yw61hcyBzb24gKipleGNsdXllbnRlcyoqLiBFbCBvcmRlbiBkZSBldmFsdWFjacOzbiBlcyBlbCBzaWd1aWVudGU6IHByaW1lcm8gc2UgcmV2aXNhIGxhIGF1c2VuY2lhIG11bmljaXBhbCwgZGVzcHXDqXMgbGEgY29uZGljacOzbiBDYWxp4oCTQ2FsaSB5IGZpbmFsbWVudGUgbGFzIGNvbXVuYXMuIFBvciBlc28gbG9zIHByb2JsZW1hcyBkZSBjb211bmEgZGUgdmlhamVzIHlhIGNsYXNpZmljYWRvcyBjb21vIGV4dGVybm9zIG5vIHZ1ZWx2ZW4gYSBjb250YXJzZSBlbiDCq0NvbXVuYSBhdXNlbnRlIG8gaW52w6FsaWRhwrsuDQoNCmBgYHtyIGNvbnRyb2xlcy1hdXNlbmNpYX0NCiMgVW4gYXVzZW50ZSBvcmlnaW5hbCBubyBlcyBsbyBtaXNtbyBxdWUgdW4gdGV4dG8gcXVlIGZhbGzDsyBhbCBjb252ZXJ0aXJzZS4NCiMgU2UgY29uc3VsdGFuIGxhcyBjb2x1bW5hcyBvcmlnaW5hbGVzIHBhcmEgZGlzdGluZ3VpciBhbWJhcyBzaXR1YWNpb25lcy4NCm1hcmNhZG9yZXNfYXVzZW5jaWEgPC0gYygiIiwgIk5BIiwgIk4vQSIsICJOVUxMIiwgIlNJTiBEQVRPIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiU0lOIElORk9STUFDSU9OIiwgIlNJTiBJTkZPUk1BQ0nDk04iKQ0KdGV4dG9fb3JpZ2VuIDwtIHRvdXBwZXIodHJpbXdzKGFzLmNoYXJhY3RlcihlbmN1ZXN0YVtbY29sdW1uYV9vcmlnZW5dXSkpKQ0KdGV4dG9fZGVzdGlubyA8LSB0b3VwcGVyKHRyaW13cyhhcy5jaGFyYWN0ZXIoZW5jdWVzdGFbW2NvbHVtbmFfZGVzdGlub11dKSkpDQphdXNlbnRlX29yaWdlbiA8LSBpcy5uYSh0ZXh0b19vcmlnZW4pIHwgdGV4dG9fb3JpZ2VuICVpbiUgbWFyY2Fkb3Jlc19hdXNlbmNpYQ0KYXVzZW50ZV9kZXN0aW5vIDwtIGlzLm5hKHRleHRvX2Rlc3Rpbm8pIHwgdGV4dG9fZGVzdGlubyAlaW4lIG1hcmNhZG9yZXNfYXVzZW5jaWENCg0KIyBFc3RvcyBjb250cm9sZXMgc2UgY2FsY3VsYW4gc29icmUgVE9ETyBlbCBhcmNoaXZvIHkgcHVlZGVuIHN1cGVycG9uZXJzZS4NCnJldmlzaW9uIDwtIGRhdGEuZnJhbWUoDQogIEluZGljYWRvciA9IGMoIlJlZ2lzdHJvcyBpbmljaWFsZXMiLCAiUmVnaXN0cm9zIGluY2x1aWRvcyIsDQogICAgIkNvbXVuYSBkZSBvcmlnZW46IGF1c2VudGUgZW4gZWwgYXJjaGl2byIsDQogICAgIkNvbXVuYSBkZSBkZXN0aW5vOiBhdXNlbnRlIGVuIGVsIGFyY2hpdm8iLA0KICAgICJDb211bmEgZGUgb3JpZ2VuOiB0ZXh0byBubyBjb252ZXJ0aWJsZSIsDQogICAgIkNvbXVuYSBkZSBkZXN0aW5vOiB0ZXh0byBubyBjb252ZXJ0aWJsZSIsDQogICAgIkNvbXVuYSBkZSBvcmlnZW46IG7Dum1lcm8gZnVlcmEgZGUgMeKAkzIyIG8gbm8gZW50ZXJvIiwNCiAgICAiQ29tdW5hIGRlIGRlc3Rpbm86IG7Dum1lcm8gZnVlcmEgZGUgMeKAkzIyIG8gbm8gZW50ZXJvIiwNCiAgICAiRmlsYXMgaWTDqW50aWNhcyBhZGljaW9uYWxlcyIpLA0KICBSZWdpc3Ryb3MgPSBjKE5faW5pY2lhbCwgTl9pbmNsdWlkbywNCiAgICBzdW0oYXVzZW50ZV9vcmlnZW4pLCBzdW0oYXVzZW50ZV9kZXN0aW5vKSwNCiAgICBzdW0oIWF1c2VudGVfb3JpZ2VuICYgaXMubmEoZGF0b3Mkb3JpZ2VuKSksDQogICAgc3VtKCFhdXNlbnRlX2Rlc3Rpbm8gJiBpcy5uYShkYXRvcyRkZXN0aW5vKSksDQogICAgc3VtKCFpcy5uYShkYXRvcyRvcmlnZW4pICYgIWRhdG9zJG9yaWdlbiAlaW4lIDE6MjIpLA0KICAgIHN1bSghaXMubmEoZGF0b3MkZGVzdGlubykgJiAhZGF0b3MkZGVzdGlubyAlaW4lIDE6MjIpLA0KICAgIHN1bShkYXRvcyRkdXBsaWNhZG8pKQ0KKSAlPiUgbXV0YXRlKFBvcmNlbnRhamUgPSByb3VuZCgxMDAgKiBSZWdpc3Ryb3MgLyBOX2luaWNpYWwsIDEpKQ0Ka25pdHI6OmthYmxlKHJldmlzaW9uLA0KICBjb2wubmFtZXMgPSBjKCJJbmRpY2Fkb3IiLCAiUmVnaXN0cm9zIChuKSIsICIlIGRlbCBhcmNoaXZvIGNvbXBsZXRvIiksDQogIGNhcHRpb24gPSBwYXN0ZTAoIkNvbnRyb2xlcyBiw6FzaWNvcyB5IHZhbG9yZXMgYXVzZW50ZXMuIE4gPSAiLA0KICAgICAgICAgICAgICAgICAgIGZvcm1hdChOX2luaWNpYWwsIGJpZy5tYXJrID0gIi4iLCB0cmltID0gVFJVRSksICIgcmVnaXN0cm9zIiksDQogIGZvcm1hdC5hcmdzID0gbGlzdChiaWcubWFyayA9ICIuIiwgZGVjaW1hbC5tYXJrID0gIiwiKSkNCmBgYA0KDQoqKkF1c2VudGUqKiBzaWduaWZpY2EgcXVlIGxhIGNvbXVuYSBubyBlc3TDoSBpbmZvcm1hZGEgZW4gZWwgYXJjaGl2byBvcmlnaW5hbCBvIGNvbnRpZW5lIHVubyBkZSBsb3MgbWFyY2Fkb3JlcyBkZSBhdXNlbmNpYSBkZWZpbmlkb3MgZW4gZWwgY8OzZGlnby4gKipUZXh0byBubyBjb252ZXJ0aWJsZSoqIHNpZ25pZmljYSBxdWUgc8OtIGV4aXN0ZSB1bmEgcmVzcHVlc3RhLCBwZXJvIG5vIHNlIG9idGllbmUgdW4gbsO6bWVybyBkZXNwdcOpcyBkZSBub3JtYWxpemFybGEuIFBvciBlamVtcGxvLCDCq0NvbXVuYSAwMsK7IHNlIGNvbnZpZXJ0ZSBjb3JyZWN0YW1lbnRlIGEgMiwgbWllbnRyYXMgcXVlIMKrZGVzY29ub2NpZGHCuyBubyBlcyBjb252ZXJ0aWJsZS4gVW4gdmFsb3IgY29tbyAyNSBvIDIsNSBwdWVkZSBzZXIgbnVtw6lyaWNvLCBwZXJvIGVzICoqaW52w6FsaWRvIHBhcmEgZXN0ZSBhbsOhbGlzaXMqKi4NCg0KTGEgdGFibGEgc2VwYXJhIGxhIGF1c2VuY2lhIG9yaWdpbmFsIGRlIGxhcyBkaWZpY3VsdGFkZXMgZGUgY29udmVyc2nDs24gcGFyYSBxdWUgcG9kYW1vcyBkaXN0aW5ndWlyIHF1w6kgaW5mb3JtYWNpw7NuIGZhbHRhIHkgY3XDoWwgbmVjZXNpdGEgcmV2aXNpw7NuLiBUb2RvcyBlc3RvcyBjb250cm9sZXMgdXNhbiBjb21vIGRlbm9taW5hZG9yICoqTiA9IGByIGZvcm1hdChOX2luaWNpYWwsIGJpZy5tYXJrID0gJy4nLCB0cmltID0gVFJVRSlgKiosIGluY2x1c28gc2kgZWwgcmVnaXN0cm8gcXVlZMOzIGZ1ZXJhIGRlbCBhbsOhbGlzaXMgdGVycml0b3JpYWwuICoqU3VzIGZpbGFzIG5vIHNlIHN1bWFuOioqIHVuIG1pc21vIHJlZ2lzdHJvIHB1ZWRlIHRlbmVyIHByb2JsZW1hcyBlbiBvcmlnZW4geSBkZXN0aW5vLCBvIHNlciB1bmEgZmlsYSBpZMOpbnRpY2EgYWRpY2lvbmFsLiBQb3IgZXN0YSByYXrDs24sIHRhbXBvY28gZGViZW4gc3VtYXJzZSBsb3MgcHJvYmxlbWFzIGRlIG9yaWdlbiB5IGRlc3Rpbm8gcGFyYSBjb21wYXJhcmxvcyBjb24gbGEgY2F0ZWdvcsOtYSB0ZXJyaXRvcmlhbCBkZSBjb211bmFzIGludsOhbGlkYXMuDQoNCmBgYHtyIGdyYWZpY28tYXVzZW5jaWFzfQ0KcmV2aXNpb25fZ3JhZmljbyA8LSByZXZpc2lvbiAlPiUgZmlsdGVyKGdyZXBsKCJeQ29tdW5hIiwgSW5kaWNhZG9yKSkgJT4lDQogIG11dGF0ZShFeHRyZW1vID0gaWZfZWxzZShncmVwbCgib3JpZ2VuIiwgSW5kaWNhZG9yKSwgIk9yaWdlbiIsICJEZXN0aW5vIiksDQogICAgICAgICBQcm9ibGVtYSA9IHN1YigiXkNvbXVuYSBkZSAob3JpZ2VufGRlc3Rpbm8pOiAiLCAiIiwgSW5kaWNhZG9yKSkNCmdncGxvdChyZXZpc2lvbl9ncmFmaWNvLCBhZXMoUHJvYmxlbWEsIFJlZ2lzdHJvcywgZmlsbCA9IEV4dHJlbW8pKSArDQogIGdlb21fY29sKHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjc1KSwgd2lkdGggPSAwLjY1KSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBmbXRfbihSZWdpc3Ryb3MpKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNzUpLA0KICAgICAgICAgICAgaGp1c3QgPSAtMC4xNSwgc2l6ZSA9IDMuNSkgKyBjb29yZF9mbGlwKCkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJPcmlnZW4iID0gIiMxNjg0N2IiLCAiRGVzdGlubyIgPSAiIzM3NWI5OSIpKSArDQogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMC4yKSkpICsNCiAgbGFicyh0aXRsZSA9ICLCv1F1w6kgbmVjZXNpdGFtb3MgcmV2aXNhciBlbiBsYXMgY29tdW5hcz8iLA0KICAgICAgIHN1YnRpdGxlID0gcGFzdGUoIlJldmlzacOzbiBkZWwgYXJjaGl2byBjb21wbGV0bzogTiA9IiwgZm10X24oTl9pbmljaWFsKSksDQogICAgICAgeCA9IE5VTEwsIHkgPSAiUmVnaXN0cm9zIiwgZmlsbCA9IE5VTEwsDQogICAgICAgY2FwdGlvbiA9ICJMb3MgY29udHJvbGVzIHB1ZWRlbiBjb2luY2lkaXIgZW4gdW4gbWlzbW8gcmVnaXN0cm87IGxhcyBiYXJyYXMgbm8gc2Ugc3VtYW4uIikNCmBgYA0KDQpDb21wYXJhbW9zIG9yaWdlbiB5IGRlc3Rpbm8gcGFyYSByZWNvbm9jZXIgZMOzbmRlIHNlIHBpZXJkZSBtw6FzIGluZm9ybWFjacOzbi4gVW5hIGJhcnJhIGFsdGEgZGUgYXVzZW5jaWFzIHN1Z2llcmUgcmV2aXNhciBlbCBkaWxpZ2VuY2lhbWllbnRvOyB1bmEgZGUgdGV4dG9zIG5vIGNvbnZlcnRpYmxlcyBzdWdpZXJlIHJldmlzYXIgY8OzbW8gc2UgZXNjcmliaWVyb24gbGFzIGNvbXVuYXMuIExvcyBjw7NkaWdvcyBmdWVyYSBkZSByYW5nbyByZXF1aWVyZW4gY29uc3VsdGFyIGVsIGRhdG8gb3JpZ2luYWwgYW50ZXMgZGUgY29ycmVnaXJsb3MuDQoNCmBgYHtyIGZyZWN1ZW5jaWFzLW1vZG99DQojIEVsIGRlbm9taW5hZG9yIGNhbWJpYTogYWhvcmEgc29uIMO6bmljYW1lbnRlIGxvcyB2aWFqZXMgaW5jbHVpZG9zLg0KZnJlY3VlbmNpYV9tb2RvIDwtIHZpYWplcyAlPiUNCiAgY291bnQobW9kbywgbmFtZSA9ICJyZWdpc3Ryb3MiLCBzb3J0ID0gVFJVRSkgJT4lDQogIG11dGF0ZShwb3JjZW50YWplID0gcm91bmQoMTAwICogcmVnaXN0cm9zIC8gTl9pbmNsdWlkbywgMSksDQogICAgICAgICBldGlxdWV0YSA9IHBhc3RlMChmb3JtYXQocmVnaXN0cm9zLCBiaWcubWFyayA9ICIuIiwgdHJpbSA9IFRSVUUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIiAoIiwgZm9ybWF0KHBvcmNlbnRhamUsIGRlY2ltYWwubWFyayA9ICIsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxLCB0cmltID0gVFJVRSksICIgJSkiKSkNCmtuaXRyOjprYWJsZShmcmVjdWVuY2lhX21vZG8gJT4lIHNlbGVjdCgtZXRpcXVldGEpLA0KICBjb2wubmFtZXMgPSBjKCJNb2RvIGRlIHRyYW5zcG9ydGUiLCAiUmVnaXN0cm9zIChuKSIsICIlIGRlIGxvcyBpbmNsdWlkb3MiKSwNCiAgY2FwdGlvbiA9IHBhc3RlMCgiTW9kb3MgZGUgdHJhbnNwb3J0ZS4gTiA9ICIsDQogICAgICAgICAgICAgICAgICAgZm9ybWF0KE5faW5jbHVpZG8sIGJpZy5tYXJrID0gIi4iLCB0cmltID0gVFJVRSksICIgdmlhamVzIGluY2x1aWRvcyIpLA0KICBmb3JtYXQuYXJncyA9IGxpc3QoYmlnLm1hcmsgPSAiLiIsIGRlY2ltYWwubWFyayA9ICIsIikpDQpgYGANCg0KRW4gZXN0YSB0YWJsYSwgY2FkYSBwb3JjZW50YWplIGNvcnJlc3BvbmRlIGEgKipuIGRlbCBtb2RvIC8gTiBkZSB2aWFqZXMgaW5jbHVpZG9zIMOXIDEwMCoqOyBubyBzZSBjYWxjdWxhIHNvYnJlIGVsIGFyY2hpdm8gY29tcGxldG8uIMKrU2luIGluZm9ybWFjacOzbsK7IGFncnVwYSByZXNwdWVzdGFzIGRlIHZlaMOtY3VsbyB2YWPDrWFzLCBgTkFgLCBgTi9BYCBvIMKrc2luIGluZm9ybWFjacOzbsK7LCBtaWVudHJhcyBxdWUgwqtPdHJvIG1vZG8gbyBjw7NkaWdvwrsgY29udGllbmUgcmVzcHVlc3RhcyBubyByZWNvbm9jaWRhcyBjb21vIGJpY2ljbGV0YSwgbW90b2NpY2xldGEgbyBhdXRvbcOzdmlsLiBFc3RhIMO6bHRpbWEgY2F0ZWdvcsOtYSBubyBzZSBpbnRlcnByZXRhIGNvbW8gdW4gbW9kbyBlc3BlY8OtZmljbyBzaW4gY29uc3VsdGFyIGxhcyBjb252ZW5jaW9uZXMgZGUgbGEgZW5jdWVzdGEuIExhcyBkaWZlcmVuY2lhcyBkZSB1bmEgZMOpY2ltYSBlbiBsYSBzdW1hIGRlIHBvcmNlbnRhamVzIHNlIGRlYmVuIGFsIHJlZG9uZGVvLg0KDQpMYXMgZmlsYXMgaWTDqW50aWNhcyBzZSBjb25zZXJ2YW4gcG9ycXVlLCBzaW4gdW4gaWRlbnRpZmljYWRvciBkZSBlbnRyZXZpc3RhLCBubyBzZSBwdWVkZSBhc2VndXJhciBxdWUgc2VhbiBlcnJvcmVzIGRlIGNhcHR1cmEuIFRhbWJpw6luIHNlIGNvbnNlcnZhbiBsb3MgdmlhamVzIGRlbnRybyBkZSB1bmEgbWlzbWEgY29tdW5hLiBMb3MgbWFwYXMgZ2VuZXJhbGVzIGluY2x1eWVuIG90cm9zIG1vZG9zIHkgcmVzcHVlc3RhcyBzaW4gbW9kbzsgbG9zIGVzcGVjw61maWNvcyBtdWVzdHJhbiDDum5pY2FtZW50ZSBiaWNpY2xldGEsIG1vdG9jaWNsZXRhIG8gYXV0b23Ds3ZpbC4NCg0KYGBge3IgY29tcG9zaWNpb24tbW9kYWx9DQojIExhcyBldGlxdWV0YXMgbXVlc3RyYW4gbiB5IHBvcmNlbnRhamU7IGVsIHN1YnTDrXR1bG8gaWRlbnRpZmljYSBlbCBkZW5vbWluYWRvci4NCiMgVG9kb3MgbG9zIHZhbG9yZXMgc2UgcmVjYWxjdWxhbiBhbCBlamVjdXRhciBlbCBkb2N1bWVudG8gY29uIGxhIGJhc2UgZGUgZGF0b3MuDQpnZ3Bsb3QoZnJlY3VlbmNpYV9tb2RvLCBhZXMoeCA9IHJlb3JkZXIobW9kbywgcmVnaXN0cm9zKSwgeSA9IHJlZ2lzdHJvcykpICsNCiAgZ2VvbV9jb2woZmlsbCA9ICIjMTY4NDdiIiwgd2lkdGggPSAwLjY1KSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBldGlxdWV0YSksIGhqdXN0ID0gLTAuMDgsIHNpemUgPSAzLjgpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLCAwLjMwKSkpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgbGFicyh0aXRsZSA9ICJDb21wb3NpY2nDs24gbW9kYWwgZGUgbG9zIHJlZ2lzdHJvcyBpbmNsdWlkb3MiLA0KICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKCJOID0gIiwgZm9ybWF0KE5faW5jbHVpZG8sIGJpZy5tYXJrID0gIi4iLCB0cmltID0gVFJVRSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIiB2aWFqZXMgaW5jbHVpZG9zIGRlICIsDQogICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0KE5faW5pY2lhbCwgYmlnLm1hcmsgPSAiLiIsIHRyaW0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiIHJlZ2lzdHJvcyBpbmljaWFsZXMgKCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0KHJvdW5kKDEwMCAqIE5faW5jbHVpZG8gLyBOX2luaWNpYWwsIDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWNpbWFsLm1hcmsgPSAiLCIsIG5zbWFsbCA9IDEsIHRyaW0gPSBUUlVFKSwgIiAlKSIpLA0KICAgICAgIHggPSBOVUxMLCB5ID0gIlJlZ2lzdHJvcyBkZSBkZXNwbGF6YW1pZW50byAobikiLA0KICAgICAgIGNhcHRpb24gPSAiQ2FkYSBldGlxdWV0YSBtdWVzdHJhIG4gKCUgZGUgbG9zIHZpYWplcyBpbmNsdWlkb3MpLiBQb3JjZW50YWplcyBzaW4gZXhwYW5zacOzbiBtdWVzdHJhbC4iKQ0KYGBgDQoNCkVsIG1vZG8gY29uIG3DoXMgcmVnaXN0cm9zIHByZWRvbWluYSBlbiBlc3RhIG11ZXN0cmEuIEVzdG8gbm8gZGVtdWVzdHJhIHF1ZSBzZWEgZWwgbcOhcyB1dGlsaXphZG8gcG9yIHRvZGEgbGEgcG9ibGFjacOzbi4NCg0KIyBSZXPDum1lbmVzIHBvciBjb211bmEgeSB1bmnDs24gZ2VvZ3LDoWZpY2ENCg0KQWhvcmEgcGFzYW1vcyBkZSBsb3MgcmVnaXN0cm9zIGluZGl2aWR1YWxlcyBhIHVuYSBsZWN0dXJhIHRlcnJpdG9yaWFsLiBDb250YW1vcyBjdcOhbnRvcyB2aWFqZXMgY29taWVuemFuIHkgdGVybWluYW4gZW4gY2FkYSBjb211bmEgeSBkZXNhZ3JlZ2Ftb3MgZXNvcyBjb250ZW9zIHBvciBtb2RvLiBEZXNwdcOpcyB1bmltb3MgbG9zIHJlc3VsdGFkb3MgY29uIGxvcyBwb2zDrWdvbm9zIHBhcmEgZGlidWphciBsb3MgbWFwYXMuIEVzdGEgdW5pw7NuIHVzYSBlbCBjw7NkaWdvIGRlIGNvbXVuYSwgbm8gZWwgb3JkZW4gZGUgbGFzIGZpbGFzLg0KDQpVbmEgbWlzbWEgY29tdW5hIHB1ZWRlIHNlciB1biBvcmlnZW4gZnJlY3VlbnRlIHkgdGFtYmnDqW4gdW4gZGVzdGlubyBmcmVjdWVudGUuIFBvciBlc28gYW5hbGl6YW1vcyBhbWJvcyBleHRyZW1vcyBhbnRlcyBkZSBoYWJsYXIgZGUgcHJlZG9taW5pbyBkZSBsbGVnYWRhcyBvIHNhbGlkYXMuDQoNCiMjIENvbnRlb3MgcG9yIG9yaWdlbiB5IGRlc3Rpbm8NCg0KYGBge3IgcmVzdW1pcn0NCiMgZ3JvdXBfYnkoKSBvcmdhbml6YSBsb3MgY8OhbGN1bG9zIHBvciBjb211bmEuDQojIHN1bShjb25kaWNpw7NuKSBjdWVudGEgbG9zIFRSVUU7IG5hLnJtID0gVFJVRSBvbWl0ZSB2YWxvcmVzIE5BLg0Kb3JpZ2VuX3Jlc3VtZW4gPC0gdmlhamVzICU+JQ0KICBncm91cF9ieShjb211bmEgPSBvcmlnZW4pICU+JQ0KICBzdW1tYXJpc2UodG90YWwgPSBuKCksDQogICAgICAgICAgICBiaWNpY2xldGFzID0gc3VtKG1vZG8gPT0gIkJpY2ljbGV0YSIsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBtb3RvcyA9IHN1bShtb2RvID09ICJNb3RvY2ljbGV0YSIsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBhdXRvbW92aWxlcyA9IHN1bShtb2RvID09ICJBdXRvbcOzdmlsIiwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIC5ncm91cHMgPSAiZHJvcCIpDQoNCmRlc3Rpbm9fcmVzdW1lbiA8LSB2aWFqZXMgJT4lDQogIGdyb3VwX2J5KGNvbXVuYSA9IGRlc3Rpbm8pICU+JQ0KICBzdW1tYXJpc2UodG90YWwgPSBuKCksDQogICAgICAgICAgICBiaWNpY2xldGFzID0gc3VtKG1vZG8gPT0gIkJpY2ljbGV0YSIsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBtb3RvcyA9IHN1bShtb2RvID09ICJNb3RvY2ljbGV0YSIsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBhdXRvbW92aWxlcyA9IHN1bShtb2RvID09ICJBdXRvbcOzdmlsIiwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIC5ncm91cHMgPSAiZHJvcCIpDQoNCmtuaXRyOjprYWJsZShvcmlnZW5fcmVzdW1lbiwgY2FwdGlvbiA9ICJSZWdpc3Ryb3MgcG9yIGNvbXVuYSBkZSBvcmlnZW4iKQ0Ka25pdHI6OmthYmxlKGRlc3Rpbm9fcmVzdW1lbiwgY2FwdGlvbiA9ICJSZWdpc3Ryb3MgcG9yIGNvbXVuYSBkZSBkZXN0aW5vIikNCg0KIyAuZ3JvdXBzID0gImRyb3AiIGRlamEgZWwgcmVzdW1lbiBzaW4gYWdydXBhY2nDs24uIEVsIHRvdGFsIHB1ZWRlIHN1cGVyYXIgbGEgc3VtYSBkZSBsb3MNCiMgdHJlcyBtb2RvcywgcG9ycXVlIGluY2x1eWUgb3RyYXMgY2F0ZWdvcsOtYXMgeSByZXNwdWVzdGFzIHNpbiBpbmZvcm1hY2nDs24gZGUgbW9kby4NCmBgYA0KDQoNCkxvcyB0b3RhbGVzIGRlIG9yaWdlbiB5IGRlc3Rpbm8gZGViZW4gY29pbmNpZGlyOiBjYWRhIHZpYWplIGFwb3J0YSB1bmEgc2FsaWRhIHkgdW5hIGxsZWdhZGEuIExhcyBjb2x1bW5hcyBkZSBsb3MgdHJlcyBtb2RvcyBwcmluY2lwYWxlcyBwdWVkZW4gc3VtYXIgbWVub3MgcXVlIGVsIHRvdGFsLCBwb3JxdWUgdG9kYXbDrWEgcHVlZGUgaGFiZXIgY8OzZGlnb3Mgc2luIGlkZW50aWZpY2FyIG8gcmVzcHVlc3RhcyBhdXNlbnRlcy4gVW5hIGNvbXVuYSBxdWUgbm8gYXBhcmV6Y2EgZW4gZXN0b3MgcmVzw7ptZW5lcyByZWNpYmlyw6EgY2VybyBhbCB1bmlybGEgY29uIGxhIGNhcnRvZ3JhZsOtYS4NCg0KIyMgwr9Ew7NuZGUgc2UgY29uY2VudHJhbiBsb3MgcmVnaXN0cm9zPw0KDQpVbiBtYXBhIHBlcm1pdGUgcmVjb25vY2VyIGNvbmNlbnRyYWNpb25lczsgbG9zIHBvcmNlbnRhamVzIG5vcyBkaWNlbiBjdcOhbnRvIHBlc2FuLiBBcXXDrSBjb21wYXJhbW9zIGxhcyBjaW5jbyBjb211bmFzIGNvbiBtw6FzIHJlZ2lzdHJvcyBlbiBjYWRhIGV4dHJlbW8geSBtZWRpbW9zIGN1w6FudGFzIGNvbXVuYXMgYWN1bXVsYW4gYWwgbWVub3MgbGEgbWl0YWQgZGUgbGFzIHNhbGlkYXMgbyBsbGVnYWRhcy4gTG9zIGVtcGF0ZXMgc2Ugb3JkZW5hbiBwb3IgY8OzZGlnbyBkZSBjb211bmEgcGFyYSBxdWUgbGFzIHNlbGVjY2lvbmVzIHNlYW4gcmVwcm9kdWNpYmxlcy4NCg0KYGBge3IgZXN0YWRpc3RpY2FzLXRlcnJpdG9yaWFsZXN9DQp0ZXJyaXRvcmlvIDwtIGRhdGEuZnJhbWUoY29tdW5hID0gMToyMikgJT4lDQogIGxlZnRfam9pbihvcmlnZW5fcmVzdW1lbiAlPiUgc2VsZWN0KGNvbXVuYSwgc2FsaWRhcyA9IHRvdGFsKSwgYnkgPSAiY29tdW5hIikgJT4lDQogIGxlZnRfam9pbihkZXN0aW5vX3Jlc3VtZW4gJT4lIHNlbGVjdChjb211bmEsIGxsZWdhZGFzID0gdG90YWwpLCBieSA9ICJjb211bmEiKSAlPiUNCiAgbXV0YXRlKHNhbGlkYXMgPSByZXBsYWNlX25hKHNhbGlkYXMsIDApLCBsbGVnYWRhcyA9IHJlcGxhY2VfbmEobGxlZ2FkYXMsIDApLA0KICAgICAgICAgcGN0X3NhbGlkYXMgPSAxMDAgKiBzYWxpZGFzIC8gTl9pbmNsdWlkbywNCiAgICAgICAgIHBjdF9sbGVnYWRhcyA9IDEwMCAqIGxsZWdhZGFzIC8gTl9pbmNsdWlkbykNCnJhbmtpbmdfbyA8LSB0ZXJyaXRvcmlvICU+JSBhcnJhbmdlKGRlc2Moc2FsaWRhcyksIGNvbXVuYSkgJT4lDQogIG11dGF0ZShhY3VtdWxhZG8gPSAxMDAgKiBjdW1zdW0oc2FsaWRhcykgLyBOX2luY2x1aWRvKQ0KcmFua2luZ19kIDwtIHRlcnJpdG9yaW8gJT4lIGFycmFuZ2UoZGVzYyhsbGVnYWRhcyksIGNvbXVuYSkgJT4lDQogIG11dGF0ZShhY3VtdWxhZG8gPSAxMDAgKiBjdW1zdW0obGxlZ2FkYXMpIC8gTl9pbmNsdWlkbykNCmMzX28gPC0gc3VtKGhlYWQocmFua2luZ19vJHNhbGlkYXMsIDMpKSAvIE5faW5jbHVpZG8gKiAxMDANCmMzX2QgPC0gc3VtKGhlYWQocmFua2luZ19kJGxsZWdhZGFzLCAzKSkgLyBOX2luY2x1aWRvICogMTAwDQptaXRhZF9vIDwtIHdoaWNoKHJhbmtpbmdfbyRhY3VtdWxhZG8gPj0gNTApWzFdDQptaXRhZF9kIDwtIHdoaWNoKHJhbmtpbmdfZCRhY3VtdWxhZG8gPj0gNTApWzFdDQpyZXN1bWVuX2NvbmNlbnRyYWNpb24gPC0gZGF0YS5mcmFtZSgNCiAgSW5kaWNhZG9yID0gYygiVG90YWwgZGUgcmVnaXN0cm9zIiwgIk1lZGlhIHBvciBjb211bmEiLCAiTWVkaWFuYSBwb3IgY29tdW5hIiwNCiAgICAgICAgICAgICAgICAiTcOtbmltbyIsICJNw6F4aW1vIiwgIkRlc3ZpYWNpw7NuIGVzdMOhbmRhciIsDQogICAgICAgICAgICAgICAgIlBhcnRpY2lwYWNpw7NuIGRlIGxhcyAzIHByaW1lcmFzICglKSIsICJQYXJ0aWNpcGFjacOzbiBkZSBsYXMgNSBwcmltZXJhcyAoJSkiLA0KICAgICAgICAgICAgICAgICJDb211bmFzIG5lY2VzYXJpYXMgcGFyYSByZXVuaXIgYWwgbWVub3MgZWwgNTAgJSIpLA0KICBTYWxpZGFzID0gYyhOX2luY2x1aWRvLCBtZWFuKHRlcnJpdG9yaW8kc2FsaWRhcyksIG1lZGlhbih0ZXJyaXRvcmlvJHNhbGlkYXMpLA0KICAgICAgICAgICAgICBtaW4odGVycml0b3JpbyRzYWxpZGFzKSwgbWF4KHRlcnJpdG9yaW8kc2FsaWRhcyksIHNkKHRlcnJpdG9yaW8kc2FsaWRhcyksDQogICAgICAgICAgICAgIGMzX28sIHN1bShoZWFkKHJhbmtpbmdfbyRzYWxpZGFzLCA1KSkgLyBOX2luY2x1aWRvICogMTAwLCBtaXRhZF9vKSwNCiAgTGxlZ2FkYXMgPSBjKE5faW5jbHVpZG8sIG1lYW4odGVycml0b3JpbyRsbGVnYWRhcyksIG1lZGlhbih0ZXJyaXRvcmlvJGxsZWdhZGFzKSwNCiAgICAgICAgICAgICAgIG1pbih0ZXJyaXRvcmlvJGxsZWdhZGFzKSwgbWF4KHRlcnJpdG9yaW8kbGxlZ2FkYXMpLCBzZCh0ZXJyaXRvcmlvJGxsZWdhZGFzKSwNCiAgICAgICAgICAgICAgIGMzX2QsIHN1bShoZWFkKHJhbmtpbmdfZCRsbGVnYWRhcywgNSkpIC8gTl9pbmNsdWlkbyAqIDEwMCwgbWl0YWRfZCkpDQprbml0cjo6a2FibGUocmVzdW1lbl9jb25jZW50cmFjaW9uLCBkaWdpdHMgPSAxLA0KICAgICAgICAgICAgIGNhcHRpb24gPSAiRGlzdHJpYnVjacOzbiBkZSBsb3MgY29udGVvcyBlbnRyZSBsYXMgMjIgY29tdW5hcyIpDQpyYW5raW5ncyA8LSBiaW5kX3Jvd3MoDQogIHJhbmtpbmdfbyAlPiUgc2xpY2VfaGVhZChuID0gNSkgJT4lIHRyYW5zbXV0ZShFeHRyZW1vID0gIk9yaWdlbiIsIGNvbXVuYSwgcmVnaXN0cm9zID0gc2FsaWRhcywgcG9yY2VudGFqZSA9IHBjdF9zYWxpZGFzKSwNCiAgcmFua2luZ19kICU+JSBzbGljZV9oZWFkKG4gPSA1KSAlPiUgdHJhbnNtdXRlKEV4dHJlbW8gPSAiRGVzdGlubyIsIGNvbXVuYSwgcmVnaXN0cm9zID0gbGxlZ2FkYXMsIHBvcmNlbnRhamUgPSBwY3RfbGxlZ2FkYXMpKQ0Ka25pdHI6OmthYmxlKHJhbmtpbmdzLCBkaWdpdHMgPSAxLCBjYXB0aW9uID0gIkNpbmNvIGNvbXVuYXMgY29uIG3DoXMgcmVnaXN0cm9zIGVuIGNhZGEgZXh0cmVtbyIpDQpnZ3Bsb3QocmFua2luZ3MsIGFlcyhyZW9yZGVyKGZhY3Rvcihjb211bmEpLCByZWdpc3Ryb3MpLCByZWdpc3Ryb3MsIGZpbGwgPSBFeHRyZW1vKSkgKw0KICBnZW9tX2NvbCh3aWR0aCA9IDAuNjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChmbXRfbihyZWdpc3Ryb3MpLCAiICgiLCBmbXRfcChwb3JjZW50YWplKSwgIiAlKSIpKSwNCiAgICAgICAgICAgIGhqdXN0ID0gLTAuMDgsIHNpemUgPSAzLjIpICsNCiAgZmFjZXRfd3JhcCh+RXh0cmVtbywgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlX3giKSArIGNvb3JkX2ZsaXAoKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk9yaWdlbiIgPSAiIzE2ODQ3YiIsICJEZXN0aW5vIiA9ICIjMzc1Yjk5IikpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLCAwLjM1KSkpICsNCiAgbGFicyh0aXRsZSA9ICJMYXMgY29tdW5hcyBxdWUgbcOhcyBwZXNhbiBlbiBsb3MgcmVnaXN0cm9zIiwNCiAgICAgICBzdWJ0aXRsZSA9IHBhc3RlKCJDYWRhIHBvcmNlbnRhamUgdXNhIE4gPSIsIGZtdF9uKE5faW5jbHVpZG8pLCAidmlhamVzIGluY2x1aWRvcyIpLA0KICAgICAgIHggPSAiQ29tdW5hIiwgeSA9ICJSZWdpc3Ryb3MiKQ0KYGBgDQoNCmBgYHtyIGxlY3R1cmEtY29uY2VudHJhY2lvbiwgcmVzdWx0cz0nYXNpcyd9DQpjYXQoIkxhcyBjb211bmFzICoqIiwgcGFzdGUoaGVhZChyYW5raW5nX28kY29tdW5hLCAzKSwgY29sbGFwc2UgPSAiLCAiKSwNCiAgICAiKiogcmXDum5lbiBlbCAqKiIsIGZtdF9wKGMzX28pLCAiICUqKiBkZSBsYXMgc2FsaWRhczsgbGFzIGNvbXVuYXMgKioiLA0KICAgIHBhc3RlKGhlYWQocmFua2luZ19kJGNvbXVuYSwgMyksIGNvbGxhcHNlID0gIiwgIiksICIqKiByZcO6bmVuIGVsICoqIiwgZm10X3AoYzNfZCksDQogICAgIiAlKiogZGUgbGFzIGxsZWdhZGFzLiBMYSBkaWZlcmVuY2lhIGRlIGNvbmNlbnRyYWNpw7NuIGVudHJlIGRlc3Rpbm9zIHkgb3LDrWdlbmVzIGVzIGRlICoqIiwNCiAgICBmbXRfcChjM19kIC0gYzNfbyksICIgcHVudG9zIHBvcmNlbnR1YWxlcyoqLlxuXG4iLCBzZXAgPSAiIikNCg0KY2F0KCJQYXJhIHJldW5pciBhbCBtZW5vcyBsYSBtaXRhZCBkZSBsb3MgcmVnaXN0cm9zIHNlIG5lY2VzaXRhbiAqKiIsIG1pdGFkX28sDQogICAgIiBjb211bmFzIGRlIG9yaWdlbioqIHkgKioiLCBtaXRhZF9kLCAiIGRlIGRlc3Rpbm8qKi5cblxuIiwgc2VwID0gIiIpDQpjYXQoIkxhIG1lZGlhIGRlIHNhbGlkYXMgeSBsbGVnYWRhcyBlcyBpZMOpbnRpY2EgcG9ycXVlIHN1cyB0b3RhbGVzIHNvbiBpZ3VhbGVzLiBMYXMgbWVkaWFuYXMgc29uICoqIiwNCiAgICBmbXRfcChtZWRpYW4odGVycml0b3JpbyRzYWxpZGFzKSksICIqKiB5ICoqIiwgZm10X3AobWVkaWFuKHRlcnJpdG9yaW8kbGxlZ2FkYXMpKSwNCiAgICAiKiosIHJlc3BlY3RpdmFtZW50ZS4gQ29tcGFyYXJsYXMgY29uIGxhIG1lZGlhIHBlcm1pdGUgYXByZWNpYXIgY3XDoW50byBpbmZsdXllbiBsYXMgY29tdW5hcyBkZSBtYXlvciBjb250ZW8uXG4iLCBzZXAgPSAiIikNCmBgYA0KDQpMYXMgbGxlZ2FkYXMgZXN0w6FuIG3DoXMgY29uY2VudHJhZGFzIGVuIGxhcyB0cmVzIHByaW1lcmFzIGNvbXVuYXMgcXVlIGxhcyBzYWxpZGFzLg0KDQpMYSBjb25jZW50cmFjacOzbiBkZXNjcmliZSBsYSBkaXN0cmlidWNpw7NuIGRlIGxvcyByZWdpc3Ryb3MuIFBhcmEgY29tcGFyYXIgaW50ZW5zaWRhZCBkZSBtb3ZpbGlkYWQgcG9yIGhhYml0YW50ZSBzZSBuZWNlc2l0YXLDrWFuIGRlbm9taW5hZG9yZXMgZGUgcG9ibGFjacOzbjsgcGFyYSBtZWRpciBkZW5zaWRhZCBlc3BhY2lhbCwgZWwgw6FyZWEgZGUgY2FkYSBjb211bmEuDQoNCiMjIFByZXBhcmFjacOzbiBjYXJ0b2dyw6FmaWNhIHkgdW5pw7NuIGRlIHRhYmxhcw0KDQpgYGB7ciB1bmlyfQ0KIyBJZGVudGlmaWNvIGxhIGxsYXZlIGRlbCBtYXBhIHNpbiBkZXBlbmRlciBkZSBtYXnDunNjdWxhcyBvIHRpbGRlcy4NCmNsYXZlc19tYXBhIDwtIGdzdWIoIlteYS16MC05XSIsICIiLCB0b2xvd2VyKGljb252KG5hbWVzKGNvbXVuYXMpLCB0byA9ICJBU0NJSS8vVFJBTlNMSVQiKSkpDQpsbGF2ZV9tYXBhIDwtIG5hbWVzKGNvbXVuYXMpW2NsYXZlc19tYXBhICVpbiUgYygiY29tdW5hIiwgImNvZGNvbXVuYSIsICJjb2RpZ29jb211bmEiKV0NCmlmIChsZW5ndGgobGxhdmVfbWFwYSkgIT0gMSkgc3RvcCgiSWRlbnRpZmlxdWUgbGEgY29sdW1uYSBkZSBjb211bmEgZW4gbGEgY2FydG9ncmFmw61hOiAiLCBwYXN0ZShuYW1lcyhjb211bmFzKSwgY29sbGFwc2UgPSAiLCAiKSkNCiMgTm8gYXNpZ25vIHVuIENSUyBwb3Igc3Vwb3NpY2nDs246IGRlYmUgdmVuaXIgZGVmaW5pZG8gZW4gZWwgYXJjaGl2byAucHJqLg0KaWYgKGlzLm5hKHN0X2Nycyhjb211bmFzKSkpIHN0b3AoIkZhbHRhIGVsIHNpc3RlbWEgZGUgcmVmZXJlbmNpYTogcmV2aXNlIGVsIGFyY2hpdm8gLnByai4iKQ0KY29tdW5hcyA8LSBjb211bmFzICU+JQ0KICBtdXRhdGUoY29tdW5hID0gc3VwcHJlc3NXYXJuaW5ncyhhcy5udW1lcmljKHN1YigiXkNPTVVOQVtbOnNwYWNlOl1dKiIsICIiLCBub3JtYWxpemFyX3RleHRvKC5kYXRhW1tsbGF2ZV9tYXBhXV0pKSkpKSAlPiUNCiAgc2VsZWN0KGNvbXVuYSkgJT4lDQogIHN0X21ha2VfdmFsaWQoKSAlPiUNCiAgZ3JvdXBfYnkoY29tdW5hKSAlPiUNCiAgc3VtbWFyaXNlKC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBzdF90cmFuc2Zvcm0oNDMyNikNCg0KIyBzZXRlcXVhbCgpIGNvbXBydWViYSBxdWUgZXN0w6luIGxhcyAyMiBjb211bmFzLCBzaW4gY8OzZGlnb3MgYWRpY2lvbmFsZXMuDQpzdG9waWZub3Qoc2V0ZXF1YWwoY29tdW5hcyRjb211bmEsIDE6MjIpLCAhYW55TkEoY29tdW5hcyRjb211bmEpLA0KICAgICAgICAgICFhbnkoc3RfaXNfZW1wdHkoY29tdW5hcykpLCBhbGwoc3RfaXNfdmFsaWQoY29tdW5hcykpKQ0KDQojIGxlZnRfam9pbigpIGNvbnNlcnZhIHRvZGFzIGxhcyBjb211bmFzIHkgYWdyZWdhIHN1cyBjb250ZW9zIHBvciBsYSBjbGF2ZSBjb211bmEuDQojIFVzbyBvYmpldG9zIHNmIHBhcmEgbWFudGVuZXIgZ2VvbWV0csOtYSB5IGF0cmlidXRvcyB1bmlkb3MgZW4gY2FkYSBvcGVyYWNpw7NuLg0KbWFwYV9vcmlnZW4gPC0gY29tdW5hcyAlPiUgbGVmdF9qb2luKG9yaWdlbl9yZXN1bWVuLCBieSA9ICJjb211bmEiKQ0KbWFwYV9kZXN0aW5vIDwtIGNvbXVuYXMgJT4lIGxlZnRfam9pbihkZXN0aW5vX3Jlc3VtZW4sIGJ5ID0gImNvbXVuYSIpDQpjb250ZW9zIDwtIGMoInRvdGFsIiwgImJpY2ljbGV0YXMiLCAibW90b3MiLCAiYXV0b21vdmlsZXMiKQ0KbWFwYV9vcmlnZW4gPC0gbWFwYV9vcmlnZW4gJT4lIG11dGF0ZShhY3Jvc3MoYWxsX29mKGNvbnRlb3MpLCB+cmVwbGFjZV9uYSgueCwgMCkpKQ0KbWFwYV9kZXN0aW5vIDwtIG1hcGFfZGVzdGlubyAlPiUgbXV0YXRlKGFjcm9zcyhhbGxfb2YoY29udGVvcyksIH5yZXBsYWNlX25hKC54LCAwKSkpDQoNCiMgQ2FkYSB2aWFqZSBkZWJlIHN1bWFyIHVuYSB2ZXogZW4gb3JpZ2VuIHkgdW5hIHZleiBlbiBkZXN0aW5vLg0Kc3RvcGlmbm90KHN1bShtYXBhX29yaWdlbiR0b3RhbCkgPT0gbnJvdyh2aWFqZXMpLA0KICAgICAgICAgIHN1bShtYXBhX2Rlc3Rpbm8kdG90YWwpID09IG5yb3codmlhamVzKSkNCmtuaXRyOjprYWJsZShzdF9kcm9wX2dlb21ldHJ5KG1hcGFfb3JpZ2VuKSwgY2FwdGlvbiA9ICJWZXJpZmljYWNpw7NuIGRlIGxhIHVuacOzbjogb3LDrWdlbmVzIikNCg0KIyBzdF9tYWtlX3ZhbGlkKCkgcmVwYXJhIGdlb21ldHLDrWFzIHkgc3RfdHJhbnNmb3JtKDQzMjYpIHRyYW5zZm9ybWEgYWwgc2lzdGVtYSBXR1MgODQgcGFyYQ0KIyB2aXN1YWxpemFyLiBBZ3J1cGFyIHkgcmVzdW1pciB1bmUgbGFzIHBhcnRlcyBkZSB1bmEgbWlzbWEgY29tdW5hLiBzdF9kcm9wX2dlb21ldHJ5KCkgZGVqYQ0KIyDDum5pY2FtZW50ZSBzdXMgYXRyaWJ1dG9zIHBhcmEgbW9zdHJhciB1bmEgdGFibGEuIGFjcm9zcygpIGFwbGljYSB1bmEgb3BlcmFjacOzbiBhIHZhcmlhcw0KIyBjb2x1bW5hczsgLnggcmVwcmVzZW50YSBjYWRhIGNvbHVtbmEgeSByZXBsYWNlX25hKCkgcmVlbXBsYXphIHN1cyBhdXNlbnRlcy4gQXF1w60gZWwgY2Vybw0KIyBzaWduaWZpY2EgcXVlIHVuYSBjb211bmEgdsOhbGlkYSBubyB0aWVuZSByZWdpc3Ryb3MgZW4gZWwgcmVzdW1lbjsgbm8gc2UgYXBsaWNhIGEgbGENCiMgaW5mb3JtYWNpw7NuIGZhbHRhbnRlIGRlIGxhIGVuY3Vlc3RhLiBzdG9waWZub3QoKSBkZXRpZW5lIGxhIGVqZWN1Y2nDs24gY3VhbmRvIHVuYQ0KIyBjb21wcm9iYWNpw7NuIG5vIHNlIGN1bXBsZS4NCmBgYA0KDQoNCiMgTWFwYXMgaW50ZXJhY3Rpdm9zDQoNClBhcmEgbGVlcmxvcywgZW1wZXphbW9zIHBvciBlbCBtYXBhIGdlbmVyYWwgeSBsdWVnbyBtaXJhbW9zIGNhZGEgbW9kby4gVW4gdG9ubyBtw6FzIG9zY3VybyBpbmRpY2EgdW4gY29udGVvIG1heW9yIGRlIHJlZ2lzdHJvcywgbm8gbmVjZXNhcmlhbWVudGUgbWF5b3IgdXNvIHBvciBoYWJpdGFudGUuIEFsIGNvbXBhcmFyIHVuYSBtaXNtYSBjb211bmEgZW4gb3JpZ2VuIHkgZGVzdGlubyBwb2RlbW9zIHJlY29ub2NlciBzaSBjb25jZW50cmEgbcOhcyBzYWxpZGFzIG8gbcOhcyBsbGVnYWRhcy4NCg0KQ2FkYSBtYXBhIG11ZXN0cmEgKipjb250ZW9zIGRlIHJlZ2lzdHJvcyoqLiBBbCBwdWxzYXIgdW5hIGNvbXVuYSBzZSBjb25zdWx0YSBzdSB2YWxvciBleGFjdG8uIFBhcmEgY29tcGFyYXIgb3JpZ2VuIHkgZGVzdGlubyBkZSB1bmEgbWlzbWEgY2F0ZWdvcsOtYSBzZSB1c2FuIGxvcyBtaXNtb3MgaW50ZXJ2YWxvcyBkZSBjb2xvci4gTG9zIGludGVydmFsb3MgcHVlZGVuIGNhbWJpYXIgZW50cmUgbW9kb3M7IHBvciB0YW50bywgbGEgY29tcGFyYWNpw7NuIGVudHJlIG1lZGlvcyBkZSB0cmFuc3BvcnRlIGRlYmUgY29uc2lkZXJhciBsb3MgdmFsb3JlcyB5IG5vIMO6bmljYW1lbnRlIGxvcyBjb2xvcmVzLg0KDQpgYGB7ciBmdW5jaW9uLW1hcGF9DQojIFNlIGRpYnVqYW4gbGFzIGNvbXVuYXMgZGlyZWN0YW1lbnRlIHkgc2UgYWp1c3RhIGxhIHZpc3RhIGEgc3UgZXh0ZW5zacOzbi4NCiMgRWwgZm9uZG8gT3BlblN0cmVldE1hcCBubyByZXF1aWVyZSB1bmEgY2xhdmUgQVBJIHBhcmEgZXN0YSB2aXN1YWxpemFjacOzbi4NCm1hcGEgPC0gZnVuY3Rpb24oY2FwYSwgdmFyaWFibGUsIHRpdHVsbykgew0KICB2YWxvcmVzIDwtIGNhcGFbW3ZhcmlhYmxlXV0NCiAgdG90YWwgPC0gc3VtKHZhbG9yZXMpDQogIG1heGltbyA8LSBtYXgoMSwgbWFwYV9vcmlnZW5bW3ZhcmlhYmxlXV0sIG1hcGFfZGVzdGlub1tbdmFyaWFibGVdXSkNCiAgcGFsZXRhIDwtIGNvbG9yQmluKCJCbHVlcyIsIGRvbWFpbiA9IGMoMCwgbWF4aW1vKSwNCiAgICAgICAgICAgICAgICAgICAgIGJpbnMgPSBzZXEoMCwgbWF4aW1vLCBsZW5ndGgub3V0ID0gNikpDQogIGxpbWl0ZXMgPC0gc3RfYmJveChjYXBhKQ0KICBwb3JjZW50YWplIDwtIHJvdW5kKDEwMCAqIHZhbG9yZXMgLyBtYXgoMSwgdG90YWwpLCAxKQ0KICBkZXRhbGxlIDwtIHBhc3RlMCgiPGI+IiwgdGl0dWxvLCAiPC9iPjxicj5Db211bmEgIiwgY2FwYSRjb211bmEsDQogICAgICAgICAgICAgICAgICAgICI8YnI+UmVnaXN0cm9zOiAiLCBmb3JtYXQodmFsb3JlcywgYmlnLm1hcmsgPSAiLiIsIHRyaW0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgICAgIjxicj5QYXJ0aWNpcGFjacOzbjogIiwgcG9yY2VudGFqZSwgIiAlIiwNCiAgICAgICAgICAgICAgICAgICAgIjxicj5Ub3RhbCBkZSBlc3RhIGNhdGVnb3LDrWEgKE4pOiAiLCB0b3RhbCkNCg0KICBsZWFmbGV0KGNhcGEsIHdpZHRoID0gIjEwMCUiLCBoZWlnaHQgPSA0ODApICU+JQ0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRFc3JpLldvcmxkVG9wb01hcCkgJT4lDQogIGFkZFBvbHlnb25zKA0KICAgIGZpbGxDb2xvciA9IHBhbGV0YSh2YWxvcmVzKSwNCiAgICBmaWxsT3BhY2l0eSA9IDAuNzUsDQogICAgY29sb3IgPSAiI0ZGRkZGRiIsDQogICAgd2VpZ2h0ID0gMS4yLA0KICAgIGxhYmVsID0gcGFzdGUoIkNvbXVuYSIsIGNhcGEkY29tdW5hLCAi4oCUIiwgdmFsb3JlcywgInJlZ2lzdHJvcyIpLA0KICAgIHBvcHVwID0gZGV0YWxsZSwNCiAgICBoaWdobGlnaHRPcHRpb25zID0gaGlnaGxpZ2h0T3B0aW9ucygNCiAgICAgIHdlaWdodCA9IDMsDQogICAgICBjb2xvciA9ICIjMTYzMjQ3Ig0KICAgICkNCiAgKSAlPiUNCiAgICBhZGRMZWdlbmQocGFsID0gcGFsZXRhLCB2YWx1ZXMgPSB2YWxvcmVzLA0KICAgICAgICAgICAgICB0aXRsZSA9IHBhc3RlMCh0aXR1bG8sICI8YnI+TiA9ICIsIHRvdGFsKSwgcG9zaXRpb24gPSAiYm90dG9tcmlnaHQiKSAlPiUNCiAgICBmaXRCb3VuZHMoYXMubnVtZXJpYyhsaW1pdGVzWyJ4bWluIl0pLCBhcy5udW1lcmljKGxpbWl0ZXNbInltaW4iXSksDQogICAgICAgICAgICAgIGFzLm51bWVyaWMobGltaXRlc1sieG1heCJdKSwgYXMubnVtZXJpYyhsaW1pdGVzWyJ5bWF4Il0pKQ0KfQ0KDQojIGNvbG9yQmluKCkgYXNpZ25hIGNvbG9yZXMgcG9yIGludGVydmFsb3MgY29tdW5lcyBkZSBvcmlnZW4geSBkZXN0aW5vLg0KIyBhZGRUaWxlcygpIGFncmVnYSBlbCBmb25kbzsgYWRkUG9seWdvbnMoKSByZXByZXNlbnRhIGxhcyBjb211bmFzIGNvbiBzdXMgY29udGVvcy4NCiMgcG9wdXAgbXVlc3RyYSBsb3MgZGF0b3MgYWwgcHVsc2FyOyBsYWJlbCBsb3MgbXVlc3RyYSBhbCBwYXNhciBlbCBjdXJzb3IuDQojIGZpdEJvdW5kcygpIGNlbnRyYSBlbCBtYXBhIGVuIGxhIGV4dGVuc2nDs24gZGUgbGFzIGNvbXVuYXMuDQpgYGANCg0KYGBge3IgZnVuY2lvbi1sZWN0dXJhLW1hcGEsIGluY2x1ZGU9RkFMU0V9DQppbnRlcnByZXRhcl9tYXBhIDwtIGZ1bmN0aW9uKGV4dHJlbW8sIHZhcmlhYmxlLCBub21icmUpIHsNCiAgY2FwYSA8LSBpZiAoZXh0cmVtbyA9PSAiT3JpZ2VuIikgbWFwYV9vcmlnZW4gZWxzZSBtYXBhX2Rlc3Rpbm8NCiAgdGFibGEgPC0gc3RfZHJvcF9nZW9tZXRyeShjYXBhKSAlPiUNCiAgICB0cmFuc211dGUoY29tdW5hLCByZWdpc3Ryb3MgPSAuZGF0YVtbdmFyaWFibGVdXSwgdG90YWxfY29tdW5hID0gdG90YWwpICU+JQ0KICAgIGFycmFuZ2UoZGVzYyhyZWdpc3Ryb3MpLCBjb211bmEpDQogIE4gPC0gc3VtKHRhYmxhJHJlZ2lzdHJvcykNCiAgaWYgKE4gPT0gMCkgew0KICAgIGNhdCgiTm8gaGF5IHJlZ2lzdHJvcyBpbmNsdWlkb3MgcGFyYSBlc3RhIGNhdGVnb3LDrWE7IG5vIHNlIGludGVycHJldGEgc3UgZGlzdHJpYnVjacOzbiB0ZXJyaXRvcmlhbC5cbiIpDQogICAgcmV0dXJuKGludmlzaWJsZShOVUxMKSkNCiAgfQ0KICBsaWRlcmVzIDwtIHRhYmxhICU+JSBmaWx0ZXIocmVnaXN0cm9zID09IG1heChyZWdpc3Ryb3MpKQ0KICB0b3AzIDwtIGhlYWQodGFibGEsIDMpDQogIHZlcmJvIDwtIGlmIChleHRyZW1vID09ICJPcmlnZW4iKSAic2FsZW4iIGVsc2UgImxsZWdhbiINCiAgY2F0KCIqKkxlY3R1cmEgZGVsIG1hcGEuKiogRW4gIiwgbm9tYnJlLCAiIHNlIHJlZ2lzdHJhbiAqKiIsIGZtdF9uKE4pLA0KICAgICAgIioqIGRlc3BsYXphbWllbnRvcy4gRWwgbWF5b3IgY29udGVvIGNvcnJlc3BvbmRlIGEgbGEocykgY29tdW5hKHMpICoqIiwNCiAgICAgIHBhc3RlKGxpZGVyZXMkY29tdW5hLCBjb2xsYXBzZSA9ICIsICIpLCAiKiosIGNvbiAqKiIsIGZtdF9uKGxpZGVyZXMkcmVnaXN0cm9zWzFdKSwNCiAgICAgICIgcmVnaXN0cm9zIGNhZGEgdW5hICgiLCBmbXRfcCgxMDAgKiBsaWRlcmVzJHJlZ2lzdHJvc1sxXSAvIE4pLCAiICUgZGUgbGEgY2F0ZWdvcsOtYSkqKi4gIiwNCiAgICAgICJFbiBsYXMgY29tdW5hcyAqKiIsIHBhc3RlKHRvcDMkY29tdW5hLCBjb2xsYXBzZSA9ICIsICIpLCAiKiogIiwgdmVyYm8sDQogICAgICAiICoqIiwgZm10X24oc3VtKHRvcDMkcmVnaXN0cm9zKSksICIgcmVnaXN0cm9zICgiLCBmbXRfcCgxMDAgKiBzdW0odG9wMyRyZWdpc3Ryb3MpIC8gTiksDQogICAgICAiICUpKiouXG5cbiIsIHNlcCA9ICIiKQ0KICBjYXQoIkhheSAqKiIsIHN1bSh0YWJsYSRyZWdpc3Ryb3MgPiAwKSwgIiBjb211bmFzIGNvbiByZWdpc3Ryb3MqKiB5ICoqIiwNCiAgICAgIHN1bSh0YWJsYSRyZWdpc3Ryb3MgPT0gMCksICIgc2luIHJlZ2lzdHJvcyoqIGRlIGVzdGEgY2F0ZWdvcsOtYSBlbiBsYSBiYXNlIGluY2x1aWRhLiAiLCBzZXAgPSAiIikNCiAgaWYgKHZhcmlhYmxlICE9ICJ0b3RhbCIpIHsNCiAgICBjdW90YV9saWRlciA8LSAxMDAgKiBsaWRlcmVzJHJlZ2lzdHJvc1sxXSAvIGxpZGVyZXMkdG90YWxfY29tdW5hWzFdDQogICAgY2F0KCJFbiBsYSBwcmltZXJhIGNvbXVuYSBkZWwgcmFua2luZywgZXN0ZSBtb2RvIHJlcHJlc2VudGEgZWwgKioiLCBmbXRfcChjdW90YV9saWRlciksDQogICAgICAgICIgJSoqIGRlbCB0b3RhbCBkZSAiLCB0b2xvd2VyKGV4dHJlbW8pLCAiIGRlIGVzYSBjb211bmEuICIsDQogICAgICAgICJFc3RhIHByb3BvcmNpw7NuIGxvY2FsIHJlc3BvbmRlIHVuYSBwcmVndW50YSBkaXN0aW50YSBhbCBwb3JjZW50YWplIGRlIGxhIGNhdGVnb3LDrWEgZW4gdG9kYSBsYSBjaXVkYWQgYW5hbGl6YWRhLlxuIiwgc2VwID0gIiIpDQogIH0gZWxzZSB7DQogICAgY2F0KCJFbCB0b25vIGRlbCBtYXBhIHJlcHJlc2VudGEgdm9sdW1lbiByZWdpc3RyYWRvOyBubyBtaWRlIGRlbnNpZGFkIGRlIHBvYmxhY2nDs24gbmkgY29uZ2VzdGnDs24uXG4iKQ0KICB9DQp9DQpgYGANCg0KIyMgT3LDrWdlbmVzDQoNCiMjIyBHZW5lcmFsDQoNCmBgYHtyIG1hcGEtMX0NCm1hcGEobWFwYV9vcmlnZW4sICJ0b3RhbCIsICIxLiBPcmlnZW46IHRvZG9zIGxvcyBtb2RvcyIpDQpgYGANCg0KYGBge3IgbGVjdHVyYS1tYXBhLTEsIGVjaG89RkFMU0UsIHJlc3VsdHM9J2FzaXMnfQ0KaW50ZXJwcmV0YXJfbWFwYSgiT3JpZ2VuIiwgInRvdGFsIiwgInRvZG9zIGxvcyBtb2RvcyIpDQpgYGANCg0KIyMjIEJpY2ljbGV0YQ0KDQpgYGB7ciBtYXBhLTJ9DQptYXBhKG1hcGFfb3JpZ2VuLCAiYmljaWNsZXRhcyIsICIyLiBPcmlnZW46IGJpY2ljbGV0YSIpDQpgYGANCg0KYGBge3IgbGVjdHVyYS1tYXBhLTIsIGVjaG89RkFMU0UsIHJlc3VsdHM9J2FzaXMnfQ0KaW50ZXJwcmV0YXJfbWFwYSgiT3JpZ2VuIiwgImJpY2ljbGV0YXMiLCAiYmljaWNsZXRhIikNCmBgYA0KDQojIyMgTW90b2NpY2xldGENCg0KYGBge3IgbWFwYS0zfQ0KbWFwYShtYXBhX29yaWdlbiwgIm1vdG9zIiwgIjMuIE9yaWdlbjogbW90b2NpY2xldGEiKQ0KYGBgDQoNCmBgYHtyIGxlY3R1cmEtbWFwYS0zLCBlY2hvPUZBTFNFLCByZXN1bHRzPSdhc2lzJ30NCmludGVycHJldGFyX21hcGEoIk9yaWdlbiIsICJtb3RvcyIsICJtb3RvY2ljbGV0YSIpDQpgYGANCg0KIyMjIEF1dG9tw7N2aWwNCg0KYGBge3IgbWFwYS00fQ0KbWFwYShtYXBhX29yaWdlbiwgImF1dG9tb3ZpbGVzIiwgIjQuIE9yaWdlbjogYXV0b23Ds3ZpbCIpDQpgYGANCg0KYGBge3IgbGVjdHVyYS1tYXBhLTQsIGVjaG89RkFMU0UsIHJlc3VsdHM9J2FzaXMnfQ0KaW50ZXJwcmV0YXJfbWFwYSgiT3JpZ2VuIiwgImF1dG9tb3ZpbGVzIiwgImF1dG9tw7N2aWwiKQ0KYGBgDQoNCiMjIERlc3Rpbm9zDQoNCiMjIyBHZW5lcmFsDQoNCmBgYHtyIG1hcGEtNX0NCm1hcGEobWFwYV9kZXN0aW5vLCAidG90YWwiLCAiNS4gRGVzdGlubzogdG9kb3MgbG9zIG1vZG9zIikNCmBgYA0KDQpgYGB7ciBsZWN0dXJhLW1hcGEtNSwgZWNobz1GQUxTRSwgcmVzdWx0cz0nYXNpcyd9DQppbnRlcnByZXRhcl9tYXBhKCJEZXN0aW5vIiwgInRvdGFsIiwgInRvZG9zIGxvcyBtb2RvcyIpDQpgYGANCg0KIyMjIEJpY2ljbGV0YQ0KDQpgYGB7ciBtYXBhLTZ9DQptYXBhKG1hcGFfZGVzdGlubywgImJpY2ljbGV0YXMiLCAiNi4gRGVzdGlubzogYmljaWNsZXRhIikNCmBgYA0KDQpgYGB7ciBsZWN0dXJhLW1hcGEtNiwgZWNobz1GQUxTRSwgcmVzdWx0cz0nYXNpcyd9DQppbnRlcnByZXRhcl9tYXBhKCJEZXN0aW5vIiwgImJpY2ljbGV0YXMiLCAiYmljaWNsZXRhIikNCmBgYA0KDQojIyMgTW90b2NpY2xldGENCg0KYGBge3IgbWFwYS03fQ0KbWFwYShtYXBhX2Rlc3Rpbm8sICJtb3RvcyIsICI3LiBEZXN0aW5vOiBtb3RvY2ljbGV0YSIpDQpgYGANCg0KYGBge3IgbGVjdHVyYS1tYXBhLTcsIGVjaG89RkFMU0UsIHJlc3VsdHM9J2FzaXMnfQ0KaW50ZXJwcmV0YXJfbWFwYSgiRGVzdGlubyIsICJtb3RvcyIsICJtb3RvY2ljbGV0YSIpDQpgYGANCg0KIyMjIEF1dG9tw7N2aWwNCg0KYGBge3IgbWFwYS04fQ0KbWFwYShtYXBhX2Rlc3Rpbm8sICJhdXRvbW92aWxlcyIsICI4LiBEZXN0aW5vOiBhdXRvbcOzdmlsIikNCmBgYA0KDQpgYGB7ciBsZWN0dXJhLW1hcGEtOCwgZWNobz1GQUxTRSwgcmVzdWx0cz0nYXNpcyd9DQppbnRlcnByZXRhcl9tYXBhKCJEZXN0aW5vIiwgImF1dG9tb3ZpbGVzIiwgImF1dG9tw7N2aWwiKQ0KYGBgDQoNCiMjIMK/UXXDqSBub3MgZGljZW4gbG9zIG1hcGFzPw0KDQpgYGB7ciBsZWN0dXJhLW1hcGFzLCByZXN1bHRzPSdhc2lzJ30NCmxlY3R1cmEgPC0gbGFwcGx5KGNvbnRlb3MsIGZ1bmN0aW9uKHYpIHsNCiAgbyA8LSBtYXBhX29yaWdlbiAlPiUgc3RfZHJvcF9nZW9tZXRyeSgpICU+JSBmaWx0ZXIoLmRhdGFbW3ZdXSA9PSBtYXgoLmRhdGFbW3ZdXSkpDQogIGQgPC0gbWFwYV9kZXN0aW5vICU+JSBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lIGZpbHRlciguZGF0YVtbdl1dID09IG1heCguZGF0YVtbdl1dKSkNCiAgTiA8LSBzdW0obWFwYV9vcmlnZW5bW3ZdXSkNCiAgZGF0YS5mcmFtZShDYXRlZ29yaWEgPSB2LCBWaWFqZXMgPSBOLA0KICAgIE9yaWdlbmVzX3ByaW5jaXBhbGVzID0gaWYgKE4gPiAwKSBwYXN0ZShvJGNvbXVuYSwgY29sbGFwc2UgPSAiLCAiKSBlbHNlICJTaW4gcmVnaXN0cm9zIiwNCiAgICBNYXhpbW9fb3JpZ2VuID0gbWF4KG1hcGFfb3JpZ2VuW1t2XV0pLA0KICAgIERlc3Rpbm9zX3ByaW5jaXBhbGVzID0gaWYgKE4gPiAwKSBwYXN0ZShkJGNvbXVuYSwgY29sbGFwc2UgPSAiLCAiKSBlbHNlICJTaW4gcmVnaXN0cm9zIiwNCiAgICBNYXhpbW9fZGVzdGlubyA9IG1heChtYXBhX2Rlc3Rpbm9bW3ZdXSkpDQp9KQ0KcHJpbnQoa25pdHI6OmthYmxlKGJpbmRfcm93cyhsZWN0dXJhKSwgY29sLm5hbWVzID0gYygiQ2F0ZWdvcsOtYSIsICJOIiwgIkNvbXVuYXMgY29uIG3DoXMgc2FsaWRhcyIsICJuIiwgIkNvbXVuYXMgY29uIG3DoXMgbGxlZ2FkYXMiLCAibiIpLA0KICBjYXB0aW9uID0gIkxlY3R1cmEgY29uanVudGEgZGUgbG9zIG1hcGFzOyBzZSBjb25zZXJ2YW4gdG9kb3MgbG9zIGVtcGF0ZXMiKSkNCmBgYA0KDQpgYGB7ciBkaWZlcmVuY2lhLXBvcmNlbnR1YWwsIGVjaG89RkFMU0V9DQptb2RvcyA8LSBjKCJCaWNpY2xldGEiLCAiTW90b2NpY2xldGEiLCAiQXV0b23Ds3ZpbCIpDQoNCiMgUGFydGljaXBhY2nDs24gZGUgY2FkYSBtb2RvIGVuIHRvZG9zIGxvcyB2aWFqZXMgaW5jbHVpZG9zLg0KcGFydGljaXBhY2lvbl9nZW5lcmFsIDwtIHZpYWplcyAlPiUNCiAgY291bnQobW9kbywgbmFtZSA9ICJyZWdpc3Ryb3NfZ2VuZXJhbCIpICU+JQ0KICBtdXRhdGUocG9yY2VudGFqZV9nZW5lcmFsID0gMTAwICogcmVnaXN0cm9zX2dlbmVyYWwgLyBucm93KHZpYWplcykpDQoNCiMgUGFydGljaXBhY2nDs24gZGUgY2FkYSBtb2RvIGVudHJlIGxhcyBsbGVnYWRhcyBhIGxhIGNvbXVuYSAyLg0KbGxlZ2FkYXNfY29tdW5hMiA8LSB2aWFqZXMgJT4lDQogIGZpbHRlcihkZXN0aW5vID09IDIpDQoNCnBhcnRpY2lwYWNpb25fY29tdW5hMiA8LSBsbGVnYWRhc19jb211bmEyICU+JQ0KICBjb3VudChtb2RvLCBuYW1lID0gInJlZ2lzdHJvc19jb211bmEyIikNCg0KdGFibGFfZGlmZXJlbmNpYXMgPC0gZGF0YS5mcmFtZShtb2RvID0gbW9kb3MpICU+JQ0KICBsZWZ0X2pvaW4ocGFydGljaXBhY2lvbl9nZW5lcmFsLCBieSA9ICJtb2RvIikgJT4lDQogIGxlZnRfam9pbihwYXJ0aWNpcGFjaW9uX2NvbXVuYTIsIGJ5ID0gIm1vZG8iKSAlPiUNCiAgbXV0YXRlKA0KICAgIHJlZ2lzdHJvc19nZW5lcmFsID0gcmVwbGFjZV9uYShyZWdpc3Ryb3NfZ2VuZXJhbCwgMCksDQogICAgcG9yY2VudGFqZV9nZW5lcmFsID0gcmVwbGFjZV9uYShwb3JjZW50YWplX2dlbmVyYWwsIDApLA0KICAgIHJlZ2lzdHJvc19jb211bmEyID0gcmVwbGFjZV9uYShyZWdpc3Ryb3NfY29tdW5hMiwgMCksDQogICAgcG9yY2VudGFqZV9jb211bmEyID0gaWYgKG5yb3cobGxlZ2FkYXNfY29tdW5hMikgPiAwKSB7DQogICAgICAxMDAgKiByZWdpc3Ryb3NfY29tdW5hMiAvIG5yb3cobGxlZ2FkYXNfY29tdW5hMikNCiAgICB9IGVsc2Ugew0KICAgICAgTkFfcmVhbF8NCiAgICB9LA0KICAgIGRpZmVyZW5jaWFfcHAgPSBwb3JjZW50YWplX2NvbXVuYTIgLSBwb3JjZW50YWplX2dlbmVyYWwNCiAgKSAlPiUNCiAgc2VsZWN0KG1vZG8sIHBvcmNlbnRhamVfZ2VuZXJhbCwgcG9yY2VudGFqZV9jb211bmEyLCBkaWZlcmVuY2lhX3BwKQ0KDQprbml0cjo6a2FibGUoDQogIHRhYmxhX2RpZmVyZW5jaWFzLA0KICBkaWdpdHMgPSAyLA0KICBjb2wubmFtZXMgPSBjKA0KICAgICJNb2RvIiwNCiAgICAiUGFydGljaXBhY2nDs24gZ2VuZXJhbCAoJSkiLA0KICAgICJQYXJ0aWNpcGFjacOzbiBlbiBsbGVnYWRhcyBhIGNvbXVuYSAyICglKSIsDQogICAgIkRpZmVyZW5jaWEgKHBwKSINCiAgKSwNCiAgY2FwdGlvbiA9ICJDb21wb3NpY2nDs24gbW9kYWw6IGNvbXVuYSAyIGZyZW50ZSBhbCBjb25qdW50byBkZSB2aWFqZXMgaW5jbHVpZG9zIiwNCiAgZm9ybWF0LmFyZ3MgPSBsaXN0KGRlY2ltYWwubWFyayA9ICIsIiwgbnNtYWxsID0gMikNCikNCmBgYA0KDQpVbmEgZGlmZXJlbmNpYSBwb3NpdGl2YSBpbmRpY2EgcXVlIGVsIG1vZG8gdGllbmUgbWF5b3IgcGFydGljaXBhY2nDs24gZW50cmUNCmxhcyBsbGVnYWRhcyBhIGxhIGNvbXVuYSAyIHF1ZSBlbiBlbCBjb25qdW50byBhbmFsaXphZG87IHVuYSBuZWdhdGl2YSBpbmRpY2ENCm1lbm9yIHBhcnRpY2lwYWNpw7NuLiBMb3MgZGVub21pbmFkb3JlcyBpbmNsdXllbiB0b2RvcyBsb3MgbW9kb3MsIGF1bnF1ZQ0KbGEgdGFibGEgcHJlc2VudGEgw7puaWNhbWVudGUgYmljaWNsZXRhLCBtb3RvY2ljbGV0YSB5IGF1dG9tw7N2aWwuDQoNCi0gKipMYSBjb211bmEgMiBjb25jZW50cmEgbGEgbWF5b3IgYXRyYWNjacOzbiBkZSB2aWFqZXM6KiogcmVjaWJlIDMuNzcyIGxsZWdhZGFzICgxNywwNCAlIGRlbCB0b3RhbCkgeSBnZW5lcmEgMi4yNDkgc2FsaWRhcyAoMTAsMTYgJSkuIFN1IHNhbGRvIGRlICoqKzEuNTIzIHJlZ2lzdHJvcyoqIGVxdWl2YWxlIGEgMTY4IGxsZWdhZGFzIHBvciBjYWRhIDEwMCBzYWxpZGFzLiBFc3RhIGNvbXVuYSBpbmNsdXllIGJhcnJpb3MgY29tbyBHcmFuYWRhLCBDZW50ZW5hcmlvLCBWZXJzYWxsZXMsIFNhbiBWaWNlbnRlLCBTYW50YSBNw7NuaWNhLCBDaGlwaWNoYXBlLCBMYSBGbG9yYSwgUHJhZG9zIGRlbCBOb3J0ZSwgVmlwYXNhLCBKdWFuYW1iw7ogeSBOb3JtYW5kw61hLCBlbnRyZSBvdHJvcy4NCg0KLSAqKlN1IGNvbmZpZ3VyYWNpw7NuIHRlcnJpdG9yaWFsIHBlcm1pdGUgcGxhbnRlYXIgdW5hIGV4cGxpY2FjacOzbiBkZWwgcGF0csOzbjoqKiBsYSBwcmVzZW5jaWEgZGUgb2ZlcnRhIGdhc3Ryb27Ds21pY2EgZW4gR3JhbmFkYSwgY29tZXJjaWFsIGVuIENoaXBpY2hhcGUgeSBDZW50ZW5hcmlvLCB5IGRlIGFsb2phbWllbnRvIGVuIGVsIGVudG9ybm8gZGUgbGEgYXZlbmlkYSBTZXh0YeKAk0NoaXBpY2hhcGUgaGFjZSBwbGF1c2libGUgcXVlIHBhcnRlIGRlIGxhcyBsbGVnYWRhcyBlc3TDqSByZWxhY2lvbmFkYSBjb24gZWwgYWNjZXNvIGEgYmllbmVzIHkgc2VydmljaW9zLiBFc3RhIGludGVycHJldGFjacOzbiBlcyBjb25zaXN0ZW50ZSBjb24gc3UgcGFwZWwgcmVjZXB0b3IsIHBlcm8gbm8gZGVtdWVzdHJhIGVsIG1vdGl2byBkZSBsb3MgdmlhamVzIG5pIHBlcm1pdGUgYXRyaWJ1aXIgbGFzIGxsZWdhZGFzIGEgdW4gYmFycmlvIG8gZXN0YWJsZWNpbWllbnRvIHBhcnRpY3VsYXIuDQoNCi0gKipMYSBhdHJhY2Npw7NuIHNlIG1hbnRpZW5lIGVudHJlIG1lZGlvcyBkZSB0cmFuc3BvcnRlOioqIGxhIGNvbXVuYSAyIHJlY2liZSBlbCAxNSw4NiAlIGRlIGxvcyBkZXN0aW5vcyBlbiBiaWNpY2xldGEsIGVsIDE2LDgzICUgZW4gbW90b2NpY2xldGEgeSBlbCAxNyw2NCAlIGVuIGF1dG9tw7N2aWwuIExhIGNvaW5jaWRlbmNpYSBzdWdpZXJlIHF1ZSBzdSBpbXBvcnRhbmNpYSBjb21vIGRlc3Rpbm8gdHJhc2NpZW5kZSB1biB2ZWjDrWN1bG8gZXNwZWPDrWZpY28uIEFkZW3DoXMsIGxhcyBtb3RvY2ljbGV0YXMgcmVwcmVzZW50YW4gZWwgNDUsOTcgJSBkZSBzdXMgbGxlZ2FkYXMsIHByb3BvcmNpw7NuIGNlcmNhbmEgYWwgNDYsNTMgJSBkZWwgY29uanVudG8gYW5hbGl6YWRvOiBzdSBlbGV2YWRvIGNvbnRlbyByZWZsZWphIHByaW5jaXBhbG1lbnRlIGVsIHZvbHVtZW4gZ2VuZXJhbCBkZSB2aWFqZXMgeSBubyB1bmEgZXNwZWNpYWxpemFjacOzbiBtYXJjYWRhIGVuIGVzdGUgbW9kby4NCg0KLSAqKkxhIGNvbXVuYSAxOSBwcmVzZW50YSB1biBwYXRyw7NuIGRpc3RpbnRvIGVuIGxvcyBvcsOtZ2VuZXM6KiogbGlkZXJhIGxhcyBzYWxpZGFzIGVuIGF1dG9tw7N2aWwsIGNvbiA5MjIgcmVnaXN0cm9zICgxMCw3MiAlIGRlIGVzZSBtb2RvKS4gSW5jbHV5ZSBiYXJyaW9zIGNvbW8gU2FuIEZlcm5hbmRvIFZpZWpvLCBTYW4gRmVybmFuZG8gTnVldm8sIFVyYmFuaXphY2nDs24gVGVxdWVuZGFtYSwgTnVldmEgVGVxdWVuZGFtYSwgRWwgTGlkbywgRWwgUmVmdWdpbywgTWlyYWZsb3JlcywgU2FudGEgSXNhYmVsLCBDaGFtcGFnbmF0LCBMb3MgQ8OhbWJ1bG9zLCBQYW1wYWxpbmRhIHkgQ3VhcnRvIGRlIExlZ3Vh4oCTR3VhZGFsdXBlLCBlbnRyZSBvdHJvcy4gTGEgcHJlc2VuY2lhIGRlIHZpdmllbmRhIHkgZXF1aXBhbWllbnRvcyBkZSBzYWx1ZCwgZWR1Y2FjacOzbiB5IGRlcG9ydGUgcGVybWl0ZSBwbGFudGVhciBxdWUgbGFzIHNhbGlkYXMgcHVlZGVuIGNvbWJpbmFyIGRlc3BsYXphbWllbnRvcyBkZXNkZSBob2dhcmVzIHkgZGVzZGUgbHVnYXJlcyBkZSBhY3RpdmlkYWQuIFBvciBlbGxvLCBlbCBvcmlnZW4gZGUgdW4gdmlhamUgbm8gZGViZSBpbnRlcnByZXRhcnNlIGF1dG9tw6F0aWNhbWVudGUgY29tbyBsYSByZXNpZGVuY2lhIGRlbCB2aWFqZXJvLg0KDQotICoqTG9zIHJlc3VsdGFkb3Mgc3VnaWVyZW4gZnVuY2lvbmVzIHRlcnJpdG9yaWFsZXMgZGlmZXJlbmNpYWRhcywgcGVybyBubyB1bmEgY29uZXhpw7NuIGRpcmVjdGEgZGVtb3N0cmFkYToqKiBsYSBjb211bmEgMiBkZXN0YWNhIGNvbW8gcmVjZXB0b3JhIGRlIHZhcmlvcyBtb2RvcyB5IGxhIGNvbXVuYSAxOSBjb21vIHByaW5jaXBhbCBvcmlnZW4gZGUgYXV0b23Ds3ZpbGVzLiBFc3RvIGp1c3RpZmljYSBleGFtaW5hciBlbCBpbnRlcmNhbWJpbyBlbnRyZSBhbWJhcyBlbiBsYSBtYXRyaXogb3JpZ2Vu4oCTZGVzdGluby4gU2luIGVtYmFyZ28sIHN1cyBwb3NpY2lvbmVzIGVuIGxvcyByYW5raW5ncyBubyBwcnVlYmFuIHF1ZSBsb3MgYXV0b23Ds3ZpbGVzIHF1ZSBzYWxlbiBkZSBsYSBjb211bmEgMTkgbGxlZ3VlbiBwcmluY2lwYWxtZW50ZSBhIGxhIGNvbXVuYSAyLg0KDQotICoqTGEgZXNjYWxhIGRlbCBhbsOhbGlzaXMgbGltaXRhIGxhIGludGVycHJldGFjacOzbiBwb3IgYmFycmlvOioqIHVuYSBjb211bmEgcmXDum5lIHNlY3RvcmVzIGNvbiBjYXJhY3RlcsOtc3RpY2FzIHkgYWN0aXZpZGFkZXMgZGlzdGludGFzLiBMb3MgcmVzdWx0YWRvcyBwZXJtaXRlbiBpZGVudGlmaWNhciB0ZXJyaXRvcmlvcyBwcmlvcml0YXJpb3MgcGFyYSBwcm9mdW5kaXphciBlbCBlc3R1ZGlvLCBwZXJvIG5vIGVzdGFibGVjZXIgY3XDoWxlcyBkZSBzdXMgYmFycmlvcyBleHBsaWNhbiBsYSBjb25jZW50cmFjacOzbi4gUGFyYSBlbGxvIHNlIG5lY2VzaXRhbiBkZXN0aW5vcyB5IG9yw61nZW5lcyBtw6FzIGRldGFsbGFkb3MsIG1vdGl2b3MgZGUgdmlhamUgeSBob3Jhcmlvcy4NCg0KDQojIyDCv0TDs25kZSB0aWVuZSBtw6FzIHBlc28gY2FkYSBtb2RvPw0KDQpFbCBtYXBhIGRlIGNvbnRlb3MgaWRlbnRpZmljYSBkw7NuZGUgaGF5IG3DoXMgcmVnaXN0cm9zIGRlIHVuIHZlaMOtY3Vsby4gTGEgY29tcG9zaWNpw7NuIGxvY2FsIGlkZW50aWZpY2EgZMOzbmRlIGVzZSB2ZWjDrWN1bG8gcmVwcmVzZW50YSB1bmEgbWF5b3IgcGFydGUgZGUgbG9zIHZpYWplcyByZWdpc3RyYWRvcy4gVW5hIGNvbXVuYSBwdWVkZSBkZXN0YWNhciBlbiB1bmEgbWVkaWRhIHkgbm8gZW4gbGEgb3RyYS4NCg0KYGBge3IgcGVyZmlsZXMtbW9kYWxlc30NCm1vZG9zX3ByaW5jaXBhbGVzIDwtIGMoIkJpY2ljbGV0YSIsICJNb3RvY2ljbGV0YSIsICJBdXRvbcOzdmlsIikNCm1vZGFsX28gPC0gdmlhamVzICU+JSBjb3VudChjb211bmEgPSBvcmlnZW4sIG1vZG8sIG5hbWUgPSAicmVnaXN0cm9zIikgJT4lDQogIGNvbXBsZXRlKGNvbXVuYSA9IDE6MjIsIG1vZG8gPSBtb2Rvc19wcmluY2lwYWxlcywgZmlsbCA9IGxpc3QocmVnaXN0cm9zID0gMCkpICU+JQ0KICBsZWZ0X2pvaW4odGVycml0b3JpbyAlPiUgc2VsZWN0KGNvbXVuYSwgdG90YWxfbG9jYWwgPSBzYWxpZGFzKSwgYnkgPSAiY29tdW5hIikgJT4lDQogIG11dGF0ZShFeHRyZW1vID0gIk9yaWdlbiIpDQptb2RhbF9kIDwtIHZpYWplcyAlPiUgY291bnQoY29tdW5hID0gZGVzdGlubywgbW9kbywgbmFtZSA9ICJyZWdpc3Ryb3MiKSAlPiUNCiAgY29tcGxldGUoY29tdW5hID0gMToyMiwgbW9kbyA9IG1vZG9zX3ByaW5jaXBhbGVzLCBmaWxsID0gbGlzdChyZWdpc3Ryb3MgPSAwKSkgJT4lDQogIGxlZnRfam9pbih0ZXJyaXRvcmlvICU+JSBzZWxlY3QoY29tdW5hLCB0b3RhbF9sb2NhbCA9IGxsZWdhZGFzKSwgYnkgPSAiY29tdW5hIikgJT4lDQogIG11dGF0ZShFeHRyZW1vID0gIkRlc3Rpbm8iKQ0KcGVyZmlsZXMgPC0gYmluZF9yb3dzKG1vZGFsX28sIG1vZGFsX2QpICU+JQ0KICBmaWx0ZXIobW9kbyAlaW4lIG1vZG9zX3ByaW5jaXBhbGVzKSAlPiUNCiAgbGVmdF9qb2luKGZyZWN1ZW5jaWFfbW9kbyAlPiUgdHJhbnNtdXRlKG1vZG8sIGN1b3RhX2dsb2JhbCA9IDEwMCAqIHJlZ2lzdHJvcyAvIE5faW5jbHVpZG8pLCBieSA9ICJtb2RvIikgJT4lDQogIG11dGF0ZShjdW90YV9nbG9iYWwgPSByZXBsYWNlX25hKGN1b3RhX2dsb2JhbCwgMCksDQogICAgICAgICBjdW90YV9sb2NhbCA9IGlmX2Vsc2UodG90YWxfbG9jYWwgPiAwLCAxMDAgKiByZWdpc3Ryb3MgLyB0b3RhbF9sb2NhbCwgTkFfcmVhbF8pLA0KICAgICAgICAgZGlmZXJlbmNpYV9wcCA9IGN1b3RhX2xvY2FsIC0gY3VvdGFfZ2xvYmFsKQ0KZ2dwbG90KHBlcmZpbGVzLCBhZXMobW9kbywgZmFjdG9yKGNvbXVuYSwgbGV2ZWxzID0gMjI6MSksIGZpbGwgPSBjdW90YV9sb2NhbCkpICsNCiAgZ2VvbV90aWxlKGNvbG91ciA9ICJ3aGl0ZSIpICsgZmFjZXRfd3JhcCh+RXh0cmVtbykgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICIjZWRmNWY2IiwgaGlnaCA9ICIjMTc0YjYwIiwgbGltaXRzID0gYygwLCAxMDApLA0KICAgICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gImdyZXk4NSIsIG5hbWUgPSAiJSBsb2NhbCIpICsNCiAgbGFicyh0aXRsZSA9ICJDb21wb3NpY2nDs24gbW9kYWwgZGVudHJvIGRlIGNhZGEgY29tdW5hIiwgeCA9IE5VTEwsIHkgPSAiQ29tdW5hIiwNCiAgICAgICBjYXB0aW9uID0gIkRlbm9taW5hZG9yOiB0b2RvcyBsb3MgcmVnaXN0cm9zIGRlIGxhIGNvbXVuYSBlbiBlc2UgZXh0cmVtby4gR3Jpczogc2luIHJlZ2lzdHJvcyB0b3RhbGVzLiIpDQojIFNlbGVjY2lvbm8gcG9yIHZvbHVtZW4gcGFyYSBxdWUgZWwgbWF5b3IgcG9yY2VudGFqZSBubyBvY3VsdGUgdW4gY29udGVvIHBlcXVlw7FvLg0KcGVyZmlsX2Rlc3RhY2FkbyA8LSBwZXJmaWxlcyAlPiUgZ3JvdXBfYnkoRXh0cmVtbywgbW9kbykgJT4lDQogIGFycmFuZ2UoZGVzYyhyZWdpc3Ryb3MpLCBjb211bmEsIC5ieV9ncm91cCA9IFRSVUUpICU+JSBzbGljZV9oZWFkKG4gPSAxKSAlPiUgdW5ncm91cCgpICU+JQ0KICBzZWxlY3QoRXh0cmVtbywgbW9kbywgY29tdW5hLCByZWdpc3Ryb3MsIHRvdGFsX2xvY2FsLCBjdW90YV9sb2NhbCwgY3VvdGFfZ2xvYmFsLCBkaWZlcmVuY2lhX3BwKQ0Ka25pdHI6OmthYmxlKHBlcmZpbF9kZXN0YWNhZG8sIGRpZ2l0cyA9IDEsDQogIGNhcHRpb24gPSAiQ29tdW5hIGNvbiBtw6FzIHJlZ2lzdHJvcyBwb3IgbW9kbzogcGVzbyBsb2NhbCB5IGRpZmVyZW5jaWEgZnJlbnRlIGFsIGNvbmp1bnRvIGluY2x1aWRvIChwcCkiKQ0KYGBgDQoNCi0gKipCaWNpY2xldGE6IGxpZGVyYXpnbyBlbiB2b2x1bWVuLCBzaW4gbWF5b3IgcGFydGljaXBhY2nDs24gbG9jYWwuKiogTGEgY29tdW5hIDIgZW5jYWJlemEgdGFudG8gbGFzIHNhbGlkYXMgY29tbyBsYXMgbGxlZ2FkYXMgZW4gYmljaWNsZXRhLCBwZXJvIGVzdGUgbW9kbyByZXByZXNlbnRhIGVsIDUsNCAlIGRlIHN1cyBvcsOtZ2VuZXMgeSBlbCA1LDEgJSBkZSBzdXMgZGVzdGlub3MsIHByb3BvcmNpb25lcyBjZXJjYW5hcyBhbCA1LDUgJSBkZWwgY29uanVudG8gYW5hbGl6YWRvLiBTdSBsaWRlcmF6Z28gcmVzcG9uZGUgcHJpbmNpcGFsbWVudGUgYWwgdm9sdW1lbiBkZSBkZXNwbGF6YW1pZW50b3MgZGUgbGEgY29tdW5hOyBubyBldmlkZW5jaWEgdW5hIHBhcnRpY2lwYWNpw7NuIGNpY2xpc3RhIGVzcGVjaWFsbWVudGUgZWxldmFkYS4NCg0KLSAqKk1vdG9jaWNsZXRhOiBwZXNvIHNpbWlsYXIgYWwgcGF0csOzbiBnZW5lcmFsLCBjb24gcHJlZG9taW5pbyBkZSBsbGVnYWRhcy4qKiBFbiBsYSBjb211bmEgMiwgbGFzIG1vdG9jaWNsZXRhcyByZXByZXNlbnRhbiBlbCA0NywyICUgZGUgbGFzIHNhbGlkYXMgeSBlbCA0NiwwICUgZGUgbGFzIGxsZWdhZGFzLCBmcmVudGUgYWwgNDYsNSAlIGdlbmVyYWwuIEF1bnF1ZSBzdSBwYXJ0aWNpcGFjacOzbiBsb2NhbCBjYW1iaWEgcG9jbywgcmVjaWJlIDEuNzM0IHZpYWplcyB5IGdlbmVyYSAxLjA2MiwgY29uIHVuIHNhbGRvIGRlICoqKzY3MiByZWdpc3Ryb3MqKi4gTGEgZGlmZXJlbmNpYSB0ZXJyaXRvcmlhbCBzZSBtYW5pZmllc3RhIHByaW5jaXBhbG1lbnRlIGVuIGVsIHZvbHVtZW4gcmVjaWJpZG8sIG3DoXMgcXVlIGVuIHVuYSBlc3BlY2lhbGl6YWNpw7NuIGVuIG1vdG9jaWNsZXRhLg0KDQotICoqQXV0b23Ds3ZpbDogbWF5b3IgcGFydGljaXBhY2nDs24gcmVsYXRpdmEgZW4gbGFzIGRvcyBjb211bmFzIGRlc3RhY2FkYXMuKiogRW4gbGEgY29tdW5hIDE5LCBlbCBhdXRvbcOzdmlsIHJlcHJlc2VudGEgZWwgNDEsMiAlIGRlIGxhcyBzYWxpZGFzLCAqKjIsMyBwdW50b3MgcG9yY2VudHVhbGVzIHBvciBlbmNpbWEqKiBkZWwgY29uanVudG8gYW5hbGl6YWRvLiBFbiBsYSBjb211bmEgMiByZXByZXNlbnRhIGVsIDQwLDIgJSBkZSBsYXMgbGxlZ2FkYXMsICoqMSw0IHB1bnRvcyBwb3IgZW5jaW1hKiouIEVzdG9zIHJlc3VsdGFkb3MgbXVlc3RyYW4gdW5hIHBhcnRpY2lwYWNpw7NuIHJlbGF0aXZhbWVudGUgbWF5b3IgZGVsIGF1dG9tw7N2aWwgZW4gYW1ib3MgZXh0cmVtb3MsIGF1bnF1ZSBsYXMgZGlmZXJlbmNpYXMgc29uIG1vZGVyYWRhcy4gTm8gcGVybWl0ZW4gYWZpcm1hciBxdWUgbG9zIHZpYWplcyBxdWUgc2FsZW4gZGUgbGEgY29tdW5hIDE5IHNlIGRpcmlqYW4gYSBsYSBjb211bmEgMi4NCg0KLSAqKkxlY3R1cmEgY29uanVudGE6IGNvbmNlbnRyYWNpw7NuIHRlcnJpdG9yaWFsIGNvbiBjb21wb3NpY2lvbmVzIG1vZGFsZXMgY2VyY2FuYXMgYWwgcHJvbWVkaW8uKiogTGFzIGRpZmVyZW5jaWFzIG9ic2VydmFkYXMgb3NjaWxhbiBlbnRyZSDiiJIwLDYgeSArMiwzIHB1bnRvcyBwb3JjZW50dWFsZXMuIFBvciB0YW50bywgbG9zIG1heW9yZXMgY29udGVvcyBkZSBlc3RhcyBjb211bmFzIG5vIHNlIGFjb21wYcOxYW4gZGUgZGVzdmlhY2lvbmVzIG1hcmNhZGFzIGVuIGxhIGNvbXBvc2ljacOzbiBtb2RhbCBkZSBsYXMgZmlsYXMgcHJlc2VudGFkYXMuIExhIGNvbXVuYSAyIGRlc3RhY2EgcHJpbmNpcGFsbWVudGUgcG9yIGNvbmNlbnRyYXIgbGxlZ2FkYXMgZGUgZGlzdGludG9zIHZlaMOtY3Vsb3MsIG1pZW50cmFzIGxhIGNvbXVuYSAxOSBjb21iaW5hIGVsIG1heW9yIGNvbnRlbyBkZSBzYWxpZGFzIGVuIGF1dG9tw7N2aWwgY29uIHVuYSBwYXJ0aWNpcGFjacOzbiBkZSBlc3RlIG1vZG8gYWxnbyBzdXBlcmlvciBhIGxhIGdlbmVyYWwuDQoNCipOb3RhOiBsYXMgZGlmZXJlbmNpYXMgc2UgY2FsY3VsYW4gYW50ZXMgZGUgcmVkb25kZWFyOyBwb3IgZWxsbywgcmVzdGFyIGxvcyBwb3JjZW50YWplcyBtb3N0cmFkb3MgcHVlZGUgcHJvZHVjaXIgdW5hIGRpc2NyZXBhbmNpYSBkZSB1bmEgZMOpY2ltYS4gTGEgdGFibGEgc2VsZWNjaW9uYSBsYXMgY29tdW5hcyBsw61kZXJlcyBwb3IgY29udGVvIGVuIGNhZGEgbW9kbywgbm8gbmVjZXNhcmlhbWVudGUgYXF1ZWxsYXMgY29uIGxhIG1heW9yIHBhcnRpY2lwYWNpw7NuIHBvcmNlbnR1YWwgbG9jYWwuKg0KDQojIENvbmV4aW9uZXMgeSBiYWxhbmNlIHRlcnJpdG9yaWFsDQoNCiMjIE1hdHJpeiBvcmlnZW7igJNkZXN0aW5vDQoNCmBgYHtyIG1hdHJpei1vZCwgZmlnLmNhcD0nQ2FkYSBjZWxkYSBjb3JyZXNwb25kZSBhIHVuIG9yaWdlbiB5IHVuIGRlc3Rpbm87IGxhIGRpYWdvbmFsIGNvbnNlcnZhIGxvcyB2aWFqZXMgaW50cmFjb211bmEuJ30NCiMgY291bnQob3JpZ2VuLCBkZXN0aW5vKSBjdWVudGEgY2FkYSBwYXIgZGlyaWdpZG8uDQojIGNvbXBsZXRlKCkgaW5jbHV5ZSBsYXMgNDg0IGNvbWJpbmFjaW9uZXMgeSBhc2lnbmEgY2VybyBhIGxhcyBubyBvYnNlcnZhZGFzLg0Kb2QgPC0gdmlhamVzICU+JQ0KICBjb3VudChvcmlnZW4sIGRlc3Rpbm8sIG5hbWUgPSAicmVnaXN0cm9zIikgJT4lDQogIGNvbXBsZXRlKG9yaWdlbiA9IDE6MjIsIGRlc3Rpbm8gPSAxOjIyLCBmaWxsID0gbGlzdChyZWdpc3Ryb3MgPSAwKSkNCnN0b3BpZm5vdChzdW0ob2QkcmVnaXN0cm9zKSA9PSBucm93KHZpYWplcykpDQoNCmdncGxvdChvZCwgYWVzKHggPSBmYWN0b3IoZGVzdGlubyksIHkgPSBmYWN0b3Iob3JpZ2VuLCBsZXZlbHMgPSAyMjoxKSwgZmlsbCA9IHJlZ2lzdHJvcykpICsNCiAgZ2VvbV90aWxlKGNvbG9yID0gIndoaXRlIikgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYyh0cmFucyA9ICJzcXJ0IiwgbmFtZSA9ICJSZWdpc3Ryb3MiKSArDQogIGNvb3JkX2VxdWFsKCkgKw0KICBsYWJzKHRpdGxlID0gIkNvbmV4aW9uZXMgZW50cmUgY29tdW5hcyBlbiBsYSBtdWVzdHJhIiwNCiAgICAgICB4ID0gIkNvbXVuYSBkZSBkZXN0aW5vIiwgeSA9ICJDb211bmEgZGUgb3JpZ2VuIikNCg0KcGFyZXNfcHJpbmNpcGFsZXMgPC0gb2QgJT4lDQogIGZpbHRlcihvcmlnZW4gIT0gZGVzdGlubywgcmVnaXN0cm9zID4gMCkgJT4lDQogIGFycmFuZ2UoZGVzYyhyZWdpc3Ryb3MpLCBvcmlnZW4sIGRlc3Rpbm8pICU+JSBzbGljZV9oZWFkKG4gPSAxMCkNCnBhcmVzX3ByaW5jaXBhbGVzIDwtIHBhcmVzX3ByaW5jaXBhbGVzICU+JQ0KICBtdXRhdGUocGN0X3RvdGFsID0gMTAwICogcmVnaXN0cm9zIC8gTl9pbmNsdWlkbywNCiAgICAgICAgIHBjdF9pbnRlcmNvbXVuYSA9IDEwMCAqIHJlZ2lzdHJvcyAvIG1heCgxLCBzdW0odmlhamVzJG9yaWdlbiAhPSB2aWFqZXMkZGVzdGlubykpKQ0Ka25pdHI6OmthYmxlKHBhcmVzX3ByaW5jaXBhbGVzLCBkaWdpdHMgPSAyLA0KICAgICAgICAgICAgIGNhcHRpb24gPSAiRGlleiBjb25leGlvbmVzIGludGVyY29tdW5hOiBwZXNvIHNvYnJlIHRvZG9zIGxvcyB2aWFqZXMgeSBzb2JyZSBsb3MgaW50ZXJjb211bmEiKQ0KDQojIGZhY3RvcigpIHRyYXRhIGxvcyBjw7NkaWdvcyBjb21vIGNhdGVnb3LDrWFzOyBsZXZlbHMgY29udHJvbGEgc3Ugb3JkZW4uIGdlb21fdGlsZSgpIGRpYnVqYQ0KIyBsYXMgY2VsZGFzIHkgY29vcmRfZXF1YWwoKSBsYXMgaGFjZSBjdWFkcmFkYXMuIExhIHRyYW5zZm9ybWFjacOzbiByYcOteiBjdWFkcmFkYSBtZWpvcmEgbGENCiMgdmlzaWJpbGlkYWQgZGUgZnJlY3VlbmNpYXMgcGVxdWXDsWFzOyBsYSBsZXllbmRhIGNvbnNlcnZhIGxvcyBjb250ZW9zIG9yaWdpbmFsZXMuDQojIGFycmFuZ2UoZGVzYygpKSBvcmRlbmEgZGUgbWF5b3IgYSBtZW5vciB5IHNsaWNlX2hlYWQoKSB0b21hIGxhcyBwcmltZXJhcyBmaWxhcy4gRWwgcGFyIDIg4oaSDQojIDE5IGVzIGRpc3RpbnRvIGRlIDE5IOKGkiAyOyBuaW5ndW5vIGluZGljYSBwb3IgcXXDqSBjYWxsZXMgc2UgdmlhasOzLg0KYGBgDQoNCkxhIG1hdHJpeiB5IGxhIHRhYmxhIG11ZXN0cmFuIHVuYSBjb25jZW50cmFjacOzbiBkZSBsbGVnYWRhcyBoYWNpYSBsYSBjb211bmEgMiBkZXNkZSBkaXN0aW50b3Mgb3LDrWdlbmVzLCBlc3BlY2lhbG1lbnRlIGRlc2RlIGxhcyBjb211bmFzIDE5LCAxNyB5IDE4LiBMYSBjb25leGnDs24gMTkg4oaSIDIgcHJlc2VudGEgZWwgbWF5b3IgY29udGVvLCBjb24gNDkyIHZpYWplcyAoMiwzOCAlIGRlIGxvcyBpbnRlcmNvbXVuYSksIGZyZW50ZSBhIDMwNiBlbiBzZW50aWRvIGNvbnRyYXJpby4gU2luIGVtYmFyZ28sIGxhcyBkaWV6IGNvbmV4aW9uZXMgcHJpbmNpcGFsZXMgcmXDum5lbiBhcGVuYXMgZWwgMTMsOCAlIGRlIGxvcyB2aWFqZXMgaW50ZXJjb211bmE6IGV4aXN0ZW4gZGVzdGlub3MgZGVzdGFjYWRvcywgcGVybyBsb3MgZGVzcGxhemFtaWVudG9zIHNlIGRpc3RyaWJ1eWVuIGVudHJlIG3Dumx0aXBsZXMgY29uZXhpb25lcy4NCg0KIyMgVmlhamVzIGRlbnRybyB5IGVudHJlIGNvbXVuYXMNCg0KYGBge3IgdmlhamVzLWludHJhfQ0KcmVzdW1lbl9pbnRyYSA8LSB2aWFqZXMgJT4lIGdyb3VwX2J5KG1vZG8pICU+JQ0KICBzdW1tYXJpc2UodG90YWwgPSBuKCksIGludHJhY29tdW5hID0gc3VtKG9yaWdlbiA9PSBkZXN0aW5vKSwNCiAgICAgICAgICAgIGludGVyY29tdW5hID0gc3VtKG9yaWdlbiAhPSBkZXN0aW5vKSwNCiAgICAgICAgICAgIHBjdF9pbnRyYSA9IDEwMCAqIGludHJhY29tdW5hIC8gdG90YWwsIC5ncm91cHMgPSAiZHJvcCIpDQprbml0cjo6a2FibGUocmVzdW1lbl9pbnRyYSwgZGlnaXRzID0gMSwgY2FwdGlvbiA9ICJWaWFqZXMgaW50cmFjb211bmEgcG9yIG1vZG8gKCUgZGUgY2FkYSBtb2RvKSIpDQppbnRyYV9ncmFmaWNvIDwtIHJlc3VtZW5faW50cmEgJT4lDQogIHBpdm90X2xvbmdlcihjKGludHJhY29tdW5hLCBpbnRlcmNvbXVuYSksIG5hbWVzX3RvID0gIlRpcG8iLCB2YWx1ZXNfdG8gPSAicmVnaXN0cm9zIikgJT4lDQogIG11dGF0ZShwb3JjZW50YWplID0gMTAwICogcmVnaXN0cm9zIC8gdG90YWwpDQpnZ3Bsb3QoaW50cmFfZ3JhZmljbywgYWVzKG1vZG8sIHBvcmNlbnRhamUsIGZpbGwgPSBUaXBvKSkgKw0KICBnZW9tX2NvbCh3aWR0aCA9IDAuNjUpICsgY29vcmRfZmxpcCgpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiaW50cmFjb211bmEiID0gIiMxNjg0N2IiLCAiaW50ZXJjb211bmEiID0gIiM5NWE5YjUiKSkgKw0KICBsYWJzKHRpdGxlID0gIsK/UXXDqSBwYXJ0ZSBkZSBjYWRhIG1vZG8gcGVybWFuZWNlIGVuIGxhIG1pc21hIGNvbXVuYT8iLA0KICAgICAgIHggPSBOVUxMLCB5ID0gIiUgZGUgcmVnaXN0cm9zIGRlIGNhZGEgbW9kbyIsIGZpbGwgPSBOVUxMKQ0KYGBgDQoNCmBgYHtyIGxlY3R1cmEtaW50cmEtY29uZXhpb25lcywgZWNobz1GQUxTRSwgcmVzdWx0cz0nYXNpcyd9DQppbnRyYV90b3RhbCA8LSBzdW0odmlhamVzJG9yaWdlbiA9PSB2aWFqZXMkZGVzdGlubykNCg0KYGBgDQoNCmBgYHtyIGluZGljYWRvcmVzLWNvbmV4aW9uZXMsIGluY2x1ZGU9RkFMU0V9DQp0b3RhbF92aWFqZXMgPC0gbnJvdyh2aWFqZXMpDQpuX2ludHJhIDwtIHN1bSh2aWFqZXMkb3JpZ2VuID09IHZpYWplcyRkZXN0aW5vKQ0Kbl9pbnRlciA8LSB0b3RhbF92aWFqZXMgLSBuX2ludHJhDQoNCiMgTG9zIHBvcmNlbnRhamVzIHF1ZWRhbiBzaW4gZGVmaW5pciBjdWFuZG8gZWwgZGVub21pbmFkb3IgZXMgY2Vyby4NCnBvcmNlbnRhamUgPC0gZnVuY3Rpb24obiwgdG90YWwpIHsNCiAgaWYgKHRvdGFsID4gMCkgMTAwICogbiAvIHRvdGFsIGVsc2UgTkFfcmVhbF8NCn0NCg0KZm9ybWF0b19wY3QgPC0gZnVuY3Rpb24oeCkgew0KICBpZiAoaXMubmEoeCkpIHJldHVybigiTm8gZGVmaW5pZG8iKQ0KICBwYXN0ZTAoZm9ybWF0Qyh4LCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDEsIGRlY2ltYWwubWFyayA9ICIsIiksICIgJSIpDQp9DQoNCmZvcm1hdG9fbiA8LSBmdW5jdGlvbih4KSB7DQogIGZvcm1hdCh4LCBiaWcubWFyayA9ICIuIiwgc2NpZW50aWZpYyA9IEZBTFNFLCB0cmltID0gVFJVRSkNCn0NCg0KIyBDb250ZW9zIGRlIGxvcyBkb3Mgc2VudGlkb3MsIGluY2x1eWVuZG8gdG9kb3MgbG9zIG1vZG9zLg0Kbl8xOV8yIDwtIHN1bSh2aWFqZXMkb3JpZ2VuID09IDE5ICYgdmlhamVzJGRlc3Rpbm8gPT0gMikNCm5fMl8xOSA8LSBzdW0odmlhamVzJG9yaWdlbiA9PSAyICYgdmlhamVzJGRlc3Rpbm8gPT0gMTkpDQpkaWZlcmVuY2lhX3NlbnRpZG9zIDwtIG5fMTlfMiAtIG5fMl8xOQ0KDQojIFNlbGVjY2nDs24gcmVwcm9kdWNpYmxlIGRlIGxhcyBkaWV6IGNvbmV4aW9uZXMgaW50ZXJjb211bmEgcHJpbmNpcGFsZXMuDQp0b3AxMF9jb25leGlvbmVzIDwtIG9kICU+JQ0KICBmaWx0ZXIob3JpZ2VuICE9IGRlc3Rpbm8sIHJlZ2lzdHJvcyA+IDApICU+JQ0KICBhcnJhbmdlKGRlc2MocmVnaXN0cm9zKSwgb3JpZ2VuLCBkZXN0aW5vKSAlPiUNCiAgc2xpY2VfaGVhZChuID0gMTApDQoNCm5fdG9wMTAgPC0gc3VtKHRvcDEwX2NvbmV4aW9uZXMkcmVnaXN0cm9zKQ0Kbl9yZXN0byA8LSBuX2ludGVyIC0gbl90b3AxMA0KDQpwY3RfaW50cmEgPC0gcG9yY2VudGFqZShuX2ludHJhLCB0b3RhbF92aWFqZXMpDQpwY3RfaW50ZXIgPC0gcG9yY2VudGFqZShuX2ludGVyLCB0b3RhbF92aWFqZXMpDQpwY3RfMTlfMiA8LSBwb3JjZW50YWplKG5fMTlfMiwgbl9pbnRlcikNCnBjdF8yXzE5IDwtIHBvcmNlbnRhamUobl8yXzE5LCBuX2ludGVyKQ0KdmFyaWFjaW9uX3NlbnRpZG9zIDwtIHBvcmNlbnRhamUoZGlmZXJlbmNpYV9zZW50aWRvcywgbl8yXzE5KQ0KcGN0X3RvcDEwIDwtIHBvcmNlbnRhamUobl90b3AxMCwgbl9pbnRlcikNCnBjdF9yZXN0byA8LSBwb3JjZW50YWplKG5fcmVzdG8sIG5faW50ZXIpDQpgYGANCg0KYGBge3IgdGFibGEtcG9yY2VudGFqZXMtY29uZXhpb25lcywgZWNobz1GQUxTRX0NCnRhYmxhX2NvbmV4aW9uZXMgPC0gZGF0YS5mcmFtZSgNCiAgSW5kaWNhZG9yID0gYygNCiAgICAiVmlhamVzIGRlbnRybyBkZSBsYSBtaXNtYSBjb211bmEiLA0KICAgICJWaWFqZXMgZW50cmUgY29tdW5hcyBkaWZlcmVudGVzIiwNCiAgICAiQ29uZXhpw7NuIDE5IOKGkiAyIiwNCiAgICAiQ29uZXhpw7NuIDIg4oaSIDE5IiwNCiAgICAiRGlleiBjb25leGlvbmVzIGludGVyY29tdW5hIHByaW5jaXBhbGVzIiwNCiAgICAiRGVtw6FzIGNvbmV4aW9uZXMgaW50ZXJjb211bmEiDQogICksDQogIFJlZ2lzdHJvcyA9IGMoDQogICAgbl9pbnRyYSwgbl9pbnRlciwgbl8xOV8yLCBuXzJfMTksIG5fdG9wMTAsIG5fcmVzdG8NCiAgKSwNCiAgRGVub21pbmFkb3IgPSBjKA0KICAgIHRvdGFsX3ZpYWplcywgdG90YWxfdmlhamVzLA0KICAgIG5faW50ZXIsIG5faW50ZXIsIG5faW50ZXIsIG5faW50ZXINCiAgKSwNCiAgUG9yY2VudGFqZSA9IGMoDQogICAgcGN0X2ludHJhLCBwY3RfaW50ZXIsIHBjdF8xOV8yLCBwY3RfMl8xOSwgcGN0X3RvcDEwLCBwY3RfcmVzdG8NCiAgKQ0KKQ0KDQprbml0cjo6a2FibGUoDQogIHRhYmxhX2NvbmV4aW9uZXMsDQogIGRpZ2l0cyA9IGMoMCwgMCwgMCwgMSksDQogIGNvbC5uYW1lcyA9IGMoIkluZGljYWRvciIsICJSZWdpc3Ryb3MgKG4pIiwgIkJhc2UgZGUgY8OhbGN1bG8gKE4pIiwgIiUiKSwNCiAgY2FwdGlvbiA9ICJEaXN0cmlidWNpw7NuIGRlIGxvcyB2aWFqZXMgeSBwYXJ0aWNpcGFjacOzbiBkZSBsYXMgY29uZXhpb25lcyIsDQogIGZvcm1hdC5hcmdzID0gbGlzdChiaWcubWFyayA9ICIuIiwgZGVjaW1hbC5tYXJrID0gIiwiKQ0KKQ0KYGBgDQoNCi0gKipEaXN0cmlidWNpw7NuIHRlcnJpdG9yaWFsIGRlIGxvcyB2aWFqZXM6KiogZGUgbG9zICoqYHIgZm9ybWF0b19uKHRvdGFsX3ZpYWplcylgIHJlZ2lzdHJvcyBpbmNsdWlkb3MqKiwgKipgciBmb3JtYXRvX24obl9pbnRlcilgIChgciBmb3JtYXRvX3BjdChwY3RfaW50ZXIpYCkqKiBjb25lY3RhbiBjb211bmFzIGRpZmVyZW50ZXMgeSAqKmByIGZvcm1hdG9fbihuX2ludHJhKWAgKGByIGZvcm1hdG9fcGN0KHBjdF9pbnRyYSlgKSoqIHBlcm1hbmVjZW4gZW4gbGEgbWlzbWEgY29tdW5hLiBFc3RhIGNvbXBhcmFjacOzbiBwZXJtaXRlIGRpbWVuc2lvbmFyIGVsIHBhcGVsIGRlIGxvcyBkZXNwbGF6YW1pZW50b3MgZW50cmUgdGVycml0b3Jpb3MuIENydXphciB1bmEgZnJvbnRlcmEgYWRtaW5pc3RyYXRpdmEgbm8gaW1wbGljYSBuZWNlc2FyaWFtZW50ZSByZWNvcnJlciB1bmEgZGlzdGFuY2lhIG1heW9yLg0KDQotICoqUGVzbyBkZSBsYXMgY29uZXhpb25lcyBwcmluY2lwYWxlczoqKiBsYXMgZGlleiBjb25leGlvbmVzIGludGVyY29tdW5hIHNlbGVjY2lvbmFkYXMgcmXDum5lbiAqKmByIGZvcm1hdG9fbihuX3RvcDEwKWAgdmlhamVzIChgciBmb3JtYXRvX3BjdChwY3RfdG9wMTApYCkqKi4gTGFzIGRlbcOhcyBhY3VtdWxhbiAqKmByIGZvcm1hdG9fbihuX3Jlc3RvKWAgKGByIGZvcm1hdG9fcGN0KHBjdF9yZXN0bylgKSoqLiBFc3RhIGRpc3RyaWJ1Y2nDs24gcGVybWl0ZSBldmFsdWFyIGN1w6FudG8gZGVsIG1vdmltaWVudG8gcmVnaXN0cmFkbyBxdWVkYSBleHBsaWNhZG8gcG9yIHVuIGdydXBvIHJlZHVjaWRvIGRlIHBhcmVzIG9yaWdlbuKAk2Rlc3Rpbm8uIExvcyBlbXBhdGVzIHNlIHJlc3VlbHZlbiBwb3IgY8OzZGlnbyBkZSBvcmlnZW4geSBkZXN0aW5vLg0KDQpgYGB7ciB0YWJsYS1kaXJlY2Npb24tY29uZXhpb24sIGVjaG89RkFMU0V9DQp0YWJsYV9kaXJlY2Npb24gPC0gZGF0YS5mcmFtZSgNCiAgSW5kaWNhZG9yID0gYygNCiAgICAiVmlhamVzIDE5IOKGkiAyIiwNCiAgICAiVmlhamVzIDIg4oaSIDE5IiwNCiAgICAiRGlmZXJlbmNpYTogMTkg4oaSIDIgbWVub3MgMiDihpIgMTkiLA0KICAgICJWYXJpYWNpw7NuIHJlbGF0aXZhIHJlc3BlY3RvIGFsIHNlbnRpZG8gMiDihpIgMTkiDQogICksDQogIFJlc3VsdGFkbyA9IGMoDQogICAgZm9ybWF0b19uKG5fMTlfMiksDQogICAgZm9ybWF0b19uKG5fMl8xOSksDQogICAgZm9ybWF0b19uKGRpZmVyZW5jaWFfc2VudGlkb3MpLA0KICAgIGZvcm1hdG9fcGN0KHZhcmlhY2lvbl9zZW50aWRvcykNCiAgKQ0KKQ0KDQprbml0cjo6a2FibGUoDQogIHRhYmxhX2RpcmVjY2lvbiwNCiAgY2FwdGlvbiA9ICJWb2x1bWVuIHkgZGlmZXJlbmNpYSBlbnRyZSBhbWJvcyBzZW50aWRvcyINCikNCmBgYA0KDQpgYGB7ciBsZWN0dXJhLWRpcmVjY2lvbi1jb25leGlvbiwgZWNobz1GQUxTRSwgcmVzdWx0cz0nYXNpcyd9DQppZiAobl8yXzE5ID4gMCAmJiBkaWZlcmVuY2lhX3NlbnRpZG9zID4gMCkgew0KICBjYXQoDQogICAgIi0gKipBc2ltZXRyw61hIGRpcmVjY2lvbmFsOioqIHNlIHJlZ2lzdHJhbiAqKiIsIGZvcm1hdG9fbihuXzE5XzIpLA0KICAgICIgdmlhamVzIGRlIGxhIGNvbXVuYSAxOSBhIGxhIDIqKiwgZnJlbnRlIGEgKioiLCBmb3JtYXRvX24obl8yXzE5KSwNCiAgICAiKiogZW4gc2VudGlkbyBjb250cmFyaW8uIEVsIHByaW1lciBzZW50aWRvIHN1cGVyYSBhbCBzZWd1bmRvIGVuICoqIiwNCiAgICBmb3JtYXRvX24oZGlmZXJlbmNpYV9zZW50aWRvcyksICIgcmVnaXN0cm9zICgiLA0KICAgIGZvcm1hdG9fcGN0KHZhcmlhY2lvbl9zZW50aWRvcyksDQogICAgIikqKi4gRXN0ZSByZXN1bHRhZG8gZXMgY29uc2lzdGVudGUgY29uIGVsIHBhcGVsIHJlY2VwdG9yIGRlIGxhIGNvbXVuYSAyICIsDQogICAgIm9ic2VydmFkbyBlbiBlbCBiYWxhbmNlIGdlbmVyYWwuXG5cbiIsDQogICAgc2VwID0gIiINCiAgKQ0KfSBlbHNlIGlmIChuXzJfMTkgPiAwICYmIGRpZmVyZW5jaWFfc2VudGlkb3MgPCAwKSB7DQogIGNhdCgNCiAgICAiLSAqKkFzaW1ldHLDrWEgZGlyZWNjaW9uYWw6KiogZWwgc2VudGlkbyAxOSDihpIgMiByZWdpc3RyYSAqKiIsDQogICAgZm9ybWF0b19uKG5fMTlfMiksICIgdmlhamVzKiosIGZyZW50ZSBhICoqIiwgZm9ybWF0b19uKG5fMl8xOSksDQogICAgIioqIGVuIHNlbnRpZG8gY29udHJhcmlvLiBTdSBjb250ZW8gZXMgKioiLA0KICAgIGZvcm1hdG9fcGN0KGFicyh2YXJpYWNpb25fc2VudGlkb3MpKSwNCiAgICAiIG1lbm9yKiogcmVzcGVjdG8gYWwgc2VudGlkbyAyIOKGkiAxOS5cblxuIiwNCiAgICBzZXAgPSAiIg0KICApDQp9IGVsc2UgaWYgKGRpZmVyZW5jaWFfc2VudGlkb3MgPT0gMCkgew0KICBjYXQoIi0gKipFcXVpbGlicmlvIGRpcmVjY2lvbmFsOioqIGFtYm9zIHNlbnRpZG9zIHRpZW5lbiBlbCBtaXNtbyBjb250ZW8uXG5cbiIpDQp9IGVsc2Ugew0KICBjYXQoDQogICAgIi0gKipDb21wYXJhY2nDs24gZGlyZWNjaW9uYWw6KiogaGF5IHJlZ2lzdHJvcyBlbiBlbCBzZW50aWRvIDE5IOKGkiAyLCAiLA0KICAgICJwZXJvIG5pbmd1bm8gZW4gZWwgc2VudGlkbyBjb250cmFyaW8uIExhIHZhcmlhY2nDs24gcG9yY2VudHVhbCAiLA0KICAgICJubyBlc3TDoSBkZWZpbmlkYSBwb3JxdWUgc3UgZGVub21pbmFkb3IgZXMgY2Vyby5cblxuIg0KICApDQp9DQpgYGANCg0KLSAqKlByZWRvbWluYW4gbG9zIGRlc3BsYXphbWllbnRvcyBlbnRyZSBjb211bmFzOioqIDIwLjY1OCB2aWFqZXMgKDkzLDMgJSkgY29uZWN0YW4gY29tdW5hcyBkaWZlcmVudGVzLCBtaWVudHJhcyBxdWUgMS40ODEgKDYsNyAlKSBwZXJtYW5lY2VuIGRlbnRybyBkZSBsYSBtaXNtYS4gRXN0byBtdWVzdHJhIHF1ZSBsYSBtb3ZpbGlkYWQgcmVnaXN0cmFkYSByZWJhc2EgYW1wbGlhbWVudGUgbG9zIGzDrW1pdGVzIGRlIGNhZGEgY29tdW5hIHkgcmVzcGFsZGEgdW5hIGxlY3R1cmEgZGUgY29uZWN0aXZpZGFkIGVudHJlIHRlcnJpdG9yaW9zLiBTaW4gZW1iYXJnbywgY3J1emFyIHVuIGzDrW1pdGUgYWRtaW5pc3RyYXRpdm8gbm8gaW1wbGljYSBuZWNlc2FyaWFtZW50ZSByZWNvcnJlciB1bmEgZGlzdGFuY2lhIG1heW9yLg0KDQotICoqTGEgY29uZXhpw7NuIDE5IOKGkiAyIHByZXNlbnRhIHVuYSBhc2ltZXRyw61hIGRpcmVjY2lvbmFsOioqIHJlZ2lzdHJhIDQ5MiB2aWFqZXMsIGZyZW50ZSBhIDMwNiBlbiBzZW50aWRvIGNvbnRyYXJpby4gRWwgZmx1am8gaGFjaWEgbGEgY29tdW5hIDIgZXMgKio2MCw4ICUgbWF5b3IqKiwgbG8gcXVlIHJlc3VsdGEgY29uc2lzdGVudGUgY29uIHN1IHBhcGVsIHJlY2VwdG9yIGVuIGVsIGFuw6FsaXNpcyBnZW5lcmFsLiANCg0KLSAqKkxhIGNvbmV4acOzbiBwcmluY2lwYWwgbm8gZG9taW5hIGVsIGNvbmp1bnRvOioqIGF1bnF1ZSAxOSDihpIgMiBvY3VwYSBlbCBwcmltZXIgbHVnYXIsIHJlcHJlc2VudGEgYXByb3hpbWFkYW1lbnRlIGVsICoqMiw0ICUgZGUgbG9zIHZpYWplcyBpbnRlcmNvbXVuYSoqLiBTdSBsaWRlcmF6Z28gZGViZSBlbnRlbmRlcnNlIGNvbW8gZWwgbWF5b3IgY29udGVvIGRlIHVuYSBjb25leGnDs24gcGFydGljdWxhciwgbm8gY29tbyB1biBlamUgcXVlIGNvbmNlbnRyZSBsYSBtYXlvcsOtYSBkZSBsb3MgZGVzcGxhemFtaWVudG9zLg0KDQotICoqTG9zIHZpYWplcyBzZSBkaXN0cmlidXllbiBlbnRyZSBudW1lcm9zYXMgY29uZXhpb25lczoqKiBsYXMgZGlleiBwcmluY2lwYWxlcyByZcO6bmVuIGVsIDEzLDggJSBkZSBsb3MgcmVnaXN0cm9zIGludGVyY29tdW5hOyBlbCAqKjg2LDIgJSByZXN0YW50ZSoqIGNvcnJlc3BvbmRlIGEgb3Ryb3MgcGFyZXMuIEVzdG8gaW5kaWNhIHF1ZSBsYSBjb25jZW50cmFjacOzbiBkZSBsbGVnYWRhcyBlbiBjaWVydGFzIGNvbXVuYXMgcHVlZGUgY29leGlzdGlyIGNvbiB1bmEgZGlzdHJpYnVjacOzbiBhbXBsaWEgZGUgY29uZXhpb25lczogdW4gZGVzdGlubyBpbXBvcnRhbnRlIHB1ZWRlIHJlY2liaXIgdmlhamVzIGRlc2RlIG3Dumx0aXBsZXMgb3LDrWdlbmVzLg0KDQotICoqTGEgcGxhbmlmaWNhY2nDs24gcmVxdWllcmUgY29tYmluYXIgZGVzdGlub3MgeSBjb25leGlvbmVzOioqIGxvcyByZXN1bHRhZG9zIGp1c3RpZmljYW4gcHJvZnVuZGl6YXIgZW4gZWwgaW50ZXJjYW1iaW8gZW50cmUgbGFzIGNvbXVuYXMgMTkgeSAyLCBqdW50byBjb24gbGFzIGRlbcOhcyBjb25leGlvbmVzIHF1ZSBhbGltZW50YW4gbG9zIHByaW5jaXBhbGVzIGRlc3Rpbm9zLiBQYXJhIHRyYWR1Y2lyIGVzdG9zIGVubGFjZXMgZW4gY29ycmVkb3JlcyBkZSBpbnRlcnZlbmNpw7NuIHNlIG5lY2VzaXRhbiByZWNvcnJpZG9zLCBtb2RvcyBkZSB0cmFuc3BvcnRlIHkgaG9yYXJpb3MsIHBvcnF1ZSBsYSBtYXRyaXogaWRlbnRpZmljYSByZWxhY2lvbmVzIGVudHJlIGNvbXVuYXMsIHBlcm8gbm8gbGFzIHbDrWFzIHV0aWxpemFkYXMuDQoNCiMjIEdlbmVyYWNpw7NuLCBhdHJhY2Npw7NuIHkgYmFsYW5jZQ0KDQpgYGB7ciBiYWxhbmNlfQ0KIyBQYXJ0byBkZSBsYXMgMjIgY29tdW5hcyBwYXJhIGluY2x1aXIgdGFtYmnDqW4gYXF1ZWxsYXMgc2luIHJlZ2lzdHJvcy4NCmJhbGFuY2UgPC0gZGF0YS5mcmFtZShjb211bmEgPSAxOjIyKSAlPiUNCiAgbGVmdF9qb2luKG9yaWdlbl9yZXN1bWVuICU+JSBzZWxlY3QoY29tdW5hLCBvcmlnZW5lcyA9IHRvdGFsKSwgYnkgPSAiY29tdW5hIikgJT4lDQogIGxlZnRfam9pbihkZXN0aW5vX3Jlc3VtZW4gJT4lIHNlbGVjdChjb211bmEsIGRlc3Rpbm9zID0gdG90YWwpLCBieSA9ICJjb211bmEiKSAlPiUNCiAgbXV0YXRlKG9yaWdlbmVzID0gcmVwbGFjZV9uYShvcmlnZW5lcywgMCksIGRlc3Rpbm9zID0gcmVwbGFjZV9uYShkZXN0aW5vcywgMCksDQogICAgICAgICBiYWxhbmNlID0gZGVzdGlub3MgLSBvcmlnZW5lcywNCiAgICAgICAgIGluZGljZSA9IGlmX2Vsc2UoZGVzdGlub3MgKyBvcmlnZW5lcyA+IDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGJhbGFuY2UgLyAoZGVzdGlub3MgKyBvcmlnZW5lcyksIE5BX3JlYWxfKSkNCnN0b3BpZm5vdChzdW0oYmFsYW5jZSRiYWxhbmNlKSA9PSAwKQ0Ka25pdHI6OmthYmxlKGJhbGFuY2UsIGRpZ2l0cyA9IDMsIGNhcHRpb24gPSAiQ29udGVvcyBlIGluZGljYWRvcmVzIHBvciBjb211bmEiKQ0KDQpnZ3Bsb3QoYmFsYW5jZSwgYWVzKHggPSByZW9yZGVyKGZhY3Rvcihjb211bmEpLCBiYWxhbmNlKSwgeSA9IGJhbGFuY2UpKSArDQogIGdlb21fY29sKGZpbGwgPSAiIzM3NWI5OSIpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCkgKyBjb29yZF9mbGlwKCkgKw0KICBsYWJzKHRpdGxlID0gIkJhbGFuY2UgZGUgbGxlZ2FkYXMgeSBzYWxpZGFzIG9ic2VydmFkYXMiLA0KICAgICAgIHggPSAiQ29tdW5hIiwgeSA9ICJEZXN0aW5vcyBtZW5vcyBvcsOtZ2VuZXMiKQ0KYGBgDQoNCkVsICoqw61uZGljZSBkZSBiYWxhbmNlIHRlcnJpdG9yaWFsKiogcGVybWl0ZSBpZGVudGlmaWNhciBzaSBlbiB1bmEgY29tdW5hIHByZWRvbWluYW4gbGFzIGxsZWdhZGFzIG8gbGFzIHNhbGlkYXMgeSBkaW1lbnNpb25hciBlc2UgZGVzZXF1aWxpYnJpbyByZXNwZWN0byBhbCBtb3ZpbWllbnRvIHJlZ2lzdHJhZG8uIFNlIGNhbGN1bGEgYXPDrToNCg0KJCQNCkkgPSBcZnJhY3tcdGV4dHtMbGVnYWRhc30gLSBcdGV4dHtTYWxpZGFzfX0NCntcdGV4dHtMbGVnYWRhc30gKyBcdGV4dHtTYWxpZGFzfX0NCiQkDQoNClN1IHZhbG9yIHZhcsOtYSBlbnRyZSAqKuKIkjEgeSAxKio6DQoNCi0gKipQb3NpdGl2bzoqKiBzZSByZWdpc3RyYW4gbcOhcyBsbGVnYWRhcyBxdWUgc2FsaWRhcy4NCi0gKipOZWdhdGl2bzoqKiBzZSByZWdpc3RyYW4gbcOhcyBzYWxpZGFzIHF1ZSBsbGVnYWRhcy4NCi0gKipDZXJjYW5vIGEgY2VybzoqKiBhbWJvcyBjb250ZW9zIHNvbiBzaW1pbGFyZXMsIGF1bnF1ZSBwdWVkZW4gc2VyIGFsdG9zIG8gYmFqb3MuDQotICoqQ2VyY2FubyBhIGxvcyBleHRyZW1vczoqKiBleGlzdGUgdW4gcHJlZG9taW5pbyBtYXJjYWRvIGRlIHVubyBkZSBsb3MgZG9zIHNlbnRpZG9zLiBVbiB2YWxvciBkZSAxIHNpZ25pZmljYSBxdWUgc29sbyBzZSByZWdpc3RyYW4gbGxlZ2FkYXMgeSB1bm8gZGUg4oiSMSwgw7puaWNhbWVudGUgc2FsaWRhcy4gU2kgYW1ib3MgY29udGVvcyBzb24gY2VybywgZWwgw61uZGljZSBxdWVkYSBzaW4gZGVmaW5pci4NCg0KQSBkaWZlcmVuY2lhIGRlbCBiYWxhbmNlIGFic29sdXRvLCBxdWUgZXhwcmVzYSBjdcOhbnRhcyBsbGVnYWRhcyBhZGljaW9uYWxlcyBvIHNhbGlkYXMgYWRpY2lvbmFsZXMgZXhpc3RlbiwgKiplbCDDrW5kaWNlIG1pZGUgZWwgZGVzZXF1aWxpYnJpbyByZWxhdGl2byoqLiBQb3IgZXNvIHVuYSBjb211bmEgY29uIHBvY29zIHJlZ2lzdHJvcyBwdWVkZSBwcmVzZW50YXIgdW4gw61uZGljZSBlbGV2YWRvIHNpbiB0ZW5lciB1biBncmFuIHZvbHVtZW4gZGUgZGVzcGxhemFtaWVudG9zLiBBbWJhcyBtZWRpZGFzIGRlYmVuIGludGVycHJldGFyc2UganVudGFzLg0KDQojIyMgSW50ZXJwcmV0YWNpw7NuIGRlIGxvcyByZXN1bHRhZG9zDQoNCi0gKipMYSBjb211bmEgMiBjb21iaW5hIHZvbHVtZW4geSBwcmVkb21pbmlvIGRlIGxsZWdhZGFzOioqIHJlZ2lzdHJhIDMuNzcyIGxsZWdhZGFzIHkgMi4yNDkgc2FsaWRhcywgY29uIHVuIGJhbGFuY2UgZGUgKiorMS41MjMqKiB5IHVuIMOtbmRpY2UgYXByb3hpbWFkbyBkZSAqKjAsMjUzKiouIExhcyBsbGVnYWRhcyByZXByZXNlbnRhbiBlbCA2Miw2ICUgZGUgbGEgc3VtYSBkZSBhbWJvcyBleHRyZW1vcyB5IGxhcyBzYWxpZGFzIGVsIDM3LDQgJS4gRXN0byBjb25maXJtYSBzdSBwYXBlbCByZWNlcHRvciBkZW50cm8gZGVsIGNvbmp1bnRvIGFuYWxpemFkby4NCg0KLSAqKkxhcyBjb211bmFzIDIsIDMgeSAyMiBwcmVzZW50YW4gbG9zIG1heW9yZXMgZXhjZWRlbnRlcyBhYnNvbHV0b3MgZGUgbGxlZ2FkYXM6KiogZGVzdGFjYW4gY29tbyB0ZXJyaXRvcmlvcyByZWNlcHRvcmVzIGRlIGRlc3BsYXphbWllbnRvcy4gU2luIGVtYmFyZ28sIGVsIG9yZGVuIGRlbCBncsOhZmljbyBjb3JyZXNwb25kZSBhbCBiYWxhbmNlIGFic29sdXRvOyBubyBwZXJtaXRlIGVzdGFibGVjZXIgY3XDoWwgcHJlc2VudGEgZWwgbWF5b3IgZGVzZXF1aWxpYnJpbyByZWxhdGl2byBzaW4gY29tcGFyYXIgc3VzIMOtbmRpY2VzLg0KDQotICoqTGEgY29tdW5hIDE4IHByZXNlbnRhIGVsIG1heW9yIGV4Y2VkZW50ZSBhYnNvbHV0byBkZSBzYWxpZGFzOioqIGVuIGVzdGUgdGVycml0b3JpbyBzZSBvcmlnaW5hbiBtw6FzIGRlc3BsYXphbWllbnRvcyBkZSBsb3MgcXVlIHRlcm1pbmFuLiBQYXJhIGRldGVybWluYXIgcXXDqSB0YW4gbWFyY2FkbyBlcyBlc2UgcHJlZG9taW5pbyByZXNwZWN0byBhIHN1IG1vdmltaWVudG8gdG90YWwgc2UgZGViZSBjb25zdWx0YXIgc3Ugw61uZGljZSwgYWRlbcOhcyBkZWwgc2FsZG8gbmVnYXRpdm8uDQoNCi0gKipVbiBiYWxhbmNlIGNlcmNhbm8gYSBjZXJvIG5vIGltcGxpY2EgcG9jYSBhY3RpdmlkYWQ6KiogbGFzIGNvbXVuYXMgY29uIHNhbGlkYXMgeSBsbGVnYWRhcyBzaW1pbGFyZXMgcHVlZGVuIG1hbnRlbmVyIHVuIGludGVyY2FtYmlvIGVsZXZhZG8gY29uIG90cm9zIHRlcnJpdG9yaW9zLiBFbCDDrW5kaWNlIGRlc2NyaWJlIGVsIGVxdWlsaWJyaW8gZW50cmUgYW1ib3MgY29udGVvcywgcGVybyBubyBsYSBpbnRlbnNpZGFkIHRvdGFsIGRlIG1vdmlsaWRhZC4NCg0KIyMjIMK/UXXDqSBjb211bmFzIHJlY2liZW4gbcOhcyBkZSBsbyBxdWUgZ2VuZXJhbj8NCg0KYGBge3IgZGV0YWxsZS1iYWxhbmNlfQ0KZGV0YWxsZV9iYWxhbmNlIDwtIGJhbGFuY2UgJT4lDQogIG11dGF0ZShwY3Rfc2FsaWRhID0gMTAwICogb3JpZ2VuZXMgLyBOX2luY2x1aWRvLA0KICAgICAgICAgcGN0X2xsZWdhZGEgPSAxMDAgKiBkZXN0aW5vcyAvIE5faW5jbHVpZG8sDQogICAgICAgICBkaWZlcmVuY2lhX3BwID0gcGN0X2xsZWdhZGEgLSBwY3Rfc2FsaWRhLA0KICAgICAgICAgbGxlZ2FkYXNfcG9yXzEwMF9zYWxpZGFzID0gaWZfZWxzZShvcmlnZW5lcyA+IDAsIDEwMCAqIGRlc3Rpbm9zIC8gb3JpZ2VuZXMsIE5BX3JlYWxfKSkNCnJlY2VwdG9yYXMgPC0gZGV0YWxsZV9iYWxhbmNlICU+JSBmaWx0ZXIoYmFsYW5jZSA+IDApICU+JSBhcnJhbmdlKGRlc2MoYmFsYW5jZSksIGNvbXVuYSkNCmdlbmVyYWRvcmFzIDwtIGRldGFsbGVfYmFsYW5jZSAlPiUgZmlsdGVyKGJhbGFuY2UgPCAwKSAlPiUgYXJyYW5nZShiYWxhbmNlLCBjb211bmEpDQprbml0cjo6a2FibGUoYmluZF9yb3dzKA0KICByZWNlcHRvcmFzICU+JSBzbGljZV9oZWFkKG4gPSAzKSAlPiUgbXV0YXRlKFBlcmZpbCA9ICJNw6FzIGxsZWdhZGFzIiksDQogIGdlbmVyYWRvcmFzICU+JSBzbGljZV9oZWFkKG4gPSAzKSAlPiUgbXV0YXRlKFBlcmZpbCA9ICJNw6FzIHNhbGlkYXMiKSkgJT4lDQogICAgc2VsZWN0KFBlcmZpbCwgY29tdW5hLCBvcmlnZW5lcywgZGVzdGlub3MsIGJhbGFuY2UsIGRpZmVyZW5jaWFfcHAsIGxsZWdhZGFzX3Bvcl8xMDBfc2FsaWRhcyksDQogIGRpZ2l0cyA9IDEsIGNhcHRpb24gPSAiTWF5b3JlcyBkaWZlcmVuY2lhcyBhYnNvbHV0YXMgeSBsbGVnYWRhcyBwb3IgY2FkYSAxMDAgc2FsaWRhcyIpDQpgYGANCg0KYGBge3IgaW50ZXJwcmV0YXItYmFsYW5jZSwgZWNobz1GQUxTRSwgcmVzdWx0cz0nYXNpcyd9DQpjYXQoIlNlIG9ic2VydmFuICoqIiwgbnJvdyhyZWNlcHRvcmFzKSwgIiBjb211bmFzIGNvbiBtw6FzIGxsZWdhZGFzKiosICoqIiwgbnJvdyhnZW5lcmFkb3JhcyksDQogICAgIiBjb24gbcOhcyBzYWxpZGFzKiogeSAqKiIsIHN1bShiYWxhbmNlJGJhbGFuY2UgPT0gMCksICIgY29uIGNvbnRlb3MgaWd1YWxlcyoqLlxuXG4iLCBzZXAgPSAiIikNCmZvciAodGlwbyBpbiBjKCJyZWNlcHRvcmFzIiwgImdlbmVyYWRvcmFzIikpIHsNCiAgZmlsYXMgPC0gaWYgKHRpcG8gPT0gInJlY2VwdG9yYXMiKSBoZWFkKHJlY2VwdG9yYXMsIDMpIGVsc2UgaGVhZChnZW5lcmFkb3JhcywgMykNCiAgZm9yIChpIGluIHNlcV9sZW4obnJvdyhmaWxhcykpKSB7DQogICAgZiA8LSBmaWxhc1tpLCBdDQogICAgY2F0KCItICoqQ29tdW5hICIsIGYkY29tdW5hLCAiOioqICIsIGZtdF9uKGYkb3JpZ2VuZXMpLCAiIHNhbGlkYXMgeSAiLCBmbXRfbihmJGRlc3Rpbm9zKSwNCiAgICAgICAgIiBsbGVnYWRhczsgZWwgc2FsZG8gZXMgIiwgaWZlbHNlKGYkYmFsYW5jZSA+IDAsICIrIiwgIiIpLCBmbXRfbihmJGJhbGFuY2UpLCAiLiAiLCBzZXAgPSAiIikNCiAgICBpZiAoIWlzLm5hKGYkbGxlZ2FkYXNfcG9yXzEwMF9zYWxpZGFzKSkgew0KICAgICAgY2F0KCJQb3IgY2FkYSAxMDAgc2FsaWRhcyBzZSByZWdpc3RyYW4gIiwgZm10X3AoZiRsbGVnYWRhc19wb3JfMTAwX3NhbGlkYXMpLCAiIGxsZWdhZGFzLlxuIiwgc2VwID0gIiIpDQogICAgfSBlbHNlIGNhdCgiTGEgcmF6w7NuIG5vIHNlIGNhbGN1bGEgcG9ycXVlIG5vIGhheSBzYWxpZGFzIHJlZ2lzdHJhZGFzLlxuIikNCiAgfQ0KICBjYXQoIlxuIikNCn0NCmBgYA0KDQpFbCBzYWxkbyBpZGVudGlmaWNhIGTDs25kZSBwcmVkb21pbmFuIGxsZWdhZGFzIG8gc2FsaWRhcyBkZW50cm8gZGVsIGNvbmp1bnRvIG9ic2VydmFkby4gTGEgcmF6w7NuIHBvciBjYWRhIDEwMCBzYWxpZGFzIGF5dWRhIGEgZGltZW5zaW9uYXIgbGEgZGlmZXJlbmNpYSwgcGVybyBwdWVkZSBzZXIgbXV5IGFsdGEgY3VhbmRvIGVsIG7Dum1lcm8gZGUgc2FsaWRhcyBlcyBwZXF1ZcOxby4gTmluZ3VuYSBkZSBlc3RhcyBtZWRpZGFzIGNvbnZpZXJ0ZSBhdXRvbcOhdGljYW1lbnRlIHVuYSBjb211bmEgZW4gem9uYSByZXNpZGVuY2lhbCwgY29tZXJjaWFsIG8gbGFib3JhbDsgZXNhIGV4cGxpY2FjacOzbiByZXF1ZXJpcsOtYSBkYXRvcyBkZWwgcHJvcMOzc2l0byBkZWwgdmlhamUgbyBkZWwgdXNvIGRlbCBzdWVsby4NCg0KIyMgUmVsYWNpw7NuIGVudHJlIHNhbGlkYXMgeSBsbGVnYWRhcyBwb3IgY29tdW5hDQoNCkFkZW3DoXMgZGVsIGJhbGFuY2UsIHBvZGVtb3MgcHJlZ3VudGFybm9zIHNpIGxhcyBjb211bmFzIGNvbiBtw6FzIHNhbGlkYXMgcmVnaXN0cmFkYXMgdGFtYmnDqW4gdGllbmVuIG3DoXMgbGxlZ2FkYXMuIFBhcmEgZWxsbyBjb21wYXJhbW9zIGxvcyBkb3MgY29udGVvcyBkZSBsYXMgKioyMiBjb211bmFzKiogZW4gdW4gZGlhZ3JhbWEgZGUgZGlzcGVyc2nDs24geSBjYWxjdWxhbW9zIGxhIGNvcnJlbGFjacOzbiBkZSBQZWFyc29uLiBObyBzZSBjb3JyZWxhY2lvbmFuIGxvcyBjw7NkaWdvcyBudW3DqXJpY29zIGRlIGxhcyBjb211bmFzOiBlc29zIG7Dum1lcm9zIHNvbiBpZGVudGlmaWNhZG9yZXMsIG5vIGNhbnRpZGFkZXMuDQoNCmBgYHtyIGNvcnJlbGFjaW9ufQ0Kcl9vZCA8LSBpZiAoc2QoYmFsYW5jZSRvcmlnZW5lcykgPiAwICYmIHNkKGJhbGFuY2UkZGVzdGlub3MpID4gMCkgew0KICBjb3IoYmFsYW5jZSRvcmlnZW5lcywgYmFsYW5jZSRkZXN0aW5vcywgbWV0aG9kID0gInBlYXJzb24iKQ0KfSBlbHNlIE5BX3JlYWxfDQpnZ3Bsb3QoYmFsYW5jZSwgYWVzKG9yaWdlbmVzLCBkZXN0aW5vcykpICsNCiAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvdXIgPSAiIzk4YTliMyIpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMy41LCBjb2xvdXIgPSAiIzE2ODQ3YiIsIGFscGhhID0gMC44NSkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gY29tdW5hKSwgdmp1c3QgPSAtMC43LCBzaXplID0gMy4yLCBjaGVja19vdmVybGFwID0gVFJVRSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAuMDgsIDAuMTIpKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAuMDgsIDAuMTIpKSkgKw0KICBjb29yZF9lcXVhbCgpICsNCiAgbGFicyh0aXRsZSA9ICLCv0xhcyBjb211bmFzIGNvbiBtw6FzIHNhbGlkYXMgdGFtYmnDqW4gcmVjaWJlbiBtw6FzIGxsZWdhZGFzPyIsDQogICAgICAgc3VidGl0bGUgPSBwYXN0ZTAoIjIyIGNvbXVuYXMgwrcgUGVhcnNvbiByID0gIiwgaWZlbHNlKGlzLm5hKHJfb2QpLCAibm8gZGVmaW5pZG8iLCBmb3JtYXQocm91bmQocl9vZCwgMyksIGRlY2ltYWwubWFyayA9ICIsIikpKSwNCiAgICAgICB4ID0gIlNhbGlkYXMgcmVnaXN0cmFkYXMiLCB5ID0gIkxsZWdhZGFzIHJlZ2lzdHJhZGFzIiwNCiAgICAgICBjYXB0aW9uID0gIkNhZGEgcHVudG8gZXMgdW5hIGNvbXVuYS4gTGEgZGlhZ29uYWwgaW5kaWNhIGlndWFsIG7Dum1lcm8gZGUgc2FsaWRhcyB5IGxsZWdhZGFzLiIpDQpgYGANCg0KTGEgY29ycmVsYWNpw7NuIGRlIFBlYXJzb24gKCoqciA9IDAsODc2KiopIG11ZXN0cmEgdW5hIGFzb2NpYWNpw7NuIGxpbmVhbCBwb3NpdGl2YSBmdWVydGU6IGxhcyBjb211bmFzIGNvbiBtw6FzIHNhbGlkYXMgc3VlbGVuIHJlZ2lzdHJhciB0YW1iacOpbiBtw6FzIGxsZWdhZGFzLiBFc3RvIGluZGljYSBxdWUgbG9zIHRlcnJpdG9yaW9zIGNvbiBtYXlvciB2b2x1bWVuIGRlIG1vdmlsaWRhZCBwYXJ0aWNpcGFuIGRlIG1hbmVyYSBpbXBvcnRhbnRlIGVuIGFtYm9zIGV4dHJlbW9zIGRlbCB2aWFqZS4NCg0KKipFc3RhIGFzb2NpYWNpw7NuIG5vIGltcGxpY2EgZXF1aWxpYnJpbyB0ZXJyaXRvcmlhbC4qKiBMYXMgY29tdW5hcyAyIHkgMyBjb21iaW5hbiBjb250ZW9zIGVsZXZhZG9zIGNvbiB1biBjbGFybyBwcmVkb21pbmlvIGRlIGxsZWdhZGFzOyBsYSAyMiB0YW1iacOpbiBkZXN0YWNhIHBvciBlbmNpbWEgZGUgbGEgZGlhZ29uYWwuIEVuIGNvbnRyYXN0ZSwgY29tdW5hcyBjb21vIGxhIDE4IHkgbGEgMTMgcHJlc2VudGFuIG3DoXMgc2FsaWRhcyBxdWUgbGxlZ2FkYXMuIEFzw60sIHVuYSByZWxhY2nDs24gZ2VuZXJhbCBmdWVydGUgY29udml2ZSBjb24gZnVuY2lvbmVzIHJlY2VwdG9yYXMgeSBnZW5lcmFkb3JhcyBkaWZlcmVuY2lhZGFzLg0KDQpFbCBjb2VmaWNpZW50ZSByZXN1bWUgbG9zIGNvbnRlb3MgZGUgbGFzIDIyIGNvbXVuYXMgeSBwdWVkZSBzZXIgc2Vuc2libGUgYSBhcXVlbGxhcyBjb24gbWF5b3JlcyB2b2zDum1lbmVzLCBlc3BlY2lhbG1lbnRlIGxhIDIgeSBsYSAzLiBTdSBpbnRlcnByZXRhY2nDs24gZXMgZGVzY3JpcHRpdmE6IG5vIGRlbXVlc3RyYSBjYXVzYWxpZGFkLCBjb25leGlvbmVzIGRpcmVjdGFzIGVudHJlIGNvbXVuYXMgbmkgZGVwZW5kZW5jaWEgZXNwYWNpYWwuDQoNCiMgSGFsbGF6Z29zIHkgYXBsaWNhY2nDs24NCg0KYGBge3IgaW5kaWNhZG9yZXMtZmluYWxlcywgaW5jbHVkZT1GQUxTRX0NCiMgSW5kaWNhZG9yZXMgY2FsY3VsYWRvcyBzb2JyZSBlbCBtaXNtbyBjb25qdW50byBkZSB2aWFqZXMgaW5jbHVpZG9zLg0KZmluX24gPC0gZnVuY3Rpb24oeCkgew0KICBmb3JtYXQoeCwgYmlnLm1hcmsgPSAiLiIsIHNjaWVudGlmaWMgPSBGQUxTRSwgdHJpbSA9IFRSVUUpDQp9DQpmaW5fZGVjaW1hbCA8LSBmdW5jdGlvbih4LCBkaWdpdG9zID0gMSkgew0KICBpZiAoaXMubmEoeCkpIHJldHVybigibm8gZGVmaW5pZG8iKQ0KICBmb3JtYXRDKHgsIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gZGlnaXRvcywgZGVjaW1hbC5tYXJrID0gIiwiKQ0KfQ0KZmluX3BjdCA8LSBmdW5jdGlvbihuLCB0b3RhbCkgew0KICBpZiAodG90YWwgPiAwKSAxMDAgKiBuIC8gdG90YWwgZWxzZSBOQV9yZWFsXw0KfQ0KDQpmaW5fTiA8LSBucm93KHZpYWplcykNCmZpbl9OX2luaWNpYWwgPC0gbnJvdyhlbmN1ZXN0YSkNCg0KdG9wX29yaWdlbiA8LSBiYWxhbmNlICU+JQ0KICBhcnJhbmdlKGRlc2Mob3JpZ2VuZXMpLCBjb211bmEpICU+JQ0KICBzbGljZV9oZWFkKG4gPSAzKQ0KDQp0b3BfZGVzdGlubyA8LSBiYWxhbmNlICU+JQ0KICBhcnJhbmdlKGRlc2MoZGVzdGlub3MpLCBjb211bmEpICU+JQ0KICBzbGljZV9oZWFkKG4gPSAzKQ0KDQpjb25jZW50cmFjaW9uX29yaWdlbiA8LSBmaW5fcGN0KHN1bSh0b3Bfb3JpZ2VuJG9yaWdlbmVzKSwgZmluX04pDQpjb25jZW50cmFjaW9uX2Rlc3Rpbm8gPC0gZmluX3BjdChzdW0odG9wX2Rlc3Rpbm8kZGVzdGlub3MpLCBmaW5fTikNCg0KZmluX2ludHJhIDwtIHN1bSh2aWFqZXMkb3JpZ2VuID09IHZpYWplcyRkZXN0aW5vKQ0KZmluX2ludGVyIDwtIGZpbl9OIC0gZmluX2ludHJhDQoNCiMgQmFsYW5jZSBkZSBsYSBjb211bmEgMi4NCmZpbl9vMiA8LSBzdW0odmlhamVzJG9yaWdlbiA9PSAyKQ0KZmluX2QyIDwtIHN1bSh2aWFqZXMkZGVzdGlubyA9PSAyKQ0KZmluX3NhbGRvMiA8LSBmaW5fZDIgLSBmaW5fbzINCmZpbl9pbmRpY2UyIDwtIGlmIChmaW5fZDIgKyBmaW5fbzIgPiAwKSB7DQogIGZpbl9zYWxkbzIgLyAoZmluX2QyICsgZmluX28yKQ0KfSBlbHNlIE5BX3JlYWxfDQoNCiMgQ29uZXhpb25lcyBkaXJpZ2lkYXMgeSBjb25jZW50cmFjacOzbiBlbiBsYXMgZGlleiBwcmluY2lwYWxlcy4NCmZpbl9jb25leGlvbmVzIDwtIHZpYWplcyAlPiUNCiAgZmlsdGVyKG9yaWdlbiAhPSBkZXN0aW5vKSAlPiUNCiAgY291bnQob3JpZ2VuLCBkZXN0aW5vLCBuYW1lID0gInJlZ2lzdHJvcyIpICU+JQ0KICBhcnJhbmdlKGRlc2MocmVnaXN0cm9zKSwgb3JpZ2VuLCBkZXN0aW5vKQ0KDQpmaW5fdG9wMTAgPC0gc3VtKGhlYWQoZmluX2NvbmV4aW9uZXMkcmVnaXN0cm9zLCAxMCkpDQpmaW5fMTlfMiA8LSBzdW0odmlhamVzJG9yaWdlbiA9PSAxOSAmIHZpYWplcyRkZXN0aW5vID09IDIpDQpmaW5fMl8xOSA8LSBzdW0odmlhamVzJG9yaWdlbiA9PSAyICYgdmlhamVzJGRlc3Rpbm8gPT0gMTkpDQoNCiMgQ29tcG9zaWNpw7NuIG1vZGFsIGdlbmVyYWwgeSBkZSBsb3MgdGVycml0b3Jpb3MgZGVzdGFjYWRvcy4NCmZpbl9tb3RvX2dlbmVyYWwgPC0gZmluX3BjdChzdW0odmlhamVzJG1vZG8gPT0gIk1vdG9jaWNsZXRhIiksIGZpbl9OKQ0KZmluX21vdG9fZDIgPC0gZmluX3BjdCgNCiAgc3VtKHZpYWplcyRkZXN0aW5vID09IDIgJiB2aWFqZXMkbW9kbyA9PSAiTW90b2NpY2xldGEiKSwgZmluX2QyDQopDQpmaW5fYXV0b19nZW5lcmFsIDwtIGZpbl9wY3Qoc3VtKHZpYWplcyRtb2RvID09ICJBdXRvbcOzdmlsIiksIGZpbl9OKQ0KZmluX2F1dG9fbzE5IDwtIGZpbl9wY3QoDQogIHN1bSh2aWFqZXMkb3JpZ2VuID09IDE5ICYgdmlhamVzJG1vZG8gPT0gIkF1dG9tw7N2aWwiKSwNCiAgc3VtKHZpYWplcyRvcmlnZW4gPT0gMTkpDQopDQoNCiMgQ29tdW5hcyBjb24gbWF5b3JlcyBkZXNlcXVpbGlicmlvcyBhYnNvbHV0b3MuDQpmaW5fcmVjZXB0b3JhIDwtIGJhbGFuY2UgJT4lDQogIGFycmFuZ2UoZGVzYyhiYWxhbmNlKSwgY29tdW5hKSAlPiUNCiAgc2xpY2VfaGVhZChuID0gMSkNCg0KZmluX2dlbmVyYWRvcmEgPC0gYmFsYW5jZSAlPiUNCiAgYXJyYW5nZShiYWxhbmNlLCBjb211bmEpICU+JQ0KICBzbGljZV9oZWFkKG4gPSAxKQ0KDQpmaW5fciA8LSBpZiAoc2QoYmFsYW5jZSRvcmlnZW5lcykgPiAwICYmIHNkKGJhbGFuY2UkZGVzdGlub3MpID4gMCkgew0KICBjb3IoYmFsYW5jZSRvcmlnZW5lcywgYmFsYW5jZSRkZXN0aW5vcywgbWV0aG9kID0gInBlYXJzb24iKQ0KfSBlbHNlIE5BX3JlYWxfDQpgYGANCg0KRWwgYW7DoWxpc2lzIGluY2x1eWUgKipgciBmaW5fbihmaW5fTilgIGRlIGByIGZpbl9uKGZpbl9OX2luaWNpYWwpYCByZWdpc3Ryb3MgKGByIGZpbl9kZWNpbWFsKGZpbl9wY3QoZmluX04sIGZpbl9OX2luaWNpYWwpKWAgJSkqKi4gTGFzIHRyZXMgY29tdW5hcyBjb24gbcOhcyBzYWxpZGFzIOKAlCoqYHIgcGFzdGUodG9wX29yaWdlbiRjb211bmEsIGNvbGxhcHNlID0gIiwgIilgKirigJQgY29uY2VudHJhbiBlbCAqKmByIGZpbl9kZWNpbWFsKGNvbmNlbnRyYWNpb25fb3JpZ2VuKWAgJSoqIGRlIGxvcyBvcsOtZ2VuZXM7IGxhcyB0cmVzIGNvbiBtw6FzIGxsZWdhZGFzIOKAlCoqYHIgcGFzdGUodG9wX2Rlc3Rpbm8kY29tdW5hLCBjb2xsYXBzZSA9ICIsICIpYCoq4oCUIHJlw7puZW4gZWwgKipgciBmaW5fZGVjaW1hbChjb25jZW50cmFjaW9uX2Rlc3Rpbm8pYCAlKiogZGUgbG9zIGRlc3Rpbm9zLiBFc3RhIGNvbmNlbnRyYWNpw7NuIHRlcnJpdG9yaWFsIGNvZXhpc3RlIGNvbiB1bmEgZGlzdHJpYnVjacOzbiBhbXBsaWEgZGUgY29uZXhpb25lczogbGFzIGRpZXogcHJpbmNpcGFsZXMgcmVwcmVzZW50YW4gZWwgKipgciBmaW5fZGVjaW1hbChmaW5fcGN0KGZpbl90b3AxMCwgZmluX2ludGVyKSlgICUqKiBkZSBsb3MgdmlhamVzIGludGVyY29tdW5hLg0KDQpMYSBsZWN0dXJhIGNvbmp1bnRhIGRlIG1hcGFzLCBjb21wb3NpY2nDs24gbW9kYWwgeSBtYXRyaXogb3JpZ2Vu4oCTZGVzdGlubyBwZXJtaXRlIGRpc3Rpbmd1aXIgKipkw7NuZGUgc2UgY29uY2VudHJhIGVsIG1vdmltaWVudG8sIHF1w6kgdmVow61jdWxvcyBwYXJ0aWNpcGFuIHkgY8OzbW8gc2UgcmVsYWNpb25hbiBsb3MgdGVycml0b3Jpb3MqKi4gRXN0YXMgdHJlcyBkaW1lbnNpb25lcyBvcmllbnRhbiBsYXMgc2lndWllbnRlcyBsw61uZWFzIGRlIGFuw6FsaXNpczoNCg0KfCBIYWxsYXpnbyB8IEFwbGljYWNpw7NuIHF1ZSBtZXJlY2UgZXN0dWRpYXJzZSB8IEluZm9ybWFjacOzbiBuZWNlc2FyaWEgcGFyYSBzdXN0ZW50YXIgZGVjaXNpb25lcyB8DQp8Oi0tfDotLXw6LS18DQp8IExhIGNvbXVuYSAyIGNvbmNlbnRyYSBsbGVnYWRhcyBkZSBkaXN0aW50b3MgbW9kb3MgfCBFdmFsdWFyIGNvbmp1bnRhbWVudGUgbGFzIGNvbmRpY2lvbmVzIGRlIGFjY2VzbyB5IGxsZWdhZGEgYSBzdXMgYWN0aXZpZGFkZXMgfCBQcm9ww7NzaXRvcywgaG9yYXJpb3MsIGRlc3Rpbm9zIGVzcGVjw61maWNvcyB5IG9mZXJ0YSBkZSB0cmFuc3BvcnRlIHwNCnwgTGFzIGNvbXVuYXMgcHJlc2VudGFuIGJhbGFuY2VzIGRlIGRpc3RpbnRvIHNpZ25vIHkgbWFnbml0dWQgfCBDb21wYXJhciB0ZXJyaXRvcmlvcyByZWNlcHRvcmVzIHkgZ2VuZXJhZG9yZXMgbWVkaWFudGUgc2FsZG8sIMOtbmRpY2UgeSB2b2x1bWVuIHRvdGFsIHwgQ29iZXJ0dXJhIGRlbCBsZXZhbnRhbWllbnRvLCB1c29zIGRlbCBzdWVsbyB5IGRpc3RyaWJ1Y2nDs24gaG9yYXJpYSB8DQp8IExhIGNvbXVuYSAxOSBkZXN0YWNhIGNvbW8gb3JpZ2VuIGRlIGF1dG9tw7N2aWxlcyB8IEV4YW1pbmFyIHN1cyBwcmluY2lwYWxlcyBkZXN0aW5vcyB5IGxhcyBhbHRlcm5hdGl2YXMgZGlzcG9uaWJsZXMgYWwgYXV0b23Ds3ZpbCB8IE1hdHJpeiBwb3IgbW9kbywgdGllbXBvcywgb2N1cGFjacOzbiB2ZWhpY3VsYXIgeSB0cmFuc3BvcnRlIHDDumJsaWNvIHwNCnwgRWwgaW50ZXJjYW1iaW8gMTkg4oaUIDIgcHJlc2VudGEgZGlmZXJlbmNpYXMgZW50cmUgc2VudGlkb3MgfCBQcm9mdW5kaXphciBlbiBsYSBjb21wb3NpY2nDs24geSB0ZW1wb3JhbGlkYWQgZGUgZXN0YSBjb25leGnDs24gfCBNb2RvLCBtb3Rpdm8sIGhvcmFyaW8geSByZWNvcnJpZG9zIGVmZWN0aXZvcyB8DQp8IExvcyB2aWFqZXMgaW50ZXJjb211bmEgcHJlZG9taW5hbiB5IHNlIGRpc3RyaWJ1eWVuIGVudHJlIG3Dumx0aXBsZXMgcGFyZXMgfCBFc3R1ZGlhciBsYSBjb25lY3RpdmlkYWQgZGUgbGEgcmVkLCBhZGVtw6FzIGRlIHN1cyBlbmxhY2VzIGRlIG1heW9yIGNvbnRlbyB8IFRyYW5zYm9yZG9zLCBjb250aW51aWRhZCBkZSBydXRhcywgZnJlY3VlbmNpYXMgeSBhY2Nlc2liaWxpZGFkIHwNCnwgRXhpc3RlbiBjb211bmFzIGNvbiBwb2NvcyByZWdpc3Ryb3MgfCBWZXJpZmljYXIgc3UgcmVwcmVzZW50YWNpw7NuIGFudGVzIGRlIGFzaWduYXJsZXMgbWVub3IgcHJpb3JpZGFkIHwgRGlzZcOxbyBtdWVzdHJhbCwgcHVudG9zIGRlIGVuY3Vlc3RhLCBob3JhcmlvcyB5IGZhY3RvcmVzIGRlIGV4cGFuc2nDs24gfA0KDQpFc3RhcyBhcGxpY2FjaW9uZXMgY29uc3RpdHV5ZW4gKipsw61uZWFzIGRlIGRpYWduw7NzdGljbyoqLCBubyBpbnRlcnZlbmNpb25lcyB5YSBqdXN0aWZpY2FkYXMuIExvcyBjb250ZW9zIG5vIG1pZGVuIGRpcmVjdGFtZW50ZSBjb25nZXN0acOzbiwgcmllc2dvIHZpYWwgbmkgZMOpZmljaXQgZGUgaW5mcmFlc3RydWN0dXJhOyB0YW1wb2NvIHVuIGNlcm8gaW1wbGljYSBhdXNlbmNpYSByZWFsIGRlIGRlc3BsYXphbWllbnRvcy4NCg0KIyBDb25jbHVzaW9uZXMNCg0KMS4gKipMYSBjb211bmEgMiBkZXN0YWNhIGNvbW8gdW4gdGVycml0b3JpbyBkZSBhdHJhY2Npw7NuIGNvbiBhY3RpdmlkYWRlcyBkaXZlcnNhcy4qKiBJbmNsdXllIGJhcnJpb3MgY29tbyBHcmFuYWRhLCBDZW50ZW5hcmlvLCBDaGlwaWNoYXBlLCBWZXJzYWxsZXMsIFNhbiBWaWNlbnRlIHkgTGEgRmxvcmEsIGVudHJlIG90cm9zLiBMYSBvZmVydGEgZ2FzdHJvbsOzbWljYSBkZSBHcmFuYWRhLCBjb21lcmNpYWwgZGUgQ2hpcGljaGFwZSB5IENlbnRlbmFyaW8sIHkgZGUgYWxvamFtaWVudG8gZGVsIGVudG9ybm8gYXZlbmlkYSBTZXh0YeKAk0NoaXBpY2hhcGUgYXBvcnRhIGNvbnRleHRvIGEgc3VzICoqYHIgZmluX24oZmluX2QyKWAgbGxlZ2FkYXMqKiwgZnJlbnRlIGEgKipgciBmaW5fbihmaW5fbzIpYCBzYWxpZGFzKiosIHkgYSBzdSDDrW5kaWNlIGRlIGJhbGFuY2UgZGUgKipgciBmaW5fZGVjaW1hbChmaW5faW5kaWNlMiwgMylgKiouIEVsIHJlc3VsdGFkbyBlcyBjb21wYXRpYmxlIGNvbiBkZXNwbGF6YW1pZW50b3MgaGFjaWEgYWN0aXZpZGFkZXMgeSBzZXJ2aWNpb3MsIGF1bnF1ZSBpZGVudGlmaWNhciBzdXMgbW90aXZvcyBleGlnZSBjb25zdWx0YXIgZXNhIHZhcmlhYmxlIGVuIGxhIGVuY3Vlc3RhLg0KDQoyLiAqKkxhIGF0cmFjY2nDs24gZGUgbGEgY29tdW5hIDIgc2UgbWFudGllbmUgZW50cmUgdmVow61jdWxvcywgc2luIHVuYSBlc3BlY2lhbGl6YWNpw7NuIG1vZGFsIG1hcmNhZGEuKiogU3UgbGlkZXJhemdvIGNvbW8gZGVzdGlubyBlbiBiaWNpY2xldGEsIG1vdG9jaWNsZXRhIHkgYXV0b23Ds3ZpbCBzdWdpZXJlIHF1ZSBkaXN0aW50b3MgbW9kb3MgcGFydGljaXBhbiBlbiBlbCBhY2Nlc28gYSBlc3RlIHRlcnJpdG9yaW8uIExhcyBtb3RvY2ljbGV0YXMgcmVwcmVzZW50YW4gZWwgKipgciBmaW5fZGVjaW1hbChmaW5fbW90b19kMilgICUqKiBkZSBzdXMgbGxlZ2FkYXMsIHByb3BvcmNpw7NuIGNlcmNhbmEgYWwgKipgciBmaW5fZGVjaW1hbChmaW5fbW90b19nZW5lcmFsKWAgJSoqIGdlbmVyYWwuIFBvciB0YW50bywgc3UgcHJpbmNpcGFsIHJhc2dvIGVzIGxhIGNvbmNlbnRyYWNpw7NuIGRlIGRlc3Rpbm9zIGRlIHZhcmlvcyBtb2RvcywgbG8gcXVlIGp1c3RpZmljYSBlc3R1ZGlhciBjb25qdW50YW1lbnRlIGxhcyBjb25kaWNpb25lcyBkZSBhY2Nlc28sIGNpcmN1bGFjacOzbiB5IGxsZWdhZGEuDQoNCjMuICoqTGEgY29tdW5hIDE5IGNvbWJpbmEgZnVuY2lvbmVzIHJlc2lkZW5jaWFsZXMgeSBkZSBzZXJ2aWNpb3MgcXVlIHJlcXVpZXJlbiB1bmEgbGVjdHVyYSBkaWZlcmVuY2lhZGEgZGUgc3VzIG9yw61nZW5lcy4qKiBDb21wcmVuZGUgc2VjdG9yZXMgY29tbyBTYW4gRmVybmFuZG8gVmllam8geSBOdWV2bywgVGVxdWVuZGFtYSwgTnVldmEgVGVxdWVuZGFtYSwgRWwgTGlkbywgRWwgUmVmdWdpbyB5IFBhbXBhbGluZGEsIHkgcmXDum5lIGVxdWlwYW1pZW50b3MgZGUgc2FsdWQsIGVkdWNhY2nDs24geSBkZXBvcnRlLiBFbCBhdXRvbcOzdmlsIHJlcHJlc2VudGEgZWwgKipgciBmaW5fZGVjaW1hbChmaW5fYXV0b19vMTkpYCAlKiogZGUgc3VzIHNhbGlkYXMsIGZyZW50ZSBhbCAqKmByIGZpbl9kZWNpbWFsKGZpbl9hdXRvX2dlbmVyYWwpYCAlKiogZ2VuZXJhbC4gU3UgbGlkZXJhemdvIGVuIHNhbGlkYXMgZGUgZXN0ZSBtb2RvIHB1ZWRlIGludm9sdWNyYXIgdmlhamVzIGRlc2RlIHZpdmllbmRhcyB5IGRlc2RlIGx1Z2FyZXMgZGUgYWN0aXZpZGFkOyBubyBwZXJtaXRlIGF0cmlidWlyIHRvZG9zIGxvcyBkZXNwbGF6YW1pZW50b3MgYSByZXNpZGVudGVzIG5pIGluZmVyaXIgc3VzIGluZ3Jlc29zIG8gdGVuZW5jaWEgZGUgdmVow61jdWxvLg0KDQo0LiAqKkVsIGludGVyY2FtYmlvIGVudHJlIGxhcyBjb211bmFzIDE5IHkgMiB2aW5jdWxhIHRlcnJpdG9yaW9zIGNvbiBhY3RpdmlkYWRlcyBkaXZlcnNhcywgcGVybyBzdSBleHBsaWNhY2nDs24gZnVuY2lvbmFsIHNpZ3VlIGFiaWVydGEuKiogU2Ugb2JzZXJ2YW4gKipgciBmaW5fbihmaW5fMTlfMilgIHZpYWplcyBlbiBzZW50aWRvIDE5IOKGkiAyKiogeSAqKmByIGZpbl9uKGZpbl8yXzE5KWAgZW4gc2VudGlkbyBjb250cmFyaW8qKi4gRXN0YSBhc2ltZXRyw61hIGVzIGNvbnNpc3RlbnRlIGNvbiBlbCBwYXBlbCByZWNlcHRvciBkZSBsYSBjb211bmEgMiwgcGVybyBubyBkZW11ZXN0cmEgdW5hIHJlbGFjacOzbiBleGNsdXNpdmEgZW50cmUgdml2aWVuZGEgeSBlbXBsZW8gbmkgdW4gcGF0csOzbiBkZSBpZGEgeSByZWdyZXNvLiBQYXJhIGVzdGFibGVjZXJsYSBzZSBuZWNlc2l0YW4gbW90aXZvcywgaG9yYXJpb3MgeSBtb2RvcyBkZSBsb3MgdmlhamVzIGRlIGVzdGEgY29uZXhpw7NuLg0KDQo1LiAqKkxhIGVzdHJ1Y3R1cmEgb2JzZXJ2YWRhIGV4aWdlIGNvbWJpbmFyIHVuYSBtaXJhZGEgZGUgcmVkIGNvbiBhbsOhbGlzaXMgZGVudHJvIGRlIGNhZGEgY29tdW5hLioqIEVsICoqYHIgZmluX2RlY2ltYWwoZmluX3BjdChmaW5faW50ZXIsIGZpbl9OKSlgICUqKiBkZSBsb3MgdmlhamVzIGNvbmVjdGEgY29tdW5hcyBkaWZlcmVudGVzLCBtaWVudHJhcyBsYXMgZGlleiBjb25leGlvbmVzIHByaW5jaXBhbGVzIHJlw7puZW4gZWwgKipgciBmaW5fZGVjaW1hbChmaW5fcGN0KGZpbl90b3AxMCwgZmluX2ludGVyKSlgICUqKiBkZSBlc29zIHJlZ2lzdHJvcy4gRXhpc3RlbiBkZXN0aW5vcyBkZXN0YWNhZG9zIGFsaW1lbnRhZG9zIHBvciBtw7psdGlwbGVzIHJlbGFjaW9uZXMgdGVycml0b3JpYWxlcy4gU2luIGVtYmFyZ28sIGNhZGEgY29tdW5hIHJlw7puZSBiYXJyaW9zIHkgYWN0aXZpZGFkZXMgaGV0ZXJvZ8OpbmVvczogc3VzIGNvbnRlb3MgYWdyZWdhZG9zIG5vIHBlcm1pdGVuIGlkZW50aWZpY2FyIHF1w6kgc2VjdG9yZXMgZXhwbGljYW4gbGEgY29uY2VudHJhY2nDs24gbmkgbG9jYWxpemFyIGRpcmVjdGFtZW50ZSBjb3JyZWRvcmVzIGRlIGludGVydmVuY2nDs24uDQoNCkVuIGNvbmp1bnRvLCBsb3MgcmVzdWx0YWRvcyBvcmllbnRhbiB1biBkaWFnbsOzc3RpY28gZGUgYWNjZXNpYmlsaWRhZCBlbnRyZSB0ZXJyaXRvcmlvcyB5IGhhY2lhIHN1cyBhY3RpdmlkYWRlcy4gU3UgYXBsaWNhY2nDs24gcmVxdWllcmUgY29udHJhc3RhciBsb3MgcGF0cm9uZXMgY29uIHVzb3MgZGVsIHN1ZWxvLCBvZmVydGEgZGUgdHJhbnNwb3J0ZSwgcmVjb3JyaWRvcyB5IGNvYmVydHVyYSBkZSBsYSBlbmN1ZXN0YS4gTGFzIGNvbmNsdXNpb25lcyBkZXNjcmliZW4gbG9zIHJlZ2lzdHJvcyBpbmNsdWlkb3MsIHNpbiBleHBhbnNpw7NuIG11ZXN0cmFsLCB5IG5vIGVzdGltYW4gcG9yIHPDrSBzb2xhcyBsYSBkZW1hbmRhIHRvdGFsIGRlIG1vdmlsaWRhZCBkZSBDYWxpLg0KDQoqKkZ1ZW50ZXMgZGVsIGNvbnRleHRvIHRlcnJpdG9yaWFsOioqIFtiYXJyaW9zIGRlIGxhIGNvbXVuYSAyXShodHRwczovL3dlYjEuY2FsaS5nb3YuY28vcGFydGljaXBhY2lvbi9wdWJsaWNhY2lvbmVzL2VzdGFkaXN0aWNhc19wb3JfYmFycmlvX2NvbXVuYV9fcHViKSwgW2JhcnJpb3MgZGUgbGEgY29tdW5hIDE5XShodHRwczovL3dlYjEuY2FsaS5nb3YuY28vcGFydGljaXBhY2lvbi9wdWJsaWNhY2lvbmVzL2NvbXVuYV9kaWV6X3lfbnVldmVfcHViKSwgW29mZXJ0YSB0dXLDrXN0aWNhIGRlIENhbGldKGh0dHBzOi8vd3d3LmNhbGkuZ292LmNvL3ByZWd1bnRhcy1mcmVjdWVudGVzLzE0MDcyL3NlY3JldGFyaWEtZGUtdHVyaXNtby8pIHkgW2NhcnRvZ3JhZsOtYSBkZSBsYSBjb211bmEgMTkg4oCUIElERVNDXShodHRwczovL2lkZXNjLmNhbGkuZ292LmNvL2Rvd25sb2FkL21hcGFzX2NvbXVuYXMvY29tdW5hXzE5LnBkZikuDQoNCioqRnVlbnRlcyBkZSBkYXRvcy4qKiBBcmNoaXZvIGBFbmN1ZXN0YU9yaWdlbkRlc3Rpbm8ueGxzeGAgeSBjYXJ0b2dyYWbDrWEgYENvbXVuYXMuc2hwYC4gRWwgcGVyaW9kbyBvYnNlcnZhZG8gc2Ugb2J0aWVuZSBkZSBsYSBjb2x1bW5hIGBmZWNoYXNgIGRlbCBFeGNlbC4gTG9zIGVtcGF0ZXMgZW4gcmFua2luZ3MgeSBjb25leGlvbmVzIHNlIHJlc3VlbHZlbiBwb3IgY8OzZGlnbyBhc2NlbmRlbnRlLg0KDQpgYGB7ciBzZXNpb24sIGVjaG89RkFMU0V9DQpzZXNzaW9uSW5mbygpDQpgYGANCg==