Series de Tiempo

Cuatro ejercicios. Los dos primeros comparan cinco modelos de pronóstico sobre una misma serie y eligen el mejor por MAPE. Los dos últimos son informes gerenciales con descomposición estacional.

Instalar paquetes y llamar librerías

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

Funciones que se usan en todo el documento

# Promedio Móvil de orden k. El pronóstico de t usa las k observaciones previas.
pm <- function(y, k){
  f <- rep(NA, length(y))
  for(t in (k+1):length(y)) f[t] <- mean(y[(t-k):(t-1)])
  f
}

# Promedio Móvil Ponderado. El primer peso del vector va al dato inmediatamente
# anterior, no al más viejo. Los pesos deben sumar 1.
pmp <- function(y, w){
  k <- length(w); f <- rep(NA, length(y))
  for(t in (k+1):length(y)) f[t] <- sum(y[(t-1):(t-k)] * w)
  f
}

# Suavizador Exponencial. Arranca con F2 = y1.
suav <- function(y, a){
  f <- rep(NA, length(y)); f[2] <- y[1]
  for(t in 3:length(y)) f[t] <- a*y[t-1] + (1-a)*f[t-1]
  f
}
suav_sig <- function(y, a){
  f <- y[1]; for(t in 2:length(y)) f <- a*y[t] + (1-a)*f; f
}

# Las seis métricas. Cada modelo se divide entre SUS propios errores.
métricas <- function(real, pron){
  ok <- !is.na(pron) & !is.na(real)
  y <- real[ok]; f <- pron[ok]; e <- y - f; n <- length(e)
  c(n=n, ME=sum(e)/n, MAE=sum(abs(e))/n, MSE=sum(e^2)/n,
    RMSE=sqrt(sum(e^2)/n), MPE=sum(e/y*100)/n, MAPE=sum(abs(e/y*100))/n)
}

# Índices estacionales por el método de razón al promedio móvil centrado.
# Se normalizan para que sumen el número de periodos del ciclo.
índices_estacionales <- function(serie){
  f <- frequency(serie)
  cma <- stats::filter(serie, c(0.5, rep(1, f-1), 0.5)/f, sides = 2)
  razón <- as.numeric(serie) / as.numeric(cma)
  crudos <- tapply(razón, cycle(serie), mean, na.rm = TRUE)
  crudos * f / sum(crudos)
}

Ejercicio 1. Ventas Semanales

Instrucciones: modela la serie con los cinco modelos vistos en clase, arma la tabla resumen y concluye.

semana <- c(1:12)
valor  <- c(17,21,19,23,18,16,20,18,22,20,15,22)
ts1 <- ts(valor, c(2025,1), frequency=52)
plot(ts1, ylab="Valor", xlab="Semana", main="Ventas semanales")

# 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
f1_naive <- c(NA, head(valor, -1))

# Modelo 2. Promedio Móvil (k=3)
f1_pm <- pm(valor, 3)

# Modelo 3. Promedio Móvil Ponderado (k=3, pesos 3/6, 2/6 y 1/6)
f1_pmp <- pmp(valor, c(3,2,1)/6)

# Modelo 4. Suavizador Exponencial (alfa = 0.2)
f1_ses <- suav(valor, 0.2)

# Modelo 5. ARIMA
arima1 <- auto.arima(ts1); 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
f1_arima <- as.numeric(fitted(arima1))
tabla1 <- data.frame(Semana=semana, Real=valor, Naive=f1_naive, PM3=f1_pm,
                     PMP3=f1_pmp, SES=f1_ses, ARIMA=f1_arima)
knitr::kable(tabla1, digits=2, caption="Pronósticos de cada modelo, Ejercicio 1")
Pronósticos de cada modelo, Ejercicio 1
Semana Real Naive PM3 PMP3 SES ARIMA
1 17 NA NA NA NA 19.25
2 21 17 NA NA 17.00 19.25
3 19 21 NA NA 17.80 19.25
4 23 19 19 19.33 18.04 19.25
5 18 23 21 21.33 19.03 19.25
6 16 18 20 19.83 18.83 19.25
7 20 16 19 17.83 18.26 19.25
8 18 20 18 18.33 18.61 19.25
9 22 18 18 18.33 18.49 19.25
10 20 22 20 20.33 19.19 19.25
11 15 20 20 20.33 19.35 19.25
12 22 15 19 17.83 18.48 19.25
modelos1 <- list("1. Naive"=f1_naive, "2. PM k=3"=f1_pm, "3. PMP k=3"=f1_pmp,
                 "4. SES alfa=0.2"=f1_ses, "5. ARIMA"=f1_arima)
res1 <- t(sapply(modelos1, métricas, real=valor))
knitr::kable(res1, digits=4, caption="Tabla resumen de resultados, Ejercicio 1")
Tabla resumen de resultados, Ejercicio 1
n ME MAE MSE RMSE MPE MAPE
1. Naive 11 0.4545 3.7273 16.2727 4.0339 0.1082 19.2443
2. PM k=3 9 0.0000 2.6667 10.2222 3.1972 -2.3101 14.3566
3. PMP k=3 9 0.0556 2.9815 11.4907 3.3898 -2.1299 15.9925
4. SES alfa=0.2 11 0.9932 2.5963 8.9822 2.9970 3.2600 13.4024
5. ARIMA 12 0.0000 2.0833 5.8542 2.4195 -1.6623 11.1853
sig1 <- c(Naive=tail(valor,1), PM3=mean(tail(valor,3)),
          PMP3=sum(rev(tail(valor,3))*c(3,2,1)/6), SES=suav_sig(valor,0.2),
          ARIMA=as.numeric(forecast(arima1, h=1)$mean))
round(sig1, 4)
##   Naive     PM3    PMP3     SES   ARIMA 
## 22.0000 19.0000 19.3333 19.1850 19.2500

Conclusión del Ejercicio 1

El MAPE más bajo lo da el ARIMA con 11.19%, y atrás van el suavizador exponencial con 13.40%, el promedio móvil simple con 14.36%, el ponderado con 15.99% y hasta el final el Naive con 19.24%.

Vale la pena ver qué modelo escogió el auto.arima, porque salió un ARIMA(0,0,0) con media. Eso quiere decir que decidió que la serie es puro ruido alrededor de un nivel fijo y que su pronóstico para todas las semanas es el mismo número, 19.25. O sea que el mejor modelo acabó siendo no hacerle caso a nada de lo reciente y quedarse en el promedio.

Eso encaja con el resto de la tabla. El orden va con el peso que cada modelo le da al último dato, porque el Naive le da todo y es el peor, el ponderado la mitad, el simple un tercio, el suavizador una quinta parte y el ARIMA prácticamente nada. Entre menos caso le hacen al último dato, mejor pronostican. La razón es que la serie se mueve alrededor de 19 sin subir ni bajar, entonces el brinco de una semana a otra es ruido y creerle nada más mete error.

El ARIMA se evalúa sobre las 12 semanas, mientras que los promedios móviles pierden 3 y el Naive pierde 1, y su media está estimada con los mismos datos con los que se le mide el error, así que la comparación no es del todo pareja. Aun con eso, los cinco modelos apuntan a la misma conclusión.


Ejercicio 2. Hershey’s

Instrucciones: mismo análisis que el ejercicio 1 pero con la base Ventas_Historicas_Lechitas.xlsx. Pronostica los siguientes 6 meses.

# El Excel trae los tres años en bloques de dos columnas uno junto al otro
# (2017 en A-B, 2018 en C-D, 2019 en E-F), por eso se apilan.
ruta <- "Ventas_Historicas_Lechitas.xlsx"
crudo <- read_excel(ruta, col_names=FALSE)

bloques <- list(crudo[,1:2], crudo[,3:4], crudo[,5:6])
df2 <- do.call(rbind, lapply(bloques, function(b){
  names(b) <- c("Mes","Ventas")
  b <- b[!is.na(suppressWarnings(as.numeric(b$Ventas))), ]
  data.frame(Mes=as.Date(as.numeric(b$Mes), origin="1899-12-30"),
             Ventas=as.numeric(b$Ventas))
}))
df2 <- df2[order(df2$Mes), ]
ventas <- df2$Ventas
ts2 <- ts(ventas, c(2017,1), frequency=12)
str(df2)
## 'data.frame':    36 obs. of  2 variables:
##  $ Mes   : Date, format: "2017-01-01" "2017-02-01" ...
##  $ Ventas: num  25521 23740 26254 25868 27073 ...
plot(ts2, ylab="Ventas en miles de USD", xlab="Año",
     main="Ventas históricas de leche saborizada Hershey México")

# 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.644 902.8514 0.8162353 3.013136 0.2579763 -0.5648044
## 
## 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
f2_naive <- c(NA, head(ventas, -1))

# Modelo 2. Promedio Móvil (k=3)
f2_pm <- pm(ventas, 3)

# Modelo 3. Promedio Móvil Ponderado (k=3, pesos 3/6, 2/6 y 1/6)
f2_pmp <- pmp(ventas, c(3,2,1)/6)

# Modelo 4. Suavizador Exponencial (alfa = 0.2)
f2_ses <- suav(ventas, 0.2)

# Modelo 5. ARIMA
arima2 <- auto.arima(ts2); arima2
## Series: ts2 
## 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
f2_arima <- as.numeric(fitted(arima2))

pronóstico2 <- forecast(arima2, level=c(95), h=6)
pronóstico2
##          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
plot(pronóstico2, ylab="Ventas en miles de USD", xlab="Año",
     main="Pronóstico a 6 meses con ARIMA")

modelos2 <- list("1. Naive"=f2_naive, "2. PM k=3"=f2_pm, "3. PMP k=3"=f2_pmp,
                 "4. SES alfa=0.2"=f2_ses, "5. ARIMA"=f2_arima)
res2 <- t(sapply(modelos2, métricas, real=ventas))
knitr::kable(res2, digits=4, caption="Tabla resumen de resultados, Ejercicio 2")
Tabla resumen de resultados, Ejercicio 2
n ME MAE MSE RMSE MPE MAPE
1. Naive 35 266.4474 902.8514 1237975.6 1112.6435 0.8162 3.0131
2. PM k=3 33 591.0093 870.4659 1063495.6 1031.2592 1.9011 2.8117
3. PMP k=3 33 481.5529 814.1926 919646.3 958.9819 1.5425 2.6245
4. SES alfa=0.2 35 1255.3971 1402.2860 2458027.1 1567.8096 3.9842 4.5734
5. ARIMA 36 25.2216 227.1700 118242.4 343.8640 0.0806 0.7070
# Promedios anuales, para ver la tendencia
tapply(ventas, rep(2017:2019, each=12), mean)
##     2017     2018     2019 
## 26838.29 30567.67 33837.78

Conclusión del Ejercicio 2

El mejor es el ARIMA, con el MAPE más bajo por mucho, y el pronóstico de los seis meses que pide el ejercicio es el que sale en la tabla de arriba.

Gana porque es el único que toma en cuenta que la serie va subiendo. Los promedios de cada año van de 26,838 en 2017 a 30,568 en 2018 y 33,838 en 2019, y con ese crecimiento los modelos que nada más promedian lo que ya pasó siempre se quedan cortos. Se nota en que los cuatro modelos simples tienen el ME positivo, o sea que pronostican por debajo del valor real mes tras mes.

Y aquí el orden entre los modelos simples se voltea respecto al Ejercicio 1. El suavizador con alfa de 0.2 pasa de ser el mejor a ser el peor, y el ponderado le gana al simple. Cuando la serie trae dirección el dato más reciente sí sirve para saber qué sigue, así que suavizar de más estorba en lugar de ayudar.


Ejercicio 3. Vintage Restaurant

Karen Payne quiere saber cómo se comportan las ventas del restaurante y qué esperar del cuarto año. Los datos son las ventas mensuales de alimentos y bebidas en miles de dólares de los tres primeros años.

año1 <- c(242,235,232,178,184,140,145,152,110,130,152,206)
año2 <- c(263,238,247,193,193,149,157,161,122,130,167,230)
año3 <- c(282,255,265,205,210,160,166,174,126,148,173,235)

vintage <- ts(c(año1, año2, año3), start=c(1,1), frequency=12)
meses <- c("Enero","Febrero","Marzo","Abril","Mayo","Junio",
           "Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre")

knitr::kable(data.frame(Mes=meses, `Año 1`=año1, `Año 2`=año2, `Año 3`=año3,
                        check.names=FALSE),
             caption="Ventas de alimentos y bebidas del restaurante Vintage, miles de dólares")
Ventas de alimentos y bebidas del restaurante Vintage, miles de dólares
Mes Año 1 Año 2 Año 3
Enero 242 263 282
Febrero 235 238 255
Marzo 232 247 265
Abril 178 193 205
Mayo 184 193 210
Junio 140 149 160
Julio 145 157 166
Agosto 152 161 174
Septiembre 110 122 126
Octubre 130 130 148
Noviembre 152 167 173
Diciembre 206 230 235

1. Gráfica de la serie de tiempo

plot(vintage, ylab="Ventas en miles de USD", xlab="Año de operación",
     main="Ventas mensuales del Vintage Restaurant", lwd=2, col="#2f5183")
points(vintage, pch=19, cex=0.5, col="#2f5183")
grid()

En la gráfica se ven dos cosas al mismo tiempo. La primera es un patrón que se repite igual cada año, con las ventas más altas en enero, febrero y marzo y las más bajas en septiembre. La segunda es que ese mismo patrón va subiendo de nivel año con año, o sea que además del ciclo hay tendencia.

Para Captiva Island tiene todo el sentido, porque es la temporada alta de turismo en Florida cuando en el norte hace frío, y septiembre cae en plena temporada de huracanes, que es cuando se vacía la isla.

2. Índices estacionales

ie <- índices_estacionales(vintage)
knitr::kable(data.frame(Mes=meses, `Índice estacional`=round(as.numeric(ie),4),
                        check.names=FALSE),
             caption="Índices estacionales por el método de razón al promedio móvil centrado")
Índices estacionales por el método de razón al promedio móvil centrado
Mes Índice estacional
Enero 1.4436
Febrero 1.2997
Marzo 1.3441
Abril 1.0412
Mayo 1.0494
Junio 0.8004
Julio 0.8283
Agosto 0.8530
Septiembre 0.6280
Octubre 0.7003
Noviembre 0.8528
Diciembre 1.1593
cat("Los índices suman:", sum(ie), "\n")
## Los índices suman: 12
barplot(as.numeric(ie), names.arg=substr(meses,1,3), las=2,
        col=ifelse(as.numeric(ie)>1, "#1a7f37", "#c8502f"),
        main="Índice estacional por mes", ylab="Índice")
abline(h=1, lwd=2, lty=2)

Los índices sí tienen sentido intuitivo y bastante. Enero sale en 1.4436, o sea que un enero vende 44% más que un mes promedio del año, y marzo y febrero andan cerca con 1.3441 y 1.2997. Los tres meses de invierno son la temporada alta y ahí es donde el restaurante hace su dinero.

Del otro lado septiembre sale en 0.6280, que es vender 37% menos que un mes normal, y octubre en 0.7003. Ese es el fondo del año.

La diferencia entre el mejor y el peor mes es enorme, porque enero vende más del doble que septiembre con los mismos costos fijos encima. Eso ya es una recomendación por sí sola para Karen, los meses buenos tienen que cargar con los malos, y conviene planear el personal y el inventario mes por mes y no con un promedio anual.

3. Serie desestacionalizada

desestacionalizada <- as.numeric(vintage) / as.numeric(ie[cycle(vintage)])
ts_des <- ts(desestacionalizada, start=c(1,1), frequency=12)

plot(vintage, ylab="Ventas en miles de USD", xlab="Año de operación",
     main="Serie original contra serie desestacionalizada", col="#b8c4d4", lwd=2)
lines(ts_des, col="#c8502f", lwd=2)
legend("topright", c("Original","Desestacionalizada"),
       col=c("#b8c4d4","#c8502f"), lwd=2, bty="n")
grid()

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

Sí hay tendencia y es clara. Al quitarle el efecto de los meses, la serie queda en una línea que sube parejo, y la regresión da una pendiente de 1.0213 con un R² de 0.9204.

Eso quiere decir que el restaurante crece alrededor de mil dólares de venta cada mes, o sea unos 12,255 dólares más al año. El negocio está creciendo de verdad, no es que unos meses tapen a otros.

4. Pronóstico del año 4 por descomposición

t4 <- 37:48
tend4 <- coef(tendencia)[1] + coef(tendencia)[2]*t4
pron_descomposición <- tend4 * as.numeric(ie)

knitr::kable(data.frame(Mes=meses, Tendencia=round(tend4,2),
                        `Índice`=round(as.numeric(ie),4),
                        `Pronóstico`=round(pron_descomposición,2), check.names=FALSE),
             caption="Pronóstico del año 4 por descomposición, miles de dólares")
Pronóstico del año 4 por descomposición, miles de dólares
Mes Tendencia Índice Pronóstico
Enero 207.14 1.4436 299.02
Febrero 208.16 1.2997 270.54
Marzo 209.18 1.3441 281.16
Abril 210.20 1.0412 218.86
Mayo 211.22 1.0494 221.65
Junio 212.24 0.8004 169.88
Julio 213.27 0.8283 176.65
Agosto 214.29 0.8530 182.78
Septiembre 215.31 0.6280 135.21
Octubre 216.33 0.7003 151.50
Noviembre 217.35 0.8528 185.35
Diciembre 218.37 1.1593 253.15
cat("Total del año 4 estimado:", round(sum(pron_descomposición),2), "miles de dólares\n")
## Total del año 4 estimado: 2545.76 miles de dólares

5. Pronóstico del año 4 con regresión de variables ficticias

mes_factor <- factor(rep(meses, 3), levels=meses)
regresión_dummies <- lm(as.numeric(vintage) ~ t + mes_factor)
summary(regresión_dummies)
## 
## Call:
## lm(formula = as.numeric(vintage) ~ t + mes_factor)
## 
## 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 ***
## mes_factorFebrero     -20.68403    3.62688  -5.703 8.30e-06 ***
## mes_factorMarzo       -16.36806    3.62924  -4.510 0.000158 ***
## mes_factorAbril       -73.38542    3.63317 -20.199 3.90e-16 ***
## mes_factorMayo        -70.73611    3.63866 -19.440 8.97e-16 ***
## mes_factorJunio      -117.75347    3.64571 -32.299  < 2e-16 ***
## mes_factorJulio      -112.43750    3.65431 -30.768  < 2e-16 ***
## mes_factorAgosto     -107.12153    3.66445 -29.233  < 2e-16 ***
## mes_factorSeptiembre -151.13889    3.67611 -41.114  < 2e-16 ***
## mes_factorOctubre    -135.48958    3.68928 -36.725  < 2e-16 ***
## mes_factorNoviembre  -108.50694    3.70395 -29.295  < 2e-16 ***
## mes_factorDiciembre   -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
nuevos <- data.frame(t=t4, mes_factor=factor(meses, levels=meses))
pron_dummies <- as.numeric(predict(regresión_dummies, nuevos))

knitr::kable(data.frame(Mes=meses,
                        `Descomposición`=round(pron_descomposición,2),
                        `Dummies`=round(pron_dummies,2),
                        `Diferencia`=round(pron_dummies-pron_descomposición,2),
                        check.names=FALSE),
             caption="Los dos pronósticos del año 4 comparados")
Los dos pronósticos del año 4 comparados
Mes Descomposición Dummies Diferencia
Enero 299.02 286.75 -12.27
Febrero 270.54 267.08 -3.46
Marzo 281.16 272.42 -8.75
Abril 218.86 216.42 -2.45
Mayo 221.65 220.08 -1.57
Junio 169.88 174.08 4.21
Julio 176.65 180.42 3.77
Agosto 182.78 186.75 3.97
Septiembre 135.21 143.75 8.54
Octubre 151.50 160.42 8.92
Noviembre 185.35 188.42 3.07
Diciembre 253.15 248.08 -5.07
cat("Total por descomposición:", round(sum(pron_descomposición),2), "\n")
## Total por descomposición: 2545.76
cat("Total por dummies:      ", round(sum(pron_dummies),2), "\n")
## Total por dummies:       2544.67

6. Resumen, error de pronóstico y conclusiones

plot(ts(c(as.numeric(vintage), pron_descomposición), start=c(1,1), frequency=12),
     col="#2f5183", lwd=2, ylab="Ventas en miles de USD", xlab="Año de operación",
     main="Historia y pronóstico del año 4")
lines(ts(c(rep(NA,36), pron_dummies), start=c(1,1), frequency=12), col="#d9a021", lwd=2)
abline(v=4, lty=2)
legend("topright", c("Histórico y descomposición","Dummies"),
       col=c("#2f5183","#d9a021"), lwd=2, bty="n")
grid()

real_enero <- 295
e_desc <- real_enero - pron_descomposición[1]
e_dumm <- real_enero - pron_dummies[1]

knitr::kable(data.frame(
  Método = c("Descomposición","Regresión con dummies"),
  Pronóstico = round(c(pron_descomposición[1], pron_dummies[1]),2),
  Real = real_enero,
  Error = round(c(e_desc, e_dumm),2),
  `Error %` = round(c(e_desc, e_dumm)/real_enero*100, 2),
  check.names=FALSE),
  caption="Error de pronóstico de enero del año 4")
Error de pronóstico de enero del año 4
Método Pronóstico Real Error Error %
Descomposición 299.02 295 -4.02 -1.36
Regresión con dummies 286.75 295 8.25 2.80
cat("R² de la regresión con dummies:", round(summary(regresión_dummies)$r.squared,4), "\n")
## R² de la regresión con dummies: 0.9942
cat("R² de la tendencia desestacionalizada:", round(summary(tendencia)$r.squared,4), "\n")
## R² de la tendencia desestacionalizada: 0.9204

Informe para Karen

Las ventas del Vintage traen dos cosas encima al mismo tiempo, un patrón de temporada muy marcado y una tendencia de crecimiento. El patrón es el que salta a la vista, porque enero vende 44% arriba del mes promedio y septiembre 37% abajo, y esa diferencia se repite igualita los tres años. La tendencia está tapada por ese patrón y solo se ve al desestacionalizar, y ahí queda una línea limpia que sube 1,021 dólares cada mes, o sea como 12 mil dólares más de venta cada año.

Los dos métodos pronostican casi lo mismo para el año 4. Por descomposición sale 2,545.76 mil dólares en el año y por regresión con dummies 2,544.67, o sea que se separan por mil dólares sobre dos millones y medio. Donde sí difieren es mes por mes.

Sobre enero, el pronóstico por descomposición fue de 299.02 mil y las ventas reales salieron en 295 mil, así que se pasó por 4 mil dólares, un 1.4%. El de dummies dio 286.75 mil y se quedó corto por 8.25 mil, un 2.8%. Los dos le atinaron bien, y si a Karen le preocupa esa diferencia lo que hay que decirle es que 4 mil dólares sobre 295 mil es ruido normal de un mes y no una falla del método.

Lo que sí le recomendaría para no quedarse con la duda mes a mes es dejar de ver el pronóstico como un número solo. Tres cosas concretas. Que se maneje un rango en vez de una cifra, porque el error típico del modelo se puede calcular y le da un piso y un techo para planear. Que se recalculen los índices estacionales cada año conforme entren datos nuevos, ya que con tres años apenas van tres observaciones por mes y con cinco o seis la estimación se vuelve más firme. Y que se lleve registro del error mes con mes, porque si los errores empiezan a salir todos del mismo lado quiere decir que el negocio cambió de nivel y el modelo se tiene que reajustar.


Ejercicio 4. Carlson Department Store

Carlson estuvo cerrada de septiembre a diciembre del año 5 por el huracán. Hay que estimar cuánto habría vendido de no haber pasado, y si le corresponde algo del aumento de actividad comercial que hubo en la zona después.

# Ventas de Carlson en los 48 meses previos al huracán, de septiembre del año 1
# a agosto del año 5, en millones de dólares
carlson <- c(1.71,1.90,2.74,4.20,
             1.45,1.80,2.03,1.99,2.32,2.20,2.13,2.43,1.90,2.13,2.56,4.16,
             2.31,1.89,2.02,2.23,2.39,2.14,2.27,2.21,1.89,2.29,2.83,4.04,
             2.31,1.99,2.42,2.45,2.57,2.42,2.40,2.50,2.09,2.54,2.97,4.35,
             2.56,2.28,2.69,2.48,2.73,2.37,2.31,2.23)

# Ventas de todas las tiendas departamentales del condado, mismos 48 meses
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,47.40,54.60,67.80,100.20,
             48.00,51.60,57.60,58.20,60.00,57.00,57.60,61.80)

# Ventas reales del condado en los cuatro meses del huracán
condado_real <- c(69.00, 75.00, 85.20, 121.80)

ts_car <- ts(carlson, start=c(1,9), frequency=12)
ts_con <- ts(condado, start=c(1,9), frequency=12)

cat("Meses de Carlson:", length(carlson), "  Meses del condado:", length(condado), "\n")
## Meses de Carlson: 48   Meses del condado: 48
par(mfrow=c(2,1), mar=c(4,4,3,1))
plot(ts_car, ylab="Millones de USD", xlab="Año", col="#2f5183", lwd=2,
     main="Carlson Department Store, 48 meses previos al huracán"); grid()
plot(ts_con, ylab="Millones de USD", xlab="Año", col="#c8502f", lwd=2,
     main="Tiendas departamentales del condado, mismos 48 meses"); grid()

par(mfrow=c(1,1))

1. Ventas que Carlson habría registrado sin el huracán

ie_car <- índices_estacionales(ts_car)
des_car <- as.numeric(ts_car) / as.numeric(ie_car[cycle(ts_car)])
tc <- 1:48
tend_car <- lm(des_car ~ tc)

knitr::kable(data.frame(Mes=meses, `Índice Carlson`=round(as.numeric(ie_car),4),
                        check.names=FALSE),
             caption="Índices estacionales de Carlson")
Índices estacionales de Carlson
Mes Índice Carlson
Enero 0.9566
Febrero 0.8194
Marzo 0.9071
Abril 0.9294
Mayo 1.0113
Junio 0.9372
Julio 0.9357
Agosto 0.9744
Septiembre 0.7967
Octubre 0.9357
Noviembre 1.1191
Diciembre 1.6774
summary(tend_car)
## 
## Call:
## lm(formula = des_car ~ tc)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.69031 -0.06049  0.01174  0.07180  0.32586 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 2.149084   0.048946   43.91  < 2e-16 ***
## tc          0.011407   0.001739    6.56 4.18e-08 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1669 on 46 degrees of freedom
## Multiple R-squared:  0.4833, Adjusted R-squared:  0.4721 
## F-statistic: 43.03 on 1 and 46 DF,  p-value: 4.181e-08
t5 <- 49:52   # septiembre a diciembre del año 5
est_car <- (coef(tend_car)[1] + coef(tend_car)[2]*t5) * as.numeric(ie_car[9:12])

