Ejemplo en clase: Población (17/02/2025)

1. Instalar paquetes y llamar librerias

library(forecast)
library(tidyverse)
library(ggplot2)
library(readxl)

2. Importar la base de datos

poblacion <- read.csv("/Users/hectordelagarzatrevino/Library/CloudStorage/GoogleDrive-a01177960@tec.mx/My Drive/LIT/8. Octavo semestre/Generación de escenarios futuros con analítica/Modulo 1/Actividades/Actividad 2/population.csv")

3. Entendiendo la base de datos

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
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 ...
head(poblacion)
##   state year population
## 1    AK 1950     135000
## 2    AK 1951     158000
## 3    AK 1952     189000
## 4    AK 1953     205000
## 5    AK 1954     215000
## 6    AK 1955     222000

4. Serie de tiempo en Texas

poblacion_texas <- poblacion %>% filter(state=="TX")
ggplot(poblacion_texas, aes(x= year, y= population)) + 
  geom_line() +
  labs(title = "Poblacion de Texas", x = "Año",
       y = "Poblacion")

ts_texas <- ts(poblacion_texas$population, start = 1900, frequency = 1) # Serie de tiempo anual
# ts_texas <- ts(poblacion_texas$population, start = c(1900,4), frequency = 4) Serie de tiempo trimestral
# ts_texas <- ts(poblacion_texas$population, start = c(1900,8), frequency = 12) Serie de tiempo mensual
arima_texas <- auto.arima(ts_texas)
summary(arima_texas)
## Series: ts_texas 
## 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_texas <- forecast(arima_texas, level = 95, h = 10) # h es el numero de predicciones
plot(pronostico_texas, main = "Pronostico de población de Texas")

Ejercicio en clase (17/02/2025): Mapa

1.1 Instalar paquetes y llamar librerias

library(forecast)
library(tidyverse)
library(ggplot2)
library(maps)
library(palette)
library(leaflet)

2.1Entendiendo la base de datos

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
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 ...
head(poblacion)
##   state year population
## 1    AK 1950     135000
## 2    AK 1951     158000
## 3    AK 1952     189000
## 4    AK 1953     205000
## 5    AK 1954     215000
## 6    AK 1955     222000

3.1 Serie de tiempo en Texas

poblacion_texas <- poblacion %>% filter(state=="TX")
ggplot(poblacion_texas, aes(x= year, y= population)) + 
  geom_line() +
  labs(title = "Poblacion de Texas", x = "Año",
       y = "Poblacion")

ts_texas <- ts(poblacion_texas$population, start = 1900, frequency = 1) # Serie de tiempo anual
# ts_texas <- ts(poblacion_texas$population, start = c(1900,4), frequency = 4) Serie de tiempo trimestral
# ts_texas <- ts(poblacion_texas$population, start = c(1900,8), frequency = 12) Serie de tiempo mensual
arima_texas <- auto.arima(ts_texas)
summary(arima_texas)
## Series: ts_texas 
## 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_texas <- forecast(arima_texas, level = 95, h = 10) # h es el numero de predicciones
plot(pronostico_texas, main = "Pronostico de población de Texas")

4.1 Crear un mapa

# Crear un mapa de EUA por decada, con un gradiente verde-rojo de la poblacion por estado, desde 1900 hasta 2050
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)

5.1 Actividad de Mapa

# Generar pronósticos con ARIMA para cada estado
proyecciones <- poblacion %>%
  group_by(state) %>%
  summarise(
    modelo = list(auto.arima(ts(population, start = 1950, frequency = 1))),  # Modelo ARIMA
    .groups = "drop"
  ) %>%
  rowwise() %>%
  mutate(
    pronostico = list(forecast(modelo, h = 31)),  # Pronóstico de 5 años
    poblacion5 = tail(pronostico$mean, 1)  # Obtener la población del último año pronosticado
  ) %>%
  select(state, poblacion5)
# Unir proyecciones con el mapa
states <- map("state", plot = FALSE, fill = TRUE)
map_data <- merge(data.frame(state = tolower(state.name)), proyecciones, by = "state", all.x = TRUE)


