library(sf)
library(dplyr)
library(leaflet)
library(htmltools)
library(readxl)
library(knitr)

Resumen. Este trabajo analiza la distribución espacial de los viajes registrados en la Encuesta de Origen y Destino de Cali (2015). Los viajes se asignan a las 22 comunas urbanas a partir de los códigos de ubicación de la encuesta, previa depuración de las categorías ambiguas, y se representan en mapas coropléticos interactivos de origen, destino y conexión con otros municipios del área metropolitana.

1 Introducción

Conocer dónde empiezan y terminan los viajes es un insumo básico para la planeación del transporte: permite identificar las zonas que generan y atraen más movilidad y qué tanto depende cada sector de la ciudad de su relación con los municipios vecinos.

El objetivo de esta actividad es representar espacialmente el origen y el destino de los viajes encuestados en Cali por comuna, y responder tres preguntas:

  1. ¿Qué comunas concentran el mayor número de viajes de origen y de destino?
  2. ¿Qué proporción de los viajes de cada comuna la conecta con otros municipios?
  3. ¿Con qué municipios se relaciona Cali con mayor frecuencia?

2 Datos

Se utilizan dos fuentes:

Fuente Descripción Formato
Encuesta de Origen y Destino de Cali Encuesta de interceptación de vehículos realizada en 35 estaciones entre mayo y junio de 2015 .xlsx
Comunas de Cali Polígonos de las 22 comunas del perímetro urbano Shapefile (.shp)

La capa de comunas se reproyecta a WGS84 (EPSG:4326), el sistema de referencia que requiere Leaflet.

Nota metodológica. Los conteos corresponden a viajes encuestados, no al total de viajes de la ciudad. Como la encuesta es de interceptación, los resultados también reflejan la ubicación de las estaciones de aforo y deben interpretarse como un indicador relativo, no como una estimación de la demanda total.

comunas=read_sf("Casos/cali/Comunas.shp") %>%
  st_transform(4326)
encuesta=read_xlsx("Casos/EncuestaOrigenDestino.xlsx")

viajes_brutos=encuesta[,c(7,9,11,13,26,27)]
names(viajes_brutos)=c("mun_origen","sdg_origen","mun_destino","sdg_destino",
                       "comuna_origen","comuna_destino")

Las variables seleccionadas son el municipio, el código SDG (código de ubicación de la encuesta) y la comuna, tanto para el origen como para el destino de cada viaje.

3 Metodología

3.1 Depuración de la variable comuna

La columna de comuna de la encuesta no se puede usar directamente, porque contiene dos categorías que mezclan casos distintos. La reclasificación se apoya en el código SDG, cuyos primeros dígitos identifican la comuna del lugar.

  • Comuna 0. Cali no tiene comuna 0. Casi todos los registros con este valor tienen un código SDG que empieza por 26, que corresponde a corregimientos como Pance, La Buitrera y Montebello; estos se agrupan como Zona rural. Los pocos cuyo código empieza por el número de una comuna se reasignan a esa comuna.
  • Fuera de Cali. Esta categoría incluye lugares dentro de la ciudad cuyo código se perdió (código SDG Perdida con municipio CALI), como la Avenida Roosevelt o La 14. Estos se separan como Sin ubicar; el resto se mantiene como Fuera de Cali.
  • Registros anulados. Se eliminan los registros marcados como ANULADO.
clasificar=function(comuna, sdg, municipio) {
  prefijo=substr(sdg, 1, nchar(sdg) - 2)
  municipio=toupper(trimws(municipio))
  case_when(
    comuna %in% sprintf("%02d", 1:22) ~ sub("^0+", "", comuna),
    prefijo == "26" ~ "Zona rural",
    comuna == "0" & prefijo %in% as.character(1:22) ~ prefijo,
    sdg == "Perdida" & (is.na(municipio) | municipio %in% c("CALI", "SIN DATOS")) ~ "Sin ubicar",
    TRUE ~ "Fuera de Cali"
  )
}

viajes=viajes_brutos %>%
  filter(!(mun_origen %in% "ANULADO" | mun_destino %in% "ANULADO")) %>%
  mutate(
    zona_origen=clasificar(comuna_origen, sdg_origen, mun_origen),
    zona_destino=clasificar(comuna_destino, sdg_destino, mun_destino)
  )

n_anulados=nrow(viajes_brutos) - nrow(viajes)
miles=function(x) format(x, big.mark = ".", decimal.mark = ",", trim = TRUE)
porcentaje=function(x) ifelse(is.na(x), "—", sub(".", ",", sprintf("%.1f%%", x), fixed = TRUE))

De los 35.054 registros originales se eliminaron 33 anulados, de modo que el análisis trabaja con 35.021 viajes. La tabla 1 muestra cómo quedan distribuidos tras la reclasificación.

tipo_zona=function(z) ifelse(z %in% as.character(1:22), "Comunas urbanas (1–22)", z)
orden_zonas=c("Comunas urbanas (1–22)", "Zona rural", "Sin ubicar", "Fuera de Cali")

full_join(
  count(viajes, Zona = tipo_zona(zona_origen), name = "Origen"),
  count(viajes, Zona = tipo_zona(zona_destino), name = "Destino"),
  by = "Zona"
) %>%
  mutate(across(c(Origen, Destino), ~coalesce(.x, 0L)),
         Zona = factor(Zona, levels = orden_zonas)) %>%
  arrange(Zona) %>%
  mutate(`% origen` = porcentaje(100 * Origen / sum(Origen)),
         `% destino` = porcentaje(100 * Destino / sum(Destino)),
         across(c(Origen, Destino), miles)) %>%
  select(Zona, Origen, `% origen`, Destino, `% destino`) %>%
  kable(align = "lrrrr",
        caption = "Tabla 1. Viajes por tipo de zona después de la depuración")
Tabla 1. Viajes por tipo de zona después de la depuración
Zona Origen % origen Destino % destino
Comunas urbanas (1–22) 28.093 80,2% 27.878 79,6%
Zona rural 695 2,0% 786 2,2%
Sin ubicar 803 2,3% 1.094 3,1%
Fuera de Cali 5.430 15,5% 5.263 15,0%

3.2 Indicadores por comuna

Para cada comuna se calculan:

  • Viajes de origen y viajes de destino: número de viajes encuestados que empiezan o terminan en la comuna.
  • Hacia fuera y desde fuera: viajes cuyo otro extremo está en otro municipio.
  • Conexión con otros municipios (%): proporción de los viajes de la comuna que la conectan con otro municipio,

\[ \%\,\text{otros municipios}_i = 100 \times \frac{\text{hacia fuera}_i + \text{desde fuera}_i}{\text{origen}_i + \text{destino}_i} \]

conteo_origen=viajes %>%
  count(zona_origen, name = "viajes_origen")

conteo_destino=viajes %>%
  count(zona_destino, name = "viajes_destino")

hacia_fuera=viajes %>%
  filter(zona_destino == "Fuera de Cali") %>%
  count(zona_origen, name = "hacia_fuera")

desde_fuera=viajes %>%
  filter(zona_origen == "Fuera de Cali") %>%
  count(zona_destino, name = "desde_fuera")

comunas_final=comunas %>%
  mutate(zona=as.character(comuna)) %>%
  left_join(conteo_origen, by = c("zona" = "zona_origen")) %>%
  left_join(conteo_destino, by = c("zona" = "zona_destino")) %>%
  left_join(hacia_fuera, by = c("zona" = "zona_origen")) %>%
  left_join(desde_fuera, by = c("zona" = "zona_destino")) %>%
  mutate(
    across(c(viajes_origen, viajes_destino, hacia_fuera, desde_fuera), ~coalesce(.x, 0L)),
    total=viajes_origen + viajes_destino,
    pct_fuera=if_else(total > 0, 100 * (hacia_fuera + desde_fuera) / total, NA_real_)
  ) %>%
  arrange(comuna)

tabla_comunas=st_drop_geometry(comunas_final)
top_origen=slice_max(tabla_comunas, viajes_origen, n = 1, with_ties = FALSE)
top_destino=slice_max(tabla_comunas, viajes_destino, n = 1, with_ties = FALSE)
top_fuera=slice_max(tabla_comunas, pct_fuera, n = 1, with_ties = FALSE)
min_fuera=slice_min(tabla_comunas, pct_fuera, n = 1, with_ties = FALSE)

4 Resultados

4.1 Distribución de origen y destino por comuna

El número sobre cada comuna es la cantidad de viajes. El selector de la esquina superior derecha permite cambiar entre origen y destino; ambas capas usan la misma escala de color, así que son directamente comparables. Al pasar el cursor sobre una comuna se ven sus dos valores, y al hacer clic se abre el detalle.

centros=st_coordinates(st_point_on_surface(st_geometry(comunas_final)))

etiqueta_numero=labelOptions(
  noHide = TRUE, textOnly = TRUE, direction = "center",
  style = list(
    "color" = "#0b0b0b", "font-weight" = "600", "font-size" = "12px",
    "background" = "rgba(255,255,255,0.8)", "border-radius" = "3px", "padding" = "0 3px"
  )
)

hover=with(comunas_final, lapply(sprintf(
  "<b>%s</b><br>Origen: %s<br>Destino: %s",
  nombre, miles(viajes_origen), miles(viajes_destino)
), HTML))

