Ejercicio Clase: Población

Instalar paquetes y librerías

#install.packages("forecast")
#install.packages("tidyverse")
#install.packages("dplyr")
#install.packages("ggplot2")
#install.packages("sf")
library(forecast)
library(tidyverse)
library(dplyr)
library(ggplot2)
library(sf)

Importar base de datos

poblacion <- read.csv("population.csv")

Entender la base de datos

# Analisis Descriptivo
summary(poblacion)
##     state                year        population      
##  Length:6020        Min.   :1900   Min.   :   43000  
##  Class :character   1st Qu.:1930   1st Qu.:  901483  
##  Mode  :character   Median :1960   Median : 2359000  
##                     Mean   :1960   Mean   : 3726003  
##                     3rd Qu.:1990   3rd Qu.: 4541883  
##                     Max.   :2019   Max.   :39512223
# Tipos de Variables
str(poblacion)
## 'data.frame':    6020 obs. of  3 variables:
##  $ state     : chr  "AK" "AK" "AK" "AK" ...
##  $ year      : int  1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 ...
##  $ population: int  135000 158000 189000 205000 215000 222000 224000 231000 224000 224000 ...

Series de Tiempo de la Población en Texas

poblacion_TX <- poblacion %>%
  filter(state == "TX")

ggplot(poblacion_TX, aes(x=year, y=population)) +
  geom_line() +
  labs(title =  "Población Texas", x = "Año", y = "Población")

ts_TX <- ts(poblacion_TX$population, start = 1900, frequency = 1) # ST Anual

#ARIMA
arima_TX <- auto.arima(ts_TX)

summary(arima_TX)
## Series: ts_TX 
## ARIMA(0,2,2) 
## 
## Coefficients:
##           ma1      ma2
##       -0.5950  -0.1798
## s.e.   0.0913   0.0951
## 
## sigma^2 = 1.031e+10:  log likelihood = -1527.14
## AIC=3060.28   AICc=3060.5   BIC=3068.6
## 
## Training set error measures:
##                    ME     RMSE      MAE       MPE      MAPE      MASE
## Training set 12147.62 99818.31 59257.39 0.1046163 0.5686743 0.2672197
##                     ACF1
## Training set -0.02136734
#Pronostico del ARIMA
pronostico_ArimaTX <- forecast(arima_TX, level = 95, h=10)
print(pronostico_ArimaTX)
##      Point Forecast    Lo 95    Hi 95
## 2020       29398472 29199487 29597457
## 2021       29806827 29463665 30149990
## 2022       30215183 29742956 30687410
## 2023       30623538 30024100 31222977
## 2024       31031894 30303359 31760429
## 2025       31440249 30579246 32301253
## 2026       31848605 30851090 32846119
## 2027       32256960 31118581 33395339
## 2028       32665316 31381587 33949044
## 2029       33073671 31640070 34507272
#Grafico del Pronostico
plot(pronostico_ArimaTX, main = "Poblacion en Texas 1990 - 2029 (Pronostico)")

Ejercicio Clase: Mapas

Importar Librerias y Paquetes

#install.packages("maps")
library(maps)

Analisis Descriptivo y Crear un mapa

# Analisis Descriptivo
summary(poblacion)
##     state                year        population      
##  Length:6020        Min.   :1900   Min.   :   43000  
##  Class :character   1st Qu.:1930   1st Qu.:  901483  
##  Mode  :character   Median :1960   Median : 2359000  
##                     Mean   :1960   Mean   : 3726003  
##                     3rd Qu.:1990   3rd Qu.: 4541883  
##                     Max.   :2019   Max.   :39512223
# Tipos de Variables
str(poblacion)
## 'data.frame':    6020 obs. of  3 variables:
##  $ state     : chr  "AK" "AK" "AK" "AK" ...
##  $ year      : int  1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 ...
##  $ population: int  135000 158000 189000 205000 215000 222000 224000 231000 224000 224000 ...
#Crear un mapa de EUA
map(database = "state")
map(database = "state", regions = "Texas", col = "red", fill = TRUE, add = TRUE)
map(database = "state", regions = "New York", col = "blue", fill = TRUE, add = TRUE)
title(main = "Pronostico de Poblacion de EUA")

Mapas Poblacionales de EUA por década 1950 - 2050

# Dataset a modificar
poblacion_extendida <- poblacion

# Identificamos los estados únicos
estados <- unique(poblacion$state)

