##
## Dołączanie pakietu: 'dplyr'
## Następujące obiekty zostały zakryte z 'package:stats':
##
## filter, lag
## Następujące obiekty zostały zakryte z 'package:base':
##
## intersect, setdiff, setequal, union
## Ładowanie wymaganego pakietu: viridisLite
Analiza opisowa
Jednym ze sposobów zrozumienia, jak działa rząd miasta, jest
spojrzenie na to, kogo zatrudnia i jak jego pracownicy są wynagradzani.
Dane te zawierają nazwiska, nazwę stanowiska i wynagrodzenie pracowników
miasta San Francisco w ujęciu rocznym od 2011 do 2014 roku.
Oto kilka pomysłów na eksplorację danych:
Jak zmieniały się wynagrodzenia w czasie między różnymi grupami
ludzi?
Jak płaca podstawowa, wynagrodzenie za nadgodziny i świadczenia
są rozdzielane pomiędzy różne grupy?
Czy w tym zestawie danych istnieją dowody na dyskryminację
płacową ze względu na płeć?
Jak przydzielany jest budżet w zależności od grupy i zakresu
obowiązków?
# wymiary ramki:
dim(salaries)
## [1] 148654 13
# nazwy kolumn:
names(salaries)
## [1] "Id" "EmployeeName" "JobTitle" "BasePay"
## [5] "OvertimePay" "OtherPay" "Benefits" "TotalPay"
## [9] "TotalPayBenefits" "Year" "Notes" "Agency"
## [13] "Status"
Histogramy
hist(salaries$TotalPay,main="Total Pay", xlab="Pay (in dollars)")
abline(v = mean(salaries$TotalPay),lty="dashed")
abline(v = median(salaries$TotalPay))
legend("topright", legend=c("Mediana","Średnia"),lty=c("solid","dashed"))

par(mfrow=c(2,2))
hist(salaries$TotalPay,main="Total Pay, default breaks", xlab="Pay (in dollars)")
hist(salaries$TotalPay,main="Total Pay, breaks=100", xlab="Pay (in dollars)", breaks=100)
hist(salaries$TotalPay,main="Total Pay, breaks=1000", xlab="Pay (in dollars)",breaks=1000)

hist(salaries$TotalPay,main="Total Pay, Zoomed-in", xlab="Pay (in dollars)", xlim=c(0,1e5), breaks=1000)

salaries2 <- subset(salaries, JobTitle=="Firefighter" & Status=="FT")
dim(salaries2)
## [1] 738 13
par(mfrow=c(2,2))
hist(salaries2$TotalPay,main="Firefighters, default breaks", xlab="Pay (in dollars)")
hist(salaries2$TotalPay,main="Firefighters, breaks=30", xlab="Pay (in dollars)", breaks=30)
hist(salaries2$TotalPay,main="Firefighters, breaks=100", xlab="Pay (in dollars)", breaks=100)
hist(salaries2$TotalPay,main="Firefighters, breaks=1000", xlab="Pay (in dollars)",breaks=1000)

Wykresy
pudełkowe
par(mfrow=c(1,1))
boxplot(salaries$TotalPay,main="Total Pay, breaks=1000", ylab="Pay (in dollars)")

Estymacja funkcji
gęstości
Pierwszy raport dotyczy nieparametrycznej estymacji gęstości.
Klasycznym nieparametrycznym estymatorem gęstości jest histogram, który
dostarcza nieciągłe i stałe oszacowania. W tym raporcie skupiono się na
niektórych alternatywach, które zapewniają ciągłe lub nawet gładkie
oszacowania zamiast.
Metody kernelowe stanowią ważną klasę gładkich estymatorów
gęstości i zaimplementowane są przez funkcję R density().
Estymatory te są w zasadzie tylko lokalnie ważonymi średnimi, a ich
obliczenie jest stosunkowo proste w teorii. W praktyce, różne wybory
sposobu implementacji obliczeń mogą jednak mieć duży wpływ na
rzeczywisty czas obliczeń, a implementację kernelowych estymatorów
gęstości zilustruje trzy punkty:
- jeśli to możliwe, wybierz wektoryzowane implementacje w R,
- jeśli niewielka strata w dokładności jest do zaakceptowania,
przybliżone rozwiązanie może być o rzędy wielkości szybsze niż
implementacja literalna,
- czas potrzebny do numerycznej oceny różnych funkcje
elementarne może bardzo zależeć od funkcji i sposobu implementacji
obliczeń.
Metody kernelowe opierają się na jednym lub więcej parametrach
regularności, które muszą być dobrane tak, aby osiągnąć właściwą
równowagę w dostosowaniu do danych bez zbytniego dostosowywania się do
losowej zmienności w danych.
Wybór odpowiedniej ilości regularności jest równie ważny jak wybór
metody do użycia w pierwszej kolejności. W rzeczywistości może być
ważniejszy. Tak naprawdę nie mamy kompletnej implementacji
nieparametrycznego estymatora dopóki nie zaimplementujemy
automatycznego, opartego na danych sposobu wyboru ilości regulacji.
Implementacja tylko obliczeń dla oceny estymatora jądra, powiedzmy, i
pozostawiając to całkowicie użytkownikowi wyboru szerokości pasma jest
pracą w połowie wykonaną. Metody i implementacje do wyboru szerokości
pasma są więc w tym raporcie omówione dość szczegółowo.
W ostatniej części przeprowadzona jest analiza prawdopodobieństwa.
Robi się to w celu dalszego wyjaśnienia, dlaczego potrzebne są
estymatory z regularyzacją w celu uniknięcia nadmiernego dopasowania do
danych, oraz dlaczego nie istnieje w ogóle nieparametryczny maksymalnego
prawdopodobieństwa estymatora gęstości. Regularyzację
prawdopodobieństwamożna osiągnąć poprzez ograniczenie szacunków gęstości
do rodziny coraz bardziej elastycznych gęstości parametrycznych, które
są dopasowane do danych. Jest to znane jako metoda sit. Inne
podejście opiera się na rozszerzeniach bazowych, ale w obu przypadkach
automatyczny wybór wielkości regularności jest tak samo ważny jak w
przypadku metod jądrowych.
Aby utworzyć wykres gęstości jądra, musisz oszacować gęstość jądra. W
tym celu można użyć funkcji density, a następnie przekazać obiekt
density do funkcji plot.
# dane
set.seed(14012021)
data <- rnorm(200, mean = 4)
# Kernel density estimation
d <- density(data)
# Kernel density plot
plot(d, lwd = 2, main = "Default kernel density plot")

Argument jądra funkcji gęstości domyślnie używa jądra gaussowskiego
(kernel = “gaussian”), ale dostępnych jest więcej typów jądra, takich
jak “prostokątne”, “trójkątne”, “epanechnikov”, “biweight”, “cosine” i
“optcosine”. Wybór będzie zależał od twoich danych, ale w większości
scenariuszy wartość domyślna jest najbardziej zalecana.
# Data
set.seed(14012021)
data <- rnorm(200, mean = 4)
# Kernel density estimation
d <- density(data,
kernel = "rectangular")
# Kernel density plot
plot(d, lwd = 2, main = "Rectangular kernel")

# Data
set.seed(14012021)
data <- rnorm(200, mean = 4)
# Kernel density estimation
d <- density(data,
kernel = "triangular")
# Kernel density plot
plot(d, lwd = 2, main = "Triangular kernel")

# Data
set.seed(14012021)
data <- rnorm(200, mean = 4)
# Kernel density estimation
d <- density(data,
kernel = "epanechnikov")
# Kernel density plot
plot(d, lwd = 2, main = "Epanechnikov kernel")

# Data
set.seed(14012021)
data <- rnorm(200, mean = 4)
# Kernel density estimation
d <- density(data,
kernel = "biweight")
# Kernel density plot
plot(d, lwd = 2, main = "Biweight kernel")

# Data
set.seed(14012021)
data <- rnorm(200, mean = 4)
# Kernel density estimation
d <- density(data,
kernel = "cosine")
# Kernel density plot
plot(d, lwd = 2, main = "Cosine kernel")

Selekcja pasma
Argument bw funkcji gęstości pozwala na zmianę używanego pasma
wygładzania. Możesz przekazać wartość lub ciąg znaków podający regułę
wyboru lub funkcję. Domyślną wartością jest “nrd0” (lub bw.nrd0(.)),
która implementuje podejście oparte na zasadzie reguły kciuka :-) Inne
dostępne opcje to:
Reguła Scotta
(1992)
# Data
set.seed(14012021)
data <- rnorm(200, mean = 4)
# Kernel density estimation
d <- density(data,
bw = "nrd")
# Kernel density plot
plot(d, lwd = 2, main = "nrd bandwidth")

Nieobciążona
cross-walidacja
# Data
set.seed(14012021)
data <- rnorm(200, mean = 4)
# Kernel density estimation
d <- density(data,
bw = "ucv")
# Kernel density plot
plot(d, lwd = 2, main = "ucv bandwidth")

