1. Úvod
Po autokorelácii a heteroskedasticite rezíduí je multikolinearita
tretím závažným porušením predpokladov použitia metódy najmenších
štvorcov. Tu sa okrem iného predpokladá, že matica \(\mathbf X\) je tvorená lineárne nezávislými
riadkami a tiež stĺpcami, čo zabezpečí regularitu matice \(\mathbf X^T\mathbf X\) a teda možnosť jej
inverzie. Tá sa používa pri odhadoch regresných koeficientov. V praxi sa
ale môže stať, že vzniká „takmer singulárna“ matica \(\mathbf X^T\mathbf X\), t. j. matica \(\mathbf X\) je tvorená „približne“ lineárne
závislými stĺpcami, t. j. existuje taká ich lineárna kombinácia, v
ktorej
\[
x_{il} = \alpha_0 + \alpha_1 x_{i1} + \dots + \alpha_{l-1} x_{i,(l-1)} +
\alpha_{l+1} x_{i,(l+1)} + \alpha_k x_{i,k} + \nu_i
\]
Tu \(\nu_i\) sú rádovo menšie čísla
než regresory \(x_{.}\), t. j. \((\forall i)(\nu_i << x_{.,i})\). V
tomto prípade je inverzná matica \((\mathbf
X^T\mathbf X)^{-1}\) veľmi nestabilná a obsahuje na hlavnej
diagonále veľmi veľké hodnoty. Táto matica sa používa pri výpočtoch
\[
\hat \beta = (\mathbf X^T\mathbf X)^{-1} \mathbf X^T \mathbf y
\] a tiež \[
\text{std}(\beta_i) = \sqrt{\sigma^2 (\mathbf X^T \mathbf X)^{-1}_{ii}}.
\] To spôsobuje nestabilitu odhadovaných regresných koeficientov
a ich nadhodnotené rozptyly.
Tento problém nazývame problémom
multikolinearity.
2. Dôsledky multikolinearity
Multikolinearita patrí medzi najčastejšie problémy viacnásobnej
lineárnej regresie.
Je dôležité jasne rozlišovať dva fakty:
- Nespôsobuje skreslené (biased) odhady
koeficientov.
- Nadhodnocuje odhady štandardných odchýlok regresných
koeficientov a vedie potom k falošnému neprijímaniu
alternatívnej hypotézy o štatistickej významnosti jednotlivých
regresorov.
- Odhadované regresné koeficienty sú nestabilné – pri malej zmene
údajov sa prudko menia koeficienty ako aj ich znamienka.
- Interpretácia regresného modelu je z dôvodu vyššie uvedených dôvodov
nespoľahlivá.
3. Východiskový model a údaje
Budeme pracovať s prierezovým regresným modelom, kde pre každú obec
uvažujeme počet obyvateľov v decembri 2024 ako závislú premennú a počty
obyvateľov v predchádzajúcich mesiacoch toho istého roku ako
vysvetľujúce premenné:
\[
\text{Pop12}_i = \beta_0 + \beta_1 \text{Pop11}_i + \beta_2
\text{Pop10}_i + \beta_3 \text{Pop09}_i + u_i,
\]
kde:
- \(\text{Pop12}_i\) – počet
obyvateľov obce \(i\) v decembri
2024,
- \(\text{Pop11}_i\) – počet
obyvateľov v novembri 2024,
- \(\text{Pop10}_i\) – počet
obyvateľov v októbri 2024,
- \(\text{Pop09}_i\) – počet
obyvateľov v septembri 2024.
Údaje máme z databázy počtu obyvateľov slovenských obcí podľa
mesiacov (rok 2024). Po načítaní ich uložíme do data.frame
udaje a zároveň premenujeme stĺpce na kratšie
názvy.
udaje <- read.csv("Databaza.csv",
sep = ";",
dec = ".",
header = TRUE,
stringsAsFactors = FALSE)
names(udaje)
[1] "Okres" "Obec" "X2024M12" "X2024M11" "X2024M10" "X2024M09" "X2024M08"
[8] "X2024M07" "X2024M06" "X2024M05" "X2024M04" "X2024M03" "X2024M02" "X2024M01"
# vyberieme si len stĺpce, ktoré potrebujeme
udaje <- udaje[, c("Okres", "Obec", "X2024M12", "X2024M11", "X2024M10", "X2024M09")]
# premenujeme stĺpce na jednoduchšie názvy
colnames(udaje) <- c("Okres", "Obec", "Pop12", "Pop11", "Pop10", "Pop09")
head(udaje)
V našej databáze nemáme chýbajúce hodnoty, takže nie je potrebná
žiadna imputácia.
4. Odhad základného regresného modelu
model <- lm(Pop12 ~ Pop11 + Pop10 + Pop09,
data = udaje)
summary(model)
Call:
lm(formula = Pop12 ~ Pop11 + Pop10 + Pop09, data = udaje)
Residuals:
Min 1Q Median 3Q Max
-44.442 -2.185 0.749 3.659 50.908
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -1.70441 0.68551 -2.486 0.0137 *
Pop11 1.24863 0.06192 20.164 < 2e-16 ***
Pop10 0.09468 0.12167 0.778 0.4374
Pop09 -0.34283 0.08212 -4.175 4.48e-05 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 8.856 on 196 degrees of freedom
Multiple R-squared: 1, Adjusted R-squared: 1
F-statistic: 1.19e+08 on 3 and 196 DF, p-value: < 2.2e-16
Vo výsledkoch zvyčajne vidíme veľmi vysoký koeficient determinácie a
koeficienty pri Pop11, Pop10 a Pop09 sú si veľmi podobné. To je
intuitívne, pretože počty obyvateľov v jednotlivých mesiacoch sa menia
len veľmi málo a sú takmer perfektné lineárne kombinácie.
5. Korelačná matica
Korelácia dokáže zachytiť párové vzťahy medzi premennými. Ak medzi
niektorými vysvetľujúcimi premennými je vysoká korelácia (signalizujúca
multikolinearitu), potom je najjednoduchšie ju zo zoznamu regresorov
vylúčiť. Korelácie sa dajú aj testovať, alebo len vyčísliť a potom podľa
intuitívneho pravidla vylúčiť jednu premennú, ktorá má koreláciu s inou
premennou v absolútnej hodnote vyššiu ako 0.8, resp. 0.9.
xvars <- udaje[, c("Pop11", "Pop10", "Pop09")]
round(cor(xvars), 3)
Pop11 Pop10 Pop09
Pop11 1 1 1
Pop10 1 1 1
Pop09 1 1 1
V našom prípade uvidíme, že korelácie medzi mesačnými počtami
obyvateľov sú prakticky rovné 1. To je extrémny príklad multikolinearity
– všetky vysvetľujúce premenné obsahujú takmer tú istú informáciu.
Korelačný vzťah sa dá vytušiť aj z jednoduchých párových
scatterplotov ako je to na nasledujúcom obrázku – body ležia takmer na
priamke.
pairs(xvars,
main = "Scatterplotová matica – premenné Pop11, Pop10, Pop09")

