Da biste mogli da koristite ovu datoteku potrebno je da imate instaliran R. Možete ga preuzeti sa linka: https://cran.r-project.org/bin/windows/ (za Windows), https://cran.r-project.org/bin/linux/ (za Linux) ili https://cran.r-project.org/bin/macosx/ (za OS X). Nisam siguran da će skriptovi raditi na Linuxu i OS X (onaj deo koji se odnosi na referisanje lokacija datoteka na disku - to morate prilagoditi ukoliko koristite ove operativne sisteme). Takođe, potrebno je da imate instaliran i RStudio (besplatnu verziju) sa linka https://www.rstudio.com/products/rstudio/download/ Zatim, potrebno je da kreirate folder OTSS(R) na disku C ili D i u njega kopirate priložene datoteke iz arhive (naredbe.r, test2.txt, rezultati.csv…) Kopirajte direktno u taj folder - ne u subfolder! Komande izvršavajte redom, klikom na zeleni trougao svakog odeljka (chunk - koji počinje sa “```{r}”). Ako želite da izvršite pojedinačnu naredbu iz odeljka, kliknite u nju (ne smete selektovati ništa ili morate selektovati celu naredbu) i pritisnite Ctrl+ENTER. Naredbe iz SCRIPT prozora možete kopirati u prozor CONSOLE i izvršavati sa ENTER. Možete ih menjati i videti rezultate. Poželjno je da to radite. Međutim, predlažem da skript prvi put prođete u izvornom obliku. Napomenuo bih da R koristi dve vrste navodnika ” i ’. Ako u komandama imate bilo koji drugi javiće vam grešku. Ne zaboravite da stavite navodnike i zareze tamo gde treba. To vrlo česte greške. Kada smo već kod grešaka, ukoliko ne znate zašto su se javile korisno je da ih kopirate u browser i potražite tamo šta je uzrok greške. Svi mi to radimo :)

SLAJD 8

Paketi

Paketi su biblioteke funkcija. Instalirate i pozivate samo one pakete koji vam trebaju – npr.

Instaliranje paketa pacman Instalacija se radi samo jednom na jednom računaru (osim ako ih ne obrišete).

Pozivanje (učitavanje) biblioteke

library(foreign) # ili 
require(foreign)
library(pacman)

Lakše je ovako

p_load(foreign)

Funkcija p_load iz paketa “pacman” proverava da li je paket (biblioteka) instaliran, ako nije instalira ga i učitava, a ako jeste samo ga učita. Prethodno mora biti učitana biblioteka pacman (samo jednom otkada pokrenete R)

SLAJD 9

Probajte…

library(Foreign)

Dobićete poruku: Error in library(Foreign) : there is no package called ‘Foreign’. Poruku ste dobili jer ste Foreign napisali sa velikim početnim slovom. R je osetljiv na velika i mala slova.

library(foreign)
# ovo je prošlo
# inače znak "#" na početku linije označava komentar (ne izvršava se)

SLAJD 10

Pomoć

Za pomoć u vezi sa nekom funkcijom kojoj znate ime ?funkcija ili help(funkcija) Ako tražite neke pojmove (koji nisu funkcije) onda ćete help dobiti sa help.search(“pojam koji me zanima”) Ili ??“pojam koji me zanima”

help(library)
?read.spss
??read.spss
help.search("chi square")

SLAJD 11

NAREDBE

R možete koristiti i kao kalkulator

2^3
sqrt(9)
2+2

Brojevi u uglastim zagradama [ ] su indeksi i označavaju poziciju elementa u nizu. Ovde se nizovi sastoje od jednog broja pa je logično da su to prvi elementi.

SLAJD 12

Ponovno pozivanje i ispravljanje naredbi

Strelicom na gore (na tastaturi) pozivate naredbu ponovo. Strelicom na levo dolazite do mesta koje želite da ispravite, brišete sa BACKSPACE (levo od kursora) ili sa DEL (desno od kursora), zatim ukucate novu vrednost i sa ENTER izvršite naredbu. Ako naredba nije kompletna pojaviće se “+”

2^
4

U gornjem odeljku to niste mogli videti jer su izvršena oba reda (naredbe). U konzolu unesite prvo samo 2^ i pritisnite ENTER. Pojaviće se znak + koji označava da je potrebno da kompletirate naredbu. Dodajte 4 i pritisnite ENTER. Ispisaće rezultat naredbe 2 na 4.

SLAJD 13

Radni direktorijum/folder

getwd()

…je naredba kojom saznajete koji vam je radni direktorijum (odnosno onaj u kojem R traži ili piše datoteke ako ne navedete punu putanju). Ako želite da ga promenite, koristite setwd(“željeni direktorijum”)

setwd("D:/OTSS(R)")

Napomena: Direktorijum (folder) mora postojati. Koristiti / umesto uobičajenog ”  “. Može i”\“. Za nastavak rada potrebno je da komandu setwd(”D:/OTSS(R)“) kopirate u konzolu i izvršite jer zbog specifičnog formata ove datoteke neće biti zapamćena izvan odeljka (chunk).

SLAJD 14

Pozivanje naredbi iz datoteke

Naredbe možete sačuvati u tekstualnoj datoteci, a iz nje ih možete pozvati komandom source

source("D:/OTSS(R)/naredbe.R")

Ili ako ste podesili radni direktorijum

source("naredbe.R")

Ako hoćete da vam rezultat bude ispisan na ekranu onda, dodajte argument “echo=TRUE”. Proverite u folderu “D:/OTSS(R) datoteku naredbe.R - možete je otvoriti sa Notepadom ili bilo kojim editorom teksta. Možete je otvoriti i u R-u. U njoj se nalaze komande 3*2 (množenje dva broja), sqrt(144) (kvadratni koren iz 144) i array(1:180, 180) (napravi niz brojeva od 1 do 180 sa 180 članova)

source("naredbe.R", echo=TRUE)

TRUE i FALSE se skraćeno mogu pisati T i F npr.

source("naredbe.R", echo=T)

Pomoć u vezi sa ovom funkcijom

?source

SLAJD 15

Ispis rezultata u datoteku

Ako želite da vam svi rezultati budu upisani u datoteku koristite naredbu sink(“naziv_datoteke”)

sink("izlaz.txt") 

Umesto “izlaz.txt” možete dati bilo koje ime. Datoteka će biti kreirana u radnom direktorijumu. Može i na drugom mestu, ali morate navesti punu putanju do tog mesta. Neće biti ispisa na ekranu. Ako želite da nastavite pisanje u datoteku u koju ste već pisali onda dodajte argument “append=TRUE”

sink("izlaz.txt", append=TRUE)
source("naredbe.R", echo=T)

Bez append=TRUE pregaziće staru datoteku.

Proverite da li u radnom folderu sad imate datoteku “izlaz.txt”. Vidite šta je u njoj.

Ipis u datoteku prekida se sa

sink()# nekada mora 2 puta da se pokrene
sink()

SLAJD 16

OBJEKTI

R radi sa objektima koje mi kreiramo ili se kreiraju u okviru rada pozvanih funkcija. Objekte kreiramo tako što im dodelimo neko ime i pridružimo određene vrednosti. Ako neku naredbu izvršimo i ne dodelimo joj ime, njen rezultat se gubi nakon ispisa na ekranu. Ako je smestimo u objekat ona ostaje sačuvana dok je ne uklonimo, a možemo je pozvati imenom objekta.

2*5
A<-2*5 #smeštamo rezultat množenja u objekat A
A #pozivamo objekat A
B=6 #smeštamo vrednost 6 u objekat B 
B
A*B #Izvršavamo operaciju sa objektima

Uklanjanje objekta (u ovom slučaju B)

remove(B) 

ili

rm(B) #ako ste pokrenuli prethodnu naredbu javiće grešku jer B više ne postoji

SLAJD 17

OBJEKTI…

ls() # ispisuje spisak svih objekata u okruženju (environment)

Uklanjanje više objekata (svih)

remove(list=ls()) 
# ili 
rm(list=ls())
ls()

SLAJD 18

PRIDRUŽIVANJE

Vrši se pomoću znakova “<-”, “->” ili komandom assign(), a može i sa “=”. Znak “<-” se u prozoru skripta i terminala najlakše može dobiti ako istovremeno pritisnete levi Alt i - sa numeričke tastature.

X<-(c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10.5))
X

Funkcija c kombinuje argumente (ono u zagradi) u vektor

assign("Y", c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10.5))
c(X, X)->XX

Na ovaj način kreirali smo dva ista vektora X i Y i vektor XX koji se sastoji od dva vektora X nastavljena jedan na drugi. Prikazaćemo ih

X
Y
XX

SLAJD 20

VEKTORI

Osnovni elementi u R-u. Indeksirani skupovi vrednosti istog tipa. Svaki element ima svoj indeks (lokaciju) pomoću kojega mu se može pristupiti (indeksi su označeni uglastim zagradama).

XX[11] #pozivamo 11. element vektora XX

Imaju svoj tip

SLAJD 21

VEKTORI - TIPOVI

Numerički Slovni (char). Koriste se kao faktori (npr kategorijalne grupišuće varijable), ali i za nazive varijabli, natpise na grafikonima… Na primer

imena<-c("pol", "starost", "sprema", "v1", "v2", "V3", "V4", "v5", "V6", "v7", "v8") #kreiramo slovni vektor nazvan "imena"
imena #ispisaćemo njegove vrednosti
names(X)<-imena #komandom names(X) dodelićemo ta imena elemntima vektora X
X #ispisaćemo da vidimo rezultat

Logički vektori

Nastaju evaluacijom logičkih uslova. Elementi su TRUE i FALSE.

Npr.

NV<-X>6 #napravićemo logički vektor NV i to takav da će vrednost TRUE dobiti elementi vektora X veći od 6
NV
cbind(X,NV)

Komanda cbind() spaja elemente u zagradi (moraju biti iste dužine) i prikazuje ih vertikalno. Sa rbind() dobijamo isto to, ali korizontalno. Probajte, ako želite

Indeksni vektori

Služe za pozivanje određenih elemenata drugih vektora i smeštaju se u uglaste zagrade. Npr.

Y<-X[1:10] 
# ili 
Y<-X[!is.na(X)] #Neka vektor Y budu samo one vrednosti X koje nisu nedostajuće NA

Funkcija is.na() testira da li su vrednosti u zagradi nedostajuće. Ako je u zagradi naveden vektor, onda se testira svaki element vektora i vraća vektor sa nedostajućim vrednostima. Ako ispred funkcije stavimo ! dobijamo njenu negaciju - vraća se vektor sa vrednostima koje nisu nedostajuće.

SLAJD 22

Računanje sa vektorima

Ako se dva ili više vektora pojavljuju u nekoј matematičkoj operaciji ona će biti ponovljena onoliko puta koliko najduži vektor ima elemenata (samo ako je dužina većeg deljiva bez ostatka dužinom kraćih vektora).

X^2 # recimo da je 2 vektor dužine 1 - skalar

Svaki element vektora X je kvadriran.

X*XX # ranije kreirani vektori

X je recikliran (pošto X ima 10 elemenata, a XX dvadeset, prvih 10 elemenata biće pomnoženo sa elementima vektora X, a zatim će drugih 10 elemenata biti pomnoženo opet istim vektorom X)

SLAJD 23

SVOJSTVA OBJEKATA

Objekti imaju tip i dužinu (između ostalog) Tipovi zavise od sadržaja objekta. Od tipa objekta zavisi šta možemo sa njime raditi. Tipovi su promenljivi. Dužina je takođe promenljiva

SLAJD 24

SVOJSTVA OBJEKATA

mode(X); length(X)

Funkcijom mode saznajemo tip, a length dužinu objekta (u ovom slučaju vektora). Znak ; omogućava vam da više komandi smestite u isti red.

X<-as.character(X[1:5]); mode(X); length(X)
X #ispisaćemo vektor X

Rezultati se ispisuju u posebnim redovima. Istovremeno smo skratili i promenili tip vektora. Komandom as.character promenili smo numerički vektor u slovni (elementi nisu brojevi već karakteri - to vidimo po navodnicima oko njih). Pomoću indeksnog vektora [1:5] rekli smo da X bude samo prvih 5 elemenata istog tog vektora X.

Produžićemo vektor dodajući mu na 15. poziciju cifru 6 (ali ne kao broj već kao karakter - uokvireno navodnicima)

X[15]<-"6"; length(X) 
X

Skratićemo vektor izbacujući nedostajuće vrednosti

X<-X[!is.na(X)] ; length(X) 
X

SLAJD 25

DALJE…

X+X

Gornja komanda se ne može izvršiti jer je vektor slovni (char). Ako izvršite naredbu dobićete grešku “Error in X + X : non-numeric argument to binary operator”. Zapamtite ovu situaciju i grešku jer vam se često može javiti ukoliko ne povedete računa o tipu podataka.

X<-as.numeric(X) # menjamo mu mode u numerički
X+X # i onda može da se sabira

SLAJD 26

Učitavanje datoteka

Podaci za R su tekstualnoj datoteci u kojoj su polja odeljena razmakom ili TABOM. Može i drugim separatorom, ali razmak je podrazumevani. Dobro je da prvi red sadržava nazive varijabli, a prva kolona redni broj slučaja. Ako to ne postoji R će dodeliti nazive i brojeve Komanda poput sledeće podaci<-read.table(file.choose(),header=T,sep=“) otvoriće klasični dijalog za izbor datoteke, i nakon njenog izbora učitaće je objekat”podaci” tipa data.frame (matrica podataka). Ovaj objekat postoji samo u memoriji ne i na disku. Na sledeći način učitavamo podatke bez otvaranja dijaloga.

podaci<-read.table("D:/OTSS(R)/test2.txt", header=TRUE, sep="", quote = "\"'", dec = ".")

Argument header=TRUE znači da su nazivi varijabli u prvom redu datoteke, dec=“.” da su decimale odeljene tačkom, sep=“” da su polja odeljena praznim mestom, a quote = “"’” da su tekstualne varijable ograničene dvostrukim ili jednostrukim navodnicima.

names(podaci) # nazivi kolona matrice podataka da proverite da li ste učitali dobre podatke
str(podaci) # naredba kojom se proverava struktura objekta (dobijete nazive kolona, broj opservacija/ispitanika, broj varijabli i prvih 10 vrednosti)
head(podaci)

Učitavanje podataka tipa Coma Separated Values

podaci2<-read.csv("rezultati.csv", header=TRUE, sep=";", quote="\"'", dec = ".")
podaci2

sep može biti i “,” u zavisnosti od podešavanja kodne stranice operativnog sistema.

Učitavanje podataka tipa EXCEL

Postoji paket za čitanje podataka iz excela pod nazivom “xlsx”

p_load(xlsx)
podaciXY = read.xlsx("rezultati.xls", sheetIndex=1, rowIndex=NULL, colIndex=NULL)
podaciXY

sheetIndex je broj lista (sheet) iz excel datoteke, rowIndex i colIndex označavaju redove i kolone koje želimo da uvezemo. Ako ostavimo NULL to znači da želimo da uvezemo sve.

Problem sa domaćim karakterima se donekle može rešiti opcijom encoding=“UTF-8”

podaciXY = read.xlsx("rezultati.xls", sheetIndex=1, rowIndex=NULL, colIndex=NULL, encoding="UTF-8")
podaciXY
?read.xlsx #možete pogledati i druge opcije

Pisanje u excel datoteku

write.xlsx(podaciXY, "podaciXY.xlsx", col.names=TRUE, row.names=TRUE)

Za excel datoteke postoji i paket “readxl”

p_load(readxl)
podaciXY = read_excel("rezultati.xls", sheet=1)
podaciXY
?read_excel

SLAJD 28

data.frame

Korisno je učitati podatke kao objekat klase data.frame. Jednostavno, samo funkciju za učitavanje staviti u funkciju data.frame(), npr…

podaciXY = data.frame(read_excel("rezultati.xls", sheet=1))

Funkcija read.xlsx to radi po defaultu

SLAJD 29

Učitavanje datoteka

Podatke zatim možete pregledati i uređivati sa

fix(podaci2)
edit(podaci2)
head(podaci2, 10) # ispis prvih 10 redova
tail(podaci2, 5) # ispis poslednjih 5 redova

Prilikom učitavanja svakoj koloni se dodeljuje tip u zavisnosti od podataka koje sadrže (obično to ispadne kako treba…) Pojedinačnoj varijabli (vektoru) u podacima pristupate navođenjem naziva objekta u kom se nalazi praćenim znakom $ i nazivom vektora npr.

podaci2$INDEX
mode(podaci2$INDEX)

Ili preko indeksa (broja) kolone

podaci2[1] #numerički indeks, prva kolona
podaci2[1:3] #numerički indeks, prve tri kolone
podaci2[c("INDEX","FORMA", "pit1")] #imena kao indeks

Na taj način možete iz matrice podataka izdvojiti varijable za analizu, na primer… Možete ih kopirati i u novi objekat…

SLAJD 30

Pisanje u datoteke

Ako hoćete da u matricu podataka (data.frame) dodate neku varijablu koju ste izračunali, morate je referisati isto kao kad je čitate

podaci2$SUMA<-rowSums(podaci2[3:42], na.rm=FALSE) 
podaci2$SUMA

Funkcija rowSums sabira redove u matrici podataka. Svi podaci moraju biti numerički. Argument na.rm=FALSE rezultira nedostajućim podacima (NA) ako je bar jedan sabirak NA. na.rm=TRUE rezultira zbirom i u takvoj situaciji.

Pisanje na disk

U CSV, sa separatorom ; , bez naziva redova, sa nazivima kolona, i duplim navodnicima za tekstualne varijable

write.table(podaci2, file = "podaci2.csv", sep = ";", row.names=FALSE, col.names = TRUE, qmethod = "double")

U TXT, sa TAB separatorom, bez naziva redova, sa nazivima kolona, bez navodnika za tekstualne varijable

write.table(podaci2, file = "podaci2.txt", sep = "\t", row.names=FALSE, col.names = TRUE, quote = FALSE)

SLAJD 31

Brisanje varijable iz matrice podataka

Prvo ćemo kreirati novu varijablu

podaci2$BEZVEZE<-rowSums(podaci2[3:42], na.rm=TRUE)

Sabrali smo po redovima varijable od 3 do 43. Sa fix(podaci2) ili View(podaci2) možete videti varijablu BEZVEZE koju ste kreirali na kraju matrice. Brisanje varijable BEZVEZE

podaci2$BEZVEZE<- NULL

Sa fix(podaci2) ili View(podaci2) možete videti da varijable BEZVEZE više nema.

SLAJD 32

Sortiranje

Koristimo funkciju order()

podaci_s=podaci2[order(podaci2$FORMA),]
podaci_s

Funkcija order(podaci2$FORMA) kreira vektor rednih brojeva slučajeva po redosledu na varijabli FORMA (ukoliko želite pokrenite je samostalno da vidite rezultat). Taj vektor se smešta u uglaste zagrade posle naziva objekta koji hoćemo da sortiramo (podaci2). Indeksi matrice podataka u zagradama imaju dva dela [ , ]. Oni pre zareza odnose se na redove, a oni posle na kolone. Ako ne stavimo zarez onda se podrazumeva da se odnose na kolone. Pošto sortiramo redove stavili smo zarez posle indeksnog vektora.

Napravili smo novi data.frame podaci_s koji je jednak data.frameu podaci2 sortiranom po varijabli FORMA (po rastućem redosledu).

Ovako sortiramo po dve varijable

podaci_s=podaci2[order(podaci2$FORMA, podaci2$INDEX),]
podaci_s

Po varijabli FORMA i po varijabli INDEX.

Promenićemo sortiranje za varijablu INDEX u opadajući redosled

podaci_s=podaci2[order(podaci2$FORMA, -rank(podaci2$INDEX)),]
podaci_s

Za FAKTORE se mora dodati -rank ispred naziva varijable u zagradama. Kod numeričkih dovoljan je - ispred imena varijable.

SLAJD 33

Referisanje elemenata tabela

Kolonama tabela se može pristupiti preko imena (ako ih imaju) ili preko numeričkih indeksa. Redovima se pristupa preko numeričkih indeksa.

podaci2[c(1:3)] # je isto što i 
podaci2[1:3] 

Prve tri kolone matrice podaci2.

Drugih 10 redova i prve tri kolone

podaci2[10:20, 1:3] 

Drugih 10 redova i prve tri kolone + 5. i 7. kolona

podaci2[10:20, c(1:3, 5, 7)]

Kada kolone ili redovi nisu jedan do drugog mora se napraviti vektor brojeva koristeći c(). Izvršite c(1:3, 5, 7) da vidite kako izgleda taj vektor. Podsećam, u uglastim zagradama pre zareza su indeksi redova, a posle zareza indeksi kolona. Ako izostavite indeks reda podrazumeva se da želite da ih uključite sve. Isto važi i za kolone.

SLAJD 34

Osnovni operatori u R-u

Aritmetički: +, -, /, *, ^

16^(1/2)# kvadratni koren – može i 
sqrt(16)
16^(1/4) # 4. koren iz 16
16^(-1) # recipročna vrednost - isto što i 1/16

Operatori odnosa (poređenja)

< (manje)

<= (nije veće)

>= (nije manje)

== (jednako) Nije greška, ako proveravate jednakost onda idu 2 znaka ==. Jedan znak = ekvivalentan je <-

!= (nije jednako)

Logički operatori

& (AND, i), | (OR, ili)

SLAJD 35

Još malo referisanja elemenata tabela

podaci2["pit2"] #naziv varijable kao indeks
podaci2[c("pit2", "pit5")]  #slovni vektor naziva varijabli kao indeks 

Ispitanici koji imaju podatak na varijabli pit2

subset(podaci2, podaci2$pit2!="NA")

Funkcijom subset() izdvojili smo sve kod kojih vrednost na varijabli pit2 nije jednaka NA (nedostajuća).

subset(podaci2, select=1:3)

Funkcijom subset izdvojili smo kolone od 1 do 3.

subset(podaci2, select=c(1:3, 5))

…kolone 1-3 i 5

subset(podaci2,  podaci2$pit3==3, select=c(1:3, 5))

…ispitanici koji su dali odgovor 3 na varijabli pit3, kolone 1-3 i 5

SLAJD 36

Selekcija varijabli i ispitanika

podaci2$SUMA #da pogledamo vrednosti na ovoj varijabli

Odabraćemo samo one ispitanike koji imaju podatak na varijabli SUMA (i smestićemo u novi data.frame)

podaci2a<-podaci2[is.na(podaci2$SUMA)!= TRUE,]
podaci2a$SUMA #da se uverimo da nema nedostajućih

Funkcija is.na() testira da li je nešto nedostajuća vrednost. U narednoj naredbi postoje dva uslova: 1. da je ispitanik odgovorio na pit1 i da na SUMA ima nedostajući podatak. Sve kolone takvih ispitanika kopirane su u objekat podaci5 (klase data.frame). Da vidite objekat (npr. podaci5, samo u konzoli ukucajte njegovo ime i pritisnite ENTER)

podaci5<-podaci2[!is.na(podaci2$pit1)==TRUE & is.na(podaci2$SUMA)==TRUE,]
podaci5

Svi sa skorom većim od 95 (sve kolone u objekat podaci2b)

podaci2b<-podaci2a[podaci2a$SUMA>95,]
podaci2b

Isto kao prethodno, ali su izabrane samo kolone INDEX i SUMA i kopiraju se u objekat podaci2c

podaci2c<-podaci2a[!podaci2a$SUMA>95,c("INDEX", "SUMA")]
podaci2c

SLAJD 37

ATTACH

Komanda attach(podaci2) smešta tabelu sa podacima na putanju pretrage R-a pa objektima koji se nalaze u njoj možemo pristupiti preko imena (bez “podaci2$”)

attach(podaci2)
INDEX #pozivanje varijable INDEX iz objekta podaci2
mode(INDEX)
SUMA

Vodite računa, ako pozovete varijablu “INDEX” bez naziva skupa podataka onda će je pozvati iz skupa podaci2, odnosno poslednjeg kojeg ste pridružili sa ATTACH

attach(podaci2a)
find("INDEX")  # vam javlja  odakle pozivate varijablu INDEX (prvi navedeni objekat)
detach(podaci2a) # uklanja "podaci2a" sa putanje
find("INDEX")  # vam javlja  odakle pozivate varijablu INDEX

SLAJD 38

UVOZ DATOTEKA

Paket foreign

library(foreign)

Ova biblioteka vam omogućava uvoz iz Minitab, S, SAS, SPSS, Stata, Systat i dBase formata. Funkcija za SPSS .sav format je read.spss

podaci3<-as.data.frame(read.spss("test4.sav", use.value.labels = TRUE))
podaci3

Argument use.value.labels = TRUE vam omogućava da uvezete labele za kategorijalne varijable (faktore)

SLAJD 39

REKODIRANJE

Koristićemo paket car

p_load(car)
podaci2$SUMA2<-Recode(podaci2$SUMA, "NA=0") # NA u 0 u novu varijablu SUMA2
provera<-subset(podaci2, select=c("SUMA", "SUMA2")) # izdvajanje dve varijable u novi objekat PROVERA
provera

Rekodiranje više vrednosti istovremeno (“reflektovanje” + NA u 0) u istu varijablu

podaci2$pit3<-Recode(podaci2$pit3, "NA=0; 1=5; 2=4; 4=2; 5=1")

Sažimanje u istu varijablu (više vrednosti u jednu + promena tipa u char(), jer smo zadali tekstualne vrednosti)

podaci2$pit3<-Recode(podaci2$pit3, "c(0, 1, 2)='nisko'; c(3, 4, 5)='visoko'") # 5 u 2 kategorije
podaci2$pit3

SLAJD 40

FAKTORI

library(foreign)
podaci4<-as.data.frame(read.spss("sredjivanje.sav", use.value.labels = TRUE)) #učitavanje podataka
table(podaci4$POL)  # kreiranje tabele vrednosti varijable pol
is.factor(podaci4$POL) # provera da li je varijabla faktor (nije - FALSE)
podaci4$POL<-factor(podaci4$POL, levels=c(1, 2), labels=c("muski", "zenski")) # pretvaranje u faktor sa nivoima muški i ženski. Navedeni nivoi (levels) i labele (labels).
podaci4$POL
is.factor(podaci4$POL)
table(podaci4$POL)
is.factor(podaci4$STA_KAT) # ponovo na varijabli STA_KAT (starost karatista)
table(podaci4$STA_KAT)
podaci4$STA_KAT<-factor(podaci4$STA_KAT, levels=c(1, 2, 3, 4), labels=c("nade", "kadeti", "juniori", "seniori"))
is.factor(podaci4$STA_KAT)
table(podaci4$STA_KAT)

SLAJD 41

DESKRIPTIVNA

mean(podaci4$OCENA, trim=0.05, na.rm=TRUE) 

Aritmetička sredina. Trim uklanja % najnižih i najviših vrednosti pre računanja AS, na.rm=TRUE izbacuje one sa nedostajućim vrednostima i računa AS. Ako je na.rm=FALSE, ako postoje oni sa nedostajućim vrednostima i AS je NA. Argument na.rm važi za sve sledeće funkcije osim za summary (radi na kompletnim podacima)

sd(podaci4$OCENA, na.rm=TRUE) #standardna devijacija
var(podaci4$OCENA) #varijansa
sd(podaci4$OCENA)^2 #rezultate većine funkcija možemo koristiti u daljem računu (ovo je samo primer - kada kvadriramo standardnu devijaciju dobijemo varijansu)
min(podaci4$OCENA) #minimum
max(podaci4$OCENA) #maximum
summary(podaci4$OCENA)

SLAJD 42

TABELE

table(podaci4$POL)
table(podaci4$STA_KAT)
table(podaci2$SUMA)
table(podaci2$SUMA, useNA="no") # useNA="no" - zanemaruje nedostajuće

Argument useNA: useNA=“ifany” - prikazuje se kolona za NA samo ako ih ima useNA=“always” - prikazuje se kolona NA uvek

table(podaci2$SUMA, useNA="ifany")
table(podaci2$SUMA, useNA="always")
table(podaci2$SUMA2, useNA="always")

Argument exclude isključuje navedene kategorije. Ako ih ima više navode se u slovnom vektoru, nivoi se stavljaju pod navodnicima, odvojeni zarezom. Npr. exclude=c(“kadeti”, “nade”)

table(podaci4$STA_KAT, exclude=c("kadeti"))

Tabele kontingencije

table(podaci4$POL, podaci4$STA_KAT)# dvosmerna 
table(podaci4$POL, podaci4$STA_KAT, podaci4$OCENA)# trosmerna

Prethodno se može kombinovati sa exclude, useNA…

