1 Introducción

En este capítulo se presenta el contexto conceptual del caso y se describe el conjunto de datos utilizado, con el fin de enmarcar el análisis dentro de los fundamentos de los Sistemas de Información Geográfica (SIG).

Un Sistema de Información Geográfica es una tecnología de manejo de información geográfica que combina cinco componentes esenciales: personas especializadas, datos descriptivos y espaciales, métodos analíticos, hardware y software, todos organizados para analizar, manipular, procesar, almacenar, generar y visualizar información referenciada geográficamente. En ese sentido, un proyecto SIG se construye para almacenar y recuperar datos, visualizar información de campo, identificar patrones espaciales, describir características de objetos y superficies, analizar datos espaciales y generar nueva información que apoye la toma de decisiones.

La Encuesta Origen-Destino de Cali del año 2015 constituye una fuente de datos secundaria en el marco del SIG, ya que proviene de un instrumento de recolección externa que registra los trayectos realizados por personas en la ciudad. La información de atributos contenida en la hoja de cálculo se integra con la cartografía vectorial de comunas mediante un proceso de join espacial-tabular, lo que permite generar nueva información georreferenciada útil para la planificación de la movilidad urbana.

El presente documento genera 8 mapas temáticos de coropletas y 4 mapas interactivos origen-destino organizados por tipo de vehículo (bicicleta, moto, automóvil y general), siguiendo la metodología de análisis de datos de área propia de los SIG.

2 Instalación y Carga de Librerías

En este capítulo se instalan y cargan todas las librerías necesarias para ejecutar el análisis espacial, el procesamiento de datos, la generación de los mapas temáticos estáticos y los mapas interactivos origen-destino.

# ---- Instalación (ejecutar solo si los paquetes no están disponibles) -------
paquetes <- c("readxl", "sf", "dplyr", "RColorBrewer",
              "classInt", "knitr", "kableExtra",
              "leaflet", "leaflet.extras")

instalados <- paquetes[!(paquetes %in% installed.packages()[, "Package"])]
if (length(instalados) > 0) {
  install.packages(instalados, repos = "https://cloud.r-project.org")
}

# ---- Carga de librerías -----------------------------------------------------
library(readxl)          # Lectura de archivos Excel (.xlsx)
library(sf)              # Datos espaciales vectoriales (sucesor de sp/rgdal)
library(dplyr)           # Manipulación y transformación de datos tabulares
library(RColorBrewer)    # Paletas de colores secuenciales para mapas
library(classInt)        # Clasificación de variables en intervalos
library(knitr)           # Tablas formateadas en R Markdown
library(kableExtra)      # Estilos adicionales para tablas kable
library(leaflet)         # Mapas interactivos con OpenStreetMap
library(leaflet.extras)  # Funciones adicionales para leaflet (heatmap, etc.)

3 Carga y Preparación de Datos

En este capítulo se cargan los datos geográficos y tabulares, se revisa su estructura y se realiza el filtrado necesario para conservar únicamente los viajes intraurbanos de la ciudad de Cali.

3.1 Shapefile de Comunas de Cali

La cartografía de comunas corresponde a un modelo de datos vectorial de polígonos. Los polígonos son entidades bidimensionales que tienen las propiedades de área y perímetro, y son la primitiva geométrica adecuada para representar unidades administrativas como las comunas. El shapefile agrupa los archivos .shp, .dbf, .shx y .prj, que contienen respectivamente la geometría, los atributos, los índices y el sistema de referencia de coordenadas. La lectura se realiza con el paquete sf mediante la función st_read(), que es el estándar moderno en R para el manejo de datos espaciales vectoriales.

# Lectura del shapefile de comunas
comunas <- st_read(
  "C:/RDC/Analitica_Geoespacial/Casos/cali/Comunas.shp",
  quiet = TRUE
)

head(comunas)

Tabla 1. Estructura de la tabla de atributos del shapefile de Comunas de Cali

kable(
  head(st_drop_geometry(comunas)),
  caption = NULL,
  align   = "c"
) |>
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width        = FALSE,
    font_size         = 13
  )
OBJECTID gid comuna nombre
1 107 2 Comuna 2
2 108 1 Comuna 1
3 109 3 Comuna 3
4 110 19 Comuna 19
5 103 15 Comuna 15
6 104 17 Comuna 17

Interpretación: La tabla de atributos del shapefile contiene cuatro campos: OBJECTID, gid, comuna (identificador numérico del 1 al 22) y nombre. El campo comuna es el vínculo que permite enlazar los datos tabulares de la encuesta con la geometría espacial. La existencia de las 22 comunas confirma que la cartografía cubre la totalidad del área urbana de Cali, lo cual es condición necesaria para que los mapas temáticos representen el territorio de manera completa y sin vacíos de información.

3.2 Encuesta Origen-Destino

La Encuesta Origen-Destino es una fuente de datos secundaria en formato de hoja de cálculo. Los datos de atributos ingresan al SIG a través de la hoja de cálculo y se enlazan con las entidades gráficas del shapefile mediante el campo de comuna compartido.

# Lectura del archivo Excel
encuesta <- read_excel(
  path  = "C:/RDC/Analitica_Geoespacial/Casos/EncuestaOrigenDestino.xlsx",
  sheet = "Hoja1"
)

cat("Nombres de columnas leidos por R:\n")
## Nombres de columnas leidos por R:
print(colnames(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"
# Renombrar columnas clave
encuesta <- encuesta |>
  rename(
    comuna_origen  = `comuna origen`,
    comuna_destino = `comuna destino`,
    tipo_vehiculo  = `TIPO DE VEHÍCULO`
  )

# Si los nombres no tienen espacios, usar esta alternativa:
# encuesta <- encuesta |>
#   rename(
#     comuna_origen  = `comunaorigen`,
#     comuna_destino = `comunadestino`,
#     tipo_vehiculo  = `TIPODEVEHÍCULO`
#   )

glimpse(encuesta)
## Rows: 35,054
## Columns: 28
## $ FECHA                                                                                      <dttm> …
## $ `ID ESTACIÓN`                                                                              <dbl> …
## $ ESTACIÓN                                                                                   <chr> …
## $ ACCESO                                                                                     <chr> …
## $ MOVIMIENTO                                                                                 <chr> …
## $ `Hora de Encuesta`                                                                         <dttm> …
## $ MUNICIPIO...7                                                                              <chr> …
## $ `DEPARTAMENTO / LOCALIDAD  / COMUNA / DISTRITO / BARRIO / VEREDA  / HITO / DIRECCIÓN...8`  <chr> …
## $ `Codigo Origen_SDG`                                                                        <chr> …
## $ `¿QUE ESTABA HACIENDO EN ESE LUGAR?`                                                       <dbl> …
## $ MUNICIPIO...11                                                                             <chr> …
## $ `DEPARTAMENTO / LOCALIDAD  / COMUNA / DISTRITO / BARRIO / VEREDA  / HITO / DIRECCIÓN...12` <chr> …
## $ `Codigo Destino_SDG`                                                                       <chr> …
## $ `¿QUE VA HACER A ESE LUGAR?`                                                               <dbl> …
## $ `ESTRATO EN SU VIVIENDA`                                                                   <dbl> …
## $ `¿DISPONIA DE UN VEHÍCULO PARA REALIZAR ESTE DESPLAZAMIENTO?`                              <dbl> …
## $ `OTRO ¿CUÁL?...17`                                                                         <lgl> …
## $ ANTES                                                                                      <dbl> …
## $ DESPUES                                                                                    <lgl> …
## $ EDAD                                                                                       <dbl> …
## $ SEXO                                                                                       <dbl> …
## $ `PERSONAS EN EL VEHÍCULO`                                                                  <dbl> …
## $ tipo_vehiculo                                                                              <dbl> …
## $ `OTRO ¿CUÁL?...24`                                                                         <lgl> …
## $ `TIPO DE VIAJERO`                                                                          <chr> …
## $ comuna_origen                                                                              <chr> …
## $ comuna_destino                                                                             <chr> …
## $ Intracomuna                                                                                <chr> …

Tabla 2. Primeras filas de la Encuesta Origen-Destino de Cali 2015

cols_tabla <- c("comuna_origen", "comuna_destino", "tipo_vehiculo",
                "ESTRATO EN SU VIVIENDA", "EDAD", "SEXO")
cols_tabla <- cols_tabla[cols_tabla %in% colnames(encuesta)]

kable(
  head(encuesta[, cols_tabla], 8),
  caption = NULL,
  align   = "c"
) |>
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width        = FALSE,
    font_size         = 12
  ) |>
  scroll_box(width = "100%")
comuna_origen comuna_destino tipo_vehiculo ESTRATO EN SU VIVIENDA EDAD SEXO
02 22 2 3 35 2
Fuera de Cali 19 3 2 40 2
Fuera de Cali 03 2 3 33 2
Fuera de Cali 09 2 2 28 2
Fuera de Cali 02 2 3 25 2
Fuera de Cali 02 3 2 31 2
Fuera de Cali 17 3 3 50 2
Fuera de Cali Fuera de Cali 2 3 37 2

Interpretación: La encuesta registra variables espaciales (comunas de origen y destino), socioeconómicas (estrato, sexo) y de movilidad (tipo de vehículo, propósito del viaje). Esta riqueza de atributos es característica de una fuente secundaria robusta: la información geográfica posee los tres atributos esenciales definidos por los SIG, el componente espacial (la localización por comuna), el componente temático (las características del viaje y del viajero) y el componente temporal (el año de levantamiento, 2015). La codificación del tipo de vehículo en valores enteros (1, 2, 3) permite realizar filtros eficientes durante el análisis.

3.3 Filtrado: Viajes Intraurbanos de Cali

encuesta_cali <- encuesta |>
  filter(
    !is.na(comuna_origen),
    comuna_origen  != "Fuera de Cali",
    !is.na(comuna_destino),
    comuna_destino != "Fuera de Cali"
  ) |>
  mutate(
    comuna_origen  = as.integer(comuna_origen),
    comuna_destino = as.integer(comuna_destino),
    tipo_vehiculo  = as.integer(tipo_vehiculo)
  )

total_general  <- nrow(encuesta)
total_intra    <- nrow(encuesta_cali)
total_excluido <- total_general - total_intra

cat("Registros totales       :", total_general,  "\n")
## Registros totales       : 35054
cat("Viajes intraurbanos Cali:", total_intra,     "\n")
## Viajes intraurbanos Cali: 23372
cat("Registros excluidos     :", total_excluido, "\n")
## Registros excluidos     : 11682

Tabla 3. Resumen del filtrado de registros de la Encuesta Origen-Destino

resumen_filtro <- data.frame(
  Categoria  = c("Registros totales en la encuesta",
                 "Viajes intraurbanos de Cali (analisis)",
                 "Registros excluidos (intermunicipales o sin datos)"),
  Cantidad   = c(total_general, total_intra, total_excluido),
  Porcentaje = paste0(round(c(total_general, total_intra, total_excluido) /
                               total_general * 100, 1), " %")
)

kable(resumen_filtro, caption = NULL, align = c("l", "r", "r")) |>
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width        = FALSE,
    font_size         = 13
  )
Categoria Cantidad Porcentaje
Registros totales en la encuesta 35054 100 %
Viajes intraurbanos de Cali (analisis) 23372 66.7 %
Registros excluidos (intermunicipales o sin datos) 11682 33.3 %

Interpretación: El proceso de filtrado delimita el universo de análisis a los viajes cuyos dos extremos, origen y destino, se encuentran dentro del perímetro urbano de Cali. Los registros con la etiqueta “Fuera de Cali” corresponden a trayectos intermunicipales que incluyen municipios del Área Metropolitana como Yumbo, Palmira, Jamundí y Candelaria; estos viajes no son representables en la cartografía comunal de la ciudad y se excluyen del análisis temático. Esta decisión metodológica es coherente con el objetivo del entregable, que se centra en la movilidad intraurbana.

4 Análisis Exploratorio de los Datos

En este capítulo se exploran las distribuciones de las variables principales de la encuesta para comprender la composición de los viajes antes de generar los mapas temáticos.

4.1 Distribución por Tipo de Vehículo

conteo_veh <- encuesta_cali |>
  filter(tipo_vehiculo %in% 1:3) |>
  mutate(Vehiculo = case_when(
    tipo_vehiculo == 1 ~ "Bicicleta",
    tipo_vehiculo == 2 ~ "Moto",
    tipo_vehiculo == 3 ~ "Automovil"
  )) |>
  group_by(Vehiculo) |>
  summarise(n = n()) |>
  mutate(pct = round(n / sum(n) * 100, 1))

bp <- barplot(
  conteo_veh$n,
  names.arg = conteo_veh$Vehiculo,
  col       = pal_vehiculos,
  border    = "white",
  main      = "",
  ylab      = "Numero de viajes",
  xlab      = "Tipo de vehiculo",
  ylim      = c(0, max(conteo_veh$n) * 1.2),
  las       = 1,
  cex.names = 1.1,
  cex.axis  = 0.95
)

text(bp, conteo_veh$n + max(conteo_veh$n) * 0.03,
     labels = paste0(conteo_veh$n, "\n(", conteo_veh$pct, "%)"),
     cex = 0.9, font = 2)

Figura 1. Distribución de viajes intraurbanos de Cali por tipo de vehículo (Encuesta Origen-Destino 2015)

Interpretación: La gráfica muestra la composición modal de los viajes intraurbanos registrados en la encuesta para los tres modos de transporte analizados. El automóvil concentra la mayor proporción de los trayectos, seguido por la moto y la bicicleta. Esta distribución refleja el patrón de motorización predominante en ciudades colombianas intermedias y grandes, donde el vehículo privado de cuatro ruedas sigue siendo el modo dominante a pesar del crecimiento sostenido de la motocicleta en la última década. La participación de la bicicleta, aunque menor, tiene implicaciones directas para la planificación de infraestructura ciclista en la ciudad.

4.2 Distribución de Viajes por Comuna de Origen

orig_bar <- encuesta_cali |>
  group_by(comuna_origen) |>
  summarise(n = n()) |>
  arrange(comuna_origen)

barplot(
  orig_bar$n,
  names.arg = orig_bar$comuna_origen,
  col       = pal_principal,
  border    = "white",
  main      = "",
  ylab      = "Numero de viajes",
  xlab      = "Comuna de origen",
  las       = 2,
  cex.names = 0.85,
  cex.axis  = 0.9
)

Figura 2. Número de viajes por comuna de origen (todos los modos, Cali 2015)

Interpretación: El gráfico de barras revela que la generación de viajes no se distribuye de manera uniforme entre las 22 comunas. Algunas comunas concentran una cantidad notablemente mayor de viajes originados, lo que indica que son zonas de alta densidad residencial o de mayor actividad generadora de desplazamientos. Este patrón heterogéneo es esperado en ciudades con estructuras urbanas consolidadas y desigualdades socioeconómicas territoriales, donde las áreas de mayor densidad poblacional y menor acceso a transporte masivo tienden a generar más viajes en modos privados o alternativos.

4.3 Distribución de Viajes por Comuna de Destino

dest_bar <- encuesta_cali |>
  group_by(comuna_destino) |>
  summarise(n = n()) |>
  arrange(comuna_destino)

barplot(
  dest_bar$n,
  names.arg = dest_bar$comuna_destino,
  col       = pal_acento,
  border    = "white",
  main      = "",
  ylab      = "Numero de viajes",
  xlab      = "Comuna de destino",
  las       = 2,
  cex.names = 0.85,
  cex.axis  = 0.9
)

Figura 3. Número de viajes por comuna de destino (todos los modos, Cali 2015)

Interpretación: La distribución de los destinos presenta un perfil diferente al de los orígenes, con algunas comunas que concentran una atracción de viajes significativamente mayor. Las comunas con mayor número de destinos corresponden típicamente a centralidades urbanas donde se localizan actividades económicas, educativas, comerciales o institucionales. Esta polarización espacial entre zonas generadoras y zonas atractoras es un patrón característico del análisis de datos de área en ciudades latinoamericanas y constituye la base del diagnóstico de la demanda de movilidad.

5 Conteos por Comuna para los 8 Mapas

En este capítulo se generan los ocho conjuntos de conteo que alimentan los mapas temáticos, agrupando los viajes por comuna de origen y destino para cada modo de transporte.

# ---- ORÍGENES ----------------------------------------------------------------
origen_general   <- encuesta_cali |> group_by(comuna = comuna_origen) |> summarise(n = n())
origen_bicicleta <- encuesta_cali |> filter(tipo_vehiculo == 1) |> group_by(comuna = comuna_origen) |> summarise(n = n())
origen_moto      <- encuesta_cali |> filter(tipo_vehiculo == 2) |> group_by(comuna = comuna_origen) |> summarise(n = n())
origen_automovil <- encuesta_cali |> filter(tipo_vehiculo == 3) |> group_by(comuna = comuna_origen) |> summarise(n = n())

# ---- DESTINOS ----------------------------------------------------------------
destino_general   <- encuesta_cali |> group_by(comuna = comuna_destino) |> summarise(n = n())
destino_bicicleta <- encuesta_cali |> filter(tipo_vehiculo == 1) |> group_by(comuna = comuna_destino) |> summarise(n = n())
destino_moto      <- encuesta_cali |> filter(tipo_vehiculo == 2) |> group_by(comuna = comuna_destino) |> summarise(n = n())
destino_automovil <- encuesta_cali |> filter(tipo_vehiculo == 3) |> group_by(comuna = comuna_destino) |> summarise(n = n())

Tabla 4. Resumen de conteos de viajes por modo de transporte y dirección

resumen_conteos <- data.frame(
  Mapa      = paste0("Mapa ", 1:8),
  Direccion = rep(c("Origen", "Destino"), each = 4),
  Modo      = rep(c("General", "Bicicleta", "Moto", "Automovil"), 2),
  Total_Viajes = c(
    sum(origen_general$n),   sum(origen_bicicleta$n),
    sum(origen_moto$n),      sum(origen_automovil$n),
    sum(destino_general$n),  sum(destino_bicicleta$n),
    sum(destino_moto$n),     sum(destino_automovil$n)
  )
)

kable(resumen_conteos, caption = NULL,
      col.names = c("Mapa", "Dirección", "Modo de Transporte", "Total de Viajes"),
      align = c("l", "l", "l", "r")) |>
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    font_size  = 13
  ) |>
  row_spec(0, bold = TRUE)
Mapa Dirección Modo de Transporte Total de Viajes
Mapa 1 Origen General 23372
Mapa 2 Origen Bicicleta 1298
Mapa 3 Origen Moto 10843
Mapa 4 Origen Automovil 9107
Mapa 5 Destino General 23372
Mapa 6 Destino Bicicleta 1298
Mapa 7 Destino Moto 10843
Mapa 8 Destino Automovil 9107

Interpretación: La tabla resume el universo de análisis para cada uno de los ocho mapas. Se observa que el total de viajes en los mapas de origen y destino coincide para cada modo, lo que valida la consistencia del proceso de filtrado y conteo. La diferencia en el número de viajes entre modos refleja la composición modal real de la encuesta: el automóvil y la moto agrupan la mayor proporción de registros, mientras que la bicicleta representa una fracción menor pero no despreciable desde el punto de vista de la planificación de infraestructura.

6 Función para Generación de Mapas de Coropletas

En este capítulo se define la función reutilizable que produce los mapas temáticos estáticos, con una paleta de colores homogénea aplicada de manera consistente a las ocho visualizaciones.

El mapa de coropletas es la representación cartográfica más adecuada para datos de área, ya que asigna una gradación de color proporcional al valor de una variable cuantitativa en unidades poligonales. Se emplea clasificación por cuantiles, método que distribuye de manera equilibrada las comunas entre las cinco clases y facilita la lectura comparativa entre mapas. Todos los mapas usan la misma paleta secuencial YlOrRd (amarillo-naranja-rojo), garantizando coherencia visual y facilitando la comparación directa entre los ocho productos cartográficos.

generar_mapa <- function(shp_sf, conteo_df, titulo) {

  shp_sf$comuna <- as.integer(shp_sf$comuna)
  shp_join      <- left_join(shp_sf, conteo_df, by = "comuna")
  shp_join$n[is.na(shp_join$n)] <- 0

  brks    <- classIntervals(shp_join$n, n = 5, style = "quantile")$brks
  brks    <- unique(brks)
  n_cl    <- length(brks) - 1
  colores <- brewer.pal(max(3, n_cl), pal_coro)[1:n_cl]
  col_asig <- colores[findInterval(shp_join$n, brks, rightmost.closed = TRUE)]

  plot(st_geometry(shp_join),
       col = col_asig, border = "white", lwd = 0.7,
       main = titulo, cex.main = 1.05)

  coords <- st_coordinates(st_centroid(st_geometry(shp_join)))
  text(coords[, 1], coords[, 2],
       labels = shp_join$comuna, cex = 0.55, col = "black", font = 2)

  legend("bottomleft",
         legend = paste0(round(brks[-length(brks)]), " - ", round(brks[-1])),
         fill = colores, title = "N viajes", cex = 0.65, bty = "n")
}

7 Mapas Temáticos de Origen

En este capítulo se presentan los cuatro mapas de origen que muestran la distribución espacial de las comunas generadoras de viajes, tanto de manera general como discriminada por tipo de vehículo.

7.1 Mapa de Origen General

generar_mapa(comunas, origen_general,
             "Origen General - Todos los modos de transporte")

Figura 4. Mapa de coropletas: Origen general de viajes por comuna, Cali 2015

Interpretación: El mapa de origen general revela los patrones de generación de viajes en el conjunto del territorio urbano de Cali. Las comunas que presentan los tonos más intensos concentran la mayor cantidad de viajes originados, independientemente del modo de transporte utilizado. Desde la perspectiva del análisis de datos de área, estas comunas representan las zonas con mayor actividad generadora de desplazamientos, lo cual está estrechamente vinculado con la densidad poblacional, la estructura socioeconómica del territorio y la disponibilidad de transporte. Las comunas periféricas del oriente y suroriente de la ciudad suelen aparecer como importantes generadoras de viajes debido a su alta densidad residencial y a las condiciones de accesibilidad que incentivan el uso de modos alternativos al transporte masivo.

7.2 Mapa de Origen en Bicicleta

generar_mapa(comunas, origen_bicicleta, "Origen - Bicicleta")

Figura 5. Mapa de coropletas: Origen de viajes en bicicleta por comuna, Cali 2015

Interpretación: La distribución espacial de los orígenes en bicicleta muestra una concentración diferenciada respecto al patrón general, lo que sugiere que el uso de este modo no es homogéneo en el territorio. La bicicleta tiende a concentrarse en comunas con topografía relativamente plana, recorridos cortos y en zonas donde la población de ingresos medios-bajos la adopta como alternativa de bajo costo frente al transporte motorizado. La comparación de este mapa con la distribución de ciclovías existentes en la ciudad permite identificar si las comunas con mayor generación de viajes en bicicleta cuentan con infraestructura ciclista adecuada, o si por el contrario existe un déficit de conectividad que expone a los ciclistas a condiciones de menor seguridad vial.

7.3 Mapa de Origen en Moto

generar_mapa(comunas, origen_moto, "Origen - Moto")

Figura 6. Mapa de coropletas: Origen de viajes en moto por comuna, Cali 2015

Interpretación: El mapa de origen en moto pone de manifiesto la penetración de este modo de transporte en el territorio urbano de Cali. La motocicleta ha experimentado un crecimiento sostenido en las ciudades colombianas durante la última década, impulsado por su bajo costo de adquisición y mantenimiento frente al automóvil, así como por su capacidad de sortear la congestión vial. Las comunas con mayor generación de viajes en moto corresponden con frecuencia a zonas de expansión urbana o de menor cobertura del sistema de transporte masivo, donde la moto se convierte en el modo principal de acceso al trabajo y a los servicios.

7.4 Mapa de Origen en Automóvil

generar_mapa(comunas, origen_automovil, "Origen - Automovil")

Figura 7. Mapa de coropletas: Origen de viajes en automóvil por comuna, Cali 2015

Interpretación: El mapa de origen en automóvil refleja la distribución socioeconómica del territorio, dado que la tasa de motorización privada guarda una correlación positiva con el nivel de ingresos del hogar. Las comunas con mayor intensidad de color en este mapa corresponden a sectores de estratos medios y altos donde la posesión y el uso del automóvil particular son más frecuentes. La concentración de orígenes en estas comunas implica que una parte importante de los viajes en automóvil convergen hacia las vías de acceso a las centralidades, lo que genera presión sobre la capacidad vial y contribuye a la congestión en los corredores principales de la ciudad.

8 Mapas Temáticos de Destino

En este capítulo se presentan los cuatro mapas de destino que muestran la distribución espacial de las comunas atractoras de viajes, diferenciadas por tipo de vehículo, complementando el análisis de los patrones de movilidad urbana.

8.1 Mapa de Destino General

generar_mapa(comunas, destino_general,
             "Destino General - Todos los modos de transporte")

Figura 8. Mapa de coropletas: Destino general de viajes por comuna, Cali 2015

Interpretación: El mapa de destino general identifica las comunas que actúan como polos de atracción de viajes dentro de la ciudad. Las zonas con mayor concentración de destinos son aquellas donde se localizan las principales actividades económicas, comerciales, educativas e institucionales de Cali. La comparación entre el mapa de origen general y este mapa de destino permite caracterizar la estructura espacial de la demanda de movilidad: las comunas con alto origen y bajo destino son zonas eminentemente residenciales, mientras que aquellas con alto destino y bajo origen corresponden a las centralidades funcionales de la ciudad.

8.2 Mapa de Destino en Bicicleta

generar_mapa(comunas, destino_bicicleta, "Destino - Bicicleta")

Figura 9. Mapa de coropletas: Destino de viajes en bicicleta por comuna, Cali 2015

Interpretación: Los destinos en bicicleta revelan hacia dónde se desplazan los ciclistas dentro de la ciudad. Una convergencia de destinos hacia comunas con centralidades comerciales o educativas confirma que la bicicleta cumple una función de acceso a actividades cotidianas y no solo de recreación. La identificación de las comunas de mayor atracción de viajes en bicicleta, contrastada con la oferta de infraestructura ciclista disponible en esos puntos de llegada, permite evaluar si la ciudad cuenta con cicloparqueaderos y conexiones seguras en las zonas de mayor demanda ciclista.

8.3 Mapa de Destino en Moto

generar_mapa(comunas, destino_moto, "Destino - Moto")

Figura 10. Mapa de coropletas: Destino de viajes en moto por comuna, Cali 2015

Interpretación: El mapa de destino en moto evidencia las comunas que actúan como polos de llegada para los motociclistas de la ciudad. La concentración de destinos en comunas con alta actividad laboral o comercial indica que la moto se usa principalmente para cubrir desplazamientos de carácter funcional. Al comparar este mapa con el de origen en moto, es posible caracterizar la longitud y dirección de los trayectos predominantes: si los orígenes se concentran en comunas periféricas y los destinos en comunas centrales, se confirma un patrón radial de movilidad que ejerce una presión asimétrica sobre los corredores viales de conexión entre la periferia y el centro.

8.4 Mapa de Destino en Automóvil

generar_mapa(comunas, destino_automovil, "Destino - Automovil")

Figura 11. Mapa de coropletas: Destino de viajes en automóvil por comuna, Cali 2015

Interpretación: El mapa de destino en automóvil muestra las comunas de mayor atracción de viajes en vehículo privado, que corresponden generalmente a zonas de alta concentración de actividad económica y servicios. La superposición entre comunas de alto origen y alto destino en automóvil genera los puntos de mayor congestión vial, ya que la demanda simultánea de entrada y salida sobre los mismos corredores supera con frecuencia la capacidad de la infraestructura disponible. Este mapa es un insumo directo para la implementación de medidas de gestión de la demanda, como zonas de tarifación vial, restricciones de circulación o incentivos al uso del transporte público en los corredores más cargados.

9 Mapas Interactivos Origen-Destino por Tipo de Vehículo

En este capítulo se presentan cuatro mapas interactivos que visualizan los flujos origen-destino entre comunas de Cali para cada tipo de vehículo. Cada mapa muestra los centroides de las comunas como nodos y dibuja líneas de flujo entre los pares origen-destino con mayor número de viajes, permitiendo explorar de manera dinámica la estructura espacial de la movilidad intraurbana. El grosor y la opacidad de cada línea son proporcionales al número de viajes registrados en ese par, y al hacer clic sobre cada nodo o línea se despliega información detallada sobre la comuna y el volumen de viajes.

# ---- Preparar centroides de comunas en WGS84 para Leaflet ------------------
comunas_wgs <- st_transform(comunas, crs = 4326)
comunas_wgs$comuna <- as.integer(comunas_wgs$comuna)

centroides <- st_centroid(comunas_wgs)
coords_c   <- st_coordinates(centroides)

nodos <- data.frame(
  comuna = comunas_wgs$comuna,
  nombre = comunas_wgs$nombre,
  lon    = coords_c[, 1],
  lat    = coords_c[, 2]
)

# ---- Función para construir tabla de flujos OD por modo -------------------
construir_flujos <- function(datos, tipo_veh = NULL, top_n = 40) {
  if (!is.null(tipo_veh)) {
    datos <- datos |> filter(tipo_vehiculo == tipo_veh)
  }
  datos |>
    group_by(comuna_origen, comuna_destino) |>
    summarise(viajes = n(), .groups = "drop") |>
    filter(comuna_origen != comuna_destino) |>
    arrange(desc(viajes)) |>
    slice_head(n = top_n) |>
    left_join(nodos, by = c("comuna_origen" = "comuna")) |>
    rename(lon_orig = lon, lat_orig = lat, nombre_orig = nombre) |>
    left_join(nodos, by = c("comuna_destino" = "comuna")) |>
    rename(lon_dest = lon, lat_dest = lat, nombre_dest = nombre) |>
    filter(!is.na(lon_orig), !is.na(lon_dest))
}

flujos_general   <- construir_flujos(encuesta_cali, tipo_veh = NULL)
flujos_bicicleta <- construir_flujos(encuesta_cali, tipo_veh = 1)
flujos_moto      <- construir_flujos(encuesta_cali, tipo_veh = 2)
flujos_automovil <- construir_flujos(encuesta_cali, tipo_veh = 3)
# ---- Función para generar mapa interactivo leaflet -------------------------
mapa_interactivo <- function(flujos, nodos_df, shp_wgs, color_linea,
                              titulo_popup) {

  max_v  <- max(flujos$viajes, 1)
  escala <- function(v) 1 + (v / max_v) * 8   # grosor entre 1 y 9 px

  # Paleta coropleta para los polígonos de fondo
  pal_poly <- colorNumeric("YlOrRd",
                           domain = c(0, max(nodos_df$lon, na.rm = TRUE)))

  m <- leaflet(options = leafletOptions(zoomControl = TRUE)) |>
    addProviderTiles(providers$CartoDB.Positron) |>

    # Polígonos de comunas como fondo referencial
    addPolygons(
      data        = shp_wgs,
      fillColor   = "#f0f0f0",
      fillOpacity = 0.4,
      color       = "#888888",
      weight      = 1,
      label       = ~paste0("Comuna ", comuna, ": ", nombre),
      labelOptions = labelOptions(style = list("font-size" = "12px"))
    )

  # Dibujar líneas de flujo OD
  for (i in seq_len(nrow(flujos))) {
    fila <- flujos[i, ]
    m <- m |>
      addPolylines(
        lng     = c(fila$lon_orig, fila$lon_dest),
        lat     = c(fila$lat_orig, fila$lat_dest),
        color   = color_linea,
        weight  = escala(fila$viajes),
        opacity = 0.55,
        popup   = paste0(
          "<b>", titulo_popup, "</b><br>",
          "Origen: <b>", fila$nombre_orig, "</b><br>",
          "Destino: <b>", fila$nombre_dest, "</b><br>",
          "Viajes: <b>", fila$viajes, "</b>"
        )
      )
  }

  # Nodos (centroides de comunas)
  m <- m |>
    addCircleMarkers(
      data        = nodos_df,
      lng         = ~lon,
      lat         = ~lat,
      radius      = 6,
      color       = color_linea,
      fillColor   = "white",
      fillOpacity = 1,
      weight      = 2,
      popup       = ~paste0("<b>", nombre, "</b><br>Comuna ", comuna)
    ) |>
    addLegend(
      position = "bottomright",
      colors   = color_linea,
      labels   = "Flujo origen-destino",
      title    = "Tipo de flujo",
      opacity  = 0.8
    )

  return(m)
}

9.1 Mapa Interactivo: Flujos Generales Origen-Destino

mapa_interactivo(
  flujos       = flujos_general,
  nodos_df     = nodos,
  shp_wgs      = comunas_wgs,
  color_linea  = pal_interactivo$general,
  titulo_popup = "Flujo General (todos los modos)"
)

Figura 12. Mapa interactivo: Top 40 flujos origen-destino generales entre comunas de Cali 2015

Interpretación: El mapa interactivo de flujos generales permite explorar los pares origen-destino con mayor volumen de viajes en la ciudad, independientemente del modo de transporte. Las líneas más gruesas y oscuras representan los corredores de mayor demanda de movilidad. Al hacer clic sobre cualquier línea se despliega la información del par de comunas y el número de viajes registrados. La estructura de los flujos revela los principales ejes de movilidad de la ciudad y permite identificar visualmente las centralidades que actúan como polos de atracción dominantes, así como las comunas periféricas que generan los mayores volúmenes de viajes hacia esas centralidades.

9.2 Mapa Interactivo: Flujos Origen-Destino en Bicicleta

mapa_interactivo(
  flujos       = flujos_bicicleta,
  nodos_df     = nodos,
  shp_wgs      = comunas_wgs,
  color_linea  = pal_interactivo$bicicleta,
  titulo_popup = "Flujo en Bicicleta"
)

Figura 13. Mapa interactivo: Top 40 flujos origen-destino en bicicleta entre comunas de Cali 2015

Interpretación: El mapa de flujos en bicicleta muestra los corredores donde este modo de transporte tiene mayor presencia como alternativa de movilidad cotidiana. La concentración de líneas de flujo en determinadas zonas de la ciudad permite identificar los tramos con mayor demanda ciclista, información que resulta fundamental para priorizar la construcción y el mantenimiento de ciclovías. Una alta densidad de flujos en zonas sin infraestructura ciclista señala una brecha entre la demanda real y la oferta de infraestructura, situación que expone a los ciclistas a condiciones de riesgo vial que deben ser atendidas desde la planificación urbana.

9.3 Mapa Interactivo: Flujos Origen-Destino en Moto

mapa_interactivo(
  flujos       = flujos_moto,
  nodos_df     = nodos,
  shp_wgs      = comunas_wgs,
  color_linea  = pal_interactivo$moto,
  titulo_popup = "Flujo en Moto"
)

Figura 14. Mapa interactivo: Top 40 flujos origen-destino en moto entre comunas de Cali 2015

Interpretación: Los flujos en moto evidencian la estructura de movilidad de uno de los modos de mayor crecimiento en la ciudad. La direccionalidad de las líneas permite confirmar o descartar la hipótesis de un patrón radial periférico-central, en el que las comunas de estratos bajos ubicadas en la periferia generan viajes hacia las centralidades económicas y comerciales. La identificación de los corredores con mayor intensidad de flujos en moto es un insumo directo para la gestión de la velocidad, el diseño de intersecciones seguras y la planificación de operativos de control de tráfico en los ejes de mayor siniestralidad.

9.4 Mapa Interactivo: Flujos Origen-Destino en Automóvil

mapa_interactivo(
  flujos       = flujos_automovil,
  nodos_df     = nodos,
  shp_wgs      = comunas_wgs,
  color_linea  = pal_interactivo$automovil,
  titulo_popup = "Flujo en Automovil"
)

Figura 15. Mapa interactivo: Top 40 flujos origen-destino en automóvil entre comunas de Cali 2015

Interpretación: El mapa de flujos en automóvil revela los corredores donde la demanda de vehículo privado es más intensa, lo cual se traduce directamente en los puntos de mayor congestión vial de la ciudad. La superposición de múltiples líneas de flujo en determinados corredores indica que esas vías soportan simultáneamente varios pares origen-destino de alta demanda, generando una presión acumulada sobre la capacidad vial que justifica intervenciones de gestión del tráfico. Este mapa, combinado con los de bicicleta y moto, permite construir una visión integral de la demanda multimodal de movilidad en Cali y orientar las políticas de transporte hacia un equilibrio más sostenible entre los distintos modos.

10 Conclusiones

En este capítulo se sintetizan los hallazgos del análisis espacial y se plantean las implicaciones para la planificación de la movilidad urbana en Cali.

Los ocho mapas de coropletas y los cuatro mapas interactivos origen-destino generados a partir de la Encuesta Origen-Destino de Cali 2015 constituyen una herramienta integral de diagnóstico espacial que permite identificar, de manera visual y sistemática, los patrones de movilidad urbana distribuidos en las 22 comunas de la ciudad. El proceso metodológico seguido ilustra de manera práctica el principio fundamental de los SIG: la integración de datos espaciales (el shapefile de comunas) con datos temáticos (los atributos de la encuesta) para generar nueva información con valor analítico y de apoyo a la toma de decisiones.

La lectura comparada de los mapas de origen y destino por tipo de vehículo permite identificar asimetrías territoriales que son claves para la planificación: la bicicleta demanda revisión de la conectividad ciclista entre sus zonas de origen y destino más frecuentes; la moto presenta un patrón radial que concentra la demanda sobre corredores específicos de conexión periferia-centro; y el automóvil revela la concentración de la motorización en comunas de estratos medios y altos, con las externalidades de congestión que ello conlleva.

Los mapas interactivos complementan el análisis estático al permitir explorar de forma dinámica los pares origen-destino dominantes para cada modo, identificar visualmente los corredores de mayor intensidad de flujo y descubrir patrones de movilidad que no son evidentes en las representaciones agregadas por comuna. Esta capacidad de exploración interactiva representa el valor agregado que los SIG modernos aportan a la planificación del transporte urbano, al acercar el análisis de datos complejos a los tomadores de decisiones de manera intuitiva y accesible.

Desde el punto de vista conceptual, el análisis se enmarca dentro del análisis de datos de área, en el que la unidad de observación es la comuna como unidad administrativa poligonal. Esta elección de escala es apropiada para el diagnóstico general de la movilidad, aunque no captura la variabilidad intracomuna. Los resultados deben interpretarse como una aproximación a la demanda agregada de movilidad en 2015, reconociendo que las condiciones urbanas de la ciudad han podido evolucionar desde entonces.

