S využitím databázy World Development Indicators (WDI) Svetovej banky, z ktorej čerpáme:

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

V hlavnom menu môžeme Session nastaviť na Source File Location,
alebo to urobiť cez Console, napr.:

setwd("/Cloud/project/tyzdne/tyzden5")

Organizácia priečinkov a podpriečinkov sa môže líšiť podľa projektu – v tomto príklade dáta nesťahujeme z lokálneho CSV, ale priamo z WDI API Svetovej banky.

Ú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:

Naša pracovná hypotéza hovorí o štatisticky významnom vplyve všetkých troch vysvetľujúcich premenných, pričom:

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

Dáta získame z WDI pre rok 2015 a následne doplníme chýbajúce hodnoty mediánmi.

# Definícia indikátorov z WDI
indicators <- c(
  "SP.DYN.LE00.IN",  # Life expectancy at birth, total (years)
  "NY.GDP.PCAP.CD",  # GDP per capita (current US$)
  "SE.SCH.LIFE",     # Expected years of schooling
  "SH.STA.OWAD.ZS"   # Prevalence of overweight (% of adults)
)

# Stiahnutie údajov pre všetky krajiny za rok 2015
wdi_raw <- WDI(
  country   = "all",
  indicator = indicators,
  start     = 2015,
  end       = 2015,
  extra     = FALSE
)

# Vyberieme len rok 2015 (pre istotu) a príslušné stĺpce
udaje_2015 <- subset(wdi_raw, year == 2015)

udaje_2015 <- udaje_2015[, c(
  "SP.DYN.LE00.IN",
  "SH.STA.OWAD.ZS",
  "NY.GDP.PCAP.CD",
  "SE.SCH.LIFE"
)]

# Premenujeme stĺpce na jednoduchšie názvy
names(udaje_2015) <- c("Life.expectancy", "BMI", "GDP", "Schooling")

# Imputácia chýbajúcich hodnôt mediánom danej premennej
column_medians <- sapply(udaje_2015, median, na.rm = TRUE)

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

str(udaje_2015)
summary(udaje_2015)

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

# Počet premenných
num_plots <- length(names(udaje_2015))

# Nastavenie layoutu: 2 riadky × 2 stĺpce
par(mfrow = c(2, 2))
par(mar = c(4, 4, 2, 1))

# Vykreslenie boxplotu pre každú premennú
for (col in names(udaje_2015)) {
  boxplot(
    udaje_2015[[col]],
    main = col,
    xlab = "Hodnota",
    col = "lightblue"
  )
}

# Hlavný nadpis
mtext("Boxploty jednotlivých premenných", outer = TRUE, cex = 1.4, font = 2)

# Reset layoutu
par(mfrow = c(1, 1))

Lineárna regresia

Model odhadujeme príkazom lm():

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

summary(model)

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

  1. vektor 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.matrix(model).
coef(model)
head(residuals(model))
head(fitted(model))
head(model.matrix(model))

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 diagnostické grafy.

Na základe Q–Q grafu získavame dojem o možných problémoch porušenia normality rezíduí.

# Rozloženie 2 x 2
par(mfrow = c(2, 2))

# Štandardné diagnostické grafy
plot(model)

# Reset layoutu
par(mfrow = c(1, 1))

Residuals vs. fitted – interpretácia

  • Centrovanie okolo nuly
    Reziduály kolíšu približne okolo 0, čo je dobré. Naznačuje to, že model nemá výrazné systematické skreslenie v predikciách.

  • Nelinearita
    Červená hladká čiara môže byť mierne zakrivená, čo naznačuje možnú miernu nelinearitu – modelu môže chýbať nelineárny tvar niektorej premennej.

  • Rozptyl rezíduí (homoskedasticita)
    Vertikálny rozptyl sa javí ako približne konštantný v rámci rozsahu vyrovnaných hodnôt – to je dobrý dôkaz homoskedasticity.
    Ak by sa rezíduá rozširovali (tvorili “lievik”), naznačovalo by to heteroskedasticitu.

  • Odľahlé hodnoty
    Niektoré body môžu ležať výrazne ďalej 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 – interpretácia

  • 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 našej vzorky.
  • 45° prerušovaná čiara: ideálny prípad – ak sú rezíduá normálne rozložené, body ležia tesne na tejto čiare.

V našom prípade:

  • Väčšina bodov leží blízko priamky – rezíduá sú približne normálne.
  • V koncoch (extrémy) sú mierne odchýlky od priamky – môžu naznačovať odľahlé hodnoty alebo mierne ťažšie chvosty rozdelenia.
  • Stredná časť grafu sa dobre zhoduje – väčšina rezíduí pekne zapadá do normálneho rozdelenia.

Scale–Location plot – interpretácia

  • Os X: vyrovnané hodnoty (fitted values).
  • Os Y: druhá odmocnina absolútnych štandardizovaných rezíduí.
  • Červená čiara: LOESS (vyhladený trend).

