Instalar paquetes y llamar librerias

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

Ejercicio 1 - Ventas semanales

INSTRUCCIONES: Modela la siguiente serie de tiempo y elige la mejor opción. Pronostica las siguientes 6 semanas.

semana <- c(1:12)
valor <- c(17,21,19,23,18,16,20,18,22,20,15,22)
df1 <- data.frame(semana, valor)

ts1 <- ts(valor, c(2025,1), frequency = 52)


# Modelo 1. Naive
naive1 <- naive(ts1, h=6)
summary(naive1)
## 
## Forecast method: Naive method
## 
## Model Information:
## Call: naive(y = ts1, h = 6) 
## 
## Residual sd: 4.0339 
## 
## Error measures:
##                     ME     RMSE      MAE       MPE     MAPE MASE       ACF1
## Training set 0.4545455 4.033947 3.727273 0.1082169 19.24431  NaN -0.4553872
## 
## Forecasts:
##          Point Forecast     Lo 80    Hi 80     Lo 95    Hi 95
## 2025.231             22 16.830289 27.16971 14.093609 29.90639
## 2025.250             22 14.688925 29.31108 10.818675 33.18132
## 2025.269             22 13.045798 30.95420  8.305730 35.69427
## 2025.288             22 11.660578 32.33942  6.187219 37.81278
## 2025.308             22 10.440175 33.55983  4.320773 39.67923
## 2025.327             22  9.336846 34.66315  2.633377 41.36662
# Modelo 2. Promedios móviles
ma1 <- ma(ts1, order = 3)
ma1_fc <- rep(tail(na.omit(ma1), 1), 6)
summary(ma1)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##    18.0    19.0    19.0    19.3    20.0    21.0       2
#Modelo 3. Promedios móviles ponderados (k=3)
pesos1 <- c(0.2, 0.3, 0.5)
wma1 <- stats::filter(df1$valor, filter = pesos1, sides = 1)
wma1_fc <- rep(tail(na.omit(wma1), 1), 6)
wma1
## Time Series:
## Start = 1 
## End = 12 
## Frequency = 1 
##  [1]   NA   NA 18.6 20.8 20.0 20.1 17.8 17.6 19.8 19.6 20.0 18.9
#Modelo 4. Suavizador exponencial (alfa = 0.2)
ses1 <- ses(ts1, alpha = 0.2, h = 6)
summary(ses1)
## 
## Forecast method: Simple exponential smoothing
## 
## Model Information:
## Simple exponential smoothing 
## 
## Call:
## ses(y = ts1, h = 6, alpha = 0.2)
## 
##   Smoothing parameters:
##     alpha = 0.2 
## 
##   Initial states:
##     l = 19.1774 
## 
##   sigma:  2.9273
## 
##      AIC     AICc      BIC 
## 57.40932 58.74266 58.37914 
## 
## Error measures:
##                      ME     RMSE      MAE       MPE     MAPE MASE       ACF1
## Training set 0.06549665 2.672288 2.312859 -1.479963 12.37329  NaN -0.3669082
## 
## Forecasts:
##          Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## 2025.231       19.33458 15.58304 23.08613 13.59709 25.07208
## 2025.250       19.33458 15.50875 23.16042 13.48347 25.18570
## 2025.269       19.33458 15.43587 23.23330 13.37201 25.29716
## 2025.288       19.33458 15.36432 23.30485 13.26259 25.40657
## 2025.308       19.33458 15.29405 23.37512 13.15512 25.51405
## 2025.327       19.33458 15.22497 23.44419 13.04948 25.61969
#Modelo 5. ARIMA
arima1 <- auto.arima(ts1)
summary(arima1)
## Series: ts1 
## ARIMA(0,0,0) with non-zero mean 
## 
## Coefficients:
##          mean
##       19.2500
## s.e.   0.6985
## 
## sigma^2 = 6.386:  log likelihood = -27.63
## AIC=59.26   AICc=60.59   BIC=60.23
## 
## Training set error measures:
##                        ME     RMSE      MAE       MPE     MAPE MASE      ACF1
## Training set 1.129763e-12 2.419539 2.083333 -1.662344 11.18529  NaN -0.363879
fc_arima1 <- forecast(arima1, h = 6)
fc_arima1
##          Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## 2025.231          19.25 16.01136 22.48864 14.29692 24.20308
## 2025.250          19.25 16.01136 22.48864 14.29692 24.20308
## 2025.269          19.25 16.01136 22.48864 14.29692 24.20308
## 2025.288          19.25 16.01136 22.48864 14.29692 24.20308
## 2025.308          19.25 16.01136 22.48864 14.29692 24.20308
## 2025.327          19.25 16.01136 22.48864 14.29692 24.20308
# --- Selección del mejor modelo (comparando RMSE) ---
rmse_naive1 <- accuracy(naive1)[,"RMSE"]
rmse_ma1    <- accuracy(ma1_fc, tail(ts1,6))[,"RMSE"]
rmse_wma1   <- accuracy(wma1_fc, tail(ts1,6))[,"RMSE"]
rmse_ses1   <- accuracy(ses1)[,"RMSE"]
rmse_arima1 <- accuracy(fc_arima1)[,"RMSE"]

comparacion1 <- data.frame(
  Modelo = c("Naive", "Promedios móviles", "Promedios ponderados", "Suavizado exponencial", "ARIMA"),
  RMSE = c(rmse_naive1, rmse_ma1, rmse_wma1, rmse_ses1, rmse_arima1)
)
comparacion1
##                  Modelo     RMSE
## 1                 Naive 4.033947
## 2     Promedios móviles 2.483277
## 3  Promedios ponderados 2.505328
## 4 Suavizado exponencial 2.672288
## 5                 ARIMA 2.419539
mejor_modelo1 <- comparacion1$Modelo[which.min(comparacion1$RMSE)]
mejor_modelo1
## [1] "ARIMA"
#Pronóstico
pronostico1 <- switch(mejor_modelo1,
  "Naive" = forecast(naive1, h=6),
  "Promedios móviles" = ma1_fc,
  "Promedios ponderados" = wma1_fc,
  "Suavizado exponencial" = forecast(ses1, h=6),
  "ARIMA" = fc_arima1
)
pronostico1
##          Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## 2025.231          19.25 16.01136 22.48864 14.29692 24.20308
## 2025.250          19.25 16.01136 22.48864 14.29692 24.20308
## 2025.269          19.25 16.01136 22.48864 14.29692 24.20308
## 2025.288          19.25 16.01136 22.48864 14.29692 24.20308
## 2025.308          19.25 16.01136 22.48864 14.29692 24.20308
## 2025.327          19.25 16.01136 22.48864 14.29692 24.20308
plot(pronostico1)

#Conclusión
cat("El mejor modelo según el RMSE es:", mejor_modelo1, "\n")
## El mejor modelo según el RMSE es: ARIMA
cat("El RMSE obtenido fue de:", round(min(comparacion1$RMSE), 3), "\n")
## El RMSE obtenido fue de: 2.42
#El modelo ganador fue ARIMA(0,0,0) con media no nula, lo que en la práctica es equivalente a pronosticar usando el promedio histórico de las ventas (~19.2 unidades). Esto ocurre porque la serie no muestra una tendencia clara ni un patrón autorregresivo fuerte con solo 12 observaciones, más bien fluctúa alrededor de un valor constante. Por eso el pronóstico para las siguientes 6 semanas es una línea plana, y el intervalo de confianza es amplio, reflejando la alta variabilidad semanal (15 a 23) y la poca información disponible para hacer una predicción más precisa.

Ejercicio 2 - Hershey’s

Instrucciones: Modela la siguiente serie de tiempo y elige la mejor opción. Pronostica los siguientes 6 meses.

#file.choose()
df2_raw <- read.csv("/Users/carlalievanoespinosa/Desktop/business analytics/8vo semestre/hersheys.csv")

# El archivo tiene 3 pares de columnas (2017, 2018, 2019) que hay que unir
ventas2 <- c(df2_raw$Ventas, df2_raw$Ventas.1, df2_raw$Ventas.2)
ventas2 <- as.numeric(gsub(",", "", trimws(ventas2)))  # quita comas y espacios

