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

Pokračujeme v predchádzajúcich úlohách, kde s pomocou regresie skúmame, nakoľko je očakávaná dĺžka života ovplyvnená priemernou dobou dochádzky do školy, úrovňou HDP na obyvateľa, respektíve priemernou úrovňou BMI v danej krajine. Výsledok tejto regresie je uvedený nižšie

#######################################################################
# PRIPRAVA UDAJOV
#######################################################################
udaje <- read.csv("udaje/Life_Expectancy_Data.csv",dec=".",sep=",",header = TRUE)
# select just the record from 2015
udaje.2015 <- udaje[udaje$Year==2015,c("Life.expectancy","BMI","GDP","Schooling")]

# data imputation

# Compute column medians
#column_medians <- sapply(udaje.2015, median, na.rm = TRUE)

# Impute missing values with column medians
# Compute column medians
column_medians <- sapply(udaje.2015, median, na.rm = TRUE)

# Impute missing values with column medians
udaje_imputed <- udaje.2015
for (col in names(udaje.2015)) {
  udaje_imputed[[col]][is.na(udaje_imputed[[col]])] <- column_medians[col]
}

udaje.2015 <- udaje_imputed
udaje <- udaje.2015

################################################################################
# ZAKLADNA REGRESIA
################################################################################
attach(udaje)
model <- lm(Life.expectancy ~ +1 + BMI + GDP + Schooling, data = udaje)
summary(model)

Call:
lm(formula = Life.expectancy ~ +1 + BMI + GDP + Schooling, data = udaje)

Residuals:
     Min       1Q   Median       3Q      Max 
-17.6471  -2.4024   0.4563   3.2355  12.8591 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 4.419e+01  1.839e+00  24.033   <2e-16 ***
BMI         4.948e-02  2.144e-02   2.308   0.0222 *  
GDP         6.972e-05  3.845e-05   1.813   0.0715 .  
Schooling   1.921e+00  1.645e-01  11.676   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5.033 on 179 degrees of freedom
Multiple R-squared:  0.6224,    Adjusted R-squared:  0.6161 
F-statistic: 98.36 on 3 and 179 DF,  p-value: < 2.2e-16

Testovať, či je model v správnej funkčnej forme (t. j. či je lineárna špecifikácia vhodná, alebo či by ste mali transformovať premenné, napríklad pomocou logaritmov alebo mocninami), možno vykonať viacerými spôsobmi.

1. Test RESET (test chyby špecifikácie Ramseyho regresnej rovnice - Ramsey Reset Test)

Ide o najštandardnejší formálny test nesprávnej špecifikácie funkčnej formy ale dá sa použiť aj pre prípady, ak sme nešpecifikovali všetky vysvetľujúce premenné.

Myšlienka: Nech pôvodný model má tvar \[y_t = \beta_0 + \beta_1 x_{t10} + \dots +\beta_k x_{tk} + u_t\] Ak je váš model správne špecifikovaný, potom pridaním mocnín vyrovnaných hodnôt (napr. \(\hat y_t^2\), \(\hat{y}_t^3\)) by sa pôvodný model nemal podstatne zlepšiť, teda budeme testovať pôvodný model uvedený vyššie a model

\[y_t = \beta_0 + \beta_1 x_{t10} + \dots +\beta_k x_{tk} + \gamma_2\hat y_t^2 + \gamma_3\hat{y}_t^3 + u_t\]

Budeme testovať hypotézu

\(H_0:\) model je správne špecifikovaný (\(\gamma_2 = \gamma_3 = 0\))

oproti

\(H_1:\) model je nesprávne špecifikovaný (\(\gamma_2 \ne 0 \quad \text{alebo} \quad \gamma_3 \ne 0\))

# Suppose your model is:
model <- lm(Life.expectancy ~ +1 + BMI + GDP + Schooling, data = udaje)

# RESET test from 'lmtest' package:
library(lmtest)
resettest(model)

    RESET test

data:  model
RESET = 6.9899, df1 = 2, df2 = 177, p-value = 0.001197

Interpretácia:

Ak p-hodnota < 0.05, prijímame alternatívnu hypotézu, a teda model je zrejme zle špecifikovaný, t.j. chýbajú mu niektoré dodatočné vysvetľujúce premenné, alebo je potrebné urobiť nelineárnu transformáciu používaných premenných.

2. Grafická analýza

Graf Residuals vs. Fitted

Grafická analýza vzťahu medzi vyrovnanými hodnotami náhodnej premennej a rezíduami:

plot(model, which = 1)

Ak rezíduá vykazujú nenáhodný vzor (napr. zakrivenie), model nemusí byť lineárny v premenných a môže pomôcť nejaká ich funkčná transformácia. Mali by sme ale zvážiť aj začlenenie ďalšej premennej

Grafy C+R

Táto analýza nám môže pomôcť pri hľadaní odpovede na otázku, ktorú premennú by sme mali transfomovať pomocou nejakej známej funkcie. Vychádzajme z pôvodnej rovnice

\[y_t = \beta_0 + \beta_1 x_{t1} + \dots +\beta_k x_{tk} + u_t\] Túto rovnicu najprv odhadneme a potom vykresľujeme grafy, kde výraz component+residual (C+R) plot vykresľuje na zvislej osi \(\hat{\beta}_ix_{ti}+e_t\) a na vodorovnej osi vykresľuje hodnoty \(x_{ti}\)

Tieto grafy pomáhajú identifikovať nelineárne vzťahy pre každý regresor:

car::crPlots(model)

Ak sa krivka výrazne odchyľuje od priamky, zvážime transformáciu posudzovanej premennej (logaritmus, druhá mocnina atď.). Povedzme si, že v našm prípade je najväčší odklon od linearity v prípade premenne Schooling, ale možno aj BMI.

3. Nelineárna špecifikácia

Častokrát môžeme aj zložitejšie nelineárne vzťahy modelovať s pomocou ich aproximácie s polynómom, teda v prípade kvadratických členov

\[y_t = \beta_0 + \beta_1 x_{t10} + \dots +\beta_k x_{tk} + \dots + \gamma_i x_{ik}^2 + \dots + \gamma_j x_{jk}^2 + \dots + u_t\] Príklad na túto modifikáciu uvidíme nižšie.

3. Porovnanie základného a modifikovaného modelu

Predpokladajme, že sa pri nelineárnych úpravách pôvodnej rovnice dostaneme k zavedeniu kvadrátu premennej Schooling a BMI nakoľko sme motivovaní práve Component+Residual Plotom uvedenýn s vysvetlením pomocou týchto premenných, čo je uvedené vyššie. Tu sa vyrovnaná krivka asi najviac líši od priamky.

Ak má transformovaný model vyšší upravený koefcient determinácie \(R^2_{adj}\) a pri RESET test prijmeme alternatívnu hypotézu, odporúčame si výsledky potvrdiť s pomocou Anova testu oboch modelov a prípadne opakovaného Reset Testu uplatneneného na nelineárne transformovaný model