LS0tDQp0aXRsZTogIioqTTFVMSAtIENhc286IEVuY3Vlc3RhIE9yaWdlbiBEZXN0aW5vIGRlIGxhIENpdWRhZCBkZSBDYWxpKioiDQphdXRob3I6ICJSYWZhZWwgSm9zZSBEZWwgQ2FzdGlsbG8gUGF2YWplYXUiDQpkYXRlOiAiMjAyNi0wNS0xOCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IHRydWUNCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICB0aGVtZTogY29zbW8NCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQpsYW5ndWFnZToNCiAgbGFiZWw6DQogICAgZmlnOiAiRmlndXJhICINCiAgICB0YWI6ICJUYWJsYSAiDQpwYXJhbXM6DQogIHNlZWQ6IDg5NzI3OTENCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgZWNobyAgICAgID0gVFJVRSwNCiAgbWVzc2FnZSAgID0gRkFMU0UsDQogIHdhcm5pbmcgICA9IEZBTFNFLA0KICBmaWcuYWxpZ24gPSAiY2VudGVyIg0KKQ0KDQojIENvbnRhZG9yIGdsb2JhbCBkZSBmaWd1cmFzIHkgdGFibGFzDQpmaWdfbiA8LSAwDQp0YWJfbiA8LSAwDQoNCiMgRnVuY2lvbmVzIGF1eGlsaWFyZXMgcGFyYSBudW1lcmFjacOzbiBhdXRvbcOhdGljYQ0KbnVldmFfZmlndXJhIDwtIGZ1bmN0aW9uKHRpdHVsbykgew0KICBmaWdfbiA8PC0gZmlnX24gKyAxDQogIHBhc3RlMCgiKipGaWd1cmEgIiwgZmlnX24sICIuKiogIiwgdGl0dWxvKQ0KfQ0KDQpudWV2YV90YWJsYSA8LSBmdW5jdGlvbih0aXR1bG8pIHsNCiAgdGFiX24gPDwtIHRhYl9uICsgMQ0KICBwYXN0ZTAoIioqVGFibGEgIiwgdGFiX24sICIuKiogIiwgdGl0dWxvKQ0KfQ0KYGBgDQoNCmBgYHtyIGZ1ZW50ZV9nbG9iYWwsIGluY2x1ZGU9RkFMU0V9DQpodG1sdG9vbHM6OnRhZ3Mkc3R5bGUoDQogICJAaW1wb3J0IHVybCgnaHR0cHM6Ly9mb250cy5nb29nbGVhcGlzLmNvbS9jc3MyP2ZhbWlseT1PdXRmaXQ6d2dodEAzMDA7NDAwOzYwMDs3MDAmZGlzcGxheT1zd2FwJyk7DQogICBib2R5LCBoMSwgaDIsIGgzLCBoNCwgcCwgbGksIHRkLCB0aCwgLmNhcHRpb24gew0KICAgICBmb250LWZhbWlseTogJ091dGZpdCcsICdBcHRvcyBEaXNwbGF5JywgJ1NlZ29lIFVJJywgQXJpYWwsIHNhbnMtc2VyaWYgIWltcG9ydGFudDsNCiAgIH0iDQopDQpgYGANCg0KPHN0eWxlPg0KQGltcG9ydCB1cmwoJ2h0dHBzOi8vZm9udHMuZ29vZ2xlYXBpcy5jb20vY3NzMj9mYW1pbHk9T3V0Zml0OndnaHRAMzAwOzQwMDs2MDA7NzAwJmRpc3BsYXk9c3dhcCcpOw0KYm9keSwgaDEsIGgyLCBoMywgaDQsIHAsIGxpLCB0ZCwgdGgsIC5jYXB0aW9uLCBjb2RlLCBwcmUgew0KICBmb250LWZhbWlseTogJ091dGZpdCcsICdBcHRvcyBEaXNwbGF5JywgJ1NlZ29lIFVJJywgQXJpYWwsIHNhbnMtc2VyaWYgIWltcG9ydGFudDsNCn0NCmgxIHsgZm9udC13ZWlnaHQ6IDcwMDsgfQ0KPC9zdHlsZT4NCg0KIyAqKkludHJvZHVjY2nDs24qKg0KDQpFbiBlc3RlIGNhcMOtdHVsbyBzZSBwcmVzZW50YSBlbCBjb250ZXh0byBjb25jZXB0dWFsIGRlbCBjYXNvIHkgc2UgZGVzY3JpYmUgZWwgY29uanVudG8gZGUgZGF0b3MgdXRpbGl6YWRvLCBjb24gZWwgZmluIGRlIGVubWFyY2FyIGVsIGFuw6FsaXNpcyBkZW50cm8gZGUgbG9zIGZ1bmRhbWVudG9zIGRlIGxvcyBTaXN0ZW1hcyBkZSBJbmZvcm1hY2nDs24gR2VvZ3LDoWZpY2EgKFNJRykuDQoNClVuIFNpc3RlbWEgZGUgSW5mb3JtYWNpw7NuIEdlb2dyw6FmaWNhIGVzIHVuYSB0ZWNub2xvZ8OtYSBkZSBtYW5lam8gZGUgaW5mb3JtYWNpw7NuIGdlb2dyw6FmaWNhIHF1ZSBjb21iaW5hIGNpbmNvIGNvbXBvbmVudGVzIGVzZW5jaWFsZXM6IHBlcnNvbmFzIGVzcGVjaWFsaXphZGFzLCBkYXRvcyBkZXNjcmlwdGl2b3MgeSBlc3BhY2lhbGVzLCBtw6l0b2RvcyBhbmFsw610aWNvcywgaGFyZHdhcmUgeSBzb2Z0d2FyZSwgdG9kb3Mgb3JnYW5pemFkb3MgcGFyYSBhbmFsaXphciwgbWFuaXB1bGFyLCBwcm9jZXNhciwgYWxtYWNlbmFyLCBnZW5lcmFyIHkgdmlzdWFsaXphciBpbmZvcm1hY2nDs24gcmVmZXJlbmNpYWRhIGdlb2dyw6FmaWNhbWVudGUuIEVuIGVzZSBzZW50aWRvLCB1biBwcm95ZWN0byBTSUcgc2UgY29uc3RydXllIHBhcmEgYWxtYWNlbmFyIHkgcmVjdXBlcmFyIGRhdG9zLCB2aXN1YWxpemFyIGluZm9ybWFjacOzbiBkZSBjYW1wbywgaWRlbnRpZmljYXIgcGF0cm9uZXMgZXNwYWNpYWxlcywgZGVzY3JpYmlyIGNhcmFjdGVyw61zdGljYXMgZGUgb2JqZXRvcyB5IHN1cGVyZmljaWVzLCBhbmFsaXphciBkYXRvcyBlc3BhY2lhbGVzIHkgZ2VuZXJhciBudWV2YSBpbmZvcm1hY2nDs24gcXVlIGFwb3llIGxhIHRvbWEgZGUgZGVjaXNpb25lcy4NCg0KTGEgRW5jdWVzdGEgT3JpZ2VuLURlc3Rpbm8gZGUgQ2FsaSBkZWwgYcOxbyAyMDE1IGNvbnN0aXR1eWUgdW5hIGZ1ZW50ZSBkZSBkYXRvcyBzZWN1bmRhcmlhIGVuIGVsIG1hcmNvIGRlbCBTSUcsIHlhIHF1ZSBwcm92aWVuZSBkZSB1biBpbnN0cnVtZW50byBkZSByZWNvbGVjY2nDs24gZXh0ZXJuYSBxdWUgcmVnaXN0cmEgbG9zIHRyYXllY3RvcyByZWFsaXphZG9zIHBvciBwZXJzb25hcyBlbiBsYSBjaXVkYWQuIExhIGluZm9ybWFjacOzbiBkZSBhdHJpYnV0b3MgY29udGVuaWRhIGVuIGxhIGhvamEgZGUgY8OhbGN1bG8gc2UgaW50ZWdyYSBjb24gbGEgY2FydG9ncmFmw61hIHZlY3RvcmlhbCBkZSBjb211bmFzIG1lZGlhbnRlIHVuIHByb2Nlc28gZGUgKmpvaW4qIGVzcGFjaWFsLXRhYnVsYXIsIGxvIHF1ZSBwZXJtaXRlIGdlbmVyYXIgbnVldmEgaW5mb3JtYWNpw7NuIGdlb3JyZWZlcmVuY2lhZGEgw7p0aWwgcGFyYSBsYSBwbGFuaWZpY2FjacOzbiBkZSBsYSBtb3ZpbGlkYWQgdXJiYW5hLg0KDQpFbCBwcmVzZW50ZSBkb2N1bWVudG8gZ2VuZXJhICoqOCBtYXBhcyB0ZW3DoXRpY29zIGRlIGNvcm9wbGV0YXMqKiB5ICoqNCBtYXBhcyBpbnRlcmFjdGl2b3Mgb3JpZ2VuLWRlc3Rpbm8qKiBvcmdhbml6YWRvcyBwb3IgdGlwbyBkZSB2ZWjDrWN1bG8gKGJpY2ljbGV0YSwgbW90bywgYXV0b23Ds3ZpbCB5IGdlbmVyYWwpLCBzaWd1aWVuZG8gbGEgbWV0b2RvbG9nw61hIGRlIGFuw6FsaXNpcyBkZSBkYXRvcyBkZSDDoXJlYSBwcm9waWEgZGUgbG9zIFNJRy4NCg0KIyAqKkluc3RhbGFjacOzbiB5IENhcmdhIGRlIExpYnJlcsOtYXMqKg0KDQpFbiBlc3RlIGNhcMOtdHVsbyBzZSBpbnN0YWxhbiB5IGNhcmdhbiB0b2RhcyBsYXMgbGlicmVyw61hcyBuZWNlc2FyaWFzIHBhcmEgZWplY3V0YXIgZWwgYW7DoWxpc2lzIGVzcGFjaWFsLCBlbCBwcm9jZXNhbWllbnRvIGRlIGRhdG9zLCBsYSBnZW5lcmFjacOzbiBkZSBsb3MgbWFwYXMgdGVtw6F0aWNvcyBlc3TDoXRpY29zIHkgbG9zIG1hcGFzIGludGVyYWN0aXZvcyBvcmlnZW4tZGVzdGluby4NCg0KYGBge3IgbGlicmVyaWFzfQ0KIyAtLS0tIEluc3RhbGFjacOzbiAoZWplY3V0YXIgc29sbyBzaSBsb3MgcGFxdWV0ZXMgbm8gZXN0w6FuIGRpc3BvbmlibGVzKSAtLS0tLS0tDQpwYXF1ZXRlcyA8LSBjKCJyZWFkeGwiLCAic2YiLCAiZHBseXIiLCAiUkNvbG9yQnJld2VyIiwNCiAgICAgICAgICAgICAgImNsYXNzSW50IiwgImtuaXRyIiwgImthYmxlRXh0cmEiLA0KICAgICAgICAgICAgICAibGVhZmxldCIsICJsZWFmbGV0LmV4dHJhcyIpDQoNCmluc3RhbGFkb3MgPC0gcGFxdWV0ZXNbIShwYXF1ZXRlcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywgIlBhY2thZ2UiXSldDQppZiAobGVuZ3RoKGluc3RhbGFkb3MpID4gMCkgew0KICBpbnN0YWxsLnBhY2thZ2VzKGluc3RhbGFkb3MsIHJlcG9zID0gImh0dHBzOi8vY2xvdWQuci1wcm9qZWN0Lm9yZyIpDQp9DQoNCiMgLS0tLSBDYXJnYSBkZSBsaWJyZXLDrWFzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpsaWJyYXJ5KHJlYWR4bCkgICAgICAgICAgIyBMZWN0dXJhIGRlIGFyY2hpdm9zIEV4Y2VsICgueGxzeCkNCmxpYnJhcnkoc2YpICAgICAgICAgICAgICAjIERhdG9zIGVzcGFjaWFsZXMgdmVjdG9yaWFsZXMgKHN1Y2Vzb3IgZGUgc3AvcmdkYWwpDQpsaWJyYXJ5KGRwbHlyKSAgICAgICAgICAgIyBNYW5pcHVsYWNpw7NuIHkgdHJhbnNmb3JtYWNpw7NuIGRlIGRhdG9zIHRhYnVsYXJlcw0KbGlicmFyeShSQ29sb3JCcmV3ZXIpICAgICMgUGFsZXRhcyBkZSBjb2xvcmVzIHNlY3VlbmNpYWxlcyBwYXJhIG1hcGFzDQpsaWJyYXJ5KGNsYXNzSW50KSAgICAgICAgIyBDbGFzaWZpY2FjacOzbiBkZSB2YXJpYWJsZXMgZW4gaW50ZXJ2YWxvcw0KbGlicmFyeShrbml0cikgICAgICAgICAgICMgVGFibGFzIGZvcm1hdGVhZGFzIGVuIFIgTWFya2Rvd24NCmxpYnJhcnkoa2FibGVFeHRyYSkgICAgICAjIEVzdGlsb3MgYWRpY2lvbmFsZXMgcGFyYSB0YWJsYXMga2FibGUNCmxpYnJhcnkobGVhZmxldCkgICAgICAgICAjIE1hcGFzIGludGVyYWN0aXZvcyBjb24gT3BlblN0cmVldE1hcA0KbGlicmFyeShsZWFmbGV0LmV4dHJhcykgICMgRnVuY2lvbmVzIGFkaWNpb25hbGVzIHBhcmEgbGVhZmxldCAoaGVhdG1hcCwgZXRjLikNCmBgYA0KDQojICoqQ2FyZ2EgeSBQcmVwYXJhY2nDs24gZGUgRGF0b3MqKg0KDQpFbiBlc3RlIGNhcMOtdHVsbyBzZSBjYXJnYW4gbG9zIGRhdG9zIGdlb2dyw6FmaWNvcyB5IHRhYnVsYXJlcywgc2UgcmV2aXNhIHN1IGVzdHJ1Y3R1cmEgeSBzZSByZWFsaXphIGVsIGZpbHRyYWRvIG5lY2VzYXJpbyBwYXJhIGNvbnNlcnZhciDDum5pY2FtZW50ZSBsb3MgdmlhamVzIGludHJhdXJiYW5vcyBkZSBsYSBjaXVkYWQgZGUgQ2FsaS4NCg0KIyMgU2hhcGVmaWxlIGRlIENvbXVuYXMgZGUgQ2FsaQ0KDQpMYSBjYXJ0b2dyYWbDrWEgZGUgY29tdW5hcyBjb3JyZXNwb25kZSBhIHVuIG1vZGVsbyBkZSBkYXRvcyB2ZWN0b3JpYWwgZGUgcG9sw61nb25vcy4gTG9zIHBvbMOtZ29ub3Mgc29uIGVudGlkYWRlcyBiaWRpbWVuc2lvbmFsZXMgcXVlIHRpZW5lbiBsYXMgcHJvcGllZGFkZXMgZGUgw6FyZWEgeSBwZXLDrW1ldHJvLCB5IHNvbiBsYSBwcmltaXRpdmEgZ2VvbcOpdHJpY2EgYWRlY3VhZGEgcGFyYSByZXByZXNlbnRhciB1bmlkYWRlcyBhZG1pbmlzdHJhdGl2YXMgY29tbyBsYXMgY29tdW5hcy4gRWwgc2hhcGVmaWxlIGFncnVwYSBsb3MgYXJjaGl2b3MgYC5zaHBgLCBgLmRiZmAsIGAuc2h4YCB5IGAucHJqYCwgcXVlIGNvbnRpZW5lbiByZXNwZWN0aXZhbWVudGUgbGEgZ2VvbWV0csOtYSwgbG9zIGF0cmlidXRvcywgbG9zIMOtbmRpY2VzIHkgZWwgc2lzdGVtYSBkZSByZWZlcmVuY2lhIGRlIGNvb3JkZW5hZGFzLiBMYSBsZWN0dXJhIHNlIHJlYWxpemEgY29uIGVsIHBhcXVldGUgYHNmYCBtZWRpYW50ZSBsYSBmdW5jacOzbiBgc3RfcmVhZCgpYCwgcXVlIGVzIGVsIGVzdMOhbmRhciBtb2Rlcm5vIGVuIFIgcGFyYSBlbCBtYW5lam8gZGUgZGF0b3MgZXNwYWNpYWxlcyB2ZWN0b3JpYWxlcy4NCg0KYGBge3Igc2hhcGVmaWxlfQ0KIyBMZWN0dXJhIGRlbCBzaGFwZWZpbGUgZGUgY29tdW5hcw0KY29tdW5hcyA8LSBzdF9yZWFkKA0KICAiQzovUkRDL0FuYWxpdGljYV9HZW9lc3BhY2lhbC9DYXNvcy9jYWxpL0NvbXVuYXMuc2hwIiwNCiAgcXVpZXQgPSBUUlVFDQopDQoNCmhlYWQoY29tdW5hcykNCmBgYA0KDQpgciBudWV2YV90YWJsYSgiRXN0cnVjdHVyYSBkZSBsYSB0YWJsYSBkZSBhdHJpYnV0b3MgZGVsIHNoYXBlZmlsZSBkZSBDb211bmFzIGRlIENhbGkiKWANCg0KYGBge3IgdGFibGFfc2hhcGVmaWxlfQ0Ka2FibGUoDQogIGhlYWQoc3RfZHJvcF9nZW9tZXRyeShjb211bmFzKSksDQogIGNhcHRpb24gPSBOVUxMLA0KICBhbGlnbiAgID0gImMiDQopIHw+DQogIGthYmxlX3N0eWxpbmcoDQogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLA0KICAgIGZ1bGxfd2lkdGggICAgICAgID0gRkFMU0UsDQogICAgZm9udF9zaXplICAgICAgICAgPSAxMw0KICApDQpgYGANCg0KKipJbnRlcnByZXRhY2nDs246KiogTGEgdGFibGEgZGUgYXRyaWJ1dG9zIGRlbCBzaGFwZWZpbGUgY29udGllbmUgY3VhdHJvIGNhbXBvczogYE9CSkVDVElEYCwgYGdpZGAsIGBjb211bmFgIChpZGVudGlmaWNhZG9yIG51bcOpcmljbyBkZWwgMSBhbCAyMikgeSBgbm9tYnJlYC4gRWwgY2FtcG8gYGNvbXVuYWAgZXMgZWwgdsOtbmN1bG8gcXVlIHBlcm1pdGUgZW5sYXphciBsb3MgZGF0b3MgdGFidWxhcmVzIGRlIGxhIGVuY3Vlc3RhIGNvbiBsYSBnZW9tZXRyw61hIGVzcGFjaWFsLiBMYSBleGlzdGVuY2lhIGRlIGxhcyAyMiBjb211bmFzIGNvbmZpcm1hIHF1ZSBsYSBjYXJ0b2dyYWbDrWEgY3VicmUgbGEgdG90YWxpZGFkIGRlbCDDoXJlYSB1cmJhbmEgZGUgQ2FsaSwgbG8gY3VhbCBlcyBjb25kaWNpw7NuIG5lY2VzYXJpYSBwYXJhIHF1ZSBsb3MgbWFwYXMgdGVtw6F0aWNvcyByZXByZXNlbnRlbiBlbCB0ZXJyaXRvcmlvIGRlIG1hbmVyYSBjb21wbGV0YSB5IHNpbiB2YWPDrW9zIGRlIGluZm9ybWFjacOzbi4NCg0KIyMgRW5jdWVzdGEgT3JpZ2VuLURlc3Rpbm8NCg0KTGEgRW5jdWVzdGEgT3JpZ2VuLURlc3Rpbm8gZXMgdW5hIGZ1ZW50ZSBkZSBkYXRvcyBzZWN1bmRhcmlhIGVuIGZvcm1hdG8gZGUgaG9qYSBkZSBjw6FsY3Vsby4gTG9zIGRhdG9zIGRlIGF0cmlidXRvcyBpbmdyZXNhbiBhbCBTSUcgYSB0cmF2w6lzIGRlIGxhIGhvamEgZGUgY8OhbGN1bG8geSBzZSBlbmxhemFuIGNvbiBsYXMgZW50aWRhZGVzIGdyw6FmaWNhcyBkZWwgc2hhcGVmaWxlIG1lZGlhbnRlIGVsIGNhbXBvIGRlIGNvbXVuYSBjb21wYXJ0aWRvLg0KDQpgYGB7ciBleGNlbH0NCiMgTGVjdHVyYSBkZWwgYXJjaGl2byBFeGNlbA0KZW5jdWVzdGEgPC0gcmVhZF9leGNlbCgNCiAgcGF0aCAgPSAiQzovUkRDL0FuYWxpdGljYV9HZW9lc3BhY2lhbC9DYXNvcy9FbmN1ZXN0YU9yaWdlbkRlc3Rpbm8ueGxzeCIsDQogIHNoZWV0ID0gIkhvamExIg0KKQ0KDQpjYXQoIk5vbWJyZXMgZGUgY29sdW1uYXMgbGVpZG9zIHBvciBSOlxuIikNCnByaW50KGNvbG5hbWVzKGVuY3Vlc3RhKSkNCmBgYA0KDQpgYGB7ciByZW5hbWVfY29sc30NCiMgUmVub21icmFyIGNvbHVtbmFzIGNsYXZlDQplbmN1ZXN0YSA8LSBlbmN1ZXN0YSB8Pg0KICByZW5hbWUoDQogICAgY29tdW5hX29yaWdlbiAgPSBgY29tdW5hIG9yaWdlbmAsDQogICAgY29tdW5hX2Rlc3Rpbm8gPSBgY29tdW5hIGRlc3Rpbm9gLA0KICAgIHRpcG9fdmVoaWN1bG8gID0gYFRJUE8gREUgVkVIw41DVUxPYA0KICApDQoNCiMgU2kgbG9zIG5vbWJyZXMgbm8gdGllbmVuIGVzcGFjaW9zLCB1c2FyIGVzdGEgYWx0ZXJuYXRpdmE6DQojIGVuY3Vlc3RhIDwtIGVuY3Vlc3RhIHw+DQojICAgcmVuYW1lKA0KIyAgICAgY29tdW5hX29yaWdlbiAgPSBgY29tdW5hb3JpZ2VuYCwNCiMgICAgIGNvbXVuYV9kZXN0aW5vID0gYGNvbXVuYWRlc3Rpbm9gLA0KIyAgICAgdGlwb192ZWhpY3VsbyAgPSBgVElQT0RFVkVIw41DVUxPYA0KIyAgICkNCg0KZ2xpbXBzZShlbmN1ZXN0YSkNCmBgYA0KDQpgciBudWV2YV90YWJsYSgiUHJpbWVyYXMgZmlsYXMgZGUgbGEgRW5jdWVzdGEgT3JpZ2VuLURlc3Rpbm8gZGUgQ2FsaSAyMDE1IilgDQoNCmBgYHtyIHRhYmxhX2VuY3Vlc3RhfQ0KY29sc190YWJsYSA8LSBjKCJjb211bmFfb3JpZ2VuIiwgImNvbXVuYV9kZXN0aW5vIiwgInRpcG9fdmVoaWN1bG8iLA0KICAgICAgICAgICAgICAgICJFU1RSQVRPIEVOIFNVIFZJVklFTkRBIiwgIkVEQUQiLCAiU0VYTyIpDQpjb2xzX3RhYmxhIDwtIGNvbHNfdGFibGFbY29sc190YWJsYSAlaW4lIGNvbG5hbWVzKGVuY3Vlc3RhKV0NCg0Ka2FibGUoDQogIGhlYWQoZW5jdWVzdGFbLCBjb2xzX3RhYmxhXSwgOCksDQogIGNhcHRpb24gPSBOVUxMLA0KICBhbGlnbiAgID0gImMiDQopIHw+DQogIGthYmxlX3N0eWxpbmcoDQogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLA0KICAgIGZ1bGxfd2lkdGggICAgICAgID0gRkFMU0UsDQogICAgZm9udF9zaXplICAgICAgICAgPSAxMg0KICApIHw+DQogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpDQpgYGANCg0KKipJbnRlcnByZXRhY2nDs246KiogTGEgZW5jdWVzdGEgcmVnaXN0cmEgdmFyaWFibGVzIGVzcGFjaWFsZXMgKGNvbXVuYXMgZGUgb3JpZ2VuIHkgZGVzdGlubyksIHNvY2lvZWNvbsOzbWljYXMgKGVzdHJhdG8sIHNleG8pIHkgZGUgbW92aWxpZGFkICh0aXBvIGRlIHZlaMOtY3VsbywgcHJvcMOzc2l0byBkZWwgdmlhamUpLiBFc3RhIHJpcXVlemEgZGUgYXRyaWJ1dG9zIGVzIGNhcmFjdGVyw61zdGljYSBkZSB1bmEgZnVlbnRlIHNlY3VuZGFyaWEgcm9idXN0YTogbGEgaW5mb3JtYWNpw7NuIGdlb2dyw6FmaWNhIHBvc2VlIGxvcyB0cmVzIGF0cmlidXRvcyBlc2VuY2lhbGVzIGRlZmluaWRvcyBwb3IgbG9zIFNJRywgZWwgY29tcG9uZW50ZSBlc3BhY2lhbCAobGEgbG9jYWxpemFjacOzbiBwb3IgY29tdW5hKSwgZWwgY29tcG9uZW50ZSB0ZW3DoXRpY28gKGxhcyBjYXJhY3RlcsOtc3RpY2FzIGRlbCB2aWFqZSB5IGRlbCB2aWFqZXJvKSB5IGVsIGNvbXBvbmVudGUgdGVtcG9yYWwgKGVsIGHDsW8gZGUgbGV2YW50YW1pZW50bywgMjAxNSkuIExhIGNvZGlmaWNhY2nDs24gZGVsIHRpcG8gZGUgdmVow61jdWxvIGVuIHZhbG9yZXMgZW50ZXJvcyAoMSwgMiwgMykgcGVybWl0ZSByZWFsaXphciBmaWx0cm9zIGVmaWNpZW50ZXMgZHVyYW50ZSBlbCBhbsOhbGlzaXMuDQoNCiMjIEZpbHRyYWRvOiBWaWFqZXMgSW50cmF1cmJhbm9zIGRlIENhbGkNCg0KYGBge3IgZmlsdHJhZG99DQplbmN1ZXN0YV9jYWxpIDwtIGVuY3Vlc3RhIHw+DQogIGZpbHRlcigNCiAgICAhaXMubmEoY29tdW5hX29yaWdlbiksDQogICAgY29tdW5hX29yaWdlbiAgIT0gIkZ1ZXJhIGRlIENhbGkiLA0KICAgICFpcy5uYShjb211bmFfZGVzdGlubyksDQogICAgY29tdW5hX2Rlc3Rpbm8gIT0gIkZ1ZXJhIGRlIENhbGkiDQogICkgfD4NCiAgbXV0YXRlKA0KICAgIGNvbXVuYV9vcmlnZW4gID0gYXMuaW50ZWdlcihjb211bmFfb3JpZ2VuKSwNCiAgICBjb211bmFfZGVzdGlubyA9IGFzLmludGVnZXIoY29tdW5hX2Rlc3Rpbm8pLA0KICAgIHRpcG9fdmVoaWN1bG8gID0gYXMuaW50ZWdlcih0aXBvX3ZlaGljdWxvKQ0KICApDQoNCnRvdGFsX2dlbmVyYWwgIDwtIG5yb3coZW5jdWVzdGEpDQp0b3RhbF9pbnRyYSAgICA8LSBucm93KGVuY3Vlc3RhX2NhbGkpDQp0b3RhbF9leGNsdWlkbyA8LSB0b3RhbF9nZW5lcmFsIC0gdG90YWxfaW50cmENCg0KY2F0KCJSZWdpc3Ryb3MgdG90YWxlcyAgICAgICA6IiwgdG90YWxfZ2VuZXJhbCwgICJcbiIpDQpjYXQoIlZpYWplcyBpbnRyYXVyYmFub3MgQ2FsaToiLCB0b3RhbF9pbnRyYSwgICAgICJcbiIpDQpjYXQoIlJlZ2lzdHJvcyBleGNsdWlkb3MgICAgIDoiLCB0b3RhbF9leGNsdWlkbywgIlxuIikNCmBgYA0KDQpgciBudWV2YV90YWJsYSgiUmVzdW1lbiBkZWwgZmlsdHJhZG8gZGUgcmVnaXN0cm9zIGRlIGxhIEVuY3Vlc3RhIE9yaWdlbi1EZXN0aW5vIilgDQoNCmBgYHtyIHRhYmxhX2ZpbHRyYWRvfQ0KcmVzdW1lbl9maWx0cm8gPC0gZGF0YS5mcmFtZSgNCiAgQ2F0ZWdvcmlhICA9IGMoIlJlZ2lzdHJvcyB0b3RhbGVzIGVuIGxhIGVuY3Vlc3RhIiwNCiAgICAgICAgICAgICAgICAgIlZpYWplcyBpbnRyYXVyYmFub3MgZGUgQ2FsaSAoYW5hbGlzaXMpIiwNCiAgICAgICAgICAgICAgICAgIlJlZ2lzdHJvcyBleGNsdWlkb3MgKGludGVybXVuaWNpcGFsZXMgbyBzaW4gZGF0b3MpIiksDQogIENhbnRpZGFkICAgPSBjKHRvdGFsX2dlbmVyYWwsIHRvdGFsX2ludHJhLCB0b3RhbF9leGNsdWlkbyksDQogIFBvcmNlbnRhamUgPSBwYXN0ZTAocm91bmQoYyh0b3RhbF9nZW5lcmFsLCB0b3RhbF9pbnRyYSwgdG90YWxfZXhjbHVpZG8pIC8NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3RhbF9nZW5lcmFsICogMTAwLCAxKSwgIiAlIikNCikNCg0Ka2FibGUocmVzdW1lbl9maWx0cm8sIGNhcHRpb24gPSBOVUxMLCBhbGlnbiA9IGMoImwiLCAiciIsICJyIikpIHw+DQogIGthYmxlX3N0eWxpbmcoDQogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLA0KICAgIGZ1bGxfd2lkdGggICAgICAgID0gRkFMU0UsDQogICAgZm9udF9zaXplICAgICAgICAgPSAxMw0KICApDQpgYGANCg0KKipJbnRlcnByZXRhY2nDs246KiogRWwgcHJvY2VzbyBkZSBmaWx0cmFkbyBkZWxpbWl0YSBlbCB1bml2ZXJzbyBkZSBhbsOhbGlzaXMgYSBsb3MgdmlhamVzIGN1eW9zIGRvcyBleHRyZW1vcywgb3JpZ2VuIHkgZGVzdGlubywgc2UgZW5jdWVudHJhbiBkZW50cm8gZGVsIHBlcsOtbWV0cm8gdXJiYW5vIGRlIENhbGkuIExvcyByZWdpc3Ryb3MgY29uIGxhIGV0aXF1ZXRhICJGdWVyYSBkZSBDYWxpIiBjb3JyZXNwb25kZW4gYSB0cmF5ZWN0b3MgaW50ZXJtdW5pY2lwYWxlcyBxdWUgaW5jbHV5ZW4gbXVuaWNpcGlvcyBkZWwgw4FyZWEgTWV0cm9wb2xpdGFuYSBjb21vIFl1bWJvLCBQYWxtaXJhLCBKYW11bmTDrSB5IENhbmRlbGFyaWE7IGVzdG9zIHZpYWplcyBubyBzb24gcmVwcmVzZW50YWJsZXMgZW4gbGEgY2FydG9ncmFmw61hIGNvbXVuYWwgZGUgbGEgY2l1ZGFkIHkgc2UgZXhjbHV5ZW4gZGVsIGFuw6FsaXNpcyB0ZW3DoXRpY28uIEVzdGEgZGVjaXNpw7NuIG1ldG9kb2zDs2dpY2EgZXMgY29oZXJlbnRlIGNvbiBlbCBvYmpldGl2byBkZWwgZW50cmVnYWJsZSwgcXVlIHNlIGNlbnRyYSBlbiBsYSBtb3ZpbGlkYWQgaW50cmF1cmJhbmEuDQoNCiMgKipBbsOhbGlzaXMgRXhwbG9yYXRvcmlvIGRlIGxvcyBEYXRvcyoqDQoNCkVuIGVzdGUgY2Fww610dWxvIHNlIGV4cGxvcmFuIGxhcyBkaXN0cmlidWNpb25lcyBkZSBsYXMgdmFyaWFibGVzIHByaW5jaXBhbGVzIGRlIGxhIGVuY3Vlc3RhIHBhcmEgY29tcHJlbmRlciBsYSBjb21wb3NpY2nDs24gZGUgbG9zIHZpYWplcyBhbnRlcyBkZSBnZW5lcmFyIGxvcyBtYXBhcyB0ZW3DoXRpY29zLg0KDQpgYGB7ciBwYWxldHRlX2dsb2JhbCwgaW5jbHVkZT1GQUxTRX0NCnBhbF9wcmluY2lwYWwgPC0gIiMyQzdCQjYiDQpwYWxfYWNlbnRvICAgIDwtICIjRDcxOTFDIg0KcGFsX3ZlaGljdWxvcyA8LSBjKCIjMkM3QkI2IiwgIiMxQTk2NDEiLCAiI0Q3MTkxQyIpDQpwYWxfY29ybyAgICAgIDwtICJZbE9yUmQiDQoNCiMgUGFsZXRhIGludGVyYWN0aXZhOiBjb2xvcmVzIHBhcmEgY2FkYSBtb2RvIGVuIGxvcyBtYXBhcyBsZWFmbGV0DQpwYWxfaW50ZXJhY3Rpdm8gPC0gbGlzdCgNCiAgZ2VuZXJhbCAgID0gIiM2MzYzNjMiLA0KICBiaWNpY2xldGEgPSAiIzFBOTY0MSIsDQogIG1vdG8gICAgICA9ICIjRTY4NTBFIiwNCiAgYXV0b21vdmlsID0gIiMyQzdCQjYiDQopDQpgYGANCg0KIyMgRGlzdHJpYnVjacOzbiBwb3IgVGlwbyBkZSBWZWjDrWN1bG8NCg0KYGBge3IgZmlnX3ZlaGljdWxvcywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NX0NCmNvbnRlb192ZWggPC0gZW5jdWVzdGFfY2FsaSB8Pg0KICBmaWx0ZXIodGlwb192ZWhpY3VsbyAlaW4lIDE6MykgfD4NCiAgbXV0YXRlKFZlaGljdWxvID0gY2FzZV93aGVuKA0KICAgIHRpcG9fdmVoaWN1bG8gPT0gMSB+ICJCaWNpY2xldGEiLA0KICAgIHRpcG9fdmVoaWN1bG8gPT0gMiB+ICJNb3RvIiwNCiAgICB0aXBvX3ZlaGljdWxvID09IDMgfiAiQXV0b21vdmlsIg0KICApKSB8Pg0KICBncm91cF9ieShWZWhpY3VsbykgfD4NCiAgc3VtbWFyaXNlKG4gPSBuKCkpIHw+DQogIG11dGF0ZShwY3QgPSByb3VuZChuIC8gc3VtKG4pICogMTAwLCAxKSkNCg0KYnAgPC0gYmFycGxvdCgNCiAgY29udGVvX3ZlaCRuLA0KICBuYW1lcy5hcmcgPSBjb250ZW9fdmVoJFZlaGljdWxvLA0KICBjb2wgICAgICAgPSBwYWxfdmVoaWN1bG9zLA0KICBib3JkZXIgICAgPSAid2hpdGUiLA0KICBtYWluICAgICAgPSAiIiwNCiAgeWxhYiAgICAgID0gIk51bWVybyBkZSB2aWFqZXMiLA0KICB4bGFiICAgICAgPSAiVGlwbyBkZSB2ZWhpY3VsbyIsDQogIHlsaW0gICAgICA9IGMoMCwgbWF4KGNvbnRlb192ZWgkbikgKiAxLjIpLA0KICBsYXMgICAgICAgPSAxLA0KICBjZXgubmFtZXMgPSAxLjEsDQogIGNleC5heGlzICA9IDAuOTUNCikNCg0KdGV4dChicCwgY29udGVvX3ZlaCRuICsgbWF4KGNvbnRlb192ZWgkbikgKiAwLjAzLA0KICAgICBsYWJlbHMgPSBwYXN0ZTAoY29udGVvX3ZlaCRuLCAiXG4oIiwgY29udGVvX3ZlaCRwY3QsICIlKSIpLA0KICAgICBjZXggPSAwLjksIGZvbnQgPSAyKQ0KYGBgDQoNCmByIG51ZXZhX2ZpZ3VyYSgiRGlzdHJpYnVjacOzbiBkZSB2aWFqZXMgaW50cmF1cmJhbm9zIGRlIENhbGkgcG9yIHRpcG8gZGUgdmVow61jdWxvIChFbmN1ZXN0YSBPcmlnZW4tRGVzdGlubyAyMDE1KSIpYA0KDQoqKkludGVycHJldGFjacOzbjoqKiBMYSBncsOhZmljYSBtdWVzdHJhIGxhIGNvbXBvc2ljacOzbiBtb2RhbCBkZSBsb3MgdmlhamVzIGludHJhdXJiYW5vcyByZWdpc3RyYWRvcyBlbiBsYSBlbmN1ZXN0YSBwYXJhIGxvcyB0cmVzIG1vZG9zIGRlIHRyYW5zcG9ydGUgYW5hbGl6YWRvcy4gRWwgYXV0b23Ds3ZpbCBjb25jZW50cmEgbGEgbWF5b3IgcHJvcG9yY2nDs24gZGUgbG9zIHRyYXllY3Rvcywgc2VndWlkbyBwb3IgbGEgbW90byB5IGxhIGJpY2ljbGV0YS4gRXN0YSBkaXN0cmlidWNpw7NuIHJlZmxlamEgZWwgcGF0csOzbiBkZSBtb3Rvcml6YWNpw7NuIHByZWRvbWluYW50ZSBlbiBjaXVkYWRlcyBjb2xvbWJpYW5hcyBpbnRlcm1lZGlhcyB5IGdyYW5kZXMsIGRvbmRlIGVsIHZlaMOtY3VsbyBwcml2YWRvIGRlIGN1YXRybyBydWVkYXMgc2lndWUgc2llbmRvIGVsIG1vZG8gZG9taW5hbnRlIGEgcGVzYXIgZGVsIGNyZWNpbWllbnRvIHNvc3RlbmlkbyBkZSBsYSBtb3RvY2ljbGV0YSBlbiBsYSDDumx0aW1hIGTDqWNhZGEuIExhIHBhcnRpY2lwYWNpw7NuIGRlIGxhIGJpY2ljbGV0YSwgYXVucXVlIG1lbm9yLCB0aWVuZSBpbXBsaWNhY2lvbmVzIGRpcmVjdGFzIHBhcmEgbGEgcGxhbmlmaWNhY2nDs24gZGUgaW5mcmFlc3RydWN0dXJhIGNpY2xpc3RhIGVuIGxhIGNpdWRhZC4NCg0KIyMgRGlzdHJpYnVjacOzbiBkZSBWaWFqZXMgcG9yIENvbXVuYSBkZSBPcmlnZW4NCg0KYGBge3IgZmlnX29yaWdlbl9nZW5lcmFsX2JhciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTV9DQpvcmlnX2JhciA8LSBlbmN1ZXN0YV9jYWxpIHw+DQogIGdyb3VwX2J5KGNvbXVuYV9vcmlnZW4pIHw+DQogIHN1bW1hcmlzZShuID0gbigpKSB8Pg0KICBhcnJhbmdlKGNvbXVuYV9vcmlnZW4pDQoNCmJhcnBsb3QoDQogIG9yaWdfYmFyJG4sDQogIG5hbWVzLmFyZyA9IG9yaWdfYmFyJGNvbXVuYV9vcmlnZW4sDQogIGNvbCAgICAgICA9IHBhbF9wcmluY2lwYWwsDQogIGJvcmRlciAgICA9ICJ3aGl0ZSIsDQogIG1haW4gICAgICA9ICIiLA0KICB5bGFiICAgICAgPSAiTnVtZXJvIGRlIHZpYWplcyIsDQogIHhsYWIgICAgICA9ICJDb211bmEgZGUgb3JpZ2VuIiwNCiAgbGFzICAgICAgID0gMiwNCiAgY2V4Lm5hbWVzID0gMC44NSwNCiAgY2V4LmF4aXMgID0gMC45DQopDQpgYGANCg0KYHIgbnVldmFfZmlndXJhKCJOw7ptZXJvIGRlIHZpYWplcyBwb3IgY29tdW5hIGRlIG9yaWdlbiAodG9kb3MgbG9zIG1vZG9zLCBDYWxpIDIwMTUpIilgDQoNCioqSW50ZXJwcmV0YWNpw7NuOioqIEVsIGdyw6FmaWNvIGRlIGJhcnJhcyByZXZlbGEgcXVlIGxhIGdlbmVyYWNpw7NuIGRlIHZpYWplcyBubyBzZSBkaXN0cmlidXllIGRlIG1hbmVyYSB1bmlmb3JtZSBlbnRyZSBsYXMgMjIgY29tdW5hcy4gQWxndW5hcyBjb211bmFzIGNvbmNlbnRyYW4gdW5hIGNhbnRpZGFkIG5vdGFibGVtZW50ZSBtYXlvciBkZSB2aWFqZXMgb3JpZ2luYWRvcywgbG8gcXVlIGluZGljYSBxdWUgc29uIHpvbmFzIGRlIGFsdGEgZGVuc2lkYWQgcmVzaWRlbmNpYWwgbyBkZSBtYXlvciBhY3RpdmlkYWQgZ2VuZXJhZG9yYSBkZSBkZXNwbGF6YW1pZW50b3MuIEVzdGUgcGF0csOzbiBoZXRlcm9nw6luZW8gZXMgZXNwZXJhZG8gZW4gY2l1ZGFkZXMgY29uIGVzdHJ1Y3R1cmFzIHVyYmFuYXMgY29uc29saWRhZGFzIHkgZGVzaWd1YWxkYWRlcyBzb2Npb2Vjb27Ds21pY2FzIHRlcnJpdG9yaWFsZXMsIGRvbmRlIGxhcyDDoXJlYXMgZGUgbWF5b3IgZGVuc2lkYWQgcG9ibGFjaW9uYWwgeSBtZW5vciBhY2Nlc28gYSB0cmFuc3BvcnRlIG1hc2l2byB0aWVuZGVuIGEgZ2VuZXJhciBtw6FzIHZpYWplcyBlbiBtb2RvcyBwcml2YWRvcyBvIGFsdGVybmF0aXZvcy4NCg0KIyMgRGlzdHJpYnVjacOzbiBkZSBWaWFqZXMgcG9yIENvbXVuYSBkZSBEZXN0aW5vDQoNCmBgYHtyIGZpZ19kZXN0aW5vX2dlbmVyYWxfYmFyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NX0NCmRlc3RfYmFyIDwtIGVuY3Vlc3RhX2NhbGkgfD4NCiAgZ3JvdXBfYnkoY29tdW5hX2Rlc3Rpbm8pIHw+DQogIHN1bW1hcmlzZShuID0gbigpKSB8Pg0KICBhcnJhbmdlKGNvbXVuYV9kZXN0aW5vKQ0KDQpiYXJwbG90KA0KICBkZXN0X2JhciRuLA0KICBuYW1lcy5hcmcgPSBkZXN0X2JhciRjb211bmFfZGVzdGlubywNCiAgY29sICAgICAgID0gcGFsX2FjZW50bywNCiAgYm9yZGVyICAgID0gIndoaXRlIiwNCiAgbWFpbiAgICAgID0gIiIsDQogIHlsYWIgICAgICA9ICJOdW1lcm8gZGUgdmlhamVzIiwNCiAgeGxhYiAgICAgID0gIkNvbXVuYSBkZSBkZXN0aW5vIiwNCiAgbGFzICAgICAgID0gMiwNCiAgY2V4Lm5hbWVzID0gMC44NSwNCiAgY2V4LmF4aXMgID0gMC45DQopDQpgYGANCg0KYHIgbnVldmFfZmlndXJhKCJOw7ptZXJvIGRlIHZpYWplcyBwb3IgY29tdW5hIGRlIGRlc3Rpbm8gKHRvZG9zIGxvcyBtb2RvcywgQ2FsaSAyMDE1KSIpYA0KDQoqKkludGVycHJldGFjacOzbjoqKiBMYSBkaXN0cmlidWNpw7NuIGRlIGxvcyBkZXN0aW5vcyBwcmVzZW50YSB1biBwZXJmaWwgZGlmZXJlbnRlIGFsIGRlIGxvcyBvcsOtZ2VuZXMsIGNvbiBhbGd1bmFzIGNvbXVuYXMgcXVlIGNvbmNlbnRyYW4gdW5hIGF0cmFjY2nDs24gZGUgdmlhamVzIHNpZ25pZmljYXRpdmFtZW50ZSBtYXlvci4gTGFzIGNvbXVuYXMgY29uIG1heW9yIG7Dum1lcm8gZGUgZGVzdGlub3MgY29ycmVzcG9uZGVuIHTDrXBpY2FtZW50ZSBhIGNlbnRyYWxpZGFkZXMgdXJiYW5hcyBkb25kZSBzZSBsb2NhbGl6YW4gYWN0aXZpZGFkZXMgZWNvbsOzbWljYXMsIGVkdWNhdGl2YXMsIGNvbWVyY2lhbGVzIG8gaW5zdGl0dWNpb25hbGVzLiBFc3RhIHBvbGFyaXphY2nDs24gZXNwYWNpYWwgZW50cmUgem9uYXMgZ2VuZXJhZG9yYXMgeSB6b25hcyBhdHJhY3RvcmFzIGVzIHVuIHBhdHLDs24gY2FyYWN0ZXLDrXN0aWNvIGRlbCBhbsOhbGlzaXMgZGUgZGF0b3MgZGUgw6FyZWEgZW4gY2l1ZGFkZXMgbGF0aW5vYW1lcmljYW5hcyB5IGNvbnN0aXR1eWUgbGEgYmFzZSBkZWwgZGlhZ27Ds3N0aWNvIGRlIGxhIGRlbWFuZGEgZGUgbW92aWxpZGFkLg0KDQojICoqQ29udGVvcyBwb3IgQ29tdW5hIHBhcmEgbG9zIDggTWFwYXMqKg0KDQpFbiBlc3RlIGNhcMOtdHVsbyBzZSBnZW5lcmFuIGxvcyBvY2hvIGNvbmp1bnRvcyBkZSBjb250ZW8gcXVlIGFsaW1lbnRhbiBsb3MgbWFwYXMgdGVtw6F0aWNvcywgYWdydXBhbmRvIGxvcyB2aWFqZXMgcG9yIGNvbXVuYSBkZSBvcmlnZW4geSBkZXN0aW5vIHBhcmEgY2FkYSBtb2RvIGRlIHRyYW5zcG9ydGUuDQoNCmBgYHtyIGNvbnRlb3N9DQojIC0tLS0gT1LDjUdFTkVTIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCm9yaWdlbl9nZW5lcmFsICAgPC0gZW5jdWVzdGFfY2FsaSB8PiBncm91cF9ieShjb211bmEgPSBjb211bmFfb3JpZ2VuKSB8PiBzdW1tYXJpc2UobiA9IG4oKSkNCm9yaWdlbl9iaWNpY2xldGEgPC0gZW5jdWVzdGFfY2FsaSB8PiBmaWx0ZXIodGlwb192ZWhpY3VsbyA9PSAxKSB8PiBncm91cF9ieShjb211bmEgPSBjb211bmFfb3JpZ2VuKSB8PiBzdW1tYXJpc2UobiA9IG4oKSkNCm9yaWdlbl9tb3RvICAgICAgPC0gZW5jdWVzdGFfY2FsaSB8PiBmaWx0ZXIodGlwb192ZWhpY3VsbyA9PSAyKSB8PiBncm91cF9ieShjb211bmEgPSBjb211bmFfb3JpZ2VuKSB8PiBzdW1tYXJpc2UobiA9IG4oKSkNCm9yaWdlbl9hdXRvbW92aWwgPC0gZW5jdWVzdGFfY2FsaSB8PiBmaWx0ZXIodGlwb192ZWhpY3VsbyA9PSAzKSB8PiBncm91cF9ieShjb211bmEgPSBjb211bmFfb3JpZ2VuKSB8PiBzdW1tYXJpc2UobiA9IG4oKSkNCg0KIyAtLS0tIERFU1RJTk9TIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmRlc3Rpbm9fZ2VuZXJhbCAgIDwtIGVuY3Vlc3RhX2NhbGkgfD4gZ3JvdXBfYnkoY29tdW5hID0gY29tdW5hX2Rlc3Rpbm8pIHw+IHN1bW1hcmlzZShuID0gbigpKQ0KZGVzdGlub19iaWNpY2xldGEgPC0gZW5jdWVzdGFfY2FsaSB8PiBmaWx0ZXIodGlwb192ZWhpY3VsbyA9PSAxKSB8PiBncm91cF9ieShjb211bmEgPSBjb211bmFfZGVzdGlubykgfD4gc3VtbWFyaXNlKG4gPSBuKCkpDQpkZXN0aW5vX21vdG8gICAgICA8LSBlbmN1ZXN0YV9jYWxpIHw+IGZpbHRlcih0aXBvX3ZlaGljdWxvID09IDIpIHw+IGdyb3VwX2J5KGNvbXVuYSA9IGNvbXVuYV9kZXN0aW5vKSB8PiBzdW1tYXJpc2UobiA9IG4oKSkNCmRlc3Rpbm9fYXV0b21vdmlsIDwtIGVuY3Vlc3RhX2NhbGkgfD4gZmlsdGVyKHRpcG9fdmVoaWN1bG8gPT0gMykgfD4gZ3JvdXBfYnkoY29tdW5hID0gY29tdW5hX2Rlc3Rpbm8pIHw+IHN1bW1hcmlzZShuID0gbigpKQ0KYGBgDQoNCmByIG51ZXZhX3RhYmxhKCJSZXN1bWVuIGRlIGNvbnRlb3MgZGUgdmlhamVzIHBvciBtb2RvIGRlIHRyYW5zcG9ydGUgeSBkaXJlY2Npw7NuIilgDQoNCmBgYHtyIHRhYmxhX2NvbnRlb3N9DQpyZXN1bWVuX2NvbnRlb3MgPC0gZGF0YS5mcmFtZSgNCiAgTWFwYSAgICAgID0gcGFzdGUwKCJNYXBhICIsIDE6OCksDQogIERpcmVjY2lvbiA9IHJlcChjKCJPcmlnZW4iLCAiRGVzdGlubyIpLCBlYWNoID0gNCksDQogIE1vZG8gICAgICA9IHJlcChjKCJHZW5lcmFsIiwgIkJpY2ljbGV0YSIsICJNb3RvIiwgIkF1dG9tb3ZpbCIpLCAyKSwNCiAgVG90YWxfVmlhamVzID0gYygNCiAgICBzdW0ob3JpZ2VuX2dlbmVyYWwkbiksICAgc3VtKG9yaWdlbl9iaWNpY2xldGEkbiksDQogICAgc3VtKG9yaWdlbl9tb3RvJG4pLCAgICAgIHN1bShvcmlnZW5fYXV0b21vdmlsJG4pLA0KICAgIHN1bShkZXN0aW5vX2dlbmVyYWwkbiksICBzdW0oZGVzdGlub19iaWNpY2xldGEkbiksDQogICAgc3VtKGRlc3Rpbm9fbW90byRuKSwgICAgIHN1bShkZXN0aW5vX2F1dG9tb3ZpbCRuKQ0KICApDQopDQoNCmthYmxlKHJlc3VtZW5fY29udGVvcywgY2FwdGlvbiA9IE5VTEwsDQogICAgICBjb2wubmFtZXMgPSBjKCJNYXBhIiwgIkRpcmVjY2nDs24iLCAiTW9kbyBkZSBUcmFuc3BvcnRlIiwgIlRvdGFsIGRlIFZpYWplcyIpLA0KICAgICAgYWxpZ24gPSBjKCJsIiwgImwiLCAibCIsICJyIikpIHw+DQogIGthYmxlX3N0eWxpbmcoDQogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLA0KICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwNCiAgICBmb250X3NpemUgID0gMTMNCiAgKSB8Pg0KICByb3dfc3BlYygwLCBib2xkID0gVFJVRSkNCmBgYA0KDQoqKkludGVycHJldGFjacOzbjoqKiBMYSB0YWJsYSByZXN1bWUgZWwgdW5pdmVyc28gZGUgYW7DoWxpc2lzIHBhcmEgY2FkYSB1bm8gZGUgbG9zIG9jaG8gbWFwYXMuIFNlIG9ic2VydmEgcXVlIGVsIHRvdGFsIGRlIHZpYWplcyBlbiBsb3MgbWFwYXMgZGUgb3JpZ2VuIHkgZGVzdGlubyBjb2luY2lkZSBwYXJhIGNhZGEgbW9kbywgbG8gcXVlIHZhbGlkYSBsYSBjb25zaXN0ZW5jaWEgZGVsIHByb2Nlc28gZGUgZmlsdHJhZG8geSBjb250ZW8uIExhIGRpZmVyZW5jaWEgZW4gZWwgbsO6bWVybyBkZSB2aWFqZXMgZW50cmUgbW9kb3MgcmVmbGVqYSBsYSBjb21wb3NpY2nDs24gbW9kYWwgcmVhbCBkZSBsYSBlbmN1ZXN0YTogZWwgYXV0b23Ds3ZpbCB5IGxhIG1vdG8gYWdydXBhbiBsYSBtYXlvciBwcm9wb3JjacOzbiBkZSByZWdpc3Ryb3MsIG1pZW50cmFzIHF1ZSBsYSBiaWNpY2xldGEgcmVwcmVzZW50YSB1bmEgZnJhY2Npw7NuIG1lbm9yIHBlcm8gbm8gZGVzcHJlY2lhYmxlIGRlc2RlIGVsIHB1bnRvIGRlIHZpc3RhIGRlIGxhIHBsYW5pZmljYWNpw7NuIGRlIGluZnJhZXN0cnVjdHVyYS4NCg0KIyAqKkZ1bmNpw7NuIHBhcmEgR2VuZXJhY2nDs24gZGUgTWFwYXMgZGUgQ29yb3BsZXRhcyoqDQoNCkVuIGVzdGUgY2Fww610dWxvIHNlIGRlZmluZSBsYSBmdW5jacOzbiByZXV0aWxpemFibGUgcXVlIHByb2R1Y2UgbG9zIG1hcGFzIHRlbcOhdGljb3MgZXN0w6F0aWNvcywgY29uIHVuYSBwYWxldGEgZGUgY29sb3JlcyBob21vZ8OpbmVhIGFwbGljYWRhIGRlIG1hbmVyYSBjb25zaXN0ZW50ZSBhIGxhcyBvY2hvIHZpc3VhbGl6YWNpb25lcy4NCg0KRWwgbWFwYSBkZSBjb3JvcGxldGFzIGVzIGxhIHJlcHJlc2VudGFjacOzbiBjYXJ0b2dyw6FmaWNhIG3DoXMgYWRlY3VhZGEgcGFyYSBkYXRvcyBkZSDDoXJlYSwgeWEgcXVlIGFzaWduYSB1bmEgZ3JhZGFjacOzbiBkZSBjb2xvciBwcm9wb3JjaW9uYWwgYWwgdmFsb3IgZGUgdW5hIHZhcmlhYmxlIGN1YW50aXRhdGl2YSBlbiB1bmlkYWRlcyBwb2xpZ29uYWxlcy4gU2UgZW1wbGVhIGNsYXNpZmljYWNpw7NuIHBvciBjdWFudGlsZXMsIG3DqXRvZG8gcXVlIGRpc3RyaWJ1eWUgZGUgbWFuZXJhIGVxdWlsaWJyYWRhIGxhcyBjb211bmFzIGVudHJlIGxhcyBjaW5jbyBjbGFzZXMgeSBmYWNpbGl0YSBsYSBsZWN0dXJhIGNvbXBhcmF0aXZhIGVudHJlIG1hcGFzLiBUb2RvcyBsb3MgbWFwYXMgdXNhbiBsYSBtaXNtYSBwYWxldGEgc2VjdWVuY2lhbCBgWWxPclJkYCAoYW1hcmlsbG8tbmFyYW5qYS1yb2pvKSwgZ2FyYW50aXphbmRvIGNvaGVyZW5jaWEgdmlzdWFsIHkgZmFjaWxpdGFuZG8gbGEgY29tcGFyYWNpw7NuIGRpcmVjdGEgZW50cmUgbG9zIG9jaG8gcHJvZHVjdG9zIGNhcnRvZ3LDoWZpY29zLg0KDQpgYGB7ciBmdW5jaW9uX21hcGF9DQpnZW5lcmFyX21hcGEgPC0gZnVuY3Rpb24oc2hwX3NmLCBjb250ZW9fZGYsIHRpdHVsbykgew0KDQogIHNocF9zZiRjb211bmEgPC0gYXMuaW50ZWdlcihzaHBfc2YkY29tdW5hKQ0KICBzaHBfam9pbiAgICAgIDwtIGxlZnRfam9pbihzaHBfc2YsIGNvbnRlb19kZiwgYnkgPSAiY29tdW5hIikNCiAgc2hwX2pvaW4kbltpcy5uYShzaHBfam9pbiRuKV0gPC0gMA0KDQogIGJya3MgICAgPC0gY2xhc3NJbnRlcnZhbHMoc2hwX2pvaW4kbiwgbiA9IDUsIHN0eWxlID0gInF1YW50aWxlIikkYnJrcw0KICBicmtzICAgIDwtIHVuaXF1ZShicmtzKQ0KICBuX2NsICAgIDwtIGxlbmd0aChicmtzKSAtIDENCiAgY29sb3JlcyA8LSBicmV3ZXIucGFsKG1heCgzLCBuX2NsKSwgcGFsX2Nvcm8pWzE6bl9jbF0NCiAgY29sX2FzaWcgPC0gY29sb3Jlc1tmaW5kSW50ZXJ2YWwoc2hwX2pvaW4kbiwgYnJrcywgcmlnaHRtb3N0LmNsb3NlZCA9IFRSVUUpXQ0KDQogIHBsb3Qoc3RfZ2VvbWV0cnkoc2hwX2pvaW4pLA0KICAgICAgIGNvbCA9IGNvbF9hc2lnLCBib3JkZXIgPSAid2hpdGUiLCBsd2QgPSAwLjcsDQogICAgICAgbWFpbiA9IHRpdHVsbywgY2V4Lm1haW4gPSAxLjA1KQ0KDQogIGNvb3JkcyA8LSBzdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZChzdF9nZW9tZXRyeShzaHBfam9pbikpKQ0KICB0ZXh0KGNvb3Jkc1ssIDFdLCBjb29yZHNbLCAyXSwNCiAgICAgICBsYWJlbHMgPSBzaHBfam9pbiRjb211bmEsIGNleCA9IDAuNTUsIGNvbCA9ICJibGFjayIsIGZvbnQgPSAyKQ0KDQogIGxlZ2VuZCgiYm90dG9tbGVmdCIsDQogICAgICAgICBsZWdlbmQgPSBwYXN0ZTAocm91bmQoYnJrc1stbGVuZ3RoKGJya3MpXSksICIgLSAiLCByb3VuZChicmtzWy0xXSkpLA0KICAgICAgICAgZmlsbCA9IGNvbG9yZXMsIHRpdGxlID0gIk4gdmlhamVzIiwgY2V4ID0gMC42NSwgYnR5ID0gIm4iKQ0KfQ0KYGBgDQoNCiMgKipNYXBhcyBUZW3DoXRpY29zIGRlIE9yaWdlbioqDQoNCkVuIGVzdGUgY2Fww610dWxvIHNlIHByZXNlbnRhbiBsb3MgY3VhdHJvIG1hcGFzIGRlIG9yaWdlbiBxdWUgbXVlc3RyYW4gbGEgZGlzdHJpYnVjacOzbiBlc3BhY2lhbCBkZSBsYXMgY29tdW5hcyBnZW5lcmFkb3JhcyBkZSB2aWFqZXMsIHRhbnRvIGRlIG1hbmVyYSBnZW5lcmFsIGNvbW8gZGlzY3JpbWluYWRhIHBvciB0aXBvIGRlIHZlaMOtY3Vsby4NCg0KIyMgTWFwYSBkZSBPcmlnZW4gR2VuZXJhbA0KDQpgYGB7ciBtYXBhMSwgZmlnLndpZHRoPTYuNSwgZmlnLmhlaWdodD02LjV9DQpnZW5lcmFyX21hcGEoY29tdW5hcywgb3JpZ2VuX2dlbmVyYWwsDQogICAgICAgICAgICAgIk9yaWdlbiBHZW5lcmFsIC0gVG9kb3MgbG9zIG1vZG9zIGRlIHRyYW5zcG9ydGUiKQ0KYGBgDQoNCmByIG51ZXZhX2ZpZ3VyYSgiTWFwYSBkZSBjb3JvcGxldGFzOiBPcmlnZW4gZ2VuZXJhbCBkZSB2aWFqZXMgcG9yIGNvbXVuYSwgQ2FsaSAyMDE1IilgDQoNCioqSW50ZXJwcmV0YWNpw7NuOioqIEVsIG1hcGEgZGUgb3JpZ2VuIGdlbmVyYWwgcmV2ZWxhIGxvcyBwYXRyb25lcyBkZSBnZW5lcmFjacOzbiBkZSB2aWFqZXMgZW4gZWwgY29uanVudG8gZGVsIHRlcnJpdG9yaW8gdXJiYW5vIGRlIENhbGkuIExhcyBjb211bmFzIHF1ZSBwcmVzZW50YW4gbG9zIHRvbm9zIG3DoXMgaW50ZW5zb3MgY29uY2VudHJhbiBsYSBtYXlvciBjYW50aWRhZCBkZSB2aWFqZXMgb3JpZ2luYWRvcywgaW5kZXBlbmRpZW50ZW1lbnRlIGRlbCBtb2RvIGRlIHRyYW5zcG9ydGUgdXRpbGl6YWRvLiBEZXNkZSBsYSBwZXJzcGVjdGl2YSBkZWwgYW7DoWxpc2lzIGRlIGRhdG9zIGRlIMOhcmVhLCBlc3RhcyBjb211bmFzIHJlcHJlc2VudGFuIGxhcyB6b25hcyBjb24gbWF5b3IgYWN0aXZpZGFkIGdlbmVyYWRvcmEgZGUgZGVzcGxhemFtaWVudG9zLCBsbyBjdWFsIGVzdMOhIGVzdHJlY2hhbWVudGUgdmluY3VsYWRvIGNvbiBsYSBkZW5zaWRhZCBwb2JsYWNpb25hbCwgbGEgZXN0cnVjdHVyYSBzb2Npb2Vjb27Ds21pY2EgZGVsIHRlcnJpdG9yaW8geSBsYSBkaXNwb25pYmlsaWRhZCBkZSB0cmFuc3BvcnRlLiBMYXMgY29tdW5hcyBwZXJpZsOpcmljYXMgZGVsIG9yaWVudGUgeSBzdXJvcmllbnRlIGRlIGxhIGNpdWRhZCBzdWVsZW4gYXBhcmVjZXIgY29tbyBpbXBvcnRhbnRlcyBnZW5lcmFkb3JhcyBkZSB2aWFqZXMgZGViaWRvIGEgc3UgYWx0YSBkZW5zaWRhZCByZXNpZGVuY2lhbCB5IGEgbGFzIGNvbmRpY2lvbmVzIGRlIGFjY2VzaWJpbGlkYWQgcXVlIGluY2VudGl2YW4gZWwgdXNvIGRlIG1vZG9zIGFsdGVybmF0aXZvcyBhbCB0cmFuc3BvcnRlIG1hc2l2by4NCg0KIyMgTWFwYSBkZSBPcmlnZW4gZW4gQmljaWNsZXRhDQoNCmBgYHtyIG1hcGEyLCBmaWcud2lkdGg9Ni41LCBmaWcuaGVpZ2h0PTYuNX0NCmdlbmVyYXJfbWFwYShjb211bmFzLCBvcmlnZW5fYmljaWNsZXRhLCAiT3JpZ2VuIC0gQmljaWNsZXRhIikNCmBgYA0KDQpgciBudWV2YV9maWd1cmEoIk1hcGEgZGUgY29yb3BsZXRhczogT3JpZ2VuIGRlIHZpYWplcyBlbiBiaWNpY2xldGEgcG9yIGNvbXVuYSwgQ2FsaSAyMDE1IilgDQoNCioqSW50ZXJwcmV0YWNpw7NuOioqIExhIGRpc3RyaWJ1Y2nDs24gZXNwYWNpYWwgZGUgbG9zIG9yw61nZW5lcyBlbiBiaWNpY2xldGEgbXVlc3RyYSB1bmEgY29uY2VudHJhY2nDs24gZGlmZXJlbmNpYWRhIHJlc3BlY3RvIGFsIHBhdHLDs24gZ2VuZXJhbCwgbG8gcXVlIHN1Z2llcmUgcXVlIGVsIHVzbyBkZSBlc3RlIG1vZG8gbm8gZXMgaG9tb2fDqW5lbyBlbiBlbCB0ZXJyaXRvcmlvLiBMYSBiaWNpY2xldGEgdGllbmRlIGEgY29uY2VudHJhcnNlIGVuIGNvbXVuYXMgY29uIHRvcG9ncmFmw61hIHJlbGF0aXZhbWVudGUgcGxhbmEsIHJlY29ycmlkb3MgY29ydG9zIHkgZW4gem9uYXMgZG9uZGUgbGEgcG9ibGFjacOzbiBkZSBpbmdyZXNvcyBtZWRpb3MtYmFqb3MgbGEgYWRvcHRhIGNvbW8gYWx0ZXJuYXRpdmEgZGUgYmFqbyBjb3N0byBmcmVudGUgYWwgdHJhbnNwb3J0ZSBtb3Rvcml6YWRvLiBMYSBjb21wYXJhY2nDs24gZGUgZXN0ZSBtYXBhIGNvbiBsYSBkaXN0cmlidWNpw7NuIGRlIGNpY2xvdsOtYXMgZXhpc3RlbnRlcyBlbiBsYSBjaXVkYWQgcGVybWl0ZSBpZGVudGlmaWNhciBzaSBsYXMgY29tdW5hcyBjb24gbWF5b3IgZ2VuZXJhY2nDs24gZGUgdmlhamVzIGVuIGJpY2ljbGV0YSBjdWVudGFuIGNvbiBpbmZyYWVzdHJ1Y3R1cmEgY2ljbGlzdGEgYWRlY3VhZGEsIG8gc2kgcG9yIGVsIGNvbnRyYXJpbyBleGlzdGUgdW4gZMOpZmljaXQgZGUgY29uZWN0aXZpZGFkIHF1ZSBleHBvbmUgYSBsb3MgY2ljbGlzdGFzIGEgY29uZGljaW9uZXMgZGUgbWVub3Igc2VndXJpZGFkIHZpYWwuDQoNCiMjIE1hcGEgZGUgT3JpZ2VuIGVuIE1vdG8NCg0KYGBge3IgbWFwYTMsIGZpZy53aWR0aD02LjUsIGZpZy5oZWlnaHQ9Ni41fQ0KZ2VuZXJhcl9tYXBhKGNvbXVuYXMsIG9yaWdlbl9tb3RvLCAiT3JpZ2VuIC0gTW90byIpDQpgYGANCg0KYHIgbnVldmFfZmlndXJhKCJNYXBhIGRlIGNvcm9wbGV0YXM6IE9yaWdlbiBkZSB2aWFqZXMgZW4gbW90byBwb3IgY29tdW5hLCBDYWxpIDIwMTUiKWANCg0KKipJbnRlcnByZXRhY2nDs246KiogRWwgbWFwYSBkZSBvcmlnZW4gZW4gbW90byBwb25lIGRlIG1hbmlmaWVzdG8gbGEgcGVuZXRyYWNpw7NuIGRlIGVzdGUgbW9kbyBkZSB0cmFuc3BvcnRlIGVuIGVsIHRlcnJpdG9yaW8gdXJiYW5vIGRlIENhbGkuIExhIG1vdG9jaWNsZXRhIGhhIGV4cGVyaW1lbnRhZG8gdW4gY3JlY2ltaWVudG8gc29zdGVuaWRvIGVuIGxhcyBjaXVkYWRlcyBjb2xvbWJpYW5hcyBkdXJhbnRlIGxhIMO6bHRpbWEgZMOpY2FkYSwgaW1wdWxzYWRvIHBvciBzdSBiYWpvIGNvc3RvIGRlIGFkcXVpc2ljacOzbiB5IG1hbnRlbmltaWVudG8gZnJlbnRlIGFsIGF1dG9tw7N2aWwsIGFzw60gY29tbyBwb3Igc3UgY2FwYWNpZGFkIGRlIHNvcnRlYXIgbGEgY29uZ2VzdGnDs24gdmlhbC4gTGFzIGNvbXVuYXMgY29uIG1heW9yIGdlbmVyYWNpw7NuIGRlIHZpYWplcyBlbiBtb3RvIGNvcnJlc3BvbmRlbiBjb24gZnJlY3VlbmNpYSBhIHpvbmFzIGRlIGV4cGFuc2nDs24gdXJiYW5hIG8gZGUgbWVub3IgY29iZXJ0dXJhIGRlbCBzaXN0ZW1hIGRlIHRyYW5zcG9ydGUgbWFzaXZvLCBkb25kZSBsYSBtb3RvIHNlIGNvbnZpZXJ0ZSBlbiBlbCBtb2RvIHByaW5jaXBhbCBkZSBhY2Nlc28gYWwgdHJhYmFqbyB5IGEgbG9zIHNlcnZpY2lvcy4NCg0KIyMgTWFwYSBkZSBPcmlnZW4gZW4gQXV0b23Ds3ZpbA0KDQpgYGB7ciBtYXBhNCwgZmlnLndpZHRoPTYuNSwgZmlnLmhlaWdodD02LjV9DQpnZW5lcmFyX21hcGEoY29tdW5hcywgb3JpZ2VuX2F1dG9tb3ZpbCwgIk9yaWdlbiAtIEF1dG9tb3ZpbCIpDQpgYGANCg0KYHIgbnVldmFfZmlndXJhKCJNYXBhIGRlIGNvcm9wbGV0YXM6IE9yaWdlbiBkZSB2aWFqZXMgZW4gYXV0b23Ds3ZpbCBwb3IgY29tdW5hLCBDYWxpIDIwMTUiKWANCg0KKipJbnRlcnByZXRhY2nDs246KiogRWwgbWFwYSBkZSBvcmlnZW4gZW4gYXV0b23Ds3ZpbCByZWZsZWphIGxhIGRpc3RyaWJ1Y2nDs24gc29jaW9lY29uw7NtaWNhIGRlbCB0ZXJyaXRvcmlvLCBkYWRvIHF1ZSBsYSB0YXNhIGRlIG1vdG9yaXphY2nDs24gcHJpdmFkYSBndWFyZGEgdW5hIGNvcnJlbGFjacOzbiBwb3NpdGl2YSBjb24gZWwgbml2ZWwgZGUgaW5ncmVzb3MgZGVsIGhvZ2FyLiBMYXMgY29tdW5hcyBjb24gbWF5b3IgaW50ZW5zaWRhZCBkZSBjb2xvciBlbiBlc3RlIG1hcGEgY29ycmVzcG9uZGVuIGEgc2VjdG9yZXMgZGUgZXN0cmF0b3MgbWVkaW9zIHkgYWx0b3MgZG9uZGUgbGEgcG9zZXNpw7NuIHkgZWwgdXNvIGRlbCBhdXRvbcOzdmlsIHBhcnRpY3VsYXIgc29uIG3DoXMgZnJlY3VlbnRlcy4gTGEgY29uY2VudHJhY2nDs24gZGUgb3LDrWdlbmVzIGVuIGVzdGFzIGNvbXVuYXMgaW1wbGljYSBxdWUgdW5hIHBhcnRlIGltcG9ydGFudGUgZGUgbG9zIHZpYWplcyBlbiBhdXRvbcOzdmlsIGNvbnZlcmdlbiBoYWNpYSBsYXMgdsOtYXMgZGUgYWNjZXNvIGEgbGFzIGNlbnRyYWxpZGFkZXMsIGxvIHF1ZSBnZW5lcmEgcHJlc2nDs24gc29icmUgbGEgY2FwYWNpZGFkIHZpYWwgeSBjb250cmlidXllIGEgbGEgY29uZ2VzdGnDs24gZW4gbG9zIGNvcnJlZG9yZXMgcHJpbmNpcGFsZXMgZGUgbGEgY2l1ZGFkLg0KDQojICoqTWFwYXMgVGVtw6F0aWNvcyBkZSBEZXN0aW5vKioNCg0KRW4gZXN0ZSBjYXDDrXR1bG8gc2UgcHJlc2VudGFuIGxvcyBjdWF0cm8gbWFwYXMgZGUgZGVzdGlubyBxdWUgbXVlc3RyYW4gbGEgZGlzdHJpYnVjacOzbiBlc3BhY2lhbCBkZSBsYXMgY29tdW5hcyBhdHJhY3RvcmFzIGRlIHZpYWplcywgZGlmZXJlbmNpYWRhcyBwb3IgdGlwbyBkZSB2ZWjDrWN1bG8sIGNvbXBsZW1lbnRhbmRvIGVsIGFuw6FsaXNpcyBkZSBsb3MgcGF0cm9uZXMgZGUgbW92aWxpZGFkIHVyYmFuYS4NCg0KIyMgTWFwYSBkZSBEZXN0aW5vIEdlbmVyYWwNCg0KYGBge3IgbWFwYTUsIGZpZy53aWR0aD02LjUsIGZpZy5oZWlnaHQ9Ni41fQ0KZ2VuZXJhcl9tYXBhKGNvbXVuYXMsIGRlc3Rpbm9fZ2VuZXJhbCwNCiAgICAgICAgICAgICAiRGVzdGlubyBHZW5lcmFsIC0gVG9kb3MgbG9zIG1vZG9zIGRlIHRyYW5zcG9ydGUiKQ0KYGBgDQoNCmByIG51ZXZhX2ZpZ3VyYSgiTWFwYSBkZSBjb3JvcGxldGFzOiBEZXN0aW5vIGdlbmVyYWwgZGUgdmlhamVzIHBvciBjb211bmEsIENhbGkgMjAxNSIpYA0KDQoqKkludGVycHJldGFjacOzbjoqKiBFbCBtYXBhIGRlIGRlc3Rpbm8gZ2VuZXJhbCBpZGVudGlmaWNhIGxhcyBjb211bmFzIHF1ZSBhY3TDumFuIGNvbW8gcG9sb3MgZGUgYXRyYWNjacOzbiBkZSB2aWFqZXMgZGVudHJvIGRlIGxhIGNpdWRhZC4gTGFzIHpvbmFzIGNvbiBtYXlvciBjb25jZW50cmFjacOzbiBkZSBkZXN0aW5vcyBzb24gYXF1ZWxsYXMgZG9uZGUgc2UgbG9jYWxpemFuIGxhcyBwcmluY2lwYWxlcyBhY3RpdmlkYWRlcyBlY29uw7NtaWNhcywgY29tZXJjaWFsZXMsIGVkdWNhdGl2YXMgZSBpbnN0aXR1Y2lvbmFsZXMgZGUgQ2FsaS4gTGEgY29tcGFyYWNpw7NuIGVudHJlIGVsIG1hcGEgZGUgb3JpZ2VuIGdlbmVyYWwgeSBlc3RlIG1hcGEgZGUgZGVzdGlubyBwZXJtaXRlIGNhcmFjdGVyaXphciBsYSBlc3RydWN0dXJhIGVzcGFjaWFsIGRlIGxhIGRlbWFuZGEgZGUgbW92aWxpZGFkOiBsYXMgY29tdW5hcyBjb24gYWx0byBvcmlnZW4geSBiYWpvIGRlc3Rpbm8gc29uIHpvbmFzIGVtaW5lbnRlbWVudGUgcmVzaWRlbmNpYWxlcywgbWllbnRyYXMgcXVlIGFxdWVsbGFzIGNvbiBhbHRvIGRlc3Rpbm8geSBiYWpvIG9yaWdlbiBjb3JyZXNwb25kZW4gYSBsYXMgY2VudHJhbGlkYWRlcyBmdW5jaW9uYWxlcyBkZSBsYSBjaXVkYWQuDQoNCiMjIE1hcGEgZGUgRGVzdGlubyBlbiBCaWNpY2xldGENCg0KYGBge3IgbWFwYTYsIGZpZy53aWR0aD02LjUsIGZpZy5oZWlnaHQ9Ni41fQ0KZ2VuZXJhcl9tYXBhKGNvbXVuYXMsIGRlc3Rpbm9fYmljaWNsZXRhLCAiRGVzdGlubyAtIEJpY2ljbGV0YSIpDQpgYGANCg0KYHIgbnVldmFfZmlndXJhKCJNYXBhIGRlIGNvcm9wbGV0YXM6IERlc3Rpbm8gZGUgdmlhamVzIGVuIGJpY2ljbGV0YSBwb3IgY29tdW5hLCBDYWxpIDIwMTUiKWANCg0KKipJbnRlcnByZXRhY2nDs246KiogTG9zIGRlc3Rpbm9zIGVuIGJpY2ljbGV0YSByZXZlbGFuIGhhY2lhIGTDs25kZSBzZSBkZXNwbGF6YW4gbG9zIGNpY2xpc3RhcyBkZW50cm8gZGUgbGEgY2l1ZGFkLiBVbmEgY29udmVyZ2VuY2lhIGRlIGRlc3Rpbm9zIGhhY2lhIGNvbXVuYXMgY29uIGNlbnRyYWxpZGFkZXMgY29tZXJjaWFsZXMgbyBlZHVjYXRpdmFzIGNvbmZpcm1hIHF1ZSBsYSBiaWNpY2xldGEgY3VtcGxlIHVuYSBmdW5jacOzbiBkZSBhY2Nlc28gYSBhY3RpdmlkYWRlcyBjb3RpZGlhbmFzIHkgbm8gc29sbyBkZSByZWNyZWFjacOzbi4gTGEgaWRlbnRpZmljYWNpw7NuIGRlIGxhcyBjb211bmFzIGRlIG1heW9yIGF0cmFjY2nDs24gZGUgdmlhamVzIGVuIGJpY2ljbGV0YSwgY29udHJhc3RhZGEgY29uIGxhIG9mZXJ0YSBkZSBpbmZyYWVzdHJ1Y3R1cmEgY2ljbGlzdGEgZGlzcG9uaWJsZSBlbiBlc29zIHB1bnRvcyBkZSBsbGVnYWRhLCBwZXJtaXRlIGV2YWx1YXIgc2kgbGEgY2l1ZGFkIGN1ZW50YSBjb24gY2ljbG9wYXJxdWVhZGVyb3MgeSBjb25leGlvbmVzIHNlZ3VyYXMgZW4gbGFzIHpvbmFzIGRlIG1heW9yIGRlbWFuZGEgY2ljbGlzdGEuDQoNCiMjIE1hcGEgZGUgRGVzdGlubyBlbiBNb3RvDQoNCmBgYHtyIG1hcGE3LCBmaWcud2lkdGg9Ni41LCBmaWcuaGVpZ2h0PTYuNX0NCmdlbmVyYXJfbWFwYShjb211bmFzLCBkZXN0aW5vX21vdG8sICJEZXN0aW5vIC0gTW90byIpDQpgYGANCg0KYHIgbnVldmFfZmlndXJhKCJNYXBhIGRlIGNvcm9wbGV0YXM6IERlc3Rpbm8gZGUgdmlhamVzIGVuIG1vdG8gcG9yIGNvbXVuYSwgQ2FsaSAyMDE1IilgDQoNCioqSW50ZXJwcmV0YWNpw7NuOioqIEVsIG1hcGEgZGUgZGVzdGlubyBlbiBtb3RvIGV2aWRlbmNpYSBsYXMgY29tdW5hcyBxdWUgYWN0w7phbiBjb21vIHBvbG9zIGRlIGxsZWdhZGEgcGFyYSBsb3MgbW90b2NpY2xpc3RhcyBkZSBsYSBjaXVkYWQuIExhIGNvbmNlbnRyYWNpw7NuIGRlIGRlc3Rpbm9zIGVuIGNvbXVuYXMgY29uIGFsdGEgYWN0aXZpZGFkIGxhYm9yYWwgbyBjb21lcmNpYWwgaW5kaWNhIHF1ZSBsYSBtb3RvIHNlIHVzYSBwcmluY2lwYWxtZW50ZSBwYXJhIGN1YnJpciBkZXNwbGF6YW1pZW50b3MgZGUgY2Fyw6FjdGVyIGZ1bmNpb25hbC4gQWwgY29tcGFyYXIgZXN0ZSBtYXBhIGNvbiBlbCBkZSBvcmlnZW4gZW4gbW90bywgZXMgcG9zaWJsZSBjYXJhY3Rlcml6YXIgbGEgbG9uZ2l0dWQgeSBkaXJlY2Npw7NuIGRlIGxvcyB0cmF5ZWN0b3MgcHJlZG9taW5hbnRlczogc2kgbG9zIG9yw61nZW5lcyBzZSBjb25jZW50cmFuIGVuIGNvbXVuYXMgcGVyaWbDqXJpY2FzIHkgbG9zIGRlc3Rpbm9zIGVuIGNvbXVuYXMgY2VudHJhbGVzLCBzZSBjb25maXJtYSB1biBwYXRyw7NuIHJhZGlhbCBkZSBtb3ZpbGlkYWQgcXVlIGVqZXJjZSB1bmEgcHJlc2nDs24gYXNpbcOpdHJpY2Egc29icmUgbG9zIGNvcnJlZG9yZXMgdmlhbGVzIGRlIGNvbmV4acOzbiBlbnRyZSBsYSBwZXJpZmVyaWEgeSBlbCBjZW50cm8uDQoNCiMjIE1hcGEgZGUgRGVzdGlubyBlbiBBdXRvbcOzdmlsDQoNCmBgYHtyIG1hcGE4LCBmaWcud2lkdGg9Ni41LCBmaWcuaGVpZ2h0PTYuNX0NCmdlbmVyYXJfbWFwYShjb211bmFzLCBkZXN0aW5vX2F1dG9tb3ZpbCwgIkRlc3Rpbm8gLSBBdXRvbW92aWwiKQ0KYGBgDQoNCmByIG51ZXZhX2ZpZ3VyYSgiTWFwYSBkZSBjb3JvcGxldGFzOiBEZXN0aW5vIGRlIHZpYWplcyBlbiBhdXRvbcOzdmlsIHBvciBjb211bmEsIENhbGkgMjAxNSIpYA0KDQoqKkludGVycHJldGFjacOzbjoqKiBFbCBtYXBhIGRlIGRlc3Rpbm8gZW4gYXV0b23Ds3ZpbCBtdWVzdHJhIGxhcyBjb211bmFzIGRlIG1heW9yIGF0cmFjY2nDs24gZGUgdmlhamVzIGVuIHZlaMOtY3VsbyBwcml2YWRvLCBxdWUgY29ycmVzcG9uZGVuIGdlbmVyYWxtZW50ZSBhIHpvbmFzIGRlIGFsdGEgY29uY2VudHJhY2nDs24gZGUgYWN0aXZpZGFkIGVjb27Ds21pY2EgeSBzZXJ2aWNpb3MuIExhIHN1cGVycG9zaWNpw7NuIGVudHJlIGNvbXVuYXMgZGUgYWx0byBvcmlnZW4geSBhbHRvIGRlc3Rpbm8gZW4gYXV0b23Ds3ZpbCBnZW5lcmEgbG9zIHB1bnRvcyBkZSBtYXlvciBjb25nZXN0acOzbiB2aWFsLCB5YSBxdWUgbGEgZGVtYW5kYSBzaW11bHTDoW5lYSBkZSBlbnRyYWRhIHkgc2FsaWRhIHNvYnJlIGxvcyBtaXNtb3MgY29ycmVkb3JlcyBzdXBlcmEgY29uIGZyZWN1ZW5jaWEgbGEgY2FwYWNpZGFkIGRlIGxhIGluZnJhZXN0cnVjdHVyYSBkaXNwb25pYmxlLiBFc3RlIG1hcGEgZXMgdW4gaW5zdW1vIGRpcmVjdG8gcGFyYSBsYSBpbXBsZW1lbnRhY2nDs24gZGUgbWVkaWRhcyBkZSBnZXN0acOzbiBkZSBsYSBkZW1hbmRhLCBjb21vIHpvbmFzIGRlIHRhcmlmYWNpw7NuIHZpYWwsIHJlc3RyaWNjaW9uZXMgZGUgY2lyY3VsYWNpw7NuIG8gaW5jZW50aXZvcyBhbCB1c28gZGVsIHRyYW5zcG9ydGUgcMO6YmxpY28gZW4gbG9zIGNvcnJlZG9yZXMgbcOhcyBjYXJnYWRvcy4NCg0KIyAqKk1hcGFzIEludGVyYWN0aXZvcyBPcmlnZW4tRGVzdGlubyBwb3IgVGlwbyBkZSBWZWjDrWN1bG8qKg0KDQpFbiBlc3RlIGNhcMOtdHVsbyBzZSBwcmVzZW50YW4gY3VhdHJvIG1hcGFzIGludGVyYWN0aXZvcyBxdWUgdmlzdWFsaXphbiBsb3MgZmx1am9zIG9yaWdlbi1kZXN0aW5vIGVudHJlIGNvbXVuYXMgZGUgQ2FsaSBwYXJhIGNhZGEgdGlwbyBkZSB2ZWjDrWN1bG8uIENhZGEgbWFwYSBtdWVzdHJhIGxvcyBjZW50cm9pZGVzIGRlIGxhcyBjb211bmFzIGNvbW8gbm9kb3MgeSBkaWJ1amEgbMOtbmVhcyBkZSBmbHVqbyBlbnRyZSBsb3MgcGFyZXMgb3JpZ2VuLWRlc3Rpbm8gY29uIG1heW9yIG7Dum1lcm8gZGUgdmlhamVzLCBwZXJtaXRpZW5kbyBleHBsb3JhciBkZSBtYW5lcmEgZGluw6FtaWNhIGxhIGVzdHJ1Y3R1cmEgZXNwYWNpYWwgZGUgbGEgbW92aWxpZGFkIGludHJhdXJiYW5hLiBFbCBncm9zb3IgeSBsYSBvcGFjaWRhZCBkZSBjYWRhIGzDrW5lYSBzb24gcHJvcG9yY2lvbmFsZXMgYWwgbsO6bWVybyBkZSB2aWFqZXMgcmVnaXN0cmFkb3MgZW4gZXNlIHBhciwgeSBhbCBoYWNlciBjbGljIHNvYnJlIGNhZGEgbm9kbyBvIGzDrW5lYSBzZSBkZXNwbGllZ2EgaW5mb3JtYWNpw7NuIGRldGFsbGFkYSBzb2JyZSBsYSBjb211bmEgeSBlbCB2b2x1bWVuIGRlIHZpYWplcy4NCg0KYGBge3IgcHJlcF9pbnRlcmFjdGl2b30NCiMgLS0tLSBQcmVwYXJhciBjZW50cm9pZGVzIGRlIGNvbXVuYXMgZW4gV0dTODQgcGFyYSBMZWFmbGV0IC0tLS0tLS0tLS0tLS0tLS0tLQ0KY29tdW5hc193Z3MgPC0gc3RfdHJhbnNmb3JtKGNvbXVuYXMsIGNycyA9IDQzMjYpDQpjb211bmFzX3dncyRjb211bmEgPC0gYXMuaW50ZWdlcihjb211bmFzX3dncyRjb211bmEpDQoNCmNlbnRyb2lkZXMgPC0gc3RfY2VudHJvaWQoY29tdW5hc193Z3MpDQpjb29yZHNfYyAgIDwtIHN0X2Nvb3JkaW5hdGVzKGNlbnRyb2lkZXMpDQoNCm5vZG9zIDwtIGRhdGEuZnJhbWUoDQogIGNvbXVuYSA9IGNvbXVuYXNfd2dzJGNvbXVuYSwNCiAgbm9tYnJlID0gY29tdW5hc193Z3Mkbm9tYnJlLA0KICBsb24gICAgPSBjb29yZHNfY1ssIDFdLA0KICBsYXQgICAgPSBjb29yZHNfY1ssIDJdDQopDQoNCiMgLS0tLSBGdW5jacOzbiBwYXJhIGNvbnN0cnVpciB0YWJsYSBkZSBmbHVqb3MgT0QgcG9yIG1vZG8gLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY29uc3RydWlyX2ZsdWpvcyA8LSBmdW5jdGlvbihkYXRvcywgdGlwb192ZWggPSBOVUxMLCB0b3BfbiA9IDQwKSB7DQogIGlmICghaXMubnVsbCh0aXBvX3ZlaCkpIHsNCiAgICBkYXRvcyA8LSBkYXRvcyB8PiBmaWx0ZXIodGlwb192ZWhpY3VsbyA9PSB0aXBvX3ZlaCkNCiAgfQ0KICBkYXRvcyB8Pg0KICAgIGdyb3VwX2J5KGNvbXVuYV9vcmlnZW4sIGNvbXVuYV9kZXN0aW5vKSB8Pg0KICAgIHN1bW1hcmlzZSh2aWFqZXMgPSBuKCksIC5ncm91cHMgPSAiZHJvcCIpIHw+DQogICAgZmlsdGVyKGNvbXVuYV9vcmlnZW4gIT0gY29tdW5hX2Rlc3Rpbm8pIHw+DQogICAgYXJyYW5nZShkZXNjKHZpYWplcykpIHw+DQogICAgc2xpY2VfaGVhZChuID0gdG9wX24pIHw+DQogICAgbGVmdF9qb2luKG5vZG9zLCBieSA9IGMoImNvbXVuYV9vcmlnZW4iID0gImNvbXVuYSIpKSB8Pg0KICAgIHJlbmFtZShsb25fb3JpZyA9IGxvbiwgbGF0X29yaWcgPSBsYXQsIG5vbWJyZV9vcmlnID0gbm9tYnJlKSB8Pg0KICAgIGxlZnRfam9pbihub2RvcywgYnkgPSBjKCJjb211bmFfZGVzdGlubyIgPSAiY29tdW5hIikpIHw+DQogICAgcmVuYW1lKGxvbl9kZXN0ID0gbG9uLCBsYXRfZGVzdCA9IGxhdCwgbm9tYnJlX2Rlc3QgPSBub21icmUpIHw+DQogICAgZmlsdGVyKCFpcy5uYShsb25fb3JpZyksICFpcy5uYShsb25fZGVzdCkpDQp9DQoNCmZsdWpvc19nZW5lcmFsICAgPC0gY29uc3RydWlyX2ZsdWpvcyhlbmN1ZXN0YV9jYWxpLCB0aXBvX3ZlaCA9IE5VTEwpDQpmbHVqb3NfYmljaWNsZXRhIDwtIGNvbnN0cnVpcl9mbHVqb3MoZW5jdWVzdGFfY2FsaSwgdGlwb192ZWggPSAxKQ0KZmx1am9zX21vdG8gICAgICA8LSBjb25zdHJ1aXJfZmx1am9zKGVuY3Vlc3RhX2NhbGksIHRpcG9fdmVoID0gMikNCmZsdWpvc19hdXRvbW92aWwgPC0gY29uc3RydWlyX2ZsdWpvcyhlbmN1ZXN0YV9jYWxpLCB0aXBvX3ZlaCA9IDMpDQpgYGANCg0KYGBge3IgZnVuY2lvbl9tYXBhX2ludGVyYWN0aXZvfQ0KIyAtLS0tIEZ1bmNpw7NuIHBhcmEgZ2VuZXJhciBtYXBhIGludGVyYWN0aXZvIGxlYWZsZXQgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KbWFwYV9pbnRlcmFjdGl2byA8LSBmdW5jdGlvbihmbHVqb3MsIG5vZG9zX2RmLCBzaHBfd2dzLCBjb2xvcl9saW5lYSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdHVsb19wb3B1cCkgew0KDQogIG1heF92ICA8LSBtYXgoZmx1am9zJHZpYWplcywgMSkNCiAgZXNjYWxhIDwtIGZ1bmN0aW9uKHYpIDEgKyAodiAvIG1heF92KSAqIDggICAjIGdyb3NvciBlbnRyZSAxIHkgOSBweA0KDQogICMgUGFsZXRhIGNvcm9wbGV0YSBwYXJhIGxvcyBwb2zDrWdvbm9zIGRlIGZvbmRvDQogIHBhbF9wb2x5IDwtIGNvbG9yTnVtZXJpYygiWWxPclJkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvbWFpbiA9IGMoMCwgbWF4KG5vZG9zX2RmJGxvbiwgbmEucm0gPSBUUlVFKSkpDQoNCiAgbSA8LSBsZWFmbGV0KG9wdGlvbnMgPSBsZWFmbGV0T3B0aW9ucyh6b29tQ29udHJvbCA9IFRSVUUpKSB8Pg0KICAgIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJENhcnRvREIuUG9zaXRyb24pIHw+DQoNCiAgICAjIFBvbMOtZ29ub3MgZGUgY29tdW5hcyBjb21vIGZvbmRvIHJlZmVyZW5jaWFsDQogICAgYWRkUG9seWdvbnMoDQogICAgICBkYXRhICAgICAgICA9IHNocF93Z3MsDQogICAgICBmaWxsQ29sb3IgICA9ICIjZjBmMGYwIiwNCiAgICAgIGZpbGxPcGFjaXR5ID0gMC40LA0KICAgICAgY29sb3IgICAgICAgPSAiIzg4ODg4OCIsDQogICAgICB3ZWlnaHQgICAgICA9IDEsDQogICAgICBsYWJlbCAgICAgICA9IH5wYXN0ZTAoIkNvbXVuYSAiLCBjb211bmEsICI6ICIsIG5vbWJyZSksDQogICAgICBsYWJlbE9wdGlvbnMgPSBsYWJlbE9wdGlvbnMoc3R5bGUgPSBsaXN0KCJmb250LXNpemUiID0gIjEycHgiKSkNCiAgICApDQoNCiAgIyBEaWJ1amFyIGzDrW5lYXMgZGUgZmx1am8gT0QNCiAgZm9yIChpIGluIHNlcV9sZW4obnJvdyhmbHVqb3MpKSkgew0KICAgIGZpbGEgPC0gZmx1am9zW2ksIF0NCiAgICBtIDwtIG0gfD4NCiAgICAgIGFkZFBvbHlsaW5lcygNCiAgICAgICAgbG5nICAgICA9IGMoZmlsYSRsb25fb3JpZywgZmlsYSRsb25fZGVzdCksDQogICAgICAgIGxhdCAgICAgPSBjKGZpbGEkbGF0X29yaWcsIGZpbGEkbGF0X2Rlc3QpLA0KICAgICAgICBjb2xvciAgID0gY29sb3JfbGluZWEsDQogICAgICAgIHdlaWdodCAgPSBlc2NhbGEoZmlsYSR2aWFqZXMpLA0KICAgICAgICBvcGFjaXR5ID0gMC41NSwNCiAgICAgICAgcG9wdXAgICA9IHBhc3RlMCgNCiAgICAgICAgICAiPGI+IiwgdGl0dWxvX3BvcHVwLCAiPC9iPjxicj4iLA0KICAgICAgICAgICJPcmlnZW46IDxiPiIsIGZpbGEkbm9tYnJlX29yaWcsICI8L2I+PGJyPiIsDQogICAgICAgICAgIkRlc3Rpbm86IDxiPiIsIGZpbGEkbm9tYnJlX2Rlc3QsICI8L2I+PGJyPiIsDQogICAgICAgICAgIlZpYWplczogPGI+IiwgZmlsYSR2aWFqZXMsICI8L2I+Ig0KICAgICAgICApDQogICAgICApDQogIH0NCg0KICAjIE5vZG9zIChjZW50cm9pZGVzIGRlIGNvbXVuYXMpDQogIG0gPC0gbSB8Pg0KICAgIGFkZENpcmNsZU1hcmtlcnMoDQogICAgICBkYXRhICAgICAgICA9IG5vZG9zX2RmLA0KICAgICAgbG5nICAgICAgICAgPSB+bG9uLA0KICAgICAgbGF0ICAgICAgICAgPSB+bGF0LA0KICAgICAgcmFkaXVzICAgICAgPSA2LA0KICAgICAgY29sb3IgICAgICAgPSBjb2xvcl9saW5lYSwNCiAgICAgIGZpbGxDb2xvciAgID0gIndoaXRlIiwNCiAgICAgIGZpbGxPcGFjaXR5ID0gMSwNCiAgICAgIHdlaWdodCAgICAgID0gMiwNCiAgICAgIHBvcHVwICAgICAgID0gfnBhc3RlMCgiPGI+Iiwgbm9tYnJlLCAiPC9iPjxicj5Db211bmEgIiwgY29tdW5hKQ0KICAgICkgfD4NCiAgICBhZGRMZWdlbmQoDQogICAgICBwb3NpdGlvbiA9ICJib3R0b21yaWdodCIsDQogICAgICBjb2xvcnMgICA9IGNvbG9yX2xpbmVhLA0KICAgICAgbGFiZWxzICAgPSAiRmx1am8gb3JpZ2VuLWRlc3Rpbm8iLA0KICAgICAgdGl0bGUgICAgPSAiVGlwbyBkZSBmbHVqbyIsDQogICAgICBvcGFjaXR5ICA9IDAuOA0KICAgICkNCg0KICByZXR1cm4obSkNCn0NCmBgYA0KDQojIyBNYXBhIEludGVyYWN0aXZvOiBGbHVqb3MgR2VuZXJhbGVzIE9yaWdlbi1EZXN0aW5vDQoNCmBgYHtyIG1hcGFfaW50X2dlbmVyYWwsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTZ9DQptYXBhX2ludGVyYWN0aXZvKA0KICBmbHVqb3MgICAgICAgPSBmbHVqb3NfZ2VuZXJhbCwNCiAgbm9kb3NfZGYgICAgID0gbm9kb3MsDQogIHNocF93Z3MgICAgICA9IGNvbXVuYXNfd2dzLA0KICBjb2xvcl9saW5lYSAgPSBwYWxfaW50ZXJhY3Rpdm8kZ2VuZXJhbCwNCiAgdGl0dWxvX3BvcHVwID0gIkZsdWpvIEdlbmVyYWwgKHRvZG9zIGxvcyBtb2RvcykiDQopDQpgYGANCg0KYHIgbnVldmFfZmlndXJhKCJNYXBhIGludGVyYWN0aXZvOiBUb3AgNDAgZmx1am9zIG9yaWdlbi1kZXN0aW5vIGdlbmVyYWxlcyBlbnRyZSBjb211bmFzIGRlIENhbGkgMjAxNSIpYA0KDQoqKkludGVycHJldGFjacOzbjoqKiBFbCBtYXBhIGludGVyYWN0aXZvIGRlIGZsdWpvcyBnZW5lcmFsZXMgcGVybWl0ZSBleHBsb3JhciBsb3MgcGFyZXMgb3JpZ2VuLWRlc3Rpbm8gY29uIG1heW9yIHZvbHVtZW4gZGUgdmlhamVzIGVuIGxhIGNpdWRhZCwgaW5kZXBlbmRpZW50ZW1lbnRlIGRlbCBtb2RvIGRlIHRyYW5zcG9ydGUuIExhcyBsw61uZWFzIG3DoXMgZ3J1ZXNhcyB5IG9zY3VyYXMgcmVwcmVzZW50YW4gbG9zIGNvcnJlZG9yZXMgZGUgbWF5b3IgZGVtYW5kYSBkZSBtb3ZpbGlkYWQuIEFsIGhhY2VyIGNsaWMgc29icmUgY3VhbHF1aWVyIGzDrW5lYSBzZSBkZXNwbGllZ2EgbGEgaW5mb3JtYWNpw7NuIGRlbCBwYXIgZGUgY29tdW5hcyB5IGVsIG7Dum1lcm8gZGUgdmlhamVzIHJlZ2lzdHJhZG9zLiBMYSBlc3RydWN0dXJhIGRlIGxvcyBmbHVqb3MgcmV2ZWxhIGxvcyBwcmluY2lwYWxlcyBlamVzIGRlIG1vdmlsaWRhZCBkZSBsYSBjaXVkYWQgeSBwZXJtaXRlIGlkZW50aWZpY2FyIHZpc3VhbG1lbnRlIGxhcyBjZW50cmFsaWRhZGVzIHF1ZSBhY3TDumFuIGNvbW8gcG9sb3MgZGUgYXRyYWNjacOzbiBkb21pbmFudGVzLCBhc8OtIGNvbW8gbGFzIGNvbXVuYXMgcGVyaWbDqXJpY2FzIHF1ZSBnZW5lcmFuIGxvcyBtYXlvcmVzIHZvbMO6bWVuZXMgZGUgdmlhamVzIGhhY2lhIGVzYXMgY2VudHJhbGlkYWRlcy4NCg0KIyMgTWFwYSBJbnRlcmFjdGl2bzogRmx1am9zIE9yaWdlbi1EZXN0aW5vIGVuIEJpY2ljbGV0YQ0KDQpgYGB7ciBtYXBhX2ludF9iaWNpY2xldGEsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTZ9DQptYXBhX2ludGVyYWN0aXZvKA0KICBmbHVqb3MgICAgICAgPSBmbHVqb3NfYmljaWNsZXRhLA0KICBub2Rvc19kZiAgICAgPSBub2RvcywNCiAgc2hwX3dncyAgICAgID0gY29tdW5hc193Z3MsDQogIGNvbG9yX2xpbmVhICA9IHBhbF9pbnRlcmFjdGl2byRiaWNpY2xldGEsDQogIHRpdHVsb19wb3B1cCA9ICJGbHVqbyBlbiBCaWNpY2xldGEiDQopDQpgYGANCg0KYHIgbnVldmFfZmlndXJhKCJNYXBhIGludGVyYWN0aXZvOiBUb3AgNDAgZmx1am9zIG9yaWdlbi1kZXN0aW5vIGVuIGJpY2ljbGV0YSBlbnRyZSBjb211bmFzIGRlIENhbGkgMjAxNSIpYA0KDQoqKkludGVycHJldGFjacOzbjoqKiBFbCBtYXBhIGRlIGZsdWpvcyBlbiBiaWNpY2xldGEgbXVlc3RyYSBsb3MgY29ycmVkb3JlcyBkb25kZSBlc3RlIG1vZG8gZGUgdHJhbnNwb3J0ZSB0aWVuZSBtYXlvciBwcmVzZW5jaWEgY29tbyBhbHRlcm5hdGl2YSBkZSBtb3ZpbGlkYWQgY290aWRpYW5hLiBMYSBjb25jZW50cmFjacOzbiBkZSBsw61uZWFzIGRlIGZsdWpvIGVuIGRldGVybWluYWRhcyB6b25hcyBkZSBsYSBjaXVkYWQgcGVybWl0ZSBpZGVudGlmaWNhciBsb3MgdHJhbW9zIGNvbiBtYXlvciBkZW1hbmRhIGNpY2xpc3RhLCBpbmZvcm1hY2nDs24gcXVlIHJlc3VsdGEgZnVuZGFtZW50YWwgcGFyYSBwcmlvcml6YXIgbGEgY29uc3RydWNjacOzbiB5IGVsIG1hbnRlbmltaWVudG8gZGUgY2ljbG92w61hcy4gVW5hIGFsdGEgZGVuc2lkYWQgZGUgZmx1am9zIGVuIHpvbmFzIHNpbiBpbmZyYWVzdHJ1Y3R1cmEgY2ljbGlzdGEgc2XDsWFsYSB1bmEgYnJlY2hhIGVudHJlIGxhIGRlbWFuZGEgcmVhbCB5IGxhIG9mZXJ0YSBkZSBpbmZyYWVzdHJ1Y3R1cmEsIHNpdHVhY2nDs24gcXVlIGV4cG9uZSBhIGxvcyBjaWNsaXN0YXMgYSBjb25kaWNpb25lcyBkZSByaWVzZ28gdmlhbCBxdWUgZGViZW4gc2VyIGF0ZW5kaWRhcyBkZXNkZSBsYSBwbGFuaWZpY2FjacOzbiB1cmJhbmEuDQoNCiMjIE1hcGEgSW50ZXJhY3Rpdm86IEZsdWpvcyBPcmlnZW4tRGVzdGlubyBlbiBNb3RvDQoNCmBgYHtyIG1hcGFfaW50X21vdG8sIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTZ9DQptYXBhX2ludGVyYWN0aXZvKA0KICBmbHVqb3MgICAgICAgPSBmbHVqb3NfbW90bywNCiAgbm9kb3NfZGYgICAgID0gbm9kb3MsDQogIHNocF93Z3MgICAgICA9IGNvbXVuYXNfd2dzLA0KICBjb2xvcl9saW5lYSAgPSBwYWxfaW50ZXJhY3Rpdm8kbW90bywNCiAgdGl0dWxvX3BvcHVwID0gIkZsdWpvIGVuIE1vdG8iDQopDQpgYGANCg0KYHIgbnVldmFfZmlndXJhKCJNYXBhIGludGVyYWN0aXZvOiBUb3AgNDAgZmx1am9zIG9yaWdlbi1kZXN0aW5vIGVuIG1vdG8gZW50cmUgY29tdW5hcyBkZSBDYWxpIDIwMTUiKWANCg0KKipJbnRlcnByZXRhY2nDs246KiogTG9zIGZsdWpvcyBlbiBtb3RvIGV2aWRlbmNpYW4gbGEgZXN0cnVjdHVyYSBkZSBtb3ZpbGlkYWQgZGUgdW5vIGRlIGxvcyBtb2RvcyBkZSBtYXlvciBjcmVjaW1pZW50byBlbiBsYSBjaXVkYWQuIExhIGRpcmVjY2lvbmFsaWRhZCBkZSBsYXMgbMOtbmVhcyBwZXJtaXRlIGNvbmZpcm1hciBvIGRlc2NhcnRhciBsYSBoaXDDs3Rlc2lzIGRlIHVuIHBhdHLDs24gcmFkaWFsIHBlcmlmw6lyaWNvLWNlbnRyYWwsIGVuIGVsIHF1ZSBsYXMgY29tdW5hcyBkZSBlc3RyYXRvcyBiYWpvcyB1YmljYWRhcyBlbiBsYSBwZXJpZmVyaWEgZ2VuZXJhbiB2aWFqZXMgaGFjaWEgbGFzIGNlbnRyYWxpZGFkZXMgZWNvbsOzbWljYXMgeSBjb21lcmNpYWxlcy4gTGEgaWRlbnRpZmljYWNpw7NuIGRlIGxvcyBjb3JyZWRvcmVzIGNvbiBtYXlvciBpbnRlbnNpZGFkIGRlIGZsdWpvcyBlbiBtb3RvIGVzIHVuIGluc3VtbyBkaXJlY3RvIHBhcmEgbGEgZ2VzdGnDs24gZGUgbGEgdmVsb2NpZGFkLCBlbCBkaXNlw7FvIGRlIGludGVyc2VjY2lvbmVzIHNlZ3VyYXMgeSBsYSBwbGFuaWZpY2FjacOzbiBkZSBvcGVyYXRpdm9zIGRlIGNvbnRyb2wgZGUgdHLDoWZpY28gZW4gbG9zIGVqZXMgZGUgbWF5b3Igc2luaWVzdHJhbGlkYWQuDQoNCiMjIE1hcGEgSW50ZXJhY3Rpdm86IEZsdWpvcyBPcmlnZW4tRGVzdGlubyBlbiBBdXRvbcOzdmlsDQoNCmBgYHtyIG1hcGFfaW50X2F1dG9tb3ZpbCwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9Nn0NCm1hcGFfaW50ZXJhY3Rpdm8oDQogIGZsdWpvcyAgICAgICA9IGZsdWpvc19hdXRvbW92aWwsDQogIG5vZG9zX2RmICAgICA9IG5vZG9zLA0KICBzaHBfd2dzICAgICAgPSBjb211bmFzX3dncywNCiAgY29sb3JfbGluZWEgID0gcGFsX2ludGVyYWN0aXZvJGF1dG9tb3ZpbCwNCiAgdGl0dWxvX3BvcHVwID0gIkZsdWpvIGVuIEF1dG9tb3ZpbCINCikNCmBgYA0KDQpgciBudWV2YV9maWd1cmEoIk1hcGEgaW50ZXJhY3Rpdm86IFRvcCA0MCBmbHVqb3Mgb3JpZ2VuLWRlc3Rpbm8gZW4gYXV0b23Ds3ZpbCBlbnRyZSBjb211bmFzIGRlIENhbGkgMjAxNSIpYA0KDQoqKkludGVycHJldGFjacOzbjoqKiBFbCBtYXBhIGRlIGZsdWpvcyBlbiBhdXRvbcOzdmlsIHJldmVsYSBsb3MgY29ycmVkb3JlcyBkb25kZSBsYSBkZW1hbmRhIGRlIHZlaMOtY3VsbyBwcml2YWRvIGVzIG3DoXMgaW50ZW5zYSwgbG8gY3VhbCBzZSB0cmFkdWNlIGRpcmVjdGFtZW50ZSBlbiBsb3MgcHVudG9zIGRlIG1heW9yIGNvbmdlc3Rpw7NuIHZpYWwgZGUgbGEgY2l1ZGFkLiBMYSBzdXBlcnBvc2ljacOzbiBkZSBtw7psdGlwbGVzIGzDrW5lYXMgZGUgZmx1am8gZW4gZGV0ZXJtaW5hZG9zIGNvcnJlZG9yZXMgaW5kaWNhIHF1ZSBlc2FzIHbDrWFzIHNvcG9ydGFuIHNpbXVsdMOhbmVhbWVudGUgdmFyaW9zIHBhcmVzIG9yaWdlbi1kZXN0aW5vIGRlIGFsdGEgZGVtYW5kYSwgZ2VuZXJhbmRvIHVuYSBwcmVzacOzbiBhY3VtdWxhZGEgc29icmUgbGEgY2FwYWNpZGFkIHZpYWwgcXVlIGp1c3RpZmljYSBpbnRlcnZlbmNpb25lcyBkZSBnZXN0acOzbiBkZWwgdHLDoWZpY28uIEVzdGUgbWFwYSwgY29tYmluYWRvIGNvbiBsb3MgZGUgYmljaWNsZXRhIHkgbW90bywgcGVybWl0ZSBjb25zdHJ1aXIgdW5hIHZpc2nDs24gaW50ZWdyYWwgZGUgbGEgZGVtYW5kYSBtdWx0aW1vZGFsIGRlIG1vdmlsaWRhZCBlbiBDYWxpIHkgb3JpZW50YXIgbGFzIHBvbMOtdGljYXMgZGUgdHJhbnNwb3J0ZSBoYWNpYSB1biBlcXVpbGlicmlvIG3DoXMgc29zdGVuaWJsZSBlbnRyZSBsb3MgZGlzdGludG9zIG1vZG9zLg0KDQojICoqQ29uY2x1c2lvbmVzKioNCg0KRW4gZXN0ZSBjYXDDrXR1bG8gc2Ugc2ludGV0aXphbiBsb3MgaGFsbGF6Z29zIGRlbCBhbsOhbGlzaXMgZXNwYWNpYWwgeSBzZSBwbGFudGVhbiBsYXMgaW1wbGljYWNpb25lcyBwYXJhIGxhIHBsYW5pZmljYWNpw7NuIGRlIGxhIG1vdmlsaWRhZCB1cmJhbmEgZW4gQ2FsaS4NCg0KTG9zIG9jaG8gbWFwYXMgZGUgY29yb3BsZXRhcyB5IGxvcyBjdWF0cm8gbWFwYXMgaW50ZXJhY3Rpdm9zIG9yaWdlbi1kZXN0aW5vIGdlbmVyYWRvcyBhIHBhcnRpciBkZSBsYSBFbmN1ZXN0YSBPcmlnZW4tRGVzdGlubyBkZSBDYWxpIDIwMTUgY29uc3RpdHV5ZW4gdW5hIGhlcnJhbWllbnRhIGludGVncmFsIGRlIGRpYWduw7NzdGljbyBlc3BhY2lhbCBxdWUgcGVybWl0ZSBpZGVudGlmaWNhciwgZGUgbWFuZXJhIHZpc3VhbCB5IHNpc3RlbcOhdGljYSwgbG9zIHBhdHJvbmVzIGRlIG1vdmlsaWRhZCB1cmJhbmEgZGlzdHJpYnVpZG9zIGVuIGxhcyAyMiBjb211bmFzIGRlIGxhIGNpdWRhZC4gRWwgcHJvY2VzbyBtZXRvZG9sw7NnaWNvIHNlZ3VpZG8gaWx1c3RyYSBkZSBtYW5lcmEgcHLDoWN0aWNhIGVsIHByaW5jaXBpbyBmdW5kYW1lbnRhbCBkZSBsb3MgU0lHOiBsYSBpbnRlZ3JhY2nDs24gZGUgZGF0b3MgZXNwYWNpYWxlcyAoZWwgc2hhcGVmaWxlIGRlIGNvbXVuYXMpIGNvbiBkYXRvcyB0ZW3DoXRpY29zIChsb3MgYXRyaWJ1dG9zIGRlIGxhIGVuY3Vlc3RhKSBwYXJhIGdlbmVyYXIgbnVldmEgaW5mb3JtYWNpw7NuIGNvbiB2YWxvciBhbmFsw610aWNvIHkgZGUgYXBveW8gYSBsYSB0b21hIGRlIGRlY2lzaW9uZXMuDQoNCkxhIGxlY3R1cmEgY29tcGFyYWRhIGRlIGxvcyBtYXBhcyBkZSBvcmlnZW4geSBkZXN0aW5vIHBvciB0aXBvIGRlIHZlaMOtY3VsbyBwZXJtaXRlIGlkZW50aWZpY2FyIGFzaW1ldHLDrWFzIHRlcnJpdG9yaWFsZXMgcXVlIHNvbiBjbGF2ZXMgcGFyYSBsYSBwbGFuaWZpY2FjacOzbjogbGEgYmljaWNsZXRhIGRlbWFuZGEgcmV2aXNpw7NuIGRlIGxhIGNvbmVjdGl2aWRhZCBjaWNsaXN0YSBlbnRyZSBzdXMgem9uYXMgZGUgb3JpZ2VuIHkgZGVzdGlubyBtw6FzIGZyZWN1ZW50ZXM7IGxhIG1vdG8gcHJlc2VudGEgdW4gcGF0csOzbiByYWRpYWwgcXVlIGNvbmNlbnRyYSBsYSBkZW1hbmRhIHNvYnJlIGNvcnJlZG9yZXMgZXNwZWPDrWZpY29zIGRlIGNvbmV4acOzbiBwZXJpZmVyaWEtY2VudHJvOyB5IGVsIGF1dG9tw7N2aWwgcmV2ZWxhIGxhIGNvbmNlbnRyYWNpw7NuIGRlIGxhIG1vdG9yaXphY2nDs24gZW4gY29tdW5hcyBkZSBlc3RyYXRvcyBtZWRpb3MgeSBhbHRvcywgY29uIGxhcyBleHRlcm5hbGlkYWRlcyBkZSBjb25nZXN0acOzbiBxdWUgZWxsbyBjb25sbGV2YS4NCg0KTG9zIG1hcGFzIGludGVyYWN0aXZvcyBjb21wbGVtZW50YW4gZWwgYW7DoWxpc2lzIGVzdMOhdGljbyBhbCBwZXJtaXRpciBleHBsb3JhciBkZSBmb3JtYSBkaW7DoW1pY2EgbG9zIHBhcmVzIG9yaWdlbi1kZXN0aW5vIGRvbWluYW50ZXMgcGFyYSBjYWRhIG1vZG8sIGlkZW50aWZpY2FyIHZpc3VhbG1lbnRlIGxvcyBjb3JyZWRvcmVzIGRlIG1heW9yIGludGVuc2lkYWQgZGUgZmx1am8geSBkZXNjdWJyaXIgcGF0cm9uZXMgZGUgbW92aWxpZGFkIHF1ZSBubyBzb24gZXZpZGVudGVzIGVuIGxhcyByZXByZXNlbnRhY2lvbmVzIGFncmVnYWRhcyBwb3IgY29tdW5hLiBFc3RhIGNhcGFjaWRhZCBkZSBleHBsb3JhY2nDs24gaW50ZXJhY3RpdmEgcmVwcmVzZW50YSBlbCB2YWxvciBhZ3JlZ2FkbyBxdWUgbG9zIFNJRyBtb2Rlcm5vcyBhcG9ydGFuIGEgbGEgcGxhbmlmaWNhY2nDs24gZGVsIHRyYW5zcG9ydGUgdXJiYW5vLCBhbCBhY2VyY2FyIGVsIGFuw6FsaXNpcyBkZSBkYXRvcyBjb21wbGVqb3MgYSBsb3MgdG9tYWRvcmVzIGRlIGRlY2lzaW9uZXMgZGUgbWFuZXJhIGludHVpdGl2YSB5IGFjY2VzaWJsZS4NCg0KRGVzZGUgZWwgcHVudG8gZGUgdmlzdGEgY29uY2VwdHVhbCwgZWwgYW7DoWxpc2lzIHNlIGVubWFyY2EgZGVudHJvIGRlbCBhbsOhbGlzaXMgZGUgZGF0b3MgZGUgw6FyZWEsIGVuIGVsIHF1ZSBsYSB1bmlkYWQgZGUgb2JzZXJ2YWNpw7NuIGVzIGxhIGNvbXVuYSBjb21vIHVuaWRhZCBhZG1pbmlzdHJhdGl2YSBwb2xpZ29uYWwuIEVzdGEgZWxlY2Npw7NuIGRlIGVzY2FsYSBlcyBhcHJvcGlhZGEgcGFyYSBlbCBkaWFnbsOzc3RpY28gZ2VuZXJhbCBkZSBsYSBtb3ZpbGlkYWQsIGF1bnF1ZSBubyBjYXB0dXJhIGxhIHZhcmlhYmlsaWRhZCBpbnRyYWNvbXVuYS4gTG9zIHJlc3VsdGFkb3MgZGViZW4gaW50ZXJwcmV0YXJzZSBjb21vIHVuYSBhcHJveGltYWNpw7NuIGEgbGEgZGVtYW5kYSBhZ3JlZ2FkYSBkZSBtb3ZpbGlkYWQgZW4gMjAxNSwgcmVjb25vY2llbmRvIHF1ZSBsYXMgY29uZGljaW9uZXMgdXJiYW5hcyBkZSBsYSBjaXVkYWQgaGFuIHBvZGlkbyBldm9sdWNpb25hciBkZXNkZSBlbnRvbmNlcy4NCg==