Skalarji (angl. scalars)
R ima štiri osnovne tipe skalarjev oziroma njihovih vrednosti:
Logični vrednosti (angl. logical) sta
TRUE
in FALSE
ali na kratko T
in
F
.
Celoštevilske (angl. integer) vrednosti
zapišemo kot številko, ki ji sledi simbol L
, npr.
123456L
. Če je celoštevilska vrednost prevelika ali
premajhna (po absolutni vrednosti večja od
.Machine$integer.max
, na mojem računalniku je to
2,147,483,647) ali smo ji pomotoma dodali decimalke (in torej ni več
celoštevilska), jo R avtomatsko pretvori v numerično vrednost (glej
naslednjo alinejo).
Numerične vrednosti (angl. double) v plavajoči
vejici (angl. floating point) lahko zapišemo v
navadni 123.456
ali znanstveni notaciji
1.23456e2
. Tri posebne numerične vrednosti so
-Inf
, npr. -1/0
, Inf
, npr.
1/0
, in NaN
, npr. 0/0
.
Nize znakov (angl. string) zapišemo med
enojne '
ali dvojne "
narekovaje. Če niz
znakov vsebuje narekovaje, slednje zapišemo s predpono \
,
npr. "To je primer niza znakov z \"narekovaji\""
. Dolžina
niza znakov je število znakov v nizu. Prazen niz znakov ""
ima dolžino 0.
Spremenljivki v R-ju dodelimo (novo) skalarno vrednost z operatorjem
<-
. Gre za tradicionalni zapis, iz časov nastanka
programskega okolja R. Danes R omogoča uporabo običajnega operatorja
=
, ki ga poznamo iz drugih programskih jezikov.
Po dodelitvi, tip spremenljivke se spremeni v tip skalarne vrednosti,
ki ji jo dodelimo. Pri izračunih, ki vključujejo vrednosti in
spremenljivke različnih tipov, se tipi dinamično (samodejno, v
terminologiji R tudi prisilno, angl. coercion) in po potrebi
prilagodijo tako, da bo izračun možno izpeljati. Prisilno
prilagajanje tipov vedno poteka v smer, ki jo nakazuje vrstni red
naštevanja štirih tipov zgoraj: logična vrednost se pretvori v
celoštevilsko, ta nato v numerično (plavajočo vejico), ter na koncu še
(seveda po potrebi) v niz znakov.
Pozor, prilagajanje tipov včasih tudi (neprijetno)
preseneti, npr. 1 == "1"
(operator ==
preveri,
če sta izraza na levi in desni strani enaka). Izraz, ki bi lahko bil
rezultat programske napake, R brez težav (prijavljanja napak ali
opozoril) ovrednoti v TRUE
in ne v morebiti bolj
pričakovano vrednost FALSE
(prepričaj se, da je temu tako,
nato premisli in pojasni zakaj).
Tip poljubne spremenljivke, vrednosti ali izračuna, lahko preverimo s
funkcijo typeof
. Primer, ki ponazori samodejno pretvorbo
med različnimi osnovnimi tipi:
x = 1L + TRUE
x
[1] 2
typeof(x)
[1] "integer"
x = 1L + TRUE + 1
x
[1] 3
typeof(x)
[1] "double"
x = !x
x
[1] FALSE
typeof(x)
[1] "logical"
Zamenjavo tipa vrednosti lahko sami sprožimo s funkcijami
as.logical
, as.integer
, as.double
in as.character
. S temi funkcijami lahko preverimo kako R
pretvarja oz. prilagaja tipe skalarjev. Med zanimivimi klici te funkcije
sodijo npr. as.logical(0)
,
as.character(FALSE)
, as.integer(2.8)
.
Preden poskusite ukaze, preverite, ali znate pravilno napovedati
njihovo delovanje.
Še ena, zelo pomembna skalarna vrednost je neznana
vrednost NA
. Bodite pozorni na naslednji dve
dejstvi (zakaj je temu tako?):
NA == 0
[1] NA
NA == NA
[1] NA
Bodite pozorni: neznana vrednost NA
je
“kužna”. V večini primerov je vrednost izraza (izračuna), ki vključuje
NA
, enaka NA
. Poznamo le nekaj redkih izjem,
npr. NA & FALSE
, NA | TRUE
in
NA ^ 0
. Znova premisli zakaj je temu tako.
Za preverjanje ali je vrednost spremenljivke ali izraza neznana
uporabimo funkcijo is.na
. Na voljo so tudi štiri podobne
funkcije s katerimi lahko preverimo, če je izraz nekega določenega tipa:
is.logical
, is.integer
, is.double
in is.character
. Rezultati vseh teh funkcij so logične
vrednosti.
Naj na tem mestu omenimo še oznako NULL
, ki v R-ju
nakazuje odsotnost vrednosti. Za razliko od NA
, ki nakazuje
prisotnost vrednosti, ki pa ni znana.
NULL == NULL
logical(0)
NULL == 0
logical(0)
NULL == NA
logical(0)
V vseh treh primerih je rezultat logical(0)
, kar je
oznaka za prazen vektor logičnih vrednosti. Podobno kot
NULL
tudi prazen vektor nakazuje odsotnost vrednosti.
Zadnja dva stavka omenjata podatkovni tip vektor, ki ga tudi štejemo med
osnovnimi podatkovnimi tipi v R-ju. Spoznali ga bomo v nadaljevanju.
Vektorji (angl. vectors, atomic vectors)
Vektor predstavlja urejen seznam skalarjev istega
tipa. Skalarje imenujemo elementi ali
komponente seznama. Urejenost seznama pomeni, da je
vrstni red elementov pomemben in lahko elemente naslavljamo na osnovi
njihove pozicije v seznamu (urejenost namreč ne pomeni, da so elementi
urejeni po naraščajoči ali padajoči vrednosti). Z drugimi besedami,
znamo povedati kateri element seznama je prvi, drugi ali zadnji.
Vektorje najbolj preprosto ustvarjamo s klicem funkcije
c
, ki sprejme poljubno število argumentov in iz njih
sestavi seznam. Dolžino vektorja, ki je enaka številu njegovih
elementov, nam vrne funkcija length
. Vektor dolžine 0
imenujemo prazen vektor. Že omenjena funkcija typeof
pa nam
vrne tip elementov vektorja (ne pozabimo, da so vsi elementi vektorja
enakega tipa).
vl = c(TRUE, FALSE, TRUE, TRUE)
typeof(vl)
[1] "logical"
vi = c(-20L, -10L, 0L, 10L, 20L)
length(vi)
[1] 5
vd = c(-1, 2.5, 3)
as.integer(vd)
[1] -1 2 3
vs = c("nekaj stringov (ups, nizov znakov)", "za primer", "pa še številka", "42")
x = as.double(vs)
Warning: NAs introduced by coercion
x
[1] NA NA NA 42
is.na(x)
[1] TRUE TRUE TRUE FALSE
Bodimo posebej pozorni na zadnje tri rezultate. Pri pretvarjanju
nizov znakov v numerične vrednosti, Tiste nize znakov, ki se jih ne da
(oziroma jih R ne zna) pretvoriti v numerične vrednosti, R pretvori v
neznane vrednosti NA
. Pri tem izpiše opozorilo (angl.
warning)
Neznane vrednosti NA vpeljane s (prisilnim) prilagajanjem tipov
(angl. NAs introduced by coercion).
Poglejmo še kaj se zgodi, če poskusimo sestaviti vektor iz vrednosti
različnih tipov:
x = c(TRUE, 1L)
x
[1] 1 1
typeof(x)
[1] "integer"
x = c(FALSE, 1, 2L)
x
[1] 0 1 2
typeof(x)
[1] "double"
x = c(TRUE, 1, 2.5)
x
[1] 1.0 1.0 2.5
typeof(x)
[1] "double"
x = c(FALSE, 1, 2L, "3")
x
[1] "FALSE" "1" "2" "3"
typeof(x)
[1] "character"
Premislite in pojasnite zakaj so tipi teh vektorjev taki kot so?
Čeprav vektorji predstavljajo le enodimenzionalne sezname vrednosti
istega tipa, z njimi lahko predstavimo tudi dvodimenzionalne matrike ali
polja (tenzorje) poljubnega števila dimenzij. Zato da razumemo kako,
spoznajmo najprej koncept vektorskih atributov.
Atributi vektorjev in matrike
Atributi so urejeni pari oblike (ime, vrednost), ki vektorje označijo
z meta podatki. Za nastavljanje vrednosti atributov vektorja
uporabljamo funkcijo attr
, ki jo lahko tudi uporabimo za
preverjanje vrednosti atributa. Funkcija attributes
vrne
vse atribute podanega vektorja.
v = c(-20L, -10L, 0L, 10L, 20L)
attr(v, "prvi_atribut") = 1
attr(v, "drugi_atribut") = 2.3
print("Prvi atribut vektorja v")
[1] "Prvi atribut vektorja v"
attr(v, "prvi_atribut")
[1] 1
print("Vsi atributi vektorja v")
[1] "Vsi atributi vektorja v"
attributes(v)
$prvi_atribut
[1] 1
$drugi_atribut
[1] 2.3
Rezultat funkcije attributes
pravzaprav seznam
atributov in njihovih vrednosti. R-jevsko podatkovno strukturo seznam
bodo spoznali pozneje. Pozor: Vektorji v R-ju
so podatkovna struktura, ki je podobna strukturi seznam v
Python-u.
Funkcija structure
omogoča dodajanje atributov že ob
tvorjenju vektorja:
v = structure(
c(-20L, -10L, 0L, 10L, 20L),
prvi_atribut = 1,
drugi_atribut = 2.3
)
v
[1] -20 -10 0 10 20
attr(,"prvi_atribut")
[1] 1
attr(,"drugi_atribut")
[1] 2.3
attributes(v)
$prvi_atribut
[1] 1
$drugi_atribut
[1] 2.3
Atributi so začasni. Mnoge funkcije in operacije nad seznami
izbrišejo (pozabijo) atribute, preizkusite npr. v[1]
ali
sum(v)
:
attributes(vi[1])
NULL
attributes(sum(vi))
NULL
Vrednost izraza v[1]
je enaka vrednosti prvega elementa
vektorja v
. Funkcija sum
vrne vsoto vrednosti
elementov podanega vektorja.
Dva atributa imajo v R-ju poseben pomen:
names
je vektor nizov znakov, ki je enake dolžine kot
originalni vektor. Vsak element vektorja names
določa
ime elementa originalnega vektorja.
dim
je vektor celoštevilskih vrednosti, ki pretvori
vektor v matriko ali polje.
Poimenovan vektor je vektor v katerem ima vsak
element svoje ime. Imena elementov vektorja lahko nastavimo na vsaj dva
načina: pri tvorjenju vektorja ali pa naknadno za navadni vektor z
uporabo funkcije names
:
x1 = c(prvi = 1, drugi = 2, tretji = 3)
x1
prvi drugi tretji
1 2 3
x2 = c(1, 2, 3)
names(x2) = c("prvi", "drugi", "tretji")
x2
prvi drugi tretji
1 2 3
Funkcijo names
vrne imena elementov v poimenovanem
seznamu. Preverite kaj vrne funkcija names
za neimenovan
seznam, npr. names(c(1,2,3))
. Poimenovan seznam lahko
spremenimo v neimenovanega (torej v vektor z elementi brez imen) s
klicem funkcije unname
:
x3 = unname(x2)
x3
[1] 1 2 3
Dimenzije vektorja, t.j., vrednost atributa dim
določimo
lahko pri tvorjenju vektorja ali naknadno z uporabo funkcije
dim
:
x = c(1, 2, 3, 4, 5, 6)
A1 = matrix(x, nrow = 2, ncol = 3)
A1
[,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6
dim(A1)
[1] 2 3
A2 = x
dim(A2) = c(3, 2)
A2
[,1] [,2]
[1,] 1 4
[2,] 2 5
[3,] 3 6
Tvorjenje vektorjev
Oglejmo si nekaj načinov za tvorjenje vektorjev:
i:j
je celoštevilski vektor z elementi
i, i+1, ..., j
, če je j >= i
oziroma
elementi i, i-1, \ldots, j
, če je
j < i
.
seq(from = i, to = j, by = s)
je vektor numeričnih
vrednosti, kjer je prvi element i
, razlika med dvema
zaporednima elementoma je s
, zadnji element pa ne sme
preseči vrednosti j
. Pri različici
seq(from = i, to = j, length.out = n)
namesto razlike med
zaporednima elementoma določimo število elementov, ki se ekvidistančno
(enakomerno) razporedijo na intervalu [from
,
to
].
rep(v, times = n)
je vektor sestavljen tako, da
elemente v
ponovimo n
-krat. Če je
v
skalar, je dolžina dobljenega vektorja n
, če
pa je v
vektor, je dolžina dobljenega vektorja
n * length(v)
. Pri različici rep(v, each = n)
določimo kolikokrat ponovimo vsak posamezen element
v
.
sample(v, n)
je vektor dolžine n
(imenujemo ga vzorec), katerega elementi so naključno izbrani elementi
vektorja v
. Posamezen element iz v
se lahko v
vzorcu pojavi le enkrat. Posledica tega je, da je predpogoj za uspešno
vzorčenje length(v) >= n
. Različica
sample(v, n, replace=T)
opravi vzorčenje s ponavljanjem,
kjer je posamezen element v
se lahko v vzorcu pojavi tudi
večkrat.
Poigraj se z naslednjimi primeri:
-1:13
[1] -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13
13:-1
[1] 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1
13:13
[1] 13
seq(from = 0.5, to = -0.5, by = -0.1)
[1] 0.5 0.4 0.3 0.2 0.1 0.0 -0.1 -0.2 -0.3 -0.4 -0.5
seq(from = -0.5, to = 0.5, length.out = 4)
[1] -0.5000000 -0.1666667 0.1666667 0.5000000
rep(-1:1, times = 3)
[1] -1 0 1 -1 0 1 -1 0 1
rep(-1:1, each = 3)
[1] -1 -1 -1 0 0 0 1 1 1
sample(-10:10, 12)
[1] 2 -1 5 -9 7 3 -4 1 4 -8 6 10
sample(-10:10, 12)
[1] -6 10 8 -3 -5 -7 -10 -8 7 9 6 1
# sample(-10:10, 24): pojasni napako, ki jo javi ta klic funkcije sample
sample(1:6, 10, replace=T)
[1] 4 3 2 2 2 5 6 6 2 6
Indeksiranje vektorjev
Indeks elementa v vektorju v
ustreza njegovi poziciji v
urejenem seznamu. Tako je indeks prvega elementa vektorja 1, drugega 2,
in tako naprej, vse do indeksa zadnjega elementa. Ta je enak dolžini
vektorja length(v)
. Pozor: to je, poleg
poimenovanja vektorjev, še ena pomembna razlika med R-jem in programskim
jezikom Python, ki nas pogosto zmede. V Pythonu je indeks prvega
elementa seznama 0, drugega 1 in zadnjega len(v) - 1
.
Posamezen element vektorja v
naslovimo z izrazom
v[i]
za poljuben element i
celoštevilskega
vektorja 1:length(x)
. Tako naslovljenemu elementu lahko
dodelimo novo vrednost z v[i] = nova_vrednost
ali pa ga
uporabimo v poljubnem izrazu, npr. v[i] + 2
. Tako
indeksiranje in naslavljanje posameznega elementa vektorja pozna tudi
večina drugih programskih jezikov. Za razliko od slednjih, R ponuja še
veliko drugih možnosti indeksiranja, ki jih bomo spoznali v
nadaljevanju. Fleksibilnost indeksiranja vektorjev je ključnega pomena
pri uporabi R-ja za podatkovno analizo.
Poglejmo štiri načine naslavljanja poljubnega
števila elementov vektorja v
. Vsi so oblike
v[vi]
, kjer je v
vektor katerega elemente
indeksiramo in vi
vektor indeksov.
Elementi vi
so pozitivna cela
števila: rezultat indeksiranja je vektor elementov vektorja
v
, katerih indeksi v v
so elementi vektorja
vi
. Element i
indeksa se lahko tudi večkrat
ponovi v vektorju indeksov vi
. To povzroči, da se tudi
element v[i]
pojavi večkrat v rezultatu
indeksiranja.
Elementi vi
so negativna cela
števila: rezultat indeksiranja je vektor elementov
v
, katerih indeksi niso elementi vektorja
absolutnih vrednosti elementov vi
. Z drugimi besedami
element -i
v vektorju indeksov vi
povzroči, da
element v[i]
ni element rezultata
indeksiranja.
Pozor: Pozitivnih in negativnih
celoštevilčnih vrednosti indeksov v vektorju vi
ne smemo
kombinirati.
Elementi vi
so logične vrednosti:
rezultat indeksiranja je vektor elementov v
za katere imajo
enako-ležeči elementi vi
vrednost TRUE
.
Običajno sta vektorja v
in vi
enakih dolžin.
Če je vektor vi
daljši od vektorja v
, se pri
indeksiranju upošteva le prvih length(v)
njegovih
elementov. Če je vektor vi
krajši od vektorja
v
, se njegovi elementi reciklirajo, kot bo opisano
v ustrenzem razdelku spodaj.
Elementi vi
so nizi znakov:
rezultat indeksiranja je vektor elementov imenovanega
vektorja v
, katerih imena so v seznamu vi
. Če
se ime i
v vektorju vi
pojavi večkrat, se tudi
v rezultatu indeksiranja element v[i]
pojavi
večkrat.
Pozor: neznana vrednost NA
v vektorju
indeksov vi
vedno vrne element NA
.
Dva posebna primera indeksiranja sta v[]
, katerega
rezultat je enak v
ter v[0]
, katerega rezultat
je prazen vektor (slednje pogosto zmede programerje v Python-u).
Oznaka za prazen vektor je logical(0)
,
integer(0)
, numeric(0)
ali
charcter(0)
, v odvisnosti od tipa vrednosti elementov
vektorja.
Nekaj primerov, kjer je vi
vektor celoštevilskih
vrednosti:
v = c(1.1, -2.2, 3.3, -4.4)
v[3:1] # običajno
[1] 3.3 -2.2 1.1
v[c(1, 3, 1)] # ponovitev istega elementa
[1] 1.1 3.3 1.1
v[order(v)] # urejene vrednosti elementov
[1] -4.4 -2.2 1.1 3.3
v[order(v)] = c(1.1, 2.2, 3.3, 4.4) # indeksiranem vektorju lahko dodelimo vrednosti (!)
v
[1] 3.3 2.2 4.4 1.1
v[c(-1, -3)] # negativni indeksi
[1] 2.2 1.1
# v[-1:1] # kombiniranje negativnih in pozitivnih indeksov ni dovoljeno
In še nekaj primerov
v = c(4.2, 3.3, 5.4, 2.1)
v
[1] 4.2 3.3 5.4 2.1
v[c(TRUE, TRUE, FALSE, FALSE)] # logične vrednosti
[1] 4.2 3.3
v[v > 3] # indeksiranje z logičnim pogojem
[1] 4.2 3.3 5.4
v > 3 # kar je pravzaprav indeksiranje z logičnimi vrednostmi
[1] TRUE TRUE TRUE FALSE
v[v > 3] = 3 # dodeljevanje (!)
v
[1] 3.0 3.0 3.0 2.1
v = v[v != 3] # izbira elementov, ki izpolnjujejo pogoj
v
[1] 2.1
Matrike najbolj pogosto indeksiramo z dvema vektorjema:
M = 1:20
dim(M) = c(4, 5)
colnames(M) = toupper(letters[1:ncol(M)])
M
A B C D E
[1,] 1 5 9 13 17
[2,] 2 6 10 14 18
[3,] 3 7 11 15 19
[4,] 4 8 12 16 20
M[1:2, ] # izbira vrstic, vsi stolpci
A B C D E
[1,] 1 5 9 13 17
[2,] 2 6 10 14 18
M[1, ] # ena vrstica, R jo samodejno spremeni v vektor
A B C D E
1 5 9 13 17
M[1, , drop = FALSE] # eno-vrstična matrika, R-ju preprečimo samodejno pretvorbo v vektor
A B C D E
[1,] 1 5 9 13 17
M[, c(2,1)] # izbira stolpcev, vse vrstice - bodite pozorni na vrstni red
B A
[1,] 5 1
[2,] 6 2
[3,] 7 3
[4,] 8 4
M[1, 1] # izbira enega elementa matrike, rezultat je skalar
A
1
M[1, 1, drop = FALSE] # rezultat je matrika dimenzij 1x1
A
[1,] 1
Lahko pa jih indeksiramo tudi z navadnim vektorjem ali matriko, v
obeh primerih je rezultat vektor:
M
A B C D E
[1,] 1 5 9 13 17
[2,] 2 6 10 14 18
[3,] 3 7 11 15 19
[4,] 4 8 12 16 20
M[c(4, 15)]
[1] 4 15
M[c(-4, -15)]
[1] 1 2 3 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20
MI = matrix(
c(
1, 1,
3, 1,
2, 5
),
ncol = 2, byrow = TRUE
)
dim(MI) = c(3, 2)
M[MI]
[1] 1 3 18
Računanje z vektorji in recikliranje elementov vektorja
Vektorji so pravzaprav osnovni tip podatkov v R-ju, tako osnovni, da
skalarje obravnavamo kot posebna vrsta vektorjev z enim
elementom. Zato nad vektorji lahko izvajamo operacije z
osnovnimi aritmetičnimi operatorji in matematičnimi funkcijami. V
nadaljevanju si bomo ogledali posebnosti uporabe operatorjev in funkcij
nad vektorji.
Osnovni operatorji
Za izvajanje osnovnih aritmetičnih operacij so v R-ju na voljo
aritmetični operatorji popisani v spodnji tabeli. V
vseh primerih se oznake vektorjev začnejo z v
, oznake
skalarjev s s
, oznake matrik z m
in oznake
logičnih vrednosti z l
.
+ |
seštevanje |
s1 + s2 , v1 + v2 ,
v + s |
- |
odštevanje |
v1 - v2 , v - s |
* |
množenje |
v1 * v2 , v * s |
/ |
deljenje |
v1 * v2 , v / s |
%/% |
celoštevilsko deljenje |
v1 %/% v2 , v1 %/% s |
%% |
ostanek pri celoštevilskem deljenju |
v1 %% v2 , v1 %% s ,
s %% v1 |
^ |
potenca |
v1 ^ v2 , v ^ s ,
s ^ v |
%*% |
matrično množenje |
m1 %*% m2 |
Kot vidimo iz primerov v zgornji tabeli, operanda podanega operatorja
sta lahko vektorja, skalarja ali pa kombinacija vektorja in skalarja.
Preden pojasnimo pravila za izvajanje operatorjev nad vektorji, si bomo
ogledali še tabelo operatorjev primerjave (prvih šest
vrstic) in logičnih operatorjev (zadnjih pet
vrstic).
== |
enaki vrednosti |
s1 == s2 , v1 == s ,
v1 == v2 |
!= |
različni vrednosti |
s1 != s2 , v1 != s ,
v1 != v2 |
< |
vrednost manjša |
s1 < s2 , v1 < s ,
v1 < v2 |
<= |
vrednost manjša ali enaka |
s1 <= s2 , v1 <= s ,
v1 <= v2 |
> |
vrednost večja |
s1 > s2 , v1 > s ,
v1 > v2 |
>= |
vrednost večja ali enaka |
s1 >= s2 , v1 >= s ,
v1 >= v2 |
& |
konjunkcija, logični in |
s1 & s2 , v1 & v2 ,
v & s |
| |
disjunkcija, logični ali |
s1 | s2 , v1 | v2 ,
v | s |
! |
negacija, logični ne |
! s , ! v |
&& |
konjunkcija dveh logičnih vrednosti |
l1 && l2 |
|| |
disjunkcija dveh logičnih vrednosti |
l1 || l2 |
Zadnja dva operatorja (&&
in ||
)
sta posebna, ker oba obravnavata svoja operanda kot skalarni logični
vrednosti in je tako rezultat operacije skalarna logična vrednost
TRUE
ali FALSE
.
Koristen operator, ki ne sodi v zgoraj opisane kategorije, je
%in%
, ki ga uporabljamo v izrazih oblike
s %in% v
zato, da preverimo, če je skalar s
element vektorja v
. Izraz vrne TRUE
v primeru,
ko je s
element vektorja v
. Sicer pa vrne
FALSE
.
Recikliranje elementov vektorja
Kombiniranje različnih tipov operandov (t.j., vektorjev in skalarjev
ali pa vektorjev različnih dolžin) ima svoje omejitve, ki jih moramo
upoštevati pri pisanju pravilnih izrazov v R-ju.
Pri izvajanju operacije v1 op v2
, morata vektorja
v1
in v2
, ki nastopata kot operanda
(primerjalnega, logičnega ali aritmetičnega) operatorja op
,
izpolnjevati enega izmed naslednjih dveh pogojev.
Vektorja sta enakih dimenzij, t.j., velja
dim(v1) == dim(v2)
. V primeru navadnih vektorjev, ki niso
večdimenzionalni (matrike ali polja oziroma tenzorji), to pomeni, da sta
enakih dolžin length(v1) == length(v2)
. Če sta matriki, to
pomeni, da morata biti enakih dimenzij. Izjema pri matrikah je matrično
množenje, pri katerem moramo upoštevati običajno omejitev da mora imeti
prva matrika toliko stolpcev, kot ima druga matrika vrstic.
Če je
ta pogoj izpolnjen, je rezulat v
izvajanja operacije enakih
dimenzij kot operanda. Prvi element dobimo z uporabo operanda na prvih
elementih operandov, t.j., prvi element rezultata je
v[1] = v1[1] op v2[1]
.
Ta pogoj izpolnjuje tudi
običajna operacija med dvema skalarjema oblike s1 op s2
,
kjer sta očitno oba operanda dimenzije oziroma dolžine 1.
Dolžina daljšega vektorja (označimo ga z v1
) je
večkratnik dolžine krajšega v2
, t.j., velja
length(v1) == length(v2) * k
, kjer je k
naravno število večje od 1. V tem primeru R pred izvajanjem operacije
predela krajši vektor v dalšega tako, da elemente krajšega vektorja
reciklira k
-krat.
Poseben primer tega
pogoja je operacija vektorja s skalarjem (in skalarja z vektorjem),
kajti, kot smo povedali že na začetku tega razdelka, je skalar
pravzaprav vektor dolžine 1. V tem primeru podano skalarno vrednost
recikliramo tolikokrat, kot je dolžina daljšega operanda, torej
vektorja.
Zato, da bolje razumeš recikliranje, se poigraj z naslednjimi
primeri:
v = -4:4
v + TRUE # pojasni rezultat, ne pozabi na prisilno prilagajanje tipov
[1] -3 -2 -1 0 1 2 3 4 5
v > 0 # premisli kako R reciklira vrednosti v tem izračunu,
[1] FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE
v < 1:-1 # kako v tem,
[1] TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE
v < 1:-2 # in zakaj tukaj dobimo opozorilo
Warning: longer object length is not a multiple of shorter object length
[1] TRUE TRUE TRUE FALSE TRUE FALSE FALSE FALSE FALSE
v * (-1:1)
[1] 4 0 -2 1 0 1 -2 0 4
v / (-1:1) # pojasni ta rezultat
[1] 4 -Inf -2 1 NaN 1 -2 Inf 4
Osnovne funkcije nad skalarji in vektorji
V R-ju pogosto uporabljamo dve obliki funkcij, ki kot argument
sprejmejo vektor.
Običajne matematične funkcije oblike \(f: \mathbb{R} \to \mathbb{R}\), kot so npr.
sin
, cos
, log
, log10
ali exp
. Ko tem funkcijam podamo za argument vektor
v
, je rezultat vektor vrednosti funkcije na posameznih
elementih vektorja v
.
Matematične funkcije oblike \(f:
\mathbb{R}^* \to \mathbb{R}\), torej funkcije, ki poljubno
število numeričnih argumentov združijo v skalarno vrednost. Ko
tem funkcijam podamo kot argument vektor v
, je rezultat
skalarna vrednost. Primeri takih funkcij so sum
,
mean
, sd
in length
, ki vrnejo
vsoto, povprečno vrednost, standardni odklon in število elementov
vektorja.
Pogosto nam koristijo tudi dve funkciji nad nizi znakov: *
nchar(x)
, ki kot rezultat vrne število znakov v podanem
nizu znakov x
. * paste(x1, x2, ..., xn)
, ki
kot rezultat vrne en niz znakov dobljen s stikom podanih nizov znakov
x1
, x2
, …, xn
z vmesnimi
presledki. Različica te funkcije paste0
opravi stik podanih
nizov brez vmesnih presledkov.
In še nekaj koristnih funkcij za vektorje: * sort(v)
vrne vektor z elementi vektorja v
urejenimi v
naraščajočem vrstnem redu. Različica klica
sort(v, decreasing=TRUE)
uredi elemente vektorja v
padajočem vrstnem redu. * unique(v)
vrne vektor
elementov v
brez ponavljanja, rezultat je torej vektor v
katerega se vsak element pojavi le enkrat. * which(v)
, kjer
je v
vektor logičnih vrednosti, vrne celoštevilski vektor
indeksov elementov v
, ki imajo vrednost TRUE
.
Če je v
poimenovan vektor, vrne vektor z imeni teh
elementov.
Primeri
Poglejmo si nekaj primerov tvorjenja seznamov in operacij nad
njimi.
Tvorjenje vektorjev z :
in primeri operacij, ki
kombinirajo vektorje in skalarje:
1:5
[1] 1 2 3 4 5
5:-5
[1] 5 4 3 2 1 0 -1 -2 -3 -4 -5
2 ^ (0:5)
[1] 1 2 4 8 16 32
(0:5) ^ 2
[1] 0 1 4 9 16 25
(0:5) ^ (5:0)
[1] 0 1 8 9 4 1
x = 10
(2:(x -1))
[1] 2 3 4 5 6 7 8 9
Tvorjenje vektorjev z seq
in funkcije:
seq(0, 1, 0.1)
[1] 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
seq(0, 1, length.out=6)
[1] 0.0 0.2 0.4 0.6 0.8 1.0
seq(0, 1, length.out=6) * seq(0, 1, length.out=3)
[1] 0.0 0.1 0.4 0.0 0.4 1.0
sin(seq(0, pi, length.out=5))
[1] 0.000000e+00 7.071068e-01 1.000000e+00 7.071068e-01 1.224647e-16
mean(0:100)
[1] 50
Ponavljanje z rep
:
rep(-1:1, 3)
[1] -1 0 1 -1 0 1 -1 0 1
rep(-1:1, each=2)
[1] -1 -1 0 0 1 1
unique(rep(-1:1, 3))
[1] -1 0 1
In še naključno vzorčenje s ponavljanjem in brez:
sort(sample(-5:5, 5, replace=F))
[1] -5 -1 1 3 4
sort(sample(-5:5, 5, replace=T))
[1] 0 4 5 5 5
Posebne vrste vektorjev
Faktor (angl. factor) je vektor z elementi,
ki imajo vrednosti iz vnaprej znane, relativno majhne množice možnih
vrednosti. To množico določa atribut faktorja levels
.
Faktorje tvorimo iz vektorjev s funkcijo factor
:
x = factor(c("a", "b", "b", "a"))
x
[1] a b b a
Levels: a b
typeof(x)
[1] "integer"
attributes(x)
$levels
[1] "a" "b"
$class
[1] "factor"
Kot vidimo so faktorji pravzaprav celoštevilski vektorji. Nabor
možnih vrednosti lahko nastavimo tudi naknadno, kar nam pomaga pri
analizi podatkov. Tako, npr. faktorju x
iz zgornje kode
lahko dodamo možno vrednost "c"
, kar spremeni obnašanje
funkcije table
, ki prešteva frekvence pojavitev različnih
vrednosti v faktorju (ali vektorju):
table(x)
x
a b
2 2
levels(x) = c(levels(x), "c")
table(x)
x
a b c
2 2 0
Možne vrednosti faktorjev so v splošnem neurejen. Če so možne
vrednosti elementov urejene, potem je faktor urejen. Urejene faktorje
tvorimo s funkcijo ordered
:
ocene = ordered(
c("dobro", "odlično", "prav dobro", "zadostno", "zadostno"),
levels = c("zadostno", "dobro", "prav dobro", "odlično")
)
ocene
[1] dobro odlično prav dobro zadostno zadostno
Levels: zadostno < dobro < prav dobro < odlično
Bodite pozorni na zadnjo vrstico izhoda, ki z uporabo znaka
<
pove kakšna je urejenost možnih vrednosti
elementov.
Datumski vektor je nadgradnja vektorjev numeričnih
vrednosti (plavajoče vejice). Tvorimo jih lahko na več načinov. S
funkcijami as.Date
, če nas čas/ ura ne zanima, ali
as.POSIXct
, če hočemo datumu pridružiti tudi uro:
now = as.Date(Sys.Date())
now
[1] "2024-10-06"
future = as.Date(Sys.Date() + 360)
future
[1] "2025-10-01"
duration = future - now
typeof(duration)
[1] "double"
duration
Time difference of 360 days
now = as.POSIXct(Sys.time(), )
Bodite pozorni na izpis zgornjega primera
Časovna razlika 360 dni
: razlika dveh datumov je poseben
tip datumskega vektorja, ki ustreza časovnemu intervalu. Tvorimo ga
lahko tudi sami s funkcijo as.difftime
in določamo
enote:
duration = as.difftime(1, units="weeks")
now + duration
[1] "2024-10-13 17:35:17 CEST"
Seznami (angl. lists)
Za razliko od vektorjev, seznami v R-ju lahko vsebujejo elemente, ki
imajo vrednosti različnih tipov. Zato rečemo, da so
seznami posplošitev vektorjev in jih imenujemo tudi splošni (generični)
vektorji (angl. generic vectors). Pozor:
Seznami v Python-u so pravzaprav podobni vektorjem (in ne seznamom) v
R-ju.
Sezname lahko tvorimo s funkcijo list
:
l1 = list(
1:3,
"a",
c(TRUE, FALSE, TRUE),
c(2.3, 5.9)
)
l1
[[1]]
[1] 1 2 3
[[2]]
[1] "a"
[[3]]
[1] TRUE FALSE TRUE
[[4]]
[1] 2.3 5.9
typeof(l1)
[1] "list"
length(l1)
[1] 4
Bodimo pozorni na dejstvo, ki ga kaže zadnji primer, da ima seznam
štiri elemente. Vektorji (in ne njihovi posamezni elementi) so elementi
seznama. Kot bomo videli pozneje so lahko tudi seznami elementi
seznama.
Tako kot vektorji, so lahko tudi seznami poimenovani:
oseba = list(
ime = "Aleksandra",
spol = "ž",
starost = "21"
)
oseba
$ime
[1] "Aleksandra"
$spol
[1] "ž"
$starost
[1] "21"
Sezname lahko gnezdimo, kar pomeni, da je lahko seznam element
seznama. Tukaj je primer nenavadnega seznama z enim elementom:
l2 = list(list(list(1))) # res čuden seznam
l2
[[1]]
[[1]][[1]]
[[1]][[1]][[1]]
[1] 1
Različne sezname lahko kombiniramo/ sestavljamo s funkcijo
c
ali list
:
l3 = list(list(1, 2), c(3, 4))
l3
[[1]]
[[1]][[1]]
[1] 1
[[1]][[2]]
[1] 2
[[2]]
[1] 3 4
l4 = c(list(1, 2), c(3, 4))
l4
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
[[4]]
[1] 4
V zgornjih primerih bodite pozorni na razliko med seznamoma
l3
in l4
. Pozorno ju primerjajte, poglejte
kakšna je dolžina vsakega in pojasnite zakaj pride do te razlike. Pri
katerem opazujemo gnezdenje seznamov?
Če si želimo vektor pretvoriti v seznam, uporabimo funkcijo
as.list
. Rezultat funkcije je seznam, ki ima enako število
elementov kot vektor in vsak element vektorja postane element seznama.
Poglejmo še en primer, ki ponazori razliko med funkcijama
as.list
in list
:
l5 = as.list(-2:2)
l5
[[1]]
[1] -2
[[2]]
[1] -1
[[3]]
[1] 0
[[4]]
[1] 1
[[5]]
[1] 2
l6 = list(-2:2)
l6
[[1]]
[1] -2 -1 0 1 2
Za pretvorbo seznama v vektor uporabljamo funkcijo
unlist
. Pri tem moramo biti pozorni na to, da morajo biti
vsi elementi vektorja istega tipa. Če so elementi seznama različnih
tipov, bo R med izračunom rezultata funkcije unlist
opravil
prisilno pretvorbo tipov. Poglejmo primere:
unlist(l1)
[1] "1" "2" "3" "a" "TRUE" "FALSE" "TRUE" "2.3" "5.9"
unlist(l2)
[1] 1
unlist(l3)
[1] 1 2 3 4
unlist(l4)
[1] 1 2 3 4
unlist(l5)
[1] -2 -1 0 1 2
unlist(l6)
[1] -2 -1 0 1 2
Pojasni zakaj sta rezultata l3
in l4
(ter
l5
in l6
) enaka.
Indeksiranje seznamov
Ko indeksiramo seznam s
tako, kot indeksiramo vektorje,
torej indeks postavimo v enojne oklepaje, na primer s[1]
ali s[2:3]
, je rezultat indeksiranja vedno
seznam, četudi seznam z enim elementom. Če želimo z indeksiranjem
seznama dobiti njegove posamezne elemente, moramo uporabljati dvojne
oglate oklepaje. Prvi element seznama torej dobimo z izrazom
s[[1]]
.
Poglejmo nekaj primerov:
l1[2:3] # seznam z dvema elementoma
[[1]]
[1] "a"
[[2]]
[1] TRUE FALSE TRUE
l1[-4] # seznam s tremi elementi
[[1]]
[1] 1 2 3
[[2]]
[1] "a"
[[3]]
[1] TRUE FALSE TRUE
l1[1] # seznam z enim elementom
[[1]]
[1] 1 2 3
typeof(l1[1])
[1] "list"
l1[[1]] # prvi element seznama
[1] 1 2 3
typeof(l1[[1]])
[1] "integer"
Opomba: dvojne oglate oklepaje lahko uporabljamo
tudi za dostop do posameznih elementov vektorja. Veliko
programerjev v R-ju se poslužuje dvojnih oklepajev pri naslavljanju
posameznih elementov vektorja (in seveda seznama) zato, da je koda bolj
pregledna. Z dvojnimi oklepaji namreč poudarimo, da želimo z
indeksiranjem nasloviti točno en element vektorja. Ko pa uporabljamo
indeksiranje za dostop do večjega (ali poljubnega) števila elementov,
potem moramo uporabljati enojne oklepaje.
V poimenovanih seznamih lahko posamezne elemente naslovimo na več
načinov:
oseba[[1]]
[1] "Aleksandra"
oseba[["ime"]]
[1] "Aleksandra"
oseba$ime
[1] "Aleksandra"
Prvi način z uporabo znaka $
je najbolj običajen in
pogosto uporabljen.
LS0tCnRpdGxlOiAnVXZvZCB2IFI6IE9zbm92bmkgcG9kYXRrb3ZuaSB0aXBpJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdAotLS0KCiMgU2thbGFyamkgKGFuZ2wuIF9zY2FsYXJzXykKClIgaW1hIMWhdGlyaSBvc25vdm5lIHRpcGUgc2thbGFyamV2IG96aXJvbWEgbmppaG92aWggdnJlZG5vc3RpOgoKKiAqKkxvZ2nEjW5pKiogdnJlZG5vc3RpIChhbmdsLiBfbG9naWNhbF8pIHN0YSBgVFJVRWAgaW4gYEZBTFNFYCBhbGkgbmEga3JhdGtvIGBUYCBpbiBgRmAuCgoqICoqQ2Vsb8WhdGV2aWxza2UqKiAoYW5nbC4gX2ludGVnZXJfKSB2cmVkbm9zdGkgemFwacWhZW1vIGtvdCDFoXRldmlsa28sIGtpIGppIHNsZWRpIHNpbWJvbCBgTGAsIG5wci4gYDEyMzQ1NkxgLiDEjGUgamUgY2Vsb8WhdGV2aWxza2EgdnJlZG5vc3QgcHJldmVsaWthIGFsaSBwcmVtYWpobmEgKHBvIGFic29sdXRuaSB2cmVkbm9zdGkgdmXEjWphIG9kIGAuTWFjaGluZSRpbnRlZ2VyLm1heGAsIG5hIG1vamVtIHJhxI11bmFsbmlrdSBqZSB0byAyLDE0Nyw0ODMsNjQ3KSBhbGkgc21vIGppIHBvbW90b21hIGRvZGFsaSBkZWNpbWFsa2UgKGluIHRvcmVqIG5pIHZlxI0gY2Vsb8WhdGV2aWxza2EpLCBqbyBSIGF2dG9tYXRza28gcHJldHZvcmkgdiBudW1lcmnEjW5vIHZyZWRub3N0IChnbGVqIG5hc2xlZG5qbyBhbGluZWpvKS4KCiogKipOdW1lcmnEjW5lKiogdnJlZG5vc3RpIChhbmdsLiBfZG91YmxlXykgdiBbKipwbGF2YWpvxI1pIHZlamljaSoqXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9GbG9hdGluZy1wb2ludF9hcml0aG1ldGljKSAoYW5nbC4gX2Zsb2F0aW5nIHBvaW50XykgbGFoa28gemFwacWhZW1vIHYgbmF2YWRuaSBgMTIzLjQ1NmAgYWxpIHpuYW5zdHZlbmkgbm90YWNpamkgYDEuMjM0NTZlMmAuIFRyaSBwb3NlYm5lIG51bWVyacSNbmUgdnJlZG5vc3RpIHNvIGAtSW5mYCwgbnByLiBgLTEvMGAsIGBJbmZgLCBucHIuIGAxLzBgLCBpbiBgTmFOYCwgbnByLiBgMC8wYC4KCiogKipOaXplIHpuYWtvdioqIChhbmdsLiBfc3RyaW5nXykgemFwacWhZW1vIG1lZCBlbm9qbmUgYCdgIGFsaSBkdm9qbmUgYCJgIG5hcmVrb3ZhamUuIMSMZSBuaXogem5ha292IHZzZWJ1amUgbmFyZWtvdmFqZSwgc2xlZG5qZSB6YXBpxaFlbW8gcyBwcmVkcG9ubyBgXGAsIG5wci4gYCJUbyBqZSBwcmltZXIgbml6YSB6bmFrb3YgeiBcIm5hcmVrb3ZhamlcIiJgLiBEb2zFvmluYSBuaXphIHpuYWtvdiBqZSDFoXRldmlsbyB6bmFrb3YgdiBuaXp1LiBQcmF6ZW4gbml6IHpuYWtvdiBgIiJgIGltYSBkb2zFvmlubyAwLgoKU3ByZW1lbmxqaXZraSB2IFItanUgZG9kZWxpbW8gKG5vdm8pIHNrYWxhcm5vIHZyZWRub3N0IHogb3BlcmF0b3JqZW0gYDwtYC4gR3JlIHphIHRyYWRpY2lvbmFsbmkgemFwaXMsIGl6IMSNYXNvdiBuYXN0YW5rYSBwcm9ncmFtc2tlZ2Egb2tvbGphIFIuIERhbmVzIFIgb21vZ2/EjWEgdXBvcmFibyBvYmnEjWFqbmVnYSBvcGVyYXRvcmphIGA9YCwga2kgZ2EgcG96bmFtbyBpeiBkcnVnaWggcHJvZ3JhbXNraWggamV6aWtvdi4KClBvIGRvZGVsaXR2aSwgdGlwIHNwcmVtZW5saml2a2Ugc2Ugc3ByZW1lbmkgdiB0aXAgc2thbGFybmUgdnJlZG5vc3RpLCBraSBqaSBqbyBkb2RlbGltby4gUHJpIGl6cmHEjXVuaWgsIGtpIHZrbGp1xI11amVqbyB2cmVkbm9zdGkgaW4gc3ByZW1lbmxqaXZrZSByYXpsacSNbmloIHRpcG92LCBzZSB0aXBpIGRpbmFtacSNbm8gKHNhbW9kZWpubywgdiB0ZXJtaW5vbG9naWppIFIgdHVkaSBwcmlzaWxubywgYW5nbC4gX2NvZXJjaW9uXykgaW4gcG8gcG90cmViaSBwcmlsYWdvZGlqbyB0YWtvLCBkYSBibyBpenJhxI11biBtb8W+bm8gaXpwZWxqYXRpLiBfUHJpc2lsbm8gcHJpbGFnYWphbmplIHRpcG92XyB2ZWRubyBwb3Rla2EgdiBzbWVyLCBraSBqbyBuYWthenVqZSB2cnN0bmkgcmVkIG5hxaF0ZXZhbmphIMWhdGlyaWggdGlwb3YgemdvcmFqOiBsb2dpxI1uYSB2cmVkbm9zdCBzZSBwcmV0dm9yaSB2IGNlbG/FoXRldmlsc2tvLCB0YSBuYXRvIHYgbnVtZXJpxI1ubyAocGxhdmFqb8SNbyB2ZWppY28pLCB0ZXIgbmEga29uY3UgxaFlIChzZXZlZGEgcG8gcG90cmViaSkgdiBuaXogem5ha292LgoKKipQb3pvcioqLCBwcmlsYWdhamFuamUgdGlwb3YgdsSNYXNpaCB0dWRpIChuZXByaWpldG5vKSBwcmVzZW5ldGksIG5wci4gYDEgPT0gIjEiYCAob3BlcmF0b3IgYD09YCBwcmV2ZXJpLCDEjWUgc3RhIGl6cmF6YSBuYSBsZXZpIGluIGRlc25pIHN0cmFuaSBlbmFrYSkuIEl6cmF6LCBraSBiaSBsYWhrbyBiaWwgcmV6dWx0YXQgcHJvZ3JhbXNrZSBuYXBha2UsIFIgYnJleiB0ZcW+YXYgKHByaWphdmxqYW5qYSBuYXBhayBhbGkgb3Bvem9yaWwpIG92cmVkbm90aSB2IGBUUlVFYCBpbiBuZSB2IG1vcmViaXRpIGJvbGogcHJpxI1ha292YW5vIHZyZWRub3N0IGBGQUxTRWAgKHByZXByacSNYWogc2UsIGRhIGplIHRlbXUgdGFrbywgbmF0byBwcmVtaXNsaSBpbiBwb2phc25pIHpha2FqKS4KClRpcCBwb2xqdWJuZSBzcHJlbWVubGppdmtlLCB2cmVkbm9zdGkgYWxpIGl6cmHEjXVuYSwgbGFoa28gcHJldmVyaW1vIHMgZnVua2Npam8gYHR5cGVvZmAuIFByaW1lciwga2kgcG9uYXpvcmkgc2Ftb2Rlam5vIHByZXR2b3JibyBtZWQgcmF6bGnEjW5pbWkgb3Nub3ZuaW1pIHRpcGk6CmBgYHtyfQp4ID0gMUwgKyBUUlVFCngKdHlwZW9mKHgpCgp4ID0gMUwgKyBUUlVFICsgMQp4CnR5cGVvZih4KQoKeCA9ICF4CngKdHlwZW9mKHgpCmBgYAoKWmFtZW5qYXZvIHRpcGEgdnJlZG5vc3RpIGxhaGtvIHNhbWkgc3Byb8W+aW1vIHMgZnVua2NpamFtaSBgYXMubG9naWNhbGAsIGBhcy5pbnRlZ2VyYCwgYGFzLmRvdWJsZWAgaW4gYGFzLmNoYXJhY3RlcmAuIFMgdGVtaSBmdW5rY2lqYW1pIGxhaGtvIHByZXZlcmltbyBrYWtvIFIgcHJldHZhcmphIG96LiBwcmlsYWdhamEgdGlwZSBza2FsYXJqZXYuIE1lZCB6YW5pbWl2aW1pIGtsaWNpIHRlIGZ1bmtjaWplIHNvZGlqbyBucHIuICBgYXMubG9naWNhbCgwKWAsIGBhcy5jaGFyYWN0ZXIoRkFMU0UpYCwgYGFzLmludGVnZXIoMi44KWAuIF9fUHJlZGVuIHBvc2t1c2l0ZSB1a2F6ZSwgcHJldmVyaXRlLCBhbGkgem5hdGUgcHJhdmlsbm8gbmFwb3ZlZGF0aSBuamlob3ZvIGRlbG92YW5qZS5fXwoKxaBlIGVuYSwgemVsbyBwb21lbWJuYSBza2FsYXJuYSB2cmVkbm9zdCBqZSAqKm5lem5hbmEgdnJlZG5vc3QqKiBgTkFgLiBCb2RpdGUgcG96b3JuaSBuYSBuYXNsZWRuamkgZHZlIGRlanN0dmkgKHpha2FqIGplIHRlbXUgdGFrbz8pOgpgYGB7cn0KTkEgPT0gMApOQSA9PSBOQQpgYGAKCioqQm9kaXRlIHBvem9ybmkqKjogbmV6bmFuYSB2cmVkbm9zdCBgTkFgIGplICJrdcW+bmEiLiBWIHZlxI1pbmkgcHJpbWVyb3YgamUgdnJlZG5vc3QgaXpyYXphIChpenJhxI11bmEpLCBraSB2a2xqdcSNdWplIGBOQWAsIGVuYWthIGBOQWAuIFBvem5hbW8gbGUgbmVrYWogcmVka2loIGl6amVtLCBucHIuIGBOQSAmIEZBTFNFYCwgYE5BIHwgVFJVRWAgaW4gYE5BIF4gMGAuIFpub3ZhIHByZW1pc2xpIHpha2FqIGplIHRlbXUgdGFrby4KClphIHByZXZlcmphbmplIGFsaSBqZSB2cmVkbm9zdCBzcHJlbWVubGppdmtlIGFsaSBpenJhemEgbmV6bmFuYSB1cG9yYWJpbW8gZnVua2Npam8gYGlzLm5hYC4gTmEgdm9sam8gc28gdHVkaSDFoXRpcmkgcG9kb2JuZSBmdW5rY2lqZSBzIGthdGVyaW1pIGxhaGtvIHByZXZlcmltbywgxI1lIGplIGl6cmF6IG5la2VnYSBkb2xvxI1lbmVnYSB0aXBhOiBgaXMubG9naWNhbGAsIGBpcy5pbnRlZ2VyYCwgYGlzLmRvdWJsZWAgaW4gYGlzLmNoYXJhY3RlcmAuIFJlenVsdGF0aSB2c2VoIHRlaCBmdW5rY2lqIHNvIGxvZ2nEjW5lIHZyZWRub3N0aS4KCk5haiBuYSB0ZW0gbWVzdHUgb21lbmltbyDFoWUgb3puYWtvIGBOVUxMYCwga2kgdiBSLWp1IG5ha2F6dWplIG9kc290bm9zdCB2cmVkbm9zdGkuIFphIHJhemxpa28gb2QgYE5BYCwga2kgbmFrYXp1amUgcHJpc290bm9zdCB2cmVkbm9zdGksIGtpIHBhIG5pIHpuYW5hLgpgYGB7cn0KTlVMTCA9PSBOVUxMCk5VTEwgPT0gMApOVUxMID09IE5BCmBgYAoKViB2c2VoIHRyZWggcHJpbWVyaWggamUgcmV6dWx0YXQgYGxvZ2ljYWwoMClgLCBrYXIgamUgb3puYWthIHphIHByYXplbiB2ZWt0b3IgbG9nacSNbmloIHZyZWRub3N0aS4gUG9kb2JubyBrb3QgYE5VTExgIHR1ZGkgcHJhemVuIHZla3RvciBuYWthenVqZSBvZHNvdG5vc3QgdnJlZG5vc3RpLiBaYWRuamEgZHZhIHN0YXZrYSBvbWVuamF0YSBwb2RhdGtvdm5pIHRpcCB2ZWt0b3IsIGtpIGdhIHR1ZGkgxaF0ZWplbW8gbWVkIG9zbm92bmltaSBwb2RhdGtvdm5pbWkgdGlwaSB2IFItanUuIFNwb3puYWxpIGdhIGJvbW8gdiBuYWRhbGpldmFuanUuCgotLS0KCiMgVmVrdG9yamkgKGFuZ2wuIF92ZWN0b3JzXywgX2F0b21pYyB2ZWN0b3JzXykKClZla3RvciBwcmVkc3RhdmxqYSBfdXJlamVuXyBzZXpuYW0gc2thbGFyamV2ICoqaXN0ZWdhIHRpcGEqKi4gU2thbGFyamUgaW1lbnVqZW1vICplbGVtZW50aSogYWxpICprb21wb25lbnRlKiBzZXpuYW1hLiBfVXJlamVub3N0XyBzZXpuYW1hIHBvbWVuaSwgZGEgamUgdnJzdG5pIHJlZCBlbGVtZW50b3YgcG9tZW1iZW4gaW4gbGFoa28gZWxlbWVudGUgbmFzbGF2bGphbW8gbmEgb3Nub3ZpIG5qaWhvdmUgcG96aWNpamUgdiBzZXpuYW11ICh1cmVqZW5vc3QgbmFtcmXEjSBuZSBwb21lbmksIGRhIHNvIGVsZW1lbnRpIHVyZWplbmkgcG8gbmFyYcWhxI1ham/EjWkgYWxpIHBhZGFqb8SNaSB2cmVkbm9zdGkpLiBaIGRydWdpbWkgYmVzZWRhbWksIHpuYW1vIHBvdmVkYXRpIGthdGVyaSBlbGVtZW50IHNlem5hbWEgamUgcHJ2aSwgZHJ1Z2kgYWxpIHphZG5qaS4KClZla3RvcmplIG5hamJvbGogcHJlcHJvc3RvIHVzdHZhcmphbW8gcyBrbGljZW0gZnVua2NpamUgYGNgLCBraSBzcHJlam1lIHBvbGp1Ym5vIMWhdGV2aWxvIGFyZ3VtZW50b3YgaW4gaXogbmppaCBzZXN0YXZpIHNlem5hbS4gRG9sxb5pbm8gdmVrdG9yamEsIGtpIGplIGVuYWthIMWhdGV2aWx1IG5qZWdvdmloIGVsZW1lbnRvdiwgbmFtIHZybmUgZnVua2NpamEgYGxlbmd0aGAuIFZla3RvciBkb2zFvmluZSAwIGltZW51amVtbyBwcmF6ZW4gdmVrdG9yLiDFvWUgb21lbmplbmEgZnVua2NpamEgYHR5cGVvZmAgcGEgbmFtIHZybmUgdGlwIGVsZW1lbnRvdiB2ZWt0b3JqYSAobmUgcG96YWJpbW8sIGRhIHNvIHZzaSBlbGVtZW50aSB2ZWt0b3JqYSBlbmFrZWdhIHRpcGEpLgpgYGB7cn0KdmwgPSBjKFRSVUUsIEZBTFNFLCBUUlVFLCBUUlVFKQp0eXBlb2YodmwpCgp2aSA9IGMoLTIwTCwgLTEwTCwgMEwsIDEwTCwgMjBMKQpsZW5ndGgodmkpCgp2ZCA9IGMoLTEsIDIuNSwgMykKYXMuaW50ZWdlcih2ZCkKCnZzID0gYygibmVrYWogc3RyaW5nb3YgKHVwcywgbml6b3Ygem5ha292KSIsICJ6YSBwcmltZXIiLCAicGEgxaFlIMWhdGV2aWxrYSIsICI0MiIpCnggPSBhcy5kb3VibGUodnMpCngKaXMubmEoeCkKYGBgCgpCb2RpbW8gcG9zZWJlaiBwb3pvcm5pIG5hIHphZG5qZSB0cmkgcmV6dWx0YXRlLiBQcmkgcHJldHZhcmphbmp1IG5pem92IHpuYWtvdiB2IG51bWVyacSNbmUgdnJlZG5vc3RpLCBUaXN0ZSBuaXplIHpuYWtvdiwga2kgc2UgamloIG5lIGRhIChvemlyb21hIGppaCBSIG5lIHpuYSkgcHJldHZvcml0aSB2IG51bWVyacSNbmUgdnJlZG5vc3RpLCBSIHByZXR2b3JpIHYgbmV6bmFuZSB2cmVkbm9zdGkgYE5BYC4gUHJpIHRlbSBpenBpxaFlIG9wb3pvcmlsbyAoYW5nbC4gX3dhcm5pbmdfKSBgTmV6bmFuZSB2cmVkbm9zdGkgTkEgdnBlbGphbmUgcyAocHJpc2lsbmltKSBwcmlsYWdhamFuamVtIHRpcG92YCAoYW5nbC4gX19OQXMgaW50cm9kdWNlZCBieSBjb2VyY2lvbl9fKS4KClBvZ2xlam1vIMWhZSBrYWogc2UgemdvZGksIMSNZSBwb3NrdXNpbW8gc2VzdGF2aXRpIHZla3RvciBpeiB2cmVkbm9zdGkgcmF6bGnEjW5paCB0aXBvdjoKYGBge3J9CnggPSBjKFRSVUUsIDFMKQp4CnR5cGVvZih4KQoKeCA9IGMoRkFMU0UsIDEsIDJMKQp4CnR5cGVvZih4KQoKeCA9IGMoVFJVRSwgMSwgMi41KQp4CnR5cGVvZih4KQoKeCA9IGMoRkFMU0UsIDEsIDJMLCAiMyIpCngKdHlwZW9mKHgpCmBgYAoKUHJlbWlzbGl0ZSBpbiBwb2phc25pdGUgemFrYWogc28gdGlwaSB0ZWggdmVrdG9yamV2IHRha2kga290IHNvPwoKxIxlcHJhdiB2ZWt0b3JqaSBwcmVkc3RhdmxqYWpvIGxlIGVub2RpbWVuemlvbmFsbmUgc2V6bmFtZSB2cmVkbm9zdGkgaXN0ZWdhIHRpcGEsIHogbmppbWkgbGFoa28gcHJlZHN0YXZpbW8gdHVkaSBkdm9kaW1lbnppb25hbG5lIG1hdHJpa2UgYWxpIHBvbGphICh0ZW56b3JqZSkgcG9sanVibmVnYSDFoXRldmlsYSBkaW1lbnppai4gWmF0byBkYSByYXp1bWVtbyBrYWtvLCBzcG96bmFqbW8gbmFqcHJlaiBrb25jZXB0IHZla3RvcnNraWggYXRyaWJ1dG92LgoKIyMgQXRyaWJ1dGkgdmVrdG9yamV2IGluIG1hdHJpa2UKCkF0cmlidXRpIHNvIHVyZWplbmkgcGFyaSBvYmxpa2UgKGltZSwgdnJlZG5vc3QpLCBraSB2ZWt0b3JqZSBvem5hxI1pam8geiBfbWV0YV8gcG9kYXRraS4gWmEgbmFzdGF2bGphbmplIHZyZWRub3N0aSBhdHJpYnV0b3YgdmVrdG9yamEgdXBvcmFibGphbW8gZnVua2Npam8gYGF0dHJgLCBraSBqbyBsYWhrbyB0dWRpIHVwb3JhYmltbyB6YSBwcmV2ZXJqYW5qZSB2cmVkbm9zdGkgYXRyaWJ1dGEuIEZ1bmtjaWphIGBhdHRyaWJ1dGVzYCB2cm5lIHZzZSBhdHJpYnV0ZSBwb2RhbmVnYSB2ZWt0b3JqYS4KYGBge3J9CnYgPSBjKC0yMEwsIC0xMEwsIDBMLCAxMEwsIDIwTCkKCmF0dHIodiwgInBydmlfYXRyaWJ1dCIpID0gMQphdHRyKHYsICJkcnVnaV9hdHJpYnV0IikgPSAyLjMKCnByaW50KCJQcnZpIGF0cmlidXQgdmVrdG9yamEgdiIpCmF0dHIodiwgInBydmlfYXRyaWJ1dCIpCgpwcmludCgiVnNpIGF0cmlidXRpIHZla3RvcmphIHYiKQphdHRyaWJ1dGVzKHYpCmBgYAoKUmV6dWx0YXQgZnVua2NpamUgYGF0dHJpYnV0ZXNgIHByYXZ6YXByYXYgX3Nlem5hbV8gYXRyaWJ1dG92IGluIG5qaWhvdmloIHZyZWRub3N0aS4gUi1qZXZza28gcG9kYXRrb3ZubyBzdHJ1a3R1cm8gc2V6bmFtIGJvZG8gc3Bvem5hbGkgcG96bmVqZS4gKipQb3pvcioqOiAqKlZla3RvcmppIHYgUi1qdSBzbyBwb2RhdGtvdm5hIHN0cnVrdHVyYSwga2kgamUgcG9kb2JuYSBzdHJ1a3R1cmkgc2V6bmFtIHYgUHl0aG9uLXUqKi4KCkZ1bmtjaWphIGBzdHJ1Y3R1cmVgIG9tb2dvxI1hIGRvZGFqYW5qZSBhdHJpYnV0b3Ygxb5lIG9iIHR2b3JqZW5qdSB2ZWt0b3JqYToKYGBge3J9CnYgPSBzdHJ1Y3R1cmUoCiAgYygtMjBMLCAtMTBMLCAwTCwgMTBMLCAyMEwpLAogIHBydmlfYXRyaWJ1dCA9IDEsCiAgZHJ1Z2lfYXRyaWJ1dCA9IDIuMwopCnYKYXR0cmlidXRlcyh2KQpgYGAKCkF0cmlidXRpIHNvIHphxI1hc25pLiBNbm9nZSBmdW5rY2lqZSBpbiBvcGVyYWNpamUgbmFkIHNlem5hbWkgaXpicmnFoWVqbyAocG96YWJpam8pIGF0cmlidXRlLCBwcmVpemt1c2l0ZSBucHIuIGB2WzFdYCBhbGkgYHN1bSh2KWA6CmBgYHtyfQphdHRyaWJ1dGVzKHZpWzFdKQphdHRyaWJ1dGVzKHN1bSh2aSkpCmBgYAoKVnJlZG5vc3QgaXpyYXphIGB2WzFdYCBqZSBlbmFrYSB2cmVkbm9zdGkgcHJ2ZWdhIGVsZW1lbnRhIHZla3RvcmphIGB2YC4gRnVua2NpamEgYHN1bWAgdnJuZSB2c290byB2cmVkbm9zdGkgZWxlbWVudG92IHBvZGFuZWdhIHZla3RvcmphLgoKRHZhIGF0cmlidXRhIGltYWpvIHYgUi1qdSBwb3NlYmVuIHBvbWVuOgoKKiBgbmFtZXNgIGplIHZla3RvciBuaXpvdiB6bmFrb3YsIGtpIGplIGVuYWtlIGRvbMW+aW5lIGtvdCBvcmlnaW5hbG5pIHZla3Rvci4gVnNhayBlbGVtZW50IHZla3RvcmphIGBuYW1lc2AgZG9sb8SNYSAqKmltZSBlbGVtZW50YSoqIG9yaWdpbmFsbmVnYSB2ZWt0b3JqYS4KKiBgZGltYCBqZSB2ZWt0b3IgY2Vsb8WhdGV2aWxza2loIHZyZWRub3N0aSwga2kgcHJldHZvcmkgdmVrdG9yIHYgbWF0cmlrbyBhbGkgcG9samUuCgoqKlBvaW1lbm92YW4gdmVrdG9yKiogamUgdmVrdG9yIHYga2F0ZXJlbSBpbWEgdnNhayBlbGVtZW50IHN2b2plIGltZS4gSW1lbmEgZWxlbWVudG92IHZla3RvcmphIGxhaGtvIG5hc3RhdmltbyBuYSB2c2FqIGR2YSBuYcSNaW5hOiBwcmkgdHZvcmplbmp1IHZla3RvcmphIGFsaSBwYSBuYWtuYWRubyB6YSBuYXZhZG5pIHZla3RvciB6IHVwb3JhYm8gZnVua2NpamUgYG5hbWVzYDoKYGBge3J9CngxID0gYyhwcnZpID0gMSwgZHJ1Z2kgPSAyLCB0cmV0amkgPSAzKQp4MQoKeDIgPSBjKDEsIDIsIDMpCm5hbWVzKHgyKSA9IGMoInBydmkiLCAiZHJ1Z2kiLCAidHJldGppIikKeDIKYGBgCgpGdW5rY2lqbyBgbmFtZXNgIHZybmUgaW1lbmEgZWxlbWVudG92IHYgcG9pbWVub3ZhbmVtIHNlem5hbXUuIFByZXZlcml0ZSBrYWogdnJuZSBmdW5rY2lqYSBgbmFtZXNgIHphIG5laW1lbm92YW4gc2V6bmFtLCBucHIuIGBuYW1lcyhjKDEsMiwzKSlgLiBQb2ltZW5vdmFuIHNlem5hbSBsYWhrbyBzcHJlbWVuaW1vIHYgbmVpbWVub3ZhbmVnYSAodG9yZWogdiB2ZWt0b3IgeiBlbGVtZW50aSBicmV6IGltZW4pIHMga2xpY2VtIGZ1bmtjaWplIGB1bm5hbWVgOgpgYGB7cn0KeDMgPSB1bm5hbWUoeDIpCngzCmBgYAoKRGltZW56aWplIHZla3RvcmphLCB0LmouLCB2cmVkbm9zdCBhdHJpYnV0YSBgZGltYCBkb2xvxI1pbW8gbGFoa28gcHJpIHR2b3JqZW5qdSB2ZWt0b3JqYSBhbGkgbmFrbmFkbm8geiB1cG9yYWJvIGZ1bmtjaWplIGBkaW1gOgpgYGB7cn0KeCA9IGMoMSwgMiwgMywgNCwgNSwgNikKCkExID0gbWF0cml4KHgsIG5yb3cgPSAyLCBuY29sID0gMykKQTEKZGltKEExKQoKQTIgPSB4CmRpbShBMikgPSBjKDMsIDIpCkEyCmBgYAoKCiMjIFR2b3JqZW5qZSB2ZWt0b3JqZXYKCk9nbGVqbW8gc2kgbmVrYWogbmHEjWlub3YgemEgdHZvcmplbmplIHZla3RvcmpldjoKCiogYGk6amAgamUgY2Vsb8WhdGV2aWxza2kgdmVrdG9yIHogZWxlbWVudGkgYGksIGkrMSwgLi4uLCBqYCwgxI1lIGplIGBqID49IGlgIG96aXJvbWEgZWxlbWVudGkgYGksIGktMSwgXGxkb3RzLCBqYCwgxI1lIGplIGBqIDwgaWAuCgoqIGBzZXEoZnJvbSA9IGksIHRvID0gaiwgYnkgPSBzKWAgamUgdmVrdG9yIG51bWVyacSNbmloIHZyZWRub3N0aSwga2plciBqZSBwcnZpIGVsZW1lbnQgYGlgLCAgcmF6bGlrYSBtZWQgZHZlbWEgemFwb3JlZG5pbWEgZWxlbWVudG9tYSBqZSBgc2AsIHphZG5qaSBlbGVtZW50IHBhIG5lIHNtZSBwcmVzZcSNaSB2cmVkbm9zdGkgYGpgLiBQcmkgcmF6bGnEjWljaSBgc2VxKGZyb20gPSBpLCB0byA9IGosIGxlbmd0aC5vdXQgPSBuKWAgbmFtZXN0byByYXpsaWtlIG1lZCB6YXBvcmVkbmltYSBlbGVtZW50b21hIGRvbG/EjWltbyDFoXRldmlsbyBlbGVtZW50b3YsIGtpIHNlIGVrdmlkaXN0YW7EjW5vIChlbmFrb21lcm5vKSByYXpwb3JlZGlqbyBuYSBpbnRlcnZhbHUgW2Bmcm9tYCwgYHRvYF0uCgoqIGByZXAodiwgdGltZXMgPSBuKWAgamUgdmVrdG9yIHNlc3RhdmxqZW4gdGFrbywgZGEgZWxlbWVudGUgYHZgIHBvbm92aW1vIGBuYC1rcmF0LiDEjGUgamUgYHZgIHNrYWxhciwgamUgZG9sxb5pbmEgZG9ibGplbmVnYSB2ZWt0b3JqYSBgbmAsIMSNZSBwYSBqZSBgdmAgdmVrdG9yLCBqZSBkb2zFvmluYSBkb2JsamVuZWdhIHZla3RvcmphIGBuICogbGVuZ3RoKHYpYC4gUHJpIHJhemxpxI1pY2kgYHJlcCh2LCBlYWNoID0gbilgIGRvbG/EjWltbyBrb2xpa29rcmF0IHBvbm92aW1vIHZzYWsgcG9zYW1lemVuIGVsZW1lbnQgYHZgLgoKKiBgc2FtcGxlKHYsIG4pYCBqZSB2ZWt0b3IgZG9sxb5pbmUgYG5gIChpbWVudWplbW8gZ2EgdnpvcmVjKSwga2F0ZXJlZ2EgZWxlbWVudGkgc28gbmFrbGp1xI1ubyBpemJyYW5pIGVsZW1lbnRpIHZla3RvcmphIGB2YC4gUG9zYW1lemVuIGVsZW1lbnQgaXogYHZgIHNlIGxhaGtvIHYgdnpvcmN1IHBvamF2aSBsZSBlbmtyYXQuIFBvc2xlZGljYSB0ZWdhIGplLCBkYSBqZSBwcmVkcG9nb2ogemEgdXNwZcWhbm8gdnpvcsSNZW5qZSBgbGVuZ3RoKHYpID49IG5gLiBSYXpsacSNaWNhIGBzYW1wbGUodiwgbiwgcmVwbGFjZT1UKWAgb3ByYXZpIHZ6b3LEjWVuamUgcyBwb25hdmxqYW5qZW0sIGtqZXIgamUgcG9zYW1lemVuIGVsZW1lbnQgYHZgIHNlIGxhaGtvIHYgdnpvcmN1IHBvamF2aSB0dWRpIHZlxI1rcmF0LgoKUG9pZ3JhaiBzZSB6IG5hc2xlZG5qaW1pIHByaW1lcmk6CmBgYHtyfQotMToxMwoxMzotMQoxMzoxMwoKc2VxKGZyb20gPSAwLjUsIHRvID0gLTAuNSwgYnkgPSAtMC4xKQpzZXEoZnJvbSA9IC0wLjUsIHRvID0gMC41LCBsZW5ndGgub3V0ID0gNCkKCnJlcCgtMToxLCB0aW1lcyA9IDMpCnJlcCgtMToxLCBlYWNoID0gMykKCnNhbXBsZSgtMTA6MTAsIDEyKQpzYW1wbGUoLTEwOjEwLCAxMikKIyBzYW1wbGUoLTEwOjEwLCAyNCk6IHBvamFzbmkgbmFwYWtvLCBraSBqbyBqYXZpIHRhIGtsaWMgZnVua2NpamUgc2FtcGxlCgpzYW1wbGUoMTo2LCAxMCwgcmVwbGFjZT1UKQpgYGAKIyMgSW5kZWtzaXJhbmplIHZla3RvcmpldgoKSW5kZWtzIGVsZW1lbnRhIHYgdmVrdG9yanUgYHZgIHVzdHJlemEgbmplZ292aSBwb3ppY2lqaSB2IHVyZWplbmVtIHNlem5hbXUuIFRha28gamUgaW5kZWtzIHBydmVnYSBlbGVtZW50YSB2ZWt0b3JqYSAxLCBkcnVnZWdhIDIsIGluIHRha28gbmFwcmVqLCB2c2UgZG8gaW5kZWtzYSB6YWRuamVnYSBlbGVtZW50YS4gVGEgamUgZW5hayBkb2zFvmluaSB2ZWt0b3JqYSBgbGVuZ3RoKHYpYC4gKipQb3pvcioqOiB0byBqZSwgcG9sZWcgcG9pbWVub3ZhbmphIHZla3RvcmpldiwgxaFlIGVuYSBwb21lbWJuYSByYXpsaWthIG1lZCBSLWplbSBpbiBwcm9ncmFtc2tpbSBqZXppa29tIFB5dGhvbiwga2kgbmFzIHBvZ29zdG8gem1lZGUuIFYgUHl0aG9udSBqZSBpbmRla3MgcHJ2ZWdhIGVsZW1lbnRhIHNlem5hbWEgMCwgZHJ1Z2VnYSAxIGluIHphZG5qZWdhIGBsZW4odikgLSAxYC4KClBvc2FtZXplbiBlbGVtZW50IHZla3RvcmphIGB2YCBuYXNsb3ZpbW8geiBpenJhem9tIGB2W2ldYCB6YSBwb2xqdWJlbiBlbGVtZW50IGBpYCBjZWxvxaF0ZXZpbHNrZWdhIHZla3RvcmphIGAxOmxlbmd0aCh4KWAuIFRha28gbmFzbG92bGplbmVtdSBlbGVtZW50dSBsYWhrbyBkb2RlbGltbyBub3ZvIHZyZWRub3N0IHogYHZbaV0gPSBub3ZhX3ZyZWRub3N0YCBhbGkgcGEgZ2EgdXBvcmFiaW1vIHYgcG9sanVibmVtIGl6cmF6dSwgbnByLiBgdltpXSArIDJgLiBUYWtvIGluZGVrc2lyYW5qZSBpbiBuYXNsYXZsamFuamUgcG9zYW1lem5lZ2EgZWxlbWVudGEgdmVrdG9yamEgcG96bmEgdHVkaSB2ZcSNaW5hIGRydWdpaCBwcm9ncmFtc2tpaCBqZXppa292LiBaYSByYXpsaWtvIG9kIHNsZWRuamloLCBSIHBvbnVqYSDFoWUgdmVsaWtvIGRydWdpaCBtb8W+bm9zdGkgaW5kZWtzaXJhbmphLCBraSBqaWggYm9tbyBzcG96bmFsaSB2IG5hZGFsamV2YW5qdS4gRmxla3NpYmlsbm9zdCBpbmRla3NpcmFuamEgdmVrdG9yamV2IGplIGtsanXEjW5lZ2EgcG9tZW5hIHByaSB1cG9yYWJpIFItamEgemEgcG9kYXRrb3ZubyBhbmFsaXpvLgoKUG9nbGVqbW8gKirFoXRpcmkgbmHEjWluZSBuYXNsYXZsamFuamEqKiBwb2xqdWJuZWdhIMWhdGV2aWxhIGVsZW1lbnRvdiB2ZWt0b3JqYSBgdmAuIFZzaSBzbyBvYmxpa2UgYHZbdmldYCwga2plciBqZSBgdmAgdmVrdG9yIGthdGVyZWdhIGVsZW1lbnRlIGluZGVrc2lyYW1vIGluIGB2aWAgdmVrdG9yIGluZGVrc292LgoKKiAqKkVsZW1lbnRpIGB2aWAgc28gcG96aXRpdm5hIGNlbGEgxaF0ZXZpbGEqKjogcmV6dWx0YXQgaW5kZWtzaXJhbmphIGplIHZla3RvciBlbGVtZW50b3YgdmVrdG9yamEgYHZgLCBrYXRlcmloIGluZGVrc2kgdiBgdmAgc28gZWxlbWVudGkgdmVrdG9yamEgYHZpYC4gRWxlbWVudCBgaWAgaW5kZWtzYSBzZSBsYWhrbyB0dWRpIHZlxI1rcmF0IHBvbm92aSB2IHZla3Rvcmp1IGluZGVrc292IGB2aWAuIFRvIHBvdnpyb8SNaSwgZGEgc2UgdHVkaSBlbGVtZW50IGB2W2ldYCBwb2phdmkgdmXEjWtyYXQgdiByZXp1bHRhdHUgaW5kZWtzaXJhbmphLgoKKiAqKkVsZW1lbnRpIGB2aWAgc28gbmVnYXRpdm5hIGNlbGEgxaF0ZXZpbGEqKjogcmV6dWx0YXQgaW5kZWtzaXJhbmphIGplIHZla3RvciBlbGVtZW50b3YgYHZgLCBrYXRlcmloIGluZGVrc2kgKipuaXNvKiogZWxlbWVudGkgdmVrdG9yamEgYWJzb2x1dG5paCB2cmVkbm9zdGkgZWxlbWVudG92IGB2aWAuIFogZHJ1Z2ltaSBiZXNlZGFtaSBlbGVtZW50IGAtaWAgdiB2ZWt0b3JqdSBpbmRla3NvdiBgdmlgIHBvdnpyb8SNaSwgZGEgZWxlbWVudCBgdltpXWAgKipuaSoqIGVsZW1lbnQgcmV6dWx0YXRhIGluZGVrc2lyYW5qYS48YnI+PGJyPioqUG96b3IqKjogUG96aXRpdm5paCBpbiBuZWdhdGl2bmloIGNlbG/FoXRldmlsxI1uaWggdnJlZG5vc3RpIGluZGVrc292IHYgdmVrdG9yanUgYHZpYCBuZSBzbWVtbyBrb21iaW5pcmF0aS4KCiogKipFbGVtZW50aSBgdmlgIHNvIGxvZ2nEjW5lIHZyZWRub3N0aSoqOiByZXp1bHRhdCBpbmRla3NpcmFuamEgamUgdmVrdG9yIGVsZW1lbnRvdiBgdmAgemEga2F0ZXJlIGltYWpvIGVuYWtvLWxlxb5lxI1pIGVsZW1lbnRpIGB2aWAgdnJlZG5vc3QgYFRSVUVgLiBPYmnEjWFqbm8gc3RhIHZla3RvcmphIGB2YCBpbiBgdmlgIGVuYWtpaCBkb2zFvmluLiDEjGUgamUgdmVrdG9yIGB2aWAgZGFsasWhaSBvZCB2ZWt0b3JqYSBgdmAsIHNlIHByaSBpbmRla3NpcmFuanUgdXBvxaF0ZXZhIGxlIHBydmloIGBsZW5ndGgodilgIG5qZWdvdmloIGVsZW1lbnRvdi4gxIxlIGplIHZla3RvciBgdmlgIGtyYWrFoWkgb2QgdmVrdG9yamEgYHZgLCBzZSBuamVnb3ZpIGVsZW1lbnRpIF9yZWNpa2xpcmFqb18sIGtvdCBibyBvcGlzYW5vIHYgdXN0cmVuemVtIHJhemRlbGt1IHNwb2Rhai4KCiogKipFbGVtZW50aSBgdmlgIHNvIG5pemkgem5ha292Kio6IHJlenVsdGF0IGluZGVrc2lyYW5qYSBqZSB2ZWt0b3IgZWxlbWVudG92ICoqaW1lbm92YW5lZ2EqKiB2ZWt0b3JqYSBgdmAsIGthdGVyaWggaW1lbmEgc28gdiBzZXpuYW11IGB2aWAuIMSMZSBzZSBpbWUgYGlgIHYgdmVrdG9yanUgYHZpYCBwb2phdmkgdmXEjWtyYXQsIHNlIHR1ZGkgdiByZXp1bHRhdHUgaW5kZWtzaXJhbmphIGVsZW1lbnQgYHZbaV1gIHBvamF2aSB2ZcSNa3JhdC4KCioqUG96b3IqKjogbmV6bmFuYSB2cmVkbm9zdCBgTkFgIHYgdmVrdG9yanUgaW5kZWtzb3YgYHZpYCB2ZWRubyB2cm5lIGVsZW1lbnQgYE5BYC4KCkR2YSBwb3NlYm5hIHByaW1lcmEgaW5kZWtzaXJhbmphIHN0YSBgdltdYCwga2F0ZXJlZ2EgcmV6dWx0YXQgamUgZW5hayBgdmAgdGVyIGB2WzBdYCwga2F0ZXJlZ2EgcmV6dWx0YXQgamUgcHJhemVuIHZla3RvciAoc2xlZG5qZSBwb2dvc3RvIHptZWRlIHByb2dyYW1lcmplIHYgUHl0aG9uLXUpLiAqKk96bmFrYSB6YSBwcmF6ZW4gdmVrdG9yKiogamUgYGxvZ2ljYWwoMClgLCBgaW50ZWdlcigwKWAsIGBudW1lcmljKDApYCBhbGkgYGNoYXJjdGVyKDApYCwgdiBvZHZpc25vc3RpIG9kIHRpcGEgdnJlZG5vc3RpIGVsZW1lbnRvdiB2ZWt0b3JqYS4KCk5la2FqIHByaW1lcm92LCBramVyIGplIGB2aWAgdmVrdG9yIGNlbG/FoXRldmlsc2tpaCB2cmVkbm9zdGk6CmBgYHtyfQp2ID0gYygxLjEsIC0yLjIsIDMuMywgLTQuNCkKCnZbMzoxXSAjIG9iacSNYWpubwp2W2MoMSwgMywgMSldICMgcG9ub3ZpdGV2IGlzdGVnYSBlbGVtZW50YQp2W29yZGVyKHYpXSAjIHVyZWplbmUgdnJlZG5vc3RpIGVsZW1lbnRvdgoKdltvcmRlcih2KV0gPSBjKDEuMSwgMi4yLCAzLjMsIDQuNCkgIyBpbmRla3NpcmFuZW0gdmVrdG9yanUgbGFoa28gZG9kZWxpbW8gdnJlZG5vc3RpICghKQoKdgp2W2MoLTEsIC0zKV0gIyBuZWdhdGl2bmkgaW5kZWtzaQojIHZbLTE6MV0gIyBrb21iaW5pcmFuamUgbmVnYXRpdm5paCBpbiBwb3ppdGl2bmloIGluZGVrc292IG5pIGRvdm9samVubwpgYGAKCkluIMWhZSBuZWthaiBwcmltZXJvdgpgYGB7cn0KdiA9IGMoNC4yLCAzLjMsIDUuNCwgMi4xKQp2Cgp2W2MoVFJVRSwgVFJVRSwgRkFMU0UsIEZBTFNFKV0gIyBsb2dpxI1uZSB2cmVkbm9zdGkKCnZbdiA+IDNdICMgaW5kZWtzaXJhbmplIHogbG9nacSNbmltIHBvZ29qZW0KdiA+IDMgIyBrYXIgamUgcHJhdnphcHJhdiBpbmRla3NpcmFuamUgeiBsb2dpxI1uaW1pIHZyZWRub3N0bWkKCnZbdiA+IDNdID0gMyAjIGRvZGVsamV2YW5qZSAoISkKdgp2ID0gdlt2ICE9IDNdICMgaXpiaXJhIGVsZW1lbnRvdiwga2kgaXpwb2xuanVqZWpvIHBvZ29qCnYKYGBgCgpNYXRyaWtlIG5hamJvbGogcG9nb3N0byBpbmRla3NpcmFtbyB6IGR2ZW1hIHZla3RvcmplbWE6CmBgYHtyfQpNID0gMToyMApkaW0oTSkgPSBjKDQsIDUpCmNvbG5hbWVzKE0pID0gdG91cHBlcihsZXR0ZXJzWzE6bmNvbChNKV0pCk0KCk1bMToyLCBdICMgaXpiaXJhIHZyc3RpYywgdnNpIHN0b2xwY2kKCk1bMSwgXSAjIGVuYSB2cnN0aWNhLCBSIGpvIHNhbW9kZWpubyBzcHJlbWVuaSB2IHZla3RvcgpNWzEsICwgZHJvcCA9IEZBTFNFXSAjIGVuby12cnN0acSNbmEgbWF0cmlrYSwgUi1qdSBwcmVwcmXEjWltbyBzYW1vZGVqbm8gcHJldHZvcmJvIHYgdmVrdG9yCgpNWywgYygyLDEpXSAjIGl6YmlyYSBzdG9scGNldiwgdnNlIHZyc3RpY2UgLSBib2RpdGUgcG96b3JuaSBuYSB2cnN0bmkgcmVkCgpNWzEsIDFdICMgaXpiaXJhIGVuZWdhIGVsZW1lbnRhIG1hdHJpa2UsIHJlenVsdGF0IGplIHNrYWxhcgpNWzEsIDEsIGRyb3AgPSBGQUxTRV0gIyByZXp1bHRhdCBqZSBtYXRyaWthIGRpbWVuemlqIDF4MQpgYGAKCkxhaGtvIHBhIGppaCBpbmRla3NpcmFtbyB0dWRpIHogbmF2YWRuaW0gdmVrdG9yamVtIGFsaSBtYXRyaWtvLCB2IG9iZWggcHJpbWVyaWggamUgcmV6dWx0YXQgdmVrdG9yOgpgYGB7cn0KTQpNW2MoNCwgMTUpXQpNW2MoLTQsIC0xNSldCgpNSSA9IG1hdHJpeCgKICBjKAogICAgMSwgMSwKICAgIDMsIDEsCiAgICAyLCA1CiAgKSwKICBuY29sID0gMiwgYnlyb3cgPSBUUlVFCikKZGltKE1JKSA9IGMoMywgMikKTVtNSV0KYGBgCgoKIyMgUmHEjXVuYW5qZSB6IHZla3RvcmppIGluIHJlY2lrbGlyYW5qZSBlbGVtZW50b3YgdmVrdG9yamEKClZla3RvcmppIHNvIHByYXZ6YXByYXYgb3Nub3ZuaSB0aXAgcG9kYXRrb3YgdiBSLWp1LCB0YWtvIG9zbm92bmksIGRhICoqc2thbGFyamUgb2JyYXZuYXZhbW8ga290IHBvc2VibmEgdnJzdGEgdmVrdG9yamV2IHogZW5pbSBlbGVtZW50b20qKi4gWmF0byBuYWQgdmVrdG9yamkgbGFoa28gaXp2YWphbW8gb3BlcmFjaWplIHogb3Nub3ZuaW1pIGFyaXRtZXRpxI1uaW1pIG9wZXJhdG9yamkgaW4gbWF0ZW1hdGnEjW5pbWkgZnVua2NpamFtaS4gViBuYWRhbGpldmFuanUgc2kgYm9tbyBvZ2xlZGFsaSBwb3NlYm5vc3RpIHVwb3JhYmUgb3BlcmF0b3JqZXYgaW4gZnVua2NpaiBuYWQgdmVrdG9yamkuCgojIyMgT3Nub3ZuaSBvcGVyYXRvcmppCgpaYSBpenZhamFuamUgb3Nub3ZuaWggYXJpdG1ldGnEjW5paCBvcGVyYWNpaiBzbyB2IFItanUgbmEgdm9sam8gKiphcml0bWV0acSNbmkgb3BlcmF0b3JqaSoqIHBvcGlzYW5pIHYgc3BvZG5qaSB0YWJlbGkuIFYgdnNlaCBwcmltZXJpaCBzZSBvem5ha2UgdmVrdG9yamV2IHphxI1uZWpvIHogYHZgLCBvem5ha2Ugc2thbGFyamV2IHMgYHNgLCBvem5ha2UgbWF0cmlrIHogYG1gIGluIG96bmFrZSBsb2dpxI1uaWggdnJlZG5vc3RpIHogYGxgLgoKfCBPcGVyYXRvciAob3puYWthKSB8IFBvbWVuIChpbWUpIHwgUHJpbWVyKGkpIHVwb3JhYmUgfAp8Oi0tLXw6LS0tfDotLS18CnwgYCtgIHwgc2XFoXRldmFuamUgfCBgczEgKyBzMmAsIGB2MSArIHYyYCwgYHYgKyBzYCB8CnwgYC1gIHwgb2TFoXRldmFuamUgfCBgdjEgLSB2MmAsIGB2IC0gc2AgfAp8IGAqYCB8IG1ub8W+ZW5qZSB8IGB2MSAqIHYyYCwgYHYgKiBzYCB8CnwgYC9gIHwgZGVsamVuamUgfCBgdjEgKiB2MmAsIGB2IC8gc2AgfAp8IGAlLyVgIHwgY2Vsb8WhdGV2aWxza28gZGVsamVuamUgfCBgdjEgJS8lIHYyYCwgYHYxICUvJSBzYCB8CnwgYCUlYCB8IG9zdGFuZWsgcHJpIGNlbG/FoXRldmlsc2tlbSBkZWxqZW5qdSB8IGB2MSAlJSB2MmAsIGB2MSAlJSBzYCwgYHMgJSUgdjFgIHwKfCBgXmAgfCBwb3RlbmNhIHwgYHYxIF4gdjJgLCBgdiBeIHMgYCwgYHMgXiB2YHwKfCBgJSolYCB8IG1hdHJpxI1ubyBtbm/FvmVuamUgfCBgbTEgJSolIG0yYCB8CgpLb3QgdmlkaW1vIGl6IHByaW1lcm92IHYgemdvcm5qaSB0YWJlbGksIG9wZXJhbmRhIHBvZGFuZWdhIG9wZXJhdG9yamEgc3RhIGxhaGtvIHZla3RvcmphLCBza2FsYXJqYSBhbGkgcGEga29tYmluYWNpamEgdmVrdG9yamEgaW4gc2thbGFyamEuIFByZWRlbiBwb2phc25pbW8gcHJhdmlsYSB6YSBpenZhamFuamUgb3BlcmF0b3JqZXYgbmFkIHZla3RvcmppLCBzaSBib21vIG9nbGVkYWxpIMWhZSB0YWJlbG8gKipvcGVyYXRvcmpldiBwcmltZXJqYXZlKiogKHBydmloIMWhZXN0IHZyc3RpYykgaW4gICoqbG9nacSNbmloIG9wZXJhdG9yamV2KiogKHphZG5qaWggcGV0IHZyc3RpYykuCgp8IE9wZXJhdG9yIChvem5ha2EpIHwgUG9tZW4gKGltZSkgfCBQcmltZXIoaSkgdXBvcmFiZSB8Cnw6LS0tfDotLS18Oi0tLXwKfCBgPT1gIHwgZW5ha2kgdnJlZG5vc3RpIHwgYHMxID09IHMyYCwgYHYxID09IHNgLCBgdjEgPT0gdjJgIHwKfCBgIT1gIHwgcmF6bGnEjW5pIHZyZWRub3N0aSB8IGBzMSAhPSBzMmAsIGB2MSAhPSBzYCwgYHYxICE9IHYyYCB8CnwgYDxgIHwgdnJlZG5vc3QgbWFuasWhYSB8IGBzMSA8IHMyYCwgYHYxIDwgc2AsIGB2MSA8IHYyYCB8CnwgYDw9YCB8IHZyZWRub3N0IG1hbmrFoWEgYWxpIGVuYWthIHwgYHMxIDw9IHMyYCwgYHYxIDw9IHNgLCBgdjEgPD0gdjJgIHwKfCBgPmAgfCB2cmVkbm9zdCB2ZcSNamEgfCBgczEgPiBzMmAsIGB2MSA+IHNgLCBgdjEgPiB2MmAgfAp8IGA+PWAgfCB2cmVkbm9zdCB2ZcSNamEgYWxpIGVuYWthIHwgYHMxID49IHMyYCwgYHYxID49IHNgLCBgdjEgPj0gdjJgIHwKfCBgJmAgfCBrb25qdW5rY2lqYSwgbG9nacSNbmkgX2luXyB8IGBzMSAmIHMyYCwgYHYxICYgdjJgLCBgdiAmIHNgIHwKfCBgfGAgfCBkaXNqdW5rY2lqYSwgbG9nacSNbmkgX2FsaV8gfCBgczEgfCBzMmAsIGB2MSB8IHYyYCwgYHYgfCBzYCB8CnwgYCFgIHwgbmVnYWNpamEsIGxvZ2nEjW5pIF9uZV8gfCBgISBzYCwgYCEgdmAgfAp8IGAmJmAgfCBrb25qdW5rY2lqYSBkdmVoIGxvZ2nEjW5paCB2cmVkbm9zdGkgfCBgbDEgJiYgbDJgIHwKfCBgfHxgIHwgZGlzanVua2NpamEgZHZlaCBsb2dpxI1uaWggdnJlZG5vc3RpIHwgYGwxIHx8IGwyYCB8CgpaYWRuamEgZHZhIG9wZXJhdG9yamEgKGAmJmAgaW4gYHx8YCkgc3RhIHBvc2VibmEsIGtlciBvYmEgb2JyYXZuYXZhdGEgc3ZvamEgb3BlcmFuZGEga290IHNrYWxhcm5pIGxvZ2nEjW5pIHZyZWRub3N0aSBpbiBqZSB0YWtvIHJlenVsdGF0IG9wZXJhY2lqZSBza2FsYXJuYSBsb2dpxI1uYSB2cmVkbm9zdCBgVFJVRWAgYWxpIGBGQUxTRWAuCgpLb3Jpc3RlbiBvcGVyYXRvciwga2kgbmUgc29kaSB2IHpnb3JhaiBvcGlzYW5lIGthdGVnb3JpamUsIGplIGAlaW4lYCwga2kgZ2EgdXBvcmFibGphbW8gdiBpenJhemloIG9ibGlrZSBgcyAlaW4lIHZgIHphdG8sIGRhIHByZXZlcmltbywgxI1lIGplIHNrYWxhciBgc2AgZWxlbWVudCB2ZWt0b3JqYSBgdmAuIEl6cmF6IHZybmUgYFRSVUVgIHYgcHJpbWVydSwga28gamUgYHNgIGVsZW1lbnQgdmVrdG9yamEgYHZgLiBTaWNlciBwYSB2cm5lIGBGQUxTRWAuCgojIyMgUmVjaWtsaXJhbmplIGVsZW1lbnRvdiB2ZWt0b3JqYQoKS29tYmluaXJhbmplIHJhemxpxI1uaWggdGlwb3Ygb3BlcmFuZG92ICh0LmouLCB2ZWt0b3JqZXYgaW4gc2thbGFyamV2IGFsaSBwYSB2ZWt0b3JqZXYgcmF6bGnEjW5paCBkb2zFvmluKSBpbWEgc3ZvamUgb21laml0dmUsIGtpIGppaCBtb3JhbW8gdXBvxaF0ZXZhdGkgcHJpIHBpc2FuanUgcHJhdmlsbmloIGl6cmF6b3YgdiBSLWp1LgoKUHJpIGl6dmFqYW5qdSBvcGVyYWNpamUgYHYxIG9wIHYyYCwgbW9yYXRhIHZla3RvcmphIGB2MWAgaW4gYHYyYCwga2kgbmFzdG9wYXRhIGtvdCBvcGVyYW5kYSAocHJpbWVyamFsbmVnYSwgbG9nacSNbmVnYSBhbGkgYXJpdG1ldGnEjW5lZ2EpIG9wZXJhdG9yamEgYG9wYCwgaXpwb2xuamV2YXRpIGVuZWdhIGl6bWVkIG5hc2xlZG5qaWggZHZlaCBwb2dvamV2LgoKICAxLiBWZWt0b3JqYSBzdGEgZW5ha2loIGRpbWVuemlqLCB0LmouLCB2ZWxqYSBgZGltKHYxKSA9PSBkaW0odjIpYC4gViBwcmltZXJ1IG5hdmFkbmloIHZla3Rvcmpldiwga2kgbmlzbyB2ZcSNZGltZW56aW9uYWxuaSAobWF0cmlrZSBhbGkgcG9samEgb3ppcm9tYSB0ZW56b3JqaSksIHRvIHBvbWVuaSwgZGEgc3RhIGVuYWtpaCBkb2zFvmluIGBsZW5ndGgodjEpID09IGxlbmd0aCh2MilgLiDEjGUgc3RhIG1hdHJpa2ksIHRvIHBvbWVuaSwgZGEgbW9yYXRhIGJpdGkgZW5ha2loIGRpbWVuemlqLiBJemplbWEgcHJpIG1hdHJpa2FoIGplIG1hdHJpxI1ubyBtbm/FvmVuamUsIHByaSBrYXRlcmVtIG1vcmFtbyB1cG/FoXRldmF0aSBvYmnEjWFqbm8gb21laml0ZXYgZGEgbW9yYSBpbWV0aSBwcnZhIG1hdHJpa2EgdG9saWtvIHN0b2xwY2V2LCBrb3QgaW1hIGRydWdhIG1hdHJpa2EgdnJzdGljLjxicj48YnI+xIxlIGplIHRhIHBvZ29qIGl6cG9sbmplbiwgamUgcmV6dWxhdCBgdmAgaXp2YWphbmphIG9wZXJhY2lqZSBlbmFraWggZGltZW56aWoga290IG9wZXJhbmRhLiBQcnZpIGVsZW1lbnQgZG9iaW1vIHogdXBvcmFibyBvcGVyYW5kYSBuYSBwcnZpaCBlbGVtZW50aWggb3BlcmFuZG92LCB0LmouLCBwcnZpIGVsZW1lbnQgcmV6dWx0YXRhIGplIGB2WzFdID0gdjFbMV0gb3AgdjJbMV1gLjxicj48YnI+VGEgcG9nb2ogaXpwb2xuanVqZSB0dWRpIG9iacSNYWpuYSBvcGVyYWNpamEgbWVkIGR2ZW1hIHNrYWxhcmplbWEgb2JsaWtlIGBzMSBvcCBzMmAsIGtqZXIgc3RhIG/EjWl0bm8gb2JhIG9wZXJhbmRhIGRpbWVuemlqZSBvemlyb21hIGRvbMW+aW5lIDEuCgogIDEuIERvbMW+aW5hIGRhbGrFoWVnYSB2ZWt0b3JqYSAob3puYcSNaW1vIGdhIHogYHYxYCkgamUgdmXEjWtyYXRuaWsgZG9sxb5pbmUga3JhasWhZWdhIGB2MmAsIHQuai4sIHZlbGphIGBsZW5ndGgodjEpID09IGxlbmd0aCh2MikgKiBrYCwga2plciBqZSBga2AgbmFyYXZubyDFoXRldmlsbyB2ZcSNamUgb2QgMS4gViB0ZW0gcHJpbWVydSBSIHByZWQgaXp2YWphbmplbSBvcGVyYWNpamUgcHJlZGVsYSBrcmFqxaFpIHZla3RvciB2IGRhbMWhZWdhIHRha28sIGRhIGVsZW1lbnRlIGtyYWrFoWVnYSB2ZWt0b3JqYSAqcmVjaWtsaXJhKiBga2Ata3JhdC48YnI+PGJyPlBvc2ViZW4gcHJpbWVyIHRlZ2EgcG9nb2phIGplIG9wZXJhY2lqYSB2ZWt0b3JqYSBzIHNrYWxhcmplbSAoaW4gc2thbGFyamEgeiB2ZWt0b3JqZW0pLCBrYWp0aSwga290IHNtbyBwb3ZlZGFsaSDFvmUgbmEgemHEjWV0a3UgdGVnYSByYXpkZWxrYSwgamUgc2thbGFyIHByYXZ6YXByYXYgdmVrdG9yIGRvbMW+aW5lIDEuIFYgdGVtIHByaW1lcnUgcG9kYW5vIHNrYWxhcm5vIHZyZWRub3N0ICpyZWNpa2xpcmFtbyogdG9saWtva3JhdCwga290IGplIGRvbMW+aW5hIGRhbGrFoWVnYSBvcGVyYW5kYSwgdG9yZWogdmVrdG9yamEuCiAgClphdG8sIGRhIGJvbGplIHJhenVtZcWhIHJlY2lrbGlyYW5qZSwgc2UgcG9pZ3JhaiB6IG5hc2xlZG5qaW1pIHByaW1lcmk6CmBgYHtyfQp2ID0gLTQ6NAoKdiArIFRSVUUgIyBwb2phc25pIHJlenVsdGF0LCBuZSBwb3phYmkgbmEgcHJpc2lsbm8gcHJpbGFnYWphbmplIHRpcG92Cgp2ID4gMCAjIHByZW1pc2xpIGtha28gUiByZWNpa2xpcmEgdnJlZG5vc3RpIHYgdGVtIGl6cmHEjXVudSwKdiA8IDE6LTEgIyBrYWtvIHYgdGVtLAp2IDwgMTotMiAjIGluIHpha2FqIHR1a2FqIGRvYmltbyBvcG96b3JpbG8KCnYgKiAoLTE6MSkKdiAvICgtMToxKSAjIHBvamFzbmkgdGEgcmV6dWx0YXQKYGBgCiAgCgojIyBPc25vdm5lIGZ1bmtjaWplIG5hZCBza2FsYXJqaSBpbiB2ZWt0b3JqaQoKViBSLWp1IHBvZ29zdG8gdXBvcmFibGphbW8gZHZlIG9ibGlraSBmdW5rY2lqLCBraSBrb3QgYXJndW1lbnQgc3ByZWptZWpvIHZla3Rvci4KCiAgMS4gT2JpxI1ham5lIG1hdGVtYXRpxI1uZSBmdW5rY2lqZSBvYmxpa2UgJGY6IFxtYXRoYmJ7Un0gXHRvIFxtYXRoYmJ7Un0kLCBrb3Qgc28gbnByLiBgc2luYCwgYGNvc2AsIGBsb2dgLCBgbG9nMTBgIGFsaSBgZXhwYC4gS28gdGVtIGZ1bmtjaWphbSBwb2RhbW8gemEgYXJndW1lbnQgdmVrdG9yIGB2YCwgamUgcmV6dWx0YXQgdmVrdG9yIHZyZWRub3N0aSBmdW5rY2lqZSBuYSBwb3NhbWV6bmloIGVsZW1lbnRpaCB2ZWt0b3JqYSBgdmAuCiAgCiAgMS4gTWF0ZW1hdGnEjW5lIGZ1bmtjaWplIG9ibGlrZSAkZjogXG1hdGhiYntSfV4qIFx0byBcbWF0aGJie1J9JCwgdG9yZWogZnVua2NpamUsIGtpIHBvbGp1Ym5vIMWhdGV2aWxvIG51bWVyacSNbmloIGFyZ3VtZW50b3YgX3pkcnXFvmlqb18gdiBza2FsYXJubyB2cmVkbm9zdC4gS28gdGVtIGZ1bmtjaWphbSBwb2RhbW8ga290IGFyZ3VtZW50IHZla3RvciBgdmAsIGplIHJlenVsdGF0IHNrYWxhcm5hIHZyZWRub3N0LiBQcmltZXJpIHRha2loIGZ1bmtjaWogc28gYHN1bWAsIGBtZWFuYCwgYHNkYCBpbiBgbGVuZ3RoYCwga2kgdnJuZWpvIHZzb3RvLCBwb3ZwcmXEjW5vIHZyZWRub3N0LCBzdGFuZGFyZG5pIG9ka2xvbiBpbiDFoXRldmlsbyBlbGVtZW50b3YgdmVrdG9yamEuCiAgClBvZ29zdG8gbmFtIGtvcmlzdGlqbyB0dWRpIGR2ZSBmdW5rY2lqaSBuYWQgbml6aSB6bmFrb3Y6CiAgKiBgbmNoYXIoeClgLCBraSBrb3QgcmV6dWx0YXQgdnJuZSDFoXRldmlsbyB6bmFrb3YgdiBwb2RhbmVtIG5penUgem5ha292IGB4YC4KICAqIGBwYXN0ZSh4MSwgeDIsIC4uLiwgeG4pYCwga2kga290IHJlenVsdGF0IHZybmUgZW4gbml6IHpuYWtvdiBkb2JsamVuIHMgc3Rpa29tIHBvZGFuaWggbml6b3Ygem5ha292IGB4MWAsIGB4MmAsIC4uLiwgYHhuYCB6IHZtZXNuaW1pIHByZXNsZWRraS4gUmF6bGnEjWljYSB0ZSBmdW5rY2lqZSBgcGFzdGUwYCBvcHJhdmkgc3RpayBwb2RhbmloIG5pem92IGJyZXogdm1lc25paCBwcmVzbGVka292LgogIApJbiDFoWUgbmVrYWoga29yaXN0bmloIGZ1bmtjaWogemEgdmVrdG9yamU6CiAgKiBgc29ydCh2KWAgdnJuZSB2ZWt0b3IgeiBlbGVtZW50aSB2ZWt0b3JqYSBgdmAgdXJlamVuaW1pIHYgX25hcmHFocSNYWpvxI1lbV8gdnJzdG5lbSByZWR1LiBSYXpsacSNaWNhIGtsaWNhIGBzb3J0KHYsIGRlY3JlYXNpbmc9VFJVRSlgIHVyZWRpIGVsZW1lbnRlIHZla3RvcmphIHYgX3BhZGFqb8SNZW1fIHZyc3RuZW0gcmVkdS4KICAqIGB1bmlxdWUodilgIHZybmUgdmVrdG9yIGVsZW1lbnRvdiBgdmAgYnJleiBwb25hdmxqYW5qYSwgcmV6dWx0YXQgamUgdG9yZWogdmVrdG9yIHYga2F0ZXJlZ2Egc2UgdnNhayBlbGVtZW50IHBvamF2aSBsZSBlbmtyYXQuCiAgKiBgd2hpY2godilgLCBramVyIGplIGB2YCB2ZWt0b3IgbG9nacSNbmloIHZyZWRub3N0aSwgdnJuZSBjZWxvxaF0ZXZpbHNraSB2ZWt0b3IgaW5kZWtzb3YgZWxlbWVudG92IGB2YCwga2kgaW1ham8gdnJlZG5vc3QgYFRSVUVgLiDEjGUgamUgYHZgIHBvaW1lbm92YW4gdmVrdG9yLCB2cm5lIHZla3RvciB6IGltZW5pIHRlaCBlbGVtZW50b3YuCgojIyMgUHJpbWVyaQoKUG9nbGVqbW8gc2kgbmVrYWogcHJpbWVyb3YgdHZvcmplbmphIHNlem5hbW92IGluIG9wZXJhY2lqIG5hZCBuamltaS4KClR2b3JqZW5qZSB2ZWt0b3JqZXYgeiBgOmAgaW4gcHJpbWVyaSBvcGVyYWNpaiwga2kga29tYmluaXJham8gdmVrdG9yamUgaW4gc2thbGFyamU6CmBgYHtyfQoxOjUKNTotNQoKMiBeICgwOjUpCigwOjUpIF4gMgooMDo1KSBeICg1OjApCgp4ID0gMTAKKDI6KHggLTEpKQpgYGAKClR2b3JqZW5qZSB2ZWt0b3JqZXYgeiBgc2VxYCBpbiBmdW5rY2lqZToKYGBge3J9CnNlcSgwLCAxLCAwLjEpCnNlcSgwLCAxLCBsZW5ndGgub3V0PTYpCgpzZXEoMCwgMSwgbGVuZ3RoLm91dD02KSAqIHNlcSgwLCAxLCBsZW5ndGgub3V0PTMpCgpzaW4oc2VxKDAsIHBpLCBsZW5ndGgub3V0PTUpKQptZWFuKDA6MTAwKQpgYGAKClBvbmF2bGphbmplIHogYHJlcGA6CmBgYHtyfQpyZXAoLTE6MSwgMykKcmVwKC0xOjEsIGVhY2g9MikKCnVuaXF1ZShyZXAoLTE6MSwgMykpCmBgYAoKSW4gxaFlIG5ha2xqdcSNbm8gdnpvcsSNZW5qZSBzIHBvbmF2bGphbmplbSBpbiBicmV6OgpgYGB7cn0Kc29ydChzYW1wbGUoLTU6NSwgNSwgcmVwbGFjZT1GKSkKc29ydChzYW1wbGUoLTU6NSwgNSwgcmVwbGFjZT1UKSkKYGBgCgoKCiMjIFBvc2VibmUgdnJzdGUgdmVrdG9yamV2CgoqKkZha3RvcioqIChhbmdsLiBfZmFjdG9yXykgamUgdmVrdG9yIHogZWxlbWVudGksIGtpIGltYWpvIHZyZWRub3N0aSBpeiB2bmFwcmVqIHpuYW5lLCByZWxhdGl2bm8gbWFqaG5lIG1ub8W+aWNlIG1vxb5uaWggdnJlZG5vc3RpLiBUbyBtbm/FvmljbyBkb2xvxI1hIGF0cmlidXQgZmFrdG9yamEgYGxldmVsc2AuCgpGYWt0b3JqZSB0dm9yaW1vIGl6IHZla3RvcmpldiBzIGZ1bmtjaWpvIGBmYWN0b3JgOgpgYGB7cn0KeCA9IGZhY3RvcihjKCJhIiwgImIiLCAiYiIsICJhIikpCngKdHlwZW9mKHgpCmF0dHJpYnV0ZXMoeCkKYGBgCgpLb3QgdmlkaW1vIHNvIGZha3RvcmppIHByYXZ6YXByYXYgY2Vsb8WhdGV2aWxza2kgdmVrdG9yamkuIE5hYm9yIG1vxb5uaWggdnJlZG5vc3RpIGxhaGtvIG5hc3RhdmltbyB0dWRpIG5ha25hZG5vLCBrYXIgbmFtIHBvbWFnYSBwcmkgYW5hbGl6aSBwb2RhdGtvdi4gVGFrbywgbnByLiBmYWt0b3JqdSBgeGAgaXogemdvcm5qZSBrb2RlIGxhaGtvIGRvZGFtbyBtb8W+bm8gdnJlZG5vc3QgYCJjImAsIGthciBzcHJlbWVuaSBvYm5hxaFhbmplIGZ1bmtjaWplIGB0YWJsZWAsIGtpIHByZcWhdGV2YSBmcmVrdmVuY2UgcG9qYXZpdGV2IHJhemxpxI1uaWggdnJlZG5vc3RpIHYgZmFrdG9yanUgKGFsaSB2ZWt0b3JqdSk6CmBgYHtyfQp0YWJsZSh4KQoKbGV2ZWxzKHgpID0gYyhsZXZlbHMoeCksICJjIikKdGFibGUoeCkKYGBgCgpNb8W+bmUgdnJlZG5vc3RpIGZha3RvcmpldiBzbyB2IHNwbG/FoW5lbSBuZXVyZWplbi4gxIxlIHNvIG1vxb5uZSB2cmVkbm9zdGkgZWxlbWVudG92IHVyZWplbmUsIHBvdGVtIGplIGZha3RvciB1cmVqZW4uIFVyZWplbmUgZmFrdG9yamUgdHZvcmltbyBzIGZ1bmtjaWpvIGBvcmRlcmVkYDoKYGBge3J9Cm9jZW5lID0gb3JkZXJlZCgKICBjKCJkb2JybyIsICJvZGxpxI1ubyIsICJwcmF2IGRvYnJvIiwgInphZG9zdG5vIiwgInphZG9zdG5vIiksCiAgbGV2ZWxzID0gYygiemFkb3N0bm8iLCAiZG9icm8iLCAicHJhdiBkb2JybyIsICJvZGxpxI1ubyIpCikKCm9jZW5lCmBgYAoKQm9kaXRlIHBvem9ybmkgbmEgemFkbmpvIHZyc3RpY28gaXpob2RhLCBraSB6IHVwb3JhYm8gem5ha2EgYDxgIHBvdmUga2FrxaFuYSBqZSB1cmVqZW5vc3QgbW/Fvm5paCB2cmVkbm9zdGkgZWxlbWVudG92LgoKKipEYXR1bXNraSB2ZWt0b3IqKiBqZSBuYWRncmFkbmphIHZla3RvcmpldiBudW1lcmnEjW5paCB2cmVkbm9zdGkgKHBsYXZham/EjWUgdmVqaWNlKS4gVHZvcmltbyBqaWggbGFoa28gbmEgdmXEjSBuYcSNaW5vdi4gUyBmdW5rY2lqYW1pIGBhcy5EYXRlYCwgxI1lIG5hcyDEjWFzLyB1cmEgbmUgemFuaW1hLCBhbGkgYGFzLlBPU0lYY3RgLCDEjWUgaG/EjWVtbyBkYXR1bXUgcHJpZHJ1xb5pdGkgdHVkaSB1cm86CmBgYHtyfQpub3cgPSBhcy5EYXRlKFN5cy5EYXRlKCkpCm5vdwoKZnV0dXJlID0gYXMuRGF0ZShTeXMuRGF0ZSgpICsgMzYwKQpmdXR1cmUKCmR1cmF0aW9uID0gZnV0dXJlIC0gbm93CnR5cGVvZihkdXJhdGlvbikKZHVyYXRpb24KCm5vdyA9IGFzLlBPU0lYY3QoU3lzLnRpbWUoKSwgKQpgYGAKCkJvZGl0ZSBwb3pvcm5pIG5hIGl6cGlzIHpnb3JuamVnYSBwcmltZXJhIGDEjGFzb3ZuYSByYXpsaWthIDM2MCBkbmlgOiByYXpsaWthIGR2ZWggZGF0dW1vdiBqZSBwb3NlYmVuIHRpcCBkYXR1bXNrZWdhIHZla3RvcmphLCBraSB1c3RyZXphIMSNYXNvdm5lbXUgaW50ZXJ2YWx1LiBUdm9yaW1vIGdhIGxhaGtvIHR1ZGkgc2FtaSBzIGZ1bmtjaWpvIGBhcy5kaWZmdGltZWAgaW4gZG9sb8SNYW1vIGVub3RlOgpgYGB7cn0KZHVyYXRpb24gPSBhcy5kaWZmdGltZSgxLCB1bml0cz0id2Vla3MiKQpub3cgKyBkdXJhdGlvbgpgYGAKCgotLS0KCiMgU2V6bmFtaSAoYW5nbC4gX2xpc3RzXykKClphIHJhemxpa28gb2QgdmVrdG9yamV2LCBzZXpuYW1pIHYgUi1qdSBsYWhrbyB2c2VidWplam8gZWxlbWVudGUsIGtpIGltYWpvICoqdnJlZG5vc3RpIHJhemxpxI1uaWggdGlwb3YqKi4gWmF0byByZcSNZW1vLCBkYSBzbyBzZXpuYW1pIHBvc3Bsb8WhaXRldiB2ZWt0b3JqZXYgaW4gamloIGltZW51amVtbyB0dWRpIHNwbG/FoW5pIChnZW5lcmnEjW5pKSB2ZWt0b3JqaSAoYW5nbC4gX2dlbmVyaWMgdmVjdG9yc18pLiAqKlBvem9yKio6IFNlem5hbWkgdiBQeXRob24tdSBzbyBwcmF2emFwcmF2IHBvZG9ibmkgdmVrdG9yamVtIChpbiBuZSBzZXpuYW1vbSkgdiBSLWp1LgoKU2V6bmFtZSBsYWhrbyB0dm9yaW1vIHMgZnVua2Npam8gYGxpc3RgOgpgYGB7cn0KbDEgPSBsaXN0KAogIDE6MywKICAiYSIsCiAgYyhUUlVFLCBGQUxTRSwgVFJVRSksCiAgYygyLjMsIDUuOSkKKQoKbDEKdHlwZW9mKGwxKQoKbGVuZ3RoKGwxKQpgYGAKQm9kaW1vIHBvem9ybmkgbmEgZGVqc3R2bywga2kgZ2Ega2HFvmUgemFkbmppIHByaW1lciwgZGEgaW1hIHNlem5hbSDFoXRpcmkgZWxlbWVudGUuIFZla3RvcmppIChpbiBuZSBuamlob3ZpIHBvc2FtZXpuaSBlbGVtZW50aSkgc28gZWxlbWVudGkgc2V6bmFtYS4gS290IGJvbW8gdmlkZWxpIHBvem5lamUgc28gbGFoa28gdHVkaSBzZXpuYW1pIGVsZW1lbnRpIHNlem5hbWEuCgoKVGFrbyBrb3QgdmVrdG9yamksIHNvIGxhaGtvIHR1ZGkgc2V6bmFtaSBwb2ltZW5vdmFuaToKYGBge3J9Cm9zZWJhID0gbGlzdCgKICBpbWUgPSAiQWxla3NhbmRyYSIsCiAgc3BvbCA9ICLFviIsCiAgc3Rhcm9zdCA9ICIyMSIKKQpvc2ViYQpgYGAKClNlem5hbWUgbGFoa28gZ25lemRpbW8sIGthciBwb21lbmksIGRhIGplIGxhaGtvIHNlem5hbSBlbGVtZW50IHNlem5hbWEuIFR1a2FqIGplIHByaW1lciBuZW5hdmFkbmVnYSBzZXpuYW1hIHogZW5pbSBlbGVtZW50b206CmBgYHtyfQpsMiA9IGxpc3QobGlzdChsaXN0KDEpKSkgIyByZXMgxI11ZGVuIHNlem5hbQpsMgpgYGAKClJhemxpxI1uZSBzZXpuYW1lIGxhaGtvIGtvbWJpbmlyYW1vLyBzZXN0YXZsamFtbyBzIGZ1bmtjaWpvIGBjYCBhbGkgYGxpc3RgOgpgYGB7cn0KbDMgPSBsaXN0KGxpc3QoMSwgMiksIGMoMywgNCkpCmwzCmw0ID0gYyhsaXN0KDEsIDIpLCBjKDMsIDQpKQpsNApgYGAKClYgemdvcm5qaWggcHJpbWVyaWggYm9kaXRlIHBvem9ybmkgbmEgcmF6bGlrbyBtZWQgc2V6bmFtb21hIGBsM2AgaW4gYGw0YC4gUG96b3JubyBqdSBwcmltZXJqYWp0ZSwgcG9nbGVqdGUga2FrxaFuYSBqZSBkb2zFvmluYSB2c2FrZWdhIGluIHBvamFzbml0ZSB6YWthaiBwcmlkZSBkbyB0ZSByYXpsaWtlLiBQcmkga2F0ZXJlbSBvcGF6dWplbW8gZ25lemRlbmplIHNlem5hbW92PwoKxIxlIHNpIMW+ZWxpbW8gdmVrdG9yIHByZXR2b3JpdGkgdiBzZXpuYW0sIHVwb3JhYmltbyBmdW5rY2lqbyBgYXMubGlzdGAuIFJlenVsdGF0IGZ1bmtjaWplIGplIHNlem5hbSwga2kgaW1hIGVuYWtvIMWhdGV2aWxvIGVsZW1lbnRvdiBrb3QgdmVrdG9yIGluIHZzYWsgZWxlbWVudCB2ZWt0b3JqYSBwb3N0YW5lIGVsZW1lbnQgc2V6bmFtYS4gUG9nbGVqbW8gxaFlIGVuIHByaW1lciwga2kgcG9uYXpvcmkgcmF6bGlrbyBtZWQgZnVua2NpamFtYSBgYXMubGlzdGAgaW4gYGxpc3RgOgpgYGB7cn0KbDUgPSBhcy5saXN0KC0yOjIpCmw1CgpsNiA9IGxpc3QoLTI6MikKbDYKYGBgCgpaYSBwcmV0dm9yYm8gc2V6bmFtYSB2IHZla3RvciB1cG9yYWJsamFtbyBmdW5rY2lqbyBgdW5saXN0YC4gUHJpIHRlbSBtb3JhbW8gYml0aSBwb3pvcm5pIG5hIHRvLCBkYSBtb3Jham8gYml0aSB2c2kgZWxlbWVudGkgdmVrdG9yamEgaXN0ZWdhIHRpcGEuIMSMZSBzbyBlbGVtZW50aSBzZXpuYW1hIHJhemxpxI1uaWggdGlwb3YsIGJvIFIgbWVkIGl6cmHEjXVub20gcmV6dWx0YXRhIGZ1bmtjaWplIGB1bmxpc3RgIG9wcmF2aWwgcHJpc2lsbm8gcHJldHZvcmJvIHRpcG92LiBQb2dsZWptbyBwcmltZXJlOgpgYGB7cn0KdW5saXN0KGwxKQp1bmxpc3QobDIpCnVubGlzdChsMykKdW5saXN0KGw0KQp1bmxpc3QobDUpCnVubGlzdChsNikKYGBgCgpQb2phc25pIHpha2FqIHN0YSByZXp1bHRhdGEgYGwzYCBpbiBgbDRgICh0ZXIgYGw1YCBpbiBgbDZgKSBlbmFrYS4KCiMjIEluZGVrc2lyYW5qZSBzZXpuYW1vdgoKS28gaW5kZWtzaXJhbW8gc2V6bmFtIGBzYCB0YWtvLCBrb3QgaW5kZWtzaXJhbW8gdmVrdG9yamUsIHRvcmVqIGluZGVrcyBwb3N0YXZpbW8gdiBlbm9qbmUgb2tsZXBhamUsIG5hIHByaW1lciBgc1sxXWAgYWxpIGBzWzI6M11gLCBqZSByZXp1bHRhdCBpbmRla3NpcmFuamEgKip2ZWRubyoqIHNlem5hbSwgxI1ldHVkaSBzZXpuYW0geiBlbmltIGVsZW1lbnRvbS4gxIxlIMW+ZWxpbW8geiBpbmRla3NpcmFuamVtIHNlem5hbWEgZG9iaXRpIG5qZWdvdmUgcG9zYW1lem5lIGVsZW1lbnRlLCBtb3JhbW8gdXBvcmFibGphdGkgZHZvam5lIG9nbGF0ZSBva2xlcGFqZS4gUHJ2aSBlbGVtZW50IHNlem5hbWEgdG9yZWogZG9iaW1vIHogaXpyYXpvbSBgc1tbMV1dYC4KClBvZ2xlam1vIG5la2FqIHByaW1lcm92OgpgYGB7cn0KbDFbMjozXSAjIHNlem5hbSB6IGR2ZW1hIGVsZW1lbnRvbWEKbDFbLTRdICMgc2V6bmFtIHMgdHJlbWkgZWxlbWVudGkKCmwxWzFdICMgc2V6bmFtIHogZW5pbSBlbGVtZW50b20KdHlwZW9mKGwxWzFdKQoKbDFbWzFdXSAjIHBydmkgZWxlbWVudCBzZXpuYW1hCnR5cGVvZihsMVtbMV1dKQpgYGAKCioqT3BvbWJhKio6IGR2b2puZSBvZ2xhdGUgb2tsZXBhamUgbGFoa28gdXBvcmFibGphbW8gdHVkaSB6YSBkb3N0b3AgZG8gcG9zYW1lem5paCBlbGVtZW50b3YgKip2ZWt0b3JqYSoqLiBWZWxpa28gcHJvZ3JhbWVyamV2IHYgUi1qdSBzZSBwb3NsdcW+dWplIGR2b2puaWggb2tsZXBhamV2IHByaSBuYXNsYXZsamFuanUgcG9zYW1lem5paCBlbGVtZW50b3YgdmVrdG9yamEgKGluIHNldmVkYSBzZXpuYW1hKSB6YXRvLCBkYSBqZSBrb2RhIGJvbGogcHJlZ2xlZG5hLiBaIGR2b2puaW1pIG9rbGVwYWppIG5hbXJlxI0gcG91ZGFyaW1vLCBkYSDFvmVsaW1vIHogaW5kZWtzaXJhbmplbSBuYXNsb3ZpdGkgdG/EjW5vIGVuIGVsZW1lbnQgdmVrdG9yamEuIEtvIHBhIHVwb3JhYmxqYW1vIGluZGVrc2lyYW5qZSB6YSBkb3N0b3AgZG8gdmXEjWplZ2EgKGFsaSBwb2xqdWJuZWdhKSDFoXRldmlsYSBlbGVtZW50b3YsIHBvdGVtIG1vcmFtbyB1cG9yYWJsamF0aSBlbm9qbmUgb2tsZXBhamUuCgpWIHBvaW1lbm92YW5paCBzZXpuYW1paCBsYWhrbyBwb3NhbWV6bmUgZWxlbWVudGUgbmFzbG92aW1vIG5hIHZlxI0gbmHEjWlub3Y6CmBgYHtyfQpvc2ViYVtbMV1dCm9zZWJhW1siaW1lIl1dCm9zZWJhJGltZQpgYGAKClBydmkgbmHEjWluIHogdXBvcmFibyB6bmFrYSBgJGAgamUgbmFqYm9saiBvYmnEjWFqZW4gaW4gcG9nb3N0byB1cG9yYWJsamVuLgoKLS0tCgojIE5hbG9nZQoKIyMgUHJlbWlzbGVraSBicmV6IHBvZ2FuamFuamEgUgoKMS4gTmHFoXRlaiBwbyBwYXIgcHJpbWVyb3YgaXpyYXpvdiB6IG51bWVyacSNbmltaSB2cmVkbm9zdG1pIGBJbmZgLCBgLUluZmAgaW4gYE5hTmAuCjEuIEtha8WhZW4gamUgdGlwIGVsZW1lbnRvdiB2ZWt0b3JqYSBgYygxOjUsIEZBTFNFKWA/CjEuIEthaiBqZSByZXp1bHRhdCBrbGljZXYgYGxlbmd0aChjKDE6NSwgNToxKSlgIGluIGBsZW5naHQoImFiYyIpYD8KMS4gUHJlbWlzbGkga2FrbyBkb2xnYSBib3N0YSB2ZWt0b3JqYSBgMTo1ICsgMWAgaW4gYDE6KDUgKyAxKWAuCjEuIEthaiBpenJhxI11bmEgaXpyYXogYHN1bSh2ID09IDMpYCB6YSBwb2RhbiB2ZWt0b3IgYHZgPyBLYWtvIGplIHZyZWRub3N0IHRlZ2EgaXpyYXphIHBvdmV6YW5hIHogdnJlZG5vc3RqbyBpenJhemEgYHdoaWNoKHYgPT0gMylgPwoxLiBTZXpuYW0gYHggPSBsaXN0KGE9YygxLDEpLCBiPTIsIGM9bGlzdChjKDMsMywzKSxjKDQpKSlgLiBLYWvFoW5lIHNvIHZyZWRub3N0aSBpenJhem92IGB4WzFdYCwgYHhbWzJdXWAgaW4gYHhbWzNdXVsxXWA/IE5hxaF0ZWogxI1pbSB2ZcSNIG5hxI1pbm92IHphIHByaWRvYml2YW5qZSB2cmVkbm9zdGkgZHJ1Z2VnYSBlbGVtZW50YSBzZXpuYW1hIGB4YC4KCiMjIFByb2dyYW1lcnNrZSBuYWxvZ2UKCjEuIERlbmltbywgZGEgc21vIEFsZcWhdSwgQmFyYmFyaSwgQ2lyaWx1IGluIERhcmppIGl6bWVyaWxpIHZpxaFpbmUgdiBjZW50aW1ldHJpaCAoMTgwLCAxNjUsIDE2MCwgMTkzKSBpbiB0ZcW+ZSB2IGtpbG9ncmFtaWggKDg3LCA1OCwgNjUsIDEwMCkuIE5hcmVkaSBkdmEgdmVrdG9yamEgYHZgIGluIGB0YCB6IGl6bWVyamVuaW1pIHBvZGF0a2kuIEluZGVrcyB0ZWxlc25lIG1hc2UgKElUTSkgaXpyYcSNdW5hbW8gcG8gZm9ybXVsaQokJCBcdGV4dHtJVE19ID0gXGZyYWN7XHRleHR7dGXFvmEgdiBraWxvZ3JhbWlofX17KFx0ZXh0e3ZpxaFpbmEgdiBtZXRyaWh9KV4yfS4gJCQKSXpyYcSNdW5haiBfcG9pbWVub3Zhbl8gdmVrdG9yIHZyZWRub3N0aSBJVE0gemEgxaF0aXJpIG9zZWJlLCBramVyIHNvIGltZW5hIGVsZW1lbnRvdiBkZWphbnNrYSBpbWVuYSBvc2ViIChBbGXFoSwgQmFyYmFyYSwgQ2lyaWwgaW4gRGFyamEpLiBOYXRvIGl6cmHEjXVuYWogdmVrdG9yIG5hcmF2bmloIGxvZ2FyaXRtb3YgdnJlZG5vc3RpIElUTS4gTmEga29uY3UgaXpwacWhaSB2ZWt0b3IgaW1lbiBvc2ViLCBraSBpbWFqbyBJVE0gdmXEjWppIG9kIDI1LCB0ZXIgaXpyYcSNdW5haiBuamlob3YgcG92cHJlxI1uaSBJVE0uCgoxLiBTZXN0YXZpIHZla3RvcmplIHogbmFzbGVkbmppbWkgZWxlbWVudGk6CgogICAgYS4gJDNeMS8xLCAzXjIvMiwgXGxkb3RzLCAzXns1MH0vNTAkOwogICAgYS4gIkExIiwgIkEyIiwgJFxsZG90cyQsICJBNTAiOwogICAgYS4gJGVeeCBcc2luIHgkIGl6cmHEjXVuYW5pbWkgdiB0b8SNa2FoICR4ID0gMywgMy4xLCAzLjIsIFxsZG90cywgNiQuPGJyPjxicj4KCjEuIFBvZGFubyBqZSBuYXJhdm5vIMWhdGV2aWxvIGB4ID4gMmAuIFphcGnFoWkgaXpyYXogdiBSLWp1LCBrYXRlcmVnYSByZXp1bHRhdCBqZSBgVFJVRWAsIMSNZSBqZSBgeGAgcHJhxaF0ZXZpbG8gaW4gYEZBTFNFYCBzaWNlci4gTmFtaWc6IGl6cmF6IGdyYWRpIHBvc3RvcG9tYS4gTmFqcHJlaiBpenJhxI11bmFqIHZla3RvciBvc3RhbmtvdiBkZWxqZW5qYSBgeGAgcyB2c2VtaSDFoXRldmlsaSBvZCBgMmAgZG8gYHggLSAxYC4gTmF0byBwcmV2ZXJpLCDEjWUgamUgYDBgIGVsZW1lbnQgdGVnYSBzZXpuYW1hLgoKMS4gSXpyYcSNdW5haiBkdmEgdmVrdG9yamEgYHhgIGluIGB5YCBkb2zFvmluZSAyNTAgdGFrbywgZGEgcyBwb25hdmxqYW5qZW0gbmFrbGp1xI1ubyBpemJpcmHFoSBjZWxhIMWhdGV2aWxhIGl6IGludGVydmFsYSAkWzAsIDk5OV0kLiBPem5hxI1pbW8geiAkeF8xLCB4XzIsIFxsZG90cywgeF97MjUwfSQgaW4gJHlfMSwgeV8yLCBcbGRvdHMsIHlfezI1MH0kIGVsZW1lbnRlIHNlem5hbW92IGB4YCBpbiBgeWAuIEl6cmHEjXVuYWo6CgogICAgYS4gVmVrdG9yIHogZWxlbWVudGkgJHlfMiAtIHhfMSwgeV8zIC0geF8yLCBcbGRvdHMsIHlfezI1MH0gLSB4X3syNDl9JC4KICAgIGEuIFZzb3RvICRcc3VtX3tpPTF9XnsyNDl9IGVeey0geF97aSsxfX0gLyAoeF9pICsgMTApJC48YnI+PGJyPgoKMS4gU2VzdGF2aSB2ZWt0b3IgdGlwYSBmYWt0b3IgeiBuYWJvcm9tIG1vxb5uaWggdnJlZG5vc3RpIGBBYCwgYEJgIGluIGBDYC4gVmVrdG9yIG5haiBpbWEgbmFrbGp1xI1ubyBpemJyYW5paCAxNSBlbGVtZW50b3YsIHBvIG5hdGFua28gcGV0ICg1KSBpeiB2c2FrZSBrYXRlZ29yaWplLgoKMS4gTmEgRk1GIHBvZ29zdG8gb2Nlbmp1amVtbyBkb3Nlxb5rZSDFoXR1ZGVudG92IG5hIGl6cGl0IHogb2RzdG90bmltaSB0b8SNa2FtaTogxaF0dWRlbnRrYSBsYWhrbyBkb3Nlxb5lIG9kIDAgZG8gMTAwIG9kc3RvdG5paCB0b8SNayAodmtsanXEjW5vKS4gRG9zZcW+ZWsgdiBvZHN0b3RuaWggdG/EjWthaCAoJSkgbmF0byBwcmV2ZWRlbW8gdiBvY2VubyBzIHByZWRwaXNvbSBpeiBzcG9kbmplIHRhYmVsZS4KCiAgICB8IGRvc2XFvmVrIHYgJSB8IG9jZW5hIHwKICAgIHw6LS0tfC0tLTp8CiAgICB8IHZlxI1qaSBvZCA5MCB8IG9kbGnEjW5vICgxMCkgfAogICAgfCB2ZcSNamkgb2QgODAgaW4gbmFqdmXEjSA5MCB8IHByYXYgZG9icm8gKDkpIHwKICAgIHwgdmXEjWppIG9kIDcwIGluIG5hanZlxI0gODAgfCBwcmF2IGRvYnJvICg4KSB8CiAgICB8IHZlxI1qaSBvZCA2MCBpbiBuYWp2ZcSNIDcwIHwgZG9icm8gKDcpIHwKICAgIHwgdmXEjWppIG9kIDUwIGluIG5hanZlxI0gNjAgfCB6YWRvc3RubyAoNikgfAogICAgfCBuYWp2ZcSNIDUwIHwgbmV6YWRvc3RubyAoNSkgfAoKICAgIGEuIFNlc3RhdmkgdmVrdG9yIGBvdGAgeiBuYWtsanXEjW5pbWkgZG9zZcW+a2kgZHZhanNldGloIMWhdHVkZW50b3Y7CiAgICBhLiBQcmV0dm9yaSBkb3Nlxb5rZSBpeiB2ZWt0b3JqYSBgb3RgIHYgdmVrdG9yIGBvYCB0aXBhIHVyZWplbiBmYWt0b3IsIGthdGVyaWggZWxlbWVudGkgc28gb2NlbmUgxaF0dWRlbnRvdiBpenJhxI11bmFuZSBwbyB6Z29ybmplbSBwcmVkcGlzdS48YnI+PGJyPgoKMS4gViBwcm9ncmFtdSB6YSB1cmVqYW5qZSBwcmVnbGVkbmljIEV4Y2VsIChpbiBwb2RvYm5vIHYgcHJvZ3JhbWloIEdvb2dsZSBTaGVldHMgYWxpIE51bWJlcnMpIHNvIHN0b2xwY2kgcG9pbWVub3ZhbmkgeiB2ZWxpa2ltaSDEjXJrYW1pIGFuZ2xlxaFrZSBhYmVjZWRlIChvZCBBIGRvIFopLCBuYXRvIHNsZWRpam8gc3RvbHBjaSBwb2ltZW5vdmFuaSBzIGR2ZW1hIMSNcmthbWEgKG9kIEFBLCBwcmVrIEFCLCBBQywgaW4gdGFrbyBuYXByZWosIGRvIFpZIGluIFpaKSwgbmF0byDFoWUgdGlzdGkgcyB0cmVtaSDEjXJrYW1pIChvZCBBQUEgZG8gWlpaKS48YnI+PGJyPlphZG5qaSBzdG9scGVjIHYgcHJlZ2xlZG5pY2kgamUgcG9pbWVub3ZhbiBYRkQuIE5hcGnFoWkgcHJvZ3JhbSB2IFItanUsIGtpIGl6cmHEjXVuYSBrb2xpa28gc3RvbHBjZXYgaW1hIHByZWdsZWRuaWNhIHYgcHJvZ3JhbXUgRXhjZWwuPGJyPjxicj5OYW1pZzogdXBvcmFiacWhIGxhaGtvIGBMRVRURVJTYCwgdC5qLiwgdmVrdG9yIHZlbGlraWggxI1yayB2IGFuZ2xlxaFraSBhYmVjZWRpLCB0ZXIga2xpY2UgZnVua2NpaiBgc29ydGAgaW4gYG91dGVyYCAocG9tYWdhaiBzaSB6IGA/b3V0ZXJgKSB2IGtvbWJpbmFjaWppIHMgZnVua2Npam8gYHBhc3RlMGAgemEgaXpyYcSNdW4gdmVrdG9yamEgeiBpbWVuaSBzdG9scGNldi4gTmF0byBzIGtsaWNlbSBmdW5rY2lqZSBgd2hpY2hgIHVnb3Rvdml0ZSBpbmRla3Mgc3RvbHBjYSBgWEZEYCB2IHRlbSB2ZWt0b3JqdS4KCjEuIFZzZSB2ZWt0b3JqZSBpbiBza2FsYXJqZSwgaXpyYcSNdW5hbmUgdiBwcnZpIG5hbG9naSB6Z29yYWogKG5hbG9naSBvIGluZGVrc3UgdGVsZXNuZSBtYXNlKSwgc2VzdGF2aSB2IHNlem5hbSBgcmVzaXRldmAgcyBwb2xqaSBgaW1lbmFgLCBgdmlzaW5lYCwgYElUTWAsIGBsb2dJVE1gLCBgSVRNLnYuMjVgLCBgaW1lbmEuSVRNLnYuMjVgIGluIGBJVE0udi4yNS5wb3ZwcmVjamVgLgoKLS0tCgpQcnZvIHJhemxpxI1pY28gdGVnYSB6dmV6a2Egc2VtIG9rdG9icmEgMjAyMSBwcmlwcmF2aWwgW0xqdXDEjW8gVG9kb3JvdnNraV0oaHR0cHM6Ly9rdC5panMuc2kvfmxqdXBjby8pIHYgamV6aWt1IFtSIE1hcmtkb3duXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkuIElkZWplIHphIHByaW1lcmUgc2VtIHNpIHYgdmVsaWtpIG1lcmkgaXpwb3NvamFsIGl6IHXEjWJlbmlrYSBbKFdpY2toYW0gMjAxOSldKGh0dHBzOi8vYWR2LXIuaGFkbGV5Lm56LyksIHZzZSBtb3JlYml0bmUgbmFwYWtlIHYgcHJpbWVyaWggc28gaXprbGp1xI1ubyBtb2plLiBadmV6ZWsgc2VtIG5hemFkbmplIHNwcmVtaW5qYWwgb2t0b2JyYSAyMDI0LgoKViB6dmV6a2loIHogemFwaXNraSBzIHByZWRhdmFuaiBwcmkgdGVtIHByZWRtZXR1IGplIGAuYCBvem5ha2EgemEgZGVjaW1hbG5vIHZlamljbyBpbiBgLGAgbG/EjWlsbyBtZWQgdGlzb8SNaWNhbWksIGthciB2IHNsb3ZlbsWhxI1pbmkgbmVuYXZhZG5vIChhbGkgcGEga2FyIG5hcm9iZSkuIFRha28gamUgMy8yID0gMS41IChpbiBuZSAxLDUpIGluIGplIDFlNiA9IDEwMDAwMDAgPSAxLDAwMCwwMDAgKGluIG5lIDEuMDAwLjAwMCku