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.
Nie všetky údaje budú použité, preto som vybral len niektoré stĺpce
pre neskoršie použitie.
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:
- Vector odhadnutých koeficientov model$coefficients
- Vektor rezíduí model$ residuals
- Vektor vyrovnaných hodnôt vysvetľovanej veličiny
model$fitted.values
- 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==