Obciążona
cross-walidacja
# Data
set.seed(14012021)
data <- rnorm(200, mean = 4)
# Kernel density estimation
d <- density(data,
bw = "bcv")
# Kernel density plot
plot(d, lwd = 2, main = "bcv bandwidth")

Metoda Sheather
& Jones (1991)
# Data
set.seed(14012021)
data <- rnorm(200, mean = 4)
# Kernel density estimation
d <- density(data,
bw = "SJ")
# Kernel density plot
plot(d, lwd = 2, main = "SJ bandwidth")

- Ostrzeżenie!
-
Szerokość pasma musi być bardzo starannie dobrana! Mała szerokość
pasma spowoduje powstanie nadmiernie dopasowanej krzywej, natomiast zbyt
duża szerokość pasma spowoduje powstanie krzywej nadmiernie
wygładzonej.
Ćwiczenie 1.
Uruchom demo estymatora funkcji gęstości kernel. Zmieniaj zarówno
dane wejściowe, jak i opcje estymatora - szerokość pasma oraz rodzaj
funkcji jądrowej. Czy widzisz istotne różnice w oszacowaniu?
Wnioski
Zauważone podczas eksperymentowania z różnymi parametrami estymatora
funkcji gęstości kernel:
Rozkłady:
Rozkład normalny ma dzwonowaty kształt, skoncentrowany wokół
średniej. Zmiana średniej przesuwa wykres w prawo lub lewo, a większe
odchylenie standardowe spłaszcza go.
Rozkład beta może przybierać różne, często asymetryczne kształty,
zależne od parametrów α i β.
Rozkład wykładniczy może sprawiać że gęstości szybko maleje od
początkowej wartości. Im większy parametr, tym szybciej spada.
Rozkład jednostajny przypisuje jednakowe prawdopodobieństwo dla
wszystkich wartości w określonym przedziale.
Nietypowe rozkłady mają nieregularny kształt i często zależą od
specyficznych danych.
Kernele
Rodzaj decyduje o sposobie wygładzania: Gaussowskie daje
dzwonowaty,gładki kształt. Biweight więcej uwagi przypisuje punktom
blisko środka. Cosinusowe wygładza wartości stopniowo w kierunku krańców
przedziału.
Szerokość pasma wpływa na precyzję i gładkość wykresu:
Węższe pasmo umożliwia lepsze odwzorowanie detali, ale może prowadzić
do zbyt szczegółowego dopasowania. Optymalne pasmo dobrze balansuje
szczegóły i gładkość. Szersze pasmo daje gładszy wykres, ale może zgubić
niektóre detale danych.
#install.packages("remotes") #tylko raz! potem #
#remotes::install_github("hericks/KDE") #tylko raz! potem #
library(KDE)
shiny_kde()
Shiny applications not supported in static R Markdown documents
Ćwiczenie 2.
Wykorzystując dowolną funkcję R do estymacji funkcji gęstości oszacuj
jej przebieg dla wynagrodzeń (zbiór danych salaries) strażaków w San
Francisco. Wykorzystaj metody graficzne dostępne w pakiecie ggplot2.
Mile widziane przekroje oraz odpowiedzi na pytania badawcze zadane na
wstępie.
Poprzednie
biblioteczki
Badanie struktury
danych Wynagrodzeń (zbiór danych salaries) strażaków w San
Francisco
salaries3a<- subset(salaries, JobTitle=="Firefighter")
dim(salaries3a)
## [1] 2359 13
hist(salaries3a$TotalPay,main="Całkowite wynagrodzenie", xlab="Wynagrodzenia w dolarach",ylab="Częstotliwość")
abline(v = mean(salaries3a$TotalPay),lty="dashed")
abline(v = median(salaries3a$TotalPay))
legend("topright", legend=c("Mediana","Średnia"),lty=c("solid","dashed"))

salaries3a <- subset(salaries, JobTitle=="Firefighter")
dim(salaries3a)
## [1] 2359 13
Szukamy czy nie ma nigdzie NA
## na_count
## 1 0
Szukamy czy nie ma
wartości odstajacych
Wykres pudełkowy wynagrodzenia strażaków w San Francisco z
wartościami odstającymi
ggplot(salaries3a, aes(y = TotalPay)) +
geom_boxplot() +
coord_cartesian(ylim = c(0, 300000))

Wykres pudełkowy wynagrodzenia strażaków w San Francisco besz
wartości odstających
ggplot(salaries3a, aes(y = TotalPay)) +
geom_boxplot(outlier.shape = NA) +
coord_cartesian(ylim = c(0, 250000))

salaries3a %>%
filter(TotalPay < 250000 , TotalPay >75000 ) %>%
ggplot(aes(y = TotalPay)) +
geom_boxplot() +
coord_cartesian(ylim = c(0, 250000))

Tworzymy zbiór danych bez wartości odstających
salaries3<-salaries3a %>%
filter(TotalPay < 250000 , TotalPay >75000 )
Aby sprawdzaić czy zbiór salaries 3 pasuje ona do naszej analizy ,
przejrzymy czy na jego podstawie da się odpowiedzieć an pytanie: jak
zmieniały się wynagrodzenia wsród strazaków?
Wykres ukazujący zmian wynagrodzenia wsród strazaków na podstawie
zbiory salaries3
ggplot(data=salaries3, aes(x=BasePay, group=Year, fill=Year)) +
geom_density(adjust=1.5) +
theme_ipsum() +
facet_wrap(~Year) +
theme(
legend.position="none",
panel.spacing = unit(0.1, "lines"),
axis.ticks.x=element_blank()
)

Wykres ukazujący zmian wynagrodzenia wsród strazaków na podstawie
zbiory salaries3a
ggplot(data=salaries3a, aes(x=BasePay, group=Year, fill=Year)) +
geom_density(adjust=1.5) +
theme_ipsum() +
facet_wrap(~Year) +
theme(
legend.position="none",
panel.spacing = unit(0.1, "lines"),
axis.ticks.x=element_blank()
)

Decydujemy się pozostać przy zbiore salaries3a aby ukazać bardziej
zróżnicowe wyniki w poszczgólnych latach.
Estymacji funkcji
gęstości dla wynagrodzeń strażaków w San Francisco
Histogram i estymacja funkcji gęstości dla Wynagrodzenie całkowitego
strażaków
ggplot(salaries3a, aes(x = TotalPay)) +
geom_histogram(aes(y = after_stat(density)), bins = 30
, fill = "green", color = "black", alpha = 0.2) +
geom_density(color = "purple", size = 1) +
labs(#title = "Histogram i estymacja funkcji gęstości dla Wynagrodzenie całkowitego strażaków",
x = "Wynagrodzenie całkowite strażaków",
y = "Gęstość") +
theme_minimal()
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Estymacji funkcji
gęstości dla wynagrodzeń strażaków w San Francisco za pomoca
kernela
data1 <- salaries3a$TotalPay
d1 <- density(data1,
kernel = "cosine")
plot(d1, lwd = 2, main = "Cosine kernel")

d2 <- density(data1,
bw = "SJ")
plot(d2, lwd = 2, main = "SJ bandwidth")

d3 <- density(data1,
bw = "nrd")
plot(d3, lwd = 2, main = "nrd bandwidth")

Jak zmieniały się
wynagrodzenia w czasie wsród strażaków?
Wykres wynagrodzenia za w zależności od roku
ggplot(data=salaries3a, aes(x=TotalPay, group=Year, fill=Year)) +
geom_density(adjust=1.5, alpha=.4) +
theme_ipsum()

Jak zmieniały się
wynagrodzenia za nadgodziny w czasie wsród strażaków?
Wykres wynagrodzenia za nadgodziny w zależności od roku
ggplot(data=salaries3a, aes(x=OvertimePay, group=as.factor(Year), fill=Year)) +
geom_density(adjust=1.5, alpha=0.4) +
theme_ipsum()

Wykres wynagrodzenia podstawowego w zależności od roku
ggplot(data=salaries3, aes(x=BasePay, group=Year, fill=Year)) +
geom_density(adjust=1.5) +
theme_ipsum() +
facet_wrap(~Year) +
theme(
legend.position="none",
panel.spacing = unit(0.1, "lines"),
axis.ticks.x=element_blank()
)

grupy <- salaries3a %>%
filter(Year %in% c("2012", "2013", "2014"))
# Tworzenie wykresu z poprawionym kolorowaniem
ggplot(grupy, aes(x = TotalPay, color = as.factor(Year))) +
geom_density(size = 1) +
labs(title = "Estymacja funkcji gęstości dla wynagrodzeń w różnych latach wsród strażaków",
x = "Wynagrodzenie (Total Pay)",
y = "Gęstość",
color = "ROK") +
theme_minimal()

