Importar paquetes y llamar librerias

#install.packages("readxl")
library(readxl)
#install.packages("ggplot2")
library(ggplot2)
#install.packages("dplyr")
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
#install.packages("tidyr")
library(tidyr)
#install.packages("plm")
library(plm)
## 
## Attaching package: 'plm'
## The following objects are masked from 'package:dplyr':
## 
##     between, lag, lead
#install.packages("gplots")
library(gplots)
## 
## ---------------------
## gplots 3.3.0 loaded:
##   * Use citation('gplots') for citation info.
##   * Homepage: https://talgalili.github.io/gplots/
##   * Report issues: https://github.com/talgalili/gplots/issues
##   * Ask questions: https://stackoverflow.com/questions/tagged/gplots
##   * Suppress this message with: suppressPackageStartupMessages(library(gplots))
## ---------------------
## 
## Attaching package: 'gplots'
## The following object is masked from 'package:stats':
## 
##     lowess

Parte 1. Patentes

Este archivo es un panel de datos empresa-año: cada fila es una combinación de una empresa (cusip) y un año (2012–2021). Es un tipo de dataset muy usado en econometría de innovación (gasto en I+D vs. patentes), popularizado por estudios como los de Hall, Griliches y Hausman sobre patentes y R&D. Diccionario de variables: cusip: identificador de la empresa (no es una variable predictiva, es un ID). merger: 1 si la empresa tuvo una fusión importante ese año, 0 si no. employ: empleados, en miles. return: retorno de la acción, en %. patents: patentes solicitadas en el año. patentsg: patentes concedidas en el año — esta es nuestra variable objetivo. stckpr: precio de la acción. rnd: gasto en I+D, en millones de dólares corrientes. rndeflt: gasto en I+D, en millones de dólares deflactados (precios constantes de 1972). rndstck: “stock” acumulado de I+D (inversión acumulada, no solo del año). sales: ventas, en millones de dólares corrientes. sic: código de industria a 4 dígitos. year: año de la observación.

# Cargamos la base de datos
df1 <- read_xls("C:\\Users\\me\\Desktop\\reto\\bds\\PATENT 3.xls")
# Relación con variables explicativas
vars_num <- df1 %>% select(employ, return, patents, stckpr, rnd, rndeflt, rndstck, sales, patentsg)
round(cor(vars_num, use = "complete.obs"), 2)
##          employ return patents stckpr  rnd rndeflt rndstck sales patentsg
## employ     1.00   0.04    0.71   0.38 0.86    0.90    0.85  0.81     0.74
## return     0.04   1.00    0.08   0.31 0.08    0.09    0.07  0.05     0.09
## patents    0.71   0.08    1.00   0.45 0.55    0.61    0.55  0.50     0.94
## stckpr     0.38   0.31    0.45   1.00 0.40    0.45    0.39  0.33     0.47
## rnd        0.86   0.08    0.55   0.40 1.00    0.98    0.99  0.79     0.59
## rndeflt    0.90   0.09    0.61   0.45 0.98    1.00    0.96  0.78     0.64
## rndstck    0.85   0.07    0.55   0.39 0.99    0.96    1.00  0.78     0.59
## sales      0.81   0.05    0.50   0.33 0.79    0.78    0.78  1.00     0.55
## patentsg   0.74   0.09    0.94   0.47 0.59    0.64    0.59  0.55     1.00
ggplot(df1, aes(x = rnd, y = patentsg)) +
  geom_point(alpha = 0.4, color = "#4C72B0") +
  labs(title = "Gasto en I+D vs. patentes concedidas",
       x = "Gasto en I+D (millones $ corrientes)", y = "Patentes concedidas") +
  theme_minimal()

# Convertimos nuestra bdd a datos panel
df_modelo <- df1 %>%
  select(cusip, year, patentsg, rnd, employ, sales, patents, merger)

pdf <- pdata.frame(df_modelo, index = c("cusip", "year"))

Variables explicativas elegidas: ** rnd (gasto en I+D), employ (empleados), sales (ventas), patents (patentes solicitadas ese año) y merger (si hubo fusión). La idea es explicar cuántas patentes le conceden a una empresa (patentsg) a partir de cuánto invierte, qué tan grande es, y cuánto solicitó. A continuación se presenta un modelo de regresión multiple para tener algo con qué comparar nuestros siguientes modelos.

modelo_referencia <- lm(patentsg ~ rnd + employ + sales + patents + merger, data = pdf)
summary(modelo_referencia)
## 
## Call:
## lm(formula = patentsg ~ rnd + employ + sales + patents + merger, 
##     data = pdf)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -266.57   -3.55   -1.49    0.79  644.42 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  1.3832054  0.5906000   2.342  0.01927 *  
## rnd          0.0265024  0.0090625   2.924  0.00349 ** 
## employ       0.1359658  0.0282759   4.809 1.62e-06 ***
## sales        0.0003127  0.0002654   1.178  0.23884    
## patents      0.9653663  0.0112721  85.642  < 2e-16 ***
## merger      -2.4621552  4.0822979  -0.603  0.54648    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 25.5 on 2230 degrees of freedom
##   (24 observations deleted due to missingness)
## Multiple R-squared:  0.8976, Adjusted R-squared:  0.8973 
## F-statistic:  3908 on 5 and 2230 DF,  p-value: < 2.2e-16

Este modelo muestra una R cuadrada de .89, lo que significa que las variables escogidas explican el 89 del comportamiento de la variable dependiente. Pasamos a los modelos Pooling, Within y Random

form <- patentsg ~ rnd + employ + sales + patents + merger

pooling <- plm(form, data = pdf, model = "pooling")
within  <- plm(form, data = pdf, model = "within")
random  <- plm(form, data = pdf, model = "random")

Resumen de Pooling

summary(pooling)
## Pooling Model
## 
## Call:
## plm(formula = form, data = pdf, model = "pooling")
## 
## Unbalanced Panel: n = 225, T = 8-10, N = 2236
## 
## Residuals:
##       Min.    1st Qu.     Median    3rd Qu.       Max. 
## -266.56790   -3.54922   -1.49325    0.78669  644.42008 
## 
## Coefficients:
##                Estimate  Std. Error t-value  Pr(>|t|)    
## (Intercept)  1.38320543  0.59059998  2.3420  0.019267 *  
## rnd          0.02650238  0.00906254  2.9244  0.003486 ** 
## employ       0.13596581  0.02827591  4.8085 1.622e-06 ***
## sales        0.00031272  0.00026542  1.1782  0.238838    
## patents      0.96536633  0.01127212 85.6420 < 2.2e-16 ***
## merger      -2.46215521  4.08229788 -0.6031  0.546484    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Total Sum of Squares:    14152000
## Residual Sum of Squares: 1449500
## R-Squared:      0.89758
## Adj. R-Squared: 0.89735
## F-statistic: 3908.43 on 5 and 2230 DF, p-value: < 2.22e-16

Resumen de Within / Efectos Fijos

summary(within)
## Oneway (individual) effect Within Model
## 
## Call:
## plm(formula = form, data = pdf, model = "within")
## 
## Unbalanced Panel: n = 225, T = 8-10, N = 2236
## 
## Residuals:
##       Min.    1st Qu.     Median    3rd Qu.       Max. 
## -223.29533   -1.88554   -0.31819    1.51373  262.80141 
## 
## Coefficients:
##           Estimate Std. Error  t-value  Pr(>|t|)    
## rnd     -0.1309985  0.0125503 -10.4379 < 2.2e-16 ***
## employ  -0.0663792  0.0607919  -1.0919 0.2750038    
## sales   -0.0013453  0.0003577  -3.7609 0.0001742 ***
## patents  0.0670273  0.0188908   3.5481 0.0003969 ***
## merger   1.8352766  3.5413607   0.5182 0.6043476    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Total Sum of Squares:    715600
## Residual Sum of Squares: 586560
## R-Squared:      0.18033
## Adj. R-Squared: 0.086759
## F-statistic: 88.2654 on 5 and 2006 DF, p-value: < 2.22e-16

Resumen de Random / Efectos Aleatorios

summary(random)
## Oneway (individual) effect Random Effect Model 
##    (Swamy-Arora's transformation)
## 
## Call:
## plm(formula = form, data = pdf, model = "random")
## 
## Unbalanced Panel: n = 225, T = 8-10, N = 2236
## 
## Effects:
##                 var std.dev share
## idiosyncratic 292.4    17.1     1
## individual      0.0     0.0     0
## theta:
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##       0       0       0       0       0       0 
## 
## Residuals:
##       Min.    1st Qu.     Median    3rd Qu.       Max. 
## -266.56790   -3.54922   -1.49325    0.78669  644.42008 
## 
## Coefficients:
##                Estimate  Std. Error z-value  Pr(>|z|)    
## (Intercept)  1.38320543  0.59059998  2.3420  0.019179 *  
## rnd          0.02650238  0.00906254  2.9244  0.003451 ** 
## employ       0.13596581  0.02827591  4.8085  1.52e-06 ***
## sales        0.00031272  0.00026542  1.1782  0.238712    
## patents      0.96536633  0.01127212 85.6420 < 2.2e-16 ***
## merger      -2.46215521  4.08229788 -0.6031  0.546422    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Total Sum of Squares:    14152000
## Residual Sum of Squares: 1449500
## R-Squared:      0.89758
## Adj. R-Squared: 0.89735
## Chisq: 19542.1 on 5 DF, p-value: < 2.22e-16

Pruebas de especificación entre los diferentes modelos

pFtest(within, pooling)
## 
##  F test for individual effects
## 
## data:  form
## F = 13.175, df1 = 224, df2 = 2006, p-value < 2.2e-16
## alternative hypothesis: significant effects
efectos_fijos <- fixef(within)
head(efectos_fijos)
##        800       4626       4671       7500       7603      20753 
## 41.2082685  5.9277947  1.4333872  0.8330017  0.5298820  2.0559662

Pasamos a la prueba de pronóstico

escenario_2022 <- df_modelo %>%
  filter(year == 2021, complete.cases(across(c(rnd, employ, sales, patents, merger))))

fe_df <- data.frame(cusip = names(efectos_fijos), efecto_fijo = as.numeric(efectos_fijos))
# pdata.frame guarda cusip como texto; igualamos el tipo para poder unir
fe_df$cusip <- as.numeric(as.character(fe_df$cusip))

coefs <- coef(within)

pronostico_2022 <- escenario_2022 %>%
  inner_join(fe_df, by = "cusip") %>%
  mutate(
    patentsg_pronostico_2022 =
      efecto_fijo +
      coefs["rnd"]     * rnd +
      coefs["employ"]  * employ +
      coefs["sales"]   * sales +
      coefs["patents"] * patents +
      coefs["merger"]  * merger
  ) %>%
  select(cusip, patentsg_2021 = patentsg, patentsg_pronostico_2022)

cat("Empresas con pronóstico calculado:", nrow(pronostico_2022), "de", n_distinct(df_modelo$cusip), "\n")
## Empresas con pronóstico calculado: 223 de 226
knitr::kable(head(pronostico_2022, 15), digits = 1)
cusip patentsg_2021 patentsg_pronostico_2022
800 70 38.4
4626 7 4.5
4671 1 1.2
7500 1 0.7
7603 0 0.2
20753 0 1.6
21367 0 1.0
23519 6 14.5
29069 0 0.8
38213 2 1.9
54303 2 0.9
67131 0 2.7
67383 9 3.5
74077 18 19.5
77491 0 0.3
summary(pronostico_2022$patentsg_pronostico_2022)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## -13.6016   0.8866   3.2913  22.7981  19.5489 772.9755
cat("Pronósticos negativos (sin sentido para un conteo):", sum(pronostico_2022$patentsg_pronostico_2022 < 0), "\n")
## Pronósticos negativos (sin sentido para un conteo): 8
ggplot(pronostico_2022, aes(x = patentsg_2021, y = patentsg_pronostico_2022)) +
  geom_point(alpha = 0.5, color = "#4C72B0") +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = "grey40") +
  labs(title = "Patentes concedidas: 2021 real vs. 2022 pronosticado",
       subtitle = "La línea punteada marca 'sin cambio' (pronóstico = valor de 2021)",
       x = "Patentes concedidas 2021 (real)", y = "Patentes concedidas 2022 (pronóstico)") +
  theme_minimal()

Conclusiones finales para el ejercicio 1: Las pruebas de especificación (PFtest, phtest) coinciden en que efectos fijos es el modelo correcto para estos datos ya que sí hay características propias de cada empresa que importan, y están correlacionadas con las variables explicativas. El pronóstico para 2022 se hizo bajo el supuesto de “todo sigue igual que 2021” # Parte 2. Cuidado de la Piel Si tuvieras que invertir en alguna sub-categoría, ¿en cuál lo harías? Justifica ampliamente tu respuesta. 1. Arreglos visuales

COLOR_HIST   <- "#2a78d6"  # azul - serie 1 (histórico / lo observado)
COLOR_FCST   <- "#eb6834"  # naranja - serie 2 (pronóstico)
COLOR_POS    <- "#2a78d6"  # azul - variación positiva
COLOR_NEG    <- "#e34948"  # rojo - variación negativa
COLOR_NEUTRAL<- "#898781"
  1. Cargamos la Base de Datos
df2 <- read.csv("C:\\Users\\me\\Desktop\\reto\\bds\\market_sizes_raw.csv")
  1. Limpieza de Datos
