1 Krótkie wprowadzenie teoretyczne

Kernel Density Estimation (KDE), czyli estymacja jądrowa gęstości, jest nieparametryczną metodą oszacowania funkcji gęstości rozkładu zmiennej losowej na podstawie dostępnych danych. W przeciwieństwie do metod parametrycznych, takich jak estymacja rozkładu normalnego, KDE nie wymaga przyjęcia konkretnej formy rozkładu danych. Zamiast tego, KDE buduje estymację funkcji gęstości w sposób gładki, opierając się na rzeczywistych obserwacjach z próby. Dla każdego punktu danych umieszczana jest funkcja jądrowa (kernel), najczęściej o kształcie zbliżonym do krzywej dzwonowej, np. rozkładu Gaussa, której zadaniem jest przypisanie “wagi” obserwacjom w pobliżu danego punktu. Szerokość pasma (bandwidth) kontroluje stopień wygładzenia całkowitej funkcji gęstości.

2 Ć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?

#install.packages("remotes") #tylko raz! potem #
#remotes::install_github("hericks/KDE") #tylko raz! potem #
#library(KDE)
#shiny_kde() 

Kilka wniosków:

-Małe pasmo (wąska szerokość): Gdy szerokość pasma jest bardzo mała, estymacja gęstości staje się bardzo szczegółowa i dokładnie dopasowuje się do poszczególnych obserwacji, co powoduje, że estymator jest podatny na lokalne wahania i “szumy” w danych.

-Duże pasmo (szeroka szerokość): Przy dużym paśmie estymator gęstości jest wygładzony, co sprawia, że lokalne szczegóły są mniej widoczne, ale za to lepiej oddaje ogólny kształt rozkładu danych. Przy zbyt dużym paśmie może jednak dojść do nadmiernego wygładzenia (underfitting), gdzie szczegóły rozkładu są zupełnie zgubione, a estymator przestaje odzwierciedlać realne zmiany w danych.

-Przy mniejszej liczbie danych estymator KDE jest bardziej wrażliwy na dobór szerokości pasma i rodzaju jądra. Przy małych próbach każde jądro ma duży wpływ na wynik, a małe pasmo generuje wyraźny szum.

-Przy większej liczbie obserwacji KDE działa bardziej stabilnie. Większe próby lepiej odzwierciedlają rzeczywisty rozkład danych, a estymacja staje się bardziej precyzyjna. Dobór szerokości pasma i jądra ma mniejszy wpływ na wynik przy dużej liczbie danych.

-Użycie walidacji krzyżowej (cross-validation) do doboru szerokości pasma okazało się skuteczne w uzyskaniu dobrze zbalansowanego estymatora, który zachowuje kształt rozkładu bez nadmiernego wygładzania.

-Jądro Gaussowskie daje chyba najbardziej intuicyjne i gładkie estymacje.

3 Ć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.

salaries <- read.csv("https://github.com/kflisikowski/ds/raw/master/Salaries.csv")

3.1 Histogram

firefighter_salaries <- subset(salaries, JobTitle == "Firefighter")

hist(firefighter_salaries$TotalPay, 
     main = "Total Pay for Firefighters", 
     xlab = "Pay (in dollars)", 
     col = "lightblue", 
     border = "black")

abline(v = mean(firefighter_salaries$TotalPay, na.rm = TRUE), col = "red", lty = "dashed")
abline(v = median(firefighter_salaries$TotalPay, na.rm = TRUE), col = "blue", lty = "solid")
legend("topright", 
       legend = c("Mediana", "Średnia"), 
       col = c("blue", "red"), 
       lty = c("solid", "dashed"))

Większość strażaków zarabia w przedziale około 100,000 - 175,000 dolarów rocznie, co stanowi największe skupisko wartości na wykresie. Rozkład ten jest względnie symetryczny, co sugeruje, że wartości wynagrodzeń są równomiernie rozłożone wokół centralnej wartości. Z tego wykresu wynika, że mediana i średnia są bardzo zbliżone do siebie, co sugeruje, że w rozkładzie nie występują duże wartości odstające, które mogłyby znacząco zniekształcić średnią.

firefighter_salaries <- subset(salaries, JobTitle == "Firefighter")
par(mfrow = c(2, 2))
hist(firefighter_salaries$TotalPay, 
     main = "Total Pay for Firefighters, default breaks", 
     xlab = "Pay (in dollars)", 
     col = "lightblue", 
     border = "black")
hist(firefighter_salaries$TotalPay, 
     main = "Total Pay for Firefighters, breaks=100", 
     xlab = "Pay (in dollars)", 
     breaks = 100, 
     col = "lightblue", 
     border = "black")
hist(firefighter_salaries$TotalPay, 
     main = "Total Pay for Firefighters, breaks=1000", 
     xlab = "Pay (in dollars)", 
     breaks = 1000, 
     col = "lightblue", 
     border = "black")

3.2 Wykres pudełkowy

firefighter_salaries <- subset(salaries, JobTitle == "Firefighter")

boxplot(firefighter_salaries$TotalPay, 
        main = "Total Pay for Firefighters", 
        ylab = "Pay (in dollars)", 
        col = "lightblue", 
        border = "black")

abline(h = median(firefighter_salaries$TotalPay, na.rm = TRUE), col = "blue", lty = "dashed")
abline(h = mean(firefighter_salaries$TotalPay, na.rm = TRUE), col = "red", lty = "dotted")
legend("topright", 
       legend = c("Mediana", "Średnia"), 
       col = c("blue", "red"), 
       lty = c("dashed", "dotted"))

3.3 Wykres ggplot

firefighter_salaries <- subset(salaries, JobTitle == "Firefighter")

ggplot(firefighter_salaries, aes(x = TotalPay)) +
  geom_density(fill = "skyblue", alpha = 0.5, color = "darkblue") +
  geom_vline(xintercept = median(firefighter_salaries$TotalPay, na.rm = TRUE), color = "blue", linetype = "dashed", linewidth = 1) +
  geom_vline(xintercept = mean(firefighter_salaries$TotalPay, na.rm = TRUE), color = "red", linetype = "dotted", linewidth = 1) +
  labs(title = "Distribution of Total Pay for Firefighters",
       x = "Pay (in dollars)", y = "Density") +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
    axis.title = element_text(size = 12)
  ) 

Rozkład wynagrodzeń strażaków jest względnie symetryczny, z głównym skupiskiem w okolicach 150,000 dolarów. Dodatkowy szczyt przy niższych wartościach może wynikać z różnych poziomów doświadczenia lub stanowisk w grupie strażaków.

3.4 Estymacja nieliniowej zależności

Poniżej wykorzystano metodę locpoly() z pakietu KernSmooth do estymacji nieliniowej zależności.

firefighter_salaries <- subset(salaries, JobTitle == "Firefighter")

fit <- locpoly(x = firefighter_salaries$TotalPay, 
               degree = 0, 
               bandwidth = 10000) %>% as_tibble()

ggplot(firefighter_salaries, aes(x = TotalPay)) +
  geom_histogram(aes(y = after_stat(density)), bins = 30, fill = "lightblue", color = "black", alpha = 0.5) +
  geom_line(data = fit, aes(x = x, y = y), color = "purple", linewidth = 1) +
  labs(
    title = "Kernel Density Estimation of Total Pay for Firefighters",
    x = "Total Pay (in dollars)", 
    y = "Density"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
    axis.title = element_text(size = 12)
  )

3.5 Modyfikacje - estymacja nieliniowej zależności

firefighter_salaries <- subset(salaries, JobTitle == "Firefighter")

fit_1 <- locpoly(firefighter_salaries$TotalPay, bandwidth = 5000, degree = 0) %>% as_tibble()
fit_2 <- locpoly(firefighter_salaries$TotalPay, bandwidth = 10000, degree = 1) %>% as_tibble()
fit_3 <- locpoly(firefighter_salaries$TotalPay, bandwidth = 20000, degree = 2) %>% as_tibble()

ggplot(firefighter_salaries, aes(x = TotalPay)) +
  geom_histogram(aes(y = after_stat(density)), bins = 30, fill = "lightblue", color = "black", alpha = 0.5) +
  geom_line(data = fit_1, aes(x = x, y = y), color = "purple", linewidth = 1, linetype = "solid", show.legend = TRUE) +
  geom_line(data = fit_2, aes(x = x, y = y), color = "red", linewidth = 1, linetype = "dashed", show.legend = TRUE) +
  geom_line(data = fit_3, aes(x = x, y = y), color = "blue", linewidth = 1, linetype = "dotdash", show.legend = TRUE) +
  labs(
    title = "Comparison of Kernel Density Estimation",
    x = "Total Pay (in dollars)", 
    y = "Density"
  ) +
  scale_color_manual(
    name = "Bandwidth and Degree",
    values = c("purple", "red", "blue"),
    labels = c("Bandwidth=5000, Degree=0", "Bandwidth=10000, Degree=1", "Bandwidth=20000, Degree=2")
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
    axis.title = element_text(size = 12)
  )