df2 <- data.frame(mes = 1:length(ventas2), ventas = ventas2)
str(df2)
## 'data.frame':    36 obs. of  2 variables:
##  $ mes   : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ ventas: num  25521 23740 26254 25868 27073 ...
ts2 <- ts(df2$ventas, start = c(2017,1), frequency = 12)
ts2
##           Jan      Feb      Mar      Apr      May      Jun      Jul      Aug
## 2017 25520.51 23740.11 26253.58 25868.43 27072.87 27150.50 27067.10 28145.25
## 2018 28463.69 26996.11 29768.20 29292.51 29950.68 30099.17 30851.26 32271.76
## 2019 32496.44 31287.28 33376.02 32949.77 34004.11 33757.89 32927.30 34324.12
##           Sep      Oct      Nov      Dec
## 2017 27546.29 28400.37 27441.98 27852.47
## 2018 31940.74 32995.93 32197.12 31984.82
## 2019 35151.28 36133.07 34799.91 34846.17
# Modelo 1. Naive
naive2 <- naive(ts2, h=6)
summary(naive2)
## 
## Forecast method: Naive method
## 
## Model Information:
## Call: naive(y = ts2, h = 6) 
## 
## Residual sd: 1112.6435 
## 
## Error measures:
##                    ME     RMSE     MAE       MPE     MAPE      MASE       ACF1
## Training set 266.4474 1112.643 902.852 0.8162355 3.013137 0.2579764 -0.5648048
## 
## Forecasts:
##          Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## Jan 2020       34846.17 33420.26 36272.08 32665.43 37026.91
## Feb 2020       34846.17 32829.63 36862.71 31762.14 37930.20
## Mar 2020       34846.17 32376.42 37315.92 31069.02 38623.32
## Apr 2020       34846.17 31994.35 37697.99 30484.69 39207.65
## May 2020       34846.17 31657.74 38034.60 29969.88 39722.46
## Jun 2020       34846.17 31353.42 38338.92 29504.47 40187.87
# Modelo 2. Promedios móviles
ma2 <- ma(ts2, order = 3)
ma2_fc <- rep(tail(na.omit(ma2), 1), 6)
summary(ma2)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   25171   27822   30687   30446   32504   35361       2
#Modelo 3. Promedios móviles ponderados (k=3)
pesos2 <- c(0.2, 0.3, 0.5)
wma2 <- stats::filter(df2$ventas, filter = pesos2, sides = 1)
wma2_fc <- rep(tail(na.omit(wma2), 1), 6)
wma2
## Time Series:
## Start = 1 
## End = 36 
## Frequency = 1 
##  [1]       NA       NA 25133.00 24919.82 26301.89 26486.18 27095.00 27324.43
##  [9] 27486.38 28016.59 27781.65 28003.27 27769.47 27864.56 28284.32 28287.02
## [17] 29661.99 29651.29 30175.34 30759.31 31495.31 32317.29 32308.57 32554.06
## [25] 32193.29 31998.80 32309.61 32246.40 33373.76 33427.70 33714.88 33621.96
## [33] 33791.14 34934.06 35375.54 35475.74
#Modelo 4. Suavizador exponencial (alfa = 0.2)
ses2 <- ses(ts2, alpha = 0.2, h = 6)
summary(ses2)
## 
## Forecast method: Simple exponential smoothing
## 
## Model Information:
## Simple exponential smoothing 
## 
## Call:
## ses(y = ts2, h = 6, alpha = 0.2)
## 
##   Smoothing parameters:
##     alpha = 0.2 
## 
##   Initial states:
##     l = 26303.9678 
## 
##   sigma:  1574.861
## 
##      AIC     AICc      BIC 
## 661.0074 661.3710 664.1744 
## 
## Error measures:
##                    ME     RMSE      MAE      MPE     MAPE      MASE      ACF1
## Training set 1111.747 1530.489 1335.458 3.458378 4.357769 0.3815872 0.3247296
## 
## Forecasts:
##          Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## Jan 2020       34308.55 32290.28 36326.81 31221.88 37395.22
## Feb 2020       34308.55 32250.31 36366.78 31160.75 37456.34
## Mar 2020       34308.55 32211.10 36405.99 31100.78 37516.31
## Apr 2020       34308.55 32172.61 36444.48 31041.92 37575.17
## May 2020       34308.55 32134.81 36482.28 30984.10 37632.99
## Jun 2020       34308.55 32097.65 36519.44 30927.27 37689.82
#Modelo 5. ARIMA
arima2 <- auto.arima(ts2)
summary(arima2)
## Series: ts2 
## ARIMA(1,0,0)(1,1,0)[12] with drift 
## 
## Coefficients:
##          ar1     sar1     drift
##       0.6383  -0.5517  288.8980
## s.e.  0.1551   0.2047   14.5025
## 
## sigma^2 = 202700:  log likelihood = -181.5
## AIC=371   AICc=373.11   BIC=375.72
## 
## Training set error measures:
##                    ME    RMSE      MAE        MPE      MAPE       MASE
## Training set 25.22163 343.863 227.1699 0.08059942 0.7069541 0.06491041
##                   ACF1
## Training set 0.2081043
fc_arima2 <- forecast(arima2, h = 6)
fc_arima2
##          Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## Jan 2020       35498.90 34921.91 36075.88 34616.48 36381.32
## Feb 2020       34202.17 33517.65 34886.69 33155.29 35249.05
## Mar 2020       36703.01 35979.24 37426.78 35596.10 37809.92
## Apr 2020       36271.90 35532.73 37011.07 35141.44 37402.36
## May 2020       37121.98 36376.63 37867.33 35982.07 38261.90
## Jun 2020       37102.65 36354.80 37850.51 35958.91 38246.40
# --- Selección del mejor modelo (comparando RMSE) ---
rmse_naive2 <- accuracy(naive2)[,"RMSE"]
rmse_ma2    <- accuracy(ma2_fc, tail(ts2,6))[,"RMSE"]
rmse_wma2   <- accuracy(wma2_fc, tail(ts2,6))[,"RMSE"]
rmse_ses2   <- accuracy(ses2)[,"RMSE"]
rmse_arima2 <- accuracy(fc_arima2)[,"RMSE"]

comparacion2 <- data.frame(
  Modelo = c("Naive", "Promedios móviles", "Promedios ponderados", "Suavizado exponencial", "ARIMA"),
  RMSE = c(rmse_naive2, rmse_ma2, rmse_wma2, rmse_ses2, rmse_arima2)
)
comparacion2
##                  Modelo     RMSE
## 1                 Naive 1112.643
## 2     Promedios móviles 1115.979
## 3  Promedios ponderados 1239.036
## 4 Suavizado exponencial 1530.489
## 5                 ARIMA  343.863
mejor_modelo2 <- comparacion2$Modelo[which.min(comparacion2$RMSE)]
mejor_modelo2
## [1] "ARIMA"
#Pronóstico
pronostico2 <- switch(mejor_modelo2,
  "Naive" = forecast(naive2, h=6),
  "Promedios móviles" = ma2_fc,
  "Promedios ponderados" = wma2_fc,
  "Suavizado exponencial" = forecast(ses2, h=6),
  "ARIMA" = fc_arima2
)
pronostico2
##          Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## Jan 2020       35498.90 34921.91 36075.88 34616.48 36381.32
## Feb 2020       34202.17 33517.65 34886.69 33155.29 35249.05
## Mar 2020       36703.01 35979.24 37426.78 35596.10 37809.92
## Apr 2020       36271.90 35532.73 37011.07 35141.44 37402.36
## May 2020       37121.98 36376.63 37867.33 35982.07 38261.90
## Jun 2020       37102.65 36354.80 37850.51 35958.91 38246.40
plot(pronostico2)

#Conclusión
cat("El mejor modelo según el RMSE es:", mejor_modelo2, "\n")
## El mejor modelo según el RMSE es: ARIMA
cat("El RMSE obtenido fue de:", round(min(comparacion2$RMSE), 3), "\n")
## El RMSE obtenido fue de: 343.863
#El modelo ganador fue ARIMA(1,0,0)(1,1,0)[12], el cual captura tanto la tendencia creciente de las ventas de Hershey's a lo largo de los 3 años (de ~25,000 a ~35,000) como el patrón estacional mensual que se repite cada 12 meses (los mismos altibajos que se ven en cada año). El pronóstico para los primeros 6 meses de 2020 mantiene esta tendencia al alza, proyectando ventas entre ~34,000 y ~37,000, con un intervalo de confianza razonablemente estrecho, reflejo de que, a diferencia del Ejercicio 1, aquí sí hay suficiente historia (36 meses) para que el modelo capture el comportamiento real de la serie con mayor certeza.

Ejercicio 3 - Vintage Restaurant

library(forecast)

# --- Datos ---
ventas <- c(242,235,232,178,184,140,145,152,110,130,152,206,   # Año 1
            263,238,247,193,193,149,157,161,122,130,167,230,   # Año 2
            282,255,265,205,210,160,166,174,126,148,173,235)   # Año 3

ts_vin <- ts(ventas, start = c(1,1), frequency = 12)

# 1. Gráfica de la serie de tiempo
plot(ts_vin, main = "Ventas mensuales - Vintage Restaurant", 
     ylab = "Ventas ($ miles)", xlab = "Año", type = "o")

# 2. Descomposición y análisis de estacionalidad
descomp <- decompose(ts_vin, type = "multiplicative")
plot(descomp)

indices_estacionales <- descomp$figure
names(indices_estacionales) <- month.abb
indices_estacionales
##       Jan       Feb       Mar       Apr       May       Jun       Jul       Aug 
## 1.4435875 1.2997005 1.3441203 1.0412017 1.0493867 0.8003807 0.8283067 0.8529746 
##       Sep       Oct       Nov       Dec 
## 0.6279872 0.7003228 0.8527594 1.1592718
# 3. Serie desestacionalizada
desestacionalizada <- ts_vin / rep(indices_estacionales, 3)
plot(desestacionalizada, main = "Serie desestacionalizada", type = "o")