popup=with(comunas_final, sprintf(
  "<b>%s</b><br>Viajes con origen aquí: %s<br>Viajes con destino aquí: %s<br>
  Hacia fuera de Cali: %s<br>Desde fuera de Cali: %s<br>Conexión con otros municipios: %s",
  nombre, miles(viajes_origen), miles(viajes_destino),
  miles(hacia_fuera), miles(desde_fuera), porcentaje(pct_fuera)
))

valores=c(comunas_final$viajes_origen, comunas_final$viajes_destino)
azules=c("#cde2fb", "#9ec5f4", "#6da7ec", "#3987e5", "#256abf", "#184f95", "#0d366b")
paleta=colorBin(azules, domain = valores, bins = pretty(valores, n = 6))

capa=function(mapa, columna, grupo) {
  mapa %>%
    addPolygons(
      fillColor = paleta(comunas_final[[columna]]), fillOpacity = 0.85,
      color = "#ffffff", weight = 1.5, group = grupo,
      label = hover, popup = popup,
      highlightOptions = highlightOptions(weight = 3, color = "#0b0b0b", bringToFront = TRUE)
    ) %>%
    addLabelOnlyMarkers(
      lng = centros[,1], lat = centros[,2], group = grupo,
      label = miles(comunas_final[[columna]]), labelOptions = etiqueta_numero
    )
}

leaflet(comunas_final, width = "100%", height = 680) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  capa("viajes_origen", "Origen") %>%
  capa("viajes_destino", "Destino") %>%
  addLayersControl(
    baseGroups = c("Origen", "Destino"),
    options = layersControlOptions(collapsed = FALSE)
  ) %>%
  addScaleBar(position = "bottomleft", options = scaleBarOptions(imperial = FALSE)) %>%
  addLegend(
    position = "bottomright", pal = paleta, values = valores,
    title = "Viajes encuestados", opacity = 0.85,
    labFormat = labelFormat(big.mark = ".")
  )

Figura 1. Viajes encuestados con origen y destino en cada comuna de Cali. Fuente: elaboración propia con datos de la Encuesta de Origen y Destino de Cali (2015).

La comuna con más viajes de origen es Comuna 2 (2.968 viajes), y la que recibe más viajes de destino es Comuna 2 (4.810 viajes). Dado el diseño de la encuesta, estas diferencias combinan la intensidad real de la movilidad con la cercanía de cada comuna a las estaciones de aforo.

4.1.1 Viajes que no aparecen en el mapa

El shapefile cubre solo el perímetro urbano, por lo que los viajes de la zona rural, de otros municipios o sin ubicación no tienen polígono. Se reportan en la tabla 2 para que no se pierdan del análisis.

zonas_otras=c("Zona rural", "Fuera de Cali", "Sin ubicar")

tibble(Zona = zonas_otras) %>%
  left_join(conteo_origen, by = c("Zona" = "zona_origen")) %>%
  left_join(conteo_destino, by = c("Zona" = "zona_destino")) %>%
  mutate(across(c(viajes_origen, viajes_destino), ~miles(coalesce(.x, 0L)))) %>%
  rename(Origen = viajes_origen, Destino = viajes_destino) %>%
  kable(align = "lrr", caption = "Tabla 2. Viajes sin polígono en el mapa de comunas")
Tabla 2. Viajes sin polígono en el mapa de comunas
Zona Origen Destino
Zona rural 695 786
Fuera de Cali 5.430 5.263
Sin ubicar 803 1.094

4.2 Conexión con otros municipios

Aunque los viajes fuera de Cali no caben en el mapa de comunas, sí muestran qué tan conectada está cada comuna con la región. La figura 2 representa el porcentaje de los viajes de cada comuna que salen hacia otro municipio o llegan desde otro.

valores_fuera=comunas_final$pct_fuera
naranjas=c("#fde3d8", "#f7b89e", "#f08f68", "#eb6834", "#bd4c1f", "#8f3410")
paleta_fuera=colorBin(naranjas, domain = valores_fuera,
                      bins = pretty(valores_fuera, n = 5), na.color = "#e0e0e0")

leaflet(comunas_final, width = "100%", height = 680) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addPolygons(
    fillColor = ~paleta_fuera(pct_fuera), fillOpacity = 0.85,
    color = "#ffffff", weight = 1.5,
    label = hover, popup = popup,
    highlightOptions = highlightOptions(weight = 3, color = "#0b0b0b", bringToFront = TRUE)
  ) %>%
  addLabelOnlyMarkers(
    lng = centros[,1], lat = centros[,2],
    label = ifelse(is.na(valores_fuera), "", sprintf("%.0f%%", valores_fuera)),
    labelOptions = etiqueta_numero
  ) %>%
  addScaleBar(position = "bottomleft", options = scaleBarOptions(imperial = FALSE)) %>%
  addLegend(
    position = "bottomright", pal = paleta_fuera, values = valores_fuera,
    title = "% de viajes con<br>otros municipios", opacity = 0.85,
    labFormat = labelFormat(suffix = "%")
  )

Figura 2. Porcentaje de los viajes de cada comuna que tienen su otro extremo en un municipio distinto de Cali. Fuente: elaboración propia con datos de la Encuesta de Origen y Destino de Cali (2015).

La conexión regional va de 10,7% en Comuna 16 a 20,1% en Comuna 3.

4.2.1 Principales municipios conectados con Cali

La tabla 3 cuenta los viajes con un extremo en Cali (incluida su zona rural) y el otro en un municipio distinto.

en_cali=function(zona) !zona %in% c("Fuera de Cali", "Sin ubicar")
sin_municipio=c("CALI", "SIN DATOS")

hacia_cali=viajes %>%
  filter(zona_origen == "Fuera de Cali", en_cali(zona_destino)) %>%
  count(municipio = toupper(trimws(mun_origen)), name = "Hacia Cali")

desde_cali=viajes %>%
  filter(zona_destino == "Fuera de Cali", en_cali(zona_origen)) %>%
  count(municipio = toupper(trimws(mun_destino)), name = "Desde Cali")

municipios=full_join(hacia_cali, desde_cali, by = "municipio") %>%
  filter(!is.na(municipio), !municipio %in% sin_municipio) %>%
  mutate(
    across(c(`Hacia Cali`, `Desde Cali`), ~coalesce(.x, 0L)),
    Total = `Hacia Cali` + `Desde Cali`,
    Municipio = tools::toTitleCase(tolower(municipio))
  ) %>%
  arrange(desc(Total))

municipios %>%
  slice_head(n = 8) %>%
  transmute(Municipio, `Hacia Cali` = miles(`Hacia Cali`),
            `Desde Cali` = miles(`Desde Cali`), Total = miles(Total)) %>%
  kable(align = "lrrr", caption = "Tabla 3. Ocho municipios con más viajes desde y hacia Cali")
Tabla 3. Ocho municipios con más viajes desde y hacia Cali
Municipio Hacia Cali Desde Cali Total
Yumbo 1.354 1.642 2.996
Jamundi 1.072 667 1.739
Palmira 827 871 1.698
Candelaria 255 278 533
Puerto Tejada 54 155 209
La Union 112 82 194
Santander De Quilichao 80 70 150
Guadalajara De Buga 73 65 138
de_paso=sum(viajes$zona_origen == "Fuera de Cali" & viajes$zona_destino == "Fuera de Cali")

El municipio con mayor intercambio es Yumbo, con 2.996 viajes en ambos sentidos. Además, 636 viajes tienen los dos extremos fuera de Cali: pasan por la ciudad sin empezar ni terminar en ella, por lo que no se asignan a ninguna comuna.

4.3 Resumen por comuna

tabla_comunas %>%
  transmute(
    Comuna = comuna, Nombre = nombre,
    Origen = miles(viajes_origen), Destino = miles(viajes_destino),
    `Hacia fuera` = miles(hacia_fuera), `Desde fuera` = miles(desde_fuera),
    `% otros municipios` = porcentaje(pct_fuera)
  ) %>%
  kable(align = "llrrrrr", caption = "Tabla 4. Indicadores de viajes por comuna")
Tabla 4. Indicadores de viajes por comuna
Comuna Nombre Origen Destino Hacia fuera Desde fuera % otros municipios
1 Comuna 1 794 230 128 38 16,2%
2 Comuna 2 2.968 4.810 568 855 18,3%
3 Comuna 3 2.121 3.859 398 804 20,1%
4 Comuna 4 1.493 1.856 229 302 15,9%
5 Comuna 5 664 522 109 67 14,8%
6 Comuna 6 998 716 159 124 16,5%
7 Comuna 7 734 748 115 113 15,4%
8 Comuna 8 1.124 1.025 187 157 16,0%
9 Comuna 9 952 1.279 150 168 14,3%
10 Comuna 10 1.401 927 212 143 15,2%
11 Comuna 11 925 564 156 62 14,6%
12 Comuna 12 348 246 70 27 16,3%
13 Comuna 13 1.238 673 147 84 12,1%
14 Comuna 14 685 379 82 55 12,9%
15 Comuna 15 1.171 579 151 82 13,3%
16 Comuna 16 993 743 119 66 10,7%
17 Comuna 17 2.382 2.166 442 416 18,9%
18 Comuna 18 1.536 662 235 108 15,6%
19 Comuna 19 2.850 3.159 444 573 16,9%
20 Comuna 20 745 360 94 36 11,8%
21 Comuna 21 818 515 85 59 10,8%
22 Comuna 22 1.153 1.860 127 217 11,4%

