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

Práca s údajmi

Tradičná práca s databázou

Príklad - moje cvičenie

🏎️ Príklad – Formula 1

Majme údaje o jazdcoch F1, ktoré predstavujú štyri premenné – Meno, Tím a Body:

Meno <- c("Max Verstappen", "Lewis Hamilton", "Charles Leclerc", "Lando Norris")
Tim <- c("Red Bull", "Mercedes", "Ferrari", "McLaren")
Body <- c(400, 350, 280, 260)
f1 <- data.frame(Meno, Tim, Body)
print(f1)

Tieto premenné tvoria tabuľku s jazdcami, ich tímami a počtom bodov.
Teraz napríklad zoradíme tabuľku podľa počtu bodov zostupne:

f1_sorted <- f1_data[order(-f1_data$Body), ]
print(f1_sorted)

Práca s jednotlivými stĺpcami

print(f1$Body)                   # vypíšeme stĺpec Body
[1] 400 350 280 260
print(mean(f1$Body))             # priemerný počet bodov
[1] 322.5
print(f1[Meno=="Lewis Hamilton",])  # vyberieme riadok podľa mena
print(f1[2,])                    # vyberieme druhý riadok tabuľky
print(f1[,2:3])                  # vyberieme stĺpce Tim a Body
print(f1[1,1])                   # vypíšeme jednu bunku (1. riadok, 1. stĺpec)
[1] "Max Verstappen"
summary(f1)                      # základná štatistika tabuľky
     Meno               Tim                 Body      
 Length:4           Length:4           Min.   :260.0  
 Class :character   Class :character   1st Qu.:275.0  
 Mode  :character   Mode  :character   Median :315.0  
                                       Mean   :322.5  
                                       3rd Qu.:362.5  
                                       Max.   :400.0  

Pridanie ďalšieho stĺpca

Napríklad pridáme, či jazdec má pole position v poslednej kvalifikácii:

PolePosition <- c(TRUE, FALSE, FALSE,TRUE)
f1 <- cbind(f1, PolePosition)
print(f1)

Ak chceme pridať riadok, potom:

# pôvodná tabuľka
Meno <- c("Max Verstappen", "Lewis Hamilton", "Charles Leclerc", "Lando Norris")
Tim <- c("Red Bull", "Mercedes", "Ferrari", "McLaren")
Body <- c(400, 350, 280, 260)
PolePosition <- c(TRUE, FALSE, FALSE, TRUE)

f1 <- data.frame(Meno, Tim, Body, PolePosition)
print(f1)

# nový riadok – musí mať rovnaké stĺpce a typy
novy.riadok <- data.frame(
  Meno = "Fernando Alonso",
  Tim = "Aston Martin",
  Body = 210,
  PolePosition = FALSE
)

# pridáme riadok k tabuľke
f1 <- rbind(f1, novy.riadok)
print(f1)

Tabuľka jazdcov F1 v prostredí kableExtra

library(knitr)
library(kableExtra)

Meno <- c("Max Verstappen", "Lewis Hamilton", "Charles Leclerc", "Lando Norris", "Fernando Alonso")
Tim <- c("Red Bull", "Mercedes", "Ferrari", "McLaren", "Aston Martin")
Body <- c(400, 350, 280, 260, 210)
PolePosition <- c(TRUE, FALSE, FALSE, TRUE, FALSE)

f1 <- data.frame(Meno, Tim, Body, PolePosition)

kable(
  f1,
  digits = 0,
  align = c("l","c","r","c"),
  caption = "Výsledky jazdcov F1"
) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Výsledky jazdcov F1
Meno Tim Body PolePosition
Max Verstappen Red Bull 400 TRUE
Lewis Hamilton Mercedes 350 FALSE
Charles Leclerc Ferrari 280 FALSE
Lando Norris McLaren 260 TRUE
Fernando Alonso Aston Martin 210 FALSE

️ Farebná tabuľka jazdcov F1

library(knitr)
library(kableExtra)

Meno <- c("Max Verstappen", "Lewis Hamilton", "Charles Leclerc", "Lando Norris", "Fernando Alonso")
Tim <- c("Red Bull", "Mercedes", "Ferrari", "McLaren", "Aston Martin")
Body <- c(400, 350, 280, 260, 210)
PolePosition <- c(TRUE, FALSE, FALSE, TRUE, FALSE)

f1 <- data.frame(Meno, Tim, Body, PolePosition)

# zafarbenie Body
f1$Body <- cell_spec(
  f1$Body,
  "html",
  color = "white",
  background = spec_color(as.numeric(Body), end = 0.7)
)

kable(
  f1,
  format = "html",   # <<< tu musí byť html
  escape = FALSE,    # <<< aby sa vykreslili farby
  caption = "Výsledky jazdcov F1 s farebným stĺpcom Body"
) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Výsledky jazdcov F1 s farebným stĺpcom Body
Meno Tim Body PolePosition
Max Verstappen Red Bull 400 TRUE
Lewis Hamilton Mercedes 350 FALSE
Charles Leclerc Ferrari 280 FALSE
Lando Norris McLaren 260 TRUE
Fernando Alonso Aston Martin 210 FALSE

Tidyverse - moderná práca s údajmi

Tidyverse je súbor knižníc, ktoré majú zjednodušiť prácu s údajmi. Majú jednotný komunikačný štandard, vzájomne sa doplňujú.

# Load tidyverse
library(tidyverse)

dplyr - pre manipuláciu s údajmi