knitr::kable(data.frame(Mes=meses[9:12],
                        `Ventas estimadas sin huracán`=round(est_car,3),
                        check.names=FALSE),
             caption="Carlson, septiembre a diciembre del año 5, millones de dólares")
Carlson, septiembre a diciembre del año 5, millones de dólares
Mes Ventas estimadas sin huracán
Septiembre 2.158
Octubre 2.544
Noviembre 3.056
Diciembre 4.600
cat("Total estimado sin huracán:", round(sum(est_car),3), "millones\n")
## Total estimado sin huracán: 12.358 millones

2. Ventas que el condado habría tenido sin el huracán

ie_con <- índices_estacionales(ts_con)
des_con <- as.numeric(ts_con) / as.numeric(ie_con[cycle(ts_con)])
tend_con <- lm(des_con ~ tc)
summary(tend_con)
## 
## Call:
## lm(formula = des_con ~ tc)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -4.520 -2.402 -0.015  1.484  5.598 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 62.22039    0.74999   82.96  < 2e-16 ***
## tc          -0.07194    0.02665   -2.70  0.00967 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.558 on 46 degrees of freedom
## Multiple R-squared:  0.1368, Adjusted R-squared:  0.118 
## F-statistic: 7.289 on 1 and 46 DF,  p-value: 0.009675
est_con <- (coef(tend_con)[1] + coef(tend_con)[2]*t5) * as.numeric(ie_con[9:12])

knitr::kable(data.frame(Mes=meses[9:12],
                        `Estimado sin huracán`=round(est_con,3),
                        `Real con huracán`=condado_real,
                        `Exceso`=round(condado_real-est_con,3),
                        `Exceso %`=round((condado_real/est_con-1)*100,2),
                        check.names=FALSE),
             caption="Condado, septiembre a diciembre del año 5, millones de dólares")
Condado, septiembre a diciembre del año 5, millones de dólares
Mes Estimado sin huracán Real con huracán Exceso Exceso %
Septiembre 50.549 69.0 18.451 36.50
Octubre 53.195 75.0 21.805 40.99
Noviembre 66.783 85.2 18.417 27.58
Diciembre 103.115 121.8 18.685 18.12
cat("Total estimado sin huracán:", round(sum(est_con),3), "millones\n")
## Total estimado sin huracán: 273.642 millones
cat("Total real con huracán:    ", round(sum(condado_real),3), "millones\n")
## Total real con huracán:     351 millones

3. Pérdida de Carlson y el argumento del exceso

factor_exceso <- sum(condado_real) / sum(est_con)
pérdida_base  <- sum(est_car)
pérdida_ajust <- pérdida_base * factor_exceso

knitr::kable(data.frame(
  Concepto = c("Ventas normales estimadas de Carlson",
               "Factor de exceso del condado",
               "Ventas estimadas con el efecto del huracán",
               "Pérdida total reclamable"),
  Valor = c(round(pérdida_base,3), round(factor_exceso,4),
            round(pérdida_ajust,3), round(pérdida_ajust,3))),
  caption="Estimación de la pérdida de Carlson, millones de dólares")
Estimación de la pérdida de Carlson, millones de dólares
Concepto Valor
Ventas normales estimadas de Carlson 12.3580
Factor de exceso del condado 1.2827
Ventas estimadas con el efecto del huracán 15.8510
Pérdida total reclamable 15.8510
cat("El condado vendió", round((factor_exceso-1)*100,2), "% más de lo esperado\n")
## El condado vendió 28.27 % más de lo esperado
cat("Pérdida sin ajustar:", round(pérdida_base,3), "millones\n")
## Pérdida sin ajustar: 12.358 millones
cat("Pérdida con el ajuste por huracán:", round(pérdida_ajust,3), "millones\n")
## Pérdida con el ajuste por huracán: 15.851 millones
cat("Diferencia entre las dos cifras:", round(pérdida_ajust-pérdida_base,3), "millones\n")
## Diferencia entre las dos cifras: 3.494 millones

Informe para los directivos de Carlson

Cuánto se perdió. Con el patrón de los 48 meses previos, Carlson habría vendido alrededor de 12.36 millones de dólares entre septiembre y diciembre del año 5. Esa es la pérdida de ventas por los cuatro meses que estuvo cerrada, calculada solo con su propia historia y sin meter nada del huracán.

Diciembre es el mes que más pesa de los cuatro, con 4.6 millones estimados, porque su índice estacional es de 1.6774 y vende casi 68% arriba de un mes normal. Cerrar en diciembre es lo que más caro salió.

El argumento del exceso, y sí se sostiene. El condado debió haber vendido cerca de 273.64 millones en esos cuatro meses según su propia tendencia, y vendió 351. Son 77.36 millones de más, un 28.27% arriba de lo normal, y eso no se explica con el comportamiento histórico del condado sino con los más de 8 mil millones de ayuda federal y pagos de seguros que entraron a la zona.

El exceso no está parejo. Octubre fue el mes más fuerte con 41% arriba de lo esperado y septiembre el más flojo con 36%, mientras que diciembre apenas subió 18%. Se ve como gente reponiendo lo que perdió en los meses inmediatos al huracán, y no como un cambio permanente del mercado.

Si Carlson hubiera estado abierta, no hay razón para pensar que se habría quedado fuera de ese movimiento, porque el dinero se repartió entre las tiendas departamentales del condado y Carlson es una de ellas. Aplicando el mismo factor de 1.2827, las ventas que habría hecho suben de 12.36 a 15.85 millones, o sea 3.49 millones más de reclamación.

Lo que le recomendaría a la tienda. Reclamar los 12.36 millones como pérdida base, que esa es la parte sólida y sale de su propia historia, y presentar los 3.49 millones adicionales por separado, con el análisis del condado como respaldo. Conviene presentarlos aparte y no revueltos, porque la primera cifra es difícil de discutir y la segunda depende de aceptar que Carlson habría capturado el mismo porcentaje de aumento que las demás tiendas.

Ahí está el punto débil que hay que reconocer antes de que lo saquen del otro lado. El supuesto es que Carlson habría crecido igual que el promedio del condado, y eso no está demostrado, solo es razonable. Además la regresión de tendencia del condado tiene un R² bajo, así que su estimación es menos firme que la de Carlson. Vale la pena decirlo de una vez y ofrecer la comparación mes por mes, que es donde el argumento se ve más convincente.

