Autores

Equipo 3 - Gamma.

Integrantes:
* César Romeo Vega
* Gamaliel Ostos
* Luis Carlos Borbón
* Rodrigo Arroyo

Introducción a la Actividad

1) Brevemente, describir con sus propias palabras qué es un ESDA y cuál es su principal propósito en el proceso de analítica de datos.

Un ESDA (Explanatory Spacial Data Analysis) por sus siglas en inglés es un conjunto de herramientas estadísticas que buscan comprender la estructura y distribución espacial de los datos antes de realizar un análisis más profundo o una modelación de datos,identificando patrones y relaciones espaciales en la información.

En comparación de un EDA (Explanatory Data Analysis) por sus siglas en inglés, el ESDA toma en cuenta el factor geoespacial para datos con ubicaciones geográfica y con posibilidad de tener autocorrelación espacial alta.

Algunos propósitos del ESDA son:

  • Identifica patrones y anomalías espaciales en los datos de un periodo definido
  • Visualiza la distribución espacial de los datos en mapas temáticos o de calor
  • Detecta autocorrelación espacial entre locaciones vecinas
  • Evalua supuestos para modelos espaciales mediante dependencias y variabilidad entre regiones

2) Brevemente, describir con sus propias palabras el concepto de autocorrelación espacial así como 1-2 ejemplos relacionados con dicho concepto.

La autocorrelación espacial es una medida que mide la relación entre el valor de una variable y las ubicaciones geográficas cercanas. Es decir, el valor de una región es influenciado por las características o valores de los lugares cercanos.

El Indice de Moran y el Indice de Geary son los indicadores más usados para medir la correlación espacial global y local de los datos.

Algunos ejemplos donde podemos observar autocorrelación espacial son:

  • Propagación de enfermedades contagiosas en lugares cercanos (Pandemia de COVID-19)
  • Distribución de poderes adquisitivos y nivel de pobreza en una ciudad (División entre San Pedro y el centro de Monterrey)
  • Renta de vivienda temporal en distintas regiones urbanas (Zona TEC y Zonas Industriales de Nuevo León)

Situación Problema

El turismo ha sido uno de las mayores actividades económicas de México, donde destaca por sus amplias culturas y tradicionaes, gastronomía, celebraciones y recintos naturales/urbanos propicios para viajes vacacinonales o de negocios.

Con el Mundial de Futbol organizado por la FIFA en 2026, México será una de las cedes por lo que el turismo se verá beneficiado durante los 40 días que dura este macroevento. Sin embargo, ¿existirá alguna relación de beneficio entre estados que potencie el turismo? o ¿habrá algun patrón de los estados que generan más turismo con los que no?

Análisis Exploratorio Espacial de los Datos

Instalar Librerías, Paquetes y Bases de Datos

#install.packages("readxl")
#install.packages("ggplot2")
#install.packages("leaflet")
#install.packages("sf")
#install.packages("tmap")
#install.packages("tmaptools")
#install.packages("htmlwidgets")
#install.packages("naniar")
#install.packages("sp")
#install.packages("spdep")
library(readxl)
library(ggplot2)
library(leaflet)
library(sf)
library(tmap)
library(tmaptools)
library(dplyr)
library(htmlwidgets)
library(naniar)
library(sp)
library(spdep)
# Read the Excel file
data <- read_excel("tourism_state_data.xlsx")

Identificando Variables

Dentro de la Base de Datos se encuentra la siguientes variables por analizar: * tourism_gdp: Viajes y turismo que contribuye directamente al PIB
* crime_rate: Ratio de criminalidad por cada 100,000 personas
* unemployment: Porcentaje de población desempleada
* employement Porcentaje de población empleada
* business_activity: Índice económico ponderado por la distancia al puerto estadounidense más cercano
* real_wage: Salario real tomando de base el INPC del 2018 ($100 MXN)
* pop_density: Población por cada km^2 * good_governance: Ratio entre la inversión y deuda pública del estado
* ratio_public_investment: Ratio entre la inversión pública y el PIB del estado
* exchange_rate: Cambio de 1 USD por MXN * inpc: Índice Nacional de Precio al Consumidor tomando de base el 2018 ($100 MX)
* college_education: Porcentaje de población con por lo menos educación avanzada

str(data)
## tibble [544 × 18] (S3: tbl_df/tbl/data.frame)
##  $ state                  : chr [1:544] "Aguascalientes" "Baja California" "Baja California Sur" "Campeche" ...
##  $ year                   : num [1:544] 2006 2006 2006 2006 2006 ...
##  $ state_id               : num [1:544] 1057 2304 2327 1086 1182 ...
##  $ tourism_gdp            : num [1:544] 11995 54372 24238 13032 22730 ...
##  $ crime_rate             : num [1:544] 2.32 15.49 4.54 4.19 11.59 ...
##  $ college_education      : num [1:544] 0.1642 0.1808 0.1913 0.1544 0.0875 ...
##  $ unemployment           : num [1:544] 0.05 0.03 0.02 0.01 0.04 0.04 0.06 0.09 0.04 0.04 ...
##  $ employment             : num [1:544] 0.96 0.98 0.98 0.98 0.98 0.97 0.96 0.96 0.97 0.97 ...
##  $ business_activity      : num [1:544] -2.29 2.31 -2.43 -1.79 -2.49 -1.53 -2.19 -1.51 -2.57 -2.14 ...
##  $ real_wage              : num [1:544] 301 333 313 348 259 ...
##  $ pop_density            : num [1:544] 199.63 42.01 7.74 13.69 63.33 ...
##  $ good_governance        : num [1:544] 0.16 0.23 0.35 0.02 0.5 0.05 5.19 0 0.29 0.48 ...
##  $ ratio_public_investment: num [1:544] 0.01 0 0.01 0 0.01 0.02 0 0 0.01 0.01 ...
##  $ exchange_rate          : num [1:544] 10.8 10.8 10.8 10.8 10.8 ...
##  $ inpc                   : num [1:544] 62.7 62.7 62.7 62.7 62.7 ...
##  $ border_distance        : num [1:544] 625.59 8.83 800.32 978.33 1111.82 ...
##  $ region...17            : chr [1:544] "Bajio" "Norte" "Occidente" "Sur" ...
##  $ region...18            : num [1:544] 2 3 4 5 5 3 1 3 4 4 ...
# Formatear valores numéricos (2 decimales)
data$tourism_gdp <- round(data$tourism_gdp, 2)
data$crime_rate <- round(data$crime_rate, 2)
data$business_activity <- round(data$business_activity, 2)
data$good_governance <- round(data$good_governance, 2)
data$real_wage <- round(data$real_wage, 2)
data$pop_density <- round(data$pop_density, 2)

# Formatear valores porcentages
data$unemployment <- round(data$unemployment * 100, 2)
data$employment <- round(data$employment * 100, 2)  
data$college_education <- round(data$college_education * 100, 2)

Análisis Exploratorio

summary(data)
##     state                year         state_id     tourism_gdp    
##  Length:544         Min.   :2006   Min.   : 888   Min.   :  6240  
##  Class :character   1st Qu.:2010   1st Qu.:1047   1st Qu.: 22685  
##  Mode  :character   Median :2014   Median :1081   Median : 32482  
##                     Mean   :2014   Mean   :1219   Mean   : 56520  
##                     3rd Qu.:2018   3rd Qu.:1118   3rd Qu.: 59014  
##                     Max.   :2022   Max.   :2357   Max.   :472642  
##    crime_rate      college_education  unemployment      employment   
##  Min.   :  1.710   Min.   : 8.75     Min.   : 1.000   Min.   :89.00  
##  1st Qu.:  8.107   1st Qu.:16.70     1st Qu.: 3.000   1st Qu.:95.00  
##  Median : 13.880   Median :20.30     Median : 4.000   Median :97.00  
##  Mean   : 22.163   Mean   :21.11     Mean   : 4.251   Mean   :96.39  
##  3rd Qu.: 26.315   3rd Qu.:25.09     3rd Qu.: 5.000   3rd Qu.:97.54  
##  Max.   :181.510   Max.   :43.76     Max.   :10.000   Max.   :99.28  
##  business_activity   real_wage      pop_density      good_governance  
##  Min.   :-2.980    Min.   :239.3   Min.   :   7.74   Min.   :  0.000  
##  1st Qu.:-2.260    1st Qu.:282.5   1st Qu.:  39.56   1st Qu.:  0.180  
##  Median :-2.070    Median :306.2   Median :  61.77   Median :  0.500  
##  Mean   :-1.757    Mean   :314.9   Mean   : 299.46   Mean   :  2.362  
##  3rd Qu.:-1.768    3rd Qu.:335.4   3rd Qu.: 150.46   3rd Qu.:  1.350  
##  Max.   : 2.470    Max.   :481.7   Max.   :6211.45   Max.   :200.020  
##  ratio_public_investment exchange_rate        inpc        border_distance  
##  Min.   :0.000000        Min.   :10.85   Min.   : 62.69   Min.   :   8.83  
##  1st Qu.:0.000000        1st Qu.:12.87   1st Qu.: 74.93   1st Qu.: 613.26  
##  Median :0.000000        Median :14.51   Median : 87.19   Median : 751.64  
##  Mean   :0.005736        Mean   :15.91   Mean   : 89.08   Mean   : 704.92  
##  3rd Qu.:0.010000        3rd Qu.:19.47   3rd Qu.:103.02   3rd Qu.: 875.76  
##  Max.   :0.067644        Max.   :20.52   Max.   :126.48   Max.   :1252.66  
##  region...17         region...18   
##  Length:544         Min.   :1.000  
##  Class :character   1st Qu.:2.000  
##  Mode  :character   Median :3.000  
##                     Mean   :3.188  
##                     3rd Qu.:4.250  
##                     Max.   :5.000

Serie de Tiempo de Variable Dependiente

# Serie de tiempo de tourism_gdp
ggplot(data, aes(x = year, y = as.numeric(gsub(",", "", tourism_gdp)), group = state, color = state)) +  # Convert tourism_gdp to numeric
  geom_line() +
  labs(title = "GDP de Turismo por Estado a través del Tiempo",
       x = "Año",
       y = "GDP de Turismo") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

A lo largo de los años se aprecia que el PIB directo del Turismo no es estacionario durante el periodo de 2006 y 2022, teniendo una caida en 2020 debido a la pandemia. En caso de querer realizar un analisis temporal se necesitaría contar con registros mensuales o semanales que indiquen un patrón estacionario o en su defecto, hacer una transformación logarítmica.

Para este ejercicio, no se considerará el factor temporal y se enfocará en el análisis espacial.

Identificación de Valores Nulos

gg_miss_var(data, show_pct=TRUE)

Como se puede apreciar, no existen valores nulos en ninguna de las variables de la base de datos

Análisis descriptivo y de dispersión

# Histograma y Box Plot de tourism_gdp
options(repr.plot.width=12, repr.plot.height=8)

ggplot(data, aes(x = state, y = tourism_gdp)) +
  geom_col() +
  labs(x = "Estado", y = "Turismo PIB", title = "Histograma de Turismo PIB por Estado") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(data, aes(x = state, y = tourism_gdp)) +
  geom_boxplot() +
  labs(x = "Estado", y = "Turismo PIB", title = "Box Plot de Turismo PIB por Estado") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

La Ciudad de México supera en PIB de turismo a todos los demás estados. Los que menos tienen son Tlaxcala y Colima.

# Histograma de crime_rate
options(repr.plot.width=12, repr.plot.height=8)
ggplot(data, aes(x = state, y = crime_rate)) +
  geom_col() +
  labs(x = "Estado", y = "Crime Rate", title = "Histograma de Crime Rate por Estado") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(data, aes(x = state, y = crime_rate)) +
  geom_boxplot() +
  labs(x = "Estado", y = "Crime Rate", title = "Box Plot de Crime Rate por Estado") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Chihuahua es el que tiene mayor índice de crimen, seguido de Baja California y Sinaloa. Los que menos tienen son Yucatán y Aguascalientes.

# Histograma de college_education
options(repr.plot.width=12, repr.plot.height=8)

ggplot(data, aes(x = state, y = college_education)) +
  geom_col() +
  labs(x = "Estado", y = "College Education", title = "Histograma de College Education por Estado") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(data, aes(x = state, y = college_education)) +
  geom_boxplot() +
  labs(x = "Estado", y = "College Education", title = "Box Plot de College Education por Estado") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

La Ciudad de México es el estado con mayor educación. Chiapas, Michoacán, Oaxaca y Zacatecas son los que tienen menor educación.

# Histograma de unemployment
options(repr.plot.width=12, repr.plot.height=8)

ggplot(data, aes(x = state, y = unemployment)) +
  geom_col() +
  labs(x = "Estado", y = "Unemployment", title = "Histograma de Unemployment por Estado") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(data, aes(x = state, y = unemployment)) +
  geom_boxplot() +
  labs(x = "Estado", y = "Unemployment", title = "Box Plot de Unemployment por Estado") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

El estado con más desempleo es Coahuila, junto con Ciudad de México, Tabasco y Tlaxcala. Los que tienen menos son Morelos, Yucatán y Campeche.

# Histograma de real_wage
options(repr.plot.width=12, repr.plot.height=8)

ggplot(data, aes(x = state, y = real_wage)) +
  geom_col() +
  labs(x = "Estado", y = "Real Wage", title = "Histograma de Real wage por Estado") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(data, aes(x = state, y = real_wage)) +
  geom_boxplot() +
  labs(x = "Estado", y = "Real Wage", title = "Box Plot de Real wage por Estado") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Hay mucha variabilidad entre los estados, siendo Durango y Sinaloa los que menor valor de “Real Wage” poseen. Mientras que Ciudad de México y Campeche son las que más alto salario real tienen.

# Histograma de real_wage
options(repr.plot.width=12, repr.plot.height=8)

ggplot(data, aes(x = state, y = ratio_public_investment)) +
  geom_col() +
  labs(x = "Estado", y = "Public Investment (%)", title = "Histograma de Public Investment (%) por Estado") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(data, aes(x = state, y = ratio_public_investment)) +
  geom_boxplot() +
  labs(x = "Estado", y = "Public Investment (%)", title = "Box Plot de Public Investment (%) por Estado") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Si bien no existe mucha variabilidad en la mayoría de los estados, Baja California, Jalisco y Ciudad de México cuentan con un menor ratio entre la inversión publica estatal y el PIB nacional. Por otro lado, Oaxaca cuenta con el mayor ratio de inversión pública con respecto al PIB nacional.

# Histograma de business_activity
options(repr.plot.width=12, repr.plot.height=8)

ggplot(data, aes(x = state, y = business_activity)) +
  geom_col() +
  labs(x = "Estado", y = "Business Activity", title = "Histograma de Business Activity por Estado") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(data, aes(x = state, y = business_activity)) +
  geom_boxplot() +
  labs(x = "Estado", y = "Business Activity", title = "Box Plot de Business Activity por Estado") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

El único estado con actividad de negocios positiva es Baja California. Los más bajos son Quintana Roo y Yucatán.

# Histograma de business_activity
options(repr.plot.width=12, repr.plot.height=8)

ggplot(data, aes(x = state, y = good_governance)) +
  geom_col() +
  labs(x = "Estado", y = "Ratio Investment/Debt", title = "Histograma de Ratio Investment/Debt por Estado") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(data, aes(x = state, y = good_governance)) +
  geom_boxplot() +
  labs(x = "Estado", y = "Ratio Investment/Debt", title = "Box Plot de Ratio Investment/Debt por Estado") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Por otro ladoel estado con mayor ratio entre la inversión y deuda pública es Nuevo León, alcanzando el valor de 200.

Estas variables cuentan con algún estado como outlier, rompiendo el patrón de los valores de otros estados y marcando una dispersión más grande. Así mismo, las variables exchange_rate e inpc no cuentan con variabilidad geoespacial a lo largo de los años, por lo que no existen patrones visibles para incluirlos en el análisis espacial de los datos.