# Asignar colores según la población (gradiente verde-rojo)
color_pal <- colorNumeric(palette = "RdYlGn", domain = proyecciones$poblacion5)

# Crear mapa interactivo con Leaflet
leaflet(data = states) %>%
  addTiles() %>%
  addPolygons(
    fillColor = ~color_pal(proyecciones$poblacion5),
    fillOpacity = 0.7,
    color = "black",
    weight = 1,
    popup = ~paste("Estado:", proyecciones$state, "<br>",
                   "Población en 5 años:", format(proyecciones$poblacion5, big.mark = ","))
  ) %>%
  addLegend(
    "bottomright",
    pal = color_pal,
    values = proyecciones$poblacion5,
    title = "Población pronosticada en 5 Años",
    opacity = 1
  )

Actividad 2. Leche saborizada Hershey´s

1.2 Instalar paquetes y llamar librerias

library(forecast)
library(tidyverse)
library(ggplot2)
library(readxl)

2.2 Importar la base de datos

ventas <- read_excel("/Users/hectordelagarzatrevino/Library/CloudStorage/GoogleDrive-a01177960@tec.mx/My Drive/LIT/8. Octavo semestre/Generación de escenarios futuros con analítica/Modulo 1/Actividades/Actividad 2/Ventas_Históricas_lechitas.xlsx")

3.2 Modelo AUTO.ARIMA

ts_ventas <- ts(ventas$Ventas, start = c(2017,1), frequency = 12)
autoplot(ts_ventas) + labs(title = "Ventas de leche saborizada Hersheys", x = "Tiempo", y = "Miles de dolares")

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
pronostico_ventas <- forecast(arima_ventas, level = 95, h = 12) 
autoplot(pronostico_ventas, main = "Pronostico de ventas de leche saborizada Hersheys")

4.2 Modelo Regresion 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 de Hersheys", xlab = "Tiempo", ylab = "Miles de dolares")
abline(regresion_ventas, col = "blue")
points(siguiente_anio$mes, prediccion_regresion, col = "red")

predicciones_reales <- predict(regresion_ventas, ventas)
MAPE <- mean(abs((ventas$Ventas - predicciones_reales)/ventas$Ventas))*100
MAPE
## [1] 2.011297

5.2 Conclusiones

El mejor modelo que se adapta a la serie es el SARIMA con un Mape de 0.71%, comparado con la Regresion Lineal que su MAPE es de 2.01%

Para el siguiente año, la proyección de ventas es la siguiente:

Mes y Año Escenario Optimista Escenario Esperado Escenario Pesimista
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
ventasporanio <- read_excel("/Users/hectordelagarzatrevino/Library/CloudStorage/GoogleDrive-a01177960@tec.mx/My Drive/LIT/8. Octavo semestre/Generación de escenarios futuros con analítica/Modulo 1/Actividades/Actividad 2/Ventas2.xlsx")
ggplot(ventasporanio, 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 de Dólares")

Nuestra recomendación sería realizar campañas publicitarias para aumentar el consumo de leche saborizada Hershey’s en el primer semestre del año.

LS0tCnRpdGxlOiAiQWN0aXZpZGFkIDI6IEdlbmVyYWNpw7NuIGRlIGVzY2VuYXJpb3MgZnV0dXJvcyBjb24gbW9kZWxvcyBkZSBwcm9uw7NzdGljb3MgZW4gc2VyaWVzIGRlIHRpZW1wby4iCmF1dGhvcjogIkjDqWN0b3IgR3VhZGFsdXBlIGRlIGxhIEdhcnphIFRyZXZpw7FvIC0gQTAxMTc3OTYwIgpkYXRlOiAiMjAyNS0wMi0xNyIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUKICAgIHRoZW1lOiBjZXJ1bGVhbgotLS0KCiMgKipFamVtcGxvIGVuIGNsYXNlOiBQb2JsYWNpw7NuICgxNy8wMi8yMDI1KSoqCgohW10oL1VzZXJzL2hlY3RvcmRlbGFnYXJ6YXRyZXZpbm8vTGlicmFyeS9DbG91ZFN0b3JhZ2UvR29vZ2xlRHJpdmUtYTAxMTc3OTYwQHRlYy5teC9NeSBEcml2ZS9MSVQvOC4gT2N0YXZvIHNlbWVzdHJlL0dlbmVyYWNpb8yBbiBkZSBlc2NlbmFyaW9zIGZ1dHVyb3MgY29uIGFuYWxpzIF0aWNhL01vZHVsbyAxL0FjdGl2aWRhZGVzL0FjdGl2aWRhZCAyL3BvYmxhY2lvbi5qcGcpCgojIyAqKjEuIEluc3RhbGFyIHBhcXVldGVzIHkgbGxhbWFyIGxpYnJlcmlhcyoqCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZm9yZWNhc3QpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocmVhZHhsKQpgYGAKCiMjICoqMi4gSW1wb3J0YXIgbGEgYmFzZSBkZSBkYXRvcyoqCmBgYHtyfQpwb2JsYWNpb24gPC0gcmVhZC5jc3YoIi9Vc2Vycy9oZWN0b3JkZWxhZ2FyemF0cmV2aW5vL0xpYnJhcnkvQ2xvdWRTdG9yYWdlL0dvb2dsZURyaXZlLWEwMTE3Nzk2MEB0ZWMubXgvTXkgRHJpdmUvTElULzguIE9jdGF2byBzZW1lc3RyZS9HZW5lcmFjaW/MgW4gZGUgZXNjZW5hcmlvcyBmdXR1cm9zIGNvbiBhbmFsacyBdGljYS9Nb2R1bG8gMS9BY3RpdmlkYWRlcy9BY3RpdmlkYWQgMi9wb3B1bGF0aW9uLmNzdiIpCmBgYAoKIyMgKiozLiBFbnRlbmRpZW5kbyBsYSBiYXNlIGRlIGRhdG9zKioKYGBge3J9CnN1bW1hcnkocG9ibGFjaW9uKQpzdHIocG9ibGFjaW9uKQpoZWFkKHBvYmxhY2lvbikKYGBgCgojIyAqKjQuIFNlcmllIGRlIHRpZW1wbyBlbiBUZXhhcyoqCmBgYHtyfQpwb2JsYWNpb25fdGV4YXMgPC0gcG9ibGFjaW9uICU+JSBmaWx0ZXIoc3RhdGU9PSJUWCIpCmdncGxvdChwb2JsYWNpb25fdGV4YXMsIGFlcyh4PSB5ZWFyLCB5PSBwb3B1bGF0aW9uKSkgKyAKICBnZW9tX2xpbmUoKSArCiAgbGFicyh0aXRsZSA9ICJQb2JsYWNpb24gZGUgVGV4YXMiLCB4ID0gIkHDsW8iLAogICAgICAgeSA9ICJQb2JsYWNpb24iKQp0c190ZXhhcyA8LSB0cyhwb2JsYWNpb25fdGV4YXMkcG9wdWxhdGlvbiwgc3RhcnQgPSAxOTAwLCBmcmVxdWVuY3kgPSAxKSAjIFNlcmllIGRlIHRpZW1wbyBhbnVhbAojIHRzX3RleGFzIDwtIHRzKHBvYmxhY2lvbl90ZXhhcyRwb3B1bGF0aW9uLCBzdGFydCA9IGMoMTkwMCw0KSwgZnJlcXVlbmN5ID0gNCkgU2VyaWUgZGUgdGllbXBvIHRyaW1lc3RyYWwKIyB0c190ZXhhcyA8LSB0cyhwb2JsYWNpb25fdGV4YXMkcG9wdWxhdGlvbiwgc3RhcnQgPSBjKDE5MDAsOCksIGZyZXF1ZW5jeSA9IDEyKSBTZXJpZSBkZSB0aWVtcG8gbWVuc3VhbAphcmltYV90ZXhhcyA8LSBhdXRvLmFyaW1hKHRzX3RleGFzKQpzdW1tYXJ5KGFyaW1hX3RleGFzKQpwcm9ub3N0aWNvX3RleGFzIDwtIGZvcmVjYXN0KGFyaW1hX3RleGFzLCBsZXZlbCA9IDk1LCBoID0gMTApICMgaCBlcyBlbCBudW1lcm8gZGUgcHJlZGljY2lvbmVzCnBsb3QocHJvbm9zdGljb190ZXhhcywgbWFpbiA9ICJQcm9ub3N0aWNvIGRlIHBvYmxhY2nDs24gZGUgVGV4YXMiKQpgYGAKCiMgKipFamVyY2ljaW8gZW4gY2xhc2UgKDE3LzAyLzIwMjUpOiBNYXBhKioKCiMjICoqMS4xIEluc3RhbGFyIHBhcXVldGVzIHkgbGxhbWFyIGxpYnJlcmlhcyoqCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZm9yZWNhc3QpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkobWFwcykKbGlicmFyeShwYWxldHRlKQpsaWJyYXJ5KGxlYWZsZXQpCmBgYAoKIyMgKioyLjFFbnRlbmRpZW5kbyBsYSBiYXNlIGRlIGRhdG9zKioKYGBge3J9CnN1bW1hcnkocG9ibGFjaW9uKQpzdHIocG9ibGFjaW9uKQpoZWFkKHBvYmxhY2lvbikKYGBgCgojIyAqKjMuMSBTZXJpZSBkZSB0aWVtcG8gZW4gVGV4YXMqKgpgYGB7cn0KcG9ibGFjaW9uX3RleGFzIDwtIHBvYmxhY2lvbiAlPiUgZmlsdGVyKHN0YXRlPT0iVFgiKQpnZ3Bsb3QocG9ibGFjaW9uX3RleGFzLCBhZXMoeD0geWVhciwgeT0gcG9wdWxhdGlvbikpICsgCiAgZ2VvbV9saW5lKCkgKwogIGxhYnModGl0bGUgPSAiUG9ibGFjaW9uIGRlIFRleGFzIiwgeCA9ICJBw7FvIiwKICAgICAgIHkgPSAiUG9ibGFjaW9uIikKdHNfdGV4YXMgPC0gdHMocG9ibGFjaW9uX3RleGFzJHBvcHVsYXRpb24sIHN0YXJ0ID0gMTkwMCwgZnJlcXVlbmN5ID0gMSkgIyBTZXJpZSBkZSB0aWVtcG8gYW51YWwKIyB0c190ZXhhcyA8LSB0cyhwb2JsYWNpb25fdGV4YXMkcG9wdWxhdGlvbiwgc3RhcnQgPSBjKDE5MDAsNCksIGZyZXF1ZW5jeSA9IDQpIFNlcmllIGRlIHRpZW1wbyB0cmltZXN0cmFsCiMgdHNfdGV4YXMgPC0gdHMocG9ibGFjaW9uX3RleGFzJHBvcHVsYXRpb24sIHN0YXJ0ID0gYygxOTAwLDgpLCBmcmVxdWVuY3kgPSAxMikgU2VyaWUgZGUgdGllbXBvIG1lbnN1YWwKYXJpbWFfdGV4YXMgPC0gYXV0by5hcmltYSh0c190ZXhhcykKc3VtbWFyeShhcmltYV90ZXhhcykKcHJvbm9zdGljb190ZXhhcyA8LSBmb3JlY2FzdChhcmltYV90ZXhhcywgbGV2ZWwgPSA5NSwgaCA9IDEwKSAjIGggZXMgZWwgbnVtZXJvIGRlIHByZWRpY2Npb25lcwpwbG90KHByb25vc3RpY29fdGV4YXMsIG1haW4gPSAiUHJvbm9zdGljbyBkZSBwb2JsYWNpw7NuIGRlIFRleGFzIikKYGBgCgojIyAqKjQuMSBDcmVhciB1biBtYXBhKioKYGBge3J9CiMgQ3JlYXIgdW4gbWFwYSBkZSBFVUEgcG9yIGRlY2FkYSwgY29uIHVuIGdyYWRpZW50ZSB2ZXJkZS1yb2pvIGRlIGxhIHBvYmxhY2lvbiBwb3IgZXN0YWRvLCBkZXNkZSAxOTAwIGhhc3RhIDIwNTAKbWFwKGRhdGFiYXNlID0gInN0YXRlIikKbWFwKGRhdGFiYXNlID0gInN0YXRlIiwgcmVnaW9ucyA9ICJUZXhhcyIsIGNvbCA9ICJyZWQiLCBmaWxsID0gVFJVRSwgYWRkID0gVFJVRSkKbWFwKGRhdGFiYXNlID0gInN0YXRlIiwgcmVnaW9ucyA9ICJOZXcgWW9yayIsIGNvbCA9ICJibHVlIiwgZmlsbCA9IFRSVUUsIGFkZCA9IFRSVUUpCmBgYAoKIyMgKio1LjEgQWN0aXZpZGFkIGRlIE1hcGEqKgoKYGBge3J9CiMgR2VuZXJhciBwcm9uw7NzdGljb3MgY29uIEFSSU1BIHBhcmEgY2FkYSBlc3RhZG8KcHJveWVjY2lvbmVzIDwtIHBvYmxhY2lvbiAlPiUKICBncm91cF9ieShzdGF0ZSkgJT4lCiAgc3VtbWFyaXNlKAogICAgbW9kZWxvID0gbGlzdChhdXRvLmFyaW1hKHRzKHBvcHVsYXRpb24sIHN0YXJ0ID0gMTk1MCwgZnJlcXVlbmN5ID0gMSkpKSwgICMgTW9kZWxvIEFSSU1BCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKSAlPiUKICByb3d3aXNlKCkgJT4lCiAgbXV0YXRlKAogICAgcHJvbm9zdGljbyA9IGxpc3QoZm9yZWNhc3QobW9kZWxvLCBoID0gMzEpKSwgICMgUHJvbsOzc3RpY28gZGUgNSBhw7FvcwogICAgcG9ibGFjaW9uNSA9IHRhaWwocHJvbm9zdGljbyRtZWFuLCAxKSAgIyBPYnRlbmVyIGxhIHBvYmxhY2nDs24gZGVsIMO6bHRpbW8gYcOxbyBwcm9ub3N0aWNhZG8KICApICU+JQogIHNlbGVjdChzdGF0ZSwgcG9ibGFjaW9uNSkKYGBgCgpgYGB7cn0KIyBVbmlyIHByb3llY2Npb25lcyBjb24gZWwgbWFwYQpzdGF0ZXMgPC0gbWFwKCJzdGF0ZSIsIHBsb3QgPSBGQUxTRSwgZmlsbCA9IFRSVUUpCm1hcF9kYXRhIDwtIG1lcmdlKGRhdGEuZnJhbWUoc3RhdGUgPSB0b2xvd2VyKHN0YXRlLm5hbWUpKSwgcHJveWVjY2lvbmVzLCBieSA9ICJzdGF0ZSIsIGFsbC54ID0gVFJVRSkKCgojIEFzaWduYXIgY29sb3JlcyBzZWfDum4gbGEgcG9ibGFjacOzbiAoZ3JhZGllbnRlIHZlcmRlLXJvam8pCmNvbG9yX3BhbCA8LSBjb2xvck51bWVyaWMocGFsZXR0ZSA9ICJSZFlsR24iLCBkb21haW4gPSBwcm95ZWNjaW9uZXMkcG9ibGFjaW9uNSkKCiMgQ3JlYXIgbWFwYSBpbnRlcmFjdGl2byBjb24gTGVhZmxldApsZWFmbGV0KGRhdGEgPSBzdGF0ZXMpICU+JQogIGFkZFRpbGVzKCkgJT4lCiAgYWRkUG9seWdvbnMoCiAgICBmaWxsQ29sb3IgPSB+Y29sb3JfcGFsKHByb3llY2Npb25lcyRwb2JsYWNpb241KSwKICAgIGZpbGxPcGFjaXR5ID0gMC43LAogICAgY29sb3IgPSAiYmxhY2siLAogICAgd2VpZ2h0ID0gMSwKICAgIHBvcHVwID0gfnBhc3RlKCJFc3RhZG86IiwgcHJveWVjY2lvbmVzJHN0YXRlLCAiPGJyPiIsCiAgICAgICAgICAgICAgICAgICAiUG9ibGFjacOzbiBlbiA1IGHDsW9zOiIsIGZvcm1hdChwcm95ZWNjaW9uZXMkcG9ibGFjaW9uNSwgYmlnLm1hcmsgPSAiLCIpKQogICkgJT4lCiAgYWRkTGVnZW5kKAogICAgImJvdHRvbXJpZ2h0IiwKICAgIHBhbCA9IGNvbG9yX3BhbCwKICAgIHZhbHVlcyA9IHByb3llY2Npb25lcyRwb2JsYWNpb241LAogICAgdGl0bGUgPSAiUG9ibGFjacOzbiBwcm9ub3N0aWNhZGEgZW4gNSBBw7FvcyIsCiAgICBvcGFjaXR5ID0gMQogICkKYGBgCgoKIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjogIzgwNDAwMDsiPioqQWN0aXZpZGFkIDIuIExlY2hlIHNhYm9yaXphZGEgSGVyc2hlecK0cyoqPC9hcGFuPgoKIVtdKC9Vc2Vycy9oZWN0b3JkZWxhZ2FyemF0cmV2aW5vL0xpYnJhcnkvQ2xvdWRTdG9yYWdlL0dvb2dsZURyaXZlLWEwMTE3Nzk2MEB0ZWMubXgvTXkgRHJpdmUvTElULzguIE9jdGF2byBzZW1lc3RyZS9HZW5lcmFjaW/MgW4gZGUgZXNjZW5hcmlvcyBmdXR1cm9zIGNvbiBhbmFsacyBdGljYS9Nb2R1bG8gMS9BY3RpdmlkYWRlcy9BY3RpdmlkYWQgMi9oZXJzaGV5LnBuZykKCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiAjODA0MDAwOyI+KioxLjIgSW5zdGFsYXIgcGFxdWV0ZXMgeSBsbGFtYXIgbGlicmVyaWFzKio8L2FwYW4+CmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZm9yZWNhc3QpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocmVhZHhsKQpgYGAKCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiAjODA0MDAwOyI+KioyLjIgSW1wb3J0YXIgbGEgYmFzZSBkZSBkYXRvcyoqPC9hcGFuPgpgYGB7cn0KdmVudGFzIDwtIHJlYWRfZXhjZWwoIi9Vc2Vycy9oZWN0b3JkZWxhZ2FyemF0cmV2aW5vL0xpYnJhcnkvQ2xvdWRTdG9yYWdlL0dvb2dsZURyaXZlLWEwMTE3Nzk2MEB0ZWMubXgvTXkgRHJpdmUvTElULzguIE9jdGF2byBzZW1lc3RyZS9HZW5lcmFjaW/MgW4gZGUgZXNjZW5hcmlvcyBmdXR1cm9zIGNvbiBhbmFsacyBdGljYS9Nb2R1bG8gMS9BY3RpdmlkYWRlcy9BY3RpdmlkYWQgMi9WZW50YXNfSGlzdMOzcmljYXNfbGVjaGl0YXMueGxzeCIpCmBgYAoKIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6ICM4MDQwMDA7Ij4qKjMuMiBNb2RlbG8gQVVUTy5BUklNQSoqPC9hcGFuPgpgYGB7cn0KdHNfdmVudGFzIDwtIHRzKHZlbnRhcyRWZW50YXMsIHN0YXJ0ID0gYygyMDE3LDEpLCBmcmVxdWVuY3kgPSAxMikKYXV0b3Bsb3QodHNfdmVudGFzKSArIGxhYnModGl0bGUgPSAiVmVudGFzIGRlIGxlY2hlIHNhYm9yaXphZGEgSGVyc2hleXMiLCB4ID0gIlRpZW1wbyIsIHkgPSAiTWlsZXMgZGUgZG9sYXJlcyIpCmFyaW1hX3ZlbnRhcyA8LSBhdXRvLmFyaW1hKHRzX3ZlbnRhcykKc3VtbWFyeShhcmltYV92ZW50YXMpCnByb25vc3RpY29fdmVudGFzIDwtIGZvcmVjYXN0KGFyaW1hX3ZlbnRhcywgbGV2ZWwgPSA5NSwgaCA9IDEyKSAKYXV0b3Bsb3QocHJvbm9zdGljb192ZW50YXMsIG1haW4gPSAiUHJvbm9zdGljbyBkZSB2ZW50YXMgZGUgbGVjaGUgc2Fib3JpemFkYSBIZXJzaGV5cyIpCmBgYAoKIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6ICM4MDQwMDA7Ij4qKjQuMiBNb2RlbG8gUmVncmVzaW9uIExpbmVhbCoqPC9hcGFuPgpgYGB7cn0KdmVudGFzJG1lcyA8LSAxOjM2CnJlZ3Jlc2lvbl92ZW50YXMgPC0gbG0oVmVudGFzIH4gbWVzLCBkYXRhID0gdmVudGFzKQpzdW1tYXJ5KHJlZ3Jlc2lvbl92ZW50YXMpCnNpZ3VpZW50ZV9hbmlvIDwtIGRhdGEuZnJhbWUobWVzPTM3OjQ4KQpwcmVkaWNjaW9uX3JlZ3Jlc2lvbiA8LSBwcmVkaWN0KHJlZ3Jlc2lvbl92ZW50YXMsIHNpZ3VpZW50ZV9hbmlvKQpwcmVkaWNjaW9uX3JlZ3Jlc2lvbgpwbG90KHZlbnRhcyRtZXMsIHZlbnRhcyRWZW50YXMsIG1haW4gPSAiUHJvbm9zdGljbyBkZSB2ZW50YXMgZGUgSGVyc2hleXMiLCB4bGFiID0gIlRpZW1wbyIsIHlsYWIgPSAiTWlsZXMgZGUgZG9sYXJlcyIpCmFibGluZShyZWdyZXNpb25fdmVudGFzLCBjb2wgPSAiYmx1ZSIpCnBvaW50cyhzaWd1aWVudGVfYW5pbyRtZXMsIHByZWRpY2Npb25fcmVncmVzaW9uLCBjb2wgPSAicmVkIikKcHJlZGljY2lvbmVzX3JlYWxlcyA8LSBwcmVkaWN0KHJlZ3Jlc2lvbl92ZW50YXMsIHZlbnRhcykKTUFQRSA8LSBtZWFuKGFicygodmVudGFzJFZlbnRhcyAtIHByZWRpY2Npb25lc19yZWFsZXMpL3ZlbnRhcyRWZW50YXMpKSoxMDAKTUFQRQpgYGAKCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOiAjODA0MDAwOyI+Kio1LjIgQ29uY2x1c2lvbmVzKio8L2FwYW4+CkVsIG1lam9yIG1vZGVsbyBxdWUgc2UgYWRhcHRhIGEgbGEgc2VyaWUgZXMgZWwgKipTQVJJTUEqKiBjb24gdW4gTWFwZSBkZSAwLjcxJSwgY29tcGFyYWRvIGNvbiBsYSBSZWdyZXNpb24gTGluZWFsIHF1ZSBzdSBNQVBFIGVzIGRlIDIuMDElCgoKUGFyYSBlbCBzaWd1aWVudGUgYcOxbywgbGEgcHJveWVjY2nDs24gZGUgdmVudGFzIGVzIGxhIHNpZ3VpZW50ZToKCnwgTWVzIHkgQcOxbyB8IEVzY2VuYXJpbyBPcHRpbWlzdGEgfCBFc2NlbmFyaW8gRXNwZXJhZG8gfCBFc2NlbmFyaW8gUGVzaW1pc3RhIHwKfC0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS18CnxKYW4gMjAyMAkgIHwgICAgICAgMzU0OTguOTAgICAgICB8CSAgICAgIDM0NjE2LjQ4ICAgICB8CSAgICAzNjM4MS4zMgkgICAgIHwKfEZlYiAyMDIwCSAgfCAgICAgICAzNDIwMi4xNyAgICAgIHwgCSAgICAzMzE1NS4yOCAgICAgfAkgICAgMzUyNDkuMDUgICAgICAgfAp8TWFyIDIwMjAJICB8ICAgICAgIDM2NzAzLjAxICAgICAgfCAgICAgCTM1NTk2LjEwICAgICB8CSAgICAzNzgwOS45MiAgICAJIHwKfEFwciAyMDIwCSAgfCAgICAgICAzNjI3MS45MCAgICAgIHwgICAgIAkzNTE0MS40NCAgICAgfAkgICAgMzc0MDIuMzYgICAgCSB8CnxNYXkgMjAyMAkgIHwgICAgICAgMzcxMjEuOTggICAgICB8ICAgICAgCTM1OTgyLjA3ICAgICB8CSAgICAzODI2MS45MCAgICAJIHwKfEp1biAyMDIwCSAgfCAgICAgICAzNzEwMi42NSAgICAgIHwgICAgIAkzNTk1OC45MCAgICAgfAkgICAgMzgyNDYuNDAgICAgCSB8CnxKdWwgMjAyMAkgIHwgICAgICAgMzcxNTEuMDQgICAgICB8ICAgICAJMzYwMDUuNzMgICAgIHwJICAgIDM4Mjk2LjM0CSAgICAgfAp8QXVnIDIwMjAJICB8ICAgICAgIDM4NTY0LjY0ICAgICAgfCAgICAgCTM3NDE4LjcwICAgICB8CSAgICAzOTcxMC41OCAgICAJIHwKfFNlcCAyMDIwCSAgfCAgICAgICAzODc1NS4yMiAgICAgIHwgICAgIAkzNzYwOS4wMyAgICAgfAkgICAgMzk5MDEuNDIgICAgCSB8CnxPY3QgMjAyMAkgIHwgICAgICAgMzk3NzkuMDIgICAgICB8ICAgICAJMzg2MzIuNzIgICAgIHwJICAgIDQwOTI1LjMyICAgICAgIHwKfE5vdiAyMDIwICAgfCAgIAkgIDM4NzQxLjYzCSAgICB8ICAgICAgIDM3NTk1LjI4ICAgICB8CSAgICAzOTg4Ny45NyAgICAgICB8CQp8RGVjIDIwMjAgICB8ICAgICAgIDM4NjQ1Ljg2CSAgICB8ICAgICAgIDM3NDk5LjUwICAgICB8CSAgICAzOTc5Mi4yMiAgICAgICB8CgpgYGB7cn0KdmVudGFzcG9yYW5pbyA8LSByZWFkX2V4Y2VsKCIvVXNlcnMvaGVjdG9yZGVsYWdhcnphdHJldmluby9MaWJyYXJ5L0Nsb3VkU3RvcmFnZS9Hb29nbGVEcml2ZS1hMDExNzc5NjBAdGVjLm14L015IERyaXZlL0xJVC84LiBPY3Rhdm8gc2VtZXN0cmUvR2VuZXJhY2lvzIFuIGRlIGVzY2VuYXJpb3MgZnV0dXJvcyBjb24gYW5hbGnMgXRpY2EvTW9kdWxvIDEvQWN0aXZpZGFkZXMvQWN0aXZpZGFkIDIvVmVudGFzMi54bHN4IikKZ2dwbG90KHZlbnRhc3BvcmFuaW8sIGFlcyh4ID0gTWVzLHk9IFZlbnRhcywgY29sPWFzLmZhY3RvcihBbmlvKSwgZ3JvdXA9QW5pbykpKwogIGdlb21fbGluZSgpICsgbGFicyh0aXRsZSA9ICJWZW50YXMgZGUgTGVjaGUgU2Fib3JpemFkYSBIZXJzaGV5J3MgcG9yIEHDsW8iLCB4PSJNZXMiLCB5PSAiTWlsZXMgZGUgRMOzbGFyZXMiKQpgYGAKCgpOdWVzdHJhIHJlY29tZW5kYWNpw7NuIHNlcsOtYSByZWFsaXphciBjYW1wYcOxYXMgcHVibGljaXRhcmlhcyBwYXJhIGF1bWVudGFyIGVsIGNvbnN1bW8gZGUgbGVjaGUgc2Fib3JpemFkYSBIZXJzaGV5J3MgZW4gZWwgcHJpbWVyIHNlbWVzdHJlIGRlbCBhw7FvLg==