Instalar paquetes y llamar librerías

library(sf)          
library(spdep)       
library(ggplot2)     
library(dplyr)      
library(leaflet)     
library(htmltools)
library(lubridate)
library(dplyr)
library(sf)
library(readxl)
library(foreign)
library(tigris)
library(rgeoda)
library(RColorBrewer)
library(viridis)
library(ggplot2)
library(tmap)
library(sf) 
library(sp)
library(spatialreg)
library(stargazer)
library(plm)
library(splm)
library(pspatreg)
library(regclass)
library(mctest)
library(lmtest)
library(spData)
library(mapview)
library(naniar)
library(dlookr)
library(caret)
library(e1071)
library(SparseM)
library(Metrics)
library(randomForest)
library(rpart.plot)
library(insight)
library(jtools)
library(xgboost)
library(DiagrammeR)
library(effects)
library(sf)
library(purrr)
library(geosphere)
library(gmapsdistance)
library(dplyr)
library(stringr)
library(knitr)
library(leaflet.extras)
library(tidyr)
library(prophet)
if (!require("pacman")) install.packages("pacman")
pacman::p_load(
  sf,          # Datos espaciales
  spdep,       # Autocorrelación espacial
  ggplot2,     # Visualización
  dbscan,      # Detección de clústers
  dplyr        # Manipulación de datos
)
if (!require("geosphere")) install.packages("geosphere")
if (!require("gmapsdistance"))install.packages("gmapsdistance")

Importar las bases de datos

datos <- read_excel("C:\\Users\\mari0\\OneDrive\\Documents\\R Studio\\8 semestre\\Evidencia\\BASE MUNICIPAL_ACCIDENTES DE TRANSITO GEORREFERENCIADOS_2023.xlsx")

geojson_data <- st_read("Transporte.geojson") %>% 
  st_transform(32614) %>%  # Convertir a UTM Zone 14N (Monterrey)
  filter(!st_is_empty(.))  # Eliminar geometrías vacías
## Reading layer `Transporte' from data source 
##   `C:\Users\mari0\OneDrive\Documents\R Studio\8 semestre\Evidencia\Transporte.geojson' 
##   using driver `GeoJSON'
## Simple feature collection with 74329 features and 4 fields
## Geometry type: LINESTRING
## Dimension:     XY
## Bounding box:  xmin: -100.7508 ymin: 25.3483 xmax: -99.7609 ymax: 26.13538
## Geodetic CRS:  WGS 84

Análisis Datos Tránsito en Nuevo León

La información de transporte proviene de la encuesta origen-destino del 2019 que el gobierno del estado de Nuevo León realizó como parte del Programa Integral de Movilidad Urbana Sustentable (PIMUS) del Área Metropolitana de Monterrey. Transconsult fue la empresa consultora responsable del levantamiento de la encuesta.

Hot Spots significativos de congestionamiento vehicular dentro del área metropolitana de Monterrey

# Desactivar geometría esférica (evita errores)
sf::sf_use_s2(FALSE)
## Spherical geometry (s2) switched off
# Crear variable numérica para análisis
geojson_data <- geojson_data %>%
  mutate(
    peso_transporte = case_when(
      Transporte %in% c("Autómovil", "TPUB", "Taxi") ~ 3,
      Transporte %in% c("Transporte de Personal", "Transporte escolar") ~ 2,
      TRUE ~ 1  # Para bicicletas, caminando y otros
    )
  )


# Obtener coordenadas de los centroides
coords <- st_coordinates(st_centroid(geojson_data))
## Warning: st_centroid assumes attributes are constant over geometries
# Calcular vecinos en un radio de 800 metros
nb <- dnearneigh(coords, d1 = 0, d2 = 800)

# Identificar y eliminar puntos aislados (sin vecinos)
aislados <- which(card(nb) == 0)
if(length(aislados) > 0){
  geojson_data <- geojson_data[-aislados, ]
  coords <- st_coordinates(st_centroid(geojson_data))
  nb <- dnearneigh(coords, d1 = 0, d2 = 800)
}
## Warning: st_centroid assumes attributes are constant over geometries
# Crear matriz de pesos espaciales
pesos <- nb2listw(nb, style = "B")  # Pesos binarios

# Calcular hotspots (Getis-Ord Gi*)
geojson_data$gi_score <- localG(
  x = geojson_data$peso_transporte,
  listw = pesos,
  zero.policy = FALSE
)
# Visualización de resultados

# Convertir LINESTRING a puntos (extremos de cada línea)
puntos_hotspots <- st_cast(geojson_data, "POINT") %>% 
  st_transform(4326)  # Convertir a WGS84
## Warning in st_cast.sf(geojson_data, "POINT"): repeating attributes for all
## sub-geometries for which they may not be constant
# Convertir los scores a numérico
geojson_data$gi_score_numeric <- as.numeric(geojson_data$gi_score)
# Convertir a puntos y filtrar
puntos_significativos <- geojson_data %>% 
  st_cast("POINT") %>% 
  st_transform(4326) %>% 
  filter(gi_score_numeric > quantile(gi_score_numeric, 0.97))  # Top 3%
## Warning in st_cast.sf(., "POINT"): repeating attributes for all sub-geometries
## for which they may not be constant
# Paleta de color para valores extremos
pal <- colorNumeric(
  palette = "Reds",
  domain = c(2.5, max(puntos_significativos$gi_score_numeric, na.rm = TRUE)),
  na.color = "transparent"
)

# Mapa final
leaflet(puntos_significativos) %>%
  addProviderTiles(providers$CartoDB.Positron) %>%
  addCircleMarkers(
    radius = ~sqrt(gi_score_numeric)*2,  # Tamaño proporcional al score
    color = ~pal(gi_score_numeric),
    fillOpacity = 0.7,
    stroke = FALSE,
    popup = ~paste("Score:", round(gi_score_numeric, 2))
  ) %>%
  addLegend(
    position = "bottomright",
    pal = pal,
    values = ~gi_score_numeric,
    title = "Hotspots<br>(Top 3%)"
  ) %>%
  setView(lng = -100.3, lat = 25.67, zoom = 12)
# Ver distribución de scores
ggplot(geojson_data, aes(x = gi_score_numeric)) + 
  geom_histogram(bins = 50) +
  geom_vline(xintercept = quantile(geojson_data$gi_score_numeric, 0.95), color = "red") +
  labs(title = "Distribución de Gi* Scores", subtitle = "Línea roja = percentil 95%")

La distribución es asimétrica con sesgo negativo: la mayor parte de los valores Gi* se concentran entre -10 y +8, con una larga cola hacia la izquierda (valores muy negativos).

Esto indica que la mayoría de los trayectos no presentan concentración significativa (Gi* cercano a 0), pero sí hay algunos con clara dispersión (Gi* muy negativo).

La línea roja marca el percentil 95, es decir, el umbral a partir del cual se considera que un Gi* score está en el 5% superior, lo cual indica un hotspot estadísticamente significativo.

# Convertir HoraOri a formato hora y luego a horas decimales
puntos_hora <- geojson_data %>%
  st_cast("POINT") %>%
  st_transform(4326) %>%
  mutate(
    hora_decimal = period_to_seconds(hms(HoraOri)) / 3600,  # Convertir a horas con decimales
    franja_horaria = cut(
      hora_decimal,
      breaks = c(0, 6, 9, 12, 15, 18, 21, 24),
      labels = c("Madrugada", "Mañana-Pico", "Mediodía", "Tarde", "Tarde-Pico", "Noche-Temprana", "Noche-Tardia"),
      right = FALSE,
      include.lowest = TRUE
    )
  ) %>%
  filter(!is.na(hora_decimal))  # Eliminar registros sin hora válida
## Warning in st_cast.sf(., "POINT"): repeating attributes for all sub-geometries
## for which they may not be constant
# Verificar
summary(puntos_hora$hora_decimal)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    5.00    8.00   12.50   12.26   16.00   22.83
table(puntos_hora$franja_horaria, useNA = "always")
## 
##      Madrugada    Mañana-Pico       Mediodía          Tarde     Tarde-Pico 
##           4098          43006          18450          35028          25250 
## Noche-Temprana   Noche-Tardia           <NA> 
##          19708           3034              0
# Paleta de colores para franjas
pal_hora <- colorFactor(
  palette = c("#1a2b5c", "#f7aa00", "#a4d4ae", "#ff6b6b", "#c81d25", "#5e17eb", "#2e2e2e"),
  domain = puntos_hora$franja_horaria
)

# Crear mapa
leaflet(puntos_hora) %>%
  addProviderTiles(providers$CartoDB.Positron) %>%
  addCircleMarkers(
    radius = ~ifelse(gi_score_numeric > 2, 6, 3),
    color = ~pal_hora(franja_horaria),
    fillOpacity = 0.7,
    popup = ~paste(
      "<strong>Hora:</strong> ", HoraOri, "<br>",
      "<strong>Franja:</strong> ", franja_horaria, "<br>",
      "<strong>Score:</strong> ", round(gi_score_numeric, 2)
    )
  ) %>%
  addLegend(
    position = "bottomright",
    pal = pal_hora,
    values = ~franja_horaria,
    title = "Franjas Horarias"
  ) %>%
  setView(lng = -100.3, lat = 25.67, zoom = 12)
# Resumen estadístico por franja horaria
puntos_hora %>%
  st_drop_geometry() %>%
  group_by(franja_horaria) %>%
  summarise(
    n_puntos = n(),
    avg_score = mean(gi_score_numeric),
    porcentaje = n() / nrow(puntos_hora) * 100
  ) %>%
  arrange(desc(avg_score)) %>%
  ggplot(aes(x = reorder(franja_horaria, -avg_score), y = avg_score)) +
  geom_col(aes(fill = franja_horaria)) +
  geom_text(aes(label = paste0(round(porcentaje,1), "%")), vjust = -0.5) +
  scale_fill_manual(values = c("#f7aa00", "#c81d25", "#ff6b6b", "#a4d4ae", "#1a2b5c", "#5e17eb", "#2e2e2e")) +
  labs(title = "Intensidad Promedio de Hotspots por Franja Horaria",
       x = "Franja Horaria",
       y = "Score Promedio (Gi*)") +
  theme_minimal()

Congestión significativa (hotspots positivos fuertes): - Madrugada y Noche-Temprana tienen valores de Gi* positivos → hay cierta concentración de congestión (aunque no muy intensa). - Pero su intensidad es ligera (Gi* < 1).

Congestión dispersa o sin concentración espacial (valores bajos/negativos de Gi*):

  • Tarde, Mañana-Pico y Mediodía muestran valores negativos altos de Gi*, especialmente: - Tarde: -2.3 - Mañana-Pico: -1.7
  • Esto indica que durante estas franjas horarias, los incidentes de congestión no se concentran espacialmente, sino que están más dispersos por la ciudad.

Transiciones interesantes:

La Noche-Tardia tiene un Gi* cercano a 0 → sin evidencia fuerte de concentración ni dispersión.

Tarde-Pico muestra apenas una ligera dispersión.

¿Qué significa esto ? En horas pico (mañana y tarde), aunque hay muchos viajes, los eventos de congestión están dispersos, lo cual sugiere que no hay zonas fijas de congestión, sino que se esparcen por varias partes de la ciudad.

En horas de baja actividad (madrugada y noche-temprana), los pocos viajes que ocurren tienden a concentrarse espacialmente, posiblemente en corredores viales clave o zonas centrales.

Planeación urbana o de transporte público debe enfocarse más en mitigar dispersión en horas pico, ya que es más difícil de controlar.

# Filtrar solo horas pico (mañana y tarde)
horas_pico <- puntos_hora %>%
  filter(franja_horaria %in% c("Mañana-Pico", "Tarde-Pico"))

# Preparar coordenadas ANTES de crear el mapa
coords <- horas_pico %>% 
  st_coordinates() %>% 
  as.data.frame()

# Crear mapa con heatmap
leaflet() %>%  # Nota: no pasamos el objeto sf directamente aquí
  addProviderTiles(providers$CartoDB.Positron) %>%
  addHeatmap(
    lng = coords$X,  # Usar las columnas ya extraídas
    lat = coords$Y,
    intensity = horas_pico$gi_score_numeric,
    radius = 15,
    blur = 20,
    max = 0.05,
    gradient = c("#f7aa00", "#c81d25")  # Gradiente de color directo
  ) %>%
  addLegend(
    position = "bottomright",
    colors = c("#f7aa00", "#c81d25"),
    labels = c("Baja intensidad", "Alta intensidad"),
    title = "Intensidad Horas Pico"
  ) %>%
  setView(lng = -100.3, lat = 25.67, zoom = 13)

Las áreas más intensamente coloreadas en rojo oscuro (alta intensidad de Gi*) corresponden a zonas con concentración estadísticamente significativa de eventos durante las horas pico. Estas incluyen:

  • Zona centro de Monterrey
  • San Nicolás de los Garza
  • Guadalupe
  • Santa Catarina
  • Escobedo

También se observan focos en áreas más alejadas como Apodaca y algunas partes de Benito Juárez y Cadereyta Jiménez

Esto indica que, aunque la congestión se concentra principalmente en el corazón de la metrópoli, hay hotspots secundarios en zonas periféricas.

# Extraer coordenadas y datos
heat_data <- horas_pico %>%
  mutate(
    lon = st_coordinates(.)[,1],
    lat = st_coordinates(.)[,2]
  ) %>%
  st_drop_geometry()

# Mapa completo
leaflet() %>%
  addProviderTiles(providers$CartoDB.Positron) %>%
  
  # Heatmap
  addHeatmap(
    data = heat_data,
    lng = ~lon,
    lat = ~lat,
    intensity = ~gi_score_numeric,
    radius = 20,
    blur = 15,
    max = 0.1,
    gradient = colorRampPalette(c("#f7aa00", "#c81d25"))(10)
  ) %>%
  
  # Puntos críticos (top 5%)
  addCircleMarkers(
    data = filter(heat_data, gi_score_numeric > quantile(gi_score_numeric, 0.95)),
    radius = ~sqrt(gi_score_numeric)*1.5,
    color = "#000000",
    fillColor = "#c81d25",
    fillOpacity = 0.8,
    popup = ~paste("Score:", round(gi_score_numeric, 2))
  ) %>%
  
  # Controles
  addLayersControl(
    overlayGroups = c("Heatmap", "Puntos Críticos")
  )
## Assuming "lon" and "lat" are longitude and latitude, respectively

Los puntos más oscuros y densos del heatmap, así como los círculos marcados, se concentran principalmente en:

  • Centro de Monterrey
  • San Nicolás de los Garza
  • Santa Catarina
  • Guadalupe
  • Zonas del sur como Benito Juárez también presentan hotspots relevantes
  • Algunas áreas periféricas como García o Pesquería también muestran aparición de hotspots aislados

Análisis Datos de Accidentes en Nuevo León

# Filtrar por EDO == 19
datos <- datos %>% 
  filter(EDO == 19) # 19 es Nuevo León

# Cambiar nombre de los municipios
datos <- datos %>%
  mutate(MPIO = recode(MPIO,
    "1" = "Abasolo",
    "6" = "Apodaca",
    "9" = "Cadereyta Jiménez",  
    "10" = "El Carmen",
    "12" = "Ciénega de Flores",
    "18" = "García",
    "19" = "San Pedro Garza García",
    "21" = "General Escobedo",
    "25" = "General Zuazua",
    "26" = "Guadalupe",
    "31" = "Juárez",
    "39" = "Monterrey",
    "41" = "Pesquería",
    "45" = "Salinas Victoria",
    "46" = "San Nicolás de los Garza",
    "47" = "Hidalgo",
    "48" = "Santa Catarina",
    "49" = "Santiago",
  ))

Hot Spots significativos de accidentes de transporte público dentro del área metropolitana de Monterrey

# Preparar los datos
datos <- datos %>% 
  filter(!is.na(LONGITUD) & !is.na(LATITUD))  # Eliminar NA

# Convertir a objeto espacial (CRS WGS84)
datos_sf <- st_as_sf(datos, coords = c("LONGITUD", "LATITUD"), crs = 4326) %>% 
  st_transform(32614)  # Proyectar a UTM zona 14N (Monterrey)

