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

Pomocou viacnásobnej lineárnej regresie skúmame, do akej miery sú namerané hodnoty teploty ovplyvnené vybranými meteorologickými faktormi – atmosférickým tlakom (Air.Pressure..hPa.) a rýchlosťou vetra (Wind.Speed..m.s.). Cieľom je identifikovať, ktoré z týchto premenných majú štatisticky významny vplyv na vysvetlenie kolísania teploty v sledovanom období.

Výsledok tejto regresie je uvedený nižšie:

#######################################################################
# PRIPRAVA UDAJOV
#######################################################################
udaje <- read.csv("Temperature_2020.csv",dec=",",sep=";",fileEncoding = "Windows-1250",header = TRUE)
# select just the record from 2020
udaje.2020 <- udaje[udaje$Year==2020,c("Temperature...C.","Air.Pressure..hPa.","Wind.Speed..m.s.")]

# data imputation
 
# Compute column medians
#column_medians <- sapply(udaje.2020, median, na.rm = TRUE)
 
# Impute missing values with column medians
# Compute column medians
column_medians <- sapply(udaje.2020, median, na.rm = TRUE)
 
# Impute missing values with column medians
udaje_imputed <- udaje.2020
for (col in names(udaje.2020)) {
  udaje_imputed[[col]][is.na(udaje_imputed[[col]])] <- column_medians[col]
}
 
udaje.2020 <- udaje_imputed
udaje <- udaje.2020

################################################################################
# ZAKLADNA REGRESIA
################################################################################
attach(udaje)
model <- lm(Temperature...C. ~ +1 + Air.Pressure..hPa. + Wind.Speed..m.s., data = udaje)
summary(model)

Call:
lm(formula = Temperature...C. ~ +1 + Air.Pressure..hPa. + Wind.Speed..m.s., 
    data = udaje)

Residuals:
    Min      1Q  Median      3Q 
-8.7467 -3.6719  0.3266  4.0500 
    Max 
 8.9692 

Coefficients:
                   Estimate
(Intercept)        231.5798
Air.Pressure..hPa.  -0.2169
Wind.Speed..m.s.    -3.0605
                   Std. Error
(Intercept)          441.9871
Air.Pressure..hPa.     0.4470
Wind.Speed..m.s.       4.5620
                   t value Pr(>|t|)
(Intercept)          0.524    0.613
Air.Pressure..hPa.  -0.485    0.639
Wind.Speed..m.s.    -0.671    0.519

Residual standard error: 6.55 on 9 degrees of freedom
Multiple R-squared:  0.07576,   Adjusted R-squared:  -0.1296 
F-statistic: 0.3689 on 2 and 9 DF,  p-value: 0.7015

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(Temperature...C. ~ +1 + Air.Pressure..hPa. + Wind.Speed..m.s., data = udaje)

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

    RESET test

data:  model
RESET = 3.6146, df1 = 2, df2 =
7, p-value = 0.08351

Interpretácia:

Na základe výsledkov RESET testu (p-hodnota = 0.08351) nezamietame nulovú hypotézu o správnej špecifikácii modelu. Model teda pravdepodobne neobsahuje štrukturálnu chybu a jeho lineárna forma sa javí ako primeraná.

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)

Graf rezíduí voči vyrovnaným hodnotám ukazuje zreteľné zakrivenie červenej vyhladenej krivky: rezíduá najprv rastú, následne klesajú. Tento systematický tvar naznačuje, že rezíduá nemajú úplne náhodné rozloženie. Môže to poukazovať na miernu nelinearitu v modeli alebo na to, že v modeli chýba ďalšia premenná, ktorá by lepšie zachytila vzťah medzi teplotou a meteorologickými faktormi.

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)

Component + Residual grafy naznačujú, že v modeli sú prítomné nelineárne vzťahy. Pri oboch premenných – Air.Pressure..hPa. aj Wind.Speed..m.s. – vidíme výrazné zakrivenie ružovej vyhladenej krivky, ktoré sa podstatne odchyľuje od modrej lineárnej priamky. Tento rozdiel ukazuje, že lineárna špecifikácia modelu nemusí postačovať na zachytenie skutočného vplyvu týchto premenných na teplotu. Môže byť preto vhodné zvážiť doplnenie modelu o nelineárne členy, napríklad kvadratické transformácie.

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\hat x_{ik}^2 + \dots + \gamma_j\hat 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 premenných Air.Pressure..hPa. a Wind.Speed..m.s., nakoľko nás k tomu motivuje Component + Residual Plot uvedený vyššie. Práve pri týchto premenných sa vyrovnaná krivka výrazne odchyľuje od lineárnej priamky, čo naznačuje potrebu zachytiť nelineárny vzťah.

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(Temperature...C. ~ +1 + Air.Pressure..hPa. + Wind.Speed..m.s.)
model_kvadr <- lm(Temperature...C. ~ +1 + Air.Pressure..hPa. + Wind.Speed..m.s. +  I(Air.Pressure..hPa.^2) + I(Wind.Speed..m.s.^2)  )
summary(model_kvadr)

Call:
lm(formula = Temperature...C. ~ +1 + Air.Pressure..hPa. + Wind.Speed..m.s. + 
    I(Air.Pressure..hPa.^2) + I(Wind.Speed..m.s.^2))

Residuals:
    Min      1Q  Median      3Q 
-5.3190 -1.6501 -0.1829  1.5628 
    Max 
 3.9403 

Coefficients:
                          Estimate
(Intercept)             -2.047e+05
Air.Pressure..hPa.       4.138e+02
Wind.Speed..m.s.         5.144e+01
I(Air.Pressure..hPa.^2) -2.091e-01
I(Wind.Speed..m.s.^2)   -1.389e+01
                        Std. Error
(Intercept)              4.436e+04
Air.Pressure..hPa.       8.959e+01
Wind.Speed..m.s.         1.321e+01
I(Air.Pressure..hPa.^2)  4.524e-02
I(Wind.Speed..m.s.^2)    3.395e+00
                        t value
(Intercept)              -4.615
Air.Pressure..hPa.        4.619
Wind.Speed..m.s.          3.894
I(Air.Pressure..hPa.^2)  -4.623
I(Wind.Speed..m.s.^2)    -4.093
                        Pr(>|t|)   
(Intercept)              0.00244 **
Air.Pressure..hPa.       0.00243 **
Wind.Speed..m.s.         0.00594 **
I(Air.Pressure..hPa.^2)  0.00242 **
I(Wind.Speed..m.s.^2)    0.00462 **
---
Signif. codes:  
  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05
  ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.147 on 7 degrees of freedom
Multiple R-squared:  0.8341,    Adjusted R-squared:  0.7392 
F-statistic: 8.796 on 4 and 7 DF,  p-value: 0.007294
anova(model, model_kvadr)
Analysis of Variance Table

Model 1: Temperature...C. ~ +1 + Air.Pressure..hPa. + Wind.Speed..m.s.
Model 2: Temperature...C. ~ +1 + Air.Pressure..hPa. + Wind.Speed..m.s. + 
    I(Air.Pressure..hPa.^2) + I(Wind.Speed..m.s.^2)
  Res.Df    RSS Df Sum of Sq      F
1      9 386.16                    
2      7  69.33  2    316.83 15.995
    Pr(>F)   
1            
2 0.002452 **
---
Signif. codes:  
  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05
  ‘.’ 0.1 ‘ ’ 1
resettest(model_kvadr)

    RESET test

data:  model_kvadr
RESET = 1.0475, df1 = 2, df2 =
5, p-value = 0.4169

Po rozšírení pôvodného lineárneho modelu o kvadratický člen Air.Pressure..hPa.^2 sa ukazuje, že ani jedna z premenných – vrátane novopridaného nelineárneho prvku – nie je štatisticky významná. P-hodnoty sa pohybujú okolo hranice 0.05, no žiadna ju jednoznačne neprekračuje. Premenná Wind.Speed..m.s. je úplne nevýznamná (p ≈ 0.64) a kvadratický člen tlaku vzduchu má len hraničný efekt (p ≈ 0.053).

Upravený koeficient determinácie (Adjusted R² = 0.2259) naznačuje, že model vysvetľuje iba približne 23 % variability teploty, čo je slabé. Navyše, oproti pôvodnému modelu sa hodnota ešte znížila, takže nelineárne rozšírenie neprinieslo žiadne zlepšenie.

Celkový F-test (p ≈ 0.1827) taktiež potvrdzuje, že model ako celok nie je štatisticky významný.

Z výsledkov teda vyplýva, že pridanie kvadratického člena model nezlepšilo a je vhodnejšie zostať pri pôvodnej lineárnej špecifikácii.

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 sa pozrieme na Component + Residual graf pre vysvetľujúcu premennú Air.Pressure..hPa., môžeme si všimnúť, že vyrovnaná krivka mení svoj tvar pri určitých hodnotách tlaku vzduchu. Najmä v oblasti okolo približne 990 hPa sa krivka odchyľuje od lineárnej priamky, čo naznačuje možnú zmenu sklonu alebo rozdielny vzťah medzi tlakom vzduchu a teplotou. Zaveďme preto pomocnú premennú DUM, pre ktorú bude platiť DUM = 1, ak Air.Pressure..hPa. presiahne určitú hranicu (napr. strednú hodnotu alebo úroveň, kde sa krivka láme), DUM = 0 inak, a pokúsme sa analyzovať vplyv tlaku vzduchu separátne pre obidve úrovne.

Kvantifikujme teraz model so zlomom v autonómnom člene, teda osobitne pre prípady, keď platí Air.Pressure..hPa. je pod touto hranicou (DUM = 0) a keď ju presahuje (DUM = 1).

Takto môžeme overiť, či má tlak vzduchu odlišný vplyv na teplotu v rôznych oblastiach jeho hodnôt.

udaje$DUM <- ifelse(udaje$Air.Pressure..hPa. < 990, 0, 1)
modelD_auto <- lm(Temperature...C. ~ +1 + DUM + Air.Pressure..hPa. + Wind.Speed..m.s.,data=udaje ) 
summary(modelD_auto)

Call:
lm(formula = Temperature...C. ~ +1 + DUM + Air.Pressure..hPa. + 
    Wind.Speed..m.s., data = udaje)

Residuals:
    Min      1Q  Median      3Q 
-8.7943 -4.8371  0.7703  4.6462 
    Max 
 7.3631 

Coefficients:
                    Estimate
(Intercept)        -354.7817
DUM                  -6.3031
Air.Pressure..hPa.    0.3764
Wind.Speed..m.s.     -1.9422
                   Std. Error
(Intercept)          765.1750
DUM                    6.6929
Air.Pressure..hPa.     0.7741
Wind.Speed..m.s.       4.7420
                   t value Pr(>|t|)
(Intercept)         -0.464    0.655
DUM                 -0.942    0.374
Air.Pressure..hPa.   0.486    0.640
Wind.Speed..m.s.    -0.410    0.693

Residual standard error: 6.592 on 8 degrees of freedom
Multiple R-squared:  0.168, Adjusted R-squared:  -0.144 
F-statistic: 0.5385 on 3 and 8 DF,  p-value: 0.6691

Po zavedení dummy premennej DUM, ktorá rozdeľuje hodnoty tlaku vzduchu podľa hranice 990 hPa, sa ukazuje, že tento model nepriniesol žiadne zlepšenie oproti pôvodnej špecifikácii. Hodnota DUM nie je štatisticky významná (p ≈ 0.374), čo znamená, že rozdelenie dát na dve skupiny podľa tlaku vzduchu nemení zachytený vzťah k teplote. Rovnako ani ostatné premenné – Air.Pressure..hPa. (p ≈ 0.640) a Wind.Speed..m.s. (p ≈ 0.693) – nie sú významné, takže model nevie spoľahlivo vysvetliť variabilitu teploty na základe týchto vysvetľujúcich veličín. Koeficient determinácie R² = 0.168 a negatívny upravený Adjusted R² = –0.144 ukazujú, že model má veľmi slabú vysvetľovaciu silu a pridanie dummy premennej dokonca modelu škodí. Aj F-test (p ≈ 0.669) potvrdzuje, že model ako celok nie je štatisticky významný. Celkovo možno konštatovať, že zavedenie premennej DUM nebolo prínosné a tento model neponúka lepší opis teploty než pôvodný lineárny model bez zlomu.


modelD_sklon <- lm(Temperature...C. ~ +1  + Air.Pressure..hPa. + I(DUM*Air.Pressure..hPa.) +  Wind.Speed..m.s.,data=udaje ) 
summary(modelD_sklon)

Call:
lm(formula = Temperature...C. ~ +1 + Air.Pressure..hPa. + I(DUM * 
    Air.Pressure..hPa.) + Wind.Speed..m.s., data = udaje)

Residuals:
    Min      1Q  Median      3Q 
-8.7926 -4.8379  0.7722  4.6261 
    Max 
 7.3438 

Coefficients:
                              Estimate
(Intercept)                 -3.641e+02
Air.Pressure..hPa.           3.858e-01
I(DUM * Air.Pressure..hPa.) -6.432e-03
Wind.Speed..m.s.            -1.929e+00
                            Std. Error
(Intercept)                  7.675e+02
Air.Pressure..hPa.           7.765e-01
I(DUM * Air.Pressure..hPa.)  6.758e-03
Wind.Speed..m.s.             4.738e+00
                            t value
(Intercept)                  -0.474
Air.Pressure..hPa.            0.497
I(DUM * Air.Pressure..hPa.)  -0.952
Wind.Speed..m.s.             -0.407
                            Pr(>|t|)
(Intercept)                    0.648
Air.Pressure..hPa.             0.633
I(DUM * Air.Pressure..hPa.)    0.369
Wind.Speed..m.s.               0.695

Residual standard error: 6.585 on 8 degrees of freedom
Multiple R-squared:  0.1698,    Adjusted R-squared:  -0.1416 
F-statistic: 0.5453 on 3 and 8 DF,  p-value: 0.665

