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