knitr::opts_chunk$set(
    echo = TRUE,
    message = FALSE,
    warning = FALSE
)

Moja vybraná databáza

Pre spracovanie som si vybrala databázu European Cities Weather Prediction Dataset z Kaggle.

Databáza obsahuje historické údaje o počasí z európskych miest. Každý záznam obsahuje dátum merania a rôzne meteorologické parametre. Údaje sú zoradené chronologicky podľa dátumu, čo umožňuje sledovať zmeny teploty v priebehu času.

Táto databáza je vhodná na vizualizáciu, tvorbu grafov a výpočet základných štatistík, ktoré ukazujú vývoj teploty v európskych mestách. Plánujem ju využiť na ďalšie spracovanie a analýzu trendov teploty v Európských mestach.

# Načítanie knižníc
library(tidyverse)
library(lubridate)
library(corrplot)
library(ggplot2)
library(kableExtra)
library(htmltools)

Načítame databázu

udaje <- read_csv("weather_prediction_dataset.csv")

Vyberieme len potrebné stĺpce

udaje_vybrane <- udaje %>%
  select(DATE, BASEL_temp_mean, BUDAPEST_temp_mean, DUSSELDORF_temp_mean)

Zobrazenie prvých 10 riadkov

# Prevod čísla YYYYMMDD na Date
udaje_vybrane$DATE <- as.Date(as.character(udaje_vybrane$DATE), format="%Y%m%d")
head(udaje_vybrane$DATE)
[1] "2000-01-01" "2000-01-02" "2000-01-03" "2000-01-04" "2000-01-05" "2000-01-06"
head(udaje_vybrane, 10) %>%
  kable(
    caption = "Ukážka prvých 10 riadkov vybraných teplôt európskych miest",
    col.names = c("Dátum", "Basel (°C)", "Budapešť (°C)", "Düsseldorf (°C)")
  ) %>%
  kable_styling(
    full_width = FALSE,
    bootstrap_options = c("striped", "hover", "condensed")
  )
Ukážka prvých 10 riadkov vybraných teplôt európskych miest
Dátum Basel (°C) Budapešť (°C) Düsseldorf (°C)
2000-01-01 2.9 -4.9 4.2
2000-01-02 3.6 -3.6 6.5
2000-01-03 2.2 -0.8 7.7
2000-01-04 3.9 -1.0 7.8
2000-01-05 6.0 0.2 5.2
2000-01-06 4.2 -0.9 7.6
2000-01-07 4.7 -2.8 6.6
2000-01-08 5.6 -1.2 6.6
2000-01-09 4.6 -1.4 2.5
2000-01-10 2.4 0.0 1.1

Výpočet základných štatistík

Nižšie ukážem základné štatistické charakteristiky priemernej teploty pre tri vybrané mestá – Basel, Budapešť a Düsseldorf vo forme troch tabuliek umiestnených vedľa seba.

# Štatistika Basel
s_basel <- udaje_vybrane %>%
summarise(
   Priemer = mean(BASEL_temp_mean, na.rm = TRUE),
   Minimum = min(BASEL_temp_mean, na.rm = TRUE),
   Maximum = max(BASEL_temp_mean, na.rm = TRUE)
)
 
# ️Štatistika Budapešť
s_budapest <- udaje_vybrane %>%
summarise(
   Priemer = mean(BUDAPEST_temp_mean, na.rm = TRUE),
   Minimum = min(BUDAPEST_temp_mean, na.rm = TRUE),
   Maximum = max(BUDAPEST_temp_mean, na.rm = TRUE)
)
# Štatistika Düsseldorf
s_dusseldorf <- udaje_vybrane %>%
summarise(
   Priemer = mean(DUSSELDORF_temp_mean, na.rm = TRUE),
   Minimum = min(DUSSELDORF_temp_mean, na.rm = TRUE),
   Maximum = max(DUSSELDORF_temp_mean, na.rm = TRUE)
)
 
