# Load data
udaje <- read.csv("world_population.csv", dec=".", sep=",", header = TRUE)
# Select relevant variables for prediction
udaje_pred <- udaje[, c("Country.Territory","X2020.Population", "X2015.Population",
"Growth.Rate", "Continent")]
# Median imputation for numeric variables
numeric_cols <- c("X2020.Population", "X2015.Population", "Growth.Rate")
column_medians <- sapply(udaje_pred[, numeric_cols], median, na.rm = TRUE)
for (col in numeric_cols) {
udaje_pred[[col]][is.na(udaje_pred[[col]])] <- column_medians[col]
}
# Log-transform for modeling
udaje_pred$log_pop <- log(udaje_pred$X2020.Population)
udaje_pred$log_pop15 <- log(udaje_pred$X2015.Population)
# Dataset ready for predictive modeling
udaje <- udaje_pred
Čo skúmame:
Pripravujeme dáta na predikciu budúcej populácie.
Chýbajúce hodnoty dopĺňame mediánom, aby model fungoval
správne.
Log-transformácia populácie stabilizuje extrémne rozdiely medzi
malými a veľkými krajinami.
# Pre budúcu populáciu nás najviac zaujíma historická populácia a rast
xvars <- udaje[, c("X2015.Population", "Growth.Rate")]
round(cor(xvars), 3)
X2015.Population Growth.Rate
X2015.Population 1.000 -0.032
Growth.Rate -0.032 1.000
Teraz zisťujeme, ako sú medzi sebou prepojené historická
populácia a ročný rast. Väčšinou historická populácia a Growth.Rate nie
sú silne korelované → obe môžu byť použité ako prediktory pre budúcu
populáciu.
pairs(xvars,
main = "Scatterplotová matica – historická populácia a Growth Rate")