Wykres ukazujący zmian w dodatkach do wynagrodzenia wsród strazaków
na podstawie zbiory salaries3
ggplot(data=salaries3a, aes(x=Benefits, group=Year, fill=Year)) +
geom_density(adjust=1.5) +
theme_ipsum() +
facet_wrap(~Year) +
theme(
legend.position="none",
panel.spacing = unit(0.1, "lines"),
axis.ticks.x=element_blank()
)

Pytania badawcze
Jak zmieniały się
wynagrodzenia w czasie między różnymi grupami ludzi?
salaries$JobTitle %>% table() %>% sort(decreasing = TRUE) %>%
head(20)
## .
## Transit Operator Special Nurse
## 7036 4389
## Registered Nurse Public Svc Aide-Public Works
## 3736 2518
## Police Officer 3 Custodian
## 2421 2418
## TRANSIT OPERATOR Firefighter
## 2388 2359
## Recreation Leader Patient Care Assistant
## 1971 1945
## Deputy Sheriff Police Officer
## 1933 1476
## SPECIAL NURSE Public Service Trainee
## 1402 1328
## REGISTERED NURSE Police Officer 2
## 1219 1141
## Attorney (Civil/Criminal) Porter
## 1126 1095
## Sergeant 3 General Laborer
## 1047 1033
grupy1 <- salaries %>%
filter(JobTitle %in% c("Firefighter", "Police Officer", "Engineer", "Special Nurse","Custodian","NURSE","General Laborer"))
ggplot(grupy1, aes(x = TotalPay, color = JobTitle)) +
geom_density(size = 1) +
labs(title = "Estymacja funkcji gęstości dla wynagrodzeń wsród różnych zawodów",
x = "Wynagrodzenie",
y = "Gęstość",
color = "Zawód") +
theme_minimal()