Por esta razón se eligirán las siguientes variables para medir el impacto espacial: * Tourism_gdp * Crime_rate * Unemployment * Real_wage * College_eductaion

Visualización de Variables en Mapas

mexico_states <- st_read('mexlatlong.shp')
## Reading layer `mexlatlong' from data source 
##   `C:\Users\rodio\OneDrive\Escritorio\8semestre\Bloque Final\Actividad 1\mexlatlong.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 32 features and 19 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -118.4042 ymin: 14.55055 xmax: -86.73862 ymax: 32.71846
## Geodetic CRS:  WGS 84
#Cambiar Distrito Federal a Ciudad de México
mexico_states$ADMIN_NAME <- gsub("Distrito Federal", "Ciudad de Mexico", mexico_states$ADMIN_NAME)

#Crear base de datos con datos geográficos
states <- merge(data, mexico_states, by.x = "state", by.y = "ADMIN_NAME")
# Crear el mapa base
GDP <- leaflet(states) %>% addTiles()

# Añadir capas para cada año
for (year in unique(states$year)) {
  # Subset the data and ensure it retains its spatial attributes
  states_subset <- states[states$year == year, ] 
  states_subset <- st_as_sf(states_subset)
  
  GDP <- GDP %>%
    addPolygons(
      data = states_subset,  # Use the subset here
      fillColor = ~colorNumeric("GnBu", domain = tourism_gdp)(tourism_gdp),
      fillOpacity = 0.7,
      weight = 2,
      color = "black",
      dashArray = "3",
      group = as.character(year), # Asignar un grupo para cada año
      popup = ~paste("Estado:", state, "<br>", "PIB Turismo:", tourism_gdp)
    )
}

# Añadir control de capas
GDP <- GDP %>%
  addLayersControl(
    baseGroups = unique(states$year), # Usar los años como grupos base
    options = layersControlOptions(collapsed = FALSE) # Mostrar el control expandido
  )

# Mostrar el mapa
GDP

Ciudad de México es quien posee más PIB del Turismo, sin embargo Quintana Roo, Estado de México, Jalisco, Guanajuato y Nuevo León han incrementado este valor en los últimos 15 años.

# Crear el mapa base
CRIM <- leaflet(states) %>% addTiles()

# Añadir capas para cada año
for (year in unique(states$year)) {
  # Subset the data and ensure it retains its spatial attributes
  states_subset <- states[states$year == year, ] 
  states_subset <- st_as_sf(states_subset)
  
  CRIM <- CRIM %>%
    addPolygons(
      data = states_subset,  # Use the subset here
      fillColor = ~colorNumeric("YlOrRd", domain = crime_rate)(crime_rate),
      fillOpacity = 0.7,
      weight = 2,
      color = "black",
      dashArray = "3",
      group = as.character(year), # Asignar un grupo para cada año
      popup = ~paste("Estado:", state, "<br>", "Tasa criminalidad (%):", crime_rate)
    )
}

# Añadir control de capas
CRIM <- CRIM %>%
  addLayersControl(
    baseGroups = unique(states$year), # Usar los años como grupos base
    options = layersControlOptions(collapsed = FALSE) # Mostrar el control expandido
  )

# Mostrar el mapa
CRIM

La variable de la tasa de criminalidad ha variado entre estados del 2006 al 2022 siendo Guerrero, Michoacan, Chihuahua, Colima, Baja California, Baja California Sur, Guanajuato y Zacatecas epicentros de una gran tasa de criminalidad por arriba del 70% en uno o varios años entre este periodo.

# Crear el mapa base
EMP <- leaflet(states) %>% addTiles()

# Añadir capas para cada año
for (year in unique(states$year)) {
  # Subset the data and ensure it retains its spatial attributes
  states_subset <- states[states$year == year, ] 
  states_subset <- st_as_sf(states_subset)
  
  EMP <- EMP %>%
    addPolygons(
      data = states_subset,  # Use the subset here
      fillColor = ~colorNumeric("YlOrRd", domain = unemployment)(unemployment),
      fillOpacity = 0.7,
      weight = 2,
      color = "black",
      dashArray = "3",
      group = as.character(year), # Asignar un grupo para cada año
      popup = ~paste("Estado:", state, "<br>", "Tasa de Desempleo:", unemployment)
    )
}

# Añadir control de capas
EMP <- EMP %>%
  addLayersControl(
    baseGroups = unique(states$year), # Usar los años como grupos base
    options = layersControlOptions(collapsed = FALSE) # Mostrar el control expandido
  )

# Mostrar el mapa
EMP

La tasa de desempleo ha afectado a la mayoría de los estados de México siendo los años 2012, 2015, 2017 y 2021 donde más estados han tenido tasas mayores a 5 puntos. Por otro lado en años como 2022, 2019, 2016 y 2008, solo algunos estados de la región del norte y sur cuentan con una alta tasa de desempleo, sin embargo la mayoria son menores a los 5 puntos.

# Crear el mapa base
WAGE <- leaflet(states) %>% addTiles()

# Añadir capas para cada año
for (year in unique(states$year)) {
  # Subset the data and ensure it retains its spatial attributes
  states_subset <- states[states$year == year, ] 
  states_subset <- st_as_sf(states_subset)
  
  WAGE <- WAGE %>%
    addPolygons(
      data = states_subset,  # Use the subset here
      fillColor = ~colorNumeric("GnBu", domain = real_wage)(real_wage),
      fillOpacity = 0.7,
      weight = 2,
      color = "black",
      dashArray = "3",
      group = as.character(year), # Asignar un grupo para cada año
      popup = ~paste("Estado:", state, "<br>", "Salario Real:", real_wage)
    )
}

# Añadir control de capas
WAGE <- WAGE %>%
  addLayersControl(
    baseGroups = unique(states$year), # Usar los años como grupos base
    options = layersControlOptions(collapsed = FALSE) # Mostrar el control expandido
  )

# Mostrar el mapa
WAGE

Los estados con más salario real de los últimos 5 años se encuentran en la zona noroeste, suroeste y el centro del país, resaltando Campeche, Ciudad de México, Nuevo León y Querétaro. Mientras qye la zona centro-este y sur tienen las concentraciones más bajas entorno al salario real

# Crear el mapa base
EDU <- leaflet(states) %>% addTiles()

# Añadir capas para cada año
for (year in unique(states$year)) {
  # Subset the data and ensure it retains its spatial attributes
  states_subset <- states[states$year == year, ] 
  states_subset <- st_as_sf(states_subset)
  
  EDU <- EDU %>%
    addPolygons(
      data = states_subset,  # Use the subset here
      fillColor = ~colorNumeric("GnBu", domain = college_education)(college_education),
      fillOpacity = 0.7,
      weight = 2,
      color = "black",
      dashArray = "3",
      group = as.character(year), # Asignar un grupo para cada año
      popup = ~paste("Estado:", state, "<br>", "Educación Avanzada (%):", college_education,
                     "<br>", "Densidad Poblacional:", pop_density)
    )
}

# Añadir control de capas
EDU <- EDU %>%
  addLayersControl(
    baseGroups = unique(states$year), # Usar los años como grupos base
    options = layersControlOptions(collapsed = FALSE) # Mostrar el control expandido
  )

# Mostrar el mapa
EDU

En términos generales, la mayoría de los estados ha tenido un incremento en la tasa de educación avanzada de sus habitantes siendo muy alto en la zona noreste, noroeste, el centro de méxico y el suroeste del país. Mientras que en la zona sur y el centro-norte cuentan con las menores tasas de educación avanzada.

Matrices de Conectividad Rook y Queen

# Load the datasets
mexlatlong <- st_read("mexlatlong.shp")  # Read shapefile
## Reading layer `mexlatlong' from data source 
##   `C:\Users\rodio\OneDrive\Escritorio\8semestre\Bloque Final\Actividad 1\mexlatlong.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 32 features and 19 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -118.4042 ymin: 14.55055 xmax: -86.73862 ymax: 32.71846
## Geodetic CRS:  WGS 84
data <- read_excel("tourism_state_data.xlsx")  # Read Excel file

# Merge the spatial data with the attribute data based on the state column
states <- mexlatlong %>%
  left_join(data, by = c("ADMIN_NAME" = "state"))  # Ensure column names match

# Define the variables to analyze
#variables <- c("tourism_gdp", "crime_rate", "college_education",
#               "unemployment", "real_wage")

variables <- c("tourism_gdp", "crime_rate", "college_education",
               "unemployment", "employment", "business_activity",
               "real_wage", "pop_density", "good_governance",
               "ratio_public_investment")

# Convertir 'states' a un objeto espacial
data_sp <- na.omit(states[, c(variables, "geometry")])

mexlatlong_sp <- as(data_sp, "Spatial")
# Crear la matriz de vecindad Queen
nb_queen <- poly2nb(mexlatlong_sp, queen = TRUE)

# Crear la matriz de pesos espaciales
lw_queen <- nb2listw(nb_queen, style = "W", zero.policy = TRUE)

# Obtener las coordenadas de los centroides
centroids <- coordinates(mexlatlong_sp)

plot(mexlatlong_sp,border="blue",axes=FALSE,las=1, main="Mexico Spatial Connectivity Matrix - Queen")
plot(mexlatlong_sp,col="grey",border=grey(0.9),axes=T,add=T) 
plot(lw_queen, centroids, add=TRUE, col='dark green')

# Crear la matriz de vecindad Rook
nb_rook <- poly2nb(mexlatlong_sp, queen = FALSE)

# Crear la matriz de pesos espaciales
lw_rook_subset <- nb2listw(nb_rook, style = "W", zero.policy = TRUE)

plot(mexlatlong_sp,border="red",axes=FALSE,las=1, main="Mexico Spatial Connectivity Matrix - Rook")
plot(mexlatlong_sp,col="grey",border=grey(0.9),axes=T,add=T) 
plot(lw_queen, centroids, add=TRUE, col='red')

Global Moran’s I Statistic - Autocorrelación Global y Local

# Convert variables to numeric to avoid errors
for (var in variables) {
  states[[var]] <- as.numeric(states[[var]])
}

# Loop through each variable for Global & Local Moran's I
for (var in variables) {
  cat("\n--- Global Moran's I for", var, "---\n")

  # Check for missing values and remove them for this variable
  data_for_moran <- na.omit(states[, c(var, "geometry")])

  if (nrow(data_for_moran) > 1) { # Ensure enough data after removing NAs

    # Convert the subset to a spatial object
    mexlatlong_sp <- as(data_for_moran, "Spatial")

    # Recalculate neighbors and spatial weights for the subset data
    # Rook contiguity
    nb_rook <- poly2nb(mexlatlong_sp, queen = FALSE)
    lw_rook_subset <- nb2listw(nb_rook, style = "W", zero.policy = TRUE)

    # Queen contiguity
    nb_queen <- poly2nb(mexlatlong_sp, queen = TRUE)
    lw_queen_subset <- nb2listw(nb_queen, style = "W", zero.policy = TRUE)

    # Global Moran’s I - Use the Rook contiguity weights
    global_moran_rook <- moran.test(data_for_moran[[var]],
                                    listw = lw_rook_subset, zero.policy = TRUE,
                                    na.action = na.omit)
    cat("\n--- Global Moran's I with Rook contiguity ---\n")
    print(global_moran_rook)

    # Global Moran’s I - Use the Queen contiguity weights
    global_moran_queen <- moran.test(data_for_moran[[var]],
                                     listw = lw_queen_subset, zero.policy = TRUE,
                                     na.action = na.omit)
    cat("\n--- Global Moran's I with Queen contiguity ---\n")
    print(global_moran_queen)

    # Moran Scatterplot - Rook contiguity
    moran.plot(data_for_moran[[var]], lw_rook_subset, main = paste("Moran’s I (Rook) -", var))

    # Moran Scatterplot - Queen contiguity
    moran.plot(data_for_moran[[var]], lw_queen_subset, main = paste("Moran’s I (Queen) -", var))

    # Local Moran’s I - Use the Rook contiguity weights
    local_moran_res_rook <- localmoran(data_for_moran[[var]], lw_rook_subset, zero.policy = TRUE)

    # Local Moran’s I - Use the Queen contiguity weights
    local_moran_res_queen <- localmoran(data_for_moran[[var]], lw_queen_subset, zero.policy = TRUE)

    # Append Local Moran’s I values and p-values to the dataset for both Rook and Queen
    pval_name_rook <- paste0("pval_", var, "_rook")
    cluster_name_rook <- paste0("cluster_", var, "_rook")
    pval_name_queen <- paste0("pval_", var, "_queen")
    cluster_name_queen <- paste0("cluster_", var, "_queen")

    states[[pval_name_rook]] <- NA  # Initialize as NA
    states[[cluster_name_rook]] <- "Not Significant"  # Default value
    states[[pval_name_queen]] <- NA  # Initialize as NA
    states[[cluster_name_queen]] <- "Not Significant"  # Default value

    # Transfer Local Moran's results for Rook and Queen into the merged_data
    states[rownames(local_moran_res_rook), pval_name_rook] <- local_moran_res_rook[, 5]
    states[rownames(local_moran_res_rook), cluster_name_rook] <- ifelse(local_moran_res_rook[, 5] < 0.05, "Significant", "Not Significant")

    states[rownames(local_moran_res_queen), pval_name_queen] <- local_moran_res_queen[, 5]
    states[rownames(local_moran_res_queen), cluster_name_queen] <- ifelse(local_moran_res_queen[, 5] < 0.05, "Significant", "Not Significant")

    # Convert to factor
    states[[cluster_name_rook]] <- as.factor(states[[cluster_name_rook]])
    states[[cluster_name_queen]] <- as.factor(states[[cluster_name_queen]])

    # Local cluster map using ggplot for Rook contiguity
    print(
      ggplot(states) +
        geom_sf(aes(fill = !!sym(cluster_name_rook))) +
        scale_fill_manual(values = c("Significant" = "red", "Not Significant" = "grey")) +
        labs(title = paste("Local Spatial Autocorrelation -", var, "(Rook)"), fill = "Cluster")
    )

    # Local cluster map using ggplot for Queen contiguity
    print(
      ggplot(states) +
        geom_sf(aes(fill = !!sym(cluster_name_queen))) +
        scale_fill_manual(values = c("Significant" = "red", "Not Significant" = "grey")) +
        labs(title = paste("Local Spatial Autocorrelation -", var, "(Queen)"), fill = "Cluster")
    )
  } else {
    cat("Skipping", var, "due to missing or invalid data.\n")
  }
}
## 
## --- Global Moran's I for tourism_gdp ---
## 
## --- Global Moran's I with Rook contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_rook_subset    
## 
## Moran I statistic standard deviate = 10.52, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      6.589445e-02     -1.901141e-03      4.152932e-05 
## 
## 
## --- Global Moran's I with Queen contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_queen_subset    
## 
## Moran I statistic standard deviate = 2.8037, p-value = 0.002526
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      1.580368e-02     -1.901141e-03      3.987752e-05

## 
## --- Global Moran's I for crime_rate ---
## 
## --- Global Moran's I with Rook contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_rook_subset    
## 
## Moran I statistic standard deviate = 27.072, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      1.717773e-01     -1.901141e-03      4.115763e-05 
## 
## 
## --- Global Moran's I with Queen contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_queen_subset    
## 
## Moran I statistic standard deviate = 28.098, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      1.747368e-01     -1.901141e-03      3.952118e-05

## 
## --- Global Moran's I for college_education ---
## 
## --- Global Moran's I with Rook contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_rook_subset    
## 
## Moran I statistic standard deviate = 25.741, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      1.644027e-01     -1.901141e-03      4.173859e-05 
## 
## 
## --- Global Moran's I with Queen contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_queen_subset    
## 
## Moran I statistic standard deviate = 27.213, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      1.703747e-01     -1.901141e-03      4.007815e-05

## 
## --- Global Moran's I for unemployment ---
## 
## --- Global Moran's I with Rook contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_rook_subset    
## 
## Moran I statistic standard deviate = 11.857, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      7.460807e-02     -1.901141e-03      4.163943e-05 
## 
## 
## --- Global Moran's I with Queen contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_queen_subset    
## 
## Moran I statistic standard deviate = 11.411, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      7.025300e-02     -1.901141e-03      3.998308e-05

