knitr::opts_chunk$set(
echo = TRUE,
message = FALSE,
warning = FALSE
)
set.seed(2025)
Práca s údajmi
Tradičná práca s databázou
Meno <- c("Ela", "Tono", "Ria")
Vek <- c(10, 11, 9)
Body <- c(83, 96, 74)
udaje <- data.frame(Meno, Vek, Body)
udaje
Základné operácie
[1] 10 11 9
[1] 10
udaje[udaje$Meno == "Tono", ]
[1] "Ela"
Meno Vek Body
Length:3 Min. : 9.0 Min. :74.00
Class :character 1st Qu.: 9.5 1st Qu.:78.50
Mode :character Median :10.0 Median :83.00
Mean :10.0 Mean :84.33
3rd Qu.:10.5 3rd Qu.:89.50
Max. :11.0 Max. :96.00
Malé cvičenie
Z tabuľky zisti meno študenta s najvyšším počtom Body a vypíš
vetu
max_body <- max(udaje$Body)
meno_max <- udaje$Meno[which.max(udaje$Body)]
paste("Najviac bodov má", meno_max, ":", max_body)
[1] "Najviac bodov má Tono : 96"
Pridávanie stĺpcov a riadkov
MaAuto <- c(FALSE, TRUE, FALSE)
udaje <- cbind(udaje, MaAuto)
udaje
novy_riadok <- data.frame(Meno = "Fero", Vek = 12, Body = 88, MaAuto = TRUE)
udaje <- rbind(udaje, novy_riadok)
udaje
Malé cvičenie
Pridaj do tabuľky nového žiaka menom Nina, má 10 rokov, 91 bodov a
nemá auto. Keď ju pridáš, vypočítaj medián bodov všetkých žiakov.
udaje <- rbind(udaje, data.frame(Meno = "Nina", Vek = 10, Body = 91, MaAuto = FALSE))
median(udaje$Body)
[1] 88
Malé cvičenie
Z tabuľky si nechaj zobraziť len mená a body. Vytvor z toho peknú
tabuľku pomocou kable a nazvi ju „Body podľa mena“.
udaje[, c("Meno", "Body")] %>%
kable(caption = "Body podľa mena") %>%
kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE)
Body podľa mena
Meno |
Body |
Ela |
83 |
Tono |
96 |
Ria |
74 |
Fero |
88 |
Nina |
91 |
Tidyverse – moderná práca s údajmi
dplyr – výber a triedenie
udaje %>%
filter(Body > 80) %>%
arrange(desc(Body)) %>%
kable(caption = "Body > 80 – zostupne") %>%
kable_styling(full_width = FALSE)
Body > 80 – zostupne
Meno |
Vek |
Body |
MaAuto |
Tono |
11 |
96 |
TRUE |
Nina |
10 |
91 |
FALSE |
Fero |
12 |
88 |
TRUE |
Ela |
10 |
83 |
FALSE |
Malé cvičenie
Vyber len tých žiakov, ktorí majú aspoň 90 bodov. Zorad ich abecedne
podľa mena a nech sa zobrazí len ich stĺpec Meno.
udaje %>%
filter(Body >= 90) %>%
arrange(Meno) %>%
select(Meno) %>%
kable(caption = "Meno so ≥ 90 bodmi (A–Z)") %>%
kable_styling(full_width = FALSE)
Meno so ≥ 90 bodmi (A–Z)
Meno |
Nina |
Tono |
Zoskupenie a sumarizácia
udaje %>%
group_by(MaAuto) %>%
summarise(
Priem_Body = mean(Body),
Pocet = n()
) %>%
kable(
caption = "Priemerné Body podľa MaAuto",
col.names = c("Má Auto", "Priemer Body", "Počet"),
align = "c"
) %>%
kable_styling(full_width = FALSE)
Priemerné Body podľa MaAuto
Má Auto |
Priemer Body |
Počet |
FALSE |
82.66667 |
3 |
TRUE |
92.00000 |
2 |
Malé cvičenie
Zoskup žiakov podľa toho, či majú auto alebo nemajú. Pre každú
skupinu vypíš najvyšší počet bodov a priemerný vek.
udaje %>%
group_by(MaAuto) %>%
summarise(Max_Body = max(Body), Avg_Vek = mean(Vek)) %>%
kable(caption = "Max Body a priemerný vek podľa MaAuto") %>%
kable_styling(full_width = FALSE)
Max Body a priemerný vek podľa MaAuto
MaAuto |
Max_Body |
Avg_Vek |
FALSE |
91 |
9.666667 |
TRUE |
96 |
11.500000 |
Vytváranie novej premennej (mutate + case_when)
udaje %>%
mutate(
grade = case_when(
Body >= 90 ~ "A",
Body >= 80 ~ "B",
Body >= 70 ~ "C",
TRUE ~ "D"
),
VekPoPlnoletosti = pmax(0, Vek - 18)
) %>%
kable(caption = "Známky a VekPoPlnoletosti") %>%
kable_styling(full_width = FALSE)
Známky a VekPoPlnoletosti
Meno |
Vek |
Body |
MaAuto |
grade |
VekPoPlnoletosti |
Ela |
10 |
83 |
FALSE |
B |
0 |
Tono |
11 |
96 |
TRUE |
A |
0 |
Ria |
9 |
74 |
FALSE |
C |
0 |
Fero |
12 |
88 |
TRUE |
B |
0 |
Nina |
10 |
91 |
FALSE |
A |
0 |
Malé cvičenie
Pridaj do tabuľky nový stĺpec Uspech, ktorý bude mať hodnotu TRUE, ak
má žiak aspoň 85 bodov, inak FALSE. Potom ukáž prvé štyri riadky
tabuľky.
udaje %>%
mutate(Uspech = Body >= 85) %>%
head(4) %>%
kable(caption = "Uspech podľa Body (prvé 4)") %>%
kable_styling(full_width = FALSE)
Uspech podľa Body (prvé 4)
Meno |
Vek |
Body |
MaAuto |
Uspech |
Ela |
10 |
83 |
FALSE |
FALSE |
Tono |
11 |
96 |
TRUE |
TRUE |
Ria |
9 |
74 |
FALSE |
FALSE |
Fero |
12 |
88 |
TRUE |
TRUE |
Záverečné cvičenie – demonštrácia na vlastnom príklade
Vytvor rebríček „A-čkári“ – teda tých, ktorí majú 90 a viac bodov.
Zorad ich podľa počtu bodov a pridaj stĺpec Poradie, kde 1 = najviac
bodov. Zobraz len Meno a Poradie.
udaje %>%
filter(Body >= 90) %>%
arrange(desc(Body)) %>%
mutate(PoradieVBod = row_number()) %>%
select(Meno, PoradieVBod) %>%
kable(caption = "A-čkári a ich poradie v bodovom rebríčku") %>%
kable_styling(full_width = FALSE)
A-čkári a ich poradie v bodovom rebríčku
Meno |
PoradieVBod |
Tono |
1 |
Nina |
2 |
Môj návrh použitia novinky
set.seed(7)
udaje$Mat <- c(76, 98, 71, 85, 94)
udaje$Slov <- c(88, 91, 69, 90, 92)
udaje$Eng <- c(79, 95, 75, 86, 90)
udaje
Môj návrh použitia novinky (jednoduché): percentily + rebríček v
skupinách
Idea: spravíme percentilové poradie podľa
Body
(0–100 %) a zároveň rebríček v rámci
skupiny MaAuto
(kto má/nemá auto). Je to iný
pohľad než len „triedenie podľa bodov“ – uvidíš svoje postavenie v celej
triede aj v „podskupine“.
library(dplyr)
library(kableExtra)
# 1) Percentil podľa Body (0–100 %)
vysl_percentil <- udaje %>%
mutate(
Percentil_Body = round(100 * (rank(Body, ties.method = "min") - 1) / (n() - 1), 1)
) %>%
arrange(desc(Body)) %>%
select(Meno, Body, Percentil_Body)
kable(vysl_percentil, caption = "Percentilové poradie podľa Body (0–100 %)") %>%
kable_styling(full_width = FALSE)
Percentilové poradie podľa Body (0–100 %)
Meno |
Body |
Percentil_Body |
Tono |
96 |
100 |
Nina |
91 |
75 |
Fero |
88 |
50 |
Ela |
83 |
25 |
Ria |
74 |
0 |
# 2) Rebríček v rámci skupiny MaAuto (1 = najviac bodov v danej skupine)
vysl_skupiny <- udaje %>%
group_by(MaAuto) %>%
arrange(desc(Body), .by_group = TRUE) %>%
mutate(Poradie_v_skupine = dense_rank(desc(Body))) %>%
ungroup() %>%
select(Meno, MaAuto, Body, Poradie_v_skupine)
kable(vysl_skupiny, caption = "Rebríček v rámci skupiny MaAuto") %>%
kable_styling(full_width = FALSE)
Rebríček v rámci skupiny MaAuto
Meno |
MaAuto |
Body |
Poradie_v_skupine |
Nina |
FALSE |
91 |
1 |
Ela |
FALSE |
83 |
2 |
Ria |
FALSE |
74 |
3 |
Tono |
TRUE |
96 |
1 |
Fero |
TRUE |
88 |
2 |
# 3) Jednoduché pásma výkonu podľa percentilu (kvintily)
vysl_pasma <- vysl_percentil %>%
mutate(Pásmo =
cut(Percentil_Body,
breaks = c(-Inf, 20, 40, 60, 80, Inf),
labels = c("E (spodných 20%)", "D", "C", "B", "A (top 20%)"),
right = TRUE))
kable(vysl_pasma, caption = "Pásma výkonu podľa percentilu (A–E)") %>%
kable_styling(full_width = FALSE)
Pásma výkonu podľa percentilu (A–E)
Meno |
Body |
Percentil_Body |
Pásmo |
Tono |
96 |
100 |
A (top 20%) |
Nina |
91 |
75 |
B |
Fero |
88 |
50 |
C |
Ela |
83 |
25 |
D |
Ria |
74 |
0 |
E (spodných 20%) |
LS0tCnRpdGxlOiAiUHLDoWNhIHMgZGF0YWLDoXpvdSDigJMgY3ZpxI1lYm7DvSBkb2t1bWVudCAoYmV6IHZlcmVqbsO9Y2ggZGF0YWLDoXopIgphdXRob3I6ICJMw612aWEgTWVsaWNob3bDoSIKZGF0ZTogIk9jdG9iZXIgMjAyNSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRoZW1lOiB1bml0ZWQKICAgIGhpZ2hsaWdodDogdGFuZ28KZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCmhlYWRlci1pbmNsdWRlczoKICAtIHwKICAgIDxzdHlsZT4KICAgICAgYm9keSB7CiAgICAgICAgYmFja2dyb3VuZDoKICAgICAgICAgIHJhZGlhbC1ncmFkaWVudChjaXJjbGUgYXQgMjBweCAyMHB4LCAjZmZkMWU4IDZweCwgdHJhbnNwYXJlbnQgN3B4KSAwIDAvNjBweCA2MHB4LAogICAgICAgICAgcmFkaWFsLWdyYWRpZW50KGNpcmNsZSBhdCA1MHB4IDQwcHgsICNmZmU2ZjIgNnB4LCB0cmFuc3BhcmVudCA3cHgpIDAgMC82MHB4IDYwcHgsCiAgICAgICAgICByYWRpYWwtZ3JhZGllbnQoY2lyY2xlIGF0IDM1cHggMTVweCwgI2ZmZjBmNyAxMHB4LCB0cmFuc3BhcmVudCAxMXB4KSAwIDAvNjBweCA2MHB4LAogICAgICAgICAgI2ZmZmFmZDsKICAgICAgfQogICAgICBoMSwgaDIsIGgzLCBoNCwgaDUsIGg2IHsKICAgICAgICBjb2xvcjogI2U5MWU2MyAhaW1wb3J0YW50OwogICAgICAgIGxldHRlci1zcGFjaW5nOiAuMnB4OwogICAgICB9CiAgICAgIC50YXNrIHsKICAgICAgICBiYWNrZ3JvdW5kOiByZ2JhKDI1NSwyMjUsMjM5LC40NSk7CiAgICAgICAgYm9yZGVyOiAxcHggc29saWQgI2ZmYzFkZDsKICAgICAgICBib3JkZXItcmFkaXVzOiAxNnB4OwogICAgICAgIHBhZGRpbmc6IDEycHggMTZweDsKICAgICAgICBtYXJnaW46IDE0cHggMDsKICAgICAgfQogICAgICAubm90ZSB7IGNvbG9yOiAjZTkxZTYzOyBmb250LXdlaWdodDogNjAwOyB9CiAgICAgIGNvZGUgeyBib3JkZXItcmFkaXVzOiA2cHg7IH0KICAgIDwvc3R5bGU+Ci0tLQo8c3R5bGU+CmJvZHkgewogIGJhY2tncm91bmQ6IGxpbmVhci1ncmFkaWVudCgxMjBkZWcsICNmZmU2ZjAgMCUsICNmZmY4ZmIgMTAwJSk7CiAgYmFja2dyb3VuZC1pbWFnZTogdXJsKCdodHRwczovL2Nkbi5waXhhYmF5LmNvbS9waG90by8yMDE5LzAxLzA2LzExLzE0L2Zsb3dlci0zOTE2ODIzXzk2MF83MjAuanBnJyk7CiAgYmFja2dyb3VuZC1yZXBlYXQ6IHJlcGVhdDsKICBiYWNrZ3JvdW5kLXBvc2l0aW9uOiBjZW50ZXIgdG9wOwogIGJhY2tncm91bmQtc2l6ZTogY29udGFpbjsKICBjb2xvcjogIzNjMmEzYTsKICBmb250LWZhbWlseTogIkNvbWljIFNhbnMgTVMiLCAiU2Vnb2UgVUkiLCBzYW5zLXNlcmlmOwp9Cjwvc3R5bGU+CjxzdHlsZT4KaDEsIGgyLCBoMywgaDQsIGg1LCBoNiB7CiAgY29sb3I6ICNlNzU0ODA7IC8qIHJ1xb5vdsOhICovCn0KPC9zdHlsZT4KYGBge3J9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvID0gVFJVRSwKICBtZXNzYWdlID0gRkFMU0UsCiAgd2FybmluZyA9IEZBTFNFCikKc2V0LnNlZWQoMjAyNSkKYGBgCgoKCiMgUHLDoWNhIHMgw7pkYWptaQoKIyMgVHJhZGnEjW7DoSBwcsOhY2EgcyBkYXRhYsOhem91CgpgYGB7cn0KTWVubyA8LSBjKCJFbGEiLCAiVG9ubyIsICJSaWEiKQpWZWsgIDwtIGMoMTAsIDExLCA5KQpCb2R5IDwtIGMoODMsIDk2LCA3NCkKdWRhamUgPC0gZGF0YS5mcmFtZShNZW5vLCBWZWssIEJvZHkpCnVkYWplCmBgYAoKIyMjIFrDoWtsYWRuw6kgb3BlcsOhY2llCgpgYGB7cn0KdWRhamUkVmVrCm1lYW4odWRhamUkVmVrKQp1ZGFqZVt1ZGFqZSRNZW5vID09ICJUb25vIiwgXQp1ZGFqZVszLCBdCnVkYWplWywgMjozXQp1ZGFqZVsxLCAxXQpzdW1tYXJ5KHVkYWplKQpgYGAKCiMjIE1hbMOpIGN2acSNZW5pZQoKWiB0YWJ1xL5reSB6aXN0aSBtZW5vIMWhdHVkZW50YSBzIG5hanZ5xaHFocOtbSBwb8SNdG9tIEJvZHkgYSB2eXDDrcWhIHZldHUKCmBgYHtyfQptYXhfYm9keSA8LSBtYXgodWRhamUkQm9keSkKbWVub19tYXggPC0gdWRhamUkTWVub1t3aGljaC5tYXgodWRhamUkQm9keSldCnBhc3RlKCJOYWp2aWFjIGJvZG92IG3DoSIsIG1lbm9fbWF4LCAiOiIsIG1heF9ib2R5KQpgYGAKCi0tLQoKIyMjIFByaWTDoXZhbmllIHN0xLpwY292IGEgcmlhZGtvdgoKYGBge3J9Ck1hQXV0byA8LSBjKEZBTFNFLCBUUlVFLCBGQUxTRSkKdWRhamUgPC0gY2JpbmQodWRhamUsIE1hQXV0bykKdWRhamUKYGBgCgpgYGB7cn0Kbm92eV9yaWFkb2sgPC0gZGF0YS5mcmFtZShNZW5vID0gIkZlcm8iLCBWZWsgPSAxMiwgQm9keSA9IDg4LCBNYUF1dG8gPSBUUlVFKQp1ZGFqZSA8LSByYmluZCh1ZGFqZSwgbm92eV9yaWFkb2spCnVkYWplCmBgYAoKIyMgTWFsw6kgY3ZpxI1lbmllClByaWRhaiBkbyB0YWJ1xL5reSBub3bDqWhvIMW+aWFrYSBtZW5vbSBOaW5hLCBtw6EgMTAgcm9rb3YsIDkxIGJvZG92IGEgbmVtw6EgYXV0by4KS2XEjyBqdSBwcmlkw6HFoSwgdnlwb8SNw610YWogbWVkacOhbiBib2RvdiB2xaFldGvDvWNoIMW+aWFrb3YuCgpgYGB7cn0KdWRhamUgPC0gcmJpbmQodWRhamUsIGRhdGEuZnJhbWUoTWVubyA9ICJOaW5hIiwgVmVrID0gMTAsIEJvZHkgPSA5MSwgTWFBdXRvID0gRkFMU0UpKQptZWRpYW4odWRhamUkQm9keSkKYGBgCgotLS0KCiMjIyBUYWJ1xL5reSB2IHByb3N0cmVkw60ga2FibGVFeHRyYQoKYGBge3J9CmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKCmthYmxlKAogIHVkYWplLAogIGRpZ2l0cyA9IDAsCiAgYWxpZ24gPSBjKCJsIiwiYyIsInIiLCJjIiksCiAgY2FwdGlvbiA9ICJQcmVoxL5hZCDFvmlha292IOKAkyBtb2plIGTDoXRhIgopICU+JQogIGthYmxlX3N0eWxpbmcoCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIKICApCmBgYAoKIyMgTWFsw6kgY3ZpxI1lbmllClogdGFidcS+a3kgc2kgbmVjaGFqIHpvYnJhemnFpSBsZW4gbWVuw6EgYSBib2R5LgpWeXR2b3IgeiB0b2hvIHBla27DuiB0YWJ1xL5rdSBwb21vY291IGthYmxlIGEgbmF6dmkganUg4oCeQm9keSBwb2TEvmEgbWVuYeKAnC4KCmBgYHtyfQp1ZGFqZVssIGMoIk1lbm8iLCAiQm9keSIpXSAlPiUKICBrYWJsZShjYXB0aW9uID0gIkJvZHkgcG9kxL5hIG1lbmEiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkKYGBgCgotLS0KCiMjIFRpZHl2ZXJzZSDigJMgbW9kZXJuw6EgcHLDoWNhIHMgw7pkYWptaQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKIyMjIGRwbHlyIOKAkyB2w71iZXIgYSB0cmllZGVuaWUKCmBgYHtyfQp1ZGFqZSAlPiUKICBmaWx0ZXIoQm9keSA+IDgwKSAlPiUKICBhcnJhbmdlKGRlc2MoQm9keSkpICU+JQogIGthYmxlKGNhcHRpb24gPSAiQm9keSA+IDgwIOKAkyB6b3N0dXBuZSIpICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFKQpgYGAKCiMjIE1hbMOpIGN2acSNZW5pZQpWeWJlciBsZW4gdMO9Y2ggxb5pYWtvdiwga3RvcsOtIG1hasO6IGFzcG/FiCA5MCBib2Rvdi4KWm9yYWQgaWNoIGFiZWNlZG5lIHBvZMS+YSBtZW5hIGEgbmVjaCBzYSB6b2JyYXrDrSBsZW4gaWNoIHN0xLpwZWMgTWVuby4KCmBgYHtyfQp1ZGFqZSAlPiUKICBmaWx0ZXIoQm9keSA+PSA5MCkgJT4lCiAgYXJyYW5nZShNZW5vKSAlPiUKICBzZWxlY3QoTWVubykgJT4lCiAga2FibGUoY2FwdGlvbiA9ICJNZW5vIHNvIOKJpSA5MCBib2RtaSAoQeKAk1opIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UpCmBgYAoKLS0tCgojIyMgWm9za3VwZW5pZSBhIHN1bWFyaXrDoWNpYQoKYGBge3J9CnVkYWplICU+JQogIGdyb3VwX2J5KE1hQXV0bykgJT4lCiAgc3VtbWFyaXNlKAogICAgUHJpZW1fQm9keSA9IG1lYW4oQm9keSksCiAgICBQb2NldCA9IG4oKQogICkgJT4lCiAga2FibGUoCiAgICBjYXB0aW9uID0gIlByaWVtZXJuw6kgQm9keSBwb2TEvmEgTWFBdXRvIiwKICAgIGNvbC5uYW1lcyA9IGMoIk3DoSBBdXRvIiwgIlByaWVtZXIgQm9keSIsICJQb8SNZXQiKSwKICAgIGFsaWduID0gImMiCiAgKSAlPiUKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSkKYGBgCgojIyBNYWzDqSBjdmnEjWVuaWUKWm9za3VwIMW+aWFrb3YgcG9kxL5hIHRvaG8sIMSNaSBtYWrDuiBhdXRvIGFsZWJvIG5lbWFqw7ouClByZSBrYcW+ZMO6IHNrdXBpbnUgdnlww63FoSBuYWp2ecWhxaHDrSBwb8SNZXQgYm9kb3YgYSBwcmllbWVybsO9IHZlay4KCmBgYHtyfQp1ZGFqZSAlPiUKICBncm91cF9ieShNYUF1dG8pICU+JQogIHN1bW1hcmlzZShNYXhfQm9keSA9IG1heChCb2R5KSwgQXZnX1ZlayA9IG1lYW4oVmVrKSkgJT4lCiAga2FibGUoY2FwdGlvbiA9ICJNYXggQm9keSBhIHByaWVtZXJuw70gdmVrIHBvZMS+YSBNYUF1dG8iKSAlPiUKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSkKYGBgCgotLS0KCiMjIyBWeXR2w6FyYW5pZSBub3ZlaiBwcmVtZW5uZWogKG11dGF0ZSArIGNhc2Vfd2hlbikKCmBgYHtyfQp1ZGFqZSAlPiUKICBtdXRhdGUoCiAgICBncmFkZSA9IGNhc2Vfd2hlbigKICAgICAgQm9keSA+PSA5MCB+ICJBIiwKICAgICAgQm9keSA+PSA4MCB+ICJCIiwKICAgICAgQm9keSA+PSA3MCB+ICJDIiwKICAgICAgVFJVRSB+ICJEIgogICAgKSwKICAgIFZla1BvUGxub2xldG9zdGkgPSBwbWF4KDAsIFZlayAtIDE4KQogICkgJT4lCiAga2FibGUoY2FwdGlvbiA9ICJabsOhbWt5IGEgVmVrUG9QbG5vbGV0b3N0aSIpICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFKQpgYGAKCiMjIE1hbMOpIGN2acSNZW5pZQpQcmlkYWogZG8gdGFidcS+a3kgbm92w70gc3TEunBlYyBVc3BlY2gsCmt0b3LDvSBidWRlIG1hxaUgaG9kbm90dSBUUlVFLCBhayBtw6Egxb5pYWsgYXNwb8WIIDg1IGJvZG92LAppbmFrIEZBTFNFLiBQb3RvbSB1a8Ohxb4gcHJ2w6kgxaF0eXJpIHJpYWRreSB0YWJ1xL5reS4KCmBgYHtyfQp1ZGFqZSAlPiUKICBtdXRhdGUoVXNwZWNoID0gQm9keSA+PSA4NSkgJT4lCiAgaGVhZCg0KSAlPiUKICBrYWJsZShjYXB0aW9uID0gIlVzcGVjaCBwb2TEvmEgQm9keSAocHJ2w6kgNCkiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSkKYGBgCgotLS0KCiMgWsOhdmVyZcSNbsOpIGN2acSNZW5pZSDigJMgZGVtb27FoXRyw6FjaWEgbmEgdmxhc3Rub20gcHLDrWtsYWRlCgpWeXR2b3IgcmVicsOtxI1layDigJ5BLcSNa8OhcmnigJwg4oCTIHRlZGEgdMO9Y2gsIGt0b3LDrSBtYWrDuiA5MCBhIHZpYWMgYm9kb3YuClpvcmFkIGljaCBwb2TEvmEgcG/EjXR1IGJvZG92IGEgcHJpZGFqIHN0xLpwZWMgUG9yYWRpZSwga2RlIDEgPSBuYWp2aWFjIGJvZG92Lgpab2JyYXogbGVuIE1lbm8gYSBQb3JhZGllLgoKYGBge3J9CnVkYWplICU+JQogIGZpbHRlcihCb2R5ID49IDkwKSAlPiUKICBhcnJhbmdlKGRlc2MoQm9keSkpICU+JQogIG11dGF0ZShQb3JhZGllVkJvZCA9IHJvd19udW1iZXIoKSkgJT4lCiAgc2VsZWN0KE1lbm8sIFBvcmFkaWVWQm9kKSAlPiUKICBrYWJsZShjYXB0aW9uID0gIkEtxI1rw6FyaSBhIGljaCBwb3JhZGllIHYgYm9kb3ZvbSByZWJyw63EjWt1IikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UpCmBgYAoKLS0tCgojIE3DtGogbsOhdnJoIHBvdcW+aXRpYSBub3Zpbmt5CgpgYGB7cn0Kc2V0LnNlZWQoNykKdWRhamUkTWF0ICA8LSBjKDc2LCA5OCwgNzEsIDg1LCA5NCkKdWRhamUkU2xvdiA8LSBjKDg4LCA5MSwgNjksIDkwLCA5MikKdWRhamUkRW5nICA8LSBjKDc5LCA5NSwgNzUsIDg2LCA5MCkKdWRhamUKYGBgCiMgTcO0aiBuw6F2cmggcG91xb5pdGlhIG5vdmlua3kgKGplZG5vZHVjaMOpKTogcGVyY2VudGlseSArIHJlYnLDrcSNZWsgdiBza3VwaW7DoWNoCgo+IElkZWE6IHNwcmF2w61tZSAqKnBlcmNlbnRpbG92w6kgcG9yYWRpZSoqIHBvZMS+YSBgQm9keWAgKDDigJMxMDAgJSkgYSB6w6Fyb3ZlxYgKPiAqKnJlYnLDrcSNZWsgdiByw6FtY2kgc2t1cGlueSoqIGBNYUF1dG9gIChrdG8gbcOhL25lbcOhIGF1dG8pLiBKZSB0byBpbsO9IHBvaMS+YWQgbmXFviBsZW4KPiDigJ50cmllZGVuaWUgcG9kxL5hIGJvZG924oCcIOKAkyB1dmlkw63FoSBzdm9qZSBwb3N0YXZlbmllIHYgY2VsZWogdHJpZWRlIGFqIHYg4oCecG9kc2t1cGluZeKAnC4KCmBgYHtyfQoKbGlicmFyeShkcGx5cikKbGlicmFyeShrYWJsZUV4dHJhKQoKIyAxKSBQZXJjZW50aWwgcG9kxL5hIEJvZHkgKDDigJMxMDAgJSkKdnlzbF9wZXJjZW50aWwgPC0gdWRhamUgJT4lCiAgbXV0YXRlKAogICAgUGVyY2VudGlsX0JvZHkgPSByb3VuZCgxMDAgKiAocmFuayhCb2R5LCB0aWVzLm1ldGhvZCA9ICJtaW4iKSAtIDEpIC8gKG4oKSAtIDEpLCAxKQogICkgJT4lCiAgYXJyYW5nZShkZXNjKEJvZHkpKSAlPiUKICBzZWxlY3QoTWVubywgQm9keSwgUGVyY2VudGlsX0JvZHkpCgprYWJsZSh2eXNsX3BlcmNlbnRpbCwgY2FwdGlvbiA9ICJQZXJjZW50aWxvdsOpIHBvcmFkaWUgcG9kxL5hIEJvZHkgKDDigJMxMDAgJSkiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSkKCmBgYAoKYGBge3J9CiMgMikgUmVicsOtxI1layB2IHLDoW1jaSBza3VwaW55IE1hQXV0byAoMSA9IG5hanZpYWMgYm9kb3YgdiBkYW5laiBza3VwaW5lKQp2eXNsX3NrdXBpbnkgPC0gdWRhamUgJT4lCiAgZ3JvdXBfYnkoTWFBdXRvKSAlPiUKICBhcnJhbmdlKGRlc2MoQm9keSksIC5ieV9ncm91cCA9IFRSVUUpICU+JQogIG11dGF0ZShQb3JhZGllX3Zfc2t1cGluZSA9IGRlbnNlX3JhbmsoZGVzYyhCb2R5KSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBzZWxlY3QoTWVubywgTWFBdXRvLCBCb2R5LCBQb3JhZGllX3Zfc2t1cGluZSkKCmthYmxlKHZ5c2xfc2t1cGlueSwgY2FwdGlvbiA9ICJSZWJyw63EjWVrIHYgcsOhbWNpIHNrdXBpbnkgTWFBdXRvIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UpCgpgYGAKYGBge3J9CiMgMykgSmVkbm9kdWNow6kgcMOhc21hIHbDvWtvbnUgcG9kxL5hIHBlcmNlbnRpbHUgKGt2aW50aWx5KQp2eXNsX3Bhc21hIDwtIHZ5c2xfcGVyY2VudGlsICU+JQogIG11dGF0ZShQw6FzbW8gPQogICAgICAgICAgIGN1dChQZXJjZW50aWxfQm9keSwKICAgICAgICAgICAgICAgYnJlYWtzID0gYygtSW5mLCAyMCwgNDAsIDYwLCA4MCwgSW5mKSwKICAgICAgICAgICAgICAgbGFiZWxzID0gYygiRSAoc3BvZG7DvWNoIDIwJSkiLCAiRCIsICJDIiwgIkIiLCAiQSAodG9wIDIwJSkiKSwKICAgICAgICAgICAgICAgcmlnaHQgPSBUUlVFKSkKCmthYmxlKHZ5c2xfcGFzbWEsIGNhcHRpb24gPSAiUMOhc21hIHbDvWtvbnUgcG9kxL5hIHBlcmNlbnRpbHUgKEHigJNFKSIpICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFKQoKYGBgCgo=