S využitím databázy WHO Life Expactancy Data database.

Pri ďalšej práci budeme používať knižnice

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

V hlavnom menu som Session nastavil na Source File Location. Môžeme to urobiť aj interaktívnym spôsobom - napísať dole do Console (alebo to zahrnúť priamo do skriptu) ako

setwd(“/Cloud/project/tyzdne/tyzden5”)

Organizácia priečinkov a podpriečinkov sa však môže líšiť v závislosti od projektu, preto som ich nezaradil do Chunk-u.

Údaje o očakávanej dĺžke života sú usporiadané v súbore csv, stĺpce sú oddelené znakom “,” a používajú desatinnú čiarku. V pracovnom priečinku som vytvoril podpriečinok s názvom udaje, aby som oddelil údaje od zvyšku projektu. Môj priečinok sa tiež nazýva udaje.

Nie všetky údaje budú použité, preto som vybral len niektoré stĺpce pre neskoršie použitie.

Úvod do problému, stanovenie hypotéz

Rozhodol som sa modelovať strednú dĺžku života Life.expectancy v závislosti od troch vysvetľujúcich premenných a to BMI, HDP na obyvateľa GDP a stredný počet rokov štúdia Schooling.

Naša pracovná hypotéza hovorí o štatisticky významnom vplyve všetkých troch vysvetľujúcich premenných, pričom u premenných GDP a Schooling by malo ísť o pozitívny vplyv (očakávame kladné znamienko odhadovaného regresného koeficienta) a v prípade BMI by malo ísť of negatívny vplyv (so záporným znamienkom)

Príprava databázy, čistenie a úprava údajov

Keďže niektoré údaje chýbajú, doplnil som ich mediánovými hodnotami premennej, ktorú zvažujem. lm

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

Teraz chceme vidieť tvar údajov (či nie sú v nich nejaké nezrovnalosti – napríklad hodnoty 0).

# Suppose udaje.2015 is your data frame

# Determine number of plots
num_plots <- length(names(udaje.2015))

# Set the layout: 2 rows × 2 columns
par(mfrow = c(2, 2))
par(mar = c(4, 4, 2, 1))  # Adjust margins

# Loop through columns and plot each boxplot with variable name as title
for (col in names(udaje.2015)) {
  boxplot(udaje.2015[[col]], 
          main = col,           # variable name as title
          xlab = "Value", 
          col = "lightblue")
}

# Add a global title
mtext("Boxploty jednotlivých premenných", outer = TRUE, cex = 1.4, font = 2)

Lineárna regresia

Model odhadujeme príkazom lm()

model <- lm(Life.expectancy ~ +1 + BMI + GDP + Schooling,data=udaje.2015)

Objekt triedy lm() nám poskytuje niekoľko výsledkov:

  1. Vector odhadnutých koeficientov model$coefficients
  2. Vektor rezíduí model$ residuals
  3. Vektor vyrovnaných hodnôt vysvetľovanej veličiny model$fitted.values
  4. Maticu X model$x
#print("Odhadnuté koeficienty sú: ")
#      print(model$coefficients)
#print("Odhadnuté rezíduá: ")
#print(model$residuals)
#print("Vyrovnané hodnoty vysvetľovanej premennej sú: ")
#print(model$fitted.values)
#print("matica model$xlevels: ")
#print(model.matrix(model))
#X <- model.matrix(model)
#diag(X %*% solve(t(X) %*% X) %*% t(X))

summary(model)

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

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

Súhrn odhadovaného modelu nám poskytuje súbor odhadovaných regresných koeficientov, ktorých znamienka budú rozoberané neskôr. Ak hovoríme o vlastnostiach modelu ako celku, pozrime sa najskôr na nasledujúce obrázky. Na základe Q-Q grafu získavame dojem o možných problémoch porušenia normality rezíduí.

# Nastaviť rozloženie 2 x 2
par(mfrow = c(2, 2))

# Vykresliť všetky 4 diagnostické grafy modelu
plot(model)

# (Voliteľné) pridať spoločný nadpis
#mtext("Diagnostické grafy regresného modelu", outer = TRUE, cex = 1.2, font = 2)

# Resetovať layout
par(mfrow = c(1, 1))

Residuals vs. fitted

Interpretácia vášho konkrétneho grafu

  • Centrovanie okolo nuly

  • Reziduály kolíšu približne okolo 0 – to je dobré.

  • Naznačuje to, že model nemá výrazné skreslenie v predikciách.

  • Červená hladká čiara je mierne zakrivená (vpravo sa ohýba nadol a uprostred nahor), to naznačuje možnú miernu nelinearitu – modelu môže chýbať nelineárny tvar nejakej premennej.

  • Rozptyl rezíduí - Vertikálny rozptyl (variancia rezíduí) sa javí ako približne konštantný v rámci prispôsobených hodnôt čo je dobrý dôkaz homoscedasticity.

  • Ak by sa rezíduá rozprestierali (tvorili kónus), naznačovalo by to heteroskedasticitu.

  • Odľahlé hodnoty - Niekoľko bodov (napr. v blízkosti −20) leží ďaleko od ostatných – ide o potenciálne odľahlé hodnoty alebo vplyvné pozorovania.

Môžeme ich preskúmať pomocou outlierTest(model) (z balíka car).

Q-Q plot

Čo ukazuje

  • Os X: Teoretické kvantily – čo by sme očakávali, ak by rezíduá boli dokonale normálne rozložené.
  • Os Y: Štandardizované rezíduá – skutočné kvantily z vašej vzorky.
  • 45° prerušovaná čiara: Ideálny prípad – ak sú rezíduá normálne rozložené, body by mali ležať tesne pozdĺž tejto čiary.

Interpretácia vášho konkrétneho grafu

Celkový tvar

Väčšina bodov leží blízko priamky — to naznačuje, že rezíduá sú približne normálne.

To je dobré: predpoklad normality sa zdá byť vo veľkej miere splnený.

Krajné hodnoty (extrémy)

Body na oboch koncoch (vľavo dole a vpravo hore) sa mierne odchyľujú od priamky.

To naznačuje miernu nenormálnosť v koncoch – možno niekoľko odľahlých hodnôt alebo ťažšie konce, ako je normálne (trochu špicatosť).

Stredná oblasť

Stredná časť grafu (−1 až +1 kvantily) sa veľmi dobre zhoduje – čo znamená, že väčšina rezíduí pekne zapadá do normálneho rozdelenia.

Zároveň vykonávame následné testy, v ktorých zamietame hypotézu o normálnom rozložení modelových chýb, ako aj prítomnosť extrémnych hodnôt.

Scale location plot

Čo to znázorňuje

Os X: Vyrovnan0 hodnoty Os Y: Druhá odmocnina absolútnych štandardizovaných rezíduí Červená čiara: LOESS (vyhladený) trend cez body

Interpretácia nášho konkrétneho grafu

Horizontálne rozptýlenie

  • Body sú rovnomerne rozptýlené po osi x bez vytvorenia lievika alebo krivky. To naznačuje približne konštantnú varianciu – rezíduá sú homoscedastické.
  • Červená hladká čiara je takmer rovná – ďalší znak, že pri zvyšovaní vyrovnaných hodnôt nedochádza k žiadnej systematickej zmene variancie.
  • Odľahlé hodnoty - niekoľko bodov je mierne nad 1,5, ale žiadny z nich nie je extrémny – takže nedochádza k žiadnym závažným anomáliám variancie.

residuals vs leverage

Čo znázorňuje graf

Os X: Pákový efekt — meria, ako ďaleko je prediktorový vektor bodu od stredu všetkých 𝑥 x.

Os Y: Štandardizované rezíduá — ako ďaleko je pozorovaná hodnota od vyrovnanej hodnoty v jednotkách štandardnej odchýlky.

Bodkované krivky: Kontúry Cookovej vzdialenosti, ktoré udávajú, do akej miery pozorovanie ovplyvňuje regresnú priamku.