6. VIF
Indikátorom multikolinearity u premennej, ktorá multikolinearitu
zapríčiňuje, je Variance Inflation Factor (VIF). Pre premennú \(x_j\) je potom
\[
VIF_j = \frac{1}{1 - R_j^2}
\]
kde \(R_j^2\) pochádza z
regresie:
\[
X_j = \gamma_0 + \gamma_1 X_1 + \cdots + \gamma_{j-1} X_{j-1}
+ \gamma_{j+1} X_{j+1} + u.
\]
Pop11 Pop10 Pop09
1367712 5278985 2404844
Intuitívnym kritériom, ktoré signalizuje prítomnosť multikolinearity,
je podmienka VIF > 5 (prísne kritérium), alebo VIF > 10 (menej
prísne kritérium). V našich dátach sú VIF-y typicky veľmi vysoké, čo
zodpovedá takmer dokonalej korelácii medzi mesačnými hodnotami počtu
obyvateľov.
7. Condition Number
Pri existencii multikolinearity sa model prejavuje tak, že koeficient
determinácie je síce vysoký a zdá sa, že model je veľmi dobrý, ale
regresné koeficienty nemusia byť stabilné a ich štandardné odchýlky môžu
byť nafúknuté. Uvedomíme si to, ak sa pozrieme, ako sa počítajú – t.
j.
\[
\text{std}(\beta_i) = \sqrt{\sigma^2 (\mathbf X^T \mathbf X)^{-1}_{ii}},
\]
kde rozhodujúci je \(i\)-ty prvok
hlavnej diagonály matice \((\mathbf X^T
\mathbf X)^{-1}\). Tie prvky sú v prípade podobnosti
vysvetľujúcich premenných mimoriadne veľké. Túto situáciu zachytáva
nasledovný ukazovateľ.
Pri výpočte Condition number \(\kappa\) sa používa vzorec
\[
\kappa = \frac{\theta_{\text{max}}}{\theta_{\text{min}}}
\]
kde \(\theta_.\) sú vlastné čísla
matice. Condition number nie je test, je to len indikátor, ktorý
posudzuje mieru multikolinearity medzi premennými. Používame intuitívne
pravidlo. Ak Condition number je
- < 10 → nízka multikolinearita,
- 10–30 → mierna,
- 30–100 → silná,
100 → veľmi vážna.
V našom prípade to vypočítame nasledovne:
X <- model.matrix(model)[, -1]
XtX <- t(X) %*% X
eig <- eigen(XtX)
condition_number <- sqrt(max(eig$values) / min(eig$values))
condition_number
[1] 5315.386
Keďže u nás tento indikátor typicky výrazne presahuje hodnotu 100,
signalizuje prítomnosť závažnej multikolinearity medzi mesačnými
údajmi.
Vlastné číslo štvorcovej matice \(\mathbf
X^T \mathbf X\) je číslo \(\theta_j\), pre ktoré platí \((\mathbf X^T \mathbf X)\mathbf h^j =
\theta_j\mathbf h^j\). \(\mathbf
h^j\) je tzv. vlastný vektor tejto matice. Máme toľko vlastných
čísel (teda aj vlastných vektorov), koľko obsahuje matica \(\mathbf X^T \mathbf X\) riadkov
(stĺpcov).
Môže sa stať, že VIF faktor nesignalizuje multikolinearitu u žiadnej
„jednej“ vysvetľujúcej veličiny, ale sú navzájom prepojené cyklickými
lineárnymi závislosťami všetky premenné. To zachytáva práve Condition
Number.
8. Riešenia multikolinearity
Vynechanie premennej
Pokúsme sa vynechať postupne dve premenné, ktoré majú najvyšší VIF (u
nás sú VIF-y vysoké pre všetky), a porovnajme následne upravené
koeficienty determinácie oboch nových modelov:
model_noPop10 <- lm(Pop12 ~ Pop11 + Pop09, data = udaje)
summary(model_noPop10)
Call:
lm(formula = Pop12 ~ Pop11 + Pop09, data = udaje)
Residuals:
Min 1Q Median 3Q Max
-43.171 -2.310 0.847 3.530 50.940
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -1.68757 0.68448 -2.465 0.0145 *
Pop11 1.28666 0.03798 33.876 < 2e-16 ***
Pop09 -0.28619 0.03799 -7.534 1.74e-12 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 8.847 on 197 degrees of freedom
Multiple R-squared: 1, Adjusted R-squared: 1
F-statistic: 1.789e+08 on 2 and 197 DF, p-value: < 2.2e-16
model_noPop11 <- lm(Pop12 ~ Pop10 + Pop09, data = udaje)
summary(model_noPop11)
Call:
lm(formula = Pop12 ~ Pop10 + Pop09, data = udaje)
Residuals:
Min 1Q Median 3Q Max
-53.303 -4.884 -0.385 4.526 85.897
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.8557 1.1967 -0.715 0.475
Pop10 2.0312 0.1307 15.546 < 2e-16 ***
Pop09 -1.0305 0.1307 -7.888 2.07e-13 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 15.49 on 197 degrees of freedom
Multiple R-squared: 1, Adjusted R-squared: 1
F-statistic: 5.836e+07 on 2 and 197 DF, p-value: < 2.2e-16
V našej databáze zvyčajne uvidíme, že vynechanie jednej z veľmi
podobných mesačných premenných zníži upravený koeficient determinácie
len minimálne. To je typické správanie pri silnej multikolinearite –
jedna premenná dokáže takmer dokonale „zastúpiť“ inú.
Škálovanie premenných
Škálovanie môže byť veľmi efektívne z hľadiska numerickej stability
(najmä pri veľmi rozdielnych rádoch premenných), znižuje však
interpretovateľnosť modelu. Ide o úpravu premenných podľa nasledovného
vzorca:
\[
x^{scale} = \frac{x-M}{\sqrt{D}}
\]
kde \(M\) je stredná hodnota
(priemer) a \(D\) je rozptyl
premennej.
V našej databáze sú všetky premenné v počte obyvateľov, teda už sú v
porovnateľných jednotkách. Napriek tomu si škálovanie ukážeme, aby sme
videli vplyv na multikolinearitu.
udaje$Pop11_c <- scale(udaje$Pop11, center = TRUE, scale = TRUE)
udaje$Pop10_c <- scale(udaje$Pop10, center = TRUE, scale = TRUE)
udaje$Pop09_c <- scale(udaje$Pop09, center = TRUE, scale = TRUE)
model_centered <- lm(Pop12 ~ Pop11_c + Pop10_c + Pop09_c,
data = udaje)
summary(model_centered)
Call:
lm(formula = Pop12 ~ Pop11_c + Pop10_c + Pop09_c, data = udaje)
Residuals:
Min 1Q Median 3Q Max
-44.442 -2.185 0.749 3.659 50.908
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 4930.9000 0.6262 7874.461 < 2e-16 ***
Pop11_c 14803.9118 734.1607 20.164 < 2e-16 ***
Pop10_c 1122.4034 1442.3446 0.778 0.437
Pop09_c -4064.1462 973.5030 -4.175 4.48e-05 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 8.856 on 196 degrees of freedom
Multiple R-squared: 1, Adjusted R-squared: 1
F-statistic: 1.19e+08 on 3 and 196 DF, p-value: < 2.2e-16
Pop11_c Pop10_c Pop09_c
1367712 5278985 2404844
Condition Number je:
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] 4909.792
Z výsledkov vidíme, že škálovanie môže zlepšiť numerické vlastnosti
matice (Condition Number), ale multikolinearitu medzi mesačnými
hodnotami ako takú neodstraňuje – dáta stále obsahujú takmer tú istú
informáciu.
Iná úprava premennej, ktorá zachová interpretovateľnosť
Ak sa chceme vyhnúť strate interpretovateľnosti, môžeme niekedy
zmeniť jednotky premennej, ktorá je v úplne iných rádoch než ostatné. V
pôvodnom príklade to bola premenná GDP, ktorá bola v dolároch. V našej
databáze sú všetky premenné v počte obyvateľov, takže tento problém
nemáme. Pre ilustráciu si však ukážeme prevod jednej premennej na
„tisíce obyvateľov“:
udaje$Pop11k <- udaje$Pop11 / 1000
head(udaje[, c("Obec", "Pop12", "Pop11", "Pop11k")])
Potom lineárny model dosiahne výsledky:
model_Pop11k <- lm(Pop12 ~ Pop11k + Pop10 + Pop09,
data = udaje)
summary(model_Pop11k)
Call:
lm(formula = Pop12 ~ Pop11k + Pop10 + Pop09, data = udaje)
Residuals:
Min 1Q Median 3Q Max
-44.442 -2.185 0.749 3.659 50.908
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -1.70441 0.68551 -2.486 0.0137 *
Pop11k 1248.62877 61.92243 20.164 < 2e-16 ***
Pop10 0.09468 0.12167 0.778 0.4374
Pop09 -0.34283 0.08212 -4.175 4.48e-05 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 8.856 on 196 degrees of freedom
Multiple R-squared: 1, Adjusted R-squared: 1
F-statistic: 1.19e+08 on 3 and 196 DF, p-value: < 2.2e-16
Pop11k Pop10 Pop09
1367712 5278985 2404844
V tomto prípade sa koeficient pri Pop11k zmení len v jednotkách (na
tisíce obyvateľov), ale samotná multikolinearita nezmizne, pretože
Pop11, Pop10 a Pop09 sú stále veľmi podobné.
Poznámka: Dummy premenné (0/1) neškálujeme.
### očistenie databázy od „pracovných“ stĺpcov
library(dplyr)
udaje <- udaje %>%
dplyr::select(Okres, Obec, Pop12, Pop11, Pop10, Pop09)
PCA (voliteľné)
PCA nám vytvára nový pracovný vektor (komponent), ktorý je definovaný
ako vážený súčet dvoch alebo viacerých korelovaných premenných. Tento
komponent potom vystupuje ako vysvetľujúca veličina, zatiaľ čo pôvodné
premenné vylúčime zo zoznamu regresorov. Tým sa redukuje počet
vysvetľujúcich veličín a môže sa zmierniť problém multikolinearity.
V našom prípade vytvoríme prvú hlavnú komponentu z premenných Pop11,
Pop10 a Pop09:
X_pca <- scale(udaje[, c("Pop11", "Pop10", "Pop09")],
center = TRUE, scale = TRUE)
pca_res <- prcomp(X_pca)
summary(pca_res)
Importance of components:
PC1 PC2 PC3
Standard deviation 1.732 0.000992 0.0003528
Proportion of Variance 1.000 0.000000 0.0000000
Cumulative Proportion 1.000 1.000000 1.0000000
udaje$PC1 <- pca_res$x[, 1]
model_pca <- lm(Pop12 ~ PC1, data = udaje)
summary(model_pca)
Call:
lm(formula = Pop12 ~ PC1, data = udaje)
Residuals:
Min 1Q Median 3Q Max
-80.322 -3.752 -0.201 3.493 112.824
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 4930.9000 1.1468 4300 <2e-16 ***
PC1 -6848.6258 0.6638 -10318 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 16.22 on 198 degrees of freedom
Multiple R-squared: 1, Adjusted R-squared: 1
F-statistic: 1.065e+08 on 1 and 198 DF, p-value: < 2.2e-16
# VIF sa nedá počítať, lebo model má len jeden vysvetľujúci regresor
# vif(model_pca)
Hrebeňová regresia (Ridge Regression – voliteľné)
Hrebeňová regresia je modifikácia regresie, ktorá zavádza perturbácie
do matice \(\mathbf X^T \mathbf X\)
tak, aby znížila dôsledky multikolinearity. Treba ale upozorniť, že
odhadované regresné koeficienty sú skreslené. Perturbácia vyzerá
nasledovne:
\[
(\mathbf X^T \mathbf X + \lambda \mathbf I)
\]
V tomto kroku si vypíšeme výsledky odhadov regresných koeficientov s
meniacimi sa parametrami \(\lambda\),
aby sme získali určitú predstavu o číselnom ráde prvkov:
library(MASS)
X <- as.matrix(udaje[, c("Pop11", "Pop10", "Pop09")])
y <- udaje$Pop12
ridge_fit <- lm.ridge(y ~ X, lambda = seq(0, 10, 1))
ridge_fit
XPop11 XPop10 XPop09
0 -1.704409 1.2486288 0.09468307 -0.3428288
1 8.529458 0.3331203 0.33297224 0.3328418
2 16.706357 0.3324811 0.33243251 0.3323613
3 24.856002 0.3319011 0.33188566 0.3318343
4 32.978628 0.3313373 0.33133840 0.3312969
5 41.074389 0.3307811 0.33079207 0.3307565
6 49.143424 0.3302296 0.33024709 0.3302155
7 57.185869 0.3296815 0.32970366 0.3296750
8 65.201856 0.3291362 0.32916186 0.3291353
9 73.191517 0.3285934 0.32862173 0.3285968
10 81.154981 0.3280529 0.32808330 0.3280597
Nastavovanie rôznych hodnôt \(\lambda\) je predmetom hlbšej analýzy,
tento prehľad je len jej časťou.
10. Zhrnutie
- Multikolinearita nezavádza bias, ale zvyšuje štandardné odchýlky
odhadovaných regresných koeficientov a robí ich nestabilnými.
- Testy (resp. diagnostické ukazovatele) ako VIF a Condition Number
umožňujú diagnostiku multikolinearity.
- Riešenia zahŕňajú: vynechanie premennej, centrovanie/škálovanie,
zmenu jednotiek, prípadne použitie PCA alebo hrebeňovej regresie. PCA a
hrebeňová regresia vyžadujú hlbšie vedomosti, preto ich tu uvádzame len
formálne a nevyžadujeme ich detailný výpočet.
LS0tCnRpdGxlOiAiQ3ZpxI1lbmllIDEwIOKAkyBNdWx0aWtvbGluZWFyaXRhIHYgcmVncmVzbsO9Y2ggbW9kZWxvY2giCmF1dGhvcjogIkZpbGlwIEp1cmvDocSNZWsgKHphIHBvbW9jaSBDaGF0R1BUKSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRoZW1lOiB1bml0ZWQKICAgIGhpZ2hsaWdodDogdGFuZ28KZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvID0gVFJVRSwKICBtZXNzYWdlID0gRkFMU0UsCiAgd2FybmluZyA9IEZBTFNFCikKYGBgCgojIDEuIMOadm9kCgpQbyBhdXRva29yZWzDoWNpaSBhIGhldGVyb3NrZWRhc3RpY2l0ZSByZXrDrWR1w60gamUgbXVsdGlrb2xpbmVhcml0YSB0cmV0w61tIHrDoXZhxb5uw71tIHBvcnXFoWVuw61tIHByZWRwb2tsYWRvdiBwb3XFvml0aWEgbWV0w7NkeSBuYWptZW7FocOtY2ggxaF0dm9yY292LiBUdSBzYSBva3JlbSBpbsOpaG8gcHJlZHBva2xhZMOhLCDFvmUgbWF0aWNhICRcbWF0aGJmIFgkIGplIHR2b3JlbsOhIGxpbmXDoXJuZSBuZXrDoXZpc2zDvW1pIHJpYWRrYW1pIGEgdGllxb4gc3TEunBjYW1pLCDEjW8gemFiZXpwZcSNw60gcmVndWxhcml0dSBtYXRpY2UgICRcbWF0aGJmIFheVFxtYXRoYmYgWCQgYSB0ZWRhIG1vxb5ub3PFpSBqZWogaW52ZXJ6aWUuIFTDoSBzYSBwb3XFvsOtdmEgcHJpIG9kaGFkb2NoIHJlZ3Jlc27DvWNoIGtvZWZpY2llbnRvdi4gViBwcmF4aSBzYSBhbGUgbcO0xb5lIHN0YcWlLCDFvmUgdnpuaWvDoSDigJ50YWttZXIgc2luZ3Vsw6FybmHigJwgbWF0aWNhICRcbWF0aGJmIFheVFxtYXRoYmYgWCQsIHQuIGouIG1hdGljYSAkXG1hdGhiZiBYJCBqZSB0dm9yZW7DoSDigJ5wcmlibGnFvm5l4oCcIGxpbmXDoXJuZSB6w6F2aXNsw71taSBzdMS6cGNhbWksIHQuIGouIGV4aXN0dWplIHRha8OhIGljaCBsaW5lw6FybmEga29tYmluw6FjaWEsIHYga3RvcmVqICAKClxbCnhfe2lsfSA9IFxhbHBoYV8wICsgXGFscGhhXzEgeF97aTF9ICsgXGRvdHMgKyBcYWxwaGFfe2wtMX0geF97aSwobC0xKX0gKyBcYWxwaGFfe2wrMX0geF97aSwobCsxKX0gKyAgXGFscGhhX2sgeF97aSxrfSArICBcbnVfaQpcXQoKVHUgJFxudV9pJCBzw7ogcsOhZG92byBtZW7FoWllIMSNw61zbGEgbmXFviByZWdyZXNvcnkgJHhfey59JCwgdC4gai4gJChcZm9yYWxsIGkpKFxudV9pIDw8IHhfey4saX0pJC4gViB0b210byBwcsOtcGFkZSBqZSBpbnZlcnpuw6EgbWF0aWNhICQoXG1hdGhiZiBYXlRcbWF0aGJmIFgpXnstMX0kIHZlxL5taSBuZXN0YWJpbG7DoSBhIG9ic2FodWplIG5hIGhsYXZuZWogZGlhZ29uw6FsZSB2ZcS+bWkgdmXEvmvDqSBob2Rub3R5LiBUw6F0byBtYXRpY2Egc2EgcG91xb7DrXZhIHByaSB2w71wb8SNdG9jaCAKXFsKXGhhdCBcYmV0YSA9IChcbWF0aGJmIFheVFxtYXRoYmYgWCleey0xfSBcbWF0aGJmIFheVCBcbWF0aGJmIHkKXF0KYSB0aWXFviAKXFsKXHRleHR7c3RkfShcYmV0YV9pKSA9IFxzcXJ0e1xzaWdtYV4yIChcbWF0aGJmIFheVCBcbWF0aGJmIFgpXnstMX1fe2lpfX0uClxdClRvIHNww7Rzb2J1amUgbmVzdGFiaWxpdHUgb2RoYWRvdmFuw71jaCByZWdyZXNuw71jaCBrb2VmaWNpZW50b3YgYSBpY2ggbmFkaG9kbm90ZW7DqSByb3pwdHlseS4KClRlbnRvIHByb2Jsw6ltIG5hesO9dmFtZSBwcm9ibMOpbW9tICoqbXVsdGlrb2xpbmVhcml0eSoqLgoKLS0tCgojIDIuIETDtHNsZWRreSBtdWx0aWtvbGluZWFyaXR5CgpNdWx0aWtvbGluZWFyaXRhIHBhdHLDrSBtZWR6aSBuYWrEjWFzdGVqxaFpZSBwcm9ibMOpbXkgdmlhY27DoXNvYm5laiBsaW5lw6FybmVqIHJlZ3Jlc2llLiAgCkplIGTDtGxlxb5pdMOpIGphc25lIHJvemxpxaFvdmHFpSBkdmEgZmFrdHk6CgoxLiAqKk5lc3DDtHNvYnVqZSBza3Jlc2xlbsOpIChiaWFzZWQpKiogb2RoYWR5IGtvZWZpY2llbnRvdi4KMi4gKipOYWRob2Rub2N1amUgb2RoYWR5IMWhdGFuZGFyZG7DvWNoIG9kY2jDvWxvayByZWdyZXNuw71jaCBrb2VmaWNpZW50b3YqKiBhIHZlZGllIHBvdG9tIGsgZmFsb8WhbsOpbXUgbmVwcmlqw61tYW5pdSBhbHRlcm5hdMOtdm5laiBoeXBvdMOpenkgbyDFoXRhdGlzdGlja2VqIHbDvXpuYW1ub3N0aSBqZWRub3RsaXbDvWNoIHJlZ3Jlc29yb3YuCjMuIE9kaGFkb3ZhbsOpIHJlZ3Jlc27DqSBrb2VmaWNpZW50eSBzw7ogbmVzdGFiaWxuw6kg4oCTIHByaSBtYWxlaiB6bWVuZSDDumRham92IHNhIHBydWRrbyBtZW5pYSBrb2VmaWNpZW50eSBha28gYWogaWNoIHpuYW1pZW5rYS4KNC4gSW50ZXJwcmV0w6FjaWEgcmVncmVzbsOpaG8gbW9kZWx1IGplIHogZMO0dm9kdSB2ecWhxaFpZSB1dmVkZW7DvWNoIGTDtHZvZG92IG5lc3BvxL5haGxpdsOhLgoKLS0tCgojIDMuIFbDvWNob2Rpc2tvdsO9IG1vZGVsIGEgw7pkYWplCgpCdWRlbWUgcHJhY292YcWlIHMgcHJpZXJlem92w71tIHJlZ3Jlc27DvW0gbW9kZWxvbSwga2RlIHByZSBrYcW+ZMO6IG9iZWMgdXZhxb51amVtZSBwb8SNZXQgb2J5dmF0ZcS+b3YgdiBkZWNlbWJyaSAyMDI0IGFrbyB6w6F2aXNsw7ogcHJlbWVubsO6IGEgcG/EjXR5IG9ieXZhdGXEvm92IHYgcHJlZGNow6FkemFqw7pjaWNoIG1lc2lhY29jaCB0b2hvIGlzdMOpaG8gcm9rdSBha28gdnlzdmV0xL51asO6Y2UgcHJlbWVubsOpOgoKXFsKXHRleHR7UG9wMTJ9X2kgPSBcYmV0YV8wICsgXGJldGFfMSBcdGV4dHtQb3AxMX1faSArIFxiZXRhXzIgXHRleHR7UG9wMTB9X2kgKyBcYmV0YV8zIFx0ZXh0e1BvcDA5fV9pICsgdV9pLApcXQoKa2RlOgoKLSAkXHRleHR7UG9wMTJ9X2kkIOKAkyBwb8SNZXQgb2J5dmF0ZcS+b3Ygb2JjZSAkaSQgdiBkZWNlbWJyaSAyMDI0LCAgCi0gJFx0ZXh0e1BvcDExfV9pJCDigJMgcG/EjWV0IG9ieXZhdGXEvm92IHYgbm92ZW1icmkgMjAyNCwgIAotICRcdGV4dHtQb3AxMH1faSQg4oCTIHBvxI1ldCBvYnl2YXRlxL5vdiB2IG9rdMOzYnJpIDIwMjQsICAKLSAkXHRleHR7UG9wMDl9X2kkIOKAkyBwb8SNZXQgb2J5dmF0ZcS+b3YgdiBzZXB0ZW1icmkgMjAyNC4KCsOaZGFqZSBtw6FtZSB6IGRhdGFiw6F6eSBwb8SNdHUgb2J5dmF0ZcS+b3Ygc2xvdmVuc2vDvWNoIG9iY8OtIHBvZMS+YSBtZXNpYWNvdiAocm9rIDIwMjQpLiBQbyBuYcSNw610YW7DrSBpY2ggdWxvxb7DrW1lIGRvIGRhdGEuZnJhbWUgKip1ZGFqZSoqIGEgesOhcm92ZcWIIHByZW1lbnVqZW1lIHN0xLpwY2UgbmEga3JhdMWhaWUgbsOhenZ5LgoKYGBge3J9CnVkYWplIDwtIHJlYWQuY3N2KCJEYXRhYmF6YS5jc3YiLAogICAgICAgICAgICAgICAgICBzZXAgPSAiOyIsCiAgICAgICAgICAgICAgICAgIGRlYyA9ICIuIiwKICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKbmFtZXModWRhamUpCgojIHZ5YmVyaWVtZSBzaSBsZW4gc3TEunBjZSwga3RvcsOpIHBvdHJlYnVqZW1lCnVkYWplIDwtIHVkYWplWywgYygiT2tyZXMiLCAiT2JlYyIsICJYMjAyNE0xMiIsICJYMjAyNE0xMSIsICJYMjAyNE0xMCIsICJYMjAyNE0wOSIpXQoKIyBwcmVtZW51amVtZSBzdMS6cGNlIG5hIGplZG5vZHVjaMWhaWUgbsOhenZ5CmNvbG5hbWVzKHVkYWplKSA8LSBjKCJPa3JlcyIsICJPYmVjIiwgIlBvcDEyIiwgIlBvcDExIiwgIlBvcDEwIiwgIlBvcDA5IikKCmhlYWQodWRhamUpCmBgYAoKViBuYcWhZWogZGF0YWLDoXplIG5lbcOhbWUgY2jDvWJhasO6Y2UgaG9kbm90eSwgdGFrxb5lIG5pZSBqZSBwb3RyZWJuw6Egxb5pYWRuYSBpbXB1dMOhY2lhLgoKLS0tCgojIDQuIE9kaGFkIHrDoWtsYWRuw6lobyByZWdyZXNuw6lobyBtb2RlbHUKCmBgYHtyfQptb2RlbCA8LSBsbShQb3AxMiB+IFBvcDExICsgUG9wMTAgKyBQb3AwOSwKICAgICAgICAgICAgZGF0YSA9IHVkYWplKQpzdW1tYXJ5KG1vZGVsKQpgYGAKClZvIHbDvXNsZWRrb2NoIHp2ecSNYWpuZSB2aWTDrW1lIHZlxL5taSB2eXNva8O9IGtvZWZpY2llbnQgZGV0ZXJtaW7DoWNpZSBhIGtvZWZpY2llbnR5IHByaSBQb3AxMSwgUG9wMTAgYSBQb3AwOSBzw7ogc2kgdmXEvm1pIHBvZG9ibsOpLiBUbyBqZSBpbnR1aXTDrXZuZSwgcHJldG/FvmUgcG/EjXR5IG9ieXZhdGXEvm92IHYgamVkbm90bGl2w71jaCBtZXNpYWNvY2ggc2EgbWVuaWEgbGVuIHZlxL5taSBtw6FsbyBhIHPDuiB0YWttZXIgcGVyZmVrdG7DqSBsaW5lw6FybmUga29tYmluw6FjaWUuCgotLS0KCiMgNS4gS29yZWxhxI1uw6EgbWF0aWNhCgpLb3JlbMOhY2lhIGRva8Ohxb5lIHphY2h5dGnFpSBww6Fyb3bDqSB2esWlYWh5IG1lZHppIHByZW1lbm7DvW1pLiBBayBtZWR6aSBuaWVrdG9yw71taSB2eXN2ZXTEvnVqw7pjaW1pIHByZW1lbm7DvW1pIGplIHZ5c29rw6Ega29yZWzDoWNpYSAoc2lnbmFsaXp1asO6Y2EgbXVsdGlrb2xpbmVhcml0dSksIHBvdG9tIGplIG5hamplZG5vZHVjaMWhaWUganUgem8gem96bmFtdSByZWdyZXNvcm92IHZ5bMO6xI1pxaUuIEtvcmVsw6FjaWUgc2EgZGFqw7ogYWogdGVzdG92YcWlLCBhbGVibyBsZW4gdnnEjcOtc2xpxaUgYSBwb3RvbSBwb2TEvmEgaW50dWl0w612bmVobyBwcmF2aWRsYSB2eWzDusSNacWlIGplZG51IHByZW1lbm7Duiwga3RvcsOhIG3DoSBrb3JlbMOhY2l1IHMgaW5vdSBwcmVtZW5ub3UgdiBhYnNvbMO6dG5laiBob2Rub3RlIHZ5xaHFoWl1IGFrbyAwLjgsIHJlc3AuIDAuOS4KCmBgYHtyfQp4dmFycyA8LSB1ZGFqZVssIGMoIlBvcDExIiwgIlBvcDEwIiwgIlBvcDA5IildCnJvdW5kKGNvcih4dmFycyksIDMpCmBgYAoKViBuYcWhb20gcHLDrXBhZGUgdXZpZMOtbWUsIMW+ZSBrb3JlbMOhY2llIG1lZHppIG1lc2HEjW7DvW1pIHBvxI10YW1pIG9ieXZhdGXEvm92IHPDuiBwcmFrdGlja3kgcm92bsOpIDEuIFRvIGplIGV4dHLDqW1ueSBwcsOta2xhZCBtdWx0aWtvbGluZWFyaXR5IOKAkyB2xaFldGt5IHZ5c3ZldMS+dWrDumNlIHByZW1lbm7DqSBvYnNhaHVqw7ogdGFrbWVyIHTDuiBpc3TDuiBpbmZvcm3DoWNpdS4KCi0tLQoKS29yZWxhxI1uw70gdnrFpWFoIHNhIGTDoSB2eXR1xaFpxaUgYWogeiBqZWRub2R1Y2jDvWNoIHDDoXJvdsO9Y2ggc2NhdHRlcnBsb3RvdiBha28gamUgdG8gbmEgbmFzbGVkdWrDumNvbSBvYnLDoXprdSDigJMgYm9keSBsZcW+aWEgdGFrbWVyIG5hIHByaWFta2UuCgpgYGB7cn0KcGFpcnMoeHZhcnMsCiAgICAgIG1haW4gPSAiU2NhdHRlcnBsb3RvdsOhIG1hdGljYSDigJMgcHJlbWVubsOpIFBvcDExLCBQb3AxMCwgUG9wMDkiKQpgYGAKCi0tLQoKIyA2LiBWSUYKCkluZGlrw6F0b3JvbSBtdWx0aWtvbGluZWFyaXR5IHUgcHJlbWVubmVqLCBrdG9yw6EgbXVsdGlrb2xpbmVhcml0dSB6YXByw63EjWnFiHVqZSwgamUgVmFyaWFuY2UgSW5mbGF0aW9uIEZhY3RvciAoVklGKS4gUHJlIHByZW1lbm7DuiAkeF9qJCBqZSBwb3RvbQoKXFsKVklGX2ogPSBcZnJhY3sxfXsxIC0gUl9qXjJ9ClxdCgprZGUgJFJfal4yJCBwb2Now6FkemEgeiByZWdyZXNpZToKClxbClhfaiA9IFxnYW1tYV8wICsgXGdhbW1hXzEgWF8xICsgXGNkb3RzICsgXGdhbW1hX3tqLTF9IFhfe2otMX0gCiAgICAgICsgXGdhbW1hX3tqKzF9IFhfe2orMX0gKyB1LgpcXQoKYGBge3J9CmxpYnJhcnkoY2FyKQp2aWYobW9kZWwpCmBgYAoKSW50dWl0w612bnltIGtyaXTDqXJpb20sIGt0b3LDqSBzaWduYWxpenVqZSBwcsOtdG9tbm9zxaUgbXVsdGlrb2xpbmVhcml0eSwgamUgcG9kbWllbmthICBWSUYgPiA1IChwcsOtc25lIGtyaXTDqXJpdW0pLCBhbGVibyBWSUYgPiAxMCAobWVuZWogcHLDrXNuZSBrcml0w6lyaXVtKS4gViBuYcWhaWNoIGTDoXRhY2ggc8O6IFZJRi15IHR5cGlja3kgdmXEvm1pIHZ5c29rw6ksIMSNbyB6b2Rwb3ZlZMOhIHRha21lciBkb2tvbmFsZWoga29yZWzDoWNpaSBtZWR6aSBtZXNhxI1uw71taSBob2Rub3RhbWkgcG/EjXR1IG9ieXZhdGXEvm92LgoKLS0tCgojIDcuIENvbmRpdGlvbiBOdW1iZXIKClByaSBleGlzdGVuY2lpIG11bHRpa29saW5lYXJpdHkgc2EgbW9kZWwgcHJlamF2dWplIHRhaywgxb5lIGtvZWZpY2llbnQgZGV0ZXJtaW7DoWNpZSBqZSBzw61jZSB2eXNva8O9IGEgemTDoSBzYSwgxb5lIG1vZGVsIGplIHZlxL5taSBkb2Jyw70sIGFsZSByZWdyZXNuw6kga29lZmljaWVudHkgbmVtdXNpYSBiecWlIHN0YWJpbG7DqSBhIGljaCDFoXRhbmRhcmRuw6kgb2RjaMO9bGt5IG3DtMW+dSBiecWlIG5hZsO6a251dMOpLiBVdmVkb23DrW1lIHNpIHRvLCBhayBzYSBwb3pyaWVtZSwgYWtvIHNhIHBvxI3DrXRhasO6IOKAkyB0LiBqLiAKClxbClx0ZXh0e3N0ZH0oXGJldGFfaSkgPSBcc3FydHtcc2lnbWFeMiAoXG1hdGhiZiBYXlQgXG1hdGhiZiBYKV57LTF9X3tpaX19LApcXQoKa2RlIHJvemhvZHVqw7pjaSBqZSAkaSQtdHkgcHJ2b2sgaGxhdm5laiBkaWFnb27DoWx5IG1hdGljZSAkKFxtYXRoYmYgWF5UIFxtYXRoYmYgWCleey0xfSQuIFRpZSBwcnZreSBzw7ogdiBwcsOtcGFkZSBwb2RvYm5vc3RpIHZ5c3ZldMS+dWrDumNpY2ggcHJlbWVubsO9Y2ggbWltb3JpYWRuZSB2ZcS+a8OpLiBUw7p0byBzaXR1w6FjaXUgemFjaHl0w6F2YSBuYXNsZWRvdm7DvSB1a2F6b3ZhdGXEvi4KClByaSB2w71wb8SNdGUgKipDb25kaXRpb24gbnVtYmVyKiogJFxrYXBwYSQgc2EgcG91xb7DrXZhIHZ6b3JlYyAKClxbClxrYXBwYSA9IFxmcmFje1x0aGV0YV97XHRleHR7bWF4fX19e1x0aGV0YV97XHRleHR7bWlufX19ClxdCgprZGUgJFx0aGV0YV8uJCBzw7ogdmxhc3Ruw6kgxI3DrXNsYSBtYXRpY2UuIENvbmRpdGlvbiBudW1iZXIgbmllIGplIHRlc3QsIGplIHRvIGxlbiBpbmRpa8OhdG9yLCBrdG9yw70gcG9zdWR6dWplIG1pZXJ1IG11bHRpa29saW5lYXJpdHkgbWVkemkgcHJlbWVubsO9bWkuIFBvdcW+w612YW1lIGludHVpdMOtdm5lIHByYXZpZGxvLiBBayBDb25kaXRpb24gbnVtYmVyIGplIAoKLSA8IDEwIOKGkiBuw616a2EgbXVsdGlrb2xpbmVhcml0YSwKLSAxMOKAkzMwIOKGkiBtaWVybmEsCi0gMzDigJMxMDAg4oaSIHNpbG7DoSwKLSA+IDEwMCDihpIgdmXEvm1pIHbDocW+bmEuCgpWIG5hxaFvbSBwcsOtcGFkZSB0byB2eXBvxI3DrXRhbWUgbmFzbGVkb3ZuZToKCmBgYHtyfQpYIDwtIG1vZGVsLm1hdHJpeChtb2RlbClbLCAtMV0KWHRYIDwtIHQoWCkgJSolIFgKZWlnIDwtIGVpZ2VuKFh0WCkKCmNvbmRpdGlvbl9udW1iZXIgPC0gc3FydChtYXgoZWlnJHZhbHVlcykgLyBtaW4oZWlnJHZhbHVlcykpCmNvbmRpdGlvbl9udW1iZXIKYGBgCgpLZcSPxb5lIHUgbsOhcyB0ZW50byBpbmRpa8OhdG9yIHR5cGlja3kgdsO9cmF6bmUgcHJlc2FodWplIGhvZG5vdHUgMTAwLCBzaWduYWxpenVqZSBwcsOtdG9tbm9zxaUgesOhdmHFvm5laiBtdWx0aWtvbGluZWFyaXR5IG1lZHppIG1lc2HEjW7DvW1pIMO6ZGFqbWkuCgo+IFZsYXN0bsOpIMSNw61zbG8gxaF0dm9yY292ZWogbWF0aWNlICRcbWF0aGJmIFheVCBcbWF0aGJmIFgkIGplIMSNw61zbG8gJFx0aGV0YV9qJCwgcHJlIGt0b3LDqSBwbGF0w60gJChcbWF0aGJmIFheVCBcbWF0aGJmIFgpXG1hdGhiZiBoXmogPSBcdGhldGFfalxtYXRoYmYgaF5qJC4gJFxtYXRoYmYgaF5qJCBqZSB0enYuIHZsYXN0bsO9IHZla3RvciB0ZWp0byBtYXRpY2UuIE3DoW1lIHRvxL5rbyB2bGFzdG7DvWNoIMSNw61zZWwgKHRlZGEgYWogdmxhc3Ruw71jaCB2ZWt0b3JvdiksIGtvxL5rbyBvYnNhaHVqZSBtYXRpY2EgJFxtYXRoYmYgWF5UIFxtYXRoYmYgWCQgcmlhZGtvdiAoc3TEunBjb3YpLgoKPiBNw7TFvmUgc2Egc3RhxaUsIMW+ZSBWSUYgZmFrdG9yIG5lc2lnbmFsaXp1amUgbXVsdGlrb2xpbmVhcml0dSB1IMW+aWFkbmVqIOKAnmplZG5lauKAnCB2eXN2ZXTEvnVqw7pjZWogdmVsacSNaW55LCBhbGUgc8O6IG5hdnrDoWpvbSBwcmVwb2plbsOpIGN5a2xpY2vDvW1pIGxpbmXDoXJueW1pIHrDoXZpc2xvc8WlYW1pIHbFoWV0a3kgcHJlbWVubsOpLiBUbyB6YWNoeXTDoXZhIHByw6F2ZSBDb25kaXRpb24gTnVtYmVyLgoKLS0tCgojIDguIFJpZcWhZW5pYSBtdWx0aWtvbGluZWFyaXR5CgojIyBWeW5lY2hhbmllIHByZW1lbm5lagoKUG9rw7pzbWUgc2EgdnluZWNoYcWlIHBvc3R1cG5lIGR2ZSBwcmVtZW5uw6ksIGt0b3LDqSBtYWrDuiBuYWp2ecWhxaHDrSBWSUYgKHUgbsOhcyBzw7ogVklGLXkgdnlzb2vDqSBwcmUgdsWhZXRreSksIGEgcG9yb3ZuYWptZSBuw6FzbGVkbmUgdXByYXZlbsOpIGtvZWZpY2llbnR5IGRldGVybWluw6FjaWUgb2JvY2ggbm92w71jaCBtb2RlbG92OgoKYGBge3J9Cm1vZGVsX25vUG9wMTAgPC0gbG0oUG9wMTIgfiBQb3AxMSArIFBvcDA5LCBkYXRhID0gdWRhamUpCnN1bW1hcnkobW9kZWxfbm9Qb3AxMCkKCm1vZGVsX25vUG9wMTEgPC0gbG0oUG9wMTIgfiBQb3AxMCArIFBvcDA5LCBkYXRhID0gdWRhamUpCnN1bW1hcnkobW9kZWxfbm9Qb3AxMSkKYGBgCgpWIG5hxaFlaiBkYXRhYsOhemUgenZ5xI1ham5lIHV2aWTDrW1lLCDFvmUgdnluZWNoYW5pZSBqZWRuZWogeiB2ZcS+bWkgcG9kb2Juw71jaCBtZXNhxI1uw71jaCBwcmVtZW5uw71jaCB6bsOtxb5pIHVwcmF2ZW7DvSBrb2VmaWNpZW50IGRldGVybWluw6FjaWUgbGVuIG1pbmltw6FsbmUuIFRvIGplIHR5cGlja8OpIHNwcsOhdmFuaWUgcHJpIHNpbG5laiBtdWx0aWtvbGluZWFyaXRlIOKAkyBqZWRuYSBwcmVtZW5uw6EgZG9rw6HFvmUgdGFrbWVyIGRva29uYWxlIOKAnnphc3TDunBpxaXigJwgaW7Dui4KCiMjIMWga8OhbG92YW5pZSBwcmVtZW5uw71jaAoKxaBrw6Fsb3ZhbmllIG3DtMW+ZSBiecWlIHZlxL5taSBlZmVrdMOtdm5lIHogaMS+YWRpc2thIG51bWVyaWNrZWogc3RhYmlsaXR5IChuYWptw6QgcHJpIHZlxL5taSByb3pkaWVsbnljaCByw6Fkb2NoIHByZW1lbm7DvWNoKSwgem5pxb51amUgdsWhYWsgaW50ZXJwcmV0b3ZhdGXEvm5vc8WlIG1vZGVsdS4gSWRlIG8gw7pwcmF2dSBwcmVtZW5uw71jaCBwb2TEvmEgbmFzbGVkb3Zuw6lobyB2em9yY2E6CgpcWwp4XntzY2FsZX0gPSBcZnJhY3t4LU19e1xzcXJ0e0R9fQpcXQoKa2RlICRNJCBqZSBzdHJlZG7DoSBob2Rub3RhIChwcmllbWVyKSBhICREJCBqZSByb3pwdHlsIHByZW1lbm5lai4KClYgbmHFoWVqIGRhdGFiw6F6ZSBzw7ogdsWhZXRreSBwcmVtZW5uw6kgdiBwb8SNdGUgb2J5dmF0ZcS+b3YsIHRlZGEgdcW+IHPDuiB2IHBvcm92bmF0ZcS+bsO9Y2ggamVkbm90a8OhY2guIE5hcHJpZWsgdG9tdSBzaSDFoWvDoWxvdmFuaWUgdWvDocW+ZW1lLCBhYnkgc21lIHZpZGVsaSB2cGx5diBuYSBtdWx0aWtvbGluZWFyaXR1LgoKYGBge3J9CnVkYWplJFBvcDExX2MgPC0gc2NhbGUodWRhamUkUG9wMTEsIGNlbnRlciA9IFRSVUUsIHNjYWxlID0gVFJVRSkKdWRhamUkUG9wMTBfYyA8LSBzY2FsZSh1ZGFqZSRQb3AxMCwgY2VudGVyID0gVFJVRSwgc2NhbGUgPSBUUlVFKQp1ZGFqZSRQb3AwOV9jIDwtIHNjYWxlKHVkYWplJFBvcDA5LCBjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IFRSVUUpCgptb2RlbF9jZW50ZXJlZCA8LSBsbShQb3AxMiB+IFBvcDExX2MgKyBQb3AxMF9jICsgUG9wMDlfYywKICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHVkYWplKQpzdW1tYXJ5KG1vZGVsX2NlbnRlcmVkKQp2aWYobW9kZWxfY2VudGVyZWQpCmBgYAoKQ29uZGl0aW9uIE51bWJlciBqZToKCmBgYHtyfQpYIDwtIG1vZGVsLm1hdHJpeChtb2RlbF9jZW50ZXJlZClbLCAtMV0KWHRYIDwtIHQoWCkgJSolIFgKZWlnIDwtIGVpZ2VuKFh0WCkKCmNvbmRpdGlvbl9udW1iZXIgPC0gc3FydChtYXgoZWlnJHZhbHVlcykgLyBtaW4oZWlnJHZhbHVlcykpCmNvbmRpdGlvbl9udW1iZXIKYGBgCgpaIHbDvXNsZWRrb3Ygdmlkw61tZSwgxb5lIMWha8OhbG92YW5pZSBtw7TFvmUgemxlcMWhacWlIG51bWVyaWNrw6kgdmxhc3Rub3N0aSBtYXRpY2UgKENvbmRpdGlvbiBOdW1iZXIpLCBhbGUgbXVsdGlrb2xpbmVhcml0dSBtZWR6aSBtZXNhxI1uw71taSBob2Rub3RhbWkgYWtvIHRha8O6IG5lb2RzdHJhxYh1amUg4oCTIGTDoXRhIHN0w6FsZSBvYnNhaHVqw7ogdGFrbWVyIHTDuiBpc3TDuiBpbmZvcm3DoWNpdS4KCiMjIEluw6Egw7pwcmF2YSBwcmVtZW5uZWosIGt0b3LDoSB6YWNob3bDoSBpbnRlcnByZXRvdmF0ZcS+bm9zxaUKCkFrIHNhIGNoY2VtZSB2eWhuw7rFpSBzdHJhdGUgaW50ZXJwcmV0b3ZhdGXEvm5vc3RpLCBtw7TFvmVtZSBuaWVrZWR5IHptZW5pxaUgamVkbm90a3kgcHJlbWVubmVqLCBrdG9yw6EgamUgdiDDunBsbmUgaW7DvWNoIHLDoWRvY2ggbmXFviBvc3RhdG7DqS4gViBww7R2b2Rub20gcHLDrWtsYWRlIHRvIGJvbGEgcHJlbWVubsOhIEdEUCwga3RvcsOhIGJvbGEgdiBkb2zDoXJvY2guIFYgbmHFoWVqIGRhdGFiw6F6ZSBzw7ogdsWhZXRreSBwcmVtZW5uw6kgdiBwb8SNdGUgb2J5dmF0ZcS+b3YsIHRha8W+ZSB0ZW50byBwcm9ibMOpbSBuZW3DoW1lLiBQcmUgaWx1c3Ryw6FjaXUgc2kgdsWhYWsgdWvDocW+ZW1lIHByZXZvZCBqZWRuZWogcHJlbWVubmVqIG5hIOKAnnRpc8OtY2Ugb2J5dmF0ZcS+b3bigJw6CgpgYGB7cn0KdWRhamUkUG9wMTFrIDwtIHVkYWplJFBvcDExIC8gMTAwMAoKaGVhZCh1ZGFqZVssIGMoIk9iZWMiLCAiUG9wMTIiLCAiUG9wMTEiLCAiUG9wMTFrIildKQpgYGAKClBvdG9tIGxpbmXDoXJueSBtb2RlbCBkb3NpYWhuZSB2w71zbGVka3k6CgpgYGB7cn0KbW9kZWxfUG9wMTFrIDwtIGxtKFBvcDEyIH4gUG9wMTFrICsgUG9wMTAgKyBQb3AwOSwKICAgICAgICAgICAgICAgICAgIGRhdGEgPSB1ZGFqZSkKc3VtbWFyeShtb2RlbF9Qb3AxMWspCnZpZihtb2RlbF9Qb3AxMWspCmBgYAoKViB0b210byBwcsOtcGFkZSBzYSBrb2VmaWNpZW50IHByaSBQb3AxMWsgem1lbsOtIGxlbiB2IGplZG5vdGvDoWNoIChuYSB0aXPDrWNlIG9ieXZhdGXEvm92KSwgYWxlIHNhbW90bsOhIG11bHRpa29saW5lYXJpdGEgbmV6bWl6bmUsIHByZXRvxb5lIFBvcDExLCBQb3AxMCBhIFBvcDA5IHPDuiBzdMOhbGUgdmXEvm1pIHBvZG9ibsOpLgoKPiBQb3puw6Fta2E6IER1bW15IHByZW1lbm7DqSAoMC8xKSBuZcWha8OhbHVqZW1lLgoKYGBge3J9CiMjIyAgIG/EjWlzdGVuaWUgZGF0YWLDoXp5IG9kIOKAnnByYWNvdm7DvWNo4oCcIHN0xLpwY292CmxpYnJhcnkoZHBseXIpCgp1ZGFqZSA8LSB1ZGFqZSAlPiUKICBkcGx5cjo6c2VsZWN0KE9rcmVzLCBPYmVjLCBQb3AxMiwgUG9wMTEsIFBvcDEwLCBQb3AwOSkKYGBgCgojIyBQQ0EgKHZvbGl0ZcS+bsOpKQoKUENBIG7DoW0gdnl0dsOhcmEgbm92w70gcHJhY292bsO9IHZla3RvciAoa29tcG9uZW50KSwga3RvcsO9IGplIGRlZmlub3ZhbsO9IGFrbyB2w6HFvmVuw70gc8O6xI1ldCBkdm9jaCBhbGVibyB2aWFjZXLDvWNoIGtvcmVsb3ZhbsO9Y2ggcHJlbWVubsO9Y2guIFRlbnRvIGtvbXBvbmVudCBwb3RvbSB2eXN0dXB1amUgYWtvIHZ5c3ZldMS+dWrDumNhIHZlbGnEjWluYSwgemF0aWHEviDEjW8gcMO0dm9kbsOpIHByZW1lbm7DqSB2eWzDusSNaW1lIHpvIHpvem5hbXUgcmVncmVzb3Jvdi4gVMO9bSBzYSByZWR1a3VqZSBwb8SNZXQgdnlzdmV0xL51asO6Y2ljaCB2ZWxpxI3DrW4gYSBtw7TFvmUgc2Egem1pZXJuacWlIHByb2Jsw6ltIG11bHRpa29saW5lYXJpdHkuCgpWIG5hxaFvbSBwcsOtcGFkZSB2eXR2b3LDrW1lIHBydsO6IGhsYXZuw7oga29tcG9uZW50dSB6IHByZW1lbm7DvWNoIFBvcDExLCBQb3AxMCBhIFBvcDA5OgoKYGBge3J9ClhfcGNhIDwtIHNjYWxlKHVkYWplWywgYygiUG9wMTEiLCAiUG9wMTAiLCAiUG9wMDkiKV0sCiAgICAgICAgICAgICAgIGNlbnRlciA9IFRSVUUsIHNjYWxlID0gVFJVRSkKcGNhX3JlcyA8LSBwcmNvbXAoWF9wY2EpCgpzdW1tYXJ5KHBjYV9yZXMpCgp1ZGFqZSRQQzEgPC0gcGNhX3JlcyR4WywgMV0KCm1vZGVsX3BjYSA8LSBsbShQb3AxMiB+IFBDMSwgZGF0YSA9IHVkYWplKQpzdW1tYXJ5KG1vZGVsX3BjYSkKIyBWSUYgc2EgbmVkw6EgcG/EjcOtdGHFpSwgbGVibyBtb2RlbCBtw6EgbGVuIGplZGVuIHZ5c3ZldMS+dWrDumNpIHJlZ3Jlc29yCiMgdmlmKG1vZGVsX3BjYSkKYGBgCgotLS0KCiMjIEhyZWJlxYhvdsOhIHJlZ3Jlc2lhIChSaWRnZSBSZWdyZXNzaW9uIOKAkyB2b2xpdGXEvm7DqSkKCkhyZWJlxYhvdsOhIHJlZ3Jlc2lhIGplIG1vZGlmaWvDoWNpYSByZWdyZXNpZSwga3RvcsOhIHphdsOhZHphIHBlcnR1cmLDoWNpZSBkbyBtYXRpY2UgJFxtYXRoYmYgWF5UIFxtYXRoYmYgWCQgdGFrLCBhYnkgem7DrcW+aWxhIGTDtHNsZWRreSBtdWx0aWtvbGluZWFyaXR5LiBUcmViYSBhbGUgdXBvem9ybmnFpSwgxb5lIG9kaGFkb3ZhbsOpIHJlZ3Jlc27DqSBrb2VmaWNpZW50eSBzw7ogc2tyZXNsZW7DqS4gUGVydHVyYsOhY2lhIHZ5emVyw6EgbmFzbGVkb3ZuZToKClxbCihcbWF0aGJmIFheVCBcbWF0aGJmIFggKyBcbGFtYmRhIFxtYXRoYmYgSSkKXF0KClYgdG9tdG8ga3Jva3Ugc2kgdnlww63FoWVtZSB2w71zbGVka3kgb2RoYWRvdiByZWdyZXNuw71jaCBrb2VmaWNpZW50b3YgcyBtZW5pYWNpbWkgc2EgcGFyYW1ldHJhbWkgJFxsYW1iZGEkLCBhYnkgc21lIHrDrXNrYWxpIHVyxI1pdMO6IHByZWRzdGF2dSBvIMSNw61zZWxub20gcsOhZGUgcHJ2a292OgoKYGBge3J9CmxpYnJhcnkoTUFTUykKClggPC0gYXMubWF0cml4KHVkYWplWywgYygiUG9wMTEiLCAiUG9wMTAiLCAiUG9wMDkiKV0pCnkgPC0gdWRhamUkUG9wMTIKCnJpZGdlX2ZpdCA8LSBsbS5yaWRnZSh5IH4gWCwgbGFtYmRhID0gc2VxKDAsIDEwLCAxKSkKcmlkZ2VfZml0CmBgYAoKTmFzdGF2b3ZhbmllIHLDtHpueWNoIGhvZG7DtHQgJFxsYW1iZGEkIGplIHByZWRtZXRvbSBobGLFoWVqIGFuYWzDvXp5LCB0ZW50byBwcmVoxL5hZCBqZSBsZW4gamVqIMSNYXPFpW91LgoKLS0tCgojIDEwLiBaaHJudXRpZQoKLSBNdWx0aWtvbGluZWFyaXRhIG5lemF2w6FkemEgYmlhcywgYWxlIHp2ecWhdWplIMWhdGFuZGFyZG7DqSBvZGNow71sa3kgb2RoYWRvdmFuw71jaCByZWdyZXNuw71jaCBrb2VmaWNpZW50b3YgYSByb2LDrSBpY2ggbmVzdGFiaWxuw71taS4gIAotIFRlc3R5IChyZXNwLiBkaWFnbm9zdGlja8OpIHVrYXpvdmF0ZWxlKSBha28gVklGIGEgQ29uZGl0aW9uIE51bWJlciB1bW/FvsWIdWrDuiBkaWFnbm9zdGlrdSBtdWx0aWtvbGluZWFyaXR5LiAgCi0gUmllxaFlbmlhIHphaMWVxYhhasO6OiB2eW5lY2hhbmllIHByZW1lbm5laiwgY2VudHJvdmFuaWUvxaFrw6Fsb3ZhbmllLCB6bWVudSBqZWRub3RpZWssIHByw61wYWRuZSBwb3XFvml0aWUgUENBIGFsZWJvIGhyZWJlxYhvdmVqIHJlZ3Jlc2llLiBQQ0EgYSBocmViZcWIb3bDoSByZWdyZXNpYSB2ecW+YWR1asO6IGhsYsWhaWUgdmVkb21vc3RpLCBwcmV0byBpY2ggdHUgdXbDoWR6YW1lIGxlbiBmb3Jtw6FsbmUgYSBuZXZ5xb5hZHVqZW1lIGljaCBkZXRhaWxuw70gdsO9cG/EjWV0LgoKLS0tCg==