Analýza teplôt vybraných miest - rok 2000

Úvod

Predkladaná analýza sa zameriava na skúmanie teplotných údajov 9 vybraných európskych miest za rok 2000. Sledované premenné zahŕňajú priemernú, maximálnu a minimálnu teplotu, ktoré charakterizujú teplotné pomery jednotlivých miest.

Cieľom analýzy je identifikovať teplotné rozdiely medzi mestami, porovnať ich teplotné profily a zoskupiť mestá podľa podobnosti teplotných podmienok.

Načítanie knižníc a príprava údajov

library(tidyverse)
library(ggplot2)
library(dplyr)
library(cluster)
library(factoextra)

# Načítanie údajov
df <- read.csv("weather_prediction_dataset.csv")

# Transformácia stĺpca DATE na dátum
df$DATE <- as.Date(as.character(df$DATE), format = "%Y%m%d")

# Výber údajov len za rok 2000
df_2000 <- df %>%
  filter(format(DATE, "%Y") == "2000")

# Výber 9 miest (bez Budapešti)
vybrate_mesta <- c('BASEL', 'DE_BILT', 'DRESDEN', 'HEATHROW', 
                   'MUENCHEN', 'OSLO', 'ROMA', 'STOCKHOLM', 'TOURS')

Priemer za rok pre každé mesto

Na základe údajov za rok 2000 sme vypočítali priemerné hodnoty teplotných ukazovateľov pre jednotlivé mestá.

# Získanie všetkých teplotných stĺpcov pre vybrané mestá
teplotne_stlpce <- character()
for(mesto in vybrate_mesta) {
  teplotne_stlpce <- c(teplotne_stlpce, 
                       grep(paste0(mesto, "_temp_"), names(df_2000), value = TRUE))
}

# Výber iba teplotných údajov
df_teploty <- df_2000[, c("DATE", "MONTH", teplotne_stlpce)]

# Výpočet priemerných hodnôt pre každé mesto
priemerne_teploty <- df_teploty %>%
  summarise(across(contains("_temp_mean"), ~ round(mean(., na.rm = TRUE), 2))) %>%
  pivot_longer(everything(), names_to = "Mesto", values_to = "Priemerna_teplota") %>%
  mutate(Mesto = str_remove(Mesto, "_temp_mean")) %>%
  arrange(desc(Priemerna_teplota))

maximalne_teploty <- df_teploty %>%
  summarise(across(contains("_temp_max"), ~ round(mean(., na.rm = TRUE), 2))) %>%
  pivot_longer(everything(), names_to = "Mesto", values_to = "Maximalna_teplota") %>%
  mutate(Mesto = str_remove(Mesto, "_temp_max"))

minimalne_teploty <- df_teploty %>%
  summarise(across(contains("_temp_min"), ~ round(mean(., na.rm = TRUE), 2))) %>%
  pivot_longer(everything(), names_to = "Mesto", values_to = "Minimalna_teplota") %>%
  mutate(Mesto = str_remove(Mesto, "_temp_min"))

# Spojenie všetkých údajov
data_mesta <- priemerne_teploty %>%
  left_join(maximalne_teploty, by = "Mesto") %>%
  left_join(minimalne_teploty, by = "Mesto")

print(data_mesta)

Analýza priemerov ukazuje, že Roma má najvyššie priemerné, maximálne aj minimálne teploty, zatiaľ čo Oslo má najnižšie hodnoty vo všetkých troch kategóriách.

Korelačná matica

Korelačná matica vypočítaná pre teplotné ukazovatele ukazuje vzťahy medzi nimi.

# Výber iba priemerných teplôt pre koreláciu
teplotne_data <- df_teploty %>%
  select(contains('_temp_mean'))

# Výpočet korelačnej matice
cor_matrix <- cor(teplotne_data, use = "complete.obs")
cor_matrix_rounded <- round(cor_matrix, 2)

# Pomenovanie riadkov a stĺpcov
mesta_nazvy <- str_remove(colnames(teplotne_data), "_temp_mean")
rownames(cor_matrix_rounded) <- mesta_nazvy
colnames(cor_matrix_rounded) <- mesta_nazvy

print(cor_matrix_rounded)
          BASEL DE_BILT DRESDEN HEATHROW MUENCHEN OSLO ROMA STOCKHOLM TOURS
BASEL      1.00    0.92    0.93     0.89     0.97 0.85 0.88      0.85  0.92
DE_BILT    0.92    1.00    0.91     0.92     0.90 0.86 0.81      0.85  0.91
DRESDEN    0.93    0.91    1.00     0.84     0.96 0.86 0.84      0.87  0.85
HEATHROW   0.89    0.92    0.84     1.00     0.86 0.84 0.81      0.82  0.94
MUENCHEN   0.97    0.90    0.96     0.86     1.00 0.84 0.86      0.84  0.88
OSLO       0.85    0.86    0.86     0.84     0.84 1.00 0.85      0.95  0.83
ROMA       0.88    0.81    0.84     0.81     0.86 0.85 1.00      0.86  0.84
STOCKHOLM  0.85    0.85    0.87     0.82     0.84 0.95 0.86      1.00  0.83
TOURS      0.92    0.91    0.85     0.94     0.88 0.83 0.84      0.83  1.00

Z matice vyplýva, že teploty medzi mestami sú vysoko kladne korelované. Najvyššia korelácia je medzi MUENCHEN a BASEL (r = 0.97), čo naznačuje veľmi podobné teplotné trendy. Naopak, najnižšia korelácia je medzi ROMA a DE_BILT a ROMA a HEATHROW (r = 0.81), čo odráža rozdielne geografické polohy.

Matica vzdialenosti medzi mestami

Matica vzdialeností ukazuje, do akej miery sa teplotné profily miest navzájom líšia.