# Crear matriz de vecindad (radio de 1 km)
coords <- st_coordinates(datos_sf)
vecindad <- dnearneigh(coords, d1 = 0, d2 = 1000)  # Ajusta el radio (en metros)
pesos <- nb2listw(vecindad, style = "B", zero.policy = TRUE)

# Calcular hotspots
datos_sf$hotspot <- as.numeric(localG(datos_sf$CLASE, pesos) > 2)  # Gi* > 2 SD

# Aplicar DBSCAN (500m, mínimo 5 accidentes)
clusters <- dbscan(coords, eps = 500, minPts = 5)
datos_sf$cluster <- as.factor(clusters$cluster)

# Filtrar clústers significativos (excluir ruido, cluster = 0)
clusters_significativos <- datos_sf[datos_sf$cluster != 0, ]
# Clase como numérica
datos_sf$clase_numeric <- as.numeric(as.character(datos_sf$CLASE))

# Crear matriz de vecindad
pesos <- nb2listw(vecindad, style = "W", zero.policy = TRUE)

# Calcular Moran's I
moran.test(datos_sf$clase_numeric, pesos, zero.policy = TRUE)
## 
##  Moran I test under randomisation
## 
## data:  datos_sf$clase_numeric  
## weights: pesos  
## n reduced by no-neighbour observations  
## 
## Moran I statistic standard deviate = 68.312, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      3.054679e-02     -1.431783e-05      2.001475e-07
# Visualización
ggplot() +
  # Hotspots (rojo = Gi* significativo)
  geom_sf(data = datos_sf, aes(color = as.factor(hotspot)), size = 2, alpha = 0.6) +
  scale_color_manual(
    values = c("0" = "gray", "1" = "red"),
    labels = c("No hotspot", "Hotspot")
  ) +
  # Clústers (círculos azules)
  geom_sf(data = clusters_significativos, aes(fill = cluster), shape = 21, size = 3) +
  scale_fill_viridis_d(option = "plasma") +
  labs(
    title = "Hotspots y Clústers de Accidentes en Monterrey",
    subtitle = "Rojo: Hotspots (Gi* > 2 SD) | Círculos: Clústers (DBSCAN)",
    caption = "Radio: 1 km (Hotspots) / 500 m (DBSCAN)"
  ) +
  theme_minimal()

ggplot() +
  geom_sf(
    data = filter(datos_sf, hotspot == 1),  # Solo hotspots
    color = "red",
    size = 2,
    alpha = 0.6
  ) +
  labs(title = "Hotspots Significativos (Gi* > 2 SD)") +
  theme_minimal()

# Resumen estadístico
cat("Número de hotspots:", sum(datos_sf$hotspot), "\n")
## Número de hotspots: NA
cat("Número de clústers:", max(clusters$cluster))
## Número de clústers: 142
# Verificar que 'CLASE' sea un factor
datos_sf$CLASE <- as.factor(datos_sf$CLASE)

# Mapa con ggplot2
ggplot() +
  geom_sf(
    data = datos_sf,
    aes(color = CLASE),  # Color por tipo de accidente
    size = 2,
    alpha = 0.7
  ) +
  scale_color_manual(
    values = c("1" = "red", "2" = "blue", "3" = "green"),  # Personaliza colores
    name = "Tipo de Accidente"  # Título de la leyenda
  ) +
  labs(
    title = "Distribución de Accidentes por Tipo en Monterrey",
    subtitle = "Color según la columna CLASE",
    caption = "Fuente: BASE MUNICIPAL 2023"
  ) +
  theme_minimal() +
  theme(
    legend.position = "bottom",
    plot.title = element_text(face = "bold", hjust = 0.5)
  )

Distribución por municipio y tipo de accidente

# Verificar conteo por municipio
tabla_mpio <- datos_sf %>%
  group_by(MPIO) %>%
  summarise(accidentes = n())

# Accidentes por tipo
tabla_clase <- datos_sf %>%
  group_by(CLASE) %>%
  summarise(cantidad = n())


ggplot(tabla_mpio, aes(x = reorder(MPIO, -accidentes), y = accidentes)) +
  geom_col(fill = "steelblue") +
  coord_flip() +
  labs(title = "Accidentes por Municipio", x = "Municipio", y = "Cantidad")

ggplot(tabla_clase, aes(x = as.factor(CLASE), y = cantidad, fill = as.factor(CLASE))) +
  geom_col() +
  labs(title = "Accidentes por Tipo (CLASE)", x = "Tipo de Accidente", y = "Cantidad") +
  theme_minimal()

Total de accidentes, muertos y heridos por municipio

resumen_mpio <- datos_sf %>%
  group_by(MPIO) %>%
  summarise(
    Total_Accidentes = n(),
    Total_Muertos = sum(TOTMUERTOS, na.rm = TRUE),
    Total_Heridos = sum(TOTHERIDOS, na.rm = TRUE)
  ) %>%
  arrange(desc(Total_Accidentes)) %>%
  slice_head(n = 10)  # Top 10 municipios


resumen_mpio_long <- resumen_mpio %>%
  pivot_longer(cols = c("Total_Muertos", "Total_Heridos"),
               names_to = "Tipo",
               values_to = "Cantidad")

ggplot(resumen_mpio_long, aes(x = reorder(MPIO, -Cantidad), y = Cantidad, fill = Tipo)) +
  geom_col(position = "dodge") +
  labs(title = "Top 10 Municipios por Severidad de Accidentes",
       x = "Municipio",
       y = "Cantidad",
       fill = "Tipo de Víctima") +
  scale_fill_manual(values = c("Total_Muertos" = "#c81d25", "Total_Heridos" = "#f7aa00")) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Resumen de severidad total

datos_sf <- datos_sf %>%
  mutate(
    severidad = TOTMUERTOS + TOTHERIDOS
  )

ggplot(datos_sf, aes(x = severidad)) +
  geom_histogram(bins = 30, fill = "darkred") +
  labs(title = "Distribución de Severidad Total por Accidente", x = "Total Muertos + Heridos")

Autocorrelación Espacial (Moran’s I)

coords <- st_coordinates(datos_sf)
nb <- dnearneigh(coords, 0, 1000)  # 1 km
lw <- nb2listw(nb, style = "W", zero.policy = TRUE)

moran.test(datos_sf$severidad, lw, zero.policy = TRUE)
## 
##  Moran I test under randomisation
## 
## data:  datos_sf$severidad  
## weights: lw  
## n reduced by no-neighbour observations  
## 
## Moran I statistic standard deviate = 54.75, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      2.442388e-02     -1.431783e-05      1.992406e-07

Hotspots con Gi* en severidad

datos_sf$gi_score <- localG(datos_sf$severidad, listw = lw, zero.policy = TRUE)
datos_sf$gi_score_numeric <- as.numeric(datos_sf$gi_score)

# Mapa de Gi*
ggplot(datos_sf) +
  geom_sf(aes(color = gi_score_numeric), size = 1, alpha = 0.7) +
  scale_color_viridis(name = "Gi* Score") +
  labs(title = "Hotspots de Severidad (Gi*)", subtitle = "Valores altos indican concentración de alta severidad") +
  theme_minimal()

hotspots <- datos_sf %>%
  filter(!is.na(gi_score_numeric) & gi_score_numeric > quantile(gi_score_numeric, 0.95, na.rm = TRUE)) %>%
  st_transform(4326)

pal <- colorNumeric("Reds", domain = hotspots$gi_score_numeric)

leaflet(hotspots) %>%
  addProviderTiles(providers$CartoDB.Positron) %>%
  addCircleMarkers(
    radius = ~sqrt(gi_score_numeric)*2,
    fillColor = ~pal(gi_score_numeric),
    color = "#000000",
    fillOpacity = 0.7,
    stroke = FALSE,
    popup = ~paste("Score:", round(gi_score_numeric, 2))
  ) %>%
  addLegend(pal = pal, values = ~gi_score_numeric, title = "Gi* Score")

Distribución por hora del día

datos_sf <- datos_sf %>%
  mutate(
    hora_decimal = HORA + MINUTOS / 60,
    FRANJA = cut(
      hora_decimal,
      breaks = c(0, 6, 9, 12, 15, 18, 21, 24),
      labels = c("Madrugada", "Mañana-Pico", "Mediodía", "Tarde", "Tarde-Pico", "Noche-Temprana", "Noche-Tardía"),
      include.lowest = TRUE,
      right = FALSE
    )
  )

ggplot(datos_sf, aes(x = FRANJA)) +
  geom_bar(fill = "#0072B2") +
  labs(title = "Frecuencia de Accidentes por Franja Horaria", x = "Franja", y = "Cantidad")

Hotspots por franja horaria

# Función para mapa de hotspots por franja
mapa_hotspots_franja <- function(data, franja_label) {
  subset <- data %>% filter(FRANJA == franja_label)
  coords <- st_coordinates(subset)
  nb <- dnearneigh(coords, 0, 1000)
  lw <- nb2listw(nb, style = "W", zero.policy = TRUE)

  subset$gi_score <- localG(subset$TOTMUERTOS + subset$TOTHERIDOS, lw, zero.policy = TRUE)

  subset_top <- subset %>% filter(gi_score > quantile(gi_score, 0.95, na.rm = TRUE))

  leaflet(subset_top %>% st_transform(4326)) %>%
    addProviderTiles(providers$CartoDB.Positron) %>%
    addCircleMarkers(
      radius = 5,
      color = "red",
      fillOpacity = 0.7,
      popup = ~paste("Score:", round(gi_score, 2))
    ) %>%
    addLegend("bottomright", colors = "red", labels = paste("Hotspots -", franja_label)) %>%
    setView(lng = -100.3, lat = 25.67, zoom = 11)
}

mapa_hotspots_franja(datos_sf, "Mañana-Pico")
mapa_hotspots_franja(datos_sf, "Mediodía")
mapa_hotspots_franja(datos_sf, "Tarde")
mapa_hotspots_franja(datos_sf, "Tarde-Pico")
mapa_hotspots_franja(datos_sf, "Noche-Tardía")

Distribución Espacial por Gravedad

datos_sf <- datos_sf %>%
  mutate(gravedad = case_when(
    TOTMUERTOS > 0 ~ "Grave (muertes)",
    TOTHERIDOS > 0 ~ "Media (heridos)",
    TRUE ~ "Leve (solo daños)"
  ))


ggplot(datos_sf) +
  geom_sf(aes(color = gravedad), alpha = 0.6) +
  scale_color_manual(values = c("Grave (muertes)" = "darkred", "Media (heridos)" = "orange", "Leve (solo daños)" = "gray")) +
  labs(title = "Distribución Espacial por Gravedad del Accidente")

Gráfico de líneas de accidentes por mes

datos_sf <- datos_sf %>%
  mutate(fecha = make_date(ANIO, MES, DIA)) %>%
  filter(!is.na(fecha))

# Agrupar por mes
mensual <- datos_sf %>%
  mutate(mes = floor_date(fecha, "month")) %>%
  group_by(mes) %>%
  summarise(n_accidentes = n())

# Gráfico
ggplot(mensual, aes(x = mes, y = n_accidentes)) +
  geom_line(color = "steelblue", size = 1.2) +
  geom_point(color = "darkred") +
  labs(
    title = "Tendencia mensual de accidentes",
    x = "Mes",
    y = "Número de accidentes"
  ) +
  theme_minimal()
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Pronósticos de Accidentes

library(dplyr)
library(lubridate)
library(stringr)

# Crear columna de fecha
datos <- datos %>%
  mutate(
    fecha = as.Date(paste(ANIO, MES, DIA, sep = "-")),
    diasemana_nombre = weekdays(fecha, abbreviate = FALSE),
    mes_nombre = months(fecha, abbreviate = FALSE)
  )

# Lista de columnas de transporte público
vehiculos_tp <- c("CAMPASAJ", "MICROBUS", "PASCAMION", "OMNIBUS", "TRANVIA")

# Agrupar por fecha y calcular variables
accidentes_por_fecha <- datos %>%
  group_by(fecha, diasemana_nombre, mes_nombre) %>%
  summarise(
    n_accidentes = n(),
    total_vehiculos = rowSums(across(c(AUTOMOVIL:OTROVEHIC)), na.rm = TRUE),
    vehiculos_promedio = total_vehiculos / n_accidentes,
    n_transporte_publico = rowSums(across(all_of(vehiculos_tp)), na.rm = TRUE),
    prop_transporte_publico = n_transporte_publico / total_vehiculos,
    total_muertos = sum(TOTMUERTOS, na.rm = TRUE),
    total_heridos = sum(TOTHERIDOS, na.rm = TRUE),
    tasa_mortalidad = total_muertos / n_accidentes,
    clase_accidente_frecuente = names(which.max(table(CLASE))),
    tipo_accidente_frecuente = names(which.max(table(TIPACCID))),
    .groups = "drop"
  )
## Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in
## dplyr 1.1.0.
## ℹ Please use `reframe()` instead.
## ℹ When switching from `summarise()` to `reframe()`, remember that `reframe()`
##   always returns an ungrouped data frame and adjust accordingly.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
# Convertir a factores para renombrar clases/tipos
accidentes_por_fecha <- accidentes_por_fecha %>%
  mutate(
    clase_accidente_frecuente = recode(clase_accidente_frecuente, `3` = "Solo daños"),
    tipo_accidente_frecuente = recode(tipo_accidente_frecuente, `1` = "Colisión con vehículo automotor")
  )

# Detectar outliers en número de accidentes
q1 <- quantile(accidentes_por_fecha$n_accidentes, 0.25)
q3 <- quantile(accidentes_por_fecha$n_accidentes, 0.75)
iqr <- q3 - q1
limite_sup <- q3 + 1.5 * iqr

accidentes_por_fecha <- accidentes_por_fecha %>%
  mutate(
    outlier = ifelse(n_accidentes > limite_sup, "Outlier", "Normal")
  )
glimpse(accidentes_por_fecha)
## Rows: 69,902
## Columns: 14
## $ fecha                     <date> 2023-01-01, 2023-01-01, 2023-01-01, 2023-01…
## $ diasemana_nombre          <chr> "domingo", "domingo", "domingo", "domingo", …
## $ mes_nombre                <chr> "enero", "enero", "enero", "enero", "enero",…
## $ n_accidentes              <int> 110, 110, 110, 110, 110, 110, 110, 110, 110,…
## $ total_vehiculos           <dbl> 1, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2,…
## $ vehiculos_promedio        <dbl> 0.009090909, 0.018181818, 0.018181818, 0.018…
## $ n_transporte_publico      <dbl> 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1,…
## $ prop_transporte_publico   <dbl> 0.0000000, 0.0000000, 0.5000000, 0.5000000, …
## $ total_muertos             <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
## $ total_heridos             <dbl> 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, …
## $ tasa_mortalidad           <dbl> 0.009090909, 0.009090909, 0.009090909, 0.009…
## $ clase_accidente_frecuente <chr> "Solo daños", "Solo daños", "Solo daños", "S…
## $ tipo_accidente_frecuente  <chr> "Colisión con vehículo automotor", "Colisión…
## $ outlier                   <chr> "Normal", "Normal", "Normal", "Normal", "Nor…
# 1. Agregación diaria 
accidentes_diarios <- accidentes_por_fecha %>%
  group_by(fecha) %>%
  summarise(
    n_accidentes = first(n_accidentes),
    dia_semana = first(diasemana_nombre),
    mes = first(mes_nombre),
    transporte_publico = sum(n_transporte_publico),
    muertos = sum(total_muertos),
    heridos = sum(total_heridos)
  ) %>%
  ungroup() %>%
  filter(fecha >= "2023-01-01") %>% 
  arrange(fecha)
# 2. Preparar datos para Prophet
df_prophet <- accidentes_diarios %>%
  select(ds = fecha, y = n_accidentes) %>%
  mutate(
    dia_semana = weekdays(ds),  # Extraer día de la semana como texto
    es_fin_de_semana = ifelse(dia_semana %in% c("sábado", "domingo", "Saturday", "Sunday"), 1, 0)
  )

# 3. Modelo Prophet con regresores adicionales
modelo_prophet <- prophet(
  growth = "flat",  # Asumimos estacionariedad en la tendencia
  yearly.seasonality = TRUE,
  weekly.seasonality = TRUE,
  daily.seasonality = FALSE
)

# Añadir regresores adicionales
modelo_prophet <- add_regressor(modelo_prophet, 'es_fin_de_semana')

