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:

  1. Nespôsobuje skreslené (biased) odhady koeficientov.
  2. 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.
  3. Odhadované regresné koeficienty sú nestabilné – pri malej zmene údajov sa prudko menia koeficienty ako aj ich znamienka.
  4. 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. \]

library(car)
vif(model)
  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
vif(model_centered)
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
vif(model_Pop11k)
 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==