model <- lm(Life.expectancy ~ +1 + BMI + GDP + Schooling)
model_schooling_kvadr <- lm(Life.expectancy ~ +1 + BMI + GDP + Schooling +  I(Schooling^2) + I(BMI^2)  )
summary(model_schooling_kvadr)

Call:
lm(formula = Life.expectancy ~ +1 + BMI + GDP + Schooling + I(Schooling^2) + 
    I(BMI^2))

Residuals:
    Min      1Q  Median      3Q     Max 
-16.293  -2.546   0.429   3.310  12.758 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)     4.926e+01  5.217e+00   9.443  < 2e-16 ***
BMI            -2.498e-01  8.656e-02  -2.886 0.004388 ** 
GDP             6.582e-05  3.813e-05   1.726 0.086008 .  
Schooling       1.901e+00  8.008e-01   2.374 0.018689 *  
I(Schooling^2) -5.065e-03  3.187e-02  -0.159 0.873892    
I(BMI^2)        3.957e-03  1.110e-03   3.563 0.000471 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.889 on 177 degrees of freedom
Multiple R-squared:  0.6477,    Adjusted R-squared:  0.6378 
F-statistic: 65.09 on 5 and 177 DF,  p-value: < 2.2e-16
anova(model, model_schooling_kvadr)
Analysis of Variance Table

Model 1: Life.expectancy ~ +1 + BMI + GDP + Schooling
Model 2: Life.expectancy ~ +1 + BMI + GDP + Schooling + I(Schooling^2) + 
    I(BMI^2)
  Res.Df    RSS Df Sum of Sq      F   Pr(>F)   
1    179 4534.9                                
2    177 4231.4  2     303.6 6.3498 0.002171 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
resettest(model_schooling_kvadr)

    RESET test

data:  model_schooling_kvadr
RESET = 14.715, df1 = 2, df2 = 175, p-value = 1.239e-06

Takto modifikovaný modeluvádza kvadrát premennej BMI ako štatisticky významný a tiež dochádza k ďalšiemu rastu kupraveného koeficientu determinácie \(R^2_{adj}\). Ak vylúčime kvadrát premennej Schooling, ktorý je štatisticky nevýznamný, potom dotaneme nasledovný výsledok

model_schooling_kvadr <- lm(Life.expectancy ~ +1 + BMI + GDP + Schooling  + I(BMI^2)  )
summary(model_schooling_kvadr)

Call:
lm(formula = Life.expectancy ~ +1 + BMI + GDP + Schooling + I(BMI^2))

Residuals:
     Min       1Q   Median       3Q      Max 
-16.2631  -2.5357   0.4467   3.3402  12.7876 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  5.000e+01  2.412e+00  20.730  < 2e-16 ***
BMI         -2.496e-01  8.631e-02  -2.892 0.004309 ** 
GDP          6.463e-05  3.728e-05   1.734 0.084694 .  
Schooling    1.776e+00  1.645e-01  10.800  < 2e-16 ***
I(BMI^2)     3.951e-03  1.107e-03   3.570 0.000459 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.876 on 178 degrees of freedom
Multiple R-squared:  0.6477,    Adjusted R-squared:  0.6397 
F-statistic:  81.8 on 4 and 178 DF,  p-value: < 2.2e-16

pričom upravený koeficient determinácie sa nezmenil. Pokračujeme teda v ďalšom využívaní tohto modelu.

5. Použitie rozšíreného RESET testu (NEPOTREBNÉ)

model_rozsireny <- lm(Life.expectancy ~ BMI + GDP + Schooling + I(BMI^2) + I(GDP^2) + I(Schooling^2) )
summary(model_rozsireny)

Call:
lm(formula = Life.expectancy ~ BMI + GDP + Schooling + I(BMI^2) + 
    I(GDP^2) + I(Schooling^2))

Residuals:
     Min       1Q   Median       3Q      Max 
-16.2301  -2.4487   0.5409   3.4535  12.8283 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)     4.974e+01  5.248e+00   9.476  < 2e-16 ***
BMI            -2.549e-01  8.682e-02  -2.936 0.003764 ** 
GDP             1.557e-04  1.101e-04   1.414 0.159259    
Schooling       1.836e+00  8.047e-01   2.282 0.023696 *  
I(BMI^2)        4.006e-03  1.113e-03   3.600 0.000413 ***
I(GDP^2)       -1.794e-09  2.062e-09  -0.870 0.385620    
I(Schooling^2) -4.089e-03  3.191e-02  -0.128 0.898172    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.893 on 176 degrees of freedom
Multiple R-squared:  0.6492,    Adjusted R-squared:  0.6373 
F-statistic: 54.29 on 6 and 176 DF,  p-value: < 2.2e-16

Tu testujeme, či koeficienty pri kvadratických členoch sú štatisticky významné, ak áno, potom musíme urobiť túto kvadratickú modifikáciu. Anova testom môžeme potom sledovať, či v porovnaní s pôvodným modelom došlo k zlepšeniu.

anova(model,model_rozsireny)
Analysis of Variance Table

Model 1: Life.expectancy ~ +1 + BMI + GDP + Schooling
Model 2: Life.expectancy ~ BMI + GDP + Schooling + I(BMI^2) + I(GDP^2) + 
    I(Schooling^2)
  Res.Df    RSS Df Sum of Sq      F   Pr(>F)   
1    179 4534.9                                
2    176 4213.2  3    321.71 4.4796 0.004665 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
resettest(model_rozsireny)

    RESET test

data:  model_rozsireny
RESET = 14.649, df1 = 2, df2 = 174, p-value = 1.319e-06

Pri reset teste tu vidíme, že problém neprávnej špecifikácie sme ešte neodstránili.

6. Transformácia pomocou dummy premennej a lineárnej lomenej funkcie

Predpokladajme, že máme dummy premennú \(D_t\), ktorá obsahuje len nuly a jedničky. Takáto premenná sa dá využiť modelovanie zlomov, a to napr.

  1. zlom v autonómnom člene \(\beta_0\) a to nasledovnou špecifikáciou \[y_t = \beta_0 + \beta_D D+ \beta_1 x_{t1} + \dots +\beta_k x_{tk} + u_t\] čo interpretujeme ako posun regresnej priamky (regresnej nadroviny) o \(\beta_D\) jednotiek pozdĺž zvislej osi a to len v pozorovaniach, ak je splnená podmienka \(D_t = 1\)
  2. zlom v sklone regresnej priamky (nadroviny) a to len v pozorovaniach, ak je splnená podmienka \(D_t = 1\), čo dosiahneme nasledovnou špecifikáciou \[y_t = \beta_0 + \beta_1 x_{t1} + \dots + \beta_{i}x_{ti} + \beta_{Di}D_tx_{ti}+ \dots + \beta_k x_{tk} + u_t\] kde teda sklon priamky pozdĺž premenne \(x_{ti}\) je \(\beta_i\) ale len v prípade \(D_t=0\), inak je ten sklon rovný \(\beta_i+\beta_{D_i}\).