V našom prípade:

  • Body sú približne rovnomerne rozptýlené po osi X, bez typického lievika – naznačuje to konštantnú varianciu (homoskedasticitu).
  • Červená čiara je relatívne rovná – pri zvyšovaní vyrovnaných hodnôt sa variancia zásadne nemení.
  • Niekoľko bodov môže byť vyššie, ale nejde o extrémne hodnoty.

Residuals vs. leverage – interpretácia

  • Os X: pákový efekt (leverage) – ako ďaleko je vektor vysvetľujúcich premenných daného bodu od stredu všetkých \(x\).
  • Os Y: štandardizované rezíduá – ako ďaleko je pozorovanie od vyrovnanej hodnoty v jednotkách štandardnej odchýlky.
  • Bodkované krivky: kontúry Cookovej vzdialenosti – mieru vplyvu pozorovania na odhadnuté koeficienty.

V našom prípade:

  • Väčšina pozorovaní má nízky pákový efekt – typické pre väčšie vyvážené vzorky.
  • Jednotlivé body s vyšším leverage sú potenciálne vplyvné, ale pokiaľ neprekračujú vysoké hodnoty Cookovej vzdialenosti, väčšinou neovplyvňujú výrazne regresné koeficienty.
  • Žiadne pozorovanie zjavne neprekračuje vonkajšie krivky Cookovej vzdialenosti, preto sa nezdá, že by niektorý bod neprimerane ovplyvňoval model.

Test normality a odľahlých hodnôt – model

# Normality test (Jarque-Bera)
residuals_model <- residuals(model)
jb_test <- jarque.bera.test(residuals_model)
jb_test

# Outlier test (Bonferroni p-value)
outlier_test <- outlierTest(model)
outlier_test

Keďže sa normalita rezíduí nepreukázala ideálne, pokúsime sa eliminovať vplyv odľahlých hodnôt v prípade GDP.
Urobíme logaritmickú transformáciu tejto premennej a vylúčime BMI, ktoré sa ukázalo ako ťažšie interpretovateľné.

Nová regresia bude mať tvar:

\[ Life.expectancy = \beta_0 + \beta_1 \log(GDP) + \beta_2 Schooling + u \]

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

summary(model2)
# Rozloženie 2 x 2
par(mfrow = c(2, 2))

# Diagnostické grafy pre model2
plot(model2)

# Reset layoutu
par(mfrow = c(1, 1))
# Normality test pre model2
residuals_model2 <- residuals(model2)
jb_test2 <- jarque.bera.test(residuals_model2)
jb_test2

# Outlier test pre model2
outlier_test2 <- outlierTest(model2)
outlier_test2

Záver

Premenné GDP a Schooling štatisticky významne predlžujú strednú dĺžku života.
Na druhej strane premenná BMI (prevalencia nadváhy v populácii) v pôvodnom modeli poskytovala nejednoznačné a ťažko interpretovateľné výsledky, preto sme ju z finálneho modelu vylúčili.

Rezíduá modelov nevykazujú dokonalé normálne rozdelenie.
Keďže však máme relatívne veľký počet pozorovaní, porušenie normality nie je kritické a naďalej môžeme s týmito modelmi pracovať (spoliehame sa na asymptotické vlastnosti odhadov).

V modeli sa nepreukazujú výrazné nelinearity ani závažné problémy s heteroskedasticitou.
Transformácia GDP pomocou logaritmu prispela k lepšiemu správaniu rezíduí a interpretovateľnosti koeficientov.

