Úvod

Klastrová (zhluková) analýza patrí medzi najpoužívanejšie metódy exploratívnej štatistiky. V praxi sa využíva všade tam, kde je potrebné rozdeliť pozorovania do homogénnych celkov – napríklad pri segmentácii zákazníkov v marketingu, identifikácii podobných regiónov z hľadiska socioekonomických ukazovateľov, hodnotení zdravotných rizík, klasifikácii biologických vzoriek či v geoinformatike pri zoskupovaní priestorových objektov. Jej výhodou je, že pracuje s viacerými premennými naraz a dokáže odhaliť vzory, ktoré by pri samostatnom hodnotení jednotlivých ukazovateľov zostali skryté. Správne zvolená metrika vzdialenosti a metóda zhlukovania umožňujú odhaliť skryté vzťahy v dátach, čím poskytujú cenný podklad pre rozhodovanie v rôznych oblastiach aplikovaného výskumu.

V tejto práci predstavíme zhlukovú analýzu pri analýze údajov o počte obyvateľov okresov Slovenskej republiky z hľadiska ich mesačného vývoja v roku 2024. Pracujeme s databázou, ktorá obsahuje okres, obec a počet obyvateľov v jednotlivých mesiacoch roku 2024 (od januára po december). Údaje najskôr agregujeme na úroveň okresov a následne náhodne vyberieme 10 okresov, na ktorých demonštrujeme celý postup hierarchického zhlukovania. V Tab. 1 uvádzame celú nami používanú databázu 10 okresov.

library(knitr)
library(kableExtra)
library(dplyr)
rm(list = ls())

# Načítanie údajov z databázy
# Predpoklad: súbor "Databaza.csv" je v rovnakom priečinku ako tento Rmd súbor.
# Používame separátor ";" keďže ide o CSV v európskom formáte.
udaje_raw <- read.csv("Databaza.csv",
                      sep = ";",
                      stringsAsFactors = FALSE)

# R automaticky dopĺňa X pred názvy stĺpcov začínajúcich číslom,
# preto ho odstránime, aby sme mali späť názvy 2024M01, 2024M02, ...
names(udaje_raw) <- sub("^X", "", names(udaje_raw))

# Premenovanie mesiacov na bežné názvy
mesiace_map <- c(
  "2024M01" = "Január",
  "2024M02" = "Február",
  "2024M03" = "Marec",
  "2024M04" = "Apríl",
  "2024M05" = "Máj",
  "2024M06" = "Jún",
  "2024M07" = "Júl",
  "2024M08" = "August",
  "2024M09" = "September",
  "2024M10" = "Október",
  "2024M11" = "November",
  "2024M12" = "December"
)

# premenujeme len stĺpce s mesiacmi
names(udaje_raw)[match(names(mesiace_map), names(udaje_raw))] <- mesiace_map
month_names <- unname(mesiace_map)  # v poradí január–december

# Agregácia na úroveň okresov (súčet obyvateľov za všetky obce)
udaje_okresy <- udaje_raw %>%
  group_by(Okres) %>%
  summarise(across(all_of(month_names), sum, na.rm = TRUE))

# Náhodný výber 10 okresov
set.seed(123)  # pre reprodukovateľnosť
okresy_vyber <- sample(unique(udaje_okresy$Okres), 10)

udaje_vyber <- subset(udaje_okresy, Okres %in% okresy_vyber)

# Usporiadanie podľa názvu okresu (nie je nutné, len pre prehľadnosť)
udaje_vyber <- udaje_vyber[order(udaje_vyber$Okres), ]

Tab. 1. Vybrané okresy a ich počet obyvateľov v jednotlivých mesiacoch roku 2024

kable(udaje_vyber, caption = "Vybrané okresy a ich počet obyvateľov (Január–December 2024)") %>%
  kable_styling(full_width = FALSE)
Vybrané okresy a ich počet obyvateľov (Január–December 2024)
Okres Január Február Marec Apríl Máj Jún Júl August September Október November December
Okres Bratislava I 47375 47391 47408 47411 47484 47503 47488 47525 47564 47565 47632 47634
Okres Bratislava II 125980 125975 126022 126047 126178 126245 126393 126435 126457 126481 126522 126632
Okres Bratislava III 77594 77678 77718 77736 77746 77817 77872 77912 77936 77918 77998 78070
Okres Bratislava IV 105043 105011 104992 105029 105039 105064 105066 105076 105140 105120 105165 105118
Okres Bratislava V 122048 122009 121976 121993 121936 121934 121918 121871 121869 121864 121836 121885
Okres Dunajská Streda 127025 127173 127217 127264 127307 127397 127460 127512 127576 127692 127772 127803
Okres Hlohovec 27236 27182 27133 27125 27084 27056 27027 27004 26961 26921 26904 26876
Okres Malacky 79689 79717 79761 79782 79849 79899 79923 79930 79917 79935 79961 79994
Okres Pezinok 69953 69936 69926 69945 69972 70006 70021 70070 70055 70067 70099 70105
Okres Senec 105075 105281 105448 105640 105848 106062 106188 106382 106488 106649 106781 106872

Hierarchická zhluková analýza pracuje s mierami vzdialenosti medzi pozorovaniami. Aby boli tieto vzdialenosti porovnateľné, je potrebné, aby všetky premenné boli definované na rovnakej škále. V našom prípade sú premennými mesačné počty obyvateľov (Január až December). Použijeme tzv. z-škálovanie, pričom transformované \(z\) hodnoty (skóre) vypočítame nasledovne

\[z = \frac{x-\mu}{\sigma}\]

kde \(\mu\) je stredná hodnota a \(\sigma\) je štandardná odchýlka pozorovaní \(x\). Predpokladáme pritom, že súbor údajov už neobsahuje NA hodnoty, ktoré boli ošetrené v predchádzajúcich krokoch.

Touto operáciou získame škálované pozorovania, pričom ich rozloženie je znázornené nasledovne.

# =======================================================
## 1) Príprava údajov a data.frame so škálovanými údajmi
## ======================================================

# Vyberieme len numerické stĺpce s mesačnými údajmi (Január–December)
udaje10 <- udaje_vyber[, month_names]

# Názvy riadkov nastavíme na názvy okresov
rownames(udaje10) <- udaje_vyber$Okres

# Odstránime prípadné chýbajúce hodnoty
udaje_complete <- na.omit(udaje10)

# Škálovanie (z-škóre) jednotlivých premenných
udaje_scaled <- scale(udaje_complete)

Obr. 1. Boxploty škálovaných numerických premenných (mesiace roku 2024)

num_vars  <- as.data.frame(udaje_scaled)
num_plots <- ncol(num_vars)

# 12 mesiacov = 3x4 mriežka
par(mfrow = c(3, 4),
    mar  = c(4, 4, 2, 1),
    oma  = c(0, 0, 3, 0))

for (col in names(num_vars)) {
  boxplot(num_vars[[col]],
          main       = col,
          col        = "lightblue",
          horizontal = TRUE)
}

mtext("Boxploty numerických premenných (mesiace roku 2024)",
      outer = TRUE, cex = 1.3, font = 2)

Tentokrát odľahlé hodnoty nevylučujeme, nakoľko definujú konkrétny okres. Napríklad okres s výrazne vyšším počtom obyvateľov je z pohľadu analýzy zaujímavý a chceme ho v dátach zachovať.