V modeli so zlomom v sklone – teda po pridaní interakčného člena DUM × Air.Pressure..hPa. – sa ukazuje, že žiadny z koeficientov nie je štatisticky významný. Lineárny efekt tlaku vzduchu (p ≈ 0.633), interakčný člen (p ≈ 0.369) aj rýchlosť vetra (p ≈ 0.695) majú vysoké p-hodnoty, takže model nepreukazuje rozdielny sklon regresnej priamky pre dve úrovne tlaku (DUM = 0 a DUM = 1). Koeficient determinácie R² = 0.1698 a negatívny Adjusted R² = –0.1416 ukazujú, že model vysvetľuje len veľmi malú časť variability teploty a po penalizácii dokonca pôsobí, akoby bol horší než model bez vysvetľujúcich premenných. Aj F-test (p ≈ 0.665) potvrdzuje, že model ako celok nie je štatisticky významný. Z toho vyplýva, že zavedenie zlomu v sklone neprinieslo žiadne zlepšenie. Interakčný člen nebol prínosný a model stále nedokáže opísať závislosť teploty od tlaku vzduchu a rýchlosti vetra. Vhodnejšie je zostať pri jednoduchšom lineárnom modeli.

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: Temperature...C. ~ +1 + Air.Pressure..hPa. + Wind.Speed..m.s.
Model 2: Temperature...C. ~ +1 + Air.Pressure..hPa. + I(DUM * Air.Pressure..hPa.) + 
    Wind.Speed..m.s.
  Res.Df    RSS Df Sum of Sq      F
1      9 386.16                    
2      8 346.88  1    39.275 0.9058
  Pr(>F)
1       
2 0.3691
resettest(modelD_sklon)

    RESET test

data:  modelD_sklon
RESET = 0.32042, df1 = 2, df2 =
6, p-value = 0.7375

ANOVA test porovnáva pôvodný lineárny model s rozšíreným modelom obsahujúcim interakčný člen DUM × Air.Pressure..hPa.. Výsledok (p ≈ 0.3691) ukazuje, že rozšírený model nie je štatisticky lepší než pôvodný. Pridanie interakčného termu teda nezlepšilo vysvetľovaciu schopnosť modelu. RESET test pre modelD_sklon má p-hodnotu ≈ 0.7375, čo znamená, že nezamietame nulovú hypotézu o správnej špecifikácii modelu. Inými slovami, nevidíme dôkazy o nesprávnej funkčnej forme, a pridanie ďalšej nelinearity nie je potrebné. Ani ANOVA test, ani RESET test neposkytujú dôvod na to, aby sme uprednostnili model so zlomom v sklone. Rozšírený model nepriniesol žiadne zlepšenie, a pôvodný jednoduchší lineárny model je postačujúci.

