INSTALACION DE LIBRERIAS

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

EJERCICIO 1 “Ventas Semanales”

Instrucciones: Modela la sig. Serie de tiempo y elige la mejor opcion, pronosticando las siguientes 6 semanas

semana <- c(1:12)
valor <- c(17, 21, 19, 23, 18, 16, 20, 18, 22, 20, 15, 22)
dataframe1 <- data.frame(semana, valor)
dataframe1
##    semana valor
## 1       1    17
## 2       2    21
## 3       3    19
## 4       4    23
## 5       5    18
## 6       6    16
## 7       7    20
## 8       8    18
## 9       9    22
## 10     10    20
## 11     11    15
## 12     12    22
tsUNO <- ts(valor, c(2025,1), frequency=52)
# Convertimos a serie de tiempo. Se deja frequency = 52 porque es una serie SEMANAL
# (52 semanas por año). Con solo 12 datos no alcanza a completar un ciclo anual,
# por lo que se respeta la naturaleza semanal de los datos.

plot(tsUNO, main = "Serie de tiempo semanal", ylab = "Valor", xlab = "Semana", type = "o", col="blue")

#MODELO 1. NAIVE
# El método naive pronostica que el valor futuro será igual al **último valor observado**.
naive1 <- naive(tsUNO, h = 6)
summary(naive1)
## 
## Forecast method: Naive method
## 
## Model Information:
## Call: naive(y = tsUNO, 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
plot(naive1)

#MODELO 2. Promedio Movil (k=3)

#El promedio móvil simple pronostica el siguiente valor como el promedio de las últimas 3 observaciones. Para proyectar varias semanas hacia adelante, el pronóstico se calcula de forma **recursiva**: cada nuevo valor pronosticado se incorpora a la ventana para calcular el siguiente.

# Suavizado / promedio móvil de orden 3 sobre los datos históricos (referencia)
promedio_movil<- ma(tsUNO, order = 3)
promedio_movil
## Time Series:
## Start = c(2025, 1) 
## End = c(2025, 12) 
## Frequency = 52 
##  [1] NA 19 21 20 19 18 18 20 20 19 19 NA
# Función para pronosticar de forma recursiva con promedio móvil simple k=3
promedio_movil_forecast <- function(serie, k = 3, h = 6){
  serie <- as.numeric(serie)
  pronosticos <- numeric(h)
  for (i in 1:h){
    ultimos <- tail(serie, k)
    siguiente <- mean(ultimos)
    pronosticos[i] <- siguiente
    serie <- c(serie, siguiente)
  }
  return(pronosticos)
}

pronostico_pm3 <- promedio_movil_forecast(tsUNO, k = 3, h = 6)
pronostico_pm3
## [1] 19.00000 18.66667 19.88889 19.18519 19.24691 19.44033

ruido sin tendencia.

#MODELO 3. Promedio Movil Ponderado (k=3) Pesos: 3/6, 2/6, 1/6
#Se le da más peso a las observaciones más recientes: 3/6 a la última semana, 2/6 a la penúltima y 1/6 a la antepenúltima.
pesos <- c(3/6, 2/6, 1/6)  # del más reciente al más antiguo

pmp_forecast <- function(serie, pesos = c(3/6, 2/6, 1/6), h = 6){
  serie <- as.numeric(serie)
  k <- length(pesos)
  pronosticos <- numeric(h)
  for (i in 1:h){
    ultimos <- tail(serie, k)
    ultimos_ordenado <- rev(ultimos)  # más reciente primero, para alinear con "pesos"
    siguiente <- sum(ultimos_ordenado * pesos)
    pronosticos[i] <- siguiente
    serie <- c(serie, siguiente)
  }
  return(pronosticos)
}

pronostico_pmp <- pmp_forecast(tsUNO, pesos = pesos, h = 6)
pronostico_pmp
## [1] 19.33333 19.50000 19.86111 19.65278 19.69676 19.70949
# Muestra la tabla de datos 
### Tabla de datos (histórico + ambos promedios móviles como referencia)
tabla_referencia <- data.frame(
  semana = semana,
  valor = valor,
  MA3 = as.numeric(promedio_movil)
)
tabla_referencia
##    semana valor MA3
## 1       1    17  NA
## 2       2    21  19
## 3       3    19  21
## 4       4    23  20
## 5       5    18  19
## 6       6    16  18
## 7       7    20  18
## 8       8    18  20
## 9       9    22  20
## 10     10    20  19
## 11     11    15  19
## 12     12    22  NA
#MODELO 4. Suavisador exponencial (k=3) con letra alpha =0.2 (la del pescadito)
# ESE HAY QUE BUSCARLO EN EL PDF QUE NOS COMPARTIRA EL PROFE
## El suavizado exponencial simple (SES) da más peso a las observaciones recientes de forma decreciente y exponencial, controlado por el parámetro alpha ("la letra del pescadito" ⍺). Aquí se fija alpha = 0.2 (en vez de dejar que R lo optimice).