Pri zhlukovej analýze je dôležitá korelačná matica premenných. Vysoká korelácia zvýhodňuje pri zhlukovej analýze korelované premenné. Preto pri korelácii nad 0,8 alebo 0,9 často vylúčime jednu z korelovaných premenných. V Tab. 2 ukážeme korelačnú maticu medzi jednotlivými mesiacmi roku 2024. Ak by sa medzi mesiacmi vyskytovali veľmi vysoké korelácie, bolo by možné uvažovať napríklad o znížení dimenzie pomocou analýzy hlavných komponentov.

V prípade, ak máme väčší počet významne korelovaných premenných, sa odporúča transformácia pomocou Analýzy hlavných komponentov (Principal Component Analysis).

Tab. 2. Korelačná matica medzi mesiacmi roku 2024

cor_mat <- cor(udaje_scaled, use = "pairwise.complete.obs")
cor_mat <- round(cor_mat, 2)
print(cor_mat)
          Január Február Marec Apríl Máj Jún Júl August September Október November December
Január         1       1     1     1   1   1   1      1         1       1        1        1
Február        1       1     1     1   1   1   1      1         1       1        1        1
Marec          1       1     1     1   1   1   1      1         1       1        1        1
Apríl          1       1     1     1   1   1   1      1         1       1        1        1
Máj            1       1     1     1   1   1   1      1         1       1        1        1
Jún            1       1     1     1   1   1   1      1         1       1        1        1
Júl            1       1     1     1   1   1   1      1         1       1        1        1
August         1       1     1     1   1   1   1      1         1       1        1        1
September      1       1     1     1   1   1   1      1         1       1        1        1
Október        1       1     1     1   1   1   1      1         1       1        1        1
November       1       1     1     1   1   1   1      1         1       1        1        1
December       1       1     1     1   1   1   1      1         1       1        1        1

Každému okresu zodpovedá jeden riadok pozorovaní. Vzdialenosť medzi okresmi \(i\) a \(j\) je:

\[ d^{ij} = \sqrt{\sum_k (x^i_k - x^j_k)^2} \]

kde \(x^i_k\) je \(k\)-tá premenná vstupujúca do výpočtu (v našom prípade ide o mesačné počty obyvateľov: Január, Február, …, December) okresu \(i\). Tento typ vzdialenosti nazývame Euklidovská vzdialenosť. Vzdialenosti medzi jednotlivými okresmi sa súhrnne vyjadrujú aj v matici vzdialenosti, čo je v našom prípade uvedené v Tab. 3. Analýzou tejto tabuľky môžeme identifikovať dvojice okresov, ktoré sú si podľa priebehu počtu obyvateľov v roku 2024 veľmi podobné (malá vzdialenosť), resp. veľmi odlišné (veľká vzdialenosť).

Tab. 3. Euklidovská matica vzdialeností medzi okresmi

## ============================
## 3) Distance matrix
## ============================
# ponecháme pôvodné názvy okresov ako rownames
dist_mat <- round(dist(udaje_scaled, method = "euclidean"), 2)
dist_mat
       1     2     3     4     5     6     7     8     9
2   7.94                                                
3   3.06  4.88                                          
4   5.80  2.14  2.75                                    
5   7.50  0.44  4.44  1.70                              
6   8.06  0.12  5.00  2.25  0.56                        
7   2.06 10.00  5.12  7.86  9.56 10.12                  
8   3.26  4.68  0.20  2.54  4.24  4.79  5.32            
9   2.27  5.67  0.79  3.53  5.23  5.79  4.33  0.99      
10  5.90  2.04  2.85  0.11  1.60  2.15  7.96  2.64  3.63

Princíp hierarchického zhlukovania (Wardova metóda)

Zhlukovanie v prípade Wardovej metódy prebieha zdola smerom nahor, t. j. začíname s jednočlennými klastrami, ktoré postupne zlučujeme. Táto metóda patrí medzi aglomeratívne hierarchické metódy. Minimalizuje nárast vnútornej variability pri spojení dvoch klastrov, pričom využíva nasledovné výpočty:

Wardova metóda minimalizuje sumu štvorcov chýb (Error Sum of Squares – ESS)

\[ESS(C) = \sum_{i \in C} \lVert x_i - \bar{x}_C \rVert^2,\]

kde \(C\) je zvažovaný klaster (zhluk). V každom kroku zlučovania dvoch klastrov Wardova metóda hľadá minimálny prírastok sumy štvorcov chýb (\(\Delta ESS\)), pričom

\[\Delta ESS = ESS(A \cup B) - ESS(A) - ESS(B).\]

Dvojica zhlukov, ktorá tejto podmienke o minimalizácii vyhovuje, je následne zlúčená a prechádza sa k ďalšiemu kroku. To spravidla vedie k vytváraniu relatívne homogénnych zhlukov, pričom nedochádza k odtrhávaniu odľahlých hodnôt tak, ako pri niektorých iných zhlukovacích metódach.

Obr. 2. Hierarchické zhlukovanie – dendrogram. Červená čiara určuje rez definujúci tri klastre.

## ============================
## 4) Hierarchical clustering
## ============================

hc <- hclust(dist_mat, method = "ward.D2")

plot(hc, labels = rownames(udaje_scaled),
     main = "Hierarchical clustering of districts (Ward.D2)",
     xlab = "", sub = "")

# počet klastrov
k <- 3
h_cut <- hc$height[length(hc$height) - (k - 1)]
abline(h = h_cut, col = "red", lwd = 2, lty = 2)


klaster_membership <- cutree(hc, k = k)

udaje_klasters <- data.frame(
  Okres = rownames(udaje_complete),
  udaje_complete,
  klaster = factor(klaster_membership)
)

Tab. 4. Príslušnosť okresov do klastrov

data_prac <- data.frame(
  Okres = udaje_klasters$Okres,
  klaster = udaje_klasters$klaster
)
data_prac

Vykonaná klastrová analýza klasifikuje okresy do troch klastrov. Zloženie klastrov odráža jednak úroveň počtu obyvateľov, ale aj podobnosť priebehu počas jednotlivých mesiacov roku 2024. V jednom klastri môžeme nájsť skôr väčšie okresy s podobným počtom obyvateľov, v inom skôr menšie okresy, prípadne okresy s odlišnou dynamikou (mierny nárast alebo pokles). Interpretácia konkrétnych klastrov závisí od výsledných hodnôt a je možné ju doplniť napríklad mapovým zobrazením alebo porovnaním s ďalšími charakteristikami okresov.

Deskriptívne štatistiky výsledkov

Na základe Tab. 5 môžeme posúdiť, ako dobre jednotlivé mesiace (premenné) prispievajú k rozlišovaniu medzi klastrami. Vnútroklastrová variabilita (WSS) vyjadruje, nakoľko sú okresy v rámci jedného klastra podobné, zatiaľ čo medzi-klastrová variabilita (BSS) vyjadruje rozdiely medzi klastrami. Podiel medzi-klastrovej variability na celkovej variabilite (stĺpec Prop_Between) nám ukazuje, do akej miery daný mesiac prispieva k separácii klastrov.

Tab. 5. Vysvetlenie vnútroklastrovej variability z hľadiska jednotlivých premenných (mesiacov)

## ============================
## 5) Variability measures
## ============================

ssq <- function(x, m) sum((x - m)^2)

var_names <- colnames(udaje_scaled)

TSS <- sapply(var_names, function(v) ssq(udaje_scaled[, v], mean(udaje_scaled[, v])))