Červená čiara: Hladká LOESS čiara prechádzajúca rezíduami.lm

Interpretácia vášho konkrétneho grafu

Rozloženie vplyvu

Väčšina pozorovaní má nízky vplyv (pod 0,05) — typické pre veľké vzorky alebo dobre vyvážené údaje.

Jeden alebo dva body (napr. okolo 0,2) vynikajú – ide o pozorovania s vysokou pákou, čo znamená, že ich hodnoty sú ďaleko od väčšiny údajov.

Veľkosť rezíduí

Štandardizované rezíduá väčšinou medzi −2 a +2 – to je dobré (žiadne závažné výnimky v 𝑦 y).

Pozorovanie označené číslom 113 má zdanlivo stredný vplyv a relatívne veľkú rezíduálnu hodnotu – potenciálne vplyvný prípad.

Kontúry Cookovej vzdialenosti

Žiaden z bodov jasne neprekračuje vonkajšie línie Cookovej vzdialenosti (≈0,5 alebo 1,0).

Preto sa nezdá, že by niektoré pozorovanie neprimerane ovplyvňovalo regresné koeficienty.

{r}lm # normality tests residuals <- residuals(model) jb_test <- jarque.bera.test(residuals) jb_test # outlier test (see p-value for Bonferroni correction) outlier_test <- outlierTest(model) outlier_test

Keďže sa nepreukázala normalita rezíduí, pokúsme sa eliminovať odľahlé hodnoty v prípade GDP - dokážeme to logaritmickou transformáciou tejto premennej a vylúčením BMI, ktoré sa ukázalo ako neinterpretovateľné. Nová regresia bude mať tvar

model2 <- lm(Life.expectancy ~ +1 + I(log(GDP)) + Schooling,data=udaje.2015)
summary(model2)

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

Residuals:
     Min       1Q   Median       3Q      Max 
-18.7351  -2.4761   0.5209   3.4551  10.6277 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  40.0434     2.1992  18.208   <2e-16 ***
I(log(GDP))   0.6338     0.3002   2.112   0.0361 *  
Schooling     2.0561     0.1557  13.203   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5.09 on 180 degrees of freedom
Multiple R-squared:  0.6118,    Adjusted R-squared:  0.6075 
F-statistic: 141.8 on 2 and 180 DF,  p-value: < 2.2e-16
# Nastaviť rozloženie 2 x 2
par(mfrow = c(2, 2))

# Vykresliť všetky 4 diagnostické grafy modelu
plot(model2)

# (Voliteľné) pridať spoločný nadpis
#mtext("Diagnostické grafy regresného modelu", outer = TRUE, cex = 1.2, font = 2)

# Resetovať layout
par(mfrow = c(1, 1))

# normality tests
residuals <- residuals(model)
jb_test <- jarque.bera.test(residuals)
jb_test

    Jarque Bera Test

data:  residuals
X-squared = 21.391, df = 2, p-value = 2.265e-05
# outlier test (see p-value for Bonferroni correction)
outlier_test <- outlierTest(model)
outlier_test
No Studentized residuals with Bonferroni p < 0.05
Largest |rstudent|:

Záver o analýze odľahlých hodnôt

Premenné GDP, a Schooling predlžujú štatisicky významne strednú dĺžku života. Na druhej strane BMI nám dávalo neinterpretovateľné výsledky. Rezíduá nevykazujú normálne rozdelenie, keďže však máme veľké množstvo pozorovaní, aj naďalej budeme pracovať s týmito údajmi. V modeli sa nepreukazujú žiadne významné nelinearity.

Heteroskedasticita

Prítomnosť heteroskedasticity (nekonštantného rozptylu náhodnej zložky) spôsobuje zlé vyhodnocovanie t-testov významnosti jednotlivých regresných koeficientov. Preto je nutné, aby sme heteroskedasticitu - detekovali (vizuálne a s pomocou testov) - a v prípade prítomnosti heteroskedasticity aby sme ju odstránili.

Aj v našom prípade by sme sa mohli pokúsiť o vizuálne vyhodnotenie nasledovných grafov (aj keď jeden graf sme už skúmali - bol to tzv. Scale-Location grafy uvedené vyššie).

Tentokrát sa pokúsime o vizuálne znázornenie závislosti štvorcov rezíduí a vysvetľujúcej premennej, u ktorej máme podozrenie, že môže heteroskedasticitu spôsobovať. Budeme posudzovať dva modely - a to model nazvaný model alebo model nazvaný model2. model2 má zlogaritmizovanú premennú GDP, čo sme robili z dôvodu odstránenia vplyvu odľahlých premenných v predchádzajúcich krokoch, model je pôvodným modelom.

library(ggplot2)
library(patchwork)  # install.packages("patchwork")

p1 <- ggplot(udaje.2015, aes(x = GDP, y = resid(model)^2)) +
  geom_point(alpha = 0.6) +
  geom_smooth(method = "loess", se = FALSE, color = "red") +
  labs(x = "GDP", 
       y = "Squared Residuals",
       title = "Sqiared Residuals vs GDP") +
  theme_minimal()

p2 <- ggplot(udaje.2015, aes(x = Schooling, y = resid(model)^2)) +
  geom_point(alpha = 0.6) +
  geom_smooth(method = "loess", se = FALSE, color = "red") +
  labs(x = "Schooling", 
       y = "Squared Residuals",
       title = "Squared Residuals vs Schooling") +
  theme_minimal()

# Combine side by side
p1 + p2

a teraz model so zlogaritmizovanou premennou GDP.

library(ggplot2)
library(patchwork)  # install.packages("patchwork")

p1 <- ggplot(udaje.2015, aes(x = log(GDP), y = resid(model2)^2)) +
  geom_point(alpha = 0.6) +
  geom_smooth(method = "loess", se = FALSE, color = "red") +
  labs(x = "log(GDP)", 
       y = "Squared Residuals",
       title = "Residuals vs log(GDP)") +
  theme_minimal()

p2 <- ggplot(udaje.2015, aes(x = Schooling, y = resid(model2)^2)) +
  geom_point(alpha = 0.6) +
  geom_smooth(method = "loess", se = FALSE, color = "red") +
  labs(x = "Schooling", 
       y = "Squared Residuals",
       title = "Residuals vs Schooling") +
  theme_minimal()

# Combine side by side
p1 + p2

Na tomto obrázku podľa vyhladených hodnôť štvorcov rezíduí (červená krivka) môžeme konštatovať, že neobsahuje žiaden významný vývoj s vysvetľujúcou premennou (či už \(log(GDP)\), alebo \(Schooling\)). Kvôli demonštrácii ale ukážme, že bez predchádzajúcej logaritmickej transformácie by to dopadlo inak, t.j. vychádzajme z pôvodného modelu označeného ako model nasledovne:

Testovanie prítomnosti heteroskedasticity

# Install (if not yet installed)
# install.packages("lmtest")

# Load the package
library(lmtest)

# Run the Breusch–Pagan test
bptest(model)

    studentized Breusch-Pagan test

data:  model
BP = 15.211, df = 3, p-value = 0.001645
# Install (if not yet installed)
# install.packages("lmtest")

# Load the package
library(lmtest)

# Run the Breusch–Pagan test
bptest(model2)

    studentized Breusch-Pagan test

data:  model2
BP = 3.9542, df = 2, p-value = 0.1385

Na základe výsledkov regresie môžeme povedať, že heteroskedasticita rezíduí nie je v model2 prítomná, ale v prípade model prétomná je. Ak by ale prítomná bola, a logaritmizácia premenných, alebo odstránenie odľahlých premenných by nám nepomohli, môžeme to riešiť s pomocou takzvanej White heteroskedasticity Consistent matrix, kde v t testoch významnosti regresných koeficientov sa používajú “hrubšie” odhady rozptylov regresných koeficientov. Urobíme to nasledovne

#install.packages("sandwich")
#install.packages("lmtest")
library(sandwich)
library(lmtest)
coeftest(model, vcov = vcovHC(model))