Ak si pozrieme C+R graf v prípade vysvetľujúcej veličiny BMI, všimneme si, že vyrovnaná krivka nadobudne približne pre hodnoty \(BMI>27\) iný sklon. Zaveďme preto premen DUM, pre ktorú bude platiť \(DUM_t = 1\) ak \(BMI_t > 27\) a pokúsme sa analyzovať vplyv BMI separátne pre obidve úrovne.

Kvantifikujme teraz model so zlomom v autonómnom člene, ak teda \(BMI_t<27\) a teda \(DUM_t = 0\) a \(BMI_t\geq 27\) a teda \(DUM_t = 1\):

udaje$DUM <- ifelse(udaje$BMI < 27, 0, 1)
modelD_auto <- lm(Life.expectancy ~ +1 + DUM + BMI + GDP + Schooling,data=udaje ) 
summary(modelD_auto)

Call:
lm(formula = Life.expectancy ~ +1 + DUM + BMI + GDP + Schooling, 
    data = udaje)

Residuals:
     Min       1Q   Median       3Q      Max 
-18.8082  -2.6521   0.6349   3.1410  12.9335 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 4.451e+01  1.848e+00  24.086   <2e-16 ***
DUM         2.016e+00  1.445e+00   1.395   0.1647    
BMI         1.294e-02  3.381e-02   0.383   0.7023    
GDP         6.844e-05  3.836e-05   1.784   0.0761 .  
Schooling   1.910e+00  1.643e-01  11.627   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5.02 on 178 degrees of freedom
Multiple R-squared:  0.6265,    Adjusted R-squared:  0.6181 
F-statistic: 74.65 on 4 and 178 DF,  p-value: < 2.2e-16

V tomto prípade sa nepotvrdilo, že by existoval signifikantný zlom v posune regresnej nadroviny.


modelD_sklon <- lm(Life.expectancy ~ +1  + BMI + I(DUM*BMI) + GDP + Schooling,data=udaje ) 
summary(modelD_sklon)

Call:
lm(formula = Life.expectancy ~ +1 + BMI + I(DUM * BMI) + GDP + 
    Schooling, data = udaje)

Residuals:
     Min       1Q   Median       3Q      Max 
-19.6911  -2.0156   0.3699   2.7548  11.3676 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)   4.995e+01  2.134e+00  23.405  < 2e-16 ***
BMI          -2.449e-01  6.633e-02  -3.692 0.000296 ***
I(DUM * BMI)  2.407e-01  5.164e-02   4.661 6.14e-06 ***
GDP           6.667e-05  3.641e-05   1.831 0.068762 .  
Schooling     1.753e+00  1.599e-01  10.966  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.765 on 178 degrees of freedom
Multiple R-squared:  0.6635,    Adjusted R-squared:  0.6559 
F-statistic: 87.75 on 4 and 178 DF,  p-value: < 2.2e-16

Tentokrát upravený koeficient determinácie narástol, čo považujeme za veľmi dobrú vlastnosť modelu. V prípade krajín s nízkymi hodnotami BMI indexu môžeme konštatovať, že nárast BMI o jednu jednotku je u nich sprevádzané s poklesom očakávanej dĺžky živoat o približne 0.2449 roka a v prípade ostatných krajín nárast BMI o jednu jednotku vyvolá nárast očakávanej dĺžky života približne o -0.2449+0.2407 = 0.0042 roka. Pokúsme sa teraz otestovať významnosť zavedenia zlomu v sklone nadroviny Porovnanie pôvodného modelu a modelu s premenlivým sklonom nadroviny vykonáme s pomocou anova testu:

anova(model, modelD_sklon)
Analysis of Variance Table

Model 1: Life.expectancy ~ +1 + BMI + GDP + Schooling
Model 2: Life.expectancy ~ +1 + BMI + I(DUM * BMI) + GDP + Schooling
  Res.Df    RSS Df Sum of Sq      F    Pr(>F)    
1    179 4534.9                                  
2    178 4041.6  1    493.37 21.729 6.137e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
resettest(modelD_sklon)

    RESET test

data:  modelD_sklon
RESET = 5.7851, df1 = 2, df2 = 176, p-value = 0.003687

Na základe anova analýzy môžeme teda konštatovať, že model zlepšil svoje štatistické vlastnosti, ale Ramsey Reset test naďalej považuje chybu špecifikácie modelu za významnú.

7. Box-Coxov transformačný test (nepotrebné)

Ide o systematickejší test na zistenie, ako by sa závislá premenná mala transformovať pomocou nejakej funkcie (napr. \(\ln(Y)\), \(\sqrt(Y)\), \(1/Y\)). Konkrétna transformácia sa robí podľa vzorca

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

library(MASS)
boxcox(model)

Interpretácia:

Maximum funkcie vierohodnosti definuje najlepšiu hodnotu \(\lambda\) zvislou bodkovanou čiarou, pričom ak:

  • \(\lambda \approx 1\), potom nie je potrebná žiadna transformácia;
  • \(\lambda \approx 0\), potom je potrebná logaritmická transformácia premennej \(\log(Y)\);
  • \(\lambda \approx 0.5\),potom je potrebná odmocninová transformácia premennej \(\sqrt(Y)\);
  • \(\lambda \approx -1\),potom je potrebná reciproká transformácia premennej \(1/Y\).

Konkrétna transformácia sa potom vykoná podľa vzorca:

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

U nás je hodnota \(\lambda\) niekde na úrovni 1.8, teda v regresii upravíme vysvetľovanú premennú Life.expectancy a opäť spustíme regresiu.

model_lambda <- lm(I((Life.expectancy^1.8-1)/1.8) ~ +1 + BMI + GDP + Schooling, data = udaje)
summary(model_lambda)

Call:
lm(formula = I((Life.expectancy^1.8 - 1)/1.8) ~ +1 + BMI + GDP + 
    Schooling, data = udaje)

Residuals:
    Min      1Q  Median      3Q     Max 
-492.26  -78.37    9.22   92.60  407.28 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 3.997e+02  5.393e+01   7.411 4.79e-12 ***
BMI         1.369e+00  6.288e-01   2.177   0.0308 *  
GDP         2.304e-03  1.128e-03   2.043   0.0425 *  
Schooling   5.798e+01  4.825e+00  12.016  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 147.6 on 179 degrees of freedom
Multiple R-squared:  0.6347,    Adjusted R-squared:  0.6286 
F-statistic: 103.7 on 3 and 179 DF,  p-value: < 2.2e-16
resettest(model_lambda)

    RESET test

data:  model_lambda
RESET = 6.9812, df1 = 2, df2 = 177, p-value = 0.001207
# tu anova porovnanie modelu nemozeme pouzit, lebo vysvetľovaná premenná sa v oboch modeloch líši

Táto úprava nám síce nepatrne zvýžila \(R^2_{adj}\) ale problém chybnej špecifikácie sa neodstránil (pozri p-value reset testu). Okrem toho sme stratili príjemnú interpretáciu modelu a preto od Cox-Boxovej transformácie upustíme.