# Ajustar modelo
modelo_prophet <- fit.prophet(modelo_prophet, df_prophet)

# 4. Crear dataframe futuro con características
futuro <- make_future_dataframe(modelo_prophet, periods = 730) %>%
  mutate(
    dia_semana = weekdays(ds),
    es_fin_de_semana = ifelse(dia_semana %in% c("sábado", "domingo", "Saturday", "Sunday"), 1, 0)
  )

# 5. Pronóstico
pronostico <- predict(modelo_prophet, futuro)
# Gráfico estático con componentes
plot(modelo_prophet, pronostico) +
  labs(title = "Pronóstico diario de accidentes 2023-2025",
       x = "Fecha", y = "Número de accidentes") +
  theme_minimal()

# Para ver los componentes estacionales
prophet_plot_components(modelo_prophet, pronostico)

Percepción del transporte

df <- read_excel("C:\\Users\\mari0\\OneDrive\\Documents\\R Studio\\8 semestre\\Evidencia\\Evidencia1_parte2\\PERCEPCION DE LA CIUDADANIA DEL TRANSPORTE PUBLICO DE LA ZONA METROPOLITANA DE MONTERREY  (Respuestas).xlsx",
                 sheet = "Respuestas de formulario 1")
colnames(df) <- c(
  "marca_temporal", "frecuencia_uso", "motivo_uso", "medio_transporte",
  "accesibilidad", "limpieza", "seguridad", "victima_delito",
  "problema_principal", "costo_percibido", "mejoras_importantes",
  "preparacion_mundial", "impacto_mundial", "edad", "genero",
  "ocupacion", "municipio_residencia", "aplicacion"
)
sum(is.na(df))
## [1] 3
moda <- function(x) {
  ux <- na.omit(unique(x))
  ux[which.max(tabulate(match(x, ux)))]
}
# Aplicar solo a columnas con NA
for (col in names(df)) {
  if (any(is.na(df[[col]]))) {
    df[[col]][is.na(df[[col]])] <- moda(df[[col]])
  }
}

Distribución por edad y género

ggplot(df, aes(x = edad)) +
  geom_bar(fill = "steelblue") +
  labs(title = "Distribución de edades", x = "Rango de edad", y = "Frecuencia") +
  theme_minimal()

ggplot(df, aes(x = genero)) +
  geom_bar(fill = "orchid") +
  labs(title = "Distribución por género", x = "Género", y = "Frecuencia") +
  theme_minimal()

Género vs Percepción de seguridad

ggplot(df, aes(x = genero, fill = seguridad)) +
  geom_bar(position = "fill") +
  labs(title = "Percepción de seguridad por género", x = "Género", y = "Proporción") +
  scale_y_continuous(labels = scales::percent) +
  theme_minimal()

Percepción del costo por grupo de edad

ggplot(df, aes(x = edad, fill = costo_percibido)) +
  geom_bar(position = "fill") +
  labs(title = "Percepción del costo del transporte por edad", x = "Edad", y = "Proporción") +
  scale_y_continuous(labels = scales::percent) +
  theme_minimal()

Problemas principales percibidos

ggplot(df, aes(x = problema_principal)) +
  geom_bar(fill = "darkorange") +
  coord_flip() +
  labs(title = "Problemas principales en el transporte", x = "Problema", y = "Frecuencia") +
  theme_minimal()

Pruebas Chi-Cuadrada

Prueba 1: Género vs percepción de seguridad

cat("1. Género vs Seguridad\n")
## 1. Género vs Seguridad
tabla1 <- table(df$genero, df$seguridad)
print(tabla1)
##            
##             No, me siento inseguro/a Sí, completamente
##   Femenino                        29                34
##   Masculino                       23                22
##            
##             Sí, pero con algunas preocupaciones
##   Femenino                                   46
##   Masculino                                  23
print(chisq.test(tabla1))
## 
##  Pearson's Chi-squared test
## 
## data:  tabla1
## X-squared = 1.5145, df = 2, p-value = 0.469

Prueba 2: Municipio vs preparación para el Mundial

cat("\n2. Municipio vs Preparación Mundial\n")
## 
## 2. Municipio vs Preparación Mundial
tabla2 <- table(df$municipio_residencia, df$preparacion_mundial)
print(tabla2)
##                 
##                  No, no está preparado Sí, completamente
##   Allende                            1                 0
##   Apodaca                            4                 0
##   Cadereyta                          1                 0
##   Escobedo                          17                 0
##   García                             4                 0
##   Guadalupe                         17                 0
##   Juárez                             4                 0
##   Monterrey                         72                 4
##   San Nicolás                        4                 0
##   Santa Catarina                    16                 0
##   Santiago                           7                 2
##                 
##                  Sí, pero requiere mejoras
##   Allende                                0
##   Apodaca                                2
##   Cadereyta                              0
##   Escobedo                               0
##   García                                 2
##   Guadalupe                              1
##   Juárez                                 1
##   Monterrey                             14
##   San Nicolás                            2
##   Santa Catarina                         2
##   Santiago                               0
print(chisq.test(tabla2))
## Warning in chisq.test(tabla2): Chi-squared approximation may be incorrect
## 
##  Pearson's Chi-squared test
## 
## data:  tabla2
## X-squared = 24.421, df = 20, p-value = 0.2245

Prueba 3: Ocupación vs percepción del costo

cat("\n3. Ocupación vs Costo percibido\n")
## 
## 3. Ocupación vs Costo percibido
tabla3 <- table(df$ocupacion, df$costo_percibido)
print(tabla3)
##                 
##                  No, es demasiado barato No, es demasiado caro Sí, es adecuado
##   Desempleado(a)                       0                     2               0
##   Empleado(a)                          2                    77              31
##   Empresario(a)                        0                     0               1
##   Estudiante                           0                    31              33
print(chisq.test(tabla3))
## Warning in chisq.test(tabla3): Chi-squared approximation may be incorrect
## 
##  Pearson's Chi-squared test
## 
## data:  tabla3
## X-squared = 13.176, df = 6, p-value = 0.04033

Prueba 4: Edad vs percepción de disponibilidad (usando problema_principal)

cat("\n4. Edad vs Problema principal (como proxy de disponibilidad)\n")
## 
## 4. Edad vs Problema principal (como proxy de disponibilidad)
tabla4 <- table(df$edad, df$problema_principal)
print(tabla4)
##                   
##                    Condiciones de las unidades Costo Puntualidad Seguridad
##   18-24 años                                17     7          48         2
##   25-34 años                                 6     5          12         1
##   35-44 años                                 3     4          19         0
##   45-54 años                                11     4          14         0
##   55 años o más                              6     0           8         2
##   Menos de 18 años                           3     0           3         2
print(chisq.test(tabla4))
## Warning in chisq.test(tabla4): Chi-squared approximation may be incorrect
## 
##  Pearson's Chi-squared test
## 
## data:  tabla4
## X-squared = 28.021, df = 15, p-value = 0.02144

Prueba 5: Limpieza vs preparación para el Mundial

cat("\n5. Limpieza vs Preparación Mundial\n")
## 
## 5. Limpieza vs Preparación Mundial
tabla5 <- table(df$limpieza, df$preparacion_mundial)
print(tabla5)
##            
##             No, no está preparado Sí, completamente Sí, pero requiere mejoras
##   Bueno                        35                 4                         9
##   Excelente                     7                 0                         0
##   Mala                         61                 1                         2
##   Regular                      44                 1                        13
print(chisq.test(tabla5))
## Warning in chisq.test(tabla5): Chi-squared approximation may be incorrect
## 
##  Pearson's Chi-squared test
## 
## data:  tabla5
## X-squared = 17.554, df = 6, p-value = 0.00745

Prueba 6: Puntualidad vs preparación para el Mundial

cat("\n6. Puntualidad (problema principal) vs Preparación Mundial\n")
## 
## 6. Puntualidad (problema principal) vs Preparación Mundial
tabla6 <- table(df$problema_principal, df$preparacion_mundial)
print(tabla6)
##                              
##                               No, no está preparado Sí, completamente
##   Condiciones de las unidades                    42                 0
##   Costo                                          18                 1
##   Puntualidad                                    85                 5
##   Seguridad                                       2                 0
##                              
##                               Sí, pero requiere mejoras
##   Condiciones de las unidades                         4
##   Costo                                               1
##   Puntualidad                                        14
##   Seguridad                                           5
print(chisq.test(tabla6))
## Warning in chisq.test(tabla6): Chi-squared approximation may be incorrect
## 
##  Pearson's Chi-squared test
## 
## data:  tabla6
## X-squared = 24.756, df = 6, p-value = 0.0003789

Prueba 7: Disponibilidad negativa vs positiva y su relación con preparación Mundial

df$disponibilidad <- ifelse(df$problema_principal == "Disponibilidad de unidades", "Negativa", "Positiva")
cat("\n7. Disponibilidad vs Preparación Mundial\n")
## 
## 7. Disponibilidad vs Preparación Mundial
tabla7 <- table(df$disponibilidad, df$preparacion_mundial)
print(tabla7)
##           
##            No, no está preparado Sí, completamente Sí, pero requiere mejoras
##   Positiva                   147                 6                        24
print(chisq.test(tabla7))
## 
##  Chi-squared test for given probabilities
## 
## data:  tabla7
## X-squared = 199.63, df = 2, p-value < 2.2e-16

Prueba 8: Edad vs percepción del costo