## 
## --- Global Moran's I for employment ---
## 
## --- Global Moran's I with Rook contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_rook_subset    
## 
## Moran I statistic standard deviate = 14.873, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      0.0940311498     -0.0019011407      0.0000416063 
## 
## 
## --- Global Moran's I with Queen contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_queen_subset    
## 
## Moran I statistic standard deviate = 14.299, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      8.847970e-02     -1.901141e-03      3.995132e-05

## 
## --- Global Moran's I for business_activity ---
## 
## --- Global Moran's I with Rook contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_rook_subset    
## 
## Moran I statistic standard deviate = 49.039, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      3.124443e-01     -1.901141e-03      4.108953e-05 
## 
## 
## --- Global Moran's I with Queen contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_queen_subset    
## 
## Moran I statistic standard deviate = 49.286, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      0.3076820338     -0.0019011407      0.0000394559

## 
## --- Global Moran's I for real_wage ---
## 
## --- Global Moran's I with Rook contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_rook_subset    
## 
## Moran I statistic standard deviate = 23.309, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      1.485904e-01     -1.901141e-03      4.168397e-05 
## 
## 
## --- Global Moran's I with Queen contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_queen_subset    
## 
## Moran I statistic standard deviate = 22.319, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      1.393045e-01     -1.901141e-03      4.002578e-05

## 
## --- Global Moran's I for pop_density ---
## 
## --- Global Moran's I with Rook contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_rook_subset    
## 
## Moran I statistic standard deviate = 71.396, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      4.553038e-01     -1.901141e-03      4.100861e-05 
## 
## 
## --- Global Moran's I with Queen contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_queen_subset    
## 
## Moran I statistic standard deviate = 72.35, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      4.521094e-01     -1.901141e-03      3.937831e-05

## 
## --- Global Moran's I for good_governance ---
## 
## --- Global Moran's I with Rook contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_rook_subset    
## 
## Moran I statistic standard deviate = 4.0102, p-value = 3.033e-05
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      1.608462e-02     -1.901141e-03      2.011524e-05 
## 
## 
## --- Global Moran's I with Queen contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_queen_subset    
## 
## Moran I statistic standard deviate = 3.6914, p-value = 0.0001115
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      1.433605e-02     -1.901141e-03      1.934785e-05

## 
## --- Global Moran's I for ratio_public_investment ---
## 
## --- Global Moran's I with Rook contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_rook_subset    
## 
## Moran I statistic standard deviate = 9.4053, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      0.0582749306     -0.0019011407      0.0000409357 
## 
## 
## --- Global Moran's I with Queen contiguity ---
## 
##  Moran I test under randomisation
## 
## data:  data_for_moran[[var]]  
## weights: lw_queen_subset    
## 
## Moran I statistic standard deviate = 9.3924, p-value < 2.2e-16
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      5.698597e-02     -1.901141e-03      3.930841e-05

Tras analizar las variables de la base de datos, se determinó que todas las variables son significativamente estadísticas, sin embargo la mayoría cuenta con un Índice de Global Moran muy cercano a 0, indicando mínima autocorrelación espacial de los datos siendo la variable de employment, ratio_public_investment, business_activity la de mayor indice en autocorrelación espacial.

En cuestión de la autocorrelación local representada por los mapas de significancia, vemos como variables como tourism_gdp y employment consideran tanto la región norte, centro y sur significativa dejando los estados más al oeste y este fuera de estos clusters.

Por otro lado, pop_density, business_activity, y crime_rate consideran a casi el 80% de los estados de la republica mexicana como significativos. Cabe aclarar que este patrón tambien se puede revisar contrariamente donde good_governance y ratio_public_investmente consideran menos del 30% de los estados como significativos; estando muy segregados para formar varios clusters pequeños.

Conclusiones y Hallazgos

  1. Presencia de autocorrelación espacial: Encontramos que hay autocorrelación espacial significativa en todas las variables analizadas (p-valor < 0.05), lo que indica que los valores en un estado si llegan a estar influenciados por los estados vecinos tanto en la matriz de conectividad rook como queen.

  2. Valor del Índice de Global Moran’s I en general: Aunque los índices fueron significativos, habían variables muy bajas como turismo_gdp cuya correlación espacial era débil mientras que variables como employment y ratio_public_investment en matriz rook y business_activity en matriz queen mostraron una autocorrelación espacial más fuerte, aunque sin superar el valor de 0.5 de Global Moran’s

  3. Turismo (tourism_gdp): La Ciudad de México destaca por tener consistentemente el mayor PIB turístico, mientras que estados como Jalisco, Nuevo León, Guanajuato y Quintana Roo han mostrado un crecimiento importante a lo largo de los años. Esto se demuestra en la significacia de los clusters, pues el centro y norte del pais fueron identificados como regiones significativas para la el análisis de autocorrelación espacial.

  4. Educación avanzada (college_education): En cuanto a la educación, la proporción de educación superior están en el norte y centro del país (CDMX, NL, QRO). En contraste, estados del sur (Chiapas, Oaxaca, Guerrero) formanron los clusters de bajo nivel educativo por lo que valdría la pena realizar un análisis a profundidad de las posibles causas de este nivel. En cuestión de autocorrelación local, tanto la parte noroeste, centro y suroeste fueron significativas.

  5. Salario real (real_wage): El centro y noroeste del país concentran los salarios más altos, destacando CDMX y Campeche. Hay una autocorrelación moderada entre estados vecinos con niveles similares de salario real posiblemente por términos de inversión extranjera o cantidad de empleos.

  6. Gobernanza (good_governance): Si bien Nuevo León destaca con un valor extremadamente alto, esto no es mas que un outlier. A pesar de la significancia estadística, la autocorrelación es baja, sugiriendo que la gobernanza es más un fenómeno local y no es influenciado por los demas estados.

  7. Densidad poblacional (pop_density): Esta variable mostró la más alta autocorrelación espacial. Los estados más densamente poblados (CDMX, EDOMEX) están agrupados, y lo mismo ocurre con los menos densos (BCS, Durango), podría ser interesante si está relacionada con el tamaño del terreno .

  8. Inversión pública (ratio_public_investment): Sorpresivamente, Oaxaca destaca como el estado con mayor inversión pública respecto al PIB, formando un cluster significativo con vecinos del sur, aunque la autocorrelación no fue de las más altas.

  9. Clusters positivos para el turismo: Los estados que han incrementado su PIB turístico están cerca de otros con desempeño similar (CDMX-EDOMEX-Guanajuato). Esto sugiere que políticas de turismo pueden tener efectos de derrame positivo regional y que probablemente la gente que visita ciertos estados, esta interesada en conocer los estados colindantes.

  10. Zonas prioritarias para intervención: Estados con clusters de alto crimen, bajo empleo y baja educación (Chiapas, Oaxaca, Guerrero, Zacatecas) deberían ser priorizados para intervenciones integrales en seguridad, empleo y educación y poder mejorar estas estadísticas en el futuro.