Výber a triedenie

library(tidyverse)

Meno <- c("Max Verstappen", "Lewis Hamilton", "Charles Leclerc", "Lando Norris", "Fernando Alonso")
Tim <- c("Red Bull", "Mercedes", "Ferrari", "McLaren", "Aston Martin")
Body <- c(400, 350, 280, 260, 210)
PolePosition <- c(TRUE, FALSE, FALSE, TRUE, FALSE)

f1 <- data.frame(Meno, Tim, Body, PolePosition)

# vyberieme jazdcov s viac ako 250 bodmi a zoradíme podľa bodov zostupne
f1 %>%
  filter(Body > 250) %>%
  arrange(desc(Body)) %>%
  knitr::kable(format = "html") %>%
  kableExtra::kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Meno Tim Body PolePosition
Max Verstappen Red Bull 400 TRUE
Lewis Hamilton Mercedes 350 FALSE
Charles Leclerc Ferrari 280 FALSE
Lando Norris McLaren 260 TRUE

Zoskupenie a sumarizácia – Formula 1

library(tidyverse)
library(knitr)
library(kableExtra)

Meno <- c("Max Verstappen", "Lewis Hamilton", "Charles Leclerc", "Lando Norris", "Fernando Alonso")
Tim <- c("Red Bull", "Mercedes", "Ferrari", "McLaren", "Aston Martin")
Body <- c(400, 350, 280, 260, 210)
PolePosition <- c(TRUE, FALSE, FALSE, TRUE, FALSE)

f1 <- data.frame(Meno, Tim, Body, PolePosition)