ses1 <- ses(tsUNO, alpha = 0.2, h = 6, initial = "simple")
summary(ses1)
## 
## Forecast method: Simple exponential smoothing
## 
## Model Information:
## Simple exponential smoothing 
## 
## Call:
## ses(y = tsUNO, h = 6, initial = "simple", alpha = 0.2)
## 
##   Smoothing parameters:
##     alpha = 0.2 
## 
##   Initial states:
##     l = 17 
## 
##   sigma:  2.8694
## Error measures:
##                    ME     RMSE      MAE      MPE     MAPE MASE       ACF1
## Training set 0.910398 2.869444 2.379977 2.988357 12.28556  NaN -0.2602375
## 
## Forecasts:
##          Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## 2025.231       19.18496 15.50761 22.86230 13.56095 24.80896
## 2025.250       19.18496 15.43479 22.93512 13.44957 24.92034
## 2025.269       19.18496 15.36335 23.00656 13.34031 25.02960
## 2025.288       19.18496 15.29322 23.07669 13.23306 25.13685
## 2025.308       19.18496 15.22434 23.14557 13.12771 25.24220
## 2025.327       19.18496 15.15663 23.21328 13.02416 25.34575
plot(ses1)

#MODELO 5. ARIMA
# Se deja que `auto.arima()` seleccione automáticamente el mejor modelo ARIMA(p,d,q) con base en el criterio de información (AICc).

arima1 <- auto.arima(tsUNO)
summary(arima1)
## Series: tsUNO 
## 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 -3.552714e-15 2.419539 2.083333 -1.662344 11.18529  NaN -0.363879
pronostico_arima <- forecast(arima1, level = c(95), h = 6)
pronostico_arima
##          Point Forecast    Lo 95    Hi 95
## 2025.231          19.25 14.29692 24.20308
## 2025.250          19.25 14.29692 24.20308
## 2025.269          19.25 14.29692 24.20308
## 2025.288          19.25 14.29692 24.20308
## 2025.308          19.25 14.29692 24.20308
## 2025.327          19.25 14.29692 24.20308
plot(pronostico_arima)

COMPARACIÓN DE MODELOS Y SELECCIÓN DEL MEJOR

Para comparar los 5 modelos de forma justa, se calcula el error (RMSE y MAE) del ajuste dentro de la muestra (in-sample) de cada uno: qué tanto se parecen los valores ajustados/pronosticados un paso adelante a los valores reales observados.

# Errores in-sample para Naive, SES y ARIMA (usando accuracy())
acc_naive <- accuracy(naive1)
acc_ses   <- accuracy(ses1)
acc_arima <- accuracy(arima1)

# Errores in-sample para "PM" y "PMP"
# (calculados manualmente ya que son funciones hechas a mano y no objetos "forecast")
valor_num <- as.numeric(tsUNO)
n <- length(valor_num)

# --- Ajuste in-sample "PM": predice semana t usando t-3, t-2, t-1 ---
ma_fitted <- sapply(4:n, function(t) mean(valor_num[(t-3):(t-1)]))
ma_actual <- valor_num[4:n]
ma_rmse <- sqrt(mean((ma_actual - ma_fitted)^2))
ma_mae  <- mean(abs(ma_actual - ma_fitted))

# --- Ajuste in-sample "PMP" con pesos 3/6, 2/6, 1/6 ---
wma_fitted <- sapply(4:n, function(t){
  ventana <- rev(valor_num[(t-3):(t-1)])  # más reciente primero
  sum(ventana * pesos)
})
wma_actual <- valor_num[4:n]
wma_rmse <- sqrt(mean((wma_actual - wma_fitted)^2))
wma_mae  <- mean(abs(wma_actual - wma_fitted))

tabla_comparacion <- data.frame(
  Modelo = c("Naive", "Promedio Movil (k=3)", "Promedio Movil Ponderado (k=3)",
             "Suavizado Exponencial (alpha=0.2)", "ARIMA"),
  RMSE = c(acc_naive[1,"RMSE"], ma_rmse, wma_rmse, acc_ses[1,"RMSE"], acc_arima[1,"RMSE"]),
  MAE  = c(acc_naive[1,"MAE"],  ma_mae,  wma_mae,  acc_ses[1,"MAE"],  acc_arima[1,"MAE"])
)