b_tab <- s_basel %>%
kable(caption = "Základné štatistiky - Basel") %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover")) %>%
as.character() 
bp_tab <- s_budapest %>%
kable(caption = "Základné štatistiky - Budapešť") %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover")) %>%
as.character()
d_tab <- s_dusseldorf %>%
kable(caption = "Základné štatistiky - Düsseldorf") %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover")) %>%
as.character()
 
browsable(
tags$div(style="display:flex; gap:20px; align-items:flex-start;",
          HTML(b_tab),
          HTML(bp_tab),
          HTML(d_tab)
)
)
Základné štatistiky - Basel
Priemer Minimum Maximum
11.0228 -9.3 29
Základné štatistiky - Budapešť
Priemer Minimum Maximum
12.17485 -9.8 33.1
Základné štatistiky - Düsseldorf
Priemer Minimum Maximum
11.14201 -11.1 29.2

Vývoj priemernej teploty v troch mestách


udaje_long <- udaje_vybrane %>%
  pivot_longer(
    cols = c(BASEL_temp_mean, BUDAPEST_temp_mean, DUSSELDORF_temp_mean),
    names_to = "Mesto",
    values_to = "Priemerna_teplota"
  )

udaje_long$Mesto <- recode(udaje_long$Mesto,
                           "BASEL_temp_mean" = "Basel",
                           "BUDAPEST_temp_mean" = "Budapešť",
                           "DUSSELDORF_temp_mean" = "Düsseldorf")

ggplot(udaje_long, aes(x = DATE, y = Priemerna_teplota, color = Mesto)) +
  geom_line(size = 1) +
  theme_minimal() +
  labs(
    title = "Vývoj priemernej teploty v troch mestách",
    x = "Dátum",
    y = "Priemerná teplota (°C)",
    color = "Mesto"
  ) +
scale_color_manual(values = c("Basel" = "blue", "Budapešť" = "red", "Düsseldorf" = "yellow"))

Tu zobrazený graf vývoja priemernej dennej teploty v troch vybraných mestách – Basel, Budapešť a Düsseldorf – za obdobie, pre ktoré sú k dispozícii merania v databáze.

Každé mesto je reprezentované samostatnou farebnou čiarou, čo umožňuje prehľadné porovnanie teplotných trendov a zmien v čase.

Priemerná a extrémna denná teplota v Düsseldorfe

dusseldorf <- udaje %>%
  select(DATE, DUSSELDORF_temp_min, DUSSELDORF_temp_mean, DUSSELDORF_temp_max) %>%
  mutate(DATE = as.Date(as.character(DATE), format="%Y%m%d"))

dusseldorf_points <- dusseldorf %>%
  filter(row_number() %% 30 == 1)

ggplot(dusseldorf, aes(x = DATE)) +
  geom_line(aes(y = DUSSELDORF_temp_mean), color="lightgreen", size=1) +
  geom_point(data = dusseldorf_points, aes(y = DUSSELDORF_temp_min), color="darkgreen", size=1.5) +
  geom_point(data = dusseldorf_points, aes(y = DUSSELDORF_temp_max), color="red", size=1.5) +
  theme_minimal() +
  labs(
    title = "Priemerná a extrémna denná teplota v Düsseldorfe",
    x = "Dátum",
    y = "Teplota (°C)"
  )

Tento graf zobrazuje priemernú dennú teplotu v Düsseldorfe spolu s minimálnymi a maximálnymi hodnotami.

Svetlozelená čiara ukazuje priemernú teplotu a body označujú extrémy iba raz za 30 dní.

Takáto vizualizácia umožňuje prehľadnejšie sledovanie trendov a variabilitu teploty v čase bez zahltenia grafu príliš veľkým množstvom bodov.

Korelačná matica - Budapešť

budapest_data <- udaje %>%
  select(BUDAPEST_temp_max, BUDAPEST_humidity, BUDAPEST_pressure, 
         BUDAPEST_sunshine, BUDAPEST_global_radiation, BUDAPEST_precipitation, 
         )

cor_matrix_budapest <- cor(budapest_data, use = "complete.obs")

library(corrplot)
corrplot(cor_matrix_budapest, method = "color", type = "upper", 
         tl.col = "black", tl.srt = 45, addCoef.col = "black",
         title = "Korelačná matica - Budapešť", mar = c(0,0,2,0))