5 Discusión y limitaciones

  • Sesgo de interceptación. Al tratarse de una encuesta en 35 estaciones, las comunas cercanas a las estaciones o a los corredores donde se ubicaron tienden a tener más viajes registrados. Los mapas describen la muestra, no la demanda total de la ciudad.
  • Cobertura espacial. El shapefile solo incluye el área urbana. Los viajes rurales, intermunicipales y sin ubicar se reportan en tablas aparte, pero no se representan espacialmente.
  • Calidad de la georreferenciación. La reclasificación depende del código SDG; los registros con código Perdida dentro de Cali no se pueden asignar a una comuna y quedan como Sin ubicar.
  • Antigüedad de los datos. La encuesta es de 2015, por lo que no refleja cambios posteriores en la red de transporte ni en la distribución de la población.

6 Conclusiones

  1. Una vez depurada la variable de comuna, 35.021 viajes encuestados pueden clasificarse por tipo de zona, y la mayoría de los que quedan dentro de Cali se asignan a una de las 22 comunas.
  2. La generación y la atracción de viajes no se distribuyen de forma homogénea: Comuna 2 concentra el mayor número de orígenes y Comuna 2 el mayor número de destinos.
  3. La dependencia de otros municipios varía de forma importante entre comunas, desde 10,7% hasta 20,1%, y Yumbo es el principal municipio conectado con Cali.
  4. Por el diseño de la encuesta, estos resultados deben leerse como patrones relativos de la muestra y complementarse con fuentes de demanda total para fines de planeación.

7 Reproducibilidad

paquetes=c("sf", "dplyr", "leaflet", "htmltools", "readxl", "knitr")
tibble(Paquete = paquetes,
       Versión = sapply(paquetes, function(p) as.character(packageVersion(p)))) %>%
  kable(caption = paste("Tabla 5. Paquetes utilizados —", R.version.string))