LS0tCnRpdGxlOiAixaBwZWNpZmlrw6FjaWEgbW9kZWx1IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKYXV0aG9yOiBWZXJvbmlrYSBSaXpzbnlvdnN6a8OhCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgpgYGB7cn0KbGlicmFyeSh6b28pCmxpYnJhcnkodHNlcmllcykKbGlicmFyeShsbXRlc3QpCmxpYnJhcnkoc2FuZHdpY2gpCmxpYnJhcnkoY2FyKQpybShsaXN0PWxzKCkpCmBgYAoKUG9tb2NvdSB2aWFjbsOhc29ibmVqIGxpbmXDoXJuZWogcmVncmVzaWUgc2vDum1hbWUsIGRvIGFrZWogbWllcnkgc8O6IG5hbWVyYW7DqSBob2Rub3R5IHRlcGxvdHkgb3ZwbHl2bmVuw6kgdnlicmFuw71taSBtZXRlb3JvbG9naWNrw71taSBmYWt0b3JtaSDigJMgYXRtb3Nmw6lyaWNrw71tIHRsYWtvbSAoQWlyLlByZXNzdXJlLi5oUGEuKSBhIHLDvWNobG9zxaVvdSB2ZXRyYSAoV2luZC5TcGVlZC4ubS5zLikuIENpZcS+b20gamUgaWRlbnRpZmlrb3ZhxaUsIGt0b3LDqSB6IHTDvWNodG8gcHJlbWVubsO9Y2ggbWFqw7ogxaF0YXRpc3RpY2t5IHbDvXpuYW1ueSB2cGx5diBuYSB2eXN2ZXRsZW5pZSBrb2zDrXNhbmlhIHRlcGxvdHkgdiBzbGVkb3Zhbm9tIG9iZG9iw60uCgpWw71zbGVkb2sgdGVqdG8gcmVncmVzaWUgamUgdXZlZGVuw70gbmnFvsWhaWU6CgpgYGB7cn0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBQUklQUkFWQSBVREFKT1YKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKdWRhamUgPC0gcmVhZC5jc3YoIlRlbXBlcmF0dXJlXzIwMjAuY3N2IixkZWM9IiwiLHNlcD0iOyIsZmlsZUVuY29kaW5nID0gIldpbmRvd3MtMTI1MCIsaGVhZGVyID0gVFJVRSkKIyBzZWxlY3QganVzdCB0aGUgcmVjb3JkIGZyb20gMjAyMAp1ZGFqZS4yMDIwIDwtIHVkYWplW3VkYWplJFllYXI9PTIwMjAsYygiVGVtcGVyYXR1cmUuLi5DLiIsIkFpci5QcmVzc3VyZS4uaFBhLiIsIldpbmQuU3BlZWQuLm0ucy4iKV0KCiMgZGF0YSBpbXB1dGF0aW9uCiAKIyBDb21wdXRlIGNvbHVtbiBtZWRpYW5zCiNjb2x1bW5fbWVkaWFucyA8LSBzYXBwbHkodWRhamUuMjAyMCwgbWVkaWFuLCBuYS5ybSA9IFRSVUUpCiAKIyBJbXB1dGUgbWlzc2luZyB2YWx1ZXMgd2l0aCBjb2x1bW4gbWVkaWFucwojIENvbXB1dGUgY29sdW1uIG1lZGlhbnMKY29sdW1uX21lZGlhbnMgPC0gc2FwcGx5KHVkYWplLjIwMjAsIG1lZGlhbiwgbmEucm0gPSBUUlVFKQogCiMgSW1wdXRlIG1pc3NpbmcgdmFsdWVzIHdpdGggY29sdW1uIG1lZGlhbnMKdWRhamVfaW1wdXRlZCA8LSB1ZGFqZS4yMDIwCmZvciAoY29sIGluIG5hbWVzKHVkYWplLjIwMjApKSB7CiAgdWRhamVfaW1wdXRlZFtbY29sXV1baXMubmEodWRhamVfaW1wdXRlZFtbY29sXV0pXSA8LSBjb2x1bW5fbWVkaWFuc1tjb2xdCn0KIAp1ZGFqZS4yMDIwIDwtIHVkYWplX2ltcHV0ZWQKdWRhamUgPC0gdWRhamUuMjAyMAoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBaQUtMQUROQSBSRUdSRVNJQQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwphdHRhY2godWRhamUpCm1vZGVsIDwtIGxtKFRlbXBlcmF0dXJlLi4uQy4gfiArMSArIEFpci5QcmVzc3VyZS4uaFBhLiArIFdpbmQuU3BlZWQuLm0ucy4sIGRhdGEgPSB1ZGFqZSkKc3VtbWFyeShtb2RlbCkKYGBgCgpUZXN0b3ZhxaUsIMSNaSBqZSBtb2RlbCB2IHNwcsOhdm5laiBmdW5rxI1uZWogZm9ybWUgKHQuIGouIMSNaSBqZSBsaW5lw6FybmEgxaFwZWNpZmlrw6FjaWEgdmhvZG7DoSwgYWxlYm8gxI1pIGJ5IHN0ZSBtYWxpIHRyYW5zZm9ybW92YcWlIHByZW1lbm7DqSwgbmFwcsOta2xhZCBwb21vY291IGxvZ2FyaXRtb3YgYWxlYm8gbW9jbmluYW1pKSwgbW/Fvm5vIHZ5a29uYcWlIHZpYWNlcsO9bWkgc3DDtHNvYm1pLgoKIyMjIDEuIFRlc3QgUkVTRVQgKHRlc3QgY2h5YnkgxaFwZWNpZmlrw6FjaWUgUmFtc2V5aG8gcmVncmVzbmVqIHJvdm5pY2UgLSBSYW1zZXkgUmVzZXQgVGVzdCkKCklkZSBvIG5hasWhdGFuZGFyZG5lasWhw60gZm9ybcOhbG55IHRlc3QgbmVzcHLDoXZuZWogxaFwZWNpZmlrw6FjaWUgZnVua8SNbmVqIGZvcm15IGFsZSBkw6Egc2EgcG91xb5pxaUgYWogcHJlIHByw61wYWR5LCBhayBzbWUgbmXFoXBlY2lmaWtvdmFsaSB2xaFldGt5IHZ5c3ZldMS+dWrDumNlIHByZW1lbm7DqS4KCk15xaFsaWVua2E6IE5lY2ggcMO0dm9kbsO9IG1vZGVsIG3DoSB0dmFyCiQkeV90ID0gXGJldGFfMCArIFxiZXRhXzEgeF97dDEwfSArIFxkb3RzICtcYmV0YV9rIHhfe3RrfSArIHVfdCQkCkFrIGplIHbDocWhIG1vZGVsIHNwcsOhdm5lIMWhcGVjaWZpa292YW7DvSwgcG90b20gcHJpZGFuw61tIG1vY27DrW4gdnlyb3ZuYW7DvWNoIGhvZG7DtHQgKG5hcHIuICRcaGF0IHlfdF4yJCwgJFxoYXR7eX1fdF4zJCkgYnkgc2EgcMO0dm9kbsO9IG1vZGVsIG5lbWFsIHBvZHN0YXRuZSB6bGVwxaFpxaUsIHRlZGEgYnVkZW1lIHRlc3RvdmHFpSBww7R2b2Ruw70gbW9kZWwgdXZlZGVuw70gdnnFocWhaWUgYSBtb2RlbAoKJCR5X3QgPSBcYmV0YV8wICsgXGJldGFfMSB4X3t0MTB9ICsgXGRvdHMgK1xiZXRhX2sgeF97dGt9ICsgXGdhbW1hXzJcaGF0IHlfdF4yICsgXGdhbW1hXzNcaGF0e3l9X3ReMyArIHVfdCQkCgpCdWRlbWUgdGVzdG92YcWlIGh5cG90w6l6dSAKCiRIXzA6JCBtb2RlbCBqZSBzcHLDoXZuZSDFoXBlY2lmaWtvdmFuw70gKCRcZ2FtbWFfMiA9IFxnYW1tYV8zID0gMCQpCgpvcHJvdGkKCiRIXzE6JCBtb2RlbCBqZSBuZXNwcsOhdm5lIMWhcGVjaWZpa292YW7DvSAoJFxnYW1tYV8yIFxuZSAwICBccXVhZCBcdGV4dHthbGVib30gIFxxdWFkIFxnYW1tYV8zIFxuZSAwJCkKCmBgYHtyfQojIFN1cHBvc2UgeW91ciBtb2RlbCBpczoKbW9kZWwgPC0gbG0oVGVtcGVyYXR1cmUuLi5DLiB+ICsxICsgQWlyLlByZXNzdXJlLi5oUGEuICsgV2luZC5TcGVlZC4ubS5zLiwgZGF0YSA9IHVkYWplKQoKIyBSRVNFVCB0ZXN0IGZyb20gJ2xtdGVzdCcgcGFja2FnZToKbGlicmFyeShsbXRlc3QpCnJlc2V0dGVzdChtb2RlbCkKCmBgYAoKKipJbnRlcnByZXTDoWNpYToqKgoKTmEgesOha2xhZGUgdsO9c2xlZGtvdiBSRVNFVCB0ZXN0dSAocC1ob2Rub3RhID0gMC4wODM1MSkgbmV6YW1pZXRhbWUgbnVsb3bDuiBoeXBvdMOpenUgbyBzcHLDoXZuZWogxaFwZWNpZmlrw6FjaWkgbW9kZWx1LiBNb2RlbCB0ZWRhIHByYXZkZXBvZG9ibmUgbmVvYnNhaHVqZSDFoXRydWt0dXLDoWxudSBjaHlidSBhIGplaG8gbGluZcOhcm5hIGZvcm1hIHNhIGphdsOtIGFrbyBwcmltZXJhbsOhLgoKIyMjIDIuIEdyYWZpY2vDoSBhbmFsw716YQoKIyMjIyBHcmFmICpSZXNpZHVhbHMgdnMuIEZpdHRlZCoKCkdyYWZpY2vDoSBhbmFsw716YSB2esWlYWh1IG1lZHppIHZ5cm92bmFuw71taSBob2Rub3RhbWkgbsOhaG9kbmVqIHByZW1lbm5laiBhIHJlesOtZHVhbWk6CgpgYGB7cn0KcGxvdChtb2RlbCwgd2hpY2ggPSAxKQpgYGAKCkdyYWYgcmV6w61kdcOtIHZvxI1pIHZ5cm92bmFuw71tIGhvZG5vdMOhbSB1a2F6dWplIHpyZXRlxL5uw6kgemFrcml2ZW5pZSDEjWVydmVuZWogdnlobGFkZW5laiBrcml2a3k6IHJlesOtZHXDoSBuYWpwcnYgcmFzdMO6LCBuw6FzbGVkbmUga2xlc2Fqw7ouIFRlbnRvIHN5c3RlbWF0aWNrw70gdHZhciBuYXpuYcSNdWplLCDFvmUgcmV6w61kdcOhIG5lbWFqw7ogw7pwbG5lIG7DoWhvZG7DqSByb3psb8W+ZW5pZS4gTcO0xb5lIHRvIHBvdWthem92YcWlIG5hIG1pZXJudSBuZWxpbmVhcml0dSB2IG1vZGVsaSBhbGVibyBuYSB0bywgxb5lIHYgbW9kZWxpIGNow71iYSDEj2FsxaFpYSBwcmVtZW5uw6EsIGt0b3LDoSBieSBsZXDFoWllIHphY2h5dGlsYSB2esWlYWggbWVkemkgdGVwbG90b3UgYSBtZXRlb3JvbG9naWNrw71taSBmYWt0b3JtaS4KCiMjIyMgR3JhZnkgQytSICoqCgpUw6F0byBhbmFsw716YSBuw6FtIG3DtMW+ZSBwb23DtGPFpSBwcmkgaMS+YWRhbsOtIG9kcG92ZWRlIG5hIG90w6F6a3UsIGt0b3LDuiBwcmVtZW5uw7ogYnkgc21lIG1hbGkgdHJhbnNmb21vdmHFpSBwb21vY291IG5lamFrZWogem7DoW1laiBmdW5rY2llLiBWeWNow6FkemFqbWUgeiBww7R2b2RuZWogcm92bmljZQoKJCR5X3QgPSBcYmV0YV8wICsgXGJldGFfMSB4X3t0MX0gKyBcZG90cyArXGJldGFfayB4X3t0a30gKyB1X3QkJApUw7p0byByb3ZuaWN1IG5hanBydiBvZGhhZG5lbWUgYSBwb3RvbSB2eWtyZXPEvnVqZW1lIGdyYWZ5LCBrZGUgdsO9cmF6IGNvbXBvbmVudCtyZXNpZHVhbCAoQytSKSBwbG90IHZ5a3Jlc8S+dWplIG5hIHp2aXNsZWogb3NpICRcaGF0e1xiZXRhfV9peF97dGl9K2VfdCQgYSBuYSB2b2Rvcm92bmVqIG9zaSB2eWtyZXPEvnVqZSBob2Rub3R5ICR4X3t0aX0kCgpUaWV0byBncmFmeSBwb23DoWhhasO6IGlkZW50aWZpa292YcWlIG5lbGluZcOhcm5lIHZ6xaVhaHkgcHJlIGthxb5kw70gcmVncmVzb3I6CgpgYGB7cn0KY2FyOjpjclBsb3RzKG1vZGVsKQoKYGBgCkNvbXBvbmVudCArIFJlc2lkdWFsIGdyYWZ5IG5hem5hxI11asO6LCDFvmUgdiBtb2RlbGkgc8O6IHByw610b21uw6kgbmVsaW5lw6FybmUgdnrFpWFoeS4gUHJpIG9ib2NoIHByZW1lbm7DvWNoIOKAkyBBaXIuUHJlc3N1cmUuLmhQYS4gYWogV2luZC5TcGVlZC4ubS5zLiDigJMgdmlkw61tZSB2w71yYXpuw6kgemFrcml2ZW5pZSBydcW+b3ZlaiB2eWhsYWRlbmVqIGtyaXZreSwga3RvcsOpIHNhIHBvZHN0YXRuZSBvZGNoecS+dWplIG9kIG1vZHJlaiBsaW5lw6FybmVqIHByaWFta3kuIFRlbnRvIHJvemRpZWwgdWthenVqZSwgxb5lIGxpbmXDoXJuYSDFoXBlY2lmaWvDoWNpYSBtb2RlbHUgbmVtdXPDrSBwb3N0YcSNb3ZhxaUgbmEgemFjaHl0ZW5pZSBza3V0b8SNbsOpaG8gdnBseXZ1IHTDvWNodG8gcHJlbWVubsO9Y2ggbmEgdGVwbG90dS4gTcO0xb5lIGJ5xaUgcHJldG8gdmhvZG7DqSB6dsOhxb5pxaUgZG9wbG5lbmllIG1vZGVsdSBvIG5lbGluZcOhcm5lIMSNbGVueSwgbmFwcsOta2xhZCBrdmFkcmF0aWNrw6kgdHJhbnNmb3Jtw6FjaWUuCgojIyAzLiBOZWxpbmXDoXJuYSDFoXBlY2lmaWvDoWNpYQoKxIxhc3Rva3LDoXQgbcO0xb5lbWUgYWogemxvxb5pdGVqxaFpZSBuZWxpbmXDoXJuZSB2esWlYWh5IG1vZGVsb3ZhxaUgcyBwb21vY291IGljaCBhcHJveGltw6FjaWUgcyBwb2x5bsOzbW9tLCB0ZWRhIHYgcHLDrXBhZGUga3ZhZHJhdGlja8O9Y2ggxI1sZW5vdgoKJCR5X3QgPSBcYmV0YV8wICsgXGJldGFfMSB4X3t0MTB9ICsgXGRvdHMgK1xiZXRhX2sgeF97dGt9ICsgXGRvdHMgKyBcZ2FtbWFfaVxoYXQgeF97aWt9XjIgKyBcZG90cyArIFxnYW1tYV9qXGhhdCB4X3tqa31eMiArIFxkb3RzICsgdV90JCQKUHLDrWtsYWQgbmEgdMO6dG8gbW9kaWZpa8OhY2l1IHV2aWTDrW1lIG5pxb7FoWllLgoKIyMgMy4gUG9yb3ZuYW5pZSB6w6FrbGFkbsOpaG8gYSBtb2RpZmlrb3ZhbsOpaG8gbW9kZWx1CgpQcmVkcG9rbGFkYWptZSwgxb5lIHNhIHByaSBuZWxpbmXDoXJueWNoIMO6cHJhdsOhY2ggcMO0dm9kbmVqIHJvdm5pY2UgZG9zdGFuZW1lIGsgemF2ZWRlbml1IGt2YWRyw6F0dSBwcmVtZW5uw71jaCBBaXIuUHJlc3N1cmUuLmhQYS4gYSBXaW5kLlNwZWVkLi5tLnMuLCBuYWtvxL5rbyBuw6FzIGsgdG9tdSBtb3RpdnVqZSBDb21wb25lbnQgKyBSZXNpZHVhbCBQbG90IHV2ZWRlbsO9IHZ5xaHFoWllLiBQcsOhdmUgcHJpIHTDvWNodG8gcHJlbWVubsO9Y2ggc2Egdnlyb3ZuYW7DoSBrcml2a2EgdsO9cmF6bmUgb2RjaHnEvnVqZSBvZCBsaW5lw6FybmVqIHByaWFta3ksIMSNbyBuYXpuYcSNdWplIHBvdHJlYnUgemFjaHl0acWlIG5lbGluZcOhcm55IHZ6xaVhaC4gCgpBayBtw6EgdHJhbnNmb3Jtb3ZhbsO9IG1vZGVsIHZ5xaHFocOtIHVwcmF2ZW7DvSBrb2VmY2llbnQgZGV0ZXJtaW7DoWNpZSAkUl4yX3thZGp9JCBhIHByaSBSRVNFVCB0ZXN0IHByaWptZW1lIGFsdGVybmF0w612bnUgaHlwb3TDqXp1LCBvZHBvcsO6xI1hbWUgc2kgdsO9c2xlZGt5IHBvdHZyZGnFpSBzIHBvbW9jb3UgQW5vdmEgdGVzdHUgb2JvY2ggbW9kZWxvdiBhIHByw61wYWRuZSBvcGFrb3ZhbsOpaG8gUmVzZXQgVGVzdHUgdXBsYXRuZW5lbsOpaG8gbmEgbmVsaW5lw6FybmUgdHJhbnNmb3Jtb3ZhbsO9IG1vZGVsCgpgYGB7cn0KbW9kZWwgPC0gbG0oVGVtcGVyYXR1cmUuLi5DLiB+ICsxICsgQWlyLlByZXNzdXJlLi5oUGEuICsgV2luZC5TcGVlZC4ubS5zLikKbW9kZWxfa3ZhZHIgPC0gbG0oVGVtcGVyYXR1cmUuLi5DLiB+ICsxICsgQWlyLlByZXNzdXJlLi5oUGEuICsgV2luZC5TcGVlZC4ubS5zLiArICBJKEFpci5QcmVzc3VyZS4uaFBhLl4yKSArIEkoV2luZC5TcGVlZC4ubS5zLl4yKSAgKQpzdW1tYXJ5KG1vZGVsX2t2YWRyKQphbm92YShtb2RlbCwgbW9kZWxfa3ZhZHIpCnJlc2V0dGVzdChtb2RlbF9rdmFkcikKCmBgYAoKUG8gcm96xaHDrXJlbsOtIHDDtHZvZG7DqWhvIGxpbmXDoXJuZWhvIG1vZGVsdSBvIGt2YWRyYXRpY2vDvSDEjWxlbiBBaXIuUHJlc3N1cmUuLmhQYS5eMiBzYSB1a2F6dWplLCDFvmUgYW5pIGplZG5hIHogcHJlbWVubsO9Y2gg4oCTIHZyw6F0YW5lIG5vdm9wcmlkYW7DqWhvIG5lbGluZcOhcm5laG8gcHJ2a3Ug4oCTIG5pZSBqZSDFoXRhdGlzdGlja3kgdsO9em5hbW7DoS4gUC1ob2Rub3R5IHNhIHBvaHlidWrDuiBva29sbyBocmFuaWNlIDAuMDUsIG5vIMW+aWFkbmEganUgamVkbm96bmHEjW5lIG5lcHJla3JhxI11amUuIFByZW1lbm7DoSBXaW5kLlNwZWVkLi5tLnMuIGplIMO6cGxuZSBuZXbDvXpuYW1uw6EgKHAg4omIIDAuNjQpIGEga3ZhZHJhdGlja8O9IMSNbGVuIHRsYWt1IHZ6ZHVjaHUgbcOhIGxlbiBocmFuacSNbsO9IGVmZWt0IChwIOKJiCAwLjA1MykuCgpVcHJhdmVuw70ga29lZmljaWVudCBkZXRlcm1pbsOhY2llIChBZGp1c3RlZCBSwrIgPSAwLjIyNTkpIG5hem5hxI11amUsIMW+ZSBtb2RlbCB2eXN2ZXTEvnVqZSBpYmEgcHJpYmxpxb5uZSAyMyAlIHZhcmlhYmlsaXR5IHRlcGxvdHksIMSNbyBqZSBzbGFiw6kuIE5hdnnFoWUsIG9wcm90aSBww7R2b2Ruw6ltdSBtb2RlbHUgc2EgaG9kbm90YSBlxaF0ZSB6bsOtxb5pbGEsIHRha8W+ZSBuZWxpbmXDoXJuZSByb3rFocOtcmVuaWUgbmVwcmluaWVzbG8gxb5pYWRuZSB6bGVwxaFlbmllLgoKQ2Vsa292w70gRi10ZXN0IChwIOKJiCAwLjE4MjcpIHRha3RpZcW+IHBvdHZyZHp1amUsIMW+ZSBtb2RlbCBha28gY2Vsb2sgbmllIGplIMWhdGF0aXN0aWNreSB2w716bmFtbsO9LgoKWiB2w71zbGVka292IHRlZGEgdnlwbMO9dmEsIMW+ZSBwcmlkYW5pZSBrdmFkcmF0aWNrw6lobyDEjWxlbmEgbW9kZWwgbmV6bGVwxaFpbG8gYSBqZSB2aG9kbmVqxaFpZSB6b3N0YcWlIHByaSBww7R2b2RuZWogbGluZcOhcm5laiDFoXBlY2lmaWvDoWNpaS4KCgojIyMgNi4gVHJhbnNmb3Jtw6FjaWEgcG9tb2NvdSBkdW1teSBwcmVtZW5uZWogYSBsaW5lw6FybmVqIGxvbWVuZWogZnVua2NpZQoKUHJlZHBva2xhZGFqbWUsIMW+ZSBtw6FtZSBkdW1teSBwcmVtZW5uw7ogJERfdCQsIGt0b3LDoSBvYnNhaHVqZSBsZW4gbnVseSBhIGplZG5pxI1reS4gVGFrw6F0byBwcmVtZW5uw6Egc2EgZMOhIHZ5dcW+acWlIG1vZGVsb3ZhbmllIHpsb21vdiwgYSB0byBuYXByLgoKMS4gemxvbSB2IGF1dG9uw7Ntbm9tIMSNbGVuZSAkXGJldGFfMCQgYSB0byBuYXNsZWRvdm5vdSDFoXBlY2lmaWvDoWNpb3UKJCR5X3QgPSBcYmV0YV8wICsgXGJldGFfRCBEKyBcYmV0YV8xIHhfe3QxfSArIFxkb3RzICtcYmV0YV9rIHhfe3RrfSArIHVfdCQkCsSNbyBpbnRlcnByZXR1amVtZSBha28gcG9zdW4gcmVncmVzbmVqIHByaWFta3kgKHJlZ3Jlc25laiBuYWRyb3ZpbnkpIG8gJFxiZXRhX0QkIGplZG5vdGllayBwb3pkxLrFviB6dmlzbGVqIG9zaSBhIHRvIGxlbiB2IHBvem9yb3ZhbmlhY2gsIGFrIGplIHNwbG5lbsOhIHBvZG1pZW5rYSAkRF90ID0gMSQKMi4gemxvbSB2IHNrbG9uZSByZWdyZXNuZWogcHJpYW1reSAobmFkcm92aW55KSBhIHRvIGxlbiB2IHBvem9yb3ZhbmlhY2gsIGFrIGplIHNwbG5lbsOhIHBvZG1pZW5rYSAkRF90ID0gMSQsIMSNbyBkb3NpYWhuZW1lIG5hc2xlZG92bm91IMWhcGVjaWZpa8OhY2lvdQokJHlfdCA9IFxiZXRhXzAgKyAgXGJldGFfMSB4X3t0MX0gKyBcZG90cyArIFxiZXRhX3tpfXhfe3RpfSArIFxiZXRhX3tEaX1EX3R4X3t0aX0rIFxkb3RzICsgXGJldGFfayB4X3t0a30gKyB1X3QkJAprZGUgdGVkYSBza2xvbiBwcmlhbWt5IHBvemTEusW+IHByZW1lbm5lICR4X3t0aX0kIGplICRcYmV0YV9pJCBhbGUgbGVuIHYgcHLDrXBhZGUgJERfdD0wJCwgaW5hayBqZSB0ZW4gc2tsb24gcm92bsO9ICRcYmV0YV9pK1xiZXRhX3tEX2l9JC4KCkFrIHNhIHBvenJpZW1lIG5hIENvbXBvbmVudCArIFJlc2lkdWFsIGdyYWYgcHJlIHZ5c3ZldMS+dWrDumN1IHByZW1lbm7DuiBBaXIuUHJlc3N1cmUuLmhQYS4sIG3DtMW+ZW1lIHNpIHbFoWltbsO6xaUsIMW+ZSB2eXJvdm5hbsOhIGtyaXZrYSBtZW7DrSBzdm9qIHR2YXIgcHJpIHVyxI1pdMO9Y2ggaG9kbm90w6FjaCB0bGFrdSB2emR1Y2h1LiBOYWptw6QgdiBvYmxhc3RpIG9rb2xvIHByaWJsacW+bmUgOTkwIGhQYSBzYSBrcml2a2Egb2RjaHnEvnVqZSBvZCBsaW5lw6FybmVqIHByaWFta3ksIMSNbyBuYXpuYcSNdWplIG1vxb5uw7ogem1lbnUgc2tsb251IGFsZWJvIHJvemRpZWxueSB2esWlYWggbWVkemkgdGxha29tIHZ6ZHVjaHUgYSB0ZXBsb3RvdS4KWmF2ZcSPbWUgcHJldG8gcG9tb2Nuw7ogcHJlbWVubsO6IERVTSwgcHJlIGt0b3LDuiBidWRlIHBsYXRpxaUgRFVNID0gMSwgYWsgQWlyLlByZXNzdXJlLi5oUGEuIHByZXNpYWhuZSB1csSNaXTDuiBocmFuaWN1IChuYXByLiBzdHJlZG7DuiBob2Rub3R1IGFsZWJvIMO6cm92ZcWILCBrZGUgc2Ega3JpdmthIGzDoW1lKSwgRFVNID0gMCBpbmFrLCBhIHBva8O6c21lIHNhIGFuYWx5em92YcWlIHZwbHl2IHRsYWt1IHZ6ZHVjaHUgc2VwYXLDoXRuZSBwcmUgb2JpZHZlIMO6cm92bmUuCgpLdmFudGlmaWt1am1lIHRlcmF6IG1vZGVsIHNvIHpsb21vbSB2IGF1dG9uw7Ntbm9tIMSNbGVuZSwgdGVkYSBvc29iaXRuZSBwcmUgcHLDrXBhZHksIGtlxI8gcGxhdMOtIEFpci5QcmVzc3VyZS4uaFBhLiBqZSBwb2QgdG91dG8gaHJhbmljb3UgKERVTSA9IDApIGEga2XEjyBqdSBwcmVzYWh1amUgKERVTSA9IDEpLgoKVGFrdG8gbcO0xb5lbWUgb3ZlcmnFpSwgxI1pIG3DoSB0bGFrIHZ6ZHVjaHUgb2RsacWhbsO9IHZwbHl2IG5hIHRlcGxvdHUgdiByw7R6bnljaCBvYmxhc3RpYWNoIGplaG8gaG9kbsO0dC4KCgpgYGB7cn0KdWRhamUkRFVNIDwtIGlmZWxzZSh1ZGFqZSRBaXIuUHJlc3N1cmUuLmhQYS4gPCA5OTAsIDAsIDEpCm1vZGVsRF9hdXRvIDwtIGxtKFRlbXBlcmF0dXJlLi4uQy4gfiArMSArIERVTSArIEFpci5QcmVzc3VyZS4uaFBhLiArIFdpbmQuU3BlZWQuLm0ucy4sZGF0YT11ZGFqZSApIApzdW1tYXJ5KG1vZGVsRF9hdXRvKQpgYGAKUG8gemF2ZWRlbsOtIGR1bW15IHByZW1lbm5laiBEVU0sIGt0b3LDoSByb3pkZcS+dWplIGhvZG5vdHkgdGxha3UgdnpkdWNodSBwb2TEvmEgaHJhbmljZSA5OTAgaFBhLCBzYSB1a2F6dWplLCDFvmUgdGVudG8gbW9kZWwgbmVwcmluaWVzb2wgxb5pYWRuZSB6bGVwxaFlbmllIG9wcm90aSBww7R2b2RuZWogxaFwZWNpZmlrw6FjaWkuIEhvZG5vdGEgRFVNIG5pZSBqZSDFoXRhdGlzdGlja3kgdsO9em5hbW7DoSAocCDiiYggMC4zNzQpLCDEjW8gem5hbWVuw6EsIMW+ZSByb3pkZWxlbmllIGTDoXQgbmEgZHZlIHNrdXBpbnkgcG9kxL5hIHRsYWt1IHZ6ZHVjaHUgbmVtZW7DrSB6YWNoeXRlbsO9IHZ6xaVhaCBrIHRlcGxvdGUuClJvdm5ha28gYW5pIG9zdGF0bsOpIHByZW1lbm7DqSDigJMgQWlyLlByZXNzdXJlLi5oUGEuIChwIOKJiCAwLjY0MCkgYSBXaW5kLlNwZWVkLi5tLnMuIChwIOKJiCAwLjY5Mykg4oCTIG5pZSBzw7ogdsO9em5hbW7DqSwgdGFrxb5lIG1vZGVsIG5ldmllIHNwb8S+YWhsaXZvIHZ5c3ZldGxpxaUgdmFyaWFiaWxpdHUgdGVwbG90eSBuYSB6w6FrbGFkZSB0w71jaHRvIHZ5c3ZldMS+dWrDumNpY2ggdmVsacSNw61uLgpLb2VmaWNpZW50IGRldGVybWluw6FjaWUgUsKyID0gMC4xNjggYSBuZWdhdMOtdm55IHVwcmF2ZW7DvSBBZGp1c3RlZCBSwrIgPSDigJMwLjE0NCB1a2F6dWrDuiwgxb5lIG1vZGVsIG3DoSB2ZcS+bWkgc2xhYsO6IHZ5c3ZldMS+b3ZhY2l1IHNpbHUgYSBwcmlkYW5pZSBkdW1teSBwcmVtZW5uZWogZG9rb25jYSBtb2RlbHUgxaFrb2TDrS4gQWogRi10ZXN0IChwIOKJiCAwLjY2OSkgcG90dnJkenVqZSwgxb5lIG1vZGVsIGFrbyBjZWxvayBuaWUgamUgxaF0YXRpc3RpY2t5IHbDvXpuYW1uw70uCkNlbGtvdm8gbW/Fvm5vIGtvbsWhdGF0b3ZhxaUsIMW+ZSB6YXZlZGVuaWUgcHJlbWVubmVqIERVTSBuZWJvbG8gcHLDrW5vc27DqSBhIHRlbnRvIG1vZGVsIG5lcG9uw7prYSBsZXDFocOtIG9waXMgdGVwbG90eSBuZcW+IHDDtHZvZG7DvSBsaW5lw6FybnkgbW9kZWwgYmV6IHpsb211LgoKCgpgYGB7cn0KCm1vZGVsRF9za2xvbiA8LSBsbShUZW1wZXJhdHVyZS4uLkMuIH4gKzEgICsgQWlyLlByZXNzdXJlLi5oUGEuICsgSShEVU0qQWlyLlByZXNzdXJlLi5oUGEuKSArICBXaW5kLlNwZWVkLi5tLnMuLGRhdGE9dWRhamUgKSAKc3VtbWFyeShtb2RlbERfc2tsb24pCmBgYApWIG1vZGVsaSBzbyB6bG9tb20gdiBza2xvbmUg4oCTIHRlZGEgcG8gcHJpZGFuw60gaW50ZXJha8SNbsOpaG8gxI1sZW5hIERVTSDDlyBBaXIuUHJlc3N1cmUuLmhQYS4g4oCTIHNhIHVrYXp1amUsIMW+ZSDFvmlhZG55IHoga29lZmljaWVudG92IG5pZSBqZSDFoXRhdGlzdGlja3kgdsO9em5hbW7DvS4gTGluZcOhcm55IGVmZWt0IHRsYWt1IHZ6ZHVjaHUgKHAg4omIIDAuNjMzKSwgaW50ZXJha8SNbsO9IMSNbGVuIChwIOKJiCAwLjM2OSkgYWogcsO9Y2hsb3PFpSB2ZXRyYSAocCDiiYggMC42OTUpIG1hasO6IHZ5c29rw6kgcC1ob2Rub3R5LCB0YWvFvmUgbW9kZWwgbmVwcmV1a2F6dWplIHJvemRpZWxueSBza2xvbiByZWdyZXNuZWogcHJpYW1reSBwcmUgZHZlIMO6cm92bmUgdGxha3UgKERVTSA9IDAgYSBEVU0gPSAxKS4KS29lZmljaWVudCBkZXRlcm1pbsOhY2llIFLCsiA9IDAuMTY5OCBhIG5lZ2F0w612bnkgQWRqdXN0ZWQgUsKyID0g4oCTMC4xNDE2IHVrYXp1asO6LCDFvmUgbW9kZWwgdnlzdmV0xL51amUgbGVuIHZlxL5taSBtYWzDuiDEjWFzxaUgdmFyaWFiaWxpdHkgdGVwbG90eSBhIHBvIHBlbmFsaXrDoWNpaSBkb2tvbmNhIHDDtHNvYsOtLCBha29ieSBib2wgaG9yxaHDrSBuZcW+IG1vZGVsIGJleiB2eXN2ZXTEvnVqw7pjaWNoIHByZW1lbm7DvWNoLiBBaiBGLXRlc3QgKHAg4omIIDAuNjY1KSBwb3R2cmR6dWplLCDFvmUgbW9kZWwgYWtvIGNlbG9rIG5pZSBqZSDFoXRhdGlzdGlja3kgdsO9em5hbW7DvS4KWiB0b2hvIHZ5cGzDvXZhLCDFvmUgemF2ZWRlbmllIHpsb211IHYgc2tsb25lIG5lcHJpbmllc2xvIMW+aWFkbmUgemxlcMWhZW5pZS4gSW50ZXJha8SNbsO9IMSNbGVuIG5lYm9sIHByw61ub3Nuw70gYSBtb2RlbCBzdMOhbGUgbmVkb2vDocW+ZSBvcMOtc2HFpSB6w6F2aXNsb3PFpSB0ZXBsb3R5IG9kIHRsYWt1IHZ6ZHVjaHUgYSByw71jaGxvc3RpIHZldHJhLiBWaG9kbmVqxaFpZSBqZSB6b3N0YcWlIHByaSBqZWRub2R1Y2jFoW9tIGxpbmXDoXJub20gbW9kZWxpLgoKUG9yb3ZuYW5pZSBww7R2b2Ruw6lobyBtb2RlbHUgYSBtb2RlbHUgcyBwcmVtZW5saXbDvW0gc2tsb25vbSBuYWRyb3Zpbnkgdnlrb27DoW1lIHMgcG9tb2NvdSBhbm92YSB0ZXN0dToKCmBgYHtyfQphbm92YShtb2RlbCwgbW9kZWxEX3NrbG9uKQpyZXNldHRlc3QobW9kZWxEX3NrbG9uKQpgYGAKQU5PVkEgdGVzdCBwb3Jvdm7DoXZhIHDDtHZvZG7DvSBsaW5lw6FybnkgbW9kZWwgcyByb3rFocOtcmVuw71tIG1vZGVsb20gb2JzYWh1asO6Y2ltIGludGVyYWvEjW7DvSDEjWxlbiBEVU0gw5cgQWlyLlByZXNzdXJlLi5oUGEuLgpWw71zbGVkb2sgKHAg4omIIDAuMzY5MSkgdWthenVqZSwgxb5lIHJvesWhw61yZW7DvSBtb2RlbCBuaWUgamUgxaF0YXRpc3RpY2t5IGxlcMWhw60gbmXFviBww7R2b2Ruw70uIFByaWRhbmllIGludGVyYWvEjW7DqWhvIHRlcm11IHRlZGEgbmV6bGVwxaFpbG8gdnlzdmV0xL5vdmFjaXUgc2Nob3Bub3PFpSBtb2RlbHUuClJFU0VUIHRlc3QgcHJlIG1vZGVsRF9za2xvbiBtw6EgcC1ob2Rub3R1IOKJiCAwLjczNzUsIMSNbyB6bmFtZW7DoSwgxb5lIG5lemFtaWV0YW1lIG51bG92w7ogaHlwb3TDqXp1IG8gc3Byw6F2bmVqIMWhcGVjaWZpa8OhY2lpIG1vZGVsdS4KSW7DvW1pIHNsb3ZhbWksIG5ldmlkw61tZSBkw7RrYXp5IG8gbmVzcHLDoXZuZWogZnVua8SNbmVqIGZvcm1lLCBhIHByaWRhbmllIMSPYWzFoWVqIG5lbGluZWFyaXR5IG5pZSBqZSBwb3RyZWJuw6kuCkFuaSBBTk9WQSB0ZXN0LCBhbmkgUkVTRVQgdGVzdCBuZXBvc2t5dHVqw7ogZMO0dm9kIG5hIHRvLCBhYnkgc21lIHVwcmVkbm9zdG5pbGkgbW9kZWwgc28gemxvbW9tIHYgc2tsb25lLgpSb3rFocOtcmVuw70gbW9kZWwgbmVwcmluaWVzb2wgxb5pYWRuZSB6bGVwxaFlbmllLCBhIHDDtHZvZG7DvSBqZWRub2R1Y2jFocOtIGxpbmXDoXJueSBtb2RlbCBqZSBwb3N0YcSNdWrDumNpLgoK