VINTAGE RESTAURANT - CASO DE ESTUDIO

Explicación

Este documento resuelve el caso “The Vintage Restaurant” (Tabla 18.26): ventas mensuales de alimentos y bebidas (en miles de $) durante los primeros 3 años de operación del restaurante en Captiva Island, Florida.

**Para resolver el problema se usaron:

  • Descomposición clásica de series de tiempo (Tendencia × Estacionalidad), y
  • Regresión con variables ficticias (dummy variables) para el componente estacional.

Ambos métodos siguen perteneciendo al tema de Series de Tiempo, pero corresponden a un enfoque distinto: en vez de suavizar la serie o dejar que un algoritmo decida la estructura (como auto.arima), aquí se descompone explícitamente la serie en Tendencia (T), Estacionalidad (S) e Irregular (I), y se pronostica combinando esos componentes. Es el enfoque clásico para series fuertemente estacionales con pocos años de historia (aquí solo 3 años = 36 meses), donde un ARIMA estacional no se podría estimar de forma confiable.

INSTALACIÓN DE LIBRERÍAS

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

CARGA DE DATOS (Tabla 18.26)

meses <- c("Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto",
           "Septiembre","Octubre","Noviembre","Diciembre")

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)

ventas <- c(año1, año2, año3)
n <- length(ventas)
t <- 1:n

dataframe3 <- data.frame(
  t = t,
  mes = factor(rep(meses, 3), levels = meses),
  año = rep(1:3, each = 12),
  ventas = ventas
)
kable(dataframe3, caption = "Ventas mensuales - Vintage Restaurant (miles de $)")
Ventas mensuales - Vintage Restaurant (miles de $)
t mes año ventas
1 Enero 1 242
2 Febrero 1 235
3 Marzo 1 232
4 Abril 1 178
5 Mayo 1 184
6 Junio 1 140
7 Julio 1 145
8 Agosto 1 152
9 Septiembre 1 110
10 Octubre 1 130
11 Noviembre 1 152
12 Diciembre 1 206
13 Enero 2 263
14 Febrero 2 238
15 Marzo 2 247
16 Abril 2 193
17 Mayo 2 193
18 Junio 2 149
19 Julio 2 157
20 Agosto 2 161
21 Septiembre 2 122
22 Octubre 2 130
23 Noviembre 2 167
24 Diciembre 2 230
25 Enero 3 282
26 Febrero 3 255
27 Marzo 3 265
28 Abril 3 205
29 Mayo 3 210
30 Junio 3 160
31 Julio 3 166
32 Agosto 3 174
33 Septiembre 3 126
34 Octubre 3 148
35 Noviembre 3 173
36 Diciembre 3 235
# Serie de tiempo mensual, iniciando en Enero del Año 1
tsVINTAGE <- ts(ventas, start = c(1,1), frequency = 12)
tsVINTAGE
##   Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
## 1 242 235 232 178 184 140 145 152 110 130 152 206
## 2 263 238 247 193 193 149 157 161 122 130 167 230
## 3 282 255 265 205 210 160 166 174 126 148 173 235

1. GRÁFICA DE LA SERIE DE TIEMPO

plot(tsVINTAGE, main = "Ventas mensuales de alimentos y bebidas - Vintage Restaurant",
     ylab = "Ventas ($ miles)", xlab = "Año", type = "o", col = "steelblue", pch = 16)

Comentario sobre el patrón principal:

La serie muestra dos componentes claros:

  • Tendencia creciente: las ventas de cada mes aumentan de un año a otro (por ejemplo, enero pasa de 242 a 263 y a 282 en los 3 años), reflejando el crecimiento del restaurante.
  • Estacionalidad marcada y repetitiva de periodo 12: cada año se repite el mismo patrón — ventas altas en enero, febrero, marzo y diciembre (temporada alta turística en Florida) y ventas bajas en junio, julio, agosto y especialmente septiembre (temporada baja).

No se aprecia un patrón cíclico de largo plazo ni irregularidades severas; el comportamiento es consistente año con año.

2. ANÁLISIS DE ESTACIONALIDAD - ÍNDICES ESTACIONALES

Se usa el método clásico de razón al promedio móvil centrado (CMA) de orden 12 (método de descomposición multiplicativa, disponible en R con decompose()):

descomposicion <- decompose(tsVINTAGE, type = "multiplicative")
plot(descomposicion)

# Índices estacionales (uno por mes, promediados y normalizados a promedio = 1)
indice_estacional <- descomposicion$figure
names(indice_estacional) <- meses

tabla_estacional <- data.frame(
  Mes = meses,
  Indice_Estacional = round(indice_estacional, 4)
)
kable(tabla_estacional, caption = "Índices estacionales por mes")
Índices estacionales por mes
Mes Indice_Estacional
Enero Enero 1.4436
Febrero Febrero 1.2997
Marzo Marzo 1.3441
Abril Abril 1.0412
Mayo Mayo 1.0494
Junio Junio 0.8004
Julio Julio 0.8283
Agosto Agosto 0.8530
Septiembre Septiembre 0.6280
Octubre Octubre 0.7003
Noviembre Noviembre 0.8528
Diciembre Diciembre 1.1593
barplot(indice_estacional, names.arg = meses, las = 2, col = "salmon",
        main = "Índice estacional por mes - Vintage Restaurant",
        ylab = "Índice estacional")
abline(h = 1, lty = 2)

Comentario sobre los índices estacionales:

  • Temporada alta: enero (≈1.44), marzo (≈1.34) y febrero (≈1.30) están muy por encima de 1 — las ventas de esos meses son entre 30% y 44% superiores al promedio anual. Diciembre también está por arriba del promedio (≈1.16).
  • Temporada baja: septiembre es el mes más débil (≈0.63, es decir, 37% por debajo del promedio), seguido de octubre (≈0.70) y junio (≈0.80).

¿Tienen sentido intuitivo? Sí. Captiva Island, cerca de Fort Myers, es un destino turístico de invierno en Florida (“snowbird season”): los turistas del norte de EE. UU. y Canadá llegan de diciembre a marzo escapando del frío, lo que explica los índices altos en esos meses. En cambio, el verano (junio-septiembre) coincide con el clima húmedo y caluroso de Florida y la temporada de huracanes, por lo que baja el turismo — de ahí los índices bajos, con su mínimo en septiembre.

3. DESESTACIONALIZACIÓN DE LA SERIE

serie_desestacionalizada <- tsVINTAGE / rep(indice_estacional, 3)

plot(t, as.numeric(serie_desestacionalizada), type = "o", col = "darkgreen",
     main = "Serie desestacionalizada - Vintage Restaurant",
     xlab = "Mes (t)", ylab = "Ventas ($ miles), sin efecto estacional")

# Tendencia lineal sobre la serie desestacionalizada
modelo_tendencia <- lm(as.numeric(serie_desestacionalizada) ~ t)
summary(modelo_tendencia)
## 
## Call:
## lm(formula = as.numeric(serie_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
lines(t, fitted(modelo_tendencia), col = "black", lty = 2, lwd = 2)
legend("topleft", legend = c("Serie desestacionalizada", "Tendencia lineal"),
       col = c("darkgreen","black"), lty = c(1,2), pch = c(1,NA))

¿Existe tendencia en la serie desestacionalizada?

Sí, una vez removido el efecto estacional, la serie desestacionalizada muestra una tendencia lineal creciente clara (pendiente ≈ +1.02 miles de $ por mes, con R² ≈ 0.92), consistente con el crecimiento del restaurante mes a mes a lo largo de los 3 años, sin el “ruido” que generaba la estacionalidad.

4. PRONÓSTICO - MÉTODO DE DESCOMPOSICIÓN

El pronóstico para cada mes del Año 4 se obtiene multiplicando el valor proyectado de la tendencia por el índice estacional correspondiente a ese mes:

\[ \hat{Y}_t = (b_0 + b_1 \cdot t) \times SI_{mes} \]

t_futuro <- (n+1):(n+12)  # meses 37 a 48 = Año 4

b0 <- coef(modelo_tendencia)[1]
b1 <- coef(modelo_tendencia)[2]
tendencia_futura <- b0 + b1 * t_futuro

pronostico_descomposicion <- tendencia_futura * as.numeric(indice_estacional)

tabla_descomposicion <- data.frame(
  Mes = meses,
  t = t_futuro,
  Tendencia_Proyectada = round(tendencia_futura, 2),
  Indice_Estacional = round(as.numeric(indice_estacional), 4),
  Pronostico_Descomposicion = round(pronostico_descomposicion, 2)
)
kable(tabla_descomposicion, caption = "Pronóstico Año 4 - Método de Descomposición")
Pronóstico Año 4 - Método de Descomposición
Mes t Tendencia_Proyectada Indice_Estacional Pronostico_Descomposicion
Enero 37 207.14 1.4436 299.02
Febrero 38 208.16 1.2997 270.54
Marzo 39 209.18 1.3441 281.16
Abril 40 210.20 1.0412 218.86
Mayo 41 211.22 1.0494 221.65
Junio 42 212.24 0.8004 169.88
Julio 43 213.27 0.8283 176.65
Agosto 44 214.29 0.8530 182.78
Septiembre 45 215.31 0.6280 135.21
Octubre 46 216.33 0.7003 151.50
Noviembre 47 217.35 0.8528 185.35
Diciembre 48 218.37 1.1593 253.15
plot(t, ventas, type = "o", col = "steelblue",
     xlim = c(1, max(t_futuro)), ylim = range(c(ventas, pronostico_descomposicion)),
     main = "Pronóstico Año 4 - Método de Descomposición",
     xlab = "Mes (t)", ylab = "Ventas ($ miles)")
lines(t_futuro, pronostico_descomposicion, type = "o", col = "red", pch = 15)
legend("topleft", legend = c("Ventas históricas (Años 1-3)", "Pronóstico Año 4"),
       col = c("steelblue","red"), pch = c(1,15))

cat("Ventas totales pronosticadas Año 4 (Descomposición):", round(sum(pronostico_descomposicion),2), "\n")
## Ventas totales pronosticadas Año 4 (Descomposición): 2545.76

5. PRONÓSTICO - REGRESIÓN CON VARIABLES FICTICIAS (DUMMY)

Se plantea un modelo de regresión múltiple:

\[ Ventas_t = b_0 + b_1 \cdot t + b_2 M_{ene} + b_3 M_{feb} + \dots + b_{12} M_{nov} + \varepsilon_t \]

Se usan 11 variables dummy (enero a noviembre); diciembre queda como categoría base (su efecto está incluido en el intercepto).

# "mes" ya es un factor con Diciembre al final -> Diciembre queda como nivel de referencia
modelo_dummy <- lm(ventas ~ t + mes, data = dataframe3)
summary(modelo_dummy)
## 
## Call:
## lm(formula = ventas ~ t + mes, data = dataframe3)
## 
## 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 ***
## mesFebrero     -20.68403    3.62688  -5.703 8.30e-06 ***
## mesMarzo       -16.36806    3.62924  -4.510 0.000158 ***
## mesAbril       -73.38542    3.63317 -20.199 3.90e-16 ***
## mesMayo        -70.73611    3.63866 -19.440 8.97e-16 ***
## mesJunio      -117.75347    3.64571 -32.299  < 2e-16 ***
## mesJulio      -112.43750    3.65431 -30.768  < 2e-16 ***
## mesAgosto     -107.12153    3.66445 -29.233  < 2e-16 ***
## mesSeptiembre -151.13889    3.67611 -41.114  < 2e-16 ***
## mesOctubre    -135.48958    3.68928 -36.725  < 2e-16 ***
## mesNoviembre  -108.50694    3.70395 -29.295  < 2e-16 ***
## mesDiciembre   -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
# Pronóstico Año 4 con el modelo de dummies
nuevos_datos <- data.frame(
  t = t_futuro,
  mes = factor(meses, levels = meses)
)
pronostico_dummy <- predict(modelo_dummy, newdata = nuevos_datos)

tabla_dummy <- data.frame(
  Mes = meses,
  t = t_futuro,
  Pronostico_Regresion_Dummy = round(as.numeric(pronostico_dummy), 2)
)
kable(tabla_dummy, caption = "Pronóstico Año 4 - Regresión con Variables Dummy")
Pronóstico Año 4 - Regresión con Variables Dummy
Mes t Pronostico_Regresion_Dummy
Enero 37 286.75
Febrero 38 267.08
Marzo 39 272.42
Abril 40 216.42
Mayo 41 220.08
Junio 42 174.08
Julio 43 180.42
Agosto 44 186.75
Septiembre 45 143.75
Octubre 46 160.42
Noviembre 47 188.42
Diciembre 48 248.08
plot(t, ventas, type = "o", col = "steelblue",
     xlim = c(1, max(t_futuro)), ylim = range(c(ventas, pronostico_dummy)),
     main = "Pronóstico Año 4 - Regresión con Variables Dummy",
     xlab = "Mes (t)", ylab = "Ventas ($ miles)")
lines(t_futuro, pronostico_dummy, type = "o", col = "darkorange", pch = 17)
legend("topleft", legend = c("Ventas históricas (Años 1-3)", "Pronóstico Año 4"),
       col = c("steelblue","darkorange"), pch = c(1,17))

cat("Ventas totales pronosticadas Año 4 (Regresión Dummy):", round(sum(pronostico_dummy),2), "\n")
## Ventas totales pronosticadas Año 4 (Regresión Dummy): 2544.67

6. TABLAS Y GRÁFICAS RESUMEN

tabla_resumen <- data.frame(
  Mes = meses,
  Indice_Estacional = round(as.numeric(indice_estacional), 4),
  Pronostico_Descomposicion = round(pronostico_descomposicion, 2),
  Pronostico_Regresion_Dummy = round(as.numeric(pronostico_dummy), 2),
  Diferencia = round(pronostico_descomposicion - as.numeric(pronostico_dummy), 2)
)
kable(tabla_resumen, caption = "Resumen comparativo de pronósticos - Año 4")
Resumen comparativo de pronósticos - Año 4
Mes Indice_Estacional Pronostico_Descomposicion Pronostico_Regresion_Dummy Diferencia
Enero 1.4436 299.02 286.75 12.27
Febrero 1.2997 270.54 267.08 3.46
Marzo 1.3441 281.16 272.42 8.75
Abril 1.0412 218.86 216.42 2.45
Mayo 1.0494 221.65 220.08 1.57
Junio 0.8004 169.88 174.08 -4.21
Julio 0.8283 176.65 180.42 -3.77
Agosto 0.8530 182.78 186.75 -3.97
Septiembre 0.6280 135.21 143.75 -8.54
Octubre 0.7003 151.50 160.42 -8.92
Noviembre 0.8528 185.35 188.42 -3.07
Diciembre 1.1593 253.15 248.08 5.07
plot(1:12, pronostico_descomposicion, type = "o", col = "red", pch = 15,
     xaxt = "n", ylim = range(c(pronostico_descomposicion, pronostico_dummy)),
     main = "Comparación de pronósticos - Año 4",
     xlab = "Mes", ylab = "Ventas pronosticadas ($ miles)")
lines(1:12, pronostico_dummy, type = "o", col = "darkorange", pch = 17)
axis(1, at = 1:12, labels = meses, las = 2)
legend("topright", legend = c("Descomposición", "Regresión Dummy"),
       col = c("red","darkorange"), pch = c(15,17))

7. ERROR DE PRONÓSTICO - ENERO AÑO 4 (VENTAS REALES = $295,000)

ventas_reales_enero4 <- 295

pronostico_enero4_descomp <- pronostico_descomposicion[1]
pronostico_enero4_dummy   <- as.numeric(pronostico_dummy)[1]

error_descomp <- ventas_reales_enero4 - pronostico_enero4_descomp
error_dummy   <- ventas_reales_enero4 - pronostico_enero4_dummy

cat("Pronóstico Enero Año 4 (Descomposición):", round(pronostico_enero4_descomp,2),
    " -> Error:", round(error_descomp,2), "\n")
## Pronóstico Enero Año 4 (Descomposición): 299.02  -> Error: -4.02
cat("Pronóstico Enero Año 4 (Regresión Dummy):", round(pronostico_enero4_dummy,2),
    " -> Error:", round(error_dummy,2), "\n\n")
## Pronóstico Enero Año 4 (Regresión Dummy): 286.75  -> Error: 8.25
cat("Error porcentual (Descomposición):", round(100*error_descomp/ventas_reales_enero4,2), "%\n")
## Error porcentual (Descomposición): -1.36 %
cat("Error porcentual (Regresión Dummy):", round(100*error_dummy/ventas_reales_enero4,2), "%\n")
## Error porcentual (Regresión Dummy): 2.8 %