4 Wprowadzenie - raport 2

Cel: wyznaczenie obszaru ufności dla dystrybuanty nieznanego rozkładu, a nie tylko oszacowania parametrów, od jakich zależą jej wartości.

  • Brzegi tego obszaru są wykresami funkcji „przedziałami stałych” (funkcji schodkowych).

  • Jeżeli przy wyznaczaniu pasma ufności dla dystrybuanty otrzymamy lewy kraniec przedziału będący liczbą ujemną, to zastępujemy ją przez zero.

  • Jeżeli otrzymamy prawy kraniec przedziału większy od jedności, to przyjmujemy, że jest on równy jeden.

  • Określenie obszaru ufności dla dystrybuanty w przedstawiony sposób polega na wyznaczeniu przedziałowego oszacowania dla każdej wartości dystrybuanty.

4.1 Funkcja CDF

Funkcja w programie R odpowiedzialna za estymację to np. CDF z pakietu spatstat. CDF jest metodą ogólną, z metodą dla klasy “gęstość”.

Oblicza ona skumulowaną funkcję rozkładu, której gęstość prawdopodobieństwa została oszacowana i zapisana w obiekcie f. Obiekt f musi należeć do klasy “gęstość” i zazwyczaj zostałby uzyskany z wywołania funkcji gęstość.

4.2 Funkcja kde

Pakiet R o nazwie snpar zawiera kilka uzupełniających metod statystyki nieparametrycznej, w tym test kwantylowy, test trendu Coxa-Stuarta, test przebiegów, test normalnego wyniku, estymację jądra PDF i CDF, estymację regresji jądra i test jądra Kołmogorowa-Smirnowa.

Funkcja kde zawiera obliczanie zarówno nieparametrycznego estymatora jądra funkcji gęstości prawdopodobieństwa (PDF) jak i funkcji rozkładu skumulowanego (CDF).

4.3 Zadanie 1

Posłużymy się zbiorem danych diagnozy społecznej.

Na jego podstawie Twoim zadaniem jest oszacowanie rozkładu “p64 Pana/Pani wlasny (osobisty) dochod miesieczny netto (na reke)” według województw/płci. Postaraj się oszacować zarówno rozkład gęstości jak i skumulowanej gęstości (dystrybuanty).

data("diagnoza")
data("diagnozaDict")

dane <- data.frame(dochod=diagnoza$gp64, 
                   plec=diagnoza$plec, 
                   woj=diagnoza$wojewodztwo)

dane <- dane %>% 
  drop_na(dochod, woj, plec)

dane$dochod <- as.numeric(dane$dochod)

Histogram rozkładu dochodu według płci

ggplot(dane, aes(x = dochod, fill = plec)) +
  geom_histogram(position = "identity", alpha = 0.5, bins = 30) + 
  labs(
    title = "Rozkład dochodów miesięcznych netto według płci",
    x = "Dochód netto (PLN)",
    y = "Liczba osób"
  ) +
  facet_wrap(~ plec) + 
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 16, face = "bold"),
    axis.title.x = element_text(size = 12),
    axis.title.y = element_text(size = 12),
    legend.position = "none" 
  )

4.3.1 Rozkład gęstości

4.3.1.1 **Typ jądra

Poniżej wykonano estymację jądrową gęstości (Kernel Density Estimation - KDE) dla zmiennej dochod z wykorzystaniem różnych funkcji jądrowych (kernels) oraz tej samej szerokości pasma (bandwidth = 3). Wykorzystano trzy różne typy jąder: Gaussa (domyślne), Epanechnikova oraz prostokątne (rectangular). Wybór jądra wpływa na gładkość i dokładność estymacji. Jądro Gaussa daje najbardziej gładką estymację, podczas gdy jądro prostokątne jest bardziej sztywne i mniej wygładza dane.

d_gaussian <- density(dane$dochod, kernel = "gaussian", bw = 3)
d_epanechnikov <- density(dane$dochod, kernel = "epanechnikov", bw = 3)
d_rectangular <- density(dane$dochod, kernel = "rectangular", bw = 3)
par(mfrow = c(3, 1))
plot(d_gaussian,main = "Default")
plot(d_epanechnikov, main = "Epanechnikov")
plot(d_rectangular, main = "Rectangular")

Z rozróżnieniem na płeć:

plot_g <- ggplot(dane, aes(x=dochod, fill=plec))+
  stat_density()+
  labs(title="Estymacja gęstości z jądrem Gaussa")+
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 16, face = "bold"),
    axis.title.x = element_text(size = 12),
    axis.title.y = element_text(size = 12),
    legend.position = "none" 
  )
plot_g

plot_e <- ggplot(dane, aes(x=dochod, fill=plec))+
  stat_density(kernel = "epanechnikov")+
  labs(title="Estymacja gęstości z jądrem Epanechnikova")+
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 16, face = "bold"),
    axis.title.x = element_text(size = 12),
    axis.title.y = element_text(size = 12),
    legend.position = "none" 
  )
plot_e

plot_t <- ggplot(dane, aes(x=dochod, fill=plec))+
  stat_density(kernel = "rectangular")+
  labs(title="Estymacja gęstości z jądrem prostokątnym")+
    theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 16, face = "bold"),
    axis.title.x = element_text(size = 12),
    axis.title.y = element_text(size = 12),
    legend.position = "none" 
  )
plot_t

Wniosek: Ponieważ różne typy jąder dają podobne wyniki, do dalszej analizy można użyć domyślnego jądra Gaussowskiego. Wybór ten upraszcza obliczenia, jednocześnie zapewniając stabilne i dokładne estymacje rozkładów dochodów według województw i płci.

par(mfrow = c(2, 2))
d_0 <- density(dane$dochod, bw = "nrd0")
plot(d_0, main = "Pasmo nrd0", col="red")

d_scott <- density(dane$dochod, bw = "nrd")
plot(d_scott, main = "Pasmo Scotta", col='yellow')

d_ucv <- density(dane$dochod, bw = "ucv")
## Warning in bw.ucv(x): minimum occurred at one end of the range
plot(d_ucv, main = "Pasmo UCV", col="green")

d_SJ <- density(dane$dochod, bw = "SJ")
plot(d_SJ, main = "Pasmo Sheathera & Jonesa", col="blue")

Wnioski:

Pasmo UCV i Pasmo Sheathera & Jonesa (SJ) ujawniają najwięcej szczegółów i lokalnych wariacji w rozkładzie dochodów, ale mogą wprowadzać „szum”, który utrudnia ogólną interpretację danych. Są one odpowiednie, jeśli potrzebujemy szczegółowej analizy lokalnych zmian w rozkładzie.

Pasmo nrd0 i Pasmo Scotta zapewniają gładszy obraz rozkładu, który jest bardziej odporny na szum i fluktuacje lokalne, co jest korzystne, gdy interesuje nas bardziej ogólny kształt rozkładu. Pasmo Scotta daje najbardziej wygładzony wynik, idealny dla dużych zestawów danych.

ggplot(dane, aes(x=dochod, fill=plec))+
  stat_density(bw = "nrd")+
  labs(title="Estymacja gęstości z pasmem Scotta z podziałem na płeć")

4.3.2 Oszacowanie skumulowanej gęstości

Dystrybuanta to funkcja, która opisuje prawdopodobieństwo, że zmienna losowa przyjmie wartość mniejszą lub równą danej wartości x.

4.3.2.1 Typ jądra

dystrybuanta_g <- approxfun(d_gaussian$x,
                          cumsum(d_gaussian$y)/sum(d_gaussian$y))

dystrybuanta_e <- approxfun(d_epanechnikov$x,
                          cumsum(d_epanechnikov$y)/sum(d_epanechnikov$y))

dystrybuanta_r <- approxfun(d_rectangular$x,
                          cumsum(d_rectangular$y)/sum(d_rectangular$y))

x_vals <- seq(min(dane$dochod), max(dane$dochod), length.out = 100)
par(mfrow = c(3, 1))
plot(x_vals, dystrybuanta_g(x_vals), pch=1,
     main = "CDF dla jądra Gaussowskiego", 
     xlab = "Dochód", ylab = "Dystrybuanta")
plot(x_vals, dystrybuanta_e(x_vals), pch=1,
     main = "CDF dla jądra Epanechnikova", 
     xlab = "Dochód", ylab = "Dystrybuanta")
plot(x_vals, dystrybuanta_r(x_vals), pch=1,
     main = "CDF dla jądra prostokątnego", 
     xlab = "Dochód", ylab = "Dystrybuanta")

Wszystkie trzy dystrybuanty (dla różnych jąder) mają bardzo podobny kształt. Każdy z wykresów pokazuje, że większość wartości dochodów znajduje się w dolnym zakresie, co powoduje szybki wzrost dystrybuanty na początku, a następnie wypłaszczenie. Oznacza to, że dla większości populacji dochody są stosunkowo niskie.

