Importar paquetes y llamar librerias

options(repos = c(CRAN = "https://cloud.r-project.org"))

#install.packages("readxl")
library(readxl)
#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

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 (Y). 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
# 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"))

Variable dependiente (Y): patentsg, las patentes otorgadas/concedidas a la empresa ese año (no las solicitadas). 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
phtest(random, within)
## 
##  Hausman Test
## 
## data:  form
## chisq = 110.59, df = 5, p-value < 2.2e-16
## alternative hypothesis: one model is inconsistent
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

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 que están correlacionadas con las variables explicativas, así que un modelo pooling (que ignora esa identidad de empresa) queda descartado y un modelo de efectos aleatorios tampoco es consistente frente al de efectos fijos. El pronóstico de patentes otorgadas para 2022 se hizo bajo el supuesto de que cada empresa mantiene en 2022 los mismos valores de I+D, empleo, ventas, patentes solicitadas y fusión que tuvo en 2021, usando el efecto fijo propio de cada empresa más los coeficientes del modelo de efectos fijos. Como patentsg es un conteo y este es un modelo lineal, algunos pronósticos salen negativos, lo cual no tiene sentido para un número de patentes pero es una limitación esperada de usar un modelo lineal sobre una variable de conteo, no un error del código.

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. Cargamos la Base de Datos
df2 <- read.csv("C:\\Users\\me\\Desktop\\reto\\bds\\market_sizes_raw.csv")
  1. Limpieza de Datos
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
  1. Convertimos la bdd a datos de panel

Aquí cada subcategoría (Category) hace el papel de “empresa” y cada año el papel de periodo de tiempo, igual que en el ejemplo de clase (empresa, anio). Guardamos una copia numérica del año (anio_num) porque pdata.frame convierte la columna que se usa como índice de tiempo, y para usar el año como variable explicativa continua (y poder pronosticar años futuros como 2026) necesitamos esa copia numérica aparte.

datos$anio_num <- datos$year

pdf_piel <- pdata.frame(datos, index = c("Category", "year"))
  1. Modelo de referencia

Variable dependiente (Y): value (tamaño de mercado, en MXN millones). Variable explicativa (X): anio_num (el año), igual que en el ejemplo de clase donde ventas se explicaba con publicidad.

modelo_referencia_piel <- lm(value ~ anio_num, data = pdf_piel)
summary(modelo_referencia_piel)
## 
## Call:
## lm(formula = value ~ anio_num, data = pdf_piel)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -30062 -10733   -673  13521  39895 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -2937049.2   670768.7  -4.379 2.60e-05 ***
## anio_num        1466.2      332.4   4.411 2.29e-05 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 15730 on 118 degrees of freedom
## Multiple R-squared:  0.1415, Adjusted R-squared:  0.1343 
## F-statistic: 19.46 on 1 and 118 DF,  p-value: 2.287e-05
  1. Modelos Pooling, Within y Random
form_piel <- value ~ anio_num

pooling_piel <- plm(form_piel, data = pdf_piel, model = "pooling")
within_piel  <- plm(form_piel, data = pdf_piel, model = "within")
random_piel  <- plm(form_piel, data = pdf_piel, model = "random")

Resumen de Pooling

summary(pooling_piel)
## Pooling Model
## 
## Call:
## plm(formula = form_piel, data = pdf_piel, model = "pooling")
## 
## Balanced Panel: n = 8, T = 15, N = 120
## 
## Residuals:
##      Min.   1st Qu.    Median   3rd Qu.      Max. 
## -30062.20 -10733.24   -672.88  13521.05  39895.00 
## 
## Coefficients:
##                Estimate  Std. Error t-value  Pr(>|t|)    
## (Intercept) -2937049.22   670768.71 -4.3786 2.598e-05 ***
## anio_num        1466.17      332.39  4.4110 2.287e-05 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Total Sum of Squares:    3.4018e+10
## Residual Sum of Squares: 2.9203e+10
## R-Squared:      0.14155
## Adj. R-Squared: 0.13427
## F-statistic: 19.4565 on 1 and 118 DF, p-value: 2.2866e-05

Resumen de Within / Efectos Fijos

summary(within_piel)
## Oneway (individual) effect Within Model
## 
## Call:
## plm(formula = form_piel, data = pdf_piel, model = "within")
## 
## Balanced Panel: n = 8, T = 15, N = 120
## 
## Residuals:
##    Min. 1st Qu.  Median 3rd Qu.    Max. 
## -9759.9 -3014.6 -1523.3  2555.2 19311.3 
## 
## Coefficients:
##          Estimate Std. Error t-value  Pr(>|t|)    
## anio_num  1466.17     116.12  12.626 < 2.2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Total Sum of Squares:    8.168e+09
## Residual Sum of Squares: 3352800000
## R-Squared:      0.58952
## Adj. R-Squared: 0.55993
## F-statistic: 159.413 on 1 and 111 DF, p-value: < 2.22e-16

Resumen de Random / Efectos Aleatorios

summary(random_piel)
## Oneway (individual) effect Random Effect Model 
##    (Swamy-Arora's transformation)
## 
## Call:
## plm(formula = form_piel, data = pdf_piel, model = "random")
## 
## Balanced Panel: n = 8, T = 15, N = 120
## 
## Effects:
##                     var   std.dev share
## idiosyncratic  30205839      5496  0.11
## individual    244180653     15626  0.89
## theta: 0.9096
## 
## Residuals:
##      Min.   1st Qu.    Median   3rd Qu.      Max. 
## -11596.07  -3143.95   -709.43   1841.40  21172.87 
## 
## Coefficients:
##                Estimate  Std. Error z-value  Pr(>|z|)    
## (Intercept) -2937049.22   234403.59 -12.530 < 2.2e-16 ***
## anio_num        1466.17      116.12  12.626 < 2.2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Total Sum of Squares:    8379500000
## Residual Sum of Squares: 3564300000
## R-Squared:      0.57464
## Adj. R-Squared: 0.57104
## Chisq: 159.413 on 1 DF, p-value: < 2.22e-16
  1. Pruebas de especificación
pFtest(within_piel, pooling_piel)
## 
##  F test for individual effects
## 
## data:  form_piel
## F = 122.26, df1 = 7, df2 = 111, p-value < 2.2e-16
## alternative hypothesis: significant effects
phtest(random_piel, within_piel)
## 
##  Hausman Test
## 
## data:  form_piel
## chisq = 4.9585e-12, df = 1, p-value = 1
## alternative hypothesis: one model is inconsistent

La prueba F rechaza que pooling sea válido: sí hay diferencias propias de cada subcategoría que pooling ignora. La prueba de Hausman, en cambio, no rechaza que random effects sea consistente (el estadístico sale prácticamente en cero), así que aquí el modelo elegido es random effects — a diferencia de patentes, donde ganaba efectos fijos.

  1. Pronóstico 2026-2028

random effects solo nos da un intercepto y una pendiente comunes para todas las subcategorías (igual que en el ejemplo de clase, donde el pronóstico de ventas usaba un único intercepto y una única pendiente para las 4 empresas). Aquí eso no sirve para diferenciar entre subcategorías, porque el año 2026 es el mismo para todas. Como la prueba de Hausman mostró que efectos fijos y random effects prácticamente coinciden en este caso (la pendiente del año es idéntica en ambos), usamos fixef() — la misma herramienta ya usada en el ejercicio de patentes — para obtener el nivel propio de cada subcategoría y así sí poder pronosticar un valor distinto para cada una.

efectos_fijos_piel <- fixef(within_piel)
efectos_fijos_piel
## Bath and Shower      Deodorants    Depilatories      Fragrances       Hair Care 
##        -2945068        -2943789        -2957351        -2928228        -2921814 
##  Men's Grooming       Skin Care        Sun Care 
##        -2927314        -2916465        -2956364
pendiente_piel <- coef(within_piel)["anio_num"]

futuro_piel <- expand.grid(Category = names(efectos_fijos_piel), anio_num = 2026:2028)
futuro_piel$efecto_fijo <- efectos_fijos_piel[as.character(futuro_piel$Category)]
futuro_piel$pronostico <- futuro_piel$efecto_fijo + pendiente_piel * futuro_piel$anio_num

knitr::kable(futuro_piel %>% select(Category, anio_num, pronostico) %>%
               pivot_wider(names_from = anio_num, values_from = pronostico),
             digits = 0, caption = "Pronóstico de valor de mercado 2026-2028 (MXN millones)")
Pronóstico de valor de mercado 2026-2028 (MXN millones)
Category 2026 2027 2028
Bath and Shower 25383 26849 28315
Deodorants 26662 28128 29594
Depilatories 13099 14565 16031
Fragrances 42222 43688 45155
Hair Care 48637 50103 51569
Men’s Grooming 43136 44602 46068
Skin Care 53985 55451 56917
Sun Care 14086 15552 17019
  1. ¿En qué subcategoría invertiría?

Skin Care. Este modelo asume que todas las subcategorías crecen la misma cantidad de pesos cada año (una sola pendiente común de aproximadamente 1,466 millones de MXN anuales) y solo las distingue por su nivel propio (el efecto fijo de cada una), así que la comparación más honesta que este modelo permite hacer es de nivel, no de tasa de crecimiento. Bajo esa comparación, Skin Care ya es la subcategoría con el valor de mercado más alto en 2025 (cerca de MXN 71,830 millones, muy por encima de Fragrances y Hair Care, las siguientes más grandes) y también la que tiene el efecto fijo más alto del modelo, por lo que el pronóstico 2026-2028 la vuelve a dejar como la más grande. Vale la pena decirlo con honestidad: como el modelo obliga a todas las subcategorías a crecer al mismo ritmo en pesos, el pronóstico de Skin Care para 2028 (alrededor de MXN 56,900 millones) queda incluso por debajo de su valor real de 2025, lo que sugiere que su crecimiento real ha sido más rápido que el promedio que el modelo le está asignando, y que subcategorías pequeñas como Depilatories o Sun Care terminan con pronósticos desproporcionadamente altos frente a su tamaño real — una limitación clara de asumir una sola pendiente para las 8 subcategorías. Aun con esa limitación, la conclusión de invertir en Skin Care se sostiene porque no depende del pronóstico lineal en sí, sino de que ya es, por un margen amplio, la subcategoría más grande y de mayor nivel del mercado de cuidado personal en México, y todo indica que su ritmo de crecimiento real es al menos tan alto como el de las demás subcategorías grandes.

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.

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")

Resumen de 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

Resumen de 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

Resumen de 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, 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?

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 que pooling está ignorando, así que efectos fijos le gana a pooling.

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, así que no se rechaza que random effects sea consistente. A diferencia del ejercicio de patentes, aquí sí conviene usar random effects, porque es más eficiente que efectos fijos y la prueba de Hausman no encontró evidencia de que esté sesgado. 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, así que pronosticar es tan simple como en el ejemplo original de clase: 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

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, ya que 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 del modelo y vale la pena decirla, 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 (p < 0.001). A diferencia del ejercicio de patentes, aquí la prueba de Hausman (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 y 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), lo que 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, y 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, lo cual no es un error del código sino una consecuencia esperable de haber usado solo una variable explicativa. Por último, esta correlación negativa entre inflación y PIB per cápita no debe leerse como que “bajar la inflación garantiza” un mayor PIB per cápita: con 3 países y una sola variable, es evidencia sugestiva, no una prueba causal.

LS0tDQp0aXRsZTogIkFjdGl2aWRhZCAxIg0KYXV0aG9yOiAiT3NjYXIgUmV0ZXMgLSBBMDEzODM2NTMgRGVuaXNzZSBSZXluYSAtIEEwMDgzNDMzMCIgDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KICAgIHRoZW1lOiB1bml0ZWQNCmRhdGU6ICIyMDI2LTA4LTEzIg0KLS0tDQojIEltcG9ydGFyIHBhcXVldGVzIHkgbGxhbWFyIGxpYnJlcmlhcw0KYGBge3Igd2FybmluZz1GQUxTRX0NCm9wdGlvbnMocmVwb3MgPSBjKENSQU4gPSAiaHR0cHM6Ly9jbG91ZC5yLXByb2plY3Qub3JnIikpDQoNCiNpbnN0YWxsLnBhY2thZ2VzKCJyZWFkeGwiKQ0KbGlicmFyeShyZWFkeGwpDQojaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KbGlicmFyeShkcGx5cikNCiNpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5ciIpDQpsaWJyYXJ5KHRpZHlyKQ0KI2luc3RhbGwucGFja2FnZXMoInBsbSIpDQpsaWJyYXJ5KHBsbSkNCmBgYA0KIyBQYXJ0ZSAxLiBQYXRlbnRlcw0KRXN0ZSBhcmNoaXZvIGVzIHVuIHBhbmVsIGRlIGRhdG9zIGVtcHJlc2EtYcOxbzogY2FkYSBmaWxhIGVzIHVuYSBjb21iaW5hY2nDs24gZGUgdW5hIGVtcHJlc2EgKGN1c2lwKSB5IHVuIGHDsW8gKDIwMTLigJMyMDIxKS4gRXMgdW4gdGlwbyBkZSBkYXRhc2V0IG11eSB1c2FkbyBlbiBlY29ub21ldHLDrWEgZGUgaW5ub3ZhY2nDs24gKGdhc3RvIGVuIEkrRCB2cy4gcGF0ZW50ZXMpLCBwb3B1bGFyaXphZG8gcG9yIGVzdHVkaW9zIGNvbW8gbG9zIGRlIEhhbGwsIEdyaWxpY2hlcyB5IEhhdXNtYW4gc29icmUgcGF0ZW50ZXMgeSBSJkQuDQpEaWNjaW9uYXJpbyBkZSB2YXJpYWJsZXM6DQpjdXNpcDogaWRlbnRpZmljYWRvciBkZSBsYSBlbXByZXNhIChubyBlcyB1bmEgdmFyaWFibGUgcHJlZGljdGl2YSwgZXMgdW4gSUQpLg0KbWVyZ2VyOiAxIHNpIGxhIGVtcHJlc2EgdHV2byB1bmEgZnVzacOzbiBpbXBvcnRhbnRlIGVzZSBhw7FvLCAwIHNpIG5vLg0KZW1wbG95OiBlbXBsZWFkb3MsIGVuIG1pbGVzLg0KcmV0dXJuOiByZXRvcm5vIGRlIGxhIGFjY2nDs24sIGVuICUuDQpwYXRlbnRzOiBwYXRlbnRlcyBzb2xpY2l0YWRhcyBlbiBlbCBhw7FvLg0KcGF0ZW50c2c6IHBhdGVudGVzIGNvbmNlZGlkYXMgZW4gZWwgYcOxbyDigJQgZXN0YSBlcyBudWVzdHJhIHZhcmlhYmxlIG9iamV0aXZvIChZKS4NCnN0Y2twcjogcHJlY2lvIGRlIGxhIGFjY2nDs24uDQpybmQ6IGdhc3RvIGVuIEkrRCwgZW4gbWlsbG9uZXMgZGUgZMOzbGFyZXMgY29ycmllbnRlcy4NCnJuZGVmbHQ6IGdhc3RvIGVuIEkrRCwgZW4gbWlsbG9uZXMgZGUgZMOzbGFyZXMgZGVmbGFjdGFkb3MgKHByZWNpb3MgY29uc3RhbnRlcyBkZSAxOTcyKS4NCnJuZHN0Y2s6ICJzdG9jayIgYWN1bXVsYWRvIGRlIEkrRCAoaW52ZXJzacOzbiBhY3VtdWxhZGEsIG5vIHNvbG8gZGVsIGHDsW8pLg0Kc2FsZXM6IHZlbnRhcywgZW4gbWlsbG9uZXMgZGUgZMOzbGFyZXMgY29ycmllbnRlcy4NCnNpYzogY8OzZGlnbyBkZSBpbmR1c3RyaWEgYSA0IGTDrWdpdG9zLg0KeWVhcjogYcOxbyBkZSBsYSBvYnNlcnZhY2nDs24uDQpgYGB7cn0NCiMgQ2FyZ2Ftb3MgbGEgYmFzZSBkZSBkYXRvcw0KZGYxIDwtIHJlYWRfeGxzKCJDOlxcVXNlcnNcXG1lXFxEZXNrdG9wXFxyZXRvXFxiZHNcXFBBVEVOVCAzLnhscyIpDQpgYGANCmBgYHtyIGNvcnJlbGFjaW9uZXN9DQojIFJlbGFjacOzbiBjb24gdmFyaWFibGVzIGV4cGxpY2F0aXZhcw0KdmFyc19udW0gPC0gZGYxICU+JSBzZWxlY3QoZW1wbG95LCByZXR1cm4sIHBhdGVudHMsIHN0Y2twciwgcm5kLCBybmRlZmx0LCBybmRzdGNrLCBzYWxlcywgcGF0ZW50c2cpDQpyb3VuZChjb3IodmFyc19udW0sIHVzZSA9ICJjb21wbGV0ZS5vYnMiKSwgMikNCmBgYA0KYGBge3IgcGRhdGF9DQojIENvbnZlcnRpbW9zIG51ZXN0cmEgYmRkIGEgZGF0b3MgcGFuZWwNCmRmX21vZGVsbyA8LSBkZjEgJT4lDQogIHNlbGVjdChjdXNpcCwgeWVhciwgcGF0ZW50c2csIHJuZCwgZW1wbG95LCBzYWxlcywgcGF0ZW50cywgbWVyZ2VyKQ0KDQpwZGYgPC0gcGRhdGEuZnJhbWUoZGZfbW9kZWxvLCBpbmRleCA9IGMoImN1c2lwIiwgInllYXIiKSkNCmBgYA0KVmFyaWFibGUgZGVwZW5kaWVudGUgKFkpOiBgcGF0ZW50c2dgLCBsYXMgcGF0ZW50ZXMgKipvdG9yZ2FkYXMvY29uY2VkaWRhcyoqIGEgbGEgZW1wcmVzYSBlc2UgYcOxbyAobm8gbGFzIHNvbGljaXRhZGFzKS4NClZhcmlhYmxlcyBleHBsaWNhdGl2YXMgZWxlZ2lkYXM6IGBybmRgIChnYXN0byBlbiBJK0QpLCBgZW1wbG95YCAoZW1wbGVhZG9zKSwNCmBzYWxlc2AgKHZlbnRhcyksIGBwYXRlbnRzYCAocGF0ZW50ZXMgc29saWNpdGFkYXMgZXNlIGHDsW8pIHkgYG1lcmdlcmAgKHNpIGh1Ym8NCmZ1c2nDs24pLiBMYSBpZGVhIGVzIGV4cGxpY2FyIGN1w6FudGFzIHBhdGVudGVzIGxlICpjb25jZWRlbiogYSB1bmEgZW1wcmVzYQ0KKGBwYXRlbnRzZ2ApIGEgcGFydGlyIGRlIGN1w6FudG8gaW52aWVydGUsIHF1w6kgdGFuIGdyYW5kZSBlcywgeSBjdcOhbnRvIHNvbGljaXTDsy4NCkEgY29udGludWFjacOzbiBzZSBwcmVzZW50YSB1biBtb2RlbG8gZGUgcmVncmVzacOzbiBtdWx0aXBsZSBwYXJhIHRlbmVyIGFsZ28gY29uIHF1w6kgY29tcGFyYXIgbnVlc3Ryb3Mgc2lndWllbnRlcyBtb2RlbG9zLg0KYGBge3IgbG0tcmVmZXJlbmNpYX0NCm1vZGVsb19yZWZlcmVuY2lhIDwtIGxtKHBhdGVudHNnIH4gcm5kICsgZW1wbG95ICsgc2FsZXMgKyBwYXRlbnRzICsgbWVyZ2VyLCBkYXRhID0gcGRmKQ0Kc3VtbWFyeShtb2RlbG9fcmVmZXJlbmNpYSkNCmBgYA0KRXN0ZSBtb2RlbG8gbXVlc3RyYSB1bmEgUiBjdWFkcmFkYSBkZSAuODksIGxvIHF1ZSBzaWduaWZpY2EgcXVlIGxhcyB2YXJpYWJsZXMgZXNjb2dpZGFzIGV4cGxpY2FuIGVsIDg5JSBkZWwgY29tcG9ydGFtaWVudG8gZGUgbGEgdmFyaWFibGUgZGVwZW5kaWVudGUuDQpQYXNhbW9zIGEgbG9zIG1vZGVsb3MgUG9vbGluZywgV2l0aGluIHkgUmFuZG9tDQpgYGB7ciB0cmVzLW1vZGVsb3N9DQpmb3JtIDwtIHBhdGVudHNnIH4gcm5kICsgZW1wbG95ICsgc2FsZXMgKyBwYXRlbnRzICsgbWVyZ2VyDQoNCnBvb2xpbmcgPC0gcGxtKGZvcm0sIGRhdGEgPSBwZGYsIG1vZGVsID0gInBvb2xpbmciKQ0Kd2l0aGluICA8LSBwbG0oZm9ybSwgZGF0YSA9IHBkZiwgbW9kZWwgPSAid2l0aGluIikNCnJhbmRvbSAgPC0gcGxtKGZvcm0sIGRhdGEgPSBwZGYsIG1vZGVsID0gInJhbmRvbSIpDQpgYGANClJlc3VtZW4gZGUgUG9vbGluZw0KYGBge3IgcmVzdW1lbi1wb29saW5nfQ0Kc3VtbWFyeShwb29saW5nKQ0KYGBgDQpSZXN1bWVuIGRlIFdpdGhpbiAvIEVmZWN0b3MgRmlqb3MNCmBgYHtyIHJlc3VtZW4td2l0aGlufQ0Kc3VtbWFyeSh3aXRoaW4pDQpgYGANClJlc3VtZW4gZGUgUmFuZG9tIC8gRWZlY3RvcyBBbGVhdG9yaW9zDQpgYGB7ciByZXN1bWVuLXJhbmRvbX0NCnN1bW1hcnkocmFuZG9tKQ0KYGBgDQpQcnVlYmFzIGRlIGVzcGVjaWZpY2FjacOzbiBlbnRyZSBsb3MgZGlmZXJlbnRlcyBtb2RlbG9zDQpgYGB7ciBQRnRlc3R9DQpwRnRlc3Qod2l0aGluLCBwb29saW5nKQ0KYGBgDQpgYGB7ciBwaHRlc3R9DQpwaHRlc3QocmFuZG9tLCB3aXRoaW4pDQpgYGANCmBgYHtyIGZpeGVmfQ0KZWZlY3Rvc19maWpvcyA8LSBmaXhlZih3aXRoaW4pDQpoZWFkKGVmZWN0b3NfZmlqb3MpDQpgYGANClBhc2Ftb3MgYSBsYSBwcnVlYmEgZGUgcHJvbsOzc3RpY28NCmBgYHtyIGVzY2VuYXJpby0yMDIyfQ0KZXNjZW5hcmlvXzIwMjIgPC0gZGZfbW9kZWxvICU+JQ0KICBmaWx0ZXIoeWVhciA9PSAyMDIxLCBjb21wbGV0ZS5jYXNlcyhhY3Jvc3MoYyhybmQsIGVtcGxveSwgc2FsZXMsIHBhdGVudHMsIG1lcmdlcikpKSkNCg0KZmVfZGYgPC0gZGF0YS5mcmFtZShjdXNpcCA9IG5hbWVzKGVmZWN0b3NfZmlqb3MpLCBlZmVjdG9fZmlqbyA9IGFzLm51bWVyaWMoZWZlY3Rvc19maWpvcykpDQojIHBkYXRhLmZyYW1lIGd1YXJkYSBjdXNpcCBjb21vIHRleHRvOyBpZ3VhbGFtb3MgZWwgdGlwbyBwYXJhIHBvZGVyIHVuaXINCmZlX2RmJGN1c2lwIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGZlX2RmJGN1c2lwKSkNCg0KY29lZnMgPC0gY29lZih3aXRoaW4pDQoNCnByb25vc3RpY29fMjAyMiA8LSBlc2NlbmFyaW9fMjAyMiAlPiUNCiAgaW5uZXJfam9pbihmZV9kZiwgYnkgPSAiY3VzaXAiKSAlPiUNCiAgbXV0YXRlKA0KICAgIHBhdGVudHNnX3Byb25vc3RpY29fMjAyMiA9DQogICAgICBlZmVjdG9fZmlqbyArDQogICAgICBjb2Vmc1sicm5kIl0gICAgICogcm5kICsNCiAgICAgIGNvZWZzWyJlbXBsb3kiXSAgKiBlbXBsb3kgKw0KICAgICAgY29lZnNbInNhbGVzIl0gICAqIHNhbGVzICsNCiAgICAgIGNvZWZzWyJwYXRlbnRzIl0gKiBwYXRlbnRzICsNCiAgICAgIGNvZWZzWyJtZXJnZXIiXSAgKiBtZXJnZXINCiAgKSAlPiUNCiAgc2VsZWN0KGN1c2lwLCBwYXRlbnRzZ18yMDIxID0gcGF0ZW50c2csIHBhdGVudHNnX3Byb25vc3RpY29fMjAyMikNCg0KY2F0KCJFbXByZXNhcyBjb24gcHJvbsOzc3RpY28gY2FsY3VsYWRvOiIsIG5yb3cocHJvbm9zdGljb18yMDIyKSwgImRlIiwgbl9kaXN0aW5jdChkZl9tb2RlbG8kY3VzaXApLCAiXG4iKQ0Ka25pdHI6OmthYmxlKGhlYWQocHJvbm9zdGljb18yMDIyLCAxNSksIGRpZ2l0cyA9IDEpDQpgYGANCg0KYGBge3IgcmVzdW1lbi1wcm9ub3N0aWNvfQ0Kc3VtbWFyeShwcm9ub3N0aWNvXzIwMjIkcGF0ZW50c2dfcHJvbm9zdGljb18yMDIyKQ0KY2F0KCJQcm9uw7NzdGljb3MgbmVnYXRpdm9zIChzaW4gc2VudGlkbyBwYXJhIHVuIGNvbnRlbyk6Iiwgc3VtKHByb25vc3RpY29fMjAyMiRwYXRlbnRzZ19wcm9ub3N0aWNvXzIwMjIgPCAwKSwgIlxuIikNCmBgYA0KDQpDb25jbHVzaW9uZXMgZmluYWxlcyBwYXJhIGVsIGVqZXJjaWNpbyAxOg0KDQpMYXMgcHJ1ZWJhcyBkZSBlc3BlY2lmaWNhY2nDs24gKGBwRnRlc3RgLCBgcGh0ZXN0YCkgY29pbmNpZGVuIGVuIHF1ZSBlZmVjdG9zIGZpam9zIGVzIGVsIG1vZGVsbyBjb3JyZWN0byBwYXJhIGVzdG9zIGRhdG9zLCB5YSBxdWUgc8OtIGhheSBjYXJhY3RlcsOtc3RpY2FzIHByb3BpYXMgZGUgY2FkYSBlbXByZXNhIHF1ZSBpbXBvcnRhbiB5IHF1ZSBlc3TDoW4gY29ycmVsYWNpb25hZGFzIGNvbiBsYXMgdmFyaWFibGVzIGV4cGxpY2F0aXZhcywgYXPDrSBxdWUgdW4gbW9kZWxvIHBvb2xpbmcgKHF1ZSBpZ25vcmEgZXNhIGlkZW50aWRhZCBkZSBlbXByZXNhKSBxdWVkYSBkZXNjYXJ0YWRvIHkgdW4gbW9kZWxvIGRlIGVmZWN0b3MgYWxlYXRvcmlvcyB0YW1wb2NvIGVzIGNvbnNpc3RlbnRlIGZyZW50ZSBhbCBkZSBlZmVjdG9zIGZpam9zLiBFbCBwcm9uw7NzdGljbyBkZSBwYXRlbnRlcyBvdG9yZ2FkYXMgcGFyYSAyMDIyIHNlIGhpem8gYmFqbyBlbCBzdXB1ZXN0byBkZSBxdWUgY2FkYSBlbXByZXNhIG1hbnRpZW5lIGVuIDIwMjIgbG9zIG1pc21vcyB2YWxvcmVzIGRlIEkrRCwgZW1wbGVvLCB2ZW50YXMsIHBhdGVudGVzIHNvbGljaXRhZGFzIHkgZnVzacOzbiBxdWUgdHV2byBlbiAyMDIxLCB1c2FuZG8gZWwgZWZlY3RvIGZpam8gcHJvcGlvIGRlIGNhZGEgZW1wcmVzYSBtw6FzIGxvcyBjb2VmaWNpZW50ZXMgZGVsIG1vZGVsbyBkZSBlZmVjdG9zIGZpam9zLiBDb21vIGBwYXRlbnRzZ2AgZXMgdW4gY29udGVvIHkgZXN0ZSBlcyB1biBtb2RlbG8gbGluZWFsLCBhbGd1bm9zIHByb27Ds3N0aWNvcyBzYWxlbiBuZWdhdGl2b3MsIGxvIGN1YWwgbm8gdGllbmUgc2VudGlkbyBwYXJhIHVuIG7Dum1lcm8gZGUgcGF0ZW50ZXMgcGVybyBlcyB1bmEgbGltaXRhY2nDs24gZXNwZXJhZGEgZGUgdXNhciB1biBtb2RlbG8gbGluZWFsIHNvYnJlIHVuYSB2YXJpYWJsZSBkZSBjb250ZW8sIG5vIHVuIGVycm9yIGRlbCBjw7NkaWdvLg0KDQojIFBhcnRlIDIuIEN1aWRhZG8gZGUgbGEgUGllbA0KU2kgdHV2aWVyYXMgcXVlIGludmVydGlyIGVuIGFsZ3VuYSBzdWItY2F0ZWdvcsOtYSwgwr9lbiBjdcOhbCBsbyBoYXLDrWFzPw0KSnVzdGlmaWNhIGFtcGxpYW1lbnRlIHR1IHJlc3B1ZXN0YS4NCg0KMS4gQ2FyZ2Ftb3MgbGEgQmFzZSBkZSBEYXRvcw0KYGBge3J9DQpkZjIgPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcbWVcXERlc2t0b3BcXHJldG9cXGJkc1xcbWFya2V0X3NpemVzX3Jhdy5jc3YiKQ0KYGBgDQoyLiBMaW1waWV6YSBkZSBEYXRvcw0KYGBge3IgY2FyZ2F9DQpyYXcgPC0gcmVhZF9leGNlbCgiQzpcXFVzZXJzXFxtZVxcRGVza3RvcFxccmV0b1xcYmRzXFxNYXJrZXRfc2l6ZXMueGxzeCIsIHNoZWV0ID0gIlN0YXRpc3RpY3MgRGF0YSIsIHNraXAgPSA1LCBjb2xfdHlwZXMgPSAidGV4dCIpDQpuYW1lcyhyYXcpWzFdIDwtICJHZW9ncmFwaHkiDQpyYXcgPC0gcmF3ICU+JSBmaWx0ZXIoIWlzLm5hKEdlb2dyYXBoeSksIEdlb2dyYXBoeSA9PSAiTWV4aWNvIikNCg0KY2F0ZWdvcmlhc19wcm9kdWN0byA8LSBjKCJCYXRoIGFuZCBTaG93ZXIiLCAiRGVvZG9yYW50cyIsICJEZXBpbGF0b3JpZXMiLCAiRnJhZ3JhbmNlcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICJIYWlyIENhcmUiLCAiTWVuJ3MgR3Jvb21pbmciLCAiU2tpbiBDYXJlIiwgIlN1biBDYXJlIikNCg0KcmF3IDwtIHJhdyAlPiUgZmlsdGVyKENhdGVnb3J5ICVpbiUgY2F0ZWdvcmlhc19wcm9kdWN0bykNCg0KZGF0b3MgPC0gcmF3ICU+JQ0KICBzZWxlY3QoQ2F0ZWdvcnksIGAyMDExYDpgMjAyNWApICU+JQ0KICBwaXZvdF9sb25nZXIoLUNhdGVnb3J5LCBuYW1lc190byA9ICJ5ZWFyIiwgdmFsdWVzX3RvID0gInZhbHVlIikgJT4lDQogIG11dGF0ZSh5ZWFyID0gYXMuaW50ZWdlcih5ZWFyKSwgdmFsdWUgPSBzdXBwcmVzc1dhcm5pbmdzKGFzLm51bWVyaWModmFsdWUpKSkNCg0Ka25pdHI6OmthYmxlKGRhdG9zICU+JSBmaWx0ZXIoeWVhciAlaW4lIGMoMjAxMSwgMjAxNSwgMjAxOSwgMjAyMCwgMjAyMSwgMjAyNSkpICU+JQ0KICAgICAgICAgICAgICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHllYXIsIHZhbHVlc19mcm9tID0gdmFsdWUpLA0KICAgICAgICAgICAgIGRpZ2l0cyA9IDAsIGNhcHRpb24gPSAiVmFsb3JlcyBkZSBtZXJjYWRvIChNWE4gbWlsbG9uZXMpLCBhw7FvcyBzZWxlY2Npb25hZG9zIikNCmBgYA0KMy4gQ29udmVydGltb3MgbGEgYmRkIGEgZGF0b3MgZGUgcGFuZWwNCg0KQXF1w60gY2FkYSBzdWJjYXRlZ29yw61hIChgQ2F0ZWdvcnlgKSBoYWNlIGVsIHBhcGVsIGRlICJlbXByZXNhIiB5IGNhZGEgYcOxbyBlbA0KcGFwZWwgZGUgcGVyaW9kbyBkZSB0aWVtcG8sIGlndWFsIHF1ZSBlbiBlbCBlamVtcGxvIGRlIGNsYXNlIChgZW1wcmVzYWAsDQpgYW5pb2ApLiBHdWFyZGFtb3MgdW5hIGNvcGlhIG51bcOpcmljYSBkZWwgYcOxbyAoYGFuaW9fbnVtYCkgcG9ycXVlDQpgcGRhdGEuZnJhbWVgIGNvbnZpZXJ0ZSBsYSBjb2x1bW5hIHF1ZSBzZSB1c2EgY29tbyDDrW5kaWNlIGRlIHRpZW1wbywgeSBwYXJhDQp1c2FyIGVsIGHDsW8gY29tbyB2YXJpYWJsZSBleHBsaWNhdGl2YSBjb250aW51YSAoeSBwb2RlciBwcm9ub3N0aWNhciBhw7Fvcw0KZnV0dXJvcyBjb21vIDIwMjYpIG5lY2VzaXRhbW9zIGVzYSBjb3BpYSBudW3DqXJpY2EgYXBhcnRlLg0KYGBge3IgcGRhdGEtcGllbH0NCmRhdG9zJGFuaW9fbnVtIDwtIGRhdG9zJHllYXINCg0KcGRmX3BpZWwgPC0gcGRhdGEuZnJhbWUoZGF0b3MsIGluZGV4ID0gYygiQ2F0ZWdvcnkiLCAieWVhciIpKQ0KYGBgDQo0LiBNb2RlbG8gZGUgcmVmZXJlbmNpYQ0KDQpWYXJpYWJsZSBkZXBlbmRpZW50ZSAoWSk6IGB2YWx1ZWAgKHRhbWHDsW8gZGUgbWVyY2FkbywgZW4gTVhOIG1pbGxvbmVzKS4NClZhcmlhYmxlIGV4cGxpY2F0aXZhIChYKTogYGFuaW9fbnVtYCAoZWwgYcOxbyksIGlndWFsIHF1ZSBlbiBlbCBlamVtcGxvIGRlDQpjbGFzZSBkb25kZSBgdmVudGFzYCBzZSBleHBsaWNhYmEgY29uIGBwdWJsaWNpZGFkYC4NCmBgYHtyIGxtLXJlZmVyZW5jaWEtcGllbH0NCm1vZGVsb19yZWZlcmVuY2lhX3BpZWwgPC0gbG0odmFsdWUgfiBhbmlvX251bSwgZGF0YSA9IHBkZl9waWVsKQ0Kc3VtbWFyeShtb2RlbG9fcmVmZXJlbmNpYV9waWVsKQ0KYGBgDQo1LiBNb2RlbG9zIFBvb2xpbmcsIFdpdGhpbiB5IFJhbmRvbQ0KYGBge3IgdHJlcy1tb2RlbG9zLXBpZWx9DQpmb3JtX3BpZWwgPC0gdmFsdWUgfiBhbmlvX251bQ0KDQpwb29saW5nX3BpZWwgPC0gcGxtKGZvcm1fcGllbCwgZGF0YSA9IHBkZl9waWVsLCBtb2RlbCA9ICJwb29saW5nIikNCndpdGhpbl9waWVsICA8LSBwbG0oZm9ybV9waWVsLCBkYXRhID0gcGRmX3BpZWwsIG1vZGVsID0gIndpdGhpbiIpDQpyYW5kb21fcGllbCAgPC0gcGxtKGZvcm1fcGllbCwgZGF0YSA9IHBkZl9waWVsLCBtb2RlbCA9ICJyYW5kb20iKQ0KYGBgDQpSZXN1bWVuIGRlIFBvb2xpbmcNCmBgYHtyIHJlc3VtZW4tcG9vbGluZy1waWVsfQ0Kc3VtbWFyeShwb29saW5nX3BpZWwpDQpgYGANClJlc3VtZW4gZGUgV2l0aGluIC8gRWZlY3RvcyBGaWpvcw0KYGBge3IgcmVzdW1lbi13aXRoaW4tcGllbH0NCnN1bW1hcnkod2l0aGluX3BpZWwpDQpgYGANClJlc3VtZW4gZGUgUmFuZG9tIC8gRWZlY3RvcyBBbGVhdG9yaW9zDQpgYGB7ciByZXN1bWVuLXJhbmRvbS1waWVsfQ0Kc3VtbWFyeShyYW5kb21fcGllbCkNCmBgYA0KNi4gUHJ1ZWJhcyBkZSBlc3BlY2lmaWNhY2nDs24NCmBgYHtyIHBmdGVzdC1waWVsfQ0KcEZ0ZXN0KHdpdGhpbl9waWVsLCBwb29saW5nX3BpZWwpDQpgYGANCmBgYHtyIHBodGVzdC1waWVsfQ0KcGh0ZXN0KHJhbmRvbV9waWVsLCB3aXRoaW5fcGllbCkNCmBgYA0KTGEgcHJ1ZWJhIEYgcmVjaGF6YSBxdWUgcG9vbGluZyBzZWEgdsOhbGlkbzogc8OtIGhheSBkaWZlcmVuY2lhcyBwcm9waWFzIGRlDQpjYWRhIHN1YmNhdGVnb3LDrWEgcXVlIHBvb2xpbmcgaWdub3JhLiBMYSBwcnVlYmEgZGUgSGF1c21hbiwgZW4gY2FtYmlvLCBubw0KcmVjaGF6YSBxdWUgcmFuZG9tIGVmZmVjdHMgc2VhIGNvbnNpc3RlbnRlIChlbCBlc3RhZMOtc3RpY28gc2FsZQ0KcHLDoWN0aWNhbWVudGUgZW4gY2VybyksIGFzw60gcXVlIGFxdcOtIGVsIG1vZGVsbyBlbGVnaWRvIGVzICoqcmFuZG9tDQplZmZlY3RzKiog4oCUIGEgZGlmZXJlbmNpYSBkZSBwYXRlbnRlcywgZG9uZGUgZ2FuYWJhIGVmZWN0b3MgZmlqb3MuDQoNCjcuIFByb27Ds3N0aWNvIDIwMjYtMjAyOA0KDQpgcmFuZG9tIGVmZmVjdHNgIHNvbG8gbm9zIGRhIHVuIGludGVyY2VwdG8geSB1bmEgcGVuZGllbnRlIGNvbXVuZXMgcGFyYQ0KdG9kYXMgbGFzIHN1YmNhdGVnb3LDrWFzIChpZ3VhbCBxdWUgZW4gZWwgZWplbXBsbyBkZSBjbGFzZSwgZG9uZGUgZWwNCnByb27Ds3N0aWNvIGRlIGB2ZW50YXNgIHVzYWJhIHVuIMO6bmljbyBpbnRlcmNlcHRvIHkgdW5hIMO6bmljYSBwZW5kaWVudGUgcGFyYQ0KbGFzIDQgZW1wcmVzYXMpLiBBcXXDrSBlc28gbm8gc2lydmUgcGFyYSBkaWZlcmVuY2lhciBlbnRyZSBzdWJjYXRlZ29yw61hcywNCnBvcnF1ZSBlbCBhw7FvIDIwMjYgZXMgZWwgbWlzbW8gcGFyYSB0b2Rhcy4gQ29tbyBsYSBwcnVlYmEgZGUgSGF1c21hbiBtb3N0csOzDQpxdWUgZWZlY3RvcyBmaWpvcyB5IHJhbmRvbSBlZmZlY3RzIHByw6FjdGljYW1lbnRlIGNvaW5jaWRlbiBlbiBlc3RlIGNhc28NCihsYSBwZW5kaWVudGUgZGVsIGHDsW8gZXMgaWTDqW50aWNhIGVuIGFtYm9zKSwgdXNhbW9zIGBmaXhlZigpYCDigJQgbGEgbWlzbWENCmhlcnJhbWllbnRhIHlhIHVzYWRhIGVuIGVsIGVqZXJjaWNpbyBkZSBwYXRlbnRlcyDigJQgcGFyYSBvYnRlbmVyIGVsIG5pdmVsDQpwcm9waW8gZGUgY2FkYSBzdWJjYXRlZ29yw61hIHkgYXPDrSBzw60gcG9kZXIgcHJvbm9zdGljYXIgdW4gdmFsb3IgZGlzdGludG8NCnBhcmEgY2FkYSB1bmEuDQpgYGB7ciBmaXhlZi1waWVsfQ0KZWZlY3Rvc19maWpvc19waWVsIDwtIGZpeGVmKHdpdGhpbl9waWVsKQ0KZWZlY3Rvc19maWpvc19waWVsDQpgYGANCmBgYHtyIHByb25vc3RpY28tcGllbH0NCnBlbmRpZW50ZV9waWVsIDwtIGNvZWYod2l0aGluX3BpZWwpWyJhbmlvX251bSJdDQoNCmZ1dHVyb19waWVsIDwtIGV4cGFuZC5ncmlkKENhdGVnb3J5ID0gbmFtZXMoZWZlY3Rvc19maWpvc19waWVsKSwgYW5pb19udW0gPSAyMDI2OjIwMjgpDQpmdXR1cm9fcGllbCRlZmVjdG9fZmlqbyA8LSBlZmVjdG9zX2Zpam9zX3BpZWxbYXMuY2hhcmFjdGVyKGZ1dHVyb19waWVsJENhdGVnb3J5KV0NCmZ1dHVyb19waWVsJHByb25vc3RpY28gPC0gZnV0dXJvX3BpZWwkZWZlY3RvX2Zpam8gKyBwZW5kaWVudGVfcGllbCAqIGZ1dHVyb19waWVsJGFuaW9fbnVtDQoNCmtuaXRyOjprYWJsZShmdXR1cm9fcGllbCAlPiUgc2VsZWN0KENhdGVnb3J5LCBhbmlvX251bSwgcHJvbm9zdGljbykgJT4lDQogICAgICAgICAgICAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gYW5pb19udW0sIHZhbHVlc19mcm9tID0gcHJvbm9zdGljbyksDQogICAgICAgICAgICAgZGlnaXRzID0gMCwgY2FwdGlvbiA9ICJQcm9uw7NzdGljbyBkZSB2YWxvciBkZSBtZXJjYWRvIDIwMjYtMjAyOCAoTVhOIG1pbGxvbmVzKSIpDQpgYGANCjguIMK/RW4gcXXDqSBzdWJjYXRlZ29yw61hIGludmVydGlyw61hPw0KDQoqKlNraW4gQ2FyZS4qKiBFc3RlIG1vZGVsbyBhc3VtZSBxdWUgdG9kYXMgbGFzIHN1YmNhdGVnb3LDrWFzIGNyZWNlbiBsYSBtaXNtYQ0KY2FudGlkYWQgZGUgcGVzb3MgY2FkYSBhw7FvICh1bmEgc29sYSBwZW5kaWVudGUgY29tw7puIGRlIGFwcm94aW1hZGFtZW50ZQ0KMSw0NjYgbWlsbG9uZXMgZGUgTVhOIGFudWFsZXMpIHkgc29sbyBsYXMgZGlzdGluZ3VlIHBvciBzdSBuaXZlbCBwcm9waW8gKGVsDQplZmVjdG8gZmlqbyBkZSBjYWRhIHVuYSksIGFzw60gcXVlIGxhIGNvbXBhcmFjacOzbiBtw6FzIGhvbmVzdGEgcXVlIGVzdGUgbW9kZWxvDQpwZXJtaXRlIGhhY2VyIGVzIGRlIG5pdmVsLCBubyBkZSB0YXNhIGRlIGNyZWNpbWllbnRvLiBCYWpvIGVzYSBjb21wYXJhY2nDs24sDQpTa2luIENhcmUgeWEgZXMgbGEgc3ViY2F0ZWdvcsOtYSBjb24gZWwgdmFsb3IgZGUgbWVyY2FkbyBtw6FzIGFsdG8gZW4gMjAyNQ0KKGNlcmNhIGRlIE1YTiA3MSw4MzAgbWlsbG9uZXMsIG11eSBwb3IgZW5jaW1hIGRlIEZyYWdyYW5jZXMgeSBIYWlyIENhcmUsIGxhcw0Kc2lndWllbnRlcyBtw6FzIGdyYW5kZXMpIHkgdGFtYmnDqW4gbGEgcXVlIHRpZW5lIGVsIGVmZWN0byBmaWpvIG3DoXMgYWx0byBkZWwNCm1vZGVsbywgcG9yIGxvIHF1ZSBlbCBwcm9uw7NzdGljbyAyMDI2LTIwMjggbGEgdnVlbHZlIGEgZGVqYXIgY29tbyBsYSBtw6FzDQpncmFuZGUuIFZhbGUgbGEgcGVuYSBkZWNpcmxvIGNvbiBob25lc3RpZGFkOiBjb21vIGVsIG1vZGVsbyBvYmxpZ2EgYSB0b2Rhcw0KbGFzIHN1YmNhdGVnb3LDrWFzIGEgY3JlY2VyIGFsIG1pc21vIHJpdG1vIGVuIHBlc29zLCBlbCBwcm9uw7NzdGljbyBkZSBTa2luDQpDYXJlIHBhcmEgMjAyOCAoYWxyZWRlZG9yIGRlIE1YTiA1Niw5MDAgbWlsbG9uZXMpIHF1ZWRhIGluY2x1c28gcG9yIGRlYmFqbw0KZGUgc3UgdmFsb3IgcmVhbCBkZSAyMDI1LCBsbyBxdWUgc3VnaWVyZSBxdWUgc3UgY3JlY2ltaWVudG8gcmVhbCBoYSBzaWRvIG3DoXMNCnLDoXBpZG8gcXVlIGVsIHByb21lZGlvIHF1ZSBlbCBtb2RlbG8gbGUgZXN0w6EgYXNpZ25hbmRvLCB5IHF1ZSBzdWJjYXRlZ29yw61hcw0KcGVxdWXDsWFzIGNvbW8gRGVwaWxhdG9yaWVzIG8gU3VuIENhcmUgdGVybWluYW4gY29uIHByb27Ds3N0aWNvcw0KZGVzcHJvcG9yY2lvbmFkYW1lbnRlIGFsdG9zIGZyZW50ZSBhIHN1IHRhbWHDsW8gcmVhbCDigJQgdW5hIGxpbWl0YWNpw7NuIGNsYXJhDQpkZSBhc3VtaXIgdW5hIHNvbGEgcGVuZGllbnRlIHBhcmEgbGFzIDggc3ViY2F0ZWdvcsOtYXMuIEF1biBjb24gZXNhDQpsaW1pdGFjacOzbiwgbGEgY29uY2x1c2nDs24gZGUgaW52ZXJ0aXIgZW4gU2tpbiBDYXJlIHNlIHNvc3RpZW5lIHBvcnF1ZSBubw0KZGVwZW5kZSBkZWwgcHJvbsOzc3RpY28gbGluZWFsIGVuIHPDrSwgc2lubyBkZSBxdWUgeWEgZXMsIHBvciB1biBtYXJnZW4NCmFtcGxpbywgbGEgc3ViY2F0ZWdvcsOtYSBtw6FzIGdyYW5kZSB5IGRlIG1heW9yIG5pdmVsIGRlbCBtZXJjYWRvIGRlIGN1aWRhZG8NCnBlcnNvbmFsIGVuIE3DqXhpY28sIHkgdG9kbyBpbmRpY2EgcXVlIHN1IHJpdG1vIGRlIGNyZWNpbWllbnRvIHJlYWwgZXMgYWwNCm1lbm9zIHRhbiBhbHRvIGNvbW8gZWwgZGUgbGFzIGRlbcOhcyBzdWJjYXRlZ29yw61hcyBncmFuZGVzLg0KDQojIFBhcnRlIDMuIEJhbmNvIG11bmRpYWwNCmBgYHtyIGltcG9ydGFyfQ0KdXJsX2dkcCAgIDwtICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZGF0YXNldHMvZ2RwL21hc3Rlci9kYXRhL2dkcC5jc3YiDQp1cmxfaW5mbCAgPC0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9kYXRhc2V0cy9pbmZsYXRpb24vbWFzdGVyL2RhdGEvaW5mbGF0aW9uLWNvbnN1bWVyLmNzdiINCnVybF9wb3AgICA8LSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2RhdGFzZXRzL3BvcHVsYXRpb24vbWFzdGVyL2RhdGEvcG9wdWxhdGlvbi5jc3YiDQoNCmdkcCAgPC0gcmVhZC5jc3YodXJsX2dkcCkNCmluZmwgPC0gcmVhZC5jc3YodXJsX2luZmwpDQpwb3AgIDwtIHJlYWQuY3N2KHVybF9wb3ApDQoNCnBhaXNlcyA8LSBjKCJDSE4iLCAiSlBOIiwgIktPUiIpDQpub21icmVzX3BhaXNlcyA8LSBjKENITiA9ICJDaGluYSIsIEpQTiA9ICJKYXDDs24iLCBLT1IgPSAiQ29yZWEgZGVsIFN1ciIpDQoNCmdkcCAgPC0gZ2RwICAlPiUgZmlsdGVyKENvdW50cnkuQ29kZSAlaW4lIHBhaXNlcykgJT4lIHNlbGVjdChDb3VudHJ5LkNvZGUsIFllYXIsIGdkcF91c2QgPSBWYWx1ZSkNCnBvcCAgPC0gcG9wICAlPiUgZmlsdGVyKENvdW50cnkuQ29kZSAlaW4lIHBhaXNlcykgJT4lIHNlbGVjdChDb3VudHJ5LkNvZGUsIFllYXIsIHBvYmxhY2lvbiA9IFZhbHVlKQ0KaW5mbCA8LSBpbmZsICU+JSBmaWx0ZXIoQ291bnRyeS5Db2RlICVpbiUgcGFpc2VzKSAlPiUgc2VsZWN0KENvdW50cnkuQ29kZSwgWWVhciwgaW5mbGFjaW9uX3BjdCA9IEluZmxhdGlvbikNCg0KZGF0b3NfYm0gPC0gZ2RwICU+JQ0KICBpbm5lcl9qb2luKHBvcCwgYnkgPSBjKCJDb3VudHJ5LkNvZGUiLCAiWWVhciIpKSAlPiUNCiAgaW5uZXJfam9pbihpbmZsLCBieSA9IGMoIkNvdW50cnkuQ29kZSIsICJZZWFyIikpICU+JQ0KICBmaWx0ZXIoWWVhciA+PSAxOTkwLCBZZWFyIDw9IDIwMjMpICU+JQ0KICBtdXRhdGUoDQogICAgcGFpcyA9IG5vbWJyZXNfcGFpc2VzW0NvdW50cnkuQ29kZV0sDQogICAgZ2RwX3BlcmNhcGl0YV91c2QgPSBnZHBfdXNkIC8gcG9ibGFjaW9uDQogICkgJT4lDQogIHJlbmFtZShhbmlvID0gWWVhcikgJT4lDQogIHNlbGVjdChwYWlzLCBDb3VudHJ5LkNvZGUsIGFuaW8sIGdkcF91c2QsIHBvYmxhY2lvbiwgZ2RwX3BlcmNhcGl0YV91c2QsIGluZmxhY2lvbl9wY3QpICU+JQ0KICBhcnJhbmdlKHBhaXMsIGFuaW8pDQoNCmNhdCgiRmlsYXM6IiwgbnJvdyhkYXRvc19ibSksICIgfCBQYcOtc2VzOiIsIG5fZGlzdGluY3QoZGF0b3NfYm0kcGFpcyksICIgfCBBw7FvczoiLCBtaW4oZGF0b3NfYm0kYW5pbyksICItIiwgbWF4KGRhdG9zX2JtJGFuaW8pLCAiXG4iKQ0Ka25pdHI6OmthYmxlKGRhdG9zX2JtICU+JSBmaWx0ZXIoYW5pbyAlaW4lIGMoMTk5MCwgMjAwMCwgMjAxMCwgMjAyMCwgMjAyMykpLCBkaWdpdHMgPSAxLA0KICAgICAgICAgICAgIGNhcHRpb24gPSAiTXVlc3RyYSBkZSBsb3MgZGF0b3MgaW1wb3J0YWRvcyIpDQpgYGANCg0KUGFuZWwgYmFsYW5jZWFkbzogMyBwYcOtc2VzIMOXIDM0IGHDsW9zID0gMTAyIG9ic2VydmFjaW9uZXMsIHNpbiBkYXRvcw0KZmFsdGFudGVzLg0KDQojIFBhc28gMTogZXN0cnVjdHVyYSBkZSBwYW5lbCB5IG1vZGVsbyBkZSByZWZlcmVuY2lhDQoNCmBgYHtyIHBkYXRhLWJtfQ0KcGRmX2JtIDwtIHBkYXRhLmZyYW1lKGRhdG9zX2JtLCBpbmRleCA9IGMoInBhaXMiLCAiYW5pbyIpKQ0KDQptb2RlbG9fcmVmZXJlbmNpYV9ibSA8LSBsbShnZHBfcGVyY2FwaXRhX3VzZCB+IGluZmxhY2lvbl9wY3QsIGRhdGEgPSBwZGZfYm0pDQpzdW1tYXJ5KG1vZGVsb19yZWZlcmVuY2lhX2JtKQ0KYGBgDQoNCiMgUGFzbyAyOiBsb3MgdHJlcyBtb2RlbG9zIChwb29saW5nLCB3aXRoaW4sIHJhbmRvbSkNCg0KYGBge3IgdHJlcy1tb2RlbG9zLWJtfQ0KZm9ybV9ibSA8LSBnZHBfcGVyY2FwaXRhX3VzZCB+IGluZmxhY2lvbl9wY3QNCg0KcG9vbGluZ19ibSA8LSBwbG0oZm9ybV9ibSwgZGF0YSA9IHBkZl9ibSwgbW9kZWwgPSAicG9vbGluZyIpDQp3aXRoaW5fYm0gIDwtIHBsbShmb3JtX2JtLCBkYXRhID0gcGRmX2JtLCBtb2RlbCA9ICJ3aXRoaW4iKQ0KcmFuZG9tX2JtICA8LSBwbG0oZm9ybV9ibSwgZGF0YSA9IHBkZl9ibSwgbW9kZWwgPSAicmFuZG9tIikNCmBgYA0KDQpSZXN1bWVuIGRlIFBvb2xpbmcNCmBgYHtyIHJlc3VtZW4tcG9vbGluZy1ibX0NCnN1bW1hcnkocG9vbGluZ19ibSkNCmBgYA0KDQpSZXN1bWVuIGRlIFdpdGhpbiAvIEVmZWN0b3MgRmlqb3MNCmBgYHtyIHJlc3VtZW4td2l0aGluLWJtfQ0Kc3VtbWFyeSh3aXRoaW5fYm0pDQpgYGANCg0KUmVzdW1lbiBkZSBSYW5kb20gLyBFZmVjdG9zIEFsZWF0b3Jpb3MNCmBgYHtyIHJlc3VtZW4tcmFuZG9tLWJtfQ0Kc3VtbWFyeShyYW5kb21fYm0pDQpgYGANCg0KRW4gbG9zIHRyZXMgbW9kZWxvcyBlbCBjb2VmaWNpZW50ZSBkZSBpbmZsYWNpw7NuIGVzIG5lZ2F0aXZvIHkgc2lnbmlmaWNhdGl2bzoNCm3DoXMgaW5mbGFjacOzbiBzZSBhc29jaWEgY29uIG1lbm9yIFBJQiBwZXIgY8OhcGl0YSwgY29uc2lzdGVudGUgY29uIGxhIGlkZWENCmVjb27Ds21pY2EgZGUgcXVlIGxhIGluZXN0YWJpbGlkYWQgZGUgcHJlY2lvcyB2YSBkZSBsYSBtYW5vIGRlIG1lbm9yDQpkZXNhcnJvbGxvIHJlbGF0aXZvIGVuIGVzdGEgbXVlc3RyYS4NCg0KIyBQYXNvIDM6IMK/Y3XDoWwgbW9kZWxvIGVzIGVsIGNvcnJlY3RvPw0KDQpgYGB7ciBwZnRlc3QtYm19DQpwRnRlc3Qod2l0aGluX2JtLCBwb29saW5nX2JtKQ0KYGBgDQoNCkVsIHAtdmFsdWUgZXMgcHLDoWN0aWNhbWVudGUgMDogc2UgcmVjaGF6YSBwb29saW5nLiBIYXkgZWZlY3RvcyBwcm9waW9zIGRlDQpjYWRhIHBhw61zIHF1ZSBwb29saW5nIGVzdMOhIGlnbm9yYW5kbywgYXPDrSBxdWUgZWZlY3RvcyBmaWpvcyBsZSBnYW5hIGENCnBvb2xpbmcuDQoNCmBgYHtyIHBodGVzdC1ibX0NCnBodGVzdChyYW5kb21fYm0sIHdpdGhpbl9ibSkNCmBgYA0KDQpBcXXDrSBlbCByZXN1bHRhZG8gZXMgZGlzdGludG8gYWwgZGVsIGVqZXJjaWNpbyBkZSBwYXRlbnRlczogZWwgcC12YWx1ZSBlcw0KMC4xOCwgbm8gc2lnbmlmaWNhdGl2bywgYXPDrSBxdWUgbm8gc2UgcmVjaGF6YSBxdWUgcmFuZG9tIGVmZmVjdHMgc2VhDQpjb25zaXN0ZW50ZS4gQSBkaWZlcmVuY2lhIGRlbCBlamVyY2ljaW8gZGUgcGF0ZW50ZXMsIGFxdcOtIHPDrSBjb252aWVuZSB1c2FyDQpyYW5kb20gZWZmZWN0cywgcG9ycXVlIGVzIG3DoXMgZWZpY2llbnRlIHF1ZSBlZmVjdG9zIGZpam9zIHkgbGEgcHJ1ZWJhIGRlDQpIYXVzbWFuIG5vIGVuY29udHLDsyBldmlkZW5jaWEgZGUgcXVlIGVzdMOpIHNlc2dhZG8uIEVsIG1vZGVsbyBlbGVnaWRvIGVzDQplZmVjdG9zIGFsZWF0b3Jpb3MgKHJhbmRvbSkuDQoNCiMgUGFzbyA0OiBwcmVkaWNjaW9uZXMgMjAyNC0yMDI2DQoNClJhbmRvbSBlZmZlY3RzIHRpZW5lIHVuIHNvbG8gaW50ZXJjZXB0byB5IHVuYSBzb2xhIHBlbmRpZW50ZSBwYXJhIGxvcyB0cmVzDQpwYcOtc2VzLCBhc8OtIHF1ZSBwcm9ub3N0aWNhciBlcyB0YW4gc2ltcGxlIGNvbW8gZW4gZWwgZWplbXBsbyBvcmlnaW5hbCBkZQ0KY2xhc2U6IGludGVyY2VwdG8gKyBwZW5kaWVudGUgKiBpbmZsYWNpw7NuLiBFbCDDum5pY28gc3VwdWVzdG8gcXVlDQpuZWNlc2l0YW1vcyBlcyB1biB2YWxvciBkZSBpbmZsYWNpw7NuIGZ1dHVyYSBwb3IgcGHDrXM7IHVzYW1vcyBlbCDDumx0aW1vIGRhdG8NCm9ic2VydmFkbyAoMjAyMykgY29tbyBlc2NlbmFyaW8gInNpIGxhIGluZmxhY2nDs24gc2UgbWFudGllbmUgaWd1YWwiLg0KDQpgYGB7ciBwcm9ub3N0aWNvLWJtfQ0KaW50ZXJjZXB0b19ibSA8LSBjb2VmKHJhbmRvbV9ibSlbIihJbnRlcmNlcHQpIl0NCnBlbmRpZW50ZV9ibSAgPC0gY29lZihyYW5kb21fYm0pWyJpbmZsYWNpb25fcGN0Il0NCg0KaW5mbGFjaW9uXzIwMjMgPC0gZGF0b3NfYm0gJT4lIGZpbHRlcihhbmlvID09IDIwMjMpICU+JSBzZWxlY3QocGFpcywgaW5mbGFjaW9uX3BjdCkNCg0KcHJvbm9zdGljb19ibSA8LSBleHBhbmQuZ3JpZChwYWlzID0gdW5pcXVlKGRhdG9zX2JtJHBhaXMpLCBhbmlvID0gMjAyNDoyMDI2KSAlPiUNCiAgbGVmdF9qb2luKGluZmxhY2lvbl8yMDIzLCBieSA9ICJwYWlzIikgJT4lDQogIG11dGF0ZShnZHBfcGVyY2FwaXRhX3Byb25vc3RpY28gPSBpbnRlcmNlcHRvX2JtICsgcGVuZGllbnRlX2JtICogaW5mbGFjaW9uX3BjdCkNCg0Ka25pdHI6OmthYmxlKHByb25vc3RpY29fYm0gJT4lIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBhbmlvLCB2YWx1ZXNfZnJvbSA9IGdkcF9wZXJjYXBpdGFfcHJvbm9zdGljbywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkX2NvbHMgPSBwYWlzKSwNCiAgICAgICAgICAgICBkaWdpdHMgPSAwLCBjYXB0aW9uID0gIlBJQiBwZXIgY8OhcGl0YSBwcm9ub3N0aWNhZG8gMjAyNC0yMDI2IChVUyQsIHN1cG9uZSBpbmZsYWNpw7NuID0gbGEgZGUgMjAyMykiKQ0KYGBgDQoNCkNvbW8gZWwgbW9kZWxvIGRlIHJhbmRvbSBlZmZlY3RzIHNvbG8gdXNhIGxhIGluZmxhY2nDs24gcGFyYSBleHBsaWNhciBlbCBQSUINCnBlciBjw6FwaXRhIHkgbGUgcGVnYSBlbCBtaXNtbyBpbnRlcmNlcHRvL3BlbmRpZW50ZSBhIGxvcyB0cmVzIHBhw61zZXMsIGVsDQpwcm9uw7NzdGljbyAyMDI0LTIwMjYgbm8gbXVldmUgbXVjaG8gZWwgbml2ZWwgZGUgY2FkYSBwYcOtcyBmcmVudGUgYSBzdSB2YWxvcg0KZGUgMjAyMywgeWEgcXVlIGVsIG1vZGVsbyBubyB0aWVuZSBmb3JtYSBkZSAic2FiZXIiIHF1ZSBDaGluYSBoYSB2ZW5pZG8NCmNyZWNpZW5kbyBkZSBmb3JtYSBzb3N0ZW5pZGEgc2kgZXNhIHRlbmRlbmNpYSBubyBwYXNhIHBvciBsYSBpbmZsYWNpw7NuOyBlc3RvDQplcyB1bmEgbGltaXRhY2nDs24gcmVhbCBkZWwgbW9kZWxvIHkgdmFsZSBsYSBwZW5hIGRlY2lybGEsIG5vIGVzY29uZGVybGEuDQoNCiMgQ29uY2x1c2lvbmVzDQoNCkVsIFBJQiBwZXIgY8OhcGl0YSBkZSBDaGluYSwgSmFww7NuIHkgQ29yZWEgZGVsIFN1ciB0aWVuZSBkaWZlcmVuY2lhcyBkZSBuaXZlbA0KdGFuIGdyYW5kZXMgcXVlIHVuIG1vZGVsbyBwb29sZWQsIHF1ZSBpZ25vcmEgbGEgaWRlbnRpZGFkIGRlbCBwYcOtcywgcXVlZGENCmNsYXJhbWVudGUgcmVjaGF6YWRvIHBvciBsYSBwcnVlYmEgRiAocCA8IDAuMDAxKS4gQSBkaWZlcmVuY2lhIGRlbCBlamVyY2ljaW8NCmRlIHBhdGVudGVzLCBhcXXDrSBsYSBwcnVlYmEgZGUgSGF1c21hbiAocCA9IDAuMTgpIG5vIHJlY2hhemEgZWZlY3Rvcw0KYWxlYXRvcmlvcywgYXPDrSBxdWUgZWwgbW9kZWxvIGVsZWdpZG8gZXMgcmFuZG9tIGVmZmVjdHMsIG3DoXMgZWZpY2llbnRlIHF1ZQ0KZWZlY3RvcyBmaWpvcyBwb3JxdWUgYXByb3ZlY2hhIHRhbWJpw6luIGxhIHZhcmlhY2nDs24gZW50cmUgcGHDrXNlcyB5IG5vIHNvbG8NCmRlbnRybyBkZSBjYWRhIHBhw61zIGVuIGVsIHRpZW1wby4gTGEgcmVsYWNpw7NuIGVzdGltYWRhIGVudHJlIGluZmxhY2nDs24geSBQSUINCnBlciBjw6FwaXRhIGVzIG5lZ2F0aXZhIHkgc2lnbmlmaWNhdGl2YSBlbiBsb3MgdHJlcyBtb2RlbG9zOiBjYWRhIHB1bnRvDQpwb3JjZW50dWFsIGFkaWNpb25hbCBkZSBpbmZsYWNpw7NuIHNlIGFzb2NpYSBjb24sIGFwcm94aW1hZGFtZW50ZSwgVVMkOTQ2DQptZW5vcyBkZSBQSUIgcGVyIGPDoXBpdGEgc2Vnw7puIGVsIG1vZGVsbyBkZSByYW5kb20gZWZmZWN0cy4gRWwgYWp1c3RlIGRlbA0KbW9kZWxvIGVzIG1vZGVzdG8gKFLCsiDiiYggMC4xOSksIGxvIHF1ZSB0aWVuZSBzZW50aWRvIHBvcnF1ZSBlbCBkZXNhcnJvbGxvDQplY29uw7NtaWNvIGRlcGVuZGUgZGUgbXVjaG9zIGZhY3RvcmVzIG3DoXMgKGNhcGl0YWwgaHVtYW5vLCBpbnN0aXR1Y2lvbmVzLA0KY29tZXJjaW8sIHRlY25vbG9nw61hKSBxdWUgbm8gZXN0w6FuIGVuIGVzdGUgbW9kZWxvIGRlIHVuYSBzb2xhIHZhcmlhYmxlLCB5IGVsDQpwcm9uw7NzdGljbyAyMDI0LTIwMjYgaGVyZWRhIGVzYSBtaXNtYSBsaW1pdGFjacOzbjogY29tbyBlbCAic2hvY2siIGRlDQppbmZsYWNpw7NuIG5vIGNhbWJpYSBtdWNobyBkZSB1biBhw7FvIGEgb3RybywgZWwgcHJvbsOzc3RpY28gdGFtcG9jbyBzZSBtdWV2ZQ0KbXVjaG8sIGxvIGN1YWwgbm8gZXMgdW4gZXJyb3IgZGVsIGPDs2RpZ28gc2lubyB1bmEgY29uc2VjdWVuY2lhIGVzcGVyYWJsZSBkZQ0KaGFiZXIgdXNhZG8gc29sbyB1bmEgdmFyaWFibGUgZXhwbGljYXRpdmEuIFBvciDDumx0aW1vLCBlc3RhIGNvcnJlbGFjacOzbg0KbmVnYXRpdmEgZW50cmUgaW5mbGFjacOzbiB5IFBJQiBwZXIgY8OhcGl0YSBubyBkZWJlIGxlZXJzZSBjb21vIHF1ZSAiYmFqYXIgbGENCmluZmxhY2nDs24gZ2FyYW50aXphIiB1biBtYXlvciBQSUIgcGVyIGPDoXBpdGE6IGNvbiAzIHBhw61zZXMgeSB1bmEgc29sYQ0KdmFyaWFibGUsIGVzIGV2aWRlbmNpYSBzdWdlc3RpdmEsIG5vIHVuYSBwcnVlYmEgY2F1c2FsLg0K