Wprowadzenie
Regresja kwantylowa (ang. quantile regression) została zaproponowana
przez Koenkera i Bassetta (1978). Szczególny przypadek regresji
kwantylowej dla kwantyla rzędu 0,5 (czyli mediany) jest równoważny
estymatorowi LAD (ang. Least Absolute Deviation) – minimalizuje sumę
bezwzględnych błędów.
Wprowadzenie różnych kwantyli regresji daje pełniejszy opis rozkładów
warunkowych zwłaszcza w przypadku rozkładów asymetrycznych lub
uciętych.
Regresja kwantylowa jest kolejną wariacją na temat najmniejszych
kwadratów . Stratą jest współczynnik \(l_1\) funkcji:
\[
\phi(u) = \tau\max(u,0) - (1-\tau)\max(-u,0) = \frac{1}{2}|u| +
\left(\tau - \frac{1}{2}\right)u,
\]
gdzie \(\tau \in (0,1)\) oznacza
konkretny kwantyl. Problemem jak poprzednio jest minimalizacja
całkowitej straty resztowej. Model ten jest powszechnie stosowany w
ekologii, ochronie zdrowia i innych dziedzinach, gdzie sama średnia nie
wystarcza do uchwycenia złożonych zależności między zmiennymi.
Przykład 1.
Wykorzystamy przykład z pakietu quantreg.
Jaki jest związek między całkowitym dochodem gospodarstwa domowego a
odsetkiem dochodów wydatkowanych na żywność? Prawo Engela w ekonomii
głosi, że w miarę wzrostu dochodów, część dochodów wydatkowanych na
żywność spada, nawet jeśli wydatki na żywność bezwzględnie rosną.
Stosując regresję kwantylową do tych danych, można określić, jakie
wydatki na żywność ponosi 90% rodzin (dla 100 rodzin z danym dochodem),
gdy nie interesują nas średnie wydatki na żywność.
Dane, które wykorzystamy - to zbiór “engel” - dane dotyczące wydatków
na żywność. Jest to zbiór danych regresyjnych składający się z 235
obserwacji dotyczących dochodów i wydatków na żywność dla belgijskich
gospodarstw domowych klasy robotniczej.

Powyższy wykres przedstawia dopasowanie regresji kwantylowej dla
\(\tau = (0.1, 0.25, 0.5, 0.75, 0.90,
0.95)\). Dopasowanie KMNK to gruba czarna linia.
Poniżej znajduje się tabela z oszacowanymi współczynnikami.
knitr::kable(fits, format = "html", caption = "Oszacowania z KMNK oraz `quantreg`") %>%
kable_styling("striped") %>%
column_spec(1:8, background = "grey")
Oszacowania z KMNK oraz quantreg
|
|
OLS
|
\(\tau_{0.10}\)
|
\(\tau_{0.25}\)
|
\(\tau_{0.50}\)
|
\(\tau_{0.75}\)
|
\(\tau_{0.90}\)
|
\(\tau_{0.95}\)
|
|
(Intercept)
|
147.4753885
|
110.1415742
|
95.4835396
|
81.4822474
|
62.3965855
|
67.3508721
|
64.1039632
|
|
income
|
0.4851784
|
0.4017658
|
0.4741032
|
0.5601806
|
0.6440141
|
0.6862995
|
0.7090685
|
Ok, możemy to zrobić bardziej przejrzyście i sformatować w ładnej
tabeli wyników:
##
## Wyniki regresji kwantylowych
## ==========================================
## Dependent variable:
## -----------------------------
## foodexp
## (1) (2) (3)
## ------------------------------------------
## income 0.474*** 0.560*** 0.644***
## (0.029) (0.028) (0.023)
##
## Constant 95.484*** 81.482*** 62.397***
## (21.392) (19.251) (16.305)
##
## ------------------------------------------
## Observations 235 235 235
## ==========================================
## Note: *p<0.1; **p<0.05; ***p<0.01
Finalnie, zaprezentujmy wyłącznie te 3 modele na wykresie:

Przykład 2.
Tutaj przeprowadzimy testy użycia pakietu quantreg, wykorzystując
wbudowany zbiór danych “mtcars”. Zmienna
“mpg” oznacza spalanie samochodów
(mile/galon).
Zamodulejmy zależność regresyjną dla tej zmiennej od kilku
predyktorów.
Najpierw oszacujmy regresję KMNK:
kmnk <- lm(mpg ~ disp + hp + factor(am) + factor(vs), data = mtcars)
summary(kmnk)
##
## Call:
## lm(formula = mpg ~ disp + hp + factor(am) + factor(vs), data = mtcars)
##
## Residuals:
## Min 1Q Median 3Q Max
## -4.7981 -1.9532 0.0111 1.5665 5.6321
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 24.832119 2.890418 8.591 3.32e-09 ***
## disp -0.008304 0.010087 -0.823 0.41757
## hp -0.037623 0.013846 -2.717 0.01135 *
## factor(am)1 4.419257 1.493243 2.960 0.00634 **
## factor(vs)1 2.052472 1.627096 1.261 0.21794
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.812 on 27 degrees of freedom
## Multiple R-squared: 0.8104, Adjusted R-squared: 0.7823
## F-statistic: 28.85 on 4 and 27 DF, p-value: 2.13e-09
Teraz oszacujmy warunkowe regresje kwantylowe na różnych kwantylach,
błąd standardowy uzyskany przez bootstrap.
Zauważ, że istnieje gradient we współczynnikach kwantylowych
hp, jak również disp. Znak
disp odwraca się, również współczynnik na czynniku
am jest różny w zależności od kwantyli:
kwantyle <- c(0.25, 0.50, 0.75)
reg_kwantylowa <- rq(mpg ~ disp + hp + factor(am),tau = kwantyle,data = mtcars)
summary(reg_kwantylowa, se = "boot")
##
## Call: rq(formula = mpg ~ disp + hp + factor(am), tau = kwantyle, data = mtcars)
##
## tau: [1] 0.25
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) 25.34665 1.46648 17.28399 0.00000
## disp -0.02441 0.00874 -2.79296 0.00931
## hp -0.01672 0.01658 -1.00848 0.32186
## factor(am)1 1.39719 1.22510 1.14047 0.26375
##
## Call: rq(formula = mpg ~ disp + hp + factor(am), tau = kwantyle, data = mtcars)
##
## tau: [1] 0.5
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) 27.49722 1.71191 16.06230 0.00000
## disp -0.02253 0.01614 -1.39605 0.17367
## hp -0.02713 0.02298 -1.18070 0.24766
## factor(am)1 3.37328 2.15080 1.56839 0.12802
##
## Call: rq(formula = mpg ~ disp + hp + factor(am), tau = kwantyle, data = mtcars)
##
## tau: [1] 0.75
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) 28.06384 1.69072 16.59871 0.00000
## disp 0.00445 0.01651 0.26963 0.78942
## hp -0.06662 0.02294 -2.90444 0.00711
## factor(am)1 7.91402 2.61149 3.03046 0.00521
Testy współczynników
Użyjemy funkcji rq.anova z pakietu regresji kwantylowej, aby
przeprowadzić test WALDA. Pamiętaj, że test WALDA mówi, że biorąc pod
uwagę nieograniczone oszacowania modelu, przetestujemy hipotezę zerową
mówiącą, że współczynniki spełniają pewne liniowe ograniczenia.
Aby ją przetestować, użyjemy obiektu zwróconego z uruchomienia
rq z różnymi liczbami kwantyli i ustawimy
opcję joint na true lub false. Gdy
joint jest true: “równość współczynników
kierunkowych powinna być wykonana jako wspólne testy na wszystkich
parametrach nachylenia”, gdy joint jest false:
“należy zgłaszać oddzielne testy na każdym z parametrów nachylenia”.
Zauważ, że testy kwantylowe są testami “linii równoległej”. Oznacza
to, że powinniśmy wyjąć różne x-wyrazy_wolne dla każdego kwantyla,
ponieważ reprezentują one poziomy rozkładów warunkowych. Jeśli jednak
współczynniki kwantyli dla współczynnikow są takie same, to nie ma
efektów specyficznych dla kwantyli, wystarczą efekty średnie.
Badanie statystycznej różnicy między 25. i 50. kwantylem
warunkowym:
Biorąc pod uwagę powyższe oszacowania kwantyli, różnica między
kwantylami 0,25 i 0,50 istnieje, ale czy są one wystarczająco duże, aby
być statystycznie różne? Jaka jest wartość p? Przeglądając poniższe
wyniki, nie są one statystycznie różne!
Po pierwsze, joint = TRUE. To nie jest testowanie, czy współczynnik
na disp jest taki sam jak współczynnik na hp. To jest wspólne
testowanie, czy współczynniki dla różnych kwantyli disp i różnych
kwantyli hp są takie same dla każdej zmiennej.
kwantyle <- c(0.25, 0.50)
reg_kwantylowa <- rq(mpg ~ disp + hp + factor(am),tau = kwantyle, data = mtcars)
anova(reg_kwantylowa, test = "Wald", joint=TRUE)
## Quantile Regression Analysis of Deviance Table
##
## Model: mpg ~ disp + hp + factor(am)
## Joint Test of Equality of Slopes: tau in { 0.25 0.5 }
##
## Df Resid Df F value Pr(>F)
## 1 3 61 0.8421 0.4761
Po drugie, joint = False:
anova(reg_kwantylowa, test = "Wald", joint=FALSE)
## Quantile Regression Analysis of Deviance Table
##
## Model: mpg ~ disp + hp + factor(am)
## Tests of Equality of Distinct Slopes: tau in { 0.25 0.5 }
##
## Df Resid Df F value Pr(>F)
## disp 1 63 0.0305 0.8619
## hp 1 63 0.5461 0.4627
## factor(am)1 1 63 1.3500 0.2497
Badanie statystycznej różnicy między 25, 50 i 75 kwantylem
warunkowym:
Pierwszy kwartyl i mediana nie wydają się być statystycznie różne,
teraz dołączymy trzeci kwartyl. Jak widać wcześniej, kwartyle wspólnie
wykazują gradient. Teraz możemy zobaczyć, że disp,
hp i am są oddzielnie statystycznie
różne.
Po pierwsze, joint = TRUE:
kwantyle <- c(0.25, 0.50, 0.75)
reg_kwantylowa <- rq(mpg ~ disp + hp + factor(am),tau = kwantyle, data = mtcars)
anova(reg_kwantylowa, test = "Wald", joint=TRUE)
## Quantile Regression Analysis of Deviance Table
##
## Model: mpg ~ disp + hp + factor(am)
## Joint Test of Equality of Slopes: tau in { 0.25 0.5 0.75 }
##
## Df Resid Df F value Pr(>F)
## 1 6 90 3.3173 0.005367 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Po drugie, joint = False:
anova(reg_kwantylowa, test = "Wald", joint=FALSE)
## Quantile Regression Analysis of Deviance Table
##
## Model: mpg ~ disp + hp + factor(am)
## Tests of Equality of Distinct Slopes: tau in { 0.25 0.5 0.75 }
##
## Df Resid Df F value Pr(>F)
## disp 2 94 5.4903 0.005558 **
## hp 2 94 6.7221 0.001868 **
## factor(am)1 2 94 7.2758 0.001154 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Dobroć dopasowania
Możemy obliczyć współczynniki dobroci dopasowania regresji
kwantylowej z wykorzystaniem reszt i reszt bezwarunkowych:
goodfit(resid, resid_nl, tau)
Miara dobroci dopasowania dla regresji kwantylowej jest szacowana
jako 1 minus stosunek sumy odchyleń bezwzględnych w modelach w pełni
sparametryzowanych do sumy odchyleń bezwzględnych w zerowym
(bezwarunkowym) modelu kwantylowym.
Wartości te są przydatne do porównań między modelami kwantylowymi,
ale nie są porównywalne ze standardowymi współczynnikami determinacji.
Te ostatnie oparte są na wariancji odchyleń kwadratowych, natomiast
wartości dobroci dopasowania dla regresji kwantylowej oparte są na
odchyleniach bezwzględnych. Wartości dobroci dopasowania zawsze będą
mniejsze niż wartości R2.
## model kwantylowy
model1 <- rq(mpg ~ disp + hp + factor(am),tau = 0.5, data = mtcars)
reszty1 <- resid(model1)
## bezwarunkowy (pusty) model kwantylowy
model2 <- rq(mpg ~ 1, tau = 0.5,data=mtcars)
reszty2 <- resid(model2)
goodfit(reszty1, reszty2, 0.5)
## [1] 0.5403311
## r2 modelu KMNK dla porównania
model_lm <- lm(mpg ~ disp + hp + factor(am), data = mtcars)
summary(model_lm)$r.squared
## [1] 0.7992061
Zadanie
Teraz Wasza kolej ;-)
Waszym zadaniem dzisiaj jest zamodelowanie - porównanie KMNK oraz
regresji kwantylowej (różno-poziomowej) dla zmiennej “earnings” -
wynagrodzenia.
Dobierz i przetestuj predyktory, kwantyle dla modeli. Wykonaj testy
różnic współczynnikow dla finalnych modeli.
W przypadku problemów - obejrzyj video tutorial (włącz polskie
napisy) oraz wejdź na jego stronę ze źródłami. Możesz również
wykorzystać w/w przykłady.
Import danych
Poniżej zaimportowano zbiór danych CPSSW9298, w którym znajdują się
badane wynagrodzenia.
data("CPSSW9298")
dane <- CPSSW9298
Wizualizacja danych
Na poniższym histogramie przedstawiono rozkład wynagrodzeń.
wykres0 <- ggplot(dane, aes(x=earnings)) +
geom_histogram(bins=30, color="dodgerblue4", fill="lightblue") +
xlab("Zarobki") + ylab("Częstość") +
ggtitle("Histogram rozkładu wynagrodzeń") +
xlab("Wynagrodzenia") + ylab("Częstość")
ggplotly(wykres0)
Na podstawie powyższego rysunku można zauważyć występowanie asymetrii
prawostronnej. Oznacza to, że w badanej próbie przeważają wynagordzenia
o wartości poniżej średniej.
Na poniższym rysunku przedstawiono porównanie wynagrodzeń kobiet i
mężczyzn.
wykres1 <- ggplot(dane, aes(y=earnings, x=gender, fill=gender)) +
geom_boxplot() +
ggtitle("Porówanie wynagrodzeń kobiet i mężczyzn") +
xlab("Płeć") + ylab("Wynagrodzenia") +
labs(fill="Płeć")
ggplotly(wykres1)
Na podstawie powyższego rysunku można zauważyć, że mężczyźni
posiadają wyższe wynagrodzenia względem kobiet.
Na poniższym rysunku przedstawiono porównanie wynagrodzeń w
zależności od wykształcenia.
wykres2 <- ggplot(dane, aes(y=earnings, x=degree, fill= degree)) +
geom_boxplot() +
ggtitle("Porówanie wynagrodzeń w zależności od wykształcenia") +
xlab("Wykształcenie") + ylab("Wynagrodzenia") +
labs(fill="Wykształcenie")
ggplotly(wykres2)
Na podstawie powyższego rysunku można zauważyć, że osoby posiadające
tytuł licencjata posiadają wyższe wynagrodzenia względem osób, które
ukończyły jedynie szkołę średnią.
Na poniższym rysunku przedstawiono porównanie wynagrodzeń w
zależności od płci i wykształcenia.
wykres3 <- ggplot(dane, aes(y=earnings, x=degree, fill=gender)) +
geom_boxplot() +
ggtitle("Porówanie wynagrodzeń kobiet i mężczyzn w zależności od wykształcenia") +
xlab("Wykształcenie") + ylab("Wynagrodzenia") +
labs(fill="Płeć")
wykres3
Na podstawie powyższego rysunku można zauważyć, że najwwyższe
wynagrodzenia posiadają mężczyźni z tytułem licencjata, a najniższe
absolwentki szkoły średniej.
Regresja KMNK
Poniżej zbudowano model regresji liniowej szacowanej za pomocą
klasycznej metody najmnijeszych kwadratwów. Zmienną wynikową stanowiły
wynagrodzenia, natomiast wśród predyktorów uwzględniono: wiek, stopień
wykształcenia oraz płeć.
kmnk <- lm(earnings ~ age + degree + gender, data = dane)
summary(kmnk)
##
## Call:
## lm(formula = earnings ~ age + degree + gender, data = dane)
##
## Residuals:
## Min 1Q Median 3Q Max
## -15.023 -3.735 -0.819 2.755 33.526
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.70477 0.52435 3.251 0.00115 **
## age 0.33245 0.01740 19.106 < 2e-16 ***
## degreebachelor 4.91123 0.09938 49.418 < 2e-16 ***
## genderfemale -2.24217 0.09902 -22.643 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 5.665 on 13497 degrees of freedom
## Multiple R-squared: 0.1877, Adjusted R-squared: 0.1876
## F-statistic: 1040 on 3 and 13497 DF, p-value: < 2.2e-16
Na podstawie uzyskanych wyników można zauważyć, że na poziom
wyngrodzeń statystycznie istotnie wpływają: wiek, ukończenie studiów na
poziomie licencjackim oraz płeć kobieca.
Regresja kwantylowa
Poniżej zbudowano model regresji kwantylowej odpowiednio dla
pierwszego (Q1=0,25), drugiego (Q2=0,5) i trzeciego kwartyla (Q3=0,75).
Podobnie jak w poprzednim przypadku zmienną wynikową stanowiły
wynagrodzenia, natomiast jako predyktory wybrano: wiek, stopień
wykształcenia oraz płeć.
kwantyle <- c(0.25, 0.50, 0.75)
reg_kwantylowa <- rq(earnings ~ age + degree + gender , tau = kwantyle, data = dane)
summary(reg_kwantylowa, se = "boot")
##
## Call: rq(formula = earnings ~ age + degree + gender, tau = kwantyle,
## data = dane)
##
## tau: [1] 0.25
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) 2.67308 0.37674 7.09526 0.00000
## age 0.17308 0.01338 12.93260 0.00000
## degreebachelor 3.59188 0.09142 39.28839 0.00000
## genderfemale -1.32265 0.08139 -16.25171 0.00000
##
## Call: rq(formula = earnings ~ age + degree + gender, tau = kwantyle,
## data = dane)
##
## tau: [1] 0.5
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) 1.45427 0.52005 2.79641 0.00517
## age 0.31389 0.01805 17.38934 0.00000
## degreebachelor 4.70043 0.11483 40.93469 0.00000
## genderfemale -2.12970 0.11439 -18.61718 0.00000
##
## Call: rq(formula = earnings ~ age + degree + gender, tau = kwantyle,
## data = dane)
##
## tau: [1] 0.75
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) 1.53846 0.92699 1.65963 0.09701
## age 0.43269 0.03052 14.17740 0.00000
## degreebachelor 5.75962 0.17179 33.52789 0.00000
## genderfemale -2.97009 0.11448 -25.94337 0.00000
Przyjmując za poziom istotności statystycznej 5% (alfa=0,05) można
zauważyć, że zarówno dla pierwszego, jak i drugiego kwartyla wszystkie
predyktory istotnie statystcznie wpływają na poziom wynagrodzeń.
Jednakże w przypadku trzeciego kwartyla wiek okazał się być staystycznie
nieistonym czynniem kształtującym wysokość wynagrodzeń.
Weryfikacja statystyczna istotności różnic między pierwszym, drugim
i trzecim kwartylem
Poniżej wykonano test ANOVA w celu zbadania czy pomiędzy rozważanymi
kwartylami wystepują statystycznie istotne różnice.
anova(reg_kwantylowa, test = "Wald", joint=TRUE)
## Quantile Regression Analysis of Deviance Table
##
## Model: earnings ~ age + degree + gender
## Joint Test of Equality of Slopes: tau in { 0.25 0.5 0.75 }
##
## Df Resid Df F value Pr(>F)
## 1 6 40497 90.65 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Na podstawie uzyskanego wyniku można zauważyć, że zarówno pierwszy,
drugi, jak i trzeci kwartyl łącznie istotnie statystycznie różnią się
między sobą.
Poniżej wykonao test ANOVA w celu zbadania czy analizowane
determinanty wynagrodzeń są istotnie statystycznie różne.
anova(reg_kwantylowa, test = "Wald", joint=FALSE)
## Quantile Regression Analysis of Deviance Table
##
## Model: earnings ~ age + degree + gender
## Tests of Equality of Distinct Slopes: tau in { 0.25 0.5 0.75 }
##
## Df Resid Df F value Pr(>F)
## age 2 40501 72.465 < 2.2e-16 ***
## degreebachelor 2 40501 117.817 < 2.2e-16 ***
## genderfemale 2 40501 88.771 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Na podstawie uzyskanych wyników możemy zobaczyć, że uwzględnione w
badaniu predyktory wynagrodzeń są oddzielnie statystycznie różne.
Dobroć dopasowania
Poniżej obliczono współczynniki dobroci dopasowania regresji
kwantylowej z wykorzystaniem reszt i reszt bezwarunkowych:
# Testy reszt:
reszty0 <- resid(kmnk)
# Bezwarunkowy (pusty) model kwantylowy dla Q2 = 0.5
model2 <- rq(earnings ~ 1, tau = 0.5,data=dane)
reszty2 <- resid(model2)
goodfit(reszty0, reszty2, 0.5)
## [1] 0.09465109
# Bezwarunkowy (pusty) model kwantylowy dla Q3 = 0.75
model3 <- rq(earnings ~ 1, tau = 0.75,data=dane)
reszty3 <- resid(model3)
goodfit(reszty0, reszty3, 0.75)
## [1] 0.01350041
# Współczynnik dterminacji modelu KMNK dla porównania
summary(kmnk)$r.squared
## [1] 0.1877333
Na podstawie uzyskanych wyników można zauważyć, że współczynnik
determinacji dla regresji liniowej uzyskał najwyższą wartość. Oznacza
to, że niniejszy model najlepiej wyjaśnia badane zjawisko. Regresja
liniowa lepiej dopasowała się do danych empirycznych wobec regresji
kwantylowej zarówno dla drugiego, jak i trzeciego kwartyla.
LS0tDQp0aXRsZTogIk5pZWtsYXN5Y3puZSBtZXRvZHkgc3RhdHlzdHlraSINCmF1dGhvcjogIktvbnN0YW5jamEgUCINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0aGVtZTogY2VydWxlYW4NCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlDQogICAgZm9udHNpemU6IDEwcHQNCiAgICB0b2M6IHllcw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogbm8NCiAgICBkZl9wcmludDogZGVmYXVsdA0KICAgIHRvY19kZXB0aDogNQ0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6ICc1Jw0Kc3VidGl0bGU6IFJlZ3Jlc2phIGt3YW50eWxvd2ENCmVkaXRvcl9vcHRpb25zOg0KICBtYXJrZG93bjoNCiAgICB3cmFwOiA3Mg0KLS0tDQoNCmBgYHtyIHByZXJlcXMsIG1lc3NhZ2UgPSBGQUxTRSwgZWNobyA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShDVlhSKQ0KbGlicmFyeShBRVIpDQpsaWJyYXJ5KHN0YXJnYXplcikNCmxpYnJhcnkoV1JURFN0aWRhbCkNCmxpYnJhcnkoaGFyZGhhdCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeShxdWFudHJlZykNCmxpYnJhcnkoUG9ncm9tY3lEYW55Y2gpDQpsaWJyYXJ5KHBsb3RseSkNCmBgYA0KDQojIyBEbGFjemVnbyBrd2FudHlsb3dhPw0KDQpEbGFjemVnbyBwb3RyemVidWplbXkgcmVncmVzamkga3dhbnR5bG93ZWogKFFSKT8NCg0KVyBzemN6ZWfDs2xub8WbY2ksIFFSOg0KDQotICAgamVzdCBvZHBvcm5hIG5hIHB1bmt0eSBvZHN0YWrEhWNlIGkgd3DFgnl3b3dlDQoNCi0gICBuaWUgemFrxYJhZGEgc3RhxYJlaiB3YXJpYW5jamkgKHpuYW5laiBqYWtvIGhvbW9za2VkYXN0eWN6bm/Fm8SHKSBkbGENCiAgICB6bWllbm5laiBvZHBvd2llZHppIGx1YiByZXN6dA0KDQotICAgbmllIHpha8WCYWRhIG5vcm1hbG5vxZtjaSBhbGUgZ8WCw7N3bsSFIHphbGV0xIUgUVIgdyBwb3LDs3duYW5pdSB6IHJlZ3Jlc2rEhQ0KICAgIGxpbmlvd8SFIChMUikgamVzdCB0bywgxbxlIFFSIGJhZGEgcsOzxbxuZSB3YXJ0b8WbY2kgem1pZW5uZWogb2Rwb3dpZWR6aSwNCiAgICBhIG5pZSB0eWxrbyDFm3JlZG5pxIUsIGkgZG9zdGFyY3phIHcgendpxIV6a3UgeiB0eW0gcGXFgm5pZWpzemVnbyBvYnJhenUNCiAgICB6d2nEhXprw7N3IG1pxJlkenkgem1pZW5ueW1pIQ0KDQojIyBXcHJvd2FkemVuaWUNCg0KUmVncmVzamEga3dhbnR5bG93YSAoYW5nLiBxdWFudGlsZSByZWdyZXNzaW9uKSB6b3N0YcWCYSB6YXByb3Bvbm93YW5hDQpwcnpleiBLb2Vua2VyYSBpIEJhc3NldHRhICgxOTc4KS4gU3pjemVnw7NsbnkgcHJ6eXBhZGVrIHJlZ3Jlc2ppDQprd2FudHlsb3dlaiBkbGEga3dhbnR5bGEgcnrEmWR1IDAsNSAoY3p5bGkgbWVkaWFueSkgamVzdCByw7N3bm93YcW8bnkNCmVzdHltYXRvcm93aSBMQUQgKGFuZy4gTGVhc3QgQWJzb2x1dGUgRGV2aWF0aW9uKSAtLSBtaW5pbWFsaXp1amUgc3VtxJkNCmJlend6Z2zEmWRueWNoIGLFgsSZZMOzdy5cDQpXcHJvd2FkemVuaWUgcsOzxbxueWNoIGt3YW50eWxpIHJlZ3Jlc2ppIGRhamUgcGXFgm5pZWpzenkgb3BpcyByb3prxYJhZMOzdw0Kd2FydW5rb3d5Y2ggenfFgmFzemN6YSB3IHByenlwYWRrdSByb3prxYJhZMOzdyBhc3ltZXRyeWN6bnljaCBsdWIgdWNpxJl0eWNoLg0KDQpSZWdyZXNqYSBrd2FudHlsb3dhIGplc3Qga29sZWpuxIUgd2FyaWFjasSFIG5hIHRlbWF0IG5ham1uaWVqc3p5Y2gNCmt3YWRyYXTDs3cgXGNpdGVwe3F1YW50aWxlfS4gU3RyYXTEhSBqZXN0IHdzcMOzxYJjenlubmlrICRsXzEkIGZ1bmtjamk6DQoNCiQkDQogICAgXHBoaSh1KSA9IFx0YXVcbWF4KHUsMCkgLSAoMS1cdGF1KVxtYXgoLXUsMCkgPSBcZnJhY3sxfXsyfXx1fCArIFxsZWZ0KFx0YXUgLSBcZnJhY3sxfXsyfVxyaWdodCl1LA0KJCQNCg0KZ2R6aWUgJFx0YXUgXGluICgwLDEpJCBvem5hY3phIGtvbmtyZXRueSBrd2FudHlsLiBQcm9ibGVtZW0gamFrDQpwb3ByemVkbmlvIGplc3QgbWluaW1hbGl6YWNqYSBjYcWCa293aXRlaiBzdHJhdHkgcmVzenRvd2VqLiBNb2RlbCB0ZW4NCmplc3QgcG93c3plY2huaWUgc3Rvc293YW55IHcgZWtvbG9naWksIG9jaHJvbmllIHpkcm93aWEgaSBpbm55Y2gNCmR6aWVkemluYWNoLCBnZHppZSBzYW1hIMWbcmVkbmlhIG5pZSB3eXN0YXJjemEgZG8gdWNod3ljZW5pYSB6xYJvxbxvbnljaA0KemFsZcW8bm/Fm2NpIG1pxJlkenkgem1pZW5ueW1pLg0KDQojIyBXeW1hZ2FuaWENCg0KV3ltYWdhbmEgamVzdCBqZWRuYSBsaWN6Ym93YSB6bWllbm5hIHphbGXFvG5hLiBabWllbm5hIHByemV3aWR5d2FuYSBtdXNpDQpiecSHIHptaWVubsSFIGlsb8WbY2lvd8SFLiBQcmVkeWt0b3J5IG1vZ8SFIGJ5xIcgem1pZW5ueW1pIGlsb8WbY2lvd3ltaSBsdWINCnN6dHVjem55bWkgem1pZW5ueW1pIHcgcHJ6eXBhZGt1IHByZWR5a3RvcsOzdyBqYWtvxZtjaW93eWNoLiBBYnkgbW/FvG5hDQpiecWCbyB1cnVjaG9tacSHIGFuYWxpesSZLCB3eW1hZ2FueSBqZXN0IHd5cmF6IHdvbG55IGx1YiBjbyBuYWptbmllaiBqZWRlbg0KcHJlZHlrdG9yLg0KDQpSZWdyZXNqYSBrd2FudHlsb3dhIG5pZSBjenluaSB6YcWCb8W8ZcWEIGRvdHljesSFY3ljaCByb3prxYJhZHUgem1pZW5uZWoNCnByemV3aWR5d2FuZWogaSBqZXN0IG9kcG9ybmEgbmEgd3DFgnl3IG9ic2Vyd2Fjamkgb2RzdGFqxIVjeWNoLg0KDQpBbmFsaXphIGt3YW50eWxvd2EgamVzdCBwb2tyZXduYSByZWdyZXNqaSBtZXRvZMSFIG5ham1uaWVqc3p5Y2gNCmt3YWRyYXTDs3cuDQoNCiMjIFByenlrxYJhZCAxLg0KDQpXeWtvcnp5c3RhbXkgcHJ6eWvFgmFkIHogcGFraWV0dSBxdWFudHJlZy4NCg0KSmFraSBqZXN0IHp3acSFemVrIG1pxJlkenkgY2HFgmtvd2l0eW0gZG9jaG9kZW0gZ29zcG9kYXJzdHdhIGRvbW93ZWdvIGENCm9kc2V0a2llbSBkb2Nob2TDs3cgd3lkYXRrb3dhbnljaCBuYSDFvHl3bm/Fm8SHPyBQcmF3byBFbmdlbGEgdyBla29ub21paQ0KZ8WCb3NpLCDFvGUgdyBtaWFyxJkgd3pyb3N0dSBkb2Nob2TDs3csIGN6xJnFm8SHIGRvY2hvZMOzdyB3eWRhdGtvd2FueWNoIG5hDQrFvHl3bm/Fm8SHIHNwYWRhLCBuYXdldCBqZcWbbGkgd3lkYXRraSBuYSDFvHl3bm/Fm8SHIGJlend6Z2zEmWRuaWUgcm9zbsSFLg0KU3Rvc3VqxIVjIHJlZ3Jlc2rEmSBrd2FudHlsb3fEhSBkbyB0eWNoIGRhbnljaCwgbW/FvG5hIG9rcmXFm2xpxIcsIGpha2llDQp3eWRhdGtpIG5hIMW8eXdub8WbxIcgcG9ub3NpIDkwJSByb2R6aW4gKGRsYSAxMDAgcm9kemluIHogZGFueW0gZG9jaG9kZW0pLA0KZ2R5IG5pZSBpbnRlcmVzdWrEhSBuYXMgxZtyZWRuaWUgd3lkYXRraSBuYSDFvHl3bm/Fm8SHLg0KDQpEYW5lLCBrdMOzcmUgd3lrb3J6eXN0YW15IC0gdG8gemJpw7NyICJlbmdlbCIgLSBkYW5lIGRvdHljesSFY2Ugd3lkYXRrw7N3IG5hDQrFvHl3bm/Fm8SHLiBKZXN0IHRvIHpiacOzciBkYW55Y2ggcmVncmVzeWpueWNoIHNrxYJhZGFqxIVjeSBzacSZIHogMjM1DQpvYnNlcndhY2ppIGRvdHljesSFY3ljaCBkb2Nob2TDs3cgaSB3eWRhdGvDs3cgbmEgxbx5d25vxZvEhyBkbGEgYmVsZ2lqc2tpY2gNCmdvc3BvZGFyc3R3IGRvbW93eWNoIGtsYXN5IHJvYm90bmljemVqLg0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0KZGF0YShlbmdlbCkgI2RhbmUgDQpwIDwtIGdncGxvdChkYXRhID0gZW5nZWwpICsNCiAgICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGluY29tZSwgeSA9IGZvb2RleHApLCBjb2xvciA9ICJibHVlIikNCnRhdXMgPC0gYygwLjEsIDAuMjUsIDAuNSwgMC43NSwgMC45MCwgMC45NSkNCmZpdHMgPC0gZGF0YS5mcmFtZSgNCiAgICBjb2VmKGxtKGZvb2RleHAgfiBpbmNvbWUsIGRhdGEgPSBlbmdlbCkpLA0KICAgIHNhcHBseSh0YXVzLCBmdW5jdGlvbih4KSBjb2VmKHJxKGZvcm11bGEgPSBmb29kZXhwIH4gaW5jb21lLCBkYXRhID0gZW5nZWwsIHRhdSA9IHgpKSkpDQpuYW1lcyhmaXRzKSA8LSBjKCJPTFMiLCBzcHJpbnRmKCIkXFx0YXVfeyUwLjJmfSQiLCB0YXVzKSkNCm5mIDwtIG5jb2woZml0cykNCmNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKGNvbG9ycyA9IGMoImJsYWNrIiwgInJlZCIpKShuZikNCnAgPC0gcCArIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IGZpdHNbMSwgMV0sIHNsb3BlID0gZml0c1syLCAxXSwgY29sb3IgPSBjb2xvcnNbMV0sIGxpbmV3aWR0aCA9IDEuNSkNCmZvciAoaSBpbiBzZXFfbGVuKG5mKVstMV0pIHsNCiAgICBwIDwtIHAgKyBnZW9tX2FibGluZShpbnRlcmNlcHQgPSBmaXRzWzEsIGldLCBzbG9wZSA9IGZpdHNbMiwgaV0sIGNvbG9yID0gY29sb3JzW2ldKQ0KfQ0KcA0KYGBgDQoNClBvd3nFvHN6eSB3eWtyZXMgcHJ6ZWRzdGF3aWEgZG9wYXNvd2FuaWUgcmVncmVzamkga3dhbnR5bG93ZWogZGxhDQokXHRhdSA9ICgwLjEsIDAuMjUsIDAuNSwgMC43NSwgMC45MCwgMC45NSkkLiBEb3Bhc293YW5pZSBLTU5LIHRvIGdydWJhDQpjemFybmEgbGluaWEuDQoNClBvbmnFvGVqIHpuYWpkdWplIHNpxJkgdGFiZWxhIHogb3N6YWNvd2FueW1pIHdzcMOzxYJjenlubmlrYW1pLg0KDQpgYGB7cn0NCmtuaXRyOjprYWJsZShmaXRzLCBmb3JtYXQgPSAiaHRtbCIsIGNhcHRpb24gPSAiT3N6YWNvd2FuaWEgeiBLTU5LIG9yYXogYHF1YW50cmVnYCIpICU+JQ0KICAgIGthYmxlX3N0eWxpbmcoInN0cmlwZWQiKSAlPiUNCiAgICBjb2x1bW5fc3BlYygxOjgsIGJhY2tncm91bmQgPSAiZ3JleSIpDQpgYGANCg0KT2ssIG1vxbxlbXkgdG8genJvYmnEhyBiYXJkemllaiBwcnplanJ6ecWbY2llIGkgc2Zvcm1hdG93YcSHIHcgxYJhZG5laiB0YWJlbGkNCnd5bmlrw7N3Og0KDQpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1UUlVFfQ0KcTI1IDwtIHJxKGZvb2RleHAgfiBpbmNvbWUsIGRhdGEgPSBlbmdlbCwgdGF1ID0gMC4yNSkNCnE1MCA8LSBycShmb29kZXhwIH4gaW5jb21lLCBkYXRhID0gZW5nZWwsIHRhdSA9IDAuNTApDQpxNzUgPC0gcnEoZm9vZGV4cCB+IGluY29tZSwgZGF0YSA9IGVuZ2VsLCB0YXUgPSAwLjc1KQ0KDQojIFRhYmVsYSB6IHBvcsOzd25hbmllbSB3eW5pa8OzdyB0cnplY2ggbW9kZWxpOiANCg0Kc3RhcmdhemVyKHEyNSwgcTUwLCBxNzUsIHRpdGxlID0gIld5bmlraSByZWdyZXNqaSBrd2FudHlsb3d5Y2giLCB0eXBlID0gInRleHQiKQ0KYGBgDQoNCkZpbmFsbmllLCB6YXByZXplbnR1am15IHd5xYLEhWN6bmllIHRlIDMgbW9kZWxlIG5hIHd5a3Jlc2llOg0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0KbXlfcXIgPC0gcnEoZm9vZGV4cCB+IGluY29tZSwgZGF0YSA9IGVuZ2VsLCB0YXUgPSBzZXEoMC4yNSwgMC43NSwgMC4yNSkpDQoNCmludGVyY2VwdF9zbG9wZSA8LSBteV9xciAlPiUgDQogIGNvZWYoKSAlPiUgDQogIHQoKSAlPiUgDQogIGRhdGEuZnJhbWUoKSAlPiUgDQogIHJlbmFtZShpbnRlcmNlcHQgPSBYLkludGVyY2VwdC4sIHNsb3BlID0gaW5jb21lKSAlPiUgDQogIG11dGF0ZShxdWFudGlsZSA9IHJvdy5uYW1lcyguKSkNCg0KZ2dwbG90KCkgKyANCiAgZ2VvbV9wb2ludChkYXRhID0gZW5nZWwsIGFlcyhpbmNvbWUsIGZvb2RleHApLCBhbHBoYSA9IDAuNSkgKyANCiAgZ2VvbV9hYmxpbmUoZGF0YSA9IGludGVyY2VwdF9zbG9wZSwgYWVzKGludGVyY2VwdCA9IGludGVyY2VwdCwgc2xvcGUgPSBzbG9wZSwgY29sb3IgPSBxdWFudGlsZSkpICsgDQogIHRoZW1lX21pbmltYWwoKSArIA0KICBsYWJzKHggPSAiRG9jaMOzZCIsIHkgPSAiV3lkYXRraSBuYSDFvHl3bm/Fm8SHIiwgdGl0bGUgPSAiUmVncmVzamUga3dhbnR5bG93ZSB6IHRhdSA9IDAuMjUsIDAuNTAgb3JheiAwLjc1IiwgDQogICAgICAgY2FwdGlvbiA9ICLFuXLDs2TFgm8gZGFueWNoOiBLb2Vua2VyIGFuZCBCYXNzZXR0ICgxOTgyKSIpDQpgYGANCg0KIyMgUHJ6eWvFgmFkIDIuDQoNClR1dGFqIHByemVwcm93YWR6aW15IHRlc3R5IHXFvHljaWEgcGFraWV0dSBxdWFudHJlZywgd3lrb3J6eXN0dWrEhWMNCndidWRvd2FueSB6YmnDs3IgZGFueWNoICIqKm10Y2FycyoqIi4gWm1pZW5uYSAiKiptcGcqKiIgb3puYWN6YSBzcGFsYW5pZQ0Kc2Ftb2Nob2TDs3cgKCptaWxlL2dhbG9uKikuDQoNClphbW9kdWxlam15IHphbGXFvG5vxZvEhyByZWdyZXN5am7EhSBkbGEgdGVqIHptaWVubmVqIG9kIGtpbGt1IHByZWR5a3RvcsOzdy4NCg0KTmFqcGllcncgb3N6YWN1am15IHJlZ3Jlc2rEmSBLTU5LOg0KDQpgYGB7cn0NCmttbmsgPC0gbG0obXBnIH4gZGlzcCArIGhwICsgZmFjdG9yKGFtKSArIGZhY3Rvcih2cyksIGRhdGEgPSBtdGNhcnMpDQpzdW1tYXJ5KGttbmspDQpgYGANCg0KVGVyYXogb3N6YWN1am15IHdhcnVua293ZSByZWdyZXNqZSBrd2FudHlsb3dlIG5hIHLDs8W8bnljaCBrd2FudHlsYWNoLA0KYsWCxIVkIHN0YW5kYXJkb3d5IHV6eXNrYW55IHByemV6ICoqKmJvb3RzdHJhcCoqKi4NCg0KWmF1d2HFvCwgxbxlIGlzdG5pZWplIGdyYWRpZW50IHdlIHdzcMOzxYJjenlubmlrYWNoIGt3YW50eWxvd3ljaCAqKmhwKiosIGphaw0KcsOzd25pZcW8ICoqZGlzcCoqLiBabmFrICoqZGlzcCoqIG9kd3JhY2Egc2nEmSwgcsOzd25pZcW8IHdzcMOzxYJjenlubmlrIG5hDQpjenlubmlrdSAqKmFtKiogamVzdCByw7PFvG55IHcgemFsZcW8bm/Fm2NpIG9kIGt3YW50eWxpOg0KDQpgYGB7cn0NCmt3YW50eWxlIDwtIGMoMC4yNSwgMC41MCwgMC43NSkNCnJlZ19rd2FudHlsb3dhIDwtIHJxKG1wZyB+IGRpc3AgKyBocCArIGZhY3RvcihhbSksdGF1ID0ga3dhbnR5bGUsZGF0YSA9IG10Y2FycykNCnN1bW1hcnkocmVnX2t3YW50eWxvd2EsIHNlID0gImJvb3QiKQ0KYGBgDQoNCiMjIyBUZXN0eSB3c3DDs8WCY3p5bm5pa8Ozdw0KDQpVxbx5amVteSBmdW5rY2ppIHJxLmFub3ZhIHogcGFraWV0dSByZWdyZXNqaSBrd2FudHlsb3dlaiwgYWJ5DQpwcnplcHJvd2FkemnEhyB0ZXN0IFdBTERBLiBQYW1pxJl0YWosIMW8ZSB0ZXN0IFdBTERBIG3Ds3dpLCDFvGUgYmlvcsSFYyBwb2QNCnV3YWfEmSBuaWVvZ3Jhbmljem9uZSBvc3phY293YW5pYSBtb2RlbHUsIHByemV0ZXN0dWplbXkgaGlwb3RlesSZIHplcm93xIUNCm3Ds3dpxIVjxIUsIMW8ZSB3c3DDs8WCY3p5bm5pa2kgc3BlxYJuaWFqxIUgcGV3bmUgbGluaW93ZSBvZ3JhbmljemVuaWEuDQoNCkFieSBqxIUgcHJ6ZXRlc3Rvd2HEhywgdcW8eWplbXkgb2JpZWt0dSB6d3LDs2NvbmVnbyB6IHVydWNob21pZW5pYSAqKipycSoqKg0KeiByw7PFvG55bWkgbGljemJhbWkga3dhbnR5bGkgaSB1c3Rhd2lteSBvcGNqxJkgKioqam9pbnQqKiogbmEgdHJ1ZSBsdWINCmZhbHNlLiBHZHkgKioqam9pbnQqKiogamVzdCB0cnVlOiAicsOzd25vxZvEhyB3c3DDs8WCY3p5bm5pa8OzdyBraWVydW5rb3d5Y2gNCnBvd2lubmEgYnnEhyB3eWtvbmFuYSBqYWtvIHdzcMOzbG5lIHRlc3R5IG5hIHdzenlzdGtpY2ggcGFyYW1ldHJhY2gNCm5hY2h5bGVuaWEiLCBnZHkgKioqam9pbnQqKiogamVzdCBmYWxzZTogIm5hbGXFvHkgemfFgmFzemHEhyBvZGR6aWVsbmUNCnRlc3R5IG5hIGthxbxkeW0geiBwYXJhbWV0csOzdyBuYWNoeWxlbmlhIi4NCg0KWmF1d2HFvCwgxbxlIHRlc3R5IGt3YW50eWxvd2Ugc8SFIHRlc3RhbWkgImxpbmlpIHLDs3dub2xlZ8WCZWoiLiBPem5hY3phIHRvLA0KxbxlIHBvd2lubmnFm215IHd5asSFxIcgcsOzxbxuZSB4LXd5cmF6eV93b2xuZSBkbGEga2HFvGRlZ28ga3dhbnR5bGEsIHBvbmlld2HFvA0KcmVwcmV6ZW50dWrEhSBvbmUgcG96aW9teSByb3prxYJhZMOzdyB3YXJ1bmtvd3ljaC4gSmXFm2xpIGplZG5haw0Kd3Nww7PFgmN6eW5uaWtpIGt3YW50eWxpIGRsYSB3c3DDs8WCY3p5bm5pa293IHPEhSB0YWtpZSBzYW1lLCB0byBuaWUgbWENCmVmZWt0w7N3IHNwZWN5Zmljem55Y2ggZGxhIGt3YW50eWxpLCB3eXN0YXJjesSFIGVmZWt0eSDFm3JlZG5pZS4NCg0KKipCYWRhbmllIHN0YXR5c3R5Y3puZWogcsOzxbxuaWN5IG1pxJlkenkgMjUuIGkgNTAuIGt3YW50eWxlbSB3YXJ1bmtvd3ltOioqDQoNCkJpb3LEhWMgcG9kIHV3YWfEmSBwb3d5xbxzemUgb3N6YWNvd2FuaWEga3dhbnR5bGksIHLDs8W8bmljYSBtacSZZHp5DQprd2FudHlsYW1pIDAsMjUgaSAwLDUwIGlzdG5pZWplLCBhbGUgY3p5IHPEhSBvbmUgd3lzdGFyY3phasSFY28gZHXFvGUsIGFieQ0KYnnEhyBzdGF0eXN0eWN6bmllIHLDs8W8bmU/IEpha2EgamVzdCB3YXJ0b8WbxIcgcD8gUHJ6ZWdsxIVkYWrEhWMgcG9uacW8c3plDQp3eW5pa2ksIG5pZSBzxIUgb25lIHN0YXR5c3R5Y3puaWUgcsOzxbxuZSENCg0KUG8gcGllcndzemUsIGpvaW50ID0gVFJVRS4gVG8gbmllIGplc3QgdGVzdG93YW5pZSwgY3p5IHdzcMOzxYJjenlubmlrIG5hDQpkaXNwIGplc3QgdGFraSBzYW0gamFrIHdzcMOzxYJjenlubmlrIG5hIGhwLiBUbyBqZXN0IHdzcMOzbG5lIHRlc3Rvd2FuaWUsDQpjenkgd3Nww7PFgmN6eW5uaWtpIGRsYSByw7PFvG55Y2gga3dhbnR5bGkgZGlzcCBpIHLDs8W8bnljaCBrd2FudHlsaSBocCBzxIUNCnRha2llIHNhbWUgZGxhIGthxbxkZWogem1pZW5uZWouDQoNCmBgYHtyfQ0Ka3dhbnR5bGUgPC0gYygwLjI1LCAwLjUwKQ0KcmVnX2t3YW50eWxvd2EgPC0gcnEobXBnIH4gZGlzcCArIGhwICsgZmFjdG9yKGFtKSx0YXUgPSBrd2FudHlsZSwgZGF0YSA9IG10Y2FycykNCmFub3ZhKHJlZ19rd2FudHlsb3dhLCB0ZXN0ID0gIldhbGQiLCBqb2ludD1UUlVFKQ0KYGBgDQoNClBvIGRydWdpZSwgam9pbnQgPSBGYWxzZToNCg0KYGBge3J9DQphbm92YShyZWdfa3dhbnR5bG93YSwgdGVzdCA9ICJXYWxkIiwgam9pbnQ9RkFMU0UpDQpgYGANCg0KKipCYWRhbmllIHN0YXR5c3R5Y3puZWogcsOzxbxuaWN5IG1pxJlkenkgMjUsIDUwIGkgNzUga3dhbnR5bGVtDQp3YXJ1bmtvd3ltOioqDQoNClBpZXJ3c3p5IGt3YXJ0eWwgaSBtZWRpYW5hIG5pZSB3eWRhasSFIHNpxJkgYnnEhyBzdGF0eXN0eWN6bmllIHLDs8W8bmUsIHRlcmF6DQpkb8WCxIVjenlteSB0cnplY2kga3dhcnR5bC4gSmFrIHdpZGHEhyB3Y3plxZtuaWVqLCBrd2FydHlsZSB3c3DDs2xuaWUNCnd5a2F6dWrEhSBncmFkaWVudC4gVGVyYXogbW/FvGVteSB6b2JhY3p5xIcsIMW8ZSAqKmRpc3AqKiwgKipocCoqIGkgKiphbSoqDQpzxIUgb2RkemllbG5pZSBzdGF0eXN0eWN6bmllIHLDs8W8bmUuDQoNClBvIHBpZXJ3c3plLCBqb2ludCA9IFRSVUU6DQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQprd2FudHlsZSA8LSBjKDAuMjUsIDAuNTAsIDAuNzUpDQoNCnJlZ19rd2FudHlsb3dhIDwtIHJxKG1wZyB+IGRpc3AgKyBocCArIGZhY3RvcihhbSksdGF1ID0ga3dhbnR5bGUsIGRhdGEgPSBtdGNhcnMpDQoNCmFub3ZhKHJlZ19rd2FudHlsb3dhLCB0ZXN0ID0gIldhbGQiLCBqb2ludD1UUlVFKQ0KYGBgDQoNClBvIGRydWdpZSwgam9pbnQgPSBGYWxzZToNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmFub3ZhKHJlZ19rd2FudHlsb3dhLCB0ZXN0ID0gIldhbGQiLCBqb2ludD1GQUxTRSkNCmBgYA0KDQojIyMgRG9icm/EhyBkb3Bhc293YW5pYQ0KDQpNb8W8ZW15IG9ibGljennEhyB3c3DDs8WCY3p5bm5pa2kgZG9icm9jaSBkb3Bhc293YW5pYSByZWdyZXNqaSBrd2FudHlsb3dlaiB6DQp3eWtvcnp5c3RhbmllbSByZXN6dCBpIHJlc3p0IGJlendhcnVua293eWNoOg0KDQpgYGAgcg0KZ29vZGZpdChyZXNpZCwgcmVzaWRfbmwsIHRhdSkNCmBgYA0KDQpNaWFyYSBkb2Jyb2NpIGRvcGFzb3dhbmlhIGRsYSByZWdyZXNqaSBrd2FudHlsb3dlaiBqZXN0IHN6YWNvd2FuYSBqYWtvIDENCm1pbnVzIHN0b3N1bmVrIHN1bXkgb2RjaHlsZcWEIGJlend6Z2zEmWRueWNoIHcgbW9kZWxhY2ggdyBwZcWCbmkNCnNwYXJhbWV0cnl6b3dhbnljaCBkbyBzdW15IG9kY2h5bGXFhCBiZXp3emdsxJlkbnljaCB3IHplcm93eW0NCihiZXp3YXJ1bmtvd3ltKSBtb2RlbHUga3dhbnR5bG93eW0uDQoNCldhcnRvxZtjaSB0ZSBzxIUgcHJ6eWRhdG5lIGRvIHBvcsOzd25hxYQgbWnEmWR6eSBtb2RlbGFtaSBrd2FudHlsb3d5bWksIGFsZQ0KbmllIHPEhSBwb3LDs3dueXdhbG5lIHplIHN0YW5kYXJkb3d5bWkgd3Nww7PFgmN6eW5uaWthbWkgZGV0ZXJtaW5hY2ppLiBUZQ0Kb3N0YXRuaWUgb3BhcnRlIHPEhSBuYSB3YXJpYW5jamkgb2RjaHlsZcWEIGt3YWRyYXRvd3ljaCwgbmF0b21pYXN0DQp3YXJ0b8WbY2kgZG9icm9jaSBkb3Bhc293YW5pYSBkbGEgcmVncmVzamkga3dhbnR5bG93ZWogb3BhcnRlIHPEhSBuYQ0Kb2RjaHlsZW5pYWNoIGJlend6Z2zEmWRueWNoLiBXYXJ0b8WbY2kgZG9icm9jaSBkb3Bhc293YW5pYSB6YXdzemUgYsSZZMSFDQptbmllanN6ZSBuacW8IHdhcnRvxZtjaSBSXjJeLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyMgbW9kZWwga3dhbnR5bG93eQ0KbW9kZWwxIDwtIHJxKG1wZyB+IGRpc3AgKyBocCArIGZhY3RvcihhbSksdGF1ID0gMC41LCBkYXRhID0gbXRjYXJzKQ0KcmVzenR5MSA8LSByZXNpZChtb2RlbDEpDQoNCiMjIGJlendhcnVua293eSAocHVzdHkpIG1vZGVsIGt3YW50eWxvd3kNCm1vZGVsMiA8LSBycShtcGcgfiAxLCB0YXUgPSAwLjUsZGF0YT1tdGNhcnMpDQpyZXN6dHkyIDwtIHJlc2lkKG1vZGVsMikNCg0KZ29vZGZpdChyZXN6dHkxLCByZXN6dHkyLCAwLjUpDQoNCiMjIHIyIG1vZGVsdSBLTU5LIGRsYSBwb3LDs3duYW5pYQ0KbW9kZWxfbG0gPC0gbG0obXBnIH4gZGlzcCArIGhwICsgZmFjdG9yKGFtKSwgZGF0YSA9IG10Y2FycykNCg0Kc3VtbWFyeShtb2RlbF9sbSkkci5zcXVhcmVkDQpgYGANCg0KIyMgWmFkYW5pZQ0KDQpUZXJheiBXYXN6YSBrb2xlaiA7LSkNCg0KV2FzenltIHphZGFuaWVtIGR6aXNpYWogamVzdCB6YW1vZGVsb3dhbmllIC0gcG9yw7N3bmFuaWUgS01OSyBvcmF6DQpyZWdyZXNqaSBrd2FudHlsb3dlaiAocsOzxbxuby1wb3ppb21vd2VqKSBkbGEgem1pZW5uZWogImVhcm5pbmdzIiAtDQp3eW5hZ3JvZHplbmlhLg0KDQpEb2JpZXJ6IGkgcHJ6ZXRlc3R1aiBwcmVkeWt0b3J5LCBrd2FudHlsZSBkbGEgbW9kZWxpLiBXeWtvbmFqIHRlc3R5DQpyw7PFvG5pYyB3c3DDs8WCY3p5bm5pa293IGRsYSBmaW5hbG55Y2ggbW9kZWxpLg0KDQpXIHByenlwYWRrdSBwcm9ibGVtw7N3IC0gb2JlanJ6eWogdmlkZW8gdHV0b3JpYWwgKHfFgsSFY3ogcG9sc2tpZSBuYXBpc3kpDQpvcmF6IHdlamTFuiBuYSBqZWdvIHN0cm9uxJkgemUgxbpyw7NkxYJhbWkuIE1vxbxlc3ogcsOzd25pZcW8IHd5a29yenlzdGHEhyB3L3cNCnByenlrxYJhZHkuDQoNCg0KIyMjIEltcG9ydCBkYW55Y2gNCg0KUG9uacW8ZWogemFpbXBvcnRvd2FubyB6YmnDs3IgZGFueWNoIENQU1NXOTI5OCwgdyBrdMOzcnltIHpuYWpkdWrEhSBzacSZIGJhZGFuZSB3eW5hZ3JvZHplbmlhLg0KDQpgYGB7cn0NCmRhdGEoIkNQU1NXOTI5OCIpDQpkYW5lIDwtIENQU1NXOTI5OA0KDQpgYGANCg0KDQojIyMgV2l6dWFsaXphY2phIGRhbnljaA0KDQpOYSBwb25pxbxzenltIGhpc3RvZ3JhbWllIHByemVkc3Rhd2lvbm8gcm96a8WCYWQgd3luYWdyb2R6ZcWELg0KDQpgYGB7cn0NCnd5a3JlczAgPC0gZ2dwbG90KGRhbmUsIGFlcyh4PWVhcm5pbmdzKSkgKyANCiAgICAgICAgICAgZ2VvbV9oaXN0b2dyYW0oYmlucz0zMCwgY29sb3I9ImRvZGdlcmJsdWU0IiwgZmlsbD0ibGlnaHRibHVlIikgKw0KICAgICAgICAgICB4bGFiKCJaYXJvYmtpIikgKyB5bGFiKCJDesSZc3RvxZvEhyIpICsNCiAgICAgICAgICAgZ2d0aXRsZSgiSGlzdG9ncmFtIHJvemvFgmFkdSB3eW5hZ3JvZHplxYQiKSArDQogICAgICAgICAgIHhsYWIoIld5bmFncm9kemVuaWEiKSArIHlsYWIoIkN6xJlzdG/Fm8SHIikNCmdncGxvdGx5KHd5a3JlczApDQoNCmBgYA0KTmEgcG9kc3Rhd2llIHBvd3nFvHN6ZWdvIHJ5c3Vua3UgbW/FvG5hIHphdXdhxbx5xIcgd3lzdMSZcG93YW5pZSBhc3ltZXRyaWkgcHJhd29zdHJvbm5lai4gT3puYWN6YSB0bywgxbxlIHcgYmFkYW5laiBwcsOzYmllIHByemV3YcW8YWrEhSB3eW5hZ29yZHplbmlhIG8gd2FydG/Fm2NpIHBvbmnFvGVqIMWbcmVkbmllai4NCg0KTmEgcG9uacW8c3p5bSByeXN1bmt1IHByemVkc3Rhd2lvbm8gcG9yw7N3bmFuaWUgd3luYWdyb2R6ZcWEIGtvYmlldCBpIG3EmcW8Y3p5em4uDQoNCmBgYHtyfQ0Kd3lrcmVzMSA8LSBnZ3Bsb3QoZGFuZSwgYWVzKHk9ZWFybmluZ3MsIHg9Z2VuZGVyLCBmaWxsPWdlbmRlcikpICsgDQogICAgICAgICAgIGdlb21fYm94cGxvdCgpICsNCiAgICAgICAgICAgZ2d0aXRsZSgiUG9yw7N3YW5pZSB3eW5hZ3JvZHplxYQga29iaWV0IGkgbcSZxbxjenl6biIpICsNCiAgICAgICAgICAgeGxhYigiUMWCZcSHIikgKyB5bGFiKCJXeW5hZ3JvZHplbmlhIikgKw0KICAgICAgICAgICBsYWJzKGZpbGw9IlDFgmXEhyIpIA0KZ2dwbG90bHkod3lrcmVzMSkNCg0KYGBgDQpOYSBwb2RzdGF3aWUgcG93ecW8c3plZ28gcnlzdW5rdSBtb8W8bmEgemF1d2HFvHnEhywgxbxlIG3EmcW8Y3p5xbpuaSBwb3NpYWRhasSFIHd5xbxzemUgd3luYWdyb2R6ZW5pYSB3emdsxJlkZW0ga29iaWV0Lg0KDQpOYSBwb25pxbxzenltIHJ5c3Vua3UgcHJ6ZWRzdGF3aW9ubyBwb3LDs3duYW5pZSB3eW5hZ3JvZHplxYQgdyB6YWxlxbxub8WbY2kgb2Qgd3lrc3p0YcWCY2VuaWEuDQoNCmBgYHtyfQ0Kd3lrcmVzMiA8LSBnZ3Bsb3QoZGFuZSwgYWVzKHk9ZWFybmluZ3MsIHg9ZGVncmVlLCBmaWxsPSBkZWdyZWUpKSArIA0KICAgICAgICAgICBnZW9tX2JveHBsb3QoKSArDQogICAgICAgICAgIGdndGl0bGUoIlBvcsOzd2FuaWUgd3luYWdyb2R6ZcWEIHcgemFsZcW8bm/Fm2NpIG9kIHd5a3N6dGHFgmNlbmlhIikgKw0KICAgICAgICAgICB4bGFiKCJXeWtzenRhxYJjZW5pZSIpICsgeWxhYigiV3luYWdyb2R6ZW5pYSIpICsNCiAgICAgICAgICAgbGFicyhmaWxsPSJXeWtzenRhxYJjZW5pZSIpDQpnZ3Bsb3RseSh3eWtyZXMyKQ0KDQpgYGANCk5hIHBvZHN0YXdpZSBwb3d5xbxzemVnbyByeXN1bmt1IG1vxbxuYSB6YXV3YcW8ecSHLCDFvGUgb3NvYnkgcG9zaWFkYWrEhWNlIHR5dHXFgiBsaWNlbmNqYXRhIHBvc2lhZGFqxIUgd3nFvHN6ZSB3eW5hZ3JvZHplbmlhIHd6Z2zEmWRlbSBvc8OzYiwga3TDs3JlIHVrb8WEY3p5xYJ5IGplZHluaWUgc3prb8WCxJkgxZtyZWRuacSFLg0KDQpOYSBwb25pxbxzenltIHJ5c3Vua3UgcHJ6ZWRzdGF3aW9ubyBwb3LDs3duYW5pZSB3eW5hZ3JvZHplxYQgdyB6YWxlxbxub8WbY2kgb2QgcMWCY2kgaSB3eWtzenRhxYJjZW5pYS4NCg0KYGBge3J9DQp3eWtyZXMzIDwtIGdncGxvdChkYW5lLCBhZXMoeT1lYXJuaW5ncywgeD1kZWdyZWUsIGZpbGw9Z2VuZGVyKSkgKyANCiAgICAgICAgICAgZ2VvbV9ib3hwbG90KCkgKw0KICAgICAgICAgICBnZ3RpdGxlKCJQb3LDs3dhbmllIHd5bmFncm9kemXFhCBrb2JpZXQgaSBtxJnFvGN6eXpuIHcgemFsZcW8bm/Fm2NpIG9kIHd5a3N6dGHFgmNlbmlhIikgKw0KICAgICAgICAgICB4bGFiKCJXeWtzenRhxYJjZW5pZSIpICsgeWxhYigiV3luYWdyb2R6ZW5pYSIpICsNCiAgICAgICAgICAgbGFicyhmaWxsPSJQxYJlxIciKQ0Kd3lrcmVzMw0KDQpgYGANCk5hIHBvZHN0YXdpZSBwb3d5xbxzemVnbyByeXN1bmt1IG1vxbxuYSB6YXV3YcW8ecSHLCDFvGUgbmFqd3d5xbxzemUgd3luYWdyb2R6ZW5pYSBwb3NpYWRhasSFIG3EmcW8Y3p5xbpuaSB6IHR5dHXFgmVtIGxpY2VuY2phdGEsIGEgbmFqbmnFvHN6ZSBhYnNvbHdlbnRraSBzemtvxYJ5IMWbcmVkbmllai4NCg0KDQojIyMgUmVncmVzamEgS01OSw0KDQpQb25pxbxlaiB6YnVkb3dhbm8gbW9kZWwgcmVncmVzamkgbGluaW93ZWogc3phY293YW5laiB6YSBwb21vY8SFIGtsYXN5Y3puZWogbWV0b2R5IG5ham1uaWplc3p5Y2gga3dhZHJhdHfDs3cuIFptaWVubsSFIHd5bmlrb3fEhSBzdGFub3dpxYJ5IHd5bmFncm9kemVuaWEsIG5hdG9taWFzdCB3xZtyw7NkIHByZWR5a3RvcsOzdyB1d3pnbMSZZG5pb25vOiB3aWVrLCBzdG9waWXFhCB3eWtzenRhxYJjZW5pYSBvcmF6IHDFgmXEhy4NCg0KYGBge3J9DQprbW5rIDwtIGxtKGVhcm5pbmdzIH4gYWdlICsgZGVncmVlICsgZ2VuZGVyLCBkYXRhID0gZGFuZSkNCnN1bW1hcnkoa21uaykNCg0KYGBgDQpOYSBwb2RzdGF3aWUgdXp5c2thbnljaCB3eW5pa8OzdyBtb8W8bmEgemF1d2HFvHnEhywgxbxlIG5hIHBvemlvbSB3eW5ncm9kemXFhCBzdGF0eXN0eWN6bmllIGlzdG90bmllIHdwxYJ5d2FqxIU6IHdpZWssIHVrb8WEY3plbmllIHN0dWRpw7N3IG5hIHBvemlvbWllIGxpY2VuY2phY2tpbSBvcmF6IHDFgmXEhyBrb2JpZWNhLg0KDQojIyMgUmVncmVzamEga3dhbnR5bG93YQ0KDQpQb25pxbxlaiB6YnVkb3dhbm8gbW9kZWwgcmVncmVzamkga3dhbnR5bG93ZWogb2Rwb3dpZWRuaW8gZGxhIHBpZXJ3c3plZ28gKFExPTAsMjUpLCBkcnVnaWVnbyAoUTI9MCw1KSBpIHRyemVjaWVnbyBrd2FydHlsYSAoUTM9MCw3NSkuIFBvZG9ibmllIGphayB3IHBvcHJ6ZWRuaW0gcHJ6eXBhZGt1IHptaWVubsSFIHd5bmlrb3fEhSBzdGFub3dpxYJ5IHd5bmFncm9kemVuaWEsIG5hdG9taWFzdCBqYWtvIHByZWR5a3Rvcnkgd3licmFubzogd2llaywgc3RvcGllxYQgd3lrc3p0YcWCY2VuaWEgb3JheiBwxYJlxIcuDQoNCmBgYHtyfQ0Ka3dhbnR5bGUgPC0gYygwLjI1LCAwLjUwLCAwLjc1KQ0KcmVnX2t3YW50eWxvd2EgPC0gcnEoZWFybmluZ3MgfiBhZ2UgKyBkZWdyZWUgKyBnZW5kZXIgLCB0YXUgPSBrd2FudHlsZSwgZGF0YSA9IGRhbmUpDQpzdW1tYXJ5KHJlZ19rd2FudHlsb3dhLCBzZSA9ICJib290IikNCg0KYGBgDQpQcnp5am11asSFYyB6YSBwb3ppb20gaXN0b3Rub8WbY2kgc3RhdHlzdHljem5laiA1JSAoYWxmYT0wLDA1KSBtb8W8bmEgemF1d2HFvHnEhywgxbxlIHphcsOzd25vIGRsYSBwaWVyd3N6ZWdvLCBqYWsgaSBkcnVnaWVnbyBrd2FydHlsYSB3c3p5c3RraWUgcHJlZHlrdG9yeSBpc3RvdG5pZSBzdGF0eXN0Y3puaWUgd3DFgnl3YWrEhSBuYSBwb3ppb20gd3luYWdyb2R6ZcWELiBKZWRuYWvFvGUgdyBwcnp5cGFka3UgdHJ6ZWNpZWdvIGt3YXJ0eWxhIHdpZWsgb2themHFgiBzacSZIGJ5xIcgc3RheXN0eWN6bmllIG5pZWlzdG9ueW0gY3p5bm5pZW0ga3N6dGHFgnR1asSFY3ltIHd5c29rb8WbxIcgd3luYWdyb2R6ZcWELg0KDQojIyMgV2VyeWZpa2FjamEgc3RhdHlzdHljem5hIGlzdG90bm/Fm2NpIHLDs8W8bmljIG1pxJlkenkgcGllcndzenltLCBkcnVnaW0gaSB0cnplY2ltIGt3YXJ0eWxlbQ0KDQpQb25pxbxlaiB3eWtvbmFubyB0ZXN0IEFOT1ZBIHcgY2VsdSB6YmFkYW5pYSBjenkgcG9tacSZZHp5IHJvendhxbxhbnltaSBrd2FydHlsYW1pIHd5c3RlcHVqxIUgc3RhdHlzdHljem5pZSBpc3RvdG5lIHLDs8W8bmljZS4NCg0KYGBge3J9DQphbm92YShyZWdfa3dhbnR5bG93YSwgdGVzdCA9ICJXYWxkIiwgam9pbnQ9VFJVRSkNCg0KYGBgDQpOYSBwb2RzdGF3aWUgdXp5c2thbmVnbyB3eW5pa3UgbW/FvG5hIHphdXdhxbx5xIcsIMW8ZSB6YXLDs3dubyBwaWVyd3N6eSwgZHJ1Z2ksIGphayBpIHRyemVjaSBrd2FydHlsIMWCxIVjem5pZSBpc3RvdG5pZSBzdGF0eXN0eWN6bmllIHLDs8W8bmnEhSBzacSZIG1pxJlkenkgc29ixIUuDQoNClBvbmnFvGVqIHd5a29uYW8gdGVzdCBBTk9WQSB3IGNlbHUgemJhZGFuaWEgY3p5IGFuYWxpem93YW5lIGRldGVybWluYW50eSB3eW5hZ3JvZHplxYQgc8SFIGlzdG90bmllIHN0YXR5c3R5Y3puaWUgcsOzxbxuZS4NCg0KYGBge3J9DQphbm92YShyZWdfa3dhbnR5bG93YSwgdGVzdCA9ICJXYWxkIiwgam9pbnQ9RkFMU0UpDQoNCmBgYA0KTmEgcG9kc3Rhd2llIHV6eXNrYW55Y2ggd3luaWvDs3cgbW/FvGVteSB6b2JhY3p5xIcsIMW8ZSB1d3pnbMSZZG5pb25lIHcgYmFkYW5pdSBwcmVkeWt0b3J5IHd5bmFncm9kemXFhCBzxIUgb2RkemllbG5pZSBzdGF0eXN0eWN6bmllIHLDs8W8bmUuDQoNCg0KIyMjIERvYnJvxIcgZG9wYXNvd2FuaWENCg0KUG9uacW8ZWogb2JsaWN6b25vIHdzcMOzxYJjenlubmlraSBkb2Jyb2NpIGRvcGFzb3dhbmlhIHJlZ3Jlc2ppIGt3YW50eWxvd2VqIHoNCnd5a29yenlzdGFuaWVtIHJlc3p0IGkgcmVzenQgYmV6d2FydW5rb3d5Y2g6DQoNCmBgYHtyfQ0KDQojIFRlc3R5IHJlc3p0Og0KcmVzenR5MCA8LSByZXNpZChrbW5rKQ0KDQojIEJlendhcnVua293eSAocHVzdHkpIG1vZGVsIGt3YW50eWxvd3kgZGxhIFEyID0gMC41DQptb2RlbDIgPC0gcnEoZWFybmluZ3MgfiAxLCB0YXUgPSAwLjUsZGF0YT1kYW5lKQ0KcmVzenR5MiA8LSByZXNpZChtb2RlbDIpDQoNCmdvb2RmaXQocmVzenR5MCwgcmVzenR5MiwgMC41KQ0KDQoNCiMgQmV6d2FydW5rb3d5IChwdXN0eSkgbW9kZWwga3dhbnR5bG93eSBkbGEgUTMgPSAwLjc1DQptb2RlbDMgPC0gcnEoZWFybmluZ3MgfiAxLCB0YXUgPSAwLjc1LGRhdGE9ZGFuZSkNCnJlc3p0eTMgPC0gcmVzaWQobW9kZWwzKQ0KDQpnb29kZml0KHJlc3p0eTAsIHJlc3p0eTMsIDAuNzUpDQoNCg0KIyBXc3DDs8WCY3p5bm5payBkdGVybWluYWNqaSBtb2RlbHUgS01OSyBkbGEgcG9yw7N3bmFuaWENCnN1bW1hcnkoa21uaykkci5zcXVhcmVkDQoNCmBgYA0KTmEgcG9kc3Rhd2llIHV6eXNrYW55Y2ggd3luaWvDs3cgbW/FvG5hIHphdXdhxbx5xIcsIMW8ZSB3c3DDs8WCY3p5bm5payBkZXRlcm1pbmFjamkgZGxhIHJlZ3Jlc2ppIGxpbmlvd2VqIHV6eXNrYcWCIG5hand5xbxzesSFIHdhcnRvxZvEhy4gT3puYWN6YSB0bywgxbxlIG5pbmllanN6eSBtb2RlbCBuYWpsZXBpZWogd3lqYcWbbmlhIGJhZGFuZSB6amF3aXNrby4gUmVncmVzamEgbGluaW93YSBsZXBpZWogZG9wYXNvd2HFgmEgc2nEmSBkbyBkYW55Y2ggZW1waXJ5Y3pueWNoIHdvYmVjIHJlZ3Jlc2ppIGt3YW50eWxvd2VqIHphcsOzd25vIGRsYSBkcnVnaWVnbywgamFrIGkgdHJ6ZWNpZWdvIGt3YXJ0eWxhLg0KDQoNCg0K