# Para cada estado, entrenamos un modelo ARIMA y hacemos forecast hasta 2050
for(st in estados){
  
  # Filtrar datos de ese estado y ordenarlos por año
  datos_st <- poblacion %>%
    filter(state == st) %>%
    arrange(year)
  
  # Determinar el último año disponible
  ultimo_anio <- max(datos_st$year)
  
  # Crear serie de tiempo
  ts_st <- ts(datos_st$population,
              start = min(datos_st$year),
              end   = ultimo_anio,
              frequency = 1)  # Anual
  
  # Entrenar el modelo ARIMA
  modelo_st <- auto.arima(ts_st)
  
  # Calcular cuántos años hay que pronosticar
  # (solo hacemos forecast si el ultimo_anio es < 2050)
  h_years <- 2050 - ultimo_anio
  
  if(h_years > 0){
    # Hacemos el pronóstico
    pronostico <- forecast(modelo_st, h = h_years)
    
    # Creamos un data frame con los resultados
    anios_pronostico <- (ultimo_anio + 1):2050
    poblacion_pronosticada <- as.numeric(pronostico$mean)
    
    df_forecast <- data.frame(
      state = st,
      year  = anios_pronostico,
      population = poblacion_pronosticada
    )
    
    # Agregamos filas con la población pronosticada
    poblacion_extendida <- rbind(poblacion_extendida, df_forecast)
  }
}

# 3) Función para graficar el mapa para un año dado ------------
plot_map <- function(year) {
  
  # Filtramos los datos para el año solicitado
  data_year <- poblacion_extendida %>%
    filter(year == !!year)
  
  # Cargar datos geográficos de EE.UU.
  states_map <- map_data("state")
  
  # Necesitamos relacionar la sigla (state) con el nombre en minúsculas
  # R trae dos vectores auxiliares: state.abb (siglas) y state.name (nombres completos)
  data_year <- data_year %>%
    mutate(region = tolower(state.name[match(state, state.abb)])) %>%
    right_join(states_map, by = "region")
  
  # Graficar
  ggplot(data_year, aes(x = long, y = lat, group = group, fill = population)) +
    geom_polygon(color = "black") +
    # Gradiente verde -> rojo
    scale_fill_gradient(
      low = "green",   # color mínimo
      high = "red",    # color máximo
      name = "Población"
    ) +
    labs(
      title = paste("Población por Estado en", year)
    ) +
    theme_void() +
    theme(
      legend.position = "right",
      plot.title = element_text(size = 16, face = "bold")
    )
}

# 4) Graficar el mapa cada década entre 1900 y 2050 -------------
for(year in seq(1900, 2050, by = 10)) {
  print(plot_map(year))
}

Actividad 2: Leche Saborizada Hershey’s

Importar librerias y paquetes

#install.packages("readxl")
library(readxl)

Importar Bases de Datos

ventas <- read_excel("Ventas_Históricas_Lechitas.xlsx")

1. Modelo ARIMA

ts_ventas <- ts(ventas$Ventas, start = c(2017,1), frequency = 12) # ST Mensual

autoplot(ts_ventas) +
  labs(title = "Ventas de Leche Saborizada Hershey's", x = "Tiempo", y = "Miles de USD")

#ARIMA
arima_ventas <- auto.arima(ts_ventas)

summary(arima_ventas)
## Series: ts_ventas 
## ARIMA(1,0,0)(1,1,0)[12] with drift 
## 
## Coefficients:
##          ar1     sar1     drift
##       0.6383  -0.5517  288.8979
## s.e.  0.1551   0.2047   14.5026
## 
## sigma^2 = 202701:  log likelihood = -181.5
## AIC=371   AICc=373.11   BIC=375.72
## 
## Training set error measures:
##                    ME    RMSE    MAE        MPE      MAPE       MASE      ACF1
## Training set 25.22158 343.864 227.17 0.08059932 0.7069542 0.06491044 0.2081026
# Generar pronostico de los proximos 12 meses (Año 2020)
pronostico_ventas <- forecast(arima_ventas, level = 95, h = 12)
pronostico_ventas
##          Point Forecast    Lo 95    Hi 95
## Jan 2020       35498.90 34616.48 36381.32
## Feb 2020       34202.17 33155.28 35249.05
## Mar 2020       36703.01 35596.10 37809.92
## Apr 2020       36271.90 35141.44 37402.36
## May 2020       37121.98 35982.07 38261.90
## Jun 2020       37102.65 35958.90 38246.40
## Jul 2020       37151.04 36005.73 38296.34
## Aug 2020       38564.64 37418.70 39710.58
## Sep 2020       38755.22 37609.03 39901.42
## Oct 2020       39779.02 38632.72 40925.32
## Nov 2020       38741.63 37595.28 39887.97
## Dec 2020       38645.86 37499.50 39792.22
# Graficar pronostico ARIMA
autoplot(pronostico_ventas) +
  labs(title = "Pronostico de Ventas 2020 de Leche Saborizada Hershey's",
       x = "Tiempo", y = "Miles de USD")

2. Modelo Regresión Lineal

ventas$mes <- 1:36
regresion_ventas <- lm(Ventas ~ mes, data = ventas)

summary(regresion_ventas)
## 
## Call:
## lm(formula = Ventas ~ mes, data = ventas)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2075.79  -326.41    33.74   458.40  1537.04 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 24894.67     275.03   90.52   <2e-16 ***
## mes           298.37      12.96   23.02   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 808 on 34 degrees of freedom
## Multiple R-squared:  0.9397, Adjusted R-squared:  0.9379 
## F-statistic: 529.8 on 1 and 34 DF,  p-value: < 2.2e-16
siguiente_anio <- data.frame(mes = 37:48)
prediccion_regresion <- predict(regresion_ventas, siguiente_anio)
prediccion_regresion
##        1        2        3        4        5        6        7        8 
## 35934.49 36232.86 36531.23 36829.61 37127.98 37426.35 37724.73 38023.10 
##        9       10       11       12 
## 38321.47 38619.85 38918.22 39216.59
plot(ventas$mes, ventas$Ventas, 
     main = "Pronostico de Ventas 2020 de Leche Saborizada Hershey's",
     xlim = c(1,50),
     ylim = range(c(ventas$Ventas,prediccion_regresion)),
     xlab = "Tiempo",
     ylab = "Miles USD") +
abline(regresion_ventas, col = "blue") +
points(siguiente_anio$mes, prediccion_regresion, col = "red")

## integer(0)
predicciones_reales <- predict(regresion_ventas, ventas)
MAPE_reg <- mean(abs((ventas$Ventas - predicciones_reales)/ ventas$Ventas))*100

MAPE_reg
## [1] 2.011297

3. Elección del Modelo y Conclusión

# MAPE ARIMA
MAPE_arima <- 0.7069542
MAPE_arima
## [1] 0.7069542
# MAPE REG
MAPE_reg
## [1] 2.011297

Conclusion:
La predicción del SARIMA es más acertada que la proyección de la regresión lineal. Al contar con un MAPE menor (0.71) con respecto a la Regresion Lineal (2.01), se elige el SARIMA como el mejor modelo.

Por lo que se puede esperar que las ventas de Hersheys incrementen en el 2020 de la siguiente manera.

Escenarios:

Mes y Año Realista Pesimista Optimista
Jan 2020 $35498.90 $34616.48 $36381.32
Feb 2020 $34202.17 $33155.28 $35249.05
Mar 2020 $36703.01 $35596.10 $37809.92
Apr 2020 $36271.90 $35141.44 $37402.36
May 2020 $37121.98 $35982.07 $38261.90
Jun 2020 $37102.65 $35958.90 $38246.40
Jul 2020 $37151.04 $36005.73 $38296.34
Aug 2020 $38564.64 $37418.70 $39710.58
Sep 2020 $38755.22 $37609.03 $39901.42
Oct 2020 $39779.02 $38632.72 $40925.32
Nov 2020 $38741.63 $37595.28 $39887.97
Dec 2020 $38645.86 $37499.50 $39792.22
ventas_anio <- read.csv("ventas_por_anio.csv")

ggplot(ventas_anio, aes(x=mes, y=ventas, col=as.factor(anio), group=anio)) + 
  geom_line() +
  labs(title = "Ventas de leche saborizada Hershey´s por año", x = "Mes", y = "Miles USD") +
  scale_x_continuous(breaks = 1:12)

Recomendacion:
- Aumentar el consumo de leche saborizada Hershey’s en el primer semestre del año mediante campañas publicitarias.

LS0tDQp0aXRsZTogIkFjdGl2aWRhZCAyIg0KYXV0aG9yOiAiUm9kcmlnbyBBcnJveW8gLSBBMDE3NDczODAiDQpkYXRlOiAiMjAyNS0wMi0xNyINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KICAgIHRoZW1lOiBjZXJ1bGVhbg0KLS0tDQoNCiMgRWplcmNpY2lvIENsYXNlOiBQb2JsYWNpw7NuDQohW10ocG9ibGFjaW9uLmdpZikNCg0KDQojIyBJbnN0YWxhciBwYXF1ZXRlcyB5IGxpYnJlcsOtYXMNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojaW5zdGFsbC5wYWNrYWdlcygiZm9yZWNhc3QiKQ0KI2luc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQojaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KI2luc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KI2luc3RhbGwucGFja2FnZXMoInNmIikNCg0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGZvcmVjYXN0KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShzZikNCmBgYA0KDQojIyBJbXBvcnRhciBiYXNlIGRlIGRhdG9zDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcG9ibGFjaW9uIDwtIHJlYWQuY3N2KCJwb3B1bGF0aW9uLmNzdiIpDQpgYGANCg0KIyMgRW50ZW5kZXIgbGEgYmFzZSBkZSBkYXRvcw0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgQW5hbGlzaXMgRGVzY3JpcHRpdm8NCnN1bW1hcnkocG9ibGFjaW9uKQ0KDQojIFRpcG9zIGRlIFZhcmlhYmxlcw0Kc3RyKHBvYmxhY2lvbikNCmBgYA0KDQojIyBTZXJpZXMgZGUgVGllbXBvIGRlIGxhIFBvYmxhY2nDs24gZW4gVGV4YXMNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpwb2JsYWNpb25fVFggPC0gcG9ibGFjaW9uICU+JQ0KICBmaWx0ZXIoc3RhdGUgPT0gIlRYIikNCg0KZ2dwbG90KHBvYmxhY2lvbl9UWCwgYWVzKHg9eWVhciwgeT1wb3B1bGF0aW9uKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGxhYnModGl0bGUgPSAgIlBvYmxhY2nDs24gVGV4YXMiLCB4ID0gIkHDsW8iLCB5ID0gIlBvYmxhY2nDs24iKQ0KYGBgDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnRzX1RYIDwtIHRzKHBvYmxhY2lvbl9UWCRwb3B1bGF0aW9uLCBzdGFydCA9IDE5MDAsIGZyZXF1ZW5jeSA9IDEpICMgU1QgQW51YWwNCg0KI0FSSU1BDQphcmltYV9UWCA8LSBhdXRvLmFyaW1hKHRzX1RYKQ0KDQpzdW1tYXJ5KGFyaW1hX1RYKQ0KDQojUHJvbm9zdGljbyBkZWwgQVJJTUENCnByb25vc3RpY29fQXJpbWFUWCA8LSBmb3JlY2FzdChhcmltYV9UWCwgbGV2ZWwgPSA5NSwgaD0xMCkNCnByaW50KHByb25vc3RpY29fQXJpbWFUWCkNCg0KI0dyYWZpY28gZGVsIFByb25vc3RpY28NCnBsb3QocHJvbm9zdGljb19BcmltYVRYLCBtYWluID0gIlBvYmxhY2lvbiBlbiBUZXhhcyAxOTkwIC0gMjAyOSAoUHJvbm9zdGljbykiKQ0KYGBgDQoNCiMgRWplcmNpY2lvIENsYXNlOiBNYXBhcw0KDQojIyBJbXBvcnRhciBMaWJyZXJpYXMgeSBQYXF1ZXRlcw0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNpbnN0YWxsLnBhY2thZ2VzKCJtYXBzIikNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShtYXBzKQ0KYGBgDQoNCiMjIEFuYWxpc2lzIERlc2NyaXB0aXZvIHkgQ3JlYXIgdW4gbWFwYQ0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgQW5hbGlzaXMgRGVzY3JpcHRpdm8NCnN1bW1hcnkocG9ibGFjaW9uKQ0KDQojIFRpcG9zIGRlIFZhcmlhYmxlcw0Kc3RyKHBvYmxhY2lvbikNCg0KI0NyZWFyIHVuIG1hcGEgZGUgRVVBDQptYXAoZGF0YWJhc2UgPSAic3RhdGUiKQ0KbWFwKGRhdGFiYXNlID0gInN0YXRlIiwgcmVnaW9ucyA9ICJUZXhhcyIsIGNvbCA9ICJyZWQiLCBmaWxsID0gVFJVRSwgYWRkID0gVFJVRSkNCm1hcChkYXRhYmFzZSA9ICJzdGF0ZSIsIHJlZ2lvbnMgPSAiTmV3IFlvcmsiLCBjb2wgPSAiYmx1ZSIsIGZpbGwgPSBUUlVFLCBhZGQgPSBUUlVFKQ0KdGl0bGUobWFpbiA9ICJQcm9ub3N0aWNvIGRlIFBvYmxhY2lvbiBkZSBFVUEiKQ0KYGBgDQoNCiMjIE1hcGFzIFBvYmxhY2lvbmFsZXMgZGUgRVVBIHBvciBkw6ljYWRhIDE5NTAgLSAyMDUwDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBEYXRhc2V0IGEgbW9kaWZpY2FyDQpwb2JsYWNpb25fZXh0ZW5kaWRhIDwtIHBvYmxhY2lvbg0KDQojIElkZW50aWZpY2Ftb3MgbG9zIGVzdGFkb3Mgw7puaWNvcw0KZXN0YWRvcyA8LSB1bmlxdWUocG9ibGFjaW9uJHN0YXRlKQ0KDQojIFBhcmEgY2FkYSBlc3RhZG8sIGVudHJlbmFtb3MgdW4gbW9kZWxvIEFSSU1BIHkgaGFjZW1vcyBmb3JlY2FzdCBoYXN0YSAyMDUwDQpmb3Ioc3QgaW4gZXN0YWRvcyl7DQogIA0KICAjIEZpbHRyYXIgZGF0b3MgZGUgZXNlIGVzdGFkbyB5IG9yZGVuYXJsb3MgcG9yIGHDsW8NCiAgZGF0b3Nfc3QgPC0gcG9ibGFjaW9uICU+JQ0KICAgIGZpbHRlcihzdGF0ZSA9PSBzdCkgJT4lDQogICAgYXJyYW5nZSh5ZWFyKQ0KICANCiAgIyBEZXRlcm1pbmFyIGVsIMO6bHRpbW8gYcOxbyBkaXNwb25pYmxlDQogIHVsdGltb19hbmlvIDwtIG1heChkYXRvc19zdCR5ZWFyKQ0KICANCiAgIyBDcmVhciBzZXJpZSBkZSB0aWVtcG8NCiAgdHNfc3QgPC0gdHMoZGF0b3Nfc3QkcG9wdWxhdGlvbiwNCiAgICAgICAgICAgICAgc3RhcnQgPSBtaW4oZGF0b3Nfc3QkeWVhciksDQogICAgICAgICAgICAgIGVuZCAgID0gdWx0aW1vX2FuaW8sDQogICAgICAgICAgICAgIGZyZXF1ZW5jeSA9IDEpICAjIEFudWFsDQogIA0KICAjIEVudHJlbmFyIGVsIG1vZGVsbyBBUklNQQ0KICBtb2RlbG9fc3QgPC0gYXV0by5hcmltYSh0c19zdCkNCiAgDQogICMgQ2FsY3VsYXIgY3XDoW50b3MgYcOxb3MgaGF5IHF1ZSBwcm9ub3N0aWNhcg0KICAjIChzb2xvIGhhY2Vtb3MgZm9yZWNhc3Qgc2kgZWwgdWx0aW1vX2FuaW8gZXMgPCAyMDUwKQ0KICBoX3llYXJzIDwtIDIwNTAgLSB1bHRpbW9fYW5pbw0KICANCiAgaWYoaF95ZWFycyA+IDApew0KICAgICMgSGFjZW1vcyBlbCBwcm9uw7NzdGljbw0KICAgIHByb25vc3RpY28gPC0gZm9yZWNhc3QobW9kZWxvX3N0LCBoID0gaF95ZWFycykNCiAgICANCiAgICAjIENyZWFtb3MgdW4gZGF0YSBmcmFtZSBjb24gbG9zIHJlc3VsdGFkb3MNCiAgICBhbmlvc19wcm9ub3N0aWNvIDwtICh1bHRpbW9fYW5pbyArIDEpOjIwNTANCiAgICBwb2JsYWNpb25fcHJvbm9zdGljYWRhIDwtIGFzLm51bWVyaWMocHJvbm9zdGljbyRtZWFuKQ0KICAgIA0KICAgIGRmX2ZvcmVjYXN0IDwtIGRhdGEuZnJhbWUoDQogICAgICBzdGF0ZSA9IHN0LA0KICAgICAgeWVhciAgPSBhbmlvc19wcm9ub3N0aWNvLA0KICAgICAgcG9wdWxhdGlvbiA9IHBvYmxhY2lvbl9wcm9ub3N0aWNhZGENCiAgICApDQogICAgDQogICAgIyBBZ3JlZ2Ftb3MgZmlsYXMgY29uIGxhIHBvYmxhY2nDs24gcHJvbm9zdGljYWRhDQogICAgcG9ibGFjaW9uX2V4dGVuZGlkYSA8LSByYmluZChwb2JsYWNpb25fZXh0ZW5kaWRhLCBkZl9mb3JlY2FzdCkNCiAgfQ0KfQ0KDQojIDMpIEZ1bmNpw7NuIHBhcmEgZ3JhZmljYXIgZWwgbWFwYSBwYXJhIHVuIGHDsW8gZGFkbyAtLS0tLS0tLS0tLS0NCnBsb3RfbWFwIDwtIGZ1bmN0aW9uKHllYXIpIHsNCiAgDQogICMgRmlsdHJhbW9zIGxvcyBkYXRvcyBwYXJhIGVsIGHDsW8gc29saWNpdGFkbw0KICBkYXRhX3llYXIgPC0gcG9ibGFjaW9uX2V4dGVuZGlkYSAlPiUNCiAgICBmaWx0ZXIoeWVhciA9PSAhIXllYXIpDQogIA0KICAjIENhcmdhciBkYXRvcyBnZW9ncsOhZmljb3MgZGUgRUUuVVUuDQogIHN0YXRlc19tYXAgPC0gbWFwX2RhdGEoInN0YXRlIikNCiAgDQogICMgTmVjZXNpdGFtb3MgcmVsYWNpb25hciBsYSBzaWdsYSAoc3RhdGUpIGNvbiBlbCBub21icmUgZW4gbWluw7pzY3VsYXMNCiAgIyBSIHRyYWUgZG9zIHZlY3RvcmVzIGF1eGlsaWFyZXM6IHN0YXRlLmFiYiAoc2lnbGFzKSB5IHN0YXRlLm5hbWUgKG5vbWJyZXMgY29tcGxldG9zKQ0KICBkYXRhX3llYXIgPC0gZGF0YV95ZWFyICU+JQ0KICAgIG11dGF0ZShyZWdpb24gPSB0b2xvd2VyKHN0YXRlLm5hbWVbbWF0Y2goc3RhdGUsIHN0YXRlLmFiYildKSkgJT4lDQogICAgcmlnaHRfam9pbihzdGF0ZXNfbWFwLCBieSA9ICJyZWdpb24iKQ0KICANCiAgIyBHcmFmaWNhcg0KICBnZ3Bsb3QoZGF0YV95ZWFyLCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXAsIGZpbGwgPSBwb3B1bGF0aW9uKSkgKw0KICAgIGdlb21fcG9seWdvbihjb2xvciA9ICJibGFjayIpICsNCiAgICAjIEdyYWRpZW50ZSB2ZXJkZSAtPiByb2pvDQogICAgc2NhbGVfZmlsbF9ncmFkaWVudCgNCiAgICAgIGxvdyA9ICJncmVlbiIsICAgIyBjb2xvciBtw61uaW1vDQogICAgICBoaWdoID0gInJlZCIsICAgICMgY29sb3IgbcOheGltbw0KICAgICAgbmFtZSA9ICJQb2JsYWNpw7NuIg0KICAgICkgKw0KICAgIGxhYnMoDQogICAgICB0aXRsZSA9IHBhc3RlKCJQb2JsYWNpw7NuIHBvciBFc3RhZG8gZW4iLCB5ZWFyKQ0KICAgICkgKw0KICAgIHRoZW1lX3ZvaWQoKSArDQogICAgdGhlbWUoDQogICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLA0KICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpDQogICAgKQ0KfQ0KDQojIDQpIEdyYWZpY2FyIGVsIG1hcGEgY2FkYSBkw6ljYWRhIGVudHJlIDE5MDAgeSAyMDUwIC0tLS0tLS0tLS0tLS0NCmZvcih5ZWFyIGluIHNlcSgxOTAwLCAyMDUwLCBieSA9IDEwKSkgew0KICBwcmludChwbG90X21hcCh5ZWFyKSkNCn0NCmBgYA0KDQoNCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBicm93bjsiPiBBY3RpdmlkYWQgMjogTGVjaGUgU2Fib3JpemFkYSBIZXJzaGV5J3MgPC9zcGFuPg0KIVtdKGNob2NvbGF0ZS5naWYpDQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogYnJvd247Ij4gSW1wb3J0YXIgbGlicmVyaWFzIHkgcGFxdWV0ZXMgPC9zcGFuPg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNpbnN0YWxsLnBhY2thZ2VzKCJyZWFkeGwiKQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHJlYWR4bCkNCmBgYA0KDQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogYnJvd247Ij4gSW1wb3J0YXIgQmFzZXMgZGUgRGF0b3MgPC9zcGFuPg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnZlbnRhcyA8LSByZWFkX2V4Y2VsKCJWZW50YXNfSGlzdMOzcmljYXNfTGVjaGl0YXMueGxzeCIpDQpgYGANCg0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiBicm93bjsiPiAxLiBNb2RlbG8gQVJJTUEgPC9zcGFuPg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnRzX3ZlbnRhcyA8LSB0cyh2ZW50YXMkVmVudGFzLCBzdGFydCA9IGMoMjAxNywxKSwgZnJlcXVlbmN5ID0gMTIpICMgU1QgTWVuc3VhbA0KDQphdXRvcGxvdCh0c192ZW50YXMpICsNCiAgbGFicyh0aXRsZSA9ICJWZW50YXMgZGUgTGVjaGUgU2Fib3JpemFkYSBIZXJzaGV5J3MiLCB4ID0gIlRpZW1wbyIsIHkgPSAiTWlsZXMgZGUgVVNEIikNCg0KI0FSSU1BDQphcmltYV92ZW50YXMgPC0gYXV0by5hcmltYSh0c192ZW50YXMpDQoNCnN1bW1hcnkoYXJpbWFfdmVudGFzKQ0KYGBgDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgR2VuZXJhciBwcm9ub3N0aWNvIGRlIGxvcyBwcm94aW1vcyAxMiBtZXNlcyAoQcOxbyAyMDIwKQ0KcHJvbm9zdGljb192ZW50YXMgPC0gZm9yZWNhc3QoYXJpbWFfdmVudGFzLCBsZXZlbCA9IDk1LCBoID0gMTIpDQpwcm9ub3N0aWNvX3ZlbnRhcw0KDQoNCiMgR3JhZmljYXIgcHJvbm9zdGljbyBBUklNQQ0KYXV0b3Bsb3QocHJvbm9zdGljb192ZW50YXMpICsNCiAgbGFicyh0aXRsZSA9ICJQcm9ub3N0aWNvIGRlIFZlbnRhcyAyMDIwIGRlIExlY2hlIFNhYm9yaXphZGEgSGVyc2hleSdzIiwNCiAgICAgICB4ID0gIlRpZW1wbyIsIHkgPSAiTWlsZXMgZGUgVVNEIikNCmBgYA0KDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJyb3duOyI+IDIuIE1vZGVsbyBSZWdyZXNpw7NuIExpbmVhbCA8L3NwYW4+DQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KdmVudGFzJG1lcyA8LSAxOjM2DQpyZWdyZXNpb25fdmVudGFzIDwtIGxtKFZlbnRhcyB+IG1lcywgZGF0YSA9IHZlbnRhcykNCg0Kc3VtbWFyeShyZWdyZXNpb25fdmVudGFzKQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpzaWd1aWVudGVfYW5pbyA8LSBkYXRhLmZyYW1lKG1lcyA9IDM3OjQ4KQ0KcHJlZGljY2lvbl9yZWdyZXNpb24gPC0gcHJlZGljdChyZWdyZXNpb25fdmVudGFzLCBzaWd1aWVudGVfYW5pbykNCnByZWRpY2Npb25fcmVncmVzaW9uDQoNCg0KcGxvdCh2ZW50YXMkbWVzLCB2ZW50YXMkVmVudGFzLCANCiAgICAgbWFpbiA9ICJQcm9ub3N0aWNvIGRlIFZlbnRhcyAyMDIwIGRlIExlY2hlIFNhYm9yaXphZGEgSGVyc2hleSdzIiwNCiAgICAgeGxpbSA9IGMoMSw1MCksDQogICAgIHlsaW0gPSByYW5nZShjKHZlbnRhcyRWZW50YXMscHJlZGljY2lvbl9yZWdyZXNpb24pKSwNCiAgICAgeGxhYiA9ICJUaWVtcG8iLA0KICAgICB5bGFiID0gIk1pbGVzIFVTRCIpICsNCmFibGluZShyZWdyZXNpb25fdmVudGFzLCBjb2wgPSAiYmx1ZSIpICsNCnBvaW50cyhzaWd1aWVudGVfYW5pbyRtZXMsIHByZWRpY2Npb25fcmVncmVzaW9uLCBjb2wgPSAicmVkIikNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcHJlZGljY2lvbmVzX3JlYWxlcyA8LSBwcmVkaWN0KHJlZ3Jlc2lvbl92ZW50YXMsIHZlbnRhcykNCk1BUEVfcmVnIDwtIG1lYW4oYWJzKCh2ZW50YXMkVmVudGFzIC0gcHJlZGljY2lvbmVzX3JlYWxlcykvIHZlbnRhcyRWZW50YXMpKSoxMDANCg0KTUFQRV9yZWcNCmBgYA0KDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJyb3duOyI+IDMuIEVsZWNjacOzbiBkZWwgTW9kZWxvIHkgQ29uY2x1c2nDs24gPC9zcGFuPg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBNQVBFIEFSSU1BDQpNQVBFX2FyaW1hIDwtIDAuNzA2OTU0Mg0KTUFQRV9hcmltYQ0KIyBNQVBFIFJFRw0KTUFQRV9yZWcNCmBgYA0KDQoNCioqQ29uY2x1c2lvbjoqKiAgDQpMYSBwcmVkaWNjacOzbiBkZWwgU0FSSU1BIGVzIG3DoXMgYWNlcnRhZGEgcXVlIGxhIHByb3llY2Npw7NuIGRlIGxhIHJlZ3Jlc2nDs24gbGluZWFsLiBBbCBjb250YXIgY29uIHVuIE1BUEUgbWVub3IgKDAuNzEpIGNvbiByZXNwZWN0byBhIGxhIFJlZ3Jlc2lvbiBMaW5lYWwgKDIuMDEpLCBzZSBlbGlnZSBlbCBTQVJJTUEgY29tbyBlbCAqKm1lam9yIG1vZGVsbyoqLiAgDQoNClBvciBsbyBxdWUgc2UgcHVlZGUgZXNwZXJhciBxdWUgbGFzIHZlbnRhcyBkZSBIZXJzaGV5cyBpbmNyZW1lbnRlbiBlbiBlbCAyMDIwIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmEuDQoNCioqRXNjZW5hcmlvczoqKiAgDQoNCnwgTWVzIHkgQcOxbyB8ICBSZWFsaXN0YSB8IFBlc2ltaXN0YSB8IE9wdGltaXN0YSB8DQp8LS0tLS0tLS0tLS18LS0tLS0tLS0tLS18LS0tLS0tLS0tLS18LS0tLS0tLS0tLS18DQp8IEphbiAyMDIwICB8CSQzNTQ5OC45MCB8CSQzNDYxNi40OCB8CSQzNjM4MS4zMiB8CQ0KfCBGZWIgMjAyMCAgfAkkMzQyMDIuMTcgfAkkMzMxNTUuMjggfAkkMzUyNDkuMDUgfAkNCnwgTWFyIDIwMjAgIHwJJDM2NzAzLjAxIHwJJDM1NTk2LjEwIHwJJDM3ODA5LjkyIHwJDQp8IEFwciAyMDIwICB8CSQzNjI3MS45MCB8CSQzNTE0MS40NCB8CSQzNzQwMi4zNiB8DQp8IE1heSAyMDIwICB8CSQzNzEyMS45OCB8CSQzNTk4Mi4wNyB8CSQzODI2MS45MCB8DQp8IEp1biAyMDIwICB8CSQzNzEwMi42NSB8CSQzNTk1OC45MCB8CSQzODI0Ni40MCB8CQ0KfCBKdWwgMjAyMCAgfAkkMzcxNTEuMDQgfAkkMzYwMDUuNzMgfAkkMzgyOTYuMzQgfAkNCnwgQXVnIDIwMjAgIHwJJDM4NTY0LjY0IHwJJDM3NDE4LjcwIHwJJDM5NzEwLjU4IHwJDQp8IFNlcCAyMDIwICB8CSQzODc1NS4yMiB8CSQzNzYwOS4wMyB8CSQzOTkwMS40MiB8CQ0KfCBPY3QgMjAyMCAgfAkkMzk3NzkuMDIgfAkkMzg2MzIuNzIgfAkkNDA5MjUuMzIgfAkNCnwgTm92IDIwMjAgIHwJJDM4NzQxLjYzIHwJJDM3NTk1LjI4IHwJJDM5ODg3Ljk3IHwJDQp8IERlYyAyMDIwICB8CSQzODY0NS44NiB8CSQzNzQ5OS41MCB8CSQzOTc5Mi4yMiB8CQ0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KdmVudGFzX2FuaW8gPC0gcmVhZC5jc3YoInZlbnRhc19wb3JfYW5pby5jc3YiKQ0KDQpnZ3Bsb3QodmVudGFzX2FuaW8sIGFlcyh4PW1lcywgeT12ZW50YXMsIGNvbD1hcy5mYWN0b3IoYW5pbyksIGdyb3VwPWFuaW8pKSArIA0KICBnZW9tX2xpbmUoKSArDQogIGxhYnModGl0bGUgPSAiVmVudGFzIGRlIGxlY2hlIHNhYm9yaXphZGEgSGVyc2hlecK0cyBwb3IgYcOxbyIsIHggPSAiTWVzIiwgeSA9ICJNaWxlcyBVU0QiKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOjEyKQ0KYGBgDQoNCioqUmVjb21lbmRhY2lvbjoqKiAgDQotIEF1bWVudGFyIGVsIGNvbnN1bW8gZGUgbGVjaGUgc2Fib3JpemFkYSBIZXJzaGV5J3MgZW4gZWwgcHJpbWVyIHNlbWVzdHJlIGRlbCBhw7FvIG1lZGlhbnRlIGNhbXBhw7FhcyBwdWJsaWNpdGFyaWFzLg0K