# Tendencia sobre la serie desestacionalizada
t <- 1:36
tendencia <- lm(desestacionalizada ~ t)
summary(tendencia)
## 
## Call:
## lm(formula = desestacionalizada ~ t)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -6.1892 -2.2527 -0.4848  0.8131  9.4189 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 169.3494     1.0926  155.00   <2e-16 ***
## t             1.0213     0.0515   19.83   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 3.21 on 34 degrees of freedom
## Multiple R-squared:  0.9204, Adjusted R-squared:  0.9181 
## F-statistic: 393.3 on 1 and 34 DF,  p-value: < 2.2e-16
# 4. Pronóstico por descomposición (tendencia x índice estacional)
t4 <- 37:48
pred_tendencia <- coef(tendencia)[1] + coef(tendencia)[2]*t4
pronostico_descomp <- pred_tendencia * indices_estacionales
pronostico_descomp
##      Jan      Feb      Mar      Apr      May      Jun      Jul      Aug 
## 299.0209 270.5439 281.1630 218.8619 221.6542 169.8759 176.6490 182.7809 
##      Sep      Oct      Nov      Dec 
## 135.2105 151.5002 185.3475 253.1521
# 5. Pronóstico con regresión de variables ficticias (dummy)
mes <- factor(rep(month.abb, 3), levels = month.abb)
df_dummy <- data.frame(t = t, mes = mes, ventas = ventas)
modelo_dummy <- lm(ventas ~ t + mes, data = df_dummy)
summary(modelo_dummy)
## 
## Call:
## lm(formula = ventas ~ t + mes, data = df_dummy)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -8.1250 -1.9583  0.1667  2.2292  7.4583 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  249.10764    2.74567  90.727  < 2e-16 ***
## t              1.01736    0.07554  13.467 2.14e-12 ***
## mesFeb       -20.68403    3.62688  -5.703 8.30e-06 ***
## mesMar       -16.36806    3.62924  -4.510 0.000158 ***
## mesApr       -73.38542    3.63317 -20.199 3.90e-16 ***
## mesMay       -70.73611    3.63866 -19.440 8.97e-16 ***
## mesJun      -117.75347    3.64571 -32.299  < 2e-16 ***
## mesJul      -112.43750    3.65431 -30.768  < 2e-16 ***
## mesAug      -107.12153    3.66445 -29.233  < 2e-16 ***
## mesSep      -151.13889    3.67611 -41.114  < 2e-16 ***
## mesOct      -135.48958    3.68928 -36.725  < 2e-16 ***
## mesNov      -108.50694    3.70395 -29.295  < 2e-16 ***
## mesDec       -49.85764    3.72009 -13.402 2.36e-12 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 4.441 on 23 degrees of freedom
## Multiple R-squared:  0.9942, Adjusted R-squared:  0.9911 
## F-statistic:   327 on 12 and 23 DF,  p-value: < 2.2e-16
nuevo <- data.frame(t = t4, mes = factor(month.abb, levels = month.abb))
pronostico_dummy <- predict(modelo_dummy, newdata = nuevo)
pronostico_dummy
##        1        2        3        4        5        6        7        8 
## 286.7500 267.0833 272.4167 216.4167 220.0833 174.0833 180.4167 186.7500 
##        9       10       11       12 
## 143.7500 160.4167 188.4167 248.0833
# Error de pronóstico para enero del año 4 (venta real = 295)
error_descomp <- 295 - pronostico_descomp[1]
error_dummy   <- 295 - pronostico_dummy[1]
error_descomp
##       Jan 
## -4.020918
error_dummy
##    1 
## 8.25

Gráfica de la serie de tiempo

La serie muestra un patrón estacional muy marcado que se repite cada 12 meses, combinado con una tendencia ligeramente creciente año con año (las ventas de cada mes son consistentemente mayores que las del mismo mes del año anterior).

Análisis de estacionalidad

library(knitr)

tabla_estacional <- data.frame(
  Mes = month.abb,
  Indice_Estacional = round(as.numeric(indices_estacionales), 3)
)

kable(tabla_estacional, 
      col.names = c("Mes", "Índice Estacional"),
      caption = "Índices estacionales - Vintage Restaurant",
      align = "c")
Índices estacionales - Vintage Restaurant
Mes Índice Estacional
Jan 1.444
Feb 1.300
Mar 1.344
Apr 1.041
May 1.049
Jun 0.800
Jul 0.828
Aug 0.853
Sep 0.628
Oct 0.700
Nov 0.853
Dec 1.159

Enero, febrero y marzo tienen ventas ~30-44% por encima del promedio, mientras que septiembre cae ~37% por debajo. Esto tiene sentido intuitivo: Captiva Island, Florida, es un destino turístico de temporada invernal (los “snowbirds” llegan de diciembre a marzo), mientras que septiembre coincide con el verano, el calor y la temporada de huracanes, cuando el turismo cae drásticamente.

Serie desestacionalizada

Al remover el efecto estacional, se observa una tendencia lineal creciente clara: las ventas desestacionalizadas suben de ~168 (mes 1) a ~203 (mes 36), con una pendiente estimada de ~1.02 unidades por mes, es decir, un crecimiento subyacente constante del negocio, sin importar la temporada.

Tabla 2: Pronóstico por descomposición (Año 4)

tabla_descomp <- data.frame(
  Mes = month.abb,
  Pronostico = round(as.numeric(pronostico_descomp), 2)
)

kable(tabla_descomp,
      col.names = c("Mes", "Pronóstico ($ miles)"),
      caption = "Pronóstico Año 4 - Método de Descomposición",
      align = "c")
Pronóstico Año 4 - Método de Descomposición
Mes Pronóstico ($ miles)
Jan 299.02
Feb 270.54
Mar 281.16
Apr 218.86
May 221.65
Jun 169.88
Jul 176.65
Aug 182.78
Sep 135.21
Oct 151.50
Nov 185.35
Dec 253.15

Pronóstico por regresión con variables dummy (Año 4)

tabla_dummy <- data.frame(
  Mes = month.abb,
  Pronostico = round(as.numeric(pronostico_dummy), 2)
)

kable(tabla_dummy,
      col.names = c("Mes", "Pronóstico ($ miles)"),
      caption = "Pronóstico Año 4 - Regresión con Variables Ficticias",
      align = "c")
Pronóstico Año 4 - Regresión con Variables Ficticias
Mes Pronóstico ($ miles)
Jan 286.75
Feb 267.08
Mar 272.42
Apr 216.42
May 220.08
Jun 174.08
Jul 180.42
Aug 186.75
Sep 143.75
Oct 160.42
Nov 188.42
Dec 248.08

Comparación de ambos métodos

tabla_comparacion <- data.frame(
  Mes = month.abb,
  Descomposicion = round(as.numeric(pronostico_descomp), 2),
  Regresion_Dummy = round(as.numeric(pronostico_dummy), 2)
)

kable(tabla_comparacion,
      col.names = c("Mes", "Descomposición ($ miles)", "Regresión Dummy ($ miles)"),
      caption = "Comparación de pronósticos - Año 4",
      align = "c")
Comparación de pronósticos - Año 4
Mes Descomposición ($ miles) Regresión Dummy ($ miles)
Jan 299.02 286.75
Feb 270.54 267.08
Mar 281.16 272.42
Apr 218.86 216.42
May 221.65 220.08
Jun 169.88 174.08
Jul 176.65 180.42
Aug 182.78 186.75
Sep 135.21 143.75
Oct 151.50 160.42
Nov 185.35 188.42
Dec 253.15 248.08

Error de pronóstico (Enero Año 4)

venta_real_ene4 <- 295

tabla_error <- data.frame(
  Metodo = c("Descomposición", "Regresión con Dummy"),
  Pronostico = c(round(pronostico_descomp[1], 2), round(pronostico_dummy[1], 2)),
  Real = venta_real_ene4,
  Error = c(round(venta_real_ene4 - pronostico_descomp[1], 2), 
            round(venta_real_ene4 - pronostico_dummy[1], 2)),
  Error_Porcentual = c(paste0(round((venta_real_ene4 - pronostico_descomp[1])/venta_real_ene4*100, 2), "%"),
                       paste0(round((venta_real_ene4 - pronostico_dummy[1])/venta_real_ene4*100, 2), "%"))
)

kable(tabla_error,
      col.names = c("Método", "Pronóstico", "Real", "Error", "Error %"),
      caption = "Error de pronóstico - Enero Año 4",
      align = "c")
Error de pronóstico - Enero Año 4
Método Pronóstico Real Error Error %
Jan Descomposición 299.02 295 -4.02 -1.36%
1 Regresión con Dummy 286.75 295 8.25 2.8%

El modelo de regresión con dummies tiene un R² de 0.994, lo que indica un ajuste excelente. Ambos métodos dan resultados muy similares y consistentes entre sí.

Error de pronóstico (enero del Año 4, venta real = $295,000)

Método de descomposición: pronóstico = $299,020 → error = –$4,020 (–1.4%) Método de regresión con dummies: pronóstico = $286,750 → error = +$8,250 (+2.8%)

Ambos errores son relativamente pequeños (menores al 3%), por lo que no deberían generar mayor confusión para Karen, son diferencias normales dentro del margen esperado de un buen modelo de pronóstico.

Ejercicio 4: Carlson Department store

library(knitr)

# --- Datos históricos pre-huracán (36 meses: Sep Año1 - Ago Año4) ---
carlson <- c(1.71,1.90,2.74,4.20,                                       # Año1 Sep-Dic
             1.45,1.80,2.03,1.99,2.32,2.20,2.13,2.43,1.90,2.13,2.56,4.16, # Año2
             2.31,1.89,2.02,2.23,2.39,2.14,2.27,2.21,1.89,2.29,2.83,4.04, # Año3
             2.31,1.99,2.42,2.45,2.57,2.42,2.40,2.50)                    # Año4 Ene-Ago

condado <- c(55.80,56.40,71.40,117.60,
             46.80,48.00,60.00,57.60,61.80,58.20,56.40,63.00,57.60,53.40,71.40,114.00,
             46.80,48.60,59.40,58.20,60.60,55.20,51.00,58.80,49.80,54.60,65.40,102.00,
             43.80,45.60,57.60,53.40,56.40,52.80,54.00,60.60)

# Ventas reales del condado durante el cierre (Sep-Dic Año4)
condado_real_cierre <- c(47.40, 54.60, 67.80, 100.20)

ts_carlson <- ts(carlson, start = c(1,9), frequency = 12)
ts_condado <- ts(condado, start = c(1,9), frequency = 12)