LS0tDQp0aXRsZTogIktlcm5lbCINCmF1dGhvcjogIlBhdWxpbmEgRmVyZW5pZWMiDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdGhlbWU6IGNlcnVsZWFuDQogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQ0KICAgIGZvbnRzaXplOiA4cHQNCiAgICB0b2M6IHRydWUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCm9wdGlvbnMoc2NpcGVuPTk5OSwgZGlnaXRzPTMpDQpzYWxhcmllcyA8LSByZWFkLmNzdigiaHR0cHM6Ly9naXRodWIuY29tL2tmbGlzaWtvd3NraS9kcy9yYXcvbWFzdGVyL1NhbGFyaWVzLmNzdiIpDQpgYGANCg0KYGBge3IsIGVjaG89RkFMU0V9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGdlb210ZXh0cGF0aCkNCmxpYnJhcnkoaHJicnRoZW1lcykNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeSh2aXJpZGlzKQ0KYGBgDQoNCiMgQW5hbGl6YSBvcGlzb3dhDQoNCkplZG55bSB6ZSBzcG9zb2LDs3cgenJvenVtaWVuaWEsIGphayBkemlhxYJhIHJ6xIVkIG1pYXN0YSwgamVzdCBzcG9qcnplbmllDQpuYSB0bywga29nbyB6YXRydWRuaWEgaSBqYWsgamVnbyBwcmFjb3duaWN5IHPEhSB3eW5hZ3JhZHphbmkuIERhbmUgdGUNCnphd2llcmFqxIUgbmF6d2lza2EsIG5henfEmSBzdGFub3dpc2thIGkgd3luYWdyb2R6ZW5pZSBwcmFjb3duaWvDs3cgbWlhc3RhDQpTYW4gRnJhbmNpc2NvIHcgdWrEmWNpdSByb2N6bnltIG9kIDIwMTEgZG8gMjAxNCByb2t1Lg0KDQpPdG8ga2lsa2EgcG9teXPFgsOzdyBuYSBla3NwbG9yYWNqxJkgZGFueWNoOg0KDQotICAgSmFrIHptaWVuaWHFgnkgc2nEmSB3eW5hZ3JvZHplbmlhIHcgY3phc2llIG1pxJlkenkgcsOzxbxueW1pIGdydXBhbWkNCiAgICBsdWR6aT8NCg0KLSAgIEphayBwxYJhY2EgcG9kc3Rhd293YSwgd3luYWdyb2R6ZW5pZSB6YSBuYWRnb2R6aW55IGkgxZt3aWFkY3plbmlhIHPEhQ0KICAgIHJvemR6aWVsYW5lIHBvbWnEmWR6eSByw7PFvG5lIGdydXB5Pw0KDQotICAgQ3p5IHcgdHltIHplc3Rhd2llIGRhbnljaCBpc3RuaWVqxIUgZG93b2R5IG5hIGR5c2tyeW1pbmFjasSZIHDFgmFjb3fEhQ0KICAgIHplIHd6Z2zEmWR1IG5hIHDFgmXEhz8NCg0KLSAgIEphayBwcnp5ZHppZWxhbnkgamVzdCBidWTFvGV0IHcgemFsZcW8bm/Fm2NpIG9kIGdydXB5IGkgemFrcmVzdQ0KICAgIG9ib3dpxIV6a8Ozdz8NCg0KYGBge3IgfQ0KIyB3eW1pYXJ5IHJhbWtpOg0KZGltKHNhbGFyaWVzKQ0KIyBuYXp3eSBrb2x1bW46DQpuYW1lcyhzYWxhcmllcykNCmBgYA0KDQojIyBIaXN0b2dyYW15DQoNCmBgYHtyIH0NCmhpc3Qoc2FsYXJpZXMkVG90YWxQYXksbWFpbj0iVG90YWwgUGF5IiwgeGxhYj0iUGF5IChpbiBkb2xsYXJzKSIpDQphYmxpbmUodiA9IG1lYW4oc2FsYXJpZXMkVG90YWxQYXkpLGx0eT0iZGFzaGVkIikNCmFibGluZSh2ID0gbWVkaWFuKHNhbGFyaWVzJFRvdGFsUGF5KSkNCmxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQ9YygiTWVkaWFuYSIsIsWacmVkbmlhIiksbHR5PWMoInNvbGlkIiwiZGFzaGVkIikpDQpgYGANCg0KYGBge3IgfQ0KcGFyKG1mcm93PWMoMiwyKSkNCmhpc3Qoc2FsYXJpZXMkVG90YWxQYXksbWFpbj0iVG90YWwgUGF5LCBkZWZhdWx0IGJyZWFrcyIsIHhsYWI9IlBheSAoaW4gZG9sbGFycykiKQ0KaGlzdChzYWxhcmllcyRUb3RhbFBheSxtYWluPSJUb3RhbCBQYXksIGJyZWFrcz0xMDAiLCB4bGFiPSJQYXkgKGluIGRvbGxhcnMpIiwgYnJlYWtzPTEwMCkNCmhpc3Qoc2FsYXJpZXMkVG90YWxQYXksbWFpbj0iVG90YWwgUGF5LCBicmVha3M9MTAwMCIsIHhsYWI9IlBheSAoaW4gZG9sbGFycykiLGJyZWFrcz0xMDAwKQ0KYGBgDQoNCmBgYHtyIH0NCmhpc3Qoc2FsYXJpZXMkVG90YWxQYXksbWFpbj0iVG90YWwgUGF5LCBab29tZWQtaW4iLCB4bGFiPSJQYXkgKGluIGRvbGxhcnMpIiwgeGxpbT1jKDAsMWU1KSwgYnJlYWtzPTEwMDApDQpgYGANCg0KYGBge3IgfQ0Kc2FsYXJpZXMyIDwtIHN1YnNldChzYWxhcmllcywgSm9iVGl0bGU9PSJGaXJlZmlnaHRlciIgJiBTdGF0dXM9PSJGVCIpDQpkaW0oc2FsYXJpZXMyKQ0KYGBgDQoNCmBgYHtyIH0NCnBhcihtZnJvdz1jKDIsMikpDQpoaXN0KHNhbGFyaWVzMiRUb3RhbFBheSxtYWluPSJGaXJlZmlnaHRlcnMsIGRlZmF1bHQgYnJlYWtzIiwgeGxhYj0iUGF5IChpbiBkb2xsYXJzKSIpDQpoaXN0KHNhbGFyaWVzMiRUb3RhbFBheSxtYWluPSJGaXJlZmlnaHRlcnMsIGJyZWFrcz0zMCIsIHhsYWI9IlBheSAoaW4gZG9sbGFycykiLCBicmVha3M9MzApDQpoaXN0KHNhbGFyaWVzMiRUb3RhbFBheSxtYWluPSJGaXJlZmlnaHRlcnMsIGJyZWFrcz0xMDAiLCB4bGFiPSJQYXkgKGluIGRvbGxhcnMpIiwgYnJlYWtzPTEwMCkNCmhpc3Qoc2FsYXJpZXMyJFRvdGFsUGF5LG1haW49IkZpcmVmaWdodGVycywgYnJlYWtzPTEwMDAiLCB4bGFiPSJQYXkgKGluIGRvbGxhcnMpIixicmVha3M9MTAwMCkNCmBgYA0KDQojIyBXeWtyZXN5IHB1ZGXFgmtvd2UNCg0KYGBge3IgfQ0KcGFyKG1mcm93PWMoMSwxKSkNCmJveHBsb3Qoc2FsYXJpZXMkVG90YWxQYXksbWFpbj0iVG90YWwgUGF5LCBicmVha3M9MTAwMCIsIHlsYWI9IlBheSAoaW4gZG9sbGFycykiKQ0KYGBgDQoNCiMgRXN0eW1hY2phIGZ1bmtjamkgZ8SZc3RvxZtjaQ0KDQpQaWVyd3N6eSByYXBvcnQgZG90eWN6eSBuaWVwYXJhbWV0cnljem5laiBlc3R5bWFjamkgZ8SZc3RvxZtjaS4gS2xhc3ljem55bQ0KbmllcGFyYW1ldHJ5Y3pueW0gZXN0eW1hdG9yZW0gZ8SZc3RvxZtjaSBqZXN0IGhpc3RvZ3JhbSwga3TDs3J5IGRvc3RhcmN6YQ0KbmllY2nEhWfFgmUgaSBzdGHFgmUgb3N6YWNvd2FuaWEuIFcgdHltIHJhcG9yY2llIHNrdXBpb25vIHNpxJkgbmEgbmlla3TDs3J5Y2gNCmFsdGVybmF0eXdhY2gsIGt0w7NyZSB6YXBld25pYWrEhSBjacSFZ8WCZSBsdWIgbmF3ZXQgZ8WCYWRraWUgb3N6YWNvd2FuaWENCnphbWlhc3QuDQoNCipNZXRvZHkga2VybmVsb3dlKiBzdGFub3dpxIUgd2HFvG7EhSBrbGFzxJkgZ8WCYWRraWNoIGVzdHltYXRvcsOzdyBnxJlzdG/Fm2NpIGkNCnphaW1wbGVtZW50b3dhbmUgc8SFIHByemV6IGZ1bmtjasSZIFIgYGRlbnNpdHkoKWAuIEVzdHltYXRvcnkgdGUgc8SFIHcNCnphc2FkemllIHR5bGtvIGxva2FsbmllIHdhxbxvbnltaSDFm3JlZG5pbWksIGEgaWNoIG9ibGljemVuaWUgamVzdA0Kc3Rvc3Vua293byBwcm9zdGUgdyB0ZW9yaWkuIFcgcHJha3R5Y2UsIHLDs8W8bmUgd3lib3J5IHNwb3NvYnUNCmltcGxlbWVudGFjamkgb2JsaWN6ZcWEIG1vZ8SFIGplZG5hayBtaWXEhyBkdcW8eSB3cMWCeXcgbmEgcnplY3p5d2lzdHkgY3phcw0Kb2JsaWN6ZcWELCBhIGltcGxlbWVudGFjasSZIGtlcm5lbG93eWNoIGVzdHltYXRvcsOzdyBnxJlzdG/Fm2NpIHppbHVzdHJ1amUNCnRyenkgcHVua3R5Og0KDQotICAgamXFm2xpIHRvIG1vxbxsaXdlLCB3eWJpZXJ6IHdla3Rvcnl6b3dhbmUgaW1wbGVtZW50YWNqZSB3IFIsDQotICAgamXFm2xpIG5pZXdpZWxrYSBzdHJhdGEgdyBkb2vFgmFkbm/Fm2NpIGplc3QgZG8gemFha2NlcHRvd2FuaWEsDQogICAgcHJ6eWJsacW8b25lIHJvendpxIV6YW5pZSBtb8W8ZSBiecSHIG8gcnrEmWR5IHdpZWxrb8WbY2kgc3p5YnN6ZSBuacW8DQogICAgaW1wbGVtZW50YWNqYSBsaXRlcmFsbmEsDQotICAgY3phcyBwb3RyemVibnkgZG8gbnVtZXJ5Y3puZWogb2NlbnkgcsOzxbxueWNoIFtmdW5rY2plDQogICAgZWxlbWVudGFybmVdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0VsZW1lbnRhcnlfZnVuY3Rpb24pIG1vxbxlDQogICAgYmFyZHpvIHphbGXFvGXEhyBvZCBmdW5rY2ppIGkgc3Bvc29idSBpbXBsZW1lbnRhY2ppIG9ibGljemXFhC4NCg0KTWV0b2R5IGtlcm5lbG93ZSBvcGllcmFqxIUgc2nEmSBuYSBqZWRueW0gbHViIHdpxJljZWogKnBhcmFtZXRyYWNoDQpyZWd1bGFybm/Fm2NpKiwga3TDs3JlIG11c3rEhSBiecSHIGRvYnJhbmUgdGFrLCBhYnkgb3NpxIVnbsSFxIcgd8WCYcWbY2l3xIUNCnLDs3dub3dhZ8SZIHcgZG9zdG9zb3dhbml1IGRvIGRhbnljaCBiZXogemJ5dG5pZWdvIGRvc3Rvc293eXdhbmlhIHNpxJkgZG8NCmxvc293ZWogem1pZW5ub8WbY2kgdyBkYW55Y2guDQoNCld5YsOzciBvZHBvd2llZG5pZWogaWxvxZtjaSByZWd1bGFybm/Fm2NpIGplc3QgcsOzd25pZSB3YcW8bnkgamFrIHd5YsOzcg0KbWV0b2R5IGRvIHXFvHljaWEgdyBwaWVyd3N6ZWoga29sZWpub8WbY2kuIFcgcnplY3p5d2lzdG/Fm2NpIG1vxbxlIGJ5xIcNCndhxbxuaWVqc3p5LiBUYWsgbmFwcmF3ZMSZIG5pZSBtYW15IGtvbXBsZXRuZWogaW1wbGVtZW50YWNqaQ0KbmllcGFyYW1ldHJ5Y3puZWdvIGVzdHltYXRvcmEgZG9ww7NraSBuaWUgemFpbXBsZW1lbnR1amVteQ0KYXV0b21hdHljem5lZ28sIG9wYXJ0ZWdvIG5hIGRhbnljaCBzcG9zb2J1IHd5Ym9ydSBpbG/Fm2NpIHJlZ3VsYWNqaS4NCg0KSW1wbGVtZW50YWNqYSB0eWxrbyBvYmxpY3plxYQgZGxhIG9jZW55IGVzdHltYXRvcmEgasSFZHJhLCBwb3dpZWR6bXksIGkNCnBvem9zdGF3aWFqxIVjIHRvIGNhxYJrb3dpY2llIHXFvHl0a293bmlrb3dpIHd5Ym9ydSBzemVyb2tvxZtjaSBwYXNtYSBqZXN0DQpwcmFjxIUgdyBwb8WCb3dpZSB3eWtvbmFuxIUuIE1ldG9keSBpIGltcGxlbWVudGFjamUgZG8gd3lib3J1IHN6ZXJva2/Fm2NpDQpwYXNtYSBzxIUgd2nEmWMgdyB0eW0gcmFwb3JjaWUgb23Ds3dpb25lIGRvxZvEhyBzemN6ZWfDs8WCb3dvLg0KDQpXIG9zdGF0bmllaiBjesSZxZtjaSBwcnplcHJvd2Fkem9uYSBqZXN0IGFuYWxpemEgcHJhd2RvcG9kb2JpZcWEc3R3YS4gUm9iaQ0Kc2nEmSB0byB3IGNlbHUgZGFsc3plZ28gd3lqYcWbbmllbmlhLCBkbGFjemVnbyBwb3RyemVibmUgc8SFIGVzdHltYXRvcnkgeg0KcmVndWxhcnl6YWNqxIUgdyBjZWx1IHVuaWtuacSZY2lhIG5hZG1pZXJuZWdvIGRvcGFzb3dhbmlhIGRvIGRhbnljaCwgb3Jheg0KZGxhY3plZ28gbmllIGlzdG5pZWplIHcgb2fDs2xlIG5pZXBhcmFtZXRyeWN6bnkgbWFrc3ltYWxuZWdvDQpwcmF3ZG9wb2RvYmllxYRzdHdhIGVzdHltYXRvcmEgZ8SZc3RvxZtjaS4gUmVndWxhcnl6YWNqxJkNCnByYXdkb3BvZG9iaWXFhHN0d2Ftb8W8bmEgb3NpxIVnbsSFxIcgcG9wcnpleiBvZ3JhbmljemVuaWUgc3phY3Vua8OzdyBnxJlzdG/Fm2NpDQpkbyByb2R6aW55IGNvcmF6IGJhcmR6aWVqIGVsYXN0eWN6bnljaCBnxJlzdG/Fm2NpIHBhcmFtZXRyeWN6bnljaCwga3TDs3JlDQpzxIUgZG9wYXNvd2FuZSBkbyBkYW55Y2guIEplc3QgdG8gem5hbmUgamFrbyAqbWV0b2RhIHNpdCouIElubmUgcG9kZWrFm2NpZQ0Kb3BpZXJhIHNpxJkgbmEgcm96c3plcnplbmlhY2ggYmF6b3d5Y2gsIGFsZSB3IG9idSBwcnp5cGFka2FjaA0KYXV0b21hdHljem55IHd5YsOzciB3aWVsa2/Fm2NpIHJlZ3VsYXJub8WbY2kgamVzdCB0YWsgc2FtbyB3YcW8bnkgamFrIHcNCnByenlwYWRrdSBtZXRvZCBqxIVkcm93eWNoLg0KDQpBYnkgdXR3b3J6ecSHIHd5a3JlcyBnxJlzdG/Fm2NpIGrEhWRyYSwgbXVzaXN6IG9zemFjb3dhxIcgZ8SZc3RvxZvEhyBqxIVkcmEuIFcNCnR5bSBjZWx1IG1vxbxuYSB1xbx5xIcgZnVua2NqaSBkZW5zaXR5LCBhIG5hc3TEmXBuaWUgcHJ6ZWthemHEhyBvYmlla3QNCmRlbnNpdHkgZG8gZnVua2NqaSBwbG90Lg0KDQpgYGB7cn0NCiMgZGFuZQ0Kc2V0LnNlZWQoMTQwMTIwMjEpDQpkYXRhIDwtIHJub3JtKDIwMCwgbWVhbiA9IDQpDQoNCiMgS2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbg0KZCA8LSBkZW5zaXR5KGRhdGEpDQoNCiMgS2VybmVsIGRlbnNpdHkgcGxvdA0KcGxvdChkLCBsd2QgPSAyLCBtYWluID0gIkRlZmF1bHQga2VybmVsIGRlbnNpdHkgcGxvdCIpDQoNCmBgYA0KDQpBcmd1bWVudCBqxIVkcmEgZnVua2NqaSBnxJlzdG/Fm2NpIGRvbXnFm2xuaWUgdcW8eXdhIGrEhWRyYSBnYXVzc293c2tpZWdvDQooa2VybmVsID0gImdhdXNzaWFuIiksIGFsZSBkb3N0xJlwbnljaCBqZXN0IHdpxJljZWogdHlww7N3IGrEhWRyYSwgdGFraWNoDQpqYWsgInByb3N0b2vEhXRuZSIsICJ0csOzamvEhXRuZSIsICJlcGFuZWNobmlrb3YiLCAiYml3ZWlnaHQiLCAiY29zaW5lIiBpDQoib3B0Y29zaW5lIi4gV3liw7NyIGLEmWR6aWUgemFsZcW8YcWCIG9kIHR3b2ljaCBkYW55Y2gsIGFsZSB3IHdpxJlrc3pvxZtjaQ0Kc2NlbmFyaXVzenkgd2FydG/Fm8SHIGRvbXnFm2xuYSBqZXN0IG5hamJhcmR6aWVqIHphbGVjYW5hLg0KDQpgYGB7cn0NCiMgRGF0YQ0Kc2V0LnNlZWQoMTQwMTIwMjEpDQpkYXRhIDwtIHJub3JtKDIwMCwgbWVhbiA9IDQpDQoNCiMgS2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbg0KZCA8LSBkZW5zaXR5KGRhdGEsDQogICAgICAgICAgICAga2VybmVsID0gInJlY3Rhbmd1bGFyIikNCg0KIyBLZXJuZWwgZGVuc2l0eSBwbG90DQpwbG90KGQsIGx3ZCA9IDIsIG1haW4gPSAiUmVjdGFuZ3VsYXIga2VybmVsIikNCmBgYA0KDQpgYGB7cn0NCiMgRGF0YQ0Kc2V0LnNlZWQoMTQwMTIwMjEpDQpkYXRhIDwtIHJub3JtKDIwMCwgbWVhbiA9IDQpDQoNCiMgS2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbg0KZCA8LSBkZW5zaXR5KGRhdGEsDQogICAgICAgICAgICAga2VybmVsID0gInRyaWFuZ3VsYXIiKQ0KDQojIEtlcm5lbCBkZW5zaXR5IHBsb3QNCnBsb3QoZCwgbHdkID0gMiwgbWFpbiA9ICJUcmlhbmd1bGFyIGtlcm5lbCIpDQpgYGANCg0KYGBge3J9DQojIERhdGENCnNldC5zZWVkKDE0MDEyMDIxKQ0KZGF0YSA8LSBybm9ybSgyMDAsIG1lYW4gPSA0KQ0KDQojIEtlcm5lbCBkZW5zaXR5IGVzdGltYXRpb24NCmQgPC0gZGVuc2l0eShkYXRhLA0KICAgICAgICAgICAgIGtlcm5lbCA9ICJlcGFuZWNobmlrb3YiKQ0KDQojIEtlcm5lbCBkZW5zaXR5IHBsb3QNCnBsb3QoZCwgbHdkID0gMiwgbWFpbiA9ICJFcGFuZWNobmlrb3Yga2VybmVsIikNCmBgYA0KDQpgYGB7cn0NCiMgRGF0YQ0Kc2V0LnNlZWQoMTQwMTIwMjEpDQpkYXRhIDwtIHJub3JtKDIwMCwgbWVhbiA9IDQpDQoNCiMgS2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbg0KZCA8LSBkZW5zaXR5KGRhdGEsDQogICAgICAgICAgICAga2VybmVsID0gImJpd2VpZ2h0IikNCg0KIyBLZXJuZWwgZGVuc2l0eSBwbG90DQpwbG90KGQsIGx3ZCA9IDIsIG1haW4gPSAiQml3ZWlnaHQga2VybmVsIikNCmBgYA0KDQpgYGB7cn0NCiMgRGF0YQ0Kc2V0LnNlZWQoMTQwMTIwMjEpDQpkYXRhIDwtIHJub3JtKDIwMCwgbWVhbiA9IDQpDQoNCiMgS2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbg0KZCA8LSBkZW5zaXR5KGRhdGEsDQogICAgICAgICAgICAga2VybmVsID0gImNvc2luZSIpDQoNCiMgS2VybmVsIGRlbnNpdHkgcGxvdA0KcGxvdChkLCBsd2QgPSAyLCBtYWluID0gIkNvc2luZSBrZXJuZWwiKQ0KYGBgDQoNCiMjIFNlbGVrY2phIHBhc21hDQoNCkFyZ3VtZW50IGJ3IGZ1bmtjamkgZ8SZc3RvxZtjaSBwb3p3YWxhIG5hIHptaWFuxJkgdcW8eXdhbmVnbyBwYXNtYQ0Kd3lnxYJhZHphbmlhLiBNb8W8ZXN6IHByemVrYXphxIcgd2FydG/Fm8SHIGx1YiBjacSFZyB6bmFrw7N3IHBvZGFqxIVjeSByZWd1xYLEmQ0Kd3lib3J1IGx1YiBmdW5rY2rEmS4gRG9tecWbbG7EhSB3YXJ0b8WbY2nEhSBqZXN0ICJucmQwIiAobHViIGJ3Lm5yZDAoLikpLA0Ka3TDs3JhIGltcGxlbWVudHVqZSBwb2RlasWbY2llIG9wYXJ0ZSBuYSB6YXNhZHppZSByZWd1xYJ5IGtjaXVrYSA6LSkgSW5uZQ0KZG9zdMSZcG5lIG9wY2plIHRvOg0KDQojIyMgUmVndcWCYSBTY290dGEgKDE5OTIpDQoNCmBgYHtyfQ0KIyBEYXRhDQpzZXQuc2VlZCgxNDAxMjAyMSkNCmRhdGEgPC0gcm5vcm0oMjAwLCBtZWFuID0gNCkNCg0KIyBLZXJuZWwgZGVuc2l0eSBlc3RpbWF0aW9uDQpkIDwtIGRlbnNpdHkoZGF0YSwNCiAgICAgICAgICAgICBidyA9ICJucmQiKQ0KDQojIEtlcm5lbCBkZW5zaXR5IHBsb3QNCnBsb3QoZCwgbHdkID0gMiwgbWFpbiA9ICJucmQgYmFuZHdpZHRoIikNCmBgYA0KDQojIyMgTmllb2JjacSFxbxvbmEgY3Jvc3Mtd2FsaWRhY2phDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIERhdGENCnNldC5zZWVkKDE0MDEyMDIxKQ0KZGF0YSA8LSBybm9ybSgyMDAsIG1lYW4gPSA0KQ0KDQojIEtlcm5lbCBkZW5zaXR5IGVzdGltYXRpb24NCmQgPC0gZGVuc2l0eShkYXRhLA0KICAgICAgICAgICAgIGJ3ID0gInVjdiIpDQoNCiMgS2VybmVsIGRlbnNpdHkgcGxvdA0KcGxvdChkLCBsd2QgPSAyLCBtYWluID0gInVjdiBiYW5kd2lkdGgiKQ0KYGBgDQoNCiMjIyBPYmNpxIXFvG9uYSBjcm9zcy13YWxpZGFjamENCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgRGF0YQ0Kc2V0LnNlZWQoMTQwMTIwMjEpDQpkYXRhIDwtIHJub3JtKDIwMCwgbWVhbiA9IDQpDQoNCiMgS2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbg0KZCA8LSBkZW5zaXR5KGRhdGEsDQogICAgICAgICAgICAgYncgPSAiYmN2IikNCg0KIyBLZXJuZWwgZGVuc2l0eSBwbG90DQpwbG90KGQsIGx3ZCA9IDIsIG1haW4gPSAiYmN2IGJhbmR3aWR0aCIpIA0KDQpgYGANCg0KIyMjIE1ldG9kYSBTaGVhdGhlciAmIEpvbmVzICgxOTkxKQ0KDQpgYGB7cn0NCiMgRGF0YQ0Kc2V0LnNlZWQoMTQwMTIwMjEpDQpkYXRhIDwtIHJub3JtKDIwMCwgbWVhbiA9IDQpDQoNCiMgS2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbg0KZCA8LSBkZW5zaXR5KGRhdGEsDQogICAgICAgICAgICAgYncgPSAiU0oiKQ0KDQojIEtlcm5lbCBkZW5zaXR5IHBsb3QNCnBsb3QoZCwgbHdkID0gMiwgbWFpbiA9ICJTSiBiYW5kd2lkdGgiKQ0KYGBgDQoNCk9zdHJ6ZcW8ZW5pZSENCg0KOiAgIFN6ZXJva2/Fm8SHIHBhc21hIG11c2kgYnnEhyBiYXJkem8gc3RhcmFubmllIGRvYnJhbmEhIE1hxYJhIHN6ZXJva2/Fm8SHDQogICAgcGFzbWEgc3Bvd29kdWplIHBvd3N0YW5pZSBuYWRtaWVybmllIGRvcGFzb3dhbmVqIGtyenl3ZWosIG5hdG9taWFzdA0KICAgIHpieXQgZHXFvGEgc3plcm9rb8WbxIcgcGFzbWEgc3Bvd29kdWplIHBvd3N0YW5pZSBrcnp5d2VqIG5hZG1pZXJuaWUNCiAgICB3eWfFgmFkem9uZWouDQoNCiMgxIZ3aWN6ZW5pZSAxLg0KDQpVcnVjaG9tIGRlbW8gZXN0eW1hdG9yYSBmdW5rY2ppIGfEmXN0b8WbY2kga2VybmVsLiBabWllbmlhaiB6YXLDs3dubyBkYW5lDQp3ZWrFm2Npb3dlLCBqYWsgaSBvcGNqZSBlc3R5bWF0b3JhIC0gc3plcm9rb8WbxIcgcGFzbWEgb3JheiByb2R6YWogZnVua2NqaQ0KasSFZHJvd2VqLiBDenkgd2lkemlzeiBpc3RvdG5lIHLDs8W8bmljZSB3IG9zemFjb3dhbml1Pw0KDQojIyBXbmlvc2tpDQoNClphdXdhxbxvbmUgcG9kY3phcyBla3NwZXJ5bWVudG93YW5pYSB6IHLDs8W8bnltaSBwYXJhbWV0cmFtaSBlc3R5bWF0b3JhDQpmdW5rY2ppIGfEmXN0b8WbY2kga2VybmVsOg0KDQpSb3prxYJhZHk6DQoNClJvemvFgmFkIG5vcm1hbG55IG1hIGR6d29ub3dhdHkga3N6dGHFgnQsIHNrb25jZW50cm93YW55IHdva8OzxYIgxZtyZWRuaWVqLg0KWm1pYW5hIMWbcmVkbmllaiBwcnplc3V3YSB3eWtyZXMgdyBwcmF3byBsdWIgbGV3bywgYSB3acSZa3N6ZSBvZGNoeWxlbmllDQpzdGFuZGFyZG93ZSBzcMWCYXN6Y3phIGdvLg0KDQpSb3prxYJhZCBiZXRhIG1vxbxlIHByenliaWVyYcSHIHLDs8W8bmUsIGN6xJlzdG8gYXN5bWV0cnljem5lIGtzenRhxYJ0eSwNCnphbGXFvG5lIG9kIHBhcmFtZXRyw7N3IM6xIGkgzrIuDQoNClJvemvFgmFkIHd5a8WCYWRuaWN6eSBtb8W8ZSBzcHJhd2lhxIcgxbxlIGfEmXN0b8WbY2kgc3p5YmtvIG1hbGVqZSBvZA0KcG9jesSFdGtvd2VqIHdhcnRvxZtjaS4gSW0gd2nEmWtzenkgcGFyYW1ldHIsIHR5bSBzenliY2llaiBzcGFkYS4NCg0KUm96a8WCYWQgamVkbm9zdGFqbnkgcHJ6eXBpc3VqZSBqZWRuYWtvd2UgcHJhd2RvcG9kb2JpZcWEc3R3byBkbGENCndzenlzdGtpY2ggd2FydG/Fm2NpIHcgb2tyZcWbbG9ueW0gcHJ6ZWR6aWFsZS4NCg0KTmlldHlwb3dlIHJvemvFgmFkeSBtYWrEhSBuaWVyZWd1bGFybnkga3N6dGHFgnQgaSBjesSZc3RvIHphbGXFvMSFIG9kDQpzcGVjeWZpY3pueWNoIGRhbnljaC4NCg0KS2VybmVsZQ0KDQpSb2R6YWogZGVjeWR1amUgbyBzcG9zb2JpZSB3eWfFgmFkemFuaWE6IEdhdXNzb3dza2llIGRhamUNCmR6d29ub3dhdHksZ8WCYWRraSBrc3p0YcWCdC4gQml3ZWlnaHQgd2nEmWNlaiB1d2FnaSBwcnp5cGlzdWplIHB1bmt0b20NCmJsaXNrbyDFm3JvZGthLiBDb3NpbnVzb3dlIHd5Z8WCYWR6YSB3YXJ0b8WbY2kgc3RvcG5pb3dvIHcga2llcnVua3Uga3JhxYRjw7N3DQpwcnplZHppYcWCdS4NCg0KU3plcm9rb8WbxIcgcGFzbWEgd3DFgnl3YSBuYSBwcmVjeXpqxJkgaSBnxYJhZGtvxZvEhyB3eWtyZXN1Og0KDQpXxJnFvHN6ZSBwYXNtbyB1bW/FvGxpd2lhIGxlcHN6ZSBvZHd6b3Jvd2FuaWUgZGV0YWxpLCBhbGUgbW/FvGUgcHJvd2FkemnEhyBkbw0KemJ5dCBzemN6ZWfDs8WCb3dlZ28gZG9wYXNvd2FuaWEuIE9wdHltYWxuZSBwYXNtbyBkb2JyemUgYmFsYW5zdWplDQpzemN6ZWfDs8WCeSBpIGfFgmFka2/Fm8SHLiBTemVyc3plIHBhc21vIGRhamUgZ8WCYWRzenkgd3lrcmVzLCBhbGUgbW/FvGUgemd1YmnEhw0Kbmlla3TDs3JlIGRldGFsZSBkYW55Y2guDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoInJlbW90ZXMiKSAjdHlsa28gcmF6ISBwb3RlbSAjDQojcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoImhlcmlja3MvS0RFIikgI3R5bGtvIHJheiEgcG90ZW0gIw0KbGlicmFyeShLREUpDQpzaGlueV9rZGUoKSANCmBgYA0KDQpgYGB7cn0NCmBgYA0KDQojIMSGd2ljemVuaWUgMi4NCg0KV3lrb3J6eXN0dWrEhWMgZG93b2xuxIUgZnVua2NqxJkgUiBkbyBlc3R5bWFjamkgZnVua2NqaSBnxJlzdG/Fm2NpIG9zemFjdWoNCmplaiBwcnplYmllZyBkbGEgd3luYWdyb2R6ZcWEICh6YmnDs3IgZGFueWNoIHNhbGFyaWVzKSBzdHJhxbxha8OzdyB3IFNhbg0KRnJhbmNpc2NvLiBXeWtvcnp5c3RhaiBtZXRvZHkgZ3JhZmljem5lIGRvc3TEmXBuZSB3IHBha2llY2llIGdncGxvdDIuDQpNaWxlIHdpZHppYW5lIHByemVrcm9qZSBvcmF6IG9kcG93aWVkemkgbmEgcHl0YW5pYSBiYWRhd2N6ZSB6YWRhbmUgbmENCndzdMSZcGllLg0KDQojIyBQb3ByemVkbmllIGJpYmxpb3RlY3praQ0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZ2VvbXRleHRwYXRoKQ0KbGlicmFyeShocmJydGhlbWVzKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KHZpcmlkaXMpDQpgYGANCg0KIyMgQmFkYW5pZSBzdHJ1a3R1cnkgZGFueWNoIFd5bmFncm9kemXFhCAoemJpw7NyIGRhbnljaCBzYWxhcmllcykgc3RyYcW8YWvDs3cgdyBTYW4gRnJhbmNpc2NvDQoNCmBgYHtyfQ0Kc2FsYXJpZXMzYTwtIHN1YnNldChzYWxhcmllcywgSm9iVGl0bGU9PSJGaXJlZmlnaHRlciIpDQpkaW0oc2FsYXJpZXMzYSkNCmBgYA0KDQpgYGB7cn0NCmhpc3Qoc2FsYXJpZXMzYSRUb3RhbFBheSxtYWluPSJDYcWCa293aXRlIHd5bmFncm9kemVuaWUiLCB4bGFiPSJXeW5hZ3JvZHplbmlhIHcgZG9sYXJhY2giLHlsYWI9IkN6xJlzdG90bGl3b8WbxIciKQ0KYWJsaW5lKHYgPSBtZWFuKHNhbGFyaWVzM2EkVG90YWxQYXkpLGx0eT0iZGFzaGVkIikNCmFibGluZSh2ID0gbWVkaWFuKHNhbGFyaWVzM2EkVG90YWxQYXkpKQ0KbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZD1jKCJNZWRpYW5hIiwixZpyZWRuaWEiKSxsdHk9Yygic29saWQiLCJkYXNoZWQiKSkNCmBgYA0KDQpgYGB7cn0NCnNhbGFyaWVzM2EgPC0gc3Vic2V0KHNhbGFyaWVzLCBKb2JUaXRsZT09IkZpcmVmaWdodGVyIikNCmRpbShzYWxhcmllczNhKQ0KYGBgDQoNClN6dWthbXkgY3p5IG5pZSBtYSBuaWdkemllIE5BDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0Kc2FsYXJpZXMzYSAlPiUgDQogIHN1bW1hcmlzZShuYV9jb3VudCA9IHN1bShpcy5uYShUb3RhbFBheSkpKQ0KI2lzLm5hKHNhbGFyaWVzM2EpDQpgYGANCg0KIyMjIFN6dWthbXkgY3p5IG5pZSBtYSB3YXJ0b8WbY2kgb2RzdGFqYWN5Y2gNCg0KV3lrcmVzIHB1ZGXFgmtvd3kgd3luYWdyb2R6ZW5pYSBzdHJhxbxha8OzdyB3IFNhbiBGcmFuY2lzY28geiB3YXJ0b8WbY2lhbWkNCm9kc3RhasSFY3ltaQ0KDQpgYGB7cn0NCmdncGxvdChzYWxhcmllczNhLCBhZXMoeSA9IFRvdGFsUGF5KSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAzMDAwMDApKSANCmBgYA0KDQpXeWtyZXMgcHVkZcWCa293eSB3eW5hZ3JvZHplbmlhIHN0cmHFvGFrw7N3IHcgU2FuIEZyYW5jaXNjbyBiZXN6IHdhcnRvxZtjaQ0Kb2RzdGFqxIVjeWNoDQoNCmBgYHtyfQ0KZ2dwbG90KHNhbGFyaWVzM2EsIGFlcyh5ID0gVG90YWxQYXkpKSArDQogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gTkEpICsNCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIDI1MDAwMCkpIA0KYGBgDQoNCmBgYHtyfQ0Kc2FsYXJpZXMzYSAlPiUgDQogIGZpbHRlcihUb3RhbFBheSA8IDI1MDAwMCAsIFRvdGFsUGF5ID43NTAwMCApICU+JSANCiAgZ2dwbG90KGFlcyh5ID0gVG90YWxQYXkpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIDI1MDAwMCkpDQpgYGANCg0KVHdvcnp5bXkgemJpw7NyIGRhbnljaCBiZXogd2FydG/Fm2NpIG9kc3RhasSFY3ljaA0KDQpgYGB7cn0NCnNhbGFyaWVzMzwtc2FsYXJpZXMzYSAlPiUNCiAgZmlsdGVyKFRvdGFsUGF5IDwgMjUwMDAwICwgVG90YWxQYXkgPjc1MDAwICkNCmBgYA0KDQpBYnkgc3ByYXdkemFpxIcgY3p5IHpiacOzciBzYWxhcmllcyAzIHBhc3VqZSBvbmEgZG8gbmFzemVqIGFuYWxpenkgLA0KcHJ6ZWpyenlteSBjenkgbmEgamVnbyBwb2RzdGF3aWUgZGEgc2nEmSBvZHBvd2llZHppZcSHIGFuIHB5dGFuaWU6IGphaw0Kem1pZW5pYcWCeSBzacSZIHd5bmFncm9kemVuaWEgd3Nyw7NkIHN0cmF6YWvDs3c/DQoNCld5a3JlcyB1a2F6dWrEhWN5IHptaWFuIHd5bmFncm9kemVuaWEgd3Nyw7NkIHN0cmF6YWvDs3cgbmEgcG9kc3Rhd2llIHpiaW9yeQ0Kc2FsYXJpZXMzDQoNCmBgYHtyLHdhcm5pbmc9RkFMU0V9DQpnZ3Bsb3QoZGF0YT1zYWxhcmllczMsIGFlcyh4PUJhc2VQYXksIGdyb3VwPVllYXIsIGZpbGw9WWVhcikpICsNCiAgICBnZW9tX2RlbnNpdHkoYWRqdXN0PTEuNSkgKw0KICAgIHRoZW1lX2lwc3VtKCkgKw0KICAgIGZhY2V0X3dyYXAoflllYXIpICsNCiAgICB0aGVtZSgNCiAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsDQogICAgICBwYW5lbC5zcGFjaW5nID0gdW5pdCgwLjEsICJsaW5lcyIpLA0KICAgICAgYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKQ0KICAgICkNCmBgYA0KDQpXeWtyZXMgdWthenVqxIVjeSB6bWlhbiB3eW5hZ3JvZHplbmlhIHdzcsOzZCBzdHJhemFrw7N3IG5hIHBvZHN0YXdpZSB6YmlvcnkNCnNhbGFyaWVzM2ENCg0KYGBge3Isd2FybmluZz1GQUxTRX0NCmdncGxvdChkYXRhPXNhbGFyaWVzM2EsIGFlcyh4PUJhc2VQYXksIGdyb3VwPVllYXIsIGZpbGw9WWVhcikpICsNCiAgICBnZW9tX2RlbnNpdHkoYWRqdXN0PTEuNSkgKw0KICAgIHRoZW1lX2lwc3VtKCkgKw0KICAgIGZhY2V0X3dyYXAoflllYXIpICsNCiAgICB0aGVtZSgNCiAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsDQogICAgICBwYW5lbC5zcGFjaW5nID0gdW5pdCgwLjEsICJsaW5lcyIpLA0KICAgICAgYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKQ0KICAgICkNCmBgYA0KDQpEZWN5ZHVqZW15IHNpxJkgcG96b3N0YcSHIHByenkgemJpb3JlIHNhbGFyaWVzM2EgYWJ5IHVrYXphxIcgYmFyZHppZWoNCnpyw7PFvG5pY293ZSB3eW5pa2kgdyBwb3N6Y3pnw7NsbnljaCBsYXRhY2guDQoNCiMjIEVzdHltYWNqaSBmdW5rY2ppIGfEmXN0b8WbY2kgZGxhIHd5bmFncm9kemXFhCBzdHJhxbxha8OzdyB3IFNhbiBGcmFuY2lzY28NCg0KSGlzdG9ncmFtIGkgZXN0eW1hY2phIGZ1bmtjamkgZ8SZc3RvxZtjaSBkbGEgV3luYWdyb2R6ZW5pZSBjYcWCa293aXRlZ28NCnN0cmHFvGFrw7N3DQoNCmBgYHtyfQ0KZ2dwbG90KHNhbGFyaWVzM2EsIGFlcyh4ID0gVG90YWxQYXkpKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gYWZ0ZXJfc3RhdChkZW5zaXR5KSksIGJpbnMgPSAzMA0KICAgICAgICAgICAgICAgICAsIGZpbGwgPSAiZ3JlZW4iLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC4yKSArDQogIGdlb21fZGVuc2l0eShjb2xvciA9ICJwdXJwbGUiLCBzaXplID0gMSkgKw0KICBsYWJzKCN0aXRsZSA9ICJIaXN0b2dyYW0gaSBlc3R5bWFjamEgZnVua2NqaSBnxJlzdG/Fm2NpIGRsYSBXeW5hZ3JvZHplbmllIGNhxYJrb3dpdGVnbyBzdHJhxbxha8OzdyIsDQogICAgICAgeCA9ICJXeW5hZ3JvZHplbmllIGNhxYJrb3dpdGUgc3RyYcW8YWvDs3ciLA0KICAgICAgIHkgPSAiR8SZc3RvxZvEhyIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KIyMgRXN0eW1hY2ppIGZ1bmtjamkgZ8SZc3RvxZtjaSBkbGEgd3luYWdyb2R6ZcWEIHN0cmHFvGFrw7N3IHcgU2FuIEZyYW5jaXNjbyB6YSBwb21vY2Ega2VybmVsYQ0KDQpgYGB7cn0NCmRhdGExIDwtIHNhbGFyaWVzM2EkVG90YWxQYXkNCmQxIDwtIGRlbnNpdHkoZGF0YTEsDQogICAgICAgICAgICAga2VybmVsID0gImNvc2luZSIpDQpwbG90KGQxLCBsd2QgPSAyLCBtYWluID0gIkNvc2luZSBrZXJuZWwiKQ0KYGBgDQoNCmBgYHtyfQ0KZDIgPC0gZGVuc2l0eShkYXRhMSwNCiAgICAgICAgICAgICBidyA9ICJTSiIpDQpwbG90KGQyLCBsd2QgPSAyLCBtYWluID0gIlNKIGJhbmR3aWR0aCIpDQpgYGANCg0KYGBge3J9DQpkMyA8LSBkZW5zaXR5KGRhdGExLA0KICAgICAgICAgICAgIGJ3ID0gIm5yZCIpDQpwbG90KGQzLCBsd2QgPSAyLCBtYWluID0gIm5yZCBiYW5kd2lkdGgiKQ0KYGBgDQoNCiMjIyBKYWsgem1pZW5pYcWCeSBzacSZIHd5bmFncm9kemVuaWEgdyBjemFzaWUgd3Nyw7NkIHN0cmHFvGFrw7N3Pw0KDQpXeWtyZXMgd3luYWdyb2R6ZW5pYSB6YSB3IHphbGXFvG5vxZtjaSBvZCByb2t1DQoNCmBgYHtyLHdhcm5pbmc9RkFMU0V9DQoNCmdncGxvdChkYXRhPXNhbGFyaWVzM2EsIGFlcyh4PVRvdGFsUGF5LCBncm91cD1ZZWFyLCBmaWxsPVllYXIpKSArDQogICAgZ2VvbV9kZW5zaXR5KGFkanVzdD0xLjUsIGFscGhhPS40KSArDQogICAgdGhlbWVfaXBzdW0oKQ0KDQpgYGANCg0KIyMjIEphayB6bWllbmlhxYJ5IHNpxJkgd3luYWdyb2R6ZW5pYSB6YSBuYWRnb2R6aW55IHcgY3phc2llIHdzcsOzZCBzdHJhxbxha8Ozdz8NCg0KV3lrcmVzIHd5bmFncm9kemVuaWEgemEgbmFkZ29kemlueSB3IHphbGXFvG5vxZtjaSBvZCByb2t1DQoNCmBgYHtyLHdhcm5pbmc9RkFMU0V9DQpnZ3Bsb3QoZGF0YT1zYWxhcmllczNhLCBhZXMoeD1PdmVydGltZVBheSwgZ3JvdXA9YXMuZmFjdG9yKFllYXIpLCBmaWxsPVllYXIpKSArDQogICAgZ2VvbV9kZW5zaXR5KGFkanVzdD0xLjUsIGFscGhhPTAuNCkgKw0KICAgIHRoZW1lX2lwc3VtKCkNCiAgIA0KYGBgDQoNCld5a3JlcyB3eW5hZ3JvZHplbmlhIHBvZHN0YXdvd2VnbyB3IHphbGXFvG5vxZtjaSBvZCByb2t1DQoNCmBgYHtyLHdhcm5pbmc9RkFMU0V9DQpnZ3Bsb3QoZGF0YT1zYWxhcmllczMsIGFlcyh4PUJhc2VQYXksIGdyb3VwPVllYXIsIGZpbGw9WWVhcikpICsNCiAgICBnZW9tX2RlbnNpdHkoYWRqdXN0PTEuNSkgKw0KICAgIHRoZW1lX2lwc3VtKCkgKw0KICAgIGZhY2V0X3dyYXAoflllYXIpICsNCiAgICB0aGVtZSgNCiAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsDQogICAgICBwYW5lbC5zcGFjaW5nID0gdW5pdCgwLjEsICJsaW5lcyIpLA0KICAgICAgYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKQ0KICAgICkNCmBgYA0KDQpgYGB7cn0NCmdydXB5IDwtIHNhbGFyaWVzM2EgJT4lDQogIGZpbHRlcihZZWFyICVpbiUgYygiMjAxMiIsICIyMDEzIiwgIjIwMTQiKSkNCg0KIyBUd29yemVuaWUgd3lrcmVzdSB6IHBvcHJhd2lvbnltIGtvbG9yb3dhbmllbQ0KZ2dwbG90KGdydXB5LCBhZXMoeCA9IFRvdGFsUGF5LCBjb2xvciA9IGFzLmZhY3RvcihZZWFyKSkpICsNCiAgZ2VvbV9kZW5zaXR5KHNpemUgPSAxKSArDQogIGxhYnModGl0bGUgPSAiRXN0eW1hY2phIGZ1bmtjamkgZ8SZc3RvxZtjaSBkbGEgd3luYWdyb2R6ZcWEIHcgcsOzxbxueWNoIGxhdGFjaCB3c3LDs2Qgc3RyYcW8YWvDs3ciLA0KICAgICAgIHggPSAiV3luYWdyb2R6ZW5pZSAoVG90YWwgUGF5KSIsDQogICAgICAgeSA9ICJHxJlzdG/Fm8SHIiwNCiAgICAgICBjb2xvciA9ICJST0siKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCld5a3JlcyB1a2F6dWrEhWN5IHptaWFuIHcgZG9kYXRrYWNoIGRvIHd5bmFncm9kemVuaWEgd3Nyw7NkIHN0cmF6YWvDs3cgbmENCnBvZHN0YXdpZSB6Ymlvcnkgc2FsYXJpZXMzDQoNCmBgYHtyLHdhcm5pbmc9RkFMU0V9DQpnZ3Bsb3QoZGF0YT1zYWxhcmllczNhLCBhZXMoeD1CZW5lZml0cywgZ3JvdXA9WWVhciwgZmlsbD1ZZWFyKSkgKw0KICAgIGdlb21fZGVuc2l0eShhZGp1c3Q9MS41KSArDQogICAgdGhlbWVfaXBzdW0oKSArDQogICAgZmFjZXRfd3JhcCh+WWVhcikgKw0KICAgIHRoZW1lKA0KICAgICAgbGVnZW5kLnBvc2l0aW9uPSJub25lIiwNCiAgICAgIHBhbmVsLnNwYWNpbmcgPSB1bml0KDAuMSwgImxpbmVzIiksDQogICAgICBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpDQogICAgKQ0KYGBgDQoNCiMgUHl0YW5pYSBiYWRhd2N6ZQ0KDQojIyBKYWsgem1pZW5pYcWCeSBzacSZIHd5bmFncm9kemVuaWEgdyBjemFzaWUgbWnEmWR6eSByw7PFvG55bWkgZ3J1cGFtaSBsdWR6aT8NCg0KYGBge3J9DQpzYWxhcmllcyRKb2JUaXRsZSAlPiUgdGFibGUoKSAlPiUgc29ydChkZWNyZWFzaW5nID0gVFJVRSkgJT4lDQpoZWFkKDIwKQ0KYGBgDQoNCmBgYHtyfQ0KZ3J1cHkxIDwtIHNhbGFyaWVzICU+JQ0KICBmaWx0ZXIoSm9iVGl0bGUgJWluJSBjKCJGaXJlZmlnaHRlciIsICJQb2xpY2UgT2ZmaWNlciIsICJFbmdpbmVlciIsICJTcGVjaWFsIE51cnNlIiwiQ3VzdG9kaWFuIiwiTlVSU0UiLCJHZW5lcmFsIExhYm9yZXIiKSkNCg0KDQpnZ3Bsb3QoZ3J1cHkxLCBhZXMoeCA9IFRvdGFsUGF5LCBjb2xvciA9IEpvYlRpdGxlKSkgKw0KICBnZW9tX2RlbnNpdHkoc2l6ZSA9IDEpICsNCiAgbGFicyh0aXRsZSA9ICJFc3R5bWFjamEgZnVua2NqaSBnxJlzdG/Fm2NpIGRsYSB3eW5hZ3JvZHplxYQgIHdzcsOzZCByw7PFvG55Y2ggemF3b2TDs3ciLA0KICAgICAgIHggPSAiV3luYWdyb2R6ZW5pZSIsDQogICAgICAgeSA9ICJHxJlzdG/Fm8SHIiwNCiAgICAgICBjb2xvciA9ICJaYXfDs2QiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQo=