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.
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.)
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.
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.
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.
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.
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.
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.
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.
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.
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.
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")
}
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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)
}
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.
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.
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.
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.
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==