SLAJD 43

TAPPLY

Primenjuje navedenu funkciju po nivoima faktora. Prvo se navodi željena varijabla, zatim faktor (grupišuća) i na kraju željena funkcija. Npr. u prvoj komandi analiziramo varijablu OCENA, po polu, a dobićemo aritmetičku sredinu. Argumenti željene funkcije mogu biti navedeni posle same funkcije, odvojeni zarezom (kao u drugoj komandi).

tapply(podaci4$OCENA, podaci4$POL, mean)# drugi argument je faktor
tapply(podaci4$OCENA, podaci4$POL, mean, trim=0.05)
tapply(podaci4$OCENA, podaci4$POL, sd)
tapply(podaci4$OCENA, podaci4$POL, var)
tapply(podaci4$OCENA, podaci4$POL, sd)^2
tapply(podaci4$OCENA, podaci4$POL, min)
tapply(podaci4$OCENA, podaci4$POL, max)
tapply(podaci4$OCENA, podaci4$POL, summary)

SLAJD 44

t-test za nezavisne uzorke

Prvo ćemo pozvati biblioteku car u kojoj se može naći funkcija za Leveneov test homogenosti varijansi (da bismo znali koji t test da primenimo).

library(car) # u ovoj biblioteci se nalazi funkcija
leveneTest(OCENA ~ POL, data=podaci4)# Leveneov test jednakosti varijansi

Pošto su varijanse homogene primenićemo varijantu za jednake varijanse

t.test(OCENA ~ POL, data=podaci4, var.equal=TRUE)

Ako varijanse nisu homogene oda se koristi argument var.equal=FALSE inače TRUE. Inače definisanje modela ZAVISNA ~ NEZAVISNA je uobičajeno za većinu (ako ne i sve) linearne modele u R-u.

t-test za zavisne uzorke

Dovoljno je vrednost argumenta repeated postaviti na TRUE

t.test(podaci4$T1_PIT1, podaci4$T1_PIT2, data=podaci4, repeated=TRUE)

Kod zavisnih uzoraka, merenja se navode jedno za drugim.

SLAJD 45

Hi-kvadrat test

Funkcija chisq.test kao argumente uzima dve varijable ili gotovu tabelu kontingencije kreiranu na osnovu njih.

chisq.test(podaci4$POL, podaci4$STA_KAT)  # ili
chisq.test(table(podaci4[1:2])) # drugačije indeksiranje varijabli (preko broja kolone) ili 
tabelica=table(podaci4[1:2])#prvo kreiramo tabelu
chisq.test(tabelica) #a zatim tabelu uvrstimo kao argument u funkciju

Rezultat ove funkcije (a i svih ostalih) možemo smestiti u objekat koji kasnije možemo pozivati. Dodati su argumenti correct koji označava da li je potrebno raditi Yatesovu korekciju (TRUE) ili ne (FALSE), simulate.p.value da li je potrebno raditi MonteCarlo simulacije ili ne, i B koliko simuliranih uzoraka je potrebno uraditi.

hi<-chisq.test(table(podaci4$POL, podaci4$STA_KAT), correct = FALSE, simulate.p.value = TRUE, B = 2000)
hi #pozivanjem objekta ispisujete rezultate
str(hi) # da vidite strukturu objekta hi

Kada pogledate strukturu vidite elemente objekta (ispred njih stoji znak $). Kasnije možete pozivati elemente tog objekta (hi) pojedinačno, smeštati ih u nove objekte, koristiti u daljim analizama… Ovo važi za sve objekte (skoro), ne samo one nastale na osnovu funkcije chi.square

hi$observed # prikazivanje opaženih frekvencija
hi$expected # očekivanih
hi$residuals # reziduala
hi$residuals[1] # reziduala samo za muške
hi$residuals[2] # reziduala samo za ženske
hi$stdres # standardizovanih reziduala
hi$statistic # vrednost hi kvadrata
hi$p.value # značajnost

SLAJD 46

chisq.test grafikoni

Stacked bar plot

plot(podaci4$POL, podaci4$STA_KAT)

Dodate su labele x ose (xlab), y ose (ylab) i naslov grafikona (main). Tekst mora ići pod navodnicima “” ili ’’. Boje su definisane argumentom col i navode se kao numerički vektor col=c(1, 3, 7, 8). Promenite brojeve i promenićete boje. Mogu se navoditi i kao tekstualni vektor. Npr. c(“red”, “dark blue”, “light green”, “orange”)

plot(podaci4$POL, podaci4$STA_KAT, xlab="pol", ylab="starost", col=c(1, 3, 7, 8), main= "starost karatista po polu")
plot(podaci4$POL, podaci4$STA_KAT, xlab="pol", ylab="starost", col=c("red", "dark blue", "light green", "orange"), main= "starost karatista po polu")

Ako navedete nepostojeću boju ili napravite grešku u kucanju dobićete sledeću poruku: Error in rect(xleft, ybottom, xright, ytop, col = col, …) : invalid color name ‘naziv boje kod koje ste pogrešili’

SLAJD 47

STANDARDIZACIJA

Standardizaciju varijable možete odraditi peške po sledećoj formuli (x-Xbar)/SD

podaci4$ZOCENA<-(podaci4$OCENA - mean(podaci4$OCENA))/sd(podaci4$OCENA) 
podaci4$ZOCENA

ili zbog jednostavnosti smestiti željenu varijablu u objekat x pa onda izvršiti standardizaciju

x<-podaci4$OCENA
podaci4$ZOCENA<-(x - mean(x))/sd(x)
podaci4$ZOCENA

…a u stvari postoji i funkcija koja to radi

podaci4$ZOCENA2<-scale(podaci4$OCENA, center=TRUE, scale=TRUE)
mean(podaci4$ZOCENA2)#AS
sd(podaci4$ZOCENA2)#SD

Ako je center=TRUE onda se x umanjuje za AS (radi se tzv. CENTRIRANJE). Praktično se AS postavlja na 0 (u stvari najčešće jako blisko 0). A ako je scale=TRUE onda se x-Xbar deli SD, ako ne - ništa. Ako je TRUE, onda se radi standardizacija (pod uslovom da je i center=TRUE) Argument center (za koliko umanjiti vrednost x-Xbar) i scale (sa čim podeliti) mogu biti numerički vektori ako se transformiše više varijabli. Može se transformisati i više varijabli odjednom

stand3_5 <-scale(podaci4[3:5], center=TRUE, scale=TRUE)
stand3_5

Gornjom komandom su od 3. do 5. varijable iz matrice podataka podaci4 standardizovani i smešteni u novi objekat klase data.frame pod nazivom stand3_5.

Test normalnosti

Koristimo Shapiro-Wilk test

shapiro.test(podaci4$OCENA)
hist(podaci4$OCENA)

Ako želimo po grupama

tapply(podaci4$OCENA, podaci4$POL, shapiro.test)

NORMALIZACIJA (navodno)

Na internetu se često kaže da se normalizacija može obaviti pomoću funkcije scale, na sledeći način

podaci4$NOCENA <-scale(podaci4$OCENA, center=TRUE, scale=FALSE) # ipak samo CENTRIRANJE
podaci4$NOCENA
hist(podaci4$NOCENA)

SLAJD 48

Normalizacija

Peške… Rangirati skorove i pretvoriti u percentile (u stvari nam ne trebaju procenti već proporcije). Zatim naći z vrednost kojoj odgovara određeni procenat ispod krive normalne raspodele. Prvo izračunamo rangove

podaci2$RANG<-rank(podaci2$SUMA, na.last = "keep", ties.method = "min")
podaci2$RANG

Jako je bitno da na.last bude “keep”. Na taj način slučajevi sa nedostajućim vrednostima na željenoj varijabli dobijaju nedostajući rang (NA) Zatim izračunamo percentilni rang. Podelimo rang iz gornje komande dužinom varijable (brojem slučajeva bez nedostajućih)

podaci2$PERC<-(rank(podaci2$SUMA, na.last = "keep", ties.method = "min"))/length(is.na(podaci2$SUMA)==FALSE) 
podaci2$PERC

I na kraju, proporcije pretvorimo u z skorove koristeći funkciju qnorm koja traži z vrednost koja odgovara određenoj proporciji ispod krive normalne raspodele

podaci2$NORM<-qnorm((rank(podaci2$SUMA, na.last = "keep", ties.method = "min"))/length(is.na(podaci2$SUMA)==FALSE)) 
podaci2$NORM

Primetite kako smo prethodne komande ugnjezdili jednu u drugu: qnorm((rank()/length)) Uradićemo i standardizaciju radi poređenja

podaci2$Z<-scale(podaci2$SUMA, center=TRUE, scale=TRUE)

SLAJD 49

NORMALIZACIJA

Uporedni grafikon sirove varijable, normalizovane i standardizovane

par(mfrow=c(1,3)); hist(podaci2$SUMA, main="sirovi", xlab=NULL, col=2); hist(podaci2$NORM, main="normalizovani", xlab=NULL, col=3); hist(podaci2$Z, main="z skorovi", xlab=NULL, col=4)

Crtamo grafikone da ih uporedimo. Vidimo da “najnormalnije” izgleda onaj koji smo dobili procedurom normalizacije koju smo napisali. Komande za crtanje su date u jednom redu, odeljene sa ;. Histograme crtamo funkcijom hist(). Naslove i boje smo definisali argumentima main, xlab, i col. Funkcija par definiše crtanje više grafikona odjednom. Tačnije to radi opcija mfrow koja prostor za crtanje grafikona deviniše kao matricu. c(1:3) znači da ćemo prostor za crtanje podeliti u 1 red, 3 kolone

Da biste prostor za crtanje grafikona vratili na normalan iskoristite funkcije

par(mfrow=c(1,1)) #ili
dev.off() 

par(mfrow=c(1,1)) podešava prostor za crtanje na 1 red i 1 kolonu, a dev.off() uklanja postojeći prostor za crtanje.

Proverićemo normalnost normalizovane varijable Shapiro-Wilk testom i ispisati njenu aritmetičku sredinu i standardnu devijaciju

shapiro.test(podaci2$NORM); mean(podaci2$NORM, na.rm=T); sd(podaci2$NORM,na.rm=T)

Normalna je (jedva)

SLAJD 50

NORMALIZACIJA

Može se smestiti u funkciju

normalizuj <- function(nenormalna) {
qnorm((rank(nenormalna, na.last = "keep", ties.method = "min"))/length(is.na(nenormalna)==FALSE))
}

Napravili smo objekat koji se zove normalizuj. On je klase “function” i uzima jedan argument “nenormalna”, a to je varijabla koju je potrebno normalizovati. Ono što će funkcija raditi nalazi se između vitičastih zagrada {} Dakle funkciju definišemo sa ime<-function(argumenti koje uzima) {komande koje izvršava}. Tako definisanu funkciju je potrebno jednom pokrenuti, a kasnije je pozivamo njenim imenom i navođenjem argumenata. Na primer

normalizuj(podaci2$SUMA) 
# odnosno
podaci2$NORM<-normalizuj(podaci2$SUMA)

Grafikoni ponovo

par(mfrow=c(1,3)); hist(podaci2$SUMA, main="sirovi", xlab=NULL, col=2); hist(podaci2$NORM, main="normalizovani", xlab=NULL, col=3); hist(podaci2$Z, main="z skorovi", xlab=NULL, col=4); 
par(mfrow=c(1,1)) 

SLAJD 51

ANOVA

Može se izvršiti na više načina i koristiti više funkcija. Na primer funkcija lm (linearni modeli)

analiza.var<-lm(OCENA ~ STA_KAT, data=podaci4) 

Ovo je jednosmerna analiza varijanse i format definisanja je: ZAVISNA ~ NEZAVISNA. Nezavisna mora biti faktor, a zavisna numerička. Argument data je obavezan i predstavlja matricu podataka koju koristite (ako su varijable deo data.frame objekta, a ne moraju biti - mogu biti i vektori). lm daje izlaz kao regresija (što je isti model).

summary(analiza.var)
analiza.var

Intercept je AS prve kategorije, a AS ostalih kategorija su intercept + parametar kategorije (4.55172+0.06567…) Proverite

tapply(podaci4$OCENA, podaci4$STA_KAT, mean)

Ili druga varijanta upotrebom funkcije aov

analiza.var<-aov(OCENA ~ STA_KAT, data=podaci4)
summary(analiza.var)
anova(analiza.var)

Višesmerna ANOVA

analiza.var2<-lm(OCENA ~ STA_KAT + POL + (POL*STA_KAT), data=podaci4)
summary(analiza.var2)
anova(analiza.var2)

Format: ZAVISNA ~ GLAVNI_EFEKAT1 + GLAVNI_EFEKAT2 + INTERAKCIJE Interakcija: Ako definišemo na sledeći način: GLAVNI_EFEKAT1 * GLAVNI_EFEKAT2 dobijaju se i sve interakcije i glavni efekti. U gornjem primeru to bi bilo

lm(OCENA ~ POL*STA_KAT, data=podaci4)

Ako definišemo na sledeći način: GLAVNI_EFEKAT1 : GLAVNI_EFEKAT2 dobijaju se samo interakcije, ali ne i glavni efekti. U gornjem primeru to bi bilo: lm(OCENA ~ POL:STA_KAT, data=podaci4). Tada se glavni efekti moraju eksplicitno zadati, a to znači da ne morate u model staviti sve glavne efekte. Na primer

lm(OCENA ~ POL+(POL:STA_KAT), data=podaci4)

SLAJD 52

Višesmerna ANOVA 2

Isto važi i za aov funkciju

analiza.var2<-aov(OCENA ~ STA_KAT + POL + (POL*STA_KAT), data=podaci4)
summary(analiza.var2)
analiza.var3<-aov(OCENA ~ POL*STA_KAT, data=podaci4)
# Operator * traži glavne efekte i interakcije
summary(analiza.var3)
analiza.var4<-aov(OCENA ~ POL:STA_KAT, data=podaci4)
# Operator : traži samo interakciju
summary(analiza.var4)
anova(analiza.var4)

Post-hoc testovi

TukeyHSD(analiza.var4)

Grafik

with(podaci4, interaction.plot(STA_KAT, POL, OCENA, col=2:3, lwd=2))

with služi da skratimo kucanje Moglo bi i interaction.plot(STA_KAT, POL, OCENA, ylim=c(4.4, 4.9), col=2:3, lwd=2), ali bi pre svake varijable trebalo kucati podaci4$ - ovako u with(podaci4, ) umetnemo funkciju i R zna da sve treba raditi na objektu podaci4 Argument lwd definiše debljinu linije (menjajte vrednosti da vidite efekat).

SLAJD 53

ANOVA – lm

Još malo post-hoc testova Prvo fitujemo model

analiza.var5<-lm(OCENA ~ STA_KAT + POL + (POL*STA_KAT), data=podaci4)
summary(analiza.var5)
anova(analiza.var5, test="F")

Zatim instaliramo i učitamo paket emmeans, i odradimo post.hoc testove. Prvi argument je objekat u kojem se nalazi fitovan model ANOVA. Zatim definišemo koja poređenja želimo i kojim metodom.

p_load(emmeans)
emmeans(analiza.var5, pairwise~STA_KAT*POL, adjust="scheffe") # primer je za sve efekte
emmeans(analiza.var5, pairwise~STA_KAT:POL, adjust="scheffe") # samo za interakciju
emmeans(analiza.var5, pairwise~STA_KAT, adjust="tukey") # ovde je za glavni efekat -  a korišćen je tukey

SLAJD 54

KORELACIJE

Funkcijom cor možemo dobiti matricu korelacija više varijabli

cor(podaci4[3:8]) # matrica korelacija
cor(podaci4$T1_PIT1, podaci4$T1_PIT3) # dve varijable
with(podaci4, cor(T1_PIT2, T1_PIT3, method="pearson")) #skraćeno pisanje

Method može biti i “spearman” i “kendall” Funkcijom cor.test dobijamo i značajnost korelacija, ali može se računati samo za dve varijable

cor.test(podaci4$T1_PIT1, podaci4$T1_PIT3) # dve varijable

Funkcija pod nazivom corr.test (ima jedno r više nego prethodna funkcija) iz paketa psych može to i na matricama

p_load(psych)
corr.test(podaci4[3:8])
print(corr.test(podaci4[3:8]), short = FALSE)

SLAJD 55

GRAFIKONI

hist(podaci4$OCENA)
hist(podaci2$SUMA)

Boja + naslov grafikona + labela X ose

hist(podaci2$SUMA, col=3, main="UKUPAN SKOR", xlab="SUMA")

Sve to + Y osa

hist(podaci2$SUMA, col=3, main="UKUPAN SKOR", xlab="SUMA", ylab="frekvencija") 

Boxplot i stem&leaf

boxplot(podaci2$SUMA)
stem(podaci2$SUMA)

Probajte da dodate nazive osa i promenite boju na boxplotu

Kada zadate komandu plot za dve varijable od kojih je prva faktor a druga numerička, dobićete dva boxplota

plot(podaci4$POL, podaci4$OCENA)

Ako obrnete redosled varijabli dobićete scatter dijagram

plot(podaci4$OCENA, podaci4$POL) 

Scattter je loš pošto je pol binarna, a ocena politomna sa max. 5 nivoa

plot(podaci4$OCENA, podaci4$POL, yaxp=c(1, 2, 1), xaxp=c(1,5,4))

yaxp i xaxp određuju minimum, maksimum i broj nivoa (tim redom) skala X i Y

Redosled varijabli u funkciji plot određuje tip grafikona, ako je X faktor onda dobijamo boxplotove, u protivnom scatter.

Ako se ovako definiše, redosled nije bitan (dobijamo boxplot).

plot(podaci4$OCENA ~ podaci4$POL)

SLAJD 56

ČUVANJE GRAFIKA U DATOTEKU

Za početak, kreiraćemo dve varijable (X i Y) izvučene iz normalne raspodele

X=rnorm(100, 30, 8); Y=rnorm(100, 25, 8) # kreiranje dve varijable iz normalne raspodele

Svaka sa po 100 ispitanika (prvi argument), X sa AS 30 (drugi argument) i SD 8 (treći argument), a Y sa AS 25 i SD 8 Funkcija rnorm pravi slučajnu varijablu iz normalne distribucije sa argumentima (n, AS i SD) Crtanje u png fajl

Dajemo naziv datoteke, širinu i visinu u pixelima. Zapisujemo u PNG - ne pojavljuje se prikaz pojavljuje se datoteka u radnom direktorijumu (pogledajte).

Crtanje u TIF fajl

tiff(filename = "skater1.tiff", width = 800, height = 800);plot(X, Y); 
dev.off()

Zapisujemo u TIFF - ne pojavljuje se prikaz KVALITET NIJE ZA ŠTAMPU

SLAJD 57

TIF 300*300

Argumentom res definišemo rezoluciju slike. 300 je dovoljno za štampu. Argumentom units definišemo merne jedinice u kojim su definisani visina i širina. cex.main, cex.lab i cex.axis definišu veličinu slova (glavnog naslova, labela i osa). 1 je normalna veličina. Vrednosti preko 1 znače uvećanje fonta, a manje od 1 umanjenje.

tiff(filename = "skater300.tif", width = 9, height = 9,units = "cm", pointsize = 6, bg = "white", res = 300, restoreConsole = TRUE); plot(X, Y, col=3, lwd = 1, lty=2, xlab = "varijabla X", ylab = "varijabla Y", main="skater 300", cex.main = 1.5, cex.lab = 1.3, cex.axis = 1.1); 
dev.off()

Sve komande moraju biti u jednoj liniji odvojene sa “;” ili u odvojenim linijama ali izvršene istovremeno (istovremeno sa dev.off()) Bitno je da res bude bar 300

SLAJD 58

Spajanje podataka (merge)

Spajanje dve tabele po ključnim varijablama. Učitaćemo dve matrice podataka

levi<-read.table("imena.txt", header=TRUE, sep=",", quote = "\"'", dec = ".")
desni<-read.table("odgovori2.txt", header=TRUE, sep=",", quote = "\"'", dec = ".")

Ove dve tabele dele varijablu INDEX koja identifikuje slučajeve i pomoću koje ih možemo spojiti koristeći komandu merge.

sve<-merge(levi, desni, by="INDEX")# spojiće dva seta podataka, ali samo one slučajeve koji se nalaze u obe matrice
sve
sve=merge(levi, desni, all=TRUE, by="INDEX")# spojiće dva seta podataka, a u podacima će se nalaziti i svi oni iz oba seta koji nemaju podatke u oba seta (nalaze se samo u jednoj od matrica) 
sve

Svi iz data.framea levi ulaze, čak i ako nemaju para u data.frame desni. Oni iz data.frame desni ulaze samo ako imaju para u levi.

sve<-merge(levi, desni, all.x=TRUE, by="INDEX")
sve

Obrnuta situacija

sve<-merge(levi, desni, all.y=TRUE, by="INDEX")
sve

SLAJD 59

LDF <- WDF

Promena iz Wide Data Formata u Long Data Format i obrnuto. Kada imamo ponovljena merenja, podaci mogu biti zapisani u Wide data formatu (svako merenje je posebna varijabla) ili u Long data formatu (sva merenja su u jednoj varijabli, ali postoji varijabla koja identifikuje merenje). Ovo je bitno zato što smo navikli da podatke zapisujemo u Wide data formatu, a mnoge analize sa ponovljenim merenjima u R-u zahtevaju Long data format. Učitaćemo podatke u Wide formatu