LS0tCnRpdGxlOiAiQ3ZpxI1lbmllIDUiCm91dHB1dDogaHRtbF9ub3RlYm9vawphdXRob3I6IE5hdMOhbGlhIFNvbGlnb3bDoQotLS0KClMgdnl1xb5pdMOtbSBkYXRhYsOhenkgKipXb3JsZCBEZXZlbG9wbWVudCBJbmRpY2F0b3JzIChXREkpKiogU3ZldG92ZWogYmFua3ksIHoga3RvcmVqIMSNZXJww6FtZToKCi0gKkxpZmUgZXhwZWN0YW5jeSBhdCBiaXJ0aCwgdG90YWwgKHllYXJzKSog4oCTIHN0cmVkbsOhIGTEusW+a2Egxb5pdm90YSAgCi0gKkdEUCBwZXIgY2FwaXRhIChjdXJyZW50IFVTJCkqIOKAkyBIRFAgbmEgb2J5dmF0ZcS+YSAgCi0gKkV4cGVjdGVkIHllYXJzIG9mIHNjaG9vbGluZyog4oCTIG/EjWFrw6F2YW7DqSByb2t5IMWhdMO6ZGlhICAKLSAqUHJldmFsZW5jZSBvZiBvdmVyd2VpZ2h0ICglIG9mIGFkdWx0cykqIOKAkyBwb2RpZWwgZG9zcGVsw71jaCBzIEJNSSA+IDI1IChwb3XFvmlqZW1lIGFrbyBwcmVtZW5uw7ogdHlwdSBCTUkpLgoKUHJpIMSPYWzFoWVqIHByw6FjaSBidWRlbWUgcG91xb7DrXZhxaUga25pxb5uaWNlOgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgbmFpbsWhdGFsb3ZhxaUgbGVuIHJheiAoYWsgZcWhdGUgbmVtw6F0ZSk6CiMgaW5zdGFsbC5wYWNrYWdlcygiV0RJIikKIyBpbnN0YWxsLnBhY2thZ2VzKCJ0c2VyaWVzIikKIyBpbnN0YWxsLnBhY2thZ2VzKCJsbXRlc3QiKQojIGluc3RhbGwucGFja2FnZXMoInNhbmR3aWNoIikKIyBpbnN0YWxsLnBhY2thZ2VzKCJjYXIiKQoKbGlicmFyeShXREkpCmxpYnJhcnkoem9vKQpsaWJyYXJ5KHRzZXJpZXMpCmxpYnJhcnkobG10ZXN0KQpsaWJyYXJ5KHNhbmR3aWNoKQpsaWJyYXJ5KGNhcikKCnJtKGxpc3QgPSBscygpKQoKIyBHbG9iw6FsbmUgbmFzdGF2ZW5pYSBjaHVua292IOKAkyBwb23DoWhhIHByaSBwZWtub20gem9icmF6ZW7DrSBncmFmb3YKa25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLAogIGRwaSA9IDk2CikKYGBgCgpWICpobGF2bm9tIG1lbnUqIG3DtMW+ZW1lICpTZXNzaW9uKiBuYXN0YXZpxaUgbmEgKlNvdXJjZSBGaWxlIExvY2F0aW9uKiwgIAphbGVibyB0byB1cm9iacWlIGNleiBDb25zb2xlLCBuYXByLjoKCmBgYHIKc2V0d2QoIi9DbG91ZC9wcm9qZWN0L3R5emRuZS90eXpkZW41IikKYGBgCgpPcmdhbml6w6FjaWEgcHJpZcSNaW5rb3YgYSBwb2RwcmllxI1pbmtvdiBzYSBtw7TFvmUgbMOtxaFpxaUgcG9kxL5hIHByb2pla3R1IOKAkyB2IHRvbXRvIHByw61rbGFkZSBkw6F0YSAqKm5lc8WlYWh1amVtZSB6IGxva8OhbG5laG8gQ1NWKiosIGFsZSAqKnByaWFtbyB6IFdESSBBUEkqKiBTdmV0b3ZlaiBiYW5reS4KCiMgw5p2b2QgZG8gcHJvYmzDqW11LCBzdGFub3ZlbmllIGh5cG90w6l6CgpSb3pob2RvbCBzb20gc2EgbW9kZWxvdmHFpSBzdHJlZG7DuiBkxLrFvmt1IMW+aXZvdGEgKkxpZmUuZXhwZWN0YW5jeSogdiB6w6F2aXNsb3N0aSBvZCB0cm9jaCB2eXN2ZXTEvnVqw7pjaWNoIHByZW1lbm7DvWNoLCBhIHRvOgoKLSAqQk1JKiAodiBuYcWhb20gcHLDrXBhZGU6IHByZXZhbGVuY2lhIG5hZHbDoWh5IHYgJSBkb3NwZWzDvWNoKSwKLSBIRFAgbmEgb2J5dmF0ZcS+YSAqR0RQKiwKLSBzdHJlZG7DvSBwb8SNZXQgcm9rb3YgxaF0w7pkaWEgKlNjaG9vbGluZyouCgpOYcWhYSBwcmFjb3Zuw6EgaHlwb3TDqXphIGhvdm9yw60gbyDFoXRhdGlzdGlja3kgdsO9em5hbW5vbSB2cGx5dmUgdsWhZXRrw71jaCB0cm9jaCB2eXN2ZXTEvnVqw7pjaWNoIHByZW1lbm7DvWNoLCBwcmnEjW9tOgoKLSB1IHByZW1lbm7DvWNoICpHRFAqIGEgKlNjaG9vbGluZyogb8SNYWvDoXZhbWUgKipwb3ppdMOtdm55IHZwbHl2KiogKGtsYWRuw6kgem5hbWllbmtvIGtvZWZpY2llbnRhKSwKLSB2IHByw61wYWRlICpCTUkqIG/EjWFrw6F2YW1lICoqbmVnYXTDrXZueSB2cGx5dioqICh6w6Fwb3Juw6kgem5hbWllbmtvIGtvZWZpY2llbnRhKS4KCiMgUHLDrXByYXZhIGRhdGFiw6F6eSwgxI1pc3RlbmllIGEgw7pwcmF2YSDDumRham92CgpEw6F0YSB6w61za2FtZSB6IFdESSBwcmUgcm9rIDIwMTUgYSBuw6FzbGVkbmUgZG9wbG7DrW1lIGNow71iYWrDumNlIGhvZG5vdHkgbWVkacOhbm1pLgoKYGBge3IgZGF0YV9wcmVwYXJhdGlvbn0KIyBEZWZpbsOtY2lhIGluZGlrw6F0b3JvdiB6IFdESQppbmRpY2F0b3JzIDwtIGMoCiAgIlNQLkRZTi5MRTAwLklOIiwgICMgTGlmZSBleHBlY3RhbmN5IGF0IGJpcnRoLCB0b3RhbCAoeWVhcnMpCiAgIk5ZLkdEUC5QQ0FQLkNEIiwgICMgR0RQIHBlciBjYXBpdGEgKGN1cnJlbnQgVVMkKQogICJTRS5TQ0guTElGRSIsICAgICAjIEV4cGVjdGVkIHllYXJzIG9mIHNjaG9vbGluZwogICJTSC5TVEEuT1dBRC5aUyIgICAjIFByZXZhbGVuY2Ugb2Ygb3ZlcndlaWdodCAoJSBvZiBhZHVsdHMpCikKCiMgU3RpYWhudXRpZSDDumRham92IHByZSB2xaFldGt5IGtyYWppbnkgemEgcm9rIDIwMTUKd2RpX3JhdyA8LSBXREkoCiAgY291bnRyeSAgID0gImFsbCIsCiAgaW5kaWNhdG9yID0gaW5kaWNhdG9ycywKICBzdGFydCAgICAgPSAyMDE1LAogIGVuZCAgICAgICA9IDIwMTUsCiAgZXh0cmEgICAgID0gRkFMU0UKKQoKIyBWeWJlcmllbWUgbGVuIHJvayAyMDE1IChwcmUgaXN0b3R1KSBhIHByw61zbHXFoW7DqSBzdMS6cGNlCnVkYWplXzIwMTUgPC0gc3Vic2V0KHdkaV9yYXcsIHllYXIgPT0gMjAxNSkKCnVkYWplXzIwMTUgPC0gdWRhamVfMjAxNVssIGMoCiAgIlNQLkRZTi5MRTAwLklOIiwKICAiU0guU1RBLk9XQUQuWlMiLAogICJOWS5HRFAuUENBUC5DRCIsCiAgIlNFLlNDSC5MSUZFIgopXQoKIyBQcmVtZW51amVtZSBzdMS6cGNlIG5hIGplZG5vZHVjaMWhaWUgbsOhenZ5Cm5hbWVzKHVkYWplXzIwMTUpIDwtIGMoIkxpZmUuZXhwZWN0YW5jeSIsICJCTUkiLCAiR0RQIiwgIlNjaG9vbGluZyIpCgojIEltcHV0w6FjaWEgY2jDvWJhasO6Y2ljaCBob2Ruw7R0IG1lZGnDoW5vbSBkYW5laiBwcmVtZW5uZWoKY29sdW1uX21lZGlhbnMgPC0gc2FwcGx5KHVkYWplXzIwMTUsIG1lZGlhbiwgbmEucm0gPSBUUlVFKQoKdWRhamVfaW1wdXRlZCA8LSB1ZGFqZV8yMDE1Cgpmb3IgKGNvbCBpbiBuYW1lcyh1ZGFqZV8yMDE1KSkgewogIHVkYWplX2ltcHV0ZWRbW2NvbF1dW2lzLm5hKHVkYWplX2ltcHV0ZWRbW2NvbF1dKV0gPC0gY29sdW1uX21lZGlhbnNbY29sXQp9Cgp1ZGFqZV8yMDE1IDwtIHVkYWplX2ltcHV0ZWQKCnN0cih1ZGFqZV8yMDE1KQpzdW1tYXJ5KHVkYWplXzIwMTUpCmBgYAoKVGVyYXogY2hjZW1lIHZpZGllxaUgdHZhciDDumRham92ICjEjWkgbmllIHPDuiB2IG5pY2ggbmVqYWvDqSBuZXpyb3ZuYWxvc3RpIOKAkyBuYXByw61rbGFkIGV4dHLDqW1uZSBob2Rub3R5KS4KCmBgYHtyIGJveHBsb3RzLCBmaWcuY2FwID0gIkJveHBsb3R5IGplZG5vdGxpdsO9Y2ggcHJlbWVubsO9Y2gifQojIFBvxI1ldCBwcmVtZW5uw71jaApudW1fcGxvdHMgPC0gbGVuZ3RoKG5hbWVzKHVkYWplXzIwMTUpKQoKIyBOYXN0YXZlbmllIGxheW91dHU6IDIgcmlhZGt5IMOXIDIgc3TEunBjZQpwYXIobWZyb3cgPSBjKDIsIDIpKQpwYXIobWFyID0gYyg0LCA0LCAyLCAxKSkKCiMgVnlrcmVzbGVuaWUgYm94cGxvdHUgcHJlIGthxb5kw7ogcHJlbWVubsO6CmZvciAoY29sIGluIG5hbWVzKHVkYWplXzIwMTUpKSB7CiAgYm94cGxvdCgKICAgIHVkYWplXzIwMTVbW2NvbF1dLAogICAgbWFpbiA9IGNvbCwKICAgIHhsYWIgPSAiSG9kbm90YSIsCiAgICBjb2wgPSAibGlnaHRibHVlIgogICkKfQoKIyBIbGF2bsO9IG5hZHBpcwptdGV4dCgiQm94cGxvdHkgamVkbm90bGl2w71jaCBwcmVtZW5uw71jaCIsIG91dGVyID0gVFJVRSwgY2V4ID0gMS40LCBmb250ID0gMikKCiMgUmVzZXQgbGF5b3V0dQpwYXIobWZyb3cgPSBjKDEsIDEpKQpgYGAKCiMjIExpbmXDoXJuYSByZWdyZXNpYQoKTW9kZWwgb2RoYWR1amVtZSBwcsOta2F6b20gYGxtKClgOgoKYGBge3IgbW9kZWwxfQptb2RlbCA8LSBsbSgKICBMaWZlLmV4cGVjdGFuY3kgfiAxICsgQk1JICsgR0RQICsgU2Nob29saW5nLAogIGRhdGEgPSB1ZGFqZV8yMDE1CikKCnN1bW1hcnkobW9kZWwpCmBgYAoKT2JqZWt0IHRyaWVkeSBgbG1gIG7DoW0gcG9za3l0dWplIG5pZWtvxL5rbyB2w71zbGVka292OgoKMS4gdmVrdG9yIG9kaGFkbnV0w71jaCBrb2VmaWNpZW50b3YgYG1vZGVsJGNvZWZmaWNpZW50c2AsICAKMi4gdmVrdG9yIHJlesOtZHXDrSBgbW9kZWwkcmVzaWR1YWxzYCwgIAozLiB2ZWt0b3Igdnlyb3ZuYW7DvWNoIGhvZG7DtHQgdnlzdmV0xL5vdmFuZWogdmVsacSNaW55IGBtb2RlbCRmaXR0ZWQudmFsdWVzYCwgIAo0LiBtYXRpY3UgWCDigJMgYG1vZGVsLm1hdHJpeChtb2RlbClgLgoKYGBge3IgbW9kZWwxX2NvbXBvbmVudHN9CmNvZWYobW9kZWwpCmhlYWQocmVzaWR1YWxzKG1vZGVsKSkKaGVhZChmaXR0ZWQobW9kZWwpKQpoZWFkKG1vZGVsLm1hdHJpeChtb2RlbCkpCmBgYAoKU8O6aHJuIG9kaGFkb3ZhbsOpaG8gbW9kZWx1IG7DoW0gcG9za3l0dWplIHPDumJvciBvZGhhZG92YW7DvWNoIHJlZ3Jlc27DvWNoIGtvZWZpY2llbnRvdiwga3RvcsO9Y2ggem5hbWllbmthIGJ1ZMO6IHJvem9iZXJhbsOpIG5lc2vDtHIuICAKQWsgaG92b3LDrW1lIG8gdmxhc3Rub3N0aWFjaCBtb2RlbHUgYWtvIGNlbGt1LCBwb3pyaW1lIHNhIG5hanNrw7RyIG5hIG5hc2xlZHVqw7pjZSBkaWFnbm9zdGlja8OpIGdyYWZ5LgoKTmEgesOha2xhZGUgUeKAk1EgZ3JhZnUgesOtc2thdmFtZSBkb2plbSBvIG1vxb5uw71jaCBwcm9ibMOpbW9jaCBwb3J1xaFlbmlhIG5vcm1hbGl0eSByZXrDrWR1w60uCgpgYGB7ciBkaWFncGxvdHMsIGZpZy5jYXAgPSAiRGlhZ25vc3RpY2vDqSBncmFmeSByZWdyZXNuw6lobyBtb2RlbHUgKG1vZGVsKSJ9CiMgUm96bG/FvmVuaWUgMiB4IDIKcGFyKG1mcm93ID0gYygyLCAyKSkKCiMgxaB0YW5kYXJkbsOpIGRpYWdub3N0aWNrw6kgZ3JhZnkKcGxvdChtb2RlbCkKCiMgUmVzZXQgbGF5b3V0dQpwYXIobWZyb3cgPSBjKDEsIDEpKQpgYGAKCiMjIFJlc2lkdWFscyB2cy4gZml0dGVkIOKAkyBpbnRlcnByZXTDoWNpYQoKLSAqKkNlbnRyb3ZhbmllIG9rb2xvIG51bHkqKiAgCiAgUmV6aWR1w6FseSBrb2zDrcWhdSBwcmlibGnFvm5lIG9rb2xvIDAsIMSNbyBqZSBkb2Jyw6kuIE5hem5hxI11amUgdG8sIMW+ZSBtb2RlbCBuZW3DoSB2w71yYXpuw6kgc3lzdGVtYXRpY2vDqSBza3Jlc2xlbmllIHYgcHJlZGlrY2nDoWNoLgoKLSAqKk5lbGluZWFyaXRhKiogIAogIMSMZXJ2ZW7DoSBobGFka8OhIMSNaWFyYSBtw7TFvmUgYnnFpSBtaWVybmUgemFrcml2ZW7DoSwgxI1vIG5hem5hxI11amUgbW/Fvm7DuiBtaWVybnUgbmVsaW5lYXJpdHUg4oCTIG1vZGVsdSBtw7TFvmUgY2jDvWJhxaUgbmVsaW5lw6FybnkgdHZhciBuaWVrdG9yZWogcHJlbWVubmVqLgoKLSAqKlJvenB0eWwgcmV6w61kdcOtIChob21vc2tlZGFzdGljaXRhKSoqICAKICBWZXJ0aWvDoWxueSByb3pwdHlsIHNhIGphdsOtIGFrbyBwcmlibGnFvm5lIGtvbsWhdGFudG7DvSB2IHLDoW1jaSByb3pzYWh1IHZ5cm92bmFuw71jaCBob2Ruw7R0IOKAkyB0byBqZSBkb2Jyw70gZMO0a2F6IGhvbW9za2VkYXN0aWNpdHkuICAKICBBayBieSBzYSByZXrDrWR1w6Egcm96xaFpcm92YWxpICh0dm9yaWxpIOKAnGxpZXZpa+KAnSksIG5hem5hxI1vdmFsbyBieSB0byBoZXRlcm9za2VkYXN0aWNpdHUuCgotICoqT2TEvmFobMOpIGhvZG5vdHkqKiAgCiAgTmlla3RvcsOpIGJvZHkgbcO0xb51IGxlxb5hxaUgdsO9cmF6bmUgxI9hbGVqIG9kIG9zdGF0bsO9Y2gg4oCTIGlkZSBvIHBvdGVuY2nDoWxuZSBvZMS+YWhsw6kgaG9kbm90eSBhbGVibyB2cGx5dm7DqSBwb3pvcm92YW5pYS4gIAogIE3DtMW+ZW1lIGljaCBwcmVza8O6bWHFpSBwb21vY291IGBvdXRsaWVyVGVzdChtb2RlbClgICh6IGJhbMOta2EgKipjYXIqKikuCgojIyBR4oCTUSBwbG90IOKAkyBpbnRlcnByZXTDoWNpYQoKLSAqKk9zIFg6KiogdGVvcmV0aWNrw6kga3ZhbnRpbHkg4oCTIMSNbyBieSBzbWUgb8SNYWvDoXZhbGksIGFrIGJ5IHJlesOtZHXDoSBib2xpIGRva29uYWxlIG5vcm3DoWxuZSByb3psb8W+ZW7DqS4gIAotICoqT3MgWToqKiDFoXRhbmRhcmRpem92YW7DqSByZXrDrWR1w6Eg4oCTIHNrdXRvxI1uw6kga3ZhbnRpbHkgeiBuYcWhZWogdnpvcmt5LiAgCi0gKio0NcKwIHByZXJ1xaFvdmFuw6EgxI1pYXJhOioqIGlkZcOhbG55IHByw61wYWQg4oCTIGFrIHPDuiByZXrDrWR1w6Egbm9ybcOhbG5lIHJvemxvxb5lbsOpLCBib2R5IGxlxb5pYSB0ZXNuZSBuYSB0ZWp0byDEjWlhcmUuCgojIyMgViBuYcWhb20gcHLDrXBhZGU6CgotIFbDpMSNxaFpbmEgYm9kb3YgbGXFvsOtIGJsw616a28gcHJpYW1reSDigJMgcmV6w61kdcOhIHPDuiAqKnByaWJsacW+bmUgbm9ybcOhbG5lKiouICAKLSBWIGtvbmNvY2ggKGV4dHLDqW15KSBzw7ogbWllcm5lIG9kY2jDvWxreSBvZCBwcmlhbWt5IOKAkyBtw7TFvnUgbmF6bmHEjW92YcWlIG9kxL5haGzDqSBob2Rub3R5IGFsZWJvIG1pZXJuZSDFpWHFvsWhaWUgY2h2b3N0eSByb3pkZWxlbmlhLiAgCi0gU3RyZWRuw6EgxI1hc8WlIGdyYWZ1IHNhIGRvYnJlIHpob2R1amUg4oCTIHbDpMSNxaFpbmEgcmV6w61kdcOtIHBla25lIHphcGFkw6EgZG8gbm9ybcOhbG5laG8gcm96ZGVsZW5pYS4KCiMjIFNjYWxl4oCTTG9jYXRpb24gcGxvdCDigJMgaW50ZXJwcmV0w6FjaWEKCi0gKipPcyBYOioqIHZ5cm92bmFuw6kgaG9kbm90eSAoZml0dGVkIHZhbHVlcykuICAKLSAqKk9zIFk6KiogZHJ1aMOhIG9kbW9jbmluYSBhYnNvbMO6dG55Y2ggxaF0YW5kYXJkaXpvdmFuw71jaCByZXrDrWR1w60uICAKLSAqKsSMZXJ2ZW7DoSDEjWlhcmE6KiogTE9FU1MgKHZ5aGxhZGVuw70gdHJlbmQpLgoKIyMjIFYgbmHFoW9tIHByw61wYWRlOgoKLSBCb2R5IHPDuiBwcmlibGnFvm5lIHJvdm5vbWVybmUgcm96cHTDvWxlbsOpIHBvIG9zaSBYLCBiZXogdHlwaWNrw6lobyBsaWV2aWthIOKAkyBuYXpuYcSNdWplIHRvICoqa29uxaF0YW50bsO6IHZhcmlhbmNpdSoqIChob21vc2tlZGFzdGljaXR1KS4gIAotIMSMZXJ2ZW7DoSDEjWlhcmEgamUgcmVsYXTDrXZuZSByb3Zuw6Eg4oCTIHByaSB6dnnFoW92YW7DrSB2eXJvdm5hbsO9Y2ggaG9kbsO0dCBzYSB2YXJpYW5jaWEgesOhc2FkbmUgbmVtZW7DrS4gIAotIE5pZWtvxL5rbyBib2RvdiBtw7TFvmUgYnnFpSB2ecWhxaFpZSwgYWxlIG5lamRlIG8gZXh0csOpbW5lIGhvZG5vdHkuCgojIyBSZXNpZHVhbHMgdnMuIGxldmVyYWdlIOKAkyBpbnRlcnByZXTDoWNpYQoKLSAqKk9zIFg6KiogcMOha292w70gZWZla3QgKGxldmVyYWdlKSDigJMgYWtvIMSPYWxla28gamUgdmVrdG9yIHZ5c3ZldMS+dWrDumNpY2ggcHJlbWVubsO9Y2ggZGFuw6lobyBib2R1IG9kIHN0cmVkdSB2xaFldGvDvWNoIFwoeFwpLiAgCi0gKipPcyBZOioqIMWhdGFuZGFyZGl6b3ZhbsOpIHJlesOtZHXDoSDigJMgYWtvIMSPYWxla28gamUgcG96b3JvdmFuaWUgb2Qgdnlyb3ZuYW5laiBob2Rub3R5IHYgamVkbm90a8OhY2ggxaF0YW5kYXJkbmVqIG9kY2jDvWxreS4gIAotICoqQm9ka292YW7DqSBrcml2a3k6Kioga29udMO6cnkgQ29va292ZWogdnpkaWFsZW5vc3RpIOKAkyBtaWVydSB2cGx5dnUgcG96b3JvdmFuaWEgbmEgb2RoYWRudXTDqSBrb2VmaWNpZW50eS4KCiMjIyBWIG5hxaFvbSBwcsOtcGFkZToKCi0gVsOkxI3FoWluYSBwb3pvcm92YW7DrSBtw6EgbsOtemt5IHDDoWtvdsO9IGVmZWt0IOKAkyB0eXBpY2vDqSBwcmUgdsOkxI3FoWllIHZ5dsOhxb5lbsOpIHZ6b3JreS4gIAotIEplZG5vdGxpdsOpIGJvZHkgcyB2ecWhxaHDrW0gbGV2ZXJhZ2Ugc8O6IHBvdGVuY2nDoWxuZSB2cGx5dm7DqSwgYWxlIHBva2lhxL4gbmVwcmVrcmHEjXVqw7ogdnlzb2vDqSBob2Rub3R5IENvb2tvdmVqIHZ6ZGlhbGVub3N0aSwgdsOkxI3FoWlub3UgbmVvdnBseXbFiHVqw7ogdsO9cmF6bmUgcmVncmVzbsOpIGtvZWZpY2llbnR5LiAgCi0gxb1pYWRuZSBwb3pvcm92YW5pZSB6amF2bmUgbmVwcmVrcmHEjXVqZSB2b25rYWrFoWllIGtyaXZreSBDb29rb3ZlaiB2emRpYWxlbm9zdGksIHByZXRvIHNhIG5lemTDoSwgxb5lIGJ5IG5pZWt0b3LDvSBib2QgbmVwcmltZXJhbmUgb3ZwbHl2xYhvdmFsIG1vZGVsLgoKIyMgVGVzdCBub3JtYWxpdHkgYSBvZMS+YWhsw71jaCBob2Ruw7R0IOKAkyBtb2RlbAoKYGBge3IgbW9kZWwxX3Rlc3RzfQojIE5vcm1hbGl0eSB0ZXN0IChKYXJxdWUtQmVyYSkKcmVzaWR1YWxzX21vZGVsIDwtIHJlc2lkdWFscyhtb2RlbCkKamJfdGVzdCA8LSBqYXJxdWUuYmVyYS50ZXN0KHJlc2lkdWFsc19tb2RlbCkKamJfdGVzdAoKIyBPdXRsaWVyIHRlc3QgKEJvbmZlcnJvbmkgcC12YWx1ZSkKb3V0bGllcl90ZXN0IDwtIG91dGxpZXJUZXN0KG1vZGVsKQpvdXRsaWVyX3Rlc3QKYGBgCgpLZcSPxb5lIHNhIG5vcm1hbGl0YSByZXrDrWR1w60gbmVwcmV1a8OhemFsYSBpZGXDoWxuZSwgcG9rw7pzaW1lIHNhIGVsaW1pbm92YcWlIHZwbHl2IG9kxL5haGzDvWNoIGhvZG7DtHQgdiBwcsOtcGFkZSAqR0RQKi4gIApVcm9iw61tZSBsb2dhcml0bWlja8O6IHRyYW5zZm9ybcOhY2l1IHRlanRvIHByZW1lbm5laiBhIHZ5bMO6xI1pbWUgKkJNSSosIGt0b3LDqSBzYSB1a8OhemFsbyBha28gxaVhxb7FoWllIGludGVycHJldG92YXRlxL5uw6kuCgpOb3bDoSByZWdyZXNpYSBidWRlIG1hxaUgdHZhcjoKClxbCkxpZmUuZXhwZWN0YW5jeSA9IFxiZXRhXzAgKyBcYmV0YV8xIFxsb2coR0RQKSArIFxiZXRhXzIgU2Nob29saW5nICsgdQpcXQoKYGBge3IgbW9kZWwyfQptb2RlbDIgPC0gbG0oCiAgTGlmZS5leHBlY3RhbmN5IH4gMSArIEkobG9nKEdEUCkpICsgU2Nob29saW5nLAogIGRhdGEgPSB1ZGFqZV8yMDE1CikKCnN1bW1hcnkobW9kZWwyKQpgYGAKCmBgYHtyIGRpYWdwbG90czIsIGZpZy5jYXAgPSAiRGlhZ25vc3RpY2vDqSBncmFmeSByZWdyZXNuw6lobyBtb2RlbHUgKG1vZGVsMikifQojIFJvemxvxb5lbmllIDIgeCAyCnBhcihtZnJvdyA9IGMoMiwgMikpCgojIERpYWdub3N0aWNrw6kgZ3JhZnkgcHJlIG1vZGVsMgpwbG90KG1vZGVsMikKCiMgUmVzZXQgbGF5b3V0dQpwYXIobWZyb3cgPSBjKDEsIDEpKQpgYGAKCmBgYHtyIG1vZGVsMl90ZXN0c30KIyBOb3JtYWxpdHkgdGVzdCBwcmUgbW9kZWwyCnJlc2lkdWFsc19tb2RlbDIgPC0gcmVzaWR1YWxzKG1vZGVsMikKamJfdGVzdDIgPC0gamFycXVlLmJlcmEudGVzdChyZXNpZHVhbHNfbW9kZWwyKQpqYl90ZXN0MgoKIyBPdXRsaWVyIHRlc3QgcHJlIG1vZGVsMgpvdXRsaWVyX3Rlc3QyIDwtIG91dGxpZXJUZXN0KG1vZGVsMikKb3V0bGllcl90ZXN0MgpgYGAKCiMjIFrDoXZlcgoKUHJlbWVubsOpICoqR0RQKiogYSAqKlNjaG9vbGluZyoqICoqxaF0YXRpc3RpY2t5IHbDvXpuYW1uZSBwcmVkbMW+dWrDuioqIHN0cmVkbsO6IGTEusW+a3Ugxb5pdm90YS4gIApOYSBkcnVoZWogc3RyYW5lIHByZW1lbm7DoSAqKkJNSSoqIChwcmV2YWxlbmNpYSBuYWR2w6FoeSB2IHBvcHVsw6FjaWkpIHYgcMO0dm9kbm9tIG1vZGVsaSBwb3NreXRvdmFsYSBuZWplZG5vem5hxI1uw6kgYSDFpWHFvmtvIGludGVycHJldG92YXRlxL5uw6kgdsO9c2xlZGt5LCBwcmV0byBzbWUganUgeiBmaW7DoWxuZWhvIG1vZGVsdSB2eWzDusSNaWxpLgoKUmV6w61kdcOhIG1vZGVsb3YgbmV2eWthenVqw7ogZG9rb25hbMOpIG5vcm3DoWxuZSByb3pkZWxlbmllLiAgCktlxI/FvmUgdsWhYWsgbcOhbWUgcmVsYXTDrXZuZSB2ZcS+a8O9IHBvxI1ldCBwb3pvcm92YW7DrSwgcG9ydcWhZW5pZSBub3JtYWxpdHkgbmllIGplIGtyaXRpY2vDqSBhIG5hxI9hbGVqIG3DtMW+ZW1lIHMgdMO9bWl0byBtb2RlbG1pIHByYWNvdmHFpSAoc3BvbGllaGFtZSBzYSBuYSBhc3ltcHRvdGlja8OpIHZsYXN0bm9zdGkgb2RoYWRvdikuCgpWIG1vZGVsaSBzYSBuZXByZXVrYXp1asO6IHbDvXJhem7DqSBuZWxpbmVhcml0eSBhbmkgesOhdmHFvm7DqSBwcm9ibMOpbXkgcyBoZXRlcm9za2VkYXN0aWNpdG91LiAgClRyYW5zZm9ybcOhY2lhICpHRFAqIHBvbW9jb3UgbG9nYXJpdG11IHByaXNwZWxhIGsgbGVwxaFpZW11IHNwcsOhdmFuaXUgcmV6w61kdcOtIGEgaW50ZXJwcmV0b3ZhdGXEvm5vc3RpIGtvZWZpY2llbnRvdi4K