LS0tCnRpdGxlOiAixaBwZWNpZmlrw6FjaWEgbW9kZWx1IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKYXV0aG9yOiBWLiBHYXpkYQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKYGBge3J9CmxpYnJhcnkoem9vKQpsaWJyYXJ5KHRzZXJpZXMpCmxpYnJhcnkobG10ZXN0KQpsaWJyYXJ5KHNhbmR3aWNoKQpsaWJyYXJ5KGNhcikKcm0obGlzdD1scygpKQpgYGAKClBva3JhxI11amVtZSB2IHByZWRjaMOhZHphasO6Y2ljaCDDumxvaMOhY2gsIGtkZSBzIHBvbW9jb3UgcmVncmVzaWUgc2vDum1hbWUsIG5ha2/EvmtvIGplIG/EjWFrw6F2YW7DoSBkxLrFvmthIMW+aXZvdGEgb3ZwbHl2bmVuw6EgcHJpZW1lcm5vdSBkb2JvdSBkb2Now6Fkemt5IGRvIMWha29seSwgw7pyb3bFiG91IEhEUCBuYSBvYnl2YXRlxL5hLCByZXNwZWt0w612ZSBwcmllbWVybm91IMO6cm92xYhvdSBCTUkgdiBkYW5laiBrcmFqaW5lLiBWw71zbGVkb2sgdGVqdG8gcmVncmVzaWUgamUgdXZlZGVuw70gbmnFvsWhaWUKCmBgYHtyfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFBSSVBSQVZBIFVEQUpPVgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwp1ZGFqZSA8LSByZWFkLmNzdigidWRhamUvTGlmZV9FeHBlY3RhbmN5X0RhdGEuY3N2IixkZWM9Ii4iLHNlcD0iLCIsaGVhZGVyID0gVFJVRSkKIyBzZWxlY3QganVzdCB0aGUgcmVjb3JkIGZyb20gMjAxNQp1ZGFqZS4yMDE1IDwtIHVkYWplW3VkYWplJFllYXI9PTIwMTUsYygiTGlmZS5leHBlY3RhbmN5IiwiQk1JIiwiR0RQIiwiU2Nob29saW5nIildCgojIGRhdGEgaW1wdXRhdGlvbgoKIyBDb21wdXRlIGNvbHVtbiBtZWRpYW5zCiNjb2x1bW5fbWVkaWFucyA8LSBzYXBwbHkodWRhamUuMjAxNSwgbWVkaWFuLCBuYS5ybSA9IFRSVUUpCgojIEltcHV0ZSBtaXNzaW5nIHZhbHVlcyB3aXRoIGNvbHVtbiBtZWRpYW5zCiMgQ29tcHV0ZSBjb2x1bW4gbWVkaWFucwpjb2x1bW5fbWVkaWFucyA8LSBzYXBwbHkodWRhamUuMjAxNSwgbWVkaWFuLCBuYS5ybSA9IFRSVUUpCgojIEltcHV0ZSBtaXNzaW5nIHZhbHVlcyB3aXRoIGNvbHVtbiBtZWRpYW5zCnVkYWplX2ltcHV0ZWQgPC0gdWRhamUuMjAxNQpmb3IgKGNvbCBpbiBuYW1lcyh1ZGFqZS4yMDE1KSkgewogIHVkYWplX2ltcHV0ZWRbW2NvbF1dW2lzLm5hKHVkYWplX2ltcHV0ZWRbW2NvbF1dKV0gPC0gY29sdW1uX21lZGlhbnNbY29sXQp9Cgp1ZGFqZS4yMDE1IDwtIHVkYWplX2ltcHV0ZWQKdWRhamUgPC0gdWRhamUuMjAxNQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBaQUtMQUROQSBSRUdSRVNJQQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwphdHRhY2godWRhamUpCm1vZGVsIDwtIGxtKExpZmUuZXhwZWN0YW5jeSB+ICsxICsgQk1JICsgR0RQICsgU2Nob29saW5nLCBkYXRhID0gdWRhamUpCnN1bW1hcnkobW9kZWwpCmBgYAoKVGVzdG92YcWlLCDEjWkgamUgbW9kZWwgdiBzcHLDoXZuZWogZnVua8SNbmVqIGZvcm1lICh0LiBqLiDEjWkgamUgbGluZcOhcm5hIMWhcGVjaWZpa8OhY2lhIHZob2Ruw6EsIGFsZWJvIMSNaSBieSBzdGUgbWFsaSB0cmFuc2Zvcm1vdmHFpSBwcmVtZW5uw6ksIG5hcHLDrWtsYWQgcG9tb2NvdSBsb2dhcml0bW92IGFsZWJvIG1vY25pbmFtaSksIG1vxb5ubyB2eWtvbmHFpSB2aWFjZXLDvW1pIHNww7Rzb2JtaS4KCiMjIyAxLiBUZXN0IFJFU0VUICh0ZXN0IGNoeWJ5IMWhcGVjaWZpa8OhY2llIFJhbXNleWhvIHJlZ3Jlc25laiByb3ZuaWNlIC0gUmFtc2V5IFJlc2V0IFRlc3QpCgpJZGUgbyBuYWrFoXRhbmRhcmRuZWrFocOtIGZvcm3DoWxueSB0ZXN0IG5lc3Byw6F2bmVqIMWhcGVjaWZpa8OhY2llIGZ1bmvEjW5laiBmb3JteSBhbGUgZMOhIHNhIHBvdcW+acWlIGFqIHByZSBwcsOtcGFkeSwgYWsgc21lIG5lxaFwZWNpZmlrb3ZhbGkgdsWhZXRreSB2eXN2ZXTEvnVqw7pjZSBwcmVtZW5uw6kuCgpNecWhbGllbmthOiBOZWNoIHDDtHZvZG7DvSBtb2RlbCBtw6EgdHZhcgokJHlfdCA9IFxiZXRhXzAgKyBcYmV0YV8xIHhfe3QxMH0gKyBcZG90cyArXGJldGFfayB4X3t0a30gKyB1X3QkJApBayBqZSB2w6HFoSBtb2RlbCBzcHLDoXZuZSDFoXBlY2lmaWtvdmFuw70sIHBvdG9tIHByaWRhbsOtbSBtb2Nuw61uIHZ5cm92bmFuw71jaCBob2Ruw7R0IChuYXByLiAkXGhhdCB5X3ReMiQsICRcaGF0e3l9X3ReMyQpIGJ5IHNhIHDDtHZvZG7DvSBtb2RlbCBuZW1hbCBwb2RzdGF0bmUgemxlcMWhacWlLCB0ZWRhIGJ1ZGVtZSB0ZXN0b3ZhxaUgcMO0dm9kbsO9IG1vZGVsIHV2ZWRlbsO9IHZ5xaHFoWllIGEgbW9kZWwKCiQkeV90ID0gXGJldGFfMCArIFxiZXRhXzEgeF97dDEwfSArIFxkb3RzICtcYmV0YV9rIHhfe3RrfSArIFxnYW1tYV8yXGhhdCB5X3ReMiArIFxnYW1tYV8zXGhhdHt5fV90XjMgKyB1X3QkJAoKQnVkZW1lIHRlc3RvdmHFpSBoeXBvdMOpenUgCgokSF8wOiQgbW9kZWwgamUgc3Byw6F2bmUgxaFwZWNpZmlrb3ZhbsO9ICgkXGdhbW1hXzIgPSBcZ2FtbWFfMyA9IDAkKQoKb3Byb3RpCgokSF8xOiQgbW9kZWwgamUgbmVzcHLDoXZuZSDFoXBlY2lmaWtvdmFuw70gKCRcZ2FtbWFfMiBcbmUgMCAgXHF1YWQgXHRleHR7YWxlYm99ICBccXVhZCBcZ2FtbWFfMyBcbmUgMCQpCgpgYGB7cn0KIyBTdXBwb3NlIHlvdXIgbW9kZWwgaXM6Cm1vZGVsIDwtIGxtKExpZmUuZXhwZWN0YW5jeSB+ICsxICsgQk1JICsgR0RQICsgU2Nob29saW5nLCBkYXRhID0gdWRhamUpCgojIFJFU0VUIHRlc3QgZnJvbSAnbG10ZXN0JyBwYWNrYWdlOgpsaWJyYXJ5KGxtdGVzdCkKcmVzZXR0ZXN0KG1vZGVsKQoKYGBgCgoqKkludGVycHJldMOhY2lhOioqCgpBayBwLWhvZG5vdGEgXDwgMC4wNSwgcHJpasOtbWFtZSBhbHRlcm5hdMOtdm51IGh5cG90w6l6dSwgYSB0ZWRhIG1vZGVsIGplIHpyZWptZSB6bGUgxaFwZWNpZmlrb3ZhbsO9LCB0LmouIGNow71iYWrDuiBtdSBuaWVrdG9yw6kgZG9kYXRvxI1uw6kgdnlzdmV0xL51asO6Y2UgcHJlbWVubsOpLCBhbGVibyBqZSBwb3RyZWJuw6kgdXJvYmnFpSBuZWxpbmXDoXJudSB0cmFuc2Zvcm3DoWNpdSBwb3XFvsOtdmFuw71jaCBwcmVtZW5uw71jaC4KCiMjIyAyLiBHcmFmaWNrw6EgYW5hbMO9emEKCiMjIyMgR3JhZiAqUmVzaWR1YWxzIHZzLiBGaXR0ZWQqCgpHcmFmaWNrw6EgYW5hbMO9emEgdnrFpWFodSBtZWR6aSB2eXJvdm5hbsO9bWkgaG9kbm90YW1pIG7DoWhvZG5laiBwcmVtZW5uZWogYSByZXrDrWR1YW1pOgoKYGBge3J9CnBsb3QobW9kZWwsIHdoaWNoID0gMSkKYGBgCgpBayByZXrDrWR1w6EgdnlrYXp1asO6IG5lbsOhaG9kbsO9IHZ6b3IgKG5hcHIuIHpha3JpdmVuaWUpLCBtb2RlbCBuZW11c8OtIGJ5xaUgbGluZcOhcm55IHYgcHJlbWVubsO9Y2ggYSBtw7TFvmUgcG9tw7RjxaUgbmVqYWvDoSBpY2ggZnVua8SNbsOhIHRyYW5zZm9ybcOhY2lhLiBNYWxpIGJ5IHNtZSBhbGUgenbDocW+acWlIGFqIHphxI1sZW5lbmllIMSPYWzFoWVqIHByZW1lbm5lagoKIyMjIyBHcmFmeSBDK1IgCgpUw6F0byBhbmFsw716YSBuw6FtIG3DtMW+ZSBwb23DtGPFpSBwcmkgaMS+YWRhbsOtIG9kcG92ZWRlIG5hIG90w6F6a3UsIGt0b3LDuiBwcmVtZW5uw7ogYnkgc21lIG1hbGkgdHJhbnNmb21vdmHFpSBwb21vY291IG5lamFrZWogem7DoW1laiBmdW5rY2llLiBWeWNow6FkemFqbWUgeiBww7R2b2RuZWogcm92bmljZQoKJCR5X3QgPSBcYmV0YV8wICsgXGJldGFfMSB4X3t0MX0gKyBcZG90cyArXGJldGFfayB4X3t0a30gKyB1X3QkJApUw7p0byByb3ZuaWN1IG5hanBydiBvZGhhZG5lbWUgYSBwb3RvbSB2eWtyZXPEvnVqZW1lIGdyYWZ5LCBrZGUgdsO9cmF6IGNvbXBvbmVudCtyZXNpZHVhbCAoQytSKSBwbG90IHZ5a3Jlc8S+dWplIG5hIHp2aXNsZWogb3NpICRcaGF0e1xiZXRhfV9peF97dGl9K2VfdCQgYSBuYSB2b2Rvcm92bmVqIG9zaSB2eWtyZXPEvnVqZSBob2Rub3R5ICR4X3t0aX0kCgpUaWV0byBncmFmeSBwb23DoWhhasO6IGlkZW50aWZpa292YcWlIG5lbGluZcOhcm5lIHZ6xaVhaHkgcHJlIGthxb5kw70gcmVncmVzb3I6CgpgYGB7cn0KY2FyOjpjclBsb3RzKG1vZGVsKQoKYGBgCkFrIHNhIGtyaXZrYSB2w71yYXpuZSBvZGNoecS+dWplIG9kIHByaWFta3ksIHp2w6HFvmltZSB0cmFuc2Zvcm3DoWNpdSBwb3N1ZHpvdmFuZWogcHJlbWVubmVqIChsb2dhcml0bXVzLCBkcnVow6EgbW9jbmluYSBhdMSPLikuIFBvdmVkem1lIHNpLCDFvmUgdiBuYcWhbSBwcsOtcGFkZSBqZSBuYWp2w6TEjcWhw60gb2RrbG9uIG9kIGxpbmVhcml0eSB2IHByw61wYWRlIHByZW1lbm5lICpTY2hvb2xpbmcqLCBhbGUgbW/Fvm5vIGFqICpCTUkqLgoKIyMgMy4gTmVsaW5lw6FybmEgxaFwZWNpZmlrw6FjaWEKCsSMYXN0b2tyw6F0IG3DtMW+ZW1lIGFqIHpsb8W+aXRlasWhaWUgbmVsaW5lw6FybmUgdnrFpWFoeSBtb2RlbG92YcWlIHMgcG9tb2NvdSBpY2ggYXByb3hpbcOhY2llIHMgcG9seW7Ds21vbSwgdGVkYSB2IHByw61wYWRlIGt2YWRyYXRpY2vDvWNoIMSNbGVub3YKCiQkeV90ID0gXGJldGFfMCArIFxiZXRhXzEgeF97dDEwfSArIFxkb3RzICtcYmV0YV9rIHhfe3RrfSArIFxkb3RzICsgXGdhbW1hX2kgeF97aWt9XjIgKyBcZG90cyArIFxnYW1tYV9qIHhfe2prfV4yICsgXGRvdHMgKyB1X3QkJApQcsOta2xhZCBuYSB0w7p0byBtb2RpZmlrw6FjaXUgdXZpZMOtbWUgbmnFvsWhaWUuCgojIyAzLiBQb3Jvdm5hbmllIHrDoWtsYWRuw6lobyBhIG1vZGlmaWtvdmFuw6lobyBtb2RlbHUKClByZWRwb2tsYWRham1lLCDFvmUgc2EgcHJpIG5lbGluZcOhcm55Y2ggw7pwcmF2w6FjaCBww7R2b2RuZWogcm92bmljZSBkb3N0YW5lbWUgayB6YXZlZGVuaXUga3ZhZHLDoXR1IHByZW1lbm5laiAqU2Nob29saW5nKiBhICpCTUkqIG5ha2/EvmtvIHNtZSBtb3Rpdm92YW7DrSBwcsOhdmUgQ29tcG9uZW50K1Jlc2lkdWFsIFBsb3RvbSB1dmVkZW7DvW4gcyB2eXN2ZXRsZW7DrW0gcG9tb2NvdSB0w71jaHRvIHByZW1lbm7DvWNoLCDEjW8gamUgdXZlZGVuw6kgdnnFocWhaWUuIFR1IHNhIHZ5cm92bmFuw6Ega3JpdmthIGFzaSBuYWp2aWFjIGzDrcWhaSBvZCBwcmlhbWt5LiAKCkFrIG3DoSB0cmFuc2Zvcm1vdmFuw70gbW9kZWwgdnnFocWhw60gdXByYXZlbsO9IGtvZWZjaWVudCBkZXRlcm1pbsOhY2llICRSXjJfe2Fkan0kIGEgcHJpIFJFU0VUIHRlc3QgcHJpam1lbWUgYWx0ZXJuYXTDrXZudSBoeXBvdMOpenUsIG9kcG9yw7rEjWFtZSBzaSB2w71zbGVka3kgcG90dnJkacWlIHMgcG9tb2NvdSBBbm92YSB0ZXN0dSBvYm9jaCBtb2RlbG92IGEgcHLDrXBhZG5lIG9wYWtvdmFuw6lobyBSZXNldCBUZXN0dSB1cGxhdG5lbmVuw6lobyBuYSBuZWxpbmXDoXJuZSB0cmFuc2Zvcm1vdmFuw70gbW9kZWwKCmBgYHtyfQptb2RlbCA8LSBsbShMaWZlLmV4cGVjdGFuY3kgfiArMSArIEJNSSArIEdEUCArIFNjaG9vbGluZykKbW9kZWxfc2Nob29saW5nX2t2YWRyIDwtIGxtKExpZmUuZXhwZWN0YW5jeSB+ICsxICsgQk1JICsgR0RQICsgU2Nob29saW5nICsgIEkoU2Nob29saW5nXjIpICsgSShCTUleMikgICkKc3VtbWFyeShtb2RlbF9zY2hvb2xpbmdfa3ZhZHIpCmFub3ZhKG1vZGVsLCBtb2RlbF9zY2hvb2xpbmdfa3ZhZHIpCnJlc2V0dGVzdChtb2RlbF9zY2hvb2xpbmdfa3ZhZHIpCgpgYGAKClRha3RvIG1vZGlmaWtvdmFuw70gbW9kZWx1dsOhZHphIGt2YWRyw6F0IHByZW1lbm5laiBCTUkgYWtvIMWhdGF0aXN0aWNreSB2w716bmFtbsO9IGEgdGllxb4gZG9jaMOhZHphIGsgxI9hbMWhaWVtdSByYXN0dSBrdXByYXZlbsOpaG8ga29lZmljaWVudHUgZGV0ZXJtaW7DoWNpZSAkUl4yX3thZGp9JC4gQWsgdnlsw7rEjWltZSBrdmFkcsOhdCBwcmVtZW5uZWogKlNjaG9vbGluZyosIGt0b3LDvSBqZSDFoXRhdGlzdGlja3kgbmV2w716bmFtbsO9LCBwb3RvbSBkb3RhbmVtZSBuYXNsZWRvdm7DvSB2w71zbGVkb2sKCmBgYHtyfQptb2RlbF9zY2hvb2xpbmdfa3ZhZHIgPC0gbG0oTGlmZS5leHBlY3RhbmN5IH4gKzEgKyBCTUkgKyBHRFAgKyBTY2hvb2xpbmcgICsgSShCTUleMikgICkKc3VtbWFyeShtb2RlbF9zY2hvb2xpbmdfa3ZhZHIpCmBgYApwcmnEjW9tIHVwcmF2ZW7DvSBrb2VmaWNpZW50IGRldGVybWluw6FjaWUgc2EgbmV6bWVuaWwuIFBva3JhxI11amVtZSB0ZWRhIHYgxI9hbMWhb20gdnl1xb7DrXZhbsOtIHRvaHRvIG1vZGVsdS4KCgoKIyMjIDUuIFBvdcW+aXRpZSByb3rFocOtcmVuw6lobyBSRVNFVCB0ZXN0dSAoTkVQT1RSRUJOw4kpCgpgYGB7cn0KbW9kZWxfcm96c2lyZW55IDwtIGxtKExpZmUuZXhwZWN0YW5jeSB+IEJNSSArIEdEUCArIFNjaG9vbGluZyArIEkoQk1JXjIpICsgSShHRFBeMikgKyBJKFNjaG9vbGluZ14yKSApCnN1bW1hcnkobW9kZWxfcm96c2lyZW55KQpgYGAKVHUgdGVzdHVqZW1lLCDEjWkga29lZmljaWVudHkgcHJpIGt2YWRyYXRpY2vDvWNoIMSNbGVub2NoIHPDuiDFoXRhdGlzdGlja3kgdsO9em5hbW7DqSwgYWsgw6FubywgcG90b20gbXVzw61tZSB1cm9iacWlIHTDunRvIGt2YWRyYXRpY2vDuiBtb2RpZmlrw6FjaXUuIEFub3ZhIHRlc3RvbSBtw7TFvmVtZSBwb3RvbSBzbGVkb3ZhxaUsIMSNaSB2IHBvcm92bmFuw60gcyBww7R2b2Ruw71tIG1vZGVsb20gZG/FoWxvIGsgemxlcMWhZW5pdS4gCgpgYGB7cn0KYW5vdmEobW9kZWwsbW9kZWxfcm96c2lyZW55KQpyZXNldHRlc3QobW9kZWxfcm96c2lyZW55KQpgYGAKUHJpIHJlc2V0IHRlc3RlIHR1IHZpZMOtbWUsIMW+ZSBwcm9ibMOpbSBuZXByw6F2bmVqIMWhcGVjaWZpa8OhY2llIHNtZSBlxaF0ZSBuZW9kc3Ryw6FuaWxpLgoKIyMjIDYuIFRyYW5zZm9ybcOhY2lhIHBvbW9jb3UgZHVtbXkgcHJlbWVubmVqIGEgbGluZcOhcm5laiBsb21lbmVqIGZ1bmtjaWUKClByZWRwb2tsYWRham1lLCDFvmUgbcOhbWUgZHVtbXkgcHJlbWVubsO6ICREX3QkLCBrdG9yw6Egb2JzYWh1amUgbGVuIG51bHkgYSBqZWRuacSNa3kuIFRha8OhdG8gcHJlbWVubsOhIHNhIGTDoSB2eXXFvmnFpSBtb2RlbG92YW5pZSB6bG9tb3YsIGEgdG8gbmFwci4KCjEuIHpsb20gdiBhdXRvbsOzbW5vbSDEjWxlbmUgJFxiZXRhXzAkIGEgdG8gbmFzbGVkb3Zub3UgxaFwZWNpZmlrw6FjaW91CiQkeV90ID0gXGJldGFfMCArIFxiZXRhX0QgRCsgXGJldGFfMSB4X3t0MX0gKyBcZG90cyArXGJldGFfayB4X3t0a30gKyB1X3QkJArEjW8gaW50ZXJwcmV0dWplbWUgYWtvIHBvc3VuIHJlZ3Jlc25laiBwcmlhbWt5IChyZWdyZXNuZWogbmFkcm92aW55KSBvICRcYmV0YV9EJCBqZWRub3RpZWsgcG96ZMS6xb4genZpc2xlaiBvc2kgYSB0byBsZW4gdiBwb3pvcm92YW5pYWNoLCBhayBqZSBzcGxuZW7DoSBwb2RtaWVua2EgJERfdCA9IDEkCjIuIHpsb20gdiBza2xvbmUgcmVncmVzbmVqIHByaWFta3kgKG5hZHJvdmlueSkgYSB0byBsZW4gdiBwb3pvcm92YW5pYWNoLCBhayBqZSBzcGxuZW7DoSBwb2RtaWVua2EgJERfdCA9IDEkLCDEjW8gZG9zaWFobmVtZSBuYXNsZWRvdm5vdSDFoXBlY2lmaWvDoWNpb3UKJCR5X3QgPSBcYmV0YV8wICsgIFxiZXRhXzEgeF97dDF9ICsgXGRvdHMgKyBcYmV0YV97aX14X3t0aX0gKyBcYmV0YV97RGl9RF90eF97dGl9KyBcZG90cyArIFxiZXRhX2sgeF97dGt9ICsgdV90JCQKa2RlIHRlZGEgc2tsb24gcHJpYW1reSBwb3pkxLrFviBwcmVtZW5uZSAkeF97dGl9JCBqZSAkXGJldGFfaSQgYWxlIGxlbiB2IHByw61wYWRlICREX3Q9MCQsIGluYWsgamUgdGVuIHNrbG9uIHJvdm7DvSAkXGJldGFfaStcYmV0YV97RF9pfSQuCgpBayBzaSBwb3pyaWVtZSBDK1IgZ3JhZiB2IHByw61wYWRlIHZ5c3ZldMS+dWrDumNlaiB2ZWxpxI1pbnkgKkJNSSosIHbFoWltbmVtZSBzaSwgxb5lIHZ5cm92bmFuw6Ega3JpdmthIG5hZG9idWRuZSBwcmlibGnFvm5lIHByZSBob2Rub3R5ICRCTUk+MjckIGluw70gc2tsb24uIFphdmXEj21lIHByZXRvIHByZW1lbiAqRFVNKiwgcHJlIGt0b3LDuiBidWRlIHBsYXRpxaUgJERVTV90ID0gMSQgYWsgJEJNSV90ID4gMjckIGEgcG9rw7pzbWUgc2EgYW5hbHl6b3ZhxaUgdnBseXYgQk1JIHNlcGFyw6F0bmUgcHJlIG9iaWR2ZSDDunJvdm5lLiAKCgoKS3ZhbnRpZmlrdWptZSB0ZXJheiBtb2RlbCBzbyB6bG9tb20gdiBhdXRvbsOzbW5vbSDEjWxlbmUsIGFrIHRlZGEgJEJNSV90PDI3JCBhIHRlZGEgJERVTV90ID0gMCQgYSAkQk1JX3RcZ2VxIDI3JCBhIHRlZGEgJERVTV90ID0gMSQ6CgoKYGBge3J9CnVkYWplJERVTSA8LSBpZmVsc2UodWRhamUkQk1JIDwgMjcsIDAsIDEpCm1vZGVsRF9hdXRvIDwtIGxtKExpZmUuZXhwZWN0YW5jeSB+ICsxICsgRFVNICsgQk1JICsgR0RQICsgU2Nob29saW5nLGRhdGE9dWRhamUgKSAKc3VtbWFyeShtb2RlbERfYXV0bykKYGBgCgoKViB0b210byBwcsOtcGFkZSBzYSBuZXBvdHZyZGlsbywgxb5lIGJ5IGV4aXN0b3ZhbCBzaWduaWZpa2FudG7DvSB6bG9tIHYgcG9zdW5lIHJlZ3Jlc25laiBuYWRyb3ZpbnkuIAoKCgpgYGB7cn0KCm1vZGVsRF9za2xvbiA8LSBsbShMaWZlLmV4cGVjdGFuY3kgfiArMSAgKyBCTUkgKyBJKERVTSpCTUkpICsgR0RQICsgU2Nob29saW5nLGRhdGE9dWRhamUgKSAKc3VtbWFyeShtb2RlbERfc2tsb24pCmBgYAoKVGVudG9rcsOhdCB1cHJhdmVuw70ga29lZmljaWVudCBkZXRlcm1pbsOhY2llIG5hcsOhc3RvbCwgxI1vIHBvdmHFvnVqZW1lIHphIHZlxL5taSBkb2Jyw7ogdmxhc3Rub3PFpSBtb2RlbHUuIFYgcHLDrXBhZGUga3JhasOtbiBzIG7DrXpreW1pIGhvZG5vdGFtaSBCTUkgaW5kZXh1IG3DtMW+ZW1lIGtvbsWhdGF0b3ZhxaUsIMW+ZSBuw6FyYXN0IEJNSSBvIGplZG51IGplZG5vdGt1IGplIHUgbmljaCBzcHJldsOhZHphbsOpIHMgcG9rbGVzb20gb8SNYWvDoXZhbmVqIGTEusW+a3kgxb5pdm9hdCBvIHByaWJsacW+bmUgMC4yNDQ5IHJva2EgYSB2IHByw61wYWRlIG9zdGF0bsO9Y2gga3JhasOtbiBuw6FyYXN0IEJNSSBvIGplZG51IGplZG5vdGt1IHZ5dm9sw6EgbsOhcmFzdCBvxI1ha8OhdmFuZWogZMS6xb5reSDFvml2b3RhIHByaWJsacW+bmUgbyAtMC4yNDQ5KzAuMjQwNyA9IDAuMDA0MiByb2thLiAgUG9rw7pzbWUgc2EgdGVyYXogb3Rlc3RvdmHFpSB2w716bmFtbm9zxaUgemF2ZWRlbmlhIHpsb211IHYgc2tsb25lIG5hZHJvdmlueQpQb3Jvdm5hbmllIHDDtHZvZG7DqWhvIG1vZGVsdSBhIG1vZGVsdSBzIHByZW1lbmxpdsO9bSBza2xvbm9tIG5hZHJvdmlueSB2eWtvbsOhbWUgcyBwb21vY291IGFub3ZhIHRlc3R1OgoKYGBge3J9CmFub3ZhKG1vZGVsLCBtb2RlbERfc2tsb24pCnJlc2V0dGVzdChtb2RlbERfc2tsb24pCmBgYApOYSB6w6FrbGFkZSBhbm92YSBhbmFsw716eSBtw7TFvmVtZSB0ZWRhIGtvbsWhdGF0b3ZhxaUsIMW+ZSBtb2RlbCB6bGVwxaFpbCBzdm9qZSDFoXRhdGlzdGlja8OpIHZsYXN0bm9zdGksIGFsZSBSYW1zZXkgUmVzZXQgdGVzdCBuYcSPYWxlaiBwb3Zhxb51amUgY2h5YnUgxaFwZWNpZmlrw6FjaWUgbW9kZWx1IHphIHbDvXpuYW1uw7ouCgoKIyMjIDcuIEJveC1Db3hvdiB0cmFuc2Zvcm1hxI1uw70gdGVzdCAobmVwb3RyZWJuw6kpCgpJZGUgbyBzeXN0ZW1hdGlja2VqxaHDrSB0ZXN0IG5hIHppc3RlbmllLCBha28gYnkgc2EgesOhdmlzbMOhIHByZW1lbm7DoSBtYWxhIHRyYW5zZm9ybW92YcWlIHBvbW9jb3UgbmVqYWtlaiBmdW5rY2llIChuYXByLiAkXGxuKFkpJCwgJFxzcXJ0KFkpJCwgJDEvWSQpLiBLb25rcsOpdG5hIHRyYW5zZm9ybcOhY2lhIHNhIHJvYsOtIHBvZMS+YSB2em9yY2EKClxbCllfaV57KFxsYW1iZGEpfSA9ClxiZWdpbntjYXNlc30KXGRmcmFje1lfaV57XGxhbWJkYX0gLSAxfXtcbGFtYmRhfSwgJiBcdGV4dHtpZiB9IFxsYW1iZGEgXG5lcSAwLCBcXFsxMHB0XQpcbG4oWV9pKSwgJiBcdGV4dHtpZiB9IFxsYW1iZGEgPSAwLgpcZW5ke2Nhc2VzfQpcXQoKYGBge3J9CmxpYnJhcnkoTUFTUykKYm94Y294KG1vZGVsKQoKYGBgCgoKKipJbnRlcnByZXTDoWNpYToqKgoKTWF4aW11bSBmdW5rY2llIHZpZXJvaG9kbm9zdGkgZGVmaW51amUgbmFqbGVwxaFpdSBob2Rub3R1ICRcbGFtYmRhJCB6dmlzbG91IGJvZGtvdmFub3UgxI1pYXJvdSwgcHJpxI1vbSAgYWs6CgotICRcbGFtYmRhIFxhcHByb3ggMSQsIHBvdG9tIG5pZSBqZSBwb3RyZWJuw6Egxb5pYWRuYSB0cmFuc2Zvcm3DoWNpYTsKLSAkXGxhbWJkYSBcYXBwcm94IDAkLCBwb3RvbSBqZSBwb3RyZWJuw6EgbG9nYXJpdG1pY2vDoSB0cmFuc2Zvcm3DoWNpYSBwcmVtZW5uZWogJFxsb2coWSkkOwotICRcbGFtYmRhIFxhcHByb3ggMC41JCxwb3RvbSBqZSBwb3RyZWJuw6Egb2Rtb2NuaW5vdsOhIHRyYW5zZm9ybcOhY2lhIHByZW1lbm5laiAkXHNxcnQoWSkkOwotICRcbGFtYmRhIFxhcHByb3ggLTEkLHBvdG9tIGplIHBvdHJlYm7DoSByZWNpcHJva8OhIHRyYW5zZm9ybcOhY2lhIHByZW1lbm5laiAkMS9ZJC4KCktvbmtyw6l0bmEgdHJhbnNmb3Jtw6FjaWEgc2EgcG90b20gdnlrb27DoSBwb2TEvmEgdnpvcmNhOgoKXFsKWV9pXnsoXGxhbWJkYSl9ID0KXGJlZ2lue2Nhc2VzfQpcZGZyYWN7WV9pXntcbGFtYmRhfSAtIDF9e1xsYW1iZGF9LCAmIFx0ZXh0e2lmIH0gXGxhbWJkYSBcbmVxIDAsIFxcWzEwcHRdClxsbihZX2kpLCAmIFx0ZXh0e2lmIH0gXGxhbWJkYSA9IDAuClxlbmR7Y2FzZXN9ClxdCgoKVSBuw6FzIGplIGhvZG5vdGEgJFxsYW1iZGEkIG5pZWtkZSBuYSDDunJvdm5pIDEuOCwgdGVkYSB2IHJlZ3Jlc2lpIHVwcmF2w61tZSB2eXN2ZXTEvm92YW7DuiBwcmVtZW5uw7ogKkxpZmUuZXhwZWN0YW5jeSogYSBvcMOkxaUgc3B1c3TDrW1lIHJlZ3Jlc2l1LgoKYGBge3J9Cm1vZGVsX2xhbWJkYSA8LSBsbShJKChMaWZlLmV4cGVjdGFuY3leMS44LTEpLzEuOCkgfiArMSArIEJNSSArIEdEUCArIFNjaG9vbGluZywgZGF0YSA9IHVkYWplKQpzdW1tYXJ5KG1vZGVsX2xhbWJkYSkKcmVzZXR0ZXN0KG1vZGVsX2xhbWJkYSkKIyB0dSBhbm92YSBwb3Jvdm5hbmllIG1vZGVsdSBuZW1vemVtZSBwb3V6aXQsIGxlYm8gdnlzdmV0xL5vdmFuw6EgcHJlbWVubsOhIHNhIHYgb2JvY2ggbW9kZWxvY2ggbMOtxaFpCmBgYAoKVMOhdG8gw7pwcmF2YSBuw6FtIHPDrWNlIG5lcGF0cm5lIHp2w73FvmlsYSAkUl4yX3thZGp9JCBhbGUgcHJvYmzDqW0gY2h5Ym5laiDFoXBlY2lmaWvDoWNpZSBzYSBuZW9kc3Ryw6FuaWwgKHBvenJpIHAtdmFsdWUgcmVzZXQgdGVzdHUpLiBPa3JlbSB0b2hvIHNtZSBzdHJhdGlsaSBwcsOtamVtbsO6IGludGVycHJldMOhY2l1IG1vZGVsdSAgYSBwcmV0byBvZCBDb3gtQm94b3ZlaiB0cmFuc2Zvcm3DoWNpZSB1cHVzdMOtbWUuCgoKCgoKCg==