Korelačná matica zobrazuje vzťahy medzi vybranými meteorologickými premennými v meste Budapešť: maximálnou teplotou vzduchu, vlhkosťou, tlakom, slnečným svitom, globálnou radiáciou a množstvom zrážok. Medzi jednotlivými premennými možno pozorovať niekoľko výrazných korelácií:

Maximálna teplota (BUDAPEST_temp_max) má silnú pozitívnu koreláciu so slnečným svitom (r = 0.65) a globálnou radiáciou (r = 0.79). To znamená, že s rastúcim množstvom slnečného žiarenia a dĺžkou slnečného svitu rastie aj teplota.

Medzi teplotou a vlhkosťou sa nachádza negatívna korelácia (r = -0.55), čo naznačuje, že počas vlhkých dní bývajú teploty spravidla nižšie.

Tlak vzduchu vykazuje len slabé vzťahy s ostatnými premennými (hodnoty okolo 0.1–0.2), čo znamená, že tlak má na dennú teplotu a ostatné ukazovatele iba malý vplyv.

Slnečný svit a globálna radiácia sú medzi sebou veľmi silno pozitívne korelované (r = 0.89), čo je očakávané, keďže oba faktory súvisia s intenzitou slnečného žiarenia.

Zrážky (BUDAPEST_precipitation) majú slabé až mierne negatívne korelácie s väčšinou ostatných ukazovateľov (napr. s teplotou r = -0.01, s radiáciou r = -0.16), čo potvrdzuje, že počas daždivých dní býva menej slnka a teploty sú nižšie.

Celkovo matica potvrdzuje očakávané vzťahy medzi meteorologickými ukazovateľmi – vyššie teploty sú spojené s vyššou intenzitou slnečného žiarenia, zatiaľ čo zvýšená vlhkosť a zrážky prispievajú k ich poklesu.