Vizualizujeme vzťah medzi historickou populáciou a rastom a
pozrieme sa, či existujú extrémne hodnoty alebo netypické
krajiny.
Ako môžme vidieť, väčšina krajín sa nachádza v stabilnom pásme rastu,
niektoré majú extrémne hodnoty, ktoré môžu ovplyvniť predikciu.
Area..km.. Density..per.km.. Growth.Rate
1.004343 1.009057 1.005232
X <- model.matrix(model)[, -1]
XtX <- t(X) %*% X
eig <- eigen(XtX)
condition_number <- sqrt(max(eig$values) / min(eig$values))
condition_number
[1] 1980890
Chceme zistiť, ako historická populácia, rast a kontinent
vplývajú na populáciu 2020. Model nám zároveň slúži na overenie
predikčnej schopnosti.
# Model predikcie populácie 2020
model_pred <- lm(log_pop ~ log_pop15 + Growth.Rate + Continent, data = udaje)
summary(model_pred)
Call:
lm(formula = log_pop ~ log_pop15 + Growth.Rate + Continent, data = udaje)
Residuals:
Min 1Q Median 3Q Max
-0.228477 -0.018980 0.001669 0.020626 0.181035
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -2.274370 0.267180 -8.513 2.39e-15 ***
log_pop15 0.999371 0.001313 761.114 < 2e-16 ***
Growth.Rate 2.347058 0.262618 8.937 < 2e-16 ***
ContinentAsia -0.021732 0.009384 -2.316 0.021465 *
ContinentEurope -0.057497 0.010139 -5.671 4.32e-08 ***
ContinentNorth America -0.041139 0.010860 -3.788 0.000195 ***
ContinentOceania -0.048714 0.012870 -3.785 0.000197 ***
ContinentSouth America -0.024168 0.013950 -1.732 0.084559 .
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.04529 on 226 degrees of freedom
Multiple R-squared: 0.9997, Adjusted R-squared: 0.9997
F-statistic: 1.25e+05 on 7 and 226 DF, p-value: < 2.2e-16
Historická populácia je najsilnejší prediktor.
Growth.Rate má pozitívny vplyv – krajiny s vyšším rastom budú
väčšie.
Kontinent zachytáva regionálne rozdiely (napr. Európa má priemerne
menšiu populáciu pri rovnakej historickej veľkosti).
# Spätná transformácia na počet obyvateľov
udaje$pred_pop2020 <- exp(predict(model_pred, newdata = udaje))
# Predikcia podľa Growth.Rate
udaje$pred_pop2025 <- udaje$X2020.Population * (1 + udaje$Growth.Rate)^5
udaje$pred_pop2030 <- udaje$X2020.Population * (1 + udaje$Growth.Rate)^10
# --- Výber konkrétnych krajín ---
vybrane_krajiny <- c("Slovakia",
"Czech Republic",
"Hungary",
"Austria",
"Ukraine",
"Poland")
# Filtrovanie podľa názvu krajiny
udaje_vybrane <- udaje[udaje$Country.Territory %in% vybrane_krajiny, ]
# --- Zobrazenie výsledku ---
udaje_vybrane[, c("Country.Territory",
"X2020.Population",
"pred_pop2020",
"pred_pop2025",
"pred_pop2030")]
Predikujeme populáciu do budúcnosti (2025 a 2030) pomocou
Growth.Rate. Umožňuje nám odhadnúť rast a porovnať ho medzi
krajinami.
Keď sa pozrieme na naše výsledky, vidíme, že východiskové hodnoty
populácie v roku 2020 zodpovedajú reálnym počtom obyvateľov v
jednotlivých krajinách. Model však pri dlhodobej predikcii (najmä smerom
k roku 2030) generuje extrémne vysoké čísla. Toto sa deje preto, že sme
použili jednoduchý výpočet založený na exponenciálnom raste, ktorý pri
dlhšom období veľmi preháňa výsledky.
V krátkodobom horizonte (do roku 2025) sú predpovede ešte relatívne
podobné realite – populácia sa mení len mierne. V dlhodobom horizonte
(rok 2030) už model predpovedá nereálne vysoký nárast, takže tieto čísla
nevnímame ako skutočnú predpoveď, ale skôr ako ukážku toho, ako rýchlo
vie populácia narásť, keď necháme v modeli pôsobiť rastovú mieru bez
obmedzenia.
Krajinám môžeme pripísať tieto trendy:
Slovensko, Česko, Rakúsko, Maďarsko a Poľsko – krátkodobo
stabilné, bez veľkých zmien.
Ukrajina – krátkodobo skôr pokles, čo zodpovedá realite.
Dlhodobé výsledky – sú enormné vo všetkých krajinách, čo
znamená, že exponenciálny model nie je vhodný na dlhé časové
obdobie.
X <- model.matrix(model_centered)[, -1]
XtX <- t(X) %*% X
eig <- eigen(XtX)
condition_number <- sqrt(max(eig$values) / min(eig$values))
condition_number
[1] 1.09998
udaje$GrowthRate_c <- scale(udaje$Growth.Rate, center=TRUE, scale=TRUE)
model_centered <- lm(log_pop ~ log_pop15 + GrowthRate_c + Continent, data = udaje)
summary(model_centered)
Call:
lm(formula = log_pop ~ log_pop15 + GrowthRate_c + Continent,
data = udaje)
Residuals:
Min 1Q Median 3Q Max
-0.228477 -0.018980 0.001669 0.020626 0.181035
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.095167 0.021598 4.406 1.62e-05 ***
log_pop15 0.999371 0.001313 761.114 < 2e-16 ***
GrowthRate_c 0.031415 0.003515 8.937 < 2e-16 ***
ContinentAsia -0.021732 0.009384 -2.316 0.021465 *
ContinentEurope -0.057497 0.010139 -5.671 4.32e-08 ***
ContinentNorth America -0.041139 0.010860 -3.788 0.000195 ***
ContinentOceania -0.048714 0.012870 -3.785 0.000197 ***
ContinentSouth America -0.024168 0.013950 -1.732 0.084559 .
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.04529 on 226 degrees of freedom
Multiple R-squared: 0.9997, Adjusted R-squared: 0.9997
F-statistic: 1.25e+05 on 7 and 226 DF, p-value: < 2.2e-16
library(MASS)
# Matrix-based analysis for predictive variables
X <- as.matrix(udaje[, c("log_pop15", "Growth.Rate")]) # historická populácia a rast
y <- udaje$log_pop # log-populácia 2020
lambda <- 0.01 # regularizačný parameter pre Ridge (môžeš upraviť)
A <- t(X) %*% X
AInv <- solve(A)
Alam <- t(X) %*% X + lambda * diag(ncol(X))
AlamInv <- solve(Alam)
cat("lambda =", lambda, "\n")
lambda = 0.01
cat("Matrix A:\n"); print(A)
Matrix A:
log_pop15 Growth.Rate
log_pop15 53207.913 3504.1094
Growth.Rate 3504.109 238.5454
cat("Matrix Alam (regularized):\n"); print(Alam)
Matrix Alam (regularized):
log_pop15 Growth.Rate
log_pop15 53207.923 3504.1094
Growth.Rate 3504.109 238.5554
cat("Inverse of A:\n"); print(AInv)
Inverse of A:
log_pop15 Growth.Rate
log_pop15 0.0005765852 -0.00846974
Growth.Rate -0.0084697403 0.12860820
cat("Inverse of Alam:\n"); print(AlamInv)
Inverse of Alam:
log_pop15 Growth.Rate
log_pop15 0.0005758655 -0.008458813
Growth.Rate -0.0084588128 0.128442299
cat("Eigenvalues of A:\n"); print(eigen(A)$values)
Eigenvalues of A:
[1] 53438.716800 7.741971
cat("Eigenvalues of Alam:\n"); print(eigen(Alam)$values)
Eigenvalues of Alam:
[1] 53438.726800 7.751971
Celkové zistenie:
Historická populácia je najsilnejším prediktorom budúcej
populácie.
Growth.Rate umožňuje predikciu rastu do budúcnosti (2025, 2030).
Kontinent zachytáva regionálne rozdiely.
Model umožňuje praktickú predikciu budúcej populácie pre všetky
krajiny a jej porovnanie medzi regiónmi.
Na základe našej analýzy môžeme povedať, že model funguje dobre
na krátkodobú projekciu, ale pri dlhšom časovom horizonte dochádza k
výraznému nadhodnocovaniu populácie. Preto tieto výsledky skôr ukazujú,
ako veľmi dokáže model reagovať na rastovú mieru, než to, ako bude
populácia v skutočnosti vyzerať.
LS0tCnRpdGxlOiAiUHLDoWNhIHMgZGF0YWLDoXpvdSAtIFdvcmxkIHBvcHVsYXRpb24iCmF1dGhvcjogIkJhcmJvcmEgS3VjaMOhcmlrb3bDoSAgPGJyPiIKZGF0ZTogIk5vdmVtYmVyIDIwMjUiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRoZW1lOiB1bml0ZWQKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIGNzczogY3VzdG9tLmNzcwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQplZGl0b3Jfb3B0aW9uczoKICBtYXJrZG93bjoKICAgIHdyYXA6IDcyCi0tLQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVjaG8gPSBUUlVFLAogIG1lc3NhZ2UgPSBGQUxTRSwKICB3YXJuaW5nID0gRkFMU0UKKQpgYGAKCmBgYHtyfQojIExvYWQgZGF0YQp1ZGFqZSA8LSByZWFkLmNzdigid29ybGRfcG9wdWxhdGlvbi5jc3YiLCBkZWM9Ii4iLCBzZXA9IiwiLCBoZWFkZXIgPSBUUlVFKQoKIyBTZWxlY3QgcmVsZXZhbnQgdmFyaWFibGVzIGZvciBwcmVkaWN0aW9uCnVkYWplX3ByZWQgPC0gdWRhamVbLCBjKCJDb3VudHJ5LlRlcnJpdG9yeSIsIlgyMDIwLlBvcHVsYXRpb24iLCAiWDIwMTUuUG9wdWxhdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJHcm93dGguUmF0ZSIsICJDb250aW5lbnQiKV0KCiMgTWVkaWFuIGltcHV0YXRpb24gZm9yIG51bWVyaWMgdmFyaWFibGVzCm51bWVyaWNfY29scyA8LSBjKCJYMjAyMC5Qb3B1bGF0aW9uIiwgIlgyMDE1LlBvcHVsYXRpb24iLCAiR3Jvd3RoLlJhdGUiKQpjb2x1bW5fbWVkaWFucyA8LSBzYXBwbHkodWRhamVfcHJlZFssIG51bWVyaWNfY29sc10sIG1lZGlhbiwgbmEucm0gPSBUUlVFKQpmb3IgKGNvbCBpbiBudW1lcmljX2NvbHMpIHsKICB1ZGFqZV9wcmVkW1tjb2xdXVtpcy5uYSh1ZGFqZV9wcmVkW1tjb2xdXSldIDwtIGNvbHVtbl9tZWRpYW5zW2NvbF0KfQoKIyBMb2ctdHJhbnNmb3JtIGZvciBtb2RlbGluZwp1ZGFqZV9wcmVkJGxvZ19wb3AgPC0gbG9nKHVkYWplX3ByZWQkWDIwMjAuUG9wdWxhdGlvbikKdWRhamVfcHJlZCRsb2dfcG9wMTUgPC0gbG9nKHVkYWplX3ByZWQkWDIwMTUuUG9wdWxhdGlvbikKCiMgRGF0YXNldCByZWFkeSBmb3IgcHJlZGljdGl2ZSBtb2RlbGluZwp1ZGFqZSA8LSB1ZGFqZV9wcmVkCmBgYAoqKsSMbyBza8O6bWFtZToqKgoKLSBQcmlwcmF2dWplbWUgZMOhdGEgbmEgcHJlZGlrY2l1IGJ1ZMO6Y2VqIHBvcHVsw6FjaWUuCgotIENow71iYWrDumNlIGhvZG5vdHkgZG9wxLrFiGFtZSBtZWRpw6Fub20sIGFieSBtb2RlbCBmdW5nb3ZhbCBzcHLDoXZuZS4KCi0gTG9nLXRyYW5zZm9ybcOhY2lhIHBvcHVsw6FjaWUgc3RhYmlsaXp1amUgZXh0csOpbW5lIHJvemRpZWx5IG1lZHppIG1hbMO9bWkgYSB2ZcS+a8O9bWkga3JhamluYW1pLgoKYGBge3J9CiMgUHJlIGJ1ZMO6Y3UgcG9wdWzDoWNpdSBuw6FzIG5hanZpYWMgemF1asOtbWEgaGlzdG9yaWNrw6EgcG9wdWzDoWNpYSBhIHJhc3QKeHZhcnMgPC0gdWRhamVbLCBjKCJYMjAxNS5Qb3B1bGF0aW9uIiwgIkdyb3d0aC5SYXRlIildCnJvdW5kKGNvcih4dmFycyksIDMpCmBgYAoqVGVyYXogemlzxaV1amVtZSwgYWtvIHPDuiBtZWR6aSBzZWJvdSBwcmVwb2plbsOpIGhpc3Rvcmlja8OhIHBvcHVsw6FjaWEgYSByb8SNbsO9IHJhc3QuIFbDpMSNxaFpbm91IGhpc3Rvcmlja8OhIHBvcHVsw6FjaWEgYSBHcm93dGguUmF0ZSBuaWUgc8O6IHNpbG5lIGtvcmVsb3ZhbsOpIOKGkiBvYmUgbcO0xb51IGJ5xaUgcG91xb5pdMOpIGFrbyBwcmVkaWt0b3J5IHByZSBidWTDumN1IHBvcHVsw6FjaXUuKgoKYGBge3J9CnBhaXJzKHh2YXJzLAogICAgICBtYWluID0gIlNjYXR0ZXJwbG90b3bDoSBtYXRpY2Eg4oCTIGhpc3Rvcmlja8OhIHBvcHVsw6FjaWEgYSBHcm93dGggUmF0ZSIpCmBgYAoqVml6dWFsaXp1amVtZSB2esWlYWggbWVkemkgaGlzdG9yaWNrb3UgcG9wdWzDoWNpb3UgYSByYXN0b20gYSBwb3pyaWVtZSBzYSwgxI1pIGV4aXN0dWrDuiBleHRyw6ltbmUgaG9kbm90eSBhbGVibyBuZXR5cGlja8OpIGtyYWppbnkuKgoKQWtvIG3DtMW+bWUgdmlkaWXFpSwgdsOkxI3FoWluYSBrcmFqw61uIHNhIG5hY2jDoWR6YSB2IHN0YWJpbG5vbSBww6FzbWUgcmFzdHUsIG5pZWt0b3LDqSBtYWrDuiBleHRyw6ltbmUgaG9kbm90eSwga3RvcsOpIG3DtMW+dSBvdnBseXZuacWlIHByZWRpa2NpdS4KCmBgYHtyfQpsaWJyYXJ5KGNhcikKdmlmKG1vZGVsKQpgYGAKCmBgYHtyfQpYIDwtIG1vZGVsLm1hdHJpeChtb2RlbClbLCAtMV0KWHRYIDwtIHQoWCkgJSolIFgKZWlnIDwtIGVpZ2VuKFh0WCkKCmNvbmRpdGlvbl9udW1iZXIgPC0gc3FydChtYXgoZWlnJHZhbHVlcykgLyBtaW4oZWlnJHZhbHVlcykpCmNvbmRpdGlvbl9udW1iZXIKYGBgCgoqQ2hjZW1lIHppc3RpxaUsIGFrbyBoaXN0b3JpY2vDoSBwb3B1bMOhY2lhLCByYXN0IGEga29udGluZW50IHZwbMO9dmFqw7ogbmEgcG9wdWzDoWNpdSAyMDIwLiBNb2RlbCBuw6FtIHrDoXJvdmXFiCBzbMO6xb5pIG5hIG92ZXJlbmllIHByZWRpa8SNbmVqIHNjaG9wbm9zdGkuKgpgYGB7cn0KIyBNb2RlbCBwcmVkaWtjaWUgcG9wdWzDoWNpZSAyMDIwCm1vZGVsX3ByZWQgPC0gbG0obG9nX3BvcCB+IGxvZ19wb3AxNSArIEdyb3d0aC5SYXRlICsgQ29udGluZW50LCBkYXRhID0gdWRhamUpCnN1bW1hcnkobW9kZWxfcHJlZCkKYGBgCkhpc3Rvcmlja8OhIHBvcHVsw6FjaWEgamUgbmFqc2lsbmVqxaHDrSBwcmVkaWt0b3IuCgpHcm93dGguUmF0ZSBtw6EgcG96aXTDrXZueSB2cGx5diDigJMga3JhamlueSBzIHZ5xaHFocOtbSByYXN0b20gYnVkw7ogdsOkxI3FoWllLgoKS29udGluZW50IHphY2h5dMOhdmEgcmVnaW9uw6FsbmUgcm96ZGllbHkgKG5hcHIuIEV1csOzcGEgbcOhIHByaWVtZXJuZSBtZW7FoWl1IHBvcHVsw6FjaXUgcHJpIHJvdm5ha2VqIGhpc3Rvcmlja2VqIHZlxL5rb3N0aSkuCgpgYGB7cn0KIyBTcMOkdG7DoSB0cmFuc2Zvcm3DoWNpYSBuYSBwb8SNZXQgb2J5dmF0ZcS+b3YKdWRhamUkcHJlZF9wb3AyMDIwIDwtIGV4cChwcmVkaWN0KG1vZGVsX3ByZWQsIG5ld2RhdGEgPSB1ZGFqZSkpCgojIFByZWRpa2NpYSBwb2TEvmEgR3Jvd3RoLlJhdGUKdWRhamUkcHJlZF9wb3AyMDI1IDwtIHVkYWplJFgyMDIwLlBvcHVsYXRpb24gKiAoMSArIHVkYWplJEdyb3d0aC5SYXRlKV41CnVkYWplJHByZWRfcG9wMjAzMCA8LSB1ZGFqZSRYMjAyMC5Qb3B1bGF0aW9uICogKDEgKyB1ZGFqZSRHcm93dGguUmF0ZSleMTAKCgojIC0tLSBWw71iZXIga29ua3LDqXRueWNoIGtyYWrDrW4gLS0tCnZ5YnJhbmVfa3JhamlueSA8LSBjKCJTbG92YWtpYSIsIAogICAgICAgICAgICAgICAgICAgICAiQ3plY2ggUmVwdWJsaWMiLCAKICAgICAgICAgICAgICAgICAgICAgIkh1bmdhcnkiLCAKICAgICAgICAgICAgICAgICAgICAgIkF1c3RyaWEiLCAKICAgICAgICAgICAgICAgICAgICAgIlVrcmFpbmUiLCAKICAgICAgICAgICAgICAgICAgICAgIlBvbGFuZCIpCgojIEZpbHRyb3ZhbmllIHBvZMS+YSBuw6F6dnUga3JhamlueQp1ZGFqZV92eWJyYW5lIDwtIHVkYWplW3VkYWplJENvdW50cnkuVGVycml0b3J5ICVpbiUgdnlicmFuZV9rcmFqaW55LCBdCgoKIyAtLS0gWm9icmF6ZW5pZSB2w71zbGVka3UgLS0tCnVkYWplX3Z5YnJhbmVbLCBjKCJDb3VudHJ5LlRlcnJpdG9yeSIsIAogICAgICAgICAgICAgICAgICAiWDIwMjAuUG9wdWxhdGlvbiIsIAogICAgICAgICAgICAgICAgICAicHJlZF9wb3AyMDIwIiwgCiAgICAgICAgICAgICAgICAgICJwcmVkX3BvcDIwMjUiLCAKICAgICAgICAgICAgICAgICAgInByZWRfcG9wMjAzMCIpXQpgYGAKKlByZWRpa3VqZW1lIHBvcHVsw6FjaXUgZG8gYnVkw7pjbm9zdGkgKDIwMjUgYSAyMDMwKSBwb21vY291IEdyb3d0aC5SYXRlLiBVbW/FvsWIdWplIG7DoW0gb2RoYWRuw7rFpSByYXN0IGEgcG9yb3ZuYcWlIGhvIG1lZHppIGtyYWppbmFtaS4qCgpLZcSPIHNhIHBvenJpZW1lIG5hIG5hxaFlIHbDvXNsZWRreSwgdmlkw61tZSwgxb5lIHbDvWNob2Rpc2tvdsOpIGhvZG5vdHkgcG9wdWzDoWNpZSB2IHJva3UgMjAyMCB6b2Rwb3ZlZGFqw7ogcmXDoWxueW0gcG/EjXRvbSBvYnl2YXRlxL5vdiB2IGplZG5vdGxpdsO9Y2gga3Jhamluw6FjaC4gTW9kZWwgdsWhYWsgcHJpIGRsaG9kb2JlaiBwcmVkaWtjaWkgKG5ham3DpCBzbWVyb20gayByb2t1IDIwMzApIGdlbmVydWplIGV4dHLDqW1uZSB2eXNva8OpIMSNw61zbGEuIFRvdG8gc2EgZGVqZSBwcmV0bywgxb5lIHNtZSBwb3XFvmlsaSBqZWRub2R1Y2jDvSB2w71wb8SNZXQgemFsb8W+ZW7DvSBuYSBleHBvbmVuY2nDoWxub20gcmFzdGUsIGt0b3LDvSBwcmkgZGxoxaFvbSBvYmRvYsOtIHZlxL5taSBwcmVow6HFiGEgdsO9c2xlZGt5LgoKViBrcsOhdGtvZG9ib20gaG9yaXpvbnRlIChkbyByb2t1IDIwMjUpIHPDuiBwcmVkcG92ZWRlIGXFoXRlIHJlbGF0w612bmUgcG9kb2Juw6kgcmVhbGl0ZSDigJMgcG9wdWzDoWNpYSBzYSBtZW7DrSBsZW4gbWllcm5lLgpWIGRsaG9kb2JvbSBob3Jpem9udGUgKHJvayAyMDMwKSB1xb4gbW9kZWwgcHJlZHBvdmVkw6EgbmVyZcOhbG5lIHZ5c29rw70gbsOhcmFzdCwgdGFrxb5lIHRpZXRvIMSNw61zbGEgbmV2bsOtbWFtZSBha28gc2t1dG/EjW7DuiBwcmVkcG92ZcSPLCBhbGUgc2vDtHIgYWtvIHVrw6HFvmt1IHRvaG8sIGFrbyByw71jaGxvIHZpZSBwb3B1bMOhY2lhIG5hcsOhc8WlLCBrZcSPIG5lY2jDoW1lIHYgbW9kZWxpIHDDtHNvYmnFpSByYXN0b3bDuiBtaWVydSBiZXogb2JtZWR6ZW5pYS4KCioqS3Jhamluw6FtIG3DtMW+ZW1lIHByaXDDrXNhxaUgdGlldG8gdHJlbmR5OioqCgoqU2xvdmVuc2tvLCDEjGVza28sIFJha8O6c2tvLCBNYcSPYXJza28gYSBQb8S+c2tvKiDigJMga3LDoXRrb2RvYm8gc3RhYmlsbsOpLCBiZXogdmXEvmvDvWNoIHptaWVuLgoKKlVrcmFqaW5hKiDigJMga3LDoXRrb2RvYm8gc2vDtHIgcG9rbGVzLCDEjW8gem9kcG92ZWTDoSByZWFsaXRlLgoKKkRsaG9kb2LDqSB2w71zbGVka3kqIOKAkyBzw7ogZW5vcm1uw6kgdm8gdsWhZXRrw71jaCBrcmFqaW7DoWNoLCDEjW8gem5hbWVuw6EsIMW+ZSBleHBvbmVuY2nDoWxueSBtb2RlbCBuaWUgamUgdmhvZG7DvSBuYSBkbGjDqSDEjWFzb3bDqSBvYmRvYmllLgoKCmBgYHtyfQpYIDwtIG1vZGVsLm1hdHJpeChtb2RlbF9jZW50ZXJlZClbLCAtMV0KWHRYIDwtIHQoWCkgJSolIFgKZWlnIDwtIGVpZ2VuKFh0WCkKCmNvbmRpdGlvbl9udW1iZXIgPC0gc3FydChtYXgoZWlnJHZhbHVlcykgLyBtaW4oZWlnJHZhbHVlcykpCmNvbmRpdGlvbl9udW1iZXIKYGBgCmBgYHtyfQp1ZGFqZSRHcm93dGhSYXRlX2MgPC0gc2NhbGUodWRhamUkR3Jvd3RoLlJhdGUsIGNlbnRlcj1UUlVFLCBzY2FsZT1UUlVFKQptb2RlbF9jZW50ZXJlZCA8LSBsbShsb2dfcG9wIH4gbG9nX3BvcDE1ICsgR3Jvd3RoUmF0ZV9jICsgQ29udGluZW50LCBkYXRhID0gdWRhamUpCnN1bW1hcnkobW9kZWxfY2VudGVyZWQpCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KE1BU1MpCgojIE1hdHJpeC1iYXNlZCBhbmFseXNpcyBmb3IgcHJlZGljdGl2ZSB2YXJpYWJsZXMKWCA8LSBhcy5tYXRyaXgodWRhamVbLCBjKCJsb2dfcG9wMTUiLCAiR3Jvd3RoLlJhdGUiKV0pICAjIGhpc3Rvcmlja8OhIHBvcHVsw6FjaWEgYSByYXN0CnkgPC0gdWRhamUkbG9nX3BvcCAgIyBsb2ctcG9wdWzDoWNpYSAyMDIwCgpsYW1iZGEgPC0gMC4wMSAgCkEgPC0gdChYKSAlKiUgWApBSW52IDwtIHNvbHZlKEEpCkFsYW0gPC0gdChYKSAlKiUgWCArIGxhbWJkYSAqIGRpYWcobmNvbChYKSkKQWxhbUludiA8LSBzb2x2ZShBbGFtKQoKY2F0KCJsYW1iZGEgPSIsIGxhbWJkYSwgIlxuIikKY2F0KCJNYXRyaXggQTpcbiIpOyBwcmludChBKQpjYXQoIk1hdHJpeCBBbGFtIChyZWd1bGFyaXplZCk6XG4iKTsgcHJpbnQoQWxhbSkKY2F0KCJJbnZlcnNlIG9mIEE6XG4iKTsgcHJpbnQoQUludikKY2F0KCJJbnZlcnNlIG9mIEFsYW06XG4iKTsgcHJpbnQoQWxhbUludikKY2F0KCJFaWdlbnZhbHVlcyBvZiBBOlxuIik7IHByaW50KGVpZ2VuKEEpJHZhbHVlcykKY2F0KCJFaWdlbnZhbHVlcyBvZiBBbGFtOlxuIik7IHByaW50KGVpZ2VuKEFsYW0pJHZhbHVlcykKYGBgCiMjICoqKkNlbGtvdsOpIHppc3RlbmllOioqKgoKSGlzdG9yaWNrw6EgcG9wdWzDoWNpYSBqZSBuYWpzaWxuZWrFocOtbSBwcmVkaWt0b3JvbSBidWTDumNlaiBwb3B1bMOhY2llLgoKR3Jvd3RoLlJhdGUgdW1vxb7FiHVqZSBwcmVkaWtjaXUgcmFzdHUgZG8gYnVkw7pjbm9zdGkgKDIwMjUsIDIwMzApLgoKS29udGluZW50IHphY2h5dMOhdmEgcmVnaW9uw6FsbmUgcm96ZGllbHkuCgpNb2RlbCB1bW/FvsWIdWplIHByYWt0aWNrw7ogcHJlZGlrY2l1IGJ1ZMO6Y2VqIHBvcHVsw6FjaWUgcHJlIHbFoWV0a3kga3JhamlueSBhIGplaiBwb3Jvdm5hbmllIG1lZHppIHJlZ2nDs25taS4KCipOYSB6w6FrbGFkZSBuYcWhZWogYW5hbMO9enkgbcO0xb5lbWUgcG92ZWRhxaUsIMW+ZSBtb2RlbCBmdW5ndWplIGRvYnJlIG5hIGtyw6F0a29kb2LDuiBwcm9qZWtjaXUsIGFsZSBwcmkgZGxoxaFvbSDEjWFzb3ZvbSBob3Jpem9udGUgZG9jaMOhZHphIGsgdsO9cmF6bsOpbXUgbmFkaG9kbm9jb3Zhbml1IHBvcHVsw6FjaWUuIFByZXRvIHRpZXRvIHbDvXNsZWRreSBza8O0ciB1a2F6dWrDuiwgYWtvIHZlxL5taSBkb2vDocW+ZSBtb2RlbCByZWFnb3ZhxaUgbmEgcmFzdG92w7ogbWllcnUsIG5lxb4gdG8sIGFrbyBidWRlIHBvcHVsw6FjaWEgdiBza3V0b8SNbm9zdGkgdnl6ZXJhxaUuKgoKCgoKCgoK