# --- Descomposición multiplicativa ---
descomp_carlson <- decompose(ts_carlson, type = "multiplicative")
descomp_condado <- decompose(ts_condado, type = "multiplicative")

indices_carlson <- descomp_carlson$figure
indices_condado <- descomp_condado$figure
names(indices_carlson) <- month.abb[c(9:12,1:8)]
names(indices_condado) <- month.abb[c(9:12,1:8)]

# --- Desestacionalizar y ajustar tendencia ---
t <- 1:36
deseason_carlson <- ts_carlson / rep(descomp_carlson$figure, 3)
deseason_condado  <- ts_condado / rep(descomp_condado$figure, 3)

tend_carlson <- lm(as.numeric(deseason_carlson) ~ t)
tend_condado <- lm(as.numeric(deseason_condado) ~ t)

# --- Pronóstico Sep-Dic Año4 (meses 37-40) SIN huracán ---
t_f <- 37:40
indices_f <- descomp_carlson$figure[9:12]  # Sep, Oct, Nov, Dic
indices_f_condado <- descomp_condado$figure[9:12]

pred_carlson <- (coef(tend_carlson)[1] + coef(tend_carlson)[2]*t_f) * indices_f
pred_condado <- (coef(tend_condado)[1] + coef(tend_condado)[2]*t_f) * indices_f_condado

pred_carlson
## [1] 2.632104 2.440441 2.467124 2.591732
pred_condado
## [1] 57.10517 53.12090 50.44835 57.16943
# --- 1. Ventas estimadas de Carlson sin huracán (pérdida) ---
perdida_carlson <- pred_carlson  # ventas reales = 0 (tienda cerrada)
sum(perdida_carlson)
## [1] 10.1314
# --- 2. Ventas estimadas del condado sin huracán ---
sum(pred_condado)
## [1] 217.8439
# --- 3. Comparación condado: real vs. pronóstico (exceso por huracán) ---
exceso <- condado_real_cierre - pred_condado
tabla_exceso <- data.frame(
  Mes = c("Sep","Oct","Nov","Dic"),
  Pronostico_sin_huracan = round(as.numeric(pred_condado), 2),
  Real = condado_real_cierre,
  Exceso = round(exceso, 2)
)
tabla_exceso
##   Mes Pronostico_sin_huracan  Real Exceso
## 1 Sep                  57.11  47.4  -9.71
## 2 Oct                  53.12  54.6   1.48
## 3 Nov                  50.45  67.8  17.35
## 4 Dic                  57.17 100.2  43.03
sum(exceso)
## [1] 52.15614
kable(data.frame(Mes = c("Sep","Oct","Nov","Dic"),
                  Pronostico = round(as.numeric(pred_carlson), 2)),
      col.names = c("Mes", "Ventas estimadas sin huracán ($ millones)"),
      caption = "Pronóstico de ventas de Carlson (sin huracán) - Sep a Dic Año 4",
      align = "c")
Pronóstico de ventas de Carlson (sin huracán) - Sep a Dic Año 4
Mes Ventas estimadas sin huracán ($ millones)
Sep 2.63
Oct 2.44
Nov 2.47
Dic 2.59
kable(tabla_exceso,
      col.names = c("Mes", "Pronóstico sin huracán", "Real", "Exceso"),
      caption = "Comparación de ventas reales vs. pronosticadas - Condado",
      align = "c")