4.3.2.2 Selekcja pasma

dystrybuanta_0 <- approxfun(d_0$x,
                          cumsum(d_0$y)/sum(d_0$y))

dystrybuanta_s <- approxfun(d_scott$x,
                          cumsum(d_scott$y)/sum(d_scott$y))

dystrybuanta_ucv <- approxfun(d_ucv$x,
                          cumsum(d_ucv$y)/sum(d_ucv$y))

dystrybuanta_SJ <- approxfun(d_SJ$x,
                          cumsum(d_SJ$y)/sum(d_SJ$y))

x_vals <- seq(min(dane$dochod), max(dane$dochod), length.out = 100)
plot(x_vals, dystrybuanta_0(x_vals), pch=1, 
     main = "CDF dla pasma nrd0", 
     xlab = "Dochód", ylab = "Dystrybuanta", 
     col = "red", lwd = 2)

Szerokość pasma ma ograniczony wpływ na wyniki estymacji dystrybuanty, co obserwujemy na wykresach, gdzie krzywe CDF dla różnych pasm i jąder są do siebie zbliżone. Zarówno estymacja gęstości, jak i skumulowanej gęstości są wartościowymi narzędziami w analizie rozkładu danych, pozwalając na dokładniejsze zrozumienie struktury danych. Różne jądra i metody wyboru pasma mogą jednak przynieść subtelne różnice w wynikach, co podkreśla znaczenie przemyślanego doboru tych parametrów, szczególnie w analizach statystycznych, gdzie precyzja ma kluczowe znaczenie.