WSS <- sapply(var_names, function(v) {
  x <- udaje_scaled[, v]
  tapply(x, klaster_membership, function(z) ssq(z, mean(z))) |> sum()
})

BSS <- TSS - WSS

ss_table <- data.frame(
  Variable = var_names,
  TSS = TSS,
  WSS = WSS,
  BSS = BSS,
  Prop_Between = BSS / TSS
)

ss_table

Vyššie hodnoty podielu Prop_Between znamenajú, že daný mesiac výraznejšie odlišuje jednotlivé klastre. Mesiace s nižšou hodnotou tohto podielu predstavujú obdobia, v ktorých sú okresy medzi sebou relatívne podobné a daná premenná nie je takým silným „separátorom“.


Na záver sa pozrieme na centroidy, t. j. priemerné hodnoty sledovaných premenných v jednotlivých klastroch. Tie nám poskytujú prehľadný opis typického okresu v každom klastri.

# Vytvoríme dátový rámec s pôvodnými (neškálovanými) hodnotami a priradeným klastrom
udaje_complete_klaster <- data.frame(
  Okres = rownames(udaje_complete),
  udaje_complete,
  klaster = factor(klaster_membership)
)

Tab. 6. Centroidy – priemerné počty obyvateľov v jednotlivých mesiacoch podľa klastrov

descriptives <- udaje_complete_klaster %>%
  group_by(klaster) %>%
  summarise(
    across(
      .cols = where(is.numeric),
      .fns  = list(mean = ~ mean(.x, na.rm = TRUE)),
      .names = "{.col}_{.fn}"
    )
  )

descriptives

Pri pohľade na centroidy vidíme rozdiely v priemernom počte obyvateľov v jednotlivých klastroch. Jeden klaster môže zahŕňať skôr väčšie okresy s vyššími priemernými hodnotami počas celého roka, iný skôr menšie okresy s nižšími hodnotami. Zaujímavé sú aj prípadné rozdiely medzi mesiacmi – napríklad mierne nárasty alebo poklesy v niektorých obdobiach roka, ktoré môžu súvisieť s migráciou, sezónnymi vplyvmi alebo administratívnymi zmenami (napr. „prihlásenie“ obyvateľov).

Záver

Predložená analýza sa zaoberá počtom obyvateľov vybraných okresov Slovenskej republiky v závislosti od ich mesačného vývoja v roku 2024. Na základe hierarchickej zhlukovej analýzy sme okresy rozdelili do troch klastrov. Klastre sa líšia najmä úrovňou priemerného počtu obyvateľov a podobnosťou priebehu v čase.

Takáto analýza môže slúžiť ako podklad pre:

  • plánovanie verejných služieb (školy, zdravotníctво, doprava),
  • priestorové plánovanie a rozvoj územia,
  • porovnávanie typicky „malých“, „stredných“ a „väčších“ okresov podľa zvolených ukazovateľov.

Zhluková analýza teda umožňuje identifikovať skupiny podobných okresov a následne cieleným spôsobom analyzovať a plánovať verejné politiky či rozvojové projekty podľa ich príslušnosti ku klastrom.

