Úvod k základným operáciám v R

Tento dokument demonštruje prácu so základnými typmi hodnôt v R:

  • numerické,
  • textové,
  • logické,
  • vektory a
  • matice.

Pri každej kategórii som zahrnula aj malé cvičenia na sumarizáciu a preopakovanie.


Skaláre (jednočíselné hodnoty)

Numerické skaláre

a <- 8
b <- 3.5
c <- -2

# základné operácie
sucet <- a + b + c
sucin <- a * b * c
priemer <- (a + b + c) / 3

# kombinácie
vyraz1 <- (a^2 + b) / c
vyraz2 <- (a * b) - (b / c)
vyraz3 <- (a %% 3) + (b %% 2)

round(b, 1)
abs(c)       # absolútna hodnota
sqrt(a)      # druhá odmocnina

sucet; sucin; priemer
vyraz1; vyraz2; vyraz3

Malé cvičenie

Vypočítajte nasledujúce výrazy (pri hodnotách a = 8, b = 3.5, c = -2):

  1. \(\frac{a^2 + b^2}{c}\)
  2. \(\frac{a}{b} + \frac{b}{c} + \frac{c}{a}\)
  3. \((a+b+c)^2 - (a \cdot b \cdot c)\)
a <- 8; b <- 3.5; c <- -2

( (a^2 + b^2) / c )
( (a/b) + (b/c) + (c/a) )
( (a+b+c)^2 - (a*b*c) )

Text

Vytváranie textovými premennými a práca s nimi

city <- "Bratislava"                     # premenná obsahujúca názov mesta
food <- "Pizza"                          # premenná obsahujúca jedlo
combo <- paste(city, food)               # spojenie textov s medzerou medzi nimi
combo_nospace <- paste0(city, food)      # spojenie textov bez medzery
list_line <- paste("coffee", "tea", "juice", sep = ",")  # spájanie textov s oddeľovačom ,
city; food; combo; combo_nospace; list_line   # vypísanie viacerých premenných za sebou

Dĺžka textového reťazca, podreťazec

sentence <- "Learning R is fun!"
nchar(sentence)                  # počet znakov v reťazci
substr(sentence, 3, 10)          # výber znakov od 3. po 10. pozíciu

Takýmto spôsobom sa v R pracuje s textovými premennými.

Malé cvičenie

Vytvorte textovú premennú s názvom svojho obľúbeného športu a:
- spojte ju s mestom, kde sa šport hrá,
- vytvorte verziu bez medzery,
- zistite počet znakov v celom reťazci,
- vypíšte iba prvé 4 znaky.

sport <- "Hokej"                     # názov športu
place <- "Kosice"                    # názov mesta

joined <- paste(sport, place)        # spojenie s medzerou
joined_nospace <- paste0(sport, place) # spojenie bez medzery

nchar(joined)                        # počet znakov v celom reťazci
substr(joined, 1, 4)                 # podreťazec - prvé 4 znaky

Logické (boolovské) hodnoty a premenné

Základy

r <- TRUE
s <- FALSE

!r                 # logická negácia (NOT)
r & s              # logický súčin (AND) - pravda len keď obidva sú TRUE
r | s              # logický súčet (OR) - pravda keď aspoň jeden je TRUE
xor(r, s)          # exkluzívne OR - pravda len ak presne jeden z nich je TRUE

Logický výsledok porovnávania

5 > 2                  # je 5 väčšie ako 2?
10 <= 15               # je 10 menšie alebo rovné 15?
"dog" == "dog"         # rovnosť dvoch reťazcov
"apple" != "orange"    # nerovnosť dvoch reťazcov
!FALSE                 # negácia hodnoty FALSE

7 == 3 + 4             # rovnosť čísla a výrazu
20 != 5 * 5            # porovnanie nerovnosti
100 >= 10^2            # mocnina a porovnanie
"car" < "cat"          # porovnanie podľa abecedy (lexikograficky)
"Zoo" > "apple"        # veľké písmená sú v ASCII pred malými

Zložitejšie logické operácie

y <- 25

y >= 20 & y <= 30       # súčasne (AND) - číslo je medzi 20 a 30
y < 0 | y > 100         # alebo (OR) - číslo je menšie ako 0 alebo väčšie ako 100
(y < 20 & y > 10) | (y > 30)   # kombinácia AND a OR - platí, ak y je medzi 10 a 20 alebo väčšie ako 30

Zlučovanie viacerých log. premenných do vektora

checks <- c(TRUE, TRUE, FALSE, FALSE, TRUE)   # vektor logických hodnôt

Malé cvičenie

Základy

Vytvorte dve logické premenné a a b.
- Zistite negáciu a.
- Vypočítajte AND (&) a OR (|).
- Vyskúšajte aj XOR medzi nimi.

a <- TRUE
b <- FALSE

!a
a & b
a | b
xor(a, b)

Logický výsledok porovnávania

Skontrolujte, či platia nasledujúce podmienky:
- číslo 50 je väčšie ako 40 a menšie ako 60,
- číslo 15 nie je rovné 10,
- reťazec “banana” je rovný “banana”,
- reťazec “Apple” je menší ako “apple” (lexikograficky),
- overte, či 7 + 3 = 10 a či 20 - 5 != 15.

num <- 50
num >= 40 & num <= 60       # AND
15 != 10                     # nerovnosť
"banana" == "banana"         # rovnosť reťazcov
"Apple" < "apple"            # lexikografické porovnanie
7 + 3 == 10                  # rovnosť výrazu
20 - 5 != 15                  # nerovnosť výrazu