Tabla 5. Paquetes utilizados — R version 4.6.1 (2026-06-24 ucrt)
Paquete Versión
sf 1.1.3
dplyr 1.2.1
leaflet 2.2.3
htmltools 0.5.9
readxl 1.6.0
knitr 1.51
LS0tCnRpdGxlOiAiT3JpZ2VuIHkgZGVzdGlubyBkZSBsb3MgdmlhamVzIGVuIFNhbnRpYWdvIGRlIENhbGkiCnN1YnRpdGxlOiAiQWN0aXZpZGFkIDEgwrcgQW7DoWxpc2lzIGRlIERhdG9zIEdlb2VzcGFjaWFsZXMgwrcgTWFlc3Ryw61hIGVuIENpZW5jaWEgZGUgRGF0b3MiCmF1dGhvcjogIkp1YW4gRGllZ28gTmFyYW5qbyIKZGF0ZTogIjIyIGRlIG9jdHVicmUgZGUgMjAyNiIKbGFuZzogZXMKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRoZW1lOiBmbGF0bHkKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduID0gImNlbnRlciIpCmBgYAoKYGBge2NzcywgZWNobz1GQUxTRX0KYm9keSB7IHRleHQtYWxpZ246IGp1c3RpZnk7IH0KLnN1YnRpdGxlIHsgY29sb3I6ICM1YTZiN2I7IGZvbnQtc2l6ZTogMS4xNWVtOyB9Ci5maWdjYXAgeyBmb250LXNpemU6IDAuOWVtOyBjb2xvcjogIzVhNmI3YjsgbWFyZ2luOiA2cHggMCAyMnB4IDA7IH0KY2FwdGlvbiB7IGNvbG9yOiAjMmMzZTUwOyBmb250LXdlaWdodDogNjAwOyB0ZXh0LWFsaWduOiBsZWZ0OyB9Ci5yZXN1bWVuIHsgYmFja2dyb3VuZDogI2Y0ZjdmYTsgYm9yZGVyLWxlZnQ6IDRweCBzb2xpZCAjMmMzZTUwOwogICAgICAgICAgIHBhZGRpbmc6IDEycHggMThweDsgbWFyZ2luOiAxOHB4IDA7IH0KYGBgCgpgYGB7ciBwYXF1ZXRlc30KbGlicmFyeShzZikKbGlicmFyeShkcGx5cikKbGlicmFyeShsZWFmbGV0KQpsaWJyYXJ5KGh0bWx0b29scykKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoa25pdHIpCmBgYAoKPGRpdiBjbGFzcz0icmVzdW1lbiI+CioqUmVzdW1lbi4qKiBFc3RlIHRyYWJham8gYW5hbGl6YSBsYSBkaXN0cmlidWNpw7NuIGVzcGFjaWFsIGRlIGxvcyB2aWFqZXMgcmVnaXN0cmFkb3MgZW4gbGEgRW5jdWVzdGEgZGUgT3JpZ2VuIHkgRGVzdGlubyBkZSBDYWxpICgyMDE1KS4gTG9zIHZpYWplcyBzZSBhc2lnbmFuIGEgbGFzIDIyIGNvbXVuYXMgdXJiYW5hcyBhIHBhcnRpciBkZSBsb3MgY8OzZGlnb3MgZGUgdWJpY2FjacOzbiBkZSBsYSBlbmN1ZXN0YSwgcHJldmlhIGRlcHVyYWNpw7NuIGRlIGxhcyBjYXRlZ29yw61hcyBhbWJpZ3VhcywgeSBzZSByZXByZXNlbnRhbiBlbiBtYXBhcyBjb3JvcGzDqXRpY29zIGludGVyYWN0aXZvcyBkZSBvcmlnZW4sIGRlc3Rpbm8geSBjb25leGnDs24gY29uIG90cm9zIG11bmljaXBpb3MgZGVsIMOhcmVhIG1ldHJvcG9saXRhbmEuCjwvZGl2PgoKIyBJbnRyb2R1Y2Npw7NuCgpDb25vY2VyIGTDs25kZSBlbXBpZXphbiB5IHRlcm1pbmFuIGxvcyB2aWFqZXMgZXMgdW4gaW5zdW1vIGLDoXNpY28gcGFyYSBsYSBwbGFuZWFjacOzbiBkZWwgdHJhbnNwb3J0ZTogcGVybWl0ZSBpZGVudGlmaWNhciBsYXMgem9uYXMgcXVlIGdlbmVyYW4geSBhdHJhZW4gbcOhcyBtb3ZpbGlkYWQgeSBxdcOpIHRhbnRvIGRlcGVuZGUgY2FkYSBzZWN0b3IgZGUgbGEgY2l1ZGFkIGRlIHN1IHJlbGFjacOzbiBjb24gbG9zIG11bmljaXBpb3MgdmVjaW5vcy4KCkVsIG9iamV0aXZvIGRlIGVzdGEgYWN0aXZpZGFkIGVzICoqcmVwcmVzZW50YXIgZXNwYWNpYWxtZW50ZSBlbCBvcmlnZW4geSBlbCBkZXN0aW5vIGRlIGxvcyB2aWFqZXMgZW5jdWVzdGFkb3MgZW4gQ2FsaSBwb3IgY29tdW5hKiosIHkgcmVzcG9uZGVyIHRyZXMgcHJlZ3VudGFzOgoKMS4gwr9RdcOpIGNvbXVuYXMgY29uY2VudHJhbiBlbCBtYXlvciBuw7ptZXJvIGRlIHZpYWplcyBkZSBvcmlnZW4geSBkZSBkZXN0aW5vPwoyLiDCv1F1w6kgcHJvcG9yY2nDs24gZGUgbG9zIHZpYWplcyBkZSBjYWRhIGNvbXVuYSBsYSBjb25lY3RhIGNvbiBvdHJvcyBtdW5pY2lwaW9zPwozLiDCv0NvbiBxdcOpIG11bmljaXBpb3Mgc2UgcmVsYWNpb25hIENhbGkgY29uIG1heW9yIGZyZWN1ZW5jaWE/CgojIERhdG9zCgpTZSB1dGlsaXphbiBkb3MgZnVlbnRlczoKCnwgRnVlbnRlIHwgRGVzY3JpcGNpw7NuIHwgRm9ybWF0byB8Cnw6LS18Oi0tfDotLXwKfCBFbmN1ZXN0YSBkZSBPcmlnZW4geSBEZXN0aW5vIGRlIENhbGkgfCBFbmN1ZXN0YSBkZSBpbnRlcmNlcHRhY2nDs24gZGUgdmVow61jdWxvcyByZWFsaXphZGEgZW4gMzUgZXN0YWNpb25lcyBlbnRyZSBtYXlvIHkganVuaW8gZGUgMjAxNSB8IGAueGxzeGAgfAp8IENvbXVuYXMgZGUgQ2FsaSB8IFBvbMOtZ29ub3MgZGUgbGFzIDIyIGNvbXVuYXMgZGVsIHBlcsOtbWV0cm8gdXJiYW5vIHwgU2hhcGVmaWxlIChgLnNocGApIHwKCkxhIGNhcGEgZGUgY29tdW5hcyBzZSByZXByb3llY3RhIGEgV0dTODQgKEVQU0c6NDMyNiksIGVsIHNpc3RlbWEgZGUgcmVmZXJlbmNpYSBxdWUgcmVxdWllcmUgTGVhZmxldC4KCj4gKipOb3RhIG1ldG9kb2zDs2dpY2EuKiogTG9zIGNvbnRlb3MgY29ycmVzcG9uZGVuIGEgKip2aWFqZXMgZW5jdWVzdGFkb3MqKiwgbm8gYWwgdG90YWwgZGUgdmlhamVzIGRlIGxhIGNpdWRhZC4gQ29tbyBsYSBlbmN1ZXN0YSBlcyBkZSBpbnRlcmNlcHRhY2nDs24sIGxvcyByZXN1bHRhZG9zIHRhbWJpw6luIHJlZmxlamFuIGxhIHViaWNhY2nDs24gZGUgbGFzIGVzdGFjaW9uZXMgZGUgYWZvcm8geSBkZWJlbiBpbnRlcnByZXRhcnNlIGNvbW8gdW4gaW5kaWNhZG9yIHJlbGF0aXZvLCBubyBjb21vIHVuYSBlc3RpbWFjacOzbiBkZSBsYSBkZW1hbmRhIHRvdGFsLgoKYGBge3IgZGF0b3N9CmNvbXVuYXM9cmVhZF9zZigiQ2Fzb3MvY2FsaS9Db211bmFzLnNocCIpICU+JQogIHN0X3RyYW5zZm9ybSg0MzI2KQplbmN1ZXN0YT1yZWFkX3hsc3goIkNhc29zL0VuY3Vlc3RhT3JpZ2VuRGVzdGluby54bHN4IikKCnZpYWplc19icnV0b3M9ZW5jdWVzdGFbLGMoNyw5LDExLDEzLDI2LDI3KV0KbmFtZXModmlhamVzX2JydXRvcyk9YygibXVuX29yaWdlbiIsInNkZ19vcmlnZW4iLCJtdW5fZGVzdGlubyIsInNkZ19kZXN0aW5vIiwKICAgICAgICAgICAgICAgICAgICAgICAiY29tdW5hX29yaWdlbiIsImNvbXVuYV9kZXN0aW5vIikKYGBgCgpMYXMgdmFyaWFibGVzIHNlbGVjY2lvbmFkYXMgc29uIGVsIG11bmljaXBpbywgZWwgY8OzZGlnbyBTREcgKGPDs2RpZ28gZGUgdWJpY2FjacOzbiBkZSBsYSBlbmN1ZXN0YSkgeSBsYSBjb211bmEsIHRhbnRvIHBhcmEgZWwgb3JpZ2VuIGNvbW8gcGFyYSBlbCBkZXN0aW5vIGRlIGNhZGEgdmlhamUuCgojIE1ldG9kb2xvZ8OtYQoKIyMgRGVwdXJhY2nDs24gZGUgbGEgdmFyaWFibGUgY29tdW5hCgpMYSBjb2x1bW5hIGRlIGNvbXVuYSBkZSBsYSBlbmN1ZXN0YSBubyBzZSBwdWVkZSB1c2FyIGRpcmVjdGFtZW50ZSwgcG9ycXVlIGNvbnRpZW5lIGRvcyBjYXRlZ29yw61hcyBxdWUgbWV6Y2xhbiBjYXNvcyBkaXN0aW50b3MuIExhIHJlY2xhc2lmaWNhY2nDs24gc2UgYXBveWEgZW4gZWwgY8OzZGlnbyBTREcsIGN1eW9zIHByaW1lcm9zIGTDrWdpdG9zIGlkZW50aWZpY2FuIGxhIGNvbXVuYSBkZWwgbHVnYXIuCgotICoqQ29tdW5hIDAuKiogQ2FsaSBubyB0aWVuZSBjb211bmEgMC4gQ2FzaSB0b2RvcyBsb3MgcmVnaXN0cm9zIGNvbiBlc3RlIHZhbG9yIHRpZW5lbiB1biBjw7NkaWdvIFNERyBxdWUgZW1waWV6YSBwb3IgMjYsIHF1ZSBjb3JyZXNwb25kZSBhIGNvcnJlZ2ltaWVudG9zIGNvbW8gUGFuY2UsIExhIEJ1aXRyZXJhIHkgTW9udGViZWxsbzsgZXN0b3Mgc2UgYWdydXBhbiBjb21vICpab25hIHJ1cmFsKi4gTG9zIHBvY29zIGN1eW8gY8OzZGlnbyBlbXBpZXphIHBvciBlbCBuw7ptZXJvIGRlIHVuYSBjb211bmEgc2UgcmVhc2lnbmFuIGEgZXNhIGNvbXVuYS4KLSAqKkZ1ZXJhIGRlIENhbGkuKiogRXN0YSBjYXRlZ29yw61hIGluY2x1eWUgbHVnYXJlcyBkZW50cm8gZGUgbGEgY2l1ZGFkIGN1eW8gY8OzZGlnbyBzZSBwZXJkacOzIChjw7NkaWdvIFNERyBgUGVyZGlkYWAgY29uIG11bmljaXBpbyBDQUxJKSwgY29tbyBsYSBBdmVuaWRhIFJvb3NldmVsdCBvIExhIDE0LiBFc3RvcyBzZSBzZXBhcmFuIGNvbW8gKlNpbiB1YmljYXIqOyBlbCByZXN0byBzZSBtYW50aWVuZSBjb21vICpGdWVyYSBkZSBDYWxpKi4KLSAqKlJlZ2lzdHJvcyBhbnVsYWRvcy4qKiBTZSBlbGltaW5hbiBsb3MgcmVnaXN0cm9zIG1hcmNhZG9zIGNvbW8gYEFOVUxBRE9gLgoKYGBge3IgbGltcGllemF9CmNsYXNpZmljYXI9ZnVuY3Rpb24oY29tdW5hLCBzZGcsIG11bmljaXBpbykgewogIHByZWZpam89c3Vic3RyKHNkZywgMSwgbmNoYXIoc2RnKSAtIDIpCiAgbXVuaWNpcGlvPXRvdXBwZXIodHJpbXdzKG11bmljaXBpbykpCiAgY2FzZV93aGVuKAogICAgY29tdW5hICVpbiUgc3ByaW50ZigiJTAyZCIsIDE6MjIpIH4gc3ViKCJeMCsiLCAiIiwgY29tdW5hKSwKICAgIHByZWZpam8gPT0gIjI2IiB+ICJab25hIHJ1cmFsIiwKICAgIGNvbXVuYSA9PSAiMCIgJiBwcmVmaWpvICVpbiUgYXMuY2hhcmFjdGVyKDE6MjIpIH4gcHJlZmlqbywKICAgIHNkZyA9PSAiUGVyZGlkYSIgJiAoaXMubmEobXVuaWNpcGlvKSB8IG11bmljaXBpbyAlaW4lIGMoIkNBTEkiLCAiU0lOIERBVE9TIikpIH4gIlNpbiB1YmljYXIiLAogICAgVFJVRSB+ICJGdWVyYSBkZSBDYWxpIgogICkKfQoKdmlhamVzPXZpYWplc19icnV0b3MgJT4lCiAgZmlsdGVyKCEobXVuX29yaWdlbiAlaW4lICJBTlVMQURPIiB8IG11bl9kZXN0aW5vICVpbiUgIkFOVUxBRE8iKSkgJT4lCiAgbXV0YXRlKAogICAgem9uYV9vcmlnZW49Y2xhc2lmaWNhcihjb211bmFfb3JpZ2VuLCBzZGdfb3JpZ2VuLCBtdW5fb3JpZ2VuKSwKICAgIHpvbmFfZGVzdGlubz1jbGFzaWZpY2FyKGNvbXVuYV9kZXN0aW5vLCBzZGdfZGVzdGlubywgbXVuX2Rlc3Rpbm8pCiAgKQoKbl9hbnVsYWRvcz1ucm93KHZpYWplc19icnV0b3MpIC0gbnJvdyh2aWFqZXMpCmBgYAoKYGBge3IgZnVuY2lvbmVzLWZvcm1hdG99Cm1pbGVzPWZ1bmN0aW9uKHgpIGZvcm1hdCh4LCBiaWcubWFyayA9ICIuIiwgZGVjaW1hbC5tYXJrID0gIiwiLCB0cmltID0gVFJVRSkKcG9yY2VudGFqZT1mdW5jdGlvbih4KSBpZmVsc2UoaXMubmEoeCksICLigJQiLCBzdWIoIi4iLCAiLCIsIHNwcmludGYoIiUuMWYlJSIsIHgpLCBmaXhlZCA9IFRSVUUpKQpgYGAKCkRlIGxvcyBgciBtaWxlcyhucm93KHZpYWplc19icnV0b3MpKWAgcmVnaXN0cm9zIG9yaWdpbmFsZXMgc2UgZWxpbWluYXJvbiBgciBtaWxlcyhuX2FudWxhZG9zKWAgYW51bGFkb3MsIGRlIG1vZG8gcXVlIGVsIGFuw6FsaXNpcyB0cmFiYWphIGNvbiAqKmByIG1pbGVzKG5yb3codmlhamVzKSlgIHZpYWplcyoqLiBMYSB0YWJsYSAxIG11ZXN0cmEgY8OzbW8gcXVlZGFuIGRpc3RyaWJ1aWRvcyB0cmFzIGxhIHJlY2xhc2lmaWNhY2nDs24uCgpgYGB7ciB0YWJsYS1saW1waWV6YX0KdGlwb196b25hPWZ1bmN0aW9uKHopIGlmZWxzZSh6ICVpbiUgYXMuY2hhcmFjdGVyKDE6MjIpLCAiQ29tdW5hcyB1cmJhbmFzICgx4oCTMjIpIiwgeikKb3JkZW5fem9uYXM9YygiQ29tdW5hcyB1cmJhbmFzICgx4oCTMjIpIiwgIlpvbmEgcnVyYWwiLCAiU2luIHViaWNhciIsICJGdWVyYSBkZSBDYWxpIikKCmZ1bGxfam9pbigKICBjb3VudCh2aWFqZXMsIFpvbmEgPSB0aXBvX3pvbmEoem9uYV9vcmlnZW4pLCBuYW1lID0gIk9yaWdlbiIpLAogIGNvdW50KHZpYWplcywgWm9uYSA9IHRpcG9fem9uYSh6b25hX2Rlc3Rpbm8pLCBuYW1lID0gIkRlc3Rpbm8iKSwKICBieSA9ICJab25hIgopICU+JQogIG11dGF0ZShhY3Jvc3MoYyhPcmlnZW4sIERlc3Rpbm8pLCB+Y29hbGVzY2UoLngsIDBMKSksCiAgICAgICAgIFpvbmEgPSBmYWN0b3IoWm9uYSwgbGV2ZWxzID0gb3JkZW5fem9uYXMpKSAlPiUKICBhcnJhbmdlKFpvbmEpICU+JQogIG11dGF0ZShgJSBvcmlnZW5gID0gcG9yY2VudGFqZSgxMDAgKiBPcmlnZW4gLyBzdW0oT3JpZ2VuKSksCiAgICAgICAgIGAlIGRlc3Rpbm9gID0gcG9yY2VudGFqZSgxMDAgKiBEZXN0aW5vIC8gc3VtKERlc3Rpbm8pKSwKICAgICAgICAgYWNyb3NzKGMoT3JpZ2VuLCBEZXN0aW5vKSwgbWlsZXMpKSAlPiUKICBzZWxlY3QoWm9uYSwgT3JpZ2VuLCBgJSBvcmlnZW5gLCBEZXN0aW5vLCBgJSBkZXN0aW5vYCkgJT4lCiAga2FibGUoYWxpZ24gPSAibHJycnIiLAogICAgICAgIGNhcHRpb24gPSAiVGFibGEgMS4gVmlhamVzIHBvciB0aXBvIGRlIHpvbmEgZGVzcHXDqXMgZGUgbGEgZGVwdXJhY2nDs24iKQpgYGAKCiMjIEluZGljYWRvcmVzIHBvciBjb211bmEKClBhcmEgY2FkYSBjb211bmEgc2UgY2FsY3VsYW46CgotICoqVmlhamVzIGRlIG9yaWdlbioqIHkgKip2aWFqZXMgZGUgZGVzdGlubyoqOiBuw7ptZXJvIGRlIHZpYWplcyBlbmN1ZXN0YWRvcyBxdWUgZW1waWV6YW4gbyB0ZXJtaW5hbiBlbiBsYSBjb211bmEuCi0gKipIYWNpYSBmdWVyYSoqIHkgKipkZXNkZSBmdWVyYSoqOiB2aWFqZXMgY3V5byBvdHJvIGV4dHJlbW8gZXN0w6EgZW4gb3RybyBtdW5pY2lwaW8uCi0gKipDb25leGnDs24gY29uIG90cm9zIG11bmljaXBpb3MgKCUpKio6IHByb3BvcmNpw7NuIGRlIGxvcyB2aWFqZXMgZGUgbGEgY29tdW5hIHF1ZSBsYSBjb25lY3RhbiBjb24gb3RybyBtdW5pY2lwaW8sCgokJApcJVwsXHRleHR7b3Ryb3MgbXVuaWNpcGlvc31faSA9IDEwMCBcdGltZXMgXGZyYWN7XHRleHR7aGFjaWEgZnVlcmF9X2kgKyBcdGV4dHtkZXNkZSBmdWVyYX1faX17XHRleHR7b3JpZ2VufV9pICsgXHRleHR7ZGVzdGlub31faX0KJCQKCmBgYHtyIGluZGljYWRvcmVzfQpjb250ZW9fb3JpZ2VuPXZpYWplcyAlPiUKICBjb3VudCh6b25hX29yaWdlbiwgbmFtZSA9ICJ2aWFqZXNfb3JpZ2VuIikKCmNvbnRlb19kZXN0aW5vPXZpYWplcyAlPiUKICBjb3VudCh6b25hX2Rlc3Rpbm8sIG5hbWUgPSAidmlhamVzX2Rlc3Rpbm8iKQoKaGFjaWFfZnVlcmE9dmlhamVzICU+JQogIGZpbHRlcih6b25hX2Rlc3Rpbm8gPT0gIkZ1ZXJhIGRlIENhbGkiKSAlPiUKICBjb3VudCh6b25hX29yaWdlbiwgbmFtZSA9ICJoYWNpYV9mdWVyYSIpCgpkZXNkZV9mdWVyYT12aWFqZXMgJT4lCiAgZmlsdGVyKHpvbmFfb3JpZ2VuID09ICJGdWVyYSBkZSBDYWxpIikgJT4lCiAgY291bnQoem9uYV9kZXN0aW5vLCBuYW1lID0gImRlc2RlX2Z1ZXJhIikKCmNvbXVuYXNfZmluYWw9Y29tdW5hcyAlPiUKICBtdXRhdGUoem9uYT1hcy5jaGFyYWN0ZXIoY29tdW5hKSkgJT4lCiAgbGVmdF9qb2luKGNvbnRlb19vcmlnZW4sIGJ5ID0gYygiem9uYSIgPSAiem9uYV9vcmlnZW4iKSkgJT4lCiAgbGVmdF9qb2luKGNvbnRlb19kZXN0aW5vLCBieSA9IGMoInpvbmEiID0gInpvbmFfZGVzdGlubyIpKSAlPiUKICBsZWZ0X2pvaW4oaGFjaWFfZnVlcmEsIGJ5ID0gYygiem9uYSIgPSAiem9uYV9vcmlnZW4iKSkgJT4lCiAgbGVmdF9qb2luKGRlc2RlX2Z1ZXJhLCBieSA9IGMoInpvbmEiID0gInpvbmFfZGVzdGlubyIpKSAlPiUKICBtdXRhdGUoCiAgICBhY3Jvc3MoYyh2aWFqZXNfb3JpZ2VuLCB2aWFqZXNfZGVzdGlubywgaGFjaWFfZnVlcmEsIGRlc2RlX2Z1ZXJhKSwgfmNvYWxlc2NlKC54LCAwTCkpLAogICAgdG90YWw9dmlhamVzX29yaWdlbiArIHZpYWplc19kZXN0aW5vLAogICAgcGN0X2Z1ZXJhPWlmX2Vsc2UodG90YWwgPiAwLCAxMDAgKiAoaGFjaWFfZnVlcmEgKyBkZXNkZV9mdWVyYSkgLyB0b3RhbCwgTkFfcmVhbF8pCiAgKSAlPiUKICBhcnJhbmdlKGNvbXVuYSkKCnRhYmxhX2NvbXVuYXM9c3RfZHJvcF9nZW9tZXRyeShjb211bmFzX2ZpbmFsKQp0b3Bfb3JpZ2VuPXNsaWNlX21heCh0YWJsYV9jb211bmFzLCB2aWFqZXNfb3JpZ2VuLCBuID0gMSwgd2l0aF90aWVzID0gRkFMU0UpCnRvcF9kZXN0aW5vPXNsaWNlX21heCh0YWJsYV9jb211bmFzLCB2aWFqZXNfZGVzdGlubywgbiA9IDEsIHdpdGhfdGllcyA9IEZBTFNFKQp0b3BfZnVlcmE9c2xpY2VfbWF4KHRhYmxhX2NvbXVuYXMsIHBjdF9mdWVyYSwgbiA9IDEsIHdpdGhfdGllcyA9IEZBTFNFKQptaW5fZnVlcmE9c2xpY2VfbWluKHRhYmxhX2NvbXVuYXMsIHBjdF9mdWVyYSwgbiA9IDEsIHdpdGhfdGllcyA9IEZBTFNFKQpgYGAKCiMgUmVzdWx0YWRvcwoKIyMgRGlzdHJpYnVjacOzbiBkZSBvcmlnZW4geSBkZXN0aW5vIHBvciBjb211bmEKCkVsIG7Dum1lcm8gc29icmUgY2FkYSBjb211bmEgZXMgbGEgY2FudGlkYWQgZGUgdmlhamVzLiBFbCBzZWxlY3RvciBkZSBsYSBlc3F1aW5hIHN1cGVyaW9yIGRlcmVjaGEgcGVybWl0ZSBjYW1iaWFyIGVudHJlIG9yaWdlbiB5IGRlc3Rpbm87IGFtYmFzIGNhcGFzIHVzYW4gbGEgbWlzbWEgZXNjYWxhIGRlIGNvbG9yLCBhc8OtIHF1ZSBzb24gZGlyZWN0YW1lbnRlIGNvbXBhcmFibGVzLiBBbCBwYXNhciBlbCBjdXJzb3Igc29icmUgdW5hIGNvbXVuYSBzZSB2ZW4gc3VzIGRvcyB2YWxvcmVzLCB5IGFsIGhhY2VyIGNsaWMgc2UgYWJyZSBlbCBkZXRhbGxlLgoKYGBge3IgbWFwYS1vcmlnZW4tZGVzdGlub30KY2VudHJvcz1zdF9jb29yZGluYXRlcyhzdF9wb2ludF9vbl9zdXJmYWNlKHN0X2dlb21ldHJ5KGNvbXVuYXNfZmluYWwpKSkKCmV0aXF1ZXRhX251bWVybz1sYWJlbE9wdGlvbnMoCiAgbm9IaWRlID0gVFJVRSwgdGV4dE9ubHkgPSBUUlVFLCBkaXJlY3Rpb24gPSAiY2VudGVyIiwKICBzdHlsZSA9IGxpc3QoCiAgICAiY29sb3IiID0gIiMwYjBiMGIiLCAiZm9udC13ZWlnaHQiID0gIjYwMCIsICJmb250LXNpemUiID0gIjEycHgiLAogICAgImJhY2tncm91bmQiID0gInJnYmEoMjU1LDI1NSwyNTUsMC44KSIsICJib3JkZXItcmFkaXVzIiA9ICIzcHgiLCAicGFkZGluZyIgPSAiMCAzcHgiCiAgKQopCgpob3Zlcj13aXRoKGNvbXVuYXNfZmluYWwsIGxhcHBseShzcHJpbnRmKAogICI8Yj4lczwvYj48YnI+T3JpZ2VuOiAlczxicj5EZXN0aW5vOiAlcyIsCiAgbm9tYnJlLCBtaWxlcyh2aWFqZXNfb3JpZ2VuKSwgbWlsZXModmlhamVzX2Rlc3Rpbm8pCiksIEhUTUwpKQoKcG9wdXA9d2l0aChjb211bmFzX2ZpbmFsLCBzcHJpbnRmKAogICI8Yj4lczwvYj48YnI+VmlhamVzIGNvbiBvcmlnZW4gYXF1w606ICVzPGJyPlZpYWplcyBjb24gZGVzdGlubyBhcXXDrTogJXM8YnI+CiAgSGFjaWEgZnVlcmEgZGUgQ2FsaTogJXM8YnI+RGVzZGUgZnVlcmEgZGUgQ2FsaTogJXM8YnI+Q29uZXhpw7NuIGNvbiBvdHJvcyBtdW5pY2lwaW9zOiAlcyIsCiAgbm9tYnJlLCBtaWxlcyh2aWFqZXNfb3JpZ2VuKSwgbWlsZXModmlhamVzX2Rlc3Rpbm8pLAogIG1pbGVzKGhhY2lhX2Z1ZXJhKSwgbWlsZXMoZGVzZGVfZnVlcmEpLCBwb3JjZW50YWplKHBjdF9mdWVyYSkKKSkKCnZhbG9yZXM9Yyhjb211bmFzX2ZpbmFsJHZpYWplc19vcmlnZW4sIGNvbXVuYXNfZmluYWwkdmlhamVzX2Rlc3Rpbm8pCmF6dWxlcz1jKCIjY2RlMmZiIiwgIiM5ZWM1ZjQiLCAiIzZkYTdlYyIsICIjMzk4N2U1IiwgIiMyNTZhYmYiLCAiIzE4NGY5NSIsICIjMGQzNjZiIikKcGFsZXRhPWNvbG9yQmluKGF6dWxlcywgZG9tYWluID0gdmFsb3JlcywgYmlucyA9IHByZXR0eSh2YWxvcmVzLCBuID0gNikpCgpjYXBhPWZ1bmN0aW9uKG1hcGEsIGNvbHVtbmEsIGdydXBvKSB7CiAgbWFwYSAlPiUKICAgIGFkZFBvbHlnb25zKAogICAgICBmaWxsQ29sb3IgPSBwYWxldGEoY29tdW5hc19maW5hbFtbY29sdW1uYV1dKSwgZmlsbE9wYWNpdHkgPSAwLjg1LAogICAgICBjb2xvciA9ICIjZmZmZmZmIiwgd2VpZ2h0ID0gMS41LCBncm91cCA9IGdydXBvLAogICAgICBsYWJlbCA9IGhvdmVyLCBwb3B1cCA9IHBvcHVwLAogICAgICBoaWdobGlnaHRPcHRpb25zID0gaGlnaGxpZ2h0T3B0aW9ucyh3ZWlnaHQgPSAzLCBjb2xvciA9ICIjMGIwYjBiIiwgYnJpbmdUb0Zyb250ID0gVFJVRSkKICAgICkgJT4lCiAgICBhZGRMYWJlbE9ubHlNYXJrZXJzKAogICAgICBsbmcgPSBjZW50cm9zWywxXSwgbGF0ID0gY2VudHJvc1ssMl0sIGdyb3VwID0gZ3J1cG8sCiAgICAgIGxhYmVsID0gbWlsZXMoY29tdW5hc19maW5hbFtbY29sdW1uYV1dKSwgbGFiZWxPcHRpb25zID0gZXRpcXVldGFfbnVtZXJvCiAgICApCn0KCmxlYWZsZXQoY29tdW5hc19maW5hbCwgd2lkdGggPSAiMTAwJSIsIGhlaWdodCA9IDY4MCkgJT4lCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkRXNyaS5Xb3JsZEdyYXlDYW52YXMpICU+JQogIGNhcGEoInZpYWplc19vcmlnZW4iLCAiT3JpZ2VuIikgJT4lCiAgY2FwYSgidmlhamVzX2Rlc3Rpbm8iLCAiRGVzdGlubyIpICU+JQogIGFkZExheWVyc0NvbnRyb2woCiAgICBiYXNlR3JvdXBzID0gYygiT3JpZ2VuIiwgIkRlc3Rpbm8iKSwKICAgIG9wdGlvbnMgPSBsYXllcnNDb250cm9sT3B0aW9ucyhjb2xsYXBzZWQgPSBGQUxTRSkKICApICU+JQogIGFkZFNjYWxlQmFyKHBvc2l0aW9uID0gImJvdHRvbWxlZnQiLCBvcHRpb25zID0gc2NhbGVCYXJPcHRpb25zKGltcGVyaWFsID0gRkFMU0UpKSAlPiUKICBhZGRMZWdlbmQoCiAgICBwb3NpdGlvbiA9ICJib3R0b21yaWdodCIsIHBhbCA9IHBhbGV0YSwgdmFsdWVzID0gdmFsb3JlcywKICAgIHRpdGxlID0gIlZpYWplcyBlbmN1ZXN0YWRvcyIsIG9wYWNpdHkgPSAwLjg1LAogICAgbGFiRm9ybWF0ID0gbGFiZWxGb3JtYXQoYmlnLm1hcmsgPSAiLiIpCiAgKQpgYGAKCjxwIGNsYXNzPSJmaWdjYXAiPioqRmlndXJhIDEuKiogVmlhamVzIGVuY3Vlc3RhZG9zIGNvbiBvcmlnZW4geSBkZXN0aW5vIGVuIGNhZGEgY29tdW5hIGRlIENhbGkuIEZ1ZW50ZTogZWxhYm9yYWNpw7NuIHByb3BpYSBjb24gZGF0b3MgZGUgbGEgRW5jdWVzdGEgZGUgT3JpZ2VuIHkgRGVzdGlubyBkZSBDYWxpICgyMDE1KS48L3A+CgpMYSBjb211bmEgY29uIG3DoXMgdmlhamVzIGRlIG9yaWdlbiBlcyAqKmByIHRvcF9vcmlnZW4kbm9tYnJlYCoqIChgciBtaWxlcyh0b3Bfb3JpZ2VuJHZpYWplc19vcmlnZW4pYCB2aWFqZXMpLCB5IGxhIHF1ZSByZWNpYmUgbcOhcyB2aWFqZXMgZGUgZGVzdGlubyBlcyAqKmByIHRvcF9kZXN0aW5vJG5vbWJyZWAqKiAoYHIgbWlsZXModG9wX2Rlc3Rpbm8kdmlhamVzX2Rlc3Rpbm8pYCB2aWFqZXMpLiBEYWRvIGVsIGRpc2XDsW8gZGUgbGEgZW5jdWVzdGEsIGVzdGFzIGRpZmVyZW5jaWFzIGNvbWJpbmFuIGxhIGludGVuc2lkYWQgcmVhbCBkZSBsYSBtb3ZpbGlkYWQgY29uIGxhIGNlcmNhbsOtYSBkZSBjYWRhIGNvbXVuYSBhIGxhcyBlc3RhY2lvbmVzIGRlIGFmb3JvLgoKIyMjIFZpYWplcyBxdWUgbm8gYXBhcmVjZW4gZW4gZWwgbWFwYQoKRWwgc2hhcGVmaWxlIGN1YnJlIHNvbG8gZWwgcGVyw61tZXRybyB1cmJhbm8sIHBvciBsbyBxdWUgbG9zIHZpYWplcyBkZSBsYSB6b25hIHJ1cmFsLCBkZSBvdHJvcyBtdW5pY2lwaW9zIG8gc2luIHViaWNhY2nDs24gbm8gdGllbmVuIHBvbMOtZ29uby4gU2UgcmVwb3J0YW4gZW4gbGEgdGFibGEgMiBwYXJhIHF1ZSBubyBzZSBwaWVyZGFuIGRlbCBhbsOhbGlzaXMuCgpgYGB7ciB0YWJsYS1vdHJhcy16b25hc30Kem9uYXNfb3RyYXM9YygiWm9uYSBydXJhbCIsICJGdWVyYSBkZSBDYWxpIiwgIlNpbiB1YmljYXIiKQoKdGliYmxlKFpvbmEgPSB6b25hc19vdHJhcykgJT4lCiAgbGVmdF9qb2luKGNvbnRlb19vcmlnZW4sIGJ5ID0gYygiWm9uYSIgPSAiem9uYV9vcmlnZW4iKSkgJT4lCiAgbGVmdF9qb2luKGNvbnRlb19kZXN0aW5vLCBieSA9IGMoIlpvbmEiID0gInpvbmFfZGVzdGlubyIpKSAlPiUKICBtdXRhdGUoYWNyb3NzKGModmlhamVzX29yaWdlbiwgdmlhamVzX2Rlc3Rpbm8pLCB+bWlsZXMoY29hbGVzY2UoLngsIDBMKSkpKSAlPiUKICByZW5hbWUoT3JpZ2VuID0gdmlhamVzX29yaWdlbiwgRGVzdGlubyA9IHZpYWplc19kZXN0aW5vKSAlPiUKICBrYWJsZShhbGlnbiA9ICJscnIiLCBjYXB0aW9uID0gIlRhYmxhIDIuIFZpYWplcyBzaW4gcG9sw61nb25vIGVuIGVsIG1hcGEgZGUgY29tdW5hcyIpCmBgYAoKIyMgQ29uZXhpw7NuIGNvbiBvdHJvcyBtdW5pY2lwaW9zCgpBdW5xdWUgbG9zIHZpYWplcyBmdWVyYSBkZSBDYWxpIG5vIGNhYmVuIGVuIGVsIG1hcGEgZGUgY29tdW5hcywgc8OtIG11ZXN0cmFuIHF1w6kgdGFuIGNvbmVjdGFkYSBlc3TDoSBjYWRhIGNvbXVuYSBjb24gbGEgcmVnacOzbi4gTGEgZmlndXJhIDIgcmVwcmVzZW50YSBlbCBwb3JjZW50YWplIGRlIGxvcyB2aWFqZXMgZGUgY2FkYSBjb211bmEgcXVlIHNhbGVuIGhhY2lhIG90cm8gbXVuaWNpcGlvIG8gbGxlZ2FuIGRlc2RlIG90cm8uCgpgYGB7ciBtYXBhLWNvbmV4aW9ufQp2YWxvcmVzX2Z1ZXJhPWNvbXVuYXNfZmluYWwkcGN0X2Z1ZXJhCm5hcmFuamFzPWMoIiNmZGUzZDgiLCAiI2Y3Yjg5ZSIsICIjZjA4ZjY4IiwgIiNlYjY4MzQiLCAiI2JkNGMxZiIsICIjOGYzNDEwIikKcGFsZXRhX2Z1ZXJhPWNvbG9yQmluKG5hcmFuamFzLCBkb21haW4gPSB2YWxvcmVzX2Z1ZXJhLAogICAgICAgICAgICAgICAgICAgICAgYmlucyA9IHByZXR0eSh2YWxvcmVzX2Z1ZXJhLCBuID0gNSksIG5hLmNvbG9yID0gIiNlMGUwZTAiKQoKbGVhZmxldChjb211bmFzX2ZpbmFsLCB3aWR0aCA9ICIxMDAlIiwgaGVpZ2h0ID0gNjgwKSAlPiUKICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRFc3JpLldvcmxkR3JheUNhbnZhcykgJT4lCiAgYWRkUG9seWdvbnMoCiAgICBmaWxsQ29sb3IgPSB+cGFsZXRhX2Z1ZXJhKHBjdF9mdWVyYSksIGZpbGxPcGFjaXR5ID0gMC44NSwKICAgIGNvbG9yID0gIiNmZmZmZmYiLCB3ZWlnaHQgPSAxLjUsCiAgICBsYWJlbCA9IGhvdmVyLCBwb3B1cCA9IHBvcHVwLAogICAgaGlnaGxpZ2h0T3B0aW9ucyA9IGhpZ2hsaWdodE9wdGlvbnMod2VpZ2h0ID0gMywgY29sb3IgPSAiIzBiMGIwYiIsIGJyaW5nVG9Gcm9udCA9IFRSVUUpCiAgKSAlPiUKICBhZGRMYWJlbE9ubHlNYXJrZXJzKAogICAgbG5nID0gY2VudHJvc1ssMV0sIGxhdCA9IGNlbnRyb3NbLDJdLAogICAgbGFiZWwgPSBpZmVsc2UoaXMubmEodmFsb3Jlc19mdWVyYSksICIiLCBzcHJpbnRmKCIlLjBmJSUiLCB2YWxvcmVzX2Z1ZXJhKSksCiAgICBsYWJlbE9wdGlvbnMgPSBldGlxdWV0YV9udW1lcm8KICApICU+JQogIGFkZFNjYWxlQmFyKHBvc2l0aW9uID0gImJvdHRvbWxlZnQiLCBvcHRpb25zID0gc2NhbGVCYXJPcHRpb25zKGltcGVyaWFsID0gRkFMU0UpKSAlPiUKICBhZGRMZWdlbmQoCiAgICBwb3NpdGlvbiA9ICJib3R0b21yaWdodCIsIHBhbCA9IHBhbGV0YV9mdWVyYSwgdmFsdWVzID0gdmFsb3Jlc19mdWVyYSwKICAgIHRpdGxlID0gIiUgZGUgdmlhamVzIGNvbjxicj5vdHJvcyBtdW5pY2lwaW9zIiwgb3BhY2l0eSA9IDAuODUsCiAgICBsYWJGb3JtYXQgPSBsYWJlbEZvcm1hdChzdWZmaXggPSAiJSIpCiAgKQpgYGAKCjxwIGNsYXNzPSJmaWdjYXAiPioqRmlndXJhIDIuKiogUG9yY2VudGFqZSBkZSBsb3MgdmlhamVzIGRlIGNhZGEgY29tdW5hIHF1ZSB0aWVuZW4gc3Ugb3RybyBleHRyZW1vIGVuIHVuIG11bmljaXBpbyBkaXN0aW50byBkZSBDYWxpLiBGdWVudGU6IGVsYWJvcmFjacOzbiBwcm9waWEgY29uIGRhdG9zIGRlIGxhIEVuY3Vlc3RhIGRlIE9yaWdlbiB5IERlc3Rpbm8gZGUgQ2FsaSAoMjAxNSkuPC9wPgoKTGEgY29uZXhpw7NuIHJlZ2lvbmFsIHZhIGRlIGByIHBvcmNlbnRhamUobWluX2Z1ZXJhJHBjdF9mdWVyYSlgIGVuICoqYHIgbWluX2Z1ZXJhJG5vbWJyZWAqKiBhIGByIHBvcmNlbnRhamUodG9wX2Z1ZXJhJHBjdF9mdWVyYSlgIGVuICoqYHIgdG9wX2Z1ZXJhJG5vbWJyZWAqKi4KCiMjIyBQcmluY2lwYWxlcyBtdW5pY2lwaW9zIGNvbmVjdGFkb3MgY29uIENhbGkKCkxhIHRhYmxhIDMgY3VlbnRhIGxvcyB2aWFqZXMgY29uIHVuIGV4dHJlbW8gZW4gQ2FsaSAoaW5jbHVpZGEgc3Ugem9uYSBydXJhbCkgeSBlbCBvdHJvIGVuIHVuIG11bmljaXBpbyBkaXN0aW50by4KCmBgYHtyIHRhYmxhLW11bmljaXBpb3N9CmVuX2NhbGk9ZnVuY3Rpb24oem9uYSkgIXpvbmEgJWluJSBjKCJGdWVyYSBkZSBDYWxpIiwgIlNpbiB1YmljYXIiKQpzaW5fbXVuaWNpcGlvPWMoIkNBTEkiLCAiU0lOIERBVE9TIikKCmhhY2lhX2NhbGk9dmlhamVzICU+JQogIGZpbHRlcih6b25hX29yaWdlbiA9PSAiRnVlcmEgZGUgQ2FsaSIsIGVuX2NhbGkoem9uYV9kZXN0aW5vKSkgJT4lCiAgY291bnQobXVuaWNpcGlvID0gdG91cHBlcih0cmltd3MobXVuX29yaWdlbikpLCBuYW1lID0gIkhhY2lhIENhbGkiKQoKZGVzZGVfY2FsaT12aWFqZXMgJT4lCiAgZmlsdGVyKHpvbmFfZGVzdGlubyA9PSAiRnVlcmEgZGUgQ2FsaSIsIGVuX2NhbGkoem9uYV9vcmlnZW4pKSAlPiUKICBjb3VudChtdW5pY2lwaW8gPSB0b3VwcGVyKHRyaW13cyhtdW5fZGVzdGlubykpLCBuYW1lID0gIkRlc2RlIENhbGkiKQoKbXVuaWNpcGlvcz1mdWxsX2pvaW4oaGFjaWFfY2FsaSwgZGVzZGVfY2FsaSwgYnkgPSAibXVuaWNpcGlvIikgJT4lCiAgZmlsdGVyKCFpcy5uYShtdW5pY2lwaW8pLCAhbXVuaWNpcGlvICVpbiUgc2luX211bmljaXBpbykgJT4lCiAgbXV0YXRlKAogICAgYWNyb3NzKGMoYEhhY2lhIENhbGlgLCBgRGVzZGUgQ2FsaWApLCB+Y29hbGVzY2UoLngsIDBMKSksCiAgICBUb3RhbCA9IGBIYWNpYSBDYWxpYCArIGBEZXNkZSBDYWxpYCwKICAgIE11bmljaXBpbyA9IHRvb2xzOjp0b1RpdGxlQ2FzZSh0b2xvd2VyKG11bmljaXBpbykpCiAgKSAlPiUKICBhcnJhbmdlKGRlc2MoVG90YWwpKQoKbXVuaWNpcGlvcyAlPiUKICBzbGljZV9oZWFkKG4gPSA4KSAlPiUKICB0cmFuc211dGUoTXVuaWNpcGlvLCBgSGFjaWEgQ2FsaWAgPSBtaWxlcyhgSGFjaWEgQ2FsaWApLAogICAgICAgICAgICBgRGVzZGUgQ2FsaWAgPSBtaWxlcyhgRGVzZGUgQ2FsaWApLCBUb3RhbCA9IG1pbGVzKFRvdGFsKSkgJT4lCiAga2FibGUoYWxpZ24gPSAibHJyciIsIGNhcHRpb24gPSAiVGFibGEgMy4gT2NobyBtdW5pY2lwaW9zIGNvbiBtw6FzIHZpYWplcyBkZXNkZSB5IGhhY2lhIENhbGkiKQoKZGVfcGFzbz1zdW0odmlhamVzJHpvbmFfb3JpZ2VuID09ICJGdWVyYSBkZSBDYWxpIiAmIHZpYWplcyR6b25hX2Rlc3Rpbm8gPT0gIkZ1ZXJhIGRlIENhbGkiKQpgYGAKCkVsIG11bmljaXBpbyBjb24gbWF5b3IgaW50ZXJjYW1iaW8gZXMgKipgciBtdW5pY2lwaW9zJE11bmljaXBpb1sxXWAqKiwgY29uIGByIG1pbGVzKG11bmljaXBpb3MkVG90YWxbMV0pYCB2aWFqZXMgZW4gYW1ib3Mgc2VudGlkb3MuIEFkZW3DoXMsIGByIG1pbGVzKGRlX3Bhc28pYCB2aWFqZXMgdGllbmVuIGxvcyBkb3MgZXh0cmVtb3MgZnVlcmEgZGUgQ2FsaTogcGFzYW4gcG9yIGxhIGNpdWRhZCBzaW4gZW1wZXphciBuaSB0ZXJtaW5hciBlbiBlbGxhLCBwb3IgbG8gcXVlIG5vIHNlIGFzaWduYW4gYSBuaW5ndW5hIGNvbXVuYS4KCiMjIFJlc3VtZW4gcG9yIGNvbXVuYQoKYGBge3IgdGFibGEtY29tdW5hc30KdGFibGFfY29tdW5hcyAlPiUKICB0cmFuc211dGUoCiAgICBDb211bmEgPSBjb211bmEsIE5vbWJyZSA9IG5vbWJyZSwKICAgIE9yaWdlbiA9IG1pbGVzKHZpYWplc19vcmlnZW4pLCBEZXN0aW5vID0gbWlsZXModmlhamVzX2Rlc3Rpbm8pLAogICAgYEhhY2lhIGZ1ZXJhYCA9IG1pbGVzKGhhY2lhX2Z1ZXJhKSwgYERlc2RlIGZ1ZXJhYCA9IG1pbGVzKGRlc2RlX2Z1ZXJhKSwKICAgIGAlIG90cm9zIG11bmljaXBpb3NgID0gcG9yY2VudGFqZShwY3RfZnVlcmEpCiAgKSAlPiUKICBrYWJsZShhbGlnbiA9ICJsbHJycnJyIiwgY2FwdGlvbiA9ICJUYWJsYSA0LiBJbmRpY2Fkb3JlcyBkZSB2aWFqZXMgcG9yIGNvbXVuYSIpCmBgYAoKIyBEaXNjdXNpw7NuIHkgbGltaXRhY2lvbmVzCgotICoqU2VzZ28gZGUgaW50ZXJjZXB0YWNpw7NuLioqIEFsIHRyYXRhcnNlIGRlIHVuYSBlbmN1ZXN0YSBlbiAzNSBlc3RhY2lvbmVzLCBsYXMgY29tdW5hcyBjZXJjYW5hcyBhIGxhcyBlc3RhY2lvbmVzIG8gYSBsb3MgY29ycmVkb3JlcyBkb25kZSBzZSB1YmljYXJvbiB0aWVuZGVuIGEgdGVuZXIgbcOhcyB2aWFqZXMgcmVnaXN0cmFkb3MuIExvcyBtYXBhcyBkZXNjcmliZW4gbGEgbXVlc3RyYSwgbm8gbGEgZGVtYW5kYSB0b3RhbCBkZSBsYSBjaXVkYWQuCi0gKipDb2JlcnR1cmEgZXNwYWNpYWwuKiogRWwgc2hhcGVmaWxlIHNvbG8gaW5jbHV5ZSBlbCDDoXJlYSB1cmJhbmEuIExvcyB2aWFqZXMgcnVyYWxlcywgaW50ZXJtdW5pY2lwYWxlcyB5IHNpbiB1YmljYXIgc2UgcmVwb3J0YW4gZW4gdGFibGFzIGFwYXJ0ZSwgcGVybyBubyBzZSByZXByZXNlbnRhbiBlc3BhY2lhbG1lbnRlLgotICoqQ2FsaWRhZCBkZSBsYSBnZW9ycmVmZXJlbmNpYWNpw7NuLioqIExhIHJlY2xhc2lmaWNhY2nDs24gZGVwZW5kZSBkZWwgY8OzZGlnbyBTREc7IGxvcyByZWdpc3Ryb3MgY29uIGPDs2RpZ28gYFBlcmRpZGFgIGRlbnRybyBkZSBDYWxpIG5vIHNlIHB1ZWRlbiBhc2lnbmFyIGEgdW5hIGNvbXVuYSB5IHF1ZWRhbiBjb21vICpTaW4gdWJpY2FyKi4KLSAqKkFudGlnw7xlZGFkIGRlIGxvcyBkYXRvcy4qKiBMYSBlbmN1ZXN0YSBlcyBkZSAyMDE1LCBwb3IgbG8gcXVlIG5vIHJlZmxlamEgY2FtYmlvcyBwb3N0ZXJpb3JlcyBlbiBsYSByZWQgZGUgdHJhbnNwb3J0ZSBuaSBlbiBsYSBkaXN0cmlidWNpw7NuIGRlIGxhIHBvYmxhY2nDs24uCgojIENvbmNsdXNpb25lcwoKMS4gVW5hIHZleiBkZXB1cmFkYSBsYSB2YXJpYWJsZSBkZSBjb211bmEsIGByIG1pbGVzKG5yb3codmlhamVzKSlgIHZpYWplcyBlbmN1ZXN0YWRvcyBwdWVkZW4gY2xhc2lmaWNhcnNlIHBvciB0aXBvIGRlIHpvbmEsIHkgbGEgbWF5b3LDrWEgZGUgbG9zIHF1ZSBxdWVkYW4gZGVudHJvIGRlIENhbGkgc2UgYXNpZ25hbiBhIHVuYSBkZSBsYXMgMjIgY29tdW5hcy4KMi4gTGEgZ2VuZXJhY2nDs24geSBsYSBhdHJhY2Npw7NuIGRlIHZpYWplcyBubyBzZSBkaXN0cmlidXllbiBkZSBmb3JtYSBob21vZ8OpbmVhOiBgciB0b3Bfb3JpZ2VuJG5vbWJyZWAgY29uY2VudHJhIGVsIG1heW9yIG7Dum1lcm8gZGUgb3LDrWdlbmVzIHkgYHIgdG9wX2Rlc3Rpbm8kbm9tYnJlYCBlbCBtYXlvciBuw7ptZXJvIGRlIGRlc3Rpbm9zLgozLiBMYSBkZXBlbmRlbmNpYSBkZSBvdHJvcyBtdW5pY2lwaW9zIHZhcsOtYSBkZSBmb3JtYSBpbXBvcnRhbnRlIGVudHJlIGNvbXVuYXMsIGRlc2RlIGByIHBvcmNlbnRhamUobWluX2Z1ZXJhJHBjdF9mdWVyYSlgIGhhc3RhIGByIHBvcmNlbnRhamUodG9wX2Z1ZXJhJHBjdF9mdWVyYSlgLCB5IGByIG11bmljaXBpb3MkTXVuaWNpcGlvWzFdYCBlcyBlbCBwcmluY2lwYWwgbXVuaWNpcGlvIGNvbmVjdGFkbyBjb24gQ2FsaS4KNC4gUG9yIGVsIGRpc2XDsW8gZGUgbGEgZW5jdWVzdGEsIGVzdG9zIHJlc3VsdGFkb3MgZGViZW4gbGVlcnNlIGNvbW8gcGF0cm9uZXMgcmVsYXRpdm9zIGRlIGxhIG11ZXN0cmEgeSBjb21wbGVtZW50YXJzZSBjb24gZnVlbnRlcyBkZSBkZW1hbmRhIHRvdGFsIHBhcmEgZmluZXMgZGUgcGxhbmVhY2nDs24uCgojIFJlcHJvZHVjaWJpbGlkYWQKCmBgYHtyIHJlcHJvZHVjaWJpbGlkYWR9CnBhcXVldGVzPWMoInNmIiwgImRwbHlyIiwgImxlYWZsZXQiLCAiaHRtbHRvb2xzIiwgInJlYWR4bCIsICJrbml0ciIpCnRpYmJsZShQYXF1ZXRlID0gcGFxdWV0ZXMsCiAgICAgICBWZXJzacOzbiA9IHNhcHBseShwYXF1ZXRlcywgZnVuY3Rpb24ocCkgYXMuY2hhcmFjdGVyKHBhY2thZ2VWZXJzaW9uKHApKSkpICU+JQogIGthYmxlKGNhcHRpb24gPSBwYXN0ZSgiVGFibGEgNS4gUGFxdWV0ZXMgdXRpbGl6YWRvcyDigJQiLCBSLnZlcnNpb24uc3RyaW5nKSkKYGBgCg==