Comparación de ventas reales vs. pronosticadas - Condado
Mes Pronóstico sin huracán Real Exceso
Sep 57.11 47.4 -9.71
Oct 53.12 54.6 1.48
Nov 50.45 67.8 17.35
Dic 57.17 100.2 43.03
LS0tCnRpdGxlOiAiU2VyaWVzIGRlIHRpZW1wbyIKYXV0aG9yOiAiQ2FybGEgTGlldmFubyAtIEEwMTcxMTg1NiIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQogICAgY29kZV9kb3dubG9hZDogVFJVRQogICAgdGhlbWU6IHVuaXRlZApkYXRlOiAiMjAyNi0wOC0xOCIKLS0tCgojIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5JbnN0YWxhciBwYXF1ZXRlcyB5IGxsYW1hciBsaWJyZXJpYXM8L3NwYW4+CmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNpbnN0YWxsLnBhY2thZ2VzKCJmb3JlY2FzdCIpCmxpYnJhcnkoZm9yZWNhc3QpCmBgYAoKIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+RWplcmNpY2lvIDEgLSBWZW50YXMgc2VtYW5hbGVzPC9zcGFuPgpJTlNUUlVDQ0lPTkVTOiBNb2RlbGEgbGEgc2lndWllbnRlIHNlcmllIGRlIHRpZW1wbyB5IGVsaWdlIGxhIG1lam9yIG9wY2nDs24uIFByb25vc3RpY2EgbGFzIHNpZ3VpZW50ZXMgNiBzZW1hbmFzLgpgYGB7cn0Kc2VtYW5hIDwtIGMoMToxMikKdmFsb3IgPC0gYygxNywyMSwxOSwyMywxOCwxNiwyMCwxOCwyMiwyMCwxNSwyMikKZGYxIDwtIGRhdGEuZnJhbWUoc2VtYW5hLCB2YWxvcikKCnRzMSA8LSB0cyh2YWxvciwgYygyMDI1LDEpLCBmcmVxdWVuY3kgPSA1MikKCgojIE1vZGVsbyAxLiBOYWl2ZQpuYWl2ZTEgPC0gbmFpdmUodHMxLCBoPTYpCnN1bW1hcnkobmFpdmUxKQoKIyBNb2RlbG8gMi4gUHJvbWVkaW9zIG3Ds3ZpbGVzCm1hMSA8LSBtYSh0czEsIG9yZGVyID0gMykKbWExX2ZjIDwtIHJlcCh0YWlsKG5hLm9taXQobWExKSwgMSksIDYpCnN1bW1hcnkobWExKQoKCiNNb2RlbG8gMy4gUHJvbWVkaW9zIG3Ds3ZpbGVzIHBvbmRlcmFkb3MgKGs9MykKcGVzb3MxIDwtIGMoMC4yLCAwLjMsIDAuNSkKd21hMSA8LSBzdGF0czo6ZmlsdGVyKGRmMSR2YWxvciwgZmlsdGVyID0gcGVzb3MxLCBzaWRlcyA9IDEpCndtYTFfZmMgPC0gcmVwKHRhaWwobmEub21pdCh3bWExKSwgMSksIDYpCndtYTEKCgojTW9kZWxvIDQuIFN1YXZpemFkb3IgZXhwb25lbmNpYWwgKGFsZmEgPSAwLjIpCnNlczEgPC0gc2VzKHRzMSwgYWxwaGEgPSAwLjIsIGggPSA2KQpzdW1tYXJ5KHNlczEpCgojTW9kZWxvIDUuIEFSSU1BCmFyaW1hMSA8LSBhdXRvLmFyaW1hKHRzMSkKc3VtbWFyeShhcmltYTEpCmZjX2FyaW1hMSA8LSBmb3JlY2FzdChhcmltYTEsIGggPSA2KQpmY19hcmltYTEKCiMgLS0tIFNlbGVjY2nDs24gZGVsIG1lam9yIG1vZGVsbyAoY29tcGFyYW5kbyBSTVNFKSAtLS0Kcm1zZV9uYWl2ZTEgPC0gYWNjdXJhY3kobmFpdmUxKVssIlJNU0UiXQpybXNlX21hMSAgICA8LSBhY2N1cmFjeShtYTFfZmMsIHRhaWwodHMxLDYpKVssIlJNU0UiXQpybXNlX3dtYTEgICA8LSBhY2N1cmFjeSh3bWExX2ZjLCB0YWlsKHRzMSw2KSlbLCJSTVNFIl0Kcm1zZV9zZXMxICAgPC0gYWNjdXJhY3koc2VzMSlbLCJSTVNFIl0Kcm1zZV9hcmltYTEgPC0gYWNjdXJhY3koZmNfYXJpbWExKVssIlJNU0UiXQoKY29tcGFyYWNpb24xIDwtIGRhdGEuZnJhbWUoCiAgTW9kZWxvID0gYygiTmFpdmUiLCAiUHJvbWVkaW9zIG3Ds3ZpbGVzIiwgIlByb21lZGlvcyBwb25kZXJhZG9zIiwgIlN1YXZpemFkbyBleHBvbmVuY2lhbCIsICJBUklNQSIpLAogIFJNU0UgPSBjKHJtc2VfbmFpdmUxLCBybXNlX21hMSwgcm1zZV93bWExLCBybXNlX3NlczEsIHJtc2VfYXJpbWExKQopCmNvbXBhcmFjaW9uMQoKbWVqb3JfbW9kZWxvMSA8LSBjb21wYXJhY2lvbjEkTW9kZWxvW3doaWNoLm1pbihjb21wYXJhY2lvbjEkUk1TRSldCm1lam9yX21vZGVsbzEKCgojUHJvbsOzc3RpY28KcHJvbm9zdGljbzEgPC0gc3dpdGNoKG1lam9yX21vZGVsbzEsCiAgIk5haXZlIiA9IGZvcmVjYXN0KG5haXZlMSwgaD02KSwKICAiUHJvbWVkaW9zIG3Ds3ZpbGVzIiA9IG1hMV9mYywKICAiUHJvbWVkaW9zIHBvbmRlcmFkb3MiID0gd21hMV9mYywKICAiU3Vhdml6YWRvIGV4cG9uZW5jaWFsIiA9IGZvcmVjYXN0KHNlczEsIGg9NiksCiAgIkFSSU1BIiA9IGZjX2FyaW1hMQopCnByb25vc3RpY28xCnBsb3QocHJvbm9zdGljbzEpCgojQ29uY2x1c2nDs24KY2F0KCJFbCBtZWpvciBtb2RlbG8gc2Vnw7puIGVsIFJNU0UgZXM6IiwgbWVqb3JfbW9kZWxvMSwgIlxuIikKY2F0KCJFbCBSTVNFIG9idGVuaWRvIGZ1ZSBkZToiLCByb3VuZChtaW4oY29tcGFyYWNpb24xJFJNU0UpLCAzKSwgIlxuIikKCiNFbCBtb2RlbG8gZ2FuYWRvciBmdWUgQVJJTUEoMCwwLDApIGNvbiBtZWRpYSBubyBudWxhLCBsbyBxdWUgZW4gbGEgcHLDoWN0aWNhIGVzIGVxdWl2YWxlbnRlIGEgcHJvbm9zdGljYXIgdXNhbmRvIGVsIHByb21lZGlvIGhpc3TDs3JpY28gZGUgbGFzIHZlbnRhcyAofjE5LjIgdW5pZGFkZXMpLiBFc3RvIG9jdXJyZSBwb3JxdWUgbGEgc2VyaWUgbm8gbXVlc3RyYSB1bmEgdGVuZGVuY2lhIGNsYXJhIG5pIHVuIHBhdHLDs24gYXV0b3JyZWdyZXNpdm8gZnVlcnRlIGNvbiBzb2xvIDEyIG9ic2VydmFjaW9uZXMsIG3DoXMgYmllbiBmbHVjdMO6YSBhbHJlZGVkb3IgZGUgdW4gdmFsb3IgY29uc3RhbnRlLiBQb3IgZXNvIGVsIHByb27Ds3N0aWNvIHBhcmEgbGFzIHNpZ3VpZW50ZXMgNiBzZW1hbmFzIGVzIHVuYSBsw61uZWEgcGxhbmEsIHkgZWwgaW50ZXJ2YWxvIGRlIGNvbmZpYW56YSBlcyBhbXBsaW8sIHJlZmxlamFuZG8gbGEgYWx0YSB2YXJpYWJpbGlkYWQgc2VtYW5hbCAoMTUgYSAyMykgeSBsYSBwb2NhIGluZm9ybWFjacOzbiBkaXNwb25pYmxlIHBhcmEgaGFjZXIgdW5hIHByZWRpY2Npw7NuIG3DoXMgcHJlY2lzYS4KCgpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkVqZXJjaWNpbyAyIC0gSGVyc2hleSdzPC9zcGFuPgpJbnN0cnVjY2lvbmVzOiBNb2RlbGEgbGEgc2lndWllbnRlIHNlcmllIGRlIHRpZW1wbyB5IGVsaWdlIGxhIG1lam9yIG9wY2nDs24uIFByb25vc3RpY2EgbG9zIHNpZ3VpZW50ZXMgNiBtZXNlcy4KCmBgYHtyfQojZmlsZS5jaG9vc2UoKQpkZjJfcmF3IDwtIHJlYWQuY3N2KCIvVXNlcnMvY2FybGFsaWV2YW5vZXNwaW5vc2EvRGVza3RvcC9idXNpbmVzcyBhbmFseXRpY3MvOHZvIHNlbWVzdHJlL2hlcnNoZXlzLmNzdiIpCgojIEVsIGFyY2hpdm8gdGllbmUgMyBwYXJlcyBkZSBjb2x1bW5hcyAoMjAxNywgMjAxOCwgMjAxOSkgcXVlIGhheSBxdWUgdW5pcgp2ZW50YXMyIDwtIGMoZGYyX3JhdyRWZW50YXMsIGRmMl9yYXckVmVudGFzLjEsIGRmMl9yYXckVmVudGFzLjIpCnZlbnRhczIgPC0gYXMubnVtZXJpYyhnc3ViKCIsIiwgIiIsIHRyaW13cyh2ZW50YXMyKSkpICAjIHF1aXRhIGNvbWFzIHkgZXNwYWNpb3MKCmRmMiA8LSBkYXRhLmZyYW1lKG1lcyA9IDE6bGVuZ3RoKHZlbnRhczIpLCB2ZW50YXMgPSB2ZW50YXMyKQpzdHIoZGYyKQoKdHMyIDwtIHRzKGRmMiR2ZW50YXMsIHN0YXJ0ID0gYygyMDE3LDEpLCBmcmVxdWVuY3kgPSAxMikKdHMyCgojIE1vZGVsbyAxLiBOYWl2ZQpuYWl2ZTIgPC0gbmFpdmUodHMyLCBoPTYpCnN1bW1hcnkobmFpdmUyKQoKIyBNb2RlbG8gMi4gUHJvbWVkaW9zIG3Ds3ZpbGVzCm1hMiA8LSBtYSh0czIsIG9yZGVyID0gMykKbWEyX2ZjIDwtIHJlcCh0YWlsKG5hLm9taXQobWEyKSwgMSksIDYpCnN1bW1hcnkobWEyKQoKI01vZGVsbyAzLiBQcm9tZWRpb3MgbcOzdmlsZXMgcG9uZGVyYWRvcyAoaz0zKQpwZXNvczIgPC0gYygwLjIsIDAuMywgMC41KQp3bWEyIDwtIHN0YXRzOjpmaWx0ZXIoZGYyJHZlbnRhcywgZmlsdGVyID0gcGVzb3MyLCBzaWRlcyA9IDEpCndtYTJfZmMgPC0gcmVwKHRhaWwobmEub21pdCh3bWEyKSwgMSksIDYpCndtYTIKCiNNb2RlbG8gNC4gU3Vhdml6YWRvciBleHBvbmVuY2lhbCAoYWxmYSA9IDAuMikKc2VzMiA8LSBzZXModHMyLCBhbHBoYSA9IDAuMiwgaCA9IDYpCnN1bW1hcnkoc2VzMikKCgojTW9kZWxvIDUuIEFSSU1BCmFyaW1hMiA8LSBhdXRvLmFyaW1hKHRzMikKc3VtbWFyeShhcmltYTIpCmZjX2FyaW1hMiA8LSBmb3JlY2FzdChhcmltYTIsIGggPSA2KQpmY19hcmltYTIKCiMgLS0tIFNlbGVjY2nDs24gZGVsIG1lam9yIG1vZGVsbyAoY29tcGFyYW5kbyBSTVNFKSAtLS0Kcm1zZV9uYWl2ZTIgPC0gYWNjdXJhY3kobmFpdmUyKVssIlJNU0UiXQpybXNlX21hMiAgICA8LSBhY2N1cmFjeShtYTJfZmMsIHRhaWwodHMyLDYpKVssIlJNU0UiXQpybXNlX3dtYTIgICA8LSBhY2N1cmFjeSh3bWEyX2ZjLCB0YWlsKHRzMiw2KSlbLCJSTVNFIl0Kcm1zZV9zZXMyICAgPC0gYWNjdXJhY3koc2VzMilbLCJSTVNFIl0Kcm1zZV9hcmltYTIgPC0gYWNjdXJhY3koZmNfYXJpbWEyKVssIlJNU0UiXQoKY29tcGFyYWNpb24yIDwtIGRhdGEuZnJhbWUoCiAgTW9kZWxvID0gYygiTmFpdmUiLCAiUHJvbWVkaW9zIG3Ds3ZpbGVzIiwgIlByb21lZGlvcyBwb25kZXJhZG9zIiwgIlN1YXZpemFkbyBleHBvbmVuY2lhbCIsICJBUklNQSIpLAogIFJNU0UgPSBjKHJtc2VfbmFpdmUyLCBybXNlX21hMiwgcm1zZV93bWEyLCBybXNlX3NlczIsIHJtc2VfYXJpbWEyKQopCmNvbXBhcmFjaW9uMgoKbWVqb3JfbW9kZWxvMiA8LSBjb21wYXJhY2lvbjIkTW9kZWxvW3doaWNoLm1pbihjb21wYXJhY2lvbjIkUk1TRSldCm1lam9yX21vZGVsbzIKCgojUHJvbsOzc3RpY28KcHJvbm9zdGljbzIgPC0gc3dpdGNoKG1lam9yX21vZGVsbzIsCiAgIk5haXZlIiA9IGZvcmVjYXN0KG5haXZlMiwgaD02KSwKICAiUHJvbWVkaW9zIG3Ds3ZpbGVzIiA9IG1hMl9mYywKICAiUHJvbWVkaW9zIHBvbmRlcmFkb3MiID0gd21hMl9mYywKICAiU3Vhdml6YWRvIGV4cG9uZW5jaWFsIiA9IGZvcmVjYXN0KHNlczIsIGg9NiksCiAgIkFSSU1BIiA9IGZjX2FyaW1hMgopCnByb25vc3RpY28yCnBsb3QocHJvbm9zdGljbzIpCgojQ29uY2x1c2nDs24KY2F0KCJFbCBtZWpvciBtb2RlbG8gc2Vnw7puIGVsIFJNU0UgZXM6IiwgbWVqb3JfbW9kZWxvMiwgIlxuIikKY2F0KCJFbCBSTVNFIG9idGVuaWRvIGZ1ZSBkZToiLCByb3VuZChtaW4oY29tcGFyYWNpb24yJFJNU0UpLCAzKSwgIlxuIikKCiNFbCBtb2RlbG8gZ2FuYWRvciBmdWUgQVJJTUEoMSwwLDApKDEsMSwwKVsxMl0sIGVsIGN1YWwgY2FwdHVyYSB0YW50byBsYSB0ZW5kZW5jaWEgY3JlY2llbnRlIGRlIGxhcyB2ZW50YXMgZGUgSGVyc2hleSdzIGEgbG8gbGFyZ28gZGUgbG9zIDMgYcOxb3MgKGRlIH4yNSwwMDAgYSB+MzUsMDAwKSBjb21vIGVsIHBhdHLDs24gZXN0YWNpb25hbCBtZW5zdWFsIHF1ZSBzZSByZXBpdGUgY2FkYSAxMiBtZXNlcyAobG9zIG1pc21vcyBhbHRpYmFqb3MgcXVlIHNlIHZlbiBlbiBjYWRhIGHDsW8pLiBFbCBwcm9uw7NzdGljbyBwYXJhIGxvcyBwcmltZXJvcyA2IG1lc2VzIGRlIDIwMjAgbWFudGllbmUgZXN0YSB0ZW5kZW5jaWEgYWwgYWx6YSwgcHJveWVjdGFuZG8gdmVudGFzIGVudHJlIH4zNCwwMDAgeSB+MzcsMDAwLCBjb24gdW4gaW50ZXJ2YWxvIGRlIGNvbmZpYW56YSByYXpvbmFibGVtZW50ZSBlc3RyZWNobywgcmVmbGVqbyBkZSBxdWUsIGEgZGlmZXJlbmNpYSBkZWwgRWplcmNpY2lvIDEsIGFxdcOtIHPDrSBoYXkgc3VmaWNpZW50ZSBoaXN0b3JpYSAoMzYgbWVzZXMpIHBhcmEgcXVlIGVsIG1vZGVsbyBjYXB0dXJlIGVsIGNvbXBvcnRhbWllbnRvIHJlYWwgZGUgbGEgc2VyaWUgY29uIG1heW9yIGNlcnRlemEuCgpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkVqZXJjaWNpbyAzIC0gVmludGFnZSBSZXN0YXVyYW50PC9zcGFuPgpgYGB7cn0KbGlicmFyeShmb3JlY2FzdCkKCiMgLS0tIERhdG9zIC0tLQp2ZW50YXMgPC0gYygyNDIsMjM1LDIzMiwxNzgsMTg0LDE0MCwxNDUsMTUyLDExMCwxMzAsMTUyLDIwNiwgICAjIEHDsW8gMQogICAgICAgICAgICAyNjMsMjM4LDI0NywxOTMsMTkzLDE0OSwxNTcsMTYxLDEyMiwxMzAsMTY3LDIzMCwgICAjIEHDsW8gMgogICAgICAgICAgICAyODIsMjU1LDI2NSwyMDUsMjEwLDE2MCwxNjYsMTc0LDEyNiwxNDgsMTczLDIzNSkgICAjIEHDsW8gMwoKdHNfdmluIDwtIHRzKHZlbnRhcywgc3RhcnQgPSBjKDEsMSksIGZyZXF1ZW5jeSA9IDEyKQoKIyAxLiBHcsOhZmljYSBkZSBsYSBzZXJpZSBkZSB0aWVtcG8KcGxvdCh0c192aW4sIG1haW4gPSAiVmVudGFzIG1lbnN1YWxlcyAtIFZpbnRhZ2UgUmVzdGF1cmFudCIsIAogICAgIHlsYWIgPSAiVmVudGFzICgkIG1pbGVzKSIsIHhsYWIgPSAiQcOxbyIsIHR5cGUgPSAibyIpCgojIDIuIERlc2NvbXBvc2ljacOzbiB5IGFuw6FsaXNpcyBkZSBlc3RhY2lvbmFsaWRhZApkZXNjb21wIDwtIGRlY29tcG9zZSh0c192aW4sIHR5cGUgPSAibXVsdGlwbGljYXRpdmUiKQpwbG90KGRlc2NvbXApCmluZGljZXNfZXN0YWNpb25hbGVzIDwtIGRlc2NvbXAkZmlndXJlCm5hbWVzKGluZGljZXNfZXN0YWNpb25hbGVzKSA8LSBtb250aC5hYmIKaW5kaWNlc19lc3RhY2lvbmFsZXMKCiMgMy4gU2VyaWUgZGVzZXN0YWNpb25hbGl6YWRhCmRlc2VzdGFjaW9uYWxpemFkYSA8LSB0c192aW4gLyByZXAoaW5kaWNlc19lc3RhY2lvbmFsZXMsIDMpCnBsb3QoZGVzZXN0YWNpb25hbGl6YWRhLCBtYWluID0gIlNlcmllIGRlc2VzdGFjaW9uYWxpemFkYSIsIHR5cGUgPSAibyIpCgojIFRlbmRlbmNpYSBzb2JyZSBsYSBzZXJpZSBkZXNlc3RhY2lvbmFsaXphZGEKdCA8LSAxOjM2CnRlbmRlbmNpYSA8LSBsbShkZXNlc3RhY2lvbmFsaXphZGEgfiB0KQpzdW1tYXJ5KHRlbmRlbmNpYSkKCiMgNC4gUHJvbsOzc3RpY28gcG9yIGRlc2NvbXBvc2ljacOzbiAodGVuZGVuY2lhIHggw61uZGljZSBlc3RhY2lvbmFsKQp0NCA8LSAzNzo0OApwcmVkX3RlbmRlbmNpYSA8LSBjb2VmKHRlbmRlbmNpYSlbMV0gKyBjb2VmKHRlbmRlbmNpYSlbMl0qdDQKcHJvbm9zdGljb19kZXNjb21wIDwtIHByZWRfdGVuZGVuY2lhICogaW5kaWNlc19lc3RhY2lvbmFsZXMKcHJvbm9zdGljb19kZXNjb21wCgojIDUuIFByb27Ds3N0aWNvIGNvbiByZWdyZXNpw7NuIGRlIHZhcmlhYmxlcyBmaWN0aWNpYXMgKGR1bW15KQptZXMgPC0gZmFjdG9yKHJlcChtb250aC5hYmIsIDMpLCBsZXZlbHMgPSBtb250aC5hYmIpCmRmX2R1bW15IDwtIGRhdGEuZnJhbWUodCA9IHQsIG1lcyA9IG1lcywgdmVudGFzID0gdmVudGFzKQptb2RlbG9fZHVtbXkgPC0gbG0odmVudGFzIH4gdCArIG1lcywgZGF0YSA9IGRmX2R1bW15KQpzdW1tYXJ5KG1vZGVsb19kdW1teSkKCm51ZXZvIDwtIGRhdGEuZnJhbWUodCA9IHQ0LCBtZXMgPSBmYWN0b3IobW9udGguYWJiLCBsZXZlbHMgPSBtb250aC5hYmIpKQpwcm9ub3N0aWNvX2R1bW15IDwtIHByZWRpY3QobW9kZWxvX2R1bW15LCBuZXdkYXRhID0gbnVldm8pCnByb25vc3RpY29fZHVtbXkKCiMgRXJyb3IgZGUgcHJvbsOzc3RpY28gcGFyYSBlbmVybyBkZWwgYcOxbyA0ICh2ZW50YSByZWFsID0gMjk1KQplcnJvcl9kZXNjb21wIDwtIDI5NSAtIHByb25vc3RpY29fZGVzY29tcFsxXQplcnJvcl9kdW1teSAgIDwtIDI5NSAtIHByb25vc3RpY29fZHVtbXlbMV0KZXJyb3JfZGVzY29tcAplcnJvcl9kdW1teQpgYGAKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkdyw6FmaWNhIGRlIGxhIHNlcmllIGRlIHRpZW1wbzwvc3Bhbj4KTGEgc2VyaWUgbXVlc3RyYSB1biBwYXRyw7NuIGVzdGFjaW9uYWwgbXV5IG1hcmNhZG8gcXVlIHNlIHJlcGl0ZSBjYWRhIDEyIG1lc2VzLCBjb21iaW5hZG8gY29uIHVuYSB0ZW5kZW5jaWEgbGlnZXJhbWVudGUgY3JlY2llbnRlIGHDsW8gY29uIGHDsW8gKGxhcyB2ZW50YXMgZGUgY2FkYSBtZXMgc29uIGNvbnNpc3RlbnRlbWVudGUgbWF5b3JlcyBxdWUgbGFzIGRlbCBtaXNtbyBtZXMgZGVsIGHDsW8gYW50ZXJpb3IpLgoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkFuw6FsaXNpcyBkZSBlc3RhY2lvbmFsaWRhZDwvc3Bhbj4KYGBge3J9CmxpYnJhcnkoa25pdHIpCgp0YWJsYV9lc3RhY2lvbmFsIDwtIGRhdGEuZnJhbWUoCiAgTWVzID0gbW9udGguYWJiLAogIEluZGljZV9Fc3RhY2lvbmFsID0gcm91bmQoYXMubnVtZXJpYyhpbmRpY2VzX2VzdGFjaW9uYWxlcyksIDMpCikKCmthYmxlKHRhYmxhX2VzdGFjaW9uYWwsIAogICAgICBjb2wubmFtZXMgPSBjKCJNZXMiLCAiw41uZGljZSBFc3RhY2lvbmFsIiksCiAgICAgIGNhcHRpb24gPSAiw41uZGljZXMgZXN0YWNpb25hbGVzIC0gVmludGFnZSBSZXN0YXVyYW50IiwKICAgICAgYWxpZ24gPSAiYyIpCmBgYAoKRW5lcm8sIGZlYnJlcm8geSBtYXJ6byB0aWVuZW4gdmVudGFzIH4zMC00NCUgcG9yIGVuY2ltYSBkZWwgcHJvbWVkaW8sIG1pZW50cmFzIHF1ZSBzZXB0aWVtYnJlIGNhZSB+MzclIHBvciBkZWJham8uIEVzdG8gdGllbmUgc2VudGlkbyBpbnR1aXRpdm86IENhcHRpdmEgSXNsYW5kLCBGbG9yaWRhLCBlcyB1biBkZXN0aW5vIHR1csOtc3RpY28gZGUgdGVtcG9yYWRhIGludmVybmFsIChsb3MgInNub3diaXJkcyIgbGxlZ2FuIGRlIGRpY2llbWJyZSBhIG1hcnpvKSwgbWllbnRyYXMgcXVlIHNlcHRpZW1icmUgY29pbmNpZGUgY29uIGVsIHZlcmFubywgZWwgY2Fsb3IgeSBsYSB0ZW1wb3JhZGEgZGUgaHVyYWNhbmVzLCBjdWFuZG8gZWwgdHVyaXNtbyBjYWUgZHLDoXN0aWNhbWVudGUuCgojIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+U2VyaWUgZGVzZXN0YWNpb25hbGl6YWRhPC9zcGFuPgpBbCByZW1vdmVyIGVsIGVmZWN0byBlc3RhY2lvbmFsLCBzZSBvYnNlcnZhIHVuYSB0ZW5kZW5jaWEgbGluZWFsIGNyZWNpZW50ZSBjbGFyYTogbGFzIHZlbnRhcyBkZXNlc3RhY2lvbmFsaXphZGFzIHN1YmVuIGRlIH4xNjggKG1lcyAxKSBhIH4yMDMgKG1lcyAzNiksIGNvbiB1bmEgcGVuZGllbnRlIGVzdGltYWRhIGRlIH4xLjAyIHVuaWRhZGVzIHBvciBtZXMsIGVzIGRlY2lyLCB1biBjcmVjaW1pZW50byBzdWJ5YWNlbnRlIGNvbnN0YW50ZSBkZWwgbmVnb2Npbywgc2luIGltcG9ydGFyIGxhIHRlbXBvcmFkYS4KCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5UYWJsYSAyOiBQcm9uw7NzdGljbyBwb3IgZGVzY29tcG9zaWNpw7NuIChBw7FvIDQpPC9zcGFuPgpgYGB7cn0KdGFibGFfZGVzY29tcCA8LSBkYXRhLmZyYW1lKAogIE1lcyA9IG1vbnRoLmFiYiwKICBQcm9ub3N0aWNvID0gcm91bmQoYXMubnVtZXJpYyhwcm9ub3N0aWNvX2Rlc2NvbXApLCAyKQopCgprYWJsZSh0YWJsYV9kZXNjb21wLAogICAgICBjb2wubmFtZXMgPSBjKCJNZXMiLCAiUHJvbsOzc3RpY28gKCQgbWlsZXMpIiksCiAgICAgIGNhcHRpb24gPSAiUHJvbsOzc3RpY28gQcOxbyA0IC0gTcOpdG9kbyBkZSBEZXNjb21wb3NpY2nDs24iLAogICAgICBhbGlnbiA9ICJjIikKYGBgCgojIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+UHJvbsOzc3RpY28gcG9yIHJlZ3Jlc2nDs24gY29uIHZhcmlhYmxlcyBkdW1teSAoQcOxbyA0KTwvc3Bhbj4KYGBge3J9CnRhYmxhX2R1bW15IDwtIGRhdGEuZnJhbWUoCiAgTWVzID0gbW9udGguYWJiLAogIFByb25vc3RpY28gPSByb3VuZChhcy5udW1lcmljKHByb25vc3RpY29fZHVtbXkpLCAyKQopCgprYWJsZSh0YWJsYV9kdW1teSwKICAgICAgY29sLm5hbWVzID0gYygiTWVzIiwgIlByb27Ds3N0aWNvICgkIG1pbGVzKSIpLAogICAgICBjYXB0aW9uID0gIlByb27Ds3N0aWNvIEHDsW8gNCAtIFJlZ3Jlc2nDs24gY29uIFZhcmlhYmxlcyBGaWN0aWNpYXMiLAogICAgICBhbGlnbiA9ICJjIikKYGBgCgojIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+Q29tcGFyYWNpw7NuIGRlIGFtYm9zIG3DqXRvZG9zPC9zcGFuPgpgYGB7cn0KdGFibGFfY29tcGFyYWNpb24gPC0gZGF0YS5mcmFtZSgKICBNZXMgPSBtb250aC5hYmIsCiAgRGVzY29tcG9zaWNpb24gPSByb3VuZChhcy5udW1lcmljKHByb25vc3RpY29fZGVzY29tcCksIDIpLAogIFJlZ3Jlc2lvbl9EdW1teSA9IHJvdW5kKGFzLm51bWVyaWMocHJvbm9zdGljb19kdW1teSksIDIpCikKCmthYmxlKHRhYmxhX2NvbXBhcmFjaW9uLAogICAgICBjb2wubmFtZXMgPSBjKCJNZXMiLCAiRGVzY29tcG9zaWNpw7NuICgkIG1pbGVzKSIsICJSZWdyZXNpw7NuIER1bW15ICgkIG1pbGVzKSIpLAogICAgICBjYXB0aW9uID0gIkNvbXBhcmFjacOzbiBkZSBwcm9uw7NzdGljb3MgLSBBw7FvIDQiLAogICAgICBhbGlnbiA9ICJjIikKYGBgCgojIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+RXJyb3IgZGUgcHJvbsOzc3RpY28gKEVuZXJvIEHDsW8gNCk8L3NwYW4+CmBgYHtyfQp2ZW50YV9yZWFsX2VuZTQgPC0gMjk1Cgp0YWJsYV9lcnJvciA8LSBkYXRhLmZyYW1lKAogIE1ldG9kbyA9IGMoIkRlc2NvbXBvc2ljacOzbiIsICJSZWdyZXNpw7NuIGNvbiBEdW1teSIpLAogIFByb25vc3RpY28gPSBjKHJvdW5kKHByb25vc3RpY29fZGVzY29tcFsxXSwgMiksIHJvdW5kKHByb25vc3RpY29fZHVtbXlbMV0sIDIpKSwKICBSZWFsID0gdmVudGFfcmVhbF9lbmU0LAogIEVycm9yID0gYyhyb3VuZCh2ZW50YV9yZWFsX2VuZTQgLSBwcm9ub3N0aWNvX2Rlc2NvbXBbMV0sIDIpLCAKICAgICAgICAgICAgcm91bmQodmVudGFfcmVhbF9lbmU0IC0gcHJvbm9zdGljb19kdW1teVsxXSwgMikpLAogIEVycm9yX1BvcmNlbnR1YWwgPSBjKHBhc3RlMChyb3VuZCgodmVudGFfcmVhbF9lbmU0IC0gcHJvbm9zdGljb19kZXNjb21wWzFdKS92ZW50YV9yZWFsX2VuZTQqMTAwLCAyKSwgIiUiKSwKICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAocm91bmQoKHZlbnRhX3JlYWxfZW5lNCAtIHByb25vc3RpY29fZHVtbXlbMV0pL3ZlbnRhX3JlYWxfZW5lNCoxMDAsIDIpLCAiJSIpKQopCgprYWJsZSh0YWJsYV9lcnJvciwKICAgICAgY29sLm5hbWVzID0gYygiTcOpdG9kbyIsICJQcm9uw7NzdGljbyIsICJSZWFsIiwgIkVycm9yIiwgIkVycm9yICUiKSwKICAgICAgY2FwdGlvbiA9ICJFcnJvciBkZSBwcm9uw7NzdGljbyAtIEVuZXJvIEHDsW8gNCIsCiAgICAgIGFsaWduID0gImMiKQpgYGAKCkVsIG1vZGVsbyBkZSByZWdyZXNpw7NuIGNvbiBkdW1taWVzIHRpZW5lIHVuIFLCsiBkZSAwLjk5NCwgbG8gcXVlIGluZGljYSB1biBhanVzdGUgZXhjZWxlbnRlLiBBbWJvcyBtw6l0b2RvcyBkYW4gcmVzdWx0YWRvcyBtdXkgc2ltaWxhcmVzIHkgY29uc2lzdGVudGVzIGVudHJlIHPDrS4KCkVycm9yIGRlIHByb27Ds3N0aWNvIChlbmVybyBkZWwgQcOxbyA0LCB2ZW50YSByZWFsID0gJDI5NSwwMDApCgpNw6l0b2RvIGRlIGRlc2NvbXBvc2ljacOzbjogcHJvbsOzc3RpY28gPSAkMjk5LDAyMCDihpIgZXJyb3IgPSDigJMkNCwwMjAgKOKAkzEuNCUpCk3DqXRvZG8gZGUgcmVncmVzacOzbiBjb24gZHVtbWllczogcHJvbsOzc3RpY28gPSAkMjg2LDc1MCDihpIgZXJyb3IgPSArJDgsMjUwICgrMi44JSkKCkFtYm9zIGVycm9yZXMgc29uIHJlbGF0aXZhbWVudGUgcGVxdWXDsW9zIChtZW5vcmVzIGFsIDMlKSwgcG9yIGxvIHF1ZSBubyBkZWJlcsOtYW4gZ2VuZXJhciBtYXlvciBjb25mdXNpw7NuIHBhcmEgS2FyZW4sIHNvbiBkaWZlcmVuY2lhcyBub3JtYWxlcyBkZW50cm8gZGVsIG1hcmdlbiBlc3BlcmFkbyBkZSB1biBidWVuIG1vZGVsbyBkZSBwcm9uw7NzdGljby4KCgojIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5FamVyY2ljaW8gNDogQ2FybHNvbiBEZXBhcnRtZW50IHN0b3JlPC9zcGFuPgpgYGB7cn0KbGlicmFyeShrbml0cikKCiMgLS0tIERhdG9zIGhpc3TDs3JpY29zIHByZS1odXJhY8OhbiAoMzYgbWVzZXM6IFNlcCBBw7FvMSAtIEFnbyBBw7FvNCkgLS0tCmNhcmxzb24gPC0gYygxLjcxLDEuOTAsMi43NCw0LjIwLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgQcOxbzEgU2VwLURpYwogICAgICAgICAgICAgMS40NSwxLjgwLDIuMDMsMS45OSwyLjMyLDIuMjAsMi4xMywyLjQzLDEuOTAsMi4xMywyLjU2LDQuMTYsICMgQcOxbzIKICAgICAgICAgICAgIDIuMzEsMS44OSwyLjAyLDIuMjMsMi4zOSwyLjE0LDIuMjcsMi4yMSwxLjg5LDIuMjksMi44Myw0LjA0LCAjIEHDsW8zCiAgICAgICAgICAgICAyLjMxLDEuOTksMi40MiwyLjQ1LDIuNTcsMi40MiwyLjQwLDIuNTApICAgICAgICAgICAgICAgICAgICAjIEHDsW80IEVuZS1BZ28KCmNvbmRhZG8gPC0gYyg1NS44MCw1Ni40MCw3MS40MCwxMTcuNjAsCiAgICAgICAgICAgICA0Ni44MCw0OC4wMCw2MC4wMCw1Ny42MCw2MS44MCw1OC4yMCw1Ni40MCw2My4wMCw1Ny42MCw1My40MCw3MS40MCwxMTQuMDAsCiAgICAgICAgICAgICA0Ni44MCw0OC42MCw1OS40MCw1OC4yMCw2MC42MCw1NS4yMCw1MS4wMCw1OC44MCw0OS44MCw1NC42MCw2NS40MCwxMDIuMDAsCiAgICAgICAgICAgICA0My44MCw0NS42MCw1Ny42MCw1My40MCw1Ni40MCw1Mi44MCw1NC4wMCw2MC42MCkKCiMgVmVudGFzIHJlYWxlcyBkZWwgY29uZGFkbyBkdXJhbnRlIGVsIGNpZXJyZSAoU2VwLURpYyBBw7FvNCkKY29uZGFkb19yZWFsX2NpZXJyZSA8LSBjKDQ3LjQwLCA1NC42MCwgNjcuODAsIDEwMC4yMCkKCnRzX2Nhcmxzb24gPC0gdHMoY2FybHNvbiwgc3RhcnQgPSBjKDEsOSksIGZyZXF1ZW5jeSA9IDEyKQp0c19jb25kYWRvIDwtIHRzKGNvbmRhZG8sIHN0YXJ0ID0gYygxLDkpLCBmcmVxdWVuY3kgPSAxMikKCgojIC0tLSBEZXNjb21wb3NpY2nDs24gbXVsdGlwbGljYXRpdmEgLS0tCmRlc2NvbXBfY2FybHNvbiA8LSBkZWNvbXBvc2UodHNfY2FybHNvbiwgdHlwZSA9ICJtdWx0aXBsaWNhdGl2ZSIpCmRlc2NvbXBfY29uZGFkbyA8LSBkZWNvbXBvc2UodHNfY29uZGFkbywgdHlwZSA9ICJtdWx0aXBsaWNhdGl2ZSIpCgppbmRpY2VzX2Nhcmxzb24gPC0gZGVzY29tcF9jYXJsc29uJGZpZ3VyZQppbmRpY2VzX2NvbmRhZG8gPC0gZGVzY29tcF9jb25kYWRvJGZpZ3VyZQpuYW1lcyhpbmRpY2VzX2Nhcmxzb24pIDwtIG1vbnRoLmFiYltjKDk6MTIsMTo4KV0KbmFtZXMoaW5kaWNlc19jb25kYWRvKSA8LSBtb250aC5hYmJbYyg5OjEyLDE6OCldCgojIC0tLSBEZXNlc3RhY2lvbmFsaXphciB5IGFqdXN0YXIgdGVuZGVuY2lhIC0tLQp0IDwtIDE6MzYKZGVzZWFzb25fY2FybHNvbiA8LSB0c19jYXJsc29uIC8gcmVwKGRlc2NvbXBfY2FybHNvbiRmaWd1cmUsIDMpCmRlc2Vhc29uX2NvbmRhZG8gIDwtIHRzX2NvbmRhZG8gLyByZXAoZGVzY29tcF9jb25kYWRvJGZpZ3VyZSwgMykKCnRlbmRfY2FybHNvbiA8LSBsbShhcy5udW1lcmljKGRlc2Vhc29uX2Nhcmxzb24pIH4gdCkKdGVuZF9jb25kYWRvIDwtIGxtKGFzLm51bWVyaWMoZGVzZWFzb25fY29uZGFkbykgfiB0KQoKIyAtLS0gUHJvbsOzc3RpY28gU2VwLURpYyBBw7FvNCAobWVzZXMgMzctNDApIFNJTiBodXJhY8OhbiAtLS0KdF9mIDwtIDM3OjQwCmluZGljZXNfZiA8LSBkZXNjb21wX2Nhcmxzb24kZmlndXJlWzk6MTJdICAjIFNlcCwgT2N0LCBOb3YsIERpYwppbmRpY2VzX2ZfY29uZGFkbyA8LSBkZXNjb21wX2NvbmRhZG8kZmlndXJlWzk6MTJdCgpwcmVkX2Nhcmxzb24gPC0gKGNvZWYodGVuZF9jYXJsc29uKVsxXSArIGNvZWYodGVuZF9jYXJsc29uKVsyXSp0X2YpICogaW5kaWNlc19mCnByZWRfY29uZGFkbyA8LSAoY29lZih0ZW5kX2NvbmRhZG8pWzFdICsgY29lZih0ZW5kX2NvbmRhZG8pWzJdKnRfZikgKiBpbmRpY2VzX2ZfY29uZGFkbwoKcHJlZF9jYXJsc29uCnByZWRfY29uZGFkbwoKCiMgLS0tIDEuIFZlbnRhcyBlc3RpbWFkYXMgZGUgQ2FybHNvbiBzaW4gaHVyYWPDoW4gKHDDqXJkaWRhKSAtLS0KcGVyZGlkYV9jYXJsc29uIDwtIHByZWRfY2FybHNvbiAgIyB2ZW50YXMgcmVhbGVzID0gMCAodGllbmRhIGNlcnJhZGEpCnN1bShwZXJkaWRhX2Nhcmxzb24pCgojIC0tLSAyLiBWZW50YXMgZXN0aW1hZGFzIGRlbCBjb25kYWRvIHNpbiBodXJhY8OhbiAtLS0Kc3VtKHByZWRfY29uZGFkbykKCiMgLS0tIDMuIENvbXBhcmFjacOzbiBjb25kYWRvOiByZWFsIHZzLiBwcm9uw7NzdGljbyAoZXhjZXNvIHBvciBodXJhY8OhbikgLS0tCmV4Y2VzbyA8LSBjb25kYWRvX3JlYWxfY2llcnJlIC0gcHJlZF9jb25kYWRvCnRhYmxhX2V4Y2VzbyA8LSBkYXRhLmZyYW1lKAogIE1lcyA9IGMoIlNlcCIsIk9jdCIsIk5vdiIsIkRpYyIpLAogIFByb25vc3RpY29fc2luX2h1cmFjYW4gPSByb3VuZChhcy5udW1lcmljKHByZWRfY29uZGFkbyksIDIpLAogIFJlYWwgPSBjb25kYWRvX3JlYWxfY2llcnJlLAogIEV4Y2VzbyA9IHJvdW5kKGV4Y2VzbywgMikKKQp0YWJsYV9leGNlc28Kc3VtKGV4Y2VzbykKYGBgCgoKCmBgYHtyfQprYWJsZShkYXRhLmZyYW1lKE1lcyA9IGMoIlNlcCIsIk9jdCIsIk5vdiIsIkRpYyIpLAogICAgICAgICAgICAgICAgICBQcm9ub3N0aWNvID0gcm91bmQoYXMubnVtZXJpYyhwcmVkX2Nhcmxzb24pLCAyKSksCiAgICAgIGNvbC5uYW1lcyA9IGMoIk1lcyIsICJWZW50YXMgZXN0aW1hZGFzIHNpbiBodXJhY8OhbiAoJCBtaWxsb25lcykiKSwKICAgICAgY2FwdGlvbiA9ICJQcm9uw7NzdGljbyBkZSB2ZW50YXMgZGUgQ2FybHNvbiAoc2luIGh1cmFjw6FuKSAtIFNlcCBhIERpYyBBw7FvIDQiLAogICAgICBhbGlnbiA9ICJjIikKCgprYWJsZSh0YWJsYV9leGNlc28sCiAgICAgIGNvbC5uYW1lcyA9IGMoIk1lcyIsICJQcm9uw7NzdGljbyBzaW4gaHVyYWPDoW4iLCAiUmVhbCIsICJFeGNlc28iKSwKICAgICAgY2FwdGlvbiA9ICJDb21wYXJhY2nDs24gZGUgdmVudGFzIHJlYWxlcyB2cy4gcHJvbm9zdGljYWRhcyAtIENvbmRhZG8iLAogICAgICBhbGlnbiA9ICJjIikKYGBgCgoKCgoKCgoKCg==