# Výpočet euklidovskej vzdialenosti
dist_matrix <- dist(t(teplotne_data), method = "euclidean")
dist_matrix <- as.matrix(dist_matrix)
dist_matrix_rounded <- round(dist_matrix, 2)

rownames(dist_matrix_rounded) <- mesta_nazvy
colnames(dist_matrix_rounded) <- mesta_nazvy

print(dist_matrix_rounded)
           BASEL DE_BILT DRESDEN HEATHROW MUENCHEN   OSLO   ROMA STOCKHOLM  TOURS
BASEL       0.00   52.74   56.86    57.21    42.23 102.87  97.71     92.97  48.77
DE_BILT    52.74    0.00   61.45    42.84    63.19  88.58 119.56     80.74  52.74
DRESDEN    56.86   61.45    0.00    80.73    40.92  88.36 126.13     78.54  81.05
HEATHROW   57.21   42.84   80.73     0.00    76.04 100.74 109.37     92.47  40.42
MUENCHEN   42.23   63.19   40.92    76.04     0.00  93.43 122.21     85.83  73.99
OSLO      102.87   88.58   88.36   100.74    93.43   0.00 168.71     42.12 111.55
ROMA       97.71  119.56  126.13   109.37   122.21 168.71   0.00    155.18  95.23
STOCKHOLM  92.97   80.74   78.54    92.47    85.83  42.12 155.18      0.00 101.64
TOURS      48.77   52.74   81.05    40.42    73.99 111.55  95.23    101.64   0.00

Najväčšia vzdialenosť je medzi Romou a Oslom (168.71), čo potvrdzuje výrazné teplotné rozdiely medzi týmito mestami. Naopak, najmenšia vzdialenosť je medzi HEATHROW a TOURS (40.42), čo naznačuje veľmi podobné teplotné podmienky.

Hierarchické zhlukovanie (Wardova metóda)

Na základe dendrogramu hierarchického zhlukovania možno pozorovať nasledujúce:

# Z-škálovanie údajov
data_scaled <- scale(teplotne_data)

# Hierarchické zhlukovanie
hc <- hclust(dist(t(data_scaled), method = "euclidean"), method = "ward.D2")

# Vizualizácia dendrogramu
plot(hc, labels = mesta_nazvy, main = "Hierarchické zhlukovanie teplôt - Wardova metóda (2000)", cex = 0.8)
rect.hclust(hc, k = 4, border = "red")

Príslušnosť miest do klastrov

clusters <- cutree(hc, k = 4)
city_data$cluster <- as.factor(clusters)

print(city_data[, c("Mesto", "cluster")])

Mestá sa rozdeľujú do 4 hlavných klastrov:

  • Klaster 1: BASEL, DRESDEN, MUENCHEN - stredoeurópske vnútrozemské mestá s kontinentálnym vplyvom

  • Klaster 2: DE_BILT, HEATHROW, TOURS - západoeurópske mestá s oceánskym a prímorským vplyvom

  • Klaster 3: OSLO, STOCKHOLM - severské mestá s chladnejším podnebím

  • Klaster 4: ROMA - jediné stredomorské mesto s výrazne teplejším podnebím

Deskriptívne štatistiky výsledkov

Vnútro- a medziklastrová variabilita

data_matrix <- as.matrix(teplotne_data)

# Celková variabilita (TSS)
celkova_stredna_hodnota <- mean(data_matrix)
tss <- sum((data_matrix - celkova_stredna_hodnota)^2)

# Vnútroklastrová variabilita (WSS)
wss <- 0
for(k in 1:4) {
  cluster_indices <- which(clusters == k)
  if(length(cluster_indices) > 0) {
    cluster_data <- data_matrix[, cluster_indices, drop = FALSE]
    cluster_stredna_hodnota <- mean(cluster_data)
    wss <- wss + sum((cluster_data - cluster_stredna_hodnota)^2)
  }
}

bss <- tss - wss
prop_between <- bss / tss

variabilita <- data.frame(
  Celkova_variancia = round(tss, 2),
  Vnutroklastrová_variancia = round(wss, 2),
  Medziklastrová_variancia = round(bss, 2),
  Podiel_medzi = round(prop_between, 3)
)

print(variabilita)

Na základe výsledkov môžeme konštatovať, že celková variabilita teplotných údajov je 149046.4. Z tejto celkovej variability je 14821.29 (9.9%) vysvetlených rozdelením miest do klastrov, zatiaľčo 134225.1 (90.1%) variability zostáva v rámci jednotlivých klastrov.

Podiel medziklastrovej variability 0.099 naznačuje, že zhlukovanie vysvetľuje približne 10% celkovej variability údajov. Táto hodnota je typická pre klimatologické údaje, kde mestá v rámci tej istej geografickej oblasti majú podobné teplotné trendy, no stále existujú výrazné rozdiely spôsobené lokálnymi vplyvmi, nadmorskou výškou a inými faktormi.

Relatívne nízka medziklastrová variabilita potvrdzuje, že európske mestá majú do určitej miery podobné teplotné profily, no identifikované klastre napriek tomu zachytávajú významné regionálne rozdiely v teplotných pomeroch.

Centroidy klastrov

# Vytvorenie kompletného data frame s klástrami
clusters <- cutree(hc, k = 4)

final_data <- data.frame(
  Mesto = c("BASEL", "DE_BILT", "DRESDEN", "HEATHROW", "MUENCHEN", "OSLO", "ROMA", "STOCKHOLM", "TOURS"),
  Klaster = as.factor(clusters)
) %>%
  left_join(data_mesta, by = "Mesto")