cat("\n8. Edad vs Costo percibido\n")
## 
## 8. Edad vs Costo percibido
tabla8 <- table(df$edad, df$costo_percibido)
print(tabla8)
##                   
##                    No, es demasiado barato No, es demasiado caro
##   18-24 años                             2                    38
##   25-34 años                             0                    14
##   35-44 años                             0                    20
##   45-54 años                             0                    26
##   55 años o más                          0                     9
##   Menos de 18 años                       0                     3
##                   
##                    Sí, es adecuado
##   18-24 años                    34
##   25-34 años                    10
##   35-44 años                     6
##   45-54 años                     3
##   55 años o más                  7
##   Menos de 18 años               5
print(chisq.test(tabla8))
## Warning in chisq.test(tabla8): Chi-squared approximation may be incorrect
## 
##  Pearson's Chi-squared test
## 
## data:  tabla8
## X-squared = 19.893, df = 10, p-value = 0.03028
LS0tDQp0aXRsZTogIkV2aWRlbmNpYTogQW7DoWxpc2lzIEVzcGFjaWFsIHkgUHJlZGljdGl2byBkZSBsYSBNb3ZpbGlkYWQgeSBBY2NpZGVudGVzIFZpYWxlcyBlbiBlbCBUcmFuc3BvcnRlIFDDumJsaWNvIGRlIE51ZXZvIExlw7NuIg0KYXV0aG9yOiAnRXF1aXBvIDI6IExlc2xpZSBNb250ZW1heW9yIEEwMDgzMzAyMiwgWWVzc2ljYSBBY29zdGEgQTAwODMzNjE3LCBDYXJsYSBWYWxlcmlhDQogIE5hbmdvIEEwMTE3NDEwNiwgTWFyaWFuYSBSYW3DrXJleiBBMDExNzQxNTUnDQpkYXRlOiAiMjAyNS0wNS0yNSINCm91dHB1dDogDQogaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlIA0KICAgIHRoZW1lOiBqb3VybmFsDQotLS0NCg0KIyMgSW5zdGFsYXIgcGFxdWV0ZXMgeSBsbGFtYXIgbGlicmVyw61hcw0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShzZikgICAgICAgICAgDQpsaWJyYXJ5KHNwZGVwKSAgICAgICANCmxpYnJhcnkoZ2dwbG90MikgICAgIA0KbGlicmFyeShkcGx5cikgICAgICANCmxpYnJhcnkobGVhZmxldCkgICAgIA0KbGlicmFyeShodG1sdG9vbHMpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KGZvcmVpZ24pDQpsaWJyYXJ5KHRpZ3JpcykNCmxpYnJhcnkocmdlb2RhKQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpsaWJyYXJ5KHZpcmlkaXMpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHRtYXApDQpsaWJyYXJ5KHNmKSANCmxpYnJhcnkoc3ApDQpsaWJyYXJ5KHNwYXRpYWxyZWcpDQpsaWJyYXJ5KHN0YXJnYXplcikNCmxpYnJhcnkocGxtKQ0KbGlicmFyeShzcGxtKQ0KbGlicmFyeShwc3BhdHJlZykNCmxpYnJhcnkocmVnY2xhc3MpDQpsaWJyYXJ5KG1jdGVzdCkNCmxpYnJhcnkobG10ZXN0KQ0KbGlicmFyeShzcERhdGEpDQpsaWJyYXJ5KG1hcHZpZXcpDQpsaWJyYXJ5KG5hbmlhcikNCmxpYnJhcnkoZGxvb2tyKQ0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkoZTEwNzEpDQpsaWJyYXJ5KFNwYXJzZU0pDQpsaWJyYXJ5KE1ldHJpY3MpDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCmxpYnJhcnkoaW5zaWdodCkNCmxpYnJhcnkoanRvb2xzKQ0KbGlicmFyeSh4Z2Jvb3N0KQ0KbGlicmFyeShEaWFncmFtbWVSKQ0KbGlicmFyeShlZmZlY3RzKQ0KbGlicmFyeShzZikNCmxpYnJhcnkocHVycnIpDQpsaWJyYXJ5KGdlb3NwaGVyZSkNCmxpYnJhcnkoZ21hcHNkaXN0YW5jZSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShsZWFmbGV0LmV4dHJhcykNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KHByb3BoZXQpDQppZiAoIXJlcXVpcmUoInBhY21hbiIpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQ0KcGFjbWFuOjpwX2xvYWQoDQogIHNmLCAgICAgICAgICAjIERhdG9zIGVzcGFjaWFsZXMNCiAgc3BkZXAsICAgICAgICMgQXV0b2NvcnJlbGFjacOzbiBlc3BhY2lhbA0KICBnZ3Bsb3QyLCAgICAgIyBWaXN1YWxpemFjacOzbg0KICBkYnNjYW4sICAgICAgIyBEZXRlY2Npw7NuIGRlIGNsw7pzdGVycw0KICBkcGx5ciAgICAgICAgIyBNYW5pcHVsYWNpw7NuIGRlIGRhdG9zDQopDQppZiAoIXJlcXVpcmUoImdlb3NwaGVyZSIpKSBpbnN0YWxsLnBhY2thZ2VzKCJnZW9zcGhlcmUiKQ0KaWYgKCFyZXF1aXJlKCJnbWFwc2Rpc3RhbmNlIikpaW5zdGFsbC5wYWNrYWdlcygiZ21hcHNkaXN0YW5jZSIpDQpgYGANCg0KIyMgSW1wb3J0YXIgbGFzIGJhc2VzIGRlIGRhdG9zDQoNCmBgYHtyfQ0KZGF0b3MgPC0gcmVhZF9leGNlbCgiQzpcXFVzZXJzXFxtYXJpMFxcT25lRHJpdmVcXERvY3VtZW50c1xcUiBTdHVkaW9cXDggc2VtZXN0cmVcXEV2aWRlbmNpYVxcQkFTRSBNVU5JQ0lQQUxfQUNDSURFTlRFUyBERSBUUkFOU0lUTyBHRU9SUkVGRVJFTkNJQURPU18yMDIzLnhsc3giKQ0KDQpnZW9qc29uX2RhdGEgPC0gc3RfcmVhZCgiVHJhbnNwb3J0ZS5nZW9qc29uIikgJT4lIA0KICBzdF90cmFuc2Zvcm0oMzI2MTQpICU+JSAgIyBDb252ZXJ0aXIgYSBVVE0gWm9uZSAxNE4gKE1vbnRlcnJleSkNCiAgZmlsdGVyKCFzdF9pc19lbXB0eSguKSkgICMgRWxpbWluYXIgZ2VvbWV0csOtYXMgdmFjw61hcw0KYGBgDQoNCiMjIEFuw6FsaXNpcyBEYXRvcyBUcsOhbnNpdG8gZW4gTnVldm8gTGXDs24NCg0KTGEgaW5mb3JtYWNpw7NuIGRlIHRyYW5zcG9ydGUgcHJvdmllbmUgZGUgbGEgZW5jdWVzdGEgb3JpZ2VuLWRlc3Rpbm8gZGVsIDIwMTkgcXVlIGVsIGdvYmllcm5vIGRlbCBlc3RhZG8gZGUgTnVldm8gTGXDs24gcmVhbGl6w7MgY29tbyBwYXJ0ZSBkZWwgUHJvZ3JhbWEgSW50ZWdyYWwgZGUgTW92aWxpZGFkIFVyYmFuYSBTdXN0ZW50YWJsZSAoUElNVVMpIGRlbCDDgXJlYSBNZXRyb3BvbGl0YW5hIGRlIE1vbnRlcnJleS4gVHJhbnNjb25zdWx0IGZ1ZSBsYSBlbXByZXNhIGNvbnN1bHRvcmEgcmVzcG9uc2FibGUgZGVsIGxldmFudGFtaWVudG8gZGUgbGEgZW5jdWVzdGEuDQoNCiMjIyBIb3QgU3BvdHMgc2lnbmlmaWNhdGl2b3MgZGUgY29uZ2VzdGlvbmFtaWVudG8gdmVoaWN1bGFyIGRlbnRybyBkZWwgw6FyZWEgbWV0cm9wb2xpdGFuYSBkZSBNb250ZXJyZXkNCg0KYGBge3J9DQojIERlc2FjdGl2YXIgZ2VvbWV0csOtYSBlc2bDqXJpY2EgKGV2aXRhIGVycm9yZXMpDQpzZjo6c2ZfdXNlX3MyKEZBTFNFKQ0KDQoNCiMgQ3JlYXIgdmFyaWFibGUgbnVtw6lyaWNhIHBhcmEgYW7DoWxpc2lzDQpnZW9qc29uX2RhdGEgPC0gZ2VvanNvbl9kYXRhICU+JQ0KICBtdXRhdGUoDQogICAgcGVzb190cmFuc3BvcnRlID0gY2FzZV93aGVuKA0KICAgICAgVHJhbnNwb3J0ZSAlaW4lIGMoIkF1dMOzbW92aWwiLCAiVFBVQiIsICJUYXhpIikgfiAzLA0KICAgICAgVHJhbnNwb3J0ZSAlaW4lIGMoIlRyYW5zcG9ydGUgZGUgUGVyc29uYWwiLCAiVHJhbnNwb3J0ZSBlc2NvbGFyIikgfiAyLA0KICAgICAgVFJVRSB+IDEgICMgUGFyYSBiaWNpY2xldGFzLCBjYW1pbmFuZG8geSBvdHJvcw0KICAgICkNCiAgKQ0KDQoNCiMgT2J0ZW5lciBjb29yZGVuYWRhcyBkZSBsb3MgY2VudHJvaWRlcw0KY29vcmRzIDwtIHN0X2Nvb3JkaW5hdGVzKHN0X2NlbnRyb2lkKGdlb2pzb25fZGF0YSkpDQoNCiMgQ2FsY3VsYXIgdmVjaW5vcyBlbiB1biByYWRpbyBkZSA4MDAgbWV0cm9zDQpuYiA8LSBkbmVhcm5laWdoKGNvb3JkcywgZDEgPSAwLCBkMiA9IDgwMCkNCg0KIyBJZGVudGlmaWNhciB5IGVsaW1pbmFyIHB1bnRvcyBhaXNsYWRvcyAoc2luIHZlY2lub3MpDQphaXNsYWRvcyA8LSB3aGljaChjYXJkKG5iKSA9PSAwKQ0KaWYobGVuZ3RoKGFpc2xhZG9zKSA+IDApew0KICBnZW9qc29uX2RhdGEgPC0gZ2VvanNvbl9kYXRhWy1haXNsYWRvcywgXQ0KICBjb29yZHMgPC0gc3RfY29vcmRpbmF0ZXMoc3RfY2VudHJvaWQoZ2VvanNvbl9kYXRhKSkNCiAgbmIgPC0gZG5lYXJuZWlnaChjb29yZHMsIGQxID0gMCwgZDIgPSA4MDApDQp9DQoNCiMgQ3JlYXIgbWF0cml6IGRlIHBlc29zIGVzcGFjaWFsZXMNCnBlc29zIDwtIG5iMmxpc3R3KG5iLCBzdHlsZSA9ICJCIikgICMgUGVzb3MgYmluYXJpb3MNCg0KIyBDYWxjdWxhciBob3RzcG90cyAoR2V0aXMtT3JkIEdpKikNCmdlb2pzb25fZGF0YSRnaV9zY29yZSA8LSBsb2NhbEcoDQogIHggPSBnZW9qc29uX2RhdGEkcGVzb190cmFuc3BvcnRlLA0KICBsaXN0dyA9IHBlc29zLA0KICB6ZXJvLnBvbGljeSA9IEZBTFNFDQopDQoNCmBgYA0KDQpgYGB7cn0NCiMgVmlzdWFsaXphY2nDs24gZGUgcmVzdWx0YWRvcw0KDQojIENvbnZlcnRpciBMSU5FU1RSSU5HIGEgcHVudG9zIChleHRyZW1vcyBkZSBjYWRhIGzDrW5lYSkNCnB1bnRvc19ob3RzcG90cyA8LSBzdF9jYXN0KGdlb2pzb25fZGF0YSwgIlBPSU5UIikgJT4lIA0KICBzdF90cmFuc2Zvcm0oNDMyNikgICMgQ29udmVydGlyIGEgV0dTODQNCg0KIyBDb252ZXJ0aXIgbG9zIHNjb3JlcyBhIG51bcOpcmljbw0KZ2VvanNvbl9kYXRhJGdpX3Njb3JlX251bWVyaWMgPC0gYXMubnVtZXJpYyhnZW9qc29uX2RhdGEkZ2lfc2NvcmUpDQpgYGANCg0KYGBge3J9DQojIENvbnZlcnRpciBhIHB1bnRvcyB5IGZpbHRyYXINCnB1bnRvc19zaWduaWZpY2F0aXZvcyA8LSBnZW9qc29uX2RhdGEgJT4lIA0KICBzdF9jYXN0KCJQT0lOVCIpICU+JSANCiAgc3RfdHJhbnNmb3JtKDQzMjYpICU+JSANCiAgZmlsdGVyKGdpX3Njb3JlX251bWVyaWMgPiBxdWFudGlsZShnaV9zY29yZV9udW1lcmljLCAwLjk3KSkgICMgVG9wIDMlDQoNCiMgUGFsZXRhIGRlIGNvbG9yIHBhcmEgdmFsb3JlcyBleHRyZW1vcw0KcGFsIDwtIGNvbG9yTnVtZXJpYygNCiAgcGFsZXR0ZSA9ICJSZWRzIiwNCiAgZG9tYWluID0gYygyLjUsIG1heChwdW50b3Nfc2lnbmlmaWNhdGl2b3MkZ2lfc2NvcmVfbnVtZXJpYywgbmEucm0gPSBUUlVFKSksDQogIG5hLmNvbG9yID0gInRyYW5zcGFyZW50Ig0KKQ0KDQojIE1hcGEgZmluYWwNCmxlYWZsZXQocHVudG9zX3NpZ25pZmljYXRpdm9zKSAlPiUNCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkQ2FydG9EQi5Qb3NpdHJvbikgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoDQogICAgcmFkaXVzID0gfnNxcnQoZ2lfc2NvcmVfbnVtZXJpYykqMiwgICMgVGFtYcOxbyBwcm9wb3JjaW9uYWwgYWwgc2NvcmUNCiAgICBjb2xvciA9IH5wYWwoZ2lfc2NvcmVfbnVtZXJpYyksDQogICAgZmlsbE9wYWNpdHkgPSAwLjcsDQogICAgc3Ryb2tlID0gRkFMU0UsDQogICAgcG9wdXAgPSB+cGFzdGUoIlNjb3JlOiIsIHJvdW5kKGdpX3Njb3JlX251bWVyaWMsIDIpKQ0KICApICU+JQ0KICBhZGRMZWdlbmQoDQogICAgcG9zaXRpb24gPSAiYm90dG9tcmlnaHQiLA0KICAgIHBhbCA9IHBhbCwNCiAgICB2YWx1ZXMgPSB+Z2lfc2NvcmVfbnVtZXJpYywNCiAgICB0aXRsZSA9ICJIb3RzcG90czxicj4oVG9wIDMlKSINCiAgKSAlPiUNCiAgc2V0VmlldyhsbmcgPSAtMTAwLjMsIGxhdCA9IDI1LjY3LCB6b29tID0gMTIpDQpgYGANCg0KYGBge3J9DQojIFZlciBkaXN0cmlidWNpw7NuIGRlIHNjb3Jlcw0KZ2dwbG90KGdlb2pzb25fZGF0YSwgYWVzKHggPSBnaV9zY29yZV9udW1lcmljKSkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDUwKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKGdlb2pzb25fZGF0YSRnaV9zY29yZV9udW1lcmljLCAwLjk1KSwgY29sb3IgPSAicmVkIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1Y2nDs24gZGUgR2kqIFNjb3JlcyIsIHN1YnRpdGxlID0gIkzDrW5lYSByb2phID0gcGVyY2VudGlsIDk1JSIpDQpgYGANCg0KTGEgZGlzdHJpYnVjacOzbiBlcyBhc2ltw6l0cmljYSBjb24gc2VzZ28gbmVnYXRpdm86IGxhIG1heW9yIHBhcnRlIGRlIGxvcyB2YWxvcmVzIEdpXCogc2UgY29uY2VudHJhbiBlbnRyZSAtMTAgeSArOCwgY29uIHVuYSBsYXJnYSBjb2xhIGhhY2lhIGxhIGl6cXVpZXJkYSAodmFsb3JlcyBtdXkgbmVnYXRpdm9zKS4NCg0KRXN0byBpbmRpY2EgcXVlIGxhIG1heW9yw61hIGRlIGxvcyB0cmF5ZWN0b3Mgbm8gcHJlc2VudGFuIGNvbmNlbnRyYWNpw7NuIHNpZ25pZmljYXRpdmEgKEdpXCogY2VyY2FubyBhIDApLCBwZXJvIHPDrSBoYXkgYWxndW5vcyBjb24gY2xhcmEgZGlzcGVyc2nDs24gKEdpXCogbXV5IG5lZ2F0aXZvKS4NCg0KTGEgbMOtbmVhIHJvamEgbWFyY2EgZWwgcGVyY2VudGlsIDk1LCBlcyBkZWNpciwgZWwgdW1icmFsIGEgcGFydGlyIGRlbCBjdWFsIHNlIGNvbnNpZGVyYSBxdWUgdW4gR2lcKiBzY29yZSBlc3TDoSBlbiBlbCA1JSBzdXBlcmlvciwgbG8gY3VhbCBpbmRpY2EgdW4gaG90c3BvdCBlc3RhZMOtc3RpY2FtZW50ZSBzaWduaWZpY2F0aXZvLg0KDQpgYGB7cn0NCg0KIyBDb252ZXJ0aXIgSG9yYU9yaSBhIGZvcm1hdG8gaG9yYSB5IGx1ZWdvIGEgaG9yYXMgZGVjaW1hbGVzDQpwdW50b3NfaG9yYSA8LSBnZW9qc29uX2RhdGEgJT4lDQogIHN0X2Nhc3QoIlBPSU5UIikgJT4lDQogIHN0X3RyYW5zZm9ybSg0MzI2KSAlPiUNCiAgbXV0YXRlKA0KICAgIGhvcmFfZGVjaW1hbCA9IHBlcmlvZF90b19zZWNvbmRzKGhtcyhIb3JhT3JpKSkgLyAzNjAwLCAgIyBDb252ZXJ0aXIgYSBob3JhcyBjb24gZGVjaW1hbGVzDQogICAgZnJhbmphX2hvcmFyaWEgPSBjdXQoDQogICAgICBob3JhX2RlY2ltYWwsDQogICAgICBicmVha3MgPSBjKDAsIDYsIDksIDEyLCAxNSwgMTgsIDIxLCAyNCksDQogICAgICBsYWJlbHMgPSBjKCJNYWRydWdhZGEiLCAiTWHDsWFuYS1QaWNvIiwgIk1lZGlvZMOtYSIsICJUYXJkZSIsICJUYXJkZS1QaWNvIiwgIk5vY2hlLVRlbXByYW5hIiwgIk5vY2hlLVRhcmRpYSIpLA0KICAgICAgcmlnaHQgPSBGQUxTRSwNCiAgICAgIGluY2x1ZGUubG93ZXN0ID0gVFJVRQ0KICAgICkNCiAgKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShob3JhX2RlY2ltYWwpKSAgIyBFbGltaW5hciByZWdpc3Ryb3Mgc2luIGhvcmEgdsOhbGlkYQ0KDQojIFZlcmlmaWNhcg0Kc3VtbWFyeShwdW50b3NfaG9yYSRob3JhX2RlY2ltYWwpDQp0YWJsZShwdW50b3NfaG9yYSRmcmFuamFfaG9yYXJpYSwgdXNlTkEgPSAiYWx3YXlzIikNCmBgYA0KDQpgYGB7cn0NCiMgUGFsZXRhIGRlIGNvbG9yZXMgcGFyYSBmcmFuamFzDQpwYWxfaG9yYSA8LSBjb2xvckZhY3RvcigNCiAgcGFsZXR0ZSA9IGMoIiMxYTJiNWMiLCAiI2Y3YWEwMCIsICIjYTRkNGFlIiwgIiNmZjZiNmIiLCAiI2M4MWQyNSIsICIjNWUxN2ViIiwgIiMyZTJlMmUiKSwNCiAgZG9tYWluID0gcHVudG9zX2hvcmEkZnJhbmphX2hvcmFyaWENCikNCg0KIyBDcmVhciBtYXBhDQpsZWFmbGV0KHB1bnRvc19ob3JhKSAlPiUNCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkQ2FydG9EQi5Qb3NpdHJvbikgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoDQogICAgcmFkaXVzID0gfmlmZWxzZShnaV9zY29yZV9udW1lcmljID4gMiwgNiwgMyksDQogICAgY29sb3IgPSB+cGFsX2hvcmEoZnJhbmphX2hvcmFyaWEpLA0KICAgIGZpbGxPcGFjaXR5ID0gMC43LA0KICAgIHBvcHVwID0gfnBhc3RlKA0KICAgICAgIjxzdHJvbmc+SG9yYTo8L3N0cm9uZz4gIiwgSG9yYU9yaSwgIjxicj4iLA0KICAgICAgIjxzdHJvbmc+RnJhbmphOjwvc3Ryb25nPiAiLCBmcmFuamFfaG9yYXJpYSwgIjxicj4iLA0KICAgICAgIjxzdHJvbmc+U2NvcmU6PC9zdHJvbmc+ICIsIHJvdW5kKGdpX3Njb3JlX251bWVyaWMsIDIpDQogICAgKQ0KICApICU+JQ0KICBhZGRMZWdlbmQoDQogICAgcG9zaXRpb24gPSAiYm90dG9tcmlnaHQiLA0KICAgIHBhbCA9IHBhbF9ob3JhLA0KICAgIHZhbHVlcyA9IH5mcmFuamFfaG9yYXJpYSwNCiAgICB0aXRsZSA9ICJGcmFuamFzIEhvcmFyaWFzIg0KICApICU+JQ0KICBzZXRWaWV3KGxuZyA9IC0xMDAuMywgbGF0ID0gMjUuNjcsIHpvb20gPSAxMikNCmBgYA0KDQpgYGB7cn0NCiMgUmVzdW1lbiBlc3RhZMOtc3RpY28gcG9yIGZyYW5qYSBob3JhcmlhDQpwdW50b3NfaG9yYSAlPiUNCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQ0KICBncm91cF9ieShmcmFuamFfaG9yYXJpYSkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBuX3B1bnRvcyA9IG4oKSwNCiAgICBhdmdfc2NvcmUgPSBtZWFuKGdpX3Njb3JlX251bWVyaWMpLA0KICAgIHBvcmNlbnRhamUgPSBuKCkgLyBucm93KHB1bnRvc19ob3JhKSAqIDEwMA0KICApICU+JQ0KICBhcnJhbmdlKGRlc2MoYXZnX3Njb3JlKSkgJT4lDQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoZnJhbmphX2hvcmFyaWEsIC1hdmdfc2NvcmUpLCB5ID0gYXZnX3Njb3JlKSkgKw0KICBnZW9tX2NvbChhZXMoZmlsbCA9IGZyYW5qYV9ob3JhcmlhKSkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKHJvdW5kKHBvcmNlbnRhamUsMSksICIlIikpLCB2anVzdCA9IC0wLjUpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI2Y3YWEwMCIsICIjYzgxZDI1IiwgIiNmZjZiNmIiLCAiI2E0ZDRhZSIsICIjMWEyYjVjIiwgIiM1ZTE3ZWIiLCAiIzJlMmUyZSIpKSArDQogIGxhYnModGl0bGUgPSAiSW50ZW5zaWRhZCBQcm9tZWRpbyBkZSBIb3RzcG90cyBwb3IgRnJhbmphIEhvcmFyaWEiLA0KICAgICAgIHggPSAiRnJhbmphIEhvcmFyaWEiLA0KICAgICAgIHkgPSAiU2NvcmUgUHJvbWVkaW8gKEdpKikiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCkNvbmdlc3Rpw7NuIHNpZ25pZmljYXRpdmEgKGhvdHNwb3RzIHBvc2l0aXZvcyBmdWVydGVzKTogLSBNYWRydWdhZGEgeSBOb2NoZS1UZW1wcmFuYSB0aWVuZW4gdmFsb3JlcyBkZSBHaVwqIHBvc2l0aXZvcyDihpIgaGF5IGNpZXJ0YSBjb25jZW50cmFjacOzbiBkZSBjb25nZXN0acOzbiAoYXVucXVlIG5vIG11eSBpbnRlbnNhKS4gLSBQZXJvIHN1IGludGVuc2lkYWQgZXMgbGlnZXJhIChHaVwqIFw8IDEpLg0KDQpDb25nZXN0acOzbiBkaXNwZXJzYSBvIHNpbiBjb25jZW50cmFjacOzbiBlc3BhY2lhbCAodmFsb3JlcyBiYWpvcy9uZWdhdGl2b3MgZGUgR2lcKik6DQoNCi0gICBUYXJkZSwgTWHDsWFuYS1QaWNvIHkgTWVkaW9kw61hIG11ZXN0cmFuIHZhbG9yZXMgbmVnYXRpdm9zIGFsdG9zIGRlIEdpXCosIGVzcGVjaWFsbWVudGU6IC0gVGFyZGU6IC0yLjMgLSBNYcOxYW5hLVBpY286IC0xLjcNCi0gICBFc3RvIGluZGljYSBxdWUgZHVyYW50ZSBlc3RhcyBmcmFuamFzIGhvcmFyaWFzLCBsb3MgaW5jaWRlbnRlcyBkZSBjb25nZXN0acOzbiBubyBzZSBjb25jZW50cmFuIGVzcGFjaWFsbWVudGUsIHNpbm8gcXVlIGVzdMOhbiBtw6FzIGRpc3BlcnNvcyBwb3IgbGEgY2l1ZGFkLg0KDQpUcmFuc2ljaW9uZXMgaW50ZXJlc2FudGVzOg0KDQpMYSBOb2NoZS1UYXJkaWEgdGllbmUgdW4gR2lcKiBjZXJjYW5vIGEgMCDihpIgc2luIGV2aWRlbmNpYSBmdWVydGUgZGUgY29uY2VudHJhY2nDs24gbmkgZGlzcGVyc2nDs24uDQoNClRhcmRlLVBpY28gbXVlc3RyYSBhcGVuYXMgdW5hIGxpZ2VyYSBkaXNwZXJzacOzbi4NCg0Kwr9RdcOpIHNpZ25pZmljYSBlc3RvID8gRW4gaG9yYXMgcGljbyAobWHDsWFuYSB5IHRhcmRlKSwgYXVucXVlIGhheSBtdWNob3MgdmlhamVzLCBsb3MgZXZlbnRvcyBkZSBjb25nZXN0acOzbiBlc3TDoW4gZGlzcGVyc29zLCBsbyBjdWFsIHN1Z2llcmUgcXVlIG5vIGhheSB6b25hcyBmaWphcyBkZSBjb25nZXN0acOzbiwgc2lubyBxdWUgc2UgZXNwYXJjZW4gcG9yIHZhcmlhcyBwYXJ0ZXMgZGUgbGEgY2l1ZGFkLg0KDQpFbiBob3JhcyBkZSBiYWphIGFjdGl2aWRhZCAobWFkcnVnYWRhIHkgbm9jaGUtdGVtcHJhbmEpLCBsb3MgcG9jb3MgdmlhamVzIHF1ZSBvY3VycmVuIHRpZW5kZW4gYSBjb25jZW50cmFyc2UgZXNwYWNpYWxtZW50ZSwgcG9zaWJsZW1lbnRlIGVuIGNvcnJlZG9yZXMgdmlhbGVzIGNsYXZlIG8gem9uYXMgY2VudHJhbGVzLg0KDQpQbGFuZWFjacOzbiB1cmJhbmEgbyBkZSB0cmFuc3BvcnRlIHDDumJsaWNvIGRlYmUgZW5mb2NhcnNlIG3DoXMgZW4gbWl0aWdhciBkaXNwZXJzacOzbiBlbiBob3JhcyBwaWNvLCB5YSBxdWUgZXMgbcOhcyBkaWbDrWNpbCBkZSBjb250cm9sYXIuDQoNCmBgYHtyfQ0KIyBGaWx0cmFyIHNvbG8gaG9yYXMgcGljbyAobWHDsWFuYSB5IHRhcmRlKQ0KaG9yYXNfcGljbyA8LSBwdW50b3NfaG9yYSAlPiUNCiAgZmlsdGVyKGZyYW5qYV9ob3JhcmlhICVpbiUgYygiTWHDsWFuYS1QaWNvIiwgIlRhcmRlLVBpY28iKSkNCg0KIyBQcmVwYXJhciBjb29yZGVuYWRhcyBBTlRFUyBkZSBjcmVhciBlbCBtYXBhDQpjb29yZHMgPC0gaG9yYXNfcGljbyAlPiUgDQogIHN0X2Nvb3JkaW5hdGVzKCkgJT4lIA0KICBhcy5kYXRhLmZyYW1lKCkNCg0KIyBDcmVhciBtYXBhIGNvbiBoZWF0bWFwDQpsZWFmbGV0KCkgJT4lICAjIE5vdGE6IG5vIHBhc2Ftb3MgZWwgb2JqZXRvIHNmIGRpcmVjdGFtZW50ZSBhcXXDrQ0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRDYXJ0b0RCLlBvc2l0cm9uKSAlPiUNCiAgYWRkSGVhdG1hcCgNCiAgICBsbmcgPSBjb29yZHMkWCwgICMgVXNhciBsYXMgY29sdW1uYXMgeWEgZXh0cmHDrWRhcw0KICAgIGxhdCA9IGNvb3JkcyRZLA0KICAgIGludGVuc2l0eSA9IGhvcmFzX3BpY28kZ2lfc2NvcmVfbnVtZXJpYywNCiAgICByYWRpdXMgPSAxNSwNCiAgICBibHVyID0gMjAsDQogICAgbWF4ID0gMC4wNSwNCiAgICBncmFkaWVudCA9IGMoIiNmN2FhMDAiLCAiI2M4MWQyNSIpICAjIEdyYWRpZW50ZSBkZSBjb2xvciBkaXJlY3RvDQogICkgJT4lDQogIGFkZExlZ2VuZCgNCiAgICBwb3NpdGlvbiA9ICJib3R0b21yaWdodCIsDQogICAgY29sb3JzID0gYygiI2Y3YWEwMCIsICIjYzgxZDI1IiksDQogICAgbGFiZWxzID0gYygiQmFqYSBpbnRlbnNpZGFkIiwgIkFsdGEgaW50ZW5zaWRhZCIpLA0KICAgIHRpdGxlID0gIkludGVuc2lkYWQgSG9yYXMgUGljbyINCiAgKSAlPiUNCiAgc2V0VmlldyhsbmcgPSAtMTAwLjMsIGxhdCA9IDI1LjY3LCB6b29tID0gMTMpDQpgYGANCg0KTGFzIMOhcmVhcyBtw6FzIGludGVuc2FtZW50ZSBjb2xvcmVhZGFzIGVuIHJvam8gb3NjdXJvIChhbHRhIGludGVuc2lkYWQgZGUgR2lcKikgY29ycmVzcG9uZGVuIGEgem9uYXMgY29uIGNvbmNlbnRyYWNpw7NuIGVzdGFkw61zdGljYW1lbnRlIHNpZ25pZmljYXRpdmEgZGUgZXZlbnRvcyBkdXJhbnRlIGxhcyBob3JhcyBwaWNvLiBFc3RhcyBpbmNsdXllbjoNCg0KLSAgIFpvbmEgY2VudHJvIGRlIE1vbnRlcnJleQ0KLSAgIFNhbiBOaWNvbMOhcyBkZSBsb3MgR2FyemENCi0gICBHdWFkYWx1cGUNCi0gICBTYW50YSBDYXRhcmluYQ0KLSAgIEVzY29iZWRvDQoNClRhbWJpw6luIHNlIG9ic2VydmFuIGZvY29zIGVuIMOhcmVhcyBtw6FzIGFsZWphZGFzIGNvbW8gQXBvZGFjYSB5IGFsZ3VuYXMgcGFydGVzIGRlIEJlbml0byBKdcOhcmV6IHkgQ2FkZXJleXRhIEppbcOpbmV6DQoNCkVzdG8gaW5kaWNhIHF1ZSwgYXVucXVlIGxhIGNvbmdlc3Rpw7NuIHNlIGNvbmNlbnRyYSBwcmluY2lwYWxtZW50ZSBlbiBlbCBjb3JhesOzbiBkZSBsYSBtZXRyw7Nwb2xpLCBoYXkgaG90c3BvdHMgc2VjdW5kYXJpb3MgZW4gem9uYXMgcGVyaWbDqXJpY2FzLg0KDQpgYGB7cn0NCiMgRXh0cmFlciBjb29yZGVuYWRhcyB5IGRhdG9zDQpoZWF0X2RhdGEgPC0gaG9yYXNfcGljbyAlPiUNCiAgbXV0YXRlKA0KICAgIGxvbiA9IHN0X2Nvb3JkaW5hdGVzKC4pWywxXSwNCiAgICBsYXQgPSBzdF9jb29yZGluYXRlcyguKVssMl0NCiAgKSAlPiUNCiAgc3RfZHJvcF9nZW9tZXRyeSgpDQoNCiMgTWFwYSBjb21wbGV0bw0KbGVhZmxldCgpICU+JQ0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRDYXJ0b0RCLlBvc2l0cm9uKSAlPiUNCiAgDQogICMgSGVhdG1hcA0KICBhZGRIZWF0bWFwKA0KICAgIGRhdGEgPSBoZWF0X2RhdGEsDQogICAgbG5nID0gfmxvbiwNCiAgICBsYXQgPSB+bGF0LA0KICAgIGludGVuc2l0eSA9IH5naV9zY29yZV9udW1lcmljLA0KICAgIHJhZGl1cyA9IDIwLA0KICAgIGJsdXIgPSAxNSwNCiAgICBtYXggPSAwLjEsDQogICAgZ3JhZGllbnQgPSBjb2xvclJhbXBQYWxldHRlKGMoIiNmN2FhMDAiLCAiI2M4MWQyNSIpKSgxMCkNCiAgKSAlPiUNCiAgDQogICMgUHVudG9zIGNyw610aWNvcyAodG9wIDUlKQ0KICBhZGRDaXJjbGVNYXJrZXJzKA0KICAgIGRhdGEgPSBmaWx0ZXIoaGVhdF9kYXRhLCBnaV9zY29yZV9udW1lcmljID4gcXVhbnRpbGUoZ2lfc2NvcmVfbnVtZXJpYywgMC45NSkpLA0KICAgIHJhZGl1cyA9IH5zcXJ0KGdpX3Njb3JlX251bWVyaWMpKjEuNSwNCiAgICBjb2xvciA9ICIjMDAwMDAwIiwNCiAgICBmaWxsQ29sb3IgPSAiI2M4MWQyNSIsDQogICAgZmlsbE9wYWNpdHkgPSAwLjgsDQogICAgcG9wdXAgPSB+cGFzdGUoIlNjb3JlOiIsIHJvdW5kKGdpX3Njb3JlX251bWVyaWMsIDIpKQ0KICApICU+JQ0KICANCiAgIyBDb250cm9sZXMNCiAgYWRkTGF5ZXJzQ29udHJvbCgNCiAgICBvdmVybGF5R3JvdXBzID0gYygiSGVhdG1hcCIsICJQdW50b3MgQ3LDrXRpY29zIikNCiAgKQ0KYGBgDQoNCkxvcyBwdW50b3MgbcOhcyBvc2N1cm9zIHkgZGVuc29zIGRlbCBoZWF0bWFwLCBhc8OtIGNvbW8gbG9zIGPDrXJjdWxvcyBtYXJjYWRvcywgc2UgY29uY2VudHJhbiBwcmluY2lwYWxtZW50ZSBlbjoNCg0KLSAgIENlbnRybyBkZSBNb250ZXJyZXkNCi0gICBTYW4gTmljb2zDoXMgZGUgbG9zIEdhcnphDQotICAgU2FudGEgQ2F0YXJpbmENCi0gICBHdWFkYWx1cGUNCi0gICBab25hcyBkZWwgc3VyIGNvbW8gQmVuaXRvIEp1w6FyZXogdGFtYmnDqW4gcHJlc2VudGFuIGhvdHNwb3RzIHJlbGV2YW50ZXMNCi0gICBBbGd1bmFzIMOhcmVhcyBwZXJpZsOpcmljYXMgY29tbyBHYXJjw61hIG8gUGVzcXVlcsOtYSB0YW1iacOpbiBtdWVzdHJhbiBhcGFyaWNpw7NuIGRlIGhvdHNwb3RzIGFpc2xhZG9zDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyBBbsOhbGlzaXMgRGF0b3MgZGUgQWNjaWRlbnRlcyBlbiBOdWV2byBMZcOzbg0KDQpgYGB7cn0NCiMgRmlsdHJhciBwb3IgRURPID09IDE5DQpkYXRvcyA8LSBkYXRvcyAlPiUgDQogIGZpbHRlcihFRE8gPT0gMTkpICMgMTkgZXMgTnVldm8gTGXDs24NCg0KIyBDYW1iaWFyIG5vbWJyZSBkZSBsb3MgbXVuaWNpcGlvcw0KZGF0b3MgPC0gZGF0b3MgJT4lDQogIG11dGF0ZShNUElPID0gcmVjb2RlKE1QSU8sDQogICAgIjEiID0gIkFiYXNvbG8iLA0KICAgICI2IiA9ICJBcG9kYWNhIiwNCiAgICAiOSIgPSAiQ2FkZXJleXRhIEppbcOpbmV6IiwgIA0KICAgICIxMCIgPSAiRWwgQ2FybWVuIiwNCiAgICAiMTIiID0gIkNpw6luZWdhIGRlIEZsb3JlcyIsDQogICAgIjE4IiA9ICJHYXJjw61hIiwNCiAgICAiMTkiID0gIlNhbiBQZWRybyBHYXJ6YSBHYXJjw61hIiwNCiAgICAiMjEiID0gIkdlbmVyYWwgRXNjb2JlZG8iLA0KICAgICIyNSIgPSAiR2VuZXJhbCBadWF6dWEiLA0KICAgICIyNiIgPSAiR3VhZGFsdXBlIiwNCiAgICAiMzEiID0gIkp1w6FyZXoiLA0KICAgICIzOSIgPSAiTW9udGVycmV5IiwNCiAgICAiNDEiID0gIlBlc3F1ZXLDrWEiLA0KICAgICI0NSIgPSAiU2FsaW5hcyBWaWN0b3JpYSIsDQogICAgIjQ2IiA9ICJTYW4gTmljb2zDoXMgZGUgbG9zIEdhcnphIiwNCiAgICAiNDciID0gIkhpZGFsZ28iLA0KICAgICI0OCIgPSAiU2FudGEgQ2F0YXJpbmEiLA0KICAgICI0OSIgPSAiU2FudGlhZ28iLA0KICApKQ0KDQpgYGANCg0KIyMjIEhvdCBTcG90cyBzaWduaWZpY2F0aXZvcyBkZSBhY2NpZGVudGVzIGRlIHRyYW5zcG9ydGUgcMO6YmxpY28gZGVudHJvIGRlbCDDoXJlYSBtZXRyb3BvbGl0YW5hIGRlIE1vbnRlcnJleQ0KDQpgYGB7cn0NCiMgUHJlcGFyYXIgbG9zIGRhdG9zDQpkYXRvcyA8LSBkYXRvcyAlPiUgDQogIGZpbHRlcighaXMubmEoTE9OR0lUVUQpICYgIWlzLm5hKExBVElUVUQpKSAgIyBFbGltaW5hciBOQQ0KDQojIENvbnZlcnRpciBhIG9iamV0byBlc3BhY2lhbCAoQ1JTIFdHUzg0KQ0KZGF0b3Nfc2YgPC0gc3RfYXNfc2YoZGF0b3MsIGNvb3JkcyA9IGMoIkxPTkdJVFVEIiwgIkxBVElUVUQiKSwgY3JzID0gNDMyNikgJT4lIA0KICBzdF90cmFuc2Zvcm0oMzI2MTQpICAjIFByb3llY3RhciBhIFVUTSB6b25hIDE0TiAoTW9udGVycmV5KQ0KDQojIENyZWFyIG1hdHJpeiBkZSB2ZWNpbmRhZCAocmFkaW8gZGUgMSBrbSkNCmNvb3JkcyA8LSBzdF9jb29yZGluYXRlcyhkYXRvc19zZikNCnZlY2luZGFkIDwtIGRuZWFybmVpZ2goY29vcmRzLCBkMSA9IDAsIGQyID0gMTAwMCkgICMgQWp1c3RhIGVsIHJhZGlvIChlbiBtZXRyb3MpDQpwZXNvcyA8LSBuYjJsaXN0dyh2ZWNpbmRhZCwgc3R5bGUgPSAiQiIsIHplcm8ucG9saWN5ID0gVFJVRSkNCg0KIyBDYWxjdWxhciBob3RzcG90cw0KZGF0b3Nfc2YkaG90c3BvdCA8LSBhcy5udW1lcmljKGxvY2FsRyhkYXRvc19zZiRDTEFTRSwgcGVzb3MpID4gMikgICMgR2kqID4gMiBTRA0KDQojIEFwbGljYXIgREJTQ0FOICg1MDBtLCBtw61uaW1vIDUgYWNjaWRlbnRlcykNCmNsdXN0ZXJzIDwtIGRic2Nhbihjb29yZHMsIGVwcyA9IDUwMCwgbWluUHRzID0gNSkNCmRhdG9zX3NmJGNsdXN0ZXIgPC0gYXMuZmFjdG9yKGNsdXN0ZXJzJGNsdXN0ZXIpDQoNCiMgRmlsdHJhciBjbMO6c3RlcnMgc2lnbmlmaWNhdGl2b3MgKGV4Y2x1aXIgcnVpZG8sIGNsdXN0ZXIgPSAwKQ0KY2x1c3RlcnNfc2lnbmlmaWNhdGl2b3MgPC0gZGF0b3Nfc2ZbZGF0b3Nfc2YkY2x1c3RlciAhPSAwLCBdDQpgYGANCg0KYGBge3J9DQojIENsYXNlIGNvbW8gbnVtw6lyaWNhDQpkYXRvc19zZiRjbGFzZV9udW1lcmljIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRhdG9zX3NmJENMQVNFKSkNCg0KIyBDcmVhciBtYXRyaXogZGUgdmVjaW5kYWQNCnBlc29zIDwtIG5iMmxpc3R3KHZlY2luZGFkLCBzdHlsZSA9ICJXIiwgemVyby5wb2xpY3kgPSBUUlVFKQ0KDQojIENhbGN1bGFyIE1vcmFuJ3MgSQ0KbW9yYW4udGVzdChkYXRvc19zZiRjbGFzZV9udW1lcmljLCBwZXNvcywgemVyby5wb2xpY3kgPSBUUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KIyBWaXN1YWxpemFjacOzbg0KZ2dwbG90KCkgKw0KICAjIEhvdHNwb3RzIChyb2pvID0gR2kqIHNpZ25pZmljYXRpdm8pDQogIGdlb21fc2YoZGF0YSA9IGRhdG9zX3NmLCBhZXMoY29sb3IgPSBhcy5mYWN0b3IoaG90c3BvdCkpLCBzaXplID0gMiwgYWxwaGEgPSAwLjYpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKA0KICAgIHZhbHVlcyA9IGMoIjAiID0gImdyYXkiLCAiMSIgPSAicmVkIiksDQogICAgbGFiZWxzID0gYygiTm8gaG90c3BvdCIsICJIb3RzcG90IikNCiAgKSArDQogICMgQ2zDunN0ZXJzIChjw61yY3Vsb3MgYXp1bGVzKQ0KICBnZW9tX3NmKGRhdGEgPSBjbHVzdGVyc19zaWduaWZpY2F0aXZvcywgYWVzKGZpbGwgPSBjbHVzdGVyKSwgc2hhcGUgPSAyMSwgc2l6ZSA9IDMpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Qob3B0aW9uID0gInBsYXNtYSIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJIb3RzcG90cyB5IENsw7pzdGVycyBkZSBBY2NpZGVudGVzIGVuIE1vbnRlcnJleSIsDQogICAgc3VidGl0bGUgPSAiUm9qbzogSG90c3BvdHMgKEdpKiA+IDIgU0QpIHwgQ8OtcmN1bG9zOiBDbMO6c3RlcnMgKERCU0NBTikiLA0KICAgIGNhcHRpb24gPSAiUmFkaW86IDEga20gKEhvdHNwb3RzKSAvIDUwMCBtIChEQlNDQU4pIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoDQogICAgZGF0YSA9IGZpbHRlcihkYXRvc19zZiwgaG90c3BvdCA9PSAxKSwgICMgU29sbyBob3RzcG90cw0KICAgIGNvbG9yID0gInJlZCIsDQogICAgc2l6ZSA9IDIsDQogICAgYWxwaGEgPSAwLjYNCiAgKSArDQogIGxhYnModGl0bGUgPSAiSG90c3BvdHMgU2lnbmlmaWNhdGl2b3MgKEdpKiA+IDIgU0QpIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpgYGB7cn0NCiMgUmVzdW1lbiBlc3RhZMOtc3RpY28NCmNhdCgiTsO6bWVybyBkZSBob3RzcG90czoiLCBzdW0oZGF0b3Nfc2YkaG90c3BvdCksICJcbiIpDQpjYXQoIk7Dum1lcm8gZGUgY2zDunN0ZXJzOiIsIG1heChjbHVzdGVycyRjbHVzdGVyKSkNCmBgYA0KDQpgYGB7cn0NCiMgVmVyaWZpY2FyIHF1ZSAnQ0xBU0UnIHNlYSB1biBmYWN0b3INCmRhdG9zX3NmJENMQVNFIDwtIGFzLmZhY3RvcihkYXRvc19zZiRDTEFTRSkNCg0KIyBNYXBhIGNvbiBnZ3Bsb3QyDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoDQogICAgZGF0YSA9IGRhdG9zX3NmLA0KICAgIGFlcyhjb2xvciA9IENMQVNFKSwgICMgQ29sb3IgcG9yIHRpcG8gZGUgYWNjaWRlbnRlDQogICAgc2l6ZSA9IDIsDQogICAgYWxwaGEgPSAwLjcNCiAgKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCgNCiAgICB2YWx1ZXMgPSBjKCIxIiA9ICJyZWQiLCAiMiIgPSAiYmx1ZSIsICIzIiA9ICJncmVlbiIpLCAgIyBQZXJzb25hbGl6YSBjb2xvcmVzDQogICAgbmFtZSA9ICJUaXBvIGRlIEFjY2lkZW50ZSIgICMgVMOtdHVsbyBkZSBsYSBsZXllbmRhDQogICkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkRpc3RyaWJ1Y2nDs24gZGUgQWNjaWRlbnRlcyBwb3IgVGlwbyBlbiBNb250ZXJyZXkiLA0KICAgIHN1YnRpdGxlID0gIkNvbG9yIHNlZ8O6biBsYSBjb2x1bW5hIENMQVNFIiwNCiAgICBjYXB0aW9uID0gIkZ1ZW50ZTogQkFTRSBNVU5JQ0lQQUwgMjAyMyINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUpDQogICkNCmBgYA0KDQojIyMgRGlzdHJpYnVjacOzbiBwb3IgbXVuaWNpcGlvIHkgdGlwbyBkZSBhY2NpZGVudGUNCg0KYGBge3J9DQojIFZlcmlmaWNhciBjb250ZW8gcG9yIG11bmljaXBpbw0KdGFibGFfbXBpbyA8LSBkYXRvc19zZiAlPiUNCiAgZ3JvdXBfYnkoTVBJTykgJT4lDQogIHN1bW1hcmlzZShhY2NpZGVudGVzID0gbigpKQ0KDQojIEFjY2lkZW50ZXMgcG9yIHRpcG8NCnRhYmxhX2NsYXNlIDwtIGRhdG9zX3NmICU+JQ0KICBncm91cF9ieShDTEFTRSkgJT4lDQogIHN1bW1hcmlzZShjYW50aWRhZCA9IG4oKSkNCg0KDQpnZ3Bsb3QodGFibGFfbXBpbywgYWVzKHggPSByZW9yZGVyKE1QSU8sIC1hY2NpZGVudGVzKSwgeSA9IGFjY2lkZW50ZXMpKSArDQogIGdlb21fY29sKGZpbGwgPSAic3RlZWxibHVlIikgKw0KICBjb29yZF9mbGlwKCkgKw0KICBsYWJzKHRpdGxlID0gIkFjY2lkZW50ZXMgcG9yIE11bmljaXBpbyIsIHggPSAiTXVuaWNpcGlvIiwgeSA9ICJDYW50aWRhZCIpDQoNCmdncGxvdCh0YWJsYV9jbGFzZSwgYWVzKHggPSBhcy5mYWN0b3IoQ0xBU0UpLCB5ID0gY2FudGlkYWQsIGZpbGwgPSBhcy5mYWN0b3IoQ0xBU0UpKSkgKw0KICBnZW9tX2NvbCgpICsNCiAgbGFicyh0aXRsZSA9ICJBY2NpZGVudGVzIHBvciBUaXBvIChDTEFTRSkiLCB4ID0gIlRpcG8gZGUgQWNjaWRlbnRlIiwgeSA9ICJDYW50aWRhZCIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KIyMjIFRvdGFsIGRlIGFjY2lkZW50ZXMsIG11ZXJ0b3MgeSBoZXJpZG9zIHBvciBtdW5pY2lwaW8NCg0KYGBge3J9DQpyZXN1bWVuX21waW8gPC0gZGF0b3Nfc2YgJT4lDQogIGdyb3VwX2J5KE1QSU8pICU+JQ0KICBzdW1tYXJpc2UoDQogICAgVG90YWxfQWNjaWRlbnRlcyA9IG4oKSwNCiAgICBUb3RhbF9NdWVydG9zID0gc3VtKFRPVE1VRVJUT1MsIG5hLnJtID0gVFJVRSksDQogICAgVG90YWxfSGVyaWRvcyA9IHN1bShUT1RIRVJJRE9TLCBuYS5ybSA9IFRSVUUpDQogICkgJT4lDQogIGFycmFuZ2UoZGVzYyhUb3RhbF9BY2NpZGVudGVzKSkgJT4lDQogIHNsaWNlX2hlYWQobiA9IDEwKSAgIyBUb3AgMTAgbXVuaWNpcGlvcw0KDQoNCnJlc3VtZW5fbXBpb19sb25nIDwtIHJlc3VtZW5fbXBpbyAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKCJUb3RhbF9NdWVydG9zIiwgIlRvdGFsX0hlcmlkb3MiKSwNCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIlRpcG8iLA0KICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIkNhbnRpZGFkIikNCg0KZ2dwbG90KHJlc3VtZW5fbXBpb19sb25nLCBhZXMoeCA9IHJlb3JkZXIoTVBJTywgLUNhbnRpZGFkKSwgeSA9IENhbnRpZGFkLCBmaWxsID0gVGlwbykpICsNCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIGxhYnModGl0bGUgPSAiVG9wIDEwIE11bmljaXBpb3MgcG9yIFNldmVyaWRhZCBkZSBBY2NpZGVudGVzIiwNCiAgICAgICB4ID0gIk11bmljaXBpbyIsDQogICAgICAgeSA9ICJDYW50aWRhZCIsDQogICAgICAgZmlsbCA9ICJUaXBvIGRlIFbDrWN0aW1hIikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJUb3RhbF9NdWVydG9zIiA9ICIjYzgxZDI1IiwgIlRvdGFsX0hlcmlkb3MiID0gIiNmN2FhMDAiKSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCiMjIyBSZXN1bWVuIGRlIHNldmVyaWRhZCB0b3RhbA0KDQpgYGB7cn0NCmRhdG9zX3NmIDwtIGRhdG9zX3NmICU+JQ0KICBtdXRhdGUoDQogICAgc2V2ZXJpZGFkID0gVE9UTVVFUlRPUyArIFRPVEhFUklET1MNCiAgKQ0KDQpnZ3Bsb3QoZGF0b3Nfc2YsIGFlcyh4ID0gc2V2ZXJpZGFkKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzAsIGZpbGwgPSAiZGFya3JlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidWNpw7NuIGRlIFNldmVyaWRhZCBUb3RhbCBwb3IgQWNjaWRlbnRlIiwgeCA9ICJUb3RhbCBNdWVydG9zICsgSGVyaWRvcyIpDQpgYGANCg0KIyMjIEF1dG9jb3JyZWxhY2nDs24gRXNwYWNpYWwgKE1vcmFuJ3MgSSkNCg0KYGBge3J9DQpjb29yZHMgPC0gc3RfY29vcmRpbmF0ZXMoZGF0b3Nfc2YpDQpuYiA8LSBkbmVhcm5laWdoKGNvb3JkcywgMCwgMTAwMCkgICMgMSBrbQ0KbHcgPC0gbmIybGlzdHcobmIsIHN0eWxlID0gIlciLCB6ZXJvLnBvbGljeSA9IFRSVUUpDQoNCm1vcmFuLnRlc3QoZGF0b3Nfc2Ykc2V2ZXJpZGFkLCBsdywgemVyby5wb2xpY3kgPSBUUlVFKQ0KYGBgDQoNCiMjIyBIb3RzcG90cyBjb24gR2lcKiBlbiBzZXZlcmlkYWQNCg0KYGBge3J9DQpkYXRvc19zZiRnaV9zY29yZSA8LSBsb2NhbEcoZGF0b3Nfc2Ykc2V2ZXJpZGFkLCBsaXN0dyA9IGx3LCB6ZXJvLnBvbGljeSA9IFRSVUUpDQpkYXRvc19zZiRnaV9zY29yZV9udW1lcmljIDwtIGFzLm51bWVyaWMoZGF0b3Nfc2YkZ2lfc2NvcmUpDQoNCiMgTWFwYSBkZSBHaSoNCmdncGxvdChkYXRvc19zZikgKw0KICBnZW9tX3NmKGFlcyhjb2xvciA9IGdpX3Njb3JlX251bWVyaWMpLCBzaXplID0gMSwgYWxwaGEgPSAwLjcpICsNCiAgc2NhbGVfY29sb3JfdmlyaWRpcyhuYW1lID0gIkdpKiBTY29yZSIpICsNCiAgbGFicyh0aXRsZSA9ICJIb3RzcG90cyBkZSBTZXZlcmlkYWQgKEdpKikiLCBzdWJ0aXRsZSA9ICJWYWxvcmVzIGFsdG9zIGluZGljYW4gY29uY2VudHJhY2nDs24gZGUgYWx0YSBzZXZlcmlkYWQiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCmBgYHtyfQ0KaG90c3BvdHMgPC0gZGF0b3Nfc2YgJT4lDQogIGZpbHRlcighaXMubmEoZ2lfc2NvcmVfbnVtZXJpYykgJiBnaV9zY29yZV9udW1lcmljID4gcXVhbnRpbGUoZ2lfc2NvcmVfbnVtZXJpYywgMC45NSwgbmEucm0gPSBUUlVFKSkgJT4lDQogIHN0X3RyYW5zZm9ybSg0MzI2KQ0KDQpwYWwgPC0gY29sb3JOdW1lcmljKCJSZWRzIiwgZG9tYWluID0gaG90c3BvdHMkZ2lfc2NvcmVfbnVtZXJpYykNCg0KbGVhZmxldChob3RzcG90cykgJT4lDQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJENhcnRvREIuUG9zaXRyb24pICU+JQ0KICBhZGRDaXJjbGVNYXJrZXJzKA0KICAgIHJhZGl1cyA9IH5zcXJ0KGdpX3Njb3JlX251bWVyaWMpKjIsDQogICAgZmlsbENvbG9yID0gfnBhbChnaV9zY29yZV9udW1lcmljKSwNCiAgICBjb2xvciA9ICIjMDAwMDAwIiwNCiAgICBmaWxsT3BhY2l0eSA9IDAuNywNCiAgICBzdHJva2UgPSBGQUxTRSwNCiAgICBwb3B1cCA9IH5wYXN0ZSgiU2NvcmU6Iiwgcm91bmQoZ2lfc2NvcmVfbnVtZXJpYywgMikpDQogICkgJT4lDQogIGFkZExlZ2VuZChwYWwgPSBwYWwsIHZhbHVlcyA9IH5naV9zY29yZV9udW1lcmljLCB0aXRsZSA9ICJHaSogU2NvcmUiKQ0KDQpgYGANCg0KIyMjIERpc3RyaWJ1Y2nDs24gcG9yIGhvcmEgZGVsIGTDrWENCg0KYGBge3J9DQpkYXRvc19zZiA8LSBkYXRvc19zZiAlPiUNCiAgbXV0YXRlKA0KICAgIGhvcmFfZGVjaW1hbCA9IEhPUkEgKyBNSU5VVE9TIC8gNjAsDQogICAgRlJBTkpBID0gY3V0KA0KICAgICAgaG9yYV9kZWNpbWFsLA0KICAgICAgYnJlYWtzID0gYygwLCA2LCA5LCAxMiwgMTUsIDE4LCAyMSwgMjQpLA0KICAgICAgbGFiZWxzID0gYygiTWFkcnVnYWRhIiwgIk1hw7FhbmEtUGljbyIsICJNZWRpb2TDrWEiLCAiVGFyZGUiLCAiVGFyZGUtUGljbyIsICJOb2NoZS1UZW1wcmFuYSIsICJOb2NoZS1UYXJkw61hIiksDQogICAgICBpbmNsdWRlLmxvd2VzdCA9IFRSVUUsDQogICAgICByaWdodCA9IEZBTFNFDQogICAgKQ0KICApDQoNCmdncGxvdChkYXRvc19zZiwgYWVzKHggPSBGUkFOSkEpKSArDQogIGdlb21fYmFyKGZpbGwgPSAiIzAwNzJCMiIpICsNCiAgbGFicyh0aXRsZSA9ICJGcmVjdWVuY2lhIGRlIEFjY2lkZW50ZXMgcG9yIEZyYW5qYSBIb3JhcmlhIiwgeCA9ICJGcmFuamEiLCB5ID0gIkNhbnRpZGFkIikNCg0KYGBgDQoNCiMjIyBIb3RzcG90cyBwb3IgZnJhbmphIGhvcmFyaWENCg0KDQpgYGB7cn0NCiMgRnVuY2nDs24gcGFyYSBtYXBhIGRlIGhvdHNwb3RzIHBvciBmcmFuamENCm1hcGFfaG90c3BvdHNfZnJhbmphIDwtIGZ1bmN0aW9uKGRhdGEsIGZyYW5qYV9sYWJlbCkgew0KICBzdWJzZXQgPC0gZGF0YSAlPiUgZmlsdGVyKEZSQU5KQSA9PSBmcmFuamFfbGFiZWwpDQogIGNvb3JkcyA8LSBzdF9jb29yZGluYXRlcyhzdWJzZXQpDQogIG5iIDwtIGRuZWFybmVpZ2goY29vcmRzLCAwLCAxMDAwKQ0KICBsdyA8LSBuYjJsaXN0dyhuYiwgc3R5bGUgPSAiVyIsIHplcm8ucG9saWN5ID0gVFJVRSkNCg0KICBzdWJzZXQkZ2lfc2NvcmUgPC0gbG9jYWxHKHN1YnNldCRUT1RNVUVSVE9TICsgc3Vic2V0JFRPVEhFUklET1MsIGx3LCB6ZXJvLnBvbGljeSA9IFRSVUUpDQoNCiAgc3Vic2V0X3RvcCA8LSBzdWJzZXQgJT4lIGZpbHRlcihnaV9zY29yZSA+IHF1YW50aWxlKGdpX3Njb3JlLCAwLjk1LCBuYS5ybSA9IFRSVUUpKQ0KDQogIGxlYWZsZXQoc3Vic2V0X3RvcCAlPiUgc3RfdHJhbnNmb3JtKDQzMjYpKSAlPiUNCiAgICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRDYXJ0b0RCLlBvc2l0cm9uKSAlPiUNCiAgICBhZGRDaXJjbGVNYXJrZXJzKA0KICAgICAgcmFkaXVzID0gNSwNCiAgICAgIGNvbG9yID0gInJlZCIsDQogICAgICBmaWxsT3BhY2l0eSA9IDAuNywNCiAgICAgIHBvcHVwID0gfnBhc3RlKCJTY29yZToiLCByb3VuZChnaV9zY29yZSwgMikpDQogICAgKSAlPiUNCiAgICBhZGRMZWdlbmQoImJvdHRvbXJpZ2h0IiwgY29sb3JzID0gInJlZCIsIGxhYmVscyA9IHBhc3RlKCJIb3RzcG90cyAtIiwgZnJhbmphX2xhYmVsKSkgJT4lDQogICAgc2V0VmlldyhsbmcgPSAtMTAwLjMsIGxhdCA9IDI1LjY3LCB6b29tID0gMTEpDQp9DQoNCm1hcGFfaG90c3BvdHNfZnJhbmphKGRhdG9zX3NmLCAiTWHDsWFuYS1QaWNvIikNCm1hcGFfaG90c3BvdHNfZnJhbmphKGRhdG9zX3NmLCAiTWVkaW9kw61hIikNCm1hcGFfaG90c3BvdHNfZnJhbmphKGRhdG9zX3NmLCAiVGFyZGUiKQ0KbWFwYV9ob3RzcG90c19mcmFuamEoZGF0b3Nfc2YsICJUYXJkZS1QaWNvIikNCm1hcGFfaG90c3BvdHNfZnJhbmphKGRhdG9zX3NmLCAiTm9jaGUtVGFyZMOtYSIpDQpgYGANCg0KDQojIyMgRGlzdHJpYnVjacOzbiBFc3BhY2lhbCBwb3IgR3JhdmVkYWQNCg0KYGBge3J9DQpkYXRvc19zZiA8LSBkYXRvc19zZiAlPiUNCiAgbXV0YXRlKGdyYXZlZGFkID0gY2FzZV93aGVuKA0KICAgIFRPVE1VRVJUT1MgPiAwIH4gIkdyYXZlIChtdWVydGVzKSIsDQogICAgVE9USEVSSURPUyA+IDAgfiAiTWVkaWEgKGhlcmlkb3MpIiwNCiAgICBUUlVFIH4gIkxldmUgKHNvbG8gZGHDsW9zKSINCiAgKSkNCg0KDQpnZ3Bsb3QoZGF0b3Nfc2YpICsNCiAgZ2VvbV9zZihhZXMoY29sb3IgPSBncmF2ZWRhZCksIGFscGhhID0gMC42KSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJHcmF2ZSAobXVlcnRlcykiID0gImRhcmtyZWQiLCAiTWVkaWEgKGhlcmlkb3MpIiA9ICJvcmFuZ2UiLCAiTGV2ZSAoc29sbyBkYcOxb3MpIiA9ICJncmF5IikpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidWNpw7NuIEVzcGFjaWFsIHBvciBHcmF2ZWRhZCBkZWwgQWNjaWRlbnRlIikNCmBgYA0KDQojIyMgR3LDoWZpY28gZGUgbMOtbmVhcyBkZSBhY2NpZGVudGVzIHBvciBtZXMNCg0KYGBge3J9DQpkYXRvc19zZiA8LSBkYXRvc19zZiAlPiUNCiAgbXV0YXRlKGZlY2hhID0gbWFrZV9kYXRlKEFOSU8sIE1FUywgRElBKSkgJT4lDQogIGZpbHRlcighaXMubmEoZmVjaGEpKQ0KDQojIEFncnVwYXIgcG9yIG1lcw0KbWVuc3VhbCA8LSBkYXRvc19zZiAlPiUNCiAgbXV0YXRlKG1lcyA9IGZsb29yX2RhdGUoZmVjaGEsICJtb250aCIpKSAlPiUNCiAgZ3JvdXBfYnkobWVzKSAlPiUNCiAgc3VtbWFyaXNlKG5fYWNjaWRlbnRlcyA9IG4oKSkNCg0KIyBHcsOhZmljbw0KZ2dwbG90KG1lbnN1YWwsIGFlcyh4ID0gbWVzLCB5ID0gbl9hY2NpZGVudGVzKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAic3RlZWxibHVlIiwgc2l6ZSA9IDEuMikgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gImRhcmtyZWQiKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiVGVuZGVuY2lhIG1lbnN1YWwgZGUgYWNjaWRlbnRlcyIsDQogICAgeCA9ICJNZXMiLA0KICAgIHkgPSAiTsO6bWVybyBkZSBhY2NpZGVudGVzIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KIyMjIFByb27Ds3N0aWNvcyBkZSBBY2NpZGVudGVzDQoNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShzdHJpbmdyKQ0KDQojIENyZWFyIGNvbHVtbmEgZGUgZmVjaGENCmRhdG9zIDwtIGRhdG9zICU+JQ0KICBtdXRhdGUoDQogICAgZmVjaGEgPSBhcy5EYXRlKHBhc3RlKEFOSU8sIE1FUywgRElBLCBzZXAgPSAiLSIpKSwNCiAgICBkaWFzZW1hbmFfbm9tYnJlID0gd2Vla2RheXMoZmVjaGEsIGFiYnJldmlhdGUgPSBGQUxTRSksDQogICAgbWVzX25vbWJyZSA9IG1vbnRocyhmZWNoYSwgYWJicmV2aWF0ZSA9IEZBTFNFKQ0KICApDQoNCiMgTGlzdGEgZGUgY29sdW1uYXMgZGUgdHJhbnNwb3J0ZSBww7pibGljbw0KdmVoaWN1bG9zX3RwIDwtIGMoIkNBTVBBU0FKIiwgIk1JQ1JPQlVTIiwgIlBBU0NBTUlPTiIsICJPTU5JQlVTIiwgIlRSQU5WSUEiKQ0KDQojIEFncnVwYXIgcG9yIGZlY2hhIHkgY2FsY3VsYXIgdmFyaWFibGVzDQphY2NpZGVudGVzX3Bvcl9mZWNoYSA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkoZmVjaGEsIGRpYXNlbWFuYV9ub21icmUsIG1lc19ub21icmUpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbl9hY2NpZGVudGVzID0gbigpLA0KICAgIHRvdGFsX3ZlaGljdWxvcyA9IHJvd1N1bXMoYWNyb3NzKGMoQVVUT01PVklMOk9UUk9WRUhJQykpLCBuYS5ybSA9IFRSVUUpLA0KICAgIHZlaGljdWxvc19wcm9tZWRpbyA9IHRvdGFsX3ZlaGljdWxvcyAvIG5fYWNjaWRlbnRlcywNCiAgICBuX3RyYW5zcG9ydGVfcHVibGljbyA9IHJvd1N1bXMoYWNyb3NzKGFsbF9vZih2ZWhpY3Vsb3NfdHApKSwgbmEucm0gPSBUUlVFKSwNCiAgICBwcm9wX3RyYW5zcG9ydGVfcHVibGljbyA9IG5fdHJhbnNwb3J0ZV9wdWJsaWNvIC8gdG90YWxfdmVoaWN1bG9zLA0KICAgIHRvdGFsX211ZXJ0b3MgPSBzdW0oVE9UTVVFUlRPUywgbmEucm0gPSBUUlVFKSwNCiAgICB0b3RhbF9oZXJpZG9zID0gc3VtKFRPVEhFUklET1MsIG5hLnJtID0gVFJVRSksDQogICAgdGFzYV9tb3J0YWxpZGFkID0gdG90YWxfbXVlcnRvcyAvIG5fYWNjaWRlbnRlcywNCiAgICBjbGFzZV9hY2NpZGVudGVfZnJlY3VlbnRlID0gbmFtZXMod2hpY2gubWF4KHRhYmxlKENMQVNFKSkpLA0KICAgIHRpcG9fYWNjaWRlbnRlX2ZyZWN1ZW50ZSA9IG5hbWVzKHdoaWNoLm1heCh0YWJsZShUSVBBQ0NJRCkpKSwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkNCg0KIyBDb252ZXJ0aXIgYSBmYWN0b3JlcyBwYXJhIHJlbm9tYnJhciBjbGFzZXMvdGlwb3MNCmFjY2lkZW50ZXNfcG9yX2ZlY2hhIDwtIGFjY2lkZW50ZXNfcG9yX2ZlY2hhICU+JQ0KICBtdXRhdGUoDQogICAgY2xhc2VfYWNjaWRlbnRlX2ZyZWN1ZW50ZSA9IHJlY29kZShjbGFzZV9hY2NpZGVudGVfZnJlY3VlbnRlLCBgM2AgPSAiU29sbyBkYcOxb3MiKSwNCiAgICB0aXBvX2FjY2lkZW50ZV9mcmVjdWVudGUgPSByZWNvZGUodGlwb19hY2NpZGVudGVfZnJlY3VlbnRlLCBgMWAgPSAiQ29saXNpw7NuIGNvbiB2ZWjDrWN1bG8gYXV0b21vdG9yIikNCiAgKQ0KDQojIERldGVjdGFyIG91dGxpZXJzIGVuIG7Dum1lcm8gZGUgYWNjaWRlbnRlcw0KcTEgPC0gcXVhbnRpbGUoYWNjaWRlbnRlc19wb3JfZmVjaGEkbl9hY2NpZGVudGVzLCAwLjI1KQ0KcTMgPC0gcXVhbnRpbGUoYWNjaWRlbnRlc19wb3JfZmVjaGEkbl9hY2NpZGVudGVzLCAwLjc1KQ0KaXFyIDwtIHEzIC0gcTENCmxpbWl0ZV9zdXAgPC0gcTMgKyAxLjUgKiBpcXINCg0KYWNjaWRlbnRlc19wb3JfZmVjaGEgPC0gYWNjaWRlbnRlc19wb3JfZmVjaGEgJT4lDQogIG11dGF0ZSgNCiAgICBvdXRsaWVyID0gaWZlbHNlKG5fYWNjaWRlbnRlcyA+IGxpbWl0ZV9zdXAsICJPdXRsaWVyIiwgIk5vcm1hbCIpDQogICkNCmBgYA0KDQpgYGB7cn0NCmdsaW1wc2UoYWNjaWRlbnRlc19wb3JfZmVjaGEpDQpgYGANCg0KYGBge3J9DQojIDEuIEFncmVnYWNpw7NuIGRpYXJpYSANCmFjY2lkZW50ZXNfZGlhcmlvcyA8LSBhY2NpZGVudGVzX3Bvcl9mZWNoYSAlPiUNCiAgZ3JvdXBfYnkoZmVjaGEpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbl9hY2NpZGVudGVzID0gZmlyc3Qobl9hY2NpZGVudGVzKSwNCiAgICBkaWFfc2VtYW5hID0gZmlyc3QoZGlhc2VtYW5hX25vbWJyZSksDQogICAgbWVzID0gZmlyc3QobWVzX25vbWJyZSksDQogICAgdHJhbnNwb3J0ZV9wdWJsaWNvID0gc3VtKG5fdHJhbnNwb3J0ZV9wdWJsaWNvKSwNCiAgICBtdWVydG9zID0gc3VtKHRvdGFsX211ZXJ0b3MpLA0KICAgIGhlcmlkb3MgPSBzdW0odG90YWxfaGVyaWRvcykNCiAgKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBmaWx0ZXIoZmVjaGEgPj0gIjIwMjMtMDEtMDEiKSAlPiUgDQogIGFycmFuZ2UoZmVjaGEpDQpgYGANCg0KYGBge3J9DQojIDIuIFByZXBhcmFyIGRhdG9zIHBhcmEgUHJvcGhldA0KZGZfcHJvcGhldCA8LSBhY2NpZGVudGVzX2RpYXJpb3MgJT4lDQogIHNlbGVjdChkcyA9IGZlY2hhLCB5ID0gbl9hY2NpZGVudGVzKSAlPiUNCiAgbXV0YXRlKA0KICAgIGRpYV9zZW1hbmEgPSB3ZWVrZGF5cyhkcyksICAjIEV4dHJhZXIgZMOtYSBkZSBsYSBzZW1hbmEgY29tbyB0ZXh0bw0KICAgIGVzX2Zpbl9kZV9zZW1hbmEgPSBpZmVsc2UoZGlhX3NlbWFuYSAlaW4lIGMoInPDoWJhZG8iLCAiZG9taW5nbyIsICJTYXR1cmRheSIsICJTdW5kYXkiKSwgMSwgMCkNCiAgKQ0KDQojIDMuIE1vZGVsbyBQcm9waGV0IGNvbiByZWdyZXNvcmVzIGFkaWNpb25hbGVzDQptb2RlbG9fcHJvcGhldCA8LSBwcm9waGV0KA0KICBncm93dGggPSAiZmxhdCIsICAjIEFzdW1pbW9zIGVzdGFjaW9uYXJpZWRhZCBlbiBsYSB0ZW5kZW5jaWENCiAgeWVhcmx5LnNlYXNvbmFsaXR5ID0gVFJVRSwNCiAgd2Vla2x5LnNlYXNvbmFsaXR5ID0gVFJVRSwNCiAgZGFpbHkuc2Vhc29uYWxpdHkgPSBGQUxTRQ0KKQ0KDQojIEHDsWFkaXIgcmVncmVzb3JlcyBhZGljaW9uYWxlcw0KbW9kZWxvX3Byb3BoZXQgPC0gYWRkX3JlZ3Jlc3Nvcihtb2RlbG9fcHJvcGhldCwgJ2VzX2Zpbl9kZV9zZW1hbmEnKQ0KDQojIEFqdXN0YXIgbW9kZWxvDQptb2RlbG9fcHJvcGhldCA8LSBmaXQucHJvcGhldChtb2RlbG9fcHJvcGhldCwgZGZfcHJvcGhldCkNCg0KIyA0LiBDcmVhciBkYXRhZnJhbWUgZnV0dXJvIGNvbiBjYXJhY3RlcsOtc3RpY2FzDQpmdXR1cm8gPC0gbWFrZV9mdXR1cmVfZGF0YWZyYW1lKG1vZGVsb19wcm9waGV0LCBwZXJpb2RzID0gNzMwKSAlPiUNCiAgbXV0YXRlKA0KICAgIGRpYV9zZW1hbmEgPSB3ZWVrZGF5cyhkcyksDQogICAgZXNfZmluX2RlX3NlbWFuYSA9IGlmZWxzZShkaWFfc2VtYW5hICVpbiUgYygic8OhYmFkbyIsICJkb21pbmdvIiwgIlNhdHVyZGF5IiwgIlN1bmRheSIpLCAxLCAwKQ0KICApDQoNCiMgNS4gUHJvbsOzc3RpY28NCnByb25vc3RpY28gPC0gcHJlZGljdChtb2RlbG9fcHJvcGhldCwgZnV0dXJvKQ0KDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBHcsOhZmljbyBlc3TDoXRpY28gY29uIGNvbXBvbmVudGVzDQpwbG90KG1vZGVsb19wcm9waGV0LCBwcm9ub3N0aWNvKSArDQogIGxhYnModGl0bGUgPSAiUHJvbsOzc3RpY28gZGlhcmlvIGRlIGFjY2lkZW50ZXMgMjAyMy0yMDI1IiwNCiAgICAgICB4ID0gIkZlY2hhIiwgeSA9ICJOw7ptZXJvIGRlIGFjY2lkZW50ZXMiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQojIFBhcmEgdmVyIGxvcyBjb21wb25lbnRlcyBlc3RhY2lvbmFsZXMNCnByb3BoZXRfcGxvdF9jb21wb25lbnRzKG1vZGVsb19wcm9waGV0LCBwcm9ub3N0aWNvKQ0KYGBgDQoNCg0KIyMgUGVyY2VwY2nDs24gZGVsIHRyYW5zcG9ydGUNCg0KYGBge3J9DQpkZiA8LSByZWFkX2V4Y2VsKCJDOlxcVXNlcnNcXG1hcmkwXFxPbmVEcml2ZVxcRG9jdW1lbnRzXFxSIFN0dWRpb1xcOCBzZW1lc3RyZVxcRXZpZGVuY2lhXFxFdmlkZW5jaWExX3BhcnRlMlxcUEVSQ0VQQ0lPTiBERSBMQSBDSVVEQURBTklBIERFTCBUUkFOU1BPUlRFIFBVQkxJQ08gREUgTEEgWk9OQSBNRVRST1BPTElUQU5BIERFIE1PTlRFUlJFWSAgKFJlc3B1ZXN0YXMpLnhsc3giLA0KICAgICAgICAgICAgICAgICBzaGVldCA9ICJSZXNwdWVzdGFzIGRlIGZvcm11bGFyaW8gMSIpDQoNCmBgYA0KDQpgYGB7cn0NCmNvbG5hbWVzKGRmKSA8LSBjKA0KICAibWFyY2FfdGVtcG9yYWwiLCAiZnJlY3VlbmNpYV91c28iLCAibW90aXZvX3VzbyIsICJtZWRpb190cmFuc3BvcnRlIiwNCiAgImFjY2VzaWJpbGlkYWQiLCAibGltcGllemEiLCAic2VndXJpZGFkIiwgInZpY3RpbWFfZGVsaXRvIiwNCiAgInByb2JsZW1hX3ByaW5jaXBhbCIsICJjb3N0b19wZXJjaWJpZG8iLCAibWVqb3Jhc19pbXBvcnRhbnRlcyIsDQogICJwcmVwYXJhY2lvbl9tdW5kaWFsIiwgImltcGFjdG9fbXVuZGlhbCIsICJlZGFkIiwgImdlbmVybyIsDQogICJvY3VwYWNpb24iLCAibXVuaWNpcGlvX3Jlc2lkZW5jaWEiLCAiYXBsaWNhY2lvbiINCikNCg0KYGBgDQoNCmBgYHtyfQ0Kc3VtKGlzLm5hKGRmKSkNCmBgYA0KYGBge3J9DQptb2RhIDwtIGZ1bmN0aW9uKHgpIHsNCiAgdXggPC0gbmEub21pdCh1bmlxdWUoeCkpDQogIHV4W3doaWNoLm1heCh0YWJ1bGF0ZShtYXRjaCh4LCB1eCkpKV0NCn0NCiMgQXBsaWNhciBzb2xvIGEgY29sdW1uYXMgY29uIE5BDQpmb3IgKGNvbCBpbiBuYW1lcyhkZikpIHsNCiAgaWYgKGFueShpcy5uYShkZltbY29sXV0pKSkgew0KICAgIGRmW1tjb2xdXVtpcy5uYShkZltbY29sXV0pXSA8LSBtb2RhKGRmW1tjb2xdXSkNCiAgfQ0KfQ0KYGBgDQoNCipEaXN0cmlidWNpw7NuIHBvciBlZGFkIHkgZ8OpbmVybyoNCg0KYGBge3J9DQoNCmdncGxvdChkZiwgYWVzKHggPSBlZGFkKSkgKw0KICBnZW9tX2JhcihmaWxsID0gInN0ZWVsYmx1ZSIpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidWNpw7NuIGRlIGVkYWRlcyIsIHggPSAiUmFuZ28gZGUgZWRhZCIsIHkgPSAiRnJlY3VlbmNpYSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmdncGxvdChkZiwgYWVzKHggPSBnZW5lcm8pKSArDQogIGdlb21fYmFyKGZpbGwgPSAib3JjaGlkIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1Y2nDs24gcG9yIGfDqW5lcm8iLCB4ID0gIkfDqW5lcm8iLCB5ID0gIkZyZWN1ZW5jaWEiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANCg0KKkfDqW5lcm8gdnMgUGVyY2VwY2nDs24gZGUgc2VndXJpZGFkKg0KDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHggPSBnZW5lcm8sIGZpbGwgPSBzZWd1cmlkYWQpKSArDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArDQogIGxhYnModGl0bGUgPSAiUGVyY2VwY2nDs24gZGUgc2VndXJpZGFkIHBvciBnw6luZXJvIiwgeCA9ICJHw6luZXJvIiwgeSA9ICJQcm9wb3JjacOzbiIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCipQZXJjZXBjacOzbiBkZWwgY29zdG8gcG9yIGdydXBvIGRlIGVkYWQqDQoNCmBgYHtyfQ0KZ2dwbG90KGRmLCBhZXMoeCA9IGVkYWQsIGZpbGwgPSBjb3N0b19wZXJjaWJpZG8pKSArDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArDQogIGxhYnModGl0bGUgPSAiUGVyY2VwY2nDs24gZGVsIGNvc3RvIGRlbCB0cmFuc3BvcnRlIHBvciBlZGFkIiwgeCA9ICJFZGFkIiwgeSA9ICJQcm9wb3JjacOzbiIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCipQcm9ibGVtYXMgcHJpbmNpcGFsZXMgcGVyY2liaWRvcyoNCg0KYGBge3J9DQpnZ3Bsb3QoZGYsIGFlcyh4ID0gcHJvYmxlbWFfcHJpbmNpcGFsKSkgKw0KICBnZW9tX2JhcihmaWxsID0gImRhcmtvcmFuZ2UiKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGxhYnModGl0bGUgPSAiUHJvYmxlbWFzIHByaW5jaXBhbGVzIGVuIGVsIHRyYW5zcG9ydGUiLCB4ID0gIlByb2JsZW1hIiwgeSA9ICJGcmVjdWVuY2lhIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCiMjIFBydWViYXMgQ2hpLUN1YWRyYWRhDQoNCipQcnVlYmEgMTogR8OpbmVybyB2cyBwZXJjZXBjacOzbiBkZSBzZWd1cmlkYWQqDQoNCmBgYHtyfQ0KY2F0KCIxLiBHw6luZXJvIHZzIFNlZ3VyaWRhZFxuIikNCnRhYmxhMSA8LSB0YWJsZShkZiRnZW5lcm8sIGRmJHNlZ3VyaWRhZCkNCnByaW50KHRhYmxhMSkNCnByaW50KGNoaXNxLnRlc3QodGFibGExKSkNCmBgYA0KDQoqUHJ1ZWJhIDI6IE11bmljaXBpbyB2cyBwcmVwYXJhY2nDs24gcGFyYSBlbCBNdW5kaWFsKg0KDQpgYGB7cn0NCmNhdCgiXG4yLiBNdW5pY2lwaW8gdnMgUHJlcGFyYWNpw7NuIE11bmRpYWxcbiIpDQp0YWJsYTIgPC0gdGFibGUoZGYkbXVuaWNpcGlvX3Jlc2lkZW5jaWEsIGRmJHByZXBhcmFjaW9uX211bmRpYWwpDQpwcmludCh0YWJsYTIpDQpwcmludChjaGlzcS50ZXN0KHRhYmxhMikpDQpgYGANCg0KKlBydWViYSAzOiBPY3VwYWNpw7NuIHZzIHBlcmNlcGNpw7NuIGRlbCBjb3N0byoNCg0KYGBge3J9DQpjYXQoIlxuMy4gT2N1cGFjacOzbiB2cyBDb3N0byBwZXJjaWJpZG9cbiIpDQp0YWJsYTMgPC0gdGFibGUoZGYkb2N1cGFjaW9uLCBkZiRjb3N0b19wZXJjaWJpZG8pDQpwcmludCh0YWJsYTMpDQpwcmludChjaGlzcS50ZXN0KHRhYmxhMykpDQoNCmBgYA0KDQoqUHJ1ZWJhIDQ6IEVkYWQgdnMgcGVyY2VwY2nDs24gZGUgZGlzcG9uaWJpbGlkYWQgKHVzYW5kbyBwcm9ibGVtYV9wcmluY2lwYWwpKg0KDQpgYGB7cn0NCmNhdCgiXG40LiBFZGFkIHZzIFByb2JsZW1hIHByaW5jaXBhbCAoY29tbyBwcm94eSBkZSBkaXNwb25pYmlsaWRhZClcbiIpDQp0YWJsYTQgPC0gdGFibGUoZGYkZWRhZCwgZGYkcHJvYmxlbWFfcHJpbmNpcGFsKQ0KcHJpbnQodGFibGE0KQ0KcHJpbnQoY2hpc3EudGVzdCh0YWJsYTQpKQ0KYGBgDQoNCipQcnVlYmEgNTogTGltcGllemEgdnMgcHJlcGFyYWNpw7NuIHBhcmEgZWwgTXVuZGlhbCoNCg0KYGBge3J9DQpjYXQoIlxuNS4gTGltcGllemEgdnMgUHJlcGFyYWNpw7NuIE11bmRpYWxcbiIpDQp0YWJsYTUgPC0gdGFibGUoZGYkbGltcGllemEsIGRmJHByZXBhcmFjaW9uX211bmRpYWwpDQpwcmludCh0YWJsYTUpDQpwcmludChjaGlzcS50ZXN0KHRhYmxhNSkpDQpgYGANCg0KKlBydWViYSA2OiBQdW50dWFsaWRhZCB2cyBwcmVwYXJhY2nDs24gcGFyYSBlbCBNdW5kaWFsKg0KDQpgYGB7cn0NCmNhdCgiXG42LiBQdW50dWFsaWRhZCAocHJvYmxlbWEgcHJpbmNpcGFsKSB2cyBQcmVwYXJhY2nDs24gTXVuZGlhbFxuIikNCnRhYmxhNiA8LSB0YWJsZShkZiRwcm9ibGVtYV9wcmluY2lwYWwsIGRmJHByZXBhcmFjaW9uX211bmRpYWwpDQpwcmludCh0YWJsYTYpDQpwcmludChjaGlzcS50ZXN0KHRhYmxhNikpDQoNCmBgYA0KDQoqUHJ1ZWJhIDc6IERpc3BvbmliaWxpZGFkIG5lZ2F0aXZhIHZzIHBvc2l0aXZhIHkgc3UgcmVsYWNpw7NuIGNvbiBwcmVwYXJhY2nDs24gTXVuZGlhbCoNCg0KYGBge3J9DQpkZiRkaXNwb25pYmlsaWRhZCA8LSBpZmVsc2UoZGYkcHJvYmxlbWFfcHJpbmNpcGFsID09ICJEaXNwb25pYmlsaWRhZCBkZSB1bmlkYWRlcyIsICJOZWdhdGl2YSIsICJQb3NpdGl2YSIpDQpjYXQoIlxuNy4gRGlzcG9uaWJpbGlkYWQgdnMgUHJlcGFyYWNpw7NuIE11bmRpYWxcbiIpDQp0YWJsYTcgPC0gdGFibGUoZGYkZGlzcG9uaWJpbGlkYWQsIGRmJHByZXBhcmFjaW9uX211bmRpYWwpDQpwcmludCh0YWJsYTcpDQpwcmludChjaGlzcS50ZXN0KHRhYmxhNykpDQpgYGANCg0KKlBydWViYSA4OiBFZGFkIHZzIHBlcmNlcGNpw7NuIGRlbCBjb3N0byoNCg0KYGBge3J9DQpjYXQoIlxuOC4gRWRhZCB2cyBDb3N0byBwZXJjaWJpZG9cbiIpDQp0YWJsYTggPC0gdGFibGUoZGYkZWRhZCwgZGYkY29zdG9fcGVyY2liaWRvKQ0KcHJpbnQodGFibGE4KQ0KcHJpbnQoY2hpc3EudGVzdCh0YWJsYTgpKQ0KYGBgDQo=