LS0tCnRpdGxlOiAiWmhsdWtvdsOhIGFuYWzDvXphIChDbHVzdGVyIEFuYWx5c2lzKSIKYXV0aG9yOiAiRmlsaXAgSnVya8OhxI1layAoemEgcG9tb2NpIENoYXRHUFQpIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdGhlbWU6IHVuaXRlZAogICAgaGlnaGxpZ2h0OiB0YW5nbwplZGl0b3Jfb3B0aW9uczogCiAgbWFya2Rvd246IAogICAgd3JhcDogNzIKLS0tCgotLS0tLS0tLS0KCmBgYHtyIHNldHVwMSwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVjaG8gICAgPSBUUlVFLCAgICAjIHpvYnJhenVqIGvDs2QKICBtZXNzYWdlID0gVFJVRSwgICAgIyB6b2JyYXp1aiBzcHLDoXZ5IHogYmFsw61rb3YKICB3YXJuaW5nID0gRkFMU0UsICAgIyBwb3Rsw6HEjWFqIHdhcm5pbmdzCiAgZXJyb3IgICA9IEZBTFNFICAgICMgcG90bMOhxI1haiBjaHlieQopCmBgYAoKIyMgw5p2b2QKCktsYXN0cm92w6EgKHpobHVrb3bDoSkgYW5hbMO9emEgcGF0csOtIG1lZHppIG5hanBvdcW+w612YW5lasWhaWUgbWV0w7NkeSBleHBsb3JhdMOtdm5laiDFoXRhdGlzdGlreS4gViBwcmF4aSBzYSB2eXXFvsOtdmEgdsWhYWRlIHRhbSwga2RlIGplIHBvdHJlYm7DqSByb3pkZWxpxaUgcG96b3JvdmFuaWEgZG8gaG9tb2fDqW5ueWNoIGNlbGtvdiDigJMgbmFwcsOta2xhZCBwcmkgc2VnbWVudMOhY2lpIHrDoWthem7DrWtvdiB2IG1hcmtldGluZ3UsIGlkZW50aWZpa8OhY2lpIHBvZG9ibsO9Y2ggcmVnacOzbm92IHogaMS+YWRpc2thIHNvY2lvZWtvbm9taWNrw71jaCB1a2F6b3ZhdGXEvm92LCBob2Rub3RlbsOtIHpkcmF2b3Ruw71jaCByaXrDrWssIGtsYXNpZmlrw6FjaWkgYmlvbG9naWNrw71jaCB2em9yaWVrIMSNaSB2IGdlb2luZm9ybWF0aWtlIHByaSB6b3NrdXBvdmFuw60gcHJpZXN0b3JvdsO9Y2ggb2JqZWt0b3YuIEplaiB2w71ob2RvdSBqZSwgxb5lIHByYWN1amUgcyB2aWFjZXLDvW1pIHByZW1lbm7DvW1pIG5hcmF6IGEgZG9rw6HFvmUgb2RoYWxpxaUgdnpvcnksIGt0b3LDqSBieSBwcmkgc2Ftb3N0YXRub20gaG9kbm90ZW7DrSBqZWRub3RsaXbDvWNoIHVrYXpvdmF0ZcS+b3Ygem9zdGFsaSBza3J5dMOpLiBTcHLDoXZuZSB6dm9sZW7DoSBtZXRyaWthIHZ6ZGlhbGVub3N0aSBhIG1ldMOzZGEgemhsdWtvdmFuaWEgdW1vxb7FiHVqw7ogb2RoYWxpxaUgc2tyeXTDqSB2esWlYWh5IHYgZMOhdGFjaCwgxI3DrW0gcG9za3l0dWrDuiBjZW5uw70gcG9ka2xhZCBwcmUgcm96aG9kb3ZhbmllIHYgcsO0em55Y2ggb2JsYXN0aWFjaCBhcGxpa292YW7DqWhvIHbDvXNrdW11LgoKViB0ZWp0byBwcsOhY2kgcHJlZHN0YXbDrW1lIHpobHVrb3bDuiBhbmFsw716dSBwcmkgYW5hbMO9emUgw7pkYWpvdiBvICoqcG/EjXRlIG9ieXZhdGXEvm92IG9rcmVzb3YgU2xvdmVuc2tlaiByZXB1Ymxpa3kqKiB6IGjEvmFkaXNrYSBpY2ggKiptZXNhxI1uw6lobyB2w712b2phIHYgcm9rdSAyMDI0KiouIFByYWN1amVtZSBzIGRhdGFiw6F6b3UsIGt0b3LDoSBvYnNhaHVqZSBva3Jlcywgb2JlYyBhIHBvxI1ldCBvYnl2YXRlxL5vdiB2IGplZG5vdGxpdsO9Y2ggbWVzaWFjb2NoIHJva3UgMjAyNCAob2QgamFudcOhcmEgcG8gZGVjZW1iZXIpLiDDmmRhamUgbmFqc2vDtHIgYWdyZWd1amVtZSBuYSDDunJvdmXFiCBva3Jlc292IGEgbsOhc2xlZG5lIG7DoWhvZG5lIHZ5YmVyaWVtZSAqKjEwIG9rcmVzb3YqKiwgbmEga3RvcsO9Y2ggZGVtb27FoXRydWplbWUgY2Vsw70gcG9zdHVwIGhpZXJhcmNoaWNrw6lobyB6aGx1a292YW5pYS4gViAqKlRhYi4gMSoqIHV2w6FkemFtZSBjZWzDuiBuYW1pIHBvdcW+w612YW7DuiBkYXRhYsOhenUgMTAgb2tyZXNvdi4KCmBgYHtyIHNldHVwMiwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShrbml0cikKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KGRwbHlyKQpgYGAKCmBgYHtyfQpybShsaXN0ID0gbHMoKSkKCiMgTmHEjcOtdGFuaWUgw7pkYWpvdiB6IGRhdGFiw6F6eQojIFByZWRwb2tsYWQ6IHPDumJvciAiRGF0YWJhemEuY3N2IiBqZSB2IHJvdm5ha29tIHByaWXEjWlua3UgYWtvIHRlbnRvIFJtZCBzw7pib3IuCiMgUG91xb7DrXZhbWUgc2VwYXLDoXRvciAiOyIga2XEj8W+ZSBpZGUgbyBDU1YgdiBldXLDs3Bza29tIGZvcm3DoXRlLgp1ZGFqZV9yYXcgPC0gcmVhZC5jc3YoIkRhdGFiYXphLmNzdiIsCiAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiOyIsCiAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgojIFIgYXV0b21hdGlja3kgZG9wxLrFiGEgWCBwcmVkIG7DoXp2eSBzdMS6cGNvdiB6YcSNw61uYWrDumNpY2ggxI3DrXNsb20sCiMgcHJldG8gaG8gb2RzdHLDoW5pbWUsIGFieSBzbWUgbWFsaSBzcMOkxaUgbsOhenZ5IDIwMjRNMDEsIDIwMjRNMDIsIC4uLgpuYW1lcyh1ZGFqZV9yYXcpIDwtIHN1YigiXlgiLCAiIiwgbmFtZXModWRhamVfcmF3KSkKCiMgUHJlbWVub3ZhbmllIG1lc2lhY292IG5hIGJlxb5uw6kgbsOhenZ5Cm1lc2lhY2VfbWFwIDwtIGMoCiAgIjIwMjRNMDEiID0gIkphbnXDoXIiLAogICIyMDI0TTAyIiA9ICJGZWJydcOhciIsCiAgIjIwMjRNMDMiID0gIk1hcmVjIiwKICAiMjAyNE0wNCIgPSAiQXByw61sIiwKICAiMjAyNE0wNSIgPSAiTcOhaiIsCiAgIjIwMjRNMDYiID0gIkrDum4iLAogICIyMDI0TTA3IiA9ICJKw7psIiwKICAiMjAyNE0wOCIgPSAiQXVndXN0IiwKICAiMjAyNE0wOSIgPSAiU2VwdGVtYmVyIiwKICAiMjAyNE0xMCIgPSAiT2t0w7NiZXIiLAogICIyMDI0TTExIiA9ICJOb3ZlbWJlciIsCiAgIjIwMjRNMTIiID0gIkRlY2VtYmVyIgopCgojIHByZW1lbnVqZW1lIGxlbiBzdMS6cGNlIHMgbWVzaWFjbWkKbmFtZXModWRhamVfcmF3KVttYXRjaChuYW1lcyhtZXNpYWNlX21hcCksIG5hbWVzKHVkYWplX3JhdykpXSA8LSBtZXNpYWNlX21hcAptb250aF9uYW1lcyA8LSB1bm5hbWUobWVzaWFjZV9tYXApICAjIHYgcG9yYWTDrSBqYW51w6Fy4oCTZGVjZW1iZXIKCiMgQWdyZWfDoWNpYSBuYSDDunJvdmXFiCBva3Jlc292IChzw7rEjWV0IG9ieXZhdGXEvm92IHphIHbFoWV0a3kgb2JjZSkKdWRhamVfb2tyZXN5IDwtIHVkYWplX3JhdyAlPiUKICBncm91cF9ieShPa3JlcykgJT4lCiAgc3VtbWFyaXNlKGFjcm9zcyhhbGxfb2YobW9udGhfbmFtZXMpLCBzdW0sIG5hLnJtID0gVFJVRSkpCgojIE7DoWhvZG7DvSB2w71iZXIgMTAgb2tyZXNvdgpzZXQuc2VlZCgxMjMpICAjIHByZSByZXByb2R1a292YXRlxL5ub3PFpQpva3Jlc3lfdnliZXIgPC0gc2FtcGxlKHVuaXF1ZSh1ZGFqZV9va3Jlc3kkT2tyZXMpLCAxMCkKCnVkYWplX3Z5YmVyIDwtIHN1YnNldCh1ZGFqZV9va3Jlc3ksIE9rcmVzICVpbiUgb2tyZXN5X3Z5YmVyKQoKIyBVc3BvcmlhZGFuaWUgcG9kxL5hIG7DoXp2dSBva3Jlc3UgKG5pZSBqZSBudXRuw6ksIGxlbiBwcmUgcHJlaMS+YWRub3PFpSkKdWRhamVfdnliZXIgPC0gdWRhamVfdnliZXJbb3JkZXIodWRhamVfdnliZXIkT2tyZXMpLCBdCmBgYAoKKipUYWIuIDEuKiogVnlicmFuw6kgb2tyZXN5IGEgaWNoIHBvxI1ldCBvYnl2YXRlxL5vdiB2IGplZG5vdGxpdsO9Y2ggbWVzaWFjb2NoIHJva3UgMjAyNAoKYGBge3J9CmthYmxlKHVkYWplX3Z5YmVyLCBjYXB0aW9uID0gIlZ5YnJhbsOpIG9rcmVzeSBhIGljaCBwb8SNZXQgb2J5dmF0ZcS+b3YgKEphbnXDoXLigJNEZWNlbWJlciAyMDI0KSIpICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFKQpgYGAKCkhpZXJhcmNoaWNrw6EgemhsdWtvdsOhIGFuYWzDvXphIHByYWN1amUgcyBtaWVyYW1pIHZ6ZGlhbGVub3N0aSBtZWR6aSBwb3pvcm92YW5pYW1pLiBBYnkgYm9saSB0aWV0byB2emRpYWxlbm9zdGkgcG9yb3ZuYXRlxL5uw6ksIGplIHBvdHJlYm7DqSwgYWJ5IHbFoWV0a3kgcHJlbWVubsOpIGJvbGkgZGVmaW5vdmFuw6kgbmEgcm92bmFrZWogxaFrw6FsZS4gViBuYcWhb20gcHLDrXBhZGUgc8O6IHByZW1lbm7DvW1pICoqbWVzYcSNbsOpIHBvxI10eSBvYnl2YXRlxL5vdiAoSmFudcOhciBhxb4gRGVjZW1iZXIpKiouIFBvdcW+aWplbWUgdHp2LiB6LcWha8OhbG92YW5pZSwgcHJpxI1vbSB0cmFuc2Zvcm1vdmFuw6kgJHokIGhvZG5vdHkgKHNrw7NyZSkgdnlwb8SNw610YW1lIG5hc2xlZG92bmUKCiQkeiA9IFxmcmFje3gtXG11fXtcc2lnbWF9JCQKCmtkZSAkXG11JCBqZSBzdHJlZG7DoSBob2Rub3RhIGEgJFxzaWdtYSQgamUgxaF0YW5kYXJkbsOhIG9kY2jDvWxrYSBwb3pvcm92YW7DrSAkeCQuIFByZWRwb2tsYWTDoW1lIHByaXRvbSwgxb5lIHPDumJvciDDumRham92IHXFviBuZW9ic2FodWplIE5BIGhvZG5vdHksIGt0b3LDqSBib2xpIG/FoWV0cmVuw6kgdiBwcmVkY2jDoWR6YWrDumNpY2gga3Jva29jaC4KClRvdXRvIG9wZXLDoWNpb3UgesOtc2thbWUgxaFrw6Fsb3ZhbsOpIHBvem9yb3ZhbmlhLCBwcmnEjW9tIGljaCByb3psb8W+ZW5pZSBqZSB6bsOhem9ybmVuw6kgbmFzbGVkb3ZuZS4KCmBgYHtyfQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyMgMSkgUHLDrXByYXZhIMO6ZGFqb3YgYSBkYXRhLmZyYW1lIHNvIMWha8OhbG92YW7DvW1pIMO6ZGFqbWkKIyMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgojIFZ5YmVyaWVtZSBsZW4gbnVtZXJpY2vDqSBzdMS6cGNlIHMgbWVzYcSNbsO9bWkgw7pkYWptaSAoSmFudcOhcuKAk0RlY2VtYmVyKQp1ZGFqZTEwIDwtIHVkYWplX3Z5YmVyWywgbW9udGhfbmFtZXNdCgojIE7DoXp2eSByaWFka292IG5hc3RhdsOtbWUgbmEgbsOhenZ5IG9rcmVzb3YKcm93bmFtZXModWRhamUxMCkgPC0gdWRhamVfdnliZXIkT2tyZXMKCiMgT2RzdHLDoW5pbWUgcHLDrXBhZG7DqSBjaMO9YmFqw7pjZSBob2Rub3R5CnVkYWplX2NvbXBsZXRlIDwtIG5hLm9taXQodWRhamUxMCkKCiMgxaBrw6Fsb3ZhbmllICh6LcWha8OzcmUpIGplZG5vdGxpdsO9Y2ggcHJlbWVubsO9Y2gKdWRhamVfc2NhbGVkIDwtIHNjYWxlKHVkYWplX2NvbXBsZXRlKQpgYGAKCioqT2JyLiAxLioqIEJveHBsb3R5IMWha8OhbG92YW7DvWNoIG51bWVyaWNrw71jaCBwcmVtZW5uw71jaCAobWVzaWFjZSByb2t1IDIwMjQpCgpgYGB7ciBib3hwbG90cywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbnVtX3ZhcnMgIDwtIGFzLmRhdGEuZnJhbWUodWRhamVfc2NhbGVkKQpudW1fcGxvdHMgPC0gbmNvbChudW1fdmFycykKCiMgMTIgbWVzaWFjb3YgPSAzeDQgbXJpZcW+a2EKcGFyKG1mcm93ID0gYygzLCA0KSwKICAgIG1hciAgPSBjKDQsIDQsIDIsIDEpLAogICAgb21hICA9IGMoMCwgMCwgMywgMCkpCgpmb3IgKGNvbCBpbiBuYW1lcyhudW1fdmFycykpIHsKICBib3hwbG90KG51bV92YXJzW1tjb2xdXSwKICAgICAgICAgIG1haW4gICAgICAgPSBjb2wsCiAgICAgICAgICBjb2wgICAgICAgID0gImxpZ2h0Ymx1ZSIsCiAgICAgICAgICBob3Jpem9udGFsID0gVFJVRSkKfQoKbXRleHQoIkJveHBsb3R5IG51bWVyaWNrw71jaCBwcmVtZW5uw71jaCAobWVzaWFjZSByb2t1IDIwMjQpIiwKICAgICAgb3V0ZXIgPSBUUlVFLCBjZXggPSAxLjMsIGZvbnQgPSAyKQpgYGAKClRlbnRva3LDoXQgb2TEvmFobMOpIGhvZG5vdHkgbmV2eWx1xI11amVtZSwgbmFrb8S+a28gZGVmaW51asO6IGtvbmtyw6l0bnkgb2tyZXMuIE5hcHLDrWtsYWQgb2tyZXMgcyB2w71yYXpuZSB2ecWhxaHDrW0gcG/EjXRvbSBvYnl2YXRlxL5vdiBqZSB6IHBvaMS+YWR1IGFuYWzDvXp5IHphdWrDrW1hdsO9IGEgY2hjZW1lIGhvIHYgZMOhdGFjaCB6YWNob3ZhxaUuCgotLS0KClByaSB6aGx1a292ZWogYW5hbMO9emUgamUgZMO0bGXFvml0w6Ega29yZWxhxI1uw6EgbWF0aWNhIHByZW1lbm7DvWNoLiBWeXNva8OhIGtvcmVsw6FjaWEgenbDvWhvZMWIdWplIHByaSB6aGx1a292ZWogYW5hbMO9emUga29yZWxvdmFuw6kgcHJlbWVubsOpLiBQcmV0byBwcmkga29yZWzDoWNpaSBuYWQgMCw4IGFsZWJvIDAsOSDEjWFzdG8gdnlsw7rEjWltZSBqZWRudSB6IGtvcmVsb3ZhbsO9Y2ggcHJlbWVubsO9Y2guIFYgKipUYWIuIDIqKiB1a8Ohxb5lbWUga29yZWxhxI1uw7ogbWF0aWN1IG1lZHppIGplZG5vdGxpdsO9bWkgbWVzaWFjbWkgcm9rdSAyMDI0LiBBayBieSBzYSBtZWR6aSBtZXNpYWNtaSB2eXNreXRvdmFsaSB2ZcS+bWkgdnlzb2vDqSBrb3JlbMOhY2llLCBib2xvIGJ5IG1vxb5uw6kgdXZhxb5vdmHFpSBuYXByw61rbGFkIG8gem7DrcW+ZW7DrSBkaW1lbnppZSBwb21vY291IGFuYWzDvXp5IGhsYXZuw71jaCBrb21wb25lbnRvdi4KCj4gViBwcsOtcGFkZSwgYWsgbcOhbWUgdsOkxI3FocOtIHBvxI1ldCB2w716bmFtbmUga29yZWxvdmFuw71jaCBwcmVtZW5uw71jaCwgc2Egb2Rwb3LDusSNYSB0cmFuc2Zvcm3DoWNpYSBwb21vY291IEFuYWzDvXp5IGhsYXZuw71jaCBrb21wb25lbnRvdiAoUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcykuIAoKKipUYWIuIDIuKiogS29yZWxhxI1uw6EgbWF0aWNhIG1lZHppIG1lc2lhY21pIHJva3UgMjAyNAoKYGBge3J9CmNvcl9tYXQgPC0gY29yKHVkYWplX3NjYWxlZCwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpCmNvcl9tYXQgPC0gcm91bmQoY29yX21hdCwgMikKcHJpbnQoY29yX21hdCkKYGBgCgpLYcW+ZMOpbXUgb2tyZXN1IHpvZHBvdmVkw6EgamVkZW4gcmlhZG9rIHBvem9yb3ZhbsOtLiBWemRpYWxlbm9zxaUgbWVkemkgb2tyZXNtaSAkaSQgYSAkaiQgamU6CgokJApkXntpan0gPSBcc3FydHtcc3VtX2sgKHheaV9rIC0geF5qX2spXjJ9CiQkCgprZGUgJHheaV9rJCBqZSAkayQtdMOhIHByZW1lbm7DoSB2c3R1cHVqw7pjYSBkbyB2w71wb8SNdHUgKHYgbmHFoW9tIHByw61wYWRlIGlkZSBvIG1lc2HEjW7DqSBwb8SNdHkgb2J5dmF0ZcS+b3Y6IEphbnXDoXIsIEZlYnJ1w6FyLCAuLi4sIERlY2VtYmVyKSBva3Jlc3UgJGkkLiBUZW50byB0eXAgdnpkaWFsZW5vc3RpIG5hesO9dmFtZSBFdWtsaWRvdnNrw6EgdnpkaWFsZW5vc8WlLiBWemRpYWxlbm9zdGkgbWVkemkgamVkbm90bGl2w71taSBva3Jlc21pIHNhIHPDumhybm5lIHZ5amFkcnVqw7ogYWogdiBtYXRpY2kgdnpkaWFsZW5vc3RpLCDEjW8gamUgdiBuYcWhb20gcHLDrXBhZGUgdXZlZGVuw6kgdiAqKlRhYi4gMyoqLiBBbmFsw716b3UgdGVqdG8gdGFidcS+a3kgbcO0xb5lbWUgaWRlbnRpZmlrb3ZhxaUgZHZvamljZSBva3Jlc292LCBrdG9yw6kgc8O6IHNpIHBvZMS+YSBwcmllYmVodSBwb8SNdHUgb2J5dmF0ZcS+b3YgdiByb2t1IDIwMjQgdmXEvm1pIHBvZG9ibsOpIChtYWzDoSB2emRpYWxlbm9zxaUpLCByZXNwLiB2ZcS+bWkgb2RsacWhbsOpICh2ZcS+a8OhIHZ6ZGlhbGVub3PFpSkuCgoqKlRhYi4gMy4qKiBFdWtsaWRvdnNrw6EgbWF0aWNhIHZ6ZGlhbGVub3N0w60gbWVkemkgb2tyZXNtaQoKYGBge3J9CiMjID09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyMgMykgRGlzdGFuY2UgbWF0cml4CiMjID09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBwb25lY2jDoW1lIHDDtHZvZG7DqSBuw6F6dnkgb2tyZXNvdiBha28gcm93bmFtZXMKZGlzdF9tYXQgPC0gcm91bmQoZGlzdCh1ZGFqZV9zY2FsZWQsIG1ldGhvZCA9ICJldWNsaWRlYW4iKSwgMikKZGlzdF9tYXQKYGBgCgojIyBQcmluY8OtcCBoaWVyYXJjaGlja8OpaG8gemhsdWtvdmFuaWEgKFdhcmRvdmEgbWV0w7NkYSkKClpobHVrb3ZhbmllIHYgcHLDrXBhZGUgV2FyZG92ZWogbWV0w7NkeSBwcmViaWVoYSB6ZG9sYSBzbWVyb20gbmFob3IsIHQuIGouIHphxI3DrW5hbWUgcyBqZWRub8SNbGVubsO9bWkga2xhc3RyYW1pLCBrdG9yw6kgcG9zdHVwbmUgemx1xI11amVtZS4gVMOhdG8gbWV0w7NkYSBwYXRyw60gbWVkemkgYWdsb21lcmF0w612bmUgaGllcmFyY2hpY2vDqSBtZXTDs2R5LiBNaW5pbWFsaXp1amUgbsOhcmFzdCB2bsO6dG9ybmVqIHZhcmlhYmlsaXR5IHByaSBzcG9qZW7DrSBkdm9jaCBrbGFzdHJvdiwgcHJpxI1vbSB2eXXFvsOtdmEgbmFzbGVkb3Zuw6kgdsO9cG/EjXR5OgoKV2FyZG92YSBtZXTDs2RhIG1pbmltYWxpenVqZSBzdW11IMWhdHZvcmNvdiBjaMO9YiAoRXJyb3IgU3VtIG9mIFNxdWFyZXMg4oCTIEVTUykKCiQkRVNTKEMpID0gXHN1bV97aSBcaW4gQ30gXGxWZXJ0IHhfaSAtIFxiYXJ7eH1fQyBcclZlcnReMiwkJAoKa2RlICRDJCBqZSB6dmHFvm92YW7DvSBrbGFzdGVyICh6aGx1aykuIFYga2HFvmRvbSBrcm9rdSB6bHXEjW92YW5pYSBkdm9jaCBrbGFzdHJvdiBXYXJkb3ZhIG1ldMOzZGEgaMS+YWTDoSBtaW5pbcOhbG55IHByw61yYXN0b2sgc3VteSDFoXR2b3Jjb3YgY2jDvWIgKCRcRGVsdGEgRVNTJCksIHByacSNb20KCiQkXERlbHRhIEVTUyA9IEVTUyhBIFxjdXAgQikgLSBFU1MoQSkgLSBFU1MoQikuJCQKCkR2b2ppY2EgemhsdWtvdiwga3RvcsOhIHRlanRvIHBvZG1pZW5rZSBvIG1pbmltYWxpesOhY2lpIHZ5aG92dWplLCBqZSBuw6FzbGVkbmUgemzDusSNZW7DoSBhIHByZWNow6FkemEgc2EgayDEj2FsxaFpZW11IGtyb2t1LiBUbyBzcHJhdmlkbGEgdmVkaWUgayB2eXR2w6FyYW5pdSByZWxhdMOtdm5lIGhvbW9nw6lubnljaCB6aGx1a292LCBwcmnEjW9tIG5lZG9jaMOhZHphIGsgb2R0cmjDoXZhbml1IG9kxL5haGzDvWNoIGhvZG7DtHQgdGFrLCBha28gcHJpIG5pZWt0b3LDvWNoIGluw71jaCB6aGx1a292YWPDrWNoIG1ldMOzZGFjaC4KCioqT2JyLiAyLioqIEhpZXJhcmNoaWNrw6kgemhsdWtvdmFuaWUg4oCTIGRlbmRyb2dyYW0uIMSMZXJ2ZW7DoSDEjWlhcmEgdXLEjXVqZSByZXogZGVmaW51asO6Y2kgdHJpIGtsYXN0cmUuCgpgYGB7cn0KIyMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIyA0KSBIaWVyYXJjaGljYWwgY2x1c3RlcmluZwojIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09CgpoYyA8LSBoY2x1c3QoZGlzdF9tYXQsIG1ldGhvZCA9ICJ3YXJkLkQyIikKCnBsb3QoaGMsIGxhYmVscyA9IHJvd25hbWVzKHVkYWplX3NjYWxlZCksCiAgICAgbWFpbiA9ICJIaWVyYXJjaGljYWwgY2x1c3RlcmluZyBvZiBkaXN0cmljdHMgKFdhcmQuRDIpIiwKICAgICB4bGFiID0gIiIsIHN1YiA9ICIiKQoKIyBwb8SNZXQga2xhc3Ryb3YKayA8LSAzCmhfY3V0IDwtIGhjJGhlaWdodFtsZW5ndGgoaGMkaGVpZ2h0KSAtIChrIC0gMSldCmFibGluZShoID0gaF9jdXQsIGNvbCA9ICJyZWQiLCBsd2QgPSAyLCBsdHkgPSAyKQoKa2xhc3Rlcl9tZW1iZXJzaGlwIDwtIGN1dHJlZShoYywgayA9IGspCgp1ZGFqZV9rbGFzdGVycyA8LSBkYXRhLmZyYW1lKAogIE9rcmVzID0gcm93bmFtZXModWRhamVfY29tcGxldGUpLAogIHVkYWplX2NvbXBsZXRlLAogIGtsYXN0ZXIgPSBmYWN0b3Ioa2xhc3Rlcl9tZW1iZXJzaGlwKQopCmBgYAoKKipUYWIuIDQuKiogUHLDrXNsdcWhbm9zxaUgb2tyZXNvdiBkbyBrbGFzdHJvdgoKYGBge3J9CmRhdGFfcHJhYyA8LSBkYXRhLmZyYW1lKAogIE9rcmVzID0gdWRhamVfa2xhc3RlcnMkT2tyZXMsCiAga2xhc3RlciA9IHVkYWplX2tsYXN0ZXJzJGtsYXN0ZXIKKQpkYXRhX3ByYWMKYGBgCgpWeWtvbmFuw6Ega2xhc3Ryb3bDoSBhbmFsw716YSBrbGFzaWZpa3VqZSBva3Jlc3kgZG8gdHJvY2gga2xhc3Ryb3YuIFpsb8W+ZW5pZSBrbGFzdHJvdiBvZHLDocW+YSBqZWRuYWsgw7pyb3ZlxYggcG/EjXR1IG9ieXZhdGXEvm92LCBhbGUgYWogcG9kb2Jub3PFpSBwcmllYmVodSBwb8SNYXMgamVkbm90bGl2w71jaCBtZXNpYWNvdiByb2t1IDIwMjQuIFYgamVkbm9tIGtsYXN0cmkgbcO0xb5lbWUgbsOhanPFpSBza8O0ciAqKnbDpMSNxaFpZSBva3Jlc3kgcyBwb2RvYm7DvW0gcG/EjXRvbSBvYnl2YXRlxL5vdioqLCB2IGlub20gc2vDtHIgKiptZW7FoWllIG9rcmVzeSoqLCBwcsOtcGFkbmUgb2tyZXN5IHMgb2RsacWhbm91IGR5bmFtaWtvdSAobWllcm55IG7DoXJhc3QgYWxlYm8gcG9rbGVzKS4gSW50ZXJwcmV0w6FjaWEga29ua3LDqXRueWNoIGtsYXN0cm92IHrDoXZpc8OtIG9kIHbDvXNsZWRuw71jaCBob2Ruw7R0IGEgamUgbW/Fvm7DqSBqdSBkb3BsbmnFpSBuYXByw61rbGFkIG1hcG92w71tIHpvYnJhemVuw61tIGFsZWJvIHBvcm92bmFuw61tIHMgxI9hbMWhw61taSBjaGFyYWt0ZXJpc3Rpa2FtaSBva3Jlc292LgoKIyMgRGVza3JpcHTDrXZuZSDFoXRhdGlzdGlreSB2w71zbGVka292CgpOYSB6w6FrbGFkZSAqKlRhYi4gNSoqIG3DtMW+ZW1lIHBvc8O6ZGnFpSwgYWtvIGRvYnJlIGplZG5vdGxpdsOpIG1lc2lhY2UgKHByZW1lbm7DqSkgcHJpc3BpZXZhasO6IGsgcm96bGnFoW92YW5pdSBtZWR6aSBrbGFzdHJhbWkuIFZuw7p0cm9rbGFzdHJvdsOhIHZhcmlhYmlsaXRhIChXU1MpIHZ5amFkcnVqZSwgbmFrb8S+a28gc8O6IG9rcmVzeSB2IHLDoW1jaSBqZWRuw6lobyBrbGFzdHJhIHBvZG9ibsOpLCB6YXRpYcS+IMSNbyBtZWR6aS1rbGFzdHJvdsOhIHZhcmlhYmlsaXRhIChCU1MpIHZ5amFkcnVqZSByb3pkaWVseSBtZWR6aSBrbGFzdHJhbWkuIFBvZGllbCBtZWR6aS1rbGFzdHJvdmVqIHZhcmlhYmlsaXR5IG5hIGNlbGtvdmVqIHZhcmlhYmlsaXRlIChzdMS6cGVjIGBQcm9wX0JldHdlZW5gKSBuw6FtIHVrYXp1amUsIGRvIGFrZWogbWllcnkgZGFuw70gbWVzaWFjIHByaXNwaWV2YSBrIHNlcGFyw6FjaWkga2xhc3Ryb3YuCgoqKlRhYi4gNS4qKiBWeXN2ZXRsZW5pZSB2bsO6dHJva2xhc3Ryb3ZlaiB2YXJpYWJpbGl0eSB6IGjEvmFkaXNrYSBqZWRub3RsaXbDvWNoIHByZW1lbm7DvWNoIChtZXNpYWNvdikKCmBgYHtyfQojIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMjIDUpIFZhcmlhYmlsaXR5IG1lYXN1cmVzCiMjID09PT09PT09PT09PT09PT09PT09PT09PT09PT0KCnNzcSA8LSBmdW5jdGlvbih4LCBtKSBzdW0oKHggLSBtKV4yKQoKdmFyX25hbWVzIDwtIGNvbG5hbWVzKHVkYWplX3NjYWxlZCkKClRTUyA8LSBzYXBwbHkodmFyX25hbWVzLCBmdW5jdGlvbih2KSBzc3EodWRhamVfc2NhbGVkWywgdl0sIG1lYW4odWRhamVfc2NhbGVkWywgdl0pKSkKCldTUyA8LSBzYXBwbHkodmFyX25hbWVzLCBmdW5jdGlvbih2KSB7CiAgeCA8LSB1ZGFqZV9zY2FsZWRbLCB2XQogIHRhcHBseSh4LCBrbGFzdGVyX21lbWJlcnNoaXAsIGZ1bmN0aW9uKHopIHNzcSh6LCBtZWFuKHopKSkgfD4gc3VtKCkKfSkKCkJTUyA8LSBUU1MgLSBXU1MKCnNzX3RhYmxlIDwtIGRhdGEuZnJhbWUoCiAgVmFyaWFibGUgPSB2YXJfbmFtZXMsCiAgVFNTID0gVFNTLAogIFdTUyA9IFdTUywKICBCU1MgPSBCU1MsCiAgUHJvcF9CZXR3ZWVuID0gQlNTIC8gVFNTCikKCnNzX3RhYmxlCmBgYAoKVnnFocWhaWUgaG9kbm90eSBwb2RpZWx1IGBQcm9wX0JldHdlZW5gIHpuYW1lbmFqw7osIMW+ZSBkYW7DvSBtZXNpYWMgdsO9cmF6bmVqxaFpZSBvZGxpxaF1amUgamVkbm90bGl2w6kga2xhc3RyZS4gTWVzaWFjZSBzIG5pxb7FoW91IGhvZG5vdG91IHRvaHRvIHBvZGllbHUgcHJlZHN0YXZ1asO6IG9iZG9iaWEsIHYga3RvcsO9Y2ggc8O6IG9rcmVzeSBtZWR6aSBzZWJvdSByZWxhdMOtdm5lIHBvZG9ibsOpIGEgZGFuw6EgcHJlbWVubsOhIG5pZSBqZSB0YWvDvW0gc2lsbsO9bSDigJ5zZXBhcsOhdG9yb23igJwuCgotLS0KCk5hIHrDoXZlciBzYSBwb3pyaWVtZSBuYSBjZW50cm9pZHksIHQuIGouIHByaWVtZXJuw6kgaG9kbm90eSBzbGVkb3ZhbsO9Y2ggcHJlbWVubsO9Y2ggdiBqZWRub3RsaXbDvWNoIGtsYXN0cm9jaC4gVGllIG7DoW0gcG9za3l0dWrDuiBwcmVoxL5hZG7DvSBvcGlzIHR5cGlja8OpaG8gb2tyZXN1IHYga2HFvmRvbSBrbGFzdHJpLgoKYGBge3J9CiMgVnl0dm9yw61tZSBkw6F0b3bDvSByw6FtZWMgcyBww7R2b2Ruw71taSAobmXFoWvDoWxvdmFuw71taSkgaG9kbm90YW1pIGEgcHJpcmFkZW7DvW0ga2xhc3Ryb20KdWRhamVfY29tcGxldGVfa2xhc3RlciA8LSBkYXRhLmZyYW1lKAogIE9rcmVzID0gcm93bmFtZXModWRhamVfY29tcGxldGUpLAogIHVkYWplX2NvbXBsZXRlLAogIGtsYXN0ZXIgPSBmYWN0b3Ioa2xhc3Rlcl9tZW1iZXJzaGlwKQopCmBgYAoKKipUYWIuIDYuKiogQ2VudHJvaWR5IOKAkyBwcmllbWVybsOpIHBvxI10eSBvYnl2YXRlxL5vdiB2IGplZG5vdGxpdsO9Y2ggbWVzaWFjb2NoIHBvZMS+YSBrbGFzdHJvdgoKYGBge3J9CmRlc2NyaXB0aXZlcyA8LSB1ZGFqZV9jb21wbGV0ZV9rbGFzdGVyICU+JQogIGdyb3VwX2J5KGtsYXN0ZXIpICU+JQogIHN1bW1hcmlzZSgKICAgIGFjcm9zcygKICAgICAgLmNvbHMgPSB3aGVyZShpcy5udW1lcmljKSwKICAgICAgLmZucyAgPSBsaXN0KG1lYW4gPSB+IG1lYW4oLngsIG5hLnJtID0gVFJVRSkpLAogICAgICAubmFtZXMgPSAiey5jb2x9X3suZm59IgogICAgKQogICkKCmRlc2NyaXB0aXZlcwpgYGAKClByaSBwb2jEvmFkZSBuYSBjZW50cm9pZHkgdmlkw61tZSByb3pkaWVseSB2IHByaWVtZXJub20gcG/EjXRlIG9ieXZhdGXEvm92IHYgamVkbm90bGl2w71jaCBrbGFzdHJvY2guIEplZGVuIGtsYXN0ZXIgbcO0xb5lIHphaMWVxYhhxaUgc2vDtHIgdsOkxI3FoWllIG9rcmVzeSBzIHZ5xaHFocOtbWkgcHJpZW1lcm7DvW1pIGhvZG5vdGFtaSBwb8SNYXMgY2Vsw6lobyByb2thLCBpbsO9IHNrw7RyIG1lbsWhaWUgb2tyZXN5IHMgbmnFvsWhw61taSBob2Rub3RhbWkuIFphdWrDrW1hdsOpIHPDuiBhaiBwcsOtcGFkbsOpIHJvemRpZWx5IG1lZHppIG1lc2lhY21pIOKAkyBuYXByw61rbGFkIG1pZXJuZSBuw6FyYXN0eSBhbGVibyBwb2tsZXN5IHYgbmlla3RvcsO9Y2ggb2Jkb2JpYWNoIHJva2EsIGt0b3LDqSBtw7TFvnUgc8O6dmlzaWXFpSBzIG1pZ3LDoWNpb3UsIHNlesOzbm55bWkgdnBseXZtaSBhbGVibyBhZG1pbmlzdHJhdMOtdm55bWkgem1lbmFtaSAobmFwci4g4oCecHJpaGzDoXNlbmll4oCcIG9ieXZhdGXEvm92KS4KCiMjIFrDoXZlcgoKUHJlZGxvxb5lbsOhIGFuYWzDvXphIHNhIHphb2JlcsOhICoqcG/EjXRvbSBvYnl2YXRlxL5vdiB2eWJyYW7DvWNoIG9rcmVzb3YgU2xvdmVuc2tlaiByZXB1Ymxpa3kqKiB2IHrDoXZpc2xvc3RpIG9kIGljaCAqKm1lc2HEjW7DqWhvIHbDvXZvamEgdiByb2t1IDIwMjQqKi4gTmEgesOha2xhZGUgaGllcmFyY2hpY2tlaiB6aGx1a292ZWogYW5hbMO9enkgc21lIG9rcmVzeSByb3pkZWxpbGkgZG8gdHJvY2gga2xhc3Ryb3YuIEtsYXN0cmUgc2EgbMOtxaFpYSBuYWptw6Qgw7pyb3bFiG91IHByaWVtZXJuw6lobyBwb8SNdHUgb2J5dmF0ZcS+b3YgYSBwb2RvYm5vc8Wlb3UgcHJpZWJlaHUgdiDEjWFzZS4gCgpUYWvDoXRvIGFuYWzDvXphIG3DtMW+ZSBzbMO6xb5pxaUgYWtvIHBvZGtsYWQgcHJlOgoKLSBwbMOhbm92YW5pZSB2ZXJlam7DvWNoIHNsdcW+aWViICjFoWtvbHksIHpkcmF2b3Ruw61jdNCy0L4sIGRvcHJhdmEpLCAgCi0gcHJpZXN0b3JvdsOpIHBsw6Fub3ZhbmllIGEgcm96dm9qIMO6emVtaWEsICAKLSBwb3Jvdm7DoXZhbmllIHR5cGlja3kg4oCebWFsw71jaOKAnCwg4oCec3RyZWRuw71jaOKAnCBhIOKAnnbDpMSNxaHDrWNo4oCcIG9rcmVzb3YgcG9kxL5hIHp2b2xlbsO9Y2ggdWthem92YXRlxL5vdi4KClpobHVrb3bDoSBhbmFsw716YSB0ZWRhIHVtb8W+xYh1amUgaWRlbnRpZmlrb3ZhxaUgc2t1cGlueSBwb2RvYm7DvWNoIG9rcmVzb3YgYSBuw6FzbGVkbmUgY2llbGVuw71tIHNww7Rzb2JvbSBhbmFseXpvdmHFpSBhIHBsw6Fub3ZhxaUgdmVyZWpuw6kgcG9saXRpa3kgxI1pIHJvenZvam92w6kgcHJvamVrdHkgcG9kxL5hIGljaCBwcsOtc2x1xaFub3N0aSBrdSBrbGFzdHJvbS4K