tabla_comparacion[order(tabla_comparacion$RMSE), ]
##                              Modelo     RMSE      MAE
## 5                             ARIMA 2.419539 2.083333
## 4 Suavizado Exponencial (alpha=0.2) 2.869444 2.379977
## 2              Promedio Movil (k=3) 3.197221 2.666667
## 3    Promedio Movil Ponderado (k=3) 3.389800 2.981481
## 1                             Naive 4.033947 3.727273
#PRONÓSTICO FINAL - MODELO GANADOR

mejor_modelo <- tabla_comparacion[order(tabla_comparacion$RMSE), ][1, "Modelo"]
cat("Modelo elegido según RMSE/MAE mas bajos:", mejor_modelo, "\n")
## Modelo elegido según RMSE/MAE mas bajos: ARIMA
# Pronóstico final con el modelo ARIMA 
pronostico_final <- forecast(arima1, h = 6)
tabla_pronostico_final <- data.frame(
  Semana = 13:18,
  Pronostico = as.numeric(pronostico_final$mean),
  Limite_Inferior_95 = as.numeric(pronostico_final$lower[,2]),
  Limite_Superior_95 = as.numeric(pronostico_final$upper[,2])
)
tabla_pronostico_final
##   Semana Pronostico Limite_Inferior_95 Limite_Superior_95
## 1     13      19.25           14.29692           24.20308
## 2     14      19.25           14.29692           24.20308
## 3     15      19.25           14.29692           24.20308
## 4     16      19.25           14.29692           24.20308
## 5     17      19.25           14.29692           24.20308
## 6     18      19.25           14.29692           24.20308
plot(pronostico_final, main = "Pronóstico de las siguientes 6 semanas")

## CONCLUSIÓN

Al comparar los cinco modelos con base en el error dentro de muestra (RMSE y MAE), se observa que el modelo Naive presenta el PEOR DESEMPEÑO ya que solo utiliza el último dato observado (22) como pronóstico constante para las 6 semanas, ignorando el resto de la información histórica y la variabilidad natural de la serie.

El modelo ARIMA, seleccionado automáticamente con auto.arima(), es el que mejor captura el patrón de la serie (que se comporta como datos que oscilan alrededor de una media, sin tendencia ni estacionalidad detectable con solo 12 observaciones), obteniendo el menor RMSE y MAE de los cinco modelos evaluados. Por esta razón, ARIMA se elige como el modelo final para pronosticar las siguientes 6 semanas, con un pronóstico que se mantiene estable alrededor del nivel promedio histórico de la serie (~19), acompañado de sus intervalos de confianza al 95%.

En términos generales, la conclusión práctica es que, ante una serie con pocos datos y sin una tendencia o estacionalidad marcada, los modelos que promedian información histórica (ARIMA, suavizado exponencial, promedios móviles) son preferibles al método Naive, y ARIMA ofrece el mejor balance entre ajuste a los datos y capacidad de generalización para el mejor pronóstico posible.