t test of coefficients:

              Estimate Std. Error t value Pr(>|t|)    
(Intercept) 4.4194e+01 1.8560e+00 23.8108  < 2e-16 ***
BMI         4.9479e-02 2.4034e-02  2.0587  0.04097 *  
GDP         6.9722e-05 3.1291e-05  2.2282  0.02711 *  
Schooling   1.9210e+00 1.7450e-01 11.0087  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Všimnime si, že tentokrát je už každá premenná štatisticky významná. Na druhej strane treba podotknúť, že použitie tejto metódy vyžaduje veľký počet pozorovaní (> 100)

Menej používanou je možnosť, kedy predelíme všetky premenné v dátovom sete premennou, ktorá heteroskedasticitu spôsobuje, čo ale často vedie k zhoršeniu interpretačnej schopnosti modelu. V prípade napr. GDP, množstva opyvateľov a rozlohy krajiny to ale napríklad možné je. Ak napríklad (podľa grafov) zistíme, že premenná množstvo obuvateľov spôsobuje heteroskedasticitu, vypočítame GDP na obyvateľa, resp. počet km štvorcových na jedného obyvateľa a môžeme dostať nový model s odstránenou heteroskedasticitou.

LS0tCnRpdGxlOiAiRWNvbm9tZXRyaWNzIGluIFIgLSBjdmnEjWVuaWUgNSIKb3V0cHV0OiBodG1sX25vdGVib29rCmF1dGhvcjogVi4gR2F6ZGEKLS0tCgpTIHZ5dcW+aXTDrW0gZGF0YWLDoXp5IFtXSE8gTGlmZSBFeHBhY3RhbmN5IERhdGFdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMva3VtYXJhamFyc2hpL2xpZmUtZXhwZWN0YW5jeS13aG8pIGRhdGFiYXNlLgoKUHJpIMSPYWzFoWVqIHByw6FjaSBidWRlbWUgcG91xb7DrXZhxaUga25pxb5uaWNlCgpgYGB7cn0KbGlicmFyeSh6b28pCmxpYnJhcnkodHNlcmllcykKbGlicmFyeShsbXRlc3QpCmxpYnJhcnkoc2FuZHdpY2gpCmxpYnJhcnkoY2FyKQpybShsaXN0PWxzKCkpCmBgYAoKClYgKmhsYXZub20gbWVudSogc29tICpTZXNzaW9uKiBuYXN0YXZpbCBuYSAqU291cmNlIEZpbGUgTG9jYXRpb24qLiBNw7TFvmVtZSB0byB1cm9iacWlIGFqIGludGVyYWt0w612bnltIHNww7Rzb2JvbSAtIG5hcMOtc2HFpSBkb2xlIGRvIENvbnNvbGUgKGFsZWJvIHRvIHphaHJuw7rFpSBwcmlhbW8gZG8gc2tyaXB0dSkgYWtvIAoKICoqc2V0d2QoIi9DbG91ZC9wcm9qZWN0L3R5emRuZS90eXpkZW41IikqKgoKT3JnYW5pesOhY2lhIHByaWXEjWlua292IGEgcG9kcHJpZcSNaW5rb3Ygc2EgdsWhYWsgbcO0xb5lIGzDrcWhacWlIHYgesOhdmlzbG9zdGkgb2QgcHJvamVrdHUsIHByZXRvIHNvbSBpY2ggbmV6YXJhZGlsIGRvIENodW5rLXUuCgrDmmRhamUgbyBvxI1ha8OhdmFuZWogZMS6xb5rZSDFvml2b3RhIHPDuiB1c3BvcmlhZGFuw6kgdiBzw7pib3JlICpjc3YqLCBzdMS6cGNlIHPDuiBvZGRlbGVuw6kgem5ha29tICIsIiBhIHBvdcW+w612YWrDuiBkZXNhdGlubsO6IMSNaWFya3UuIFYgcHJhY292bm9tIHByaWXEjWlua3Ugc29tIHZ5dHZvcmlsIHBvZHByaWXEjWlub2sgcyBuw6F6dm9tICp1ZGFqZSosIGFieSBzb20gb2RkZWxpbCDDumRhamUgb2QgenZ5xaFrdSBwcm9qZWt0dS4gTcO0aiBwcmllxI1pbm9rIHNhIHRpZcW+IG5hesO9dmEgKnVkYWplKi4gCgpOaWUgdsWhZXRreSDDumRhamUgYnVkw7ogcG91xb5pdMOpLCBwcmV0byBzb20gdnlicmFsIGxlbiBuaWVrdG9yw6kgc3TEunBjZSBwcmUgbmVza29yxaFpZSBwb3XFvml0aWUuCgojIMOadm9kIGRvIHByb2Jsw6ltdSwgc3Rhbm92ZW5pZSBoeXBvdMOpeiAKClJvemhvZG9sIHNvbSBzYSBtb2RlbG92YcWlIHN0cmVkbsO6IGTEusW+a3Ugxb5pdm90YSAqTGlmZS5leHBlY3RhbmN5KiB2IHrDoXZpc2xvc3RpIG9kIHRyb2NoIHZ5c3ZldMS+dWrDumNpY2ggcHJlbWVubsO9Y2ggYSB0byAqQk1JKiwgSERQIG5hIG9ieXZhdGXEvmEgKkdEUCogYSBzdHJlZG7DvSBwb8SNZXQgcm9rb3YgxaF0w7pkaWEgICpTY2hvb2xpbmcqLgoKTmHFoWEgcHJhY292bsOhIGh5cG90w6l6YSBob3ZvcsOtIG8gxaF0YXRpc3RpY2t5IHbDvXpuYW1ub20gdnBseXZlIHbFoWV0a8O9Y2ggdHJvY2ggdnlzdmV0xL51asO6Y2ljaCBwcmVtZW5uw71jaCwgcHJpxI1vbSB1IHByZW1lbm7DvWNoICpHRFAqIGEgKlNjaG9vbGluZyogYnkgbWFsbyDDrXPFpSBvIHBveml0w612bnkgdnBseXYgKG/EjWFrw6F2YW1lIGtsYWRuw6kgem5hbWllbmtvIG9kaGFkb3ZhbsOpaG8gcmVncmVzbsOpaG8ga29lZmljaWVudGEpIGEgdiBwcsOtcGFkZSBCTUkgYnkgbWFsbyDDrXPFpSBvZiBuZWdhdMOtdm55IHZwbHl2IChzbyB6w6Fwb3Juw71tIHpuYW1pZW5rb20pCgoKCgojIFByw61wcmF2YSBkYXRhYsOhenksIMSNaXN0ZW5pZSBhIMO6cHJhdmEgw7pkYWpvdgoKS2XEj8W+ZSBuaWVrdG9yw6kgw7pkYWplIGNow71iYWrDuiwgZG9wbG5pbCBzb20gaWNoIG1lZGnDoW5vdsO9bWkgaG9kbm90YW1pIHByZW1lbm5laiwga3RvcsO6IHp2YcW+dWplbS4gbG0KCgpgYGB7cn0KdWRhamUgPC0gcmVhZC5jc3YoInVkYWplL0xpZmVfRXhwZWN0YW5jeV9EYXRhLmNzdiIsZGVjPSIuIixzZXA9IiwiLGhlYWRlciA9IFRSVUUpCiMgc2VsZWN0IGp1c3QgdGhlIHJlY29yZCBmcm9tIDIwMTUKdWRhamUuMjAxNSA8LSB1ZGFqZVt1ZGFqZSRZZWFyPT0yMDE1LGMoIkxpZmUuZXhwZWN0YW5jeSIsIkJNSSIsIkdEUCIsIlNjaG9vbGluZyIpXQoKIyBkYXRhIGltcHV0YXRpb24KCiMgQ29tcHV0ZSBjb2x1bW4gbWVkaWFucwojY29sdW1uX21lZGlhbnMgPC0gc2FwcGx5KHVkYWplLjIwMTUsIG1lZGlhbiwgbmEucm0gPSBUUlVFKQoKIyBJbXB1dGUgbWlzc2luZyB2YWx1ZXMgd2l0aCBjb2x1bW4gbWVkaWFucwojIENvbXB1dGUgY29sdW1uIG1lZGlhbnMKY29sdW1uX21lZGlhbnMgPC0gc2FwcGx5KHVkYWplLjIwMTUsIG1lZGlhbiwgbmEucm0gPSBUUlVFKQoKIyBJbXB1dGUgbWlzc2luZyB2YWx1ZXMgd2l0aCBjb2x1bW4gbWVkaWFucwp1ZGFqZV9pbXB1dGVkIDwtIHVkYWplLjIwMTUKZm9yIChjb2wgaW4gbmFtZXModWRhamUuMjAxNSkpIHsKICB1ZGFqZV9pbXB1dGVkW1tjb2xdXVtpcy5uYSh1ZGFqZV9pbXB1dGVkW1tjb2xdXSldIDwtIGNvbHVtbl9tZWRpYW5zW2NvbF0KfQoKdWRhamUuMjAxNSA8LSB1ZGFqZV9pbXB1dGVkCgpgYGAKCgoKVGVyYXogY2hjZW1lIHZpZGllxaUgdHZhciDDumRham92ICjEjWkgbmllIHPDuiB2IG5pY2ggbmVqYWvDqSBuZXpyb3ZuYWxvc3RpIOKAkyBuYXByw61rbGFkIGhvZG5vdHkgMCkuCgpgYGB7cn0KIyBTdXBwb3NlIHVkYWplLjIwMTUgaXMgeW91ciBkYXRhIGZyYW1lCgojIERldGVybWluZSBudW1iZXIgb2YgcGxvdHMKbnVtX3Bsb3RzIDwtIGxlbmd0aChuYW1lcyh1ZGFqZS4yMDE1KSkKCiMgU2V0IHRoZSBsYXlvdXQ6IDIgcm93cyDDlyAyIGNvbHVtbnMKcGFyKG1mcm93ID0gYygyLCAyKSkKcGFyKG1hciA9IGMoNCwgNCwgMiwgMSkpICAjIEFkanVzdCBtYXJnaW5zCgojIExvb3AgdGhyb3VnaCBjb2x1bW5zIGFuZCBwbG90IGVhY2ggYm94cGxvdCB3aXRoIHZhcmlhYmxlIG5hbWUgYXMgdGl0bGUKZm9yIChjb2wgaW4gbmFtZXModWRhamUuMjAxNSkpIHsKICBib3hwbG90KHVkYWplLjIwMTVbW2NvbF1dLCAKICAgICAgICAgIG1haW4gPSBjb2wsICAgICAgICAgICAjIHZhcmlhYmxlIG5hbWUgYXMgdGl0bGUKICAgICAgICAgIHhsYWIgPSAiVmFsdWUiLCAKICAgICAgICAgIGNvbCA9ICJsaWdodGJsdWUiKQp9CgojIEFkZCBhIGdsb2JhbCB0aXRsZQptdGV4dCgiQm94cGxvdHkgamVkbm90bGl2w71jaCBwcmVtZW5uw71jaCIsIG91dGVyID0gVFJVRSwgY2V4ID0gMS40LCBmb250ID0gMikKCmBgYAoKCgoKIyMgTGluZcOhcm5hIHJlZ3Jlc2lhCgpNb2RlbCBvZGhhZHVqZW1lIHByw61rYXpvbSAqbG0oKSoKCmBgYHtyfQptb2RlbCA8LSBsbShMaWZlLmV4cGVjdGFuY3kgfiArMSArIEJNSSArIEdEUCArIFNjaG9vbGluZyxkYXRhPXVkYWplLjIwMTUpCmBgYAoKT2JqZWt0IHRyaWVkeSAqbG0oKSogbsOhbSBwb3NreXR1amUgbmlla2/EvmtvIHbDvXNsZWRrb3Y6CgoxLiBWZWN0b3Igb2RoYWRudXTDvWNoIGtvZWZpY2llbnRvdiAqbW9kZWwkY29lZmZpY2llbnRzKgoyLiBWZWt0b3IgcmV6w61kdcOtICptb2RlbCQgcmVzaWR1YWxzKgozLiBWZWt0b3Igdnlyb3ZuYW7DvWNoIGhvZG7DtHQgdnlzdmV0xL5vdmFuZWogdmVsacSNaW55ICptb2RlbCRmaXR0ZWQudmFsdWVzKgo0LiBNYXRpY3UgWCAqbW9kZWwkeCoKCmBgYHtyfQojcHJpbnQoIk9kaGFkbnV0w6kga29lZmljaWVudHkgc8O6OiAiKQojICAgICAgcHJpbnQobW9kZWwkY29lZmZpY2llbnRzKQojcHJpbnQoIk9kaGFkbnV0w6kgcmV6w61kdcOhOiAiKQojcHJpbnQobW9kZWwkcmVzaWR1YWxzKQojcHJpbnQoIlZ5cm92bmFuw6kgaG9kbm90eSB2eXN2ZXTEvm92YW5laiBwcmVtZW5uZWogc8O6OiAiKQojcHJpbnQobW9kZWwkZml0dGVkLnZhbHVlcykKI3ByaW50KCJtYXRpY2EgbW9kZWwkeGxldmVsczogIikKI3ByaW50KG1vZGVsLm1hdHJpeChtb2RlbCkpCiNYIDwtIG1vZGVsLm1hdHJpeChtb2RlbCkKI2RpYWcoWCAlKiUgc29sdmUodChYKSAlKiUgWCkgJSolIHQoWCkpCgpzdW1tYXJ5KG1vZGVsKQoKYGBgCgoKClPDumhybiBvZGhhZG92YW7DqWhvIG1vZGVsdSBuw6FtIHBvc2t5dHVqZSBzw7pib3Igb2RoYWRvdmFuw71jaCByZWdyZXNuw71jaCBrb2VmaWNpZW50b3YsIGt0b3LDvWNoIHpuYW1pZW5rYSBidWTDuiByb3pvYmVyYW7DqSBuZXNrw7RyLiBBayBob3ZvcsOtbWUgbyB2bGFzdG5vc3RpYWNoIG1vZGVsdSBha28gY2Vsa3UsIHBvenJpbWUgc2EgbmFqc2vDtHIgbmEgbmFzbGVkdWrDumNlIG9icsOhemt5LiBOYSB6w6FrbGFkZSBRLVEgZ3JhZnUgesOtc2thdmFtZSBkb2plbSBvIG1vxb5uw71jaCBwcm9ibMOpbW9jaCBwb3J1xaFlbmlhIG5vcm1hbGl0eSByZXrDrWR1w60uIAoKYGBge3IgZGlhZ3Bsb3RzLCBmaWcuY2FwPSJEaWFnbm9zdGlja8OpIGdyYWZ5IHJlZ3Jlc27DqWhvIG1vZGVsdSJ9CiMgTmFzdGF2acWlIHJvemxvxb5lbmllIDIgeCAyCnBhcihtZnJvdyA9IGMoMiwgMikpCgojIFZ5a3Jlc2xpxaUgdsWhZXRreSA0IGRpYWdub3N0aWNrw6kgZ3JhZnkgbW9kZWx1CnBsb3QobW9kZWwpCgojIChWb2xpdGXEvm7DqSkgcHJpZGHFpSBzcG9sb8SNbsO9IG5hZHBpcwojbXRleHQoIkRpYWdub3N0aWNrw6kgZ3JhZnkgcmVncmVzbsOpaG8gbW9kZWx1Iiwgb3V0ZXIgPSBUUlVFLCBjZXggPSAxLjIsIGZvbnQgPSAyKQoKIyBSZXNldG92YcWlIGxheW91dApwYXIobWZyb3cgPSBjKDEsIDEpKQpgYGAKCgojIyBSZXNpZHVhbHMgdnMuIGZpdHRlZAoKIyMjIEludGVycHJldMOhY2lhIHbDocWhaG8ga29ua3LDqXRuZWhvIGdyYWZ1CgotIENlbnRyb3ZhbmllIG9rb2xvIG51bHkKLSBSZXppZHXDoWx5IGtvbMOtxaF1IHByaWJsacW+bmUgb2tvbG8gMCDigJMgdG8gamUgZG9icsOpLgotIE5hem5hxI11amUgdG8sIMW+ZSBtb2RlbCBuZW3DoSB2w71yYXpuw6kgc2tyZXNsZW5pZSB2IHByZWRpa2Npw6FjaC4KLSDEjGVydmVuw6EgaGxhZGvDoSDEjWlhcmEgamUgbWllcm5lIHpha3JpdmVuw6EgKHZwcmF2byBzYSBvaMO9YmEgbmFkb2wgYSB1cHJvc3RyZWQgbmFob3IpLCB0byBuYXpuYcSNdWplIG1vxb5uw7ogbWllcm51IG5lbGluZWFyaXR1IOKAkyBtb2RlbHUgbcO0xb5lIGNow71iYcWlIG5lbGluZcOhcm55IHR2YXIgbmVqYWtlaiBwcmVtZW5uZWouCgotIFJvenB0eWwgcmV6w61kdcOtIC0gVmVydGlrw6Fsbnkgcm96cHR5bCAodmFyaWFuY2lhIHJlesOtZHXDrSkgc2EgamF2w60gYWtvIHByaWJsacW+bmUga29uxaF0YW50bsO9IHYgcsOhbWNpIHByaXNww7Rzb2JlbsO9Y2ggaG9kbsO0dCAgxI1vIGplIGRvYnLDvSBkw7RrYXogaG9tb3NjZWRhc3RpY2l0eS4KCi0gQWsgYnkgc2EgcmV6w61kdcOhIHJvenByZXN0aWVyYWxpICh0dm9yaWxpIGvDs251cyksIG5hem5hxI1vdmFsbyBieSB0byBoZXRlcm9za2VkYXN0aWNpdHUuCgotIE9kxL5haGzDqSBob2Rub3R5IC0gTmlla2/EvmtvIGJvZG92IChuYXByLiB2IGJsw616a29zdGkg4oiSMjApIGxlxb7DrSDEj2FsZWtvIG9kIG9zdGF0bsO9Y2gg4oCTIGlkZSBvIHBvdGVuY2nDoWxuZSBvZMS+YWhsw6kgaG9kbm90eSBhbGVibyB2cGx5dm7DqSBwb3pvcm92YW5pYS4KCk3DtMW+ZW1lIGljaCBwcmVza8O6bWHFpSBwb21vY291IG91dGxpZXJUZXN0KG1vZGVsKSAoeiBiYWzDrWthIGNhcikuCgojIyBRLVEgcGxvdAoKIyMjIMSMbyB1a2F6dWplCgoKLSBPcyBYOiBUZW9yZXRpY2vDqSBrdmFudGlseSDigJMgxI1vIGJ5IHNtZSBvxI1ha8OhdmFsaSwgYWsgYnkgcmV6w61kdcOhIGJvbGkgZG9rb25hbGUgbm9ybcOhbG5lIHJvemxvxb5lbsOpLgotIE9zIFk6IMWgdGFuZGFyZGl6b3ZhbsOpIHJlesOtZHXDoSDigJMgc2t1dG/EjW7DqSBrdmFudGlseSB6IHZhxaFlaiB2em9ya3kuCi0gNDXCsCBwcmVydcWhb3ZhbsOhIMSNaWFyYTogSWRlw6FsbnkgcHLDrXBhZCDigJMgYWsgc8O6IHJlesOtZHXDoSBub3Jtw6FsbmUgcm96bG/FvmVuw6ksIGJvZHkgYnkgbWFsaSBsZcW+YcWlIHRlc25lIHBvemTEusW+IHRlanRvIMSNaWFyeS4KCiMjIyBJbnRlcnByZXTDoWNpYSB2w6HFoWhvIGtvbmtyw6l0bmVobyBncmFmdQoKQ2Vsa292w70gdHZhcgoKVsOkxI3FoWluYSBib2RvdiBsZcW+w60gYmzDrXprbyBwcmlhbWt5IOKAlCB0byBuYXpuYcSNdWplLCDFvmUgcmV6w61kdcOhIHPDuiBwcmlibGnFvm5lIG5vcm3DoWxuZS4KClRvIGplIGRvYnLDqTogcHJlZHBva2xhZCBub3JtYWxpdHkgc2EgemTDoSBiecWlIHZvIHZlxL5rZWogbWllcmUgc3BsbmVuw70uCgpLcmFqbsOpIGhvZG5vdHkgKGV4dHLDqW15KQoKQm9keSBuYSBvYm9jaCBrb25jb2NoICh2xL5hdm8gZG9sZSBhIHZwcmF2byBob3JlKSBzYSBtaWVybmUgb2RjaHnEvnVqw7ogb2QgcHJpYW1reS4KClRvIG5hem5hxI11amUgbWllcm51IG5lbm9ybcOhbG5vc8WlIHYga29uY29jaCDigJMgbW/Fvm5vIG5pZWtvxL5rbyBvZMS+YWhsw71jaCBob2Ruw7R0IGFsZWJvIMWlYcW+xaFpZSBrb25jZSwgYWtvIGplIG5vcm3DoWxuZSAodHJvY2h1IMWhcGljYXRvc8WlKS4KClN0cmVkbsOhIG9ibGFzxaUKClN0cmVkbsOhIMSNYXPFpSBncmFmdSAo4oiSMSBhxb4gKzEga3ZhbnRpbHkpIHNhIHZlxL5taSBkb2JyZSB6aG9kdWplIOKAkyDEjW8gem5hbWVuw6EsIMW+ZSB2w6TEjcWhaW5hIHJlesOtZHXDrSBwZWtuZSB6YXBhZMOhIGRvIG5vcm3DoWxuZWhvIHJvemRlbGVuaWEuCgpaw6Fyb3ZlxYggdnlrb27DoXZhbWUgbsOhc2xlZG7DqSB0ZXN0eSwgdiBrdG9yw71jaCB6YW1pZXRhbWUgaHlwb3TDqXp1IG8gbm9ybcOhbG5vbSByb3psb8W+ZW7DrSBtb2RlbG92w71jaCBjaMO9YiwgYWtvIGFqIHByw610b21ub3PFpSBleHRyw6ltbnljaCBob2Ruw7R0LiAKCiMjIFNjYWxlIGxvY2F0aW9uIHBsb3QKCiMjIyDEjG8gdG8gem7DoXpvcsWIdWplCgpPcyBYOiBWeXJvdm5hbjAgaG9kbm90eSAKT3MgWTogRHJ1aMOhIG9kbW9jbmluYSBhYnNvbMO6dG55Y2ggxaF0YW5kYXJkaXpvdmFuw71jaCByZXrDrWR1w60gCsSMZXJ2ZW7DoSDEjWlhcmE6IExPRVNTICh2eWhsYWRlbsO9KSB0cmVuZCBjZXogYm9keQoKIyMjIEludGVycHJldMOhY2lhIG7DocWhaG8ga29ua3LDqXRuZWhvIGdyYWZ1CgpIb3Jpem9udMOhbG5lIHJvenB0w71sZW5pZQoKLSBCb2R5IHPDuiByb3Zub21lcm5lIHJvenB0w71sZW7DqSBwbyBvc2kgeCBiZXogdnl0dm9yZW5pYSBsaWV2aWthIGFsZWJvIGtyaXZreS4gVG8gbmF6bmHEjXVqZSBwcmlibGnFvm5lIGtvbsWhdGFudG7DuiB2YXJpYW5jaXUg4oCTIHJlesOtZHXDoSBzw7ogaG9tb3NjZWRhc3RpY2vDqS4KLSDEjGVydmVuw6EgaGxhZGvDoSDEjWlhcmEgamUgdGFrbWVyIHJvdm7DoSDigJMgxI9hbMWhw60gem5haywgxb5lIHByaSB6dnnFoW92YW7DrSB2eXJvdm5hbsO9Y2ggaG9kbsO0dCBuZWRvY2jDoWR6YSBrIMW+aWFkbmVqIHN5c3RlbWF0aWNrZWogem1lbmUgdmFyaWFuY2llLgotIE9kxL5haGzDqSBob2Rub3R5IC0gbmlla2/EvmtvIGJvZG92IGplIG1pZXJuZSBuYWQgMSw1LCBhbGUgxb5pYWRueSB6IG5pY2ggbmllIGplIGV4dHLDqW1ueSDigJMgdGFrxb5lIG5lZG9jaMOhZHphIGsgxb5pYWRueW0gesOhdmHFvm7DvW0gYW5vbcOhbGnDoW0gdmFyaWFuY2llLgoKIyMgcmVzaWR1YWxzIHZzIGxldmVyYWdlCgojIyMgxIxvIHpuw6F6b3LFiHVqZSBncmFmCgpPcyBYOiBQw6Frb3bDvSBlZmVrdCAg4oCUIG1lcmlhLCBha28gxI9hbGVrbyBqZSBwcmVkaWt0b3JvdsO9IHZla3RvciBib2R1ICBvZCBzdHJlZHUgdsWhZXRrw71jaCAK8J2RpQp4LgoKT3MgWTogxaB0YW5kYXJkaXpvdmFuw6kgcmV6w61kdcOhIOKAlCBha28gxI9hbGVrbyBqZSBwb3pvcm92YW7DoSBob2Rub3RhIAogb2Qgdnlyb3ZuYW5laiBob2Rub3R5IHYgamVkbm90a8OhY2ggxaF0YW5kYXJkbmVqIG9kY2jDvWxreS4KCkJvZGtvdmFuw6kga3Jpdmt5OiBLb250w7pyeSBDb29rb3ZlaiB2emRpYWxlbm9zdGksIGt0b3LDqSB1ZMOhdmFqw7osIGRvIGFrZWogbWllcnkgcG96b3JvdmFuaWUgb3ZwbHl2xYh1amUgcmVncmVzbsO6IHByaWFta3UuCgrEjGVydmVuw6EgxI1pYXJhOiBIbGFka8OhIExPRVNTIMSNaWFyYSBwcmVjaMOhZHphasO6Y2EgcmV6w61kdWFtaS5sbQoKIyMjIEludGVycHJldMOhY2lhIHbDocWhaG8ga29ua3LDqXRuZWhvIGdyYWZ1CgpSb3psb8W+ZW5pZSB2cGx5dnUKClbDpMSNxaFpbmEgcG96b3JvdmFuw60gbcOhIG7DrXpreSB2cGx5diAocG9kIDAsMDUpIOKAlCB0eXBpY2vDqSBwcmUgdmXEvmvDqSB2em9ya3kgYWxlYm8gZG9icmUgdnl2w6HFvmVuw6kgw7pkYWplLgoKSmVkZW4gYWxlYm8gZHZhIGJvZHkgKG5hcHIuIG9rb2xvIDAsMikgdnluaWthasO6IOKAkyBpZGUgbyBwb3pvcm92YW5pYSBzIHZ5c29rb3UgcMOha291LCDEjW8gem5hbWVuw6EsIMW+ZSBpY2ggaG9kbm90eSBzw7ogxI9hbGVrbyBvZCB2w6TEjcWhaW55IMO6ZGFqb3YuCgpWZcS+a29zxaUgcmV6w61kdcOtCgrFoHRhbmRhcmRpem92YW7DqSByZXrDrWR1w6EgdsOkxI3FoWlub3UgbWVkemkg4oiSMiBhICsyIOKAkyB0byBqZSBkb2Jyw6kgKMW+aWFkbmUgesOhdmHFvm7DqSB2w71uaW1reSB2IArwnZGmCnkpLgoKUG96b3JvdmFuaWUgb3puYcSNZW7DqSDEjcOtc2xvbSAxMTMgbcOhIHpkYW5saXZvIHN0cmVkbsO9IHZwbHl2IGEgcmVsYXTDrXZuZSB2ZcS+a8O6IHJlesOtZHXDoWxudSBob2Rub3R1IOKAkyBwb3RlbmNpw6FsbmUgdnBseXZuw70gcHLDrXBhZC4KCktvbnTDunJ5IENvb2tvdmVqIHZ6ZGlhbGVub3N0aQoKxb1pYWRlbiB6IGJvZG92IGphc25lIG5lcHJla3JhxI11amUgdm9ua2FqxaFpZSBsw61uaWUgQ29va292ZWogdnpkaWFsZW5vc3RpICjiiYgwLDUgYWxlYm8gMSwwKS4KClByZXRvIHNhIG5lemTDoSwgxb5lIGJ5IG5pZWt0b3LDqSBwb3pvcm92YW5pZSBuZXByaW1lcmFuZSBvdnBseXbFiG92YWxvIHJlZ3Jlc27DqSBrb2VmaWNpZW50eS4KCmBgYHtyfWxtCiMgbm9ybWFsaXR5IHRlc3RzCnJlc2lkdWFscyA8LSByZXNpZHVhbHMobW9kZWwpCmpiX3Rlc3QgPC0gamFycXVlLmJlcmEudGVzdChyZXNpZHVhbHMpCmpiX3Rlc3QKIyBvdXRsaWVyIHRlc3QgKHNlZSBwLXZhbHVlIGZvciBCb25mZXJyb25pIGNvcnJlY3Rpb24pCm91dGxpZXJfdGVzdCA8LSBvdXRsaWVyVGVzdChtb2RlbCkKb3V0bGllcl90ZXN0CmBgYAoKS2XEj8W+ZSBzYSBuZXByZXVrw6F6YWxhIG5vcm1hbGl0YSByZXrDrWR1w60sIHBva8O6c21lIHNhIGVsaW1pbm92YcWlIG9kxL5haGzDqSBob2Rub3R5IHYgcHLDrXBhZGUgR0RQIC0gZG9rw6HFvmVtZSB0byBsb2dhcml0bWlja291IHRyYW5zZm9ybcOhY2lvdSB0ZWp0byBwcmVtZW5uZWogYSB2eWzDusSNZW7DrW0gQk1JLCBrdG9yw6kgc2EgdWvDoXphbG8gYWtvIG5laW50ZXJwcmV0b3ZhdGXEvm7DqS4gTm92w6EgcmVncmVzaWEgYnVkZSBtYcWlIHR2YXIKCgpgYGB7cn0KbW9kZWwyIDwtIGxtKExpZmUuZXhwZWN0YW5jeSB+ICsxICsgSShsb2coR0RQKSkgKyBTY2hvb2xpbmcsZGF0YT11ZGFqZS4yMDE1KQpzdW1tYXJ5KG1vZGVsMikKYGBgCmBgYHtyIGRpYWdwbG90czIsIGZpZy5jYXA9IkRpYWdub3N0aWNrw6kgZ3JhZnkgcmVncmVzbsOpaG8gbW9kZWx1In0KIyBOYXN0YXZpxaUgcm96bG/FvmVuaWUgMiB4IDIKcGFyKG1mcm93ID0gYygyLCAyKSkKCiMgVnlrcmVzbGnFpSB2xaFldGt5IDQgZGlhZ25vc3RpY2vDqSBncmFmeSBtb2RlbHUKcGxvdChtb2RlbDIpCgojIChWb2xpdGXEvm7DqSkgcHJpZGHFpSBzcG9sb8SNbsO9IG5hZHBpcwojbXRleHQoIkRpYWdub3N0aWNrw6kgZ3JhZnkgcmVncmVzbsOpaG8gbW9kZWx1Iiwgb3V0ZXIgPSBUUlVFLCBjZXggPSAxLjIsIGZvbnQgPSAyKQoKIyBSZXNldG92YcWlIGxheW91dApwYXIobWZyb3cgPSBjKDEsIDEpKQpgYGAKCgoKCmBgYHtyfQojIG5vcm1hbGl0eSB0ZXN0cwpyZXNpZHVhbHMgPC0gcmVzaWR1YWxzKG1vZGVsKQpqYl90ZXN0IDwtIGphcnF1ZS5iZXJhLnRlc3QocmVzaWR1YWxzKQpqYl90ZXN0CiMgb3V0bGllciB0ZXN0IChzZWUgcC12YWx1ZSBmb3IgQm9uZmVycm9uaSBjb3JyZWN0aW9uKQpvdXRsaWVyX3Rlc3QgPC0gb3V0bGllclRlc3QobW9kZWwpCm91dGxpZXJfdGVzdApgYGAKCiMjIFrDoXZlciBvIGFuYWzDvXplIG9kxL5haGzDvWNoIGhvZG7DtHQKClByZW1lbm7DqSAqR0RQKiwgYSAqU2Nob29saW5nKiBwcmVkbMW+dWrDuiDFoXRhdGlzaWNreSB2w716bmFtbmUgc3RyZWRuw7ogZMS6xb5rdSDFvml2b3RhLiBOYSBkcnVoZWogc3RyYW5lICpCTUkqIG7DoW0gZMOhdmFsbyBuZWludGVycHJldG92YXRlxL5uw6kgdsO9c2xlZGt5LiBSZXrDrWR1w6EgbmV2eWthenVqw7ogbm9ybcOhbG5lIHJvemRlbGVuaWUsIGtlxI/FvmUgdsWhYWsgbcOhbWUgdmXEvmvDqSBtbm/FvnN0dm8gcG96b3JvdmFuw60sIGFqIG5hxI9hbGVqIGJ1ZGVtZSBwcmFjb3ZhxaUgcyB0w71taXRvIMO6ZGFqbWkuIFYgbW9kZWxpIHNhIG5lcHJldWthenVqw7ogxb5pYWRuZSB2w716bmFtbsOpIG5lbGluZWFyaXR5LgoKIyMgSGV0ZXJvc2tlZGFzdGljaXRhCgpQcsOtdG9tbm9zxaUgaGV0ZXJvc2tlZGFzdGljaXR5IChuZWtvbsWhdGFudG7DqWhvIHJvenB0eWx1IG7DoWhvZG5laiB6bG/Fvmt5KSBzcMO0c29idWplIHpsw6kgdnlob2Rub2NvdmFuaWUgdC10ZXN0b3YgdsO9em5hbW5vc3RpIGplZG5vdGxpdsO9Y2ggcmVncmVzbsO9Y2gga29lZmljaWVudG92LiBQcmV0byBqZSBudXRuw6ksIGFieSBzbWUgaGV0ZXJvc2tlZGFzdGljaXR1IAotIGRldGVrb3ZhbGkgKHZpenXDoWxuZSBhIHMgcG9tb2NvdSB0ZXN0b3YpCi0gYSB2IHByw61wYWRlIHByw610b21ub3N0aSBoZXRlcm9za2VkYXN0aWNpdHkgYWJ5IHNtZSBqdSBvZHN0csOhbmlsaS4KCkFqIHYgbmHFoW9tIHByw61wYWRlIGJ5IHNtZSBzYSBtb2hsaSBwb2vDunNpxaUgbyB2aXp1w6FsbmUgdnlob2Rub3RlbmllIG5hc2xlZG92bsO9Y2ggZ3JhZm92IChhaiBrZcSPIGplZGVuIGdyYWYgc21lIHXFviBza8O6bWFsaSAtIGJvbCB0byB0enYuIFNjYWxlLUxvY2F0aW9uIGdyYWZ5IHV2ZWRlbsOpIHZ5xaHFoWllKS4KClRlbnRva3LDoXQgc2EgcG9rw7pzaW1lIG8gdml6dcOhbG5lIHpuw6F6b3JuZW5pZSB6w6F2aXNsb3N0aSDFoXR2b3Jjb3YgcmV6w61kdcOtIGEgdnlzdmV0xL51asO6Y2VqIHByZW1lbm5laiwgdSBrdG9yZWogbcOhbWUgcG9kb3pyZW5pZSwgxb5lIG3DtMW+ZSBoZXRlcm9za2VkYXN0aWNpdHUgc3DDtHNvYm92YcWlLiBCdWRlbWUgcG9zdWR6b3ZhxaUgZHZhIG1vZGVseSAtIGEgdG8gbW9kZWwgbmF6dmFuw70gKm1vZGVsKiBhbGVibyBtb2RlbCBuYXp2YW7DvSAqbW9kZWwyKi4gKm1vZGVsMiogbcOhIHpsb2dhcml0bWl6b3ZhbsO6IHByZW1lbm7DuiAqR0RQKiwgxI1vIHNtZSByb2JpbGkgeiBkw7R2b2R1IG9kc3Ryw6FuZW5pYSB2cGx5dnUgb2TEvmFobMO9Y2ggcHJlbWVubsO9Y2ggdiBwcmVkY2jDoWR6YWrDumNpY2gga3Jva29jaCwgKm1vZGVsKiBqZSBww7R2b2Ruw71tIG1vZGVsb20uCgoKYGBge3IgaGV0ZXJvcGxvdHMxLCBmaWcuY2FwPSJTa8O6bWFuaWUgaGV0ZXJvc2tlZGFzdGljaXR5IiwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTR9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwYXRjaHdvcmspICAjIGluc3RhbGwucGFja2FnZXMoInBhdGNod29yayIpCgpwMSA8LSBnZ3Bsb3QodWRhamUuMjAxNSwgYWVzKHggPSBHRFAsIHkgPSByZXNpZChtb2RlbCleMikpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC42KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAicmVkIikgKwogIGxhYnMoeCA9ICJHRFAiLCAKICAgICAgIHkgPSAiU3F1YXJlZCBSZXNpZHVhbHMiLAogICAgICAgdGl0bGUgPSAiU3FpYXJlZCBSZXNpZHVhbHMgdnMgR0RQIikgKwogIHRoZW1lX21pbmltYWwoKQoKcDIgPC0gZ2dwbG90KHVkYWplLjIwMTUsIGFlcyh4ID0gU2Nob29saW5nLCB5ID0gcmVzaWQobW9kZWwpXjIpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gRkFMU0UsIGNvbG9yID0gInJlZCIpICsKICBsYWJzKHggPSAiU2Nob29saW5nIiwgCiAgICAgICB5ID0gIlNxdWFyZWQgUmVzaWR1YWxzIiwKICAgICAgIHRpdGxlID0gIlNxdWFyZWQgUmVzaWR1YWxzIHZzIFNjaG9vbGluZyIpICsKICB0aGVtZV9taW5pbWFsKCkKCiMgQ29tYmluZSBzaWRlIGJ5IHNpZGUKcDEgKyBwMgpgYGAKCmEgdGVyYXogbW9kZWwgc28gemxvZ2FyaXRtaXpvdmFub3UgcHJlbWVubm91ICpHRFAqLgoKYGBge3IgaGV0ZXJvcGxvdHMyLCBmaWcuY2FwPSJTa8O6bWFuaWUgaGV0ZXJvc2tlZGFzdGljaXR5IiwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTR9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwYXRjaHdvcmspICAjIGluc3RhbGwucGFja2FnZXMoInBhdGNod29yayIpCgpwMSA8LSBnZ3Bsb3QodWRhamUuMjAxNSwgYWVzKHggPSBsb2coR0RQKSwgeSA9IHJlc2lkKG1vZGVsMileMikpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC42KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAicmVkIikgKwogIGxhYnMoeCA9ICJsb2coR0RQKSIsIAogICAgICAgeSA9ICJTcXVhcmVkIFJlc2lkdWFscyIsCiAgICAgICB0aXRsZSA9ICJSZXNpZHVhbHMgdnMgbG9nKEdEUCkiKSArCiAgdGhlbWVfbWluaW1hbCgpCgpwMiA8LSBnZ3Bsb3QodWRhamUuMjAxNSwgYWVzKHggPSBTY2hvb2xpbmcsIHkgPSByZXNpZChtb2RlbDIpXjIpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gRkFMU0UsIGNvbG9yID0gInJlZCIpICsKICBsYWJzKHggPSAiU2Nob29saW5nIiwgCiAgICAgICB5ID0gIlNxdWFyZWQgUmVzaWR1YWxzIiwKICAgICAgIHRpdGxlID0gIlJlc2lkdWFscyB2cyBTY2hvb2xpbmciKSArCiAgdGhlbWVfbWluaW1hbCgpCgojIENvbWJpbmUgc2lkZSBieSBzaWRlCnAxICsgcDIKYGBgCgoKTmEgdG9tdG8gb2Jyw6F6a3UgcG9kxL5hIHZ5aGxhZGVuw71jaCBob2Ruw7TFpSDFoXR2b3Jjb3YgcmV6w61kdcOtICjEjWVydmVuw6Ega3JpdmthKSBtw7TFvmVtZSBrb27FoXRhdG92YcWlLCDFvmUgbmVvYnNhaHVqZSDFvmlhZGVuIHbDvXpuYW1uw70gdsO9dm9qIHMgdnlzdmV0xL51asO6Y291IHByZW1lbm5vdSAoxI1pIHXFviAkbG9nKEdEUCkkLCBhbGVibyAkU2Nob29saW5nJCkuIEt2w7RsaSBkZW1vbsWhdHLDoWNpaSBhbGUgdWvDocW+bWUsIMW+ZSBiZXogcHJlZGNow6FkemFqw7pjZWogbG9nYXJpdG1pY2tlaiB0cmFuc2Zvcm3DoWNpZSBieSB0byBkb3BhZGxvIGluYWssIHQuai4gdnljaMOhZHpham1lIHogcMO0dm9kbsOpaG8gbW9kZWx1IG96bmHEjWVuw6lobyBha28gKm1vZGVsKiAgbmFzbGVkb3ZuZToKCgojIyBUZXN0b3ZhbmllIHByw610b21ub3N0aSBoZXRlcm9za2VkYXN0aWNpdHkKCmBgYHtyfQojIEluc3RhbGwgKGlmIG5vdCB5ZXQgaW5zdGFsbGVkKQojIGluc3RhbGwucGFja2FnZXMoImxtdGVzdCIpCgojIExvYWQgdGhlIHBhY2thZ2UKbGlicmFyeShsbXRlc3QpCgojIFJ1biB0aGUgQnJldXNjaOKAk1BhZ2FuIHRlc3QKYnB0ZXN0KG1vZGVsKQoKYGBgCgoKYGBge3J9CiMgSW5zdGFsbCAoaWYgbm90IHlldCBpbnN0YWxsZWQpCiMgaW5zdGFsbC5wYWNrYWdlcygibG10ZXN0IikKCiMgTG9hZCB0aGUgcGFja2FnZQpsaWJyYXJ5KGxtdGVzdCkKCiMgUnVuIHRoZSBCcmV1c2No4oCTUGFnYW4gdGVzdApicHRlc3QobW9kZWwyKQoKYGBgCgpOYSB6w6FrbGFkZSB2w71zbGVka292IHJlZ3Jlc2llIG3DtMW+ZW1lIHBvdmVkYcWlLCDFvmUgaGV0ZXJvc2tlZGFzdGljaXRhIHJlesOtZHXDrSBuaWUgamUgdiAqbW9kZWwyKiAgcHLDrXRvbW7DoSwgYWxlIHYgcHLDrXBhZGUgKm1vZGVsKiBwcsOpdG9tbsOhIGplLiBBayBieSBhbGUgcHLDrXRvbW7DoSBib2xhLCBhIGxvZ2FyaXRtaXrDoWNpYSBwcmVtZW5uw71jaCwgYWxlYm8gb2RzdHLDoW5lbmllIG9kxL5haGzDvWNoIHByZW1lbm7DvWNoIGJ5IG7DoW0gbmVwb21vaGxpLCBtw7TFvmVtZSB0byByaWXFoWnFpSBzIHBvbW9jb3UgdGFrenZhbmVqIFdoaXRlIGhldGVyb3NrZWRhc3RpY2l0eSBDb25zaXN0ZW50IG1hdHJpeCwga2RlIHYgdCB0ZXN0b2NoIHbDvXpuYW1ub3N0aSByZWdyZXNuw71jaCBrb2VmaWNpZW50b3Ygc2EgcG91xb7DrXZhasO6ICJocnVixaFpZSIgb2RoYWR5IHJvenB0eWxvdiByZWdyZXNuw71jaCBrb2VmaWNpZW50b3YuIFVyb2LDrW1lIHRvIG5hc2xlZG92bmUKCmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygic2FuZHdpY2giKQojaW5zdGFsbC5wYWNrYWdlcygibG10ZXN0IikKbGlicmFyeShzYW5kd2ljaCkKbGlicmFyeShsbXRlc3QpCmNvZWZ0ZXN0KG1vZGVsLCB2Y292ID0gdmNvdkhDKG1vZGVsKSkKCgpgYGAKClbFoWltbmltZSBzaSwgxb5lIHRlbnRva3LDoXQgamUgdcW+IGthxb5kw6EgcHJlbWVubsOhIMWhdGF0aXN0aWNreSB2w716bmFtbsOhLiBOYSBkcnVoZWogc3RyYW5lIHRyZWJhIHBvZG90a27DusWlLCDFvmUgcG91xb5pdGllIHRlanRvIG1ldMOzZHkgdnnFvmFkdWplIHZlxL5rw70gcG/EjWV0IHBvem9yb3ZhbsOtICg+IDEwMCkKCk1lbmVqIHBvdcW+w612YW5vdSBqZSBtb8W+bm9zxaUsIGtlZHkgcHJlZGVsw61tZSB2xaFldGt5IHByZW1lbm7DqSB2IGTDoXRvdm9tIHNldGUgcHJlbWVubm91LCBrdG9yw6EgaGV0ZXJvc2tlZGFzdGljaXR1IHNww7Rzb2J1amUsIMSNbyBhbGUgxI1hc3RvIHZlZGllIGsgemhvcsWhZW5pdSBpbnRlcnByZXRhxI1uZWogc2Nob3Bub3N0aSBtb2RlbHUuIFYgcHLDrXBhZGUgbmFwci4gR0RQLCBtbm/FvnN0dmEgb3B5dmF0ZcS+b3YgYSByb3psb2h5IGtyYWppbnkgdG8gYWxlIG5hcHLDrWtsYWQgbW/Fvm7DqSBqZS4gQWsgbmFwcsOta2xhZCAocG9kxL5hIGdyYWZvdikgemlzdMOtbWUsIMW+ZSBwcmVtZW5uw6EgKm1ub8W+c3R2byBvYnV2YXRlxL5vdiogc3DDtHNvYnVqZSBoZXRlcm9za2VkYXN0aWNpdHUsIHZ5cG/EjcOtdGFtZSBHRFAgbmEgb2J5dmF0ZcS+YSwgcmVzcC4gcG/EjWV0IGttIMWhdHZvcmNvdsO9Y2ggbmEgamVkbsOpaG8gb2J5dmF0ZcS+YSBhIG3DtMW+ZW1lIGRvc3RhxaUgbm92w70gbW9kZWwgcyBvZHN0csOhbmVub3UgaGV0ZXJvc2tlZGFzdGljaXRvdS4KCgoKCg==