LS0tDQp0aXRsZTogIk5pZWtsYXN5Y3puZSBtZXRvZHkgc3RhdHlzdHlraSINCnN1YnRpdGxlOiAiTmllcGFyYW1ldHJ5Y3puYSBlc3R5bWFjamEgZ8SZc3RvxZtjaS9keXN0cnlidWFudHkgLSBSYXBvcnQgMSArIDIiDQphdXRob3I6ICJNYcWCZ29yemF0YSBEdWRhbm93aWN6LCBFd2EgQnJlemEiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdGhlbWU6IGNlcnVsZWFuDQogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQ0KICAgIGZvbnRzaXplOiA4cHQNCiAgICB0b2M6IHRydWUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmxpYnJhcnkoZHBseXIpDQojaWYgKCFyZXF1aXJlKCJkZXZ0b29scyIpKQ0KI2luc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikNCiNkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImRlYmlucWl1L3NucGFyIikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KFBvZ3JvbWN5RGFueWNoKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShLZXJuU21vb3RoKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmBgYA0KDQojIEtyw7N0a2llIHdwcm93YWR6ZW5pZSB0ZW9yZXR5Y3puZQ0KDQpLZXJuZWwgRGVuc2l0eSBFc3RpbWF0aW9uIChLREUpLCBjenlsaSBlc3R5bWFjamEgasSFZHJvd2EgZ8SZc3RvxZtjaSwgamVzdA0KbmllcGFyYW1ldHJ5Y3puxIUgbWV0b2TEhSBvc3phY293YW5pYSBmdW5rY2ppIGfEmXN0b8WbY2kgcm96a8WCYWR1IHptaWVubmVqDQpsb3Nvd2VqIG5hIHBvZHN0YXdpZSBkb3N0xJlwbnljaCBkYW55Y2guIFcgcHJ6ZWNpd2llxYRzdHdpZSBkbyBtZXRvZA0KcGFyYW1ldHJ5Y3pueWNoLCB0YWtpY2ggamFrIGVzdHltYWNqYSByb3prxYJhZHUgbm9ybWFsbmVnbywgS0RFIG5pZQ0Kd3ltYWdhIHByenlqxJljaWEga29ua3JldG5laiBmb3JteSByb3prxYJhZHUgZGFueWNoLiBaYW1pYXN0IHRlZ28sIEtERQ0KYnVkdWplIGVzdHltYWNqxJkgZnVua2NqaSBnxJlzdG/Fm2NpIHcgc3Bvc8OzYiBnxYJhZGtpLCBvcGllcmFqxIVjIHNpxJkgbmENCnJ6ZWN6eXdpc3R5Y2ggb2JzZXJ3YWNqYWNoIHogcHLDs2J5LiBEbGEga2HFvGRlZ28gcHVua3R1IGRhbnljaA0KdW1pZXN6Y3phbmEgamVzdCBmdW5rY2phIGrEhWRyb3dhIChrZXJuZWwpLCBuYWpjesSZxZtjaWVqIG8ga3N6dGHFgmNpZQ0KemJsacW8b255bSBkbyBrcnp5d2VqIGR6d29ub3dlaiwgbnAuIHJvemvFgmFkdSBHYXVzc2EsIGt0w7NyZWogemFkYW5pZW0NCmplc3QgcHJ6eXBpc2FuaWUgIndhZ2kiIG9ic2Vyd2Fjam9tIHcgcG9ibGnFvHUgZGFuZWdvIHB1bmt0dS4gU3plcm9rb8WbxIcNCnBhc21hIChiYW5kd2lkdGgpIGtvbnRyb2x1amUgc3RvcGllxYQgd3lnxYJhZHplbmlhIGNhxYJrb3dpdGVqIGZ1bmtjamkNCmfEmXN0b8WbY2kuDQoNCiMgxIZ3aWN6ZW5pZSAxLg0KDQpVcnVjaG9tIGRlbW8gZXN0eW1hdG9yYSBmdW5rY2ppIGfEmXN0b8WbY2kga2VybmVsLiBabWllbmlhaiB6YXLDs3dubyBkYW5lDQp3ZWrFm2Npb3dlLCBqYWsgaSBvcGNqZSBlc3R5bWF0b3JhIC0gc3plcm9rb8WbxIcgcGFzbWEgb3JheiByb2R6YWogZnVua2NqaQ0KasSFZHJvd2VqLiBDenkgd2lkemlzeiBpc3RvdG5lIHLDs8W8bmljZSB3IG9zemFjb3dhbml1Pw0KDQpgYGB7ciBjd2ljemVuaWUxfQ0KI2luc3RhbGwucGFja2FnZXMoInJlbW90ZXMiKSAjdHlsa28gcmF6ISBwb3RlbSAjDQojcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoImhlcmlja3MvS0RFIikgI3R5bGtvIHJheiEgcG90ZW0gIw0KI2xpYnJhcnkoS0RFKQ0KI3NoaW55X2tkZSgpIA0KYGBgDQoNCktpbGthIHduaW9za8OzdzoNCg0KLU1hxYJlIHBhc21vICh3xIVza2Egc3plcm9rb8WbxIcpOiBHZHkgc3plcm9rb8WbxIcgcGFzbWEgamVzdCBiYXJkem8gbWHFgmEsDQplc3R5bWFjamEgZ8SZc3RvxZtjaSBzdGFqZSBzacSZIGJhcmR6byBzemN6ZWfDs8WCb3dhIGkgZG9rxYJhZG5pZSBkb3Bhc293dWplDQpzacSZIGRvIHBvc3pjemVnw7NsbnljaCBvYnNlcndhY2ppLCBjbyBwb3dvZHVqZSwgxbxlIGVzdHltYXRvciBqZXN0IHBvZGF0bnkNCm5hIGxva2FsbmUgd2FoYW5pYSBpICJzenVteSIgdyBkYW55Y2guDQoNCi1EdcW8ZSBwYXNtbyAoc3plcm9rYSBzemVyb2tvxZvEhyk6IFByenkgZHXFvHltIHBhxZttaWUgZXN0eW1hdG9yIGfEmXN0b8WbY2kNCmplc3Qgd3lnxYJhZHpvbnksIGNvIHNwcmF3aWEsIMW8ZSBsb2thbG5lIHN6Y3plZ8OzxYJ5IHPEhSBtbmllaiB3aWRvY3puZSwgYWxlDQp6YSB0byBsZXBpZWogb2RkYWplIG9nw7Nsbnkga3N6dGHFgnQgcm96a8WCYWR1IGRhbnljaC4gUHJ6eSB6Ynl0IGR1xbx5bQ0KcGHFm21pZSBtb8W8ZSBqZWRuYWsgZG9qxZvEhyBkbyBuYWRtaWVybmVnbyB3eWfFgmFkemVuaWEgKHVuZGVyZml0dGluZyksDQpnZHppZSBzemN6ZWfDs8WCeSByb3prxYJhZHUgc8SFIHp1cGXFgm5pZSB6Z3ViaW9uZSwgYSBlc3R5bWF0b3IgcHJ6ZXN0YWplDQpvZHp3aWVyY2llZGxhxIcgcmVhbG5lIHptaWFueSB3IGRhbnljaC4NCg0KLVByenkgbW5pZWpzemVqIGxpY3piaWUgZGFueWNoIGVzdHltYXRvciBLREUgamVzdCBiYXJkemllaiB3cmHFvGxpd3kgbmENCmRvYsOzciBzemVyb2tvxZtjaSBwYXNtYSBpIHJvZHphanUgasSFZHJhLiBQcnp5IG1hxYJ5Y2ggcHLDs2JhY2gga2HFvGRlIGrEhWRybw0KbWEgZHXFvHkgd3DFgnl3IG5hIHd5bmlrLCBhIG1hxYJlIHBhc21vIGdlbmVydWplIHd5cmHFum55IHN6dW0uDQoNCi1Qcnp5IHdpxJlrc3plaiBsaWN6YmllIG9ic2Vyd2FjamkgS0RFIGR6aWHFgmEgYmFyZHppZWogc3RhYmlsbmllLiBXacSZa3N6ZQ0KcHLDs2J5IGxlcGllaiBvZHp3aWVyY2llZGxhasSFIHJ6ZWN6eXdpc3R5IHJvemvFgmFkIGRhbnljaCwgYSBlc3R5bWFjamENCnN0YWplIHNpxJkgYmFyZHppZWogcHJlY3l6eWpuYS4gRG9iw7NyIHN6ZXJva2/Fm2NpIHBhc21hIGkgasSFZHJhIG1hDQptbmllanN6eSB3cMWCeXcgbmEgd3luaWsgcHJ6eSBkdcW8ZWogbGljemJpZSBkYW55Y2guDQoNCi1Vxbx5Y2llIHdhbGlkYWNqaSBrcnp5xbxvd2VqIChjcm9zcy12YWxpZGF0aW9uKSBkbyBkb2JvcnUgc3plcm9rb8WbY2kNCnBhc21hIG9rYXphxYJvIHNpxJkgc2t1dGVjem5lIHcgdXp5c2thbml1IGRvYnJ6ZSB6YmFsYW5zb3dhbmVnbw0KZXN0eW1hdG9yYSwga3TDs3J5IHphY2hvd3VqZSBrc3p0YcWCdCByb3prxYJhZHUgYmV6IG5hZG1pZXJuZWdvDQp3eWfFgmFkemFuaWEuDQoNCi1KxIVkcm8gR2F1c3Nvd3NraWUgZGFqZSBjaHliYSBuYWpiYXJkemllaiBpbnR1aWN5am5lIGkgZ8WCYWRraWUNCmVzdHltYWNqZS4NCg0KIyDEhndpY3plbmllIDIuDQoNCld5a29yenlzdHVqxIVjIGRvd29sbsSFIGZ1bmtjasSZIFIgZG8gZXN0eW1hY2ppIGZ1bmtjamkgZ8SZc3RvxZtjaSBvc3phY3VqDQpqZWogcHJ6ZWJpZWcgZGxhIHd5bmFncm9kemXFhCAoemJpw7NyIGRhbnljaCBzYWxhcmllcykgc3RyYcW8YWvDs3cgdyBTYW4NCkZyYW5jaXNjby4gV3lrb3J6eXN0YWogbWV0b2R5IGdyYWZpY3puZSBkb3N0xJlwbmUgdyBwYWtpZWNpZSBnZ3Bsb3QyLg0KTWlsZSB3aWR6aWFuZSBwcnpla3JvamUgb3JheiBvZHBvd2llZHppIG5hIHB5dGFuaWEgYmFkYXdjemUgemFkYW5lIG5hDQp3c3TEmXBpZS4NCg0KYGBge3J9DQpzYWxhcmllcyA8LSByZWFkLmNzdigiaHR0cHM6Ly9naXRodWIuY29tL2tmbGlzaWtvd3NraS9kcy9yYXcvbWFzdGVyL1NhbGFyaWVzLmNzdiIpDQpgYGANCg0KIyMgSGlzdG9ncmFtDQoNCmBgYHtyfQ0KZmlyZWZpZ2h0ZXJfc2FsYXJpZXMgPC0gc3Vic2V0KHNhbGFyaWVzLCBKb2JUaXRsZSA9PSAiRmlyZWZpZ2h0ZXIiKQ0KDQpoaXN0KGZpcmVmaWdodGVyX3NhbGFyaWVzJFRvdGFsUGF5LCANCiAgICAgbWFpbiA9ICJUb3RhbCBQYXkgZm9yIEZpcmVmaWdodGVycyIsIA0KICAgICB4bGFiID0gIlBheSAoaW4gZG9sbGFycykiLCANCiAgICAgY29sID0gImxpZ2h0Ymx1ZSIsIA0KICAgICBib3JkZXIgPSAiYmxhY2siKQ0KDQphYmxpbmUodiA9IG1lYW4oZmlyZWZpZ2h0ZXJfc2FsYXJpZXMkVG90YWxQYXksIG5hLnJtID0gVFJVRSksIGNvbCA9ICJyZWQiLCBsdHkgPSAiZGFzaGVkIikNCmFibGluZSh2ID0gbWVkaWFuKGZpcmVmaWdodGVyX3NhbGFyaWVzJFRvdGFsUGF5LCBuYS5ybSA9IFRSVUUpLCBjb2wgPSAiYmx1ZSIsIGx0eSA9ICJzb2xpZCIpDQpsZWdlbmQoInRvcHJpZ2h0IiwgDQogICAgICAgbGVnZW5kID0gYygiTWVkaWFuYSIsICLFmnJlZG5pYSIpLCANCiAgICAgICBjb2wgPSBjKCJibHVlIiwgInJlZCIpLCANCiAgICAgICBsdHkgPSBjKCJzb2xpZCIsICJkYXNoZWQiKSkNCmBgYA0KDQpXacSZa3N6b8WbxIcgc3RyYcW8YWvDs3cgemFyYWJpYSB3IHByemVkemlhbGUgb2tvxYJvIDEwMCwwMDAgLSAxNzUsMDAwIGRvbGFyw7N3DQpyb2N6bmllLCBjbyBzdGFub3dpIG5handpxJlrc3plIHNrdXBpc2tvIHdhcnRvxZtjaSBuYSB3eWtyZXNpZS4gUm96a8WCYWQNCnRlbiBqZXN0IHd6Z2zEmWRuaWUgc3ltZXRyeWN6bnksIGNvIHN1Z2VydWplLCDFvGUgd2FydG/Fm2NpIHd5bmFncm9kemXFhCBzxIUNCnLDs3dub21pZXJuaWUgcm96xYJvxbxvbmUgd29rw7PFgiBjZW50cmFsbmVqIHdhcnRvxZtjaS4gWiB0ZWdvIHd5a3Jlc3Ugd3luaWthLA0KxbxlIG1lZGlhbmEgaSDFm3JlZG5pYSBzxIUgYmFyZHpvIHpibGnFvG9uZSBkbyBzaWViaWUsIGNvIHN1Z2VydWplLCDFvGUgdw0Kcm96a8WCYWR6aWUgbmllIHd5c3TEmXB1asSFIGR1xbxlIHdhcnRvxZtjaSBvZHN0YWrEhWNlLCBrdMOzcmUgbW9nxYJ5Ynkgem5hY3rEhWNvDQp6bmlla3N6dGHFgmNpxIcgxZtyZWRuacSFLg0KDQpgYGB7cn0NCmZpcmVmaWdodGVyX3NhbGFyaWVzIDwtIHN1YnNldChzYWxhcmllcywgSm9iVGl0bGUgPT0gIkZpcmVmaWdodGVyIikNCnBhcihtZnJvdyA9IGMoMiwgMikpDQpoaXN0KGZpcmVmaWdodGVyX3NhbGFyaWVzJFRvdGFsUGF5LCANCiAgICAgbWFpbiA9ICJUb3RhbCBQYXkgZm9yIEZpcmVmaWdodGVycywgZGVmYXVsdCBicmVha3MiLCANCiAgICAgeGxhYiA9ICJQYXkgKGluIGRvbGxhcnMpIiwgDQogICAgIGNvbCA9ICJsaWdodGJsdWUiLCANCiAgICAgYm9yZGVyID0gImJsYWNrIikNCmhpc3QoZmlyZWZpZ2h0ZXJfc2FsYXJpZXMkVG90YWxQYXksIA0KICAgICBtYWluID0gIlRvdGFsIFBheSBmb3IgRmlyZWZpZ2h0ZXJzLCBicmVha3M9MTAwIiwgDQogICAgIHhsYWIgPSAiUGF5IChpbiBkb2xsYXJzKSIsIA0KICAgICBicmVha3MgPSAxMDAsIA0KICAgICBjb2wgPSAibGlnaHRibHVlIiwgDQogICAgIGJvcmRlciA9ICJibGFjayIpDQpoaXN0KGZpcmVmaWdodGVyX3NhbGFyaWVzJFRvdGFsUGF5LCANCiAgICAgbWFpbiA9ICJUb3RhbCBQYXkgZm9yIEZpcmVmaWdodGVycywgYnJlYWtzPTEwMDAiLCANCiAgICAgeGxhYiA9ICJQYXkgKGluIGRvbGxhcnMpIiwgDQogICAgIGJyZWFrcyA9IDEwMDAsIA0KICAgICBjb2wgPSAibGlnaHRibHVlIiwgDQogICAgIGJvcmRlciA9ICJibGFjayIpDQpgYGANCg0KIyMgV3lrcmVzIHB1ZGXFgmtvd3kNCg0KYGBge3J9DQpmaXJlZmlnaHRlcl9zYWxhcmllcyA8LSBzdWJzZXQoc2FsYXJpZXMsIEpvYlRpdGxlID09ICJGaXJlZmlnaHRlciIpDQoNCmJveHBsb3QoZmlyZWZpZ2h0ZXJfc2FsYXJpZXMkVG90YWxQYXksIA0KICAgICAgICBtYWluID0gIlRvdGFsIFBheSBmb3IgRmlyZWZpZ2h0ZXJzIiwgDQogICAgICAgIHlsYWIgPSAiUGF5IChpbiBkb2xsYXJzKSIsIA0KICAgICAgICBjb2wgPSAibGlnaHRibHVlIiwgDQogICAgICAgIGJvcmRlciA9ICJibGFjayIpDQoNCmFibGluZShoID0gbWVkaWFuKGZpcmVmaWdodGVyX3NhbGFyaWVzJFRvdGFsUGF5LCBuYS5ybSA9IFRSVUUpLCBjb2wgPSAiYmx1ZSIsIGx0eSA9ICJkYXNoZWQiKQ0KYWJsaW5lKGggPSBtZWFuKGZpcmVmaWdodGVyX3NhbGFyaWVzJFRvdGFsUGF5LCBuYS5ybSA9IFRSVUUpLCBjb2wgPSAicmVkIiwgbHR5ID0gImRvdHRlZCIpDQpsZWdlbmQoInRvcHJpZ2h0IiwgDQogICAgICAgbGVnZW5kID0gYygiTWVkaWFuYSIsICLFmnJlZG5pYSIpLCANCiAgICAgICBjb2wgPSBjKCJibHVlIiwgInJlZCIpLCANCiAgICAgICBsdHkgPSBjKCJkYXNoZWQiLCAiZG90dGVkIikpDQpgYGANCg0KIyMgV3lrcmVzIGdncGxvdA0KDQpgYGB7cn0NCg0KZmlyZWZpZ2h0ZXJfc2FsYXJpZXMgPC0gc3Vic2V0KHNhbGFyaWVzLCBKb2JUaXRsZSA9PSAiRmlyZWZpZ2h0ZXIiKQ0KDQpnZ3Bsb3QoZmlyZWZpZ2h0ZXJfc2FsYXJpZXMsIGFlcyh4ID0gVG90YWxQYXkpKSArDQogIGdlb21fZGVuc2l0eShmaWxsID0gInNreWJsdWUiLCBhbHBoYSA9IDAuNSwgY29sb3IgPSAiZGFya2JsdWUiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lZGlhbihmaXJlZmlnaHRlcl9zYWxhcmllcyRUb3RhbFBheSwgbmEucm0gPSBUUlVFKSwgY29sb3IgPSAiYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIGxpbmV3aWR0aCA9IDEpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWVhbihmaXJlZmlnaHRlcl9zYWxhcmllcyRUb3RhbFBheSwgbmEucm0gPSBUUlVFKSwgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZG90dGVkIiwgbGluZXdpZHRoID0gMSkgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBUb3RhbCBQYXkgZm9yIEZpcmVmaWdodGVycyIsDQogICAgICAgeCA9ICJQYXkgKGluIGRvbGxhcnMpIiwgeSA9ICJEZW5zaXR5IikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKQ0KICApIA0KYGBgDQoNClJvemvFgmFkIHd5bmFncm9kemXFhCBzdHJhxbxha8OzdyBqZXN0IHd6Z2zEmWRuaWUgc3ltZXRyeWN6bnksIHogZ8WCw7N3bnltDQpza3VwaXNraWVtIHcgb2tvbGljYWNoIDE1MCwwMDAgZG9sYXLDs3cuIERvZGF0a293eSBzemN6eXQgcHJ6eSBuacW8c3p5Y2gNCndhcnRvxZtjaWFjaCBtb8W8ZSB3eW5pa2HEhyB6IHLDs8W8bnljaCBwb3ppb23Ds3cgZG/Fm3dpYWRjemVuaWEgbHViIHN0YW5vd2lzaw0KdyBncnVwaWUgc3RyYcW8YWvDs3cuDQoNCiMjIEVzdHltYWNqYSBuaWVsaW5pb3dlaiB6YWxlxbxub8WbY2kNCg0KUG9uacW8ZWogd3lrb3J6eXN0YW5vIG1ldG9kxJkgbG9jcG9seSgpIHogcGFraWV0dSBLZXJuU21vb3RoIGRvIGVzdHltYWNqaQ0KbmllbGluaW93ZWogemFsZcW8bm/Fm2NpLg0KDQpgYGB7cn0NCg0KZmlyZWZpZ2h0ZXJfc2FsYXJpZXMgPC0gc3Vic2V0KHNhbGFyaWVzLCBKb2JUaXRsZSA9PSAiRmlyZWZpZ2h0ZXIiKQ0KDQpmaXQgPC0gbG9jcG9seSh4ID0gZmlyZWZpZ2h0ZXJfc2FsYXJpZXMkVG90YWxQYXksIA0KICAgICAgICAgICAgICAgZGVncmVlID0gMCwgDQogICAgICAgICAgICAgICBiYW5kd2lkdGggPSAxMDAwMCkgJT4lIGFzX3RpYmJsZSgpDQoNCmdncGxvdChmaXJlZmlnaHRlcl9zYWxhcmllcywgYWVzKHggPSBUb3RhbFBheSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSBhZnRlcl9zdGF0KGRlbnNpdHkpKSwgYmlucyA9IDMwLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjUpICsNCiAgZ2VvbV9saW5lKGRhdGEgPSBmaXQsIGFlcyh4ID0geCwgeSA9IHkpLCBjb2xvciA9ICJwdXJwbGUiLCBsaW5ld2lkdGggPSAxKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiS2VybmVsIERlbnNpdHkgRXN0aW1hdGlvbiBvZiBUb3RhbCBQYXkgZm9yIEZpcmVmaWdodGVycyIsDQogICAgeCA9ICJUb3RhbCBQYXkgKGluIGRvbGxhcnMpIiwgDQogICAgeSA9ICJEZW5zaXR5Ig0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSwNCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikNCiAgKQ0KYGBgDQoNCiMjIE1vZHlmaWthY2plIC0gZXN0eW1hY2phIG5pZWxpbmlvd2VqIHphbGXFvG5vxZtjaQ0KDQpgYGB7cn0NCmZpcmVmaWdodGVyX3NhbGFyaWVzIDwtIHN1YnNldChzYWxhcmllcywgSm9iVGl0bGUgPT0gIkZpcmVmaWdodGVyIikNCg0KZml0XzEgPC0gbG9jcG9seShmaXJlZmlnaHRlcl9zYWxhcmllcyRUb3RhbFBheSwgYmFuZHdpZHRoID0gNTAwMCwgZGVncmVlID0gMCkgJT4lIGFzX3RpYmJsZSgpDQpmaXRfMiA8LSBsb2Nwb2x5KGZpcmVmaWdodGVyX3NhbGFyaWVzJFRvdGFsUGF5LCBiYW5kd2lkdGggPSAxMDAwMCwgZGVncmVlID0gMSkgJT4lIGFzX3RpYmJsZSgpDQpmaXRfMyA8LSBsb2Nwb2x5KGZpcmVmaWdodGVyX3NhbGFyaWVzJFRvdGFsUGF5LCBiYW5kd2lkdGggPSAyMDAwMCwgZGVncmVlID0gMikgJT4lIGFzX3RpYmJsZSgpDQoNCmdncGxvdChmaXJlZmlnaHRlcl9zYWxhcmllcywgYWVzKHggPSBUb3RhbFBheSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSBhZnRlcl9zdGF0KGRlbnNpdHkpKSwgYmlucyA9IDMwLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjUpICsNCiAgZ2VvbV9saW5lKGRhdGEgPSBmaXRfMSwgYWVzKHggPSB4LCB5ID0geSksIGNvbG9yID0gInB1cnBsZSIsIGxpbmV3aWR0aCA9IDEsIGxpbmV0eXBlID0gInNvbGlkIiwgc2hvdy5sZWdlbmQgPSBUUlVFKSArDQogIGdlb21fbGluZShkYXRhID0gZml0XzIsIGFlcyh4ID0geCwgeSA9IHkpLCBjb2xvciA9ICJyZWQiLCBsaW5ld2lkdGggPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaG93LmxlZ2VuZCA9IFRSVUUpICsNCiAgZ2VvbV9saW5lKGRhdGEgPSBmaXRfMywgYWVzKHggPSB4LCB5ID0geSksIGNvbG9yID0gImJsdWUiLCBsaW5ld2lkdGggPSAxLCBsaW5ldHlwZSA9ICJkb3RkYXNoIiwgc2hvdy5sZWdlbmQgPSBUUlVFKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiQ29tcGFyaXNvbiBvZiBLZXJuZWwgRGVuc2l0eSBFc3RpbWF0aW9uIiwNCiAgICB4ID0gIlRvdGFsIFBheSAoaW4gZG9sbGFycykiLCANCiAgICB5ID0gIkRlbnNpdHkiDQogICkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwoDQogICAgbmFtZSA9ICJCYW5kd2lkdGggYW5kIERlZ3JlZSIsDQogICAgdmFsdWVzID0gYygicHVycGxlIiwgInJlZCIsICJibHVlIiksDQogICAgbGFiZWxzID0gYygiQmFuZHdpZHRoPTUwMDAsIERlZ3JlZT0wIiwgIkJhbmR3aWR0aD0xMDAwMCwgRGVncmVlPTEiLCAiQmFuZHdpZHRoPTIwMDAwLCBEZWdyZWU9MiIpDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKQ0KICApDQpgYGANCg0KIyBXcHJvd2FkemVuaWUgLSByYXBvcnQgMg0KDQpDZWw6IHd5em5hY3plbmllIG9ic3phcnUgdWZub8WbY2kgZGxhIGR5c3RyeWJ1YW50eSBuaWV6bmFuZWdvIHJvemvFgmFkdSwgYQ0KbmllIHR5bGtvIG9zemFjb3dhbmlhIHBhcmFtZXRyw7N3LCBvZCBqYWtpY2ggemFsZcW8xIUgamVqIHdhcnRvxZtjaS4NCg0KLSAgIEJyemVnaSB0ZWdvIG9ic3phcnUgc8SFIHd5a3Jlc2FtaSBmdW5rY2ppIOKAnnByemVkemlhxYJhbWkgc3RhxYJ5Y2giDQogICAgKGZ1bmtjamkgc2Nob2Rrb3d5Y2gpLg0KDQotICAgSmXFvGVsaSBwcnp5IHd5em5hY3phbml1IHBhc21hIHVmbm/Fm2NpIGRsYSBkeXN0cnlidWFudHkgb3RyenltYW15DQogICAgbGV3eSBrcmFuaWVjIHByemVkemlhxYJ1IGLEmWTEhWN5IGxpY3pixIUgdWplbW7EhSwgdG8gemFzdMSZcHVqZW15IGrEhQ0KICAgIHByemV6IHplcm8uDQoNCi0gICBKZcW8ZWxpIG90cnp5bWFteSBwcmF3eSBrcmFuaWVjIHByemVkemlhxYJ1IHdpxJlrc3p5IG9kIGplZG5vxZtjaSwgdG8NCiAgICBwcnp5am11amVteSwgxbxlIGplc3Qgb24gcsOzd255IGplZGVuLg0KDQotICAgT2tyZcWbbGVuaWUgb2JzemFydSB1Zm5vxZtjaSBkbGEgZHlzdHJ5YnVhbnR5IHcgcHJ6ZWRzdGF3aW9ueSBzcG9zw7NiDQogICAgcG9sZWdhIG5hIHd5em5hY3plbml1IHByemVkemlhxYJvd2VnbyBvc3phY293YW5pYSBkbGEga2HFvGRlaiB3YXJ0b8WbY2kNCiAgICBkeXN0cnlidWFudHkuDQoNCiMjIEZ1bmtjamEgQ0RGDQoNCkZ1bmtjamEgdyBwcm9ncmFtaWUgUiBvZHBvd2llZHppYWxuYSB6YSBlc3R5bWFjasSZIHRvIG5wLiBDREYgeiBwYWtpZXR1DQpzcGF0c3RhdC4gQ0RGIGplc3QgbWV0b2TEhSBvZ8OzbG7EhSwgeiBtZXRvZMSFIGRsYSBrbGFzeSAiZ8SZc3RvxZvEhyIuDQoNCk9ibGljemEgb25hIHNrdW11bG93YW7EhSBmdW5rY2rEmSByb3prxYJhZHUsIGt0w7NyZWogZ8SZc3RvxZvEhw0KcHJhd2RvcG9kb2JpZcWEc3R3YSB6b3N0YcWCYSBvc3phY293YW5hIGkgemFwaXNhbmEgdyBvYmlla2NpZSBmLiBPYmlla3QgZg0KbXVzaSBuYWxlxbxlxIcgZG8ga2xhc3kgImfEmXN0b8WbxIciIGkgemF6d3ljemFqIHpvc3RhxYJieSB1enlza2FueSB6DQp3eXdvxYJhbmlhIGZ1bmtjamkgZ8SZc3RvxZvEhy4NCg0KIyMgRnVua2NqYSBrZGUNCg0KUGFraWV0IFIgbyBuYXp3aWUgc25wYXIgemF3aWVyYSBraWxrYSB1enVwZcWCbmlhasSFY3ljaCBtZXRvZCBzdGF0eXN0eWtpDQpuaWVwYXJhbWV0cnljem5laiwgdyB0eW0gdGVzdCBrd2FudHlsb3d5LCB0ZXN0IHRyZW5kdSBDb3hhLVN0dWFydGEsIHRlc3QNCnByemViaWVnw7N3LCB0ZXN0IG5vcm1hbG5lZ28gd3luaWt1LCBlc3R5bWFjasSZIGrEhWRyYSBQREYgaSBDREYsIGVzdHltYWNqxJkNCnJlZ3Jlc2ppIGrEhWRyYSBpIHRlc3QgasSFZHJhIEtvxYJtb2dvcm93YS1TbWlybm93YS4NCg0KRnVua2NqYSBrZGUgemF3aWVyYSBvYmxpY3phbmllIHphcsOzd25vIG5pZXBhcmFtZXRyeWN6bmVnbyBlc3R5bWF0b3JhDQpqxIVkcmEgZnVua2NqaSBnxJlzdG/Fm2NpIHByYXdkb3BvZG9iaWXFhHN0d2EgKFBERikgamFrIGkgZnVua2NqaSByb3prxYJhZHUNCnNrdW11bG93YW5lZ28gKENERikuDQoNCiMjIFphZGFuaWUgMQ0KDQpQb3PFgnXFvHlteSBzacSZIHpiaW9yZW0gZGFueWNoIGRpYWdub3p5IHNwb8WCZWN6bmVqLg0KDQpOYSBqZWdvIHBvZHN0YXdpZSBUd29pbSB6YWRhbmllbSBqZXN0IG9zemFjb3dhbmllIHJvemvFgmFkdSAicDY0DQpQYW5hL1Bhbmkgd2xhc255IChvc29iaXN0eSkgZG9jaG9kIG1pZXNpZWN6bnkgbmV0dG8gKG5hIHJla2UpIiB3ZWTFgnVnDQp3b2pld8OzZHp0dy9wxYJjaS4gUG9zdGFyYWogc2nEmSBvc3phY293YcSHIHphcsOzd25vIHJvemvFgmFkIGfEmXN0b8WbY2kgamFrIGkNCnNrdW11bG93YW5laiBnxJlzdG/Fm2NpIChkeXN0cnlidWFudHkpLg0KDQpgYGB7ciB6YWRhbmllfQ0KZGF0YSgiZGlhZ25vemEiKQ0KZGF0YSgiZGlhZ25vemFEaWN0IikNCg0KZGFuZSA8LSBkYXRhLmZyYW1lKGRvY2hvZD1kaWFnbm96YSRncDY0LCANCiAgICAgICAgICAgICAgICAgICBwbGVjPWRpYWdub3phJHBsZWMsIA0KICAgICAgICAgICAgICAgICAgIHdvaj1kaWFnbm96YSR3b2pld29kenR3bykNCg0KZGFuZSA8LSBkYW5lICU+JSANCiAgZHJvcF9uYShkb2Nob2QsIHdvaiwgcGxlYykNCg0KZGFuZSRkb2Nob2QgPC0gYXMubnVtZXJpYyhkYW5lJGRvY2hvZCkNCmBgYA0KDQoqKkhpc3RvZ3JhbSByb3prxYJhZHUgZG9jaG9kdSB3ZWTFgnVnIHDFgmNpKioNCg0KYGBge3J9DQpnZ3Bsb3QoZGFuZSwgYWVzKHggPSBkb2Nob2QsIGZpbGwgPSBwbGVjKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShwb3NpdGlvbiA9ICJpZGVudGl0eSIsIGFscGhhID0gMC41LCBiaW5zID0gMzApICsgDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUm96a8WCYWQgZG9jaG9kw7N3IG1pZXNpxJljem55Y2ggbmV0dG8gd2VkxYJ1ZyBwxYJjaSIsDQogICAgeCA9ICJEb2Now7NkIG5ldHRvIChQTE4pIiwNCiAgICB5ID0gIkxpY3piYSBvc8OzYiINCiAgKSArDQogIGZhY2V0X3dyYXAofiBwbGVjKSArIA0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLA0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLA0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiANCiAgKQ0KDQpgYGANCg0KIyMjIFJvemvFgmFkIGfEmXN0b8WbY2kNCg0KIyMjIyBcKlwqVHlwIGrEhWRyYQ0KDQpQb25pxbxlaiB3eWtvbmFubyBlc3R5bWFjasSZIGrEhWRyb3fEhSBnxJlzdG/Fm2NpIChLZXJuZWwgRGVuc2l0eSBFc3RpbWF0aW9uIC0NCktERSkgZGxhIHptaWVubmVqIGRvY2hvZCB6IHd5a29yenlzdGFuaWVtIHLDs8W8bnljaCBmdW5rY2ppIGrEhWRyb3d5Y2gNCihrZXJuZWxzKSBvcmF6IHRlaiBzYW1laiBzemVyb2tvxZtjaSBwYXNtYSAoYmFuZHdpZHRoID0gMykuIFd5a29yenlzdGFubw0KdHJ6eSByw7PFvG5lIHR5cHkgasSFZGVyOiBHYXVzc2EgKGRvbXnFm2xuZSksIEVwYW5lY2huaWtvdmEgb3JheiBwcm9zdG9rxIV0bmUNCihyZWN0YW5ndWxhcikuIFd5YsOzciBqxIVkcmEgd3DFgnl3YSBuYSBnxYJhZGtvxZvEhyBpIGRva8WCYWRub8WbxIcgZXN0eW1hY2ppLg0KSsSFZHJvIEdhdXNzYSBkYWplIG5hamJhcmR6aWVqIGfFgmFka8SFIGVzdHltYWNqxJksIHBvZGN6YXMgZ2R5IGrEhWRybw0KcHJvc3Rva8SFdG5lIGplc3QgYmFyZHppZWogc3p0eXduZSBpIG1uaWVqIHd5Z8WCYWR6YSBkYW5lLg0KDQpgYGB7cn0NCmRfZ2F1c3NpYW4gPC0gZGVuc2l0eShkYW5lJGRvY2hvZCwga2VybmVsID0gImdhdXNzaWFuIiwgYncgPSAzKQ0KZF9lcGFuZWNobmlrb3YgPC0gZGVuc2l0eShkYW5lJGRvY2hvZCwga2VybmVsID0gImVwYW5lY2huaWtvdiIsIGJ3ID0gMykNCmRfcmVjdGFuZ3VsYXIgPC0gZGVuc2l0eShkYW5lJGRvY2hvZCwga2VybmVsID0gInJlY3Rhbmd1bGFyIiwgYncgPSAzKQ0KYGBgDQoNCmBgYHtyfQ0KcGFyKG1mcm93ID0gYygzLCAxKSkNCnBsb3QoZF9nYXVzc2lhbixtYWluID0gIkRlZmF1bHQiKQ0KcGxvdChkX2VwYW5lY2huaWtvdiwgbWFpbiA9ICJFcGFuZWNobmlrb3YiKQ0KcGxvdChkX3JlY3Rhbmd1bGFyLCBtYWluID0gIlJlY3Rhbmd1bGFyIikNCg0KYGBgDQoNClogcm96csOzxbxuaWVuaWVtIG5hIHDFgmXEhzoNCg0KYGBge3J9DQpwbG90X2cgPC0gZ2dwbG90KGRhbmUsIGFlcyh4PWRvY2hvZCwgZmlsbD1wbGVjKSkrDQogIHN0YXRfZGVuc2l0eSgpKw0KICBsYWJzKHRpdGxlPSJFc3R5bWFjamEgZ8SZc3RvxZtjaSB6IGrEhWRyZW0gR2F1c3NhIikrDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksDQogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksDQogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksDQogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiIA0KICApDQpwbG90X2cNCmBgYA0KDQpgYGB7cn0NCnBsb3RfZSA8LSBnZ3Bsb3QoZGFuZSwgYWVzKHg9ZG9jaG9kLCBmaWxsPXBsZWMpKSsNCiAgc3RhdF9kZW5zaXR5KGtlcm5lbCA9ICJlcGFuZWNobmlrb3YiKSsNCiAgbGFicyh0aXRsZT0iRXN0eW1hY2phIGfEmXN0b8WbY2kgeiBqxIVkcmVtIEVwYW5lY2huaWtvdmEiKSsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwNCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIgDQogICkNCnBsb3RfZQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdF90IDwtIGdncGxvdChkYW5lLCBhZXMoeD1kb2Nob2QsIGZpbGw9cGxlYykpKw0KICBzdGF0X2RlbnNpdHkoa2VybmVsID0gInJlY3Rhbmd1bGFyIikrDQogIGxhYnModGl0bGU9IkVzdHltYWNqYSBnxJlzdG/Fm2NpIHogasSFZHJlbSBwcm9zdG9rxIV0bnltIikrDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwNCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIgDQogICkNCnBsb3RfdA0KYGBgDQoNCioqV25pb3NlayoqOiBQb25pZXdhxbwgcsOzxbxuZSB0eXB5IGrEhWRlciBkYWrEhSBwb2RvYm5lIHd5bmlraSwgZG8gZGFsc3plag0KYW5hbGl6eSBtb8W8bmEgdcW8ecSHIGRvbXnFm2xuZWdvIGrEhWRyYSBHYXVzc293c2tpZWdvLiBXeWLDs3IgdGVuIHVwcmFzemN6YQ0Kb2JsaWN6ZW5pYSwgamVkbm9jemXFm25pZSB6YXBld25pYWrEhWMgc3RhYmlsbmUgaSBkb2vFgmFkbmUgZXN0eW1hY2plDQpyb3prxYJhZMOzdyBkb2Nob2TDs3cgd2VkxYJ1ZyB3b2pld8OzZHp0dyBpIHDFgmNpLg0KDQpgYGB7cn0NCnBhcihtZnJvdyA9IGMoMiwgMikpDQpkXzAgPC0gZGVuc2l0eShkYW5lJGRvY2hvZCwgYncgPSAibnJkMCIpDQpwbG90KGRfMCwgbWFpbiA9ICJQYXNtbyBucmQwIiwgY29sPSJyZWQiKQ0KDQpkX3Njb3R0IDwtIGRlbnNpdHkoZGFuZSRkb2Nob2QsIGJ3ID0gIm5yZCIpDQpwbG90KGRfc2NvdHQsIG1haW4gPSAiUGFzbW8gU2NvdHRhIiwgY29sPSd5ZWxsb3cnKQ0KDQpkX3VjdiA8LSBkZW5zaXR5KGRhbmUkZG9jaG9kLCBidyA9ICJ1Y3YiKQ0KcGxvdChkX3VjdiwgbWFpbiA9ICJQYXNtbyBVQ1YiLCBjb2w9ImdyZWVuIikNCg0KZF9TSiA8LSBkZW5zaXR5KGRhbmUkZG9jaG9kLCBidyA9ICJTSiIpDQpwbG90KGRfU0osIG1haW4gPSAiUGFzbW8gU2hlYXRoZXJhICYgSm9uZXNhIiwgY29sPSJibHVlIikNCmBgYA0KDQpXbmlvc2tpOg0KDQpQYXNtbyBVQ1YgaSBQYXNtbyBTaGVhdGhlcmEgJiBKb25lc2EgKFNKKSB1amF3bmlhasSFIG5handpxJljZWogc3pjemVnw7PFgsOzdw0KaSBsb2thbG55Y2ggd2FyaWFjamkgdyByb3prxYJhZHppZSBkb2Nob2TDs3csIGFsZSBtb2fEhSB3cHJvd2FkemHEhyDigJ5zenVtIiwNCmt0w7NyeSB1dHJ1ZG5pYSBvZ8OzbG7EhSBpbnRlcnByZXRhY2rEmSBkYW55Y2guIFPEhSBvbmUgb2Rwb3dpZWRuaWUsIGplxZtsaQ0KcG90cnplYnVqZW15IHN6Y3plZ8OzxYJvd2VqIGFuYWxpenkgbG9rYWxueWNoIHptaWFuIHcgcm96a8WCYWR6aWUuDQoNClBhc21vIG5yZDAgaSBQYXNtbyBTY290dGEgemFwZXduaWFqxIUgZ8WCYWRzenkgb2JyYXogcm96a8WCYWR1LCBrdMOzcnkgamVzdA0KYmFyZHppZWogb2Rwb3JueSBuYSBzenVtIGkgZmx1a3R1YWNqZSBsb2thbG5lLCBjbyBqZXN0IGtvcnp5c3RuZSwgZ2R5DQppbnRlcmVzdWplIG5hcyBiYXJkemllaiBvZ8OzbG55IGtzenRhxYJ0IHJvemvFgmFkdS4gUGFzbW8gU2NvdHRhIGRhamUNCm5hamJhcmR6aWVqIHd5Z8WCYWR6b255IHd5bmlrLCBpZGVhbG55IGRsYSBkdcW8eWNoIHplc3Rhd8OzdyBkYW55Y2guDQoNCmBgYHtyfQ0KZ2dwbG90KGRhbmUsIGFlcyh4PWRvY2hvZCwgZmlsbD1wbGVjKSkrDQogIHN0YXRfZGVuc2l0eShidyA9ICJucmQiKSsNCiAgbGFicyh0aXRsZT0iRXN0eW1hY2phIGfEmXN0b8WbY2kgeiBwYXNtZW0gU2NvdHRhIHogcG9kemlhxYJlbSBuYSBwxYJlxIciKQ0KYGBgDQoNCiMjIyBPc3phY293YW5pZSBza3VtdWxvd2FuZWogZ8SZc3RvxZtjaQ0KDQpEeXN0cnlidWFudGEgdG8gZnVua2NqYSwga3TDs3JhIG9waXN1amUgcHJhd2RvcG9kb2JpZcWEc3R3bywgxbxlIHptaWVubmENCmxvc293YSBwcnp5am1pZSB3YXJ0b8WbxIcgbW5pZWpzesSFIGx1YiByw7N3bsSFIGRhbmVqIHdhcnRvxZtjaSB4Lg0KDQojIyMjICoqVHlwIGrEhWRyYSoqDQoNCmBgYHtyfQ0KZHlzdHJ5YnVhbnRhX2cgPC0gYXBwcm94ZnVuKGRfZ2F1c3NpYW4keCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY3Vtc3VtKGRfZ2F1c3NpYW4keSkvc3VtKGRfZ2F1c3NpYW4keSkpDQoNCmR5c3RyeWJ1YW50YV9lIDwtIGFwcHJveGZ1bihkX2VwYW5lY2huaWtvdiR4LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjdW1zdW0oZF9lcGFuZWNobmlrb3YkeSkvc3VtKGRfZXBhbmVjaG5pa292JHkpKQ0KDQpkeXN0cnlidWFudGFfciA8LSBhcHByb3hmdW4oZF9yZWN0YW5ndWxhciR4LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjdW1zdW0oZF9yZWN0YW5ndWxhciR5KS9zdW0oZF9yZWN0YW5ndWxhciR5KSkNCg0KeF92YWxzIDwtIHNlcShtaW4oZGFuZSRkb2Nob2QpLCBtYXgoZGFuZSRkb2Nob2QpLCBsZW5ndGgub3V0ID0gMTAwKQ0KYGBgDQoNCmBgYHtyfQ0KDQpwYXIobWZyb3cgPSBjKDMsIDEpKQ0KcGxvdCh4X3ZhbHMsIGR5c3RyeWJ1YW50YV9nKHhfdmFscyksIHBjaD0xLA0KICAgICBtYWluID0gIkNERiBkbGEgasSFZHJhIEdhdXNzb3dza2llZ28iLCANCiAgICAgeGxhYiA9ICJEb2Now7NkIiwgeWxhYiA9ICJEeXN0cnlidWFudGEiKQ0KcGxvdCh4X3ZhbHMsIGR5c3RyeWJ1YW50YV9lKHhfdmFscyksIHBjaD0xLA0KICAgICBtYWluID0gIkNERiBkbGEgasSFZHJhIEVwYW5lY2huaWtvdmEiLCANCiAgICAgeGxhYiA9ICJEb2Now7NkIiwgeWxhYiA9ICJEeXN0cnlidWFudGEiKQ0KcGxvdCh4X3ZhbHMsIGR5c3RyeWJ1YW50YV9yKHhfdmFscyksIHBjaD0xLA0KICAgICBtYWluID0gIkNERiBkbGEgasSFZHJhIHByb3N0b2vEhXRuZWdvIiwgDQogICAgIHhsYWIgPSAiRG9jaMOzZCIsIHlsYWIgPSAiRHlzdHJ5YnVhbnRhIikNCmBgYA0KDQpXc3p5c3RraWUgdHJ6eSBkeXN0cnlidWFudHkgKGRsYSByw7PFvG55Y2ggasSFZGVyKSBtYWrEhSBiYXJkem8gcG9kb2JueQ0Ka3N6dGHFgnQuIEthxbxkeSB6IHd5a3Jlc8OzdyBwb2thenVqZSwgxbxlIHdpxJlrc3pvxZvEhyB3YXJ0b8WbY2kgZG9jaG9kw7N3DQp6bmFqZHVqZSBzacSZIHcgZG9sbnltIHpha3Jlc2llLCBjbyBwb3dvZHVqZSBzenlia2kgd3pyb3N0IGR5c3RyeWJ1YW50eQ0KbmEgcG9jesSFdGt1LCBhIG5hc3TEmXBuaWUgd3lwxYJhc3pjemVuaWUuIE96bmFjemEgdG8sIMW8ZSBkbGEgd2nEmWtzem/Fm2NpDQpwb3B1bGFjamkgZG9jaG9keSBzxIUgc3Rvc3Vua293byBuaXNraWUuDQoNCiMjIyMgU2VsZWtjamEgcGFzbWENCg0KYGBge3J9DQpkeXN0cnlidWFudGFfMCA8LSBhcHByb3hmdW4oZF8wJHgsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGN1bXN1bShkXzAkeSkvc3VtKGRfMCR5KSkNCg0KZHlzdHJ5YnVhbnRhX3MgPC0gYXBwcm94ZnVuKGRfc2NvdHQkeCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY3Vtc3VtKGRfc2NvdHQkeSkvc3VtKGRfc2NvdHQkeSkpDQoNCmR5c3RyeWJ1YW50YV91Y3YgPC0gYXBwcm94ZnVuKGRfdWN2JHgsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGN1bXN1bShkX3VjdiR5KS9zdW0oZF91Y3YkeSkpDQoNCmR5c3RyeWJ1YW50YV9TSiA8LSBhcHByb3hmdW4oZF9TSiR4LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjdW1zdW0oZF9TSiR5KS9zdW0oZF9TSiR5KSkNCg0KeF92YWxzIDwtIHNlcShtaW4oZGFuZSRkb2Nob2QpLCBtYXgoZGFuZSRkb2Nob2QpLCBsZW5ndGgub3V0ID0gMTAwKQ0KDQpgYGANCg0KYGBge3J9DQoNCnBsb3QoeF92YWxzLCBkeXN0cnlidWFudGFfMCh4X3ZhbHMpLCBwY2g9MSwgDQogICAgIG1haW4gPSAiQ0RGIGRsYSBwYXNtYSBucmQwIiwgDQogICAgIHhsYWIgPSAiRG9jaMOzZCIsIHlsYWIgPSAiRHlzdHJ5YnVhbnRhIiwgDQogICAgIGNvbCA9ICJyZWQiLCBsd2QgPSAyKQ0KDQpgYGANCg0KU3plcm9rb8WbxIcgcGFzbWEgbWEgb2dyYW5pY3pvbnkgd3DFgnl3IG5hIHd5bmlraSBlc3R5bWFjamkgZHlzdHJ5YnVhbnR5LA0KY28gb2JzZXJ3dWplbXkgbmEgd3lrcmVzYWNoLCBnZHppZSBrcnp5d2UgQ0RGIGRsYSByw7PFvG55Y2ggcGFzbSBpIGrEhWRlcg0Kc8SFIGRvIHNpZWJpZSB6YmxpxbxvbmUuIFphcsOzd25vIGVzdHltYWNqYSBnxJlzdG/Fm2NpLCBqYWsgaSBza3VtdWxvd2FuZWoNCmfEmXN0b8WbY2kgc8SFIHdhcnRvxZtjaW93eW1pIG5hcnrEmWR6aWFtaSB3IGFuYWxpemllIHJvemvFgmFkdSBkYW55Y2gsDQpwb3p3YWxhasSFYyBuYSBkb2vFgmFkbmllanN6ZSB6cm96dW1pZW5pZSBzdHJ1a3R1cnkgZGFueWNoLiBSw7PFvG5lIGrEhWRyYSBpDQptZXRvZHkgd3lib3J1IHBhc21hIG1vZ8SFIGplZG5hayBwcnp5bmllxZvEhyBzdWJ0ZWxuZSByw7PFvG5pY2UgdyB3eW5pa2FjaCwNCmNvIHBvZGtyZcWbbGEgem5hY3plbmllIHByemVtecWbbGFuZWdvIGRvYm9ydSB0eWNoIHBhcmFtZXRyw7N3LCBzemN6ZWfDs2xuaWUNCncgYW5hbGl6YWNoIHN0YXR5c3R5Y3pueWNoLCBnZHppZSBwcmVjeXpqYSBtYSBrbHVjem93ZSB6bmFjemVuaWUuDQo=