LS0tDQp0aXRsZTogIkFjdGl2aWRhZCAxIg0KYXV0aG9yOiAiRXF1aXBvIDMgLSBHYW1tYSINCmRhdGU6ICIyMDI1LTA0LTAzIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFDQogICAgdGhlbWU6IGNlcnVsZWFuDQotLS0NCiMgKipBdXRvcmVzKioNCg0KKipFcXVpcG8gMyAtIEdhbW1hLioqDQoNCipJbnRlZ3JhbnRlczoqICANCiogICBDw6lzYXIgUm9tZW8gVmVnYSAgDQoqICAgR2FtYWxpZWwgT3N0b3MgIA0KKiAgIEx1aXMgQ2FybG9zIEJvcmLDs24gIA0KKiAgIFJvZHJpZ28gQXJyb3lvICANCg0KIyAqKkludHJvZHVjY2nDs24gYSBsYSBBY3RpdmlkYWQqKg0KDQohW10odHVyaXNtby5wbmcpDQoNCioqMSkgQnJldmVtZW50ZSwgZGVzY3JpYmlyIGNvbiBzdXMgcHJvcGlhcyBwYWxhYnJhcyBxdcOpIGVzIHVuIEVTREEgeSBjdcOhbCBlcyBzdSBwcmluY2lwYWwgcHJvcMOzc2l0byBlbiBlbCBwcm9jZXNvIGRlIGFuYWzDrXRpY2EgZGUgZGF0b3MuKiogIA0KDQpVbiBFU0RBIChFeHBsYW5hdG9yeSBTcGFjaWFsIERhdGEgQW5hbHlzaXMpIHBvciBzdXMgc2lnbGFzIGVuIGluZ2zDqXMgZXMgdW4gY29uanVudG8gZGUgaGVycmFtaWVudGFzIGVzdGFkw61zdGljYXMgcXVlIGJ1c2NhbiBjb21wcmVuZGVyIGxhIGVzdHJ1Y3R1cmEgeSBkaXN0cmlidWNpw7NuIGVzcGFjaWFsIGRlIGxvcyBkYXRvcyBhbnRlcyBkZSByZWFsaXphciB1biBhbsOhbGlzaXMgbcOhcyBwcm9mdW5kbyBvIHVuYSBtb2RlbGFjacOzbiBkZSBkYXRvcyxpZGVudGlmaWNhbmRvIHBhdHJvbmVzIHkgcmVsYWNpb25lcyBlc3BhY2lhbGVzIGVuIGxhIGluZm9ybWFjacOzbi4gIA0KDQpFbiBjb21wYXJhY2nDs24gZGUgdW4gRURBIChFeHBsYW5hdG9yeSBEYXRhIEFuYWx5c2lzKSBwb3Igc3VzIHNpZ2xhcyBlbiBpbmdsw6lzLCBlbCBFU0RBIHRvbWEgZW4gY3VlbnRhIGVsIGZhY3RvciBnZW9lc3BhY2lhbCBwYXJhIGRhdG9zIGNvbiB1YmljYWNpb25lcyBnZW9ncsOhZmljYSB5IGNvbiBwb3NpYmlsaWRhZCBkZSB0ZW5lciBhdXRvY29ycmVsYWNpw7NuIGVzcGFjaWFsIGFsdGEuICANCg0KQWxndW5vcyBwcm9ww7NzaXRvcyBkZWwgRVNEQSBzb246ICANCg0KKiAgIElkZW50aWZpY2EgKipwYXRyb25lcyB5IGFub21hbMOtYXMgZXNwYWNpYWxlcyoqIGVuIGxvcyBkYXRvcyBkZSB1biBwZXJpb2RvIGRlZmluaWRvDQoqICAgVmlzdWFsaXphIGxhICoqZGlzdHJpYnVjacOzbiBlc3BhY2lhbCoqIGRlIGxvcyBkYXRvcyBlbiBtYXBhcyB0ZW3DoXRpY29zIG8gZGUgY2Fsb3INCiogICBEZXRlY3RhICoqYXV0b2NvcnJlbGFjacOzbiBlc3BhY2lhbCoqIGVudHJlIGxvY2FjaW9uZXMgdmVjaW5hcw0KKiAgIEV2YWx1YSAqKnN1cHVlc3RvcyBwYXJhIG1vZGVsb3MgZXNwYWNpYWxlcyoqIG1lZGlhbnRlIGRlcGVuZGVuY2lhcyB5IHZhcmlhYmlsaWRhZCBlbnRyZSByZWdpb25lcw0KDQoqKjIpIEJyZXZlbWVudGUsIGRlc2NyaWJpciBjb24gc3VzIHByb3BpYXMgcGFsYWJyYXMgZWwgY29uY2VwdG8gZGUgYXV0b2NvcnJlbGFjacOzbiBlc3BhY2lhbCBhc8OtIGNvbW8gMS0yIGVqZW1wbG9zIHJlbGFjaW9uYWRvcyBjb24gZGljaG8gY29uY2VwdG8uKiogIA0KDQpMYSBhdXRvY29ycmVsYWNpw7NuIGVzcGFjaWFsIGVzIHVuYSBtZWRpZGEgcXVlIG1pZGUgbGEgcmVsYWNpw7NuIGVudHJlIGVsIHZhbG9yIGRlIHVuYSB2YXJpYWJsZSB5IGxhcyB1YmljYWNpb25lcyBnZW9ncsOhZmljYXMgY2VyY2FuYXMuIEVzIGRlY2lyLCBlbCB2YWxvciBkZSB1bmEgcmVnacOzbiBlcyBpbmZsdWVuY2lhZG8gcG9yIGxhcyBjYXJhY3RlcsOtc3RpY2FzIG8gdmFsb3JlcyBkZSBsb3MgbHVnYXJlcyBjZXJjYW5vcy4gIA0KDQpFbCBJbmRpY2UgZGUgTW9yYW4geSBlbCBJbmRpY2UgZGUgR2Vhcnkgc29uIGxvcyBpbmRpY2Fkb3JlcyBtw6FzIHVzYWRvcyBwYXJhIG1lZGlyIGxhIGNvcnJlbGFjacOzbiBlc3BhY2lhbCBnbG9iYWwgeSBsb2NhbCBkZSBsb3MgZGF0b3MuICANCg0KQWxndW5vcyBlamVtcGxvcyBkb25kZSBwb2RlbW9zIG9ic2VydmFyIGF1dG9jb3JyZWxhY2nDs24gZXNwYWNpYWwgc29uOg0KDQoqICAgUHJvcGFnYWNpw7NuIGRlIGVuZmVybWVkYWRlcyBjb250YWdpb3NhcyBlbiBsdWdhcmVzIGNlcmNhbm9zIChQYW5kZW1pYSBkZSBDT1ZJRC0xOSkNCiogICBEaXN0cmlidWNpw7NuIGRlIHBvZGVyZXMgYWRxdWlzaXRpdm9zIHkgbml2ZWwgZGUgcG9icmV6YSBlbiB1bmEgY2l1ZGFkIChEaXZpc2nDs24gZW50cmUgU2FuIFBlZHJvIHkgZWwgY2VudHJvIGRlIE1vbnRlcnJleSkNCiogICBSZW50YSBkZSB2aXZpZW5kYSB0ZW1wb3JhbCBlbiBkaXN0aW50YXMgcmVnaW9uZXMgdXJiYW5hcyAoWm9uYSBURUMgeSBab25hcyBJbmR1c3RyaWFsZXMgZGUgTnVldm8gTGXDs24pDQoNCiMgKipTaXR1YWNpw7NuIFByb2JsZW1hKioNCg0KRWwgdHVyaXNtbyBoYSBzaWRvIHVubyBkZSBsYXMgbWF5b3JlcyBhY3RpdmlkYWRlcyBlY29uw7NtaWNhcyBkZSBNw6l4aWNvLCBkb25kZSBkZXN0YWNhIHBvciBzdXMgYW1wbGlhcyBjdWx0dXJhcyB5IHRyYWRpY2lvbmFlcywgZ2FzdHJvbm9tw61hLCBjZWxlYnJhY2lvbmVzIHkgcmVjaW50b3MgbmF0dXJhbGVzL3VyYmFub3MgcHJvcGljaW9zIHBhcmEgdmlhamVzIHZhY2FjaW5vbmFsZXMgbyBkZSBuZWdvY2lvcy4gIA0KDQpDb24gZWwgTXVuZGlhbCBkZSBGdXRib2wgb3JnYW5pemFkbyBwb3IgbGEgRklGQSBlbiAyMDI2LCBNw6l4aWNvIHNlcsOhIHVuYSBkZSBsYXMgY2VkZXMgcG9yIGxvIHF1ZSBlbCB0dXJpc21vIHNlIHZlcsOhIGJlbmVmaWNpYWRvIGR1cmFudGUgbG9zIDQwIGTDrWFzIHF1ZSBkdXJhIGVzdGUgbWFjcm9ldmVudG8uIFNpbiBlbWJhcmdvLCDCv2V4aXN0aXLDoSBhbGd1bmEgcmVsYWNpw7NuIGRlIGJlbmVmaWNpbyBlbnRyZSBlc3RhZG9zIHF1ZSBwb3RlbmNpZSBlbCB0dXJpc21vPyBvIMK/aGFicsOhIGFsZ3VuIHBhdHLDs24gZGUgbG9zIGVzdGFkb3MgcXVlIGdlbmVyYW4gbcOhcyB0dXJpc21vIGNvbiBsb3MgcXVlIG5vPyAgDQoNCiMgKipBbsOhbGlzaXMgRXhwbG9yYXRvcmlvIEVzcGFjaWFsIGRlIGxvcyBEYXRvcyoqDQoNCiMjICpJbnN0YWxhciBMaWJyZXLDrWFzLCBQYXF1ZXRlcyB5IEJhc2VzIGRlIERhdG9zKg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNpbnN0YWxsLnBhY2thZ2VzKCJyZWFkeGwiKQ0KI2luc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KI2luc3RhbGwucGFja2FnZXMoImxlYWZsZXQiKQ0KI2luc3RhbGwucGFja2FnZXMoInNmIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJ0bWFwIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJ0bWFwdG9vbHMiKQ0KI2luc3RhbGwucGFja2FnZXMoImh0bWx3aWRnZXRzIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJuYW5pYXIiKQ0KI2luc3RhbGwucGFja2FnZXMoInNwIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJzcGRlcCIpDQogIA0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkobGVhZmxldCkNCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KHRtYXApDQpsaWJyYXJ5KHRtYXB0b29scykNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGh0bWx3aWRnZXRzKQ0KbGlicmFyeShuYW5pYXIpDQpsaWJyYXJ5KHNwKQ0KbGlicmFyeShzcGRlcCkNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBSZWFkIHRoZSBFeGNlbCBmaWxlDQpkYXRhIDwtIHJlYWRfZXhjZWwoInRvdXJpc21fc3RhdGVfZGF0YS54bHN4IikNCmBgYA0KDQojIyAqSWRlbnRpZmljYW5kbyBWYXJpYWJsZXMqDQoNCkRlbnRybyBkZSBsYSBCYXNlIGRlIERhdG9zIHNlIGVuY3VlbnRyYSBsYSBzaWd1aWVudGVzIHZhcmlhYmxlcyBwb3IgYW5hbGl6YXI6DQoqICAgKip0b3VyaXNtX2dkcDoqKiBWaWFqZXMgeSB0dXJpc21vIHF1ZSBjb250cmlidXllIGRpcmVjdGFtZW50ZSBhbCBQSUIgIA0KKiAgICoqY3JpbWVfcmF0ZToqKiBSYXRpbyBkZSBjcmltaW5hbGlkYWQgcG9yIGNhZGEgMTAwLDAwMCBwZXJzb25hcyAgDQoqICAgKip1bmVtcGxveW1lbnQ6KiogUG9yY2VudGFqZSBkZSBwb2JsYWNpw7NuIGRlc2VtcGxlYWRhICANCiogICAqKmVtcGxveWVtZW50KiogUG9yY2VudGFqZSBkZSBwb2JsYWNpw7NuIGVtcGxlYWRhICANCiogICAqKmJ1c2luZXNzX2FjdGl2aXR5OioqIMONbmRpY2UgZWNvbsOzbWljbyBwb25kZXJhZG8gcG9yIGxhIGRpc3RhbmNpYSBhbCBwdWVydG8gZXN0YWRvdW5pZGVuc2UgbcOhcyBjZXJjYW5vICANCiogICAqKnJlYWxfd2FnZToqKiBTYWxhcmlvIHJlYWwgdG9tYW5kbyBkZSBiYXNlIGVsIElOUEMgZGVsIDIwMTggKCQxMDAgTVhOKSAgDQoqICAgKipwb3BfZGVuc2l0eToqKiBQb2JsYWNpw7NuIHBvciBjYWRhIGttXjINCiogICAqKmdvb2RfZ292ZXJuYW5jZToqKiBSYXRpbyBlbnRyZSBsYSBpbnZlcnNpw7NuIHkgZGV1ZGEgcMO6YmxpY2EgZGVsIGVzdGFkbyAgDQoqICAgKipyYXRpb19wdWJsaWNfaW52ZXN0bWVudDoqKiBSYXRpbyBlbnRyZSBsYSBpbnZlcnNpw7NuIHDDumJsaWNhIHkgZWwgUElCIGRlbCBlc3RhZG8gIA0KKiAgICoqZXhjaGFuZ2VfcmF0ZToqKiBDYW1iaW8gZGUgMSBVU0QgcG9yIE1YTg0KKiAgICoqaW5wYzoqKiDDjW5kaWNlIE5hY2lvbmFsIGRlIFByZWNpbyBhbCBDb25zdW1pZG9yIHRvbWFuZG8gZGUgYmFzZSBlbCAyMDE4ICgkMTAwIE1YKSAgDQoqICAgKipjb2xsZWdlX2VkdWNhdGlvbjoqKiBQb3JjZW50YWplIGRlIHBvYmxhY2nDs24gY29uIHBvciBsbyBtZW5vcyBlZHVjYWNpw7NuIGF2YW56YWRhICANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnN0cihkYXRhKQ0KYGBgDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgRm9ybWF0ZWFyIHZhbG9yZXMgbnVtw6lyaWNvcyAoMiBkZWNpbWFsZXMpDQpkYXRhJHRvdXJpc21fZ2RwIDwtIHJvdW5kKGRhdGEkdG91cmlzbV9nZHAsIDIpDQpkYXRhJGNyaW1lX3JhdGUgPC0gcm91bmQoZGF0YSRjcmltZV9yYXRlLCAyKQ0KZGF0YSRidXNpbmVzc19hY3Rpdml0eSA8LSByb3VuZChkYXRhJGJ1c2luZXNzX2FjdGl2aXR5LCAyKQ0KZGF0YSRnb29kX2dvdmVybmFuY2UgPC0gcm91bmQoZGF0YSRnb29kX2dvdmVybmFuY2UsIDIpDQpkYXRhJHJlYWxfd2FnZSA8LSByb3VuZChkYXRhJHJlYWxfd2FnZSwgMikNCmRhdGEkcG9wX2RlbnNpdHkgPC0gcm91bmQoZGF0YSRwb3BfZGVuc2l0eSwgMikNCg0KIyBGb3JtYXRlYXIgdmFsb3JlcyBwb3JjZW50YWdlcw0KZGF0YSR1bmVtcGxveW1lbnQgPC0gcm91bmQoZGF0YSR1bmVtcGxveW1lbnQgKiAxMDAsIDIpDQpkYXRhJGVtcGxveW1lbnQgPC0gcm91bmQoZGF0YSRlbXBsb3ltZW50ICogMTAwLCAyKSAgDQpkYXRhJGNvbGxlZ2VfZWR1Y2F0aW9uIDwtIHJvdW5kKGRhdGEkY29sbGVnZV9lZHVjYXRpb24gKiAxMDAsIDIpDQoNCmBgYA0KDQoNCg0KIyMgKkFuw6FsaXNpcyBFeHBsb3JhdG9yaW8qDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpzdW1tYXJ5KGRhdGEpDQpgYGANCg0KIyMjIFNlcmllIGRlIFRpZW1wbyBkZSBWYXJpYWJsZSBEZXBlbmRpZW50ZQ0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBTZXJpZSBkZSB0aWVtcG8gZGUgdG91cmlzbV9nZHANCmdncGxvdChkYXRhLCBhZXMoeCA9IHllYXIsIHkgPSBhcy5udW1lcmljKGdzdWIoIiwiLCAiIiwgdG91cmlzbV9nZHApKSwgZ3JvdXAgPSBzdGF0ZSwgY29sb3IgPSBzdGF0ZSkpICsgICMgQ29udmVydCB0b3VyaXNtX2dkcCB0byBudW1lcmljDQogIGdlb21fbGluZSgpICsNCiAgbGFicyh0aXRsZSA9ICJHRFAgZGUgVHVyaXNtbyBwb3IgRXN0YWRvIGEgdHJhdsOpcyBkZWwgVGllbXBvIiwNCiAgICAgICB4ID0gIkHDsW8iLA0KICAgICAgIHkgPSAiR0RQIGRlIFR1cmlzbW8iKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KQSBsbyBsYXJnbyBkZSBsb3MgYcOxb3Mgc2UgYXByZWNpYSBxdWUgZWwgUElCIGRpcmVjdG8gZGVsIFR1cmlzbW8gbm8gZXMgZXN0YWNpb25hcmlvIGR1cmFudGUgZWwgcGVyaW9kbyBkZSAyMDA2IHkgMjAyMiwgdGVuaWVuZG8gdW5hIGNhaWRhIGVuIDIwMjAgZGViaWRvIGEgbGEgcGFuZGVtaWEuIEVuIGNhc28gZGUgcXVlcmVyIHJlYWxpemFyIHVuIGFuYWxpc2lzIHRlbXBvcmFsIHNlIG5lY2VzaXRhcsOtYSBjb250YXIgY29uIHJlZ2lzdHJvcyBtZW5zdWFsZXMgbyBzZW1hbmFsZXMgcXVlIGluZGlxdWVuIHVuIHBhdHLDs24gZXN0YWNpb25hcmlvIG8gZW4gc3UgZGVmZWN0bywgaGFjZXIgdW5hIHRyYW5zZm9ybWFjacOzbiBsb2dhcsOtdG1pY2EuDQoNClBhcmEgZXN0ZSBlamVyY2ljaW8sIG5vIHNlIGNvbnNpZGVyYXLDoSBlbCBmYWN0b3IgdGVtcG9yYWwgeSBzZSBlbmZvY2Fyw6EgZW4gZWwgYW7DoWxpc2lzIGVzcGFjaWFsLg0KDQoNCg0KDQojIyMgSWRlbnRpZmljYWNpw7NuIGRlIFZhbG9yZXMgTnVsb3MNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpnZ19taXNzX3ZhcihkYXRhLCBzaG93X3BjdD1UUlVFKQ0KYGBgDQoNCkNvbW8gc2UgcHVlZGUgYXByZWNpYXIsIG5vIGV4aXN0ZW4gdmFsb3JlcyBudWxvcyBlbiBuaW5ndW5hIGRlIGxhcyB2YXJpYWJsZXMgZGUgbGEgYmFzZSBkZSBkYXRvcw0KDQojIyMgQW7DoWxpc2lzIGRlc2NyaXB0aXZvIHkgZGUgZGlzcGVyc2nDs24NCmBgYHtyfQ0KIyBIaXN0b2dyYW1hIHkgQm94IFBsb3QgZGUgdG91cmlzbV9nZHANCm9wdGlvbnMocmVwci5wbG90LndpZHRoPTEyLCByZXByLnBsb3QuaGVpZ2h0PTgpDQoNCmdncGxvdChkYXRhLCBhZXMoeCA9IHN0YXRlLCB5ID0gdG91cmlzbV9nZHApKSArDQogIGdlb21fY29sKCkgKw0KICBsYWJzKHggPSAiRXN0YWRvIiwgeSA9ICJUdXJpc21vIFBJQiIsIHRpdGxlID0gIkhpc3RvZ3JhbWEgZGUgVHVyaXNtbyBQSUIgcG9yIEVzdGFkbyIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gc3RhdGUsIHkgPSB0b3VyaXNtX2dkcCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBsYWJzKHggPSAiRXN0YWRvIiwgeSA9ICJUdXJpc21vIFBJQiIsIHRpdGxlID0gIkJveCBQbG90IGRlIFR1cmlzbW8gUElCIHBvciBFc3RhZG8iKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KTGEgQ2l1ZGFkIGRlIE3DqXhpY28gc3VwZXJhIGVuIFBJQiBkZSB0dXJpc21vIGEgdG9kb3MgbG9zIGRlbcOhcyBlc3RhZG9zLiBMb3MgcXVlIG1lbm9zIHRpZW5lbiBzb24gVGxheGNhbGEgeSBDb2xpbWEuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIEhpc3RvZ3JhbWEgZGUgY3JpbWVfcmF0ZQ0Kb3B0aW9ucyhyZXByLnBsb3Qud2lkdGg9MTIsIHJlcHIucGxvdC5oZWlnaHQ9OCkNCmdncGxvdChkYXRhLCBhZXMoeCA9IHN0YXRlLCB5ID0gY3JpbWVfcmF0ZSkpICsNCiAgZ2VvbV9jb2woKSArDQogIGxhYnMoeCA9ICJFc3RhZG8iLCB5ID0gIkNyaW1lIFJhdGUiLCB0aXRsZSA9ICJIaXN0b2dyYW1hIGRlIENyaW1lIFJhdGUgcG9yIEVzdGFkbyIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gc3RhdGUsIHkgPSBjcmltZV9yYXRlKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGxhYnMoeCA9ICJFc3RhZG8iLCB5ID0gIkNyaW1lIFJhdGUiLCB0aXRsZSA9ICJCb3ggUGxvdCBkZSBDcmltZSBSYXRlIHBvciBFc3RhZG8iKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KQ2hpaHVhaHVhIGVzIGVsIHF1ZSB0aWVuZSBtYXlvciDDrW5kaWNlIGRlIGNyaW1lbiwgc2VndWlkbyBkZSBCYWphIENhbGlmb3JuaWEgeSBTaW5hbG9hLiBMb3MgcXVlIG1lbm9zIHRpZW5lbiBzb24gWXVjYXTDoW4geSBBZ3Vhc2NhbGllbnRlcy4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgSGlzdG9ncmFtYSBkZSBjb2xsZWdlX2VkdWNhdGlvbg0Kb3B0aW9ucyhyZXByLnBsb3Qud2lkdGg9MTIsIHJlcHIucGxvdC5oZWlnaHQ9OCkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gc3RhdGUsIHkgPSBjb2xsZWdlX2VkdWNhdGlvbikpICsNCiAgZ2VvbV9jb2woKSArDQogIGxhYnMoeCA9ICJFc3RhZG8iLCB5ID0gIkNvbGxlZ2UgRWR1Y2F0aW9uIiwgdGl0bGUgPSAiSGlzdG9ncmFtYSBkZSBDb2xsZWdlIEVkdWNhdGlvbiBwb3IgRXN0YWRvIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBzdGF0ZSwgeSA9IGNvbGxlZ2VfZWR1Y2F0aW9uKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGxhYnMoeCA9ICJFc3RhZG8iLCB5ID0gIkNvbGxlZ2UgRWR1Y2F0aW9uIiwgdGl0bGUgPSAiQm94IFBsb3QgZGUgQ29sbGVnZSBFZHVjYXRpb24gcG9yIEVzdGFkbyIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQpMYSBDaXVkYWQgZGUgTcOpeGljbyBlcyBlbCBlc3RhZG8gY29uIG1heW9yIGVkdWNhY2nDs24uIENoaWFwYXMsIE1pY2hvYWPDoW4sIE9heGFjYSB5IFphY2F0ZWNhcyBzb24gbG9zIHF1ZSB0aWVuZW4gbWVub3IgZWR1Y2FjacOzbi4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgSGlzdG9ncmFtYSBkZSB1bmVtcGxveW1lbnQNCm9wdGlvbnMocmVwci5wbG90LndpZHRoPTEyLCByZXByLnBsb3QuaGVpZ2h0PTgpDQoNCmdncGxvdChkYXRhLCBhZXMoeCA9IHN0YXRlLCB5ID0gdW5lbXBsb3ltZW50KSkgKw0KICBnZW9tX2NvbCgpICsNCiAgbGFicyh4ID0gIkVzdGFkbyIsIHkgPSAiVW5lbXBsb3ltZW50IiwgdGl0bGUgPSAiSGlzdG9ncmFtYSBkZSBVbmVtcGxveW1lbnQgcG9yIEVzdGFkbyIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gc3RhdGUsIHkgPSB1bmVtcGxveW1lbnQpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgbGFicyh4ID0gIkVzdGFkbyIsIHkgPSAiVW5lbXBsb3ltZW50IiwgdGl0bGUgPSAiQm94IFBsb3QgZGUgVW5lbXBsb3ltZW50IHBvciBFc3RhZG8iKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KRWwgZXN0YWRvIGNvbiBtw6FzIGRlc2VtcGxlbyBlcyBDb2FodWlsYSwganVudG8gY29uIENpdWRhZCBkZSBNw6l4aWNvLCBUYWJhc2NvIHkgVGxheGNhbGEuIExvcyBxdWUgdGllbmVuIG1lbm9zIHNvbiBNb3JlbG9zLCBZdWNhdMOhbiB5IENhbXBlY2hlLg0KDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIEhpc3RvZ3JhbWEgZGUgcmVhbF93YWdlDQpvcHRpb25zKHJlcHIucGxvdC53aWR0aD0xMiwgcmVwci5wbG90LmhlaWdodD04KQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBzdGF0ZSwgeSA9IHJlYWxfd2FnZSkpICsNCiAgZ2VvbV9jb2woKSArDQogIGxhYnMoeCA9ICJFc3RhZG8iLCB5ID0gIlJlYWwgV2FnZSIsIHRpdGxlID0gIkhpc3RvZ3JhbWEgZGUgUmVhbCB3YWdlIHBvciBFc3RhZG8iKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQoNCmdncGxvdChkYXRhLCBhZXMoeCA9IHN0YXRlLCB5ID0gcmVhbF93YWdlKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGxhYnMoeCA9ICJFc3RhZG8iLCB5ID0gIlJlYWwgV2FnZSIsIHRpdGxlID0gIkJveCBQbG90IGRlIFJlYWwgd2FnZSBwb3IgRXN0YWRvIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCkhheSBtdWNoYSB2YXJpYWJpbGlkYWQgZW50cmUgbG9zIGVzdGFkb3MsIHNpZW5kbyBEdXJhbmdvIHkgU2luYWxvYSBsb3MgcXVlIG1lbm9yIHZhbG9yIGRlICJSZWFsIFdhZ2UiIHBvc2Vlbi4gTWllbnRyYXMgcXVlIENpdWRhZCBkZSBNw6l4aWNvIHkgQ2FtcGVjaGUgc29uIGxhcyBxdWUgbcOhcyBhbHRvIHNhbGFyaW8gcmVhbCB0aWVuZW4uDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIEhpc3RvZ3JhbWEgZGUgcmVhbF93YWdlDQpvcHRpb25zKHJlcHIucGxvdC53aWR0aD0xMiwgcmVwci5wbG90LmhlaWdodD04KQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBzdGF0ZSwgeSA9IHJhdGlvX3B1YmxpY19pbnZlc3RtZW50KSkgKw0KICBnZW9tX2NvbCgpICsNCiAgbGFicyh4ID0gIkVzdGFkbyIsIHkgPSAiUHVibGljIEludmVzdG1lbnQgKCUpIiwgdGl0bGUgPSAiSGlzdG9ncmFtYSBkZSBQdWJsaWMgSW52ZXN0bWVudCAoJSkgcG9yIEVzdGFkbyIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gc3RhdGUsIHkgPSByYXRpb19wdWJsaWNfaW52ZXN0bWVudCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBsYWJzKHggPSAiRXN0YWRvIiwgeSA9ICJQdWJsaWMgSW52ZXN0bWVudCAoJSkiLCB0aXRsZSA9ICJCb3ggUGxvdCBkZSBQdWJsaWMgSW52ZXN0bWVudCAoJSkgcG9yIEVzdGFkbyIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQpTaSBiaWVuIG5vIGV4aXN0ZSBtdWNoYSB2YXJpYWJpbGlkYWQgZW4gbGEgbWF5b3LDrWEgZGUgbG9zIGVzdGFkb3MsIEJhamEgQ2FsaWZvcm5pYSwgSmFsaXNjbyB5IENpdWRhZCBkZSBNw6l4aWNvIGN1ZW50YW4gY29uIHVuIG1lbm9yIHJhdGlvIGVudHJlIGxhIGludmVyc2nDs24gcHVibGljYSBlc3RhdGFsIHkgZWwgUElCIG5hY2lvbmFsLiBQb3Igb3RybyBsYWRvLCBPYXhhY2EgY3VlbnRhIGNvbiBlbCBtYXlvciByYXRpbyBkZSBpbnZlcnNpw7NuIHDDumJsaWNhIGNvbiByZXNwZWN0byBhbCBQSUIgbmFjaW9uYWwuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIEhpc3RvZ3JhbWEgZGUgYnVzaW5lc3NfYWN0aXZpdHkNCm9wdGlvbnMocmVwci5wbG90LndpZHRoPTEyLCByZXByLnBsb3QuaGVpZ2h0PTgpDQoNCmdncGxvdChkYXRhLCBhZXMoeCA9IHN0YXRlLCB5ID0gYnVzaW5lc3NfYWN0aXZpdHkpKSArDQogIGdlb21fY29sKCkgKw0KICBsYWJzKHggPSAiRXN0YWRvIiwgeSA9ICJCdXNpbmVzcyBBY3Rpdml0eSIsIHRpdGxlID0gIkhpc3RvZ3JhbWEgZGUgQnVzaW5lc3MgQWN0aXZpdHkgcG9yIEVzdGFkbyIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gc3RhdGUsIHkgPSBidXNpbmVzc19hY3Rpdml0eSkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBsYWJzKHggPSAiRXN0YWRvIiwgeSA9ICJCdXNpbmVzcyBBY3Rpdml0eSIsIHRpdGxlID0gIkJveCBQbG90IGRlIEJ1c2luZXNzIEFjdGl2aXR5IHBvciBFc3RhZG8iKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KRWwgw7puaWNvIGVzdGFkbyBjb24gYWN0aXZpZGFkIGRlIG5lZ29jaW9zIHBvc2l0aXZhIGVzIEJhamEgQ2FsaWZvcm5pYS4gTG9zIG3DoXMgYmFqb3Mgc29uIFF1aW50YW5hIFJvbyB5IFl1Y2F0w6FuLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBIaXN0b2dyYW1hIGRlIGJ1c2luZXNzX2FjdGl2aXR5DQpvcHRpb25zKHJlcHIucGxvdC53aWR0aD0xMiwgcmVwci5wbG90LmhlaWdodD04KQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBzdGF0ZSwgeSA9IGdvb2RfZ292ZXJuYW5jZSkpICsNCiAgZ2VvbV9jb2woKSArDQogIGxhYnMoeCA9ICJFc3RhZG8iLCB5ID0gIlJhdGlvIEludmVzdG1lbnQvRGVidCIsIHRpdGxlID0gIkhpc3RvZ3JhbWEgZGUgUmF0aW8gSW52ZXN0bWVudC9EZWJ0IHBvciBFc3RhZG8iKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQoNCmdncGxvdChkYXRhLCBhZXMoeCA9IHN0YXRlLCB5ID0gZ29vZF9nb3Zlcm5hbmNlKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGxhYnMoeCA9ICJFc3RhZG8iLCB5ID0gIlJhdGlvIEludmVzdG1lbnQvRGVidCIsIHRpdGxlID0gIkJveCBQbG90IGRlIFJhdGlvIEludmVzdG1lbnQvRGVidCBwb3IgRXN0YWRvIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNClBvciBvdHJvIGxhZG9lbCBlc3RhZG8gY29uIG1heW9yIHJhdGlvIGVudHJlIGxhIGludmVyc2nDs24geSBkZXVkYSBww7pibGljYSBlcyBOdWV2byBMZcOzbiwgYWxjYW56YW5kbyBlbCB2YWxvciBkZSAyMDAuDQoNCkVzdGFzIHZhcmlhYmxlcyBjdWVudGFuIGNvbiBhbGfDum4gZXN0YWRvIGNvbW8gb3V0bGllciwgcm9tcGllbmRvIGVsIHBhdHLDs24gZGUgbG9zIHZhbG9yZXMgZGUgb3Ryb3MgZXN0YWRvcyB5IG1hcmNhbmRvIHVuYSBkaXNwZXJzacOzbiBtw6FzIGdyYW5kZS4gQXPDrSBtaXNtbywgbGFzIHZhcmlhYmxlcyAqKmV4Y2hhbmdlX3JhdGUqKiBlICoqaW5wYyoqIG5vIGN1ZW50YW4gY29uIHZhcmlhYmlsaWRhZCBnZW9lc3BhY2lhbCBhIGxvIGxhcmdvIGRlIGxvcyBhw7FvcywgcG9yIGxvIHF1ZSBubyBleGlzdGVuIHBhdHJvbmVzIHZpc2libGVzIHBhcmEgaW5jbHVpcmxvcyBlbiBlbCBhbsOhbGlzaXMgZXNwYWNpYWwgZGUgbG9zIGRhdG9zLg0KDQpQb3IgZXN0YSByYXrDs24gc2UgZWxpZ2lyw6FuIGxhcyBzaWd1aWVudGVzIHZhcmlhYmxlcyBwYXJhIG1lZGlyIGVsIGltcGFjdG8gZXNwYWNpYWw6DQoqICAgVG91cmlzbV9nZHANCiogICBDcmltZV9yYXRlDQoqICAgVW5lbXBsb3ltZW50DQoqICAgUmVhbF93YWdlDQoqICAgQ29sbGVnZV9lZHVjdGFpb24NCg0KIyMgKlZpc3VhbGl6YWNpw7NuIGRlIFZhcmlhYmxlcyBlbiBNYXBhcyoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm1leGljb19zdGF0ZXMgPC0gc3RfcmVhZCgnbWV4bGF0bG9uZy5zaHAnKQ0KDQojQ2FtYmlhciBEaXN0cml0byBGZWRlcmFsIGEgQ2l1ZGFkIGRlIE3DqXhpY28NCm1leGljb19zdGF0ZXMkQURNSU5fTkFNRSA8LSBnc3ViKCJEaXN0cml0byBGZWRlcmFsIiwgIkNpdWRhZCBkZSBNZXhpY28iLCBtZXhpY29fc3RhdGVzJEFETUlOX05BTUUpDQoNCiNDcmVhciBiYXNlIGRlIGRhdG9zIGNvbiBkYXRvcyBnZW9ncsOhZmljb3MNCnN0YXRlcyA8LSBtZXJnZShkYXRhLCBtZXhpY29fc3RhdGVzLCBieS54ID0gInN0YXRlIiwgYnkueSA9ICJBRE1JTl9OQU1FIikNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBDcmVhciBlbCBtYXBhIGJhc2UNCkdEUCA8LSBsZWFmbGV0KHN0YXRlcykgJT4lIGFkZFRpbGVzKCkNCg0KIyBBw7FhZGlyIGNhcGFzIHBhcmEgY2FkYSBhw7FvDQpmb3IgKHllYXIgaW4gdW5pcXVlKHN0YXRlcyR5ZWFyKSkgew0KICAjIFN1YnNldCB0aGUgZGF0YSBhbmQgZW5zdXJlIGl0IHJldGFpbnMgaXRzIHNwYXRpYWwgYXR0cmlidXRlcw0KICBzdGF0ZXNfc3Vic2V0IDwtIHN0YXRlc1tzdGF0ZXMkeWVhciA9PSB5ZWFyLCBdIA0KICBzdGF0ZXNfc3Vic2V0IDwtIHN0X2FzX3NmKHN0YXRlc19zdWJzZXQpDQogIA0KICBHRFAgPC0gR0RQICU+JQ0KICAgIGFkZFBvbHlnb25zKA0KICAgICAgZGF0YSA9IHN0YXRlc19zdWJzZXQsICAjIFVzZSB0aGUgc3Vic2V0IGhlcmUNCiAgICAgIGZpbGxDb2xvciA9IH5jb2xvck51bWVyaWMoIkduQnUiLCBkb21haW4gPSB0b3VyaXNtX2dkcCkodG91cmlzbV9nZHApLA0KICAgICAgZmlsbE9wYWNpdHkgPSAwLjcsDQogICAgICB3ZWlnaHQgPSAyLA0KICAgICAgY29sb3IgPSAiYmxhY2siLA0KICAgICAgZGFzaEFycmF5ID0gIjMiLA0KICAgICAgZ3JvdXAgPSBhcy5jaGFyYWN0ZXIoeWVhciksICMgQXNpZ25hciB1biBncnVwbyBwYXJhIGNhZGEgYcOxbw0KICAgICAgcG9wdXAgPSB+cGFzdGUoIkVzdGFkbzoiLCBzdGF0ZSwgIjxicj4iLCAiUElCIFR1cmlzbW86IiwgdG91cmlzbV9nZHApDQogICAgKQ0KfQ0KDQojIEHDsWFkaXIgY29udHJvbCBkZSBjYXBhcw0KR0RQIDwtIEdEUCAlPiUNCiAgYWRkTGF5ZXJzQ29udHJvbCgNCiAgICBiYXNlR3JvdXBzID0gdW5pcXVlKHN0YXRlcyR5ZWFyKSwgIyBVc2FyIGxvcyBhw7FvcyBjb21vIGdydXBvcyBiYXNlDQogICAgb3B0aW9ucyA9IGxheWVyc0NvbnRyb2xPcHRpb25zKGNvbGxhcHNlZCA9IEZBTFNFKSAjIE1vc3RyYXIgZWwgY29udHJvbCBleHBhbmRpZG8NCiAgKQ0KDQojIE1vc3RyYXIgZWwgbWFwYQ0KR0RQDQpgYGANCg0KQ2l1ZGFkIGRlIE3DqXhpY28gZXMgcXVpZW4gcG9zZWUgbcOhcyBQSUIgZGVsIFR1cmlzbW8sIHNpbiBlbWJhcmdvIFF1aW50YW5hIFJvbywgRXN0YWRvIGRlIE3DqXhpY28sIEphbGlzY28sIEd1YW5hanVhdG8geSBOdWV2byBMZcOzbiBoYW4gaW5jcmVtZW50YWRvIGVzdGUgdmFsb3IgZW4gbG9zIMO6bHRpbW9zIDE1IGHDsW9zLg0KDQoNCmBgYHtyfQ0KIyBDcmVhciBlbCBtYXBhIGJhc2UNCkNSSU0gPC0gbGVhZmxldChzdGF0ZXMpICU+JSBhZGRUaWxlcygpDQoNCiMgQcOxYWRpciBjYXBhcyBwYXJhIGNhZGEgYcOxbw0KZm9yICh5ZWFyIGluIHVuaXF1ZShzdGF0ZXMkeWVhcikpIHsNCiAgIyBTdWJzZXQgdGhlIGRhdGEgYW5kIGVuc3VyZSBpdCByZXRhaW5zIGl0cyBzcGF0aWFsIGF0dHJpYnV0ZXMNCiAgc3RhdGVzX3N1YnNldCA8LSBzdGF0ZXNbc3RhdGVzJHllYXIgPT0geWVhciwgXSANCiAgc3RhdGVzX3N1YnNldCA8LSBzdF9hc19zZihzdGF0ZXNfc3Vic2V0KQ0KICANCiAgQ1JJTSA8LSBDUklNICU+JQ0KICAgIGFkZFBvbHlnb25zKA0KICAgICAgZGF0YSA9IHN0YXRlc19zdWJzZXQsICAjIFVzZSB0aGUgc3Vic2V0IGhlcmUNCiAgICAgIGZpbGxDb2xvciA9IH5jb2xvck51bWVyaWMoIllsT3JSZCIsIGRvbWFpbiA9IGNyaW1lX3JhdGUpKGNyaW1lX3JhdGUpLA0KICAgICAgZmlsbE9wYWNpdHkgPSAwLjcsDQogICAgICB3ZWlnaHQgPSAyLA0KICAgICAgY29sb3IgPSAiYmxhY2siLA0KICAgICAgZGFzaEFycmF5ID0gIjMiLA0KICAgICAgZ3JvdXAgPSBhcy5jaGFyYWN0ZXIoeWVhciksICMgQXNpZ25hciB1biBncnVwbyBwYXJhIGNhZGEgYcOxbw0KICAgICAgcG9wdXAgPSB+cGFzdGUoIkVzdGFkbzoiLCBzdGF0ZSwgIjxicj4iLCAiVGFzYSBjcmltaW5hbGlkYWQgKCUpOiIsIGNyaW1lX3JhdGUpDQogICAgKQ0KfQ0KDQojIEHDsWFkaXIgY29udHJvbCBkZSBjYXBhcw0KQ1JJTSA8LSBDUklNICU+JQ0KICBhZGRMYXllcnNDb250cm9sKA0KICAgIGJhc2VHcm91cHMgPSB1bmlxdWUoc3RhdGVzJHllYXIpLCAjIFVzYXIgbG9zIGHDsW9zIGNvbW8gZ3J1cG9zIGJhc2UNCiAgICBvcHRpb25zID0gbGF5ZXJzQ29udHJvbE9wdGlvbnMoY29sbGFwc2VkID0gRkFMU0UpICMgTW9zdHJhciBlbCBjb250cm9sIGV4cGFuZGlkbw0KICApDQoNCiMgTW9zdHJhciBlbCBtYXBhDQpDUklNDQpgYGANCg0KTGEgdmFyaWFibGUgZGUgbGEgdGFzYSBkZSBjcmltaW5hbGlkYWQgaGEgdmFyaWFkbyBlbnRyZSBlc3RhZG9zIGRlbCAyMDA2IGFsIDIwMjIgc2llbmRvIEd1ZXJyZXJvLCBNaWNob2FjYW4sIENoaWh1YWh1YSwgQ29saW1hLCBCYWphIENhbGlmb3JuaWEsIEJhamEgQ2FsaWZvcm5pYSBTdXIsIEd1YW5hanVhdG8geSBaYWNhdGVjYXMgZXBpY2VudHJvcyBkZSB1bmEgZ3JhbiB0YXNhIGRlIGNyaW1pbmFsaWRhZCBwb3IgYXJyaWJhIGRlbCA3MCUgZW4gdW5vIG8gdmFyaW9zIGHDsW9zIGVudHJlIGVzdGUgcGVyaW9kby4NCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBDcmVhciBlbCBtYXBhIGJhc2UNCkVNUCA8LSBsZWFmbGV0KHN0YXRlcykgJT4lIGFkZFRpbGVzKCkNCg0KIyBBw7FhZGlyIGNhcGFzIHBhcmEgY2FkYSBhw7FvDQpmb3IgKHllYXIgaW4gdW5pcXVlKHN0YXRlcyR5ZWFyKSkgew0KICAjIFN1YnNldCB0aGUgZGF0YSBhbmQgZW5zdXJlIGl0IHJldGFpbnMgaXRzIHNwYXRpYWwgYXR0cmlidXRlcw0KICBzdGF0ZXNfc3Vic2V0IDwtIHN0YXRlc1tzdGF0ZXMkeWVhciA9PSB5ZWFyLCBdIA0KICBzdGF0ZXNfc3Vic2V0IDwtIHN0X2FzX3NmKHN0YXRlc19zdWJzZXQpDQogIA0KICBFTVAgPC0gRU1QICU+JQ0KICAgIGFkZFBvbHlnb25zKA0KICAgICAgZGF0YSA9IHN0YXRlc19zdWJzZXQsICAjIFVzZSB0aGUgc3Vic2V0IGhlcmUNCiAgICAgIGZpbGxDb2xvciA9IH5jb2xvck51bWVyaWMoIllsT3JSZCIsIGRvbWFpbiA9IHVuZW1wbG95bWVudCkodW5lbXBsb3ltZW50KSwNCiAgICAgIGZpbGxPcGFjaXR5ID0gMC43LA0KICAgICAgd2VpZ2h0ID0gMiwNCiAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgIGRhc2hBcnJheSA9ICIzIiwNCiAgICAgIGdyb3VwID0gYXMuY2hhcmFjdGVyKHllYXIpLCAjIEFzaWduYXIgdW4gZ3J1cG8gcGFyYSBjYWRhIGHDsW8NCiAgICAgIHBvcHVwID0gfnBhc3RlKCJFc3RhZG86Iiwgc3RhdGUsICI8YnI+IiwgIlRhc2EgZGUgRGVzZW1wbGVvOiIsIHVuZW1wbG95bWVudCkNCiAgICApDQp9DQoNCiMgQcOxYWRpciBjb250cm9sIGRlIGNhcGFzDQpFTVAgPC0gRU1QICU+JQ0KICBhZGRMYXllcnNDb250cm9sKA0KICAgIGJhc2VHcm91cHMgPSB1bmlxdWUoc3RhdGVzJHllYXIpLCAjIFVzYXIgbG9zIGHDsW9zIGNvbW8gZ3J1cG9zIGJhc2UNCiAgICBvcHRpb25zID0gbGF5ZXJzQ29udHJvbE9wdGlvbnMoY29sbGFwc2VkID0gRkFMU0UpICMgTW9zdHJhciBlbCBjb250cm9sIGV4cGFuZGlkbw0KICApDQoNCiMgTW9zdHJhciBlbCBtYXBhDQpFTVANCmBgYA0KDQpMYSB0YXNhIGRlIGRlc2VtcGxlbyBoYSBhZmVjdGFkbyBhIGxhIG1heW9yw61hIGRlIGxvcyBlc3RhZG9zIGRlIE3DqXhpY28gc2llbmRvIGxvcyBhw7FvcyAyMDEyLCAyMDE1LCAyMDE3IHkgMjAyMSBkb25kZSBtw6FzIGVzdGFkb3MgaGFuIHRlbmlkbyB0YXNhcyBtYXlvcmVzIGEgNSBwdW50b3MuIFBvciBvdHJvIGxhZG8gZW4gYcOxb3MgY29tbyAyMDIyLCAyMDE5LCAyMDE2IHkgMjAwOCwgc29sbyBhbGd1bm9zIGVzdGFkb3MgZGUgbGEgcmVnacOzbiBkZWwgbm9ydGUgeSBzdXIgY3VlbnRhbiBjb24gdW5hIGFsdGEgdGFzYSBkZSBkZXNlbXBsZW8sIHNpbiBlbWJhcmdvIGxhIG1heW9yaWEgc29uIG1lbm9yZXMgYSBsb3MgNSBwdW50b3MuDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgQ3JlYXIgZWwgbWFwYSBiYXNlDQpXQUdFIDwtIGxlYWZsZXQoc3RhdGVzKSAlPiUgYWRkVGlsZXMoKQ0KDQojIEHDsWFkaXIgY2FwYXMgcGFyYSBjYWRhIGHDsW8NCmZvciAoeWVhciBpbiB1bmlxdWUoc3RhdGVzJHllYXIpKSB7DQogICMgU3Vic2V0IHRoZSBkYXRhIGFuZCBlbnN1cmUgaXQgcmV0YWlucyBpdHMgc3BhdGlhbCBhdHRyaWJ1dGVzDQogIHN0YXRlc19zdWJzZXQgPC0gc3RhdGVzW3N0YXRlcyR5ZWFyID09IHllYXIsIF0gDQogIHN0YXRlc19zdWJzZXQgPC0gc3RfYXNfc2Yoc3RhdGVzX3N1YnNldCkNCiAgDQogIFdBR0UgPC0gV0FHRSAlPiUNCiAgICBhZGRQb2x5Z29ucygNCiAgICAgIGRhdGEgPSBzdGF0ZXNfc3Vic2V0LCAgIyBVc2UgdGhlIHN1YnNldCBoZXJlDQogICAgICBmaWxsQ29sb3IgPSB+Y29sb3JOdW1lcmljKCJHbkJ1IiwgZG9tYWluID0gcmVhbF93YWdlKShyZWFsX3dhZ2UpLA0KICAgICAgZmlsbE9wYWNpdHkgPSAwLjcsDQogICAgICB3ZWlnaHQgPSAyLA0KICAgICAgY29sb3IgPSAiYmxhY2siLA0KICAgICAgZGFzaEFycmF5ID0gIjMiLA0KICAgICAgZ3JvdXAgPSBhcy5jaGFyYWN0ZXIoeWVhciksICMgQXNpZ25hciB1biBncnVwbyBwYXJhIGNhZGEgYcOxbw0KICAgICAgcG9wdXAgPSB+cGFzdGUoIkVzdGFkbzoiLCBzdGF0ZSwgIjxicj4iLCAiU2FsYXJpbyBSZWFsOiIsIHJlYWxfd2FnZSkNCiAgICApDQp9DQoNCiMgQcOxYWRpciBjb250cm9sIGRlIGNhcGFzDQpXQUdFIDwtIFdBR0UgJT4lDQogIGFkZExheWVyc0NvbnRyb2woDQogICAgYmFzZUdyb3VwcyA9IHVuaXF1ZShzdGF0ZXMkeWVhciksICMgVXNhciBsb3MgYcOxb3MgY29tbyBncnVwb3MgYmFzZQ0KICAgIG9wdGlvbnMgPSBsYXllcnNDb250cm9sT3B0aW9ucyhjb2xsYXBzZWQgPSBGQUxTRSkgIyBNb3N0cmFyIGVsIGNvbnRyb2wgZXhwYW5kaWRvDQogICkNCg0KIyBNb3N0cmFyIGVsIG1hcGENCldBR0UNCmBgYA0KDQpMb3MgZXN0YWRvcyBjb24gbcOhcyBzYWxhcmlvIHJlYWwgZGUgbG9zIMO6bHRpbW9zIDUgYcOxb3Mgc2UgZW5jdWVudHJhbiBlbiBsYSB6b25hIG5vcm9lc3RlLCBzdXJvZXN0ZSB5IGVsIGNlbnRybyBkZWwgcGHDrXMsIHJlc2FsdGFuZG8gQ2FtcGVjaGUsIENpdWRhZCBkZSBNw6l4aWNvLCBOdWV2byBMZcOzbiB5IFF1ZXLDqXRhcm8uIE1pZW50cmFzIHF5ZSBsYSB6b25hIGNlbnRyby1lc3RlIHkgc3VyIHRpZW5lbiBsYXMgY29uY2VudHJhY2lvbmVzIG3DoXMgYmFqYXMgZW50b3JubyBhbCBzYWxhcmlvIHJlYWwNCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBDcmVhciBlbCBtYXBhIGJhc2UNCkVEVSA8LSBsZWFmbGV0KHN0YXRlcykgJT4lIGFkZFRpbGVzKCkNCg0KIyBBw7FhZGlyIGNhcGFzIHBhcmEgY2FkYSBhw7FvDQpmb3IgKHllYXIgaW4gdW5pcXVlKHN0YXRlcyR5ZWFyKSkgew0KICAjIFN1YnNldCB0aGUgZGF0YSBhbmQgZW5zdXJlIGl0IHJldGFpbnMgaXRzIHNwYXRpYWwgYXR0cmlidXRlcw0KICBzdGF0ZXNfc3Vic2V0IDwtIHN0YXRlc1tzdGF0ZXMkeWVhciA9PSB5ZWFyLCBdIA0KICBzdGF0ZXNfc3Vic2V0IDwtIHN0X2FzX3NmKHN0YXRlc19zdWJzZXQpDQogIA0KICBFRFUgPC0gRURVICU+JQ0KICAgIGFkZFBvbHlnb25zKA0KICAgICAgZGF0YSA9IHN0YXRlc19zdWJzZXQsICAjIFVzZSB0aGUgc3Vic2V0IGhlcmUNCiAgICAgIGZpbGxDb2xvciA9IH5jb2xvck51bWVyaWMoIkduQnUiLCBkb21haW4gPSBjb2xsZWdlX2VkdWNhdGlvbikoY29sbGVnZV9lZHVjYXRpb24pLA0KICAgICAgZmlsbE9wYWNpdHkgPSAwLjcsDQogICAgICB3ZWlnaHQgPSAyLA0KICAgICAgY29sb3IgPSAiYmxhY2siLA0KICAgICAgZGFzaEFycmF5ID0gIjMiLA0KICAgICAgZ3JvdXAgPSBhcy5jaGFyYWN0ZXIoeWVhciksICMgQXNpZ25hciB1biBncnVwbyBwYXJhIGNhZGEgYcOxbw0KICAgICAgcG9wdXAgPSB+cGFzdGUoIkVzdGFkbzoiLCBzdGF0ZSwgIjxicj4iLCAiRWR1Y2FjacOzbiBBdmFuemFkYSAoJSk6IiwgY29sbGVnZV9lZHVjYXRpb24sDQogICAgICAgICAgICAgICAgICAgICAiPGJyPiIsICJEZW5zaWRhZCBQb2JsYWNpb25hbDoiLCBwb3BfZGVuc2l0eSkNCiAgICApDQp9DQoNCiMgQcOxYWRpciBjb250cm9sIGRlIGNhcGFzDQpFRFUgPC0gRURVICU+JQ0KICBhZGRMYXllcnNDb250cm9sKA0KICAgIGJhc2VHcm91cHMgPSB1bmlxdWUoc3RhdGVzJHllYXIpLCAjIFVzYXIgbG9zIGHDsW9zIGNvbW8gZ3J1cG9zIGJhc2UNCiAgICBvcHRpb25zID0gbGF5ZXJzQ29udHJvbE9wdGlvbnMoY29sbGFwc2VkID0gRkFMU0UpICMgTW9zdHJhciBlbCBjb250cm9sIGV4cGFuZGlkbw0KICApDQoNCiMgTW9zdHJhciBlbCBtYXBhDQpFRFUNCmBgYA0KDQoNCkVuIHTDqXJtaW5vcyBnZW5lcmFsZXMsIGxhIG1heW9yw61hIGRlIGxvcyBlc3RhZG9zIGhhIHRlbmlkbyB1biBpbmNyZW1lbnRvIGVuIGxhIHRhc2EgZGUgZWR1Y2FjacOzbiBhdmFuemFkYSBkZSBzdXMgaGFiaXRhbnRlcyBzaWVuZG8gbXV5IGFsdG8gZW4gbGEgem9uYSBub3Jlc3RlLCBub3JvZXN0ZSwgZWwgY2VudHJvIGRlIG3DqXhpY28geSBlbCBzdXJvZXN0ZSBkZWwgcGHDrXMuIE1pZW50cmFzIHF1ZSBlbiBsYSB6b25hIHN1ciB5IGVsIGNlbnRyby1ub3J0ZSBjdWVudGFuIGNvbiBsYXMgbWVub3JlcyB0YXNhcyBkZSBlZHVjYWNpw7NuIGF2YW56YWRhLg0KDQojIyAqTWF0cmljZXMgZGUgQ29uZWN0aXZpZGFkIFJvb2sgeSBRdWVlbioNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgTG9hZCB0aGUgZGF0YXNldHMNCm1leGxhdGxvbmcgPC0gc3RfcmVhZCgibWV4bGF0bG9uZy5zaHAiKSAgIyBSZWFkIHNoYXBlZmlsZQ0KZGF0YSA8LSByZWFkX2V4Y2VsKCJ0b3VyaXNtX3N0YXRlX2RhdGEueGxzeCIpICAjIFJlYWQgRXhjZWwgZmlsZQ0KDQojIE1lcmdlIHRoZSBzcGF0aWFsIGRhdGEgd2l0aCB0aGUgYXR0cmlidXRlIGRhdGEgYmFzZWQgb24gdGhlIHN0YXRlIGNvbHVtbg0Kc3RhdGVzIDwtIG1leGxhdGxvbmcgJT4lDQogIGxlZnRfam9pbihkYXRhLCBieSA9IGMoIkFETUlOX05BTUUiID0gInN0YXRlIikpICAjIEVuc3VyZSBjb2x1bW4gbmFtZXMgbWF0Y2gNCg0KIyBEZWZpbmUgdGhlIHZhcmlhYmxlcyB0byBhbmFseXplDQojdmFyaWFibGVzIDwtIGMoInRvdXJpc21fZ2RwIiwgImNyaW1lX3JhdGUiLCAiY29sbGVnZV9lZHVjYXRpb24iLA0KIyAgICAgICAgICAgICAgICJ1bmVtcGxveW1lbnQiLCAicmVhbF93YWdlIikNCg0KdmFyaWFibGVzIDwtIGMoInRvdXJpc21fZ2RwIiwgImNyaW1lX3JhdGUiLCAiY29sbGVnZV9lZHVjYXRpb24iLA0KICAgICAgICAgICAgICAgInVuZW1wbG95bWVudCIsICJlbXBsb3ltZW50IiwgImJ1c2luZXNzX2FjdGl2aXR5IiwNCiAgICAgICAgICAgICAgICJyZWFsX3dhZ2UiLCAicG9wX2RlbnNpdHkiLCAiZ29vZF9nb3Zlcm5hbmNlIiwNCiAgICAgICAgICAgICAgICJyYXRpb19wdWJsaWNfaW52ZXN0bWVudCIpDQoNCiMgQ29udmVydGlyICdzdGF0ZXMnIGEgdW4gb2JqZXRvIGVzcGFjaWFsDQpkYXRhX3NwIDwtIG5hLm9taXQoc3RhdGVzWywgYyh2YXJpYWJsZXMsICJnZW9tZXRyeSIpXSkNCg0KbWV4bGF0bG9uZ19zcCA8LSBhcyhkYXRhX3NwLCAiU3BhdGlhbCIpDQpgYGANCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBDcmVhciBsYSBtYXRyaXogZGUgdmVjaW5kYWQgUXVlZW4NCm5iX3F1ZWVuIDwtIHBvbHkybmIobWV4bGF0bG9uZ19zcCwgcXVlZW4gPSBUUlVFKQ0KDQojIENyZWFyIGxhIG1hdHJpeiBkZSBwZXNvcyBlc3BhY2lhbGVzDQpsd19xdWVlbiA8LSBuYjJsaXN0dyhuYl9xdWVlbiwgc3R5bGUgPSAiVyIsIHplcm8ucG9saWN5ID0gVFJVRSkNCg0KIyBPYnRlbmVyIGxhcyBjb29yZGVuYWRhcyBkZSBsb3MgY2VudHJvaWRlcw0KY2VudHJvaWRzIDwtIGNvb3JkaW5hdGVzKG1leGxhdGxvbmdfc3ApDQoNCnBsb3QobWV4bGF0bG9uZ19zcCxib3JkZXI9ImJsdWUiLGF4ZXM9RkFMU0UsbGFzPTEsIG1haW49Ik1leGljbyBTcGF0aWFsIENvbm5lY3Rpdml0eSBNYXRyaXggLSBRdWVlbiIpDQpwbG90KG1leGxhdGxvbmdfc3AsY29sPSJncmV5Iixib3JkZXI9Z3JleSgwLjkpLGF4ZXM9VCxhZGQ9VCkgDQpwbG90KGx3X3F1ZWVuLCBjZW50cm9pZHMsIGFkZD1UUlVFLCBjb2w9J2RhcmsgZ3JlZW4nKQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIENyZWFyIGxhIG1hdHJpeiBkZSB2ZWNpbmRhZCBSb29rDQpuYl9yb29rIDwtIHBvbHkybmIobWV4bGF0bG9uZ19zcCwgcXVlZW4gPSBGQUxTRSkNCg0KIyBDcmVhciBsYSBtYXRyaXogZGUgcGVzb3MgZXNwYWNpYWxlcw0KbHdfcm9va19zdWJzZXQgPC0gbmIybGlzdHcobmJfcm9vaywgc3R5bGUgPSAiVyIsIHplcm8ucG9saWN5ID0gVFJVRSkNCg0KcGxvdChtZXhsYXRsb25nX3NwLGJvcmRlcj0icmVkIixheGVzPUZBTFNFLGxhcz0xLCBtYWluPSJNZXhpY28gU3BhdGlhbCBDb25uZWN0aXZpdHkgTWF0cml4IC0gUm9vayIpDQpwbG90KG1leGxhdGxvbmdfc3AsY29sPSJncmV5Iixib3JkZXI9Z3JleSgwLjkpLGF4ZXM9VCxhZGQ9VCkgDQpwbG90KGx3X3F1ZWVuLCBjZW50cm9pZHMsIGFkZD1UUlVFLCBjb2w9J3JlZCcpDQpgYGANCg0KDQojIyAqR2xvYmFsIE1vcmFu4oCZcyBJIFN0YXRpc3RpYyAtIEF1dG9jb3JyZWxhY2nDs24gR2xvYmFsIHkgTG9jYWwqDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgQ29udmVydCB2YXJpYWJsZXMgdG8gbnVtZXJpYyB0byBhdm9pZCBlcnJvcnMNCmZvciAodmFyIGluIHZhcmlhYmxlcykgew0KICBzdGF0ZXNbW3Zhcl1dIDwtIGFzLm51bWVyaWMoc3RhdGVzW1t2YXJdXSkNCn0NCg0KIyBMb29wIHRocm91Z2ggZWFjaCB2YXJpYWJsZSBmb3IgR2xvYmFsICYgTG9jYWwgTW9yYW4ncyBJDQpmb3IgKHZhciBpbiB2YXJpYWJsZXMpIHsNCiAgY2F0KCJcbi0tLSBHbG9iYWwgTW9yYW4ncyBJIGZvciIsIHZhciwgIi0tLVxuIikNCg0KICAjIENoZWNrIGZvciBtaXNzaW5nIHZhbHVlcyBhbmQgcmVtb3ZlIHRoZW0gZm9yIHRoaXMgdmFyaWFibGUNCiAgZGF0YV9mb3JfbW9yYW4gPC0gbmEub21pdChzdGF0ZXNbLCBjKHZhciwgImdlb21ldHJ5IildKQ0KDQogIGlmIChucm93KGRhdGFfZm9yX21vcmFuKSA+IDEpIHsgIyBFbnN1cmUgZW5vdWdoIGRhdGEgYWZ0ZXIgcmVtb3ZpbmcgTkFzDQoNCiAgICAjIENvbnZlcnQgdGhlIHN1YnNldCB0byBhIHNwYXRpYWwgb2JqZWN0DQogICAgbWV4bGF0bG9uZ19zcCA8LSBhcyhkYXRhX2Zvcl9tb3JhbiwgIlNwYXRpYWwiKQ0KDQogICAgIyBSZWNhbGN1bGF0ZSBuZWlnaGJvcnMgYW5kIHNwYXRpYWwgd2VpZ2h0cyBmb3IgdGhlIHN1YnNldCBkYXRhDQogICAgIyBSb29rIGNvbnRpZ3VpdHkNCiAgICBuYl9yb29rIDwtIHBvbHkybmIobWV4bGF0bG9uZ19zcCwgcXVlZW4gPSBGQUxTRSkNCiAgICBsd19yb29rX3N1YnNldCA8LSBuYjJsaXN0dyhuYl9yb29rLCBzdHlsZSA9ICJXIiwgemVyby5wb2xpY3kgPSBUUlVFKQ0KDQogICAgIyBRdWVlbiBjb250aWd1aXR5DQogICAgbmJfcXVlZW4gPC0gcG9seTJuYihtZXhsYXRsb25nX3NwLCBxdWVlbiA9IFRSVUUpDQogICAgbHdfcXVlZW5fc3Vic2V0IDwtIG5iMmxpc3R3KG5iX3F1ZWVuLCBzdHlsZSA9ICJXIiwgemVyby5wb2xpY3kgPSBUUlVFKQ0KDQogICAgIyBHbG9iYWwgTW9yYW7igJlzIEkgLSBVc2UgdGhlIFJvb2sgY29udGlndWl0eSB3ZWlnaHRzDQogICAgZ2xvYmFsX21vcmFuX3Jvb2sgPC0gbW9yYW4udGVzdChkYXRhX2Zvcl9tb3JhbltbdmFyXV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0dyA9IGx3X3Jvb2tfc3Vic2V0LCB6ZXJvLnBvbGljeSA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5hY3Rpb24gPSBuYS5vbWl0KQ0KICAgIGNhdCgiXG4tLS0gR2xvYmFsIE1vcmFuJ3MgSSB3aXRoIFJvb2sgY29udGlndWl0eSAtLS1cbiIpDQogICAgcHJpbnQoZ2xvYmFsX21vcmFuX3Jvb2spDQoNCiAgICAjIEdsb2JhbCBNb3JhbuKAmXMgSSAtIFVzZSB0aGUgUXVlZW4gY29udGlndWl0eSB3ZWlnaHRzDQogICAgZ2xvYmFsX21vcmFuX3F1ZWVuIDwtIG1vcmFuLnRlc3QoZGF0YV9mb3JfbW9yYW5bW3Zhcl1dLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3R3ID0gbHdfcXVlZW5fc3Vic2V0LCB6ZXJvLnBvbGljeSA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEuYWN0aW9uID0gbmEub21pdCkNCiAgICBjYXQoIlxuLS0tIEdsb2JhbCBNb3JhbidzIEkgd2l0aCBRdWVlbiBjb250aWd1aXR5IC0tLVxuIikNCiAgICBwcmludChnbG9iYWxfbW9yYW5fcXVlZW4pDQoNCiAgICAjIE1vcmFuIFNjYXR0ZXJwbG90IC0gUm9vayBjb250aWd1aXR5DQogICAgbW9yYW4ucGxvdChkYXRhX2Zvcl9tb3JhbltbdmFyXV0sIGx3X3Jvb2tfc3Vic2V0LCBtYWluID0gcGFzdGUoIk1vcmFu4oCZcyBJIChSb29rKSAtIiwgdmFyKSkNCg0KICAgICMgTW9yYW4gU2NhdHRlcnBsb3QgLSBRdWVlbiBjb250aWd1aXR5DQogICAgbW9yYW4ucGxvdChkYXRhX2Zvcl9tb3JhbltbdmFyXV0sIGx3X3F1ZWVuX3N1YnNldCwgbWFpbiA9IHBhc3RlKCJNb3JhbuKAmXMgSSAoUXVlZW4pIC0iLCB2YXIpKQ0KDQogICAgIyBMb2NhbCBNb3JhbuKAmXMgSSAtIFVzZSB0aGUgUm9vayBjb250aWd1aXR5IHdlaWdodHMNCiAgICBsb2NhbF9tb3Jhbl9yZXNfcm9vayA8LSBsb2NhbG1vcmFuKGRhdGFfZm9yX21vcmFuW1t2YXJdXSwgbHdfcm9va19zdWJzZXQsIHplcm8ucG9saWN5ID0gVFJVRSkNCg0KICAgICMgTG9jYWwgTW9yYW7igJlzIEkgLSBVc2UgdGhlIFF1ZWVuIGNvbnRpZ3VpdHkgd2VpZ2h0cw0KICAgIGxvY2FsX21vcmFuX3Jlc19xdWVlbiA8LSBsb2NhbG1vcmFuKGRhdGFfZm9yX21vcmFuW1t2YXJdXSwgbHdfcXVlZW5fc3Vic2V0LCB6ZXJvLnBvbGljeSA9IFRSVUUpDQoNCiAgICAjIEFwcGVuZCBMb2NhbCBNb3JhbuKAmXMgSSB2YWx1ZXMgYW5kIHAtdmFsdWVzIHRvIHRoZSBkYXRhc2V0IGZvciBib3RoIFJvb2sgYW5kIFF1ZWVuDQogICAgcHZhbF9uYW1lX3Jvb2sgPC0gcGFzdGUwKCJwdmFsXyIsIHZhciwgIl9yb29rIikNCiAgICBjbHVzdGVyX25hbWVfcm9vayA8LSBwYXN0ZTAoImNsdXN0ZXJfIiwgdmFyLCAiX3Jvb2siKQ0KICAgIHB2YWxfbmFtZV9xdWVlbiA8LSBwYXN0ZTAoInB2YWxfIiwgdmFyLCAiX3F1ZWVuIikNCiAgICBjbHVzdGVyX25hbWVfcXVlZW4gPC0gcGFzdGUwKCJjbHVzdGVyXyIsIHZhciwgIl9xdWVlbiIpDQoNCiAgICBzdGF0ZXNbW3B2YWxfbmFtZV9yb29rXV0gPC0gTkEgICMgSW5pdGlhbGl6ZSBhcyBOQQ0KICAgIHN0YXRlc1tbY2x1c3Rlcl9uYW1lX3Jvb2tdXSA8LSAiTm90IFNpZ25pZmljYW50IiAgIyBEZWZhdWx0IHZhbHVlDQogICAgc3RhdGVzW1twdmFsX25hbWVfcXVlZW5dXSA8LSBOQSAgIyBJbml0aWFsaXplIGFzIE5BDQogICAgc3RhdGVzW1tjbHVzdGVyX25hbWVfcXVlZW5dXSA8LSAiTm90IFNpZ25pZmljYW50IiAgIyBEZWZhdWx0IHZhbHVlDQoNCiAgICAjIFRyYW5zZmVyIExvY2FsIE1vcmFuJ3MgcmVzdWx0cyBmb3IgUm9vayBhbmQgUXVlZW4gaW50byB0aGUgbWVyZ2VkX2RhdGENCiAgICBzdGF0ZXNbcm93bmFtZXMobG9jYWxfbW9yYW5fcmVzX3Jvb2spLCBwdmFsX25hbWVfcm9va10gPC0gbG9jYWxfbW9yYW5fcmVzX3Jvb2tbLCA1XQ0KICAgIHN0YXRlc1tyb3duYW1lcyhsb2NhbF9tb3Jhbl9yZXNfcm9vayksIGNsdXN0ZXJfbmFtZV9yb29rXSA8LSBpZmVsc2UobG9jYWxfbW9yYW5fcmVzX3Jvb2tbLCA1XSA8IDAuMDUsICJTaWduaWZpY2FudCIsICJOb3QgU2lnbmlmaWNhbnQiKQ0KDQogICAgc3RhdGVzW3Jvd25hbWVzKGxvY2FsX21vcmFuX3Jlc19xdWVlbiksIHB2YWxfbmFtZV9xdWVlbl0gPC0gbG9jYWxfbW9yYW5fcmVzX3F1ZWVuWywgNV0NCiAgICBzdGF0ZXNbcm93bmFtZXMobG9jYWxfbW9yYW5fcmVzX3F1ZWVuKSwgY2x1c3Rlcl9uYW1lX3F1ZWVuXSA8LSBpZmVsc2UobG9jYWxfbW9yYW5fcmVzX3F1ZWVuWywgNV0gPCAwLjA1LCAiU2lnbmlmaWNhbnQiLCAiTm90IFNpZ25pZmljYW50IikNCg0KICAgICMgQ29udmVydCB0byBmYWN0b3INCiAgICBzdGF0ZXNbW2NsdXN0ZXJfbmFtZV9yb29rXV0gPC0gYXMuZmFjdG9yKHN0YXRlc1tbY2x1c3Rlcl9uYW1lX3Jvb2tdXSkNCiAgICBzdGF0ZXNbW2NsdXN0ZXJfbmFtZV9xdWVlbl1dIDwtIGFzLmZhY3RvcihzdGF0ZXNbW2NsdXN0ZXJfbmFtZV9xdWVlbl1dKQ0KDQogICAgIyBMb2NhbCBjbHVzdGVyIG1hcCB1c2luZyBnZ3Bsb3QgZm9yIFJvb2sgY29udGlndWl0eQ0KICAgIHByaW50KA0KICAgICAgZ2dwbG90KHN0YXRlcykgKw0KICAgICAgICBnZW9tX3NmKGFlcyhmaWxsID0gISFzeW0oY2x1c3Rlcl9uYW1lX3Jvb2spKSkgKw0KICAgICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJTaWduaWZpY2FudCIgPSAicmVkIiwgIk5vdCBTaWduaWZpY2FudCIgPSAiZ3JleSIpKSArDQogICAgICAgIGxhYnModGl0bGUgPSBwYXN0ZSgiTG9jYWwgU3BhdGlhbCBBdXRvY29ycmVsYXRpb24gLSIsIHZhciwgIihSb29rKSIpLCBmaWxsID0gIkNsdXN0ZXIiKQ0KICAgICkNCg0KICAgICMgTG9jYWwgY2x1c3RlciBtYXAgdXNpbmcgZ2dwbG90IGZvciBRdWVlbiBjb250aWd1aXR5DQogICAgcHJpbnQoDQogICAgICBnZ3Bsb3Qoc3RhdGVzKSArDQogICAgICAgIGdlb21fc2YoYWVzKGZpbGwgPSAhIXN5bShjbHVzdGVyX25hbWVfcXVlZW4pKSkgKw0KICAgICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJTaWduaWZpY2FudCIgPSAicmVkIiwgIk5vdCBTaWduaWZpY2FudCIgPSAiZ3JleSIpKSArDQogICAgICAgIGxhYnModGl0bGUgPSBwYXN0ZSgiTG9jYWwgU3BhdGlhbCBBdXRvY29ycmVsYXRpb24gLSIsIHZhciwgIihRdWVlbikiKSwgZmlsbCA9ICJDbHVzdGVyIikNCiAgICApDQogIH0gZWxzZSB7DQogICAgY2F0KCJTa2lwcGluZyIsIHZhciwgImR1ZSB0byBtaXNzaW5nIG9yIGludmFsaWQgZGF0YS5cbiIpDQogIH0NCn0NCg0KYGBgDQoNCg0KVHJhcyBhbmFsaXphciBsYXMgdmFyaWFibGVzIGRlIGxhIGJhc2UgZGUgZGF0b3MsIHNlIGRldGVybWluw7MgcXVlIHRvZGFzIGxhcyB2YXJpYWJsZXMgc29uIHNpZ25pZmljYXRpdmFtZW50ZSBlc3RhZMOtc3RpY2FzLCBzaW4gZW1iYXJnbyBsYSBtYXlvcsOtYSBjdWVudGEgY29uIHVuIMONbmRpY2UgZGUgR2xvYmFsIE1vcmFuIG11eSBjZXJjYW5vIGEgMCwgaW5kaWNhbmRvIG3DrW5pbWEgYXV0b2NvcnJlbGFjacOzbiBlc3BhY2lhbCBkZSBsb3MgZGF0b3Mgc2llbmRvIGxhIHZhcmlhYmxlIGRlICplbXBsb3ltZW50KiwgKnJhdGlvX3B1YmxpY19pbnZlc3RtZW50KiwgKmJ1c2luZXNzX2FjdGl2aXR5KiBsYSBkZSBtYXlvciBpbmRpY2UgZW4gYXV0b2NvcnJlbGFjacOzbiBlc3BhY2lhbC4NCg0KRW4gY3Vlc3Rpw7NuIGRlIGxhIGF1dG9jb3JyZWxhY2nDs24gbG9jYWwgcmVwcmVzZW50YWRhIHBvciBsb3MgbWFwYXMgZGUgc2lnbmlmaWNhbmNpYSwgdmVtb3MgY29tbyB2YXJpYWJsZXMgY29tbyAqdG91cmlzbV9nZHAqIHkgKmVtcGxveW1lbnQqIGNvbnNpZGVyYW4gdGFudG8gbGEgcmVnacOzbiBub3J0ZSwgY2VudHJvIHkgc3VyIHNpZ25pZmljYXRpdmEgZGVqYW5kbyBsb3MgZXN0YWRvcyBtw6FzIGFsIG9lc3RlIHkgZXN0ZSBmdWVyYSBkZSBlc3RvcyBjbHVzdGVycy4gIA0KDQpQb3Igb3RybyBsYWRvLCAqcG9wX2RlbnNpdHkqLCAqYnVzaW5lc3NfYWN0aXZpdHkqLCB5ICpjcmltZV9yYXRlKiBjb25zaWRlcmFuIGEgY2FzaSBlbCA4MCUgZGUgbG9zIGVzdGFkb3MgZGUgbGEgcmVwdWJsaWNhIG1leGljYW5hIGNvbW8gc2lnbmlmaWNhdGl2b3MuIENhYmUgYWNsYXJhciBxdWUgZXN0ZSBwYXRyw7NuIHRhbWJpZW4gc2UgcHVlZGUgcmV2aXNhciBjb250cmFyaWFtZW50ZSBkb25kZSAqZ29vZF9nb3Zlcm5hbmNlKiB5ICpyYXRpb19wdWJsaWNfaW52ZXN0bWVudGUqIGNvbnNpZGVyYW4gbWVub3MgZGVsIDMwJSBkZSBsb3MgZXN0YWRvcyBjb21vIHNpZ25pZmljYXRpdm9zOyBlc3RhbmRvIG11eSBzZWdyZWdhZG9zIHBhcmEgZm9ybWFyIHZhcmlvcyBjbHVzdGVycyBwZXF1ZcOxb3MuDQoNCg0KDQojICoqQ29uY2x1c2lvbmVzIHkgSGFsbGF6Z29zKioNCg0KMS4JKipQcmVzZW5jaWEgZGUgYXV0b2NvcnJlbGFjacOzbiBlc3BhY2lhbDoqKg0KRW5jb250cmFtb3MgcXVlIGhheSBhdXRvY29ycmVsYWNpw7NuIGVzcGFjaWFsIHNpZ25pZmljYXRpdmEgZW4gdG9kYXMgbGFzIHZhcmlhYmxlcyBhbmFsaXphZGFzIChwLXZhbG9yIDwgMC4wNSksIGxvIHF1ZSBpbmRpY2EgcXVlIGxvcyB2YWxvcmVzIGVuIHVuIGVzdGFkbyBzaSBsbGVnYW4gYSBlc3RhciBpbmZsdWVuY2lhZG9zIHBvciBsb3MgZXN0YWRvcyB2ZWNpbm9zIHRhbnRvIGVuIGxhIG1hdHJpeiBkZSBjb25lY3RpdmlkYWQgcm9vayBjb21vIHF1ZWVuLg0KDQoyLgkqKlZhbG9yIGRlbCDDjW5kaWNlIGRlIEdsb2JhbCBNb3JhbidzIEkgZW4gZ2VuZXJhbDoqKg0KQXVucXVlIGxvcyDDrW5kaWNlcyBmdWVyb24gc2lnbmlmaWNhdGl2b3MsIGhhYsOtYW4gdmFyaWFibGVzIG11eSBiYWphcyBjb21vICp0dXJpc21vX2dkcCogY3V5YSBjb3JyZWxhY2nDs24gZXNwYWNpYWwgZXJhIGTDqWJpbCBtaWVudHJhcyBxdWUgdmFyaWFibGVzIGNvbW8gKmVtcGxveW1lbnQqIHkgKnJhdGlvX3B1YmxpY19pbnZlc3RtZW50KiBlbiBtYXRyaXogcm9vayB5ICpidXNpbmVzc19hY3Rpdml0eSogZW4gbWF0cml6IHF1ZWVuIG1vc3RyYXJvbiB1bmEgYXV0b2NvcnJlbGFjacOzbiBlc3BhY2lhbCBtw6FzIGZ1ZXJ0ZSwgYXVucXVlIHNpbiBzdXBlcmFyIGVsIHZhbG9yIGRlIDAuNSBkZSBHbG9iYWwgTW9yYW4ncw0KDQozLgkqKlR1cmlzbW8gKHRvdXJpc21fZ2RwKToqKg0KTGEgQ2l1ZGFkIGRlIE3DqXhpY28gZGVzdGFjYSBwb3IgdGVuZXIgY29uc2lzdGVudGVtZW50ZSBlbCBtYXlvciBQSUIgdHVyw61zdGljbywgbWllbnRyYXMgcXVlIGVzdGFkb3MgY29tbyBKYWxpc2NvLCBOdWV2byBMZcOzbiwgR3VhbmFqdWF0byB5IFF1aW50YW5hIFJvbyBoYW4gbW9zdHJhZG8gdW4gY3JlY2ltaWVudG8gaW1wb3J0YW50ZSBhIGxvIGxhcmdvIGRlIGxvcyBhw7Fvcy4gRXN0byBzZSBkZW11ZXN0cmEgZW4gbGEgc2lnbmlmaWNhY2lhIGRlIGxvcyBjbHVzdGVycywgcHVlcyBlbCAqKmNlbnRybyoqIHkgKipub3J0ZSoqIGRlbCBwYWlzIGZ1ZXJvbiBpZGVudGlmaWNhZG9zIGNvbW8gcmVnaW9uZXMgc2lnbmlmaWNhdGl2YXMgcGFyYSBsYSBlbCBhbsOhbGlzaXMgZGUgYXV0b2NvcnJlbGFjacOzbiBlc3BhY2lhbC4NCg0KNC4JKipFZHVjYWNpw7NuIGF2YW56YWRhIChjb2xsZWdlX2VkdWNhdGlvbik6KioNCkVuIGN1YW50byBhIGxhIGVkdWNhY2nDs24sIGxhIHByb3BvcmNpw7NuIGRlIGVkdWNhY2nDs24gc3VwZXJpb3IgZXN0w6FuIGVuIGVsICoqbm9ydGUgeSBjZW50cm8qKiBkZWwgcGHDrXMgKENETVgsIE5MLCBRUk8pLiBFbiBjb250cmFzdGUsIGVzdGFkb3MgZGVsIHN1ciAoQ2hpYXBhcywgT2F4YWNhLCBHdWVycmVybykgZm9ybWFucm9uIGxvcyBjbHVzdGVycyBkZSBiYWpvIG5pdmVsIGVkdWNhdGl2byBwb3IgbG8gcXVlIHZhbGRyw61hIGxhIHBlbmEgcmVhbGl6YXIgdW4gYW7DoWxpc2lzIGEgcHJvZnVuZGlkYWQgZGUgbGFzIHBvc2libGVzIGNhdXNhcyBkZSBlc3RlIG5pdmVsLiBFbiBjdWVzdGnDs24gZGUgYXV0b2NvcnJlbGFjacOzbiBsb2NhbCwgdGFudG8gbGEgcGFydGUgbm9yb2VzdGUsIGNlbnRybyB5IHN1cm9lc3RlIGZ1ZXJvbiBzaWduaWZpY2F0aXZhcy4NCg0KNS4JKipTYWxhcmlvIHJlYWwgKHJlYWxfd2FnZSk6KioNCkVsICoqY2VudHJvIHkgbm9yb2VzdGUqKiBkZWwgcGHDrXMgY29uY2VudHJhbiBsb3Mgc2FsYXJpb3MgbcOhcyBhbHRvcywgZGVzdGFjYW5kbyBDRE1YIHkgQ2FtcGVjaGUuIEhheSB1bmEgYXV0b2NvcnJlbGFjacOzbiBtb2RlcmFkYSBlbnRyZSBlc3RhZG9zIHZlY2lub3MgY29uIG5pdmVsZXMgc2ltaWxhcmVzIGRlIHNhbGFyaW8gcmVhbCBwb3NpYmxlbWVudGUgcG9yIHTDqXJtaW5vcyBkZSBpbnZlcnNpw7NuIGV4dHJhbmplcmEgbyBjYW50aWRhZCBkZSBlbXBsZW9zLiANCg0KNi4JKipHb2Jlcm5hbnphIChnb29kX2dvdmVybmFuY2UpOioqDQpTaSBiaWVuIE51ZXZvIExlw7NuIGRlc3RhY2EgY29uIHVuIHZhbG9yIGV4dHJlbWFkYW1lbnRlIGFsdG8sIGVzdG8gbm8gZXMgbWFzIHF1ZSB1biBvdXRsaWVyLiBBIHBlc2FyIGRlIGxhIHNpZ25pZmljYW5jaWEgZXN0YWTDrXN0aWNhLCBsYSBhdXRvY29ycmVsYWNpw7NuIGVzIGJhamEsIHN1Z2lyaWVuZG8gcXVlIGxhIGdvYmVybmFuemEgZXMgbcOhcyB1biBmZW7Ds21lbm8gbG9jYWwgeSBubyBlcyBpbmZsdWVuY2lhZG8gcG9yIGxvcyBkZW1hcyBlc3RhZG9zLg0KDQo3LgkqKkRlbnNpZGFkIHBvYmxhY2lvbmFsIChwb3BfZGVuc2l0eSk6KioNCkVzdGEgdmFyaWFibGUgbW9zdHLDsyBsYSBtw6FzIGFsdGEgYXV0b2NvcnJlbGFjacOzbiBlc3BhY2lhbC4gTG9zICoqZXN0YWRvcyBtw6FzIGRlbnNhbWVudGUgcG9ibGFkb3MqKiAoQ0RNWCwgRURPTUVYKSBlc3TDoW4gYWdydXBhZG9zLCB5IGxvIG1pc21vIG9jdXJyZSBjb24gbG9zIG1lbm9zIGRlbnNvcyAoQkNTLCBEdXJhbmdvKSwgcG9kcsOtYSBzZXIgaW50ZXJlc2FudGUgc2kgZXN0w6EgcmVsYWNpb25hZGEgY29uIGVsIHRhbWHDsW8gZGVsIHRlcnJlbm8gLg0KDQo4LgkqKkludmVyc2nDs24gcMO6YmxpY2EgKHJhdGlvX3B1YmxpY19pbnZlc3RtZW50KToqKg0KU29ycHJlc2l2YW1lbnRlLCBPYXhhY2EgZGVzdGFjYSBjb21vIGVsIGVzdGFkbyBjb24gbWF5b3IgaW52ZXJzacOzbiBww7pibGljYSByZXNwZWN0byBhbCBQSUIsIGZvcm1hbmRvIHVuIGNsdXN0ZXIgc2lnbmlmaWNhdGl2byBjb24gKip2ZWNpbm9zIGRlbCBzdXIqKiwgYXVucXVlIGxhIGF1dG9jb3JyZWxhY2nDs24gbm8gZnVlIGRlIGxhcyBtw6FzIGFsdGFzLg0KDQo5LgkqKkNsdXN0ZXJzIHBvc2l0aXZvcyBwYXJhIGVsIHR1cmlzbW86KioNCkxvcyBlc3RhZG9zIHF1ZSBoYW4gaW5jcmVtZW50YWRvIHN1IFBJQiB0dXLDrXN0aWNvIGVzdMOhbiBjZXJjYSBkZSBvdHJvcyBjb24gZGVzZW1wZcOxbyBzaW1pbGFyIChDRE1YLUVET01FWC1HdWFuYWp1YXRvKS4gRXN0byBzdWdpZXJlIHF1ZSBwb2zDrXRpY2FzIGRlIHR1cmlzbW8gcHVlZGVuIHRlbmVyIGVmZWN0b3MgZGUgZGVycmFtZSBwb3NpdGl2byByZWdpb25hbCB5IHF1ZSBwcm9iYWJsZW1lbnRlIGxhIGdlbnRlIHF1ZSB2aXNpdGEgY2llcnRvcyBlc3RhZG9zLCBlc3RhIGludGVyZXNhZGEgZW4gY29ub2NlciBsb3MgZXN0YWRvcyBjb2xpbmRhbnRlcy4gDQoNCjEwLgkqKlpvbmFzIHByaW9yaXRhcmlhcyBwYXJhIGludGVydmVuY2nDs246KioNCkVzdGFkb3MgY29uIGNsdXN0ZXJzIGRlICphbHRvIGNyaW1lbiwgYmFqbyBlbXBsZW8geSBiYWphIGVkdWNhY2nDs24qIChDaGlhcGFzLCBPYXhhY2EsIEd1ZXJyZXJvLCBaYWNhdGVjYXMpIGRlYmVyw61hbiBzZXIgcHJpb3JpemFkb3MgcGFyYSBpbnRlcnZlbmNpb25lcyBpbnRlZ3JhbGVzIGVuIHNlZ3VyaWRhZCwgZW1wbGVvIHkgZWR1Y2FjacOzbiB5IHBvZGVyIG1lam9yYXIgZXN0YXMgZXN0YWTDrXN0aWNhcyBlbiBlbCBmdXR1cm8uDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==