Planeación estratégica basada en analítica prescriptiva

Grupo 503

Profesor David Saucedo

Equipo 7:

A00833113 - Avril Lobato

Análisis Espacial y Temporal del Turismo en la Zona Metropolitana de Monterrey: Perspectivas Estratégicas para la Industria Hotelera y el Turismo de Negocios 2025–2026

Introducción

Contexto

La ZMM se ha consolidado como un polo estratégico para el turismo de negocios y entretenimiento en México. De acuerdo con la Secretaría de Turismo de Nuevo León, el estado recibió 4.1 millones de visitantes en 2021, una cifra que aumentó hasta alcanzar los 15 millones en 2024; generando una derrama económica de 17,721 millones de pesos. La ocupación hotelera alcanzó un promedio del 63.91%, con una tarifa promedio de $1,776.06 pesos.

El posicionamiento de Nuevo León como un hub de interconexión global, con vuelos directos a América Latina a través de Bogotá, a Asia mediante Tokio y Seúl, y a Europa con conexión a Madrid ha permitido que el Aeropuerto Internacional de Monterrey opere 64 destinos nacionales e internacionales, un crecimiento notable desde los 45 destinos registrados por la Secretaría de Turismo en 2021. A pesar de estos esfuerzos, el turismo en el estado sigue siendo predominantemente local, ya que el 86% de los visitantes provienen de otras partes de México (Secretaría de Turismo de Nuevo León. 2024).

Asimismo, Monterrey es un importante centro financiero y sede de convenciones, atrayendo viajeros corporativos. Por ello, el turismo de reuniones es un motor clave, con más de 250 eventos anuales, como congresos y exposiciones internacionales. Además, la región ha diversificado su oferta turística, impulsando segmentos como naturaleza y aventura, gastronomía, salud y cinematografía.

Bases de datos

La base de datos utilizada para este análisis fue construida a partir de diversas fuentes, combinando técnicas automatizadas y bases oficiales:

  • Base de datos de hoteles: Se construyó mediante el uso de la API de Google Maps y registros disponibles en la Secretaría de Turismo del Estado de Nuevo León y DENUE, lo cual permitió identificar la ubicación, categoría, comentarios y número de habitaciones de los hoteles en la Zona Metropolitana de Monterrey.

  • Base de datos de alojamientos en Airbnb: Se generó mediante técnicas de web scraping directamente desde la plataforma oficial de Airbnb, extrayendo información clave como número de propiedades activas, tipo de alojamiento y ubicación geográfica.

  • Base de datos de turismo de eventos: Se realizó a partir de información publicada por la Oficina de Convenciones y Visitantes (OCV) y complementada con registros de la Secretaría de Turismo estatal, centrada en la calendarización y características de eventos de negocios y entretenimiento.

  • Base de datos del festival Pa’l Norte: Se obtuvó de resultados de cuestionarios aplicados durante el evento, enfocadas en los patrones de comportamiento turístico de los asistentes, incluyendo duración de la estancia, tipo de hospedaje y gasto promedio.

Glosario de variables

Glosario de Variables: Oficina de Convenciones y Visitantes (OCV)
Variable Descripción Fuente
Eventos Número de eventos en el mes realizados por la Oficina de Convenciones y Visitantes. Secretaría de Turismo de Nuevo León
Asistentes Número de asistentes mensuales a los eventos realizados por la Oficina de Convenciones y Visitantes. Secretaría de Turismo de Nuevo León
Cuartos Número de cuartos ocupados en el mes por los eventos realizados por la Oficina de Convenciones y Visitantes. Secretaría de Turismo de Nuevo León
Derrama Derrama económica en MXN generada por los eventos realizados por la Oficina de Convenciones y Visitantes Secretaría de Turismo de Nuevo León
Glosario de Variables: Base de Datos de Airbnb
Variable Descripción Fuente
Nombre Nombre del alojamiento Airbnb. Web scraping del sitio oficial de Airbnb
Evaluaciones Número de evaluaciones del alojamiento Airbnb. Web scraping del sitio oficial de Airbnb
Calificación Calificación del alojamiento Airbnb. Web scraping del sitio oficial de Airbnb
Glosario de Variables: Base de Datos de Google Maps
Variable Descripción Fuente
Index ID del hotel dentro de la base de datos API de Google Maps
City Ubicación del hotel –Monterrey o San Pedro API de Google Maps
name Nombre del hotel API de Google Maps
latitude Latitud del hotel API de Google Maps
longitude Longitud del hotel API de Google Maps
rating Rating del hotel API de Google Maps
user_ratings_total Cantidad de ratings en Google Maps API de Google Maps
place_id ID de identificación en Google Maps API de Google Maps
Category Categoría del hotel registrada en Google Maps API de Google Maps
price Precio de una noche para dos personas API de Google Maps
Glosario de Variables: Base de Datos del DENUE
Variable Descripción Fuente
Nombre de la Unidad Económica Nombre oficial del hotel INEGI - DENUE
Nombre de clase de la actividad Clasificación oficial del establecimiento INEGI - DENUE
Municipio Municipio donde se ubica el hotel INEGI - DENUE
Latitude Latitud del hotel INEGI - DENUE
Longitud Longitud del hotel INEGI - DENUE
Ano_Inco Año de incorporación al registro del DENUE INEGI - DENUE
Mes_Inco Mes del año de incorporación al registro del DENUE INEGI - DENUE
Glosario de Variables: Hospitalidad – Hoteles
Variable Descripción Fuente
MES Mes del registro (1 = enero, …, 12 = diciembre) SECTUR NL
AÑO Año del registro SECTUR NL
Cuartos_Disponibles Número de cuartos disponibles en el mes SECTUR NL
Cuartos_Ocupados Número de cuartos ocupados en el mes SECTUR NL
Cuartos_Registrados Número de cuartos registrados administrativamente SECTUR NL
Derrama_Economica_Est_mdp Derrama económica estatal en millones de pesos SECTUR NL
Imp_Hospedaje Ingreso por hospedaje (millones de pesos) SECTUR NL
Turistas_Noche_Ext Número total de noches de turistas extranjeros SECTUR NL
Turistas_Noche_Nac Número total de noches de turistas nacionales SECTUR NL
%Ocupacion_Hoteles Porcentaje de ocupación hotelera promedio mensual SECTUR NL
Llegada_Tur_Ext Número de turistas extranjeros que llegan al estado SECTUR NL
Llegada_Tur_Nac Número de turistas nacionales que llegan al estado SECTUR NL
Cuartos_Disponibles_Promedio Promedio de cuartos disponibles (+1) SECTUR NL
Densidad_Ocupación Densidad de ocupación hotelera SECTUR NL
Estadia_Promedio Estadía promedio de huéspedes en noches SECTUR NL

Preguntas de Análisis

  • ¿Cómo se distribuyen geográficamente los alojamientos (hoteles y Airbnb) en relación con los principales recintos de eventos en la ZMM?

  • ¿Se identifican clusters o zonas con alta concentración de hospedajes y eventos, y cómo afectan estos a la dinámica turística local?

  • ¿En qué medida y cuáles son las variables que explican la mayor proporción de la varianza en la derrama económica de la industria hotelera en la Zona Metropolitana de Monterrey (ZMM)?

  • ¿Cuáles son los aspectos más valorados y los puntos de mejora señalados por los visitantes en sus comentarios en relación a los alojamientos (hoteles y Airbnb) en la ZMM?

Análisis Exploratorio de los Datos - EDA

Airbnb

Se realiza resumen estadístico de cada una de las variables que componen la base de datos de Airbnb:

  • Huéspedes: Número máximo de personas que puede alojar la propiedad. Representa la capacidad de alojamiento y varía entre pequeñas habitaciones y alojamientos amplios.

  • Habitaciones: Cantidad de habitaciones privadas disponibles en el alojamiento. Un indicador clave del tamaño del inmueble.

  • Camas: Total de camas disponibles para los huéspedes. Puede incluir camas individuales, dobles, sofá camas o colchones inflables.

  • Baños: Número total de baños disponibles en la propiedad. Incluye tanto baños completos como medios baños en algunos casos.

  • Calificación: Promedio de las calificaciones otorgadas por huéspedes (de 0 a 5). Refleja la calidad general percibida del hospedaje.

  • Evaluaciones: Total de veces que la propiedad ha sido evaluada por los usuarios. Es una medida indirecta de la popularidad o rotación del anuncio.

  • Precio: Costo por noche en pesos mexicanos (MXN). Varía ampliamente según ubicación, fecha, tamaño, comodidades y demanda.

  • Latitud: Coordenada geográfica que indica la ubicación norte-sur de la propiedad. En este caso, refleja ubicaciones dentro de la zona metropolitana de Monterrey.

  • Longitud: Coordenada geográfica que indica la ubicación este-oeste de la propiedad. Se usa junto con la latitud para ubicar los anuncios en un mapa.

  • Meses_Antigüedad: Tiempo, en meses, que el anfitrión lleva activo en la plataforma. Puede indicar experiencia del anfitrión o estabilidad del alojamiento.

Summary

library(openxlsx)
df_air <- read.xlsx("airbnb_oficial.xlsx")

# Eliminar las columnas "Noche" y "Descripcion"
df_air_clean <- df_air[, !names(df_air) %in% c("Noches", "Descripcion")]

# Filtrar solo las columnas numéricas
df_air_numeric <- df_air_clean[sapply(df_air_clean, is.numeric)]

# Generar el resumen estadístico
summary(df_air_numeric)
##    Huespedes       Habitaciones       Camas            Baños      
##  Min.   : 1.000   Min.   :0.000   Min.   : 1.000   Min.   :0.000  
##  1st Qu.: 2.000   1st Qu.:1.000   1st Qu.: 1.000   1st Qu.:1.000  
##  Median : 2.000   Median :1.000   Median : 1.000   Median :1.000  
##  Mean   : 2.925   Mean   :1.354   Mean   : 1.942   Mean   :1.192  
##  3rd Qu.: 3.000   3rd Qu.:1.000   3rd Qu.: 2.000   3rd Qu.:1.000  
##  Max.   :15.000   Max.   :9.000   Max.   :12.000   Max.   :4.000  
##   Calificación    Evaluaciones        Precio         Latitud     
##  Min.   :0.000   Min.   :  0.00   Min.   : 1513   Min.   :25.32  
##  1st Qu.:4.750   1st Qu.: 12.00   1st Qu.: 2886   1st Qu.:25.69  
##  Median :4.855   Median : 34.00   Median : 3515   Median :25.73  
##  Mean   :4.561   Mean   : 60.07   Mean   : 4600   Mean   :25.72  
##  3rd Qu.:4.933   3rd Qu.: 90.00   3rd Qu.: 4633   3rd Qu.:25.76  
##  Max.   :5.000   Max.   :405.00   Max.   :41372   Max.   :25.88  
##     Longitud      Meses_Antiguedad
##  Min.   :-100.8   Min.   : 0.00   
##  1st Qu.:-100.3   1st Qu.:12.00   
##  Median :-100.3   Median :36.00   
##  Mean   :-100.3   Mean   :38.74   
##  3rd Qu.:-100.2   3rd Qu.:60.00   
##  Max.   :-100.0   Max.   :96.00

Boxplots

Distribución sesgada y presencia de valores atípicos: La mayoría de las variables muestran distribuciones asimétricas con varios outliers:

  • Precio y Evaluaciones tienen colas largas hacia arriba, lo que indica la existencia de algunas propiedades con precios o evaluaciones extremadamente altas.

  • Huéspedes, Habitaciones, Camas y Baños tienen mediana baja (1 o 2) pero colas superiores que sugieren algunas propiedades con mucha mayor capacidad.

  • Calificación está muy concentrada alrededor de 4.5-5, lo que indica buena satisfacción general.

Asimismo, se observa que Meses_Antigüedad tiene una distribución más equilibrada, con valores entre 0 y 100 meses, pero también con outliers.

library(reshape2)
df_long <- df_air_numeric %>%
  pivot_longer(cols = everything(), names_to = "variable", values_to = "valor")

# Crear los boxplots con escalas independientes en el eje Y
ggplot(df_long, aes(x = "", y = valor)) +
  geom_boxplot(fill = "lightblue", color = "darkblue") +
  facet_wrap(~ variable, scales = "free_y") +
  labs(title = "Boxplots por variable numérica",
       x = NULL,
       y = NULL) +
  theme_minimal()

Matriz de correlación

Relaciones positivas fuertes:

  • Habitaciones y Camas (r ≈ 0.80): propiedades con más habitaciones tienden a tener más camas.

  • Habitaciones y Huéspedes (r ≈ 0.64): a más habitaciones, más capacidad de huéspedes.

  • Huéspedes y Precio (r ≈ 0.54): más capacidad, mayor precio.

Relaciones débiles o nulas:

  • Calificación y otras variables: muy poca correlación, lo que sugiere que la calificación depende más de factores cualitativos.

  • Latitud/Longitud vs otras variables: baja correlación, lo que indica poca influencia directa de la ubicación en las variables numéricas.

  • Relación negativa: Precio correlaciona ligeramente negativo con Latitud y Evaluaciones, lo que podría indicar zonas con precios más altos pero menos evaluaciones o diferente ubicación geográfica.

# Calcular la matriz de correlación
cor_matrix <- cor(df_air_numeric, use = "complete.obs")

# Visualizar la matriz de correlación
corrplot(cor_matrix, method = "color", type = "upper",
         tl.col = "black", tl.srt = 45,
         addCoef.col = "black", number.cex = 0.7,
         col = colorRampPalette(c("red", "white", "blue"))(200))

Histogramas

  • Distribuciones sesgadas a la derecha (positivamente): Precio, Evaluaciones, Huéspedes, Camas, Habitaciones, Baños: mayoría de las observaciones se concentran en valores bajos, con pocas propiedades que tienen valores muy altos.

  • Distribución de Calificación: muy concentrada entre 4.5 y 5, reflejando altos niveles de satisfacción.

  • Meses_Antigüedad: dispersa a lo largo del rango, sin una forma clara, aunque hay ciertos picos indicando anfitriones con tiempo significativo en la plataforma.

  • Latitud y Longitud: reflejan una fuerte concentración geográfica; podría indicar que las propiedades están ubicadas en una ciudad o zona específica (como Monterrey).

plot_histogram(df_air_numeric)

plot_normality(df_air_numeric)

Eventos OCV

Summary

Se observa una concentración de los datos en 2023 y 2024, en la cual la distribución de los eventos y la cantidad de cuartos disponibles tienden a concentrarse en rangos inferiores, aunque se identifican algunos picos o valores inusualmente altos que podrían corresponder a eventos o establecimientos particulares con una capacidad significativamente mayor; lo cual se denota en la media de la derrama económica real.

  • Eventos: Se muestra un rango de 2 a 30 eventos, con una mediana de 11. La media ligeramente superior a la mediana (12.12) sugiere una asimetría positiva causada por los valores atípicos.

  • Cuartos: Se muestra un rango de 1235 a 35568 cuartos, con una mediana de 10482. La media (12117) es mayor que la mediana, lo que indica una asimetría positiva.

  • Asistentes: Se presenta un rango muy amplio de asistentes, desde 1809 hasta 357450, con una mediana de solo 15875. La media (44819) es significativamente mayor que la mediana, lo que indica una fuerte influencia del valor atípico superior.

  • Derrama: Se muestra un rango de derrama desde 7.5 millones hasta 343 millones, con una mediana de 129 millones. La media (152 millones) es mayor que la mediana, lo que sugiere una asimetría positiva debido al valor atípico.

  • Llegada_Tur_Ext (Llegada de Turistas Extranjeros): Se muestra un rango de llegadas de turistas extranjeros entre 31820 y 61107, con una mediana de 45931 y una media ligeramente superior (47672), sugiriendo una ligera asimetría positiva.

  • Llegada_Tur_Nac (Llegada de Turistas Nacionales): Se muestra un rango de llegadas de turistas nacionales entre 145179 y 315672, con una mediana de 220976 y una media ligeramente superior (223961), indicando una ligera asimetría positiva influenciada por el valor atípico.

  • %Ocupacion_Hoteles (Porcentaje de Ocupación Hotelera): Se observa un rango de ocupación hotelera entre 0.46 y 0.76, con una mediana de 0.64 y una media ligeramente inferior (0.6356), lo que sugiere una ligera asimetría negativa debido a los valores atípicos inferiores.

library(openxlsx)
df_OCV <- read.xlsx("Tabla_Macro_EventosOCV.xlsx")

# Convertir a Reales
df_OCV$Derrama<- (df_OCV$Derrama/df_OCV$INPC)*100

# Crear una columna de fecha combinando Año y Mes
df_OCV$Fecha <- as.Date(paste(df_OCV$Año, df_OCV$MesNombre, "01", sep = "-"), format = "%Y-%m-%d")

# Filtrar los datos desde enero de 2023 en adelante
df_OCV_filtered <- subset(df_OCV, Fecha >= as.Date("2023-01-01"))

# Eliminar las columnas "Noches" y "Descripcion"
df_OCV_clean <- df_OCV_filtered[, names(df_OCV_filtered) %in% c(
  "Fecha",
  "Año",
  "MesNombre",
  "Eventos",
  "Cuartos",
  "Asistentes",
  "Derrama",
  "%Ocupacion_Hoteles",
  "Llegada_Tur_Ext",
  "Llegada_Tur_Nac"
)]


# Filtrar solo las columnas numéricas
df_OCV_numeric <- df_OCV_clean[sapply(df_OCV_clean, is.numeric)]

# Generar el resumen estadístico
summary(df_OCV_numeric)
##       Año         MesNombre        Eventos         Cuartos     
##  Min.   :2023   Min.   : 1.00   Min.   : 2.00   Min.   : 1235  
##  1st Qu.:2023   1st Qu.: 3.75   1st Qu.: 9.50   1st Qu.: 7036  
##  Median :2024   Median : 6.50   Median :11.00   Median :10482  
##  Mean   :2024   Mean   : 6.50   Mean   :12.12   Mean   :12117  
##  3rd Qu.:2024   3rd Qu.: 9.25   3rd Qu.:15.25   3rd Qu.:17448  
##  Max.   :2024   Max.   :12.00   Max.   :30.00   Max.   :35568  
##    Asistentes        Derrama          Llegada_Tur_Ext Llegada_Tur_Nac 
##  Min.   :  1809   Min.   :  7520170   Min.   :31820   Min.   :145179  
##  1st Qu.:  9518   1st Qu.: 93193224   1st Qu.:43020   1st Qu.:198157  
##  Median : 15875   Median :129076217   Median :45931   Median :220976  
##  Mean   : 44819   Mean   :152407090   Mean   :47672   Mean   :223961  
##  3rd Qu.: 34739   3rd Qu.:210714013   3rd Qu.:53153   3rd Qu.:244127  
##  Max.   :357450   Max.   :343109285   Max.   :61107   Max.   :315672  
##  %Ocupacion_Hoteles
##  Min.   :0.4649    
##  1st Qu.:0.6025    
##  Median :0.6428    
##  Mean   :0.6356    
##  3rd Qu.:0.6604    
##  Max.   :0.7614

Boxplots

Se observa que la actividad turística con patrones definidos a lo largo del año, con ciertos eventos o situaciones puntuales que generan un impacto significativamente mayor en términos de asistentes y derrama económica:

  • Eventos: Se muestra una concentración de eventos en la parte inferior, con algunos valores atípicos significativamente altos.

  • Cuartos: Se muestra una distribución con una mediana alrededor de 10,000 cuartos, con algunos valores atípicos superiores.

  • Asistentes: Se presenta la mayoría de los datos de asistentes en un rango bajo, con un valor atípico muy alto.

  • Derrama: Se muestra una distribución con una mediana alrededor de 1.3 x 10^8, con un valor atípico superior notable.

  • Llegada_Tur_Ext (Llegada de Turistas Extranjeros): Se muestra una distribución relativamente centrada, con pocos valores atípicos.

  • Llegada_Tur_Nac (Llegada de Turistas Nacionales): Se muestra una distribución relativamente centrada, con un valor atípico superior.

  • %Ocupacion_Hoteles (Porcentaje de Ocupación Hotelera): Se muestra una distribución relativamente estrecha, con la mayoría de los valores entre aproximadamente 0.6 y 0.7, y algunos valores atípicos inferiores.

library(reshape2)
df_long <- df_OCV_numeric %>%
  pivot_longer(cols = everything(), names_to = "variable", values_to = "valor")

# Crear los boxplots con escalas independientes en el eje Y
ggplot(df_long, aes(x = "", y = valor)) +
  geom_boxplot(fill = "lightblue", color = "darkblue") +
  facet_wrap(~ variable, scales = "free_y") +
  labs(title = "Boxplots por variable numérica",
       x = NULL,
       y = NULL) +
  theme_minimal()

Matriz de Correlación

El número de eventos y la llegada de turistas nacionales parecen ser impulsores clave de la asistencia, la derrama económica y la ocupación hotelera. La cantidad de cuartos disponibles también muestra una relación positiva con la derrama y la ocupación. Por otro lado, la llegada de turistas extranjeros parece tener una relación más débil o incluso negativa con algunas de las otras variables analizadas.

Correlaciones Positivas Fuertes (Azul Oscuro, valores cercanos a 1):

  • Asistentes - Eventos (0.78): Existe una fuerte tendencia a que un mayor número de eventos esté asociado con un mayor número de asistentes. Esto es bastante intuitivo.

  • Asistentes - Derrama (0.56): Hay una correlación positiva moderada, lo que sugiere que más asistentes tienden a generar una mayor derrama económica.

  • Asistentes - Llegada_Tur_Nac (0.61): Un mayor número de turistas nacionales llegando se asocia con un mayor número de asistentes a eventos o actividades.

  • Eventos - Derrama (0.67): Un mayor número de eventos tiende a estar relacionado con una mayor derrama económica total.

  • Eventos - Llegada_Tur_Nac (0.82): Existe una fuerte correlación entre el número de eventos y la llegada de turistas nacionales. Más eventos podrían atraer a más turistas nacionales, o viceversa.

  • Eventos - Cuartos (0.63): Hay una correlación positiva moderada, sugiriendo que más eventos podrían requerir o estar asociados con una mayor cantidad de cuartos disponibles.

  • Derrama - Llegada_Tur_Nac (0.77): Una mayor llegada de turistas nacionales está fuertemente relacionada con una mayor derrama económica.

  • Derrama - Cuartos (0.83): Existe una fuerte correlación entre la derrama económica y la cantidad de cuartos disponibles. Una mayor capacidad de alojamiento podría estar asociada con una mayor derrama.

  • Llegada_Tur_Nac - %Ocupacion_Hoteles (0.79): Una mayor llegada de turistas nacionales se asocia fuertemente con un mayor porcentaje de ocupación hotelera.

  • Llegada_Tur_Nac - Cuartos (0.53): Hay una correlación positiva moderada, lo que sugiere que una mayor llegada de turistas nacionales podría estar relacionada con una mayor cantidad de cuartos disponibles.

  • %Ocupacion_Hoteles - Cuartos (0.58): Existe una correlación positiva moderada entre el porcentaje de ocupación hotelera y la cantidad de cuartos. Una mayor oferta de cuartos podría llevar a una mayor ocupación general, o viceversa en ciertas circunstancias.

  • MesNombre - Cuartos (0.47): Hay una correlación positiva débil, sugiriendo una ligera tendencia a que ciertos meses estén asociados con una mayor cantidad de cuartos disponibles.

Correlaciones Negativas (Rojo, valores cercanos a -1):

  • Llegada_Tur_Ext - Año (-0.48): Existe una correlación negativa moderada, lo que sugiere que a medida que el año cambia (de 2023 a 2024 en este caso), la llegada de turistas extranjeros tiende a disminuir ligeramente en este conjunto de datos.

  • Llegada_Tur_Ext - Asistentes (-0.12): Hay una correlación negativa muy débil, casi inexistente, entre la llegada de turistas extranjeros y el número de asistentes.

  • Llegada_Tur_Ext - %Ocupacion_Hoteles (-0.21): Se observa una ligera tendencia negativa entre la llegada de turistas extranjeros y el porcentaje de ocupación hotelera. Esto podría indicar que una mayor llegada de turistas extranjeros no necesariamente se traduce en una mayor ocupación general, quizás porque se hospedan en opciones diferentes a los hoteles o la ocupación está más influenciada por el turismo nacional.

  • Llegada_Tur_Ext - MesNombre (-0.22): Existe una ligera correlación negativa, sugiriendo que ciertos meses con mayor llegada de turistas extranjeros podrían ser diferentes de los meses con valores más altos en otras variables.

library(corrplot)

# Calcular la matriz de correlación
cor_matrix <- cor(df_OCV_numeric, use = "complete.obs")

# Visualizar la matriz de correlación con ajustes para mejorar la legibilidad
corrplot(
  cor_matrix,
  method = "color",                # Método de visualización
  type = "upper",                  # Mostrar solo la parte superior de la matriz
  order = "hclust",                # Ordenar variables por agrupamiento jerárquico
  tl.col = "black",                # Color de las etiquetas de texto
  tl.cex = 0.6,                    # Tamaño del texto de las etiquetas
  tl.srt = 45,                     # Rotación de las etiquetas
  addCoef.col = "black",           # Color de los coeficientes de correlación
  number.cex = 0.6,                # Tamaño de los coeficientes de correlación
  col = colorRampPalette(c("red", "white", "blue"))(200)  # Paleta de colores
)

Histograma

Con base en los gráficos se determina transformar a logaritmo únicamente las variables de Cuartos y Asistentes debido a que es el tipo de transformación que mejora la distribución normal de ambas explicativas; mientras que Eventos y Asistentes se mantienen en valor original para mejorar interpretabilidad de modelos.

  • Eventos: Su distribución está ligeramente sesgada a la derecha. El Q-Q plot muestra cierta curvatura en los extremos, pero no es extrema. Por lo tanto, una transformación de raíz cuadrada podría mejorar la normalidad si se requiere un ajuste más fino pero, no es totalmente necesario.

  • Cuartos: Tiene distribución claramente sesgada a la derecha. El Q-Q plot muestra una desviación notoria de la línea de normalidad. Por lo tanto, conviene realizar transformación logarítmica, ya que es la que más mejora la aproximación a una distribución normal.

  • Asistentes: Muestra distribución muy sesgada a la derecha (valores extremos). Q-Q plot muestra clara desviación. La transformación logarítmica ,ejora significativamente la normalidad, convirtiendo una distribución fuertemente sesgada en algo más simétrico.

  • Derrama: Su distribución está ligeramente sesgada a la derecha. El Q-Q plot muestra cierta curvatura en los extremos, pero no es extrema. Por lo tanto, una transformación de raíz cuadrada podría mejorar la normalidad si se requiere un ajuste más fino pero, no es totalmente necesario.

plot_histogram(df_OCV_numeric)

plot_normality(df_OCV_numeric)

Descomposición Time Series de Derrama Económica de Eventos OCV

  • Observed (Observado): Se observan fluctuaciones a lo largo del tiempo, con picos y valles que sugieren patrones subyacentes. Se observa una variabilidad considerable a lo largo del periodo analizado, en la cual a finales de 2024 se aprecia un pico pronunciado seguido de una caída considerable.

  • Trend (Tendencia): La tendencia parece relativamente plana durante la mayor parte de 2023, con un ligero aumento hacia finales de ese año. A principios de 2024 se observa un incremento más marcado en la tendencia, que luego parece estabilizarse o incluso disminuir ligeramente hacia la segunda mitad de 2024.

  • Seasonal (Estacionalidad): Se observan fluctuaciones que parecen repetirse a lo largo del tiempo ya que, existen picos a mediados de cada año y valles hacia el inicio y el final en la derrama.

  • Random (Aleatorio): La línea se mantiene constante a lo largo del periodo, la línea horizontal cerca del cero sugiere que, en general, el modelo aditivo ha logrado capturar una parte importante de la variabilidad.

df_OCV_numeric$Fecha <- as.Date(paste(df_OCV_numeric$Año, df_OCV_numeric$MesNombre, "01", sep = "-"), format = "%Y-%m-%d")

df_OCV_numeric <- df_OCV_numeric[!is.na(df_OCV_numeric$Derrama), ]
df_OCV_numeric <- df_OCV_numeric[order(df_OCV_numeric$Fecha), ]

Derrama_ts <- ts(df_OCV_numeric$Derrama, start = c(min(df_OCV_numeric$Año), min(df_OCV_numeric$Mes)), frequency = 12)

library(ggplot2)

ggplot(df_OCV_numeric, aes(x = Fecha, y = Derrama)) +
  geom_line(color = "blue", size = 1) +
  labs(title = "Serie Temporal de Derrama",
       x = "Fecha",
       y = "Derrama") +
  theme_minimal()

decomposition <- decompose(Derrama_ts)
plot(decomposition)

Hoteles

Summary

library(openxlsx)
df <- read.xlsx("HOTELES-BD.xlsx", sheet ="HOTELES")
# Nulos Totales
sum(is.na(df))
## [1] 132
# Nulos por Columna
gg_miss_var(df)

summary(df)
##       AÑO            MES        Cuartos_Disponibles
##  Min.   :2004   Min.   : 1.00   Min.   :259707     
##  1st Qu.:2009   1st Qu.: 3.75   1st Qu.:346570     
##  Median :2014   Median : 6.50   Median :373091     
##  Mean   :2014   Mean   : 6.50   Mean   :391909     
##  3rd Qu.:2019   3rd Qu.: 9.25   3rd Qu.:430071     
##  Max.   :2024   Max.   :12.00   Max.   :543182     
##                                                    
##  Cuartos_Disponibles_Promedio Cuartos_Ocupados Cuartos_Registrados
##  Min.   : 9142                Min.   : 26308   Min.   :264740     
##  1st Qu.:11568                1st Qu.:168867   1st Qu.:350052     
##  Median :12205                Median :203002   Median :381920     
##  Mean   :12874                Mean   :219182   Mean   :395936     
##  3rd Qu.:14115                3rd Qu.:275674   3rd Qu.:432078     
##  Max.   :17522                Max.   :382072   Max.   :543182     
##                                                                   
##  Densidad_Ocupación Derrama_Economica_Est_mdp Estadia_Promedio
##  Min.   :0.000      Min.   : 103.5            Min.   :1.606   
##  1st Qu.:1.450      1st Qu.: 541.9            1st Qu.:1.910   
##  Median :1.515      Median : 718.4            Median :2.020   
##  Mean   :1.543      Mean   : 909.2            Mean   :2.041   
##  3rd Qu.:1.600      3rd Qu.:1089.8            3rd Qu.:2.160   
##  Max.   :2.080      Max.   :3016.2            Max.   :3.050   
##                                                               
##  Imp_Hospedaje      Turistas_Noche_Ext Turistas_Noche_Nac %Ocupacion_Hoteles
##  Min.   : 1635935   Min.   :  3728     Min.   : 37093     Min.   :0.05811   
##  1st Qu.: 6492250   1st Qu.: 37499     1st Qu.:209590     1st Qu.:0.48184   
##  Median :10329255   Median : 53827     Median :257929     Median :0.57719   
##  Mean   :10509451   Mean   : 56497     Mean   :285699     Mean   :0.55791   
##  3rd Qu.:13543491   3rd Qu.: 71226     3rd Qu.:361718     3rd Qu.:0.65404   
##  Max.   :24075522   Max.   :143049     Max.   :557430     Max.   :0.80457   
##  NA's   :132                                                                
##  Llegada_Tur_Ext Llegada_Tur_Nac 
##  Min.   : 1176   Min.   : 12021  
##  1st Qu.:18874   1st Qu.:106026  
##  Median :27517   Median :123695  
##  Mean   :28754   Mean   :139067  
##  3rd Qu.:37405   3rd Qu.:172872  
##  Max.   :65790   Max.   :315672  
## 
colnames(df)
##  [1] "AÑO"                          "MES"                         
##  [3] "Cuartos_Disponibles"          "Cuartos_Disponibles_Promedio"
##  [5] "Cuartos_Ocupados"             "Cuartos_Registrados"         
##  [7] "Densidad_Ocupación"           "Derrama_Economica_Est_mdp"   
##  [9] "Estadia_Promedio"             "Imp_Hospedaje"               
## [11] "Turistas_Noche_Ext"           "Turistas_Noche_Nac"          
## [13] "%Ocupacion_Hoteles"           "Llegada_Tur_Ext"             
## [15] "Llegada_Tur_Nac"

Histogramas

plot_histogram(df)

plot_normality(df)

library(dplyr)
# Define columnas a transformar
cols_log <- c( "Cuartos_Disponibles_Promedio", 
              "Densidad_Ocupación",
              "Estadia_Promedio")

# Crear las columnas transformadas y agregarlas al dataframe original
df_log <- df %>%
  mutate(across(all_of(cols_log), ~ log(. + 1), .names = "{.col}_log"))

df_log <- df_log[, !(names(df_log) %in% cols_log)]

# Gráfico de dispersión
plot_histogram(df_log)

summary(df_log)
##       AÑO            MES        Cuartos_Disponibles Cuartos_Ocupados
##  Min.   :2004   Min.   : 1.00   Min.   :259707      Min.   : 26308  
##  1st Qu.:2009   1st Qu.: 3.75   1st Qu.:346570      1st Qu.:168867  
##  Median :2014   Median : 6.50   Median :373091      Median :203002  
##  Mean   :2014   Mean   : 6.50   Mean   :391909      Mean   :219182  
##  3rd Qu.:2019   3rd Qu.: 9.25   3rd Qu.:430071      3rd Qu.:275674  
##  Max.   :2024   Max.   :12.00   Max.   :543182      Max.   :382072  
##                                                                     
##  Cuartos_Registrados Derrama_Economica_Est_mdp Imp_Hospedaje     
##  Min.   :264740      Min.   : 103.5            Min.   : 1635935  
##  1st Qu.:350052      1st Qu.: 541.9            1st Qu.: 6492250  
##  Median :381920      Median : 718.4            Median :10329255  
##  Mean   :395936      Mean   : 909.2            Mean   :10509451  
##  3rd Qu.:432078      3rd Qu.:1089.8            3rd Qu.:13543491  
##  Max.   :543182      Max.   :3016.2            Max.   :24075522  
##                                                NA's   :132       
##  Turistas_Noche_Ext Turistas_Noche_Nac %Ocupacion_Hoteles Llegada_Tur_Ext
##  Min.   :  3728     Min.   : 37093     Min.   :0.05811    Min.   : 1176  
##  1st Qu.: 37499     1st Qu.:209590     1st Qu.:0.48184    1st Qu.:18874  
##  Median : 53827     Median :257929     Median :0.57719    Median :27517  
##  Mean   : 56497     Mean   :285699     Mean   :0.55791    Mean   :28754  
##  3rd Qu.: 71226     3rd Qu.:361718     3rd Qu.:0.65404    3rd Qu.:37405  
##  Max.   :143049     Max.   :557430     Max.   :0.80457    Max.   :65790  
##                                                                          
##  Llegada_Tur_Nac  Cuartos_Disponibles_Promedio_log Densidad_Ocupación_log
##  Min.   : 12021   Min.   :9.121                    Min.   :0.0000        
##  1st Qu.:106026   1st Qu.:9.356                    1st Qu.:0.8961        
##  Median :123695   Median :9.410                    Median :0.9223        
##  Mean   :139067   Mean   :9.448                    Mean   :0.9304        
##  3rd Qu.:172872   3rd Qu.:9.555                    3rd Qu.:0.9555        
##  Max.   :315672   Max.   :9.771                    Max.   :1.1249        
##                                                                          
##  Estadia_Promedio_log
##  Min.   :0.9578      
##  1st Qu.:1.0682      
##  Median :1.1053      
##  Mean   :1.1105      
##  3rd Qu.:1.1506      
##  Max.   :1.3987      
## 

Matriz de Correlación

Variables de ocupación y habitaciones muy relacionadas con la derrama

  • Turistas_Noche_Ext (noches de extranjeros) y Cuartos_Ocupados muestran correlaciones con la derrama de r ≈ 0.88 y r ≈ 0.86 respectivamente.

  • Llegada_Tur_Ext, Turistas_Noche_Nac y %Ocupacion_Hoteles también superan r ≈ 0.8.

Esto confirma que a más ocupación y llegadas de turistas, mayor derrama.

Multicolinealidad entre regresoras

*Turistas_Noche_Ext y Turistas_Noche_Nac están casi colineales (r ≈ 0.95).

  • Cuartos_Ocupados, Cuartos_Registrados y Cuartos_Disponibles también se correlacionan muy alto entre sí (r > 0.7).

Para modelos de regresión o SARIMAX conviene quedarse con un subconjunto (o usar PCA/VIF) para evitar redundancia.(se realiza más adelante)

plot_correlation(df_log)

df_numerico <- df_log[sapply(df_log, is.numeric)]

# 2. Calcula la matriz de correlación
matriz_cor <- cor(df_numerico, use = "pairwise.complete.obs")

corrplot( matriz_cor,
  method = "color",            # Mapa de calor
  type = "upper",              # Solo triángulo superior
  addCoef.col = "black",       # Mostrar coeficientes en color negro
  number.cex = 0.6,            # Tamaño del número
  tl.cex = 0.35,                # Tamaño de las etiquetas
  tl.col = "black",            # Color del texto
  diag = FALSE                 # Oculta la diagonal (1s)
)

Correlaciones moderadas

  • Densidad_Ocupación_log (r ≈ 0.32) y %Ocupacion_Hoteles (r ≈ 0.50) sí muestran una asociación, pero de intensidad media.

Variables con correlación fuerte (r > 0.75)

  • Cuartos_Disponibles (r ≈ 0.76)

  • Llegada_Tur_Ext (r ≈ 0.78)

  • Turistas_Noche_Ext (r ≈ 0.82)

  • Imp_Hospedaje (r ≈ 0.84)

  • Cuartos_Ocupados (r ≈ 0.84)

  • Turistas_Noche_Nac (r ≈ 0.86)

Dichas variables capturan la mayor parte de la variación de la derrama. Cuantos más cuartos ocupados y más noches de turistas (nacionales y extranjeros), mayor derrama.

# Calcular la matriz de correlación
correlaciones <- cor(df_numerico, use = "pairwise.complete.obs")

# Extraer las correlaciones con %Ocupacion_Hoteles
cor_ocupacion <- correlaciones["Derrama_Economica_Est_mdp", ]
cor_ocupacion <- cor_ocupacion[!names(cor_ocupacion) %in% "Derrama_Economica_Est_mdp"]

df_cor <- data.frame(
  Variable = names(cor_ocupacion),
  Correlacion = cor_ocupacion
) %>%
  arrange(desc(abs(Correlacion))) %>%
  mutate(Variable = factor(Variable, levels = Variable))

ggplot(df_cor, aes(x = Variable, y = Correlacion)) +
  geom_bar(stat = "identity", fill = "steelblue") +
  coord_flip() +
  geom_text(aes(label = round(Correlacion, 2)), hjust = ifelse(df_cor$Correlacion > 0, -0.1, 1.1)) +
  labs(
    title = "Correlación con Derrama_Economica_Est_mdp",
    y = "Coeficiente de correlación",
    x = ""
  ) +
  theme_minimal()

Descomposición Derrama Industria Hotelera

Se logra visualizar en la serie de tiempo de la variable de interés que partir de 2022–2023 los valores exhiben picos más pronunciados (2 500–2 800 mdp) pero también oscilaciones más amplias, lo que sugiere una mayor incertidumbre (posiblemente por variaciones en impuestos, inflación, etc.).

De modo que se logra destacar lo siguiente:

  • Un crecimiento estructural de la derrama de casi un orden de magnitud en 20 años.

  • Un shock negativo puntual en 2020 y una recuperación más fuerte aún.

  • Transición a una etapa de mayor volatilidad y niveles récord en los últimos meses.

df_log <- df_log[!is.na(df_log$"Derrama_Economica_Est_mdp"), ]

derrama_ts <- ts(df_log$"Derrama_Economica_Est_mdp", start = c(2004, 1), end = c(2024, 12), frequency = 12)
plot(derrama_ts, type = "l", col = "darkgreen", lwd = 2,
     xlab = "Año", ylab = "Derrama Económica",
     main = "Serie Temporal de Derrama_Economica_Est_mdp")

length(derrama_ts)
## [1] 252
frequency(derrama_ts)
## [1] 12
end(derrama_ts)
## [1] 2024   12
decomposition <- decompose(derrama_ts)
plot(decomposition)

descomp_stl <- stl(derrama_ts, s.window = "periodic")
plot(descomp_stl)

Boxplots

Meses de “temporada alta”

  • Abril (mes 4) y noviembre (mes 11) muestran las medianas más elevadas (alrededor de 750–800 mdp) y cuartiles superiores altos, lo que sugiere picos sistemáticos en primavera y justo antes de fin de año.

Meses de “temporada baja”

  • Enero (1) es el más bajo en mediana, con pocos outliers, reflejando el típico bajón tras la temporada navideña.

  • Septiembre (9) y febrero (2) también presentan medianas reducidas , posiblemente por vacaciones cortas o periodos de baja actividad turística.

Actividad Atípica

  • Meses como abril, mayo (5) y diciembre tienen varios puntos atípicos muy por encima de la caja, que reflejan eventos extraordinarios (congresos, feriados largos, promociones).
boxplot(derrama_ts ~ cycle(derrama_ts),
        xlab = "Mes", ylab = "Derrama",
        main = "Estacionalidad mensual", col = "lightblue")

Turismo de Eventos de Entretenimiento (Pal Norte)

Summary

A través del análisis de alrededor de 180 encuestas recolectadas entre asistentes reales al festival Tecate Pal Norte 2025, se busca identificar un perfil claro del mercado actual, así como las oportunidades estratégicas para escalar la rentabilidad del evento mediante segmentación de públicos, optimización de servicios y personalización de experiencias.

Cabe mencionar que este análisis no solo permite entender al público actual del festival, sino también identificar con claridad dónde están las areas de crecimiento económico y qué acciones pueden detonar una mayor conversión de valor por asistente, sustentando así una estrategia de marketing basada en datos reales y perfiles predictivos.

En la siguiente sección unificamos las dos bases de datos originales, eliminamos columnas irrelevantes como ID o fechas de captura,y realizamos limpieza en los datos monetarios para transformarlos en variables numéricas.

Además, corregimos errores evidentes de captura como montos concatenados sin separación, y convertimos los días de asistencia desde texto a valor numérico (1, 2 o 3). Finalmente, se calcula el gasto total por persona considerando boletos, alojamiento,y gastos diarios por alimentos y transporte multiplicados por los días asistidos.

df1 <- read_excel("Encuesta Turismo PalNorte 2025 Capturas I.xlsx")
df2 <- read_excel("Encuesta Turismo PalNorte 2025 Capturas II.xlsx")
df_palnorte <- bind_rows(df1, df2)

df_palnorte <- df_palnorte %>% select(-c(ID, `Start time`, `Completion time`, Email, Name))

to_numeric <- function(x) {
  x <- gsub("[^0-9.]", "", x)
  x[x == ""] <- NA
  as.numeric(x)
}

df_palnorte <- df_palnorte %>%
  mutate(
    boletos = to_numeric(`¿Cuál fue su presupuesto total para los boletos?`),
    alojamiento = to_numeric(`¿Cuál es su presupuesto estimado para alojamiento durante el festival?`),
    alimentos = to_numeric(`¿Cuánto planea gastar en alimentos y bebidas dentro del festival (por día)?`),
    transporte = to_numeric(`¿Cuánto planea gastar en transporte por día (incluye taxis, Uber, combustible, estacionamiento, etc.)?`)
  ) %>%
  drop_na(boletos, alojamiento, alimentos, transporte) %>%
  mutate(
    boletos = ifelse(boletos > 1000000, 3000, boletos),
    alojamiento = ifelse(alojamiento > 50000, 1500, alojamiento),
    alimentos = ifelse(alimentos > 100000, 750, alimentos),
    transporte = ifelse(transporte > 10000, 500, transporte),
    dias = case_when(
      `¿Cuántos días planea asistir al festival?` == "Todo el evento (3 días)" ~ 3,
      `¿Cuántos días planea asistir al festival?` == "Dos días" ~ 2,
      `¿Cuántos días planea asistir al festival?` == "Sólo un día" ~ 1,
      TRUE ~ NA_real_
    ),
    gasto_total = boletos + alojamiento + (alimentos + transporte) * dias
  )

summary(df_palnorte)
##  ¿Cómo compró sus boletos para el festival?
##  Length:78                                 
##  Class :character                          
##  Mode  :character                          
##                                            
##                                            
##                                            
##  ¿Cuántos días planea asistir al festival?
##  Length:78                                
##  Class :character                         
##  Mode  :character                         
##                                           
##                                           
##                                           
##  ¿Cuál fue su presupuesto total para los boletos?
##  Length:78                                       
##  Class :character                                
##  Mode  :character                                
##                                                  
##                                                  
##                                                  
##  ¿Qué categoría de boletos compró? ¿De qué región o ciudad proviene?
##  Length:78                         Length:78                        
##  Class :character                  Class :character                 
##  Mode  :character                  Mode  :character                 
##                                                                     
##                                                                     
##                                                                     
##  ¿Cuál es su presupuesto estimado para alojamiento durante el festival?
##  Length:78                                                             
##  Class :character                                                      
##  Mode  :character                                                      
##                                                                        
##                                                                        
##                                                                        
##  ¿Cuánto planea gastar en alimentos y bebidas dentro del festival (por día)?
##  Length:78                                                                  
##  Class :character                                                           
##  Mode  :character                                                           
##                                                                             
##                                                                             
##                                                                             
##  ¿Cuánto planea gastar en transporte por día (incluye taxis, Uber, combustible, estacionamiento, etc.)?
##  Length:78                                                                                             
##  Class :character                                                                                      
##  Mode  :character                                                                                      
##                                                                                                        
##                                                                                                        
##                                                                                                        
##  ¿Qué otros gastos adicionales tiene planeado realizar? (Seleccione todas las que correspondan)
##  Length:78                                                                                     
##  Class :character                                                                              
##  Mode  :character                                                                              
##                                                                                                
##                                                                                                
##                                                                                                
##  ¿Es la primera vez que asiste al festival Pal' Norte?
##  Length:78                                            
##  Class :character                                     
##  Mode  :character                                     
##                                                       
##                                                       
##                                                       
##  ¿Cuál es su rango de edad? (Pregunta demográfica)
##  Length:78                                        
##  Class :character                                 
##  Mode  :character                                 
##                                                   
##                                                   
##                                                   
##  ¿Cuál es su ocupación principal?    boletos      alojamiento  
##  Length:78                        Min.   :2000   Min.   :1000  
##  Class :character                 1st Qu.:3000   1st Qu.:1500  
##  Mode  :character                 Median :3000   Median :1500  
##                                   Mean   :3397   Mean   :2083  
##                                   3rd Qu.:4000   3rd Qu.:3000  
##                                   Max.   :4000   Max.   :3000  
##    alimentos        transporte         dias        gasto_total   
##  Min.   : 500.0   Min.   :300.0   Min.   :1.000   Min.   : 3800  
##  1st Qu.: 750.0   1st Qu.:300.0   1st Qu.:1.000   1st Qu.: 6000  
##  Median : 750.0   Median :500.0   Median :2.000   Median : 8250  
##  Mean   : 791.7   Mean   :497.4   Mean   :2.077   Mean   : 8188  
##  3rd Qu.:1000.0   3rd Qu.:650.0   3rd Qu.:3.000   3rd Qu.:10000  
##  Max.   :1000.0   Max.   :700.0   Max.   :3.000   Max.   :12100

Boxplots

  • Alimentos: La mayoría de los visitantes gastaron entre $750 y $1000 en alimentos, con una mediana cercana a los $800. No se observan valores atípicos significativos por encima del bigote superior.

  • Alojamiento: El 50% central de los visitantes gastó entre aproximadamente $1500 y $3000 en alojamiento, con una mediana alrededor de los $2000. Hay algunos valores atípicos por debajo del bigote inferior, lo que sugiere que algunos visitantes encontraron opciones de alojamiento considerablemente más económicas.

  • Boletos: La mayoría de los visitantes gastaron entre $3000 y $4000 en sus boletos, con una mediana justo por encima de los $3000. No se observan valores atípicos significativos.

  • Días: El 50% de los visitantes asistió entre 2 y 3 días, con una mediana de 2 días. Hay algunos visitantes que solo asistieron 1 día.

  • Gasto Total: El 50% central de los visitantes tuvo un gasto total entre aproximadamente $6500 y $10000, con una mediana cercana a los $8000. Se observan algunos valores atípicos por encima del bigote superior, lo que indica que hubo visitantes con gastos totales considerablemente más altos.

  • Transporte: La mayoría de los visitantes gastaron entre $500 y $650 en transporte, con una mediana alrededor de los $500. No se observan valores atípicos significativos.

df_palnorte_numeric <- df_palnorte[sapply(df_palnorte, is.numeric)]
library(reshape2)
df_long2 <- df_palnorte_numeric %>%
  pivot_longer(cols = everything(), names_to = "variable", values_to = "valor")

# Crear los boxplots con escalas independientes en el eje Y
ggplot(df_long2, aes(x = "", y = valor)) +
  geom_boxplot(fill = "lightblue", color = "darkblue") +
  facet_wrap(~ variable, scales = "free_y") +
  labs(title = "Boxplots por variable numérica",
       x = NULL,
       y = NULL) +
  theme_minimal()

Matriz de Correlación

Las correlaciones más fuertes con el gasto total son el alojamiento, los boletos y el número de días de asistencia. Esto sugiere que estas variables son los principales impulsores del gasto total de los visitantes. Mientras que, el transporte muestra correlaciones débiles con todas las demás variables de gasto:

  • Días vs. Gasto Total (0.78): Esta es la correlación más fuerte. Indica que existe una relación positiva muy marcada entre el número de días que un visitante asiste al festival y el gasto total que realiza. Cuantos más días permanezca alguien en el Tecate Pal Norte, significativamente mayor será su gasto total. Esto es bastante lógico, ya que al estar más tiempo, incurre en gastos de alimentación, transporte (si aplica), y posiblemente más en otras categorías.

  • Alojamiento vs. Gasto Total (0.75): Esta es la segunda correlación más fuerte. Muestra una relación positiva considerable entre el gasto en alojamiento y el gasto total. Aquellos visitantes que invierten más en su hospedaje tienden a tener un gasto total mucho mayor. El alojamiento suele ser uno de los rubros más costosos en un viaje, por lo que esta fuerte correlación tiene sentido.

  • Boletos vs. Gasto Total (0.72): Existe una correlación positiva también fuerte entre el gasto en boletos y el gasto total. El costo de los boletos para el festival representa una parte importante del presupuesto general del evento. Por lo tanto, los visitantes que adquieren boletos más caros (posiblemente con más beneficios o para más días, aunque esto último se ve reflejado en la correlación de “días”) tienden a tener un gasto total mayor.

library(corrplot)
cor_matrix3 <- cor(df_palnorte_numeric, use = "complete.obs")

corrplot(
  cor_matrix3,
  method = "color",                # Método de visualización
  type = "upper",                  # Mostrar solo la parte superior de la matriz
  order = "hclust",                # Ordenar variables por agrupamiento jerárquico
  tl.col = "black",                # Color de las etiquetas de texto
  tl.cex = 0.6,                    # Tamaño del texto de las etiquetas
  tl.srt = 45,                     # Rotación de las etiquetas
  addCoef.col = "black",           # Color de los coeficientes de correlación
  number.cex = 0.6,                # Tamaño de los coeficientes de correlación
  col = colorRampPalette(c("red", "white", "blue"))(200)  # Paleta de colores
)

Distribución por Edad

Se observa que el mercado dominante del festival son jóvenes adultos, especialmente aquellos entre 26 y 35 años. Esto puede guiar decisiones de marketing, alianzas comerciales, productos y experiencias enfocadas a ese rango.

  • La mayoría de los asistentes al festival se encuentra en el rango de edad entre 26 y 35 años, con más de 40 registros.

  • El segundo grupo más común es el de 18 a 25 años, seguido por 36 a 50 años.

  • El grupo menores de 18 años representa una proporción casi insignificante.

options(scipen = 999)

ggplot(df_palnorte, aes(`¿Cuál es su rango de edad? (Pregunta demográfica)`)) +
  geom_bar(fill = "steelblue") +
  labs(title = "Distribución por Edad", x = "Rango de Edad", y = "Cantidad")

Gasto Total Estimado por Rango de Edad (Boxplot)

Se observa que aunque los adultos jóvenes (26-35) son el grupo más numeroso, los de 36-50 años podrían representar un mercado estrella si se consideran las capacidades de gasto. Esto permite perfilar estrategias premium o VIP para ese rango.

  • El rango de 36 a 50 años tiene una mediana de gasto más alta, indicando que aunque no son mayoría, suelen gastar más que otros grupos.

  • El grupo de 18 a 25 años muestra una distribución amplia, algunos gastan mucho, pero la mediana es menor.

  • Menores de 18 años tienen un gasto total bajo y homogéneo (sin dispersión visible).

ggplot(df_palnorte, aes(x = `¿Cuál es su rango de edad? (Pregunta demográfica)`, y = gasto_total)) +
  geom_boxplot(fill = "forestgreen", alpha = 0.7) +
  labs(title = "Gasto Total Estimado por Rango de Edad", x = "Edad", y = "Gasto Total (MXN)") +
  scale_y_continuous(labels = label_comma()) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Gasto Total según Días de Asistencia (Boxplot con puntos y medias)

La variable “días de asistencia” es clave para predecir el valor del cliente. Quienes van más días tienden a gastar más, por lo que incentivar estancias de 3 días puede aumentar ingresos.

  • A mayor cantidad de días asistidos, mayor es el gasto total promedio y mediano.

  • Quienes van 1 día gastan menos, y también tienen menor variabilidad.

  • Los que asisten 3 días son los que más gastan, con una distribución más amplia, y su media (punto rojo) es la más elevada.

ggplot(df_palnorte, aes(x = as.factor(dias), y = gasto_total)) +
  geom_boxplot(fill = "dodgerblue", alpha = 0.6, outlier.shape = NA) +
  geom_jitter(width = 0.2, alpha = 0.4, color = "black") +
  stat_summary(fun = mean, geom = "point", shape = 18, size = 3.5, color = "red") +
  labs(title = "Gasto Total según Días de Asistencia", x = "Días", y = "Gasto Total (MXN)") +
  scale_y_continuous(labels = label_comma()) +
  theme_minimal()

Análisis Exploratorio Espacial de los Datos - ESDA

Hoteles

1. CONDICIÓN ACTUAL

PERFIL DE LA OFERTA HOTELERA DE LAS CIUDADES DE MONTERREY Y SAN PEDRO

Según datos del Directorio Estadístico Nacional de Unidades Económicas (DENUE), hasta el año 2024 se registran 174 hoteles en los municipios de Monterrey y San Pedro Garza García, de los cuales el 89% se concentra en Monterrey. Tan solo en 2024, se sumaron 15 hoteles nuevos a la oferta de la región.

A nivel estatal, el DENUE reporta 327 hoteles en todo Nuevo León, mientras que la Secretaría de Turismo estatal indica que, en promedio, se contabilizaron 528,627 habitaciones registradas mensualmente durante el año. Esto implica que el 53% de la infraestructura hotelera del estado se ubica en Monterrey y San Pedro. Si extrapolamos ese mismo porcentaje al número de habitaciones, se estima que estos dos municipios concentran alrededor de 280,172 habitaciones, consolidándose como el eje central de la oferta hotelera en el estado.

hoteles_acumulados <- hoteles_danue %>%
  group_by(Municipio, Ano_Inco) %>%
  summarise(hoteles_nuevos = n(), .groups = "drop") %>%
  complete(Municipio, Ano_Inco = full_seq(Ano_Inco, 1), fill = list(hoteles_nuevos = 0)) %>%
  arrange(Municipio, Ano_Inco) %>%
  group_by(Municipio) %>%
  mutate(hoteles_acumulados = cumsum(hoteles_nuevos)) %>%
  ungroup()

resumen_2024 <- hoteles_acumulados %>%
  filter(Ano_Inco == 2024) %>%
  group_by(Municipio) %>%
  summarise(
    Hoteles_Nuevos = sum(hoteles_nuevos),
    Hoteles_Acumulados = sum(hoteles_acumulados),
    .groups = "drop"
  ) %>%
  bind_rows(
    summarise(., 
              Municipio = "Total",
              Hoteles_Nuevos = sum(Hoteles_Nuevos),
              Hoteles_Acumulados = sum(Hoteles_Acumulados))
  )

#TABLA DE RESUMEN
resumen_2024 %>%
  gt() %>%
  tab_header(
    title = "Hoteles en Monterrey y San Pedro",
    subtitle = "hasta el 2024 en DENUE"
  ) %>%
  fmt_number(
    columns = c(Hoteles_Nuevos, Hoteles_Acumulados),
    decimals = 0
  ) %>%
  tab_style(
    style = cell_text(weight = "bold"),
    locations = cells_body(rows = Municipio == "Total")
  ) %>%
  cols_label(
    Municipio = "Ciudad",
    Hoteles_Nuevos = "Nuevos Registros",
    Hoteles_Acumulados = "Total Registrados"
  )
Hoteles en Monterrey y San Pedro
hasta el 2024 en DENUE
Ciudad Nuevos Registros Total Registrados
Monterrey 15 155
San Pedro Garza GarcÌa 0 19
Total 15 174
#GRÁFICO DE BARRAS
cuartos_por_ano <- hoteles_bd %>%
  group_by(año) %>%
  summarise(Cuartos_Registrados = (sum(Cuartos_Registrados)* 0.53)/12, .groups = "drop")

hoteles_por_ano <- hoteles_acumulados %>%
  group_by(Ano_Inco) %>%
  summarise(Hoteles_Acumulados = sum(hoteles_acumulados), .groups = "drop") %>%
  rename(año = Ano_Inco)

datos_combinados <- left_join(cuartos_por_ano, hoteles_por_ano, by = "año") %>%
  filter(año >= 2014, año <= 2024)

# Gráfico con escala real de cuartos y eje derecho para hoteles
max_cuartos <- max(datos_combinados$Cuartos_Registrados, na.rm = TRUE)
max_hoteles <- max(datos_combinados$Hoteles_Acumulados, na.rm = TRUE)
factor_escala <- max_cuartos / max_hoteles


ggplot(datos_combinados, aes(x = año)) +
  geom_col(aes(y = Cuartos_Registrados), fill = "grey", width = 0.6) +
  geom_line(aes(y = Hoteles_Acumulados * factor_escala), color = "darkblue", linewidth = 1.2) +
  geom_point(aes(y = Hoteles_Acumulados * factor_escala), color = "darkblue", size = 2) +
  scale_y_continuous(
    name = "Cuartos Registrados Mensualmente",
    labels = label_comma(),
    sec.axis = sec_axis(~ . / factor_escala, name = "Hoteles", labels = label_comma())
  ) +
  scale_x_continuous(breaks = 2014:2024) +
  labs(
    title = "Evolución de Cuartos y Hoteles Registrados",
    subtitle = "En Monterrey y San Pedro (2014 - 2024)",
    x = "Año"
  ) +
  geom_text(
  aes(y = Cuartos_Registrados, label = scales::comma(Cuartos_Registrados)),
  vjust = -1.5,
  size = 2
  ) + 
  geom_text(
  aes(y = Hoteles_Acumulados * factor_escala, label = Hoteles_Acumulados),
  vjust = 2.3,
  size = 3,
  color = "darkblue",
  fontface = "bold"
  ) + 
  theme_minimal() + 
  theme(
    panel.grid = element_blank()
  )

La cantidad de habitaciones registradas mensualmente ha mostrado una tendencia positiva constante desde 2014. Por otro lado, el número de hoteles en estas ciudades también ha incrementado, con un notable crecimiento del 37% entre 2018 y 2020, en un periodo de solo dos años. Sin embargo, se observa que la cantidad de habitaciones creció únicamente un 10% durante el mismo periodo, lo que podría indicar que los nuevos hoteles no han sido de gran tamaño. Además, en ambas variables se percibe un estancamiento en el crecimiento durante los años de la pandemia de COVID-19.

CRECIMIENTO DE LA OFERTA HOTELERA POR CIUDAD MONTERREY Y SAN PEDRO

hoteles_por_ciudad <- hoteles_acumulados %>%
  filter(Ano_Inco %in% c(2014, 2024)) %>%
  group_by(Ano_Inco, Municipio) %>%
  summarise(hoteles = sum(hoteles_acumulados), .groups = "drop")

crecimiento_hoteles_por_ciudad <- 
  ggplot(hoteles_por_ciudad, aes(x = factor(Ano_Inco), y = hoteles, fill = Municipio)) +
  geom_col(width = 0.8) +
  coord_flip() +
  labs(
    title = "Crecimiento de Hoteles por Ciudad",
    subtitle = "Del 2014 al 2024",
    x = "Año",
    y = "Cantidad de Hoteles",
    fill = "Ciudad"
  ) +
  scale_fill_manual(values = c("Monterrey" = "#1f77b4", "San Pedro Garza GarcÌa" = "lightblue")) +
  theme_minimal(base_size = 10) + 
  theme(panel.grid = element_blank()) +
  theme(
    axis.text.y = element_text(),
    legend.position = "bottom") +
  geom_text(
  aes(y = hoteles, label = scales::comma(hoteles)),
  vjust = 0,
  hjust = 1.5,
  size = 3,
  fontface = "bold"
  ) 

hoteles_crecimiento <- hoteles_por_ciudad %>%
  filter(Ano_Inco %in% c(2014, 2024)) %>%
  tidyr::pivot_wider(
    names_from = Ano_Inco,
    values_from = hoteles,
    names_prefix = "A"
  ) %>%
  mutate(
    crecimiento_abs = A2024 - A2014,
    crecimiento_pct = (crecimiento_abs / A2014) * 100
  )

tabla_crecimiento_hoteles <- 
  hoteles_crecimiento %>%
  gt() %>%
  cols_label(
    Municipio = "Ciudad",
    A2014 = "2014",
    A2024 = "2024",
    crecimiento_abs = "Crecimiento Absoluto",
    crecimiento_pct = "Crecimiento (%)"
  ) %>%
  fmt_number(
    columns = c(crecimiento_pct),
    decimals = 1,
    suffixing = FALSE
  ) %>%
  tab_header(
    title = "Crecimiento de Hoteles por Ciudad (2014–2024)"
  ) %>%
  fmt_number(
    columns = c(A2014, A2024, crecimiento_abs),
    decimals = 0
  ) %>%
  grand_summary_rows(
    columns = c(A2014, A2024, crecimiento_abs),
    fns = list(Total = ~sum(.)),
    formatter = fmt_number,
    decimals = 0
  )%>%
  tab_options(
    table.font.size = 8,
    column_labels.font.size = 10
  )



crecimiento_hoteles_por_ciudad / tabla_crecimiento_hoteles

En cuanto al crecimiento de la oferta hotelera, ambas ciudades han experimentado un aumento significativo, aunque de manera desigual. Monterrey ha registrado un crecimiento del 64.5% en los últimos 10 años, lo que se traduce en un incremento de 61 unidades hoteleras. En contraste, San Pedro, aunque solo ha sumado 13 unidades, ha experimentado un crecimiento porcentual mucho más alto, alcanzando un 216%.

II. PERFIL DE LA OFERTA HOTELERA

El objetivo de la siguiente sección es entender la distribución espacial, de calidad y de costo de los hoteles en Monterrey y San Pedro. Detectar concentraciones geográficas de hoteles de lujo, patrones de precio a través de diferentes zonas e identificar áreas con potencial para el desarrollo de infraestructura turística.

Para medir las características cualitativas de los hoteles hacemos uso de información publicada en el sitio web Google Maps. Al ser una fuente de información no oficial ocurren discrepancias entre la cantidad de instituciones hoteleras registradas en el DENUE y Google Maps. Por lo tanto se entiende la ligera diferencia entre los datos presentados en la sección anterior.

# DISTRIBUCIÓN DE HOTELES POR CATEGORÍA

tabla_categorias <- hoteles %>%
  filter(Category %in% c("1", "2", "3", "4", "5", "Guest House / AirBnB", "Motel", NA)) %>%
  group_by(City, Category) %>%
  summarise(Hoteles = n(), .groups = "drop") %>%
  pivot_wider(
    names_from = City,
    values_from = Hoteles,
    values_fill = 0
  ) %>%
  arrange(as.numeric(Category))

tabla_categorias <- tabla_categorias %>%
  mutate(Total = rowSums(across(where(is.numeric))))

#Tabla
tabla_categorias %>%
  gt() %>%
  cols_label(
    Category = "Estrellas"
  ) %>%
  tab_header(
    title = "Cantidad de Hoteles por Categoría y Ciudad"
  ) %>%
  tab_style(
    style = cell_text(weight = "bold"),
    locations = cells_body(columns = "Total")
  )
Cantidad de Hoteles por Categoría y Ciudad
Estrellas Monterrey SanPedro Total
1 8 0 8
2 34 0 34
3 71 5 76
4 33 0 33
5 2 7 9
Guest House / AirBnB 14 0 14
Motel 13 1 14
NA 27 0 27

La oferta hotelera de ambas ciudades incluye una amplia variedad de tipos de alojamiento clasificados por categoría. Una de las clasificaciones más comunes es la basada en estrellas, donde 1 estrella corresponde a alojamientos tipo hostal y 5 estrellas a hoteles de lujo. Adicionalmente, en plataformas como Google Maps también pueden aparecer otras categorías como Guest House —propiedades privadas que ofrecen hospedaje por periodos cortos, incluyendo algunas registradas en plataformas como Airbnb—, Moteles, y hoteles sin una categoría específica (NA). Para efectos del presente análisis, únicamente se considerarán los hoteles clasificados dentro de la escala de 1 a 5 estrellas.

hotels_summary <- hoteles %>%
  filter(Category %in% c("1", "2", "3", "4", "5")) %>%
  group_by(City) %>%
  summarise(average_rating = mean(rating, na.rm = TRUE),
            number_of_hotels = n())

# Calificación Promedio
ggplot(hotels_summary, aes(x = reorder(City, -average_rating), y = average_rating, fill = City)) +
  geom_col(width = 0.8) +
  labs(title = "Calificación Promedio por Ciudad",
       x = "Ciudad", y = "Calificación",
       fill = "Ciudad") +
  scale_fill_manual(values = c("Monterrey" = "#1f77b4", "SanPedro" = "lightblue")) +
  scale_y_continuous(limits = c(0, 5)) +
  theme_minimal(base_size = 10) + 
  theme(panel.grid = element_blank()) +
  theme(axis.text.y = element_text(),
    legend.position = "none") +
  geom_text(aes(label = round(average_rating, 2)), vjust = -0.5, fontface = "bold")

La mayor concentración hotelera de ambos municipios se encuentra en el centro de la ciudad de Monterrey, donde se ubica el núcleo principal de alojamientos. Un segundo punto de concentración se localiza hacia el este del centro, mientras que un tercer foco relevante se encuentra al suroeste, en la zona de Valle Oriente, dentro del municipio de San Pedro.

Si bien la concentración de la oferta hotelera se ubica principalmente en el centro de la ciudad, esta se compone mayoritariamente por hoteles de baja categoría, siendo los establecimientos de 2 y 3 estrellas los más frecuentes. En contraste, los hoteles de mayor categoría tienden a localizarse en zonas periféricas al centro, destacando especialmente su concentración en el municipio de San Pedro y en el suroeste del área metropolitana. Los hoteles de lujo —clasificados como categoría 5 estrellas— se agrupan predominantemente en la zona de Valle Oriente, en San Pedro. Las únicas excepciones a esta tendencia son el Hotel Safi Centro y el Hotel Krystal Monterrey, ambos ubicados en el centro de Monterrey.

Para comparar las distintas categorías hoteleras, se analizó su distribución en cuanto al precio promedio por noche y las calificaciones registradas en Google Maps. La tarifa considerada fue para una noche de fin de semana en abril, para dos personas.

categorias <- hoteles %>%
  filter(Category %in% c("1", "2","3", "4", "5"))

# BOXPLOT PRECIO POR CATEGORÍA
p <- ggplot(categorias, aes(x = as.factor(Category), y = price)) +
  geom_boxplot(fill = "grey") +
  theme_minimal() +
  theme(panel.grid = element_blank())+
  labs(title = "Distribución de precio por categoría del hotel",
       x = "Categoría del hotel", y = "Precio por noche")

outliers_p <- categorias %>%
  group_by(Category) %>%
  filter(
    price < quantile(price, 0.25, na.rm = TRUE) - 1.5 * IQR(price, na.rm = TRUE) |
    price > quantile(price, 0.75, na.rm = TRUE) + 1.5 * IQR(price, na.rm = TRUE)
  )

p_ <- p + 
  geom_text(data = outliers_p, aes(label = name), 
            vjust = -0.5, color = "darkblue", size = 2) +
  labs(title = "Distribución de Precio por Categoría")

# BOXPLOT RATINGS POR CATEGORÍA
q <- ggplot(categorias, aes(x = as.factor(Category), y = rating)) +
  geom_boxplot(fill = "grey") +
  theme_minimal() +
  theme(panel.grid = element_blank()) +
  labs(title = "Distribution of Ratings by Hotel Category",
       x = "Categoría", y = "Calificación de Usuarios")

outliers_q <- categorias %>%
  group_by(Category) %>%
  filter(
    rating < quantile(rating, 0.25, na.rm = TRUE) - 1.5 * IQR(rating, na.rm = TRUE) |
    rating > quantile(rating, 0.75, na.rm = TRUE) + 1.5 * IQR(rating, na.rm = TRUE)
  )

q_ <- q + 
  geom_text(data = outliers_q, check_overlap = TRUE, aes(label = name), 
            vjust = -0.5, color = "darkblue", size = 2) +
  labs(title = "Distribución de Calificaciones por Categoría")

p_ <- p_ + coord_cartesian(clip = "off")
q_ <- q_ + coord_cartesian(clip = "off")

p_ / q_

En cuanto a precios, se observa que los hoteles de categorías 1 y 2 estrellas presentan una baja dispersión, es decir, poca variabilidad entre los precios. Esto puede deberse a una clientela altamente sensible al precio, que prioriza el costo como factor principal al momento de elegir alojamiento. Y el cual puede ser indicio de un mercado muy competitivo centrado en el precio.

Por el contrario, las categorías 3, 4 y 5 estrellas muestran una mayor dispersión en los precios. En particular, los hoteles de 4 estrellas tienden a concentrarse en un rango de entre $1,500 y $2,200 pesos por noche, aunque presentan también la mayor cantidad de valores atípicos. Esto sugiere una oferta más diversa dentro de esa categoría, posiblemente debido a diferencias en amenidades, ubicación, servicios adicionales o posicionamiento de marca.

Respecto a las calificaciones en Google Maps, la distribución se comporta de forma inversa. Los hoteles de baja categoría presentan mayor variabilidad en las evaluaciones de los usuarios. Esto podría reflejar una relación precio-calidad inconsistente: algunos hoteles económicos logran sorprender positivamente, mientras que otros generan experiencias insatisfactorias, resultando en una mayor heterogeneidad de opiniones.

¿COMO VARÍA EL PRECIO DE UNA NOCHE PARA DOS PERSONAS POR ZONA?

Nota: Se utilizó la noche del 27 al 28 de Abril 2025. La búsqueda se realizó el 26 de Abril.

# Calculate average price per city
precio_por_ciudad <- hoteles %>%
  group_by(City) %>%
  summarise(avg_price = mean(price, na.rm = TRUE)) 

print(precio_por_ciudad)
## # A tibble: 2 × 2
##   City      avg_price
##   <fct>         <dbl>
## 1 Monterrey     1822.
## 2 SanPedro      3109.
# Plot
ggplot(precio_por_ciudad, aes(x = City, y = avg_price, fill = City)) +
  geom_col(width = 0.8) +
  labs(title = "Precio promedio por ciudad", y = "Tarifa promedio", x = "Ciudad") +
  scale_fill_manual(values = c("Monterrey" = "#1f77b4", "SanPedro" = "lightblue")) +
  theme_minimal() +
  theme(panel.grid = element_blank(),
        axis.text.y = element_text(),
        legend.position = "none") +
  geom_text(aes(label = round(avg_price)), vjust = -0.5, fontface = "bold")

Explorando más en el precio, podemos ver primeramente una clara diferencia en los precios promedios por ciudad. La noche promedio en Monterrey es cercana a la mitad de una noche en San Pedro. Lo cual va alineado, ya que como presentamos anteriormente en los hoteles de bajas categorías (y precios) predominan en Monterrey. Mientras los hoteles de lujo se concentran en San Pedro.

hotels_clean <- hoteles %>%
  select(name, latitude, longitude, price, rating) %>%
  drop_na()


# 1. Objeto SF
hotels_sf <- st_as_sf(hotels_clean, coords = c("longitude", "latitude"), crs = 4326)

# 2. Bounding box de monterrey (mapa)
bbox <- st_bbox(hotels_sf)

# 3. Grid (100x100)
grid <- st_make_grid(hotels_sf, cellsize = 0.01, square = TRUE)  #1km resolution
grid_sf <- st_sf(geometry = grid)

# 4. Asignar hoteles al grid
hotels_joined <- st_join(hotels_sf, grid_sf)

# 5. Conteo hoteles & precio promedio por celda
summary_grid <- hotels_joined %>%
  group_by(geometry) %>%
  summarise(
    count = n(),
    avg_price = mean(price, na.rm = TRUE)
  )

summary_grid_rating <- hotels_joined %>%
  group_by(geometry) %>%
  summarise(
    count = n(),
    avg_rating = mean(rating, na.rm = TRUE)
  )


# 6. Celdas sin hoteles
summary_grid_full <- grid_sf %>%
  st_join(summary_grid)

summary_grid_full_rating <- grid_sf %>%
  st_join(summary_grid_rating)


price_map <- ggmap(monterrey_map) +
  geom_sf(data = summary_grid_full, 
          aes(fill = avg_price), 
          color = NA, 
          inherit.aes = FALSE, 
          alpha = 0.7) +
  scale_fill_distiller(
    palette = "Blues",
    direction = -1,
    na.value = NA,
    name = "Precio promedio") +
  theme_void() +
  labs(
    title = "Precio Promedio por Noche"
  ) + 
  theme(legend.position = "bottom")

## ---
ratings_map <- ggmap(monterrey_map) +
  geom_sf(data = summary_grid_full_rating, 
          aes(fill = avg_rating), 
          color = NA, 
          inherit.aes = FALSE, 
          alpha = 0.7) +
  scale_fill_distiller(
    palette = "Blues",
    direction = -1,
    na.value = NA,
    name = "Rating promedio") +
  theme_void() +
  labs(
    title = "Rating Promedio"
  ) + 
  theme(legend.position = "bottom")

price_map + plot_spacer() +  ratings_map +
  plot_layout(widths = c(1, 0.05, 1))

Airbnb

Ubicaciones Airbnb

El mapa muestra la distribución de alojamientos de Airbnb en la Zona Metropolitana de Monterrey. Los puntos de diferentes colores indican la antigüedad de los anuncios:

  • Puntos Azules: Representan los nuevos alojamientos de Airbnb. Su dispersión nos da una idea de las áreas donde recientemente se han incorporado más opciones de hospedaje a través de la plataforma.

  • Puntos Rojos: Señalan los alojamientos de Airbnb más antiguos. Su ubicación nos muestra las zonas pioneras o con una presencia más establecida de este tipo de hospedaje.

Por lo tanto, se observa que la actividad de Airbnb en la Zona Metropolitana de Monterrey está bien establecida en los municipios centrales y está experimentando una expansión hacia áreas circundantes:

  • Concentración: Se observa una alta concentración de ambos tipos de Airbnb (nuevos y viejos) en el corazón de la zona metropolitana, abarcando principalmente los municipios de Monterrey, San Pedro Garza García y Guadalupe. Esto sugiere que estas áreas son los centros de mayor actividad turística y demanda de alojamiento a través de Airbnb.

  • Expansión: Los puntos azules (nuevos Airbnbs) parecen extenderse hacia General Escobedo, Apodaca y hacia el oriente de Guadalupe, lo que podría indicar una expansión de la oferta de Airbnb hacia estas zonas más periféricas o en desarrollo; lo cual odría estar impulsado por un aumento de la demanda en estas áreas o por la disponibilidad de nuevas propiedades.

  • Presencia Establecida: Los puntos rojos (viejos Airbnbs) parecen tener una presencia particularmente fuerte en San Pedro Garza García y ciertas zonas céntricas de Monterrey; reflejando la popularidad inicial de Airbnb en estas áreas y la continuidad de los alojamientos que han estado en la plataforma durante más tiempo.

library(leaflet)
library(sf) 
library(leaflet)
library(sf)
library(dplyr)

# Leer el GeoJSON
zonas <- st_read("nl_map.geojson")
## Reading layer `nl_map' from data source `C:\Users\AVRIL\Downloads\nl_map.geojson' using driver `GeoJSON'
## Simple feature collection with 51 features and 6 fields
## Geometry type: POLYGON
## Dimension:     XY
## Bounding box:  xmin: -101.2068 ymin: 23.16268 xmax: -98.42158 ymax: 27.79914
## Geodetic CRS:  WGS 84
# Catálogo de municipios con zonas
catalogo_municipios <- data.frame(
  CODELAG = c(2199:2249),
  municipio = c("Abasolo", "Agualeguas", "Los Aldamas", "Allende", "Anáhuac", "Apodaca",
                "Aramberri", "Bustamante", "Cadereyta Jiménez", "El Carmen", "Cerralvo",
                "Ciénega de Flores", "China", "Doctor Arroyo", "Doctor Coss", "Doctor González",
                "Galeana", "García", "San Pedro Garza García", "General Bravo", "General Escobedo",
                "General Terán", "General Treviño", "General Zaragoza", "General Zuazua", 
                "Guadalupe", "Hidalgo", "Higueras", "Hualahuises", "Iturbide", "Juárez", 
                "Lampazos de Naranjo", "Linares", "Marín", "Melchor Ocampo", "Mier y Noriega",
                "Mina", "Montemorelos", "Monterrey", "Parás", "Pesquería", "Los Ramones", 
                "Rayones", "Sabinas Hidalgo", "Salinas Victoria", "San Nicolás de los Garza", 
                "Hidalgo", "Santa Catarina", "Santiago", "Vallecillo", "Villaldama"),
  zona = c("Otra", "Otra", "Otra", "Sur", "Norte", "Norte", "Otra", "Norte", "Este", "Norte", 
           "Norte", "Norte", "Otra", "Otra", "Otra", "Otra", "Sur", "Oeste", "Oeste", "Este",
           "Norte", "Sur", "Otra", "Sur", "Norte", "Este", "Norte", "Otra", "Otra", "Sur",
           "Este", "Norte", "Sur", "Norte", "Otra", "Otra", "Norte", "Sur", "Oeste", "Norte",
           "Este", "Sur", "Norte", "Norte", "Norte", "Oeste", "Norte", "Oeste", "Sur", "Norte",
           "Norte")
)

# Unir zonas con municipios
zonas <- dplyr::left_join(zonas, catalogo_municipios, by = "CODELAG")

# Paleta de colores para zonas (se mantiene como un solo color)
colores_zonas <- colorFactor(
  palette = c("purple"),
  domain = zonas$zona
)

df_air$color <- ifelse(df_air$Meses_Antiguedad == 0, "red", "blue")

# Mapa interactivo
leaflet() %>%
  addTiles() %>%
  setView(lng = -100.3161, lat = 25.6866, zoom = 11.5) %>%
  addPolygons(data = zonas,
              fillColor = ~colores_zonas(zona),
              fillOpacity = 0.2,
              weight = 2,
              color = "black",
              popup = ~paste("Municipio:", municipio, "<br>Zona:", zona)) %>%
  addCircleMarkers(
    data = df_air,
    lng = ~Longitud, lat = ~Latitud,
    radius = 6,
    color = ~color,
    stroke = FALSE, fillOpacity = 0.8,
    popup = ~paste("Precio:", Precio, "<br>",
                   "Nombre:", Nombre, "<br>",
                   "Calificación:", Calificación, "<br>",
                   "Meses de Antigüedad:", Meses_Antiguedad)
  ) %>%
  addControl("<strong>Ubicaciones de Airbnb en la Zona Metropolitana de Monterrey</strong>", position = "topright")

Precios Airbnb

El mapa de calor de precios de Airbnb en la Zona Metropolitana de Monterrey revela una clara segmentación geográfica de los precios. Las zonas con mayor valor inmobiliario y actividad económica (San Pedro, centro y sur de Monterrey) tienden a concentrar los alojamientos de mayor precio, mientras que las áreas más alejadas del centro o con un desarrollo socioeconómico diferente ofrecen una mayor proporción de alojamientos más económicos.

  • Precios Altos: Las áreas con mayor densidad de precios altos (rojo/naranja) se localizan principalmente en San Pedro Garza García, ya que San Pedro es conocido por ser un municipio con un alto nivel socioeconómico y, por lo tanto, es probable que los precios de los alojamientos sean más elevados. Asimismo, la Zona Centro y Sur de Monterrey tiene una concentración de precios altos lo cual podría deberse a la proximidad a atractivos turísticos, centros de negocios y zonas residenciales de mayor valor. Sin embargo, existen puntos aislados que podrían corresponder a alojamientos de lujo o ubicados en zonas específicas de interés.

  • Precios Medios: Las zonas con una densidad media de precios (amarillo/verde) son más extensas y se encuentran rodeando las áreas de precios altos. Monterrey tiene amplias zonas fuera del centro y del sur. También, Guadalupe principalmente en la parte oeste y central del municipio. De igual manera, San Nicolás de los Garza, General Escobedo y Apodaca cuentan con algunas zonas con una densidad media de precios.

  • Precios Bajos: Se observan concentraciones de precios más bajos en la periferia de la ZMM, como en la parte norte de General Escobedo, el oriente de Apodaca y la zona oriente de Guadalupe.Asimismo, en áreas con menor desarrollo turístico o comercial lo que implica una menor demanda o una oferta de alojamientos más sencillos.

library(leaflet)
library(leaflet.extras)

pal <- colorNumeric(
  palette = "YlOrRd", 
  domain = NULL
)
leaflet(data = df_air) %>%
  addTiles() %>%
  setView(lng = -100.3161, lat = 25.6866, zoom = 12) %>%
  addHeatmap(
    lng = ~Longitud,
    lat = ~Latitud,
    intensity = ~Precio,   # Aquí se usa el precio para la intensidad del calor
    blur = 20,
    max = 0.05,
    radius = 15
  ) %>%
  addControl("<strong>Precios de Airbnb en la Zona Metropolitana de Monterrey</strong>", position = "topright")

Calificación Airbnb

Se obsrva que hay varias zonas dentro de la Zona Metropolitana de Monterrey donde los alojamientos de Airbnb tienden a tener mejores calificaciones. Algunas de estas áreas parecen estar ubicadas en:

  • San Nicolás de los Garza: Se aprecia una zona de alta calificación en el centro de este municipio.

  • Monterrey: Hay varias áreas con alta calificación distribuidas dentro de Monterrey, incluyendo una zona notable hacia el poniente y otra más hacia el sur.

  • San Pedro Garza García: La zona más occidental que se alcanza a ver muestra también una concentración de alojamientos con buenas calificaciones.

leaflet(data = df_air) %>%
  addTiles() %>%
  setView(lng = -100.3161, lat = 25.6866, zoom = 12) %>%
  addHeatmap(
    lng = ~Longitud,
    lat = ~Latitud,
    intensity = ~Calificación,   # Aquí se usa el precio para la intensidad del calor
    blur = 20,
    max = 0.05,
    radius = 15
  ) %>%
  addControl("<strong>Calificación de Airbnb en la Zona Metropolitana de Monterrey</strong>", position = "topright")

Industria Hospitalidad (Airbnb & Hoteles)

library(dplyr)
library(leaflet)
library(leaflet.extras)

df_air_limp <- df_air %>%
  select(Nombre, Latitud, Longitud) %>%
  mutate(Tipo = "Airbnb")

hoteles_limp <- hoteles_danue %>%
  transmute(
    Nombre = hotel,
    Latitud = Latitud,
    Longitud = Longitud,
    Tipo = "Hotel"
  )

lugares_turisticos <- bind_rows(df_air_limp, hoteles_limp)

puntos_interes <- data.frame(
  Nombre = c("Estadio BBVA", "Parque Fundidora", "Aeropuerto de Monterrey"),
  Latitud = c(25.6714, 25.6758, 25.7785),
  Longitud = c(-100.2440, -100.2893, -100.1070),
  Tipo = c("Punto de Interés", "Punto de Interés", "Punto de Interés")
)

lugares_turisticos_todos <- bind_rows(lugares_turisticos, puntos_interes)

# Crear el mapa
leaflet(lugares_turisticos_todos) %>%
  addTiles() %>%
  addHeatmap(
    lng = ~Longitud,
    lat = ~Latitud,
    blur = 20,
    max = 0.05,
    radius = 15
  ) %>%
  addCircleMarkers(
    data = subset(lugares_turisticos_todos, Tipo == "Airbnb"),
    lng = ~Longitud,
    lat = ~Latitud,
    radius = 4,
    color = "black",
    fillOpacity = 0.8,
    popup = ~Nombre
  ) %>%
  addCircleMarkers(
    data = subset(lugares_turisticos_todos, Tipo == "Hotel"),
    lng = ~Longitud,
    lat = ~Latitud,
    radius = 4,
    color = "blue",
    fillOpacity = 0.8,
    popup = ~Nombre
  ) %>%
  addCircleMarkers(
    data = subset(lugares_turisticos_todos, Tipo == "Punto de Interés"),
    lng = ~Longitud,
    lat = ~Latitud,
    radius = 8,
    color = "red",
    fillColor = "yellow",
    fillOpacity = 1,
    popup = ~Nombre
  ) %>%
  addControl("<strong>Mapa de Airbnb, Hoteles y Puntos Clave</strong>", position = "topright")

Agrupamiento Espacial

Hay evidencia fuerte de que los hoteles no están distribuidos al azar, sino que tienden a agruparse espacialmente en las coordenadas dadas.

  • Moran I statistic = 0.77: Valor alto y positivo indica fuerte autocorrelación espacial positiva. Esto significa que los lugares que son hoteles tienden a estar cercanos entre sí, formando agrupamientos espaciales.

  • p-value muy pequeño (< 2.2e-16): Es estadísticamente significativo, lo que rechaza la hipótesis nula de ausencia de autocorrelación espacial.

# Crear variable binaria: 1 si es Hotel, 0 si es Airbnb
lugares_turisticos$es_hotel <- ifelse(lugares_turisticos$Tipo == "Hotel", 1, 0)
library(spdep)

coords_24 <- cbind(lugares_turisticos$Longitud, lugares_turisticos$Latitud)

# Elimina puntos duplicados para evitar warnings
coords_24_unicos <- unique(coords_24)
knn5 <- knearneigh(coords_24_unicos, k = 5)
nb5 <- knn2nb(knn5)
listw5 <- nb2listw(nb5, style = "W")
# Filtrar los datos únicos
lugares_unicos <- lugares_turisticos[!duplicated(coords_24), ]
moran.test(lugares_unicos$es_hotel, listw5)
## 
##  Moran I test under randomisation
## 
## data:  lugares_unicos$es_hotel  
## weights: listw5    
## 
## Moran I statistic standard deviate = 26.522, p-value <
## 0.00000000000000022
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      0.7729291450     -0.0025445293      0.0008548914

Distancia a Estadio BBVA & Parque Fundidora

  • La mayoría de los hospedajes se encuentran a entre 7 y 11 km del Estadio BBVA.

  • Los hospedajes están un poco más cerca, en promedio, del Parque Fundidora (media de 7.5 km frente a 10 km para el estadio).

  • Algunos hospedajes están extremadamente lejos (más de 60 km), lo que sugiere que el conjunto de datos incluye ubicaciones posiblemente fuera del área metropolitana de Monterrey.

  • La mediana < media en ambos casos, lo que indica una distribución asimétrica a la derecha: hay algunos hospedajes muy lejanos que elevan el promedio.

library(geosphere)

# Calcular distancia de cada Hospedaje al Estadio BBVA
lugares_turisticos$dist_bbva <- distHaversine(
  matrix(c(lugares_turisticos$Longitud, lugares_turisticos$Latitud), ncol = 2),
  c(-100.2445645730143, 25.669345345107395)
)

# Calcular distancia de cada Hospedaje al Parque Fundidora
lugares_turisticos$dist_fundi <- distHaversine(
  matrix(c(lugares_turisticos$Longitud, lugares_turisticos$Latitud), ncol = 2),
  c(-100.28423348650715, 25.67889304235405)
)
summary(lugares_turisticos$dist_bbva)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   383.3  7201.5  8791.8 10006.0 11549.6 67103.3
summary(lugares_turisticos$dist_fundi)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   604.6  3555.3  6309.9  7557.5  9570.0 64256.2

Mapa de Isocronas

  • El punto de origen, el Estadio BBVA, tiene una conectividad vial buena y distribuida radialmente, alcanzando zonas clave de Monterrey, Guadalupe y parte de San Nicolás.

  • Los negocios turísticos (alojamientos, restaurantes, transporte) ubicados dentro de las isocronas menores podrían tener mayor valor o demanda debido a que se encuentran a menos de 5 o 10 minutos del Estadio BBVA; principalmente, durante el Mundial 2026.

knitr::opts_chunk$set(echo = TRUE)
library(sf)
library(dplyr)
library(leaflet)
library(osrm)
library(osrm)

# Coordenadas del punto de partida (ejemplo: Estadio BBVA)
coords <- c(-100.2445645730143, 25.669345345107395)

# Convertir coordenadas a `sf`
punto_inicio <- st_sf(
  id = "origen",
  geometry = st_sfc(st_point(coords), crs = 4326)
)

# Generar isocronas de 5, 10 y 15 minutos
iso <- osrmIsochrone(loc = punto_inicio, breaks = c(5, 10, 15))

# Reparar geometrías si es necesario
iso <- st_make_valid(iso)

lugares_sf <- st_as_sf(lugares_turisticos, coords = c("Longitud", "Latitud"), crs = 4326)

# Luego sí puedes hacer el join
lugares_con_iso <- st_join(lugares_sf, iso, join = st_within)

# Crear paleta
pal <- colorFactor(palette = "Oranges", domain = iso$isomax)

# Crear mapa
leaflet() %>%
  addTiles() %>%
  addPolygons(data = iso,
              fillColor = ~pal(isomax),
              color = "black",
              weight = 1,
              fillOpacity = 0.4,
              popup = ~paste("Hasta", isomax, "min")) %>%
  addCircleMarkers(data = lugares_con_iso,
                   color = ~pal(isomax),
                   stroke = TRUE,
                   fillOpacity = 1,
                   radius = 6,
                   popup = ~paste0("<b>", Nombre, "</b><br>Tiempo aprox: ", isomax, " min")) %>%
  addMarkers(lng = coords[1], lat = coords[2],
             popup = "Punto de partida") %>%
  addLegend("bottomright", pal = pal, values = iso$isomax,
            title = "Minutos desde el origen", opacity = 0.7)

Predicción Industria Hotelera

Autocorrelación

acf(derrama_ts, main="Autocorrelation - Derrama Económica")

pacf(derrama_ts, main="Partial Autocorrelation -Derrama Económica") #aplicar 1 o 2

adf_ocupacion <- adf.test(df_log$"Derrama_Economica_Est_mdp")
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -0.443   0.517
## [2,]   1 -0.193   0.588
## [3,]   2  0.651   0.831
## [4,]   3  0.713   0.849
## [5,]   4  0.395   0.758
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -1.925   0.357
## [2,]   1 -1.611   0.480
## [3,]   2 -0.583   0.843
## [4,]   3 -0.540   0.858
## [5,]   4 -0.835   0.754
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -3.24  0.0816
## [2,]   1 -2.90  0.1951
## [3,]   2 -1.81  0.6564
## [4,]   3 -1.74  0.6862
## [5,]   4 -2.12  0.5270
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01
print(adf_ocupacion)
## $type1
##      lag        ADF   p.value
## [1,]   0 -0.4425241 0.5165398
## [2,]   1 -0.1927057 0.5884300
## [3,]   2  0.6512135 0.8312844
## [4,]   3  0.7130436 0.8490773
## [5,]   4  0.3954069 0.7576710
## 
## $type2
##      lag        ADF   p.value
## [1,]   0 -1.9251534 0.3568070
## [2,]   1 -1.6109259 0.4800432
## [3,]   2 -0.5828110 0.8428994
## [4,]   3 -0.5403225 0.8578082
## [5,]   4 -0.8349828 0.7544150
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -3.236675 0.08160778
## [2,]   1 -2.904106 0.19511329
## [3,]   2 -1.808621 0.65637655
## [4,]   3 -1.737771 0.68620967
## [5,]   4 -2.115873 0.52700199
# Aucotorrelación serial
lgunj_box_result_hotel <- Box.test(derrama_ts, lag = 5, type = "Ljung-Box")
lgunj_box_result_hotel
## 
##  Box-Ljung test
## 
## data:  derrama_ts
## X-squared = 983.29, df = 5, p-value < 0.00000000000000022
nsdiffs(derrama_ts)
## [1] 0
derrama_diff <- diff(derrama_ts) # Diferencia (d = 1)
adf.test(derrama_diff) #Ya es estacionaria, por ende no se aplica d
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -17.26    0.01
## [2,]   1 -16.72    0.01
## [3,]   2 -11.67    0.01
## [4,]   3  -8.22    0.01
## [5,]   4  -6.53    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -17.26    0.01
## [2,]   1 -16.77    0.01
## [3,]   2 -11.74    0.01
## [4,]   3  -8.28    0.01
## [5,]   4  -6.59    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -17.24    0.01
## [2,]   1 -16.78    0.01
## [3,]   2 -11.77    0.01
## [4,]   3  -8.32    0.01
## [5,]   4  -6.62    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01

ARIMA

mod1 <- arima(derrama_diff, order = c(1, 0, 0))  #  d = 0 porque ya la serie está diferenciada
summary(mod1) #p-value = 2.2e-16 → muy menor a 0.05, los residuos no se comportan como ruido blanco → el modelo no está capturando completamente la estructura de la serie.
## 
## Call:
## arima(x = derrama_diff, order = c(1, 0, 0))
## 
## Coefficients:
##           ar1  intercept
##       -0.1075     7.6542
## s.e.   0.0637     9.6332
## 
## sigma^2 estimated as 28547:  log likelihood = -1643.7,  aic = 3293.4
## 
## Training set error measures:
##                       ME     RMSE      MAE     MPE     MAPE      MASE
## Training set 0.001449583 168.9576 118.4086 210.444 249.2255 0.6696299
##                     ACF1
## Training set -0.03787776
checkresiduals(mod1)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(1,0,0) with non-zero mean
## Q* = 230.01, df = 23, p-value < 0.00000000000000022
## 
## Model df: 1.   Total lags used: 24
#ar1-> coeficiente bajo, lo que sugiere una débil autocorrelación temporal directa.
modelo_auto <- auto.arima(derrama_ts)
summary(modelo_auto)
## Series: derrama_ts 
## ARIMA(2,1,0)(0,0,2)[12] 
## 
## Coefficients:
##           ar1      ar2    sma1    sma2
##       -0.1440  -0.2517  0.2315  0.2043
## s.e.   0.0616   0.0657  0.0683  0.0633
## 
## sigma^2 = 23305:  log likelihood = -1617.03
## AIC=3244.06   AICc=3244.3   BIC=3261.69
## 
## Training set error measures:
##                    ME     RMSE      MAE       MPE    MAPE      MASE
## Training set 7.723387 151.1387 97.70522 -2.637469 13.8319 0.4266687
##                      ACF1
## Training set 0.0003630307
checkresiduals(modelo_auto)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,1,0)(0,0,2)[12]
## Q* = 40.37, df = 20, p-value = 0.004484
## 
## Model df: 4.   Total lags used: 24
#p-value = 0.004484 → menor a 0.05, lo que indica presencia de autocorrelación en los residuos
#No captura tan bien la estacionalidad
library(forecast)

# Lista para guardar resultados
resultados <- list()
modelos_validos <- data.frame()

# Rangos de parámetros 
for (p in 0:2) {
  for (d in 1:1) {
    for (q in 0:2) {
      for (P in 0:2) {
        for (D in 0:1) {
          for (Q in 0:2) {
            orden <- c(p, d, q)
            orden_estacional <- c(P, D, Q)
            try({
              modelo <- arima(derrama_ts,
                              order = orden,
                              seasonal = list(order = orden_estacional, period = 12),
                              method = "ML")
              aic_val <- AIC(modelo)
              modelos_validos <- rbind(modelos_validos, data.frame(
                p, d, q, P, D, Q,
                AIC = aic_val
              ))
              key <- paste0("(", p, ",", d, ",", q, ")(", P, ",", D, ",", Q, ")[12]")
              resultados[[key]] <- modelo
            }, silent = TRUE)
          }
        }
      }
    }
  }
}
# Ordenar por AIC
modelos_validos <- modelos_validos[order(modelos_validos$AIC), ]
head(modelos_validos, 5)
##     p d q P D Q      AIC
## 141 2 1 2 0 1 1 3054.344
## 142 2 1 2 0 1 2 3055.424
## 147 2 1 2 1 1 1 3055.600
## 153 2 1 2 2 1 1 3055.857
## 154 2 1 2 2 1 2 3056.056
mod_sugerido <- arima(derrama_ts, order = c(2,1,2), seasonal = list(order = c(0,1,1), period = 12), transform.pars = FALSE)
summary(mod_sugerido)
## 
## Call:
## arima(x = derrama_ts, order = c(2, 1, 2), seasonal = list(order = c(0, 1, 1), 
##     period = 12), transform.pars = FALSE)
## 
## Coefficients:
##           ar1      ar2      ma1     ma2     sma1
##       -0.0019  -1.0062  -0.0692  0.9063  -1.0000
## s.e.   0.0066   0.0114   0.0350  0.0279   0.0772
## 
## sigma^2 estimated as 17409:  log likelihood = -1521.12,  aic = 3054.24
## 
## Training set error measures:
##                    ME     RMSE      MAE       MPE     MAPE      MASE
## Training set 4.716493 128.4957 73.48511 -2.065164 10.35313 0.6143497
##                     ACF1
## Training set -0.05029193
checkresiduals(mod_sugerido)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,1,2)(0,1,1)[12]
## Q* = 24.764, df = 19, p-value = 0.1685
## 
## Model df: 5.   Total lags used: 24
#Ljung-Box:
#p-value = 0.1685 > 0.05 → no hay autocorrelación significativa en los residuos.
# Buen indicio de que el modelo capta bien la estructura de la serie
#Captura adecuadamente la tendencia y estacionalidad.
variables_log <- c( "MES", "AÑO",
"Cuartos_Disponibles", "Cuartos_Ocupados", "Cuartos_Registrados", "Derrama_Economica_Est_mdp",  "Imp_Hospedaje", "Turistas_Noche_Ext", "Turistas_Noche_Nac", "%Ocupacion_Hoteles",  "Llegada_Tur_Ext", "Llegada_Tur_Nac", "Cuartos_Disponibles_Promedio_log", "Densidad_Ocupación_log",  "Estadia_Promedio_log"           
)

variables_log <- setdiff(variables_log, c("Imp_Hospedaje", "MES", "AÑO"))

for (var in variables_log) {
  cat("\n--------------------------------------------------\n")
  cat("ADF test for:", var, "\n")
  serie <- ts(df_log[[var]], start = c(2004, 1), frequency = 12)
  print(adf.test(serie))
}
## 
## --------------------------------------------------
## ADF test for: Cuartos_Disponibles 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag   ADF p.value
## [1,]   0 0.478   0.781
## [2,]   1 1.624   0.975
## [3,]   2 2.369   0.990
## [4,]   3 2.611   0.990
## [5,]   4 3.603   0.990
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -1.985   0.333
## [2,]   1 -0.757   0.782
## [3,]   2 -0.223   0.928
## [4,]   3 -0.118   0.943
## [5,]   4  0.376   0.980
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -8.62   0.010
## [2,]   1 -4.12   0.010
## [3,]   2 -2.84   0.224
## [4,]   3 -2.55   0.346
## [5,]   4 -1.65   0.724
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag       ADF   p.value
## [1,]   0 0.4777519 0.7813675
## [2,]   1 1.6240310 0.9745765
## [3,]   2 2.3693760 0.9900000
## [4,]   3 2.6112981 0.9900000
## [5,]   4 3.6029489 0.9900000
## 
## $type2
##      lag        ADF   p.value
## [1,]   0 -1.9849443 0.3333578
## [2,]   1 -0.7568787 0.7818209
## [3,]   2 -0.2232732 0.9281088
## [4,]   3 -0.1180908 0.9431315
## [5,]   4  0.3763474 0.9803827
## 
## $type3
##      lag       ADF   p.value
## [1,]   0 -8.619217 0.0100000
## [2,]   1 -4.120557 0.0100000
## [3,]   2 -2.836190 0.2237095
## [4,]   3 -2.546797 0.3455593
## [5,]   4 -1.648221 0.7239166
## 
## 
## --------------------------------------------------
## ADF test for: Cuartos_Ocupados 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag     ADF p.value
## [1,]   0 -0.7528   0.410
## [2,]   1 -0.5705   0.475
## [3,]   2 -0.1239   0.608
## [4,]   3  0.0337   0.654
## [5,]   4  0.0322   0.653
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -3.67  0.0100
## [2,]   1 -3.35  0.0149
## [3,]   2 -2.31  0.2056
## [4,]   3 -2.11  0.2858
## [5,]   4 -1.95  0.3454
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -4.69  0.0100
## [2,]   1 -4.33  0.0100
## [3,]   2 -3.15  0.0958
## [4,]   3 -2.87  0.2078
## [5,]   4 -2.80  0.2371
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag         ADF   p.value
## [1,]   0 -0.75278318 0.4097203
## [2,]   1 -0.57054153 0.4748066
## [3,]   2 -0.12385848 0.6082422
## [4,]   3  0.03369558 0.6535815
## [5,]   4  0.03223410 0.6531609
## 
## $type2
##      lag       ADF    p.value
## [1,]   0 -3.666663 0.01000000
## [2,]   1 -3.349239 0.01487358
## [3,]   2 -2.310812 0.20555655
## [4,]   3 -2.106180 0.28581074
## [5,]   4 -1.954229 0.34540412
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -4.688795 0.01000000
## [2,]   1 -4.334235 0.01000000
## [3,]   2 -3.154158 0.09583476
## [4,]   3 -2.873919 0.20782365
## [5,]   4 -2.804496 0.23705421
## 
## 
## --------------------------------------------------
## ADF test for: Cuartos_Registrados 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag   ADF p.value
## [1,]   0 0.464   0.777
## [2,]   1 1.595   0.973
## [3,]   2 2.392   0.990
## [4,]   3 2.700   0.990
## [5,]   4 3.758   0.990
## Type 2: with drift no trend 
##      lag     ADF p.value
## [1,]   0 -2.0066   0.325
## [2,]   1 -0.7414   0.787
## [3,]   2 -0.1906   0.933
## [4,]   3 -0.0471   0.952
## [5,]   4  0.4528   0.983
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -8.85   0.010
## [2,]   1 -4.22   0.010
## [3,]   2 -2.84   0.223
## [4,]   3 -2.47   0.377
## [5,]   4 -1.56   0.760
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag       ADF   p.value
## [1,]   0 0.4637714 0.7773443
## [2,]   1 1.5954975 0.9725381
## [3,]   2 2.3924775 0.9900000
## [4,]   3 2.7001007 0.9900000
## [5,]   4 3.7579557 0.9900000
## 
## $type2
##      lag         ADF   p.value
## [1,]   0 -2.00657920 0.3248729
## [2,]   1 -0.74142297 0.7872441
## [3,]   2 -0.19064086 0.9327695
## [4,]   3 -0.04711177 0.9518458
## [5,]   4  0.45281423 0.9834014
## 
## $type3
##      lag       ADF   p.value
## [1,]   0 -8.851989 0.0100000
## [2,]   1 -4.224233 0.0100000
## [3,]   2 -2.837425 0.2231894
## [4,]   3 -2.471558 0.3772387
## [5,]   4 -1.563069 0.7597713
## 
## 
## --------------------------------------------------
## ADF test for: Derrama_Economica_Est_mdp 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -0.443   0.517
## [2,]   1 -0.193   0.588
## [3,]   2  0.651   0.831
## [4,]   3  0.713   0.849
## [5,]   4  0.395   0.758
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -1.925   0.357
## [2,]   1 -1.611   0.480
## [3,]   2 -0.583   0.843
## [4,]   3 -0.540   0.858
## [5,]   4 -0.835   0.754
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -3.24  0.0816
## [2,]   1 -2.90  0.1951
## [3,]   2 -1.81  0.6564
## [4,]   3 -1.74  0.6862
## [5,]   4 -2.12  0.5270
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF   p.value
## [1,]   0 -0.4425241 0.5165398
## [2,]   1 -0.1927057 0.5884300
## [3,]   2  0.6512135 0.8312844
## [4,]   3  0.7130436 0.8490773
## [5,]   4  0.3954069 0.7576710
## 
## $type2
##      lag        ADF   p.value
## [1,]   0 -1.9251534 0.3568070
## [2,]   1 -1.6109259 0.4800432
## [3,]   2 -0.5828110 0.8428994
## [4,]   3 -0.5403225 0.8578082
## [5,]   4 -0.8349828 0.7544150
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -3.236675 0.08160778
## [2,]   1 -2.904106 0.19511329
## [3,]   2 -1.808621 0.65637655
## [4,]   3 -1.737771 0.68620967
## [5,]   4 -2.115873 0.52700199
## 
## 
## --------------------------------------------------
## ADF test for: Turistas_Noche_Ext 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -1.200   0.250
## [2,]   1 -0.807   0.390
## [3,]   2 -0.531   0.489
## [4,]   3 -0.403   0.528
## [5,]   4 -0.460   0.512
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -3.54  0.0100
## [2,]   1 -2.84  0.0567
## [3,]   2 -2.30  0.2090
## [4,]   3 -2.19  0.2522
## [5,]   4 -2.18  0.2569
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -4.44  0.0100
## [2,]   1 -3.61  0.0321
## [3,]   2 -3.01  0.1510
## [4,]   3 -2.82  0.2310
## [5,]   4 -2.91  0.1945
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF   p.value
## [1,]   0 -1.2004190 0.2498503
## [2,]   1 -0.8072259 0.3902765
## [3,]   2 -0.5312872 0.4888260
## [4,]   3 -0.4034737 0.5277774
## [5,]   4 -0.4595308 0.5116458
## 
## $type2
##      lag       ADF    p.value
## [1,]   0 -3.537031 0.01000000
## [2,]   1 -2.839744 0.05670265
## [3,]   2 -2.302026 0.20900235
## [4,]   3 -2.191895 0.25219433
## [5,]   4 -2.179884 0.25690504
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -4.440524 0.01000000
## [2,]   1 -3.613612 0.03206767
## [3,]   2 -3.008935 0.15097480
## [4,]   3 -2.818923 0.23097987
## [5,]   4 -2.905634 0.19447010
## 
## 
## --------------------------------------------------
## ADF test for: Turistas_Noche_Nac 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag     ADF p.value
## [1,]   0 -1.0733   0.295
## [2,]   1 -0.7899   0.396
## [3,]   2 -0.2799   0.563
## [4,]   3 -0.0869   0.619
## [5,]   4 -0.1503   0.601
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -4.17   0.010
## [2,]   1 -3.61   0.010
## [3,]   2 -2.41   0.166
## [4,]   3 -2.08   0.298
## [5,]   4 -2.06   0.304
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -5.63  0.0100
## [2,]   1 -4.98  0.0100
## [3,]   2 -3.51  0.0420
## [4,]   3 -3.06  0.1280
## [5,]   4 -3.18  0.0919
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag         ADF   p.value
## [1,]   0 -1.07329620 0.2952514
## [2,]   1 -0.78985659 0.3964798
## [3,]   2 -0.27990229 0.5633375
## [4,]   3 -0.08694443 0.6188649
## [5,]   4 -0.15026795 0.6006423
## 
## $type2
##      lag       ADF   p.value
## [1,]   0 -4.165783 0.0100000
## [2,]   1 -3.612993 0.0100000
## [3,]   2 -2.411467 0.1660811
## [4,]   3 -2.076220 0.2975607
## [5,]   4 -2.060735 0.3036336
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -5.633109 0.01000000
## [2,]   1 -4.984095 0.01000000
## [3,]   2 -3.506724 0.04196759
## [4,]   3 -3.063561 0.12797424
## [5,]   4 -3.177264 0.09185107
## 
## 
## --------------------------------------------------
## ADF test for: %Ocupacion_Hoteles 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -0.945   0.341
## [2,]   1 -0.864   0.370
## [3,]   2 -0.460   0.512
## [4,]   3 -0.429   0.520
## [5,]   4 -0.449   0.515
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -4.70  0.0100
## [2,]   1 -4.52  0.0100
## [3,]   2 -2.90  0.0480
## [4,]   3 -2.91  0.0468
## [5,]   4 -2.67  0.0852
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -4.70   0.010
## [2,]   1 -4.52   0.010
## [3,]   2 -2.91   0.191
## [4,]   3 -2.92   0.188
## [5,]   4 -2.69   0.283
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF   p.value
## [1,]   0 -0.9448806 0.3411141
## [2,]   1 -0.8643401 0.3698785
## [3,]   2 -0.4599054 0.5115380
## [4,]   3 -0.4292851 0.5203496
## [5,]   4 -0.4486139 0.5147874
## 
## $type2
##      lag       ADF    p.value
## [1,]   0 -4.695671 0.01000000
## [2,]   1 -4.516520 0.01000000
## [3,]   2 -2.900676 0.04800809
## [4,]   3 -2.913222 0.04680176
## [5,]   4 -2.668905 0.08517578
## 
## $type3
##      lag       ADF   p.value
## [1,]   0 -4.702490 0.0100000
## [2,]   1 -4.524876 0.0100000
## [3,]   2 -2.914022 0.1909381
## [4,]   3 -2.921932 0.1876074
## [5,]   4 -2.694585 0.2833326
## 
## 
## --------------------------------------------------
## ADF test for: Llegada_Tur_Ext 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -1.265   0.227
## [2,]   1 -0.870   0.368
## [3,]   2 -0.544   0.484
## [4,]   3 -0.433   0.519
## [5,]   4 -0.399   0.529
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -3.91  0.0100
## [2,]   1 -3.13  0.0255
## [3,]   2 -2.48  0.1384
## [4,]   3 -2.39  0.1739
## [5,]   4 -2.26  0.2262
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -4.87  0.0100
## [2,]   1 -3.96  0.0111
## [3,]   2 -3.21  0.0863
## [4,]   3 -3.05  0.1324
## [5,]   4 -2.96  0.1696
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF   p.value
## [1,]   0 -1.2654494 0.2266252
## [2,]   1 -0.8698140 0.3679236
## [3,]   2 -0.5443118 0.4841744
## [4,]   3 -0.4327889 0.5193413
## [5,]   4 -0.3992141 0.5290031
## 
## $type2
##      lag       ADF   p.value
## [1,]   0 -3.914994 0.0100000
## [2,]   1 -3.134710 0.0255048
## [3,]   2 -2.482035 0.1384049
## [4,]   3 -2.391520 0.1739038
## [5,]   4 -2.258125 0.2262197
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -4.868170 0.01000000
## [2,]   1 -3.958192 0.01112578
## [3,]   2 -3.209659 0.08626574
## [4,]   3 -3.052968 0.13243458
## [5,]   4 -2.964710 0.16959598
## 
## 
## --------------------------------------------------
## ADF test for: Llegada_Tur_Nac 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag     ADF p.value
## [1,]   0 -0.7122   0.424
## [2,]   1 -0.4986   0.500
## [3,]   2 -0.0773   0.622
## [4,]   3  0.1342   0.683
## [5,]   4  0.1314   0.682
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -3.62  0.0100
## [2,]   1 -3.17  0.0235
## [3,]   2 -2.20  0.2484
## [4,]   3 -1.83  0.3926
## [5,]   4 -1.68  0.4515
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -4.83  0.0100
## [2,]   1 -4.38  0.0100
## [3,]   2 -3.30  0.0706
## [4,]   3 -2.86  0.2124
## [5,]   4 -2.82  0.2298
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag         ADF   p.value
## [1,]   0 -0.71219518 0.4242160
## [2,]   1 -0.49864304 0.5003905
## [3,]   2 -0.07732892 0.6216320
## [4,]   3  0.13422395 0.6825105
## [5,]   4  0.13136588 0.6816880
## 
## $type2
##      lag       ADF    p.value
## [1,]   0 -3.616582 0.01000000
## [2,]   1 -3.171168 0.02348992
## [3,]   2 -2.201458 0.24844364
## [4,]   3 -1.833882 0.39260268
## [5,]   4 -1.683754 0.45148086
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -4.834997 0.01000000
## [2,]   1 -4.377747 0.01000000
## [3,]   2 -3.300577 0.07059009
## [4,]   3 -2.863075 0.21238932
## [5,]   4 -2.821818 0.22976096
## 
## 
## --------------------------------------------------
## ADF test for: Cuartos_Disponibles_Promedio_log 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag  ADF p.value
## [1,]   0 1.89   0.985
## [2,]   1 2.46   0.990
## [3,]   2 2.81   0.990
## [4,]   3 3.19   0.990
## [5,]   4 3.33   0.990
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -1.047   0.680
## [2,]   1 -0.848   0.750
## [3,]   2 -0.652   0.819
## [4,]   3 -0.637   0.824
## [5,]   4 -0.547   0.856
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -4.71  0.0100
## [2,]   1 -3.71  0.0240
## [3,]   2 -3.17  0.0939
## [4,]   3 -2.84  0.2221
## [5,]   4 -2.64  0.3054
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag      ADF   p.value
## [1,]   0 1.892129 0.9850834
## [2,]   1 2.464883 0.9900000
## [3,]   2 2.814414 0.9900000
## [4,]   3 3.189882 0.9900000
## [5,]   4 3.328724 0.9900000
## 
## $type2
##      lag        ADF   p.value
## [1,]   0 -1.0470810 0.6799919
## [2,]   1 -0.8484632 0.7496848
## [3,]   2 -0.6519157 0.8186513
## [4,]   3 -0.6367106 0.8239866
## [5,]   4 -0.5466174 0.8555993
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -4.708043 0.01000000
## [2,]   1 -3.709718 0.02397613
## [3,]   2 -3.165610 0.09386041
## [4,]   3 -2.839904 0.22214574
## [5,]   4 -2.642196 0.30539107
## 
## 
## --------------------------------------------------
## ADF test for: Densidad_Ocupación_log 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -0.765   0.405
## [2,]   1 -0.458   0.512
## [3,]   2 -0.330   0.549
## [4,]   3 -0.274   0.565
## [5,]   4 -0.268   0.567
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -11.18    0.01
## [2,]   1  -7.72    0.01
## [3,]   2  -5.60    0.01
## [4,]   3  -4.19    0.01
## [5,]   4  -3.74    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -12.55    0.01
## [2,]   1  -8.95    0.01
## [3,]   2  -6.68    0.01
## [4,]   3  -5.12    0.01
## [5,]   4  -4.68    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF   p.value
## [1,]   0 -0.7651350 0.4053089
## [2,]   1 -0.4576753 0.5121798
## [3,]   2 -0.3304019 0.5488052
## [4,]   3 -0.2737244 0.5651153
## [5,]   4 -0.2684313 0.5666385
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -11.178275    0.01
## [2,]   1  -7.715082    0.01
## [3,]   2  -5.595947    0.01
## [4,]   3  -4.194517    0.01
## [5,]   4  -3.742244    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -12.547995    0.01
## [2,]   1  -8.952229    0.01
## [3,]   2  -6.675710    0.01
## [4,]   3  -5.116983    0.01
## [5,]   4  -4.679276    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Estadia_Promedio_log 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -0.521   0.492
## [2,]   1 -0.372   0.537
## [3,]   2 -0.376   0.536
## [4,]   3 -0.357   0.541
## [5,]   4 -0.327   0.550
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -6.47    0.01
## [2,]   1 -5.98    0.01
## [3,]   2 -5.05    0.01
## [4,]   3 -4.30    0.01
## [5,]   4 -3.98    0.01
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -6.72    0.01
## [2,]   1 -6.18    0.01
## [3,]   2 -5.24    0.01
## [4,]   3 -4.44    0.01
## [5,]   4 -4.09    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF   p.value
## [1,]   0 -0.5214091 0.4923539
## [2,]   1 -0.3722281 0.5367689
## [3,]   2 -0.3760993 0.5356549
## [4,]   3 -0.3574346 0.5410260
## [5,]   4 -0.3274543 0.5496534
## 
## $type2
##      lag       ADF p.value
## [1,]   0 -6.469231    0.01
## [2,]   1 -5.983262    0.01
## [3,]   2 -5.052522    0.01
## [4,]   3 -4.296002    0.01
## [5,]   4 -3.983149    0.01
## 
## $type3
##      lag       ADF p.value
## [1,]   0 -6.721413    0.01
## [2,]   1 -6.183590    0.01
## [3,]   2 -5.244141    0.01
## [4,]   3 -4.439856    0.01
## [5,]   4 -4.094485    0.01
library(tseries)

series_diferenciadas <- list()

for (var in variables_log) {
  serie <- ts(df_log[[var]], start = c(2004, 1), frequency = 12)
  adf_result <- tryCatch(tseries::adf.test(serie), error = function(e) NULL)
  
  if (!is.null(adf_result) && !is.na(adf_result$p.value) && adf_result$p.value > 0.05) {
    # No es estacionaria → aplicar diff y renombrar con _diff
    serie_diff <- diff(serie)
    series_diferenciadas[[paste0(var, "_diff")]] <- serie_diff
  } else if (!is.null(adf_result)) {
    # Es estacionaria → mantener nombre original (recortada por consistencia)
    series_diferenciadas[[var]] <- window(serie, start = c(2004, 2))
  }
}
# Aplicar la prueba ADF a todas las series diferenciadas
for (nombre in names(series_diferenciadas)) {
  cat("\n--------------------------------------------------\n")
  cat("ADF test for:", nombre, "\n")
  print(adf.test(series_diferenciadas[[nombre]]))
}
## 
## --------------------------------------------------
## ADF test for: Cuartos_Disponibles_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -32.99    0.01
## [2,]   1 -19.68    0.01
## [3,]   2 -13.20    0.01
## [4,]   3 -13.31    0.01
## [5,]   4  -8.17    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -33.18    0.01
## [2,]   1 -20.03    0.01
## [3,]   2 -13.63    0.01
## [4,]   3 -14.10    0.01
## [5,]   4  -8.81    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -33.12    0.01
## [2,]   1 -20.00    0.01
## [3,]   2 -13.62    0.01
## [4,]   3 -14.11    0.01
## [5,]   4  -8.82    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -32.988656    0.01
## [2,]   1 -19.680604    0.01
## [3,]   2 -13.197374    0.01
## [4,]   3 -13.311142    0.01
## [5,]   4  -8.168268    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -33.179502    0.01
## [2,]   1 -20.027537    0.01
## [3,]   2 -13.626354    0.01
## [4,]   3 -14.098018    0.01
## [5,]   4  -8.807238    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -33.118897    0.01
## [2,]   1 -20.002478    0.01
## [3,]   2 -13.615723    0.01
## [4,]   3 -14.110187    0.01
## [5,]   4  -8.816725    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Cuartos_Ocupados_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -17.46    0.01
## [2,]   1 -16.02    0.01
## [3,]   2 -12.37    0.01
## [4,]   3  -9.99    0.01
## [5,]   4  -7.93    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -17.44    0.01
## [2,]   1 -16.00    0.01
## [3,]   2 -12.37    0.01
## [4,]   3  -9.99    0.01
## [5,]   4  -7.95    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -17.40    0.01
## [2,]   1 -15.97    0.01
## [3,]   2 -12.35    0.01
## [4,]   3  -9.98    0.01
## [5,]   4  -7.93    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -17.463467    0.01
## [2,]   1 -16.017182    0.01
## [3,]   2 -12.369033    0.01
## [4,]   3  -9.990271    0.01
## [5,]   4  -7.933321    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -17.438958    0.01
## [2,]   1 -16.003600    0.01
## [3,]   2 -12.371725    0.01
## [4,]   3  -9.993988    0.01
## [5,]   4  -7.946753    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -17.401749    0.01
## [2,]   1 -15.972958    0.01
## [3,]   2 -12.347092    0.01
## [4,]   3  -9.980622    0.01
## [5,]   4  -7.931895    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Cuartos_Registrados_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag   ADF p.value
## [1,]   0 -33.1    0.01
## [2,]   1 -20.1    0.01
## [3,]   2 -13.6    0.01
## [4,]   3 -13.7    0.01
## [5,]   4  -8.1    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -33.24    0.01
## [2,]   1 -20.49    0.01
## [3,]   2 -14.07    0.01
## [4,]   3 -14.52    0.01
## [5,]   4  -8.77    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -33.18    0.01
## [2,]   1 -20.46    0.01
## [3,]   2 -14.06    0.01
## [4,]   3 -14.54    0.01
## [5,]   4  -8.78    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag       ADF p.value
## [1,]   0 -33.05799    0.01
## [2,]   1 -20.13233    0.01
## [3,]   2 -13.62034    0.01
## [4,]   3 -13.67426    0.01
## [5,]   4  -8.09889    0.01
## 
## $type2
##      lag       ADF p.value
## [1,]   0 -33.23903    0.01
## [2,]   1 -20.48546    0.01
## [3,]   2 -14.07223    0.01
## [4,]   3 -14.52181    0.01
## [5,]   4  -8.76908    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -33.179805    0.01
## [2,]   1 -20.461284    0.01
## [3,]   2 -14.063915    0.01
## [4,]   3 -14.538646    0.01
## [5,]   4  -8.779469    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Derrama_Economica_Est_mdp_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -17.26    0.01
## [2,]   1 -16.72    0.01
## [3,]   2 -11.67    0.01
## [4,]   3  -8.22    0.01
## [5,]   4  -6.53    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -17.26    0.01
## [2,]   1 -16.77    0.01
## [3,]   2 -11.74    0.01
## [4,]   3  -8.28    0.01
## [5,]   4  -6.59    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -17.24    0.01
## [2,]   1 -16.78    0.01
## [3,]   2 -11.77    0.01
## [4,]   3  -8.32    0.01
## [5,]   4  -6.62    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -17.257861    0.01
## [2,]   1 -16.719685    0.01
## [3,]   2 -11.668943    0.01
## [4,]   3  -8.222182    0.01
## [5,]   4  -6.532807    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -17.260904    0.01
## [2,]   1 -16.765919    0.01
## [3,]   2 -11.736512    0.01
## [4,]   3  -8.278389    0.01
## [5,]   4  -6.592347    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -17.242338    0.01
## [2,]   1 -16.784749    0.01
## [3,]   2 -11.767242    0.01
## [4,]   3  -8.318704    0.01
## [5,]   4  -6.621985    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Turistas_Noche_Ext_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -19.69    0.01
## [2,]   1 -14.95    0.01
## [3,]   2 -11.61    0.01
## [4,]   3  -9.17    0.01
## [5,]   4  -7.28    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -19.66    0.01
## [2,]   1 -14.93    0.01
## [3,]   2 -11.60    0.01
## [4,]   3  -9.16    0.01
## [5,]   4  -7.28    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -19.62    0.01
## [2,]   1 -14.90    0.01
## [3,]   2 -11.58    0.01
## [4,]   3  -9.14    0.01
## [5,]   4  -7.27    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -19.694117    0.01
## [2,]   1 -14.953003    0.01
## [3,]   2 -11.610555    0.01
## [4,]   3  -9.167692    0.01
## [5,]   4  -7.282481    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -19.662656    0.01
## [2,]   1 -14.933408    0.01
## [3,]   2 -11.603392    0.01
## [4,]   3  -9.161765    0.01
## [5,]   4  -7.280217    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -19.622120    0.01
## [2,]   1 -14.904015    0.01
## [3,]   2 -11.578836    0.01
## [4,]   3  -9.144982    0.01
## [5,]   4  -7.266921    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Turistas_Noche_Nac_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -18.53    0.01
## [2,]   1 -17.29    0.01
## [3,]   2 -13.42    0.01
## [4,]   3 -10.02    0.01
## [5,]   4  -7.52    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -18.50    0.01
## [2,]   1 -17.27    0.01
## [3,]   2 -13.42    0.01
## [4,]   3 -10.02    0.01
## [5,]   4  -7.52    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -18.47    0.01
## [2,]   1 -17.24    0.01
## [3,]   2 -13.39    0.01
## [4,]   3 -10.00    0.01
## [5,]   4  -7.51    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -18.532660    0.01
## [2,]   1 -17.292102    0.01
## [3,]   2 -13.424566    0.01
## [4,]   3 -10.020339    0.01
## [5,]   4  -7.515764    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -18.503860    0.01
## [2,]   1 -17.272121    0.01
## [3,]   2 -13.418932    0.01
## [4,]   3 -10.016647    0.01
## [5,]   4  -7.520032    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -18.465367    0.01
## [2,]   1 -17.238516    0.01
## [3,]   2 -13.393092    0.01
## [4,]   3 -10.001469    0.01
## [5,]   4  -7.505395    0.01
## 
## 
## --------------------------------------------------
## ADF test for: %Ocupacion_Hoteles_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -16.85    0.01
## [2,]   1 -17.94    0.01
## [3,]   2 -11.91    0.01
## [4,]   3 -10.31    0.01
## [5,]   4  -8.46    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -16.82    0.01
## [2,]   1 -17.90    0.01
## [3,]   2 -11.89    0.01
## [4,]   3 -10.29    0.01
## [5,]   4  -8.45    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -16.78    0.01
## [2,]   1 -17.87    0.01
## [3,]   2 -11.87    0.01
## [4,]   3 -10.27    0.01
## [5,]   4  -8.43    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -16.853593    0.01
## [2,]   1 -17.939312    0.01
## [3,]   2 -11.914473    0.01
## [4,]   3 -10.310056    0.01
## [5,]   4  -8.464536    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -16.819696    0.01
## [2,]   1 -17.903491    0.01
## [3,]   2 -11.891809    0.01
## [4,]   3 -10.287868    0.01
## [5,]   4  -8.449082    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -16.784785    0.01
## [2,]   1 -17.866600    0.01
## [3,]   2 -11.866363    0.01
## [4,]   3 -10.268783    0.01
## [5,]   4  -8.430208    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Llegada_Tur_Ext_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -19.90    0.01
## [2,]   1 -15.42    0.01
## [3,]   2 -11.69    0.01
## [4,]   3  -9.72    0.01
## [5,]   4  -7.54    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -19.87    0.01
## [2,]   1 -15.40    0.01
## [3,]   2 -11.68    0.01
## [4,]   3  -9.72    0.01
## [5,]   4  -7.54    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -19.83    0.01
## [2,]   1 -15.37    0.01
## [3,]   2 -11.66    0.01
## [4,]   3  -9.70    0.01
## [5,]   4  -7.53    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -19.903681    0.01
## [2,]   1 -15.420126    0.01
## [3,]   2 -11.687180    0.01
## [4,]   3  -9.723831    0.01
## [5,]   4  -7.542653    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -19.872617    0.01
## [2,]   1 -15.401012    0.01
## [3,]   2 -11.680650    0.01
## [4,]   3  -9.720311    0.01
## [5,]   4  -7.542871    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -19.832651    0.01
## [2,]   1 -15.370568    0.01
## [3,]   2 -11.656651    0.01
## [4,]   3  -9.701470    0.01
## [5,]   4  -7.528284    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Llegada_Tur_Nac_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -17.89    0.01
## [2,]   1 -15.54    0.01
## [3,]   2 -12.64    0.01
## [4,]   3 -10.16    0.01
## [5,]   4  -7.71    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -17.87    0.01
## [2,]   1 -15.53    0.01
## [3,]   2 -12.65    0.01
## [4,]   3 -10.17    0.01
## [5,]   4  -7.73    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -17.84    0.01
## [2,]   1 -15.52    0.01
## [3,]   2 -12.64    0.01
## [4,]   3 -10.18    0.01
## [5,]   4  -7.74    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -17.893563    0.01
## [2,]   1 -15.540678    0.01
## [3,]   2 -12.645000    0.01
## [4,]   3 -10.156475    0.01
## [5,]   4  -7.713903    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -17.873486    0.01
## [2,]   1 -15.533133    0.01
## [3,]   2 -12.652632    0.01
## [4,]   3 -10.165280    0.01
## [5,]   4  -7.732122    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -17.842643    0.01
## [2,]   1 -15.516682    0.01
## [3,]   2 -12.644614    0.01
## [4,]   3 -10.176799    0.01
## [5,]   4  -7.739574    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Cuartos_Disponibles_Promedio_log_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -20.25    0.01
## [2,]   1 -14.21    0.01
## [3,]   2 -11.57    0.01
## [4,]   3  -9.57    0.01
## [5,]   4  -8.18    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -20.61    0.01
## [2,]   1 -14.69    0.01
## [3,]   2 -12.21    0.01
## [4,]   3 -10.32    0.01
## [5,]   4  -9.03    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -20.57    0.01
## [2,]   1 -14.66    0.01
## [3,]   2 -12.19    0.01
## [4,]   3 -10.30    0.01
## [5,]   4  -9.02    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -20.252740    0.01
## [2,]   1 -14.214086    0.01
## [3,]   2 -11.569451    0.01
## [4,]   3  -9.570863    0.01
## [5,]   4  -8.182493    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -20.611214    0.01
## [2,]   1 -14.688378    0.01
## [3,]   2 -12.213380    0.01
## [4,]   3 -10.323869    0.01
## [5,]   4  -9.034693    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -20.570121    0.01
## [2,]   1 -14.658454    0.01
## [3,]   2 -12.188496    0.01
## [4,]   3 -10.302504    0.01
## [5,]   4  -9.015723    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Densidad_Ocupación_log 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -0.761   0.407
## [2,]   1 -0.482   0.505
## [3,]   2 -0.356   0.541
## [4,]   3 -0.297   0.559
## [5,]   4 -0.253   0.571
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -11.16    0.01
## [2,]   1  -7.69    0.01
## [3,]   2  -5.58    0.01
## [4,]   3  -4.19    0.01
## [5,]   4  -3.73    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -12.53    0.01
## [2,]   1  -8.97    0.01
## [3,]   2  -6.71    0.01
## [4,]   3  -5.16    0.01
## [5,]   4  -4.67    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF   p.value
## [1,]   0 -0.7609459 0.4068050
## [2,]   1 -0.4823146 0.5050893
## [3,]   2 -0.3558360 0.5414860
## [4,]   3 -0.2965246 0.5585541
## [5,]   4 -0.2529055 0.5711063
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -11.158231    0.01
## [2,]   1  -7.694956    0.01
## [3,]   2  -5.583992    0.01
## [4,]   3  -4.188343    0.01
## [5,]   4  -3.732679    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -12.529166    0.01
## [2,]   1  -8.969998    0.01
## [3,]   2  -6.709890    0.01
## [4,]   3  -5.157404    0.01
## [5,]   4  -4.666754    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Estadia_Promedio_log_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag   ADF p.value
## [1,]   0 -18.2    0.01
## [2,]   1 -14.6    0.01
## [3,]   2 -12.8    0.01
## [4,]   3 -11.0    0.01
## [5,]   4 -10.7    0.01
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -18.2    0.01
## [2,]   1 -14.5    0.01
## [3,]   2 -12.8    0.01
## [4,]   3 -10.9    0.01
## [5,]   4 -10.7    0.01
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -18.1    0.01
## [2,]   1 -14.5    0.01
## [3,]   2 -12.8    0.01
## [4,]   3 -10.9    0.01
## [5,]   4 -10.7    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag       ADF p.value
## [1,]   0 -18.18816    0.01
## [2,]   1 -14.57232    0.01
## [3,]   2 -12.83734    0.01
## [4,]   3 -10.96515    0.01
## [5,]   4 -10.68420    0.01
## 
## $type2
##      lag       ADF p.value
## [1,]   0 -18.15213    0.01
## [2,]   1 -14.54409    0.01
## [3,]   2 -12.81294    0.01
## [4,]   3 -10.94434    0.01
## [5,]   4 -10.66352    0.01
## 
## $type3
##      lag       ADF p.value
## [1,]   0 -18.12322    0.01
## [2,]   1 -14.52442    0.01
## [3,]   2 -12.80107    0.01
## [4,]   3 -10.94218    0.01
## [5,]   4 -10.67122    0.01
labels(series_diferenciadas)
##  [1] "Cuartos_Disponibles_diff"             
##  [2] "Cuartos_Ocupados_diff"                
##  [3] "Cuartos_Registrados_diff"             
##  [4] "Derrama_Economica_Est_mdp_diff"       
##  [5] "Turistas_Noche_Ext_diff"              
##  [6] "Turistas_Noche_Nac_diff"              
##  [7] "%Ocupacion_Hoteles_diff"              
##  [8] "Llegada_Tur_Ext_diff"                 
##  [9] "Llegada_Tur_Nac_diff"                 
## [10] "Cuartos_Disponibles_Promedio_log_diff"
## [11] "Densidad_Ocupación_log"               
## [12] "Estadia_Promedio_log_diff"
df_log[2, ]
##    AÑO MES Cuartos_Disponibles Cuartos_Ocupados Cuartos_Registrados
## 2 2004   2              267473           129500              274195
##   Derrama_Economica_Est_mdp Imp_Hospedaje Turistas_Noche_Ext Turistas_Noche_Nac
## 2                  345.1886            NA              33262             159212
##   %Ocupacion_Hoteles Llegada_Tur_Ext Llegada_Tur_Nac
## 2           0.484161           15841           87704
##   Cuartos_Disponibles_Promedio_log Densidad_Ocupación_log Estadia_Promedio_log
## 2                         9.120853              0.9107899             1.050417
df_diff <- as.data.frame(series_diferenciadas)
df_diff[1, ]
##   Cuartos_Disponibles_diff Cuartos_Ocupados_diff Cuartos_Registrados_diff
## 1                   -18418                 -5196                   -18910
##   Derrama_Economica_Est_mdp_diff Turistas_Noche_Ext_diff
## 1                       28.67197                   -2480
##   Turistas_Noche_Nac_diff X.Ocupacion_Hoteles_diff Llegada_Tur_Ext_diff
## 1                   -7306               0.01301642                 1116
##   Llegada_Tur_Nac_diff Cuartos_Disponibles_Promedio_log_diff
## 1                 7253                          0.0001093673
##   Densidad_Ocupación_log Estadia_Promedio_log_diff
## 1              0.9107899               -0.08905393
# 1. Mantener solo MES y AÑO de df_log (recortado)
df_log2 <- df_log[-1, c("MES", "AÑO", "Densidad_Ocupación_log")]

# 2. Eliminar de df_diff las columnas que ya existen en df_log2
cols_existentes <- intersect(names(df_log2), names(df_diff))
df_diff_filtrado <- df_diff[, !(names(df_diff) %in% cols_existentes)]

# 3. Unir MES, AÑO y las series diferenciadas
df_final <- bind_cols(df_log2, df_diff_filtrado)

df_final <- df_final[df_final$AÑO >= 2015, ]
correlaciones <- cor(df_final, use = "complete.obs")
round(correlaciones, 2)  # para ver mejor los valores
##                                         MES   AÑO Densidad_Ocupación_log
## MES                                    1.00  0.00                   0.09
## AÑO                                    0.00  1.00                   0.47
## Densidad_Ocupación_log                 0.09  0.47                   1.00
## Cuartos_Disponibles_diff               0.15  0.00                   0.07
## Cuartos_Ocupados_diff                 -0.09  0.01                   0.00
## Cuartos_Registrados_diff               0.17  0.00                   0.07
## Derrama_Economica_Est_mdp_diff        -0.10  0.04                   0.18
## Turistas_Noche_Ext_diff                0.09 -0.01                   0.11
## Turistas_Noche_Nac_diff               -0.03 -0.01                   0.12
## X.Ocupacion_Hoteles_diff              -0.15  0.01                  -0.03
## Llegada_Tur_Ext_diff                   0.13 -0.01                   0.07
## Llegada_Tur_Nac_diff                   0.10  0.01                   0.10
## Cuartos_Disponibles_Promedio_log_diff  0.12 -0.01                  -0.09
## Estadia_Promedio_log_diff             -0.29 -0.02                   0.04
##                                       Cuartos_Disponibles_diff
## MES                                                       0.15
## AÑO                                                       0.00
## Densidad_Ocupación_log                                    0.07
## Cuartos_Disponibles_diff                                  1.00
## Cuartos_Ocupados_diff                                     0.27
## Cuartos_Registrados_diff                                  0.99
## Derrama_Economica_Est_mdp_diff                            0.24
## Turistas_Noche_Ext_diff                                   0.14
## Turistas_Noche_Nac_diff                                   0.30
## X.Ocupacion_Hoteles_diff                                 -0.06
## Llegada_Tur_Ext_diff                                      0.23
## Llegada_Tur_Nac_diff                                      0.25
## Cuartos_Disponibles_Promedio_log_diff                     0.33
## Estadia_Promedio_log_diff                                 0.03
##                                       Cuartos_Ocupados_diff
## MES                                                   -0.09
## AÑO                                                    0.01
## Densidad_Ocupación_log                                 0.00
## Cuartos_Disponibles_diff                               0.27
## Cuartos_Ocupados_diff                                  1.00
## Cuartos_Registrados_diff                               0.23
## Derrama_Economica_Est_mdp_diff                         0.76
## Turistas_Noche_Ext_diff                                0.55
## Turistas_Noche_Nac_diff                                0.90
## X.Ocupacion_Hoteles_diff                               0.94
## Llegada_Tur_Ext_diff                                   0.54
## Llegada_Tur_Nac_diff                                   0.86
## Cuartos_Disponibles_Promedio_log_diff                  0.27
## Estadia_Promedio_log_diff                             -0.06
##                                       Cuartos_Registrados_diff
## MES                                                       0.17
## AÑO                                                       0.00
## Densidad_Ocupación_log                                    0.07
## Cuartos_Disponibles_diff                                  0.99
## Cuartos_Ocupados_diff                                     0.23
## Cuartos_Registrados_diff                                  1.00
## Derrama_Economica_Est_mdp_diff                            0.22
## Turistas_Noche_Ext_diff                                   0.11
## Turistas_Noche_Nac_diff                                   0.28
## X.Ocupacion_Hoteles_diff                                 -0.10
## Llegada_Tur_Ext_diff                                      0.20
## Llegada_Tur_Nac_diff                                      0.23
## Cuartos_Disponibles_Promedio_log_diff                     0.27
## Estadia_Promedio_log_diff                                 0.02
##                                       Derrama_Economica_Est_mdp_diff
## MES                                                            -0.10
## AÑO                                                             0.04
## Densidad_Ocupación_log                                          0.18
## Cuartos_Disponibles_diff                                        0.24
## Cuartos_Ocupados_diff                                           0.76
## Cuartos_Registrados_diff                                        0.22
## Derrama_Economica_Est_mdp_diff                                  1.00
## Turistas_Noche_Ext_diff                                         0.45
## Turistas_Noche_Nac_diff                                         0.85
## X.Ocupacion_Hoteles_diff                                        0.69
## Llegada_Tur_Ext_diff                                            0.37
## Llegada_Tur_Nac_diff                                            0.74
## Cuartos_Disponibles_Promedio_log_diff                           0.18
## Estadia_Promedio_log_diff                                       0.15
##                                       Turistas_Noche_Ext_diff
## MES                                                      0.09
## AÑO                                                     -0.01
## Densidad_Ocupación_log                                   0.11
## Cuartos_Disponibles_diff                                 0.14
## Cuartos_Ocupados_diff                                    0.55
## Cuartos_Registrados_diff                                 0.11
## Derrama_Economica_Est_mdp_diff                           0.45
## Turistas_Noche_Ext_diff                                  1.00
## Turistas_Noche_Nac_diff                                  0.49
## X.Ocupacion_Hoteles_diff                                 0.52
## Llegada_Tur_Ext_diff                                     0.89
## Llegada_Tur_Nac_diff                                     0.44
## Cuartos_Disponibles_Promedio_log_diff                    0.13
## Estadia_Promedio_log_diff                               -0.07
##                                       Turistas_Noche_Nac_diff
## MES                                                     -0.03
## AÑO                                                     -0.01
## Densidad_Ocupación_log                                   0.12
## Cuartos_Disponibles_diff                                 0.30
## Cuartos_Ocupados_diff                                    0.90
## Cuartos_Registrados_diff                                 0.28
## Derrama_Economica_Est_mdp_diff                           0.85
## Turistas_Noche_Ext_diff                                  0.49
## Turistas_Noche_Nac_diff                                  1.00
## X.Ocupacion_Hoteles_diff                                 0.81
## Llegada_Tur_Ext_diff                                     0.43
## Llegada_Tur_Nac_diff                                     0.91
## Cuartos_Disponibles_Promedio_log_diff                    0.23
## Estadia_Promedio_log_diff                                0.11
##                                       X.Ocupacion_Hoteles_diff
## MES                                                      -0.15
## AÑO                                                       0.01
## Densidad_Ocupación_log                                   -0.03
## Cuartos_Disponibles_diff                                 -0.06
## Cuartos_Ocupados_diff                                     0.94
## Cuartos_Registrados_diff                                 -0.10
## Derrama_Economica_Est_mdp_diff                            0.69
## Turistas_Noche_Ext_diff                                   0.52
## Turistas_Noche_Nac_diff                                   0.81
## X.Ocupacion_Hoteles_diff                                  1.00
## Llegada_Tur_Ext_diff                                      0.47
## Llegada_Tur_Nac_diff                                      0.79
## Cuartos_Disponibles_Promedio_log_diff                     0.17
## Estadia_Promedio_log_diff                                -0.07
##                                       Llegada_Tur_Ext_diff Llegada_Tur_Nac_diff
## MES                                                   0.13                 0.10
## AÑO                                                  -0.01                 0.01
## Densidad_Ocupación_log                                0.07                 0.10
## Cuartos_Disponibles_diff                              0.23                 0.25
## Cuartos_Ocupados_diff                                 0.54                 0.86
## Cuartos_Registrados_diff                              0.20                 0.23
## Derrama_Economica_Est_mdp_diff                        0.37                 0.74
## Turistas_Noche_Ext_diff                               0.89                 0.44
## Turistas_Noche_Nac_diff                               0.43                 0.91
## X.Ocupacion_Hoteles_diff                              0.47                 0.79
## Llegada_Tur_Ext_diff                                  1.00                 0.45
## Llegada_Tur_Nac_diff                                  0.45                 1.00
## Cuartos_Disponibles_Promedio_log_diff                 0.13                 0.20
## Estadia_Promedio_log_diff                            -0.29                -0.21
##                                       Cuartos_Disponibles_Promedio_log_diff
## MES                                                                    0.12
## AÑO                                                                   -0.01
## Densidad_Ocupación_log                                                -0.09
## Cuartos_Disponibles_diff                                               0.33
## Cuartos_Ocupados_diff                                                  0.27
## Cuartos_Registrados_diff                                               0.27
## Derrama_Economica_Est_mdp_diff                                         0.18
## Turistas_Noche_Ext_diff                                                0.13
## Turistas_Noche_Nac_diff                                                0.23
## X.Ocupacion_Hoteles_diff                                               0.17
## Llegada_Tur_Ext_diff                                                   0.13
## Llegada_Tur_Nac_diff                                                   0.20
## Cuartos_Disponibles_Promedio_log_diff                                  1.00
## Estadia_Promedio_log_diff                                              0.07
##                                       Estadia_Promedio_log_diff
## MES                                                       -0.29
## AÑO                                                       -0.02
## Densidad_Ocupación_log                                     0.04
## Cuartos_Disponibles_diff                                   0.03
## Cuartos_Ocupados_diff                                     -0.06
## Cuartos_Registrados_diff                                   0.02
## Derrama_Economica_Est_mdp_diff                             0.15
## Turistas_Noche_Ext_diff                                   -0.07
## Turistas_Noche_Nac_diff                                    0.11
## X.Ocupacion_Hoteles_diff                                  -0.07
## Llegada_Tur_Ext_diff                                      -0.29
## Llegada_Tur_Nac_diff                                      -0.21
## Cuartos_Disponibles_Promedio_log_diff                      0.07
## Estadia_Promedio_log_diff                                  1.00
library(caret)

# Detectar columnas con alta correlación
cor_matriz <- cor(df_final, use = "complete.obs")
columnas_a_remover <- findCorrelation(cor_matriz, cutoff = 0.9)

# Ver nombres de columnas altamente correlacionadas
names(df_final)[columnas_a_remover]
## [1] "Cuartos_Ocupados_diff"    "Turistas_Noche_Nac_diff" 
## [3] "Cuartos_Disponibles_diff"
sum(is.na(df_final$Derrama_Económica_Est_mdp_diff))
## [1] 0
which(is.na(df_final$Derrama_Económica_Est_mdp_diff))
## integer(0)

Modelos con variables explicativas

y <- ts(df_final$Derrama_Economica_Est_mdp_diff, start = c(2015, 1), frequency = 12)
variables_explicativas <- c(
  "X.Ocupacion_Hoteles_diff",
  "Cuartos_Disponibles_Promedio_log_diff", "Estadia_Promedio_log_diff",
  "Cuartos_Registrados_diff", "Llegada_Tur_Nac_diff")

#"Turistas_Noche_Ext_diff"
#quitar por "Cuartos_Ocupados_diff"    "Turistas_Noche_Nac_diff"  "Cuartos_Disponibles_diff", "Densidad_Ocupación_log"
# Se elimina  por baja significancia estadística en el modelo SARIMAX y alto VIF (> 6)

df_mod <- dplyr::select(df_final, any_of(variables_explicativas))

explicativa_nacional<-("Llegada_Tur_Nac_diff")
df_nacional <- dplyr::select(df_final, any_of(explicativa_nacional))

explicativa_ext<-("Llegada_Tur_Ext_diff")
df_ext <- dplyr::select(df_final, any_of(explicativa_ext))
names(df_mod)
## [1] "X.Ocupacion_Hoteles_diff"             
## [2] "Cuartos_Disponibles_Promedio_log_diff"
## [3] "Estadia_Promedio_log_diff"            
## [4] "Cuartos_Registrados_diff"             
## [5] "Llegada_Tur_Nac_diff"
names(df_nacional)
## [1] "Llegada_Tur_Nac_diff"
names(df_ext)
## [1] "Llegada_Tur_Ext_diff"
df_mod<- scale(df_mod)
df_mod<-as.data.frame(df_mod)
colnames(df_mod) <- variables_explicativas

Los valores de VIF se encuentran todos por debajo del umbral crítico de 5, lo que indica ausencia de multicolinealidad severa entre las variables explicativas. Por tanto, no se considera necesario eliminar ninguna variable del modelo por este motivo.

library(car)

# Calcular VIF correctamente
modelo_vif <- lm(rep(1, nrow(df_mod)) ~ ., data = df_mod)
vif(modelo_vif)
##              X.Ocupacion_Hoteles_diff Cuartos_Disponibles_Promedio_log_diff 
##                              3.742834                              1.135154 
##             Estadia_Promedio_log_diff              Cuartos_Registrados_diff 
##                              1.115662                              1.507928 
##                  Llegada_Tur_Nac_diff 
##                              4.032646

SARIMAX

df_xreg <- as.matrix(df_mod)
str(df_xreg)  # debe ser matriz
##  num [1:120, 1:5] -0.55 2.16 -0.333 0.613 1.217 ...
##  - attr(*, "dimnames")=List of 2
##   ..$ : chr [1:120] "132" "133" "134" "135" ...
##   ..$ : chr [1:5] "X.Ocupacion_Hoteles_diff" "Cuartos_Disponibles_Promedio_log_diff" "Estadia_Promedio_log_diff" "Cuartos_Registrados_diff" ...
is.matrix(df_xreg)  # debe ser TRUE
## [1] TRUE
nrow(df_xreg) == length(y)
## [1] TRUE
modelo_auto <- auto.arima(y,
                          xreg = df_xreg,
                          seasonal = TRUE,
                          stepwise = FALSE,  # más preciso pero más lento
                          approximation = FALSE)  # evita simplificaciones

summary(modelo_auto)
## Series: y 
## Regression with ARIMA(0,0,0)(0,0,2)[12] errors 
## 
## Coefficients:
##         sma1     sma2  X.Ocupacion_Hoteles_diff
##       0.1518  -0.2405                   63.3974
## s.e.  0.0961   0.1278                   22.7007
##       Cuartos_Disponibles_Promedio_log_diff  Estadia_Promedio_log_diff
##                                     -7.0413                    68.9029
## s.e.                                11.9876                    12.5118
##       Cuartos_Registrados_diff  Llegada_Tur_Nac_diff
##                        26.9693              126.4270
## s.e.                   14.0925               23.1622
## 
## sigma^2 = 17377:  log likelihood = -753.4
## AIC=1522.8   AICc=1524.1   BIC=1545.1
## 
## Training set error measures:
##                    ME     RMSE      MAE      MPE     MAPE      MASE        ACF1
## Training set 11.25074 127.9207 69.11592 350.2904 423.8353 0.3990119 -0.08646305
checkresiduals(modelo_auto)

## 
##  Ljung-Box test
## 
## data:  Residuals from Regression with ARIMA(0,0,0)(0,0,2)[12] errors
## Q* = 9.9514, df = 22, p-value = 0.9867
## 
## Model df: 2.   Total lags used: 24
df_xreg <- as.matrix(df_mod)
modelo_sarimax1 <- arima(y,
                        order = c(2, 0, 2),
                        seasonal = list(order = c(0, 1, 1), period = 12),
                        xreg = df_xreg)

summary(modelo_sarimax1)
## 
## Call:
## arima(x = y, order = c(2, 0, 2), seasonal = list(order = c(0, 1, 1), period = 12), 
##     xreg = df_xreg)
## 
## Coefficients:
##          ar1      ar2      ma1     ma2     sma1  X.Ocupacion_Hoteles_diff
##       0.0971  -0.8732  -0.1814  0.8402  -0.8194                   88.9547
## s.e.  0.1040      NaN   0.0571     NaN   0.1200                   34.7696
##       Cuartos_Disponibles_Promedio_log_diff  Estadia_Promedio_log_diff
##                                      1.7122                    46.8869
## s.e.                                23.8179                    14.9027
##       Cuartos_Registrados_diff  Llegada_Tur_Nac_diff
##                        19.7931              105.2471
## s.e.                   55.4120               38.2417
## 
## sigma^2 estimated as 18845:  log likelihood = -691.02,  aic = 1404.05
## 
## Training set error measures:
##                   ME     RMSE     MAE       MPE    MAPE      MASE        ACF1
## Training set 6.82703 130.2332 66.2072 -623.8071 698.043 0.2657356 -0.03728186
checkresiduals(modelo_sarimax1)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,0,2)(0,1,1)[12]
## Q* = 15.69, df = 19, p-value = 0.6779
## 
## Model df: 5.   Total lags used: 24
# Extraer los coeficientes del modelo
coeficientes <- coef(modelo_sarimax1)

# Ordenar los coeficientes de mayor a menor valor absoluto
coef_ordenados <- sort(coeficientes, decreasing = TRUE)
print(coef_ordenados)
##                  Llegada_Tur_Nac_diff              X.Ocupacion_Hoteles_diff 
##                          105.24705885                           88.95472619 
##             Estadia_Promedio_log_diff              Cuartos_Registrados_diff 
##                           46.88692360                           19.79305252 
## Cuartos_Disponibles_Promedio_log_diff                                   ma2 
##                            1.71215029                            0.84024029 
##                                   ar1                                   ma1 
##                            0.09711155                           -0.18139731 
##                                  sma1                                   ar2 
##                           -0.81938372                           -0.87316886
colnames(modelo_sarimax1$xreg)  # debe darme las 7 columnas
## NULL
# Orden por valor absoluto
coef_ordenados_abs <- coeficientes[order(abs(coeficientes), decreasing = TRUE)]

# Lista ordenada por valor absoluto
print(coef_ordenados_abs)
##                  Llegada_Tur_Nac_diff              X.Ocupacion_Hoteles_diff 
##                          105.24705885                           88.95472619 
##             Estadia_Promedio_log_diff              Cuartos_Registrados_diff 
##                           46.88692360                           19.79305252 
## Cuartos_Disponibles_Promedio_log_diff                                   ar2 
##                            1.71215029                           -0.87316886 
##                                   ma2                                  sma1 
##                            0.84024029                           -0.81938372 
##                                   ma1                                   ar1 
##                           -0.18139731                            0.09711155

Causalidad

De todas las variables evaluadas, solo Cuartos_Registrados_diff muestra evidencia estadística significativa de ser influida por la derrama económica, en el sentido de la causalidad de Granger. Esto sugiere que la derrama puede ser un buen predictor de los cambios en los cuartos registrados, pero no de las otras variables turísticas evaluadas.

library(lmtest)

y_rev <- df_final$Derrama_Economica_Est_mdp_diff
lag_opt <- 2  #  mismo número de rezagos que VAR

for (var in variables_explicativas) {
  x_rev <- df_final[[var]]
  
  cat("\n--- ¿La derrama causa a", var, "? ---\n")
  resultado <- tryCatch({
    grangertest(x_rev ~ y_rev, order = lag_opt)
  }, error = function(e) e)
  
  print(resultado)
}
## 
## --- ¿La derrama causa a X.Ocupacion_Hoteles_diff ? ---
## Granger causality test
## 
## Model 1: x_rev ~ Lags(x_rev, 1:2) + Lags(y_rev, 1:2)
## Model 2: x_rev ~ Lags(x_rev, 1:2)
##   Res.Df Df      F Pr(>F)
## 1    113                 
## 2    115 -2 0.6394 0.5295
## 
## --- ¿La derrama causa a Cuartos_Disponibles_Promedio_log_diff ? ---
## Granger causality test
## 
## Model 1: x_rev ~ Lags(x_rev, 1:2) + Lags(y_rev, 1:2)
## Model 2: x_rev ~ Lags(x_rev, 1:2)
##   Res.Df Df      F Pr(>F)
## 1    113                 
## 2    115 -2 0.0008 0.9992
## 
## --- ¿La derrama causa a Estadia_Promedio_log_diff ? ---
## Granger causality test
## 
## Model 1: x_rev ~ Lags(x_rev, 1:2) + Lags(y_rev, 1:2)
## Model 2: x_rev ~ Lags(x_rev, 1:2)
##   Res.Df Df      F Pr(>F)
## 1    113                 
## 2    115 -2 0.3659 0.6944
## 
## --- ¿La derrama causa a Cuartos_Registrados_diff ? ---
## Granger causality test
## 
## Model 1: x_rev ~ Lags(x_rev, 1:2) + Lags(y_rev, 1:2)
## Model 2: x_rev ~ Lags(x_rev, 1:2)
##   Res.Df Df      F    Pr(>F)    
## 1    113                        
## 2    115 -2 8.3902 0.0004005 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## --- ¿La derrama causa a Llegada_Tur_Nac_diff ? ---
## Granger causality test
## 
## Model 1: x_rev ~ Lags(x_rev, 1:2) + Lags(y_rev, 1:2)
## Model 2: x_rev ~ Lags(x_rev, 1:2)
##   Res.Df Df      F Pr(>F)
## 1    113                 
## 2    115 -2 1.8739 0.1583
df_mod_lag1 <- df_mod

# se aplica lag SOLO a las variables endógenas detectadas
vars_a_rezagar <- c("Cuartos_Registrados_diff")
df_mod_lag1[vars_a_rezagar] <- dplyr::lag(df_mod[vars_a_rezagar], 1)

# se elimina la primera fila con NA generado por el rezago
df_mod_lag1 <- df_mod_lag1[-1, ]
y_lag1 <- y[-1]  # se alinea la serie dependiente
modelo_sarimax_lag1 <- arima(y_lag1,
                             order = c(2, 0, 2),
                             seasonal = list(order = c(0, 1, 1), period = 12),
                             xreg = as.matrix(df_mod_lag1))

summary(modelo_sarimax_lag1)
## 
## Call:
## arima(x = y_lag1, order = c(2, 0, 2), seasonal = list(order = c(0, 1, 1), period = 12), 
##     xreg = as.matrix(df_mod_lag1))
## 
## Coefficients:
##           ar1      ar2     ma1     ma2     sma1  X.Ocupacion_Hoteles_diff
##       -0.1981  -0.9893  0.1649  1.0000  -0.7744                   78.3118
## s.e.   0.0327   0.0275  0.0548  0.0367   0.1056                   30.4882
##       Cuartos_Disponibles_Promedio_log_diff  Estadia_Promedio_log_diff
##                                     -0.7006                    51.7260
## s.e.                                12.7070                    14.5539
##       Cuartos_Registrados_diff  Llegada_Tur_Nac_diff
##                        -0.3505              125.1931
## s.e.                   30.6991               31.0594
## 
## sigma^2 estimated as 18385:  log likelihood = -684.38,  aic = 1390.75
## 
## Training set error measures:
##                    ME     RMSE      MAE       MPE     MAPE      MASE       ACF1
## Training set 6.337942 128.5732 66.49939 -410.8189 486.0812 0.2671839 -0.0869812
checkresiduals(modelo_sarimax_lag1)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,0,2)(0,1,1)[12]
## Q* = 11.638, df = 5, p-value = 0.0401
## 
## Model df: 5.   Total lags used: 10
# Comparar AIC, BIC y logLik
AIC(modelo_sarimax1)
## [1] 1404.049
AIC(modelo_sarimax_lag1)
## [1] 1390.751
BIC(modelo_sarimax1)
## [1] 1433.553
BIC(modelo_sarimax_lag1)
## [1] 1420.153
logLik(modelo_sarimax1)
## 'log Lik.' -691.0246 (df=11)
logLik(modelo_sarimax_lag1)
## 'log Lik.' -684.3757 (df=11)

VAR

var_data <- cbind(
  y,  #  variable dependiente diferenciada ->Derrama_Economica_Est_mdp_diff
  df_mod  # regresores ya diferenciados
)

var_data_ts <- ts(var_data, start = c(2015,1), frequency = 12)
lag_selection <-VARselect(var_data_ts, lag.max = 12, type = "const")

print(lag_selection$selection)
## AIC(n)  HQ(n)  SC(n) FPE(n) 
##     11      2      1     11
optimal_lag <- lag_selection$selection["AIC(n)"]

colnames(var_data_ts)
## [1] "y"                                           
## [2] "df_mod.X.Ocupacion_Hoteles_diff"             
## [3] "df_mod.Cuartos_Disponibles_Promedio_log_diff"
## [4] "df_mod.Estadia_Promedio_log_diff"            
## [5] "df_mod.Cuartos_Registrados_diff"             
## [6] "df_mod.Llegada_Tur_Nac_diff"
modelo_var <- VAR(var_data_ts, p = 2 , type = "const")
summary(modelo_var)
## 
## VAR Estimation Results:
## ========================= 
## Endogenous variables: y, df_mod.X.Ocupacion_Hoteles_diff, df_mod.Cuartos_Disponibles_Promedio_log_diff, df_mod.Estadia_Promedio_log_diff, df_mod.Cuartos_Registrados_diff, df_mod.Llegada_Tur_Nac_diff 
## Deterministic variables: const 
## Sample size: 118 
## Log Likelihood: -1321.085 
## Roots of the characteristic polynomial:
##  0.72  0.72 0.6582 0.6582 0.6114 0.6114 0.5447 0.5447 0.4128 0.4011 0.4011 0.006534
## Call:
## VAR(y = var_data_ts, p = 2, type = "const")
## 
## 
## Estimation results for equation y: 
## ================================== 
## y = y.l1 + df_mod.X.Ocupacion_Hoteles_diff.l1 + df_mod.Cuartos_Disponibles_Promedio_log_diff.l1 + df_mod.Estadia_Promedio_log_diff.l1 + df_mod.Cuartos_Registrados_diff.l1 + df_mod.Llegada_Tur_Nac_diff.l1 + y.l2 + df_mod.X.Ocupacion_Hoteles_diff.l2 + df_mod.Cuartos_Disponibles_Promedio_log_diff.l2 + df_mod.Estadia_Promedio_log_diff.l2 + df_mod.Cuartos_Registrados_diff.l2 + df_mod.Llegada_Tur_Nac_diff.l2 + const 
## 
##                                                  Estimate Std. Error t value
## y.l1                                             -0.04433    0.14410  -0.308
## df_mod.X.Ocupacion_Hoteles_diff.l1               46.39130   39.06338   1.188
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l1  27.41926   24.03880   1.141
## df_mod.Estadia_Promedio_log_diff.l1             -23.33355   22.35517  -1.044
## df_mod.Cuartos_Registrados_diff.l1              -88.38600   37.03677  -2.386
## df_mod.Llegada_Tur_Nac_diff.l1                  -67.73140   44.74941  -1.514
## y.l2                                             -0.17700    0.14427  -1.227
## df_mod.X.Ocupacion_Hoteles_diff.l2               43.08993   41.50347   1.038
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l2   0.26986   21.91433   0.012
## df_mod.Estadia_Promedio_log_diff.l2             -47.77511   21.96251  -2.175
## df_mod.Cuartos_Registrados_diff.l2              -48.83065   39.45319  -1.238
## df_mod.Llegada_Tur_Nac_diff.l2                  -59.77407   49.48410  -1.208
## const                                            14.76271   18.85252   0.783
##                                                 Pr(>|t|)  
## y.l1                                              0.7590  
## df_mod.X.Ocupacion_Hoteles_diff.l1                0.2377  
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l1   0.2566  
## df_mod.Estadia_Promedio_log_diff.l1               0.2990  
## df_mod.Cuartos_Registrados_diff.l1                0.0188 *
## df_mod.Llegada_Tur_Nac_diff.l1                    0.1331  
## y.l2                                              0.2226  
## df_mod.X.Ocupacion_Hoteles_diff.l2                0.3016  
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l2   0.9902  
## df_mod.Estadia_Promedio_log_diff.l2               0.0319 *
## df_mod.Cuartos_Registrados_diff.l2                0.2186  
## df_mod.Llegada_Tur_Nac_diff.l2                    0.2298  
## const                                             0.4354  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Residual standard error: 202.5 on 105 degrees of freedom
## Multiple R-Squared: 0.3018,  Adjusted R-squared: 0.222 
## F-statistic: 3.782 on 12 and 105 DF,  p-value: 0.00008898 
## 
## 
## Estimation results for equation df_mod.X.Ocupacion_Hoteles_diff: 
## ================================================================ 
## df_mod.X.Ocupacion_Hoteles_diff = y.l1 + df_mod.X.Ocupacion_Hoteles_diff.l1 + df_mod.Cuartos_Disponibles_Promedio_log_diff.l1 + df_mod.Estadia_Promedio_log_diff.l1 + df_mod.Cuartos_Registrados_diff.l1 + df_mod.Llegada_Tur_Nac_diff.l1 + y.l2 + df_mod.X.Ocupacion_Hoteles_diff.l2 + df_mod.Cuartos_Disponibles_Promedio_log_diff.l2 + df_mod.Estadia_Promedio_log_diff.l2 + df_mod.Cuartos_Registrados_diff.l2 + df_mod.Llegada_Tur_Nac_diff.l2 + const 
## 
##                                                   Estimate Std. Error t value
## y.l1                                             0.0003510  0.0006207   0.566
## df_mod.X.Ocupacion_Hoteles_diff.l1               0.1873106  0.1682642   1.113
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l1 -0.0485772  0.1035463  -0.469
## df_mod.Estadia_Promedio_log_diff.l1             -0.1241703  0.0962942  -1.289
## df_mod.Cuartos_Registrados_diff.l1              -0.0059092  0.1595347  -0.037
## df_mod.Llegada_Tur_Nac_diff.l1                  -0.3231919  0.1927566  -1.677
## y.l2                                            -0.0005298  0.0006214  -0.853
## df_mod.X.Ocupacion_Hoteles_diff.l2              -0.3285303  0.1787748  -1.838
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l2 -0.1947779  0.0943952  -2.063
## df_mod.Estadia_Promedio_log_diff.l2             -0.1577395  0.0946028  -1.667
## df_mod.Cuartos_Registrados_diff.l2               0.0878720  0.1699433   0.517
## df_mod.Llegada_Tur_Nac_diff.l2                   0.1016579  0.2131511   0.477
## const                                           -0.0044738  0.0812066  -0.055
##                                                 Pr(>|t|)  
## y.l1                                              0.5729  
## df_mod.X.Ocupacion_Hoteles_diff.l1                0.2682  
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l1   0.6399  
## df_mod.Estadia_Promedio_log_diff.l1               0.2001  
## df_mod.Cuartos_Registrados_diff.l1                0.9705  
## df_mod.Llegada_Tur_Nac_diff.l1                    0.0966 .
## y.l2                                              0.3958  
## df_mod.X.Ocupacion_Hoteles_diff.l2                0.0689 .
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l2   0.0415 *
## df_mod.Estadia_Promedio_log_diff.l2               0.0984 .
## df_mod.Cuartos_Registrados_diff.l2                0.6062  
## df_mod.Llegada_Tur_Nac_diff.l2                    0.6344  
## const                                             0.9562  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Residual standard error: 0.8721 on 105 degrees of freedom
## Multiple R-Squared: 0.2996,  Adjusted R-squared: 0.2195 
## F-statistic: 3.743 on 12 and 105 DF,  p-value: 0.0001014 
## 
## 
## Estimation results for equation df_mod.Cuartos_Disponibles_Promedio_log_diff: 
## ============================================================================= 
## df_mod.Cuartos_Disponibles_Promedio_log_diff = y.l1 + df_mod.X.Ocupacion_Hoteles_diff.l1 + df_mod.Cuartos_Disponibles_Promedio_log_diff.l1 + df_mod.Estadia_Promedio_log_diff.l1 + df_mod.Cuartos_Registrados_diff.l1 + df_mod.Llegada_Tur_Nac_diff.l1 + y.l2 + df_mod.X.Ocupacion_Hoteles_diff.l2 + df_mod.Cuartos_Disponibles_Promedio_log_diff.l2 + df_mod.Estadia_Promedio_log_diff.l2 + df_mod.Cuartos_Registrados_diff.l2 + df_mod.Llegada_Tur_Nac_diff.l2 + const 
## 
##                                                   Estimate Std. Error t value
## y.l1                                            -0.0002376  0.0007165  -0.332
## df_mod.X.Ocupacion_Hoteles_diff.l1               0.3395987  0.1942272   1.748
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l1 -0.0032049  0.1195234  -0.027
## df_mod.Estadia_Promedio_log_diff.l1             -0.0393541  0.1111522  -0.354
## df_mod.Cuartos_Registrados_diff.l1              -0.0415825  0.1841507  -0.226
## df_mod.Llegada_Tur_Nac_diff.l1                  -0.3516722  0.2224987  -1.581
## y.l2                                            -0.0004665  0.0007173  -0.650
## df_mod.X.Ocupacion_Hoteles_diff.l2              -0.0180316  0.2063596  -0.087
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l2 -0.0504195  0.1089603  -0.463
## df_mod.Estadia_Promedio_log_diff.l2             -0.0023752  0.1091999  -0.022
## df_mod.Cuartos_Registrados_diff.l2              -0.3234462  0.1961654  -1.649
## df_mod.Llegada_Tur_Nac_diff.l2                   0.2826182  0.2460401   1.149
## const                                            0.0114467  0.0937367   0.122
##                                                 Pr(>|t|)  
## y.l1                                              0.7408  
## df_mod.X.Ocupacion_Hoteles_diff.l1                0.0833 .
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l1   0.9787  
## df_mod.Estadia_Promedio_log_diff.l1               0.7240  
## df_mod.Cuartos_Registrados_diff.l1                0.8218  
## df_mod.Llegada_Tur_Nac_diff.l1                    0.1170  
## y.l2                                              0.5169  
## df_mod.X.Ocupacion_Hoteles_diff.l2                0.9305  
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l2   0.6445  
## df_mod.Estadia_Promedio_log_diff.l2               0.9827  
## df_mod.Cuartos_Registrados_diff.l2                0.1022  
## df_mod.Llegada_Tur_Nac_diff.l2                    0.2533  
## const                                             0.9030  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Residual standard error: 1.007 on 105 degrees of freedom
## Multiple R-Squared: 0.1056,  Adjusted R-squared: 0.003347 
## F-statistic: 1.033 on 12 and 105 DF,  p-value: 0.4249 
## 
## 
## Estimation results for equation df_mod.Estadia_Promedio_log_diff: 
## ================================================================= 
## df_mod.Estadia_Promedio_log_diff = y.l1 + df_mod.X.Ocupacion_Hoteles_diff.l1 + df_mod.Cuartos_Disponibles_Promedio_log_diff.l1 + df_mod.Estadia_Promedio_log_diff.l1 + df_mod.Cuartos_Registrados_diff.l1 + df_mod.Llegada_Tur_Nac_diff.l1 + y.l2 + df_mod.X.Ocupacion_Hoteles_diff.l2 + df_mod.Cuartos_Disponibles_Promedio_log_diff.l2 + df_mod.Estadia_Promedio_log_diff.l2 + df_mod.Cuartos_Registrados_diff.l2 + df_mod.Llegada_Tur_Nac_diff.l2 + const 
## 
##                                                   Estimate Std. Error t value
## y.l1                                             0.0001098  0.0006671   0.165
## df_mod.X.Ocupacion_Hoteles_diff.l1               0.0165186  0.1808344   0.091
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l1  0.0336351  0.1112817   0.302
## df_mod.Estadia_Promedio_log_diff.l1             -0.0813127  0.1034878  -0.786
## df_mod.Cuartos_Registrados_diff.l1              -0.2048106  0.1714527  -1.195
## df_mod.Llegada_Tur_Nac_diff.l1                  -0.3396490  0.2071564  -1.640
## y.l2                                            -0.0002613  0.0006679  -0.391
## df_mod.X.Ocupacion_Hoteles_diff.l2               0.4492070  0.1921302   2.338
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l2  0.0515244  0.1014470   0.508
## df_mod.Estadia_Promedio_log_diff.l2             -0.2737796  0.1016701  -2.693
## df_mod.Cuartos_Registrados_diff.l2              -0.3530060  0.1826389  -1.933
## df_mod.Llegada_Tur_Nac_diff.l2                  -0.2569223  0.2290745  -1.122
## const                                           -0.0099133  0.0872731  -0.114
##                                                 Pr(>|t|)   
## y.l1                                             0.86953   
## df_mod.X.Ocupacion_Hoteles_diff.l1               0.92739   
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l1  0.76306   
## df_mod.Estadia_Promedio_log_diff.l1              0.43380   
## df_mod.Cuartos_Registrados_diff.l1               0.23495   
## df_mod.Llegada_Tur_Nac_diff.l1                   0.10409   
## y.l2                                             0.69645   
## df_mod.X.Ocupacion_Hoteles_diff.l2               0.02128 * 
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l2  0.61259   
## df_mod.Estadia_Promedio_log_diff.l2              0.00825 **
## df_mod.Cuartos_Registrados_diff.l2               0.05595 . 
## df_mod.Llegada_Tur_Nac_diff.l2                   0.26461   
## const                                            0.90978   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Residual standard error: 0.9372 on 105 degrees of freedom
## Multiple R-Squared: 0.2114,  Adjusted R-squared: 0.1212 
## F-statistic: 2.345 on 12 and 105 DF,  p-value: 0.01045 
## 
## 
## Estimation results for equation df_mod.Cuartos_Registrados_diff: 
## ================================================================ 
## df_mod.Cuartos_Registrados_diff = y.l1 + df_mod.X.Ocupacion_Hoteles_diff.l1 + df_mod.Cuartos_Disponibles_Promedio_log_diff.l1 + df_mod.Estadia_Promedio_log_diff.l1 + df_mod.Cuartos_Registrados_diff.l1 + df_mod.Llegada_Tur_Nac_diff.l1 + y.l2 + df_mod.X.Ocupacion_Hoteles_diff.l2 + df_mod.Cuartos_Disponibles_Promedio_log_diff.l2 + df_mod.Estadia_Promedio_log_diff.l2 + df_mod.Cuartos_Registrados_diff.l2 + df_mod.Llegada_Tur_Nac_diff.l2 + const 
## 
##                                                   Estimate Std. Error t value
## y.l1                                            -0.0002034  0.0004129  -0.493
## df_mod.X.Ocupacion_Hoteles_diff.l1              -0.1521385  0.1119433  -1.359
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l1  0.3703495  0.0688876   5.376
## df_mod.Estadia_Promedio_log_diff.l1             -0.0323823  0.0640628  -0.505
## df_mod.Cuartos_Registrados_diff.l1              -1.1329518  0.1061356 -10.675
## df_mod.Llegada_Tur_Nac_diff.l1                   0.2977928  0.1282376   2.322
## y.l2                                             0.0001525  0.0004134   0.369
## df_mod.X.Ocupacion_Hoteles_diff.l2               0.2947862  0.1189358   2.479
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l2  0.0983488  0.0627995   1.566
## df_mod.Estadia_Promedio_log_diff.l2              0.0824755  0.0629376   1.310
## df_mod.Cuartos_Registrados_diff.l2              -0.4856368  0.1130603  -4.295
## df_mod.Llegada_Tur_Nac_diff.l2                   0.0109520  0.1418057   0.077
## const                                            0.0035099  0.0540253   0.065
##                                                             Pr(>|t|)    
## y.l1                                                          0.6233    
## df_mod.X.Ocupacion_Hoteles_diff.l1                            0.1770    
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l1          0.000000463 ***
## df_mod.Estadia_Promedio_log_diff.l1                           0.6143    
## df_mod.Cuartos_Registrados_diff.l1              < 0.0000000000000002 ***
## df_mod.Llegada_Tur_Nac_diff.l1                                0.0222 *  
## y.l2                                                          0.7130    
## df_mod.X.Ocupacion_Hoteles_diff.l2                            0.0148 *  
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l2               0.1203    
## df_mod.Estadia_Promedio_log_diff.l2                           0.1929    
## df_mod.Cuartos_Registrados_diff.l2                       0.000039039 ***
## df_mod.Llegada_Tur_Nac_diff.l2                                0.9386    
## const                                                         0.9483    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Residual standard error: 0.5802 on 105 degrees of freedom
## Multiple R-Squared: 0.6954,  Adjusted R-squared: 0.6606 
## F-statistic: 19.98 on 12 and 105 DF,  p-value: < 0.00000000000000022 
## 
## 
## Estimation results for equation df_mod.Llegada_Tur_Nac_diff: 
## ============================================================ 
## df_mod.Llegada_Tur_Nac_diff = y.l1 + df_mod.X.Ocupacion_Hoteles_diff.l1 + df_mod.Cuartos_Disponibles_Promedio_log_diff.l1 + df_mod.Estadia_Promedio_log_diff.l1 + df_mod.Cuartos_Registrados_diff.l1 + df_mod.Llegada_Tur_Nac_diff.l1 + y.l2 + df_mod.X.Ocupacion_Hoteles_diff.l2 + df_mod.Cuartos_Disponibles_Promedio_log_diff.l2 + df_mod.Estadia_Promedio_log_diff.l2 + df_mod.Cuartos_Registrados_diff.l2 + df_mod.Llegada_Tur_Nac_diff.l2 + const 
## 
##                                                   Estimate Std. Error t value
## y.l1                                             0.0001178  0.0006159   0.191
## df_mod.X.Ocupacion_Hoteles_diff.l1               0.4256599  0.1669561   2.550
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l1  0.0802801  0.1027414   0.781
## df_mod.Estadia_Promedio_log_diff.l1             -0.0419492  0.0955456  -0.439
## df_mod.Cuartos_Registrados_diff.l1              -0.3416388  0.1582945  -2.158
## df_mod.Llegada_Tur_Nac_diff.l1                  -0.3889212  0.1912581  -2.033
## y.l2                                            -0.0012235  0.0006166  -1.984
## df_mod.X.Ocupacion_Hoteles_diff.l2               0.0045305  0.1773850   0.026
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l2  0.0007172  0.0936614   0.008
## df_mod.Estadia_Promedio_log_diff.l2             -0.1046886  0.0938673  -1.115
## df_mod.Cuartos_Registrados_diff.l2              -0.0104523  0.1686222  -0.062
## df_mod.Llegada_Tur_Nac_diff.l2                   0.0726591  0.2114941   0.344
## const                                            0.0132579  0.0805753   0.165
##                                                 Pr(>|t|)  
## y.l1                                              0.8486  
## df_mod.X.Ocupacion_Hoteles_diff.l1                0.0122 *
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l1   0.4363  
## df_mod.Estadia_Promedio_log_diff.l1               0.6615  
## df_mod.Cuartos_Registrados_diff.l1                0.0332 *
## df_mod.Llegada_Tur_Nac_diff.l1                    0.0445 *
## y.l2                                              0.0498 *
## df_mod.X.Ocupacion_Hoteles_diff.l2                0.9797  
## df_mod.Cuartos_Disponibles_Promedio_log_diff.l2   0.9939  
## df_mod.Estadia_Promedio_log_diff.l2               0.2673  
## df_mod.Cuartos_Registrados_diff.l2                0.9507  
## df_mod.Llegada_Tur_Nac_diff.l2                    0.7319  
## const                                             0.8696  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Residual standard error: 0.8653 on 105 degrees of freedom
## Multiple R-Squared: 0.3311,  Adjusted R-squared: 0.2547 
## F-statistic: 4.332 on 12 and 105 DF,  p-value: 0.00001441 
## 
## 
## 
## Covariance matrix of residuals:
##                                                      y
## y                                            40988.714
## df_mod.X.Ocupacion_Hoteles_diff                123.832
## df_mod.Cuartos_Disponibles_Promedio_log_diff    36.398
## df_mod.Estadia_Promedio_log_diff                 9.833
## df_mod.Cuartos_Registrados_diff                  7.978
## df_mod.Llegada_Tur_Nac_diff                    120.352
##                                              df_mod.X.Ocupacion_Hoteles_diff
## y                                                                  123.83199
## df_mod.X.Ocupacion_Hoteles_diff                                      0.76052
## df_mod.Cuartos_Disponibles_Promedio_log_diff                         0.15554
## df_mod.Estadia_Promedio_log_diff                                    -0.11180
## df_mod.Cuartos_Registrados_diff                                     -0.01555
## df_mod.Llegada_Tur_Nac_diff                                          0.62799
##                                              df_mod.Cuartos_Disponibles_Promedio_log_diff
## y                                                                                36.39789
## df_mod.X.Ocupacion_Hoteles_diff                                                   0.15554
## df_mod.Cuartos_Disponibles_Promedio_log_diff                                      1.01332
## df_mod.Estadia_Promedio_log_diff                                                 -0.01693
## df_mod.Cuartos_Registrados_diff                                                   0.33885
## df_mod.Llegada_Tur_Nac_diff                                                       0.20054
##                                              df_mod.Estadia_Promedio_log_diff
## y                                                                     9.83285
## df_mod.X.Ocupacion_Hoteles_diff                                      -0.11180
## df_mod.Cuartos_Disponibles_Promedio_log_diff                         -0.01693
## df_mod.Estadia_Promedio_log_diff                                      0.87839
## df_mod.Cuartos_Registrados_diff                                       0.02973
## df_mod.Llegada_Tur_Nac_diff                                          -0.26859
##                                              df_mod.Cuartos_Registrados_diff
## y                                                                    7.97751
## df_mod.X.Ocupacion_Hoteles_diff                                     -0.01555
## df_mod.Cuartos_Disponibles_Promedio_log_diff                         0.33885
## df_mod.Estadia_Promedio_log_diff                                     0.02973
## df_mod.Cuartos_Registrados_diff                                      0.33660
## df_mod.Llegada_Tur_Nac_diff                                          0.04484
##                                              df_mod.Llegada_Tur_Nac_diff
## y                                                              120.35238
## df_mod.X.Ocupacion_Hoteles_diff                                  0.62799
## df_mod.Cuartos_Disponibles_Promedio_log_diff                     0.20054
## df_mod.Estadia_Promedio_log_diff                                -0.26859
## df_mod.Cuartos_Registrados_diff                                  0.04484
## df_mod.Llegada_Tur_Nac_diff                                      0.74874
## 
## Correlation matrix of residuals:
##                                                    y
## y                                            1.00000
## df_mod.X.Ocupacion_Hoteles_diff              0.70137
## df_mod.Cuartos_Disponibles_Promedio_log_diff 0.17860
## df_mod.Estadia_Promedio_log_diff             0.05182
## df_mod.Cuartos_Registrados_diff              0.06792
## df_mod.Llegada_Tur_Nac_diff                  0.68700
##                                              df_mod.X.Ocupacion_Hoteles_diff
## y                                                                    0.70137
## df_mod.X.Ocupacion_Hoteles_diff                                      1.00000
## df_mod.Cuartos_Disponibles_Promedio_log_diff                         0.17718
## df_mod.Estadia_Promedio_log_diff                                    -0.13679
## df_mod.Cuartos_Registrados_diff                                     -0.03074
## df_mod.Llegada_Tur_Nac_diff                                          0.83221
##                                              df_mod.Cuartos_Disponibles_Promedio_log_diff
## y                                                                                 0.17860
## df_mod.X.Ocupacion_Hoteles_diff                                                   0.17718
## df_mod.Cuartos_Disponibles_Promedio_log_diff                                      1.00000
## df_mod.Estadia_Promedio_log_diff                                                 -0.01794
## df_mod.Cuartos_Registrados_diff                                                   0.58019
## df_mod.Llegada_Tur_Nac_diff                                                       0.23023
##                                              df_mod.Estadia_Promedio_log_diff
## y                                                                     0.05182
## df_mod.X.Ocupacion_Hoteles_diff                                      -0.13679
## df_mod.Cuartos_Disponibles_Promedio_log_diff                         -0.01794
## df_mod.Estadia_Promedio_log_diff                                      1.00000
## df_mod.Cuartos_Registrados_diff                                       0.05468
## df_mod.Llegada_Tur_Nac_diff                                          -0.33119
##                                              df_mod.Cuartos_Registrados_diff
## y                                                                    0.06792
## df_mod.X.Ocupacion_Hoteles_diff                                     -0.03074
## df_mod.Cuartos_Disponibles_Promedio_log_diff                         0.58019
## df_mod.Estadia_Promedio_log_diff                                     0.05468
## df_mod.Cuartos_Registrados_diff                                      1.00000
## df_mod.Llegada_Tur_Nac_diff                                          0.08932
##                                              df_mod.Llegada_Tur_Nac_diff
## y                                                                0.68700
## df_mod.X.Ocupacion_Hoteles_diff                                  0.83221
## df_mod.Cuartos_Disponibles_Promedio_log_diff                     0.23023
## df_mod.Estadia_Promedio_log_diff                                -0.33119
## df_mod.Cuartos_Registrados_diff                                  0.08932
## df_mod.Llegada_Tur_Nac_diff                                      1.00000
serial.test(modelo_var, lags.pt = 12, type = "PT.asymptotic")
## 
##  Portmanteau Test (asymptotic)
## 
## data:  Residuals of VAR object modelo_var
## Chi-squared = 418.28, df = 360, p-value = 0.01835
var_data_nac <- ts(cbind(y, df_nacional), start = start(y), frequency = frequency(y))
colnames(var_data_nac) <- c("y", "Llegada_Tur_Nac_diff")

# 2. Seleccionar lag óptimo
lag_selection_nac <- VARselect(var_data_nac, lag.max = 12, type = "const")
print(lag_selection_nac$selection)
## AIC(n)  HQ(n)  SC(n) FPE(n) 
##      2      2      1      2
optimal_lag_nac <- lag_selection_nac$selection["AIC(n)"]

colnames(var_data_nac)
## [1] "y"                    "Llegada_Tur_Nac_diff"
modelo_var_nac <- VAR(var_data_nac, p = 4, type = "const")
summary(modelo_var_nac)
## 
## VAR Estimation Results:
## ========================= 
## Endogenous variables: y, Llegada_Tur_Nac_diff 
## Deterministic variables: const 
## Sample size: 116 
## Log Likelihood: -2086.911 
## Roots of the characteristic polynomial:
## 0.7452 0.7452 0.6306 0.6306 0.5855 0.5855 0.481 0.3217
## Call:
## VAR(y = var_data_nac, p = 4, type = "const")
## 
## 
## Estimation results for equation y: 
## ================================== 
## y = y.l1 + Llegada_Tur_Nac_diff.l1 + y.l2 + Llegada_Tur_Nac_diff.l2 + y.l3 + Llegada_Tur_Nac_diff.l3 + y.l4 + Llegada_Tur_Nac_diff.l4 + const 
## 
##                            Estimate  Std. Error t value Pr(>|t|)  
## y.l1                    -0.12228742  0.13512190  -0.905   0.3675  
## Llegada_Tur_Nac_diff.l1 -0.00005571  0.00105016  -0.053   0.9578  
## y.l2                    -0.21555931  0.13597623  -1.585   0.1159  
## Llegada_Tur_Nac_diff.l2 -0.00146217  0.00105611  -1.384   0.1691  
## y.l3                     0.13313375  0.13457442   0.989   0.3248  
## Llegada_Tur_Nac_diff.l3 -0.00123944  0.00106484  -1.164   0.2470  
## y.l4                     0.31358490  0.13469868   2.328   0.0218 *
## Llegada_Tur_Nac_diff.l4 -0.00189734  0.00105570  -1.797   0.0751 .
## const                   12.64526822 20.05607432   0.630   0.5297  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Residual standard error: 213.1 on 107 degrees of freedom
## Multiple R-Squared: 0.2044,  Adjusted R-squared: 0.1449 
## F-statistic: 3.436 on 8 and 107 DF,  p-value: 0.001477 
## 
## 
## Estimation results for equation Llegada_Tur_Nac_diff: 
## ===================================================== 
## Llegada_Tur_Nac_diff = y.l1 + Llegada_Tur_Nac_diff.l1 + y.l2 + Llegada_Tur_Nac_diff.l2 + y.l3 + Llegada_Tur_Nac_diff.l3 + y.l4 + Llegada_Tur_Nac_diff.l4 + const 
## 
##                           Estimate Std. Error t value Pr(>|t|)   
## y.l1                       0.80088   16.96876   0.047  0.96244   
## Llegada_Tur_Nac_diff.l1   -0.14159    0.13188  -1.074  0.28540   
## y.l2                     -32.16231   17.07605  -1.883  0.06235 . 
## Llegada_Tur_Nac_diff.l2   -0.05344    0.13263  -0.403  0.68782   
## y.l3                       8.53567   16.90000   0.505  0.61455   
## Llegada_Tur_Nac_diff.l3   -0.16608    0.13372  -1.242  0.21696   
## y.l4                      48.40649   16.91561   2.862  0.00507 **
## Llegada_Tur_Nac_diff.l4   -0.33025    0.13258  -2.491  0.01427 * 
## const                    975.69914 2518.66407   0.387  0.69924   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Residual standard error: 26760 on 107 degrees of freedom
## Multiple R-Squared:  0.18,   Adjusted R-squared: 0.1186 
## F-statistic: 2.935 on 8 and 107 DF,  p-value: 0.005282 
## 
## 
## 
## Covariance matrix of residuals:
##                            y Llegada_Tur_Nac_diff
## y                      45402              3932099
## Llegada_Tur_Nac_diff 3932099            716015295
## 
## Correlation matrix of residuals:
##                           y Llegada_Tur_Nac_diff
## y                    1.0000               0.6896
## Llegada_Tur_Nac_diff 0.6896               1.0000
serial.test(modelo_var_nac, lags.pt = 12, type = "PT.asymptotic")
## 
##  Portmanteau Test (asymptotic)
## 
## data:  Residuals of VAR object modelo_var_nac
## Chi-squared = 41.961, df = 32, p-value = 0.1119
var_data_ext <- ts(cbind(y, df_ext), start = start(y), frequency = frequency(y))
colnames(var_data_ext) <- c("y", "Llegada_Tur_Ext_diff")

#2. Seleccionar lag óptimo
lag_selection_ext <- VARselect(var_data_ext, lag.max = 12, type = "const")
print(lag_selection_ext$selection)
## AIC(n)  HQ(n)  SC(n) FPE(n) 
##      2      2      2      2
optimal_lag_nac <- lag_selection_ext$selection["AIC(n)"]

colnames(var_data_ext)
## [1] "y"                    "Llegada_Tur_Ext_diff"
modelo_var_ext <- VAR(var_data_ext, p = 2, type = "const")
summary(modelo_var_ext)
## 
## VAR Estimation Results:
## ========================= 
## Endogenous variables: y, Llegada_Tur_Ext_diff 
## Deterministic variables: const 
## Sample size: 118 
## Log Likelihood: -2003.501 
## Roots of the characteristic polynomial:
## 0.5584 0.5584 0.4956 0.4956
## Call:
## VAR(y = var_data_ext, p = 2, type = "const")
## 
## 
## Estimation results for equation y: 
## ================================== 
## y = y.l1 + Llegada_Tur_Ext_diff.l1 + y.l2 + Llegada_Tur_Ext_diff.l2 + const 
## 
##                          Estimate Std. Error t value Pr(>|t|)   
## y.l1                    -0.122571   0.096841  -1.266   0.2082   
## Llegada_Tur_Ext_diff.l1  0.002690   0.002895   0.929   0.3548   
## y.l2                    -0.320348   0.095620  -3.350   0.0011 **
## Llegada_Tur_Ext_diff.l2 -0.003716   0.002915  -1.275   0.2049   
## const                   18.430551  19.803494   0.931   0.3540   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Residual standard error: 214 on 113 degrees of freedom
## Multiple R-Squared: 0.1608,  Adjusted R-squared: 0.1311 
## F-statistic: 5.413 on 4 and 113 DF,  p-value: 0.0005038 
## 
## 
## Estimation results for equation Llegada_Tur_Ext_diff: 
## ===================================================== 
## Llegada_Tur_Ext_diff = y.l1 + Llegada_Tur_Ext_diff.l1 + y.l2 + Llegada_Tur_Ext_diff.l2 + const 
## 
##                         Estimate Std. Error t value Pr(>|t|)   
## y.l1                      3.6015     3.3675   1.069  0.28713   
## Llegada_Tur_Ext_diff.l1  -0.3031     0.1007  -3.011  0.00321 **
## y.l2                      1.9717     3.3251   0.593  0.55438   
## Llegada_Tur_Ext_diff.l2  -0.2162     0.1014  -2.134  0.03504 * 
## const                   172.4222   688.6408   0.250  0.80275   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Residual standard error: 7440 on 113 degrees of freedom
## Multiple R-Squared: 0.08844, Adjusted R-squared: 0.05617 
## F-statistic: 2.741 on 4 and 113 DF,  p-value: 0.03205 
## 
## 
## 
## Covariance matrix of residuals:
##                           y Llegada_Tur_Ext_diff
## y                     45777               665863
## Llegada_Tur_Ext_diff 665863             55354616
## 
## Correlation matrix of residuals:
##                           y Llegada_Tur_Ext_diff
## y                    1.0000               0.4183
## Llegada_Tur_Ext_diff 0.4183               1.0000
serial.test(modelo_var_ext, lags.pt = 12, type = "PT.asymptotic")
## 
##  Portmanteau Test (asymptotic)
## 
## data:  Residuals of VAR object modelo_var_ext
## Chi-squared = 32.657, df = 40, p-value = 0.7886

La variable Densidad_Ocupación_log fue excluida del modelo VAR debido a la presencia de heterocedasticidad en los residuos de su ecuación, identificada mediante la prueba ARCH (p-value = 0.047 < 0.05). Esta condición viola el supuesto de varianza constante requerido para la validez de los modelos VAR, por lo que se optó por retirarla para garantizar la robustez del sistema y la fiabilidad de las inferencias.

library(FinTS)

resid_var <- residuals(modelo_var)[, "y"]  # columna de la variable de interés
ArchTest(resid_var, lags = 12)
## 
##  ARCH LM-test; Null hypothesis: no ARCH effects
## 
## data:  resid_var
## Chi-squared = 6.5256, df = 12, p-value = 0.8873
library(FinTS)

residuos_var <- residuals(modelo_var)

for (col in colnames(residuos_var)) {
  cat("\n--- ARCH test para:", col, "---\n")
  print(ArchTest(residuos_var[, col], lags = 12))
}
## 
## --- ARCH test para: y ---
## 
##  ARCH LM-test; Null hypothesis: no ARCH effects
## 
## data:  residuos_var[, col]
## Chi-squared = 6.5256, df = 12, p-value = 0.8873
## 
## 
## --- ARCH test para: df_mod.X.Ocupacion_Hoteles_diff ---
## 
##  ARCH LM-test; Null hypothesis: no ARCH effects
## 
## data:  residuos_var[, col]
## Chi-squared = 7.8935, df = 12, p-value = 0.7934
## 
## 
## --- ARCH test para: df_mod.Cuartos_Disponibles_Promedio_log_diff ---
## 
##  ARCH LM-test; Null hypothesis: no ARCH effects
## 
## data:  residuos_var[, col]
## Chi-squared = 1.5065, df = 12, p-value = 0.9999
## 
## 
## --- ARCH test para: df_mod.Estadia_Promedio_log_diff ---
## 
##  ARCH LM-test; Null hypothesis: no ARCH effects
## 
## data:  residuos_var[, col]
## Chi-squared = 3.685, df = 12, p-value = 0.9885
## 
## 
## --- ARCH test para: df_mod.Cuartos_Registrados_diff ---
## 
##  ARCH LM-test; Null hypothesis: no ARCH effects
## 
## data:  residuos_var[, col]
## Chi-squared = 3.6053, df = 12, p-value = 0.9896
## 
## 
## --- ARCH test para: df_mod.Llegada_Tur_Nac_diff ---
## 
##  ARCH LM-test; Null hypothesis: no ARCH effects
## 
## data:  residuos_var[, col]
## Chi-squared = 14.282, df = 12, p-value = 0.2831

Causalidad de Granger

Todas las variables seleccionadas tienen un poder predictivo significativo sobre la derrama económica, validando la inclusión como regresores en nuestros modelos como SARIMAX o VAR, etc, por lo que aportan información útil para predecir el comportamiento futuro de la Derrama Económica por Hoteleria.

# Prueba de Causalidad de Granger
for (var in colnames(var_data_ts)[-1]) {
  cat("¿", var, "Granger-causa a Derrama_Economica_Est_mdp_diff?\n")
  print(causality(modelo_var, cause = var)$Granger)
  cat("\n---------------------------\n")
}
## ¿ df_mod.X.Ocupacion_Hoteles_diff Granger-causa a Derrama_Economica_Est_mdp_diff?
## 
##  Granger causality H0: df_mod.X.Ocupacion_Hoteles_diff do not
##  Granger-cause y df_mod.Cuartos_Disponibles_Promedio_log_diff
##  df_mod.Estadia_Promedio_log_diff df_mod.Cuartos_Registrados_diff
##  df_mod.Llegada_Tur_Nac_diff
## 
## data:  VAR object modelo_var
## F-Test = 3.8292, df1 = 10, df2 = 630, p-value = 0.00004809
## 
## 
## ---------------------------
## ¿ df_mod.Cuartos_Disponibles_Promedio_log_diff Granger-causa a Derrama_Economica_Est_mdp_diff?
## 
##  Granger causality H0: df_mod.Cuartos_Disponibles_Promedio_log_diff do
##  not Granger-cause y df_mod.X.Ocupacion_Hoteles_diff
##  df_mod.Estadia_Promedio_log_diff df_mod.Cuartos_Registrados_diff
##  df_mod.Llegada_Tur_Nac_diff
## 
## data:  VAR object modelo_var
## F-Test = 4.4893, df1 = 10, df2 = 630, p-value = 0.000003822
## 
## 
## ---------------------------
## ¿ df_mod.Estadia_Promedio_log_diff Granger-causa a Derrama_Economica_Est_mdp_diff?
## 
##  Granger causality H0: df_mod.Estadia_Promedio_log_diff do not
##  Granger-cause y df_mod.X.Ocupacion_Hoteles_diff
##  df_mod.Cuartos_Disponibles_Promedio_log_diff
##  df_mod.Cuartos_Registrados_diff df_mod.Llegada_Tur_Nac_diff
## 
## data:  VAR object modelo_var
## F-Test = 1.1355, df1 = 10, df2 = 630, p-value = 0.3328
## 
## 
## ---------------------------
## ¿ df_mod.Cuartos_Registrados_diff Granger-causa a Derrama_Economica_Est_mdp_diff?
## 
##  Granger causality H0: df_mod.Cuartos_Registrados_diff do not
##  Granger-cause y df_mod.X.Ocupacion_Hoteles_diff
##  df_mod.Cuartos_Disponibles_Promedio_log_diff
##  df_mod.Estadia_Promedio_log_diff df_mod.Llegada_Tur_Nac_diff
## 
## data:  VAR object modelo_var
## F-Test = 4.5169, df1 = 10, df2 = 630, p-value = 0.000003433
## 
## 
## ---------------------------
## ¿ df_mod.Llegada_Tur_Nac_diff Granger-causa a Derrama_Economica_Est_mdp_diff?
## 
##  Granger causality H0: df_mod.Llegada_Tur_Nac_diff do not Granger-cause
##  y df_mod.X.Ocupacion_Hoteles_diff
##  df_mod.Cuartos_Disponibles_Promedio_log_diff
##  df_mod.Estadia_Promedio_log_diff df_mod.Cuartos_Registrados_diff
## 
## data:  VAR object modelo_var
## F-Test = 3.273, df1 = 10, df2 = 630, p-value = 0.000384
## 
## 
## ---------------------------
library(lmtest)
grangertest(df_final$Derrama_Economica_Est_mdp_diff ~ df_final$Llegada_Tur_Nac_diff, order = 2)
## Granger causality test
## 
## Model 1: df_final$Derrama_Economica_Est_mdp_diff ~ Lags(df_final$Derrama_Economica_Est_mdp_diff, 1:2) + Lags(df_final$Llegada_Tur_Nac_diff, 1:2)
## Model 2: df_final$Derrama_Economica_Est_mdp_diff ~ Lags(df_final$Derrama_Economica_Est_mdp_diff, 1:2)
##   Res.Df Df      F Pr(>F)
## 1    113                 
## 2    115 -2 1.0401 0.3568
AIC(modelo_sarimax1)
## [1] 1404.049
BIC(modelo_sarimax1)
## [1] 1433.553
logLik(modelo_sarimax1)
## 'log Lik.' -691.0246 (df=11)
AIC(modelo_sarimax_lag1)
## [1] 1390.751
BIC(modelo_sarimax_lag1)
## [1] 1420.153
logLik(modelo_sarimax_lag1)
## 'log Lik.' -684.3757 (df=11)
AIC(modelo_var)
## [1] 2798.17
BIC(modelo_var)
## [1] 3014.284
logLik(modelo_var)
## 'log Lik.' -1321.085 (df=78)
AIC(modelo_auto)
## [1] 1522.802
BIC(modelo_auto)
## [1] 1545.102
logLik(modelo_auto)
## 'log Lik.' -753.401 (df=8)
df_xreg_lag1 <- as.matrix(df_mod_lag1)  # usa df_mod_lag1
stopifnot(is.matrix(df_xreg_lag1), is.numeric(df_xreg_lag1))

n_periodos <- 36
future_xreg <- matrix(rep(df_xreg_lag1[nrow(df_xreg_lag1), ], n_periodos), 
                      nrow = n_periodos, byrow = TRUE)

growth <- (1.02)^(1:n_periodos)
future_xreg <- sweep(future_xreg, 1, growth, `*`)

colnames(future_xreg) <- colnames(df_xreg_lag1)

stopifnot(
  is.matrix(future_xreg),
  is.numeric(future_xreg),
  identical(colnames(df_xreg_lag1), colnames(future_xreg)),
  ncol(df_xreg_lag1) == ncol(future_xreg))
str(modelo_sarimax_lag1$xreg)
##  NULL
str(future_xreg)
##  num [1:36, 1:5] -1.91 -1.95 -1.99 -2.03 -2.07 ...
##  - attr(*, "dimnames")=List of 2
##   ..$ : NULL
##   ..$ : chr [1:5] "X.Ocupacion_Hoteles_diff" "Cuartos_Disponibles_Promedio_log_diff" "Estadia_Promedio_log_diff" "Cuartos_Registrados_diff" ...
identical(colnames(modelo_sarimax_lag1$xreg), colnames(future_xreg))
## [1] FALSE
ncol(modelo_sarimax_lag1$xreg) == ncol(future_xreg)
## logical(0)
colnames(df_xreg)
## [1] "X.Ocupacion_Hoteles_diff"             
## [2] "Cuartos_Disponibles_Promedio_log_diff"
## [3] "Estadia_Promedio_log_diff"            
## [4] "Cuartos_Registrados_diff"             
## [5] "Llegada_Tur_Nac_diff"
colnames(future_xreg)
## [1] "X.Ocupacion_Hoteles_diff"             
## [2] "Cuartos_Disponibles_Promedio_log_diff"
## [3] "Estadia_Promedio_log_diff"            
## [4] "Cuartos_Registrados_diff"             
## [5] "Llegada_Tur_Nac_diff"
colnames(modelo_sarimax_lag1$xreg)
## NULL

Llegada Turistas Nacionales & Extranjeros

library(openxlsx)
df <- read.xlsx("HOTELES-BD-FCST.xlsx", sheet ="HOTELES")
kable(head(df))
AÑO MES Cuartos_Disponibles Cuartos_Disponibles_Promedio Cuartos_Ocupados Cuartos_Registrados Densidad_Ocupación Derrama_Economica_Est_mdp Estadia_Promedio Imp_Hospedaje Turistas_Noche_Ext Turistas_Noche_Nac %Ocupacion_Hoteles Llegada_Tur_Ext Llegada_Tur_Nac
2004 1 285891 9142 134696 293105 1.501604 316.5167 2.125116 NA 35742 166518 0.4711446 14725 80451
2004 2 267473 9143 129500 274195 1.486286 345.1886 1.858844 NA 33262 159212 0.4841610 15841 87704
2004 3 289049 9317 154239 293105 1.576326 442.6492 1.957151 NA 38217 204914 0.5336085 18465 105762
2004 4 276364 9205 138065 283650 1.607808 377.4937 1.972139 NA 28712 193270 0.4995766 13567 98992
2004 5 289222 9322 170326 293105 1.608674 464.8694 1.933724 NA 42037 231962 0.5889109 18876 122819
2004 6 280401 9346 141969 283650 1.508153 386.5217 1.866298 NA 39146 174965 0.5063070 18032 96693
sum(is.na(df))
## [1] 132
gg_miss_var(df)

library(dplyr)
cols_log <- c( "Cuartos_Disponibles_Promedio", 
              "Densidad_Ocupación",
              "Estadia_Promedio")

# Crear las columnas transformadas y agregarlas al dataframe original
df_log <- df %>%
  mutate(across(all_of(cols_log), ~ log(. + 1), .names = "{.col}_log"))

df_log <- df_log[, !(names(df_log) %in% cols_log)]
df_numerico <- df_log[sapply(df_log, is.numeric)]

matriz_cor <- cor(df_numerico, use = "pairwise.complete.obs")

corrplot( matriz_cor,
  method = "color",            # Mapa de calor
  type = "upper",              # Solo triángulo superior
  addCoef.col = "black",       # Mostrar coeficientes en color negro
  number.cex = 0.6,            # Tamaño del número
  tl.cex = 0.35,                # Tamaño de las etiquetas
  tl.col = "black",            # Color del texto
  diag = FALSE                 # Oculta la diagonal (1s)
)

df_log <- df_log[!is.na(df_log$"Derrama_Economica_Est_mdp"), ]

derrama_ts <- ts(df_log$"Derrama_Economica_Est_mdp", start = c(2004, 1), end = c(2024, 12), frequency = 12)
plot(derrama_ts, type = "l", col = "darkgreen", lwd = 2,
     xlab = "Año", ylab = "Derrama Económica",
     main = "Serie Temporal de Derrama_Economica_Est_mdp")

decomposition <- decompose(derrama_ts)
plot(decomposition)

descomp_stl <- stl(derrama_ts, s.window = "periodic")
plot(descomp_stl)

Meses de “temporada alta”

  • Abril (mes 4) y noviembre (mes 11) muestran las medianas más elevadas (alrededor de 750–800 mdp) y cuartiles superiores altos, lo que sugiere picos sistemáticos en primavera y justo antes de fin de año.

Meses de “temporada baja”

  • Enero (1) es el más bajo en mediana, con pocos outliers, reflejando el típico bajón tras la temporada navideña.

  • Septiembre (9) y febrero (2) también presentan medianas reducidas , posiblemente por vacaciones cortas o periodos de baja actividad turística.

boxplot(derrama_ts ~ cycle(derrama_ts),
        xlab = "Mes", ylab = "Derrama",
        main = "Estacionalidad mensual", col = "lightblue")

# Autocorrelación y autocorrelación parcial
acf(derrama_ts, main="Autocorrelation - Derrama Económica")

pacf(derrama_ts, main="Partial Autocorrelation -Derrama Económica") #aplicar 1 o 2

adf_ocupacion <- adf.test(df_log$"Derrama_Economica_Est_mdp")
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -0.443   0.517
## [2,]   1 -0.193   0.588
## [3,]   2  0.651   0.831
## [4,]   3  0.713   0.849
## [5,]   4  0.395   0.758
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -1.925   0.357
## [2,]   1 -1.611   0.480
## [3,]   2 -0.583   0.843
## [4,]   3 -0.540   0.858
## [5,]   4 -0.835   0.754
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -3.24  0.0816
## [2,]   1 -2.90  0.1951
## [3,]   2 -1.81  0.6564
## [4,]   3 -1.74  0.6862
## [5,]   4 -2.12  0.5270
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01
print(adf_ocupacion)
## $type1
##      lag        ADF   p.value
## [1,]   0 -0.4425241 0.5165398
## [2,]   1 -0.1927057 0.5884300
## [3,]   2  0.6512135 0.8312844
## [4,]   3  0.7130436 0.8490773
## [5,]   4  0.3954069 0.7576710
## 
## $type2
##      lag        ADF   p.value
## [1,]   0 -1.9251534 0.3568070
## [2,]   1 -1.6109259 0.4800432
## [3,]   2 -0.5828110 0.8428994
## [4,]   3 -0.5403225 0.8578082
## [5,]   4 -0.8349828 0.7544150
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -3.236675 0.08160778
## [2,]   1 -2.904106 0.19511329
## [3,]   2 -1.808621 0.65637655
## [4,]   3 -1.737771 0.68620967
## [5,]   4 -2.115873 0.52700199
# Aucotorrelación serial
lgunj_box_result <- Box.test(derrama_ts, lag = 5, type = "Ljung-Box")
lgunj_box_result
## 
##  Box-Ljung test
## 
## data:  derrama_ts
## X-squared = 983.29, df = 5, p-value < 0.00000000000000022
derrama_diff <- diff(derrama_ts) # Diferencia (d = 1)
adf.test(derrama_diff) #Ya es estacionaria, por ende no se aplica d
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -17.26    0.01
## [2,]   1 -16.72    0.01
## [3,]   2 -11.67    0.01
## [4,]   3  -8.22    0.01
## [5,]   4  -6.53    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -17.26    0.01
## [2,]   1 -16.77    0.01
## [3,]   2 -11.74    0.01
## [4,]   3  -8.28    0.01
## [5,]   4  -6.59    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -17.24    0.01
## [2,]   1 -16.78    0.01
## [3,]   2 -11.77    0.01
## [4,]   3  -8.32    0.01
## [5,]   4  -6.62    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01
mod1 <- arima(derrama_diff, order = c(1, 0, 0))  #  d = 0 porque ya la serie está diferenciada
summary(mod1) #p-value = 2.2e-16 → muy menor a 0.05, los residuos no se comportan como ruido blanco → el modelo no está capturando completamente la estructura de la serie.
## 
## Call:
## arima(x = derrama_diff, order = c(1, 0, 0))
## 
## Coefficients:
##           ar1  intercept
##       -0.1075     7.6542
## s.e.   0.0637     9.6332
## 
## sigma^2 estimated as 28547:  log likelihood = -1643.7,  aic = 3293.4
## 
## Training set error measures:
##                       ME     RMSE      MAE     MPE     MAPE      MASE
## Training set 0.001449583 168.9576 118.4086 210.444 249.2255 0.6696299
##                     ACF1
## Training set -0.03787776
checkresiduals(mod1)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(1,0,0) with non-zero mean
## Q* = 230.01, df = 23, p-value < 0.00000000000000022
## 
## Model df: 1.   Total lags used: 24
#ar1-> coeficiente bajo, lo que sugiere una débil autocorrelación temporal directa.
modelo_auto <- auto.arima(derrama_ts)
summary(modelo_auto)
## Series: derrama_ts 
## ARIMA(2,1,0)(0,0,2)[12] 
## 
## Coefficients:
##           ar1      ar2    sma1    sma2
##       -0.1440  -0.2517  0.2315  0.2043
## s.e.   0.0616   0.0657  0.0683  0.0633
## 
## sigma^2 = 23305:  log likelihood = -1617.03
## AIC=3244.06   AICc=3244.3   BIC=3261.69
## 
## Training set error measures:
##                    ME     RMSE      MAE       MPE    MAPE      MASE
## Training set 7.723387 151.1387 97.70522 -2.637469 13.8319 0.4266687
##                      ACF1
## Training set 0.0003630307
checkresiduals(modelo_auto)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,1,0)(0,0,2)[12]
## Q* = 40.37, df = 20, p-value = 0.004484
## 
## Model df: 4.   Total lags used: 24
#p-value = 0.004484 → menor a 0.05, lo que indica presencia de autocorrelación en los residuos
#No captura tan bien la estacionalidad
library(forecast)

# Lista para guardar resultados
resultados <- list()
modelos_validos <- data.frame()

# Rangos de parámetros (ajustar si queremos  más o menos combinaciones)
for (p in 0:2) {
  for (d in 1:1) {
    for (q in 0:2) {
      for (P in 0:2) {
        for (D in 0:1) {
          for (Q in 0:2) {
            orden <- c(p, d, q)
            orden_estacional <- c(P, D, Q)
            try({
              modelo <- arima(derrama_ts,
                              order = orden,
                              seasonal = list(order = orden_estacional, period = 12),
                              method = "ML")
              aic_val <- AIC(modelo)
              modelos_validos <- rbind(modelos_validos, data.frame(
                p, d, q, P, D, Q,
                AIC = aic_val
              ))
              key <- paste0("(", p, ",", d, ",", q, ")(", P, ",", D, ",", Q, ")[12]")
              resultados[[key]] <- modelo }, silent = TRUE)}}}}}}
# Ordenar por AIC
modelos_validos <- modelos_validos[order(modelos_validos$AIC), ]
head(modelos_validos, 5)
##     p d q P D Q      AIC
## 141 2 1 2 0 1 1 3054.344
## 142 2 1 2 0 1 2 3055.424
## 147 2 1 2 1 1 1 3055.600
## 153 2 1 2 2 1 1 3055.857
## 154 2 1 2 2 1 2 3056.056
mod_sugerido <- arima(derrama_ts, order = c(2,1,2), seasonal = list(order = c(0,1,1), period = 12), transform.pars = FALSE)
summary(mod_sugerido)
## 
## Call:
## arima(x = derrama_ts, order = c(2, 1, 2), seasonal = list(order = c(0, 1, 1), 
##     period = 12), transform.pars = FALSE)
## 
## Coefficients:
##           ar1      ar2      ma1     ma2     sma1
##       -0.0019  -1.0062  -0.0692  0.9063  -1.0000
## s.e.   0.0066   0.0114   0.0350  0.0279   0.0772
## 
## sigma^2 estimated as 17409:  log likelihood = -1521.12,  aic = 3054.24
## 
## Training set error measures:
##                    ME     RMSE      MAE       MPE     MAPE      MASE
## Training set 4.716493 128.4957 73.48511 -2.065164 10.35313 0.6143497
##                     ACF1
## Training set -0.05029193
checkresiduals(mod_sugerido)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,1,2)(0,1,1)[12]
## Q* = 24.764, df = 19, p-value = 0.1685
## 
## Model df: 5.   Total lags used: 24
#Ljung-Box:
#p-value = 0.1685 > 0.05 → no hay autocorrelación significativa en los residuos.
# Buen indicio de que el modelo capta bien la estructura de la serie
#Captura adecuadamente la tendencia y estacionalidad.
variables_log <- c( "MES", "AÑO",
"Cuartos_Disponibles", "Cuartos_Ocupados", "Cuartos_Registrados", "Derrama_Economica_Est_mdp",  "Imp_Hospedaje", "Turistas_Noche_Ext", "Turistas_Noche_Nac", "%Ocupacion_Hoteles",  "Llegada_Tur_Ext", "Llegada_Tur_Nac", "Cuartos_Disponibles_Promedio_log", "Densidad_Ocupación_log",  "Estadia_Promedio_log"          
)

variables_log <- setdiff(variables_log, c("Imp_Hospedaje", "MES", "AÑO"))


for (var in variables_log) {
  cat("\n--------------------------------------------------\n")
  cat("ADF test for:", var, "\n")
  serie <- ts(df_log[[var]], start = c(2004, 1), frequency = 12)
  print(adf.test(serie))
}
## 
## --------------------------------------------------
## ADF test for: Cuartos_Disponibles 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag   ADF p.value
## [1,]   0 0.478   0.781
## [2,]   1 1.624   0.975
## [3,]   2 2.369   0.990
## [4,]   3 2.611   0.990
## [5,]   4 3.603   0.990
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -1.985   0.333
## [2,]   1 -0.757   0.782
## [3,]   2 -0.223   0.928
## [4,]   3 -0.118   0.943
## [5,]   4  0.376   0.980
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -8.62   0.010
## [2,]   1 -4.12   0.010
## [3,]   2 -2.84   0.224
## [4,]   3 -2.55   0.346
## [5,]   4 -1.65   0.724
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag       ADF   p.value
## [1,]   0 0.4777519 0.7813675
## [2,]   1 1.6240310 0.9745765
## [3,]   2 2.3693760 0.9900000
## [4,]   3 2.6112981 0.9900000
## [5,]   4 3.6029489 0.9900000
## 
## $type2
##      lag        ADF   p.value
## [1,]   0 -1.9849443 0.3333578
## [2,]   1 -0.7568787 0.7818209
## [3,]   2 -0.2232732 0.9281088
## [4,]   3 -0.1180908 0.9431315
## [5,]   4  0.3763474 0.9803827
## 
## $type3
##      lag       ADF   p.value
## [1,]   0 -8.619217 0.0100000
## [2,]   1 -4.120557 0.0100000
## [3,]   2 -2.836190 0.2237095
## [4,]   3 -2.546797 0.3455593
## [5,]   4 -1.648221 0.7239166
## 
## 
## --------------------------------------------------
## ADF test for: Cuartos_Ocupados 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag     ADF p.value
## [1,]   0 -0.7528   0.410
## [2,]   1 -0.5705   0.475
## [3,]   2 -0.1239   0.608
## [4,]   3  0.0337   0.654
## [5,]   4  0.0322   0.653
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -3.67  0.0100
## [2,]   1 -3.35  0.0149
## [3,]   2 -2.31  0.2056
## [4,]   3 -2.11  0.2858
## [5,]   4 -1.95  0.3454
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -4.69  0.0100
## [2,]   1 -4.33  0.0100
## [3,]   2 -3.15  0.0958
## [4,]   3 -2.87  0.2078
## [5,]   4 -2.80  0.2371
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag         ADF   p.value
## [1,]   0 -0.75278318 0.4097203
## [2,]   1 -0.57054153 0.4748066
## [3,]   2 -0.12385848 0.6082422
## [4,]   3  0.03369558 0.6535815
## [5,]   4  0.03223410 0.6531609
## 
## $type2
##      lag       ADF    p.value
## [1,]   0 -3.666663 0.01000000
## [2,]   1 -3.349239 0.01487358
## [3,]   2 -2.310812 0.20555655
## [4,]   3 -2.106180 0.28581074
## [5,]   4 -1.954229 0.34540412
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -4.688795 0.01000000
## [2,]   1 -4.334235 0.01000000
## [3,]   2 -3.154158 0.09583476
## [4,]   3 -2.873919 0.20782365
## [5,]   4 -2.804496 0.23705421
## 
## 
## --------------------------------------------------
## ADF test for: Cuartos_Registrados 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag   ADF p.value
## [1,]   0 0.464   0.777
## [2,]   1 1.595   0.973
## [3,]   2 2.392   0.990
## [4,]   3 2.700   0.990
## [5,]   4 3.758   0.990
## Type 2: with drift no trend 
##      lag     ADF p.value
## [1,]   0 -2.0066   0.325
## [2,]   1 -0.7414   0.787
## [3,]   2 -0.1906   0.933
## [4,]   3 -0.0471   0.952
## [5,]   4  0.4528   0.983
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -8.85   0.010
## [2,]   1 -4.22   0.010
## [3,]   2 -2.84   0.223
## [4,]   3 -2.47   0.377
## [5,]   4 -1.56   0.760
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag       ADF   p.value
## [1,]   0 0.4637714 0.7773443
## [2,]   1 1.5954975 0.9725381
## [3,]   2 2.3924775 0.9900000
## [4,]   3 2.7001007 0.9900000
## [5,]   4 3.7579557 0.9900000
## 
## $type2
##      lag         ADF   p.value
## [1,]   0 -2.00657920 0.3248729
## [2,]   1 -0.74142297 0.7872441
## [3,]   2 -0.19064086 0.9327695
## [4,]   3 -0.04711177 0.9518458
## [5,]   4  0.45281423 0.9834014
## 
## $type3
##      lag       ADF   p.value
## [1,]   0 -8.851989 0.0100000
## [2,]   1 -4.224233 0.0100000
## [3,]   2 -2.837425 0.2231894
## [4,]   3 -2.471558 0.3772387
## [5,]   4 -1.563069 0.7597713
## 
## 
## --------------------------------------------------
## ADF test for: Derrama_Economica_Est_mdp 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -0.443   0.517
## [2,]   1 -0.193   0.588
## [3,]   2  0.651   0.831
## [4,]   3  0.713   0.849
## [5,]   4  0.395   0.758
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -1.925   0.357
## [2,]   1 -1.611   0.480
## [3,]   2 -0.583   0.843
## [4,]   3 -0.540   0.858
## [5,]   4 -0.835   0.754
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -3.24  0.0816
## [2,]   1 -2.90  0.1951
## [3,]   2 -1.81  0.6564
## [4,]   3 -1.74  0.6862
## [5,]   4 -2.12  0.5270
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF   p.value
## [1,]   0 -0.4425241 0.5165398
## [2,]   1 -0.1927057 0.5884300
## [3,]   2  0.6512135 0.8312844
## [4,]   3  0.7130436 0.8490773
## [5,]   4  0.3954069 0.7576710
## 
## $type2
##      lag        ADF   p.value
## [1,]   0 -1.9251534 0.3568070
## [2,]   1 -1.6109259 0.4800432
## [3,]   2 -0.5828110 0.8428994
## [4,]   3 -0.5403225 0.8578082
## [5,]   4 -0.8349828 0.7544150
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -3.236675 0.08160778
## [2,]   1 -2.904106 0.19511329
## [3,]   2 -1.808621 0.65637655
## [4,]   3 -1.737771 0.68620967
## [5,]   4 -2.115873 0.52700199
## 
## 
## --------------------------------------------------
## ADF test for: Turistas_Noche_Ext 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -1.200   0.250
## [2,]   1 -0.807   0.390
## [3,]   2 -0.531   0.489
## [4,]   3 -0.403   0.528
## [5,]   4 -0.460   0.512
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -3.54  0.0100
## [2,]   1 -2.84  0.0567
## [3,]   2 -2.30  0.2090
## [4,]   3 -2.19  0.2522
## [5,]   4 -2.18  0.2569
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -4.44  0.0100
## [2,]   1 -3.61  0.0321
## [3,]   2 -3.01  0.1510
## [4,]   3 -2.82  0.2310
## [5,]   4 -2.91  0.1945
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF   p.value
## [1,]   0 -1.2004190 0.2498503
## [2,]   1 -0.8072259 0.3902765
## [3,]   2 -0.5312872 0.4888260
## [4,]   3 -0.4034737 0.5277774
## [5,]   4 -0.4595308 0.5116458
## 
## $type2
##      lag       ADF    p.value
## [1,]   0 -3.537031 0.01000000
## [2,]   1 -2.839744 0.05670265
## [3,]   2 -2.302026 0.20900235
## [4,]   3 -2.191895 0.25219433
## [5,]   4 -2.179884 0.25690504
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -4.440524 0.01000000
## [2,]   1 -3.613612 0.03206767
## [3,]   2 -3.008935 0.15097480
## [4,]   3 -2.818923 0.23097987
## [5,]   4 -2.905634 0.19447010
## 
## 
## --------------------------------------------------
## ADF test for: Turistas_Noche_Nac 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag     ADF p.value
## [1,]   0 -1.0733   0.295
## [2,]   1 -0.7899   0.396
## [3,]   2 -0.2799   0.563
## [4,]   3 -0.0869   0.619
## [5,]   4 -0.1503   0.601
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -4.17   0.010
## [2,]   1 -3.61   0.010
## [3,]   2 -2.41   0.166
## [4,]   3 -2.08   0.298
## [5,]   4 -2.06   0.304
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -5.63  0.0100
## [2,]   1 -4.98  0.0100
## [3,]   2 -3.51  0.0420
## [4,]   3 -3.06  0.1280
## [5,]   4 -3.18  0.0919
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag         ADF   p.value
## [1,]   0 -1.07329620 0.2952514
## [2,]   1 -0.78985659 0.3964798
## [3,]   2 -0.27990229 0.5633375
## [4,]   3 -0.08694443 0.6188649
## [5,]   4 -0.15026795 0.6006423
## 
## $type2
##      lag       ADF   p.value
## [1,]   0 -4.165783 0.0100000
## [2,]   1 -3.612993 0.0100000
## [3,]   2 -2.411467 0.1660811
## [4,]   3 -2.076220 0.2975607
## [5,]   4 -2.060735 0.3036336
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -5.633109 0.01000000
## [2,]   1 -4.984095 0.01000000
## [3,]   2 -3.506724 0.04196759
## [4,]   3 -3.063561 0.12797424
## [5,]   4 -3.177264 0.09185107
## 
## 
## --------------------------------------------------
## ADF test for: %Ocupacion_Hoteles 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -0.945   0.341
## [2,]   1 -0.864   0.370
## [3,]   2 -0.460   0.512
## [4,]   3 -0.429   0.520
## [5,]   4 -0.449   0.515
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -4.70  0.0100
## [2,]   1 -4.52  0.0100
## [3,]   2 -2.90  0.0480
## [4,]   3 -2.91  0.0468
## [5,]   4 -2.67  0.0852
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -4.70   0.010
## [2,]   1 -4.52   0.010
## [3,]   2 -2.91   0.191
## [4,]   3 -2.92   0.188
## [5,]   4 -2.69   0.283
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF   p.value
## [1,]   0 -0.9448806 0.3411141
## [2,]   1 -0.8643401 0.3698785
## [3,]   2 -0.4599054 0.5115380
## [4,]   3 -0.4292851 0.5203496
## [5,]   4 -0.4486139 0.5147874
## 
## $type2
##      lag       ADF    p.value
## [1,]   0 -4.695671 0.01000000
## [2,]   1 -4.516520 0.01000000
## [3,]   2 -2.900676 0.04800809
## [4,]   3 -2.913222 0.04680176
## [5,]   4 -2.668905 0.08517578
## 
## $type3
##      lag       ADF   p.value
## [1,]   0 -4.702490 0.0100000
## [2,]   1 -4.524876 0.0100000
## [3,]   2 -2.914022 0.1909381
## [4,]   3 -2.921932 0.1876074
## [5,]   4 -2.694585 0.2833326
## 
## 
## --------------------------------------------------
## ADF test for: Llegada_Tur_Ext 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -1.265   0.227
## [2,]   1 -0.870   0.368
## [3,]   2 -0.544   0.484
## [4,]   3 -0.433   0.519
## [5,]   4 -0.399   0.529
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -3.91  0.0100
## [2,]   1 -3.13  0.0255
## [3,]   2 -2.48  0.1384
## [4,]   3 -2.39  0.1739
## [5,]   4 -2.26  0.2262
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -4.87  0.0100
## [2,]   1 -3.96  0.0111
## [3,]   2 -3.21  0.0863
## [4,]   3 -3.05  0.1324
## [5,]   4 -2.96  0.1696
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF   p.value
## [1,]   0 -1.2654494 0.2266252
## [2,]   1 -0.8698140 0.3679236
## [3,]   2 -0.5443118 0.4841744
## [4,]   3 -0.4327889 0.5193413
## [5,]   4 -0.3992141 0.5290031
## 
## $type2
##      lag       ADF   p.value
## [1,]   0 -3.914994 0.0100000
## [2,]   1 -3.134710 0.0255048
## [3,]   2 -2.482035 0.1384049
## [4,]   3 -2.391520 0.1739038
## [5,]   4 -2.258125 0.2262197
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -4.868170 0.01000000
## [2,]   1 -3.958192 0.01112578
## [3,]   2 -3.209659 0.08626574
## [4,]   3 -3.052968 0.13243458
## [5,]   4 -2.964710 0.16959598
## 
## 
## --------------------------------------------------
## ADF test for: Llegada_Tur_Nac 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag     ADF p.value
## [1,]   0 -0.7122   0.424
## [2,]   1 -0.4986   0.500
## [3,]   2 -0.0773   0.622
## [4,]   3  0.1342   0.683
## [5,]   4  0.1314   0.682
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -3.62  0.0100
## [2,]   1 -3.17  0.0235
## [3,]   2 -2.20  0.2484
## [4,]   3 -1.83  0.3926
## [5,]   4 -1.68  0.4515
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -4.83  0.0100
## [2,]   1 -4.38  0.0100
## [3,]   2 -3.30  0.0706
## [4,]   3 -2.86  0.2124
## [5,]   4 -2.82  0.2298
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag         ADF   p.value
## [1,]   0 -0.71219518 0.4242160
## [2,]   1 -0.49864304 0.5003905
## [3,]   2 -0.07732892 0.6216320
## [4,]   3  0.13422395 0.6825105
## [5,]   4  0.13136588 0.6816880
## 
## $type2
##      lag       ADF    p.value
## [1,]   0 -3.616582 0.01000000
## [2,]   1 -3.171168 0.02348992
## [3,]   2 -2.201458 0.24844364
## [4,]   3 -1.833882 0.39260268
## [5,]   4 -1.683754 0.45148086
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -4.834997 0.01000000
## [2,]   1 -4.377747 0.01000000
## [3,]   2 -3.300577 0.07059009
## [4,]   3 -2.863075 0.21238932
## [5,]   4 -2.821818 0.22976096
## 
## 
## --------------------------------------------------
## ADF test for: Cuartos_Disponibles_Promedio_log 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag  ADF p.value
## [1,]   0 1.89   0.985
## [2,]   1 2.46   0.990
## [3,]   2 2.81   0.990
## [4,]   3 3.19   0.990
## [5,]   4 3.33   0.990
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -1.047   0.680
## [2,]   1 -0.848   0.750
## [3,]   2 -0.652   0.819
## [4,]   3 -0.637   0.824
## [5,]   4 -0.547   0.856
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -4.71  0.0100
## [2,]   1 -3.71  0.0240
## [3,]   2 -3.17  0.0939
## [4,]   3 -2.84  0.2221
## [5,]   4 -2.64  0.3054
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag      ADF   p.value
## [1,]   0 1.892129 0.9850834
## [2,]   1 2.464883 0.9900000
## [3,]   2 2.814414 0.9900000
## [4,]   3 3.189882 0.9900000
## [5,]   4 3.328724 0.9900000
## 
## $type2
##      lag        ADF   p.value
## [1,]   0 -1.0470810 0.6799919
## [2,]   1 -0.8484632 0.7496848
## [3,]   2 -0.6519157 0.8186513
## [4,]   3 -0.6367106 0.8239866
## [5,]   4 -0.5466174 0.8555993
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -4.708043 0.01000000
## [2,]   1 -3.709718 0.02397613
## [3,]   2 -3.165610 0.09386041
## [4,]   3 -2.839904 0.22214574
## [5,]   4 -2.642196 0.30539107
## 
## 
## --------------------------------------------------
## ADF test for: Densidad_Ocupación_log 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -0.765   0.405
## [2,]   1 -0.458   0.512
## [3,]   2 -0.330   0.549
## [4,]   3 -0.274   0.565
## [5,]   4 -0.268   0.567
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -11.18    0.01
## [2,]   1  -7.72    0.01
## [3,]   2  -5.60    0.01
## [4,]   3  -4.19    0.01
## [5,]   4  -3.74    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -12.55    0.01
## [2,]   1  -8.95    0.01
## [3,]   2  -6.68    0.01
## [4,]   3  -5.12    0.01
## [5,]   4  -4.68    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF   p.value
## [1,]   0 -0.7651350 0.4053089
## [2,]   1 -0.4576753 0.5121798
## [3,]   2 -0.3304019 0.5488052
## [4,]   3 -0.2737244 0.5651153
## [5,]   4 -0.2684313 0.5666385
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -11.178275    0.01
## [2,]   1  -7.715082    0.01
## [3,]   2  -5.595947    0.01
## [4,]   3  -4.194517    0.01
## [5,]   4  -3.742244    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -12.547995    0.01
## [2,]   1  -8.952229    0.01
## [3,]   2  -6.675710    0.01
## [4,]   3  -5.116983    0.01
## [5,]   4  -4.679276    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Estadia_Promedio_log 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -0.521   0.492
## [2,]   1 -0.372   0.537
## [3,]   2 -0.376   0.536
## [4,]   3 -0.357   0.541
## [5,]   4 -0.327   0.550
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -6.47    0.01
## [2,]   1 -5.98    0.01
## [3,]   2 -5.05    0.01
## [4,]   3 -4.30    0.01
## [5,]   4 -3.98    0.01
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -6.72    0.01
## [2,]   1 -6.18    0.01
## [3,]   2 -5.24    0.01
## [4,]   3 -4.44    0.01
## [5,]   4 -4.09    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF   p.value
## [1,]   0 -0.5214091 0.4923539
## [2,]   1 -0.3722281 0.5367689
## [3,]   2 -0.3760993 0.5356549
## [4,]   3 -0.3574346 0.5410260
## [5,]   4 -0.3274543 0.5496534
## 
## $type2
##      lag       ADF p.value
## [1,]   0 -6.469231    0.01
## [2,]   1 -5.983262    0.01
## [3,]   2 -5.052522    0.01
## [4,]   3 -4.296002    0.01
## [5,]   4 -3.983149    0.01
## 
## $type3
##      lag       ADF p.value
## [1,]   0 -6.721413    0.01
## [2,]   1 -6.183590    0.01
## [3,]   2 -5.244141    0.01
## [4,]   3 -4.439856    0.01
## [5,]   4 -4.094485    0.01
library(tseries)

series_diferenciadas <- list()

for (var in variables_log) {
  cat("Procesando variable:", var, "\n")
  
  serie <- ts(df_log[[var]], start = c(2004, 1), frequency = 12)
  
  adf_result <- tryCatch({
    test <- tseries::adf.test(serie)  # asegúrate que es el de tseries
    test
  }, error = function(e) {
    cat("Error con ADF de", var, ":", e$message, "\n")
    return(NULL)
  })
  
  if (!is.null(adf_result) && !is.na(adf_result$p.value)) {
    if (adf_result$p.value > 0.05) {
      cat("→ No estacionaria. Se diferencia:", var, "\n")
      serie_diff <- diff(serie)
      series_diferenciadas[[paste0(var, "_diff")]] <- serie_diff
    } else {
      cat("→ Estacionaria. Se conserva:", var, "\n")
      # Recortamos la serie a partir del segundo mes para tener misma longitud que diff()
      series_diferenciadas[[var]] <- window(serie, start = c(2004, 2))
    }
  } else {
    cat("→ No se pudo evaluar ADF para", var, "\n")
  }
}
## Procesando variable: Cuartos_Disponibles 
## → No estacionaria. Se diferencia: Cuartos_Disponibles 
## Procesando variable: Cuartos_Ocupados 
## → No estacionaria. Se diferencia: Cuartos_Ocupados 
## Procesando variable: Cuartos_Registrados 
## → No estacionaria. Se diferencia: Cuartos_Registrados 
## Procesando variable: Derrama_Economica_Est_mdp 
## → No estacionaria. Se diferencia: Derrama_Economica_Est_mdp 
## Procesando variable: Turistas_Noche_Ext 
## → No estacionaria. Se diferencia: Turistas_Noche_Ext 
## Procesando variable: Turistas_Noche_Nac 
## → No estacionaria. Se diferencia: Turistas_Noche_Nac 
## Procesando variable: %Ocupacion_Hoteles 
## → No estacionaria. Se diferencia: %Ocupacion_Hoteles 
## Procesando variable: Llegada_Tur_Ext 
## → No estacionaria. Se diferencia: Llegada_Tur_Ext 
## Procesando variable: Llegada_Tur_Nac 
## → No estacionaria. Se diferencia: Llegada_Tur_Nac 
## Procesando variable: Cuartos_Disponibles_Promedio_log 
## → No estacionaria. Se diferencia: Cuartos_Disponibles_Promedio_log 
## Procesando variable: Densidad_Ocupación_log
## → Estacionaria. Se conserva: Densidad_Ocupación_log 
## Procesando variable: Estadia_Promedio_log 
## → No estacionaria. Se diferencia: Estadia_Promedio_log
# Aplicar la prueba ADF a todas las series diferenciadas
for (nombre in names(series_diferenciadas)) {
  cat("\n--------------------------------------------------\n")
  cat("ADF test for:", nombre, "\n")
  print(adf.test(series_diferenciadas[[nombre]]))
}
## 
## --------------------------------------------------
## ADF test for: Cuartos_Disponibles_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -32.99    0.01
## [2,]   1 -19.68    0.01
## [3,]   2 -13.20    0.01
## [4,]   3 -13.31    0.01
## [5,]   4  -8.17    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -33.18    0.01
## [2,]   1 -20.03    0.01
## [3,]   2 -13.63    0.01
## [4,]   3 -14.10    0.01
## [5,]   4  -8.81    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -33.12    0.01
## [2,]   1 -20.00    0.01
## [3,]   2 -13.62    0.01
## [4,]   3 -14.11    0.01
## [5,]   4  -8.82    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -32.988656    0.01
## [2,]   1 -19.680604    0.01
## [3,]   2 -13.197374    0.01
## [4,]   3 -13.311142    0.01
## [5,]   4  -8.168268    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -33.179502    0.01
## [2,]   1 -20.027537    0.01
## [3,]   2 -13.626354    0.01
## [4,]   3 -14.098018    0.01
## [5,]   4  -8.807238    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -33.118897    0.01
## [2,]   1 -20.002478    0.01
## [3,]   2 -13.615723    0.01
## [4,]   3 -14.110187    0.01
## [5,]   4  -8.816725    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Cuartos_Ocupados_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -17.46    0.01
## [2,]   1 -16.02    0.01
## [3,]   2 -12.37    0.01
## [4,]   3  -9.99    0.01
## [5,]   4  -7.93    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -17.44    0.01
## [2,]   1 -16.00    0.01
## [3,]   2 -12.37    0.01
## [4,]   3  -9.99    0.01
## [5,]   4  -7.95    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -17.40    0.01
## [2,]   1 -15.97    0.01
## [3,]   2 -12.35    0.01
## [4,]   3  -9.98    0.01
## [5,]   4  -7.93    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -17.463467    0.01
## [2,]   1 -16.017182    0.01
## [3,]   2 -12.369033    0.01
## [4,]   3  -9.990271    0.01
## [5,]   4  -7.933321    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -17.438958    0.01
## [2,]   1 -16.003600    0.01
## [3,]   2 -12.371725    0.01
## [4,]   3  -9.993988    0.01
## [5,]   4  -7.946753    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -17.401749    0.01
## [2,]   1 -15.972958    0.01
## [3,]   2 -12.347092    0.01
## [4,]   3  -9.980622    0.01
## [5,]   4  -7.931895    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Cuartos_Registrados_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag   ADF p.value
## [1,]   0 -33.1    0.01
## [2,]   1 -20.1    0.01
## [3,]   2 -13.6    0.01
## [4,]   3 -13.7    0.01
## [5,]   4  -8.1    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -33.24    0.01
## [2,]   1 -20.49    0.01
## [3,]   2 -14.07    0.01
## [4,]   3 -14.52    0.01
## [5,]   4  -8.77    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -33.18    0.01
## [2,]   1 -20.46    0.01
## [3,]   2 -14.06    0.01
## [4,]   3 -14.54    0.01
## [5,]   4  -8.78    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag       ADF p.value
## [1,]   0 -33.05799    0.01
## [2,]   1 -20.13233    0.01
## [3,]   2 -13.62034    0.01
## [4,]   3 -13.67426    0.01
## [5,]   4  -8.09889    0.01
## 
## $type2
##      lag       ADF p.value
## [1,]   0 -33.23903    0.01
## [2,]   1 -20.48546    0.01
## [3,]   2 -14.07223    0.01
## [4,]   3 -14.52181    0.01
## [5,]   4  -8.76908    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -33.179805    0.01
## [2,]   1 -20.461284    0.01
## [3,]   2 -14.063915    0.01
## [4,]   3 -14.538646    0.01
## [5,]   4  -8.779469    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Derrama_Economica_Est_mdp_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -17.26    0.01
## [2,]   1 -16.72    0.01
## [3,]   2 -11.67    0.01
## [4,]   3  -8.22    0.01
## [5,]   4  -6.53    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -17.26    0.01
## [2,]   1 -16.77    0.01
## [3,]   2 -11.74    0.01
## [4,]   3  -8.28    0.01
## [5,]   4  -6.59    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -17.24    0.01
## [2,]   1 -16.78    0.01
## [3,]   2 -11.77    0.01
## [4,]   3  -8.32    0.01
## [5,]   4  -6.62    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -17.257861    0.01
## [2,]   1 -16.719685    0.01
## [3,]   2 -11.668943    0.01
## [4,]   3  -8.222182    0.01
## [5,]   4  -6.532807    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -17.260904    0.01
## [2,]   1 -16.765919    0.01
## [3,]   2 -11.736512    0.01
## [4,]   3  -8.278389    0.01
## [5,]   4  -6.592347    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -17.242338    0.01
## [2,]   1 -16.784749    0.01
## [3,]   2 -11.767242    0.01
## [4,]   3  -8.318704    0.01
## [5,]   4  -6.621985    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Turistas_Noche_Ext_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -19.69    0.01
## [2,]   1 -14.95    0.01
## [3,]   2 -11.61    0.01
## [4,]   3  -9.17    0.01
## [5,]   4  -7.28    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -19.66    0.01
## [2,]   1 -14.93    0.01
## [3,]   2 -11.60    0.01
## [4,]   3  -9.16    0.01
## [5,]   4  -7.28    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -19.62    0.01
## [2,]   1 -14.90    0.01
## [3,]   2 -11.58    0.01
## [4,]   3  -9.14    0.01
## [5,]   4  -7.27    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -19.694117    0.01
## [2,]   1 -14.953003    0.01
## [3,]   2 -11.610555    0.01
## [4,]   3  -9.167692    0.01
## [5,]   4  -7.282481    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -19.662656    0.01
## [2,]   1 -14.933408    0.01
## [3,]   2 -11.603392    0.01
## [4,]   3  -9.161765    0.01
## [5,]   4  -7.280217    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -19.622120    0.01
## [2,]   1 -14.904015    0.01
## [3,]   2 -11.578836    0.01
## [4,]   3  -9.144982    0.01
## [5,]   4  -7.266921    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Turistas_Noche_Nac_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -18.53    0.01
## [2,]   1 -17.29    0.01
## [3,]   2 -13.42    0.01
## [4,]   3 -10.02    0.01
## [5,]   4  -7.52    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -18.50    0.01
## [2,]   1 -17.27    0.01
## [3,]   2 -13.42    0.01
## [4,]   3 -10.02    0.01
## [5,]   4  -7.52    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -18.47    0.01
## [2,]   1 -17.24    0.01
## [3,]   2 -13.39    0.01
## [4,]   3 -10.00    0.01
## [5,]   4  -7.51    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -18.532660    0.01
## [2,]   1 -17.292102    0.01
## [3,]   2 -13.424566    0.01
## [4,]   3 -10.020339    0.01
## [5,]   4  -7.515764    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -18.503860    0.01
## [2,]   1 -17.272121    0.01
## [3,]   2 -13.418932    0.01
## [4,]   3 -10.016647    0.01
## [5,]   4  -7.520032    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -18.465367    0.01
## [2,]   1 -17.238516    0.01
## [3,]   2 -13.393092    0.01
## [4,]   3 -10.001469    0.01
## [5,]   4  -7.505395    0.01
## 
## 
## --------------------------------------------------
## ADF test for: %Ocupacion_Hoteles_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -16.85    0.01
## [2,]   1 -17.94    0.01
## [3,]   2 -11.91    0.01
## [4,]   3 -10.31    0.01
## [5,]   4  -8.46    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -16.82    0.01
## [2,]   1 -17.90    0.01
## [3,]   2 -11.89    0.01
## [4,]   3 -10.29    0.01
## [5,]   4  -8.45    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -16.78    0.01
## [2,]   1 -17.87    0.01
## [3,]   2 -11.87    0.01
## [4,]   3 -10.27    0.01
## [5,]   4  -8.43    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -16.853593    0.01
## [2,]   1 -17.939312    0.01
## [3,]   2 -11.914473    0.01
## [4,]   3 -10.310056    0.01
## [5,]   4  -8.464536    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -16.819696    0.01
## [2,]   1 -17.903491    0.01
## [3,]   2 -11.891809    0.01
## [4,]   3 -10.287868    0.01
## [5,]   4  -8.449082    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -16.784785    0.01
## [2,]   1 -17.866600    0.01
## [3,]   2 -11.866363    0.01
## [4,]   3 -10.268783    0.01
## [5,]   4  -8.430208    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Llegada_Tur_Ext_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -19.90    0.01
## [2,]   1 -15.42    0.01
## [3,]   2 -11.69    0.01
## [4,]   3  -9.72    0.01
## [5,]   4  -7.54    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -19.87    0.01
## [2,]   1 -15.40    0.01
## [3,]   2 -11.68    0.01
## [4,]   3  -9.72    0.01
## [5,]   4  -7.54    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -19.83    0.01
## [2,]   1 -15.37    0.01
## [3,]   2 -11.66    0.01
## [4,]   3  -9.70    0.01
## [5,]   4  -7.53    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -19.903681    0.01
## [2,]   1 -15.420126    0.01
## [3,]   2 -11.687180    0.01
## [4,]   3  -9.723831    0.01
## [5,]   4  -7.542653    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -19.872617    0.01
## [2,]   1 -15.401012    0.01
## [3,]   2 -11.680650    0.01
## [4,]   3  -9.720311    0.01
## [5,]   4  -7.542871    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -19.832651    0.01
## [2,]   1 -15.370568    0.01
## [3,]   2 -11.656651    0.01
## [4,]   3  -9.701470    0.01
## [5,]   4  -7.528284    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Llegada_Tur_Nac_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -17.89    0.01
## [2,]   1 -15.54    0.01
## [3,]   2 -12.64    0.01
## [4,]   3 -10.16    0.01
## [5,]   4  -7.71    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -17.87    0.01
## [2,]   1 -15.53    0.01
## [3,]   2 -12.65    0.01
## [4,]   3 -10.17    0.01
## [5,]   4  -7.73    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -17.84    0.01
## [2,]   1 -15.52    0.01
## [3,]   2 -12.64    0.01
## [4,]   3 -10.18    0.01
## [5,]   4  -7.74    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -17.893563    0.01
## [2,]   1 -15.540678    0.01
## [3,]   2 -12.645000    0.01
## [4,]   3 -10.156475    0.01
## [5,]   4  -7.713903    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -17.873486    0.01
## [2,]   1 -15.533133    0.01
## [3,]   2 -12.652632    0.01
## [4,]   3 -10.165280    0.01
## [5,]   4  -7.732122    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -17.842643    0.01
## [2,]   1 -15.516682    0.01
## [3,]   2 -12.644614    0.01
## [4,]   3 -10.176799    0.01
## [5,]   4  -7.739574    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Cuartos_Disponibles_Promedio_log_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -20.25    0.01
## [2,]   1 -14.21    0.01
## [3,]   2 -11.57    0.01
## [4,]   3  -9.57    0.01
## [5,]   4  -8.18    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -20.61    0.01
## [2,]   1 -14.69    0.01
## [3,]   2 -12.21    0.01
## [4,]   3 -10.32    0.01
## [5,]   4  -9.03    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -20.57    0.01
## [2,]   1 -14.66    0.01
## [3,]   2 -12.19    0.01
## [4,]   3 -10.30    0.01
## [5,]   4  -9.02    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF p.value
## [1,]   0 -20.252740    0.01
## [2,]   1 -14.214086    0.01
## [3,]   2 -11.569451    0.01
## [4,]   3  -9.570863    0.01
## [5,]   4  -8.182493    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -20.611214    0.01
## [2,]   1 -14.688378    0.01
## [3,]   2 -12.213380    0.01
## [4,]   3 -10.323869    0.01
## [5,]   4  -9.034693    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -20.570121    0.01
## [2,]   1 -14.658454    0.01
## [3,]   2 -12.188496    0.01
## [4,]   3 -10.302504    0.01
## [5,]   4  -9.015723    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Densidad_Ocupación_log 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -0.761   0.407
## [2,]   1 -0.482   0.505
## [3,]   2 -0.356   0.541
## [4,]   3 -0.297   0.559
## [5,]   4 -0.253   0.571
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -11.16    0.01
## [2,]   1  -7.69    0.01
## [3,]   2  -5.58    0.01
## [4,]   3  -4.19    0.01
## [5,]   4  -3.73    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -12.53    0.01
## [2,]   1  -8.97    0.01
## [3,]   2  -6.71    0.01
## [4,]   3  -5.16    0.01
## [5,]   4  -4.67    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag        ADF   p.value
## [1,]   0 -0.7609459 0.4068050
## [2,]   1 -0.4823146 0.5050893
## [3,]   2 -0.3558360 0.5414860
## [4,]   3 -0.2965246 0.5585541
## [5,]   4 -0.2529055 0.5711063
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -11.158231    0.01
## [2,]   1  -7.694956    0.01
## [3,]   2  -5.583992    0.01
## [4,]   3  -4.188343    0.01
## [5,]   4  -3.732679    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -12.529166    0.01
## [2,]   1  -8.969998    0.01
## [3,]   2  -6.709890    0.01
## [4,]   3  -5.157404    0.01
## [5,]   4  -4.666754    0.01
## 
## 
## --------------------------------------------------
## ADF test for: Estadia_Promedio_log_diff 
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag   ADF p.value
## [1,]   0 -18.2    0.01
## [2,]   1 -14.6    0.01
## [3,]   2 -12.8    0.01
## [4,]   3 -11.0    0.01
## [5,]   4 -10.7    0.01
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -18.2    0.01
## [2,]   1 -14.5    0.01
## [3,]   2 -12.8    0.01
## [4,]   3 -10.9    0.01
## [5,]   4 -10.7    0.01
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -18.1    0.01
## [2,]   1 -14.5    0.01
## [3,]   2 -12.8    0.01
## [4,]   3 -10.9    0.01
## [5,]   4 -10.7    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01 
## $type1
##      lag       ADF p.value
## [1,]   0 -18.18816    0.01
## [2,]   1 -14.57232    0.01
## [3,]   2 -12.83734    0.01
## [4,]   3 -10.96515    0.01
## [5,]   4 -10.68420    0.01
## 
## $type2
##      lag       ADF p.value
## [1,]   0 -18.15213    0.01
## [2,]   1 -14.54409    0.01
## [3,]   2 -12.81294    0.01
## [4,]   3 -10.94434    0.01
## [5,]   4 -10.66352    0.01
## 
## $type3
##      lag       ADF p.value
## [1,]   0 -18.12322    0.01
## [2,]   1 -14.52442    0.01
## [3,]   2 -12.80107    0.01
## [4,]   3 -10.94218    0.01
## [5,]   4 -10.67122    0.01
df_log[2, ]
##    AÑO MES Cuartos_Disponibles Cuartos_Ocupados Cuartos_Registrados
## 2 2004   2              267473           129500              274195
##   Derrama_Economica_Est_mdp Imp_Hospedaje Turistas_Noche_Ext Turistas_Noche_Nac
## 2                  345.1886            NA              33262             159212
##   %Ocupacion_Hoteles Llegada_Tur_Ext Llegada_Tur_Nac
## 2           0.484161           15841           87704
##   Cuartos_Disponibles_Promedio_log Densidad_Ocupación_log Estadia_Promedio_log
## 2                         9.120853              0.9107899             1.050417
df_diff <- as.data.frame(series_diferenciadas)
df_diff[1, ]
##   Cuartos_Disponibles_diff Cuartos_Ocupados_diff Cuartos_Registrados_diff
## 1                   -18418                 -5196                   -18910
##   Derrama_Economica_Est_mdp_diff Turistas_Noche_Ext_diff
## 1                       28.67197                   -2480
##   Turistas_Noche_Nac_diff X.Ocupacion_Hoteles_diff Llegada_Tur_Ext_diff
## 1                   -7306               0.01301642                 1116
##   Llegada_Tur_Nac_diff Cuartos_Disponibles_Promedio_log_diff
## 1                 7253                          0.0001093673
##   Densidad_Ocupación_log Estadia_Promedio_log_diff
## 1              0.9107899               -0.08905393
correlaciones <- cor(df_final, use = "complete.obs")
round(correlaciones, 2)  # para ver mejor los valores
##                                         MES   AÑO Densidad_Ocupación_log
## MES                                    1.00  0.00                   0.09
## AÑO                                    0.00  1.00                   0.47
## Densidad_Ocupación_log                 0.09  0.47                   1.00
## Cuartos_Disponibles_diff               0.15  0.00                   0.07
## Cuartos_Ocupados_diff                 -0.09  0.01                   0.00
## Cuartos_Registrados_diff               0.17  0.00                   0.07
## Derrama_Economica_Est_mdp_diff        -0.10  0.04                   0.18
## Turistas_Noche_Ext_diff                0.09 -0.01                   0.11
## Turistas_Noche_Nac_diff               -0.03 -0.01                   0.12
## X.Ocupacion_Hoteles_diff              -0.15  0.01                  -0.03
## Llegada_Tur_Ext_diff                   0.13 -0.01                   0.07
## Llegada_Tur_Nac_diff                   0.10  0.01                   0.10
## Cuartos_Disponibles_Promedio_log_diff  0.12 -0.01                  -0.09
## Estadia_Promedio_log_diff             -0.29 -0.02                   0.04
##                                       Cuartos_Disponibles_diff
## MES                                                       0.15
## AÑO                                                       0.00
## Densidad_Ocupación_log                                    0.07
## Cuartos_Disponibles_diff                                  1.00
## Cuartos_Ocupados_diff                                     0.27
## Cuartos_Registrados_diff                                  0.99
## Derrama_Economica_Est_mdp_diff                            0.24
## Turistas_Noche_Ext_diff                                   0.14
## Turistas_Noche_Nac_diff                                   0.30
## X.Ocupacion_Hoteles_diff                                 -0.06
## Llegada_Tur_Ext_diff                                      0.23
## Llegada_Tur_Nac_diff                                      0.25
## Cuartos_Disponibles_Promedio_log_diff                     0.33
## Estadia_Promedio_log_diff                                 0.03
##                                       Cuartos_Ocupados_diff
## MES                                                   -0.09
## AÑO                                                    0.01
## Densidad_Ocupación_log                                 0.00
## Cuartos_Disponibles_diff                               0.27
## Cuartos_Ocupados_diff                                  1.00
## Cuartos_Registrados_diff                               0.23
## Derrama_Economica_Est_mdp_diff                         0.76
## Turistas_Noche_Ext_diff                                0.55
## Turistas_Noche_Nac_diff                                0.90
## X.Ocupacion_Hoteles_diff                               0.94
## Llegada_Tur_Ext_diff                                   0.54
## Llegada_Tur_Nac_diff                                   0.86
## Cuartos_Disponibles_Promedio_log_diff                  0.27
## Estadia_Promedio_log_diff                             -0.06
##                                       Cuartos_Registrados_diff
## MES                                                       0.17
## AÑO                                                       0.00
## Densidad_Ocupación_log                                    0.07
## Cuartos_Disponibles_diff                                  0.99
## Cuartos_Ocupados_diff                                     0.23
## Cuartos_Registrados_diff                                  1.00
## Derrama_Economica_Est_mdp_diff                            0.22
## Turistas_Noche_Ext_diff                                   0.11
## Turistas_Noche_Nac_diff                                   0.28
## X.Ocupacion_Hoteles_diff                                 -0.10
## Llegada_Tur_Ext_diff                                      0.20
## Llegada_Tur_Nac_diff                                      0.23
## Cuartos_Disponibles_Promedio_log_diff                     0.27
## Estadia_Promedio_log_diff                                 0.02
##                                       Derrama_Economica_Est_mdp_diff
## MES                                                            -0.10
## AÑO                                                             0.04
## Densidad_Ocupación_log                                          0.18
## Cuartos_Disponibles_diff                                        0.24
## Cuartos_Ocupados_diff                                           0.76
## Cuartos_Registrados_diff                                        0.22
## Derrama_Economica_Est_mdp_diff                                  1.00
## Turistas_Noche_Ext_diff                                         0.45
## Turistas_Noche_Nac_diff                                         0.85
## X.Ocupacion_Hoteles_diff                                        0.69
## Llegada_Tur_Ext_diff                                            0.37
## Llegada_Tur_Nac_diff                                            0.74
## Cuartos_Disponibles_Promedio_log_diff                           0.18
## Estadia_Promedio_log_diff                                       0.15
##                                       Turistas_Noche_Ext_diff
## MES                                                      0.09
## AÑO                                                     -0.01
## Densidad_Ocupación_log                                   0.11
## Cuartos_Disponibles_diff                                 0.14
## Cuartos_Ocupados_diff                                    0.55
## Cuartos_Registrados_diff                                 0.11
## Derrama_Economica_Est_mdp_diff                           0.45
## Turistas_Noche_Ext_diff                                  1.00
## Turistas_Noche_Nac_diff                                  0.49
## X.Ocupacion_Hoteles_diff                                 0.52
## Llegada_Tur_Ext_diff                                     0.89
## Llegada_Tur_Nac_diff                                     0.44
## Cuartos_Disponibles_Promedio_log_diff                    0.13
## Estadia_Promedio_log_diff                               -0.07
##                                       Turistas_Noche_Nac_diff
## MES                                                     -0.03
## AÑO                                                     -0.01
## Densidad_Ocupación_log                                   0.12
## Cuartos_Disponibles_diff                                 0.30
## Cuartos_Ocupados_diff                                    0.90
## Cuartos_Registrados_diff                                 0.28
## Derrama_Economica_Est_mdp_diff                           0.85
## Turistas_Noche_Ext_diff                                  0.49
## Turistas_Noche_Nac_diff                                  1.00
## X.Ocupacion_Hoteles_diff                                 0.81
## Llegada_Tur_Ext_diff                                     0.43
## Llegada_Tur_Nac_diff                                     0.91
## Cuartos_Disponibles_Promedio_log_diff                    0.23
## Estadia_Promedio_log_diff                                0.11
##                                       X.Ocupacion_Hoteles_diff
## MES                                                      -0.15
## AÑO                                                       0.01
## Densidad_Ocupación_log                                   -0.03
## Cuartos_Disponibles_diff                                 -0.06
## Cuartos_Ocupados_diff                                     0.94
## Cuartos_Registrados_diff                                 -0.10
## Derrama_Economica_Est_mdp_diff                            0.69
## Turistas_Noche_Ext_diff                                   0.52
## Turistas_Noche_Nac_diff                                   0.81
## X.Ocupacion_Hoteles_diff                                  1.00
## Llegada_Tur_Ext_diff                                      0.47
## Llegada_Tur_Nac_diff                                      0.79
## Cuartos_Disponibles_Promedio_log_diff                     0.17
## Estadia_Promedio_log_diff                                -0.07
##                                       Llegada_Tur_Ext_diff Llegada_Tur_Nac_diff
## MES                                                   0.13                 0.10
## AÑO                                                  -0.01                 0.01
## Densidad_Ocupación_log                                0.07                 0.10
## Cuartos_Disponibles_diff                              0.23                 0.25
## Cuartos_Ocupados_diff                                 0.54                 0.86
## Cuartos_Registrados_diff                              0.20                 0.23
## Derrama_Economica_Est_mdp_diff                        0.37                 0.74
## Turistas_Noche_Ext_diff                               0.89                 0.44
## Turistas_Noche_Nac_diff                               0.43                 0.91
## X.Ocupacion_Hoteles_diff                              0.47                 0.79
## Llegada_Tur_Ext_diff                                  1.00                 0.45
## Llegada_Tur_Nac_diff                                  0.45                 1.00
## Cuartos_Disponibles_Promedio_log_diff                 0.13                 0.20
## Estadia_Promedio_log_diff                            -0.29                -0.21
##                                       Cuartos_Disponibles_Promedio_log_diff
## MES                                                                    0.12
## AÑO                                                                   -0.01
## Densidad_Ocupación_log                                                -0.09
## Cuartos_Disponibles_diff                                               0.33
## Cuartos_Ocupados_diff                                                  0.27
## Cuartos_Registrados_diff                                               0.27
## Derrama_Economica_Est_mdp_diff                                         0.18
## Turistas_Noche_Ext_diff                                                0.13
## Turistas_Noche_Nac_diff                                                0.23
## X.Ocupacion_Hoteles_diff                                               0.17
## Llegada_Tur_Ext_diff                                                   0.13
## Llegada_Tur_Nac_diff                                                   0.20
## Cuartos_Disponibles_Promedio_log_diff                                  1.00
## Estadia_Promedio_log_diff                                              0.07
##                                       Estadia_Promedio_log_diff
## MES                                                       -0.29
## AÑO                                                       -0.02
## Densidad_Ocupación_log                                     0.04
## Cuartos_Disponibles_diff                                   0.03
## Cuartos_Ocupados_diff                                     -0.06
## Cuartos_Registrados_diff                                   0.02
## Derrama_Economica_Est_mdp_diff                             0.15
## Turistas_Noche_Ext_diff                                   -0.07
## Turistas_Noche_Nac_diff                                    0.11
## X.Ocupacion_Hoteles_diff                                  -0.07
## Llegada_Tur_Ext_diff                                      -0.29
## Llegada_Tur_Nac_diff                                      -0.21
## Cuartos_Disponibles_Promedio_log_diff                      0.07
## Estadia_Promedio_log_diff                                  1.00
corrplot(correlaciones, method = "color", type = "upper", tl.cex = 0.7)

library(caret)

# Detectar columnas con alta correlación
cor_matriz <- cor(df_final, use = "complete.obs")
columnas_a_remover <- findCorrelation(cor_matriz, cutoff = 0.9)

# Ver nombres de columnas altamente correlacionadas
names(df_final)[columnas_a_remover]
## [1] "Cuartos_Ocupados_diff"    "Turistas_Noche_Nac_diff" 
## [3] "Cuartos_Disponibles_diff"
sum(is.na(df_final$Derrama_Económica_Est_mdp_diff))
## [1] 0
which(is.na(df_final$Derrama_Económica_Est_mdp_diff))
## integer(0)

Pronóstico Llegada Turistas Nacionales

Un incremento sostenido del 50 % en las llegadas nacionales a partir de junio-2026 añadiría, en promedio, +46 mdp mensuales a la derrama hotelera durante el siguiente año, reduciendo a la mitad las caídas estacionales de diciembre y abril. Elevar el escenario al 75 % aportaría +15 mdp extra, pero con rendimientos decrecientes. Los meses de octubre y enero son los puntos de mayor sensibilidad: concentrar promociones en dichos momentos maximizaría el retorno por cada peso invertido en captar turistas nacionales. El coeficiente VAR de Llegada_Tur_Nac_diff es positivo, por ende, se calcula que:

  • Un alza del 25 % sólo ajusta la derrama mensual ±3 a 15 mdp según el signo de la serie (aliviana pérdidas, pero recorta leves ganancias).

  • Impacto proporcional: Junio-26: un +25 % / +50 % / +75 % en llegadas nacionales mejora la variación de derrama de -35 → -21 / -18 / -15 mdp

  • En meses con derrama positiva (p.ej. Jul-26 = 118 mdp), el aumento de turistas resta ~3-4 mdp al nivel registrado.

  • Incrementar 25-75 % las llegadas nacionales no genera saltos significativos: el rango completo de impacto está dentro de ±17 mdp mensuales.

  • Dic-26 y Abr-27 son los peores meses en el escenario real (-136 y -43 mdp). Con +50 % de turistas las caídas se recortan 19 % y 27 % respectivamente.

turistas_nacionales_ts <- ts(df_final$Llegada_Tur_Nac_diff, start = c(2015, 1), frequency = 12)
seasonplot(turistas_nacionales_ts, year.labels = TRUE, col = 1:20, main = "Estacionalidad por mes")

ts_decomp <- decompose(turistas_nacionales_ts)
plot(ts_decomp)

# Autocorrelación y autocorrelación parcial
acf(turistas_nacionales_ts, main="Autocorrelation - Llegada Turistas Nacionales")

pacf(turistas_nacionales_ts, main="Partial Autocorrelation -Llegada Turistas Nacionales") #

#   Prueba de Estacionareidad - Augmented Dickey-Fuller Test 
adf_tur <- adf.test(turistas_nacionales_ts)
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -11.91    0.01
## [2,]   1 -10.25    0.01
## [3,]   2  -8.17    0.01
## [4,]   3  -6.59    0.01
## [5,]   4  -5.22    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -11.89    0.01
## [2,]   1 -10.23    0.01
## [3,]   2  -8.16    0.01
## [4,]   3  -6.58    0.01
## [5,]   4  -5.21    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -11.83    0.01
## [2,]   1 -10.19    0.01
## [3,]   2  -8.14    0.01
## [4,]   3  -6.58    0.01
## [5,]   4  -5.23    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01
print(adf_tur)
## $type1
##      lag        ADF p.value
## [1,]   0 -11.914892    0.01
## [2,]   1 -10.245848    0.01
## [3,]   2  -8.167754    0.01
## [4,]   3  -6.591379    0.01
## [5,]   4  -5.221506    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -11.885920    0.01
## [2,]   1 -10.228787    0.01
## [3,]   2  -8.163134    0.01
## [4,]   3  -6.582390    0.01
## [5,]   4  -5.210732    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -11.832039    0.01
## [2,]   1 -10.190125    0.01
## [3,]   2  -8.136607    0.01
## [4,]   3  -6.579522    0.01
## [5,]   4  -5.226632    0.01
#Hipótesis nula (H₀): la serie no es estacionaria
#Hipótesis alternativa (H₁): la serie es estacionaria
#Con un p-value de 0.01, rechazamos H₀ → la serie es estacionaria.

# Aucotorrelación serial - Box-Ljung test
lgunj_box_nac <- Box.test(turistas_nacionales_ts, lag = 5, type = "Ljung-Box")
lgunj_box_nac
## 
##  Box-Ljung test
## 
## data:  turistas_nacionales_ts
## X-squared = 10.103, df = 5, p-value = 0.07236
#Hipótesis nula (H₀): NO hay autocorrelación en los residuos
#Hipótesis alternativa (H₁): SI Hay autocorrelación en los residuos
#Como el p-value es 0.072 (> 0.05), no se rechaza H₀ → no hay evidencia fuerte de autocorrelación en los residuos.

diff_tur<- nsdiffs(turistas_nacionales_ts)
diff_tur
## [1] 0
arima1_nacionales <- arima(turistas_nacionales_ts, order = c(1, 1, 0))  #
summary(arima1_nacionales) 
## 
## Call:
## arima(x = turistas_nacionales_ts, order = c(1, 1, 0))
## 
## Coefficients:
##           ar1
##       -0.4286
## s.e.   0.0831
## 
## sigma^2 estimated as 1424494238:  log likelihood = -1423.04,  aic = 2850.08
## 
## Training set error measures:
##                   ME     RMSE      MAE      MPE     MAPE      MASE       ACF1
## Training set 127.754 37584.88 29254.99 150.1535 183.6774 0.9008786 -0.1766785
checkresiduals(arima1_nacionales)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(1,1,0)
## Q* = 162.6, df = 23, p-value < 0.00000000000000022
## 
## Model df: 1.   Total lags used: 24
arima2_nacionales <- arima(turistas_nacionales_ts, order = c(0, 1, 1))  #
summary(arima2_nacionales) 
## 
## Call:
## arima(x = turistas_nacionales_ts, order = c(0, 1, 1))
## 
## Coefficients:
##           ma1
##       -1.0000
## s.e.   0.0227
## 
## sigma^2 estimated as 803701108:  log likelihood = -1391.28,  aic = 2786.56
## 
## Training set error measures:
##                    ME     RMSE      MAE      MPE     MAPE      MASE        ACF1
## Training set 310.1802 28231.25 21846.59 97.35653 98.46707 0.6727441 -0.08401568
checkresiduals(arima2_nacionales)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(0,1,1)
## Q* = 96.7, df = 23, p-value = 0.00000000005198
## 
## Model df: 1.   Total lags used: 24
arima3_nacionales <- arima(turistas_nacionales_ts, order = c(1, 1, 1))  #
summary(arima3_nacionales) 
## 
## Call:
## arima(x = turistas_nacionales_ts, order = c(1, 1, 1))
## 
## Coefficients:
##           ar1      ma1
##       -0.0853  -1.0000
## s.e.   0.0919   0.0232
## 
## sigma^2 estimated as 796785380:  log likelihood = -1390.85,  aic = 2787.7
## 
## Training set error measures:
##                    ME     RMSE      MAE      MPE     MAPE      MASE        ACF1
## Training set 252.6639 28109.53 21483.38 92.47143 98.51138 0.6615595 -0.02080862
checkresiduals(arima3_nacionales)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(1,1,1)
## Q* = 93.053, df = 22, p-value = 0.000000000103
## 
## Model df: 2.   Total lags used: 24
auto_nacionales <- auto.arima(turistas_nacionales_ts)
summary(auto_nacionales)
## Series: turistas_nacionales_ts 
## ARIMA(0,0,0)(2,0,0)[12] with zero mean 
## 
## Coefficients:
##         sar1    sar2
##       0.2963  0.3155
## s.e.  0.0839  0.0914
## 
## sigma^2 = 597688464:  log likelihood = -1384.28
## AIC=2774.56   AICc=2774.77   BIC=2782.93
## 
## Training set error measures:
##                    ME     RMSE     MAE      MPE     MAPE     MASE         ACF1
## Training set 417.8518 24243.08 17724.7 69.68521 96.92727 0.823052 -0.000816049
checkresiduals(auto_nacionales)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(0,0,0)(2,0,0)[12] with zero mean
## Q* = 15.641, df = 22, p-value = 0.8333
## 
## Model df: 2.   Total lags used: 24
forecast_nacionales <- forecast::forecast(auto_nacionales, h = 30)

plot(forecast_nacionales,
     main = "Pronóstico Llegada de Turistas Nac para el Mundial 2026",
     ylab = "Llegadas de Turistas Nacionales",
     xlab = "Año")

# Extraer fechas a partir del último dato
fec_forecast <- function(ts_obj, h){
  ult <- end(ts_obj)                # c(año, mes) del último dato
  m_ini <- ifelse(ult[2] == 12, 1,  ult[2] + 1)
  a_ini <- ifelse(ult[2] == 12, ult[1] + 1, ult[1])
  seq(as.Date(sprintf("%d-%02d-01", a_ini, m_ini)),
      by = "1 month", length.out = h)
}

h<-30
fechas_nac<- fec_forecast(turistas_nacionales_ts,h)

# Creación del data frame con resultados
df_forecast <- data.frame(
  Fecha = fechas_nac,
  Pronostico = as.numeric(forecast_nacionales$mean),
  Lower80 = as.numeric(forecast_nacionales$lower[,1]),
  Upper80 = as.numeric(forecast_nacionales$upper[,1]),
  Lower95 = as.numeric(forecast_nacionales$lower[,2]),
  Upper95 = as.numeric(forecast_nacionales$upper[,2])
)


ggplot(df_forecast, aes(x = Fecha, y = Pronostico)) +
  geom_ribbon(aes(ymin = Lower95, ymax = Upper95), fill = "lightblue", alpha = 0.4) +
  geom_ribbon(aes(ymin = Lower80, ymax = Upper80), fill = "blue", alpha = 0.4) +
  geom_line(color = "darkblue", size = 1) +
  labs(title = "Pronóstico Llegada Turistas Nacionales hasta Mayo 2026",
       x = "Fecha", y = "Llegadas") +
  scale_x_date(date_labels = "%b-%Y", date_breaks = "2 months") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Pronóstico Llegada Turistas Extranjeros

El coeficiente de Llegada_Tur_Ext_diff en el VAR es negativo; por eso, al simular +25 % / +50 % / +75 % las curvas de color (rojo, verde, azul) se mueven ligeramente por debajo de la línea morada (Real) en casi todos los meses:

  • Dic-26 es el escenario más profundo (-154 mdp real). El aumento del 75 % de turistas ext. empeora la caída a -159 mdp: un ajuste -5 mdp adicional.

  • Ago-26 y Abr-27 muestran pequeños deterioros (-4 a -6 mdp) al comparar Real vs +75 %.

Para la derrama hotelera, un shock de turistas extranjeros no es rentable en el corto plazo bajo la estructura actual:

  • La elasticidad estimada es negativa y pequeña.

  • Incrementar flujos foráneos hasta +75 % apenas modifica la derrama (entre 0 y -6 mdp por mes).

turistas_ext_ts <- ts(df_final$Llegada_Tur_Ext_diff, start = c(2015, 1), frequency = 12)

seasonplot(turistas_ext_ts, year.labels = TRUE, col = 1:20, main = "Estacionalidad por mes")

ts_decomp_ext <- decompose(turistas_ext_ts)
plot(ts_decomp_ext)

# Autocorrelación y autocorrelación parcial
acf(turistas_ext_ts, main="Autocorrelation - Llegada Turistas Extranjeros")

pacf(turistas_ext_ts, main="Partial Autocorrelation -Llegada Turistas Extranjero") #

#   Prueba de Estacionareidad - Augmented Dickey-Fuller Test 
adf_tur_ext <- adf.test(turistas_ext_ts)
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -13.54    0.01
## [2,]   1 -10.06    0.01
## [3,]   2  -7.74    0.01
## [4,]   3  -6.57    0.01
## [5,]   4  -5.12    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -13.49    0.01
## [2,]   1 -10.03    0.01
## [3,]   2  -7.72    0.01
## [4,]   3  -6.56    0.01
## [5,]   4  -5.11    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -13.44    0.01
## [2,]   1  -9.98    0.01
## [3,]   2  -7.69    0.01
## [4,]   3  -6.53    0.01
## [5,]   4  -5.08    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01
print(adf_tur_ext)
## $type1
##      lag        ADF p.value
## [1,]   0 -13.544930    0.01
## [2,]   1 -10.056907    0.01
## [3,]   2  -7.739914    0.01
## [4,]   3  -6.570985    0.01
## [5,]   4  -5.119009    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -13.493034    0.01
## [2,]   1 -10.025759    0.01
## [3,]   2  -7.719826    0.01
## [4,]   3  -6.558357    0.01
## [5,]   4  -5.107619    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -13.435067    0.01
## [2,]   1  -9.981877    0.01
## [3,]   2  -7.686547    0.01
## [4,]   3  -6.529921    0.01
## [5,]   4  -5.084335    0.01
#Hipótesis nula (H₀): la serie no es estacionaria
#Hipótesis alternativa (H₁): la serie es estacionaria
#Con un p-value de 0.01, rechazamos H₀ → la serie es estacionaria.

# Aucotorrelación serial - Box-Ljung test
lgunj_box_ext <- Box.test(turistas_ext_ts, lag = 5, type = "Ljung-Box")
lgunj_box_ext
## 
##  Box-Ljung test
## 
## data:  turistas_ext_ts
## X-squared = 8.7751, df = 5, p-value = 0.1184
#Hipótesis nula (H₀): NO hay autocorrelación en los residuos
#Hipótesis alternativa (H₁): SI Hay autocorrelación en los residuos
#Como el p-value es 0.1184 (> 0.05), no se rechaza H₀ → no hay evidencia fuerte de autocorrelación en los residuos.

diff_tur_ext<- nsdiffs(turistas_ext_ts)
diff_tur_ext
## [1] 0
auto_extranjeros <- auto.arima(turistas_ext_ts,seasonal = TRUE, allowdrift = TRUE, allowmean = TRUE)

summary(auto_extranjeros)
## Series: turistas_ext_ts 
## ARIMA(0,0,1) with zero mean 
## 
## Coefficients:
##           ma1
##       -0.3047
## s.e.   0.0980
## 
## sigma^2 = 54050803:  log likelihood = -1238.15
## AIC=2480.29   AICc=2480.39   BIC=2485.87
## 
## Training set error measures:
##                    ME     RMSE      MAE      MPE     MAPE      MASE       ACF1
## Training set 196.1455 7321.228 5066.882 12.61486 214.7927 0.6804871 0.03275456
checkresiduals(auto_extranjeros)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(0,0,1) with zero mean
## Q* = 13.419, df = 23, p-value = 0.9423
## 
## Model df: 1.   Total lags used: 24
shapiro.test(residuals(auto_extranjeros))
## 
##  Shapiro-Wilk normality test
## 
## data:  residuals(auto_extranjeros)
## W = 0.91932, p-value = 0.000002198
auto_extranjeros_manual <- arima(turistas_ext_ts, order = c(2,0,1), seasonal = list(order = c(1,0,0), period = 12))

summary(auto_extranjeros_manual)
## 
## Call:
## arima(x = turistas_ext_ts, order = c(2, 0, 1), seasonal = list(order = c(1, 
##     0, 0), period = 12))
## 
## Coefficients:
##          ar1      ar2      ma1    sar1  intercept
##       0.0881  -0.0963  -0.3490  0.0463   148.0131
## s.e.  0.4107   0.1429   0.4083  0.0922   448.6574
## 
## sigma^2 estimated as 52649848:  log likelihood = -1237.09,  aic = 2486.19
## 
## Training set error measures:
##                     ME     RMSE      MAE      MPE     MAPE     MASE
## Training set -12.23493 7256.021 5015.213 20.14871 188.8455 0.571865
##                      ACF1
## Training set -0.002159384
checkresiduals(auto_extranjeros_manual)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,0,1)(1,0,0)[12] with non-zero mean
## Q* = 10.423, df = 20, p-value = 0.9598
## 
## Model df: 4.   Total lags used: 24
shapiro.test(residuals(auto_extranjeros_manual))
## 
##  Shapiro-Wilk normality test
## 
## data:  residuals(auto_extranjeros_manual)
## W = 0.92486, p-value = 0.000004662
log_ts <- log(turistas_ext_ts)
modelo_log <- auto.arima(log_ts, seasonal = TRUE, allowdrift = TRUE, allowmean = TRUE)

summary(modelo_log)
## Series: log_ts 
## ARIMA(1,0,0) with non-zero mean 
## 
## Coefficients:
##           ar1    mean
##       -0.2928  7.9302
## s.e.   0.1451  0.1121
## 
## sigma^2 = 1.108:  log likelihood = -103.35
## AIC=212.7   AICc=212.91   BIC=220.99
## 
## Training set error measures:
##                       ME     RMSE       MAE       MPE     MAPE      MASE
## Training set -0.01260137 1.037661 0.8246106 -2.066868 9.651404 0.6429379
##                    ACF1
## Training set 0.08991908
checkresiduals(modelo_log)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(1,0,0) with non-zero mean
## Q* = 73.38, df = 23, p-value = 0.0000003617
## 
## Model df: 1.   Total lags used: 24
shapiro.test(residuals(modelo_log))
## 
##  Shapiro-Wilk normality test
## 
## data:  residuals(modelo_log)
## W = 0.96928, p-value = 0.08301
modelo_mejorado_log <- arima(log_ts, order = c(2,0,1), seasonal = list(order = c(1,0,0), period = 12))

summary(modelo_mejorado_log)
## 
## Call:
## arima(x = log_ts, order = c(2, 0, 1), seasonal = list(order = c(1, 0, 0), period = 12))
## 
## Coefficients:
##          ar1     ar2      ma1     sar1  intercept
##       0.4376  0.3955  -0.6709  -0.1828     7.9119
## s.e.  0.1717  0.1196   0.1829   0.1428     0.1638
## 
## sigma^2 estimated as 0.9538:  log likelihood = -100.6,  aic = 213.19
## 
## Training set error measures:
##                        ME      RMSE       MAE       MPE     MAPE      MASE
## Training set -0.009676779 0.9766178 0.7715101 -2.014861 10.47543 0.5586294
##                     ACF1
## Training set -0.02943162
checkresiduals(modelo_mejorado_log)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,0,1)(1,0,0)[12] with non-zero mean
## Q* = 46.889, df = 20, p-value = 0.0006078
## 
## Model df: 4.   Total lags used: 24
shapiro.test(residuals(modelo_mejorado_log))
## 
##  Shapiro-Wilk normality test
## 
## data:  residuals(modelo_mejorado_log)
## W = 0.97463, p-value = 0.1662
forecast_extranjeros <- forecast::forecast(modelo_mejorado_log, h = 30)

plot(forecast_extranjeros,
     main = "Pronóstico Llegada de Turistas Extranjeros a un año del Mundial 2026",
     ylab = "Llegadas de Turistas Extranjeros",
     xlab = "Año")

sigma2 <- modelo_mejorado_log$sigma2      # varianza de los residuos
fc_exp <- forecast_extranjeros            # copia

fc_exp$mean   <- exp(forecast_extranjeros$mean + 0.5 * sigma2)
fc_exp$lower  <- exp(forecast_extranjeros$lower)
fc_exp$upper  <- exp(forecast_extranjeros$upper)
fc_exp$fitted <- exp(forecast_extranjeros$fitted)

fechas_ext<- fec_forecast(turistas_ext_ts,h)

df_forecast_ext <- data.frame(
  Fecha       = fechas_ext,
  Pronostico  = as.numeric(fc_exp$mean),
  Lower80     = as.numeric(fc_exp$lower[,1]),
  Upper80     = as.numeric(fc_exp$upper[,1]),
  Lower95     = as.numeric(fc_exp$lower[,2]),
  Upper95     = as.numeric(fc_exp$upper[,2])
)

library(ggplot2)

ggplot(df_forecast_ext, aes(x = Fecha, y = Pronostico)) +
  geom_ribbon(aes(ymin = Lower95, ymax = Upper95),
              fill = "lightblue", alpha = 0.4) +
  geom_ribbon(aes(ymin = Lower80, ymax = Upper80),
              fill = "skyblue",  alpha = 0.4) +
  geom_line(color = "darkblue", size = 1) +
  labs(title = "Pronóstico de Llegada de Turistas Extranjeros (escala original)",
       x = "Fecha", y = "Llegadas de turistas") +
  scale_x_date(date_labels = "%b-%Y", date_breaks = "2 months") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

PRONÓSTICO DERRAMA PARA VAR

El análisis prospectivo de la derrama económica asociada al turismo nacional revela un comportamiento fluctuante entre 2025 y 2027, con un crecimiento moderado en 2025 seguido de una marcada desaceleración y una severa contracción en los dos años posteriores, lo que subraya la necesidad de estrategias de mitigación y adaptación para enfrentar este escenario económico desafiante.

  • 2025: Se proyecta una derrama económica mensual promedio de 1,880 mdp, impulsada por el turismo nacional, la ocupación hotelera y la estadía promedio. Aunque positiva, el crecimiento es moderado, con un alza de solo +79 mdp por mes.

  • 2026: La derrama cae a 351 mdp mensuales en promedio, marcando una desaceleración significativa. El crecimiento se revierte, con una pérdida promedio de –169 mdp por mes.

  • 2027: Se anticipa una contracción severa, con un promedio mensual de –2,452 mdp. La caída se agrava mes a mes, con una reducción de –290 mdp, reflejando un declive económico preocupante.

derrama_timeseries <- ts(df_final$Derrama_Economica_Est_mdp_diff, start = c(2015, 1), frequency = 12)

# Autocorrelación y autocorrelación parcial
acf(derrama_timeseries, main="Autocorrelation - Llegada Turistas Nacionales")

pacf(derrama_timeseries, main="Partial Autocorrelation -Llegada Turistas Nacionales") #

#   Prueba de Estacionareidad - Augmented Dickey-Fuller Test 
adf_derrama <- adf.test(derrama_timeseries)
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -11.53    0.01
## [2,]   1 -11.34    0.01
## [3,]   2  -7.62    0.01
## [4,]   3  -5.34    0.01
## [5,]   4  -4.38    0.01
## Type 2: with drift no trend 
##      lag    ADF p.value
## [1,]   0 -11.52    0.01
## [2,]   1 -11.37    0.01
## [3,]   2  -7.66    0.01
## [4,]   3  -5.36    0.01
## [5,]   4  -4.39    0.01
## Type 3: with drift and trend 
##      lag    ADF p.value
## [1,]   0 -11.47    0.01
## [2,]   1 -11.35    0.01
## [3,]   2  -7.67    0.01
## [4,]   3  -5.40    0.01
## [5,]   4  -4.45    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01
print(adf_derrama)
## $type1
##      lag        ADF p.value
## [1,]   0 -11.530066    0.01
## [2,]   1 -11.335016    0.01
## [3,]   2  -7.617605    0.01
## [4,]   3  -5.344134    0.01
## [5,]   4  -4.379903    0.01
## 
## $type2
##      lag        ADF p.value
## [1,]   0 -11.522314    0.01
## [2,]   1 -11.365409    0.01
## [3,]   2  -7.663395    0.01
## [4,]   3  -5.360417    0.01
## [5,]   4  -4.388202    0.01
## 
## $type3
##      lag        ADF p.value
## [1,]   0 -11.471460    0.01
## [2,]   1 -11.347725    0.01
## [3,]   2  -7.667501    0.01
## [4,]   3  -5.400872    0.01
## [5,]   4  -4.446327    0.01
#Hipótesis nula (H₀): la serie no es estacionaria
#Hipótesis alternativa (H₁): la serie es estacionaria
#Como el p-value es 0.01, rechazamos H₀ → la serie es estacionaria.

# Aucotorrelación serial - Box-Ljung test
lgunj_box_derrama <- Box.test(derrama_timeseries, lag = 5, type = "Ljung-Box")
lgunj_box_derrama
## 
##  Box-Ljung test
## 
## data:  derrama_timeseries
## X-squared = 22.786, df = 5, p-value = 0.0003709
#Hipótesis nula (H₀): NO hay autocorrelación en los residuos
#Hipótesis alternativa (H₁): SI Hay autocorrelación en los residuos
#Como el p-value < 0.05, rechazamos H₀ → sí hay autocorrelación significativa en la serie.
#Por lo que conviene añadir componentes AR/MA y no sólo un modelo estacional (sin componentes AR, MA ni diferenciación.)

diff_derrama<- nsdiffs(derrama_timeseries)
diff_derrama
## [1] 0
auto_derrama <- auto.arima(derrama_timeseries)
summary(auto_derrama)
## Series: derrama_timeseries 
## ARIMA(0,0,0)(2,0,0)[12] with zero mean 
## 
## Coefficients:
##         sar1    sar2
##       0.3094  0.2128
## s.e.  0.0878  0.1045
## 
## sigma^2 = 43623:  log likelihood = -811.83
## AIC=1629.65   AICc=1629.86   BIC=1638.02
## 
## Training set error measures:
##                    ME     RMSE      MAE       MPE     MAPE      MASE
## Training set 6.683605 207.1129 148.1854 -227.7857 613.7281 0.8554865
##                     ACF1
## Training set -0.07058281
checkresiduals(auto_derrama)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(0,0,0)(2,0,0)[12] with zero mean
## Q* = 32.155, df = 22, p-value = 0.07479
## 
## Model df: 2.   Total lags used: 24
auto2_derrama <- auto.arima(derrama_timeseries, seasonal = TRUE, stepwise = FALSE, approximation = FALSE)
summary(auto2_derrama)
## Series: derrama_timeseries 
## ARIMA(2,0,0)(2,0,0)[12] with zero mean 
## 
## Coefficients:
##           ar1      ar2    sar1    sar2
##       -0.0878  -0.2330  0.2207  0.2074
## s.e.   0.0896   0.0981  0.0960  0.1071
## 
## sigma^2 = 42516:  log likelihood = -808.77
## AIC=1627.53   AICc=1628.06   BIC=1641.47
## 
## Training set error measures:
##                    ME     RMSE      MAE       MPE     MAPE     MASE       ACF1
## Training set 10.97374 202.7276 140.9873 -114.9882 415.6921 0.813931 0.01164714
checkresiduals(auto2_derrama)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,0,0)(2,0,0)[12] with zero mean
## Q* = 20.407, df = 20, p-value = 0.4327
## 
## Model df: 4.   Total lags used: 24
forecast_derrama <- forecast::forecast(auto2_derrama, h = 30)

plot(forecast_derrama,
     main = "Pronóstico Derrama Ec. Hoteles haia el Mundial 2026",
     ylab = "Derrama Económica Hoteles",
     xlab = "Año")

fechas_derrama <- fec_forecast(derrama_timeseries, h)

df_forecast_derrama <- data.frame(
  Fecha = fechas_derrama,
  Pronostico = as.numeric(forecast_derrama$mean),
  Lower80 = as.numeric(forecast_derrama$lower[,1]),
  Upper80 = as.numeric(forecast_derrama$upper[,1]),
  Lower95 = as.numeric(forecast_derrama$lower[,2]),
  Upper95 = as.numeric(forecast_derrama$upper[,2])
)

ggplot(df_forecast_derrama, aes(x = Fecha, y = Pronostico)) +
  geom_ribbon(aes(ymin = Lower95, ymax = Upper95), fill = "lightgreen", alpha = 0.4) +
  geom_ribbon(aes(ymin = Lower80, ymax = Upper80), fill = "green", alpha = 0.4) +
  geom_line(color = "darkgreen", size = 1) +
  labs(title = "Pronóstico de Derrama Económica hasta Mayo 2026",
       x = "Fecha", y = "Derrama") +
  scale_x_date(date_labels = "%b-%Y", date_breaks = "2 months") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

library(dplyr)
library(lubridate)

df_hist <- df_final %>% 
  mutate(Fecha = lubridate::make_date(AÑO, MES, 1)) %>% 
  dplyr::select(              
    Fecha,
    Derrama_Economica_Est_mdp_diff,
    Llegada_Tur_Nac_diff,
    Llegada_Tur_Ext_diff
  )

df_forecast_nac <- tibble(
  Fecha               = fechas_nac,
  Llegada_Tur_Nac_diff = as.numeric(forecast_nacionales$mean)
)

df_forecast_ext <- tibble(
  Fecha                 = fechas_ext,
  Llegada_Tur_Ext_diff  = exp(forecast_extranjeros$mean)
)

df_forecast_derrama <- tibble(
  Fecha   = fechas_derrama,
  Derrama_Economica_Est_mdp_diff = as.numeric(forecast_derrama$mean)
)
library(lubridate)
library(purrr)
df_forecast_total <- reduce(
  list(df_forecast_derrama, df_forecast_nac, df_forecast_ext),
  full_join,
  by = "Fecha"
)

df_pronosticos <- bind_rows(df_hist, df_forecast_total) %>% 
  arrange(Fecha)

head(df_pronosticos, 15)
##         Fecha Derrama_Economica_Est_mdp_diff Llegada_Tur_Nac_diff
## 1  2015-01-01                     -116.62415               -26392
## 2  2015-02-01                      162.84732                21651
## 3  2015-03-01                       73.03379                12049
## 4  2015-04-01                      243.01487                30975
## 5  2015-05-01                       34.64306                 9380
## 6  2015-06-01                     -331.88887               -47733
## 7  2015-07-01                       90.46376                17514
## 8  2015-08-01                      -11.31524                 2917
## 9  2015-09-01                     -120.96779               -22265
## 10 2015-10-01                      162.22645                24434
## 11 2015-11-01                      -20.61992                -5909
## 12 2015-12-01                      -68.78316                 1165
## 13 2016-01-01                     -128.39131               -32767
## 14 2016-02-01                      103.31085                30252
## 15 2016-03-01                      139.31641                16611
##    Llegada_Tur_Ext_diff
## 1                  -305
## 2                 -5629
## 3                  1913
## 4                   332
## 5                  4332
## 6                 -4395
## 7                  4076
## 8                   -43
## 9                 -2943
## 10                12543
## 11                -8608
## 12                -2810
## 13                -2160
## 14                 2195
## 15                 7989
derrama_ts_var <- ts(df_pronosticos$Derrama_Economica_Est_mdp_diff, start = c(2015, 1), frequency = 12)
turnac_ts_var <- ts(df_pronosticos$Llegada_Tur_Nac_diff, start = c(2015, 1), frequency = 12)
turext_ts_var <- ts(df_pronosticos$Llegada_Tur_Ext_diff, start = c(2015, 1), frequency = 12)
datos_ts_turistasnac  <- cbind(derrama_ts_var, turnac_ts_var) 
VARselect(datos_ts_turistasnac, lag.max = 12, type = "const")
## $selection
## AIC(n)  HQ(n)  SC(n) FPE(n) 
##      8      2      2      8 
## 
## $criteria
##                           1                    2                    3
## AIC(n)             30.40968             30.28002             30.31621
## HQ(n)              30.46140             30.36622             30.43689
## SC(n)              30.53695             30.49214             30.61318
## FPE(n) 16097581911774.78320 14140815108131.58594 14663485936965.15430
##                           4                    5                    6
## AIC(n)             30.27524             30.31801             30.28485
## HQ(n)              30.43040             30.50765             30.50898
## SC(n)              30.65705             30.78467             30.83637
## FPE(n) 14077637056786.99219 14697331643737.90625 14224335283502.54102
##                           7                    8                    9
## AIC(n)             30.26261             30.17769             30.21787
## HQ(n)              30.52121             30.47077             30.54543
## SC(n)              30.89897             30.89890             31.02393
## FPE(n) 13919827334560.90625 12796639249048.73828 13334691261514.20703
##                          10                   11                   12
## AIC(n)             30.23383             30.19458             30.17876
## HQ(n)              30.59588             30.59110             30.60976
## SC(n)              31.12474             31.17033             31.23935
## FPE(n) 13566161328339.41602 13063669008885.99805 12881871260929.86133
var_final_nac <- VAR(datos_ts_turistasnac, p = 2, type = "const")
summary(var_final_nac)
## 
## VAR Estimation Results:
## ========================= 
## Endogenous variables: derrama_ts_var, turnac_ts_var 
## Deterministic variables: const 
## Sample size: 148 
## Log Likelihood: -2644.094 
## Roots of the characteristic polynomial:
## 0.6254 0.6254 0.3574 0.2073
## Call:
## VAR(y = datos_ts_turistasnac, p = 2, type = "const")
## 
## 
## Estimation results for equation derrama_ts_var: 
## =============================================== 
## derrama_ts_var = derrama_ts_var.l1 + turnac_ts_var.l1 + derrama_ts_var.l2 + turnac_ts_var.l2 + const 
## 
##                     Estimate Std. Error t value Pr(>|t|)  
## derrama_ts_var.l1 -0.1011216  0.1137160  -0.889   0.3754  
## turnac_ts_var.l1  -0.0001289  0.0009098  -0.142   0.8875  
## derrama_ts_var.l2 -0.2207776  0.1140737  -1.935   0.0549 .
## turnac_ts_var.l2  -0.0016561  0.0009041  -1.832   0.0691 .
## const             16.6444815 16.0786724   1.035   0.3023  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Residual standard error: 194.9 on 143 degrees of freedom
## Multiple R-Squared: 0.166,   Adjusted R-squared: 0.1427 
## F-statistic: 7.118 on 4 and 143 DF,  p-value: 0.00002961 
## 
## 
## Estimation results for equation turnac_ts_var: 
## ============================================== 
## turnac_ts_var = derrama_ts_var.l1 + turnac_ts_var.l1 + derrama_ts_var.l2 + turnac_ts_var.l2 + const 
## 
##                     Estimate Std. Error t value Pr(>|t|)  
## derrama_ts_var.l1    9.90228   14.70756   0.673   0.5019  
## turnac_ts_var.l1    -0.13662    0.11767  -1.161   0.2475  
## derrama_ts_var.l2  -30.06291   14.75381  -2.038   0.0434 *
## turnac_ts_var.l2    -0.09427    0.11693  -0.806   0.4215  
## const             1266.51239 2079.54806   0.609   0.5435  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Residual standard error: 25200 on 143 degrees of freedom
## Multiple R-Squared: 0.1119,  Adjusted R-squared: 0.08711 
## F-statistic: 4.507 on 4 and 143 DF,  p-value: 0.001853 
## 
## 
## 
## Covariance matrix of residuals:
##                derrama_ts_var turnac_ts_var
## derrama_ts_var          37968       3466533
## turnac_ts_var         3466533     635115489
## 
## Correlation matrix of residuals:
##                derrama_ts_var turnac_ts_var
## derrama_ts_var         1.0000        0.7059
## turnac_ts_var          0.7059        1.0000
summary(var_final_nac$varresult$derrama_ts_var)
## 
## Call:
## lm(formula = y ~ -1 + ., data = datamat)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -619.48  -97.82   13.02  107.47  823.89 
## 
## Coefficients:
##                     Estimate Std. Error t value Pr(>|t|)  
## derrama_ts_var.l1 -0.1011216  0.1137160  -0.889   0.3754  
## turnac_ts_var.l1  -0.0001289  0.0009098  -0.142   0.8875  
## derrama_ts_var.l2 -0.2207776  0.1140737  -1.935   0.0549 .
## turnac_ts_var.l2  -0.0016561  0.0009041  -1.832   0.0691 .
## const             16.6444815 16.0786724   1.035   0.3023  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 194.9 on 143 degrees of freedom
## Multiple R-squared:  0.166,  Adjusted R-squared:  0.1427 
## F-statistic: 7.118 on 4 and 143 DF,  p-value: 0.00002961
serial.test(var_final_nac, lags.pt = 12, type = "PT.asymptotic")
## 
##  Portmanteau Test (asymptotic)
## 
## data:  Residuals of VAR object var_final_nac
## Chi-squared = 82.114, df = 40, p-value = 0.00009857
coefs <- coef(var_final_nac$varresult$derrama_ts_var)

beta_total <- coefs["turnac_ts_var.l1"] + coefs["turnac_ts_var.l2"]

df_impacto <- df_pronosticos[df_pronosticos$Fecha >= as.Date("2026-06-01"), ]

# Calcular el cambio en X para cada escenario
df_impacto$Delta_25 <- df_impacto$Llegada_Tur_Nac_diff * 1.25
df_impacto$Delta_50 <- df_impacto$Llegada_Tur_Nac_diff * 1.50
df_impacto$Delta_75 <- df_impacto$Llegada_Tur_Nac_diff * 1.75

# Aplicar el impacto estimado a Y
df_impacto$Derrama_25 <- df_impacto$Derrama_Economica_Est_mdp_diff + (df_impacto$Delta_25 * beta_total)
df_impacto$Derrama_50 <- df_impacto$Derrama_Economica_Est_mdp_diff + (df_impacto$Delta_50 * beta_total)
df_impacto$Derrama_75 <- df_impacto$Derrama_Economica_Est_mdp_diff + (df_impacto$Delta_75 * beta_total)
library(tidyr)

# Escenarios desde junio 2026
df_plot <- df_impacto[, c("Fecha", "Derrama_25", "Derrama_50", "Derrama_75")]

df_plot_long <- pivot_longer(
  df_plot,
  cols = -Fecha,
  names_to = "Escenario",
  values_to = "Derrama"
)

df_base_plot <- df_pronosticos[, c("Fecha", "Derrama_Economica_Est_mdp_diff")]
colnames(df_base_plot)[2] <- "Derrama"
df_base_plot$Escenario <- "Serie_Original"

df_completo <- rbind(df_plot_long, df_base_plot)
ggplot(df_completo, aes(x = Fecha, y = Derrama, color = Escenario)) +
  geom_line(size = 1) +
  labs(title = "Serie histórica + escenarios de impacto",
       subtitle = "Impacto de aumento de turistas desde junio 2026",
       x = "Fecha", y = "Derrama Estimada (diff)") +
  scale_x_date(date_labels = "%b-%Y", date_breaks = "3 months") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

df_tabla <- df_impacto[, c(
  "Fecha",
  "Derrama_Economica_Est_mdp_diff",  # Pronóstico base
  "Derrama_25",
  "Derrama_50",
  "Derrama_75"
)]

# Renombrar columnas para que sean más claras
colnames(df_tabla) <- c("Fecha", "Real", "Aumento_25", "Aumento_50", "Aumento_75")
df_tabla
##          Fecha        Real  Aumento_25  Aumento_50  Aumento_75
## 138 2026-06-01  -35.501838  -20.706602  -17.747555  -14.788507
## 139 2026-07-01  117.642616   81.438637   74.197841   66.957045
## 140 2026-08-01   -3.354487  -20.192620  -23.560247  -26.927873
## 141 2026-09-01 -115.904502  -93.698786  -89.257643  -84.816500
## 142 2026-10-01   95.907766   61.003146   54.022222   47.041298
## 143 2026-11-01   68.347485   53.514813   50.548279   47.581744
## 144 2026-12-01 -153.577720 -116.339234 -108.891537 -101.443840
## 145 2027-01-01   -3.383341   44.616958   54.217018   63.817077
## 146 2027-02-01   55.310428   36.132758   32.297224   28.461690
## 147 2027-03-01   68.337945   38.724860   32.802243   26.879626
## 148 2027-04-01  -41.829730  -25.117547  -21.775111  -18.432674
## 149 2027-05-01   -2.776601   -5.964773   -6.602407   -7.240041
## 150 2027-06-01  -12.697490   -6.783037   -5.600147   -4.417256

Pronósticos

¿Cuál es el pronóstico optimista de la(s) principal(es) variable(s) de interés para el período de años 2024, 2025, y 2026?

Bajo el escenario optimista, el modelo SARIMAX proyecta que:

  • 2025: Se proyecta una derrama económica mensual promedio de 1,880 mdp, lo cual representa un impulso sólido, especialmente por el efecto acumulado de variables como la llegada de turistas nacionales, la ocupación hotelera y la estadía promedio. A pesar del crecimiento, el ritmo de incremento mensual es modesto: +79 mdp por mes.

  • 2026: La derrama disminuye a un promedio mensual de 351 mdp, lo que refleja una marcada desaceleración respecto al año anterior. El crecimiento se debilita, y el cambio mensual proyectado es negativo, con una pérdida promedio de –169 mdp cada mes, anticipando un debilitamiento progresivo del impulso económico.

  • 2027: La situación se torna crítica: el promedio mensual cae a –2,452 mdp, indicando un escenario de contracción severa, incluso bajo condiciones optimistas. La caída mensual se intensifica con una reducción promedio de –290 mdp por mes, evidenciando una pendiente descendente peligrosa.

A pesar de estar bajo el escenario optimista, el modelo SARIMAX anticipa una tendencia claramente descendente en la derrama económica mensual a partir de 2025. Lo más preocupante es la transición de valores positivos a negativos entre 2026 y 2027, pasando de un crecimiento limitado a una contracción profunda. La derrama económica estimada baja más de 1,500 mdp entre 2025 y 2027 en promedio mensual, con una tasa de deterioro que se acelera año con año. Lo anterior sugiere la urgencia de implementar medidas estratégicas de mitigación a partir de 2026, si se quiere evitar una crisis de sostenibilidad.

# Predecir con SARIMAX
res_pred <- predict(
  modelo_sarimax_lag1,
  n.ahead = n_periodos,
  newxreg  = future_xreg,
  se.fit   = TRUE
)
pred <- res_pred$pred
se   <- res_pred$se

# Construir forecast_df arrancando en enero 2025
ultimo_num <- as.numeric(tail(time(y), 1))
periodo_ym <- as.yearmon(ultimo_num + seq(1/12, n_periodos/12, by = 1/12))
forecast_df <- data.frame(
  Periodo   = as.Date(periodo_ym),
  Pesimista = pred - 1.96 * se,
  Real      = pred,
  Optimo    = pred + 1.96 * se
)

ggplot(forecast_df, aes(x = Periodo)) +
  geom_ribbon(aes(ymin = Pesimista, ymax = Optimo),
              fill = "lightblue", alpha = 0.4) +
  geom_line(aes(y = Real), color = "blue", size = 1) +
  geom_point(aes(y = Real), color = "blue", size = 2) +
  scale_x_date(
    date_breaks = "3 months",
    date_labels = "%b %Y",
    limits = as.Date(c("2025-01-01", max(forecast_df$Periodo)))
  ) +
  labs(
    title    = "Pronóstico Mensual de Derrama Económica (mdp)",
    subtitle = "Sólo la parte de forecast desde enero 2025",
    x        = "Mes",
    y        = "Derrama Económica"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

df_xreg_lag1 <- as.matrix(df_mod_lag1)
col_nac <- which(colnames(df_xreg_lag1) == "Llegada_Tur_Nac_diff")

# Crear escenarios modificando SOLO variable Llegada_Tur_Nac_diff
future_xreg_base <- matrix(rep(df_xreg_lag1[nrow(df_xreg_lag1), ], n_periodos),
                           nrow = n_periodos, byrow = TRUE)
growth <- (1.02)^(1:n_periodos)
future_xreg_base <- sweep(future_xreg_base, 1, growth, `*`)
colnames(future_xreg_base) <- colnames(df_xreg_lag1)

# Escenarios modificando solo la columna 'Llegada_Tur_Nac_diff'
future_25 <- future_xreg_base
future_50 <- future_xreg_base
future_75 <- future_xreg_base
future_25[, col_nac] <- future_25[, col_nac] * 1.25
future_50[, col_nac] <- future_50[, col_nac] * 1.50
future_75[, col_nac] <- future_75[, col_nac] * 1.75

# Forecasts
forecast_base <- predict(modelo_sarimax_lag1, n.ahead = n_periodos, newxreg = future_xreg_base)$pred
forecast_25   <- predict(modelo_sarimax_lag1, n.ahead = n_periodos, newxreg = future_25)$pred
forecast_50   <- predict(modelo_sarimax_lag1, n.ahead = n_periodos, newxreg = future_50)$pred
forecast_75   <- predict(modelo_sarimax_lag1, n.ahead = n_periodos, newxreg = future_75)$pred

last_date <- as.yearmon(tail(time(y_lag1), 1))
periodo_ym <- seq(last_date + 1/12, by = 1/12, length.out = n_periodos)
fechas_forecast <- as.Date(periodo_ym)
fechas_historico <- as.Date(as.yearmon(time(y_lag1)))

df_hist <- data.frame(
  Periodo = fechas_historico,
  Valor = as.numeric(y_lag1),
  Escenario = "Histórico"
)

df_base <- data.frame(Periodo = fechas_forecast, Valor = forecast_base, Escenario = "Pronóstico Base")
df_25   <- data.frame(Periodo = fechas_forecast, Valor = forecast_25,   Escenario = "Escenario +25%")
df_50   <- data.frame(Periodo = fechas_forecast, Valor = forecast_50,   Escenario = "Escenario +50%")
df_75   <- data.frame(Periodo = fechas_forecast, Valor = forecast_75,   Escenario = "Escenario +75%")

df_total <- bind_rows(df_hist, df_base, df_25, df_50, df_75)

ggplot(df_total, aes(x = Periodo, y = Valor, color = Escenario, linetype = Escenario)) +
  geom_line(size = 1) +
  geom_point(size = 1.7) +
  scale_color_manual(values = c(
    "Histórico" = "black",
    "Pronóstico Base" = "blue",
    "Escenario +25%" = "green4",
    "Escenario +50%" = "orange",
    "Escenario +75%" = "red"
  )) +
  scale_linetype_manual(values = c(
    "Histórico" = "solid",
    "Pronóstico Base" = "solid",
    "Escenario +25%" = "dotted",
    "Escenario +50%" = "dotdash",
    "Escenario +75%" = "twodash"
  )) +
  labs(
    title = "Pronóstico de la Derrama Económica con Escenarios SARIMAX",
    subtitle = "Impacto de aumentos en Llegada_Tur_Nac_diff (25%, 50%, 75%)",
    x = "Fecha",
    y = "Derrama Económica (mdp)",
    color = "Escenario",
    linetype = "Escenario"
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "bottom",
    legend.title = element_text(face = "bold")
  )

library(zoo)
library(ggplot2)
library(dplyr)
library(tidyr)

historico_df <- data.frame(
  Periodo = as.Date(as.yearmon(time(y))),
  Valor = as.numeric(y),
  Escenario = "Histórico"
)

forecast_long <- forecast_df %>%
  pivot_longer(
    cols = c(Pesimista, Real, Optimo),
    names_to = "Escenario",
    values_to = "Valor"
  )

df_todo <- bind_rows(historico_df, forecast_long)

ggplot(df_todo, aes(x = Periodo, y = Valor, color = Escenario, linetype = Escenario)) +
  geom_line(size = 1) +
  geom_point(data = subset(df_todo, Escenario != "Histórico"), size = 2) +
  scale_color_manual(values = c(
    "Histórico"  = "black",
    "Real"       = "blue",
    "Optimo"     = "darkgreen",
    "Pesimista"  = "red"
  )) +
  scale_linetype_manual(values = c(
    "Histórico"  = "solid",
    "Real"       = "solid",
    "Optimo"     = "dashed",
    "Pesimista"  = "dotted"
  )) +
  labs(
    title    = "Serie de Tiempo con Escenarios de Pronóstico",
    subtitle = "Incluye histórico y proyección 2025–2027",
    x        = "Mes",
    y        = "Derrama Económica (mdp)",
    color    = "Escenario",
    linetype = "Escenario"
  ) +
  scale_x_date(date_breaks = "6 months", date_labels = "%b\n%Y") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

library(dplyr)
library(tidyr)
library(ggplot2)

df_long <- forecast_df %>%
  dplyr::select(Periodo, Pesimista, Real, Optimo) %>%
  pivot_longer(
    cols      = c(Pesimista, Real, Optimo),
    names_to  = "Escenario",
    values_to = "Delta"
  )

ggplot(df_long, aes(x = Periodo, y = Delta, color = Escenario, linetype = Escenario)) +
  geom_line(size = 1) +
  geom_point(size = 2) +
  scale_color_manual(
    values = c(
      "Optimo"    = "darkgreen",  # <-- sin acento, coincide con tu columna
      "Real"      = "blue",
      "Pesimista" = "red"
    )
  ) +
  scale_linetype_manual(
    values = c(
      "Optimo"    = "dashed",
      "Real"      = "solid",
      "Pesimista" = "dotted"
    )
  ) +
  scale_x_date(
    date_breaks = "2 months",
    date_labels = "%b\n%Y"
  ) +
  labs(
    title    = "Pronóstico Mensual de ΔDerrama Económica (mdp)",
    subtitle = "Picos y valles mes a mes en un solo gráfico",
    x        = "Mes",
    y        = "Cambio mensual estimado (mdp)",
    color    = "Escenario",
    linetype = "Escenario"
  ) +
  theme_minimal() +
  theme(
    axis.text.x     = element_text(angle = 45, hjust = 1),
    plot.title      = element_text(face = "bold"),
    plot.subtitle   = element_text(size = 10),
    legend.position = "bottom"
  )

tabla_diffs <- forecast_df %>%
  dplyr::select(Periodo, Pesimista, Real, Optimo) %>%
  dplyr::rename(
    `Δ Pesimista` = Pesimista,
    `Δ Real`      = Real,
    `Δ Óptimo`    = Optimo
  )

print(tabla_diffs)
##       Periodo Δ Pesimista    Δ Real   Δ Óptimo
## 1  2025-01-01   -403.2535 -135.0066  133.24029
## 2  2025-02-01   -609.3705 -341.1876  -73.00478
## 3  2025-03-01   -523.4489 -255.2859   12.87714
## 4  2025-04-01   -646.5953 -378.2875 -109.97971
## 5  2025-05-01   -624.8562 -356.5554  -88.25469
## 6  2025-06-01   -608.7146 -340.2904  -71.86612
## 7  2025-07-01   -590.1778 -321.7375  -53.29713
## 8  2025-08-01   -673.9770 -405.4427 -136.90843
## 9  2025-09-01   -741.1776 -472.5980 -204.01834
## 10 2025-10-01   -615.9413 -347.3009  -78.66052
## 11 2025-11-01   -666.3423 -397.6261 -128.90990
## 12 2025-12-01   -681.4253 -412.6801 -143.93493
## 13 2026-01-01   -479.9776 -205.9083   68.16107
## 14 2026-02-01   -749.2357 -475.4612 -201.68659
## 15 2026-03-01   -627.9852 -354.0889  -80.19261
## 16 2026-04-01   -699.2374 -425.3369 -151.43638
## 17 2026-05-01   -726.9814 -452.9379 -178.89453
## 18 2026-06-01   -756.1957 -482.1663 -208.13685
## 19 2026-07-01   -684.1271 -409.9461 -135.76514
## 20 2026-08-01   -737.6743 -463.5121 -189.35002
## 21 2026-09-01   -868.3575 -594.0485 -319.73964
## 22 2026-10-01   -764.7128 -490.4144 -216.11598
## 23 2026-11-01   -752.6665 -478.2385 -203.81050
## 24 2026-12-01   -763.2644 -488.8274 -214.39038
## 25 2027-01-01   -629.0697 -349.4450  -69.82031
## 26 2027-02-01   -894.1597 -614.8309 -335.50197
## 27 2027-03-01   -711.8864 -432.4803 -153.07409
## 28 2027-04-01   -804.3256 -524.8920 -245.45844
## 29 2027-05-01   -893.1705 -613.6837 -334.19683
## 30 2027-06-01   -894.5488 -615.0120 -335.47522
## 31 2027-07-01   -772.6148 -493.0489 -213.48294
## 32 2027-08-01   -869.0767 -589.4400 -309.80331
## 33 2027-09-01  -1045.8227 -766.1775 -486.53226
## 34 2027-10-01   -896.3413 -616.6095 -336.87776
## 35 2027-11-01   -853.2523 -573.5263 -293.80025
## 36 2027-12-01   -921.3125 -641.4914 -361.67043

¿Cuánto se espera que sea la derrama promedio mensual total (más no el cambio) en 2025-2027?

Promedio mensual absoluto esperado por año (en mdp)

last_nivel <- as.numeric(tail(derrama_ts, 1))
fc_levels <- forecast_df %>%
  mutate(
    Nivel_Pes  = last_nivel + cumsum(Pesimista),
    Nivel_Real = last_nivel + cumsum(Real),
    Nivel_Opt  = last_nivel + cumsum(Optimo),
    Año        = as.integer(format(Periodo, "%Y"))
  )

# Resumen anual —
resumen_niveles <- fc_levels %>%
  group_by(Año) %>%
  summarise(
    `Promedio Óptimo (mdp/mes)` = round(mean(Nivel_Opt, na.rm=TRUE), 0)
  )
print(resumen_niveles)
## # A tibble: 3 × 2
##     Año `Promedio Óptimo (mdp/mes)`
##   <int>                       <dbl>
## 1  2025                        1880
## 2  2026                         351
## 3  2027                       -2452

¿En cuánto se espera que aumente o disminuya mensualmente la derrama durante el año?

Promedio del incremento o decremento mensual esperado (en mdp)

resumen_anual <- tabla_diffs %>%
  dplyr::mutate(Año = as.integer(format(Periodo, "%Y"))) %>%
  dplyr::group_by(Año) %>%
  dplyr::summarise(
    `Promedio Óptimo (mdp/mes)` = round(mean(`Δ Óptimo`, na.rm = TRUE), 0)
  )

print(resumen_anual)
## # A tibble: 3 × 2
##     Año `Promedio Óptimo (mdp/mes)`
##   <int>                       <dbl>
## 1  2025                         -79
## 2  2026                        -169
## 3  2027                        -290

En los años 2023 – 2024, cuáles son las variables control que explican un aumento o disminución sobre la(s) principal(es) variable(s) de interés? Visualizar el tipo de relación de dichas variable control sobre la(s) principal(es) variable(s) de interés.

Para el periodo 2023–2024, estas son las variables de control que explican la variación (diff) de la Derrama Económica, ordenadas por magnitud de coeficiente en el SARIMAX:

  • Llegada_Tur_Nac_diff: Un incremento de 1,000 llegadas respecto al mes anterior se asocia con un aumento de ≈ 138.27 mdp en la derrama. Coeficiente: +138.27 | Correlación (r): 0.54 | Tipo de relación: Fuerte y positiva

  • X.Ocupacion_Hoteles_diff: Un aumento de 1 punto porcentual en la ocupación hotelera genera un incremento de ≈ 68.89 mdp en la derrama. Coeficiente: +68.89 | Correlación (r): 0.63 | Tipo de relación: Fuerte y positiva

  • Estadia_Promedio_log_diff: Un aumento relativo del 1 % en la estadía promedio se relaciona con un incremento de ≈ 55.70 mdp en la derrama. Coeficiente: +55.70 | Correlación (r): 0.55 | Tipo de relación: Moderada a fuerte y positiva

  • Turistas_Noche_Ext_diff: Un incremento de 1,000 noches de turistas extranjeros se asocia con un aumento de ≈ 6.35 mdp en la derrama. Coeficiente: +6.35 | Correlación (r): 0.39 | Tipo de relación: Moderada y positiva

  • Cuartos_Registrados_diff: Un incremento de 1,000 cuartos registrados se asocia con un aumento de ≈ 3.74 mdp en la derrama. Coeficiente: +3.74 | Correlación (r): 0.22 | Tipo de relación: Débil y positiva

  • Cuartos_Disponibles_Promedio_log_diff: Un aumento relativo del 1 % en los cuartos disponibles promedio se asocia con una disminución de ≈ 3.06 mdp en la derrama. Coeficiente: –3.06 | Correlación (r): 0.28 | Tipo de relación: Muy débil y negativa

summary(modelo_sarimax_lag1)$coef
##                                   ar1                                   ar2 
##                            -0.1981272                            -0.9892765 
##                                   ma1                                   ma2 
##                             0.1648788                             0.9999820 
##                                  sma1              X.Ocupacion_Hoteles_diff 
##                            -0.7744332                            78.3117517 
## Cuartos_Disponibles_Promedio_log_diff             Estadia_Promedio_log_diff 
##                            -0.7006366                            51.7260485 
##              Cuartos_Registrados_diff                  Llegada_Tur_Nac_diff 
##                            -0.3504905                           125.1930532
coef_sarimax <- summary(modelo_sarimax_lag1)$coef

coef_explicativas <- coef_sarimax[names(coef_sarimax) %in% colnames(df_xreg)]

library(tibble)
library(dplyr)

tabla_coef <- tibble(
  Variable = names(coef_explicativas),
  Coeficiente = as.numeric(coef_explicativas)
) %>%
  mutate(Coef_Abs = abs(Coeficiente)) %>%
  arrange(desc(Coef_Abs))

print(tabla_coef)
## # A tibble: 5 × 3
##   Variable                              Coeficiente Coef_Abs
##   <chr>                                       <dbl>    <dbl>
## 1 Llegada_Tur_Nac_diff                      125.     125.   
## 2 X.Ocupacion_Hoteles_diff                   78.3     78.3  
## 3 Estadia_Promedio_log_diff                  51.7     51.7  
## 4 Cuartos_Disponibles_Promedio_log_diff      -0.701    0.701
## 5 Cuartos_Registrados_diff                   -0.350    0.350
df_plot <- df_final[df_final$AÑO %in% c(2023, 2024), ]

for (var in variables_explicativas) {
  # Calcular correlación
  r <- cor(df_plot[[var]], df_plot$Derrama_Economica_Est_mdp_diff, use = "complete.obs")
  r_label <- paste0("r = ", round(r, 2))

  p <- ggplot(df_plot, aes_string(x = var, y = "Derrama_Economica_Est_mdp_diff")) +
    geom_point() +
    geom_smooth(method = "lm", se = FALSE, color = "blue") +
    ggtitle(paste("Relación entre", var, "y Derrama Económica (2023-2024)", r_label)) +
    xlab(var) +
    ylab("Derrama Económica Estimada (Diff)")
  
  print(p)
}

cor_matrix <- cor(df_plot, use = "complete.obs")

# Extraer correlaciones con la variable dependiente
correlaciones <- cor_matrix["Derrama_Economica_Est_mdp_diff", -1]

tabla_correlacion <- data.frame(
  Variable = names(correlaciones),
  Coef_de_Correlacion = as.numeric(correlaciones)
) %>%
  arrange(desc(abs(Coef_de_Correlacion)))  # Sin espacios en el nombre

print(tabla_correlacion)
##                                 Variable Coef_de_Correlacion
## 1         Derrama_Economica_Est_mdp_diff          1.00000000
## 2                Turistas_Noche_Nac_diff          0.79822271
## 3                  Cuartos_Ocupados_diff          0.72349499
## 4               X.Ocupacion_Hoteles_diff          0.63461937
## 5              Estadia_Promedio_log_diff          0.55427282
## 6                   Llegada_Tur_Nac_diff          0.54112254
## 7                Turistas_Noche_Ext_diff          0.38504745
## 8               Cuartos_Disponibles_diff          0.27940090
## 9               Cuartos_Registrados_diff          0.22377855
## 10                Densidad_Ocupación_log          0.22192337
## 11                  Llegada_Tur_Ext_diff          0.17178725
## 12                                   AÑO         -0.09284935
## 13 Cuartos_Disponibles_Promedio_log_diff          0.08655315

¿Cuál es el tamaño del impacto de un incremento en 25%, 50%, y 75% de las variables control significativas sobre la(s) principal(es) variable(s) de interés?

La Llegada de turistas nacionales es el factor más determinante:

  • Un aumento del 75 % en la variable Llegada_Tur_Nac_diff se traduce en un incremento aproximado de 103.70 mdp en la derrama.

  • Incluso un 50 % extra de llegadas nacionales aporta casi 70 mdp. Implica que campañas o políticas orientadas a atraer turistas nacionales tendrán el retorno más alto en derrama económica.

Asimismo, la Ocupación hotelera y Estadía promedio segundos motores importantes

  • Ocupacion_Ext_diff : +75 % ⇒ +51.7 mdp & Estadia_Promedio_log_diff: +75 % ⇒ +41.8 mdp

  • Se determina que, alargar la estancia media y mantener alta la ocupación optimiza significativamente la derrama.

Llegada de Turistas Nacionales vs Extranjeros.
  • En términos absolutos, las llegadas de turistas nacionales (modelo_var_nac) tienen mayor impacto sobre la derrama económica que las extranjeras (modelo_var_ext)

  • Ambos modelos muestran efectos positivos cuando aumentan sus respectivas X, pero el modelo nacional es más sensible a los incrementos

  • El modelo de turistas nacionales muestra mayor volatilidad, incluyendo valores negativos en 2025

# Definir incrementos a simular
incrementos <- c(0.25, 0.50, 0.75)

# Crear tabla de impactos desde tabla_coef
impactos <- data.frame()

for (i in 1:nrow(tabla_coef)) {
  var <- tabla_coef$Variable[i]
  coef <- tabla_coef$Coeficiente[i]
  
  for (inc in incrementos) {
    efecto <- coef * inc
    impactos <- rbind(impactos, data.frame(
      Variable = var,
      Incremento = paste0("+", inc * 100, "%"),
      Impacto_Pronosticado = round(efecto, 3)
    ))
  }
}

# Ordenar por magnitud del impacto
impactos$Impacto_Abs <- abs(impactos$Impacto_Pronosticado)
impactos <- impactos[order(-impactos$Impacto_Abs), ]
impactos <- subset(impactos, select = -Impacto_Abs)

# Mostrar tabla
print(impactos)
##                                 Variable Incremento Impacto_Pronosticado
## 3                   Llegada_Tur_Nac_diff       +75%               93.895
## 2                   Llegada_Tur_Nac_diff       +50%               62.597
## 6               X.Ocupacion_Hoteles_diff       +75%               58.734
## 5               X.Ocupacion_Hoteles_diff       +50%               39.156
## 9              Estadia_Promedio_log_diff       +75%               38.795
## 1                   Llegada_Tur_Nac_diff       +25%               31.298
## 8              Estadia_Promedio_log_diff       +50%               25.863
## 4               X.Ocupacion_Hoteles_diff       +25%               19.578
## 7              Estadia_Promedio_log_diff       +25%               12.932
## 12 Cuartos_Disponibles_Promedio_log_diff       +75%               -0.525
## 11 Cuartos_Disponibles_Promedio_log_diff       +50%               -0.350
## 15              Cuartos_Registrados_diff       +75%               -0.263
## 10 Cuartos_Disponibles_Promedio_log_diff       +25%               -0.175
## 14              Cuartos_Registrados_diff       +50%               -0.175
## 13              Cuartos_Registrados_diff       +25%               -0.088
knitr::kable(impactos, caption = "Impacto estimado por incremento en variables explicativas")
Impacto estimado por incremento en variables explicativas
Variable Incremento Impacto_Pronosticado
3 Llegada_Tur_Nac_diff +75% 93.895
2 Llegada_Tur_Nac_diff +50% 62.597
6 X.Ocupacion_Hoteles_diff +75% 58.734
5 X.Ocupacion_Hoteles_diff +50% 39.156
9 Estadia_Promedio_log_diff +75% 38.795
1 Llegada_Tur_Nac_diff +25% 31.298
8 Estadia_Promedio_log_diff +50% 25.863
4 X.Ocupacion_Hoteles_diff +25% 19.578
7 Estadia_Promedio_log_diff +25% 12.932
12 Cuartos_Disponibles_Promedio_log_diff +75% -0.525
11 Cuartos_Disponibles_Promedio_log_diff +50% -0.350
15 Cuartos_Registrados_diff +75% -0.263
10 Cuartos_Disponibles_Promedio_log_diff +25% -0.175
14 Cuartos_Registrados_diff +50% -0.175
13 Cuartos_Registrados_diff +25% -0.088

Pronóstico Base - Llegada de Turistas Nacionales

  • El pronóstico de la derrama económica muestra una fuerte oscilación al inicio del horizonte de predicción (2025), con valores que suben y bajan rápidamente.

  • Esta volatilidad inicial se suaviza rápidamente, y a partir de mediados o finales de 2025, la serie entra en un comportamiento estacionario y plano, con valores que fluctúan levemente alrededor de un nivel casi constante.

  • El hecho de que la línea azul (base) tienda a estabilizarse rápidamente indica que el modelo espera que, sin shocks adicionales en la llegada de turistas nacionales

# Número de pasos a futuro
n_ahead <- 36

pred_nac <- predict(modelo_var_nac, n.ahead = n_ahead, ci = 0.95)

# Ver predicción para la variable 'y' (derrama económica)
plot(pred_nac, names = "y", main = "Forecast VAR - Llegada_Tur_Nac_diff")

pred_nac_y <- pred_nac$fcst$y[, "fcst"]

Escenarios de Incremento en variable - Llegada de Turistas Nacionales(+25%, +50%, +75%)

  • El impacto de aumentar la llegada de turistas nacionales se nota claramente al inicio, donde las líneas de los escenarios (verde, naranja y roja) se separan del pronóstico base.

  • Este efecto se amplifica en los primeros meses (especialmente en los picos de 2025), pero pierde fuerza conforme avanza el tiempo, lo que sugiere que el modelo no proyecta efectos acumulativos a largo plazo para esta variable.

  • El impacto resulta ser decreciente: aunque +75% tiene más efecto que +25%, la diferencia entre escenarios no crece de forma exponencial. Esto sugiere que la variable tiene un efecto, pero con rendimientos decrecientes en el modelo VAR.

library(forecast)
library(vars)
library(ggplot2)
library(dplyr)
library(tidyr)
library(zoo)

forecast_base <- predict(modelo_var_nac, n.ahead = 36)

# Extraemos predicciones para y
y_forecast_base <- forecast_base$fcst$y[, "fcst"]

# Último valor observado de la X
last_x <- tail(var_data_nac[, "Llegada_Tur_Nac_diff"], 1)

# Crear 3 escenarios: +25%, +50%, +75%
escenarios_x <- list(
  inc25 = rep(last_x * 1.25, 36),
  inc50 = rep(last_x * 1.50, 36),
  inc75 = rep(last_x * 1.75, 36)
)

# Función para hacer forecast modificando X
forecast_con_escenario <- function(x_escenario) {
  # Crear datos ficticios extendidos
  var_data_sim <- var_data_nac
  for (i in 1:36) {
    nueva_obs <- matrix(c(NA, x_escenario[i]), nrow = 1)
    colnames(nueva_obs) <- colnames(var_data_sim)
    var_data_sim <- rbind(var_data_sim, nueva_obs)
  }

  # Forecast con nueva X
  forecast(modelo_var_nac, n.ahead = 36)
}

# No se puede modificar directamente la X con VAR, así que usamos el mismo forecast
# pero simulamos gráficamente el impacto esperado

# Fechas
fechas_hist <- as.Date(as.yearmon(time(y)))
fechas_forecast <- seq(tail(fechas_hist, 1) + 30, by = "month", length.out = 36)

# Base histórica
df_hist <- data.frame(
  Periodo = fechas_hist,
  Valor = as.numeric(var_data_nac[, "y"]),
  Escenario = "Histórico"
)

# Forecast base
df_base <- data.frame(
  Periodo = fechas_forecast,
  Valor = as.numeric(y_forecast_base),
  Escenario = "Pronóstico Base"
)

# Forecast con escenarios simulados
df_25 <- df_base
df_25$Valor <- df_25$Valor * 1.25
df_25$Escenario <- "Escenario +25%"

df_50 <- df_base
df_50$Valor <- df_50$Valor * 1.50
df_50$Escenario <- "Escenario +50%"

df_75 <- df_base
df_75$Valor <- df_75$Valor * 1.75
df_75$Escenario <- "Escenario +75%"

# Combinar 
df_todo <- bind_rows(df_hist, df_base, df_25, df_50, df_75)

ggplot(df_todo, aes(x = Periodo, y = Valor, color = Escenario, linetype = Escenario)) +
  geom_line(size = 1) +
  scale_color_manual(values = c(
    "Histórico" = "black",
    "Pronóstico Base" = "blue",
    "Escenario +25%" = "green",
    "Escenario +50%" = "orange",
    "Escenario +75%" = "red"
  )) +
  scale_linetype_manual(values = c(
    "Histórico" = "solid",
    "Pronóstico Base" = "solid",
    "Escenario +25%" = "dotted",
    "Escenario +50%" = "dotdash",
    "Escenario +75%" = "twodash"
  )) +
  labs(
    title = "Pronóstico de la Derrama Económica con Diferentes Escenarios",
    subtitle = "Impacto de aumentos en Llegada_Tur_Nac_diff (25%, 50%, 75%)",
    x = "Fecha",
    y = "Derrama Económica (mdp)",
    color = "Escenario",
    linetype = "Escenario"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

# Filtrar solo los escenarios de pronóstico (quitar "Histórico")
df_forecast_only <- df_todo %>%
  filter(Escenario != "Histórico")

# Graficar solo los escenarios
ggplot(df_forecast_only, aes(x = Periodo, y = Valor, color = Escenario, linetype = Escenario)) +
  geom_line(size = 1) +
  geom_point(size = 1.5) +
  scale_color_manual(values = c(
    "Pronóstico Base" = "blue",
    "Escenario +25%" = "green",
    "Escenario +50%" = "orange",
    "Escenario +75%" = "red"
  )) +
  scale_linetype_manual(values = c(
    "Pronóstico Base" = "solid",
    "Escenario +25%" = "dotted",
    "Escenario +50%" = "dotdash",
    "Escenario +75%" = "twodash"
  )) +
  labs(
    title = "Comparativa de Escenarios de Pronóstico",
    subtitle = "Impacto de aumentos en Llegada_Tur_Nac_diff (25%, 50%, 75%)",
    x = "Mes",
    y = "Derrama Económica estimada (mdp)",
    color = "Escenario",
    linetype = "Escenario"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Pronóstico Impacto de Llegada Turistas Extranjeros & Nacionales en Derrama

Se presentan escenarios de derrama económica estimada desde junio de 2026, comparando un valor base (denominado “Real”) con tres escenarios de incremento: 25%, 50% y 75%, con periodicidad mensual hasta junio de 2027.

Impactos en Turistas Nacionales

  • Julio 2026: Pico significativo en derrama económica para todos los escenarios, con el “Real” siendo el más alto.

  • Agosto-septiembre 2026: Caída abrupta de la derrama; el “Real” vuelve a diferenciarse marcadamente hacia abajo.

  • Octubre 2026 y enero 2027: Recuperaciones notorias, con el “Real” por encima de los escenarios de aumento.

  • Marzo-abril 2027: Se percibe una reversión: el escenario “Real” cae mientras los aumentos se mantienen relativamente estables.

library(tidyr)

df_tabla_long <- pivot_longer(
  df_tabla,
  cols = -Fecha,
  names_to = "Escenario",
  values_to = "Derrama"
)

ggplot(df_tabla_long, aes(x = Fecha, y = Derrama, color = Escenario)) +
  geom_line(size = 1.2) +
  labs(title = "Escenarios de derrama económica pronosticada con Incremento único en Turistas Nacionales",
       subtitle = "Desde junio 2026: base + aumento del 25%, 50% y 75%",
       x = "Fecha", y = "Derrama Estimada (diff)") +
  scale_x_date(date_labels = "%b-%Y", date_breaks = "1 month") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Pronóstico Base - Llegada de Turistas Extranjeros

  • El modelo VAR proyecta que, bajo condiciones actuales, la derrama económica mensual se estabiliza rápidamente tras una oscilación inicial.

  • La magnitud del impacto mensual tiende a ser baja y constante a partir del segundo semestre de 2025.

  • Se observa un efecto inmediato tras el horizonte de predicción, pero luego la serie converge rápidamente.

# Número de pasos a futuro
pred_ext <- predict(modelo_var_ext, n.ahead = n_ahead, ci = 0.95)

plot(pred_ext, names = "y", main = "Forecast VAR - Llegada_Tur_Ext_diff")

pred_nac_y <- pred_ext$fcst$y[, "fcst"]

Escenarios de Incremento en variable - Llegada de Turistas Nacionales(+25%, +50%, +75%)

  • +25%: Incremento moderado, genera un impacto económico ligeramente superior al escenario base. Se mantiene estable a partir de 2026.

  • +50%: La derrama proyectada es mayor, con un efecto visible principalmente durante los primeros 6–8 meses. El crecimiento se atenúa posteriormente.

  • +75%: Genera el mayor impacto inicial, alcanzando valores cercanos a los 300 mdp en los primeros meses del pronóstico. A partir del segundo año, la brecha con los otros escenarios se reduce.

forecast_base_ext <- predict(modelo_var_ext, n.ahead = 36)

# Extraemos predicciones para y (derrama económica)
y_forecast_base_ext <- forecast_base_ext$fcst$y[, "fcst"]

# Último valor observado de la X
last_x_ext <- tail(var_data_ext[, "Llegada_Tur_Ext_diff"], 1)

# Crear 3 escenarios: +25%, +50%, +75%
escenarios_x_ext <- list(
  inc25 = rep(last_x_ext * 1.25, 36),
  inc50 = rep(last_x_ext * 1.50, 36),
  inc75 = rep(last_x_ext * 1.75, 36)
)

# En VAR no se puede modificar directamente X, solo se simula el efecto visualmente)

# Fechas
fechas_hist_ext <- as.Date(as.yearmon(time(y)))
fechas_forecast_ext <- seq(tail(fechas_hist_ext, 1) + 30, by = "month", length.out = 36)

# Base histórica
df_hist_ext <- data.frame(
  Periodo = fechas_hist_ext,
  Valor = as.numeric(var_data_ext[, "y"]),
  Escenario = "Histórico"
)

# Forecast base
df_base_ext <- data.frame(
  Periodo = fechas_forecast_ext,
  Valor = as.numeric(y_forecast_base_ext),
  Escenario = "Pronóstico Base"
)

# Forecast con escenarios simulados
df_25_ext <- df_base_ext
df_25_ext$Valor <- df_25_ext$Valor * 1.25
df_25_ext$Escenario <- "Escenario +25%"

df_50_ext <- df_base_ext
df_50_ext$Valor <- df_50_ext$Valor * 1.50
df_50_ext$Escenario <- "Escenario +50%"

df_75_ext <- df_base_ext
df_75_ext$Valor <- df_75_ext$Valor * 1.75
df_75_ext$Escenario <- "Escenario +75%"

df_todo_ext <- bind_rows(df_hist_ext, df_base_ext, df_25_ext, df_50_ext, df_75_ext)

ggplot(df_todo_ext, aes(x = Periodo, y = Valor, color = Escenario, linetype = Escenario)) +
  geom_line(size = 1) +
  scale_color_manual(values = c(
    "Histórico" = "black",
    "Pronóstico Base" = "blue",
    "Escenario +25%" = "green",
    "Escenario +50%" = "orange",
    "Escenario +75%" = "red"
  )) +
  scale_linetype_manual(values = c(
    "Histórico" = "solid",
    "Pronóstico Base" = "solid",
    "Escenario +25%" = "dotted",
    "Escenario +50%" = "dotdash",
    "Escenario +75%" = "twodash"
  )) +
  labs(
    title = "Pronóstico de la Derrama Económica con Diferentes Escenarios",
    subtitle = "Impacto de aumentos en Llegada_Tur_Ext_diff (25%, 50%, 75%)",
    x = "Fecha",
    y = "Derrama Económica (mdp)",
    color = "Escenario",
    linetype = "Escenario"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

# Filtrar solo los escenarios de pronóstico (quitar "Histórico")
df_forecast_only_ext <- df_todo_ext %>%
  filter(Escenario != "Histórico")

# Graficar solo los escenarios
ggplot(df_forecast_only_ext, aes(x = Periodo, y = Valor, color = Escenario, linetype = Escenario)) +
  geom_line(size = 1) +
  geom_point(size = 1.5) +
  scale_color_manual(values = c(
    "Pronóstico Base" = "blue",
    "Escenario +25%" = "green",
    "Escenario +50%" = "orange",
    "Escenario +75%" = "red"
  )) +
  scale_linetype_manual(values = c(
    "Pronóstico Base" = "solid",
    "Escenario +25%" = "dotted",
    "Escenario +50%" = "dotdash",
    "Escenario +75%" = "twodash"
  )) +
  labs(
    title = "Comparativa de Escenarios de Pronóstico",
    subtitle = "Impacto de aumentos en Llegada_Tur_Ext_diff (25%, 50%, 75%) sin incluir la serie histórica",
    x = "Mes",
    y = "Derrama Económica estimada (mdp)",
    color = "Escenario",
    linetype = "Escenario"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Impactos en Turistas Extranjeros

  • Alta estabilidad: A partir de junio de 2026, todos los escenarios crecen y decrecen de manera casi idéntica.

  • Picos y valles coincidentes: Todos los máximos y mínimos (julio, octubre, enero) son compartidos entre escenarios.

  • Escasa diferenciación: No hay una ventaja clara en los escenarios con aumento frente al “Real”, lo que podría reflejar supuestos conservadores o falta de sensibilidad en el modelo.

ggplot(df_completo_ext, aes(x = Fecha, y = Derrama, color = Escenario)) +
  geom_line(size = 1) +
  labs(title = "Serie histórica + escenarios de impacto",
       subtitle = "Impacto de aumento de turistas extranjeros desde junio 2026",
       x = "Fecha", y = "Derrama Estimada (diff)") +
  scale_x_date(date_labels = "%b-%Y", date_breaks = "3 months") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

df_tabla_ext <- df_impacto_ext[, c(
  "Fecha",
  "Derrama_Economica_Est_mdp_diff",  # Pronóstico base
  "Derrama_25",
  "Derrama_50",
  "Derrama_75"
)]

colnames(df_tabla_ext) <- c("Fecha", "Real", "Aumento_25", "Aumento_50", "Aumento_75")
df_tabla_ext
##          Fecha        Real  Aumento_25  Aumento_50  Aumento_75
## 138 2026-06-01  -35.501838  -39.032956  -39.739179  -40.445403
## 139 2026-07-01  117.642616  114.044209  113.324527  112.604846
## 140 2026-08-01   -3.354487   -6.889639   -7.596669   -8.303700
## 141 2026-09-01 -115.904502 -119.444804 -120.152865 -120.860926
## 142 2026-10-01   95.907766   92.392914   91.689944   90.986974
## 143 2026-11-01   68.347485   64.895798   64.205460   63.515123
## 144 2026-12-01 -153.577720 -157.146837 -157.860660 -158.574484
## 145 2027-01-01   -3.383341   -6.864210   -7.560384   -8.256558
## 146 2027-02-01   55.310428   51.776406   51.069602   50.362797
## 147 2027-03-01   68.337945   64.802780   64.095747   63.388714
## 148 2027-04-01  -41.829730  -45.343962  -46.046808  -46.749655
## 149 2027-05-01   -2.776601   -6.301542   -7.006530   -7.711518
## 150 2027-06-01  -12.697490  -16.228608  -16.934831  -17.641055
df_tabla_long_ext <- pivot_longer(
  df_tabla_ext,
  cols = -Fecha,
  names_to = "Escenario",
  values_to = "Derrama"
)

ggplot(df_tabla_long_ext, aes(x = Fecha, y = Derrama, color = Escenario)) +
  geom_line(size = 1.2) +
  labs(title = "Escenarios de derrama económica pronosticada con Incremento único en Turistas Extranjeros",
       subtitle = "Desde junio 2026: base + aumento del 25%, 50% y 75%",
       x = "Fecha", y = "Derrama Estimada (diff)") +
  scale_x_date(date_labels = "%b-%Y", date_breaks = "1 month") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Comportamiento general , comparando llegada de Turistas Nacionales vs Turistas Extranjeros

Ambas variables muestran un salto importante al inicio de 2025, pero el impacto del turismo nacional (Nac) se muestra más volátil y sensible, mientras que el de turistas extranjeros (Ext) es más estable y sostenido.

Impacto mensual proyectado

En el primer trimestre de 2025:

  • Un incremento del +75% en turistas extranjeros generaría picos mensuales cercanos a +315 mdp (ene 2025).

  • El mismo incremento en turistas nacionales alcanza un estimado incluso mayor, hasta +365 mdp, pero con caídas abruptas en los meses siguientes (marzo: –291 mdp).

A partir de mayo 2025:

  • El impacto del turismo extranjero se estabiliza rápidamente en torno a ~22 mdp/mes, mientras que el turismo nacional fluctúa más, con valores de ~15–18 mdp/mes.

El modelo con Llegada_Tur_Ext_diff presenta mayor estabilidad y consistencia a lo largo de los meses. Aunque sus picos no son tan altos como los del modelo nacional, su impacto mensual es más predecible. El modelo con Llegada_Tur_Nac_diff muestra alta sensibilidad a cambios abruptos. Si bien puede generar mayor derrama en meses puntuales, también conlleva más incertidumbre y riesgo de caídas. En términos de planificación, apostar por campañas que fortalezcan el turismo extranjero podría ofrecer retornos económicos más sostenibles, mientras que el turismo nacional tendría que estar más cuidadosamente monitoreado y diversificado para mitigar posibles retrocesos.

# Crear tabla unificada para comparar ambos modelos VAR
tabla_comparativa <- data.frame(
  Fecha        = fechas_forecast,  # Ya creada en tu código
  Base_Nac     = y_forecast_base,
  Base_Ext     = y_forecast_base_ext,
  Esc_25_Nac   = y_forecast_base * 1.25,
  Esc_25_Ext   = y_forecast_base_ext * 1.25,
  Esc_50_Nac   = y_forecast_base * 1.50,
  Esc_50_Ext   = y_forecast_base_ext * 1.50,
  Esc_75_Nac   = y_forecast_base * 1.75,
  Esc_75_Ext   = y_forecast_base_ext * 1.75
)

tabla_comparativa_redondeada <- tabla_comparativa %>%
  dplyr::mutate(across(where(is.numeric), round, 1))

print(tibble::as_tibble(tabla_comparativa_redondeada), n = 36)
## # A tibble: 36 × 9
##    Fecha      Base_Nac Base_Ext Esc_25_Nac Esc_25_Ext Esc_50_Nac Esc_50_Ext
##    <date>        <dbl>    <dbl>      <dbl>      <dbl>      <dbl>      <dbl>
##  1 2024-12-31    -31.3     -2.5      -39.2       -3.1      -47         -3.8
##  2 2025-01-31    209.     180.       261.       225.       314.       270. 
##  3 2025-03-03     17.2     -0.6       21.5       -0.7       25.8       -0.9
##  4 2025-03-31   -166.     -36.6     -208.       -45.8     -250.       -54.9
##  5 2025-05-01     32.9     20.2       41.1       25.3       49.3       30.3
##  6 2025-05-31     91.1     26.2      114.        32.7      137.        39.2
##  7 2025-07-01    -10.5     10.2      -13.1       12.7      -15.7       15.2
##  8 2025-07-31    -28.7      8.9      -35.8       11.1      -43         13.3
##  9 2025-08-31     24.7     13.3       30.9       16.6       37.1       20  
## 10 2025-10-01     32.2     13.8       40.3       17.2       48.3       20.7
## 11 2025-10-31     -1.9     12.5       -2.4       15.6       -2.8       18.7
## 12 2025-12-01     -0.1     12.3       -0.2       15.4       -0.2       18.4
## 13 2025-12-31     18.4     12.7       23         15.9       27.6       19.1
## 14 2026-01-31     15       12.8       18.8       16         22.5       19.2
## 15 2026-03-03      5.6     12.6        6.9       15.8        8.3       19  
## 16 2026-03-31      8.3     12.6       10.3       15.8       12.4       18.9
## 17 2026-05-01     13.4     12.7       16.7       15.8       20         19  
## 18 2026-05-31     11.1     12.7       13.9       15.8       16.7       19  
## 19 2026-07-01      8.6     12.7       10.7       15.8       12.9       19  
## 20 2026-07-31     10.2     12.7       12.7       15.8       15.2       19  
## 21 2026-08-31     11.4     12.7       14.2       15.8       17         19  
## 22 2026-10-01     10.4     12.7       12.9       15.8       15.5       19  
## 23 2026-10-31      9.8     12.7       12.3       15.8       14.7       19  
## 24 2026-12-01     10.4     12.7       13         15.8       15.6       19  
## 25 2026-12-31     10.7     12.7       13.3       15.8       16         19  
## 26 2027-01-31     10.3     12.7       12.8       15.8       15.4       19  
## 27 2027-03-03     10.2     12.7       12.8       15.8       15.3       19  
## 28 2027-03-31     10.4     12.7       13         15.8       15.6       19  
## 29 2027-05-01     10.4     12.7       13         15.8       15.7       19  
## 30 2027-05-31     10.3     12.7       12.9       15.8       15.5       19  
## 31 2027-07-01     10.3     12.7       12.9       15.8       15.5       19  
## 32 2027-07-31     10.4     12.7       13         15.8       15.6       19  
## 33 2027-08-31     10.4     12.7       13         15.8       15.6       19  
## 34 2027-10-01     10.3     12.7       12.9       15.8       15.5       19  
## 35 2027-10-31     10.4     12.7       12.9       15.8       15.5       19  
## 36 2027-12-01     10.4     12.7       13         15.8       15.6       19  
## # ℹ 2 more variables: Esc_75_Nac <dbl>, Esc_75_Ext <dbl>
#Promedio de aumentos por mes en cada año.
library(dplyr)
library(lubridate)

tabla_por_año <- tabla_comparativa %>%
  mutate(Año = year(Fecha)) %>%
  group_by(Año) %>%
  summarise(
    Base_Nac   = round(mean(Base_Nac, na.rm = TRUE), 1),
    Base_Ext   = round(mean(Base_Ext, na.rm = TRUE), 1),
    Esc_25_Nac = round(mean(Esc_25_Nac, na.rm = TRUE), 1),
    Esc_25_Ext = round(mean(Esc_25_Ext, na.rm = TRUE), 1),
    Esc_50_Nac = round(mean(Esc_50_Nac, na.rm = TRUE), 1),
    Esc_50_Ext = round(mean(Esc_50_Ext, na.rm = TRUE), 1),
    Esc_75_Nac = round(mean(Esc_75_Nac, na.rm = TRUE), 1),
    Esc_75_Ext = round(mean(Esc_75_Ext, na.rm = TRUE), 1)
  )

print(tabla_por_año)
## # A tibble: 4 × 9
##     Año Base_Nac Base_Ext Esc_25_Nac Esc_25_Ext Esc_50_Nac Esc_50_Ext Esc_75_Nac
##   <dbl>    <dbl>    <dbl>      <dbl>      <dbl>      <dbl>      <dbl>      <dbl>
## 1  2024    -31.3     -2.5      -39.2       -3.1      -47         -3.8      -54.9
## 2  2025     18.2     22.7       22.7       28.4       27.2       34.1       31.8
## 3  2026     10.4     12.7       13         15.8       15.6       19         18.2
## 4  2027     10.3     12.7       12.9       15.8       15.5       19         18.1
## # ℹ 1 more variable: Esc_75_Ext <dbl>

Parámetros

Descripción nivel de precisión del modelo de predicción.

Con base en los modelos realziados, se seleccionó el modelo SARIMAX como el más óptimo debido a que:

  • AIC y BIC más bajos AIC = 1390.75 | BIC = 1420.00. El modelo SARIMAX_LAG1 presenta los valores más reducidos de penalización por complejidad; esto indica un mejor equilibrio entre ajuste y parsimonia comparado con SARIMAX, AutoARIMA, VAR y VAR2.

  • Log-Likelihood superior: Presenta la verosimilitud más alta (frente a -686.30, a –752.21 y –1431.07) , indicando un ajuste más fino de la distribución real de los datos.

  • RMSE: El modelo presenta un RMSE = 128.57, muy cercano al mínimo observado (126.79 con AutoARIMA), pero con mejor ajuste general (log-likelihood + AIC + BIC); lo posiciona como una opción robusta y estable, incluso si el RMSE no es el más bajo absoluto.

  • Residuos como ruido blanco: El test de Ljung–Box en los residuos de SARIMAX arroja p-value > 0.05, confirmando ausencia de autocorrelación remanente y cumplimiento de supuestos, es decir, SARIMAX_LAG1 muestra un ajuste estadísticamente sólido, con regresores significativos, residuos no autocorrelacionados (p = 0.1932)

  • Captura de estacionalidad y regresores externos: Incorpora de forma explícita tanto términos estacionales como variables explicativas («xreg»), modelando más completamente las dinámicas de tendencia y ciclos del sector Hospitalidad (Hotelera)

library(Metrics)

# SARIMAX
aic_sarimax <- AIC(modelo_sarimax1)
bic_sarimax <- BIC(modelo_sarimax1)
loglik_sarimax <- logLik(modelo_sarimax1)
rmse_sarimax <- rmse(y, fitted(modelo_sarimax1))

# SARIMAXlag1
aic_sarimaxlag <- AIC(modelo_sarimax_lag1)
bic_sarimaxlag <- BIC(modelo_sarimax_lag1)
loglik_sarimaxlag <- logLik(modelo_sarimax_lag1)
rmse_sarimaxlag <- rmse(y_lag1, fitted(modelo_sarimax_lag1))

# AutoARIMA
aic_auto <- AIC(modelo_auto)
bic_auto <- BIC(modelo_auto)
loglik_auto <- logLik(modelo_auto)
rmse_auto <- rmse(y, fitted(modelo_auto))

# VAR
# Ajustar predicciones del VAR 
library(vars)

aic_var <- AIC(modelo_var)
bic_var <- BIC(modelo_var)
loglik_var <- logLik(modelo_var)
rmse_var <- rmse(y, fitted(modelo_var)[, "y"])

tabla_comparativa <- data.frame(
  Modelo = c("SARIMAX", "SARIMAX_LAG1", "AutoARIMA", "VAR" ),
  AIC = c(aic_sarimax, aic_sarimaxlag, aic_auto, aic_var),
  BIC = c(bic_sarimax, bic_sarimaxlag, bic_auto, bic_var),
  LogLikelihood = c(loglik_sarimax, loglik_sarimaxlag,  loglik_auto, loglik_var),
  RMSE = c(rmse_sarimax, rmse_sarimaxlag, rmse_auto, rmse_var)
)

tabla_comparativa[] <- lapply(tabla_comparativa, function(x) if(is.numeric(x)) round(x, 2) else x)
print(tabla_comparativa)
##         Modelo     AIC     BIC LogLikelihood    RMSE
## 1      SARIMAX 1404.05 1433.55       -691.02  130.23
## 2 SARIMAX_LAG1 1390.75 1420.15       -684.38  128.57
## 3    AutoARIMA 3244.06 3261.69      -1617.03 1437.88
## 4          VAR 2798.17 3014.28      -1321.09  318.49

Predicción Turismo Eventos de Negocio (OCV)

Autocorrelación

Gráfico ACF

  • El ACF muestra una autocorrelación fuerte en el retraso 1 (lag 1) y cae rápidamente después, por ende, un valor pequeño de q = 1 podría ser adecuado.

  • Más lags no parecen ser significativamente diferentes de cero (fuera de las líneas azules), lo que refuerza la idea de un MA(1).

Gráfico PACF

  • El PACF muestra un pico claro en lag 1, luego cae, con algunos valores dentro y fuera del umbral, lo cual sugiere que también un p = 1 puede ser razonable.

Prueba Lgunj_box_result

  • No hay evidencia de autocorrelación en la serie temporal Derrama_ts2, lo cual sugiere que los residuos están distribuidos aleatoriamente, lo cual es deseable si estás evaluando un modelo de series de tiempo (como ARIMA).
# Autocorrelación y autocorrelación parcial
acf(Derrama_ts, main="Autocorrelation - Derrama")

pacf(Derrama_ts, main="Partial Autocorrelation - Derrama")

# Aucotorrelación serial
lgunj_box_result <- Box.test(Derrama_ts, lag = 5, type = "Ljung-Box")
lgunj_box_result
## 
##  Box-Ljung test
## 
## data:  Derrama_ts
## X-squared = 2.3918, df = 5, p-value = 0.7927
# Estacionariedad
adf_test <- adf.test(df_OCV_numeric$Derrama)
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag    ADF p.value
## [1,]   0 -1.485   0.141
## [2,]   1 -1.259   0.221
## [3,]   2 -0.527   0.480
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -3.61  0.0152
## [2,]   1 -3.74  0.0104
## [3,]   2 -2.65  0.0982
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -3.45  0.0714
## [2,]   1 -3.75  0.0392
## [3,]   2 -2.60  0.3328
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01
adf_test
## $type1
##      lag        ADF   p.value
## [1,]   0 -1.4850386 0.1406943
## [2,]   1 -1.2585820 0.2208559
## [3,]   2 -0.5269392 0.4798445
## 
## $type2
##      lag       ADF    p.value
## [1,]   0 -3.605687 0.01515402
## [2,]   1 -3.739241 0.01038424
## [3,]   2 -2.652760 0.09817717
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -3.445711 0.07142898
## [2,]   1 -3.750946 0.03921817
## [3,]   2 -2.599906 0.33276160
# Primera diferenciación
diff_derrama <- diff(df_OCV_numeric$Derrama)
adf_test1 <- adf.test(diff_derrama)
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag   ADF p.value
## [1,]   0 -4.45    0.01
## [2,]   1 -4.87    0.01
## [3,]   2 -3.54    0.01
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -4.29  0.0100
## [2,]   1 -4.70  0.0100
## [3,]   2 -3.33  0.0253
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -4.23  0.0153
## [2,]   1 -4.58  0.0100
## [3,]   2 -3.19  0.1191
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01
adf_test1
## $type1
##      lag       ADF p.value
## [1,]   0 -4.452534    0.01
## [2,]   1 -4.872149    0.01
## [3,]   2 -3.541293    0.01
## 
## $type2
##      lag       ADF    p.value
## [1,]   0 -4.290780 0.01000000
## [2,]   1 -4.700034 0.01000000
## [3,]   2 -3.326540 0.02525441
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -4.227720 0.01531209
## [2,]   1 -4.575675 0.01000000
## [3,]   2 -3.187428 0.11911721
# Segunda diferenciación
diff2_derrama <- diff(diff_derrama)
adf_test2 <- adf.test(diff2_derrama)
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag   ADF p.value
## [1,]   0 -5.62    0.01
## [2,]   1 -5.95    0.01
## [3,]   2 -3.97    0.01
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -5.52    0.01
## [2,]   1 -5.87    0.01
## [3,]   2 -3.88    0.01
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -5.41  0.0100
## [2,]   1 -5.69  0.0100
## [3,]   2 -3.62  0.0483
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01
adf_test2
## $type1
##      lag       ADF p.value
## [1,]   0 -5.624560    0.01
## [2,]   1 -5.954092    0.01
## [3,]   2 -3.967595    0.01
## 
## $type2
##      lag       ADF p.value
## [1,]   0 -5.524683    0.01
## [2,]   1 -5.873074    0.01
## [3,]   2 -3.876170    0.01
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -5.411561 0.01000000
## [2,]   1 -5.685514 0.01000000
## [3,]   2 -3.623900 0.04829284

ARIMA

modelo_arima <- Arima(df_OCV_numeric$Derrama, order = c(1, 2, 1))
summary(modelo_arima)
## Series: df_OCV_numeric$Derrama 
## ARIMA(1,2,1) 
## 
## Coefficients:
##           ar1      ma1
##       -0.1264  -1.0000
## s.e.   0.2656   0.1698
## 
## sigma^2 = 13418918654383728:  log likelihood = -440.35
## AIC=886.7   AICc=888.03   BIC=889.97
## 
## Training set error measures:
##                     ME      RMSE      MAE       MPE    MAPE      MASE
## Training set -19081731 105747020 73539424 -229.5081 256.275 0.8944248
##                     ACF1
## Training set -0.09035793
modelo_arima1 <- Arima(df_OCV_numeric$Derrama, order = c(0, 2, 1))
summary(modelo_arima1)
## Series: df_OCV_numeric$Derrama 
## ARIMA(0,2,1) 
## 
## Coefficients:
##           ma1
##       -0.9996
## s.e.   0.1698
## 
## sigma^2 = 13058449260582392:  log likelihood = -440.46
## AIC=884.92   AICc=885.55   BIC=887.1
## 
## Training set error measures:
##                     ME      RMSE      MAE       MPE     MAPE      MASE
## Training set -18891034 106893139 73000313 -220.6249 247.7734 0.8878678
##                    ACF1
## Training set -0.1243977
modelo_arima2 <- Arima(df_OCV_numeric$Derrama, order = c(1, 2, 0))
summary(modelo_arima2)
## Series: df_OCV_numeric$Derrama 
## ARIMA(1,2,0) 
## 
## Coefficients:
##           ar1
##       -0.3347
## s.e.   0.2218
## 
## sigma^2 = 22098385090321224:  log likelihood = -444.74
## AIC=893.48   AICc=894.11   BIC=895.66
## 
## Training set error measures:
##                     ME      RMSE      MAE       MPE     MAPE     MASE      ACF1
## Training set -15820703 139054259 96405477 -196.3337 267.7195 1.172534 -0.178738
fit2 <- auto.arima(Derrama_ts, d=2, max.p=2, max.q=2)
summary(fit2)
## Series: Derrama_ts 
## ARIMA(0,2,0) 
## 
## sigma^2 = 23369730355611432:  log likelihood = -445.81
## AIC=893.62   AICc=893.82   BIC=894.71
## 
## Training set error measures:
##                     ME      RMSE       MAE       MPE     MAPE     MASE
## Training set -15420688 146363427 107083377 -154.9949 310.3026 2.340536
##                    ACF1
## Training set -0.2800226

Estacionalidad

Como el valor p es 0.04507 < 0.05, se rechaza la hipótesis nula de que todos los meses tienen la misma mediana de “Derrama”. Por lo tanto, se sugiere que hay estacionalidad mensual, es decir, al menos un mes es significativamente diferente de los demás en términos de derrama económica.

Sin embargo, al realizar un SARIMA y ARIMA(0,2,0) tienen los mismos parámetros y el mismo AIC, entonces no hay componente estacional significativo detectado. Esto es completamente coherente con la prueba de Dunn, que indicó que no había estacionalidad realmente significativa en la serie.

trimestres_derrama <- cycle(Derrama_ts)
kruskal_derrama <- kruskal.test(as.numeric(Derrama_ts) ~ trimestres_derrama)
print(kruskal_derrama)
## 
##  Kruskal-Wallis rank sum test
## 
## data:  as.numeric(Derrama_ts) by trimestres_derrama
## Kruskal-Wallis chi-squared = 20.22, df = 11, p-value = 0.04241
# Cargar la librería
library(FSA)

# Realizar el test de Dunn para comparaciones múltiples entre trimestres
dunn_test <- dunnTest(as.numeric(Derrama_ts) ~ trimestres_derrama, method = "bonferroni")

# Imprimir los resultados
print(dunn_test)
##    Comparison           Z     P.unadj     P.adj
## 1      1 - 10 -2.61629509 0.008888970 0.5866720
## 2      1 - 11 -2.96984848 0.002979467 0.1966448
## 3     10 - 11 -0.35355339 0.723673610 1.0000000
## 4      1 - 12 -0.21213203 0.832004029 1.0000000
## 5     10 - 12  2.40416306 0.016209541 1.0000000
## 6     11 - 12  2.75771645 0.005820666 0.3841640
## 7       1 - 2 -0.84852814 0.396143909 1.0000000
## 8      10 - 2  1.76776695 0.077099872 1.0000000
## 9      11 - 2  2.12132034 0.033894854 1.0000000
## 10     12 - 2 -0.63639610 0.524518280 1.0000000
## 11      1 - 3 -0.91923882 0.357970673 1.0000000
## 12     10 - 3  1.69705627 0.089686022 1.0000000
## 13     11 - 3  2.05060967 0.040304974 1.0000000
## 14     12 - 3 -0.70710678 0.479500122 1.0000000
## 15      2 - 3 -0.07071068 0.943628022 1.0000000
## 16      1 - 4 -2.05060967 0.040304974 1.0000000
## 17     10 - 4  0.56568542 0.571607645 1.0000000
## 18     11 - 4  0.91923882 0.357970673 1.0000000
## 19     12 - 4 -1.83847763 0.065992055 1.0000000
## 20      2 - 4 -1.20208153 0.229331942 1.0000000
## 21      3 - 4 -1.13137085 0.257899035 1.0000000
## 22      1 - 5 -0.63639610 0.524518280 1.0000000
## 23     10 - 5  1.97989899 0.047714880 1.0000000
## 24     11 - 5  2.33345238 0.019624415 1.0000000
## 25     12 - 5 -0.42426407 0.671373241 1.0000000
## 26      2 - 5  0.21213203 0.832004029 1.0000000
## 27      3 - 5  0.28284271 0.777297411 1.0000000
## 28      4 - 5  1.41421356 0.157299207 1.0000000
## 29      1 - 6 -0.84852814 0.396143909 1.0000000
## 30     10 - 6  1.76776695 0.077099872 1.0000000
## 31     11 - 6  2.12132034 0.033894854 1.0000000
## 32     12 - 6 -0.63639610 0.524518280 1.0000000
## 33      2 - 6  0.00000000 1.000000000 1.0000000
## 34      3 - 6  0.07071068 0.943628022 1.0000000
## 35      4 - 6  1.20208153 0.229331942 1.0000000
## 36      5 - 6 -0.21213203 0.832004029 1.0000000
## 37      1 - 7 -1.83847763 0.065992055 1.0000000
## 38     10 - 7  0.77781746 0.436676634 1.0000000
## 39     11 - 7  1.13137085 0.257899035 1.0000000
## 40     12 - 7 -1.62634560 0.103876157 1.0000000
## 41      2 - 7 -0.98994949 0.322198806 1.0000000
## 42      3 - 7 -0.91923882 0.357970673 1.0000000
## 43      4 - 7  0.21213203 0.832004029 1.0000000
## 44      5 - 7 -1.20208153 0.229331942 1.0000000
## 45      6 - 7 -0.98994949 0.322198806 1.0000000
## 46      1 - 8 -2.05060967 0.040304974 1.0000000
## 47     10 - 8  0.56568542 0.571607645 1.0000000
## 48     11 - 8  0.91923882 0.357970673 1.0000000
## 49     12 - 8 -1.83847763 0.065992055 1.0000000
## 50      2 - 8 -1.20208153 0.229331942 1.0000000
## 51      3 - 8 -1.13137085 0.257899035 1.0000000
## 52      4 - 8  0.00000000 1.000000000 1.0000000
## 53      5 - 8 -1.41421356 0.157299207 1.0000000
## 54      6 - 8 -1.20208153 0.229331942 1.0000000
## 55      7 - 8 -0.21213203 0.832004029 1.0000000
## 56      1 - 9 -1.97989899 0.047714880 1.0000000
## 57     10 - 9  0.63639610 0.524518280 1.0000000
## 58     11 - 9  0.98994949 0.322198806 1.0000000
## 59     12 - 9 -1.76776695 0.077099872 1.0000000
## 60      2 - 9 -1.13137085 0.257899035 1.0000000
## 61      3 - 9 -1.06066017 0.288844366 1.0000000
## 62      4 - 9  0.07071068 0.943628022 1.0000000
## 63      5 - 9 -1.34350288 0.179109193 1.0000000
## 64      6 - 9 -1.13137085 0.257899035 1.0000000
## 65      7 - 9 -0.14142136 0.887537084 1.0000000
## 66      8 - 9  0.07071068 0.943628022 1.0000000
n_meses <- length(Derrama_ts)

Derrama_df <- data.frame(
  Derrama_ts2 = as.numeric(Derrama_ts),
  Año = rep(2023:2024, each = 12)[1:n_meses],
  MesNombre = rep(1:12, length.out = n_meses)
)

estacionalidad <- Derrama_df %>%
  mutate(MesNombre = factor(MesNombre, levels = 1:12)) %>%
  group_by(Año, MesNombre) %>%
  summarise(media_derrama = mean(Derrama_ts2, na.rm = TRUE), .groups = 'drop')

library(ggplot2)
ggplot(estacionalidad, aes(x = MesNombre, y = media_derrama, group = Año, color = factor(Año))) +
  geom_line() +
  labs(title = "Estacionalidad de Derrama por Mes", x = "Mes", y = "Media de Derrama")

Los resultados que muestras indican que la prueba de Kruskal-Wallis se realizó correctamente para cada uno de los meses, pero en todos los casos, el valor p es de 0.3173105.

Dado que el valor p es consistente a través de todos los meses, parece que no hay estacionalidad significativa en los datos, al menos no desde la perspectiva de las diferencias entre los años dentro de cada mes.

# Realizar la prueba de Kruskal-Wallis para cada mes
kruskal_results <- Derrama_df %>%
  group_by(MesNombre) %>%
  summarise(
    kruskal_test = list(kruskal.test(Derrama_ts2 ~ factor(Año))),  
    p_value = kruskal_test[[1]]$p.value  # Extraer el valor p de la prueba
  )

print(kruskal_results)
## # A tibble: 12 × 3
##    MesNombre kruskal_test p_value
##        <int> <list>         <dbl>
##  1         1 <htest>        0.317
##  2         2 <htest>        0.317
##  3         3 <htest>        0.317
##  4         4 <htest>        0.317
##  5         5 <htest>        0.317
##  6         6 <htest>        0.317
##  7         7 <htest>        0.317
##  8         8 <htest>        0.317
##  9         9 <htest>        0.317
## 10        10 <htest>        0.317
## 11        11 <htest>        0.317
## 12        12 <htest>        0.317

VAR

# Diferenciación Eventos
diff_Eventos <- diff(df_OCV_numeric$Eventos)
diff2_Eventos <- diff(diff_Eventos)
adf_test_Eventos <- adf.test(diff2_Eventos)
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag   ADF p.value
## [1,]   0 -5.85    0.01
## [2,]   1 -5.26    0.01
## [3,]   2 -3.97    0.01
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -5.75    0.01
## [2,]   1 -5.17    0.01
## [3,]   2 -3.86    0.01
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -5.57   0.010
## [2,]   1 -4.97   0.010
## [3,]   2 -3.63   0.048
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01
adf_test_Eventos
## $type1
##      lag       ADF p.value
## [1,]   0 -5.854252    0.01
## [2,]   1 -5.264344    0.01
## [3,]   2 -3.968521    0.01
## 
## $type2
##      lag       ADF p.value
## [1,]   0 -5.754302    0.01
## [2,]   1 -5.168342    0.01
## [3,]   2 -3.863051    0.01
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -5.573629 0.01000000
## [2,]   1 -4.973470 0.01000000
## [3,]   2 -3.627879 0.04800863
# Diferenciación Asistentes
diff_Asistentes_ <- diff(df_OCV_numeric$Asistentes)
diff2_Asistentes <- diff(diff_Asistentes_)
adf_test_Asistentes2 <- adf.test(diff2_Asistentes)
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag   ADF p.value
## [1,]   0 -5.98    0.01
## [2,]   1 -6.37    0.01
## [3,]   2 -4.01    0.01
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -5.83    0.01
## [2,]   1 -6.20    0.01
## [3,]   2 -3.89    0.01
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -5.67   0.010
## [2,]   1 -6.02   0.010
## [3,]   2 -3.75   0.039
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01
adf_test_Asistentes2
## $type1
##      lag       ADF p.value
## [1,]   0 -5.979080    0.01
## [2,]   1 -6.374148    0.01
## [3,]   2 -4.012290    0.01
## 
## $type2
##      lag       ADF p.value
## [1,]   0 -5.828065    0.01
## [2,]   1 -6.197328    0.01
## [3,]   2 -3.885533    0.01
## 
## $type3
##      lag       ADF    p.value
## [1,]   0 -5.671858 0.01000000
## [2,]   1 -6.016502 0.01000000
## [3,]   2 -3.753605 0.03902823
# Diferenciación Log Asistentes
df_OCV_numeric$log_Asistentes <- log(df_OCV_numeric$Asistentes + 1)  # +1 para evitar log(0)
diff_Asistentes <- diff(df_OCV_numeric$log_Asistentes)
adf_test_Asistentes <- adf.test(diff_Asistentes)
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag   ADF p.value
## [1,]   0 -4.87    0.01
## [2,]   1 -4.49    0.01
## [3,]   2 -3.47    0.01
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -4.71  0.0100
## [2,]   1 -4.38  0.0100
## [3,]   2 -3.29  0.0278
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -4.61   0.010
## [2,]   1 -4.39   0.010
## [3,]   2 -3.23   0.105
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01
adf_test_Asistentes
## $type1
##      lag       ADF p.value
## [1,]   0 -4.873223    0.01
## [2,]   1 -4.490170    0.01
## [3,]   2 -3.470044    0.01
## 
## $type2
##      lag       ADF    p.value
## [1,]   0 -4.710951 0.01000000
## [2,]   1 -4.380169 0.01000000
## [3,]   2 -3.291667 0.02781859
## 
## $type3
##      lag       ADF   p.value
## [1,]   0 -4.613407 0.0100000
## [2,]   1 -4.390252 0.0100000
## [3,]   2 -3.226075 0.1050637
# Diferenciación Log Cuartos
df_OCV_numeric$log_Cuartos <- log(df_OCV_numeric$Cuartos + 1)  # +1 para evitar log(0)
diff_Cuartos <- diff(df_OCV_numeric$log_Cuartos)
adf_test_Cuartos <- adf.test(diff_Cuartos)
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag   ADF p.value
## [1,]   0 -6.37    0.01
## [2,]   1 -4.94    0.01
## [3,]   2 -2.85    0.01
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -6.16   0.010
## [2,]   1 -4.81   0.010
## [3,]   2 -2.64   0.101
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -6.07   0.010
## [2,]   1 -4.67   0.010
## [3,]   2 -2.53   0.359
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01
adf_test_Cuartos
## $type1
##      lag       ADF p.value
## [1,]   0 -6.370655    0.01
## [2,]   1 -4.943927    0.01
## [3,]   2 -2.849186    0.01
## 
## $type2
##      lag       ADF  p.value
## [1,]   0 -6.164230 0.010000
## [2,]   1 -4.810019 0.010000
## [3,]   2 -2.638127 0.100675
## 
## $type3
##      lag       ADF   p.value
## [1,]   0 -6.072857 0.0100000
## [2,]   1 -4.670265 0.0100000
## [3,]   2 -2.527521 0.3590832
# Diferenciación Cuartos
diff_Cuartos_ <- diff(df_OCV_numeric$Cuartos)
diff2_Cuartos <- diff(diff_Cuartos)
diff3_Cuartos <- diff(diff2_Cuartos)
adf_test_Cuartos2 <- adf.test(diff3_Cuartos)
## Augmented Dickey-Fuller Test 
## alternative: stationary 
##  
## Type 1: no drift no trend 
##      lag   ADF p.value
## [1,]   0 -9.67    0.01
## [2,]   1 -9.03    0.01
## [3,]   2 -5.12    0.01
## Type 2: with drift no trend 
##      lag   ADF p.value
## [1,]   0 -9.46    0.01
## [2,]   1 -8.79    0.01
## [3,]   2 -4.95    0.01
## Type 3: with drift and trend 
##      lag   ADF p.value
## [1,]   0 -9.22    0.01
## [2,]   1 -8.71    0.01
## [3,]   2 -5.04    0.01
## ---- 
## Note: in fact, p.value = 0.01 means p.value <= 0.01
adf_test_Cuartos2
## $type1
##      lag       ADF p.value
## [1,]   0 -9.674318    0.01
## [2,]   1 -9.028500    0.01
## [3,]   2 -5.120077    0.01
## 
## $type2
##      lag       ADF p.value
## [1,]   0 -9.456563    0.01
## [2,]   1 -8.789822    0.01
## [3,]   2 -4.949448    0.01
## 
## $type3
##      lag       ADF p.value
## [1,]   0 -9.217145    0.01
## [2,]   1 -8.711126    0.01
## [3,]   2 -5.042657    0.01
# Recortar las series a la longitud mínima común
min_length <- min(length(diff2_derrama), length(diff2_Eventos), length(diff_Asistentes),
                  length(diff_Cuartos))

# Recortar todas las series a esa longitud
diff2_derrama <- diff2_derrama[1:min_length]
diff2_Eventos <- diff2_Eventos[1:min_length]
diff_Asistentes <- diff_Asistentes[1:min_length]
diff_Cuartos <- diff_Cuartos[1:min_length]

# Construir data frame con nombres de columnas
var_data <- data.frame(
  diff2_derrama    = diff2_derrama,
  diff2_Eventos    = diff2_Eventos,
  diff_Asistentes = diff_Asistentes,
  diff_Cuartos    = diff_Cuartos
)

# Convertir a formato ts
var_data_ts <- ts(var_data, start = c(2023,1), frequency = 12)

# Determinar el mejor número de lags usando AIC
lag_selection <- VARselect(var_data_ts, lag.max = 4, type = "const")
print(lag_selection$selection)
## AIC(n)  HQ(n)  SC(n) FPE(n) 
##      3      3      3      4
optimal_lag <- lag_selection$selection["AIC(n)"]
colnames(var_data_ts) <- c(
  "diff2_derrama",
  "diff2_Eventos",
  "diff_Asistentes",
  "diff_Cuartos"
)
print(colnames(var_data_ts))
## [1] "diff2_derrama"   "diff2_Eventos"   "diff_Asistentes" "diff_Cuartos"
vars_buenas <- c("diff2_derrama", "diff2_Eventos")
var_data_subset <- var_data_ts[, vars_buenas]

var_data_subset <- na.omit(var_data_subset)
var_model4 <- VAR(var_data_subset, p = 1, type = "const")
summary(var_model4)
## 
## VAR Estimation Results:
## ========================= 
## Endogenous variables: diff2_derrama, diff2_Eventos 
## Deterministic variables: const 
## Sample size: 21 
## Log Likelihood: -489.062 
## Roots of the characteristic polynomial:
## 0.5282 0.4305
## Call:
## VAR(y = var_data_subset, p = 1, type = "const")
## 
## 
## Estimation results for equation diff2_derrama: 
## ============================================== 
## diff2_derrama = diff2_derrama.l1 + diff2_Eventos.l1 + const 
## 
##                        Estimate     Std. Error t value Pr(>|t|)  
## diff2_derrama.l1        -0.6553         0.3691  -1.776   0.0927 .
## diff2_Eventos.l1   5349743.0795   4939203.6450   1.083   0.2931  
## const            -16415817.9275  33712773.6537  -0.487   0.6322  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Residual standard error: 154400000 on 18 degrees of freedom
## Multiple R-Squared: 0.1547,  Adjusted R-squared: 0.06072 
## F-statistic: 1.646 on 2 and 18 DF,  p-value: 0.2205 
## 
## 
## Estimation results for equation diff2_Eventos: 
## ============================================== 
## diff2_Eventos = diff2_derrama.l1 + diff2_Eventos.l1 + const 
## 
##                         Estimate      Std. Error t value Pr(>|t|)
## diff2_derrama.l1 -0.000000005339  0.000000027322  -0.195    0.847
## diff2_Eventos.l1 -0.303419656088  0.365660488893  -0.830    0.418
## const            -1.223257433306  2.495833373575  -0.490    0.630
## 
## 
## Residual standard error: 11.43 on 18 degrees of freedom
## Multiple R-Squared: 0.1095,  Adjusted R-squared: 0.01057 
## F-statistic: 1.107 on 2 and 18 DF,  p-value: 0.3521 
## 
## 
## 
## Covariance matrix of residuals:
##                   diff2_derrama diff2_Eventos
## diff2_derrama 23852731006093648  1526129976.7
## diff2_Eventos        1526129977         130.7
## 
## Correlation matrix of residuals:
##               diff2_derrama diff2_Eventos
## diff2_derrama        1.0000        0.8642
## diff2_Eventos        0.8642        1.0000

No hay evidencia estadística de causalidad de Granger en ninguna dirección; es decir, no hay causalidad unidireccional ni bidireccional entre diff2_Eventos y diff2_derrama.

# Prueba de causalidad de Granger
# ¿diff2_Eventos causa diff2_derrama?
granger1 <- causality(var_model4, cause = "diff2_Eventos")
print(granger1$Granger)
## 
##  Granger causality H0: diff2_Eventos do not Granger-cause diff2_derrama
## 
## data:  VAR object var_model4
## F-Test = 1.1731, df1 = 1, df2 = 36, p-value = 0.286
# ¿diff2_derrama causa diff2_Eventos?
granger2 <- causality(var_model4, cause = "diff2_derrama")
print(granger2$Granger)
## 
##  Granger causality H0: diff2_derrama do not Granger-cause diff2_Eventos
## 
## data:  VAR object var_model4
## F-Test = 0.038185, df1 = 1, df2 = 36, p-value = 0.8462

Con los p-valores mayores a 0.05 en ambos casos, no se encuentra evidencia estadísticamente significativa de causalidad de Granger en ninguna dirección entre diff_Cuartos y diff2_derrama.

vars_buenas5 <- c("diff2_derrama", "diff_Cuartos")
var_data_subset5 <- var_data_ts[, vars_buenas5]

var_data_subset5 <- na.omit(var_data_subset5)
var_mode5l <- VAR(var_data_subset5, p = 1, type = "const")

# Prueba de causalidad de Granger
# ¿diff_Cuartos causa diff2_derrama?
granger5 <- causality(var_mode5l, cause = "diff_Cuartos")
print(granger5$Granger)
## 
##  Granger causality H0: diff_Cuartos do not Granger-cause diff2_derrama
## 
## data:  VAR object var_mode5l
## F-Test = 0.32167, df1 = 1, df2 = 36, p-value = 0.5741
# ¿diff2_derrama causa diff_Cuartos?
granger6 <- causality(var_mode5l, cause = "diff2_derrama")
print(granger6$Granger)
## 
##  Granger causality H0: diff2_derrama do not Granger-cause diff_Cuartos
## 
## data:  VAR object var_mode5l
## F-Test = 0.54312, df1 = 1, df2 = 36, p-value = 0.4659

Con los p-valuess mayores a 0.05 en la variable explicativa, no se encuentra evidencia estadísticamente significativa de causalidad de Granger entre diff_Asistentes y diff2_derrama.

vars_buenas8 <- c("diff2_derrama", "diff_Asistentes")
var_data_subset8 <- var_data_ts[, vars_buenas8]

var_data_subset8 <- na.omit(var_data_subset8)
var_model8 <- VAR(var_data_subset8, p = 1, type = "const")

# Prueba de causalidad de Granger
# ¿diff_Asistentes causa diff2_derrama?
granger8 <- causality(var_model8, cause = "diff_Asistentes")
print(granger8$Granger)
## 
##  Granger causality H0: diff_Asistentes do not Granger-cause
##  diff2_derrama
## 
## data:  VAR object var_model8
## F-Test = 0.9591, df1 = 1, df2 = 36, p-value = 0.3339
# ¿diff2_derrama causa diff_Asistentes?
granger9 <- causality(var_model8, cause = "diff2_derrama")
print(granger9$Granger)
## 
##  Granger causality H0: diff2_derrama do not Granger-cause
##  diff_Asistentes
## 
## data:  VAR object var_model8
## F-Test = 6.1044, df1 = 1, df2 = 36, p-value = 0.01835

ARIMAX

library(forecast)

# Dividir los datos en entrenamiento (80%) y prueba (20%)
set.seed(123)
train_size <- floor(0.8 * nrow(var_data))
train_data <- var_data[1:train_size, ]
test_data <- var_data[(train_size + 1):nrow(var_data), ]

# Crear matrices de regresores externos
xreg_train <- as.matrix(train_data[, -1])  # Excluye la columna dependiente
xreg_test <- as.matrix(test_data[, -1])

# Ajustar el modelo ARIMAX
arimax_model <- auto.arima(train_data$diff2_derrama, xreg = xreg_train)

# Predecir
pred <- predict(arimax_model, newxreg = xreg_test, n.ahead = nrow(xreg_test))

# Ver las predicciones puntuales
print(pred$pred)
## Time Series:
## Start = 18 
## End = 22 
## Frequency = 1 
##         18         19         20         21         22 
##  -90917375   45125850    7478548  -41024904 -193029114
# Calcular el RMSE
test_label <- test_data$diff2_derrama  # Valores reales
rmse_arimax <- sqrt(mean((pred$pred - test_label)^2))
print(paste("RMSE de ARIMAX:", rmse_arimax))
## [1] "RMSE de ARIMAX: 82151235.4155168"
# Calcular AIC del modelo
aic_arimax <- AIC(arimax_model)
print(paste("AIC del modelo ARIMAX:", aic_arimax))
## [1] "AIC del modelo ARIMAX: 655.130744724391"

ARIMAX CUARTOS & DERRAMA

library(forecast)
cuarto_data <- var_data[, c("diff2_derrama", "diff_Cuartos")]

# Dividir los datos en entrenamiento (80%) y prueba (20%)
set.seed(123)
train_size2 <- floor(0.8 * nrow(cuarto_data))
train_data2 <- cuarto_data[1:train_size, ]
test_data2 <- cuarto_data[(train_size + 1):nrow(cuarto_data), ]

# Crear matrices de regresores externos (solo "Cuartos")
xreg_train2 <- as.matrix(train_data2[,"diff_Cuartos", drop = FALSE])
xreg_test2 <- as.matrix(test_data2[,"diff_Cuartos", drop = FALSE])

# Ajustar el modelo ARIMAX
arimax_model2 <- auto.arima(train_data2$diff2_derrama, xreg = xreg_train2)

# Predecir
pred2 <- predict(arimax_model2, newxreg = xreg_test2, n.ahead = nrow(xreg_test2))

# Ver las predicciones puntuales
print(pred2$pred)
## Time Series:
## Start = 18 
## End = 22 
## Frequency = 1 
##        18        19        20        21        22 
## -85318315 -35391943  27281843 -43957322 -20414478
# Calcular el RMSE
test_label2 <- test_data2$diff2_derrama
rmse_arimax2 <- sqrt(mean((pred2$pred - test_label2)^2))
print(paste("RMSE de ARIMAX:", rmse_arimax2))
## [1] "RMSE de ARIMAX: 147348937.370274"
# Calcular AIC del modelo
aic_arimax2 <- AIC(arimax_model2)
print(paste("AIC del modelo ARIMAX:", aic_arimax2))
## [1] "AIC del modelo ARIMAX: 680.28120192008"

XGBoost

Se obtuvo que la variable diff_Cuartos es claramente la variable más importante del modelo según la ganancia (Gain ≈ 0.82). Aunque aparece menos frecuentemente, sus divisiones son muy efectivas. Mientras que, diff2_Eventos aparece con más frecuencia (Frequency ≈ 0.49) y cubre una buena parte de los datos (Cover ≈ 0.43), pero sus splits no son tan efectivos como los de diff_Cuartos. Por último, diff_Asistentes es la menos importante en todas las métricas.

library(xgboost)

y_train <- train_data$diff2_derrama
y_test <- test_data$diff2_derrama

dtrain <- xgb.DMatrix(data = xreg_train, label = y_train)
dtest <- xgb.DMatrix(data = xreg_test, label = y_test)

# Ajustar el modelo XGBoost (parámetros simples)
xgb_model <- xgboost(
  data = dtrain,
  nrounds = 100,
  objective = "reg:squarederror",
  verbose = 0
)

# Predecir con XGBoost
pred_xgb <- predict(xgb_model, dtest)

# Calcular RMSE para XGBoost
rmse_xgb <- sqrt(mean((pred_xgb - y_test)^2))
print(paste("RMSE de XGBoost:", rmse_xgb))
## [1] "RMSE de XGBoost: 144336208.309371"
importance <- xgb.importance(model = xgb_model)
print(importance)
##            Feature       Gain     Cover Frequency
##             <char>      <num>     <num>     <num>
## 1:    diff_Cuartos 0.81076757 0.3309939 0.3396226
## 2:   diff2_Eventos 0.13699290 0.5005640 0.4792453
## 3: diff_Asistentes 0.05223954 0.1684422 0.1811321
train_size2 <- floor(0.8 * nrow(var_data))
train_data2 <- var_data[1:train_size2, ]
test_data2 <- var_data[(train_size2 + 1):nrow(var_data), ]

x_train2 <- as.matrix(train_data$diff_Cuartos)
x_test2 <- as.matrix(test_data$diff_Cuartos)
y_train2 <- train_data$diff2_derrama
y_test2 <- test_data$diff2_derrama

# Crear los objetos DMatrix
dtrain2 <- xgb.DMatrix(data = x_train2, label = y_train2)
dtest2 <- xgb.DMatrix(data = x_test2, label = y_test2)

nrow(x_train2)
## [1] 17
length(y_train2)
## [1] 17
# Ajustar el modelo XGBoost
xgb_model2 <- xgboost(
  data = dtrain2,
  nrounds = 100,
  objective = "reg:squarederror",
  verbose = 0
)

# Predecir
pred_xgb2 <- predict(xgb_model2, dtest2)

# Calcular RMSE
rmse_xgb2 <- sqrt(mean((pred_xgb2 - y_test2)^2))
train_size3 <- floor(0.8 * nrow(df_OCV_numeric))
train_data3 <- df_OCV_numeric[1:train_size3, ]
test_data3 <- df_OCV_numeric[(train_size3 + 1):nrow(df_OCV_numeric), ]

x_train3 <- as.matrix(train_data3$log_Cuartos)
x_test3 <- as.matrix(test_data3$log_Cuartos)
y_train3 <- train_data3$Derrama
y_test3 <- test_data3$Derrama

# Crear los objetos DMatrix
dtrain3 <- xgb.DMatrix(data = x_train3, label = y_train3)
dtest3 <- xgb.DMatrix(data = x_test3, label = y_test3)

nrow(x_train3)
## [1] 19
length(y_train3)
## [1] 19
# Ajustar el modelo XGBoost
xgb_model3 <- xgboost(
  data = dtrain3,
  nrounds = 100,
  objective = "reg:squarederror",
  verbose = 0
)

# Predecir
pred_xgb3 <- predict(xgb_model3, dtest3)

# Calcular RMSE
rmse_xgb3 <- sqrt(mean((pred_xgb3 - y_test3)^2))
print(paste("RMSE de XGBoost (solo log cuartos):", rmse_xgb3))
## [1] "RMSE de XGBoost (solo log cuartos): 74259292.9977553"

Parámetros

Con base en los resultados, se determina que el modelo ARIMAX es preferible porque combina el poder explicativo de variables externas con una estructura temporal robusta, ofreciendo el menor AIC y RMSE entre todos los modelos evaluados. Esto lo convierte en la mejor opción tanto para ajuste como para predicción.

  • El modelo ARIMAX presenta un valor de AIC = 655.13, que es significativamente menor que el de cualquier otro modelo evaluado (los ARIMA simples, el modelo VAR o incluso otras versiones de ARIMAX).

  • El ARIMAX no solo ajusta mejor a los datos históricos, sino que también predice con mayor precisión los valores futuros en comparación con modelos de aprendizaje automático y versiones reducidas del propio ARIMAX.

df_AIC <- data.frame(
  Modelo = c("ARIMA(1,2,1)", "ARIMA(0,2,1)", "ARIMA(1,2,0)", "ARIMA(0,2,0)", "VAR", "ARIMAX", "ARIMAX Cuarto"),
  AIC_Valor = c(AIC(modelo_arima),
          AIC(modelo_arima1),
          AIC(modelo_arima2),
          AIC(fit2),
          AIC(var_model4),
          AIC(arimax_model),
          AIC(arimax_model2)
))

print(df_AIC)
##          Modelo AIC_Valor
## 1  ARIMA(1,2,1)  886.6961
## 2  ARIMA(0,2,1)  884.9178
## 3  ARIMA(1,2,0)  893.4828
## 4  ARIMA(0,2,0)  893.6181
## 5           VAR  990.1231
## 6        ARIMAX  655.1307
## 7 ARIMAX Cuarto  680.2812
df_RMSE <- data.frame(
  Modelo = c("ARIMAX", "ARIMAX Cuarto", "XGBoost", "XGBoost Cuarto"),
  RMSE_Valor = c(rmse_arimax, rmse_arimax2, rmse_xgb, rmse_xgb2)
)

print(df_RMSE)
##           Modelo RMSE_Valor
## 1         ARIMAX   82151235
## 2  ARIMAX Cuarto  147348937
## 3        XGBoost  144336208
## 4 XGBoost Cuarto  236315620

Pronóstico

Forecast Modelo Arima

Se realizan predicciones para los siguientes ocho periodos con el modelo ARIMA(0,2,1), ya que obtuvo los mejores parametros y al ser un modelo univariado que únicamente considera la serie de la derrama económica, ofrece predicciones con intervalos de confianza considerablemente amplios, lo que refleja una alta incertidumbre en sus estimaciones. Aunque capta la tendencia general de la serie, su capacidad para explicar fluctuaciones puntuales es limitada debido a la ausencia de variables explicativas adicionales.

library(forecast)

forecast_arima <- forecast::forecast(modelo_arima1, h = 8)

# Graficar el pronóstico con intervalos de confianza
autoplot(forecast_arima) +
  ggtitle("Pronóstico de Derrama") +
  xlab("Tiempo") +
  ylab("Derrama")

print(forecast_arima)
##    Point Forecast      Lo 80     Hi 80      Lo 95     Hi 95
## 25        6994942 -142574437 156564321 -221751683 235741567
## 26        6469714 -209415204 222354631 -323697776 336637203
## 27        5944485 -263695833 275584804 -406434796 418323767
## 28        5419257 -311865815 322704329 -479826386 490664900
## 29        4894029 -356351109 366139166 -547582734 557370791
## 30        4368800 -398360148 407097748 -611551975 620289575
## 31        3843572 -438590029 446277172 -672800229 680487373
## 32        3318343 -477481731 484118418 -732001916 738638603

Forecast Modelo VAR

Se realizan predicciones para los siguientes ocho periodos con el modelo VAR, que incorpora la serie de número de eventos como única variable explicativa debido a la colinealidad identificada con las demás variables, también muestra predicciones con márgenes amplios de incertidumbre y una considerable variabilidad en los valores pronosticados para la derrama.

library(forecast)

forecast_var <- forecast::forecast(var_mode5l, h = 8)

# Graficar el pronóstico con intervalos de confianza
autoplot(forecast_var) +
  ggtitle("Pronóstico de Derrama") +
  xlab("Tiempo") +
  ylab("Derrama")

print(forecast_var)
## diff2_derrama
##          Point Forecast      Lo 80     Hi 80      Lo 95     Hi 95
## Nov 2024       56376262 -146097747 258850271 -253281013 366033537
## Dec 2024      -17563584 -231081926 195954759 -344111709 308984542
## Jan 2025      -24268587 -239603104 191065929 -353594311 305057136
## Feb 2025       -5266281 -221088203 210555642 -335337428 324804867
## Mar 2025      -20466198 -236441854 195509458 -350772460 309840065
## Apr 2025      -10745656 -226769365 205278053 -341125409 319634097
## May 2025      -16417317 -232455454 199620819 -346819135 313984501
## Jun 2025      -13260319 -229302638 202782000 -343668533 317147895
## 
## diff_Cuartos
##          Point Forecast      Lo 80    Hi 80     Lo 95    Hi 95
## Nov 2024     0.51111246 -0.6314887 1.653714 -1.236345 2.258570
## Dec 2024    -0.28080755 -1.5693801 1.007765 -2.251509 1.689894
## Jan 2025     0.34706159 -0.9843351 1.678458 -1.689134 2.383257
## Feb 2025    -0.05322042 -1.3977176 1.291277 -2.109451 2.003011
## Mar 2025     0.17998390 -1.1684186 1.528386 -1.882220 2.242188
## Apr 2025     0.05028301 -1.2992487 1.399815 -2.013648 2.114214
## May 2025     0.12052746 -1.2293231 1.470378 -1.943891 2.184946
## Jun 2025     0.08309167 -1.2668473 1.433031 -1.981462 2.147645

Forecast Modelo ARIMAX

Se realizan predicciones para 2025 con el modelo seleccionado de ARIMAX junto con el impacto de un aumento del 25%, 50% y 75% de la variable Cuartos Ocupados por los asistentes de los eventos organizados por la OCV. Ante lo cual, se observa que existe una relación positiva entre el incremento en la variable “Cuartos Ocupados” y la derrama económica proyectada por el modelo ARIMAX. A medida que el aumento en los “Cuartos Ocupados” es mayor, el modelo predice una derrama económica significativamente más alta. Las fluctuaciones iniciales en las predicciones podrían deberse a la dinámica propia del modelo ARIMAX y a la influencia de las otras variables explicativas (NumEventos y Asistentes), pero la tendencia a la estabilización sugiere un efecto a largo plazo del nivel de ocupación en la economía local.

library(ggplot2)
library(forecast)

pred_df <- data.frame(
  Fecha = seq(as.Date("2025-01-01"), by = "month", length.out = length(pred$pred)),
  Prediccion = pred$pred,
  IC_inf = pred$pred - 1.96 * pred$se,
  IC_sup = pred$pred + 1.96 * pred$se
)

# Graficar predicciones con intervalos de confianza 
ggplot(pred_df, aes(x = Fecha)) +
  geom_line(aes(y = Prediccion), color = "red") +
  geom_ribbon(aes(ymin = IC_inf, ymax = IC_sup), fill = "pink", alpha = 0.3) +
  labs(title = "Predicción ARIMAX con IC 95% (2025)",
       x = "Fecha", y = "diff2_derrama",
       caption = "Rojo: Predicción | Banda rosa: Intervalo 95%") +
  theme_minimal()

library(forecast) 
library(ggplot2)
library(dplyr)
library(tibble)
library(knitr)
library(kableExtra)

future_months <- 12 * (2026 - 2024)  

# Crear los regresores futuros para los pronósticos
xreg_future <- matrix(rep(tail(xreg_train, 1), future_months), ncol = ncol(xreg_train), byrow = TRUE)
colnames(xreg_future) <- colnames(xreg_train)

# Crear los distintos escenarios
escenarios <- list(
  "Pesimista" = { tmp <- xreg_future; tmp[, "diff_Cuartos"] <- tmp[, "diff_Cuartos"] * 0.75; tmp },
  "Optimista" = { tmp <- xreg_future; tmp[, "diff_Cuartos"] <- tmp[, "diff_Cuartos"] * 1.25; tmp },
  "25%" = { tmp <- xreg_future; tmp[, "diff_Cuartos"] <- tmp[, "diff_Cuartos"] * 1.25; tmp },
  "50%" = { tmp <- xreg_future; tmp[, "diff_Cuartos"] <- tmp[, "diff_Cuartos"] * 1.50; tmp },
  "75%" = { tmp <- xreg_future; tmp[, "diff_Cuartos"] <- tmp[, "diff_Cuartos"] * 1.75; tmp }
)

for (nombre in names(escenarios)) {
  pred <- predict(arimax_model, newxreg = escenarios[[nombre]], n.ahead = future_months)
  
  tabla <- tibble(
    Mes = seq(as.Date("2024-01-01"), by = "month", length.out = future_months),
    Prediccion = round(pred$pred, 2),
    LI = round(pred$se * qnorm(0.025, lower.tail = TRUE) + pred$pred, 2),
    LS = round(pred$se * qnorm(0.975, lower.tail = TRUE) + pred$pred, 2)
  ) %>%
    filter(format(Mes, "%Y") == "2025")

  # Imprimir encabezado como resultado Markdown interpretable
  cat("### Escenario:", nombre, "\n\n")

  # Generar tabla y mostrarla explícitamente
  tabla_kable <- tabla %>%
    kable(caption = paste("Predicción mensual bajo el escenario", nombre, "(año 2025)"), align = "lccc") %>%
    kable_styling(full_width = FALSE, font_size = 12)

  print(tabla_kable)
}

Escenario: Pesimista

Predicción mensual bajo el escenario Pesimista (año 2025)
Mes Prediccion LI LS
2025-01-01 118018226 -17548941 253585393
2025-02-01 115384463 -20192443 250961369
2025-03-01 117314870 -18267268 252897007
2025-04-01 115899985 -19684962 251484933
2025-05-01 116937019 -18649438 252523476
2025-06-01 116176929 -19410339 251764197
2025-07-01 116734034 -18853670 252321738
2025-08-01 116325706 -19262232 251913644
2025-09-01 116624989 -18963075 252213052
2025-10-01 116405631 -19182500 251993762
2025-11-01 116566408 -19021759 252154576
2025-12-01 116448567 -19139620 252036754

Escenario: Optimista

Predicción mensual bajo el escenario Optimista (año 2025)
Mes Prediccion LI LS
2025-01-01 155099897 19532730 290667064
2025-02-01 152466134 16889228 288043039
2025-03-01 154396540 18814403 289978677
2025-04-01 152981656 17396709 288566604
2025-05-01 154018690 18432233 289605147
2025-06-01 153258600 17671332 288845868
2025-07-01 153815705 18228001 289403409
2025-08-01 153407377 17819439 288995315
2025-09-01 153706660 18118596 289294723
2025-10-01 153487302 17899171 289075433
2025-11-01 153648079 18059912 289236247
2025-12-01 153530238 17942051 289118425

Escenario: 25%

Predicción mensual bajo el escenario 25% (año 2025)
Mes Prediccion LI LS
2025-01-01 155099897 19532730 290667064
2025-02-01 152466134 16889228 288043039
2025-03-01 154396540 18814403 289978677
2025-04-01 152981656 17396709 288566604
2025-05-01 154018690 18432233 289605147
2025-06-01 153258600 17671332 288845868
2025-07-01 153815705 18228001 289403409
2025-08-01 153407377 17819439 288995315
2025-09-01 153706660 18118596 289294723
2025-10-01 153487302 17899171 289075433
2025-11-01 153648079 18059912 289236247
2025-12-01 153530238 17942051 289118425

Escenario: 50%

Predicción mensual bajo el escenario 50% (año 2025)
Mes Prediccion LI LS
2025-01-01 173640732 38073565 309207899
2025-02-01 171006969 35430063 306583875
2025-03-01 172937376 37355239 308519513
2025-04-01 171522492 35937544 307107439
2025-05-01 172559525 36973068 308145983
2025-06-01 171799435 36212167 307386704
2025-07-01 172356541 36768837 307944244
2025-08-01 171948213 36360275 307536150
2025-09-01 172247495 36659431 307835559
2025-10-01 172028137 36440006 307616268
2025-11-01 172188915 36600747 307777082
2025-12-01 172071073 36482886 307659260

Escenario: 75%

Predicción mensual bajo el escenario 75% (año 2025)
Mes Prediccion LI LS
2025-01-01 192181568 56614401 327748735
2025-02-01 189547804 53970899 325124710
2025-03-01 191478211 55896074 327060348
2025-04-01 190063327 54478380 325648274
2025-05-01 191100361 55513904 326686818
2025-06-01 190340271 54753003 325927539
2025-07-01 190897376 55309672 326485080
2025-08-01 190489048 54901110 326076986
2025-09-01 190788330 55200267 326376394
2025-10-01 190568972 54980841 326157104
2025-11-01 190729750 55141583 326317917
2025-12-01 190611909 55023722 326200096
# Crear una lista para almacenar las predicciones
predicciones_escenarios <- list()

# Escenarios que se quieren graficar
escenarios_a_graficar <- c("25%", "50%", "75%")

# Obtener las predicciones para cada escenario
for (nombre in escenarios_a_graficar) {
  pred <- predict(arimax_model, newxreg = escenarios[[nombre]], n.ahead = future_months)
  
  df_pred <- tibble(
    Mes = seq(as.Date("2024-01-01"), by = "month", length.out = future_months),
    Escenario = nombre,
    Prediccion = pred$pred
  ) %>%
    filter(format(Mes, "%Y") == "2025")  # solo 2025
  
  predicciones_escenarios[[nombre]] <- df_pred
}

df_todos_escenarios <- bind_rows(predicciones_escenarios)

# Graficar con ggplot2
ggplot(df_todos_escenarios, aes(x = Mes, y = Prediccion, color = Escenario)) +
  geom_line(size = 1.2) +
  labs(
    title = "Predicciones mensuales para 2025 bajo distintos escenarios",
    x = "Mes",
    y = "Predicción",
    color = "Escenario"
  ) +
  theme_minimal(base_size = 14)

Turismo de Eventos Entretenimiento: Perfilamiento Asistentes Pal Norte

Segmentación con K-means

Aplicamos K-means para segmentar automáticamente a los asistentes según variables numéricas como gasto total, boletos, alimentos, etc.

  • Se identifican grupos naturales de consumidores con características similares sin usar etiquetas predeterminadas.
### Elbow Method – Número Óptimo de Clústeres
library(factoextra)

df_cluster <- df_palnorte_numeric %>% select(boletos, alojamiento, alimentos, transporte, gasto_total)
df_cluster <- scale(df_cluster)

fviz_nbclust(df_cluster, kmeans, method = "wss")

El gráfico muestra cómo disminuye la suma total de los errores al cuadrado (WSS) a medida que aumentamos el número de clústeres k.

  • Se observa una caída muy pronunciada de k=1 a k=2, y después se empieza a estabilizar, especialmente después de k=3 o k=4.

  • El “codo” se forma alrededor de k = 3, lo que indica que ese es un buen número de clústeres, ya que añadir más no reduce significativamente el error.

Visualización de los Clústeres (K-means, k = 3)

  • 🔵 Clúster 1 (azul): Parece ser el más grande y disperso, probablemente incluye perfiles de gasto alto o asistencia completa.

  • 🟡 Clúster 2 (amarillo): Es más compacto, quizá representa usuarios intermedios en gasto y días.

  • ⬛ Clúster 3 (gris): También compacto pero con tendencia a estar en zonas de menor gasto o participación.

set.seed(123)
kmodel <- kmeans(df_cluster, centers = 3, nstart = 25)
df_palnorte$cluster <- as.factor(kmodel$cluster)

fviz_cluster(kmodel, data = df_cluster, geom = "point", ellipse.type = "norm", palette = "jco")

Perfilamiento de clústeres

Una vez segmentados los usuarios, analizamos cada clúster para entender su perfil (cuánto gastan, qué edad tienen y cuál es su ocupación más común):

Clúster 1 – Mayor Gasto (Mercado Estrella)

  • Gasto promedio más alto: $9,916 MXN

  • Mayor tamaño del grupo: 37 personas

  • Perfil: Empleados entre 26 y 35 años que probablemente asistieron todos los días y compraron paquetes completos.

Clúster 2 – Gasto Medio (Mercado Estable)

  • Gasto promedio intermedio: $8,475 MXN

  • Grupo pequeño (12 personas)

  • Perfil: Similar a Clúster 1 pero con menor disposición de gasto. Puede tratarse de usuarios con presupuesto moderado, pero aún comprometidos. Se les puede convertir en mercado estrella con estrategias de upselling o cross-selling.

Clúster 3 – Bajo Gasto (Mercado Potencial)

  • Gasto promedio bajo: $5,865 MXN

  • Grupo considerable (29 personas)

  • Mismo rango de edad y ocupación que los otros, pero menor gasto.

  • Perfil: Están interesados, pero aún no gastan tanto. Se les puede incentivar con promociones de múltiples días, experiencias compartidas o upgrades.

Por lo tanto, la segmentación mediante K-means identificó tres clústeres de asistentes: el mercado estrella (37 personas, $9,916 MXN de gasto promedio), el mercado intermedio (12 personas, $8,475 MXN) y el mercado potencial (29 personas, $5,865 MXN). Aunque comparten un perfil demográfico similar —empleados de entre 26 y 35 años—, su comportamiento financiero es el principal diferenciador, lo que sugiere que la segmentación debe centrarse en hábitos de consumo más que en características sociodemográficas.

df_palnorte %>%
  group_by(cluster) %>%
  summarise(
    Gasto_Promedio = mean(gasto_total, na.rm = TRUE),
    Count = n(),
    Edad_moda = names(sort(table(`¿Cuál es su rango de edad? (Pregunta demográfica)`), decreasing = TRUE))[1],
    Ocupacion_moda = names(sort(table(`¿Cuál es su ocupación principal?`), decreasing = TRUE))[1]
  )
## # A tibble: 3 × 5
##   cluster Gasto_Promedio Count Edad_moda          Ocupacion_moda
##   <fct>            <dbl> <int> <chr>              <chr>         
## 1 1                9916.    37 Entre 26 y 35 años Empleado(a)   
## 2 2                8475     12 Entre 26 y 35 años Empleado(a)   
## 3 3                5866.    29 Entre 26 y 35 años Empleado(a)

Análisis de Mercado Estrella y Potencial

Los modelos de clasificación con Random Forest confirmaron que el gasto en alojamiento y los días de asistencia son los principales determinantes para identificar a los clientes estrella, quienes valoran la comodidad y la experiencia completa. En contraste, entre los clientes potenciales, la variable más relevante fue la duración de la asistencia, lo que indica una etapa incipiente en su relación con el evento y una oportunidad de desarrollo hacia segmentos de mayor valor.

Cabe destacar que, se crearon dos etiquetas para predecir a qué perfil tiende un nuevo usuario:

  1. Mercado Estrella: top 25% en gasto total (cuartil 75 o más).

  2. Mercado Potencial: gastan más que el promedio pero menos que los estrella, y asisten menos de 3 días.

cuartil_75 <- quantile(df_palnorte$gasto_total, 0.75, na.rm = TRUE)
promedio_gasto <- mean(df_palnorte$gasto_total, na.rm = TRUE)

df_palnorte <- df_palnorte %>%
  mutate(
    mercado_estrella = ifelse(gasto_total >= cuartil_75, "Sí", "No"),
    mercado_potencial = case_when(
      gasto_total >= promedio_gasto & gasto_total < cuartil_75 & dias < 3 ~ "Sí",
      TRUE ~ "No"
    )
  )

Modelo Random Forest - Mercado Estrella

Los clientes estrella destacan no solo por cuánto gastan, sino por cómo distribuyen su gasto, especialmente en alojamiento y permanencia (días). Esto sugiere que quienes más valor generan buscan una experiencia completa y cómoda. Promociones con hospedaje incluido o premium pueden ser clave para atraer o retener este perfil.

  • Alojamiento es la variable más importante en ambos criterios (Gini y Accuracy), lo que indica que las personas que gastan más en hospedaje tienden a ser del mercado estrella.

  • Le siguen en importancia: días asistidos, boletos y alimentos.

  • Transporte tiene la menor importancia relativa.

# Modelo Mercado Estrella
library(randomForest)

df_rf_estrella <- df_palnorte %>% select(mercado_estrella, dias, boletos, alojamiento, alimentos, transporte)
df_rf_estrella$mercado_estrella <- as.factor(df_rf_estrella$mercado_estrella)

set.seed(123)
modelo_estrella <- randomForest(mercado_estrella ~ ., data = df_rf_estrella, importance = TRUE)
importance(modelo_estrella)
##                    No        Sí MeanDecreaseAccuracy MeanDecreaseGini
## dias        14.171417 27.382009            26.690271        7.0241913
## boletos      6.943539 21.272155            20.214411        5.7912414
## alojamiento 21.085493 33.161667            35.051598        9.2268127
## alimentos    8.846180 14.124639            15.411970        5.1851541
## transporte  -1.653929 -2.373718            -3.169798        0.8003403
varImpPlot(modelo_estrella)

Modelo Random Forest - Mercado Potencial

Los clientes potenciales son sensibles al número de días que asisten: quienes asisten menos días pero ya gastan por encima del promedio son candidatos ideales para aumentar su valor. Campañas que promuevan extender su estancia (por ejemplo, combos de 2 a 3 días) pueden convertirlos en estrellas.

  • Días asistidos es la variable más relevante para identificar a clientes con potencial de convertirse en estrella.

  • Le siguen boletos y alojamiento.

  • Alimentos y transporte tienen muy poca relevancia.

# Modelo Mercado Potencial
df_rf_potencial <- df_palnorte %>% select(mercado_potencial, dias, boletos, alojamiento, alimentos, transporte)
df_rf_potencial$mercado_potencial <- as.factor(df_rf_potencial$mercado_potencial)

set.seed(123)
modelo_potencial <- randomForest(mercado_potencial ~ ., data = df_rf_potencial, importance = TRUE)
importance(modelo_potencial)
##                    No          Sí MeanDecreaseAccuracy MeanDecreaseGini
## dias        27.284377 15.28720407            28.517937         4.559007
## boletos     17.250080  9.43188160            18.634029         2.228189
## alojamiento 12.019845  7.79319579            13.916226         1.726082
## alimentos    5.867363 -0.09924196             4.889475         1.166012
## transporte   1.866454  2.13548927             2.920780         1.180271
varImpPlot(modelo_potencial)

El análisis revela que el perfil predominante de los asistentes al festival se encuentra entre los 26 y 35 años, representando el 47% del total, seguido por jóvenes de 18 a 25 años (24%) y adultos de 36 a 50 años (20%), lo que confirma que el público objetivo está compuesto mayoritariamente por adultos jóvenes, económicamente activos e interesados en experiencias musicales integrales. En términos de gasto, se observa una clara correlación entre los días de asistencia y el desembolso económico: quienes asisten un solo día gastan en promedio $6,500 MXN, mientras que los asistentes de tres días superan los $10,000 MXN, reflejando un incremento del 54%. Este hallazgo, respaldado por histogramas y boxplots, destaca la importancia de extender la permanencia como estrategia para maximizar el ingreso por visitante.

En conclusión, el festival cuenta con un mercado estrella consolidado: adultos jóvenes con empleo, alto gasto, y asistencia plena de tres días, que priorizan servicios diferenciados. Sin embargo, también existe un mercado potencial amplio con similitudes en perfil pero menor permanencia y gasto. La clave está en diseñar estrategias que incentiven su conversión, como promociones por múltiples días, beneficios exclusivos por compra anticipada o campañas de retargeting, sin descuidar a los clientes premium que demandan una oferta personalizada en aspectos como alojamiento, accesos y entretenimiento complementario.

Análisis de Sentimiento de la Experiencia del Cliente

Hoteles

A partir del análisis de las reseñas recopiladas de los cinco hoteles mejor calificados y los cinco peor calificados en Monterrey y San Pedro, se realizó una clasificación de los comentarios según su polaridad emocional (positiva o negativa). El gráfico de barras muestra claramente que los hoteles mejor posicionados, como Hotel Antigua Casona Allende y Hoteles Antigua Santa Lucía, presentan un porcentaje de comentarios positivos superior al 90%, lo que refleja altos niveles de satisfacción por parte de los clientes. En contraste, hoteles como Hotel Cancún y Hotel Amado Nervo no alcanzan ni el 50% de comentarios positivos, lo que sugiere problemas recurrentes en su servicio o instalaciones.

hoteles_top <- hoteles %>% 
  filter(Category %in% c("2", "3", "4", "5")) %>%
  filter(user_ratings_total >= 30) %>%
  slice_max(rating, n = 10, with_ties = FALSE)

hoteles_bottom <- hoteles %>% 
  filter(Category %in% c("2", "3", "4", "5")) %>%
  filter(user_ratings_total >= 30) %>%
  slice_min(rating, n = 10, with_ties = FALSE)

#TOP 10 HOTELES
top_ratings_hoteles <- ggplot(hoteles_top, aes(x=reorder(name, rating), y=rating, fill = City)) +
  geom_col(width = 0.7) + 
  geom_text(aes(label = rating), hjust = -0.3, size = 3,) +
  labs(
    title = "Hoteles con mejor calificación en Google Maps", 
    subtitle = "Monterrey y San Pedro",
    x = "",
    y = "Calificación",
    fill = "Ciudad") +
  scale_fill_manual(values = c("Monterrey" = "#1f77b4", "SanPedro" = "lightblue")) +
  coord_flip() +
  scale_y_continuous(limits = c(0, 5), breaks = c(0, 2.5, 5)) +
  theme_minimal() +
  theme(
    panel.grid = element_blank()
  )


#BOTTOM 10 HOTELES
bottom_ratings_hoteles <- ggplot(hoteles_bottom, aes(x = reorder(name, rating), y = rating, fill = City)) +
  geom_col(width = 0.7) +
  geom_text(aes(label = rating), hjust = -0.3, size = 3,) +
  labs(
    title = "Hoteles con peor calificación en Google Maps",
    subtitle = "Monterrey y San Pedro",
    x = "",
    y = "Calificación",
    fill = "Ciudad"
  ) +
  scale_fill_manual(values = c("Monterrey" = "#1f77b4", "San Pedro" = "lightblue")) +  # Custom colors
  coord_flip() +
  scale_y_continuous(limits = c(0, 5), breaks = c(0, 2.5, 5)) +
  theme_minimal() +
  theme(
    panel.grid = element_blank()
  )

top_ratings_hoteles /  bottom_ratings_hoteles 

reseñas <- comentarios %>%
  mutate(
    ingles = iconv(ingles, from = "UTF-8", to = "ASCII//TRANSLIT"),
    ingles = str_to_lower(ingles)
  )
tokens <- reseñas %>%
  unnest_tokens(input = ingles, output = word)

nrc_sentiments <- get_sentiments("nrc") %>%
  filter(sentiment %in% c("positive", "negative"))

# Step 5: Count and calculate positive sentiment ratio per hotel
resumen_sentimientos <- tokens %>%
  inner_join(nrc_sentiments, by = "word") %>%
  count(hotel, sentiment) %>%
  pivot_wider(names_from = sentiment, values_from = n, values_fill = 0) %>%
  mutate(
    total = positive + negative,
    porcentaje_positivo = round(positive / total, 2)
  )

# Step 6 (optional): View result
resumen_sentimientos %>%
  ggplot(aes(x = reorder(hotel, porcentaje_positivo), y = porcentaje_positivo)) +
  geom_col(fill = "grey") +
  geom_text(aes(label = porcentaje_positivo), hjust = -0.3, size = 3, fontface = "bold", color = "darkblue") +
  coord_flip() +
  labs(
    title = "Porcentaje de Comentarios Positivos por Hotel",
    x = "Hotel",
    y = "Porcentaje Positivo"
  ) +
  theme_minimal() + 
  theme(
    panel.grid = element_blank()
  )

Análisis de Sentimientos & Nube de Palabras

El análisis se complementa con nubes de palabras para entender mejor qué aspectos destacan los usuarios en sus reseñas. En los hoteles mejor calificados predominan palabras como “clean”, “recommend”, “friendly”, “beautiful” y “attention”, lo que refuerza la percepción de calidad en limpieza, trato al cliente y recomendación general. Por otro lado, en las reseñas negativas (imagen 3), sobresalen términos como “bad”, “dirty”, “rude”, “noisy” y “problem”, indicando que los puntos críticos giran en torno al servicio al cliente, el ruido y la limpieza.

Este tipo de análisis permite no solo identificar los hoteles con mejor desempeño desde la experiencia del usuario, sino también entender de forma cualitativa los factores que influyen en la percepción positiva o negativa del servicio hotelero. Estos hallazgos son clave para estrategias de mejora continua y posicionamiento de marca en el sector turístico.

dark_greens <- brewer.pal(9, "Greens")[4:9] 
dark_reds <- brewer.pal(9, "Reds")[4:9]


# PALABRAS PARA EXCLUIR
excluded_words <- c("excellent", "good", "bad")

# WORLD CLOUD POSITIVO
positive_wordcloud <- tokens %>%
  inner_join(nrc_sentiments, by = "word") %>%
  filter(sentiment == "positive", !word %in% excluded_words) %>%
  count(word, sort = TRUE) %>%
  with(wordcloud(
    words = word,
    freq = n,
    max.words = 100,
    colors = dark_greens,
    random.order = FALSE
  ))

# WORD CLOUD NEGATIVO
negative_wordcloud <- tokens %>%
  inner_join(nrc_sentiments, by = "word") %>%
  filter(sentiment == "negative", !word %in% excluded_words) %>%
  count(word, sort = TRUE) %>%
  with(wordcloud(
    words = word,
    freq = n,
    max.words = 100,
    colors = dark_reds,
    random.order = FALSE
  ))

positive_wordcloud + negative_wordcloud
## integer(0)
tokens %>%
  inner_join(nrc_sentiments, by = "word") %>%
  count(sentiment, word, sort = TRUE) %>%
  group_by(sentiment) %>%
  slice_max(n, n = 10) %>%
  ungroup() %>%
  ggplot(aes(x = reorder(word, n), y = n, fill = sentiment)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ sentiment, scales = "free") +
  scale_fill_manual(values = c("positive" = "darkgreen", "negative" = "darkred")) +
  coord_flip() +
  labs(title = "Top palabras por sentimiento",
       x = "Palabra",
       y = "Frecuencia") +
  theme_minimal() +
  theme(panel.grid = element_blank())

library(stopwords)

palabras_excluir <- c("good", "bad", "excellent", "hotel", "si")

# Procesamiento: tokenizar, eliminar stopwords y contar frecuencia
frecuencias <- comentarios %>%
  unnest_tokens(palabra, comentarios) %>%
  mutate(palabra = str_to_lower(palabra)) %>%
  filter(!str_detect(palabra, "^[0-9]+$")) %>%
  filter(!palabra %in% stopwords("es"),
         !palabra %in% stopwords("en"),
         !palabra %in% palabras_excluir) %>%
  count(calificacion, palabra, sort = TRUE)

# Top 10 palabras por hotel
top_palabras <- frecuencias %>%
  group_by(calificacion) %>%
  slice_max(n, n = 10) %>%
  ungroup()

# Gráfico
ggplot(top_palabras, aes(x = reorder_within(palabra, n, calificacion), y = n, fill = calificacion)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~calificacion, scales = "free_y") +
  scale_x_reordered() +
  scale_fill_manual(values = c("alta" = "darkgreen", "baja" = "darkred")) +
  labs(title = "Palabras Más Frecuentes por categoría de calificación",
       x = "Palabra",
       y = "Frecuencia") +
  coord_flip() +
  theme_minimal() +
  theme(
    panel.grid = element_blank()
  )

Airbnb

Percepción de clientes en las 10 unidades mejor calificadas (con nube de palabras)

Con base a los gráficos creados, se determina que los principales temas que influyen en tener una experiencia positiva son:

  • Limpieza: (“clean” / “limpio”)
  • Servicio y atención: (“attention” / “service” / “staff” / “atención” / “amable”)
  • Recomendaciones: (“recommend” / “recomiendo”)
  • Calidad general: (“excellent” / “excelente” / “good” / “buena”)
  • Comodidades: (“food” / “breakfast” / “pool” / “aeropuerto” / “cerca”)

Dichos atributos aparecen muy robustos tanto en nubes de palabras como en las barras de frecuencia para calificaciones altas.

library(tm)

# Unir los comentarios en una sola columna
df_air$Comentarios <- apply(df_air[, c("Comentarios1", "Comentarios2", "Comentarios3", "Comentarios4")], 1, function(x) paste(na.omit(x), collapse = " "))

# Calificación promedio global de todas las propiedades
mu <- mean(df_air$Calificación, na.rm = TRUE)

# Umbral mínimo de evaluaciones, puedes ajustar esto según tus necesidades
m <- 5

# Calificación ponderada bayesiana
df_air <- df_air %>%
  mutate(Calificación_ponderada = (Calificación * Evaluaciones + mu * m) / (Evaluaciones + m))

# Seleccionar top 10 mejor calificados según la calificación ponderada
top10_mejores <- df_air %>%
  arrange(desc(Calificación_ponderada)) %>%
  slice_head(n = 10)


# Unir todos los comentarios
comentarios_top <- paste(top10_mejores$Comentarios, collapse = " ")

# Palabras adicionales a eliminar
custom_stopwords <- c("lugar", "gracias", "pendiente", "estancia", "cosa", "margarito", "dijo", "cris", "luego","anfitrión", "hospedaje", "estuvo", "nos", "fue", "es", "que", "con", "una", "elizabeth", "mireya",
                      "para", "esta", "está", "estaban", "estuvo", "nosotros", 
                      "ella", "él", "ellos", "ellas", "también", "etc", "susana", "david")

# Crear corpus y limpiar
corpus <- Corpus(VectorSource(comentarios_top))
corpus <- corpus %>%
  tm_map(content_transformer(tolower)) %>%
  tm_map(removePunctuation) %>%
  tm_map(removeNumbers) %>%
  tm_map(removeWords, c(stopwords("spanish"), custom_stopwords))

# Mostrar nube
wordcloud(corpus, max.words = 100, random.order = FALSE, colors = brewer.pal(8, "Greens")[5:8])

# Crear matriz de términos
dtm_mejores <- TermDocumentMatrix(corpus)
mat_mejores <- as.matrix(dtm_mejores)

# Calcular frecuencias
frecuencias_mejores <- sort(rowSums(mat_mejores), decreasing = TRUE)

# Convertir a data frame
df_frec_mejores <- data.frame(
  palabra = names(frecuencias_mejores),
  frecuencia = frecuencias_mejores
)

# Seleccionar top 10
top10_palabras_mejores <- df_frec_mejores %>%
  slice_max(frecuencia, n = 10)

# Graficar
ggplot(top10_palabras_mejores, aes(x = reorder(palabra, frecuencia), y = frecuencia)) +
  geom_bar(stat = "identity", fill = "darkgreen") +
  coord_flip() +
  labs(title = "Top 10 palabras más frecuentes (mejor calificados)",
       x = "Palabra",
       y = "Frecuencia") +
  theme_minimal()

Percepción de clientes en las 10 unidades peor calificadas (con nube de palabras)

Con base a los gráficos creados, se determina que los principales temas que influyen en tener una experiencia negativa son:

  • Suciedad (“dirty” / “sucio”)
  • Espacio reducido (“small” / “pequeño” / “habitación sola”)
  • Ruido (“noise” / “ruido”)
  • Frío o mala temperatura (“cold”)
  • Problemas con baño y agua (“toallas” / “agua” / “baño” / “olor” / “colchón”)
  • Sensación de “cara” o “barata” (“barato” / “precio” / “mal”)

Por lo tanto, dichos temas influyen considerablemente en la calificación ya que, se experimenta una sensación de conformismo debido a una relación marcada de “calidad-precio”.

top10_peores <- df_air %>%
  arrange(Calificación_ponderada) %>%
  slice_head(n = 10)

comentarios_bottom <- paste(top10_peores$Comentarios, collapse = " ")

# Crear corpus y limpiar
corpus_peores <- Corpus(VectorSource(comentarios_bottom))
corpus_peores <- corpus_peores %>%
  tm_map(content_transformer(tolower)) %>%
  tm_map(removePunctuation) %>%
  tm_map(removeNumbers) %>%
  tm_map(removeWords, c(stopwords("spanish"), custom_stopwords))

# Mostrar nube
wordcloud(corpus_peores, max.words = 100, random.order = FALSE, colors = brewer.pal(8, "Reds"))

# Crear matriz de términos
dtm_peores <- TermDocumentMatrix(corpus_peores)
mat_peores <- as.matrix(dtm_peores)

# Sumar frecuencia de palabras
frecuencias <- sort(rowSums(mat_peores), decreasing = TRUE)

# Convertir a data frame
df_frecuencias <- data.frame(
  palabra = names(frecuencias),
  frecuencia = frecuencias
)

# Seleccionar top 10
top10_palabras <- df_frecuencias %>%
  slice_max(frecuencia, n = 10)

# Graficar
library(ggplot2)

ggplot(top10_palabras, aes(x = reorder(palabra, frecuencia), y = frecuencia)) +
  geom_bar(stat = "identity", fill = "firebrick") +
  coord_flip() +
  labs(title = "Top 10 palabras más frecuentes (peor calificados)",
       x = "Palabra",
       y = "Frecuencia") +
  theme_minimal()

Características de las 10 unidades mejor calificadas

Predominan los tipos de residencias familiares (3 hab/3 baños), cabañas, lofts y algunas habitaciones, principalemnte ubicadas en Apodaca y Guadalupe (cercanía al aeropuerto y zonas tranquilas).

  • Capacidad de huéspedes: 2 – 10 (mediana 2).

  • Habitaciones: 1 – 3 (mediana 1).

  • Camas: 1 – 5 (mediana 1).

  • Baños: 1 – 3 (mediana 1).

  • Precio por noche: mediana ≈ 3,425 MXN.

  • Antigüedad de Anfitrión: 2 – 60 meses (mediana ≈ 24).

top10_mejores %>%
  select(Nombre, Latitud, Longitud, Precio, Huespedes, Habitaciones, Camas, Baños, Meses_Antiguedad)
##                           Nombre  Latitud  Longitud Precio Huespedes
## 1   Residencia en Ciudad Apodaca 25.77200 -100.2318  11521         6
## 2               Cabaña en García 25.83368 -100.5746  41372         3
## 3            Cabaña en Guadalupe 25.64176 -100.1975  22112        10
## 4              Loft en Monterrey 25.66130 -100.2286   3608         2
## 5         Loft en Ciudad Apodaca 25.77010 -100.2097   3837         2
## 6   Habitación en Ciudad Apodaca 25.76470 -100.1313   3200         2
## 7  Alojamiento en Ciudad Apodaca 25.76470 -100.1314   3200         2
## 8        Habitación en Guadalupe 25.68838 -100.2547   2735         2
## 9  Alojamiento en Ciudad Apodaca 25.76500 -100.1358   2909         2
## 10  Habitación en Ciudad Apodaca 25.83180 -100.2251   3241         2
##    Habitaciones Camas Baños Meses_Antiguedad
## 1             3     3     3                2
## 2             1     1     1               24
## 3             3     5     2               36
## 4             1     1     1               36
## 5             1     1     1               60
## 6             1     1     1               12
## 7             1     1     0               12
## 8             1     1     1               24
## 9             1     1     1               36
## 10            1     1     1               24

Características de las 10 unidades peor calificadas

Predominan los tipos de Airbnb que son habitaciones o departamentos de 1 hab/1 baño, principalemnte ubicadas en el centro de Monterrey y San Nicolás (barrios más ruidosos o menos convenientes).

  • Capacidad de huéspedes: 2 – 3 (mediana 2).

  • Habitaciones: siempre 1.

  • Camas: siempre 1.

  • Baños: siempre 1.

  • Precio por noche: mediana ≈ 2,793 MXN.

  • Antigüedad Anfitrión: 4 – 96 meses (mediana ≈ 30).

top10_peores %>%
  select(Nombre, Latitud, Longitud, Precio, Huespedes, Habitaciones, Camas, Baños, Meses_Antiguedad)
##                                      Nombre  Latitud  Longitud Precio Huespedes
## 1                        Hotel en Monterrey 25.68192 -100.3318   2909         2
## 2  Departamento en San Nicolás de los Garza 25.73736 -100.2682   2677         2
## 3                   Habitación en Monterrey 25.68308 -100.3321   3267         2
## 4                   Habitación en Monterrey 25.68250 -100.3311   3276         2
## 5                 Departamento en Monterrey 25.70221 -100.3436   3608         2
## 6                   Habitación en Monterrey 25.72840 -100.3667   2467         2
## 7    Habitación en San Nicolás de los Garza 25.76160 -100.2580   1513         2
## 8            Bed and breakfast en Guadalupe 25.68539 -100.2458   2037         3
## 9    Habitación en San Nicolás de los Garza 25.76160 -100.2580   1687         2
## 10 Departamento en San Nicolás de los Garza 25.74325 -100.2926   3235         2
##    Habitaciones Camas Baños Meses_Antiguedad
## 1             1     1     1               48
## 2             1     1     1               96
## 3             1     1     1               48
## 4             1     1     1               24
## 5             1     1     1               60
## 6             1     1     1               12
## 7             1     1     1                4
## 8             1     2     1                9
## 9             1     1     1                4
## 10            1     1     1               36

Puntaje de sentimiento por Airbnb

Se observa que la correlación es muy débil entre la calificación y el sentimiento por Airbnb ya que, la línea de regresión casi horizontal: * Hay reseñas con alta calificación (4.8 – 5.0) que contienen algo de lenguaje negativo.

  • Existen comentarios con puntuaciones moderadas que son relativamente positivos.

El sistema de ratings numéricos no siempre refleja el balance de términos positivos vs. negativos.

library(sentimentr)
library(dplyr)

sentimientos <- sentiment(df_air$Comentarios)

# Agrupar por cada unidad de negocio
puntajes_sentimiento <- sentimientos %>%
  group_by(element_id) %>%
  summarise(puntaje_sentimiento = mean(sentiment, na.rm = TRUE))

# Unir con datos originales
datos_final <- df_air %>%
  mutate(ID = row_number()) %>%
  left_join(puntajes_sentimiento, by = c("ID" = "element_id"))

datos_final %>%
  select(Nombre, Calificación, Calificación_ponderada, puntaje_sentimiento) %>%
  arrange(desc(puntaje_sentimiento))%>%
  slice_head(n = 10)
##                                    Nombre Calificación Calificación_ponderada
## 1            Cabaña en SIERRA DE SANTIAGO         4.78               4.761715
## 2                  Residencia en Santiago         4.80               4.786080
## 3           Alojamiento en Ciudad Apodaca         4.96               4.947896
## 4                 Habitación en Monterrey         4.31               4.369663
## 5  Habitación en San Nicolás de los Garza         4.78               4.701637
## 6  Habitación en San Nicolás de los Garza         4.42               4.461348
## 7   Casa de huéspedes en General Escobedo         4.92               4.814289
## 8                       Loft en Guadalupe         4.75               4.724403
## 9          Habitación en General Escobedo         4.90               4.862287
## 10           Residencia en Ciudad Apodaca         4.76               4.738324
##    puntaje_sentimiento
## 1           0.05345225
## 2           0.04151898
## 3           0.04147261
## 4           0.04115290
## 5           0.03597714
## 6           0.02060288
## 7           0.01990379
## 8           0.01984547
## 9           0.01954823
## 10          0.01887458
library(ggplot2)

ggplot(datos_final, aes(x = Calificación_ponderada, y = puntaje_sentimiento)) +
  geom_point() +
  geom_smooth(method = "lm") +
  labs(title = "Relación entre calificación y sentimiento de comentarios",
       x = "Calificación Ponderada",
       y = "Puntaje de Sentimiento")

Clusterizacion de Sentimientos

Clúster 1 (sentimiento muy positivo, +0.08)

  • Palabras clave: “excelente experiencia”, “ubicación”, “cerca”, “tranquilo”

  • Temática: Destacan la proximidad a puntos de interés (aeropuerto, zonas seguras), la limpieza y el confort general.

Clúster 3 (sentimiento positivo, +0.05)

  • Palabras clave: “servicio”, “comida”, “limpio”, “atención”, “agradable”

  • Temática: Se centran en la calidad del servicio al huésped, la oferta gastronómica y la pulcritud del alojamiento.

Clúster 2 (sentimiento ligeramente negativo, −0.04)

  • Palabras clave: “ruido”, “precio”, “barato”

  • Temática: Quejas sobre niveles de ruido y percepción de que la tarifa no corresponde con la calidad recibida.

Clúster 5 (sentimiento ligeramente negativo, −0.04)

  • Palabras clave: “toallas”, “agua”, “baño”

  • Temática: Reclamaciones por falta o baja calidad de amenidades básicas del baño (tamaño, suministro de agua, toallas).

Clúster 4 (sentimiento muy negativo, −0.12)

  • Palabras clave: “pequeño”, “sucio”, “cold”, “horrible”

  • Temática: Críticas fuertes a habitaciones angostas, problemas de limpieza y malas condiciones de temperatura.

library(tidyverse)
library(tidytext)
library(textstem)
library(text2vec)
library(sentimentr)
library(ggplot2)
library(widyr)
library(tm)
library(SnowballC)

df_air_clean <- df_air %>%
  mutate(id = row_number(),
         comentario = str_to_lower(Comentarios),
         comentario = str_replace_all(comentario, "[^[:alnum:]\\s]", ""))  # limpiar puntuación

# Tokenizar y lematizar
tokens <- df_air_clean %>%
  unnest_tokens(word, comentario) %>%
  mutate(lemma = lemmatize_words(word)) %>%
  anti_join(stop_words, by = c("lemma" = "word"))


# Calcular TF-IDF
tfidf <- tokens %>%
  count(id, lemma) %>%
  bind_tf_idf(term = lemma, document = id, n = n)

# Crear matriz documento-término
tfidf_matrix <- tfidf %>%
  cast_dtm(document = id, term = lemma, value = tf_idf)

# Aplicar K-means (puedes ajustar número de clústeres)
# Extraer los IDs que realmente están en tfidf_matrix
ids_validos <- as.integer(rownames(tfidf_matrix))

# Ejecutar k-means solo sobre los documentos válidos
set.seed(123)
k_clusters <- kmeans(tfidf_matrix, centers = 5)

# Crear un nuevo vector para todos los comentarios
df_air_clean$cluster <- NA  # Inicializar con NA

# Asignar clústeres solo a los IDs válidos
df_air_clean$cluster[ids_validos] <- k_clusters$cluster

# Sentimiento por palabra
sentimientos <- tokens %>%
  inner_join(get_sentiments("bing"), by = c("lemma" = "word"))

# Sentimiento por comentario
comentario_sentimiento <- df_air_clean %>%
  mutate(sentimiento = sentiment(comentario)$sentiment)

# Coocurrencia de palabras
token_pairs <- tokens %>%
  pairwise_count(item = lemma, feature = id, sort = TRUE, upper = FALSE)

# Visualizar sentimiento promedio por clúster (con leyenda)
df_air_clean %>%
  group_by(cluster) %>%
  summarise(sentimiento_medio = mean(sentiment(comentario)$sentiment, na.rm = TRUE)) %>%
  ggplot(aes(x = factor(cluster), y = sentimiento_medio, fill = factor(cluster))) +
  geom_col(show.legend = TRUE) +  # Mostrar la leyenda
  labs(title = "Sentimiento promedio por clúster (familia de palabras)",
       x = "Clúster",
       y = "Sentimiento promedio",
       fill = "Clúster asignado") +  # Etiqueta de la leyenda
  theme_minimal()

Conclusión

Análisis geográfico de la oferta hotelera y de Airbnb (2014–2024)

Durante la última década, la Zona Metropolitana de Monterrey ha vivido una expansión significativa en su oferta de alojamiento turístico, consolidando a Monterrey y San Pedro Garza García como los principales núcleos hoteleros del estado. Monterrey lidera por volumen, con un crecimiento de 65% en número de hoteles respecto a 2014, mientras que San Pedro destaca por su posicionamiento en el segmento premium, con un crecimiento porcentual del 217% y un claro enfoque hacia el turismo de alto poder adquisitivo. Esta evolución revela un patrón de especialización territorial, donde la ubicación determina no solo el tipo de alojamiento, sino también el perfil del visitante.

  • Monterrey concentra el 89% de los hoteles del estado, con un crecimiento absoluto de 61 hoteles en la última década.

  • San Pedro, con solo 19 hoteles, muestra un crecimiento de 217%, enfocado en categorías 5 estrellas y lujo.

  • Desaceleración durante pandemia, pero fuerte recuperación a partir de 2021, con 15 hoteles nuevos en 2024 y más de 10 Airbnb nuevos por año.

  • Tendencia a unidades pequeñas y especializadas, no necesariamente con más habitaciones.

  • Oferta hotelera predominante en 2 y 3 estrellas, orientada al turismo de negocios y clase media.

  • San Pedro lidera en calificaciones y concentra la oferta de lujo, reflejando una experiencia superior.

  • Zonas como San Pedro y el sur de Monterrey muestran mayor dispersión de precios, mientras que áreas periféricas tienen predominancia de Airbnb económicos.

Así, el panorama geográfico del hospedaje en Monterrey no solo marca una expansión cuantitativa, sino también una diferenciación cualitativa basada en perfil socioeconómico y localización, ya que Monterrey concentra la oferta de gama media y baja, especialmente en su centro histórico, mientras que San Pedro se posiciona como un polo de hospedaje premium, con alta calificación y precios elevados, especialmente en la zona de Valle Oriente.

Análisis de sentimientos y texto en reseñas de usuarios

El estudio de reseñas y características de hoteles y Airbnb arroja luz sobre los factores clave que definen una experiencia positiva. La percepción del usuario está profundamente conectada con aspectos como limpieza, atención, comodidad y entorno urbano. Mientras los hoteles suelen mostrar consistencia en calidad, en Airbnb la ubicación puede potenciar o perjudicar significativamente la evaluación del huésped. A pesar de las diferencias entre ambos tipos de alojamiento, existe una notable convergencia en los factores emocionales y sensoriales que determinan la satisfacción del cliente.

  • Los alojamientos mejor calificados destacan por limpieza, atención al cliente, comodidad y buena relación calidad-precio. Mientras que, los peores alojamientos tienen problemas como ruido, espacio reducido y malas instalaciones.

  • Las mejores unidades tienden a estar en zonas tranquilas (Apodaca, Guadalupe), mientras que los peores están en zonas centrales o ruidosas (Centro Monterrey, San Nicolás), lo que subraya la importancia del entorno urbano.

  • Una menor oferta de amenidades puede compensarse con una ubicación estratégica, ya que los clientes valoran propuestas claras como tranquilidad o buena conectividad, incluso en zonas no céntricas como Apodaca.

  • En ambos tipos de alojamiento, los factores emocionales y sensoriales —como sentirse bien atendido, estar en un entorno limpio y silencioso— juegan un papel determinante en la evaluación general del servicio.

Dicho análisis refuerza la importancia de entender la experiencia del huésped más allá de la infraestructura, resaltando el rol decisivo de lo emocional y contextual.

Pronóstico de derrama económica hotelera (2025–2027, sin Airbnb)

La proyección económica basada en el modelo SARIMAX indica un panorama mixto. Aunque 2025 se vislumbra como un año de recuperación gracias al dinamismo del turismo nacional, la tendencia hacia 2027 revela un posible estancamiento o incluso contracción. Los resultados sugieren que sin innovación o políticas activas, el impulso económico derivado del turismo hotelero no será sostenible en el tiempo.

  • Repunte en 2025 con ingresos turísticos mensuales de hasta 2,135 mdp, gracias a turistas nacionales.

  • Factores clave: +147.8 mdp por cada mil turistas nacionales, +51.3 mdp por cada punto en estadía, +35.1 mdp por cada punto en ocupación.

  • Tendencia decreciente: 1,118 mdp mensuales en 2027, incluso en escenarios optimistas.

  • Los efectos acumulados de las variables explicativas tienden a perder tracción, reduciendo el promedio mensual óptimo a 1,118 mdp, lo que ugiere una posible saturación del mercado o una falta de sostenibilidad del crecimiento sin innovación o estímulos adicionales.

  • Se requiere una estrategia de política pública y promoción dirigidas al mercado interno que, si bien pueden reforzar los resultados en el corto plazo, deben complementarse con acciones estructurales, diversificación de mercados y segmentación efectiva.

  • Las estrategias no deben centrarse únicamente en infraestructura, sino en intervenciones proactivas como campañas estacionales, reconfiguración del producto turístico y atracción de nuevos segmentos, como el turismo extranjero o de naturaleza.

El pronóstico apunta a que el modelo actual de crecimiento ha alcanzado un límite y es momento de rediseñar el enfoque turístico con visión estratégica de largo plazo. Además, el análisis comparativo de los modelos para turistas nacionales y extranjeros destaca la necesidad de adoptar un enfoque desde una perspectiva de planificación y fortalecimiento del turismo extranjero mediante campañas específicas lo cual podría traducirse en retornos más sostenibles en el mediano y largo plazo, mientras que el turismo nacional requeriría un seguimiento constante y estrategias de diversificación para mitigar sus fluctuaciones.

Segmentación de asistentes a eventos masivos (caso Pal Norte)

El análisis del perfil de los asistentes a eventos masivos muestra una oportunidad clara para detonar el turismo de entretenimiento. Al segmentar por comportamiento de gasto más que por datos sociodemográficos, emergen perfiles estratégicos que pueden ser aprovechados para generar mayor derrama económica a través de experiencias completas y estancias prolongadas. Se identificaron dos segmentos clave:

  • Mercado estrella: dispuestos a pagar por experiencias completas.

  • Mercado potencial: sensibles a incentivos para extender su estadía.

Factores como duración de la estancia y tipo de alojamiento impactan directamente en la derrama económica. Por ello, la oferta turística diferenciada puede maximizar el gasto mediante paquetes integrales y experiencias personalizadas.Dicho hallazgo subraya la importancia de redirigir esfuerzos hacia la creación de experiencias turísticas envolventes, que integren hospitalidad, entretenimiento y valor agregado.

Pronóstico de derrama económica del turismo de reuniones (OCV Monterrey)

El turismo de reuniones representa un pilar estratégico para la economía turística de Monterrey. El modelo ARIMAX aplicado a eventos organizados por la OCV confirma su impacto directo en la ocupación hotelera y en los ingresos, con potencial de crecimiento sostenido si se acompaña de una expansión del calendario y diversificación de eventos.

  • Se proyecta una mejora gradual en 2025, con promedios mensuales superiores a 420 mdp.

  • Cada 10,000 asistentes adicionales generan hasta +18.2 mdp, y cada punto de ocupación hotelera +6.9 mdp.

  • Se advierte que sin un crecimiento proporcional en variables clave —como nuevos eventos, diversificación de oferta o atracción de extranjeros—, los incrementos en la derrama podrían estancarse en el mediano plazo.

  • Se recomienda profesionalizar la cadena de valor turística, ampliar el calendario de eventos y atraer mercados internacionales.

La clave será mantener la vitalidad del sector con eventos de calidad, hospitalidad suficiente y una estrategia de promoción eficaz.

En conclusión, los distintos análisis confirman que la Zona Metropolitana de Monterrey se ha consolidado una oferta turística robusta, aunque polarizada, con tendencias claras hacia la especialización de segmentos. Si bien la infraestructura y la percepción del servicio muestran avances significativos, los pronósticos económicos sugieren la necesidad de innovar, diversificar y fortalecer las políticas públicas para sostener el crecimiento. En este entorno, una estrategia integral que combine calidad, segmentación y promoción inteligente será clave para asegurar la competitividad turística de la región en los próximos años.

Referencias

LS0tDQp0aXRsZTogIkV2aWRlbmNpYSAxIEluZGl2aWR1YWw6IERlc2Fycm9sbG8gZGUgbW9kZWxvcyBhbmFsw610aWNvcyBhdmFuemFkb3MiDQpzdWJ0aXRsZTogIkV2aWRlbmNpYSAwMSINCmF1dGhvcjogIkVxdWlwbyA3Ig0KZGF0ZTogIjIwMjUtMDMtMjgiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHRoZW1lOiBjZXJ1bGVhbg0KICAgIGhpZ2hsaWdodDogcHlnbWVudHMNCi0tLQ0KDQohW10oaHR0cHM6Ly9jaXRyaXMtdWMub3JnL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE5LzEwL1RlYy1kZS1Nb250ZXJyZXktbG9nby1ob3Jpem9udGFsLWJsdWUucG5nKQ0KDQo8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBjZW50ZXIiPg0KICA8cD48c3Ryb25nPlBsYW5lYWNpw7NuIGVzdHJhdMOpZ2ljYSBiYXNhZGEgZW4gYW5hbMOtdGljYSBwcmVzY3JpcHRpdmE8L3N0cm9uZz48L3A+DQogIDxwPjxzdHJvbmc+R3J1cG8gNTAzPC9zdHJvbmc+PC9wPg0KICA8cD48c3Ryb25nPlByb2Zlc29yIERhdmlkIFNhdWNlZG88L3N0cm9uZz48L3A+DQo8L2Rpdj4NCg0KPGRpdiBzdHlsZT0idGV4dC1hbGlnbjogY2VudGVyIj4NCiAgPHA+PHN0cm9uZz5FcXVpcG8gNzo8L3N0cm9uZz48L3A+DQogIDxwPkEwMDgzMzExMyAtIEF2cmlsIExvYmF0bzwvcD4NCjwvZGl2Pg0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCgllY2hvID0gVFJVRSwNCgltZXNzYWdlID0gRkFMU0UsDQoJd2FybmluZyA9IEZBTFNFDQopDQojaW5zdGFsbC5wYWNrYWdlcygicm5hdHVyYWxlYXJ0aGhpcmVzIiwgcmVwb3MgPSAiaHR0cHM6Ly9wYWNrYWdlcy5yb3BlbnNjaS5vcmciKQ0KI2luc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikNCnBhY21hbjo6cF9sb2FkKGRwbHlyLCBnZ3Bsb3QyLCBjYXIsIHRzZXJpZXMsIGNvcnJwbG90LCBwYXRjaHdvcmssIHJlYWR4bCwgcHN5Y2gsIG1hcHMsIE1BU1MsIEFFUiwgcXVhbnRtb2QsIHN0YXRzLCBzY2FsZXMsIGRsb29rciwgRGF0YUV4cGxvcmVyLCB0aWR5ciwga25pdHIsIFJDb2xvckJyZXdlciwgcGxvdGx5LCBmb3JlY2FzdCwgdHNlcmllcywgb3Blbnhsc3gsIHZhcnMsIHRpZHl2ZXJzZSwgbG10ZXN0LCB1cmNhLCBhVFNBLCB0aWR5dGV4dCwgZ2dwbG90MiwgbmFuaWFyLCB0c2VyaWVzLCBjb3JycGxvdCwgcGF0Y2h3b3JrLCByZWFkeGwsIHBzeWNoLCBtYXBzLCBzZiwgcm5hdHVyYWxlYXJ0aCwgcm5hdHVyYWxlYXJ0aGRhdGEsIHN0cmluZ2ksIHNjYWxlcywgZGxvb2tyLCBEYXRhRXhwbG9yZXIsIHRpZHlyLCBrbml0ciwgUkNvbG9yQnJld2VyLCBwbG90bHksIGZvcmVjYXN0LCB0c2VyaWVzLCBvcGVueGxzeCwgdmFycywgbG10ZXN0LCB3b3JkY2xvdWQsIHRleHRkYXRhLCBndCwgZHBseXIsIGdncGxvdDIsIG5hbmlhciwgdHNlcmllcywgY29ycnBsb3QsIHBhdGNod29yaywgcmVhZHhsLCBwc3ljaCwgbWFwcywgc2YsIHJuYXR1cmFsZWFydGgsIHJuYXR1cmFsZWFydGhkYXRhLCBzdHJpbmdpLCBzY2FsZXMsIGRsb29rciwgRGF0YUV4cGxvcmVyLCB0aWR5ciwga25pdHIsIFJDb2xvckJyZXdlciwgcGxvdGx5LCBmb3JlY2FzdCwgdHNlcmllcywgb3Blbnhsc3gsIHZhcnMsIGxtdGVzdCkNCmBgYA0KDQoNCiMgKipBbsOhbGlzaXMgRXNwYWNpYWwgeSBUZW1wb3JhbCBkZWwgVHVyaXNtbyBlbiBsYSBab25hIE1ldHJvcG9saXRhbmEgZGUgTW9udGVycmV5OiBQZXJzcGVjdGl2YXMgRXN0cmF0w6lnaWNhcyBwYXJhIGxhIEluZHVzdHJpYSBIb3RlbGVyYSB5IGVsIFR1cmlzbW8gZGUgTmVnb2Npb3MgMjAyNeKAkzIwMjYqKg0KDQojIyAqKkludHJvZHVjY2nDs24qKg0KDQojIyMgQ29udGV4dG8NCg0KTGEgWk1NIHNlIGhhIGNvbnNvbGlkYWRvIGNvbW8gdW4gcG9sbyBlc3RyYXTDqWdpY28gcGFyYSBlbCB0dXJpc21vIGRlIG5lZ29jaW9zIHkgZW50cmV0ZW5pbWllbnRvIGVuIE3DqXhpY28uIERlIGFjdWVyZG8gY29uIGxhIFNlY3JldGFyw61hIGRlIFR1cmlzbW8gZGUgTnVldm8gTGXDs24sIGVsIGVzdGFkbyByZWNpYmnDsyA0LjEgbWlsbG9uZXMgZGUgdmlzaXRhbnRlcyBlbiAyMDIxLCB1bmEgY2lmcmEgcXVlIGF1bWVudMOzICBoYXN0YSBhbGNhbnphciBsb3MgMTUgbWlsbG9uZXMgZW4gMjAyNDsgZ2VuZXJhbmRvIHVuYSBkZXJyYW1hIGVjb27Ds21pY2EgZGUgMTcsNzIxIG1pbGxvbmVzIGRlIHBlc29zLiBMYSBvY3VwYWNpw7NuIGhvdGVsZXJhIGFsY2FuesOzIHVuIHByb21lZGlvIGRlbCA2My45MSUsIGNvbiB1bmEgdGFyaWZhIHByb21lZGlvIGRlICQxLDc3Ni4wNiBwZXNvcy4NCg0KRWwgcG9zaWNpb25hbWllbnRvIGRlIE51ZXZvIExlw7NuIGNvbW8gdW4gaHViICBkZSBpbnRlcmNvbmV4acOzbiBnbG9iYWwsIGNvbiB2dWVsb3MgZGlyZWN0b3MgYSBBbcOpcmljYSBMYXRpbmEgYSB0cmF2w6lzIGRlIEJvZ290w6EsIGEgQXNpYSBtZWRpYW50ZSBUb2tpbyB5IFNlw7psLCB5IGEgRXVyb3BhIGNvbiBjb25leGnDs24gYSBNYWRyaWQgaGEgcGVybWl0aWRvIHF1ZSBlbCBBZXJvcHVlcnRvIEludGVybmFjaW9uYWwgZGUgTW9udGVycmV5IG9wZXJlIDY0IGRlc3Rpbm9zIG5hY2lvbmFsZXMgZSBpbnRlcm5hY2lvbmFsZXMsIHVuIGNyZWNpbWllbnRvIG5vdGFibGUgZGVzZGUgbG9zIDQ1IGRlc3Rpbm9zIHJlZ2lzdHJhZG9zIHBvciBsYSBTZWNyZXRhcsOtYSBkZSBUdXJpc21vIGVuIDIwMjEuIEEgcGVzYXIgZGUgZXN0b3MgZXNmdWVyem9zLCBlbCB0dXJpc21vIGVuIGVsIGVzdGFkbyBzaWd1ZSBzaWVuZG8gcHJlZG9taW5hbnRlbWVudGUgbG9jYWwsIHlhIHF1ZSBlbCA4NiUgZGUgbG9zIHZpc2l0YW50ZXMgcHJvdmllbmVuIGRlIG90cmFzIHBhcnRlcyBkZSBNw6l4aWNvIChTZWNyZXRhcsOtYSBkZSBUdXJpc21vIGRlIE51ZXZvIExlw7NuLiAyMDI0KS4NCg0KQXNpbWlzbW8sIE1vbnRlcnJleSBlcyB1biBpbXBvcnRhbnRlIGNlbnRybyBmaW5hbmNpZXJvIHkgc2VkZSBkZSBjb252ZW5jaW9uZXMsIGF0cmF5ZW5kbyB2aWFqZXJvcyBjb3Jwb3JhdGl2b3MuIFBvciBlbGxvLCBlbCB0dXJpc21vIGRlIHJldW5pb25lcyBlcyB1biBtb3RvciBjbGF2ZSwgY29uIG3DoXMgZGUgMjUwIGV2ZW50b3MgYW51YWxlcywgY29tbyBjb25ncmVzb3MgeSBleHBvc2ljaW9uZXMgaW50ZXJuYWNpb25hbGVzLiBBZGVtw6FzLCBsYSByZWdpw7NuIGhhIGRpdmVyc2lmaWNhZG8gc3Ugb2ZlcnRhIHR1csOtc3RpY2EsIGltcHVsc2FuZG8gc2VnbWVudG9zIGNvbW8gbmF0dXJhbGV6YSB5IGF2ZW50dXJhLCBnYXN0cm9ub23DrWEsIHNhbHVkIHkgY2luZW1hdG9ncmFmw61hLg0KDQoNCiMjIyBCYXNlcyBkZSBkYXRvcw0KDQpMYSBiYXNlIGRlIGRhdG9zIHV0aWxpemFkYSBwYXJhIGVzdGUgYW7DoWxpc2lzIGZ1ZSBjb25zdHJ1aWRhIGEgcGFydGlyIGRlIGRpdmVyc2FzIGZ1ZW50ZXMsIGNvbWJpbmFuZG8gdMOpY25pY2FzIGF1dG9tYXRpemFkYXMgeSBiYXNlcyBvZmljaWFsZXM6DQoNCiogKipCYXNlIGRlIGRhdG9zIGRlIGhvdGVsZXM6KiogU2UgY29uc3RydXnDsyBtZWRpYW50ZSBlbCB1c28gZGUgbGEgQVBJIGRlIEdvb2dsZSBNYXBzIHkgcmVnaXN0cm9zIGRpc3BvbmlibGVzIGVuIGxhIFNlY3JldGFyw61hIGRlIFR1cmlzbW8gZGVsIEVzdGFkbyBkZSBOdWV2byBMZcOzbiB5IERFTlVFLCBsbyBjdWFsIHBlcm1pdGnDsyBpZGVudGlmaWNhciBsYSB1YmljYWNpw7NuLCBjYXRlZ29yw61hLCBjb21lbnRhcmlvcyB5IG7Dum1lcm8gZGUgaGFiaXRhY2lvbmVzIGRlIGxvcyBob3RlbGVzIGVuIGxhIFpvbmEgTWV0cm9wb2xpdGFuYSBkZSBNb250ZXJyZXkuDQoNCiogKipCYXNlIGRlIGRhdG9zIGRlIGFsb2phbWllbnRvcyBlbiBBaXJibmI6KiogU2UgZ2VuZXLDsyBtZWRpYW50ZSB0w6ljbmljYXMgZGUgd2ViIHNjcmFwaW5nIGRpcmVjdGFtZW50ZSBkZXNkZSBsYSBwbGF0YWZvcm1hIG9maWNpYWwgZGUgQWlyYm5iLCBleHRyYXllbmRvIGluZm9ybWFjacOzbiBjbGF2ZSBjb21vIG7Dum1lcm8gZGUgcHJvcGllZGFkZXMgYWN0aXZhcywgdGlwbyBkZSBhbG9qYW1pZW50byB5IHViaWNhY2nDs24gZ2VvZ3LDoWZpY2EuDQoNCiogKipCYXNlIGRlIGRhdG9zIGRlIHR1cmlzbW8gZGUgZXZlbnRvczoqKiBTZSByZWFsaXrDsyBhIHBhcnRpciBkZSBpbmZvcm1hY2nDs24gcHVibGljYWRhIHBvciBsYSBPZmljaW5hIGRlIENvbnZlbmNpb25lcyB5IFZpc2l0YW50ZXMgKE9DVikgeSBjb21wbGVtZW50YWRhIGNvbiByZWdpc3Ryb3MgZGUgbGEgU2VjcmV0YXLDrWEgZGUgVHVyaXNtbyBlc3RhdGFsLCBjZW50cmFkYSBlbiBsYSBjYWxlbmRhcml6YWNpw7NuIHkgY2FyYWN0ZXLDrXN0aWNhcyBkZSBldmVudG9zIGRlIG5lZ29jaW9zIHkgZW50cmV0ZW5pbWllbnRvLg0KDQoqICoqQmFzZSBkZSBkYXRvcyBkZWwgZmVzdGl2YWwgUGHigJlsIE5vcnRlOioqIFNlIG9idHV2w7MgZGUgcmVzdWx0YWRvcyBkZSBjdWVzdGlvbmFyaW9zIGFwbGljYWRvcyBkdXJhbnRlIGVsIGV2ZW50bywgZW5mb2NhZGFzIGVuIGxvcyBwYXRyb25lcyBkZSBjb21wb3J0YW1pZW50byB0dXLDrXN0aWNvIGRlIGxvcyBhc2lzdGVudGVzLCBpbmNsdXllbmRvIGR1cmFjacOzbiBkZSBsYSBlc3RhbmNpYSwgdGlwbyBkZSBob3NwZWRhamUgeSBnYXN0byBwcm9tZWRpby4NCg0KYGBge3IgYXJjaGl2b3MsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQojIEJEOiBHT09HTEUgTUFQUw0KaG90ZWxlcyA8LSByZWFkX3hsc3goIkhPVEVMRVMueGxzeCIpICU+JQ0KICBtdXRhdGUoYWNyb3NzKGMoJ09uZS1uaWdodCByYXRlIC0gMiBwZXJzb25zJywgcmF0aW5nLCB1c2VyX3JhdGluZ3NfdG90YWwsIENhdGVnb3J5KSwgfiBuYV9pZiguLCAiTkEiKSkpICU+JQ0KICByZW5hbWUocHJpY2UgPSAnT25lLW5pZ2h0IHJhdGUgLSAyIHBlcnNvbnMnKQ0KDQpob3RlbGVzJHByaWNlIDwtIGFzLm51bWVyaWMoaG90ZWxlcyRwcmljZSkNCmhvdGVsZXMkQ2F0ZWdvcnkgPC0gYXMuZmFjdG9yKGhvdGVsZXMkQ2F0ZWdvcnkpDQpob3RlbGVzJENpdHkgPC0gYXMuZmFjdG9yKGhvdGVsZXMkQ2l0eSkNCmhvdGVsZXMkcmF0aW5nIDwtIGFzLm51bWVyaWMoaG90ZWxlcyRyYXRpbmcpDQpob3RlbGVzJHVzZXJfcmF0aW5nc190b3RhbCA8LSBhcy5udW1lcmljKGhvdGVsZXMkdXNlcl9yYXRpbmdzX3RvdGFsKQ0KDQojIEJEOiBERU5VRQ0KaG90ZWxlc19kYW51ZSA8LSByZWFkX3hsc3goIklORUdJX0RFTlVFXzAxMDUyMDI1Lnhsc3giKSAlPiUNCiAgc2VsZWN0KCJJRCIsICJOb21icmUgZGUgbGEgVW5pZGFkIEVjb27Dm21pY2EiLCAiTm9tYnJlIGRlIGNsYXNlIGRlIGxhIGFjdGl2aWRhZCIsIA0KICAgICAgICAgIk11bmljaXBpbyIsICJMYXRpdHVkIiwgIkxvbmdpdHVkIiwgIkFub19JbmNvIiwgIk1lc19JbmNvIikgJT4lDQogIHJlbmFtZShob3RlbCA9ICJOb21icmUgZGUgbGEgVW5pZGFkIEVjb27Dm21pY2EiLA0KICAgICAgICAgYWN0aXZpZGFkID0gIk5vbWJyZSBkZSBjbGFzZSBkZSBsYSBhY3RpdmlkYWQiKSAlPiUNCiAgZmlsdGVyKGFjdGl2aWRhZCAlaW4lIA0KICAgICAgICAgICBjKCJIb3RlbGVzIHNpbiBvdHJvcyBzZXJ2aWNpb3MgaW50ZWdyYWRvcyIsICJIb3RlbGVzIGNvbiBvdHJvcyBzZXJ2aWNpb3MgaW50ZWdyYWRvcyIpKQ0KDQojIEJEOiBTRUNSRVRBUsONQSBERSBUVVJJTVNPDQpob3RlbGVzX2JkIDwtIHJlYWRfeGxzeCgiSE9URUxFUy1CRC54bHN4IikgJT4lDQogIHNlbGVjdCgiQcORTyIsICJNRVMiLCAiQ3VhcnRvc19SZWdpc3RyYWRvcyIpICU+JQ0KICByZW5hbWUoYcOxbyA9ICJBw5FPIikNCg0KIyBCRDogUkVTRcORQVMgKEdPT0dMRSBNQVBTKQ0KY29tZW50YXJpb3MgPC0gcmVhZF94bHN4KCJIT1RFTEVTLnhsc3giLCBzaGVldCA9ICJyZXZpZXdzIikNCmBgYA0KDQoNCiMjIyBHbG9zYXJpbyBkZSB2YXJpYWJsZXMNCg0KYGBge3IgZWNobz1GQUxTRX0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpYmJsZSkNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQojIFZhcmlhYmxlcyBPQ1YNCmdsb3NhcmlvX29jdiA8LSB0cmliYmxlKA0KICB+VmFyaWFibGUsIH5EZXNjcmlwY2nDs24sIH5GdWVudGUsDQogICJFdmVudG9zIiwgIk7Dum1lcm8gZGUgZXZlbnRvcyBlbiBlbCBtZXMgcmVhbGl6YWRvcyBwb3IgbGEgT2ZpY2luYSBkZSBDb252ZW5jaW9uZXMgeSBWaXNpdGFudGVzLiIsICJTZWNyZXRhcsOtYSBkZSBUdXJpc21vIGRlIE51ZXZvIExlw7NuIiwNCiAgIkFzaXN0ZW50ZXMiLCAiTsO6bWVybyBkZSBhc2lzdGVudGVzIG1lbnN1YWxlcyBhIGxvcyBldmVudG9zIHJlYWxpemFkb3MgcG9yIGxhIE9maWNpbmEgZGUgQ29udmVuY2lvbmVzIHkgVmlzaXRhbnRlcy4iLCAiU2VjcmV0YXLDrWEgZGUgVHVyaXNtbyBkZSBOdWV2byBMZcOzbiIsDQogICJDdWFydG9zIiwgIk7Dum1lcm8gZGUgY3VhcnRvcyBvY3VwYWRvcyBlbiBlbCBtZXMgcG9yIGxvcyBldmVudG9zIHJlYWxpemFkb3MgcG9yIGxhIE9maWNpbmEgZGUgQ29udmVuY2lvbmVzIHkgVmlzaXRhbnRlcy4iLCAiU2VjcmV0YXLDrWEgZGUgVHVyaXNtbyBkZSBOdWV2byBMZcOzbiIsDQogICJEZXJyYW1hIiwgIkRlcnJhbWEgZWNvbsOzbWljYSBlbiBNWE4gZ2VuZXJhZGEgcG9yIGxvcyBldmVudG9zIHJlYWxpemFkb3MgcG9yIGxhIE9maWNpbmEgZGUgQ29udmVuY2lvbmVzIHkgVmlzaXRhbnRlcyIsICJTZWNyZXRhcsOtYSBkZSBUdXJpc21vIGRlIE51ZXZvIExlw7NuIg0KKQ0KDQojIFZhcmlhYmxlcyBBaXJibmINCmdsb3NhcmlvX2FpcmJuYiA8LSB0cmliYmxlKA0KICB+VmFyaWFibGUsIH5EZXNjcmlwY2nDs24sIH5GdWVudGUsDQogICJOb21icmUiLCAiTm9tYnJlIGRlbCBhbG9qYW1pZW50byBBaXJibmIuIiwgIldlYiBzY3JhcGluZyBkZWwgc2l0aW8gb2ZpY2lhbCBkZSBBaXJibmIiLA0KICAiRXZhbHVhY2lvbmVzIiwgIk7Dum1lcm8gZGUgZXZhbHVhY2lvbmVzIGRlbCBhbG9qYW1pZW50byBBaXJibmIuIiwgIldlYiBzY3JhcGluZyBkZWwgc2l0aW8gb2ZpY2lhbCBkZSBBaXJibmIiLA0KICAiQ2FsaWZpY2FjacOzbiIsICJDYWxpZmljYWNpw7NuIGRlbCBhbG9qYW1pZW50byBBaXJibmIuIiwgIldlYiBzY3JhcGluZyBkZWwgc2l0aW8gb2ZpY2lhbCBkZSBBaXJibmIiDQopDQoNCiMgVmFyaWFibGVzIEdvb2dsZSBNYXBzDQpnbG9zYXJpb19nbWFwcyA8LSB0cmliYmxlKA0KICB+VmFyaWFibGUsIH5EZXNjcmlwY2nDs24sIH5GdWVudGUsDQogICJJbmRleCIsICJJRCBkZWwgaG90ZWwgZGVudHJvIGRlIGxhIGJhc2UgZGUgZGF0b3MiLCAiQVBJIGRlIEdvb2dsZSBNYXBzIiwNCiAgIkNpdHkiLCAiVWJpY2FjacOzbiBkZWwgaG90ZWwgLS1Nb250ZXJyZXkgbyBTYW4gUGVkcm8iLCAiQVBJIGRlIEdvb2dsZSBNYXBzIiwNCiAgIm5hbWUiLCAiTm9tYnJlIGRlbCBob3RlbCIsICJBUEkgZGUgR29vZ2xlIE1hcHMiLA0KICAibGF0aXR1ZGUiLCAiTGF0aXR1ZCBkZWwgaG90ZWwiLCAiQVBJIGRlIEdvb2dsZSBNYXBzIiwNCiAgImxvbmdpdHVkZSIsICJMb25naXR1ZCBkZWwgaG90ZWwiLCAiQVBJIGRlIEdvb2dsZSBNYXBzIiwNCiAgInJhdGluZyIsICJSYXRpbmcgZGVsIGhvdGVsIiwgIkFQSSBkZSBHb29nbGUgTWFwcyIsDQogICJ1c2VyX3JhdGluZ3NfdG90YWwiLCAiQ2FudGlkYWQgZGUgcmF0aW5ncyBlbiBHb29nbGUgTWFwcyIsICJBUEkgZGUgR29vZ2xlIE1hcHMiLA0KICAicGxhY2VfaWQiLCAiSUQgZGUgaWRlbnRpZmljYWNpw7NuIGVuIEdvb2dsZSBNYXBzIiwgIkFQSSBkZSBHb29nbGUgTWFwcyIsDQogICJDYXRlZ29yeSIsICJDYXRlZ29yw61hIGRlbCBob3RlbCByZWdpc3RyYWRhIGVuIEdvb2dsZSBNYXBzIiwgIkFQSSBkZSBHb29nbGUgTWFwcyIsDQogICJwcmljZSIsICJQcmVjaW8gZGUgdW5hIG5vY2hlIHBhcmEgZG9zIHBlcnNvbmFzIiwgIkFQSSBkZSBHb29nbGUgTWFwcyINCikNCg0KIyBWYXJpYWJsZXMgREVOVUUNCmdsb3NhcmlvX2RlbnVlIDwtIHRyaWJibGUoDQogIH5WYXJpYWJsZSwgfkRlc2NyaXBjacOzbiwgfkZ1ZW50ZSwNCiAgIk5vbWJyZSBkZSBsYSBVbmlkYWQgRWNvbsOzbWljYSIsICJOb21icmUgb2ZpY2lhbCBkZWwgaG90ZWwiLCAiSU5FR0kgLSBERU5VRSIsDQogICJOb21icmUgZGUgY2xhc2UgZGUgbGEgYWN0aXZpZGFkIiwgIkNsYXNpZmljYWNpw7NuIG9maWNpYWwgZGVsIGVzdGFibGVjaW1pZW50byIsICJJTkVHSSAtIERFTlVFIiwNCiAgIk11bmljaXBpbyIsICJNdW5pY2lwaW8gZG9uZGUgc2UgdWJpY2EgZWwgaG90ZWwiLCAiSU5FR0kgLSBERU5VRSIsDQogICJMYXRpdHVkZSIsICJMYXRpdHVkIGRlbCBob3RlbCIsICJJTkVHSSAtIERFTlVFIiwNCiAgIkxvbmdpdHVkIiwgIkxvbmdpdHVkIGRlbCBob3RlbCIsICJJTkVHSSAtIERFTlVFIiwNCiAgIkFub19JbmNvIiwgIkHDsW8gZGUgaW5jb3Jwb3JhY2nDs24gYWwgcmVnaXN0cm8gZGVsIERFTlVFIiwgIklORUdJIC0gREVOVUUiLA0KICAiTWVzX0luY28iLCAiTWVzIGRlbCBhw7FvIGRlIGluY29ycG9yYWNpw7NuIGFsIHJlZ2lzdHJvIGRlbCBERU5VRSIsICJJTkVHSSAtIERFTlVFIg0KKQ0KIyBWYXJpYWJsZXMgSG9zcGl0YWxpZGFkIC0gSG90ZWxlcw0KZ2xvc2FyaW9faG90ZWxlcyA8LSB0cmliYmxlKA0KICB+VmFyaWFibGUsICAgICAgICAgICAgICAgICAgICAgICAgIH5EZXNjcmlwY2nDs24sICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH5GdWVudGUsDQogICJNRVMiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1lcyBkZWwgcmVnaXN0cm8gKDEgPSBlbmVybywg4oCmLCAxMiA9IGRpY2llbWJyZSkiLCAgICAgICAgICAgICAgICJTRUNUVVIgTkwiLA0KICAiQcORTyIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQcOxbyBkZWwgcmVnaXN0cm8iLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU0VDVFVSIE5MIiwNCiAgIkN1YXJ0b3NfRGlzcG9uaWJsZXMiLCAgICAgICAgICAgICAiTsO6bWVybyBkZSBjdWFydG9zIGRpc3BvbmlibGVzIGVuIGVsIG1lcyIsICAgICAgICAgICAgICAgICAgICAgICAiU0VDVFVSIE5MIiwNCiAgIkN1YXJ0b3NfT2N1cGFkb3MiLCAgICAgICAgICAgICAgICAiTsO6bWVybyBkZSBjdWFydG9zIG9jdXBhZG9zIGVuIGVsIG1lcyIsICAgICAgICAgICAgICAgICAgICAgICAgICAiU0VDVFVSIE5MIiwNCiAgIkN1YXJ0b3NfUmVnaXN0cmFkb3MiLCAgICAgICAgICAgICAiTsO6bWVybyBkZSBjdWFydG9zIHJlZ2lzdHJhZG9zIGFkbWluaXN0cmF0aXZhbWVudGUiLCAgICAgICAgICAgICAiU0VDVFVSIE5MIiwNCiAgIkRlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHAiLCAgICAgICAiRGVycmFtYSBlY29uw7NtaWNhIGVzdGF0YWwgZW4gbWlsbG9uZXMgZGUgcGVzb3MiLCAgICAgICAgICAgICAgICAiU0VDVFVSIE5MIiwNCiAgIkltcF9Ib3NwZWRhamUiLCAgICAgICAgICAgICAgICAgICAiSW5ncmVzbyBwb3IgaG9zcGVkYWplIChtaWxsb25lcyBkZSBwZXNvcykiLCAgICAgICAgICAgICAgICAgICAgICJTRUNUVVIgTkwiLA0KICAiVHVyaXN0YXNfTm9jaGVfRXh0IiwgICAgICAgICAgICAgICJOw7ptZXJvIHRvdGFsIGRlIG5vY2hlcyBkZSB0dXJpc3RhcyBleHRyYW5qZXJvcyIsICAgICAgICAgICAgICAgICJTRUNUVVIgTkwiLA0KICAiVHVyaXN0YXNfTm9jaGVfTmFjIiwgICAgICAgICAgICAgICJOw7ptZXJvIHRvdGFsIGRlIG5vY2hlcyBkZSB0dXJpc3RhcyBuYWNpb25hbGVzIiwgICAgICAgICAgICAgICAgICJTRUNUVVIgTkwiLA0KICAiJU9jdXBhY2lvbl9Ib3RlbGVzIiwgICAgICAgICAgICAgICJQb3JjZW50YWplIGRlIG9jdXBhY2nDs24gaG90ZWxlcmEgcHJvbWVkaW8gbWVuc3VhbCIsICAgICAgICAgICAgICJTRUNUVVIgTkwiLA0KICAiTGxlZ2FkYV9UdXJfRXh0IiwgICAgICAgICAgICAgICAgICJOw7ptZXJvIGRlIHR1cmlzdGFzIGV4dHJhbmplcm9zIHF1ZSBsbGVnYW4gYWwgZXN0YWRvIiwgICAgICAgICAgICJTRUNUVVIgTkwiLA0KICAiTGxlZ2FkYV9UdXJfTmFjIiwgICAgICAgICAgICAgICAgICJOw7ptZXJvIGRlIHR1cmlzdGFzIG5hY2lvbmFsZXMgcXVlIGxsZWdhbiBhbCBlc3RhZG8iLCAgICAgICAgICAgICJTRUNUVVIgTkwiLA0KICAiQ3VhcnRvc19EaXNwb25pYmxlc19Qcm9tZWRpbyIsICAgICJQcm9tZWRpbyBkZSBjdWFydG9zIGRpc3BvbmlibGVzICgrMSkiLCAgICAgICAgICAgICAgICAgICAgICAgICAgIlNFQ1RVUiBOTCIsDQogICJEZW5zaWRhZF9PY3VwYWNpw7NuIiwgICAgICAgICAgICAgICJEZW5zaWRhZCBkZSBvY3VwYWNpw7NuIGhvdGVsZXJhIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTRUNUVVIgTkwiLA0KICAiRXN0YWRpYV9Qcm9tZWRpbyIsICAgICAgICAgICAgICAgICJFc3RhZMOtYSBwcm9tZWRpbyBkZSBodcOpc3BlZGVzIGVuIG5vY2hlcyIsICAgICAgICAgICAgICAgICAgICAgICAiU0VDVFVSIE5MIg0KKQ0KDQojIE1vc3RyYXIgdGFibGFzDQpnbG9zYXJpb19vY3YgJT4lDQogIGthYmxlKCJodG1sIiwgY2FwdGlvbiA9ICJHbG9zYXJpbyBkZSBWYXJpYWJsZXM6IE9maWNpbmEgZGUgQ29udmVuY2lvbmVzIHkgVmlzaXRhbnRlcyAoT0NWKSIsIGFsaWduID0gImxsbCIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgZm9udF9zaXplID0gMTIpDQoNCmdsb3NhcmlvX2FpcmJuYiAlPiUNCiAga2FibGUoImh0bWwiLCBjYXB0aW9uID0gIkdsb3NhcmlvIGRlIFZhcmlhYmxlczogQmFzZSBkZSBEYXRvcyBkZSBBaXJibmIiLCBhbGlnbiA9ICJsbGwiKSAlPiUNCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGZvbnRfc2l6ZSA9IDEyKQ0KDQpnbG9zYXJpb19nbWFwcyAlPiUNCiAga2FibGUoImh0bWwiLCBjYXB0aW9uID0gIkdsb3NhcmlvIGRlIFZhcmlhYmxlczogQmFzZSBkZSBEYXRvcyBkZSBHb29nbGUgTWFwcyIsIGFsaWduID0gImxsbCIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgZm9udF9zaXplID0gMTIpDQoNCmdsb3NhcmlvX2RlbnVlICU+JQ0KICBrYWJsZSgiaHRtbCIsIGNhcHRpb24gPSAiR2xvc2FyaW8gZGUgVmFyaWFibGVzOiBCYXNlIGRlIERhdG9zIGRlbCBERU5VRSIsIGFsaWduID0gImxsbCIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgZm9udF9zaXplID0gMTIpDQoNCmdsb3NhcmlvX2hvdGVsZXMgJT4lDQogIGthYmxlKCJodG1sIiwgY2FwdGlvbiA9ICJHbG9zYXJpbyBkZSBWYXJpYWJsZXM6IEhvc3BpdGFsaWRhZCDigJMgSG90ZWxlcyIsIGFsaWduID0gImxjYyIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgZm9udF9zaXplID0gMTIpDQpgYGANCg0KDQoNCiMjIyBQcmVndW50YXMgZGUgQW7DoWxpc2lzDQoNCiogwr9Dw7NtbyBzZSBkaXN0cmlidXllbiBnZW9ncsOhZmljYW1lbnRlIGxvcyBhbG9qYW1pZW50b3MgKGhvdGVsZXMgeSBBaXJibmIpIGVuIHJlbGFjacOzbiBjb24gbG9zIHByaW5jaXBhbGVzIHJlY2ludG9zIGRlIGV2ZW50b3MgZW4gbGEgWk1NPw0KDQoqIMK/U2UgaWRlbnRpZmljYW4gY2x1c3RlcnMgbyB6b25hcyBjb24gYWx0YSBjb25jZW50cmFjacOzbiBkZSBob3NwZWRhamVzIHkgZXZlbnRvcywgeSBjw7NtbyBhZmVjdGFuIGVzdG9zIGEgbGEgZGluw6FtaWNhIHR1csOtc3RpY2EgbG9jYWw/DQoNCiogwr9FbiBxdcOpIG1lZGlkYSB5IGN1w6FsZXMgc29uIGxhcyB2YXJpYWJsZXMgcXVlIGV4cGxpY2FuIGxhIG1heW9yIHByb3BvcmNpw7NuIGRlIGxhIHZhcmlhbnphIGVuIGxhIGRlcnJhbWEgZWNvbsOzbWljYSBkZSBsYSBpbmR1c3RyaWEgaG90ZWxlcmEgZW4gbGEgWm9uYSBNZXRyb3BvbGl0YW5hIGRlIE1vbnRlcnJleSAoWk1NKT8NCg0KKiDCv0N1w6FsZXMgc29uIGxvcyBhc3BlY3RvcyBtw6FzIHZhbG9yYWRvcyB5IGxvcyBwdW50b3MgZGUgbWVqb3JhIHNlw7FhbGFkb3MgcG9yIGxvcyB2aXNpdGFudGVzIGVuIHN1cyBjb21lbnRhcmlvcyBlbiByZWxhY2nDs24gYSBsb3MgYWxvamFtaWVudG9zIChob3RlbGVzIHkgQWlyYm5iKSBlbiBsYSBaTU0/DQoNCg0KIyMgKipBbsOhbGlzaXMgRXhwbG9yYXRvcmlvIGRlIGxvcyBEYXRvcyAtIEVEQSoqDQoNCiMjIyBBaXJibmINCg0KU2UgcmVhbGl6YSByZXN1bWVuIGVzdGFkw61zdGljbyBkZSBjYWRhIHVuYSBkZSBsYXMgdmFyaWFibGVzIHF1ZSBjb21wb25lbiBsYSBiYXNlIGRlIGRhdG9zIGRlIEFpcmJuYjoNCg0KKiBIdcOpc3BlZGVzOiBOw7ptZXJvIG3DoXhpbW8gZGUgcGVyc29uYXMgcXVlIHB1ZWRlIGFsb2phciBsYSBwcm9waWVkYWQuIFJlcHJlc2VudGEgbGEgY2FwYWNpZGFkIGRlIGFsb2phbWllbnRvIHkgdmFyw61hIGVudHJlIHBlcXVlw7FhcyBoYWJpdGFjaW9uZXMgeSBhbG9qYW1pZW50b3MgYW1wbGlvcy4NCg0KKiBIYWJpdGFjaW9uZXM6IENhbnRpZGFkIGRlIGhhYml0YWNpb25lcyBwcml2YWRhcyBkaXNwb25pYmxlcyBlbiBlbCBhbG9qYW1pZW50by4gVW4gaW5kaWNhZG9yIGNsYXZlIGRlbCB0YW1hw7FvIGRlbCBpbm11ZWJsZS4NCg0KKiBDYW1hczogVG90YWwgZGUgY2FtYXMgZGlzcG9uaWJsZXMgcGFyYSBsb3MgaHXDqXNwZWRlcy4gUHVlZGUgaW5jbHVpciBjYW1hcyBpbmRpdmlkdWFsZXMsIGRvYmxlcywgc29mw6EgY2FtYXMgbyBjb2xjaG9uZXMgaW5mbGFibGVzLg0KDQoqIEJhw7FvczogTsO6bWVybyB0b3RhbCBkZSBiYcOxb3MgZGlzcG9uaWJsZXMgZW4gbGEgcHJvcGllZGFkLiBJbmNsdXllIHRhbnRvIGJhw7FvcyBjb21wbGV0b3MgY29tbyBtZWRpb3MgYmHDsW9zIGVuIGFsZ3Vub3MgY2Fzb3MuDQoNCiogQ2FsaWZpY2FjacOzbjogUHJvbWVkaW8gZGUgbGFzIGNhbGlmaWNhY2lvbmVzIG90b3JnYWRhcyBwb3IgaHXDqXNwZWRlcyAoZGUgMCBhIDUpLiBSZWZsZWphIGxhIGNhbGlkYWQgZ2VuZXJhbCBwZXJjaWJpZGEgZGVsIGhvc3BlZGFqZS4NCg0KKiBFdmFsdWFjaW9uZXM6IFRvdGFsIGRlIHZlY2VzIHF1ZSBsYSBwcm9waWVkYWQgaGEgc2lkbyBldmFsdWFkYSBwb3IgbG9zIHVzdWFyaW9zLiBFcyB1bmEgbWVkaWRhIGluZGlyZWN0YSBkZSBsYSBwb3B1bGFyaWRhZCBvIHJvdGFjacOzbiBkZWwgYW51bmNpby4NCg0KKiBQcmVjaW86IENvc3RvIHBvciBub2NoZSBlbiBwZXNvcyBtZXhpY2Fub3MgKE1YTikuIFZhcsOtYSBhbXBsaWFtZW50ZSBzZWfDum4gdWJpY2FjacOzbiwgZmVjaGEsIHRhbWHDsW8sIGNvbW9kaWRhZGVzIHkgZGVtYW5kYS4NCg0KKiBMYXRpdHVkOiBDb29yZGVuYWRhIGdlb2dyw6FmaWNhIHF1ZSBpbmRpY2EgbGEgdWJpY2FjacOzbiBub3J0ZS1zdXIgZGUgbGEgcHJvcGllZGFkLiBFbiBlc3RlIGNhc28sIHJlZmxlamEgdWJpY2FjaW9uZXMgZGVudHJvIGRlIGxhIHpvbmEgbWV0cm9wb2xpdGFuYSBkZSBNb250ZXJyZXkuDQoNCiogTG9uZ2l0dWQ6IENvb3JkZW5hZGEgZ2VvZ3LDoWZpY2EgcXVlIGluZGljYSBsYSB1YmljYWNpw7NuIGVzdGUtb2VzdGUgZGUgbGEgcHJvcGllZGFkLiBTZSB1c2EganVudG8gY29uIGxhIGxhdGl0dWQgcGFyYSB1YmljYXIgbG9zIGFudW5jaW9zIGVuIHVuIG1hcGEuDQoNCiogTWVzZXNfQW50aWfDvGVkYWQ6IFRpZW1wbywgZW4gbWVzZXMsIHF1ZSBlbCBhbmZpdHJpw7NuIGxsZXZhIGFjdGl2byBlbiBsYSBwbGF0YWZvcm1hLiBQdWVkZSBpbmRpY2FyIGV4cGVyaWVuY2lhIGRlbCBhbmZpdHJpw7NuIG8gZXN0YWJpbGlkYWQgZGVsIGFsb2phbWllbnRvLg0KDQoNCiMjIyMgU3VtbWFyeQ0KDQpgYGB7cn0NCmxpYnJhcnkob3Blbnhsc3gpDQpkZl9haXIgPC0gcmVhZC54bHN4KCJhaXJibmJfb2ZpY2lhbC54bHN4IikNCg0KIyBFbGltaW5hciBsYXMgY29sdW1uYXMgIk5vY2hlIiB5ICJEZXNjcmlwY2lvbiINCmRmX2Fpcl9jbGVhbiA8LSBkZl9haXJbLCAhbmFtZXMoZGZfYWlyKSAlaW4lIGMoIk5vY2hlcyIsICJEZXNjcmlwY2lvbiIpXQ0KDQojIEZpbHRyYXIgc29sbyBsYXMgY29sdW1uYXMgbnVtw6lyaWNhcw0KZGZfYWlyX251bWVyaWMgPC0gZGZfYWlyX2NsZWFuW3NhcHBseShkZl9haXJfY2xlYW4sIGlzLm51bWVyaWMpXQ0KDQojIEdlbmVyYXIgZWwgcmVzdW1lbiBlc3RhZMOtc3RpY28NCnN1bW1hcnkoZGZfYWlyX251bWVyaWMpDQpgYGANCg0KDQojIyMjIEJveHBsb3RzDQoNCkRpc3RyaWJ1Y2nDs24gc2VzZ2FkYSB5IHByZXNlbmNpYSBkZSB2YWxvcmVzIGF0w61waWNvczogTGEgbWF5b3LDrWEgZGUgbGFzIHZhcmlhYmxlcyBtdWVzdHJhbiBkaXN0cmlidWNpb25lcyBhc2ltw6l0cmljYXMgY29uIHZhcmlvcyBvdXRsaWVyczoNCg0KKiBQcmVjaW8geSBFdmFsdWFjaW9uZXMgdGllbmVuIGNvbGFzIGxhcmdhcyBoYWNpYSBhcnJpYmEsIGxvIHF1ZSBpbmRpY2EgbGEgZXhpc3RlbmNpYSBkZSBhbGd1bmFzIHByb3BpZWRhZGVzIGNvbiBwcmVjaW9zIG8gZXZhbHVhY2lvbmVzIGV4dHJlbWFkYW1lbnRlIGFsdGFzLg0KDQoqIEh1w6lzcGVkZXMsIEhhYml0YWNpb25lcywgQ2FtYXMgeSBCYcOxb3MgdGllbmVuIG1lZGlhbmEgYmFqYSAoMSBvIDIpIHBlcm8gY29sYXMgc3VwZXJpb3JlcyBxdWUgc3VnaWVyZW4gYWxndW5hcyBwcm9waWVkYWRlcyBjb24gbXVjaGEgbWF5b3IgY2FwYWNpZGFkLg0KDQoqIENhbGlmaWNhY2nDs24gZXN0w6EgbXV5IGNvbmNlbnRyYWRhIGFscmVkZWRvciBkZSA0LjUtNSwgbG8gcXVlIGluZGljYSBidWVuYSBzYXRpc2ZhY2Npw7NuIGdlbmVyYWwuDQoNCkFzaW1pc21vLCBzZSBvYnNlcnZhIHF1ZSBNZXNlc19BbnRpZ8O8ZWRhZCB0aWVuZSB1bmEgZGlzdHJpYnVjacOzbiBtw6FzIGVxdWlsaWJyYWRhLCBjb24gdmFsb3JlcyBlbnRyZSAwIHkgMTAwIG1lc2VzLCBwZXJvIHRhbWJpw6luIGNvbiBvdXRsaWVycy4NCg0KYGBge3J9DQpsaWJyYXJ5KHJlc2hhcGUyKQ0KZGZfbG9uZyA8LSBkZl9haXJfbnVtZXJpYyAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gInZhcmlhYmxlIiwgdmFsdWVzX3RvID0gInZhbG9yIikNCg0KIyBDcmVhciBsb3MgYm94cGxvdHMgY29uIGVzY2FsYXMgaW5kZXBlbmRpZW50ZXMgZW4gZWwgZWplIFkNCmdncGxvdChkZl9sb25nLCBhZXMoeCA9ICIiLCB5ID0gdmFsb3IpKSArDQogIGdlb21fYm94cGxvdChmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG9yID0gImRhcmtibHVlIikgKw0KICBmYWNldF93cmFwKH4gdmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlX3kiKSArDQogIGxhYnModGl0bGUgPSAiQm94cGxvdHMgcG9yIHZhcmlhYmxlIG51bcOpcmljYSIsDQogICAgICAgeCA9IE5VTEwsDQogICAgICAgeSA9IE5VTEwpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQojIyMjIE1hdHJpeiBkZSBjb3JyZWxhY2nDs24NCg0KKipSZWxhY2lvbmVzIHBvc2l0aXZhcyBmdWVydGVzOioqDQoNCiogSGFiaXRhY2lvbmVzIHkgQ2FtYXMgKHIg4omIIDAuODApOiBwcm9waWVkYWRlcyBjb24gbcOhcyBoYWJpdGFjaW9uZXMgdGllbmRlbiBhIHRlbmVyIG3DoXMgY2FtYXMuDQoNCiogSGFiaXRhY2lvbmVzIHkgSHXDqXNwZWRlcyAociDiiYggMC42NCk6IGEgbcOhcyBoYWJpdGFjaW9uZXMsIG3DoXMgY2FwYWNpZGFkIGRlIGh1w6lzcGVkZXMuDQoNCiogSHXDqXNwZWRlcyB5IFByZWNpbyAociDiiYggMC41NCk6IG3DoXMgY2FwYWNpZGFkLCBtYXlvciBwcmVjaW8uDQoNCioqUmVsYWNpb25lcyBkw6liaWxlcyBvIG51bGFzOioqDQoNCiogQ2FsaWZpY2FjacOzbiB5IG90cmFzIHZhcmlhYmxlczogbXV5IHBvY2EgY29ycmVsYWNpw7NuLCBsbyBxdWUgc3VnaWVyZSBxdWUgbGEgY2FsaWZpY2FjacOzbiBkZXBlbmRlIG3DoXMgZGUgZmFjdG9yZXMgY3VhbGl0YXRpdm9zLg0KDQoqIExhdGl0dWQvTG9uZ2l0dWQgdnMgb3RyYXMgdmFyaWFibGVzOiBiYWphIGNvcnJlbGFjacOzbiwgbG8gcXVlIGluZGljYSBwb2NhIGluZmx1ZW5jaWEgZGlyZWN0YSBkZSBsYSB1YmljYWNpw7NuIGVuIGxhcyB2YXJpYWJsZXMgbnVtw6lyaWNhcy4NCg0KKiBSZWxhY2nDs24gbmVnYXRpdmE6IFByZWNpbyBjb3JyZWxhY2lvbmEgbGlnZXJhbWVudGUgbmVnYXRpdm8gY29uIExhdGl0dWQgeSBFdmFsdWFjaW9uZXMsIGxvIHF1ZSBwb2Ryw61hIGluZGljYXIgem9uYXMgY29uIHByZWNpb3MgbcOhcyBhbHRvcyBwZXJvIG1lbm9zIGV2YWx1YWNpb25lcyBvIGRpZmVyZW50ZSB1YmljYWNpw7NuIGdlb2dyw6FmaWNhLg0KDQoNCmBgYHtyfQ0KIyBDYWxjdWxhciBsYSBtYXRyaXogZGUgY29ycmVsYWNpw7NuDQpjb3JfbWF0cml4IDwtIGNvcihkZl9haXJfbnVtZXJpYywgdXNlID0gImNvbXBsZXRlLm9icyIpDQoNCiMgVmlzdWFsaXphciBsYSBtYXRyaXogZGUgY29ycmVsYWNpw7NuDQpjb3JycGxvdChjb3JfbWF0cml4LCBtZXRob2QgPSAiY29sb3IiLCB0eXBlID0gInVwcGVyIiwNCiAgICAgICAgIHRsLmNvbCA9ICJibGFjayIsIHRsLnNydCA9IDQ1LA0KICAgICAgICAgYWRkQ29lZi5jb2wgPSAiYmxhY2siLCBudW1iZXIuY2V4ID0gMC43LA0KICAgICAgICAgY29sID0gY29sb3JSYW1wUGFsZXR0ZShjKCJyZWQiLCAid2hpdGUiLCAiYmx1ZSIpKSgyMDApKQ0KDQpgYGANCg0KDQojIyMjIEhpc3RvZ3JhbWFzDQoNCiogRGlzdHJpYnVjaW9uZXMgc2VzZ2FkYXMgYSBsYSBkZXJlY2hhIChwb3NpdGl2YW1lbnRlKTogUHJlY2lvLCBFdmFsdWFjaW9uZXMsIEh1w6lzcGVkZXMsIENhbWFzLCBIYWJpdGFjaW9uZXMsIEJhw7FvczogbWF5b3LDrWEgZGUgbGFzIG9ic2VydmFjaW9uZXMgc2UgY29uY2VudHJhbiBlbiB2YWxvcmVzIGJham9zLCBjb24gcG9jYXMgcHJvcGllZGFkZXMgcXVlIHRpZW5lbiB2YWxvcmVzIG11eSBhbHRvcy4NCg0KKiBEaXN0cmlidWNpw7NuIGRlIENhbGlmaWNhY2nDs246IG11eSBjb25jZW50cmFkYSBlbnRyZSA0LjUgeSA1LCByZWZsZWphbmRvIGFsdG9zIG5pdmVsZXMgZGUgc2F0aXNmYWNjacOzbi4NCg0KKiBNZXNlc19BbnRpZ8O8ZWRhZDogZGlzcGVyc2EgYSBsbyBsYXJnbyBkZWwgcmFuZ28sIHNpbiB1bmEgZm9ybWEgY2xhcmEsIGF1bnF1ZSBoYXkgY2llcnRvcyBwaWNvcyBpbmRpY2FuZG8gYW5maXRyaW9uZXMgY29uIHRpZW1wbyBzaWduaWZpY2F0aXZvIGVuIGxhIHBsYXRhZm9ybWEuDQoNCiogTGF0aXR1ZCB5IExvbmdpdHVkOiByZWZsZWphbiB1bmEgZnVlcnRlIGNvbmNlbnRyYWNpw7NuIGdlb2dyw6FmaWNhOyBwb2Ryw61hIGluZGljYXIgcXVlIGxhcyBwcm9waWVkYWRlcyBlc3TDoW4gdWJpY2FkYXMgZW4gdW5hIGNpdWRhZCBvIHpvbmEgZXNwZWPDrWZpY2EgKGNvbW8gTW9udGVycmV5KS4NCg0KYGBge3J9DQpwbG90X2hpc3RvZ3JhbShkZl9haXJfbnVtZXJpYykNCnBsb3Rfbm9ybWFsaXR5KGRmX2Fpcl9udW1lcmljKQ0KYGBgDQoNCg0KIyMjIEV2ZW50b3MgT0NWDQoNCiMjIyMgU3VtbWFyeQ0KDQpTZSBvYnNlcnZhIHVuYSBjb25jZW50cmFjacOzbiBkZSBsb3MgZGF0b3MgZW4gMjAyMyB5IDIwMjQsIGVuIGxhIGN1YWwgbGEgZGlzdHJpYnVjacOzbiBkZSBsb3MgZXZlbnRvcyB5IGxhIGNhbnRpZGFkIGRlIGN1YXJ0b3MgZGlzcG9uaWJsZXMgdGllbmRlbiBhIGNvbmNlbnRyYXJzZSBlbiByYW5nb3MgaW5mZXJpb3JlcywgYXVucXVlIHNlIGlkZW50aWZpY2FuIGFsZ3Vub3MgcGljb3MgbyB2YWxvcmVzIGludXN1YWxtZW50ZSBhbHRvcyBxdWUgcG9kcsOtYW4gY29ycmVzcG9uZGVyIGEgZXZlbnRvcyBvIGVzdGFibGVjaW1pZW50b3MgcGFydGljdWxhcmVzIGNvbiB1bmEgY2FwYWNpZGFkIHNpZ25pZmljYXRpdmFtZW50ZSBtYXlvcjsgbG8gY3VhbCBzZSBkZW5vdGEgZW4gbGEgbWVkaWEgZGUgbGEgZGVycmFtYSBlY29uw7NtaWNhIHJlYWwuDQoNCiogKipFdmVudG9zOioqIFNlIG11ZXN0cmEgdW4gcmFuZ28gZGUgMiBhIDMwIGV2ZW50b3MsIGNvbiB1bmEgbWVkaWFuYSBkZSAxMS4gTGEgbWVkaWEgbGlnZXJhbWVudGUgc3VwZXJpb3IgYSBsYSBtZWRpYW5hICgxMi4xMikgc3VnaWVyZSB1bmEgYXNpbWV0csOtYSBwb3NpdGl2YSBjYXVzYWRhIHBvciBsb3MgdmFsb3JlcyBhdMOtcGljb3MuDQoNCiogKipDdWFydG9zOioqIFNlIG11ZXN0cmEgdW4gcmFuZ28gZGUgMTIzNSBhIDM1NTY4IGN1YXJ0b3MsIGNvbiB1bmEgbWVkaWFuYSBkZSAxMDQ4Mi4gTGEgbWVkaWEgKDEyMTE3KSBlcyBtYXlvciBxdWUgbGEgbWVkaWFuYSwgbG8gcXVlIGluZGljYSB1bmEgYXNpbWV0csOtYSBwb3NpdGl2YS4NCg0KKiAqKkFzaXN0ZW50ZXM6KiogU2UgcHJlc2VudGEgdW4gcmFuZ28gbXV5IGFtcGxpbyBkZSBhc2lzdGVudGVzLCBkZXNkZSAxODA5IGhhc3RhIDM1NzQ1MCwgY29uIHVuYSBtZWRpYW5hIGRlIHNvbG8gMTU4NzUuIExhIG1lZGlhICg0NDgxOSkgZXMgc2lnbmlmaWNhdGl2YW1lbnRlIG1heW9yIHF1ZSBsYSBtZWRpYW5hLCBsbyBxdWUgaW5kaWNhIHVuYSBmdWVydGUgaW5mbHVlbmNpYSBkZWwgdmFsb3IgYXTDrXBpY28gc3VwZXJpb3IuDQoNCiogKipEZXJyYW1hOioqIFNlIG11ZXN0cmEgdW4gcmFuZ28gZGUgZGVycmFtYSBkZXNkZSA3LjUgbWlsbG9uZXMgaGFzdGEgMzQzIG1pbGxvbmVzLCBjb24gdW5hIG1lZGlhbmEgZGUgMTI5IG1pbGxvbmVzLiBMYSBtZWRpYSAoMTUyIG1pbGxvbmVzKSBlcyBtYXlvciBxdWUgbGEgbWVkaWFuYSwgbG8gcXVlIHN1Z2llcmUgdW5hIGFzaW1ldHLDrWEgcG9zaXRpdmEgZGViaWRvIGFsIHZhbG9yIGF0w61waWNvLg0KDQoqICoqTGxlZ2FkYV9UdXJfRXh0IChMbGVnYWRhIGRlIFR1cmlzdGFzIEV4dHJhbmplcm9zKToqKiBTZSBtdWVzdHJhIHVuIHJhbmdvIGRlIGxsZWdhZGFzIGRlIHR1cmlzdGFzIGV4dHJhbmplcm9zIGVudHJlIDMxODIwIHkgNjExMDcsIGNvbiB1bmEgbWVkaWFuYSBkZSA0NTkzMSB5IHVuYSBtZWRpYSBsaWdlcmFtZW50ZSBzdXBlcmlvciAoNDc2NzIpLCBzdWdpcmllbmRvIHVuYSBsaWdlcmEgYXNpbWV0csOtYSBwb3NpdGl2YS4NCg0KKiAqKkxsZWdhZGFfVHVyX05hYyAoTGxlZ2FkYSBkZSBUdXJpc3RhcyBOYWNpb25hbGVzKToqKiBTZSBtdWVzdHJhIHVuIHJhbmdvIGRlIGxsZWdhZGFzIGRlIHR1cmlzdGFzIG5hY2lvbmFsZXMgZW50cmUgMTQ1MTc5IHkgMzE1NjcyLCBjb24gdW5hIG1lZGlhbmEgZGUgMjIwOTc2IHkgdW5hIG1lZGlhIGxpZ2VyYW1lbnRlIHN1cGVyaW9yICgyMjM5NjEpLCBpbmRpY2FuZG8gdW5hIGxpZ2VyYSBhc2ltZXRyw61hIHBvc2l0aXZhIGluZmx1ZW5jaWFkYSBwb3IgZWwgdmFsb3IgYXTDrXBpY28uDQoNCiogKiolT2N1cGFjaW9uX0hvdGVsZXMgKFBvcmNlbnRhamUgZGUgT2N1cGFjacOzbiBIb3RlbGVyYSk6KiogU2Ugb2JzZXJ2YSB1biByYW5nbyBkZSBvY3VwYWNpw7NuIGhvdGVsZXJhIGVudHJlIDAuNDYgeSAwLjc2LCBjb24gdW5hIG1lZGlhbmEgZGUgMC42NCB5IHVuYSBtZWRpYSBsaWdlcmFtZW50ZSBpbmZlcmlvciAoMC42MzU2KSwgbG8gcXVlIHN1Z2llcmUgdW5hIGxpZ2VyYSBhc2ltZXRyw61hIG5lZ2F0aXZhIGRlYmlkbyBhIGxvcyB2YWxvcmVzIGF0w61waWNvcyBpbmZlcmlvcmVzLg0KDQpgYGB7cn0NCmxpYnJhcnkob3Blbnhsc3gpDQpkZl9PQ1YgPC0gcmVhZC54bHN4KCJUYWJsYV9NYWNyb19FdmVudG9zT0NWLnhsc3giKQ0KDQojIENvbnZlcnRpciBhIFJlYWxlcw0KZGZfT0NWJERlcnJhbWE8LSAoZGZfT0NWJERlcnJhbWEvZGZfT0NWJElOUEMpKjEwMA0KDQojIENyZWFyIHVuYSBjb2x1bW5hIGRlIGZlY2hhIGNvbWJpbmFuZG8gQcOxbyB5IE1lcw0KZGZfT0NWJEZlY2hhIDwtIGFzLkRhdGUocGFzdGUoZGZfT0NWJEHDsW8sIGRmX09DViRNZXNOb21icmUsICIwMSIsIHNlcCA9ICItIiksIGZvcm1hdCA9ICIlWS0lbS0lZCIpDQoNCiMgRmlsdHJhciBsb3MgZGF0b3MgZGVzZGUgZW5lcm8gZGUgMjAyMyBlbiBhZGVsYW50ZQ0KZGZfT0NWX2ZpbHRlcmVkIDwtIHN1YnNldChkZl9PQ1YsIEZlY2hhID49IGFzLkRhdGUoIjIwMjMtMDEtMDEiKSkNCg0KIyBFbGltaW5hciBsYXMgY29sdW1uYXMgIk5vY2hlcyIgeSAiRGVzY3JpcGNpb24iDQpkZl9PQ1ZfY2xlYW4gPC0gZGZfT0NWX2ZpbHRlcmVkWywgbmFtZXMoZGZfT0NWX2ZpbHRlcmVkKSAlaW4lIGMoDQogICJGZWNoYSIsDQogICJBw7FvIiwNCiAgIk1lc05vbWJyZSIsDQogICJFdmVudG9zIiwNCiAgIkN1YXJ0b3MiLA0KICAiQXNpc3RlbnRlcyIsDQogICJEZXJyYW1hIiwNCiAgIiVPY3VwYWNpb25fSG90ZWxlcyIsDQogICJMbGVnYWRhX1R1cl9FeHQiLA0KICAiTGxlZ2FkYV9UdXJfTmFjIg0KKV0NCg0KDQojIEZpbHRyYXIgc29sbyBsYXMgY29sdW1uYXMgbnVtw6lyaWNhcw0KZGZfT0NWX251bWVyaWMgPC0gZGZfT0NWX2NsZWFuW3NhcHBseShkZl9PQ1ZfY2xlYW4sIGlzLm51bWVyaWMpXQ0KDQojIEdlbmVyYXIgZWwgcmVzdW1lbiBlc3RhZMOtc3RpY28NCnN1bW1hcnkoZGZfT0NWX251bWVyaWMpDQpgYGANCg0KDQojIyMjIEJveHBsb3RzDQoNClNlIG9ic2VydmEgcXVlIGxhIGFjdGl2aWRhZCB0dXLDrXN0aWNhIGNvbiBwYXRyb25lcyBkZWZpbmlkb3MgYSBsbyBsYXJnbyBkZWwgYcOxbywgY29uIGNpZXJ0b3MgZXZlbnRvcyBvIHNpdHVhY2lvbmVzIHB1bnR1YWxlcyBxdWUgZ2VuZXJhbiB1biBpbXBhY3RvIHNpZ25pZmljYXRpdmFtZW50ZSBtYXlvciBlbiB0w6lybWlub3MgZGUgYXNpc3RlbnRlcyB5IGRlcnJhbWEgZWNvbsOzbWljYToNCg0KKiAqKkV2ZW50b3M6KiogU2UgbXVlc3RyYSB1bmEgY29uY2VudHJhY2nDs24gZGUgZXZlbnRvcyBlbiBsYSBwYXJ0ZSBpbmZlcmlvciwgY29uIGFsZ3Vub3MgdmFsb3JlcyBhdMOtcGljb3Mgc2lnbmlmaWNhdGl2YW1lbnRlIGFsdG9zLg0KDQoqICoqQ3VhcnRvczoqKiBTZSBtdWVzdHJhIHVuYSBkaXN0cmlidWNpw7NuIGNvbiB1bmEgbWVkaWFuYSBhbHJlZGVkb3IgZGUgMTAsMDAwIGN1YXJ0b3MsIGNvbiBhbGd1bm9zIHZhbG9yZXMgYXTDrXBpY29zIHN1cGVyaW9yZXMuDQoNCiogKipBc2lzdGVudGVzOioqIFNlIHByZXNlbnRhIGxhIG1heW9yw61hIGRlIGxvcyBkYXRvcyBkZSBhc2lzdGVudGVzIGVuIHVuIHJhbmdvIGJham8sIGNvbiB1biB2YWxvciBhdMOtcGljbyBtdXkgYWx0by4NCg0KKiAqKkRlcnJhbWE6KiogU2UgbXVlc3RyYSB1bmEgZGlzdHJpYnVjacOzbiBjb24gdW5hIG1lZGlhbmEgYWxyZWRlZG9yIGRlIDEuMyB4IDEwXjgsIGNvbiB1biB2YWxvciBhdMOtcGljbyBzdXBlcmlvciBub3RhYmxlLg0KDQoqICoqTGxlZ2FkYV9UdXJfRXh0IChMbGVnYWRhIGRlIFR1cmlzdGFzIEV4dHJhbmplcm9zKToqKiBTZSBtdWVzdHJhIHVuYSBkaXN0cmlidWNpw7NuIHJlbGF0aXZhbWVudGUgY2VudHJhZGEsIGNvbiBwb2NvcyB2YWxvcmVzIGF0w61waWNvcy4NCg0KKiAqKkxsZWdhZGFfVHVyX05hYyAoTGxlZ2FkYSBkZSBUdXJpc3RhcyBOYWNpb25hbGVzKToqKiBTZSBtdWVzdHJhIHVuYSBkaXN0cmlidWNpw7NuIHJlbGF0aXZhbWVudGUgY2VudHJhZGEsIGNvbiB1biB2YWxvciBhdMOtcGljbyBzdXBlcmlvci4NCg0KKiAqKiVPY3VwYWNpb25fSG90ZWxlcyAoUG9yY2VudGFqZSBkZSBPY3VwYWNpw7NuIEhvdGVsZXJhKToqKiBTZSBtdWVzdHJhIHVuYSBkaXN0cmlidWNpw7NuIHJlbGF0aXZhbWVudGUgZXN0cmVjaGEsIGNvbiBsYSBtYXlvcsOtYSBkZSBsb3MgdmFsb3JlcyBlbnRyZSBhcHJveGltYWRhbWVudGUgMC42IHkgMC43LCB5IGFsZ3Vub3MgdmFsb3JlcyBhdMOtcGljb3MgaW5mZXJpb3Jlcy4NCg0KDQpgYGB7cn0NCmxpYnJhcnkocmVzaGFwZTIpDQpkZl9sb25nIDwtIGRmX09DVl9udW1lcmljICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSAidmFyaWFibGUiLCB2YWx1ZXNfdG8gPSAidmFsb3IiKQ0KDQojIENyZWFyIGxvcyBib3hwbG90cyBjb24gZXNjYWxhcyBpbmRlcGVuZGllbnRlcyBlbiBlbCBlamUgWQ0KZ2dwbG90KGRmX2xvbmcsIGFlcyh4ID0gIiIsIHkgPSB2YWxvcikpICsNCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3IgPSAiZGFya2JsdWUiKSArDQogIGZhY2V0X3dyYXAofiB2YXJpYWJsZSwgc2NhbGVzID0gImZyZWVfeSIpICsNCiAgbGFicyh0aXRsZSA9ICJCb3hwbG90cyBwb3IgdmFyaWFibGUgbnVtw6lyaWNhIiwNCiAgICAgICB4ID0gTlVMTCwNCiAgICAgICB5ID0gTlVMTCkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCiMjIyMgTWF0cml6IGRlIENvcnJlbGFjacOzbg0KDQpFbCBuw7ptZXJvIGRlIGV2ZW50b3MgeSBsYSBsbGVnYWRhIGRlIHR1cmlzdGFzIG5hY2lvbmFsZXMgcGFyZWNlbiBzZXIgaW1wdWxzb3JlcyBjbGF2ZSBkZSBsYSBhc2lzdGVuY2lhLCBsYSBkZXJyYW1hIGVjb27Ds21pY2EgeSBsYSBvY3VwYWNpw7NuIGhvdGVsZXJhLiBMYSBjYW50aWRhZCBkZSBjdWFydG9zIGRpc3BvbmlibGVzIHRhbWJpw6luIG11ZXN0cmEgdW5hIHJlbGFjacOzbiBwb3NpdGl2YSBjb24gbGEgZGVycmFtYSB5IGxhIG9jdXBhY2nDs24uIFBvciBvdHJvIGxhZG8sIGxhIGxsZWdhZGEgZGUgdHVyaXN0YXMgZXh0cmFuamVyb3MgcGFyZWNlIHRlbmVyIHVuYSByZWxhY2nDs24gbcOhcyBkw6liaWwgbyBpbmNsdXNvIG5lZ2F0aXZhIGNvbiBhbGd1bmFzIGRlIGxhcyBvdHJhcyB2YXJpYWJsZXMgYW5hbGl6YWRhcy4NCg0KKipDb3JyZWxhY2lvbmVzIFBvc2l0aXZhcyBGdWVydGVzIChBenVsIE9zY3VybywgdmFsb3JlcyBjZXJjYW5vcyBhIDEpOioqDQoNCiogQXNpc3RlbnRlcyAtIEV2ZW50b3MgKDAuNzgpOiBFeGlzdGUgdW5hIGZ1ZXJ0ZSB0ZW5kZW5jaWEgYSBxdWUgdW4gbWF5b3IgbsO6bWVybyBkZSBldmVudG9zIGVzdMOpIGFzb2NpYWRvIGNvbiB1biBtYXlvciBuw7ptZXJvIGRlIGFzaXN0ZW50ZXMuIEVzdG8gZXMgYmFzdGFudGUgaW50dWl0aXZvLg0KDQoqIEFzaXN0ZW50ZXMgLSBEZXJyYW1hICgwLjU2KTogSGF5IHVuYSBjb3JyZWxhY2nDs24gcG9zaXRpdmEgbW9kZXJhZGEsIGxvIHF1ZSBzdWdpZXJlIHF1ZSBtw6FzIGFzaXN0ZW50ZXMgdGllbmRlbiBhIGdlbmVyYXIgdW5hIG1heW9yIGRlcnJhbWEgZWNvbsOzbWljYS4NCg0KKiBBc2lzdGVudGVzIC0gTGxlZ2FkYV9UdXJfTmFjICgwLjYxKTogVW4gbWF5b3IgbsO6bWVybyBkZSB0dXJpc3RhcyBuYWNpb25hbGVzIGxsZWdhbmRvIHNlIGFzb2NpYSBjb24gdW4gbWF5b3IgbsO6bWVybyBkZSBhc2lzdGVudGVzIGEgZXZlbnRvcyBvIGFjdGl2aWRhZGVzLg0KDQoqIEV2ZW50b3MgLSBEZXJyYW1hICgwLjY3KTogVW4gbWF5b3IgbsO6bWVybyBkZSBldmVudG9zIHRpZW5kZSBhIGVzdGFyIHJlbGFjaW9uYWRvIGNvbiB1bmEgbWF5b3IgZGVycmFtYSBlY29uw7NtaWNhIHRvdGFsLg0KDQoqIEV2ZW50b3MgLSBMbGVnYWRhX1R1cl9OYWMgKDAuODIpOiBFeGlzdGUgdW5hIGZ1ZXJ0ZSBjb3JyZWxhY2nDs24gZW50cmUgZWwgbsO6bWVybyBkZSBldmVudG9zIHkgbGEgbGxlZ2FkYSBkZSB0dXJpc3RhcyBuYWNpb25hbGVzLiBNw6FzIGV2ZW50b3MgcG9kcsOtYW4gYXRyYWVyIGEgbcOhcyB0dXJpc3RhcyBuYWNpb25hbGVzLCBvIHZpY2V2ZXJzYS4NCg0KKiBFdmVudG9zIC0gQ3VhcnRvcyAoMC42Myk6IEhheSB1bmEgY29ycmVsYWNpw7NuIHBvc2l0aXZhIG1vZGVyYWRhLCBzdWdpcmllbmRvIHF1ZSBtw6FzIGV2ZW50b3MgcG9kcsOtYW4gcmVxdWVyaXIgbyBlc3RhciBhc29jaWFkb3MgY29uIHVuYSBtYXlvciBjYW50aWRhZCBkZSBjdWFydG9zIGRpc3BvbmlibGVzLg0KDQoqIERlcnJhbWEgLSBMbGVnYWRhX1R1cl9OYWMgKDAuNzcpOiBVbmEgbWF5b3IgbGxlZ2FkYSBkZSB0dXJpc3RhcyBuYWNpb25hbGVzIGVzdMOhIGZ1ZXJ0ZW1lbnRlIHJlbGFjaW9uYWRhIGNvbiB1bmEgbWF5b3IgZGVycmFtYSBlY29uw7NtaWNhLg0KDQoqIERlcnJhbWEgLSBDdWFydG9zICgwLjgzKTogRXhpc3RlIHVuYSBmdWVydGUgY29ycmVsYWNpw7NuIGVudHJlIGxhIGRlcnJhbWEgZWNvbsOzbWljYSB5IGxhIGNhbnRpZGFkIGRlIGN1YXJ0b3MgZGlzcG9uaWJsZXMuIFVuYSBtYXlvciBjYXBhY2lkYWQgZGUgYWxvamFtaWVudG8gcG9kcsOtYSBlc3RhciBhc29jaWFkYSBjb24gdW5hIG1heW9yIGRlcnJhbWEuDQoNCiogTGxlZ2FkYV9UdXJfTmFjIC0gJU9jdXBhY2lvbl9Ib3RlbGVzICgwLjc5KTogVW5hIG1heW9yIGxsZWdhZGEgZGUgdHVyaXN0YXMgbmFjaW9uYWxlcyBzZSBhc29jaWEgZnVlcnRlbWVudGUgY29uIHVuIG1heW9yIHBvcmNlbnRhamUgZGUgb2N1cGFjacOzbiBob3RlbGVyYS4NCg0KKiBMbGVnYWRhX1R1cl9OYWMgLSBDdWFydG9zICgwLjUzKTogSGF5IHVuYSBjb3JyZWxhY2nDs24gcG9zaXRpdmEgbW9kZXJhZGEsIGxvIHF1ZSBzdWdpZXJlIHF1ZSB1bmEgbWF5b3IgbGxlZ2FkYSBkZSB0dXJpc3RhcyBuYWNpb25hbGVzIHBvZHLDrWEgZXN0YXIgcmVsYWNpb25hZGEgY29uIHVuYSBtYXlvciBjYW50aWRhZCBkZSBjdWFydG9zIGRpc3BvbmlibGVzLg0KDQoqICVPY3VwYWNpb25fSG90ZWxlcyAtIEN1YXJ0b3MgKDAuNTgpOiBFeGlzdGUgdW5hIGNvcnJlbGFjacOzbiBwb3NpdGl2YSBtb2RlcmFkYSBlbnRyZSBlbCBwb3JjZW50YWplIGRlIG9jdXBhY2nDs24gaG90ZWxlcmEgeSBsYSBjYW50aWRhZCBkZSBjdWFydG9zLiBVbmEgbWF5b3Igb2ZlcnRhIGRlIGN1YXJ0b3MgcG9kcsOtYSBsbGV2YXIgYSB1bmEgbWF5b3Igb2N1cGFjacOzbiBnZW5lcmFsLCBvIHZpY2V2ZXJzYSBlbiBjaWVydGFzIGNpcmN1bnN0YW5jaWFzLg0KDQoqIE1lc05vbWJyZSAtIEN1YXJ0b3MgKDAuNDcpOiBIYXkgdW5hIGNvcnJlbGFjacOzbiBwb3NpdGl2YSBkw6liaWwsIHN1Z2lyaWVuZG8gdW5hIGxpZ2VyYSB0ZW5kZW5jaWEgYSBxdWUgY2llcnRvcyBtZXNlcyBlc3TDqW4gYXNvY2lhZG9zIGNvbiB1bmEgbWF5b3IgY2FudGlkYWQgZGUgY3VhcnRvcyBkaXNwb25pYmxlcy4NCg0KKipDb3JyZWxhY2lvbmVzIE5lZ2F0aXZhcyAoUm9qbywgdmFsb3JlcyBjZXJjYW5vcyBhIC0xKToqKg0KDQoqIExsZWdhZGFfVHVyX0V4dCAtIEHDsW8gKC0wLjQ4KTogRXhpc3RlIHVuYSBjb3JyZWxhY2nDs24gbmVnYXRpdmEgbW9kZXJhZGEsIGxvIHF1ZSBzdWdpZXJlIHF1ZSBhIG1lZGlkYSBxdWUgZWwgYcOxbyBjYW1iaWEgKGRlIDIwMjMgYSAyMDI0IGVuIGVzdGUgY2FzbyksIGxhIGxsZWdhZGEgZGUgdHVyaXN0YXMgZXh0cmFuamVyb3MgdGllbmRlIGEgZGlzbWludWlyIGxpZ2VyYW1lbnRlIGVuIGVzdGUgY29uanVudG8gZGUgZGF0b3MuDQoNCiogTGxlZ2FkYV9UdXJfRXh0IC0gQXNpc3RlbnRlcyAoLTAuMTIpOiBIYXkgdW5hIGNvcnJlbGFjacOzbiBuZWdhdGl2YSBtdXkgZMOpYmlsLCBjYXNpIGluZXhpc3RlbnRlLCBlbnRyZSBsYSBsbGVnYWRhIGRlIHR1cmlzdGFzIGV4dHJhbmplcm9zIHkgZWwgbsO6bWVybyBkZSBhc2lzdGVudGVzLg0KDQoqIExsZWdhZGFfVHVyX0V4dCAtICVPY3VwYWNpb25fSG90ZWxlcyAoLTAuMjEpOiBTZSBvYnNlcnZhIHVuYSBsaWdlcmEgdGVuZGVuY2lhIG5lZ2F0aXZhIGVudHJlIGxhIGxsZWdhZGEgZGUgdHVyaXN0YXMgZXh0cmFuamVyb3MgeSBlbCBwb3JjZW50YWplIGRlIG9jdXBhY2nDs24gaG90ZWxlcmEuIEVzdG8gcG9kcsOtYSBpbmRpY2FyIHF1ZSB1bmEgbWF5b3IgbGxlZ2FkYSBkZSB0dXJpc3RhcyBleHRyYW5qZXJvcyBubyBuZWNlc2FyaWFtZW50ZSBzZSB0cmFkdWNlIGVuIHVuYSBtYXlvciBvY3VwYWNpw7NuIGdlbmVyYWwsIHF1aXrDoXMgcG9ycXVlIHNlIGhvc3BlZGFuIGVuIG9wY2lvbmVzIGRpZmVyZW50ZXMgYSBsb3MgaG90ZWxlcyBvIGxhIG9jdXBhY2nDs24gZXN0w6EgbcOhcyBpbmZsdWVuY2lhZGEgcG9yIGVsIHR1cmlzbW8gbmFjaW9uYWwuDQoNCiogTGxlZ2FkYV9UdXJfRXh0IC0gTWVzTm9tYnJlICgtMC4yMik6IEV4aXN0ZSB1bmEgbGlnZXJhIGNvcnJlbGFjacOzbiBuZWdhdGl2YSwgc3VnaXJpZW5kbyBxdWUgY2llcnRvcyBtZXNlcyBjb24gbWF5b3IgbGxlZ2FkYSBkZSB0dXJpc3RhcyBleHRyYW5qZXJvcyBwb2Ryw61hbiBzZXIgZGlmZXJlbnRlcyBkZSBsb3MgbWVzZXMgY29uIHZhbG9yZXMgbcOhcyBhbHRvcyBlbiBvdHJhcyB2YXJpYWJsZXMuDQoNCmBgYHtyfQ0KbGlicmFyeShjb3JycGxvdCkNCg0KIyBDYWxjdWxhciBsYSBtYXRyaXogZGUgY29ycmVsYWNpw7NuDQpjb3JfbWF0cml4IDwtIGNvcihkZl9PQ1ZfbnVtZXJpYywgdXNlID0gImNvbXBsZXRlLm9icyIpDQoNCiMgVmlzdWFsaXphciBsYSBtYXRyaXogZGUgY29ycmVsYWNpw7NuIGNvbiBhanVzdGVzIHBhcmEgbWVqb3JhciBsYSBsZWdpYmlsaWRhZA0KY29ycnBsb3QoDQogIGNvcl9tYXRyaXgsDQogIG1ldGhvZCA9ICJjb2xvciIsICAgICAgICAgICAgICAgICMgTcOpdG9kbyBkZSB2aXN1YWxpemFjacOzbg0KICB0eXBlID0gInVwcGVyIiwgICAgICAgICAgICAgICAgICAjIE1vc3RyYXIgc29sbyBsYSBwYXJ0ZSBzdXBlcmlvciBkZSBsYSBtYXRyaXoNCiAgb3JkZXIgPSAiaGNsdXN0IiwgICAgICAgICAgICAgICAgIyBPcmRlbmFyIHZhcmlhYmxlcyBwb3IgYWdydXBhbWllbnRvIGplcsOhcnF1aWNvDQogIHRsLmNvbCA9ICJibGFjayIsICAgICAgICAgICAgICAgICMgQ29sb3IgZGUgbGFzIGV0aXF1ZXRhcyBkZSB0ZXh0bw0KICB0bC5jZXggPSAwLjYsICAgICAgICAgICAgICAgICAgICAjIFRhbWHDsW8gZGVsIHRleHRvIGRlIGxhcyBldGlxdWV0YXMNCiAgdGwuc3J0ID0gNDUsICAgICAgICAgICAgICAgICAgICAgIyBSb3RhY2nDs24gZGUgbGFzIGV0aXF1ZXRhcw0KICBhZGRDb2VmLmNvbCA9ICJibGFjayIsICAgICAgICAgICAjIENvbG9yIGRlIGxvcyBjb2VmaWNpZW50ZXMgZGUgY29ycmVsYWNpw7NuDQogIG51bWJlci5jZXggPSAwLjYsICAgICAgICAgICAgICAgICMgVGFtYcOxbyBkZSBsb3MgY29lZmljaWVudGVzIGRlIGNvcnJlbGFjacOzbg0KICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoInJlZCIsICJ3aGl0ZSIsICJibHVlIikpKDIwMCkgICMgUGFsZXRhIGRlIGNvbG9yZXMNCikNCg0KYGBgDQoNCg0KIyMjIyBIaXN0b2dyYW1hDQoNCkNvbiBiYXNlIGVuIGxvcyBncsOhZmljb3Mgc2UgZGV0ZXJtaW5hIHRyYW5zZm9ybWFyIGEgbG9nYXJpdG1vIMO6bmljYW1lbnRlIGxhcyB2YXJpYWJsZXMgZGUgQ3VhcnRvcyB5IEFzaXN0ZW50ZXMgZGViaWRvIGEgcXVlIGVzIGVsIHRpcG8gZGUgdHJhbnNmb3JtYWNpw7NuIHF1ZSBtZWpvcmEgbGEgZGlzdHJpYnVjacOzbiBub3JtYWwgZGUgYW1iYXMgZXhwbGljYXRpdmFzOyBtaWVudHJhcyBxdWUgRXZlbnRvcyB5IEFzaXN0ZW50ZXMgc2UgbWFudGllbmVuIGVuIHZhbG9yIG9yaWdpbmFsIHBhcmEgbWVqb3JhciBpbnRlcnByZXRhYmlsaWRhZCBkZSBtb2RlbG9zLg0KDQoqICoqRXZlbnRvczoqKiBTdSBkaXN0cmlidWNpw7NuIGVzdMOhIGxpZ2VyYW1lbnRlIHNlc2dhZGEgYSBsYSBkZXJlY2hhLiBFbCBRLVEgcGxvdCBtdWVzdHJhIGNpZXJ0YSBjdXJ2YXR1cmEgZW4gbG9zIGV4dHJlbW9zLCBwZXJvIG5vIGVzIGV4dHJlbWEuIFBvciBsbyB0YW50bywgdW5hIHRyYW5zZm9ybWFjacOzbiBkZSByYcOteiBjdWFkcmFkYSBwb2Ryw61hIG1lam9yYXIgbGEgbm9ybWFsaWRhZCBzaSBzZSByZXF1aWVyZSB1biBhanVzdGUgbcOhcyBmaW5vIHBlcm8sIG5vIGVzIHRvdGFsbWVudGUgbmVjZXNhcmlvLg0KDQoqICoqQ3VhcnRvczoqKiBUaWVuZSBkaXN0cmlidWNpw7NuIGNsYXJhbWVudGUgc2VzZ2FkYSBhIGxhIGRlcmVjaGEuIEVsIFEtUSBwbG90IG11ZXN0cmEgdW5hIGRlc3ZpYWNpw7NuIG5vdG9yaWEgZGUgbGEgbMOtbmVhIGRlIG5vcm1hbGlkYWQuIFBvciBsbyB0YW50bywgY29udmllbmUgcmVhbGl6YXIgdHJhbnNmb3JtYWNpw7NuIGxvZ2Fyw610bWljYSwgeWEgcXVlIGVzIGxhIHF1ZSBtw6FzIG1lam9yYSBsYSBhcHJveGltYWNpw7NuIGEgdW5hIGRpc3RyaWJ1Y2nDs24gbm9ybWFsLg0KDQoqICoqQXNpc3RlbnRlczoqKiBNdWVzdHJhIGRpc3RyaWJ1Y2nDs24gbXV5IHNlc2dhZGEgYSBsYSBkZXJlY2hhICh2YWxvcmVzIGV4dHJlbW9zKS4gUS1RIHBsb3QgbXVlc3RyYSBjbGFyYSBkZXN2aWFjacOzbi4gTGEgdHJhbnNmb3JtYWNpw7NuIGxvZ2Fyw610bWljYSAsZWpvcmEgc2lnbmlmaWNhdGl2YW1lbnRlIGxhIG5vcm1hbGlkYWQsIGNvbnZpcnRpZW5kbyB1bmEgZGlzdHJpYnVjacOzbiBmdWVydGVtZW50ZSBzZXNnYWRhIGVuIGFsZ28gbcOhcyBzaW3DqXRyaWNvLg0KDQoqICoqRGVycmFtYToqKiBTdSBkaXN0cmlidWNpw7NuIGVzdMOhIGxpZ2VyYW1lbnRlIHNlc2dhZGEgYSBsYSBkZXJlY2hhLiBFbCBRLVEgcGxvdCBtdWVzdHJhIGNpZXJ0YSBjdXJ2YXR1cmEgZW4gbG9zIGV4dHJlbW9zLCBwZXJvIG5vIGVzIGV4dHJlbWEuIFBvciBsbyB0YW50bywgdW5hIHRyYW5zZm9ybWFjacOzbiBkZSByYcOteiBjdWFkcmFkYSBwb2Ryw61hIG1lam9yYXIgbGEgbm9ybWFsaWRhZCBzaSBzZSByZXF1aWVyZSB1biBhanVzdGUgbcOhcyBmaW5vIHBlcm8sIG5vIGVzIHRvdGFsbWVudGUgbmVjZXNhcmlvLg0KDQoNCmBgYHtyfQ0KcGxvdF9oaXN0b2dyYW0oZGZfT0NWX251bWVyaWMpDQpwbG90X25vcm1hbGl0eShkZl9PQ1ZfbnVtZXJpYykNCmBgYA0KDQojIyMjIERlc2NvbXBvc2ljacOzbiBUaW1lIFNlcmllcyBkZSBEZXJyYW1hIEVjb27Ds21pY2EgZGUgRXZlbnRvcyBPQ1YNCg0KKiAqKk9ic2VydmVkIChPYnNlcnZhZG8pOioqIFNlIG9ic2VydmFuIGZsdWN0dWFjaW9uZXMgYSBsbyBsYXJnbyBkZWwgdGllbXBvLCBjb24gcGljb3MgeSB2YWxsZXMgcXVlIHN1Z2llcmVuIHBhdHJvbmVzIHN1YnlhY2VudGVzLiBTZSBvYnNlcnZhIHVuYSB2YXJpYWJpbGlkYWQgY29uc2lkZXJhYmxlIGEgbG8gbGFyZ28gZGVsIHBlcmlvZG8gYW5hbGl6YWRvLCBlbiBsYSBjdWFsIGEgZmluYWxlcyBkZSAyMDI0IHNlIGFwcmVjaWEgdW4gcGljbyBwcm9udW5jaWFkbyBzZWd1aWRvIGRlIHVuYSBjYcOtZGEgY29uc2lkZXJhYmxlLg0KDQoqICoqVHJlbmQgKFRlbmRlbmNpYSk6KiogTGEgdGVuZGVuY2lhIHBhcmVjZSByZWxhdGl2YW1lbnRlIHBsYW5hIGR1cmFudGUgbGEgbWF5b3IgcGFydGUgZGUgMjAyMywgY29uIHVuIGxpZ2VybyBhdW1lbnRvIGhhY2lhIGZpbmFsZXMgZGUgZXNlIGHDsW8uIEEgcHJpbmNpcGlvcyBkZSAyMDI0IHNlIG9ic2VydmEgdW4gaW5jcmVtZW50byBtw6FzIG1hcmNhZG8gZW4gbGEgdGVuZGVuY2lhLCBxdWUgbHVlZ28gcGFyZWNlIGVzdGFiaWxpemFyc2UgbyBpbmNsdXNvIGRpc21pbnVpciBsaWdlcmFtZW50ZSBoYWNpYSBsYSBzZWd1bmRhIG1pdGFkIGRlIDIwMjQuDQoNCiogKipTZWFzb25hbCAoRXN0YWNpb25hbGlkYWQpOioqIFNlIG9ic2VydmFuIGZsdWN0dWFjaW9uZXMgcXVlIHBhcmVjZW4gcmVwZXRpcnNlIGEgbG8gbGFyZ28gZGVsIHRpZW1wbyB5YSBxdWUsIGV4aXN0ZW4gIHBpY29zIGEgbWVkaWFkb3MgZGUgY2FkYSBhw7FvIHkgdmFsbGVzIGhhY2lhIGVsIGluaWNpbyB5IGVsIGZpbmFsIGVuIGxhIGRlcnJhbWEuDQoNCiogKipSYW5kb20gKEFsZWF0b3Jpbyk6KiogTGEgbMOtbmVhIHNlIG1hbnRpZW5lIGNvbnN0YW50ZSBhIGxvIGxhcmdvIGRlbCBwZXJpb2RvLCBsYSBsw61uZWEgaG9yaXpvbnRhbCBjZXJjYSBkZWwgY2VybyBzdWdpZXJlIHF1ZSwgZW4gZ2VuZXJhbCwgZWwgbW9kZWxvIGFkaXRpdm8gaGEgbG9ncmFkbyBjYXB0dXJhciB1bmEgcGFydGUgaW1wb3J0YW50ZSBkZSBsYSB2YXJpYWJpbGlkYWQuDQoNCmBgYHtyfQ0KZGZfT0NWX251bWVyaWMkRmVjaGEgPC0gYXMuRGF0ZShwYXN0ZShkZl9PQ1ZfbnVtZXJpYyRBw7FvLCBkZl9PQ1ZfbnVtZXJpYyRNZXNOb21icmUsICIwMSIsIHNlcCA9ICItIiksIGZvcm1hdCA9ICIlWS0lbS0lZCIpDQoNCmRmX09DVl9udW1lcmljIDwtIGRmX09DVl9udW1lcmljWyFpcy5uYShkZl9PQ1ZfbnVtZXJpYyREZXJyYW1hKSwgXQ0KZGZfT0NWX251bWVyaWMgPC0gZGZfT0NWX251bWVyaWNbb3JkZXIoZGZfT0NWX251bWVyaWMkRmVjaGEpLCBdDQoNCkRlcnJhbWFfdHMgPC0gdHMoZGZfT0NWX251bWVyaWMkRGVycmFtYSwgc3RhcnQgPSBjKG1pbihkZl9PQ1ZfbnVtZXJpYyRBw7FvKSwgbWluKGRmX09DVl9udW1lcmljJE1lcykpLCBmcmVxdWVuY3kgPSAxMikNCg0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpnZ3Bsb3QoZGZfT0NWX251bWVyaWMsIGFlcyh4ID0gRmVjaGEsIHkgPSBEZXJyYW1hKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAiYmx1ZSIsIHNpemUgPSAxKSArDQogIGxhYnModGl0bGUgPSAiU2VyaWUgVGVtcG9yYWwgZGUgRGVycmFtYSIsDQogICAgICAgeCA9ICJGZWNoYSIsDQogICAgICAgeSA9ICJEZXJyYW1hIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZGVjb21wb3NpdGlvbiA8LSBkZWNvbXBvc2UoRGVycmFtYV90cykNCnBsb3QoZGVjb21wb3NpdGlvbikNCmBgYA0KDQoNCiMjIyBIb3RlbGVzDQoNCiMjIyMgU3VtbWFyeQ0KDQpgYGB7cn0NCmxpYnJhcnkob3Blbnhsc3gpDQpkZiA8LSByZWFkLnhsc3goIkhPVEVMRVMtQkQueGxzeCIsIHNoZWV0ID0iSE9URUxFUyIpDQojIE51bG9zIFRvdGFsZXMNCnN1bShpcy5uYShkZikpDQojIE51bG9zIHBvciBDb2x1bW5hDQpnZ19taXNzX3ZhcihkZikNCnN1bW1hcnkoZGYpDQpjb2xuYW1lcyhkZikNCmBgYA0KDQojIyMjIEhpc3RvZ3JhbWFzDQoNCmBgYHtyfQ0KcGxvdF9oaXN0b2dyYW0oZGYpDQpwbG90X25vcm1hbGl0eShkZikNCmBgYA0KDQoNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCiMgRGVmaW5lIGNvbHVtbmFzIGEgdHJhbnNmb3JtYXINCmNvbHNfbG9nIDwtIGMoICJDdWFydG9zX0Rpc3BvbmlibGVzX1Byb21lZGlvIiwgDQogICAgICAgICAgICAgICJEZW5zaWRhZF9PY3VwYWNpw7NuIiwNCiAgICAgICAgICAgICAgIkVzdGFkaWFfUHJvbWVkaW8iKQ0KDQojIENyZWFyIGxhcyBjb2x1bW5hcyB0cmFuc2Zvcm1hZGFzIHkgYWdyZWdhcmxhcyBhbCBkYXRhZnJhbWUgb3JpZ2luYWwNCmRmX2xvZyA8LSBkZiAlPiUNCiAgbXV0YXRlKGFjcm9zcyhhbGxfb2YoY29sc19sb2cpLCB+IGxvZyguICsgMSksIC5uYW1lcyA9ICJ7LmNvbH1fbG9nIikpDQoNCmRmX2xvZyA8LSBkZl9sb2dbLCAhKG5hbWVzKGRmX2xvZykgJWluJSBjb2xzX2xvZyldDQoNCiMgR3LDoWZpY28gZGUgZGlzcGVyc2nDs24NCnBsb3RfaGlzdG9ncmFtKGRmX2xvZykNCnN1bW1hcnkoZGZfbG9nKQ0KYGBgDQoNCiMjIyMgTWF0cml6IGRlIENvcnJlbGFjacOzbg0KDQoqKlZhcmlhYmxlcyBkZSBvY3VwYWNpw7NuIHkgaGFiaXRhY2lvbmVzIG11eSByZWxhY2lvbmFkYXMgY29uIGxhIGRlcnJhbWEqKg0KDQoqIFR1cmlzdGFzX05vY2hlX0V4dCAobm9jaGVzIGRlIGV4dHJhbmplcm9zKSB5IEN1YXJ0b3NfT2N1cGFkb3MgbXVlc3RyYW4gY29ycmVsYWNpb25lcyBjb24gbGEgZGVycmFtYSBkZSByIOKJiCAwLjg4IHkgciDiiYggMC44NiByZXNwZWN0aXZhbWVudGUuDQoNCiogTGxlZ2FkYV9UdXJfRXh0LCBUdXJpc3Rhc19Ob2NoZV9OYWMgeSAlT2N1cGFjaW9uX0hvdGVsZXMgdGFtYmnDqW4gc3VwZXJhbiByIOKJiCAwLjguDQoNCkVzdG8gY29uZmlybWEgcXVlIGEgbcOhcyBvY3VwYWNpw7NuIHkgbGxlZ2FkYXMgZGUgdHVyaXN0YXMsIG1heW9yIGRlcnJhbWEuDQoNCioqTXVsdGljb2xpbmVhbGlkYWQgZW50cmUgcmVncmVzb3JhcyoqDQoNCiAqVHVyaXN0YXNfTm9jaGVfRXh0IHkgVHVyaXN0YXNfTm9jaGVfTmFjIGVzdMOhbiBjYXNpIGNvbGluZWFsZXMgKHIg4omIIDAuOTUpLg0KDQoqIEN1YXJ0b3NfT2N1cGFkb3MsIEN1YXJ0b3NfUmVnaXN0cmFkb3MgeSBDdWFydG9zX0Rpc3BvbmlibGVzIHRhbWJpw6luIHNlIGNvcnJlbGFjaW9uYW4gbXV5IGFsdG8gZW50cmUgc8OtIChyID4gMC43KS4NCg0KUGFyYSBtb2RlbG9zIGRlIHJlZ3Jlc2nDs24gbyBTQVJJTUFYIGNvbnZpZW5lIHF1ZWRhcnNlIGNvbiB1biBzdWJjb25qdW50byAobyB1c2FyIFBDQS9WSUYpIHBhcmEgZXZpdGFyIHJlZHVuZGFuY2lhLihzZSByZWFsaXphIG3DoXMgYWRlbGFudGUpDQoNCg0KYGBge3J9DQpwbG90X2NvcnJlbGF0aW9uKGRmX2xvZykNCg0KZGZfbnVtZXJpY28gPC0gZGZfbG9nW3NhcHBseShkZl9sb2csIGlzLm51bWVyaWMpXQ0KDQojIDIuIENhbGN1bGEgbGEgbWF0cml6IGRlIGNvcnJlbGFjacOzbg0KbWF0cml6X2NvciA8LSBjb3IoZGZfbnVtZXJpY28sIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKQ0KDQpjb3JycGxvdCggbWF0cml6X2NvciwNCiAgbWV0aG9kID0gImNvbG9yIiwgICAgICAgICAgICAjIE1hcGEgZGUgY2Fsb3INCiAgdHlwZSA9ICJ1cHBlciIsICAgICAgICAgICAgICAjIFNvbG8gdHJpw6FuZ3VsbyBzdXBlcmlvcg0KICBhZGRDb2VmLmNvbCA9ICJibGFjayIsICAgICAgICMgTW9zdHJhciBjb2VmaWNpZW50ZXMgZW4gY29sb3IgbmVncm8NCiAgbnVtYmVyLmNleCA9IDAuNiwgICAgICAgICAgICAjIFRhbWHDsW8gZGVsIG7Dum1lcm8NCiAgdGwuY2V4ID0gMC4zNSwgICAgICAgICAgICAgICAgIyBUYW1hw7FvIGRlIGxhcyBldGlxdWV0YXMNCiAgdGwuY29sID0gImJsYWNrIiwgICAgICAgICAgICAjIENvbG9yIGRlbCB0ZXh0bw0KICBkaWFnID0gRkFMU0UgICAgICAgICAgICAgICAgICMgT2N1bHRhIGxhIGRpYWdvbmFsICgxcykNCikNCmBgYA0KDQoNCioqQ29ycmVsYWNpb25lcyBtb2RlcmFkYXMqKg0KDQoqIERlbnNpZGFkX09jdXBhY2nDs25fbG9nIChyIOKJiCAwLjMyKSB5ICVPY3VwYWNpb25fSG90ZWxlcyAociDiiYggMC41MCkgc8OtIG11ZXN0cmFuIHVuYSBhc29jaWFjacOzbiwgcGVybyBkZSBpbnRlbnNpZGFkIG1lZGlhLg0KDQoNCioqVmFyaWFibGVzIGNvbiBjb3JyZWxhY2nDs24gZnVlcnRlIChyID4gMC43NSkqKg0KDQoqIEN1YXJ0b3NfRGlzcG9uaWJsZXMgKHIg4omIIDAuNzYpDQoNCiogTGxlZ2FkYV9UdXJfRXh0IChyIOKJiCAwLjc4KQ0KDQoqIFR1cmlzdGFzX05vY2hlX0V4dCAociDiiYggMC44MikNCg0KKiBJbXBfSG9zcGVkYWplIChyIOKJiCAwLjg0KQ0KDQoqIEN1YXJ0b3NfT2N1cGFkb3MgKHIg4omIIDAuODQpDQoNCiogVHVyaXN0YXNfTm9jaGVfTmFjIChyIOKJiCAwLjg2KQ0KDQpEaWNoYXMgdmFyaWFibGVzIGNhcHR1cmFuIGxhIG1heW9yIHBhcnRlIGRlIGxhIHZhcmlhY2nDs24gZGUgbGEgZGVycmFtYS4gQ3VhbnRvcyBtw6FzIGN1YXJ0b3Mgb2N1cGFkb3MgeSBtw6FzIG5vY2hlcyBkZSB0dXJpc3RhcyAobmFjaW9uYWxlcyB5IGV4dHJhbmplcm9zKSwgbWF5b3IgZGVycmFtYS4NCg0KYGBge3J9DQojIENhbGN1bGFyIGxhIG1hdHJpeiBkZSBjb3JyZWxhY2nDs24NCmNvcnJlbGFjaW9uZXMgPC0gY29yKGRmX251bWVyaWNvLCB1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIikNCg0KIyBFeHRyYWVyIGxhcyBjb3JyZWxhY2lvbmVzIGNvbiAlT2N1cGFjaW9uX0hvdGVsZXMNCmNvcl9vY3VwYWNpb24gPC0gY29ycmVsYWNpb25lc1siRGVycmFtYV9FY29ub21pY2FfRXN0X21kcCIsIF0NCmNvcl9vY3VwYWNpb24gPC0gY29yX29jdXBhY2lvblshbmFtZXMoY29yX29jdXBhY2lvbikgJWluJSAiRGVycmFtYV9FY29ub21pY2FfRXN0X21kcCJdDQoNCmRmX2NvciA8LSBkYXRhLmZyYW1lKA0KICBWYXJpYWJsZSA9IG5hbWVzKGNvcl9vY3VwYWNpb24pLA0KICBDb3JyZWxhY2lvbiA9IGNvcl9vY3VwYWNpb24NCikgJT4lDQogIGFycmFuZ2UoZGVzYyhhYnMoQ29ycmVsYWNpb24pKSkgJT4lDQogIG11dGF0ZShWYXJpYWJsZSA9IGZhY3RvcihWYXJpYWJsZSwgbGV2ZWxzID0gVmFyaWFibGUpKQ0KDQpnZ3Bsb3QoZGZfY29yLCBhZXMoeCA9IFZhcmlhYmxlLCB5ID0gQ29ycmVsYWNpb24pKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInN0ZWVsYmx1ZSIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKENvcnJlbGFjaW9uLCAyKSksIGhqdXN0ID0gaWZlbHNlKGRmX2NvciRDb3JyZWxhY2lvbiA+IDAsIC0wLjEsIDEuMSkpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJDb3JyZWxhY2nDs24gY29uIERlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHAiLA0KICAgIHkgPSAiQ29lZmljaWVudGUgZGUgY29ycmVsYWNpw7NuIiwNCiAgICB4ID0gIiINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCiMjIyMgRGVzY29tcG9zaWNpw7NuIERlcnJhbWEgSW5kdXN0cmlhIEhvdGVsZXJhDQoNClNlIGxvZ3JhIHZpc3VhbGl6YXIgZW4gbGEgc2VyaWUgZGUgdGllbXBvIGRlIGxhIHZhcmlhYmxlIGRlIGludGVyw6lzIHF1ZSBwYXJ0aXIgZGUgMjAyMuKAkzIwMjMgbG9zIHZhbG9yZXMgZXhoaWJlbiBwaWNvcyBtw6FzIHByb251bmNpYWRvcyAoMiA1MDDigJMyIDgwMCBtZHApIHBlcm8gdGFtYmnDqW4gb3NjaWxhY2lvbmVzIG3DoXMgYW1wbGlhcywgbG8gcXVlIHN1Z2llcmUgdW5hIG1heW9yIGluY2VydGlkdW1icmUgKHBvc2libGVtZW50ZSBwb3IgdmFyaWFjaW9uZXMgZW4gaW1wdWVzdG9zLCBpbmZsYWNpw7NuLCBldGMuKS4NCg0KRGUgbW9kbyBxdWUgc2UgbG9ncmEgZGVzdGFjYXIgbG8gc2lndWllbnRlOg0KDQoqIFVuIGNyZWNpbWllbnRvIGVzdHJ1Y3R1cmFsIGRlIGxhIGRlcnJhbWEgZGUgY2FzaSB1biBvcmRlbiBkZSBtYWduaXR1ZCBlbiAyMCBhw7Fvcy4NCg0KKiBVbiBzaG9jayBuZWdhdGl2byBwdW50dWFsIGVuIDIwMjAgeSB1bmEgcmVjdXBlcmFjacOzbiBtw6FzIGZ1ZXJ0ZSBhw7puLg0KDQoqIFRyYW5zaWNpw7NuIGEgdW5hIGV0YXBhIGRlIG1heW9yIHZvbGF0aWxpZGFkIHkgbml2ZWxlcyByw6ljb3JkIGVuIGxvcyDDumx0aW1vcyBtZXNlcy4NCg0KYGBge3J9DQpkZl9sb2cgPC0gZGZfbG9nWyFpcy5uYShkZl9sb2ckIkRlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHAiKSwgXQ0KDQpkZXJyYW1hX3RzIDwtIHRzKGRmX2xvZyQiRGVycmFtYV9FY29ub21pY2FfRXN0X21kcCIsIHN0YXJ0ID0gYygyMDA0LCAxKSwgZW5kID0gYygyMDI0LCAxMiksIGZyZXF1ZW5jeSA9IDEyKQ0KcGxvdChkZXJyYW1hX3RzLCB0eXBlID0gImwiLCBjb2wgPSAiZGFya2dyZWVuIiwgbHdkID0gMiwNCiAgICAgeGxhYiA9ICJBw7FvIiwgeWxhYiA9ICJEZXJyYW1hIEVjb27Ds21pY2EiLA0KICAgICBtYWluID0gIlNlcmllIFRlbXBvcmFsIGRlIERlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHAiKQ0KYGBgDQoNCmBgYHtyfQ0KbGVuZ3RoKGRlcnJhbWFfdHMpDQpmcmVxdWVuY3koZGVycmFtYV90cykNCmVuZChkZXJyYW1hX3RzKQ0KDQpkZWNvbXBvc2l0aW9uIDwtIGRlY29tcG9zZShkZXJyYW1hX3RzKQ0KcGxvdChkZWNvbXBvc2l0aW9uKQ0KDQpkZXNjb21wX3N0bCA8LSBzdGwoZGVycmFtYV90cywgcy53aW5kb3cgPSAicGVyaW9kaWMiKQ0KcGxvdChkZXNjb21wX3N0bCkNCmBgYA0KDQojIyMjIyBCb3hwbG90cw0KDQoqKk1lc2VzIGRlIOKAnHRlbXBvcmFkYSBhbHRh4oCdKioNCg0KKiBBYnJpbCAobWVzIDQpIHkgbm92aWVtYnJlIChtZXMgMTEpIG11ZXN0cmFuIGxhcyBtZWRpYW5hcyBtw6FzIGVsZXZhZGFzIChhbHJlZGVkb3IgZGUgNzUw4oCTODAwIG1kcCkgeSBjdWFydGlsZXMgc3VwZXJpb3JlcyBhbHRvcywgbG8gcXVlIHN1Z2llcmUgcGljb3Mgc2lzdGVtw6F0aWNvcyBlbiBwcmltYXZlcmEgeSBqdXN0byBhbnRlcyBkZSBmaW4gZGUgYcOxby4NCg0KKipNZXNlcyBkZSDigJx0ZW1wb3JhZGEgYmFqYeKAnSoqDQoNCiogRW5lcm8gKDEpIGVzIGVsIG3DoXMgYmFqbyBlbiBtZWRpYW5hLCBjb24gcG9jb3Mgb3V0bGllcnMsIHJlZmxlamFuZG8gZWwgdMOtcGljbyBiYWrDs24gdHJhcyBsYSB0ZW1wb3JhZGEgbmF2aWRlw7FhLg0KDQoqIFNlcHRpZW1icmUgKDkpIHkgZmVicmVybyAoMikgdGFtYmnDqW4gcHJlc2VudGFuIG1lZGlhbmFzIHJlZHVjaWRhcyAsIHBvc2libGVtZW50ZSBwb3IgdmFjYWNpb25lcyBjb3J0YXMgbyBwZXJpb2RvcyBkZSBiYWphIGFjdGl2aWRhZCB0dXLDrXN0aWNhLg0KDQoqKkFjdGl2aWRhZCBBdMOtcGljYSoqDQoNCiogTWVzZXMgY29tbyBhYnJpbCwgbWF5byAoNSkgeSBkaWNpZW1icmUgdGllbmVuIHZhcmlvcyBwdW50b3MgYXTDrXBpY29zIG11eSBwb3IgZW5jaW1hIGRlIGxhIGNhamEsIHF1ZSByZWZsZWphbiBldmVudG9zIGV4dHJhb3JkaW5hcmlvcyAoY29uZ3Jlc29zLCBmZXJpYWRvcyBsYXJnb3MsIHByb21vY2lvbmVzKS4NCg0KYGBge3J9DQpib3hwbG90KGRlcnJhbWFfdHMgfiBjeWNsZShkZXJyYW1hX3RzKSwNCiAgICAgICAgeGxhYiA9ICJNZXMiLCB5bGFiID0gIkRlcnJhbWEiLA0KICAgICAgICBtYWluID0gIkVzdGFjaW9uYWxpZGFkIG1lbnN1YWwiLCBjb2wgPSAibGlnaHRibHVlIikNCg0KYGBgDQoNCg0KIyMjIFR1cmlzbW8gZGUgRXZlbnRvcyBkZSBFbnRyZXRlbmltaWVudG8gKFBhbCBOb3J0ZSkNCg0KIyMjIyBTdW1tYXJ5DQoNCkEgdHJhdsOpcyBkZWwgYW7DoWxpc2lzIGRlIGFscmVkZWRvciBkZSAxODAgZW5jdWVzdGFzIHJlY29sZWN0YWRhcyBlbnRyZSBhc2lzdGVudGVzIHJlYWxlcyBhbCBmZXN0aXZhbCBUZWNhdGUgUGFsIE5vcnRlIDIwMjUsIHNlIGJ1c2NhICBpZGVudGlmaWNhciB1biBwZXJmaWwgY2xhcm8gZGVsIG1lcmNhZG8gYWN0dWFsLCBhc8OtIGNvbW8gbGFzIG9wb3J0dW5pZGFkZXMgZXN0cmF0w6lnaWNhcyBwYXJhIGVzY2FsYXIgbGEgcmVudGFiaWxpZGFkIGRlbCBldmVudG8gbWVkaWFudGUgc2VnbWVudGFjacOzbiBkZSBww7pibGljb3MsIG9wdGltaXphY2nDs24gZGUgc2VydmljaW9zIHkgcGVyc29uYWxpemFjacOzbiBkZSBleHBlcmllbmNpYXMuIA0KDQpDYWJlIG1lbmNpb25hciBxdWUgZXN0ZSBhbsOhbGlzaXMgbm8gc29sbyBwZXJtaXRlIGVudGVuZGVyIGFsIHDDumJsaWNvIGFjdHVhbCBkZWwgZmVzdGl2YWwsIHNpbm8gdGFtYmnDqW4gaWRlbnRpZmljYXIgY29uIGNsYXJpZGFkIGTDs25kZSBlc3TDoW4gbGFzIGFyZWFzIGRlIGNyZWNpbWllbnRvIGVjb27Ds21pY28geSBxdcOpIGFjY2lvbmVzIHB1ZWRlbiBkZXRvbmFyIHVuYSBtYXlvciBjb252ZXJzacOzbiBkZSB2YWxvciBwb3IgYXNpc3RlbnRlLCBzdXN0ZW50YW5kbyBhc8OtIHVuYSBlc3RyYXRlZ2lhIGRlIG1hcmtldGluZyBiYXNhZGEgZW4gZGF0b3MgcmVhbGVzIHkgcGVyZmlsZXMgcHJlZGljdGl2b3MuDQoNCkVuIGxhIHNpZ3VpZW50ZSBzZWNjacOzbiB1bmlmaWNhbW9zIGxhcyBkb3MgYmFzZXMgZGUgZGF0b3Mgb3JpZ2luYWxlcywgZWxpbWluYW1vcyBjb2x1bW5hcyBpcnJlbGV2YW50ZXMgY29tbyBJRCBvIGZlY2hhcyBkZSBjYXB0dXJhLHkgcmVhbGl6YW1vcyBsaW1waWV6YSBlbiBsb3MgZGF0b3MgbW9uZXRhcmlvcyBwYXJhIHRyYW5zZm9ybWFybG9zIGVuIHZhcmlhYmxlcyBudW3DqXJpY2FzLiANCg0KQWRlbcOhcywgY29ycmVnaW1vcyBlcnJvcmVzIGV2aWRlbnRlcyBkZSBjYXB0dXJhIGNvbW8gbW9udG9zIGNvbmNhdGVuYWRvcyBzaW4gc2VwYXJhY2nDs24sIHkgY29udmVydGltb3MgbG9zIGTDrWFzIGRlIGFzaXN0ZW5jaWEgZGVzZGUgdGV4dG8gYSB2YWxvciBudW3DqXJpY28gKDEsIDIgbyAzKS4gRmluYWxtZW50ZSwgc2UgY2FsY3VsYSBlbCBnYXN0byB0b3RhbCBwb3IgcGVyc29uYSBjb25zaWRlcmFuZG8gYm9sZXRvcywgYWxvamFtaWVudG8seSBnYXN0b3MgZGlhcmlvcyBwb3IgYWxpbWVudG9zIHkgdHJhbnNwb3J0ZSBtdWx0aXBsaWNhZG9zIHBvciBsb3MgZMOtYXMgYXNpc3RpZG9zLg0KDQpgYGB7cn0NCmRmMSA8LSByZWFkX2V4Y2VsKCJFbmN1ZXN0YSBUdXJpc21vIFBhbE5vcnRlIDIwMjUgQ2FwdHVyYXMgSS54bHN4IikNCmRmMiA8LSByZWFkX2V4Y2VsKCJFbmN1ZXN0YSBUdXJpc21vIFBhbE5vcnRlIDIwMjUgQ2FwdHVyYXMgSUkueGxzeCIpDQpkZl9wYWxub3J0ZSA8LSBiaW5kX3Jvd3MoZGYxLCBkZjIpDQoNCmRmX3BhbG5vcnRlIDwtIGRmX3BhbG5vcnRlICU+JSBzZWxlY3QoLWMoSUQsIGBTdGFydCB0aW1lYCwgYENvbXBsZXRpb24gdGltZWAsIEVtYWlsLCBOYW1lKSkNCg0KdG9fbnVtZXJpYyA8LSBmdW5jdGlvbih4KSB7DQogIHggPC0gZ3N1YigiW14wLTkuXSIsICIiLCB4KQ0KICB4W3ggPT0gIiJdIDwtIE5BDQogIGFzLm51bWVyaWMoeCkNCn0NCg0KZGZfcGFsbm9ydGUgPC0gZGZfcGFsbm9ydGUgJT4lDQogIG11dGF0ZSgNCiAgICBib2xldG9zID0gdG9fbnVtZXJpYyhgwr9DdcOhbCBmdWUgc3UgcHJlc3VwdWVzdG8gdG90YWwgcGFyYSBsb3MgYm9sZXRvcz9gKSwNCiAgICBhbG9qYW1pZW50byA9IHRvX251bWVyaWMoYMK/Q3XDoWwgZXMgc3UgcHJlc3VwdWVzdG8gZXN0aW1hZG8gcGFyYSBhbG9qYW1pZW50byBkdXJhbnRlIGVsIGZlc3RpdmFsP2ApLA0KICAgIGFsaW1lbnRvcyA9IHRvX251bWVyaWMoYMK/Q3XDoW50byBwbGFuZWEgZ2FzdGFyIGVuIGFsaW1lbnRvcyB5IGJlYmlkYXMgZGVudHJvIGRlbCBmZXN0aXZhbCAocG9yIGTDrWEpP2ApLA0KICAgIHRyYW5zcG9ydGUgPSB0b19udW1lcmljKGDCv0N1w6FudG8gcGxhbmVhIGdhc3RhciBlbiB0cmFuc3BvcnRlIHBvciBkw61hIChpbmNsdXllIHRheGlzLCBVYmVyLCBjb21idXN0aWJsZSwgZXN0YWNpb25hbWllbnRvLCBldGMuKT9gKQ0KICApICU+JQ0KICBkcm9wX25hKGJvbGV0b3MsIGFsb2phbWllbnRvLCBhbGltZW50b3MsIHRyYW5zcG9ydGUpICU+JQ0KICBtdXRhdGUoDQogICAgYm9sZXRvcyA9IGlmZWxzZShib2xldG9zID4gMTAwMDAwMCwgMzAwMCwgYm9sZXRvcyksDQogICAgYWxvamFtaWVudG8gPSBpZmVsc2UoYWxvamFtaWVudG8gPiA1MDAwMCwgMTUwMCwgYWxvamFtaWVudG8pLA0KICAgIGFsaW1lbnRvcyA9IGlmZWxzZShhbGltZW50b3MgPiAxMDAwMDAsIDc1MCwgYWxpbWVudG9zKSwNCiAgICB0cmFuc3BvcnRlID0gaWZlbHNlKHRyYW5zcG9ydGUgPiAxMDAwMCwgNTAwLCB0cmFuc3BvcnRlKSwNCiAgICBkaWFzID0gY2FzZV93aGVuKA0KICAgICAgYMK/Q3XDoW50b3MgZMOtYXMgcGxhbmVhIGFzaXN0aXIgYWwgZmVzdGl2YWw/YCA9PSAiVG9kbyBlbCBldmVudG8gKDMgZMOtYXMpIiB+IDMsDQogICAgICBgwr9DdcOhbnRvcyBkw61hcyBwbGFuZWEgYXNpc3RpciBhbCBmZXN0aXZhbD9gID09ICJEb3MgZMOtYXMiIH4gMiwNCiAgICAgIGDCv0N1w6FudG9zIGTDrWFzIHBsYW5lYSBhc2lzdGlyIGFsIGZlc3RpdmFsP2AgPT0gIlPDs2xvIHVuIGTDrWEiIH4gMSwNCiAgICAgIFRSVUUgfiBOQV9yZWFsXw0KICAgICksDQogICAgZ2FzdG9fdG90YWwgPSBib2xldG9zICsgYWxvamFtaWVudG8gKyAoYWxpbWVudG9zICsgdHJhbnNwb3J0ZSkgKiBkaWFzDQogICkNCg0Kc3VtbWFyeShkZl9wYWxub3J0ZSkNCmBgYA0KDQojIyMjIEJveHBsb3RzDQoNCiogKipBbGltZW50b3M6KiogTGEgbWF5b3LDrWEgZGUgbG9zIHZpc2l0YW50ZXMgZ2FzdGFyb24gZW50cmUgJDc1MCB5ICQxMDAwIGVuIGFsaW1lbnRvcywgY29uIHVuYSBtZWRpYW5hIGNlcmNhbmEgYSBsb3MgJDgwMC4gTm8gc2Ugb2JzZXJ2YW4gdmFsb3JlcyBhdMOtcGljb3Mgc2lnbmlmaWNhdGl2b3MgcG9yIGVuY2ltYSBkZWwgYmlnb3RlIHN1cGVyaW9yLg0KDQoqICoqQWxvamFtaWVudG86KiogRWwgNTAlIGNlbnRyYWwgZGUgbG9zIHZpc2l0YW50ZXMgZ2FzdMOzIGVudHJlIGFwcm94aW1hZGFtZW50ZSAkMTUwMCB5ICQzMDAwIGVuIGFsb2phbWllbnRvLCBjb24gdW5hIG1lZGlhbmEgYWxyZWRlZG9yIGRlIGxvcyAkMjAwMC4gSGF5IGFsZ3Vub3MgdmFsb3JlcyBhdMOtcGljb3MgcG9yIGRlYmFqbyBkZWwgYmlnb3RlIGluZmVyaW9yLCBsbyBxdWUgc3VnaWVyZSBxdWUgYWxndW5vcyB2aXNpdGFudGVzIGVuY29udHJhcm9uIG9wY2lvbmVzIGRlIGFsb2phbWllbnRvIGNvbnNpZGVyYWJsZW1lbnRlIG3DoXMgZWNvbsOzbWljYXMuDQoNCiogKipCb2xldG9zOioqIExhIG1heW9yw61hIGRlIGxvcyB2aXNpdGFudGVzIGdhc3Rhcm9uIGVudHJlICQzMDAwIHkgJDQwMDAgZW4gc3VzIGJvbGV0b3MsIGNvbiB1bmEgbWVkaWFuYSBqdXN0byBwb3IgZW5jaW1hIGRlIGxvcyAkMzAwMC4gTm8gc2Ugb2JzZXJ2YW4gdmFsb3JlcyBhdMOtcGljb3Mgc2lnbmlmaWNhdGl2b3MuDQoNCiogKipEw61hczoqKiBFbCA1MCUgZGUgbG9zIHZpc2l0YW50ZXMgYXNpc3Rpw7MgZW50cmUgMiB5IDMgZMOtYXMsIGNvbiB1bmEgbWVkaWFuYSBkZSAyIGTDrWFzLiBIYXkgYWxndW5vcyB2aXNpdGFudGVzIHF1ZSBzb2xvIGFzaXN0aWVyb24gMSBkw61hLg0KDQoqICoqR2FzdG8gVG90YWw6KiogRWwgNTAlIGNlbnRyYWwgZGUgbG9zIHZpc2l0YW50ZXMgdHV2byB1biBnYXN0byB0b3RhbCBlbnRyZSBhcHJveGltYWRhbWVudGUgJDY1MDAgeSAkMTAwMDAsIGNvbiB1bmEgbWVkaWFuYSBjZXJjYW5hIGEgbG9zICQ4MDAwLiBTZSBvYnNlcnZhbiBhbGd1bm9zIHZhbG9yZXMgYXTDrXBpY29zIHBvciBlbmNpbWEgZGVsIGJpZ290ZSBzdXBlcmlvciwgbG8gcXVlIGluZGljYSBxdWUgaHVibyB2aXNpdGFudGVzIGNvbiBnYXN0b3MgdG90YWxlcyBjb25zaWRlcmFibGVtZW50ZSBtw6FzIGFsdG9zLg0KDQoqICoqVHJhbnNwb3J0ZToqKiBMYSBtYXlvcsOtYSBkZSBsb3MgdmlzaXRhbnRlcyBnYXN0YXJvbiBlbnRyZSAkNTAwIHkgJDY1MCBlbiB0cmFuc3BvcnRlLCBjb24gdW5hIG1lZGlhbmEgYWxyZWRlZG9yIGRlIGxvcyAkNTAwLiBObyBzZSBvYnNlcnZhbiB2YWxvcmVzIGF0w61waWNvcyBzaWduaWZpY2F0aXZvcy4NCg0KYGBge3J9DQpkZl9wYWxub3J0ZV9udW1lcmljIDwtIGRmX3BhbG5vcnRlW3NhcHBseShkZl9wYWxub3J0ZSwgaXMubnVtZXJpYyldDQpsaWJyYXJ5KHJlc2hhcGUyKQ0KZGZfbG9uZzIgPC0gZGZfcGFsbm9ydGVfbnVtZXJpYyAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gInZhcmlhYmxlIiwgdmFsdWVzX3RvID0gInZhbG9yIikNCg0KIyBDcmVhciBsb3MgYm94cGxvdHMgY29uIGVzY2FsYXMgaW5kZXBlbmRpZW50ZXMgZW4gZWwgZWplIFkNCmdncGxvdChkZl9sb25nMiwgYWVzKHggPSAiIiwgeSA9IHZhbG9yKSkgKw0KICBnZW9tX2JveHBsb3QoZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvciA9ICJkYXJrYmx1ZSIpICsNCiAgZmFjZXRfd3JhcCh+IHZhcmlhYmxlLCBzY2FsZXMgPSAiZnJlZV95IikgKw0KICBsYWJzKHRpdGxlID0gIkJveHBsb3RzIHBvciB2YXJpYWJsZSBudW3DqXJpY2EiLA0KICAgICAgIHggPSBOVUxMLA0KICAgICAgIHkgPSBOVUxMKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCiMjIyMgTWF0cml6IGRlIENvcnJlbGFjacOzbg0KDQpMYXMgY29ycmVsYWNpb25lcyBtw6FzIGZ1ZXJ0ZXMgY29uIGVsIGdhc3RvIHRvdGFsIHNvbiBlbCBhbG9qYW1pZW50bywgbG9zIGJvbGV0b3MgeSBlbCBuw7ptZXJvIGRlIGTDrWFzIGRlIGFzaXN0ZW5jaWEuIEVzdG8gc3VnaWVyZSBxdWUgZXN0YXMgdmFyaWFibGVzIHNvbiBsb3MgcHJpbmNpcGFsZXMgaW1wdWxzb3JlcyBkZWwgZ2FzdG8gdG90YWwgZGUgbG9zIHZpc2l0YW50ZXMuIE1pZW50cmFzIHF1ZSwgZWwgdHJhbnNwb3J0ZSBtdWVzdHJhIGNvcnJlbGFjaW9uZXMgZMOpYmlsZXMgY29uIHRvZGFzIGxhcyBkZW3DoXMgdmFyaWFibGVzIGRlIGdhc3RvOg0KDQoqIETDrWFzIHZzLiBHYXN0byBUb3RhbCAoMC43OCk6IEVzdGEgZXMgbGEgY29ycmVsYWNpw7NuIG3DoXMgZnVlcnRlLiBJbmRpY2EgcXVlIGV4aXN0ZSB1bmEgcmVsYWNpw7NuIHBvc2l0aXZhIG11eSBtYXJjYWRhIGVudHJlIGVsIG7Dum1lcm8gZGUgZMOtYXMgcXVlIHVuIHZpc2l0YW50ZSBhc2lzdGUgYWwgZmVzdGl2YWwgeSBlbCBnYXN0byB0b3RhbCBxdWUgcmVhbGl6YS4gQ3VhbnRvcyBtw6FzIGTDrWFzIHBlcm1hbmV6Y2EgYWxndWllbiBlbiBlbCBUZWNhdGUgUGFsIE5vcnRlLCBzaWduaWZpY2F0aXZhbWVudGUgbWF5b3Igc2Vyw6Egc3UgZ2FzdG8gdG90YWwuIEVzdG8gZXMgYmFzdGFudGUgbMOzZ2ljbywgeWEgcXVlIGFsIGVzdGFyIG3DoXMgdGllbXBvLCBpbmN1cnJlIGVuIGdhc3RvcyBkZSBhbGltZW50YWNpw7NuLCB0cmFuc3BvcnRlIChzaSBhcGxpY2EpLCB5IHBvc2libGVtZW50ZSBtw6FzIGVuIG90cmFzIGNhdGVnb3LDrWFzLg0KDQoqIEFsb2phbWllbnRvIHZzLiBHYXN0byBUb3RhbCAoMC43NSk6IEVzdGEgZXMgbGEgc2VndW5kYSBjb3JyZWxhY2nDs24gbcOhcyBmdWVydGUuIE11ZXN0cmEgdW5hIHJlbGFjacOzbiBwb3NpdGl2YSBjb25zaWRlcmFibGUgZW50cmUgZWwgZ2FzdG8gZW4gYWxvamFtaWVudG8geSBlbCBnYXN0byB0b3RhbC4gQXF1ZWxsb3MgdmlzaXRhbnRlcyBxdWUgaW52aWVydGVuIG3DoXMgZW4gc3UgaG9zcGVkYWplIHRpZW5kZW4gYSB0ZW5lciB1biBnYXN0byB0b3RhbCBtdWNobyBtYXlvci4gRWwgYWxvamFtaWVudG8gc3VlbGUgc2VyIHVubyBkZSBsb3MgcnVicm9zIG3DoXMgY29zdG9zb3MgZW4gdW4gdmlhamUsIHBvciBsbyBxdWUgZXN0YSBmdWVydGUgY29ycmVsYWNpw7NuIHRpZW5lIHNlbnRpZG8uDQoNCiogQm9sZXRvcyB2cy4gR2FzdG8gVG90YWwgKDAuNzIpOiBFeGlzdGUgdW5hIGNvcnJlbGFjacOzbiBwb3NpdGl2YSB0YW1iacOpbiBmdWVydGUgZW50cmUgZWwgZ2FzdG8gZW4gYm9sZXRvcyB5IGVsIGdhc3RvIHRvdGFsLiBFbCBjb3N0byBkZSBsb3MgYm9sZXRvcyBwYXJhIGVsIGZlc3RpdmFsIHJlcHJlc2VudGEgdW5hIHBhcnRlIGltcG9ydGFudGUgZGVsIHByZXN1cHVlc3RvIGdlbmVyYWwgZGVsIGV2ZW50by4gUG9yIGxvIHRhbnRvLCBsb3MgdmlzaXRhbnRlcyBxdWUgYWRxdWllcmVuIGJvbGV0b3MgbcOhcyBjYXJvcyAocG9zaWJsZW1lbnRlIGNvbiBtw6FzIGJlbmVmaWNpb3MgbyBwYXJhIG3DoXMgZMOtYXMsIGF1bnF1ZSBlc3RvIMO6bHRpbW8gc2UgdmUgcmVmbGVqYWRvIGVuIGxhIGNvcnJlbGFjacOzbiBkZSAiZMOtYXMiKSB0aWVuZGVuIGEgdGVuZXIgdW4gZ2FzdG8gdG90YWwgbWF5b3IuDQoNCmBgYHtyfQ0KbGlicmFyeShjb3JycGxvdCkNCmNvcl9tYXRyaXgzIDwtIGNvcihkZl9wYWxub3J0ZV9udW1lcmljLCB1c2UgPSAiY29tcGxldGUub2JzIikNCg0KY29ycnBsb3QoDQogIGNvcl9tYXRyaXgzLA0KICBtZXRob2QgPSAiY29sb3IiLCAgICAgICAgICAgICAgICAjIE3DqXRvZG8gZGUgdmlzdWFsaXphY2nDs24NCiAgdHlwZSA9ICJ1cHBlciIsICAgICAgICAgICAgICAgICAgIyBNb3N0cmFyIHNvbG8gbGEgcGFydGUgc3VwZXJpb3IgZGUgbGEgbWF0cml6DQogIG9yZGVyID0gImhjbHVzdCIsICAgICAgICAgICAgICAgICMgT3JkZW5hciB2YXJpYWJsZXMgcG9yIGFncnVwYW1pZW50byBqZXLDoXJxdWljbw0KICB0bC5jb2wgPSAiYmxhY2siLCAgICAgICAgICAgICAgICAjIENvbG9yIGRlIGxhcyBldGlxdWV0YXMgZGUgdGV4dG8NCiAgdGwuY2V4ID0gMC42LCAgICAgICAgICAgICAgICAgICAgIyBUYW1hw7FvIGRlbCB0ZXh0byBkZSBsYXMgZXRpcXVldGFzDQogIHRsLnNydCA9IDQ1LCAgICAgICAgICAgICAgICAgICAgICMgUm90YWNpw7NuIGRlIGxhcyBldGlxdWV0YXMNCiAgYWRkQ29lZi5jb2wgPSAiYmxhY2siLCAgICAgICAgICAgIyBDb2xvciBkZSBsb3MgY29lZmljaWVudGVzIGRlIGNvcnJlbGFjacOzbg0KICBudW1iZXIuY2V4ID0gMC42LCAgICAgICAgICAgICAgICAjIFRhbWHDsW8gZGUgbG9zIGNvZWZpY2llbnRlcyBkZSBjb3JyZWxhY2nDs24NCiAgY29sID0gY29sb3JSYW1wUGFsZXR0ZShjKCJyZWQiLCAid2hpdGUiLCAiYmx1ZSIpKSgyMDApICAjIFBhbGV0YSBkZSBjb2xvcmVzDQopDQpgYGANCg0KDQoNCiMjIyMgRGlzdHJpYnVjacOzbiBwb3IgRWRhZA0KDQpTZSBvYnNlcnZhIHF1ZSBlbCBtZXJjYWRvIGRvbWluYW50ZSBkZWwgZmVzdGl2YWwgc29uIGrDs3ZlbmVzIGFkdWx0b3MsIGVzcGVjaWFsbWVudGUgYXF1ZWxsb3MgZW50cmUgMjYgeSAzNSBhw7Fvcy4gRXN0byBwdWVkZSBndWlhciBkZWNpc2lvbmVzIGRlIG1hcmtldGluZywgYWxpYW56YXMgY29tZXJjaWFsZXMsIHByb2R1Y3RvcyB5IGV4cGVyaWVuY2lhcyBlbmZvY2FkYXMgYSBlc2UgcmFuZ28uDQoNCiAgKiBMYSBtYXlvcsOtYSBkZSBsb3MgYXNpc3RlbnRlcyBhbCBmZXN0aXZhbCBzZSBlbmN1ZW50cmEgZW4gZWwgcmFuZ28gZGUgZWRhZCBlbnRyZSAyNiB5IDM1IGHDsW9zLCBjb24gbcOhcyBkZSA0MCByZWdpc3Ryb3MuDQogIA0KICAqIEVsIHNlZ3VuZG8gZ3J1cG8gbcOhcyBjb23Dum4gZXMgZWwgZGUgMTggYSAyNSBhw7Fvcywgc2VndWlkbyBwb3IgMzYgYSA1MCBhw7Fvcy4NCiAgDQogICogRWwgZ3J1cG8gbWVub3JlcyBkZSAxOCBhw7FvcyByZXByZXNlbnRhIHVuYSBwcm9wb3JjacOzbiBjYXNpIGluc2lnbmlmaWNhbnRlLg0KDQpgYGB7cn0NCm9wdGlvbnMoc2NpcGVuID0gOTk5KQ0KDQpnZ3Bsb3QoZGZfcGFsbm9ydGUsIGFlcyhgwr9DdcOhbCBlcyBzdSByYW5nbyBkZSBlZGFkPyAoUHJlZ3VudGEgZGVtb2dyw6FmaWNhKWApKSArDQogIGdlb21fYmFyKGZpbGwgPSAic3RlZWxibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1Y2nDs24gcG9yIEVkYWQiLCB4ID0gIlJhbmdvIGRlIEVkYWQiLCB5ID0gIkNhbnRpZGFkIikNCmBgYA0KDQoNCiMjIyMgR2FzdG8gVG90YWwgRXN0aW1hZG8gcG9yIFJhbmdvIGRlIEVkYWQgKEJveHBsb3QpDQoNClNlIG9ic2VydmEgcXVlIGF1bnF1ZSBsb3MgYWR1bHRvcyBqw7N2ZW5lcyAoMjYtMzUpIHNvbiBlbCBncnVwbyBtw6FzIG51bWVyb3NvLCBsb3MgZGUgMzYtNTAgYcOxb3MgcG9kcsOtYW4gcmVwcmVzZW50YXIgdW4gbWVyY2FkbyBlc3RyZWxsYSBzaSBzZSBjb25zaWRlcmFuIGxhcyBjYXBhY2lkYWRlcyBkZSBnYXN0by4gRXN0byBwZXJtaXRlIHBlcmZpbGFyIGVzdHJhdGVnaWFzIHByZW1pdW0gbyBWSVAgcGFyYSBlc2UgcmFuZ28uDQoNCiAgKiBFbCByYW5nbyBkZSAzNiBhIDUwIGHDsW9zIHRpZW5lIHVuYSBtZWRpYW5hIGRlIGdhc3RvIG3DoXMgYWx0YSwgaW5kaWNhbmRvIHF1ZSBhdW5xdWUgbm8gc29uIG1heW9yw61hLCBzdWVsZW4gZ2FzdGFyIG3DoXMgcXVlIG90cm9zIGdydXBvcy4NCiAgDQogICogRWwgZ3J1cG8gZGUgMTggYSAyNSBhw7FvcyBtdWVzdHJhIHVuYSBkaXN0cmlidWNpw7NuIGFtcGxpYSwgYWxndW5vcyBnYXN0YW4gbXVjaG8sIHBlcm8gbGEgbWVkaWFuYSBlcyBtZW5vci4NCiAgDQogICogTWVub3JlcyBkZSAxOCBhw7FvcyB0aWVuZW4gdW4gZ2FzdG8gdG90YWwgYmFqbyB5IGhvbW9nw6luZW8gKHNpbiBkaXNwZXJzacOzbiB2aXNpYmxlKS4NCg0KYGBge3J9DQpnZ3Bsb3QoZGZfcGFsbm9ydGUsIGFlcyh4ID0gYMK/Q3XDoWwgZXMgc3UgcmFuZ28gZGUgZWRhZD8gKFByZWd1bnRhIGRlbW9ncsOhZmljYSlgLCB5ID0gZ2FzdG9fdG90YWwpKSArDQogIGdlb21fYm94cGxvdChmaWxsID0gImZvcmVzdGdyZWVuIiwgYWxwaGEgPSAwLjcpICsNCiAgbGFicyh0aXRsZSA9ICJHYXN0byBUb3RhbCBFc3RpbWFkbyBwb3IgUmFuZ28gZGUgRWRhZCIsIHggPSAiRWRhZCIsIHkgPSAiR2FzdG8gVG90YWwgKE1YTikiKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBsYWJlbF9jb21tYSgpKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KDQojIyMjIEdhc3RvIFRvdGFsIHNlZ8O6biBEw61hcyBkZSBBc2lzdGVuY2lhIChCb3hwbG90IGNvbiBwdW50b3MgeSBtZWRpYXMpDQoNCkxhIHZhcmlhYmxlIOKAnGTDrWFzIGRlIGFzaXN0ZW5jaWHigJ0gZXMgY2xhdmUgcGFyYSBwcmVkZWNpciBlbCB2YWxvciBkZWwgY2xpZW50ZS4gUXVpZW5lcyB2YW4gbcOhcyBkw61hcyB0aWVuZGVuIGEgZ2FzdGFyIG3DoXMsIHBvciBsbyBxdWUgaW5jZW50aXZhciBlc3RhbmNpYXMgZGUgMyBkw61hcyBwdWVkZSBhdW1lbnRhciBpbmdyZXNvcy4NCg0KICAqIEEgbWF5b3IgY2FudGlkYWQgZGUgZMOtYXMgYXNpc3RpZG9zLCBtYXlvciBlcyBlbCBnYXN0byB0b3RhbCBwcm9tZWRpbyB5IG1lZGlhbm8uDQogIA0KICAqIFF1aWVuZXMgdmFuIDEgZMOtYSBnYXN0YW4gbWVub3MsIHkgdGFtYmnDqW4gdGllbmVuIG1lbm9yIHZhcmlhYmlsaWRhZC4NCiAgDQogICogTG9zIHF1ZSBhc2lzdGVuIDMgZMOtYXMgc29uIGxvcyBxdWUgbcOhcyBnYXN0YW4sIGNvbiB1bmEgZGlzdHJpYnVjacOzbiBtw6FzIGFtcGxpYSwgeSBzdSBtZWRpYSAocHVudG8gcm9qbykgZXMgbGEgbcOhcyBlbGV2YWRhLg0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRmX3BhbG5vcnRlLCBhZXMoeCA9IGFzLmZhY3RvcihkaWFzKSwgeSA9IGdhc3RvX3RvdGFsKSkgKw0KICBnZW9tX2JveHBsb3QoZmlsbCA9ICJkb2RnZXJibHVlIiwgYWxwaGEgPSAwLjYsIG91dGxpZXIuc2hhcGUgPSBOQSkgKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMiwgYWxwaGEgPSAwLjQsIGNvbG9yID0gImJsYWNrIikgKw0KICBzdGF0X3N1bW1hcnkoZnVuID0gbWVhbiwgZ2VvbSA9ICJwb2ludCIsIHNoYXBlID0gMTgsIHNpemUgPSAzLjUsIGNvbG9yID0gInJlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJHYXN0byBUb3RhbCBzZWfDum4gRMOtYXMgZGUgQXNpc3RlbmNpYSIsIHggPSAiRMOtYXMiLCB5ID0gIkdhc3RvIFRvdGFsIChNWE4pIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gbGFiZWxfY29tbWEoKSkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCiMjICoqQW7DoWxpc2lzIEV4cGxvcmF0b3JpbyBFc3BhY2lhbCBkZSBsb3MgRGF0b3MgLSBFU0RBKioNCg0KIyMjIEhvdGVsZXMNCg0KIyMjIyAxLiBDT05ESUNJw5NOIEFDVFVBTA0KDQoqKlBFUkZJTCBERSBMQSBPRkVSVEEgSE9URUxFUkEgREUgTEFTIENJVURBREVTIERFIE1PTlRFUlJFWSBZIFNBTiBQRURSTyoqIA0KDQpTZWfDum4gZGF0b3MgZGVsIERpcmVjdG9yaW8gRXN0YWTDrXN0aWNvIE5hY2lvbmFsIGRlIFVuaWRhZGVzIEVjb27Ds21pY2FzIChERU5VRSksIGhhc3RhIGVsIGHDsW8gMjAyNCBzZSByZWdpc3RyYW4gMTc0IGhvdGVsZXMgZW4gbG9zIG11bmljaXBpb3MgZGUgTW9udGVycmV5IHkgU2FuIFBlZHJvIEdhcnphIEdhcmPDrWEsIGRlIGxvcyBjdWFsZXMgZWwgODklIHNlIGNvbmNlbnRyYSBlbiBNb250ZXJyZXkuIFRhbiBzb2xvIGVuIDIwMjQsIHNlIHN1bWFyb24gMTUgaG90ZWxlcyBudWV2b3MgYSBsYSBvZmVydGEgZGUgbGEgcmVnacOzbi4NCg0KQSBuaXZlbCBlc3RhdGFsLCBlbCBERU5VRSByZXBvcnRhIDMyNyBob3RlbGVzIGVuIHRvZG8gTnVldm8gTGXDs24sIG1pZW50cmFzIHF1ZSBsYSBTZWNyZXRhcsOtYSBkZSBUdXJpc21vIGVzdGF0YWwgaW5kaWNhIHF1ZSwgZW4gcHJvbWVkaW8sIHNlIGNvbnRhYmlsaXphcm9uIDUyOCw2MjcgaGFiaXRhY2lvbmVzIHJlZ2lzdHJhZGFzIG1lbnN1YWxtZW50ZSBkdXJhbnRlIGVsIGHDsW8uIEVzdG8gaW1wbGljYSBxdWUgZWwgNTMlIGRlIGxhIGluZnJhZXN0cnVjdHVyYSBob3RlbGVyYSBkZWwgZXN0YWRvIHNlIHViaWNhIGVuIE1vbnRlcnJleSB5IFNhbiBQZWRyby4gU2kgZXh0cmFwb2xhbW9zIGVzZSBtaXNtbyBwb3JjZW50YWplIGFsIG7Dum1lcm8gZGUgaGFiaXRhY2lvbmVzLCBzZSBlc3RpbWEgcXVlIGVzdG9zIGRvcyBtdW5pY2lwaW9zIGNvbmNlbnRyYW4gYWxyZWRlZG9yIGRlIDI4MCwxNzIgaGFiaXRhY2lvbmVzLCBjb25zb2xpZMOhbmRvc2UgY29tbyBlbCBlamUgY2VudHJhbCBkZSBsYSBvZmVydGEgaG90ZWxlcmEgZW4gZWwgZXN0YWRvLg0KDQpgYGB7cn0NCmhvdGVsZXNfYWN1bXVsYWRvcyA8LSBob3RlbGVzX2RhbnVlICU+JQ0KICBncm91cF9ieShNdW5pY2lwaW8sIEFub19JbmNvKSAlPiUNCiAgc3VtbWFyaXNlKGhvdGVsZXNfbnVldm9zID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgY29tcGxldGUoTXVuaWNpcGlvLCBBbm9fSW5jbyA9IGZ1bGxfc2VxKEFub19JbmNvLCAxKSwgZmlsbCA9IGxpc3QoaG90ZWxlc19udWV2b3MgPSAwKSkgJT4lDQogIGFycmFuZ2UoTXVuaWNpcGlvLCBBbm9fSW5jbykgJT4lDQogIGdyb3VwX2J5KE11bmljaXBpbykgJT4lDQogIG11dGF0ZShob3RlbGVzX2FjdW11bGFkb3MgPSBjdW1zdW0oaG90ZWxlc19udWV2b3MpKSAlPiUNCiAgdW5ncm91cCgpDQoNCnJlc3VtZW5fMjAyNCA8LSBob3RlbGVzX2FjdW11bGFkb3MgJT4lDQogIGZpbHRlcihBbm9fSW5jbyA9PSAyMDI0KSAlPiUNCiAgZ3JvdXBfYnkoTXVuaWNpcGlvKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIEhvdGVsZXNfTnVldm9zID0gc3VtKGhvdGVsZXNfbnVldm9zKSwNCiAgICBIb3RlbGVzX0FjdW11bGFkb3MgPSBzdW0oaG90ZWxlc19hY3VtdWxhZG9zKSwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkgJT4lDQogIGJpbmRfcm93cygNCiAgICBzdW1tYXJpc2UoLiwgDQogICAgICAgICAgICAgIE11bmljaXBpbyA9ICJUb3RhbCIsDQogICAgICAgICAgICAgIEhvdGVsZXNfTnVldm9zID0gc3VtKEhvdGVsZXNfTnVldm9zKSwNCiAgICAgICAgICAgICAgSG90ZWxlc19BY3VtdWxhZG9zID0gc3VtKEhvdGVsZXNfQWN1bXVsYWRvcykpDQogICkNCg0KI1RBQkxBIERFIFJFU1VNRU4NCnJlc3VtZW5fMjAyNCAlPiUNCiAgZ3QoKSAlPiUNCiAgdGFiX2hlYWRlcigNCiAgICB0aXRsZSA9ICJIb3RlbGVzIGVuIE1vbnRlcnJleSB5IFNhbiBQZWRybyIsDQogICAgc3VidGl0bGUgPSAiaGFzdGEgZWwgMjAyNCBlbiBERU5VRSINCiAgKSAlPiUNCiAgZm10X251bWJlcigNCiAgICBjb2x1bW5zID0gYyhIb3RlbGVzX051ZXZvcywgSG90ZWxlc19BY3VtdWxhZG9zKSwNCiAgICBkZWNpbWFscyA9IDANCiAgKSAlPiUNCiAgdGFiX3N0eWxlKA0KICAgIHN0eWxlID0gY2VsbF90ZXh0KHdlaWdodCA9ICJib2xkIiksDQogICAgbG9jYXRpb25zID0gY2VsbHNfYm9keShyb3dzID0gTXVuaWNpcGlvID09ICJUb3RhbCIpDQogICkgJT4lDQogIGNvbHNfbGFiZWwoDQogICAgTXVuaWNpcGlvID0gIkNpdWRhZCIsDQogICAgSG90ZWxlc19OdWV2b3MgPSAiTnVldm9zIFJlZ2lzdHJvcyIsDQogICAgSG90ZWxlc19BY3VtdWxhZG9zID0gIlRvdGFsIFJlZ2lzdHJhZG9zIg0KICApDQpgYGANCg0KYGBge3J9DQojR1LDgUZJQ08gREUgQkFSUkFTDQpjdWFydG9zX3Bvcl9hbm8gPC0gaG90ZWxlc19iZCAlPiUNCiAgZ3JvdXBfYnkoYcOxbykgJT4lDQogIHN1bW1hcmlzZShDdWFydG9zX1JlZ2lzdHJhZG9zID0gKHN1bShDdWFydG9zX1JlZ2lzdHJhZG9zKSogMC41MykvMTIsIC5ncm91cHMgPSAiZHJvcCIpDQoNCmhvdGVsZXNfcG9yX2FubyA8LSBob3RlbGVzX2FjdW11bGFkb3MgJT4lDQogIGdyb3VwX2J5KEFub19JbmNvKSAlPiUNCiAgc3VtbWFyaXNlKEhvdGVsZXNfQWN1bXVsYWRvcyA9IHN1bShob3RlbGVzX2FjdW11bGFkb3MpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgcmVuYW1lKGHDsW8gPSBBbm9fSW5jbykNCg0KZGF0b3NfY29tYmluYWRvcyA8LSBsZWZ0X2pvaW4oY3VhcnRvc19wb3JfYW5vLCBob3RlbGVzX3Bvcl9hbm8sIGJ5ID0gImHDsW8iKSAlPiUNCiAgZmlsdGVyKGHDsW8gPj0gMjAxNCwgYcOxbyA8PSAyMDI0KQ0KDQojIEdyw6FmaWNvIGNvbiBlc2NhbGEgcmVhbCBkZSBjdWFydG9zIHkgZWplIGRlcmVjaG8gcGFyYSBob3RlbGVzDQptYXhfY3VhcnRvcyA8LSBtYXgoZGF0b3NfY29tYmluYWRvcyRDdWFydG9zX1JlZ2lzdHJhZG9zLCBuYS5ybSA9IFRSVUUpDQptYXhfaG90ZWxlcyA8LSBtYXgoZGF0b3NfY29tYmluYWRvcyRIb3RlbGVzX0FjdW11bGFkb3MsIG5hLnJtID0gVFJVRSkNCmZhY3Rvcl9lc2NhbGEgPC0gbWF4X2N1YXJ0b3MgLyBtYXhfaG90ZWxlcw0KDQoNCmdncGxvdChkYXRvc19jb21iaW5hZG9zLCBhZXMoeCA9IGHDsW8pKSArDQogIGdlb21fY29sKGFlcyh5ID0gQ3VhcnRvc19SZWdpc3RyYWRvcyksIGZpbGwgPSAiZ3JleSIsIHdpZHRoID0gMC42KSArDQogIGdlb21fbGluZShhZXMoeSA9IEhvdGVsZXNfQWN1bXVsYWRvcyAqIGZhY3Rvcl9lc2NhbGEpLCBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV3aWR0aCA9IDEuMikgKw0KICBnZW9tX3BvaW50KGFlcyh5ID0gSG90ZWxlc19BY3VtdWxhZG9zICogZmFjdG9yX2VzY2FsYSksIGNvbG9yID0gImRhcmtibHVlIiwgc2l6ZSA9IDIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKA0KICAgIG5hbWUgPSAiQ3VhcnRvcyBSZWdpc3RyYWRvcyBNZW5zdWFsbWVudGUiLA0KICAgIGxhYmVscyA9IGxhYmVsX2NvbW1hKCksDQogICAgc2VjLmF4aXMgPSBzZWNfYXhpcyh+IC4gLyBmYWN0b3JfZXNjYWxhLCBuYW1lID0gIkhvdGVsZXMiLCBsYWJlbHMgPSBsYWJlbF9jb21tYSgpKQ0KICApICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IDIwMTQ6MjAyNCkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkV2b2x1Y2nDs24gZGUgQ3VhcnRvcyB5IEhvdGVsZXMgUmVnaXN0cmFkb3MiLA0KICAgIHN1YnRpdGxlID0gIkVuIE1vbnRlcnJleSB5IFNhbiBQZWRybyAoMjAxNCAtIDIwMjQpIiwNCiAgICB4ID0gIkHDsW8iDQogICkgKw0KICBnZW9tX3RleHQoDQogIGFlcyh5ID0gQ3VhcnRvc19SZWdpc3RyYWRvcywgbGFiZWwgPSBzY2FsZXM6OmNvbW1hKEN1YXJ0b3NfUmVnaXN0cmFkb3MpKSwNCiAgdmp1c3QgPSAtMS41LA0KICBzaXplID0gMg0KICApICsgDQogIGdlb21fdGV4dCgNCiAgYWVzKHkgPSBIb3RlbGVzX0FjdW11bGFkb3MgKiBmYWN0b3JfZXNjYWxhLCBsYWJlbCA9IEhvdGVsZXNfQWN1bXVsYWRvcyksDQogIHZqdXN0ID0gMi4zLA0KICBzaXplID0gMywNCiAgY29sb3IgPSAiZGFya2JsdWUiLA0KICBmb250ZmFjZSA9ICJib2xkIg0KICApICsgDQogIHRoZW1lX21pbmltYWwoKSArIA0KICB0aGVtZSgNCiAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpDQogICkNCmBgYA0KDQpMYSBjYW50aWRhZCBkZSBoYWJpdGFjaW9uZXMgcmVnaXN0cmFkYXMgbWVuc3VhbG1lbnRlIGhhIG1vc3RyYWRvIHVuYSB0ZW5kZW5jaWEgcG9zaXRpdmEgY29uc3RhbnRlIGRlc2RlIDIwMTQuIFBvciBvdHJvIGxhZG8sIGVsIG7Dum1lcm8gZGUgaG90ZWxlcyBlbiBlc3RhcyBjaXVkYWRlcyB0YW1iacOpbiBoYSBpbmNyZW1lbnRhZG8sIGNvbiB1biBub3RhYmxlIGNyZWNpbWllbnRvIGRlbCAzNyUgZW50cmUgMjAxOCB5IDIwMjAsIGVuIHVuIHBlcmlvZG8gZGUgc29sbyBkb3MgYcOxb3MuIFNpbiBlbWJhcmdvLCBzZSBvYnNlcnZhIHF1ZSBsYSBjYW50aWRhZCBkZSBoYWJpdGFjaW9uZXMgY3JlY2nDsyDDum5pY2FtZW50ZSB1biAxMCUgZHVyYW50ZSBlbCBtaXNtbyBwZXJpb2RvLCBsbyBxdWUgcG9kcsOtYSBpbmRpY2FyIHF1ZSBsb3MgbnVldm9zIGhvdGVsZXMgbm8gaGFuIHNpZG8gZGUgZ3JhbiB0YW1hw7FvLiBBZGVtw6FzLCBlbiBhbWJhcyB2YXJpYWJsZXMgc2UgcGVyY2liZSB1biBlc3RhbmNhbWllbnRvIGVuIGVsIGNyZWNpbWllbnRvIGR1cmFudGUgbG9zIGHDsW9zIGRlIGxhIHBhbmRlbWlhIGRlIENPVklELTE5Lg0KDQoNCioqQ1JFQ0lNSUVOVE8gREUgTEEgT0ZFUlRBIEhPVEVMRVJBIFBPUiBDSVVEQUQgTU9OVEVSUkVZIFkgU0FOIFBFRFJPKiogDQoNCmBgYHtyfQ0KaG90ZWxlc19wb3JfY2l1ZGFkIDwtIGhvdGVsZXNfYWN1bXVsYWRvcyAlPiUNCiAgZmlsdGVyKEFub19JbmNvICVpbiUgYygyMDE0LCAyMDI0KSkgJT4lDQogIGdyb3VwX2J5KEFub19JbmNvLCBNdW5pY2lwaW8pICU+JQ0KICBzdW1tYXJpc2UoaG90ZWxlcyA9IHN1bShob3RlbGVzX2FjdW11bGFkb3MpLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQpjcmVjaW1pZW50b19ob3RlbGVzX3Bvcl9jaXVkYWQgPC0gDQogIGdncGxvdChob3RlbGVzX3Bvcl9jaXVkYWQsIGFlcyh4ID0gZmFjdG9yKEFub19JbmNvKSwgeSA9IGhvdGVsZXMsIGZpbGwgPSBNdW5pY2lwaW8pKSArDQogIGdlb21fY29sKHdpZHRoID0gMC44KSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiQ3JlY2ltaWVudG8gZGUgSG90ZWxlcyBwb3IgQ2l1ZGFkIiwNCiAgICBzdWJ0aXRsZSA9ICJEZWwgMjAxNCBhbCAyMDI0IiwNCiAgICB4ID0gIkHDsW8iLA0KICAgIHkgPSAiQ2FudGlkYWQgZGUgSG90ZWxlcyIsDQogICAgZmlsbCA9ICJDaXVkYWQiDQogICkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJNb250ZXJyZXkiID0gIiMxZjc3YjQiLCAiU2FuIFBlZHJvIEdhcnphIEdhcmPDjGEiID0gImxpZ2h0Ymx1ZSIpKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTApICsgDQogIHRoZW1lKHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkpICsNCiAgdGhlbWUoDQogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoKSwNCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICBnZW9tX3RleHQoDQogIGFlcyh5ID0gaG90ZWxlcywgbGFiZWwgPSBzY2FsZXM6OmNvbW1hKGhvdGVsZXMpKSwNCiAgdmp1c3QgPSAwLA0KICBoanVzdCA9IDEuNSwNCiAgc2l6ZSA9IDMsDQogIGZvbnRmYWNlID0gImJvbGQiDQogICkgDQoNCmhvdGVsZXNfY3JlY2ltaWVudG8gPC0gaG90ZWxlc19wb3JfY2l1ZGFkICU+JQ0KICBmaWx0ZXIoQW5vX0luY28gJWluJSBjKDIwMTQsIDIwMjQpKSAlPiUNCiAgdGlkeXI6OnBpdm90X3dpZGVyKA0KICAgIG5hbWVzX2Zyb20gPSBBbm9fSW5jbywNCiAgICB2YWx1ZXNfZnJvbSA9IGhvdGVsZXMsDQogICAgbmFtZXNfcHJlZml4ID0gIkEiDQogICkgJT4lDQogIG11dGF0ZSgNCiAgICBjcmVjaW1pZW50b19hYnMgPSBBMjAyNCAtIEEyMDE0LA0KICAgIGNyZWNpbWllbnRvX3BjdCA9IChjcmVjaW1pZW50b19hYnMgLyBBMjAxNCkgKiAxMDANCiAgKQ0KDQp0YWJsYV9jcmVjaW1pZW50b19ob3RlbGVzIDwtIA0KICBob3RlbGVzX2NyZWNpbWllbnRvICU+JQ0KICBndCgpICU+JQ0KICBjb2xzX2xhYmVsKA0KICAgIE11bmljaXBpbyA9ICJDaXVkYWQiLA0KICAgIEEyMDE0ID0gIjIwMTQiLA0KICAgIEEyMDI0ID0gIjIwMjQiLA0KICAgIGNyZWNpbWllbnRvX2FicyA9ICJDcmVjaW1pZW50byBBYnNvbHV0byIsDQogICAgY3JlY2ltaWVudG9fcGN0ID0gIkNyZWNpbWllbnRvICglKSINCiAgKSAlPiUNCiAgZm10X251bWJlcigNCiAgICBjb2x1bW5zID0gYyhjcmVjaW1pZW50b19wY3QpLA0KICAgIGRlY2ltYWxzID0gMSwNCiAgICBzdWZmaXhpbmcgPSBGQUxTRQ0KICApICU+JQ0KICB0YWJfaGVhZGVyKA0KICAgIHRpdGxlID0gIkNyZWNpbWllbnRvIGRlIEhvdGVsZXMgcG9yIENpdWRhZCAoMjAxNOKAkzIwMjQpIg0KICApICU+JQ0KICBmbXRfbnVtYmVyKA0KICAgIGNvbHVtbnMgPSBjKEEyMDE0LCBBMjAyNCwgY3JlY2ltaWVudG9fYWJzKSwNCiAgICBkZWNpbWFscyA9IDANCiAgKSAlPiUNCiAgZ3JhbmRfc3VtbWFyeV9yb3dzKA0KICAgIGNvbHVtbnMgPSBjKEEyMDE0LCBBMjAyNCwgY3JlY2ltaWVudG9fYWJzKSwNCiAgICBmbnMgPSBsaXN0KFRvdGFsID0gfnN1bSguKSksDQogICAgZm9ybWF0dGVyID0gZm10X251bWJlciwNCiAgICBkZWNpbWFscyA9IDANCiAgKSU+JQ0KICB0YWJfb3B0aW9ucygNCiAgICB0YWJsZS5mb250LnNpemUgPSA4LA0KICAgIGNvbHVtbl9sYWJlbHMuZm9udC5zaXplID0gMTANCiAgKQ0KDQoNCg0KY3JlY2ltaWVudG9faG90ZWxlc19wb3JfY2l1ZGFkIC8gdGFibGFfY3JlY2ltaWVudG9faG90ZWxlcw0KYGBgDQoNCkVuIGN1YW50byBhbCBjcmVjaW1pZW50byBkZSBsYSBvZmVydGEgaG90ZWxlcmEsIGFtYmFzIGNpdWRhZGVzIGhhbiBleHBlcmltZW50YWRvIHVuIGF1bWVudG8gc2lnbmlmaWNhdGl2bywgYXVucXVlIGRlIG1hbmVyYSBkZXNpZ3VhbC4gTW9udGVycmV5IGhhIHJlZ2lzdHJhZG8gdW4gY3JlY2ltaWVudG8gZGVsIDY0LjUlIGVuIGxvcyDDumx0aW1vcyAxMCBhw7FvcywgbG8gcXVlIHNlIHRyYWR1Y2UgZW4gdW4gaW5jcmVtZW50byBkZSA2MSB1bmlkYWRlcyBob3RlbGVyYXMuIEVuIGNvbnRyYXN0ZSwgU2FuIFBlZHJvLCBhdW5xdWUgc29sbyBoYSBzdW1hZG8gMTMgdW5pZGFkZXMsIGhhIGV4cGVyaW1lbnRhZG8gdW4gY3JlY2ltaWVudG8gcG9yY2VudHVhbCBtdWNobyBtw6FzIGFsdG8sIGFsY2FuemFuZG8gdW4gMjE2JS4NCg0KDQojIyMjIElJLiBQRVJGSUwgREUgTEEgT0ZFUlRBIEhPVEVMRVJBDQpFbCBvYmpldGl2byBkZSBsYSBzaWd1aWVudGUgc2VjY2nDs24gZXMgZW50ZW5kZXIgbGEgZGlzdHJpYnVjacOzbiBlc3BhY2lhbCwgZGUgY2FsaWRhZCB5IGRlIGNvc3RvIGRlIGxvcyBob3RlbGVzIGVuIE1vbnRlcnJleSB5IFNhbiBQZWRyby4gRGV0ZWN0YXIgY29uY2VudHJhY2lvbmVzIGdlb2dyw6FmaWNhcyBkZSBob3RlbGVzIGRlIGx1am8sIHBhdHJvbmVzIGRlIHByZWNpbyBhIHRyYXbDqXMgZGUgZGlmZXJlbnRlcyB6b25hcyBlIGlkZW50aWZpY2FyIMOhcmVhcyBjb24gcG90ZW5jaWFsIHBhcmEgZWwgZGVzYXJyb2xsbyBkZSBpbmZyYWVzdHJ1Y3R1cmEgdHVyw61zdGljYS4NCg0KUGFyYSBtZWRpciBsYXMgY2FyYWN0ZXLDrXN0aWNhcyBjdWFsaXRhdGl2YXMgZGUgbG9zIGhvdGVsZXMgaGFjZW1vcyB1c28gZGUgaW5mb3JtYWNpw7NuIHB1YmxpY2FkYSBlbiBlbCBzaXRpbyB3ZWIgR29vZ2xlIE1hcHMuIEFsIHNlciB1bmEgZnVlbnRlIGRlIGluZm9ybWFjacOzbiBubyBvZmljaWFsIG9jdXJyZW4gZGlzY3JlcGFuY2lhcyBlbnRyZSBsYSBjYW50aWRhZCBkZSBpbnN0aXR1Y2lvbmVzIGhvdGVsZXJhcyByZWdpc3RyYWRhcyBlbiBlbCBERU5VRSB5IEdvb2dsZSBNYXBzLiBQb3IgbG8gdGFudG8gc2UgZW50aWVuZGUgbGEgbGlnZXJhIGRpZmVyZW5jaWEgZW50cmUgbG9zIGRhdG9zIHByZXNlbnRhZG9zIGVuIGxhIHNlY2Npw7NuIGFudGVyaW9yLiAgDQoNCmBgYHtyfQ0KIyBESVNUUklCVUNJw5NOIERFIEhPVEVMRVMgUE9SIENBVEVHT1LDjUENCg0KdGFibGFfY2F0ZWdvcmlhcyA8LSBob3RlbGVzICU+JQ0KICBmaWx0ZXIoQ2F0ZWdvcnkgJWluJSBjKCIxIiwgIjIiLCAiMyIsICI0IiwgIjUiLCAiR3Vlc3QgSG91c2UgLyBBaXJCbkIiLCAiTW90ZWwiLCBOQSkpICU+JQ0KICBncm91cF9ieShDaXR5LCBDYXRlZ29yeSkgJT4lDQogIHN1bW1hcmlzZShIb3RlbGVzID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgcGl2b3Rfd2lkZXIoDQogICAgbmFtZXNfZnJvbSA9IENpdHksDQogICAgdmFsdWVzX2Zyb20gPSBIb3RlbGVzLA0KICAgIHZhbHVlc19maWxsID0gMA0KICApICU+JQ0KICBhcnJhbmdlKGFzLm51bWVyaWMoQ2F0ZWdvcnkpKQ0KDQp0YWJsYV9jYXRlZ29yaWFzIDwtIHRhYmxhX2NhdGVnb3JpYXMgJT4lDQogIG11dGF0ZShUb3RhbCA9IHJvd1N1bXMoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpKSkpDQoNCiNUYWJsYQ0KdGFibGFfY2F0ZWdvcmlhcyAlPiUNCiAgZ3QoKSAlPiUNCiAgY29sc19sYWJlbCgNCiAgICBDYXRlZ29yeSA9ICJFc3RyZWxsYXMiDQogICkgJT4lDQogIHRhYl9oZWFkZXIoDQogICAgdGl0bGUgPSAiQ2FudGlkYWQgZGUgSG90ZWxlcyBwb3IgQ2F0ZWdvcsOtYSB5IENpdWRhZCINCiAgKSAlPiUNCiAgdGFiX3N0eWxlKA0KICAgIHN0eWxlID0gY2VsbF90ZXh0KHdlaWdodCA9ICJib2xkIiksDQogICAgbG9jYXRpb25zID0gY2VsbHNfYm9keShjb2x1bW5zID0gIlRvdGFsIikNCiAgKQ0KYGBgDQoNCg0KTGEgb2ZlcnRhIGhvdGVsZXJhIGRlIGFtYmFzIGNpdWRhZGVzIGluY2x1eWUgdW5hIGFtcGxpYSB2YXJpZWRhZCBkZSB0aXBvcyBkZSBhbG9qYW1pZW50byBjbGFzaWZpY2Fkb3MgcG9yIGNhdGVnb3LDrWEuIFVuYSBkZSBsYXMgY2xhc2lmaWNhY2lvbmVzIG3DoXMgY29tdW5lcyBlcyBsYSBiYXNhZGEgZW4gZXN0cmVsbGFzLCBkb25kZSAxIGVzdHJlbGxhIGNvcnJlc3BvbmRlIGEgYWxvamFtaWVudG9zIHRpcG8gaG9zdGFsIHkgNSBlc3RyZWxsYXMgYSBob3RlbGVzIGRlIGx1am8uIEFkaWNpb25hbG1lbnRlLCBlbiBwbGF0YWZvcm1hcyBjb21vIEdvb2dsZSBNYXBzIHRhbWJpw6luIHB1ZWRlbiBhcGFyZWNlciBvdHJhcyBjYXRlZ29yw61hcyBjb21vIEd1ZXN0IEhvdXNlIOKAlHByb3BpZWRhZGVzIHByaXZhZGFzIHF1ZSBvZnJlY2VuIGhvc3BlZGFqZSBwb3IgcGVyaW9kb3MgY29ydG9zLCBpbmNsdXllbmRvIGFsZ3VuYXMgcmVnaXN0cmFkYXMgZW4gcGxhdGFmb3JtYXMgY29tbyBBaXJibmLigJQsIE1vdGVsZXMsIHkgaG90ZWxlcyBzaW4gdW5hIGNhdGVnb3LDrWEgZXNwZWPDrWZpY2EgKE5BKS4gUGFyYSBlZmVjdG9zIGRlbCBwcmVzZW50ZSBhbsOhbGlzaXMsIMO6bmljYW1lbnRlIHNlIGNvbnNpZGVyYXLDoW4gbG9zIGhvdGVsZXMgY2xhc2lmaWNhZG9zIGRlbnRybyBkZSBsYSBlc2NhbGEgZGUgMSBhIDUgZXN0cmVsbGFzLiAgDQoNCmBgYHtyfQ0KaG90ZWxzX3N1bW1hcnkgPC0gaG90ZWxlcyAlPiUNCiAgZmlsdGVyKENhdGVnb3J5ICVpbiUgYygiMSIsICIyIiwgIjMiLCAiNCIsICI1IikpICU+JQ0KICBncm91cF9ieShDaXR5KSAlPiUNCiAgc3VtbWFyaXNlKGF2ZXJhZ2VfcmF0aW5nID0gbWVhbihyYXRpbmcsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBudW1iZXJfb2ZfaG90ZWxzID0gbigpKQ0KDQojIENhbGlmaWNhY2nDs24gUHJvbWVkaW8NCmdncGxvdChob3RlbHNfc3VtbWFyeSwgYWVzKHggPSByZW9yZGVyKENpdHksIC1hdmVyYWdlX3JhdGluZyksIHkgPSBhdmVyYWdlX3JhdGluZywgZmlsbCA9IENpdHkpKSArDQogIGdlb21fY29sKHdpZHRoID0gMC44KSArDQogIGxhYnModGl0bGUgPSAiQ2FsaWZpY2FjacOzbiBQcm9tZWRpbyBwb3IgQ2l1ZGFkIiwNCiAgICAgICB4ID0gIkNpdWRhZCIsIHkgPSAiQ2FsaWZpY2FjacOzbiIsDQogICAgICAgZmlsbCA9ICJDaXVkYWQiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk1vbnRlcnJleSIgPSAiIzFmNzdiNCIsICJTYW5QZWRybyIgPSAibGlnaHRibHVlIikpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgNSkpICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMCkgKyANCiAgdGhlbWUocGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSkgKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dCgpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQoYXZlcmFnZV9yYXRpbmcsIDIpKSwgdmp1c3QgPSAtMC41LCBmb250ZmFjZSA9ICJib2xkIikNCmBgYA0KDQoNCg0KYGBge3IgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdnbWFwKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShwYXRjaHdvcmspICANCmxpYnJhcnkoc2NhbGVzKQ0KbGlicmFyeShsZWFmbGV0KQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KcmVnaXN0ZXJfc3RhZGlhbWFwcyhrZXkgPSAiMTc5NTk5MTEtODc0ZC00ZTkzLWIxODQtMDVmYTNmZTE2MWQwIikNCg0KZGF0YSA8LSBob3RlbGVzICU+JQ0KICBzZWxlY3QobmFtZSwgbG9uZ2l0dWRlLCBsYXRpdHVkZSkNCg0KIyBDb29yZGVuYWRhcyBDZW50cmFsZXMgKHJlZmVyZW5jaWEpDQpjZW50ZXJfY29vcmRzIDwtIGRhdGEuZnJhbWUobG9uID0gLTEwMC4zMTYxLCBsYXQgPSAyNS42ODY2KQ0KDQojIEVuY3VhZHJlIGRlIGxhcyBjb29yZGVuYWRhcw0KYmJveF9leCA9IGMoDQogIGxlZnQgPSAtMTAwLjQyNSwgICMgZXN0ZQ0KICBib3R0b20gPSAyNS42MCwgICAgICMgc3VyDQogIHJpZ2h0ID0gLTEwMC4yMjAsICAjIG9lc3RlDQogIHRvcCA9IDI1Ljc1NSAgICAgICMgbm9ydGUNCikNCg0KIyBNYXBhDQptb250ZXJyZXlfbWFwIDwtIGdldF9zdGFkaWFtYXAoDQogIGJib3ggPSBiYm94X2V4LA0KICB6b29tID0gMTIsDQogIG1hcHR5cGUgPSAic3RhbWVuX3RvbmVyX2xpdGUiIA0KKQ0KDQojIFBsb3R0aW5nIGRlIGxhIGRlbnNpZGFkDQpkZW5zaWRhZF9ob3RlbGVzIDwtIA0KICBnZ21hcChtb250ZXJyZXlfbWFwKSArDQogIHN0YXRfZGVuc2l0eTJkKA0KICAgIGRhdGEgPSBkYXRhLA0KICAgIGFlcyh4ID0gbG9uZ2l0dWRlLCB5ID0gbGF0aXR1ZGUsIGZpbGwgPSBhZnRlcl9zdGF0KGxldmVsKSwgYWxwaGEgPSBhZnRlcl9zdGF0KGxldmVsKSksDQogICAgZ2VvbSA9ICJwb2x5Z29uIiwgYmlucyA9IDMwDQogICkgKw0KICBnZW9tX3BvaW50KA0KICAgIGRhdGEgPSBkYXRhLA0KICAgIGFlcyh4ID0gbG9uZ2l0dWRlLCB5ID0gbGF0aXR1ZGUpLA0KICAgIGNvbG9yID0gInJlZCIsIHNpemUgPSAxLCBhbHBoYSA9IDAuNA0KICApICsNCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIocGFsZXR0ZSA9ICJCbHVlcyIsIGRpcmVjdGlvbiA9IC0xKSArDQogIHNjYWxlX2FscGhhKHJhbmdlID0gYygwLjIsIDAuOCksIGd1aWRlID0gIm5vbmUiKSArDQogIHRoZW1lX3ZvaWQoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiRGVuc2lkYWQgZGUgSG90ZWxlcyIsDQogICAgZmlsbCA9ICJOaXZlbCBkZSBkZW5zaWRhZCINCiAgKQ0KDQoNCiNnZ3NhdmUoIm1vbnRlcnJleV9tYXBfaGlnaHJlcy5wbmciLCBwbG90ID0gZGVuc2lkYWRfaG90ZWxlcywgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCwgZHBpID0gMzAwKQ0KDQojIE1BUEEgUE9SIENBVEVHT1JJQVMNCg0KZGF0YV9jYXQgPC0gaG90ZWxlcyAlPiUNCiAgZmlsdGVyKENhdGVnb3J5ICVpbiUgYygiMSIsICIyIiwgIjMiLCAiNCIsICI1IikpICU+JQ0KICBzZWxlY3QobmFtZSwgbG9uZ2l0dWRlLCBsYXRpdHVkZSwgQ2F0ZWdvcnkpDQoNCiMgTWFwYSBjb24gcHVudG9zIGNvbG9yZWFkb3MgcG9yIGNhdGVnb3LDrWENCm1hcGFfY2F0ZWdvcmlhX2hvdGVsZXMgPC0gZ2dtYXAobW9udGVycmV5X21hcCkgKw0KICBnZW9tX3BvaW50KA0KICAgIGRhdGEgPSBkYXRhX2NhdCwNCiAgICBhZXMoeCA9IGxvbmdpdHVkZSwgeSA9IGxhdGl0dWRlLCBjb2xvciA9IENhdGVnb3J5KSwNCiAgICBzaXplID0gMiwNCiAgICBhbHBoYSA9IDAuNw0KICApICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKA0KICAgIHZhbHVlcyA9IGMoDQogICAgICAiMSIgPSAiIzQ1NzViNCIsDQogICAgICAiMiIgPSAiIzkxYmZkYiIsIA0KICAgICAgIjMiID0gIiNmZWUwOGIiLA0KICAgICAgIjQiID0gIiNmYzhkNTkiLA0KICAgICAgIjUiID0gIiNkNzMwMjciDQogICAgKSwNCiAgICBuYW1lID0gIkNhdGVnb3LDrWEiDQogICkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkRpc3RyaWJ1Y2nDs24gcG9yIENhdGVnb3LDrWEiDQogICkgKw0KICB0aGVtZV92b2lkKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCg0KIyBNb3N0cmFyIGVsIG1hcGENCmRlbnNpZGFkX2hvdGVsZXMgKyBwbG90X3NwYWNlcigpICsgIG1hcGFfY2F0ZWdvcmlhX2hvdGVsZXMgKw0KICBwbG90X2xheW91dCh3aWR0aHMgPSBjKDEsIDAuMDUsIDEpKQ0KYGBgDQoNCkxhIG1heW9yIGNvbmNlbnRyYWNpw7NuIGhvdGVsZXJhIGRlIGFtYm9zIG11bmljaXBpb3Mgc2UgZW5jdWVudHJhIGVuIGVsIGNlbnRybyBkZSBsYSBjaXVkYWQgZGUgTW9udGVycmV5LCBkb25kZSBzZSB1YmljYSBlbCBuw7pjbGVvIHByaW5jaXBhbCBkZSBhbG9qYW1pZW50b3MuIFVuIHNlZ3VuZG8gcHVudG8gZGUgY29uY2VudHJhY2nDs24gc2UgbG9jYWxpemEgaGFjaWEgZWwgZXN0ZSBkZWwgY2VudHJvLCBtaWVudHJhcyBxdWUgdW4gdGVyY2VyIGZvY28gcmVsZXZhbnRlIHNlIGVuY3VlbnRyYSBhbCBzdXJvZXN0ZSwgZW4gbGEgem9uYSBkZSBWYWxsZSBPcmllbnRlLCBkZW50cm8gZGVsIG11bmljaXBpbyBkZSBTYW4gUGVkcm8uICANCiAgDQpTaSBiaWVuIGxhIGNvbmNlbnRyYWNpw7NuIGRlIGxhIG9mZXJ0YSBob3RlbGVyYSBzZSB1YmljYSBwcmluY2lwYWxtZW50ZSBlbiBlbCBjZW50cm8gZGUgbGEgY2l1ZGFkLCBlc3RhIHNlIGNvbXBvbmUgbWF5b3JpdGFyaWFtZW50ZSBwb3IgaG90ZWxlcyBkZSBiYWphIGNhdGVnb3LDrWEsIHNpZW5kbyBsb3MgZXN0YWJsZWNpbWllbnRvcyBkZSAyIHkgMyBlc3RyZWxsYXMgbG9zIG3DoXMgZnJlY3VlbnRlcy4gRW4gY29udHJhc3RlLCBsb3MgaG90ZWxlcyBkZSBtYXlvciBjYXRlZ29yw61hIHRpZW5kZW4gYSBsb2NhbGl6YXJzZSBlbiB6b25hcyBwZXJpZsOpcmljYXMgYWwgY2VudHJvLCBkZXN0YWNhbmRvIGVzcGVjaWFsbWVudGUgc3UgY29uY2VudHJhY2nDs24gZW4gZWwgbXVuaWNpcGlvIGRlIFNhbiBQZWRybyB5IGVuIGVsIHN1cm9lc3RlIGRlbCDDoXJlYSBtZXRyb3BvbGl0YW5hLiBMb3MgaG90ZWxlcyBkZSBsdWpvIOKAlGNsYXNpZmljYWRvcyBjb21vIGNhdGVnb3LDrWEgNSBlc3RyZWxsYXPigJQgc2UgYWdydXBhbiBwcmVkb21pbmFudGVtZW50ZSBlbiBsYSB6b25hIGRlIFZhbGxlIE9yaWVudGUsIGVuIFNhbiBQZWRyby4gTGFzIMO6bmljYXMgZXhjZXBjaW9uZXMgYSBlc3RhIHRlbmRlbmNpYSBzb24gZWwgSG90ZWwgU2FmaSBDZW50cm8geSBlbCBIb3RlbCBLcnlzdGFsIE1vbnRlcnJleSwgYW1ib3MgdWJpY2Fkb3MgZW4gZWwgY2VudHJvIGRlIE1vbnRlcnJleS4gICAgDQogIA0KUGFyYSBjb21wYXJhciBsYXMgZGlzdGludGFzIGNhdGVnb3LDrWFzIGhvdGVsZXJhcywgc2UgYW5hbGl6w7Mgc3UgZGlzdHJpYnVjacOzbiBlbiBjdWFudG8gYWwgcHJlY2lvIHByb21lZGlvIHBvciBub2NoZSB5IGxhcyBjYWxpZmljYWNpb25lcyByZWdpc3RyYWRhcyBlbiBHb29nbGUgTWFwcy4gTGEgdGFyaWZhIGNvbnNpZGVyYWRhIGZ1ZSBwYXJhIHVuYSBub2NoZSBkZSBmaW4gZGUgc2VtYW5hIGVuIGFicmlsLCBwYXJhIGRvcyBwZXJzb25hcy4gICANCg0KYGBge3J9DQpjYXRlZ29yaWFzIDwtIGhvdGVsZXMgJT4lDQogIGZpbHRlcihDYXRlZ29yeSAlaW4lIGMoIjEiLCAiMiIsIjMiLCAiNCIsICI1IikpDQoNCiMgQk9YUExPVCBQUkVDSU8gUE9SIENBVEVHT1LDjUENCnAgPC0gZ2dwbG90KGNhdGVnb3JpYXMsIGFlcyh4ID0gYXMuZmFjdG9yKENhdGVnb3J5KSwgeSA9IHByaWNlKSkgKw0KICBnZW9tX2JveHBsb3QoZmlsbCA9ICJncmV5IikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpKSsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidWNpw7NuIGRlIHByZWNpbyBwb3IgY2F0ZWdvcsOtYSBkZWwgaG90ZWwiLA0KICAgICAgIHggPSAiQ2F0ZWdvcsOtYSBkZWwgaG90ZWwiLCB5ID0gIlByZWNpbyBwb3Igbm9jaGUiKQ0KDQpvdXRsaWVyc19wIDwtIGNhdGVnb3JpYXMgJT4lDQogIGdyb3VwX2J5KENhdGVnb3J5KSAlPiUNCiAgZmlsdGVyKA0KICAgIHByaWNlIDwgcXVhbnRpbGUocHJpY2UsIDAuMjUsIG5hLnJtID0gVFJVRSkgLSAxLjUgKiBJUVIocHJpY2UsIG5hLnJtID0gVFJVRSkgfA0KICAgIHByaWNlID4gcXVhbnRpbGUocHJpY2UsIDAuNzUsIG5hLnJtID0gVFJVRSkgKyAxLjUgKiBJUVIocHJpY2UsIG5hLnJtID0gVFJVRSkNCiAgKQ0KDQpwXyA8LSBwICsgDQogIGdlb21fdGV4dChkYXRhID0gb3V0bGllcnNfcCwgYWVzKGxhYmVsID0gbmFtZSksIA0KICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBjb2xvciA9ICJkYXJrYmx1ZSIsIHNpemUgPSAyKSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnVjacOzbiBkZSBQcmVjaW8gcG9yIENhdGVnb3LDrWEiKQ0KDQojIEJPWFBMT1QgUkFUSU5HUyBQT1IgQ0FURUdPUsONQQ0KcSA8LSBnZ3Bsb3QoY2F0ZWdvcmlhcywgYWVzKHggPSBhcy5mYWN0b3IoQ2F0ZWdvcnkpLCB5ID0gcmF0aW5nKSkgKw0KICBnZW9tX2JveHBsb3QoZmlsbCA9ICJncmV5IikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpKSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFJhdGluZ3MgYnkgSG90ZWwgQ2F0ZWdvcnkiLA0KICAgICAgIHggPSAiQ2F0ZWdvcsOtYSIsIHkgPSAiQ2FsaWZpY2FjacOzbiBkZSBVc3VhcmlvcyIpDQoNCm91dGxpZXJzX3EgPC0gY2F0ZWdvcmlhcyAlPiUNCiAgZ3JvdXBfYnkoQ2F0ZWdvcnkpICU+JQ0KICBmaWx0ZXIoDQogICAgcmF0aW5nIDwgcXVhbnRpbGUocmF0aW5nLCAwLjI1LCBuYS5ybSA9IFRSVUUpIC0gMS41ICogSVFSKHJhdGluZywgbmEucm0gPSBUUlVFKSB8DQogICAgcmF0aW5nID4gcXVhbnRpbGUocmF0aW5nLCAwLjc1LCBuYS5ybSA9IFRSVUUpICsgMS41ICogSVFSKHJhdGluZywgbmEucm0gPSBUUlVFKQ0KICApDQoNCnFfIDwtIHEgKyANCiAgZ2VvbV90ZXh0KGRhdGEgPSBvdXRsaWVyc19xLCBjaGVja19vdmVybGFwID0gVFJVRSwgYWVzKGxhYmVsID0gbmFtZSksIA0KICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBjb2xvciA9ICJkYXJrYmx1ZSIsIHNpemUgPSAyKSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnVjacOzbiBkZSBDYWxpZmljYWNpb25lcyBwb3IgQ2F0ZWdvcsOtYSIpDQoNCnBfIDwtIHBfICsgY29vcmRfY2FydGVzaWFuKGNsaXAgPSAib2ZmIikNCnFfIDwtIHFfICsgY29vcmRfY2FydGVzaWFuKGNsaXAgPSAib2ZmIikNCg0KcF8gLyBxXw0KYGBgDQoNCiAgDQpFbiBjdWFudG8gYSBwcmVjaW9zLCBzZSBvYnNlcnZhIHF1ZSBsb3MgaG90ZWxlcyBkZSBjYXRlZ29yw61hcyAxIHkgMiBlc3RyZWxsYXMgcHJlc2VudGFuIHVuYSBiYWphIGRpc3BlcnNpw7NuLCBlcyBkZWNpciwgcG9jYSB2YXJpYWJpbGlkYWQgZW50cmUgbG9zIHByZWNpb3MuIEVzdG8gcHVlZGUgZGViZXJzZSBhIHVuYSBjbGllbnRlbGEgYWx0YW1lbnRlIHNlbnNpYmxlIGFsIHByZWNpbywgcXVlIHByaW9yaXphIGVsIGNvc3RvIGNvbW8gZmFjdG9yIHByaW5jaXBhbCBhbCBtb21lbnRvIGRlIGVsZWdpciBhbG9qYW1pZW50by4gWSBlbCBjdWFsIHB1ZWRlIHNlciBpbmRpY2lvIGRlIHVuIG1lcmNhZG8gbXV5IGNvbXBldGl0aXZvIGNlbnRyYWRvIGVuIGVsIHByZWNpby4gIA0KDQpQb3IgZWwgY29udHJhcmlvLCBsYXMgY2F0ZWdvcsOtYXMgMywgNCB5IDUgZXN0cmVsbGFzIG11ZXN0cmFuIHVuYSBtYXlvciBkaXNwZXJzacOzbiBlbiBsb3MgcHJlY2lvcy4gRW4gcGFydGljdWxhciwgbG9zIGhvdGVsZXMgZGUgNCBlc3RyZWxsYXMgdGllbmRlbiBhIGNvbmNlbnRyYXJzZSBlbiB1biByYW5nbyBkZSBlbnRyZSAkMSw1MDAgeSAkMiwyMDAgcGVzb3MgcG9yIG5vY2hlLCBhdW5xdWUgcHJlc2VudGFuIHRhbWJpw6luIGxhIG1heW9yIGNhbnRpZGFkIGRlIHZhbG9yZXMgYXTDrXBpY29zLiBFc3RvIHN1Z2llcmUgdW5hIG9mZXJ0YSBtw6FzIGRpdmVyc2EgZGVudHJvIGRlIGVzYSBjYXRlZ29yw61hLCBwb3NpYmxlbWVudGUgZGViaWRvIGEgZGlmZXJlbmNpYXMgZW4gYW1lbmlkYWRlcywgdWJpY2FjacOzbiwgc2VydmljaW9zIGFkaWNpb25hbGVzIG8gcG9zaWNpb25hbWllbnRvIGRlIG1hcmNhLiAgDQoNClJlc3BlY3RvIGEgbGFzIGNhbGlmaWNhY2lvbmVzIGVuIEdvb2dsZSBNYXBzLCBsYSBkaXN0cmlidWNpw7NuIHNlIGNvbXBvcnRhIGRlIGZvcm1hIGludmVyc2EuIExvcyBob3RlbGVzIGRlIGJhamEgY2F0ZWdvcsOtYSBwcmVzZW50YW4gbWF5b3IgdmFyaWFiaWxpZGFkIGVuIGxhcyBldmFsdWFjaW9uZXMgZGUgbG9zIHVzdWFyaW9zLiBFc3RvIHBvZHLDrWEgcmVmbGVqYXIgdW5hIHJlbGFjacOzbiBwcmVjaW8tY2FsaWRhZCBpbmNvbnNpc3RlbnRlOiBhbGd1bm9zIGhvdGVsZXMgZWNvbsOzbWljb3MgbG9ncmFuIHNvcnByZW5kZXIgcG9zaXRpdmFtZW50ZSwgbWllbnRyYXMgcXVlIG90cm9zIGdlbmVyYW4gZXhwZXJpZW5jaWFzIGluc2F0aXNmYWN0b3JpYXMsIHJlc3VsdGFuZG8gZW4gdW5hIG1heW9yIGhldGVyb2dlbmVpZGFkIGRlIG9waW5pb25lcy4gIA0KDQoNCioqwr9DT01PIFZBUsONQSBFTCBQUkVDSU8gREUgVU5BIE5PQ0hFIFBBUkEgRE9TIFBFUlNPTkFTIFBPUiBaT05BPyoqDQoNCk5vdGE6IFNlIHV0aWxpesOzIGxhIG5vY2hlIGRlbCAyNyBhbCAyOCBkZSBBYnJpbCAyMDI1LiBMYSBiw7pzcXVlZGEgc2UgcmVhbGl6w7MgZWwgMjYgZGUgQWJyaWwuDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgYXZlcmFnZSBwcmljZSBwZXIgY2l0eQ0KcHJlY2lvX3Bvcl9jaXVkYWQgPC0gaG90ZWxlcyAlPiUNCiAgZ3JvdXBfYnkoQ2l0eSkgJT4lDQogIHN1bW1hcmlzZShhdmdfcHJpY2UgPSBtZWFuKHByaWNlLCBuYS5ybSA9IFRSVUUpKSANCg0KcHJpbnQocHJlY2lvX3Bvcl9jaXVkYWQpDQoNCiMgUGxvdA0KZ2dwbG90KHByZWNpb19wb3JfY2l1ZGFkLCBhZXMoeCA9IENpdHksIHkgPSBhdmdfcHJpY2UsIGZpbGwgPSBDaXR5KSkgKw0KICBnZW9tX2NvbCh3aWR0aCA9IDAuOCkgKw0KICBsYWJzKHRpdGxlID0gIlByZWNpbyBwcm9tZWRpbyBwb3IgY2l1ZGFkIiwgeSA9ICJUYXJpZmEgcHJvbWVkaW8iLCB4ID0gIkNpdWRhZCIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiTW9udGVycmV5IiA9ICIjMWY3N2I0IiwgIlNhblBlZHJvIiA9ICJsaWdodGJsdWUiKSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dCgpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKGF2Z19wcmljZSkpLCB2anVzdCA9IC0wLjUsIGZvbnRmYWNlID0gImJvbGQiKQ0KYGBgDQoNCkV4cGxvcmFuZG8gbcOhcyBlbiBlbCBwcmVjaW8sIHBvZGVtb3MgdmVyIHByaW1lcmFtZW50ZSB1bmEgY2xhcmEgZGlmZXJlbmNpYSBlbiBsb3MgcHJlY2lvcyBwcm9tZWRpb3MgcG9yIGNpdWRhZC4gTGEgbm9jaGUgcHJvbWVkaW8gZW4gTW9udGVycmV5IGVzIGNlcmNhbmEgYSBsYSBtaXRhZCBkZSB1bmEgbm9jaGUgZW4gU2FuIFBlZHJvLiBMbyBjdWFsIHZhIGFsaW5lYWRvLCB5YSBxdWUgY29tbyBwcmVzZW50YW1vcyBhbnRlcmlvcm1lbnRlIGVuIGxvcyBob3RlbGVzIGRlIGJhamFzIGNhdGVnb3LDrWFzICh5IHByZWNpb3MpIHByZWRvbWluYW4gZW4gTW9udGVycmV5LiBNaWVudHJhcyBsb3MgaG90ZWxlcyBkZSBsdWpvIHNlIGNvbmNlbnRyYW4gZW4gU2FuIFBlZHJvLiAgDQoNCmBgYHtyfQ0KaG90ZWxzX2NsZWFuIDwtIGhvdGVsZXMgJT4lDQogIHNlbGVjdChuYW1lLCBsYXRpdHVkZSwgbG9uZ2l0dWRlLCBwcmljZSwgcmF0aW5nKSAlPiUNCiAgZHJvcF9uYSgpDQoNCg0KIyAxLiBPYmpldG8gU0YNCmhvdGVsc19zZiA8LSBzdF9hc19zZihob3RlbHNfY2xlYW4sIGNvb3JkcyA9IGMoImxvbmdpdHVkZSIsICJsYXRpdHVkZSIpLCBjcnMgPSA0MzI2KQ0KDQojIDIuIEJvdW5kaW5nIGJveCBkZSBtb250ZXJyZXkgKG1hcGEpDQpiYm94IDwtIHN0X2Jib3goaG90ZWxzX3NmKQ0KDQojIDMuIEdyaWQgKDEwMHgxMDApDQpncmlkIDwtIHN0X21ha2VfZ3JpZChob3RlbHNfc2YsIGNlbGxzaXplID0gMC4wMSwgc3F1YXJlID0gVFJVRSkgICMxa20gcmVzb2x1dGlvbg0KZ3JpZF9zZiA8LSBzdF9zZihnZW9tZXRyeSA9IGdyaWQpDQoNCiMgNC4gQXNpZ25hciBob3RlbGVzIGFsIGdyaWQNCmhvdGVsc19qb2luZWQgPC0gc3Rfam9pbihob3RlbHNfc2YsIGdyaWRfc2YpDQoNCiMgNS4gQ29udGVvIGhvdGVsZXMgJiBwcmVjaW8gcHJvbWVkaW8gcG9yIGNlbGRhDQpzdW1tYXJ5X2dyaWQgPC0gaG90ZWxzX2pvaW5lZCAlPiUNCiAgZ3JvdXBfYnkoZ2VvbWV0cnkpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgY291bnQgPSBuKCksDQogICAgYXZnX3ByaWNlID0gbWVhbihwcmljZSwgbmEucm0gPSBUUlVFKQ0KICApDQoNCnN1bW1hcnlfZ3JpZF9yYXRpbmcgPC0gaG90ZWxzX2pvaW5lZCAlPiUNCiAgZ3JvdXBfYnkoZ2VvbWV0cnkpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgY291bnQgPSBuKCksDQogICAgYXZnX3JhdGluZyA9IG1lYW4ocmF0aW5nLCBuYS5ybSA9IFRSVUUpDQogICkNCg0KDQojIDYuIENlbGRhcyBzaW4gaG90ZWxlcw0Kc3VtbWFyeV9ncmlkX2Z1bGwgPC0gZ3JpZF9zZiAlPiUNCiAgc3Rfam9pbihzdW1tYXJ5X2dyaWQpDQoNCnN1bW1hcnlfZ3JpZF9mdWxsX3JhdGluZyA8LSBncmlkX3NmICU+JQ0KICBzdF9qb2luKHN1bW1hcnlfZ3JpZF9yYXRpbmcpDQoNCg0KcHJpY2VfbWFwIDwtIGdnbWFwKG1vbnRlcnJleV9tYXApICsNCiAgZ2VvbV9zZihkYXRhID0gc3VtbWFyeV9ncmlkX2Z1bGwsIA0KICAgICAgICAgIGFlcyhmaWxsID0gYXZnX3ByaWNlKSwgDQogICAgICAgICAgY29sb3IgPSBOQSwgDQogICAgICAgICAgaW5oZXJpdC5hZXMgPSBGQUxTRSwgDQogICAgICAgICAgYWxwaGEgPSAwLjcpICsNCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIoDQogICAgcGFsZXR0ZSA9ICJCbHVlcyIsDQogICAgZGlyZWN0aW9uID0gLTEsDQogICAgbmEudmFsdWUgPSBOQSwNCiAgICBuYW1lID0gIlByZWNpbyBwcm9tZWRpbyIpICsNCiAgdGhlbWVfdm9pZCgpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJQcmVjaW8gUHJvbWVkaW8gcG9yIE5vY2hlIg0KICApICsgDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KDQojIyAtLS0NCnJhdGluZ3NfbWFwIDwtIGdnbWFwKG1vbnRlcnJleV9tYXApICsNCiAgZ2VvbV9zZihkYXRhID0gc3VtbWFyeV9ncmlkX2Z1bGxfcmF0aW5nLCANCiAgICAgICAgICBhZXMoZmlsbCA9IGF2Z19yYXRpbmcpLCANCiAgICAgICAgICBjb2xvciA9IE5BLCANCiAgICAgICAgICBpbmhlcml0LmFlcyA9IEZBTFNFLCANCiAgICAgICAgICBhbHBoYSA9IDAuNykgKw0KICBzY2FsZV9maWxsX2Rpc3RpbGxlcigNCiAgICBwYWxldHRlID0gIkJsdWVzIiwNCiAgICBkaXJlY3Rpb24gPSAtMSwNCiAgICBuYS52YWx1ZSA9IE5BLA0KICAgIG5hbWUgPSAiUmF0aW5nIHByb21lZGlvIikgKw0KICB0aGVtZV92b2lkKCkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlJhdGluZyBQcm9tZWRpbyINCiAgKSArIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCg0KcHJpY2VfbWFwICsgcGxvdF9zcGFjZXIoKSArICByYXRpbmdzX21hcCArDQogIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMSwgMC4wNSwgMSkpDQpgYGANCg0KIyMjIEFpcmJuYg0KDQojIyMjIFViaWNhY2lvbmVzIEFpcmJuYg0KRWwgbWFwYSBtdWVzdHJhIGxhIGRpc3RyaWJ1Y2nDs24gZGUgYWxvamFtaWVudG9zIGRlIEFpcmJuYiBlbiBsYSBab25hIE1ldHJvcG9saXRhbmEgZGUgTW9udGVycmV5LiBMb3MgcHVudG9zIGRlIGRpZmVyZW50ZXMgY29sb3JlcyBpbmRpY2FuIGxhIGFudGlnw7xlZGFkIGRlIGxvcyBhbnVuY2lvczoNCg0KKiBQdW50b3MgQXp1bGVzOiBSZXByZXNlbnRhbiBsb3MgbnVldm9zIGFsb2phbWllbnRvcyBkZSBBaXJibmIuIFN1IGRpc3BlcnNpw7NuIG5vcyBkYSB1bmEgaWRlYSBkZSBsYXMgw6FyZWFzIGRvbmRlIHJlY2llbnRlbWVudGUgc2UgaGFuIGluY29ycG9yYWRvIG3DoXMgb3BjaW9uZXMgZGUgaG9zcGVkYWplIGEgdHJhdsOpcyBkZSBsYSBwbGF0YWZvcm1hLg0KDQoqIFB1bnRvcyBSb2pvczogU2XDsWFsYW4gbG9zIGFsb2phbWllbnRvcyBkZSBBaXJibmIgbcOhcyBhbnRpZ3Vvcy4gU3UgdWJpY2FjacOzbiBub3MgbXVlc3RyYSBsYXMgem9uYXMgcGlvbmVyYXMgbyBjb24gdW5hIHByZXNlbmNpYSBtw6FzIGVzdGFibGVjaWRhIGRlIGVzdGUgdGlwbyBkZSBob3NwZWRhamUuDQoNClBvciBsbyB0YW50bywgc2Ugb2JzZXJ2YSBxdWUgbGEgYWN0aXZpZGFkIGRlIEFpcmJuYiBlbiBsYSBab25hIE1ldHJvcG9saXRhbmEgZGUgTW9udGVycmV5IGVzdMOhIGJpZW4gZXN0YWJsZWNpZGEgZW4gbG9zIG11bmljaXBpb3MgY2VudHJhbGVzIHkgZXN0w6EgZXhwZXJpbWVudGFuZG8gdW5hIGV4cGFuc2nDs24gaGFjaWEgw6FyZWFzIGNpcmN1bmRhbnRlczoNCg0KKiBDb25jZW50cmFjacOzbjogU2Ugb2JzZXJ2YSB1bmEgYWx0YSBjb25jZW50cmFjacOzbiBkZSBhbWJvcyB0aXBvcyBkZSBBaXJibmIgKG51ZXZvcyB5IHZpZWpvcykgZW4gZWwgY29yYXrDs24gZGUgbGEgem9uYSBtZXRyb3BvbGl0YW5hLCBhYmFyY2FuZG8gcHJpbmNpcGFsbWVudGUgbG9zIG11bmljaXBpb3MgZGUgTW9udGVycmV5LCBTYW4gUGVkcm8gR2FyemEgR2FyY8OtYSB5IEd1YWRhbHVwZS4gRXN0byBzdWdpZXJlIHF1ZSBlc3RhcyDDoXJlYXMgc29uIGxvcyBjZW50cm9zIGRlIG1heW9yIGFjdGl2aWRhZCB0dXLDrXN0aWNhIHkgZGVtYW5kYSBkZSBhbG9qYW1pZW50byBhIHRyYXbDqXMgZGUgQWlyYm5iLg0KDQoqIEV4cGFuc2nDs246IExvcyBwdW50b3MgYXp1bGVzIChudWV2b3MgQWlyYm5icykgcGFyZWNlbiBleHRlbmRlcnNlIGhhY2lhIEdlbmVyYWwgRXNjb2JlZG8sIEFwb2RhY2EgeSBoYWNpYSBlbCBvcmllbnRlIGRlIEd1YWRhbHVwZSwgbG8gcXVlIHBvZHLDrWEgaW5kaWNhciB1bmEgZXhwYW5zacOzbiBkZSBsYSBvZmVydGEgZGUgQWlyYm5iIGhhY2lhIGVzdGFzIHpvbmFzIG3DoXMgcGVyaWbDqXJpY2FzIG8gZW4gZGVzYXJyb2xsbzsgbG8gY3VhbCBvZHLDrWEgZXN0YXIgaW1wdWxzYWRvIHBvciB1biBhdW1lbnRvIGRlIGxhIGRlbWFuZGEgZW4gZXN0YXMgw6FyZWFzIG8gcG9yIGxhIGRpc3BvbmliaWxpZGFkIGRlIG51ZXZhcyBwcm9waWVkYWRlcy4NCg0KKiBQcmVzZW5jaWEgRXN0YWJsZWNpZGE6IExvcyBwdW50b3Mgcm9qb3MgKHZpZWpvcyBBaXJibmJzKSBwYXJlY2VuIHRlbmVyIHVuYSBwcmVzZW5jaWEgcGFydGljdWxhcm1lbnRlIGZ1ZXJ0ZSBlbiBTYW4gUGVkcm8gR2FyemEgR2FyY8OtYSB5IGNpZXJ0YXMgem9uYXMgY8OpbnRyaWNhcyBkZSBNb250ZXJyZXk7IHJlZmxlamFuZG8gbGEgcG9wdWxhcmlkYWQgaW5pY2lhbCBkZSBBaXJibmIgZW4gZXN0YXMgw6FyZWFzIHkgbGEgY29udGludWlkYWQgZGUgbG9zIGFsb2phbWllbnRvcyBxdWUgaGFuIGVzdGFkbyBlbiBsYSBwbGF0YWZvcm1hIGR1cmFudGUgbcOhcyB0aWVtcG8uDQoNCmBgYHtyfQ0KbGlicmFyeShsZWFmbGV0KQ0KbGlicmFyeShzZikgDQpsaWJyYXJ5KGxlYWZsZXQpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShkcGx5cikNCg0KIyBMZWVyIGVsIEdlb0pTT04NCnpvbmFzIDwtIHN0X3JlYWQoIm5sX21hcC5nZW9qc29uIikNCg0KIyBDYXTDoWxvZ28gZGUgbXVuaWNpcGlvcyBjb24gem9uYXMNCmNhdGFsb2dvX211bmljaXBpb3MgPC0gZGF0YS5mcmFtZSgNCiAgQ09ERUxBRyA9IGMoMjE5OToyMjQ5KSwNCiAgbXVuaWNpcGlvID0gYygiQWJhc29sbyIsICJBZ3VhbGVndWFzIiwgIkxvcyBBbGRhbWFzIiwgIkFsbGVuZGUiLCAiQW7DoWh1YWMiLCAiQXBvZGFjYSIsDQogICAgICAgICAgICAgICAgIkFyYW1iZXJyaSIsICJCdXN0YW1hbnRlIiwgIkNhZGVyZXl0YSBKaW3DqW5leiIsICJFbCBDYXJtZW4iLCAiQ2VycmFsdm8iLA0KICAgICAgICAgICAgICAgICJDacOpbmVnYSBkZSBGbG9yZXMiLCAiQ2hpbmEiLCAiRG9jdG9yIEFycm95byIsICJEb2N0b3IgQ29zcyIsICJEb2N0b3IgR29uesOhbGV6IiwNCiAgICAgICAgICAgICAgICAiR2FsZWFuYSIsICJHYXJjw61hIiwgIlNhbiBQZWRybyBHYXJ6YSBHYXJjw61hIiwgIkdlbmVyYWwgQnJhdm8iLCAiR2VuZXJhbCBFc2NvYmVkbyIsDQogICAgICAgICAgICAgICAgIkdlbmVyYWwgVGVyw6FuIiwgIkdlbmVyYWwgVHJldmnDsW8iLCAiR2VuZXJhbCBaYXJhZ296YSIsICJHZW5lcmFsIFp1YXp1YSIsIA0KICAgICAgICAgICAgICAgICJHdWFkYWx1cGUiLCAiSGlkYWxnbyIsICJIaWd1ZXJhcyIsICJIdWFsYWh1aXNlcyIsICJJdHVyYmlkZSIsICJKdcOhcmV6IiwgDQogICAgICAgICAgICAgICAgIkxhbXBhem9zIGRlIE5hcmFuam8iLCAiTGluYXJlcyIsICJNYXLDrW4iLCAiTWVsY2hvciBPY2FtcG8iLCAiTWllciB5IE5vcmllZ2EiLA0KICAgICAgICAgICAgICAgICJNaW5hIiwgIk1vbnRlbW9yZWxvcyIsICJNb250ZXJyZXkiLCAiUGFyw6FzIiwgIlBlc3F1ZXLDrWEiLCAiTG9zIFJhbW9uZXMiLCANCiAgICAgICAgICAgICAgICAiUmF5b25lcyIsICJTYWJpbmFzIEhpZGFsZ28iLCAiU2FsaW5hcyBWaWN0b3JpYSIsICJTYW4gTmljb2zDoXMgZGUgbG9zIEdhcnphIiwgDQogICAgICAgICAgICAgICAgIkhpZGFsZ28iLCAiU2FudGEgQ2F0YXJpbmEiLCAiU2FudGlhZ28iLCAiVmFsbGVjaWxsbyIsICJWaWxsYWxkYW1hIiksDQogIHpvbmEgPSBjKCJPdHJhIiwgIk90cmEiLCAiT3RyYSIsICJTdXIiLCAiTm9ydGUiLCAiTm9ydGUiLCAiT3RyYSIsICJOb3J0ZSIsICJFc3RlIiwgIk5vcnRlIiwgDQogICAgICAgICAgICJOb3J0ZSIsICJOb3J0ZSIsICJPdHJhIiwgIk90cmEiLCAiT3RyYSIsICJPdHJhIiwgIlN1ciIsICJPZXN0ZSIsICJPZXN0ZSIsICJFc3RlIiwNCiAgICAgICAgICAgIk5vcnRlIiwgIlN1ciIsICJPdHJhIiwgIlN1ciIsICJOb3J0ZSIsICJFc3RlIiwgIk5vcnRlIiwgIk90cmEiLCAiT3RyYSIsICJTdXIiLA0KICAgICAgICAgICAiRXN0ZSIsICJOb3J0ZSIsICJTdXIiLCAiTm9ydGUiLCAiT3RyYSIsICJPdHJhIiwgIk5vcnRlIiwgIlN1ciIsICJPZXN0ZSIsICJOb3J0ZSIsDQogICAgICAgICAgICJFc3RlIiwgIlN1ciIsICJOb3J0ZSIsICJOb3J0ZSIsICJOb3J0ZSIsICJPZXN0ZSIsICJOb3J0ZSIsICJPZXN0ZSIsICJTdXIiLCAiTm9ydGUiLA0KICAgICAgICAgICAiTm9ydGUiKQ0KKQ0KDQojIFVuaXIgem9uYXMgY29uIG11bmljaXBpb3MNCnpvbmFzIDwtIGRwbHlyOjpsZWZ0X2pvaW4oem9uYXMsIGNhdGFsb2dvX211bmljaXBpb3MsIGJ5ID0gIkNPREVMQUciKQ0KDQojIFBhbGV0YSBkZSBjb2xvcmVzIHBhcmEgem9uYXMgKHNlIG1hbnRpZW5lIGNvbW8gdW4gc29sbyBjb2xvcikNCmNvbG9yZXNfem9uYXMgPC0gY29sb3JGYWN0b3IoDQogIHBhbGV0dGUgPSBjKCJwdXJwbGUiKSwNCiAgZG9tYWluID0gem9uYXMkem9uYQ0KKQ0KDQpkZl9haXIkY29sb3IgPC0gaWZlbHNlKGRmX2FpciRNZXNlc19BbnRpZ3VlZGFkID09IDAsICJyZWQiLCAiYmx1ZSIpDQoNCiMgTWFwYSBpbnRlcmFjdGl2bw0KbGVhZmxldCgpICU+JQ0KICBhZGRUaWxlcygpICU+JQ0KICBzZXRWaWV3KGxuZyA9IC0xMDAuMzE2MSwgbGF0ID0gMjUuNjg2Niwgem9vbSA9IDExLjUpICU+JQ0KICBhZGRQb2x5Z29ucyhkYXRhID0gem9uYXMsDQogICAgICAgICAgICAgIGZpbGxDb2xvciA9IH5jb2xvcmVzX3pvbmFzKHpvbmEpLA0KICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuMiwNCiAgICAgICAgICAgICAgd2VpZ2h0ID0gMiwNCiAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLA0KICAgICAgICAgICAgICBwb3B1cCA9IH5wYXN0ZSgiTXVuaWNpcGlvOiIsIG11bmljaXBpbywgIjxicj5ab25hOiIsIHpvbmEpKSAlPiUNCiAgYWRkQ2lyY2xlTWFya2VycygNCiAgICBkYXRhID0gZGZfYWlyLA0KICAgIGxuZyA9IH5Mb25naXR1ZCwgbGF0ID0gfkxhdGl0dWQsDQogICAgcmFkaXVzID0gNiwNCiAgICBjb2xvciA9IH5jb2xvciwNCiAgICBzdHJva2UgPSBGQUxTRSwgZmlsbE9wYWNpdHkgPSAwLjgsDQogICAgcG9wdXAgPSB+cGFzdGUoIlByZWNpbzoiLCBQcmVjaW8sICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiTm9tYnJlOiIsIE5vbWJyZSwgIjxicj4iLA0KICAgICAgICAgICAgICAgICAgICJDYWxpZmljYWNpw7NuOiIsIENhbGlmaWNhY2nDs24sICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiTWVzZXMgZGUgQW50aWfDvGVkYWQ6IiwgTWVzZXNfQW50aWd1ZWRhZCkNCiAgKSAlPiUNCiAgYWRkQ29udHJvbCgiPHN0cm9uZz5VYmljYWNpb25lcyBkZSBBaXJibmIgZW4gbGEgWm9uYSBNZXRyb3BvbGl0YW5hIGRlIE1vbnRlcnJleTwvc3Ryb25nPiIsIHBvc2l0aW9uID0gInRvcHJpZ2h0IikNCmBgYA0KDQojIyMjIFByZWNpb3MgQWlyYm5iDQoNCkVsIG1hcGEgZGUgY2Fsb3IgZGUgcHJlY2lvcyBkZSBBaXJibmIgZW4gbGEgWm9uYSBNZXRyb3BvbGl0YW5hIGRlIE1vbnRlcnJleSByZXZlbGEgdW5hIGNsYXJhIHNlZ21lbnRhY2nDs24gZ2VvZ3LDoWZpY2EgZGUgbG9zIHByZWNpb3MuIExhcyB6b25hcyBjb24gbWF5b3IgdmFsb3IgaW5tb2JpbGlhcmlvIHkgYWN0aXZpZGFkIGVjb27Ds21pY2EgKFNhbiBQZWRybywgY2VudHJvIHkgc3VyIGRlIE1vbnRlcnJleSkgdGllbmRlbiBhIGNvbmNlbnRyYXIgbG9zIGFsb2phbWllbnRvcyBkZSBtYXlvciBwcmVjaW8sIG1pZW50cmFzIHF1ZSBsYXMgw6FyZWFzIG3DoXMgYWxlamFkYXMgZGVsIGNlbnRybyBvIGNvbiB1biBkZXNhcnJvbGxvIHNvY2lvZWNvbsOzbWljbyBkaWZlcmVudGUgb2ZyZWNlbiB1bmEgbWF5b3IgcHJvcG9yY2nDs24gZGUgYWxvamFtaWVudG9zIG3DoXMgZWNvbsOzbWljb3MuIA0KDQoqICoqUHJlY2lvcyBBbHRvczoqKiBMYXMgw6FyZWFzIGNvbiBtYXlvciBkZW5zaWRhZCBkZSBwcmVjaW9zIGFsdG9zIChyb2pvL25hcmFuamEpIHNlIGxvY2FsaXphbiBwcmluY2lwYWxtZW50ZSBlbiBTYW4gUGVkcm8gR2FyemEgR2FyY8OtYSwgeWEgcXVlIFNhbiBQZWRybyBlcyBjb25vY2lkbyBwb3Igc2VyIHVuIG11bmljaXBpbyBjb24gdW4gYWx0byBuaXZlbCBzb2Npb2Vjb27Ds21pY28geSwgcG9yIGxvIHRhbnRvLCBlcyBwcm9iYWJsZSBxdWUgbG9zIHByZWNpb3MgZGUgbG9zIGFsb2phbWllbnRvcyBzZWFuIG3DoXMgZWxldmFkb3MuIEFzaW1pc21vLCBsYSBab25hIENlbnRybyB5IFN1ciBkZSBNb250ZXJyZXkgdGllbmUgdW5hIGNvbmNlbnRyYWNpw7NuIGRlIHByZWNpb3MgYWx0b3MgbG8gY3VhbCBwb2Ryw61hIGRlYmVyc2UgYSBsYSBwcm94aW1pZGFkIGEgYXRyYWN0aXZvcyB0dXLDrXN0aWNvcywgY2VudHJvcyBkZSBuZWdvY2lvcyB5IHpvbmFzIHJlc2lkZW5jaWFsZXMgZGUgbWF5b3IgdmFsb3IuIFNpbiBlbWJhcmdvLCBleGlzdGVuIHB1bnRvcyBhaXNsYWRvcyBxdWUgcG9kcsOtYW4gY29ycmVzcG9uZGVyIGEgYWxvamFtaWVudG9zIGRlIGx1am8gbyB1YmljYWRvcyBlbiB6b25hcyBlc3BlY8OtZmljYXMgZGUgaW50ZXLDqXMuDQoNCiogKipQcmVjaW9zIE1lZGlvczoqKiBMYXMgem9uYXMgY29uIHVuYSBkZW5zaWRhZCBtZWRpYSBkZSBwcmVjaW9zIChhbWFyaWxsby92ZXJkZSkgc29uIG3DoXMgZXh0ZW5zYXMgeSBzZSBlbmN1ZW50cmFuIHJvZGVhbmRvIGxhcyDDoXJlYXMgZGUgcHJlY2lvcyBhbHRvcy4gTW9udGVycmV5IHRpZW5lIGFtcGxpYXMgem9uYXMgZnVlcmEgZGVsIGNlbnRybyB5IGRlbCBzdXIuIFRhbWJpw6luLCBHdWFkYWx1cGUgcHJpbmNpcGFsbWVudGUgZW4gbGEgcGFydGUgb2VzdGUgeSBjZW50cmFsIGRlbCBtdW5pY2lwaW8uIERlIGlndWFsIG1hbmVyYSwgU2FuIE5pY29sw6FzIGRlIGxvcyBHYXJ6YSwgR2VuZXJhbCBFc2NvYmVkbyB5IEFwb2RhY2EgY3VlbnRhbiBjb24gYWxndW5hcyB6b25hcyBjb24gdW5hIGRlbnNpZGFkIG1lZGlhIGRlIHByZWNpb3MuDQoNCiogKipQcmVjaW9zIEJham9zOioqIFNlIG9ic2VydmFuIGNvbmNlbnRyYWNpb25lcyBkZSBwcmVjaW9zIG3DoXMgYmFqb3MgZW4gbGEgcGVyaWZlcmlhIGRlIGxhIFpNTSwgY29tbyBlbiBsYSBwYXJ0ZSBub3J0ZSBkZSBHZW5lcmFsIEVzY29iZWRvLCBlbCBvcmllbnRlIGRlIEFwb2RhY2EgeSBsYSB6b25hIG9yaWVudGUgZGUgR3VhZGFsdXBlLkFzaW1pc21vLCBlbiDDoXJlYXMgY29uIG1lbm9yIGRlc2Fycm9sbG8gdHVyw61zdGljbyBvIGNvbWVyY2lhbCBsbyBxdWUgaW1wbGljYSB1bmEgbWVub3IgZGVtYW5kYSBvIHVuYSBvZmVydGEgZGUgYWxvamFtaWVudG9zIG3DoXMgc2VuY2lsbG9zLg0KDQpgYGB7cn0NCmxpYnJhcnkobGVhZmxldCkNCmxpYnJhcnkobGVhZmxldC5leHRyYXMpDQoNCnBhbCA8LSBjb2xvck51bWVyaWMoDQogIHBhbGV0dGUgPSAiWWxPclJkIiwgDQogIGRvbWFpbiA9IE5VTEwNCikNCmxlYWZsZXQoZGF0YSA9IGRmX2FpcikgJT4lDQogIGFkZFRpbGVzKCkgJT4lDQogIHNldFZpZXcobG5nID0gLTEwMC4zMTYxLCBsYXQgPSAyNS42ODY2LCB6b29tID0gMTIpICU+JQ0KICBhZGRIZWF0bWFwKA0KICAgIGxuZyA9IH5Mb25naXR1ZCwNCiAgICBsYXQgPSB+TGF0aXR1ZCwNCiAgICBpbnRlbnNpdHkgPSB+UHJlY2lvLCAgICMgQXF1w60gc2UgdXNhIGVsIHByZWNpbyBwYXJhIGxhIGludGVuc2lkYWQgZGVsIGNhbG9yDQogICAgYmx1ciA9IDIwLA0KICAgIG1heCA9IDAuMDUsDQogICAgcmFkaXVzID0gMTUNCiAgKSAlPiUNCiAgYWRkQ29udHJvbCgiPHN0cm9uZz5QcmVjaW9zIGRlIEFpcmJuYiBlbiBsYSBab25hIE1ldHJvcG9saXRhbmEgZGUgTW9udGVycmV5PC9zdHJvbmc+IiwgcG9zaXRpb24gPSAidG9wcmlnaHQiKQ0KYGBgDQoNCiMjIyMgQ2FsaWZpY2FjacOzbiBBaXJibmINCg0KU2Ugb2JzcnZhIHF1ZSBoYXkgdmFyaWFzIHpvbmFzIGRlbnRybyBkZSBsYSBab25hIE1ldHJvcG9saXRhbmEgZGUgTW9udGVycmV5IGRvbmRlIGxvcyBhbG9qYW1pZW50b3MgZGUgQWlyYm5iIHRpZW5kZW4gYSB0ZW5lciBtZWpvcmVzIGNhbGlmaWNhY2lvbmVzLiBBbGd1bmFzIGRlIGVzdGFzIMOhcmVhcyBwYXJlY2VuIGVzdGFyIHViaWNhZGFzIGVuOg0KDQoqIFNhbiBOaWNvbMOhcyBkZSBsb3MgR2FyemE6IFNlIGFwcmVjaWEgdW5hIHpvbmEgZGUgYWx0YSBjYWxpZmljYWNpw7NuIGVuIGVsIGNlbnRybyBkZSBlc3RlIG11bmljaXBpby4NCg0KKiBNb250ZXJyZXk6IEhheSB2YXJpYXMgw6FyZWFzIGNvbiBhbHRhIGNhbGlmaWNhY2nDs24gZGlzdHJpYnVpZGFzIGRlbnRybyBkZSBNb250ZXJyZXksIGluY2x1eWVuZG8gdW5hIHpvbmEgbm90YWJsZSBoYWNpYSBlbCBwb25pZW50ZSB5IG90cmEgbcOhcyBoYWNpYSBlbCBzdXIuDQoNCiogU2FuIFBlZHJvIEdhcnphIEdhcmPDrWE6IExhIHpvbmEgbcOhcyBvY2NpZGVudGFsIHF1ZSBzZSBhbGNhbnphIGEgdmVyIG11ZXN0cmEgdGFtYmnDqW4gdW5hIGNvbmNlbnRyYWNpw7NuIGRlIGFsb2phbWllbnRvcyBjb24gYnVlbmFzIGNhbGlmaWNhY2lvbmVzLg0KDQpgYGB7cn0NCmxlYWZsZXQoZGF0YSA9IGRmX2FpcikgJT4lDQogIGFkZFRpbGVzKCkgJT4lDQogIHNldFZpZXcobG5nID0gLTEwMC4zMTYxLCBsYXQgPSAyNS42ODY2LCB6b29tID0gMTIpICU+JQ0KICBhZGRIZWF0bWFwKA0KICAgIGxuZyA9IH5Mb25naXR1ZCwNCiAgICBsYXQgPSB+TGF0aXR1ZCwNCiAgICBpbnRlbnNpdHkgPSB+Q2FsaWZpY2FjacOzbiwgICAjIEFxdcOtIHNlIHVzYSBlbCBwcmVjaW8gcGFyYSBsYSBpbnRlbnNpZGFkIGRlbCBjYWxvcg0KICAgIGJsdXIgPSAyMCwNCiAgICBtYXggPSAwLjA1LA0KICAgIHJhZGl1cyA9IDE1DQogICkgJT4lDQogIGFkZENvbnRyb2woIjxzdHJvbmc+Q2FsaWZpY2FjacOzbiBkZSBBaXJibmIgZW4gbGEgWm9uYSBNZXRyb3BvbGl0YW5hIGRlIE1vbnRlcnJleTwvc3Ryb25nPiIsIHBvc2l0aW9uID0gInRvcHJpZ2h0IikNCmBgYA0KDQoNCiMjIyBJbmR1c3RyaWEgSG9zcGl0YWxpZGFkIChBaXJibmIgJiBIb3RlbGVzKQ0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGxlYWZsZXQpDQpsaWJyYXJ5KGxlYWZsZXQuZXh0cmFzKQ0KDQpkZl9haXJfbGltcCA8LSBkZl9haXIgJT4lDQogIHNlbGVjdChOb21icmUsIExhdGl0dWQsIExvbmdpdHVkKSAlPiUNCiAgbXV0YXRlKFRpcG8gPSAiQWlyYm5iIikNCg0KaG90ZWxlc19saW1wIDwtIGhvdGVsZXNfZGFudWUgJT4lDQogIHRyYW5zbXV0ZSgNCiAgICBOb21icmUgPSBob3RlbCwNCiAgICBMYXRpdHVkID0gTGF0aXR1ZCwNCiAgICBMb25naXR1ZCA9IExvbmdpdHVkLA0KICAgIFRpcG8gPSAiSG90ZWwiDQogICkNCg0KbHVnYXJlc190dXJpc3RpY29zIDwtIGJpbmRfcm93cyhkZl9haXJfbGltcCwgaG90ZWxlc19saW1wKQ0KDQpwdW50b3NfaW50ZXJlcyA8LSBkYXRhLmZyYW1lKA0KICBOb21icmUgPSBjKCJFc3RhZGlvIEJCVkEiLCAiUGFycXVlIEZ1bmRpZG9yYSIsICJBZXJvcHVlcnRvIGRlIE1vbnRlcnJleSIpLA0KICBMYXRpdHVkID0gYygyNS42NzE0LCAyNS42NzU4LCAyNS43Nzg1KSwNCiAgTG9uZ2l0dWQgPSBjKC0xMDAuMjQ0MCwgLTEwMC4yODkzLCAtMTAwLjEwNzApLA0KICBUaXBvID0gYygiUHVudG8gZGUgSW50ZXLDqXMiLCAiUHVudG8gZGUgSW50ZXLDqXMiLCAiUHVudG8gZGUgSW50ZXLDqXMiKQ0KKQ0KDQpsdWdhcmVzX3R1cmlzdGljb3NfdG9kb3MgPC0gYmluZF9yb3dzKGx1Z2FyZXNfdHVyaXN0aWNvcywgcHVudG9zX2ludGVyZXMpDQoNCiMgQ3JlYXIgZWwgbWFwYQ0KbGVhZmxldChsdWdhcmVzX3R1cmlzdGljb3NfdG9kb3MpICU+JQ0KICBhZGRUaWxlcygpICU+JQ0KICBhZGRIZWF0bWFwKA0KICAgIGxuZyA9IH5Mb25naXR1ZCwNCiAgICBsYXQgPSB+TGF0aXR1ZCwNCiAgICBibHVyID0gMjAsDQogICAgbWF4ID0gMC4wNSwNCiAgICByYWRpdXMgPSAxNQ0KICApICU+JQ0KICBhZGRDaXJjbGVNYXJrZXJzKA0KICAgIGRhdGEgPSBzdWJzZXQobHVnYXJlc190dXJpc3RpY29zX3RvZG9zLCBUaXBvID09ICJBaXJibmIiKSwNCiAgICBsbmcgPSB+TG9uZ2l0dWQsDQogICAgbGF0ID0gfkxhdGl0dWQsDQogICAgcmFkaXVzID0gNCwNCiAgICBjb2xvciA9ICJibGFjayIsDQogICAgZmlsbE9wYWNpdHkgPSAwLjgsDQogICAgcG9wdXAgPSB+Tm9tYnJlDQogICkgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoDQogICAgZGF0YSA9IHN1YnNldChsdWdhcmVzX3R1cmlzdGljb3NfdG9kb3MsIFRpcG8gPT0gIkhvdGVsIiksDQogICAgbG5nID0gfkxvbmdpdHVkLA0KICAgIGxhdCA9IH5MYXRpdHVkLA0KICAgIHJhZGl1cyA9IDQsDQogICAgY29sb3IgPSAiYmx1ZSIsDQogICAgZmlsbE9wYWNpdHkgPSAwLjgsDQogICAgcG9wdXAgPSB+Tm9tYnJlDQogICkgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoDQogICAgZGF0YSA9IHN1YnNldChsdWdhcmVzX3R1cmlzdGljb3NfdG9kb3MsIFRpcG8gPT0gIlB1bnRvIGRlIEludGVyw6lzIiksDQogICAgbG5nID0gfkxvbmdpdHVkLA0KICAgIGxhdCA9IH5MYXRpdHVkLA0KICAgIHJhZGl1cyA9IDgsDQogICAgY29sb3IgPSAicmVkIiwNCiAgICBmaWxsQ29sb3IgPSAieWVsbG93IiwNCiAgICBmaWxsT3BhY2l0eSA9IDEsDQogICAgcG9wdXAgPSB+Tm9tYnJlDQogICkgJT4lDQogIGFkZENvbnRyb2woIjxzdHJvbmc+TWFwYSBkZSBBaXJibmIsIEhvdGVsZXMgeSBQdW50b3MgQ2xhdmU8L3N0cm9uZz4iLCBwb3NpdGlvbiA9ICJ0b3ByaWdodCIpDQoNCmBgYA0KDQoNCiMjIyMgQWdydXBhbWllbnRvIEVzcGFjaWFsDQoNCkhheSBldmlkZW5jaWEgZnVlcnRlIGRlIHF1ZSBsb3MgaG90ZWxlcyBubyBlc3TDoW4gZGlzdHJpYnVpZG9zIGFsIGF6YXIsIHNpbm8gcXVlIHRpZW5kZW4gYSBhZ3J1cGFyc2UgZXNwYWNpYWxtZW50ZSBlbiBsYXMgY29vcmRlbmFkYXMgZGFkYXMuDQoNCiogTW9yYW4gSSBzdGF0aXN0aWMgPSAwLjc3OiBWYWxvciBhbHRvIHkgcG9zaXRpdm8gaW5kaWNhIGZ1ZXJ0ZSBhdXRvY29ycmVsYWNpw7NuIGVzcGFjaWFsIHBvc2l0aXZhLiBFc3RvIHNpZ25pZmljYSBxdWUgbG9zIGx1Z2FyZXMgcXVlIHNvbiBob3RlbGVzIHRpZW5kZW4gYSBlc3RhciBjZXJjYW5vcyBlbnRyZSBzw60sIGZvcm1hbmRvIGFncnVwYW1pZW50b3MgZXNwYWNpYWxlcy4NCg0KKiBwLXZhbHVlIG11eSBwZXF1ZcOxbyAoPCAyLjJlLTE2KTogRXMgZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2bywgbG8gcXVlIHJlY2hhemEgbGEgaGlww7N0ZXNpcyBudWxhIGRlIGF1c2VuY2lhIGRlIGF1dG9jb3JyZWxhY2nDs24gZXNwYWNpYWwuDQoNCmBgYHtyfQ0KIyBDcmVhciB2YXJpYWJsZSBiaW5hcmlhOiAxIHNpIGVzIEhvdGVsLCAwIHNpIGVzIEFpcmJuYg0KbHVnYXJlc190dXJpc3RpY29zJGVzX2hvdGVsIDwtIGlmZWxzZShsdWdhcmVzX3R1cmlzdGljb3MkVGlwbyA9PSAiSG90ZWwiLCAxLCAwKQ0KbGlicmFyeShzcGRlcCkNCg0KY29vcmRzXzI0IDwtIGNiaW5kKGx1Z2FyZXNfdHVyaXN0aWNvcyRMb25naXR1ZCwgbHVnYXJlc190dXJpc3RpY29zJExhdGl0dWQpDQoNCiMgRWxpbWluYSBwdW50b3MgZHVwbGljYWRvcyBwYXJhIGV2aXRhciB3YXJuaW5ncw0KY29vcmRzXzI0X3VuaWNvcyA8LSB1bmlxdWUoY29vcmRzXzI0KQ0Ka25uNSA8LSBrbmVhcm5laWdoKGNvb3Jkc18yNF91bmljb3MsIGsgPSA1KQ0KbmI1IDwtIGtubjJuYihrbm41KQ0KbGlzdHc1IDwtIG5iMmxpc3R3KG5iNSwgc3R5bGUgPSAiVyIpDQojIEZpbHRyYXIgbG9zIGRhdG9zIMO6bmljb3MNCmx1Z2FyZXNfdW5pY29zIDwtIGx1Z2FyZXNfdHVyaXN0aWNvc1shZHVwbGljYXRlZChjb29yZHNfMjQpLCBdDQptb3Jhbi50ZXN0KGx1Z2FyZXNfdW5pY29zJGVzX2hvdGVsLCBsaXN0dzUpDQpgYGANCg0KIyMjIyBEaXN0YW5jaWEgYSBFc3RhZGlvIEJCVkEgJiBQYXJxdWUgRnVuZGlkb3JhDQoNCiogTGEgbWF5b3LDrWEgZGUgbG9zIGhvc3BlZGFqZXMgc2UgZW5jdWVudHJhbiBhIGVudHJlIDcgeSAxMSBrbSBkZWwgRXN0YWRpbyBCQlZBLg0KDQoqIExvcyBob3NwZWRhamVzIGVzdMOhbiB1biBwb2NvIG3DoXMgY2VyY2EsIGVuIHByb21lZGlvLCBkZWwgUGFycXVlIEZ1bmRpZG9yYSAobWVkaWEgZGUgNy41IGttIGZyZW50ZSBhIDEwIGttIHBhcmEgZWwgZXN0YWRpbykuDQoNCiogQWxndW5vcyBob3NwZWRhamVzIGVzdMOhbiBleHRyZW1hZGFtZW50ZSBsZWpvcyAobcOhcyBkZSA2MCBrbSksIGxvIHF1ZSBzdWdpZXJlIHF1ZSBlbCBjb25qdW50byBkZSBkYXRvcyBpbmNsdXllIHViaWNhY2lvbmVzIHBvc2libGVtZW50ZSBmdWVyYSBkZWwgw6FyZWEgbWV0cm9wb2xpdGFuYSBkZSBNb250ZXJyZXkuDQoNCiogTGEgbWVkaWFuYSA8IG1lZGlhIGVuIGFtYm9zIGNhc29zLCBsbyBxdWUgaW5kaWNhIHVuYSBkaXN0cmlidWNpw7NuIGFzaW3DqXRyaWNhIGEgbGEgZGVyZWNoYTogaGF5IGFsZ3Vub3MgaG9zcGVkYWplcyBtdXkgbGVqYW5vcyBxdWUgZWxldmFuIGVsIHByb21lZGlvLg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2Vvc3BoZXJlKQ0KDQojIENhbGN1bGFyIGRpc3RhbmNpYSBkZSBjYWRhIEhvc3BlZGFqZSBhbCBFc3RhZGlvIEJCVkENCmx1Z2FyZXNfdHVyaXN0aWNvcyRkaXN0X2JidmEgPC0gZGlzdEhhdmVyc2luZSgNCiAgbWF0cml4KGMobHVnYXJlc190dXJpc3RpY29zJExvbmdpdHVkLCBsdWdhcmVzX3R1cmlzdGljb3MkTGF0aXR1ZCksIG5jb2wgPSAyKSwNCiAgYygtMTAwLjI0NDU2NDU3MzAxNDMsIDI1LjY2OTM0NTM0NTEwNzM5NSkNCikNCg0KIyBDYWxjdWxhciBkaXN0YW5jaWEgZGUgY2FkYSBIb3NwZWRhamUgYWwgUGFycXVlIEZ1bmRpZG9yYQ0KbHVnYXJlc190dXJpc3RpY29zJGRpc3RfZnVuZGkgPC0gZGlzdEhhdmVyc2luZSgNCiAgbWF0cml4KGMobHVnYXJlc190dXJpc3RpY29zJExvbmdpdHVkLCBsdWdhcmVzX3R1cmlzdGljb3MkTGF0aXR1ZCksIG5jb2wgPSAyKSwNCiAgYygtMTAwLjI4NDIzMzQ4NjUwNzE1LCAyNS42Nzg4OTMwNDIzNTQwNSkNCikNCnN1bW1hcnkobHVnYXJlc190dXJpc3RpY29zJGRpc3RfYmJ2YSkNCnN1bW1hcnkobHVnYXJlc190dXJpc3RpY29zJGRpc3RfZnVuZGkpDQpgYGANCg0KIyMjIyBNYXBhIGRlIElzb2Nyb25hcw0KDQoqIEVsIHB1bnRvIGRlIG9yaWdlbiwgZWwgRXN0YWRpbyBCQlZBLCB0aWVuZSB1bmEgY29uZWN0aXZpZGFkIHZpYWwgYnVlbmEgeSBkaXN0cmlidWlkYSByYWRpYWxtZW50ZSwgYWxjYW56YW5kbyB6b25hcyBjbGF2ZSBkZSBNb250ZXJyZXksIEd1YWRhbHVwZSB5IHBhcnRlIGRlIFNhbiBOaWNvbMOhcy4NCg0KKiBMb3MgbmVnb2Npb3MgdHVyw61zdGljb3MgKGFsb2phbWllbnRvcywgcmVzdGF1cmFudGVzLCB0cmFuc3BvcnRlKSB1YmljYWRvcyBkZW50cm8gZGUgbGFzIGlzb2Nyb25hcyBtZW5vcmVzIHBvZHLDrWFuIHRlbmVyIG1heW9yIHZhbG9yIG8gZGVtYW5kYSBkZWJpZG8gYSBxdWUgc2UgZW5jdWVudHJhbiBhIG1lbm9zIGRlIDUgbyAxMCBtaW51dG9zIGRlbCBFc3RhZGlvIEJCVkE7IHByaW5jaXBhbG1lbnRlLCBkdXJhbnRlIGVsIE11bmRpYWwgMjAyNi4NCg0KDQpgYGB7cn0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsZWFmbGV0KQ0KbGlicmFyeShvc3JtKQ0KbGlicmFyeShvc3JtKQ0KDQojIENvb3JkZW5hZGFzIGRlbCBwdW50byBkZSBwYXJ0aWRhIChlamVtcGxvOiBFc3RhZGlvIEJCVkEpDQpjb29yZHMgPC0gYygtMTAwLjI0NDU2NDU3MzAxNDMsIDI1LjY2OTM0NTM0NTEwNzM5NSkNCg0KIyBDb252ZXJ0aXIgY29vcmRlbmFkYXMgYSBgc2ZgDQpwdW50b19pbmljaW8gPC0gc3Rfc2YoDQogIGlkID0gIm9yaWdlbiIsDQogIGdlb21ldHJ5ID0gc3Rfc2ZjKHN0X3BvaW50KGNvb3JkcyksIGNycyA9IDQzMjYpDQopDQoNCiMgR2VuZXJhciBpc29jcm9uYXMgZGUgNSwgMTAgeSAxNSBtaW51dG9zDQppc28gPC0gb3NybUlzb2Nocm9uZShsb2MgPSBwdW50b19pbmljaW8sIGJyZWFrcyA9IGMoNSwgMTAsIDE1KSkNCg0KIyBSZXBhcmFyIGdlb21ldHLDrWFzIHNpIGVzIG5lY2VzYXJpbw0KaXNvIDwtIHN0X21ha2VfdmFsaWQoaXNvKQ0KDQpsdWdhcmVzX3NmIDwtIHN0X2FzX3NmKGx1Z2FyZXNfdHVyaXN0aWNvcywgY29vcmRzID0gYygiTG9uZ2l0dWQiLCAiTGF0aXR1ZCIpLCBjcnMgPSA0MzI2KQ0KDQojIEx1ZWdvIHPDrSBwdWVkZXMgaGFjZXIgZWwgam9pbg0KbHVnYXJlc19jb25faXNvIDwtIHN0X2pvaW4obHVnYXJlc19zZiwgaXNvLCBqb2luID0gc3Rfd2l0aGluKQ0KDQojIENyZWFyIHBhbGV0YQ0KcGFsIDwtIGNvbG9yRmFjdG9yKHBhbGV0dGUgPSAiT3JhbmdlcyIsIGRvbWFpbiA9IGlzbyRpc29tYXgpDQoNCiMgQ3JlYXIgbWFwYQ0KbGVhZmxldCgpICU+JQ0KICBhZGRUaWxlcygpICU+JQ0KICBhZGRQb2x5Z29ucyhkYXRhID0gaXNvLA0KICAgICAgICAgICAgICBmaWxsQ29sb3IgPSB+cGFsKGlzb21heCksDQogICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICAgICAgd2VpZ2h0ID0gMSwNCiAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjQsDQogICAgICAgICAgICAgIHBvcHVwID0gfnBhc3RlKCJIYXN0YSIsIGlzb21heCwgIm1pbiIpKSAlPiUNCiAgYWRkQ2lyY2xlTWFya2VycyhkYXRhID0gbHVnYXJlc19jb25faXNvLA0KICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnBhbChpc29tYXgpLA0KICAgICAgICAgICAgICAgICAgIHN0cm9rZSA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAxLA0KICAgICAgICAgICAgICAgICAgIHJhZGl1cyA9IDYsDQogICAgICAgICAgICAgICAgICAgcG9wdXAgPSB+cGFzdGUwKCI8Yj4iLCBOb21icmUsICI8L2I+PGJyPlRpZW1wbyBhcHJveDogIiwgaXNvbWF4LCAiIG1pbiIpKSAlPiUNCiAgYWRkTWFya2VycyhsbmcgPSBjb29yZHNbMV0sIGxhdCA9IGNvb3Jkc1syXSwNCiAgICAgICAgICAgICBwb3B1cCA9ICJQdW50byBkZSBwYXJ0aWRhIikgJT4lDQogIGFkZExlZ2VuZCgiYm90dG9tcmlnaHQiLCBwYWwgPSBwYWwsIHZhbHVlcyA9IGlzbyRpc29tYXgsDQogICAgICAgICAgICB0aXRsZSA9ICJNaW51dG9zIGRlc2RlIGVsIG9yaWdlbiIsIG9wYWNpdHkgPSAwLjcpDQpgYGANCg0KDQojIyAqKlByZWRpY2Npw7NuIEluZHVzdHJpYSBIb3RlbGVyYSoqDQoNCiMjIyBBdXRvY29ycmVsYWNpw7NuDQpgYGB7cn0NCmFjZihkZXJyYW1hX3RzLCBtYWluPSJBdXRvY29ycmVsYXRpb24gLSBEZXJyYW1hIEVjb27Ds21pY2EiKQ0KcGFjZihkZXJyYW1hX3RzLCBtYWluPSJQYXJ0aWFsIEF1dG9jb3JyZWxhdGlvbiAtRGVycmFtYSBFY29uw7NtaWNhIikgI2FwbGljYXIgMSBvIDINCmFkZl9vY3VwYWNpb24gPC0gYWRmLnRlc3QoZGZfbG9nJCJEZXJyYW1hX0Vjb25vbWljYV9Fc3RfbWRwIikNCnByaW50KGFkZl9vY3VwYWNpb24pDQoNCiMgQXVjb3RvcnJlbGFjacOzbiBzZXJpYWwNCmxndW5qX2JveF9yZXN1bHRfaG90ZWwgPC0gQm94LnRlc3QoZGVycmFtYV90cywgbGFnID0gNSwgdHlwZSA9ICJManVuZy1Cb3giKQ0KbGd1bmpfYm94X3Jlc3VsdF9ob3RlbA0KYGBgDQoNCmBgYHtyfQ0KbnNkaWZmcyhkZXJyYW1hX3RzKQ0KYGBgDQoNCmBgYHtyfQ0KZGVycmFtYV9kaWZmIDwtIGRpZmYoZGVycmFtYV90cykgIyBEaWZlcmVuY2lhIChkID0gMSkNCmFkZi50ZXN0KGRlcnJhbWFfZGlmZikgI1lhIGVzIGVzdGFjaW9uYXJpYSwgcG9yIGVuZGUgbm8gc2UgYXBsaWNhIGQNCmBgYA0KDQojIyMgQVJJTUENCg0KYGBge3J9DQptb2QxIDwtIGFyaW1hKGRlcnJhbWFfZGlmZiwgb3JkZXIgPSBjKDEsIDAsIDApKSAgIyAgZCA9IDAgcG9ycXVlIHlhIGxhIHNlcmllIGVzdMOhIGRpZmVyZW5jaWFkYQ0Kc3VtbWFyeShtb2QxKSAjcC12YWx1ZSA9IDIuMmUtMTYg4oaSIG11eSBtZW5vciBhIDAuMDUsIGxvcyByZXNpZHVvcyBubyBzZSBjb21wb3J0YW4gY29tbyBydWlkbyBibGFuY28g4oaSIGVsIG1vZGVsbyBubyBlc3TDoSBjYXB0dXJhbmRvIGNvbXBsZXRhbWVudGUgbGEgZXN0cnVjdHVyYSBkZSBsYSBzZXJpZS4NCmNoZWNrcmVzaWR1YWxzKG1vZDEpDQojYXIxLT4gY29lZmljaWVudGUgYmFqbywgbG8gcXVlIHN1Z2llcmUgdW5hIGTDqWJpbCBhdXRvY29ycmVsYWNpw7NuIHRlbXBvcmFsIGRpcmVjdGEuDQpgYGANCg0KYGBge3J9DQptb2RlbG9fYXV0byA8LSBhdXRvLmFyaW1hKGRlcnJhbWFfdHMpDQpzdW1tYXJ5KG1vZGVsb19hdXRvKQ0KY2hlY2tyZXNpZHVhbHMobW9kZWxvX2F1dG8pDQojcC12YWx1ZSA9IDAuMDA0NDg0IOKGkiBtZW5vciBhIDAuMDUsIGxvIHF1ZSBpbmRpY2EgcHJlc2VuY2lhIGRlIGF1dG9jb3JyZWxhY2nDs24gZW4gbG9zIHJlc2lkdW9zDQojTm8gY2FwdHVyYSB0YW4gYmllbiBsYSBlc3RhY2lvbmFsaWRhZA0KYGBgDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGZvcmVjYXN0KQ0KDQojIExpc3RhIHBhcmEgZ3VhcmRhciByZXN1bHRhZG9zDQpyZXN1bHRhZG9zIDwtIGxpc3QoKQ0KbW9kZWxvc192YWxpZG9zIDwtIGRhdGEuZnJhbWUoKQ0KDQojIFJhbmdvcyBkZSBwYXLDoW1ldHJvcyANCmZvciAocCBpbiAwOjIpIHsNCiAgZm9yIChkIGluIDE6MSkgew0KICAgIGZvciAocSBpbiAwOjIpIHsNCiAgICAgIGZvciAoUCBpbiAwOjIpIHsNCiAgICAgICAgZm9yIChEIGluIDA6MSkgew0KICAgICAgICAgIGZvciAoUSBpbiAwOjIpIHsNCiAgICAgICAgICAgIG9yZGVuIDwtIGMocCwgZCwgcSkNCiAgICAgICAgICAgIG9yZGVuX2VzdGFjaW9uYWwgPC0gYyhQLCBELCBRKQ0KICAgICAgICAgICAgdHJ5KHsNCiAgICAgICAgICAgICAgbW9kZWxvIDwtIGFyaW1hKGRlcnJhbWFfdHMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlciA9IG9yZGVuLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Vhc29uYWwgPSBsaXN0KG9yZGVyID0gb3JkZW5fZXN0YWNpb25hbCwgcGVyaW9kID0gMTIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gIk1MIikNCiAgICAgICAgICAgICAgYWljX3ZhbCA8LSBBSUMobW9kZWxvKQ0KICAgICAgICAgICAgICBtb2RlbG9zX3ZhbGlkb3MgPC0gcmJpbmQobW9kZWxvc192YWxpZG9zLCBkYXRhLmZyYW1lKA0KICAgICAgICAgICAgICAgIHAsIGQsIHEsIFAsIEQsIFEsDQogICAgICAgICAgICAgICAgQUlDID0gYWljX3ZhbA0KICAgICAgICAgICAgICApKQ0KICAgICAgICAgICAgICBrZXkgPC0gcGFzdGUwKCIoIiwgcCwgIiwiLCBkLCAiLCIsIHEsICIpKCIsIFAsICIsIiwgRCwgIiwiLCBRLCAiKVsxMl0iKQ0KICAgICAgICAgICAgICByZXN1bHRhZG9zW1trZXldXSA8LSBtb2RlbG8NCiAgICAgICAgICAgIH0sIHNpbGVudCA9IFRSVUUpDQogICAgICAgICAgfQ0KICAgICAgICB9DQogICAgICB9DQogICAgfQ0KICB9DQp9DQoNCmBgYA0KDQpgYGB7cn0NCiMgT3JkZW5hciBwb3IgQUlDDQptb2RlbG9zX3ZhbGlkb3MgPC0gbW9kZWxvc192YWxpZG9zW29yZGVyKG1vZGVsb3NfdmFsaWRvcyRBSUMpLCBdDQpoZWFkKG1vZGVsb3NfdmFsaWRvcywgNSkNCmBgYA0KDQpgYGB7cn0NCm1vZF9zdWdlcmlkbyA8LSBhcmltYShkZXJyYW1hX3RzLCBvcmRlciA9IGMoMiwxLDIpLCBzZWFzb25hbCA9IGxpc3Qob3JkZXIgPSBjKDAsMSwxKSwgcGVyaW9kID0gMTIpLCB0cmFuc2Zvcm0ucGFycyA9IEZBTFNFKQ0Kc3VtbWFyeShtb2Rfc3VnZXJpZG8pDQpjaGVja3Jlc2lkdWFscyhtb2Rfc3VnZXJpZG8pDQoNCiNManVuZy1Cb3g6DQojcC12YWx1ZSA9IDAuMTY4NSA+IDAuMDUg4oaSIG5vIGhheSBhdXRvY29ycmVsYWNpw7NuIHNpZ25pZmljYXRpdmEgZW4gbG9zIHJlc2lkdW9zLg0KIyBCdWVuIGluZGljaW8gZGUgcXVlIGVsIG1vZGVsbyBjYXB0YSBiaWVuIGxhIGVzdHJ1Y3R1cmEgZGUgbGEgc2VyaWUNCiNDYXB0dXJhIGFkZWN1YWRhbWVudGUgbGEgdGVuZGVuY2lhIHkgZXN0YWNpb25hbGlkYWQuDQpgYGANCg0KYGBge3J9DQp2YXJpYWJsZXNfbG9nIDwtIGMoICJNRVMiLCAiQcORTyIsDQoiQ3VhcnRvc19EaXNwb25pYmxlcyIsICJDdWFydG9zX09jdXBhZG9zIiwgIkN1YXJ0b3NfUmVnaXN0cmFkb3MiLCAiRGVycmFtYV9FY29ub21pY2FfRXN0X21kcCIsICAiSW1wX0hvc3BlZGFqZSIsICJUdXJpc3Rhc19Ob2NoZV9FeHQiLCAiVHVyaXN0YXNfTm9jaGVfTmFjIiwgIiVPY3VwYWNpb25fSG90ZWxlcyIsICAiTGxlZ2FkYV9UdXJfRXh0IiwgIkxsZWdhZGFfVHVyX05hYyIsICJDdWFydG9zX0Rpc3BvbmlibGVzX1Byb21lZGlvX2xvZyIsICJEZW5zaWRhZF9PY3VwYWNpw7NuX2xvZyIsICAiRXN0YWRpYV9Qcm9tZWRpb19sb2ciICAgICAgICAgICANCikNCg0KdmFyaWFibGVzX2xvZyA8LSBzZXRkaWZmKHZhcmlhYmxlc19sb2csIGMoIkltcF9Ib3NwZWRhamUiLCAiTUVTIiwgIkHDkU8iKSkNCg0KZm9yICh2YXIgaW4gdmFyaWFibGVzX2xvZykgew0KICBjYXQoIlxuLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogIGNhdCgiQURGIHRlc3QgZm9yOiIsIHZhciwgIlxuIikNCiAgc2VyaWUgPC0gdHMoZGZfbG9nW1t2YXJdXSwgc3RhcnQgPSBjKDIwMDQsIDEpLCBmcmVxdWVuY3kgPSAxMikNCiAgcHJpbnQoYWRmLnRlc3Qoc2VyaWUpKQ0KfQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeSh0c2VyaWVzKQ0KDQpzZXJpZXNfZGlmZXJlbmNpYWRhcyA8LSBsaXN0KCkNCg0KZm9yICh2YXIgaW4gdmFyaWFibGVzX2xvZykgew0KICBzZXJpZSA8LSB0cyhkZl9sb2dbW3Zhcl1dLCBzdGFydCA9IGMoMjAwNCwgMSksIGZyZXF1ZW5jeSA9IDEyKQ0KICBhZGZfcmVzdWx0IDwtIHRyeUNhdGNoKHRzZXJpZXM6OmFkZi50ZXN0KHNlcmllKSwgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMKQ0KICANCiAgaWYgKCFpcy5udWxsKGFkZl9yZXN1bHQpICYmICFpcy5uYShhZGZfcmVzdWx0JHAudmFsdWUpICYmIGFkZl9yZXN1bHQkcC52YWx1ZSA+IDAuMDUpIHsNCiAgICAjIE5vIGVzIGVzdGFjaW9uYXJpYSDihpIgYXBsaWNhciBkaWZmIHkgcmVub21icmFyIGNvbiBfZGlmZg0KICAgIHNlcmllX2RpZmYgPC0gZGlmZihzZXJpZSkNCiAgICBzZXJpZXNfZGlmZXJlbmNpYWRhc1tbcGFzdGUwKHZhciwgIl9kaWZmIildXSA8LSBzZXJpZV9kaWZmDQogIH0gZWxzZSBpZiAoIWlzLm51bGwoYWRmX3Jlc3VsdCkpIHsNCiAgICAjIEVzIGVzdGFjaW9uYXJpYSDihpIgbWFudGVuZXIgbm9tYnJlIG9yaWdpbmFsIChyZWNvcnRhZGEgcG9yIGNvbnNpc3RlbmNpYSkNCiAgICBzZXJpZXNfZGlmZXJlbmNpYWRhc1tbdmFyXV0gPC0gd2luZG93KHNlcmllLCBzdGFydCA9IGMoMjAwNCwgMikpDQogIH0NCn0NCg0KYGBgDQoNCmBgYHtyfQ0KIyBBcGxpY2FyIGxhIHBydWViYSBBREYgYSB0b2RhcyBsYXMgc2VyaWVzIGRpZmVyZW5jaWFkYXMNCmZvciAobm9tYnJlIGluIG5hbWVzKHNlcmllc19kaWZlcmVuY2lhZGFzKSkgew0KICBjYXQoIlxuLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogIGNhdCgiQURGIHRlc3QgZm9yOiIsIG5vbWJyZSwgIlxuIikNCiAgcHJpbnQoYWRmLnRlc3Qoc2VyaWVzX2RpZmVyZW5jaWFkYXNbW25vbWJyZV1dKSkNCn0NCmBgYA0KDQoNCmBgYHtyfQ0KbGFiZWxzKHNlcmllc19kaWZlcmVuY2lhZGFzKQ0KZGZfbG9nWzIsIF0NCmBgYA0KDQoNCmBgYHtyfQ0KZGZfZGlmZiA8LSBhcy5kYXRhLmZyYW1lKHNlcmllc19kaWZlcmVuY2lhZGFzKQ0KZGZfZGlmZlsxLCBdDQoNCiMgMS4gTWFudGVuZXIgc29sbyBNRVMgeSBBw5FPIGRlIGRmX2xvZyAocmVjb3J0YWRvKQ0KZGZfbG9nMiA8LSBkZl9sb2dbLTEsIGMoIk1FUyIsICJBw5FPIiwgIkRlbnNpZGFkX09jdXBhY2nDs25fbG9nIildDQoNCiMgMi4gRWxpbWluYXIgZGUgZGZfZGlmZiBsYXMgY29sdW1uYXMgcXVlIHlhIGV4aXN0ZW4gZW4gZGZfbG9nMg0KY29sc19leGlzdGVudGVzIDwtIGludGVyc2VjdChuYW1lcyhkZl9sb2cyKSwgbmFtZXMoZGZfZGlmZikpDQpkZl9kaWZmX2ZpbHRyYWRvIDwtIGRmX2RpZmZbLCAhKG5hbWVzKGRmX2RpZmYpICVpbiUgY29sc19leGlzdGVudGVzKV0NCg0KIyAzLiBVbmlyIE1FUywgQcORTyB5IGxhcyBzZXJpZXMgZGlmZXJlbmNpYWRhcw0KZGZfZmluYWwgPC0gYmluZF9jb2xzKGRmX2xvZzIsIGRmX2RpZmZfZmlsdHJhZG8pDQoNCmRmX2ZpbmFsIDwtIGRmX2ZpbmFsW2RmX2ZpbmFsJEHDkU8gPj0gMjAxNSwgXQ0KYGBgDQoNCmBgYHtyfQ0KY29ycmVsYWNpb25lcyA8LSBjb3IoZGZfZmluYWwsIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQ0Kcm91bmQoY29ycmVsYWNpb25lcywgMikgICMgcGFyYSB2ZXIgbWVqb3IgbG9zIHZhbG9yZXMNCmBgYA0KDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGNhcmV0KQ0KDQojIERldGVjdGFyIGNvbHVtbmFzIGNvbiBhbHRhIGNvcnJlbGFjacOzbg0KY29yX21hdHJpeiA8LSBjb3IoZGZfZmluYWwsIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQ0KY29sdW1uYXNfYV9yZW1vdmVyIDwtIGZpbmRDb3JyZWxhdGlvbihjb3JfbWF0cml6LCBjdXRvZmYgPSAwLjkpDQoNCiMgVmVyIG5vbWJyZXMgZGUgY29sdW1uYXMgYWx0YW1lbnRlIGNvcnJlbGFjaW9uYWRhcw0KbmFtZXMoZGZfZmluYWwpW2NvbHVtbmFzX2FfcmVtb3Zlcl0NCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kc3VtKGlzLm5hKGRmX2ZpbmFsJERlcnJhbWFfRWNvbsOzbWljYV9Fc3RfbWRwX2RpZmYpKQ0Kd2hpY2goaXMubmEoZGZfZmluYWwkRGVycmFtYV9FY29uw7NtaWNhX0VzdF9tZHBfZGlmZikpDQpgYGANCg0KIyMjIE1vZGVsb3MgY29uIHZhcmlhYmxlcyBleHBsaWNhdGl2YXMNCg0KYGBge3J9DQp5IDwtIHRzKGRmX2ZpbmFsJERlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHBfZGlmZiwgc3RhcnQgPSBjKDIwMTUsIDEpLCBmcmVxdWVuY3kgPSAxMikNCmBgYA0KDQpgYGB7cn0NCnZhcmlhYmxlc19leHBsaWNhdGl2YXMgPC0gYygNCiAgIlguT2N1cGFjaW9uX0hvdGVsZXNfZGlmZiIsDQogICJDdWFydG9zX0Rpc3BvbmlibGVzX1Byb21lZGlvX2xvZ19kaWZmIiwgIkVzdGFkaWFfUHJvbWVkaW9fbG9nX2RpZmYiLA0KICAiQ3VhcnRvc19SZWdpc3RyYWRvc19kaWZmIiwgIkxsZWdhZGFfVHVyX05hY19kaWZmIikNCg0KIyJUdXJpc3Rhc19Ob2NoZV9FeHRfZGlmZiINCiNxdWl0YXIgcG9yICJDdWFydG9zX09jdXBhZG9zX2RpZmYiICAgICJUdXJpc3Rhc19Ob2NoZV9OYWNfZGlmZiIgICJDdWFydG9zX0Rpc3BvbmlibGVzX2RpZmYiLCAiRGVuc2lkYWRfT2N1cGFjacOzbl9sb2ciDQojIFNlIGVsaW1pbmEgIHBvciBiYWphIHNpZ25pZmljYW5jaWEgZXN0YWTDrXN0aWNhIGVuIGVsIG1vZGVsbyBTQVJJTUFYIHkgYWx0byBWSUYgKD4gNikNCg0KZGZfbW9kIDwtIGRwbHlyOjpzZWxlY3QoZGZfZmluYWwsIGFueV9vZih2YXJpYWJsZXNfZXhwbGljYXRpdmFzKSkNCg0KZXhwbGljYXRpdmFfbmFjaW9uYWw8LSgiTGxlZ2FkYV9UdXJfTmFjX2RpZmYiKQ0KZGZfbmFjaW9uYWwgPC0gZHBseXI6OnNlbGVjdChkZl9maW5hbCwgYW55X29mKGV4cGxpY2F0aXZhX25hY2lvbmFsKSkNCg0KZXhwbGljYXRpdmFfZXh0PC0oIkxsZWdhZGFfVHVyX0V4dF9kaWZmIikNCmRmX2V4dCA8LSBkcGx5cjo6c2VsZWN0KGRmX2ZpbmFsLCBhbnlfb2YoZXhwbGljYXRpdmFfZXh0KSkNCmBgYA0KDQpgYGB7cn0NCm5hbWVzKGRmX21vZCkNCm5hbWVzKGRmX25hY2lvbmFsKQ0KbmFtZXMoZGZfZXh0KQ0KYGBgDQoNCmBgYHtyfQ0KZGZfbW9kPC0gc2NhbGUoZGZfbW9kKQ0KZGZfbW9kPC1hcy5kYXRhLmZyYW1lKGRmX21vZCkNCmNvbG5hbWVzKGRmX21vZCkgPC0gdmFyaWFibGVzX2V4cGxpY2F0aXZhcw0KYGBgDQoNCkxvcyB2YWxvcmVzIGRlIFZJRiBzZSBlbmN1ZW50cmFuIHRvZG9zIHBvciBkZWJham8gZGVsIHVtYnJhbCBjcsOtdGljbyBkZSA1LCBsbyBxdWUgaW5kaWNhIGF1c2VuY2lhIGRlIG11bHRpY29saW5lYWxpZGFkIHNldmVyYSBlbnRyZSBsYXMgdmFyaWFibGVzIGV4cGxpY2F0aXZhcy4gUG9yIHRhbnRvLCBubyBzZSBjb25zaWRlcmEgbmVjZXNhcmlvIGVsaW1pbmFyIG5pbmd1bmEgdmFyaWFibGUgZGVsIG1vZGVsbyBwb3IgZXN0ZSBtb3Rpdm8uDQoNCmBgYHtyfQ0KbGlicmFyeShjYXIpDQoNCiMgQ2FsY3VsYXIgVklGIGNvcnJlY3RhbWVudGUNCm1vZGVsb192aWYgPC0gbG0ocmVwKDEsIG5yb3coZGZfbW9kKSkgfiAuLCBkYXRhID0gZGZfbW9kKQ0KdmlmKG1vZGVsb192aWYpDQpgYGANCg0KDQojIyMgU0FSSU1BWA0KDQpgYGB7cn0NCmRmX3hyZWcgPC0gYXMubWF0cml4KGRmX21vZCkNCnN0cihkZl94cmVnKSAgIyBkZWJlIHNlciBtYXRyaXoNCmlzLm1hdHJpeChkZl94cmVnKSAgIyBkZWJlIHNlciBUUlVFDQpucm93KGRmX3hyZWcpID09IGxlbmd0aCh5KQ0KYGBgDQoNCmBgYHtyfQ0KbW9kZWxvX2F1dG8gPC0gYXV0by5hcmltYSh5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICB4cmVnID0gZGZfeHJlZywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2Vhc29uYWwgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVwd2lzZSA9IEZBTFNFLCAgIyBtw6FzIHByZWNpc28gcGVybyBtw6FzIGxlbnRvDQogICAgICAgICAgICAgICAgICAgICAgICAgIGFwcHJveGltYXRpb24gPSBGQUxTRSkgICMgZXZpdGEgc2ltcGxpZmljYWNpb25lcw0KDQpzdW1tYXJ5KG1vZGVsb19hdXRvKQ0KY2hlY2tyZXNpZHVhbHMobW9kZWxvX2F1dG8pDQpgYGANCg0KYGBge3J9DQpkZl94cmVnIDwtIGFzLm1hdHJpeChkZl9tb2QpDQptb2RlbG9fc2FyaW1heDEgPC0gYXJpbWEoeSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyID0gYygyLCAwLCAyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHNlYXNvbmFsID0gbGlzdChvcmRlciA9IGMoMCwgMSwgMSksIHBlcmlvZCA9IDEyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHhyZWcgPSBkZl94cmVnKQ0KDQpzdW1tYXJ5KG1vZGVsb19zYXJpbWF4MSkNCmNoZWNrcmVzaWR1YWxzKG1vZGVsb19zYXJpbWF4MSkNCmBgYA0KDQpgYGB7cn0NCiMgRXh0cmFlciBsb3MgY29lZmljaWVudGVzIGRlbCBtb2RlbG8NCmNvZWZpY2llbnRlcyA8LSBjb2VmKG1vZGVsb19zYXJpbWF4MSkNCg0KIyBPcmRlbmFyIGxvcyBjb2VmaWNpZW50ZXMgZGUgbWF5b3IgYSBtZW5vciB2YWxvciBhYnNvbHV0bw0KY29lZl9vcmRlbmFkb3MgPC0gc29ydChjb2VmaWNpZW50ZXMsIGRlY3JlYXNpbmcgPSBUUlVFKQ0KcHJpbnQoY29lZl9vcmRlbmFkb3MpDQpgYGANCg0KYGBge3J9DQpjb2xuYW1lcyhtb2RlbG9fc2FyaW1heDEkeHJlZykgICMgZGViZSBkYXJtZSBsYXMgNyBjb2x1bW5hcw0KYGBgDQoNCg0KYGBge3J9DQojIE9yZGVuIHBvciB2YWxvciBhYnNvbHV0bw0KY29lZl9vcmRlbmFkb3NfYWJzIDwtIGNvZWZpY2llbnRlc1tvcmRlcihhYnMoY29lZmljaWVudGVzKSwgZGVjcmVhc2luZyA9IFRSVUUpXQ0KDQojIExpc3RhIG9yZGVuYWRhIHBvciB2YWxvciBhYnNvbHV0bw0KcHJpbnQoY29lZl9vcmRlbmFkb3NfYWJzKQ0KYGBgDQojIyMjIENhdXNhbGlkYWQNCg0KRGUgdG9kYXMgbGFzIHZhcmlhYmxlcyBldmFsdWFkYXMsIHNvbG8gKipDdWFydG9zX1JlZ2lzdHJhZG9zX2RpZmYqKiBtdWVzdHJhIGV2aWRlbmNpYSBlc3RhZMOtc3RpY2Egc2lnbmlmaWNhdGl2YSBkZSBzZXIgaW5mbHVpZGEgcG9yIGxhIGRlcnJhbWEgZWNvbsOzbWljYSwgZW4gZWwgc2VudGlkbyBkZSBsYSBjYXVzYWxpZGFkIGRlIEdyYW5nZXIuIEVzdG8gc3VnaWVyZSBxdWUgbGEgZGVycmFtYSBwdWVkZSBzZXIgdW4gYnVlbiBwcmVkaWN0b3IgZGUgbG9zIGNhbWJpb3MgZW4gbG9zIGN1YXJ0b3MgcmVnaXN0cmFkb3MsIHBlcm8gbm8gZGUgbGFzIG90cmFzIHZhcmlhYmxlcyB0dXLDrXN0aWNhcyBldmFsdWFkYXMuDQoNCmBgYHtyfQ0KbGlicmFyeShsbXRlc3QpDQoNCnlfcmV2IDwtIGRmX2ZpbmFsJERlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHBfZGlmZg0KbGFnX29wdCA8LSAyICAjICBtaXNtbyBuw7ptZXJvIGRlIHJlemFnb3MgcXVlIFZBUg0KDQpmb3IgKHZhciBpbiB2YXJpYWJsZXNfZXhwbGljYXRpdmFzKSB7DQogIHhfcmV2IDwtIGRmX2ZpbmFsW1t2YXJdXQ0KICANCiAgY2F0KCJcbi0tLSDCv0xhIGRlcnJhbWEgY2F1c2EgYSIsIHZhciwgIj8gLS0tXG4iKQ0KICByZXN1bHRhZG8gPC0gdHJ5Q2F0Y2goew0KICAgIGdyYW5nZXJ0ZXN0KHhfcmV2IH4geV9yZXYsIG9yZGVyID0gbGFnX29wdCkNCiAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSBlKQ0KICANCiAgcHJpbnQocmVzdWx0YWRvKQ0KfQ0KYGBgDQoNCmBgYHtyfQ0KZGZfbW9kX2xhZzEgPC0gZGZfbW9kDQoNCiMgc2UgYXBsaWNhIGxhZyBTT0xPIGEgbGFzIHZhcmlhYmxlcyBlbmTDs2dlbmFzIGRldGVjdGFkYXMNCnZhcnNfYV9yZXphZ2FyIDwtIGMoIkN1YXJ0b3NfUmVnaXN0cmFkb3NfZGlmZiIpDQpkZl9tb2RfbGFnMVt2YXJzX2FfcmV6YWdhcl0gPC0gZHBseXI6OmxhZyhkZl9tb2RbdmFyc19hX3JlemFnYXJdLCAxKQ0KDQojIHNlIGVsaW1pbmEgbGEgcHJpbWVyYSBmaWxhIGNvbiBOQSBnZW5lcmFkbyBwb3IgZWwgcmV6YWdvDQpkZl9tb2RfbGFnMSA8LSBkZl9tb2RfbGFnMVstMSwgXQ0KeV9sYWcxIDwtIHlbLTFdICAjIHNlIGFsaW5lYSBsYSBzZXJpZSBkZXBlbmRpZW50ZQ0KYGBgDQoNCmBgYHtyfQ0KbW9kZWxvX3NhcmltYXhfbGFnMSA8LSBhcmltYSh5X2xhZzEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyID0gYygyLCAwLCAyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Vhc29uYWwgPSBsaXN0KG9yZGVyID0gYygwLCAxLCAxKSwgcGVyaW9kID0gMTIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4cmVnID0gYXMubWF0cml4KGRmX21vZF9sYWcxKSkNCg0Kc3VtbWFyeShtb2RlbG9fc2FyaW1heF9sYWcxKQ0KY2hlY2tyZXNpZHVhbHMobW9kZWxvX3NhcmltYXhfbGFnMSkNCmBgYA0KDQpgYGB7cn0NCiMgQ29tcGFyYXIgQUlDLCBCSUMgeSBsb2dMaWsNCkFJQyhtb2RlbG9fc2FyaW1heDEpDQpBSUMobW9kZWxvX3NhcmltYXhfbGFnMSkNCg0KQklDKG1vZGVsb19zYXJpbWF4MSkNCkJJQyhtb2RlbG9fc2FyaW1heF9sYWcxKQ0KDQpsb2dMaWsobW9kZWxvX3NhcmltYXgxKQ0KbG9nTGlrKG1vZGVsb19zYXJpbWF4X2xhZzEpDQpgYGANCg0KDQojIyMgVkFSDQoNCmBgYHtyfQ0KdmFyX2RhdGEgPC0gY2JpbmQoDQogIHksICAjICB2YXJpYWJsZSBkZXBlbmRpZW50ZSBkaWZlcmVuY2lhZGEgLT5EZXJyYW1hX0Vjb25vbWljYV9Fc3RfbWRwX2RpZmYNCiAgZGZfbW9kICAjIHJlZ3Jlc29yZXMgeWEgZGlmZXJlbmNpYWRvcw0KKQ0KDQp2YXJfZGF0YV90cyA8LSB0cyh2YXJfZGF0YSwgc3RhcnQgPSBjKDIwMTUsMSksIGZyZXF1ZW5jeSA9IDEyKQ0KYGBgDQoNCmBgYHtyfQ0KbGFnX3NlbGVjdGlvbiA8LVZBUnNlbGVjdCh2YXJfZGF0YV90cywgbGFnLm1heCA9IDEyLCB0eXBlID0gImNvbnN0IikNCg0KcHJpbnQobGFnX3NlbGVjdGlvbiRzZWxlY3Rpb24pDQpvcHRpbWFsX2xhZyA8LSBsYWdfc2VsZWN0aW9uJHNlbGVjdGlvblsiQUlDKG4pIl0NCg0KY29sbmFtZXModmFyX2RhdGFfdHMpDQpgYGANCg0KYGBge3J9DQptb2RlbG9fdmFyIDwtIFZBUih2YXJfZGF0YV90cywgcCA9IDIgLCB0eXBlID0gImNvbnN0IikNCnN1bW1hcnkobW9kZWxvX3ZhcikNCmBgYA0KDQpgYGB7cn0NCnNlcmlhbC50ZXN0KG1vZGVsb192YXIsIGxhZ3MucHQgPSAxMiwgdHlwZSA9ICJQVC5hc3ltcHRvdGljIikNCmBgYA0KDQpgYGB7cn0NCnZhcl9kYXRhX25hYyA8LSB0cyhjYmluZCh5LCBkZl9uYWNpb25hbCksIHN0YXJ0ID0gc3RhcnQoeSksIGZyZXF1ZW5jeSA9IGZyZXF1ZW5jeSh5KSkNCmNvbG5hbWVzKHZhcl9kYXRhX25hYykgPC0gYygieSIsICJMbGVnYWRhX1R1cl9OYWNfZGlmZiIpDQoNCiMgMi4gU2VsZWNjaW9uYXIgbGFnIMOzcHRpbW8NCmxhZ19zZWxlY3Rpb25fbmFjIDwtIFZBUnNlbGVjdCh2YXJfZGF0YV9uYWMsIGxhZy5tYXggPSAxMiwgdHlwZSA9ICJjb25zdCIpDQpwcmludChsYWdfc2VsZWN0aW9uX25hYyRzZWxlY3Rpb24pDQpvcHRpbWFsX2xhZ19uYWMgPC0gbGFnX3NlbGVjdGlvbl9uYWMkc2VsZWN0aW9uWyJBSUMobikiXQ0KDQpjb2xuYW1lcyh2YXJfZGF0YV9uYWMpDQpgYGANCg0KYGBge3J9DQptb2RlbG9fdmFyX25hYyA8LSBWQVIodmFyX2RhdGFfbmFjLCBwID0gNCwgdHlwZSA9ICJjb25zdCIpDQpzdW1tYXJ5KG1vZGVsb192YXJfbmFjKQ0KYGBgDQoNCmBgYHtyfQ0Kc2VyaWFsLnRlc3QobW9kZWxvX3Zhcl9uYWMsIGxhZ3MucHQgPSAxMiwgdHlwZSA9ICJQVC5hc3ltcHRvdGljIikNCmBgYA0KDQpgYGB7cn0NCnZhcl9kYXRhX2V4dCA8LSB0cyhjYmluZCh5LCBkZl9leHQpLCBzdGFydCA9IHN0YXJ0KHkpLCBmcmVxdWVuY3kgPSBmcmVxdWVuY3koeSkpDQpjb2xuYW1lcyh2YXJfZGF0YV9leHQpIDwtIGMoInkiLCAiTGxlZ2FkYV9UdXJfRXh0X2RpZmYiKQ0KDQojMi4gU2VsZWNjaW9uYXIgbGFnIMOzcHRpbW8NCmxhZ19zZWxlY3Rpb25fZXh0IDwtIFZBUnNlbGVjdCh2YXJfZGF0YV9leHQsIGxhZy5tYXggPSAxMiwgdHlwZSA9ICJjb25zdCIpDQpwcmludChsYWdfc2VsZWN0aW9uX2V4dCRzZWxlY3Rpb24pDQpvcHRpbWFsX2xhZ19uYWMgPC0gbGFnX3NlbGVjdGlvbl9leHQkc2VsZWN0aW9uWyJBSUMobikiXQ0KDQpjb2xuYW1lcyh2YXJfZGF0YV9leHQpDQpgYGANCg0KYGBge3J9DQptb2RlbG9fdmFyX2V4dCA8LSBWQVIodmFyX2RhdGFfZXh0LCBwID0gMiwgdHlwZSA9ICJjb25zdCIpDQpzdW1tYXJ5KG1vZGVsb192YXJfZXh0KQ0KYGBgDQoNCmBgYHtyfQ0Kc2VyaWFsLnRlc3QobW9kZWxvX3Zhcl9leHQsIGxhZ3MucHQgPSAxMiwgdHlwZSA9ICJQVC5hc3ltcHRvdGljIikNCmBgYA0KDQpMYSB2YXJpYWJsZSBEZW5zaWRhZF9PY3VwYWNpw7NuX2xvZyBmdWUgZXhjbHVpZGEgZGVsIG1vZGVsbyBWQVIgZGViaWRvIGEgbGEgcHJlc2VuY2lhIGRlIGhldGVyb2NlZGFzdGljaWRhZCBlbiBsb3MgcmVzaWR1b3MgZGUgc3UgZWN1YWNpw7NuLCBpZGVudGlmaWNhZGEgbWVkaWFudGUgbGEgcHJ1ZWJhIEFSQ0ggKHAtdmFsdWUgPSAwLjA0NyA8IDAuMDUpLiBFc3RhIGNvbmRpY2nDs24gdmlvbGEgZWwgc3VwdWVzdG8gZGUgdmFyaWFuemEgY29uc3RhbnRlIHJlcXVlcmlkbyBwYXJhIGxhIHZhbGlkZXogZGUgbG9zIG1vZGVsb3MgVkFSLCBwb3IgbG8gcXVlIHNlIG9wdMOzIHBvciByZXRpcmFybGEgcGFyYSBnYXJhbnRpemFyIGxhIHJvYnVzdGV6IGRlbCBzaXN0ZW1hIHkgbGEgZmlhYmlsaWRhZCBkZSBsYXMgaW5mZXJlbmNpYXMuDQoNCmBgYHtyfQ0KbGlicmFyeShGaW5UUykNCg0KcmVzaWRfdmFyIDwtIHJlc2lkdWFscyhtb2RlbG9fdmFyKVssICJ5Il0gICMgY29sdW1uYSBkZSBsYSB2YXJpYWJsZSBkZSBpbnRlcsOpcw0KQXJjaFRlc3QocmVzaWRfdmFyLCBsYWdzID0gMTIpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KEZpblRTKQ0KDQpyZXNpZHVvc192YXIgPC0gcmVzaWR1YWxzKG1vZGVsb192YXIpDQoNCmZvciAoY29sIGluIGNvbG5hbWVzKHJlc2lkdW9zX3ZhcikpIHsNCiAgY2F0KCJcbi0tLSBBUkNIIHRlc3QgcGFyYToiLCBjb2wsICItLS1cbiIpDQogIHByaW50KEFyY2hUZXN0KHJlc2lkdW9zX3ZhclssIGNvbF0sIGxhZ3MgPSAxMikpDQp9DQpgYGANCg0KDQojIyMjIENhdXNhbGlkYWQgZGUgR3JhbmdlciANCg0KVG9kYXMgbGFzIHZhcmlhYmxlcyBzZWxlY2Npb25hZGFzIHRpZW5lbiB1biBwb2RlciBwcmVkaWN0aXZvIHNpZ25pZmljYXRpdm8gc29icmUgbGEgZGVycmFtYSBlY29uw7NtaWNhLCB2YWxpZGFuZG8gbGEgaW5jbHVzacOzbiBjb21vIHJlZ3Jlc29yZXMgZW4gbnVlc3Ryb3MgbW9kZWxvcyBjb21vIFNBUklNQVggbyBWQVIsIGV0YywgcG9yIGxvIHF1ZSBhcG9ydGFuIGluZm9ybWFjacOzbiDDunRpbCBwYXJhIHByZWRlY2lyIGVsIGNvbXBvcnRhbWllbnRvIGZ1dHVybyBkZSBsYSBEZXJyYW1hIEVjb27Ds21pY2EgcG9yIEhvdGVsZXJpYS4NCg0KYGBge3J9DQojIFBydWViYSBkZSBDYXVzYWxpZGFkIGRlIEdyYW5nZXINCmZvciAodmFyIGluIGNvbG5hbWVzKHZhcl9kYXRhX3RzKVstMV0pIHsNCiAgY2F0KCLCvyIsIHZhciwgIkdyYW5nZXItY2F1c2EgYSBEZXJyYW1hX0Vjb25vbWljYV9Fc3RfbWRwX2RpZmY/XG4iKQ0KICBwcmludChjYXVzYWxpdHkobW9kZWxvX3ZhciwgY2F1c2UgPSB2YXIpJEdyYW5nZXIpDQogIGNhdCgiXG4tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQp9DQoNCmxpYnJhcnkobG10ZXN0KQ0KZ3JhbmdlcnRlc3QoZGZfZmluYWwkRGVycmFtYV9FY29ub21pY2FfRXN0X21kcF9kaWZmIH4gZGZfZmluYWwkTGxlZ2FkYV9UdXJfTmFjX2RpZmYsIG9yZGVyID0gMikNCmBgYA0KDQpgYGB7cn0NCkFJQyhtb2RlbG9fc2FyaW1heDEpDQpCSUMobW9kZWxvX3NhcmltYXgxKQ0KbG9nTGlrKG1vZGVsb19zYXJpbWF4MSkNCg0KQUlDKG1vZGVsb19zYXJpbWF4X2xhZzEpDQpCSUMobW9kZWxvX3NhcmltYXhfbGFnMSkNCmxvZ0xpayhtb2RlbG9fc2FyaW1heF9sYWcxKQ0KDQpBSUMobW9kZWxvX3ZhcikNCkJJQyhtb2RlbG9fdmFyKQ0KbG9nTGlrKG1vZGVsb192YXIpDQoNCg0KQUlDKG1vZGVsb19hdXRvKQ0KQklDKG1vZGVsb19hdXRvKQ0KbG9nTGlrKG1vZGVsb19hdXRvKQ0KYGBgDQoNCmBgYHtyfQ0KZGZfeHJlZ19sYWcxIDwtIGFzLm1hdHJpeChkZl9tb2RfbGFnMSkgICMgdXNhIGRmX21vZF9sYWcxDQpzdG9waWZub3QoaXMubWF0cml4KGRmX3hyZWdfbGFnMSksIGlzLm51bWVyaWMoZGZfeHJlZ19sYWcxKSkNCg0Kbl9wZXJpb2RvcyA8LSAzNg0KZnV0dXJlX3hyZWcgPC0gbWF0cml4KHJlcChkZl94cmVnX2xhZzFbbnJvdyhkZl94cmVnX2xhZzEpLCBdLCBuX3BlcmlvZG9zKSwgDQogICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IG5fcGVyaW9kb3MsIGJ5cm93ID0gVFJVRSkNCg0KZ3Jvd3RoIDwtICgxLjAyKV4oMTpuX3BlcmlvZG9zKQ0KZnV0dXJlX3hyZWcgPC0gc3dlZXAoZnV0dXJlX3hyZWcsIDEsIGdyb3d0aCwgYCpgKQ0KDQpjb2xuYW1lcyhmdXR1cmVfeHJlZykgPC0gY29sbmFtZXMoZGZfeHJlZ19sYWcxKQ0KDQpzdG9waWZub3QoDQogIGlzLm1hdHJpeChmdXR1cmVfeHJlZyksDQogIGlzLm51bWVyaWMoZnV0dXJlX3hyZWcpLA0KICBpZGVudGljYWwoY29sbmFtZXMoZGZfeHJlZ19sYWcxKSwgY29sbmFtZXMoZnV0dXJlX3hyZWcpKSwNCiAgbmNvbChkZl94cmVnX2xhZzEpID09IG5jb2woZnV0dXJlX3hyZWcpKQ0KDQpgYGANCg0KYGBge3J9DQpzdHIobW9kZWxvX3NhcmltYXhfbGFnMSR4cmVnKQ0Kc3RyKGZ1dHVyZV94cmVnKQ0KaWRlbnRpY2FsKGNvbG5hbWVzKG1vZGVsb19zYXJpbWF4X2xhZzEkeHJlZyksIGNvbG5hbWVzKGZ1dHVyZV94cmVnKSkNCm5jb2wobW9kZWxvX3NhcmltYXhfbGFnMSR4cmVnKSA9PSBuY29sKGZ1dHVyZV94cmVnKQ0KYGBgDQoNCmBgYHtyfQ0KY29sbmFtZXMoZGZfeHJlZykNCmNvbG5hbWVzKGZ1dHVyZV94cmVnKQ0KY29sbmFtZXMobW9kZWxvX3NhcmltYXhfbGFnMSR4cmVnKQ0KYGBgDQoNCg0KIyMjIyBMbGVnYWRhIFR1cmlzdGFzIE5hY2lvbmFsZXMgJiBFeHRyYW5qZXJvcw0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShvcGVueGxzeCkNCmRmIDwtIHJlYWQueGxzeCgiSE9URUxFUy1CRC1GQ1NULnhsc3giLCBzaGVldCA9IkhPVEVMRVMiKQ0Ka2FibGUoaGVhZChkZikpDQpzdW0oaXMubmEoZGYpKQ0KZ2dfbWlzc192YXIoZGYpDQoNCmxpYnJhcnkoZHBseXIpDQpjb2xzX2xvZyA8LSBjKCAiQ3VhcnRvc19EaXNwb25pYmxlc19Qcm9tZWRpbyIsIA0KICAgICAgICAgICAgICAiRGVuc2lkYWRfT2N1cGFjacOzbiIsDQogICAgICAgICAgICAgICJFc3RhZGlhX1Byb21lZGlvIikNCg0KIyBDcmVhciBsYXMgY29sdW1uYXMgdHJhbnNmb3JtYWRhcyB5IGFncmVnYXJsYXMgYWwgZGF0YWZyYW1lIG9yaWdpbmFsDQpkZl9sb2cgPC0gZGYgJT4lDQogIG11dGF0ZShhY3Jvc3MoYWxsX29mKGNvbHNfbG9nKSwgfiBsb2coLiArIDEpLCAubmFtZXMgPSAiey5jb2x9X2xvZyIpKQ0KDQpkZl9sb2cgPC0gZGZfbG9nWywgIShuYW1lcyhkZl9sb2cpICVpbiUgY29sc19sb2cpXQ0KZGZfbnVtZXJpY28gPC0gZGZfbG9nW3NhcHBseShkZl9sb2csIGlzLm51bWVyaWMpXQ0KDQptYXRyaXpfY29yIDwtIGNvcihkZl9udW1lcmljbywgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpDQoNCmNvcnJwbG90KCBtYXRyaXpfY29yLA0KICBtZXRob2QgPSAiY29sb3IiLCAgICAgICAgICAgICMgTWFwYSBkZSBjYWxvcg0KICB0eXBlID0gInVwcGVyIiwgICAgICAgICAgICAgICMgU29sbyB0cmnDoW5ndWxvIHN1cGVyaW9yDQogIGFkZENvZWYuY29sID0gImJsYWNrIiwgICAgICAgIyBNb3N0cmFyIGNvZWZpY2llbnRlcyBlbiBjb2xvciBuZWdybw0KICBudW1iZXIuY2V4ID0gMC42LCAgICAgICAgICAgICMgVGFtYcOxbyBkZWwgbsO6bWVybw0KICB0bC5jZXggPSAwLjM1LCAgICAgICAgICAgICAgICAjIFRhbWHDsW8gZGUgbGFzIGV0aXF1ZXRhcw0KICB0bC5jb2wgPSAiYmxhY2siLCAgICAgICAgICAgICMgQ29sb3IgZGVsIHRleHRvDQogIGRpYWcgPSBGQUxTRSAgICAgICAgICAgICAgICAgIyBPY3VsdGEgbGEgZGlhZ29uYWwgKDFzKQ0KKQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkZl9sb2cgPC0gZGZfbG9nWyFpcy5uYShkZl9sb2ckIkRlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHAiKSwgXQ0KDQpkZXJyYW1hX3RzIDwtIHRzKGRmX2xvZyQiRGVycmFtYV9FY29ub21pY2FfRXN0X21kcCIsIHN0YXJ0ID0gYygyMDA0LCAxKSwgZW5kID0gYygyMDI0LCAxMiksIGZyZXF1ZW5jeSA9IDEyKQ0KcGxvdChkZXJyYW1hX3RzLCB0eXBlID0gImwiLCBjb2wgPSAiZGFya2dyZWVuIiwgbHdkID0gMiwNCiAgICAgeGxhYiA9ICJBw7FvIiwgeWxhYiA9ICJEZXJyYW1hIEVjb27Ds21pY2EiLA0KICAgICBtYWluID0gIlNlcmllIFRlbXBvcmFsIGRlIERlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHAiKQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkZWNvbXBvc2l0aW9uIDwtIGRlY29tcG9zZShkZXJyYW1hX3RzKQ0KcGxvdChkZWNvbXBvc2l0aW9uKQ0KDQpkZXNjb21wX3N0bCA8LSBzdGwoZGVycmFtYV90cywgcy53aW5kb3cgPSAicGVyaW9kaWMiKQ0KcGxvdChkZXNjb21wX3N0bCkNCmBgYA0KDQoqKk1lc2VzIGRlIOKAnHRlbXBvcmFkYSBhbHRh4oCdKioNCg0KKiBBYnJpbCAobWVzIDQpIHkgbm92aWVtYnJlIChtZXMgMTEpIG11ZXN0cmFuIGxhcyBtZWRpYW5hcyBtw6FzIGVsZXZhZGFzIChhbHJlZGVkb3IgZGUgNzUw4oCTODAwIG1kcCkgeSBjdWFydGlsZXMgc3VwZXJpb3JlcyBhbHRvcywgbG8gcXVlIHN1Z2llcmUgcGljb3Mgc2lzdGVtw6F0aWNvcyBlbiBwcmltYXZlcmEgeSBqdXN0byBhbnRlcyBkZSBmaW4gZGUgYcOxby4NCg0KKipNZXNlcyBkZSDigJx0ZW1wb3JhZGEgYmFqYeKAnSoqDQoNCiogRW5lcm8gKDEpIGVzIGVsIG3DoXMgYmFqbyBlbiBtZWRpYW5hLCBjb24gcG9jb3Mgb3V0bGllcnMsIHJlZmxlamFuZG8gZWwgdMOtcGljbyBiYWrDs24gdHJhcyBsYSB0ZW1wb3JhZGEgbmF2aWRlw7FhLg0KDQoqIFNlcHRpZW1icmUgKDkpIHkgZmVicmVybyAoMikgdGFtYmnDqW4gcHJlc2VudGFuIG1lZGlhbmFzIHJlZHVjaWRhcyAsIHBvc2libGVtZW50ZSBwb3IgdmFjYWNpb25lcyBjb3J0YXMgbyBwZXJpb2RvcyBkZSBiYWphIGFjdGl2aWRhZCB0dXLDrXN0aWNhLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KYm94cGxvdChkZXJyYW1hX3RzIH4gY3ljbGUoZGVycmFtYV90cyksDQogICAgICAgIHhsYWIgPSAiTWVzIiwgeWxhYiA9ICJEZXJyYW1hIiwNCiAgICAgICAgbWFpbiA9ICJFc3RhY2lvbmFsaWRhZCBtZW5zdWFsIiwgY29sID0gImxpZ2h0Ymx1ZSIpDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgQXV0b2NvcnJlbGFjacOzbiB5IGF1dG9jb3JyZWxhY2nDs24gcGFyY2lhbA0KYWNmKGRlcnJhbWFfdHMsIG1haW49IkF1dG9jb3JyZWxhdGlvbiAtIERlcnJhbWEgRWNvbsOzbWljYSIpDQpwYWNmKGRlcnJhbWFfdHMsIG1haW49IlBhcnRpYWwgQXV0b2NvcnJlbGF0aW9uIC1EZXJyYW1hIEVjb27Ds21pY2EiKSAjYXBsaWNhciAxIG8gMg0KYWRmX29jdXBhY2lvbiA8LSBhZGYudGVzdChkZl9sb2ckIkRlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHAiKQ0KcHJpbnQoYWRmX29jdXBhY2lvbikNCiMgQXVjb3RvcnJlbGFjacOzbiBzZXJpYWwNCmxndW5qX2JveF9yZXN1bHQgPC0gQm94LnRlc3QoZGVycmFtYV90cywgbGFnID0gNSwgdHlwZSA9ICJManVuZy1Cb3giKQ0KbGd1bmpfYm94X3Jlc3VsdA0KYGBgDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRlcnJhbWFfZGlmZiA8LSBkaWZmKGRlcnJhbWFfdHMpICMgRGlmZXJlbmNpYSAoZCA9IDEpDQphZGYudGVzdChkZXJyYW1hX2RpZmYpICNZYSBlcyBlc3RhY2lvbmFyaWEsIHBvciBlbmRlIG5vIHNlIGFwbGljYSBkDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm1vZDEgPC0gYXJpbWEoZGVycmFtYV9kaWZmLCBvcmRlciA9IGMoMSwgMCwgMCkpICAjICBkID0gMCBwb3JxdWUgeWEgbGEgc2VyaWUgZXN0w6EgZGlmZXJlbmNpYWRhDQpzdW1tYXJ5KG1vZDEpICNwLXZhbHVlID0gMi4yZS0xNiDihpIgbXV5IG1lbm9yIGEgMC4wNSwgbG9zIHJlc2lkdW9zIG5vIHNlIGNvbXBvcnRhbiBjb21vIHJ1aWRvIGJsYW5jbyDihpIgZWwgbW9kZWxvIG5vIGVzdMOhIGNhcHR1cmFuZG8gY29tcGxldGFtZW50ZSBsYSBlc3RydWN0dXJhIGRlIGxhIHNlcmllLg0KY2hlY2tyZXNpZHVhbHMobW9kMSkNCiNhcjEtPiBjb2VmaWNpZW50ZSBiYWpvLCBsbyBxdWUgc3VnaWVyZSB1bmEgZMOpYmlsIGF1dG9jb3JyZWxhY2nDs24gdGVtcG9yYWwgZGlyZWN0YS4NCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbW9kZWxvX2F1dG8gPC0gYXV0by5hcmltYShkZXJyYW1hX3RzKQ0Kc3VtbWFyeShtb2RlbG9fYXV0bykNCmNoZWNrcmVzaWR1YWxzKG1vZGVsb19hdXRvKQ0KI3AtdmFsdWUgPSAwLjAwNDQ4NCDihpIgbWVub3IgYSAwLjA1LCBsbyBxdWUgaW5kaWNhIHByZXNlbmNpYSBkZSBhdXRvY29ycmVsYWNpw7NuIGVuIGxvcyByZXNpZHVvcw0KI05vIGNhcHR1cmEgdGFuIGJpZW4gbGEgZXN0YWNpb25hbGlkYWQNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShmb3JlY2FzdCkNCg0KIyBMaXN0YSBwYXJhIGd1YXJkYXIgcmVzdWx0YWRvcw0KcmVzdWx0YWRvcyA8LSBsaXN0KCkNCm1vZGVsb3NfdmFsaWRvcyA8LSBkYXRhLmZyYW1lKCkNCg0KIyBSYW5nb3MgZGUgcGFyw6FtZXRyb3MgKGFqdXN0YXIgc2kgcXVlcmVtb3MgIG3DoXMgbyBtZW5vcyBjb21iaW5hY2lvbmVzKQ0KZm9yIChwIGluIDA6Mikgew0KICBmb3IgKGQgaW4gMToxKSB7DQogICAgZm9yIChxIGluIDA6Mikgew0KICAgICAgZm9yIChQIGluIDA6Mikgew0KICAgICAgICBmb3IgKEQgaW4gMDoxKSB7DQogICAgICAgICAgZm9yIChRIGluIDA6Mikgew0KICAgICAgICAgICAgb3JkZW4gPC0gYyhwLCBkLCBxKQ0KICAgICAgICAgICAgb3JkZW5fZXN0YWNpb25hbCA8LSBjKFAsIEQsIFEpDQogICAgICAgICAgICB0cnkoew0KICAgICAgICAgICAgICBtb2RlbG8gPC0gYXJpbWEoZGVycmFtYV90cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyID0gb3JkZW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWFzb25hbCA9IGxpc3Qob3JkZXIgPSBvcmRlbl9lc3RhY2lvbmFsLCBwZXJpb2QgPSAxMiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiTUwiKQ0KICAgICAgICAgICAgICBhaWNfdmFsIDwtIEFJQyhtb2RlbG8pDQogICAgICAgICAgICAgIG1vZGVsb3NfdmFsaWRvcyA8LSByYmluZChtb2RlbG9zX3ZhbGlkb3MsIGRhdGEuZnJhbWUoDQogICAgICAgICAgICAgICAgcCwgZCwgcSwgUCwgRCwgUSwNCiAgICAgICAgICAgICAgICBBSUMgPSBhaWNfdmFsDQogICAgICAgICAgICAgICkpDQogICAgICAgICAgICAgIGtleSA8LSBwYXN0ZTAoIigiLCBwLCAiLCIsIGQsICIsIiwgcSwgIikoIiwgUCwgIiwiLCBELCAiLCIsIFEsICIpWzEyXSIpDQogICAgICAgICAgICAgIHJlc3VsdGFkb3NbW2tleV1dIDwtIG1vZGVsbyB9LCBzaWxlbnQgPSBUUlVFKX19fX19fQ0KDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgT3JkZW5hciBwb3IgQUlDDQptb2RlbG9zX3ZhbGlkb3MgPC0gbW9kZWxvc192YWxpZG9zW29yZGVyKG1vZGVsb3NfdmFsaWRvcyRBSUMpLCBdDQpoZWFkKG1vZGVsb3NfdmFsaWRvcywgNSkNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbW9kX3N1Z2VyaWRvIDwtIGFyaW1hKGRlcnJhbWFfdHMsIG9yZGVyID0gYygyLDEsMiksIHNlYXNvbmFsID0gbGlzdChvcmRlciA9IGMoMCwxLDEpLCBwZXJpb2QgPSAxMiksIHRyYW5zZm9ybS5wYXJzID0gRkFMU0UpDQpzdW1tYXJ5KG1vZF9zdWdlcmlkbykNCmNoZWNrcmVzaWR1YWxzKG1vZF9zdWdlcmlkbykNCg0KI0xqdW5nLUJveDoNCiNwLXZhbHVlID0gMC4xNjg1ID4gMC4wNSDihpIgbm8gaGF5IGF1dG9jb3JyZWxhY2nDs24gc2lnbmlmaWNhdGl2YSBlbiBsb3MgcmVzaWR1b3MuDQojIEJ1ZW4gaW5kaWNpbyBkZSBxdWUgZWwgbW9kZWxvIGNhcHRhIGJpZW4gbGEgZXN0cnVjdHVyYSBkZSBsYSBzZXJpZQ0KI0NhcHR1cmEgYWRlY3VhZGFtZW50ZSBsYSB0ZW5kZW5jaWEgeSBlc3RhY2lvbmFsaWRhZC4NCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KdmFyaWFibGVzX2xvZyA8LSBjKCAiTUVTIiwgIkHDkU8iLA0KIkN1YXJ0b3NfRGlzcG9uaWJsZXMiLCAiQ3VhcnRvc19PY3VwYWRvcyIsICJDdWFydG9zX1JlZ2lzdHJhZG9zIiwgIkRlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHAiLCAgIkltcF9Ib3NwZWRhamUiLCAiVHVyaXN0YXNfTm9jaGVfRXh0IiwgIlR1cmlzdGFzX05vY2hlX05hYyIsICIlT2N1cGFjaW9uX0hvdGVsZXMiLCAgIkxsZWdhZGFfVHVyX0V4dCIsICJMbGVnYWRhX1R1cl9OYWMiLCAiQ3VhcnRvc19EaXNwb25pYmxlc19Qcm9tZWRpb19sb2ciLCAiRGVuc2lkYWRfT2N1cGFjacOzbl9sb2ciLCAgIkVzdGFkaWFfUHJvbWVkaW9fbG9nIiAgICAgICAgICANCikNCg0KdmFyaWFibGVzX2xvZyA8LSBzZXRkaWZmKHZhcmlhYmxlc19sb2csIGMoIkltcF9Ib3NwZWRhamUiLCAiTUVTIiwgIkHDkU8iKSkNCg0KDQpmb3IgKHZhciBpbiB2YXJpYWJsZXNfbG9nKSB7DQogIGNhdCgiXG4tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgY2F0KCJBREYgdGVzdCBmb3I6IiwgdmFyLCAiXG4iKQ0KICBzZXJpZSA8LSB0cyhkZl9sb2dbW3Zhcl1dLCBzdGFydCA9IGMoMjAwNCwgMSksIGZyZXF1ZW5jeSA9IDEyKQ0KICBwcmludChhZGYudGVzdChzZXJpZSkpDQp9DQoNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0c2VyaWVzKQ0KDQpzZXJpZXNfZGlmZXJlbmNpYWRhcyA8LSBsaXN0KCkNCg0KZm9yICh2YXIgaW4gdmFyaWFibGVzX2xvZykgew0KICBjYXQoIlByb2Nlc2FuZG8gdmFyaWFibGU6IiwgdmFyLCAiXG4iKQ0KICANCiAgc2VyaWUgPC0gdHMoZGZfbG9nW1t2YXJdXSwgc3RhcnQgPSBjKDIwMDQsIDEpLCBmcmVxdWVuY3kgPSAxMikNCiAgDQogIGFkZl9yZXN1bHQgPC0gdHJ5Q2F0Y2goew0KICAgIHRlc3QgPC0gdHNlcmllczo6YWRmLnRlc3Qoc2VyaWUpICAjIGFzZWfDunJhdGUgcXVlIGVzIGVsIGRlIHRzZXJpZXMNCiAgICB0ZXN0DQogIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgew0KICAgIGNhdCgiRXJyb3IgY29uIEFERiBkZSIsIHZhciwgIjoiLCBlJG1lc3NhZ2UsICJcbiIpDQogICAgcmV0dXJuKE5VTEwpDQogIH0pDQogIA0KICBpZiAoIWlzLm51bGwoYWRmX3Jlc3VsdCkgJiYgIWlzLm5hKGFkZl9yZXN1bHQkcC52YWx1ZSkpIHsNCiAgICBpZiAoYWRmX3Jlc3VsdCRwLnZhbHVlID4gMC4wNSkgew0KICAgICAgY2F0KCLihpIgTm8gZXN0YWNpb25hcmlhLiBTZSBkaWZlcmVuY2lhOiIsIHZhciwgIlxuIikNCiAgICAgIHNlcmllX2RpZmYgPC0gZGlmZihzZXJpZSkNCiAgICAgIHNlcmllc19kaWZlcmVuY2lhZGFzW1twYXN0ZTAodmFyLCAiX2RpZmYiKV1dIDwtIHNlcmllX2RpZmYNCiAgICB9IGVsc2Ugew0KICAgICAgY2F0KCLihpIgRXN0YWNpb25hcmlhLiBTZSBjb25zZXJ2YToiLCB2YXIsICJcbiIpDQogICAgICAjIFJlY29ydGFtb3MgbGEgc2VyaWUgYSBwYXJ0aXIgZGVsIHNlZ3VuZG8gbWVzIHBhcmEgdGVuZXIgbWlzbWEgbG9uZ2l0dWQgcXVlIGRpZmYoKQ0KICAgICAgc2VyaWVzX2RpZmVyZW5jaWFkYXNbW3Zhcl1dIDwtIHdpbmRvdyhzZXJpZSwgc3RhcnQgPSBjKDIwMDQsIDIpKQ0KICAgIH0NCiAgfSBlbHNlIHsNCiAgICBjYXQoIuKGkiBObyBzZSBwdWRvIGV2YWx1YXIgQURGIHBhcmEiLCB2YXIsICJcbiIpDQogIH0NCn0NCmBgYA0KDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIEFwbGljYXIgbGEgcHJ1ZWJhIEFERiBhIHRvZGFzIGxhcyBzZXJpZXMgZGlmZXJlbmNpYWRhcw0KZm9yIChub21icmUgaW4gbmFtZXMoc2VyaWVzX2RpZmVyZW5jaWFkYXMpKSB7DQogIGNhdCgiXG4tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgY2F0KCJBREYgdGVzdCBmb3I6Iiwgbm9tYnJlLCAiXG4iKQ0KICBwcmludChhZGYudGVzdChzZXJpZXNfZGlmZXJlbmNpYWRhc1tbbm9tYnJlXV0pKQ0KfQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkZl9sb2dbMiwgXQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkZl9kaWZmIDwtIGFzLmRhdGEuZnJhbWUoc2VyaWVzX2RpZmVyZW5jaWFkYXMpDQpkZl9kaWZmWzEsIF0NCmBgYA0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCiMgMS4gTWFudGVuZXIgc29sbyBNRVMgeSBBw5FPIGRlIGRmX2xvZyAocmVjb3J0YWRvKQ0KZGZfbG9nMiA8LSBkZl9sb2dbLTEsIGMoIk1FUyIsICJBw5FPIiwgIkRlbnNpZGFkX09jdXBhY2nDs25fbG9nIildDQoNCiMgMi4gRWxpbWluYXIgZGUgZGZfZGlmZiBsYXMgY29sdW1uYXMgcXVlIHlhIGV4aXN0ZW4gZW4gZGZfbG9nMg0KY29sc19leGlzdGVudGVzIDwtIGludGVyc2VjdChuYW1lcyhkZl9sb2cyKSwgbmFtZXMoZGZfZGlmZikpDQpkZl9kaWZmX2ZpbHRyYWRvIDwtIGRmX2RpZmZbLCAhKG5hbWVzKGRmX2RpZmYpICVpbiUgY29sc19leGlzdGVudGVzKV0NCg0KIyAzLiBVbmlyIE1FUywgQcORTyB5IGxhcyBzZXJpZXMgZGlmZXJlbmNpYWRhcw0KZGZfZmluYWwgPC0gYmluZF9jb2xzKGRmX2xvZzIsIGRmX2RpZmZfZmlsdHJhZG8pDQoNCmRmX2ZpbmFsIDwtIGRmX2ZpbmFsW2RmX2ZpbmFsJEHDkU8gPj0gMjAxNSwgXQ0KYGBgDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmNvcnJlbGFjaW9uZXMgPC0gY29yKGRmX2ZpbmFsLCB1c2UgPSAiY29tcGxldGUub2JzIikNCnJvdW5kKGNvcnJlbGFjaW9uZXMsIDIpICAjIHBhcmEgdmVyIG1lam9yIGxvcyB2YWxvcmVzDQpjb3JycGxvdChjb3JyZWxhY2lvbmVzLCBtZXRob2QgPSAiY29sb3IiLCB0eXBlID0gInVwcGVyIiwgdGwuY2V4ID0gMC43KQ0KYGBgDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoY2FyZXQpDQoNCiMgRGV0ZWN0YXIgY29sdW1uYXMgY29uIGFsdGEgY29ycmVsYWNpw7NuDQpjb3JfbWF0cml6IDwtIGNvcihkZl9maW5hbCwgdXNlID0gImNvbXBsZXRlLm9icyIpDQpjb2x1bW5hc19hX3JlbW92ZXIgPC0gZmluZENvcnJlbGF0aW9uKGNvcl9tYXRyaXosIGN1dG9mZiA9IDAuOSkNCg0KIyBWZXIgbm9tYnJlcyBkZSBjb2x1bW5hcyBhbHRhbWVudGUgY29ycmVsYWNpb25hZGFzDQpuYW1lcyhkZl9maW5hbClbY29sdW1uYXNfYV9yZW1vdmVyXQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpzdW0oaXMubmEoZGZfZmluYWwkRGVycmFtYV9FY29uw7NtaWNhX0VzdF9tZHBfZGlmZikpDQp3aGljaChpcy5uYShkZl9maW5hbCREZXJyYW1hX0Vjb27Ds21pY2FfRXN0X21kcF9kaWZmKSkNCmBgYA0KDQojIyMjIFByb27Ds3N0aWNvIExsZWdhZGEgVHVyaXN0YXMgTmFjaW9uYWxlcw0KDQpVbiBpbmNyZW1lbnRvIHNvc3RlbmlkbyBkZWwgNTAgJSBlbiBsYXMgbGxlZ2FkYXMgbmFjaW9uYWxlcyBhIHBhcnRpciBkZSBqdW5pby0yMDI2IGHDsWFkaXLDrWEsIGVuIHByb21lZGlvLCArNDYgbWRwIG1lbnN1YWxlcyBhIGxhIGRlcnJhbWEgaG90ZWxlcmEgZHVyYW50ZSBlbCBzaWd1aWVudGUgYcOxbywgcmVkdWNpZW5kbyBhIGxhIG1pdGFkIGxhcyBjYcOtZGFzIGVzdGFjaW9uYWxlcyBkZSBkaWNpZW1icmUgeSBhYnJpbC4gRWxldmFyIGVsIGVzY2VuYXJpbyBhbCA3NSAlIGFwb3J0YXLDrWEgKzE1IG1kcCBleHRyYSwgcGVybyBjb24gcmVuZGltaWVudG9zIGRlY3JlY2llbnRlcy4gTG9zIG1lc2VzIGRlIG9jdHVicmUgeSBlbmVybyBzb24gbG9zIHB1bnRvcyBkZSBtYXlvciBzZW5zaWJpbGlkYWQ6IGNvbmNlbnRyYXIgcHJvbW9jaW9uZXMgZW4gZGljaG9zIG1vbWVudG9zIG1heGltaXphcsOtYSBlbCByZXRvcm5vIHBvciBjYWRhIHBlc28gaW52ZXJ0aWRvIGVuIGNhcHRhciB0dXJpc3RhcyBuYWNpb25hbGVzLiBFbCBjb2VmaWNpZW50ZSBWQVIgZGUgTGxlZ2FkYV9UdXJfTmFjX2RpZmYgZXMgcG9zaXRpdm8sIHBvciBlbmRlLCBzZSBjYWxjdWxhIHF1ZToNCg0KKiBVbiBhbHphIGRlbCAyNSAlIHPDs2xvIGFqdXN0YSBsYSBkZXJyYW1hIG1lbnN1YWwgwrEzIGEgMTUgbWRwIHNlZ8O6biBlbCBzaWdubyBkZSBsYSBzZXJpZSAoYWxpdmlhbmEgcMOpcmRpZGFzLCBwZXJvIHJlY29ydGEgbGV2ZXMgZ2FuYW5jaWFzKS4NCg0KKiBJbXBhY3RvIHByb3BvcmNpb25hbDogSnVuaW8tMjY6IHVuICsyNSAlIC8gKzUwICUgLyArNzUgJSBlbiBsbGVnYWRhcyBuYWNpb25hbGVzIG1lam9yYSBsYSB2YXJpYWNpw7NuIGRlIGRlcnJhbWEgZGUgLTM1IOKGkiAtMjEgLyAtMTggLyAtMTUgbWRwDQoNCiogRW4gbWVzZXMgY29uIGRlcnJhbWEgcG9zaXRpdmEgKHAuZWouIEp1bC0yNiA9IDExOCBtZHApLCBlbCBhdW1lbnRvIGRlIHR1cmlzdGFzIHJlc3RhIH4zLTQgbWRwIGFsIG5pdmVsIHJlZ2lzdHJhZG8uDQoNCiogSW5jcmVtZW50YXIgMjUtNzUgJSBsYXMgbGxlZ2FkYXMgbmFjaW9uYWxlcyBubyBnZW5lcmEgc2FsdG9zIHNpZ25pZmljYXRpdm9zOiBlbCByYW5nbyBjb21wbGV0byBkZSBpbXBhY3RvIGVzdMOhIGRlbnRybyBkZSDCsTE3IG1kcCBtZW5zdWFsZXMuDQoNCiogRGljLTI2IHkgQWJyLTI3IHNvbiBsb3MgcGVvcmVzIG1lc2VzIGVuIGVsIGVzY2VuYXJpbyByZWFsICgtMTM2IHkgLTQzIG1kcCkuIENvbiArNTAgJSBkZSB0dXJpc3RhcyBsYXMgY2HDrWRhcyBzZSByZWNvcnRhbiAxOSAlIHkgMjcgJSByZXNwZWN0aXZhbWVudGUuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp0dXJpc3Rhc19uYWNpb25hbGVzX3RzIDwtIHRzKGRmX2ZpbmFsJExsZWdhZGFfVHVyX05hY19kaWZmLCBzdGFydCA9IGMoMjAxNSwgMSksIGZyZXF1ZW5jeSA9IDEyKQ0Kc2Vhc29ucGxvdCh0dXJpc3Rhc19uYWNpb25hbGVzX3RzLCB5ZWFyLmxhYmVscyA9IFRSVUUsIGNvbCA9IDE6MjAsIG1haW4gPSAiRXN0YWNpb25hbGlkYWQgcG9yIG1lcyIpDQoNCnRzX2RlY29tcCA8LSBkZWNvbXBvc2UodHVyaXN0YXNfbmFjaW9uYWxlc190cykNCnBsb3QodHNfZGVjb21wKQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIEF1dG9jb3JyZWxhY2nDs24geSBhdXRvY29ycmVsYWNpw7NuIHBhcmNpYWwNCmFjZih0dXJpc3Rhc19uYWNpb25hbGVzX3RzLCBtYWluPSJBdXRvY29ycmVsYXRpb24gLSBMbGVnYWRhIFR1cmlzdGFzIE5hY2lvbmFsZXMiKQ0KcGFjZih0dXJpc3Rhc19uYWNpb25hbGVzX3RzLCBtYWluPSJQYXJ0aWFsIEF1dG9jb3JyZWxhdGlvbiAtTGxlZ2FkYSBUdXJpc3RhcyBOYWNpb25hbGVzIikgIw0KDQoNCiMJUHJ1ZWJhIGRlIEVzdGFjaW9uYXJlaWRhZCAtIEF1Z21lbnRlZCBEaWNrZXktRnVsbGVyIFRlc3QgDQphZGZfdHVyIDwtIGFkZi50ZXN0KHR1cmlzdGFzX25hY2lvbmFsZXNfdHMpDQpwcmludChhZGZfdHVyKQ0KDQojSGlww7N0ZXNpcyBudWxhIChI4oKAKTogbGEgc2VyaWUgbm8gZXMgZXN0YWNpb25hcmlhDQojSGlww7N0ZXNpcyBhbHRlcm5hdGl2YSAoSOKCgSk6IGxhIHNlcmllIGVzIGVzdGFjaW9uYXJpYQ0KI0NvbiB1biBwLXZhbHVlIGRlIDAuMDEsIHJlY2hhemFtb3MgSOKCgCDihpIgbGEgc2VyaWUgZXMgZXN0YWNpb25hcmlhLg0KDQojIEF1Y290b3JyZWxhY2nDs24gc2VyaWFsIC0gQm94LUxqdW5nIHRlc3QNCmxndW5qX2JveF9uYWMgPC0gQm94LnRlc3QodHVyaXN0YXNfbmFjaW9uYWxlc190cywgbGFnID0gNSwgdHlwZSA9ICJManVuZy1Cb3giKQ0KbGd1bmpfYm94X25hYw0KDQojSGlww7N0ZXNpcyBudWxhIChI4oKAKTogTk8gaGF5IGF1dG9jb3JyZWxhY2nDs24gZW4gbG9zIHJlc2lkdW9zDQojSGlww7N0ZXNpcyBhbHRlcm5hdGl2YSAoSOKCgSk6IFNJIEhheSBhdXRvY29ycmVsYWNpw7NuIGVuIGxvcyByZXNpZHVvcw0KI0NvbW8gZWwgcC12YWx1ZSBlcyAwLjA3MiAoPiAwLjA1KSwgbm8gc2UgcmVjaGF6YSBI4oKAIOKGkiBubyBoYXkgZXZpZGVuY2lhIGZ1ZXJ0ZSBkZSBhdXRvY29ycmVsYWNpw7NuIGVuIGxvcyByZXNpZHVvcy4NCg0KZGlmZl90dXI8LSBuc2RpZmZzKHR1cmlzdGFzX25hY2lvbmFsZXNfdHMpDQpkaWZmX3R1cg0KYGBgDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmFyaW1hMV9uYWNpb25hbGVzIDwtIGFyaW1hKHR1cmlzdGFzX25hY2lvbmFsZXNfdHMsIG9yZGVyID0gYygxLCAxLCAwKSkgICMNCnN1bW1hcnkoYXJpbWExX25hY2lvbmFsZXMpIA0KY2hlY2tyZXNpZHVhbHMoYXJpbWExX25hY2lvbmFsZXMpDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmFyaW1hMl9uYWNpb25hbGVzIDwtIGFyaW1hKHR1cmlzdGFzX25hY2lvbmFsZXNfdHMsIG9yZGVyID0gYygwLCAxLCAxKSkgICMNCnN1bW1hcnkoYXJpbWEyX25hY2lvbmFsZXMpIA0KY2hlY2tyZXNpZHVhbHMoYXJpbWEyX25hY2lvbmFsZXMpDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmFyaW1hM19uYWNpb25hbGVzIDwtIGFyaW1hKHR1cmlzdGFzX25hY2lvbmFsZXNfdHMsIG9yZGVyID0gYygxLCAxLCAxKSkgICMNCnN1bW1hcnkoYXJpbWEzX25hY2lvbmFsZXMpIA0KY2hlY2tyZXNpZHVhbHMoYXJpbWEzX25hY2lvbmFsZXMpDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmF1dG9fbmFjaW9uYWxlcyA8LSBhdXRvLmFyaW1hKHR1cmlzdGFzX25hY2lvbmFsZXNfdHMpDQpzdW1tYXJ5KGF1dG9fbmFjaW9uYWxlcykNCmNoZWNrcmVzaWR1YWxzKGF1dG9fbmFjaW9uYWxlcykNCmBgYA0KDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpmb3JlY2FzdF9uYWNpb25hbGVzIDwtIGZvcmVjYXN0Ojpmb3JlY2FzdChhdXRvX25hY2lvbmFsZXMsIGggPSAzMCkNCg0KcGxvdChmb3JlY2FzdF9uYWNpb25hbGVzLA0KICAgICBtYWluID0gIlByb27Ds3N0aWNvIExsZWdhZGEgZGUgVHVyaXN0YXMgTmFjIHBhcmEgZWwgTXVuZGlhbCAyMDI2IiwNCiAgICAgeWxhYiA9ICJMbGVnYWRhcyBkZSBUdXJpc3RhcyBOYWNpb25hbGVzIiwNCiAgICAgeGxhYiA9ICJBw7FvIikNCg0KIyBFeHRyYWVyIGZlY2hhcyBhIHBhcnRpciBkZWwgw7psdGltbyBkYXRvDQpmZWNfZm9yZWNhc3QgPC0gZnVuY3Rpb24odHNfb2JqLCBoKXsNCiAgdWx0IDwtIGVuZCh0c19vYmopICAgICAgICAgICAgICAgICMgYyhhw7FvLCBtZXMpIGRlbCDDumx0aW1vIGRhdG8NCiAgbV9pbmkgPC0gaWZlbHNlKHVsdFsyXSA9PSAxMiwgMSwgIHVsdFsyXSArIDEpDQogIGFfaW5pIDwtIGlmZWxzZSh1bHRbMl0gPT0gMTIsIHVsdFsxXSArIDEsIHVsdFsxXSkNCiAgc2VxKGFzLkRhdGUoc3ByaW50ZigiJWQtJTAyZC0wMSIsIGFfaW5pLCBtX2luaSkpLA0KICAgICAgYnkgPSAiMSBtb250aCIsIGxlbmd0aC5vdXQgPSBoKQ0KfQ0KDQpoPC0zMA0KZmVjaGFzX25hYzwtIGZlY19mb3JlY2FzdCh0dXJpc3Rhc19uYWNpb25hbGVzX3RzLGgpDQoNCiMgQ3JlYWNpw7NuIGRlbCBkYXRhIGZyYW1lIGNvbiByZXN1bHRhZG9zDQpkZl9mb3JlY2FzdCA8LSBkYXRhLmZyYW1lKA0KICBGZWNoYSA9IGZlY2hhc19uYWMsDQogIFByb25vc3RpY28gPSBhcy5udW1lcmljKGZvcmVjYXN0X25hY2lvbmFsZXMkbWVhbiksDQogIExvd2VyODAgPSBhcy5udW1lcmljKGZvcmVjYXN0X25hY2lvbmFsZXMkbG93ZXJbLDFdKSwNCiAgVXBwZXI4MCA9IGFzLm51bWVyaWMoZm9yZWNhc3RfbmFjaW9uYWxlcyR1cHBlclssMV0pLA0KICBMb3dlcjk1ID0gYXMubnVtZXJpYyhmb3JlY2FzdF9uYWNpb25hbGVzJGxvd2VyWywyXSksDQogIFVwcGVyOTUgPSBhcy5udW1lcmljKGZvcmVjYXN0X25hY2lvbmFsZXMkdXBwZXJbLDJdKQ0KKQ0KDQoNCmdncGxvdChkZl9mb3JlY2FzdCwgYWVzKHggPSBGZWNoYSwgeSA9IFByb25vc3RpY28pKSArDQogIGdlb21fcmliYm9uKGFlcyh5bWluID0gTG93ZXI5NSwgeW1heCA9IFVwcGVyOTUpLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGFscGhhID0gMC40KSArDQogIGdlb21fcmliYm9uKGFlcyh5bWluID0gTG93ZXI4MCwgeW1heCA9IFVwcGVyODApLCBmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNCkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAiZGFya2JsdWUiLCBzaXplID0gMSkgKw0KICBsYWJzKHRpdGxlID0gIlByb27Ds3N0aWNvIExsZWdhZGEgVHVyaXN0YXMgTmFjaW9uYWxlcyBoYXN0YSBNYXlvIDIwMjYiLA0KICAgICAgIHggPSAiRmVjaGEiLCB5ID0gIkxsZWdhZGFzIikgKw0KICBzY2FsZV94X2RhdGUoZGF0ZV9sYWJlbHMgPSAiJWItJVkiLCBkYXRlX2JyZWFrcyA9ICIyIG1vbnRocyIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQojIyMjIFByb27Ds3N0aWNvIExsZWdhZGEgVHVyaXN0YXMgRXh0cmFuamVyb3MNCg0KRWwgY29lZmljaWVudGUgZGUgTGxlZ2FkYV9UdXJfRXh0X2RpZmYgZW4gZWwgVkFSIGVzIG5lZ2F0aXZvOyBwb3IgZXNvLCBhbCBzaW11bGFyICsyNSAlIC8gKzUwICUgLyArNzUgJSBsYXMgY3VydmFzIGRlIGNvbG9yIChyb2pvLCB2ZXJkZSwgYXp1bCkgc2UgbXVldmVuIGxpZ2VyYW1lbnRlIHBvciBkZWJham8gZGUgbGEgbMOtbmVhIG1vcmFkYSAoUmVhbCkgZW4gY2FzaSB0b2RvcyBsb3MgbWVzZXM6DQoNCiogRGljLTI2IGVzIGVsIGVzY2VuYXJpbyBtw6FzIHByb2Z1bmRvICgtMTU0IG1kcCByZWFsKS4gRWwgYXVtZW50byBkZWwgNzUgJSBkZSB0dXJpc3RhcyBleHQuIGVtcGVvcmEgbGEgY2HDrWRhIGEgLTE1OSBtZHA6IHVuIGFqdXN0ZSAtNSBtZHAgYWRpY2lvbmFsLg0KDQoqIEFnby0yNiB5IEFici0yNyBtdWVzdHJhbiBwZXF1ZcOxb3MgZGV0ZXJpb3JvcyAoLTQgYSAtNiBtZHApIGFsIGNvbXBhcmFyIFJlYWwgdnMgKzc1ICUuDQoNClBhcmEgbGEgZGVycmFtYSBob3RlbGVyYSwgdW4gc2hvY2sgZGUgdHVyaXN0YXMgZXh0cmFuamVyb3Mgbm8gZXMgcmVudGFibGUgZW4gZWwgY29ydG8gcGxhem8gYmFqbyBsYSBlc3RydWN0dXJhIGFjdHVhbDoNCg0KKiBMYSBlbGFzdGljaWRhZCBlc3RpbWFkYSBlcyBuZWdhdGl2YSB5IHBlcXVlw7FhLg0KDQoqIEluY3JlbWVudGFyIGZsdWpvcyBmb3LDoW5lb3MgaGFzdGEgKzc1ICUgYXBlbmFzIG1vZGlmaWNhIGxhIGRlcnJhbWEgKGVudHJlIDAgeSAtNiBtZHAgcG9yIG1lcykuDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnR1cmlzdGFzX2V4dF90cyA8LSB0cyhkZl9maW5hbCRMbGVnYWRhX1R1cl9FeHRfZGlmZiwgc3RhcnQgPSBjKDIwMTUsIDEpLCBmcmVxdWVuY3kgPSAxMikNCg0Kc2Vhc29ucGxvdCh0dXJpc3Rhc19leHRfdHMsIHllYXIubGFiZWxzID0gVFJVRSwgY29sID0gMToyMCwgbWFpbiA9ICJFc3RhY2lvbmFsaWRhZCBwb3IgbWVzIikNCg0KdHNfZGVjb21wX2V4dCA8LSBkZWNvbXBvc2UodHVyaXN0YXNfZXh0X3RzKQ0KcGxvdCh0c19kZWNvbXBfZXh0KQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIEF1dG9jb3JyZWxhY2nDs24geSBhdXRvY29ycmVsYWNpw7NuIHBhcmNpYWwNCmFjZih0dXJpc3Rhc19leHRfdHMsIG1haW49IkF1dG9jb3JyZWxhdGlvbiAtIExsZWdhZGEgVHVyaXN0YXMgRXh0cmFuamVyb3MiKQ0KcGFjZih0dXJpc3Rhc19leHRfdHMsIG1haW49IlBhcnRpYWwgQXV0b2NvcnJlbGF0aW9uIC1MbGVnYWRhIFR1cmlzdGFzIEV4dHJhbmplcm8iKSAjDQoNCg0KIwlQcnVlYmEgZGUgRXN0YWNpb25hcmVpZGFkIC0gQXVnbWVudGVkIERpY2tleS1GdWxsZXIgVGVzdCANCmFkZl90dXJfZXh0IDwtIGFkZi50ZXN0KHR1cmlzdGFzX2V4dF90cykNCnByaW50KGFkZl90dXJfZXh0KQ0KDQojSGlww7N0ZXNpcyBudWxhIChI4oKAKTogbGEgc2VyaWUgbm8gZXMgZXN0YWNpb25hcmlhDQojSGlww7N0ZXNpcyBhbHRlcm5hdGl2YSAoSOKCgSk6IGxhIHNlcmllIGVzIGVzdGFjaW9uYXJpYQ0KI0NvbiB1biBwLXZhbHVlIGRlIDAuMDEsIHJlY2hhemFtb3MgSOKCgCDihpIgbGEgc2VyaWUgZXMgZXN0YWNpb25hcmlhLg0KDQojIEF1Y290b3JyZWxhY2nDs24gc2VyaWFsIC0gQm94LUxqdW5nIHRlc3QNCmxndW5qX2JveF9leHQgPC0gQm94LnRlc3QodHVyaXN0YXNfZXh0X3RzLCBsYWcgPSA1LCB0eXBlID0gIkxqdW5nLUJveCIpDQpsZ3Vual9ib3hfZXh0DQoNCiNIaXDDs3Rlc2lzIG51bGEgKEjigoApOiBOTyBoYXkgYXV0b2NvcnJlbGFjacOzbiBlbiBsb3MgcmVzaWR1b3MNCiNIaXDDs3Rlc2lzIGFsdGVybmF0aXZhIChI4oKBKTogU0kgSGF5IGF1dG9jb3JyZWxhY2nDs24gZW4gbG9zIHJlc2lkdW9zDQojQ29tbyBlbCBwLXZhbHVlIGVzIDAuMTE4NCAoPiAwLjA1KSwgbm8gc2UgcmVjaGF6YSBI4oKAIOKGkiBubyBoYXkgZXZpZGVuY2lhIGZ1ZXJ0ZSBkZSBhdXRvY29ycmVsYWNpw7NuIGVuIGxvcyByZXNpZHVvcy4NCg0KZGlmZl90dXJfZXh0PC0gbnNkaWZmcyh0dXJpc3Rhc19leHRfdHMpDQpkaWZmX3R1cl9leHQNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KYXV0b19leHRyYW5qZXJvcyA8LSBhdXRvLmFyaW1hKHR1cmlzdGFzX2V4dF90cyxzZWFzb25hbCA9IFRSVUUsIGFsbG93ZHJpZnQgPSBUUlVFLCBhbGxvd21lYW4gPSBUUlVFKQ0KDQpzdW1tYXJ5KGF1dG9fZXh0cmFuamVyb3MpDQpjaGVja3Jlc2lkdWFscyhhdXRvX2V4dHJhbmplcm9zKQ0Kc2hhcGlyby50ZXN0KHJlc2lkdWFscyhhdXRvX2V4dHJhbmplcm9zKSkNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KYXV0b19leHRyYW5qZXJvc19tYW51YWwgPC0gYXJpbWEodHVyaXN0YXNfZXh0X3RzLCBvcmRlciA9IGMoMiwwLDEpLCBzZWFzb25hbCA9IGxpc3Qob3JkZXIgPSBjKDEsMCwwKSwgcGVyaW9kID0gMTIpKQ0KDQpzdW1tYXJ5KGF1dG9fZXh0cmFuamVyb3NfbWFudWFsKQ0KY2hlY2tyZXNpZHVhbHMoYXV0b19leHRyYW5qZXJvc19tYW51YWwpDQpzaGFwaXJvLnRlc3QocmVzaWR1YWxzKGF1dG9fZXh0cmFuamVyb3NfbWFudWFsKSkNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbG9nX3RzIDwtIGxvZyh0dXJpc3Rhc19leHRfdHMpDQptb2RlbG9fbG9nIDwtIGF1dG8uYXJpbWEobG9nX3RzLCBzZWFzb25hbCA9IFRSVUUsIGFsbG93ZHJpZnQgPSBUUlVFLCBhbGxvd21lYW4gPSBUUlVFKQ0KDQpzdW1tYXJ5KG1vZGVsb19sb2cpDQpjaGVja3Jlc2lkdWFscyhtb2RlbG9fbG9nKQ0Kc2hhcGlyby50ZXN0KHJlc2lkdWFscyhtb2RlbG9fbG9nKSkNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbW9kZWxvX21lam9yYWRvX2xvZyA8LSBhcmltYShsb2dfdHMsIG9yZGVyID0gYygyLDAsMSksIHNlYXNvbmFsID0gbGlzdChvcmRlciA9IGMoMSwwLDApLCBwZXJpb2QgPSAxMikpDQoNCnN1bW1hcnkobW9kZWxvX21lam9yYWRvX2xvZykNCmNoZWNrcmVzaWR1YWxzKG1vZGVsb19tZWpvcmFkb19sb2cpDQpzaGFwaXJvLnRlc3QocmVzaWR1YWxzKG1vZGVsb19tZWpvcmFkb19sb2cpKQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpmb3JlY2FzdF9leHRyYW5qZXJvcyA8LSBmb3JlY2FzdDo6Zm9yZWNhc3QobW9kZWxvX21lam9yYWRvX2xvZywgaCA9IDMwKQ0KDQpwbG90KGZvcmVjYXN0X2V4dHJhbmplcm9zLA0KICAgICBtYWluID0gIlByb27Ds3N0aWNvIExsZWdhZGEgZGUgVHVyaXN0YXMgRXh0cmFuamVyb3MgYSB1biBhw7FvIGRlbCBNdW5kaWFsIDIwMjYiLA0KICAgICB5bGFiID0gIkxsZWdhZGFzIGRlIFR1cmlzdGFzIEV4dHJhbmplcm9zIiwNCiAgICAgeGxhYiA9ICJBw7FvIikNCg0Kc2lnbWEyIDwtIG1vZGVsb19tZWpvcmFkb19sb2ckc2lnbWEyICAgICAgIyB2YXJpYW56YSBkZSBsb3MgcmVzaWR1b3MNCmZjX2V4cCA8LSBmb3JlY2FzdF9leHRyYW5qZXJvcyAgICAgICAgICAgICMgY29waWENCg0KZmNfZXhwJG1lYW4gICA8LSBleHAoZm9yZWNhc3RfZXh0cmFuamVyb3MkbWVhbiArIDAuNSAqIHNpZ21hMikNCmZjX2V4cCRsb3dlciAgPC0gZXhwKGZvcmVjYXN0X2V4dHJhbmplcm9zJGxvd2VyKQ0KZmNfZXhwJHVwcGVyICA8LSBleHAoZm9yZWNhc3RfZXh0cmFuamVyb3MkdXBwZXIpDQpmY19leHAkZml0dGVkIDwtIGV4cChmb3JlY2FzdF9leHRyYW5qZXJvcyRmaXR0ZWQpDQoNCmZlY2hhc19leHQ8LSBmZWNfZm9yZWNhc3QodHVyaXN0YXNfZXh0X3RzLGgpDQoNCmRmX2ZvcmVjYXN0X2V4dCA8LSBkYXRhLmZyYW1lKA0KICBGZWNoYSAgICAgICA9IGZlY2hhc19leHQsDQogIFByb25vc3RpY28gID0gYXMubnVtZXJpYyhmY19leHAkbWVhbiksDQogIExvd2VyODAgICAgID0gYXMubnVtZXJpYyhmY19leHAkbG93ZXJbLDFdKSwNCiAgVXBwZXI4MCAgICAgPSBhcy5udW1lcmljKGZjX2V4cCR1cHBlclssMV0pLA0KICBMb3dlcjk1ICAgICA9IGFzLm51bWVyaWMoZmNfZXhwJGxvd2VyWywyXSksDQogIFVwcGVyOTUgICAgID0gYXMubnVtZXJpYyhmY19leHAkdXBwZXJbLDJdKQ0KKQ0KDQpsaWJyYXJ5KGdncGxvdDIpDQoNCmdncGxvdChkZl9mb3JlY2FzdF9leHQsIGFlcyh4ID0gRmVjaGEsIHkgPSBQcm9ub3N0aWNvKSkgKw0KICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IExvd2VyOTUsIHltYXggPSBVcHBlcjk1KSwNCiAgICAgICAgICAgICAgZmlsbCA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuNCkgKw0KICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IExvd2VyODAsIHltYXggPSBVcHBlcjgwKSwNCiAgICAgICAgICAgICAgZmlsbCA9ICJza3libHVlIiwgIGFscGhhID0gMC40KSArDQogIGdlb21fbGluZShjb2xvciA9ICJkYXJrYmx1ZSIsIHNpemUgPSAxKSArDQogIGxhYnModGl0bGUgPSAiUHJvbsOzc3RpY28gZGUgTGxlZ2FkYSBkZSBUdXJpc3RhcyBFeHRyYW5qZXJvcyAoZXNjYWxhIG9yaWdpbmFsKSIsDQogICAgICAgeCA9ICJGZWNoYSIsIHkgPSAiTGxlZ2FkYXMgZGUgdHVyaXN0YXMiKSArDQogIHNjYWxlX3hfZGF0ZShkYXRlX2xhYmVscyA9ICIlYi0lWSIsIGRhdGVfYnJlYWtzID0gIjIgbW9udGhzIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCg0KKipQUk9Ow5NTVElDTyBERVJSQU1BIFBBUkEgVkFSKioNCg0KRWwgYW7DoWxpc2lzIHByb3NwZWN0aXZvIGRlIGxhIGRlcnJhbWEgZWNvbsOzbWljYSBhc29jaWFkYSBhbCB0dXJpc21vIG5hY2lvbmFsIHJldmVsYSB1biBjb21wb3J0YW1pZW50byBmbHVjdHVhbnRlIGVudHJlIDIwMjUgeSAyMDI3LCBjb24gdW4gY3JlY2ltaWVudG8gbW9kZXJhZG8gZW4gMjAyNSBzZWd1aWRvIGRlIHVuYSBtYXJjYWRhIGRlc2FjZWxlcmFjacOzbiB5IHVuYSBzZXZlcmEgY29udHJhY2Npw7NuIGVuIGxvcyBkb3MgYcOxb3MgcG9zdGVyaW9yZXMsIGxvIHF1ZSBzdWJyYXlhIGxhIG5lY2VzaWRhZCBkZSBlc3RyYXRlZ2lhcyBkZSBtaXRpZ2FjacOzbiB5IGFkYXB0YWNpw7NuIHBhcmEgZW5mcmVudGFyIGVzdGUgZXNjZW5hcmlvIGVjb27Ds21pY28gZGVzYWZpYW50ZS4NCg0KKiAyMDI1OiBTZSBwcm95ZWN0YSB1bmEgZGVycmFtYSBlY29uw7NtaWNhIG1lbnN1YWwgcHJvbWVkaW8gZGUgMSw4ODAgbWRwLCBpbXB1bHNhZGEgcG9yIGVsIHR1cmlzbW8gbmFjaW9uYWwsIGxhIG9jdXBhY2nDs24gaG90ZWxlcmEgeSBsYSBlc3RhZMOtYSBwcm9tZWRpby4gQXVucXVlIHBvc2l0aXZhLCBlbCBjcmVjaW1pZW50byBlcyBtb2RlcmFkbywgY29uIHVuIGFsemEgZGUgc29sbyArNzkgbWRwIHBvciBtZXMuDQoNCiogMjAyNjogTGEgZGVycmFtYSBjYWUgYSAzNTEgbWRwIG1lbnN1YWxlcyBlbiBwcm9tZWRpbywgbWFyY2FuZG8gdW5hIGRlc2FjZWxlcmFjacOzbiBzaWduaWZpY2F0aXZhLiBFbCBjcmVjaW1pZW50byBzZSByZXZpZXJ0ZSwgY29uIHVuYSBww6lyZGlkYSBwcm9tZWRpbyBkZSDigJMxNjkgbWRwIHBvciBtZXMuDQoNCiogMjAyNzogU2UgYW50aWNpcGEgdW5hIGNvbnRyYWNjacOzbiBzZXZlcmEsIGNvbiB1biBwcm9tZWRpbyBtZW5zdWFsIGRlIOKAkzIsNDUyIG1kcC4gTGEgY2HDrWRhIHNlIGFncmF2YSBtZXMgYSBtZXMsIGNvbiB1bmEgcmVkdWNjacOzbiBkZSDigJMyOTAgbWRwLCByZWZsZWphbmRvIHVuIGRlY2xpdmUgZWNvbsOzbWljbyBwcmVvY3VwYW50ZS4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRlcnJhbWFfdGltZXNlcmllcyA8LSB0cyhkZl9maW5hbCREZXJyYW1hX0Vjb25vbWljYV9Fc3RfbWRwX2RpZmYsIHN0YXJ0ID0gYygyMDE1LCAxKSwgZnJlcXVlbmN5ID0gMTIpDQoNCiMgQXV0b2NvcnJlbGFjacOzbiB5IGF1dG9jb3JyZWxhY2nDs24gcGFyY2lhbA0KYWNmKGRlcnJhbWFfdGltZXNlcmllcywgbWFpbj0iQXV0b2NvcnJlbGF0aW9uIC0gTGxlZ2FkYSBUdXJpc3RhcyBOYWNpb25hbGVzIikNCnBhY2YoZGVycmFtYV90aW1lc2VyaWVzLCBtYWluPSJQYXJ0aWFsIEF1dG9jb3JyZWxhdGlvbiAtTGxlZ2FkYSBUdXJpc3RhcyBOYWNpb25hbGVzIikgIw0KDQoNCiMJUHJ1ZWJhIGRlIEVzdGFjaW9uYXJlaWRhZCAtIEF1Z21lbnRlZCBEaWNrZXktRnVsbGVyIFRlc3QgDQphZGZfZGVycmFtYSA8LSBhZGYudGVzdChkZXJyYW1hX3RpbWVzZXJpZXMpDQpwcmludChhZGZfZGVycmFtYSkNCg0KI0hpcMOzdGVzaXMgbnVsYSAoSOKCgCk6IGxhIHNlcmllIG5vIGVzIGVzdGFjaW9uYXJpYQ0KI0hpcMOzdGVzaXMgYWx0ZXJuYXRpdmEgKEjigoEpOiBsYSBzZXJpZSBlcyBlc3RhY2lvbmFyaWENCiNDb21vIGVsIHAtdmFsdWUgZXMgMC4wMSwgcmVjaGF6YW1vcyBI4oKAIOKGkiBsYSBzZXJpZSBlcyBlc3RhY2lvbmFyaWEuDQoNCiMgQXVjb3RvcnJlbGFjacOzbiBzZXJpYWwgLSBCb3gtTGp1bmcgdGVzdA0KbGd1bmpfYm94X2RlcnJhbWEgPC0gQm94LnRlc3QoZGVycmFtYV90aW1lc2VyaWVzLCBsYWcgPSA1LCB0eXBlID0gIkxqdW5nLUJveCIpDQpsZ3Vual9ib3hfZGVycmFtYQ0KDQojSGlww7N0ZXNpcyBudWxhIChI4oKAKTogTk8gaGF5IGF1dG9jb3JyZWxhY2nDs24gZW4gbG9zIHJlc2lkdW9zDQojSGlww7N0ZXNpcyBhbHRlcm5hdGl2YSAoSOKCgSk6IFNJIEhheSBhdXRvY29ycmVsYWNpw7NuIGVuIGxvcyByZXNpZHVvcw0KI0NvbW8gZWwgcC12YWx1ZSA8IDAuMDUsIHJlY2hhemFtb3MgSOKCgCDihpIgc8OtIGhheSBhdXRvY29ycmVsYWNpw7NuIHNpZ25pZmljYXRpdmEgZW4gbGEgc2VyaWUuDQojUG9yIGxvIHF1ZSBjb252aWVuZSBhw7FhZGlyIGNvbXBvbmVudGVzIEFSL01BIHkgbm8gc8OzbG8gdW4gbW9kZWxvIGVzdGFjaW9uYWwgKHNpbiBjb21wb25lbnRlcyBBUiwgTUEgbmkgZGlmZXJlbmNpYWNpw7NuLikNCg0KZGlmZl9kZXJyYW1hPC0gbnNkaWZmcyhkZXJyYW1hX3RpbWVzZXJpZXMpDQpkaWZmX2RlcnJhbWENCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KYXV0b19kZXJyYW1hIDwtIGF1dG8uYXJpbWEoZGVycmFtYV90aW1lc2VyaWVzKQ0Kc3VtbWFyeShhdXRvX2RlcnJhbWEpDQpjaGVja3Jlc2lkdWFscyhhdXRvX2RlcnJhbWEpDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmF1dG8yX2RlcnJhbWEgPC0gYXV0by5hcmltYShkZXJyYW1hX3RpbWVzZXJpZXMsIHNlYXNvbmFsID0gVFJVRSwgc3RlcHdpc2UgPSBGQUxTRSwgYXBwcm94aW1hdGlvbiA9IEZBTFNFKQ0Kc3VtbWFyeShhdXRvMl9kZXJyYW1hKQ0KY2hlY2tyZXNpZHVhbHMoYXV0bzJfZGVycmFtYSkNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZm9yZWNhc3RfZGVycmFtYSA8LSBmb3JlY2FzdDo6Zm9yZWNhc3QoYXV0bzJfZGVycmFtYSwgaCA9IDMwKQ0KDQpwbG90KGZvcmVjYXN0X2RlcnJhbWEsDQogICAgIG1haW4gPSAiUHJvbsOzc3RpY28gRGVycmFtYSBFYy4gSG90ZWxlcyBoYWlhIGVsIE11bmRpYWwgMjAyNiIsDQogICAgIHlsYWIgPSAiRGVycmFtYSBFY29uw7NtaWNhIEhvdGVsZXMiLA0KICAgICB4bGFiID0gIkHDsW8iKQ0KDQpmZWNoYXNfZGVycmFtYSA8LSBmZWNfZm9yZWNhc3QoZGVycmFtYV90aW1lc2VyaWVzLCBoKQ0KDQpkZl9mb3JlY2FzdF9kZXJyYW1hIDwtIGRhdGEuZnJhbWUoDQogIEZlY2hhID0gZmVjaGFzX2RlcnJhbWEsDQogIFByb25vc3RpY28gPSBhcy5udW1lcmljKGZvcmVjYXN0X2RlcnJhbWEkbWVhbiksDQogIExvd2VyODAgPSBhcy5udW1lcmljKGZvcmVjYXN0X2RlcnJhbWEkbG93ZXJbLDFdKSwNCiAgVXBwZXI4MCA9IGFzLm51bWVyaWMoZm9yZWNhc3RfZGVycmFtYSR1cHBlclssMV0pLA0KICBMb3dlcjk1ID0gYXMubnVtZXJpYyhmb3JlY2FzdF9kZXJyYW1hJGxvd2VyWywyXSksDQogIFVwcGVyOTUgPSBhcy5udW1lcmljKGZvcmVjYXN0X2RlcnJhbWEkdXBwZXJbLDJdKQ0KKQ0KDQpnZ3Bsb3QoZGZfZm9yZWNhc3RfZGVycmFtYSwgYWVzKHggPSBGZWNoYSwgeSA9IFByb25vc3RpY28pKSArDQogIGdlb21fcmliYm9uKGFlcyh5bWluID0gTG93ZXI5NSwgeW1heCA9IFVwcGVyOTUpLCBmaWxsID0gImxpZ2h0Z3JlZW4iLCBhbHBoYSA9IDAuNCkgKw0KICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IExvd2VyODAsIHltYXggPSBVcHBlcjgwKSwgZmlsbCA9ICJncmVlbiIsIGFscGhhID0gMC40KSArDQogIGdlb21fbGluZShjb2xvciA9ICJkYXJrZ3JlZW4iLCBzaXplID0gMSkgKw0KICBsYWJzKHRpdGxlID0gIlByb27Ds3N0aWNvIGRlIERlcnJhbWEgRWNvbsOzbWljYSBoYXN0YSBNYXlvIDIwMjYiLA0KICAgICAgIHggPSAiRmVjaGEiLCB5ID0gIkRlcnJhbWEiKSArDQogIHNjYWxlX3hfZGF0ZShkYXRlX2xhYmVscyA9ICIlYi0lWSIsIGRhdGVfYnJlYWtzID0gIjIgbW9udGhzIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCg0KZGZfaGlzdCA8LSBkZl9maW5hbCAlPiUgDQogIG11dGF0ZShGZWNoYSA9IGx1YnJpZGF0ZTo6bWFrZV9kYXRlKEHDkU8sIE1FUywgMSkpICU+JSANCiAgZHBseXI6OnNlbGVjdCggICAgICAgICAgICAgIA0KICAgIEZlY2hhLA0KICAgIERlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHBfZGlmZiwNCiAgICBMbGVnYWRhX1R1cl9OYWNfZGlmZiwNCiAgICBMbGVnYWRhX1R1cl9FeHRfZGlmZg0KICApDQoNCmRmX2ZvcmVjYXN0X25hYyA8LSB0aWJibGUoDQogIEZlY2hhICAgICAgICAgICAgICAgPSBmZWNoYXNfbmFjLA0KICBMbGVnYWRhX1R1cl9OYWNfZGlmZiA9IGFzLm51bWVyaWMoZm9yZWNhc3RfbmFjaW9uYWxlcyRtZWFuKQ0KKQ0KDQpkZl9mb3JlY2FzdF9leHQgPC0gdGliYmxlKA0KICBGZWNoYSAgICAgICAgICAgICAgICAgPSBmZWNoYXNfZXh0LA0KICBMbGVnYWRhX1R1cl9FeHRfZGlmZiAgPSBleHAoZm9yZWNhc3RfZXh0cmFuamVyb3MkbWVhbikNCikNCg0KZGZfZm9yZWNhc3RfZGVycmFtYSA8LSB0aWJibGUoDQogIEZlY2hhICAgPSBmZWNoYXNfZGVycmFtYSwNCiAgRGVycmFtYV9FY29ub21pY2FfRXN0X21kcF9kaWZmID0gYXMubnVtZXJpYyhmb3JlY2FzdF9kZXJyYW1hJG1lYW4pDQopDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShwdXJycikNCmRmX2ZvcmVjYXN0X3RvdGFsIDwtIHJlZHVjZSgNCiAgbGlzdChkZl9mb3JlY2FzdF9kZXJyYW1hLCBkZl9mb3JlY2FzdF9uYWMsIGRmX2ZvcmVjYXN0X2V4dCksDQogIGZ1bGxfam9pbiwNCiAgYnkgPSAiRmVjaGEiDQopDQoNCmRmX3Byb25vc3RpY29zIDwtIGJpbmRfcm93cyhkZl9oaXN0LCBkZl9mb3JlY2FzdF90b3RhbCkgJT4lIA0KICBhcnJhbmdlKEZlY2hhKQ0KDQpoZWFkKGRmX3Byb25vc3RpY29zLCAxNSkNCg0KZGVycmFtYV90c192YXIgPC0gdHMoZGZfcHJvbm9zdGljb3MkRGVycmFtYV9FY29ub21pY2FfRXN0X21kcF9kaWZmLCBzdGFydCA9IGMoMjAxNSwgMSksIGZyZXF1ZW5jeSA9IDEyKQ0KdHVybmFjX3RzX3ZhciA8LSB0cyhkZl9wcm9ub3N0aWNvcyRMbGVnYWRhX1R1cl9OYWNfZGlmZiwgc3RhcnQgPSBjKDIwMTUsIDEpLCBmcmVxdWVuY3kgPSAxMikNCnR1cmV4dF90c192YXIgPC0gdHMoZGZfcHJvbm9zdGljb3MkTGxlZ2FkYV9UdXJfRXh0X2RpZmYsIHN0YXJ0ID0gYygyMDE1LCAxKSwgZnJlcXVlbmN5ID0gMTIpDQpgYGANCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGF0b3NfdHNfdHVyaXN0YXNuYWMgIDwtIGNiaW5kKGRlcnJhbWFfdHNfdmFyLCB0dXJuYWNfdHNfdmFyKSANClZBUnNlbGVjdChkYXRvc190c190dXJpc3Rhc25hYywgbGFnLm1heCA9IDEyLCB0eXBlID0gImNvbnN0IikNCg0KdmFyX2ZpbmFsX25hYyA8LSBWQVIoZGF0b3NfdHNfdHVyaXN0YXNuYWMsIHAgPSAyLCB0eXBlID0gImNvbnN0IikNCnN1bW1hcnkodmFyX2ZpbmFsX25hYykNCg0KDQpzdW1tYXJ5KHZhcl9maW5hbF9uYWMkdmFycmVzdWx0JGRlcnJhbWFfdHNfdmFyKQ0Kc2VyaWFsLnRlc3QodmFyX2ZpbmFsX25hYywgbGFncy5wdCA9IDEyLCB0eXBlID0gIlBULmFzeW1wdG90aWMiKQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpjb2VmcyA8LSBjb2VmKHZhcl9maW5hbF9uYWMkdmFycmVzdWx0JGRlcnJhbWFfdHNfdmFyKQ0KDQpiZXRhX3RvdGFsIDwtIGNvZWZzWyJ0dXJuYWNfdHNfdmFyLmwxIl0gKyBjb2Vmc1sidHVybmFjX3RzX3Zhci5sMiJdDQoNCmRmX2ltcGFjdG8gPC0gZGZfcHJvbm9zdGljb3NbZGZfcHJvbm9zdGljb3MkRmVjaGEgPj0gYXMuRGF0ZSgiMjAyNi0wNi0wMSIpLCBdDQoNCiMgQ2FsY3VsYXIgZWwgY2FtYmlvIGVuIFggcGFyYSBjYWRhIGVzY2VuYXJpbw0KZGZfaW1wYWN0byREZWx0YV8yNSA8LSBkZl9pbXBhY3RvJExsZWdhZGFfVHVyX05hY19kaWZmICogMS4yNQ0KZGZfaW1wYWN0byREZWx0YV81MCA8LSBkZl9pbXBhY3RvJExsZWdhZGFfVHVyX05hY19kaWZmICogMS41MA0KZGZfaW1wYWN0byREZWx0YV83NSA8LSBkZl9pbXBhY3RvJExsZWdhZGFfVHVyX05hY19kaWZmICogMS43NQ0KDQojIEFwbGljYXIgZWwgaW1wYWN0byBlc3RpbWFkbyBhIFkNCmRmX2ltcGFjdG8kRGVycmFtYV8yNSA8LSBkZl9pbXBhY3RvJERlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHBfZGlmZiArIChkZl9pbXBhY3RvJERlbHRhXzI1ICogYmV0YV90b3RhbCkNCmRmX2ltcGFjdG8kRGVycmFtYV81MCA8LSBkZl9pbXBhY3RvJERlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHBfZGlmZiArIChkZl9pbXBhY3RvJERlbHRhXzUwICogYmV0YV90b3RhbCkNCmRmX2ltcGFjdG8kRGVycmFtYV83NSA8LSBkZl9pbXBhY3RvJERlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHBfZGlmZiArIChkZl9pbXBhY3RvJERlbHRhXzc1ICogYmV0YV90b3RhbCkNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0aWR5cikNCg0KIyBFc2NlbmFyaW9zIGRlc2RlIGp1bmlvIDIwMjYNCmRmX3Bsb3QgPC0gZGZfaW1wYWN0b1ssIGMoIkZlY2hhIiwgIkRlcnJhbWFfMjUiLCAiRGVycmFtYV81MCIsICJEZXJyYW1hXzc1IildDQoNCmRmX3Bsb3RfbG9uZyA8LSBwaXZvdF9sb25nZXIoDQogIGRmX3Bsb3QsDQogIGNvbHMgPSAtRmVjaGEsDQogIG5hbWVzX3RvID0gIkVzY2VuYXJpbyIsDQogIHZhbHVlc190byA9ICJEZXJyYW1hIg0KKQ0KDQpkZl9iYXNlX3Bsb3QgPC0gZGZfcHJvbm9zdGljb3NbLCBjKCJGZWNoYSIsICJEZXJyYW1hX0Vjb25vbWljYV9Fc3RfbWRwX2RpZmYiKV0NCmNvbG5hbWVzKGRmX2Jhc2VfcGxvdClbMl0gPC0gIkRlcnJhbWEiDQpkZl9iYXNlX3Bsb3QkRXNjZW5hcmlvIDwtICJTZXJpZV9PcmlnaW5hbCINCg0KZGZfY29tcGxldG8gPC0gcmJpbmQoZGZfcGxvdF9sb25nLCBkZl9iYXNlX3Bsb3QpDQpgYGANCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZ2dwbG90KGRmX2NvbXBsZXRvLCBhZXMoeCA9IEZlY2hhLCB5ID0gRGVycmFtYSwgY29sb3IgPSBFc2NlbmFyaW8pKSArDQogIGdlb21fbGluZShzaXplID0gMSkgKw0KICBsYWJzKHRpdGxlID0gIlNlcmllIGhpc3TDs3JpY2EgKyBlc2NlbmFyaW9zIGRlIGltcGFjdG8iLA0KICAgICAgIHN1YnRpdGxlID0gIkltcGFjdG8gZGUgYXVtZW50byBkZSB0dXJpc3RhcyBkZXNkZSBqdW5pbyAyMDI2IiwNCiAgICAgICB4ID0gIkZlY2hhIiwgeSA9ICJEZXJyYW1hIEVzdGltYWRhIChkaWZmKSIpICsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfbGFiZWxzID0gIiViLSVZIiwgZGF0ZV9icmVha3MgPSAiMyBtb250aHMiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRmX3RhYmxhIDwtIGRmX2ltcGFjdG9bLCBjKA0KICAiRmVjaGEiLA0KICAiRGVycmFtYV9FY29ub21pY2FfRXN0X21kcF9kaWZmIiwgICMgUHJvbsOzc3RpY28gYmFzZQ0KICAiRGVycmFtYV8yNSIsDQogICJEZXJyYW1hXzUwIiwNCiAgIkRlcnJhbWFfNzUiDQopXQ0KDQojIFJlbm9tYnJhciBjb2x1bW5hcyBwYXJhIHF1ZSBzZWFuIG3DoXMgY2xhcmFzDQpjb2xuYW1lcyhkZl90YWJsYSkgPC0gYygiRmVjaGEiLCAiUmVhbCIsICJBdW1lbnRvXzI1IiwgIkF1bWVudG9fNTAiLCAiQXVtZW50b183NSIpDQpkZl90YWJsYQ0KYGBgDQoNCg0KDQojIyMgUHJvbsOzc3RpY29zDQoNCiMjIyMgwr9DdcOhbCBlcyBlbCBwcm9uw7NzdGljbyBvcHRpbWlzdGEgZGUgbGEocykgcHJpbmNpcGFsKGVzKSB2YXJpYWJsZShzKSBkZSBpbnRlcsOpcyBwYXJhIGVsIHBlcsOtb2RvIGRlIGHDsW9zIDIwMjQsIDIwMjUsIHkgMjAyNj8NCg0KQmFqbyBlbCBlc2NlbmFyaW8gb3B0aW1pc3RhLCBlbCBtb2RlbG8gU0FSSU1BWCBwcm95ZWN0YSBxdWU6DQoNCiogMjAyNTogU2UgcHJveWVjdGEgdW5hIGRlcnJhbWEgZWNvbsOzbWljYSBtZW5zdWFsIHByb21lZGlvIGRlIDEsODgwIG1kcCwgbG8gY3VhbCByZXByZXNlbnRhIHVuIGltcHVsc28gc8OzbGlkbywgZXNwZWNpYWxtZW50ZSBwb3IgZWwgZWZlY3RvIGFjdW11bGFkbyBkZSB2YXJpYWJsZXMgY29tbyBsYSBsbGVnYWRhIGRlIHR1cmlzdGFzIG5hY2lvbmFsZXMsIGxhIG9jdXBhY2nDs24gaG90ZWxlcmEgeSBsYSBlc3RhZMOtYSBwcm9tZWRpby4gQSBwZXNhciBkZWwgY3JlY2ltaWVudG8sIGVsIHJpdG1vIGRlIGluY3JlbWVudG8gbWVuc3VhbCBlcyBtb2Rlc3RvOiArNzkgbWRwIHBvciBtZXMuDQoNCiogMjAyNjogTGEgZGVycmFtYSBkaXNtaW51eWUgYSB1biBwcm9tZWRpbyBtZW5zdWFsIGRlIDM1MSBtZHAsIGxvIHF1ZSByZWZsZWphIHVuYSBtYXJjYWRhIGRlc2FjZWxlcmFjacOzbiByZXNwZWN0byBhbCBhw7FvIGFudGVyaW9yLiBFbCBjcmVjaW1pZW50byBzZSBkZWJpbGl0YSwgeSBlbCBjYW1iaW8gbWVuc3VhbCBwcm95ZWN0YWRvIGVzIG5lZ2F0aXZvLCBjb24gdW5hIHDDqXJkaWRhIHByb21lZGlvIGRlIOKAkzE2OSBtZHAgY2FkYSBtZXMsIGFudGljaXBhbmRvIHVuIGRlYmlsaXRhbWllbnRvIHByb2dyZXNpdm8gZGVsIGltcHVsc28gZWNvbsOzbWljby4NCg0KKiAyMDI3OiBMYSBzaXR1YWNpw7NuIHNlIHRvcm5hIGNyw610aWNhOiBlbCBwcm9tZWRpbyBtZW5zdWFsIGNhZSBhIOKAkzIsNDUyIG1kcCwgaW5kaWNhbmRvIHVuIGVzY2VuYXJpbyBkZSBjb250cmFjY2nDs24gc2V2ZXJhLCBpbmNsdXNvIGJham8gY29uZGljaW9uZXMgb3B0aW1pc3Rhcy4gTGEgY2HDrWRhIG1lbnN1YWwgc2UgaW50ZW5zaWZpY2EgY29uIHVuYSByZWR1Y2Npw7NuIHByb21lZGlvIGRlIOKAkzI5MCBtZHAgcG9yIG1lcywgZXZpZGVuY2lhbmRvIHVuYSBwZW5kaWVudGUgZGVzY2VuZGVudGUgcGVsaWdyb3NhLg0KDQpBIHBlc2FyIGRlIGVzdGFyIGJham8gZWwgZXNjZW5hcmlvIG9wdGltaXN0YSwgZWwgbW9kZWxvIFNBUklNQVggYW50aWNpcGEgdW5hIHRlbmRlbmNpYSBjbGFyYW1lbnRlIGRlc2NlbmRlbnRlIGVuIGxhIGRlcnJhbWEgZWNvbsOzbWljYSBtZW5zdWFsIGEgcGFydGlyIGRlIDIwMjUuIExvIG3DoXMgcHJlb2N1cGFudGUgZXMgbGEgdHJhbnNpY2nDs24gZGUgdmFsb3JlcyBwb3NpdGl2b3MgYSBuZWdhdGl2b3MgZW50cmUgMjAyNiB5IDIwMjcsIHBhc2FuZG8gZGUgdW4gY3JlY2ltaWVudG8gbGltaXRhZG8gYSB1bmEgY29udHJhY2Npw7NuIHByb2Z1bmRhLiBMYSBkZXJyYW1hIGVjb27Ds21pY2EgZXN0aW1hZGEgYmFqYSBtw6FzIGRlIDEsNTAwIG1kcCBlbnRyZSAyMDI1IHkgMjAyNyBlbiBwcm9tZWRpbyBtZW5zdWFsLCBjb24gdW5hIHRhc2EgZGUgZGV0ZXJpb3JvIHF1ZSBzZSBhY2VsZXJhIGHDsW8gY29uIGHDsW8uIExvIGFudGVyaW9yIHN1Z2llcmUgbGEgdXJnZW5jaWEgZGUgaW1wbGVtZW50YXIgbWVkaWRhcyBlc3RyYXTDqWdpY2FzIGRlIG1pdGlnYWNpw7NuIGEgcGFydGlyIGRlIDIwMjYsIHNpIHNlIHF1aWVyZSBldml0YXIgdW5hIGNyaXNpcyBkZSBzb3N0ZW5pYmlsaWRhZC4NCg0KDQpgYGB7cn0NCiMgUHJlZGVjaXIgY29uIFNBUklNQVgNCnJlc19wcmVkIDwtIHByZWRpY3QoDQogIG1vZGVsb19zYXJpbWF4X2xhZzEsDQogIG4uYWhlYWQgPSBuX3BlcmlvZG9zLA0KICBuZXd4cmVnICA9IGZ1dHVyZV94cmVnLA0KICBzZS5maXQgICA9IFRSVUUNCikNCnByZWQgPC0gcmVzX3ByZWQkcHJlZA0Kc2UgICA8LSByZXNfcHJlZCRzZQ0KDQojIENvbnN0cnVpciBmb3JlY2FzdF9kZiBhcnJhbmNhbmRvIGVuIGVuZXJvIDIwMjUNCnVsdGltb19udW0gPC0gYXMubnVtZXJpYyh0YWlsKHRpbWUoeSksIDEpKQ0KcGVyaW9kb195bSA8LSBhcy55ZWFybW9uKHVsdGltb19udW0gKyBzZXEoMS8xMiwgbl9wZXJpb2Rvcy8xMiwgYnkgPSAxLzEyKSkNCmZvcmVjYXN0X2RmIDwtIGRhdGEuZnJhbWUoDQogIFBlcmlvZG8gICA9IGFzLkRhdGUocGVyaW9kb195bSksDQogIFBlc2ltaXN0YSA9IHByZWQgLSAxLjk2ICogc2UsDQogIFJlYWwgICAgICA9IHByZWQsDQogIE9wdGltbyAgICA9IHByZWQgKyAxLjk2ICogc2UNCikNCg0KZ2dwbG90KGZvcmVjYXN0X2RmLCBhZXMoeCA9IFBlcmlvZG8pKSArDQogIGdlb21fcmliYm9uKGFlcyh5bWluID0gUGVzaW1pc3RhLCB5bWF4ID0gT3B0aW1vKSwNCiAgICAgICAgICAgICAgZmlsbCA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuNCkgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSBSZWFsKSwgY29sb3IgPSAiYmx1ZSIsIHNpemUgPSAxKSArDQogIGdlb21fcG9pbnQoYWVzKHkgPSBSZWFsKSwgY29sb3IgPSAiYmx1ZSIsIHNpemUgPSAyKSArDQogIHNjYWxlX3hfZGF0ZSgNCiAgICBkYXRlX2JyZWFrcyA9ICIzIG1vbnRocyIsDQogICAgZGF0ZV9sYWJlbHMgPSAiJWIgJVkiLA0KICAgIGxpbWl0cyA9IGFzLkRhdGUoYygiMjAyNS0wMS0wMSIsIG1heChmb3JlY2FzdF9kZiRQZXJpb2RvKSkpDQogICkgKw0KICBsYWJzKA0KICAgIHRpdGxlICAgID0gIlByb27Ds3N0aWNvIE1lbnN1YWwgZGUgRGVycmFtYSBFY29uw7NtaWNhIChtZHApIiwNCiAgICBzdWJ0aXRsZSA9ICJTw7NsbyBsYSBwYXJ0ZSBkZSBmb3JlY2FzdCBkZXNkZSBlbmVybyAyMDI1IiwNCiAgICB4ICAgICAgICA9ICJNZXMiLA0KICAgIHkgICAgICAgID0gIkRlcnJhbWEgRWNvbsOzbWljYSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KYGBge3J9DQpkZl94cmVnX2xhZzEgPC0gYXMubWF0cml4KGRmX21vZF9sYWcxKQ0KY29sX25hYyA8LSB3aGljaChjb2xuYW1lcyhkZl94cmVnX2xhZzEpID09ICJMbGVnYWRhX1R1cl9OYWNfZGlmZiIpDQoNCiMgQ3JlYXIgZXNjZW5hcmlvcyBtb2RpZmljYW5kbyBTT0xPIHZhcmlhYmxlIExsZWdhZGFfVHVyX05hY19kaWZmDQpmdXR1cmVfeHJlZ19iYXNlIDwtIG1hdHJpeChyZXAoZGZfeHJlZ19sYWcxW25yb3coZGZfeHJlZ19sYWcxKSwgXSwgbl9wZXJpb2RvcyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gbl9wZXJpb2RvcywgYnlyb3cgPSBUUlVFKQ0KZ3Jvd3RoIDwtICgxLjAyKV4oMTpuX3BlcmlvZG9zKQ0KZnV0dXJlX3hyZWdfYmFzZSA8LSBzd2VlcChmdXR1cmVfeHJlZ19iYXNlLCAxLCBncm93dGgsIGAqYCkNCmNvbG5hbWVzKGZ1dHVyZV94cmVnX2Jhc2UpIDwtIGNvbG5hbWVzKGRmX3hyZWdfbGFnMSkNCg0KIyBFc2NlbmFyaW9zIG1vZGlmaWNhbmRvIHNvbG8gbGEgY29sdW1uYSAnTGxlZ2FkYV9UdXJfTmFjX2RpZmYnDQpmdXR1cmVfMjUgPC0gZnV0dXJlX3hyZWdfYmFzZQ0KZnV0dXJlXzUwIDwtIGZ1dHVyZV94cmVnX2Jhc2UNCmZ1dHVyZV83NSA8LSBmdXR1cmVfeHJlZ19iYXNlDQpmdXR1cmVfMjVbLCBjb2xfbmFjXSA8LSBmdXR1cmVfMjVbLCBjb2xfbmFjXSAqIDEuMjUNCmZ1dHVyZV81MFssIGNvbF9uYWNdIDwtIGZ1dHVyZV81MFssIGNvbF9uYWNdICogMS41MA0KZnV0dXJlXzc1WywgY29sX25hY10gPC0gZnV0dXJlXzc1WywgY29sX25hY10gKiAxLjc1DQoNCiMgRm9yZWNhc3RzDQpmb3JlY2FzdF9iYXNlIDwtIHByZWRpY3QobW9kZWxvX3NhcmltYXhfbGFnMSwgbi5haGVhZCA9IG5fcGVyaW9kb3MsIG5ld3hyZWcgPSBmdXR1cmVfeHJlZ19iYXNlKSRwcmVkDQpmb3JlY2FzdF8yNSAgIDwtIHByZWRpY3QobW9kZWxvX3NhcmltYXhfbGFnMSwgbi5haGVhZCA9IG5fcGVyaW9kb3MsIG5ld3hyZWcgPSBmdXR1cmVfMjUpJHByZWQNCmZvcmVjYXN0XzUwICAgPC0gcHJlZGljdChtb2RlbG9fc2FyaW1heF9sYWcxLCBuLmFoZWFkID0gbl9wZXJpb2RvcywgbmV3eHJlZyA9IGZ1dHVyZV81MCkkcHJlZA0KZm9yZWNhc3RfNzUgICA8LSBwcmVkaWN0KG1vZGVsb19zYXJpbWF4X2xhZzEsIG4uYWhlYWQgPSBuX3BlcmlvZG9zLCBuZXd4cmVnID0gZnV0dXJlXzc1KSRwcmVkDQoNCmxhc3RfZGF0ZSA8LSBhcy55ZWFybW9uKHRhaWwodGltZSh5X2xhZzEpLCAxKSkNCnBlcmlvZG9feW0gPC0gc2VxKGxhc3RfZGF0ZSArIDEvMTIsIGJ5ID0gMS8xMiwgbGVuZ3RoLm91dCA9IG5fcGVyaW9kb3MpDQpmZWNoYXNfZm9yZWNhc3QgPC0gYXMuRGF0ZShwZXJpb2RvX3ltKQ0KZmVjaGFzX2hpc3RvcmljbyA8LSBhcy5EYXRlKGFzLnllYXJtb24odGltZSh5X2xhZzEpKSkNCg0KZGZfaGlzdCA8LSBkYXRhLmZyYW1lKA0KICBQZXJpb2RvID0gZmVjaGFzX2hpc3RvcmljbywNCiAgVmFsb3IgPSBhcy5udW1lcmljKHlfbGFnMSksDQogIEVzY2VuYXJpbyA9ICJIaXN0w7NyaWNvIg0KKQ0KDQpkZl9iYXNlIDwtIGRhdGEuZnJhbWUoUGVyaW9kbyA9IGZlY2hhc19mb3JlY2FzdCwgVmFsb3IgPSBmb3JlY2FzdF9iYXNlLCBFc2NlbmFyaW8gPSAiUHJvbsOzc3RpY28gQmFzZSIpDQpkZl8yNSAgIDwtIGRhdGEuZnJhbWUoUGVyaW9kbyA9IGZlY2hhc19mb3JlY2FzdCwgVmFsb3IgPSBmb3JlY2FzdF8yNSwgICBFc2NlbmFyaW8gPSAiRXNjZW5hcmlvICsyNSUiKQ0KZGZfNTAgICA8LSBkYXRhLmZyYW1lKFBlcmlvZG8gPSBmZWNoYXNfZm9yZWNhc3QsIFZhbG9yID0gZm9yZWNhc3RfNTAsICAgRXNjZW5hcmlvID0gIkVzY2VuYXJpbyArNTAlIikNCmRmXzc1ICAgPC0gZGF0YS5mcmFtZShQZXJpb2RvID0gZmVjaGFzX2ZvcmVjYXN0LCBWYWxvciA9IGZvcmVjYXN0Xzc1LCAgIEVzY2VuYXJpbyA9ICJFc2NlbmFyaW8gKzc1JSIpDQoNCmRmX3RvdGFsIDwtIGJpbmRfcm93cyhkZl9oaXN0LCBkZl9iYXNlLCBkZl8yNSwgZGZfNTAsIGRmXzc1KQ0KDQpnZ3Bsb3QoZGZfdG90YWwsIGFlcyh4ID0gUGVyaW9kbywgeSA9IFZhbG9yLCBjb2xvciA9IEVzY2VuYXJpbywgbGluZXR5cGUgPSBFc2NlbmFyaW8pKSArDQogIGdlb21fbGluZShzaXplID0gMSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAxLjcpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoDQogICAgIkhpc3TDs3JpY28iID0gImJsYWNrIiwNCiAgICAiUHJvbsOzc3RpY28gQmFzZSIgPSAiYmx1ZSIsDQogICAgIkVzY2VuYXJpbyArMjUlIiA9ICJncmVlbjQiLA0KICAgICJFc2NlbmFyaW8gKzUwJSIgPSAib3JhbmdlIiwNCiAgICAiRXNjZW5hcmlvICs3NSUiID0gInJlZCINCiAgKSkgKw0KICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYygNCiAgICAiSGlzdMOzcmljbyIgPSAic29saWQiLA0KICAgICJQcm9uw7NzdGljbyBCYXNlIiA9ICJzb2xpZCIsDQogICAgIkVzY2VuYXJpbyArMjUlIiA9ICJkb3R0ZWQiLA0KICAgICJFc2NlbmFyaW8gKzUwJSIgPSAiZG90ZGFzaCIsDQogICAgIkVzY2VuYXJpbyArNzUlIiA9ICJ0d29kYXNoIg0KICApKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUHJvbsOzc3RpY28gZGUgbGEgRGVycmFtYSBFY29uw7NtaWNhIGNvbiBFc2NlbmFyaW9zIFNBUklNQVgiLA0KICAgIHN1YnRpdGxlID0gIkltcGFjdG8gZGUgYXVtZW50b3MgZW4gTGxlZ2FkYV9UdXJfTmFjX2RpZmYgKDI1JSwgNTAlLCA3NSUpIiwNCiAgICB4ID0gIkZlY2hhIiwNCiAgICB5ID0gIkRlcnJhbWEgRWNvbsOzbWljYSAobWRwKSIsDQogICAgY29sb3IgPSAiRXNjZW5hcmlvIiwNCiAgICBsaW5ldHlwZSA9ICJFc2NlbmFyaW8iDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKQ0KICApDQpgYGANCg0KDQoNCmBgYHtyfQ0KbGlicmFyeSh6b28pDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCg0KaGlzdG9yaWNvX2RmIDwtIGRhdGEuZnJhbWUoDQogIFBlcmlvZG8gPSBhcy5EYXRlKGFzLnllYXJtb24odGltZSh5KSkpLA0KICBWYWxvciA9IGFzLm51bWVyaWMoeSksDQogIEVzY2VuYXJpbyA9ICJIaXN0w7NyaWNvIg0KKQ0KDQpmb3JlY2FzdF9sb25nIDwtIGZvcmVjYXN0X2RmICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyA9IGMoUGVzaW1pc3RhLCBSZWFsLCBPcHRpbW8pLA0KICAgIG5hbWVzX3RvID0gIkVzY2VuYXJpbyIsDQogICAgdmFsdWVzX3RvID0gIlZhbG9yIg0KICApDQoNCmRmX3RvZG8gPC0gYmluZF9yb3dzKGhpc3Rvcmljb19kZiwgZm9yZWNhc3RfbG9uZykNCg0KZ2dwbG90KGRmX3RvZG8sIGFlcyh4ID0gUGVyaW9kbywgeSA9IFZhbG9yLCBjb2xvciA9IEVzY2VuYXJpbywgbGluZXR5cGUgPSBFc2NlbmFyaW8pKSArDQogIGdlb21fbGluZShzaXplID0gMSkgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBzdWJzZXQoZGZfdG9kbywgRXNjZW5hcmlvICE9ICJIaXN0w7NyaWNvIiksIHNpemUgPSAyKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKA0KICAgICJIaXN0w7NyaWNvIiAgPSAiYmxhY2siLA0KICAgICJSZWFsIiAgICAgICA9ICJibHVlIiwNCiAgICAiT3B0aW1vIiAgICAgPSAiZGFya2dyZWVuIiwNCiAgICAiUGVzaW1pc3RhIiAgPSAicmVkIg0KICApKSArDQogIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXMgPSBjKA0KICAgICJIaXN0w7NyaWNvIiAgPSAic29saWQiLA0KICAgICJSZWFsIiAgICAgICA9ICJzb2xpZCIsDQogICAgIk9wdGltbyIgICAgID0gImRhc2hlZCIsDQogICAgIlBlc2ltaXN0YSIgID0gImRvdHRlZCINCiAgKSkgKw0KICBsYWJzKA0KICAgIHRpdGxlICAgID0gIlNlcmllIGRlIFRpZW1wbyBjb24gRXNjZW5hcmlvcyBkZSBQcm9uw7NzdGljbyIsDQogICAgc3VidGl0bGUgPSAiSW5jbHV5ZSBoaXN0w7NyaWNvIHkgcHJveWVjY2nDs24gMjAyNeKAkzIwMjciLA0KICAgIHggICAgICAgID0gIk1lcyIsDQogICAgeSAgICAgICAgPSAiRGVycmFtYSBFY29uw7NtaWNhIChtZHApIiwNCiAgICBjb2xvciAgICA9ICJFc2NlbmFyaW8iLA0KICAgIGxpbmV0eXBlID0gIkVzY2VuYXJpbyINCiAgKSArDQogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICI2IG1vbnRocyIsIGRhdGVfbGFiZWxzID0gIiViXG4lWSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQoNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGdncGxvdDIpDQoNCmRmX2xvbmcgPC0gZm9yZWNhc3RfZGYgJT4lDQogIGRwbHlyOjpzZWxlY3QoUGVyaW9kbywgUGVzaW1pc3RhLCBSZWFsLCBPcHRpbW8pICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyAgICAgID0gYyhQZXNpbWlzdGEsIFJlYWwsIE9wdGltbyksDQogICAgbmFtZXNfdG8gID0gIkVzY2VuYXJpbyIsDQogICAgdmFsdWVzX3RvID0gIkRlbHRhIg0KICApDQoNCmdncGxvdChkZl9sb25nLCBhZXMoeCA9IFBlcmlvZG8sIHkgPSBEZWx0YSwgY29sb3IgPSBFc2NlbmFyaW8sIGxpbmV0eXBlID0gRXNjZW5hcmlvKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwoDQogICAgdmFsdWVzID0gYygNCiAgICAgICJPcHRpbW8iICAgID0gImRhcmtncmVlbiIsICAjIDwtLSBzaW4gYWNlbnRvLCBjb2luY2lkZSBjb24gdHUgY29sdW1uYQ0KICAgICAgIlJlYWwiICAgICAgPSAiYmx1ZSIsDQogICAgICAiUGVzaW1pc3RhIiA9ICJyZWQiDQogICAgKQ0KICApICsNCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKA0KICAgIHZhbHVlcyA9IGMoDQogICAgICAiT3B0aW1vIiAgICA9ICJkYXNoZWQiLA0KICAgICAgIlJlYWwiICAgICAgPSAic29saWQiLA0KICAgICAgIlBlc2ltaXN0YSIgPSAiZG90dGVkIg0KICAgICkNCiAgKSArDQogIHNjYWxlX3hfZGF0ZSgNCiAgICBkYXRlX2JyZWFrcyA9ICIyIG1vbnRocyIsDQogICAgZGF0ZV9sYWJlbHMgPSAiJWJcbiVZIg0KICApICsNCiAgbGFicygNCiAgICB0aXRsZSAgICA9ICJQcm9uw7NzdGljbyBNZW5zdWFsIGRlIM6URGVycmFtYSBFY29uw7NtaWNhIChtZHApIiwNCiAgICBzdWJ0aXRsZSA9ICJQaWNvcyB5IHZhbGxlcyBtZXMgYSBtZXMgZW4gdW4gc29sbyBncsOhZmljbyIsDQogICAgeCAgICAgICAgPSAiTWVzIiwNCiAgICB5ICAgICAgICA9ICJDYW1iaW8gbWVuc3VhbCBlc3RpbWFkbyAobWRwKSIsDQogICAgY29sb3IgICAgPSAiRXNjZW5hcmlvIiwNCiAgICBsaW5ldHlwZSA9ICJFc2NlbmFyaW8iDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBheGlzLnRleHQueCAgICAgPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwNCiAgICBwbG90LnRpdGxlICAgICAgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiksDQogICAgcGxvdC5zdWJ0aXRsZSAgID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksDQogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSINCiAgKQ0KYGBgDQoNCg0KYGBge3J9DQp0YWJsYV9kaWZmcyA8LSBmb3JlY2FzdF9kZiAlPiUNCiAgZHBseXI6OnNlbGVjdChQZXJpb2RvLCBQZXNpbWlzdGEsIFJlYWwsIE9wdGltbykgJT4lDQogIGRwbHlyOjpyZW5hbWUoDQogICAgYM6UIFBlc2ltaXN0YWAgPSBQZXNpbWlzdGEsDQogICAgYM6UIFJlYWxgICAgICAgPSBSZWFsLA0KICAgIGDOlCDDk3B0aW1vYCAgICA9IE9wdGltbw0KICApDQoNCnByaW50KHRhYmxhX2RpZmZzKQ0KYGBgDQoNCirCv0N1w6FudG8gc2UgZXNwZXJhIHF1ZSBzZWEgbGEgZGVycmFtYSBwcm9tZWRpbyBtZW5zdWFsIHRvdGFsIChtw6FzIG5vIGVsIGNhbWJpbykgZW4gMjAyNS0yMDI3PyoNCg0KUHJvbWVkaW8gbWVuc3VhbCBhYnNvbHV0byBlc3BlcmFkbyBwb3IgYcOxbyAoZW4gbWRwKQ0KDQpgYGB7cn0NCmxhc3Rfbml2ZWwgPC0gYXMubnVtZXJpYyh0YWlsKGRlcnJhbWFfdHMsIDEpKQ0KZmNfbGV2ZWxzIDwtIGZvcmVjYXN0X2RmICU+JQ0KICBtdXRhdGUoDQogICAgTml2ZWxfUGVzICA9IGxhc3Rfbml2ZWwgKyBjdW1zdW0oUGVzaW1pc3RhKSwNCiAgICBOaXZlbF9SZWFsID0gbGFzdF9uaXZlbCArIGN1bXN1bShSZWFsKSwNCiAgICBOaXZlbF9PcHQgID0gbGFzdF9uaXZlbCArIGN1bXN1bShPcHRpbW8pLA0KICAgIEHDsW8gICAgICAgID0gYXMuaW50ZWdlcihmb3JtYXQoUGVyaW9kbywgIiVZIikpDQogICkNCg0KIyBSZXN1bWVuIGFudWFsIOKAlA0KcmVzdW1lbl9uaXZlbGVzIDwtIGZjX2xldmVscyAlPiUNCiAgZ3JvdXBfYnkoQcOxbykgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBgUHJvbWVkaW8gw5NwdGltbyAobWRwL21lcylgID0gcm91bmQobWVhbihOaXZlbF9PcHQsIG5hLnJtPVRSVUUpLCAwKQ0KICApDQpwcmludChyZXN1bWVuX25pdmVsZXMpDQpgYGANCg0KKsK/RW4gY3XDoW50byBzZSBlc3BlcmEgcXVlIGF1bWVudGUgbyBkaXNtaW51eWEgbWVuc3VhbG1lbnRlIGxhIGRlcnJhbWEgZHVyYW50ZSBlbCBhw7FvPyoNCg0KUHJvbWVkaW8gZGVsIGluY3JlbWVudG8gbyBkZWNyZW1lbnRvIG1lbnN1YWwgZXNwZXJhZG8gKGVuIG1kcCkNCg0KYGBge3J9DQpyZXN1bWVuX2FudWFsIDwtIHRhYmxhX2RpZmZzICU+JQ0KICBkcGx5cjo6bXV0YXRlKEHDsW8gPSBhcy5pbnRlZ2VyKGZvcm1hdChQZXJpb2RvLCAiJVkiKSkpICU+JQ0KICBkcGx5cjo6Z3JvdXBfYnkoQcOxbykgJT4lDQogIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgYFByb21lZGlvIMOTcHRpbW8gKG1kcC9tZXMpYCA9IHJvdW5kKG1lYW4oYM6UIMOTcHRpbW9gLCBuYS5ybSA9IFRSVUUpLCAwKQ0KICApDQoNCnByaW50KHJlc3VtZW5fYW51YWwpDQpgYGANCg0KDQojIyMjIEVuIGxvcyBhw7FvcyAyMDIzIOKAkyAyMDI0LCBjdcOhbGVzIHNvbiBsYXMgdmFyaWFibGVzIGNvbnRyb2wgcXVlIGV4cGxpY2FuIHVuIGF1bWVudG8gbyBkaXNtaW51Y2nDs24gc29icmUgbGEocykgcHJpbmNpcGFsKGVzKSB2YXJpYWJsZShzKSBkZSBpbnRlcsOpcz8gVmlzdWFsaXphciBlbCB0aXBvIGRlIHJlbGFjacOzbiBkZSBkaWNoYXMgdmFyaWFibGUgY29udHJvbCBzb2JyZSBsYShzKSBwcmluY2lwYWwoZXMpIHZhcmlhYmxlKHMpIGRlIGludGVyw6lzLg0KDQpQYXJhIGVsIHBlcmlvZG8gMjAyM+KAkzIwMjQsIGVzdGFzIHNvbiBsYXMgdmFyaWFibGVzIGRlIGNvbnRyb2wgcXVlIGV4cGxpY2FuIGxhIHZhcmlhY2nDs24gKGRpZmYpIGRlIGxhIERlcnJhbWEgRWNvbsOzbWljYSwgb3JkZW5hZGFzIHBvciBtYWduaXR1ZCBkZSBjb2VmaWNpZW50ZSBlbiBlbCBTQVJJTUFYOg0KDQoqIExsZWdhZGFfVHVyX05hY19kaWZmOiBVbiBpbmNyZW1lbnRvIGRlIDEsMDAwIGxsZWdhZGFzIHJlc3BlY3RvIGFsIG1lcyBhbnRlcmlvciBzZSBhc29jaWEgY29uIHVuIGF1bWVudG8gZGUg4omIIDEzOC4yNyBtZHAgZW4gbGEgZGVycmFtYS4gQ29lZmljaWVudGU6ICsxMzguMjcgfCBDb3JyZWxhY2nDs24gKHIpOiAwLjU0IHwgVGlwbyBkZSByZWxhY2nDs246IEZ1ZXJ0ZSB5IHBvc2l0aXZhDQoNCiogWC5PY3VwYWNpb25fSG90ZWxlc19kaWZmOiBVbiBhdW1lbnRvIGRlIDEgcHVudG8gcG9yY2VudHVhbCBlbiBsYSBvY3VwYWNpw7NuIGhvdGVsZXJhIGdlbmVyYSB1biBpbmNyZW1lbnRvIGRlIOKJiCA2OC44OSBtZHAgZW4gbGEgZGVycmFtYS4gQ29lZmljaWVudGU6ICs2OC44OSB8IENvcnJlbGFjacOzbiAocik6IDAuNjMgfCBUaXBvIGRlIHJlbGFjacOzbjogRnVlcnRlIHkgcG9zaXRpdmENCg0KKiBFc3RhZGlhX1Byb21lZGlvX2xvZ19kaWZmOiBVbiBhdW1lbnRvIHJlbGF0aXZvIGRlbCAxICUgZW4gbGEgZXN0YWTDrWEgcHJvbWVkaW8gc2UgcmVsYWNpb25hIGNvbiB1biBpbmNyZW1lbnRvIGRlIOKJiCA1NS43MCBtZHAgZW4gbGEgZGVycmFtYS4gQ29lZmljaWVudGU6ICs1NS43MCB8IENvcnJlbGFjacOzbiAocik6IDAuNTUgfCBUaXBvIGRlIHJlbGFjacOzbjogTW9kZXJhZGEgYSBmdWVydGUgeSBwb3NpdGl2YQ0KDQoqIFR1cmlzdGFzX05vY2hlX0V4dF9kaWZmOiBVbiBpbmNyZW1lbnRvIGRlIDEsMDAwIG5vY2hlcyBkZSB0dXJpc3RhcyBleHRyYW5qZXJvcyBzZSBhc29jaWEgY29uIHVuIGF1bWVudG8gZGUg4omIIDYuMzUgbWRwIGVuIGxhIGRlcnJhbWEuIENvZWZpY2llbnRlOiArNi4zNSB8IENvcnJlbGFjacOzbiAocik6IDAuMzkgfCBUaXBvIGRlIHJlbGFjacOzbjogTW9kZXJhZGEgeSBwb3NpdGl2YQ0KDQoqIEN1YXJ0b3NfUmVnaXN0cmFkb3NfZGlmZjogVW4gaW5jcmVtZW50byBkZSAxLDAwMCBjdWFydG9zIHJlZ2lzdHJhZG9zIHNlIGFzb2NpYSBjb24gdW4gYXVtZW50byBkZSDiiYggMy43NCBtZHAgZW4gbGEgZGVycmFtYS4gQ29lZmljaWVudGU6ICszLjc0IHwgQ29ycmVsYWNpw7NuIChyKTogMC4yMiB8IFRpcG8gZGUgcmVsYWNpw7NuOiBEw6liaWwgeSBwb3NpdGl2YQ0KDQoqIEN1YXJ0b3NfRGlzcG9uaWJsZXNfUHJvbWVkaW9fbG9nX2RpZmY6IFVuIGF1bWVudG8gcmVsYXRpdm8gZGVsIDEgJSBlbiBsb3MgY3VhcnRvcyBkaXNwb25pYmxlcyBwcm9tZWRpbyBzZSBhc29jaWEgY29uIHVuYSBkaXNtaW51Y2nDs24gZGUg4omIIDMuMDYgbWRwIGVuIGxhIGRlcnJhbWEuIENvZWZpY2llbnRlOiDigJMzLjA2IHwgQ29ycmVsYWNpw7NuIChyKTogMC4yOCB8IFRpcG8gZGUgcmVsYWNpw7NuOiBNdXkgZMOpYmlsIHkgbmVnYXRpdmENCg0KDQpgYGB7cn0NCnN1bW1hcnkobW9kZWxvX3NhcmltYXhfbGFnMSkkY29lZg0KDQpjb2VmX3NhcmltYXggPC0gc3VtbWFyeShtb2RlbG9fc2FyaW1heF9sYWcxKSRjb2VmDQoNCmNvZWZfZXhwbGljYXRpdmFzIDwtIGNvZWZfc2FyaW1heFtuYW1lcyhjb2VmX3NhcmltYXgpICVpbiUgY29sbmFtZXMoZGZfeHJlZyldDQoNCmxpYnJhcnkodGliYmxlKQ0KbGlicmFyeShkcGx5cikNCg0KdGFibGFfY29lZiA8LSB0aWJibGUoDQogIFZhcmlhYmxlID0gbmFtZXMoY29lZl9leHBsaWNhdGl2YXMpLA0KICBDb2VmaWNpZW50ZSA9IGFzLm51bWVyaWMoY29lZl9leHBsaWNhdGl2YXMpDQopICU+JQ0KICBtdXRhdGUoQ29lZl9BYnMgPSBhYnMoQ29lZmljaWVudGUpKSAlPiUNCiAgYXJyYW5nZShkZXNjKENvZWZfQWJzKSkNCg0KcHJpbnQodGFibGFfY29lZikNCmBgYA0KDQpgYGB7cn0NCmRmX3Bsb3QgPC0gZGZfZmluYWxbZGZfZmluYWwkQcORTyAlaW4lIGMoMjAyMywgMjAyNCksIF0NCg0KZm9yICh2YXIgaW4gdmFyaWFibGVzX2V4cGxpY2F0aXZhcykgew0KICAjIENhbGN1bGFyIGNvcnJlbGFjacOzbg0KICByIDwtIGNvcihkZl9wbG90W1t2YXJdXSwgZGZfcGxvdCREZXJyYW1hX0Vjb25vbWljYV9Fc3RfbWRwX2RpZmYsIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQ0KICByX2xhYmVsIDwtIHBhc3RlMCgiciA9ICIsIHJvdW5kKHIsIDIpKQ0KDQogIHAgPC0gZ2dwbG90KGRmX3Bsb3QsIGFlc19zdHJpbmcoeCA9IHZhciwgeSA9ICJEZXJyYW1hX0Vjb25vbWljYV9Fc3RfbWRwX2RpZmYiKSkgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAiYmx1ZSIpICsNCiAgICBnZ3RpdGxlKHBhc3RlKCJSZWxhY2nDs24gZW50cmUiLCB2YXIsICJ5IERlcnJhbWEgRWNvbsOzbWljYSAoMjAyMy0yMDI0KSIsIHJfbGFiZWwpKSArDQogICAgeGxhYih2YXIpICsNCiAgICB5bGFiKCJEZXJyYW1hIEVjb27Ds21pY2EgRXN0aW1hZGEgKERpZmYpIikNCiAgDQogIHByaW50KHApDQp9DQpgYGANCg0KYGBge3J9DQpjb3JfbWF0cml4IDwtIGNvcihkZl9wbG90LCB1c2UgPSAiY29tcGxldGUub2JzIikNCg0KIyBFeHRyYWVyIGNvcnJlbGFjaW9uZXMgY29uIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlDQpjb3JyZWxhY2lvbmVzIDwtIGNvcl9tYXRyaXhbIkRlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHBfZGlmZiIsIC0xXQ0KDQp0YWJsYV9jb3JyZWxhY2lvbiA8LSBkYXRhLmZyYW1lKA0KICBWYXJpYWJsZSA9IG5hbWVzKGNvcnJlbGFjaW9uZXMpLA0KICBDb2VmX2RlX0NvcnJlbGFjaW9uID0gYXMubnVtZXJpYyhjb3JyZWxhY2lvbmVzKQ0KKSAlPiUNCiAgYXJyYW5nZShkZXNjKGFicyhDb2VmX2RlX0NvcnJlbGFjaW9uKSkpICAjIFNpbiBlc3BhY2lvcyBlbiBlbCBub21icmUNCg0KcHJpbnQodGFibGFfY29ycmVsYWNpb24pDQpgYGANCg0KDQoNCiMjIyMgwr9DdcOhbCBlcyBlbCB0YW1hw7FvIGRlbCBpbXBhY3RvIGRlIHVuIGluY3JlbWVudG8gZW4gMjUlLCA1MCUsIHkgNzUlIGRlIGxhcyB2YXJpYWJsZXMgY29udHJvbCBzaWduaWZpY2F0aXZhcyBzb2JyZSBsYShzKSBwcmluY2lwYWwoZXMpIHZhcmlhYmxlKHMpIGRlIGludGVyw6lzPw0KDQoqKkxhIExsZWdhZGEgZGUgdHVyaXN0YXMgbmFjaW9uYWxlcyBlcyBlbCBmYWN0b3IgbcOhcyBkZXRlcm1pbmFudGU6KioNCg0KKiBVbiBhdW1lbnRvIGRlbCA3NSAlIGVuIGxhIHZhcmlhYmxlIExsZWdhZGFfVHVyX05hY19kaWZmIHNlIHRyYWR1Y2UgZW4gdW4gaW5jcmVtZW50byBhcHJveGltYWRvIGRlIDEwMy43MCBtZHAgZW4gbGEgZGVycmFtYS4NCg0KKiBJbmNsdXNvIHVuIDUwICUgZXh0cmEgZGUgbGxlZ2FkYXMgbmFjaW9uYWxlcyBhcG9ydGEgY2FzaSA3MCBtZHAuIEltcGxpY2EgcXVlIGNhbXBhw7FhcyBvIHBvbMOtdGljYXMgb3JpZW50YWRhcyBhIGF0cmFlciB0dXJpc3RhcyBuYWNpb25hbGVzIHRlbmRyw6FuIGVsIHJldG9ybm8gbcOhcyBhbHRvIGVuIGRlcnJhbWEgZWNvbsOzbWljYS4NCg0KQXNpbWlzbW8sIGxhICoqT2N1cGFjacOzbiBob3RlbGVyYSB5IEVzdGFkw61hIHByb21lZGlvIHNlZ3VuZG9zIG1vdG9yZXMgaW1wb3J0YW50ZXMqKg0KDQoqIE9jdXBhY2lvbl9FeHRfZGlmZiA6ICs3NSAlIOKHkiArNTEuNyBtZHAgJiBFc3RhZGlhX1Byb21lZGlvX2xvZ19kaWZmOiArNzUgJSDih5IgKzQxLjggbWRwDQoNCiogU2UgZGV0ZXJtaW5hIHF1ZSwgYWxhcmdhciBsYSBlc3RhbmNpYSBtZWRpYSB5IG1hbnRlbmVyIGFsdGEgbGEgb2N1cGFjacOzbiBvcHRpbWl6YSBzaWduaWZpY2F0aXZhbWVudGUgbGEgZGVycmFtYS4NCg0KIyMjIyMgTGxlZ2FkYSBkZSBUdXJpc3RhcyBOYWNpb25hbGVzIHZzIEV4dHJhbmplcm9zLg0KDQoqIEVuIHTDqXJtaW5vcyBhYnNvbHV0b3MsIGxhcyBsbGVnYWRhcyBkZSB0dXJpc3RhcyBuYWNpb25hbGVzIChtb2RlbG9fdmFyX25hYykgdGllbmVuIG1heW9yIGltcGFjdG8gc29icmUgbGEgZGVycmFtYSBlY29uw7NtaWNhIHF1ZSBsYXMgZXh0cmFuamVyYXMgKG1vZGVsb192YXJfZXh0KQ0KDQoqIEFtYm9zIG1vZGVsb3MgbXVlc3RyYW4gZWZlY3RvcyBwb3NpdGl2b3MgY3VhbmRvIGF1bWVudGFuIHN1cyByZXNwZWN0aXZhcyBYLCBwZXJvIGVsIG1vZGVsbyBuYWNpb25hbCBlcyBtw6FzIHNlbnNpYmxlIGEgbG9zIGluY3JlbWVudG9zDQoNCiogRWwgbW9kZWxvIGRlIHR1cmlzdGFzIG5hY2lvbmFsZXMgbXVlc3RyYSBtYXlvciB2b2xhdGlsaWRhZCwgaW5jbHV5ZW5kbyB2YWxvcmVzIG5lZ2F0aXZvcyBlbiAyMDI1DQoNCmBgYHtyfQ0KIyBEZWZpbmlyIGluY3JlbWVudG9zIGEgc2ltdWxhcg0KaW5jcmVtZW50b3MgPC0gYygwLjI1LCAwLjUwLCAwLjc1KQ0KDQojIENyZWFyIHRhYmxhIGRlIGltcGFjdG9zIGRlc2RlIHRhYmxhX2NvZWYNCmltcGFjdG9zIDwtIGRhdGEuZnJhbWUoKQ0KDQpmb3IgKGkgaW4gMTpucm93KHRhYmxhX2NvZWYpKSB7DQogIHZhciA8LSB0YWJsYV9jb2VmJFZhcmlhYmxlW2ldDQogIGNvZWYgPC0gdGFibGFfY29lZiRDb2VmaWNpZW50ZVtpXQ0KICANCiAgZm9yIChpbmMgaW4gaW5jcmVtZW50b3MpIHsNCiAgICBlZmVjdG8gPC0gY29lZiAqIGluYw0KICAgIGltcGFjdG9zIDwtIHJiaW5kKGltcGFjdG9zLCBkYXRhLmZyYW1lKA0KICAgICAgVmFyaWFibGUgPSB2YXIsDQogICAgICBJbmNyZW1lbnRvID0gcGFzdGUwKCIrIiwgaW5jICogMTAwLCAiJSIpLA0KICAgICAgSW1wYWN0b19Qcm9ub3N0aWNhZG8gPSByb3VuZChlZmVjdG8sIDMpDQogICAgKSkNCiAgfQ0KfQ0KDQojIE9yZGVuYXIgcG9yIG1hZ25pdHVkIGRlbCBpbXBhY3RvDQppbXBhY3RvcyRJbXBhY3RvX0FicyA8LSBhYnMoaW1wYWN0b3MkSW1wYWN0b19Qcm9ub3N0aWNhZG8pDQppbXBhY3RvcyA8LSBpbXBhY3Rvc1tvcmRlcigtaW1wYWN0b3MkSW1wYWN0b19BYnMpLCBdDQppbXBhY3RvcyA8LSBzdWJzZXQoaW1wYWN0b3MsIHNlbGVjdCA9IC1JbXBhY3RvX0FicykNCg0KIyBNb3N0cmFyIHRhYmxhDQpwcmludChpbXBhY3RvcykNCg0Ka25pdHI6OmthYmxlKGltcGFjdG9zLCBjYXB0aW9uID0gIkltcGFjdG8gZXN0aW1hZG8gcG9yIGluY3JlbWVudG8gZW4gdmFyaWFibGVzIGV4cGxpY2F0aXZhcyIpDQpgYGANCg0KKipQcm9uw7NzdGljbyBCYXNlIC0gTGxlZ2FkYSBkZSBUdXJpc3RhcyBOYWNpb25hbGVzKioNCg0KKiBFbCBwcm9uw7NzdGljbyBkZSBsYSBkZXJyYW1hIGVjb27Ds21pY2EgbXVlc3RyYSB1bmEgZnVlcnRlIG9zY2lsYWNpw7NuIGFsIGluaWNpbyBkZWwgaG9yaXpvbnRlIGRlIHByZWRpY2Npw7NuICgyMDI1KSwgY29uIHZhbG9yZXMgcXVlIHN1YmVuIHkgYmFqYW4gcsOhcGlkYW1lbnRlLg0KDQoqIEVzdGEgdm9sYXRpbGlkYWQgaW5pY2lhbCBzZSBzdWF2aXphIHLDoXBpZGFtZW50ZSwgeSBhIHBhcnRpciBkZSBtZWRpYWRvcyBvIGZpbmFsZXMgZGUgMjAyNSwgbGEgc2VyaWUgZW50cmEgZW4gdW4gY29tcG9ydGFtaWVudG8gZXN0YWNpb25hcmlvIHkgcGxhbm8sIGNvbiB2YWxvcmVzIHF1ZSBmbHVjdMO6YW4gbGV2ZW1lbnRlIGFscmVkZWRvciBkZSB1biBuaXZlbCBjYXNpIGNvbnN0YW50ZS4NCg0KKiBFbCBoZWNobyBkZSBxdWUgbGEgbMOtbmVhIGF6dWwgKGJhc2UpIHRpZW5kYSBhIGVzdGFiaWxpemFyc2UgcsOhcGlkYW1lbnRlIGluZGljYSBxdWUgZWwgbW9kZWxvIGVzcGVyYSBxdWUsIHNpbiBzaG9ja3MgYWRpY2lvbmFsZXMgZW4gbGEgbGxlZ2FkYSBkZSB0dXJpc3RhcyBuYWNpb25hbGVzDQoNCmBgYHtyfQ0KIyBOw7ptZXJvIGRlIHBhc29zIGEgZnV0dXJvDQpuX2FoZWFkIDwtIDM2DQoNCnByZWRfbmFjIDwtIHByZWRpY3QobW9kZWxvX3Zhcl9uYWMsIG4uYWhlYWQgPSBuX2FoZWFkLCBjaSA9IDAuOTUpDQoNCiMgVmVyIHByZWRpY2Npw7NuIHBhcmEgbGEgdmFyaWFibGUgJ3knIChkZXJyYW1hIGVjb27Ds21pY2EpDQpwbG90KHByZWRfbmFjLCBuYW1lcyA9ICJ5IiwgbWFpbiA9ICJGb3JlY2FzdCBWQVIgLSBMbGVnYWRhX1R1cl9OYWNfZGlmZiIpDQoNCnByZWRfbmFjX3kgPC0gcHJlZF9uYWMkZmNzdCR5WywgImZjc3QiXQ0KYGBgDQoNCioqRXNjZW5hcmlvcyBkZSBJbmNyZW1lbnRvIGVuIHZhcmlhYmxlIC0gTGxlZ2FkYSBkZSBUdXJpc3RhcyBOYWNpb25hbGVzKCsyNSUsICs1MCUsICs3NSUpKioNCg0KKiBFbCBpbXBhY3RvIGRlIGF1bWVudGFyIGxhIGxsZWdhZGEgZGUgdHVyaXN0YXMgbmFjaW9uYWxlcyBzZSBub3RhIGNsYXJhbWVudGUgYWwgaW5pY2lvLCBkb25kZSBsYXMgbMOtbmVhcyBkZSBsb3MgZXNjZW5hcmlvcyAodmVyZGUsIG5hcmFuamEgeSByb2phKSBzZSBzZXBhcmFuIGRlbCBwcm9uw7NzdGljbyBiYXNlLg0KDQoqIEVzdGUgZWZlY3RvIHNlIGFtcGxpZmljYSBlbiBsb3MgcHJpbWVyb3MgbWVzZXMgKGVzcGVjaWFsbWVudGUgZW4gbG9zIHBpY29zIGRlIDIwMjUpLCBwZXJvIHBpZXJkZSBmdWVyemEgY29uZm9ybWUgYXZhbnphIGVsIHRpZW1wbywgbG8gcXVlIHN1Z2llcmUgcXVlIGVsIG1vZGVsbyBubyBwcm95ZWN0YSBlZmVjdG9zIGFjdW11bGF0aXZvcyBhIGxhcmdvIHBsYXpvIHBhcmEgZXN0YSB2YXJpYWJsZS4NCg0KKiBFbCBpbXBhY3RvIHJlc3VsdGEgc2VyIGRlY3JlY2llbnRlOiBhdW5xdWUgKzc1JSB0aWVuZSBtw6FzIGVmZWN0byBxdWUgKzI1JSwgbGEgZGlmZXJlbmNpYSBlbnRyZSBlc2NlbmFyaW9zIG5vIGNyZWNlIGRlIGZvcm1hIGV4cG9uZW5jaWFsLiBFc3RvIHN1Z2llcmUgcXVlIGxhIHZhcmlhYmxlIHRpZW5lIHVuIGVmZWN0bywgcGVybyBjb24gcmVuZGltaWVudG9zIGRlY3JlY2llbnRlcyBlbiBlbCBtb2RlbG8gVkFSLg0KDQpgYGB7cn0NCmxpYnJhcnkoZm9yZWNhc3QpDQpsaWJyYXJ5KHZhcnMpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoem9vKQ0KDQpmb3JlY2FzdF9iYXNlIDwtIHByZWRpY3QobW9kZWxvX3Zhcl9uYWMsIG4uYWhlYWQgPSAzNikNCg0KIyBFeHRyYWVtb3MgcHJlZGljY2lvbmVzIHBhcmEgeQ0KeV9mb3JlY2FzdF9iYXNlIDwtIGZvcmVjYXN0X2Jhc2UkZmNzdCR5WywgImZjc3QiXQ0KDQojIMOabHRpbW8gdmFsb3Igb2JzZXJ2YWRvIGRlIGxhIFgNCmxhc3RfeCA8LSB0YWlsKHZhcl9kYXRhX25hY1ssICJMbGVnYWRhX1R1cl9OYWNfZGlmZiJdLCAxKQ0KDQojIENyZWFyIDMgZXNjZW5hcmlvczogKzI1JSwgKzUwJSwgKzc1JQ0KZXNjZW5hcmlvc194IDwtIGxpc3QoDQogIGluYzI1ID0gcmVwKGxhc3RfeCAqIDEuMjUsIDM2KSwNCiAgaW5jNTAgPSByZXAobGFzdF94ICogMS41MCwgMzYpLA0KICBpbmM3NSA9IHJlcChsYXN0X3ggKiAxLjc1LCAzNikNCikNCg0KIyBGdW5jacOzbiBwYXJhIGhhY2VyIGZvcmVjYXN0IG1vZGlmaWNhbmRvIFgNCmZvcmVjYXN0X2Nvbl9lc2NlbmFyaW8gPC0gZnVuY3Rpb24oeF9lc2NlbmFyaW8pIHsNCiAgIyBDcmVhciBkYXRvcyBmaWN0aWNpb3MgZXh0ZW5kaWRvcw0KICB2YXJfZGF0YV9zaW0gPC0gdmFyX2RhdGFfbmFjDQogIGZvciAoaSBpbiAxOjM2KSB7DQogICAgbnVldmFfb2JzIDwtIG1hdHJpeChjKE5BLCB4X2VzY2VuYXJpb1tpXSksIG5yb3cgPSAxKQ0KICAgIGNvbG5hbWVzKG51ZXZhX29icykgPC0gY29sbmFtZXModmFyX2RhdGFfc2ltKQ0KICAgIHZhcl9kYXRhX3NpbSA8LSByYmluZCh2YXJfZGF0YV9zaW0sIG51ZXZhX29icykNCiAgfQ0KDQogICMgRm9yZWNhc3QgY29uIG51ZXZhIFgNCiAgZm9yZWNhc3QobW9kZWxvX3Zhcl9uYWMsIG4uYWhlYWQgPSAzNikNCn0NCg0KIyBObyBzZSBwdWVkZSBtb2RpZmljYXIgZGlyZWN0YW1lbnRlIGxhIFggY29uIFZBUiwgYXPDrSBxdWUgdXNhbW9zIGVsIG1pc21vIGZvcmVjYXN0DQojIHBlcm8gc2ltdWxhbW9zIGdyw6FmaWNhbWVudGUgZWwgaW1wYWN0byBlc3BlcmFkbw0KDQojIEZlY2hhcw0KZmVjaGFzX2hpc3QgPC0gYXMuRGF0ZShhcy55ZWFybW9uKHRpbWUoeSkpKQ0KZmVjaGFzX2ZvcmVjYXN0IDwtIHNlcSh0YWlsKGZlY2hhc19oaXN0LCAxKSArIDMwLCBieSA9ICJtb250aCIsIGxlbmd0aC5vdXQgPSAzNikNCg0KIyBCYXNlIGhpc3TDs3JpY2ENCmRmX2hpc3QgPC0gZGF0YS5mcmFtZSgNCiAgUGVyaW9kbyA9IGZlY2hhc19oaXN0LA0KICBWYWxvciA9IGFzLm51bWVyaWModmFyX2RhdGFfbmFjWywgInkiXSksDQogIEVzY2VuYXJpbyA9ICJIaXN0w7NyaWNvIg0KKQ0KDQojIEZvcmVjYXN0IGJhc2UNCmRmX2Jhc2UgPC0gZGF0YS5mcmFtZSgNCiAgUGVyaW9kbyA9IGZlY2hhc19mb3JlY2FzdCwNCiAgVmFsb3IgPSBhcy5udW1lcmljKHlfZm9yZWNhc3RfYmFzZSksDQogIEVzY2VuYXJpbyA9ICJQcm9uw7NzdGljbyBCYXNlIg0KKQ0KDQojIEZvcmVjYXN0IGNvbiBlc2NlbmFyaW9zIHNpbXVsYWRvcw0KZGZfMjUgPC0gZGZfYmFzZQ0KZGZfMjUkVmFsb3IgPC0gZGZfMjUkVmFsb3IgKiAxLjI1DQpkZl8yNSRFc2NlbmFyaW8gPC0gIkVzY2VuYXJpbyArMjUlIg0KDQpkZl81MCA8LSBkZl9iYXNlDQpkZl81MCRWYWxvciA8LSBkZl81MCRWYWxvciAqIDEuNTANCmRmXzUwJEVzY2VuYXJpbyA8LSAiRXNjZW5hcmlvICs1MCUiDQoNCmRmXzc1IDwtIGRmX2Jhc2UNCmRmXzc1JFZhbG9yIDwtIGRmXzc1JFZhbG9yICogMS43NQ0KZGZfNzUkRXNjZW5hcmlvIDwtICJFc2NlbmFyaW8gKzc1JSINCg0KIyBDb21iaW5hciANCmRmX3RvZG8gPC0gYmluZF9yb3dzKGRmX2hpc3QsIGRmX2Jhc2UsIGRmXzI1LCBkZl81MCwgZGZfNzUpDQoNCmdncGxvdChkZl90b2RvLCBhZXMoeCA9IFBlcmlvZG8sIHkgPSBWYWxvciwgY29sb3IgPSBFc2NlbmFyaW8sIGxpbmV0eXBlID0gRXNjZW5hcmlvKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoDQogICAgIkhpc3TDs3JpY28iID0gImJsYWNrIiwNCiAgICAiUHJvbsOzc3RpY28gQmFzZSIgPSAiYmx1ZSIsDQogICAgIkVzY2VuYXJpbyArMjUlIiA9ICJncmVlbiIsDQogICAgIkVzY2VuYXJpbyArNTAlIiA9ICJvcmFuZ2UiLA0KICAgICJFc2NlbmFyaW8gKzc1JSIgPSAicmVkIg0KICApKSArDQogIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXMgPSBjKA0KICAgICJIaXN0w7NyaWNvIiA9ICJzb2xpZCIsDQogICAgIlByb27Ds3N0aWNvIEJhc2UiID0gInNvbGlkIiwNCiAgICAiRXNjZW5hcmlvICsyNSUiID0gImRvdHRlZCIsDQogICAgIkVzY2VuYXJpbyArNTAlIiA9ICJkb3RkYXNoIiwNCiAgICAiRXNjZW5hcmlvICs3NSUiID0gInR3b2Rhc2giDQogICkpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJQcm9uw7NzdGljbyBkZSBsYSBEZXJyYW1hIEVjb27Ds21pY2EgY29uIERpZmVyZW50ZXMgRXNjZW5hcmlvcyIsDQogICAgc3VidGl0bGUgPSAiSW1wYWN0byBkZSBhdW1lbnRvcyBlbiBMbGVnYWRhX1R1cl9OYWNfZGlmZiAoMjUlLCA1MCUsIDc1JSkiLA0KICAgIHggPSAiRmVjaGEiLA0KICAgIHkgPSAiRGVycmFtYSBFY29uw7NtaWNhIChtZHApIiwNCiAgICBjb2xvciA9ICJFc2NlbmFyaW8iLA0KICAgIGxpbmV0eXBlID0gIkVzY2VuYXJpbyINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KYGBge3J9DQojIEZpbHRyYXIgc29sbyBsb3MgZXNjZW5hcmlvcyBkZSBwcm9uw7NzdGljbyAocXVpdGFyICJIaXN0w7NyaWNvIikNCmRmX2ZvcmVjYXN0X29ubHkgPC0gZGZfdG9kbyAlPiUNCiAgZmlsdGVyKEVzY2VuYXJpbyAhPSAiSGlzdMOzcmljbyIpDQoNCiMgR3JhZmljYXIgc29sbyBsb3MgZXNjZW5hcmlvcw0KZ2dwbG90KGRmX2ZvcmVjYXN0X29ubHksIGFlcyh4ID0gUGVyaW9kbywgeSA9IFZhbG9yLCBjb2xvciA9IEVzY2VuYXJpbywgbGluZXR5cGUgPSBFc2NlbmFyaW8pKSArDQogIGdlb21fbGluZShzaXplID0gMSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoDQogICAgIlByb27Ds3N0aWNvIEJhc2UiID0gImJsdWUiLA0KICAgICJFc2NlbmFyaW8gKzI1JSIgPSAiZ3JlZW4iLA0KICAgICJFc2NlbmFyaW8gKzUwJSIgPSAib3JhbmdlIiwNCiAgICAiRXNjZW5hcmlvICs3NSUiID0gInJlZCINCiAgKSkgKw0KICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYygNCiAgICAiUHJvbsOzc3RpY28gQmFzZSIgPSAic29saWQiLA0KICAgICJFc2NlbmFyaW8gKzI1JSIgPSAiZG90dGVkIiwNCiAgICAiRXNjZW5hcmlvICs1MCUiID0gImRvdGRhc2giLA0KICAgICJFc2NlbmFyaW8gKzc1JSIgPSAidHdvZGFzaCINCiAgKSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkNvbXBhcmF0aXZhIGRlIEVzY2VuYXJpb3MgZGUgUHJvbsOzc3RpY28iLA0KICAgIHN1YnRpdGxlID0gIkltcGFjdG8gZGUgYXVtZW50b3MgZW4gTGxlZ2FkYV9UdXJfTmFjX2RpZmYgKDI1JSwgNTAlLCA3NSUpIiwNCiAgICB4ID0gIk1lcyIsDQogICAgeSA9ICJEZXJyYW1hIEVjb27Ds21pY2EgZXN0aW1hZGEgKG1kcCkiLA0KICAgIGNvbG9yID0gIkVzY2VuYXJpbyIsDQogICAgbGluZXR5cGUgPSAiRXNjZW5hcmlvIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQoNCg0KDQojIyMjIFByb27Ds3N0aWNvIEltcGFjdG8gZGUgTGxlZ2FkYSBUdXJpc3RhcyBFeHRyYW5qZXJvcyAmIE5hY2lvbmFsZXMgZW4gRGVycmFtYQ0KDQpTZSBwcmVzZW50YW4gZXNjZW5hcmlvcyBkZSBkZXJyYW1hIGVjb27Ds21pY2EgZXN0aW1hZGEgZGVzZGUganVuaW8gZGUgMjAyNiwgY29tcGFyYW5kbyB1biB2YWxvciBiYXNlIChkZW5vbWluYWRvICJSZWFsIikgY29uIHRyZXMgZXNjZW5hcmlvcyBkZSBpbmNyZW1lbnRvOiAyNSUsIDUwJSB5IDc1JSwgY29uIHBlcmlvZGljaWRhZCBtZW5zdWFsIGhhc3RhIGp1bmlvIGRlIDIwMjcuDQogDQoqKkltcGFjdG9zIGVuIFR1cmlzdGFzIE5hY2lvbmFsZXMqKg0KIA0KKiBKdWxpbyAyMDI2OiBQaWNvIHNpZ25pZmljYXRpdm8gZW4gZGVycmFtYSBlY29uw7NtaWNhIHBhcmEgdG9kb3MgbG9zIGVzY2VuYXJpb3MsIGNvbiBlbCAiUmVhbCIgc2llbmRvIGVsIG3DoXMgYWx0by4NCg0KKiBBZ29zdG8tc2VwdGllbWJyZSAyMDI2OiBDYcOtZGEgYWJydXB0YSBkZSBsYSBkZXJyYW1hOyBlbCAiUmVhbCIgdnVlbHZlIGEgZGlmZXJlbmNpYXJzZSBtYXJjYWRhbWVudGUgaGFjaWEgYWJham8uDQoNCiogT2N0dWJyZSAyMDI2IHkgZW5lcm8gMjAyNzogUmVjdXBlcmFjaW9uZXMgbm90b3JpYXMsIGNvbiBlbCAiUmVhbCIgcG9yIGVuY2ltYSBkZSBsb3MgZXNjZW5hcmlvcyBkZSBhdW1lbnRvLg0KDQoqIE1hcnpvLWFicmlsIDIwMjc6IFNlIHBlcmNpYmUgdW5hIHJldmVyc2nDs246IGVsIGVzY2VuYXJpbyAiUmVhbCIgY2FlIG1pZW50cmFzIGxvcyBhdW1lbnRvcyBzZSBtYW50aWVuZW4gcmVsYXRpdmFtZW50ZSBlc3RhYmxlcy4NCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHlyKQ0KDQpkZl90YWJsYV9sb25nIDwtIHBpdm90X2xvbmdlcigNCiAgZGZfdGFibGEsDQogIGNvbHMgPSAtRmVjaGEsDQogIG5hbWVzX3RvID0gIkVzY2VuYXJpbyIsDQogIHZhbHVlc190byA9ICJEZXJyYW1hIg0KKQ0KDQpnZ3Bsb3QoZGZfdGFibGFfbG9uZywgYWVzKHggPSBGZWNoYSwgeSA9IERlcnJhbWEsIGNvbG9yID0gRXNjZW5hcmlvKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKw0KICBsYWJzKHRpdGxlID0gIkVzY2VuYXJpb3MgZGUgZGVycmFtYSBlY29uw7NtaWNhIHByb25vc3RpY2FkYSBjb24gSW5jcmVtZW50byDDum5pY28gZW4gVHVyaXN0YXMgTmFjaW9uYWxlcyIsDQogICAgICAgc3VidGl0bGUgPSAiRGVzZGUganVuaW8gMjAyNjogYmFzZSArIGF1bWVudG8gZGVsIDI1JSwgNTAlIHkgNzUlIiwNCiAgICAgICB4ID0gIkZlY2hhIiwgeSA9ICJEZXJyYW1hIEVzdGltYWRhIChkaWZmKSIpICsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfbGFiZWxzID0gIiViLSVZIiwgZGF0ZV9icmVha3MgPSAiMSBtb250aCIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCg0KYGBgDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmRhdG9zX3RzX3R1cmlzdGFzZXh0ICA8LSBjYmluZChkZXJyYW1hX3RzX3ZhciwgdHVyZXh0X3RzX3ZhcikgDQpWQVJzZWxlY3QoZGF0b3NfdHNfdHVyaXN0YXNleHQsIGxhZy5tYXggPSAxMiwgdHlwZSA9ICJjb25zdCIpDQoNCnZhcl9maW5hbF9leHQgPC0gVkFSKGRhdG9zX3RzX3R1cmlzdGFzZXh0LCBwID0gMiwgdHlwZSA9ICJjb25zdCIpDQoNCnNlcmlhbC50ZXN0KHZhcl9maW5hbF9leHQsIGxhZ3MucHQgPSAxMiwgdHlwZSA9ICJQVC5hc3ltcHRvdGljIikNCg0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpjb2VmcyA8LSBjb2VmKHZhcl9maW5hbF9leHQkdmFycmVzdWx0JGRlcnJhbWFfdHNfdmFyKQ0KYmV0YV90b3RhbF9leHQgPC0gY29lZnNbInR1cmV4dF90c192YXIubDEiXSArIGNvZWZzWyJ0dXJleHRfdHNfdmFyLmwyIl0NCg0KZGZfaW1wYWN0b19leHQgPC0gZGZfcHJvbm9zdGljb3NbZGZfcHJvbm9zdGljb3MkRmVjaGEgPj0gYXMuRGF0ZSgiMjAyNi0wNi0wMSIpLCBdDQoNCmRmX2ltcGFjdG9fZXh0JERlbHRhXzI1IDwtIGRmX2ltcGFjdG9fZXh0JExsZWdhZGFfVHVyX0V4dF9kaWZmICogMS4yNQ0KZGZfaW1wYWN0b19leHQkRGVsdGFfNTAgPC0gZGZfaW1wYWN0b19leHQkTGxlZ2FkYV9UdXJfRXh0X2RpZmYgKiAxLjUwDQpkZl9pbXBhY3RvX2V4dCREZWx0YV83NSA8LSBkZl9pbXBhY3RvX2V4dCRMbGVnYWRhX1R1cl9FeHRfZGlmZiAqIDEuNzUNCg0KIyAgQXBsaWNhciBiZXRhIGRlbCBtb2RlbG8gVkFSDQpkZl9pbXBhY3RvX2V4dCREZXJyYW1hXzI1IDwtIGRmX2ltcGFjdG9fZXh0JERlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHBfZGlmZiArIChkZl9pbXBhY3RvX2V4dCREZWx0YV8yNSAqIGJldGFfdG90YWxfZXh0KQ0KZGZfaW1wYWN0b19leHQkRGVycmFtYV81MCA8LSBkZl9pbXBhY3RvX2V4dCREZXJyYW1hX0Vjb25vbWljYV9Fc3RfbWRwX2RpZmYgKyAoZGZfaW1wYWN0b19leHQkRGVsdGFfNTAgKiBiZXRhX3RvdGFsX2V4dCkNCmRmX2ltcGFjdG9fZXh0JERlcnJhbWFfNzUgPC0gZGZfaW1wYWN0b19leHQkRGVycmFtYV9FY29ub21pY2FfRXN0X21kcF9kaWZmICsgKGRmX2ltcGFjdG9fZXh0JERlbHRhXzc1ICogYmV0YV90b3RhbF9leHQpDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkodGlkeXIpDQoNCiMgRm9ybWF0byBsYXJnbyBkZSBlc2NlbmFyaW9zDQpkZl9wbG90X2V4dCA8LSBkZl9pbXBhY3RvX2V4dFssIGMoIkZlY2hhIiwgIkRlcnJhbWFfMjUiLCAiRGVycmFtYV81MCIsICJEZXJyYW1hXzc1IildDQoNCmRmX3Bsb3RfbG9uZ19leHQgPC0gcGl2b3RfbG9uZ2VyKA0KICBkZl9wbG90X2V4dCwNCiAgY29scyA9IC1GZWNoYSwNCiAgbmFtZXNfdG8gPSAiRXNjZW5hcmlvIiwNCiAgdmFsdWVzX3RvID0gIkRlcnJhbWEiDQopDQoNCiMgU2VyaWUgYmFzZSBjb21wbGV0YQ0KZGZfYmFzZV9wbG90X2V4dCA8LSBkZl9wcm9ub3N0aWNvc1ssIGMoIkZlY2hhIiwgIkRlcnJhbWFfRWNvbm9taWNhX0VzdF9tZHBfZGlmZiIpXQ0KY29sbmFtZXMoZGZfYmFzZV9wbG90X2V4dClbMl0gPC0gIkRlcnJhbWEiDQpkZl9iYXNlX3Bsb3RfZXh0JEVzY2VuYXJpbyA8LSAiU2VyaWVfT3JpZ2luYWwiDQoNCiMgQ29tYmluYXIgYmFzZSArIGVzY2VuYXJpb3MNCmRmX2NvbXBsZXRvX2V4dCA8LSByYmluZChkZl9wbG90X2xvbmdfZXh0LCBkZl9iYXNlX3Bsb3RfZXh0KQ0KDQpgYGANCg0KDQoqKlByb27Ds3N0aWNvIEJhc2UgLSBMbGVnYWRhIGRlIFR1cmlzdGFzIEV4dHJhbmplcm9zKioNCg0KKiBFbCBtb2RlbG8gVkFSIHByb3llY3RhIHF1ZSwgYmFqbyBjb25kaWNpb25lcyBhY3R1YWxlcywgbGEgZGVycmFtYSBlY29uw7NtaWNhIG1lbnN1YWwgc2UgZXN0YWJpbGl6YSByw6FwaWRhbWVudGUgdHJhcyB1bmEgb3NjaWxhY2nDs24gaW5pY2lhbC4NCg0KKiBMYSBtYWduaXR1ZCBkZWwgaW1wYWN0byBtZW5zdWFsIHRpZW5kZSBhIHNlciBiYWphIHkgY29uc3RhbnRlIGEgcGFydGlyIGRlbCBzZWd1bmRvIHNlbWVzdHJlIGRlIDIwMjUuDQoNCiogU2Ugb2JzZXJ2YSB1biBlZmVjdG8gaW5tZWRpYXRvIHRyYXMgZWwgaG9yaXpvbnRlIGRlIHByZWRpY2Npw7NuLCBwZXJvIGx1ZWdvIGxhIHNlcmllIGNvbnZlcmdlIHLDoXBpZGFtZW50ZS4NCg0KYGBge3J9DQojIE7Dum1lcm8gZGUgcGFzb3MgYSBmdXR1cm8NCnByZWRfZXh0IDwtIHByZWRpY3QobW9kZWxvX3Zhcl9leHQsIG4uYWhlYWQgPSBuX2FoZWFkLCBjaSA9IDAuOTUpDQoNCnBsb3QocHJlZF9leHQsIG5hbWVzID0gInkiLCBtYWluID0gIkZvcmVjYXN0IFZBUiAtIExsZWdhZGFfVHVyX0V4dF9kaWZmIikNCnByZWRfbmFjX3kgPC0gcHJlZF9leHQkZmNzdCR5WywgImZjc3QiXQ0KYGBgDQoNCioqRXNjZW5hcmlvcyBkZSBJbmNyZW1lbnRvIGVuIHZhcmlhYmxlIC0gTGxlZ2FkYSBkZSBUdXJpc3RhcyBOYWNpb25hbGVzKCsyNSUsICs1MCUsICs3NSUpKioNCg0KKiArMjUlOiBJbmNyZW1lbnRvIG1vZGVyYWRvLCBnZW5lcmEgdW4gaW1wYWN0byBlY29uw7NtaWNvIGxpZ2VyYW1lbnRlIHN1cGVyaW9yIGFsIGVzY2VuYXJpbyBiYXNlLiBTZSBtYW50aWVuZSBlc3RhYmxlIGEgcGFydGlyIGRlIDIwMjYuDQoNCiogKzUwJTogTGEgZGVycmFtYSBwcm95ZWN0YWRhIGVzIG1heW9yLCBjb24gdW4gZWZlY3RvIHZpc2libGUgcHJpbmNpcGFsbWVudGUgZHVyYW50ZSBsb3MgcHJpbWVyb3MgNuKAkzggbWVzZXMuIEVsIGNyZWNpbWllbnRvIHNlIGF0ZW7DumEgcG9zdGVyaW9ybWVudGUuDQoNCiogKzc1JTogR2VuZXJhIGVsIG1heW9yIGltcGFjdG8gaW5pY2lhbCwgYWxjYW56YW5kbyB2YWxvcmVzIGNlcmNhbm9zIGEgbG9zIDMwMCBtZHAgZW4gbG9zIHByaW1lcm9zIG1lc2VzIGRlbCBwcm9uw7NzdGljby4gQSBwYXJ0aXIgZGVsIHNlZ3VuZG8gYcOxbywgbGEgYnJlY2hhIGNvbiBsb3Mgb3Ryb3MgZXNjZW5hcmlvcyBzZSByZWR1Y2UuDQoNCmBgYHtyfQ0KZm9yZWNhc3RfYmFzZV9leHQgPC0gcHJlZGljdChtb2RlbG9fdmFyX2V4dCwgbi5haGVhZCA9IDM2KQ0KDQojIEV4dHJhZW1vcyBwcmVkaWNjaW9uZXMgcGFyYSB5IChkZXJyYW1hIGVjb27Ds21pY2EpDQp5X2ZvcmVjYXN0X2Jhc2VfZXh0IDwtIGZvcmVjYXN0X2Jhc2VfZXh0JGZjc3QkeVssICJmY3N0Il0NCg0KIyDDmmx0aW1vIHZhbG9yIG9ic2VydmFkbyBkZSBsYSBYDQpsYXN0X3hfZXh0IDwtIHRhaWwodmFyX2RhdGFfZXh0WywgIkxsZWdhZGFfVHVyX0V4dF9kaWZmIl0sIDEpDQoNCiMgQ3JlYXIgMyBlc2NlbmFyaW9zOiArMjUlLCArNTAlLCArNzUlDQplc2NlbmFyaW9zX3hfZXh0IDwtIGxpc3QoDQogIGluYzI1ID0gcmVwKGxhc3RfeF9leHQgKiAxLjI1LCAzNiksDQogIGluYzUwID0gcmVwKGxhc3RfeF9leHQgKiAxLjUwLCAzNiksDQogIGluYzc1ID0gcmVwKGxhc3RfeF9leHQgKiAxLjc1LCAzNikNCikNCg0KIyBFbiBWQVIgbm8gc2UgcHVlZGUgbW9kaWZpY2FyIGRpcmVjdGFtZW50ZSBYLCBzb2xvIHNlIHNpbXVsYSBlbCBlZmVjdG8gdmlzdWFsbWVudGUpDQoNCiMgRmVjaGFzDQpmZWNoYXNfaGlzdF9leHQgPC0gYXMuRGF0ZShhcy55ZWFybW9uKHRpbWUoeSkpKQ0KZmVjaGFzX2ZvcmVjYXN0X2V4dCA8LSBzZXEodGFpbChmZWNoYXNfaGlzdF9leHQsIDEpICsgMzAsIGJ5ID0gIm1vbnRoIiwgbGVuZ3RoLm91dCA9IDM2KQ0KDQojIEJhc2UgaGlzdMOzcmljYQ0KZGZfaGlzdF9leHQgPC0gZGF0YS5mcmFtZSgNCiAgUGVyaW9kbyA9IGZlY2hhc19oaXN0X2V4dCwNCiAgVmFsb3IgPSBhcy5udW1lcmljKHZhcl9kYXRhX2V4dFssICJ5Il0pLA0KICBFc2NlbmFyaW8gPSAiSGlzdMOzcmljbyINCikNCg0KIyBGb3JlY2FzdCBiYXNlDQpkZl9iYXNlX2V4dCA8LSBkYXRhLmZyYW1lKA0KICBQZXJpb2RvID0gZmVjaGFzX2ZvcmVjYXN0X2V4dCwNCiAgVmFsb3IgPSBhcy5udW1lcmljKHlfZm9yZWNhc3RfYmFzZV9leHQpLA0KICBFc2NlbmFyaW8gPSAiUHJvbsOzc3RpY28gQmFzZSINCikNCg0KIyBGb3JlY2FzdCBjb24gZXNjZW5hcmlvcyBzaW11bGFkb3MNCmRmXzI1X2V4dCA8LSBkZl9iYXNlX2V4dA0KZGZfMjVfZXh0JFZhbG9yIDwtIGRmXzI1X2V4dCRWYWxvciAqIDEuMjUNCmRmXzI1X2V4dCRFc2NlbmFyaW8gPC0gIkVzY2VuYXJpbyArMjUlIg0KDQpkZl81MF9leHQgPC0gZGZfYmFzZV9leHQNCmRmXzUwX2V4dCRWYWxvciA8LSBkZl81MF9leHQkVmFsb3IgKiAxLjUwDQpkZl81MF9leHQkRXNjZW5hcmlvIDwtICJFc2NlbmFyaW8gKzUwJSINCg0KZGZfNzVfZXh0IDwtIGRmX2Jhc2VfZXh0DQpkZl83NV9leHQkVmFsb3IgPC0gZGZfNzVfZXh0JFZhbG9yICogMS43NQ0KZGZfNzVfZXh0JEVzY2VuYXJpbyA8LSAiRXNjZW5hcmlvICs3NSUiDQoNCmRmX3RvZG9fZXh0IDwtIGJpbmRfcm93cyhkZl9oaXN0X2V4dCwgZGZfYmFzZV9leHQsIGRmXzI1X2V4dCwgZGZfNTBfZXh0LCBkZl83NV9leHQpDQoNCmdncGxvdChkZl90b2RvX2V4dCwgYWVzKHggPSBQZXJpb2RvLCB5ID0gVmFsb3IsIGNvbG9yID0gRXNjZW5hcmlvLCBsaW5ldHlwZSA9IEVzY2VuYXJpbykpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKA0KICAgICJIaXN0w7NyaWNvIiA9ICJibGFjayIsDQogICAgIlByb27Ds3N0aWNvIEJhc2UiID0gImJsdWUiLA0KICAgICJFc2NlbmFyaW8gKzI1JSIgPSAiZ3JlZW4iLA0KICAgICJFc2NlbmFyaW8gKzUwJSIgPSAib3JhbmdlIiwNCiAgICAiRXNjZW5hcmlvICs3NSUiID0gInJlZCINCiAgKSkgKw0KICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYygNCiAgICAiSGlzdMOzcmljbyIgPSAic29saWQiLA0KICAgICJQcm9uw7NzdGljbyBCYXNlIiA9ICJzb2xpZCIsDQogICAgIkVzY2VuYXJpbyArMjUlIiA9ICJkb3R0ZWQiLA0KICAgICJFc2NlbmFyaW8gKzUwJSIgPSAiZG90ZGFzaCIsDQogICAgIkVzY2VuYXJpbyArNzUlIiA9ICJ0d29kYXNoIg0KICApKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUHJvbsOzc3RpY28gZGUgbGEgRGVycmFtYSBFY29uw7NtaWNhIGNvbiBEaWZlcmVudGVzIEVzY2VuYXJpb3MiLA0KICAgIHN1YnRpdGxlID0gIkltcGFjdG8gZGUgYXVtZW50b3MgZW4gTGxlZ2FkYV9UdXJfRXh0X2RpZmYgKDI1JSwgNTAlLCA3NSUpIiwNCiAgICB4ID0gIkZlY2hhIiwNCiAgICB5ID0gIkRlcnJhbWEgRWNvbsOzbWljYSAobWRwKSIsDQogICAgY29sb3IgPSAiRXNjZW5hcmlvIiwNCiAgICBsaW5ldHlwZSA9ICJFc2NlbmFyaW8iDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KDQpgYGANCg0KYGBge3J9DQojIEZpbHRyYXIgc29sbyBsb3MgZXNjZW5hcmlvcyBkZSBwcm9uw7NzdGljbyAocXVpdGFyICJIaXN0w7NyaWNvIikNCmRmX2ZvcmVjYXN0X29ubHlfZXh0IDwtIGRmX3RvZG9fZXh0ICU+JQ0KICBmaWx0ZXIoRXNjZW5hcmlvICE9ICJIaXN0w7NyaWNvIikNCg0KIyBHcmFmaWNhciBzb2xvIGxvcyBlc2NlbmFyaW9zDQpnZ3Bsb3QoZGZfZm9yZWNhc3Rfb25seV9leHQsIGFlcyh4ID0gUGVyaW9kbywgeSA9IFZhbG9yLCBjb2xvciA9IEVzY2VuYXJpbywgbGluZXR5cGUgPSBFc2NlbmFyaW8pKSArDQogIGdlb21fbGluZShzaXplID0gMSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoDQogICAgIlByb27Ds3N0aWNvIEJhc2UiID0gImJsdWUiLA0KICAgICJFc2NlbmFyaW8gKzI1JSIgPSAiZ3JlZW4iLA0KICAgICJFc2NlbmFyaW8gKzUwJSIgPSAib3JhbmdlIiwNCiAgICAiRXNjZW5hcmlvICs3NSUiID0gInJlZCINCiAgKSkgKw0KICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYygNCiAgICAiUHJvbsOzc3RpY28gQmFzZSIgPSAic29saWQiLA0KICAgICJFc2NlbmFyaW8gKzI1JSIgPSAiZG90dGVkIiwNCiAgICAiRXNjZW5hcmlvICs1MCUiID0gImRvdGRhc2giLA0KICAgICJFc2NlbmFyaW8gKzc1JSIgPSAidHdvZGFzaCINCiAgKSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkNvbXBhcmF0aXZhIGRlIEVzY2VuYXJpb3MgZGUgUHJvbsOzc3RpY28iLA0KICAgIHN1YnRpdGxlID0gIkltcGFjdG8gZGUgYXVtZW50b3MgZW4gTGxlZ2FkYV9UdXJfRXh0X2RpZmYgKDI1JSwgNTAlLCA3NSUpIHNpbiBpbmNsdWlyIGxhIHNlcmllIGhpc3TDs3JpY2EiLA0KICAgIHggPSAiTWVzIiwNCiAgICB5ID0gIkRlcnJhbWEgRWNvbsOzbWljYSBlc3RpbWFkYSAobWRwKSIsDQogICAgY29sb3IgPSAiRXNjZW5hcmlvIiwNCiAgICBsaW5ldHlwZSA9ICJFc2NlbmFyaW8iDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCg0KKipJbXBhY3RvcyBlbiBUdXJpc3RhcyBFeHRyYW5qZXJvcyoqDQoNCiogQWx0YSBlc3RhYmlsaWRhZDogQSBwYXJ0aXIgZGUganVuaW8gZGUgMjAyNiwgdG9kb3MgbG9zIGVzY2VuYXJpb3MgY3JlY2VuIHkgZGVjcmVjZW4gZGUgbWFuZXJhIGNhc2kgaWTDqW50aWNhLg0KDQoqIFBpY29zIHkgdmFsbGVzIGNvaW5jaWRlbnRlczogVG9kb3MgbG9zIG3DoXhpbW9zIHkgbcOtbmltb3MgKGp1bGlvLCBvY3R1YnJlLCBlbmVybykgc29uIGNvbXBhcnRpZG9zIGVudHJlIGVzY2VuYXJpb3MuDQoNCiogRXNjYXNhIGRpZmVyZW5jaWFjacOzbjogTm8gaGF5IHVuYSB2ZW50YWphIGNsYXJhIGVuIGxvcyBlc2NlbmFyaW9zIGNvbiBhdW1lbnRvIGZyZW50ZSBhbCAiUmVhbCIsIGxvIHF1ZSBwb2Ryw61hIHJlZmxlamFyIHN1cHVlc3RvcyBjb25zZXJ2YWRvcmVzIG8gZmFsdGEgZGUgc2Vuc2liaWxpZGFkIGVuIGVsIG1vZGVsby4NCg0KYGBge3J9DQpnZ3Bsb3QoZGZfY29tcGxldG9fZXh0LCBhZXMoeCA9IEZlY2hhLCB5ID0gRGVycmFtYSwgY29sb3IgPSBFc2NlbmFyaW8pKSArDQogIGdlb21fbGluZShzaXplID0gMSkgKw0KICBsYWJzKHRpdGxlID0gIlNlcmllIGhpc3TDs3JpY2EgKyBlc2NlbmFyaW9zIGRlIGltcGFjdG8iLA0KICAgICAgIHN1YnRpdGxlID0gIkltcGFjdG8gZGUgYXVtZW50byBkZSB0dXJpc3RhcyBleHRyYW5qZXJvcyBkZXNkZSBqdW5pbyAyMDI2IiwNCiAgICAgICB4ID0gIkZlY2hhIiwgeSA9ICJEZXJyYW1hIEVzdGltYWRhIChkaWZmKSIpICsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfbGFiZWxzID0gIiViLSVZIiwgZGF0ZV9icmVha3MgPSAiMyBtb250aHMiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KYGBge3J9DQpkZl90YWJsYV9leHQgPC0gZGZfaW1wYWN0b19leHRbLCBjKA0KICAiRmVjaGEiLA0KICAiRGVycmFtYV9FY29ub21pY2FfRXN0X21kcF9kaWZmIiwgICMgUHJvbsOzc3RpY28gYmFzZQ0KICAiRGVycmFtYV8yNSIsDQogICJEZXJyYW1hXzUwIiwNCiAgIkRlcnJhbWFfNzUiDQopXQ0KDQpjb2xuYW1lcyhkZl90YWJsYV9leHQpIDwtIGMoIkZlY2hhIiwgIlJlYWwiLCAiQXVtZW50b18yNSIsICJBdW1lbnRvXzUwIiwgIkF1bWVudG9fNzUiKQ0KZGZfdGFibGFfZXh0DQpgYGANCg0KDQpgYGB7cn0NCmRmX3RhYmxhX2xvbmdfZXh0IDwtIHBpdm90X2xvbmdlcigNCiAgZGZfdGFibGFfZXh0LA0KICBjb2xzID0gLUZlY2hhLA0KICBuYW1lc190byA9ICJFc2NlbmFyaW8iLA0KICB2YWx1ZXNfdG8gPSAiRGVycmFtYSINCikNCg0KZ2dwbG90KGRmX3RhYmxhX2xvbmdfZXh0LCBhZXMoeCA9IEZlY2hhLCB5ID0gRGVycmFtYSwgY29sb3IgPSBFc2NlbmFyaW8pKSArDQogIGdlb21fbGluZShzaXplID0gMS4yKSArDQogIGxhYnModGl0bGUgPSAiRXNjZW5hcmlvcyBkZSBkZXJyYW1hIGVjb27Ds21pY2EgcHJvbm9zdGljYWRhIGNvbiBJbmNyZW1lbnRvIMO6bmljbyBlbiBUdXJpc3RhcyBFeHRyYW5qZXJvcyIsDQogICAgICAgc3VidGl0bGUgPSAiRGVzZGUganVuaW8gMjAyNjogYmFzZSArIGF1bWVudG8gZGVsIDI1JSwgNTAlIHkgNzUlIiwNCiAgICAgICB4ID0gIkZlY2hhIiwgeSA9ICJEZXJyYW1hIEVzdGltYWRhIChkaWZmKSIpICsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfbGFiZWxzID0gIiViLSVZIiwgZGF0ZV9icmVha3MgPSAiMSBtb250aCIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQoNCioqQ29tcG9ydGFtaWVudG8gZ2VuZXJhbCAsIGNvbXBhcmFuZG8gbGxlZ2FkYSBkZSBUdXJpc3RhcyBOYWNpb25hbGVzIHZzIFR1cmlzdGFzIEV4dHJhbmplcm9zKioNCg0KQW1iYXMgdmFyaWFibGVzIG11ZXN0cmFuIHVuIHNhbHRvIGltcG9ydGFudGUgYWwgaW5pY2lvIGRlIDIwMjUsIHBlcm8gZWwgaW1wYWN0byBkZWwgdHVyaXNtbyBuYWNpb25hbCAoTmFjKSBzZSBtdWVzdHJhIG3DoXMgdm9sw6F0aWwgeSBzZW5zaWJsZSwgbWllbnRyYXMgcXVlIGVsIGRlIHR1cmlzdGFzIGV4dHJhbmplcm9zIChFeHQpIGVzIG3DoXMgZXN0YWJsZSB5IHNvc3Rlbmlkby4NCg0KKipJbXBhY3RvIG1lbnN1YWwgcHJveWVjdGFkbyoqDQoNCkVuIGVsIHByaW1lciB0cmltZXN0cmUgZGUgMjAyNToNCg0KKiBVbiBpbmNyZW1lbnRvIGRlbCArNzUlIGVuIHR1cmlzdGFzIGV4dHJhbmplcm9zIGdlbmVyYXLDrWEgcGljb3MgbWVuc3VhbGVzIGNlcmNhbm9zIGEgKzMxNSBtZHAgKGVuZSAyMDI1KS4NCg0KKiBFbCBtaXNtbyBpbmNyZW1lbnRvIGVuIHR1cmlzdGFzIG5hY2lvbmFsZXMgYWxjYW56YSB1biBlc3RpbWFkbyBpbmNsdXNvIG1heW9yLCBoYXN0YSArMzY1IG1kcCwgcGVybyBjb24gY2HDrWRhcyBhYnJ1cHRhcyBlbiBsb3MgbWVzZXMgc2lndWllbnRlcyAobWFyem86IOKAkzI5MSBtZHApLg0KDQpBIHBhcnRpciBkZSBtYXlvIDIwMjU6DQoNCiogRWwgaW1wYWN0byBkZWwgdHVyaXNtbyBleHRyYW5qZXJvIHNlIGVzdGFiaWxpemEgcsOhcGlkYW1lbnRlIGVuIHRvcm5vIGEgfjIyIG1kcC9tZXMsIG1pZW50cmFzIHF1ZSBlbCB0dXJpc21vIG5hY2lvbmFsIGZsdWN0w7phIG3DoXMsIGNvbiB2YWxvcmVzIGRlIH4xNeKAkzE4IG1kcC9tZXMuDQoNCkVsIG1vZGVsbyBjb24gTGxlZ2FkYV9UdXJfRXh0X2RpZmYgcHJlc2VudGEgbWF5b3IgZXN0YWJpbGlkYWQgeSBjb25zaXN0ZW5jaWEgYSBsbyBsYXJnbyBkZSBsb3MgbWVzZXMuIEF1bnF1ZSBzdXMgcGljb3Mgbm8gc29uIHRhbiBhbHRvcyBjb21vIGxvcyBkZWwgbW9kZWxvIG5hY2lvbmFsLCBzdSBpbXBhY3RvIG1lbnN1YWwgZXMgbcOhcyBwcmVkZWNpYmxlLiBFbCBtb2RlbG8gY29uIExsZWdhZGFfVHVyX05hY19kaWZmIG11ZXN0cmEgYWx0YSBzZW5zaWJpbGlkYWQgYSBjYW1iaW9zIGFicnVwdG9zLiBTaSBiaWVuIHB1ZWRlIGdlbmVyYXIgbWF5b3IgZGVycmFtYSBlbiBtZXNlcyBwdW50dWFsZXMsIHRhbWJpw6luIGNvbmxsZXZhIG3DoXMgaW5jZXJ0aWR1bWJyZSB5IHJpZXNnbyBkZSBjYcOtZGFzLiBFbiB0w6lybWlub3MgZGUgcGxhbmlmaWNhY2nDs24sIGFwb3N0YXIgcG9yIGNhbXBhw7FhcyBxdWUgZm9ydGFsZXpjYW4gZWwgdHVyaXNtbyBleHRyYW5qZXJvIHBvZHLDrWEgb2ZyZWNlciByZXRvcm5vcyBlY29uw7NtaWNvcyBtw6FzIHNvc3RlbmlibGVzLCBtaWVudHJhcyBxdWUgZWwgdHVyaXNtbyBuYWNpb25hbCB0ZW5kcsOtYSBxdWUgZXN0YXIgbcOhcyBjdWlkYWRvc2FtZW50ZSBtb25pdG9yZWFkbyB5IGRpdmVyc2lmaWNhZG8gcGFyYSBtaXRpZ2FyIHBvc2libGVzIHJldHJvY2Vzb3MuDQoNCmBgYHtyfQ0KIyBDcmVhciB0YWJsYSB1bmlmaWNhZGEgcGFyYSBjb21wYXJhciBhbWJvcyBtb2RlbG9zIFZBUg0KdGFibGFfY29tcGFyYXRpdmEgPC0gZGF0YS5mcmFtZSgNCiAgRmVjaGEgICAgICAgID0gZmVjaGFzX2ZvcmVjYXN0LCAgIyBZYSBjcmVhZGEgZW4gdHUgY8OzZGlnbw0KICBCYXNlX05hYyAgICAgPSB5X2ZvcmVjYXN0X2Jhc2UsDQogIEJhc2VfRXh0ICAgICA9IHlfZm9yZWNhc3RfYmFzZV9leHQsDQogIEVzY18yNV9OYWMgICA9IHlfZm9yZWNhc3RfYmFzZSAqIDEuMjUsDQogIEVzY18yNV9FeHQgICA9IHlfZm9yZWNhc3RfYmFzZV9leHQgKiAxLjI1LA0KICBFc2NfNTBfTmFjICAgPSB5X2ZvcmVjYXN0X2Jhc2UgKiAxLjUwLA0KICBFc2NfNTBfRXh0ICAgPSB5X2ZvcmVjYXN0X2Jhc2VfZXh0ICogMS41MCwNCiAgRXNjXzc1X05hYyAgID0geV9mb3JlY2FzdF9iYXNlICogMS43NSwNCiAgRXNjXzc1X0V4dCAgID0geV9mb3JlY2FzdF9iYXNlX2V4dCAqIDEuNzUNCikNCg0KdGFibGFfY29tcGFyYXRpdmFfcmVkb25kZWFkYSA8LSB0YWJsYV9jb21wYXJhdGl2YSAlPiUNCiAgZHBseXI6Om11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIHJvdW5kLCAxKSkNCg0KcHJpbnQodGliYmxlOjphc190aWJibGUodGFibGFfY29tcGFyYXRpdmFfcmVkb25kZWFkYSksIG4gPSAzNikNCmBgYA0KDQpgYGB7cn0NCiNQcm9tZWRpbyBkZSBhdW1lbnRvcyBwb3IgbWVzIGVuIGNhZGEgYcOxby4NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCg0KdGFibGFfcG9yX2HDsW8gPC0gdGFibGFfY29tcGFyYXRpdmEgJT4lDQogIG11dGF0ZShBw7FvID0geWVhcihGZWNoYSkpICU+JQ0KICBncm91cF9ieShBw7FvKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIEJhc2VfTmFjICAgPSByb3VuZChtZWFuKEJhc2VfTmFjLCBuYS5ybSA9IFRSVUUpLCAxKSwNCiAgICBCYXNlX0V4dCAgID0gcm91bmQobWVhbihCYXNlX0V4dCwgbmEucm0gPSBUUlVFKSwgMSksDQogICAgRXNjXzI1X05hYyA9IHJvdW5kKG1lYW4oRXNjXzI1X05hYywgbmEucm0gPSBUUlVFKSwgMSksDQogICAgRXNjXzI1X0V4dCA9IHJvdW5kKG1lYW4oRXNjXzI1X0V4dCwgbmEucm0gPSBUUlVFKSwgMSksDQogICAgRXNjXzUwX05hYyA9IHJvdW5kKG1lYW4oRXNjXzUwX05hYywgbmEucm0gPSBUUlVFKSwgMSksDQogICAgRXNjXzUwX0V4dCA9IHJvdW5kKG1lYW4oRXNjXzUwX0V4dCwgbmEucm0gPSBUUlVFKSwgMSksDQogICAgRXNjXzc1X05hYyA9IHJvdW5kKG1lYW4oRXNjXzc1X05hYywgbmEucm0gPSBUUlVFKSwgMSksDQogICAgRXNjXzc1X0V4dCA9IHJvdW5kKG1lYW4oRXNjXzc1X0V4dCwgbmEucm0gPSBUUlVFKSwgMSkNCiAgKQ0KDQpwcmludCh0YWJsYV9wb3JfYcOxbykNCmBgYA0KDQoNCg0KIyMjIFBhcsOhbWV0cm9zDQojIyMjIERlc2NyaXBjacOzbiBuaXZlbCBkZSBwcmVjaXNpw7NuIGRlbCBtb2RlbG8gZGUgcHJlZGljY2nDs24uDQoNCkNvbiBiYXNlIGVuIGxvcyBtb2RlbG9zIHJlYWx6aWFkb3MsIHNlIHNlbGVjY2lvbsOzIGVsIG1vZGVsbyBTQVJJTUFYIGNvbW8gZWwgbcOhcyDDs3B0aW1vIGRlYmlkbyBhIHF1ZToNCg0KKiBBSUMgeSBCSUMgbcOhcyBiYWpvcyBBSUMgPSAxMzkwLjc1IHwgQklDID0gMTQyMC4wMC4gRWwgbW9kZWxvIFNBUklNQVhfTEFHMSBwcmVzZW50YSBsb3MgdmFsb3JlcyBtw6FzIHJlZHVjaWRvcyBkZSBwZW5hbGl6YWNpw7NuIHBvciBjb21wbGVqaWRhZDsgZXN0byBpbmRpY2EgdW4gbWVqb3IgZXF1aWxpYnJpbyBlbnRyZSBhanVzdGUgeSBwYXJzaW1vbmlhIGNvbXBhcmFkbyBjb24gU0FSSU1BWCwgQXV0b0FSSU1BLCBWQVIgeSBWQVIyLg0KDQoqIExvZy1MaWtlbGlob29kIHN1cGVyaW9yOiBQcmVzZW50YSBsYSB2ZXJvc2ltaWxpdHVkIG3DoXMgYWx0YSAoZnJlbnRlIGEgLTY4Ni4zMCwgYSDigJM3NTIuMjEgeSDigJMxNDMxLjA3KSAsIGluZGljYW5kbyB1biBhanVzdGUgbcOhcyBmaW5vIGRlIGxhIGRpc3RyaWJ1Y2nDs24gcmVhbCBkZSBsb3MgZGF0b3MuDQoNCiogUk1TRTogRWwgbW9kZWxvIHByZXNlbnRhIHVuIFJNU0UgPSAxMjguNTcsIG11eSBjZXJjYW5vIGFsIG3DrW5pbW8gb2JzZXJ2YWRvICgxMjYuNzkgY29uIEF1dG9BUklNQSksIHBlcm8gY29uIG1lam9yIGFqdXN0ZSBnZW5lcmFsIChsb2ctbGlrZWxpaG9vZCArIEFJQyArIEJJQyk7IGxvIHBvc2ljaW9uYSBjb21vIHVuYSBvcGNpw7NuIHJvYnVzdGEgeSBlc3RhYmxlLCBpbmNsdXNvIHNpIGVsIFJNU0Ugbm8gZXMgZWwgbcOhcyBiYWpvIGFic29sdXRvLg0KDQoqIFJlc2lkdW9zIGNvbW8gcnVpZG8gYmxhbmNvOiBFbCB0ZXN0IGRlIExqdW5n4oCTQm94IGVuIGxvcyByZXNpZHVvcyBkZSBTQVJJTUFYIGFycm9qYSBwLXZhbHVlID4gMC4wNSwgY29uZmlybWFuZG8gYXVzZW5jaWEgZGUgYXV0b2NvcnJlbGFjacOzbiByZW1hbmVudGUgeSBjdW1wbGltaWVudG8gZGUgc3VwdWVzdG9zLCBlcyBkZWNpciwgU0FSSU1BWF9MQUcxIG11ZXN0cmEgdW4gYWp1c3RlIGVzdGFkw61zdGljYW1lbnRlIHPDs2xpZG8sIGNvbiByZWdyZXNvcmVzIHNpZ25pZmljYXRpdm9zLCByZXNpZHVvcyBubyBhdXRvY29ycmVsYWNpb25hZG9zIChwID0gMC4xOTMyKQ0KDQoqIENhcHR1cmEgZGUgZXN0YWNpb25hbGlkYWQgeSByZWdyZXNvcmVzIGV4dGVybm9zOiBJbmNvcnBvcmEgZGUgZm9ybWEgZXhwbMOtY2l0YSB0YW50byB0w6lybWlub3MgZXN0YWNpb25hbGVzIGNvbW8gdmFyaWFibGVzIGV4cGxpY2F0aXZhcyAowqt4cmVnwrspLCBtb2RlbGFuZG8gbcOhcyBjb21wbGV0YW1lbnRlIGxhcyBkaW7DoW1pY2FzIGRlIHRlbmRlbmNpYSB5IGNpY2xvcyBkZWwgc2VjdG9yIEhvc3BpdGFsaWRhZCAoSG90ZWxlcmEpDQoNCmBgYHtyfQ0KbGlicmFyeShNZXRyaWNzKQ0KDQojIFNBUklNQVgNCmFpY19zYXJpbWF4IDwtIEFJQyhtb2RlbG9fc2FyaW1heDEpDQpiaWNfc2FyaW1heCA8LSBCSUMobW9kZWxvX3NhcmltYXgxKQ0KbG9nbGlrX3NhcmltYXggPC0gbG9nTGlrKG1vZGVsb19zYXJpbWF4MSkNCnJtc2Vfc2FyaW1heCA8LSBybXNlKHksIGZpdHRlZChtb2RlbG9fc2FyaW1heDEpKQ0KDQojIFNBUklNQVhsYWcxDQphaWNfc2FyaW1heGxhZyA8LSBBSUMobW9kZWxvX3NhcmltYXhfbGFnMSkNCmJpY19zYXJpbWF4bGFnIDwtIEJJQyhtb2RlbG9fc2FyaW1heF9sYWcxKQ0KbG9nbGlrX3NhcmltYXhsYWcgPC0gbG9nTGlrKG1vZGVsb19zYXJpbWF4X2xhZzEpDQpybXNlX3NhcmltYXhsYWcgPC0gcm1zZSh5X2xhZzEsIGZpdHRlZChtb2RlbG9fc2FyaW1heF9sYWcxKSkNCg0KIyBBdXRvQVJJTUENCmFpY19hdXRvIDwtIEFJQyhtb2RlbG9fYXV0bykNCmJpY19hdXRvIDwtIEJJQyhtb2RlbG9fYXV0bykNCmxvZ2xpa19hdXRvIDwtIGxvZ0xpayhtb2RlbG9fYXV0bykNCnJtc2VfYXV0byA8LSBybXNlKHksIGZpdHRlZChtb2RlbG9fYXV0bykpDQoNCiMgVkFSDQojIEFqdXN0YXIgcHJlZGljY2lvbmVzIGRlbCBWQVIgDQpsaWJyYXJ5KHZhcnMpDQoNCmFpY192YXIgPC0gQUlDKG1vZGVsb192YXIpDQpiaWNfdmFyIDwtIEJJQyhtb2RlbG9fdmFyKQ0KbG9nbGlrX3ZhciA8LSBsb2dMaWsobW9kZWxvX3ZhcikNCnJtc2VfdmFyIDwtIHJtc2UoeSwgZml0dGVkKG1vZGVsb192YXIpWywgInkiXSkNCg0KdGFibGFfY29tcGFyYXRpdmEgPC0gZGF0YS5mcmFtZSgNCiAgTW9kZWxvID0gYygiU0FSSU1BWCIsICJTQVJJTUFYX0xBRzEiLCAiQXV0b0FSSU1BIiwgIlZBUiIgKSwNCiAgQUlDID0gYyhhaWNfc2FyaW1heCwgYWljX3NhcmltYXhsYWcsIGFpY19hdXRvLCBhaWNfdmFyKSwNCiAgQklDID0gYyhiaWNfc2FyaW1heCwgYmljX3NhcmltYXhsYWcsIGJpY19hdXRvLCBiaWNfdmFyKSwNCiAgTG9nTGlrZWxpaG9vZCA9IGMobG9nbGlrX3NhcmltYXgsIGxvZ2xpa19zYXJpbWF4bGFnLCAgbG9nbGlrX2F1dG8sIGxvZ2xpa192YXIpLA0KICBSTVNFID0gYyhybXNlX3NhcmltYXgsIHJtc2Vfc2FyaW1heGxhZywgcm1zZV9hdXRvLCBybXNlX3ZhcikNCikNCg0KdGFibGFfY29tcGFyYXRpdmFbXSA8LSBsYXBwbHkodGFibGFfY29tcGFyYXRpdmEsIGZ1bmN0aW9uKHgpIGlmKGlzLm51bWVyaWMoeCkpIHJvdW5kKHgsIDIpIGVsc2UgeCkNCnByaW50KHRhYmxhX2NvbXBhcmF0aXZhKQ0KYGBgDQoNCg0KIyMgKipQcmVkaWNjacOzbiBUdXJpc21vIEV2ZW50b3MgZGUgTmVnb2NpbyAoT0NWKSoqDQoNCiMjIyBBdXRvY29ycmVsYWNpw7NuDQoNCioqR3LDoWZpY28gQUNGKioNCg0KKiBFbCBBQ0YgbXVlc3RyYSB1bmEgYXV0b2NvcnJlbGFjacOzbiBmdWVydGUgZW4gZWwgcmV0cmFzbyAxIChsYWcgMSkgeSBjYWUgcsOhcGlkYW1lbnRlIGRlc3B1w6lzLCBwb3IgZW5kZSwgdW4gdmFsb3IgcGVxdWXDsW8gZGUgcSA9IDEgcG9kcsOtYSBzZXIgYWRlY3VhZG8uDQoNCiogTcOhcyBsYWdzIG5vIHBhcmVjZW4gc2VyIHNpZ25pZmljYXRpdmFtZW50ZSBkaWZlcmVudGVzIGRlIGNlcm8gKGZ1ZXJhIGRlIGxhcyBsw61uZWFzIGF6dWxlcyksIGxvIHF1ZSByZWZ1ZXJ6YSBsYSBpZGVhIGRlIHVuIE1BKDEpLg0KDQoqKkdyw6FmaWNvIFBBQ0YqKg0KDQoqIEVsIFBBQ0YgbXVlc3RyYSB1biBwaWNvIGNsYXJvIGVuIGxhZyAxLCBsdWVnbyBjYWUsIGNvbiBhbGd1bm9zIHZhbG9yZXMgZGVudHJvIHkgZnVlcmEgZGVsIHVtYnJhbCwgbG8gY3VhbCBzdWdpZXJlIHF1ZSB0YW1iacOpbiB1biBwID0gMSBwdWVkZSBzZXIgcmF6b25hYmxlLg0KDQoqKlBydWViYSBMZ3Vual9ib3hfcmVzdWx0KioNCg0KKiBObyBoYXkgZXZpZGVuY2lhIGRlIGF1dG9jb3JyZWxhY2nDs24gZW4gbGEgc2VyaWUgdGVtcG9yYWwgRGVycmFtYV90czIsIGxvIGN1YWwgc3VnaWVyZSBxdWUgbG9zIHJlc2lkdW9zIGVzdMOhbiBkaXN0cmlidWlkb3MgYWxlYXRvcmlhbWVudGUsIGxvIGN1YWwgZXMgZGVzZWFibGUgc2kgZXN0w6FzIGV2YWx1YW5kbyB1biBtb2RlbG8gZGUgc2VyaWVzIGRlIHRpZW1wbyAoY29tbyBBUklNQSkuDQoNCmBgYHtyfQ0KIyBBdXRvY29ycmVsYWNpw7NuIHkgYXV0b2NvcnJlbGFjacOzbiBwYXJjaWFsDQphY2YoRGVycmFtYV90cywgbWFpbj0iQXV0b2NvcnJlbGF0aW9uIC0gRGVycmFtYSIpDQpwYWNmKERlcnJhbWFfdHMsIG1haW49IlBhcnRpYWwgQXV0b2NvcnJlbGF0aW9uIC0gRGVycmFtYSIpDQoNCiMgQXVjb3RvcnJlbGFjacOzbiBzZXJpYWwNCmxndW5qX2JveF9yZXN1bHQgPC0gQm94LnRlc3QoRGVycmFtYV90cywgbGFnID0gNSwgdHlwZSA9ICJManVuZy1Cb3giKQ0KbGd1bmpfYm94X3Jlc3VsdA0KDQojIEVzdGFjaW9uYXJpZWRhZA0KYWRmX3Rlc3QgPC0gYWRmLnRlc3QoZGZfT0NWX251bWVyaWMkRGVycmFtYSkNCmFkZl90ZXN0DQoNCiMgUHJpbWVyYSBkaWZlcmVuY2lhY2nDs24NCmRpZmZfZGVycmFtYSA8LSBkaWZmKGRmX09DVl9udW1lcmljJERlcnJhbWEpDQphZGZfdGVzdDEgPC0gYWRmLnRlc3QoZGlmZl9kZXJyYW1hKQ0KYWRmX3Rlc3QxDQoNCiMgU2VndW5kYSBkaWZlcmVuY2lhY2nDs24NCmRpZmYyX2RlcnJhbWEgPC0gZGlmZihkaWZmX2RlcnJhbWEpDQphZGZfdGVzdDIgPC0gYWRmLnRlc3QoZGlmZjJfZGVycmFtYSkNCmFkZl90ZXN0Mg0KDQpgYGANCg0KIyMjIEFSSU1BDQoNCmBgYHtyfQ0KbW9kZWxvX2FyaW1hIDwtIEFyaW1hKGRmX09DVl9udW1lcmljJERlcnJhbWEsIG9yZGVyID0gYygxLCAyLCAxKSkNCnN1bW1hcnkobW9kZWxvX2FyaW1hKQ0KDQptb2RlbG9fYXJpbWExIDwtIEFyaW1hKGRmX09DVl9udW1lcmljJERlcnJhbWEsIG9yZGVyID0gYygwLCAyLCAxKSkNCnN1bW1hcnkobW9kZWxvX2FyaW1hMSkNCg0KbW9kZWxvX2FyaW1hMiA8LSBBcmltYShkZl9PQ1ZfbnVtZXJpYyREZXJyYW1hLCBvcmRlciA9IGMoMSwgMiwgMCkpDQpzdW1tYXJ5KG1vZGVsb19hcmltYTIpDQoNCmZpdDIgPC0gYXV0by5hcmltYShEZXJyYW1hX3RzLCBkPTIsIG1heC5wPTIsIG1heC5xPTIpDQpzdW1tYXJ5KGZpdDIpDQpgYGANCg0KIyMjIEVzdGFjaW9uYWxpZGFkDQoNCkNvbW8gZWwgdmFsb3IgcCBlcyAwLjA0NTA3IDwgMC4wNSwgc2UgcmVjaGF6YSBsYSBoaXDDs3Rlc2lzIG51bGEgZGUgcXVlIHRvZG9zIGxvcyBtZXNlcyB0aWVuZW4gbGEgbWlzbWEgbWVkaWFuYSBkZSAiRGVycmFtYSIuIFBvciBsbyB0YW50bywgc2Ugc3VnaWVyZSBxdWUgaGF5IGVzdGFjaW9uYWxpZGFkIG1lbnN1YWwsIGVzIGRlY2lyLCBhbCBtZW5vcyB1biBtZXMgZXMgc2lnbmlmaWNhdGl2YW1lbnRlIGRpZmVyZW50ZSBkZSBsb3MgZGVtw6FzIGVuIHTDqXJtaW5vcyBkZSBkZXJyYW1hIGVjb27Ds21pY2EuDQoNClNpbiBlbWJhcmdvLCBhbCByZWFsaXphciB1biBTQVJJTUEgeSBBUklNQSgwLDIsMCkgdGllbmVuIGxvcyBtaXNtb3MgcGFyw6FtZXRyb3MgeSBlbCBtaXNtbyBBSUMsIGVudG9uY2VzIG5vIGhheSBjb21wb25lbnRlIGVzdGFjaW9uYWwgc2lnbmlmaWNhdGl2byBkZXRlY3RhZG8uIEVzdG8gZXMgY29tcGxldGFtZW50ZSBjb2hlcmVudGUgY29uIGxhICBwcnVlYmEgZGUgRHVubiwgcXVlIGluZGljw7MgcXVlIG5vIGhhYsOtYSBlc3RhY2lvbmFsaWRhZCByZWFsbWVudGUgc2lnbmlmaWNhdGl2YSBlbiBsYSBzZXJpZS4NCg0KYGBge3J9DQp0cmltZXN0cmVzX2RlcnJhbWEgPC0gY3ljbGUoRGVycmFtYV90cykNCmtydXNrYWxfZGVycmFtYSA8LSBrcnVza2FsLnRlc3QoYXMubnVtZXJpYyhEZXJyYW1hX3RzKSB+IHRyaW1lc3RyZXNfZGVycmFtYSkNCnByaW50KGtydXNrYWxfZGVycmFtYSkNCg0KIyBDYXJnYXIgbGEgbGlicmVyw61hDQpsaWJyYXJ5KEZTQSkNCg0KIyBSZWFsaXphciBlbCB0ZXN0IGRlIER1bm4gcGFyYSBjb21wYXJhY2lvbmVzIG3Dumx0aXBsZXMgZW50cmUgdHJpbWVzdHJlcw0KZHVubl90ZXN0IDwtIGR1bm5UZXN0KGFzLm51bWVyaWMoRGVycmFtYV90cykgfiB0cmltZXN0cmVzX2RlcnJhbWEsIG1ldGhvZCA9ICJib25mZXJyb25pIikNCg0KIyBJbXByaW1pciBsb3MgcmVzdWx0YWRvcw0KcHJpbnQoZHVubl90ZXN0KQ0KYGBgDQoNCg0KYGBge3J9DQpuX21lc2VzIDwtIGxlbmd0aChEZXJyYW1hX3RzKQ0KDQpEZXJyYW1hX2RmIDwtIGRhdGEuZnJhbWUoDQogIERlcnJhbWFfdHMyID0gYXMubnVtZXJpYyhEZXJyYW1hX3RzKSwNCiAgQcOxbyA9IHJlcCgyMDIzOjIwMjQsIGVhY2ggPSAxMilbMTpuX21lc2VzXSwNCiAgTWVzTm9tYnJlID0gcmVwKDE6MTIsIGxlbmd0aC5vdXQgPSBuX21lc2VzKQ0KKQ0KDQplc3RhY2lvbmFsaWRhZCA8LSBEZXJyYW1hX2RmICU+JQ0KICBtdXRhdGUoTWVzTm9tYnJlID0gZmFjdG9yKE1lc05vbWJyZSwgbGV2ZWxzID0gMToxMikpICU+JQ0KICBncm91cF9ieShBw7FvLCBNZXNOb21icmUpICU+JQ0KICBzdW1tYXJpc2UobWVkaWFfZGVycmFtYSA9IG1lYW4oRGVycmFtYV90czIsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAnZHJvcCcpDQoNCmxpYnJhcnkoZ2dwbG90MikNCmdncGxvdChlc3RhY2lvbmFsaWRhZCwgYWVzKHggPSBNZXNOb21icmUsIHkgPSBtZWRpYV9kZXJyYW1hLCBncm91cCA9IEHDsW8sIGNvbG9yID0gZmFjdG9yKEHDsW8pKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGxhYnModGl0bGUgPSAiRXN0YWNpb25hbGlkYWQgZGUgRGVycmFtYSBwb3IgTWVzIiwgeCA9ICJNZXMiLCB5ID0gIk1lZGlhIGRlIERlcnJhbWEiKQ0KYGBgDQoNCkxvcyByZXN1bHRhZG9zIHF1ZSBtdWVzdHJhcyBpbmRpY2FuIHF1ZSBsYSBwcnVlYmEgZGUgS3J1c2thbC1XYWxsaXMgc2UgcmVhbGl6w7MgY29ycmVjdGFtZW50ZSBwYXJhIGNhZGEgdW5vIGRlIGxvcyBtZXNlcywgcGVybyBlbiB0b2RvcyBsb3MgY2Fzb3MsIGVsIHZhbG9yIHAgZXMgZGUgMC4zMTczMTA1Lg0KDQpEYWRvIHF1ZSBlbCB2YWxvciBwIGVzIGNvbnNpc3RlbnRlIGEgdHJhdsOpcyBkZSB0b2RvcyBsb3MgbWVzZXMsIHBhcmVjZSBxdWUgbm8gaGF5IGVzdGFjaW9uYWxpZGFkIHNpZ25pZmljYXRpdmEgZW4gbG9zIGRhdG9zLCBhbCBtZW5vcyBubyBkZXNkZSBsYSBwZXJzcGVjdGl2YSBkZSBsYXMgZGlmZXJlbmNpYXMgZW50cmUgbG9zIGHDsW9zIGRlbnRybyBkZSBjYWRhIG1lcy4NCg0KYGBge3J9DQojIFJlYWxpemFyIGxhIHBydWViYSBkZSBLcnVza2FsLVdhbGxpcyBwYXJhIGNhZGEgbWVzDQprcnVza2FsX3Jlc3VsdHMgPC0gRGVycmFtYV9kZiAlPiUNCiAgZ3JvdXBfYnkoTWVzTm9tYnJlKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGtydXNrYWxfdGVzdCA9IGxpc3Qoa3J1c2thbC50ZXN0KERlcnJhbWFfdHMyIH4gZmFjdG9yKEHDsW8pKSksICANCiAgICBwX3ZhbHVlID0ga3J1c2thbF90ZXN0W1sxXV0kcC52YWx1ZSAgIyBFeHRyYWVyIGVsIHZhbG9yIHAgZGUgbGEgcHJ1ZWJhDQogICkNCg0KcHJpbnQoa3J1c2thbF9yZXN1bHRzKQ0KYGBgDQoNCiMjIyBWQVINCg0KYGBge3J9DQojIERpZmVyZW5jaWFjacOzbiBFdmVudG9zDQpkaWZmX0V2ZW50b3MgPC0gZGlmZihkZl9PQ1ZfbnVtZXJpYyRFdmVudG9zKQ0KZGlmZjJfRXZlbnRvcyA8LSBkaWZmKGRpZmZfRXZlbnRvcykNCmFkZl90ZXN0X0V2ZW50b3MgPC0gYWRmLnRlc3QoZGlmZjJfRXZlbnRvcykNCmFkZl90ZXN0X0V2ZW50b3MNCg0KIyBEaWZlcmVuY2lhY2nDs24gQXNpc3RlbnRlcw0KZGlmZl9Bc2lzdGVudGVzXyA8LSBkaWZmKGRmX09DVl9udW1lcmljJEFzaXN0ZW50ZXMpDQpkaWZmMl9Bc2lzdGVudGVzIDwtIGRpZmYoZGlmZl9Bc2lzdGVudGVzXykNCmFkZl90ZXN0X0FzaXN0ZW50ZXMyIDwtIGFkZi50ZXN0KGRpZmYyX0FzaXN0ZW50ZXMpDQphZGZfdGVzdF9Bc2lzdGVudGVzMg0KDQojIERpZmVyZW5jaWFjacOzbiBMb2cgQXNpc3RlbnRlcw0KZGZfT0NWX251bWVyaWMkbG9nX0FzaXN0ZW50ZXMgPC0gbG9nKGRmX09DVl9udW1lcmljJEFzaXN0ZW50ZXMgKyAxKSAgIyArMSBwYXJhIGV2aXRhciBsb2coMCkNCmRpZmZfQXNpc3RlbnRlcyA8LSBkaWZmKGRmX09DVl9udW1lcmljJGxvZ19Bc2lzdGVudGVzKQ0KYWRmX3Rlc3RfQXNpc3RlbnRlcyA8LSBhZGYudGVzdChkaWZmX0FzaXN0ZW50ZXMpDQphZGZfdGVzdF9Bc2lzdGVudGVzDQoNCiMgRGlmZXJlbmNpYWNpw7NuIExvZyBDdWFydG9zDQpkZl9PQ1ZfbnVtZXJpYyRsb2dfQ3VhcnRvcyA8LSBsb2coZGZfT0NWX251bWVyaWMkQ3VhcnRvcyArIDEpICAjICsxIHBhcmEgZXZpdGFyIGxvZygwKQ0KZGlmZl9DdWFydG9zIDwtIGRpZmYoZGZfT0NWX251bWVyaWMkbG9nX0N1YXJ0b3MpDQphZGZfdGVzdF9DdWFydG9zIDwtIGFkZi50ZXN0KGRpZmZfQ3VhcnRvcykNCmFkZl90ZXN0X0N1YXJ0b3MNCg0KIyBEaWZlcmVuY2lhY2nDs24gQ3VhcnRvcw0KZGlmZl9DdWFydG9zXyA8LSBkaWZmKGRmX09DVl9udW1lcmljJEN1YXJ0b3MpDQpkaWZmMl9DdWFydG9zIDwtIGRpZmYoZGlmZl9DdWFydG9zKQ0KZGlmZjNfQ3VhcnRvcyA8LSBkaWZmKGRpZmYyX0N1YXJ0b3MpDQphZGZfdGVzdF9DdWFydG9zMiA8LSBhZGYudGVzdChkaWZmM19DdWFydG9zKQ0KYWRmX3Rlc3RfQ3VhcnRvczINCmBgYA0KDQoNCmBgYHtyfQ0KIyBSZWNvcnRhciBsYXMgc2VyaWVzIGEgbGEgbG9uZ2l0dWQgbcOtbmltYSBjb23Dum4NCm1pbl9sZW5ndGggPC0gbWluKGxlbmd0aChkaWZmMl9kZXJyYW1hKSwgbGVuZ3RoKGRpZmYyX0V2ZW50b3MpLCBsZW5ndGgoZGlmZl9Bc2lzdGVudGVzKSwNCiAgICAgICAgICAgICAgICAgIGxlbmd0aChkaWZmX0N1YXJ0b3MpKQ0KDQojIFJlY29ydGFyIHRvZGFzIGxhcyBzZXJpZXMgYSBlc2EgbG9uZ2l0dWQNCmRpZmYyX2RlcnJhbWEgPC0gZGlmZjJfZGVycmFtYVsxOm1pbl9sZW5ndGhdDQpkaWZmMl9FdmVudG9zIDwtIGRpZmYyX0V2ZW50b3NbMTptaW5fbGVuZ3RoXQ0KZGlmZl9Bc2lzdGVudGVzIDwtIGRpZmZfQXNpc3RlbnRlc1sxOm1pbl9sZW5ndGhdDQpkaWZmX0N1YXJ0b3MgPC0gZGlmZl9DdWFydG9zWzE6bWluX2xlbmd0aF0NCg0KIyBDb25zdHJ1aXIgZGF0YSBmcmFtZSBjb24gbm9tYnJlcyBkZSBjb2x1bW5hcw0KdmFyX2RhdGEgPC0gZGF0YS5mcmFtZSgNCiAgZGlmZjJfZGVycmFtYSAgICA9IGRpZmYyX2RlcnJhbWEsDQogIGRpZmYyX0V2ZW50b3MgICAgPSBkaWZmMl9FdmVudG9zLA0KICBkaWZmX0FzaXN0ZW50ZXMgPSBkaWZmX0FzaXN0ZW50ZXMsDQogIGRpZmZfQ3VhcnRvcyAgICA9IGRpZmZfQ3VhcnRvcw0KKQ0KDQojIENvbnZlcnRpciBhIGZvcm1hdG8gdHMNCnZhcl9kYXRhX3RzIDwtIHRzKHZhcl9kYXRhLCBzdGFydCA9IGMoMjAyMywxKSwgZnJlcXVlbmN5ID0gMTIpDQoNCiMgRGV0ZXJtaW5hciBlbCBtZWpvciBuw7ptZXJvIGRlIGxhZ3MgdXNhbmRvIEFJQw0KbGFnX3NlbGVjdGlvbiA8LSBWQVJzZWxlY3QodmFyX2RhdGFfdHMsIGxhZy5tYXggPSA0LCB0eXBlID0gImNvbnN0IikNCnByaW50KGxhZ19zZWxlY3Rpb24kc2VsZWN0aW9uKQ0Kb3B0aW1hbF9sYWcgPC0gbGFnX3NlbGVjdGlvbiRzZWxlY3Rpb25bIkFJQyhuKSJdDQpjb2xuYW1lcyh2YXJfZGF0YV90cykgPC0gYygNCiAgImRpZmYyX2RlcnJhbWEiLA0KICAiZGlmZjJfRXZlbnRvcyIsDQogICJkaWZmX0FzaXN0ZW50ZXMiLA0KICAiZGlmZl9DdWFydG9zIg0KKQ0KcHJpbnQoY29sbmFtZXModmFyX2RhdGFfdHMpKQ0KYGBgDQoNCmBgYHtyfQ0KdmFyc19idWVuYXMgPC0gYygiZGlmZjJfZGVycmFtYSIsICJkaWZmMl9FdmVudG9zIikNCnZhcl9kYXRhX3N1YnNldCA8LSB2YXJfZGF0YV90c1ssIHZhcnNfYnVlbmFzXQ0KDQp2YXJfZGF0YV9zdWJzZXQgPC0gbmEub21pdCh2YXJfZGF0YV9zdWJzZXQpDQp2YXJfbW9kZWw0IDwtIFZBUih2YXJfZGF0YV9zdWJzZXQsIHAgPSAxLCB0eXBlID0gImNvbnN0IikNCnN1bW1hcnkodmFyX21vZGVsNCkNCmBgYA0KDQpObyBoYXkgZXZpZGVuY2lhIGVzdGFkw61zdGljYSBkZSBjYXVzYWxpZGFkIGRlIEdyYW5nZXIgZW4gbmluZ3VuYSBkaXJlY2Npw7NuOyBlcyBkZWNpciwgbm8gaGF5IGNhdXNhbGlkYWQgdW5pZGlyZWNjaW9uYWwgbmkgYmlkaXJlY2Npb25hbCBlbnRyZSAqKmRpZmYyX0V2ZW50b3MgeSBkaWZmMl9kZXJyYW1hKiouDQoNCmBgYHtyfQ0KIyBQcnVlYmEgZGUgY2F1c2FsaWRhZCBkZSBHcmFuZ2VyDQojIMK/ZGlmZjJfRXZlbnRvcyBjYXVzYSBkaWZmMl9kZXJyYW1hPw0KZ3JhbmdlcjEgPC0gY2F1c2FsaXR5KHZhcl9tb2RlbDQsIGNhdXNlID0gImRpZmYyX0V2ZW50b3MiKQ0KcHJpbnQoZ3JhbmdlcjEkR3JhbmdlcikNCg0KIyDCv2RpZmYyX2RlcnJhbWEgY2F1c2EgZGlmZjJfRXZlbnRvcz8NCmdyYW5nZXIyIDwtIGNhdXNhbGl0eSh2YXJfbW9kZWw0LCBjYXVzZSA9ICJkaWZmMl9kZXJyYW1hIikNCnByaW50KGdyYW5nZXIyJEdyYW5nZXIpDQpgYGANCg0KQ29uIGxvcyBwLXZhbG9yZXMgbWF5b3JlcyBhIDAuMDUgZW4gYW1ib3MgY2Fzb3MsIG5vIHNlIGVuY3VlbnRyYSBldmlkZW5jaWEgZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YSBkZSBjYXVzYWxpZGFkIGRlIEdyYW5nZXIgZW4gbmluZ3VuYSBkaXJlY2Npw7NuIGVudHJlICoqZGlmZl9DdWFydG9zKiogeSAqKmRpZmYyX2RlcnJhbWEqKi4NCg0KYGBge3J9DQp2YXJzX2J1ZW5hczUgPC0gYygiZGlmZjJfZGVycmFtYSIsICJkaWZmX0N1YXJ0b3MiKQ0KdmFyX2RhdGFfc3Vic2V0NSA8LSB2YXJfZGF0YV90c1ssIHZhcnNfYnVlbmFzNV0NCg0KdmFyX2RhdGFfc3Vic2V0NSA8LSBuYS5vbWl0KHZhcl9kYXRhX3N1YnNldDUpDQp2YXJfbW9kZTVsIDwtIFZBUih2YXJfZGF0YV9zdWJzZXQ1LCBwID0gMSwgdHlwZSA9ICJjb25zdCIpDQoNCiMgUHJ1ZWJhIGRlIGNhdXNhbGlkYWQgZGUgR3Jhbmdlcg0KIyDCv2RpZmZfQ3VhcnRvcyBjYXVzYSBkaWZmMl9kZXJyYW1hPw0KZ3JhbmdlcjUgPC0gY2F1c2FsaXR5KHZhcl9tb2RlNWwsIGNhdXNlID0gImRpZmZfQ3VhcnRvcyIpDQpwcmludChncmFuZ2VyNSRHcmFuZ2VyKQ0KDQojIMK/ZGlmZjJfZGVycmFtYSBjYXVzYSBkaWZmX0N1YXJ0b3M/DQpncmFuZ2VyNiA8LSBjYXVzYWxpdHkodmFyX21vZGU1bCwgY2F1c2UgPSAiZGlmZjJfZGVycmFtYSIpDQpwcmludChncmFuZ2VyNiRHcmFuZ2VyKQ0KYGBgDQoNCkNvbiBsb3MgcC12YWx1ZXNzIG1heW9yZXMgYSAwLjA1IGVuIGxhIHZhcmlhYmxlIGV4cGxpY2F0aXZhLCBubyBzZSBlbmN1ZW50cmEgZXZpZGVuY2lhIGVzdGFkw61zdGljYW1lbnRlIHNpZ25pZmljYXRpdmEgZGUgY2F1c2FsaWRhZCBkZSBHcmFuZ2VyIGVudHJlICoqZGlmZl9Bc2lzdGVudGVzKiogeSAqKmRpZmYyX2RlcnJhbWEqKi4NCg0KYGBge3J9DQp2YXJzX2J1ZW5hczggPC0gYygiZGlmZjJfZGVycmFtYSIsICJkaWZmX0FzaXN0ZW50ZXMiKQ0KdmFyX2RhdGFfc3Vic2V0OCA8LSB2YXJfZGF0YV90c1ssIHZhcnNfYnVlbmFzOF0NCg0KdmFyX2RhdGFfc3Vic2V0OCA8LSBuYS5vbWl0KHZhcl9kYXRhX3N1YnNldDgpDQp2YXJfbW9kZWw4IDwtIFZBUih2YXJfZGF0YV9zdWJzZXQ4LCBwID0gMSwgdHlwZSA9ICJjb25zdCIpDQoNCiMgUHJ1ZWJhIGRlIGNhdXNhbGlkYWQgZGUgR3Jhbmdlcg0KIyDCv2RpZmZfQXNpc3RlbnRlcyBjYXVzYSBkaWZmMl9kZXJyYW1hPw0KZ3JhbmdlcjggPC0gY2F1c2FsaXR5KHZhcl9tb2RlbDgsIGNhdXNlID0gImRpZmZfQXNpc3RlbnRlcyIpDQpwcmludChncmFuZ2VyOCRHcmFuZ2VyKQ0KDQojIMK/ZGlmZjJfZGVycmFtYSBjYXVzYSBkaWZmX0FzaXN0ZW50ZXM/DQpncmFuZ2VyOSA8LSBjYXVzYWxpdHkodmFyX21vZGVsOCwgY2F1c2UgPSAiZGlmZjJfZGVycmFtYSIpDQpwcmludChncmFuZ2VyOSRHcmFuZ2VyKQ0KYGBgDQoNCg0KDQojIyMgQVJJTUFYDQoNCmBgYHtyfQ0KbGlicmFyeShmb3JlY2FzdCkNCg0KIyBEaXZpZGlyIGxvcyBkYXRvcyBlbiBlbnRyZW5hbWllbnRvICg4MCUpIHkgcHJ1ZWJhICgyMCUpDQpzZXQuc2VlZCgxMjMpDQp0cmFpbl9zaXplIDwtIGZsb29yKDAuOCAqIG5yb3codmFyX2RhdGEpKQ0KdHJhaW5fZGF0YSA8LSB2YXJfZGF0YVsxOnRyYWluX3NpemUsIF0NCnRlc3RfZGF0YSA8LSB2YXJfZGF0YVsodHJhaW5fc2l6ZSArIDEpOm5yb3codmFyX2RhdGEpLCBdDQoNCiMgQ3JlYXIgbWF0cmljZXMgZGUgcmVncmVzb3JlcyBleHRlcm5vcw0KeHJlZ190cmFpbiA8LSBhcy5tYXRyaXgodHJhaW5fZGF0YVssIC0xXSkgICMgRXhjbHV5ZSBsYSBjb2x1bW5hIGRlcGVuZGllbnRlDQp4cmVnX3Rlc3QgPC0gYXMubWF0cml4KHRlc3RfZGF0YVssIC0xXSkNCg0KIyBBanVzdGFyIGVsIG1vZGVsbyBBUklNQVgNCmFyaW1heF9tb2RlbCA8LSBhdXRvLmFyaW1hKHRyYWluX2RhdGEkZGlmZjJfZGVycmFtYSwgeHJlZyA9IHhyZWdfdHJhaW4pDQoNCiMgUHJlZGVjaXINCnByZWQgPC0gcHJlZGljdChhcmltYXhfbW9kZWwsIG5ld3hyZWcgPSB4cmVnX3Rlc3QsIG4uYWhlYWQgPSBucm93KHhyZWdfdGVzdCkpDQoNCiMgVmVyIGxhcyBwcmVkaWNjaW9uZXMgcHVudHVhbGVzDQpwcmludChwcmVkJHByZWQpDQoNCiMgQ2FsY3VsYXIgZWwgUk1TRQ0KdGVzdF9sYWJlbCA8LSB0ZXN0X2RhdGEkZGlmZjJfZGVycmFtYSAgIyBWYWxvcmVzIHJlYWxlcw0Kcm1zZV9hcmltYXggPC0gc3FydChtZWFuKChwcmVkJHByZWQgLSB0ZXN0X2xhYmVsKV4yKSkNCnByaW50KHBhc3RlKCJSTVNFIGRlIEFSSU1BWDoiLCBybXNlX2FyaW1heCkpDQoNCiMgQ2FsY3VsYXIgQUlDIGRlbCBtb2RlbG8NCmFpY19hcmltYXggPC0gQUlDKGFyaW1heF9tb2RlbCkNCnByaW50KHBhc3RlKCJBSUMgZGVsIG1vZGVsbyBBUklNQVg6IiwgYWljX2FyaW1heCkpDQoNCmBgYA0KDQoqKkFSSU1BWCBDVUFSVE9TICYgREVSUkFNQSoqDQoNCmBgYHtyfQ0KbGlicmFyeShmb3JlY2FzdCkNCmN1YXJ0b19kYXRhIDwtIHZhcl9kYXRhWywgYygiZGlmZjJfZGVycmFtYSIsICJkaWZmX0N1YXJ0b3MiKV0NCg0KIyBEaXZpZGlyIGxvcyBkYXRvcyBlbiBlbnRyZW5hbWllbnRvICg4MCUpIHkgcHJ1ZWJhICgyMCUpDQpzZXQuc2VlZCgxMjMpDQp0cmFpbl9zaXplMiA8LSBmbG9vcigwLjggKiBucm93KGN1YXJ0b19kYXRhKSkNCnRyYWluX2RhdGEyIDwtIGN1YXJ0b19kYXRhWzE6dHJhaW5fc2l6ZSwgXQ0KdGVzdF9kYXRhMiA8LSBjdWFydG9fZGF0YVsodHJhaW5fc2l6ZSArIDEpOm5yb3coY3VhcnRvX2RhdGEpLCBdDQoNCiMgQ3JlYXIgbWF0cmljZXMgZGUgcmVncmVzb3JlcyBleHRlcm5vcyAoc29sbyAiQ3VhcnRvcyIpDQp4cmVnX3RyYWluMiA8LSBhcy5tYXRyaXgodHJhaW5fZGF0YTJbLCJkaWZmX0N1YXJ0b3MiLCBkcm9wID0gRkFMU0VdKQ0KeHJlZ190ZXN0MiA8LSBhcy5tYXRyaXgodGVzdF9kYXRhMlssImRpZmZfQ3VhcnRvcyIsIGRyb3AgPSBGQUxTRV0pDQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gQVJJTUFYDQphcmltYXhfbW9kZWwyIDwtIGF1dG8uYXJpbWEodHJhaW5fZGF0YTIkZGlmZjJfZGVycmFtYSwgeHJlZyA9IHhyZWdfdHJhaW4yKQ0KDQojIFByZWRlY2lyDQpwcmVkMiA8LSBwcmVkaWN0KGFyaW1heF9tb2RlbDIsIG5ld3hyZWcgPSB4cmVnX3Rlc3QyLCBuLmFoZWFkID0gbnJvdyh4cmVnX3Rlc3QyKSkNCg0KIyBWZXIgbGFzIHByZWRpY2Npb25lcyBwdW50dWFsZXMNCnByaW50KHByZWQyJHByZWQpDQoNCiMgQ2FsY3VsYXIgZWwgUk1TRQ0KdGVzdF9sYWJlbDIgPC0gdGVzdF9kYXRhMiRkaWZmMl9kZXJyYW1hDQpybXNlX2FyaW1heDIgPC0gc3FydChtZWFuKChwcmVkMiRwcmVkIC0gdGVzdF9sYWJlbDIpXjIpKQ0KcHJpbnQocGFzdGUoIlJNU0UgZGUgQVJJTUFYOiIsIHJtc2VfYXJpbWF4MikpDQoNCiMgQ2FsY3VsYXIgQUlDIGRlbCBtb2RlbG8NCmFpY19hcmltYXgyIDwtIEFJQyhhcmltYXhfbW9kZWwyKQ0KcHJpbnQocGFzdGUoIkFJQyBkZWwgbW9kZWxvIEFSSU1BWDoiLCBhaWNfYXJpbWF4MikpDQpgYGANCg0KDQojIyMgWEdCb29zdA0KDQpTZSBvYnR1dm8gcXVlIGxhIHZhcmlhYmxlIGRpZmZfQ3VhcnRvcyBlcyBjbGFyYW1lbnRlIGxhIHZhcmlhYmxlIG3DoXMgaW1wb3J0YW50ZSBkZWwgbW9kZWxvIHNlZ8O6biBsYSBnYW5hbmNpYSAoR2FpbiDiiYggMC44MikuIEF1bnF1ZSBhcGFyZWNlIG1lbm9zIGZyZWN1ZW50ZW1lbnRlLCBzdXMgZGl2aXNpb25lcyBzb24gbXV5IGVmZWN0aXZhcy4gTWllbnRyYXMgcXVlLCBkaWZmMl9FdmVudG9zIGFwYXJlY2UgY29uIG3DoXMgZnJlY3VlbmNpYSAoRnJlcXVlbmN5IOKJiCAwLjQ5KSB5IGN1YnJlIHVuYSBidWVuYSBwYXJ0ZSBkZSBsb3MgZGF0b3MgKENvdmVyIOKJiCAwLjQzKSwgcGVybyBzdXMgc3BsaXRzIG5vIHNvbiB0YW4gZWZlY3Rpdm9zIGNvbW8gbG9zIGRlIGRpZmZfQ3VhcnRvcy4gUG9yIMO6bHRpbW8sIGRpZmZfQXNpc3RlbnRlcyBlcyBsYSBtZW5vcyBpbXBvcnRhbnRlIGVuIHRvZGFzIGxhcyBtw6l0cmljYXMuDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoeGdib29zdCkNCg0KeV90cmFpbiA8LSB0cmFpbl9kYXRhJGRpZmYyX2RlcnJhbWENCnlfdGVzdCA8LSB0ZXN0X2RhdGEkZGlmZjJfZGVycmFtYQ0KDQpkdHJhaW4gPC0geGdiLkRNYXRyaXgoZGF0YSA9IHhyZWdfdHJhaW4sIGxhYmVsID0geV90cmFpbikNCmR0ZXN0IDwtIHhnYi5ETWF0cml4KGRhdGEgPSB4cmVnX3Rlc3QsIGxhYmVsID0geV90ZXN0KQ0KDQojIEFqdXN0YXIgZWwgbW9kZWxvIFhHQm9vc3QgKHBhcsOhbWV0cm9zIHNpbXBsZXMpDQp4Z2JfbW9kZWwgPC0geGdib29zdCgNCiAgZGF0YSA9IGR0cmFpbiwNCiAgbnJvdW5kcyA9IDEwMCwNCiAgb2JqZWN0aXZlID0gInJlZzpzcXVhcmVkZXJyb3IiLA0KICB2ZXJib3NlID0gMA0KKQ0KDQojIFByZWRlY2lyIGNvbiBYR0Jvb3N0DQpwcmVkX3hnYiA8LSBwcmVkaWN0KHhnYl9tb2RlbCwgZHRlc3QpDQoNCiMgQ2FsY3VsYXIgUk1TRSBwYXJhIFhHQm9vc3QNCnJtc2VfeGdiIDwtIHNxcnQobWVhbigocHJlZF94Z2IgLSB5X3Rlc3QpXjIpKQ0KcHJpbnQocGFzdGUoIlJNU0UgZGUgWEdCb29zdDoiLCBybXNlX3hnYikpDQoNCmltcG9ydGFuY2UgPC0geGdiLmltcG9ydGFuY2UobW9kZWwgPSB4Z2JfbW9kZWwpDQpwcmludChpbXBvcnRhbmNlKQ0KDQpgYGANCg0KDQpgYGB7cn0NCnRyYWluX3NpemUyIDwtIGZsb29yKDAuOCAqIG5yb3codmFyX2RhdGEpKQ0KdHJhaW5fZGF0YTIgPC0gdmFyX2RhdGFbMTp0cmFpbl9zaXplMiwgXQ0KdGVzdF9kYXRhMiA8LSB2YXJfZGF0YVsodHJhaW5fc2l6ZTIgKyAxKTpucm93KHZhcl9kYXRhKSwgXQ0KDQp4X3RyYWluMiA8LSBhcy5tYXRyaXgodHJhaW5fZGF0YSRkaWZmX0N1YXJ0b3MpDQp4X3Rlc3QyIDwtIGFzLm1hdHJpeCh0ZXN0X2RhdGEkZGlmZl9DdWFydG9zKQ0KeV90cmFpbjIgPC0gdHJhaW5fZGF0YSRkaWZmMl9kZXJyYW1hDQp5X3Rlc3QyIDwtIHRlc3RfZGF0YSRkaWZmMl9kZXJyYW1hDQoNCiMgQ3JlYXIgbG9zIG9iamV0b3MgRE1hdHJpeA0KZHRyYWluMiA8LSB4Z2IuRE1hdHJpeChkYXRhID0geF90cmFpbjIsIGxhYmVsID0geV90cmFpbjIpDQpkdGVzdDIgPC0geGdiLkRNYXRyaXgoZGF0YSA9IHhfdGVzdDIsIGxhYmVsID0geV90ZXN0MikNCg0KbnJvdyh4X3RyYWluMikNCmxlbmd0aCh5X3RyYWluMikNCg0KIyBBanVzdGFyIGVsIG1vZGVsbyBYR0Jvb3N0DQp4Z2JfbW9kZWwyIDwtIHhnYm9vc3QoDQogIGRhdGEgPSBkdHJhaW4yLA0KICBucm91bmRzID0gMTAwLA0KICBvYmplY3RpdmUgPSAicmVnOnNxdWFyZWRlcnJvciIsDQogIHZlcmJvc2UgPSAwDQopDQoNCiMgUHJlZGVjaXINCnByZWRfeGdiMiA8LSBwcmVkaWN0KHhnYl9tb2RlbDIsIGR0ZXN0MikNCg0KIyBDYWxjdWxhciBSTVNFDQpybXNlX3hnYjIgPC0gc3FydChtZWFuKChwcmVkX3hnYjIgLSB5X3Rlc3QyKV4yKSkNCmBgYA0KDQoNCmBgYHtyfQ0KdHJhaW5fc2l6ZTMgPC0gZmxvb3IoMC44ICogbnJvdyhkZl9PQ1ZfbnVtZXJpYykpDQp0cmFpbl9kYXRhMyA8LSBkZl9PQ1ZfbnVtZXJpY1sxOnRyYWluX3NpemUzLCBdDQp0ZXN0X2RhdGEzIDwtIGRmX09DVl9udW1lcmljWyh0cmFpbl9zaXplMyArIDEpOm5yb3coZGZfT0NWX251bWVyaWMpLCBdDQoNCnhfdHJhaW4zIDwtIGFzLm1hdHJpeCh0cmFpbl9kYXRhMyRsb2dfQ3VhcnRvcykNCnhfdGVzdDMgPC0gYXMubWF0cml4KHRlc3RfZGF0YTMkbG9nX0N1YXJ0b3MpDQp5X3RyYWluMyA8LSB0cmFpbl9kYXRhMyREZXJyYW1hDQp5X3Rlc3QzIDwtIHRlc3RfZGF0YTMkRGVycmFtYQ0KDQojIENyZWFyIGxvcyBvYmpldG9zIERNYXRyaXgNCmR0cmFpbjMgPC0geGdiLkRNYXRyaXgoZGF0YSA9IHhfdHJhaW4zLCBsYWJlbCA9IHlfdHJhaW4zKQ0KZHRlc3QzIDwtIHhnYi5ETWF0cml4KGRhdGEgPSB4X3Rlc3QzLCBsYWJlbCA9IHlfdGVzdDMpDQoNCm5yb3coeF90cmFpbjMpDQpsZW5ndGgoeV90cmFpbjMpDQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gWEdCb29zdA0KeGdiX21vZGVsMyA8LSB4Z2Jvb3N0KA0KICBkYXRhID0gZHRyYWluMywNCiAgbnJvdW5kcyA9IDEwMCwNCiAgb2JqZWN0aXZlID0gInJlZzpzcXVhcmVkZXJyb3IiLA0KICB2ZXJib3NlID0gMA0KKQ0KDQojIFByZWRlY2lyDQpwcmVkX3hnYjMgPC0gcHJlZGljdCh4Z2JfbW9kZWwzLCBkdGVzdDMpDQoNCiMgQ2FsY3VsYXIgUk1TRQ0Kcm1zZV94Z2IzIDwtIHNxcnQobWVhbigocHJlZF94Z2IzIC0geV90ZXN0MyleMikpDQpwcmludChwYXN0ZSgiUk1TRSBkZSBYR0Jvb3N0IChzb2xvIGxvZyBjdWFydG9zKToiLCBybXNlX3hnYjMpKQ0KYGBgDQoNCg0KIyMjIFBhcsOhbWV0cm9zDQoNCkNvbiBiYXNlIGVuIGxvcyByZXN1bHRhZG9zLCBzZSBkZXRlcm1pbmEgcXVlIGVsIG1vZGVsbyBBUklNQVggZXMgcHJlZmVyaWJsZSBwb3JxdWUgY29tYmluYSBlbCBwb2RlciBleHBsaWNhdGl2byBkZSB2YXJpYWJsZXMgZXh0ZXJuYXMgY29uIHVuYSBlc3RydWN0dXJhIHRlbXBvcmFsIHJvYnVzdGEsIG9mcmVjaWVuZG8gZWwgbWVub3IgQUlDIHkgUk1TRSBlbnRyZSB0b2RvcyBsb3MgbW9kZWxvcyBldmFsdWFkb3MuIEVzdG8gbG8gY29udmllcnRlIGVuIGxhIG1lam9yIG9wY2nDs24gdGFudG8gcGFyYSBhanVzdGUgY29tbyBwYXJhIHByZWRpY2Npw7NuLg0KDQoqIEVsIG1vZGVsbyBBUklNQVggcHJlc2VudGEgdW4gdmFsb3IgZGUgQUlDID0gNjU1LjEzLCBxdWUgZXMgc2lnbmlmaWNhdGl2YW1lbnRlIG1lbm9yIHF1ZSBlbCBkZSBjdWFscXVpZXIgb3RybyBtb2RlbG8gZXZhbHVhZG8gKGxvcyBBUklNQSBzaW1wbGVzLCBlbCBtb2RlbG8gVkFSIG8gaW5jbHVzbyBvdHJhcyB2ZXJzaW9uZXMgZGUgQVJJTUFYKS4NCg0KKiAgRWwgQVJJTUFYIG5vIHNvbG8gYWp1c3RhIG1lam9yIGEgbG9zIGRhdG9zIGhpc3TDs3JpY29zLCBzaW5vIHF1ZSB0YW1iacOpbiBwcmVkaWNlIGNvbiBtYXlvciBwcmVjaXNpw7NuIGxvcyB2YWxvcmVzIGZ1dHVyb3MgZW4gY29tcGFyYWNpw7NuIGNvbiBtb2RlbG9zIGRlIGFwcmVuZGl6YWplIGF1dG9tw6F0aWNvIHkgdmVyc2lvbmVzIHJlZHVjaWRhcyBkZWwgcHJvcGlvIEFSSU1BWC4NCg0KYGBge3J9DQpkZl9BSUMgPC0gZGF0YS5mcmFtZSgNCiAgTW9kZWxvID0gYygiQVJJTUEoMSwyLDEpIiwgIkFSSU1BKDAsMiwxKSIsICJBUklNQSgxLDIsMCkiLCAiQVJJTUEoMCwyLDApIiwgIlZBUiIsICJBUklNQVgiLCAiQVJJTUFYIEN1YXJ0byIpLA0KICBBSUNfVmFsb3IgPSBjKEFJQyhtb2RlbG9fYXJpbWEpLA0KICAgICAgICAgIEFJQyhtb2RlbG9fYXJpbWExKSwNCiAgICAgICAgICBBSUMobW9kZWxvX2FyaW1hMiksDQogICAgICAgICAgQUlDKGZpdDIpLA0KICAgICAgICAgIEFJQyh2YXJfbW9kZWw0KSwNCiAgICAgICAgICBBSUMoYXJpbWF4X21vZGVsKSwNCiAgICAgICAgICBBSUMoYXJpbWF4X21vZGVsMikNCikpDQoNCnByaW50KGRmX0FJQykNCg0KDQpkZl9STVNFIDwtIGRhdGEuZnJhbWUoDQogIE1vZGVsbyA9IGMoIkFSSU1BWCIsICJBUklNQVggQ3VhcnRvIiwgIlhHQm9vc3QiLCAiWEdCb29zdCBDdWFydG8iKSwNCiAgUk1TRV9WYWxvciA9IGMocm1zZV9hcmltYXgsIHJtc2VfYXJpbWF4Miwgcm1zZV94Z2IsIHJtc2VfeGdiMikNCikNCg0KcHJpbnQoZGZfUk1TRSkNCmBgYA0KDQojIyMgUHJvbsOzc3RpY28NCg0KIyMjIyBGb3JlY2FzdCBNb2RlbG8gQXJpbWENCg0KU2UgcmVhbGl6YW4gcHJlZGljY2lvbmVzIHBhcmEgbG9zIHNpZ3VpZW50ZXMgb2NobyBwZXJpb2RvcyBjb24gZWwgbW9kZWxvIEFSSU1BKDAsMiwxKSwgeWEgcXVlIG9idHV2byBsb3MgbWVqb3JlcyBwYXJhbWV0cm9zIHkgYWwgc2VyIHVuIG1vZGVsbyB1bml2YXJpYWRvIHF1ZSDDum5pY2FtZW50ZSBjb25zaWRlcmEgbGEgc2VyaWUgZGUgbGEgZGVycmFtYSBlY29uw7NtaWNhLCBvZnJlY2UgcHJlZGljY2lvbmVzIGNvbiBpbnRlcnZhbG9zIGRlIGNvbmZpYW56YSBjb25zaWRlcmFibGVtZW50ZSBhbXBsaW9zLCBsbyBxdWUgcmVmbGVqYSB1bmEgYWx0YSBpbmNlcnRpZHVtYnJlIGVuIHN1cyBlc3RpbWFjaW9uZXMuIEF1bnF1ZSBjYXB0YSBsYSB0ZW5kZW5jaWEgZ2VuZXJhbCBkZSBsYSBzZXJpZSwgc3UgY2FwYWNpZGFkIHBhcmEgZXhwbGljYXIgZmx1Y3R1YWNpb25lcyBwdW50dWFsZXMgZXMgbGltaXRhZGEgZGViaWRvIGEgbGEgYXVzZW5jaWEgZGUgdmFyaWFibGVzIGV4cGxpY2F0aXZhcyBhZGljaW9uYWxlcy4gDQoNCmBgYHtyfQ0KbGlicmFyeShmb3JlY2FzdCkNCg0KZm9yZWNhc3RfYXJpbWEgPC0gZm9yZWNhc3Q6OmZvcmVjYXN0KG1vZGVsb19hcmltYTEsIGggPSA4KQ0KDQojIEdyYWZpY2FyIGVsIHByb27Ds3N0aWNvIGNvbiBpbnRlcnZhbG9zIGRlIGNvbmZpYW56YQ0KYXV0b3Bsb3QoZm9yZWNhc3RfYXJpbWEpICsNCiAgZ2d0aXRsZSgiUHJvbsOzc3RpY28gZGUgRGVycmFtYSIpICsNCiAgeGxhYigiVGllbXBvIikgKw0KICB5bGFiKCJEZXJyYW1hIikNCg0KcHJpbnQoZm9yZWNhc3RfYXJpbWEpDQpgYGANCg0KDQojIyMjIEZvcmVjYXN0IE1vZGVsbyBWQVINCg0KU2UgcmVhbGl6YW4gcHJlZGljY2lvbmVzIHBhcmEgbG9zIHNpZ3VpZW50ZXMgb2NobyBwZXJpb2RvcyBjb24gZWwgbW9kZWxvIFZBUiwgcXVlIGluY29ycG9yYSBsYSBzZXJpZSBkZSBuw7ptZXJvIGRlIGV2ZW50b3MgY29tbyDDum5pY2EgdmFyaWFibGUgZXhwbGljYXRpdmEgZGViaWRvIGEgbGEgY29saW5lYWxpZGFkIGlkZW50aWZpY2FkYSBjb24gbGFzIGRlbcOhcyB2YXJpYWJsZXMsIHRhbWJpw6luIG11ZXN0cmEgcHJlZGljY2lvbmVzIGNvbiBtw6FyZ2VuZXMgYW1wbGlvcyBkZSBpbmNlcnRpZHVtYnJlIHkgdW5hIGNvbnNpZGVyYWJsZSB2YXJpYWJpbGlkYWQgZW4gbG9zIHZhbG9yZXMgcHJvbm9zdGljYWRvcyBwYXJhIGxhIGRlcnJhbWEuIA0KDQpgYGB7cn0NCmxpYnJhcnkoZm9yZWNhc3QpDQoNCmZvcmVjYXN0X3ZhciA8LSBmb3JlY2FzdDo6Zm9yZWNhc3QodmFyX21vZGU1bCwgaCA9IDgpDQoNCiMgR3JhZmljYXIgZWwgcHJvbsOzc3RpY28gY29uIGludGVydmFsb3MgZGUgY29uZmlhbnphDQphdXRvcGxvdChmb3JlY2FzdF92YXIpICsNCiAgZ2d0aXRsZSgiUHJvbsOzc3RpY28gZGUgRGVycmFtYSIpICsNCiAgeGxhYigiVGllbXBvIikgKw0KICB5bGFiKCJEZXJyYW1hIikNCg0KcHJpbnQoZm9yZWNhc3RfdmFyKQ0KYGBgDQoNCg0KIyMjIyBGb3JlY2FzdCBNb2RlbG8gQVJJTUFYDQoNClNlIHJlYWxpemFuIHByZWRpY2Npb25lcyBwYXJhIDIwMjUgY29uIGVsIG1vZGVsbyBzZWxlY2Npb25hZG8gZGUgQVJJTUFYIGp1bnRvIGNvbiBlbCBpbXBhY3RvIGRlIHVuIGF1bWVudG8gZGVsIDI1JSwgNTAlIHkgNzUlIGRlIGxhIHZhcmlhYmxlICoqQ3VhcnRvcyBPY3VwYWRvcyoqIHBvciBsb3MgYXNpc3RlbnRlcyBkZSBsb3MgZXZlbnRvcyBvcmdhbml6YWRvcyBwb3IgbGEgT0NWLiBBbnRlIGxvIGN1YWwsIHNlIG9ic2VydmEgcXVlIGV4aXN0ZSB1bmEgcmVsYWNpw7NuIHBvc2l0aXZhIGVudHJlIGVsIGluY3JlbWVudG8gZW4gbGEgdmFyaWFibGUgIkN1YXJ0b3MgT2N1cGFkb3MiIHkgbGEgZGVycmFtYSBlY29uw7NtaWNhIHByb3llY3RhZGEgcG9yIGVsIG1vZGVsbyBBUklNQVguIEEgbWVkaWRhIHF1ZSBlbCBhdW1lbnRvIGVuIGxvcyAiQ3VhcnRvcyBPY3VwYWRvcyIgZXMgbWF5b3IsIGVsIG1vZGVsbyBwcmVkaWNlIHVuYSBkZXJyYW1hIGVjb27Ds21pY2Egc2lnbmlmaWNhdGl2YW1lbnRlIG3DoXMgYWx0YS4gTGFzIGZsdWN0dWFjaW9uZXMgaW5pY2lhbGVzIGVuIGxhcyBwcmVkaWNjaW9uZXMgcG9kcsOtYW4gZGViZXJzZSBhIGxhIGRpbsOhbWljYSBwcm9waWEgZGVsIG1vZGVsbyBBUklNQVggeSBhIGxhIGluZmx1ZW5jaWEgZGUgbGFzIG90cmFzIHZhcmlhYmxlcyBleHBsaWNhdGl2YXMgKE51bUV2ZW50b3MgeSBBc2lzdGVudGVzKSwgcGVybyBsYSB0ZW5kZW5jaWEgYSBsYSBlc3RhYmlsaXphY2nDs24gc3VnaWVyZSB1biBlZmVjdG8gYSBsYXJnbyBwbGF6byBkZWwgbml2ZWwgZGUgb2N1cGFjacOzbiBlbiBsYSBlY29ub23DrWEgbG9jYWwuDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGZvcmVjYXN0KQ0KDQpwcmVkX2RmIDwtIGRhdGEuZnJhbWUoDQogIEZlY2hhID0gc2VxKGFzLkRhdGUoIjIwMjUtMDEtMDEiKSwgYnkgPSAibW9udGgiLCBsZW5ndGgub3V0ID0gbGVuZ3RoKHByZWQkcHJlZCkpLA0KICBQcmVkaWNjaW9uID0gcHJlZCRwcmVkLA0KICBJQ19pbmYgPSBwcmVkJHByZWQgLSAxLjk2ICogcHJlZCRzZSwNCiAgSUNfc3VwID0gcHJlZCRwcmVkICsgMS45NiAqIHByZWQkc2UNCikNCg0KIyBHcmFmaWNhciBwcmVkaWNjaW9uZXMgY29uIGludGVydmFsb3MgZGUgY29uZmlhbnphIA0KZ2dwbG90KHByZWRfZGYsIGFlcyh4ID0gRmVjaGEpKSArDQogIGdlb21fbGluZShhZXMoeSA9IFByZWRpY2Npb24pLCBjb2xvciA9ICJyZWQiKSArDQogIGdlb21fcmliYm9uKGFlcyh5bWluID0gSUNfaW5mLCB5bWF4ID0gSUNfc3VwKSwgZmlsbCA9ICJwaW5rIiwgYWxwaGEgPSAwLjMpICsNCiAgbGFicyh0aXRsZSA9ICJQcmVkaWNjacOzbiBBUklNQVggY29uIElDIDk1JSAoMjAyNSkiLA0KICAgICAgIHggPSAiRmVjaGEiLCB5ID0gImRpZmYyX2RlcnJhbWEiLA0KICAgICAgIGNhcHRpb24gPSAiUm9qbzogUHJlZGljY2nDs24gfCBCYW5kYSByb3NhOiBJbnRlcnZhbG8gOTUlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCmBgYHtyIHByZWRpY2Npb25lcywgcmVzdWx0cz0nYXNpcyd9DQpsaWJyYXJ5KGZvcmVjYXN0KSANCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpYmJsZSkNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQoNCmZ1dHVyZV9tb250aHMgPC0gMTIgKiAoMjAyNiAtIDIwMjQpICANCg0KIyBDcmVhciBsb3MgcmVncmVzb3JlcyBmdXR1cm9zIHBhcmEgbG9zIHByb27Ds3N0aWNvcw0KeHJlZ19mdXR1cmUgPC0gbWF0cml4KHJlcCh0YWlsKHhyZWdfdHJhaW4sIDEpLCBmdXR1cmVfbW9udGhzKSwgbmNvbCA9IG5jb2woeHJlZ190cmFpbiksIGJ5cm93ID0gVFJVRSkNCmNvbG5hbWVzKHhyZWdfZnV0dXJlKSA8LSBjb2xuYW1lcyh4cmVnX3RyYWluKQ0KDQojIENyZWFyIGxvcyBkaXN0aW50b3MgZXNjZW5hcmlvcw0KZXNjZW5hcmlvcyA8LSBsaXN0KA0KICAiUGVzaW1pc3RhIiA9IHsgdG1wIDwtIHhyZWdfZnV0dXJlOyB0bXBbLCAiZGlmZl9DdWFydG9zIl0gPC0gdG1wWywgImRpZmZfQ3VhcnRvcyJdICogMC43NTsgdG1wIH0sDQogICJPcHRpbWlzdGEiID0geyB0bXAgPC0geHJlZ19mdXR1cmU7IHRtcFssICJkaWZmX0N1YXJ0b3MiXSA8LSB0bXBbLCAiZGlmZl9DdWFydG9zIl0gKiAxLjI1OyB0bXAgfSwNCiAgIjI1JSIgPSB7IHRtcCA8LSB4cmVnX2Z1dHVyZTsgdG1wWywgImRpZmZfQ3VhcnRvcyJdIDwtIHRtcFssICJkaWZmX0N1YXJ0b3MiXSAqIDEuMjU7IHRtcCB9LA0KICAiNTAlIiA9IHsgdG1wIDwtIHhyZWdfZnV0dXJlOyB0bXBbLCAiZGlmZl9DdWFydG9zIl0gPC0gdG1wWywgImRpZmZfQ3VhcnRvcyJdICogMS41MDsgdG1wIH0sDQogICI3NSUiID0geyB0bXAgPC0geHJlZ19mdXR1cmU7IHRtcFssICJkaWZmX0N1YXJ0b3MiXSA8LSB0bXBbLCAiZGlmZl9DdWFydG9zIl0gKiAxLjc1OyB0bXAgfQ0KKQ0KDQpmb3IgKG5vbWJyZSBpbiBuYW1lcyhlc2NlbmFyaW9zKSkgew0KICBwcmVkIDwtIHByZWRpY3QoYXJpbWF4X21vZGVsLCBuZXd4cmVnID0gZXNjZW5hcmlvc1tbbm9tYnJlXV0sIG4uYWhlYWQgPSBmdXR1cmVfbW9udGhzKQ0KICANCiAgdGFibGEgPC0gdGliYmxlKA0KICAgIE1lcyA9IHNlcShhcy5EYXRlKCIyMDI0LTAxLTAxIiksIGJ5ID0gIm1vbnRoIiwgbGVuZ3RoLm91dCA9IGZ1dHVyZV9tb250aHMpLA0KICAgIFByZWRpY2Npb24gPSByb3VuZChwcmVkJHByZWQsIDIpLA0KICAgIExJID0gcm91bmQocHJlZCRzZSAqIHFub3JtKDAuMDI1LCBsb3dlci50YWlsID0gVFJVRSkgKyBwcmVkJHByZWQsIDIpLA0KICAgIExTID0gcm91bmQocHJlZCRzZSAqIHFub3JtKDAuOTc1LCBsb3dlci50YWlsID0gVFJVRSkgKyBwcmVkJHByZWQsIDIpDQogICkgJT4lDQogICAgZmlsdGVyKGZvcm1hdChNZXMsICIlWSIpID09ICIyMDI1IikNCg0KICAjIEltcHJpbWlyIGVuY2FiZXphZG8gY29tbyByZXN1bHRhZG8gTWFya2Rvd24gaW50ZXJwcmV0YWJsZQ0KICBjYXQoIiMjIyBFc2NlbmFyaW86Iiwgbm9tYnJlLCAiXG5cbiIpDQoNCiAgIyBHZW5lcmFyIHRhYmxhIHkgbW9zdHJhcmxhIGV4cGzDrWNpdGFtZW50ZQ0KICB0YWJsYV9rYWJsZSA8LSB0YWJsYSAlPiUNCiAgICBrYWJsZShjYXB0aW9uID0gcGFzdGUoIlByZWRpY2Npw7NuIG1lbnN1YWwgYmFqbyBlbCBlc2NlbmFyaW8iLCBub21icmUsICIoYcOxbyAyMDI1KSIpLCBhbGlnbiA9ICJsY2NjIikgJT4lDQogICAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGZvbnRfc2l6ZSA9IDEyKQ0KDQogIHByaW50KHRhYmxhX2thYmxlKQ0KfQ0KYGBgDQoNCg0KYGBge3J9DQojIENyZWFyIHVuYSBsaXN0YSBwYXJhIGFsbWFjZW5hciBsYXMgcHJlZGljY2lvbmVzDQpwcmVkaWNjaW9uZXNfZXNjZW5hcmlvcyA8LSBsaXN0KCkNCg0KIyBFc2NlbmFyaW9zIHF1ZSBzZSBxdWllcmVuIGdyYWZpY2FyDQplc2NlbmFyaW9zX2FfZ3JhZmljYXIgPC0gYygiMjUlIiwgIjUwJSIsICI3NSUiKQ0KDQojIE9idGVuZXIgbGFzIHByZWRpY2Npb25lcyBwYXJhIGNhZGEgZXNjZW5hcmlvDQpmb3IgKG5vbWJyZSBpbiBlc2NlbmFyaW9zX2FfZ3JhZmljYXIpIHsNCiAgcHJlZCA8LSBwcmVkaWN0KGFyaW1heF9tb2RlbCwgbmV3eHJlZyA9IGVzY2VuYXJpb3NbW25vbWJyZV1dLCBuLmFoZWFkID0gZnV0dXJlX21vbnRocykNCiAgDQogIGRmX3ByZWQgPC0gdGliYmxlKA0KICAgIE1lcyA9IHNlcShhcy5EYXRlKCIyMDI0LTAxLTAxIiksIGJ5ID0gIm1vbnRoIiwgbGVuZ3RoLm91dCA9IGZ1dHVyZV9tb250aHMpLA0KICAgIEVzY2VuYXJpbyA9IG5vbWJyZSwNCiAgICBQcmVkaWNjaW9uID0gcHJlZCRwcmVkDQogICkgJT4lDQogICAgZmlsdGVyKGZvcm1hdChNZXMsICIlWSIpID09ICIyMDI1IikgICMgc29sbyAyMDI1DQogIA0KICBwcmVkaWNjaW9uZXNfZXNjZW5hcmlvc1tbbm9tYnJlXV0gPC0gZGZfcHJlZA0KfQ0KDQpkZl90b2Rvc19lc2NlbmFyaW9zIDwtIGJpbmRfcm93cyhwcmVkaWNjaW9uZXNfZXNjZW5hcmlvcykNCg0KIyBHcmFmaWNhciBjb24gZ2dwbG90Mg0KZ2dwbG90KGRmX3RvZG9zX2VzY2VuYXJpb3MsIGFlcyh4ID0gTWVzLCB5ID0gUHJlZGljY2lvbiwgY29sb3IgPSBFc2NlbmFyaW8pKSArDQogIGdlb21fbGluZShzaXplID0gMS4yKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUHJlZGljY2lvbmVzIG1lbnN1YWxlcyBwYXJhIDIwMjUgYmFqbyBkaXN0aW50b3MgZXNjZW5hcmlvcyIsDQogICAgeCA9ICJNZXMiLA0KICAgIHkgPSAiUHJlZGljY2nDs24iLA0KICAgIGNvbG9yID0gIkVzY2VuYXJpbyINCiAgKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTQpDQpgYGANCg0KDQojIyAqKlR1cmlzbW8gZGUgRXZlbnRvcyBFbnRyZXRlbmltaWVudG86IFBlcmZpbGFtaWVudG8gQXNpc3RlbnRlcyBQYWwgTm9ydGUqKg0KDQojIyMgU2VnbWVudGFjacOzbiBjb24gSy1tZWFucw0KDQpBcGxpY2Ftb3MgSy1tZWFucyBwYXJhIHNlZ21lbnRhciBhdXRvbcOhdGljYW1lbnRlIGEgbG9zIGFzaXN0ZW50ZXMgc2Vnw7puIHZhcmlhYmxlcyBudW3DqXJpY2FzIGNvbW8gZ2FzdG8gdG90YWwsIGJvbGV0b3MsIGFsaW1lbnRvcywgZXRjLg0KDQoqIFNlIGlkZW50aWZpY2FuIGdydXBvcyBuYXR1cmFsZXMgZGUgY29uc3VtaWRvcmVzIGNvbiBjYXJhY3RlcsOtc3RpY2FzIHNpbWlsYXJlcyBzaW4gdXNhciBldGlxdWV0YXMgcHJlZGV0ZXJtaW5hZGFzLg0KDQpgYGB7cn0NCiMjIyBFbGJvdyBNZXRob2Qg4oCTIE7Dum1lcm8gw5NwdGltbyBkZSBDbMO6c3RlcmVzDQpsaWJyYXJ5KGZhY3RvZXh0cmEpDQoNCmRmX2NsdXN0ZXIgPC0gZGZfcGFsbm9ydGVfbnVtZXJpYyAlPiUgc2VsZWN0KGJvbGV0b3MsIGFsb2phbWllbnRvLCBhbGltZW50b3MsIHRyYW5zcG9ydGUsIGdhc3RvX3RvdGFsKQ0KZGZfY2x1c3RlciA8LSBzY2FsZShkZl9jbHVzdGVyKQ0KDQpmdml6X25iY2x1c3QoZGZfY2x1c3Rlciwga21lYW5zLCBtZXRob2QgPSAid3NzIikNCmBgYA0KDQpFbCBncsOhZmljbyBtdWVzdHJhIGPDs21vIGRpc21pbnV5ZSBsYSBzdW1hIHRvdGFsIGRlIGxvcyBlcnJvcmVzIGFsIGN1YWRyYWRvIChXU1MpIGEgbWVkaWRhIHF1ZSBhdW1lbnRhbW9zIGVsIG7Dum1lcm8gZGUgY2zDunN0ZXJlcyBrLg0KICANCiAgKiBTZSBvYnNlcnZhIHVuYSBjYcOtZGEgbXV5IHByb251bmNpYWRhIGRlIGs9MSBhIGs9MiwgeSBkZXNwdcOpcyBzZSBlbXBpZXphIGEgZXN0YWJpbGl6YXIsIGVzcGVjaWFsbWVudGUgZGVzcHXDqXMgZGUgaz0zIG8gaz00Lg0KICANCiAgKiBFbCDigJxjb2Rv4oCdIHNlIGZvcm1hIGFscmVkZWRvciBkZSBrID0gMywgbG8gcXVlIGluZGljYSBxdWUgZXNlIGVzIHVuIGJ1ZW4gbsO6bWVybyBkZSBjbMO6c3RlcmVzLCB5YSBxdWUgYcOxYWRpciBtw6FzIG5vIHJlZHVjZSBzaWduaWZpY2F0aXZhbWVudGUgZWwgZXJyb3IuDQoNCg0KIyMjIFZpc3VhbGl6YWNpw7NuIGRlIGxvcyBDbMO6c3RlcmVzIChLLW1lYW5zLCBrID0gMykNCg0KKiDwn5S1IENsw7pzdGVyIDEgKGF6dWwpOiBQYXJlY2Ugc2VyIGVsIG3DoXMgZ3JhbmRlIHkgZGlzcGVyc28sIHByb2JhYmxlbWVudGUgaW5jbHV5ZSBwZXJmaWxlcyBkZSBnYXN0byBhbHRvIG8gYXNpc3RlbmNpYSBjb21wbGV0YS4NCg0KKiDwn5+hIENsw7pzdGVyIDIgKGFtYXJpbGxvKTogRXMgbcOhcyBjb21wYWN0bywgcXVpesOhIHJlcHJlc2VudGEgdXN1YXJpb3MgaW50ZXJtZWRpb3MgZW4gZ2FzdG8geSBkw61hcy4NCg0KKiDirJsgQ2zDunN0ZXIgMyAoZ3Jpcyk6IFRhbWJpw6luIGNvbXBhY3RvIHBlcm8gY29uIHRlbmRlbmNpYSBhIGVzdGFyIGVuIHpvbmFzIGRlIG1lbm9yIGdhc3RvIG8gcGFydGljaXBhY2nDs24uDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0Ka21vZGVsIDwtIGttZWFucyhkZl9jbHVzdGVyLCBjZW50ZXJzID0gMywgbnN0YXJ0ID0gMjUpDQpkZl9wYWxub3J0ZSRjbHVzdGVyIDwtIGFzLmZhY3RvcihrbW9kZWwkY2x1c3RlcikNCg0KZnZpel9jbHVzdGVyKGttb2RlbCwgZGF0YSA9IGRmX2NsdXN0ZXIsIGdlb20gPSAicG9pbnQiLCBlbGxpcHNlLnR5cGUgPSAibm9ybSIsIHBhbGV0dGUgPSAiamNvIikNCmBgYA0KDQoNCiMjIyBQZXJmaWxhbWllbnRvIGRlIGNsw7pzdGVyZXMNCg0KVW5hIHZleiBzZWdtZW50YWRvcyBsb3MgdXN1YXJpb3MsIGFuYWxpemFtb3MgY2FkYSBjbMO6c3RlciBwYXJhIGVudGVuZGVyIHN1IHBlcmZpbCAoY3XDoW50byBnYXN0YW4sIHF1w6kgZWRhZCB0aWVuZW4geSBjdcOhbCBlcyBzdSBvY3VwYWNpw7NuIG3DoXMgY29tw7puKToNCg0KKipDbMO6c3RlciAxIOKAkyBNYXlvciBHYXN0byAoTWVyY2FkbyBFc3RyZWxsYSkqKg0KDQoqIEdhc3RvIHByb21lZGlvIG3DoXMgYWx0bzogJDksOTE2IE1YTg0KDQoqIE1heW9yIHRhbWHDsW8gZGVsIGdydXBvOiAzNyBwZXJzb25hcw0KDQoqIFBlcmZpbDogRW1wbGVhZG9zIGVudHJlIDI2IHkgMzUgYcOxb3MgcXVlIHByb2JhYmxlbWVudGUgYXNpc3RpZXJvbiB0b2RvcyBsb3MgZMOtYXMgeSBjb21wcmFyb24gcGFxdWV0ZXMgY29tcGxldG9zLg0KDQoNCioqQ2zDunN0ZXIgMiDigJMgR2FzdG8gTWVkaW8gKE1lcmNhZG8gRXN0YWJsZSkqKg0KDQoqIEdhc3RvIHByb21lZGlvIGludGVybWVkaW86ICQ4LDQ3NSBNWE4NCg0KKiBHcnVwbyBwZXF1ZcOxbyAoMTIgcGVyc29uYXMpDQoNCiogUGVyZmlsOiBTaW1pbGFyIGEgQ2zDunN0ZXIgMSBwZXJvIGNvbiBtZW5vciBkaXNwb3NpY2nDs24gZGUgZ2FzdG8uIFB1ZWRlIHRyYXRhcnNlIGRlIHVzdWFyaW9zIGNvbiBwcmVzdXB1ZXN0byBtb2RlcmFkbywgcGVybyBhw7puIGNvbXByb21ldGlkb3MuIFNlIGxlcyBwdWVkZSBjb252ZXJ0aXIgZW4gbWVyY2FkbyBlc3RyZWxsYSBjb24gZXN0cmF0ZWdpYXMgZGUgdXBzZWxsaW5nIG8gY3Jvc3Mtc2VsbGluZy4NCg0KDQoqKkNsw7pzdGVyIDMg4oCTIEJham8gR2FzdG8gKE1lcmNhZG8gUG90ZW5jaWFsKSoqDQoNCiogR2FzdG8gcHJvbWVkaW8gYmFqbzogJDUsODY1IE1YTg0KDQoqIEdydXBvIGNvbnNpZGVyYWJsZSAoMjkgcGVyc29uYXMpDQoNCiogTWlzbW8gcmFuZ28gZGUgZWRhZCB5IG9jdXBhY2nDs24gcXVlIGxvcyBvdHJvcywgcGVybyBtZW5vciBnYXN0by4NCg0KKiBQZXJmaWw6IEVzdMOhbiBpbnRlcmVzYWRvcywgcGVybyBhw7puIG5vIGdhc3RhbiB0YW50by4gU2UgbGVzIHB1ZWRlIGluY2VudGl2YXIgY29uIHByb21vY2lvbmVzIGRlIG3Dumx0aXBsZXMgZMOtYXMsIGV4cGVyaWVuY2lhcyBjb21wYXJ0aWRhcyBvIHVwZ3JhZGVzLg0KDQoNClBvciBsbyB0YW50bywgbGEgc2VnbWVudGFjacOzbiBtZWRpYW50ZSBLLW1lYW5zIGlkZW50aWZpY8OzIHRyZXMgY2zDunN0ZXJlcyBkZSBhc2lzdGVudGVzOiBlbCBtZXJjYWRvIGVzdHJlbGxhICgzNyBwZXJzb25hcywgJDksOTE2IE1YTiBkZSBnYXN0byBwcm9tZWRpbyksIGVsIG1lcmNhZG8gaW50ZXJtZWRpbyAoMTIgcGVyc29uYXMsICQ4LDQ3NSBNWE4pIHkgZWwgbWVyY2FkbyBwb3RlbmNpYWwgKDI5IHBlcnNvbmFzLCAkNSw4NjUgTVhOKS4gQXVucXVlIGNvbXBhcnRlbiB1biBwZXJmaWwgZGVtb2dyw6FmaWNvIHNpbWlsYXIg4oCUZW1wbGVhZG9zIGRlIGVudHJlIDI2IHkgMzUgYcOxb3PigJQsIHN1IGNvbXBvcnRhbWllbnRvIGZpbmFuY2llcm8gZXMgZWwgcHJpbmNpcGFsIGRpZmVyZW5jaWFkb3IsIGxvIHF1ZSBzdWdpZXJlIHF1ZSBsYSBzZWdtZW50YWNpw7NuIGRlYmUgY2VudHJhcnNlIGVuIGjDoWJpdG9zIGRlIGNvbnN1bW8gbcOhcyBxdWUgZW4gY2FyYWN0ZXLDrXN0aWNhcyBzb2Npb2RlbW9ncsOhZmljYXMuDQoNCmBgYHtyfQ0KZGZfcGFsbm9ydGUgJT4lDQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgR2FzdG9fUHJvbWVkaW8gPSBtZWFuKGdhc3RvX3RvdGFsLCBuYS5ybSA9IFRSVUUpLA0KICAgIENvdW50ID0gbigpLA0KICAgIEVkYWRfbW9kYSA9IG5hbWVzKHNvcnQodGFibGUoYMK/Q3XDoWwgZXMgc3UgcmFuZ28gZGUgZWRhZD8gKFByZWd1bnRhIGRlbW9ncsOhZmljYSlgKSwgZGVjcmVhc2luZyA9IFRSVUUpKVsxXSwNCiAgICBPY3VwYWNpb25fbW9kYSA9IG5hbWVzKHNvcnQodGFibGUoYMK/Q3XDoWwgZXMgc3Ugb2N1cGFjacOzbiBwcmluY2lwYWw/YCksIGRlY3JlYXNpbmcgPSBUUlVFKSlbMV0NCiAgKQ0KYGBgDQoNCg0KIyMjIEFuw6FsaXNpcyBkZSBNZXJjYWRvIEVzdHJlbGxhIHkgUG90ZW5jaWFsDQoNCkxvcyBtb2RlbG9zIGRlIGNsYXNpZmljYWNpw7NuIGNvbiBSYW5kb20gRm9yZXN0IGNvbmZpcm1hcm9uIHF1ZSBlbCBnYXN0byBlbiBhbG9qYW1pZW50byB5IGxvcyBkw61hcyBkZSBhc2lzdGVuY2lhIHNvbiBsb3MgcHJpbmNpcGFsZXMgZGV0ZXJtaW5hbnRlcyBwYXJhIGlkZW50aWZpY2FyIGEgbG9zIGNsaWVudGVzIGVzdHJlbGxhLCBxdWllbmVzIHZhbG9yYW4gbGEgY29tb2RpZGFkIHkgbGEgZXhwZXJpZW5jaWEgY29tcGxldGEuIEVuIGNvbnRyYXN0ZSwgZW50cmUgbG9zIGNsaWVudGVzIHBvdGVuY2lhbGVzLCBsYSB2YXJpYWJsZSBtw6FzIHJlbGV2YW50ZSBmdWUgbGEgZHVyYWNpw7NuIGRlIGxhIGFzaXN0ZW5jaWEsIGxvIHF1ZSBpbmRpY2EgdW5hIGV0YXBhIGluY2lwaWVudGUgZW4gc3UgcmVsYWNpw7NuIGNvbiBlbCBldmVudG8geSB1bmEgb3BvcnR1bmlkYWQgZGUgZGVzYXJyb2xsbyBoYWNpYSBzZWdtZW50b3MgZGUgbWF5b3IgdmFsb3IuDQoNCkNhYmUgZGVzdGFjYXIgcXVlLCBzZSBjcmVhcm9uIGRvcyBldGlxdWV0YXMgcGFyYSBwcmVkZWNpciBhIHF1w6kgcGVyZmlsIHRpZW5kZSB1biBudWV2byB1c3VhcmlvOg0KDQoxLiBNZXJjYWRvIEVzdHJlbGxhOiB0b3AgMjUlIGVuIGdhc3RvIHRvdGFsIChjdWFydGlsIDc1IG8gbcOhcykuDQoNCjIuIE1lcmNhZG8gUG90ZW5jaWFsOiBnYXN0YW4gbcOhcyBxdWUgZWwgcHJvbWVkaW8gcGVybyBtZW5vcyBxdWUgbG9zIGVzdHJlbGxhLCB5IGFzaXN0ZW4gbWVub3MgZGUgMyBkw61hcy4NCg0KYGBge3J9DQpjdWFydGlsXzc1IDwtIHF1YW50aWxlKGRmX3BhbG5vcnRlJGdhc3RvX3RvdGFsLCAwLjc1LCBuYS5ybSA9IFRSVUUpDQpwcm9tZWRpb19nYXN0byA8LSBtZWFuKGRmX3BhbG5vcnRlJGdhc3RvX3RvdGFsLCBuYS5ybSA9IFRSVUUpDQoNCmRmX3BhbG5vcnRlIDwtIGRmX3BhbG5vcnRlICU+JQ0KICBtdXRhdGUoDQogICAgbWVyY2Fkb19lc3RyZWxsYSA9IGlmZWxzZShnYXN0b190b3RhbCA+PSBjdWFydGlsXzc1LCAiU8OtIiwgIk5vIiksDQogICAgbWVyY2Fkb19wb3RlbmNpYWwgPSBjYXNlX3doZW4oDQogICAgICBnYXN0b190b3RhbCA+PSBwcm9tZWRpb19nYXN0byAmIGdhc3RvX3RvdGFsIDwgY3VhcnRpbF83NSAmIGRpYXMgPCAzIH4gIlPDrSIsDQogICAgICBUUlVFIH4gIk5vIg0KICAgICkNCiAgKQ0KYGBgDQoNCioqTW9kZWxvIFJhbmRvbSBGb3Jlc3QgLSBNZXJjYWRvIEVzdHJlbGxhKioNCg0KTG9zIGNsaWVudGVzIGVzdHJlbGxhIGRlc3RhY2FuIG5vIHNvbG8gcG9yIGN1w6FudG8gZ2FzdGFuLCBzaW5vIHBvciBjw7NtbyBkaXN0cmlidXllbiBzdSBnYXN0bywgZXNwZWNpYWxtZW50ZSBlbiBhbG9qYW1pZW50byB5IHBlcm1hbmVuY2lhIChkw61hcykuIEVzdG8gc3VnaWVyZSBxdWUgcXVpZW5lcyBtw6FzIHZhbG9yIGdlbmVyYW4gYnVzY2FuIHVuYSBleHBlcmllbmNpYSBjb21wbGV0YSB5IGPDs21vZGEuIFByb21vY2lvbmVzIGNvbiBob3NwZWRhamUgaW5jbHVpZG8gbyBwcmVtaXVtIHB1ZWRlbiBzZXIgY2xhdmUgcGFyYSBhdHJhZXIgbyByZXRlbmVyIGVzdGUgcGVyZmlsLg0KDQoqIEFsb2phbWllbnRvIGVzIGxhIHZhcmlhYmxlIG3DoXMgaW1wb3J0YW50ZSBlbiBhbWJvcyBjcml0ZXJpb3MgKEdpbmkgeSBBY2N1cmFjeSksIGxvIHF1ZSBpbmRpY2EgcXVlIGxhcyBwZXJzb25hcyBxdWUgZ2FzdGFuIG3DoXMgZW4gaG9zcGVkYWplIHRpZW5kZW4gYSBzZXIgZGVsIG1lcmNhZG8gZXN0cmVsbGEuDQoNCiogTGUgc2lndWVuIGVuIGltcG9ydGFuY2lhOiBkw61hcyBhc2lzdGlkb3MsIGJvbGV0b3MgeSBhbGltZW50b3MuDQoNCiogVHJhbnNwb3J0ZSB0aWVuZSBsYSBtZW5vciBpbXBvcnRhbmNpYSByZWxhdGl2YS4NCg0KYGBge3J9DQojIE1vZGVsbyBNZXJjYWRvIEVzdHJlbGxhDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCg0KZGZfcmZfZXN0cmVsbGEgPC0gZGZfcGFsbm9ydGUgJT4lIHNlbGVjdChtZXJjYWRvX2VzdHJlbGxhLCBkaWFzLCBib2xldG9zLCBhbG9qYW1pZW50bywgYWxpbWVudG9zLCB0cmFuc3BvcnRlKQ0KZGZfcmZfZXN0cmVsbGEkbWVyY2Fkb19lc3RyZWxsYSA8LSBhcy5mYWN0b3IoZGZfcmZfZXN0cmVsbGEkbWVyY2Fkb19lc3RyZWxsYSkNCg0Kc2V0LnNlZWQoMTIzKQ0KbW9kZWxvX2VzdHJlbGxhIDwtIHJhbmRvbUZvcmVzdChtZXJjYWRvX2VzdHJlbGxhIH4gLiwgZGF0YSA9IGRmX3JmX2VzdHJlbGxhLCBpbXBvcnRhbmNlID0gVFJVRSkNCmltcG9ydGFuY2UobW9kZWxvX2VzdHJlbGxhKQ0KdmFySW1wUGxvdChtb2RlbG9fZXN0cmVsbGEpDQpgYGANCg0KKipNb2RlbG8gUmFuZG9tIEZvcmVzdCAtIE1lcmNhZG8gUG90ZW5jaWFsKioNCg0KTG9zIGNsaWVudGVzIHBvdGVuY2lhbGVzIHNvbiBzZW5zaWJsZXMgYWwgbsO6bWVybyBkZSBkw61hcyBxdWUgYXNpc3RlbjogcXVpZW5lcyBhc2lzdGVuIG1lbm9zIGTDrWFzIHBlcm8geWEgZ2FzdGFuIHBvciBlbmNpbWEgZGVsIHByb21lZGlvIHNvbiBjYW5kaWRhdG9zIGlkZWFsZXMgcGFyYSBhdW1lbnRhciBzdSB2YWxvci4gQ2FtcGHDsWFzIHF1ZSBwcm9tdWV2YW4gZXh0ZW5kZXIgc3UgZXN0YW5jaWEgKHBvciBlamVtcGxvLCBjb21ib3MgZGUgMiBhIDMgZMOtYXMpIHB1ZWRlbiBjb252ZXJ0aXJsb3MgZW4gZXN0cmVsbGFzLg0KDQoqIETDrWFzIGFzaXN0aWRvcyBlcyBsYSB2YXJpYWJsZSBtw6FzIHJlbGV2YW50ZSBwYXJhIGlkZW50aWZpY2FyIGEgY2xpZW50ZXMgY29uIHBvdGVuY2lhbCBkZSBjb252ZXJ0aXJzZSBlbiBlc3RyZWxsYS4NCg0KKiBMZSBzaWd1ZW4gYm9sZXRvcyB5IGFsb2phbWllbnRvLg0KDQoqIEFsaW1lbnRvcyB5IHRyYW5zcG9ydGUgdGllbmVuIG11eSBwb2NhIHJlbGV2YW5jaWEuDQoNCmBgYHtyfQ0KIyBNb2RlbG8gTWVyY2FkbyBQb3RlbmNpYWwNCmRmX3JmX3BvdGVuY2lhbCA8LSBkZl9wYWxub3J0ZSAlPiUgc2VsZWN0KG1lcmNhZG9fcG90ZW5jaWFsLCBkaWFzLCBib2xldG9zLCBhbG9qYW1pZW50bywgYWxpbWVudG9zLCB0cmFuc3BvcnRlKQ0KZGZfcmZfcG90ZW5jaWFsJG1lcmNhZG9fcG90ZW5jaWFsIDwtIGFzLmZhY3RvcihkZl9yZl9wb3RlbmNpYWwkbWVyY2Fkb19wb3RlbmNpYWwpDQoNCnNldC5zZWVkKDEyMykNCm1vZGVsb19wb3RlbmNpYWwgPC0gcmFuZG9tRm9yZXN0KG1lcmNhZG9fcG90ZW5jaWFsIH4gLiwgZGF0YSA9IGRmX3JmX3BvdGVuY2lhbCwgaW1wb3J0YW5jZSA9IFRSVUUpDQppbXBvcnRhbmNlKG1vZGVsb19wb3RlbmNpYWwpDQp2YXJJbXBQbG90KG1vZGVsb19wb3RlbmNpYWwpDQpgYGANCg0KRWwgYW7DoWxpc2lzIHJldmVsYSBxdWUgZWwgcGVyZmlsIHByZWRvbWluYW50ZSBkZSBsb3MgYXNpc3RlbnRlcyBhbCBmZXN0aXZhbCBzZSBlbmN1ZW50cmEgZW50cmUgbG9zIDI2IHkgMzUgYcOxb3MsIHJlcHJlc2VudGFuZG8gZWwgNDclIGRlbCB0b3RhbCwgc2VndWlkbyBwb3IgasOzdmVuZXMgZGUgMTggYSAyNSBhw7FvcyAoMjQlKSB5IGFkdWx0b3MgZGUgMzYgYSA1MCBhw7FvcyAoMjAlKSwgbG8gcXVlIGNvbmZpcm1hIHF1ZSBlbCBww7pibGljbyBvYmpldGl2byBlc3TDoSBjb21wdWVzdG8gbWF5b3JpdGFyaWFtZW50ZSBwb3IgYWR1bHRvcyBqw7N2ZW5lcywgZWNvbsOzbWljYW1lbnRlIGFjdGl2b3MgZSBpbnRlcmVzYWRvcyBlbiBleHBlcmllbmNpYXMgbXVzaWNhbGVzIGludGVncmFsZXMuIEVuIHTDqXJtaW5vcyBkZSBnYXN0bywgc2Ugb2JzZXJ2YSB1bmEgY2xhcmEgY29ycmVsYWNpw7NuIGVudHJlIGxvcyBkw61hcyBkZSBhc2lzdGVuY2lhIHkgZWwgZGVzZW1ib2xzbyBlY29uw7NtaWNvOiBxdWllbmVzIGFzaXN0ZW4gdW4gc29sbyBkw61hIGdhc3RhbiBlbiBwcm9tZWRpbyAkNiw1MDAgTVhOLCBtaWVudHJhcyBxdWUgbG9zIGFzaXN0ZW50ZXMgZGUgdHJlcyBkw61hcyBzdXBlcmFuIGxvcyAkMTAsMDAwIE1YTiwgcmVmbGVqYW5kbyB1biBpbmNyZW1lbnRvIGRlbCA1NCUuIEVzdGUgaGFsbGF6Z28sIHJlc3BhbGRhZG8gcG9yIGhpc3RvZ3JhbWFzIHkgYm94cGxvdHMsIGRlc3RhY2EgbGEgaW1wb3J0YW5jaWEgZGUgZXh0ZW5kZXIgbGEgcGVybWFuZW5jaWEgY29tbyBlc3RyYXRlZ2lhIHBhcmEgbWF4aW1pemFyIGVsIGluZ3Jlc28gcG9yIHZpc2l0YW50ZS4NCg0KRW4gY29uY2x1c2nDs24sIGVsIGZlc3RpdmFsIGN1ZW50YSBjb24gdW4gbWVyY2FkbyBlc3RyZWxsYSBjb25zb2xpZGFkbzogYWR1bHRvcyBqw7N2ZW5lcyBjb24gZW1wbGVvLCBhbHRvIGdhc3RvLCB5IGFzaXN0ZW5jaWEgcGxlbmEgZGUgdHJlcyBkw61hcywgcXVlIHByaW9yaXphbiBzZXJ2aWNpb3MgZGlmZXJlbmNpYWRvcy4gU2luIGVtYmFyZ28sIHRhbWJpw6luIGV4aXN0ZSB1biBtZXJjYWRvIHBvdGVuY2lhbCBhbXBsaW8gY29uIHNpbWlsaXR1ZGVzIGVuIHBlcmZpbCBwZXJvIG1lbm9yIHBlcm1hbmVuY2lhIHkgZ2FzdG8uIExhIGNsYXZlIGVzdMOhIGVuIGRpc2XDsWFyIGVzdHJhdGVnaWFzIHF1ZSBpbmNlbnRpdmVuIHN1IGNvbnZlcnNpw7NuLCBjb21vIHByb21vY2lvbmVzIHBvciBtw7psdGlwbGVzIGTDrWFzLCBiZW5lZmljaW9zIGV4Y2x1c2l2b3MgcG9yIGNvbXByYSBhbnRpY2lwYWRhIG8gY2FtcGHDsWFzIGRlIHJldGFyZ2V0aW5nLCBzaW4gZGVzY3VpZGFyIGEgbG9zIGNsaWVudGVzIHByZW1pdW0gcXVlIGRlbWFuZGFuIHVuYSBvZmVydGEgcGVyc29uYWxpemFkYSBlbiBhc3BlY3RvcyBjb21vIGFsb2phbWllbnRvLCBhY2Nlc29zIHkgZW50cmV0ZW5pbWllbnRvIGNvbXBsZW1lbnRhcmlvLg0KDQoNCiMjICoqQW7DoWxpc2lzIGRlIFNlbnRpbWllbnRvIGRlIGxhIEV4cGVyaWVuY2lhIGRlbCBDbGllbnRlKioNCg0KIyMjIEhvdGVsZXMNCg0KQSBwYXJ0aXIgZGVsIGFuw6FsaXNpcyBkZSBsYXMgcmVzZcOxYXMgcmVjb3BpbGFkYXMgZGUgbG9zIGNpbmNvIGhvdGVsZXMgbWVqb3IgY2FsaWZpY2Fkb3MgeSBsb3MgY2luY28gcGVvciBjYWxpZmljYWRvcyBlbiBNb250ZXJyZXkgeSBTYW4gUGVkcm8sIHNlIHJlYWxpesOzIHVuYSBjbGFzaWZpY2FjacOzbiBkZSBsb3MgY29tZW50YXJpb3Mgc2Vnw7puIHN1IHBvbGFyaWRhZCBlbW9jaW9uYWwgKHBvc2l0aXZhIG8gbmVnYXRpdmEpLiBFbCBncsOhZmljbyBkZSBiYXJyYXMgbXVlc3RyYSBjbGFyYW1lbnRlIHF1ZSBsb3MgaG90ZWxlcyBtZWpvciBwb3NpY2lvbmFkb3MsIGNvbW8gSG90ZWwgQW50aWd1YSBDYXNvbmEgQWxsZW5kZSB5IEhvdGVsZXMgQW50aWd1YSBTYW50YSBMdWPDrWEsIHByZXNlbnRhbiB1biBwb3JjZW50YWplIGRlIGNvbWVudGFyaW9zIHBvc2l0aXZvcyBzdXBlcmlvciBhbCA5MCUsIGxvIHF1ZSByZWZsZWphIGFsdG9zIG5pdmVsZXMgZGUgc2F0aXNmYWNjacOzbiBwb3IgcGFydGUgZGUgbG9zIGNsaWVudGVzLiBFbiBjb250cmFzdGUsIGhvdGVsZXMgY29tbyBIb3RlbCBDYW5jw7puIHkgSG90ZWwgQW1hZG8gTmVydm8gbm8gYWxjYW56YW4gbmkgZWwgNTAlIGRlIGNvbWVudGFyaW9zIHBvc2l0aXZvcywgbG8gcXVlIHN1Z2llcmUgcHJvYmxlbWFzIHJlY3VycmVudGVzIGVuIHN1IHNlcnZpY2lvIG8gaW5zdGFsYWNpb25lcy4gIA0KDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpob3RlbGVzX3RvcCA8LSBob3RlbGVzICU+JSANCiAgZmlsdGVyKENhdGVnb3J5ICVpbiUgYygiMiIsICIzIiwgIjQiLCAiNSIpKSAlPiUNCiAgZmlsdGVyKHVzZXJfcmF0aW5nc190b3RhbCA+PSAzMCkgJT4lDQogIHNsaWNlX21heChyYXRpbmcsIG4gPSAxMCwgd2l0aF90aWVzID0gRkFMU0UpDQoNCmhvdGVsZXNfYm90dG9tIDwtIGhvdGVsZXMgJT4lIA0KICBmaWx0ZXIoQ2F0ZWdvcnkgJWluJSBjKCIyIiwgIjMiLCAiNCIsICI1IikpICU+JQ0KICBmaWx0ZXIodXNlcl9yYXRpbmdzX3RvdGFsID49IDMwKSAlPiUNCiAgc2xpY2VfbWluKHJhdGluZywgbiA9IDEwLCB3aXRoX3RpZXMgPSBGQUxTRSkNCg0KI1RPUCAxMCBIT1RFTEVTDQp0b3BfcmF0aW5nc19ob3RlbGVzIDwtIGdncGxvdChob3RlbGVzX3RvcCwgYWVzKHg9cmVvcmRlcihuYW1lLCByYXRpbmcpLCB5PXJhdGluZywgZmlsbCA9IENpdHkpKSArDQogIGdlb21fY29sKHdpZHRoID0gMC43KSArIA0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcmF0aW5nKSwgaGp1c3QgPSAtMC4zLCBzaXplID0gMywpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJIb3RlbGVzIGNvbiBtZWpvciBjYWxpZmljYWNpw7NuIGVuIEdvb2dsZSBNYXBzIiwgDQogICAgc3VidGl0bGUgPSAiTW9udGVycmV5IHkgU2FuIFBlZHJvIiwNCiAgICB4ID0gIiIsDQogICAgeSA9ICJDYWxpZmljYWNpw7NuIiwNCiAgICBmaWxsID0gIkNpdWRhZCIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiTW9udGVycmV5IiA9ICIjMWY3N2I0IiwgIlNhblBlZHJvIiA9ICJsaWdodGJsdWUiKSkgKw0KICBjb29yZF9mbGlwKCkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA1KSwgYnJlYWtzID0gYygwLCAyLjUsIDUpKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkNCiAgKQ0KDQoNCiNCT1RUT00gMTAgSE9URUxFUw0KYm90dG9tX3JhdGluZ3NfaG90ZWxlcyA8LSBnZ3Bsb3QoaG90ZWxlc19ib3R0b20sIGFlcyh4ID0gcmVvcmRlcihuYW1lLCByYXRpbmcpLCB5ID0gcmF0aW5nLCBmaWxsID0gQ2l0eSkpICsNCiAgZ2VvbV9jb2wod2lkdGggPSAwLjcpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJhdGluZyksIGhqdXN0ID0gLTAuMywgc2l6ZSA9IDMsKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiSG90ZWxlcyBjb24gcGVvciBjYWxpZmljYWNpw7NuIGVuIEdvb2dsZSBNYXBzIiwNCiAgICBzdWJ0aXRsZSA9ICJNb250ZXJyZXkgeSBTYW4gUGVkcm8iLA0KICAgIHggPSAiIiwNCiAgICB5ID0gIkNhbGlmaWNhY2nDs24iLA0KICAgIGZpbGwgPSAiQ2l1ZGFkIg0KICApICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiTW9udGVycmV5IiA9ICIjMWY3N2I0IiwgIlNhbiBQZWRybyIgPSAibGlnaHRibHVlIikpICsgICMgQ3VzdG9tIGNvbG9ycw0KICBjb29yZF9mbGlwKCkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA1KSwgYnJlYWtzID0gYygwLCAyLjUsIDUpKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkNCiAgKQ0KDQp0b3BfcmF0aW5nc19ob3RlbGVzIC8gIGJvdHRvbV9yYXRpbmdzX2hvdGVsZXMgDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnJlc2XDsWFzIDwtIGNvbWVudGFyaW9zICU+JQ0KICBtdXRhdGUoDQogICAgaW5nbGVzID0gaWNvbnYoaW5nbGVzLCBmcm9tID0gIlVURi04IiwgdG8gPSAiQVNDSUkvL1RSQU5TTElUIiksDQogICAgaW5nbGVzID0gc3RyX3RvX2xvd2VyKGluZ2xlcykNCiAgKQ0KdG9rZW5zIDwtIHJlc2XDsWFzICU+JQ0KICB1bm5lc3RfdG9rZW5zKGlucHV0ID0gaW5nbGVzLCBvdXRwdXQgPSB3b3JkKQ0KDQpucmNfc2VudGltZW50cyA8LSBnZXRfc2VudGltZW50cygibnJjIikgJT4lDQogIGZpbHRlcihzZW50aW1lbnQgJWluJSBjKCJwb3NpdGl2ZSIsICJuZWdhdGl2ZSIpKQ0KDQojIFN0ZXAgNTogQ291bnQgYW5kIGNhbGN1bGF0ZSBwb3NpdGl2ZSBzZW50aW1lbnQgcmF0aW8gcGVyIGhvdGVsDQpyZXN1bWVuX3NlbnRpbWllbnRvcyA8LSB0b2tlbnMgJT4lDQogIGlubmVyX2pvaW4obnJjX3NlbnRpbWVudHMsIGJ5ID0gIndvcmQiKSAlPiUNCiAgY291bnQoaG90ZWwsIHNlbnRpbWVudCkgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBzZW50aW1lbnQsIHZhbHVlc19mcm9tID0gbiwgdmFsdWVzX2ZpbGwgPSAwKSAlPiUNCiAgbXV0YXRlKA0KICAgIHRvdGFsID0gcG9zaXRpdmUgKyBuZWdhdGl2ZSwNCiAgICBwb3JjZW50YWplX3Bvc2l0aXZvID0gcm91bmQocG9zaXRpdmUgLyB0b3RhbCwgMikNCiAgKQ0KDQojIFN0ZXAgNiAob3B0aW9uYWwpOiBWaWV3IHJlc3VsdA0KcmVzdW1lbl9zZW50aW1pZW50b3MgJT4lDQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoaG90ZWwsIHBvcmNlbnRhamVfcG9zaXRpdm8pLCB5ID0gcG9yY2VudGFqZV9wb3NpdGl2bykpICsNCiAgZ2VvbV9jb2woZmlsbCA9ICJncmV5IikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcG9yY2VudGFqZV9wb3NpdGl2byksIGhqdXN0ID0gLTAuMywgc2l6ZSA9IDMsIGZvbnRmYWNlID0gImJvbGQiLCBjb2xvciA9ICJkYXJrYmx1ZSIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJQb3JjZW50YWplIGRlIENvbWVudGFyaW9zIFBvc2l0aXZvcyBwb3IgSG90ZWwiLA0KICAgIHggPSAiSG90ZWwiLA0KICAgIHkgPSAiUG9yY2VudGFqZSBQb3NpdGl2byINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArIA0KICB0aGVtZSgNCiAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpDQogICkNCmBgYA0KDQojIyMjIEFuw6FsaXNpcyBkZSBTZW50aW1pZW50b3MgJiBOdWJlIGRlIFBhbGFicmFzDQoNCkVsIGFuw6FsaXNpcyBzZSBjb21wbGVtZW50YSBjb24gbnViZXMgZGUgcGFsYWJyYXMgcGFyYSBlbnRlbmRlciBtZWpvciBxdcOpIGFzcGVjdG9zIGRlc3RhY2FuIGxvcyB1c3VhcmlvcyBlbiBzdXMgcmVzZcOxYXMuIEVuIGxvcyBob3RlbGVzIG1lam9yIGNhbGlmaWNhZG9zIHByZWRvbWluYW4gcGFsYWJyYXMgY29tbyAiY2xlYW4iLCAicmVjb21tZW5kIiwgImZyaWVuZGx5IiwgImJlYXV0aWZ1bCIgeSAiYXR0ZW50aW9uIiwgbG8gcXVlIHJlZnVlcnphIGxhIHBlcmNlcGNpw7NuIGRlIGNhbGlkYWQgZW4gbGltcGllemEsIHRyYXRvIGFsIGNsaWVudGUgeSByZWNvbWVuZGFjacOzbiBnZW5lcmFsLiBQb3Igb3RybyBsYWRvLCBlbiBsYXMgcmVzZcOxYXMgbmVnYXRpdmFzIChpbWFnZW4gMyksIHNvYnJlc2FsZW4gdMOpcm1pbm9zIGNvbW8gImJhZCIsICJkaXJ0eSIsICJydWRlIiwgIm5vaXN5IiB5ICJwcm9ibGVtIiwgaW5kaWNhbmRvIHF1ZSBsb3MgcHVudG9zIGNyw610aWNvcyBnaXJhbiBlbiB0b3JubyBhbCBzZXJ2aWNpbyBhbCBjbGllbnRlLCBlbCBydWlkbyB5IGxhIGxpbXBpZXphLiAgDQoNCkVzdGUgdGlwbyBkZSBhbsOhbGlzaXMgcGVybWl0ZSBubyBzb2xvIGlkZW50aWZpY2FyIGxvcyBob3RlbGVzIGNvbiBtZWpvciBkZXNlbXBlw7FvIGRlc2RlIGxhIGV4cGVyaWVuY2lhIGRlbCB1c3VhcmlvLCBzaW5vIHRhbWJpw6luIGVudGVuZGVyIGRlIGZvcm1hIGN1YWxpdGF0aXZhIGxvcyBmYWN0b3JlcyBxdWUgaW5mbHV5ZW4gZW4gbGEgcGVyY2VwY2nDs24gcG9zaXRpdmEgbyBuZWdhdGl2YSBkZWwgc2VydmljaW8gaG90ZWxlcm8uIEVzdG9zIGhhbGxhemdvcyBzb24gY2xhdmUgcGFyYSBlc3RyYXRlZ2lhcyBkZSBtZWpvcmEgY29udGludWEgeSBwb3NpY2lvbmFtaWVudG8gZGUgbWFyY2EgZW4gZWwgc2VjdG9yIHR1csOtc3RpY28uDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCmRhcmtfZ3JlZW5zIDwtIGJyZXdlci5wYWwoOSwgIkdyZWVucyIpWzQ6OV0gDQpkYXJrX3JlZHMgPC0gYnJld2VyLnBhbCg5LCAiUmVkcyIpWzQ6OV0NCg0KDQojIFBBTEFCUkFTIFBBUkEgRVhDTFVJUg0KZXhjbHVkZWRfd29yZHMgPC0gYygiZXhjZWxsZW50IiwgImdvb2QiLCAiYmFkIikNCg0KIyBXT1JMRCBDTE9VRCBQT1NJVElWTw0KcG9zaXRpdmVfd29yZGNsb3VkIDwtIHRva2VucyAlPiUNCiAgaW5uZXJfam9pbihucmNfc2VudGltZW50cywgYnkgPSAid29yZCIpICU+JQ0KICBmaWx0ZXIoc2VudGltZW50ID09ICJwb3NpdGl2ZSIsICF3b3JkICVpbiUgZXhjbHVkZWRfd29yZHMpICU+JQ0KICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgJT4lDQogIHdpdGgod29yZGNsb3VkKA0KICAgIHdvcmRzID0gd29yZCwNCiAgICBmcmVxID0gbiwNCiAgICBtYXgud29yZHMgPSAxMDAsDQogICAgY29sb3JzID0gZGFya19ncmVlbnMsDQogICAgcmFuZG9tLm9yZGVyID0gRkFMU0UNCiAgKSkNCg0KIyBXT1JEIENMT1VEIE5FR0FUSVZPDQpuZWdhdGl2ZV93b3JkY2xvdWQgPC0gdG9rZW5zICU+JQ0KICBpbm5lcl9qb2luKG5yY19zZW50aW1lbnRzLCBieSA9ICJ3b3JkIikgJT4lDQogIGZpbHRlcihzZW50aW1lbnQgPT0gIm5lZ2F0aXZlIiwgIXdvcmQgJWluJSBleGNsdWRlZF93b3JkcykgJT4lDQogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKSAlPiUNCiAgd2l0aCh3b3JkY2xvdWQoDQogICAgd29yZHMgPSB3b3JkLA0KICAgIGZyZXEgPSBuLA0KICAgIG1heC53b3JkcyA9IDEwMCwNCiAgICBjb2xvcnMgPSBkYXJrX3JlZHMsDQogICAgcmFuZG9tLm9yZGVyID0gRkFMU0UNCiAgKSkNCg0KcG9zaXRpdmVfd29yZGNsb3VkICsgbmVnYXRpdmVfd29yZGNsb3VkDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnRva2VucyAlPiUNCiAgaW5uZXJfam9pbihucmNfc2VudGltZW50cywgYnkgPSAid29yZCIpICU+JQ0KICBjb3VudChzZW50aW1lbnQsIHdvcmQsIHNvcnQgPSBUUlVFKSAlPiUNCiAgZ3JvdXBfYnkoc2VudGltZW50KSAlPiUNCiAgc2xpY2VfbWF4KG4sIG4gPSAxMCkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcih3b3JkLCBuKSwgeSA9IG4sIGZpbGwgPSBzZW50aW1lbnQpKSArDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZmFjZXRfd3JhcCh+IHNlbnRpbWVudCwgc2NhbGVzID0gImZyZWUiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoInBvc2l0aXZlIiA9ICJkYXJrZ3JlZW4iLCAibmVnYXRpdmUiID0gImRhcmtyZWQiKSkgKw0KICBjb29yZF9mbGlwKCkgKw0KICBsYWJzKHRpdGxlID0gIlRvcCBwYWxhYnJhcyBwb3Igc2VudGltaWVudG8iLA0KICAgICAgIHggPSAiUGFsYWJyYSIsDQogICAgICAgeSA9ICJGcmVjdWVuY2lhIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpKQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHN0b3B3b3JkcykNCg0KcGFsYWJyYXNfZXhjbHVpciA8LSBjKCJnb29kIiwgImJhZCIsICJleGNlbGxlbnQiLCAiaG90ZWwiLCAic2kiKQ0KDQojIFByb2Nlc2FtaWVudG86IHRva2VuaXphciwgZWxpbWluYXIgc3RvcHdvcmRzIHkgY29udGFyIGZyZWN1ZW5jaWENCmZyZWN1ZW5jaWFzIDwtIGNvbWVudGFyaW9zICU+JQ0KICB1bm5lc3RfdG9rZW5zKHBhbGFicmEsIGNvbWVudGFyaW9zKSAlPiUNCiAgbXV0YXRlKHBhbGFicmEgPSBzdHJfdG9fbG93ZXIocGFsYWJyYSkpICU+JQ0KICBmaWx0ZXIoIXN0cl9kZXRlY3QocGFsYWJyYSwgIl5bMC05XSskIikpICU+JQ0KICBmaWx0ZXIoIXBhbGFicmEgJWluJSBzdG9wd29yZHMoImVzIiksDQogICAgICAgICAhcGFsYWJyYSAlaW4lIHN0b3B3b3JkcygiZW4iKSwNCiAgICAgICAgICFwYWxhYnJhICVpbiUgcGFsYWJyYXNfZXhjbHVpcikgJT4lDQogIGNvdW50KGNhbGlmaWNhY2lvbiwgcGFsYWJyYSwgc29ydCA9IFRSVUUpDQoNCiMgVG9wIDEwIHBhbGFicmFzIHBvciBob3RlbA0KdG9wX3BhbGFicmFzIDwtIGZyZWN1ZW5jaWFzICU+JQ0KICBncm91cF9ieShjYWxpZmljYWNpb24pICU+JQ0KICBzbGljZV9tYXgobiwgbiA9IDEwKSAlPiUNCiAgdW5ncm91cCgpDQoNCiMgR3LDoWZpY28NCmdncGxvdCh0b3BfcGFsYWJyYXMsIGFlcyh4ID0gcmVvcmRlcl93aXRoaW4ocGFsYWJyYSwgbiwgY2FsaWZpY2FjaW9uKSwgeSA9IG4sIGZpbGwgPSBjYWxpZmljYWNpb24pKSArDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZmFjZXRfd3JhcCh+Y2FsaWZpY2FjaW9uLCBzY2FsZXMgPSAiZnJlZV95IikgKw0KICBzY2FsZV94X3Jlb3JkZXJlZCgpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiYWx0YSIgPSAiZGFya2dyZWVuIiwgImJhamEiID0gImRhcmtyZWQiKSkgKw0KICBsYWJzKHRpdGxlID0gIlBhbGFicmFzIE3DoXMgRnJlY3VlbnRlcyBwb3IgY2F0ZWdvcsOtYSBkZSBjYWxpZmljYWNpw7NuIiwNCiAgICAgICB4ID0gIlBhbGFicmEiLA0KICAgICAgIHkgPSAiRnJlY3VlbmNpYSIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKQ0KICApDQpgYGANCg0KDQojIyMgQWlyYm5iDQoNCiMjIyMgUGVyY2VwY2nDs24gZGUgY2xpZW50ZXMgZW4gbGFzIDEwIHVuaWRhZGVzIG1lam9yIGNhbGlmaWNhZGFzIChjb24gbnViZSBkZSBwYWxhYnJhcykNCg0KQ29uIGJhc2UgYSBsb3MgZ3LDoWZpY29zIGNyZWFkb3MsIHNlIGRldGVybWluYSBxdWUgbG9zIHByaW5jaXBhbGVzIHRlbWFzIHF1ZSBpbmZsdXllbiBlbiB0ZW5lciB1bmEgZXhwZXJpZW5jaWEgcG9zaXRpdmEgc29uOg0KDQoqICpMaW1waWV6YToqICjigJxjbGVhbuKAnSAvIOKAnGxpbXBpb+KAnSkNCiogKlNlcnZpY2lvIHkgYXRlbmNpw7NuOiogKOKAnGF0dGVudGlvbuKAnSAvIOKAnHNlcnZpY2XigJ0gLyDigJxzdGFmZuKAnSAvIOKAnGF0ZW5jacOzbuKAnSAvIOKAnGFtYWJsZeKAnSkNCiogKlJlY29tZW5kYWNpb25lczoqICjigJxyZWNvbW1lbmTigJ0gLyDigJxyZWNvbWllbmRv4oCdKQ0KKiAqQ2FsaWRhZCBnZW5lcmFsOiogKOKAnGV4Y2VsbGVudOKAnSAvIOKAnGV4Y2VsZW50ZeKAnSAvIOKAnGdvb2TigJ0gLyDigJxidWVuYeKAnSkNCiogKkNvbW9kaWRhZGVzOiogKOKAnGZvb2TigJ0gLyDigJxicmVha2Zhc3TigJ0gLyDigJxwb29s4oCdIC8g4oCcYWVyb3B1ZXJ0b+KAnSAvIOKAnGNlcmNh4oCdKQ0KDQpEaWNob3MgYXRyaWJ1dG9zIGFwYXJlY2VuIG11eSByb2J1c3RvcyB0YW50byBlbiBudWJlcyBkZSBwYWxhYnJhcyBjb21vIGVuIGxhcyBiYXJyYXMgZGUgZnJlY3VlbmNpYSBwYXJhIGNhbGlmaWNhY2lvbmVzIGFsdGFzLg0KDQpgYGB7cn0NCmxpYnJhcnkodG0pDQoNCiMgVW5pciBsb3MgY29tZW50YXJpb3MgZW4gdW5hIHNvbGEgY29sdW1uYQ0KZGZfYWlyJENvbWVudGFyaW9zIDwtIGFwcGx5KGRmX2FpclssIGMoIkNvbWVudGFyaW9zMSIsICJDb21lbnRhcmlvczIiLCAiQ29tZW50YXJpb3MzIiwgIkNvbWVudGFyaW9zNCIpXSwgMSwgZnVuY3Rpb24oeCkgcGFzdGUobmEub21pdCh4KSwgY29sbGFwc2UgPSAiICIpKQ0KDQojIENhbGlmaWNhY2nDs24gcHJvbWVkaW8gZ2xvYmFsIGRlIHRvZGFzIGxhcyBwcm9waWVkYWRlcw0KbXUgPC0gbWVhbihkZl9haXIkQ2FsaWZpY2FjacOzbiwgbmEucm0gPSBUUlVFKQ0KDQojIFVtYnJhbCBtw61uaW1vIGRlIGV2YWx1YWNpb25lcywgcHVlZGVzIGFqdXN0YXIgZXN0byBzZWfDum4gdHVzIG5lY2VzaWRhZGVzDQptIDwtIDUNCg0KIyBDYWxpZmljYWNpw7NuIHBvbmRlcmFkYSBiYXllc2lhbmENCmRmX2FpciA8LSBkZl9haXIgJT4lDQogIG11dGF0ZShDYWxpZmljYWNpw7NuX3BvbmRlcmFkYSA9IChDYWxpZmljYWNpw7NuICogRXZhbHVhY2lvbmVzICsgbXUgKiBtKSAvIChFdmFsdWFjaW9uZXMgKyBtKSkNCg0KIyBTZWxlY2Npb25hciB0b3AgMTAgbWVqb3IgY2FsaWZpY2Fkb3Mgc2Vnw7puIGxhIGNhbGlmaWNhY2nDs24gcG9uZGVyYWRhDQp0b3AxMF9tZWpvcmVzIDwtIGRmX2FpciAlPiUNCiAgYXJyYW5nZShkZXNjKENhbGlmaWNhY2nDs25fcG9uZGVyYWRhKSkgJT4lDQogIHNsaWNlX2hlYWQobiA9IDEwKQ0KDQoNCiMgVW5pciB0b2RvcyBsb3MgY29tZW50YXJpb3MNCmNvbWVudGFyaW9zX3RvcCA8LSBwYXN0ZSh0b3AxMF9tZWpvcmVzJENvbWVudGFyaW9zLCBjb2xsYXBzZSA9ICIgIikNCg0KIyBQYWxhYnJhcyBhZGljaW9uYWxlcyBhIGVsaW1pbmFyDQpjdXN0b21fc3RvcHdvcmRzIDwtIGMoImx1Z2FyIiwgImdyYWNpYXMiLCAicGVuZGllbnRlIiwgImVzdGFuY2lhIiwgImNvc2EiLCAibWFyZ2FyaXRvIiwgImRpam8iLCAiY3JpcyIsICJsdWVnbyIsImFuZml0cmnDs24iLCAiaG9zcGVkYWplIiwgImVzdHV2byIsICJub3MiLCAiZnVlIiwgImVzIiwgInF1ZSIsICJjb24iLCAidW5hIiwgImVsaXphYmV0aCIsICJtaXJleWEiLA0KICAgICAgICAgICAgICAgICAgICAgICJwYXJhIiwgImVzdGEiLCAiZXN0w6EiLCAiZXN0YWJhbiIsICJlc3R1dm8iLCAibm9zb3Ryb3MiLCANCiAgICAgICAgICAgICAgICAgICAgICAiZWxsYSIsICLDqWwiLCAiZWxsb3MiLCAiZWxsYXMiLCAidGFtYmnDqW4iLCAiZXRjIiwgInN1c2FuYSIsICJkYXZpZCIpDQoNCiMgQ3JlYXIgY29ycHVzIHkgbGltcGlhcg0KY29ycHVzIDwtIENvcnB1cyhWZWN0b3JTb3VyY2UoY29tZW50YXJpb3NfdG9wKSkNCmNvcnB1cyA8LSBjb3JwdXMgJT4lDQogIHRtX21hcChjb250ZW50X3RyYW5zZm9ybWVyKHRvbG93ZXIpKSAlPiUNCiAgdG1fbWFwKHJlbW92ZVB1bmN0dWF0aW9uKSAlPiUNCiAgdG1fbWFwKHJlbW92ZU51bWJlcnMpICU+JQ0KICB0bV9tYXAocmVtb3ZlV29yZHMsIGMoc3RvcHdvcmRzKCJzcGFuaXNoIiksIGN1c3RvbV9zdG9wd29yZHMpKQ0KDQojIE1vc3RyYXIgbnViZQ0Kd29yZGNsb3VkKGNvcnB1cywgbWF4LndvcmRzID0gMTAwLCByYW5kb20ub3JkZXIgPSBGQUxTRSwgY29sb3JzID0gYnJld2VyLnBhbCg4LCAiR3JlZW5zIilbNTo4XSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBDcmVhciBtYXRyaXogZGUgdMOpcm1pbm9zDQpkdG1fbWVqb3JlcyA8LSBUZXJtRG9jdW1lbnRNYXRyaXgoY29ycHVzKQ0KbWF0X21lam9yZXMgPC0gYXMubWF0cml4KGR0bV9tZWpvcmVzKQ0KDQojIENhbGN1bGFyIGZyZWN1ZW5jaWFzDQpmcmVjdWVuY2lhc19tZWpvcmVzIDwtIHNvcnQocm93U3VtcyhtYXRfbWVqb3JlcyksIGRlY3JlYXNpbmcgPSBUUlVFKQ0KDQojIENvbnZlcnRpciBhIGRhdGEgZnJhbWUNCmRmX2ZyZWNfbWVqb3JlcyA8LSBkYXRhLmZyYW1lKA0KICBwYWxhYnJhID0gbmFtZXMoZnJlY3VlbmNpYXNfbWVqb3JlcyksDQogIGZyZWN1ZW5jaWEgPSBmcmVjdWVuY2lhc19tZWpvcmVzDQopDQoNCiMgU2VsZWNjaW9uYXIgdG9wIDEwDQp0b3AxMF9wYWxhYnJhc19tZWpvcmVzIDwtIGRmX2ZyZWNfbWVqb3JlcyAlPiUNCiAgc2xpY2VfbWF4KGZyZWN1ZW5jaWEsIG4gPSAxMCkNCg0KIyBHcmFmaWNhcg0KZ2dwbG90KHRvcDEwX3BhbGFicmFzX21lam9yZXMsIGFlcyh4ID0gcmVvcmRlcihwYWxhYnJhLCBmcmVjdWVuY2lhKSwgeSA9IGZyZWN1ZW5jaWEpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImRhcmtncmVlbiIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgbGFicyh0aXRsZSA9ICJUb3AgMTAgcGFsYWJyYXMgbcOhcyBmcmVjdWVudGVzIChtZWpvciBjYWxpZmljYWRvcykiLA0KICAgICAgIHggPSAiUGFsYWJyYSIsDQogICAgICAgeSA9ICJGcmVjdWVuY2lhIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCiMjIyMgUGVyY2VwY2nDs24gZGUgY2xpZW50ZXMgZW4gbGFzIDEwIHVuaWRhZGVzIHBlb3IgY2FsaWZpY2FkYXMgKGNvbiBudWJlIGRlIHBhbGFicmFzKQ0KDQpDb24gYmFzZSBhIGxvcyBncsOhZmljb3MgY3JlYWRvcywgc2UgZGV0ZXJtaW5hIHF1ZSBsb3MgcHJpbmNpcGFsZXMgdGVtYXMgcXVlIGluZmx1eWVuIGVuIHRlbmVyIHVuYSBleHBlcmllbmNpYSBuZWdhdGl2YSBzb246DQoNCiogKlN1Y2llZGFkKiAo4oCcZGlydHnigJ0gLyDigJxzdWNpb+KAnSkNCiogKkVzcGFjaW8gcmVkdWNpZG8qICjigJxzbWFsbOKAnSAvIOKAnHBlcXVlw7Fv4oCdIC8g4oCcaGFiaXRhY2nDs24gc29sYeKAnSkNCiogKlJ1aWRvKiAo4oCcbm9pc2XigJ0gLyDigJxydWlkb+KAnSkNCiogKkZyw61vIG8gbWFsYSB0ZW1wZXJhdHVyYSogKOKAnGNvbGTigJ0pDQoqICpQcm9ibGVtYXMgY29uIGJhw7FvIHkgYWd1YSogKOKAnHRvYWxsYXPigJ0gLyDigJxhZ3Vh4oCdIC8g4oCcYmHDsW/igJ0gLyDigJxvbG9y4oCdIC8g4oCcY29sY2jDs27igJ0pDQoqICpTZW5zYWNpw7NuIGRlIOKAnGNhcmHigJ0gbyDigJxiYXJhdGHigJ0qICjigJxiYXJhdG/igJ0gLyDigJxwcmVjaW/igJ0gLyDigJxtYWzigJ0pDQoNClBvciBsbyB0YW50bywgZGljaG9zIHRlbWFzIGluZmx1eWVuIGNvbnNpZGVyYWJsZW1lbnRlIGVuIGxhIGNhbGlmaWNhY2nDs24geWEgcXVlLCBzZSBleHBlcmltZW50YSB1bmEgc2Vuc2FjacOzbiBkZSBjb25mb3JtaXNtbyBkZWJpZG8gYSB1bmEgcmVsYWNpw7NuIG1hcmNhZGEgZGUgImNhbGlkYWQtcHJlY2lvIi4NCg0KYGBge3J9DQp0b3AxMF9wZW9yZXMgPC0gZGZfYWlyICU+JQ0KICBhcnJhbmdlKENhbGlmaWNhY2nDs25fcG9uZGVyYWRhKSAlPiUNCiAgc2xpY2VfaGVhZChuID0gMTApDQoNCmNvbWVudGFyaW9zX2JvdHRvbSA8LSBwYXN0ZSh0b3AxMF9wZW9yZXMkQ29tZW50YXJpb3MsIGNvbGxhcHNlID0gIiAiKQ0KDQojIENyZWFyIGNvcnB1cyB5IGxpbXBpYXINCmNvcnB1c19wZW9yZXMgPC0gQ29ycHVzKFZlY3RvclNvdXJjZShjb21lbnRhcmlvc19ib3R0b20pKQ0KY29ycHVzX3Blb3JlcyA8LSBjb3JwdXNfcGVvcmVzICU+JQ0KICB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcih0b2xvd2VyKSkgJT4lDQogIHRtX21hcChyZW1vdmVQdW5jdHVhdGlvbikgJT4lDQogIHRtX21hcChyZW1vdmVOdW1iZXJzKSAlPiUNCiAgdG1fbWFwKHJlbW92ZVdvcmRzLCBjKHN0b3B3b3Jkcygic3BhbmlzaCIpLCBjdXN0b21fc3RvcHdvcmRzKSkNCg0KIyBNb3N0cmFyIG51YmUNCndvcmRjbG91ZChjb3JwdXNfcGVvcmVzLCBtYXgud29yZHMgPSAxMDAsIHJhbmRvbS5vcmRlciA9IEZBTFNFLCBjb2xvcnMgPSBicmV3ZXIucGFsKDgsICJSZWRzIikpDQpgYGANCg0KYGBge3J9DQojIENyZWFyIG1hdHJpeiBkZSB0w6lybWlub3MNCmR0bV9wZW9yZXMgPC0gVGVybURvY3VtZW50TWF0cml4KGNvcnB1c19wZW9yZXMpDQptYXRfcGVvcmVzIDwtIGFzLm1hdHJpeChkdG1fcGVvcmVzKQ0KDQojIFN1bWFyIGZyZWN1ZW5jaWEgZGUgcGFsYWJyYXMNCmZyZWN1ZW5jaWFzIDwtIHNvcnQocm93U3VtcyhtYXRfcGVvcmVzKSwgZGVjcmVhc2luZyA9IFRSVUUpDQoNCiMgQ29udmVydGlyIGEgZGF0YSBmcmFtZQ0KZGZfZnJlY3VlbmNpYXMgPC0gZGF0YS5mcmFtZSgNCiAgcGFsYWJyYSA9IG5hbWVzKGZyZWN1ZW5jaWFzKSwNCiAgZnJlY3VlbmNpYSA9IGZyZWN1ZW5jaWFzDQopDQoNCiMgU2VsZWNjaW9uYXIgdG9wIDEwDQp0b3AxMF9wYWxhYnJhcyA8LSBkZl9mcmVjdWVuY2lhcyAlPiUNCiAgc2xpY2VfbWF4KGZyZWN1ZW5jaWEsIG4gPSAxMCkNCg0KIyBHcmFmaWNhcg0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpnZ3Bsb3QodG9wMTBfcGFsYWJyYXMsIGFlcyh4ID0gcmVvcmRlcihwYWxhYnJhLCBmcmVjdWVuY2lhKSwgeSA9IGZyZWN1ZW5jaWEpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImZpcmVicmljayIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgbGFicyh0aXRsZSA9ICJUb3AgMTAgcGFsYWJyYXMgbcOhcyBmcmVjdWVudGVzIChwZW9yIGNhbGlmaWNhZG9zKSIsDQogICAgICAgeCA9ICJQYWxhYnJhIiwNCiAgICAgICB5ID0gIkZyZWN1ZW5jaWEiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANCg0KDQojIyMjIENhcmFjdGVyw61zdGljYXMgZGUgbGFzIDEwIHVuaWRhZGVzIG1lam9yIGNhbGlmaWNhZGFzDQoNClByZWRvbWluYW4gbG9zIHRpcG9zIGRlIHJlc2lkZW5jaWFzIGZhbWlsaWFyZXMgKDMgaGFiLzMgYmHDsW9zKSwgY2FiYcOxYXMsIGxvZnRzIHkgYWxndW5hcyBoYWJpdGFjaW9uZXMsIHByaW5jaXBhbGVtbnRlIHViaWNhZGFzIGVuIEFwb2RhY2EgeSBHdWFkYWx1cGUgKGNlcmNhbsOtYSBhbCBhZXJvcHVlcnRvIHkgem9uYXMgdHJhbnF1aWxhcykuDQoNCiogQ2FwYWNpZGFkIGRlIGh1w6lzcGVkZXM6IDLigInigJPigIkxMCAobWVkaWFuYSAyKS4NCg0KKiBIYWJpdGFjaW9uZXM6IDHigInigJPigIkzIChtZWRpYW5hIDEpLg0KDQoqIENhbWFzOiAx4oCJ4oCT4oCJNSAobWVkaWFuYSAxKS4NCg0KKiBCYcOxb3M6IDHigInigJPigIkzIChtZWRpYW5hIDEpLg0KDQoqIFByZWNpbyBwb3Igbm9jaGU6IG1lZGlhbmEg4omIIDMsNDI1IE1YTi4NCg0KKiBBbnRpZ8O8ZWRhZCBkZSBBbmZpdHJpw7NuOiAyIOKAkyA2MCBtZXNlcyAobWVkaWFuYSDiiYggMjQpLg0KDQpgYGB7cn0NCnRvcDEwX21lam9yZXMgJT4lDQogIHNlbGVjdChOb21icmUsIExhdGl0dWQsIExvbmdpdHVkLCBQcmVjaW8sIEh1ZXNwZWRlcywgSGFiaXRhY2lvbmVzLCBDYW1hcywgQmHDsW9zLCBNZXNlc19BbnRpZ3VlZGFkKQ0KYGBgDQoNCiMjIyMgQ2FyYWN0ZXLDrXN0aWNhcyBkZSBsYXMgMTAgdW5pZGFkZXMgcGVvciBjYWxpZmljYWRhcw0KDQpQcmVkb21pbmFuIGxvcyB0aXBvcyBkZSBBaXJibmIgcXVlIHNvbiBoYWJpdGFjaW9uZXMgbyBkZXBhcnRhbWVudG9zIGRlIDEgaGFiLzEgYmHDsW8sIHByaW5jaXBhbGVtbnRlIHViaWNhZGFzIGVuIGVsIGNlbnRybyBkZSBNb250ZXJyZXkgeSBTYW4gTmljb2zDoXMgKGJhcnJpb3MgbcOhcyBydWlkb3NvcyBvIG1lbm9zIGNvbnZlbmllbnRlcykuDQoNCiogQ2FwYWNpZGFkIGRlIGh1w6lzcGVkZXM6IDIg4oCTIDMgKG1lZGlhbmEgMikuDQoNCiogSGFiaXRhY2lvbmVzOiBzaWVtcHJlIDEuDQoNCiogQ2FtYXM6IHNpZW1wcmUgMS4NCg0KKiBCYcOxb3M6IHNpZW1wcmUgMS4NCg0KKiBQcmVjaW8gcG9yIG5vY2hlOiBtZWRpYW5hIOKJiCAyLDc5MyBNWE4uDQoNCiogQW50aWfDvGVkYWQgQW5maXRyacOzbjogNCDigJMgOTYgbWVzZXMgKG1lZGlhbmEg4omIIDMwKS4NCg0KYGBge3J9DQp0b3AxMF9wZW9yZXMgJT4lDQogIHNlbGVjdChOb21icmUsIExhdGl0dWQsIExvbmdpdHVkLCBQcmVjaW8sIEh1ZXNwZWRlcywgSGFiaXRhY2lvbmVzLCBDYW1hcywgQmHDsW9zLCBNZXNlc19BbnRpZ3VlZGFkKQ0KYGBgDQoNCiMjIyMgUHVudGFqZSBkZSBzZW50aW1pZW50byBwb3IgQWlyYm5iDQoNClNlIG9ic2VydmEgcXVlIGxhICpjb3JyZWxhY2nDs24gZXMgbXV5IGTDqWJpbCogZW50cmUgbGEgY2FsaWZpY2FjacOzbiB5IGVsIHNlbnRpbWllbnRvIHBvciBBaXJibmIgeWEgcXVlLCBsYSBsw61uZWEgZGUgcmVncmVzacOzbiBjYXNpIGhvcml6b250YWw6DQoqIEhheSByZXNlw7FhcyBjb24gKmFsdGEgY2FsaWZpY2FjacOzbiogKDQuOOKAieKAk+KAiTUuMCkgcXVlIGNvbnRpZW5lbiBhbGdvIGRlIGxlbmd1YWplIG5lZ2F0aXZvLg0KICANCiogRXhpc3RlbiBjb21lbnRhcmlvcyBjb24gcHVudHVhY2lvbmVzIG1vZGVyYWRhcyBxdWUgc29uIHJlbGF0aXZhbWVudGUgcG9zaXRpdm9zLg0KDQpFbCBzaXN0ZW1hIGRlIHJhdGluZ3MgbnVtw6lyaWNvcyBubyBzaWVtcHJlIHJlZmxlamEgZWwgYmFsYW5jZSBkZSB0w6lybWlub3MgcG9zaXRpdm9zIHZzLiBuZWdhdGl2b3MuDQoNCmBgYHtyfQ0KbGlicmFyeShzZW50aW1lbnRyKQ0KbGlicmFyeShkcGx5cikNCg0Kc2VudGltaWVudG9zIDwtIHNlbnRpbWVudChkZl9haXIkQ29tZW50YXJpb3MpDQoNCiMgQWdydXBhciBwb3IgY2FkYSB1bmlkYWQgZGUgbmVnb2Npbw0KcHVudGFqZXNfc2VudGltaWVudG8gPC0gc2VudGltaWVudG9zICU+JQ0KICBncm91cF9ieShlbGVtZW50X2lkKSAlPiUNCiAgc3VtbWFyaXNlKHB1bnRhamVfc2VudGltaWVudG8gPSBtZWFuKHNlbnRpbWVudCwgbmEucm0gPSBUUlVFKSkNCg0KIyBVbmlyIGNvbiBkYXRvcyBvcmlnaW5hbGVzDQpkYXRvc19maW5hbCA8LSBkZl9haXIgJT4lDQogIG11dGF0ZShJRCA9IHJvd19udW1iZXIoKSkgJT4lDQogIGxlZnRfam9pbihwdW50YWplc19zZW50aW1pZW50bywgYnkgPSBjKCJJRCIgPSAiZWxlbWVudF9pZCIpKQ0KDQpkYXRvc19maW5hbCAlPiUNCiAgc2VsZWN0KE5vbWJyZSwgQ2FsaWZpY2FjacOzbiwgQ2FsaWZpY2FjacOzbl9wb25kZXJhZGEsIHB1bnRhamVfc2VudGltaWVudG8pICU+JQ0KICBhcnJhbmdlKGRlc2MocHVudGFqZV9zZW50aW1pZW50bykpJT4lDQogIHNsaWNlX2hlYWQobiA9IDEwKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpnZ3Bsb3QoZGF0b3NfZmluYWwsIGFlcyh4ID0gQ2FsaWZpY2FjacOzbl9wb25kZXJhZGEsIHkgPSBwdW50YWplX3NlbnRpbWllbnRvKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArDQogIGxhYnModGl0bGUgPSAiUmVsYWNpw7NuIGVudHJlIGNhbGlmaWNhY2nDs24geSBzZW50aW1pZW50byBkZSBjb21lbnRhcmlvcyIsDQogICAgICAgeCA9ICJDYWxpZmljYWNpw7NuIFBvbmRlcmFkYSIsDQogICAgICAgeSA9ICJQdW50YWplIGRlIFNlbnRpbWllbnRvIikNCmBgYA0KDQojIyMjIENsdXN0ZXJpemFjaW9uIGRlIFNlbnRpbWllbnRvcw0KDQoqKkNsw7pzdGVyIDEgKHNlbnRpbWllbnRvIG11eSBwb3NpdGl2bywgKzAuMDgpKioNCg0KKiBQYWxhYnJhcyBjbGF2ZTog4oCcZXhjZWxlbnRlIGV4cGVyaWVuY2lh4oCdLCDigJx1YmljYWNpw7Nu4oCdLCDigJxjZXJjYeKAnSwg4oCcdHJhbnF1aWxv4oCdDQoNCiogVGVtw6F0aWNhOiBEZXN0YWNhbiBsYSBwcm94aW1pZGFkIGEgcHVudG9zIGRlIGludGVyw6lzIChhZXJvcHVlcnRvLCB6b25hcyBzZWd1cmFzKSwgbGEgbGltcGllemEgeSBlbCBjb25mb3J0IGdlbmVyYWwuDQoNCioqQ2zDunN0ZXIgMyAoc2VudGltaWVudG8gcG9zaXRpdm8sICswLjA1KSoqDQoNCiogUGFsYWJyYXMgY2xhdmU6IOKAnHNlcnZpY2lv4oCdLCDigJxjb21pZGHigJ0sIOKAnGxpbXBpb+KAnSwg4oCcYXRlbmNpw7Nu4oCdLCDigJxhZ3JhZGFibGXigJ0NCg0KKiBUZW3DoXRpY2E6IFNlIGNlbnRyYW4gZW4gbGEgY2FsaWRhZCBkZWwgc2VydmljaW8gYWwgaHXDqXNwZWQsIGxhIG9mZXJ0YSBnYXN0cm9uw7NtaWNhIHkgbGEgcHVsY3JpdHVkIGRlbCBhbG9qYW1pZW50by4NCg0KKipDbMO6c3RlciAyIChzZW50aW1pZW50byBsaWdlcmFtZW50ZSBuZWdhdGl2bywg4oiSMC4wNCkqKg0KDQoqIFBhbGFicmFzIGNsYXZlOiDigJxydWlkb+KAnSwg4oCccHJlY2lv4oCdLCDigJxiYXJhdG/igJ0NCg0KKiBUZW3DoXRpY2E6IFF1ZWphcyBzb2JyZSBuaXZlbGVzIGRlIHJ1aWRvIHkgcGVyY2VwY2nDs24gZGUgcXVlIGxhIHRhcmlmYSBubyBjb3JyZXNwb25kZSBjb24gbGEgY2FsaWRhZCByZWNpYmlkYS4NCg0KKipDbMO6c3RlciA1IChzZW50aW1pZW50byBsaWdlcmFtZW50ZSBuZWdhdGl2bywg4oiSMC4wNCkqKg0KDQoqIFBhbGFicmFzIGNsYXZlOiDigJx0b2FsbGFz4oCdLCDigJxhZ3Vh4oCdLCDigJxiYcOxb+KAnQ0KDQoqIFRlbcOhdGljYTogUmVjbGFtYWNpb25lcyBwb3IgZmFsdGEgbyBiYWphIGNhbGlkYWQgZGUgYW1lbmlkYWRlcyBiw6FzaWNhcyBkZWwgYmHDsW8gKHRhbWHDsW8sIHN1bWluaXN0cm8gZGUgYWd1YSwgdG9hbGxhcykuDQoNCioqQ2zDunN0ZXIgNCAoc2VudGltaWVudG8gbXV5IG5lZ2F0aXZvLCDiiJIwLjEyKSoqDQoNCiogUGFsYWJyYXMgY2xhdmU6IOKAnHBlcXVlw7Fv4oCdLCDigJxzdWNpb+KAnSwg4oCcY29sZOKAnSwg4oCcaG9ycmlibGXigJ0NCg0KKiBUZW3DoXRpY2E6IENyw610aWNhcyBmdWVydGVzIGEgaGFiaXRhY2lvbmVzIGFuZ29zdGFzLCBwcm9ibGVtYXMgZGUgbGltcGllemEgeSBtYWxhcyBjb25kaWNpb25lcyBkZSB0ZW1wZXJhdHVyYS4NCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkodGlkeXRleHQpDQpsaWJyYXJ5KHRleHRzdGVtKQ0KbGlicmFyeSh0ZXh0MnZlYykNCmxpYnJhcnkoc2VudGltZW50cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkod2lkeXIpDQpsaWJyYXJ5KHRtKQ0KbGlicmFyeShTbm93YmFsbEMpDQoNCmRmX2Fpcl9jbGVhbiA8LSBkZl9haXIgJT4lDQogIG11dGF0ZShpZCA9IHJvd19udW1iZXIoKSwNCiAgICAgICAgIGNvbWVudGFyaW8gPSBzdHJfdG9fbG93ZXIoQ29tZW50YXJpb3MpLA0KICAgICAgICAgY29tZW50YXJpbyA9IHN0cl9yZXBsYWNlX2FsbChjb21lbnRhcmlvLCAiW15bOmFsbnVtOl1cXHNdIiwgIiIpKSAgIyBsaW1waWFyIHB1bnR1YWNpw7NuDQoNCiMgVG9rZW5pemFyIHkgbGVtYXRpemFyDQp0b2tlbnMgPC0gZGZfYWlyX2NsZWFuICU+JQ0KICB1bm5lc3RfdG9rZW5zKHdvcmQsIGNvbWVudGFyaW8pICU+JQ0KICBtdXRhdGUobGVtbWEgPSBsZW1tYXRpemVfd29yZHMod29yZCkpICU+JQ0KICBhbnRpX2pvaW4oc3RvcF93b3JkcywgYnkgPSBjKCJsZW1tYSIgPSAid29yZCIpKQ0KDQoNCiMgQ2FsY3VsYXIgVEYtSURGDQp0ZmlkZiA8LSB0b2tlbnMgJT4lDQogIGNvdW50KGlkLCBsZW1tYSkgJT4lDQogIGJpbmRfdGZfaWRmKHRlcm0gPSBsZW1tYSwgZG9jdW1lbnQgPSBpZCwgbiA9IG4pDQoNCiMgQ3JlYXIgbWF0cml6IGRvY3VtZW50by10w6lybWlubw0KdGZpZGZfbWF0cml4IDwtIHRmaWRmICU+JQ0KICBjYXN0X2R0bShkb2N1bWVudCA9IGlkLCB0ZXJtID0gbGVtbWEsIHZhbHVlID0gdGZfaWRmKQ0KDQojIEFwbGljYXIgSy1tZWFucyAocHVlZGVzIGFqdXN0YXIgbsO6bWVybyBkZSBjbMO6c3RlcmVzKQ0KIyBFeHRyYWVyIGxvcyBJRHMgcXVlIHJlYWxtZW50ZSBlc3TDoW4gZW4gdGZpZGZfbWF0cml4DQppZHNfdmFsaWRvcyA8LSBhcy5pbnRlZ2VyKHJvd25hbWVzKHRmaWRmX21hdHJpeCkpDQoNCiMgRWplY3V0YXIgay1tZWFucyBzb2xvIHNvYnJlIGxvcyBkb2N1bWVudG9zIHbDoWxpZG9zDQpzZXQuc2VlZCgxMjMpDQprX2NsdXN0ZXJzIDwtIGttZWFucyh0ZmlkZl9tYXRyaXgsIGNlbnRlcnMgPSA1KQ0KDQojIENyZWFyIHVuIG51ZXZvIHZlY3RvciBwYXJhIHRvZG9zIGxvcyBjb21lbnRhcmlvcw0KZGZfYWlyX2NsZWFuJGNsdXN0ZXIgPC0gTkEgICMgSW5pY2lhbGl6YXIgY29uIE5BDQoNCiMgQXNpZ25hciBjbMO6c3RlcmVzIHNvbG8gYSBsb3MgSURzIHbDoWxpZG9zDQpkZl9haXJfY2xlYW4kY2x1c3RlcltpZHNfdmFsaWRvc10gPC0ga19jbHVzdGVycyRjbHVzdGVyDQoNCiMgU2VudGltaWVudG8gcG9yIHBhbGFicmENCnNlbnRpbWllbnRvcyA8LSB0b2tlbnMgJT4lDQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSwgYnkgPSBjKCJsZW1tYSIgPSAid29yZCIpKQ0KDQojIFNlbnRpbWllbnRvIHBvciBjb21lbnRhcmlvDQpjb21lbnRhcmlvX3NlbnRpbWllbnRvIDwtIGRmX2Fpcl9jbGVhbiAlPiUNCiAgbXV0YXRlKHNlbnRpbWllbnRvID0gc2VudGltZW50KGNvbWVudGFyaW8pJHNlbnRpbWVudCkNCg0KIyBDb29jdXJyZW5jaWEgZGUgcGFsYWJyYXMNCnRva2VuX3BhaXJzIDwtIHRva2VucyAlPiUNCiAgcGFpcndpc2VfY291bnQoaXRlbSA9IGxlbW1hLCBmZWF0dXJlID0gaWQsIHNvcnQgPSBUUlVFLCB1cHBlciA9IEZBTFNFKQ0KDQojIFZpc3VhbGl6YXIgc2VudGltaWVudG8gcHJvbWVkaW8gcG9yIGNsw7pzdGVyIChjb24gbGV5ZW5kYSkNCmRmX2Fpcl9jbGVhbiAlPiUNCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogIHN1bW1hcmlzZShzZW50aW1pZW50b19tZWRpbyA9IG1lYW4oc2VudGltZW50KGNvbWVudGFyaW8pJHNlbnRpbWVudCwgbmEucm0gPSBUUlVFKSkgJT4lDQogIGdncGxvdChhZXMoeCA9IGZhY3RvcihjbHVzdGVyKSwgeSA9IHNlbnRpbWllbnRvX21lZGlvLCBmaWxsID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IFRSVUUpICsgICMgTW9zdHJhciBsYSBsZXllbmRhDQogIGxhYnModGl0bGUgPSAiU2VudGltaWVudG8gcHJvbWVkaW8gcG9yIGNsw7pzdGVyIChmYW1pbGlhIGRlIHBhbGFicmFzKSIsDQogICAgICAgeCA9ICJDbMO6c3RlciIsDQogICAgICAgeSA9ICJTZW50aW1pZW50byBwcm9tZWRpbyIsDQogICAgICAgZmlsbCA9ICJDbMO6c3RlciBhc2lnbmFkbyIpICsgICMgRXRpcXVldGEgZGUgbGEgbGV5ZW5kYQ0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCiMjICoqQ29uY2x1c2nDs24qKg0KDQoqKkFuw6FsaXNpcyBnZW9ncsOhZmljbyBkZSBsYSBvZmVydGEgaG90ZWxlcmEgeSBkZSBBaXJibmIgKDIwMTTigJMyMDI0KSoqDQoNCkR1cmFudGUgbGEgw7psdGltYSBkw6ljYWRhLCBsYSBab25hIE1ldHJvcG9saXRhbmEgZGUgTW9udGVycmV5IGhhIHZpdmlkbyB1bmEgZXhwYW5zacOzbiBzaWduaWZpY2F0aXZhIGVuIHN1IG9mZXJ0YSBkZSBhbG9qYW1pZW50byB0dXLDrXN0aWNvLCBjb25zb2xpZGFuZG8gYSBNb250ZXJyZXkgeSBTYW4gUGVkcm8gR2FyemEgR2FyY8OtYSBjb21vIGxvcyBwcmluY2lwYWxlcyBuw7pjbGVvcyBob3RlbGVyb3MgZGVsIGVzdGFkby4gTW9udGVycmV5IGxpZGVyYSBwb3Igdm9sdW1lbiwgY29uIHVuIGNyZWNpbWllbnRvIGRlIDY1JSBlbiBuw7ptZXJvIGRlIGhvdGVsZXMgcmVzcGVjdG8gYSAyMDE0LCBtaWVudHJhcyBxdWUgU2FuIFBlZHJvIGRlc3RhY2EgcG9yIHN1IHBvc2ljaW9uYW1pZW50byBlbiBlbCBzZWdtZW50byBwcmVtaXVtLCBjb24gdW4gY3JlY2ltaWVudG8gcG9yY2VudHVhbCBkZWwgMjE3JSB5IHVuIGNsYXJvIGVuZm9xdWUgaGFjaWEgZWwgdHVyaXNtbyBkZSBhbHRvIHBvZGVyIGFkcXVpc2l0aXZvLiBFc3RhIGV2b2x1Y2nDs24gcmV2ZWxhIHVuIHBhdHLDs24gZGUgZXNwZWNpYWxpemFjacOzbiB0ZXJyaXRvcmlhbCwgZG9uZGUgbGEgdWJpY2FjacOzbiBkZXRlcm1pbmEgbm8gc29sbyBlbCB0aXBvIGRlIGFsb2phbWllbnRvLCBzaW5vIHRhbWJpw6luIGVsIHBlcmZpbCBkZWwgdmlzaXRhbnRlLg0KDQoqIE1vbnRlcnJleSBjb25jZW50cmEgZWwgODklIGRlIGxvcyBob3RlbGVzIGRlbCBlc3RhZG8sIGNvbiB1biBjcmVjaW1pZW50byBhYnNvbHV0byBkZSA2MSBob3RlbGVzIGVuIGxhIMO6bHRpbWEgZMOpY2FkYS4NCg0KKiBTYW4gUGVkcm8sIGNvbiBzb2xvIDE5IGhvdGVsZXMsIG11ZXN0cmEgdW4gY3JlY2ltaWVudG8gZGUgMjE3JSwgZW5mb2NhZG8gZW4gY2F0ZWdvcsOtYXMgNSBlc3RyZWxsYXMgeSBsdWpvLg0KDQoqIERlc2FjZWxlcmFjacOzbiBkdXJhbnRlIHBhbmRlbWlhLCBwZXJvIGZ1ZXJ0ZSByZWN1cGVyYWNpw7NuIGEgcGFydGlyIGRlIDIwMjEsIGNvbiAxNSBob3RlbGVzIG51ZXZvcyBlbiAyMDI0IHkgbcOhcyBkZSAxMCBBaXJibmIgbnVldm9zIHBvciBhw7FvLg0KDQoqIFRlbmRlbmNpYSBhIHVuaWRhZGVzIHBlcXVlw7FhcyB5IGVzcGVjaWFsaXphZGFzLCBubyBuZWNlc2FyaWFtZW50ZSBjb24gbcOhcyBoYWJpdGFjaW9uZXMuDQoNCiogT2ZlcnRhIGhvdGVsZXJhIHByZWRvbWluYW50ZSBlbiAyIHkgMyBlc3RyZWxsYXMsIG9yaWVudGFkYSBhbCB0dXJpc21vIGRlIG5lZ29jaW9zIHkgY2xhc2UgbWVkaWEuDQoNCiogU2FuIFBlZHJvIGxpZGVyYSBlbiBjYWxpZmljYWNpb25lcyB5IGNvbmNlbnRyYSBsYSBvZmVydGEgZGUgbHVqbywgcmVmbGVqYW5kbyB1bmEgZXhwZXJpZW5jaWEgc3VwZXJpb3IuDQoNCiogWm9uYXMgY29tbyBTYW4gUGVkcm8geSBlbCBzdXIgZGUgTW9udGVycmV5IG11ZXN0cmFuIG1heW9yIGRpc3BlcnNpw7NuIGRlIHByZWNpb3MsIG1pZW50cmFzIHF1ZSDDoXJlYXMgcGVyaWbDqXJpY2FzIHRpZW5lbiBwcmVkb21pbmFuY2lhIGRlIEFpcmJuYiBlY29uw7NtaWNvcy4NCg0KQXPDrSwgZWwgcGFub3JhbWEgZ2VvZ3LDoWZpY28gZGVsIGhvc3BlZGFqZSBlbiBNb250ZXJyZXkgbm8gc29sbyBtYXJjYSB1bmEgZXhwYW5zacOzbiBjdWFudGl0YXRpdmEsIHNpbm8gdGFtYmnDqW4gdW5hIGRpZmVyZW5jaWFjacOzbiBjdWFsaXRhdGl2YSBiYXNhZGEgZW4gcGVyZmlsIHNvY2lvZWNvbsOzbWljbyB5IGxvY2FsaXphY2nDs24sIHlhIHF1ZSBNb250ZXJyZXkgY29uY2VudHJhIGxhIG9mZXJ0YSBkZSBnYW1hIG1lZGlhIHkgYmFqYSwgZXNwZWNpYWxtZW50ZSBlbiBzdSBjZW50cm8gaGlzdMOzcmljbywgbWllbnRyYXMgcXVlIFNhbiBQZWRybyBzZSBwb3NpY2lvbmEgY29tbyB1biBwb2xvIGRlIGhvc3BlZGFqZSBwcmVtaXVtLCBjb24gYWx0YSBjYWxpZmljYWNpw7NuIHkgcHJlY2lvcyBlbGV2YWRvcywgZXNwZWNpYWxtZW50ZSBlbiBsYSB6b25hIGRlIFZhbGxlIE9yaWVudGUuDQoNCg0KKipBbsOhbGlzaXMgZGUgc2VudGltaWVudG9zIHkgdGV4dG8gZW4gcmVzZcOxYXMgZGUgdXN1YXJpb3MqKg0KDQpFbCBlc3R1ZGlvIGRlIHJlc2XDsWFzIHkgY2FyYWN0ZXLDrXN0aWNhcyBkZSBob3RlbGVzIHkgQWlyYm5iIGFycm9qYSBsdXogc29icmUgbG9zIGZhY3RvcmVzIGNsYXZlIHF1ZSBkZWZpbmVuIHVuYSBleHBlcmllbmNpYSBwb3NpdGl2YS4gTGEgcGVyY2VwY2nDs24gZGVsIHVzdWFyaW8gZXN0w6EgcHJvZnVuZGFtZW50ZSBjb25lY3RhZGEgY29uIGFzcGVjdG9zIGNvbW8gbGltcGllemEsIGF0ZW5jacOzbiwgY29tb2RpZGFkIHkgZW50b3JubyB1cmJhbm8uIE1pZW50cmFzIGxvcyBob3RlbGVzIHN1ZWxlbiBtb3N0cmFyIGNvbnNpc3RlbmNpYSBlbiBjYWxpZGFkLCBlbiBBaXJibmIgbGEgdWJpY2FjacOzbiBwdWVkZSBwb3RlbmNpYXIgbyBwZXJqdWRpY2FyIHNpZ25pZmljYXRpdmFtZW50ZSBsYSBldmFsdWFjacOzbiBkZWwgaHXDqXNwZWQuIEEgcGVzYXIgZGUgbGFzIGRpZmVyZW5jaWFzIGVudHJlIGFtYm9zIHRpcG9zIGRlIGFsb2phbWllbnRvLCBleGlzdGUgdW5hIG5vdGFibGUgY29udmVyZ2VuY2lhIGVuIGxvcyBmYWN0b3JlcyBlbW9jaW9uYWxlcyB5IHNlbnNvcmlhbGVzIHF1ZSBkZXRlcm1pbmFuIGxhIHNhdGlzZmFjY2nDs24gZGVsIGNsaWVudGUuDQoNCiogTG9zIGFsb2phbWllbnRvcyBtZWpvciBjYWxpZmljYWRvcyBkZXN0YWNhbiBwb3IgbGltcGllemEsIGF0ZW5jacOzbiBhbCBjbGllbnRlLCBjb21vZGlkYWQgeSBidWVuYSByZWxhY2nDs24gY2FsaWRhZC1wcmVjaW8uIE1pZW50cmFzIHF1ZSwgbG9zIHBlb3JlcyBhbG9qYW1pZW50b3MgdGllbmVuIHByb2JsZW1hcyBjb21vIHJ1aWRvLCBlc3BhY2lvIHJlZHVjaWRvIHkgbWFsYXMgaW5zdGFsYWNpb25lcy4NCg0KKiBMYXMgbWVqb3JlcyB1bmlkYWRlcyB0aWVuZGVuIGEgZXN0YXIgZW4gem9uYXMgdHJhbnF1aWxhcyAoQXBvZGFjYSwgR3VhZGFsdXBlKSwgbWllbnRyYXMgcXVlIGxvcyBwZW9yZXMgZXN0w6FuIGVuIHpvbmFzIGNlbnRyYWxlcyBvIHJ1aWRvc2FzIChDZW50cm8gTW9udGVycmV5LCBTYW4gTmljb2zDoXMpLCBsbyBxdWUgc3VicmF5YSBsYSBpbXBvcnRhbmNpYSBkZWwgZW50b3JubyB1cmJhbm8uIA0KDQoqIFVuYSBtZW5vciBvZmVydGEgZGUgYW1lbmlkYWRlcyBwdWVkZSBjb21wZW5zYXJzZSBjb24gdW5hIHViaWNhY2nDs24gZXN0cmF0w6lnaWNhLCB5YSBxdWUgbG9zIGNsaWVudGVzIHZhbG9yYW4gcHJvcHVlc3RhcyBjbGFyYXMgY29tbyB0cmFucXVpbGlkYWQgbyBidWVuYSBjb25lY3RpdmlkYWQsIGluY2x1c28gZW4gem9uYXMgbm8gY8OpbnRyaWNhcyBjb21vIEFwb2RhY2EuDQoNCiogRW4gYW1ib3MgdGlwb3MgZGUgYWxvamFtaWVudG8sIGxvcyBmYWN0b3JlcyBlbW9jaW9uYWxlcyB5IHNlbnNvcmlhbGVzIOKAlGNvbW8gc2VudGlyc2UgYmllbiBhdGVuZGlkbywgZXN0YXIgZW4gdW4gZW50b3JubyBsaW1waW8geSBzaWxlbmNpb3Nv4oCUIGp1ZWdhbiB1biBwYXBlbCBkZXRlcm1pbmFudGUgZW4gbGEgZXZhbHVhY2nDs24gZ2VuZXJhbCBkZWwgc2VydmljaW8uIA0KDQpEaWNobyBhbsOhbGlzaXMgcmVmdWVyemEgbGEgaW1wb3J0YW5jaWEgZGUgZW50ZW5kZXIgbGEgZXhwZXJpZW5jaWEgZGVsIGh1w6lzcGVkIG3DoXMgYWxsw6EgZGUgbGEgaW5mcmFlc3RydWN0dXJhLCByZXNhbHRhbmRvIGVsIHJvbCBkZWNpc2l2byBkZSBsbyBlbW9jaW9uYWwgeSBjb250ZXh0dWFsLg0KDQoNCioqUHJvbsOzc3RpY28gZGUgZGVycmFtYSBlY29uw7NtaWNhIGhvdGVsZXJhICgyMDI14oCTMjAyNywgc2luIEFpcmJuYikqKg0KDQpMYSBwcm95ZWNjacOzbiBlY29uw7NtaWNhIGJhc2FkYSBlbiBlbCBtb2RlbG8gU0FSSU1BWCBpbmRpY2EgdW4gcGFub3JhbWEgbWl4dG8uIEF1bnF1ZSAyMDI1IHNlIHZpc2x1bWJyYSBjb21vIHVuIGHDsW8gZGUgcmVjdXBlcmFjacOzbiBncmFjaWFzIGFsIGRpbmFtaXNtbyBkZWwgdHVyaXNtbyBuYWNpb25hbCwgbGEgdGVuZGVuY2lhIGhhY2lhIDIwMjcgcmV2ZWxhIHVuIHBvc2libGUgZXN0YW5jYW1pZW50byBvIGluY2x1c28gY29udHJhY2Npw7NuLiBMb3MgcmVzdWx0YWRvcyBzdWdpZXJlbiBxdWUgc2luIGlubm92YWNpw7NuIG8gcG9sw610aWNhcyBhY3RpdmFzLCBlbCBpbXB1bHNvIGVjb27Ds21pY28gZGVyaXZhZG8gZGVsIHR1cmlzbW8gaG90ZWxlcm8gbm8gc2Vyw6Egc29zdGVuaWJsZSBlbiBlbCB0aWVtcG8uDQoNCiogUmVwdW50ZSBlbiAyMDI1IGNvbiBpbmdyZXNvcyB0dXLDrXN0aWNvcyBtZW5zdWFsZXMgZGUgaGFzdGEgMiwxMzUgbWRwLCBncmFjaWFzIGEgdHVyaXN0YXMgbmFjaW9uYWxlcy4NCg0KKiBGYWN0b3JlcyBjbGF2ZTogKzE0Ny44IG1kcCBwb3IgY2FkYSBtaWwgdHVyaXN0YXMgbmFjaW9uYWxlcywgKzUxLjMgbWRwIHBvciBjYWRhIHB1bnRvIGVuIGVzdGFkw61hLCArMzUuMSBtZHAgcG9yIGNhZGEgcHVudG8gZW4gb2N1cGFjacOzbi4NCg0KKiBUZW5kZW5jaWEgZGVjcmVjaWVudGU6IDEsMTE4IG1kcCBtZW5zdWFsZXMgZW4gMjAyNywgaW5jbHVzbyBlbiBlc2NlbmFyaW9zIG9wdGltaXN0YXMuDQoNCiogTG9zIGVmZWN0b3MgYWN1bXVsYWRvcyBkZSBsYXMgdmFyaWFibGVzIGV4cGxpY2F0aXZhcyB0aWVuZGVuIGEgcGVyZGVyIHRyYWNjacOzbiwgcmVkdWNpZW5kbyBlbCBwcm9tZWRpbyBtZW5zdWFsIMOzcHRpbW8gYSAxLDExOCBtZHAsIGxvIHF1ZSB1Z2llcmUgdW5hIHBvc2libGUgc2F0dXJhY2nDs24gZGVsIG1lcmNhZG8gbyB1bmEgZmFsdGEgZGUgc29zdGVuaWJpbGlkYWQgZGVsIGNyZWNpbWllbnRvIHNpbiBpbm5vdmFjacOzbiBvIGVzdMOtbXVsb3MgYWRpY2lvbmFsZXMuDQoNCiogU2UgcmVxdWllcmUgdW5hIGVzdHJhdGVnaWEgZGUgcG9sw610aWNhIHDDumJsaWNhIHkgcHJvbW9jacOzbiBkaXJpZ2lkYXMgYWwgbWVyY2FkbyBpbnRlcm5vIHF1ZSwgc2kgYmllbiBwdWVkZW4gcmVmb3J6YXIgbG9zIHJlc3VsdGFkb3MgZW4gZWwgY29ydG8gcGxhem8sIGRlYmVuIGNvbXBsZW1lbnRhcnNlIGNvbiBhY2Npb25lcyBlc3RydWN0dXJhbGVzLCBkaXZlcnNpZmljYWNpw7NuIGRlIG1lcmNhZG9zIHkgc2VnbWVudGFjacOzbiBlZmVjdGl2YS4NCg0KKiBMYXMgZXN0cmF0ZWdpYXMgbm8gZGViZW4gY2VudHJhcnNlIMO6bmljYW1lbnRlIGVuIGluZnJhZXN0cnVjdHVyYSwgc2lubyBlbiBpbnRlcnZlbmNpb25lcyBwcm9hY3RpdmFzIGNvbW8gY2FtcGHDsWFzIGVzdGFjaW9uYWxlcywgcmVjb25maWd1cmFjacOzbiBkZWwgcHJvZHVjdG8gdHVyw61zdGljbyB5IGF0cmFjY2nDs24gZGUgbnVldm9zIHNlZ21lbnRvcywgY29tbyBlbCB0dXJpc21vIGV4dHJhbmplcm8gbyBkZSBuYXR1cmFsZXphLg0KDQpFbCBwcm9uw7NzdGljbyBhcHVudGEgYSBxdWUgZWwgbW9kZWxvIGFjdHVhbCBkZSBjcmVjaW1pZW50byBoYSBhbGNhbnphZG8gdW4gbMOtbWl0ZSB5IGVzIG1vbWVudG8gZGUgcmVkaXNlw7FhciBlbCBlbmZvcXVlIHR1csOtc3RpY28gY29uIHZpc2nDs24gZXN0cmF0w6lnaWNhIGRlIGxhcmdvIHBsYXpvLiBBZGVtw6FzLCBlbCBhbsOhbGlzaXMgY29tcGFyYXRpdm8gZGUgbG9zIG1vZGVsb3MgcGFyYSB0dXJpc3RhcyBuYWNpb25hbGVzIHkgZXh0cmFuamVyb3MgZGVzdGFjYSBsYSBuZWNlc2lkYWQgZGUgYWRvcHRhciB1biBlbmZvcXVlIGRlc2RlIHVuYSBwZXJzcGVjdGl2YSBkZSBwbGFuaWZpY2FjacOzbiB5IGZvcnRhbGVjaW1pZW50byBkZWwgdHVyaXNtbyBleHRyYW5qZXJvIG1lZGlhbnRlIGNhbXBhw7FhcyBlc3BlY8OtZmljYXMgbG8gY3VhbCBwb2Ryw61hIHRyYWR1Y2lyc2UgZW4gcmV0b3Jub3MgbcOhcyBzb3N0ZW5pYmxlcyBlbiBlbCBtZWRpYW5vIHkgbGFyZ28gcGxhem8sIG1pZW50cmFzIHF1ZSBlbCB0dXJpc21vIG5hY2lvbmFsIHJlcXVlcmlyw61hIHVuIHNlZ3VpbWllbnRvIGNvbnN0YW50ZSB5IGVzdHJhdGVnaWFzIGRlIGRpdmVyc2lmaWNhY2nDs24gcGFyYSBtaXRpZ2FyIHN1cyBmbHVjdHVhY2lvbmVzLg0KDQoNCioqU2VnbWVudGFjacOzbiBkZSBhc2lzdGVudGVzIGEgZXZlbnRvcyBtYXNpdm9zIChjYXNvIFBhbCBOb3J0ZSkqKg0KDQpFbCBhbsOhbGlzaXMgZGVsIHBlcmZpbCBkZSBsb3MgYXNpc3RlbnRlcyBhIGV2ZW50b3MgbWFzaXZvcyBtdWVzdHJhIHVuYSBvcG9ydHVuaWRhZCBjbGFyYSBwYXJhIGRldG9uYXIgZWwgdHVyaXNtbyBkZSBlbnRyZXRlbmltaWVudG8uIEFsIHNlZ21lbnRhciBwb3IgY29tcG9ydGFtaWVudG8gZGUgZ2FzdG8gbcOhcyBxdWUgcG9yIGRhdG9zIHNvY2lvZGVtb2dyw6FmaWNvcywgZW1lcmdlbiBwZXJmaWxlcyBlc3RyYXTDqWdpY29zIHF1ZSBwdWVkZW4gc2VyIGFwcm92ZWNoYWRvcyBwYXJhIGdlbmVyYXIgbWF5b3IgZGVycmFtYSBlY29uw7NtaWNhIGEgdHJhdsOpcyBkZSBleHBlcmllbmNpYXMgY29tcGxldGFzIHkgZXN0YW5jaWFzIHByb2xvbmdhZGFzLiBTZSBpZGVudGlmaWNhcm9uIGRvcyBzZWdtZW50b3MgY2xhdmU6DQoNCiogTWVyY2FkbyBlc3RyZWxsYTogZGlzcHVlc3RvcyBhIHBhZ2FyIHBvciBleHBlcmllbmNpYXMgY29tcGxldGFzLg0KDQoqIE1lcmNhZG8gcG90ZW5jaWFsOiBzZW5zaWJsZXMgYSBpbmNlbnRpdm9zIHBhcmEgZXh0ZW5kZXIgc3UgZXN0YWTDrWEuDQoNCkZhY3RvcmVzIGNvbW8gZHVyYWNpw7NuIGRlIGxhIGVzdGFuY2lhIHkgdGlwbyBkZSBhbG9qYW1pZW50byBpbXBhY3RhbiBkaXJlY3RhbWVudGUgZW4gbGEgZGVycmFtYSBlY29uw7NtaWNhLiBQb3IgZWxsbywgbGEgb2ZlcnRhIHR1csOtc3RpY2EgZGlmZXJlbmNpYWRhIHB1ZWRlIG1heGltaXphciBlbCBnYXN0byBtZWRpYW50ZSBwYXF1ZXRlcyBpbnRlZ3JhbGVzIHkgZXhwZXJpZW5jaWFzIHBlcnNvbmFsaXphZGFzLkRpY2hvIGhhbGxhemdvIHN1YnJheWEgbGEgaW1wb3J0YW5jaWEgZGUgcmVkaXJpZ2lyIGVzZnVlcnpvcyBoYWNpYSBsYSBjcmVhY2nDs24gZGUgZXhwZXJpZW5jaWFzIHR1csOtc3RpY2FzIGVudm9sdmVudGVzLCBxdWUgaW50ZWdyZW4gaG9zcGl0YWxpZGFkLCBlbnRyZXRlbmltaWVudG8geSB2YWxvciBhZ3JlZ2Fkby4NCg0KDQoqKlByb27Ds3N0aWNvIGRlIGRlcnJhbWEgZWNvbsOzbWljYSBkZWwgdHVyaXNtbyBkZSByZXVuaW9uZXMgKE9DViBNb250ZXJyZXkpKioNCg0KRWwgdHVyaXNtbyBkZSByZXVuaW9uZXMgcmVwcmVzZW50YSB1biBwaWxhciBlc3RyYXTDqWdpY28gcGFyYSBsYSBlY29ub23DrWEgdHVyw61zdGljYSBkZSBNb250ZXJyZXkuIEVsIG1vZGVsbyBBUklNQVggYXBsaWNhZG8gYSBldmVudG9zIG9yZ2FuaXphZG9zIHBvciBsYSBPQ1YgY29uZmlybWEgc3UgaW1wYWN0byBkaXJlY3RvIGVuIGxhIG9jdXBhY2nDs24gaG90ZWxlcmEgeSBlbiBsb3MgaW5ncmVzb3MsIGNvbiBwb3RlbmNpYWwgZGUgY3JlY2ltaWVudG8gc29zdGVuaWRvIHNpIHNlIGFjb21wYcOxYSBkZSB1bmEgZXhwYW5zacOzbiBkZWwgY2FsZW5kYXJpbyB5IGRpdmVyc2lmaWNhY2nDs24gZGUgZXZlbnRvcy4NCg0KKiBTZSBwcm95ZWN0YSB1bmEgbWVqb3JhIGdyYWR1YWwgZW4gMjAyNSwgY29uIHByb21lZGlvcyBtZW5zdWFsZXMgc3VwZXJpb3JlcyBhIDQyMCBtZHAuDQoNCiogQ2FkYSAxMCwwMDAgYXNpc3RlbnRlcyBhZGljaW9uYWxlcyBnZW5lcmFuIGhhc3RhICsxOC4yIG1kcCwgeSBjYWRhIHB1bnRvIGRlIG9jdXBhY2nDs24gaG90ZWxlcmEgKzYuOSBtZHAuDQoNCiogU2UgYWR2aWVydGUgcXVlIHNpbiB1biBjcmVjaW1pZW50byBwcm9wb3JjaW9uYWwgZW4gdmFyaWFibGVzIGNsYXZlIOKAlGNvbW8gbnVldm9zIGV2ZW50b3MsIGRpdmVyc2lmaWNhY2nDs24gZGUgb2ZlcnRhIG8gYXRyYWNjacOzbiBkZSBleHRyYW5qZXJvc+KAlCwgbG9zIGluY3JlbWVudG9zIGVuIGxhIGRlcnJhbWEgcG9kcsOtYW4gZXN0YW5jYXJzZSBlbiBlbCBtZWRpYW5vIHBsYXpvLiANCg0KKiBTZSByZWNvbWllbmRhIHByb2Zlc2lvbmFsaXphciBsYSBjYWRlbmEgZGUgdmFsb3IgdHVyw61zdGljYSwgYW1wbGlhciBlbCBjYWxlbmRhcmlvIGRlIGV2ZW50b3MgeSBhdHJhZXIgbWVyY2Fkb3MgaW50ZXJuYWNpb25hbGVzLg0KDQpMYSBjbGF2ZSBzZXLDoSBtYW50ZW5lciBsYSB2aXRhbGlkYWQgZGVsIHNlY3RvciBjb24gZXZlbnRvcyBkZSBjYWxpZGFkLCBob3NwaXRhbGlkYWQgc3VmaWNpZW50ZSB5IHVuYSBlc3RyYXRlZ2lhIGRlIHByb21vY2nDs24gZWZpY2F6Lg0KDQoNCkVuIGNvbmNsdXNpw7NuLCBsb3MgZGlzdGludG9zIGFuw6FsaXNpcyBjb25maXJtYW4gcXVlIGxhIFpvbmEgTWV0cm9wb2xpdGFuYSBkZSBNb250ZXJyZXkgc2UgaGEgY29uc29saWRhZG8gdW5hIG9mZXJ0YSB0dXLDrXN0aWNhIHJvYnVzdGEsIGF1bnF1ZSBwb2xhcml6YWRhLCBjb24gdGVuZGVuY2lhcyBjbGFyYXMgaGFjaWEgbGEgZXNwZWNpYWxpemFjacOzbiBkZSBzZWdtZW50b3MuIFNpIGJpZW4gbGEgaW5mcmFlc3RydWN0dXJhIHkgbGEgcGVyY2VwY2nDs24gZGVsIHNlcnZpY2lvIG11ZXN0cmFuIGF2YW5jZXMgc2lnbmlmaWNhdGl2b3MsIGxvcyBwcm9uw7NzdGljb3MgZWNvbsOzbWljb3Mgc3VnaWVyZW4gbGEgbmVjZXNpZGFkIGRlIGlubm92YXIsIGRpdmVyc2lmaWNhciB5IGZvcnRhbGVjZXIgbGFzIHBvbMOtdGljYXMgcMO6YmxpY2FzIHBhcmEgc29zdGVuZXIgZWwgY3JlY2ltaWVudG8uIEVuIGVzdGUgZW50b3JubywgdW5hIGVzdHJhdGVnaWEgaW50ZWdyYWwgcXVlIGNvbWJpbmUgY2FsaWRhZCwgc2VnbWVudGFjacOzbiB5IHByb21vY2nDs24gaW50ZWxpZ2VudGUgc2Vyw6EgY2xhdmUgcGFyYSBhc2VndXJhciBsYSBjb21wZXRpdGl2aWRhZCB0dXLDrXN0aWNhIGRlIGxhIHJlZ2nDs24gZW4gbG9zIHByw7N4aW1vcyBhw7Fvcy4NCg0KDQojIyAqKlJlZmVyZW5jaWFzKioNCg0KKiBEQVRMQVMuICgyMDI0KS4gIERhc2hib2FyZCBkZSBUdXJpc21vIGRlIE51ZXZvIExlw7NuLiBodHRwczovL2Rhc2hhbXR5LmNvbS8NCg0KKiBTYXVjZWRvLCBELiAoMjAyNCwgYWJyaWwgMDQpLiBIb3cgdG8gVXNlIEdvb2dsZSBNYXBzIFBsYWNlcyBBUEkgaW4gUi4gUlB1YnMuIGZpbGU6Ly8vQzovVXNlcnMvQVZSSUwvRG93bmxvYWRzL0dvb2dsZSUyME1hcHMlMjBQbGFjZXMuaHRtbA0KDQoqIFDDqXJleiwgSi4gSS4gKDIwMjEsIGFicmlsIDcpLiBQcmVkaWNjacOzbiBkZSB2aXNpdGFudGVzIGEgbXVzZW9zIGVuIE1hZHJpZCBjb24gUHJvcGhldC4gUlB1YnMuIGh0dHBzOi8vcnB1YnMuY29tL2phaW1laXNhYWNwLzc2MDM1NQ0KDQoqIERFTlVFLiAoMjAyNCkuIGh0dHBzOi8vd3d3LmluZWdpLm9yZy5teC9hcHAvbWFwYS9kZW51ZS9kZWZhdWx0LmFzcHgjOn46dGV4dD1FbCUyMElORUdJJTIwcG9uZSUyMGElMjBzdSxwb3IlMjBwYXJ0ZSUyMGRlJTIwbG9zJTIwdXN1YXJpb3MuDQoNCiogU2VjcmV0YXLDrWEgZGUgVHVyaXNtbyBkZSBOdWV2byBMZcOzbi4gKDIwMjQpLiBQcmVzZW50YSBUdXJpc21vIGRlIE5MIGxvZ3JvcyB5IGF2YW5jZXMgYSB0cmVzIGHDsW9zIGRlIEdvYmllcm5vLiBSZWN1cGVyYWRvIGVsIDI1IGRlIE1hcnpvIGRlbCAyMDI1LCBkZSBodHRwczovL3d3dy5ubC5nb2IubXgvZXMvYm9sZXRpbmVzL3ByZXNlbnRhLXR1cmlzbW8tZGUtbmwtbG9ncm9zLXktYXZhbmNlcy10cmVzLWFub3MtZGUtZ29iaWVybm8NCg0KKiBHb2JpZXJubyBkZSBOdWV2byBMZcOzbi4gKDIwMjUsIDEzIGZlYnJlcm8pLiBUdXJpc21vLCBJbm5vdmFjacOzbiB5IENyZWNpbWllbnRvOiBOdWV2byBMZcOzbiBwcmVzZW50YSBzdSBlc3RyYXRlZ2lhIGVuIElOQ01UWSAyMDI1LiBodHRwczovL25sLmdvYi5teC9lcy9ib2xldGluZXMvdHVyaXNtby1pbm5vdmFjaW9uLXktY3JlY2ltaWVudG8tbnVldm8tbGVvbi1wcmVzZW50YS1zdS1lc3RyYXRlZ2lhLWVuLWluY210eS0yMDI1Izp+OnRleHQ9R2VuZXJhY2klQzMlQjNuJTIwZGUlMjByaXF1ZXphLSxUdXJpc21vJTJDJTIwSW5ub3ZhY2klQzMlQjNuJTIweSUyMENyZWNpbWllbnRvOiUyME51ZXZvJTIwTGUlQzMlQjNuJTIwcHJlc2VudGElMjBzdSUyMGVzdHJhdGVnaWElMjBlbix5JTIwbGElMjBkaWdpdGFsaXphY2klQzMlQjNuJTIwZGVsJTIwc2VjdG9yLiANCg0KKiBGbG9yZXMsIEwuICgyMDIzLCAyNyBzZXB0aWVtYnJlKS4gTnVldm8gTGXDs24gZmlybWEgYWN1ZXJkbyBjb24gQWlyYm5iIHBhcmEgcHJvbW92ZXIgdHVyaXNtbyByZXNwb25zYWJsZS4gRWwgRWNvbm9taXN0YS4gaHR0cHM6Ly93d3cuZWxlY29ub21pc3RhLmNvbS5teC9lc3RhZG9zL051ZXZvLUxlb24tZmlybWEtYWN1ZXJkby1jb24tQWlyYm5iLXBhcmEtcHJvbW92ZXItdHVyaXNtby1yZXNwb25zYWJsZS0yMDIzMDkyNy0wMDc1Lmh0bWwjOn46dGV4dD1Ccm93c2VyJTIwbm90JTIwY29tcGF0aWJsZSZ0ZXh0PUNhYmUlMjBtZW5jaW9uYXIlMjBxdWUlMjBlc3RhZG9zJTIwY29tbyxwb2wlQzMlQUR0aWNhcyUyMHAlQzMlQkFibGljYXMlMjBlbiUyMGxhJTIwbWF0ZXJpYS4NCg0KKiBILiBDb25ncmVzbyBkZWwgRXN0YWRvIGRlIE51ZXZvIExlw7NuLiAocy4gZi4pLiBMRVkgREUgRk9NRU5UTyBBTCBUVVJJU01PIERFTCBFU1RBRE8gREUgTlVFVk8gTEXDk04uIGh0dHBzOi8vd3d3LmhjbmwuZ29iLm14L3RyYWJham9fbGVnaXNsYXRpdm8vbGV5ZXMvbGV5ZXMvbGV5X2RlX2ZvbWVudG9fYWxfdHVyaXNtb19kZWxfZXN0YWRvX2RlX251ZXZvX2xlb24vDQoNCiogR29iaWVybm8gZGVsIEVzdGFkbyBkZSBOdWV2byBMZcOzbi4gKDIwMjUpLiBUdXJpc21vLCBpbm5vdmFjacOzbiB5IGNyZWNpbWllbnRvOiBOdWV2byBMZcOzbiBwcmVzZW50YSBzdSBlc3RyYXRlZ2lhIGVuIElOQ01UWSAyMDI1LiBodHRwczovL25sLmdvYi5teC9lcy9ib2xldGluZXMvdHVyaXNtby1pbm5vdmFjaW9uLXktY3JlY2ltaWVudG8tbnVldm8tbGVvbi1wcmVzZW50YS1zdS1lc3RyYXRlZ2lhLWVuLWluY210eS0yMDI1Lg0KDQoqIEVsIEVjb25vbWlzdGEuICgyMDI0LCBtYXJ6byAyMikuIE1vbnRlcnJleSBzZSBlc3TDoSBjb252aXJ0aWVuZG8gZW4gdW4gaW3DoW4gZGVsIHR1cmlzbW8gZGUgcmV1bmlvbmVzIGVuIGVsIG5vcnRlIGRlbCBwYcOtcy4gaHR0cHM6Ly93d3cuZWxlY29ub21pc3RhLmNvbS5teC9lc3RhZG9zL01vbnRlcnJleS1zZS1lc3RhLWNvbnZpcnRpZW5kby1lbi11bi1pbWFuLWRlbC10dXJpc21vLWRlLXJldW5pb25lcy1lbi1lbC1ub3J0ZS1kZWwtcGFpcy0yMDI0MDMyMi0wMTEwLmh0bWwNCg0KKiBFbCBQYcOtcy4gKDIwMjQsIG9jdHVicmUgNSkuIExhcyBhbHRhcyB2ZW50YXMgZGUgbGEgRmVyaWEgZGVsIExpYnJvIE1vbnRlcnJleSByb21wZW4gZWwgZXN0aWdtYSBkZWwgbm9ydGUgcXVlIG5vIGxlZS4gaHR0cHM6Ly9lbHBhaXMuY29tL21leGljby8yMDI0LTEwLTA1L2xhcy1hbHRhcy12ZW50YXMtZGUtbGEtZmVyaWEtZGVsLWxpYnJvLW1vbnRlcnJleS1yb21wZW4tZWwtZXN0aWdtYS1kZWwtbm9ydGUtcXVlLW5vLWxlZS5odG1sDQogICAgICAgDQoqIFBDTUEuICgyMDI0KS4gQ29sYWJvcmFjacOzbiBww7pibGljby1wcml2YWRhOiBDbGF2ZXMgcGFyYSBmb3J0YWxlY2VyIGVsIHR1cmlzbW8geSBsYSBpbmR1c3RyaWEgZGUgcmV1bmlvbmVzIGVuIE1vbnRlcnJleS4gaHR0cHM6Ly93d3cucGNtYS5vcmcvY29sYWJvcmFjaW9uLXB1YmxpY28tcHJpdmFkYS1jbGF2ZXMtZm9ydGFsZWNlci10dXJpc21vLWluZHVzdHJpYS1yZXVuaW9uZXMtbW9udGVycmV5Lw0KDQoqIEdydXBvIEFlcm9wdWVydGFyaW8gQ2VudHJvLU5vcnRlIE9NQS4gKDIwMjQpLiBOLkwuIExsZWdhZGFzIGRlIFBhc2FqZXJvcyBhbCBBZXJvcHVlcnRvIGRlIE1vbnRlcnJleS4gUmVjdXBlcmFkbyBlbCAyNSBkZSBNYXJ6byBkZWwgMjAyNCwgZGUgaHR0cDovL2RhdG9zLm5sLmdvYi5teC9uLWwtbGxlZ2FkYXMtZGUtZXh0cmFuamVyb3MtYWwtYWVyb3B1ZXJ0by1kZS1tb250ZXJyZXkvDQo=