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
Č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