# Výpočet centroidov
centroids <- final_data %>%
  group_by(Klaster) %>%
  summarise(
    Pocet_miest = n(),
    Priemerna_teplota = round(mean(Priemerna_teplota), 2),
    Maximalna_teplota = round(mean(Maximalna_teplota), 2),
    Minimalna_teplota = round(mean(Minimalna_teplota), 2),
    .groups = 'drop'
  )

print(centroids)

Na základe centroidov klastrov môžeme pozorovať charakteristiky jednotlivých klastrov:

  • Klaster 1 (3 mestá): Priemerná teplota 10.98°C, maximálna 15.22°C, minimálna 7.17°C - stredoeurópske vnútrozemské mestá so strednými teplotami

  • Klaster 2 (3 mestá): Priemerná teplota 11.60°C, maximálna 15.42°C, minimálna 7.72°C - západoeurópske mestá s mierne vyššími teplotami vďaka oceánskemu vplyvu

  • Klaster 3 (2 mestá): Priemerná teplota 8.15°C, maximálna 11.48°C, minimálna 5.24°C - severské mestá s výrazne nižšími teplotami

  • Klaster 4 (1 mesto): Priemerná teplota 15.82°C, maximálna 21.90°C, minimálna 11.47°C - stredomorské mesto s najvyššími teplotami

Rozdiely medzi klastrami sú výrazné - medzi najteplejším a najchladnejším klastrom je rozdiel 7.67°C v priemernej teplote. Klastre dobre odrážajú geografickú polohu a klimatické pomery jednotlivých miest.

Záver

Na základe vykonanej analýzy teplotných údajov 9 vybraných európskych miest za rok 2000 možno konštatovať, že identifikované klastre dobre odrážajú geografickú a klimatickú rozmanitosť Európy.

Zhluková analýza úspešne rozdelila mestá do 4 homogénnych skupín podľa ich teplotných charakteristík. Najvýraznejšie rozdiely sú medzi stredomorským klimatom (Roma) a severskými oblasťami (Oslo, Stockholm), pričom rozdiel v priemerných teplotách dosahuje takmer 8°C. Stredoeurópske a západoeurópske mestá tvoria dve samostatné skupiny so podobnými, no mierne odlišnými teplotnými profilmi.

Hodnota medziklastrovej variability 9.9% naznačuje, že napriek určitej podobnosti teplotných trendov medzi európskymi mestami, identifikované klastre zachytávajú významné regionálne rozdiely. Tieto výsledky poskytujú cenný vhľad do klimatických pomerov Európy a môžu slúžiť ako podklad pre ďalšie klimatologické štúdie alebo regionálne plánovanie.

