library(zoo)
library(tseries)
library(lmtest)
library(sandwich)
library(car)
rm(list = ls())

V tejto úlohe budeme pomocou regresie skúmať, nakoľko je hodnotenie filmu podľa IMDb ovplyvnené:

Na to použijeme databázu najlepšie hodnotených filmov z portálu IMDb, ktorá obsahuje napríklad umiestnenie v rebríčku, názov filmu, rok, žáner, hodnotenie IMDb, dĺžku, režiséra, štúdio a rozpočet.

#######################################################################
# PRÍPRAVA ÚDAJOV
#######################################################################

# načítanie dát (oddelené bodkočiarkou, desatinná čiarka)
udaje_raw <- read.csv("data_ekonometria.csv",
                      sep = ";",
                      dec = ",",
                      header = TRUE,
                      check.names = FALSE)

# pozrime si názvy stĺpcov
names(udaje_raw)
[1] "Umiestnenie"     "Názov"           "Rok"             "Žáner"          
[5] "IMDb hodnotenie" "Dĺžka (min)"     "Réžia"           "Spoločnosť"     
[9] "Rozpočet [$]"   
# vytvoríme si jednoduchšie (bez diakritiky) a numerické premenné
udaje <- udaje_raw

# IMDb hodnotenie
udaje$imdb <- udaje[["IMDb hodnotenie"]]

# dĺžka filmu v minútach
udaje$dlzka <- udaje[["Dĺžka (min)"]]

# rok uvedenia
udaje$rok <- udaje[["Rok"]]

# rozpočet – je v texte (medzery a znak $), preto ho musíme očistiť a prekonvertovať na číslo
udaje$rozpocet <- as.numeric(gsub(" |\\$|,", "", udaje[["Rozpočet [$]"]]))

# vyberieme len premenné, ktoré budeme používať v modeli
udaje <- udaje[, c("imdb", "dlzka", "rok", "rozpocet")]

summary(udaje)
      imdb           dlzka            rok          rozpocet        
 Min.   :8.000   Min.   : 45.0   Min.   :1921   Min.   :    12000  
 1st Qu.:8.100   1st Qu.:110.0   1st Qu.:1966   1st Qu.:  2500000  
 Median :8.200   Median :128.0   Median :1994   Median : 13000000  
 Mean   :8.294   Mean   :130.4   Mean   :1986   Mean   : 35923313  
 3rd Qu.:8.400   3rd Qu.:148.0   3rd Qu.:2006   3rd Qu.: 35000000  
 Max.   :9.300   Max.   :238.0   Max.   :2022   Max.   :400000000  

Teraz máme pripravenú menšiu databázu, kde každý riadok predstavuje film a premenné:

Môže sa stať, že niektoré filmy nebudú mať uvedený rozpočet; tieto chýbajúce hodnoty nahradíme mediánmi.

# IMPUTÁCIA CHÝBAJÚCICH HODNÔT

# vypočítame mediány pre jednotlivé stĺpce (ignorujeme NA)
column_medians <- sapply(udaje, median, na.rm = TRUE)

# nahradíme chýbajúce hodnoty mediánom danej premennej
udaje_imputed <- udaje
for (col in names(udaje_imputed)) {
  udaje_imputed[[col]][is.na(udaje_imputed[[col]])] <- column_medians[col]
}

udaje <- udaje_imputed
summary(udaje)
      imdb           dlzka            rok          rozpocet        
 Min.   :8.000   Min.   : 45.0   Min.   :1921   Min.   :    12000  
 1st Qu.:8.100   1st Qu.:110.0   1st Qu.:1966   1st Qu.:  2500000  
 Median :8.200   Median :128.0   Median :1994   Median : 13000000  
 Mean   :8.294   Mean   :130.4   Mean   :1986   Mean   : 35923313  
 3rd Qu.:8.400   3rd Qu.:148.0   3rd Qu.:2006   3rd Qu.: 35000000  
 Max.   :9.300   Max.   :238.0   Max.   :2022   Max.   :400000000  

1. Základná regresia

Základný model bude mať tvar:

Formálne:

\[ \text{imdb}_i = \beta_0 + \beta_1 \text{dlzka}_i + \beta_2 \text{rok}_i + \beta_3 \text{rozpocet}_i + u_i \]

################################################################################
# ZÁKLADNÁ REGRESIA
################################################################################
attach(udaje)

model <- lm(imdb ~ 1 + dlzka + rok + rozpocet, data = udaje)
summary(model)

Call:
lm(formula = imdb ~ 1 + dlzka + rok + rozpocet, data = udaje)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.42001 -0.15545 -0.05865  0.13091  0.98723 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  8.678e+00  1.238e+00   7.010 2.28e-11 ***
dlzka        2.126e-03  4.689e-04   4.535 9.00e-06 ***
rok         -3.391e-04  6.267e-04  -0.541    0.589    
rozpocet     3.424e-10  2.624e-10   1.305    0.193    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2218 on 246 degrees of freedom
Multiple R-squared:  0.08716,   Adjusted R-squared:  0.07603 
F-statistic: 7.829 on 3 and 246 DF,  p-value: 5.186e-05

Interpretácia tohto modelu: snažíme sa zistiť, či sú filmy s vyšším rozpočtom, dlhšou stopážou a novším rokom uvedenia na IMDb hodnotené systematicky inak (lepšie alebo horšie).

2. Test správnej funkčnej formy – Ramsey RESET test

Chceme otestovať, či lineárna špecifikácia je pre naše dáta (hodnotenie filmov) vhodná, alebo či by sme mali napríklad zaviesť kvadratické členy, logaritmy a podobne.

Myšlienka: nech pôvodný model má tvar

\[ \text{imdb}_i = \beta_0 + \beta_1 \text{dlzka}_i + \beta_2 \text{rok}_i + \beta_3 \text{rozpočet}_i + u_i \]

Ak je model správne špecifikovaný, potom pridaním mocnín vyrovnaných hodnôt (\(\hat y^2, \hat y^3\)) sa model podstatne nezlepší.

Budeme testovať:

{r # RESET test resettest(model)

Interpretácia:

3. Grafická analýza

3.1 Graf Residuals vs. Fitted

Pozrieme sa na vzťah medzi vyrovnanými hodnotami IMDb hodnotenia a rezíduami.

plot(model, which = 1)

Ak rezíduá vykazujú systematický (nezhlukovaný) vzor – napríklad zakrivenie –, môže to znamenať, že model závislosti IMDb hodnotenia na dĺžke, roku a rozpočte nie je úplne lineárny. Vtedy môže pomôcť transformácia niektorej premennej (napr. zavedenie kvadratického člena) alebo doplnenie novej premennej (napr. dummy pre „staršie“/„novšie“ filmy).

3.2 C+R grafy (Component + Residual plots)

C+R grafy nám pomôžu zisťovať, či je vzťah medzi IMDb hodnotením a jednotlivými vysvetľujúcimi premennými lineárny, alebo zakrivený.

car::crPlots(model)

Ak sa krivka výrazne odchyľuje od priamky, zvážime transformáciu danej premennej (napr. kvadratický člen, logaritmus, odmocnina). Predstavme si, že najväčší odklon od linearity pozorujeme najmä pri premennej dĺžka filmu (dlzka) a možno aj pri rozpočte (rozpocet).

4. Nelineárna špecifikácia – kvadratické členy

Zložitejšie nelineárne vzťahy môžeme približovať polynómom, napr. zavedením kvadrátu vybraných vysvetľujúcich premenných.

Predpokladajme, že sa rozhodneme zaviesť kvadrát premennej dĺžka a rozpočet, lebo C+R grafy naznačujú zakrivenie ich vzťahu k IMDb hodnoteniu.

model <- lm(imdb ~ 1 + dlzka + rok + rozpocet, data = udaje)

model_kvadr <- lm(imdb ~ 1 + dlzka + rok + rozpocet +
                    I(dlzka^2) + I(rozpocet^2),
                  data = udaje)

summary(model_kvadr)

Call:
lm(formula = imdb ~ 1 + dlzka + rok + rozpocet + I(dlzka^2) + 
    I(rozpocet^2), data = udaje)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.41110 -0.15480 -0.05242  0.11408  0.98026 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)    9.245e+00  1.308e+00   7.066 1.66e-11 ***
dlzka          4.049e-03  3.010e-03   1.345   0.1799    
rok           -6.942e-04  6.844e-04  -1.014   0.3114    
rozpocet       9.925e-10  5.942e-10   1.670   0.0961 .  
I(dlzka^2)    -6.907e-06  1.062e-05  -0.650   0.5160    
I(rozpocet^2) -2.352e-18  1.967e-18  -1.196   0.2330    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2218 on 244 degrees of freedom
Multiple R-squared:  0.09387,   Adjusted R-squared:  0.07531 
F-statistic: 5.056 on 5 and 244 DF,  p-value: 0.0001988
# porovnanie lineárneho a kvadratického modelu
anova(model, model_kvadr)
Analysis of Variance Table

Model 1: imdb ~ 1 + dlzka + rok + rozpocet
Model 2: imdb ~ 1 + dlzka + rok + rozpocet + I(dlzka^2) + I(rozpocet^2)
  Res.Df    RSS Df Sum of Sq      F Pr(>F)
1    246 12.097                           
2    244 12.008  2  0.088998 0.9042 0.4062
# RESET test pre kvadratický model
resettest(model_kvadr)

    RESET test

data:  model_kvadr
RESET = 2.5842, df1 = 2, df2 = 242, p-value = 0.07754

Ak má transformovaný model vyšší upravený koeficient determinácie \(R^2_{adj}\) a zároveň sa zlepší aj výsledok RESET testu, môžeme konštatovať, že kvadratické členy pomohli lepšie vystihnúť vzťah medzi hodnotením filmu a jeho dĺžkou/rozpočtom.

Ak sa ukáže, že niektoré kvadratické členy sú štatisticky nevýznamné, môžeme ich postupne vylučovať a zvoliť „úspornejší“ model (bez zbytočných členov).

5. Rozšírený model s nelineárnymi členmi

Skúsme model, kde pre všetky tri vysvetľujúce premenné (dĺžka, rok, rozpočet) zavedie­me aj kvadratické členy a následne vyhodnotíme ich štatistickú významnosť:

model_rozsireny <- lm(imdb ~ dlzka + rok + rozpocet +
                        I(dlzka^2) + I(rok^2) + I(rozpocet^2),
                      data = udaje)
summary(model_rozsireny)

Call:
lm(formula = imdb ~ dlzka + rok + rozpocet + I(dlzka^2) + I(rok^2) + 
    I(rozpocet^2), data = udaje)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.43993 -0.14709 -0.05205  0.12665  0.96420 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)  
(Intercept)   -2.064e+02  9.073e+01  -2.275   0.0238 *
dlzka          2.905e-03  3.020e-03   0.962   0.3370  
rok            2.176e-01  9.183e-02   2.369   0.0186 *
rozpocet       1.108e-09  5.906e-10   1.876   0.0618 .
I(dlzka^2)    -3.559e-06  1.061e-05  -0.335   0.7376  
I(rok^2)      -5.520e-05  2.322e-05  -2.377   0.0182 *
I(rozpocet^2) -2.186e-18  1.950e-18  -1.121   0.2632  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2198 on 243 degrees of freedom
Multiple R-squared:  0.1145,    Adjusted R-squared:  0.0926 
F-statistic: 5.235 on 6 and 243 DF,  p-value: 4.33e-05
anova(model, model_rozsireny)
Analysis of Variance Table

Model 1: imdb ~ 1 + dlzka + rok + rozpocet
Model 2: imdb ~ dlzka + rok + rozpocet + I(dlzka^2) + I(rok^2) + I(rozpocet^2)
  Res.Df    RSS Df Sum of Sq      F  Pr(>F)  
1    246 12.097                              
2    243 11.735  3   0.36183 2.4974 0.06034 .
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
resettest(model_rozsireny)

    RESET test

data:  model_rozsireny
RESET = 4.0892, df1 = 2, df2 = 241, p-value = 0.01793

Interpretácia:

6. Dummy premenná a lineárna lomená funkcia

Predpokladajme teraz, že C+R graf pre premennú dĺžka filmu (dlzka) naznačuje, že približne pri 150 minútach nastáva zmena trendu – kratšie filmy sa správajú inak ako veľmi dlhé filmy.

Zavedieme preto dummy premennú:

\[ DUM_i = \begin{cases} 0, & \text{ak } \text{dlzka}_i < 150 \\ 1, & \text{ak } \text{dlzka}_i \ge 150 \end{cases} \]

Túto dummy možno využiť:

  1. na zlom v autonómnom (konštantnom) člene,
  2. na zlom v sklone voči premenným (napr. voči dĺžke).

6.1 Zlom v autonómnom člene

udaje$DUM <- ifelse(udaje$dlzka < 150, 0, 1)

modelD_auto <- lm(imdb ~ 1 + DUM + dlzka + rok + rozpocet,
                  data = udaje)
summary(modelD_auto)

Call:
lm(formula = imdb ~ 1 + DUM + dlzka + rok + rozpocet, data = udaje)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.42320 -0.15874 -0.06193  0.12451  0.99780 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  8.670e+00  1.240e+00   6.993 2.54e-11 ***
DUM          3.008e-02  5.289e-02   0.569   0.5701    
dlzka        1.799e-03  7.436e-04   2.419   0.0163 *  
rok         -3.168e-04  6.288e-04  -0.504   0.6148    
rozpocet     3.382e-10  2.629e-10   1.287   0.1995    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2221 on 245 degrees of freedom
Multiple R-squared:  0.08836,   Adjusted R-squared:  0.07348 
F-statistic: 5.937 on 4 and 245 DF,  p-value: 0.0001416

Tu testujeme, či sa pri prechode z kratších na dlhé filmy (nad 150 minút) posunie priemerné IMDb hodnotenie o nejakú konštantnú hodnotu (bez zmeny sklonu).

6.2 Zlom v sklone (lineárna lomená funkcia)

Teraz vytvoríme model so zlomeným sklonom voči dĺžke – čiže iný vplyv dĺžky na hodnotenie pri kratších a pri dlhších filmoch:

modelD_sklon <- lm(imdb ~ 1 + dlzka + I(DUM * dlzka) + rok + rozpocet,
                   data = udaje)
summary(modelD_sklon)

Call:
lm(formula = imdb ~ 1 + dlzka + I(DUM * dlzka) + rok + rozpocet, 
    data = udaje)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.42400 -0.15910 -0.06235  0.12705  0.99543 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)     8.654e+00  1.242e+00   6.970 2.92e-11 ***
dlzka           1.861e-03  8.020e-04   2.320   0.0212 *  
I(DUM * dlzka)  1.337e-04  3.269e-04   0.409   0.6829    
rok            -3.118e-04  6.313e-04  -0.494   0.6218    
rozpocet        3.388e-10  2.630e-10   1.288   0.1989    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2221 on 245 degrees of freedom
Multiple R-squared:  0.08778,   Adjusted R-squared:  0.07289 
F-statistic: 5.894 on 4 and 245 DF,  p-value: 0.0001522

Interpretácia:

  • koeficient pri dlzka vyjadruje vplyv dĺžky na IMDb hodnotenie pre kratšie filmy (kde DUM = 0),
  • súčet koeficientov dlzka + I(DUM * dlzka) vyjadruje vplyv dĺžky na hodnotenie pre dlhšie filmy (DUM = 1).

Porovnanie pôvodného lineárneho modelu a modelu so zlomeným sklonom urobíme pomocou ANOVA a RESET testu:

anova(model, modelD_sklon)
Analysis of Variance Table

Model 1: imdb ~ 1 + dlzka + rok + rozpocet
Model 2: imdb ~ 1 + dlzka + I(DUM * dlzka) + rok + rozpocet
  Res.Df    RSS Df Sum of Sq      F Pr(>F)
1    246 12.097                           
2    245 12.089  1 0.0082517 0.1672 0.6829
resettest(modelD_sklon)

    RESET test

data:  modelD_sklon
RESET = 0.60852, df1 = 2, df2 = 243, p-value = 0.545

Ak ANOVA test ukáže, že model so zlomom v sklone je štatisticky lepší, môžeme konštatovať, že dĺžka filmu ovplyvňuje hodnotenie IMDb inak pri kratších filmoch a inak pri veľmi dlhých.

7. Box–Coxov transformačný test (voliteľné)

Box–Coxova transformácia sa používa na systematické hľadanie vhodnej transformácie závislej premenné, v našom prípade IMDb hodnotenia filmu.

\[ Y_i^{(\lambda)} = \begin{cases} \dfrac{Y_i^{\lambda} - 1}{\lambda}, & \text{ak } \lambda \neq 0, \\[10pt] \ln(Y_i), & \text{ak } \lambda = 0. \end{cases} \]

library(MASS)
boxcox(model)

Z grafu Box–Cox vyhodnotíme, ktorá hodnota \(\lambda\) by mohla byť vhodná. Podľa toho:

Pre ilustráciu si vyskúšajme nejakú vybranú hodnotu \(\lambda\) (napríklad \(\lambda = 1.8\)) a znova odhadnime model:

lambda <- 1.8

model_lambda <- lm(I((imdb^lambda - 1) / lambda) ~ 1 + dlzka + rok + rozpocet,
                   data = udaje)
summary(model_lambda)

Call:
lm(formula = I((imdb^lambda - 1)/lambda) ~ 1 + dlzka + rok + 
    rozpocet, data = udaje)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.2858 -0.8483 -0.3328  0.7056  5.6102 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  2.658e+01  6.836e+00   3.889  0.00013 ***
dlzka        1.177e-02  2.589e-03   4.544 8.66e-06 ***
rok         -1.859e-03  3.461e-03  -0.537  0.59160    
rozpocet     1.869e-09  1.449e-09   1.290  0.19843    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.225 on 246 degrees of freedom
Multiple R-squared:  0.08726,   Adjusted R-squared:  0.07613 
F-statistic: 7.839 on 3 and 246 DF,  p-value: 5.118e-05
resettest(model_lambda)

    RESET test

data:  model_lambda
RESET = 0.7661, df1 = 2, df2 = 244, p-value = 0.4659
# anova porovnanie s pôvodným modelom nemá zmysel, pretože sme zmenili vysvetľovanú premennú

Ak by transformácia výraznejšie zlepšila vlastnosti modelu (vyšší \(R^2_{adj}\), lepší RESET test), bolo by možné ju ďalej používať. Zároveň však strácame priamočiaru interpretáciu „o koľko bodov IMDb sa zmení hodnotenie“, preto je pri praktickej interpretácii často výhodnejšie zostať pri pôvodnej mierke IMDb a nelinearity riešiť napríklad kvadratickými členmi alebo lomenými funkciami.

LS0tCnRpdGxlOiAixaBwZWNpZmlrw6FjaWEgbW9kZWx1IOKAkyBob2Rub3RlbmllIGZpbG1vdiIKb3V0cHV0OiBodG1sX25vdGVib29rCmF1dGhvcjogIkRpYW5hIMSOdXJpYW7EjWlrb3bDoSIKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KHpvbykKbGlicmFyeSh0c2VyaWVzKQpsaWJyYXJ5KGxtdGVzdCkKbGlicmFyeShzYW5kd2ljaCkKbGlicmFyeShjYXIpCnJtKGxpc3QgPSBscygpKQpgYGAKClYgdGVqdG8gw7psb2hlIGJ1ZGVtZSBwb21vY291IHJlZ3Jlc2llIHNrw7ptYcWlLCBuYWtvxL5rbyBqZSAqKmhvZG5vdGVuaWUgZmlsbXUgcG9kxL5hIElNRGIqKiBvdnBseXZuZW7DqToKCi0gKipkxLrFvmtvdSBmaWxtdSoqIHYgbWluw7p0YWNoLCAgCi0gKipyb2tvbSB1dmVkZW5pYSoqIChzdGFyxaFpZSB2cy4gbm92xaFpZSBmaWxteSksICAKLSAqKnJvenBvxI10b20gZmlsbXUqKi4KCk5hIHRvIHBvdcW+aWplbWUgZGF0YWLDoXp1IG5hamxlcMWhaWUgaG9kbm90ZW7DvWNoIGZpbG1vdiB6IHBvcnTDoWx1IElNRGIsIGt0b3LDoSBvYnNhaHVqZSBuYXByw61rbGFkIHVtaWVzdG5lbmllIHYgcmVicsOtxI1rdSwgbsOhem92IGZpbG11LCByb2ssIMW+w6FuZXIsIGhvZG5vdGVuaWUgSU1EYiwgZMS6xb5rdSwgcmXFvmlzw6lyYSwgxaF0w7pkaW8gYSByb3pwb8SNZXQuCgpgYGB7cn0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBQUsONUFJBVkEgw5pEQUpPVgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBuYcSNw610YW5pZSBkw6F0IChvZGRlbGVuw6kgYm9ka2/EjWlhcmtvdSwgZGVzYXRpbm7DoSDEjWlhcmthKQp1ZGFqZV9yYXcgPC0gcmVhZC5jc3YoImRhdGFfZWtvbm9tZXRyaWEuY3N2IiwKICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICI7IiwKICAgICAgICAgICAgICAgICAgICAgIGRlYyA9ICIsIiwKICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEZBTFNFKQoKIyBwb3pyaW1lIHNpIG7DoXp2eSBzdMS6cGNvdgpuYW1lcyh1ZGFqZV9yYXcpCgojIHZ5dHZvcsOtbWUgc2kgamVkbm9kdWNoxaFpZSAoYmV6IGRpYWtyaXRpa3kpIGEgbnVtZXJpY2vDqSBwcmVtZW5uw6kKdWRhamUgPC0gdWRhamVfcmF3CgojIElNRGIgaG9kbm90ZW5pZQp1ZGFqZSRpbWRiIDwtIHVkYWplW1siSU1EYiBob2Rub3RlbmllIl1dCgojIGTEusW+a2EgZmlsbXUgdiBtaW7DunRhY2gKdWRhamUkZGx6a2EgPC0gdWRhamVbWyJExLrFvmthIChtaW4pIl1dCgojIHJvayB1dmVkZW5pYQp1ZGFqZSRyb2sgPC0gdWRhamVbWyJSb2siXV0KCiMgcm96cG/EjWV0IOKAkyBqZSB2IHRleHRlIChtZWR6ZXJ5IGEgem5hayAkKSwgcHJldG8gaG8gbXVzw61tZSBvxI1pc3RpxaUgYSBwcmVrb252ZXJ0b3ZhxaUgbmEgxI3DrXNsbwp1ZGFqZSRyb3pwb2NldCA8LSBhcy5udW1lcmljKGdzdWIoIiB8XFwkfCwiLCAiIiwgdWRhamVbWyJSb3pwb8SNZXQgWyRdIl1dKSkKCiMgdnliZXJpZW1lIGxlbiBwcmVtZW5uw6ksIGt0b3LDqSBidWRlbWUgcG91xb7DrXZhxaUgdiBtb2RlbGkKdWRhamUgPC0gdWRhamVbLCBjKCJpbWRiIiwgImRsemthIiwgInJvayIsICJyb3pwb2NldCIpXQoKc3VtbWFyeSh1ZGFqZSkKYGBgCgpUZXJheiBtw6FtZSBwcmlwcmF2ZW7DuiBtZW7FoWl1IGRhdGFiw6F6dSwga2RlIGthxb5kw70gcmlhZG9rIHByZWRzdGF2dWplIGZpbG0gYSBwcmVtZW5uw6k6CgotIGBpbWRiYCDigJMgaG9kbm90ZW5pZSBmaWxtdSBuYSBJTURiICh6w6F2aXNsw6EgcHJlbWVubsOhKSwgIAotIGBkbHprYWAg4oCTIGTEusW+a2EgZmlsbXUgdiBtaW7DunRhY2gsICAKLSBgcm9rYCDigJMgcm9rIHV2ZWRlbmlhIGZpbG11LCAgCi0gYHJvenBvY2V0YCDigJMgcm96cG/EjWV0IGZpbG11IHYgVVNELgoKTcO0xb5lIHNhIHN0YcWlLCDFvmUgbmlla3RvcsOpIGZpbG15IG5lYnVkw7ogbWHFpSB1dmVkZW7DvSByb3pwb8SNZXQ7IHRpZXRvIGNow71iYWrDumNlIGhvZG5vdHkgbmFocmFkw61tZSBtZWRpw6FubWkuCgpgYGB7cn0KIyBJTVBVVMOBQ0lBIENIw51CQUrDmkNJQ0ggSE9ETsOUVAoKIyB2eXBvxI3DrXRhbWUgbWVkacOhbnkgcHJlIGplZG5vdGxpdsOpIHN0xLpwY2UgKGlnbm9ydWplbWUgTkEpCmNvbHVtbl9tZWRpYW5zIDwtIHNhcHBseSh1ZGFqZSwgbWVkaWFuLCBuYS5ybSA9IFRSVUUpCgojIG5haHJhZMOtbWUgY2jDvWJhasO6Y2UgaG9kbm90eSBtZWRpw6Fub20gZGFuZWogcHJlbWVubmVqCnVkYWplX2ltcHV0ZWQgPC0gdWRhamUKZm9yIChjb2wgaW4gbmFtZXModWRhamVfaW1wdXRlZCkpIHsKICB1ZGFqZV9pbXB1dGVkW1tjb2xdXVtpcy5uYSh1ZGFqZV9pbXB1dGVkW1tjb2xdXSldIDwtIGNvbHVtbl9tZWRpYW5zW2NvbF0KfQoKdWRhamUgPC0gdWRhamVfaW1wdXRlZApzdW1tYXJ5KHVkYWplKQpgYGAKCiMjIDEuIFrDoWtsYWRuw6EgcmVncmVzaWEKClrDoWtsYWRuw70gbW9kZWwgYnVkZSBtYcWlIHR2YXI6CgotIHZ5c3ZldMS+b3ZhbsOhIHByZW1lbm7DoTogKipJTURiIGhvZG5vdGVuaWUgZmlsbXUgKGBpbWRiYCkqKiwKLSB2eXN2ZXTEvnVqw7pjZSBwcmVtZW5uw6k6ICoqZMS6xb5rYSBmaWxtdSAoYGRsemthYCkqKiwgKipyb2sgdXZlZGVuaWEgKGByb2tgKSoqLCAqKnJvenBvxI1ldCAoYHJvenBvY2V0YCkqKi4KCkZvcm3DoWxuZToKClxbClx0ZXh0e2ltZGJ9X2kgPSBcYmV0YV8wICsgXGJldGFfMSBcdGV4dHtkbHprYX1faSArIFxiZXRhXzIgXHRleHR7cm9rfV9pICsgXGJldGFfMyBcdGV4dHtyb3pwb2NldH1faSArIHVfaQpcXQoKYGBge3J9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgWsOBS0xBRE7DgSBSRUdSRVNJQQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwphdHRhY2godWRhamUpCgptb2RlbCA8LSBsbShpbWRiIH4gMSArIGRsemthICsgcm9rICsgcm96cG9jZXQsIGRhdGEgPSB1ZGFqZSkKc3VtbWFyeShtb2RlbCkKYGBgCgpJbnRlcnByZXTDoWNpYSB0b2h0byBtb2RlbHU6IHNuYcW+w61tZSBzYSB6aXN0acWlLCDEjWkgc8O6IGZpbG15IHMgdnnFocWhw61tIHJvenBvxI10b20sIGRsaMWhb3Ugc3RvcMOhxb5vdSBhIG5vdsWhw61tIHJva29tIHV2ZWRlbmlhIG5hIElNRGIgaG9kbm90ZW7DqSBzeXN0ZW1hdGlja3kgaW5hayAobGVwxaFpZSBhbGVibyBob3LFoWllKS4KCiMjIDIuIFRlc3Qgc3Byw6F2bmVqIGZ1bmvEjW5laiBmb3JteSDigJMgUmFtc2V5IFJFU0VUIHRlc3QKCkNoY2VtZSBvdGVzdG92YcWlLCDEjWkgbGluZcOhcm5hIMWhcGVjaWZpa8OhY2lhIGplIHByZSBuYcWhZSBkw6F0YSAoaG9kbm90ZW5pZSBmaWxtb3YpIHZob2Ruw6EsIGFsZWJvIMSNaSBieSBzbWUgbWFsaSBuYXByw61rbGFkIHphdmllc8WlIGt2YWRyYXRpY2vDqSDEjWxlbnksIGxvZ2FyaXRteSBhIHBvZG9ibmUuCgpNecWhbGllbmthOiBuZWNoIHDDtHZvZG7DvSBtb2RlbCBtw6EgdHZhcgoKXFsKXHRleHR7aW1kYn1faSA9IFxiZXRhXzAgKyBcYmV0YV8xIFx0ZXh0e2RsemthfV9pICsgXGJldGFfMiBcdGV4dHtyb2t9X2kgKyBcYmV0YV8zIFx0ZXh0e3JvenBvxI1ldH1faSArIHVfaQpcXQoKQWsgamUgbW9kZWwgc3Byw6F2bmUgxaFwZWNpZmlrb3ZhbsO9LCBwb3RvbSBwcmlkYW7DrW0gbW9jbsOtbiB2eXJvdm5hbsO9Y2ggaG9kbsO0dCAoXChcaGF0IHleMiwgXGhhdCB5XjNcKSkgc2EgbW9kZWwgKipwb2RzdGF0bmUgbmV6bGVwxaHDrSoqLgoKQnVkZW1lIHRlc3RvdmHFpToKCi0gXChIXzA6XCkgbW9kZWwgamUgc3Byw6F2bmUgxaFwZWNpZmlrb3ZhbsO9LCAgCi0gXChIXzE6XCkgbW9kZWwgamUgxaFwZWNpZmlrb3ZhbsO9IG5lc3Byw6F2bmUgKGNow71iYWrDuiBkw7RsZcW+aXTDqSBuZWxpbmXDoXJuZSBhbGVibyBpbsOpIHByZW1lbm7DqSkuCgpgYGB7cgojIFJFU0VUIHRlc3QKcmVzZXR0ZXN0KG1vZGVsKQpgYGAKCioqSW50ZXJwcmV0w6FjaWE6KioKCi0gQWsgcC1ob2Rub3RhIDwgMC4wNSDihpIgKipvZG1pZXRhbWUqKiBcKEhfMFwpIOKGkiBtb2RlbCBmaWxtb3bDqWhvIGhvZG5vdGVuaWEgamUgcHJhdmRlcG9kb2JuZSB6bGUgxaFwZWNpZmlrb3ZhbsO9IChjaMO9YmFqw7ogbmVsaW5lw6FybmUgxI1sZW55IGFsZWJvIMSPYWzFoWllIHByZW1lbm7DqSkuCi0gQWsgcC1ob2Rub3RhIOKJpSAwLjA1IOKGkiBuZW3DoW1lIGTDtGtheiBvIG5lc3Byw6F2bmVqIMWhcGVjaWZpa8OhY2lpLgoKIyMgMy4gR3JhZmlja8OhIGFuYWzDvXphCgojIyMgMy4xIEdyYWYgKlJlc2lkdWFscyB2cy4gRml0dGVkKgoKUG96cmllbWUgc2EgbmEgdnrFpWFoIG1lZHppIHZ5cm92bmFuw71taSBob2Rub3RhbWkgSU1EYiBob2Rub3RlbmlhIGEgcmV6w61kdWFtaS4KCmBgYHtyfQpwbG90KG1vZGVsLCB3aGljaCA9IDEpCmBgYAoKQWsgcmV6w61kdcOhIHZ5a2F6dWrDuiBzeXN0ZW1hdGlja8O9IChuZXpobHVrb3ZhbsO9KSB2em9yIOKAkyBuYXByw61rbGFkIHpha3JpdmVuaWUg4oCTLCBtw7TFvmUgdG8gem5hbWVuYcWlLCDFvmUgbW9kZWwgesOhdmlzbG9zdGkgSU1EYiBob2Rub3RlbmlhIG5hIGTEusW+a2UsIHJva3UgYSByb3pwb8SNdGUgbmllIGplIMO6cGxuZSBsaW5lw6FybnkuIFZ0ZWR5IG3DtMW+ZSBwb23DtGPFpSB0cmFuc2Zvcm3DoWNpYSBuaWVrdG9yZWogcHJlbWVubmVqIChuYXByLiB6YXZlZGVuaWUga3ZhZHJhdGlja8OpaG8gxI1sZW5hKSBhbGVibyBkb3BsbmVuaWUgbm92ZWogcHJlbWVubmVqIChuYXByLiBkdW1teSBwcmUg4oCec3RhcsWhaWXigJwv4oCebm92xaFpZeKAnCBmaWxteSkuCgojIyMgMy4yIEMrUiBncmFmeSAoQ29tcG9uZW50ICsgUmVzaWR1YWwgcGxvdHMpCgpDK1IgZ3JhZnkgbsOhbSBwb23DtMW+dSB6aXPFpW92YcWlLCDEjWkgamUgdnrFpWFoIG1lZHppIElNRGIgaG9kbm90ZW7DrW0gYSBqZWRub3RsaXbDvW1pIHZ5c3ZldMS+dWrDumNpbWkgcHJlbWVubsO9bWkgbGluZcOhcm55LCBhbGVibyB6YWtyaXZlbsO9LgoKYGBge3J9CmNhcjo6Y3JQbG90cyhtb2RlbCkKYGBgCgpBayBzYSBrcml2a2EgdsO9cmF6bmUgb2RjaHnEvnVqZSBvZCBwcmlhbWt5LCB6dsOhxb5pbWUgdHJhbnNmb3Jtw6FjaXUgZGFuZWogcHJlbWVubmVqIChuYXByLiBrdmFkcmF0aWNrw70gxI1sZW4sIGxvZ2FyaXRtdXMsIG9kbW9jbmluYSkuIFByZWRzdGF2bWUgc2ksIMW+ZSBuYWp2w6TEjcWhw60gb2RrbG9uIG9kIGxpbmVhcml0eSBwb3pvcnVqZW1lIG5ham3DpCBwcmkgcHJlbWVubmVqICoqZMS6xb5rYSBmaWxtdSAoYGRsemthYCkqKiBhIG1vxb5ubyBhaiBwcmkgKipyb3pwb8SNdGUgKGByb3pwb2NldGApKiouCgojIyA0LiBOZWxpbmXDoXJuYSDFoXBlY2lmaWvDoWNpYSDigJMga3ZhZHJhdGlja8OpIMSNbGVueQoKWmxvxb5pdGVqxaFpZSBuZWxpbmXDoXJuZSB2esWlYWh5IG3DtMW+ZW1lIHByaWJsacW+b3ZhxaUgcG9seW7Ds21vbSwgbmFwci4gemF2ZWRlbsOtbSBrdmFkcsOhdHUgdnlicmFuw71jaCB2eXN2ZXTEvnVqw7pjaWNoIHByZW1lbm7DvWNoLgoKUHJlZHBva2xhZGFqbWUsIMW+ZSBzYSByb3pob2RuZW1lIHphdmllc8WlIGt2YWRyw6F0IHByZW1lbm5laiAqKmTEusW+a2EqKiBhICoqcm96cG/EjWV0KiosIGxlYm8gQytSIGdyYWZ5IG5hem5hxI11asO6IHpha3JpdmVuaWUgaWNoIHZ6xaVhaHUgayBJTURiIGhvZG5vdGVuaXUuCgpgYGB7cn0KbW9kZWwgPC0gbG0oaW1kYiB+IDEgKyBkbHprYSArIHJvayArIHJvenBvY2V0LCBkYXRhID0gdWRhamUpCgptb2RlbF9rdmFkciA8LSBsbShpbWRiIH4gMSArIGRsemthICsgcm9rICsgcm96cG9jZXQgKwogICAgICAgICAgICAgICAgICAgIEkoZGx6a2FeMikgKyBJKHJvenBvY2V0XjIpLAogICAgICAgICAgICAgICAgICBkYXRhID0gdWRhamUpCgpzdW1tYXJ5KG1vZGVsX2t2YWRyKQoKIyBwb3Jvdm5hbmllIGxpbmXDoXJuZWhvIGEga3ZhZHJhdGlja8OpaG8gbW9kZWx1CmFub3ZhKG1vZGVsLCBtb2RlbF9rdmFkcikKCiMgUkVTRVQgdGVzdCBwcmUga3ZhZHJhdGlja8O9IG1vZGVsCnJlc2V0dGVzdChtb2RlbF9rdmFkcikKYGBgCgpBayBtw6EgdHJhbnNmb3Jtb3ZhbsO9IG1vZGVsIHZ5xaHFocOtIHVwcmF2ZW7DvSBrb2VmaWNpZW50IGRldGVybWluw6FjaWUgXChSXjJfe2Fkan1cKSBhIHrDoXJvdmXFiCBzYSB6bGVwxaHDrSBhaiB2w71zbGVkb2sgUkVTRVQgdGVzdHUsIG3DtMW+ZW1lIGtvbsWhdGF0b3ZhxaUsIMW+ZSBrdmFkcmF0aWNrw6kgxI1sZW55IHBvbW9obGkgbGVwxaFpZSB2eXN0aWhuw7rFpSB2esWlYWggbWVkemkgaG9kbm90ZW7DrW0gZmlsbXUgYSBqZWhvIGTEusW+a291L3JvenBvxI10b20uCgpBayBzYSB1a8Ohxb5lLCDFvmUgbmlla3RvcsOpIGt2YWRyYXRpY2vDqSDEjWxlbnkgc8O6IMWhdGF0aXN0aWNreSBuZXbDvXpuYW1uw6ksIG3DtMW+ZW1lIGljaCBwb3N0dXBuZSB2eWx1xI1vdmHFpSBhIHp2b2xpxaUg4oCew7pzcG9ybmVqxaHDreKAnCBtb2RlbCAoYmV6IHpieXRvxI1uw71jaCDEjWxlbm92KS4KCiMjIDUuIFJvesWhw61yZW7DvSBtb2RlbCBzIG5lbGluZcOhcm55bWkgxI1sZW5taQoKU2vDunNtZSBtb2RlbCwga2RlIHByZSB2xaFldGt5IHRyaSB2eXN2ZXTEvnVqw7pjZSBwcmVtZW5uw6kgKGTEusW+a2EsIHJvaywgcm96cG/EjWV0KSB6YXZlZGllwq1tZSBhaiBrdmFkcmF0aWNrw6kgxI1sZW55IGEgbsOhc2xlZG5lIHZ5aG9kbm90w61tZSBpY2ggxaF0YXRpc3RpY2vDuiB2w716bmFtbm9zxaU6CgpgYGB7cn0KbW9kZWxfcm96c2lyZW55IDwtIGxtKGltZGIgfiBkbHprYSArIHJvayArIHJvenBvY2V0ICsKICAgICAgICAgICAgICAgICAgICAgICAgSShkbHprYV4yKSArIEkocm9rXjIpICsgSShyb3pwb2NldF4yKSwKICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSB1ZGFqZSkKc3VtbWFyeShtb2RlbF9yb3pzaXJlbnkpCgphbm92YShtb2RlbCwgbW9kZWxfcm96c2lyZW55KQpyZXNldHRlc3QobW9kZWxfcm96c2lyZW55KQpgYGAKCkludGVycHJldMOhY2lhOgoKLSDFoXRhdGlzdGlja3kgdsO9em5hbW5lIGt2YWRyYXRpY2vDqSBrb2VmaWNpZW50eSBuYXpuYcSNdWrDuiB6YWtyaXZlbsO9IHZ6xaVhaCAobmFwci4gdmXEvm1pIGRsaMOpIGZpbG15IHXFviBuZW11c2lhIGJ5xaUgaG9kbm90ZW7DqSBsZXDFoWllKSwKLSBwb3Jvdm5hbsOtbSBtb2RlbG92IHBvbW9jb3UgKipBTk9WQSoqIHNsZWR1amVtZSwgxI1pIHJvesWhw61yZW7DvSBtb2RlbCBwcmluw6HFoWEgxaF0YXRpc3RpY2t5IHbDvXpuYW1uw6kgemxlcMWhZW5pZSBvcHJvdGkgcMO0dm9kbsOpbXUgbGluZcOhcm5lbXUgbW9kZWx1LgoKIyMgNi4gRHVtbXkgcHJlbWVubsOhIGEgbGluZcOhcm5hIGxvbWVuw6EgZnVua2NpYQoKUHJlZHBva2xhZGFqbWUgdGVyYXosIMW+ZSBDK1IgZ3JhZiBwcmUgcHJlbWVubsO6ICoqZMS6xb5rYSBmaWxtdSAoYGRsemthYCkqKiBuYXpuYcSNdWplLCDFvmUgcHJpYmxpxb5uZSBwcmkgKioxNTAgbWluw7p0YWNoKiogbmFzdMOhdmEgem1lbmEgdHJlbmR1IOKAkyBrcmF0xaFpZSBmaWxteSBzYSBzcHLDoXZhasO6IGluYWsgYWtvIHZlxL5taSBkbGjDqSBmaWxteS4KClphdmVkaWVtZSBwcmV0byBkdW1teSBwcmVtZW5uw7o6CgpcWwpEVU1faSA9ClxiZWdpbntjYXNlc30KMCwgJiBcdGV4dHthayB9IFx0ZXh0e2RsemthfV9pIDwgMTUwIFxcCjEsICYgXHRleHR7YWsgfSBcdGV4dHtkbHprYX1faSBcZ2UgMTUwClxlbmR7Y2FzZXN9ClxdCgpUw7p0byBkdW1teSBtb8W+bm8gdnl1xb5pxaU6CgoxLiAqKm5hIHpsb20gdiBhdXRvbsOzbW5vbSAoa29uxaF0YW50bm9tKSDEjWxlbmUqKiwgIAoyLiAqKm5hIHpsb20gdiBza2xvbmUqKiB2b8SNaSBwcmVtZW5uw71tIChuYXByLiB2b8SNaSBkxLrFvmtlKS4KCiMjIyA2LjEgWmxvbSB2IGF1dG9uw7Ntbm9tIMSNbGVuZQoKYGBge3J9CnVkYWplJERVTSA8LSBpZmVsc2UodWRhamUkZGx6a2EgPCAxNTAsIDAsIDEpCgptb2RlbERfYXV0byA8LSBsbShpbWRiIH4gMSArIERVTSArIGRsemthICsgcm9rICsgcm96cG9jZXQsCiAgICAgICAgICAgICAgICAgIGRhdGEgPSB1ZGFqZSkKc3VtbWFyeShtb2RlbERfYXV0bykKYGBgCgpUdSB0ZXN0dWplbWUsIMSNaSBzYSBwcmkgcHJlY2hvZGUgeiBrcmF0xaHDrWNoIG5hIGRsaMOpIGZpbG15IChuYWQgMTUwIG1pbsO6dCkgcG9zdW5pZSBwcmllbWVybsOpIElNRGIgaG9kbm90ZW5pZSBvIG5lamFrw7oga29uxaF0YW50bsO6IGhvZG5vdHUgKGJleiB6bWVueSBza2xvbnUpLgoKIyMjIDYuMiBabG9tIHYgc2tsb25lIChsaW5lw6FybmEgbG9tZW7DoSBmdW5rY2lhKQoKVGVyYXogdnl0dm9yw61tZSBtb2RlbCBzbyB6bG9tZW7DvW0gc2tsb25vbSB2b8SNaSBkxLrFvmtlIOKAkyDEjWnFvmUgaW7DvSB2cGx5diBkxLrFvmt5IG5hIGhvZG5vdGVuaWUgcHJpIGtyYXTFocOtY2ggYSBwcmkgZGxoxaHDrWNoIGZpbG1vY2g6CgpgYGB7cn0KbW9kZWxEX3NrbG9uIDwtIGxtKGltZGIgfiAxICsgZGx6a2EgKyBJKERVTSAqIGRsemthKSArIHJvayArIHJvenBvY2V0LAogICAgICAgICAgICAgICAgICAgZGF0YSA9IHVkYWplKQpzdW1tYXJ5KG1vZGVsRF9za2xvbikKYGBgCgpJbnRlcnByZXTDoWNpYToKCi0ga29lZmljaWVudCBwcmkgYGRsemthYCB2eWphZHJ1amUgdnBseXYgZMS6xb5reSBuYSBJTURiIGhvZG5vdGVuaWUgcHJlIGtyYXTFoWllIGZpbG15IChrZGUgYERVTSA9IDBgKSwgIAotIHPDusSNZXQga29lZmljaWVudG92IGBkbHprYWAgKyBgSShEVU0gKiBkbHprYSlgIHZ5amFkcnVqZSB2cGx5diBkxLrFvmt5IG5hIGhvZG5vdGVuaWUgcHJlIGRsaMWhaWUgZmlsbXkgKGBEVU0gPSAxYCkuCgpQb3Jvdm5hbmllIHDDtHZvZG7DqWhvIGxpbmXDoXJuZWhvIG1vZGVsdSBhIG1vZGVsdSBzbyB6bG9tZW7DvW0gc2tsb25vbSB1cm9iw61tZSBwb21vY291IEFOT1ZBIGEgUkVTRVQgdGVzdHU6CgpgYGB7cn0KYW5vdmEobW9kZWwsIG1vZGVsRF9za2xvbikKcmVzZXR0ZXN0KG1vZGVsRF9za2xvbikKYGBgCgpBayBBTk9WQSB0ZXN0IHVrw6HFvmUsIMW+ZSBtb2RlbCBzbyB6bG9tb20gdiBza2xvbmUgamUgxaF0YXRpc3RpY2t5IGxlcMWhw60sIG3DtMW+ZW1lIGtvbsWhdGF0b3ZhxaUsIMW+ZSBkxLrFvmthIGZpbG11IG92cGx5dsWIdWplIGhvZG5vdGVuaWUgSU1EYiBpbmFrIHByaSBrcmF0xaHDrWNoIGZpbG1vY2ggYSBpbmFrIHByaSB2ZcS+bWkgZGxow71jaC4KCiMjIDcuIEJveOKAk0NveG92IHRyYW5zZm9ybWHEjW7DvSB0ZXN0ICh2b2xpdGXEvm7DqSkKCkJveOKAk0NveG92YSB0cmFuc2Zvcm3DoWNpYSBzYSBwb3XFvsOtdmEgbmEgc3lzdGVtYXRpY2vDqSBoxL5hZGFuaWUgdmhvZG5laiB0cmFuc2Zvcm3DoWNpZSB6w6F2aXNsZWogcHJlbWVubsOpLCB2IG5hxaFvbSBwcsOtcGFkZSBJTURiIGhvZG5vdGVuaWEgZmlsbXUuCgpcWwpZX2leeyhcbGFtYmRhKX0gPQpcYmVnaW57Y2FzZXN9ClxkZnJhY3tZX2lee1xsYW1iZGF9IC0gMX17XGxhbWJkYX0sICYgXHRleHR7YWsgfSBcbGFtYmRhIFxuZXEgMCwgXFxbMTBwdF0KXGxuKFlfaSksICYgXHRleHR7YWsgfSBcbGFtYmRhID0gMC4KXGVuZHtjYXNlc30KXF0KCmBgYHtyfQpsaWJyYXJ5KE1BU1MpCmJveGNveChtb2RlbCkKYGBgCgpaIGdyYWZ1IEJveOKAk0NveCB2eWhvZG5vdMOtbWUsIGt0b3LDoSBob2Rub3RhIFwoXGxhbWJkYVwpIGJ5IG1vaGxhIGJ5xaUgdmhvZG7DoS4gUG9kxL5hIHRvaG86CgotIGFrIFwoXGxhbWJkYSBcYXBwcm94IDFcKSDihpIgbmV0cmViYSB0cmFuc2Zvcm1vdmHFpSwgIAotIGFrIFwoXGxhbWJkYSBcYXBwcm94IDBcKSDihpIgdmhvZG7DoSBsb2dhcml0bWlja8OhIHRyYW5zZm9ybcOhY2lhIFwoXGxvZyhcdGV4dHtpbWRifSlcKSwgIAotIGFrIFwoXGxhbWJkYSBcYXBwcm94IDB7LH01XCkg4oaSIHZob2Ruw6Egb2Rtb2NuaW5vdsOhIHRyYW5zZm9ybcOhY2lhLCAgCi0gYWsgXChcbGFtYmRhIFxhcHByb3ggLTFcKSDihpIgdHJhbnNmb3Jtw6FjaWEgXCgxL1x0ZXh0e2ltZGJ9XCkuCgpQcmUgaWx1c3Ryw6FjaXUgc2kgdnlza8O6xaFham1lIG5lamFrw7ogdnlicmFuw7ogaG9kbm90dSBcKFxsYW1iZGFcKSAobmFwcsOta2xhZCBcKFxsYW1iZGEgPSAxLjhcKSkgYSB6bm92YSBvZGhhZG5pbWUgbW9kZWw6CgpgYGB7cn0KbGFtYmRhIDwtIDEuOAoKbW9kZWxfbGFtYmRhIDwtIGxtKEkoKGltZGJebGFtYmRhIC0gMSkgLyBsYW1iZGEpIH4gMSArIGRsemthICsgcm9rICsgcm96cG9jZXQsCiAgICAgICAgICAgICAgICAgICBkYXRhID0gdWRhamUpCnN1bW1hcnkobW9kZWxfbGFtYmRhKQpyZXNldHRlc3QobW9kZWxfbGFtYmRhKQojIGFub3ZhIHBvcm92bmFuaWUgcyBww7R2b2Ruw71tIG1vZGVsb20gbmVtw6Egem15c2VsLCBwcmV0b8W+ZSBzbWUgem1lbmlsaSB2eXN2ZXTEvm92YW7DuiBwcmVtZW5uw7oKYGBgCgpBayBieSB0cmFuc2Zvcm3DoWNpYSB2w71yYXpuZWrFoWllIHpsZXDFoWlsYSB2bGFzdG5vc3RpIG1vZGVsdSAodnnFocWhw60gXChSXjJfe2Fkan1cKSwgbGVwxaHDrSBSRVNFVCB0ZXN0KSwgYm9sbyBieSBtb8W+bsOpIGp1IMSPYWxlaiBwb3XFvsOtdmHFpS4gWsOhcm92ZcWIIHbFoWFrIHN0csOhY2FtZSBwcmlhbW/EjWlhcnUgaW50ZXJwcmV0w6FjaXUg4oCebyBrb8S+a28gYm9kb3YgSU1EYiBzYSB6bWVuw60gaG9kbm90ZW5pZeKAnCwgcHJldG8gamUgcHJpIHByYWt0aWNrZWogaW50ZXJwcmV0w6FjaWkgxI1hc3RvIHbDvWhvZG5lasWhaWUgem9zdGHFpSBwcmkgcMO0dm9kbmVqIG1pZXJrZSBJTURiIGEgbmVsaW5lYXJpdHkgcmllxaFpxaUgbmFwcsOta2xhZCBrdmFkcmF0aWNrw71taSDEjWxlbm1pIGFsZWJvIGxvbWVuw71taSBmdW5rY2lhbWkuCg==