podaci.wide <- read.table(header=T, text='
ispitanik pol merenje1 merenje2 merenje3
1    M      7.9   12.3     10.7
2    Z      6.3   10.6     11.1
3    Z      9.5   13.1     13.8
4    M     11.5   13.4     12.9
')

Kolona sa subjektima (identifikacijom subjekta) mora biti tipa “factor”

podaci.wide$ispitanik <- factor(podaci.wide$ispitanik)

Koristićemo paket reshape

p_load(reshape)

Podatke ćemo pretvoriti u Long format i smestiti u objekat novipodaci.long

novipodaci.long<-reshape(podaci.wide, direction='long', varying=c('merenje1', 'merenje2', 'merenje3'), timevar='merenje', times=c('1', '2', '3'), v.names=c('stasmomerili'), idvar=c("ispitanik", "pol"))
novipodaci.long

Argumenti: Prvi argument su podaci koje želimo da pretvorimo direction: da li pretvaramo u long ili wide varying: varijable u kojima su ponovljena merenja id.vars: varijable za identifikaciju ispitanika - ne menjaju se timevar: varijabla u kojoj će biti zapisan redni broj merenja (vi definišete željeno ime) v.names: kako da nazove novu varijablu ili varijable (vi definišete ime - u toj varijabli se nalaze vrednosti merenja) times: čime su u nazivima varijabli označena merenja (imena varijabli sa ponovljenim merenjima moraju imati nepromenljiv deo i deo koji označava merenje). U primeru nepromenljivi deo je “merenje”, a promenljivi je označen brojem (mada može i drugačije)

SLAJD 60

LDF -> WDF

Vratićemo prethodno kreirane podatke (novipodaci.long) u Wide data format.

novipodaci.wide<-reshape(novipodaci.long, direction="wide", varying="stasmomerili") 

Argumenti: direction: u kom smeru menjamo strukturu matrice (želimo da postane wide). varying: koje varijable treba razbiti na više varijabli. Može ih biti i više, ali onda je potrebno definisati slovni vektor sa imenima varijabli c(“stasmomerili”, “drugavarijabla”, “trecavarijabla”) Još jedan primer

#učitavanje podataka
podaci.long <- as.data.frame(read.table(header=T, text='
ispitanik  pol     merenje        mera
1   M     merenje1         7.9
1   M     merenje2        12.3
1   M     merenje3        10.7
2   Z     merenje1         6.3
2   Z     merenje2        10.6
3   Z     merenje1         9.5
3   Z     merenje2        13.1
3   Z     merenje3        13.8
4   M     merenje1        11.5
4   M     merenje2        13.4
4   M     merenje3        12.9
'))

Kolona sa subjektima mora biti tipa “factor”.

podaci.long$ispitanik <- factor(podaci.long$ispitanik)
novipodaci.wide<-reshape(podaci.long, direction = "wide", v.names= c("mera"), timevar="merenje", idvar=c("ispitanik", "pol"))

Argumenti: direction: da li pretvaramo u long ili wide, idvar: su varijable za identifikaciju ispitanika - ne menjaju se po merenjima timevar: naziv varijabli sa posebnim merenjima (R dodaje broj za svako merenje) v.names: su nazivi varijabli koje se menjaju. Ako se radi o jednoj varijabli po argumentu može i bez c() kao kod timevar=, ali navodnici moraju

SLAJD 61

Iz WIDE u LONG

Još jedna mogućnost koristi paket reshape2

library(reshape2)

id.vars: varijable koje treba da ostanu ali ne i da budu deljene po merenjima (neponovljena merenja)

novipodaci.long <- melt(podaci.wide, id.vars=c("ispitanik","pol"), measure.vars=c("merenje1", "merenje2", "merenje3" ), variable_name="merenje")

Argumenti: measure.vars: varijable u kojima se nalaze različita merenja id.vars: nepromenljive varijable koje služe za jedinstvenu identifikaciju ispitanika variable_name: kako ćemo nazvati novu varijablu

SLAJD 62

Iz WIDE u LONG

Još jedan način učitavanja podataka (direktno iz komande, polja odvojena separatorom ;)

podaci2.wide <- read.table(header=T, sep=";", text='
ispitanik;pol;merenje1;merenje2;merenje3;neponovljeno
1;M;7.9;12.3;10.7;1.2
2;Z;6.3;10.6;11.1;2.2
3;Z;9.5;13.1;13.8;3.2
4;M;11.5;13.4;12.9;4.2'
)

Transformacija upotrebom funkcije melt() iz paketa reshape2. Nije specifikovana varijabla “neponovljeno” pa će biti ispuštena

novipodaci2.long <- melt(podaci2.wide, id.vars=c("ispitanik","pol"), measure.vars=c("merenje1", "merenje2", "merenje3" ), variable_name="merenje")
head(novipodaci2.long)

Kada je specifikovana biva zadržana

novipodaci3.long <- melt(podaci2.wide, id.vars=c("ispitanik","pol", "neponovljeno"), measure.vars=c("merenje1", "merenje2", "merenje3" ), variable.name="merenje")
head(novipodaci3.long)

SLAJD 63

Vraćanje u WIDE

Transformacija funkcijom dcast() iz paketa maditr.

p_load(maditr)
novipodaci3.wide<-dcast(novipodaci3.long, ispitanik + pol ~ merenje, value.var="value")
# Nije specifikovana varijabla neponovljeno pa je izbačena
head(novipodaci3.wide)

Argumenti: Prvi je data.frame koji transformišemo Zatim sledi formula u obliku neponovljene (sa + između) ~ varijabla koja identifikuje merenje) value.var: varijabla ili više njih koje sadrže ponovljena merenja. Ako ih je više, navesti kao slovni vektor c(“v1”, “v2”, “v3”).

# Specifikovana varijabla neponovljeno pa je ušla u data.frame
novipodaci3a.wide=dcast(novipodaci3.long, ispitanik + pol + neponovljeno ~ merenje, value.var="value")
head(novipodaci3a.wide)

KOREKCIJA ZA ATENUACIJU

Korekcija za atenuaciju Naziv funkcije (objekat) kojem pridružujemo funkciju i potrebne argumente Naredbe su uokvirene vitičastim zagradama

aten <- function(kor, rtt1=1, rtt2=1) {
  if (kor<(-1) | kor>1 | !is.numeric(kor)) {
      stop("Neispravna vrednost korelacije!")
  }
  if (rtt1<0 | rtt1>1 | !is.numeric(rtt1) | rtt2< 0 | rtt2>1 | !is.numeric(rtt2)) {
    stop("Neispravna vrednost pouzdanosti!")
    }
  cat("Korelacija sa korekcijom za atenuaciju iznosi:", round(kor/sqrt(rtt1*rtt2), 3))
}

Pozivanje funkcije

aten(0.2, 0.62, 0.7) #prvi argument je korelacija, drugi pouzdanost prvog, a treći pouzdanost drugog testa.

Funkcija cat() služi za ispisivanje teksta. Spaja argumente odvojene zarezima u tekstualni niz koji se ispisuje na ekranu. Argumenti funkcije cat() mogu biti i numerički. Možete je koristiti za pisanje naslova u ispisu i na grafikonima. Prelom reda u okviru funkcije se radi sa Tekstualni deo mora biti pod navodnicima. Funkcija round() zaokružuje broj na određeni broj decimala. Argumenti su joj broj koji želimo da zaokružimo i željeni broj decimala.

PETLJA FOR

Ponavlja određenu komandu (funkciju) dok su zadovoljeni uslovi. U narednom slučaju to radi dok je brojač (i) u opsegu od 1 do 10. Inače, brojač ne mora počinjati od 1 (npr. for (i in 15:20)) niti mora biti brojčani.

for (i in 1:10) {
 podaci2[,i]=as.numeric(podaci2[,i])
 }

Petlja for ponavlja naredbu sve dok je i u zadatom okviru Svakim ponavljanjem i postaje za 1 veći U ovom slučaju pretvara prvih 10 varijabli data.frame podaci2 u numerički tip. U ovom slučaju varijable su kolone data.framea jer je u indeksu [,] i posle zareza pa označava kolonu.

SLAJD 71

Lavaan

Paket za modelovanje strukturalnih jednačina

p_load(lavaan)

Učitavanje podataka

bfs<-read.table("bfd500o.dat", header = T)

Kreiranje modela (dvofaktorski sa koreliranim faktorima)

model1='F1=~V1+V2+V3+V4+V5 
F2=~V6+V7+V8+V9+V10 
F1~~F2'

Značenje prethodne sintakse: F1=~V1+V2+V3+V4+V5 Faktor 1 meren preko varijabli V1, V2, V3, V4 i V5 (nazivi varijabli iz data.framea bfs) F2=~V6+V7+V8+V9+V10 Faktor 2 meren preko varijabli V6, V7, V8, V9 i V10 (nazivi varijabli iz data.framea bfs F1~~F2 dozvoljeno je da Faktor 1 i Faktor 2 koreliraju

Fitovanje modela (konfirmatorna faktorska analiza). Osnovno. Više ćete raditi kasnije. Fitujemo model funkcijom cfa i smeštamo ga u objekat m1. Dovoljni argumentii su nazivi gore kreiranog modela i matrice podataka.

m1 <- cfa(model1, data=bfs)
summary(m1, fit.measures=TRUE) #osnovne informacije o modelu i pokazatelji fita
standardizedsolution(m1) #standardizovano rešenje
modindices(m1) #indeksi modifikacije

Ili bifaktorski model…

model2 <- 'G=~V1+V2+V3+V4+V5+V6+V7+V8+V9+V10; S1=~V1+V2+V3+V4+V5; S2=~V6+V7+V8+V9+V10;  S1~~0*S2; G~~0*S1; G~~0*S2'

G - generalni faktor (sve varijable) S1 - prvi specifični faktor (prvih 5 varijabli) S2 - drugi specifični faktor (drugih 5 varijabli) U poslednja tri izraza operator ~~ označava kovarijansu varijabli (u ovom slučaju faktora) Premultiplikacija 0* ograničava kovarijanse varijabli na 0 (moraju biti 0 - generalni faktor ne sme korelirati sa specifičnim, a ni specifični međusobno)

Fitovanje modela

m2<-cfa(model2, bfs, estimator="WLSMV", std.lv = TRUE)
summary(m2, fit.measures=TRUE) #osnovne informacije o modelu i pokazatelji fita
standardizedsolution(m2) #standardizovano rešenje
modindices(m2) #indeksi modifikacije

Argumenti funkcije cfa: estimator - koji estimator se koristi std.lv - da li da standardizuje latentne varijable

Sintaksa lavaan modela

Tip formule Operator Primer Uzročnost Značenje

definicija latentna uzrokuje ili latentne varijable =~ F1=~x1+x2 F1->x1 i x2 je merena pomoću —————————————————————————– uzrokovana je, regresija ~ y~x1+x2 x1+x2-> regresirana na —————————————————————————– (ko)varijansa slobodna (rezidualna) ~~ xy, xx x<->y, x<->x (ko)varijansa —————————————————————————– odsečak x~1 proceni odsečak x —————————————————————————– novi parametar u novi parametar := u:=a+b jednak a+b —————————————————————————– startna vrednost se može startne vrednosti start() y~start(0.2)x menjati tokom iteracija —————————————————————————– fiksirane vrednosti y~1x ne menja se i jednak je 1 —————————————————————————– slobodna vrednost y~NA*x slobodan je —————————————————————————–

ČUVANJE KREIRANIH OBJEKATA NA DISKU

Komandom save.image(“Analize.Rdata”) zapisaćete sve kreirane objekte na hard disk, u radni folder (osim ako ne navedete punu putanju).

save.image("Analize.Rdata")

Možete ih ponovo učitati sa

load("Analize.Rdata")

Prethodno je potrebno da definišete radni folder, ili da navedete punu putanju do datoteke (zajedno sa imenom datoteke, u okviru navodnika npr. load(“D:/OTSS(R)/Analize.Rdata”))

LS0tDQp0aXRsZTogIk9kYWJyYW5lIHRlbWUgaXogc2F2cmVtZW5lIHN0YXRpc3Rpa2UgLSBSIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAga2VlcF9tZDogeWVzDQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KZWRpdG9yX29wdGlvbnM6DQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KI3JlcXVpcmUoImtuaXRyIikNCiMjIHNldHRpbmcgd29ya2luZyBkaXJlY3RvcnkNCm9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSAiRDovT1RTUyhSKS8iKQ0KDQpgYGANCg0KRGEgYmlzdGUgbW9nbGkgZGEga29yaXN0aXRlIG92dSBkYXRvdGVrdSBwb3RyZWJubyBqZSBkYSBpbWF0ZSBpbnN0YWxpcmFuIFIuIE1vxb5ldGUgZ2EgcHJldXpldGkgc2EgbGlua2E6IGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL2Jpbi93aW5kb3dzLyAoemEgV2luZG93cyksIGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL2Jpbi9saW51eC8gKHphIExpbnV4KSBpbGkgaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvYmluL21hY29zeC8gKHphIE9TIFgpLg0KTmlzYW0gc2lndXJhbiBkYSDEh2Ugc2tyaXB0b3ZpIHJhZGl0aSBuYSBMaW51eHUgaSBPUyBYIChvbmFqIGRlbyBrb2ppIHNlIG9kbm9zaSBuYSByZWZlcmlzYW5qZSBsb2thY2lqYSBkYXRvdGVrYSBuYSBkaXNrdSAtIHRvIG1vcmF0ZSBwcmlsYWdvZGl0aSB1a29saWtvIGtvcmlzdGl0ZSBvdmUgb3BlcmF0aXZuZSBzaXN0ZW1lKS4NClRha2/EkWUsIHBvdHJlYm5vIGplIGRhIGltYXRlIGluc3RhbGlyYW4gaSBSU3R1ZGlvIChiZXNwbGF0bnUgdmVyemlqdSkgc2EgbGlua2EgaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcHJvZHVjdHMvcnN0dWRpby9kb3dubG9hZC8NClphdGltLCBwb3RyZWJubyBqZSBkYSBrcmVpcmF0ZSBmb2xkZXIgT1RTUyhSKSBuYSBkaXNrdSBDIGlsaSBEIGkgdSBuamVnYSBrb3BpcmF0ZSBwcmlsb8W+ZW5lIGRhdG90ZWtlIGl6IGFyaGl2ZSAobmFyZWRiZS5yLCB0ZXN0Mi50eHQsIHJlenVsdGF0aS5jc3YuLi4pIEtvcGlyYWp0ZSBkaXJla3RubyB1IHRhaiBmb2xkZXIgLSBuZSB1IHN1YmZvbGRlciENCktvbWFuZGUgaXp2csWhYXZhanRlIHJlZG9tLCBrbGlrb20gbmEgemVsZW5pIHRyb3VnYW8gc3Zha29nIG9kZWxqa2EgKGNodW5rIC0ga29qaSBwb8SNaW5qZSBzYSAiYGBge3J9IikuDQpBa28gxb5lbGl0ZSBkYSBpenZyxaFpdGUgcG9qZWRpbmHEjW51IG5hcmVkYnUgaXogb2RlbGprYSwga2xpa25pdGUgdSBuanUgKG5lIHNtZXRlIHNlbGVrdG92YXRpIG5pxaF0YSBpbGkgbW9yYXRlIHNlbGVrdG92YXRpIGNlbHUgbmFyZWRidSkgaSBwcml0aXNuaXRlIEN0cmwrRU5URVIuDQpOYXJlZGJlIGl6IFNDUklQVCBwcm96b3JhIG1vxb5ldGUga29waXJhdGkgdSBwcm96b3IgQ09OU09MRSBpIGl6dnLFoWF2YXRpIHNhIEVOVEVSLiBNb8W+ZXRlIGloIG1lbmphdGkgaSB2aWRldGkgcmV6dWx0YXRlLiBQb8W+ZWxqbm8gamUgZGEgdG8gcmFkaXRlLiBNZcSRdXRpbSwgcHJlZGxhxb5lbSBkYSBza3JpcHQgcHJ2aSBwdXQgcHJvxJFldGUgdSBpenZvcm5vbSBvYmxpa3UuDQpOYXBvbWVudW8gYmloIGRhIFIga29yaXN0aSBkdmUgdnJzdGUgbmF2b2RuaWthICIgaSAnLiBBa28gdSBrb21hbmRhbWEgaW1hdGUgYmlsbyBrb2ppIGRydWdpIGphdmnEh2UgdmFtIGdyZcWha3UuIE5lIHphYm9yYXZpdGUgZGEgc3Rhdml0ZSBuYXZvZG5pa2UgaSB6YXJlemUgdGFtbyBnZGUgdHJlYmEuIFRvIHZybG8gxI1lc3RlIGdyZcWha2UuDQpLYWRhIHNtbyB2ZcSHIGtvZCBncmXFoWFrYSwgdWtvbGlrbyBuZSB6bmF0ZSB6YcWhdG8gc3Ugc2UgamF2aWxlIGtvcmlzbm8gamUgZGEgaWgga29waXJhdGUgdSBicm93c2VyIGkgcG90cmHFvml0ZSB0YW1vIMWhdGEgamUgdXpyb2sgZ3JlxaFrZS4gU3ZpIG1pIHRvIHJhZGltbyA6KQ0KDQojIyMgU0xBSkQgOCAjIyMNCiMjIFBha2V0aQ0KUGFrZXRpIHN1IGJpYmxpb3Rla2UgZnVua2NpamEuDQpJbnN0YWxpcmF0ZSBpIHBveml2YXRlIHNhbW8gb25lIHBha2V0ZSBrb2ppIHZhbSB0cmViYWp1IOKAkyBucHIuDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQppbnN0YWxsLnBhY2thZ2VzKCJmb3JlaWduIikNCjIrNQ0KDQpgYGANCg0KSW5zdGFsaXJhbmplIHBha2V0YSBwYWNtYW4NCkluc3RhbGFjaWphIHNlIHJhZGkgc2FtbyBqZWRub20gbmEgamVkbm9tIHJhxI11bmFydSAob3NpbSBha28gaWggbmUgb2JyacWhZXRlKS4NCiANCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQppbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQ0KYGBgDQoNCiMjIFBveml2YW5qZSAodcSNaXRhdmFuamUpIGJpYmxpb3Rla2UNCg0KYGBge3J9DQpsaWJyYXJ5KGZvcmVpZ24pICMgaWxpIA0KcmVxdWlyZShmb3JlaWduKQ0KbGlicmFyeShwYWNtYW4pDQpgYGANCg0KDQpMYWvFoWUgamUgb3Zha28gDQoNCmBgYHtyfQ0KcF9sb2FkKGZvcmVpZ24pDQpgYGANCg0KRnVua2NpamEgcF9sb2FkIGl6IHBha2V0YSAicGFjbWFuIiBwcm92ZXJhdmEgZGEgbGkgamUgcGFrZXQgKGJpYmxpb3Rla2EpIGluc3RhbGlyYW4sIGFrbyBuaWplIGluc3RhbGlyYSBnYSBpIHXEjWl0YXZhLCBhIGFrbyBqZXN0ZSBzYW1vIGdhIHXEjWl0YS4NClByZXRob2RubyBtb3JhIGJpdGkgdcSNaXRhbmEgYmlibGlvdGVrYSBwYWNtYW4gKHNhbW8gamVkbm9tIG90a2FkYSBwb2tyZW5ldGUgUikNCg0KIyMjIFNMQUpEIDkgIyMjDQpQcm9iYWp0ZS4uLiANCmBgYHtyfQ0KbGlicmFyeShGb3JlaWduKQ0KYGBgDQpEb2JpxIdldGUgcG9ydWt1Og0KRXJyb3IgaW4gbGlicmFyeShGb3JlaWduKSA6IHRoZXJlIGlzIG5vIHBhY2thZ2UgY2FsbGVkIOKAmEZvcmVpZ27igJkuDQpQb3J1a3Ugc3RlIGRvYmlsaSBqZXIgc3RlIEZvcmVpZ24gbmFwaXNhbGkgc2EgdmVsaWtpbSBwb8SNZXRuaW0gc2xvdm9tLg0KUiBqZSBvc2V0bGppdiBuYSB2ZWxpa2EgaSBtYWxhIHNsb3ZhLiAgDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGZvcmVpZ24pDQojIG92byBqZSBwcm/FoWxvDQojIGluYcSNZSB6bmFrICIjIiBuYSBwb8SNZXRrdSBsaW5pamUgb3puYcSNYXZhIGtvbWVudGFyIChuZSBpenZyxaFhdmEgc2UpDQpgYGANCg0KIyMjIFNMQUpEIDEwICMjIw0KIyMgUG9tb8SHDQpaYSBwb21vxIcgdSB2ZXppIHNhIG5la29tIGZ1bmtjaWpvbSBrb2pvaiB6bmF0ZSBpbWUNCj9mdW5rY2lqYSBpbGkNCmhlbHAoZnVua2NpamEpDQpBa28gdHJhxb5pdGUgbmVrZSBwb2ptb3ZlIChrb2ppIG5pc3UgZnVua2NpamUpIG9uZGEgxIdldGUgaGVscCBkb2JpdGkgc2ENCmhlbHAuc2VhcmNoKCJwb2phbSBrb2ppIG1lIHphbmltYSIpDQpJbGkgPz8icG9qYW0ga29qaSBtZSB6YW5pbWEiIA0KDQpgYGB7cn0NCmhlbHAobGlicmFyeSkNCj9yZWFkLnNwc3MNCj8/cmVhZC5zcHNzDQpoZWxwLnNlYXJjaCgiY2hpIHNxdWFyZSIpDQpgYGANCg0KIyMjIFNMQUpEIDExICMjIw0KIyMgTkFSRURCRQ0KUiBtb8W+ZXRlIGtvcmlzdGl0aSBpIGthbyBrYWxrdWxhdG9yDQoNCmBgYHtyfQ0KMl4zDQpzcXJ0KDkpDQoyKzINCmBgYA0KQnJvamV2aSB1IHVnbGFzdGltIHphZ3JhZGFtYSBbIF0gc3UgaW5kZWtzaSBpIG96bmHEjWF2YWp1IHBvemljaWp1IGVsZW1lbnRhIHUgbml6dS4gT3ZkZSBzZSBuaXpvdmkgc2FzdG9qZSBvZCBqZWRub2cgYnJvamEgcGEgamUgbG9nacSNbm8gZGEgc3UgdG8gcHJ2aSBlbGVtZW50aS4NCg0KIyMjIFNMQUpEIDEyICMjIw0KIyMgUG9ub3ZubyBwb3ppdmFuamUgaSBpc3ByYXZsamFuamUgbmFyZWRiaQ0KU3RyZWxpY29tIG5hIGdvcmUgKG5hIHRhc3RhdHVyaSkgcG96aXZhdGUgbmFyZWRidSBwb25vdm8uDQpTdHJlbGljb20gbmEgbGV2byBkb2xheml0ZSBkbyBtZXN0YSBrb2plIMW+ZWxpdGUgZGEgaXNwcmF2aXRlLCBicmnFoWV0ZSBzYSBCQUNLU1BBQ0UgKGxldm8gb2Qga3Vyc29yYSkgaWxpIHNhIERFTCAoZGVzbm8gb2Qga3Vyc29yYSksIHphdGltIHVrdWNhdGUgbm92dSB2cmVkbm9zdCBpIHNhIEVOVEVSIGl6dnLFoWl0ZSBuYXJlZGJ1Lg0KQWtvIG5hcmVkYmEgbmlqZSBrb21wbGV0bmEgcG9qYXZpxIdlIHNlICIrIg0KDQpgYGB7cn0NCjJeDQo0DQpgYGANClUgZ29ybmplbSBvZGVsamt1IHRvIG5pc3RlIG1vZ2xpIHZpZGV0aSBqZXIgc3UgaXp2csWhZW5hIG9iYSByZWRhIChuYXJlZGJlKS4gVSBrb256b2x1IHVuZXNpdGUgcHJ2byBzYW1vIDJeIGkgcHJpdGlzbml0ZSBFTlRFUi4gUG9qYXZpxIdlIHNlIHpuYWsgKyBrb2ppIG96bmHEjWF2YSBkYSBqZSBwb3RyZWJubyBkYSBrb21wbGV0aXJhdGUgbmFyZWRidS4gRG9kYWp0ZSA0IGkgcHJpdGlzbml0ZSBFTlRFUi4gSXNwaXNhxIdlIHJlenVsdGF0IG5hcmVkYmUgMiBuYSA0Lg0KDQojIyMgU0xBSkQgMTMgIyMjDQojIyBSYWRuaSBkaXJla3RvcmlqdW0vZm9sZGVyDQpgYGB7cn0NCmdldHdkKCkNCmBgYA0K4oCmamUgbmFyZWRiYSBrb2pvbSBzYXpuYWpldGUga29qaSB2YW0gamUgcmFkbmkgZGlyZWt0b3JpanVtIChvZG5vc25vIG9uYWogdSBrb2plbSBSIHRyYcW+aSBpbGkgcGnFoWUgZGF0b3Rla2UgYWtvIG5lIG5hdmVkZXRlIHB1bnUgcHV0YW5qdSkuDQpBa28gxb5lbGl0ZSBkYSBnYSBwcm9tZW5pdGUsIGtvcmlzdGl0ZSBzZXR3ZCgixb5lbGplbmkgZGlyZWt0b3JpanVtIikNCg0KYGBge3J9DQpzZXR3ZCgiRDovT1RTUyhSKSIpDQpgYGANCg0KTmFwb21lbmE6IERpcmVrdG9yaWp1bSAoZm9sZGVyKSBtb3JhIHBvc3RvamF0aS4gS29yaXN0aXRpIC8gdW1lc3RvIHVvYmnEjWFqZW5vZyAiIFwgIi4gTW/FvmUgaSAiXFwiLg0KWmEgbmFzdGF2YWsgcmFkYSBwb3RyZWJubyBqZSBkYSBrb21hbmR1IHNldHdkKCJEOi9PVFNTKFIpIikga29waXJhdGUgdSBrb256b2x1IGkgaXp2csWhaXRlIGplciB6Ym9nIHNwZWNpZmnEjW5vZyBmb3JtYXRhIG92ZSBkYXRvdGVrZSBuZcSHZSBiaXRpIHphcGFtxIdlbmEgaXp2YW4gb2RlbGprYSAoY2h1bmspLg0KDQojIyMgU0xBSkQgMTQgIyMjDQojIyBQb3ppdmFuamUgbmFyZWRiaSBpeiBkYXRvdGVrZQ0KTmFyZWRiZSBtb8W+ZXRlIHNhxI11dmF0aSB1IHRla3N0dWFsbm9qIGRhdG90ZWNpLCBhIGl6IG5qZSBpaCBtb8W+ZXRlIHBvenZhdGkga29tYW5kb20gc291cmNlIA0KDQpgYGB7ciBlY2hvPVRSVUV9DQpzb3VyY2UoIkQ6L09UU1MoUikvbmFyZWRiZS5SIikNCmBgYA0KDQpJbGkgYWtvIHN0ZSBwb2Rlc2lsaSByYWRuaSBkaXJla3RvcmlqdW0NCg0KYGBge3J9DQpzb3VyY2UoIm5hcmVkYmUuUiIpDQpgYGANCg0KQWtvIGhvxIdldGUgZGEgdmFtIHJlenVsdGF0IGJ1ZGUgaXNwaXNhbiBuYSBla3JhbnUgb25kYSwgZG9kYWp0ZSBhcmd1bWVudCAiZWNobz1UUlVFIi4gDQpQcm92ZXJpdGUgdSBmb2xkZXJ1ICJEOi9PVFNTKFIpIGRhdG90ZWt1IG5hcmVkYmUuUiAtIG1vxb5ldGUgamUgb3R2b3JpdGkgc2EgTm90ZXBhZG9tIGlsaSBiaWxvIGtvamltIGVkaXRvcm9tIHRla3N0YS4gTW/FvmV0ZSBqZSBvdHZvcml0aSBpIHUgUi11LiBVIG5qb2ogc2UgbmFsYXplIGtvbWFuZGUgMyoyIChtbm/FvmVuamUgZHZhIGJyb2phKSwgc3FydCgxNDQpIChrdmFkcmF0bmkga29yZW4gaXogMTQ0KSBpIGFycmF5KDE6MTgwLCAxODApIChuYXByYXZpIG5peiBicm9qZXZhIG9kIDEgZG8gMTgwIHNhIDE4MCDEjWxhbm92YSkNCg0KYGBge3J9DQpzb3VyY2UoIm5hcmVkYmUuUiIsIGVjaG89VFJVRSkNCmBgYA0KVFJVRSBpIEZBTFNFIHNlIHNrcmHEh2VubyBtb2d1IHBpc2F0aSBUIGkgRg0KbnByLg0KDQpgYGB7cn0NCnNvdXJjZSgibmFyZWRiZS5SIiwgZWNobz1UKQ0KYGBgDQoNClBvbW/EhyB1IHZlemkgc2Egb3ZvbSBmdW5rY2lqb20NCg0KYGBge3J9DQo/c291cmNlDQpgYGANCg0KDQojIyMgU0xBSkQgMTUgIyMjDQojIyBJc3BpcyByZXp1bHRhdGEgdSBkYXRvdGVrdQ0KQWtvIMW+ZWxpdGUgZGEgdmFtIHN2aSByZXp1bHRhdGkgYnVkdSB1cGlzYW5pIHUgZGF0b3Rla3Uga29yaXN0aXRlIG5hcmVkYnUgc2luaygibmF6aXZfZGF0b3Rla2UiKQ0KDQpgYGB7cn0NCnNpbmsoIml6bGF6LnR4dCIpIA0KYGBgDQoNClVtZXN0byAiaXpsYXoudHh0IiBtb8W+ZXRlIGRhdGkgYmlsbyBrb2plIGltZS4NCkRhdG90ZWthIMSHZSBiaXRpIGtyZWlyYW5hIHUgcmFkbm9tIGRpcmVrdG9yaWp1bXUuDQpNb8W+ZSBpIG5hIGRydWdvbSBtZXN0dSwgYWxpIG1vcmF0ZSBuYXZlc3RpIHB1bnUgcHV0YW5qdSBkbyB0b2cgbWVzdGEuDQpOZcSHZSBiaXRpIGlzcGlzYSBuYSBla3JhbnUuDQpBa28gxb5lbGl0ZSBkYSBuYXN0YXZpdGUgcGlzYW5qZSB1IGRhdG90ZWt1IHUga29qdSBzdGUgdmXEhyBwaXNhbGkgb25kYSBkb2RhanRlIGFyZ3VtZW50ICJhcHBlbmQ9VFJVRSINCg0KYGBge3J9DQpzaW5rKCJpemxhei50eHQiLCBhcHBlbmQ9VFJVRSkNCnNvdXJjZSgibmFyZWRiZS5SIiwgZWNobz1UKQ0KYGBgDQoNCkJleiBhcHBlbmQ9VFJVRSBwcmVnYXppxIdlIHN0YXJ1IGRhdG90ZWt1Lg0KDQpQcm92ZXJpdGUgZGEgbGkgdSByYWRub20gZm9sZGVydSBzYWQgaW1hdGUgZGF0b3Rla3UgIml6bGF6LnR4dCIuDQpWaWRpdGUgxaF0YSBqZSB1IG5qb2ouDQoNCklwaXMgdSBkYXRvdGVrdSBwcmVraWRhIHNlIHNhIA0KDQpgYGB7cn0NCnNpbmsoKSMgbmVrYWRhIG1vcmEgMiBwdXRhIGRhIHNlIHBva3JlbmUNCnNpbmsoKQ0KYGBgDQoNCiMjIyBTTEFKRCAxNiAjIyMNCg0KIyMgT0JKRUtUSQ0KDQpSIHJhZGkgc2Egb2JqZWt0aW1hIGtvamUgbWkga3JlaXJhbW8gaWxpIHNlIGtyZWlyYWp1IHUgb2t2aXJ1IHJhZGEgcG96dmFuaWggZnVua2NpamEuDQpPYmpla3RlIGtyZWlyYW1vIHRha28gxaF0byBpbSBkb2RlbGltbyBuZWtvIGltZSBpIHByaWRydcW+aW1vIG9kcmXEkWVuZSB2cmVkbm9zdGkuDQpBa28gbmVrdSBuYXJlZGJ1IGl6dnLFoWltbyBpIG5lIGRvZGVsaW1vIGpvaiBpbWUsIG5qZW4gcmV6dWx0YXQgc2UgZ3ViaSBuYWtvbiBpc3Bpc2EgbmEgZWtyYW51Lg0KQWtvIGplIHNtZXN0aW1vIHUgb2JqZWthdCBvbmEgb3N0YWplIHNhxI11dmFuYSBkb2sgamUgbmUgdWtsb25pbW8sIGEgbW/FvmVtbyBqZSBwb3p2YXRpIGltZW5vbSBvYmpla3RhLg0KDQpgYGB7cn0NCjIqNQ0KQTwtMio1ICNzbWXFoXRhbW8gcmV6dWx0YXQgbW5vxb5lbmphIHUgb2JqZWthdCBBDQpBICNwb3ppdmFtbyBvYmpla2F0IEENCkI9NiAjc21lxaF0YW1vIHZyZWRub3N0IDYgdSBvYmpla2F0IEIgDQpCDQpBKkIgI0l6dnLFoWF2YW1vIG9wZXJhY2lqdSBzYSBvYmpla3RpbWENCmBgYA0KIyMjIFVrbGFuamFuamUgb2JqZWt0YSAodSBvdm9tIHNsdcSNYWp1IEIpDQoNCmBgYHtyfQ0KcmVtb3ZlKEIpIA0KYGBgDQoNCiMjIyBpbGkgDQoNCmBgYHtyfQ0Kcm0oQikgI2FrbyBzdGUgcG9rcmVudWxpIHByZXRob2RudSBuYXJlZGJ1IGphdmnEh2UgZ3JlxaFrdSBqZXIgQiB2acWhZSBuZSBwb3N0b2ppDQpgYGANCiMjIyBTTEFKRCAxNyAjIyMNCiMjIE9CSkVLVEkuLi4NCg0KYGBge3J9DQpscygpICMgaXNwaXN1amUgc3Bpc2FrIHN2aWggb2JqZWthdGEgdSBva3J1xb5lbmp1IChlbnZpcm9ubWVudCkNCmBgYA0KVWtsYW5qYW5qZSB2acWhZSBvYmpla2F0YSAoc3ZpaCkNCg0KYGBge3J9DQpyZW1vdmUobGlzdD1scygpKSANCiMgaWxpIA0Kcm0obGlzdD1scygpKQ0KbHMoKQ0KDQpgYGANCg0KIyMjIFNMQUpEIDE4ICMjIw0KIyMgUFJJRFJVxb1JVkFOSkUNClZyxaFpIHNlIHBvbW/Eh3Ugem5ha292YSAiPC0iLCAiLT4iIGlsaSBrb21hbmRvbSBhc3NpZ24oKSwgYSBtb8W+ZSBpIHNhICI9Ii4NClpuYWsgIjwtIiBzZSB1IHByb3pvcnUgc2tyaXB0YSBpIHRlcm1pbmFsYSBuYWpsYWvFoWUgbW/FvmUgZG9iaXRpIGFrbyBpc3RvdnJlbWVubyBwcml0aXNuZXRlIGxldmkgQWx0IGkgLSBzYSBudW1lcmnEjWtlIHRhc3RhdHVyZS4NCg0KYGBge3J9DQpYPC0oYygxLCAyLCAzLCA0LCA1LCA2LCA3LCA4LCA5LCAxMCwgMTAuNSkpDQpYDQpgYGANCkZ1bmtjaWphIGMga29tYmludWplIGFyZ3VtZW50ZSAob25vIHUgemFncmFkaSkgdSB2ZWt0b3INCg0KYGBge3J9DQphc3NpZ24oIlkiLCBjKDEsIDIsIDMsIDQsIDUsIDYsIDcsIDgsIDksIDEwLCAxMC41KSkNCmMoWCwgWCktPlhYDQpgYGANCg0KTmEgb3ZhaiBuYcSNaW4ga3JlaXJhbGkgc21vIGR2YSBpc3RhIHZla3RvcmEgWCBpIFkgaSB2ZWt0b3IgWFgga29qaSBzZSBzYXN0b2ppIG9kIGR2YSB2ZWt0b3JhIFggbmFzdGF2bGplbmEgamVkYW4gbmEgZHJ1Z2kuIFByaWthemHEh2VtbyBpaA0KDQpgYGB7cn0NClgNClkNClhYDQpgYGANCiMjIyBTTEFKRCAyMCAjIyMNCiMjIFZFS1RPUkkNCk9zbm92bmkgZWxlbWVudGkgdSBSLXUuDQpJbmRla3NpcmFuaSBza3Vwb3ZpIHZyZWRub3N0aSBpc3RvZyB0aXBhLg0KU3Zha2kgZWxlbWVudCBpbWEgc3ZvaiBpbmRla3MgKGxva2FjaWp1KSBwb21vxId1IGtvamVnYSBtdSBzZSBtb8W+ZSBwcmlzdHVwaXRpIChpbmRla3NpIHN1IG96bmHEjWVuaSB1Z2xhc3RpbSB6YWdyYWRhbWEpLg0KDQpgYGB7cn0NClhYWzExXSAjcG96aXZhbW8gMTEuIGVsZW1lbnQgdmVrdG9yYSBYWA0KYGBgDQpJbWFqdSBzdm9qIHRpcA0KDQojIyMgU0xBSkQgMjEgIyMjDQojIyBWRUtUT1JJIC0gVElQT1ZJDQpOdW1lcmnEjWtpDQpTbG92bmkgKGNoYXIpLiBLb3Jpc3RlIHNlIGthbyBmYWt0b3JpIChucHIga2F0ZWdvcmlqYWxuZSBncnVwacWhdcSHZSB2YXJpamFibGUpLCBhbGkgaSB6YSBuYXppdmUgdmFyaWphYmxpLCBuYXRwaXNlIG5hIGdyYWZpa29uaW1hLi4uDQpOYSBwcmltZXIgDQoNCmBgYHtyfQ0KaW1lbmE8LWMoInBvbCIsICJzdGFyb3N0IiwgInNwcmVtYSIsICJ2MSIsICJ2MiIsICJWMyIsICJWNCIsICJ2NSIsICJWNiIsICJ2NyIsICJ2OCIpICNrcmVpcmFtbyBzbG92bmkgdmVrdG9yIG5henZhbiAiaW1lbmEiDQppbWVuYSAjaXNwaXNhxIdlbW8gbmplZ292ZSB2cmVkbm9zdGkNCm5hbWVzKFgpPC1pbWVuYSAja29tYW5kb20gbmFtZXMoWCkgZG9kZWxpxIdlbW8gdGEgaW1lbmEgZWxlbW50aW1hIHZla3RvcmEgWA0KWCAjaXNwaXNhxIdlbW8gZGEgdmlkaW1vIHJlenVsdGF0DQpgYGANCiMjIExvZ2nEjWtpIHZla3RvcmkNCk5hc3RhanUgZXZhbHVhY2lqb20gbG9nacSNa2loIHVzbG92YS4gRWxlbWVudGkgc3UgVFJVRSBpIEZBTFNFLg0KDQpOcHIuIA0KDQpgYGB7cn0NCk5WPC1YPjYgI25hcHJhdmnEh2VtbyBsb2dpxI1raSB2ZWt0b3IgTlYgaSB0byB0YWthdiBkYSDEh2UgdnJlZG5vc3QgVFJVRSBkb2JpdGkgZWxlbWVudGkgdmVrdG9yYSBYIHZlxIdpIG9kIDYNCk5WDQpjYmluZChYLE5WKQ0KYGBgDQpLb21hbmRhIGNiaW5kKCkgc3BhamEgZWxlbWVudGUgdSB6YWdyYWRpIChtb3JhanUgYml0aSBpc3RlIGR1xb5pbmUpIGkgcHJpa2F6dWplIGloIHZlcnRpa2Fsbm8uIFNhIHJiaW5kKCkgZG9iaWphbW8gaXN0byB0bywgYWxpIGtvcml6b250YWxuby4gUHJvYmFqdGUsIGFrbyDFvmVsaXRlDQoNCiMjIEluZGVrc25pIHZla3RvcmkNClNsdcW+ZSB6YSBwb3ppdmFuamUgb2RyZcSRZW5paCBlbGVtZW5hdGEgZHJ1Z2loIHZla3RvcmEgaSBzbWXFoXRhanUgc2UgdSB1Z2xhc3RlIHphZ3JhZGUuDQpOcHIuIA0KDQpgYGB7cn0NClk8LVhbMToxMF0gDQojIGlsaSANClk8LVhbIWlzLm5hKFgpXSAjTmVrYSB2ZWt0b3IgWSBidWR1IHNhbW8gb25lIHZyZWRub3N0aSBYIGtvamUgbmlzdSBuZWRvc3RhanXEh2UgTkENCmBgYA0KDQpGdW5rY2lqYSBpcy5uYSgpIHRlc3RpcmEgZGEgbGkgc3UgdnJlZG5vc3RpIHUgemFncmFkaSBuZWRvc3RhanXEh2UuIEFrbyBqZSB1IHphZ3JhZGkgbmF2ZWRlbiB2ZWt0b3IsIG9uZGEgc2UgdGVzdGlyYSBzdmFraSBlbGVtZW50IHZla3RvcmEgaSB2cmHEh2EgdmVrdG9yIHNhIG5lZG9zdGFqdcSHaW0gdnJlZG5vc3RpbWEuIEFrbyBpc3ByZWQgZnVua2NpamUgc3RhdmltbyAhIGRvYmlqYW1vIG5qZW51IG5lZ2FjaWp1IC0gdnJhxIdhIHNlIHZla3RvciBzYSB2cmVkbm9zdGltYSBrb2plIG5pc3UgbmVkb3N0YWp1xIdlLiANCg0KIyMjIFNMQUpEIDIyICMjIw0KIyMgUmHEjXVuYW5qZSBzYSB2ZWt0b3JpbWENCkFrbyBzZSBkdmEgaWxpIHZpxaFlIHZla3RvcmEgcG9qYXZsanVqdSB1IG5la2/RmCBtYXRlbWF0acSNa29qIG9wZXJhY2lqaSBvbmEgxIdlIGJpdGkgcG9ub3ZsamVuYSBvbm9saWtvIHB1dGEga29saWtvIG5hamR1xb5pIHZla3RvciBpbWEgZWxlbWVuYXRhIChzYW1vIGFrbyBqZSBkdcW+aW5hIHZlxIdlZyBkZWxqaXZhIGJleiBvc3RhdGthIGR1xb5pbm9tIGtyYcSHaWggdmVrdG9yYSkuDQoNCmBgYHtyfQ0KWF4yICMgcmVjaW1vIGRhIGplIDIgdmVrdG9yIGR1xb5pbmUgMSAtIHNrYWxhcg0KYGBgDQpTdmFraSBlbGVtZW50IHZla3RvcmEgWCBqZSBrdmFkcmlyYW4uDQpgYGB7cn0NClgqWFggIyByYW5pamUga3JlaXJhbmkgdmVrdG9yaQ0KYGBgDQpYIGplIHJlY2lrbGlyYW4gKHBvxaF0byBYIGltYSAxMCBlbGVtZW5hdGEsIGEgWFggZHZhZGVzZXQsIHBydmloIDEwIGVsZW1lbmF0YSBiacSHZSBwb21ub8W+ZW5vIHNhIGVsZW1lbnRpbWEgdmVrdG9yYSBYLCBhIHphdGltIMSHZSBkcnVnaWggMTAgZWxlbWVuYXRhIGJpdGkgcG9tbm/FvmVubyBvcGV0IGlzdGltIHZla3Rvcm9tIFgpDQoNCiMjIyBTTEFKRCAyMyAjIyMNCiMjIFNWT0pTVFZBIE9CSkVLQVRBDQpPYmpla3RpIGltYWp1IHRpcCBpIGR1xb5pbnUgKGl6bWXEkXUgb3N0YWxvZykNClRpcG92aSB6YXZpc2Ugb2Qgc2FkcsW+YWphIG9iamVrdGEuDQpPZCB0aXBhIG9iamVrdGEgemF2aXNpIMWhdGEgbW/FvmVtbyBzYSBuamltZSByYWRpdGkuDQpUaXBvdmkgc3UgcHJvbWVubGppdmkuDQpEdcW+aW5hIGplIHRha2/EkWUgcHJvbWVubGppdmENCg0KIyMjIFNMQUpEIDI0ICMjIw0KIyMgU1ZPSlNUVkEgT0JKRUtBVEENCmBgYHtyfQ0KbW9kZShYKTsgbGVuZ3RoKFgpDQpgYGANCkZ1bmtjaWpvbSBtb2RlIHNhem5hamVtbyB0aXAsIGEgbGVuZ3RoIGR1xb5pbnUgb2JqZWt0YSAodSBvdm9tIHNsdcSNYWp1IHZla3RvcmEpLg0KWm5hayA7IG9tb2d1xIdhdmEgdmFtIGRhIHZpxaFlIGtvbWFuZGkgc21lc3RpdGUgdSBpc3RpIHJlZC4NCmBgYHtyfQ0KWDwtYXMuY2hhcmFjdGVyKFhbMTo1XSk7IG1vZGUoWCk7IGxlbmd0aChYKQ0KWCAjaXNwaXNhxIdlbW8gdmVrdG9yIFgNCmBgYA0KUmV6dWx0YXRpIHNlIGlzcGlzdWp1IHUgcG9zZWJuaW0gcmVkb3ZpbWEuDQpJc3RvdnJlbWVubyBzbW8gc2tyYXRpbGkgaSBwcm9tZW5pbGkgdGlwIHZla3RvcmEuDQpLb21hbmRvbSBhcy5jaGFyYWN0ZXIgcHJvbWVuaWxpIHNtbyBudW1lcmnEjWtpIHZla3RvciB1IHNsb3ZuaSAoZWxlbWVudGkgbmlzdSBicm9qZXZpIHZlxIcga2FyYWt0ZXJpIC0gdG8gdmlkaW1vIHBvIG5hdm9kbmljaW1hIG9rbyBuamloKS4NClBvbW/Eh3UgaW5kZWtzbm9nIHZla3RvcmEgWzE6NV0gcmVrbGkgc21vIGRhIFggYnVkZSBzYW1vIHBydmloIDUgZWxlbWVuYXRhIGlzdG9nIHRvZyB2ZWt0b3JhIFguDQoNClByb2R1xb5pxIdlbW8gdmVrdG9yIGRvZGFqdcSHaSBtdSBuYSAxNS4gcG96aWNpanUgY2lmcnUgNiAoYWxpIG5lIGthbyBicm9qIHZlxIcga2FvIGthcmFrdGVyIC0gdW9rdmlyZW5vIG5hdm9kbmljaW1hKQ0KYGBge3J9DQpYWzE1XTwtIjYiOyBsZW5ndGgoWCkgDQpYDQpgYGANClNrcmF0acSHZW1vIHZla3RvciBpemJhY3VqdcSHaSBuZWRvc3RhanXEh2UgdnJlZG5vc3RpDQpgYGB7cn0NClg8LVhbIWlzLm5hKFgpXSA7IGxlbmd0aChYKSANClgNCmBgYA0KIyMjIFNMQUpEIDI1ICMjIw0KIyMgREFMSkUuLi4NCmBgYHtyfQ0KWCtYDQpgYGANCkdvcm5qYSBrb21hbmRhIHNlIG5lIG1vxb5lIGl6dnLFoWl0aSBqZXIgamUgdmVrdG9yIHNsb3ZuaSAoY2hhcikuIEFrbyBpenZyxaFpdGUgbmFyZWRidSBkb2JpxIdldGUgZ3JlxaFrdSAiRXJyb3IgaW4gWCArIFggOiBub24tbnVtZXJpYyBhcmd1bWVudCB0byBiaW5hcnkgb3BlcmF0b3IiLiBaYXBhbXRpdGUgb3Z1IHNpdHVhY2lqdSBpIGdyZcWha3UgamVyIHZhbSBzZSDEjWVzdG8gbW/FvmUgamF2aXRpIHVrb2xpa28gbmUgcG92ZWRldGUgcmHEjXVuYSBvIHRpcHUgcG9kYXRha2EuDQpgYGB7cn0NClg8LWFzLm51bWVyaWMoWCkgIyBtZW5qYW1vIG11IG1vZGUgdSBudW1lcmnEjWtpDQpYK1ggIyBpIG9uZGEgbW/FvmUgZGEgc2Ugc2FiaXJhDQoNCmBgYA0KDQojIyMgU0xBSkQgMjYgIyMjDQojIyBVxI1pdGF2YW5qZSBkYXRvdGVrYQ0KUG9kYWNpIHphIFIgc3UgdGVrc3R1YWxub2ogZGF0b3RlY2kgdSBrb2pvaiBzdSBwb2xqYSBvZGVsamVuYSByYXptYWtvbSBpbGkgVEFCT00uIE1vxb5lIGkgZHJ1Z2ltIHNlcGFyYXRvcm9tLCBhbGkgcmF6bWFrIGplIHBvZHJhenVtZXZhbmkuDQpEb2JybyBqZSBkYSBwcnZpIHJlZCBzYWRyxb5hdmEgbmF6aXZlIHZhcmlqYWJsaSwgYSBwcnZhIGtvbG9uYSByZWRuaSBicm9qIHNsdcSNYWphLg0KQWtvIHRvIG5lIHBvc3RvamkgUiDEh2UgZG9kZWxpdGkgbmF6aXZlIGkgYnJvamV2ZQ0KS29tYW5kYSBwb3B1dCBzbGVkZcSHZQ0KcG9kYWNpPC1yZWFkLnRhYmxlKGZpbGUuY2hvb3NlKCksaGVhZGVyPVQsc2VwPSJcdCIpDQpvdHZvcmnEh2Uga2xhc2nEjW5pIGRpamFsb2cgemEgaXpib3IgZGF0b3Rla2UsIGkgbmFrb24gbmplbm9nIGl6Ym9yYSB1xI1pdGHEh2UgamUgb2JqZWthdCAicG9kYWNpIiB0aXBhIGRhdGEuZnJhbWUgKG1hdHJpY2EgcG9kYXRha2EpLg0KT3ZhaiBvYmpla2F0IHBvc3Rvamkgc2FtbyB1IG1lbW9yaWppIG5lIGkgbmEgZGlza3UuDQpOYSBzbGVkZcSHaSBuYcSNaW4gdcSNaXRhdmFtbyBwb2RhdGtlIGJleiBvdHZhcmFuamEgZGlqYWxvZ2EuDQpgYGB7cn0NCnBvZGFjaTwtcmVhZC50YWJsZSgiRDovT1RTUyhSKS90ZXN0Mi50eHQiLCBoZWFkZXI9VFJVRSwgc2VwPSIiLCBxdW90ZSA9ICJcIiciLCBkZWMgPSAiLiIpDQpgYGANCkFyZ3VtZW50IGhlYWRlcj1UUlVFIHpuYcSNaSBkYSBzdSBuYXppdmkgdmFyaWphYmxpIHUgcHJ2b20gcmVkdSBkYXRvdGVrZSwgZGVjPSIuIiBkYSBzdSBkZWNpbWFsZSBvZGVsamVuZSB0YcSNa29tLCBzZXA9IiIgZGEgc3UgcG9samEgb2RlbGplbmEgcHJhem5pbSBtZXN0b20sIGEgcXVvdGUgPSAiXCInIiBkYSBzdSB0ZWtzdHVhbG5lIHZhcmlqYWJsZSBvZ3JhbmnEjWVuZSBkdm9zdHJ1a2ltIGlsaSBqZWRub3N0cnVraW0gbmF2b2RuaWNpbWEuDQpgYGB7cn0NCm5hbWVzKHBvZGFjaSkgIyBuYXppdmkga29sb25hIG1hdHJpY2UgcG9kYXRha2EgZGEgcHJvdmVyaXRlIGRhIGxpIHN0ZSB1xI1pdGFsaSBkb2JyZSBwb2RhdGtlDQpzdHIocG9kYWNpKSAjIG5hcmVkYmEga29qb20gc2UgcHJvdmVyYXZhIHN0cnVrdHVyYSBvYmpla3RhIChkb2JpamV0ZSBuYXppdmUga29sb25hLCBicm9qIG9wc2VydmFjaWphL2lzcGl0YW5pa2EsIGJyb2ogdmFyaWphYmxpIGkgcHJ2aWggMTAgdnJlZG5vc3RpKQ0KaGVhZChwb2RhY2kpDQpgYGANCiMjIFXEjWl0YXZhbmplIHBvZGF0YWthIHRpcGEgQ29tYSBTZXBhcmF0ZWQgVmFsdWVzDQpgYGB7cn0NCnBvZGFjaTI8LXJlYWQuY3N2KCJyZXp1bHRhdGkuY3N2IiwgaGVhZGVyPVRSVUUsIHNlcD0iOyIsIHF1b3RlPSJcIiciLCBkZWMgPSAiLiIpDQpwb2RhY2kyDQpgYGANCnNlcCBtb8W+ZSBiaXRpIGkgIiwiIHUgemF2aXNub3N0aSBvZCBwb2RlxaFhdmFuamEga29kbmUgc3RyYW5pY2Ugb3BlcmF0aXZub2cgc2lzdGVtYS4NCg0KIyMgVcSNaXRhdmFuamUgcG9kYXRha2EgdGlwYSBFWENFTA0KUG9zdG9qaSBwYWtldCB6YSDEjWl0YW5qZSBwb2RhdGFrYSBpeiBleGNlbGEgcG9kIG5heml2b20gInhsc3giDQpgYGB7cn0NCnBfbG9hZCh4bHN4KQ0KcG9kYWNpWFkgPSByZWFkLnhsc3goInJlenVsdGF0aS54bHMiLCBzaGVldEluZGV4PTEsIHJvd0luZGV4PU5VTEwsIGNvbEluZGV4PU5VTEwpDQpwb2RhY2lYWQ0KYGBgDQpzaGVldEluZGV4IGplIGJyb2ogbGlzdGEgKHNoZWV0KSBpeiBleGNlbCBkYXRvdGVrZSwgcm93SW5kZXggaSBjb2xJbmRleCBvem5hxI1hdmFqdSByZWRvdmUgaSBrb2xvbmUga29qZSDFvmVsaW1vIGRhIHV2ZXplbW8uIEFrbyBvc3RhdmltbyBOVUxMIHRvIHpuYcSNaSBkYSDFvmVsaW1vIGRhIHV2ZXplbW8gc3ZlLg0KDQpQcm9ibGVtIHNhIGRvbWHEh2ltIGthcmFrdGVyaW1hIHNlIGRvbmVrbGUgbW/FvmUgcmXFoWl0aSBvcGNpam9tIGVuY29kaW5nPSJVVEYtOCINCmBgYHtyfQ0KcG9kYWNpWFkgPSByZWFkLnhsc3goInJlenVsdGF0aS54bHMiLCBzaGVldEluZGV4PTEsIHJvd0luZGV4PU5VTEwsIGNvbEluZGV4PU5VTEwsIGVuY29kaW5nPSJVVEYtOCIpDQpwb2RhY2lYWQ0KP3JlYWQueGxzeCAjbW/FvmV0ZSBwb2dsZWRhdGkgaSBkcnVnZSBvcGNpamUNCmBgYA0KIyMgUGlzYW5qZSB1IGV4Y2VsIGRhdG90ZWt1DQpgYGB7cn0NCndyaXRlLnhsc3gocG9kYWNpWFksICJwb2RhY2lYWS54bHN4IiwgY29sLm5hbWVzPVRSVUUsIHJvdy5uYW1lcz1UUlVFKQ0KYGBgDQpaYSBleGNlbCBkYXRvdGVrZSBwb3N0b2ppIGkgcGFrZXQgInJlYWR4bCINCmBgYHtyfQ0KcF9sb2FkKHJlYWR4bCkNCnBvZGFjaVhZID0gcmVhZF9leGNlbCgicmV6dWx0YXRpLnhscyIsIHNoZWV0PTEpDQpwb2RhY2lYWQ0KP3JlYWRfZXhjZWwNCmBgYA0KIyMjIFNMQUpEIDI4ICMjIw0KIyMgZGF0YS5mcmFtZQ0KS29yaXNubyBqZSB1xI1pdGF0aSBwb2RhdGtlIGthbyBvYmpla2F0IGtsYXNlIGRhdGEuZnJhbWUuDQpKZWRub3N0YXZubywgc2FtbyBmdW5rY2lqdSB6YSB1xI1pdGF2YW5qZSBzdGF2aXRpIHUgZnVua2NpanUgZGF0YS5mcmFtZSgpLCBucHIuLi4NCmBgYHtyfQ0KcG9kYWNpWFkgPSBkYXRhLmZyYW1lKHJlYWRfZXhjZWwoInJlenVsdGF0aS54bHMiLCBzaGVldD0xKSkNCmBgYA0KRnVua2NpamEgcmVhZC54bHN4IHRvIHJhZGkgcG8gZGVmYXVsdHUNCg0KIyMjIFNMQUpEIDI5ICMjIw0KIyMgVcSNaXRhdmFuamUgZGF0b3Rla2ENClBvZGF0a2UgemF0aW0gbW/FvmV0ZSBwcmVnbGVkYXRpIGkgdXJlxJFpdmF0aSBzYSANCmBgYHtyfQ0KZml4KHBvZGFjaTIpDQplZGl0KHBvZGFjaTIpDQpoZWFkKHBvZGFjaTIsIDEwKSAjIGlzcGlzIHBydmloIDEwIHJlZG92YQ0KdGFpbChwb2RhY2kyLCA1KSAjIGlzcGlzIHBvc2xlZG5qaWggNSByZWRvdmENCmBgYA0KDQpQcmlsaWtvbSB1xI1pdGF2YW5qYSBzdmFrb2oga29sb25pIHNlIGRvZGVsanVqZSB0aXAgdSB6YXZpc25vc3RpIG9kIHBvZGF0YWthIGtvamUgc2FkcsW+ZSAob2JpxI1ubyB0byBpc3BhZG5lIGtha28gdHJlYmEuLi4pDQpQb2plZGluYcSNbm9qIHZhcmlqYWJsaSAodmVrdG9ydSkgdSBwb2RhY2ltYSBwcmlzdHVwYXRlIG5hdm/EkWVuamVtIG5heml2YSBvYmpla3RhIHUga29tIHNlIG5hbGF6aSBwcmHEh2VuaW0gem5ha29tICQgaSBuYXppdm9tIHZla3RvcmEgbnByLg0KYGBge3J9DQpwb2RhY2kyJElOREVYDQptb2RlKHBvZGFjaTIkSU5ERVgpDQpgYGANCg0KSWxpIHByZWtvIGluZGVrc2EgKGJyb2phKSBrb2xvbmUNCmBgYHtyfQ0KcG9kYWNpMlsxXSAjbnVtZXJpxI1raSBpbmRla3MsIHBydmEga29sb25hDQpwb2RhY2kyWzE6M10gI251bWVyacSNa2kgaW5kZWtzLCBwcnZlIHRyaSBrb2xvbmUNCnBvZGFjaTJbYygiSU5ERVgiLCJGT1JNQSIsICJwaXQxIildICNpbWVuYSBrYW8gaW5kZWtzDQpgYGANCk5hIHRhaiBuYcSNaW4gbW/FvmV0ZSBpeiBtYXRyaWNlIHBvZGF0YWthIGl6ZHZvaml0aSB2YXJpamFibGUgemEgYW5hbGl6dSwgbmEgcHJpbWVyLi4uIE1vxb5ldGUgaWgga29waXJhdGkgaSB1IG5vdmkgb2JqZWthdC4uLg0KDQojIyMgU0xBSkQgMzAgIyMjDQojIyBQaXNhbmplIHUgZGF0b3Rla2UNCkFrbyBob8SHZXRlIGRhIHUgbWF0cmljdSBwb2RhdGFrYSAoZGF0YS5mcmFtZSkgZG9kYXRlIG5la3UgdmFyaWphYmx1IGtvanUgc3RlIGl6cmHEjXVuYWxpLCBtb3JhdGUgamUgcmVmZXJpc2F0aSBpc3RvIGthbyBrYWQgamUgxI1pdGF0ZQ0KYGBge3J9DQpwb2RhY2kyJFNVTUE8LXJvd1N1bXMocG9kYWNpMlszOjQyXSwgbmEucm09RkFMU0UpIA0KcG9kYWNpMiRTVU1BDQpgYGANCg0KRnVua2NpamEgcm93U3VtcyBzYWJpcmEgcmVkb3ZlIHUgbWF0cmljaSBwb2RhdGFrYS4gU3ZpIHBvZGFjaSBtb3JhanUgYml0aSBudW1lcmnEjWtpLiBBcmd1bWVudCBuYS5ybT1GQUxTRSByZXp1bHRpcmEgbmVkb3N0YWp1xIdpbSBwb2RhY2ltYSAoTkEpIGFrbyBqZSBiYXIgamVkYW4gc2FiaXJhayBOQS4gbmEucm09VFJVRSByZXp1bHRpcmEgemJpcm9tIGkgdSB0YWt2b2ogc2l0dWFjaWppLg0KDQojIyBQaXNhbmplIG5hIGRpc2sNClUgQ1NWLCBzYSBzZXBhcmF0b3JvbSA7ICwgYmV6IG5heml2YSByZWRvdmEsIHNhIG5heml2aW1hIGtvbG9uYSwgaSBkdXBsaW0gbmF2b2RuaWNpbWEgemEgdGVrc3R1YWxuZSB2YXJpamFibGUNCmBgYHtyfQ0Kd3JpdGUudGFibGUocG9kYWNpMiwgZmlsZSA9ICJwb2RhY2kyLmNzdiIsIHNlcCA9ICI7Iiwgcm93Lm5hbWVzPUZBTFNFLCBjb2wubmFtZXMgPSBUUlVFLCBxbWV0aG9kID0gImRvdWJsZSIpDQpgYGANClUgVFhULCBzYSBUQUIgc2VwYXJhdG9yb20sIGJleiBuYXppdmEgcmVkb3ZhLCBzYSBuYXppdmltYSBrb2xvbmEsIGJleiBuYXZvZG5pa2EgemEgdGVrc3R1YWxuZSB2YXJpamFibGUNCmBgYHtyfQ0Kd3JpdGUudGFibGUocG9kYWNpMiwgZmlsZSA9ICJwb2RhY2kyLnR4dCIsIHNlcCA9ICJcdCIsIHJvdy5uYW1lcz1GQUxTRSwgY29sLm5hbWVzID0gVFJVRSwgcXVvdGUgPSBGQUxTRSkNCmBgYA0KIyMjIFNMQUpEIDMxICMjIw0KIyMgQnJpc2FuamUgdmFyaWphYmxlIGl6IG1hdHJpY2UgcG9kYXRha2ENClBydm8gxIdlbW8ga3JlaXJhdGkgbm92dSB2YXJpamFibHUNCmBgYHtyfQ0KcG9kYWNpMiRCRVpWRVpFPC1yb3dTdW1zKHBvZGFjaTJbMzo0Ml0sIG5hLnJtPVRSVUUpDQpgYGANClNhYnJhbGkgc21vIHBvIHJlZG92aW1hIHZhcmlqYWJsZSBvZCAzIGRvIDQzLg0KU2EgZml4KHBvZGFjaTIpIGlsaSBWaWV3KHBvZGFjaTIpIG1vxb5ldGUgdmlkZXRpIHZhcmlqYWJsdSBCRVpWRVpFIGtvanUgc3RlIGtyZWlyYWxpIG5hIGtyYWp1IG1hdHJpY2UuDQpCcmlzYW5qZSB2YXJpamFibGUgQkVaVkVaRQ0KYGBge3J9DQpwb2RhY2kyJEJFWlZFWkU8LSBOVUxMDQpgYGANClNhIGZpeChwb2RhY2kyKSBpbGkgVmlldyhwb2RhY2kyKSBtb8W+ZXRlIHZpZGV0aSBkYSB2YXJpamFibGUgQkVaVkVaRSB2acWhZSBuZW1hLg0KDQojIyMgU0xBSkQgMzIgIyMjDQojIyBTb3J0aXJhbmplDQpLb3Jpc3RpbW8gZnVua2NpanUgb3JkZXIoKQ0KYGBge3J9DQpwb2RhY2lfcz1wb2RhY2kyW29yZGVyKHBvZGFjaTIkRk9STUEpLF0NCnBvZGFjaV9zDQpgYGANCg0KRnVua2NpamEgb3JkZXIocG9kYWNpMiRGT1JNQSkga3JlaXJhIHZla3RvciByZWRuaWggYnJvamV2YSBzbHXEjWFqZXZhIHBvIHJlZG9zbGVkdSBuYSB2YXJpamFibGkgRk9STUEgKHVrb2xpa28gxb5lbGl0ZSBwb2tyZW5pdGUgamUgc2Ftb3N0YWxubyBkYSB2aWRpdGUgcmV6dWx0YXQpLiBUYWogdmVrdG9yIHNlIHNtZcWhdGEgdSB1Z2xhc3RlIHphZ3JhZGUgcG9zbGUgbmF6aXZhIG9iamVrdGEga29qaSBob8SHZW1vIGRhIHNvcnRpcmFtbyAocG9kYWNpMikuIEluZGVrc2kgbWF0cmljZSBwb2RhdGFrYSB1IHphZ3JhZGFtYSBpbWFqdSBkdmEgZGVsYSBbICwgXS4gT25pIHByZSB6YXJlemEgb2Rub3NlIHNlIG5hIHJlZG92ZSwgYSBvbmkgcG9zbGUgbmEga29sb25lLiBBa28gbmUgc3RhdmltbyB6YXJleiBvbmRhIHNlIHBvZHJhenVtZXZhIGRhIHNlIG9kbm9zZSBuYSBrb2xvbmUuIFBvxaF0byBzb3J0aXJhbW8gcmVkb3ZlIHN0YXZpbGkgc21vIHphcmV6IHBvc2xlIGluZGVrc25vZyB2ZWt0b3JhLg0KDQpOYXByYXZpbGkgc21vIG5vdmkgZGF0YS5mcmFtZSBwb2RhY2lfcyBrb2ppIGplIGplZG5hayBkYXRhLmZyYW1ldSBwb2RhY2kyIHNvcnRpcmFub20gcG8gdmFyaWphYmxpIEZPUk1BIChwbyByYXN0dcSHZW0gcmVkb3NsZWR1KS4NCg0KT3Zha28gc29ydGlyYW1vIHBvIGR2ZSB2YXJpamFibGUNCmBgYHtyfQ0KcG9kYWNpX3M9cG9kYWNpMltvcmRlcihwb2RhY2kyJEZPUk1BLCBwb2RhY2kyJElOREVYKSxdDQpwb2RhY2lfcw0KYGBgDQpQbyB2YXJpamFibGkgRk9STUEgaSBwbyB2YXJpamFibGkgSU5ERVguDQoNCg0KUHJvbWVuacSHZW1vIHNvcnRpcmFuamUgemEgdmFyaWphYmx1IElOREVYIHUgb3BhZGFqdcSHaSByZWRvc2xlZA0KYGBge3J9DQpwb2RhY2lfcz1wb2RhY2kyW29yZGVyKHBvZGFjaTIkRk9STUEsIC1yYW5rKHBvZGFjaTIkSU5ERVgpKSxdDQpwb2RhY2lfcw0KYGBgDQpaYSBGQUtUT1JFIHNlIG1vcmEgZG9kYXRpIC1yYW5rIGlzcHJlZCBuYXppdmEgdmFyaWphYmxlIHUgemFncmFkYW1hLiBLb2QgbnVtZXJpxI1raWggZG92b2xqYW4gamUgLSBpc3ByZWQgaW1lbmEgdmFyaWphYmxlLg0KDQojIyMgU0xBSkQgMzMgIyMjDQojIyBSZWZlcmlzYW5qZSBlbGVtZW5hdGEgdGFiZWxhDQpLb2xvbmFtYSB0YWJlbGEgc2UgbW/FvmUgcHJpc3R1cGl0aSBwcmVrbyBpbWVuYSAoYWtvIGloIGltYWp1KSBpbGkgcHJla28gbnVtZXJpxI1raWggaW5kZWtzYS4gUmVkb3ZpbWEgc2UgcHJpc3R1cGEgcHJla28gbnVtZXJpxI1raWggaW5kZWtzYS4NCmBgYHtyfQ0KcG9kYWNpMltjKDE6MyldICMgamUgaXN0byDFoXRvIGkgDQpwb2RhY2kyWzE6M10gDQpgYGANClBydmUgdHJpIGtvbG9uZSBtYXRyaWNlIHBvZGFjaTIuDQoNCkRydWdpaCAxMCByZWRvdmEgaSBwcnZlIHRyaSBrb2xvbmUNCmBgYHtyfQ0KcG9kYWNpMlsxMDoyMCwgMTozXSANCmBgYA0KRHJ1Z2loIDEwIHJlZG92YSBpIHBydmUgdHJpIGtvbG9uZSArIDUuIGkgNy4ga29sb25hDQpgYGB7cn0NCnBvZGFjaTJbMTA6MjAsIGMoMTozLCA1LCA3KV0NCmBgYA0KS2FkYSBrb2xvbmUgaWxpIHJlZG92aSBuaXN1IGplZGFuIGRvIGRydWdvZyBtb3JhIHNlIG5hcHJhdml0aSB2ZWt0b3IgYnJvamV2YSBrb3Jpc3RlxIdpIGMoKS4gSXp2csWhaXRlIGMoMTozLCA1LCA3KSBkYSB2aWRpdGUga2FrbyBpemdsZWRhIHRhaiB2ZWt0b3IuDQpQb2RzZcSHYW0sIHUgdWdsYXN0aW0gemFncmFkYW1hIHByZSB6YXJlemEgc3UgaW5kZWtzaSByZWRvdmEsIGEgcG9zbGUgemFyZXphIGluZGVrc2kga29sb25hLiBBa28gaXpvc3Rhdml0ZSBpbmRla3MgcmVkYSBwb2RyYXp1bWV2YSBzZSBkYSDFvmVsaXRlIGRhIGloIHVrbGp1xI1pdGUgc3ZlLiBJc3RvIHZhxb5pIGkgemEga29sb25lLg0KDQojIyMgU0xBSkQgMzQgIyMjDQojIyBPc25vdm5pIG9wZXJhdG9yaSB1IFItdQ0KQXJpdG1ldGnEjWtpOg0KKywgLSwgLywgKiwgXg0KYGBge3J9DQoxNl4oMS8yKSMga3ZhZHJhdG5pIGtvcmVuIOKAkyBtb8W+ZSBpIA0Kc3FydCgxNikNCjE2XigxLzQpICMgNC4ga29yZW4gaXogMTYNCjE2XigtMSkgIyByZWNpcHJvxI1uYSB2cmVkbm9zdCAtIGlzdG8gxaF0byBpIDEvMTYNCmBgYA0KIyMgT3BlcmF0b3JpIG9kbm9zYSAocG9yZcSRZW5qYSkNCiMjIyMgPCAobWFuamUpDQojIyMjIDw9IChuaWplIHZlxIdlKQ0KIyMjIyA+PSAobmlqZSBtYW5qZSkNCiMjIyMgPT0gKGplZG5ha28pIE5pamUgZ3JlxaFrYSwgYWtvIHByb3ZlcmF2YXRlIGplZG5ha29zdCBvbmRhIGlkdSAyIHpuYWthID09LiBKZWRhbiB6bmFrID0gZWt2aXZhbGVudGFuIGplIDwtDQojIyMjICE9IChuaWplIGplZG5ha28pDQoNCiMjIExvZ2nEjWtpIG9wZXJhdG9yaSANCiYgIChBTkQsIGkpLCB8IChPUiwgaWxpKQ0KDQojIyMgU0xBSkQgMzUgICMjIw0KIyMgSm/FoSBtYWxvIHJlZmVyaXNhbmphIGVsZW1lbmF0YSB0YWJlbGENCmBgYHtyfQ0KcG9kYWNpMlsicGl0MiJdICNuYXppdiB2YXJpamFibGUga2FvIGluZGVrcw0KcG9kYWNpMltjKCJwaXQyIiwgInBpdDUiKV0gICNzbG92bmkgdmVrdG9yIG5heml2YSB2YXJpamFibGkga2FvIGluZGVrcyANCmBgYA0KSXNwaXRhbmljaSBrb2ppIGltYWp1IHBvZGF0YWsgbmEgdmFyaWphYmxpIHBpdDINCmBgYHtyfQ0Kc3Vic2V0KHBvZGFjaTIsIHBvZGFjaTIkcGl0MiE9Ik5BIikNCmBgYA0KRnVua2Npam9tIHN1YnNldCgpIGl6ZHZvamlsaSBzbW8gc3ZlIGtvZCBrb2ppaCB2cmVkbm9zdCBuYSB2YXJpamFibGkgcGl0MiBuaWplIGplZG5ha2EgTkEgKG5lZG9zdGFqdcSHYSkuDQoNCmBgYHtyfQ0Kc3Vic2V0KHBvZGFjaTIsIHNlbGVjdD0xOjMpDQpgYGANCkZ1bmtjaWpvbSBzdWJzZXQgaXpkdm9qaWxpIHNtbyBrb2xvbmUgb2QgMSBkbyAzLg0KYGBge3J9DQpzdWJzZXQocG9kYWNpMiwgc2VsZWN0PWMoMTozLCA1KSkNCmBgYA0KLi4ua29sb25lIDEtMyBpIDUNCg0KYGBge3J9DQpzdWJzZXQocG9kYWNpMiwgIHBvZGFjaTIkcGl0Mz09Mywgc2VsZWN0PWMoMTozLCA1KSkNCmBgYA0KLi4uaXNwaXRhbmljaSBrb2ppIHN1IGRhbGkgb2Rnb3ZvciAzIG5hIHZhcmlqYWJsaSBwaXQzLCBrb2xvbmUgMS0zIGkgNQ0KDQojIyMgU0xBSkQgMzYgIyMjDQojIyBTZWxla2NpamEgdmFyaWphYmxpIGkgaXNwaXRhbmlrYQ0KYGBge3J9DQpwb2RhY2kyJFNVTUEgI2RhIHBvZ2xlZGFtbyB2cmVkbm9zdGkgbmEgb3ZvaiB2YXJpamFibGkNCmBgYA0KT2RhYnJhxIdlbW8gc2FtbyBvbmUgaXNwaXRhbmlrZSBrb2ppIGltYWp1IHBvZGF0YWsgbmEgdmFyaWphYmxpIFNVTUEgKGkgc21lc3RpxIdlbW8gdSBub3ZpIGRhdGEuZnJhbWUpDQpgYGB7cn0NCnBvZGFjaTJhPC1wb2RhY2kyW2lzLm5hKHBvZGFjaTIkU1VNQSkhPSBUUlVFLF0NCnBvZGFjaTJhJFNVTUEgI2RhIHNlIHV2ZXJpbW8gZGEgbmVtYSBuZWRvc3RhanXEh2loDQpgYGANCg0KDQpGdW5rY2lqYSBpcy5uYSgpIHRlc3RpcmEgZGEgbGkgamUgbmXFoXRvIG5lZG9zdGFqdcSHYSB2cmVkbm9zdC4NClUgbmFyZWRub2ogbmFyZWRiaSBwb3N0b2plIGR2YSB1c2xvdmE6IDEuIGRhIGplIGlzcGl0YW5payBvZGdvdm9yaW8gbmEgcGl0MSBpIGRhIG5hIFNVTUEgaW1hIG5lZG9zdGFqdcSHaSBwb2RhdGFrLiBTdmUga29sb25lIHRha3ZpaCBpc3BpdGFuaWthIGtvcGlyYW5lIHN1IHUgb2JqZWthdCBwb2RhY2k1IChrbGFzZSBkYXRhLmZyYW1lKS4gRGEgdmlkaXRlIG9iamVrYXQgKG5wci4gcG9kYWNpNSwgc2FtbyB1IGtvbnpvbGkgdWt1Y2FqdGUgbmplZ292byBpbWUgaSBwcml0aXNuaXRlIEVOVEVSKQ0KYGBge3J9DQpwb2RhY2k1PC1wb2RhY2kyWyFpcy5uYShwb2RhY2kyJHBpdDEpPT1UUlVFICYgaXMubmEocG9kYWNpMiRTVU1BKT09VFJVRSxdDQpwb2RhY2k1DQpgYGANClN2aSBzYSBza29yb20gdmXEh2ltIG9kIDk1IChzdmUga29sb25lIHUgb2JqZWthdCBwb2RhY2kyYikNCmBgYHtyfQ0KcG9kYWNpMmI8LXBvZGFjaTJhW3BvZGFjaTJhJFNVTUE+OTUsXQ0KcG9kYWNpMmINCmBgYA0KSXN0byBrYW8gcHJldGhvZG5vLCBhbGkgc3UgaXphYnJhbmUgc2FtbyBrb2xvbmUgSU5ERVggaSBTVU1BIGkga29waXJhanUgc2UgdSBvYmpla2F0IHBvZGFjaTJjDQpgYGB7cn0NCnBvZGFjaTJjPC1wb2RhY2kyYVshcG9kYWNpMmEkU1VNQT45NSxjKCJJTkRFWCIsICJTVU1BIildDQpwb2RhY2kyYw0KYGBgDQojIyMgU0xBSkQgMzcgIyMjDQojIyBBVFRBQ0gNCktvbWFuZGEgYXR0YWNoKHBvZGFjaTIpIHNtZcWhdGEgdGFiZWx1IHNhIHBvZGFjaW1hIG5hIHB1dGFuanUgcHJldHJhZ2UgUi1hIHBhIG9iamVrdGltYSBrb2ppIHNlIG5hbGF6ZSB1IG5qb2ogbW/FvmVtbyBwcmlzdHVwaXRpIHByZWtvIGltZW5hIChiZXogInBvZGFjaTIkIikNCmBgYHtyfQ0KYXR0YWNoKHBvZGFjaTIpDQpJTkRFWCAjcG96aXZhbmplIHZhcmlqYWJsZSBJTkRFWCBpeiBvYmpla3RhIHBvZGFjaTINCm1vZGUoSU5ERVgpDQpTVU1BDQpgYGANClZvZGl0ZSByYcSNdW5hLCBha28gcG96b3ZldGUgdmFyaWphYmx1ICJJTkRFWCIgYmV6IG5heml2YSBza3VwYSBwb2RhdGFrYSBvbmRhIMSHZSBqZSBwb3p2YXRpIGl6IHNrdXBhIHBvZGFjaTIsIG9kbm9zbm8gcG9zbGVkbmplZyBrb2plZyBzdGUgcHJpZHJ1xb5pbGkgc2EgQVRUQUNIDQpgYGB7cn0NCmF0dGFjaChwb2RhY2kyYSkNCmZpbmQoIklOREVYIikgICMgdmFtIGphdmxqYSAgb2Rha2xlIHBveml2YXRlIHZhcmlqYWJsdSBJTkRFWCAocHJ2aSBuYXZlZGVuaSBvYmpla2F0KQ0KZGV0YWNoKHBvZGFjaTJhKSAjIHVrbGFuamEgInBvZGFjaTJhIiBzYSBwdXRhbmplDQpmaW5kKCJJTkRFWCIpICAjIHZhbSBqYXZsamEgIG9kYWtsZSBwb3ppdmF0ZSB2YXJpamFibHUgSU5ERVgNCg0KYGBgDQoNCg0KIyMjIFNMQUpEIDM4ICMjIw0KIyMgVVZPWiBEQVRPVEVLQQ0KIyMjIyBQYWtldCBmb3JlaWduDQpgYGB7cn0NCmxpYnJhcnkoZm9yZWlnbikNCmBgYA0KT3ZhIGJpYmxpb3Rla2EgdmFtIG9tb2d1xIdhdmEgdXZveiBpeiBNaW5pdGFiLCBTLCBTQVMsIFNQU1MsIFN0YXRhLCBTeXN0YXQgaSBkQmFzZSBmb3JtYXRhLg0KRnVua2NpamEgemEgU1BTUyAuc2F2IGZvcm1hdCBqZSByZWFkLnNwc3MNCmBgYHtyfQ0KcG9kYWNpMzwtYXMuZGF0YS5mcmFtZShyZWFkLnNwc3MoInRlc3Q0LnNhdiIsIHVzZS52YWx1ZS5sYWJlbHMgPSBUUlVFKSkNCnBvZGFjaTMNCmBgYA0KQXJndW1lbnQgdXNlLnZhbHVlLmxhYmVscyA9IFRSVUUgdmFtIG9tb2d1xIdhdmEgZGEgdXZlemV0ZSBsYWJlbGUgemEga2F0ZWdvcmlqYWxuZSB2YXJpamFibGUgKGZha3RvcmUpDQoNCiMjIyBTTEFKRCAzOSAjIyMNCiMjIFJFS09ESVJBTkpFDQpLb3Jpc3RpxIdlbW8gcGFrZXQgY2FyDQpgYGB7cn0NCnBfbG9hZChjYXIpDQpwb2RhY2kyJFNVTUEyPC1SZWNvZGUocG9kYWNpMiRTVU1BLCAiTkE9MCIpICMgTkEgdSAwIHUgbm92dSB2YXJpamFibHUgU1VNQTINCnByb3ZlcmE8LXN1YnNldChwb2RhY2kyLCBzZWxlY3Q9YygiU1VNQSIsICJTVU1BMiIpKSAjIGl6ZHZhamFuamUgZHZlIHZhcmlqYWJsZSB1IG5vdmkgb2JqZWthdCBQUk9WRVJBDQpwcm92ZXJhDQpgYGANClJla29kaXJhbmplIHZpxaFlIHZyZWRub3N0aSBpc3RvdnJlbWVubyAoInJlZmxla3RvdmFuamUiICsgTkEgdSAwKSB1IGlzdHUgdmFyaWphYmx1DQpgYGB7cn0NCnBvZGFjaTIkcGl0MzwtUmVjb2RlKHBvZGFjaTIkcGl0MywgIk5BPTA7IDE9NTsgMj00OyA0PTI7IDU9MSIpDQpgYGANClNhxb5pbWFuamUgdSBpc3R1IHZhcmlqYWJsdSAodmnFoWUgdnJlZG5vc3RpIHUgamVkbnUgKyBwcm9tZW5hIHRpcGEgdSBjaGFyKCksIGplciBzbW8gemFkYWxpIHRla3N0dWFsbmUgdnJlZG5vc3RpKQ0KYGBge3J9DQpwb2RhY2kyJHBpdDM8LVJlY29kZShwb2RhY2kyJHBpdDMsICJjKDAsIDEsIDIpPSduaXNrbyc7IGMoMywgNCwgNSk9J3Zpc29rbyciKSAjIDUgdSAyIGthdGVnb3JpamUNCnBvZGFjaTIkcGl0Mw0KYGBgDQojIyMgU0xBSkQgNDAgIyMjDQojIyBGQUtUT1JJDQpgYGB7cn0NCmxpYnJhcnkoZm9yZWlnbikNCnBvZGFjaTQ8LWFzLmRhdGEuZnJhbWUocmVhZC5zcHNzKCJzcmVkaml2YW5qZS5zYXYiLCB1c2UudmFsdWUubGFiZWxzID0gVFJVRSkpICN1xI1pdGF2YW5qZSBwb2RhdGFrYQ0KdGFibGUocG9kYWNpNCRQT0wpICAjIGtyZWlyYW5qZSB0YWJlbGUgdnJlZG5vc3RpIHZhcmlqYWJsZSBwb2wNCmlzLmZhY3Rvcihwb2RhY2k0JFBPTCkgIyBwcm92ZXJhIGRhIGxpIGplIHZhcmlqYWJsYSBmYWt0b3IgKG5pamUgLSBGQUxTRSkNCnBvZGFjaTQkUE9MPC1mYWN0b3IocG9kYWNpNCRQT0wsIGxldmVscz1jKDEsIDIpLCBsYWJlbHM9YygibXVza2kiLCAiemVuc2tpIikpICMgcHJldHZhcmFuamUgdSBmYWt0b3Igc2Egbml2b2ltYSBtdcWha2kgaSDFvmVuc2tpLiBOYXZlZGVuaSBuaXZvaSAobGV2ZWxzKSBpIGxhYmVsZSAobGFiZWxzKS4NCnBvZGFjaTQkUE9MDQppcy5mYWN0b3IocG9kYWNpNCRQT0wpDQp0YWJsZShwb2RhY2k0JFBPTCkNCmlzLmZhY3Rvcihwb2RhY2k0JFNUQV9LQVQpICMgcG9ub3ZvIG5hIHZhcmlqYWJsaSBTVEFfS0FUIChzdGFyb3N0IGthcmF0aXN0YSkNCnRhYmxlKHBvZGFjaTQkU1RBX0tBVCkNCnBvZGFjaTQkU1RBX0tBVDwtZmFjdG9yKHBvZGFjaTQkU1RBX0tBVCwgbGV2ZWxzPWMoMSwgMiwgMywgNCksIGxhYmVscz1jKCJuYWRlIiwgImthZGV0aSIsICJqdW5pb3JpIiwgInNlbmlvcmkiKSkNCmlzLmZhY3Rvcihwb2RhY2k0JFNUQV9LQVQpDQp0YWJsZShwb2RhY2k0JFNUQV9LQVQpDQoNCmBgYA0KIyMjIFNMQUpEIDQxICMjIw0KIyMgREVTS1JJUFRJVk5BDQpgYGB7cn0NCm1lYW4ocG9kYWNpNCRPQ0VOQSwgdHJpbT0wLjA1LCBuYS5ybT1UUlVFKSANCmBgYA0KQXJpdG1ldGnEjWthIHNyZWRpbmEuIFRyaW0gdWtsYW5qYSAlIG5ham5pxb5paCBpIG5hanZpxaFpaCB2cmVkbm9zdGkgcHJlIHJhxI11bmFuamEgQVMsIG5hLnJtPVRSVUUgaXpiYWN1amUgb25lIHNhIG5lZG9zdGFqdcSHaW0gdnJlZG5vc3RpbWEgaSByYcSNdW5hIEFTLiBBa28gamUgbmEucm09RkFMU0UsIGFrbyBwb3N0b2plIG9uaSBzYSBuZWRvc3RhanXEh2ltIHZyZWRub3N0aW1hIGkgQVMgamUgTkEuIEFyZ3VtZW50IG5hLnJtIHZhxb5pIHphIHN2ZSBzbGVkZcSHZSBmdW5rY2lqZSBvc2ltIHphIHN1bW1hcnkgKHJhZGkgbmEga29tcGxldG5pbSBwb2RhY2ltYSkNCmBgYHtyfQ0Kc2QocG9kYWNpNCRPQ0VOQSwgbmEucm09VFJVRSkgI3N0YW5kYXJkbmEgZGV2aWphY2lqYQ0KdmFyKHBvZGFjaTQkT0NFTkEpICN2YXJpamFuc2ENCnNkKHBvZGFjaTQkT0NFTkEpXjIgI3JlenVsdGF0ZSB2ZcSHaW5lIGZ1bmtjaWphIG1vxb5lbW8ga29yaXN0aXRpIHUgZGFsamVtIHJhxI11bnUgKG92byBqZSBzYW1vIHByaW1lciAtIGthZGEga3ZhZHJpcmFtbyBzdGFuZGFyZG51IGRldmlqYWNpanUgZG9iaWplbW8gdmFyaWphbnN1KQ0KbWluKHBvZGFjaTQkT0NFTkEpICNtaW5pbXVtDQptYXgocG9kYWNpNCRPQ0VOQSkgI21heGltdW0NCnN1bW1hcnkocG9kYWNpNCRPQ0VOQSkNCmBgYA0KIyMjIFNMQUpEIDQyICMjIw0KIyMgVEFCRUxFDQpgYGB7cn0NCnRhYmxlKHBvZGFjaTQkUE9MKQ0KdGFibGUocG9kYWNpNCRTVEFfS0FUKQ0KdGFibGUocG9kYWNpMiRTVU1BKQ0KdGFibGUocG9kYWNpMiRTVU1BLCB1c2VOQT0ibm8iKSAjIHVzZU5BPSJubyIgLSB6YW5lbWFydWplIG5lZG9zdGFqdcSHZQ0KYGBgDQpBcmd1bWVudCB1c2VOQToNCnVzZU5BPSJpZmFueSIgLSBwcmlrYXp1amUgc2Uga29sb25hIHphIE5BIHNhbW8gYWtvIGloIGltYQ0KdXNlTkE9ImFsd2F5cyIgLSBwcmlrYXp1amUgc2Uga29sb25hIE5BIHV2ZWsNCmBgYHtyfQ0KdGFibGUocG9kYWNpMiRTVU1BLCB1c2VOQT0iaWZhbnkiKQ0KdGFibGUocG9kYWNpMiRTVU1BLCB1c2VOQT0iYWx3YXlzIikNCnRhYmxlKHBvZGFjaTIkU1VNQTIsIHVzZU5BPSJhbHdheXMiKQ0KYGBgDQpBcmd1bWVudCBleGNsdWRlIGlza2xqdcSNdWplIG5hdmVkZW5lIGthdGVnb3JpamUuIEFrbyBpaCBpbWEgdmnFoWUgbmF2b2RlIHNlIHUgc2xvdm5vbSB2ZWt0b3J1LCBuaXZvaSBzZSBzdGF2bGphanUgcG9kIG5hdm9kbmljaW1hLCBvZHZvamVuaSB6YXJlem9tLiBOcHIuIGV4Y2x1ZGU9Yygia2FkZXRpIiwgIm5hZGUiKQ0KYGBge3J9DQp0YWJsZShwb2RhY2k0JFNUQV9LQVQsIGV4Y2x1ZGU9Yygia2FkZXRpIikpDQpgYGANCiMjIFRhYmVsZSBrb250aW5nZW5jaWplDQpgYGB7cn0NCnRhYmxlKHBvZGFjaTQkUE9MLCBwb2RhY2k0JFNUQV9LQVQpIyBkdm9zbWVybmEgDQp0YWJsZShwb2RhY2k0JFBPTCwgcG9kYWNpNCRTVEFfS0FULCBwb2RhY2k0JE9DRU5BKSMgdHJvc21lcm5hDQpgYGANClByZXRob2RubyBzZSBtb8W+ZSBrb21iaW5vdmF0aSBzYSBleGNsdWRlLCB1c2VOQS4uLg0KDQojIyMgU0xBSkQgNDMgIyMjDQojIyBUQVBQTFkNClByaW1lbmp1amUgbmF2ZWRlbnUgZnVua2NpanUgcG8gbml2b2ltYSBmYWt0b3JhLiBQcnZvIHNlIG5hdm9kaSDFvmVsamVuYSB2YXJpamFibGEsIHphdGltIGZha3RvciAoZ3J1cGnFoXXEh2EpIGkgbmEga3JhanUgxb5lbGplbmEgZnVua2NpamEuIE5wci4gdSBwcnZvaiBrb21hbmRpIGFuYWxpemlyYW1vIHZhcmlqYWJsdSBPQ0VOQSwgcG8gcG9sdSwgYSBkb2JpxIdlbW8gYXJpdG1ldGnEjWt1IHNyZWRpbnUuIEFyZ3VtZW50aSDFvmVsamVuZSBmdW5rY2lqZSBtb2d1IGJpdGkgbmF2ZWRlbmkgcG9zbGUgc2FtZSBmdW5rY2lqZSwgb2R2b2plbmkgemFyZXpvbSAoa2FvIHUgZHJ1Z29qIGtvbWFuZGkpLg0KYGBge3J9DQp0YXBwbHkocG9kYWNpNCRPQ0VOQSwgcG9kYWNpNCRQT0wsIG1lYW4pIyBkcnVnaSBhcmd1bWVudCBqZSBmYWt0b3INCnRhcHBseShwb2RhY2k0JE9DRU5BLCBwb2RhY2k0JFBPTCwgbWVhbiwgdHJpbT0wLjA1KQ0KdGFwcGx5KHBvZGFjaTQkT0NFTkEsIHBvZGFjaTQkUE9MLCBzZCkNCnRhcHBseShwb2RhY2k0JE9DRU5BLCBwb2RhY2k0JFBPTCwgdmFyKQ0KdGFwcGx5KHBvZGFjaTQkT0NFTkEsIHBvZGFjaTQkUE9MLCBzZCleMg0KdGFwcGx5KHBvZGFjaTQkT0NFTkEsIHBvZGFjaTQkUE9MLCBtaW4pDQp0YXBwbHkocG9kYWNpNCRPQ0VOQSwgcG9kYWNpNCRQT0wsIG1heCkNCnRhcHBseShwb2RhY2k0JE9DRU5BLCBwb2RhY2k0JFBPTCwgc3VtbWFyeSkNCmBgYA0KIyMjIFNMQUpEIDQ0ICMjIw0KIyMgdC10ZXN0IHphIG5lemF2aXNuZSB1em9ya2UNClBydm8gxIdlbW8gcG96dmF0aSBiaWJsaW90ZWt1IGNhciB1IGtvam9qIHNlIG1vxb5lIG5hxIdpIGZ1bmtjaWphIHphIExldmVuZW92IHRlc3QgaG9tb2dlbm9zdGkgdmFyaWphbnNpIChkYSBiaXNtbyB6bmFsaSBrb2ppIHQgdGVzdCBkYSBwcmltZW5pbW8pLg0KYGBge3J9DQpsaWJyYXJ5KGNhcikgIyB1IG92b2ogYmlibGlvdGVjaSBzZSBuYWxhemkgZnVua2NpamENCmxldmVuZVRlc3QoT0NFTkEgfiBQT0wsIGRhdGE9cG9kYWNpNCkjIExldmVuZW92IHRlc3QgamVkbmFrb3N0aSB2YXJpamFuc2kNCmBgYA0KUG/FoXRvIHN1IHZhcmlqYW5zZSBob21vZ2VuZSBwcmltZW5pxIdlbW8gdmFyaWphbnR1IHphIGplZG5ha2UgdmFyaWphbnNlDQpgYGB7cn0NCnQudGVzdChPQ0VOQSB+IFBPTCwgZGF0YT1wb2RhY2k0LCB2YXIuZXF1YWw9VFJVRSkNCmBgYA0KQWtvIHZhcmlqYW5zZSBuaXN1IGhvbW9nZW5lIG9kYSBzZSBrb3Jpc3RpIGFyZ3VtZW50IHZhci5lcXVhbD1GQUxTRSBpbmHEjWUgVFJVRS4NCkluYcSNZSBkZWZpbmlzYW5qZSBtb2RlbGEgWkFWSVNOQSB+IE5FWkFWSVNOQSBqZSB1b2JpxI1hamVubyB6YSB2ZcSHaW51IChha28gbmUgaSBzdmUpIGxpbmVhcm5lIG1vZGVsZSB1IFItdS4NCg0KIyMgdC10ZXN0IHphIHphdmlzbmUgdXpvcmtlDQpEb3ZvbGpubyBqZSB2cmVkbm9zdCBhcmd1bWVudGEgcmVwZWF0ZWQgcG9zdGF2aXRpIG5hIFRSVUUNCmBgYHtyfQ0KdC50ZXN0KHBvZGFjaTQkVDFfUElUMSwgcG9kYWNpNCRUMV9QSVQyLCBkYXRhPXBvZGFjaTQsIHJlcGVhdGVkPVRSVUUpDQpgYGANCktvZCB6YXZpc25paCB1em9yYWthLCBtZXJlbmphIHNlIG5hdm9kZSBqZWRubyB6YSBkcnVnaW0uDQoNCiMjIyBTTEFKRCA0NSAjIyMNCiMjIEhpLWt2YWRyYXQgdGVzdA0KRnVua2NpamEgY2hpc3EudGVzdCBrYW8gYXJndW1lbnRlIHV6aW1hIGR2ZSB2YXJpamFibGUgaWxpIGdvdG92dSB0YWJlbHUga29udGluZ2VuY2lqZSBrcmVpcmFudSBuYSBvc25vdnUgbmppaC4NCmBgYHtyfQ0KY2hpc3EudGVzdChwb2RhY2k0JFBPTCwgcG9kYWNpNCRTVEFfS0FUKSAgIyBpbGkNCmNoaXNxLnRlc3QodGFibGUocG9kYWNpNFsxOjJdKSkgIyBkcnVnYcSNaWplIGluZGVrc2lyYW5qZSB2YXJpamFibGkgKHByZWtvIGJyb2phIGtvbG9uZSkgaWxpIA0KdGFiZWxpY2E9dGFibGUocG9kYWNpNFsxOjJdKSNwcnZvIGtyZWlyYW1vIHRhYmVsdQ0KY2hpc3EudGVzdCh0YWJlbGljYSkgI2EgemF0aW0gdGFiZWx1IHV2cnN0aW1vIGthbyBhcmd1bWVudCB1IGZ1bmtjaWp1DQpgYGANClJlenVsdGF0IG92ZSBmdW5rY2lqZSAoYSBpIHN2aWggb3N0YWxpaCkgbW/FvmVtbyBzbWVzdGl0aSB1IG9iamVrYXQga29qaSBrYXNuaWplIG1vxb5lbW8gcG96aXZhdGkuIERvZGF0aSBzdSBhcmd1bWVudGkgY29ycmVjdCBrb2ppIG96bmHEjWF2YSBkYSBsaSBqZSBwb3RyZWJubyByYWRpdGkgWWF0ZXNvdnUga29yZWtjaWp1IChUUlVFKSBpbGkgbmUgKEZBTFNFKSwgc2ltdWxhdGUucC52YWx1ZSBkYSBsaSBqZSBwb3RyZWJubyByYWRpdGkgTW9udGVDYXJsbyBzaW11bGFjaWplIGlsaSBuZSwgaSBCIGtvbGlrbyBzaW11bGlyYW5paCB1em9yYWthIGplIHBvdHJlYm5vIHVyYWRpdGkuDQpgYGB7cn0NCmhpPC1jaGlzcS50ZXN0KHRhYmxlKHBvZGFjaTQkUE9MLCBwb2RhY2k0JFNUQV9LQVQpLCBjb3JyZWN0ID0gRkFMU0UsIHNpbXVsYXRlLnAudmFsdWUgPSBUUlVFLCBCID0gMjAwMCkNCmhpICNwb3ppdmFuamVtIG9iamVrdGEgaXNwaXN1amV0ZSByZXp1bHRhdGUNCnN0cihoaSkgIyBkYSB2aWRpdGUgc3RydWt0dXJ1IG9iamVrdGEgaGkNCmBgYA0KS2FkYSBwb2dsZWRhdGUgc3RydWt0dXJ1IHZpZGl0ZSBlbGVtZW50ZSBvYmpla3RhIChpc3ByZWQgbmppaCBzdG9qaSB6bmFrICQpLiBLYXNuaWplIG1vxb5ldGUgcG96aXZhdGkgZWxlbWVudGUgdG9nIG9iamVrdGEgKGhpKSBwb2plZGluYcSNbm8sIHNtZcWhdGF0aSBpaCB1IG5vdmUgb2JqZWt0ZSwga29yaXN0aXRpIHUgZGFsamltIGFuYWxpemFtYS4uLiBPdm8gdmHFvmkgemEgc3ZlIG9iamVrdGUgKHNrb3JvKSwgbmUgc2FtbyBvbmUgbmFzdGFsZSBuYSBvc25vdnUgZnVua2NpamUgY2hpLnNxdWFyZQ0KYGBge3J9DQpoaSRvYnNlcnZlZCAjIHByaWtheml2YW5qZSBvcGHFvmVuaWggZnJla3ZlbmNpamENCmhpJGV4cGVjdGVkICMgb8SNZWtpdmFuaWgNCmhpJHJlc2lkdWFscyAjIHJlemlkdWFsYQ0KaGkkcmVzaWR1YWxzWzFdICMgcmV6aWR1YWxhIHNhbW8gemEgbXXFoWtlDQpoaSRyZXNpZHVhbHNbMl0gIyByZXppZHVhbGEgc2FtbyB6YSDFvmVuc2tlDQpoaSRzdGRyZXMgIyBzdGFuZGFyZGl6b3ZhbmloIHJlemlkdWFsYQ0KaGkkc3RhdGlzdGljICMgdnJlZG5vc3QgaGkga3ZhZHJhdGENCmhpJHAudmFsdWUgIyB6bmHEjWFqbm9zdA0KYGBgDQojIyMgU0xBSkQgNDYgIyMjDQojIyBjaGlzcS50ZXN0IGdyYWZpa29uaQ0KU3RhY2tlZCBiYXIgcGxvdA0KYGBge3J9DQpwbG90KHBvZGFjaTQkUE9MLCBwb2RhY2k0JFNUQV9LQVQpDQpgYGANCkRvZGF0ZSBzdSBsYWJlbGUgeCBvc2UgKHhsYWIpLCB5IG9zZSAoeWxhYikgaSBuYXNsb3YgZ3JhZmlrb25hIChtYWluKS4gVGVrc3QgbW9yYSBpxIdpIHBvZCBuYXZvZG5pY2ltYSAiIiBpbGkgJycuIEJvamUgc3UgZGVmaW5pc2FuZSBhcmd1bWVudG9tIGNvbCBpIG5hdm9kZSBzZSBrYW8gbnVtZXJpxI1raSB2ZWt0b3IgY29sPWMoMSwgMywgNywgOCkuIFByb21lbml0ZSBicm9qZXZlIGkgcHJvbWVuacSHZXRlIGJvamUuIE1vZ3Ugc2UgbmF2b2RpdGkgaSBrYW8gdGVrc3R1YWxuaSB2ZWt0b3IuIE5wci4gYygicmVkIiwgImRhcmsgYmx1ZSIsICJsaWdodCBncmVlbiIsICJvcmFuZ2UiKQ0KYGBge3J9DQpwbG90KHBvZGFjaTQkUE9MLCBwb2RhY2k0JFNUQV9LQVQsIHhsYWI9InBvbCIsIHlsYWI9InN0YXJvc3QiLCBjb2w9YygxLCAzLCA3LCA4KSwgbWFpbj0gInN0YXJvc3Qga2FyYXRpc3RhIHBvIHBvbHUiKQ0KcGxvdChwb2RhY2k0JFBPTCwgcG9kYWNpNCRTVEFfS0FULCB4bGFiPSJwb2wiLCB5bGFiPSJzdGFyb3N0IiwgY29sPWMoInJlZCIsICJkYXJrIGJsdWUiLCAibGlnaHQgZ3JlZW4iLCAib3JhbmdlIiksIG1haW49ICJzdGFyb3N0IGthcmF0aXN0YSBwbyBwb2x1IikNCmBgYA0KQWtvIG5hdmVkZXRlIG5lcG9zdG9qZcSHdSBib2p1IGlsaSBuYXByYXZpdGUgZ3JlxaFrdSB1IGt1Y2FuanUgZG9iacSHZXRlIHNsZWRlxId1IHBvcnVrdTogRXJyb3IgaW4gcmVjdCh4bGVmdCwgeWJvdHRvbSwgeHJpZ2h0LCB5dG9wLCBjb2wgPSBjb2wsIC4uLikgOiANCiAgaW52YWxpZCBjb2xvciBuYW1lICduYXppdiBib2plIGtvZCBrb2plIHN0ZSBwb2dyZcWhaWxpJw0KICANCiMjIyBTTEFKRCA0NyAjIyMNCiMjIFNUQU5EQVJESVpBQ0lKQQ0KU3RhbmRhcmRpemFjaWp1IHZhcmlqYWJsZSBtb8W+ZXRlIG9kcmFkaXRpIHBlxaFrZSBwbyBzbGVkZcSHb2ogZm9ybXVsaQ0KKHgtWGJhcikvU0QNCmBgYHtyfQ0KcG9kYWNpNCRaT0NFTkE8LShwb2RhY2k0JE9DRU5BIC0gbWVhbihwb2RhY2k0JE9DRU5BKSkvc2QocG9kYWNpNCRPQ0VOQSkgDQpwb2RhY2k0JFpPQ0VOQQ0KYGBgDQppbGkgemJvZyBqZWRub3N0YXZub3N0aSBzbWVzdGl0aSDFvmVsamVudSB2YXJpamFibHUgdSBvYmpla2F0IHggcGEgb25kYSBpenZyxaFpdGkgc3RhbmRhcmRpemFjaWp1DQpgYGB7cn0NCng8LXBvZGFjaTQkT0NFTkENCnBvZGFjaTQkWk9DRU5BPC0oeCAtIG1lYW4oeCkpL3NkKHgpDQpwb2RhY2k0JFpPQ0VOQQ0KYGBgDQouLi5hIHUgc3R2YXJpIHBvc3RvamkgaSBmdW5rY2lqYSBrb2phIHRvIHJhZGkNCmBgYHtyfQ0KcG9kYWNpNCRaT0NFTkEyPC1zY2FsZShwb2RhY2k0JE9DRU5BLCBjZW50ZXI9VFJVRSwgc2NhbGU9VFJVRSkNCm1lYW4ocG9kYWNpNCRaT0NFTkEyKSNBUw0Kc2QocG9kYWNpNCRaT0NFTkEyKSNTRA0KYGBgDQpBa28gamUgY2VudGVyPVRSVUUgb25kYSBzZSB4IHVtYW5qdWplIHphIEFTIChyYWRpIHNlIHR6di4gQ0VOVFJJUkFOSkUpLiBQcmFrdGnEjW5vIHNlIEFTIHBvc3RhdmxqYSBuYSAwICh1IHN0dmFyaSBuYWrEjWXFocSHZSBqYWtvIGJsaXNrbyAwKS4NCkEgYWtvIGplIHNjYWxlPVRSVUUgb25kYSBzZSB4LVhiYXIgZGVsaSBTRCwgYWtvIG5lIC0gbmnFoXRhLiBBa28gamUgVFJVRSwgb25kYSBzZSByYWRpIHN0YW5kYXJkaXphY2lqYSAocG9kIHVzbG92b20gZGEgamUgaSBjZW50ZXI9VFJVRSkNCkFyZ3VtZW50IGNlbnRlciAoemEga29saWtvIHVtYW5qaXRpIHZyZWRub3N0IHgtWGJhcikgaSBzY2FsZSAoc2EgxI1pbSBwb2RlbGl0aSkgbW9ndSBiaXRpIG51bWVyacSNa2kgdmVrdG9yaSBha28gc2UgdHJhbnNmb3JtacWhZSB2acWhZSB2YXJpamFibGkuDQpNb8W+ZSBzZSB0cmFuc2Zvcm1pc2F0aSBpIHZpxaFlIHZhcmlqYWJsaSBvZGplZG5vbQ0KYGBge3J9DQpzdGFuZDNfNSA8LXNjYWxlKHBvZGFjaTRbMzo1XSwgY2VudGVyPVRSVUUsIHNjYWxlPVRSVUUpDQpzdGFuZDNfNQ0KYGBgDQpHb3Juam9tIGtvbWFuZG9tIHN1IG9kIDMuIGRvIDUuIHZhcmlqYWJsZSBpeiBtYXRyaWNlIHBvZGF0YWthIHBvZGFjaTQgc3RhbmRhcmRpem92YW5pIGkgc21lxaF0ZW5pIHUgbm92aSBvYmpla2F0IGtsYXNlIGRhdGEuZnJhbWUgcG9kIG5heml2b20gc3RhbmQzXzUuDQoNCiMjIFRlc3Qgbm9ybWFsbm9zdGkNCg0KIyMjIEtvcmlzdGltbyBTaGFwaXJvLVdpbGsgdGVzdA0KYGBge3J9DQpzaGFwaXJvLnRlc3QocG9kYWNpNCRPQ0VOQSkNCmhpc3QocG9kYWNpNCRPQ0VOQSkNCmBgYA0KQWtvIMW+ZWxpbW8gcG8gZ3J1cGFtYQ0KYGBge3J9DQp0YXBwbHkocG9kYWNpNCRPQ0VOQSwgcG9kYWNpNCRQT0wsIHNoYXBpcm8udGVzdCkNCmBgYA0KIyMgTk9STUFMSVpBQ0lKQSAobmF2b2RubykNCk5hIGludGVybmV0dSBzZSDEjWVzdG8ga2HFvmUgZGEgc2Ugbm9ybWFsaXphY2lqYSBtb8W+ZSBvYmF2aXRpIHBvbW/Eh3UgZnVua2NpamUgc2NhbGUsIG5hIHNsZWRlxIdpIG5hxI1pbg0KYGBge3J9DQpwb2RhY2k0JE5PQ0VOQSA8LXNjYWxlKHBvZGFjaTQkT0NFTkEsIGNlbnRlcj1UUlVFLCBzY2FsZT1GQUxTRSkgIyBpcGFrIHNhbW8gQ0VOVFJJUkFOSkUNCnBvZGFjaTQkTk9DRU5BDQpoaXN0KHBvZGFjaTQkTk9DRU5BKQ0KYGBgDQoNCiMjIyBTTEFKRCA0OCAjIyMNCiMjIE5vcm1hbGl6YWNpamENClBlxaFrZS4uLiBSYW5naXJhdGkgc2tvcm92ZSBpIHByZXR2b3JpdGkgdSBwZXJjZW50aWxlICh1IHN0dmFyaSBuYW0gbmUgdHJlYmFqdSBwcm9jZW50aSB2ZcSHIHByb3BvcmNpamUpLiBaYXRpbSBuYcSHaSB6IHZyZWRub3N0IGtvam9qIG9kZ292YXJhIG9kcmXEkWVuaSBwcm9jZW5hdCBpc3BvZCBrcml2ZSBub3JtYWxuZSByYXNwb2RlbGUuDQpQcnZvIGl6cmHEjXVuYW1vIHJhbmdvdmUNCmBgYHtyfQ0KcG9kYWNpMiRSQU5HPC1yYW5rKHBvZGFjaTIkU1VNQSwgbmEubGFzdCA9ICJrZWVwIiwgdGllcy5tZXRob2QgPSAibWluIikNCnBvZGFjaTIkUkFORw0KYGBgDQpKYWtvIGplIGJpdG5vIGRhIG5hLmxhc3QgYnVkZSAia2VlcCIuIE5hIHRhaiBuYcSNaW4gc2x1xI1hamV2aSBzYSBuZWRvc3RhanXEh2ltIHZyZWRub3N0aW1hIG5hIMW+ZWxqZW5vaiB2YXJpamFibGkgZG9iaWphanUgbmVkb3N0YWp1xIdpIHJhbmcgKE5BKQ0KWmF0aW0gaXpyYcSNdW5hbW8gcGVyY2VudGlsbmkgcmFuZy4gUG9kZWxpbW8gcmFuZyBpeiBnb3JuamUga29tYW5kZSBkdcW+aW5vbSB2YXJpamFibGUgKGJyb2plbSBzbHXEjWFqZXZhIGJleiBuZWRvc3RhanXEh2loKSANCg0KYGBge3J9DQpwb2RhY2kyJFBFUkM8LShyYW5rKHBvZGFjaTIkU1VNQSwgbmEubGFzdCA9ICJrZWVwIiwgdGllcy5tZXRob2QgPSAibWluIikpL2xlbmd0aChpcy5uYShwb2RhY2kyJFNVTUEpPT1GQUxTRSkgDQpwb2RhY2kyJFBFUkMNCmBgYA0KSSBuYSBrcmFqdSwgcHJvcG9yY2lqZSBwcmV0dm9yaW1vIHUgeiBza29yb3ZlIGtvcmlzdGXEh2kgZnVua2NpanUgcW5vcm0ga29qYSB0cmHFvmkgeiB2cmVkbm9zdCBrb2phIG9kZ292YXJhIG9kcmXEkWVub2ogcHJvcG9yY2lqaSBpc3BvZCBrcml2ZSBub3JtYWxuZSByYXNwb2RlbGUNCmBgYHtyfQ0KcG9kYWNpMiROT1JNPC1xbm9ybSgocmFuayhwb2RhY2kyJFNVTUEsIG5hLmxhc3QgPSAia2VlcCIsIHRpZXMubWV0aG9kID0gIm1pbiIpKS9sZW5ndGgoaXMubmEocG9kYWNpMiRTVU1BKT09RkFMU0UpKSANCnBvZGFjaTIkTk9STQ0KYGBgDQpQcmltZXRpdGUga2FrbyBzbW8gcHJldGhvZG5lIGtvbWFuZGUgdWduamV6ZGlsaSBqZWRudSB1IGRydWd1OiBxbm9ybSgocmFuaygpL2xlbmd0aCkpDQpVcmFkacSHZW1vIGkgc3RhbmRhcmRpemFjaWp1IHJhZGkgcG9yZcSRZW5qYQ0KYGBge3J9DQpwb2RhY2kyJFo8LXNjYWxlKHBvZGFjaTIkU1VNQSwgY2VudGVyPVRSVUUsIHNjYWxlPVRSVUUpDQpgYGANCg0KIyMjIFNMQUpEIDQ5ICMjIw0KIyMgTk9STUFMSVpBQ0lKQQ0KVXBvcmVkbmkgZ3JhZmlrb24gc2lyb3ZlIHZhcmlqYWJsZSwgbm9ybWFsaXpvdmFuZSBpIHN0YW5kYXJkaXpvdmFuZQ0KYGBge3J9DQpwYXIobWZyb3c9YygxLDMpKTsgaGlzdChwb2RhY2kyJFNVTUEsIG1haW49InNpcm92aSIsIHhsYWI9TlVMTCwgY29sPTIpOyBoaXN0KHBvZGFjaTIkTk9STSwgbWFpbj0ibm9ybWFsaXpvdmFuaSIsIHhsYWI9TlVMTCwgY29sPTMpOyBoaXN0KHBvZGFjaTIkWiwgbWFpbj0ieiBza29yb3ZpIiwgeGxhYj1OVUxMLCBjb2w9NCkNCmBgYA0KQ3J0YW1vIGdyYWZpa29uZSBkYSBpaCB1cG9yZWRpbW8uIFZpZGltbyBkYSAibmFqbm9ybWFsbmlqZSIgaXpnbGVkYSBvbmFqIGtvamkgc21vIGRvYmlsaSBwcm9jZWR1cm9tIG5vcm1hbGl6YWNpamUga29qdSBzbW8gbmFwaXNhbGkuDQpLb21hbmRlIHphIGNydGFuamUgc3UgZGF0ZSB1IGplZG5vbSByZWR1LCBvZGVsamVuZSBzYSA7Lg0KSGlzdG9ncmFtZSBjcnRhbW8gZnVua2Npam9tIGhpc3QoKS4gTmFzbG92ZSBpIGJvamUgc21vIGRlZmluaXNhbGkgYXJndW1lbnRpbWEgbWFpbiwgeGxhYiwgaSBjb2wuDQpGdW5rY2lqYSBwYXIgZGVmaW5pxaFlIGNydGFuamUgdmnFoWUgZ3JhZmlrb25hIG9kamVkbm9tLiBUYcSNbmlqZSB0byByYWRpIG9wY2lqYSBtZnJvdyBrb2phIHByb3N0b3IgemEgY3J0YW5qZSBncmFmaWtvbmEgZGV2aW5pxaFlIGthbyBtYXRyaWN1LiBjKDE6Mykgem5hxI1pIGRhIMSHZW1vIHByb3N0b3IgemEgY3J0YW5qZSBwb2RlbGl0aSB1IDEgcmVkLCAzIGtvbG9uZQ0KDQpEYSBiaXN0ZSBwcm9zdG9yIHphIGNydGFuamUgZ3JhZmlrb25hIHZyYXRpbGkgbmEgbm9ybWFsYW4gaXNrb3Jpc3RpdGUgZnVua2NpamUNCmBgYHtyfQ0KcGFyKG1mcm93PWMoMSwxKSkgI2lsaQ0KZGV2Lm9mZigpIA0KYGBgDQpwYXIobWZyb3c9YygxLDEpKSBwb2RlxaFhdmEgcHJvc3RvciB6YSBjcnRhbmplIG5hIDEgcmVkIGkgMSBrb2xvbnUsIGEgZGV2Lm9mZigpIHVrbGFuamEgcG9zdG9qZcSHaSBwcm9zdG9yIHphIGNydGFuamUuDQoNClByb3ZlcmnEh2VtbyBub3JtYWxub3N0IG5vcm1hbGl6b3ZhbmUgdmFyaWphYmxlIFNoYXBpcm8tV2lsayB0ZXN0b20gaSBpc3Bpc2F0aSBuamVudSBhcml0bWV0acSNa3Ugc3JlZGludSBpIHN0YW5kYXJkbnUgZGV2aWphY2lqdQ0KYGBge3J9DQpzaGFwaXJvLnRlc3QocG9kYWNpMiROT1JNKTsgbWVhbihwb2RhY2kyJE5PUk0sIG5hLnJtPVQpOyBzZChwb2RhY2kyJE5PUk0sbmEucm09VCkNCg0KYGBgDQpOb3JtYWxuYSBqZSAoamVkdmEpDQoNCg0KIyMjIFNMQUpEIDUwICMjIw0KIyMgTk9STUFMSVpBQ0lKQQ0KTW/FvmUgc2Ugc21lc3RpdGkgdSBmdW5rY2lqdQ0KYGBge3J9DQpub3JtYWxpenVqIDwtIGZ1bmN0aW9uKG5lbm9ybWFsbmEpIHsNCnFub3JtKChyYW5rKG5lbm9ybWFsbmEsIG5hLmxhc3QgPSAia2VlcCIsIHRpZXMubWV0aG9kID0gIm1pbiIpKS9sZW5ndGgoaXMubmEobmVub3JtYWxuYSk9PUZBTFNFKSkNCn0NCmBgYA0KTmFwcmF2aWxpIHNtbyBvYmpla2F0IGtvamkgc2Ugem92ZSBub3JtYWxpenVqLiBPbiBqZSBrbGFzZSAiZnVuY3Rpb24iIGkgdXppbWEgamVkYW4gYXJndW1lbnQgIm5lbm9ybWFsbmEiLCBhIHRvIGplIHZhcmlqYWJsYSBrb2p1IGplIHBvdHJlYm5vIG5vcm1hbGl6b3ZhdGkuIE9ubyDFoXRvIMSHZSBmdW5rY2lqYSByYWRpdGkgbmFsYXppIHNlIGl6bWXEkXUgdml0acSNYXN0aWggemFncmFkYSB7fQ0KRGFrbGUgZnVua2NpanUgZGVmaW5pxaFlbW8gc2EgaW1lPC1mdW5jdGlvbihhcmd1bWVudGkga29qZSB1emltYSkge2tvbWFuZGUga29qZSBpenZyxaFhdmF9LiBUYWtvIGRlZmluaXNhbnUgZnVua2NpanUgamUgcG90cmVibm8gamVkbm9tIHBva3JlbnV0aSwgYSBrYXNuaWplIGplIHBveml2YW1vIG5qZW5pbSBpbWVub20gaSBuYXZvxJFlbmplbSBhcmd1bWVuYXRhLg0KTmEgcHJpbWVyDQpgYGB7cn0NCm5vcm1hbGl6dWoocG9kYWNpMiRTVU1BKSANCiMgb2Rub3Nubw0KcG9kYWNpMiROT1JNPC1ub3JtYWxpenVqKHBvZGFjaTIkU1VNQSkNCmBgYA0KR3JhZmlrb25pIHBvbm92bw0KYGBge3J9DQpwYXIobWZyb3c9YygxLDMpKTsgaGlzdChwb2RhY2kyJFNVTUEsIG1haW49InNpcm92aSIsIHhsYWI9TlVMTCwgY29sPTIpOyBoaXN0KHBvZGFjaTIkTk9STSwgbWFpbj0ibm9ybWFsaXpvdmFuaSIsIHhsYWI9TlVMTCwgY29sPTMpOyBoaXN0KHBvZGFjaTIkWiwgbWFpbj0ieiBza29yb3ZpIiwgeGxhYj1OVUxMLCBjb2w9NCk7IA0KcGFyKG1mcm93PWMoMSwxKSkgDQpgYGANCiMjIyBTTEFKRCA1MSAjIyMNCiMjIEFOT1ZBDQpNb8W+ZSBzZSBpenZyxaFpdGkgbmEgdmnFoWUgbmHEjWluYSBpIGtvcmlzdGl0aSB2acWhZSBmdW5rY2lqYS4gTmEgcHJpbWVyIGZ1bmtjaWphIGxtIChsaW5lYXJuaSBtb2RlbGkpDQpgYGB7cn0NCmFuYWxpemEudmFyPC1sbShPQ0VOQSB+IFNUQV9LQVQsIGRhdGE9cG9kYWNpNCkgDQpgYGANCk92byBqZSBqZWRub3NtZXJuYSBhbmFsaXphIHZhcmlqYW5zZSBpIGZvcm1hdCBkZWZpbmlzYW5qYSBqZTogWkFWSVNOQSB+IE5FWkFWSVNOQS4gTmV6YXZpc25hIG1vcmEgYml0aSBmYWt0b3IsIGEgemF2aXNuYSBudW1lcmnEjWthLiBBcmd1bWVudCBkYXRhIGplIG9iYXZlemFuIGkgcHJlZHN0YXZsamEgbWF0cmljdSBwb2RhdGFrYSBrb2p1IGtvcmlzdGl0ZSAoYWtvIHN1IHZhcmlqYWJsZSBkZW8gZGF0YS5mcmFtZSBvYmpla3RhLCBhIG5lIG1vcmFqdSBiaXRpIC0gbW9ndSBiaXRpIGkgdmVrdG9yaSkuDQpsbSBkYWplIGl6bGF6IGthbyByZWdyZXNpamEgKMWhdG8gamUgaXN0aSBtb2RlbCkuDQpgYGB7cn0NCnN1bW1hcnkoYW5hbGl6YS52YXIpDQphbmFsaXphLnZhcg0KYGBgDQoNCkludGVyY2VwdCBqZSBBUyBwcnZlIGthdGVnb3JpamUsIGEgQVMgb3N0YWxpaCBrYXRlZ29yaWphIHN1IGludGVyY2VwdCArIHBhcmFtZXRhciBrYXRlZ29yaWplICg0LjU1MTcyKzAuMDY1NjcuLi4pDQpQcm92ZXJpdGUNCmBgYHtyfQ0KdGFwcGx5KHBvZGFjaTQkT0NFTkEsIHBvZGFjaTQkU1RBX0tBVCwgbWVhbikNCmBgYA0KSWxpIGRydWdhIHZhcmlqYW50YSB1cG90cmVib20gZnVua2NpamUgYW92DQpgYGB7cn0NCmFuYWxpemEudmFyPC1hb3YoT0NFTkEgfiBTVEFfS0FULCBkYXRhPXBvZGFjaTQpDQpzdW1tYXJ5KGFuYWxpemEudmFyKQ0KYW5vdmEoYW5hbGl6YS52YXIpDQpgYGANCg0KIyMgVmnFoWVzbWVybmEgQU5PVkENCmBgYHtyfQ0KYW5hbGl6YS52YXIyPC1sbShPQ0VOQSB+IFNUQV9LQVQgKyBQT0wgKyAoUE9MKlNUQV9LQVQpLCBkYXRhPXBvZGFjaTQpDQpzdW1tYXJ5KGFuYWxpemEudmFyMikNCmFub3ZhKGFuYWxpemEudmFyMikNCmBgYA0KRm9ybWF0OiBaQVZJU05BIH4gR0xBVk5JX0VGRUtBVDEgKyBHTEFWTklfRUZFS0FUMiArIElOVEVSQUtDSUpFDQpJbnRlcmFrY2lqYTogDQpBa28gZGVmaW5pxaFlbW8gbmEgc2xlZGXEh2kgbmHEjWluOiBHTEFWTklfRUZFS0FUMSAqIEdMQVZOSV9FRkVLQVQyIGRvYmlqYWp1IHNlIGkgc3ZlIGludGVyYWtjaWplIGkgZ2xhdm5pIGVmZWt0aS4gVSBnb3JuamVtIHByaW1lcnUgdG8gYmkgYmlsbw0KYGBge3J9DQpsbShPQ0VOQSB+IFBPTCpTVEFfS0FULCBkYXRhPXBvZGFjaTQpDQpgYGANCkFrbyBkZWZpbmnFoWVtbyBuYSBzbGVkZcSHaSBuYcSNaW46IEdMQVZOSV9FRkVLQVQxIDogR0xBVk5JX0VGRUtBVDIgZG9iaWphanUgc2Ugc2FtbyBpbnRlcmFrY2lqZSwgYWxpIG5lIGkgZ2xhdm5pIGVmZWt0aS4gVSBnb3JuamVtIHByaW1lcnUgdG8gYmkgYmlsbzogbG0oT0NFTkEgfiBQT0w6U1RBX0tBVCwgZGF0YT1wb2RhY2k0KS4gVGFkYSBzZSBnbGF2bmkgZWZla3RpIG1vcmFqdSBla3NwbGljaXRubyB6YWRhdGksIGEgdG8gem5hxI1pIGRhIG5lIG1vcmF0ZSB1IG1vZGVsIHN0YXZpdGkgc3ZlIGdsYXZuZSBlZmVrdGUuIE5hIHByaW1lcg0KYGBge3J9DQpsbShPQ0VOQSB+IFBPTCsoUE9MOlNUQV9LQVQpLCBkYXRhPXBvZGFjaTQpDQpgYGANCiMjIyBTTEFKRCA1MiAjIyMNCiMjIFZpxaFlc21lcm5hIEFOT1ZBIDINCklzdG8gdmHFvmkgaSB6YSBhb3YgZnVua2NpanUNCmBgYHtyfQ0KYW5hbGl6YS52YXIyPC1hb3YoT0NFTkEgfiBTVEFfS0FUICsgUE9MICsgKFBPTCpTVEFfS0FUKSwgZGF0YT1wb2RhY2k0KQ0Kc3VtbWFyeShhbmFsaXphLnZhcjIpDQphbmFsaXphLnZhcjM8LWFvdihPQ0VOQSB+IFBPTCpTVEFfS0FULCBkYXRhPXBvZGFjaTQpDQojIE9wZXJhdG9yICogdHJhxb5pIGdsYXZuZSBlZmVrdGUgaSBpbnRlcmFrY2lqZQ0Kc3VtbWFyeShhbmFsaXphLnZhcjMpDQphbmFsaXphLnZhcjQ8LWFvdihPQ0VOQSB+IFBPTDpTVEFfS0FULCBkYXRhPXBvZGFjaTQpDQojIE9wZXJhdG9yIDogdHJhxb5pIHNhbW8gaW50ZXJha2NpanUNCnN1bW1hcnkoYW5hbGl6YS52YXI0KQ0KYW5vdmEoYW5hbGl6YS52YXI0KQ0KYGBgDQojIyBQb3N0LWhvYyB0ZXN0b3ZpDQpgYGB7cn0NClR1a2V5SFNEKGFuYWxpemEudmFyNCkNCmBgYA0KR3JhZmlrDQpgYGB7cn0NCndpdGgocG9kYWNpNCwgaW50ZXJhY3Rpb24ucGxvdChTVEFfS0FULCBQT0wsIE9DRU5BLCBjb2w9MjozLCBsd2Q9MikpDQpgYGANCndpdGggc2x1xb5pIGRhIHNrcmF0aW1vIGt1Y2FuamUNCk1vZ2xvIGJpIGkgaW50ZXJhY3Rpb24ucGxvdChTVEFfS0FULCBQT0wsIE9DRU5BLCB5bGltPWMoNC40LCA0LjkpLCBjb2w9MjozLCBsd2Q9MiksIGFsaSBiaSBwcmUgc3Zha2UgdmFyaWphYmxlIHRyZWJhbG8ga3VjYXRpIHBvZGFjaTQkIC0gb3Zha28gdSB3aXRoKHBvZGFjaTQsICAgKSB1bWV0bmVtbyBmdW5rY2lqdSBpIFIgem5hIGRhIHN2ZSB0cmViYSByYWRpdGkgbmEgb2JqZWt0dSBwb2RhY2k0DQpBcmd1bWVudCBsd2QgZGVmaW5pxaFlIGRlYmxqaW51IGxpbmlqZSAobWVuamFqdGUgdnJlZG5vc3RpIGRhIHZpZGl0ZSBlZmVrYXQpLg0KDQojIyMgU0xBSkQgNTMgIyMjDQojIyBBTk9WQSDigJMgbG0NCkpvxaEgbWFsbyBwb3N0LWhvYyB0ZXN0b3ZhDQpQcnZvIGZpdHVqZW1vIG1vZGVsDQpgYGB7cn0NCmFuYWxpemEudmFyNTwtbG0oT0NFTkEgfiBTVEFfS0FUICsgUE9MICsgKFBPTCpTVEFfS0FUKSwgZGF0YT1wb2RhY2k0KQ0Kc3VtbWFyeShhbmFsaXphLnZhcjUpDQphbm92YShhbmFsaXphLnZhcjUsIHRlc3Q9IkYiKQ0KYGBgDQpaYXRpbSBpbnN0YWxpcmFtbyBpIHXEjWl0YW1vIHBha2V0IGVtbWVhbnMsIGkgb2RyYWRpbW8gcG9zdC5ob2MgdGVzdG92ZS4gUHJ2aSBhcmd1bWVudCBqZSBvYmpla2F0IHUga29qZW0gc2UgbmFsYXppIGZpdG92YW4gbW9kZWwgQU5PVkEuIFphdGltIGRlZmluacWhZW1vIGtvamEgcG9yZcSRZW5qYSDFvmVsaW1vIGkga29qaW0gbWV0b2RvbS4NCmBgYHtyfQ0KcF9sb2FkKGVtbWVhbnMpDQplbW1lYW5zKGFuYWxpemEudmFyNSwgcGFpcndpc2V+U1RBX0tBVCpQT0wsIGFkanVzdD0ic2NoZWZmZSIpICMgcHJpbWVyIGplIHphIHN2ZSBlZmVrdGUNCmVtbWVhbnMoYW5hbGl6YS52YXI1LCBwYWlyd2lzZX5TVEFfS0FUOlBPTCwgYWRqdXN0PSJzY2hlZmZlIikgIyBzYW1vIHphIGludGVyYWtjaWp1DQplbW1lYW5zKGFuYWxpemEudmFyNSwgcGFpcndpc2V+U1RBX0tBVCwgYWRqdXN0PSJ0dWtleSIpICMgb3ZkZSBqZSB6YSBnbGF2bmkgZWZla2F0IC0gIGEga29yacWhxIdlbiBqZSB0dWtleQ0KDQpgYGANCg0KIyMjIFNMQUpEIDU0ICMjIw0KIyMgS09SRUxBQ0lKRQ0KRnVua2Npam9tIGNvciBtb8W+ZW1vIGRvYml0aSBtYXRyaWN1IGtvcmVsYWNpamEgdmnFoWUgdmFyaWphYmxpDQpgYGB7cn0NCmNvcihwb2RhY2k0WzM6OF0pICMgbWF0cmljYSBrb3JlbGFjaWphDQpjb3IocG9kYWNpNCRUMV9QSVQxLCBwb2RhY2k0JFQxX1BJVDMpICMgZHZlIHZhcmlqYWJsZQ0Kd2l0aChwb2RhY2k0LCBjb3IoVDFfUElUMiwgVDFfUElUMywgbWV0aG9kPSJwZWFyc29uIikpICNza3JhxIdlbm8gcGlzYW5qZQ0KYGBgDQpNZXRob2QgbW/FvmUgYml0aSBpICJzcGVhcm1hbiIgaSAia2VuZGFsbCINCkZ1bmtjaWpvbSBjb3IudGVzdCBkb2JpamFtbyBpIHpuYcSNYWpub3N0IGtvcmVsYWNpamEsIGFsaSBtb8W+ZSBzZSByYcSNdW5hdGkgc2FtbyB6YSBkdmUgdmFyaWphYmxlDQpgYGB7cn0NCmNvci50ZXN0KHBvZGFjaTQkVDFfUElUMSwgcG9kYWNpNCRUMV9QSVQzKSAjIGR2ZSB2YXJpamFibGUNCmBgYA0KRnVua2NpamEgcG9kIG5heml2b20gY29yci50ZXN0IChpbWEgamVkbm8gciB2acWhZSBuZWdvIHByZXRob2RuYSBmdW5rY2lqYSkgaXogcGFrZXRhIHBzeWNoIG1vxb5lIHRvIGkgbmEgbWF0cmljYW1hDQpgYGB7cn0NCnBfbG9hZChwc3ljaCkNCmNvcnIudGVzdChwb2RhY2k0WzM6OF0pDQpwcmludChjb3JyLnRlc3QocG9kYWNpNFszOjhdKSwgc2hvcnQgPSBGQUxTRSkNCg0KYGBgDQoNCiMjIyBTTEFKRCA1NSAjIyMNCiMjIEdSQUZJS09OSQ0KYGBge3J9DQpoaXN0KHBvZGFjaTQkT0NFTkEpDQpoaXN0KHBvZGFjaTIkU1VNQSkNCmBgYA0KQm9qYSArIG5hc2xvdiBncmFmaWtvbmEgKyBsYWJlbGEgWCBvc2UNCmBgYHtyfQ0KaGlzdChwb2RhY2kyJFNVTUEsIGNvbD0zLCBtYWluPSJVS1VQQU4gU0tPUiIsIHhsYWI9IlNVTUEiKQ0KYGBgDQpTdmUgdG8gKyBZIG9zYQ0KYGBge3J9DQpoaXN0KHBvZGFjaTIkU1VNQSwgY29sPTMsIG1haW49IlVLVVBBTiBTS09SIiwgeGxhYj0iU1VNQSIsIHlsYWI9ImZyZWt2ZW5jaWphIikgDQpgYGANCkJveHBsb3QgaSBzdGVtJmxlYWYNCmBgYHtyfQ0KYm94cGxvdChwb2RhY2kyJFNVTUEpDQpzdGVtKHBvZGFjaTIkU1VNQSkNCmBgYA0KUHJvYmFqdGUgZGEgZG9kYXRlIG5heml2ZSBvc2EgaSBwcm9tZW5pdGUgYm9qdSBuYSBib3hwbG90dQ0KDQpLYWRhIHphZGF0ZSBrb21hbmR1IHBsb3QgemEgZHZlIHZhcmlqYWJsZSBvZCBrb2ppaCBqZSBwcnZhIGZha3RvciBhIGRydWdhIG51bWVyacSNa2EsIGRvYmnEh2V0ZSBkdmEgYm94cGxvdGENCmBgYHtyfQ0KcGxvdChwb2RhY2k0JFBPTCwgcG9kYWNpNCRPQ0VOQSkNCmBgYA0KQWtvIG9icm5ldGUgcmVkb3NsZWQgdmFyaWphYmxpIGRvYmnEh2V0ZSBzY2F0dGVyIGRpamFncmFtDQpgYGB7cn0NCnBsb3QocG9kYWNpNCRPQ0VOQSwgcG9kYWNpNCRQT0wpIA0KYGBgDQpTY2F0dHRlciBqZSBsb8WhIHBvxaF0byBqZSBwb2wgYmluYXJuYSwgYSBvY2VuYSBwb2xpdG9tbmEgc2EgbWF4LiA1IG5pdm9hIA0KYGBge3J9DQpwbG90KHBvZGFjaTQkT0NFTkEsIHBvZGFjaTQkUE9MLCB5YXhwPWMoMSwgMiwgMSksIHhheHA9YygxLDUsNCkpDQpgYGANCnlheHAgaSB4YXhwIG9kcmXEkXVqdSBtaW5pbXVtLCBtYWtzaW11bSBpIGJyb2ogbml2b2EgKHRpbSByZWRvbSkgc2thbGEgWCBpIFkNCg0KUmVkb3NsZWQgdmFyaWphYmxpIHUgZnVua2NpamkgcGxvdCBvZHJlxJF1amUgdGlwIGdyYWZpa29uYSwgYWtvIGplIFggZmFrdG9yIG9uZGEgZG9iaWphbW8gYm94cGxvdG92ZSwgdSBwcm90aXZub20gc2NhdHRlci4NCg0KQWtvIHNlIG92YWtvIGRlZmluacWhZSwgcmVkb3NsZWQgbmlqZSBiaXRhbiAoZG9iaWphbW8gYm94cGxvdCkuDQpgYGB7cn0NCnBsb3QocG9kYWNpNCRPQ0VOQSB+IHBvZGFjaTQkUE9MKQ0KYGBgDQojIyMgU0xBSkQgNTYgIyMjDQojIyDEjFVWQU5KRSBHUkFGSUtBIFUgREFUT1RFS1UNClphIHBvxI1ldGFrLCBrcmVpcmHEh2VtbyBkdmUgdmFyaWphYmxlIChYIGkgWSkgaXp2dcSNZW5lIGl6IG5vcm1hbG5lIHJhc3BvZGVsZQ0KYGBge3J9DQpYPXJub3JtKDEwMCwgMzAsIDgpOyBZPXJub3JtKDEwMCwgMjUsIDgpICMga3JlaXJhbmplIGR2ZSB2YXJpamFibGUgaXogbm9ybWFsbmUgcmFzcG9kZWxlDQpgYGANClN2YWthIHNhIHBvIDEwMCBpc3BpdGFuaWthIChwcnZpIGFyZ3VtZW50KSwgWCBzYSBBUyAzMCAoZHJ1Z2kgYXJndW1lbnQpIGkgU0QgOCAodHJlxIdpIGFyZ3VtZW50KSwgYSBZIHNhIEFTIDI1IGkgU0QgOA0KRnVua2NpamEgcm5vcm0gcHJhdmkgc2x1xI1ham51IHZhcmlqYWJsdSBpeiBub3JtYWxuZSBkaXN0cmlidWNpamUgc2EgYXJndW1lbnRpbWEgKG4sIEFTIGkgU0QpDQpDcnRhbmplIHUgcG5nIGZhamwNCmBgYHtyfQ0KcG5nKGZpbGVuYW1lID0gInNrYXRlcjEucG5nIiwgd2lkdGggPSA4MDAsIGhlaWdodCA9IDgwMCk7ICBwbG90KFgsIFkpOyANCmRldi5vZmYoKQ0KYGBgDQpEYWplbW8gbmF6aXYgZGF0b3Rla2UsIMWhaXJpbnUgaSB2aXNpbnUgdSBwaXhlbGltYS4NClphcGlzdWplbW8gdSBQTkcgLSBuZSBwb2phdmxqdWplIHNlIHByaWtheiBwb2phdmxqdWplIHNlIGRhdG90ZWthIHUgcmFkbm9tIGRpcmVrdG9yaWp1bXUgKHBvZ2xlZGFqdGUpLg0KDQpDcnRhbmplIHUgVElGIGZhamwNCmBgYHtyfQ0KdGlmZihmaWxlbmFtZSA9ICJza2F0ZXIxLnRpZmYiLCB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gODAwKTtwbG90KFgsIFkpOyANCmRldi5vZmYoKQ0KYGBgDQpaYXBpc3VqZW1vIHUgVElGRiAtIG5lIHBvamF2bGp1amUgc2UgcHJpa2F6DQpLVkFMSVRFVCBOSUpFIFpBIMWgVEFNUFUNCg0KIyMjIFNMQUpEIDU3DQoNCiMjIFRJRiAzMDAqMzAwDQoNCkFyZ3VtZW50b20gcmVzIGRlZmluacWhZW1vIHJlem9sdWNpanUgc2xpa2UuIDMwMCBqZSBkb3ZvbGpubyB6YSDFoXRhbXB1LiBBcmd1bWVudG9tIHVuaXRzIGRlZmluacWhZW1vIG1lcm5lIGplZGluaWNlIHUga29qaW0gc3UgZGVmaW5pc2FuaSB2aXNpbmEgaSDFoWlyaW5hLiBjZXgubWFpbiwgY2V4LmxhYiBpIGNleC5heGlzIGRlZmluacWhdSB2ZWxpxI1pbnUgc2xvdmEgKGdsYXZub2cgbmFzbG92YSwgbGFiZWxhIGkgb3NhKS4gMSBqZSBub3JtYWxuYSB2ZWxpxI1pbmEuIFZyZWRub3N0aSBwcmVrbyAxIHpuYcSNZSB1dmXEh2FuamUgZm9udGEsIGEgbWFuamUgb2QgMSB1bWFuamVuamUuDQpgYGB7cn0NCnRpZmYoZmlsZW5hbWUgPSAic2thdGVyMzAwLnRpZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSx1bml0cyA9ICJjbSIsIHBvaW50c2l6ZSA9IDYsIGJnID0gIndoaXRlIiwgcmVzID0gMzAwLCByZXN0b3JlQ29uc29sZSA9IFRSVUUpOyBwbG90KFgsIFksIGNvbD0zLCBsd2QgPSAxLCBsdHk9MiwgeGxhYiA9ICJ2YXJpamFibGEgWCIsIHlsYWIgPSAidmFyaWphYmxhIFkiLCBtYWluPSJza2F0ZXIgMzAwIiwgY2V4Lm1haW4gPSAxLjUsIGNleC5sYWIgPSAxLjMsIGNleC5heGlzID0gMS4xKTsgDQpkZXYub2ZmKCkNCmBgYA0KU3ZlIGtvbWFuZGUgbW9yYWp1IGJpdGkgdSBqZWRub2ogbGluaWppIG9kdm9qZW5lIHNhICI7IiBpbGkgdSBvZHZvamVuaW0gbGluaWphbWEgYWxpIGl6dnLFoWVuZSBpc3RvdnJlbWVubyAoaXN0b3ZyZW1lbm8gc2EgZGV2Lm9mZigpKQ0KQml0bm8gamUgZGEgcmVzIGJ1ZGUgYmFyIDMwMA0KDQojIyMgU0xBSkQgNTggIyMjDQoNCiMjIFNwYWphbmplIHBvZGF0YWthIChtZXJnZSkNClNwYWphbmplIGR2ZSB0YWJlbGUgcG8ga2xqdcSNbmltIHZhcmlqYWJsYW1hLg0KVcSNaXRhxIdlbW8gZHZlIG1hdHJpY2UgcG9kYXRha2ENCmBgYHtyfQ0KbGV2aTwtcmVhZC50YWJsZSgiaW1lbmEudHh0IiwgaGVhZGVyPVRSVUUsIHNlcD0iLCIsIHF1b3RlID0gIlwiJyIsIGRlYyA9ICIuIikNCmRlc25pPC1yZWFkLnRhYmxlKCJvZGdvdm9yaTIudHh0IiwgaGVhZGVyPVRSVUUsIHNlcD0iLCIsIHF1b3RlID0gIlwiJyIsIGRlYyA9ICIuIikNCmBgYA0KT3ZlIGR2ZSB0YWJlbGUgZGVsZSB2YXJpamFibHUgSU5ERVgga29qYSBpZGVudGlmaWt1amUgc2x1xI1hamV2ZSBpIHBvbW/Eh3Uga29qZSBpaCBtb8W+ZW1vIHNwb2ppdGkga29yaXN0ZcSHaSBrb21hbmR1IG1lcmdlLg0KYGBge3J9DQpzdmU8LW1lcmdlKGxldmksIGRlc25pLCBieT0iSU5ERVgiKSMgc3BvamnEh2UgZHZhIHNldGEgcG9kYXRha2EsIGFsaSBzYW1vIG9uZSBzbHXEjWFqZXZlIGtvamkgc2UgbmFsYXplIHUgb2JlIG1hdHJpY2UNCnN2ZQ0Kc3ZlPW1lcmdlKGxldmksIGRlc25pLCBhbGw9VFJVRSwgYnk9IklOREVYIikjIHNwb2ppxIdlIGR2YSBzZXRhIHBvZGF0YWthLCBhIHUgcG9kYWNpbWEgxIdlIHNlIG5hbGF6aXRpIGkgc3ZpIG9uaSBpeiBvYmEgc2V0YSBrb2ppIG5lbWFqdSBwb2RhdGtlIHUgb2JhIHNldGEgKG5hbGF6ZSBzZSBzYW1vIHUgamVkbm9qIG9kIG1hdHJpY2EpIA0Kc3ZlDQpgYGANCg0KU3ZpIGl6IGRhdGEuZnJhbWVhIGxldmkgdWxhemUsIMSNYWsgaSBha28gbmVtYWp1IHBhcmEgdSBkYXRhLmZyYW1lIGRlc25pLg0KT25pIGl6IGRhdGEuZnJhbWUgZGVzbmkgdWxhemUgc2FtbyBha28gaW1hanUgcGFyYSB1IGxldmkuDQpgYGB7cn0NCnN2ZTwtbWVyZ2UobGV2aSwgZGVzbmksIGFsbC54PVRSVUUsIGJ5PSJJTkRFWCIpDQpzdmUNCmBgYA0KT2JybnV0YSBzaXR1YWNpamENCmBgYHtyfQ0Kc3ZlPC1tZXJnZShsZXZpLCBkZXNuaSwgYWxsLnk9VFJVRSwgYnk9IklOREVYIikNCnN2ZQ0KYGBgDQoNCiMjIyBTTEFKRCA1OSAjIyMNCiMjIExERiA8LSBXREYNClByb21lbmEgaXogV2lkZSBEYXRhIEZvcm1hdGEgdSBMb25nIERhdGEgRm9ybWF0IGkgb2JybnV0by4NCkthZGEgaW1hbW8gcG9ub3ZsamVuYSBtZXJlbmphLCBwb2RhY2kgbW9ndSBiaXRpIHphcGlzYW5pIHUgV2lkZSBkYXRhIGZvcm1hdHUgKHN2YWtvIG1lcmVuamUgamUgcG9zZWJuYSB2YXJpamFibGEpIGlsaSB1IExvbmcgZGF0YSBmb3JtYXR1IChzdmEgbWVyZW5qYSBzdSB1IGplZG5vaiB2YXJpamFibGksIGFsaSBwb3N0b2ppIHZhcmlqYWJsYSBrb2phIGlkZW50aWZpa3VqZSBtZXJlbmplKS4gT3ZvIGplIGJpdG5vIHphdG8gxaF0byBzbW8gbmF2aWtsaSBkYSBwb2RhdGtlIHphcGlzdWplbW8gdSBXaWRlIGRhdGEgZm9ybWF0dSwgYSBtbm9nZSBhbmFsaXplIHNhIHBvbm92bGplbmltIG1lcmVuamltYSB1IFItdSB6YWh0ZXZhanUgTG9uZyBkYXRhIGZvcm1hdC4NClXEjWl0YcSHZW1vIHBvZGF0a2UgdSBXaWRlIGZvcm1hdHUNCmBgYHtyfQ0KcG9kYWNpLndpZGUgPC0gcmVhZC50YWJsZShoZWFkZXI9VCwgdGV4dD0nDQppc3BpdGFuaWsgcG9sIG1lcmVuamUxIG1lcmVuamUyIG1lcmVuamUzDQoxICAgIE0gICAgICA3LjkgICAxMi4zICAgICAxMC43DQoyICAgIFogICAgICA2LjMgICAxMC42ICAgICAxMS4xDQozICAgIFogICAgICA5LjUgICAxMy4xICAgICAxMy44DQo0ICAgIE0gICAgIDExLjUgICAxMy40ICAgICAxMi45DQonKQ0KYGBgDQpLb2xvbmEgc2Egc3ViamVrdGltYSAoaWRlbnRpZmlrYWNpam9tIHN1Ympla3RhKSBtb3JhIGJpdGkgdGlwYSAiZmFjdG9yIg0KYGBge3J9DQpwb2RhY2kud2lkZSRpc3BpdGFuaWsgPC0gZmFjdG9yKHBvZGFjaS53aWRlJGlzcGl0YW5paykNCmBgYA0KS29yaXN0acSHZW1vIHBha2V0IHJlc2hhcGUNCmBgYHtyfQ0KcF9sb2FkKHJlc2hhcGUpDQpgYGANClBvZGF0a2UgxIdlbW8gcHJldHZvcml0aSB1IExvbmcgZm9ybWF0IGkgc21lc3RpdGkgdSBvYmpla2F0IG5vdmlwb2RhY2kubG9uZw0KYGBge3J9DQpub3ZpcG9kYWNpLmxvbmc8LXJlc2hhcGUocG9kYWNpLndpZGUsIGRpcmVjdGlvbj0nbG9uZycsIHZhcnlpbmc9YygnbWVyZW5qZTEnLCAnbWVyZW5qZTInLCAnbWVyZW5qZTMnKSwgdGltZXZhcj0nbWVyZW5qZScsIHRpbWVzPWMoJzEnLCAnMicsICczJyksIHYubmFtZXM9Yygnc3Rhc21vbWVyaWxpJyksIGlkdmFyPWMoImlzcGl0YW5payIsICJwb2wiKSkNCm5vdmlwb2RhY2kubG9uZw0KYGBgDQpBcmd1bWVudGk6DQpQcnZpIGFyZ3VtZW50IHN1IHBvZGFjaSBrb2plIMW+ZWxpbW8gZGEgcHJldHZvcmltbw0KZGlyZWN0aW9uOiBkYSBsaSBwcmV0dmFyYW1vIHUgbG9uZyBpbGkgd2lkZQ0KdmFyeWluZzogdmFyaWphYmxlIHUga29qaW1hIHN1IHBvbm92bGplbmEgbWVyZW5qYQ0KaWQudmFyczogdmFyaWphYmxlIHphIGlkZW50aWZpa2FjaWp1IGlzcGl0YW5pa2EgLSBuZSBtZW5qYWp1IHNlDQp0aW1ldmFyOiB2YXJpamFibGEgdSBrb2pvaiDEh2UgYml0aSB6YXBpc2FuIHJlZG5pIGJyb2ogbWVyZW5qYSAodmkgZGVmaW5pxaFldGUgxb5lbGplbm8gaW1lKQ0Kdi5uYW1lczoga2FrbyBkYSBuYXpvdmUgbm92dSB2YXJpamFibHUgaWxpIHZhcmlqYWJsZSAodmkgZGVmaW5pxaFldGUgaW1lIC0gdSB0b2ogdmFyaWphYmxpIHNlIG5hbGF6ZSB2cmVkbm9zdGkgbWVyZW5qYSkNCnRpbWVzOiDEjWltZSBzdSB1IG5heml2aW1hIHZhcmlqYWJsaSBvem5hxI1lbmEgbWVyZW5qYSAoaW1lbmEgdmFyaWphYmxpIHNhIHBvbm92bGplbmltIG1lcmVuamltYSBtb3JhanUgaW1hdGkgbmVwcm9tZW5saml2IGRlbyBpIGRlbyBrb2ppIG96bmHEjWF2YSBtZXJlbmplKS4gVSBwcmltZXJ1IG5lcHJvbWVubGppdmkgZGVvIGplICJtZXJlbmplIiwgYSBwcm9tZW5saml2aSBqZSBvem5hxI1lbiBicm9qZW0gKG1hZGEgbW/FvmUgaSBkcnVnYcSNaWplKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICANCiMjIyBTTEFKRCA2MCMjIw0KIyMgTERGIC0+IFdERg0KVnJhdGnEh2VtbyBwcmV0aG9kbm8ga3JlaXJhbmUgcG9kYXRrZSAobm92aXBvZGFjaS5sb25nKSB1IFdpZGUgZGF0YSBmb3JtYXQuDQpgYGB7cn0NCm5vdmlwb2RhY2kud2lkZTwtcmVzaGFwZShub3ZpcG9kYWNpLmxvbmcsIGRpcmVjdGlvbj0id2lkZSIsIHZhcnlpbmc9InN0YXNtb21lcmlsaSIpIA0KYGBgDQpBcmd1bWVudGk6DQpkaXJlY3Rpb246IHUga29tIHNtZXJ1IG1lbmphbW8gc3RydWt0dXJ1IG1hdHJpY2UgKMW+ZWxpbW8gZGEgcG9zdGFuZSB3aWRlKS4NCnZhcnlpbmc6IGtvamUgdmFyaWphYmxlIHRyZWJhIHJhemJpdGkgbmEgdmnFoWUgdmFyaWphYmxpLiBNb8W+ZSBpaCBiaXRpIGkgdmnFoWUsIGFsaSBvbmRhIGplIHBvdHJlYm5vIGRlZmluaXNhdGkgc2xvdm5pIHZla3RvciBzYSBpbWVuaW1hIHZhcmlqYWJsaSBjKCJzdGFzbW9tZXJpbGkiLCAiZHJ1Z2F2YXJpamFibGEiLCAidHJlY2F2YXJpamFibGEiKQ0KSm/FoSBqZWRhbiBwcmltZXINCmBgYHtyfQ0KI3XEjWl0YXZhbmplIHBvZGF0YWthDQpwb2RhY2kubG9uZyA8LSBhcy5kYXRhLmZyYW1lKHJlYWQudGFibGUoaGVhZGVyPVQsIHRleHQ9Jw0KaXNwaXRhbmlrICBwb2wgICAgIG1lcmVuamUgICAgICAgIG1lcmENCjEgICBNICAgICBtZXJlbmplMSAgICAgICAgIDcuOQ0KMSAgIE0gICAgIG1lcmVuamUyICAgICAgICAxMi4zDQoxICAgTSAgICAgbWVyZW5qZTMgICAgICAgIDEwLjcNCjIgICBaICAgICBtZXJlbmplMSAgICAgICAgIDYuMw0KMiAgIFogICAgIG1lcmVuamUyICAgICAgICAxMC42DQozICAgWiAgICAgbWVyZW5qZTEgICAgICAgICA5LjUNCjMgICBaICAgICBtZXJlbmplMiAgICAgICAgMTMuMQ0KMyAgIFogICAgIG1lcmVuamUzICAgICAgICAxMy44DQo0ICAgTSAgICAgbWVyZW5qZTEgICAgICAgIDExLjUNCjQgICBNICAgICBtZXJlbmplMiAgICAgICAgMTMuNA0KNCAgIE0gICAgIG1lcmVuamUzICAgICAgICAxMi45DQonKSkNCmBgYA0KS29sb25hIHNhIHN1Ympla3RpbWEgbW9yYSBiaXRpIHRpcGEgImZhY3RvciIuDQpgYGB7cn0NCnBvZGFjaS5sb25nJGlzcGl0YW5payA8LSBmYWN0b3IocG9kYWNpLmxvbmckaXNwaXRhbmlrKQ0Kbm92aXBvZGFjaS53aWRlPC1yZXNoYXBlKHBvZGFjaS5sb25nLCBkaXJlY3Rpb24gPSAid2lkZSIsIHYubmFtZXM9IGMoIm1lcmEiKSwgdGltZXZhcj0ibWVyZW5qZSIsIGlkdmFyPWMoImlzcGl0YW5payIsICJwb2wiKSkNCmBgYA0KQXJndW1lbnRpOiANCmRpcmVjdGlvbjogZGEgbGkgcHJldHZhcmFtbyB1IGxvbmcgaWxpIHdpZGUsIA0KaWR2YXI6IHN1IHZhcmlqYWJsZSB6YSBpZGVudGlmaWthY2lqdSBpc3BpdGFuaWthIC0gbmUgbWVuamFqdSBzZSBwbyBtZXJlbmppbWENCnRpbWV2YXI6IG5heml2IHZhcmlqYWJsaSBzYSBwb3NlYm5pbSBtZXJlbmppbWEgKFIgZG9kYWplIGJyb2ogemEgc3Zha28gbWVyZW5qZSkNCnYubmFtZXM6IHN1IG5heml2aSB2YXJpamFibGkga29qZSBzZSBtZW5qYWp1LiBBa28gc2UgcmFkaSBvIGplZG5vaiB2YXJpamFibGkgcG8gYXJndW1lbnR1IG1vxb5lIGkgYmV6IGMoKSBrYW8ga29kIHRpbWV2YXI9LCBhbGkgbmF2b2RuaWNpIG1vcmFqdQ0KDQojIyMgU0xBSkQgNjEgIyMjDQojIyBJeiBXSURFIHUgTE9ORw0KSm/FoSBqZWRuYSBtb2d1xIdub3N0IGtvcmlzdGkgcGFrZXQgcmVzaGFwZTINCmBgYHtyfQ0KbGlicmFyeShyZXNoYXBlMikNCg0KYGBgDQoNCmlkLnZhcnM6IHZhcmlqYWJsZSBrb2plIHRyZWJhIGRhIG9zdGFudSBhbGkgbmUgaSBkYSBidWR1IGRlbGplbmUgcG8gbWVyZW5qaW1hIChuZXBvbm92bGplbmEgbWVyZW5qYSkNCmBgYHtyfQ0Kbm92aXBvZGFjaS5sb25nIDwtIG1lbHQocG9kYWNpLndpZGUsIGlkLnZhcnM9YygiaXNwaXRhbmlrIiwicG9sIiksIG1lYXN1cmUudmFycz1jKCJtZXJlbmplMSIsICJtZXJlbmplMiIsICJtZXJlbmplMyIgKSwgdmFyaWFibGVfbmFtZT0ibWVyZW5qZSIpDQpgYGANCkFyZ3VtZW50aToNCm1lYXN1cmUudmFyczogdmFyaWphYmxlIHUga29qaW1hIHNlIG5hbGF6ZSByYXpsacSNaXRhIG1lcmVuamENCmlkLnZhcnM6IG5lcHJvbWVubGppdmUgdmFyaWphYmxlIGtvamUgc2x1xb5lIHphIGplZGluc3R2ZW51IGlkZW50aWZpa2FjaWp1IGlzcGl0YW5pa2ENCnZhcmlhYmxlX25hbWU6IGtha28gxIdlbW8gbmF6dmF0aSBub3Z1IHZhcmlqYWJsdQ0KDQojIyMgU0xBSkQgNjIgIyMjDQojIyBJeiBXSURFIHUgTE9ORw0KSm/FoSBqZWRhbiBuYcSNaW4gdcSNaXRhdmFuamEgcG9kYXRha2EgKGRpcmVrdG5vIGl6IGtvbWFuZGUsIHBvbGphIG9kdm9qZW5hIHNlcGFyYXRvcm9tIDspDQpgYGB7cn0NCnBvZGFjaTIud2lkZSA8LSByZWFkLnRhYmxlKGhlYWRlcj1ULCBzZXA9IjsiLCB0ZXh0PScNCmlzcGl0YW5paztwb2w7bWVyZW5qZTE7bWVyZW5qZTI7bWVyZW5qZTM7bmVwb25vdmxqZW5vDQoxO007Ny45OzEyLjM7MTAuNzsxLjINCjI7Wjs2LjM7MTAuNjsxMS4xOzIuMg0KMztaOzkuNTsxMy4xOzEzLjg7My4yDQo0O007MTEuNTsxMy40OzEyLjk7NC4yJw0KKQ0KYGBgDQpUcmFuc2Zvcm1hY2lqYSB1cG90cmVib20gZnVua2NpamUgbWVsdCgpIGl6IHBha2V0YSByZXNoYXBlMi4NCk5pamUgc3BlY2lmaWtvdmFuYSB2YXJpamFibGEgIm5lcG9ub3ZsamVubyIgcGEgxIdlIGJpdGkgaXNwdcWhdGVuYQ0KYGBge3J9DQpub3ZpcG9kYWNpMi5sb25nIDwtIG1lbHQocG9kYWNpMi53aWRlLCBpZC52YXJzPWMoImlzcGl0YW5payIsInBvbCIpLCBtZWFzdXJlLnZhcnM9YygibWVyZW5qZTEiLCAibWVyZW5qZTIiLCAibWVyZW5qZTMiICksIHZhcmlhYmxlX25hbWU9Im1lcmVuamUiKQ0KaGVhZChub3ZpcG9kYWNpMi5sb25nKQ0KYGBgDQpLYWRhIGplIHNwZWNpZmlrb3ZhbmEgYml2YSB6YWRyxb5hbmENCmBgYHtyfQ0Kbm92aXBvZGFjaTMubG9uZyA8LSBtZWx0KHBvZGFjaTIud2lkZSwgaWQudmFycz1jKCJpc3BpdGFuaWsiLCJwb2wiLCAibmVwb25vdmxqZW5vIiksIG1lYXN1cmUudmFycz1jKCJtZXJlbmplMSIsICJtZXJlbmplMiIsICJtZXJlbmplMyIgKSwgdmFyaWFibGUubmFtZT0ibWVyZW5qZSIpDQpoZWFkKG5vdmlwb2RhY2kzLmxvbmcpDQpgYGANCiMjIyBTTEFKRCA2MyAjIyMNCiMjIFZyYcSHYW5qZSB1IFdJREUNClRyYW5zZm9ybWFjaWphIGZ1bmtjaWpvbSBkY2FzdCgpIGl6IHBha2V0YSBtYWRpdHIuDQpgYGB7cn0NCnBfbG9hZChtYWRpdHIpDQpub3ZpcG9kYWNpMy53aWRlPC1kY2FzdChub3ZpcG9kYWNpMy5sb25nLCBpc3BpdGFuaWsgKyBwb2wgfiBtZXJlbmplLCB2YWx1ZS52YXI9InZhbHVlIikNCiMgTmlqZSBzcGVjaWZpa292YW5hIHZhcmlqYWJsYSBuZXBvbm92bGplbm8gcGEgamUgaXpiYcSNZW5hDQpoZWFkKG5vdmlwb2RhY2kzLndpZGUpDQpgYGANCkFyZ3VtZW50aToNClBydmkgamUgZGF0YS5mcmFtZSBrb2ppIHRyYW5zZm9ybWnFoWVtbw0KWmF0aW0gc2xlZGkgZm9ybXVsYSB1IG9ibGlrdSBuZXBvbm92bGplbmUgKHNhICsgaXptZcSRdSkgfiB2YXJpamFibGEga29qYSBpZGVudGlmaWt1amUgbWVyZW5qZSkNCnZhbHVlLnZhcjogdmFyaWphYmxhIGlsaSB2acWhZSBuamloIGtvamUgc2FkcsW+ZSBwb25vdmxqZW5hIG1lcmVuamEuIEFrbyBpaCBqZSB2acWhZSwgbmF2ZXN0aSBrYW8gc2xvdm5pIHZla3RvciBjKCJ2MSIsICJ2MiIsICJ2MyIpLg0KDQpgYGB7cn0NCiMgU3BlY2lmaWtvdmFuYSB2YXJpamFibGEgbmVwb25vdmxqZW5vIHBhIGplIHXFoWxhIHUgZGF0YS5mcmFtZQ0Kbm92aXBvZGFjaTNhLndpZGU9ZGNhc3Qobm92aXBvZGFjaTMubG9uZywgaXNwaXRhbmlrICsgcG9sICsgbmVwb25vdmxqZW5vIH4gbWVyZW5qZSwgdmFsdWUudmFyPSJ2YWx1ZSIpDQpoZWFkKG5vdmlwb2RhY2kzYS53aWRlKQ0KYGBgDQojIyMgS09SRUtDSUpBIFpBIEFURU5VQUNJSlUNCktvcmVrY2lqYSB6YSBhdGVudWFjaWp1DQpOYXppdiBmdW5rY2lqZSAob2JqZWthdCkga29qZW0gcHJpZHJ1xb51amVtbyBmdW5rY2lqdSBpIHBvdHJlYm5lIGFyZ3VtZW50ZQ0KTmFyZWRiZSBzdSB1b2t2aXJlbmUgdml0acSNYXN0aW0gemFncmFkYW1hDQpgYGB7cn0NCmF0ZW4gPC0gZnVuY3Rpb24oa29yLCBydHQxPTEsIHJ0dDI9MSkgew0KICBpZiAoa29yPCgtMSkgfCBrb3I+MSB8ICFpcy5udW1lcmljKGtvcikpIHsNCiAgICAgIHN0b3AoIk5laXNwcmF2bmEgdnJlZG5vc3Qga29yZWxhY2lqZSEiKQ0KICB9DQogIGlmIChydHQxPDAgfCBydHQxPjEgfCAhaXMubnVtZXJpYyhydHQxKSB8IHJ0dDI8IDAgfCBydHQyPjEgfCAhaXMubnVtZXJpYyhydHQyKSkgew0KICAgIHN0b3AoIk5laXNwcmF2bmEgdnJlZG5vc3QgcG91emRhbm9zdGkhIikNCiAgICB9DQogIGNhdCgiS29yZWxhY2lqYSBzYSBrb3Jla2Npam9tIHphIGF0ZW51YWNpanUgaXpub3NpOiIsIHJvdW5kKGtvci9zcXJ0KHJ0dDEqcnR0MiksIDMpKQ0KfQ0KYGBgDQpQb3ppdmFuamUgZnVua2NpamUNCmBgYHtyfQ0KYXRlbigwLjIsIDAuNjIsIDAuNykgI3BydmkgYXJndW1lbnQgamUga29yZWxhY2lqYSwgZHJ1Z2kgcG91emRhbm9zdCBwcnZvZywgYSB0cmXEh2kgcG91emRhbm9zdCBkcnVnb2cgdGVzdGEuDQpgYGANCkZ1bmtjaWphIGNhdCgpIHNsdcW+aSB6YSBpc3Bpc2l2YW5qZSB0ZWtzdGEuDQpTcGFqYSBhcmd1bWVudGUgb2R2b2plbmUgemFyZXppbWEgdSB0ZWtzdHVhbG5pIG5peiBrb2ppIHNlIGlzcGlzdWplIG5hIGVrcmFudS4NCkFyZ3VtZW50aSBmdW5rY2lqZSBjYXQoKSBtb2d1IGJpdGkgaSBudW1lcmnEjWtpLg0KTW/FvmV0ZSBqZSBrb3Jpc3RpdGkgemEgcGlzYW5qZSBuYXNsb3ZhIHUgaXNwaXN1IGkgbmEgZ3JhZmlrb25pbWEuDQpQcmVsb20gcmVkYSB1IG9rdmlydSBmdW5rY2lqZSBzZSByYWRpIHNhIFxuDQpUZWtzdHVhbG5pIGRlbyBtb3JhIGJpdGkgcG9kIG5hdm9kbmljaW1hLg0KRnVua2NpamEgcm91bmQoKSB6YW9rcnXFvnVqZSBicm9qIG5hIG9kcmXEkWVuaSBicm9qIGRlY2ltYWxhLg0KQXJndW1lbnRpIHN1IGpvaiBicm9qIGtvamkgxb5lbGltbyBkYSB6YW9rcnXFvmltbyBpIMW+ZWxqZW5pIGJyb2ogZGVjaW1hbGEuDQoNCiMjIFBFVExKQSBGT1INClBvbmF2bGphIG9kcmXEkWVudSBrb21hbmR1IChmdW5rY2lqdSkgZG9rIHN1IHphZG92b2xqZW5pIHVzbG92aS4NClUgbmFyZWRub20gc2x1xI1hanUgdG8gcmFkaSBkb2sgamUgYnJvamHEjSAoaSkgdSBvcHNlZ3Ugb2QgMSBkbyAxMC4NCkluYcSNZSwgYnJvamHEjSBuZSBtb3JhIHBvxI1pbmphdGkgb2QgMSAobnByLiBmb3IgKGkgaW4gMTU6MjApKSBuaXRpIG1vcmEgYml0aSBicm9qxI1hbmkuDQoNCmBgYHtyfQ0KZm9yIChpIGluIDE6MTApIHsNCiBwb2RhY2kyWyxpXT1hcy5udW1lcmljKHBvZGFjaTJbLGldKQ0KIH0NCmBgYA0KUGV0bGphIGZvciBwb25hdmxqYSBuYXJlZGJ1IHN2ZSBkb2sgamUgaSB1IHphZGF0b20gb2t2aXJ1DQpTdmFraW0gcG9uYXZsamFuamVtIGkgcG9zdGFqZSB6YSAxIHZlxIdpDQpVIG92b20gc2x1xI1hanUgcHJldHZhcmEgcHJ2aWggMTAgdmFyaWphYmxpIGRhdGEuZnJhbWUgcG9kYWNpMiB1IG51bWVyacSNa2kgdGlwLiBVIG92b20gc2x1xI1hanUgdmFyaWphYmxlIHN1IGtvbG9uZSBkYXRhLmZyYW1lYSBqZXIgamUgdSBpbmRla3N1IFssXSBpIHBvc2xlIHphcmV6YSBwYSBvem5hxI1hdmEga29sb251Lg0KDQoNCiMjIyBTTEFKRCA3MSAjIyMNCiMjIExhdmFhbg0KUGFrZXQgemEgbW9kZWxvdmFuamUgc3RydWt0dXJhbG5paCBqZWRuYcSNaW5hDQpgYGB7cn0NCnBfbG9hZChsYXZhYW4pDQpgYGANClXEjWl0YXZhbmplIHBvZGF0YWthDQpgYGB7cn0NCmJmczwtcmVhZC50YWJsZSgiYmZkNTAwby5kYXQiLCBoZWFkZXIgPSBUKQ0KYGBgDQpLcmVpcmFuamUgbW9kZWxhIChkdm9mYWt0b3Jza2kgc2Ega29yZWxpcmFuaW0gZmFrdG9yaW1hKQ0KYGBge3J9DQptb2RlbDE9J0YxPX5WMStWMitWMytWNCtWNSANCkYyPX5WNitWNytWOCtWOStWMTAgDQpGMX5+RjInDQpgYGANClpuYcSNZW5qZSBwcmV0aG9kbmUgc2ludGFrc2U6DQpGMT1+VjErVjIrVjMrVjQrVjUgRmFrdG9yIDEgbWVyZW4gcHJla28gdmFyaWphYmxpIFYxLCBWMiwgVjMsIFY0IGkgVjUgKG5heml2aSB2YXJpamFibGkgaXogZGF0YS5mcmFtZWEgYmZzKQ0KRjI9flY2K1Y3K1Y4K1Y5K1YxMCBGYWt0b3IgMiBtZXJlbiBwcmVrbyB2YXJpamFibGkgVjYsIFY3LCBWOCwgVjkgaSBWMTAgKG5heml2aSB2YXJpamFibGkgaXogZGF0YS5mcmFtZWEgYmZzDQpGMX5+RjIgZG96dm9samVubyBqZSBkYSBGYWt0b3IgMSBpIEZha3RvciAyIGtvcmVsaXJhanUNCg0KRml0b3ZhbmplIG1vZGVsYSAoa29uZmlybWF0b3JuYSBmYWt0b3Jza2EgYW5hbGl6YSkuIE9zbm92bm8uIFZpxaFlIMSHZXRlIHJhZGl0aSBrYXNuaWplLg0KRml0dWplbW8gbW9kZWwgZnVua2Npam9tIGNmYSBpIHNtZcWhdGFtbyBnYSB1IG9iamVrYXQgbTEuDQpEb3ZvbGpuaSBhcmd1bWVudGlpIHN1IG5heml2aSBnb3JlIGtyZWlyYW5vZyBtb2RlbGEgaSBtYXRyaWNlIHBvZGF0YWthLg0KYGBge3J9DQptMSA8LSBjZmEobW9kZWwxLCBkYXRhPWJmcykNCnN1bW1hcnkobTEsIGZpdC5tZWFzdXJlcz1UUlVFKSAjb3Nub3ZuZSBpbmZvcm1hY2lqZSBvIG1vZGVsdSBpIHBva2F6YXRlbGppIGZpdGENCnN0YW5kYXJkaXplZHNvbHV0aW9uKG0xKSAjc3RhbmRhcmRpem92YW5vIHJlxaFlbmplDQptb2RpbmRpY2VzKG0xKSAjaW5kZWtzaSBtb2RpZmlrYWNpamUNCmBgYA0KSWxpIGJpZmFrdG9yc2tpIG1vZGVsLi4uDQpgYGB7cn0NCm1vZGVsMiA8LSAnRz1+VjErVjIrVjMrVjQrVjUrVjYrVjcrVjgrVjkrVjEwOyBTMT1+VjErVjIrVjMrVjQrVjU7IFMyPX5WNitWNytWOCtWOStWMTA7ICBTMX5+MCpTMjsgR35+MCpTMTsgR35+MCpTMicNCmBgYA0KRyAtIGdlbmVyYWxuaSBmYWt0b3IgKHN2ZSB2YXJpamFibGUpDQpTMSAtIHBydmkgc3BlY2lmacSNbmkgZmFrdG9yIChwcnZpaCA1IHZhcmlqYWJsaSkNClMyIC0gZHJ1Z2kgc3BlY2lmacSNbmkgZmFrdG9yIChkcnVnaWggNSB2YXJpamFibGkpDQpVIHBvc2xlZG5qYSB0cmkgaXpyYXphIG9wZXJhdG9yIH5+IG96bmHEjWF2YSBrb3ZhcmlqYW5zdSB2YXJpamFibGkgKHUgb3ZvbSBzbHXEjWFqdSBmYWt0b3JhKQ0KUHJlbXVsdGlwbGlrYWNpamEgMCogb2dyYW5pxI1hdmEga292YXJpamFuc2UgdmFyaWphYmxpIG5hIDAgKG1vcmFqdSBiaXRpIDAgLSBnZW5lcmFsbmkgZmFrdG9yIG5lIHNtZSBrb3JlbGlyYXRpIHNhIHNwZWNpZmnEjW5pbSwgYSBuaSBzcGVjaWZpxI1uaSBtZcSRdXNvYm5vKQ0KDQpGaXRvdmFuamUgbW9kZWxhDQpgYGB7cn0NCm0yPC1jZmEobW9kZWwyLCBiZnMsIGVzdGltYXRvcj0iV0xTTVYiLCBzdGQubHYgPSBUUlVFKQ0Kc3VtbWFyeShtMiwgZml0Lm1lYXN1cmVzPVRSVUUpICNvc25vdm5lIGluZm9ybWFjaWplIG8gbW9kZWx1IGkgcG9rYXphdGVsamkgZml0YQ0Kc3RhbmRhcmRpemVkc29sdXRpb24obTIpICNzdGFuZGFyZGl6b3Zhbm8gcmXFoWVuamUNCm1vZGluZGljZXMobTIpICNpbmRla3NpIG1vZGlmaWthY2lqZQ0KYGBgDQpBcmd1bWVudGkgZnVua2NpamUgY2ZhOg0KZXN0aW1hdG9yIC0ga29qaSBlc3RpbWF0b3Igc2Uga29yaXN0aQ0Kc3RkLmx2IC0gZGEgbGkgZGEgc3RhbmRhcmRpenVqZSBsYXRlbnRuZSB2YXJpamFibGUNCg0KDQojIyBTaW50YWtzYSBsYXZhYW4gbW9kZWxhICMjIw0KVGlwIGZvcm11bGUJICAgICAgT3BlcmF0b3IJICBQcmltZXIJICBVenJvxI1ub3N0CSAgICBabmHEjWVuamUgDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KZGVmaW5pY2lqYSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0ZW50bmEgdXpyb2t1amUgaWxpDQpsYXRlbnRuZSB2YXJpamFibGUJICA9fgkgICAgRjE9fngxK3gyCSAgRjEtPngxIGkgeDIJamUgbWVyZW5hIHBvbW/Eh3UNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHV6cm9rb3ZhbmEgamUsIA0KcmVncmVzaWphCSAgICAgICAgICAgIH4JICAgICAgeX54MSt4MgkgICAgeDEreDItPgkgICAgcmVncmVzaXJhbmEgbmENCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQooa28pdmFyaWphbnNhICAgICAgCSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzbG9ib2RuYQ0KKHJlemlkdWFsbmEpICAgICAgICAgfn4JICB4fn55LCB4fn54CXg8LT55LCB4PC0+eAkgIChrbyl2YXJpamFuc2ENCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpvZHNlxI1hawkJICAgICAgICAgICAgICAgICAgICB4fjEJCSAgICAgICAgICAgICAgICAgIHByb2Nlbmkgb2RzZcSNYWsgeA0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm92aSBwYXJhbWV0YXIgdQ0Kbm92aSBwYXJhbWV0YXIJICAgICAgOj0JICAgIHU6PWErYgkJICAgICAgICAgICAgICBqZWRuYWsgYStiDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydG5hIHZyZWRub3N0IHNlIG1vxb5lDQpzdGFydG5lIHZyZWRub3N0aQlzdGFydCgpCSAgeX5zdGFydCgwLjIpKngJCSAgICAgICAgICBtZW5qYXRpIHRva29tIGl0ZXJhY2lqYQ0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmZpa3NpcmFuZSANCnZyZWRub3N0aQkgICAgICAgICAgICAgICAgICAJeX4xKngJCSAgICAgICAgICAgICAgICBuZSBtZW5qYSBzZSBpIGplZG5hayBqZSAxDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0Kc2xvYm9kbmEgdnJlZG5vc3QJCSAgICAgICAgICB5fk5BKngJCSAgICAgICAgICAgICAgc2xvYm9kYW4gamUNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgxIxVVkFOSkUgS1JFSVJBTklIIE9CSkVLQVRBIE5BIERJU0tVIyMjDQpLb21hbmRvbSAgc2F2ZS5pbWFnZSgiQW5hbGl6ZS5SZGF0YSIpIHphcGlzYcSHZXRlIHN2ZSBrcmVpcmFuZSBvYmpla3RlIG5hIGhhcmQgZGlzaywgdSByYWRuaSBmb2xkZXIgKG9zaW0gYWtvIG5lIG5hdmVkZXRlIHB1bnUgcHV0YW5qdSkuDQpgYGB7cn0NCnNhdmUuaW1hZ2UoIkFuYWxpemUuUmRhdGEiKQ0KYGBgDQpNb8W+ZXRlIGloIHBvbm92byB1xI1pdGF0aSBzYQ0KYGBge3J9DQpsb2FkKCJBbmFsaXplLlJkYXRhIikNCmBgYA0KUHJldGhvZG5vIGplIHBvdHJlYm5vIGRhIGRlZmluacWhZXRlIHJhZG5pIGZvbGRlciwgaWxpIGRhIG5hdmVkZXRlIHB1bnUgcHV0YW5qdSBkbyBkYXRvdGVrZSAoemFqZWRubyBzYSBpbWVub20gZGF0b3Rla2UsIHUgb2t2aXJ1IG5hdm9kbmlrYSBucHIuIGxvYWQoIkQ6L09UU1MoUikvQW5hbGl6ZS5SZGF0YSIpKQ0K