Globálne nastavenie Chunkov
V nižšie uvedenom Chunku je urobené základné globálne nastavenie
Chunkov v celom Notebooku.
- echo nastavuje, či chceme v Notebooku vypisovať
jednotlivé kódy R.
- message = FALSE potláča pracovné hlásenia (napr.
pri načítaní knižníc).
- warning = FALSE potláča varovné hlášky.
knitr::opts_chunk$set(
echo = TRUE,
message = FALSE,
warning = FALSE
)
Úvod k základným operáciám v R
Tento notebook demonštruje základné operácie v
jazyku R so:
- skalárnymi číslami (jednočíselné hodnoty),
- textovými (znakovými) reťazcami,
- logickými (boolovskými) hodnotami,
- numerickými vektormi,
- maticami.
Tam, kde je to užitočné, sú zahrnuté aj malé cvičenia (## Malé
cvičenie), v ktorých používam podobné príkazy na vlastných
príkladoch.
Skaláre (jednočíselné hodnoty)
Numerické skaláre
# Priradenie konštánt do premenných
a <- 7
b <- 3.5
# Základné aritmetické operácie
sum_ab <- a + b # súčet
diff_ab <- a - b # rozdiel
prod_ab <- a * b # násobenie
quot_ab <- a / b # delenie
power_ab <- a ^ b # umocňovanie
mod_ab <- a %% 3 # zvyšok po delení tromi (modulo)
int_div_ab <- a %/% 3 # celočíselné delenie (bez zvyšku)
# Zaokrúhľovanie
round_b <- round(b) # zaokrúhlenie na najbližšie celé číslo
ceil_b <- ceiling(b) # najbližšie vyššie celé číslo
floor_b <- floor(b) # najbližšie nižšie celé číslo
a; b
sum_ab; diff_ab; prod_ab; quot_ab; power_ab; mod_ab; int_div_ab
round_b; ceil_b; floor_b
Poznámky
^ je operátor umocňovania.
%% je modulo – zvyšok po delení.
%/% je celočíselné delenie (delenie „bez desatinnej
časti“).
round(x, digits = 0) zaokrúhli na zadaný počet
desatinných miest, ak digits = 0, ide o celočíselné
zaokrúhlenie.
Malé cvičenie
Vypočítajte hodnotu výrazu
\[
\frac{(15^2 - 4)}{7}
\]
a výsledok uložte do premennej vysledok15.
vysledok15 <- (15^2 - 4) / 7
vysledok15
Text
Vytváranie textových premenných a práca s nimi
first <- "Natalia" # meno
last <- "Soligova" # priezvisko
full_name <- paste(first, last) # spojenie s medzerou
full_nospace <- paste0(first, last) # spojenie bez medzery
csv_line <- paste("apple", "banana", "pear", sep = ",")
semicolon_line <- paste("Ahoj", "svet", sep = ";")
first; last
full_name; full_nospace
csv_line
semicolon_line
Dĺžka textového reťazca a podreťazec
x <- "R je skvely jazyk!"
nchar(x) # počet znakov v reťazci
substr(x, 1, 5) # podreťazec od 1. do 5. znaku
substr(x, 8, nchar(x)) # podreťazec od 8. po koniec
Tip: knižnica stringr ponúka ešte viac užitočných
funkcií pre prácu s textom (str_to_upper(),
str_replace(), …), ale na základné úlohy stačia aj vstavané
funkcie R.
Malé cvičenie
Vytvorte textovú premennú veta s obsahom
"Ekonometria je zaujimava" a: 1. zistite počet znakov, 2.
vytiahnite prvých 10 znakov.
veta <- "Ekonometria je zaujimava"
pocet_znakov <- nchar(veta)
prvych_desat <- substr(veta, 1, 10)
pocet_znakov
prvych_desat
Logické (boolovské) hodnoty a premenné
Základy
p <- TRUE
q <- FALSE
!p # NOT
p & q # AND
p | q # OR
xor(p, q) # exclusive OR - platí presne jedno z p, q
Logický výsledok porovnávania
3 < 5
7 >= 7
"cat" == "cat"
"cat" != "dog" # != znamená "nie je rovné"
!TRUE
Zložitejšie logické operácie
x <- 10
x > 5 & x < 20 # x je väčšie ako 5 a zároveň menšie ako 20
x < 0 | x > 100 # x je menšie ako 0 alebo väčšie ako 100
# pri zložitejších výrazoch je dobré používať zátvorky
(x > 5 & x < 20) | (x == 0)
Zlučovanie viacerých logických premenných do vektora
vals <- c(TRUE, FALSE, TRUE, TRUE)
vals
Malé cvičenie
Majme číslo y <- 25. Pomocou logických operácií
zistite, či: 1. je väčšie ako 10 a zároveň menšie alebo rovné 30, 2. nie
je rovné 0.
y <- 25
podmienka1 <- (y > 10 & y <= 30)
podmienka2 <- (y != 0)
podmienka1
podmienka2
Numerické vektory
Generovanie vektorov
v1 <- c(2, 4, 6, 8)
v2 <- 1:5 # postupnosť 1,2,3,4,5
v3 <- seq(from = 0, to = 1, by = 0.25) # postupnosť s krokom 0.25
v4 <- rep(3, times = 5) # 3,3,3,3,3
v5 <- runif(5) # rovnomerné rozdelenie v [0,1]
v6 <- rnorm(5) # normálne rozdelené náhodné hodnoty
v1; v2; v3; v4; v5; v6
Aritmetické operácie s vektormi
v <- c(1, 2, 3, 4)
v + 10 # každý prvok +10
v * 2 # každý prvok *2
(v + 1) / 2
exp(v) # exponenciálna funkcia z každého prvku
# rôzne spôsoby skalárneho súčinu dvoch vektorov
sum(c(1,2,3) * c(1,1,1)) # skalárny súčin - výsledok je skalar
crossprod(c(1,2,3), c(1,1,1)) # skalárny súčin - výsledok je matica 1x1
c(1,2,3) * c(1,1,1) # Hadamardov súčin (po prvkoch)
Matematické operácie s dvoma vektormi rovnakého rozmeru
length(c(1,2,3,4,5))
length(v5)
c(1,2,3,4,5) + v5 # oba vektory musia mať rovnakú dĺžku
Indexovanie a výber niektorých prvkov vektora
x <- c(5, 12, 3, 18, 7, 0, 21)
x[1] # prvý prvok
x[2:4] # druhý až štvrtý prvok
x[-1] # všetko okrem prvého prvku
x[x > 10] # prvky väčšie ako 10
which(x > 10) # indexy prvkov väčších ako 10
Práca s chýbajúcimi hodnotami
y <- c(1, NA, 3, NA, 5)
is.na(y) # logický vektor, kde je NA
mean(y) # výsledok NA
mean(y, na.rm = TRUE) # ignorovanie NA pri výpočte priemeru
Základné štatistiky a usporiadanie prvkov
z <- c(10, 3, 5, 8, 2)
mean(z) # priemer
sd(z) # štandardná odchýlka
max(z) # maximum
summary(z) # rýchly prehľad
sort(z) # rastúce usporiadanie
sort(z, decreasing = TRUE) # klesajúco
Jednoduchý graf z vektora
plot(z,
type = "b",
main = "Jednoduchý lineárny graf vektora z",
xlab = "Index prvku",
ylab = "Hodnota",
col = "darkgreen",
pch = 19)
Malé cvičenie
Vytvorte vektor w s číslami od 1 do 20 a vypočítajte
súčet všetkých párnych čísel vo vektore.
Potom vypočítajte aj priemer všetkých nepárnych čísel.
w <- 1:20
suma_parne <- sum(w[w %% 2 == 0])
priemer_nepar <- mean(w[w %% 2 == 1])
suma_parne
priemer_nepar
Matice
Vytvorenie matíc
m <- matrix(1:12, nrow = 3, ncol = 4) # hodnoty po stĺpcoch
m_byrow <- matrix(1:12, nrow = 3, byrow = TRUE) # hodnoty po riadkoch
m
m_byrow
Rozmery matice
dim(m) # počet riadkov a stĺpcov
Adresovanie prvkov matice
m[1, 2] # riadok 1, stĺpec 2
m[ , 3] # všetky prvky v treťom stĺpci
m[2, ] # všetky prvky v druhom riadku
m[1:2, 2:3] # podmatica riadky 1-2, stĺpce 2-3
Maticové operácie
A <- matrix(c(1, 2, 3, 4), nrow = 2)
B <- matrix(c(5, 6, 7, 8), nrow = 2)
A + B # sčítanie matíc
A * B # Hadamardov súčin (po prvkoch)
A %*% B # skutočné násobenie matíc
t(A) # transpozícia
det(A) # determinant
solve(A) # inverzia matice (ak existuje)
Zlučovanie vektorov do matíc
C <- cbind(1:3, 4:6) # po stĺpcoch
D <- rbind(1:3, 4:6) # po riadkoch
C
D
Vypočítanie štatistiky po riadkoch / stĺpcoch
M <- matrix(1:9, nrow = 3)
M
apply(M, 1, sum) # suma po riadkoch
apply(M, 2, mean) # priemery po stĺpcoch
Malé cvičenie
Vytvorte maticu M2 s rozmermi 5×5, hodnoty 1..25
zadávané po riadkoch.
a) vypočítajte stĺpcové sumy,
b) vypočítajte súčin \(M2^T M2\).
M2 <- matrix(1:25, nrow = 5, byrow = TRUE)
stlpce_sum <- colSums(M2)
sucin_M2 <- t(M2) %*% M2
M2
stlpce_sum
sucin_M2
Môj návrh použitia novinky
V tejto poslednej sekcii ukazujem nové príkazy,
ktoré v pôvodnom Rmd dokumente neboli:
- vytvorenie
data.frame z viacerých vektorov,
- výpočet korelácie pomocou
cor(),
- jednoduchý regresný model pomocou
lm() a graf so
spätnou regresnou čiarou,
- použitie
ifelse() na vytvorenie kategórie
“úspešný/neúspešný študent”,
- zobrazenie výsledku pomocou
table() a
barplot().
Simulácia dát o štúdiu a výsledkoch testu
set.seed(42)
pocet_studentov <- 30
hodiny_studia <- runif(pocet_studentov, min = 0, max = 10)
body_test <- 20 + 6 * hodiny_studia + rnorm(pocet_studentov, sd = 5)
data_test <- data.frame(
hodiny = hodiny_studia,
body = body_test
)
head(data_test)
Korelácia a jednoduchý regresný model
cor_hodiny_body <- cor(data_test$hodiny, data_test$body)
cor_hodiny_body
model_test <- lm(body ~ hodiny, data = data_test)
summary(model_test)
Graf so spätnou regresnou čiarou
plot(
body ~ hodiny,
data = data_test,
pch = 19,
col = "darkblue",
main = "Čím viac študujem, tým viac bodov?",
xlab = "Hodiny štúdia za týždeň",
ylab = "Počet bodov v teste"
)
abline(model_test, col = "red", lwd = 2)
Použitie ifelse() a barplot()
Predpokladajme, že na test treba aspoň 60 bodov.
data_test$status <- ifelse(data_test$body >= 60, "úspešný", "neúspešný")
tab_status <- table(data_test$status)
tab_status
barplot(
tab_status,
main = "Počet úspešných a neúspešných študentov",
ylab = "Počet študentov",
col = c("tomato", "steelblue")
)
V tejto sekcii som teda použila nové príkazy, ktoré
v pôvodnom dokumente neboli, a ukázala:
- ako vytvoriť
data.frame,
- ako zistiť koreláciu
cor(),
- ako odhadnúť jednoduchý model
lm() a zobraziť ho v
grafe,
- ako vytvoriť novú kategóriu pomocou
ifelse(),
- ako použiť
table() a barplot() na
zobrazenie výsledkov.
LS0tCnRpdGxlOiAiMiBaQURBTklFICIKYXV0aG9yOiAiTmF0w6FsaWEgU29saWdvdsOhIgpkYXRlOiAiTk9WRU1CRVIgMjAyNSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRoZW1lOiB1bml0ZWQKICAgIGhpZ2hsaWdodDogdGFuZ28KZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCi0tLQoKIyBHbG9iw6FsbmUgbmFzdGF2ZW5pZSBDaHVua292CgpWIG5pxb7FoWllIHV2ZWRlbm9tIENodW5rdSBqZSB1cm9iZW7DqSB6w6FrbGFkbsOpIGdsb2LDoWxuZSBuYXN0YXZlbmllIENodW5rb3YgdiBjZWxvbQpOb3RlYm9va3UuIAoKLSAqKmVjaG8qKiBuYXN0YXZ1amUsIMSNaSBjaGNlbWUgdiBOb3RlYm9va3UgdnlwaXNvdmHFpSBqZWRub3RsaXbDqSBrw7NkeSBSLgotICoqbWVzc2FnZSA9IEZBTFNFKiogcG90bMOhxI1hIHByYWNvdm7DqSBobMOhc2VuaWEgKG5hcHIuIHByaSBuYcSNw610YW7DrSBrbmnFvm7DrWMpLgotICoqd2FybmluZyA9IEZBTFNFKiogcG90bMOhxI1hIHZhcm92bsOpIGhsw6HFoWt5LgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9VFJVRSwgZWNobz1UUlVFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobyA9IFRSVUUsCiAgbWVzc2FnZSA9IEZBTFNFLAogIHdhcm5pbmcgPSBGQUxTRQopCmBgYAoKIyDDmnZvZCBrIHrDoWtsYWRuw71tIG9wZXLDoWNpw6FtIHYgUgoKVGVudG8gbm90ZWJvb2sgZGVtb27FoXRydWplICoqesOha2xhZG7DqSBvcGVyw6FjaWUqKiB2IGphenlrdSBSIHNvOgoKLSBza2Fsw6FybnltaSDEjcOtc2xhbWkgKGplZG5vxI3DrXNlbG7DqSBob2Rub3R5KSwKLSB0ZXh0b3bDvW1pICh6bmFrb3bDvW1pKSByZcWlYXpjYW1pLAotIGxvZ2lja8O9bWkgKGJvb2xvdnNrw71taSkgaG9kbm90YW1pLAotIG51bWVyaWNrw71taSB2ZWt0b3JtaSwKLSBtYXRpY2FtaS4KClRhbSwga2RlIGplIHRvIHXFvml0b8SNbsOpLCBzw7ogemFocm51dMOpIGFqIG1hbMOpIGN2acSNZW5pYSAoKiMjIE1hbMOpIGN2acSNZW5pZSopLAp2IGt0b3LDvWNoIHBvdcW+w612YW0gcG9kb2Juw6kgcHLDrWthenkgbmEgdmxhc3Ruw71jaCBwcsOta2xhZG9jaC4KCi0tLQoKIyBTa2Fsw6FyZSAoamVkbm/EjcOtc2VsbsOpIGhvZG5vdHkpCgojIyBOdW1lcmlja8OpIHNrYWzDoXJlCgpgYGB7ciBzY2FsYXJzfQojIFByaXJhZGVuaWUga29uxaF0w6FudCBkbyBwcmVtZW5uw71jaAphIDwtIDcKYiA8LSAzLjUKCiMgWsOha2xhZG7DqSBhcml0bWV0aWNrw6kgb3BlcsOhY2llCnN1bV9hYiAgICA8LSBhICsgYiAgICAgICAgIyBzw7rEjWV0CmRpZmZfYWIgICA8LSBhIC0gYiAgICAgICAgIyByb3pkaWVsCnByb2RfYWIgICA8LSBhICogYiAgICAgICAgIyBuw6Fzb2JlbmllCnF1b3RfYWIgICA8LSBhIC8gYiAgICAgICAgIyBkZWxlbmllCnBvd2VyX2FiICA8LSBhIF4gYiAgICAgICAgIyB1bW9jxYhvdmFuaWUKbW9kX2FiICAgIDwtIGEgJSUgMyAgICAgICAjIHp2ecWhb2sgcG8gZGVsZW7DrSB0cm9taSAobW9kdWxvKQppbnRfZGl2X2FiIDwtIGEgJS8lIDMgICAgICMgY2Vsb8SNw61zZWxuw6kgZGVsZW5pZSAoYmV6IHp2ecWha3UpCgojIFphb2tyw7poxL5vdmFuaWUKcm91bmRfYiA8LSByb3VuZChiKSAgICAgICAjIHphb2tyw7pobGVuaWUgbmEgbmFqYmxpxb7FoWllIGNlbMOpIMSNw61zbG8KY2VpbF9iICA8LSBjZWlsaW5nKGIpICAgICAjIG5hamJsacW+xaFpZSB2ecWhxaFpZSBjZWzDqSDEjcOtc2xvCmZsb29yX2IgPC0gZmxvb3IoYikgICAgICAgIyBuYWpibGnFvsWhaWUgbmnFvsWhaWUgY2Vsw6kgxI3DrXNsbwoKYTsgYgpzdW1fYWI7IGRpZmZfYWI7IHByb2RfYWI7IHF1b3RfYWI7IHBvd2VyX2FiOyBtb2RfYWI7IGludF9kaXZfYWIKcm91bmRfYjsgY2VpbF9iOyBmbG9vcl9iCmBgYAoKKipQb3puw6Fta3kqKgoKLSBgXmAgamUgb3BlcsOhdG9yIHVtb2PFiG92YW5pYS4KLSBgJSVgIGplIG1vZHVsbyDigJMgenZ5xaFvayBwbyBkZWxlbsOtLgotIGAlLyVgIGplIGNlbG/EjcOtc2VsbsOpIGRlbGVuaWUgKGRlbGVuaWUg4oCeYmV6IGRlc2F0aW5uZWogxI1hc3Rp4oCcKS4KLSBgcm91bmQoeCwgZGlnaXRzID0gMClgIHphb2tyw7pobGkgbmEgemFkYW7DvSBwb8SNZXQgZGVzYXRpbm7DvWNoIG1pZXN0LAogIGFrIGBkaWdpdHMgPSAwYCwgaWRlIG8gY2Vsb8SNw61zZWxuw6kgemFva3LDumhsZW5pZS4KCiMjIE1hbMOpIGN2acSNZW5pZQoKPiBWeXBvxI3DrXRhanRlIGhvZG5vdHUgdsO9cmF6dSAgCj4gXFsKPiBcZnJhY3soMTVeMiAtIDQpfXs3fQo+IFxdICAKPiBhIHbDvXNsZWRvayB1bG/FvnRlIGRvIHByZW1lbm5laiBgdnlzbGVkb2sxNWAuCgpgYGB7ciBzY2FsYXJzX2V4ZXJjaXNlfQp2eXNsZWRvazE1IDwtICgxNV4yIC0gNCkgLyA3CnZ5c2xlZG9rMTUKYGBgCgotLS0KCiMgVGV4dCAKCiMjIFZ5dHbDoXJhbmllIHRleHRvdsO9Y2ggcHJlbWVubsO9Y2ggYSBwcsOhY2EgcyBuaW1pCgpgYGB7ciB0ZXh0X2Jhc2ljc30KZmlyc3QgPC0gIk5hdGFsaWEiICAgICAgICAgICAgICAgICAgICAgICMgbWVubwpsYXN0ICA8LSAiU29saWdvdmEiICAgICAgICAgICAgICAgICAgICAgIyBwcmllenZpc2tvCgpmdWxsX25hbWUgICAgICAgPC0gcGFzdGUoZmlyc3QsIGxhc3QpICAgICAgICAjIHNwb2plbmllIHMgbWVkemVyb3UKZnVsbF9ub3NwYWNlICAgIDwtIHBhc3RlMChmaXJzdCwgbGFzdCkgICAgICAgIyBzcG9qZW5pZSBiZXogbWVkemVyeQpjc3ZfbGluZSAgICAgICAgPC0gcGFzdGUoImFwcGxlIiwgImJhbmFuYSIsICJwZWFyIiwgc2VwID0gIiwiKQpzZW1pY29sb25fbGluZSAgPC0gcGFzdGUoIkFob2oiLCAic3ZldCIsIHNlcCA9ICI7IikKCmZpcnN0OyBsYXN0CmZ1bGxfbmFtZTsgZnVsbF9ub3NwYWNlCmNzdl9saW5lCnNlbWljb2xvbl9saW5lCmBgYAoKIyMgRMS6xb5rYSB0ZXh0b3bDqWhvIHJlxaVhemNhIGEgcG9kcmXFpWF6ZWMKCmBgYHtyIHRleHRfbGVuZ3RoX3N1YnN0cn0KeCA8LSAiUiBqZSBza3ZlbHkgamF6eWshIgpuY2hhcih4KSAgICAgICAgICAgICAgICAgIyBwb8SNZXQgem5ha292IHYgcmXFpWF6Y2kKc3Vic3RyKHgsIDEsIDUpICAgICAgICAgICMgcG9kcmXFpWF6ZWMgb2QgMS4gZG8gNS4gem5ha3UKc3Vic3RyKHgsIDgsIG5jaGFyKHgpKSAgICMgcG9kcmXFpWF6ZWMgb2QgOC4gcG8ga29uaWVjCmBgYAoKPiBUaXA6IGtuacW+bmljYSAqKnN0cmluZ3IqKiBwb27DumthIGXFoXRlIHZpYWMgdcW+aXRvxI1uw71jaCBmdW5rY2nDrSBwcmUgcHLDoWN1Cj4gcyB0ZXh0b20gKGBzdHJfdG9fdXBwZXIoKWAsIGBzdHJfcmVwbGFjZSgpYCwgLi4uKSwgYWxlIG5hIHrDoWtsYWRuw6kgw7psb2h5Cj4gc3RhxI1pYSBhaiB2c3RhdmFuw6kgZnVua2NpZSBSLgoKIyMgTWFsw6kgY3ZpxI1lbmllCgo+IFZ5dHZvcnRlIHRleHRvdsO6IHByZW1lbm7DuiBgdmV0YWAgcyBvYnNhaG9tICAKPiBgIkVrb25vbWV0cmlhIGplIHphdWppbWF2YSJgIGE6Cj4gMS4gemlzdGl0ZSBwb8SNZXQgem5ha292LAo+IDIuIHZ5dGlhaG5pdGUgcHJ2w71jaCAxMCB6bmFrb3YuCgpgYGB7ciB0ZXh0X2V4ZXJjaXNlfQp2ZXRhIDwtICJFa29ub21ldHJpYSBqZSB6YXVqaW1hdmEiCnBvY2V0X3puYWtvdiA8LSBuY2hhcih2ZXRhKQpwcnZ5Y2hfZGVzYXQgPC0gc3Vic3RyKHZldGEsIDEsIDEwKQoKcG9jZXRfem5ha292CnBydnljaF9kZXNhdApgYGAKCi0tLQoKIyBMb2dpY2vDqSAoYm9vbG92c2vDqSkgaG9kbm90eSBhIHByZW1lbm7DqQoKIyMgWsOha2xhZHkKCmBgYHtyIGxvZ2ljYWxfYmFzaWNzfQpwIDwtIFRSVUUKcSA8LSBGQUxTRQoKIXAgICAgICAgICMgTk9UCnAgJiBxICAgICAjIEFORApwIHwgcSAgICAgIyBPUgp4b3IocCwgcSkgIyBleGNsdXNpdmUgT1IgLSBwbGF0w60gcHJlc25lIGplZG5vIHogcCwgcQpgYGAKCiMjIExvZ2lja8O9IHbDvXNsZWRvayBwb3Jvdm7DoXZhbmlhCgpgYGB7ciBsb2dpY2FsX2NvbXBhcmV9CjMgPCA1CjcgPj0gNwoiY2F0IiA9PSAiY2F0IgoiY2F0IiAhPSAiZG9nIiAgICMgIT0gem5hbWVuw6EgIm5pZSBqZSByb3Zuw6kiCiFUUlVFCmBgYAoKIyMgWmxvxb5pdGVqxaFpZSBsb2dpY2vDqSBvcGVyw6FjaWUKCmBgYHtyIGxvZ2ljYWxfY29tcGxleH0KeCA8LSAxMAoKeCA+IDUgJiB4IDwgMjAgICAgICAgIyB4IGplIHbDpMSNxaFpZSBha28gNSBhIHrDoXJvdmXFiCBtZW7FoWllIGFrbyAyMAp4IDwgMCB8IHggPiAxMDAgICAgICAjIHggamUgbWVuxaFpZSBha28gMCBhbGVibyB2w6TEjcWhaWUgYWtvIDEwMAoKIyBwcmkgemxvxb5pdGVqxaHDrWNoIHbDvXJhem9jaCBqZSBkb2Jyw6kgcG91xb7DrXZhxaUgesOhdHZvcmt5Cih4ID4gNSAmIHggPCAyMCkgfCAoeCA9PSAwKQpgYGAKCiMjIFpsdcSNb3ZhbmllIHZpYWNlcsO9Y2ggbG9naWNrw71jaCBwcmVtZW5uw71jaCBkbyB2ZWt0b3JhCgpgYGB7ciBsb2dpY2FsX3ZlY3Rvcn0KdmFscyA8LSBjKFRSVUUsIEZBTFNFLCBUUlVFLCBUUlVFKQp2YWxzCmBgYAoKIyMgTWFsw6kgY3ZpxI1lbmllCgo+IE1ham1lIMSNw61zbG8gYHkgPC0gMjVgLiBQb21vY291IGxvZ2lja8O9Y2ggb3BlcsOhY2nDrSB6aXN0aXRlLCDEjWk6Cj4gMS4gamUgdsOkxI3FoWllIGFrbyAxMCBhIHrDoXJvdmXFiCBtZW7FoWllIGFsZWJvIHJvdm7DqSAzMCwKPiAyLiBuaWUgamUgcm92bsOpIDAuCgpgYGB7ciBsb2dpY2FsX2V4ZXJjaXNlfQp5IDwtIDI1CnBvZG1pZW5rYTEgPC0gKHkgPiAxMCAmIHkgPD0gMzApCnBvZG1pZW5rYTIgPC0gKHkgIT0gMCkKCnBvZG1pZW5rYTEKcG9kbWllbmthMgpgYGAKCi0tLQoKIyBOdW1lcmlja8OpIHZla3RvcnkKCiMjIEdlbmVyb3ZhbmllIHZla3Rvcm92CgpgYGB7ciB2ZWN0b3JzX2NyZWF0ZX0KdjEgPC0gYygyLCA0LCA2LCA4KQp2MiA8LSAxOjUgICAgICAgICAgICAgICAgICAgICAgICAgICMgcG9zdHVwbm9zxaUgMSwyLDMsNCw1CnYzIDwtIHNlcShmcm9tID0gMCwgdG8gPSAxLCBieSA9IDAuMjUpICAjIHBvc3R1cG5vc8WlIHMga3Jva29tIDAuMjUKdjQgPC0gcmVwKDMsIHRpbWVzID0gNSkgICAgICAgICAgICAjIDMsMywzLDMsMwp2NSA8LSBydW5pZig1KSAgICAgICAgICAgICAgICAgICAgICMgcm92bm9tZXJuw6kgcm96ZGVsZW5pZSB2IFswLDFdCnY2IDwtIHJub3JtKDUpICAgICAgICAgICAgICAgICAgICAgIyBub3Jtw6FsbmUgcm96ZGVsZW7DqSBuw6Fob2Ruw6kgaG9kbm90eQoKdjE7IHYyOyB2MzsgdjQ7IHY1OyB2NgpgYGAKCiMjIEFyaXRtZXRpY2vDqSBvcGVyw6FjaWUgcyB2ZWt0b3JtaQoKYGBge3IgdmVjdG9yc19hcml0aH0KdiA8LSBjKDEsIDIsIDMsIDQpCgp2ICsgMTAgICAgICAgICAgICMga2HFvmTDvSBwcnZvayArMTAKdiAqIDIgICAgICAgICAgICAjIGthxb5kw70gcHJ2b2sgKjIKKHYgKyAxKSAvIDIKZXhwKHYpICAgICAgICAgICAjIGV4cG9uZW5jacOhbG5hIGZ1bmtjaWEgeiBrYcW+ZMOpaG8gcHJ2a3UKCiMgcsO0em5lIHNww7Rzb2J5IHNrYWzDoXJuZWhvIHPDusSNaW51IGR2b2NoIHZla3Rvcm92CnN1bShjKDEsMiwzKSAqIGMoMSwxLDEpKSAgICAgICAgIyBza2Fsw6Fybnkgc8O6xI1pbiAtIHbDvXNsZWRvayBqZSBza2FsYXIKY3Jvc3Nwcm9kKGMoMSwyLDMpLCBjKDEsMSwxKSkgICAjIHNrYWzDoXJueSBzw7rEjWluIC0gdsO9c2xlZG9rIGplIG1hdGljYSAxeDEKYygxLDIsMykgKiBjKDEsMSwxKSAgICAgICAgICAgICAjIEhhZGFtYXJkb3Ygc8O6xI1pbiAocG8gcHJ2a29jaCkKYGBgCgojIyBNYXRlbWF0aWNrw6kgb3BlcsOhY2llIHMgZHZvbWEgdmVrdG9ybWkgcm92bmFrw6lobyByb3ptZXJ1CgpgYGB7ciB2ZWN0b3JzX3NhbWVfbGVuZ3RofQpsZW5ndGgoYygxLDIsMyw0LDUpKQpsZW5ndGgodjUpCgpjKDEsMiwzLDQsNSkgKyB2NSAgICAgIyBvYmEgdmVrdG9yeSBtdXNpYSBtYcWlIHJvdm5ha8O6IGTEusW+a3UKYGBgCgojIyBJbmRleG92YW5pZSBhIHbDvWJlciBuaWVrdG9yw71jaCBwcnZrb3YgdmVrdG9yYQoKYGBge3IgdmVjdG9yc19pbmRleH0KeCA8LSBjKDUsIDEyLCAzLCAxOCwgNywgMCwgMjEpCgp4WzFdICAgICAgICAgICAjIHBydsO9IHBydm9rCnhbMjo0XSAgICAgICAgICMgZHJ1aMO9IGHFviDFoXR2cnTDvSBwcnZvawp4Wy0xXSAgICAgICAgICAjIHbFoWV0a28gb2tyZW0gcHJ2w6lobyBwcnZrdQp4W3ggPiAxMF0gICAgICAjIHBydmt5IHbDpMSNxaFpZSBha28gMTAKd2hpY2goeCA+IDEwKSAgIyBpbmRleHkgcHJ2a292IHbDpMSNxaHDrWNoIGFrbyAxMApgYGAKCiMjIFByw6FjYSBzIGNow71iYWrDumNpbWkgaG9kbm90YW1pCgpgYGB7ciB2ZWN0b3JzX25hfQp5IDwtIGMoMSwgTkEsIDMsIE5BLCA1KQoKaXMubmEoeSkgICAgICAgICAgICAgICAgICAjIGxvZ2lja8O9IHZla3Rvciwga2RlIGplIE5BCm1lYW4oeSkgICAgICAgICAgICAgICAgICAgIyB2w71zbGVkb2sgTkEKbWVhbih5LCBuYS5ybSA9IFRSVUUpICAgICAjIGlnbm9yb3ZhbmllIE5BIHByaSB2w71wb8SNdGUgcHJpZW1lcnUKYGBgCgojIyBaw6FrbGFkbsOpIMWhdGF0aXN0aWt5IGEgdXNwb3JpYWRhbmllIHBydmtvdgoKYGBge3IgdmVjdG9yc19zdGF0c30KeiA8LSBjKDEwLCAzLCA1LCA4LCAyKQoKbWVhbih6KSAgICAgICAgICAgICAgICAjIHByaWVtZXIKc2QoeikgICAgICAgICAgICAgICAgICAjIMWhdGFuZGFyZG7DoSBvZGNow71sa2EKbWF4KHopICAgICAgICAgICAgICAgICAjIG1heGltdW0Kc3VtbWFyeSh6KSAgICAgICAgICAgICAjIHLDvWNobHkgcHJlaMS+YWQKc29ydCh6KSAgICAgICAgICAgICAgICAjIHJhc3TDumNlIHVzcG9yaWFkYW5pZQpzb3J0KHosIGRlY3JlYXNpbmcgPSBUUlVFKSAgIyBrbGVzYWrDumNvCmBgYAoKIyMgSmVkbm9kdWNow70gZ3JhZiB6IHZla3RvcmEKCmBgYHtyIHZlY3RvcnNfcGxvdCwgZmlnLmNhcD0ixIxhc292w70gcHJpZWJlaCBob2Rub3QgdmVrdG9yYSB6In0KcGxvdCh6LAogICAgIHR5cGUgPSAiYiIsCiAgICAgbWFpbiA9ICJKZWRub2R1Y2jDvSBsaW5lw6FybnkgZ3JhZiB2ZWt0b3JhIHoiLAogICAgIHhsYWIgPSAiSW5kZXggcHJ2a3UiLAogICAgIHlsYWIgPSAiSG9kbm90YSIsCiAgICAgY29sICA9ICJkYXJrZ3JlZW4iLAogICAgIHBjaCAgPSAxOSkKYGBgCgojIyBNYWzDqSBjdmnEjWVuaWUKCj4gVnl0dm9ydGUgdmVrdG9yIGB3YCBzIMSNw61zbGFtaSBvZCAxIGRvIDIwIGEgdnlwb8SNw610YWp0ZSAqKnPDusSNZXQgdsWhZXRrw71jaAo+IHDDoXJueWNoIMSNw61zZWwqKiB2byB2ZWt0b3JlLiAgCj4gUG90b20gdnlwb8SNw610YWp0ZSBhaiBwcmllbWVyIHbFoWV0a8O9Y2ggbmVww6FybnljaCDEjcOtc2VsLgoKYGBge3IgdmVjdG9yc19leGVyY2lzZX0KdyA8LSAxOjIwCgpzdW1hX3Bhcm5lICAgPC0gc3VtKHdbdyAlJSAyID09IDBdKQpwcmllbWVyX25lcGFyIDwtIG1lYW4od1t3ICUlIDIgPT0gMV0pCgpzdW1hX3Bhcm5lCnByaWVtZXJfbmVwYXIKYGBgCgotLS0KCiMgTWF0aWNlCgojIyBWeXR2b3JlbmllIG1hdMOtYwoKYGBge3IgbWF0cml4X2NyZWF0ZX0KbSA8LSBtYXRyaXgoMToxMiwgbnJvdyA9IDMsIG5jb2wgPSA0KSAgICAgICAgICAgICMgaG9kbm90eSBwbyBzdMS6cGNvY2gKbV9ieXJvdyA8LSBtYXRyaXgoMToxMiwgbnJvdyA9IDMsIGJ5cm93ID0gVFJVRSkgICMgaG9kbm90eSBwbyByaWFka29jaAoKbQptX2J5cm93CmBgYAoKIyMgUm96bWVyeSBtYXRpY2UKCmBgYHtyIG1hdHJpeF9kaW1zfQpkaW0obSkgICAjIHBvxI1ldCByaWFka292IGEgc3TEunBjb3YKYGBgCgojIyBBZHJlc292YW5pZSBwcnZrb3YgbWF0aWNlCgpgYGB7ciBtYXRyaXhfaW5kZXh9Cm1bMSwgMl0gICAgICAjIHJpYWRvayAxLCBzdMS6cGVjIDIKbVsgLCAzXSAgICAgICMgdsWhZXRreSBwcnZreSB2IHRyZcWlb20gc3TEunBjaQptWzIsIF0gICAgICAgIyB2xaFldGt5IHBydmt5IHYgZHJ1aG9tIHJpYWRrdQptWzE6MiwgMjozXSAgIyBwb2RtYXRpY2EgcmlhZGt5IDEtMiwgc3TEunBjZSAyLTMKYGBgCgojIyBNYXRpY292w6kgb3BlcsOhY2llCgpgYGB7ciBtYXRyaXhfb3BzfQpBIDwtIG1hdHJpeChjKDEsIDIsIDMsIDQpLCBucm93ID0gMikKQiA8LSBtYXRyaXgoYyg1LCA2LCA3LCA4KSwgbnJvdyA9IDIpCgpBICsgQiAgICAgICAgIyBzxI3DrXRhbmllIG1hdMOtYwpBICogQiAgICAgICAgIyBIYWRhbWFyZG92IHPDusSNaW4gKHBvIHBydmtvY2gpCkEgJSolIEIgICAgICAjIHNrdXRvxI1uw6kgbsOhc29iZW5pZSBtYXTDrWMKdChBKSAgICAgICAgICMgdHJhbnNwb3rDrWNpYQpkZXQoQSkgICAgICAgIyBkZXRlcm1pbmFudApzb2x2ZShBKSAgICAgIyBpbnZlcnppYSBtYXRpY2UgKGFrIGV4aXN0dWplKQpgYGAKCiMjIFpsdcSNb3ZhbmllIHZla3Rvcm92IGRvIG1hdMOtYyAKCmBgYHtyIG1hdHJpeF9iaW5kfQpDIDwtIGNiaW5kKDE6MywgNDo2KSAgICMgcG8gc3TEunBjb2NoCkQgPC0gcmJpbmQoMTozLCA0OjYpICAgIyBwbyByaWFka29jaAoKQwpECmBgYAoKIyMgVnlwb8SNw610YW5pZSDFoXRhdGlzdGlreSBwbyByaWFka29jaCAvIHN0xLpwY29jaAoKYGBge3IgbWF0cml4X2FwcGx5fQpNIDwtIG1hdHJpeCgxOjksIG5yb3cgPSAzKQoKTQphcHBseShNLCAxLCBzdW0pICAgIyBzdW1hIHBvIHJpYWRrb2NoCmFwcGx5KE0sIDIsIG1lYW4pICAjIHByaWVtZXJ5IHBvIHN0xLpwY29jaApgYGAKCiMjIE1hbMOpIGN2acSNZW5pZQoKPiBWeXR2b3J0ZSBtYXRpY3UgYE0yYCBzIHJvem1lcm1pIDXDlzUsIGhvZG5vdHkgMS4uMjUgemFkw6F2YW7DqSBwbyByaWFka29jaC4gIAo+IGEpIHZ5cG/EjcOtdGFqdGUgc3TEunBjb3bDqSBzdW15LCAgCj4gYikgdnlwb8SNw610YWp0ZSBzw7rEjWluIFwoTTJeVCBNMlwpLgoKYGBge3IgbWF0cml4X2V4ZXJjaXNlfQpNMiA8LSBtYXRyaXgoMToyNSwgbnJvdyA9IDUsIGJ5cm93ID0gVFJVRSkKCnN0bHBjZV9zdW0gPC0gY29sU3VtcyhNMikKc3VjaW5fTTIgICA8LSB0KE0yKSAlKiUgTTIKCk0yCnN0bHBjZV9zdW0Kc3VjaW5fTTIKYGBgCgotLS0KCiMgTcO0aiBuw6F2cmggcG91xb5pdGlhIG5vdmlua3kKClYgdGVqdG8gcG9zbGVkbmVqIHNla2NpaSB1a2F6dWplbSAqKm5vdsOpIHByw61rYXp5KiosIGt0b3LDqSB2IHDDtHZvZG5vbSBSbWQKZG9rdW1lbnRlIG5lYm9saToKCi0gdnl0dm9yZW5pZSBgZGF0YS5mcmFtZWAgeiB2aWFjZXLDvWNoIHZla3Rvcm92LAotIHbDvXBvxI1ldCBrb3JlbMOhY2llIHBvbW9jb3UgYGNvcigpYCwKLSBqZWRub2R1Y2jDvSByZWdyZXNuw70gbW9kZWwgcG9tb2NvdSBgbG0oKWAgYSBncmFmIHNvIHNww6R0bm91IHJlZ3Jlc25vdSDEjWlhcm91LAotIHBvdcW+aXRpZSBgaWZlbHNlKClgIG5hIHZ5dHZvcmVuaWUga2F0ZWfDs3JpZSAqIsO6c3BlxaFuw70vbmXDunNwZcWhbsO9IMWhdHVkZW50IiosCi0gem9icmF6ZW5pZSB2w71zbGVka3UgcG9tb2NvdSBgdGFibGUoKWAgYSBgYmFycGxvdCgpYC4KCiMjIFNpbXVsw6FjaWEgZMOhdCBvIMWhdMO6ZGl1IGEgdsO9c2xlZGtvY2ggdGVzdHUKCmBgYHtyIGlubm92YXRpb25fZGF0YX0Kc2V0LnNlZWQoNDIpCgpwb2NldF9zdHVkZW50b3YgPC0gMzAKCmhvZGlueV9zdHVkaWEgPC0gcnVuaWYocG9jZXRfc3R1ZGVudG92LCBtaW4gPSAwLCBtYXggPSAxMCkKYm9keV90ZXN0ICAgICA8LSAyMCArIDYgKiBob2Rpbnlfc3R1ZGlhICsgcm5vcm0ocG9jZXRfc3R1ZGVudG92LCBzZCA9IDUpCgpkYXRhX3Rlc3QgPC0gZGF0YS5mcmFtZSgKICBob2RpbnkgPSBob2Rpbnlfc3R1ZGlhLAogIGJvZHkgICA9IGJvZHlfdGVzdAopCgpoZWFkKGRhdGFfdGVzdCkKYGBgCgojIyBLb3JlbMOhY2lhIGEgamVkbm9kdWNow70gcmVncmVzbsO9IG1vZGVsCgpgYGB7ciBpbm5vdmF0aW9uX2Nvcl9sbX0KY29yX2hvZGlueV9ib2R5IDwtIGNvcihkYXRhX3Rlc3QkaG9kaW55LCBkYXRhX3Rlc3QkYm9keSkKY29yX2hvZGlueV9ib2R5Cgptb2RlbF90ZXN0IDwtIGxtKGJvZHkgfiBob2RpbnksIGRhdGEgPSBkYXRhX3Rlc3QpCnN1bW1hcnkobW9kZWxfdGVzdCkKYGBgCgojIyBHcmFmIHNvIHNww6R0bm91IHJlZ3Jlc25vdSDEjWlhcm91CgpgYGB7ciBpbm5vdmF0aW9uX3Bsb3QsIGZpZy5jYXA9IlZ6xaVhaCBtZWR6aSBob2RpbmFtaSDFoXTDumRpYSBhIHBvxI10b20gYm9kb3YifQpwbG90KAogIGJvZHkgfiBob2RpbnksCiAgZGF0YSA9IGRhdGFfdGVzdCwKICBwY2ggPSAxOSwKICBjb2wgPSAiZGFya2JsdWUiLAogIG1haW4gPSAixIzDrW0gdmlhYyDFoXR1ZHVqZW0sIHTDvW0gdmlhYyBib2Rvdj8iLAogIHhsYWIgPSAiSG9kaW55IMWhdMO6ZGlhIHphIHTDvcW+ZGXFiCIsCiAgeWxhYiA9ICJQb8SNZXQgYm9kb3YgdiB0ZXN0ZSIKKQoKYWJsaW5lKG1vZGVsX3Rlc3QsIGNvbCA9ICJyZWQiLCBsd2QgPSAyKQpgYGAKCiMjIFBvdcW+aXRpZSBpZmVsc2UoKSBhIGJhcnBsb3QoKQoKUHJlZHBva2xhZGFqbWUsIMW+ZSBuYSB0ZXN0IHRyZWJhICoqYXNwb8WIIDYwIGJvZG92KiouCgpgYGB7ciBpbm5vdmF0aW9uX2lmZWxzZV9iYXJ9CmRhdGFfdGVzdCRzdGF0dXMgPC0gaWZlbHNlKGRhdGFfdGVzdCRib2R5ID49IDYwLCAiw7pzcGXFoW7DvSIsICJuZcO6c3BlxaFuw70iKQoKdGFiX3N0YXR1cyA8LSB0YWJsZShkYXRhX3Rlc3Qkc3RhdHVzKQp0YWJfc3RhdHVzCgpiYXJwbG90KAogIHRhYl9zdGF0dXMsCiAgbWFpbiA9ICJQb8SNZXQgw7pzcGXFoW7DvWNoIGEgbmXDunNwZcWhbsO9Y2ggxaF0dWRlbnRvdiIsCiAgeWxhYiA9ICJQb8SNZXQgxaF0dWRlbnRvdiIsCiAgY29sICA9IGMoInRvbWF0byIsICJzdGVlbGJsdWUiKQopCmBgYAoKViB0ZWp0byBzZWtjaWkgc29tIHRlZGEgcG91xb5pbGEgKipub3bDqSBwcsOta2F6eSoqLCBrdG9yw6kgdiBww7R2b2Rub20gZG9rdW1lbnRlCm5lYm9saSwgYSB1a8OhemFsYToKCi0gYWtvIHZ5dHZvcmnFpSBgZGF0YS5mcmFtZWAsCi0gYWtvIHppc3RpxaUga29yZWzDoWNpdSBgY29yKClgLAotIGFrbyBvZGhhZG7DusWlIGplZG5vZHVjaMO9IG1vZGVsIGBsbSgpYCBhIHpvYnJhemnFpSBobyB2IGdyYWZlLAotIGFrbyB2eXR2b3JpxaUgbm92w7oga2F0ZWfDs3JpdSBwb21vY291IGBpZmVsc2UoKWAsCi0gYWtvIHBvdcW+acWlIGB0YWJsZSgpYCBhIGBiYXJwbG90KClgIG5hIHpvYnJhemVuaWUgdsO9c2xlZGtvdi4K