# col_types = "text" fuerza a que TODAS las columnas se lean como texto desde
# el inicio (en vez de dejar que readxl adivine el tipo columna por columna).
# Esto evita el error "Can't combine `2011` <character> and `2016` <double>",
# que pasa porque algunas columnas de año traen "-" en otras filas del archivo
# (en Prestige/Dermocosmetics) y eso hace que readxl las adivine como texto,
# mientras que columnas sin "-" las adivina como número — con tipos distintos,
# pivot_longer() no las puede apilar en una sola columna.
raw <- read_excel("C:\\Users\\me\\Desktop\\reto\\bds\\Market_sizes.xlsx", sheet = "Statistics Data", skip = 5, col_types = "text")
names(raw)[1] <- "Geography"
raw <- raw %>% filter(!is.na(Geography), Geography == "Mexico")

categorias_producto <- c("Bath and Shower", "Deodorants", "Depilatories", "Fragrances",
                          "Hair Care", "Men's Grooming", "Skin Care", "Sun Care")

raw <- raw %>% filter(Category %in% categorias_producto)

datos <- raw %>%
  select(Category, `2011`:`2025`) %>%
  pivot_longer(-Category, names_to = "year", values_to = "value") %>%
  mutate(year = as.integer(year), value = suppressWarnings(as.numeric(value)))

knitr::kable(datos %>% filter(year %in% c(2011, 2015, 2019, 2020, 2021, 2025)) %>%
               pivot_wider(names_from = year, values_from = value),
             digits = 0, caption = "Valores de mercado (MXN millones), años seleccionados")
Valores de mercado (MXN millones), años seleccionados
Category 2011 2015 2019 2020 2021 2025
Bath and Shower 8410 10815 12960 14634 15469 21342
Deodorants 9151 12379 16745 13835 15091 21824
Depilatories 802 1106 1516 1491 1564 1873
Fragrances 18728 22568 28483 25516 31801 56742
Hair Care 24854 30416 37247 36951 39555 56290
Men’s Grooming 18672 24774 32693 29669 32939 49508
Skin Care 25650 30964 40638 42196 47981 71830
Sun Care 1145 1693 2444 2043 2294 4326

4. Exploración: ¿cómo se ha movido cada categoría?

ggplot(datos, aes(x = year, y = value)) +
  geom_line(color = COLOR_HIST, linewidth = 0.9) +
  geom_point(color = COLOR_HIST, size = 1.6) +
  facet_wrap(~ Category, scales = "free_y", ncol = 4) +
  labs(title = "Tamaño de mercado histórico por subcategoría (2011-2025)",
       x = "Año", y = "MXN millones") +
  theme_minimal(base_size = 10) +
  theme(strip.text = element_text(face = "bold"))

# 5.Crecimiento acumulado 2011 → 2025 (CAGR)

cagr <- datos %>%
  group_by(Category) %>%
  summarise(
    valor_2011 = value[year == 2011],
    valor_2025 = value[year == 2025],
    cagr = (valor_2025 / valor_2011)^(1 / (2025 - 2011)) - 1
  ) %>%
  arrange(desc(cagr))

knitr::kable(cagr, digits = c(0, 0, 0, 3),
             col.names = c("Categoría", "Valor 2011", "Valor 2025", "CAGR 2011-2025"))
Categoría Valor 2011 Valor 2025 CAGR 2011-2025
Sun Care 1145 4326 0.100
Fragrances 18728 56742 0.082
Skin Care 25650 71830 0.076
Men’s Grooming 18672 49508 0.072
Bath and Shower 8410 21342 0.069
Deodorants 9151 21824 0.064
Depilatories 802 1873 0.062
Hair Care 24854 56290 0.060
cagr %>%
  mutate(Category = factor(Category, levels = Category)) %>%
  ggplot(aes(x = Category, y = cagr)) +
  geom_col(fill = COLOR_HIST, width = 0.65) +
  geom_text(aes(label = scales::percent(cagr, accuracy = 0.1)), hjust = -0.15, size = 3.3) +
  coord_flip(clip = "off") +
  scale_y_continuous(labels = scales::percent, expand = expansion(mult = c(0, 0.18))) +
  labs(title = "Crecimiento anual compuesto (CAGR) 2011-2025 por subcategoría",
       x = NULL, y = "CAGR") +
  theme_minimal(base_size = 11) +
  theme(panel.grid.minor = element_blank())

# 6. Corremos varios modelos para encontrar el que mejor se acomode a nuestros datos

comparar_modelos <- function(d) {
  m_lin <- lm(value ~ year, data = d)
  m_exp <- lm(log(value) ~ year, data = d)

  pred_lin <- predict(m_lin)
  pred_exp <- exp(predict(m_exp))

  rmse_lin <- sqrt(mean((d$value - pred_lin)^2))
  rmse_exp <- sqrt(mean((d$value - pred_exp)^2))

  data.frame(
    r2_lineal = summary(m_lin)$r.squared,
    r2_exponencial = summary(m_exp)$r.squared,
    rmse_lineal = rmse_lin,
    rmse_exponencial = rmse_exp,
    modelo_elegido = ifelse(rmse_exp < rmse_lin, "Exponencial", "Lineal"),
    crecimiento_anual_modelo = ifelse(rmse_exp < rmse_lin,
                                       exp(coef(m_exp)["year"]) - 1,
                                       coef(m_lin)["year"] / mean(d$value))
  )
}

seleccion <- datos %>%
  group_by(Category) %>%
  group_modify(~ comparar_modelos(.x)) %>%
  ungroup()

knitr::kable(seleccion, digits = 4, caption = "Comparación lineal vs. exponencial y modelo elegido por categoría")
Comparación lineal vs. exponencial y modelo elegido por categoría
Category r2_lineal r2_exponencial rmse_lineal rmse_exponencial modelo_elegido crecimiento_anual_modelo
Bath and Shower 0.9484 0.9866 904.9124 501.8314 Exponencial 0.0675
Deodorants 0.9141 0.9231 1098.0006 1065.2359 Exponencial 0.0583
Depilatories 0.9849 0.9689 42.2082 69.2755 Lineal 0.0576
Fragrances 0.8194 0.9033 4901.5038 3852.3353 Exponencial 0.0775
Hair Care 0.9279 0.9728 2447.2723 1773.3294 Exponencial 0.0557
Men’s Grooming 0.9240 0.9616 2531.3553 1948.1793 Exponencial 0.0672
Skin Care 0.9095 0.9703 4392.0379 2717.0337 Exponencial 0.0773
Sun Care 0.8627 0.9349 361.2479 277.1663 Exponencial 0.0924

7. Comparación de modelos

La tabla de la sección anterior ya es esa comparación: en 7 de las 8 categorías el modelo exponencial tiene menor RMSE (mejor ajuste) que el lineal; la excepción es Depilatories, donde el lineal se ajusta mejor. Por eso no se vuelve a correr el mismo cálculo aquí (la sección 6 original y esta tenían el mismo código pegado dos veces, así que se dejó solo una vez).

8. Pronósticos

futuro <- data.frame(year = 2026:2028)

pronosticar <- function(d) {
  m_lin <- lm(value ~ year, data = d)
  m_exp <- lm(log(value) ~ year, data = d)
  rmse_lin <- sqrt(mean((d$value - predict(m_lin))^2))
  rmse_exp <- sqrt(mean((d$value - exp(predict(m_exp)))^2))

  if (rmse_exp < rmse_lin) {
    valor <- exp(predict(m_exp, newdata = futuro))
    modelo <- "Exponencial"
  } else {
    valor <- predict(m_lin, newdata = futuro)
    modelo <- "Lineal"
  }
  data.frame(year = futuro$year, value = valor, modelo = modelo, tipo = "Pronóstico")
}

pron <- datos %>%
  group_by(Category) %>%
  group_modify(~ pronosticar(.x)) %>%
  ungroup()

datos_hist <- datos %>% mutate(tipo = "Histórico")

serie_completa <- bind_rows(
  datos_hist %>% select(Category, year, value, tipo),
  pron %>% select(Category, year, value, tipo)
)

knitr::kable(pron %>% select(Category, year, value, modelo) %>%
               pivot_wider(names_from = year, values_from = value),
             digits = 0, caption = "Pronóstico de valor de mercado 2026-2028 (MXN millones)")
Pronóstico de valor de mercado 2026-2028 (MXN millones)
Category modelo 2026 2027 2028
Bath and Shower Exponencial 22102 23595 25188
Deodorants Exponencial 22763 24092 25497
Depilatories Lineal 2001 2080 2159
Fragrances Exponencial 52078 56113 60460
Hair Care Exponencial 55318 58398 61650
Men’s Grooming Exponencial 50702 54109 57746
Skin Care Exponencial 72529 78134 84172
Sun Care Exponencial 4413 4820 5265
ggplot(serie_completa, aes(x = year, y = value, color = tipo, linetype = tipo)) +
  geom_line(linewidth = 0.9) +
  geom_point(size = 1.4) +
  facet_wrap(~ Category, scales = "free_y", ncol = 4) +
  scale_color_manual(values = c("Histórico" = COLOR_HIST, "Pronóstico" = COLOR_FCST)) +
  scale_linetype_manual(values = c("Histórico" = "solid", "Pronóstico" = "22")) +
  labs(title = "Histórico (2011-2025) y pronóstico (2026-2028) por subcategoría",
       x = "Año", y = "MXN millones", color = NULL, linetype = NULL) +
  theme_minimal(base_size = 10) +
  theme(strip.text = element_text(face = "bold"), legend.position = "top")

9. ¿En qué subcategoría invertiría?

Skin Care. La justificación combina tres criterios, no solo el crecimiento histórico:

  1. Tamaño y trayectoria: ya es la categoría más grande (~MXN 71,830 millones en 2025, proyectada a ~MXN 84,200 millones en 2028), así que invertir ahí no depende de que un nicho pequeño se vuelva grande — el mercado ya es grande y sigue creciendo a un ritmo alto (~7.6%/año compuesto).
  2. Calidad y confiabilidad de la tendencia: el modelo exponencial ajusta con R² = 0.97 sobre 15 años de historia, uno de los mejores ajustes de toda la categoría. Eso da más confianza en el pronóstico que, por ejemplo, el de Fragrances (R² = 0.90) o Sun Care (R² = 0.93), cuyas series son más erráticas.
  3. Resiliencia ante crisis: Skin Care fue una de las dos únicas categorías que no cayó en 2020 (+3.8%) y tuvo la mejor recuperación relativa hacia 2021 (+18.1% vs. 2019). Eso importa mucho para una decisión de inversión real: un mercado que crece rápido pero se desploma en la siguiente crisis (como Sun Care, -16.4% en 2020 y todavía -6.2% por debajo de 2019 un año después) es una apuesta más arriesgada.

Parte 3. Banco mundial

url_gdp   <- "https://raw.githubusercontent.com/datasets/gdp/master/data/gdp.csv"
url_infl  <- "https://raw.githubusercontent.com/datasets/inflation/master/data/inflation-consumer.csv"
url_pop   <- "https://raw.githubusercontent.com/datasets/population/master/data/population.csv"

gdp  <- read.csv(url_gdp)
infl <- read.csv(url_infl)
pop  <- read.csv(url_pop)

paises <- c("CHN", "JPN", "KOR")
nombres_paises <- c(CHN = "China", JPN = "Japón", KOR = "Corea del Sur")

gdp  <- gdp  %>% filter(Country.Code %in% paises) %>% select(Country.Code, Year, gdp_usd = Value)
pop  <- pop  %>% filter(Country.Code %in% paises) %>% select(Country.Code, Year, poblacion = Value)
infl <- infl %>% filter(Country.Code %in% paises) %>% select(Country.Code, Year, inflacion_pct = Inflation)

datos_bm <- gdp %>%
  inner_join(pop, by = c("Country.Code", "Year")) %>%
  inner_join(infl, by = c("Country.Code", "Year")) %>%
  filter(Year >= 1990, Year <= 2023) %>%
  mutate(
    pais = nombres_paises[Country.Code],
    gdp_percapita_usd = gdp_usd / poblacion
  ) %>%
  rename(anio = Year) %>%
  select(pais, Country.Code, anio, gdp_usd, poblacion, gdp_percapita_usd, inflacion_pct) %>%
  arrange(pais, anio)

cat("Filas:", nrow(datos_bm), " | Países:", n_distinct(datos_bm$pais), " | Años:", min(datos_bm$anio), "-", max(datos_bm$anio), "\n")
## Filas: 102  | Países: 3  | Años: 1990 - 2023
knitr::kable(datos_bm %>% filter(anio %in% c(1990, 2000, 2010, 2020, 2023)), digits = 1,
             caption = "Muestra de los datos importados")
Muestra de los datos importados
pais Country.Code anio gdp_usd poblacion gdp_percapita_usd inflacion_pct
China CHN 1990 3.608579e+11 1135185000 317.9 5.7
China CHN 2000 1.211332e+12 1262645000 959.4 2.1
China CHN 2010 6.087192e+12 1337705000 4550.5 6.9
China CHN 2020 1.468774e+13 1411100000 10408.7 0.5
China CHN 2023 1.779478e+13 1410710000 12614.1 -0.6
Corea del Sur KOR 1990 2.833658e+11 42869283 6610.0 10.1
Corea del Sur KOR 2000 5.761794e+11 47008111 12257.0 1.0
Corea del Sur KOR 2010 1.143672e+12 49554112 23079.3 2.7
Corea del Sur KOR 2020 1.644313e+12 51836239 31721.3 1.6
Corea del Sur KOR 2023 1.712793e+12 51712619 33121.4 2.1
Japón JPN 1990 3.185905e+12 123478000 25801.4 2.6
Japón JPN 2000 4.968359e+12 126843000 39169.4 -1.3
Japón JPN 2010 5.759072e+12 128070000 44968.2 -1.9
Japón JPN 2020 5.055587e+12 126261000 40040.8 0.9
Japón JPN 2023 4.212945e+12 124516650 33834.4 3.8