LS0tDQp0aXRsZTogIlRFTUEyX1RpbWVzZXJpZXMrR0VGQSINCmF1dGhvcjogIkx1Y2Vyb19TYWxhemFyX0EwMDgzNDg5MyINCmRhdGU6ICIyMDI2LTA4LTE5Ig0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFDQogICAgdGhlbWU6IHVuaXRlZA0KLS0tDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogZ3JlZW47Ij5JTlNUQUxBQ0lPTiBERSBMSUJSRVJJQVM8L3NwYW4+DQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI2luc3RhbGwucGFja2FnZXMoImZvcmVjYXN0IikNCmxpYnJhcnkoZm9yZWNhc3QpDQpgYGANCg0KDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6IHllbGxvdzsiPkVKRVJDSUNJTyAxICJWZW50YXMgU2VtYW5hbGVzIjwvc3Bhbj4NCkluc3RydWNjaW9uZXM6IE1vZGVsYSBsYSBzaWcuIFNlcmllIGRlIHRpZW1wbyB5IGVsaWdlIGxhIG1lam9yIG9wY2lvbiwgcHJvbm9zdGljYW5kbyBsYXMgc2lndWllbnRlcyA2IHNlbWFuYXMNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnNlbWFuYSA8LSBjKDE6MTIpDQp2YWxvciA8LSBjKDE3LCAyMSwgMTksIDIzLCAxOCwgMTYsIDIwLCAxOCwgMjIsIDIwLCAxNSwgMjIpDQpkYXRhZnJhbWUxIDwtIGRhdGEuZnJhbWUoc2VtYW5hLCB2YWxvcikNCmRhdGFmcmFtZTENCg0KdHNVTk8gPC0gdHModmFsb3IsIGMoMjAyNSwxKSwgZnJlcXVlbmN5PTUyKQ0KIyBDb252ZXJ0aW1vcyBhIHNlcmllIGRlIHRpZW1wby4gU2UgZGVqYSBmcmVxdWVuY3kgPSA1MiBwb3JxdWUgZXMgdW5hIHNlcmllIFNFTUFOQUwNCiMgKDUyIHNlbWFuYXMgcG9yIGHDsW8pLiBDb24gc29sbyAxMiBkYXRvcyBubyBhbGNhbnphIGEgY29tcGxldGFyIHVuIGNpY2xvIGFudWFsLA0KIyBwb3IgbG8gcXVlIHNlIHJlc3BldGEgbGEgbmF0dXJhbGV6YSBzZW1hbmFsIGRlIGxvcyBkYXRvcy4NCg0KcGxvdCh0c1VOTywgbWFpbiA9ICJTZXJpZSBkZSB0aWVtcG8gc2VtYW5hbCIsIHlsYWIgPSAiVmFsb3IiLCB4bGFiID0gIlNlbWFuYSIsIHR5cGUgPSAibyIsIGNvbD0iYmx1ZSIpDQoNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI01PREVMTyAxLiBOQUlWRQ0KIyBFbCBtw6l0b2RvIG5haXZlIHByb25vc3RpY2EgcXVlIGVsIHZhbG9yIGZ1dHVybyBzZXLDoSBpZ3VhbCBhbCAqKsO6bHRpbW8gdmFsb3Igb2JzZXJ2YWRvKiouDQpuYWl2ZTEgPC0gbmFpdmUodHNVTk8sIGggPSA2KQ0Kc3VtbWFyeShuYWl2ZTEpDQpwbG90KG5haXZlMSkNCg0KYGBgDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNNT0RFTE8gMi4gUHJvbWVkaW8gTW92aWwgKGs9MykNCg0KI0VsIHByb21lZGlvIG3Ds3ZpbCBzaW1wbGUgcHJvbm9zdGljYSBlbCBzaWd1aWVudGUgdmFsb3IgY29tbyBlbCBwcm9tZWRpbyBkZSBsYXMgw7psdGltYXMgMyBvYnNlcnZhY2lvbmVzLiBQYXJhIHByb3llY3RhciB2YXJpYXMgc2VtYW5hcyBoYWNpYSBhZGVsYW50ZSwgZWwgcHJvbsOzc3RpY28gc2UgY2FsY3VsYSBkZSBmb3JtYSAqKnJlY3Vyc2l2YSoqOiBjYWRhIG51ZXZvIHZhbG9yIHByb25vc3RpY2FkbyBzZSBpbmNvcnBvcmEgYSBsYSB2ZW50YW5hIHBhcmEgY2FsY3VsYXIgZWwgc2lndWllbnRlLg0KDQojIFN1YXZpemFkbyAvIHByb21lZGlvIG3Ds3ZpbCBkZSBvcmRlbiAzIHNvYnJlIGxvcyBkYXRvcyBoaXN0w7NyaWNvcyAocmVmZXJlbmNpYSkNCnByb21lZGlvX21vdmlsPC0gbWEodHNVTk8sIG9yZGVyID0gMykNCnByb21lZGlvX21vdmlsDQoNCiMgRnVuY2nDs24gcGFyYSBwcm9ub3N0aWNhciBkZSBmb3JtYSByZWN1cnNpdmEgY29uIHByb21lZGlvIG3Ds3ZpbCBzaW1wbGUgaz0zDQpwcm9tZWRpb19tb3ZpbF9mb3JlY2FzdCA8LSBmdW5jdGlvbihzZXJpZSwgayA9IDMsIGggPSA2KXsNCiAgc2VyaWUgPC0gYXMubnVtZXJpYyhzZXJpZSkNCiAgcHJvbm9zdGljb3MgPC0gbnVtZXJpYyhoKQ0KICBmb3IgKGkgaW4gMTpoKXsNCiAgICB1bHRpbW9zIDwtIHRhaWwoc2VyaWUsIGspDQogICAgc2lndWllbnRlIDwtIG1lYW4odWx0aW1vcykNCiAgICBwcm9ub3N0aWNvc1tpXSA8LSBzaWd1aWVudGUNCiAgICBzZXJpZSA8LSBjKHNlcmllLCBzaWd1aWVudGUpDQogIH0NCiAgcmV0dXJuKHByb25vc3RpY29zKQ0KfQ0KDQpwcm9ub3N0aWNvX3BtMyA8LSBwcm9tZWRpb19tb3ZpbF9mb3JlY2FzdCh0c1VOTywgayA9IDMsIGggPSA2KQ0KcHJvbm9zdGljb19wbTMNCg0KYGBgDQoNCg0KcnVpZG8gc2luIHRlbmRlbmNpYS4NCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI01PREVMTyAzLiBQcm9tZWRpbyBNb3ZpbCBQb25kZXJhZG8gKGs9MykgUGVzb3M6IDMvNiwgMi82LCAxLzYNCiNTZSBsZSBkYSBtw6FzIHBlc28gYSBsYXMgb2JzZXJ2YWNpb25lcyBtw6FzIHJlY2llbnRlczogMy82IGEgbGEgw7psdGltYSBzZW1hbmEsIDIvNiBhIGxhIHBlbsO6bHRpbWEgeSAxLzYgYSBsYSBhbnRlcGVuw7psdGltYS4NCnBlc29zIDwtIGMoMy82LCAyLzYsIDEvNikgICMgZGVsIG3DoXMgcmVjaWVudGUgYWwgbcOhcyBhbnRpZ3VvDQoNCnBtcF9mb3JlY2FzdCA8LSBmdW5jdGlvbihzZXJpZSwgcGVzb3MgPSBjKDMvNiwgMi82LCAxLzYpLCBoID0gNil7DQogIHNlcmllIDwtIGFzLm51bWVyaWMoc2VyaWUpDQogIGsgPC0gbGVuZ3RoKHBlc29zKQ0KICBwcm9ub3N0aWNvcyA8LSBudW1lcmljKGgpDQogIGZvciAoaSBpbiAxOmgpew0KICAgIHVsdGltb3MgPC0gdGFpbChzZXJpZSwgaykNCiAgICB1bHRpbW9zX29yZGVuYWRvIDwtIHJldih1bHRpbW9zKSAgIyBtw6FzIHJlY2llbnRlIHByaW1lcm8sIHBhcmEgYWxpbmVhciBjb24gInBlc29zIg0KICAgIHNpZ3VpZW50ZSA8LSBzdW0odWx0aW1vc19vcmRlbmFkbyAqIHBlc29zKQ0KICAgIHByb25vc3RpY29zW2ldIDwtIHNpZ3VpZW50ZQ0KICAgIHNlcmllIDwtIGMoc2VyaWUsIHNpZ3VpZW50ZSkNCiAgfQ0KICByZXR1cm4ocHJvbm9zdGljb3MpDQp9DQoNCnByb25vc3RpY29fcG1wIDwtIHBtcF9mb3JlY2FzdCh0c1VOTywgcGVzb3MgPSBwZXNvcywgaCA9IDYpDQpwcm9ub3N0aWNvX3BtcA0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIE11ZXN0cmEgbGEgdGFibGEgZGUgZGF0b3MgDQojIyMgVGFibGEgZGUgZGF0b3MgKGhpc3TDs3JpY28gKyBhbWJvcyBwcm9tZWRpb3MgbcOzdmlsZXMgY29tbyByZWZlcmVuY2lhKQ0KdGFibGFfcmVmZXJlbmNpYSA8LSBkYXRhLmZyYW1lKA0KICBzZW1hbmEgPSBzZW1hbmEsDQogIHZhbG9yID0gdmFsb3IsDQogIE1BMyA9IGFzLm51bWVyaWMocHJvbWVkaW9fbW92aWwpDQopDQp0YWJsYV9yZWZlcmVuY2lhDQpgYGANCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI01PREVMTyA0LiBTdWF2aXNhZG9yIGV4cG9uZW5jaWFsIChrPTMpIGNvbiBsZXRyYSBhbHBoYSA9MC4yIChsYSBkZWwgcGVzY2FkaXRvKQ0KIyBFU0UgSEFZIFFVRSBCVVNDQVJMTyBFTiBFTCBQREYgUVVFIE5PUyBDT01QQVJUSVJBIEVMIFBST0ZFDQojIyBFbCBzdWF2aXphZG8gZXhwb25lbmNpYWwgc2ltcGxlIChTRVMpIGRhIG3DoXMgcGVzbyBhIGxhcyBvYnNlcnZhY2lvbmVzIHJlY2llbnRlcyBkZSBmb3JtYSBkZWNyZWNpZW50ZSB5IGV4cG9uZW5jaWFsLCBjb250cm9sYWRvIHBvciBlbCBwYXLDoW1ldHJvIGFscGhhICgibGEgbGV0cmEgZGVsIHBlc2NhZGl0byIg4o26KS4gQXF1w60gc2UgZmlqYSBhbHBoYSA9IDAuMiAoZW4gdmV6IGRlIGRlamFyIHF1ZSBSIGxvIG9wdGltaWNlKS4NCg0Kc2VzMSA8LSBzZXModHNVTk8sIGFscGhhID0gMC4yLCBoID0gNiwgaW5pdGlhbCA9ICJzaW1wbGUiKQ0Kc3VtbWFyeShzZXMxKQ0KcGxvdChzZXMxKQ0KDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNNT0RFTE8gNS4gQVJJTUENCiMgU2UgZGVqYSBxdWUgYGF1dG8uYXJpbWEoKWAgc2VsZWNjaW9uZSBhdXRvbcOhdGljYW1lbnRlIGVsIG1lam9yIG1vZGVsbyBBUklNQShwLGQscSkgY29uIGJhc2UgZW4gZWwgY3JpdGVyaW8gZGUgaW5mb3JtYWNpw7NuIChBSUNjKS4NCg0KYXJpbWExIDwtIGF1dG8uYXJpbWEodHNVTk8pDQpzdW1tYXJ5KGFyaW1hMSkNCg0KcHJvbm9zdGljb19hcmltYSA8LSBmb3JlY2FzdChhcmltYTEsIGxldmVsID0gYyg5NSksIGggPSA2KQ0KcHJvbm9zdGljb19hcmltYQ0KcGxvdChwcm9ub3N0aWNvX2FyaW1hKQ0KYGBgDQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPkNPTVBBUkFDScOTTiBERSBNT0RFTE9TIFkgU0VMRUNDScOTTiBERUwgTUVKT1I8L3NwYW4+DQoNClBhcmEgY29tcGFyYXIgbG9zIDUgbW9kZWxvcyBkZSBmb3JtYSBqdXN0YSwgc2UgY2FsY3VsYSBlbCBlcnJvciAoUk1TRSB5IE1BRSkgZGVsIGFqdXN0ZSBkZW50cm8gZGUgbGEgbXVlc3RyYSAoaW4tc2FtcGxlKSBkZSBjYWRhIHVubzogcXXDqSB0YW50byBzZSBwYXJlY2VuIGxvcyB2YWxvcmVzIGFqdXN0YWRvcy9wcm9ub3N0aWNhZG9zIHVuIHBhc28gYWRlbGFudGUgYSBsb3MgdmFsb3JlcyByZWFsZXMgb2JzZXJ2YWRvcy4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgRXJyb3JlcyBpbi1zYW1wbGUgcGFyYSBOYWl2ZSwgU0VTIHkgQVJJTUEgKHVzYW5kbyBhY2N1cmFjeSgpKQ0KYWNjX25haXZlIDwtIGFjY3VyYWN5KG5haXZlMSkNCmFjY19zZXMgICA8LSBhY2N1cmFjeShzZXMxKQ0KYWNjX2FyaW1hIDwtIGFjY3VyYWN5KGFyaW1hMSkNCg0KIyBFcnJvcmVzIGluLXNhbXBsZSBwYXJhICJQTSIgeSAiUE1QIg0KIyAoY2FsY3VsYWRvcyBtYW51YWxtZW50ZSB5YSBxdWUgc29uIGZ1bmNpb25lcyBoZWNoYXMgYSBtYW5vIHkgbm8gb2JqZXRvcyAiZm9yZWNhc3QiKQ0KdmFsb3JfbnVtIDwtIGFzLm51bWVyaWModHNVTk8pDQpuIDwtIGxlbmd0aCh2YWxvcl9udW0pDQoNCiMgLS0tIEFqdXN0ZSBpbi1zYW1wbGUgIlBNIjogcHJlZGljZSBzZW1hbmEgdCB1c2FuZG8gdC0zLCB0LTIsIHQtMSAtLS0NCm1hX2ZpdHRlZCA8LSBzYXBwbHkoNDpuLCBmdW5jdGlvbih0KSBtZWFuKHZhbG9yX251bVsodC0zKToodC0xKV0pKQ0KbWFfYWN0dWFsIDwtIHZhbG9yX251bVs0Om5dDQptYV9ybXNlIDwtIHNxcnQobWVhbigobWFfYWN0dWFsIC0gbWFfZml0dGVkKV4yKSkNCm1hX21hZSAgPC0gbWVhbihhYnMobWFfYWN0dWFsIC0gbWFfZml0dGVkKSkNCg0KIyAtLS0gQWp1c3RlIGluLXNhbXBsZSAiUE1QIiBjb24gcGVzb3MgMy82LCAyLzYsIDEvNiAtLS0NCndtYV9maXR0ZWQgPC0gc2FwcGx5KDQ6biwgZnVuY3Rpb24odCl7DQogIHZlbnRhbmEgPC0gcmV2KHZhbG9yX251bVsodC0zKToodC0xKV0pICAjIG3DoXMgcmVjaWVudGUgcHJpbWVybw0KICBzdW0odmVudGFuYSAqIHBlc29zKQ0KfSkNCndtYV9hY3R1YWwgPC0gdmFsb3JfbnVtWzQ6bl0NCndtYV9ybXNlIDwtIHNxcnQobWVhbigod21hX2FjdHVhbCAtIHdtYV9maXR0ZWQpXjIpKQ0Kd21hX21hZSAgPC0gbWVhbihhYnMod21hX2FjdHVhbCAtIHdtYV9maXR0ZWQpKQ0KDQp0YWJsYV9jb21wYXJhY2lvbiA8LSBkYXRhLmZyYW1lKA0KICBNb2RlbG8gPSBjKCJOYWl2ZSIsICJQcm9tZWRpbyBNb3ZpbCAoaz0zKSIsICJQcm9tZWRpbyBNb3ZpbCBQb25kZXJhZG8gKGs9MykiLA0KICAgICAgICAgICAgICJTdWF2aXphZG8gRXhwb25lbmNpYWwgKGFscGhhPTAuMikiLCAiQVJJTUEiKSwNCiAgUk1TRSA9IGMoYWNjX25haXZlWzEsIlJNU0UiXSwgbWFfcm1zZSwgd21hX3Jtc2UsIGFjY19zZXNbMSwiUk1TRSJdLCBhY2NfYXJpbWFbMSwiUk1TRSJdKSwNCiAgTUFFICA9IGMoYWNjX25haXZlWzEsIk1BRSJdLCAgbWFfbWFlLCAgd21hX21hZSwgIGFjY19zZXNbMSwiTUFFIl0sICBhY2NfYXJpbWFbMSwiTUFFIl0pDQopDQoNCnRhYmxhX2NvbXBhcmFjaW9uW29yZGVyKHRhYmxhX2NvbXBhcmFjaW9uJFJNU0UpLCBdDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNQUk9Ow5NTVElDTyBGSU5BTCAtIE1PREVMTyBHQU5BRE9SDQoNCm1lam9yX21vZGVsbyA8LSB0YWJsYV9jb21wYXJhY2lvbltvcmRlcih0YWJsYV9jb21wYXJhY2lvbiRSTVNFKSwgXVsxLCAiTW9kZWxvIl0NCmNhdCgiTW9kZWxvIGVsZWdpZG8gc2Vnw7puIFJNU0UvTUFFIG1hcyBiYWpvczoiLCBtZWpvcl9tb2RlbG8sICJcbiIpDQoNCiMgUHJvbsOzc3RpY28gZmluYWwgY29uIGVsIG1vZGVsbyBBUklNQSANCnByb25vc3RpY29fZmluYWwgPC0gZm9yZWNhc3QoYXJpbWExLCBoID0gNikNCnRhYmxhX3Byb25vc3RpY29fZmluYWwgPC0gZGF0YS5mcmFtZSgNCiAgU2VtYW5hID0gMTM6MTgsDQogIFByb25vc3RpY28gPSBhcy5udW1lcmljKHByb25vc3RpY29fZmluYWwkbWVhbiksDQogIExpbWl0ZV9JbmZlcmlvcl85NSA9IGFzLm51bWVyaWMocHJvbm9zdGljb19maW5hbCRsb3dlclssMl0pLA0KICBMaW1pdGVfU3VwZXJpb3JfOTUgPSBhcy5udW1lcmljKHByb25vc3RpY29fZmluYWwkdXBwZXJbLDJdKQ0KKQ0KdGFibGFfcHJvbm9zdGljb19maW5hbA0KDQpwbG90KHByb25vc3RpY29fZmluYWwsIG1haW4gPSAiUHJvbsOzc3RpY28gZGUgbGFzIHNpZ3VpZW50ZXMgNiBzZW1hbmFzIikNCmBgYA0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiBncmVlbjsiPkNPTkNMVVNJw5NOPC9zcGFuPg0KDQpBbCBjb21wYXJhciBsb3MgY2luY28gbW9kZWxvcyBjb24gYmFzZSBlbiBlbCBlcnJvciBkZW50cm8gZGUgbXVlc3RyYSAoUk1TRSB5IE1BRSksIHNlIG9ic2VydmEgcXVlIGVsIG1vZGVsbyAqKk5haXZlKiogcHJlc2VudGEgZWwgUEVPUiBERVNFTVBFw5FPIHlhIHF1ZSBzb2xvIHV0aWxpemEgZWwgw7psdGltbyBkYXRvIG9ic2VydmFkbyAoMjIpIGNvbW8gcHJvbsOzc3RpY28gY29uc3RhbnRlIHBhcmEgbGFzIDYgc2VtYW5hcywgaWdub3JhbmRvIGVsIHJlc3RvIGRlIGxhIGluZm9ybWFjacOzbiBoaXN0w7NyaWNhIHkgbGEgdmFyaWFiaWxpZGFkIG5hdHVyYWwgZGUgbGEgc2VyaWUuDQoNCkVsIG1vZGVsbyAqKkFSSU1BKiosIHNlbGVjY2lvbmFkbyBhdXRvbcOhdGljYW1lbnRlIGNvbiBgYXV0by5hcmltYSgpYCwgZXMgZWwgcXVlIG1lam9yIGNhcHR1cmEgZWwgcGF0csOzbiBkZSBsYSBzZXJpZSAocXVlIHNlIGNvbXBvcnRhIGNvbW8gZGF0b3MgcXVlIG9zY2lsYW4gYWxyZWRlZG9yIGRlIHVuYSBtZWRpYSwgc2luIHRlbmRlbmNpYSBuaSBlc3RhY2lvbmFsaWRhZCBkZXRlY3RhYmxlIGNvbiBzb2xvIDEyIG9ic2VydmFjaW9uZXMpLCBvYnRlbmllbmRvIGVsIG1lbm9yIFJNU0UgeSBNQUUgZGUgbG9zIGNpbmNvIG1vZGVsb3MgZXZhbHVhZG9zLiBQb3IgZXN0YSByYXrDs24sICoqQVJJTUEgc2UgZWxpZ2UgY29tbyBlbCBtb2RlbG8gZmluYWwqKiBwYXJhIHByb25vc3RpY2FyIGxhcyBzaWd1aWVudGVzIDYgc2VtYW5hcywgY29uIHVuIHByb27Ds3N0aWNvIHF1ZSBzZSBtYW50aWVuZSBlc3RhYmxlIGFscmVkZWRvciBkZWwgbml2ZWwgcHJvbWVkaW8gaGlzdMOzcmljbyBkZSBsYSBzZXJpZSAofjE5KSwgYWNvbXBhw7FhZG8gZGUgc3VzIGludGVydmFsb3MgZGUgY29uZmlhbnphIGFsIDk1JS4NCg0KRW4gdMOpcm1pbm9zIGdlbmVyYWxlcywgbGEgY29uY2x1c2nDs24gcHLDoWN0aWNhIGVzIHF1ZSwgYW50ZSB1bmEgc2VyaWUgY29uIHBvY29zIGRhdG9zIHkgc2luIHVuYSB0ZW5kZW5jaWEgbyBlc3RhY2lvbmFsaWRhZCBtYXJjYWRhLCBsb3MgbW9kZWxvcyBxdWUgcHJvbWVkaWFuIGluZm9ybWFjacOzbiBoaXN0w7NyaWNhIChBUklNQSwgc3Vhdml6YWRvIGV4cG9uZW5jaWFsLCBwcm9tZWRpb3MgbcOzdmlsZXMpIHNvbiBwcmVmZXJpYmxlcyBhbCBtw6l0b2RvIE5haXZlLCB5IEFSSU1BIG9mcmVjZSBlbCBtZWpvciBiYWxhbmNlIGVudHJlIGFqdXN0ZSBhIGxvcyBkYXRvcyB5IGNhcGFjaWRhZCBkZSBnZW5lcmFsaXphY2nDs24gcGFyYSBlbCBtZWpvciBwcm9uw7NzdGljbyBwb3NpYmxlLg0KDQo=