LS0tDQp0aXRsZTogIsOabG9oYV84Ig0KYXV0aG9yOiAiQmMuIEtyeXN0eW5hIFZhc3lseW5hIg0KZGF0ZTogIk5vdmVtYmVyIDIwMjUiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdGhlbWU6IHVuaXRlZA0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgcGRmX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIGRmX3ByaW50OiBwYWdlZA0KZWRpdG9yX29wdGlvbnM6DQogIG1hcmtkb3duOg0KICAgIHdyYXA6IDcyDQotLS0NCg0KIyBBbmFsw716YSB0ZXBsw7R0IHZ5YnJhbsO9Y2ggbWllc3QgLSByb2sgMjAwMA0KDQojIyDDmnZvZA0KUHJlZGtsYWRhbsOhIGFuYWzDvXphIHNhIHphbWVyaWF2YSBuYSBza8O6bWFuaWUgdGVwbG90bsO9Y2ggw7pkYWpvdiAqKjkqKiB2eWJyYW7DvWNoIGV1csOzcHNreWNoIG1pZXN0IHphIHJvayAqKjIwMDAqKi4gU2xlZG92YW7DqSBwcmVtZW5uw6kgemFoxZXFiGFqw7ogcHJpZW1lcm7DuiwgbWF4aW3DoWxudSBhIG1pbmltw6FsbnUgdGVwbG90dSwga3RvcsOpIGNoYXJha3Rlcml6dWrDuiB0ZXBsb3Ruw6kgcG9tZXJ5IGplZG5vdGxpdsO9Y2ggbWllc3QuDQoNCkNpZcS+b20gYW5hbMO9enkgamUgaWRlbnRpZmlrb3ZhxaUgdGVwbG90bsOpIHJvemRpZWx5IG1lZHppIG1lc3RhbWksIHBvcm92bmHFpSBpY2ggdGVwbG90bsOpIHByb2ZpbHkgYSB6b3NrdXBpxaUgbWVzdMOhIHBvZMS+YSBwb2RvYm5vc3RpIHRlcGxvdG7DvWNoIHBvZG1pZW5vay4NCg0KIyMgTmHEjcOtdGFuaWUga25pxb5uw61jIGEgcHLDrXByYXZhIMO6ZGFqb3YNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShjbHVzdGVyKQ0KbGlicmFyeShmYWN0b2V4dHJhKQ0KDQojIE5hxI3DrXRhbmllIMO6ZGFqb3YNCmRmIDwtIHJlYWQuY3N2KCJ3ZWF0aGVyX3ByZWRpY3Rpb25fZGF0YXNldC5jc3YiKQ0KDQojIFRyYW5zZm9ybcOhY2lhIHN0xLpwY2EgREFURSBuYSBkw6F0dW0NCmRmJERBVEUgPC0gYXMuRGF0ZShhcy5jaGFyYWN0ZXIoZGYkREFURSksIGZvcm1hdCA9ICIlWSVtJWQiKQ0KDQojIFbDvWJlciDDumRham92IGxlbiB6YSByb2sgMjAwMA0KZGZfMjAwMCA8LSBkZiAlPiUNCiAgZmlsdGVyKGZvcm1hdChEQVRFLCAiJVkiKSA9PSAiMjAwMCIpDQoNCiMgVsO9YmVyIDkgbWllc3QNCnZ5YnJhdGVfbWVzdGEgPC0gYygnQkFTRUwnLCAnREVfQklMVCcsICdEUkVTREVOJywgJ0hFQVRIUk9XJywgDQogICAgICAgICAgICAgICAgICAgJ01VRU5DSEVOJywgJ09TTE8nLCAnUk9NQScsICdTVE9DS0hPTE0nLCAnVE9VUlMnKQ0KYGBgDQoNCiMjIFByaWVtZXIgemEgcm9rIHByZSBrYcW+ZMOpIG1lc3RvDQoNCk5hIHrDoWtsYWRlIMO6ZGFqb3YgemEgcm9rIDIwMDAgc21lIHZ5cG/EjcOtdGFsaSBwcmllbWVybsOpIGhvZG5vdHkgdGVwbG90bsO9Y2ggdWthem92YXRlxL5vdiBwcmUgamVkbm90bGl2w6kgbWVzdMOhLiANCg0KYGBge3J9DQojIFrDrXNrYW5pZSB2xaFldGvDvWNoIHRlcGxvdG7DvWNoIHN0xLpwY292IHByZSB2eWJyYW7DqSBtZXN0w6ENCnRlcGxvdG5lX3N0bHBjZSA8LSBjaGFyYWN0ZXIoKQ0KZm9yKG1lc3RvIGluIHZ5YnJhdGVfbWVzdGEpIHsNCiAgdGVwbG90bmVfc3RscGNlIDwtIGModGVwbG90bmVfc3RscGNlLCANCiAgICAgICAgICAgICAgICAgICAgICAgZ3JlcChwYXN0ZTAobWVzdG8sICJfdGVtcF8iKSwgbmFtZXMoZGZfMjAwMCksIHZhbHVlID0gVFJVRSkpDQp9DQoNCiMgVsO9YmVyIGliYSB0ZXBsb3Ruw71jaCDDumRham92DQpkZl90ZXBsb3R5IDwtIGRmXzIwMDBbLCBjKCJEQVRFIiwgIk1PTlRIIiwgdGVwbG90bmVfc3RscGNlKV0NCg0KIyBWw71wb8SNZXQgcHJpZW1lcm7DvWNoIGhvZG7DtHQgcHJlIGthxb5kw6kgbWVzdG8NCnByaWVtZXJuZV90ZXBsb3R5IDwtIGRmX3RlcGxvdHkgJT4lDQogIHN1bW1hcmlzZShhY3Jvc3MoY29udGFpbnMoIl90ZW1wX21lYW4iKSwgfiByb3VuZChtZWFuKC4sIG5hLnJtID0gVFJVRSksIDIpKSkgJT4lDQogIHBpdm90X2xvbmdlcihldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gIk1lc3RvIiwgdmFsdWVzX3RvID0gIlByaWVtZXJuYV90ZXBsb3RhIikgJT4lDQogIG11dGF0ZShNZXN0byA9IHN0cl9yZW1vdmUoTWVzdG8sICJfdGVtcF9tZWFuIikpICU+JQ0KICBhcnJhbmdlKGRlc2MoUHJpZW1lcm5hX3RlcGxvdGEpKQ0KDQptYXhpbWFsbmVfdGVwbG90eSA8LSBkZl90ZXBsb3R5ICU+JQ0KICBzdW1tYXJpc2UoYWNyb3NzKGNvbnRhaW5zKCJfdGVtcF9tYXgiKSwgfiByb3VuZChtZWFuKC4sIG5hLnJtID0gVFJVRSksIDIpKSkgJT4lDQogIHBpdm90X2xvbmdlcihldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gIk1lc3RvIiwgdmFsdWVzX3RvID0gIk1heGltYWxuYV90ZXBsb3RhIikgJT4lDQogIG11dGF0ZShNZXN0byA9IHN0cl9yZW1vdmUoTWVzdG8sICJfdGVtcF9tYXgiKSkNCg0KbWluaW1hbG5lX3RlcGxvdHkgPC0gZGZfdGVwbG90eSAlPiUNCiAgc3VtbWFyaXNlKGFjcm9zcyhjb250YWlucygiX3RlbXBfbWluIiksIH4gcm91bmQobWVhbiguLCBuYS5ybSA9IFRSVUUpLCAyKSkpICU+JQ0KICBwaXZvdF9sb25nZXIoZXZlcnl0aGluZygpLCBuYW1lc190byA9ICJNZXN0byIsIHZhbHVlc190byA9ICJNaW5pbWFsbmFfdGVwbG90YSIpICU+JQ0KICBtdXRhdGUoTWVzdG8gPSBzdHJfcmVtb3ZlKE1lc3RvLCAiX3RlbXBfbWluIikpDQoNCiMgU3BvamVuaWUgdsWhZXRrw71jaCDDumRham92DQpkYXRhX21lc3RhIDwtIHByaWVtZXJuZV90ZXBsb3R5ICU+JQ0KICBsZWZ0X2pvaW4obWF4aW1hbG5lX3RlcGxvdHksIGJ5ID0gIk1lc3RvIikgJT4lDQogIGxlZnRfam9pbihtaW5pbWFsbmVfdGVwbG90eSwgYnkgPSAiTWVzdG8iKQ0KDQpwcmludChkYXRhX21lc3RhKQ0KYGBgDQoNCkFuYWzDvXphIHByaWVtZXJvdiB1a2F6dWplLCDFvmUgKipSb21hKiogbcOhIG5hanZ5xaHFoWllIHByaWVtZXJuw6ksIG1heGltw6FsbmUgYWogbWluaW3DoWxuZSB0ZXBsb3R5LCB6YXRpYcS+IMSNbyAqKk9zbG8qKiBtw6EgbmFqbmnFvsWhaWUgaG9kbm90eSB2byB2xaFldGvDvWNoIHRyb2NoIGthdGVnw7NyacOhY2guIA0KDQojIyBLb3JlbGHEjW7DoSBtYXRpY2ENCg0KS29yZWxhxI1uw6EgbWF0aWNhIHZ5cG/EjcOtdGFuw6EgcHJlIHRlcGxvdG7DqSB1a2F6b3ZhdGVsZSB1a2F6dWplIHZ6xaVhaHkgbWVkemkgbmltaS4NCg0KYGBge3J9DQojIFbDvWJlciBpYmEgcHJpZW1lcm7DvWNoIHRlcGzDtHQgcHJlIGtvcmVsw6FjaXUNCnRlcGxvdG5lX2RhdGEgPC0gZGZfdGVwbG90eSAlPiUNCiAgc2VsZWN0KGNvbnRhaW5zKCdfdGVtcF9tZWFuJykpDQoNCiMgVsO9cG/EjWV0IGtvcmVsYcSNbmVqIG1hdGljZQ0KY29yX21hdHJpeCA8LSBjb3IodGVwbG90bmVfZGF0YSwgdXNlID0gImNvbXBsZXRlLm9icyIpDQpjb3JfbWF0cml4X3JvdW5kZWQgPC0gcm91bmQoY29yX21hdHJpeCwgMikNCg0KIyBQb21lbm92YW5pZSByaWFka292IGEgc3TEunBjb3YNCm1lc3RhX25henZ5IDwtIHN0cl9yZW1vdmUoY29sbmFtZXModGVwbG90bmVfZGF0YSksICJfdGVtcF9tZWFuIikNCnJvd25hbWVzKGNvcl9tYXRyaXhfcm91bmRlZCkgPC0gbWVzdGFfbmF6dnkNCmNvbG5hbWVzKGNvcl9tYXRyaXhfcm91bmRlZCkgPC0gbWVzdGFfbmF6dnkNCg0KcHJpbnQoY29yX21hdHJpeF9yb3VuZGVkKQ0KYGBgDQpaIG1hdGljZSB2eXBsw712YSwgxb5lIHRlcGxvdHkgbWVkemkgbWVzdGFtaSBzw7ogdnlzb2tvIGtsYWRuZSBrb3JlbG92YW7DqS4gTmFqdnnFocWhaWEga29yZWzDoWNpYSBqZSBtZWR6aSAqKk1VRU5DSEVOIGEgQkFTRUwqKiAociA9IDAuOTcpLCDEjW8gbmF6bmHEjXVqZSB2ZcS+bWkgcG9kb2Juw6kgdGVwbG90bsOpIHRyZW5keS4gTmFvcGFrLCBuYWpuacW+xaFpYSBrb3JlbMOhY2lhIGplIG1lZHppICoqUk9NQSBhIERFX0JJTFQqKiBhICoqUk9NQSBhIEhFQVRIUk9XKiogKHIgPSAwLjgxKSwgxI1vIG9kcsOhxb5hIHJvemRpZWxuZSBnZW9ncmFmaWNrw6kgcG9sb2h5Lg0KDQojIyBNYXRpY2EgdnpkaWFsZW5vc3RpIG1lZHppIG1lc3RhbWkNCg0KTWF0aWNhIHZ6ZGlhbGVub3N0w60gdWthenVqZSwgZG8gYWtlaiBtaWVyeSBzYSB0ZXBsb3Ruw6kgcHJvZmlseSBtaWVzdCBuYXZ6w6Fqb20gbMOtxaFpYS4NCg0KYGBge3J9DQojIFbDvXBvxI1ldCBldWtsaWRvdnNrZWogdnpkaWFsZW5vc3RpDQpkaXN0X21hdHJpeCA8LSBkaXN0KHQodGVwbG90bmVfZGF0YSksIG1ldGhvZCA9ICJldWNsaWRlYW4iKQ0KZGlzdF9tYXRyaXggPC0gYXMubWF0cml4KGRpc3RfbWF0cml4KQ0KZGlzdF9tYXRyaXhfcm91bmRlZCA8LSByb3VuZChkaXN0X21hdHJpeCwgMikNCg0Kcm93bmFtZXMoZGlzdF9tYXRyaXhfcm91bmRlZCkgPC0gbWVzdGFfbmF6dnkNCmNvbG5hbWVzKGRpc3RfbWF0cml4X3JvdW5kZWQpIDwtIG1lc3RhX25henZ5DQoNCnByaW50KGRpc3RfbWF0cml4X3JvdW5kZWQpDQpgYGANCk5hanbDpMSNxaFpYSB2emRpYWxlbm9zxaUgamUgbWVkemkgKipSb21vdSBhIE9zbG9tKiogKDE2OC43MSksIMSNbyBwb3R2cmR6dWplIHbDvXJhem7DqSB0ZXBsb3Ruw6kgcm96ZGllbHkgbWVkemkgdMO9bWl0byBtZXN0YW1pLiBOYW9wYWssIG5ham1lbsWhaWEgdnpkaWFsZW5vc8WlIGplIG1lZHppICoqSEVBVEhST1cgYSBUT1VSUyoqICg0MC40MiksIMSNbyBuYXpuYcSNdWplIHZlxL5taSBwb2RvYm7DqSB0ZXBsb3Ruw6kgcG9kbWllbmt5Lg0KDQojIyBIaWVyYXJjaGlja8OpIHpobHVrb3ZhbmllIChXYXJkb3ZhIG1ldMOzZGEpDQoNCk5hIHrDoWtsYWRlIGRlbmRyb2dyYW11IGhpZXJhcmNoaWNrw6lobyB6aGx1a292YW5pYSBtb8W+bm8gcG96b3JvdmHFpSBuYXNsZWR1asO6Y2U6DQoNCmBgYHtyfQ0KIyBaLcWha8OhbG92YW5pZSDDumRham92DQpkYXRhX3NjYWxlZCA8LSBzY2FsZSh0ZXBsb3RuZV9kYXRhKQ0KDQojIEhpZXJhcmNoaWNrw6kgemhsdWtvdmFuaWUNCmhjIDwtIGhjbHVzdChkaXN0KHQoZGF0YV9zY2FsZWQpLCBtZXRob2QgPSAiZXVjbGlkZWFuIiksIG1ldGhvZCA9ICJ3YXJkLkQyIikNCg0KIyBWaXp1YWxpesOhY2lhIGRlbmRyb2dyYW11DQpwbG90KGhjLCBsYWJlbHMgPSBtZXN0YV9uYXp2eSwgbWFpbiA9ICJIaWVyYXJjaGlja8OpIHpobHVrb3ZhbmllIHRlcGzDtHQgLSBXYXJkb3ZhIG1ldMOzZGEgKDIwMDApIiwgY2V4ID0gMC44KQ0KcmVjdC5oY2x1c3QoaGMsIGsgPSA0LCBib3JkZXIgPSAicmVkIikNCmBgYA0KDQojIyBQcsOtc2x1xaFub3PFpSBtaWVzdCBkbyBrbGFzdHJvdg0KYGBge3J9DQpjbHVzdGVycyA8LSBjdXRyZWUoaGMsIGsgPSA0KQ0KY2l0eV9kYXRhJGNsdXN0ZXIgPC0gYXMuZmFjdG9yKGNsdXN0ZXJzKQ0KDQpwcmludChjaXR5X2RhdGFbLCBjKCJNZXN0byIsICJjbHVzdGVyIildKQ0KYGBgDQpNZXN0w6Egc2Egcm96ZGXEvnVqw7ogZG8gNCBobGF2bsO9Y2gga2xhc3Ryb3Y6DQoNCi0gKipLbGFzdGVyIDEqKjogQkFTRUwsIERSRVNERU4sIE1VRU5DSEVOIC0gc3RyZWRvZXVyw7Nwc2tlIHZuw7p0cm96ZW1za8OpIG1lc3TDoSBzIGtvbnRpbmVudMOhbG55bSB2cGx5dm9tDQoNCi0gKipLbGFzdGVyIDIqKjogREVfQklMVCwgSEVBVEhST1csIFRPVVJTIC0gesOhcGFkb2V1csOzcHNrZSBtZXN0w6EgcyBvY2XDoW5za3ltIGEgcHLDrW1vcnNrw71tIHZwbHl2b20NCg0KLSAqKktsYXN0ZXIgMyoqOiBPU0xPLCBTVE9DS0hPTE0gLSBzZXZlcnNrw6kgbWVzdMOhIHMgY2hsYWRuZWrFocOtbSBwb2RuZWLDrW0NCg0KLSAqKktsYXN0ZXIgNCoqOiBST01BIC0gamVkaW7DqSBzdHJlZG9tb3Jza8OpIG1lc3RvIHMgdsO9cmF6bmUgdGVwbGVqxaHDrW0gcG9kbmViw61tDQoNCiMjIERlc2tyaXB0w612bmUgxaF0YXRpc3Rpa3kgdsO9c2xlZGtvdg0KDQojIyMgVm7DunRyby0gYSBtZWR6aWtsYXN0cm92w6EgdmFyaWFiaWxpdGENCg0KYGBge3J9DQpkYXRhX21hdHJpeCA8LSBhcy5tYXRyaXgodGVwbG90bmVfZGF0YSkNCg0KIyBDZWxrb3bDoSB2YXJpYWJpbGl0YSAoVFNTKQ0KY2Vsa292YV9zdHJlZG5hX2hvZG5vdGEgPC0gbWVhbihkYXRhX21hdHJpeCkNCnRzcyA8LSBzdW0oKGRhdGFfbWF0cml4IC0gY2Vsa292YV9zdHJlZG5hX2hvZG5vdGEpXjIpDQoNCiMgVm7DunRyb2tsYXN0cm92w6EgdmFyaWFiaWxpdGEgKFdTUykNCndzcyA8LSAwDQpmb3IoayBpbiAxOjQpIHsNCiAgY2x1c3Rlcl9pbmRpY2VzIDwtIHdoaWNoKGNsdXN0ZXJzID09IGspDQogIGlmKGxlbmd0aChjbHVzdGVyX2luZGljZXMpID4gMCkgew0KICAgIGNsdXN0ZXJfZGF0YSA8LSBkYXRhX21hdHJpeFssIGNsdXN0ZXJfaW5kaWNlcywgZHJvcCA9IEZBTFNFXQ0KICAgIGNsdXN0ZXJfc3RyZWRuYV9ob2Rub3RhIDwtIG1lYW4oY2x1c3Rlcl9kYXRhKQ0KICAgIHdzcyA8LSB3c3MgKyBzdW0oKGNsdXN0ZXJfZGF0YSAtIGNsdXN0ZXJfc3RyZWRuYV9ob2Rub3RhKV4yKQ0KICB9DQp9DQoNCmJzcyA8LSB0c3MgLSB3c3MNCnByb3BfYmV0d2VlbiA8LSBic3MgLyB0c3MNCg0KdmFyaWFiaWxpdGEgPC0gZGF0YS5mcmFtZSgNCiAgQ2Vsa292YV92YXJpYW5jaWEgPSByb3VuZCh0c3MsIDIpLA0KICBWbnV0cm9rbGFzdHJvdsOhX3ZhcmlhbmNpYSA9IHJvdW5kKHdzcywgMiksDQogIE1lZHppa2xhc3Ryb3bDoV92YXJpYW5jaWEgPSByb3VuZChic3MsIDIpLA0KICBQb2RpZWxfbWVkemkgPSByb3VuZChwcm9wX2JldHdlZW4sIDMpDQopDQoNCnByaW50KHZhcmlhYmlsaXRhKQ0KYGBgDQpOYSB6w6FrbGFkZSB2w71zbGVka292IG3DtMW+ZW1lIGtvbsWhdGF0b3ZhxaUsIMW+ZSBjZWxrb3bDoSB2YXJpYWJpbGl0YSB0ZXBsb3Ruw71jaCDDumRham92IGplIDE0OTA0Ni40LiBaIHRlanRvIGNlbGtvdmVqIHZhcmlhYmlsaXR5IGplIDE0ODIxLjI5ICgqKjkuOSUqKikgdnlzdmV0bGVuw71jaCByb3pkZWxlbsOtbSBtaWVzdCBkbyBrbGFzdHJvdiwgemF0aWHEvsSNbyAxMzQyMjUuMSAoKio5MC4xJSoqKSB2YXJpYWJpbGl0eSB6b3N0w6F2YSB2IHLDoW1jaSBqZWRub3RsaXbDvWNoIGtsYXN0cm92Lg0KDQpQb2RpZWwgbWVkemlrbGFzdHJvdmVqIHZhcmlhYmlsaXR5IDAuMDk5IG5hem5hxI11amUsIMW+ZSB6aGx1a292YW5pZSB2eXN2ZXTEvnVqZSBwcmlibGnFvm5lIDEwJSBjZWxrb3ZlaiB2YXJpYWJpbGl0eSDDumRham92LiBUw6F0byBob2Rub3RhIGplIHR5cGlja8OhIHByZSBrbGltYXRvbG9naWNrw6kgw7pkYWplLCBrZGUgbWVzdMOhIHYgcsOhbWNpIHRlaiBpc3RlaiBnZW9ncmFmaWNrZWogb2JsYXN0aSBtYWrDuiBwb2RvYm7DqSB0ZXBsb3Ruw6kgdHJlbmR5LCBubyBzdMOhbGUgZXhpc3R1asO6IHbDvXJhem7DqSByb3pkaWVseSBzcMO0c29iZW7DqSBsb2vDoWxueW1pIHZwbHl2bWksIG5hZG1vcnNrb3UgdsO9xaFrb3UgYSBpbsO9bWkgZmFrdG9ybWkuDQoNClJlbGF0w612bmUgbsOtemthIG1lZHppa2xhc3Ryb3bDoSB2YXJpYWJpbGl0YSBwb3R2cmR6dWplLCDFvmUgZXVyw7Nwc2tlIG1lc3TDoSBtYWrDuiBkbyB1csSNaXRlaiBtaWVyeSBwb2RvYm7DqSB0ZXBsb3Ruw6kgcHJvZmlseSwgbm8gaWRlbnRpZmlrb3ZhbsOpIGtsYXN0cmUgbmFwcmllayB0b211IHphY2h5dMOhdmFqw7ogdsO9em5hbW7DqSByZWdpb27DoWxuZSByb3pkaWVseSB2IHRlcGxvdG7DvWNoIHBvbWVyb2NoLg0KDQojIyMgQ2VudHJvaWR5IGtsYXN0cm92DQpgYGB7cn0NCmNsdXN0ZXJzIDwtIGN1dHJlZShoYywgayA9IDQpDQoNCmZpbmFsX2RhdGEgPC0gZGF0YS5mcmFtZSgNCiAgTWVzdG8gPSBjKCJCQVNFTCIsICJERV9CSUxUIiwgIkRSRVNERU4iLCAiSEVBVEhST1ciLCAiTVVFTkNIRU4iLCAiT1NMTyIsICJST01BIiwgIlNUT0NLSE9MTSIsICJUT1VSUyIpLA0KICBLbGFzdGVyID0gYXMuZmFjdG9yKGNsdXN0ZXJzKQ0KKSAlPiUNCiAgbGVmdF9qb2luKGRhdGFfbWVzdGEsIGJ5ID0gIk1lc3RvIikNCg0KY2VudHJvaWRzIDwtIGZpbmFsX2RhdGEgJT4lDQogIGdyb3VwX2J5KEtsYXN0ZXIpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgUG9jZXRfbWllc3QgPSBuKCksDQogICAgUHJpZW1lcm5hX3RlcGxvdGEgPSByb3VuZChtZWFuKFByaWVtZXJuYV90ZXBsb3RhKSwgMiksDQogICAgTWF4aW1hbG5hX3RlcGxvdGEgPSByb3VuZChtZWFuKE1heGltYWxuYV90ZXBsb3RhKSwgMiksDQogICAgTWluaW1hbG5hX3RlcGxvdGEgPSByb3VuZChtZWFuKE1pbmltYWxuYV90ZXBsb3RhKSwgMiksDQogICAgLmdyb3VwcyA9ICdkcm9wJw0KICApDQoNCnByaW50KGNlbnRyb2lkcykNCmBgYA0KTmEgesOha2xhZGUgY2VudHJvaWRvdiBrbGFzdHJvdiBtw7TFvmVtZSBwb3pvcm92YcWlIGNoYXJha3RlcmlzdGlreSBqZWRub3RsaXbDvWNoIGtsYXN0cm92Og0KDQotICoqS2xhc3RlciAxKiogKDMgbWVzdMOhKTogUHJpZW1lcm7DoSB0ZXBsb3RhIDEwLjk4wrBDLCBtYXhpbcOhbG5hIDE1LjIywrBDLCBtaW5pbcOhbG5hIDcuMTfCsEMgLSBzdHJlZG9ldXLDs3Bza2Ugdm7DunRyb3plbXNrw6kgbWVzdMOhIHNvIHN0cmVkbsO9bWkgdGVwbG90YW1pDQoNCi0gKipLbGFzdGVyIDIqKiAoMyBtZXN0w6EpOiBQcmllbWVybsOhIHRlcGxvdGEgMTEuNjDCsEMsIG1heGltw6FsbmEgMTUuNDLCsEMsIG1pbmltw6FsbmEgNy43MsKwQyAtIHrDoXBhZG9ldXLDs3Bza2UgbWVzdMOhIHMgbWllcm5lIHZ5xaHFocOtbWkgdGVwbG90YW1pIHbEj2FrYSBvY2XDoW5za2VtdSB2cGx5dnUNCg0KLSAqKktsYXN0ZXIgMyoqICgyIG1lc3TDoSk6IFByaWVtZXJuw6EgdGVwbG90YSA4LjE1wrBDLCBtYXhpbcOhbG5hIDExLjQ4wrBDLCBtaW5pbcOhbG5hIDUuMjTCsEMgLSBzZXZlcnNrw6kgbWVzdMOhIHMgdsO9cmF6bmUgbmnFvsWhw61taSB0ZXBsb3RhbWkNCg0KLSAqKktsYXN0ZXIgNCoqICgxIG1lc3RvKTogUHJpZW1lcm7DoSB0ZXBsb3RhIDE1LjgywrBDLCBtYXhpbcOhbG5hIDIxLjkwwrBDLCBtaW5pbcOhbG5hIDExLjQ3wrBDIC0gc3RyZWRvbW9yc2vDqSBtZXN0byBzIG5hanZ5xaHFocOtbWkgdGVwbG90YW1pDQoNClJvemRpZWx5IG1lZHppIGtsYXN0cmFtaSBzw7ogdsO9cmF6bsOpIC0gbWVkemkgbmFqdGVwbGVqxaHDrW0gYSBuYWpjaGxhZG5lasWhw61tIGtsYXN0cm9tIGplIHJvemRpZWwgNy42N8KwQyB2IHByaWVtZXJuZWogdGVwbG90ZS4gS2xhc3RyZSBkb2JyZSBvZHLDocW+YWrDuiBnZW9ncmFmaWNrw7ogcG9sb2h1IGEga2xpbWF0aWNrw6kgcG9tZXJ5IGplZG5vdGxpdsO9Y2ggbWllc3QuDQoNCiMjIFrDoXZlcg0KDQpOYSB6w6FrbGFkZSB2eWtvbmFuZWogYW5hbMO9enkgdGVwbG90bsO9Y2ggw7pkYWpvdiAqKjkqKiB2eWJyYW7DvWNoIGV1csOzcHNreWNoIG1pZXN0IHphIHJvayAqKjIwMDAqKiBtb8W+bm8ga29uxaF0YXRvdmHFpSwgxb5lIGlkZW50aWZpa292YW7DqSBrbGFzdHJlIGRvYnJlIG9kcsOhxb5hasO6IGdlb2dyYWZpY2vDuiBhIGtsaW1hdGlja8O6IHJvem1hbml0b3PFpSBFdXLDs3B5LiANCg0KWmhsdWtvdsOhIGFuYWzDvXphIMO6c3BlxaFuZSByb3pkZWxpbGEgbWVzdMOhIGRvICoqNCoqIGhvbW9nw6lubnljaCBza3Vww61uIHBvZMS+YSBpY2ggdGVwbG90bsO9Y2ggY2hhcmFrdGVyaXN0w61rLiBOYWp2w71yYXpuZWrFoWllIHJvemRpZWx5IHPDuiBtZWR6aSBzdHJlZG9tb3Jza8O9bSBrbGltYXRvbSAoUm9tYSkgYSBzZXZlcnNrw71taSBvYmxhc8WlYW1pIChPc2xvLCBTdG9ja2hvbG0pLCBwcmnEjW9tIHJvemRpZWwgdiBwcmllbWVybsO9Y2ggdGVwbG90w6FjaCBkb3NhaHVqZSB0YWttZXIgKio4wrBDKiouIFN0cmVkb2V1csOzcHNrZSBhIHrDoXBhZG9ldXLDs3Bza2UgbWVzdMOhIHR2b3JpYSBkdmUgc2Ftb3N0YXRuw6kgc2t1cGlueSBzbyBwb2RvYm7DvW1pLCBubyBtaWVybmUgb2RsacWhbsO9bWkgdGVwbG90bsO9bWkgcHJvZmlsbWkuDQoNCkhvZG5vdGEgbWVkemlrbGFzdHJvdmVqIHZhcmlhYmlsaXR5ICoqOS45JSoqIG5hem5hxI11amUsIMW+ZSBuYXByaWVrIHVyxI1pdGVqIHBvZG9ibm9zdGkgdGVwbG90bsO9Y2ggdHJlbmRvdiBtZWR6aSBldXLDs3Bza3ltaSBtZXN0YW1pLCBpZGVudGlmaWtvdmFuw6kga2xhc3RyZSB6YWNoeXTDoXZhasO6IHbDvXpuYW1uw6kgcmVnaW9uw6FsbmUgcm96ZGllbHkuIFRpZXRvIHbDvXNsZWRreSBwb3NreXR1asO6IGNlbm7DvSB2aMS+YWQgZG8ga2xpbWF0aWNrw71jaCBwb21lcm92IEV1csOzcHkgYSBtw7TFvnUgc2zDusW+acWlIGFrbyBwb2RrbGFkIHByZSDEj2FsxaFpZSBrbGltYXRvbG9naWNrw6kgxaF0w7pkaWUgYWxlYm8gcmVnaW9uw6FsbmUgcGzDoW5vdmFuaWUuDQoNCg==