# zoskupíme podľa PolePosition a spočítame priemerné body a počet jazdcov
f1 %>%
  group_by(PolePosition) %>%
  summarise(
    PriemerBody = mean(Body),
    Pocet = n()
  ) %>%
  kable(
    caption = "Priemerné body podľa toho, či jazdec získal Pole Position",
    col.names = c("Pole Position", "Priemerné Body", "Počet"),
    align = "c"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Priemerné body podľa toho, či jazdec získal Pole Position
Pole Position Priemerné Body Počet
FALSE 280 3
TRUE 330 2

Vytváranie novej premennej – Formula 1

BodyDoCiela nám ukazuje o koľko boodov jazdec zaostáva za lídrom M.V. Hodnotenie S- super výkon jazdec má viac ako 350bodov, B - priemer jazdec má 250 až 299 bodov, C - slabý výkon jazdec má menej ako 250 bodov

library(tidyverse)
library(knitr)
library(kableExtra)

Meno <- c("Max Verstappen", "Lewis Hamilton", "Charles Leclerc", "Lando Norris", "Fernando Alonso")
Tim <- c("Red Bull", "Mercedes", "Ferrari", "McLaren", "Aston Martin")
Body <- c(400, 350, 280, 260, 210)
PolePosition <- c(TRUE, FALSE, FALSE, TRUE, FALSE)

f1 <- data.frame(Meno, Tim, Body, PolePosition)

# vytvoríme nové stĺpce
f1 %>%
  mutate(
    Hodnotenie = case_when(
      Body >= 350 ~ "S",
      Body >= 300 ~ "A",
      Body >= 250 ~ "B",
      TRUE ~ "C"
    ),
    BodyDoCiela = max(Body) - Body
  ) %>%
  kable(
    caption = "F1 jazdci s novými premennými Hodnotenie a BodyDoCiela",
    align = "c"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
F1 jazdci s novými premennými Hodnotenie a BodyDoCiela
Meno Tim Body PolePosition Hodnotenie BodyDoCiela
Max Verstappen Red Bull 400 TRUE S 0
Lewis Hamilton Mercedes 350 FALSE S 50
Charles Leclerc Ferrari 280 FALSE B 120
Lando Norris McLaren 260 TRUE B 140
Fernando Alonso Aston Martin 210 FALSE C 190

Import údajov

na základe viacerých datasetov z kaggle som si pripravila excel ktorý obsahuje základné údaje z F1

library(readxl)

f1 <- read_excel("f1_long_dataset.xlsx")
head(f1)

Grafy

PolePosition vs Body

library(ggplot2)

ggplot(f1, aes(x = PolePosition, y = Body, color = Jazdec)) +
  geom_point(size = 3) +
  theme_minimal() +
  labs(title = "Vzťah medzi pole position a počtom bodov",
       x = "Počet pole position",
       y = "Počet bodov")

Čiarový graf – Body podľa rokov

ggplot(f1, aes(x = Rok, y = Body, color = Jazdec, group = Jazdec)) +
  geom_line(size = 1.2) +
  geom_point(size = 3) +
  theme_minimal() +
  labs(title = "Vývoj bodov jazdcov F1 (2020–2024)",
       x = "Rok",
       y = "Body")

Boxplot – rozloženie bodov podľa jazdcov

ggplot(f1, aes(x = Jazdec, y = Body, fill = Jazdec)) +
  geom_boxplot() +
  theme_minimal() +
  labs(title = "Rozloženie bodov podľa jazdcov",
       x = "Jazdec",
       y = "Body")

Základné štatistiky.

knitr - tabuľka

library(dplyr)
library(knitr)
library(kableExtra)

# Vypočítame základné štatistiky pre body F1 jazdcov podľa rokov
f1.stats <- f1 %>%
  group_by(Rok) %>%                        # zoskupenie podľa roku
  summarise(
    n       = n(),                         # počet záznamov (jazdcov v danom roku)
    mean    = mean(Body, na.rm = TRUE),    # priemer bodov
    sd      = sd(Body, na.rm = TRUE),      # smerodajná odchýlka
    min     = min(Body, na.rm = TRUE),     # minimálny počet bodov
    q25     = quantile(Body, 0.25, na.rm = TRUE), # 1. kvartil (25 %)
    median  = median(Body, na.rm = TRUE),  # medián (50 %)
    q75     = quantile(Body, 0.75, na.rm = TRUE), # 3. kvartil (75 %)
    max     = max(Body, na.rm = TRUE),     # maximálny počet bodov
    .groups = "drop"                       # odstránenie vnorených skupín
  )

# Vytvoríme pekne naformátovanú tabuľku pomocou kableExtra
f1.stats %>%
  kable(
    digits = 2,                                     # zaokrúhlenie na 2 desatinné miesta
    caption = "Základné štatistiky bodov F1 podľa rokov"  # názov tabuľky
  ) %>%
  kable_styling(
    full_width = FALSE,                             # tabuľka nebude cez celú šírku
    bootstrap_options = c("striped", "hover", "condensed") # štýl tabuľky
  ) %>%
  column_spec(1, bold = TRUE) %>%                   # prvý stĺpec (rok) bude tučný
  row_spec(0, bold = TRUE, background = "#f2f2f2") %>%  # hlavička bude tučná a so sivým pozadím
  add_header_above(c(" " = 2, "Body Statistics" = 7))    # nadpis pre skupinu štatistík
Základné štatistiky bodov F1 podľa rokov
Body Statistics
Rok n mean sd min q25 median q75 max
2020 6 269.50 94.96 180 202.5 230 352.75 390
2021 6 275.83 89.58 190 212.5 240 350.00 395
2022 6 283.33 82.38 200 230.0 250 345.00 400
2023 6 290.00 78.49 210 240.0 260 340.00 410
2024 6 296.67 75.28 220 250.0 270 335.00 420

T-test – porovnanie priemeru bodov v dvoch rokoch

# t-test porovnanie priemeru bodov v rokoch 2020 a 2024
t.test.result <- t.test(
  f1$Body[f1$Rok == 2020],
  f1$Body[f1$Rok == 2024]
)

print(t.test.result)

    Welch Two Sample t-test

data:  f1$Body[f1$Rok == 2020] and f1$Body[f1$Rok == 2024]
t = -0.54915, df = 9.505, p-value = 0.5956
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -138.17750   83.84417
sample estimates:
mean of x mean of y 
 269.5000  296.6667 

ANOVA – porovnanie priemerov medzi viacerými rokmi

# ANOVA: rozdiely v bodoch medzi rokmi
anova.result <- aov(Body ~ as.factor(Rok), data = f1)
summary(anova.result)
               Df Sum Sq Mean Sq F value Pr(>F)
as.factor(Rok)  4   2817     704   0.099  0.982
Residuals      25 178275    7131               

Výsledky ANOVA testu (F = 0.099, p = 0.982) ukazujú, že medzi rokmi 2020–2024 neexistuje štatisticky významný rozdiel v priemernom počte bodov jazdcov F1. Priemery bodov sa medzi jednotlivými rokmi nelíšia

Lineárna regresia - predpovedanie bodov

# Lineárna regresia: predikcia bodov podľa výhier a pole position
model <- lm(Body ~ Vyhry + PolePosition, data = f1)
summary(model)

Call:
lm(formula = Body ~ Vyhry + PolePosition, data = f1)

Residuals:
    Min      1Q  Median      3Q     Max 
-38.546 -11.681  -3.194   9.514  60.330 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)   211.681      5.908  35.829   <2e-16 ***
Vyhry           7.860      5.430   1.447    0.159    
PolePosition   12.897      8.245   1.564    0.129    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 23.19 on 27 degrees of freedom
Multiple R-squared:  0.9198,    Adjusted R-squared:  0.9139 
F-statistic: 154.9 on 2 and 27 DF,  p-value: 1.603e-15
LS0tCnRpdGxlOiAiUHLDoWNhIHMgZGF0YWLDoXpvdSIKYXV0aG9yOiAiU2ltb25hIFZhbsSNb3bDoSA8YnI+CihzIHZ5dcW+aXTDrW0gdmVyZWpuZSBkb3N0dXBuw71jaCBrw7Nkb3YpIgpkYXRlOiAiU2VwdGVtYmVyIDIwMjUiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0aGVtZTogdW5pdGVkCiAgICBoaWdobGlnaHQ6IHRhbmdvCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCmBgYHtyfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgICBlY2hvID0gVFJVRSwKICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgIHdhcm5pbmcgPSBGQUxTRQopCmBgYAoKPGgxIHN0eWxlPSJjb2xvcjpvcmFuZ2U7Ij4KClByw6FjYSBzIMO6ZGFqbWkKCjwvaDE+Cgo8aDIgc3R5bGU9ImNvbG9yOmJsdWU7Ij4KClRyYWRpxI1uw6EgcHLDoWNhIHMgZGF0YWLDoXpvdQoKPC9oMj4KCiMjIFByw61rbGFkIC0gbW9qZSBjdmnEjWVuaWUKCiMjIyDwn4+O77iPIFByw61rbGFkIOKAkyBGb3JtdWxhIDEKCk1ham1lIMO6ZGFqZSBvIGphemRjb2NoIEYxLCBrdG9yw6kgcHJlZHN0YXZ1asO6IMWhdHlyaSBwcmVtZW5uw6kg4oCTICoqTWVubyoqLAoqKlTDrW0qKiBhICoqQm9keSoqOgoKYGBge3J9Ck1lbm8gPC0gYygiTWF4IFZlcnN0YXBwZW4iLCAiTGV3aXMgSGFtaWx0b24iLCAiQ2hhcmxlcyBMZWNsZXJjIiwgIkxhbmRvIE5vcnJpcyIpClRpbSA8LSBjKCJSZWQgQnVsbCIsICJNZXJjZWRlcyIsICJGZXJyYXJpIiwgIk1jTGFyZW4iKQpCb2R5IDwtIGMoNDAwLCAzNTAsIDI4MCwgMjYwKQpmMSA8LSBkYXRhLmZyYW1lKE1lbm8sIFRpbSwgQm9keSkKcHJpbnQoZjEpCmBgYAoKVGlldG8gcHJlbWVubsOpIHR2b3JpYSB0YWJ1xL5rdSBzIGphemRjYW1pLCBpY2ggdMOtbWFtaSBhIHBvxI10b20KYm9kb3YuXApUZXJheiBuYXByw61rbGFkIHpvcmFkw61tZSB0YWJ1xL5rdSBwb2TEvmEgcG/EjXR1IGJvZG92IHpvc3R1cG5lOgoKYGBge3J9CmYxX3NvcnRlZCA8LSBmMV9kYXRhW29yZGVyKC1mMV9kYXRhJEJvZHkpLCBdCnByaW50KGYxX3NvcnRlZCkKYGBgCgojIyMgUHLDoWNhIHMgamVkbm90bGl2w71taSBzdMS6cGNhbWkKCmBgYHtyfQpwcmludChmMSRCb2R5KSAgICAgICAgICAgICAgICAgICAjIHZ5cMOtxaFlbWUgc3TEunBlYyBCb2R5CnByaW50KG1lYW4oZjEkQm9keSkpICAgICAgICAgICAgICMgcHJpZW1lcm7DvSBwb8SNZXQgYm9kb3YKcHJpbnQoZjFbTWVubz09Ikxld2lzIEhhbWlsdG9uIixdKSAgIyB2eWJlcmllbWUgcmlhZG9rIHBvZMS+YSBtZW5hCnByaW50KGYxWzIsXSkgICAgICAgICAgICAgICAgICAgICMgdnliZXJpZW1lIGRydWjDvSByaWFkb2sgdGFidcS+a3kKcHJpbnQoZjFbLDI6M10pICAgICAgICAgICAgICAgICAgIyB2eWJlcmllbWUgc3TEunBjZSBUaW0gYSBCb2R5CnByaW50KGYxWzEsMV0pICAgICAgICAgICAgICAgICAgICMgdnlww63FoWVtZSBqZWRudSBidW5rdSAoMS4gcmlhZG9rLCAxLiBzdMS6cGVjKQpzdW1tYXJ5KGYxKSAgICAgICAgICAgICAgICAgICAgICAjIHrDoWtsYWRuw6EgxaF0YXRpc3Rpa2EgdGFidcS+a3kKYGBgCgojIyMgUHJpZGFuaWUgxI9hbMWhaWVobyBzdMS6cGNhCgpOYXByw61rbGFkIHByaWTDoW1lLCDEjWkgamF6ZGVjIG3DoSBwb2xlIHBvc2l0aW9uIHYgcG9zbGVkbmVqIGt2YWxpZmlrw6FjaWk6CgpgYGB7cn0KUG9sZVBvc2l0aW9uIDwtIGMoVFJVRSwgRkFMU0UsIEZBTFNFLFRSVUUpCmYxIDwtIGNiaW5kKGYxLCBQb2xlUG9zaXRpb24pCnByaW50KGYxKQpgYGAKCkFrIGNoY2VtZSBwcmlkYcWlIHJpYWRvaywgcG90b206CgpgYGB7cn0KIyBww7R2b2Ruw6EgdGFidcS+a2EKTWVubyA8LSBjKCJNYXggVmVyc3RhcHBlbiIsICJMZXdpcyBIYW1pbHRvbiIsICJDaGFybGVzIExlY2xlcmMiLCAiTGFuZG8gTm9ycmlzIikKVGltIDwtIGMoIlJlZCBCdWxsIiwgIk1lcmNlZGVzIiwgIkZlcnJhcmkiLCAiTWNMYXJlbiIpCkJvZHkgPC0gYyg0MDAsIDM1MCwgMjgwLCAyNjApClBvbGVQb3NpdGlvbiA8LSBjKFRSVUUsIEZBTFNFLCBGQUxTRSwgVFJVRSkKCmYxIDwtIGRhdGEuZnJhbWUoTWVubywgVGltLCBCb2R5LCBQb2xlUG9zaXRpb24pCnByaW50KGYxKQoKIyBub3bDvSByaWFkb2sg4oCTIG11c8OtIG1hxaUgcm92bmFrw6kgc3TEunBjZSBhIHR5cHkKbm92eS5yaWFkb2sgPC0gZGF0YS5mcmFtZSgKICBNZW5vID0gIkZlcm5hbmRvIEFsb25zbyIsCiAgVGltID0gIkFzdG9uIE1hcnRpbiIsCiAgQm9keSA9IDIxMCwKICBQb2xlUG9zaXRpb24gPSBGQUxTRQopCgojIHByaWTDoW1lIHJpYWRvayBrIHRhYnXEvmtlCmYxIDwtIHJiaW5kKGYxLCBub3Z5LnJpYWRvaykKcHJpbnQoZjEpCmBgYAoKIyMjIFRhYnXEvmthIGphemRjb3YgRjEgdiBwcm9zdHJlZMOtIGthYmxlRXh0cmEKCmBgYHtyfQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCgpNZW5vIDwtIGMoIk1heCBWZXJzdGFwcGVuIiwgIkxld2lzIEhhbWlsdG9uIiwgIkNoYXJsZXMgTGVjbGVyYyIsICJMYW5kbyBOb3JyaXMiLCAiRmVybmFuZG8gQWxvbnNvIikKVGltIDwtIGMoIlJlZCBCdWxsIiwgIk1lcmNlZGVzIiwgIkZlcnJhcmkiLCAiTWNMYXJlbiIsICJBc3RvbiBNYXJ0aW4iKQpCb2R5IDwtIGMoNDAwLCAzNTAsIDI4MCwgMjYwLCAyMTApClBvbGVQb3NpdGlvbiA8LSBjKFRSVUUsIEZBTFNFLCBGQUxTRSwgVFJVRSwgRkFMU0UpCgpmMSA8LSBkYXRhLmZyYW1lKE1lbm8sIFRpbSwgQm9keSwgUG9sZVBvc2l0aW9uKQoKa2FibGUoCiAgZjEsCiAgZGlnaXRzID0gMCwKICBhbGlnbiA9IGMoImwiLCJjIiwiciIsImMiKSwKICBjYXB0aW9uID0gIlbDvXNsZWRreSBqYXpkY292IEYxIgopICU+JQogIGthYmxlX3N0eWxpbmcoCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIKICApCmBgYAoKIyMjIO+4jyBGYXJlYm7DoSB0YWJ1xL5rYSBqYXpkY292IEYxCgpgYGB7cn0KbGlicmFyeShrbml0cikKbGlicmFyeShrYWJsZUV4dHJhKQoKTWVubyA8LSBjKCJNYXggVmVyc3RhcHBlbiIsICJMZXdpcyBIYW1pbHRvbiIsICJDaGFybGVzIExlY2xlcmMiLCAiTGFuZG8gTm9ycmlzIiwgIkZlcm5hbmRvIEFsb25zbyIpClRpbSA8LSBjKCJSZWQgQnVsbCIsICJNZXJjZWRlcyIsICJGZXJyYXJpIiwgIk1jTGFyZW4iLCAiQXN0b24gTWFydGluIikKQm9keSA8LSBjKDQwMCwgMzUwLCAyODAsIDI2MCwgMjEwKQpQb2xlUG9zaXRpb24gPC0gYyhUUlVFLCBGQUxTRSwgRkFMU0UsIFRSVUUsIEZBTFNFKQoKZjEgPC0gZGF0YS5mcmFtZShNZW5vLCBUaW0sIEJvZHksIFBvbGVQb3NpdGlvbikKCiMgemFmYXJiZW5pZSBCb2R5CmYxJEJvZHkgPC0gY2VsbF9zcGVjKAogIGYxJEJvZHksCiAgImh0bWwiLAogIGNvbG9yID0gIndoaXRlIiwKICBiYWNrZ3JvdW5kID0gc3BlY19jb2xvcihhcy5udW1lcmljKEJvZHkpLCBlbmQgPSAwLjcpCikKCmthYmxlKAogIGYxLAogIGZvcm1hdCA9ICJodG1sIiwgICAjIDw8PCB0dSBtdXPDrSBiecWlIGh0bWwKICBlc2NhcGUgPSBGQUxTRSwgICAgIyA8PDwgYWJ5IHNhIHZ5a3Jlc2xpbGkgZmFyYnkKICBjYXB0aW9uID0gIlbDvXNsZWRreSBqYXpkY292IEYxIHMgZmFyZWJuw71tIHN0xLpwY29tIEJvZHkiCikgJT4lCiAga2FibGVfc3R5bGluZygKICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpLAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIgogICkKYGBgCgojIyBUaWR5dmVyc2UgLSBtb2Rlcm7DoSBwcsOhY2EgcyDDumRham1pCgpUaWR5dmVyc2UgamUgc8O6Ym9yIGtuacW+bsOtYywga3RvcsOpIG1hasO6IHpqZWRub2R1xaFpxaUgcHLDoWN1IHMgw7pkYWptaS4gTWFqw7oKamVkbm90bsO9IGtvbXVuaWthxI1uw70gxaF0YW5kYXJkLCB2esOham9tbmUgc2EgZG9wbMWIdWrDui4KCmBgYHtyfQojIExvYWQgdGlkeXZlcnNlCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIyBkcGx5ciAtIHByZSBtYW5pcHVsw6FjaXUgcyDDumRham1pCgojIyMjIFbDvWJlciBhIHRyaWVkZW5pZQoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQoKTWVubyA8LSBjKCJNYXggVmVyc3RhcHBlbiIsICJMZXdpcyBIYW1pbHRvbiIsICJDaGFybGVzIExlY2xlcmMiLCAiTGFuZG8gTm9ycmlzIiwgIkZlcm5hbmRvIEFsb25zbyIpClRpbSA8LSBjKCJSZWQgQnVsbCIsICJNZXJjZWRlcyIsICJGZXJyYXJpIiwgIk1jTGFyZW4iLCAiQXN0b24gTWFydGluIikKQm9keSA8LSBjKDQwMCwgMzUwLCAyODAsIDI2MCwgMjEwKQpQb2xlUG9zaXRpb24gPC0gYyhUUlVFLCBGQUxTRSwgRkFMU0UsIFRSVUUsIEZBTFNFKQoKZjEgPC0gZGF0YS5mcmFtZShNZW5vLCBUaW0sIEJvZHksIFBvbGVQb3NpdGlvbikKCiMgdnliZXJpZW1lIGphemRjb3YgcyB2aWFjIGFrbyAyNTAgYm9kbWkgYSB6b3JhZMOtbWUgcG9kxL5hIGJvZG92IHpvc3R1cG5lCmYxICU+JQogIGZpbHRlcihCb2R5ID4gMjUwKSAlPiUKICBhcnJhbmdlKGRlc2MoQm9keSkpICU+JQogIGtuaXRyOjprYWJsZShmb3JtYXQgPSAiaHRtbCIpICU+JQogIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIKICApCmBgYAoKIyMjIyBab3NrdXBlbmllIGEgc3VtYXJpesOhY2lhIOKAkyBGb3JtdWxhIDEKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShrbml0cikKbGlicmFyeShrYWJsZUV4dHJhKQoKTWVubyA8LSBjKCJNYXggVmVyc3RhcHBlbiIsICJMZXdpcyBIYW1pbHRvbiIsICJDaGFybGVzIExlY2xlcmMiLCAiTGFuZG8gTm9ycmlzIiwgIkZlcm5hbmRvIEFsb25zbyIpClRpbSA8LSBjKCJSZWQgQnVsbCIsICJNZXJjZWRlcyIsICJGZXJyYXJpIiwgIk1jTGFyZW4iLCAiQXN0b24gTWFydGluIikKQm9keSA8LSBjKDQwMCwgMzUwLCAyODAsIDI2MCwgMjEwKQpQb2xlUG9zaXRpb24gPC0gYyhUUlVFLCBGQUxTRSwgRkFMU0UsIFRSVUUsIEZBTFNFKQoKZjEgPC0gZGF0YS5mcmFtZShNZW5vLCBUaW0sIEJvZHksIFBvbGVQb3NpdGlvbikKCiMgem9za3Vww61tZSBwb2TEvmEgUG9sZVBvc2l0aW9uIGEgc3BvxI3DrXRhbWUgcHJpZW1lcm7DqSBib2R5IGEgcG/EjWV0IGphemRjb3YKZjEgJT4lCiAgZ3JvdXBfYnkoUG9sZVBvc2l0aW9uKSAlPiUKICBzdW1tYXJpc2UoCiAgICBQcmllbWVyQm9keSA9IG1lYW4oQm9keSksCiAgICBQb2NldCA9IG4oKQogICkgJT4lCiAga2FibGUoCiAgICBjYXB0aW9uID0gIlByaWVtZXJuw6kgYm9keSBwb2TEvmEgdG9obywgxI1pIGphemRlYyB6w61za2FsIFBvbGUgUG9zaXRpb24iLAogICAgY29sLm5hbWVzID0gYygiUG9sZSBQb3NpdGlvbiIsICJQcmllbWVybsOpIEJvZHkiLCAiUG/EjWV0IiksCiAgICBhbGlnbiA9ICJjIgogICkgJT4lCiAga2FibGVfc3R5bGluZygKICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpLAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIgogICkKYGBgCgojIyMjIFZ5dHbDoXJhbmllIG5vdmVqIHByZW1lbm5laiDigJMgRm9ybXVsYSAxCgpCb2R5RG9DaWVsYSBuw6FtIHVrYXp1amUgbyBrb8S+a28gYm9vZG92IGphemRlYyB6YW9zdMOhdmEgemEgbMOtZHJvbSBNLlYuCkhvZG5vdGVuaWUgUy0gc3VwZXIgdsO9a29uIGphemRlYyBtw6EgdmlhYyBha28gMzUwYm9kb3YsIEIgLSBwcmllbWVyCmphemRlYyBtw6EgMjUwIGHFviAyOTkgYm9kb3YsIEMgLSBzbGFiw70gdsO9a29uIGphemRlYyBtw6EgbWVuZWogYWtvIDI1MApib2RvdgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCgpNZW5vIDwtIGMoIk1heCBWZXJzdGFwcGVuIiwgIkxld2lzIEhhbWlsdG9uIiwgIkNoYXJsZXMgTGVjbGVyYyIsICJMYW5kbyBOb3JyaXMiLCAiRmVybmFuZG8gQWxvbnNvIikKVGltIDwtIGMoIlJlZCBCdWxsIiwgIk1lcmNlZGVzIiwgIkZlcnJhcmkiLCAiTWNMYXJlbiIsICJBc3RvbiBNYXJ0aW4iKQpCb2R5IDwtIGMoNDAwLCAzNTAsIDI4MCwgMjYwLCAyMTApClBvbGVQb3NpdGlvbiA8LSBjKFRSVUUsIEZBTFNFLCBGQUxTRSwgVFJVRSwgRkFMU0UpCgpmMSA8LSBkYXRhLmZyYW1lKE1lbm8sIFRpbSwgQm9keSwgUG9sZVBvc2l0aW9uKQoKIyB2eXR2b3LDrW1lIG5vdsOpIHN0xLpwY2UKZjEgJT4lCiAgbXV0YXRlKAogICAgSG9kbm90ZW5pZSA9IGNhc2Vfd2hlbigKICAgICAgQm9keSA+PSAzNTAgfiAiUyIsCiAgICAgIEJvZHkgPj0gMzAwIH4gIkEiLAogICAgICBCb2R5ID49IDI1MCB+ICJCIiwKICAgICAgVFJVRSB+ICJDIgogICAgKSwKICAgIEJvZHlEb0NpZWxhID0gbWF4KEJvZHkpIC0gQm9keQogICkgJT4lCiAga2FibGUoCiAgICBjYXB0aW9uID0gIkYxIGphemRjaSBzIG5vdsO9bWkgcHJlbWVubsO9bWkgSG9kbm90ZW5pZSBhIEJvZHlEb0NpZWxhIiwKICAgIGFsaWduID0gImMiCiAgKSAlPiUKICBrYWJsZV9zdHlsaW5nKAogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIsICJyZXNwb25zaXZlIiksCiAgICBmdWxsX3dpZHRoID0gRkFMU0UsCiAgICBwb3NpdGlvbiA9ICJjZW50ZXIiCiAgKQpgYGAKCiMjIEltcG9ydCDDumRham92CgpuYSB6w6FrbGFkZSB2aWFjZXLDvWNoIGRhdGFzZXRvdiB6IGthZ2dsZSBzb20gc2kgcHJpcHJhdmlsYSBleGNlbCBrdG9yw70Kb2JzYWh1amUgesOha2xhZG7DqSDDumRhamUgeiBGMQoKYGBge3J9CmxpYnJhcnkocmVhZHhsKQoKZjEgPC0gcmVhZF9leGNlbCgiZjFfbG9uZ19kYXRhc2V0Lnhsc3giKQpoZWFkKGYxKQpgYGAKCiMjIEdyYWZ5CgojIyMgUG9sZVBvc2l0aW9uIHZzIEJvZHkKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCgpnZ3Bsb3QoZjEsIGFlcyh4ID0gUG9sZVBvc2l0aW9uLCB5ID0gQm9keSwgY29sb3IgPSBKYXpkZWMpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh0aXRsZSA9ICJWesWlYWggbWVkemkgcG9sZSBwb3NpdGlvbiBhIHBvxI10b20gYm9kb3YiLAogICAgICAgeCA9ICJQb8SNZXQgcG9sZSBwb3NpdGlvbiIsCiAgICAgICB5ID0gIlBvxI1ldCBib2RvdiIpCmBgYAoKIyMjIMSMaWFyb3bDvSBncmFmIOKAkyBCb2R5IHBvZMS+YSByb2tvdgoKYGBge3J9CmdncGxvdChmMSwgYWVzKHggPSBSb2ssIHkgPSBCb2R5LCBjb2xvciA9IEphemRlYywgZ3JvdXAgPSBKYXpkZWMpKSArCiAgZ2VvbV9saW5lKHNpemUgPSAxLjIpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gIlbDvXZvaiBib2RvdiBqYXpkY292IEYxICgyMDIw4oCTMjAyNCkiLAogICAgICAgeCA9ICJSb2siLAogICAgICAgeSA9ICJCb2R5IikKYGBgCgojIyMgQm94cGxvdCDigJMgcm96bG/FvmVuaWUgYm9kb3YgcG9kxL5hIGphemRjb3YKCmBgYHtyfQpnZ3Bsb3QoZjEsIGFlcyh4ID0gSmF6ZGVjLCB5ID0gQm9keSwgZmlsbCA9IEphemRlYykpICsKICBnZW9tX2JveHBsb3QoKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gIlJvemxvxb5lbmllIGJvZG92IHBvZMS+YSBqYXpkY292IiwKICAgICAgIHggPSAiSmF6ZGVjIiwKICAgICAgIHkgPSAiQm9keSIpCgpgYGAKCiMgWsOha2xhZG7DqSDFoXRhdGlzdGlreS4KCiMjIGtuaXRyIC0gdGFidcS+a2EKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCgojIFZ5cG/EjcOtdGFtZSB6w6FrbGFkbsOpIMWhdGF0aXN0aWt5IHByZSBib2R5IEYxIGphemRjb3YgcG9kxL5hIHJva292CmYxLnN0YXRzIDwtIGYxICU+JQogIGdyb3VwX2J5KFJvaykgJT4lICAgICAgICAgICAgICAgICAgICAgICAgIyB6b3NrdXBlbmllIHBvZMS+YSByb2t1CiAgc3VtbWFyaXNlKAogICAgbiAgICAgICA9IG4oKSwgICAgICAgICAgICAgICAgICAgICAgICAgIyBwb8SNZXQgesOhem5hbW92IChqYXpkY292IHYgZGFub20gcm9rdSkKICAgIG1lYW4gICAgPSBtZWFuKEJvZHksIG5hLnJtID0gVFJVRSksICAgICMgcHJpZW1lciBib2RvdgogICAgc2QgICAgICA9IHNkKEJvZHksIG5hLnJtID0gVFJVRSksICAgICAgIyBzbWVyb2Rham7DoSBvZGNow71sa2EKICAgIG1pbiAgICAgPSBtaW4oQm9keSwgbmEucm0gPSBUUlVFKSwgICAgICMgbWluaW3DoWxueSBwb8SNZXQgYm9kb3YKICAgIHEyNSAgICAgPSBxdWFudGlsZShCb2R5LCAwLjI1LCBuYS5ybSA9IFRSVUUpLCAjIDEuIGt2YXJ0aWwgKDI1ICUpCiAgICBtZWRpYW4gID0gbWVkaWFuKEJvZHksIG5hLnJtID0gVFJVRSksICAjIG1lZGnDoW4gKDUwICUpCiAgICBxNzUgICAgID0gcXVhbnRpbGUoQm9keSwgMC43NSwgbmEucm0gPSBUUlVFKSwgIyAzLiBrdmFydGlsICg3NSAlKQogICAgbWF4ICAgICA9IG1heChCb2R5LCBuYS5ybSA9IFRSVUUpLCAgICAgIyBtYXhpbcOhbG55IHBvxI1ldCBib2RvdgogICAgLmdyb3VwcyA9ICJkcm9wIiAgICAgICAgICAgICAgICAgICAgICAgIyBvZHN0csOhbmVuaWUgdm5vcmVuw71jaCBza3Vww61uCiAgKQoKIyBWeXR2b3LDrW1lIHBla25lIG5hZm9ybcOhdG92YW7DuiB0YWJ1xL5rdSBwb21vY291IGthYmxlRXh0cmEKZjEuc3RhdHMgJT4lCiAga2FibGUoCiAgICBkaWdpdHMgPSAyLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHphb2tyw7pobGVuaWUgbmEgMiBkZXNhdGlubsOpIG1pZXN0YQogICAgY2FwdGlvbiA9ICJaw6FrbGFkbsOpIMWhdGF0aXN0aWt5IGJvZG92IEYxIHBvZMS+YSByb2tvdiIgICMgbsOhem92IHRhYnXEvmt5CiAgKSAlPiUKICBrYWJsZV9zdHlsaW5nKAogICAgZnVsbF93aWR0aCA9IEZBTFNFLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0YWJ1xL5rYSBuZWJ1ZGUgY2V6IGNlbMO6IMWhw61ya3UKICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSAjIMWhdMO9bCB0YWJ1xL5reQogICkgJT4lCiAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFRSVUUpICU+JSAgICAgICAgICAgICAgICAgICAjIHBydsO9IHN0xLpwZWMgKHJvaykgYnVkZSB0dcSNbsO9CiAgcm93X3NwZWMoMCwgYm9sZCA9IFRSVUUsIGJhY2tncm91bmQgPSAiI2YyZjJmMiIpICU+JSAgIyBobGF2acSNa2EgYnVkZSB0dcSNbsOhIGEgc28gc2l2w71tIHBvemFkw61tCiAgYWRkX2hlYWRlcl9hYm92ZShjKCIgIiA9IDIsICJCb2R5IFN0YXRpc3RpY3MiID0gNykpICAgICMgbmFkcGlzIHByZSBza3VwaW51IMWhdGF0aXN0w61rCmBgYAoKIyMgVC10ZXN0IOKAkyBwb3Jvdm5hbmllIHByaWVtZXJ1IGJvZG92IHYgZHZvY2ggcm9rb2NoCgpgYGB7cn0KIyB0LXRlc3QgcG9yb3ZuYW5pZSBwcmllbWVydSBib2RvdiB2IHJva29jaCAyMDIwIGEgMjAyNAp0LnRlc3QucmVzdWx0IDwtIHQudGVzdCgKICBmMSRCb2R5W2YxJFJvayA9PSAyMDIwXSwKICBmMSRCb2R5W2YxJFJvayA9PSAyMDI0XQopCgpwcmludCh0LnRlc3QucmVzdWx0KQpgYGAKCiMjIEFOT1ZBIOKAkyBwb3Jvdm5hbmllIHByaWVtZXJvdiBtZWR6aSB2aWFjZXLDvW1pIHJva21pCgpgYGB7cn0KIyBBTk9WQTogcm96ZGllbHkgdiBib2RvY2ggbWVkemkgcm9rbWkKYW5vdmEucmVzdWx0IDwtIGFvdihCb2R5IH4gYXMuZmFjdG9yKFJvayksIGRhdGEgPSBmMSkKc3VtbWFyeShhbm92YS5yZXN1bHQpCmBgYAoKVsO9c2xlZGt5IEFOT1ZBIHRlc3R1IChGID0gMC4wOTksIHAgPSAwLjk4MikgdWthenVqw7osIMW+ZSBtZWR6aSByb2ttaQoyMDIw4oCTMjAyNCBuZWV4aXN0dWplIMWhdGF0aXN0aWNreSB2w716bmFtbsO9IHJvemRpZWwgdiBwcmllbWVybm9tIHBvxI10ZQpib2RvdiBqYXpkY292IEYxLiBQcmllbWVyeSBib2RvdiBzYSBtZWR6aSBqZWRub3RsaXbDvW1pIHJva21pIG5lbMOtxaFpYQoKIyMgTGluZcOhcm5hIHJlZ3Jlc2lhIC0gcHJlZHBvdmVkYW5pZSBib2RvdgoKYGBge3J9CiMgTGluZcOhcm5hIHJlZ3Jlc2lhOiBwcmVkaWtjaWEgYm9kb3YgcG9kxL5hIHbDvWhpZXIgYSBwb2xlIHBvc2l0aW9uCm1vZGVsIDwtIGxtKEJvZHkgfiBWeWhyeSArIFBvbGVQb3NpdGlvbiwgZGF0YSA9IGYxKQpzdW1tYXJ5KG1vZGVsKQpgYGAK