Zložitejšie logické operácie

Použite číslo y <- 25 a:
- overte, či je medzi 20 a 30 (AND),
- overte, či je menšie ako 0 alebo väčšie ako 100 (OR),
- kombinujte podmienky: medzi 10 a 20 alebo väčšie ako 30,
- vyskúšajte negáciu celej kombinácie.

y <- 25

y >= 20 & y <= 30                        # AND
y < 0 | y > 100                          # OR
(y < 20 & y > 10) | (y > 30)             # kombinácia AND a OR
!( (y < 20 & y > 10) | (y > 30) )        # negácia celej kombinácie

Zlučovanie viacerých log. premenných do vektora

Vytvorte vektor vals <- c(5, 15, 25, 35, 45) a:
- zistite, ktoré hodnoty sú väčšie ako 20,
- ktoré hodnoty sú menšie ako 30,
- ktoré hodnoty sú zároveň väčšie ako 20 a menšie ako 40,
- skombinujte OR a AND vo výraze pre každý prvok, aby ste zistili, ktoré hodnoty sú menšie ako 10 alebo väčšie ako 40.

vals <- c(5, 15, 25, 35, 45)

vals > 20                 # TRUE pre prvky väčšie ako 20
vals < 30                 # TRUE pre prvky menšie ako 30
vals > 20 & vals < 40     # kombinácia AND
(vals < 10 | vals > 40)   # kombinácia OR

Numerické vektory

Generovanie vektorov

a1 <- c(10, 20, 30, 40)
a2 <- 5:12                        # postupnosť čísel od 5 po 12
a3 <- seq(from = 2, to = 4, by = 0.5)   # postupnosť s krokom 0.5
a4 <- rep(7, times = 6)           # vektor so šiestimi sedmičkami
a5 <- runif(6, min = -1, max = 1) # náhodné čísla z intervalu [-1,1]
a6 <- rnorm(6, mean = 50, sd = 10) # normálne rozdelenie so stredom 50

a1; a2; a3; a4; a5; a6

Aritmetické operácie s vektormi

b <- c(2, 4, 6, 8)
b - 3             # každý prvok zmenšíme o 3
b / 2             # každý prvok vydelíme dvomi
(b^2) + 1         # druhá mocnina každého prvku plus 1
log(b)            # prirodzený logaritmus prvkov
sum(c(2,4,6), c(1,1,1))         # skalárny súčin (skrátená forma)
crossprod(c(2,4,6), c(1,1,1))   # skalárny súčin - matica 1x1
c(2,4,6) * c(3,3,3)             # Hadamardov súčin

Matematické operácie s 2 vektormi rovnakého rozmeru

length(c(10,20,30,40))
length(a5)           # dĺžka vektora a5
c(10,20,30,40) + a5  # súčet vektorov
c(10,20,30,40) - a5  # rozdiel vektorov

Indexovanie a výber niektorych prvkov vektora

c1 <- c(9, 15, 2, 27, 5, 18, 0)
c1[3]          # tretí prvok
c1[2:5]        # druhý až piaty prvok
c1[-4]         # všetko okrem štvrtého prvku
c1[c1 %% 3 == 0]  # len tie prvky, ktoré sú deliteľné tromi
which(c1 < 10) # indexy prvkov menších ako 10

Práca s chýbajúcimi hodnotami

d <- c(NA, 4, 8, NA, 12, 16)
is.na(d)                
sum(is.na(d))           # počet chýbajúcich hodnôt
mean(d, na.rm = TRUE)   # priemer bez NA
median(d, na.rm = TRUE) # medián bez NA

Základné štatistiky a usporiadanie prvkov vektora podľa veľkosti

e <- c(25, 7, 14, 32, 19, 3)
mean(e)                  
sd(e)                    
min(e)                   
max(e)                   
summary(e)               
sort(e)                  
sort(e, decreasing = TRUE) 

Malé cvičenie 1

Vytvorte vektor f s číslami 50..100 a vypočítajte aritmetický priemer všetkých nepárnych čísel.

f <- 50:100
mean(f[f %% 2 == 1])

Malé cvičenie 2

Vytvorte vektor 10 náhodných čísel z intervalu [-5,5] a spočítajte, koľko z nich je kladných.

g <- runif(10, min = -5, max = 5)
g
sum(g > 0)

Malé cvičenie 3

Vytvorte vektor h s hodnotami od 1 do 12 a zistite, aký je rozdiel medzi súčtom čísel deliteľných 4 a súčtom čísel deliteľných 3.

h <- 1:12
sum4 <- sum(h[h %% 4 == 0])
sum3 <- sum(h[h %% 3 == 0])
sum4 - sum3

Môj návrh použitia novinky

V sekcii “Skaláre” som:

  • použila aj tretiu premennú c - išlo konkrétne o zápornú hodnotu, čím som rozšírila možnosti výpočtov o prácu so znamienkami,
  • urobila vlastné zložitejšie kombinácie výpočtových operácií,
  • použila ďalšie matematické funkcie, ako sú absolútna hodnota, druhá odmocnina a zaokrúhľovanie na 1 desatinné miesto,
  • som namiesto jedného jednoduchého cvičenia zahrnula tri rôzne úlohy, kde sa kombinujú mocniny, zlomky, súčty a súčiny.

V sekcii “Text” som:

  • pridala malé cvičenie, ktoré rozširuje prácu s textom a dáva priestor na tvorenie vlastného príkladu.

V sekcii “Logické hodnoty” som:

  • pridala viac príkladov logických porovnaní vrátane čísel, výrazov a textov,
  • doplnila jednotlivé operácie o podrobnejšie komentáre,
  • ukázala kombinované logické výrazy s AND, OR a negáciou,
  • pri zlučovaní logických premenných použila číselný vektor a podmienkové filtrovanie,
  • doplnila 4 bloky cvičení (jeden ku každej podkapitole), ktoré rozvíjajú základné aj pokročilé znalosti.

V sekcii “Numerické vektory” som:

  • pri aritmetických operáciách poukázala na odčítanie, delenie, mocniny a logaritmickú funkciu,
  • rozšírila operácie s dvomi vektormi o rozdiel vektorov,
  • pri indexovaní a výbere pridala príklad výberu podľa deliteľnosti, nielen podľa veľkosti,
  • doplnila prácu s chýbajúcimi hodnotami a základné štatistiky o ďalšie funkcie.
LS0tCnRpdGxlOiAiWsOha2xhZG7DqSBvcGVyw6FjaWUgdiBSIgphdXRob3I6ICJMaW5kYSBLYWthc292w6EgIDxicj4KKHMgdnl1xb5pdMOtbSBDaGF0R1BUKSIKZGF0ZTogIk9rdMOzYmVyIDIwMjUiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0aGVtZTogdW5pdGVkCiAgICBoaWdobGlnaHQ6IHRhbmdvCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCgojIMOadm9kIGsgesOha2xhZG7DvW0gb3BlcsOhY2nDoW0gdiBSCgpUZW50byBkb2t1bWVudCBkZW1vbsWhdHJ1amUgcHLDoWN1IHNvICoqesOha2xhZG7DvW1pIHR5cG1pIGhvZG7DtHQqKiB2IFI6CgoqIG51bWVyaWNrw6ksIAoqIHRleHRvdsOpLCAKKiBsb2dpY2vDqSwgCiogdmVrdG9yeSBhIAoqIG1hdGljZS4KClByaSBrYcW+ZGVqIGthdGVnw7NyaWkgc29tIHphaHJudWxhIGFqICoqbWFsw6kgY3ZpxI1lbmlhKiogbmEgc3VtYXJpesOhY2l1IGEgcHJlb3Bha292YW5pZS4KCi0tLQoKIyBTa2Fsw6FyZSAoamVkbm/EjcOtc2VsbsOpIGhvZG5vdHkpCgojIyBOdW1lcmlja8OpIHNrYWzDoXJlCgpgYGB7cn0KYSA8LSA4CmIgPC0gMy41CmMgPC0gLTIKCiMgesOha2xhZG7DqSBvcGVyw6FjaWUKc3VjZXQgPC0gYSArIGIgKyBjCnN1Y2luIDwtIGEgKiBiICogYwpwcmllbWVyIDwtIChhICsgYiArIGMpIC8gMwoKIyBrb21iaW7DoWNpZQp2eXJhejEgPC0gKGFeMiArIGIpIC8gYwp2eXJhejIgPC0gKGEgKiBiKSAtIChiIC8gYykKdnlyYXozIDwtIChhICUlIDMpICsgKGIgJSUgMikKCnJvdW5kKGIsIDEpCmFicyhjKSAgICAgICAjIGFic29sw7p0bmEgaG9kbm90YQpzcXJ0KGEpICAgICAgIyBkcnVow6Egb2Rtb2NuaW5hCgpzdWNldDsgc3VjaW47IHByaWVtZXIKdnlyYXoxOyB2eXJhejI7IHZ5cmF6MwpgYGAKCgojIyBNYWzDqSBjdmnEjWVuaWUKCj4gVnlwb8SNw610YWp0ZSBuYXNsZWR1asO6Y2UgdsO9cmF6eSAocHJpIGhvZG5vdMOhY2ggYSA9IDgsIGIgPSAzLjUsIGMgPSAtMik6CgoxLiBcKFxmcmFje2FeMiArIGJeMn17Y31cKSAgCjIuIFwoXGZyYWN7YX17Yn0gKyBcZnJhY3tifXtjfSArIFxmcmFje2N9e2F9XCkgIAozLiBcKChhK2IrYyleMiAtIChhIFxjZG90IGIgXGNkb3QgYylcKSAgCgpgYGB7cn0KYSA8LSA4OyBiIDwtIDMuNTsgYyA8LSAtMgoKKCAoYV4yICsgYl4yKSAvIGMgKQooIChhL2IpICsgKGIvYykgKyAoYy9hKSApCiggKGErYitjKV4yIC0gKGEqYipjKSApCmBgYAoKLS0tCgojIFRleHQgCgojIyBWeXR2w6FyYW5pZSB0ZXh0b3bDvW1pIHByZW1lbm7DvW1pIGEgcHLDoWNhIHMgbmltaQoKYGBge3J9CmNpdHkgPC0gIkJyYXRpc2xhdmEiICAgICAgICAgICAgICAgICAgICAgIyBwcmVtZW5uw6Egb2JzYWh1asO6Y2EgbsOhem92IG1lc3RhCmZvb2QgPC0gIlBpenphIiAgICAgICAgICAgICAgICAgICAgICAgICAgIyBwcmVtZW5uw6Egb2JzYWh1asO6Y2EgamVkbG8KY29tYm8gPC0gcGFzdGUoY2l0eSwgZm9vZCkgICAgICAgICAgICAgICAjIHNwb2plbmllIHRleHRvdiBzIG1lZHplcm91IG1lZHppIG5pbWkKY29tYm9fbm9zcGFjZSA8LSBwYXN0ZTAoY2l0eSwgZm9vZCkgICAgICAjIHNwb2plbmllIHRleHRvdiBiZXogbWVkemVyeQpsaXN0X2xpbmUgPC0gcGFzdGUoImNvZmZlZSIsICJ0ZWEiLCAianVpY2UiLCBzZXAgPSAiLCIpICAjIHNww6FqYW5pZSB0ZXh0b3YgcyBvZGRlxL5vdmHEjW9tICwKY2l0eTsgZm9vZDsgY29tYm87IGNvbWJvX25vc3BhY2U7IGxpc3RfbGluZSAgICMgdnlww61zYW5pZSB2aWFjZXLDvWNoIHByZW1lbm7DvWNoIHphIHNlYm91CmBgYAoKIyMgRMS6xb5rYSB0ZXh0b3bDqWhvIHJlxaVhemNhLCBwb2RyZcWlYXplYwoKYGBge3J9CnNlbnRlbmNlIDwtICJMZWFybmluZyBSIGlzIGZ1biEiCm5jaGFyKHNlbnRlbmNlKSAgICAgICAgICAgICAgICAgICMgcG/EjWV0IHpuYWtvdiB2IHJlxaVhemNpCnN1YnN0cihzZW50ZW5jZSwgMywgMTApICAgICAgICAgICMgdsO9YmVyIHpuYWtvdiBvZCAzLiBwbyAxMC4gcG96w61jaXUKYGBgCgoKPiBUYWvDvW10byBzcMO0c29ib20gc2EgdiBSIHByYWN1amUgcyB0ZXh0b3bDvW1pIHByZW1lbm7DvW1pLgoKIyMgTWFsw6kgY3ZpxI1lbmllCgo+IFZ5dHZvcnRlIHRleHRvdsO6IHByZW1lbm7DuiBzIG7DoXp2b20gc3ZvamhvIG9ixL7DumJlbsOpaG8gxaFwb3J0dSBhOiAgCj4gLSBzcG9qdGUganUgcyBtZXN0b20sIGtkZSBzYSDFoXBvcnQgaHLDoSwgIAo+IC0gdnl0dm9ydGUgdmVyeml1IGJleiBtZWR6ZXJ5LCAgCj4gLSB6aXN0aXRlIHBvxI1ldCB6bmFrb3YgdiBjZWxvbSByZcWlYXpjaSwgIAo+IC0gdnlww63FoXRlIGliYSBwcnbDqSA0IHpuYWt5LgoKYGBge3J9CnNwb3J0IDwtICJIb2tlaiIgICAgICAgICAgICAgICAgICAgICAjIG7DoXpvdiDFoXBvcnR1CnBsYWNlIDwtICJLb3NpY2UiICAgICAgICAgICAgICAgICAgICAjIG7DoXpvdiBtZXN0YQoKam9pbmVkIDwtIHBhc3RlKHNwb3J0LCBwbGFjZSkgICAgICAgICMgc3BvamVuaWUgcyBtZWR6ZXJvdQpqb2luZWRfbm9zcGFjZSA8LSBwYXN0ZTAoc3BvcnQsIHBsYWNlKSAjIHNwb2plbmllIGJleiBtZWR6ZXJ5CgpuY2hhcihqb2luZWQpICAgICAgICAgICAgICAgICAgICAgICAgIyBwb8SNZXQgem5ha292IHYgY2Vsb20gcmXFpWF6Y2kKc3Vic3RyKGpvaW5lZCwgMSwgNCkgICAgICAgICAgICAgICAgICMgcG9kcmXFpWF6ZWMgLSBwcnbDqSA0IHpuYWt5CmBgYAoKLS0tCgojIExvZ2lja8OpIChib29sb3Zza8OpKSBob2Rub3R5IGEgcHJlbWVubsOpCgojIyBaw6FrbGFkeQoKYGBge3J9CnIgPC0gVFJVRQpzIDwtIEZBTFNFCgohciAgICAgICAgICAgICAgICAgIyBsb2dpY2vDoSBuZWfDoWNpYSAoTk9UKQpyICYgcyAgICAgICAgICAgICAgIyBsb2dpY2vDvSBzw7rEjWluIChBTkQpIC0gcHJhdmRhIGxlbiBrZcSPIG9iaWR2YSBzw7ogVFJVRQpyIHwgcyAgICAgICAgICAgICAgIyBsb2dpY2vDvSBzw7rEjWV0IChPUikgLSBwcmF2ZGEga2XEjyBhc3BvxYggamVkZW4gamUgVFJVRQp4b3IociwgcykgICAgICAgICAgIyBleGtsdXrDrXZuZSBPUiAtIHByYXZkYSBsZW4gYWsgcHJlc25lIGplZGVuIHogbmljaCBqZSBUUlVFCmBgYAoKIyMgTG9naWNrw70gdsO9c2xlZG9rIHBvcm92bsOhdmFuaWEKCmBgYHtyfQo1ID4gMiAgICAgICAgICAgICAgICAgICMgamUgNSB2w6TEjcWhaWUgYWtvIDI/CjEwIDw9IDE1ICAgICAgICAgICAgICAgIyBqZSAxMCBtZW7FoWllIGFsZWJvIHJvdm7DqSAxNT8KImRvZyIgPT0gImRvZyIgICAgICAgICAjIHJvdm5vc8WlIGR2b2NoIHJlxaVhemNvdgoiYXBwbGUiICE9ICJvcmFuZ2UiICAgICMgbmVyb3Zub3PFpSBkdm9jaCByZcWlYXpjb3YKIUZBTFNFICAgICAgICAgICAgICAgICAjIG5lZ8OhY2lhIGhvZG5vdHkgRkFMU0UKCjcgPT0gMyArIDQgICAgICAgICAgICAgIyByb3Zub3PFpSDEjcOtc2xhIGEgdsO9cmF6dQoyMCAhPSA1ICogNSAgICAgICAgICAgICMgcG9yb3ZuYW5pZSBuZXJvdm5vc3RpCjEwMCA+PSAxMF4yICAgICAgICAgICAgIyBtb2NuaW5hIGEgcG9yb3ZuYW5pZQoiY2FyIiA8ICJjYXQiICAgICAgICAgICMgcG9yb3ZuYW5pZSBwb2TEvmEgYWJlY2VkeSAobGV4aWtvZ3JhZmlja3kpCiJab28iID4gImFwcGxlIiAgICAgICAgIyB2ZcS+a8OpIHDDrXNtZW7DoSBzw7ogdiBBU0NJSSBwcmVkIG1hbMO9bWkKYGBgCgojIyBabG/Fvml0ZWrFoWllIGxvZ2lja8OpIG9wZXLDoWNpZQoKYGBge3J9CnkgPC0gMjUKCnkgPj0gMjAgJiB5IDw9IDMwICAgICAgICMgc8O6xI1hc25lIChBTkQpIC0gxI3DrXNsbyBqZSBtZWR6aSAyMCBhIDMwCnkgPCAwIHwgeSA+IDEwMCAgICAgICAgICMgYWxlYm8gKE9SKSAtIMSNw61zbG8gamUgbWVuxaFpZSBha28gMCBhbGVibyB2w6TEjcWhaWUgYWtvIDEwMAooeSA8IDIwICYgeSA+IDEwKSB8ICh5ID4gMzApICAgIyBrb21iaW7DoWNpYSBBTkQgYSBPUiAtIHBsYXTDrSwgYWsgeSBqZSBtZWR6aSAxMCBhIDIwIGFsZWJvIHbDpMSNxaFpZSBha28gMzAKYGBgCgojIyBabHXEjW92YW5pZSB2aWFjZXLDvWNoIGxvZy4gcHJlbWVubsO9Y2ggZG8gdmVrdG9yYQoKYGBge3J9CmNoZWNrcyA8LSBjKFRSVUUsIFRSVUUsIEZBTFNFLCBGQUxTRSwgVFJVRSkgICAjIHZla3RvciBsb2dpY2vDvWNoIGhvZG7DtHQKCmBgYAoKIyMgTWFsw6kgY3ZpxI1lbmllCgojIyMgWsOha2xhZHkKCj4gVnl0dm9ydGUgZHZlIGxvZ2lja8OpIHByZW1lbm7DqSBgYWAgYSBgYmAuICAKPiAtIFppc3RpdGUgbmVnw6FjaXUgYGFgLiAgCj4gLSBWeXBvxI3DrXRhanRlIEFORCAoYCZgKSBhIE9SIChgfGApLiAgCj4gLSBWeXNrw7rFoWFqdGUgYWogWE9SIG1lZHppIG5pbWkuICAKCmBgYHtyfQphIDwtIFRSVUUKYiA8LSBGQUxTRQoKIWEKYSAmIGIKYSB8IGIKeG9yKGEsIGIpCmBgYAoKIyMjIExvZ2lja8O9IHbDvXNsZWRvayBwb3Jvdm7DoXZhbmlhCgo+IFNrb250cm9sdWp0ZSwgxI1pIHBsYXRpYSBuYXNsZWR1asO6Y2UgcG9kbWllbmt5OiAgCj4gLSDEjcOtc2xvIDUwIGplIHbDpMSNxaFpZSBha28gNDAgYSBtZW7FoWllIGFrbyA2MCwgIAo+IC0gxI3DrXNsbyAxNSBuaWUgamUgcm92bsOpIDEwLCAgCj4gLSByZcWlYXplYyAiYmFuYW5hIiBqZSByb3Zuw70gImJhbmFuYSIsICAKPiAtIHJlxaVhemVjICJBcHBsZSIgamUgbWVuxaHDrSBha28gImFwcGxlIiAobGV4aWtvZ3JhZmlja3kpLCAgCj4gLSBvdmVydGUsIMSNaSA3ICsgMyA9IDEwIGEgxI1pIDIwIC0gNSAhPSAxNS4KCmBgYHtyfQpudW0gPC0gNTAKbnVtID49IDQwICYgbnVtIDw9IDYwICAgICAgICMgQU5ECjE1ICE9IDEwICAgICAgICAgICAgICAgICAgICAgIyBuZXJvdm5vc8WlCiJiYW5hbmEiID09ICJiYW5hbmEiICAgICAgICAgIyByb3Zub3PFpSByZcWlYXpjb3YKIkFwcGxlIiA8ICJhcHBsZSIgICAgICAgICAgICAjIGxleGlrb2dyYWZpY2vDqSBwb3Jvdm5hbmllCjcgKyAzID09IDEwICAgICAgICAgICAgICAgICAgIyByb3Zub3PFpSB2w71yYXp1CjIwIC0gNSAhPSAxNSAgICAgICAgICAgICAgICAgICMgbmVyb3Zub3PFpSB2w71yYXp1CmBgYAoKIyMjIFpsb8W+aXRlasWhaWUgbG9naWNrw6kgb3BlcsOhY2llCgo+IFBvdcW+aXRlIMSNw61zbG8gYHkgPC0gMjVgIGE6ICAKPiAtIG92ZXJ0ZSwgxI1pIGplIG1lZHppIDIwIGEgMzAgKEFORCksICAKPiAtIG92ZXJ0ZSwgxI1pIGplIG1lbsWhaWUgYWtvIDAgYWxlYm8gdsOkxI3FoWllIGFrbyAxMDAgKE9SKSwgIAo+IC0ga29tYmludWp0ZSBwb2RtaWVua3k6IG1lZHppIDEwIGEgMjAgYWxlYm8gdsOkxI3FoWllIGFrbyAzMCwgIAo+IC0gdnlza8O6xaFhanRlIG5lZ8OhY2l1IGNlbGVqIGtvbWJpbsOhY2llLiAgCgpgYGB7cn0KeSA8LSAyNQoKeSA+PSAyMCAmIHkgPD0gMzAgICAgICAgICAgICAgICAgICAgICAgICAjIEFORAp5IDwgMCB8IHkgPiAxMDAgICAgICAgICAgICAgICAgICAgICAgICAgICMgT1IKKHkgPCAyMCAmIHkgPiAxMCkgfCAoeSA+IDMwKSAgICAgICAgICAgICAjIGtvbWJpbsOhY2lhIEFORCBhIE9SCiEoICh5IDwgMjAgJiB5ID4gMTApIHwgKHkgPiAzMCkgKSAgICAgICAgIyBuZWfDoWNpYSBjZWxlaiBrb21iaW7DoWNpZQpgYGAKCgojIyMgWmx1xI1vdmFuaWUgdmlhY2Vyw71jaCBsb2cuIHByZW1lbm7DvWNoIGRvIHZla3RvcmEKCj4gVnl0dm9ydGUgdmVrdG9yIGB2YWxzIDwtIGMoNSwgMTUsIDI1LCAzNSwgNDUpYCBhOiAgCj4gLSB6aXN0aXRlLCBrdG9yw6kgaG9kbm90eSBzw7ogdsOkxI3FoWllIGFrbyAyMCwgIAo+IC0ga3RvcsOpIGhvZG5vdHkgc8O6IG1lbsWhaWUgYWtvIDMwLCAgCj4gLSBrdG9yw6kgaG9kbm90eSBzw7ogesOhcm92ZcWIIHbDpMSNxaFpZSBha28gMjAgYSBtZW7FoWllIGFrbyA0MCwgIAo+IC0gc2tvbWJpbnVqdGUgT1IgYSBBTkQgdm8gdsO9cmF6ZSBwcmUga2HFvmTDvSBwcnZvaywgYWJ5IHN0ZSB6aXN0aWxpLCBrdG9yw6kgaG9kbm90eSBzw7ogbWVuxaFpZSBha28gMTAgYWxlYm8gdsOkxI3FoWllIGFrbyA0MC4gIAoKYGBge3J9CnZhbHMgPC0gYyg1LCAxNSwgMjUsIDM1LCA0NSkKCnZhbHMgPiAyMCAgICAgICAgICAgICAgICAgIyBUUlVFIHByZSBwcnZreSB2w6TEjcWhaWUgYWtvIDIwCnZhbHMgPCAzMCAgICAgICAgICAgICAgICAgIyBUUlVFIHByZSBwcnZreSBtZW7FoWllIGFrbyAzMAp2YWxzID4gMjAgJiB2YWxzIDwgNDAgICAgICMga29tYmluw6FjaWEgQU5ECih2YWxzIDwgMTAgfCB2YWxzID4gNDApICAgIyBrb21iaW7DoWNpYSBPUgpgYGAKCgoKLS0tCgojIE51bWVyaWNrw6kgdmVrdG9yeQoKIyMgR2VuZXJvdmFuaWUgdmVrdG9yb3YKCmBgYHtyfQphMSA8LSBjKDEwLCAyMCwgMzAsIDQwKQphMiA8LSA1OjEyICAgICAgICAgICAgICAgICAgICAgICAgIyBwb3N0dXBub3PFpSDEjcOtc2VsIG9kIDUgcG8gMTIKYTMgPC0gc2VxKGZyb20gPSAyLCB0byA9IDQsIGJ5ID0gMC41KSAgICMgcG9zdHVwbm9zxaUgcyBrcm9rb20gMC41CmE0IDwtIHJlcCg3LCB0aW1lcyA9IDYpICAgICAgICAgICAjIHZla3RvciBzbyDFoWllc3RpbWkgc2VkbWnEjWthbWkKYTUgPC0gcnVuaWYoNiwgbWluID0gLTEsIG1heCA9IDEpICMgbsOhaG9kbsOpIMSNw61zbGEgeiBpbnRlcnZhbHUgWy0xLDFdCmE2IDwtIHJub3JtKDYsIG1lYW4gPSA1MCwgc2QgPSAxMCkgIyBub3Jtw6FsbmUgcm96ZGVsZW5pZSBzbyBzdHJlZG9tIDUwCgphMTsgYTI7IGEzOyBhNDsgYTU7IGE2CmBgYAoKIyMgQXJpdG1ldGlja8OpIG9wZXLDoWNpZSBzIHZla3Rvcm1pCgpgYGB7cn0KYiA8LSBjKDIsIDQsIDYsIDgpCmIgLSAzICAgICAgICAgICAgICMga2HFvmTDvSBwcnZvayB6bWVuxaHDrW1lIG8gMwpiIC8gMiAgICAgICAgICAgICAjIGthxb5kw70gcHJ2b2sgdnlkZWzDrW1lIGR2b21pCihiXjIpICsgMSAgICAgICAgICMgZHJ1aMOhIG1vY25pbmEga2HFvmTDqWhvIHBydmt1IHBsdXMgMQpsb2coYikgICAgICAgICAgICAjIHByaXJvZHplbsO9IGxvZ2FyaXRtdXMgcHJ2a292CnN1bShjKDIsNCw2KSwgYygxLDEsMSkpICAgICAgICAgIyBza2Fsw6Fybnkgc8O6xI1pbiAoc2tyw6F0ZW7DoSBmb3JtYSkKY3Jvc3Nwcm9kKGMoMiw0LDYpLCBjKDEsMSwxKSkgICAjIHNrYWzDoXJueSBzw7rEjWluIC0gbWF0aWNhIDF4MQpjKDIsNCw2KSAqIGMoMywzLDMpICAgICAgICAgICAgICMgSGFkYW1hcmRvdiBzw7rEjWluCmBgYAoKIyMgTWF0ZW1hdGlja8OpIG9wZXLDoWNpZSBzIDIgdmVrdG9ybWkgcm92bmFrw6lobyByb3ptZXJ1CgpgYGB7cn0KbGVuZ3RoKGMoMTAsMjAsMzAsNDApKQpsZW5ndGgoYTUpICAgICAgICAgICAjIGTEusW+a2EgdmVrdG9yYSBhNQpjKDEwLDIwLDMwLDQwKSArIGE1ICAjIHPDusSNZXQgdmVrdG9yb3YKYygxMCwyMCwzMCw0MCkgLSBhNSAgIyByb3pkaWVsIHZla3Rvcm92CmBgYAoKIyMgSW5kZXhvdmFuaWUgYSB2w71iZXIgbmlla3RvcnljaCBwcnZrb3YgdmVrdG9yYQoKYGBge3J9CmMxIDwtIGMoOSwgMTUsIDIsIDI3LCA1LCAxOCwgMCkKYzFbM10gICAgICAgICAgIyB0cmV0w60gcHJ2b2sKYzFbMjo1XSAgICAgICAgIyBkcnVow70gYcW+IHBpYXR5IHBydm9rCmMxWy00XSAgICAgICAgICMgdsWhZXRrbyBva3JlbSDFoXR2cnTDqWhvIHBydmt1CmMxW2MxICUlIDMgPT0gMF0gICMgbGVuIHRpZSBwcnZreSwga3RvcsOpIHPDuiBkZWxpdGXEvm7DqSB0cm9taQp3aGljaChjMSA8IDEwKSAjIGluZGV4eSBwcnZrb3YgbWVuxaHDrWNoIGFrbyAxMApgYGAKCiMjIFByw6FjYSBzIGNow71iYWrDumNpbWkgaG9kbm90YW1pCgpgYGB7cn0KZCA8LSBjKE5BLCA0LCA4LCBOQSwgMTIsIDE2KQppcy5uYShkKSAgICAgICAgICAgICAgICAKc3VtKGlzLm5hKGQpKSAgICAgICAgICAgIyBwb8SNZXQgY2jDvWJhasO6Y2ljaCBob2Ruw7R0Cm1lYW4oZCwgbmEucm0gPSBUUlVFKSAgICMgcHJpZW1lciBiZXogTkEKbWVkaWFuKGQsIG5hLnJtID0gVFJVRSkgIyBtZWRpw6FuIGJleiBOQQpgYGAKCiMjIFrDoWtsYWRuw6kgxaF0YXRpc3Rpa3kgYSB1c3BvcmlhZGFuaWUgcHJ2a292IHZla3RvcmEgcG9kxL5hIHZlxL5rb3N0aQoKYGBge3J9CmUgPC0gYygyNSwgNywgMTQsIDMyLCAxOSwgMykKbWVhbihlKSAgICAgICAgICAgICAgICAgIApzZChlKSAgICAgICAgICAgICAgICAgICAgCm1pbihlKSAgICAgICAgICAgICAgICAgICAKbWF4KGUpICAgICAgICAgICAgICAgICAgIApzdW1tYXJ5KGUpICAgICAgICAgICAgICAgCnNvcnQoZSkgICAgICAgICAgICAgICAgICAKc29ydChlLCBkZWNyZWFzaW5nID0gVFJVRSkgCmBgYAoKIyMgTWFsw6kgY3ZpxI1lbmllIDEKPiBWeXR2b3J0ZSB2ZWt0b3IgYGZgIHMgxI3DrXNsYW1pIDUwLi4xMDAgYSB2eXBvxI3DrXRhanRlIGFyaXRtZXRpY2vDvSBwcmllbWVyIHbFoWV0a8O9Y2ggbmVww6FybnljaCDEjcOtc2VsLgoKYGBge3J9CmYgPC0gNTA6MTAwCm1lYW4oZltmICUlIDIgPT0gMV0pCmBgYAoKIyMgTWFsw6kgY3ZpxI1lbmllIDIKPiBWeXR2b3J0ZSB2ZWt0b3IgMTAgbsOhaG9kbsO9Y2ggxI3DrXNlbCB6IGludGVydmFsdSBbLTUsNV0gYSBzcG/EjcOtdGFqdGUsIGtvxL5rbyB6IG5pY2ggamUga2xhZG7DvWNoLgoKYGBge3J9CmcgPC0gcnVuaWYoMTAsIG1pbiA9IC01LCBtYXggPSA1KQpnCnN1bShnID4gMCkKYGBgCgojIyBNYWzDqSBjdmnEjWVuaWUgMwo+IFZ5dHZvcnRlIHZla3RvciBgaGAgcyBob2Rub3RhbWkgb2QgMSBkbyAxMiBhIHppc3RpdGUsIGFrw70gamUgcm96ZGllbCBtZWR6aSBzw7rEjXRvbSDEjcOtc2VsIGRlbGl0ZcS+bsO9Y2ggNCBhIHPDusSNdG9tIMSNw61zZWwgZGVsaXRlxL5uw71jaCAzLgoKYGBge3J9CmggPC0gMToxMgpzdW00IDwtIHN1bShoW2ggJSUgNCA9PSAwXSkKc3VtMyA8LSBzdW0oaFtoICUlIDMgPT0gMF0pCnN1bTQgLSBzdW0zCmBgYAoKIyBNw7RqIG7DoXZyaCBwb3XFvml0aWEgbm92aW5reQo+IFYgc2VrY2lpICoqIlNrYWzDoXJlIioqIHNvbToKCj4qIHBvdcW+aWxhIGFqIHRyZXRpdSBwcmVtZW5uw7ogYyAtIGnFoWxvIGtvbmtyw6l0bmUgbyB6w6Fwb3Juw7ogaG9kbm90dSwgxI3DrW0gc29tIHJvesWhw61yaWxhIG1vxb5ub3N0aSB2w71wb8SNdG92IG8gcHLDoWN1IHNvIHpuYW1pZW5rYW1pLAoKPiogdXJvYmlsYSB2bGFzdG7DqSB6bG/Fvml0ZWrFoWllIGtvbWJpbsOhY2llIHbDvXBvxI10b3bDvWNoIG9wZXLDoWNpw60sCgo+KiBwb3XFvmlsYSDEj2FsxaFpZSBtYXRlbWF0aWNrw6kgZnVua2NpZSwgYWtvIHPDuiBhYnNvbMO6dG5hIGhvZG5vdGEsIGRydWjDoSBvZG1vY25pbmEgYSB6YW9rcsO6aMS+b3ZhbmllIG5hIDEgZGVzYXRpbm7DqSBtaWVzdG8sCgo+KiBzb20gbmFtaWVzdG8gamVkbsOpaG8gamVkbm9kdWNow6lobyBjdmnEjWVuaWEgemFocm51bGEgdHJpIHLDtHpuZSDDumxvaHksIGtkZSBzYSBrb21iaW51asO6IG1vY25pbnksIHpsb21reSwgc8O6xI10eSBhIHPDusSNaW55LgoKPiBWIHNla2NpaSAqKiJUZXh0IioqIHNvbToKCj4qIHByaWRhbGEgbWFsw6kgY3ZpxI1lbmllLCBrdG9yw6kgcm96xaFpcnVqZSBwcsOhY3UgcyB0ZXh0b20gYSBkw6F2YSBwcmllc3RvciBuYSB0dm9yZW5pZSB2bGFzdG7DqWhvIHByw61rbGFkdS4KCj4gViBzZWtjaWkgKioiTG9naWNrw6kgaG9kbm90eSIqKiBzb206Cgo+KiBwcmlkYWxhIHZpYWMgcHLDrWtsYWRvdiBsb2dpY2vDvWNoIHBvcm92bmFuw60gdnLDoXRhbmUgxI3DrXNlbCwgdsO9cmF6b3YgYSB0ZXh0b3YsCgo+KiBkb3BsbmlsYSBqZWRub3RsaXbDqSBvcGVyw6FjaWUgbyBwb2Ryb2JuZWrFoWllIGtvbWVudMOhcmUsCgo+KiB1a8OhemFsYSBrb21iaW5vdmFuw6kgbG9naWNrw6kgdsO9cmF6eSBzIEFORCwgT1IgYSBuZWfDoWNpb3UsCgo+KiBwcmkgemx1xI1vdmFuw60gbG9naWNrw71jaCBwcmVtZW5uw71jaCBwb3XFvmlsYSDEjcOtc2VsbsO9IHZla3RvciBhIHBvZG1pZW5rb3bDqSBmaWx0cm92YW5pZSwKCj4qIGRvcGxuaWxhIDQgYmxva3kgY3ZpxI1lbsOtIChqZWRlbiBrdSBrYcW+ZGVqIHBvZGthcGl0b2xlKSwga3RvcsOpIHJvenbDrWphasO6IHrDoWtsYWRuw6kgYWogcG9rcm/EjWlsw6kgem5hbG9zdGkuCgo+IFYgc2VrY2lpICoqIk51bWVyaWNrw6kgdmVrdG9yeSIqKiBzb206Cgo+KiBwcmkgYXJpdG1ldGlja8O9Y2ggb3BlcsOhY2nDoWNoIHBvdWvDoXphbGEgbmEgb2TEjcOtdGFuaWUsIGRlbGVuaWUsIG1vY25pbnkgYSBsb2dhcml0bWlja8O6IGZ1bmtjaXUsCgo+KiByb3rFocOtcmlsYSBvcGVyw6FjaWUgcyBkdm9taSB2ZWt0b3JtaSBvIHJvemRpZWwgdmVrdG9yb3YsCgo+KiBwcmkgaW5kZXhvdmFuw60gYSB2w71iZXJlIHByaWRhbGEgcHLDrWtsYWQgdsO9YmVydSBwb2TEvmEgZGVsaXRlxL5ub3N0aSwgbmllbGVuIHBvZMS+YSB2ZcS+a29zdGksCgo+KiBkb3BsbmlsYSBwcsOhY3UgcyBjaMO9YmFqw7pjaW1pIGhvZG5vdGFtaSBhIHrDoWtsYWRuw6kgxaF0YXRpc3Rpa3kgbyDEj2FsxaFpZSBmdW5rY2llLg==