Panel balanceado: 3 países × 34 años = 102 observaciones, sin datos faltantes.

Exploración

ggplot(datos_bm, aes(x = anio, y = gdp_percapita_usd, color = pais)) +
  geom_line(linewidth = 1) +
  scale_color_manual(values = c("China" = "#2a78d6", "Japón" = "#eb6834", "Corea del Sur" = "#1baf7a")) +
  labs(title = "PIB per cápita (US$ corrientes), 1990-2023",
       x = "Año", y = "PIB per cápita (US$)", color = NULL) +
  theme_minimal(base_size = 11)

Las diferencias de nivel son enormes: Japón y Corea del Sur ya eran economías de ingreso alto en 1990, mientras que China partió de un PIB per cápita muy bajo y ha ido cerrando la brecha con un crecimiento sostenido. Esta clase de diferencias estructurales entre países es exactamente lo que un modelo pooled (que ignora la identidad del país) no puede capturar bien.

ggplot(datos_bm, aes(x = anio, y = inflacion_pct, color = pais)) +
  geom_line(linewidth = 1) +
  geom_hline(yintercept = 0, color = "grey50", linetype = "dashed") +
  scale_color_manual(values = c("China" = "#2a78d6", "Japón" = "#eb6834", "Corea del Sur" = "#1baf7a")) +
  labs(title = "Inflación anual (%), 1990-2023",
       x = "Año", y = "Inflación (%)", color = NULL) +
  theme_minimal(base_size = 11)

Japón ha tenido inflación cercana a cero (o negativa, deflación) buena parte del periodo; China y Corea tuvieron picos de inflación más altos en los años 90 y se han moderado desde entonces.

Paso 1: estructura de panel y modelo de referencia

pdf_bm <- pdata.frame(datos_bm, index = c("pais", "anio"))

modelo_referencia_bm <- lm(gdp_percapita_usd ~ inflacion_pct, data = pdf_bm)
summary(modelo_referencia_bm)
## 
## Call:
## lm(formula = gdp_percapita_usd ~ inflacion_pct, data = pdf_bm)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -29089  -8392   2748   9522  23319 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)    26913.7     1455.2  18.495  < 2e-16 ***
## inflacion_pct  -2413.5      322.9  -7.474 3.02e-11 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 12190 on 100 degrees of freedom
## Multiple R-squared:  0.3584, Adjusted R-squared:  0.352 
## F-statistic: 55.86 on 1 and 100 DF,  p-value: 3.02e-11

Paso 2: los tres modelos (pooling, within, random)

form_bm <- gdp_percapita_usd ~ inflacion_pct

pooling_bm <- plm(form_bm, data = pdf_bm, model = "pooling")
within_bm  <- plm(form_bm, data = pdf_bm, model = "within")
random_bm  <- plm(form_bm, data = pdf_bm, model = "random")

Pooling

summary(pooling_bm)
## Pooling Model
## 
## Call:
## plm(formula = form_bm, data = pdf_bm, model = "pooling")
## 
## Balanced Panel: n = 3, T = 34, N = 102
## 
## Residuals:
##     Min.  1st Qu.   Median  3rd Qu.     Max. 
## -29088.7  -8391.5   2748.3   9521.7  23318.5 
## 
## Coefficients:
##               Estimate Std. Error t-value  Pr(>|t|)    
## (Intercept)   26913.65    1455.17 18.4952 < 2.2e-16 ***
## inflacion_pct -2413.48     322.93 -7.4738  3.02e-11 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Total Sum of Squares:    2.3176e+10
## Residual Sum of Squares: 1.487e+10
## R-Squared:      0.35839
## Adj. R-Squared: 0.35197
## F-statistic: 55.8571 on 1 and 100 DF, p-value: 3.0202e-11

Within / Efectos fijos

summary(within_bm)
## Oneway (individual) effect Within Model
## 
## Call:
## plm(formula = form_bm, data = pdf_bm, model = "within")
## 
## Balanced Panel: n = 3, T = 34, N = 102
## 
## Residuals:
##       Min.    1st Qu.     Median    3rd Qu.       Max. 
## -13310.366  -4410.235    -26.126   3237.947  14535.611 
## 
## Coefficients:
##               Estimate Std. Error t-value  Pr(>|t|)    
## inflacion_pct  -849.76     179.68 -4.7294 7.559e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Total Sum of Squares:    4181900000
## Residual Sum of Squares: 3404800000
## R-Squared:      0.18583
## Adj. R-Squared: 0.1609
## F-statistic: 22.3675 on 1 and 98 DF, p-value: 7.559e-06

Random / Efectos aleatorios

summary(random_bm)
## Oneway (individual) effect Random Effect Model 
##    (Swamy-Arora's transformation)
## 
## Call:
## plm(formula = form_bm, data = pdf_bm, model = "random")
## 
## Balanced Panel: n = 3, T = 34, N = 102
## 
## Effects:
##                    var  std.dev share
## idiosyncratic 34742880     5894 0.627
## individual    20630819     4542 0.373
## theta: 0.7828
## 
## Residuals:
##       Min.    1st Qu.     Median    3rd Qu.       Max. 
## -13724.627  -5046.723    -19.408   4771.156  14505.859 
## 
## Coefficients:
##               Estimate Std. Error z-value  Pr(>|z|)    
## (Intercept)   23222.90    2958.95  7.8484 4.215e-15 ***
## inflacion_pct  -946.06     193.71 -4.8839 1.040e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Total Sum of Squares:    5078300000
## Residual Sum of Squares: 4100300000
## R-Squared:      0.19258
## Adj. R-Squared: 0.18451
## Chisq: 23.852 on 1 DF, p-value: 1.0403e-06

En los tres modelos el coeficiente de inflación es negativo y significativo: más inflación se asocia con menor PIB per cápita (o con países/años de menor PIB per cápita), consistente con la idea económica de que la inestabilidad de precios va de la mano de menor desarrollo relativo en esta muestra.

Paso 3: ¿cuál modelo es el correcto?

Pooling vs. efectos fijos

pFtest(within_bm, pooling_bm)
## 
##  F test for individual effects
## 
## data:  form_bm
## F = 165, df1 = 2, df2 = 98, p-value < 2.2e-16
## alternative hypothesis: significant effects

El p-value es prácticamente 0: se rechaza pooling. Hay efectos propios de cada país (su nivel de desarrollo, historia económica, etc.) que pooling está ignorando. Efectos fijos le gana a pooling.

Efectos fijos vs. efectos aleatorios (prueba de Hausman)

phtest(random_bm, within_bm)
## 
##  Hausman Test
## 
## data:  form_bm
## chisq = 1.7694, df = 1, p-value = 0.1835
## alternative hypothesis: one model is inconsistent

Aquí el resultado es distinto al del ejercicio de patentes: el p-value es 0.18, no significativo. Eso quiere decir que no se rechaza que random effects sea consistente, así que —a diferencia del ejercicio anterior— aquí sí conviene usar random effects, porque es más eficiente que efectos fijos (usa tanto la variación entre países como en el tiempo) y la prueba de Hausman no encontró evidencia de que esté sesgado.

Conclusión del Paso 3: el modelo elegido es efectos aleatorios (random).

Paso 4: predicciones 2024-2026

Random effects tiene un solo intercepto y una sola pendiente para los tres países (a diferencia de efectos fijos, que le da un intercepto propio a cada uno), así que pronosticar es tan simple como en tu ejemplo original: intercepto + pendiente * inflación. El único supuesto que necesitamos es un valor de inflación futura por país; usamos el último dato observado (2023) como escenario “si la inflación se mantiene igual”.

intercepto_bm <- coef(random_bm)["(Intercept)"]
pendiente_bm  <- coef(random_bm)["inflacion_pct"]

inflacion_2023 <- datos_bm %>% filter(anio == 2023) %>% select(pais, inflacion_pct)

pronostico_bm <- expand.grid(pais = unique(datos_bm$pais), anio = 2024:2026) %>%
  left_join(inflacion_2023, by = "pais") %>%
  mutate(gdp_percapita_pronostico = intercepto_bm + pendiente_bm * inflacion_pct)

knitr::kable(pronostico_bm %>% pivot_wider(names_from = anio, values_from = gdp_percapita_pronostico,
                                          id_cols = pais),
             digits = 0, caption = "PIB per cápita pronosticado 2024-2026 (US$, supone inflación = la de 2023)")
PIB per cápita pronosticado 2024-2026 (US$, supone inflación = la de 2023)
pais 2024 2025 2026
China 23774 23774 23774
Corea del Sur 21270 21270 21270
Japón 19634 19634 19634
serie_completa_bm <- bind_rows(
  datos_bm %>% select(pais, anio, valor = gdp_percapita_usd) %>% mutate(tipo = "Histórico"),
  pronostico_bm %>% select(pais, anio, valor = gdp_percapita_pronostico) %>% mutate(tipo = "Pronóstico")
)

ggplot(serie_completa_bm, aes(x = anio, y = valor, color = pais, linetype = tipo)) +
  geom_line(linewidth = 1) +
  scale_color_manual(values = c("China" = "#2a78d6", "Japón" = "#eb6834", "Corea del Sur" = "#1baf7a")) +
  scale_linetype_manual(values = c("Histórico" = "solid", "Pronóstico" = "22")) +
  labs(title = "PIB per cápita: histórico (1990-2023) y pronóstico (2024-2026)",
       x = "Año", y = "PIB per cápita (US$)", color = NULL, linetype = NULL) +
  theme_minimal(base_size = 11)

Por qué el pronóstico sale casi plano: como el modelo de random effects solo usa la inflación para explicar el PIB per cápita (y le pega el mismo intercepto/pendiente a los tres países), el pronóstico 2024-2026 no mueve mucho el nivel de cada país frente a su valor de 2023 — el modelo no tiene forma de “saber” que China ha venido creciendo de forma sostenida si esa tendencia no pasa por la inflación. Esto es una limitación real y vale la pena decirla en las conclusiones, no esconderla.

Conclusiones

  • El PIB per cápita de China, Japón y Corea del Sur tiene diferencias de nivel tan grandes que un modelo pooled (que ignora la identidad del país) queda claramente rechazado por la prueba F (pFtest, p < 0.001).
  • A diferencia del ejercicio de patentes, aquí la prueba de Hausman (phtest, p = 0.18) no rechaza efectos aleatorios, así que el modelo elegido es random effects — más eficiente que efectos fijos porque aprovecha también la variación entre países, no solo dentro de cada país en el tiempo.
  • La relación estimada entre inflación y PIB per cápita es negativa y significativa en los tres modelos: cada punto porcentual adicional de inflación se asocia con, aproximadamente, US$946 menos de PIB per cápita (según el modelo de random effects).
  • El ajuste del modelo es modesto (R² ≈ 0.19): la inflación por sí sola explica una parte relativamente pequeña de las diferencias de PIB per cápita entre estos tres países — tiene sentido, porque el desarrollo económico depende de muchos factores más (capital humano, instituciones, comercio, tecnología) que no están en este modelo de una sola variable. El pronóstico 2024-2026 hereda esa misma limitación: como el “shock” de inflación no cambia mucho de un año a otro, el pronóstico tampoco se mueve mucho — esto no es un error del código, es una consecuencia esperable de haber usado solo una variable explicativa.
  • Nota de asociación vs. causalidad: esta correlación negativa entre inflación y PIB per cápita no debe leerse como que “bajar la inflación garantiza” mayor PIB per cápita — con 3 países y una sola variable, es evidencia sugestiva, no una prueba causal.
LS0tDQp0aXRsZTogIkFjdGl2aWRhZCAxIg0KYXV0aG9yOiAiT3NjYXIgUmV0ZXMgLSBBMDEzODM2NTMiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KICAgIHRoZW1lOiB1bml0ZWQNCmRhdGU6ICIyMDI2LTA4LTEzIg0KLS0tDQojIEltcG9ydGFyIHBhcXVldGVzIHkgbGxhbWFyIGxpYnJlcmlhcw0KYGBge3Igd2FybmluZz1GQUxTRX0NCiNpbnN0YWxsLnBhY2thZ2VzKCJyZWFkeGwiKQ0KbGlicmFyeShyZWFkeGwpDQojaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpDQpsaWJyYXJ5KGdncGxvdDIpDQojaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KbGlicmFyeShkcGx5cikNCiNpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5ciIpDQpsaWJyYXJ5KHRpZHlyKQ0KI2luc3RhbGwucGFja2FnZXMoInBsbSIpDQpsaWJyYXJ5KHBsbSkNCiNpbnN0YWxsLnBhY2thZ2VzKCJncGxvdHMiKQ0KbGlicmFyeShncGxvdHMpDQpgYGANCiMgUGFydGUgMS4gUGF0ZW50ZXMNCkVzdGUgYXJjaGl2byBlcyB1biBwYW5lbCBkZSBkYXRvcyBlbXByZXNhLWHDsW86IGNhZGEgZmlsYSBlcyB1bmEgY29tYmluYWNpw7NuIGRlIHVuYSBlbXByZXNhIChjdXNpcCkgeSB1biBhw7FvICgyMDEy4oCTMjAyMSkuIEVzIHVuIHRpcG8gZGUgZGF0YXNldCBtdXkgdXNhZG8gZW4gZWNvbm9tZXRyw61hIGRlIGlubm92YWNpw7NuIChnYXN0byBlbiBJK0QgdnMuIHBhdGVudGVzKSwgcG9wdWxhcml6YWRvIHBvciBlc3R1ZGlvcyBjb21vIGxvcyBkZSBIYWxsLCBHcmlsaWNoZXMgeSBIYXVzbWFuIHNvYnJlIHBhdGVudGVzIHkgUiZELg0KRGljY2lvbmFyaW8gZGUgdmFyaWFibGVzOg0KY3VzaXA6IGlkZW50aWZpY2Fkb3IgZGUgbGEgZW1wcmVzYSAobm8gZXMgdW5hIHZhcmlhYmxlIHByZWRpY3RpdmEsIGVzIHVuIElEKS4NCm1lcmdlcjogMSBzaSBsYSBlbXByZXNhIHR1dm8gdW5hIGZ1c2nDs24gaW1wb3J0YW50ZSBlc2UgYcOxbywgMCBzaSBuby4NCmVtcGxveTogZW1wbGVhZG9zLCBlbiBtaWxlcy4NCnJldHVybjogcmV0b3JubyBkZSBsYSBhY2Npw7NuLCBlbiAlLg0KcGF0ZW50czogcGF0ZW50ZXMgc29saWNpdGFkYXMgZW4gZWwgYcOxby4NCnBhdGVudHNnOiBwYXRlbnRlcyBjb25jZWRpZGFzIGVuIGVsIGHDsW8g4oCUIGVzdGEgZXMgbnVlc3RyYSB2YXJpYWJsZSBvYmpldGl2by4NCnN0Y2twcjogcHJlY2lvIGRlIGxhIGFjY2nDs24uDQpybmQ6IGdhc3RvIGVuIEkrRCwgZW4gbWlsbG9uZXMgZGUgZMOzbGFyZXMgY29ycmllbnRlcy4NCnJuZGVmbHQ6IGdhc3RvIGVuIEkrRCwgZW4gbWlsbG9uZXMgZGUgZMOzbGFyZXMgZGVmbGFjdGFkb3MgKHByZWNpb3MgY29uc3RhbnRlcyBkZSAxOTcyKS4NCnJuZHN0Y2s6ICJzdG9jayIgYWN1bXVsYWRvIGRlIEkrRCAoaW52ZXJzacOzbiBhY3VtdWxhZGEsIG5vIHNvbG8gZGVsIGHDsW8pLg0Kc2FsZXM6IHZlbnRhcywgZW4gbWlsbG9uZXMgZGUgZMOzbGFyZXMgY29ycmllbnRlcy4NCnNpYzogY8OzZGlnbyBkZSBpbmR1c3RyaWEgYSA0IGTDrWdpdG9zLg0KeWVhcjogYcOxbyBkZSBsYSBvYnNlcnZhY2nDs24uDQpgYGB7cn0NCiMgQ2FyZ2Ftb3MgbGEgYmFzZSBkZSBkYXRvcw0KZGYxIDwtIHJlYWRfeGxzKCJDOlxcVXNlcnNcXG1lXFxEZXNrdG9wXFxyZXRvXFxiZHNcXFBBVEVOVCAzLnhscyIpDQpgYGANCmBgYHtyIGNvcnJlbGFjaW9uZXN9DQojIFJlbGFjacOzbiBjb24gdmFyaWFibGVzIGV4cGxpY2F0aXZhcw0KdmFyc19udW0gPC0gZGYxICU+JSBzZWxlY3QoZW1wbG95LCByZXR1cm4sIHBhdGVudHMsIHN0Y2twciwgcm5kLCBybmRlZmx0LCBybmRzdGNrLCBzYWxlcywgcGF0ZW50c2cpDQpyb3VuZChjb3IodmFyc19udW0sIHVzZSA9ICJjb21wbGV0ZS5vYnMiKSwgMikNCmBgYA0KDQpgYGB7ciBzY2F0dGVyLXJuZH0NCmdncGxvdChkZjEsIGFlcyh4ID0gcm5kLCB5ID0gcGF0ZW50c2cpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjQsIGNvbG9yID0gIiM0QzcyQjAiKSArDQogIGxhYnModGl0bGUgPSAiR2FzdG8gZW4gSStEIHZzLiBwYXRlbnRlcyBjb25jZWRpZGFzIiwNCiAgICAgICB4ID0gIkdhc3RvIGVuIEkrRCAobWlsbG9uZXMgJCBjb3JyaWVudGVzKSIsIHkgPSAiUGF0ZW50ZXMgY29uY2VkaWRhcyIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCmBgYHtyIHBkYXRhfQ0KIyBDb252ZXJ0aW1vcyBudWVzdHJhIGJkZCBhIGRhdG9zIHBhbmVsDQpkZl9tb2RlbG8gPC0gZGYxICU+JQ0KICBzZWxlY3QoY3VzaXAsIHllYXIsIHBhdGVudHNnLCBybmQsIGVtcGxveSwgc2FsZXMsIHBhdGVudHMsIG1lcmdlcikNCg0KcGRmIDwtIHBkYXRhLmZyYW1lKGRmX21vZGVsbywgaW5kZXggPSBjKCJjdXNpcCIsICJ5ZWFyIikpDQpgYGANClZhcmlhYmxlcyBleHBsaWNhdGl2YXMgZWxlZ2lkYXM6ICoqIGBybmRgIChnYXN0byBlbiBJK0QpLCBgZW1wbG95YCAoZW1wbGVhZG9zKSwNCmBzYWxlc2AgKHZlbnRhcyksIGBwYXRlbnRzYCAocGF0ZW50ZXMgc29saWNpdGFkYXMgZXNlIGHDsW8pIHkgYG1lcmdlcmAgKHNpIGh1Ym8NCmZ1c2nDs24pLiBMYSBpZGVhIGVzIGV4cGxpY2FyIGN1w6FudGFzIHBhdGVudGVzIGxlICpjb25jZWRlbiogYSB1bmEgZW1wcmVzYQ0KKGBwYXRlbnRzZ2ApIGEgcGFydGlyIGRlIGN1w6FudG8gaW52aWVydGUsIHF1w6kgdGFuIGdyYW5kZSBlcywgeSBjdcOhbnRvIHNvbGljaXTDsy4NCkEgY29udGludWFjacOzbiBzZSBwcmVzZW50YSB1biBtb2RlbG8gZGUgcmVncmVzacOzbiBtdWx0aXBsZSBwYXJhIHRlbmVyIGFsZ28gY29uIHF1w6kgY29tcGFyYXIgbnVlc3Ryb3Mgc2lndWllbnRlcyBtb2RlbG9zLg0KYGBge3IgbG0tcmVmZXJlbmNpYX0NCm1vZGVsb19yZWZlcmVuY2lhIDwtIGxtKHBhdGVudHNnIH4gcm5kICsgZW1wbG95ICsgc2FsZXMgKyBwYXRlbnRzICsgbWVyZ2VyLCBkYXRhID0gcGRmKQ0Kc3VtbWFyeShtb2RlbG9fcmVmZXJlbmNpYSkNCmBgYA0KRXN0ZSBtb2RlbG8gbXVlc3RyYSB1bmEgUiBjdWFkcmFkYSBkZSAuODksIGxvIHF1ZSBzaWduaWZpY2EgcXVlIGxhcyB2YXJpYWJsZXMgZXNjb2dpZGFzIGV4cGxpY2FuIGVsIDg5IGRlbCBjb21wb3J0YW1pZW50byBkZSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZS4NClBhc2Ftb3MgYSBsb3MgbW9kZWxvcyBQb29saW5nLCBXaXRoaW4geSBSYW5kb20NCmBgYHtyIHRyZXMtbW9kZWxvc30NCmZvcm0gPC0gcGF0ZW50c2cgfiBybmQgKyBlbXBsb3kgKyBzYWxlcyArIHBhdGVudHMgKyBtZXJnZXINCg0KcG9vbGluZyA8LSBwbG0oZm9ybSwgZGF0YSA9IHBkZiwgbW9kZWwgPSAicG9vbGluZyIpDQp3aXRoaW4gIDwtIHBsbShmb3JtLCBkYXRhID0gcGRmLCBtb2RlbCA9ICJ3aXRoaW4iKQ0KcmFuZG9tICA8LSBwbG0oZm9ybSwgZGF0YSA9IHBkZiwgbW9kZWwgPSAicmFuZG9tIikNCmBgYA0KUmVzdW1lbiBkZSBQb29saW5nDQpgYGB7ciByZXN1bWVuLXBvb2xpbmd9DQpzdW1tYXJ5KHBvb2xpbmcpDQpgYGANClJlc3VtZW4gZGUgV2l0aGluIC8gRWZlY3RvcyBGaWpvcw0KYGBge3IgcmVzdW1lbi13aXRoaW59DQpzdW1tYXJ5KHdpdGhpbikNCmBgYA0KUmVzdW1lbiBkZSBSYW5kb20gLyBFZmVjdG9zIEFsZWF0b3Jpb3MNCmBgYHtyIHJlc3VtZW4tcmFuZG9tfQ0Kc3VtbWFyeShyYW5kb20pDQpgYGANClBydWViYXMgZGUgZXNwZWNpZmljYWNpw7NuIGVudHJlIGxvcyBkaWZlcmVudGVzIG1vZGVsb3MNCmBgYHtyIFBGdGVzdH0NCnBGdGVzdCh3aXRoaW4sIHBvb2xpbmcpDQpgYGANCmBgYHtyIGZpeGVmfQ0KZWZlY3Rvc19maWpvcyA8LSBmaXhlZih3aXRoaW4pDQpoZWFkKGVmZWN0b3NfZmlqb3MpDQpgYGANClBhc2Ftb3MgYSBsYSBwcnVlYmEgZGUgcHJvbsOzc3RpY28NCmBgYHtyIGVzY2VuYXJpby0yMDIyfQ0KZXNjZW5hcmlvXzIwMjIgPC0gZGZfbW9kZWxvICU+JQ0KICBmaWx0ZXIoeWVhciA9PSAyMDIxLCBjb21wbGV0ZS5jYXNlcyhhY3Jvc3MoYyhybmQsIGVtcGxveSwgc2FsZXMsIHBhdGVudHMsIG1lcmdlcikpKSkNCg0KZmVfZGYgPC0gZGF0YS5mcmFtZShjdXNpcCA9IG5hbWVzKGVmZWN0b3NfZmlqb3MpLCBlZmVjdG9fZmlqbyA9IGFzLm51bWVyaWMoZWZlY3Rvc19maWpvcykpDQojIHBkYXRhLmZyYW1lIGd1YXJkYSBjdXNpcCBjb21vIHRleHRvOyBpZ3VhbGFtb3MgZWwgdGlwbyBwYXJhIHBvZGVyIHVuaXINCmZlX2RmJGN1c2lwIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGZlX2RmJGN1c2lwKSkNCg0KY29lZnMgPC0gY29lZih3aXRoaW4pDQoNCnByb25vc3RpY29fMjAyMiA8LSBlc2NlbmFyaW9fMjAyMiAlPiUNCiAgaW5uZXJfam9pbihmZV9kZiwgYnkgPSAiY3VzaXAiKSAlPiUNCiAgbXV0YXRlKA0KICAgIHBhdGVudHNnX3Byb25vc3RpY29fMjAyMiA9DQogICAgICBlZmVjdG9fZmlqbyArDQogICAgICBjb2Vmc1sicm5kIl0gICAgICogcm5kICsNCiAgICAgIGNvZWZzWyJlbXBsb3kiXSAgKiBlbXBsb3kgKw0KICAgICAgY29lZnNbInNhbGVzIl0gICAqIHNhbGVzICsNCiAgICAgIGNvZWZzWyJwYXRlbnRzIl0gKiBwYXRlbnRzICsNCiAgICAgIGNvZWZzWyJtZXJnZXIiXSAgKiBtZXJnZXINCiAgKSAlPiUNCiAgc2VsZWN0KGN1c2lwLCBwYXRlbnRzZ18yMDIxID0gcGF0ZW50c2csIHBhdGVudHNnX3Byb25vc3RpY29fMjAyMikNCg0KY2F0KCJFbXByZXNhcyBjb24gcHJvbsOzc3RpY28gY2FsY3VsYWRvOiIsIG5yb3cocHJvbm9zdGljb18yMDIyKSwgImRlIiwgbl9kaXN0aW5jdChkZl9tb2RlbG8kY3VzaXApLCAiXG4iKQ0Ka25pdHI6OmthYmxlKGhlYWQocHJvbm9zdGljb18yMDIyLCAxNSksIGRpZ2l0cyA9IDEpDQpgYGANCg0KYGBge3IgcmVzdW1lbi1wcm9ub3N0aWNvfQ0Kc3VtbWFyeShwcm9ub3N0aWNvXzIwMjIkcGF0ZW50c2dfcHJvbm9zdGljb18yMDIyKQ0KY2F0KCJQcm9uw7NzdGljb3MgbmVnYXRpdm9zIChzaW4gc2VudGlkbyBwYXJhIHVuIGNvbnRlbyk6Iiwgc3VtKHByb25vc3RpY29fMjAyMiRwYXRlbnRzZ19wcm9ub3N0aWNvXzIwMjIgPCAwKSwgIlxuIikNCmBgYA0KDQpgYGB7ciBwbG90LXByb25vc3RpY299DQpnZ3Bsb3QocHJvbm9zdGljb18yMDIyLCBhZXMoeCA9IHBhdGVudHNnXzIwMjEsIHkgPSBwYXRlbnRzZ19wcm9ub3N0aWNvXzIwMjIpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIGNvbG9yID0gIiM0QzcyQjAiKSArDQogIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JleTQwIikgKw0KICBsYWJzKHRpdGxlID0gIlBhdGVudGVzIGNvbmNlZGlkYXM6IDIwMjEgcmVhbCB2cy4gMjAyMiBwcm9ub3N0aWNhZG8iLA0KICAgICAgIHN1YnRpdGxlID0gIkxhIGzDrW5lYSBwdW50ZWFkYSBtYXJjYSAnc2luIGNhbWJpbycgKHByb27Ds3N0aWNvID0gdmFsb3IgZGUgMjAyMSkiLA0KICAgICAgIHggPSAiUGF0ZW50ZXMgY29uY2VkaWRhcyAyMDIxIChyZWFsKSIsIHkgPSAiUGF0ZW50ZXMgY29uY2VkaWRhcyAyMDIyIChwcm9uw7NzdGljbykiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQpDb25jbHVzaW9uZXMgZmluYWxlcyBwYXJhIGVsIGVqZXJjaWNpbyAxOg0KTGFzIHBydWViYXMgZGUgZXNwZWNpZmljYWNpw7NuIChgUEZ0ZXN0YCwgYHBodGVzdGApIGNvaW5jaWRlbiBlbiBxdWUgZWZlY3RvcyBmaWpvcyBlcyBlbCBtb2RlbG8gY29ycmVjdG8gcGFyYSBlc3RvcyBkYXRvcyB5YSBxdWUgc8OtIGhheSBjYXJhY3RlcsOtc3RpY2FzIHByb3BpYXMgZGUgY2FkYSBlbXByZXNhIHF1ZSBpbXBvcnRhbiwgeSBlc3TDoW4NCmNvcnJlbGFjaW9uYWRhcyBjb24gbGFzIHZhcmlhYmxlcyBleHBsaWNhdGl2YXMuDQpFbCBwcm9uw7NzdGljbyBwYXJhIDIwMjIgc2UgaGl6byBiYWpvIGVsIHN1cHVlc3RvIGRlICJ0b2RvIHNpZ3VlIGlndWFsIHF1ZSAyMDIxIg0KIyBQYXJ0ZSAyLiBDdWlkYWRvIGRlIGxhIFBpZWwNClNpIHR1dmllcmFzIHF1ZSBpbnZlcnRpciBlbiBhbGd1bmEgc3ViLWNhdGVnb3LDrWEsIMK/ZW4gY3XDoWwgbG8gaGFyw61hcz8NCkp1c3RpZmljYSBhbXBsaWFtZW50ZSB0dSByZXNwdWVzdGEuDQoxLiBBcnJlZ2xvcyB2aXN1YWxlcw0KYGBge3J9DQpDT0xPUl9ISVNUICAgPC0gIiMyYTc4ZDYiICAjIGF6dWwgLSBzZXJpZSAxIChoaXN0w7NyaWNvIC8gbG8gb2JzZXJ2YWRvKQ0KQ09MT1JfRkNTVCAgIDwtICIjZWI2ODM0IiAgIyBuYXJhbmphIC0gc2VyaWUgMiAocHJvbsOzc3RpY28pDQpDT0xPUl9QT1MgICAgPC0gIiMyYTc4ZDYiICAjIGF6dWwgLSB2YXJpYWNpw7NuIHBvc2l0aXZhDQpDT0xPUl9ORUcgICAgPC0gIiNlMzQ5NDgiICAjIHJvam8gLSB2YXJpYWNpw7NuIG5lZ2F0aXZhDQpDT0xPUl9ORVVUUkFMPC0gIiM4OTg3ODEiDQpgYGANCjIuIENhcmdhbW9zIGxhIEJhc2UgZGUgRGF0b3MNCmBgYHtyfQ0KZGYyIDwtIHJlYWQuY3N2KCJDOlxcVXNlcnNcXG1lXFxEZXNrdG9wXFxyZXRvXFxiZHNcXG1hcmtldF9zaXplc19yYXcuY3N2IikNCmBgYA0KMy4gTGltcGllemEgZGUgRGF0b3MNCmBgYHtyIGNhcmdhfQ0KIyBjb2xfdHlwZXMgPSAidGV4dCIgZnVlcnphIGEgcXVlIFRPREFTIGxhcyBjb2x1bW5hcyBzZSBsZWFuIGNvbW8gdGV4dG8gZGVzZGUNCiMgZWwgaW5pY2lvIChlbiB2ZXogZGUgZGVqYXIgcXVlIHJlYWR4bCBhZGl2aW5lIGVsIHRpcG8gY29sdW1uYSBwb3IgY29sdW1uYSkuDQojIEVzdG8gZXZpdGEgZWwgZXJyb3IgIkNhbid0IGNvbWJpbmUgYDIwMTFgIDxjaGFyYWN0ZXI+IGFuZCBgMjAxNmAgPGRvdWJsZT4iLA0KIyBxdWUgcGFzYSBwb3JxdWUgYWxndW5hcyBjb2x1bW5hcyBkZSBhw7FvIHRyYWVuICItIiBlbiBvdHJhcyBmaWxhcyBkZWwgYXJjaGl2bw0KIyAoZW4gUHJlc3RpZ2UvRGVybW9jb3NtZXRpY3MpIHkgZXNvIGhhY2UgcXVlIHJlYWR4bCBsYXMgYWRpdmluZSBjb21vIHRleHRvLA0KIyBtaWVudHJhcyBxdWUgY29sdW1uYXMgc2luICItIiBsYXMgYWRpdmluYSBjb21vIG7Dum1lcm8g4oCUIGNvbiB0aXBvcyBkaXN0aW50b3MsDQojIHBpdm90X2xvbmdlcigpIG5vIGxhcyBwdWVkZSBhcGlsYXIgZW4gdW5hIHNvbGEgY29sdW1uYS4NCnJhdyA8LSByZWFkX2V4Y2VsKCJDOlxcVXNlcnNcXG1lXFxEZXNrdG9wXFxyZXRvXFxiZHNcXE1hcmtldF9zaXplcy54bHN4Iiwgc2hlZXQgPSAiU3RhdGlzdGljcyBEYXRhIiwgc2tpcCA9IDUsIGNvbF90eXBlcyA9ICJ0ZXh0IikNCm5hbWVzKHJhdylbMV0gPC0gIkdlb2dyYXBoeSINCnJhdyA8LSByYXcgJT4lIGZpbHRlcighaXMubmEoR2VvZ3JhcGh5KSwgR2VvZ3JhcGh5ID09ICJNZXhpY28iKQ0KDQpjYXRlZ29yaWFzX3Byb2R1Y3RvIDwtIGMoIkJhdGggYW5kIFNob3dlciIsICJEZW9kb3JhbnRzIiwgIkRlcGlsYXRvcmllcyIsICJGcmFncmFuY2VzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIkhhaXIgQ2FyZSIsICJNZW4ncyBHcm9vbWluZyIsICJTa2luIENhcmUiLCAiU3VuIENhcmUiKQ0KDQpyYXcgPC0gcmF3ICU+JSBmaWx0ZXIoQ2F0ZWdvcnkgJWluJSBjYXRlZ29yaWFzX3Byb2R1Y3RvKQ0KDQpkYXRvcyA8LSByYXcgJT4lDQogIHNlbGVjdChDYXRlZ29yeSwgYDIwMTFgOmAyMDI1YCkgJT4lDQogIHBpdm90X2xvbmdlcigtQ2F0ZWdvcnksIG5hbWVzX3RvID0gInllYXIiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUNCiAgbXV0YXRlKHllYXIgPSBhcy5pbnRlZ2VyKHllYXIpLCB2YWx1ZSA9IHN1cHByZXNzV2FybmluZ3MoYXMubnVtZXJpYyh2YWx1ZSkpKQ0KDQprbml0cjo6a2FibGUoZGF0b3MgJT4lIGZpbHRlcih5ZWFyICVpbiUgYygyMDExLCAyMDE1LCAyMDE5LCAyMDIwLCAyMDIxLCAyMDI1KSkgJT4lDQogICAgICAgICAgICAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0geWVhciwgdmFsdWVzX2Zyb20gPSB2YWx1ZSksDQogICAgICAgICAgICAgZGlnaXRzID0gMCwgY2FwdGlvbiA9ICJWYWxvcmVzIGRlIG1lcmNhZG8gKE1YTiBtaWxsb25lcyksIGHDsW9zIHNlbGVjY2lvbmFkb3MiKQ0KYGBgDQojIDQuIEV4cGxvcmFjacOzbjogwr9jw7NtbyBzZSBoYSBtb3ZpZG8gY2FkYSBjYXRlZ29yw61hPw0KYGBge3IgZXhwbG9yYS1saW5lYXN9DQpnZ3Bsb3QoZGF0b3MsIGFlcyh4ID0geWVhciwgeSA9IHZhbHVlKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSBDT0xPUl9ISVNULCBsaW5ld2lkdGggPSAwLjkpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9IENPTE9SX0hJU1QsIHNpemUgPSAxLjYpICsNCiAgZmFjZXRfd3JhcCh+IENhdGVnb3J5LCBzY2FsZXMgPSAiZnJlZV95IiwgbmNvbCA9IDQpICsNCiAgbGFicyh0aXRsZSA9ICJUYW1hw7FvIGRlIG1lcmNhZG8gaGlzdMOzcmljbyBwb3Igc3ViY2F0ZWdvcsOtYSAoMjAxMS0yMDI1KSIsDQogICAgICAgeCA9ICJBw7FvIiwgeSA9ICJNWE4gbWlsbG9uZXMiKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTApICsNCiAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSkNCmBgYA0KIyA1LkNyZWNpbWllbnRvIGFjdW11bGFkbyAyMDExIOKGkiAyMDI1IChDQUdSKQ0KYGBge3IgY2Fncn0NCmNhZ3IgPC0gZGF0b3MgJT4lDQogIGdyb3VwX2J5KENhdGVnb3J5KSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIHZhbG9yXzIwMTEgPSB2YWx1ZVt5ZWFyID09IDIwMTFdLA0KICAgIHZhbG9yXzIwMjUgPSB2YWx1ZVt5ZWFyID09IDIwMjVdLA0KICAgIGNhZ3IgPSAodmFsb3JfMjAyNSAvIHZhbG9yXzIwMTEpXigxIC8gKDIwMjUgLSAyMDExKSkgLSAxDQogICkgJT4lDQogIGFycmFuZ2UoZGVzYyhjYWdyKSkNCg0Ka25pdHI6OmthYmxlKGNhZ3IsIGRpZ2l0cyA9IGMoMCwgMCwgMCwgMyksDQogICAgICAgICAgICAgY29sLm5hbWVzID0gYygiQ2F0ZWdvcsOtYSIsICJWYWxvciAyMDExIiwgIlZhbG9yIDIwMjUiLCAiQ0FHUiAyMDExLTIwMjUiKSkNCmBgYA0KDQpgYGB7ciBjYWdyLXBsb3R9DQpjYWdyICU+JQ0KICBtdXRhdGUoQ2F0ZWdvcnkgPSBmYWN0b3IoQ2F0ZWdvcnksIGxldmVscyA9IENhdGVnb3J5KSkgJT4lDQogIGdncGxvdChhZXMoeCA9IENhdGVnb3J5LCB5ID0gY2FncikpICsNCiAgZ2VvbV9jb2woZmlsbCA9IENPTE9SX0hJU1QsIHdpZHRoID0gMC42NSkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KGNhZ3IsIGFjY3VyYWN5ID0gMC4xKSksIGhqdXN0ID0gLTAuMTUsIHNpemUgPSAzLjMpICsNCiAgY29vcmRfZmxpcChjbGlwID0gIm9mZiIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCwgZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsIDAuMTgpKSkgKw0KICBsYWJzKHRpdGxlID0gIkNyZWNpbWllbnRvIGFudWFsIGNvbXB1ZXN0byAoQ0FHUikgMjAxMS0yMDI1IHBvciBzdWJjYXRlZ29yw61hIiwNCiAgICAgICB4ID0gTlVMTCwgeSA9ICJDQUdSIikgKw0KICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDExKSArDQogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpDQpgYGANCiMgNi4gQ29ycmVtb3MgdmFyaW9zIG1vZGVsb3MgcGFyYSBlbmNvbnRyYXIgZWwgcXVlIG1lam9yIHNlIGFjb21vZGUgYSBudWVzdHJvcyBkYXRvcw0KYGBge3Igc2VsZWNjaW9uLW1vZGVsb30NCmNvbXBhcmFyX21vZGVsb3MgPC0gZnVuY3Rpb24oZCkgew0KICBtX2xpbiA8LSBsbSh2YWx1ZSB+IHllYXIsIGRhdGEgPSBkKQ0KICBtX2V4cCA8LSBsbShsb2codmFsdWUpIH4geWVhciwgZGF0YSA9IGQpDQoNCiAgcHJlZF9saW4gPC0gcHJlZGljdChtX2xpbikNCiAgcHJlZF9leHAgPC0gZXhwKHByZWRpY3QobV9leHApKQ0KDQogIHJtc2VfbGluIDwtIHNxcnQobWVhbigoZCR2YWx1ZSAtIHByZWRfbGluKV4yKSkNCiAgcm1zZV9leHAgPC0gc3FydChtZWFuKChkJHZhbHVlIC0gcHJlZF9leHApXjIpKQ0KDQogIGRhdGEuZnJhbWUoDQogICAgcjJfbGluZWFsID0gc3VtbWFyeShtX2xpbikkci5zcXVhcmVkLA0KICAgIHIyX2V4cG9uZW5jaWFsID0gc3VtbWFyeShtX2V4cCkkci5zcXVhcmVkLA0KICAgIHJtc2VfbGluZWFsID0gcm1zZV9saW4sDQogICAgcm1zZV9leHBvbmVuY2lhbCA9IHJtc2VfZXhwLA0KICAgIG1vZGVsb19lbGVnaWRvID0gaWZlbHNlKHJtc2VfZXhwIDwgcm1zZV9saW4sICJFeHBvbmVuY2lhbCIsICJMaW5lYWwiKSwNCiAgICBjcmVjaW1pZW50b19hbnVhbF9tb2RlbG8gPSBpZmVsc2Uocm1zZV9leHAgPCBybXNlX2xpbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cChjb2VmKG1fZXhwKVsieWVhciJdKSAtIDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2VmKG1fbGluKVsieWVhciJdIC8gbWVhbihkJHZhbHVlKSkNCiAgKQ0KfQ0KDQpzZWxlY2Npb24gPC0gZGF0b3MgJT4lDQogIGdyb3VwX2J5KENhdGVnb3J5KSAlPiUNCiAgZ3JvdXBfbW9kaWZ5KH4gY29tcGFyYXJfbW9kZWxvcygueCkpICU+JQ0KICB1bmdyb3VwKCkNCg0Ka25pdHI6OmthYmxlKHNlbGVjY2lvbiwgZGlnaXRzID0gNCwgY2FwdGlvbiA9ICJDb21wYXJhY2nDs24gbGluZWFsIHZzLiBleHBvbmVuY2lhbCB5IG1vZGVsbyBlbGVnaWRvIHBvciBjYXRlZ29yw61hIikNCmBgYA0KIyA3LiBDb21wYXJhY2nDs24gZGUgbW9kZWxvcw0KDQpMYSB0YWJsYSBkZSBsYSBzZWNjacOzbiBhbnRlcmlvciB5YSBlcyBlc2EgY29tcGFyYWNpw7NuOiBlbiA3IGRlIGxhcyA4DQpjYXRlZ29yw61hcyBlbCBtb2RlbG8gZXhwb25lbmNpYWwgdGllbmUgbWVub3IgUk1TRSAobWVqb3IgYWp1c3RlKSBxdWUgZWwNCmxpbmVhbDsgbGEgZXhjZXBjacOzbiBlcyBEZXBpbGF0b3JpZXMsIGRvbmRlIGVsIGxpbmVhbCBzZSBhanVzdGEgbWVqb3IuIFBvcg0KZXNvIG5vIHNlIHZ1ZWx2ZSBhIGNvcnJlciBlbCBtaXNtbyBjw6FsY3VsbyBhcXXDrSAobGEgc2VjY2nDs24gNiBvcmlnaW5hbCB5IGVzdGENCnRlbsOtYW4gZWwgbWlzbW8gY8OzZGlnbyBwZWdhZG8gZG9zIHZlY2VzLCBhc8OtIHF1ZSBzZSBkZWrDsyBzb2xvIHVuYSB2ZXopLg0KDQojIDguIFByb27Ds3N0aWNvcw0KYGBge3IgcHJvbm9zdGljb30NCmZ1dHVybyA8LSBkYXRhLmZyYW1lKHllYXIgPSAyMDI2OjIwMjgpDQoNCnByb25vc3RpY2FyIDwtIGZ1bmN0aW9uKGQpIHsNCiAgbV9saW4gPC0gbG0odmFsdWUgfiB5ZWFyLCBkYXRhID0gZCkNCiAgbV9leHAgPC0gbG0obG9nKHZhbHVlKSB+IHllYXIsIGRhdGEgPSBkKQ0KICBybXNlX2xpbiA8LSBzcXJ0KG1lYW4oKGQkdmFsdWUgLSBwcmVkaWN0KG1fbGluKSleMikpDQogIHJtc2VfZXhwIDwtIHNxcnQobWVhbigoZCR2YWx1ZSAtIGV4cChwcmVkaWN0KG1fZXhwKSkpXjIpKQ0KDQogIGlmIChybXNlX2V4cCA8IHJtc2VfbGluKSB7DQogICAgdmFsb3IgPC0gZXhwKHByZWRpY3QobV9leHAsIG5ld2RhdGEgPSBmdXR1cm8pKQ0KICAgIG1vZGVsbyA8LSAiRXhwb25lbmNpYWwiDQogIH0gZWxzZSB7DQogICAgdmFsb3IgPC0gcHJlZGljdChtX2xpbiwgbmV3ZGF0YSA9IGZ1dHVybykNCiAgICBtb2RlbG8gPC0gIkxpbmVhbCINCiAgfQ0KICBkYXRhLmZyYW1lKHllYXIgPSBmdXR1cm8keWVhciwgdmFsdWUgPSB2YWxvciwgbW9kZWxvID0gbW9kZWxvLCB0aXBvID0gIlByb27Ds3N0aWNvIikNCn0NCg0KcHJvbiA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkoQ2F0ZWdvcnkpICU+JQ0KICBncm91cF9tb2RpZnkofiBwcm9ub3N0aWNhcigueCkpICU+JQ0KICB1bmdyb3VwKCkNCg0KZGF0b3NfaGlzdCA8LSBkYXRvcyAlPiUgbXV0YXRlKHRpcG8gPSAiSGlzdMOzcmljbyIpDQoNCnNlcmllX2NvbXBsZXRhIDwtIGJpbmRfcm93cygNCiAgZGF0b3NfaGlzdCAlPiUgc2VsZWN0KENhdGVnb3J5LCB5ZWFyLCB2YWx1ZSwgdGlwbyksDQogIHByb24gJT4lIHNlbGVjdChDYXRlZ29yeSwgeWVhciwgdmFsdWUsIHRpcG8pDQopDQoNCmtuaXRyOjprYWJsZShwcm9uICU+JSBzZWxlY3QoQ2F0ZWdvcnksIHllYXIsIHZhbHVlLCBtb2RlbG8pICU+JQ0KICAgICAgICAgICAgICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHllYXIsIHZhbHVlc19mcm9tID0gdmFsdWUpLA0KICAgICAgICAgICAgIGRpZ2l0cyA9IDAsIGNhcHRpb24gPSAiUHJvbsOzc3RpY28gZGUgdmFsb3IgZGUgbWVyY2FkbyAyMDI2LTIwMjggKE1YTiBtaWxsb25lcykiKQ0KYGBgDQpgYGB7ciBwcm9ub3N0aWNvLXBsb3R9DQpnZ3Bsb3Qoc2VyaWVfY29tcGxldGEsIGFlcyh4ID0geWVhciwgeSA9IHZhbHVlLCBjb2xvciA9IHRpcG8sIGxpbmV0eXBlID0gdGlwbykpICsNCiAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDAuOSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAxLjQpICsNCiAgZmFjZXRfd3JhcCh+IENhdGVnb3J5LCBzY2FsZXMgPSAiZnJlZV95IiwgbmNvbCA9IDQpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkhpc3TDs3JpY28iID0gQ09MT1JfSElTVCwgIlByb27Ds3N0aWNvIiA9IENPTE9SX0ZDU1QpKSArDQogIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXMgPSBjKCJIaXN0w7NyaWNvIiA9ICJzb2xpZCIsICJQcm9uw7NzdGljbyIgPSAiMjIiKSkgKw0KICBsYWJzKHRpdGxlID0gIkhpc3TDs3JpY28gKDIwMTEtMjAyNSkgeSBwcm9uw7NzdGljbyAoMjAyNi0yMDI4KSBwb3Igc3ViY2F0ZWdvcsOtYSIsDQogICAgICAgeCA9ICJBw7FvIiwgeSA9ICJNWE4gbWlsbG9uZXMiLCBjb2xvciA9IE5VTEwsIGxpbmV0eXBlID0gTlVMTCkgKw0KICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEwKSArDQogIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiksIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KYGBgDQo5LiDCv0VuIHF1w6kgc3ViY2F0ZWdvcsOtYSBpbnZlcnRpcsOtYT8NCg0KKipTa2luIENhcmUuKiogTGEganVzdGlmaWNhY2nDs24gY29tYmluYSB0cmVzIGNyaXRlcmlvcywgbm8gc29sbyBlbA0KY3JlY2ltaWVudG8gaGlzdMOzcmljbzoNCg0KMS4gKipUYW1hw7FvIHkgdHJheWVjdG9yaWE6KiogeWEgZXMgbGEgY2F0ZWdvcsOtYSBtw6FzIGdyYW5kZSAofk1YTiA3MSw4MzANCiAgIG1pbGxvbmVzIGVuIDIwMjUsIHByb3llY3RhZGEgYSB+TVhOIDg0LDIwMCBtaWxsb25lcyBlbiAyMDI4KSwgYXPDrSBxdWUNCiAgIGludmVydGlyIGFow60gbm8gZGVwZW5kZSBkZSBxdWUgdW4gbmljaG8gcGVxdWXDsW8gc2UgdnVlbHZhIGdyYW5kZSDigJQgZWwNCiAgIG1lcmNhZG8geWEgZXMgZ3JhbmRlIHkgc2lndWUgY3JlY2llbmRvIGEgdW4gcml0bW8gYWx0byAofjcuNiUvYcOxbw0KICAgY29tcHVlc3RvKS4NCjIuICoqQ2FsaWRhZCB5IGNvbmZpYWJpbGlkYWQgZGUgbGEgdGVuZGVuY2lhOioqIGVsIG1vZGVsbyBleHBvbmVuY2lhbCBhanVzdGENCiAgIGNvbiBSwrIgPSAwLjk3IHNvYnJlIDE1IGHDsW9zIGRlIGhpc3RvcmlhLCB1bm8gZGUgbG9zIG1lam9yZXMgYWp1c3RlcyBkZQ0KICAgdG9kYSBsYSBjYXRlZ29yw61hLiBFc28gZGEgbcOhcyBjb25maWFuemEgZW4gZWwgcHJvbsOzc3RpY28gcXVlLCBwb3INCiAgIGVqZW1wbG8sIGVsIGRlIEZyYWdyYW5jZXMgKFLCsiA9IDAuOTApIG8gU3VuIENhcmUgKFLCsiA9IDAuOTMpLCBjdXlhcyBzZXJpZXMNCiAgIHNvbiBtw6FzIGVycsOhdGljYXMuDQozLiAqKlJlc2lsaWVuY2lhIGFudGUgY3Jpc2lzOioqIFNraW4gQ2FyZSBmdWUgdW5hIGRlIGxhcyBkb3Mgw7puaWNhcw0KICAgY2F0ZWdvcsOtYXMgcXVlIG5vIGNhecOzIGVuIDIwMjAgKCszLjglKSB5IHR1dm8gbGEgbWVqb3IgcmVjdXBlcmFjacOzbg0KICAgcmVsYXRpdmEgaGFjaWEgMjAyMSAoKzE4LjElIHZzLiAyMDE5KS4gRXNvIGltcG9ydGEgbXVjaG8gcGFyYSB1bmEgZGVjaXNpw7NuDQogICBkZSBpbnZlcnNpw7NuIHJlYWw6IHVuIG1lcmNhZG8gcXVlIGNyZWNlIHLDoXBpZG8gcGVybyBzZSBkZXNwbG9tYSBlbiBsYQ0KICAgc2lndWllbnRlIGNyaXNpcyAoY29tbyBTdW4gQ2FyZSwgLTE2LjQlIGVuIDIwMjAgeSB0b2RhdsOtYSAtNi4yJSBwb3IgZGViYWpvDQogICBkZSAyMDE5IHVuIGHDsW8gZGVzcHXDqXMpIGVzIHVuYSBhcHVlc3RhIG3DoXMgYXJyaWVzZ2FkYS4NCg0KIyBQYXJ0ZSAzLiBCYW5jbyBtdW5kaWFsDQpgYGB7ciBpbXBvcnRhcn0NCnVybF9nZHAgICA8LSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2RhdGFzZXRzL2dkcC9tYXN0ZXIvZGF0YS9nZHAuY3N2Ig0KdXJsX2luZmwgIDwtICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZGF0YXNldHMvaW5mbGF0aW9uL21hc3Rlci9kYXRhL2luZmxhdGlvbi1jb25zdW1lci5jc3YiDQp1cmxfcG9wICAgPC0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9kYXRhc2V0cy9wb3B1bGF0aW9uL21hc3Rlci9kYXRhL3BvcHVsYXRpb24uY3N2Ig0KDQpnZHAgIDwtIHJlYWQuY3N2KHVybF9nZHApDQppbmZsIDwtIHJlYWQuY3N2KHVybF9pbmZsKQ0KcG9wICA8LSByZWFkLmNzdih1cmxfcG9wKQ0KDQpwYWlzZXMgPC0gYygiQ0hOIiwgIkpQTiIsICJLT1IiKQ0Kbm9tYnJlc19wYWlzZXMgPC0gYyhDSE4gPSAiQ2hpbmEiLCBKUE4gPSAiSmFww7NuIiwgS09SID0gIkNvcmVhIGRlbCBTdXIiKQ0KDQpnZHAgIDwtIGdkcCAgJT4lIGZpbHRlcihDb3VudHJ5LkNvZGUgJWluJSBwYWlzZXMpICU+JSBzZWxlY3QoQ291bnRyeS5Db2RlLCBZZWFyLCBnZHBfdXNkID0gVmFsdWUpDQpwb3AgIDwtIHBvcCAgJT4lIGZpbHRlcihDb3VudHJ5LkNvZGUgJWluJSBwYWlzZXMpICU+JSBzZWxlY3QoQ291bnRyeS5Db2RlLCBZZWFyLCBwb2JsYWNpb24gPSBWYWx1ZSkNCmluZmwgPC0gaW5mbCAlPiUgZmlsdGVyKENvdW50cnkuQ29kZSAlaW4lIHBhaXNlcykgJT4lIHNlbGVjdChDb3VudHJ5LkNvZGUsIFllYXIsIGluZmxhY2lvbl9wY3QgPSBJbmZsYXRpb24pDQoNCmRhdG9zX2JtIDwtIGdkcCAlPiUNCiAgaW5uZXJfam9pbihwb3AsIGJ5ID0gYygiQ291bnRyeS5Db2RlIiwgIlllYXIiKSkgJT4lDQogIGlubmVyX2pvaW4oaW5mbCwgYnkgPSBjKCJDb3VudHJ5LkNvZGUiLCAiWWVhciIpKSAlPiUNCiAgZmlsdGVyKFllYXIgPj0gMTk5MCwgWWVhciA8PSAyMDIzKSAlPiUNCiAgbXV0YXRlKA0KICAgIHBhaXMgPSBub21icmVzX3BhaXNlc1tDb3VudHJ5LkNvZGVdLA0KICAgIGdkcF9wZXJjYXBpdGFfdXNkID0gZ2RwX3VzZCAvIHBvYmxhY2lvbg0KICApICU+JQ0KICByZW5hbWUoYW5pbyA9IFllYXIpICU+JQ0KICBzZWxlY3QocGFpcywgQ291bnRyeS5Db2RlLCBhbmlvLCBnZHBfdXNkLCBwb2JsYWNpb24sIGdkcF9wZXJjYXBpdGFfdXNkLCBpbmZsYWNpb25fcGN0KSAlPiUNCiAgYXJyYW5nZShwYWlzLCBhbmlvKQ0KDQpjYXQoIkZpbGFzOiIsIG5yb3coZGF0b3NfYm0pLCAiIHwgUGHDrXNlczoiLCBuX2Rpc3RpbmN0KGRhdG9zX2JtJHBhaXMpLCAiIHwgQcOxb3M6IiwgbWluKGRhdG9zX2JtJGFuaW8pLCAiLSIsIG1heChkYXRvc19ibSRhbmlvKSwgIlxuIikNCmtuaXRyOjprYWJsZShkYXRvc19ibSAlPiUgZmlsdGVyKGFuaW8gJWluJSBjKDE5OTAsIDIwMDAsIDIwMTAsIDIwMjAsIDIwMjMpKSwgZGlnaXRzID0gMSwNCiAgICAgICAgICAgICBjYXB0aW9uID0gIk11ZXN0cmEgZGUgbG9zIGRhdG9zIGltcG9ydGFkb3MiKQ0KYGBgDQoNClBhbmVsIGJhbGFuY2VhZG86IDMgcGHDrXNlcyDDlyAzNCBhw7FvcyA9IDEwMiBvYnNlcnZhY2lvbmVzLCBzaW4gZGF0b3MNCmZhbHRhbnRlcy4NCg0KIyBFeHBsb3JhY2nDs24NCg0KYGBge3IgZXhwbG9yYS1waWJwY30NCmdncGxvdChkYXRvc19ibSwgYWVzKHggPSBhbmlvLCB5ID0gZ2RwX3BlcmNhcGl0YV91c2QsIGNvbG9yID0gcGFpcykpICsNCiAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDEpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkNoaW5hIiA9ICIjMmE3OGQ2IiwgIkphcMOzbiIgPSAiI2ViNjgzNCIsICJDb3JlYSBkZWwgU3VyIiA9ICIjMWJhZjdhIikpICsNCiAgbGFicyh0aXRsZSA9ICJQSUIgcGVyIGPDoXBpdGEgKFVTJCBjb3JyaWVudGVzKSwgMTk5MC0yMDIzIiwNCiAgICAgICB4ID0gIkHDsW8iLCB5ID0gIlBJQiBwZXIgY8OhcGl0YSAoVVMkKSIsIGNvbG9yID0gTlVMTCkgKw0KICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDExKQ0KYGBgDQoNCkxhcyBkaWZlcmVuY2lhcyBkZSBuaXZlbCBzb24gZW5vcm1lczogSmFww7NuIHkgQ29yZWEgZGVsIFN1ciB5YSBlcmFuDQplY29ub23DrWFzIGRlIGluZ3Jlc28gYWx0byBlbiAxOTkwLCBtaWVudHJhcyBxdWUgQ2hpbmEgcGFydGnDsyBkZSB1biBQSUIgcGVyDQpjw6FwaXRhIG11eSBiYWpvIHkgaGEgaWRvIGNlcnJhbmRvIGxhIGJyZWNoYSBjb24gdW4gY3JlY2ltaWVudG8gc29zdGVuaWRvLg0KRXN0YSBjbGFzZSBkZSBkaWZlcmVuY2lhcyBlc3RydWN0dXJhbGVzIGVudHJlIHBhw61zZXMgZXMgZXhhY3RhbWVudGUgbG8gcXVlDQp1biBtb2RlbG8gKnBvb2xlZCogKHF1ZSBpZ25vcmEgbGEgaWRlbnRpZGFkIGRlbCBwYcOtcykgbm8gcHVlZGUgY2FwdHVyYXINCmJpZW4uDQoNCmBgYHtyIGV4cGxvcmEtaW5mbGFjaW9ufQ0KZ2dwbG90KGRhdG9zX2JtLCBhZXMoeCA9IGFuaW8sIHkgPSBpbmZsYWNpb25fcGN0LCBjb2xvciA9IHBhaXMpKSArDQogIGdlb21fbGluZShsaW5ld2lkdGggPSAxKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGNvbG9yID0gImdyZXk1MCIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkNoaW5hIiA9ICIjMmE3OGQ2IiwgIkphcMOzbiIgPSAiI2ViNjgzNCIsICJDb3JlYSBkZWwgU3VyIiA9ICIjMWJhZjdhIikpICsNCiAgbGFicyh0aXRsZSA9ICJJbmZsYWNpw7NuIGFudWFsICglKSwgMTk5MC0yMDIzIiwNCiAgICAgICB4ID0gIkHDsW8iLCB5ID0gIkluZmxhY2nDs24gKCUpIiwgY29sb3IgPSBOVUxMKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTEpDQpgYGANCg0KSmFww7NuIGhhIHRlbmlkbyBpbmZsYWNpw7NuIGNlcmNhbmEgYSBjZXJvIChvIG5lZ2F0aXZhLCBkZWZsYWNpw7NuKSBidWVuYSBwYXJ0ZQ0KZGVsIHBlcmlvZG87IENoaW5hIHkgQ29yZWEgdHV2aWVyb24gcGljb3MgZGUgaW5mbGFjacOzbiBtw6FzIGFsdG9zIGVuIGxvcw0KYcOxb3MgOTAgeSBzZSBoYW4gbW9kZXJhZG8gZGVzZGUgZW50b25jZXMuDQoNCiMgUGFzbyAxOiBlc3RydWN0dXJhIGRlIHBhbmVsIHkgbW9kZWxvIGRlIHJlZmVyZW5jaWENCg0KYGBge3IgcGRhdGEtYm19DQpwZGZfYm0gPC0gcGRhdGEuZnJhbWUoZGF0b3NfYm0sIGluZGV4ID0gYygicGFpcyIsICJhbmlvIikpDQoNCm1vZGVsb19yZWZlcmVuY2lhX2JtIDwtIGxtKGdkcF9wZXJjYXBpdGFfdXNkIH4gaW5mbGFjaW9uX3BjdCwgZGF0YSA9IHBkZl9ibSkNCnN1bW1hcnkobW9kZWxvX3JlZmVyZW5jaWFfYm0pDQpgYGANCg0KIyBQYXNvIDI6IGxvcyB0cmVzIG1vZGVsb3MgKHBvb2xpbmcsIHdpdGhpbiwgcmFuZG9tKQ0KDQpgYGB7ciB0cmVzLW1vZGVsb3MtYm19DQpmb3JtX2JtIDwtIGdkcF9wZXJjYXBpdGFfdXNkIH4gaW5mbGFjaW9uX3BjdA0KDQpwb29saW5nX2JtIDwtIHBsbShmb3JtX2JtLCBkYXRhID0gcGRmX2JtLCBtb2RlbCA9ICJwb29saW5nIikNCndpdGhpbl9ibSAgPC0gcGxtKGZvcm1fYm0sIGRhdGEgPSBwZGZfYm0sIG1vZGVsID0gIndpdGhpbiIpDQpyYW5kb21fYm0gIDwtIHBsbShmb3JtX2JtLCBkYXRhID0gcGRmX2JtLCBtb2RlbCA9ICJyYW5kb20iKQ0KYGBgDQoNCiMjIFBvb2xpbmcNCg0KYGBge3IgcmVzdW1lbi1wb29saW5nLWJtfQ0Kc3VtbWFyeShwb29saW5nX2JtKQ0KYGBgDQoNCiMjIFdpdGhpbiAvIEVmZWN0b3MgZmlqb3MNCg0KYGBge3IgcmVzdW1lbi13aXRoaW4tYm19DQpzdW1tYXJ5KHdpdGhpbl9ibSkNCmBgYA0KDQojIyBSYW5kb20gLyBFZmVjdG9zIGFsZWF0b3Jpb3MNCg0KYGBge3IgcmVzdW1lbi1yYW5kb20tYm19DQpzdW1tYXJ5KHJhbmRvbV9ibSkNCmBgYA0KDQpFbiBsb3MgdHJlcyBtb2RlbG9zIGVsIGNvZWZpY2llbnRlIGRlIGluZmxhY2nDs24gZXMgKipuZWdhdGl2byB5DQpzaWduaWZpY2F0aXZvKio6IG3DoXMgaW5mbGFjacOzbiBzZSBhc29jaWEgY29uIG1lbm9yIFBJQiBwZXIgY8OhcGl0YSAobyBjb24NCnBhw61zZXMvYcOxb3MgZGUgbWVub3IgUElCIHBlciBjw6FwaXRhKSwgY29uc2lzdGVudGUgY29uIGxhIGlkZWEgZWNvbsOzbWljYSBkZQ0KcXVlIGxhIGluZXN0YWJpbGlkYWQgZGUgcHJlY2lvcyB2YSBkZSBsYSBtYW5vIGRlIG1lbm9yIGRlc2Fycm9sbG8gcmVsYXRpdm8NCmVuIGVzdGEgbXVlc3RyYS4NCg0KIyBQYXNvIDM6IMK/Y3XDoWwgbW9kZWxvIGVzIGVsIGNvcnJlY3RvPw0KDQojIyBQb29saW5nIHZzLiBlZmVjdG9zIGZpam9zDQoNCmBgYHtyIHBmdGVzdH0NCnBGdGVzdCh3aXRoaW5fYm0sIHBvb2xpbmdfYm0pDQpgYGANCg0KRWwgcC12YWx1ZSBlcyBwcsOhY3RpY2FtZW50ZSAwOiAqKnNlIHJlY2hhemEgcG9vbGluZyoqLiBIYXkgZWZlY3RvcyBwcm9waW9zDQpkZSBjYWRhIHBhw61zIChzdSBuaXZlbCBkZSBkZXNhcnJvbGxvLCBoaXN0b3JpYSBlY29uw7NtaWNhLCBldGMuKSBxdWUgcG9vbGluZw0KZXN0w6EgaWdub3JhbmRvLiBFZmVjdG9zIGZpam9zIGxlIGdhbmEgYSBwb29saW5nLg0KDQojIyBFZmVjdG9zIGZpam9zIHZzLiBlZmVjdG9zIGFsZWF0b3Jpb3MgKHBydWViYSBkZSBIYXVzbWFuKQ0KDQpgYGB7ciBwaHRlc3R9DQpwaHRlc3QocmFuZG9tX2JtLCB3aXRoaW5fYm0pDQpgYGANCg0KQXF1w60gZWwgcmVzdWx0YWRvIGVzIGRpc3RpbnRvIGFsIGRlbCBlamVyY2ljaW8gZGUgcGF0ZW50ZXM6IGVsIHAtdmFsdWUgZXMNCioqMC4xOCwgbm8gc2lnbmlmaWNhdGl2byoqLiBFc28gcXVpZXJlIGRlY2lyIHF1ZSAqKm5vIHNlIHJlY2hhemEgcXVlIHJhbmRvbQ0KZWZmZWN0cyBzZWEgY29uc2lzdGVudGUqKiwgYXPDrSBxdWUg4oCUYSBkaWZlcmVuY2lhIGRlbCBlamVyY2ljaW8gYW50ZXJpb3LigJQNCmFxdcOtICoqc8OtIGNvbnZpZW5lIHVzYXIgcmFuZG9tIGVmZmVjdHMqKiwgcG9ycXVlIGVzIG3DoXMgZWZpY2llbnRlIHF1ZQ0KZWZlY3RvcyBmaWpvcyAodXNhIHRhbnRvIGxhIHZhcmlhY2nDs24gZW50cmUgcGHDrXNlcyBjb21vIGVuIGVsIHRpZW1wbykgeSBsYQ0KcHJ1ZWJhIGRlIEhhdXNtYW4gbm8gZW5jb250csOzIGV2aWRlbmNpYSBkZSBxdWUgZXN0w6kgc2VzZ2Fkby4NCg0KKipDb25jbHVzacOzbiBkZWwgUGFzbyAzOioqIGVsIG1vZGVsbyBlbGVnaWRvIGVzICoqZWZlY3RvcyBhbGVhdG9yaW9zDQoocmFuZG9tKSoqLg0KDQojIFBhc28gNDogcHJlZGljY2lvbmVzIDIwMjQtMjAyNg0KDQpSYW5kb20gZWZmZWN0cyB0aWVuZSB1biBzb2xvIGludGVyY2VwdG8geSB1bmEgc29sYSBwZW5kaWVudGUgcGFyYSBsb3MgdHJlcw0KcGHDrXNlcyAoYSBkaWZlcmVuY2lhIGRlIGVmZWN0b3MgZmlqb3MsIHF1ZSBsZSBkYSB1biBpbnRlcmNlcHRvIHByb3BpbyBhIGNhZGENCnVubyksIGFzw60gcXVlIHByb25vc3RpY2FyIGVzIHRhbiBzaW1wbGUgY29tbyBlbiB0dSBlamVtcGxvIG9yaWdpbmFsOg0KYGludGVyY2VwdG8gKyBwZW5kaWVudGUgKiBpbmZsYWNpw7NuYC4gRWwgw7puaWNvIHN1cHVlc3RvIHF1ZSBuZWNlc2l0YW1vcyBlcw0KdW4gdmFsb3IgZGUgaW5mbGFjacOzbiBmdXR1cmEgcG9yIHBhw61zOyB1c2Ftb3MgZWwgw7psdGltbyBkYXRvIG9ic2VydmFkbw0KKDIwMjMpIGNvbW8gZXNjZW5hcmlvICJzaSBsYSBpbmZsYWNpw7NuIHNlIG1hbnRpZW5lIGlndWFsIi4NCg0KYGBge3IgcHJvbm9zdGljby1ibX0NCmludGVyY2VwdG9fYm0gPC0gY29lZihyYW5kb21fYm0pWyIoSW50ZXJjZXB0KSJdDQpwZW5kaWVudGVfYm0gIDwtIGNvZWYocmFuZG9tX2JtKVsiaW5mbGFjaW9uX3BjdCJdDQoNCmluZmxhY2lvbl8yMDIzIDwtIGRhdG9zX2JtICU+JSBmaWx0ZXIoYW5pbyA9PSAyMDIzKSAlPiUgc2VsZWN0KHBhaXMsIGluZmxhY2lvbl9wY3QpDQoNCnByb25vc3RpY29fYm0gPC0gZXhwYW5kLmdyaWQocGFpcyA9IHVuaXF1ZShkYXRvc19ibSRwYWlzKSwgYW5pbyA9IDIwMjQ6MjAyNikgJT4lDQogIGxlZnRfam9pbihpbmZsYWNpb25fMjAyMywgYnkgPSAicGFpcyIpICU+JQ0KICBtdXRhdGUoZ2RwX3BlcmNhcGl0YV9wcm9ub3N0aWNvID0gaW50ZXJjZXB0b19ibSArIHBlbmRpZW50ZV9ibSAqIGluZmxhY2lvbl9wY3QpDQoNCmtuaXRyOjprYWJsZShwcm9ub3N0aWNvX2JtICU+JSBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gYW5pbywgdmFsdWVzX2Zyb20gPSBnZHBfcGVyY2FwaXRhX3Byb25vc3RpY28sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZF9jb2xzID0gcGFpcyksDQogICAgICAgICAgICAgZGlnaXRzID0gMCwgY2FwdGlvbiA9ICJQSUIgcGVyIGPDoXBpdGEgcHJvbm9zdGljYWRvIDIwMjQtMjAyNiAoVVMkLCBzdXBvbmUgaW5mbGFjacOzbiA9IGxhIGRlIDIwMjMpIikNCmBgYA0KDQpgYGB7ciBwcm9ub3N0aWNvLWJtLXBsb3R9DQpzZXJpZV9jb21wbGV0YV9ibSA8LSBiaW5kX3Jvd3MoDQogIGRhdG9zX2JtICU+JSBzZWxlY3QocGFpcywgYW5pbywgdmFsb3IgPSBnZHBfcGVyY2FwaXRhX3VzZCkgJT4lIG11dGF0ZSh0aXBvID0gIkhpc3TDs3JpY28iKSwNCiAgcHJvbm9zdGljb19ibSAlPiUgc2VsZWN0KHBhaXMsIGFuaW8sIHZhbG9yID0gZ2RwX3BlcmNhcGl0YV9wcm9ub3N0aWNvKSAlPiUgbXV0YXRlKHRpcG8gPSAiUHJvbsOzc3RpY28iKQ0KKQ0KDQpnZ3Bsb3Qoc2VyaWVfY29tcGxldGFfYm0sIGFlcyh4ID0gYW5pbywgeSA9IHZhbG9yLCBjb2xvciA9IHBhaXMsIGxpbmV0eXBlID0gdGlwbykpICsNCiAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDEpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkNoaW5hIiA9ICIjMmE3OGQ2IiwgIkphcMOzbiIgPSAiI2ViNjgzNCIsICJDb3JlYSBkZWwgU3VyIiA9ICIjMWJhZjdhIikpICsNCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMoIkhpc3TDs3JpY28iID0gInNvbGlkIiwgIlByb27Ds3N0aWNvIiA9ICIyMiIpKSArDQogIGxhYnModGl0bGUgPSAiUElCIHBlciBjw6FwaXRhOiBoaXN0w7NyaWNvICgxOTkwLTIwMjMpIHkgcHJvbsOzc3RpY28gKDIwMjQtMjAyNikiLA0KICAgICAgIHggPSAiQcOxbyIsIHkgPSAiUElCIHBlciBjw6FwaXRhIChVUyQpIiwgY29sb3IgPSBOVUxMLCBsaW5ldHlwZSA9IE5VTEwpICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMSkNCmBgYA0KDQoqKlBvciBxdcOpIGVsIHByb27Ds3N0aWNvIHNhbGUgY2FzaSBwbGFubzoqKiBjb21vIGVsIG1vZGVsbyBkZSByYW5kb20gZWZmZWN0cw0Kc29sbyB1c2EgbGEgaW5mbGFjacOzbiBwYXJhIGV4cGxpY2FyIGVsIFBJQiBwZXIgY8OhcGl0YSAoeSBsZSBwZWdhIGVsIG1pc21vDQppbnRlcmNlcHRvL3BlbmRpZW50ZSBhIGxvcyB0cmVzIHBhw61zZXMpLCBlbCBwcm9uw7NzdGljbyAyMDI0LTIwMjYgbm8NCm11ZXZlIG11Y2hvIGVsIG5pdmVsIGRlIGNhZGEgcGHDrXMgZnJlbnRlIGEgc3UgdmFsb3IgZGUgMjAyMyDigJQgZWwgbW9kZWxvIG5vDQp0aWVuZSBmb3JtYSBkZSAic2FiZXIiIHF1ZSBDaGluYSBoYSB2ZW5pZG8gY3JlY2llbmRvIGRlIGZvcm1hIHNvc3RlbmlkYSBzaQ0KZXNhIHRlbmRlbmNpYSBubyBwYXNhIHBvciBsYSBpbmZsYWNpw7NuLiBFc3RvIGVzIHVuYSBsaW1pdGFjacOzbiByZWFsIHkgdmFsZQ0KbGEgcGVuYSBkZWNpcmxhIGVuIGxhcyBjb25jbHVzaW9uZXMsIG5vIGVzY29uZGVybGEuDQoNCiMgQ29uY2x1c2lvbmVzDQoNCi0gRWwgUElCIHBlciBjw6FwaXRhIGRlIENoaW5hLCBKYXDDs24geSBDb3JlYSBkZWwgU3VyIHRpZW5lIGRpZmVyZW5jaWFzIGRlDQogIG5pdmVsIHRhbiBncmFuZGVzIHF1ZSB1biBtb2RlbG8gKnBvb2xlZCogKHF1ZSBpZ25vcmEgbGEgaWRlbnRpZGFkIGRlbA0KICBwYcOtcykgcXVlZGEgY2xhcmFtZW50ZSByZWNoYXphZG8gcG9yIGxhIHBydWViYSBGIChgcEZ0ZXN0YCwgcCA8IDAuMDAxKS4NCi0gQSBkaWZlcmVuY2lhIGRlbCBlamVyY2ljaW8gZGUgcGF0ZW50ZXMsIGFxdcOtIGxhIHBydWViYSBkZSBIYXVzbWFuDQogIChgcGh0ZXN0YCwgcCA9IDAuMTgpICoqbm8qKiByZWNoYXphIGVmZWN0b3MgYWxlYXRvcmlvcywgYXPDrSBxdWUgZWwgbW9kZWxvDQogIGVsZWdpZG8gZXMgKipyYW5kb20gZWZmZWN0cyoqIOKAlCBtw6FzIGVmaWNpZW50ZSBxdWUgZWZlY3RvcyBmaWpvcyBwb3JxdWUNCiAgYXByb3ZlY2hhIHRhbWJpw6luIGxhIHZhcmlhY2nDs24gKmVudHJlKiBwYcOtc2VzLCBubyBzb2xvICpkZW50cm8qIGRlIGNhZGENCiAgcGHDrXMgZW4gZWwgdGllbXBvLg0KLSBMYSByZWxhY2nDs24gZXN0aW1hZGEgZW50cmUgaW5mbGFjacOzbiB5IFBJQiBwZXIgY8OhcGl0YSBlcyAqKm5lZ2F0aXZhIHkNCiAgc2lnbmlmaWNhdGl2YSoqIGVuIGxvcyB0cmVzIG1vZGVsb3M6IGNhZGEgcHVudG8gcG9yY2VudHVhbCBhZGljaW9uYWwgZGUNCiAgaW5mbGFjacOzbiBzZSBhc29jaWEgY29uLCBhcHJveGltYWRhbWVudGUsIFVTJDk0NiBtZW5vcyBkZSBQSUIgcGVyIGPDoXBpdGENCiAgKHNlZ8O6biBlbCBtb2RlbG8gZGUgcmFuZG9tIGVmZmVjdHMpLg0KLSBFbCBhanVzdGUgZGVsIG1vZGVsbyBlcyBtb2Rlc3RvIChSwrIg4omIIDAuMTkpOiBsYSBpbmZsYWNpw7NuIHBvciBzw60gc29sYQ0KICBleHBsaWNhIHVuYSBwYXJ0ZSByZWxhdGl2YW1lbnRlIHBlcXVlw7FhIGRlIGxhcyBkaWZlcmVuY2lhcyBkZSBQSUIgcGVyDQogIGPDoXBpdGEgZW50cmUgZXN0b3MgdHJlcyBwYcOtc2VzIOKAlCB0aWVuZSBzZW50aWRvLCBwb3JxdWUgZWwgZGVzYXJyb2xsbw0KICBlY29uw7NtaWNvIGRlcGVuZGUgZGUgbXVjaG9zIGZhY3RvcmVzIG3DoXMgKGNhcGl0YWwgaHVtYW5vLCBpbnN0aXR1Y2lvbmVzLA0KICBjb21lcmNpbywgdGVjbm9sb2fDrWEpIHF1ZSBubyBlc3TDoW4gZW4gZXN0ZSBtb2RlbG8gZGUgdW5hIHNvbGEgdmFyaWFibGUuDQogIEVsIHByb27Ds3N0aWNvIDIwMjQtMjAyNiBoZXJlZGEgZXNhIG1pc21hIGxpbWl0YWNpw7NuOiBjb21vIGVsICJzaG9jayIgZGUNCiAgaW5mbGFjacOzbiBubyBjYW1iaWEgbXVjaG8gZGUgdW4gYcOxbyBhIG90cm8sIGVsIHByb27Ds3N0aWNvIHRhbXBvY28gc2UNCiAgbXVldmUgbXVjaG8g4oCUIGVzdG8gbm8gZXMgdW4gZXJyb3IgZGVsIGPDs2RpZ28sIGVzIHVuYSBjb25zZWN1ZW5jaWENCiAgZXNwZXJhYmxlIGRlIGhhYmVyIHVzYWRvIHNvbG8gdW5hIHZhcmlhYmxlIGV4cGxpY2F0aXZhLg0KLSBOb3RhIGRlIGFzb2NpYWNpw7NuIHZzLiBjYXVzYWxpZGFkOiBlc3RhIGNvcnJlbGFjacOzbiBuZWdhdGl2YSBlbnRyZQ0KICBpbmZsYWNpw7NuIHkgUElCIHBlciBjw6FwaXRhIG5vIGRlYmUgbGVlcnNlIGNvbW8gcXVlICJiYWphciBsYSBpbmZsYWNpw7NuDQogIGdhcmFudGl6YSIgbWF5b3IgUElCIHBlciBjw6FwaXRhIOKAlCBjb24gMyBwYcOtc2VzIHkgdW5hIHNvbGEgdmFyaWFibGUsIGVzDQogIGV2aWRlbmNpYSBzdWdlc3RpdmEsIG5vIHVuYSBwcnVlYmEgY2F1c2FsLg0K