LS0tDQp0aXRsZTogIkFjdGl2aWRhZCAyLiBNb2RlbG9zIGRlIHByb27Ds3N0aWNvcyBlbiBzZXJpZXMgZGUgdGllbXBvIg0KYXV0aG9yOiAiRGVyZWNrIElrZXIgVmlsbGFmYcOxYSBSb21lcm8gLSBBMDA1NzM3NTYiDQpkYXRlOiAiMjAyNi0wOC0yNiINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHNwYWNlbGFiDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFKQ0KYGBgDQoNCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlIj5TZXJpZXMgZGUgVGllbXBvPC9zcGFuPg0KDQpDdWF0cm8gZWplcmNpY2lvcy4gTG9zIGRvcyBwcmltZXJvcyBjb21wYXJhbiBjaW5jbyBtb2RlbG9zIGRlIHByb27Ds3N0aWNvIHNvYnJlIHVuYSBtaXNtYQ0Kc2VyaWUgeSBlbGlnZW4gZWwgbWVqb3IgcG9yIE1BUEUuIExvcyBkb3Mgw7psdGltb3Mgc29uIGluZm9ybWVzIGdlcmVuY2lhbGVzIGNvbg0KZGVzY29tcG9zaWNpw7NuIGVzdGFjaW9uYWwuDQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5JbnN0YWxhciBwYXF1ZXRlcyB5IGxsYW1hciBsaWJyZXLDrWFzPC9zcGFuPg0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKCJmb3JlY2FzdCIpDQojaW5zdGFsbC5wYWNrYWdlcygicmVhZHhsIikNCmxpYnJhcnkoZm9yZWNhc3QpDQpsaWJyYXJ5KHJlYWR4bCkNCmBgYA0KDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+RnVuY2lvbmVzIHF1ZSBzZSB1c2FuIGVuIHRvZG8gZWwgZG9jdW1lbnRvPC9zcGFuPg0KDQpgYGB7cn0NCiMgUHJvbWVkaW8gTcOzdmlsIGRlIG9yZGVuIGsuIEVsIHByb27Ds3N0aWNvIGRlIHQgdXNhIGxhcyBrIG9ic2VydmFjaW9uZXMgcHJldmlhcy4NCnBtIDwtIGZ1bmN0aW9uKHksIGspew0KICBmIDwtIHJlcChOQSwgbGVuZ3RoKHkpKQ0KICBmb3IodCBpbiAoaysxKTpsZW5ndGgoeSkpIGZbdF0gPC0gbWVhbih5Wyh0LWspOih0LTEpXSkNCiAgZg0KfQ0KDQojIFByb21lZGlvIE3Ds3ZpbCBQb25kZXJhZG8uIEVsIHByaW1lciBwZXNvIGRlbCB2ZWN0b3IgdmEgYWwgZGF0byBpbm1lZGlhdGFtZW50ZQ0KIyBhbnRlcmlvciwgbm8gYWwgbcOhcyB2aWVqby4gTG9zIHBlc29zIGRlYmVuIHN1bWFyIDEuDQpwbXAgPC0gZnVuY3Rpb24oeSwgdyl7DQogIGsgPC0gbGVuZ3RoKHcpOyBmIDwtIHJlcChOQSwgbGVuZ3RoKHkpKQ0KICBmb3IodCBpbiAoaysxKTpsZW5ndGgoeSkpIGZbdF0gPC0gc3VtKHlbKHQtMSk6KHQtayldICogdykNCiAgZg0KfQ0KDQojIFN1YXZpemFkb3IgRXhwb25lbmNpYWwuIEFycmFuY2EgY29uIEYyID0geTEuDQpzdWF2IDwtIGZ1bmN0aW9uKHksIGEpew0KICBmIDwtIHJlcChOQSwgbGVuZ3RoKHkpKTsgZlsyXSA8LSB5WzFdDQogIGZvcih0IGluIDM6bGVuZ3RoKHkpKSBmW3RdIDwtIGEqeVt0LTFdICsgKDEtYSkqZlt0LTFdDQogIGYNCn0NCnN1YXZfc2lnIDwtIGZ1bmN0aW9uKHksIGEpew0KICBmIDwtIHlbMV07IGZvcih0IGluIDI6bGVuZ3RoKHkpKSBmIDwtIGEqeVt0XSArICgxLWEpKmY7IGYNCn0NCg0KIyBMYXMgc2VpcyBtw6l0cmljYXMuIENhZGEgbW9kZWxvIHNlIGRpdmlkZSBlbnRyZSBTVVMgcHJvcGlvcyBlcnJvcmVzLg0KbcOpdHJpY2FzIDwtIGZ1bmN0aW9uKHJlYWwsIHByb24pew0KICBvayA8LSAhaXMubmEocHJvbikgJiAhaXMubmEocmVhbCkNCiAgeSA8LSByZWFsW29rXTsgZiA8LSBwcm9uW29rXTsgZSA8LSB5IC0gZjsgbiA8LSBsZW5ndGgoZSkNCiAgYyhuPW4sIE1FPXN1bShlKS9uLCBNQUU9c3VtKGFicyhlKSkvbiwgTVNFPXN1bShlXjIpL24sDQogICAgUk1TRT1zcXJ0KHN1bShlXjIpL24pLCBNUEU9c3VtKGUveSoxMDApL24sIE1BUEU9c3VtKGFicyhlL3kqMTAwKSkvbikNCn0NCg0KIyDDjW5kaWNlcyBlc3RhY2lvbmFsZXMgcG9yIGVsIG3DqXRvZG8gZGUgcmF6w7NuIGFsIHByb21lZGlvIG3Ds3ZpbCBjZW50cmFkby4NCiMgU2Ugbm9ybWFsaXphbiBwYXJhIHF1ZSBzdW1lbiBlbCBuw7ptZXJvIGRlIHBlcmlvZG9zIGRlbCBjaWNsby4NCsOtbmRpY2VzX2VzdGFjaW9uYWxlcyA8LSBmdW5jdGlvbihzZXJpZSl7DQogIGYgPC0gZnJlcXVlbmN5KHNlcmllKQ0KICBjbWEgPC0gc3RhdHM6OmZpbHRlcihzZXJpZSwgYygwLjUsIHJlcCgxLCBmLTEpLCAwLjUpL2YsIHNpZGVzID0gMikNCiAgcmF6w7NuIDwtIGFzLm51bWVyaWMoc2VyaWUpIC8gYXMubnVtZXJpYyhjbWEpDQogIGNydWRvcyA8LSB0YXBwbHkocmF6w7NuLCBjeWNsZShzZXJpZSksIG1lYW4sIG5hLnJtID0gVFJVRSkNCiAgY3J1ZG9zICogZiAvIHN1bShjcnVkb3MpDQp9DQpgYGANCg0KLS0tDQoNCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQiPkVqZXJjaWNpbyAxLiBWZW50YXMgU2VtYW5hbGVzPC9zcGFuPg0KDQpJbnN0cnVjY2lvbmVzOiBtb2RlbGEgbGEgc2VyaWUgY29uIGxvcyBjaW5jbyBtb2RlbG9zIHZpc3RvcyBlbiBjbGFzZSwgYXJtYSBsYSB0YWJsYQ0KcmVzdW1lbiB5IGNvbmNsdXllLg0KDQpgYGB7cn0NCnNlbWFuYSA8LSBjKDE6MTIpDQp2YWxvciAgPC0gYygxNywyMSwxOSwyMywxOCwxNiwyMCwxOCwyMiwyMCwxNSwyMikNCnRzMSA8LSB0cyh2YWxvciwgYygyMDI1LDEpLCBmcmVxdWVuY3k9NTIpDQpwbG90KHRzMSwgeWxhYj0iVmFsb3IiLCB4bGFiPSJTZW1hbmEiLCBtYWluPSJWZW50YXMgc2VtYW5hbGVzIikNCmBgYA0KDQpgYGB7cn0NCiMgTW9kZWxvIDEuIE5haXZlDQpuYWl2ZTEgPC0gbmFpdmUodHMxLCBoPTYpOyBzdW1tYXJ5KG5haXZlMSkNCmYxX25haXZlIDwtIGMoTkEsIGhlYWQodmFsb3IsIC0xKSkNCg0KIyBNb2RlbG8gMi4gUHJvbWVkaW8gTcOzdmlsIChrPTMpDQpmMV9wbSA8LSBwbSh2YWxvciwgMykNCg0KIyBNb2RlbG8gMy4gUHJvbWVkaW8gTcOzdmlsIFBvbmRlcmFkbyAoaz0zLCBwZXNvcyAzLzYsIDIvNiB5IDEvNikNCmYxX3BtcCA8LSBwbXAodmFsb3IsIGMoMywyLDEpLzYpDQoNCiMgTW9kZWxvIDQuIFN1YXZpemFkb3IgRXhwb25lbmNpYWwgKGFsZmEgPSAwLjIpDQpmMV9zZXMgPC0gc3Vhdih2YWxvciwgMC4yKQ0KDQojIE1vZGVsbyA1LiBBUklNQQ0KYXJpbWExIDwtIGF1dG8uYXJpbWEodHMxKTsgYXJpbWExDQpmMV9hcmltYSA8LSBhcy5udW1lcmljKGZpdHRlZChhcmltYTEpKQ0KYGBgDQoNCmBgYHtyfQ0KdGFibGExIDwtIGRhdGEuZnJhbWUoU2VtYW5hPXNlbWFuYSwgUmVhbD12YWxvciwgTmFpdmU9ZjFfbmFpdmUsIFBNMz1mMV9wbSwNCiAgICAgICAgICAgICAgICAgICAgIFBNUDM9ZjFfcG1wLCBTRVM9ZjFfc2VzLCBBUklNQT1mMV9hcmltYSkNCmtuaXRyOjprYWJsZSh0YWJsYTEsIGRpZ2l0cz0yLCBjYXB0aW9uPSJQcm9uw7NzdGljb3MgZGUgY2FkYSBtb2RlbG8sIEVqZXJjaWNpbyAxIikNCg0KbW9kZWxvczEgPC0gbGlzdCgiMS4gTmFpdmUiPWYxX25haXZlLCAiMi4gUE0gaz0zIj1mMV9wbSwgIjMuIFBNUCBrPTMiPWYxX3BtcCwNCiAgICAgICAgICAgICAgICAgIjQuIFNFUyBhbGZhPTAuMiI9ZjFfc2VzLCAiNS4gQVJJTUEiPWYxX2FyaW1hKQ0KcmVzMSA8LSB0KHNhcHBseShtb2RlbG9zMSwgbcOpdHJpY2FzLCByZWFsPXZhbG9yKSkNCmtuaXRyOjprYWJsZShyZXMxLCBkaWdpdHM9NCwgY2FwdGlvbj0iVGFibGEgcmVzdW1lbiBkZSByZXN1bHRhZG9zLCBFamVyY2ljaW8gMSIpDQoNCnNpZzEgPC0gYyhOYWl2ZT10YWlsKHZhbG9yLDEpLCBQTTM9bWVhbih0YWlsKHZhbG9yLDMpKSwNCiAgICAgICAgICBQTVAzPXN1bShyZXYodGFpbCh2YWxvciwzKSkqYygzLDIsMSkvNiksIFNFUz1zdWF2X3NpZyh2YWxvciwwLjIpLA0KICAgICAgICAgIEFSSU1BPWFzLm51bWVyaWMoZm9yZWNhc3QoYXJpbWExLCBoPTEpJG1lYW4pKQ0Kcm91bmQoc2lnMSwgNCkNCmBgYA0KDQojIyMgQ29uY2x1c2nDs24gZGVsIEVqZXJjaWNpbyAxDQoNCkVsIE1BUEUgbcOhcyBiYWpvIGxvIGRhIGVsICoqQVJJTUEqKiBjb24gMTEuMTklLCB5IGF0csOhcyB2YW4gZWwgc3Vhdml6YWRvciBleHBvbmVuY2lhbA0KY29uIDEzLjQwJSwgZWwgcHJvbWVkaW8gbcOzdmlsIHNpbXBsZSBjb24gMTQuMzYlLCBlbCBwb25kZXJhZG8gY29uIDE1Ljk5JSB5IGhhc3RhIGVsDQpmaW5hbCBlbCBOYWl2ZSBjb24gMTkuMjQlLg0KDQpWYWxlIGxhIHBlbmEgdmVyIHF1w6kgbW9kZWxvIGVzY29nacOzIGVsIGF1dG8uYXJpbWEsIHBvcnF1ZSBzYWxpw7MgdW4gQVJJTUEoMCwwLDApIGNvbg0KbWVkaWEuIEVzbyBxdWllcmUgZGVjaXIgcXVlIGRlY2lkacOzIHF1ZSBsYSBzZXJpZSBlcyBwdXJvIHJ1aWRvIGFscmVkZWRvciBkZSB1biBuaXZlbCBmaWpvDQp5IHF1ZSBzdSBwcm9uw7NzdGljbyBwYXJhIHRvZGFzIGxhcyBzZW1hbmFzIGVzIGVsIG1pc21vIG7Dum1lcm8sIDE5LjI1LiBPIHNlYSBxdWUgZWwgbWVqb3INCm1vZGVsbyBhY2Fiw7Mgc2llbmRvIG5vIGhhY2VybGUgY2FzbyBhIG5hZGEgZGUgbG8gcmVjaWVudGUgeSBxdWVkYXJzZSBlbiBlbCBwcm9tZWRpby4NCg0KRXNvIGVuY2FqYSBjb24gZWwgcmVzdG8gZGUgbGEgdGFibGEuIEVsIG9yZGVuIHZhIGNvbiBlbCBwZXNvIHF1ZSBjYWRhIG1vZGVsbyBsZSBkYSBhbA0Kw7psdGltbyBkYXRvLCBwb3JxdWUgZWwgTmFpdmUgbGUgZGEgdG9kbyB5IGVzIGVsIHBlb3IsIGVsIHBvbmRlcmFkbyBsYSBtaXRhZCwgZWwgc2ltcGxlIHVuDQp0ZXJjaW8sIGVsIHN1YXZpemFkb3IgdW5hIHF1aW50YSBwYXJ0ZSB5IGVsIEFSSU1BIHByw6FjdGljYW1lbnRlIG5hZGEuIEVudHJlIG1lbm9zIGNhc28gbGUNCmhhY2VuIGFsIMO6bHRpbW8gZGF0bywgbWVqb3IgcHJvbm9zdGljYW4uIExhIHJhesOzbiBlcyBxdWUgbGEgc2VyaWUgc2UgbXVldmUgYWxyZWRlZG9yIGRlDQoxOSBzaW4gc3ViaXIgbmkgYmFqYXIsIGVudG9uY2VzIGVsIGJyaW5jbyBkZSB1bmEgc2VtYW5hIGEgb3RyYSBlcyBydWlkbyB5IGNyZWVybGUgbmFkYQ0KbcOhcyBtZXRlIGVycm9yLg0KDQpFbCBBUklNQSBzZSBldmFsw7phIHNvYnJlIGxhcyAxMiBzZW1hbmFzLCBtaWVudHJhcyBxdWUgbG9zIHByb21lZGlvcyBtw7N2aWxlcyBwaWVyZGVuIDMgeSBlbCBOYWl2ZQ0KcGllcmRlIDEsIHkgc3UgbWVkaWEgZXN0w6ENCmVzdGltYWRhIGNvbiBsb3MgbWlzbW9zIGRhdG9zIGNvbiBsb3MgcXVlIHNlIGxlIG1pZGUgZWwgZXJyb3IsIGFzw60gcXVlIGxhIGNvbXBhcmFjacOzbiBubw0KZXMgZGVsIHRvZG8gcGFyZWphLiBBdW4gY29uIGVzbywgbG9zIGNpbmNvIG1vZGVsb3MgYXB1bnRhbiBhIGxhIG1pc21hIGNvbmNsdXNpw7NuLg0KDQotLS0NCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+RWplcmNpY2lvIDIuIEhlcnNoZXknczwvc3Bhbj4NCg0KSW5zdHJ1Y2Npb25lczogbWlzbW8gYW7DoWxpc2lzIHF1ZSBlbCBlamVyY2ljaW8gMSBwZXJvIGNvbiBsYSBiYXNlDQpgVmVudGFzX0hpc3RvcmljYXNfTGVjaGl0YXMueGxzeGAuIFByb25vc3RpY2EgbG9zIHNpZ3VpZW50ZXMgNiBtZXNlcy4NCg0KYGBge3J9DQojIEVsIEV4Y2VsIHRyYWUgbG9zIHRyZXMgYcOxb3MgZW4gYmxvcXVlcyBkZSBkb3MgY29sdW1uYXMgdW5vIGp1bnRvIGFsIG90cm8NCiMgKDIwMTcgZW4gQS1CLCAyMDE4IGVuIEMtRCwgMjAxOSBlbiBFLUYpLCBwb3IgZXNvIHNlIGFwaWxhbi4NCnJ1dGEgPC0gIlZlbnRhc19IaXN0b3JpY2FzX0xlY2hpdGFzLnhsc3giDQpjcnVkbyA8LSByZWFkX2V4Y2VsKHJ1dGEsIGNvbF9uYW1lcz1GQUxTRSkNCg0KYmxvcXVlcyA8LSBsaXN0KGNydWRvWywxOjJdLCBjcnVkb1ssMzo0XSwgY3J1ZG9bLDU6Nl0pDQpkZjIgPC0gZG8uY2FsbChyYmluZCwgbGFwcGx5KGJsb3F1ZXMsIGZ1bmN0aW9uKGIpew0KICBuYW1lcyhiKSA8LSBjKCJNZXMiLCJWZW50YXMiKQ0KICBiIDwtIGJbIWlzLm5hKHN1cHByZXNzV2FybmluZ3MoYXMubnVtZXJpYyhiJFZlbnRhcykpKSwgXQ0KICBkYXRhLmZyYW1lKE1lcz1hcy5EYXRlKGFzLm51bWVyaWMoYiRNZXMpLCBvcmlnaW49IjE4OTktMTItMzAiKSwNCiAgICAgICAgICAgICBWZW50YXM9YXMubnVtZXJpYyhiJFZlbnRhcykpDQp9KSkNCmRmMiA8LSBkZjJbb3JkZXIoZGYyJE1lcyksIF0NCnZlbnRhcyA8LSBkZjIkVmVudGFzDQp0czIgPC0gdHModmVudGFzLCBjKDIwMTcsMSksIGZyZXF1ZW5jeT0xMikNCnN0cihkZjIpDQpwbG90KHRzMiwgeWxhYj0iVmVudGFzIGVuIG1pbGVzIGRlIFVTRCIsIHhsYWI9IkHDsW8iLA0KICAgICBtYWluPSJWZW50YXMgaGlzdMOzcmljYXMgZGUgbGVjaGUgc2Fib3JpemFkYSBIZXJzaGV5IE3DqXhpY28iKQ0KYGBgDQoNCmBgYHtyfQ0KIyBNb2RlbG8gMS4gTmFpdmUNCm5haXZlMiA8LSBuYWl2ZSh0czIsIGg9Nik7IHN1bW1hcnkobmFpdmUyKQ0KZjJfbmFpdmUgPC0gYyhOQSwgaGVhZCh2ZW50YXMsIC0xKSkNCg0KIyBNb2RlbG8gMi4gUHJvbWVkaW8gTcOzdmlsIChrPTMpDQpmMl9wbSA8LSBwbSh2ZW50YXMsIDMpDQoNCiMgTW9kZWxvIDMuIFByb21lZGlvIE3Ds3ZpbCBQb25kZXJhZG8gKGs9MywgcGVzb3MgMy82LCAyLzYgeSAxLzYpDQpmMl9wbXAgPC0gcG1wKHZlbnRhcywgYygzLDIsMSkvNikNCg0KIyBNb2RlbG8gNC4gU3Vhdml6YWRvciBFeHBvbmVuY2lhbCAoYWxmYSA9IDAuMikNCmYyX3NlcyA8LSBzdWF2KHZlbnRhcywgMC4yKQ0KDQojIE1vZGVsbyA1LiBBUklNQQ0KYXJpbWEyIDwtIGF1dG8uYXJpbWEodHMyKTsgYXJpbWEyDQpmMl9hcmltYSA8LSBhcy5udW1lcmljKGZpdHRlZChhcmltYTIpKQ0KDQpwcm9uw7NzdGljbzIgPC0gZm9yZWNhc3QoYXJpbWEyLCBsZXZlbD1jKDk1KSwgaD02KQ0KcHJvbsOzc3RpY28yDQpwbG90KHByb27Ds3N0aWNvMiwgeWxhYj0iVmVudGFzIGVuIG1pbGVzIGRlIFVTRCIsIHhsYWI9IkHDsW8iLA0KICAgICBtYWluPSJQcm9uw7NzdGljbyBhIDYgbWVzZXMgY29uIEFSSU1BIikNCmBgYA0KDQpgYGB7cn0NCm1vZGVsb3MyIDwtIGxpc3QoIjEuIE5haXZlIj1mMl9uYWl2ZSwgIjIuIFBNIGs9MyI9ZjJfcG0sICIzLiBQTVAgaz0zIj1mMl9wbXAsDQogICAgICAgICAgICAgICAgICI0LiBTRVMgYWxmYT0wLjIiPWYyX3NlcywgIjUuIEFSSU1BIj1mMl9hcmltYSkNCnJlczIgPC0gdChzYXBwbHkobW9kZWxvczIsIG3DqXRyaWNhcywgcmVhbD12ZW50YXMpKQ0Ka25pdHI6OmthYmxlKHJlczIsIGRpZ2l0cz00LCBjYXB0aW9uPSJUYWJsYSByZXN1bWVuIGRlIHJlc3VsdGFkb3MsIEVqZXJjaWNpbyAyIikNCg0KIyBQcm9tZWRpb3MgYW51YWxlcywgcGFyYSB2ZXIgbGEgdGVuZGVuY2lhDQp0YXBwbHkodmVudGFzLCByZXAoMjAxNzoyMDE5LCBlYWNoPTEyKSwgbWVhbikNCmBgYA0KDQojIyMgQ29uY2x1c2nDs24gZGVsIEVqZXJjaWNpbyAyDQoNCkVsIG1lam9yIGVzIGVsICoqQVJJTUEqKiwgY29uIGVsIE1BUEUgbcOhcyBiYWpvIHBvciBtdWNobywgeSBlbCBwcm9uw7NzdGljbyBkZSBsb3Mgc2Vpcw0KbWVzZXMgcXVlIHBpZGUgZWwgZWplcmNpY2lvIGVzIGVsIHF1ZSBzYWxlIGVuIGxhIHRhYmxhIGRlIGFycmliYS4NCg0KR2FuYSBwb3JxdWUgZXMgZWwgw7puaWNvIHF1ZSB0b21hIGVuIGN1ZW50YSBxdWUgbGEgc2VyaWUgdmEgc3ViaWVuZG8uIExvcyBwcm9tZWRpb3MgZGUNCmNhZGEgYcOxbyB2YW4gZGUgMjYsODM4IGVuIDIwMTcgYSAzMCw1NjggZW4gMjAxOCB5IDMzLDgzOCBlbiAyMDE5LCB5IGNvbiBlc2UgY3JlY2ltaWVudG8NCmxvcyBtb2RlbG9zIHF1ZSBuYWRhIG3DoXMgcHJvbWVkaWFuIGxvIHF1ZSB5YSBwYXPDsyBzaWVtcHJlIHNlIHF1ZWRhbiBjb3J0b3MuIFNlIG5vdGEgZW4NCnF1ZSBsb3MgY3VhdHJvIG1vZGVsb3Mgc2ltcGxlcyB0aWVuZW4gZWwgTUUgcG9zaXRpdm8sIG8gc2VhIHF1ZSBwcm9ub3N0aWNhbiBwb3IgZGViYWpvDQpkZWwgdmFsb3IgcmVhbCBtZXMgdHJhcyBtZXMuDQoNClkgYXF1w60gZWwgb3JkZW4gZW50cmUgbG9zIG1vZGVsb3Mgc2ltcGxlcyBzZSB2b2x0ZWEgcmVzcGVjdG8gYWwgRWplcmNpY2lvIDEuIEVsDQpzdWF2aXphZG9yIGNvbiBhbGZhIGRlIDAuMiBwYXNhIGRlIHNlciBlbCBtZWpvciBhIHNlciBlbCBwZW9yLCB5IGVsIHBvbmRlcmFkbyBsZSBnYW5hIGFsDQpzaW1wbGUuIEN1YW5kbyBsYSBzZXJpZSB0cmFlIGRpcmVjY2nDs24gZWwgZGF0byBtw6FzIHJlY2llbnRlIHPDrSBzaXJ2ZSBwYXJhIHNhYmVyIHF1w6kNCnNpZ3VlLCBhc8OtIHF1ZSBzdWF2aXphciBkZSBtw6FzIGVzdG9yYmEgZW4gbHVnYXIgZGUgYXl1ZGFyLg0KDQotLS0NCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZCI+RWplcmNpY2lvIDMuIFZpbnRhZ2UgUmVzdGF1cmFudDwvc3Bhbj4NCg0KS2FyZW4gUGF5bmUgcXVpZXJlIHNhYmVyIGPDs21vIHNlIGNvbXBvcnRhbiBsYXMgdmVudGFzIGRlbCByZXN0YXVyYW50ZSB5IHF1w6kgZXNwZXJhciBkZWwNCmN1YXJ0byBhw7FvLiBMb3MgZGF0b3Mgc29uIGxhcyB2ZW50YXMgbWVuc3VhbGVzIGRlIGFsaW1lbnRvcyB5IGJlYmlkYXMgZW4gbWlsZXMgZGUNCmTDs2xhcmVzIGRlIGxvcyB0cmVzIHByaW1lcm9zIGHDsW9zLg0KDQpgYGB7cn0NCmHDsW8xIDwtIGMoMjQyLDIzNSwyMzIsMTc4LDE4NCwxNDAsMTQ1LDE1MiwxMTAsMTMwLDE1MiwyMDYpDQphw7FvMiA8LSBjKDI2MywyMzgsMjQ3LDE5MywxOTMsMTQ5LDE1NywxNjEsMTIyLDEzMCwxNjcsMjMwKQ0KYcOxbzMgPC0gYygyODIsMjU1LDI2NSwyMDUsMjEwLDE2MCwxNjYsMTc0LDEyNiwxNDgsMTczLDIzNSkNCg0KdmludGFnZSA8LSB0cyhjKGHDsW8xLCBhw7FvMiwgYcOxbzMpLCBzdGFydD1jKDEsMSksIGZyZXF1ZW5jeT0xMikNCm1lc2VzIDwtIGMoIkVuZXJvIiwiRmVicmVybyIsIk1hcnpvIiwiQWJyaWwiLCJNYXlvIiwiSnVuaW8iLA0KICAgICAgICAgICAiSnVsaW8iLCJBZ29zdG8iLCJTZXB0aWVtYnJlIiwiT2N0dWJyZSIsIk5vdmllbWJyZSIsIkRpY2llbWJyZSIpDQoNCmtuaXRyOjprYWJsZShkYXRhLmZyYW1lKE1lcz1tZXNlcywgYEHDsW8gMWA9YcOxbzEsIGBBw7FvIDJgPWHDsW8yLCBgQcOxbyAzYD1hw7FvMywNCiAgICAgICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzPUZBTFNFKSwNCiAgICAgICAgICAgICBjYXB0aW9uPSJWZW50YXMgZGUgYWxpbWVudG9zIHkgYmViaWRhcyBkZWwgcmVzdGF1cmFudGUgVmludGFnZSwgbWlsZXMgZGUgZMOzbGFyZXMiKQ0KYGBgDQoNCiMjIDEuIEdyw6FmaWNhIGRlIGxhIHNlcmllIGRlIHRpZW1wbw0KDQpgYGB7cn0NCnBsb3QodmludGFnZSwgeWxhYj0iVmVudGFzIGVuIG1pbGVzIGRlIFVTRCIsIHhsYWI9IkHDsW8gZGUgb3BlcmFjacOzbiIsDQogICAgIG1haW49IlZlbnRhcyBtZW5zdWFsZXMgZGVsIFZpbnRhZ2UgUmVzdGF1cmFudCIsIGx3ZD0yLCBjb2w9IiMyZjUxODMiKQ0KcG9pbnRzKHZpbnRhZ2UsIHBjaD0xOSwgY2V4PTAuNSwgY29sPSIjMmY1MTgzIikNCmdyaWQoKQ0KYGBgDQoNCkVuIGxhIGdyw6FmaWNhIHNlIHZlbiBkb3MgY29zYXMgYWwgbWlzbW8gdGllbXBvLiBMYSBwcmltZXJhIGVzIHVuIHBhdHLDs24gcXVlIHNlIHJlcGl0ZQ0KaWd1YWwgY2FkYSBhw7FvLCBjb24gbGFzIHZlbnRhcyBtw6FzIGFsdGFzIGVuIGVuZXJvLCBmZWJyZXJvIHkgbWFyem8geSBsYXMgbcOhcyBiYWphcyBlbg0Kc2VwdGllbWJyZS4gTGEgc2VndW5kYSBlcyBxdWUgZXNlIG1pc21vIHBhdHLDs24gdmEgc3ViaWVuZG8gZGUgbml2ZWwgYcOxbyBjb24gYcOxbywgbyBzZWENCnF1ZSBhZGVtw6FzIGRlbCBjaWNsbyBoYXkgdGVuZGVuY2lhLg0KDQpQYXJhIENhcHRpdmEgSXNsYW5kIHRpZW5lIHRvZG8gZWwgc2VudGlkbywgcG9ycXVlIGVzIGxhIHRlbXBvcmFkYSBhbHRhIGRlIHR1cmlzbW8gZW4NCkZsb3JpZGEgY3VhbmRvIGVuIGVsIG5vcnRlIGhhY2UgZnLDrW8sIHkgc2VwdGllbWJyZSBjYWUgZW4gcGxlbmEgdGVtcG9yYWRhIGRlIGh1cmFjYW5lcywNCnF1ZSBlcyBjdWFuZG8gc2UgdmFjw61hIGxhIGlzbGEuDQoNCiMjIDIuIMONbmRpY2VzIGVzdGFjaW9uYWxlcw0KDQpgYGB7cn0NCmllIDwtIMOtbmRpY2VzX2VzdGFjaW9uYWxlcyh2aW50YWdlKQ0Ka25pdHI6OmthYmxlKGRhdGEuZnJhbWUoTWVzPW1lc2VzLCBgw41uZGljZSBlc3RhY2lvbmFsYD1yb3VuZChhcy5udW1lcmljKGllKSw0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzPUZBTFNFKSwNCiAgICAgICAgICAgICBjYXB0aW9uPSLDjW5kaWNlcyBlc3RhY2lvbmFsZXMgcG9yIGVsIG3DqXRvZG8gZGUgcmF6w7NuIGFsIHByb21lZGlvIG3Ds3ZpbCBjZW50cmFkbyIpDQpjYXQoIkxvcyDDrW5kaWNlcyBzdW1hbjoiLCBzdW0oaWUpLCAiXG4iKQ0KYGBgDQoNCmBgYHtyfQ0KYmFycGxvdChhcy5udW1lcmljKGllKSwgbmFtZXMuYXJnPXN1YnN0cihtZXNlcywxLDMpLCBsYXM9MiwNCiAgICAgICAgY29sPWlmZWxzZShhcy5udW1lcmljKGllKT4xLCAiIzFhN2YzNyIsICIjYzg1MDJmIiksDQogICAgICAgIG1haW49IsONbmRpY2UgZXN0YWNpb25hbCBwb3IgbWVzIiwgeWxhYj0iw41uZGljZSIpDQphYmxpbmUoaD0xLCBsd2Q9MiwgbHR5PTIpDQpgYGANCg0KTG9zIMOtbmRpY2VzIHPDrSB0aWVuZW4gc2VudGlkbyBpbnR1aXRpdm8geSBiYXN0YW50ZS4gRW5lcm8gc2FsZSBlbiAxLjQ0MzYsIG8gc2VhIHF1ZSB1bg0KZW5lcm8gdmVuZGUgNDQlIG3DoXMgcXVlIHVuIG1lcyBwcm9tZWRpbyBkZWwgYcOxbywgeSBtYXJ6byB5IGZlYnJlcm8gYW5kYW4gY2VyY2EgY29uIDEuMzQ0MQ0KeSAxLjI5OTcuIExvcyB0cmVzIG1lc2VzIGRlIGludmllcm5vIHNvbiBsYSB0ZW1wb3JhZGEgYWx0YSB5IGFow60gZXMgZG9uZGUgZWwgcmVzdGF1cmFudGUNCmhhY2Ugc3UgZGluZXJvLg0KDQpEZWwgb3RybyBsYWRvIHNlcHRpZW1icmUgc2FsZSBlbiAwLjYyODAsIHF1ZSBlcyB2ZW5kZXIgMzclIG1lbm9zIHF1ZSB1biBtZXMgbm9ybWFsLCB5DQpvY3R1YnJlIGVuIDAuNzAwMy4gRXNlIGVzIGVsIGZvbmRvIGRlbCBhw7FvLg0KDQpMYSBkaWZlcmVuY2lhIGVudHJlIGVsIG1lam9yIHkgZWwgcGVvciBtZXMgZXMgZW5vcm1lLCBwb3JxdWUgZW5lcm8gdmVuZGUgbcOhcyBkZWwgZG9ibGUNCnF1ZSBzZXB0aWVtYnJlIGNvbiBsb3MgbWlzbW9zIGNvc3RvcyBmaWpvcyBlbmNpbWEuIEVzbyB5YSBlcyB1bmEgcmVjb21lbmRhY2nDs24gcG9yIHPDrQ0Kc29sYSBwYXJhIEthcmVuLCBsb3MgbWVzZXMgYnVlbm9zIHRpZW5lbiBxdWUgY2FyZ2FyIGNvbiBsb3MgbWFsb3MsIHkgY29udmllbmUgcGxhbmVhciBlbA0KcGVyc29uYWwgeSBlbCBpbnZlbnRhcmlvIG1lcyBwb3IgbWVzIHkgbm8gY29uIHVuIHByb21lZGlvIGFudWFsLg0KDQojIyAzLiBTZXJpZSBkZXNlc3RhY2lvbmFsaXphZGENCg0KYGBge3J9DQpkZXNlc3RhY2lvbmFsaXphZGEgPC0gYXMubnVtZXJpYyh2aW50YWdlKSAvIGFzLm51bWVyaWMoaWVbY3ljbGUodmludGFnZSldKQ0KdHNfZGVzIDwtIHRzKGRlc2VzdGFjaW9uYWxpemFkYSwgc3RhcnQ9YygxLDEpLCBmcmVxdWVuY3k9MTIpDQoNCnBsb3QodmludGFnZSwgeWxhYj0iVmVudGFzIGVuIG1pbGVzIGRlIFVTRCIsIHhsYWI9IkHDsW8gZGUgb3BlcmFjacOzbiIsDQogICAgIG1haW49IlNlcmllIG9yaWdpbmFsIGNvbnRyYSBzZXJpZSBkZXNlc3RhY2lvbmFsaXphZGEiLCBjb2w9IiNiOGM0ZDQiLCBsd2Q9MikNCmxpbmVzKHRzX2RlcywgY29sPSIjYzg1MDJmIiwgbHdkPTIpDQpsZWdlbmQoInRvcHJpZ2h0IiwgYygiT3JpZ2luYWwiLCJEZXNlc3RhY2lvbmFsaXphZGEiKSwNCiAgICAgICBjb2w9YygiI2I4YzRkNCIsIiNjODUwMmYiKSwgbHdkPTIsIGJ0eT0ibiIpDQpncmlkKCkNCmBgYA0KDQpgYGB7cn0NCnQgPC0gMTozNg0KdGVuZGVuY2lhIDwtIGxtKGRlc2VzdGFjaW9uYWxpemFkYSB+IHQpDQpzdW1tYXJ5KHRlbmRlbmNpYSkNCmBgYA0KDQpTw60gaGF5IHRlbmRlbmNpYSB5IGVzIGNsYXJhLiBBbCBxdWl0YXJsZSBlbCBlZmVjdG8gZGUgbG9zIG1lc2VzLCBsYSBzZXJpZSBxdWVkYSBlbiB1bmENCmzDrW5lYSBxdWUgc3ViZSBwYXJlam8sIHkgbGEgcmVncmVzacOzbiBkYSB1bmEgcGVuZGllbnRlIGRlIDEuMDIxMyBjb24gdW4gUsKyIGRlIDAuOTIwNC4NCg0KRXNvIHF1aWVyZSBkZWNpciBxdWUgZWwgcmVzdGF1cmFudGUgY3JlY2UgYWxyZWRlZG9yIGRlIG1pbCBkw7NsYXJlcyBkZSB2ZW50YSBjYWRhIG1lcywgbw0Kc2VhIHVub3MgMTIsMjU1IGTDs2xhcmVzIG3DoXMgYWwgYcOxby4gRWwgbmVnb2NpbyBlc3TDoSBjcmVjaWVuZG8gZGUgdmVyZGFkLCBubyBlcyBxdWUgdW5vcw0KbWVzZXMgdGFwZW4gYSBvdHJvcy4NCg0KIyMgNC4gUHJvbsOzc3RpY28gZGVsIGHDsW8gNCBwb3IgZGVzY29tcG9zaWNpw7NuDQoNCmBgYHtyfQ0KdDQgPC0gMzc6NDgNCnRlbmQ0IDwtIGNvZWYodGVuZGVuY2lhKVsxXSArIGNvZWYodGVuZGVuY2lhKVsyXSp0NA0KcHJvbl9kZXNjb21wb3NpY2nDs24gPC0gdGVuZDQgKiBhcy5udW1lcmljKGllKQ0KDQprbml0cjo6a2FibGUoZGF0YS5mcmFtZShNZXM9bWVzZXMsIFRlbmRlbmNpYT1yb3VuZCh0ZW5kNCwyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGDDjW5kaWNlYD1yb3VuZChhcy5udW1lcmljKGllKSw0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGBQcm9uw7NzdGljb2A9cm91bmQocHJvbl9kZXNjb21wb3NpY2nDs24sMiksIGNoZWNrLm5hbWVzPUZBTFNFKSwNCiAgICAgICAgICAgICBjYXB0aW9uPSJQcm9uw7NzdGljbyBkZWwgYcOxbyA0IHBvciBkZXNjb21wb3NpY2nDs24sIG1pbGVzIGRlIGTDs2xhcmVzIikNCmNhdCgiVG90YWwgZGVsIGHDsW8gNCBlc3RpbWFkbzoiLCByb3VuZChzdW0ocHJvbl9kZXNjb21wb3NpY2nDs24pLDIpLCAibWlsZXMgZGUgZMOzbGFyZXNcbiIpDQpgYGANCg0KIyMgNS4gUHJvbsOzc3RpY28gZGVsIGHDsW8gNCBjb24gcmVncmVzacOzbiBkZSB2YXJpYWJsZXMgZmljdGljaWFzDQoNCmBgYHtyfQ0KbWVzX2ZhY3RvciA8LSBmYWN0b3IocmVwKG1lc2VzLCAzKSwgbGV2ZWxzPW1lc2VzKQ0KcmVncmVzacOzbl9kdW1taWVzIDwtIGxtKGFzLm51bWVyaWModmludGFnZSkgfiB0ICsgbWVzX2ZhY3RvcikNCnN1bW1hcnkocmVncmVzacOzbl9kdW1taWVzKQ0KYGBgDQoNCmBgYHtyfQ0KbnVldm9zIDwtIGRhdGEuZnJhbWUodD10NCwgbWVzX2ZhY3Rvcj1mYWN0b3IobWVzZXMsIGxldmVscz1tZXNlcykpDQpwcm9uX2R1bW1pZXMgPC0gYXMubnVtZXJpYyhwcmVkaWN0KHJlZ3Jlc2nDs25fZHVtbWllcywgbnVldm9zKSkNCg0Ka25pdHI6OmthYmxlKGRhdGEuZnJhbWUoTWVzPW1lc2VzLA0KICAgICAgICAgICAgICAgICAgICAgICAgYERlc2NvbXBvc2ljacOzbmA9cm91bmQocHJvbl9kZXNjb21wb3NpY2nDs24sMiksDQogICAgICAgICAgICAgICAgICAgICAgICBgRHVtbWllc2A9cm91bmQocHJvbl9kdW1taWVzLDIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgYERpZmVyZW5jaWFgPXJvdW5kKHByb25fZHVtbWllcy1wcm9uX2Rlc2NvbXBvc2ljacOzbiwyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzPUZBTFNFKSwNCiAgICAgICAgICAgICBjYXB0aW9uPSJMb3MgZG9zIHByb27Ds3N0aWNvcyBkZWwgYcOxbyA0IGNvbXBhcmFkb3MiKQ0KY2F0KCJUb3RhbCBwb3IgZGVzY29tcG9zaWNpw7NuOiIsIHJvdW5kKHN1bShwcm9uX2Rlc2NvbXBvc2ljacOzbiksMiksICJcbiIpDQpjYXQoIlRvdGFsIHBvciBkdW1taWVzOiAgICAgICIsIHJvdW5kKHN1bShwcm9uX2R1bW1pZXMpLDIpLCAiXG4iKQ0KYGBgDQoNCiMjIDYuIFJlc3VtZW4sIGVycm9yIGRlIHByb27Ds3N0aWNvIHkgY29uY2x1c2lvbmVzDQoNCmBgYHtyfQ0KcGxvdCh0cyhjKGFzLm51bWVyaWModmludGFnZSksIHByb25fZGVzY29tcG9zaWNpw7NuKSwgc3RhcnQ9YygxLDEpLCBmcmVxdWVuY3k9MTIpLA0KICAgICBjb2w9IiMyZjUxODMiLCBsd2Q9MiwgeWxhYj0iVmVudGFzIGVuIG1pbGVzIGRlIFVTRCIsIHhsYWI9IkHDsW8gZGUgb3BlcmFjacOzbiIsDQogICAgIG1haW49Ikhpc3RvcmlhIHkgcHJvbsOzc3RpY28gZGVsIGHDsW8gNCIpDQpsaW5lcyh0cyhjKHJlcChOQSwzNiksIHByb25fZHVtbWllcyksIHN0YXJ0PWMoMSwxKSwgZnJlcXVlbmN5PTEyKSwgY29sPSIjZDlhMDIxIiwgbHdkPTIpDQphYmxpbmUodj00LCBsdHk9MikNCmxlZ2VuZCgidG9wcmlnaHQiLCBjKCJIaXN0w7NyaWNvIHkgZGVzY29tcG9zaWNpw7NuIiwiRHVtbWllcyIpLA0KICAgICAgIGNvbD1jKCIjMmY1MTgzIiwiI2Q5YTAyMSIpLCBsd2Q9MiwgYnR5PSJuIikNCmdyaWQoKQ0KYGBgDQoNCmBgYHtyfQ0KcmVhbF9lbmVybyA8LSAyOTUNCmVfZGVzYyA8LSByZWFsX2VuZXJvIC0gcHJvbl9kZXNjb21wb3NpY2nDs25bMV0NCmVfZHVtbSA8LSByZWFsX2VuZXJvIC0gcHJvbl9kdW1taWVzWzFdDQoNCmtuaXRyOjprYWJsZShkYXRhLmZyYW1lKA0KICBNw6l0b2RvID0gYygiRGVzY29tcG9zaWNpw7NuIiwiUmVncmVzacOzbiBjb24gZHVtbWllcyIpLA0KICBQcm9uw7NzdGljbyA9IHJvdW5kKGMocHJvbl9kZXNjb21wb3NpY2nDs25bMV0sIHByb25fZHVtbWllc1sxXSksMiksDQogIFJlYWwgPSByZWFsX2VuZXJvLA0KICBFcnJvciA9IHJvdW5kKGMoZV9kZXNjLCBlX2R1bW0pLDIpLA0KICBgRXJyb3IgJWAgPSByb3VuZChjKGVfZGVzYywgZV9kdW1tKS9yZWFsX2VuZXJvKjEwMCwgMiksDQogIGNoZWNrLm5hbWVzPUZBTFNFKSwNCiAgY2FwdGlvbj0iRXJyb3IgZGUgcHJvbsOzc3RpY28gZGUgZW5lcm8gZGVsIGHDsW8gNCIpDQoNCmNhdCgiUsKyIGRlIGxhIHJlZ3Jlc2nDs24gY29uIGR1bW1pZXM6Iiwgcm91bmQoc3VtbWFyeShyZWdyZXNpw7NuX2R1bW1pZXMpJHIuc3F1YXJlZCw0KSwgIlxuIikNCmNhdCgiUsKyIGRlIGxhIHRlbmRlbmNpYSBkZXNlc3RhY2lvbmFsaXphZGE6Iiwgcm91bmQoc3VtbWFyeSh0ZW5kZW5jaWEpJHIuc3F1YXJlZCw0KSwgIlxuIikNCmBgYA0KDQojIyMgSW5mb3JtZSBwYXJhIEthcmVuDQoNCkxhcyB2ZW50YXMgZGVsIFZpbnRhZ2UgdHJhZW4gZG9zIGNvc2FzIGVuY2ltYSBhbCBtaXNtbyB0aWVtcG8sIHVuIHBhdHLDs24gZGUgdGVtcG9yYWRhDQptdXkgbWFyY2FkbyB5IHVuYSB0ZW5kZW5jaWEgZGUgY3JlY2ltaWVudG8uIEVsIHBhdHLDs24gZXMgZWwgcXVlIHNhbHRhIGEgbGEgdmlzdGEsIHBvcnF1ZQ0KZW5lcm8gdmVuZGUgNDQlIGFycmliYSBkZWwgbWVzIHByb21lZGlvIHkgc2VwdGllbWJyZSAzNyUgYWJham8sIHkgZXNhIGRpZmVyZW5jaWEgc2UNCnJlcGl0ZSBpZ3VhbGl0YSBsb3MgdHJlcyBhw7Fvcy4gTGEgdGVuZGVuY2lhIGVzdMOhIHRhcGFkYSBwb3IgZXNlIHBhdHLDs24geSBzb2xvIHNlIHZlIGFsDQpkZXNlc3RhY2lvbmFsaXphciwgeSBhaMOtIHF1ZWRhIHVuYSBsw61uZWEgbGltcGlhIHF1ZSBzdWJlIDEsMDIxIGTDs2xhcmVzIGNhZGEgbWVzLCBvIHNlYQ0KY29tbyAxMiBtaWwgZMOzbGFyZXMgbcOhcyBkZSB2ZW50YSBjYWRhIGHDsW8uDQoNCkxvcyBkb3MgbcOpdG9kb3MgcHJvbm9zdGljYW4gY2FzaSBsbyBtaXNtbyBwYXJhIGVsIGHDsW8gNC4gUG9yIGRlc2NvbXBvc2ljacOzbiBzYWxlDQoyLDU0NS43NiBtaWwgZMOzbGFyZXMgZW4gZWwgYcOxbyB5IHBvciByZWdyZXNpw7NuIGNvbiBkdW1taWVzIDIsNTQ0LjY3LCBvIHNlYSBxdWUgc2UNCnNlcGFyYW4gcG9yIG1pbCBkw7NsYXJlcyBzb2JyZSBkb3MgbWlsbG9uZXMgeSBtZWRpby4gRG9uZGUgc8OtIGRpZmllcmVuIGVzIG1lcyBwb3IgbWVzLg0KDQpTb2JyZSBlbmVybywgZWwgcHJvbsOzc3RpY28gcG9yIGRlc2NvbXBvc2ljacOzbiBmdWUgZGUgMjk5LjAyIG1pbCB5IGxhcyB2ZW50YXMgcmVhbGVzDQpzYWxpZXJvbiBlbiAyOTUgbWlsLCBhc8OtIHF1ZSBzZSBwYXPDsyBwb3IgNCBtaWwgZMOzbGFyZXMsIHVuIDEuNCUuIEVsIGRlIGR1bW1pZXMgZGlvIDI4Ni43NQ0KbWlsIHkgc2UgcXVlZMOzIGNvcnRvIHBvciA4LjI1IG1pbCwgdW4gMi44JS4gTG9zIGRvcyBsZSBhdGluYXJvbiBiaWVuLCB5IHNpIGEgS2FyZW4gbGUNCnByZW9jdXBhIGVzYSBkaWZlcmVuY2lhIGxvIHF1ZSBoYXkgcXVlIGRlY2lybGUgZXMgcXVlIDQgbWlsIGTDs2xhcmVzIHNvYnJlIDI5NSBtaWwgZXMNCnJ1aWRvIG5vcm1hbCBkZSB1biBtZXMgeSBubyB1bmEgZmFsbGEgZGVsIG3DqXRvZG8uDQoNCkxvIHF1ZSBzw60gbGUgcmVjb21lbmRhcsOtYSBwYXJhIG5vIHF1ZWRhcnNlIGNvbiBsYSBkdWRhIG1lcyBhIG1lcyBlcyBkZWphciBkZSB2ZXIgZWwNCnByb27Ds3N0aWNvIGNvbW8gdW4gbsO6bWVybyBzb2xvLiBUcmVzIGNvc2FzIGNvbmNyZXRhcy4gUXVlIHNlIG1hbmVqZSB1biByYW5nbyBlbiB2ZXogZGUNCnVuYSBjaWZyYSwgcG9ycXVlIGVsIGVycm9yIHTDrXBpY28gZGVsIG1vZGVsbyBzZSBwdWVkZSBjYWxjdWxhciB5IGxlIGRhIHVuIHBpc28geSB1bg0KdGVjaG8gcGFyYSBwbGFuZWFyLiBRdWUgc2UgcmVjYWxjdWxlbiBsb3Mgw61uZGljZXMgZXN0YWNpb25hbGVzIGNhZGEgYcOxbyBjb25mb3JtZSBlbnRyZW4NCmRhdG9zIG51ZXZvcywgeWEgcXVlIGNvbiB0cmVzIGHDsW9zIGFwZW5hcyB2YW4gdHJlcyBvYnNlcnZhY2lvbmVzIHBvciBtZXMgeSBjb24gY2luY28gbw0Kc2VpcyBsYSBlc3RpbWFjacOzbiBzZSB2dWVsdmUgbcOhcyBmaXJtZS4gWSBxdWUgc2UgbGxldmUgcmVnaXN0cm8gZGVsIGVycm9yIG1lcyBjb24gbWVzLA0KcG9ycXVlIHNpIGxvcyBlcnJvcmVzIGVtcGllemFuIGEgc2FsaXIgdG9kb3MgZGVsIG1pc21vIGxhZG8gcXVpZXJlIGRlY2lyIHF1ZSBlbCBuZWdvY2lvDQpjYW1iacOzIGRlIG5pdmVsIHkgZWwgbW9kZWxvIHNlIHRpZW5lIHF1ZSByZWFqdXN0YXIuDQoNCi0tLQ0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkIj5FamVyY2ljaW8gNC4gQ2FybHNvbiBEZXBhcnRtZW50IFN0b3JlPC9zcGFuPg0KDQpDYXJsc29uIGVzdHV2byBjZXJyYWRhIGRlIHNlcHRpZW1icmUgYSBkaWNpZW1icmUgZGVsIGHDsW8gNSBwb3IgZWwgaHVyYWPDoW4uIEhheSBxdWUNCmVzdGltYXIgY3XDoW50byBoYWJyw61hIHZlbmRpZG8gZGUgbm8gaGFiZXIgcGFzYWRvLCB5IHNpIGxlIGNvcnJlc3BvbmRlIGFsZ28gZGVsIGF1bWVudG8NCmRlIGFjdGl2aWRhZCBjb21lcmNpYWwgcXVlIGh1Ym8gZW4gbGEgem9uYSBkZXNwdcOpcy4NCg0KYGBge3J9DQojIFZlbnRhcyBkZSBDYXJsc29uIGVuIGxvcyA0OCBtZXNlcyBwcmV2aW9zIGFsIGh1cmFjw6FuLCBkZSBzZXB0aWVtYnJlIGRlbCBhw7FvIDENCiMgYSBhZ29zdG8gZGVsIGHDsW8gNSwgZW4gbWlsbG9uZXMgZGUgZMOzbGFyZXMNCmNhcmxzb24gPC0gYygxLjcxLDEuOTAsMi43NCw0LjIwLA0KICAgICAgICAgICAgIDEuNDUsMS44MCwyLjAzLDEuOTksMi4zMiwyLjIwLDIuMTMsMi40MywxLjkwLDIuMTMsMi41Niw0LjE2LA0KICAgICAgICAgICAgIDIuMzEsMS44OSwyLjAyLDIuMjMsMi4zOSwyLjE0LDIuMjcsMi4yMSwxLjg5LDIuMjksMi44Myw0LjA0LA0KICAgICAgICAgICAgIDIuMzEsMS45OSwyLjQyLDIuNDUsMi41NywyLjQyLDIuNDAsMi41MCwyLjA5LDIuNTQsMi45Nyw0LjM1LA0KICAgICAgICAgICAgIDIuNTYsMi4yOCwyLjY5LDIuNDgsMi43MywyLjM3LDIuMzEsMi4yMykNCg0KIyBWZW50YXMgZGUgdG9kYXMgbGFzIHRpZW5kYXMgZGVwYXJ0YW1lbnRhbGVzIGRlbCBjb25kYWRvLCBtaXNtb3MgNDggbWVzZXMNCmNvbmRhZG8gPC0gYyg1NS44MCw1Ni40MCw3MS40MCwxMTcuNjAsDQogICAgICAgICAgICAgNDYuODAsNDguMDAsNjAuMDAsNTcuNjAsNjEuODAsNTguMjAsNTYuNDAsNjMuMDAsNTcuNjAsNTMuNDAsNzEuNDAsMTE0LjAwLA0KICAgICAgICAgICAgIDQ2LjgwLDQ4LjYwLDU5LjQwLDU4LjIwLDYwLjYwLDU1LjIwLDUxLjAwLDU4LjgwLDQ5LjgwLDU0LjYwLDY1LjQwLDEwMi4wMCwNCiAgICAgICAgICAgICA0My44MCw0NS42MCw1Ny42MCw1My40MCw1Ni40MCw1Mi44MCw1NC4wMCw2MC42MCw0Ny40MCw1NC42MCw2Ny44MCwxMDAuMjAsDQogICAgICAgICAgICAgNDguMDAsNTEuNjAsNTcuNjAsNTguMjAsNjAuMDAsNTcuMDAsNTcuNjAsNjEuODApDQoNCiMgVmVudGFzIHJlYWxlcyBkZWwgY29uZGFkbyBlbiBsb3MgY3VhdHJvIG1lc2VzIGRlbCBodXJhY8Ohbg0KY29uZGFkb19yZWFsIDwtIGMoNjkuMDAsIDc1LjAwLCA4NS4yMCwgMTIxLjgwKQ0KDQp0c19jYXIgPC0gdHMoY2FybHNvbiwgc3RhcnQ9YygxLDkpLCBmcmVxdWVuY3k9MTIpDQp0c19jb24gPC0gdHMoY29uZGFkbywgc3RhcnQ9YygxLDkpLCBmcmVxdWVuY3k9MTIpDQoNCmNhdCgiTWVzZXMgZGUgQ2FybHNvbjoiLCBsZW5ndGgoY2FybHNvbiksICIgIE1lc2VzIGRlbCBjb25kYWRvOiIsIGxlbmd0aChjb25kYWRvKSwgIlxuIikNCmBgYA0KDQpgYGB7cn0NCnBhcihtZnJvdz1jKDIsMSksIG1hcj1jKDQsNCwzLDEpKQ0KcGxvdCh0c19jYXIsIHlsYWI9Ik1pbGxvbmVzIGRlIFVTRCIsIHhsYWI9IkHDsW8iLCBjb2w9IiMyZjUxODMiLCBsd2Q9MiwNCiAgICAgbWFpbj0iQ2FybHNvbiBEZXBhcnRtZW50IFN0b3JlLCA0OCBtZXNlcyBwcmV2aW9zIGFsIGh1cmFjw6FuIik7IGdyaWQoKQ0KcGxvdCh0c19jb24sIHlsYWI9Ik1pbGxvbmVzIGRlIFVTRCIsIHhsYWI9IkHDsW8iLCBjb2w9IiNjODUwMmYiLCBsd2Q9MiwNCiAgICAgbWFpbj0iVGllbmRhcyBkZXBhcnRhbWVudGFsZXMgZGVsIGNvbmRhZG8sIG1pc21vcyA0OCBtZXNlcyIpOyBncmlkKCkNCnBhcihtZnJvdz1jKDEsMSkpDQpgYGANCg0KIyMgMS4gVmVudGFzIHF1ZSBDYXJsc29uIGhhYnLDrWEgcmVnaXN0cmFkbyBzaW4gZWwgaHVyYWPDoW4NCg0KYGBge3J9DQppZV9jYXIgPC0gw61uZGljZXNfZXN0YWNpb25hbGVzKHRzX2NhcikNCmRlc19jYXIgPC0gYXMubnVtZXJpYyh0c19jYXIpIC8gYXMubnVtZXJpYyhpZV9jYXJbY3ljbGUodHNfY2FyKV0pDQp0YyA8LSAxOjQ4DQp0ZW5kX2NhciA8LSBsbShkZXNfY2FyIH4gdGMpDQoNCmtuaXRyOjprYWJsZShkYXRhLmZyYW1lKE1lcz1tZXNlcywgYMONbmRpY2UgQ2FybHNvbmA9cm91bmQoYXMubnVtZXJpYyhpZV9jYXIpLDQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXM9RkFMU0UpLA0KICAgICAgICAgICAgIGNhcHRpb249IsONbmRpY2VzIGVzdGFjaW9uYWxlcyBkZSBDYXJsc29uIikNCnN1bW1hcnkodGVuZF9jYXIpDQpgYGANCg0KYGBge3J9DQp0NSA8LSA0OTo1MiAgICMgc2VwdGllbWJyZSBhIGRpY2llbWJyZSBkZWwgYcOxbyA1DQplc3RfY2FyIDwtIChjb2VmKHRlbmRfY2FyKVsxXSArIGNvZWYodGVuZF9jYXIpWzJdKnQ1KSAqIGFzLm51bWVyaWMoaWVfY2FyWzk6MTJdKQ0KDQprbml0cjo6a2FibGUoZGF0YS5mcmFtZShNZXM9bWVzZXNbOToxMl0sDQogICAgICAgICAgICAgICAgICAgICAgICBgVmVudGFzIGVzdGltYWRhcyBzaW4gaHVyYWPDoW5gPXJvdW5kKGVzdF9jYXIsMyksDQogICAgICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcz1GQUxTRSksDQogICAgICAgICAgICAgY2FwdGlvbj0iQ2FybHNvbiwgc2VwdGllbWJyZSBhIGRpY2llbWJyZSBkZWwgYcOxbyA1LCBtaWxsb25lcyBkZSBkw7NsYXJlcyIpDQpjYXQoIlRvdGFsIGVzdGltYWRvIHNpbiBodXJhY8OhbjoiLCByb3VuZChzdW0oZXN0X2NhciksMyksICJtaWxsb25lc1xuIikNCmBgYA0KDQojIyAyLiBWZW50YXMgcXVlIGVsIGNvbmRhZG8gaGFicsOtYSB0ZW5pZG8gc2luIGVsIGh1cmFjw6FuDQoNCmBgYHtyfQ0KaWVfY29uIDwtIMOtbmRpY2VzX2VzdGFjaW9uYWxlcyh0c19jb24pDQpkZXNfY29uIDwtIGFzLm51bWVyaWModHNfY29uKSAvIGFzLm51bWVyaWMoaWVfY29uW2N5Y2xlKHRzX2NvbildKQ0KdGVuZF9jb24gPC0gbG0oZGVzX2NvbiB+IHRjKQ0Kc3VtbWFyeSh0ZW5kX2NvbikNCg0KZXN0X2NvbiA8LSAoY29lZih0ZW5kX2NvbilbMV0gKyBjb2VmKHRlbmRfY29uKVsyXSp0NSkgKiBhcy5udW1lcmljKGllX2Nvbls5OjEyXSkNCg0Ka25pdHI6OmthYmxlKGRhdGEuZnJhbWUoTWVzPW1lc2VzWzk6MTJdLA0KICAgICAgICAgICAgICAgICAgICAgICAgYEVzdGltYWRvIHNpbiBodXJhY8OhbmA9cm91bmQoZXN0X2NvbiwzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGBSZWFsIGNvbiBodXJhY8OhbmA9Y29uZGFkb19yZWFsLA0KICAgICAgICAgICAgICAgICAgICAgICAgYEV4Y2Vzb2A9cm91bmQoY29uZGFkb19yZWFsLWVzdF9jb24sMyksDQogICAgICAgICAgICAgICAgICAgICAgICBgRXhjZXNvICVgPXJvdW5kKChjb25kYWRvX3JlYWwvZXN0X2Nvbi0xKSoxMDAsMiksDQogICAgICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcz1GQUxTRSksDQogICAgICAgICAgICAgY2FwdGlvbj0iQ29uZGFkbywgc2VwdGllbWJyZSBhIGRpY2llbWJyZSBkZWwgYcOxbyA1LCBtaWxsb25lcyBkZSBkw7NsYXJlcyIpDQpjYXQoIlRvdGFsIGVzdGltYWRvIHNpbiBodXJhY8OhbjoiLCByb3VuZChzdW0oZXN0X2NvbiksMyksICJtaWxsb25lc1xuIikNCmNhdCgiVG90YWwgcmVhbCBjb24gaHVyYWPDoW46ICAgICIsIHJvdW5kKHN1bShjb25kYWRvX3JlYWwpLDMpLCAibWlsbG9uZXNcbiIpDQpgYGANCg0KIyMgMy4gUMOpcmRpZGEgZGUgQ2FybHNvbiB5IGVsIGFyZ3VtZW50byBkZWwgZXhjZXNvDQoNCmBgYHtyfQ0KZmFjdG9yX2V4Y2VzbyA8LSBzdW0oY29uZGFkb19yZWFsKSAvIHN1bShlc3RfY29uKQ0KcMOpcmRpZGFfYmFzZSAgPC0gc3VtKGVzdF9jYXIpDQpww6lyZGlkYV9hanVzdCA8LSBww6lyZGlkYV9iYXNlICogZmFjdG9yX2V4Y2Vzbw0KDQprbml0cjo6a2FibGUoZGF0YS5mcmFtZSgNCiAgQ29uY2VwdG8gPSBjKCJWZW50YXMgbm9ybWFsZXMgZXN0aW1hZGFzIGRlIENhcmxzb24iLA0KICAgICAgICAgICAgICAgIkZhY3RvciBkZSBleGNlc28gZGVsIGNvbmRhZG8iLA0KICAgICAgICAgICAgICAgIlZlbnRhcyBlc3RpbWFkYXMgY29uIGVsIGVmZWN0byBkZWwgaHVyYWPDoW4iLA0KICAgICAgICAgICAgICAgIlDDqXJkaWRhIHRvdGFsIHJlY2xhbWFibGUiKSwNCiAgVmFsb3IgPSBjKHJvdW5kKHDDqXJkaWRhX2Jhc2UsMyksIHJvdW5kKGZhY3Rvcl9leGNlc28sNCksDQogICAgICAgICAgICByb3VuZChww6lyZGlkYV9hanVzdCwzKSwgcm91bmQocMOpcmRpZGFfYWp1c3QsMykpKSwNCiAgY2FwdGlvbj0iRXN0aW1hY2nDs24gZGUgbGEgcMOpcmRpZGEgZGUgQ2FybHNvbiwgbWlsbG9uZXMgZGUgZMOzbGFyZXMiKQ0KDQpjYXQoIkVsIGNvbmRhZG8gdmVuZGnDsyIsIHJvdW5kKChmYWN0b3JfZXhjZXNvLTEpKjEwMCwyKSwgIiUgbcOhcyBkZSBsbyBlc3BlcmFkb1xuIikNCmNhdCgiUMOpcmRpZGEgc2luIGFqdXN0YXI6Iiwgcm91bmQocMOpcmRpZGFfYmFzZSwzKSwgIm1pbGxvbmVzXG4iKQ0KY2F0KCJQw6lyZGlkYSBjb24gZWwgYWp1c3RlIHBvciBodXJhY8OhbjoiLCByb3VuZChww6lyZGlkYV9hanVzdCwzKSwgIm1pbGxvbmVzXG4iKQ0KY2F0KCJEaWZlcmVuY2lhIGVudHJlIGxhcyBkb3MgY2lmcmFzOiIsIHJvdW5kKHDDqXJkaWRhX2FqdXN0LXDDqXJkaWRhX2Jhc2UsMyksICJtaWxsb25lc1xuIikNCmBgYA0KDQojIyMgSW5mb3JtZSBwYXJhIGxvcyBkaXJlY3Rpdm9zIGRlIENhcmxzb24NCg0KKipDdcOhbnRvIHNlIHBlcmRpw7MuKiogQ29uIGVsIHBhdHLDs24gZGUgbG9zIDQ4IG1lc2VzIHByZXZpb3MsIENhcmxzb24gaGFicsOtYSB2ZW5kaWRvDQphbHJlZGVkb3IgZGUgMTIuMzYgbWlsbG9uZXMgZGUgZMOzbGFyZXMgZW50cmUgc2VwdGllbWJyZSB5IGRpY2llbWJyZSBkZWwgYcOxbyA1LiBFc2EgZXMgbGENCnDDqXJkaWRhIGRlIHZlbnRhcyBwb3IgbG9zIGN1YXRybyBtZXNlcyBxdWUgZXN0dXZvIGNlcnJhZGEsIGNhbGN1bGFkYSBzb2xvIGNvbiBzdSBwcm9waWENCmhpc3RvcmlhIHkgc2luIG1ldGVyIG5hZGEgZGVsIGh1cmFjw6FuLg0KDQpEaWNpZW1icmUgZXMgZWwgbWVzIHF1ZSBtw6FzIHBlc2EgZGUgbG9zIGN1YXRybywgY29uIDQuNiBtaWxsb25lcyBlc3RpbWFkb3MsIHBvcnF1ZSBzdQ0Kw61uZGljZSBlc3RhY2lvbmFsIGVzIGRlIDEuNjc3NCB5IHZlbmRlIGNhc2kgNjglIGFycmliYSBkZSB1biBtZXMgbm9ybWFsLiBDZXJyYXIgZW4NCmRpY2llbWJyZSBlcyBsbyBxdWUgbcOhcyBjYXJvIHNhbGnDsy4NCg0KKipFbCBhcmd1bWVudG8gZGVsIGV4Y2VzbywgeSBzw60gc2Ugc29zdGllbmUuKiogRWwgY29uZGFkbyBkZWJpw7MgaGFiZXIgdmVuZGlkbyBjZXJjYSBkZQ0KMjczLjY0IG1pbGxvbmVzIGVuIGVzb3MgY3VhdHJvIG1lc2VzIHNlZ8O6biBzdSBwcm9waWEgdGVuZGVuY2lhLCB5IHZlbmRpw7MgMzUxLiBTb24gNzcuMzYNCm1pbGxvbmVzIGRlIG3DoXMsIHVuIDI4LjI3JSBhcnJpYmEgZGUgbG8gbm9ybWFsLCB5IGVzbyBubyBzZSBleHBsaWNhIGNvbiBlbA0KY29tcG9ydGFtaWVudG8gaGlzdMOzcmljbyBkZWwgY29uZGFkbyBzaW5vIGNvbiBsb3MgbcOhcyBkZSA4IG1pbCBtaWxsb25lcyBkZSBheXVkYSBmZWRlcmFsDQp5IHBhZ29zIGRlIHNlZ3Vyb3MgcXVlIGVudHJhcm9uIGEgbGEgem9uYS4NCg0KRWwgZXhjZXNvIG5vIGVzdMOhIHBhcmVqby4gT2N0dWJyZSBmdWUgZWwgbWVzIG3DoXMgZnVlcnRlIGNvbiA0MSUgYXJyaWJhIGRlIGxvIGVzcGVyYWRvIHkNCnNlcHRpZW1icmUgZWwgbcOhcyBmbG9qbyBjb24gMzYlLCBtaWVudHJhcyBxdWUgZGljaWVtYnJlIGFwZW5hcyBzdWJpw7MgMTglLiBTZSB2ZSBjb21vDQpnZW50ZSByZXBvbmllbmRvIGxvIHF1ZSBwZXJkacOzIGVuIGxvcyBtZXNlcyBpbm1lZGlhdG9zIGFsIGh1cmFjw6FuLCB5IG5vIGNvbW8gdW4gY2FtYmlvDQpwZXJtYW5lbnRlIGRlbCBtZXJjYWRvLg0KDQpTaSBDYXJsc29uIGh1YmllcmEgZXN0YWRvIGFiaWVydGEsIG5vIGhheSByYXrDs24gcGFyYSBwZW5zYXIgcXVlIHNlIGhhYnLDrWEgcXVlZGFkbyBmdWVyYQ0KZGUgZXNlIG1vdmltaWVudG8sIHBvcnF1ZSBlbCBkaW5lcm8gc2UgcmVwYXJ0acOzIGVudHJlIGxhcyB0aWVuZGFzIGRlcGFydGFtZW50YWxlcyBkZWwNCmNvbmRhZG8geSBDYXJsc29uIGVzIHVuYSBkZSBlbGxhcy4gQXBsaWNhbmRvIGVsIG1pc21vIGZhY3RvciBkZSAxLjI4MjcsIGxhcyB2ZW50YXMgcXVlDQpoYWJyw61hIGhlY2hvIHN1YmVuIGRlIDEyLjM2IGEgMTUuODUgbWlsbG9uZXMsIG8gc2VhICoqMy40OSBtaWxsb25lcyBtw6FzIGRlIHJlY2xhbWFjacOzbioqLg0KDQoqKkxvIHF1ZSBsZSByZWNvbWVuZGFyw61hIGEgbGEgdGllbmRhLioqIFJlY2xhbWFyIGxvcyAxMi4zNiBtaWxsb25lcyBjb21vIHDDqXJkaWRhIGJhc2UsDQpxdWUgZXNhIGVzIGxhIHBhcnRlIHPDs2xpZGEgeSBzYWxlIGRlIHN1IHByb3BpYSBoaXN0b3JpYSwgeSBwcmVzZW50YXIgbG9zIDMuNDkgbWlsbG9uZXMNCmFkaWNpb25hbGVzIHBvciBzZXBhcmFkbywgY29uIGVsIGFuw6FsaXNpcyBkZWwgY29uZGFkbyBjb21vIHJlc3BhbGRvLiBDb252aWVuZQ0KcHJlc2VudGFybG9zIGFwYXJ0ZSB5IG5vIHJldnVlbHRvcywgcG9ycXVlIGxhIHByaW1lcmEgY2lmcmEgZXMgZGlmw61jaWwgZGUgZGlzY3V0aXIgeSBsYQ0Kc2VndW5kYSBkZXBlbmRlIGRlIGFjZXB0YXIgcXVlIENhcmxzb24gaGFicsOtYSBjYXB0dXJhZG8gZWwgbWlzbW8gcG9yY2VudGFqZSBkZSBhdW1lbnRvDQpxdWUgbGFzIGRlbcOhcyB0aWVuZGFzLg0KDQpBaMOtIGVzdMOhIGVsIHB1bnRvIGTDqWJpbCBxdWUgaGF5IHF1ZSByZWNvbm9jZXIgYW50ZXMgZGUgcXVlIGxvIHNhcXVlbiBkZWwgb3RybyBsYWRvLiBFbA0Kc3VwdWVzdG8gZXMgcXVlIENhcmxzb24gaGFicsOtYSBjcmVjaWRvIGlndWFsIHF1ZSBlbCBwcm9tZWRpbyBkZWwgY29uZGFkbywgeSBlc28gbm8gZXN0w6ENCmRlbW9zdHJhZG8sIHNvbG8gZXMgcmF6b25hYmxlLiBBZGVtw6FzIGxhIHJlZ3Jlc2nDs24gZGUgdGVuZGVuY2lhIGRlbCBjb25kYWRvIHRpZW5lIHVuIFLCsg0KYmFqbywgYXPDrSBxdWUgc3UgZXN0aW1hY2nDs24gZXMgbWVub3MgZmlybWUgcXVlIGxhIGRlIENhcmxzb24uIFZhbGUgbGEgcGVuYSBkZWNpcmxvIGRlDQp1bmEgdmV6IHkgb2ZyZWNlciBsYSBjb21wYXJhY2nDs24gbWVzIHBvciBtZXMsIHF1ZSBlcyBkb25kZSBlbCBhcmd1bWVudG8gc2UgdmUgbcOhcw0KY29udmluY2VudGUuDQo=