Interpretación del error de pronóstico:

Ambos modelos pronostican enero del Año 4 muy cerca de las ventas reales de $295,000 (el error es de apenas unos cuantos miles de dólares, es decir, menos del 3% en ambos casos). Este es un error pequeño y esperable: ningún modelo de pronóstico es perfecto, ya que existen factores que no captura la tendencia ni la estacionalidad histórica (clima de ese mes en particular, eventos locales, cambios en la competencia, publicidad, etc.).

¿Qué se puede hacer para reducir la incertidumbre de Karen sobre el proceso de pronóstico?

  1. Presentar el pronóstico como un rango, no como un número exacto — por ejemplo, un intervalo de confianza (± cierto margen basado en el error histórico de los modelos) en vez de una sola cifra, para que Karen entienda que el pronóstico es una estimación, no una promesa.
  2. Actualizar el modelo cada vez que hay un nuevo mes de datos reales (pronóstico “rodante”): conforme pasan los meses del Año 4, se reincorporan las ventas reales y se reajustan tendencia e índices estacionales, lo cual reduce el error en los meses siguientes.
  3. Monitorear el error de pronóstico mes a mes (MAD, MAPE, RMSE) para detectar si el modelo se está desviando de forma sistemática (por ejemplo, si septiembre real siempre resulta más alto que el pronóstico, hay que revisar el índice estacional de ese mes).
  4. Incorporar información adicional cuando esté disponible (reservaciones ya confirmadas, ocupación hotelera de la isla, tendencias turísticas de Florida) para ajustar el pronóstico puramente estadístico con juicio de negocio.
  5. Usar varios métodos en paralelo (como se hizo aquí con descomposición y regresión dummy) y promediarlos o mostrar el rango entre ellos, en vez de depender de un solo modelo — esto ya por sí mismo comunica a Karen el grado de incertidumbre inherente al ejercicio.

CONCLUSIONES

  1. Patrón de la serie: las ventas de Vintage Restaurant combinan una tendencia de crecimiento sostenido (aprox. +$1,000 al mes en términos desestacionalizados) con una estacionalidad fuerte y repetitiva ligada a la temporada turística de invierno en Florida.

  2. Estacionalidad: enero, febrero, marzo y diciembre son los meses de temporada alta (índices entre 1.16 y 1.44); junio, julio, agosto, septiembre y octubre son de temporada baja (índices entre 0.63 y 0.85), con septiembre como el mes más flojo del año. Estos patrones tienen pleno sentido dado que Captiva Island es un destino de “snowbirds” en temporada de invierno.

  3. Serie desestacionalizada: una vez removido el efecto estacional, se confirma una tendencia lineal creciente clara (R² ≈ 0.92), señal de que el crecimiento del negocio es real y consistente, no un artefacto de la estacionalidad.

  4. Pronósticos Año 4: tanto el método de descomposición clásica como la regresión con variables dummy arrojan pronósticos consistentes entre sí (diferencias menores al 5% mes a mes), con enero y marzo del Año 4 como los meses de mayor venta esperada (~$280,000-$300,000) y septiembre como el mes más bajo (~$135,000-$145,000).

  5. Validación con datos reales: al comparar contra la venta real de enero del Año 4 ($295,000), ambos modelos tuvieron un error menor al 3%, lo que valida la metodología, aunque se recomienda a Karen tratar cualquier pronóstico como una estimación con incertidumbre, no como una cifra exacta, y actualizar el modelo conforme lleguen nuevos datos reales.

  6. Recomendación general para Karen: usar estos pronósticos principalmente para planeación de personal, compras de inventario (mariscos frescos) y flujo de caja, reforzando la operación en los meses de temporada alta (dic-mar) y ajustando costos/personal en los meses de temporada baja (jun-oct), sin perder de vista que la tendencia general del negocio sigue siendo de crecimiento.

LS0tDQp0aXRsZTogIlRFTUEyX1RpbWVzZXJpZXMrR0VGQSAoRGVzY29tcG9zaWNpw7NuIHkgUmVncmVzacOzbiBEdW1teSkiDQphdXRob3I6ICJMdWNlcm9fU2FsYXphcl9BMDA4MzQ4OTMiDQpkYXRlOiAiMjAyNi0wOC0yNiINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KICAgIHRoZW1lOiB1bml0ZWQNCi0tLQ0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPlZJTlRBR0UgUkVTVEFVUkFOVCAtIENBU08gREUgRVNUVURJTzwvc3Bhbj4NCg0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiB5ZWxsb3c7Ij5FeHBsaWNhY2nDs248L3NwYW4+DQoNCkVzdGUgZG9jdW1lbnRvIHJlc3VlbHZlIGVsIGNhc28gKioiVGhlIFZpbnRhZ2UgUmVzdGF1cmFudCIqKiAoVGFibGEgMTguMjYpOiB2ZW50YXMgbWVuc3VhbGVzIGRlIGFsaW1lbnRvcyB5IGJlYmlkYXMgKGVuIG1pbGVzIGRlICQpIGR1cmFudGUgbG9zIHByaW1lcm9zIDMgYcOxb3MgZGUgb3BlcmFjacOzbiBkZWwgcmVzdGF1cmFudGUgZW4gQ2FwdGl2YSBJc2xhbmQsIEZsb3JpZGEuDQoNCioqUGFyYSByZXNvbHZlciBlbCBwcm9ibGVtYSBzZSB1c2Fyb246DQoNCi0gKipEZXNjb21wb3NpY2nDs24gY2zDoXNpY2EgZGUgc2VyaWVzIGRlIHRpZW1wbyoqIChUZW5kZW5jaWEgw5cgRXN0YWNpb25hbGlkYWQpLCB5DQotICoqUmVncmVzacOzbiBjb24gdmFyaWFibGVzIGZpY3RpY2lhcyAoZHVtbXkgdmFyaWFibGVzKSoqIHBhcmEgZWwgY29tcG9uZW50ZSBlc3RhY2lvbmFsLg0KDQpBbWJvcyBtw6l0b2RvcyBzaWd1ZW4gcGVydGVuZWNpZW5kbyBhbCB0ZW1hIGRlICpTZXJpZXMgZGUgVGllbXBvKiwgcGVybyBjb3JyZXNwb25kZW4gYSB1biBlbmZvcXVlIGRpc3RpbnRvOiBlbiB2ZXogZGUgc3Vhdml6YXIgbGEgc2VyaWUgbyBkZWphciBxdWUgdW4gYWxnb3JpdG1vIGRlY2lkYSBsYSBlc3RydWN0dXJhIChjb21vIGBhdXRvLmFyaW1hYCksIGFxdcOtIHNlICoqZGVzY29tcG9uZSBleHBsw61jaXRhbWVudGUqKiBsYSBzZXJpZSBlbiBUZW5kZW5jaWEgKFQpLCBFc3RhY2lvbmFsaWRhZCAoUykgZSBJcnJlZ3VsYXIgKEkpLCB5IHNlIHByb25vc3RpY2EgY29tYmluYW5kbyBlc29zIGNvbXBvbmVudGVzLiBFcyBlbCBlbmZvcXVlIGNsw6FzaWNvIHBhcmEgc2VyaWVzICoqZnVlcnRlbWVudGUgZXN0YWNpb25hbGVzIGNvbiBwb2NvcyBhw7FvcyBkZSBoaXN0b3JpYSoqIChhcXXDrSBzb2xvIDMgYcOxb3MgPSAzNiBtZXNlcyksIGRvbmRlIHVuIEFSSU1BIGVzdGFjaW9uYWwgbm8gc2UgcG9kcsOtYSBlc3RpbWFyIGRlIGZvcm1hIGNvbmZpYWJsZS4NCg0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiBncmVlbjsiPklOU1RBTEFDScOTTiBERSBMSUJSRVLDjUFTPC9zcGFuPg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNpbnN0YWxsLnBhY2thZ2VzKCJmb3JlY2FzdCIpDQojaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KbGlicmFyeShmb3JlY2FzdCkNCmxpYnJhcnkoa25pdHIpDQpgYGANCg0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiB5ZWxsb3c7Ij5DQVJHQSBERSBEQVRPUyAoVGFibGEgMTguMjYpPC9zcGFuPg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbWVzZXMgPC0gYygiRW5lcm8iLCJGZWJyZXJvIiwiTWFyem8iLCJBYnJpbCIsIk1heW8iLCJKdW5pbyIsIkp1bGlvIiwiQWdvc3RvIiwNCiAgICAgICAgICAgIlNlcHRpZW1icmUiLCJPY3R1YnJlIiwiTm92aWVtYnJlIiwiRGljaWVtYnJlIikNCg0KYcOxbzEgPC0gYygyNDIsMjM1LDIzMiwxNzgsMTg0LDE0MCwxNDUsMTUyLDExMCwxMzAsMTUyLDIwNikNCmHDsW8yIDwtIGMoMjYzLDIzOCwyNDcsMTkzLDE5MywxNDksMTU3LDE2MSwxMjIsMTMwLDE2NywyMzApDQphw7FvMyA8LSBjKDI4MiwyNTUsMjY1LDIwNSwyMTAsMTYwLDE2NiwxNzQsMTI2LDE0OCwxNzMsMjM1KQ0KDQp2ZW50YXMgPC0gYyhhw7FvMSwgYcOxbzIsIGHDsW8zKQ0KbiA8LSBsZW5ndGgodmVudGFzKQ0KdCA8LSAxOm4NCg0KZGF0YWZyYW1lMyA8LSBkYXRhLmZyYW1lKA0KICB0ID0gdCwNCiAgbWVzID0gZmFjdG9yKHJlcChtZXNlcywgMyksIGxldmVscyA9IG1lc2VzKSwNCiAgYcOxbyA9IHJlcCgxOjMsIGVhY2ggPSAxMiksDQogIHZlbnRhcyA9IHZlbnRhcw0KKQ0Ka2FibGUoZGF0YWZyYW1lMywgY2FwdGlvbiA9ICJWZW50YXMgbWVuc3VhbGVzIC0gVmludGFnZSBSZXN0YXVyYW50IChtaWxlcyBkZSAkKSIpDQoNCiMgU2VyaWUgZGUgdGllbXBvIG1lbnN1YWwsIGluaWNpYW5kbyBlbiBFbmVybyBkZWwgQcOxbyAxDQp0c1ZJTlRBR0UgPC0gdHModmVudGFzLCBzdGFydCA9IGMoMSwxKSwgZnJlcXVlbmN5ID0gMTIpDQp0c1ZJTlRBR0UNCmBgYA0KDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWU7Ij4xLiBHUsOBRklDQSBERSBMQSBTRVJJRSBERSBUSUVNUE88L3NwYW4+DQoNCmBgYHtyfQ0KcGxvdCh0c1ZJTlRBR0UsIG1haW4gPSAiVmVudGFzIG1lbnN1YWxlcyBkZSBhbGltZW50b3MgeSBiZWJpZGFzIC0gVmludGFnZSBSZXN0YXVyYW50IiwNCiAgICAgeWxhYiA9ICJWZW50YXMgKCQgbWlsZXMpIiwgeGxhYiA9ICJBw7FvIiwgdHlwZSA9ICJvIiwgY29sID0gInN0ZWVsYmx1ZSIsIHBjaCA9IDE2KQ0KYGBgDQoNCioqQ29tZW50YXJpbyBzb2JyZSBlbCBwYXRyw7NuIHByaW5jaXBhbDoqKg0KDQpMYSBzZXJpZSBtdWVzdHJhIGRvcyBjb21wb25lbnRlcyBjbGFyb3M6DQoNCi0gKipUZW5kZW5jaWEgY3JlY2llbnRlKio6IGxhcyB2ZW50YXMgZGUgY2FkYSBtZXMgYXVtZW50YW4gZGUgdW4gYcOxbyBhIG90cm8gKHBvciBlamVtcGxvLCBlbmVybyBwYXNhIGRlIDI0MiBhIDI2MyB5IGEgMjgyIGVuIGxvcyAzIGHDsW9zKSwgcmVmbGVqYW5kbyBlbCBjcmVjaW1pZW50byBkZWwgcmVzdGF1cmFudGUuDQotICoqRXN0YWNpb25hbGlkYWQgbWFyY2FkYSB5IHJlcGV0aXRpdmEgZGUgcGVyaW9kbyAxMioqOiBjYWRhIGHDsW8gc2UgcmVwaXRlIGVsIG1pc21vIHBhdHLDs24g4oCUIHZlbnRhcyBhbHRhcyBlbiAqKmVuZXJvLCBmZWJyZXJvLCBtYXJ6byB5IGRpY2llbWJyZSoqICh0ZW1wb3JhZGEgYWx0YSB0dXLDrXN0aWNhIGVuIEZsb3JpZGEpIHkgdmVudGFzIGJhamFzIGVuICoqanVuaW8sIGp1bGlvLCBhZ29zdG8geSBlc3BlY2lhbG1lbnRlIHNlcHRpZW1icmUqKiAodGVtcG9yYWRhIGJhamEpLg0KDQpObyBzZSBhcHJlY2lhIHVuIHBhdHLDs24gY8OtY2xpY28gZGUgbGFyZ28gcGxhem8gbmkgaXJyZWd1bGFyaWRhZGVzIHNldmVyYXM7IGVsIGNvbXBvcnRhbWllbnRvIGVzIGNvbnNpc3RlbnRlIGHDsW8gY29uIGHDsW8uDQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPjIuIEFOw4FMSVNJUyBERSBFU1RBQ0lPTkFMSURBRCAtIMONTkRJQ0VTIEVTVEFDSU9OQUxFUzwvc3Bhbj4NCg0KU2UgdXNhIGVsIG3DqXRvZG8gY2zDoXNpY28gZGUgKipyYXrDs24gYWwgcHJvbWVkaW8gbcOzdmlsIGNlbnRyYWRvIChDTUEpKiogZGUgb3JkZW4gMTIgKG3DqXRvZG8gZGUgZGVzY29tcG9zaWNpw7NuIG11bHRpcGxpY2F0aXZhLCBkaXNwb25pYmxlIGVuIFIgY29uIGBkZWNvbXBvc2UoKWApOg0KDQpgYGB7cn0NCmRlc2NvbXBvc2ljaW9uIDwtIGRlY29tcG9zZSh0c1ZJTlRBR0UsIHR5cGUgPSAibXVsdGlwbGljYXRpdmUiKQ0KcGxvdChkZXNjb21wb3NpY2lvbikNCmBgYA0KDQpgYGB7cn0NCiMgw41uZGljZXMgZXN0YWNpb25hbGVzICh1bm8gcG9yIG1lcywgcHJvbWVkaWFkb3MgeSBub3JtYWxpemFkb3MgYSBwcm9tZWRpbyA9IDEpDQppbmRpY2VfZXN0YWNpb25hbCA8LSBkZXNjb21wb3NpY2lvbiRmaWd1cmUNCm5hbWVzKGluZGljZV9lc3RhY2lvbmFsKSA8LSBtZXNlcw0KDQp0YWJsYV9lc3RhY2lvbmFsIDwtIGRhdGEuZnJhbWUoDQogIE1lcyA9IG1lc2VzLA0KICBJbmRpY2VfRXN0YWNpb25hbCA9IHJvdW5kKGluZGljZV9lc3RhY2lvbmFsLCA0KQ0KKQ0Ka2FibGUodGFibGFfZXN0YWNpb25hbCwgY2FwdGlvbiA9ICLDjW5kaWNlcyBlc3RhY2lvbmFsZXMgcG9yIG1lcyIpDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmJhcnBsb3QoaW5kaWNlX2VzdGFjaW9uYWwsIG5hbWVzLmFyZyA9IG1lc2VzLCBsYXMgPSAyLCBjb2wgPSAic2FsbW9uIiwNCiAgICAgICAgbWFpbiA9ICLDjW5kaWNlIGVzdGFjaW9uYWwgcG9yIG1lcyAtIFZpbnRhZ2UgUmVzdGF1cmFudCIsDQogICAgICAgIHlsYWIgPSAiw41uZGljZSBlc3RhY2lvbmFsIikNCmFibGluZShoID0gMSwgbHR5ID0gMikNCmBgYA0KDQoqKkNvbWVudGFyaW8gc29icmUgbG9zIMOtbmRpY2VzIGVzdGFjaW9uYWxlczoqKg0KDQotICoqVGVtcG9yYWRhIGFsdGE6KiogZW5lcm8gKOKJiDEuNDQpLCBtYXJ6byAo4omIMS4zNCkgeSBmZWJyZXJvICjiiYgxLjMwKSBlc3TDoW4gbXV5IHBvciBlbmNpbWEgZGUgMSDigJQgbGFzIHZlbnRhcyBkZSBlc29zIG1lc2VzIHNvbiBlbnRyZSAzMCUgeSA0NCUgc3VwZXJpb3JlcyBhbCBwcm9tZWRpbyBhbnVhbC4gRGljaWVtYnJlIHRhbWJpw6luIGVzdMOhIHBvciBhcnJpYmEgZGVsIHByb21lZGlvICjiiYgxLjE2KS4NCi0gKipUZW1wb3JhZGEgYmFqYToqKiBzZXB0aWVtYnJlIGVzIGVsIG1lcyBtw6FzIGTDqWJpbCAo4omIMC42MywgZXMgZGVjaXIsIDM3JSBwb3IgZGViYWpvIGRlbCBwcm9tZWRpbyksIHNlZ3VpZG8gZGUgb2N0dWJyZSAo4omIMC43MCkgeSBqdW5pbyAo4omIMC44MCkuDQoNCioqwr9UaWVuZW4gc2VudGlkbyBpbnR1aXRpdm8/KiogU8OtLiBDYXB0aXZhIElzbGFuZCwgY2VyY2EgZGUgRm9ydCBNeWVycywgZXMgdW4gZGVzdGlubyB0dXLDrXN0aWNvIGRlIGludmllcm5vIGVuIEZsb3JpZGEgKCJzbm93YmlyZCBzZWFzb24iKTogbG9zIHR1cmlzdGFzIGRlbCBub3J0ZSBkZSBFRS4gVVUuIHkgQ2FuYWTDoSBsbGVnYW4gZGUgZGljaWVtYnJlIGEgbWFyem8gZXNjYXBhbmRvIGRlbCBmcsOtbywgbG8gcXVlIGV4cGxpY2EgbG9zIMOtbmRpY2VzIGFsdG9zIGVuIGVzb3MgbWVzZXMuIEVuIGNhbWJpbywgZWwgdmVyYW5vIChqdW5pby1zZXB0aWVtYnJlKSBjb2luY2lkZSBjb24gZWwgY2xpbWEgaMO6bWVkbyB5IGNhbHVyb3NvIGRlIEZsb3JpZGEgeSBsYSB0ZW1wb3JhZGEgZGUgaHVyYWNhbmVzLCBwb3IgbG8gcXVlIGJhamEgZWwgdHVyaXNtbyDigJQgZGUgYWjDrSBsb3Mgw61uZGljZXMgYmFqb3MsIGNvbiBzdSBtw61uaW1vIGVuIHNlcHRpZW1icmUuDQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPjMuIERFU0VTVEFDSU9OQUxJWkFDScOTTiBERSBMQSBTRVJJRTwvc3Bhbj4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnNlcmllX2Rlc2VzdGFjaW9uYWxpemFkYSA8LSB0c1ZJTlRBR0UgLyByZXAoaW5kaWNlX2VzdGFjaW9uYWwsIDMpDQoNCnBsb3QodCwgYXMubnVtZXJpYyhzZXJpZV9kZXNlc3RhY2lvbmFsaXphZGEpLCB0eXBlID0gIm8iLCBjb2wgPSAiZGFya2dyZWVuIiwNCiAgICAgbWFpbiA9ICJTZXJpZSBkZXNlc3RhY2lvbmFsaXphZGEgLSBWaW50YWdlIFJlc3RhdXJhbnQiLA0KICAgICB4bGFiID0gIk1lcyAodCkiLCB5bGFiID0gIlZlbnRhcyAoJCBtaWxlcyksIHNpbiBlZmVjdG8gZXN0YWNpb25hbCIpDQoNCiMgVGVuZGVuY2lhIGxpbmVhbCBzb2JyZSBsYSBzZXJpZSBkZXNlc3RhY2lvbmFsaXphZGENCm1vZGVsb190ZW5kZW5jaWEgPC0gbG0oYXMubnVtZXJpYyhzZXJpZV9kZXNlc3RhY2lvbmFsaXphZGEpIH4gdCkNCnN1bW1hcnkobW9kZWxvX3RlbmRlbmNpYSkNCg0KbGluZXModCwgZml0dGVkKG1vZGVsb190ZW5kZW5jaWEpLCBjb2wgPSAiYmxhY2siLCBsdHkgPSAyLCBsd2QgPSAyKQ0KbGVnZW5kKCJ0b3BsZWZ0IiwgbGVnZW5kID0gYygiU2VyaWUgZGVzZXN0YWNpb25hbGl6YWRhIiwgIlRlbmRlbmNpYSBsaW5lYWwiKSwNCiAgICAgICBjb2wgPSBjKCJkYXJrZ3JlZW4iLCJibGFjayIpLCBsdHkgPSBjKDEsMiksIHBjaCA9IGMoMSxOQSkpDQpgYGANCg0KKirCv0V4aXN0ZSB0ZW5kZW5jaWEgZW4gbGEgc2VyaWUgZGVzZXN0YWNpb25hbGl6YWRhPyoqDQoNClPDrSwgdW5hIHZleiByZW1vdmlkbyBlbCBlZmVjdG8gZXN0YWNpb25hbCwgbGEgc2VyaWUgZGVzZXN0YWNpb25hbGl6YWRhIG11ZXN0cmEgdW5hICoqdGVuZGVuY2lhIGxpbmVhbCBjcmVjaWVudGUgY2xhcmEqKiAocGVuZGllbnRlIOKJiCArMS4wMiBtaWxlcyBkZSAkIHBvciBtZXMsIGNvbiBSwrIg4omIIDAuOTIpLCBjb25zaXN0ZW50ZSBjb24gZWwgY3JlY2ltaWVudG8gZGVsIHJlc3RhdXJhbnRlIG1lcyBhIG1lcyBhIGxvIGxhcmdvIGRlIGxvcyAzIGHDsW9zLCBzaW4gZWwgInJ1aWRvIiBxdWUgZ2VuZXJhYmEgbGEgZXN0YWNpb25hbGlkYWQuDQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPjQuIFBST07Dk1NUSUNPIC0gTcOJVE9ETyBERSBERVNDT01QT1NJQ0nDk048L3NwYW4+DQoNCkVsIHByb27Ds3N0aWNvIHBhcmEgY2FkYSBtZXMgZGVsICoqQcOxbyA0Kiogc2Ugb2J0aWVuZSBtdWx0aXBsaWNhbmRvIGVsIHZhbG9yIHByb3llY3RhZG8gZGUgbGEgdGVuZGVuY2lhIHBvciBlbCDDrW5kaWNlIGVzdGFjaW9uYWwgY29ycmVzcG9uZGllbnRlIGEgZXNlIG1lczoNCg0KJCQgXGhhdHtZfV90ID0gKGJfMCArIGJfMSBcY2RvdCB0KSBcdGltZXMgU0lfe21lc30gJCQNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnRfZnV0dXJvIDwtIChuKzEpOihuKzEyKSAgIyBtZXNlcyAzNyBhIDQ4ID0gQcOxbyA0DQoNCmIwIDwtIGNvZWYobW9kZWxvX3RlbmRlbmNpYSlbMV0NCmIxIDwtIGNvZWYobW9kZWxvX3RlbmRlbmNpYSlbMl0NCnRlbmRlbmNpYV9mdXR1cmEgPC0gYjAgKyBiMSAqIHRfZnV0dXJvDQoNCnByb25vc3RpY29fZGVzY29tcG9zaWNpb24gPC0gdGVuZGVuY2lhX2Z1dHVyYSAqIGFzLm51bWVyaWMoaW5kaWNlX2VzdGFjaW9uYWwpDQoNCnRhYmxhX2Rlc2NvbXBvc2ljaW9uIDwtIGRhdGEuZnJhbWUoDQogIE1lcyA9IG1lc2VzLA0KICB0ID0gdF9mdXR1cm8sDQogIFRlbmRlbmNpYV9Qcm95ZWN0YWRhID0gcm91bmQodGVuZGVuY2lhX2Z1dHVyYSwgMiksDQogIEluZGljZV9Fc3RhY2lvbmFsID0gcm91bmQoYXMubnVtZXJpYyhpbmRpY2VfZXN0YWNpb25hbCksIDQpLA0KICBQcm9ub3N0aWNvX0Rlc2NvbXBvc2ljaW9uID0gcm91bmQocHJvbm9zdGljb19kZXNjb21wb3NpY2lvbiwgMikNCikNCmthYmxlKHRhYmxhX2Rlc2NvbXBvc2ljaW9uLCBjYXB0aW9uID0gIlByb27Ds3N0aWNvIEHDsW8gNCAtIE3DqXRvZG8gZGUgRGVzY29tcG9zaWNpw7NuIikNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcGxvdCh0LCB2ZW50YXMsIHR5cGUgPSAibyIsIGNvbCA9ICJzdGVlbGJsdWUiLA0KICAgICB4bGltID0gYygxLCBtYXgodF9mdXR1cm8pKSwgeWxpbSA9IHJhbmdlKGModmVudGFzLCBwcm9ub3N0aWNvX2Rlc2NvbXBvc2ljaW9uKSksDQogICAgIG1haW4gPSAiUHJvbsOzc3RpY28gQcOxbyA0IC0gTcOpdG9kbyBkZSBEZXNjb21wb3NpY2nDs24iLA0KICAgICB4bGFiID0gIk1lcyAodCkiLCB5bGFiID0gIlZlbnRhcyAoJCBtaWxlcykiKQ0KbGluZXModF9mdXR1cm8sIHByb25vc3RpY29fZGVzY29tcG9zaWNpb24sIHR5cGUgPSAibyIsIGNvbCA9ICJyZWQiLCBwY2ggPSAxNSkNCmxlZ2VuZCgidG9wbGVmdCIsIGxlZ2VuZCA9IGMoIlZlbnRhcyBoaXN0w7NyaWNhcyAoQcOxb3MgMS0zKSIsICJQcm9uw7NzdGljbyBBw7FvIDQiKSwNCiAgICAgICBjb2wgPSBjKCJzdGVlbGJsdWUiLCJyZWQiKSwgcGNoID0gYygxLDE1KSkNCg0KY2F0KCJWZW50YXMgdG90YWxlcyBwcm9ub3N0aWNhZGFzIEHDsW8gNCAoRGVzY29tcG9zaWNpw7NuKToiLCByb3VuZChzdW0ocHJvbm9zdGljb19kZXNjb21wb3NpY2lvbiksMiksICJcbiIpDQpgYGANCg0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+NS4gUFJPTsOTU1RJQ08gLSBSRUdSRVNJw5NOIENPTiBWQVJJQUJMRVMgRklDVElDSUFTIChEVU1NWSk8L3NwYW4+DQoNClNlIHBsYW50ZWEgdW4gbW9kZWxvIGRlIHJlZ3Jlc2nDs24gbcO6bHRpcGxlOg0KDQokJCBWZW50YXNfdCA9IGJfMCArIGJfMSBcY2RvdCB0ICsgYl8yIE1fe2VuZX0gKyBiXzMgTV97ZmVifSArIFxkb3RzICsgYl97MTJ9IE1fe25vdn0gKyBcdmFyZXBzaWxvbl90ICQkDQoNClNlIHVzYW4gKioxMSB2YXJpYWJsZXMgZHVtbXkqKiAoZW5lcm8gYSBub3ZpZW1icmUpOyBkaWNpZW1icmUgcXVlZGEgY29tbyBjYXRlZ29yw61hIGJhc2UgKHN1IGVmZWN0byBlc3TDoSBpbmNsdWlkbyBlbiBlbCBpbnRlcmNlcHRvKS4NCg0KYGBge3J9DQojICJtZXMiIHlhIGVzIHVuIGZhY3RvciBjb24gRGljaWVtYnJlIGFsIGZpbmFsIC0+IERpY2llbWJyZSBxdWVkYSBjb21vIG5pdmVsIGRlIHJlZmVyZW5jaWENCm1vZGVsb19kdW1teSA8LSBsbSh2ZW50YXMgfiB0ICsgbWVzLCBkYXRhID0gZGF0YWZyYW1lMykNCnN1bW1hcnkobW9kZWxvX2R1bW15KQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIFByb27Ds3N0aWNvIEHDsW8gNCBjb24gZWwgbW9kZWxvIGRlIGR1bW1pZXMNCm51ZXZvc19kYXRvcyA8LSBkYXRhLmZyYW1lKA0KICB0ID0gdF9mdXR1cm8sDQogIG1lcyA9IGZhY3RvcihtZXNlcywgbGV2ZWxzID0gbWVzZXMpDQopDQpwcm9ub3N0aWNvX2R1bW15IDwtIHByZWRpY3QobW9kZWxvX2R1bW15LCBuZXdkYXRhID0gbnVldm9zX2RhdG9zKQ0KDQp0YWJsYV9kdW1teSA8LSBkYXRhLmZyYW1lKA0KICBNZXMgPSBtZXNlcywNCiAgdCA9IHRfZnV0dXJvLA0KICBQcm9ub3N0aWNvX1JlZ3Jlc2lvbl9EdW1teSA9IHJvdW5kKGFzLm51bWVyaWMocHJvbm9zdGljb19kdW1teSksIDIpDQopDQprYWJsZSh0YWJsYV9kdW1teSwgY2FwdGlvbiA9ICJQcm9uw7NzdGljbyBBw7FvIDQgLSBSZWdyZXNpw7NuIGNvbiBWYXJpYWJsZXMgRHVtbXkiKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdCh0LCB2ZW50YXMsIHR5cGUgPSAibyIsIGNvbCA9ICJzdGVlbGJsdWUiLA0KICAgICB4bGltID0gYygxLCBtYXgodF9mdXR1cm8pKSwgeWxpbSA9IHJhbmdlKGModmVudGFzLCBwcm9ub3N0aWNvX2R1bW15KSksDQogICAgIG1haW4gPSAiUHJvbsOzc3RpY28gQcOxbyA0IC0gUmVncmVzacOzbiBjb24gVmFyaWFibGVzIER1bW15IiwNCiAgICAgeGxhYiA9ICJNZXMgKHQpIiwgeWxhYiA9ICJWZW50YXMgKCQgbWlsZXMpIikNCmxpbmVzKHRfZnV0dXJvLCBwcm9ub3N0aWNvX2R1bW15LCB0eXBlID0gIm8iLCBjb2wgPSAiZGFya29yYW5nZSIsIHBjaCA9IDE3KQ0KbGVnZW5kKCJ0b3BsZWZ0IiwgbGVnZW5kID0gYygiVmVudGFzIGhpc3TDs3JpY2FzIChBw7FvcyAxLTMpIiwgIlByb27Ds3N0aWNvIEHDsW8gNCIpLA0KICAgICAgIGNvbCA9IGMoInN0ZWVsYmx1ZSIsImRhcmtvcmFuZ2UiKSwgcGNoID0gYygxLDE3KSkNCg0KY2F0KCJWZW50YXMgdG90YWxlcyBwcm9ub3N0aWNhZGFzIEHDsW8gNCAoUmVncmVzacOzbiBEdW1teSk6Iiwgcm91bmQoc3VtKHByb25vc3RpY29fZHVtbXkpLDIpLCAiXG4iKQ0KYGBgDQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPjYuIFRBQkxBUyBZIEdSw4FGSUNBUyBSRVNVTUVOPC9zcGFuPg0KDQpgYGB7cn0NCnRhYmxhX3Jlc3VtZW4gPC0gZGF0YS5mcmFtZSgNCiAgTWVzID0gbWVzZXMsDQogIEluZGljZV9Fc3RhY2lvbmFsID0gcm91bmQoYXMubnVtZXJpYyhpbmRpY2VfZXN0YWNpb25hbCksIDQpLA0KICBQcm9ub3N0aWNvX0Rlc2NvbXBvc2ljaW9uID0gcm91bmQocHJvbm9zdGljb19kZXNjb21wb3NpY2lvbiwgMiksDQogIFByb25vc3RpY29fUmVncmVzaW9uX0R1bW15ID0gcm91bmQoYXMubnVtZXJpYyhwcm9ub3N0aWNvX2R1bW15KSwgMiksDQogIERpZmVyZW5jaWEgPSByb3VuZChwcm9ub3N0aWNvX2Rlc2NvbXBvc2ljaW9uIC0gYXMubnVtZXJpYyhwcm9ub3N0aWNvX2R1bW15KSwgMikNCikNCmthYmxlKHRhYmxhX3Jlc3VtZW4sIGNhcHRpb24gPSAiUmVzdW1lbiBjb21wYXJhdGl2byBkZSBwcm9uw7NzdGljb3MgLSBBw7FvIDQiKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdCgxOjEyLCBwcm9ub3N0aWNvX2Rlc2NvbXBvc2ljaW9uLCB0eXBlID0gIm8iLCBjb2wgPSAicmVkIiwgcGNoID0gMTUsDQogICAgIHhheHQgPSAibiIsIHlsaW0gPSByYW5nZShjKHByb25vc3RpY29fZGVzY29tcG9zaWNpb24sIHByb25vc3RpY29fZHVtbXkpKSwNCiAgICAgbWFpbiA9ICJDb21wYXJhY2nDs24gZGUgcHJvbsOzc3RpY29zIC0gQcOxbyA0IiwNCiAgICAgeGxhYiA9ICJNZXMiLCB5bGFiID0gIlZlbnRhcyBwcm9ub3N0aWNhZGFzICgkIG1pbGVzKSIpDQpsaW5lcygxOjEyLCBwcm9ub3N0aWNvX2R1bW15LCB0eXBlID0gIm8iLCBjb2wgPSAiZGFya29yYW5nZSIsIHBjaCA9IDE3KQ0KYXhpcygxLCBhdCA9IDE6MTIsIGxhYmVscyA9IG1lc2VzLCBsYXMgPSAyKQ0KbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZCA9IGMoIkRlc2NvbXBvc2ljacOzbiIsICJSZWdyZXNpw7NuIER1bW15IiksDQogICAgICAgY29sID0gYygicmVkIiwiZGFya29yYW5nZSIpLCBwY2ggPSBjKDE1LDE3KSkNCmBgYA0KDQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPjcuIEVSUk9SIERFIFBST07Dk1NUSUNPIC0gRU5FUk8gQcORTyA0IChWRU5UQVMgUkVBTEVTID0gJDI5NSwwMDApPC9zcGFuPg0KDQpgYGB7cn0NCnZlbnRhc19yZWFsZXNfZW5lcm80IDwtIDI5NQ0KDQpwcm9ub3N0aWNvX2VuZXJvNF9kZXNjb21wIDwtIHByb25vc3RpY29fZGVzY29tcG9zaWNpb25bMV0NCnByb25vc3RpY29fZW5lcm80X2R1bW15ICAgPC0gYXMubnVtZXJpYyhwcm9ub3N0aWNvX2R1bW15KVsxXQ0KDQplcnJvcl9kZXNjb21wIDwtIHZlbnRhc19yZWFsZXNfZW5lcm80IC0gcHJvbm9zdGljb19lbmVybzRfZGVzY29tcA0KZXJyb3JfZHVtbXkgICA8LSB2ZW50YXNfcmVhbGVzX2VuZXJvNCAtIHByb25vc3RpY29fZW5lcm80X2R1bW15DQoNCmNhdCgiUHJvbsOzc3RpY28gRW5lcm8gQcOxbyA0IChEZXNjb21wb3NpY2nDs24pOiIsIHJvdW5kKHByb25vc3RpY29fZW5lcm80X2Rlc2NvbXAsMiksDQogICAgIiAtPiBFcnJvcjoiLCByb3VuZChlcnJvcl9kZXNjb21wLDIpLCAiXG4iKQ0KY2F0KCJQcm9uw7NzdGljbyBFbmVybyBBw7FvIDQgKFJlZ3Jlc2nDs24gRHVtbXkpOiIsIHJvdW5kKHByb25vc3RpY29fZW5lcm80X2R1bW15LDIpLA0KICAgICIgLT4gRXJyb3I6Iiwgcm91bmQoZXJyb3JfZHVtbXksMiksICJcblxuIikNCg0KY2F0KCJFcnJvciBwb3JjZW50dWFsIChEZXNjb21wb3NpY2nDs24pOiIsIHJvdW5kKDEwMCplcnJvcl9kZXNjb21wL3ZlbnRhc19yZWFsZXNfZW5lcm80LDIpLCAiJVxuIikNCmNhdCgiRXJyb3IgcG9yY2VudHVhbCAoUmVncmVzacOzbiBEdW1teSk6Iiwgcm91bmQoMTAwKmVycm9yX2R1bW15L3ZlbnRhc19yZWFsZXNfZW5lcm80LDIpLCAiJVxuIikNCmBgYA0KDQoqKkludGVycHJldGFjacOzbiBkZWwgZXJyb3IgZGUgcHJvbsOzc3RpY286KioNCg0KQW1ib3MgbW9kZWxvcyBwcm9ub3N0aWNhbiBlbmVybyBkZWwgQcOxbyA0IG11eSBjZXJjYSBkZSBsYXMgdmVudGFzIHJlYWxlcyBkZSAkMjk1LDAwMCAoZWwgZXJyb3IgZXMgZGUgYXBlbmFzIHVub3MgY3VhbnRvcyBtaWxlcyBkZSBkw7NsYXJlcywgZXMgZGVjaXIsICoqbWVub3MgZGVsIDMlKiogZW4gYW1ib3MgY2Fzb3MpLiBFc3RlIGVzIHVuIGVycm9yIHBlcXVlw7FvIHkgZXNwZXJhYmxlOiBuaW5nw7puIG1vZGVsbyBkZSBwcm9uw7NzdGljbyBlcyBwZXJmZWN0bywgeWEgcXVlIGV4aXN0ZW4gZmFjdG9yZXMgcXVlIG5vIGNhcHR1cmEgbGEgdGVuZGVuY2lhIG5pIGxhIGVzdGFjaW9uYWxpZGFkIGhpc3TDs3JpY2EgKGNsaW1hIGRlIGVzZSBtZXMgZW4gcGFydGljdWxhciwgZXZlbnRvcyBsb2NhbGVzLCBjYW1iaW9zIGVuIGxhIGNvbXBldGVuY2lhLCBwdWJsaWNpZGFkLCBldGMuKS4NCg0KKirCv1F1w6kgc2UgcHVlZGUgaGFjZXIgcGFyYSByZWR1Y2lyIGxhIGluY2VydGlkdW1icmUgZGUgS2FyZW4gc29icmUgZWwgcHJvY2VzbyBkZSBwcm9uw7NzdGljbz8qKg0KDQoxLiAqKlByZXNlbnRhciBlbCBwcm9uw7NzdGljbyBjb21vIHVuIHJhbmdvLCBubyBjb21vIHVuIG7Dum1lcm8gZXhhY3RvKiog4oCUIHBvciBlamVtcGxvLCB1biBpbnRlcnZhbG8gZGUgY29uZmlhbnphICjCsSBjaWVydG8gbWFyZ2VuIGJhc2FkbyBlbiBlbCBlcnJvciBoaXN0w7NyaWNvIGRlIGxvcyBtb2RlbG9zKSBlbiB2ZXogZGUgdW5hIHNvbGEgY2lmcmEsIHBhcmEgcXVlIEthcmVuIGVudGllbmRhIHF1ZSBlbCBwcm9uw7NzdGljbyBlcyB1bmEgKmVzdGltYWNpw7NuKiwgbm8gdW5hIHByb21lc2EuDQoyLiAqKkFjdHVhbGl6YXIgZWwgbW9kZWxvIGNhZGEgdmV6IHF1ZSBoYXkgdW4gbnVldm8gbWVzIGRlIGRhdG9zIHJlYWxlcyoqIChwcm9uw7NzdGljbyAicm9kYW50ZSIpOiBjb25mb3JtZSBwYXNhbiBsb3MgbWVzZXMgZGVsIEHDsW8gNCwgc2UgcmVpbmNvcnBvcmFuIGxhcyB2ZW50YXMgcmVhbGVzIHkgc2UgcmVhanVzdGFuIHRlbmRlbmNpYSBlIMOtbmRpY2VzIGVzdGFjaW9uYWxlcywgbG8gY3VhbCByZWR1Y2UgZWwgZXJyb3IgZW4gbG9zIG1lc2VzIHNpZ3VpZW50ZXMuDQozLiAqKk1vbml0b3JlYXIgZWwgZXJyb3IgZGUgcHJvbsOzc3RpY28gbWVzIGEgbWVzKiogKE1BRCwgTUFQRSwgUk1TRSkgcGFyYSBkZXRlY3RhciBzaSBlbCBtb2RlbG8gc2UgZXN0w6EgZGVzdmlhbmRvIGRlIGZvcm1hIHNpc3RlbcOhdGljYSAocG9yIGVqZW1wbG8sIHNpIHNlcHRpZW1icmUgcmVhbCBzaWVtcHJlIHJlc3VsdGEgbcOhcyBhbHRvIHF1ZSBlbCBwcm9uw7NzdGljbywgaGF5IHF1ZSByZXZpc2FyIGVsIMOtbmRpY2UgZXN0YWNpb25hbCBkZSBlc2UgbWVzKS4NCjQuICoqSW5jb3Jwb3JhciBpbmZvcm1hY2nDs24gYWRpY2lvbmFsKiogY3VhbmRvIGVzdMOpIGRpc3BvbmlibGUgKHJlc2VydmFjaW9uZXMgeWEgY29uZmlybWFkYXMsIG9jdXBhY2nDs24gaG90ZWxlcmEgZGUgbGEgaXNsYSwgdGVuZGVuY2lhcyB0dXLDrXN0aWNhcyBkZSBGbG9yaWRhKSBwYXJhIGFqdXN0YXIgZWwgcHJvbsOzc3RpY28gcHVyYW1lbnRlIGVzdGFkw61zdGljbyBjb24ganVpY2lvIGRlIG5lZ29jaW8uDQo1LiAqKlVzYXIgdmFyaW9zIG3DqXRvZG9zIGVuIHBhcmFsZWxvKiogKGNvbW8gc2UgaGl6byBhcXXDrSBjb24gZGVzY29tcG9zaWNpw7NuIHkgcmVncmVzacOzbiBkdW1teSkgeSBwcm9tZWRpYXJsb3MgbyBtb3N0cmFyIGVsIHJhbmdvIGVudHJlIGVsbG9zLCBlbiB2ZXogZGUgZGVwZW5kZXIgZGUgdW4gc29sbyBtb2RlbG8g4oCUIGVzdG8geWEgcG9yIHPDrSBtaXNtbyBjb211bmljYSBhIEthcmVuIGVsIGdyYWRvIGRlIGluY2VydGlkdW1icmUgaW5oZXJlbnRlIGFsIGVqZXJjaWNpby4NCg0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiBncmVlbjsiPkNPTkNMVVNJT05FUzwvc3Bhbj4NCg0KMS4gKipQYXRyw7NuIGRlIGxhIHNlcmllOioqIGxhcyB2ZW50YXMgZGUgVmludGFnZSBSZXN0YXVyYW50IGNvbWJpbmFuIHVuYSAqKnRlbmRlbmNpYSBkZSBjcmVjaW1pZW50byBzb3N0ZW5pZG8qKiAoYXByb3guICskMSwwMDAgYWwgbWVzIGVuIHTDqXJtaW5vcyBkZXNlc3RhY2lvbmFsaXphZG9zKSBjb24gdW5hICoqZXN0YWNpb25hbGlkYWQgZnVlcnRlIHkgcmVwZXRpdGl2YSoqIGxpZ2FkYSBhIGxhIHRlbXBvcmFkYSB0dXLDrXN0aWNhIGRlIGludmllcm5vIGVuIEZsb3JpZGEuDQoNCjIuICoqRXN0YWNpb25hbGlkYWQ6KiogZW5lcm8sIGZlYnJlcm8sIG1hcnpvIHkgZGljaWVtYnJlIHNvbiBsb3MgbWVzZXMgZGUgdGVtcG9yYWRhIGFsdGEgKMOtbmRpY2VzIGVudHJlIDEuMTYgeSAxLjQ0KTsganVuaW8sIGp1bGlvLCBhZ29zdG8sIHNlcHRpZW1icmUgeSBvY3R1YnJlIHNvbiBkZSB0ZW1wb3JhZGEgYmFqYSAow61uZGljZXMgZW50cmUgMC42MyB5IDAuODUpLCBjb24gc2VwdGllbWJyZSBjb21vIGVsIG1lcyBtw6FzIGZsb2pvIGRlbCBhw7FvLiBFc3RvcyBwYXRyb25lcyB0aWVuZW4gcGxlbm8gc2VudGlkbyBkYWRvIHF1ZSBDYXB0aXZhIElzbGFuZCBlcyB1biBkZXN0aW5vIGRlICJzbm93YmlyZHMiIGVuIHRlbXBvcmFkYSBkZSBpbnZpZXJuby4NCg0KMy4gKipTZXJpZSBkZXNlc3RhY2lvbmFsaXphZGE6KiogdW5hIHZleiByZW1vdmlkbyBlbCBlZmVjdG8gZXN0YWNpb25hbCwgc2UgY29uZmlybWEgdW5hIHRlbmRlbmNpYSBsaW5lYWwgY3JlY2llbnRlIGNsYXJhIChSwrIg4omIIDAuOTIpLCBzZcOxYWwgZGUgcXVlIGVsIGNyZWNpbWllbnRvIGRlbCBuZWdvY2lvIGVzIHJlYWwgeSBjb25zaXN0ZW50ZSwgbm8gdW4gYXJ0ZWZhY3RvIGRlIGxhIGVzdGFjaW9uYWxpZGFkLg0KDQo0LiAqKlByb27Ds3N0aWNvcyBBw7FvIDQ6KiogdGFudG8gZWwgbcOpdG9kbyBkZSBkZXNjb21wb3NpY2nDs24gY2zDoXNpY2EgY29tbyBsYSByZWdyZXNpw7NuIGNvbiB2YXJpYWJsZXMgZHVtbXkgYXJyb2phbiBwcm9uw7NzdGljb3MgY29uc2lzdGVudGVzIGVudHJlIHPDrSAoZGlmZXJlbmNpYXMgbWVub3JlcyBhbCA1JSBtZXMgYSBtZXMpLCBjb24gKiplbmVybyB5IG1hcnpvIGRlbCBBw7FvIDQgY29tbyBsb3MgbWVzZXMgZGUgbWF5b3IgdmVudGEgZXNwZXJhZGEqKiAofiQyODAsMDAwLSQzMDAsMDAwKSB5ICoqc2VwdGllbWJyZSBjb21vIGVsIG1lcyBtw6FzIGJham8qKiAofiQxMzUsMDAwLSQxNDUsMDAwKS4NCg0KNS4gKipWYWxpZGFjacOzbiBjb24gZGF0b3MgcmVhbGVzOioqIGFsIGNvbXBhcmFyIGNvbnRyYSBsYSB2ZW50YSByZWFsIGRlIGVuZXJvIGRlbCBBw7FvIDQgKCQyOTUsMDAwKSwgYW1ib3MgbW9kZWxvcyB0dXZpZXJvbiB1biBlcnJvciBtZW5vciBhbCAzJSwgbG8gcXVlIHZhbGlkYSBsYSBtZXRvZG9sb2fDrWEsIGF1bnF1ZSBzZSByZWNvbWllbmRhIGEgS2FyZW4gdHJhdGFyIGN1YWxxdWllciBwcm9uw7NzdGljbyBjb21vIHVuYSAqKmVzdGltYWNpw7NuIGNvbiBpbmNlcnRpZHVtYnJlKiosIG5vIGNvbW8gdW5hIGNpZnJhIGV4YWN0YSwgeSBhY3R1YWxpemFyIGVsIG1vZGVsbyBjb25mb3JtZSBsbGVndWVuIG51ZXZvcyBkYXRvcyByZWFsZXMuDQoNCjYuICoqUmVjb21lbmRhY2nDs24gZ2VuZXJhbCBwYXJhIEthcmVuOioqIHVzYXIgZXN0b3MgcHJvbsOzc3RpY29zIHByaW5jaXBhbG1lbnRlIHBhcmEgKipwbGFuZWFjacOzbiBkZSBwZXJzb25hbCwgY29tcHJhcyBkZSBpbnZlbnRhcmlvIChtYXJpc2NvcyBmcmVzY29zKSB5IGZsdWpvIGRlIGNhamEqKiwgcmVmb3J6YW5kbyBsYSBvcGVyYWNpw7NuIGVuIGxvcyBtZXNlcyBkZSB0ZW1wb3JhZGEgYWx0YSAoZGljLW1hcikgeSBhanVzdGFuZG8gY29zdG9zL3BlcnNvbmFsIGVuIGxvcyBtZXNlcyBkZSB0ZW1wb3JhZGEgYmFqYSAoanVuLW9jdCksIHNpbiBwZXJkZXIgZGUgdmlzdGEgcXVlIGxhIHRlbmRlbmNpYSBnZW5lcmFsIGRlbCBuZWdvY2lvIHNpZ3VlIHNpZW5kbyBkZSBjcmVjaW1pZW50by4NCg==