LS0tCnRpdGxlOiAiw5psb2hhXzMtNCIKYXV0aG9yOiAiQmMuIEtyeXN0eW5hIFZhc3lseW5hIgpkYXRlOiAiT2t0b2JlciAyMDI1IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0aGVtZTogdW5pdGVkCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgZGZfcHJpbnQ6IHBhZ2VkCmVkaXRvcl9vcHRpb25zOgogIG1hcmtkb3duOgogICAgd3JhcDogNzIKLS0tCmBgYHtyfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgICBlY2hvID0gVFJVRSwKICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgIHdhcm5pbmcgPSBGQUxTRQopCmBgYAoKIyMgTW9qYSB2eWJyYW7DoSBkYXRhYsOhemEKClByZSBzcHJhY292YW5pZSBzb20gc2kgdnlicmFsYSBkYXRhYsOhenUgCltFdXJvcGVhbiBDaXRpZXMgV2VhdGhlciBQcmVkaWN0aW9uIERhdGFzZXRdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvb3J2aWxlL2V1cm9wZWFuLWNpdGllcy13ZWF0aGVyLXByZWRpY3Rpb24tZGF0YXNldC9kYXRhKSB6IEthZ2dsZS4gCgpEYXRhYsOhemEgb2JzYWh1amUgaGlzdG9yaWNrw6kgw7pkYWplIG8gcG/EjWFzw60geiBldXLDs3Bza3ljaCBtaWVzdC4gCkthxb5kw70gesOhem5hbSBvYnNhaHVqZSBkw6F0dW0gbWVyYW5pYSBhIHLDtHpuZSBtZXRlb3JvbG9naWNrw6kgcGFyYW1ldHJlLgrDmmRhamUgc8O6IHpvcmFkZW7DqSBjaHJvbm9sb2dpY2t5IHBvZMS+YSBkw6F0dW11LCDEjW8gdW1vxb7FiHVqZSBzbGVkb3ZhxaUgem1lbnkgdGVwbG90eSB2IHByaWViZWh1IMSNYXN1LgoKVMOhdG8gZGF0YWLDoXphIGplIHZob2Ruw6EgbmEgdml6dWFsaXrDoWNpdSwgdHZvcmJ1IGdyYWZvdiBhIHbDvXBvxI1ldCB6w6FrbGFkbsO9Y2ggxaF0YXRpc3TDrWssIGt0b3LDqSB1a2F6dWrDuiB2w712b2ogdGVwbG90eSB2IGV1csOzcHNreWNoIG1lc3TDoWNoLiAKUGzDoW51amVtIGp1IHZ5dcW+acWlIG5hIMSPYWzFoWllIHNwcmFjb3ZhbmllIGEgYW5hbMO9enUgdHJlbmRvdiB0ZXBsb3R5IHYgRXVyw7Nwc2vDvWNoIG1lc3RhY2guCgpgYGB7cn0KIyBOYcSNw610YW5pZSBrbmnFvm7DrWMKCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShjb3JycGxvdCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkoaHRtbHRvb2xzKQpgYGAKCiMjIE5hxI3DrXRhbWUgZGF0YWLDoXp1CgpgYGB7cn0KdWRhamUgPC0gcmVhZF9jc3YoIndlYXRoZXJfcHJlZGljdGlvbl9kYXRhc2V0LmNzdiIpCmBgYAoKIyMjIFZ5YmVyaWVtZSBsZW4gcG90cmVibsOpIHN0xLpwY2UKCmBgYHtyfQp1ZGFqZV92eWJyYW5lIDwtIHVkYWplICU+JQogIHNlbGVjdChEQVRFLCBCQVNFTF90ZW1wX21lYW4sIEJVREFQRVNUX3RlbXBfbWVhbiwgRFVTU0VMRE9SRl90ZW1wX21lYW4pCmBgYAoKIyMjIFpvYnJhemVuaWUgcHJ2w71jaCAxMCByaWFka292CgpgYGB7cn0KIyBQcmV2b2QgxI3DrXNsYSBZWVlZTU1ERCBuYSBEYXRlCnVkYWplX3Z5YnJhbmUkREFURSA8LSBhcy5EYXRlKGFzLmNoYXJhY3Rlcih1ZGFqZV92eWJyYW5lJERBVEUpLCBmb3JtYXQ9IiVZJW0lZCIpCmhlYWQodWRhamVfdnlicmFuZSREQVRFKQpoZWFkKHVkYWplX3Z5YnJhbmUsIDEwKSAlPiUKICBrYWJsZSgKICAgIGNhcHRpb24gPSAiVWvDocW+a2EgcHJ2w71jaCAxMCByaWFka292IHZ5YnJhbsO9Y2ggdGVwbMO0dCBldXLDs3Bza3ljaCBtaWVzdCIsCiAgICBjb2wubmFtZXMgPSBjKCJEw6F0dW0iLCAiQmFzZWwgKMKwQykiLCAiQnVkYXBlxaHFpSAowrBDKSIsICJEw7xzc2VsZG9yZiAowrBDKSIpCiAgKSAlPiUKICBrYWJsZV9zdHlsaW5nKAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpCiAgKQpgYGAKCiMjIFbDvXBvxI1ldCB6w6FrbGFkbsO9Y2ggxaF0YXRpc3TDrWsKCk5pxb7FoWllIHVrw6HFvmVtIHrDoWtsYWRuw6kgxaF0YXRpc3RpY2vDqSBjaGFyYWt0ZXJpc3Rpa3kgcHJpZW1lcm5laiB0ZXBsb3R5IHByZSB0cmkgdnlicmFuw6kgbWVzdMOhIOKAkyBCYXNlbCwgQnVkYXBlxaHFpSBhIETDvHNzZWxkb3JmIHZvIGZvcm1lIHRyb2NoIHRhYnVsaWVrIHVtaWVzdG5lbsO9Y2ggdmVkxL5hIHNlYmEuCgpgYGB7cn0KIyDFoHRhdGlzdGlrYSBCYXNlbApzX2Jhc2VsIDwtIHVkYWplX3Z5YnJhbmUgJT4lCnN1bW1hcmlzZSgKICAgUHJpZW1lciA9IG1lYW4oQkFTRUxfdGVtcF9tZWFuLCBuYS5ybSA9IFRSVUUpLAogICBNaW5pbXVtID0gbWluKEJBU0VMX3RlbXBfbWVhbiwgbmEucm0gPSBUUlVFKSwKICAgTWF4aW11bSA9IG1heChCQVNFTF90ZW1wX21lYW4sIG5hLnJtID0gVFJVRSkKKQogCiMg77iPxaB0YXRpc3Rpa2EgQnVkYXBlxaHFpQpzX2J1ZGFwZXN0IDwtIHVkYWplX3Z5YnJhbmUgJT4lCnN1bW1hcmlzZSgKICAgUHJpZW1lciA9IG1lYW4oQlVEQVBFU1RfdGVtcF9tZWFuLCBuYS5ybSA9IFRSVUUpLAogICBNaW5pbXVtID0gbWluKEJVREFQRVNUX3RlbXBfbWVhbiwgbmEucm0gPSBUUlVFKSwKICAgTWF4aW11bSA9IG1heChCVURBUEVTVF90ZW1wX21lYW4sIG5hLnJtID0gVFJVRSkKKQojIMWgdGF0aXN0aWthIETDvHNzZWxkb3JmCnNfZHVzc2VsZG9yZiA8LSB1ZGFqZV92eWJyYW5lICU+JQpzdW1tYXJpc2UoCiAgIFByaWVtZXIgPSBtZWFuKERVU1NFTERPUkZfdGVtcF9tZWFuLCBuYS5ybSA9IFRSVUUpLAogICBNaW5pbXVtID0gbWluKERVU1NFTERPUkZfdGVtcF9tZWFuLCBuYS5ybSA9IFRSVUUpLAogICBNYXhpbXVtID0gbWF4KERVU1NFTERPUkZfdGVtcF9tZWFuLCBuYS5ybSA9IFRSVUUpCikKIApiX3RhYiA8LSBzX2Jhc2VsICU+JQprYWJsZShjYXB0aW9uID0gIlrDoWtsYWRuw6kgxaF0YXRpc3Rpa3kgLSBCYXNlbCIpICU+JQprYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpICU+JQphcy5jaGFyYWN0ZXIoKSAKYnBfdGFiIDwtIHNfYnVkYXBlc3QgJT4lCmthYmxlKGNhcHRpb24gPSAiWsOha2xhZG7DqSDFoXRhdGlzdGlreSAtIEJ1ZGFwZcWhxaUiKSAlPiUKa2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKSAlPiUKYXMuY2hhcmFjdGVyKCkKZF90YWIgPC0gc19kdXNzZWxkb3JmICU+JQprYWJsZShjYXB0aW9uID0gIlrDoWtsYWRuw6kgxaF0YXRpc3Rpa3kgLSBEw7xzc2VsZG9yZiIpICU+JQprYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpICU+JQphcy5jaGFyYWN0ZXIoKQogCmJyb3dzYWJsZSgKdGFncyRkaXYoc3R5bGU9ImRpc3BsYXk6ZmxleDsgZ2FwOjIwcHg7IGFsaWduLWl0ZW1zOmZsZXgtc3RhcnQ7IiwKICAgICAgICAgIEhUTUwoYl90YWIpLAogICAgICAgICAgSFRNTChicF90YWIpLAogICAgICAgICAgSFRNTChkX3RhYikKKQopCmBgYAoKIyMgVsO9dm9qIHByaWVtZXJuZWogdGVwbG90eSB2IHRyb2NoIG1lc3TDoWNoCgpgYGB7cn0KCnVkYWplX2xvbmcgPC0gdWRhamVfdnlicmFuZSAlPiUKICBwaXZvdF9sb25nZXIoCiAgICBjb2xzID0gYyhCQVNFTF90ZW1wX21lYW4sIEJVREFQRVNUX3RlbXBfbWVhbiwgRFVTU0VMRE9SRl90ZW1wX21lYW4pLAogICAgbmFtZXNfdG8gPSAiTWVzdG8iLAogICAgdmFsdWVzX3RvID0gIlByaWVtZXJuYV90ZXBsb3RhIgogICkKCnVkYWplX2xvbmckTWVzdG8gPC0gcmVjb2RlKHVkYWplX2xvbmckTWVzdG8sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJCQVNFTF90ZW1wX21lYW4iID0gIkJhc2VsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJVREFQRVNUX3RlbXBfbWVhbiIgPSAiQnVkYXBlxaHFpSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJEVVNTRUxET1JGX3RlbXBfbWVhbiIgPSAiRMO8c3NlbGRvcmYiKQoKZ2dwbG90KHVkYWplX2xvbmcsIGFlcyh4ID0gREFURSwgeSA9IFByaWVtZXJuYV90ZXBsb3RhLCBjb2xvciA9IE1lc3RvKSkgKwogIGdlb21fbGluZShzaXplID0gMSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicygKICAgIHRpdGxlID0gIlbDvXZvaiBwcmllbWVybmVqIHRlcGxvdHkgdiB0cm9jaCBtZXN0w6FjaCIsCiAgICB4ID0gIkTDoXR1bSIsCiAgICB5ID0gIlByaWVtZXJuw6EgdGVwbG90YSAowrBDKSIsCiAgICBjb2xvciA9ICJNZXN0byIKICApICsKc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkJhc2VsIiA9ICJibHVlIiwgIkJ1ZGFwZcWhxaUiID0gInJlZCIsICJEw7xzc2VsZG9yZiIgPSAieWVsbG93IikpCmBgYApUdSB6b2JyYXplbsO9IGdyYWYgdsO9dm9qYSBwcmllbWVybmVqIGRlbm5laiB0ZXBsb3R5IHYgdHJvY2ggdnlicmFuw71jaCBtZXN0w6FjaCDigJMgQmFzZWwsIEJ1ZGFwZcWhxaUgYSBEw7xzc2VsZG9yZiDigJMgemEgb2Jkb2JpZSwgcHJlIGt0b3LDqSBzw7ogayBkaXNwb3rDrWNpaSBtZXJhbmlhIHYgZGF0YWLDoXplLiAKCkthxb5kw6kgbWVzdG8gamUgcmVwcmV6ZW50b3ZhbsOpIHNhbW9zdGF0bm91IGZhcmVibm91IMSNaWFyb3UsIMSNbyB1bW/FvsWIdWplIHByZWjEvmFkbsOpIHBvcm92bmFuaWUgdGVwbG90bsO9Y2ggdHJlbmRvdiBhIHptaWVuIHYgxI1hc2UuCgojIyBQcmllbWVybsOhIGEgZXh0csOpbW5hIGRlbm7DoSB0ZXBsb3RhIHYgRMO8c3NlbGRvcmZlCgpgYGB7cn0KZHVzc2VsZG9yZiA8LSB1ZGFqZSAlPiUKICBzZWxlY3QoREFURSwgRFVTU0VMRE9SRl90ZW1wX21pbiwgRFVTU0VMRE9SRl90ZW1wX21lYW4sIERVU1NFTERPUkZfdGVtcF9tYXgpICU+JQogIG11dGF0ZShEQVRFID0gYXMuRGF0ZShhcy5jaGFyYWN0ZXIoREFURSksIGZvcm1hdD0iJVklbSVkIikpCgpkdXNzZWxkb3JmX3BvaW50cyA8LSBkdXNzZWxkb3JmICU+JQogIGZpbHRlcihyb3dfbnVtYmVyKCkgJSUgMzAgPT0gMSkKCmdncGxvdChkdXNzZWxkb3JmLCBhZXMoeCA9IERBVEUpKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gRFVTU0VMRE9SRl90ZW1wX21lYW4pLCBjb2xvcj0ibGlnaHRncmVlbiIsIHNpemU9MSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGR1c3NlbGRvcmZfcG9pbnRzLCBhZXMoeSA9IERVU1NFTERPUkZfdGVtcF9taW4pLCBjb2xvcj0iZGFya2dyZWVuIiwgc2l6ZT0xLjUpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkdXNzZWxkb3JmX3BvaW50cywgYWVzKHkgPSBEVVNTRUxET1JGX3RlbXBfbWF4KSwgY29sb3I9InJlZCIsIHNpemU9MS41KSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKAogICAgdGl0bGUgPSAiUHJpZW1lcm7DoSBhIGV4dHLDqW1uYSBkZW5uw6EgdGVwbG90YSB2IETDvHNzZWxkb3JmZSIsCiAgICB4ID0gIkTDoXR1bSIsCiAgICB5ID0gIlRlcGxvdGEgKMKwQykiCiAgKQpgYGAKClRlbnRvIGdyYWYgem9icmF6dWplIHByaWVtZXJuw7ogZGVubsO6IHRlcGxvdHUgdiBEw7xzc2VsZG9yZmUgc3BvbHUgcyBtaW5pbcOhbG55bWkgYSBtYXhpbcOhbG55bWkgaG9kbm90YW1pLiAKClN2ZXRsb3plbGVuw6EgxI1pYXJhIHVrYXp1amUgcHJpZW1lcm7DuiB0ZXBsb3R1IGEgYm9keSBvem5hxI11asO6IGV4dHLDqW15IGliYSByYXogemEgMzAgZG7DrS4gCgpUYWvDoXRvIHZpenVhbGl6w6FjaWEgdW1vxb7FiHVqZSBwcmVoxL5hZG5lasWhaWUgc2xlZG92YW5pZSB0cmVuZG92IGEgdmFyaWFiaWxpdHUgdGVwbG90eSB2IMSNYXNlIGJleiB6YWhsdGVuaWEgZ3JhZnUgcHLDrWxpxaEgdmXEvmvDvW0gbW5vxb5zdHZvbSBib2Rvdi4KCiMjIEtvcmVsYcSNbsOhIG1hdGljYSAtIEJ1ZGFwZcWhxaUKCmBgYHtyfQpidWRhcGVzdF9kYXRhIDwtIHVkYWplICU+JQogIHNlbGVjdChCVURBUEVTVF90ZW1wX21heCwgQlVEQVBFU1RfaHVtaWRpdHksIEJVREFQRVNUX3ByZXNzdXJlLCAKICAgICAgICAgQlVEQVBFU1Rfc3Vuc2hpbmUsIEJVREFQRVNUX2dsb2JhbF9yYWRpYXRpb24sIEJVREFQRVNUX3ByZWNpcGl0YXRpb24sIAogICAgICAgICApCgpjb3JfbWF0cml4X2J1ZGFwZXN0IDwtIGNvcihidWRhcGVzdF9kYXRhLCB1c2UgPSAiY29tcGxldGUub2JzIikKCmxpYnJhcnkoY29ycnBsb3QpCmNvcnJwbG90KGNvcl9tYXRyaXhfYnVkYXBlc3QsIG1ldGhvZCA9ICJjb2xvciIsIHR5cGUgPSAidXBwZXIiLCAKICAgICAgICAgdGwuY29sID0gImJsYWNrIiwgdGwuc3J0ID0gNDUsIGFkZENvZWYuY29sID0gImJsYWNrIiwKICAgICAgICAgdGl0bGUgPSAiS29yZWxhxI1uw6EgbWF0aWNhIC0gQnVkYXBlxaHFpSIsIG1hciA9IGMoMCwwLDIsMCkpCmBgYApLb3JlbGHEjW7DoSBtYXRpY2Egem9icmF6dWplIHZ6xaVhaHkgbWVkemkgdnlicmFuw71taSBtZXRlb3JvbG9naWNrw71taSBwcmVtZW5uw71taSB2IG1lc3RlIEJ1ZGFwZcWhxaU6IG1heGltw6Fsbm91IHRlcGxvdG91IHZ6ZHVjaHUsIHZsaGtvc8Wlb3UsIHRsYWtvbSwgc2xuZcSNbsO9bSBzdml0b20sIGdsb2LDoWxub3UgcmFkacOhY2lvdSBhIG1ub8W+c3R2b20genLDocW+b2suCk1lZHppIGplZG5vdGxpdsO9bWkgcHJlbWVubsO9bWkgbW/Fvm5vIHBvem9yb3ZhxaUgbmlla2/EvmtvIHbDvXJhem7DvWNoIGtvcmVsw6FjacOtOgoKTWF4aW3DoWxuYSB0ZXBsb3RhIChCVURBUEVTVF90ZW1wX21heCkgbcOhIHNpbG7DuiBwb3ppdMOtdm51IGtvcmVsw6FjaXUgc28gc2xuZcSNbsO9bSBzdml0b20gKHIgPSAwLjY1KSBhIGdsb2LDoWxub3UgcmFkacOhY2lvdSAociA9IDAuNzkpLiBUbyB6bmFtZW7DoSwgxb5lIHMgcmFzdMO6Y2ltIG1ub8W+c3R2b20gc2xuZcSNbsOpaG8gxb5pYXJlbmlhIGEgZMS6xb5rb3Ugc2xuZcSNbsOpaG8gc3ZpdHUgcmFzdGllIGFqIHRlcGxvdGEuCgpNZWR6aSB0ZXBsb3RvdSBhIHZsaGtvc8Wlb3Ugc2EgbmFjaMOhZHphIG5lZ2F0w612bmEga29yZWzDoWNpYSAociA9IC0wLjU1KSwgxI1vIG5hem5hxI11amUsIMW+ZSBwb8SNYXMgdmxoa8O9Y2ggZG7DrSBiw712YWrDuiB0ZXBsb3R5IHNwcmF2aWRsYSBuacW+xaFpZS4KClRsYWsgdnpkdWNodSB2eWthenVqZSBsZW4gc2xhYsOpIHZ6xaVhaHkgcyBvc3RhdG7DvW1pIHByZW1lbm7DvW1pIChob2Rub3R5IG9rb2xvIDAuMeKAkzAuMiksIMSNbyB6bmFtZW7DoSwgxb5lIHRsYWsgbcOhIG5hIGRlbm7DuiB0ZXBsb3R1IGEgb3N0YXRuw6kgdWthem92YXRlbGUgaWJhIG1hbMO9IHZwbHl2LgoKU2xuZcSNbsO9IHN2aXQgYSBnbG9iw6FsbmEgcmFkacOhY2lhIHPDuiBtZWR6aSBzZWJvdSB2ZcS+bWkgc2lsbm8gcG96aXTDrXZuZSBrb3JlbG92YW7DqSAociA9IDAuODkpLCDEjW8gamUgb8SNYWvDoXZhbsOpLCBrZcSPxb5lIG9iYSBmYWt0b3J5IHPDunZpc2lhIHMgaW50ZW56aXRvdSBzbG5lxI1uw6lobyDFvmlhcmVuaWEuCgpacsOhxb5reSAoQlVEQVBFU1RfcHJlY2lwaXRhdGlvbikgbWFqw7ogc2xhYsOpIGHFviBtaWVybmUgbmVnYXTDrXZuZSBrb3JlbMOhY2llIHMgdsOkxI3FoWlub3Ugb3N0YXRuw71jaCB1a2F6b3ZhdGXEvm92IChuYXByLiBzIHRlcGxvdG91IHIgPSAtMC4wMSwgcyByYWRpw6FjaW91IHIgPSAtMC4xNiksIMSNbyBwb3R2cmR6dWplLCDFvmUgcG/EjWFzIGRhxb5kaXbDvWNoIGRuw60gYsO9dmEgbWVuZWogc2xua2EgYSB0ZXBsb3R5IHPDuiBuacW+xaFpZS4KCkNlbGtvdm8gbWF0aWNhIHBvdHZyZHp1amUgb8SNYWvDoXZhbsOpIHZ6xaVhaHkgbWVkemkgbWV0ZW9yb2xvZ2lja8O9bWkgdWthem92YXRlxL5taSDigJMgdnnFocWhaWUgdGVwbG90eSBzw7ogc3BvamVuw6kgcyB2ecWhxaFvdSBpbnRlbnppdG91IHNsbmXEjW7DqWhvIMW+aWFyZW5pYSwgemF0aWHEviDEjW8genbDvcWhZW7DoSB2bGhrb3PFpSBhIHpyw6HFvmt5IHByaXNwaWV2YWrDuiBrIGljaCBwb2tsZXN1Lg==